"Fossies" - the Fresh Open Source Software Archive

Member "littleutils-1.2.5/imageutils/libpngrecolor.c" (29 Oct 2021, 24080 Bytes) of package /linux/privat/littleutils-1.2.5.tar.lz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "libpngrecolor.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.2.4_vs_1.2.5.

    1 // libpngrecolor.c - part of pngrecolor
    2 // Copyright (C) 2010 by Jason Summers (from original libpngrewrite.c)
    3 // Modifications (C) 2013-2021 by Brian Lindholm
    4 //   [1] Renamed from "pngrewrite" to "pngrecolor" at avoid confusion with
    5 //       original program.
    6 //   [2] Modified palette comparison routine to ensure a *unique* sorting.
    7 //   [3] Added command-line options "-n" and "-q" for no-interlacing and quiet
    8 //       output.
    9 //   These modifications may be used without restriction.
   10 
   11 #include <config.h>
   12 
   13 #if defined(_WIN32) && !defined(__GNUC__) && !defined(PNGRW_WINDOWS)
   14 # define PNGRW_WINDOWS
   15 #endif
   16 
   17 #ifdef PNGRW_WINDOWS
   18 # include <search.h> /* for qsort */
   19 # include <strsafe.h>
   20 # include <tchar.h>
   21 #endif
   22 
   23 #include <stdarg.h>
   24 #ifdef HAVE_STDIO_H
   25 # include <stdio.h>
   26 #endif
   27 #ifdef HAVE_STDLIB_H
   28 # include <stdlib.h>
   29 #endif
   30 #ifdef HAVE_STRING_H
   31 # include <string.h>
   32 #endif
   33 
   34 #include <png.h>
   35 
   36 #include "libpngrecolor.h"
   37 
   38 #ifdef PNGRW_WINDOWS
   39 # define PNGRW_TEXT _T
   40 #else
   41 # define PNGRW_TEXT(x) x
   42 # define _tfopen fopen
   43 # define _ftprintf fprintf
   44 #endif
   45 
   46 #define PNGREWRITEVERSION  PNGRW_TEXT("1.4.0")
   47 
   48 struct errstruct {
   49     jmp_buf jbuf;
   50     PNGRW_CHAR errmsg[200];
   51 };
   52 
   53 struct pal_entry_info {
   54     unsigned char red;
   55     unsigned char green;
   56     unsigned char blue;
   57     unsigned char alpha;
   58     unsigned int  count;
   59 };
   60 
   61 struct pngrw_ctx {
   62 
   63     pngrw_print_fn_type printfn;
   64     pngrw_print_fn_type errorfn;
   65     void *userdata;
   66     int pal_sort_by_frequency;
   67 
   68     struct pal_entry_info pal[256];
   69     int pal_used;
   70     int new_bit_depth;
   71     int valid_gray;
   72     int gray_trns_orig_palentry;
   73     int gray_trns_target_palentry; // index into the grayscale fake palette
   74     unsigned char gray_trns_shade;
   75     struct pal_entry_info bkgd;             /* RGB background color */
   76     unsigned char bkgd_pal;    /* new background color palette entry */
   77     int ori_pal_size;
   78 
   79     unsigned char *image1, *image2;
   80     unsigned char **row_pointers1;
   81     unsigned char **row_pointers2;
   82     int rowbytes, channels;
   83 
   84     png_uint_32 width, height;
   85     int bit_depth, color_type, interlace_type;
   86 
   87     int has_gAMA;
   88     int has_bKGD;
   89     int has_sRGB;
   90     int has_tIME;
   91     int has_pHYs;
   92 
   93     png_time savechunk_time; /*  S.A. */
   94     double image_gamma;
   95     int srgb_intent;
   96     png_uint_32 res_x,res_y;
   97     int res_unit_type;
   98 
   99     int prev_entry;
  100     int prev_entry_valid;
  101     unsigned char prev_r;
  102     unsigned char prev_g;
  103     unsigned char prev_b;
  104     unsigned char prev_a;
  105 
  106     PNGRW_CHAR errmsg[200];
  107 };
  108 
  109 static void pngrw_StringCchCopy(PNGRW_CHAR *dst, int dst_len, const PNGRW_CHAR *src)
  110 {
  111 #ifdef PNGRW_WINDOWS
  112     StringCchCopy(dst,dst_len,src);
  113 #else
  114     strncpy(dst,src,dst_len);
  115     dst[dst_len-1]='\0';
  116 #endif
  117 }
  118 
  119 static void pngrw_StringCchVPrintf(PNGRW_CHAR *dst, int dst_len, const PNGRW_CHAR *fmt, va_list ap)
  120 {
  121 #ifdef PNGRW_WINDOWS
  122     StringCchVPrintf(dst,dst_len,fmt,ap);
  123 #else
  124     vsnprintf(dst,dst_len,fmt,ap);
  125     dst[dst_len-1]='\0';
  126 #endif
  127 }
  128 
  129 static void pngrw_StringCchPrintf(PNGRW_CHAR *dst, int dst_len, const PNGRW_CHAR *fmt, ...)
  130 {
  131     va_list ap;
  132 
  133     va_start(ap, fmt);
  134     pngrw_StringCchVPrintf(dst,dst_len,fmt,ap);
  135     va_end(ap);
  136 }
  137 
  138 
  139 static void pngrw_print_error(struct pngrw_ctx *ctx, const PNGRW_CHAR *fmt, ...)
  140 {
  141     va_list ap;
  142     PNGRW_CHAR buf[1000];
  143 
  144     if(!ctx->errorfn) return;
  145     va_start(ap, fmt);
  146     pngrw_StringCchVPrintf(buf,1000,fmt,ap);
  147     va_end(ap);
  148     (*ctx->errorfn)(ctx,buf);
  149 }
  150 
  151 static void pngrw_print_info(struct pngrw_ctx *ctx, const PNGRW_CHAR *fmt, ...)
  152 {
  153     va_list ap;
  154     PNGRW_CHAR buf[1000];
  155 
  156     if(!ctx->printfn) return;
  157     va_start(ap, fmt);
  158     pngrw_StringCchVPrintf(buf,1000,fmt,ap);
  159     va_end(ap);
  160     (*ctx->printfn)(ctx,buf);
  161 }
  162 
  163 static void my_png_error_fn(png_structp png_ptr, const char *err_msg)
  164 {
  165     struct errstruct *errinfop;
  166 
  167     errinfop = (struct errstruct *)png_get_error_ptr(png_ptr);
  168 
  169     pngrw_StringCchPrintf(errinfop->errmsg,200,PNGRW_TEXT("%s"),err_msg);
  170 
  171     longjmp(errinfop->jbuf, -1);
  172 }
  173 
  174 static void my_png_warning_fn(png_structp png_ptr, const char *warn_msg)
  175 {
  176     return;
  177 }
  178 
  179 static void pngrw_read_tIME_chunk(struct pngrw_ctx *ctx,
  180     png_structp png_ptr, png_infop info_ptr)
  181 {
  182     png_timep in_time;  /*  a struct POINTER  */
  183 
  184     /* S.A.  .................................  */
  185     if(!ctx->has_tIME &&
  186         png_get_valid(png_ptr,info_ptr,PNG_INFO_tIME))
  187     {
  188         if(png_get_tIME(png_ptr, info_ptr, &in_time) == PNG_INFO_tIME) {
  189             ctx->savechunk_time = *in_time;
  190             ctx->has_tIME = 1;
  191         }
  192     }
  193 }
  194 
  195 static void pngrw_record_ancillary_chunks_beforeimage(struct pngrw_ctx *ctx,
  196     png_structp png_ptr, png_infop info_ptr)
  197 {
  198     if (png_get_gAMA(png_ptr, info_ptr, &ctx->image_gamma)) {
  199         ctx->has_gAMA=1;
  200     }
  201 
  202     if (png_get_sRGB(png_ptr, info_ptr, &ctx->srgb_intent)) {
  203         ctx->has_sRGB=1;
  204     }
  205 
  206     if (png_get_valid(png_ptr,info_ptr,PNG_INFO_pHYs)) {
  207         ctx->has_pHYs=1;
  208         png_get_pHYs(png_ptr,info_ptr,&ctx->res_x,&ctx->res_y,&ctx->res_unit_type);
  209         if(ctx->res_x<1 || ctx->res_y<1) ctx->has_pHYs=0;
  210     }
  211 
  212     pngrw_read_tIME_chunk(ctx,png_ptr,info_ptr);
  213 }
  214 
  215 static void pngrw_record_ancillary_chunks_afterimage(struct pngrw_ctx *ctx,
  216     png_structp png_ptr, png_infop info_ptr)
  217 {
  218     pngrw_read_tIME_chunk(ctx,png_ptr,info_ptr);
  219 }
  220 
  221 static int pngrw_read_png(struct pngrw_ctx *ctx, FILE *infp, int verbose)
  222 {
  223     png_structp png_ptr = NULL;
  224     png_infop info_ptr = NULL;
  225     png_colorp ori_pal;
  226     int ori_bpp;
  227     struct errstruct errinfo;
  228     int retval = 0;
  229     int i;
  230     png_color_16 *image_background;
  231 
  232     pngrw_StringCchCopy(errinfo.errmsg,200,PNGRW_TEXT(""));
  233 
  234     png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
  235         (void*)(&errinfo),my_png_error_fn, my_png_warning_fn);
  236 
  237     if(!png_ptr) {
  238         pngrw_print_error(ctx,PNGRW_TEXT("pngrecolor error: Error creating read_struct"));
  239         goto done;
  240     }
  241 
  242     info_ptr = png_create_info_struct(png_ptr);
  243     if(!info_ptr) {
  244         pngrw_print_error(ctx,PNGRW_TEXT("pngrecolor error: Error creating read_info_struct"));
  245         goto done;
  246     }
  247 
  248     if (setjmp(errinfo.jbuf)) {
  249         pngrw_print_error(ctx,PNGRW_TEXT("pngrecolor error: PNG decode error: %s"), errinfo.errmsg);
  250         goto done;
  251     }
  252 
  253     png_init_io(png_ptr, infp);
  254 
  255     png_read_info(png_ptr, info_ptr);
  256 
  257     png_get_IHDR(png_ptr, info_ptr, &ctx->width, &ctx->height, &ctx->bit_depth, &ctx->color_type,
  258         &ctx->interlace_type, NULL, NULL);
  259 
  260     if(ctx->bit_depth>8) {
  261         // TODO: Some (rare?) 16-bpp images *can* be perfectly converted to palette images.
  262         pngrw_print_error(ctx,PNGRW_TEXT("pngrecolor warning: This image can't be converted because it has 16 bits/sample."));
  263         goto done;
  264     }
  265 
  266 
  267     if (ctx->color_type == PNG_COLOR_TYPE_PALETTE) {
  268         ctx->ori_pal_size=0;
  269         ori_bpp = ctx->bit_depth;
  270 
  271         /* we only do this to get the palette size so we can print it */
  272         png_get_PLTE(png_ptr,info_ptr,&ori_pal,&ctx->ori_pal_size);
  273 
  274         if (verbose) {
  275             pngrw_print_info(ctx, PNGRW_TEXT("original palette size:   %3d, %2d bpp"),ctx->ori_pal_size,ori_bpp);
  276         }
  277 
  278         png_set_expand(png_ptr);
  279     }
  280     else {
  281         /* figure out bits per pixel so we can print it */
  282         ori_bpp= ctx->bit_depth*png_get_channels(png_ptr,info_ptr);
  283         if (verbose) {
  284             pngrw_print_info(ctx, PNGRW_TEXT("original palette size: [n/a], %2d bpp"),ori_bpp);
  285         }
  286     }
  287 
  288     if (ctx->color_type == PNG_COLOR_TYPE_GRAY && ctx->bit_depth < 8)
  289         png_set_expand(png_ptr);
  290     if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
  291         png_set_expand(png_ptr);
  292     /* if (bit_depth == 16)
  293         png_set_strip_16(png_ptr); */
  294     if (ctx->color_type == PNG_COLOR_TYPE_GRAY ||
  295         ctx->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
  296         png_set_gray_to_rgb(png_ptr);
  297     }
  298 
  299     if (png_get_bKGD(png_ptr, info_ptr, &image_background)) {
  300         ctx->has_bKGD=1;
  301         ctx->bkgd.red=   (unsigned char)image_background->red;
  302         ctx->bkgd.green= (unsigned char)image_background->green;
  303         ctx->bkgd.blue=  (unsigned char)image_background->blue;
  304         ctx->bkgd.alpha=255;
  305     }
  306 
  307     pngrw_record_ancillary_chunks_beforeimage(ctx,png_ptr,info_ptr);
  308 
  309     png_read_update_info(png_ptr, info_ptr);
  310 
  311     ctx->rowbytes=png_get_rowbytes(png_ptr, info_ptr);
  312     ctx->channels=(int)png_get_channels(png_ptr, info_ptr);
  313 
  314     if(ctx->channels<3 || ctx->channels>4) {
  315         pngrw_print_error(ctx,PNGRW_TEXT("pngrecolor error: internal error: channels=%d"),ctx->channels);
  316         goto done;
  317     }
  318 
  319     if (verbose) {
  320         pngrw_print_info(ctx,PNGRW_TEXT("pngrecolor message: Image size is %dx%d  memory required=%.3fMB"),
  321             (int)ctx->width, (int)ctx->height,
  322             (ctx->height*ctx->rowbytes + ctx->height*sizeof(unsigned char*) + ctx->width*ctx->height) / (1024. * 1024.) );
  323     }
  324 
  325     ctx->image1= (unsigned char*)malloc(ctx->height*ctx->rowbytes);
  326     ctx->row_pointers1 = (unsigned char**)malloc(ctx->height * sizeof(unsigned char*));
  327     if(!ctx->image1 || !ctx->row_pointers1) {
  328         pngrw_print_error(ctx,PNGRW_TEXT("pngrecolor error: Unable to allocate memory for image"));
  329         goto done;
  330     }
  331 
  332     for(i=0;i<(int)ctx->height;i++) {
  333         ctx->row_pointers1[i] = &ctx->image1[ctx->rowbytes*i];
  334     }
  335 
  336     png_read_image(png_ptr, ctx->row_pointers1);
  337 
  338     png_read_end(png_ptr, info_ptr);
  339 
  340     pngrw_record_ancillary_chunks_afterimage(ctx,png_ptr,info_ptr);
  341 
  342     retval = 1;
  343 
  344 done:
  345     if(png_ptr) {
  346         png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
  347     }
  348 
  349     return retval;
  350 }
  351 
  352 static void pngrw_write_ancillary_chunks(struct pngrw_ctx *ctx,
  353     png_structp png_ptr, png_infop info_ptr)
  354 {
  355     if(ctx->has_gAMA) png_set_gAMA(png_ptr, info_ptr, ctx->image_gamma);
  356     if(ctx->has_sRGB) png_set_sRGB(png_ptr, info_ptr, ctx->srgb_intent);
  357     if(ctx->has_pHYs) png_set_pHYs(png_ptr, info_ptr, ctx->res_x, ctx->res_y, ctx->res_unit_type);
  358 
  359     /*  S.A.  ............................  */
  360     if(ctx->has_tIME) {
  361         png_set_tIME(png_ptr, info_ptr, &ctx->savechunk_time);
  362     }
  363 }
  364 
  365 static int pngrw_write_new_png(struct pngrw_ctx *ctx, FILE *outfp, int no_interlace)
  366 {
  367     png_structp png_ptr = NULL;
  368     png_infop  info_ptr = NULL;
  369     png_color palette[256];
  370     unsigned char trans[256];
  371     int num_trans;
  372     int i;
  373     png_color_16 newtrns;
  374     png_color_16 newbackground;
  375     struct errstruct errinfo;
  376     int retval = 0;
  377 
  378     memset(&newtrns      ,0,sizeof(png_color_16));
  379     memset(&newbackground,0,sizeof(png_color_16));
  380 
  381     pngrw_StringCchCopy(errinfo.errmsg,200,PNGRW_TEXT(""));
  382 
  383     png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
  384         (void*)(&errinfo),my_png_error_fn, my_png_warning_fn);
  385     if (!png_ptr) {
  386         pngrw_print_error(ctx,PNGRW_TEXT("pngrecolor error: Error creating write_struct"));
  387         goto done;
  388     }
  389 
  390     info_ptr = png_create_info_struct(png_ptr);
  391     if (!info_ptr) {
  392         pngrw_print_error(ctx,PNGRW_TEXT("pngrecolor error: Error creating write_info_struct"));
  393         goto done;
  394     }
  395 
  396     if (setjmp(errinfo.jbuf)) {
  397         pngrw_print_error(ctx,PNGRW_TEXT("pngrecolor error: PNG encode error: %s"), errinfo.errmsg);
  398         goto done;
  399     }
  400 
  401     png_set_compression_level(png_ptr,9);
  402     png_set_compression_buffer_size(png_ptr, 1024*1024);
  403     png_init_io(png_ptr, outfp);
  404     if (no_interlace) {
  405         ctx->interlace_type = 0;
  406     }
  407 
  408     if( ctx->valid_gray ) {
  409         png_set_IHDR(png_ptr, info_ptr, ctx->width, ctx->height, ctx->new_bit_depth,
  410              PNG_COLOR_TYPE_GRAY, ctx->interlace_type,
  411              PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
  412 
  413         if(ctx->gray_trns_target_palentry>=0) {
  414             newtrns.gray = ctx->gray_trns_target_palentry;
  415             png_set_tRNS(png_ptr, info_ptr, NULL, 1, &newtrns);
  416         }
  417 
  418     }
  419     else {
  420         png_set_IHDR(png_ptr, info_ptr, ctx->width, ctx->height, ctx->new_bit_depth,
  421              PNG_COLOR_TYPE_PALETTE, ctx->interlace_type,
  422              PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
  423 
  424         /* ... set palette colors ... */
  425 
  426         num_trans=0;  /* number of (fully or partially) transparent palette entries */
  427 
  428         for(i=0;i<ctx->pal_used;i++) {
  429             palette[i].red=ctx->pal[i].red;
  430             palette[i].green=ctx->pal[i].green;
  431             palette[i].blue=ctx->pal[i].blue;
  432 
  433             trans[i]=ctx->pal[i].alpha;
  434             if(trans[i]<255) num_trans=i+1;
  435         }
  436         png_set_PLTE(png_ptr, info_ptr, palette, ctx->pal_used);
  437 
  438         if(num_trans>0) {
  439             png_set_tRNS(png_ptr, info_ptr, trans, num_trans, 0);
  440         }
  441     }
  442 
  443     if(ctx->has_bKGD) {
  444         if(ctx->valid_gray)
  445             newbackground.gray = ctx->bkgd_pal;
  446         else
  447             newbackground.index = ctx->bkgd_pal;
  448         png_set_bKGD(png_ptr, info_ptr, &newbackground);
  449     }
  450     
  451     pngrw_write_ancillary_chunks(ctx,png_ptr,info_ptr);
  452 
  453     png_write_info(png_ptr, info_ptr);
  454 
  455     png_set_packing(png_ptr);
  456 
  457     png_write_image(png_ptr, ctx->row_pointers2);
  458 
  459     png_write_end(png_ptr, info_ptr);
  460 
  461     retval = 1;
  462 
  463 done:
  464 
  465     if(png_ptr) {
  466         png_destroy_write_struct(&png_ptr, &info_ptr);
  467     }
  468     return retval;
  469 }
  470 
  471 
  472 /* set ignorealpha to 1 if you don't care if the alpha value matches
  473  * (for the background) */
  474 static int
  475 add_to_palette(struct pngrw_ctx *ctx, const struct pal_entry_info *color, int ignorealpha, int verbose)
  476 {
  477     if(ctx->pal_used>=256) {
  478         if (verbose) {
  479             pngrw_print_error(ctx,PNGRW_TEXT("pngrecolor warning: This image can't be converted because it has more than 256 colors."));
  480         }
  481         return 0;
  482     }
  483     ctx->pal[ctx->pal_used].red   = color->red;
  484     ctx->pal[ctx->pal_used].green = color->green;
  485     ctx->pal[ctx->pal_used].blue  = color->blue;
  486     ctx->pal[ctx->pal_used].alpha = ignorealpha?255:color->alpha;
  487     ctx->pal[ctx->pal_used].count = 1;
  488     ctx->pal_used++;
  489     return 1;
  490 }
  491 
  492 #if 0
  493 static void debug_print_pal(struct pngrw_ctx *ctx)
  494 {
  495     int i;
  496     for(i=0;i<ctx->pal_used;i++) {
  497         _ftprintf(stderr, PNGRW_TEXT("%d. %d %d %d %d\n"),i,
  498             ctx->pal[i].red,ctx->pal[i].green,ctx->pal[i].blue,ctx->pal[i].alpha);
  499     }
  500 }
  501 #endif
  502 
  503 
  504 /* Sort the palette to put transparent colors first,
  505  * then sort by how frequently the color is used, then by dark to
  506  * light on R+G+B, then G, then B, then R.   Sorting by frequency
  507  * will often help the png filters make the image more compressible.
  508  * It also makes it easier for people to see which colors aren't used much
  509  * and allow them to manually reduce the color palette. */
  510 static int palsortfunc_internal(const void* p1, const void* p2, int byfreq)
  511 {
  512     struct pal_entry_info *e1,*e2;
  513     int s1,s2;
  514 
  515     e1=(struct pal_entry_info*)p1;
  516     e2=(struct pal_entry_info*)p2;
  517 
  518     if(e1->alpha==255 && e2->alpha<255) return 1;
  519     if(e1->alpha<255 && e2->alpha==255) return -1;
  520 
  521     if(byfreq) {
  522         if(e1->count<e2->count) return 1;
  523         if(e1->count>e2->count) return -1;
  524     }
  525 
  526     s1=e1->red+e1->green+e1->blue;
  527     s2=e2->red+e2->green+e2->blue;
  528     if(s1>s2) return 1;
  529     if(s1<s2) return -1;
  530     if(e1->green>e2->green) return 1;
  531     if(e1->green<e2->green) return -1;
  532     if(e1->blue>e2->blue) return 1;
  533     if(e1->blue<e2->blue) return -1;
  534     if(e1->red>e2->red) return 1;
  535     if(e1->red<e2->red) return -1;
  536     return 0;
  537 }
  538 
  539 static int palsortfunc(const void* p1, const void* p2)
  540 {
  541     return palsortfunc_internal(p1,p2,0);
  542 }
  543 
  544 static int palsortfunc_byfreq(const void* p1, const void* p2)
  545 {
  546     return palsortfunc_internal(p1,p2,1);
  547 }
  548 
  549 static void reset_pal_entry_cache(struct pngrw_ctx *ctx)
  550 {
  551     ctx->prev_entry_valid = 0;
  552 }
  553 
  554 #define PNGRW_ALPHA_MUST_MATCH  0
  555 #define PNGRW_IGNORE_ALPHA      1
  556 
  557 static int find_pal_entry(struct pngrw_ctx *ctx, unsigned char r,
  558      unsigned char g, unsigned char b, unsigned char a, int ignorealpha)
  559 {
  560     int i;
  561 
  562     if(ctx->prev_entry_valid && ctx->prev_r==r && ctx->prev_g==g  &&
  563        ctx->prev_b==b && ctx->prev_a==a)
  564     {
  565         return ctx->prev_entry;
  566     }
  567 
  568     for(i=0;i<ctx->pal_used;i++) {
  569         if(ctx->pal[i].red==r && ctx->pal[i].green==g && ctx->pal[i].blue==b
  570            && (ctx->pal[i].alpha==a || ignorealpha)) {
  571 
  572             ctx->prev_r = r;
  573             ctx->prev_g = g;
  574             ctx->prev_b = b;
  575             ctx->prev_a = a;
  576             ctx->prev_entry = i;
  577             
  578             return i;
  579         }
  580     }
  581     return (-1);
  582 }
  583 
  584 // Same as find_pal_entry(), but with different parameters.
  585 static int find_pal_entry_p(struct pngrw_ctx *ctx, struct pal_entry_info *e,
  586                             int ignorealpha)
  587 {
  588     return find_pal_entry(ctx,e->red,e->green,e->blue,e->alpha,ignorealpha);
  589 }
  590 
  591 // Call this when encountering a pixel or background color.
  592 // Adds the color to the palette if necessary, and does other
  593 // bookkeeping.
  594 static int pngrw_process_new_color(struct pngrw_ctx *ctx, struct pal_entry_info *e,
  595         int ignorealpha, int verbose)
  596 {
  597     int palent;
  598 
  599     palent = find_pal_entry_p(ctx,e,ignorealpha);
  600     if(palent<0) {
  601         if(!add_to_palette(ctx,e,ignorealpha,verbose))
  602             return 0;
  603     }
  604     else
  605         ctx->pal[palent].count++;
  606 
  607     return 1;
  608 }
  609 
  610 // Scans the pixels to figure out what colors are used, and
  611 // adds the colors found to the target palette.
  612 // Returns 0 on failure (e.g. too many colors for the palette).
  613 static int pngrw_process_pixels(struct pngrw_ctx *ctx, int verbose)
  614 {
  615     int x,y;
  616     unsigned char *p;
  617     struct pal_entry_info thispix;
  618 
  619     for(y=0;y<(int)ctx->height;y++) {
  620         for(x=0;x<(int)ctx->width;x++) {
  621             p=&ctx->row_pointers1[y][x*ctx->channels];
  622             thispix.red=p[0];
  623             thispix.green=p[1];
  624             thispix.blue=p[2];
  625             if(ctx->channels==4) thispix.alpha=p[3];
  626             else thispix.alpha=255;
  627 
  628             if(!pngrw_process_new_color(ctx,&thispix,0,verbose)) return 0;
  629         }
  630     }
  631     return 1;
  632 }
  633 
  634 static int pngrw_process_bkgd_color(struct pngrw_ctx *ctx, int verbose)
  635 {
  636     if(!ctx->has_bKGD) return 1;
  637     if(!pngrw_process_new_color(ctx,&ctx->bkgd,1,verbose)) return 0;
  638     return 1;
  639 }
  640 
  641 // Figure out if this is a valid grayscale image.
  642 // If it is returns nonzero, and may set ctx->gray_trns_orig_palentry.
  643 static int pngrw_is_valid_grayscale(struct pngrw_ctx *ctx)
  644 {
  645     int i;
  646     unsigned char gray_shade; // Gray shade of the palette entry being examined.
  647 
  648     ctx->gray_trns_orig_palentry = -1;
  649 
  650     for( i = 0; i < ctx->pal_used; i++ ) {
  651 
  652         if( ctx->pal[i].red != ctx->pal[i].green
  653            || ctx->pal[i].red != ctx->pal[i].blue)
  654         {
  655             // Found a pixel that's not gray.
  656             return 0;
  657         }
  658 
  659         gray_shade = ctx->pal[i].red;
  660 
  661         // Check for partial transparency, which isn't supported in grayscale images
  662         // (we don't support writing grayscale+alpha images).
  663         if(ctx->pal[i].alpha!=255 && ctx->pal[i].alpha!=0) {
  664             return 0;
  665         }
  666 
  667         // Check for binary transparency.
  668         if(ctx->pal[i].alpha == 0) {
  669             if(ctx->gray_trns_orig_palentry != -1) {
  670                 // Multiple transparent colors.
  671                 return 0;
  672             }
  673             ctx->gray_trns_orig_palentry = i;  // binary transparency ok (so far)
  674             ctx->gray_trns_shade = gray_shade;
  675         }
  676 
  677         // Check if the gray shade is one that can be represented at the
  678         // target bit depth.
  679         switch(ctx->new_bit_depth) {
  680         case 1:
  681             if(gray_shade%255) return 0;
  682             break;
  683         case 2:
  684             if(gray_shade%85) return 0;
  685             break;
  686         case 4:
  687             if(gray_shade%17) return 0;
  688         }
  689     }
  690 
  691     // One thing the above doesn't check for is a nontransparent
  692     // grayscale pixel that's the same as the transparent grayscale color.
  693     // In this case we have to use palette color.
  694     if(ctx->gray_trns_orig_palentry != -1) {
  695         if(-1 != find_pal_entry(ctx,ctx->gray_trns_shade,ctx->gray_trns_shade,ctx->gray_trns_shade,255,PNGRW_ALPHA_MUST_MATCH)) {
  696             return 0;
  697         }
  698     }
  699     return 1;
  700 }
  701 
  702 static int pngrw_create_grayscale_palette(struct pngrw_ctx *ctx)
  703 {
  704     int i;
  705     int step=1;
  706 
  707     switch(ctx->new_bit_depth) {
  708     case 1:  step=255; ctx->pal_used=2;   break;
  709     case 2:  step=85;  ctx->pal_used=4;   break;
  710     case 4:  step=17;  ctx->pal_used=16;  break;
  711     default: step=1;   ctx->pal_used=256;
  712     }
  713 
  714     for(i=0;i<ctx->pal_used;i++) {
  715         ctx->pal[i].red = ctx->pal[i].green = ctx->pal[i].blue = i*step;
  716         ctx->pal[i].alpha = 255;
  717     }
  718 
  719     // Translate the binary transparency setting to the new palette.
  720     if(ctx->gray_trns_orig_palentry != -1) {
  721         ctx->gray_trns_target_palentry=find_pal_entry(ctx,ctx->gray_trns_shade,ctx->gray_trns_shade,ctx->gray_trns_shade,255,PNGRW_ALPHA_MUST_MATCH);
  722         if(ctx->gray_trns_target_palentry == -1) {
  723             pngrw_print_error(ctx,PNGRW_TEXT("pngrecolor error: internal error: can't find transparent grayscale color"));
  724             return 0;
  725         }
  726         ctx->pal[ctx->gray_trns_target_palentry].alpha = 0;
  727     }
  728 
  729     return 1;
  730 }
  731 
  732 // The core function that creates the new image.
  733 // Allocates and writes to ctx->image2, and sets ctx->bkgd_pal.
  734 static int pngrw_generate_optimized_image(struct pngrw_ctx *ctx)
  735 {
  736     unsigned char *p;
  737     int x,y;
  738     int palent;
  739 
  740     ctx->image2 = (unsigned char*)malloc(ctx->width*ctx->height);
  741     ctx->row_pointers2 = (unsigned char**)malloc(ctx->height * sizeof(unsigned char*));
  742     if(!ctx->image2 || !ctx->row_pointers2) {
  743         pngrw_print_error(ctx,PNGRW_TEXT("pngrecolor error: out of memory"));
  744         return 0;
  745     }
  746 
  747     for(y=0;y<(int)ctx->height;y++) {
  748         ctx->row_pointers2[y]= &ctx->image2[y*ctx->width];
  749     }
  750 
  751     for(y=0;y<(int)ctx->height;y++) {
  752         for(x=0;x<(int)ctx->width;x++) {
  753             p=&ctx->row_pointers1[y][x*ctx->channels];
  754 
  755             palent = find_pal_entry(ctx,p[0],p[1],p[2],(unsigned char)((ctx->channels==4)?p[3]:255),PNGRW_ALPHA_MUST_MATCH);
  756             if(palent<0) {
  757                 pngrw_print_error(ctx,PNGRW_TEXT("pngrecolor error: internal error: can't locate palette entry"));
  758                 return 0;
  759             }
  760             ctx->row_pointers2[y][x] = (unsigned char)palent;
  761         }
  762     }
  763 
  764     if(ctx->has_bKGD) {
  765         palent = find_pal_entry_p(ctx,&ctx->bkgd,PNGRW_IGNORE_ALPHA);
  766         if(palent<0) {
  767             pngrw_print_error(ctx,PNGRW_TEXT("pngrecolor error: internal error: can't locate palette entry for bkgd"));
  768             return 0;
  769         }
  770         ctx->bkgd_pal = (unsigned char)palent;
  771     }
  772 
  773     return 1;
  774 }
  775 int pngrw_optimize_image(struct pngrw_ctx *ctx, int verbose)
  776 {
  777     ctx->gray_trns_target_palentry = -1;
  778     ctx->gray_trns_shade=0;
  779     ctx->pal_used=0;
  780 
  781     if(!pngrw_process_pixels(ctx,verbose)) return 0;
  782 
  783     if(!pngrw_process_bkgd_color(ctx,verbose)) return 0;
  784 
  785     /* Decide on the target bit depth. */
  786     ctx->new_bit_depth=8;
  787     if(ctx->pal_used<=16) ctx->new_bit_depth=4;
  788     if(ctx->pal_used<=4) ctx->new_bit_depth=2;
  789     if(ctx->pal_used<=2) ctx->new_bit_depth=1;
  790 
  791     /* figure out if this is a valid grayscale image */
  792     ctx->valid_gray = pngrw_is_valid_grayscale(ctx);
  793 
  794     if(ctx->valid_gray) {
  795         // If grayscale, create a "fake" palette consisting of all
  796         // available gray shades.
  797         if(!pngrw_create_grayscale_palette(ctx)) return 0;
  798 
  799     }
  800     else {
  801         // If color, put the palette in a good order.
  802         qsort((void*)ctx->pal,ctx->pal_used,sizeof(struct pal_entry_info),
  803             ctx->pal_sort_by_frequency?palsortfunc_byfreq:palsortfunc);
  804     }
  805 
  806     if (verbose) {
  807         if(ctx->valid_gray)
  808             pngrw_print_info(ctx, PNGRW_TEXT("saving as grayscale:          %2d bpp"),ctx->new_bit_depth);
  809         else
  810             pngrw_print_info(ctx, PNGRW_TEXT("new palette size:        %3d, %2d bpp"),ctx->pal_used,ctx->new_bit_depth);
  811     }
  812 
  813     reset_pal_entry_cache(ctx);
  814 
  815     /* debug_print_pal(ctx); */
  816 
  817     if(!pngrw_generate_optimized_image(ctx)) return 0;
  818 
  819     return 1;
  820 }
  821 
  822 // Sets default values for everything.
  823 static void init_ctx(struct pngrw_ctx *ctx)
  824 {
  825     memset(ctx,0,sizeof(struct pngrw_ctx));
  826     ctx->pal_sort_by_frequency=1;
  827 }
  828 
  829 static void free_ctx_contents(struct pngrw_ctx *ctx)
  830 {
  831     if(ctx->row_pointers1) free(ctx->row_pointers1);
  832     if(ctx->row_pointers2) free(ctx->row_pointers2);
  833     if(ctx->image1) free(ctx->image1);
  834     if(ctx->image2) free(ctx->image2);
  835 }
  836 
  837 struct pngrw_ctx *pngrw_create(void)
  838 {
  839     struct pngrw_ctx *ctx;
  840     ctx = (struct pngrw_ctx*)calloc(sizeof(struct pngrw_ctx),1);
  841     if(!ctx) return NULL;
  842     init_ctx(ctx);
  843     return ctx;
  844 }
  845 
  846 void pngrw_destroy(struct pngrw_ctx *ctx)
  847 {
  848     if(ctx) {
  849         free_ctx_contents(ctx);
  850         free(ctx);
  851     }
  852 }
  853 
  854 int pngrw_read_stdio(struct pngrw_ctx *ctx, FILE *infp, int verbose)
  855 {
  856     return pngrw_read_png(ctx,infp,verbose);
  857 }
  858 
  859 int pngrw_write_stdio(struct pngrw_ctx *ctx, FILE *outfp, int no_interlace)
  860 {
  861     return pngrw_write_new_png(ctx,outfp,no_interlace);
  862 }
  863 
  864 int pngrw_read_filename(struct pngrw_ctx *ctx, const PNGRW_CHAR *in_filename, int verbose)
  865 {
  866     FILE *infp = NULL;
  867     int ret;
  868 
  869     infp = _tfopen(in_filename, PNGRW_TEXT("rb"));
  870     if(!infp) {
  871         pngrw_print_error(ctx,PNGRW_TEXT("pngrecolor error: Failed to open file for reading"));
  872         return 0;
  873     }
  874 
  875     ret = pngrw_read_png(ctx,infp,verbose);
  876     fclose(infp);
  877     return ret;
  878 }
  879 
  880 int pngrw_write_filename(struct pngrw_ctx *ctx, const PNGRW_CHAR *out_filename, int no_interlace)
  881 {
  882     FILE *outfp = NULL;
  883     int ret;
  884 
  885     outfp = _tfopen(out_filename, PNGRW_TEXT("wb"));
  886     if(!outfp) {
  887         pngrw_print_error(ctx,PNGRW_TEXT("pngrecolor error: Failed to open file for writing"));
  888         return 0;
  889     }
  890 
  891     ret = pngrw_write_new_png(ctx,outfp,no_interlace);
  892     fclose(outfp);
  893     return ret;
  894 }
  895 
  896 void pngrw_set_print_fn(struct pngrw_ctx *ctx, pngrw_print_fn_type prntfn)
  897 {
  898     ctx->printfn = prntfn;
  899 }
  900 
  901 void pngrw_set_error_fn(struct pngrw_ctx *ctx, pngrw_print_fn_type errfn)
  902 {
  903     ctx->errorfn = errfn;
  904 }
  905 
  906 void pngrw_set_userdata(struct pngrw_ctx *ctx, void *u)
  907 {
  908     ctx->userdata = u;
  909 }
  910 
  911 void *pngrw_get_userdata(struct pngrw_ctx *ctx)
  912 {
  913     return ctx->userdata;
  914 }
  915 
  916 void pngrw_set_sort_by_frequency(struct pngrw_ctx *ctx, int n)
  917 {
  918     ctx->pal_sort_by_frequency = n;
  919 }
  920 
  921 const PNGRW_CHAR *pngrw_get_version_string(void)
  922 {
  923     return PNGREWRITEVERSION;
  924 }