Exemplo n.º 1
0
/**
 *   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;
}