Ejemplo n.º 1
0
int msQuantizeRasterBuffer(rgbaPixel *pixelsIn,int height,int width,
      unsigned int *reqcolors, rgbaPixel *palette,
      unsigned int *palette_scaling_maxval) 
{
    register rgbaPixel *pP;
    register int col;
    unsigned char newmaxval;
    acolorhist_vector achv, acolormap=NULL;
    int row;
    int colors;
    int newcolors = 0;
    int x;
    *palette_scaling_maxval = 255;
    int row_step = width*4;

    rgbaPixel **apixels=(rgbaPixel**)malloc(height*sizeof(rgbaPixel**));
    
    for(row=0;row<height;row++) {
        apixels[row]=(rgbaPixel*)(&(pixelsIn[row*width]));
    }

    /*
     ** Step 2: attempt to make a histogram of the colors, unclustered.
     ** If at first we don't succeed, lower maxval to increase color
     ** coherence and try again.  This will eventually terminate, with
     ** maxval at worst 15, since 32^3 is approximately MAXCOLORS.
                  [GRR POSSIBLE BUG:  what about 32^4 ?]
     */
    for ( ; ; ) {
        achv = pam_computeacolorhist(
                apixels, width, height, MAXCOLORS, &colors );
        if ( achv != (acolorhist_vector) 0 )
            break;
        newmaxval = *palette_scaling_maxval / 2;
        for ( row = 0; row < height; ++row )
            for ( col = 0, pP = apixels[row]; col < width; ++col, ++pP )
                PAM_DEPTH( *pP, *pP, *palette_scaling_maxval, newmaxval );
        *palette_scaling_maxval = newmaxval;
    }
    newcolors = MS_MIN(colors, *reqcolors);
    acolormap = mediancut(achv, colors, width*height, *palette_scaling_maxval, newcolors);
    pam_freeacolorhist(achv);
    
    
    *reqcolors = newcolors;

    for (x = 0; x < newcolors; ++x) {
       palette[x].r = acolormap[x].acolor.r;
       palette[x].g = acolormap[x].acolor.g;
       palette[x].b = acolormap[x].acolor.b;
       palette[x].a = acolormap[x].acolor.a;
    }
    
   free(acolormap);
   free(apixels);
   return MS_SUCCESS;
}
Ejemplo n.º 2
0
/**
 * Compute a palette for the given RGBA rasterBuffer using a median cut quantization.
 * - rb: the rasterBuffer to quantize
 * - reqcolors: the desired number of colors the palette should contain. will be set
 *   with the actual number of entries in the computed palette
 * - forced_palette: entries that should appear in the computed palette
 * - num_forced_palette_entries: number of entries contained in "force_palette". if 0,
 *   "force_palette" can be NULL
 * - palette_scaling_maxval: the quantization process may have to reduce the image depth
 *   by iteratively dividing the pixels by 2. In case palette_scaling_maxval is set to
 *   something different than 255, the returned palette colors have to be scaled back up to
 *   255, and rb's pixels will have been scaled down to maxsize (see bug #3848)
 */
int msQuantizeRasterBuffer(rasterBufferObj *rb,
                           unsigned int *reqcolors, rgbaPixel *palette,
                           rgbaPixel *forced_palette, int num_forced_palette_entries,
                           unsigned int *palette_scaling_maxval)
{
  rgbaPixel **apixels=NULL; /* pointer to the start rows of truecolor pixels */

  register rgbaPixel *pP;
  register int col;

  unsigned char newmaxval;
  acolorhist_vector achv, acolormap=NULL;

  int row;
  int colors;
  int newcolors = 0;

  int x;
  /*  int channels;  */

  assert(rb->type == MS_BUFFER_BYTE_RGBA);

  *palette_scaling_maxval = 255;

  apixels=(rgbaPixel**)msSmallMalloc(rb->height*sizeof(rgbaPixel*));

  for(row=0; row<rb->height; row++) {
    apixels[row]=(rgbaPixel*)(&(rb->data.rgba.pixels[row * rb->data.rgba.row_step]));
  }

  /*
   ** Step 2: attempt to make a histogram of the colors, unclustered.
   ** If at first we don't succeed, lower maxval to increase color
   ** coherence and try again.  This will eventually terminate, with
   ** maxval at worst 15, since 32^3 is approximately MAXCOLORS.
                [GRR POSSIBLE BUG:  what about 32^4 ?]
   */
  for ( ; ; ) {
    achv = pam_computeacolorhist(
             apixels, rb->width, rb->height, MAXCOLORS, &colors );
    if ( achv != (acolorhist_vector) 0 )
      break;
    newmaxval = *palette_scaling_maxval / 2;
    for ( row = 0; row < rb->height; ++row )
      for ( col = 0, pP = apixels[row]; col < rb->width; ++col, ++pP )
        PAM_DEPTH( *pP, *pP, *palette_scaling_maxval, newmaxval );
    *palette_scaling_maxval = newmaxval;
  }
  newcolors = MS_MIN(colors, *reqcolors);
  acolormap = mediancut(achv, colors, rb->width*rb->height, *palette_scaling_maxval, newcolors);
  pam_freeacolorhist(achv);


  *reqcolors = newcolors;


  for (x = 0; x < newcolors; ++x) {
    palette[x].r = acolormap[x].acolor.r;
    palette[x].g = acolormap[x].acolor.g;
    palette[x].b = acolormap[x].acolor.b;
    palette[x].a = acolormap[x].acolor.a;
  }

  free(acolormap);
  free(apixels);
  return MS_SUCCESS;
}
Ejemplo n.º 3
0
/**
 * Compute a palette for the given RGBA rasterBuffer using a median cut quantization.
 * - rb: the rasterBuffer to quantize
 * - reqcolors: the desired number of colors the palette should contain. will be set
 *   with the actual number of entries in the computed palette
 * - palette: preallocated array of palette entries that will be populated by the
 *   function
 * - maxval: max value of pixel intensity. In some cases, the input data has to
 *   be rescaled to compute the quantization. if the returned value of maxscale is
 *   less than 255, this means that the input pixels have been rescaled, and that
 *   the returned palette must be upscaled before being written to the png file
 * - forced_palette: entries that should appear in the computed palette
 * - num_forced_palette_entries: number of entries contained in "force_palette". if 0,
 *   "force_palette" can be NULL
 */
int _mapcache_imageio_quantize_image(mapcache_image *rb,
      unsigned int *reqcolors, rgbaPixel *palette,
      unsigned int *maxval,
      rgbaPixel *forced_palette, int num_forced_palette_entries) {

   rgbaPixel **apixels=NULL; /* pointer to the start rows of truecolor pixels */
   register rgbaPixel *pP;
   register int col;

   unsigned char newmaxval;
   acolorhist_vector achv, acolormap=NULL;

   int row;
   int colors;
   int newcolors = 0;

   int x;
   /*  int channels;  */


   *maxval = 255;

   apixels=(rgbaPixel**)malloc(rb->h*sizeof(rgbaPixel**));
   if(!apixels) return MAPCACHE_FAILURE;

   for(row=0;row<rb->h;row++) {
      apixels[row]=(rgbaPixel*)(&(rb->data[row * rb->stride]));
   }

   /*
    ** Step 2: attempt to make a histogram of the colors, unclustered.
    ** If at first we don't succeed, lower maxval to increase color
    ** coherence and try again.  This will eventually terminate, with
    ** maxval at worst 15, since 32^3 is approximately MAXCOLORS.
                  [GRR POSSIBLE BUG:  what about 32^4 ?]
    */
   for ( ; ; ) {
      achv = pam_computeacolorhist(
            apixels, rb->w, rb->h, MAXCOLORS, &colors );
      if ( achv != (acolorhist_vector) 0 )
         break;
      newmaxval = *maxval / 2;
      for ( row = 0; row < rb->h; ++row )
         for ( col = 0, pP = apixels[row]; col < rb->w; ++col, ++pP )
            PAM_DEPTH( *pP, *pP, *maxval, newmaxval );
      *maxval = newmaxval;
   }
   newcolors = MAPCACHE_MIN(colors, *reqcolors);
   acolormap = mediancut(achv, colors, rb->w*rb->h, *maxval, newcolors);
   pam_freeacolorhist(achv);


   *reqcolors = newcolors;


   for (x = 0; x < newcolors; ++x) {
      palette[x].r = acolormap[x].acolor.r;
      palette[x].g = acolormap[x].acolor.g;
      palette[x].b = acolormap[x].acolor.b;
      palette[x].a = acolormap[x].acolor.a;
   }

   free(acolormap);
   free(apixels);
   return MAPCACHE_SUCCESS;
}
Ejemplo n.º 4
0
/*
 * Voronoi iteration: new palette color is computed from weighted average of colors that map to that palette entry.
 */
static void viter_init(const colormap *map,
                     f_pixel *average_color, float *average_color_count,
                     f_pixel *base_color, float *base_color_count)
{
    colormap_item *newmap = map->palette;
    int newcolors = map->colors;
    for (int i=0; i < newcolors; i++) {
        average_color_count[i] = 0;
        average_color[i] = (f_pixel){0,0,0,0};
    }

    // Rather than only using separate mapping and averaging steps
    // new palette colors are computed at the same time as mapping is done
    // but to avoid first few matches moving the entry too much
    // some base color and weight is added
    if (base_color) {
        for (int i=0; i < newcolors; i++) {
            float value = 1.0+newmap[i].popularity/2.0;
            base_color_count[i] = value;
            base_color[i] = (f_pixel){
                .a = newmap[i].acolor.a * value,
                .r = newmap[i].acolor.r * value,
                .g = newmap[i].acolor.g * value,
                .b = newmap[i].acolor.b * value,
            };
        }
    }
}

static void viter_update_color(f_pixel acolor, float value, colormap *map, int match,
                             f_pixel *average_color, float *average_color_count,
                             const f_pixel *base_color, const float *base_color_count)
{
    average_color[match].a += acolor.a * value;
    average_color[match].r += acolor.r * value;
    average_color[match].g += acolor.g * value;
    average_color[match].b += acolor.b * value;
    average_color_count[match] += value;

    if (base_color) {
        map->palette[match].acolor = (f_pixel){
            .a = (average_color[match].a + base_color[match].a) / (average_color_count[match] + base_color_count[match]),
            .r = (average_color[match].r + base_color[match].r) / (average_color_count[match] + base_color_count[match]),
            .g = (average_color[match].g + base_color[match].g) / (average_color_count[match] + base_color_count[match]),
            .b = (average_color[match].b + base_color[match].b) / (average_color_count[match] + base_color_count[match]),
        };
    }
}

static void viter_finalize(colormap *map, f_pixel *average_color, float *average_color_count)
{
    for (int i=0; i < map->colors; i++) {
        if (average_color_count[i]) {
            map->palette[i].acolor = (f_pixel){
                .a = (average_color[i].a) / average_color_count[i],
                .r = (average_color[i].r) / average_color_count[i],
                .g = (average_color[i].g) / average_color_count[i],
                .b = (average_color[i].b) / average_color_count[i],
            };
        }
        map->palette[i].popularity = average_color_count[i];
    }
}

void viter_do_interation(const hist *hist, colormap *map, float min_opaque_val)
{
    f_pixel average_color[map->colors];
    float average_color_count[map->colors];

    hist_item *achv = hist->achv;
    viter_init(map, average_color,average_color_count, NULL,NULL);

    for(int j=0; j < hist->size; j++) {

        int match = best_color_index(achv[j].acolor, map, min_opaque_val, NULL);
        viter_update_color(achv[j].acolor, achv[j].perceptual_weight, map, match, average_color,average_color_count, NULL,NULL);
    }

    viter_finalize(map, average_color,average_color_count);
}

pngquant_error pngquant(read_info *input_image, write_info *output_image, int floyd, int reqcolors, int ie_bug, int speed_tradeoff)
{
    float min_opaque_val;

    verbose_printf("  reading file corrected for gamma %2.1f\n", 1.0/input_image->gamma);

    min_opaque_val = modify_alpha(input_image,ie_bug);
    assert(min_opaque_val>0);

    hist *hist = histogram(input_image, reqcolors, speed_tradeoff);
    hist_item *achv = hist->achv;

    colormap *acolormap = NULL;
    float least_error = -1;
    int feedback_loop_trials = 56-9*speed_tradeoff;
    const double percent = (double)(feedback_loop_trials>0?feedback_loop_trials:1)/100.0;

    do
    {
        verbose_printf("  selecting colors");

        colormap *newmap = mediancut(hist, min_opaque_val, reqcolors);

        verbose_printf("...");

        float total_error=0;
        f_pixel average_color[newmap->colors], base_color[newmap->colors];
        float average_color_count[newmap->colors], base_color_count[newmap->colors];

        if (feedback_loop_trials) {

            viter_init(newmap, average_color,average_color_count,base_color,base_color_count);

            for(int i=0; i < hist->size; i++) {
                float diff;
                int match = best_color_index(achv[i].acolor, newmap, min_opaque_val, &diff);
                assert(diff >= 0);
                assert(achv[i].perceptual_weight > 0);
                total_error += diff * achv[i].perceptual_weight;

                viter_update_color(achv[i].acolor, achv[i].perceptual_weight, newmap, match,
                                   average_color,average_color_count,base_color,base_color_count);

                achv[i].adjusted_weight = (achv[i].perceptual_weight+achv[i].adjusted_weight) * (1.0+sqrtf(diff));
            }
        }

        if (total_error < least_error || !acolormap) {
            if (acolormap) pam_freecolormap(acolormap);

            acolormap = newmap;

            viter_finalize(acolormap, average_color,average_color_count);

            least_error = total_error;
            feedback_loop_trials -= 1; // asymptotic improvement could make it go on forever
        } else {
            feedback_loop_trials -= 6;
            if (total_error > least_error*4) feedback_loop_trials -= 3;
            pam_freecolormap(newmap);
        }

        verbose_printf("%d%%\n",100-MAX(0,(int)(feedback_loop_trials/percent)));
    }
    while(feedback_loop_trials > 0);

    verbose_printf("  moving colormap towards local minimum\n");

    int iterations = MAX(5-speed_tradeoff,0); iterations *= iterations;
    for(int i=0; i < iterations; i++) {
        viter_do_interation(hist, acolormap, min_opaque_val);
    }

    pam_freeacolorhist(hist);

    output_image->width = input_image->width;
    output_image->height = input_image->height;
    output_image->gamma = 0.45455;

    /*
    ** Step 3.7 [GRR]: allocate memory for the entire indexed image
    */

    output_image->indexed_data = malloc(output_image->height * output_image->width);
    output_image->row_pointers = malloc(output_image->height * sizeof(output_image->row_pointers[0]));

    if (!output_image->indexed_data || !output_image->row_pointers) {
        fprintf(stderr, "  insufficient memory for indexed data and/or row pointers\n");
        return OUT_OF_MEMORY_ERROR;
    }

    for (int row = 0;  row < output_image->height;  ++row) {
        output_image->row_pointers[row] = output_image->indexed_data + row*output_image->width;
    }

    // tRNS, etc.
    sort_palette(output_image, acolormap);

    /*
     ** Step 4: map the colors in the image to their closest match in the
     ** new colormap, and write 'em out.
     */
    verbose_printf("  mapping image to new colors...");

    float remapping_error;

    if (floyd) {
        // if dithering, save rounding error and stick to that palette
        // otherwise palette can be improved after remapping
        set_palette(output_image, acolormap);
        remapping_error = remap_to_palette_floyd(input_image, output_image, acolormap, min_opaque_val, ie_bug);
    } else {
        remapping_error = remap_to_palette(input_image, output_image, acolormap, min_opaque_val, ie_bug);
        set_palette(output_image, acolormap);
    }

    verbose_printf("MSE=%.3f\n", remapping_error*256.0f);

    pam_freecolormap(acolormap);

    return SUCCESS;
}