#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#pragma pack(push, 1) // 确保结构体紧凑排列
typedef struct {
char signature[2];
int file_size;
short reserved1;
short reserved2;
int data_offset;
} BmpHeader;
typedef struct {
int header_size;
int width;
int height;
short planes;
short bits_per_pixel;
int compression;
int image_size;
int x_pixels_per_m;
int y_pixels_per_m;
int colors_used;
int colors_important;
} BmpInfoHeader;
#pragma pack(pop)
// 简单的5x7点阵字体(只包含0-9和A-Z)
const unsigned char font[][5] = {
{0x3E,0x51,0x49,0x45,0x3E}, // A
{0x7F,0x49,0x49,0x49,0x36}, // B
{0x3E,0x41,0x41,0x41,0x22}, // C
// 可继续添加其他字符...
};
void add_watermark(unsigned char* pixels, int width, int height, const char* text) {
int text_len = strlen(text);
int char_width = 6;
int char_height = 8;
// 从右下角开始计算起始位置
int start_x = width - (text_len * char_width) - 10;
int start_y = height - char_height - 10;
for (int i = 0; i < text_len; ++i) {
int char_index = toupper(text[i]) - 'A';
if (char_index < 0 || char_index > 25) continue;
// 绘制每个字符
for (int y = 0; y < 7; ++y) {
for (int x = 0; x < 5; ++x) {
if (font[char_index][x] & (1 << (6 - y))) {
int px = start_x + x + i * char_width;
int py = start_y + y;
if (px < width && py < height) {
int offset = (py * width + px) * 3;
// 设置为白色 (255,255,255)
pixels[offset] = 255; // B
pixels[offset + 1] = 255; // G
pixels[offset + 2] = 255; // R
}
}
}
}
}
}
int main(int argc, char* argv[]) {
if (argc != 4) {
printf("Usage: %s <input.bmp> <output.bmp> \"<watermark text>\"\n", argv[0]);
return 1;
}
FILE* fp_in = fopen(argv[1], "rb");
FILE* fp_out = fopen(argv[2], "wb");
const char* watermark = argv[3];
if (!fp_in || !fp_out) {
perror("Error opening file");
return 1;
}
// 读取文件头
BmpHeader header;
fread(&header, sizeof(BmpHeader), 1, fp_in);
// 验证BMP文件
if (memcmp(header.signature, "BM", 2) != 0) {
printf("Not a valid BMP file\n");
return 1;
}
// 读取信息头
BmpInfoHeader info_header;
fread(&info_header, sizeof(BmpInfoHeader), 1, fp_in);
// 仅支持24位BMP
if (info_header.bits_per_pixel != 24) {
printf("Only 24-bit BMP supported\n");
return 1;
}
// 分配像素内存
int width = info_header.width;
int height = info_header.height;
int pixel_data_size = width * height * 3;
unsigned char* pixels = malloc(pixel_data_size);
// 读取像素数据
fseek(fp_in, header.data_offset, SEEK_SET);
fread(pixels, 1, pixel_data_size, fp_in);
// 添加水印
add_watermark(pixels, width, height, watermark);
// 写入输出文件
fwrite(&header, sizeof(BmpHeader), 1, fp_out);
fwrite(&info_header, sizeof(BmpInfoHeader), 1, fp_out);
fseek(fp_out, header.data_offset, SEEK_SET);
fwrite(pixels, 1, pixel_data_size, fp_out);
// 清理资源
free(pixels);
fclose(fp_in);
fclose(fp_out);
printf("Watermark added successfully!\n");
return 0;
}
使用方法:
-
将代码保存为
watermark.c
-
编译:
gcc watermark.c -o watermark
-
运行:
./watermark input.bmp output.bmp "WATERMARK"
注意事项:
-
仅支持24位未压缩的BMP格式
-
水印会添加在图片右下角
-
当前版本只支持大写英文字母
-
需要自行补充完整的点阵字体数据(示例中只实现了A-C)
扩展建议:
-
添加错误处理(文件验证、内存分配检查)
-
支持更多字符和符号
-
实现透明效果(Alpha混合)
-
支持命令行参数自定义水印位置、颜色
-
添加抗锯齿功能
如果需要处理其他格式(如PNG/JPEG),建议使用第三方库(如libpng、libjpeg)来处理图像解码/编码。