in imagecore/formats/internal/jpeg.cpp [1029:1181]
unsigned int ImageWriterJPEG::writeRows(Image* source, unsigned int sourceRow, unsigned int numRows)
{
if( Image::colorModelIsRGBA(source->getColorModel()) ) {
ImageRGBA* sourceImage = source->asRGBA();
uint8_t** rows = (uint8_t**)malloc(numRows * sizeof(uint8_t*));
if( rows == NULL ) {
return 0;
}
if( setjmp(m_JPEGError.jmp) ) {
fprintf(stderr, "error during jpeg compress: %s", s_JPEGLastError);
jpeg_destroy_compress(&m_JPEGCompress);
free(rows);
return 0;
}
unsigned int sourceHeight = sourceImage->getHeight();
unsigned int sourcePitch = sourceImage->getPitch();
uint8_t* sourceBuffer = sourceImage->lockRect(0, sourceRow, sourceImage->getWidth(), numRows, sourcePitch);
SECURE_ASSERT(sourceRow + numRows <= sourceHeight);
#if !HAVE_RGBX
unsigned int sourceWidth = sourceImage->getWidth();
// Provided purely for compatibility for vanilla libjpeg, not recommended.
uint8_t* jpegBuffer = (uint8_t*)malloc(sourceWidth * numRows * 3);
if( jpegBuffer == NULL ) {
free(rows);
jpeg_destroy_compress(&m_JPEGCompress);
return 0;
}
unsigned int jpegPitch = sourceWidth * 3;
for (unsigned int y = 0; y < numRows; y++) {
for (unsigned int x = 0; x < sourceWidth; x++) {
uint8_t* out = jpegBuffer + y * jpegPitch + x * 3;
uint8_t* in = sourceBuffer + y * sourcePitch + x * 4;
out[0] = in[0];
out[1] = in[1];
out[2] = in[2];
}
}
#else
uint8_t* jpegBuffer = sourceBuffer;
unsigned int jpegPitch = sourcePitch;
#endif
for( unsigned int i = 0; i < numRows; i++ ) {
rows[i] = jpegBuffer + i * jpegPitch;
}
unsigned int currentRow = 0;
while( currentRow < numRows ) {
unsigned int numRowsWritten = jpeg_write_scanlines(&m_JPEGCompress, rows + currentRow, numRows - currentRow);
if( numRowsWritten == 0 ) {
break;
}
currentRow += numRowsWritten;
}
free(rows);
#if !HAVE_RGBX
free(jpegBuffer);
#endif
return numRows;
} else if( Image::colorModelIsYUV(source->getColorModel()) ) {
// TODO: Incremental writing
ASSERT(sourceRow == 0);
ASSERT(numRows == m_JPEGCompress.image_height);
if( setjmp(m_JPEGError.jmp) ) {
fprintf(stderr, "error during jpeg compress: %s", s_JPEGLastError);
jpeg_destroy_compress(&m_JPEGCompress);
return 0;
}
ImageYUV* yuvImage = source->asYUV();
unsigned int minPitchY = align(DCTSIZE * 2, yuvImage->getPlaneY()->getWidth());
unsigned int minPitchUV = align(DCTSIZE, yuvImage->getPlaneU()->getWidth());
ImageYUV* sourceImage = yuvImage;
ImageYUV* tempImage = NULL;
// Image doesn't meet padding or range requirements, copy to a new buffer and correct.
if( yuvImage->getRange() == kYUVRange_Compressed || (sourceImage->getPadding() < 16 && (m_WriteOptions & kWriteOption_AssumeMCUPaddingFilled) == 0) || yuvImage->getPlaneY()->getPitch() < minPitchY || yuvImage->getPlaneU()->getPitch() < minPitchUV ) {
tempImage = ImageYUV::create(yuvImage->getWidth(), yuvImage->getHeight(), 16, 16);
if( tempImage == NULL ) {
return 0;
}
if( yuvImage->getRange() == kYUVRange_Compressed ) {
yuvImage->expandRange(tempImage);
} else {
yuvImage->copy(tempImage);
}
sourceImage = tempImage;
// Fill padding region of new image.
sourceImage->getPlaneY()->fillPadding(kEdge_Right);
sourceImage->getPlaneU()->fillPadding(kEdge_Right);
sourceImage->getPlaneV()->fillPadding(kEdge_Right);
} else if( (m_WriteOptions & kWriteOption_AssumeMCUPaddingFilled) == 0 ) {
// Image was large enough and in the correct range, just fill the padding (unless it's been requested that we don't).
// Use the existing padding region to satisfy jpeg_write_raw_data's requirement that we write
// data aligned to the block size. Fill the edge padding data with data from the image (clamped)
// to prevent any problems with the DCT.
sourceImage->getPlaneY()->fillPadding(kEdge_Right);
sourceImage->getPlaneU()->fillPadding(kEdge_Right);
sourceImage->getPlaneV()->fillPadding(kEdge_Right);
}
SECURE_ASSERT(sourceImage->getPlaneY()->getPitch() >= minPitchY);
SECURE_ASSERT(sourceImage->getPlaneU()->getPitch() >= minPitchUV);
SECURE_ASSERT(sourceImage->getPlaneV()->getPitch() >= minPitchUV);
ImagePlane8* planeY = sourceImage->getPlaneY();
ImagePlane8* planeU = sourceImage->getPlaneU();
ImagePlane8* planeV = sourceImage->getPlaneV();
uint8_t* bufferY = (uint8_t*)planeY->getBytes();
uint8_t* bufferU = (uint8_t*)planeU->getBytes();
uint8_t* bufferV = (uint8_t*)planeV->getBytes();
unsigned int pitchY = planeY->getPitch();
unsigned int pitchU = planeU->getPitch();
unsigned int pitchV = planeV->getPitch();
unsigned int currentRowY = 0;
unsigned int currentRowUV = 0;
constexpr unsigned int rowStepY = DCTSIZE * 2;
constexpr unsigned int rowStepUV = DCTSIZE;
while( currentRowY < numRows ) {
JSAMPROW rowsY[rowStepY];
JSAMPROW rowsU[rowStepY];
JSAMPROW rowsV[rowStepY];
for( unsigned int y = 0; y < rowStepY; y++ ) {
rowsY[y] = bufferY + pitchY * clamp(0, planeY->getHeight() - 1, y + currentRowY);
}
for( unsigned int y = 0; y < rowStepUV; y++ ) {
rowsU[y] = bufferU + pitchU * clamp(0, planeU->getHeight() - 1, y + currentRowUV);
rowsV[y] = bufferV + pitchV * clamp(0, planeV->getHeight() - 1, y + currentRowUV);
}
JSAMPARRAY rows[] = { rowsY, rowsU, rowsV };
unsigned int numRowsWritten = jpeg_write_raw_data(&m_JPEGCompress, rows, rowStepY);
currentRowY += numRowsWritten;
currentRowUV += numRowsWritten / 2;
}
delete tempImage;
return numRows;
} else {
return 0;
}
}