// Create a new (8-bit) image by copying up to 3 channels from the old image. static void iw_opt_copychannels_8(struct iw_context *ctx, struct iw_opt_ctx *optctx, int new_imgtype, int c0, int c1, int c2) { unsigned char *newpixels; int oldnc, newnc; // num_channels size_t newbpr; int i,j; oldnc = iw_imgtype_num_channels(optctx->imgtype); newnc = iw_imgtype_num_channels(new_imgtype); newbpr = iw_calc_bytesperrow(optctx->width,8*newnc); newpixels = iw_malloc_large(ctx, newbpr, optctx->height); if(!newpixels) return; for(j=0;j<optctx->height;j++) { for(i=0;i<optctx->width;i++) { newpixels[j*newbpr + i*newnc +0] = optctx->pixelsptr[j*optctx->bpr + i*oldnc +c0]; if(newnc>1) newpixels[j*newbpr + i*newnc +1] = optctx->pixelsptr[j*optctx->bpr + i*oldnc +c1]; if(newnc>2) newpixels[j*newbpr + i*newnc +2] = optctx->pixelsptr[j*optctx->bpr + i*oldnc +c2]; } } // Remove previous image if it was allocated by the optimization code. if(optctx->tmp_pixels) iw_free(optctx->tmp_pixels); // Attach our new image optctx->tmp_pixels = newpixels; optctx->pixelsptr = optctx->tmp_pixels; optctx->bpr = newbpr; optctx->imgtype = new_imgtype; }
static void iw_opt_16_to_8(struct iw_context *ctx, struct iw_opt_ctx *optctx, int spp) { unsigned char *newpixels; size_t newbpr; int i,j; if(!ctx->opt_16_to_8) return; newbpr = iw_calc_bytesperrow(optctx->width,8*spp); newpixels = iw_malloc_large(ctx, newbpr, optctx->height); if(!newpixels) return; for(j=0;j<optctx->height;j++) { for(i=0;i<spp*optctx->width;i++) { // i is a sample number, not a pixel number. newpixels[j*newbpr + i] = optctx->pixelsptr[j*optctx->bpr + i*2]; } } // Remove previous image if it was allocated by the optimization code. if(optctx->tmp_pixels) iw_free(optctx->tmp_pixels); // Attach our new image optctx->tmp_pixels = newpixels; optctx->pixelsptr = optctx->tmp_pixels; optctx->bpr = newbpr; optctx->bit_depth = 8; }
static void iw_opt_16_to_8(struct iw_context *ctx, struct iw_opt_ctx *optctx, int spp) { iw_byte *newpixels; size_t newbpr; int i,j,k; if(!ctx->opt_16_to_8) return; newbpr = iw_calc_bytesperrow(optctx->width,8*spp); newpixels = iw_malloc_large(ctx, newbpr, optctx->height); if(!newpixels) return; for(j=0;j<optctx->height;j++) { for(i=0;i<spp*optctx->width;i++) { // i is a sample number, not a pixel number. newpixels[j*newbpr + i] = optctx->pixelsptr[j*optctx->bpr + i*2]; } } // Remove previous image if it was allocated by the optimization code. if(optctx->tmp_pixels) iw_free(ctx,optctx->tmp_pixels); // Attach our new image optctx->tmp_pixels = newpixels; optctx->pixelsptr = optctx->tmp_pixels; optctx->bpr = newbpr; optctx->bit_depth = 8; // If there's a background color label, also reduce its precision. if(optctx->has_bkgdlabel) { for(k=0;k<4;k++) { optctx->bkgdlabel[k] >>= 8; } } }
static void iwopt_try_gray8_binary_trns(struct iw_context *ctx, struct iw_opt_ctx *optctx) { int i,j; const unsigned char *ptr; unsigned char *ptr2; unsigned char clr_used[256]; unsigned char key_clr; unsigned char *trns_mask = NULL; if(!(ctx->output_profile&IW_PROFILE_BINARYTRNS)) return; if(!ctx->opt_binary_trns) return; memset(&clr_used,0,256*sizeof(unsigned char)); trns_mask = iw_malloc_large(ctx, optctx->width, optctx->height); if(!trns_mask) goto done; for(j=0;j<optctx->height;j++) { for(i=0;i<optctx->width;i++) { ptr = &optctx->pixelsptr[j*optctx->bpr+i*2]; if(ptr[1]==0) { // Transparent pixel trns_mask[j*optctx->width+i] = 0; continue; } else { // Nontransparent pixel trns_mask[j*optctx->width+i] = 1; } clr_used[(int)ptr[0]] = 1; } } if(!iwopt_find_unused(clr_used,256,&key_clr)) { goto done; } // Strip the alpha channel: iw_opt_copychannels_8(ctx,optctx,IW_IMGTYPE_GRAY,0,0,0); if(!optctx->tmp_pixels) goto done; // Change the color of all transparent pixels to the key color for(j=0;j<optctx->height;j++) { for(i=0;i<optctx->width;i++) { ptr2 = &optctx->tmp_pixels[j*optctx->bpr+i]; if(trns_mask[j*optctx->width+i]==0) { ptr2[0] = key_clr; } } } optctx->has_colorkey_trns = 1; optctx->colorkey_r = key_clr; optctx->colorkey_g = key_clr; optctx->colorkey_b = key_clr; done: if(trns_mask) iw_free(trns_mask); }
static void iwwebp_gray_to_rgb(struct iwwebpwritecontext *wctx) { int i,j; struct iw_image *img = wctx->img; size_t bpr; unsigned char g; bpr = 3 * img->width; wctx->tmppixels = iw_malloc_large(wctx->ctx, img->height, bpr); if(!wctx) return; for(j=0;j<img->height;j++) { for(i=0;i<img->width;i++) { g = img->pixels[j*img->bpr+i]; wctx->tmppixels[j*bpr+3*i+0]=g; wctx->tmppixels[j*bpr+3*i+1]=g; wctx->tmppixels[j*bpr+3*i+2]=g; } } }
static void iwopt_convert_to_palette_image(struct iw_context *ctx, struct iw_opt_ctx *optctx) { unsigned char *newpixels; size_t newbpr; int x,y; struct iw_rgba8color c; const unsigned char *ptr; int spp; int e; spp = iw_imgtype_num_channels(optctx->imgtype); newbpr = optctx->width; newpixels = iw_malloc_large(ctx, newbpr, optctx->height); if(!newpixels) return; for(y=0;y<optctx->height;y++) { for(x=0;x<optctx->width;x++) { ptr = &optctx->pixelsptr[y*optctx->bpr + x*spp]; if(optctx->imgtype==IW_IMGTYPE_RGB) { c.r = ptr[0]; c.g = ptr[1]; c.b = ptr[2]; c.a = 255; } else if(optctx->imgtype==IW_IMGTYPE_RGBA) { c.r = ptr[0]; c.g = ptr[1]; c.b = ptr[2]; c.a = ptr[3]; if(c.a==0) { c.r = c.g = c.b = 0; } } else if(optctx->imgtype==IW_IMGTYPE_GRAYA) { c.r = c.g = c.b = ptr[0]; c.a = ptr[1]; if(c.a==0) { c.r = c.g = c.b = 0; } } else { // optctx->imgtype==IW_IMGTYPE_GRAY(?) c.r = c.g = c.b = ptr[0]; c.a = 255; } if(optctx->has_colorkey_trns && c.a==0) { // We'll only get here if the image is really grayscale. e = optctx->colorkey_r; } else { e = iwopt_find_color(optctx->palette,&c); if(e<0) e=0; // shouldn't happen } newpixels[y*newbpr + x] = e; } } // Remove previous image if it was allocated by the optimization code. if(optctx->tmp_pixels) iw_free(optctx->tmp_pixels); // Attach our new image optctx->tmp_pixels = newpixels; optctx->pixelsptr = optctx->tmp_pixels; optctx->bpr = newbpr; optctx->bit_depth = 8; optctx->imgtype = IW_IMGTYPE_PALETTE; }
static void iwopt_try_rgb16_binary_trns(struct iw_context *ctx, struct iw_opt_ctx *optctx) { int i,j; const unsigned char *ptr; unsigned char *ptr2; unsigned char clr_used[256]; unsigned char key_clr; // low 8-bits of red component of the key color unsigned char *trns_mask = NULL; if(!(ctx->output_profile&IW_PROFILE_BINARYTRNS)) return; if(!ctx->opt_binary_trns) return; memset(&clr_used,0,256*sizeof(unsigned char)); trns_mask = iw_malloc_large(ctx, optctx->width, optctx->height); if(!trns_mask) goto done; for(j=0;j<optctx->height;j++) { for(i=0;i<optctx->width;i++) { ptr = &optctx->pixelsptr[j*optctx->bpr+(i*2)*4]; if(ptr[6]==0 && ptr[7]==0) { // Transparent pixel trns_mask[j*optctx->width+i] = 0; continue; } else { // Nontransparent pixel trns_mask[j*optctx->width+i] = 1; } // For the colors we look for, all bytes are 192 except possibly the low-red byte. if(ptr[0]!=192 || ptr[2]!=192 || ptr[3]!=192 || ptr[4]!=192 || ptr[5]!=192) continue; clr_used[(int)ptr[1]] = 1; } } if(!iwopt_find_unused(clr_used,256,&key_clr)) { goto done; } // Strip the alpha channel: iw_opt_copychannels_16(ctx,optctx,IW_IMGTYPE_RGB,0,1,2); if(!optctx->tmp_pixels) goto done; // Change the color of all transparent pixels to the key color for(j=0;j<optctx->height;j++) { for(i=0;i<optctx->width;i++) { ptr2 = &optctx->tmp_pixels[j*optctx->bpr+(i*2)*3]; if(trns_mask[j*optctx->width+i]==0) { ptr2[0] = 192; ptr2[1] = key_clr; ptr2[2] = 192; ptr2[3] = 192; ptr2[4] = 192; ptr2[5] = 192; } } } optctx->has_colorkey_trns = 1; optctx->colorkey_r = 192*256+key_clr; optctx->colorkey_g = 192*256+192; optctx->colorkey_b = 192*256+192; done: if(trns_mask) iw_free(trns_mask); }
// Try to convert from RGBA to RGB+binary trns. // Assumes we already know there is transparency, but no partial transparency. static void iwopt_try_rgb8_binary_trns(struct iw_context *ctx, struct iw_opt_ctx *optctx) { int i,j; const unsigned char *ptr; unsigned char *ptr2; unsigned char clr_used[256]; unsigned char key_clr; // Red component of the key color unsigned char *trns_mask = NULL; if(!(ctx->output_profile&IW_PROFILE_BINARYTRNS)) return; if(!ctx->opt_binary_trns) return; // Try to find a color that's not used in the image. // Looking for all 2^24 possible colors is too much work. // We will just look for 256 predefined colors: R={0-255},G=192,B=192 memset(&clr_used,0,256*sizeof(unsigned char)); // Hard to decide how to do this. I don't want the optimization phase // to modify img2.pixels, though that would be the easiest method. // Another option would be to make a version of iw_opt_copychannels_8() // that sets the transparent pixels to a certain value, but that would // get messy. // Instead, I'll make a transparency mask, then strip the alpha // channel, then use the mask to patch up the new image. trns_mask = iw_malloc_large(ctx, optctx->width, optctx->height); if(!trns_mask) goto done; for(j=0;j<optctx->height;j++) { for(i=0;i<optctx->width;i++) { ptr = &optctx->pixelsptr[j*optctx->bpr+i*4]; if(ptr[3]==0) { // transparent pixel trns_mask[j*optctx->width+i] = 0; // Remember which pixels are transparent. continue; } else { trns_mask[j*optctx->width+i] = 1; } if(ptr[1]!=192 || ptr[2]!=192) continue; clr_used[(int)ptr[0]] = 1; } } if(!iwopt_find_unused(clr_used,256,&key_clr)) { goto done; } // Strip the alpha channel: iw_opt_copychannels_8(ctx,optctx,IW_IMGTYPE_RGB,0,1,2); if(!optctx->tmp_pixels) goto done; // Change the color of all transparent pixels to the key color for(j=0;j<optctx->height;j++) { for(i=0;i<optctx->width;i++) { ptr2 = &optctx->tmp_pixels[j*optctx->bpr+i*3]; if(trns_mask[j*optctx->width+i]==0) { ptr2[0] = key_clr; ptr2[1] = 192; ptr2[2] = 192; } } } optctx->has_colorkey_trns = 1; optctx->colorkey_r = key_clr; optctx->colorkey_g = 192; optctx->colorkey_b = 192; done: if(trns_mask) iw_free(trns_mask); }
static void iwopt_try_gray16_binary_trns(struct iw_context *ctx, struct iw_opt_ctx *optctx) { int i,j; const iw_byte *ptr; iw_byte *ptr2; iw_byte clr_used[256]; iw_byte key_clr=0; // low 8-bits of the key color iw_byte *trns_mask = NULL; if(!(ctx->output_profile&IW_PROFILE_BINARYTRNS)) return; if(!ctx->opt_binary_trns) return; iw_zeromem(clr_used,256); trns_mask = iw_malloc_large(ctx, optctx->width, optctx->height); if(!trns_mask) goto done; for(j=0;j<optctx->height;j++) { for(i=0;i<optctx->width;i++) { ptr = &optctx->pixelsptr[j*optctx->bpr+(i*2)*2]; if(ptr[2]==0 && ptr[3]==0) { // Transparent pixel trns_mask[j*optctx->width+i] = 0; continue; } else { // Nontransparent pixel trns_mask[j*optctx->width+i] = 1; } // For the colors we look for, the high byte is always 192. if(ptr[0]!=192) continue; clr_used[(int)ptr[1]] = 1; } } if(!iwopt_find_unused(clr_used,256,&key_clr)) { goto done; } // Strip the alpha channel: iw_opt_copychannels_16(ctx,optctx,IW_IMGTYPE_GRAY,0,0,0); if(!optctx->tmp_pixels) goto done; // Change the color of all transparent pixels to the key color for(j=0;j<optctx->height;j++) { for(i=0;i<optctx->width;i++) { ptr2 = &optctx->tmp_pixels[j*optctx->bpr+(i*2)]; if(trns_mask[j*optctx->width+i]==0) { ptr2[0] = 192; ptr2[1] = key_clr; } } } optctx->has_colorkey_trns = 1; optctx->colorkey[IW_CHANNELTYPE_RED] = 192*256+key_clr; optctx->colorkey[IW_CHANNELTYPE_GREEN] = 192*256+key_clr; optctx->colorkey[IW_CHANNELTYPE_BLUE] = 192*256+key_clr; done: if(trns_mask) iw_free(ctx,trns_mask); }
static int iwwebp_read_main(struct iwwebpreadcontext *rctx) { struct iw_image *img; int retval=0; void *webpimage=NULL; size_t webpimage_size=0; uint8_t* uncmpr_webp_pixels = NULL; int width, height; size_t npixels; int bytes_per_pixel; img = rctx->img; // TODO: This is really memory-inefficient. // Honestly, I just can't figure out libwebp. // It has several different ways to decode a webp image, but they don't // seem to add up to any way to do it without allocating at least one // more image's worth of memory than ought to be necessary. // It's like it expects you to know the width, height, and color format // of the image before that information has been read from the file. // Read the whole WebP file into a memory block. if(!iw_file_to_memory(rctx->ctx, rctx->iodescr, &webpimage, &webpimage_size)) { goto done; } // Have libwebp decode that memory block, to a memory block that // it allocates. width=height=0; uncmpr_webp_pixels = WebPDecodeRGBA(webpimage, (uint32_t)webpimage_size, &width, &height); if(!uncmpr_webp_pixels) goto done; if(!iw_check_image_dimensons(rctx->ctx,width,height)) goto done; npixels = ((size_t)width)*height; // Figure out if the image has transparency, etc. iwwebp_scan_pixels(rctx,uncmpr_webp_pixels,npixels); // Choose the color format to use for IW's internal source image. if(rctx->has_color) img->imgtype = rctx->has_transparency ? IW_IMGTYPE_RGBA : IW_IMGTYPE_RGB; else img->imgtype = rctx->has_transparency ? IW_IMGTYPE_GRAYA : IW_IMGTYPE_GRAY; img->width = width; img->height = height; img->bit_depth = 8; bytes_per_pixel = iw_imgtype_num_channels(img->imgtype); img->bpr = bytes_per_pixel * img->width; img->pixels = (unsigned char*)iw_malloc_large(rctx->ctx, img->bpr, img->height); if(!img->pixels) goto done; switch(img->imgtype) { case IW_IMGTYPE_GRAY: iwwebpr_convert_pixels_gray(rctx,(unsigned char*)uncmpr_webp_pixels,npixels); break; case IW_IMGTYPE_GRAYA: iwwebpr_convert_pixels_graya(rctx,(unsigned char*)uncmpr_webp_pixels,npixels); break; case IW_IMGTYPE_RGB: iwwebpr_convert_pixels_rgb(rctx,(unsigned char*)uncmpr_webp_pixels,npixels); break; default: iwwebpr_convert_pixels_rgba(rctx,(unsigned char*)uncmpr_webp_pixels,npixels); break; } retval=1; done: if(webpimage) iw_free(webpimage); // Caution: We must use the right free() function: the one that corresponds // to the malloc function that libwebp used. But we can't always be sure // how to do that. It depends on how libwebp was compiled. if(uncmpr_webp_pixels) free(uncmpr_webp_pixels); return retval; }