Exemple #1
0
    // FIXME: Currently sharing interlaced callback for all rows and subset. It's not
    // as expensive as the subset version of non-interlaced, but it still does extra
    // work.
    void interlacedRowCallback(png_bytep row, int rowNum, int pass) {
        if (rowNum < fFirstRow || rowNum > fLastRow) {
            // Ignore this row
            return;
        }

        png_bytep oldRow = fInterlaceBuffer.get() + (rowNum - fFirstRow) * fPng_rowbytes;
        png_progressive_combine_row(this->png_ptr(), oldRow, row);

        if (0 == pass) {
            // The first pass initializes all rows.
            SkASSERT(row);
            SkASSERT(fLinesDecoded == rowNum - fFirstRow);
            fLinesDecoded++;
        } else {
            SkASSERT(fLinesDecoded == fLastRow - fFirstRow + 1);
            if (fNumberPasses - 1 == pass && rowNum == fLastRow) {
                // Last pass, and we have read all of the rows we care about. Note that
                // we do not care about reading anything beyond the end of the image (or
                // beyond the last scanline requested).
                fInterlacedComplete = true;
                // Fake error to stop decoding scanlines.
                longjmp(png_jmpbuf(this->png_ptr()), kStopDecoding);
            }
        }
    }
Exemple #2
0
static void
user_endrow_callback (png_structp png_ptr, png_bytep new_row,
    png_uint_32 row_num, int pass)
{
  GstPngDec *pngdec = NULL;

  pngdec = GST_PNGDEC (png_get_io_ptr (png_ptr));

  /* If buffer_out doesn't exist, it means buffer_alloc failed, which 
   * will already have set the return code */
  if (new_row && GST_IS_BUFFER (pngdec->current_frame->output_buffer)) {
    GstVideoFrame frame;
    GstBuffer *buffer = pngdec->current_frame->output_buffer;
    size_t offset;
    guint8 *data;

    if (!gst_video_frame_map (&frame, &pngdec->output_state->info, buffer,
            GST_MAP_WRITE)) {
      pngdec->ret = GST_FLOW_ERROR;
      return;
    }

    data = GST_VIDEO_FRAME_COMP_DATA (&frame, 0);
    offset = row_num * GST_VIDEO_FRAME_COMP_STRIDE (&frame, 0);
    GST_LOG ("got row %u at pass %d, copying in buffer %p at offset %"
        G_GSIZE_FORMAT, (guint) row_num, pass,
        pngdec->current_frame->output_buffer, offset);
    png_progressive_combine_row (pngdec->png, data + offset, new_row);
    gst_video_frame_unmap (&frame);
    pngdec->ret = GST_FLOW_OK;
  } else
    pngdec->ret = GST_FLOW_OK;
}
Exemple #3
0
row_callback(png_structp png_ptr, png_bytep new_row,
   png_uint_32 row_num, int pass)
{
/* this function is called for every row in the image.  If the
   image is interlacing, and you turned on the interlace handler,
   this function will be called for every row in every pass.
   Some of these rows will not be changed from the previous pass.
   When the row is not changed, the new_row variable will be NULL.
   The rows and passes are called in order, so you don't really
   need the row_num and pass, but I'm supplying them because it
   may make your life easier.

   For the non-NULL rows of interlaced images, you must call
   png_progressive_combine_row() passing in the row and the
   old row.  You can call this function for NULL rows (it will
   just return) and for non-interlaced images (it just does the
   memcpy for you) if it will make the code easier.  Thus, you
   can just do this for all cases: */

   png_progressive_combine_row(png_ptr, old_row, new_row);

/* where old_row is what was displayed for previous rows.  Note
   that the first pass (pass == 0 really) will completely cover
   the old row, so the rows do not have to be initialized.  After
   the first pass (and only for interlaced images), you will have
   to pass the current row, and the function will combine the
   old row and the new row. */
}
Exemple #4
0
void png_row_callback(png_structp png_ptr, png_bytep new_row, png_uint_32
	row_num, int pass)
{
	struct cached_image *cimg;
#ifdef REPACK_16
	unsigned char *tmp;
	int channels;
#endif /* #ifdef REPACK_16 */

	cimg=global_cimg;
#ifdef REPACK_16
	if (cimg->buffer_bytes_per_pixel>4)
	{
		channels=cimg->buffer_bytes_per_pixel/sizeof(unsigned
			short);
		if (PNG_INTERLACE_NONE==png_get_interlace_type(png_ptr,
			((struct png_decoder *)cimg->decoder)->info_ptr))
		{
			unsigned_short_from_2char((unsigned short *)(cimg->buffer+cimg
				->buffer_bytes_per_pixel *cimg->width
				*row_num), new_row, cimg->width
				*channels);
		}else{
			if ((unsigned)cimg->width > MAXINT / 2 / channels) overalloc();
			tmp=mem_alloc(cimg->width*2*channels);
			a2char_from_unsigned_short(tmp, (unsigned short *)(cimg->buffer
				+cimg->buffer_bytes_per_pixel
				*cimg->width*row_num), cimg->width*channels);
			png_progressive_combine_row(png_ptr, tmp, new_row);
			unsigned_short_from_2char((unsigned short *)(cimg->buffer
				+cimg->buffer_bytes_per_pixel
				*cimg->width*row_num), tmp, cimg->width*channels);
			mem_free(tmp);
		}
	}else
#endif /* #ifdef REPACK_16 */
	{
		png_progressive_combine_row(png_ptr,
			cimg->buffer+cimg->buffer_bytes_per_pixel
			*cimg->width*row_num, new_row);
	}
	cimg->rows_added=1;
}
Exemple #5
0
 void haveRow(unsigned int rowNum, int pass, unsigned char* data)
 {
     if (interlaced)
     {
         png_byte pngDepth = png_get_bit_depth(pngReadStruct, pngInfoStruct);
         Q_ASSERT(pngDepth <= depth * 8);
         requestScanline(rowNum, scanlineBuf);
         png_progressive_combine_row(pngReadStruct, scanlineBuf, data);
         notifyScanline(pass + 1, scanlineBuf);
     }
     else
         notifyScanline(pass + 1, data);
 }
Exemple #6
0
void PngReader::row_callback(
    png_structp png, png_bytep row,
    png_uint_32 rowpos, int pass )
{
    if( !row )
    {
        return;
    }
    PngReader* reader =
        ( PngReader* )png_get_progressive_ptr( png );
    png_progressive_combine_row(
        png,
        reader->m_data->m_data +
        reader->m_stride * ( reader->m_height - rowpos - 1 ),
        row );
}
Exemple #7
0
/* This function is called when each row of image data is complete */
void row_callback(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num, int pass) {

  if(row_num >= height) {
    // bad row number
  }

  png_progressive_combine_row(png_ptr, row_pointers[row_num], new_row);
  for(int n=0;n<width;n++) {
    uint32_t pixel = inlineget_pixel(new_row,pixel_depth,n);
    
    if(pixel_depth == 1) {
      if(pixel!=0) pixel = 0xFFFFFFFF;
    }

    nsdl_pointS(inline_data_layer,n,row_num,pixel);
  }
}
static void _png_get_row_func(png_structp png_ptr,
                              png_bytep new_row,
                              png_uint_32 row_num,
                              int pass) {
  FXPNG_Context* p = (FXPNG_Context*)png_get_progressive_ptr(png_ptr);
  if (!p)
    return;

  CCodec_PngModule* pModule = (CCodec_PngModule*)p->parent_ptr;
  uint8_t* src_buf = nullptr;
  if (!pModule->AskScanlineBufCallback(p->child_ptr, row_num, src_buf)) {
    png_error(png_ptr, "Ask Scanline buffer Callback Error");
  }
  if (src_buf) {
    png_progressive_combine_row(png_ptr, src_buf, new_row);
  }
  pModule->FillScanlineBufCompletedCallback(p->child_ptr, pass, row_num);
}
Exemple #9
0
/* Called for each row; note that you will get duplicate row numbers
   for interlaced PNGs */
static void
png_row_callback   (png_structp png_read_ptr,
                    png_bytep   new_row,
                    png_uint_32 row_num,
                    int pass_num)
{
        LoadContext* lc;
        guchar* old_row = NULL;

        lc = png_get_progressive_ptr(png_read_ptr);

        if (lc->fatal_error_occurred)
                return;

        if (row_num >= lc->pixbuf->height) {
                lc->fatal_error_occurred = TRUE;
                if (lc->error && *lc->error == NULL) {
                        g_set_error_literal (lc->error,
                                             GDK_PIXBUF_ERROR,
                                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
                                             _("Fatal error reading PNG image file"));
                }
                return;
        }

        if (lc->first_row_seen_in_chunk < 0) {
                lc->first_row_seen_in_chunk = row_num;
                lc->first_pass_seen_in_chunk = pass_num;
        }

        lc->max_row_seen_in_chunk = MAX(lc->max_row_seen_in_chunk, ((gint)row_num));
        lc->last_row_seen_in_chunk = row_num;
        lc->last_pass_seen_in_chunk = pass_num;
        
        old_row = lc->pixbuf->pixels + (row_num * lc->pixbuf->rowstride);

        png_progressive_combine_row(lc->png_read_ptr, old_row, new_row);
}
Exemple #10
0
void pngDecRowCallback(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num, int pass)
{
	PngStream* stream = (PngStream*)png_get_progressive_ptr(png_ptr);
	if (!stream)
	{
		cellPngDec.error("Failed to obtain streamPtr in rowCallback.");
		return;
	}

	// we have to check this everytime as this func can be called multiple times per row, and/or only once per row
	if (stream->nextRow + stream->outputCounts == row_num)
		stream->nextRow = row_num;

	if (stream->ppuContext && (stream->nextRow == row_num  || pass > 0))
	{
		if (pass > 0 )
		{
			stream->cbDispInfo->scanPassCount = pass;
			stream->cbDispInfo->nextOutputStartY = row_num;
		}
		else {
			stream->cbDispInfo->scanPassCount = 0;
			stream->cbDispInfo->nextOutputStartY = 0;
		}

		stream->cbDispInfo->outputImage = stream->cbDispParam->nextOutputImage;
		stream->cbCtrlDisp.cbCtrlDispFunc(*stream->ppuContext, stream->cbDispInfo, stream->cbDispParam, stream->cbCtrlDisp.cbCtrlDispArg);
		stream->cbDispInfo->outputStartY = row_num;
	}
	u8* data;
	if (pass > 0)
		data = static_cast<u8*>(stream->cbDispParam->nextOutputImage.get_ptr());
	else
		data = static_cast<u8*>(stream->cbDispParam->nextOutputImage.get_ptr()) + ((row_num - stream->cbDispInfo->outputStartY) * stream->cbDispInfo->outputFrameWidthByte);

	png_progressive_combine_row(png_ptr, data, new_row);
}
void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, int)
{
    if (m_frameBufferCache.isEmpty())
        return;

    // Initialize the framebuffer if needed.
    ImageFrame& buffer = m_frameBufferCache[0];
    if (buffer.status() == ImageFrame::FrameEmpty) {
        png_structp png = m_reader->pngPtr();
        if (!buffer.setSize(size().width(), size().height())) {
            longjmp(JMPBUF(png), 1);
            return;
        }

        unsigned colorChannels = m_reader->hasAlpha() ? 4 : 3;
        if (PNG_INTERLACE_ADAM7 == png_get_interlace_type(png, m_reader->infoPtr())) {
            m_reader->createInterlaceBuffer(colorChannels * size().width() * size().height());
            if (!m_reader->interlaceBuffer()) {
                longjmp(JMPBUF(png), 1);
                return;
            }
        }

#if USE(QCMSLIB)
        if (m_reader->colorTransform()) {
            m_reader->createRowBuffer(colorChannels * size().width());
            if (!m_reader->rowBuffer()) {
                longjmp(JMPBUF(png), 1);
                return;
            }
        }
#endif
        buffer.setStatus(ImageFrame::FramePartial);
        buffer.setHasAlpha(false);

        // For PNGs, the frame always fills the entire image.
        buffer.setOriginalFrameRect(IntRect(IntPoint(), size()));
    }

    /* libpng comments (here to explain what follows).
     *
     * this function is called for every row in the image. If the
     * image is interlacing, and you turned on the interlace handler,
     * this function will be called for every row in every pass.
     * Some of these rows will not be changed from the previous pass.
     * When the row is not changed, the new_row variable will be NULL.
     * The rows and passes are called in order, so you don't really
     * need the row_num and pass, but I'm supplying them because it
     * may make your life easier.
     */

    // Nothing to do if the row is unchanged, or the row is outside
    // the image bounds: libpng may send extra rows, ignore them to
    // make our lives easier.
    if (!rowBuffer)
        return;
    int y = rowIndex;
    if (y < 0 || y >= size().height())
        return;

    /* libpng comments (continued).
     *
     * For the non-NULL rows of interlaced images, you must call
     * png_progressive_combine_row() passing in the row and the
     * old row.  You can call this function for NULL rows (it will
     * just return) and for non-interlaced images (it just does the
     * memcpy for you) if it will make the code easier. Thus, you
     * can just do this for all cases:
     *
     *    png_progressive_combine_row(png_ptr, old_row, new_row);
     *
     * where old_row is what was displayed for previous rows. Note
     * that the first pass (pass == 0 really) will completely cover
     * the old row, so the rows do not have to be initialized. After
     * the first pass (and only for interlaced images), you will have
     * to pass the current row, and the function will combine the
     * old row and the new row.
     */

    bool hasAlpha = m_reader->hasAlpha();
    png_bytep row = rowBuffer;

    if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) {
        unsigned colorChannels = hasAlpha ? 4 : 3;
        row = interlaceBuffer + (rowIndex * colorChannels * size().width());
        png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer);
    }

#if USE(QCMSLIB)
    if (qcms_transform* transform = m_reader->colorTransform()) {
        qcms_transform_data(transform, row, m_reader->rowBuffer(), size().width());
        row = m_reader->rowBuffer();
    }
#endif

    // Write the decoded row pixels to the frame buffer. The repetitive
    // form of the row write loops is for speed.
    ImageFrame::PixelData* address = buffer.getAddr(0, y);
    unsigned alphaMask = 255;
    int width = size().width();

    png_bytep pixel = row;
    if (hasAlpha) {
        if (buffer.premultiplyAlpha()) {
            for (int x = 0; x < width; ++x, pixel += 4) {
                buffer.setRGBAPremultiply(address++, pixel[0], pixel[1], pixel[2], pixel[3]);
                alphaMask &= pixel[3];
            }
        } else {
            for (int x = 0; x < width; ++x, pixel += 4) {
                buffer.setRGBARaw(address++, pixel[0], pixel[1], pixel[2], pixel[3]);
                alphaMask &= pixel[3];
            }
        }
    } else {
        for (int x = 0; x < width; ++x, pixel += 3) {
            buffer.setRGBARaw(address++, pixel[0], pixel[1], pixel[2], 255);
        }
    }

    if (alphaMask != 255 && !buffer.hasAlpha())
        buffer.setHasAlpha(true);

    buffer.setPixelsChanged(true);
}
Exemple #12
0
void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, int interlacePass)
{
    if (m_frameBufferCache.isEmpty())
        return;

    // Initialize the framebuffer if needed.
    ImageFrame& buffer = m_frameBufferCache[0];
    if (buffer.status() == ImageFrame::FrameEmpty) {
        if (!buffer.setSize(scaledSize().width(), scaledSize().height())) {
            longjmp(JMPBUF(m_reader->pngPtr()), 1);
            return;
        }
        buffer.setStatus(ImageFrame::FramePartial);
        buffer.setHasAlpha(false);
        buffer.setColorProfile(m_colorProfile);

        // For PNGs, the frame always fills the entire image.
        buffer.setOriginalFrameRect(IntRect(IntPoint(), size()));

        if (png_get_interlace_type(m_reader->pngPtr(), m_reader->infoPtr()) != PNG_INTERLACE_NONE)
            m_reader->createInterlaceBuffer((m_reader->hasAlpha() ? 4 : 3) * size().width() * size().height());
    }

    if (!rowBuffer)
        return;

    // libpng comments (pasted in here to explain what follows)
    /*
     * this function is called for every row in the image.  If the
     * image is interlacing, and you turned on the interlace handler,
     * this function will be called for every row in every pass.
     * Some of these rows will not be changed from the previous pass.
     * When the row is not changed, the new_row variable will be NULL.
     * The rows and passes are called in order, so you don't really
     * need the row_num and pass, but I'm supplying them because it
     * may make your life easier.
     *
     * For the non-NULL rows of interlaced images, you must call
     * png_progressive_combine_row() passing in the row and the
     * old row.  You can call this function for NULL rows (it will
     * just return) and for non-interlaced images (it just does the
     * memcpy for you) if it will make the code easier.  Thus, you
     * can just do this for all cases:
     *
     *    png_progressive_combine_row(png_ptr, old_row, new_row);
     *
     * where old_row is what was displayed for previous rows.  Note
     * that the first pass (pass == 0 really) will completely cover
     * the old row, so the rows do not have to be initialized.  After
     * the first pass (and only for interlaced images), you will have
     * to pass the current row, and the function will combine the
     * old row and the new row.
     */

    png_structp png = m_reader->pngPtr();
    bool hasAlpha = m_reader->hasAlpha();
    unsigned colorChannels = hasAlpha ? 4 : 3;
    png_bytep row;
    png_bytep interlaceBuffer = m_reader->interlaceBuffer();
    if (interlaceBuffer) {
        row = interlaceBuffer + (rowIndex * colorChannels * size().width());
        png_progressive_combine_row(png, row, rowBuffer);
    } else
        row = rowBuffer;

    // Copy the data into our buffer.
    int width = scaledSize().width();
    int destY = scaledY(rowIndex);

    // Check that the row is within the image bounds. LibPNG may supply an extra row.
    if (destY < 0 || destY >= scaledSize().height())
        return;
    bool nonTrivialAlpha = false;
    for (int x = 0; x < width; ++x) {
        png_bytep pixel = row + (m_scaled ? m_scaledColumns[x] : x) * colorChannels;
        unsigned alpha = hasAlpha ? pixel[3] : 255;
        buffer.setRGBA(x, destY, pixel[0], pixel[1], pixel[2], alpha);
        nonTrivialAlpha |= alpha < 255;
    }
    if (nonTrivialAlpha && !buffer.hasAlpha())
        buffer.setHasAlpha(nonTrivialAlpha);
}
void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, int interlacePass)
{
    // Resize to the width and height of the image.
    RGBA32Buffer& buffer = m_frameBufferCache[0];
    if (buffer.status() == RGBA32Buffer::FrameEmpty) {
        // Let's resize our buffer now to the correct width/height.
        RGBA32Array& bytes = buffer.bytes();
        bytes.resize(m_size.width() * m_size.height());

        // Update our status to be partially complete.
        buffer.setStatus(RGBA32Buffer::FramePartial);

        // For PNGs, the frame always fills the entire image.
        buffer.setRect(IntRect(0, 0, m_size.width(), m_size.height()));

        if (reader()->pngPtr()->interlaced)
            reader()->createInterlaceBuffer((reader()->hasAlpha() ? 4 : 3) * m_size.width() * m_size.height());
    }

    if (rowBuffer == 0)
        return;

   /* libpng comments (pasted in here to explain what follows)
    *
    * this function is called for every row in the image.  If the
    * image is interlacing, and you turned on the interlace handler,
    * this function will be called for every row in every pass.
    * Some of these rows will not be changed from the previous pass.
    * When the row is not changed, the new_row variable will be NULL.
    * The rows and passes are called in order, so you don't really
    * need the row_num and pass, but I'm supplying them because it
    * may make your life easier.
    *
    * For the non-NULL rows of interlaced images, you must call
    * png_progressive_combine_row() passing in the row and the
    * old row.  You can call this function for NULL rows (it will
    * just return) and for non-interlaced images (it just does the
    * memcpy for you) if it will make the code easier.  Thus, you
    * can just do this for all cases:
    *
    *    png_progressive_combine_row(png_ptr, old_row, new_row);
    *
    * where old_row is what was displayed for previous rows.  Note
    * that the first pass (pass == 0 really) will completely cover
    * the old row, so the rows do not have to be initialized.  After
    * the first pass (and only for interlaced images), you will have
    * to pass the current row, and the function will combine the
    * old row and the new row.
    */
    
    png_structp png = reader()->pngPtr();
    bool hasAlpha = reader()->hasAlpha();
    unsigned colorChannels = hasAlpha ? 4 : 3;
    png_bytep row;
    png_bytep interlaceBuffer = reader()->interlaceBuffer();
    if (interlaceBuffer) {
        row = interlaceBuffer + (rowIndex * colorChannels * m_size.width());
        png_progressive_combine_row(png, row, rowBuffer);
    }
    else
        row = rowBuffer;

    // Copy the data into our buffer.
    int width = m_size.width();
    unsigned* dst = buffer.bytes().data() + rowIndex * width;
    bool sawAlpha = false;
    for (int i = 0; i < width; i++) {
        unsigned red = *row++;
        unsigned green = *row++;
        unsigned blue = *row++;
        unsigned alpha = (hasAlpha ? *row++ : 255);
        RGBA32Buffer::setRGBA(*dst++, red, green, blue, alpha);
        if (!sawAlpha && alpha < 255) {
            sawAlpha = true;
            buffer.setHasAlpha(true);
        }
    }

    buffer.ensureHeight(rowIndex + 1);
}
Exemple #14
0
void
nsPNGDecoder::row_callback(png_structp png_ptr, png_bytep new_row,
                           png_uint_32 row_num, int pass)
{
  /* libpng comments:
   *
   * this function is called for every row in the image.  If the
   * image is interlacing, and you turned on the interlace handler,
   * this function will be called for every row in every pass.
   * Some of these rows will not be changed from the previous pass.
   * When the row is not changed, the new_row variable will be
   * nullptr. The rows and passes are called in order, so you don't
   * really need the row_num and pass, but I'm supplying them
   * because it may make your life easier.
   *
   * For the non-nullptr rows of interlaced images, you must call
   * png_progressive_combine_row() passing in the row and the
   * old row.  You can call this function for nullptr rows (it will
   * just return) and for non-interlaced images (it just does the
   * memcpy for you) if it will make the code easier.  Thus, you
   * can just do this for all cases:
   *
   *    png_progressive_combine_row(png_ptr, old_row, new_row);
   *
   * where old_row is what was displayed for previous rows.  Note
   * that the first pass (pass == 0 really) will completely cover
   * the old row, so the rows do not have to be initialized.  After
   * the first pass (and only for interlaced images), you will have
   * to pass the current row, and the function will combine the
   * old row and the new row.
   */
  nsPNGDecoder* decoder =
               static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr));

  // skip this frame
  if (decoder->mFrameIsHidden) {
    return;
  }

  if (row_num >= (png_uint_32) decoder->mFrameRect.height) {
    return;
  }

  if (new_row) {
    int32_t width = decoder->mFrameRect.width;
    uint32_t iwidth = decoder->mFrameRect.width;

    png_bytep line = new_row;
    if (decoder->interlacebuf) {
      line = decoder->interlacebuf + (row_num * decoder->mChannels * width);
      png_progressive_combine_row(png_ptr, line, new_row);
    }

    uint32_t bpr = width * sizeof(uint32_t);
    uint32_t* cptr32 = (uint32_t*)(decoder->mImageData + (row_num*bpr));

    if (decoder->mTransform) {
      if (decoder->mCMSLine) {
        qcms_transform_data(decoder->mTransform, line, decoder->mCMSLine,
                            iwidth);
        // copy alpha over
        uint32_t channels = decoder->mChannels;
        if (channels == 2 || channels == 4) {
          for (uint32_t i = 0; i < iwidth; i++)
            decoder->mCMSLine[4 * i + 3] = line[channels * i + channels - 1];
        }
        line = decoder->mCMSLine;
      } else {
        qcms_transform_data(decoder->mTransform, line, line, iwidth);
      }
    }

    switch (decoder->format) {
      case gfx::SurfaceFormat::B8G8R8X8: {
        // counter for while() loops below
        uint32_t idx = iwidth;

        // copy as bytes until source pointer is 32-bit-aligned
        for (; (NS_PTR_TO_UINT32(line) & 0x3) && idx; --idx) {
          *cptr32++ = gfxPackedPixel(0xFF, line[0], line[1], line[2]);
          line += 3;
        }

        // copy pixels in blocks of 4
        while (idx >= 4) {
          GFX_BLOCK_RGB_TO_FRGB(line, cptr32);
          idx    -=  4;
          line   += 12;
          cptr32 +=  4;
        }

        // copy remaining pixel(s)
        while (idx--) {
          // 32-bit read of final pixel will exceed buffer, so read bytes
          *cptr32++ = gfxPackedPixel(0xFF, line[0], line[1], line[2]);
          line += 3;
        }
      }
      break;
      case gfx::SurfaceFormat::B8G8R8A8: {
        if (!decoder->mDisablePremultipliedAlpha) {
          for (uint32_t x=width; x>0; --x) {
            *cptr32++ = gfxPackedPixel(line[3], line[0], line[1], line[2]);
            line += 4;
          }
        } else {
          for (uint32_t x=width; x>0; --x) {
            *cptr32++ = gfxPackedPixelNoPreMultiply(line[3], line[0], line[1],
                                                    line[2]);
            line += 4;
          }
        }
      }
      break;
      default:
        png_longjmp(decoder->mPNG, 1);
    }

    if (decoder->mNumFrames <= 1) {
      // Only do incremental image display for the first frame
      // XXXbholley - this check should be handled in the superclass
      nsIntRect r(0, row_num, width, 1);
      decoder->PostInvalidation(r);
    }
  }
}
Exemple #15
0
/*
 * @brief process each row of data provided by libpng
 * @note callback from libpng
 *
 * @param [in] new_row  pointer to start of next row of data
 * @param [in] row_num  number of row in image
 */
void apng_ani::row_callback(png_bytep new_row, png_uint_32 row_num)
{
	png_progressive_combine_row(_pngp, _frame_raw.rows.at(row_num), new_row);
}
void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, int interlacePass)
{
    // Resize to the width and height of the image.
    RGBA32Buffer& buffer = m_frameBufferCache[0];

    //+ 7/13/09 CSidhall - Added buffer size check
    RGBA32Array& bytes = buffer.bytes(); 
    int curSize = bytes.size(); // Verify that buffer is actually there
    int size = m_size.width() * m_size.height();
    if ((buffer.status() == RGBA32Buffer::FrameEmpty) || (size != curSize)) {
    setImagePruneLockStatus(true);  // Lock against pruning this resource   
#ifdef _DEBUG   
    static bool overflowFlag = false;    // Avoid too many asserts
    bool cachePrune = cache()->pruneImages(size << 2);
    if(cachePrune == false) {
        if(!overflowFlag) {
            char buffer[256];
            sprintf(buffer, "RAM Cache Overflow: cache is too small to decode PNG image: %d bytes\n", (size<<2) );
            EAW_ASSERT_MSG(cachePrune, buffer);                       
            overflowFlag = true;    
        }
        // Could exit here but we get into image skipping issues that still need to be worked out.
        // return false;
    }
#else
    cache()->pruneImages(size << 2);
#endif
    setImagePruneLockStatus(false); 

        // Let's resize our buffer now to the correct width/height.
        bytes.resize(size);
        if(!bytes.data())
            return;
        //- CS
        
        // Update our status to be partially complete.
        buffer.setStatus(RGBA32Buffer::FramePartial);

        // For PNGs, the frame always fills the entire image.
        buffer.setRect(IntRect(0, 0, m_size.width(), m_size.height()));

        if (reader()->pngPtr()->interlaced)
            reader()->createInterlaceBuffer((reader()->hasAlpha() ? 4 : 3) * m_size.width() * m_size.height());
    }

    if (rowBuffer == 0)
        return;

   /* libpng comments (pasted in here to explain what follows)
    *
    * this function is called for every row in the image.  If the
    * image is interlacing, and you turned on the interlace handler,
    * this function will be called for every row in every pass.
    * Some of these rows will not be changed from the previous pass.
    * When the row is not changed, the new_row variable will be NULL.
    * The rows and passes are called in order, so you don't really
    * need the row_num and pass, but I'm supplying them because it
    * may make your life easier.
    *
    * For the non-NULL rows of interlaced images, you must call
    * png_progressive_combine_row() passing in the row and the
    * old row.  You can call this function for NULL rows (it will
    * just return) and for non-interlaced images (it just does the
    * memcpy for you) if it will make the code easier.  Thus, you
    * can just do this for all cases:
    *
    *    png_progressive_combine_row(png_ptr, old_row, new_row);
    *
    * where old_row is what was displayed for previous rows.  Note
    * that the first pass (pass == 0 really) will completely cover
    * the old row, so the rows do not have to be initialized.  After
    * the first pass (and only for interlaced images), you will have
    * to pass the current row, and the function will combine the
    * old row and the new row.
    */
 
    png_structp png = reader()->pngPtr();
    bool hasAlpha = reader()->hasAlpha();
    unsigned colorChannels = hasAlpha ? 4 : 3;
    png_bytep row;
    png_bytep interlaceBuffer = reader()->interlaceBuffer();
    if (interlaceBuffer) {
        row = interlaceBuffer + (rowIndex * colorChannels * m_size.width());
        png_progressive_combine_row(png, row, rowBuffer);
    }
    else
        row = rowBuffer;

    // Old code for reference:
/*
    // Copy the data into our buffer.
    int width = m_size.width();
    unsigned* dst = buffer.bytes().data() + rowIndex * width;
    bool sawAlpha = false;
    for (int i = 0; i < width; i++) {
        unsigned red = *row++;
        unsigned green = *row++;
        unsigned blue = *row++;
        unsigned alpha = (hasAlpha ? *row++ : 255);
        RGBA32Buffer::setRGBA(*dst++, red, green, blue, alpha);
        if (!sawAlpha && alpha < 255) {
            sawAlpha = true;
          buffer.setHasAlpha(true);
        }
    }
*/

    // New code:
    // EA/Alex Mole: don't check for alpha at 
    //every single pixel, only do it once per row
    if( hasAlpha )
    {
        // Copy the data into our buffer.
        int width = m_size.width();
        unsigned* dst = buffer.bytes().data() + rowIndex * width;

        // EA/Alex Mole: instead of testing whether we've seen any alpha at each pixel,
        //               we can AND together all of the alpha values and see if we end up
        //               with 255 or not.
        unsigned minalpha = 255;

        for (int i = 0; i < width; i++) {
            unsigned red = *row++;
            unsigned green = *row++;
            unsigned blue = *row++;

            // EA/Alex Mole
            unsigned alpha = *row++;
            minalpha = minalpha & alpha;

            RGBA32Buffer::setRGBA(*dst++, red, green, blue, alpha);
        }
        
        // EA/Alex Mole: see comment above minalpha declaration
        if (minalpha != 255) {
            buffer.setHasAlpha(true);
        }
    }
    else
    {
        // Copy the data into our buffer.
        int width = m_size.width();
        unsigned* dst = buffer.bytes().data() + rowIndex * width;
        for (int i = 0; i < width; i++) {
            unsigned red = *row++;
            unsigned green = *row++;
            unsigned blue = *row++;
            RGBA32Buffer::setRGBWithPresetAlpha(*dst++, red, green, blue);
        }
    }

    buffer.ensureHeight(rowIndex + 1);
}