unsigned int ImageReaderJPEG::readRows()

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;
}