/** * N = automatic quality, uses limit unless force is set (N-N or 0-N) * -N = no better than N (same as 0-N) * N-M = no worse than N, no better than M * N- = no worse than N, perfect if possible (same as N-100) * * where N,M are numbers between 0 (lousy) and 100 (perfect) */ static bool parse_quality(const char *quality, liq_attr *options, bool *min_quality_limit) { long limit, target; const char *str = quality; char *end; long t1 = strtol(str, &end, 10); if (str == end) return false; str = end; if ('\0' == end[0] && t1 < 0) { // quality="-%d" target = -t1; limit = 0; } else if ('\0' == end[0]) { // quality="%d" target = t1; limit = t1*9/10; } else if ('-' == end[0] && '\0' == end[1]) { // quality="%d-" target = 100; limit = t1; } else { // quality="%d-%d" long t2 = strtol(str, &end, 10); if (str == end || t2 > 0) return false; target = -t2; limit = t1; } *min_quality_limit = (limit > 0); return LIQ_OK == liq_set_quality(options, limit, target); }
/** * Based on libgd/gd_topal.c */ static int ngx_pngquant_gd_image(gdImagePtr oim, int dither, int colorsWanted, int speed) { int i; int maxColors = gdMaxColors; if (!oim->trueColor) { return 1; } /* If we have a transparent color (the alphaless mode of transparency), we * must reserve a palette entry for it at the end of the palette. */ if (oim->transparent >= 0) { maxColors--; } if (colorsWanted > maxColors) { colorsWanted = maxColors; } oim->pixels = calloc(sizeof (unsigned char *), oim->sy); if (!oim->pixels) { /* No can do */ goto outOfMemory; } for (i = 0; (i < oim->sy); i++) { oim->pixels[i] = (unsigned char *) calloc(sizeof (unsigned char *), oim->sx); if (!oim->pixels[i]) { goto outOfMemory; } } liq_attr *attr = liq_attr_create_with_allocator(malloc, gdFree); liq_image *image; liq_result *remap; int remapped_ok = 0; liq_set_max_colors(attr, colorsWanted); /* by default make it fast to match speed of previous implementation */ liq_set_speed(attr, speed ? speed : 9); if (oim->paletteQuantizationMaxQuality) { liq_set_quality(attr, oim->paletteQuantizationMinQuality, oim->paletteQuantizationMaxQuality); } image = liq_image_create_custom(attr, ngx_pngquant_convert_gd_pixel_to_rgba, oim, oim->sx, oim->sy, 0); remap = liq_quantize_image(attr, image); if (!remap) { /* minimum quality not met, leave image unmodified */ liq_image_destroy(image); liq_attr_destroy(attr); goto outOfMemory; } liq_set_dithering_level(remap, dither ? 1 : 0); if (LIQ_OK == liq_write_remapped_image_rows(remap, image, oim->pixels)) { remapped_ok = 1; const liq_palette *pal = liq_get_palette(remap); oim->transparent = -1; unsigned int icolor; for(icolor=0; icolor < pal->count; icolor++) { oim->open[icolor] = 0; oim->red[icolor] = pal->entries[icolor].r * gdRedMax/255; oim->green[icolor] = pal->entries[icolor].g * gdGreenMax/255; oim->blue[icolor] = pal->entries[icolor].b * gdBlueMax/255; int alpha = pal->entries[icolor].a * gdAlphaMax/255; if (gdAlphaOpaque < gdAlphaTransparent) { alpha = gdAlphaTransparent - alpha; } oim->alpha[icolor] = alpha; if (oim->transparent == -1 && alpha == gdAlphaTransparent) { oim->transparent = icolor; } } oim->colorsTotal = pal->count; } liq_result_destroy(remap); liq_image_destroy(image); liq_attr_destroy(attr); if (remapped_ok) { ngx_pngquant_free_true_color_image_data(oim); return 1; } outOfMemory: if (oim->trueColor) { /* On failure only */ if (oim->pixels) { for (i = 0; i < oim->sy; i++) { if (oim->pixels[i]) { gdFree (oim->pixels[i]); } } gdFree (oim->pixels); } oim->pixels = NULL; } return 0; }