fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / sdext / source / pdfimport / xpdfwrapper / pnghelper.cxx
blob37ec3b992be0acc941f8ca4759852e28de137126
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "pnghelper.hxx"
21 #include <sal/macros.h>
23 #include <zlib.h>
25 using namespace pdfi;
27 // checksum helpers, courtesy of libpng.org
29 /* Table of CRCs of all 8-bit messages. */
30 sal_uInt32 PngHelper::crc_table[256];
32 /* Flag: has the table been computed? Initially false. */
33 bool PngHelper::bCRCTableInit = true;
35 /* Make the table for a fast CRC. */
36 void PngHelper::initCRCTable()
38 for (sal_uInt32 n = 0; n < 256; n++)
40 sal_uInt32 c = n;
41 for (int k = 0; k < 8; k++)
43 if (c & 1)
44 c = 0xedb88320L ^ (c >> 1);
45 else
46 c = c >> 1;
48 crc_table[n] = c;
50 bCRCTableInit = false;
53 /* Update a running CRC with the bytes buf[0..len-1]--the CRC
54 should be initialized to all 1's, and the transmitted value
55 is the 1's complement of the final running CRC (see the
56 crc() routine below)). */
58 void PngHelper::updateCRC( sal_uInt32& io_rCRC, const sal_uInt8* i_pBuf, size_t i_nLen )
60 if( bCRCTableInit )
61 initCRCTable();
63 sal_uInt32 nCRC = io_rCRC;
64 for( size_t n = 0; n < i_nLen; n++ )
65 nCRC = crc_table[(nCRC ^ i_pBuf[n]) & 0xff] ^ (nCRC >> 8);
66 io_rCRC = nCRC;
69 sal_uInt32 PngHelper::getCRC( const sal_uInt8* i_pBuf, size_t i_nLen )
71 sal_uInt32 nCRC = 0xffffffff;
72 updateCRC( nCRC, i_pBuf, i_nLen );
73 return nCRC ^ 0xffffffff;
76 sal_uInt32 PngHelper::deflateBuffer( const Output_t* i_pBuf, size_t i_nLen, OutputBuffer& o_rOut )
78 size_t nOrigSize = o_rOut.size();
80 // prepare z stream
81 z_stream aStream;
82 aStream.zalloc = Z_NULL;
83 aStream.zfree = Z_NULL;
84 aStream.opaque = Z_NULL;
85 deflateInit( &aStream, Z_BEST_COMPRESSION );
86 aStream.avail_in = uInt(i_nLen);
87 aStream.next_in = const_cast<Bytef*>(i_pBuf);
89 sal_uInt8 aOutBuf[ 32768 ];
92 aStream.avail_out = sizeof( aOutBuf );
93 aStream.next_out = aOutBuf;
95 if( deflate( &aStream, Z_FINISH ) == Z_STREAM_ERROR )
97 deflateEnd( &aStream );
98 // scrao the data of this broken stream
99 o_rOut.resize( nOrigSize );
100 return 0;
103 // append compressed bytes
104 sal_uInt32 nCompressedBytes = sizeof( aOutBuf ) - aStream.avail_out;
105 if( nCompressedBytes )
106 o_rOut.insert( o_rOut.end(), aOutBuf, aOutBuf+nCompressedBytes );
108 } while( aStream.avail_out == 0 );
110 // cleanup
111 deflateEnd( &aStream );
113 return sal_uInt32( o_rOut.size() - nOrigSize );
116 void PngHelper::appendFileHeader( OutputBuffer& o_rOutputBuf )
118 static const unsigned char aHeader[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
120 o_rOutputBuf.insert( o_rOutputBuf.end(), aHeader, aHeader + SAL_N_ELEMENTS(aHeader) );
123 size_t PngHelper::startChunk( const char* pChunkName, OutputBuffer& o_rOutputBuf )
125 size_t nIndex = sal_uInt32( o_rOutputBuf.size() );
126 o_rOutputBuf.insert( o_rOutputBuf.end(), 4, (Output_t)0 );
127 o_rOutputBuf.push_back( pChunkName[0] );
128 o_rOutputBuf.push_back( pChunkName[1] );
129 o_rOutputBuf.push_back( pChunkName[2] );
130 o_rOutputBuf.push_back( pChunkName[3] );
131 return nIndex;
134 void PngHelper::set( sal_uInt32 i_nValue, OutputBuffer& o_rOutputBuf, size_t i_nIndex )
136 o_rOutputBuf[ i_nIndex ] = (i_nValue & 0xff000000) >> 24;
137 o_rOutputBuf[ i_nIndex+1 ] = (i_nValue & 0x00ff0000) >> 16;
138 o_rOutputBuf[ i_nIndex+2 ] = (i_nValue & 0x0000ff00) >> 8;
139 o_rOutputBuf[ i_nIndex+3 ] = (i_nValue & 0x000000ff);
142 void PngHelper::endChunk( size_t nStart, OutputBuffer& o_rOutputBuf )
144 if( nStart+8 > o_rOutputBuf.size() )
145 return; // something broken is going on
147 // update chunk length
148 size_t nLen = o_rOutputBuf.size() - nStart;
149 sal_uInt32 nDataLen = sal_uInt32(nLen)-8;
150 set( nDataLen, o_rOutputBuf, nStart );
152 // append chunk crc
153 sal_uInt32 nChunkCRC = getCRC( (sal_uInt8*)&o_rOutputBuf[nStart+4], nLen-4 );
154 append( nChunkCRC, o_rOutputBuf );
157 void PngHelper::appendIHDR( OutputBuffer& o_rOutputBuf, int width, int height, int depth, int colortype )
159 size_t nStart = startChunk( "IHDR", o_rOutputBuf );
160 append( width, o_rOutputBuf );
161 append( height, o_rOutputBuf );
162 o_rOutputBuf.push_back( Output_t(depth) );
163 o_rOutputBuf.push_back( Output_t(colortype) );
164 o_rOutputBuf.push_back( 0 ); // compression method deflate
165 o_rOutputBuf.push_back( 0 ); // filtering method 0 (default)
166 o_rOutputBuf.push_back( 0 ); // no interlacing
167 endChunk( nStart, o_rOutputBuf );
170 void PngHelper::appendIEND( OutputBuffer& o_rOutputBuf )
172 size_t nStart = startChunk( "IEND", o_rOutputBuf );
173 endChunk( nStart, o_rOutputBuf );
176 void PngHelper::createPng( OutputBuffer& o_rOutputBuf,
177 Stream* str,
178 int width,
179 int height,
180 GfxRGB& zeroColor,
181 GfxRGB& oneColor,
182 bool bIsMask
185 appendFileHeader( o_rOutputBuf );
186 appendIHDR( o_rOutputBuf, width, height, 1, 3 );
188 // write palette
189 size_t nIdx = startChunk( "PLTE", o_rOutputBuf );
190 // write colors 0 and 1
191 o_rOutputBuf.push_back(colToByte(zeroColor.r));
192 o_rOutputBuf.push_back(colToByte(zeroColor.g));
193 o_rOutputBuf.push_back(colToByte(zeroColor.b));
194 o_rOutputBuf.push_back(colToByte(oneColor.r));
195 o_rOutputBuf.push_back(colToByte(oneColor.g));
196 o_rOutputBuf.push_back(colToByte(oneColor.b));
197 // end PLTE chunk
198 endChunk( nIdx, o_rOutputBuf );
200 if( bIsMask )
202 // write tRNS chunk
203 nIdx = startChunk( "tRNS", o_rOutputBuf );
204 o_rOutputBuf.push_back( 0xff );
205 o_rOutputBuf.push_back( 0 );
206 // end tRNS chunk
207 endChunk( nIdx, o_rOutputBuf );
210 // create scan line data buffer
211 OutputBuffer aScanlines;
212 int nLineSize = (width + 7)/8;
213 aScanlines.reserve( nLineSize * height + height );
215 str->reset();
216 for( int y = 0; y < height; y++ )
218 // determine filter type (none) for this scanline
219 aScanlines.push_back( 0 );
220 for( int x = 0; x < nLineSize; x++ )
221 aScanlines.push_back( str->getChar() );
224 // begin IDAT chunk for scanline data
225 nIdx = startChunk( "IDAT", o_rOutputBuf );
226 // compress scanlines
227 deflateBuffer( &aScanlines[0], aScanlines.size(), o_rOutputBuf );
228 // end IDAT chunk
229 endChunk( nIdx, o_rOutputBuf );
231 // output IEND
232 appendIEND( o_rOutputBuf );
235 void PngHelper::createPng( OutputBuffer& o_rOutputBuf,
236 Stream* str,
237 int width, int height, GfxImageColorMap* colorMap,
238 Stream* maskStr,
239 int maskWidth, int maskHeight, GfxImageColorMap* maskColorMap )
241 appendFileHeader( o_rOutputBuf );
242 appendIHDR( o_rOutputBuf, width, height, 8, 6 ); // RGBA image
244 // initialize stream
245 Guchar *p, *pm;
246 GfxRGB rgb;
247 GfxGray alpha;
248 ImageStream* imgStr =
249 new ImageStream(str,
250 width,
251 colorMap->getNumPixelComps(),
252 colorMap->getBits());
253 imgStr->reset();
255 // create scan line data buffer
256 OutputBuffer aScanlines;
257 aScanlines.reserve( width*height*4 + height );
259 for( int y=0; y<height; ++y)
261 aScanlines.push_back( 0 );
262 p = imgStr->getLine();
263 for( int x=0; x<width; ++x)
265 colorMap->getRGB(p, &rgb);
266 aScanlines.push_back(colToByte(rgb.r));
267 aScanlines.push_back(colToByte(rgb.g));
268 aScanlines.push_back(colToByte(rgb.b));
269 aScanlines.push_back( 0xff );
271 p +=colorMap->getNumPixelComps();
276 // now fill in the mask data
278 // CAUTION: originally this was done in one single loop
279 // it caused merry chaos; the reason is that maskStr and str are
280 // not independent streams, it happens that reading one advances
281 // the other, too. Hence the two passes are imperative !
283 // initialize mask stream
284 ImageStream* imgStrMask =
285 new ImageStream(maskStr,
286 maskWidth,
287 maskColorMap->getNumPixelComps(),
288 maskColorMap->getBits());
290 imgStrMask->reset();
291 for( int y = 0; y < maskHeight; ++y )
293 pm = imgStrMask->getLine();
294 for( int x = 0; x < maskWidth; ++x )
296 maskColorMap->getGray(pm,&alpha);
297 pm += maskColorMap->getNumPixelComps();
298 int nIndex = (y*height/maskHeight) * (width*4+1) + // mapped line
299 (x*width/maskWidth)*4 + 1 + 3 // mapped column
301 aScanlines[ nIndex ] = colToByte(alpha);
305 delete imgStr;
306 delete imgStrMask;
308 // begind IDAT chunk for scanline data
309 size_t nIdx = startChunk( "IDAT", o_rOutputBuf );
310 // compress scanlines
311 deflateBuffer( &aScanlines[0], aScanlines.size(), o_rOutputBuf );
312 // end IDAT chunk
313 endChunk( nIdx, o_rOutputBuf );
314 // output IEND
315 appendIEND( o_rOutputBuf );
318 // one bit mask; 0 bits opaque
319 void PngHelper::createPng( OutputBuffer& o_rOutputBuf,
320 Stream* str,
321 int width, int height, GfxImageColorMap* colorMap,
322 Stream* maskStr,
323 int maskWidth, int maskHeight,
324 bool maskInvert
327 appendFileHeader( o_rOutputBuf );
328 appendIHDR( o_rOutputBuf, width, height, 8, 6 ); // RGBA image
330 // initialize stream
331 Guchar *p;
332 GfxRGB rgb;
333 ImageStream* imgStr =
334 new ImageStream(str,
335 width,
336 colorMap->getNumPixelComps(),
337 colorMap->getBits());
338 imgStr->reset();
340 // create scan line data buffer
341 OutputBuffer aScanlines;
342 aScanlines.reserve( width*height*4 + height );
344 for( int y=0; y<height; ++y)
346 aScanlines.push_back( 0 );
347 p = imgStr->getLine();
348 for( int x=0; x<width; ++x)
350 colorMap->getRGB(p, &rgb);
351 aScanlines.push_back(colToByte(rgb.r));
352 aScanlines.push_back(colToByte(rgb.g));
353 aScanlines.push_back(colToByte(rgb.b));
354 aScanlines.push_back( 0xff );
356 p +=colorMap->getNumPixelComps();
361 // now fill in the mask data
363 // CAUTION: originally this was done in one single loop
364 // it caused merry chaos; the reason is that maskStr and str are
365 // not independent streams, it happens that reading one advances
366 // the other, too. Hence the two passes are imperative !
368 // initialize mask stream
369 ImageStream* imgStrMask =
370 new ImageStream(maskStr, maskWidth, 1, 1);
372 imgStrMask->reset();
373 for( int y = 0; y < maskHeight; ++y )
375 for( int x = 0; x < maskWidth; ++x )
377 Guchar aPixel = 0;
378 imgStrMask->getPixel( &aPixel );
379 int nIndex = (y*height/maskHeight) * (width*4+1) + // mapped line
380 (x*width/maskWidth)*4 + 1 + 3 // mapped column
382 if( maskInvert )
383 aScanlines[ nIndex ] = aPixel ? 0xff : 0x00;
384 else
385 aScanlines[ nIndex ] = aPixel ? 0x00 : 0xff;
389 delete imgStr;
390 delete imgStrMask;
392 // begind IDAT chunk for scanline data
393 size_t nIdx = startChunk( "IDAT", o_rOutputBuf );
394 // compress scanlines
395 deflateBuffer( &aScanlines[0], aScanlines.size(), o_rOutputBuf );
396 // end IDAT chunk
397 endChunk( nIdx, o_rOutputBuf );
398 // output IEND
399 appendIEND( o_rOutputBuf );
402 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */