Esempio n. 1
0
void
nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr)
{
//  int number_passes;   NOT USED
  png_uint_32 width, height;
  int bit_depth, color_type, interlace_type, compression_type, filter_type;
  unsigned int channels;

  png_bytep trans = nullptr;
  int num_trans = 0;

  nsPNGDecoder* decoder =
               static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr));

  // Always decode to 24-bit RGB or 32-bit RGBA
  png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
               &interlace_type, &compression_type, &filter_type);

  // Are we too big?
  if (width > MOZ_PNG_MAX_DIMENSION || height > MOZ_PNG_MAX_DIMENSION) {
    png_longjmp(decoder->mPNG, 1);
  }

  // Post our size to the superclass
  decoder->PostSize(width, height);
  if (decoder->HasError()) {
    // Setting the size led to an error.
    png_longjmp(decoder->mPNG, 1);
  }

  if (color_type == PNG_COLOR_TYPE_PALETTE) {
    png_set_expand(png_ptr);
  }

  if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
    png_set_expand(png_ptr);
  }

  if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
    png_color_16p trans_values;
    png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &trans_values);
    // libpng doesn't reject a tRNS chunk with out-of-range samples
    // so we check it here to avoid setting up a useless opacity
    // channel or producing unexpected transparent pixels (bug #428045)
    if (bit_depth < 16) {
      png_uint_16 sample_max = (1 << bit_depth) - 1;
      if ((color_type == PNG_COLOR_TYPE_GRAY &&
           trans_values->gray > sample_max) ||
           (color_type == PNG_COLOR_TYPE_RGB &&
           (trans_values->red > sample_max ||
           trans_values->green > sample_max ||
           trans_values->blue > sample_max))) {
        // clear the tRNS valid flag and release tRNS memory
        png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0);
        num_trans = 0;
      }
    }
    if (num_trans != 0) {
      png_set_expand(png_ptr);
    }
  }

  if (bit_depth == 16) {
    png_set_scale_16(png_ptr);
  }

  qcms_data_type inType = QCMS_DATA_RGBA_8;
  uint32_t intent = -1;
  uint32_t pIntent;
  if (decoder->mCMSMode != eCMSMode_Off) {
    intent = gfxPlatform::GetRenderingIntent();
    decoder->mInProfile = PNGGetColorProfile(png_ptr, info_ptr,
                                             color_type, &inType, &pIntent);
    // If we're not mandating an intent, use the one from the image.
    if (intent == uint32_t(-1)) {
      intent = pIntent;
    }
  }
  if (decoder->mInProfile && gfxPlatform::GetCMSOutputProfile()) {
    qcms_data_type outType;

    if (color_type & PNG_COLOR_MASK_ALPHA || num_trans) {
      outType = QCMS_DATA_RGBA_8;
    } else {
      outType = QCMS_DATA_RGB_8;
    }

    decoder->mTransform = qcms_transform_create(decoder->mInProfile,
                                           inType,
                                           gfxPlatform::GetCMSOutputProfile(),
                                           outType,
                                           (qcms_intent)intent);
  } else {
    png_set_gray_to_rgb(png_ptr);

    // only do gamma correction if CMS isn't entirely disabled
    if (decoder->mCMSMode != eCMSMode_Off) {
      PNGDoGammaCorrection(png_ptr, info_ptr);
    }

    if (decoder->mCMSMode == eCMSMode_All) {
      if (color_type & PNG_COLOR_MASK_ALPHA || num_trans) {
        decoder->mTransform = gfxPlatform::GetCMSRGBATransform();
      } else {
        decoder->mTransform = gfxPlatform::GetCMSRGBTransform();
      }
    }
  }

  // let libpng expand interlaced images
  if (interlace_type == PNG_INTERLACE_ADAM7) {
    // number_passes =
    png_set_interlace_handling(png_ptr);
  }

  // now all of those things we set above are used to update various struct
  // members and whatnot, after which we can get channels, rowbytes, etc.
  png_read_update_info(png_ptr, info_ptr);
  decoder->mChannels = channels = png_get_channels(png_ptr, info_ptr);

  //---------------------------------------------------------------//
  // copy PNG info into imagelib structs (formerly png_set_dims()) //
  //---------------------------------------------------------------//

  if (channels == 1 || channels == 3) {
    decoder->format = gfx::SurfaceFormat::B8G8R8X8;
  } else if (channels == 2 || channels == 4) {
    decoder->format = gfx::SurfaceFormat::B8G8R8A8;
  } else {
    png_longjmp(decoder->mPNG, 1); // invalid number of channels
  }

#ifdef PNG_APNG_SUPPORTED
  if (png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL)) {
    png_set_progressive_frame_fn(png_ptr, nsPNGDecoder::frame_info_callback,
                                 nullptr);
  }

  if (png_get_first_frame_is_hidden(png_ptr, info_ptr)) {
    decoder->mFrameIsHidden = true;
  } else {
#endif
    decoder->CreateFrame(0, 0, width, height, decoder->format);
#ifdef PNG_APNG_SUPPORTED
  }
#endif

  if (decoder->mTransform &&
      (channels <= 2 || interlace_type == PNG_INTERLACE_ADAM7)) {
    uint32_t bpp[] = { 0, 3, 4, 3, 4 };
    decoder->mCMSLine =
      (uint8_t*)malloc(bpp[channels] * width);
    if (!decoder->mCMSLine) {
      png_longjmp(decoder->mPNG, 5); // NS_ERROR_OUT_OF_MEMORY
    }
  }

  if (interlace_type == PNG_INTERLACE_ADAM7) {
    if (height < INT32_MAX / (width * channels)) {
      decoder->interlacebuf = (uint8_t*)malloc(channels * width * height);
    }
    if (!decoder->interlacebuf) {
      png_longjmp(decoder->mPNG, 5); // NS_ERROR_OUT_OF_MEMORY
    }
  }

  if (decoder->NeedsNewFrame()) {
    // We know that we need a new frame, so pause input so the decoder
    // infrastructure can give it to us.
    png_process_data_pause(png_ptr, /* save = */ 1);
  }
}
Esempio n. 2
0
static void readAnimated( imageCache &cache, PngInfo& png ){
	auto width  = png.width();
	auto height = png.height();
	png_uint_32 x_offset=0, y_offset=0;
	png_uint_16 delay_num=0, delay_den=0;
	png_byte dispose_op = PNG_DISPOSE_OP_NONE, blend_op = PNG_BLEND_OP_SOURCE;
	
	QImage canvas( width, height, QImage::Format_ARGB32 );
	canvas.fill( qRgba( 0,0,0,0 ) );
	AnimCombiner combiner( canvas );
	
	if( setjmp( png_jmpbuf( png.png ) ) )
		return;
	
	unsigned repeats = png_get_num_plays( png.png, png.info );
	unsigned frames = png_get_num_frames( png.png, png.info );
	
	//NOTE: We discard the frame if it is not a part of the animation
	if( png_get_first_frame_is_hidden( png.png, png.info ) ){
		readImage( png, width, height );
		--frames; //libpng appears to tell the total amount of images
	}
	
	cache.set_info( frames, true, repeats>0 ? repeats-1 : -1 );
	
	for( unsigned i=0; i < frames; ++i ){
		png_read_frame_head( png.png, png.info );
		
		if( png_get_valid( png.png, png.info, PNG_INFO_fcTL ) ){
			png_get_next_frame_fcTL( png.png, png.info
				,	&width, &height
				,	&x_offset, &y_offset
				,	&delay_num, &delay_den
				,	&dispose_op, &blend_op
				);
		}
		else{
			width  = png.width();
			height = png.height();
		}
		
		readImage( png, width, height, i==0 );
		
		//Calculate delay
		delay_den = delay_den==0 ? 100 : delay_den;
		unsigned delay = std::ceil( (double)delay_num / delay_den * 1000 );
		if( delay == 0 )
			delay = 1; //Fastest speed we support
		
		//Compose and add
		auto blend_mode = blend_op == PNG_BLEND_OP_SOURCE ? BlendMode::OVERLAY : BlendMode::REPLACE;
		auto dispose_mode = [=](){ switch( dispose_op ){
				case PNG_DISPOSE_OP_NONE:       return DisposeMode::NONE;
				case PNG_DISPOSE_OP_BACKGROUND: return DisposeMode::BACKGROUND;
				case PNG_DISPOSE_OP_PREVIOUS:   return DisposeMode::REVERT;
				default: return DisposeMode::NONE; //TODO: add error
			} }();
		QImage output = combiner.combine( png.frame, x_offset, y_offset, blend_mode, dispose_mode );
		cache.add_frame( output, delay );
	}
}
Esempio n. 3
0
void
nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr)
{
/*  int number_passes;   NOT USED  */
  png_uint_32 width, height;
  int bit_depth, color_type, interlace_type, compression_type, filter_type;
  unsigned int channels;

  png_bytep trans = NULL;
  int num_trans = 0;

  nsPNGDecoder *decoder =
               static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr));

  /* always decode to 24-bit RGB or 32-bit RGBA  */
  png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
               &interlace_type, &compression_type, &filter_type);

  /* Are we too big? */
  if (width > MOZ_PNG_MAX_DIMENSION || height > MOZ_PNG_MAX_DIMENSION)
    longjmp(png_jmpbuf(decoder->mPNG), 1);

  // Post our size to the superclass
  decoder->PostSize(width, height);
  if (decoder->HasError()) {
    // Setting the size lead to an error; this can happen when for example
    // a multipart channel sends an image of a different size.
    longjmp(png_jmpbuf(decoder->mPNG), 1);
  }

  if (color_type == PNG_COLOR_TYPE_PALETTE)
    png_set_expand(png_ptr);

  if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
    png_set_expand(png_ptr);

  if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
    int sample_max = (1 << bit_depth);
    png_color_16p trans_values;
    png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &trans_values);
    /* libpng doesn't reject a tRNS chunk with out-of-range samples
       so we check it here to avoid setting up a useless opacity
       channel or producing unexpected transparent pixels when using
       libpng-1.2.19 through 1.2.26 (bug #428045) */
    if ((color_type == PNG_COLOR_TYPE_GRAY &&
       (int)trans_values->gray > sample_max) ||
       (color_type == PNG_COLOR_TYPE_RGB &&
       ((int)trans_values->red > sample_max ||
       (int)trans_values->green > sample_max ||
       (int)trans_values->blue > sample_max)))
      {
        /* clear the tRNS valid flag and release tRNS memory */
        png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0);
      }
    else
      png_set_expand(png_ptr);
  }

  if (bit_depth == 16)
    png_set_strip_16(png_ptr);

  qcms_data_type inType;
  PRUint32 intent = -1;
  PRUint32 pIntent;
  if (decoder->mCMSMode != eCMSMode_Off) {
    intent = gfxPlatform::GetRenderingIntent();
    decoder->mInProfile = PNGGetColorProfile(png_ptr, info_ptr,
                                             color_type, &inType, &pIntent);
    /* If we're not mandating an intent, use the one from the image. */
    if (intent == PRUint32(-1))
      intent = pIntent;
  }
  if (decoder->mInProfile && gfxPlatform::GetCMSOutputProfile()) {
    qcms_data_type outType;

    if (color_type & PNG_COLOR_MASK_ALPHA || num_trans)
      outType = QCMS_DATA_RGBA_8;
    else
      outType = QCMS_DATA_RGB_8;

    decoder->mTransform = qcms_transform_create(decoder->mInProfile,
                                           inType,
                                           gfxPlatform::GetCMSOutputProfile(),
                                           outType,
                                           (qcms_intent)intent);
  } else {
    png_set_gray_to_rgb(png_ptr);

    // only do gamma correction if CMS isn't entirely disabled
    if (decoder->mCMSMode != eCMSMode_Off)
      PNGDoGammaCorrection(png_ptr, info_ptr);

    if (decoder->mCMSMode == eCMSMode_All) {
      if (color_type & PNG_COLOR_MASK_ALPHA || num_trans)
        decoder->mTransform = gfxPlatform::GetCMSRGBATransform();
      else
        decoder->mTransform = gfxPlatform::GetCMSRGBTransform();
    }
  }

  /* let libpng expand interlaced images */
  if (interlace_type == PNG_INTERLACE_ADAM7) {
    /* number_passes = */
    png_set_interlace_handling(png_ptr);
  }

  /* now all of those things we set above are used to update various struct
   * members and whatnot, after which we can get channels, rowbytes, etc. */
  png_read_update_info(png_ptr, info_ptr);
  decoder->mChannels = channels = png_get_channels(png_ptr, info_ptr);

  /*---------------------------------------------------------------*/
  /* copy PNG info into imagelib structs (formerly png_set_dims()) */
  /*---------------------------------------------------------------*/

  PRInt32 alpha_bits = 1;

  if (channels == 2 || channels == 4) {
    /* check if alpha is coming from a tRNS chunk and is binary */
    if (num_trans) {
      /* if it's not an indexed color image, tRNS means binary */
      if (color_type == PNG_COLOR_TYPE_PALETTE) {
        for (int i=0; i<num_trans; i++) {
          if ((trans[i] != 0) && (trans[i] != 255)) {
            alpha_bits = 8;
            break;
          }
        }
      }
    } else {
      alpha_bits = 8;
    }
  }

  if (channels == 1 || channels == 3)
    decoder->format = gfxASurface::ImageFormatRGB24;
  else if (channels == 2 || channels == 4)
    decoder->format = gfxASurface::ImageFormatARGB32;

#ifdef PNG_APNG_SUPPORTED
  if (png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL))
    png_set_progressive_frame_fn(png_ptr, nsPNGDecoder::frame_info_callback, NULL);

  if (png_get_first_frame_is_hidden(png_ptr, info_ptr)) {
    decoder->mFrameIsHidden = PR_TRUE;
  } else {
#endif
    decoder->CreateFrame(0, 0, width, height, decoder->format);
#ifdef PNG_APNG_SUPPORTED
  }
#endif

  if (decoder->mTransform &&
      (channels <= 2 || interlace_type == PNG_INTERLACE_ADAM7)) {
    PRUint32 bpp[] = { 0, 3, 4, 3, 4 };
    decoder->mCMSLine =
      (PRUint8 *)moz_malloc(bpp[channels] * width);
    if (!decoder->mCMSLine) {
      longjmp(png_jmpbuf(decoder->mPNG), 5); // NS_ERROR_OUT_OF_MEMORY
    }
  }

  if (interlace_type == PNG_INTERLACE_ADAM7) {
    if (height < PR_INT32_MAX / (width * channels))
      decoder->interlacebuf = (PRUint8 *)moz_malloc(channels * width * height);
    if (!decoder->interlacebuf) {
      longjmp(png_jmpbuf(decoder->mPNG), 5); // NS_ERROR_OUT_OF_MEMORY
    }
  }

  /* Reject any ancillary chunk after IDAT with a bad CRC (bug #397593).
   * It would be better to show the default frame (if one has already been
   * successfully decoded) before bailing, but it's simpler to just bail
   * out with an error message.
   */
  png_set_crc_action(png_ptr, PNG_CRC_NO_CHANGE, PNG_CRC_ERROR_QUIT);

  return;
}
Esempio n. 4
0
void* PNG_decode(JNIEnv* env, PatchHeadInputStream* patch_head_input_stream, bool partially)
{
  PNG *png = NULL;
  png_structp png_ptr = NULL;
  png_infop info_ptr = NULL;
  bool apng;
  unsigned int width;
  unsigned int height;
  int color_type;
  int bit_depth;
  bool is_opaque;
  unsigned char* buffer = NULL;
  unsigned int frame_count = 0;
  bool hide_first_frame = false;
  PNG_FRAME_INFO* frame_info_array = NULL;
  int i;

  png = (PNG *) malloc(sizeof(PNG));
  if (png == NULL) {
    WTF_OM;
    close_patch_head_input_stream(get_env(), patch_head_input_stream);
    destroy_patch_head_input_stream(get_env(), &patch_head_input_stream);
    return NULL;
  }

  png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, &user_error_fn, &user_warn_fn);
  if (png_ptr == NULL) {
    free(png);
    png = NULL;
    close_patch_head_input_stream(get_env(), patch_head_input_stream);
    destroy_patch_head_input_stream(get_env(), &patch_head_input_stream);
    return NULL;
  }

  info_ptr = png_create_info_struct(png_ptr);
  if (info_ptr == NULL) {
    png_destroy_read_struct(&png_ptr, NULL, NULL);
    free(png);
    png = NULL;
    close_patch_head_input_stream(get_env(), patch_head_input_stream);
    destroy_patch_head_input_stream(get_env(), &patch_head_input_stream);
    return NULL;
  }

  if (setjmp(png_jmpbuf(png_ptr))) {
    LOGE(EMSG("Error in png decode"));
    free_frame_info_array(frame_info_array, frame_count);
    frame_info_array = NULL;
    free(buffer);
    buffer = NULL;
    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
    free(png);
    png = NULL;
    close_patch_head_input_stream(get_env(), patch_head_input_stream);
    destroy_patch_head_input_stream(get_env(), &patch_head_input_stream);
    return NULL;
  }

  // Set custom read function
  png_set_read_fn(png_ptr, patch_head_input_stream, &user_read_fn);

  // Get png info
  png_read_info(png_ptr, info_ptr);

  // Check apng
  if (png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL)) {
    apng = true;
  } else {
    apng = false;
  }

  // PNG info
  width = png_get_image_width(png_ptr, info_ptr);
  height = png_get_image_height(png_ptr, info_ptr);
  color_type = png_get_color_type(png_ptr, info_ptr);
  bit_depth = png_get_bit_depth(png_ptr, info_ptr);

  // Create buffer
  buffer = (unsigned char*) malloc(width * height * 4);
  if (buffer == NULL) {
    WTF_OM;
    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
    free(png);
    png = NULL;
    close_patch_head_input_stream(get_env(), patch_head_input_stream);
    destroy_patch_head_input_stream(get_env(), &patch_head_input_stream);
    return NULL;
  }

  if (apng) {
    // Get frame count
    frame_count = png_get_num_frames(png_ptr, info_ptr);
    hide_first_frame = png_get_first_frame_is_hidden(png_ptr, info_ptr);
    if (hide_first_frame) {
      frame_count--;
    }

    // Create frame info array
    frame_info_array = (PNG_FRAME_INFO*) calloc(frame_count, sizeof(PNG_FRAME_INFO));
    if (frame_info_array == NULL) {
      WTF_OM;
      free(buffer);
      buffer = NULL;
      png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
      free(png);
      png = NULL;
      close_patch_head_input_stream(get_env(), patch_head_input_stream);
      destroy_patch_head_input_stream(get_env(), &patch_head_input_stream);
      return NULL;
    }
  }

  // Configure to ARGB
  png_set_expand(png_ptr);
  if (bit_depth == 16) {
    png_set_scale_16(png_ptr);
  }
  if (color_type == PNG_COLOR_TYPE_GRAY ||
      color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
    png_set_gray_to_rgb(png_ptr);
  }
  if (!(color_type & PNG_COLOR_MASK_ALPHA)) {
    is_opaque = true;
    png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER);
  } else {
    is_opaque = false;
  }

  if (apng) {
    if (hide_first_frame) {
      // Skip first frame
      read_image(png_ptr, buffer, width, height);
    }

    // Read first frame
    read_frame(png_ptr, info_ptr, frame_info_array);
    // Fix dop
    if (frame_info_array->dop == PNG_DISPOSE_OP_PREVIOUS) {
      frame_info_array->dop = PNG_DISPOSE_OP_BACKGROUND;
    }

    if (!partially || frame_count == 1) {
      // Read all frame
      for (i = 1; i < frame_count; read_frame(png_ptr, info_ptr, frame_info_array + i++));

      // Generate pop
      generate_pop(frame_info_array, frame_count);

      // End read
      png_read_end(png_ptr, info_ptr);
      png_destroy_read_struct(&png_ptr, &info_ptr, NULL);

      // Close input stream
      close_patch_head_input_stream(env, patch_head_input_stream);
      destroy_patch_head_input_stream(env, &patch_head_input_stream);

      png->partially = false;
      png->png_ptr = NULL;
      png->info_ptr = NULL;
      png->patch_head_input_stream = NULL;
    } else {
      png->partially = true;
      png->png_ptr = png_ptr;
      png->info_ptr = info_ptr;
      png->patch_head_input_stream = patch_head_input_stream;
    }

    // Fill PNG
    png->width = width;
    png->height = height;
    png->is_opaque = is_opaque;
    png->buffer = buffer;
    png->apng = true;
    png->buffer_index = -1;
    png->frame_info_array = frame_info_array;
    png->frame_count = frame_count;
    png->backup = NULL;

    // Render first frame
    PNG_advance(png);
  } else {
    read_image(png_ptr, buffer, width, height);

    // End read
    png_read_end(png_ptr, info_ptr);
    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);

    // Close input stream
    close_patch_head_input_stream(env, patch_head_input_stream);
    destroy_patch_head_input_stream(env, &patch_head_input_stream);

    // Fill PNG
    png->width = width;
    png->height = height;
    png->buffer = buffer;
    png->apng = false;
    png->buffer_index = 0;
    png->frame_info_array = NULL;
    png->frame_count = 0;
    png->backup = NULL;
    png->partially = false;
    png->png_ptr = NULL;
    png->info_ptr = NULL;
    png->patch_head_input_stream = NULL;
  }

  return png;
}