bool ImageWriterPNG::copyLossless()

in imagecore/formats/internal/png.cpp [342:438]


bool ImageWriterPNG::copyLossless(ImageReader* reader)
{
	if( reader->getFormat() != EImageFormat::kImageFormat_PNG ) {
		bool ret = ImageWriter::copyLossless(reader); // call baseclass for non png->png cleans
		if (!ret) {
			png_destroy_write_struct(&m_PNGCompress, &m_PNGInfo);
		}
		return ret;
	}

	ImageReaderPNG* readerPNG = (ImageReaderPNG*)reader;
	if( setjmp(png_jmpbuf(readerPNG->m_PNGDecompress)) ) {
		png_destroy_read_struct(&readerPNG->m_PNGDecompress, &readerPNG->m_PNGInfo, NULL);
		png_destroy_write_struct(&m_PNGCompress, &m_PNGInfo);
		return false;
	}

	uint32_t pitch = (uint32_t)png_get_rowbytes(readerPNG->m_PNGDecompress, readerPNG->m_PNGInfo);

	int32_t width;
	int32_t height;
	int32_t bitDepth;
	int32_t colorType;
	int32_t interlace;
	int32_t compressionType;
	int32_t filterMethod; // this is actually filter method not filter type, even though libpng docs refer to it as filter type. The actual filter type needs to be set separately using png_set_filter function
	png_get_IHDR(readerPNG->m_PNGDecompress, readerPNG->m_PNGInfo, (png_uint_32*)&width, (png_uint_32*)&height, &bitDepth, &colorType, &interlace, &compressionType, &filterMethod);

	uint32_t allocSize = SafeUMul(pitch, height);
	uint8_t* imageData = (uint8_t*)malloc(allocSize);
	if( imageData == NULL ) {
		png_destroy_read_struct(&readerPNG->m_PNGDecompress, &readerPNG->m_PNGInfo, NULL);
		png_destroy_write_struct(&m_PNGCompress, &m_PNGInfo);
		return false;
	}
	allocSize = SafeUMul((unsigned int)sizeof(png_bytep), height);
	png_bytep* rowPointers = (png_bytep*)malloc(allocSize);
	if( rowPointers == NULL ) {
		png_destroy_read_struct(&readerPNG->m_PNGDecompress, &readerPNG->m_PNGInfo, NULL);
		png_destroy_write_struct(&m_PNGCompress, &m_PNGInfo);
		free(imageData);
		return false;
	}

	// libpng asks for an array of scanline pointers, into each of which it'll write width * components * depth bytes.
	for( unsigned int y = 0; y < height; y++ ) {
		const uint32_t rowOffset = SafeUMul(y, pitch);
		rowPointers[y] = imageData + rowOffset;
	}

	if( setjmp(png_jmpbuf(readerPNG->m_PNGDecompress)) ) {
		png_destroy_read_struct(&readerPNG->m_PNGDecompress, &readerPNG->m_PNGInfo, NULL);
		png_destroy_write_struct(&m_PNGCompress, &m_PNGInfo);
		free(imageData);
		free(rowPointers);
		return false;
	}
	png_read_image(readerPNG->m_PNGDecompress, rowPointers);

	png_set_IHDR(m_PNGCompress, m_PNGInfo, width, height, bitDepth, colorType, PNG_INTERLACE_NONE, compressionType, filterMethod);
	uint32_t filterType = PNG_ALL_FILTERS;
	if( colorType == PNG_COLOR_TYPE_PALETTE ) {
		png_colorp palette;
		int32_t numEntries;
		png_get_PLTE(readerPNG->m_PNGDecompress, readerPNG->m_PNGInfo, &palette, &numEntries);
		png_set_PLTE(m_PNGCompress, m_PNGInfo, palette, numEntries);
		png_bytep atRNS = NULL;
		png_color_16p ctRNS = NULL;
		int numtRNS = 0;
		png_get_tRNS(readerPNG->m_PNGDecompress, readerPNG->m_PNGInfo, &atRNS, &numtRNS, &ctRNS);
		png_set_tRNS(m_PNGCompress, m_PNGInfo, atRNS, numtRNS, ctRNS);
		filterType = 0;
	}

	if( setjmp(png_jmpbuf(m_PNGCompress)) ) {
		png_destroy_read_struct(&readerPNG->m_PNGDecompress, &readerPNG->m_PNGInfo, NULL);
		png_destroy_write_struct(&m_PNGCompress, &m_PNGInfo);
		free(rowPointers);
		free(imageData);
		return false;
	}

	png_write_info(m_PNGCompress, m_PNGInfo);

	png_set_filter(m_PNGCompress, PNG_FILTER_TYPE_BASE, filterType);
	png_set_compression_level(m_PNGCompress, 9); // maximum compression, tests showed 6 out of 13400 png images had to be cleaned, because transform produced higher sizes.

	png_write_rows(m_PNGCompress, rowPointers, height);

	readerPNG->endRead();

	png_write_end(m_PNGCompress, NULL);
	png_destroy_write_struct(&m_PNGCompress, &m_PNGInfo);
	free(rowPointers);
	free(imageData);
	return true;
}