コード例 #1
0
ファイル: opngreduc.c プロジェクト: is00hcw/page-speed
/*
 * Reduce the image type from grayscale(+alpha) or RGB(+alpha) to palette,
 * if possible.
 * The parameter reductions indicates the intended reductions.
 * The function returns the successful reductions.
 */
png_uint_32 /* PRIVATE */
opng_reduce_to_palette(png_structp png_ptr, png_infop info_ptr,
   png_uint_32 reductions)
{
   png_uint_32 result;
   png_bytepp row_ptr;
   png_bytep sample_ptr, alpha_row;
   png_uint_32 height, width, channels, i, j;
   unsigned int color_type, dest_bit_depth;
   png_color palette[256];
   png_byte trans_alpha[256];
   int num_palette, num_trans, index;
   png_color_16p background;
   unsigned int gray, red, green, blue, alpha;
   unsigned int prev_gray, prev_red, prev_green, prev_blue, prev_alpha;

   opng_debug(1, "in opng_reduce_to_palette");

   if (info_ptr->bit_depth != 8)
      return OPNG_REDUCE_NONE;  /* nothing is done in this case */

   color_type = info_ptr->color_type;
   OPNG_ASSERT(!(info_ptr->color_type & PNG_COLOR_MASK_PALETTE));

   row_ptr   = info_ptr->row_pointers;
   height    = info_ptr->height;
   width     = info_ptr->width;
   channels  = info_ptr->channels;
   alpha_row = (png_bytep)png_malloc(png_ptr, width);

   /* Analyze the possibility of this reduction. */
   num_palette = num_trans = 0;
   prev_gray = prev_red = prev_green = prev_blue = prev_alpha = 256;
   for (i = 0; i < height; ++i, ++row_ptr)
   {
      sample_ptr = *row_ptr;
      opng_get_alpha_row(png_ptr, info_ptr, *row_ptr, alpha_row);
      if (color_type & PNG_COLOR_MASK_COLOR)
      {
         for (j = 0; j < width; ++j, sample_ptr += channels)
         {
            red   = sample_ptr[0];
            green = sample_ptr[1];
            blue  = sample_ptr[2];
            alpha = alpha_row[j];
            /* Check the cache first. */
            if (red != prev_red || green != prev_green || blue != prev_blue ||
                alpha != prev_alpha)
            {
               prev_red   = red;
               prev_green = green;
               prev_blue  = blue;
               prev_alpha = alpha;
               if (opng_insert_palette_entry(palette, &num_palette,
                   trans_alpha, &num_trans, 256,
                   red, green, blue, alpha, &index) < 0)  /* overflow */
               {
                  OPNG_ASSERT(num_palette < 0);
                  i = height;  /* forced exit from outer loop */
                  break;
               }
            }
         }
      }
      else  /* grayscale */
      {
         for (j = 0; j < width; ++j, sample_ptr += channels)
         {
            gray  = sample_ptr[0];
            alpha = alpha_row[j];
            /* Check the cache first. */
            if (gray != prev_gray || alpha != prev_alpha)
            {
               prev_gray  = gray;
               prev_alpha = alpha;
               if (opng_insert_palette_entry(palette, &num_palette,
                   trans_alpha, &num_trans, 256,
                   gray, gray, gray, alpha, &index) < 0)  /* overflow */
               {
                  OPNG_ASSERT(num_palette < 0);
                  i = height;  /* forced exit from outer loop */
                  break;
               }
            }
         }
      }
   }
#if defined(PNG_bKGD_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
   if ((num_palette >= 0) && (info_ptr->valid & PNG_INFO_bKGD))
   {
      /* bKGD has an alpha-agnostic palette entry. */
      background = &info_ptr->background;
      if (color_type & PNG_COLOR_MASK_COLOR)
      {
         red   = background->red;
         green = background->green;
         blue  = background->blue;
      }
      else
         red = green = blue = background->gray;
      opng_insert_palette_entry(palette, &num_palette,
         trans_alpha, &num_trans, 256,
         red, green, blue, 256, &index);
      if (index >= 0)
         background->index = (png_byte)index;
   }
#endif

   /* Continue only if the uncompressed indexed image (pixels + PLTE + tRNS)
    * is smaller than the uncompressed RGB(A) image.
    * Casual overhead (headers, CRCs, etc.) is ignored.
    *
    * Compare:
    * num_pixels * (src_bit_depth * channels - dest_bit_depth) / 8
    * vs.
    * sizeof(PLTE) + sizeof(tRNS)
    */
   if (num_palette >= 0)
   {
      OPNG_ASSERT(num_palette > 0 && num_palette <= 256);
      OPNG_ASSERT(num_trans >= 0 && num_trans <= num_palette);
      if (num_palette <= 2)
         dest_bit_depth = 1;
      else if (num_palette <= 4)
         dest_bit_depth = 2;
      else if (num_palette <= 16)
         dest_bit_depth = 4;
      else
         dest_bit_depth = 8;
      /* Do the comparison in a way that does not cause overflow. */
      if (channels * 8 == dest_bit_depth ||
          (3 * num_palette + num_trans) * 8 / (channels * 8 - dest_bit_depth)
             / width / height >= 1)
         num_palette = -1;
   }

   if (num_palette < 0)  /* can't reduce */
   {
      png_free(png_ptr, alpha_row);
      return OPNG_REDUCE_NONE;
   }

   /* Reduce. */
   row_ptr = info_ptr->row_pointers;
   index = -1;
   prev_red = prev_green = prev_blue = prev_alpha = (unsigned int)(-1);
   for (i = 0; i < height; ++i, ++row_ptr)
   {
      sample_ptr = *row_ptr;
      opng_get_alpha_row(png_ptr, info_ptr, *row_ptr, alpha_row);
      if (color_type & PNG_COLOR_MASK_COLOR)
      {
         for (j = 0; j < width; ++j, sample_ptr += channels)
         {
            red   = sample_ptr[0];
            green = sample_ptr[1];
            blue  = sample_ptr[2];
            alpha = alpha_row[j];
            /* Check the cache first. */
            if (red != prev_red || green != prev_green || blue != prev_blue ||
                alpha != prev_alpha)
            {
               prev_red   = red;
               prev_green = green;
               prev_blue  = blue;
               prev_alpha = alpha;
               if (opng_insert_palette_entry(palette, &num_palette,
                   trans_alpha, &num_trans, 256,
                   red, green, blue, alpha, &index) != 0)
                  index = -1;  /* this should not happen */
            }
            OPNG_ASSERT(index >= 0);
            (*row_ptr)[j] = (png_byte)index;
         }
      }
      else  /* grayscale */
      {
         for (j = 0; j < width; ++j, sample_ptr += channels)
         {
            gray  = sample_ptr[0];
            alpha = alpha_row[j];
            /* Check the cache first. */
            if (gray != prev_gray || alpha != prev_alpha)
            {
               prev_gray  = gray;
               prev_alpha = alpha;
               if (opng_insert_palette_entry(palette, &num_palette,
                   trans_alpha, &num_trans, 256,
                   gray, gray, gray, alpha, &index) != 0)
                  index = -1;  /* this should not happen */
            }
            OPNG_ASSERT(index >= 0);
            (*row_ptr)[j] = (png_byte)index;
         }
      }
   }

   /* Update the image info. */
   png_ptr->rowbytes    = info_ptr->rowbytes    = 0;
   png_ptr->color_type  = info_ptr->color_type  = PNG_COLOR_TYPE_PALETTE;
   png_ptr->channels    = info_ptr->channels    = 1;
   png_ptr->pixel_depth = info_ptr->pixel_depth = 8;
   png_set_PLTE(png_ptr, info_ptr, palette, num_palette);
   if (num_trans > 0)
      png_set_tRNS(png_ptr, info_ptr, trans_alpha, num_trans, NULL);
   /* bKGD (if present) is already updated. */

   png_free(png_ptr, alpha_row);

   result = OPNG_REDUCE_RGB_TO_PALETTE;
   if (reductions & OPNG_REDUCE_8_TO_4_2_1)
      result |= opng_reduce_palette_bits(png_ptr, info_ptr, reductions);
   return result;
}
コード例 #2
0
/*
 * Reduce the image type from grayscale(+alpha) or RGB(+alpha) to palette,
 * if possible.
 * The parameter reductions indicates the intended reductions.
 * The function returns the successful reductions.
 */
static png_uint_32 opng_reduce_to_palette(png_structp png_ptr, png_infop info_ptr, png_uint_32 reductions)
{
   png_row_info row_info;
   png_bytep sample_ptr;
   png_uint_32 height, width;
   int color_type, interlace_type, compression_type, filter_type;
   int src_bit_depth;
   png_color palette[256];
   png_byte trans_alpha[256];
   int num_trans, index;
   unsigned gray, red, green, blue, alpha;
   unsigned prev_red, prev_green, prev_blue, prev_alpha;
   png_uint_32 j;
   png_get_IHDR(png_ptr, info_ptr, &width, &height, &src_bit_depth,
      &color_type, &interlace_type, &compression_type, &filter_type);
  if (src_bit_depth != 8 || !height || !width)
      return OPNG_REDUCE_NONE;  /* nothing is done in this case */
   OPNG_ASSERT(!(color_type & PNG_COLOR_MASK_PALETTE));

   png_bytepp row_ptr = png_get_rows(png_ptr, info_ptr);
   int channels = png_get_channels(png_ptr, info_ptr);
   png_bytep alpha_row = (png_bytep)png_malloc(png_ptr, width);
   if (!alpha_row){
     exit(1);
   }


   row_info.width = width;
   row_info.rowbytes = 0;  /* not used */
   row_info.color_type = (png_byte)color_type;
   row_info.bit_depth = (png_byte)src_bit_depth;
   row_info.channels = (png_byte)channels;
   row_info.pixel_depth = 0;  /* not used */

   /* Analyze the possibility of this reduction. */
   int num_palette = num_trans = 0;
   png_color_16p trans_color = 0;
   png_get_tRNS(png_ptr, info_ptr, 0, 0, &trans_color);
   unsigned prev_gray = prev_red = prev_green = prev_blue = prev_alpha = 256;
   png_uint_32 i;
   for (i = 0; i < height; ++i, ++row_ptr)
   {
      sample_ptr = *row_ptr;
      opng_get_alpha_row(&row_info, trans_color, *row_ptr, alpha_row);
      if (color_type & PNG_COLOR_MASK_COLOR)
      {
         for (j = 0; j < width; ++j, sample_ptr += channels)
         {
            red   = sample_ptr[0];
            green = sample_ptr[1];
            blue  = sample_ptr[2];
            alpha = alpha_row[j];
            /* Check the cache first. */
            if (red != prev_red || green != prev_green || blue != prev_blue ||
                alpha != prev_alpha)
            {
               prev_red   = red;
               prev_green = green;
               prev_blue  = blue;
               prev_alpha = alpha;
               if (opng_insert_palette_entry(palette, &num_palette,
                   trans_alpha, &num_trans, 256,
                   red, green, blue, alpha, &index) < 0)  /* overflow */
               {
                  OPNG_ASSERT(num_palette < 0);
                  i = height;  /* forced exit from outer loop */
                  break;
               }
            }
         }
      }
      else  /* grayscale */
      {
         for (j = 0; j < width; ++j, sample_ptr += channels)
         {
            gray  = sample_ptr[0];
            alpha = alpha_row[j];
            /* Check the cache first. */
            if (gray != prev_gray || alpha != prev_alpha)
            {
               prev_gray  = gray;
               prev_alpha = alpha;
               if (opng_insert_palette_entry(palette, &num_palette,
                   trans_alpha, &num_trans, 256,
                   gray, gray, gray, alpha, &index) < 0)  /* overflow */
               {
                  OPNG_ASSERT(num_palette < 0);
                  i = height;  /* forced exit from outer loop */
                  break;
               }
            }
         }
      }
   }
#ifdef PNG_bKGD_SUPPORTED
    png_color_16p background;
    if ((num_palette >= 0) && png_get_bKGD(png_ptr, info_ptr, &background))
    {
        /* bKGD has an alpha-agnostic palette entry. */
        if (color_type & PNG_COLOR_MASK_COLOR)
        {
            red   = background->red;
            green = background->green;
            blue  = background->blue;
        }
        else
            red = green = blue = background->gray;
        opng_insert_palette_entry(palette, &num_palette,
                                  trans_alpha, &num_trans, 256,
                                  red, green, blue, 256, &index);
        if (index >= 0)
            background->index = (png_byte)index;
    }
#endif

    /* Continue only if the uncompressed indexed image (pixels + PLTE + tRNS)
     * is smaller than the uncompressed RGB(A) image.
     * Casual overhead (headers, CRCs, etc.) is ignored.
     *
     * Compare:
     * num_pixels * (src_bit_depth * channels - dest_bit_depth) / 8
     * vs.
     * sizeof(PLTE) + sizeof(tRNS)
     */
       /* 5/3 times sizeof(PLTE) + sizeof(tRNS) as:
          1. Palette is uncompressed additional IDAT data is
          2. Headers */
    if (num_palette >= 0)
    {
        int dest_bit_depth;
        OPNG_ASSERT(num_palette > 0 && num_palette <= 256);
        OPNG_ASSERT(num_trans >= 0 && num_trans <= num_palette);
        if (num_palette <= 2)
            dest_bit_depth = 1;
        else if (num_palette <= 4)
            dest_bit_depth = 2;
        else if (num_palette <= 16)
            dest_bit_depth = 4;
        else
            dest_bit_depth = 8;
        /* Do the comparison in a way that does not cause overflow. */
        /*if (channels * 8 == dest_bit_depth || (3 * num_palette + num_trans) * 8 / (channels * 8 - dest_bit_depth) / width / height >= 1) */
        if (channels * 8 == dest_bit_depth || (12 + (5 * num_palette + num_trans)) * 8 / (channels * 8 - dest_bit_depth) / width / height >= 1)
        {num_palette = -1;}
        if ((num_palette && width * height < 12u * num_palette) || width * height < 8000){
            num_palette = -1;
        }
    }

   if (num_palette < 0)  /* can't reduce */
   {
      png_free(png_ptr, alpha_row);
      return OPNG_REDUCE_NONE;
   }

   /* Reduce. */
   row_ptr = png_get_rows(png_ptr, info_ptr);
   index = -1;
   prev_red = prev_green = prev_blue = prev_alpha = (unsigned int)(-1);
   for (i = 0; i < height; ++i, ++row_ptr)
   {
      sample_ptr = *row_ptr;
      opng_get_alpha_row(&row_info, trans_color, *row_ptr, alpha_row);
      if (color_type & PNG_COLOR_MASK_COLOR)
      {
         for (j = 0; j < width; ++j, sample_ptr += channels)
         {
            red   = sample_ptr[0];
            green = sample_ptr[1];
            blue  = sample_ptr[2];
            alpha = alpha_row[j];
            /* Check the cache first. */
            if (red != prev_red || green != prev_green || blue != prev_blue ||
                alpha != prev_alpha)
            {
               prev_red   = red;
               prev_green = green;
               prev_blue  = blue;
               prev_alpha = alpha;
               if (opng_insert_palette_entry(palette, &num_palette,
                   trans_alpha, &num_trans, 256,
                   red, green, blue, alpha, &index) != 0)
                  index = -1;  /* this should not happen */
            }
            OPNG_ASSERT(index >= 0);
            (*row_ptr)[j] = (png_byte)index;
         }
      }
      else  /* grayscale */
      {
         for (j = 0; j < width; ++j, sample_ptr += channels)
         {
            gray  = sample_ptr[0];
            alpha = alpha_row[j];
            /* Check the cache first. */
            if (gray != prev_gray || alpha != prev_alpha)
            {
               prev_gray  = gray;
               prev_alpha = alpha;
               if (opng_insert_palette_entry(palette, &num_palette,
                   trans_alpha, &num_trans, 256,
                   gray, gray, gray, alpha, &index) != 0)
                  index = -1;  /* this should not happen */
            }
            OPNG_ASSERT(index >= 0);
            (*row_ptr)[j] = (png_byte)index;
         }
      }
   }

   /* Update the image information. */
   png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_PALETTE,
      interlace_type, compression_type, filter_type);
   png_set_PLTE(png_ptr, info_ptr, palette, num_palette);
   if (num_trans > 0)
      png_set_tRNS(png_ptr, info_ptr, trans_alpha, num_trans, 0);
   /* bKGD (if present) is automatically updated. */

   png_free(png_ptr, alpha_row);

   png_uint_32 result = OPNG_REDUCE_RGB_TO_PALETTE;
   if (reductions & OPNG_REDUCE_8_TO_4_2_1)
      result |= opng_reduce_palette_bits(png_ptr, info_ptr, reductions);
   return result;
}