in imagecore/formats/internal/jpeg.cpp [412:609]
unsigned int ImageReaderJPEG::readRows(Image* dest, unsigned int destRow, unsigned int numRows)
{
bool failed = true;
if( Image::colorModelIsRGBA(dest->getColorModel()) ) {
ImageRGBA* destImage = dest->asRGBA();
unsigned int destHeight = destImage->getHeight();
unsigned int destPitch = 0;
SECURE_ASSERT(destRow + numRows <= destHeight);
uint8_t* destBuffer = destImage->lockRect(0, destRow, destImage->getWidth(), numRows, destPitch);
// Prepare row pointers, not essential, but it makes the following read loop simpler.
uint8_t** rows = (uint8_t**)malloc(numRows * sizeof(uint8_t*));
if( rows == NULL ) {
fprintf(stderr, "error: allocation failed\n");
jpeg_abort_decompress(&m_JPEGDecompress);
jpeg_destroy_decompress(&m_JPEGDecompress);
return 0;
}
if( setjmp(m_JPEGError.jmp) ) {
fprintf(stderr, "error reading JPEG data: %s\n", s_JPEGLastError);
free(rows);
jpeg_abort_decompress(&m_JPEGDecompress);
jpeg_destroy_decompress(&m_JPEGDecompress);
return 0;
}
#if !HAVE_RGBX
unsigned int destWidth = destImage->getWidth();
uint8_t* jpegBuffer = (uint8_t*)malloc(destWidth * numRows * 3);
if( jpegBuffer == NULL ) {
free(rows);
jpeg_abort_decompress(&m_JPEGDecompress);
jpeg_destroy_decompress(&m_JPEGDecompress);
return 0;
}
unsigned int jpegPitch = destWidth * 3;
#else
uint8_t* jpegBuffer = destBuffer;
unsigned int jpegPitch = destPitch;
#endif
for( unsigned int y = 0; y < numRows; y++ ) {
rows[y] = jpegBuffer + jpegPitch * y;
}
unsigned int currentRow = 0;
bool shouldProcessScanlines = false;
#if IMAGECORE_WITH_LCMS
shouldProcessScanlines = (m_ReadOptions & kReadOption_ApplyColorProfile) != 0 && !m_IgnoreColorProfile && m_ColorProfile != NULL && m_ColorTransform != NULL;
#endif
if( shouldProcessScanlines ) {
while( currentRow < numRows ) {
unsigned int numRowsRead = jpeg_read_scanlines(&m_JPEGDecompress, rows + currentRow, numRows - currentRow);
if( !postProcessScanlines(destBuffer + (currentRow * destPitch), numRowsRead * destPitch) ) {
break;
}
currentRow += numRowsRead;
m_TotalRowsRead++;
}
} else {
while( currentRow < numRows ) {
unsigned int numRowsRead = jpeg_read_scanlines(&m_JPEGDecompress, rows + currentRow, numRows - currentRow);
currentRow += numRowsRead;
m_TotalRowsRead++;
}
}
#if !HAVE_RGBX
// Provided purely for compatibility for vanilla libjpeg, not recommended.
for (unsigned int y = 0; y < numRows; y++) {
for (unsigned int x = 0; x < destWidth; x++) {
uint8_t* in = jpegBuffer + y * jpegPitch + x * 3;
uint8_t* out = destBuffer + y * destPitch + x * 4;
out[0] = in[0];
out[1] = in[1];
out[2] = in[2];
}
}
free(jpegBuffer);
#endif
failed = false;
destImage->unlockRect();
free(rows);
} else if ( Image::colorModelIsYUV(dest->getColorModel()) ) {
// jpeg_read_raw_data requires you to handle the edge padding data inserted to align
// the image to the DCT block sizes. Make sure the destination image has enough space in each
// row beyond the normal image data. The bottom edge is handled by reading into junk buffers
// and discarding the information.
// TODO: Relax this by reading into temporary buffers, and copying.
ImageYUV* destImage = dest->asYUV();
EYUVRange desiredRange = destImage->getRange();
unsigned int destPadding = destImage->getPadding();
unsigned int dctAlignedPitchY = align(destImage->getPlaneY()->getWidth(), DCTSIZE * 2);
unsigned int dctAlignedPitchU = align(destImage->getPlaneU()->getWidth(), DCTSIZE * 2);
unsigned int dctAlignedPitchV = align(destImage->getPlaneV()->getWidth(), DCTSIZE * 2);
unsigned int heightY = destImage->getPlaneY()->getHeight();
unsigned int heightUV = destImage->getPlaneU()->getHeight();
SECURE_ASSERT(destImage->getPlaneY()->getPitch() >= dctAlignedPitchY);
SECURE_ASSERT(destImage->getPlaneU()->getPitch() >= dctAlignedPitchU);
SECURE_ASSERT(destImage->getPlaneV()->getPitch() >= dctAlignedPitchV);
ImagePlane8* planeY = destImage->getPlaneY();
ImagePlane8* planeU = destImage->getPlaneU();
ImagePlane8* planeV = destImage->getPlaneV();
unsigned int pitchY = 0;
uint8_t* bufferY = planeY->lockRect(0, destRow, planeY->getWidth(), numRows, pitchY);
unsigned int pitchU = 0;
uint8_t* bufferU = planeU->lockRect(0, destRow / 2, planeU->getWidth(), div2_round(numRows), pitchU);
unsigned int pitchV = 0;
uint8_t* bufferV = planeV->lockRect(0, destRow / 2, planeV->getWidth(), div2_round(numRows), pitchV);
unsigned int currentRowY = 0;
unsigned int currentRowUV = 0;
constexpr unsigned int rowStepY = DCTSIZE * 2;
constexpr unsigned int rowStepUV = DCTSIZE;
if (destPadding < 16) {
// If the image has insufficient padding to use to write the unused edge padding, allocate space for it.
uint8_t* bottomEdgeJunkY = (uint8_t*)malloc(dctAlignedPitchY);
uint8_t* bottomEdgeJunkU = (uint8_t*)malloc(dctAlignedPitchU);
uint8_t* bottomEdgeJunkV = (uint8_t*)malloc(dctAlignedPitchV);
if( bottomEdgeJunkY != NULL && bottomEdgeJunkU != NULL && bottomEdgeJunkV != NULL ) {
while( currentRowY < numRows ) {
JSAMPROW rowsY[rowStepY];
JSAMPROW rowsU[rowStepY];
JSAMPROW rowsV[rowStepY];
for( unsigned int y = 0; y < rowStepY; y++ ) {
unsigned int destY = y + currentRowY;
if( destY < heightY ) {
rowsY[y] = bufferY + pitchY * destY;
} else {
rowsY[y] = bottomEdgeJunkY;
}
}
for( unsigned int y = 0; y < rowStepUV; y++ ) {
unsigned int destUV = y + currentRowUV;
if( destUV < heightUV ) {
rowsU[y] = bufferU + pitchU * destUV;
rowsV[y] = bufferV + pitchV * destUV;
} else {
rowsU[y] = bottomEdgeJunkU;
rowsV[y] = bottomEdgeJunkV;
}
}
JSAMPARRAY rows[] = { rowsY, rowsU, rowsV };
unsigned int numRowsRead = jpeg_read_raw_data(&m_JPEGDecompress, rows, rowStepY);
currentRowY += numRowsRead;
currentRowUV += numRowsRead / 2;
m_TotalRowsRead += numRowsRead;
}
failed = false;
}
free(bottomEdgeJunkY);
free(bottomEdgeJunkU);
free(bottomEdgeJunkV);
} else {
// Enough space to read the raw image.
while( currentRowY < numRows ) {
JSAMPROW rowsY[rowStepY];
JSAMPROW rowsU[rowStepY];
JSAMPROW rowsV[rowStepY];
for( unsigned int y = 0; y < rowStepY; y++ ) {
rowsY[y] = bufferY + pitchY * (y + currentRowY);
}
for( unsigned int y = 0; y < rowStepUV; y++ ) {
rowsU[y] = bufferU + pitchU * (y + currentRowUV);
rowsV[y] = bufferV + pitchV * (y + currentRowUV);
}
JSAMPARRAY rows[] = { rowsY, rowsU, rowsV };
unsigned int numRowsRead = jpeg_read_raw_data(&m_JPEGDecompress, rows, rowStepY);
currentRowY += numRowsRead;
currentRowUV += numRowsRead / 2;
m_TotalRowsRead += numRowsRead;
}
failed = false;
}
destImage->setRange(kYUVRange_Full);
if( desiredRange == kYUVRange_Compressed ) {
destImage->compressRange(destImage);
}
}
if (failed) {
jpeg_abort_decompress(&m_JPEGDecompress);
jpeg_destroy_decompress(&m_JPEGDecompress);
return 0;
}
return numRows;
}