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; }
// 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) { 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; } } }
void *iw_realloc(const char *file, unsigned int line, void *ptr, size_t size) { if(!iw_memory_tracking) { return realloc(ptr, size); } void *new_chunk = NULL; // TODO: To simplify realloc we just allocate a new memory chunk and free the // old one. if(size != 0) { new_chunk = iw_malloc(file, line, size); if(new_chunk == NULL) { // Realloc does not free or move the old memory if allocation fails. return NULL; } } if(ptr != NULL) { if(new_chunk != NULL) { void *len_ptr = ptr - PRE_GUARD_SIZE - LEN_SIZE; unsigned int len = ntohl(*(unsigned int *)len_ptr); memcpy(new_chunk, ptr, len); } iw_free(ptr); } return new_chunk; }
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 int iwwebp_write_main(struct iwwebpwritecontext *wctx) { struct iw_image *img; size_t ret; uint8_t *cmpr_webp_data = NULL; int retval=0; double quality; img = wctx->img; quality = iw_get_value_dbl(wctx->ctx,IW_VAL_WEBP_QUALITY); if(quality<0.0) { quality=80.0; // Default quality. } switch(img->imgtype) { case IW_IMGTYPE_GRAY: // IW requires encoders to support grayscale, but WebP doesn't (?) // support it. So, convert grayscale images to RGB. iwwebp_gray_to_rgb(wctx); // Allocates RGB image at wctx->tmppixels. if(!wctx->tmppixels) goto done; ret = WebPEncodeRGB(wctx->tmppixels, img->width, img->height, 3*img->width, (float)quality, &cmpr_webp_data); break; case IW_IMGTYPE_RGB: ret = WebPEncodeRGB(img->pixels, img->width, img->height, (int)img->bpr, (float)quality, &cmpr_webp_data); break; default: iw_seterror(wctx->ctx,iwwebp_get_string(wctx->ctx,iws_webp_enc_bad_imgtype),img->imgtype); goto done; } if(ret<1 || !cmpr_webp_data) { goto done; } iwwebp_write(wctx, cmpr_webp_data, ret); retval=1; done: if(cmpr_webp_data) free(cmpr_webp_data); if(wctx->tmppixels) iw_free(wctx->tmppixels); return 1; }
// Optimize to palette, or 1-, 2-, or 4-bpp grayscale. static void iwopt_try_pal_lowgray_optimization(struct iw_context *ctx, struct iw_opt_ctx *optctx) { int ret; int binary_trns; unsigned int trns_shade; if(!(ctx->output_profile&IW_PROFILE_PAL1) && !(ctx->output_profile&IW_PROFILE_PAL2) && !(ctx->output_profile&IW_PROFILE_PAL4) && !(ctx->output_profile&IW_PROFILE_PAL8) && !(ctx->output_profile&IW_PROFILE_GRAY1) && !(ctx->output_profile&IW_PROFILE_GRAY2) && !(ctx->output_profile&IW_PROFILE_GRAY4) ) { // Output format doesn't support anything that this optimization can provide. return; } if(optctx->bit_depth!=8) { // Palettes aren't supported with bitdepth>8. return; } optctx->palette = iw_malloc(ctx,sizeof(struct iw_palette)); if(!optctx->palette) return; optctx->palette->num_entries=0; ret = optctx_collect_palette_colors(ctx,optctx); if(!ret) { // Image can't be converted to a palette image. goto done; } // optctx->palette now contains a palette that can be used. // For images that have at most 256 (8-bit-compatible) colors, the order // of preference is gray1, pal1, gray2, pal2, gray4, pal4, gray8, pal8. if(ctx->opt_grayscale && (ctx->output_profile&IW_PROFILE_GRAY1) && iwopt_palette_is_valid_gray(ctx,optctx,1,&binary_trns,&trns_shade)) { // Replace the palette with a fully-populated grayscale palette. // The palette might already be correct, but it might not be. // It will be missing any gray shade that wasn't in the image. iwopt_make_gray_palette(ctx,optctx,1); if(binary_trns) { optctx->has_colorkey_trns = 1; optctx->colorkey_r = optctx->colorkey_b = optctx->colorkey_g = trns_shade; } } else if(iwopt_palette_opt_ok(ctx,optctx,1)) { ; } else if(ctx->opt_grayscale && (ctx->output_profile&IW_PROFILE_GRAY2) && iwopt_palette_is_valid_gray(ctx,optctx,2,&binary_trns,&trns_shade)) { iwopt_make_gray_palette(ctx,optctx,2); if(binary_trns) { optctx->has_colorkey_trns = 1; optctx->colorkey_r = optctx->colorkey_b = optctx->colorkey_g = trns_shade; } } else if(iwopt_palette_opt_ok(ctx,optctx,2)) { ; } else if(ctx->opt_grayscale && (ctx->output_profile&IW_PROFILE_GRAY4) && iwopt_palette_is_valid_gray(ctx,optctx,4,&binary_trns,&trns_shade)) { iwopt_make_gray_palette(ctx,optctx,4); if(binary_trns) { optctx->has_colorkey_trns = 1; optctx->colorkey_r = optctx->colorkey_b = optctx->colorkey_g = trns_shade; } } else if(iwopt_palette_opt_ok(ctx,optctx,4)) { ; } else if(ctx->opt_grayscale && (ctx->output_profile&IW_PROFILE_GRAYSCALE) && iwopt_palette_is_valid_gray(ctx,optctx,8,&binary_trns,&trns_shade)) { // This image can best be encoded as 8-bit grayscale. We don't handle that here. goto done; } else if(iwopt_palette_opt_ok(ctx,optctx,8)) { ; } else { // Found no optimizations that we can perform. goto done; } if(!optctx->palette_is_grayscale) { // Sort the palette qsort((void*)optctx->palette->entry,optctx->palette->num_entries, sizeof(struct iw_rgba8color),iwopt_palsortfunc); } iwopt_convert_to_palette_image(ctx,optctx); done: if(optctx->imgtype!=IW_IMGTYPE_PALETTE) { iw_free(optctx->palette); optctx->palette = NULL; } }
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; }