summaryrefslogtreecommitdiffstats
path: root/sdext/source/pdfimport/xpdfwrapper/pnghelper.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /sdext/source/pdfimport/xpdfwrapper/pnghelper.cxx
parentInitial commit. (diff)
downloadlibreoffice-upstream/4%7.4.7.tar.xz
libreoffice-upstream/4%7.4.7.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sdext/source/pdfimport/xpdfwrapper/pnghelper.cxx')
-rw-r--r--sdext/source/pdfimport/xpdfwrapper/pnghelper.cxx404
1 files changed, 404 insertions, 0 deletions
diff --git a/sdext/source/pdfimport/xpdfwrapper/pnghelper.cxx b/sdext/source/pdfimport/xpdfwrapper/pnghelper.cxx
new file mode 100644
index 000000000..798555fe2
--- /dev/null
+++ b/sdext/source/pdfimport/xpdfwrapper/pnghelper.cxx
@@ -0,0 +1,404 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "pnghelper.hxx"
+#include <sal/macros.h>
+
+#include <zlib.h>
+
+using namespace pdfi;
+
+// checksum helpers, courtesy of libpng.org
+
+/* Table of CRCs of all 8-bit messages. */
+sal_uInt32 PngHelper::crc_table[256];
+
+/* Flag: has the table been computed? Initially false. */
+bool PngHelper::bCRCTableInit = true;
+
+/* Make the table for a fast CRC. */
+void PngHelper::initCRCTable()
+{
+ for (sal_uInt32 n = 0; n < 256; n++)
+ {
+ sal_uInt32 c = n;
+ for (int k = 0; k < 8; k++)
+ {
+ if (c & 1)
+ c = 0xedb88320L ^ (c >> 1);
+ else
+ c = c >> 1;
+ }
+ crc_table[n] = c;
+ }
+ bCRCTableInit = false;
+}
+
+/* Update a running CRC with the bytes buf[0..len-1]--the CRC
+ should be initialized to all 1's, and the transmitted value
+ is the 1's complement of the final running CRC (see the
+ crc() routine below)). */
+
+void PngHelper::updateCRC( sal_uInt32& io_rCRC, const sal_uInt8* i_pBuf, size_t i_nLen )
+{
+ if( bCRCTableInit )
+ initCRCTable();
+
+ sal_uInt32 nCRC = io_rCRC;
+ for( size_t n = 0; n < i_nLen; n++ )
+ nCRC = crc_table[(nCRC ^ i_pBuf[n]) & 0xff] ^ (nCRC >> 8);
+ io_rCRC = nCRC;
+}
+
+sal_uInt32 PngHelper::getCRC( const sal_uInt8* i_pBuf, size_t i_nLen )
+{
+ sal_uInt32 nCRC = 0xffffffff;
+ updateCRC( nCRC, i_pBuf, i_nLen );
+ return nCRC ^ 0xffffffff;
+}
+
+sal_uInt32 PngHelper::deflateBuffer( const Output_t* i_pBuf, size_t i_nLen, OutputBuffer& o_rOut )
+{
+ size_t nOrigSize = o_rOut.size();
+
+ // prepare z stream
+ z_stream aStream;
+ aStream.zalloc = Z_NULL;
+ aStream.zfree = Z_NULL;
+ aStream.opaque = Z_NULL;
+ aStream.total_out = aStream.total_in = 0;
+ if (Z_OK != deflateInit(&aStream, Z_BEST_COMPRESSION))
+ return 0;
+ aStream.avail_in = uInt(i_nLen);
+ aStream.next_in = i_pBuf;
+
+ sal_uInt8 aOutBuf[ 32768 ];
+ do
+ {
+ aStream.avail_out = sizeof( aOutBuf );
+ aStream.next_out = aOutBuf;
+
+ if( deflate( &aStream, Z_FINISH ) == Z_STREAM_ERROR )
+ {
+ deflateEnd( &aStream );
+ // scrap the data of this broken stream
+ o_rOut.resize( nOrigSize );
+ return 0;
+ }
+
+ // append compressed bytes
+ sal_uInt32 nCompressedBytes = sizeof( aOutBuf ) - aStream.avail_out;
+ if( nCompressedBytes )
+ o_rOut.insert( o_rOut.end(), aOutBuf, aOutBuf+nCompressedBytes );
+
+ } while( aStream.avail_out == 0 );
+
+ // cleanup
+ deflateEnd( &aStream );
+
+ return sal_uInt32( o_rOut.size() - nOrigSize );
+}
+
+void PngHelper::appendFileHeader( OutputBuffer& o_rOutputBuf )
+{
+ static const unsigned char aHeader[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
+
+ o_rOutputBuf.insert( o_rOutputBuf.end(), aHeader, aHeader + SAL_N_ELEMENTS(aHeader) );
+}
+
+size_t PngHelper::startChunk( const char* pChunkName, OutputBuffer& o_rOutputBuf )
+{
+ size_t nIndex = sal_uInt32( o_rOutputBuf.size() );
+ o_rOutputBuf.insert( o_rOutputBuf.end(), 4, Output_t(0) );
+ o_rOutputBuf.push_back( pChunkName[0] );
+ o_rOutputBuf.push_back( pChunkName[1] );
+ o_rOutputBuf.push_back( pChunkName[2] );
+ o_rOutputBuf.push_back( pChunkName[3] );
+ return nIndex;
+}
+
+void PngHelper::set( sal_uInt32 i_nValue, OutputBuffer& o_rOutputBuf, size_t i_nIndex )
+{
+ o_rOutputBuf[ i_nIndex ] = (i_nValue & 0xff000000) >> 24;
+ o_rOutputBuf[ i_nIndex+1 ] = (i_nValue & 0x00ff0000) >> 16;
+ o_rOutputBuf[ i_nIndex+2 ] = (i_nValue & 0x0000ff00) >> 8;
+ o_rOutputBuf[ i_nIndex+3 ] = (i_nValue & 0x000000ff);
+}
+
+void PngHelper::endChunk( size_t nStart, OutputBuffer& o_rOutputBuf )
+{
+ if( nStart+8 > o_rOutputBuf.size() )
+ return; // something broken is going on
+
+ // update chunk length
+ size_t nLen = o_rOutputBuf.size() - nStart;
+ sal_uInt32 nDataLen = sal_uInt32(nLen)-8;
+ set( nDataLen, o_rOutputBuf, nStart );
+
+ // append chunk crc
+ sal_uInt32 nChunkCRC = getCRC( reinterpret_cast<sal_uInt8*>(&o_rOutputBuf[nStart+4]), nLen-4 );
+ append( nChunkCRC, o_rOutputBuf );
+}
+
+void PngHelper::appendIHDR( OutputBuffer& o_rOutputBuf, int width, int height, int depth, int colortype )
+{
+ size_t nStart = startChunk( "IHDR", o_rOutputBuf );
+ append( width, o_rOutputBuf );
+ append( height, o_rOutputBuf );
+ o_rOutputBuf.push_back( Output_t(depth) );
+ o_rOutputBuf.push_back( Output_t(colortype) );
+ o_rOutputBuf.push_back( 0 ); // compression method deflate
+ o_rOutputBuf.push_back( 0 ); // filtering method 0 (default)
+ o_rOutputBuf.push_back( 0 ); // no interlacing
+ endChunk( nStart, o_rOutputBuf );
+}
+
+void PngHelper::appendIEND( OutputBuffer& o_rOutputBuf )
+{
+ size_t nStart = startChunk( "IEND", o_rOutputBuf );
+ endChunk( nStart, o_rOutputBuf );
+}
+
+void PngHelper::createPng( OutputBuffer& o_rOutputBuf,
+ Stream* str,
+ int width,
+ int height,
+ GfxRGB const & zeroColor,
+ GfxRGB const & oneColor,
+ bool bIsMask
+ )
+{
+ appendFileHeader( o_rOutputBuf );
+ appendIHDR( o_rOutputBuf, width, height, 1, 3 );
+
+ // write palette
+ size_t nIdx = startChunk( "PLTE", o_rOutputBuf );
+ // write colors 0 and 1
+ o_rOutputBuf.push_back(colToByte(zeroColor.r));
+ o_rOutputBuf.push_back(colToByte(zeroColor.g));
+ o_rOutputBuf.push_back(colToByte(zeroColor.b));
+ o_rOutputBuf.push_back(colToByte(oneColor.r));
+ o_rOutputBuf.push_back(colToByte(oneColor.g));
+ o_rOutputBuf.push_back(colToByte(oneColor.b));
+ // end PLTE chunk
+ endChunk( nIdx, o_rOutputBuf );
+
+ if( bIsMask )
+ {
+ // write tRNS chunk
+ nIdx = startChunk( "tRNS", o_rOutputBuf );
+ o_rOutputBuf.push_back( 0xff );
+ o_rOutputBuf.push_back( 0 );
+ // end tRNS chunk
+ endChunk( nIdx, o_rOutputBuf );
+ }
+
+ // create scan line data buffer
+ OutputBuffer aScanlines;
+ int nLineSize = (width + 7)/8;
+ aScanlines.reserve( nLineSize * height + height );
+
+ str->reset();
+ for( int y = 0; y < height; y++ )
+ {
+ // determine filter type (none) for this scanline
+ aScanlines.push_back( 0 );
+ for( int x = 0; x < nLineSize; x++ )
+ aScanlines.push_back( str->getChar() );
+ }
+
+ // begin IDAT chunk for scanline data
+ nIdx = startChunk( "IDAT", o_rOutputBuf );
+ // compress scanlines
+ deflateBuffer( aScanlines.data(), aScanlines.size(), o_rOutputBuf );
+ // end IDAT chunk
+ endChunk( nIdx, o_rOutputBuf );
+
+ // output IEND
+ appendIEND( o_rOutputBuf );
+}
+
+void PngHelper::createPng( OutputBuffer& o_rOutputBuf,
+ Stream* str,
+ int width, int height, GfxImageColorMap* colorMap,
+ Stream* maskStr,
+ int maskWidth, int maskHeight, GfxImageColorMap* maskColorMap )
+{
+ appendFileHeader( o_rOutputBuf );
+ appendIHDR( o_rOutputBuf, width, height, 8, 6 ); // RGBA image
+
+ // initialize stream
+ unsigned char *p, *pm;
+ GfxRGB rgb;
+ GfxGray alpha;
+ std::unique_ptr<ImageStream> imgStr(
+ new ImageStream(str,
+ width,
+ colorMap->getNumPixelComps(),
+ colorMap->getBits()));
+ imgStr->reset();
+
+ // create scan line data buffer
+ OutputBuffer aScanlines;
+ aScanlines.reserve( width*height*4 + height );
+
+ for( int y=0; y<height; ++y)
+ {
+ aScanlines.push_back( 0 );
+ p = imgStr->getLine();
+ for( int x=0; x<width; ++x)
+ {
+ colorMap->getRGB(p, &rgb);
+ aScanlines.push_back(colToByte(rgb.r));
+ aScanlines.push_back(colToByte(rgb.g));
+ aScanlines.push_back(colToByte(rgb.b));
+ aScanlines.push_back( 0xff );
+
+ p +=colorMap->getNumPixelComps();
+ }
+ }
+
+
+ // now fill in the mask data
+
+ // CAUTION: originally this was done in one single loop
+ // it caused merry chaos; the reason is that maskStr and str are
+ // not independent streams, it happens that reading one advances
+ // the other, too. Hence the two passes are imperative !
+
+ // initialize mask stream
+ std::unique_ptr<ImageStream> imgStrMask(
+ new ImageStream(maskStr,
+ maskWidth,
+ maskColorMap->getNumPixelComps(),
+ maskColorMap->getBits()));
+
+ imgStrMask->reset();
+ for( int y = 0; y < maskHeight; ++y )
+ {
+ pm = imgStrMask->getLine();
+ for( int x = 0; x < maskWidth; ++x )
+ {
+ maskColorMap->getGray(pm,&alpha);
+ pm += maskColorMap->getNumPixelComps();
+ int nIndex = (y*height/maskHeight) * (width*4+1) + // mapped line
+ (x*width/maskWidth)*4 + 1 + 3 // mapped column
+ ;
+ aScanlines[ nIndex ] = colToByte(alpha);
+ }
+ }
+
+ imgStr.reset();
+ imgStrMask.reset();
+
+ // begind IDAT chunk for scanline data
+ size_t nIdx = startChunk( "IDAT", o_rOutputBuf );
+ // compress scanlines
+ deflateBuffer( aScanlines.data(), aScanlines.size(), o_rOutputBuf );
+ // end IDAT chunk
+ endChunk( nIdx, o_rOutputBuf );
+ // output IEND
+ appendIEND( o_rOutputBuf );
+}
+
+// one bit mask; 0 bits opaque
+void PngHelper::createPng( OutputBuffer& o_rOutputBuf,
+ Stream* str,
+ int width, int height, GfxImageColorMap* colorMap,
+ Stream* maskStr,
+ int maskWidth, int maskHeight,
+ bool maskInvert
+ )
+{
+ appendFileHeader( o_rOutputBuf );
+ appendIHDR( o_rOutputBuf, width, height, 8, 6 ); // RGBA image
+
+ // initialize stream
+ unsigned char *p;
+ GfxRGB rgb;
+ std::unique_ptr<ImageStream> imgStr(
+ new ImageStream(str,
+ width,
+ colorMap->getNumPixelComps(),
+ colorMap->getBits()));
+ imgStr->reset();
+
+ // create scan line data buffer
+ OutputBuffer aScanlines;
+ aScanlines.reserve( width*height*4 + height );
+
+ for( int y=0; y<height; ++y)
+ {
+ aScanlines.push_back( 0 );
+ p = imgStr->getLine();
+ for( int x=0; x<width; ++x)
+ {
+ colorMap->getRGB(p, &rgb);
+ aScanlines.push_back(colToByte(rgb.r));
+ aScanlines.push_back(colToByte(rgb.g));
+ aScanlines.push_back(colToByte(rgb.b));
+ aScanlines.push_back( 0xff );
+
+ p +=colorMap->getNumPixelComps();
+ }
+ }
+
+
+ // now fill in the mask data
+
+ // CAUTION: originally this was done in one single loop
+ // it caused merry chaos; the reason is that maskStr and str are
+ // not independent streams, it happens that reading one advances
+ // the other, too. Hence the two passes are imperative !
+
+ // initialize mask stream
+ std::unique_ptr<ImageStream> imgStrMask(
+ new ImageStream(maskStr, maskWidth, 1, 1));
+
+ imgStrMask->reset();
+ for( int y = 0; y < maskHeight; ++y )
+ {
+ for( int x = 0; x < maskWidth; ++x )
+ {
+ unsigned char aPixel = 0;
+ imgStrMask->getPixel( &aPixel );
+ int nIndex = (y*height/maskHeight) * (width*4+1) + // mapped line
+ (x*width/maskWidth)*4 + 1 + 3 // mapped column
+ ;
+ if( maskInvert )
+ aScanlines[ nIndex ] = aPixel ? 0xff : 0x00;
+ else
+ aScanlines[ nIndex ] = aPixel ? 0x00 : 0xff;
+ }
+ }
+
+ imgStr.reset();
+ imgStrMask.reset();
+
+ // begind IDAT chunk for scanline data
+ size_t nIdx = startChunk( "IDAT", o_rOutputBuf );
+ // compress scanlines
+ deflateBuffer( aScanlines.data(), aScanlines.size(), o_rOutputBuf );
+ // end IDAT chunk
+ endChunk( nIdx, o_rOutputBuf );
+ // output IEND
+ appendIEND( o_rOutputBuf );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */