png图片,背景替换成无颜色背景
实验需要,加上好奇,使用libpng库将png文件的背景,由黑色,替换成无背景颜色。效果如下图:
替换前:
替换后:
黑色背景都没了,只剩下白色了,看不出来。。。。。。
步骤:
1. 下载png相关库,libpng,以及zlib压缩解压缩文件
libpng官网下载,Ubuntu下zlib下载安装: sudo apt-get install zlib1-dev
2. 代码:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <png.h> 5 6 7 #define PNG_BYTES_TO_CHECK 8 8 #define HAVE_ALPHA 1 9 #define NOT_HAVE_ALPHA 0 10 11 typedef struct _pic_data pic_data; 12 struct _pic_data { 13 int width, height; //长宽 14 int bit_depth; //位深度 15 int alpha_flag; //是否有透明通道 16 unsigned char *rgba;//实际rgb数据 17 }; 18 19 int check_is_png(FILE **fp, const char *filename) //检查是否png文件 20 { 21 char checkheader[PNG_BYTES_TO_CHECK]; //查询是否png头 22 *fp = fopen(filename, "rb"); 23 if (*fp == NULL) { 24 printf("open failed ...1\n"); 25 return -1; 26 } 27 if (fread(checkheader, 1, PNG_BYTES_TO_CHECK, *fp) != PNG_BYTES_TO_CHECK) //读取png文件长度错误直接退出 28 return 0; 29 return png_sig_cmp(checkheader, 0, PNG_BYTES_TO_CHECK); //0正确, 非0错误 30 } 31 32 int decode_png(const char *filename, pic_data *out) //取出png文件中的rgb数据 33 { 34 png_structp png_ptr; //png文件句柄 35 png_infop info_ptr;//png图像信息句柄 36 int ret; 37 FILE *fp; 38 if (check_is_png(&fp, filename) != 0) { 39 printf("file is not png ...\n"); 40 return -1; 41 } 42 printf("launcher[%s] ...\n", PNG_LIBPNG_VER_STRING); //打印当前libpng版本号 43 44 //1: 初始化libpng的数据结构 :png_ptr, info_ptr 45 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); 46 info_ptr = png_create_info_struct(png_ptr); 47 48 //2: 设置错误的返回点 49 setjmp(png_jmpbuf(png_ptr)); 50 rewind(fp); //等价fseek(fp, 0, SEEK_SET); 51 52 //3: 把png结构体和文件流io进行绑定 53 png_init_io(png_ptr, fp); 54 //4:读取png文件信息以及强转转换成RGBA:8888数据格式 55 png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, 0); //读取文件信息 56 int channels, color_type; 57 channels = png_get_channels(png_ptr, info_ptr); //通道数量 58 color_type = png_get_color_type(png_ptr, info_ptr);//颜色类型 59 out->bit_depth = png_get_bit_depth(png_ptr, info_ptr);//位深度 60 out->width = png_get_image_width(png_ptr, info_ptr);//宽 61 out->height = png_get_image_height(png_ptr, info_ptr);//高 62 63 //if(color_type == PNG_COLOR_TYPE_PALETTE) 64 // png_set_palette_to_rgb(png_ptr);//要求转换索引颜色到RGB 65 //if(color_type == PNG_COLOR_TYPE_GRAY && out->bit_depth < 8) 66 // png_set_expand_gray_1_2_4_to_8(png_ptr);//要求位深度强制8bit 67 //if(out->bit_depth == 16) 68 // png_set_strip_16(png_ptr);//要求位深度强制8bit 69 //if(png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS)) 70 // png_set_tRNS_to_alpha(png_ptr); 71 //if(color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) 72 // png_set_gray_to_rgb(png_ptr);//灰度必须转换成RG 73 printf("channels = %d color_type = %d bit_depth = %d width = %d height = %d ...\n", 74 channels, color_type, out->bit_depth, out->width, out->height); 75 76 int i, j, k; 77 int size, pos = 0; 78 int temp; 79 80 //5: 读取实际的rgb数据 81 png_bytepp row_pointers; //实际存储rgb数据的buf 82 row_pointers = png_get_rows(png_ptr, info_ptr); //也可以分别每一行获取png_get_rowbytes(); 83 size = out->width * out->height; //申请内存先计算空间 84 if (channels == 4 || color_type == PNG_COLOR_TYPE_RGB_ALPHA) { //判断是24位还是32位 85 out->alpha_flag = HAVE_ALPHA; //记录是否有透明通道 86 size *= (sizeof(unsigned char) * 4); //size = out->width * out->height * channel 87 out->rgba = (png_bytep)malloc(size); 88 if (NULL == out->rgba) { 89 printf("malloc rgba faile ...\n"); 90 png_destroy_read_struct(&png_ptr, &info_ptr, 0); 91 fclose(fp); 92 return -1; 93 } 94 //从row_pointers里读出实际的rgb数据出来 95 temp = channels - 1; 96 for (i = 0; i < out->height; i++) 97 for (j = 0; j < out->width * 4; j += 4) 98 for (k = temp; k >= 0; k--) 99 out->rgba[pos++] = row_pointers[i][j + k]; 100 } else if (channels == 3 || color_type == PNG_COLOR_TYPE_RGB) { //判断颜色深度是24位还是32位 101 out->alpha_flag = NOT_HAVE_ALPHA; 102 size *= (sizeof(unsigned char) * 3); 103 out->rgba = (png_bytep)malloc(size); 104 if (NULL == out->rgba) { 105 printf("malloc rgba faile ...\n"); 106 png_destroy_read_struct(&png_ptr, &info_ptr, 0); 107 fclose(fp); 108 return -1; 109 } 110 //从row_pointers里读出实际的rgb数据 111 temp = (3 * out->width); 112 for (i = 0; i < out->height; i ++) { 113 for (j = 0; j < temp; j += 3) { 114 out->rgba[pos++] = row_pointers[i][j+2]; 115 out->rgba[pos++] = row_pointers[i][j+1]; 116 out->rgba[pos++] = row_pointers[i][j+0]; 117 } 118 } 119 } else return -1; 120 //6:销毁内存 121 png_destroy_read_struct(&png_ptr, &info_ptr, 0); 122 fclose(fp); 123 //此时, 我们的out->rgba里面已经存储有实际的rgb数据了 124 //处理完成以后free(out->rgba) 125 return 0; 126 } 127 128 int RotationRight90_1(pic_data *in) //顺时针旋转90度 129 { 130 unsigned char * tempSrc = NULL; //临时的buf用来记录原始的图像(未旋转之前的图像) 131 int mSize = 0, i = 0, j = 0, k = 0, channel = 0; 132 int desW = 0; 133 int desH = 0; 134 int tmp1, tmp2; 135 136 desW = in->height; 137 desH = in->width; 138 if(in->alpha_flag == HAVE_ALPHA) 139 { 140 channel = 4; 141 } 142 else 143 { 144 channel = 3; 145 } 146 mSize = (in->width) * (in->height) * sizeof(char) * (channel); 147 // printf("start copy\n"); 148 tempSrc = (unsigned char *)malloc(mSize); 149 memcpy(tempSrc, in->rgba, mSize); //拷贝原始图像至tempbuf 150 // printf("copy ok--------\n"); 151 for(i = 0; i < desH; i ++) 152 { 153 for(j = 0; j < desW; j ++) 154 { 155 for(k = 0; k < channel; k ++) 156 { 157 tmp1 = (i * desW + j) * channel + k; 158 tmp2 = (((in->height) - 1 - j) * (in->width) + i) * channel + k; 159 in->rgba[(i * desW + j) * channel + k] = tempSrc[(((in->height) - 1 - j) * (in->width) + i) * channel + k]; //替换像素 160 // printf("tmp1 = %d, tmp2 = %d\n", tmp1, tmp2); 161 } 162 } 163 } 164 free(tempSrc); 165 in->height = desH; 166 in->width = desW; 167 return 0; 168 } 169 170 // 统一格式,将png转换成具有ALPHA模式 171 int convert_to_alphamode(pic_data *in) 172 { 173 unsigned char * tempSrc = NULL; // 临时的buf用来记录原始的图像(未旋转之前的图像) 174 int temp; 175 int i, j, pos = 0, pos_out = 0; 176 unsigned char *pchar; 177 178 if (in->alpha_flag == HAVE_ALPHA) 179 { 180 temp = (4 * in->width); 181 printf("already have alpha ...\n"); 182 return 1; 183 } 184 else 185 { 186 in->alpha_flag = HAVE_ALPHA; 187 temp = (3 * in->width); 188 printf("not have alpha ...\n"); 189 } 190 pchar = in->rgba; 191 in->rgba = (unsigned char *)malloc(sizeof(char) * (in->width) * (in->height) * 4); 192 193 for (i = 0; i < in->height; i++) 194 { 195 for (j = 0; j < temp; j += 3) 196 { 197 in->rgba[pos_out++] = 0xFF; 198 in->rgba[pos_out++] = pchar[pos++]; 199 in->rgba[pos_out++] = pchar[pos++]; 200 in->rgba[pos_out++] = pchar[pos++]; 201 } 202 } 203 free(pchar); 204 printf("change to alpha ok\n"); 205 return 2; 206 } 207 208 // 将黑色设置成透明 209 int png_set_alpha(pic_data *in) 210 { 211 int temp; 212 int i, j, pos = 0; 213 if (in->alpha_flag != HAVE_ALPHA) 214 { 215 printf("png_set_alpha fail, change png to alpha mode first...\n"); 216 return -1; 217 } 218 temp = (4 * in->width); 219 for (i = 0; i < in->height; i++) 220 { 221 pos = i * (in->width) * 4; 222 for (j = 0; j < temp; j += 4) 223 { 224 if ((in->rgba[pos + j + 1] + in->rgba[pos + j + 2] + in->rgba[pos + j + 3]) < 60) // 黑色区域都变成透明的 225 { 226 //printf("i = %d, j = %d, pos = %d\n", i, j, pos); 227 in->rgba[pos + j + 0] = 0x00; 228 } 229 else // 将有颜色的都变成白色 230 { 231 in->rgba[pos + j + 0] = 0xFF; 232 in->rgba[pos + j + 1] = 0xFF; 233 in->rgba[pos + j + 2] = 0xFF; 234 in->rgba[pos + j + 3] = 0xFF; 235 } 236 } 237 } 238 printf("png_set_alpha ok\n"); 239 return 1; 240 } 241 242 // 将白色设置成透明 243 int png_set_alpha_1(pic_data *in) 244 { 245 int temp; 246 int i, j, pos = 0; 247 if (in->alpha_flag != HAVE_ALPHA) 248 { 249 printf("png_set_alpha fail, change png to alpha mode first...\n"); 250 return -1; 251 } 252 temp = (4 * in->width); 253 for (i = 0; i < in->height; i++) 254 { 255 pos = i * (in->width) * 4; 256 for (j = 0; j < temp; j += 4) 257 { 258 if ((in->rgba[pos + j + 1] + in->rgba[pos + j + 2] + in->rgba[pos + j + 3]) > 600) // 白色区域都变成透明的 259 { 260 //printf("i = %d, j = %d, pos = %d\n", i, j, pos); 261 in->rgba[pos + j + 0] = 0x00; // 透明 262 } 263 else // 将有颜色的都变成白色 264 { 265 in->rgba[pos + j + 0] = 0xFF; // 非透明 266 in->rgba[pos + j + 1] = 0x00; 267 in->rgba[pos + j + 2] = 0x00; 268 in->rgba[pos + j + 3] = 0x00; 269 } 270 } 271 } 272 printf("png_set_alpha ok\n"); 273 return 1; 274 } 275 276 // 截取图片 277 // 参数: 278 // startx: 新图片的起始x坐标 279 // starty: 新图片的起始y坐标 280 // newwidth: 新图片的宽度 281 // newheight: 新图片的高度 282 int screen_cut(pic_data *in, int startx, int starty, int newwidth, int newheight) 283 { 284 int temp = 0; 285 int channel = 0; 286 int i, j, k, pos = 0; 287 unsigned char *puchar; 288 if ((newwidth > in->width) || (newheight > in->height)) 289 { 290 printf("error: newwidth or newheight error, -1\n"); 291 return -1; 292 } 293 if (((startx + newwidth) > in->width) || ((starty + newheight) > in->height)) 294 { 295 printf("error: startx + newwidth or starty + newheight error, -2\n"); 296 return -2; 297 } 298 299 if((startx < 0) || (starty < 0) || (newwidth < 1) || (newheight < 1)) 300 { 301 printf("error: parameter error, -3\n"); 302 return -3; 303 } 304 305 if (in->alpha_flag == HAVE_ALPHA) 306 { 307 channel = 4; 308 } 309 else 310 { 311 channel = 3; 312 } 313 temp = newwidth * newheight * sizeof(char) * channel; 314 puchar = in->rgba; 315 in->rgba = (unsigned char *)malloc(temp); 316 for(i = starty; i < (newheight + starty); i++) 317 { 318 temp = i * (in->width) * channel; 319 for(j = startx; j < (newwidth + startx); j++) 320 { 321 for(k = 0; k < channel; k++) 322 { 323 in->rgba[((i - starty) * newwidth + (j - startx)) * channel + k] = puchar[temp + j*channel + k]; 324 } 325 } 326 } 327 in->width = newwidth; 328 in->height = newheight; 329 free(puchar); 330 return 1; 331 } 332 333 int write_png_file(const char *filename , pic_data *out) //生成一个新的png图像 334 { 335 png_structp png_ptr; 336 png_infop info_ptr; 337 png_byte color_type; 338 png_bytep * row_pointers; 339 FILE *fp = fopen(filename, "wb"); 340 if (NULL == fp) { 341 printf("open failed ...2\n"); 342 return -1; 343 } 344 //1: 初始化libpng结构体 345 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); 346 if (!png_ptr) { 347 printf("png_create_write_struct failed ...\n"); 348 return -1; 349 } 350 //2: 初始化png_infop结构体 , 351 //此结构体包含了图像的各种信息如尺寸,像素位深, 颜色类型等等 352 info_ptr = png_create_info_struct(png_ptr); 353 if (!info_ptr) { 354 printf("png_create_info_struct failed ...\n"); 355 return -1; 356 } 357 //3: 设置错误返回点 358 if (setjmp(png_jmpbuf(png_ptr))) { 359 printf("error during init_io ...\n"); 360 return -1; 361 } 362 //4:绑定文件IO到Png结构体 363 png_init_io(png_ptr, fp); 364 if (setjmp(png_jmpbuf(png_ptr))) { 365 printf("error during init_io ...\n"); 366 return -1; 367 } 368 if (out->alpha_flag == HAVE_ALPHA) color_type = PNG_COLOR_TYPE_RGB_ALPHA; 369 else color_type = PNG_COLOR_TYPE_RGB; 370 //5:设置以及写入头部信息到Png文件 371 png_set_IHDR(png_ptr, info_ptr, out->width, out->height, out->bit_depth, 372 color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); 373 png_write_info(png_ptr, info_ptr); 374 if (setjmp(png_jmpbuf(png_ptr))) { 375 printf("error during init_io ...\n"); 376 return -1; 377 } 378 int channels, temp; 379 int i, j, pos = 0; 380 if (out->alpha_flag == HAVE_ALPHA) { 381 channels = 4; 382 temp = (4 * out->width); 383 printf("have alpha ...\n"); 384 } else { 385 channels = 3; 386 temp = (3 * out->width); 387 printf("not have alpha ...\n"); 388 } 389 // 顺时针旋转90度 , 旋转完了一定要把width 和height调换 不然得到的图像是花的 旋转三次就是逆时针旋转一次 390 //RotationRight90(out->rgba, &out->width, &out->height, channels); 391 //RotationRight90(out->rgba, out->height, out->width, channels); 392 //RotationRight90(out->rgba, out->width, out->height, channels); 393 row_pointers = (png_bytep*)malloc(out->height * sizeof(png_bytep)); 394 for (i = 0; i < out->height; i++) { 395 row_pointers[i] = (png_bytep)malloc(temp* sizeof(unsigned char)); 396 for (j = 0; j < temp; j += channels) { 397 if (channels == 4) { 398 //if (out->alpha_flag == HAVE_ALPHA) 399 row_pointers[i][j+3] = out->rgba[pos++]; 400 row_pointers[i][j+2] = out->rgba[pos++]; 401 row_pointers[i][j+1] = out->rgba[pos++]; 402 row_pointers[i][j+0] = out->rgba[pos++]; 403 //printf("---- i = %d, j = %d, %d, %d, %d, %d\n", i, j, row_pointers[i][j + 0], row_pointers[i][j + 1],row_pointers[i][j + 2],row_pointers[i][j + 3]); 404 //printf("---- i = %d, j = %d, %d, %d, %d, %d\n", i, j, row_pointers[i][j + 0], row_pointers[i][j + 1],row_pointers[i][j + 2],row_pointers[i][j + 3]); 405 } else { 406 row_pointers[i][j+2] = out->rgba[pos++]; 407 row_pointers[i][j+1] = out->rgba[pos++]; 408 row_pointers[i][j+0] = out->rgba[pos++]; 409 //printf("++++ i = %d, j = %d, %d, %d, %d, %d\n", i, j, row_pointers[i][j + 0], row_pointers[i][j + 1],row_pointers[i][j + 2],row_pointers[i][j + 3]); 410 } 411 } 412 } 413 //6: 写入rgb数据到Png文件 414 png_write_image(png_ptr, (png_bytepp)row_pointers); 415 if (setjmp(png_jmpbuf(png_ptr))) { 416 printf("error during init_io ...\n"); 417 return -1; 418 } 419 //7: 写入尾部信息 420 png_write_end(png_ptr, NULL); 421 //8:释放内存 ,销毁png结构体 422 for (i = 0; i < out->height; i ++) 423 free(row_pointers[i]); 424 free(row_pointers); 425 png_destroy_write_struct(&png_ptr, &info_ptr); 426 fclose(fp); 427 return 0; 428 } 429 430 int main(int argc, char **argv) 431 { 432 pic_data out; 433 434 if(argc == 2) 435 { 436 if(strstr(argv[1],"help") != NULL) 437 { 438 printf("\n===============================================================================\n"); 439 printf(" usage: %s -cmd parameter...\n", argv[0]); 440 printf(" cmd1(change to alpha mode): %s 1 srcfilename outfilename\n", argv[0]); 441 printf(" cmd2(screen cut): %s 2 srcfilename outfilename startx starty newwidth newheight\n", argv[0]); 442 printf(" cmd3(change black to transparent): %s 3 srcfilename outfilename\n", argv[0]); 443 printf(" cmd4(change write to transparent): %s 4 srcfilename outfilename\n", argv[0]); 444 printf("===============================================================================\n\n"); 445 446 return 0; 447 } 448 } 449 if(argc >= 4) 450 { 451 if(strcmp(argv[1],"1") == 0) // cmd1 452 { 453 printf(" usage: %s 1 in.png out.png\n", argv[0]); 454 decode_png(argv[2], &out); 455 convert_to_alphamode(&out); 456 write_png_file(argv[3], &out); 457 free(out.rgba); 458 return 1; 459 } 460 else if(strcmp(argv[1],"2") == 0) // cmd2 461 { 462 printf(" usage: %s 2 in.png out.png startx starty newwidth newheight\n", argv[0]); 463 printf(" startx = %d\n", atoi(argv[4])); 464 printf(" starty = %d\n", atoi(argv[5])); 465 printf(" newwidth = %d\n", atoi(argv[6])); 466 printf(" newheight = %d\n", atoi(argv[7])); 467 468 if(argc != 8) 469 { 470 printf("Error: parameter count not correct\n"); 471 return -1; 472 } 473 decode_png(argv[2], &out); 474 screen_cut(&out, atoi(argv[4]), atoi(argv[5]), atoi(argv[6]), atoi(argv[7])); 475 write_png_file(argv[3], &out); 476 } 477 else if(strcmp(argv[1],"3") == 0) // cmd3 478 { 479 printf(" usage: %s 3 in.png out.png\n", argv[0]); 480 decode_png(argv[2], &out); 481 png_set_alpha(&out); 482 write_png_file(argv[3], &out); 483 free(out.rgba); 484 } 485 else if(strcmp(argv[1],"4") == 0) // cmd4 486 { 487 printf(" usage: %s 4 in.png out.png\n", argv[0]); 488 decode_png(argv[2], &out); 489 png_set_alpha_1(&out); 490 write_png_file(argv[3], &out); 491 free(out.rgba); 492 } 493 } 494 return 0; 495 }
参考:https://blog.csdn.net/wang93IT/article/details/85003730