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