Branch data Line data Source code
1 : : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : : /*************************************************************************
3 : : *
4 : : * OpenOffice.org - a multi-platform office productivity suite
5 : : *
6 : : * The Contents of this file are made available subject to
7 : : * the terms of GNU General Public License Version 2.
8 : : *
9 : : *
10 : : * GNU General Public License, version 2
11 : : * =============================================
12 : : * Copyright 2005 by Sun Microsystems, Inc.
13 : : * 901 San Antonio Road, Palo Alto, CA 94303, USA
14 : : *
15 : : * This program is free software; you can redistribute it and/or
16 : : * modify it under the terms of the GNU General Public License as
17 : : * published by the Free Software Foundation; either version 2 of
18 : : * the License, or (at your option) any later version.
19 : : *
20 : : * This program is distributed in the hope that it will be useful,
21 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 : : * GNU General Public License for more details.
24 : : *
25 : : * You should have received a copy of the GNU General Public
26 : : * License along with this program; if not, write to the Free
27 : : * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
28 : : * Boston, MA 02110-1301, USA.
29 : : *
30 : : ************************************************************************/
31 : :
32 : : #include "pnghelper.hxx"
33 : : #include <sal/macros.h>
34 : :
35 : : #ifdef SYSTEM_ZLIB
36 : : #include "zlib.h"
37 : : #else
38 : : #define ZLIB_INTERNAL 1
39 : : #include <zlib/zlib.h>
40 : : #endif
41 : :
42 : : using namespace pdfi;
43 : :
44 : : // checksum helpers, courtesy of libpng.org
45 : :
46 : : /* Table of CRCs of all 8-bit messages. */
47 : : sal_uInt32 PngHelper::crc_table[256];
48 : :
49 : : /* Flag: has the table been computed? Initially false. */
50 : : bool PngHelper::bCRCTableInit = true;
51 : :
52 : : /* Make the table for a fast CRC. */
53 : 0 : void PngHelper::initCRCTable()
54 : : {
55 [ # # ]: 0 : for (sal_uInt32 n = 0; n < 256; n++)
56 : : {
57 : 0 : sal_uInt32 c = n;
58 [ # # ]: 0 : for (int k = 0; k < 8; k++)
59 : : {
60 [ # # ]: 0 : if (c & 1)
61 : 0 : c = 0xedb88320L ^ (c >> 1);
62 : : else
63 : 0 : c = c >> 1;
64 : : }
65 : 0 : crc_table[n] = c;
66 : : }
67 : 0 : bCRCTableInit = false;
68 : 0 : }
69 : :
70 : : /* Update a running CRC with the bytes buf[0..len-1]--the CRC
71 : : should be initialized to all 1's, and the transmitted value
72 : : is the 1's complement of the final running CRC (see the
73 : : crc() routine below)). */
74 : :
75 : 0 : void PngHelper::updateCRC( sal_uInt32& io_rCRC, const sal_uInt8* i_pBuf, size_t i_nLen )
76 : : {
77 [ # # ]: 0 : if( bCRCTableInit )
78 : 0 : initCRCTable();
79 : :
80 : 0 : sal_uInt32 nCRC = io_rCRC;
81 [ # # ]: 0 : for( size_t n = 0; n < i_nLen; n++ )
82 : 0 : nCRC = crc_table[(nCRC ^ i_pBuf[n]) & 0xff] ^ (nCRC >> 8);
83 : 0 : io_rCRC = nCRC;
84 : 0 : }
85 : :
86 : 0 : sal_uInt32 PngHelper::getCRC( const sal_uInt8* i_pBuf, size_t i_nLen )
87 : : {
88 : 0 : sal_uInt32 nCRC = 0xffffffff;
89 : 0 : updateCRC( nCRC, i_pBuf, i_nLen );
90 : 0 : return nCRC ^ 0xffffffff;
91 : : }
92 : :
93 : 0 : sal_uInt32 PngHelper::deflateBuffer( const Output_t* i_pBuf, size_t i_nLen, OutputBuffer& o_rOut )
94 : : {
95 : 0 : size_t nOrigSize = o_rOut.size();
96 : :
97 : : // prepare z stream
98 : : z_stream aStream;
99 : 0 : aStream.zalloc = Z_NULL;
100 : 0 : aStream.zfree = Z_NULL;
101 : 0 : aStream.opaque = Z_NULL;
102 [ # # ]: 0 : deflateInit( &aStream, Z_BEST_COMPRESSION );
103 : 0 : aStream.avail_in = uInt(i_nLen);
104 : 0 : aStream.next_in = (Bytef*)i_pBuf;
105 : :
106 : : sal_uInt8 aOutBuf[ 32768 ];
107 [ # # ]: 0 : do
108 : : {
109 : 0 : aStream.avail_out = sizeof( aOutBuf );
110 : 0 : aStream.next_out = aOutBuf;
111 : :
112 [ # # ][ # # ]: 0 : if( deflate( &aStream, Z_FINISH ) == Z_STREAM_ERROR )
113 : : {
114 [ # # ]: 0 : deflateEnd( &aStream );
115 : : // scrao the data of this broken stream
116 [ # # ]: 0 : o_rOut.resize( nOrigSize );
117 : 0 : return 0;
118 : : }
119 : :
120 : : // append compressed bytes
121 : 0 : sal_uInt32 nCompressedBytes = sizeof( aOutBuf ) - aStream.avail_out;
122 [ # # ]: 0 : if( nCompressedBytes )
123 [ # # ]: 0 : o_rOut.insert( o_rOut.end(), aOutBuf, aOutBuf+nCompressedBytes );
124 : :
125 : : } while( aStream.avail_out == 0 );
126 : :
127 : : // cleanup
128 [ # # ]: 0 : deflateEnd( &aStream );
129 : :
130 : 0 : return sal_uInt32( o_rOut.size() - nOrigSize );
131 : : }
132 : :
133 : 0 : void PngHelper::appendFileHeader( OutputBuffer& o_rOutputBuf )
134 : : {
135 : : static const unsigned char aHeader[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
136 : :
137 : 0 : o_rOutputBuf.insert( o_rOutputBuf.end(), aHeader, aHeader + SAL_N_ELEMENTS(aHeader) );
138 : 0 : }
139 : :
140 : 0 : size_t PngHelper::startChunk( const char* pChunkName, OutputBuffer& o_rOutputBuf )
141 : : {
142 : 0 : size_t nIndex = sal_uInt32( o_rOutputBuf.size() );
143 [ # # ]: 0 : o_rOutputBuf.insert( o_rOutputBuf.end(), 4, (Output_t)0 );
144 [ # # ]: 0 : o_rOutputBuf.push_back( pChunkName[0] );
145 [ # # ]: 0 : o_rOutputBuf.push_back( pChunkName[1] );
146 [ # # ]: 0 : o_rOutputBuf.push_back( pChunkName[2] );
147 [ # # ]: 0 : o_rOutputBuf.push_back( pChunkName[3] );
148 : 0 : return nIndex;
149 : : }
150 : :
151 : 0 : void PngHelper::set( sal_uInt32 i_nValue, OutputBuffer& o_rOutputBuf, size_t i_nIndex )
152 : : {
153 : 0 : o_rOutputBuf[ i_nIndex ] = (i_nValue & 0xff000000) >> 24;
154 : 0 : o_rOutputBuf[ i_nIndex+1 ] = (i_nValue & 0x00ff0000) >> 16;
155 : 0 : o_rOutputBuf[ i_nIndex+2 ] = (i_nValue & 0x0000ff00) >> 8;
156 : 0 : o_rOutputBuf[ i_nIndex+3 ] = (i_nValue & 0x000000ff);
157 : 0 : }
158 : :
159 : 0 : void PngHelper::endChunk( size_t nStart, OutputBuffer& o_rOutputBuf )
160 : : {
161 [ # # ]: 0 : if( nStart+8 > o_rOutputBuf.size() )
162 : 0 : return; // something broken is going on
163 : :
164 : : // update chunk length
165 : 0 : size_t nLen = o_rOutputBuf.size() - nStart;
166 : 0 : sal_uInt32 nDataLen = sal_uInt32(nLen)-8;
167 : 0 : set( nDataLen, o_rOutputBuf, nStart );
168 : :
169 : : // append chunk crc
170 : 0 : sal_uInt32 nChunkCRC = getCRC( (sal_uInt8*)&o_rOutputBuf[nStart+4], nLen-4 );
171 : 0 : append( nChunkCRC, o_rOutputBuf );
172 : : }
173 : :
174 : 0 : void PngHelper::appendIHDR( OutputBuffer& o_rOutputBuf, int width, int height, int depth, int colortype )
175 : : {
176 : 0 : size_t nStart = startChunk( "IHDR", o_rOutputBuf );
177 : 0 : append( width, o_rOutputBuf );
178 : 0 : append( height, o_rOutputBuf );
179 [ # # ]: 0 : o_rOutputBuf.push_back( Output_t(depth) );
180 [ # # ]: 0 : o_rOutputBuf.push_back( Output_t(colortype) );
181 [ # # ]: 0 : o_rOutputBuf.push_back( 0 ); // compression method deflate
182 [ # # ]: 0 : o_rOutputBuf.push_back( 0 ); // filtering method 0 (default)
183 [ # # ]: 0 : o_rOutputBuf.push_back( 0 ); // no interlacing
184 : 0 : endChunk( nStart, o_rOutputBuf );
185 : 0 : }
186 : :
187 : 0 : void PngHelper::appendIEND( OutputBuffer& o_rOutputBuf )
188 : : {
189 : 0 : size_t nStart = startChunk( "IEND", o_rOutputBuf );
190 : 0 : endChunk( nStart, o_rOutputBuf );
191 : 0 : }
192 : :
193 : 0 : void PngHelper::createPng( OutputBuffer& o_rOutputBuf,
194 : : Stream* str,
195 : : int width,
196 : : int height,
197 : : GfxRGB& zeroColor,
198 : : GfxRGB& oneColor,
199 : : bool bIsMask
200 : : )
201 : : {
202 [ # # ]: 0 : appendFileHeader( o_rOutputBuf );
203 [ # # ]: 0 : appendIHDR( o_rOutputBuf, width, height, 1, 3 );
204 : :
205 : : // write palette
206 [ # # ]: 0 : size_t nIdx = startChunk( "PLTE", o_rOutputBuf );
207 : : // write colors 0 and 1
208 [ # # ]: 0 : o_rOutputBuf.push_back(colToByte(zeroColor.r));
209 [ # # ]: 0 : o_rOutputBuf.push_back(colToByte(zeroColor.g));
210 [ # # ]: 0 : o_rOutputBuf.push_back(colToByte(zeroColor.b));
211 [ # # ]: 0 : o_rOutputBuf.push_back(colToByte(oneColor.r));
212 [ # # ]: 0 : o_rOutputBuf.push_back(colToByte(oneColor.g));
213 [ # # ]: 0 : o_rOutputBuf.push_back(colToByte(oneColor.b));
214 : : // end PLTE chunk
215 [ # # ]: 0 : endChunk( nIdx, o_rOutputBuf );
216 : :
217 [ # # ]: 0 : if( bIsMask )
218 : : {
219 : : // write tRNS chunk
220 [ # # ]: 0 : nIdx = startChunk( "tRNS", o_rOutputBuf );
221 [ # # ]: 0 : o_rOutputBuf.push_back( 0xff );
222 [ # # ]: 0 : o_rOutputBuf.push_back( 0 );
223 : : // end tRNS chunk
224 [ # # ]: 0 : endChunk( nIdx, o_rOutputBuf );
225 : : }
226 : :
227 : : // create scan line data buffer
228 [ # # ]: 0 : OutputBuffer aScanlines;
229 : 0 : int nLineSize = (width + 7)/8;
230 [ # # ]: 0 : aScanlines.reserve( nLineSize * height + height );
231 : :
232 [ # # ]: 0 : str->reset();
233 [ # # ]: 0 : for( int y = 0; y < height; y++ )
234 : : {
235 : : // determine filter type (none) for this scanline
236 [ # # ]: 0 : aScanlines.push_back( 0 );
237 [ # # ]: 0 : for( int x = 0; x < nLineSize; x++ )
238 [ # # ][ # # ]: 0 : aScanlines.push_back( str->getChar() );
239 : : }
240 : :
241 : : // begin IDAT chunk for scanline data
242 [ # # ]: 0 : nIdx = startChunk( "IDAT", o_rOutputBuf );
243 : : // compress scanlines
244 [ # # ][ # # ]: 0 : deflateBuffer( &aScanlines[0], aScanlines.size(), o_rOutputBuf );
245 : : // end IDAT chunk
246 [ # # ]: 0 : endChunk( nIdx, o_rOutputBuf );
247 : :
248 : : // output IEND
249 [ # # ]: 0 : appendIEND( o_rOutputBuf );
250 : 0 : }
251 : :
252 : 0 : void PngHelper::createPng( OutputBuffer& o_rOutputBuf,
253 : : Stream* str,
254 : : int width, int height, GfxImageColorMap* colorMap,
255 : : Stream* maskStr,
256 : : int maskWidth, int maskHeight, GfxImageColorMap* maskColorMap )
257 : : {
258 [ # # ]: 0 : appendFileHeader( o_rOutputBuf );
259 [ # # ]: 0 : appendIHDR( o_rOutputBuf, width, height, 8, 6 ); // RGBA image
260 : :
261 : : // initialize stream
262 : : Guchar *p, *pm;
263 : : GfxRGB rgb;
264 : : GfxGray alpha;
265 : : ImageStream* imgStr =
266 : : new ImageStream(str,
267 : : width,
268 : : colorMap->getNumPixelComps(),
269 [ # # ][ # # ]: 0 : colorMap->getBits());
270 [ # # ]: 0 : imgStr->reset();
271 : :
272 : : // create scan line data buffer
273 [ # # ]: 0 : OutputBuffer aScanlines;
274 [ # # ]: 0 : aScanlines.reserve( width*height*4 + height );
275 : :
276 [ # # ]: 0 : for( int y=0; y<height; ++y)
277 : : {
278 [ # # ]: 0 : aScanlines.push_back( 0 );
279 [ # # ]: 0 : p = imgStr->getLine();
280 [ # # ]: 0 : for( int x=0; x<width; ++x)
281 : : {
282 [ # # ]: 0 : colorMap->getRGB(p, &rgb);
283 [ # # ]: 0 : aScanlines.push_back(colToByte(rgb.r));
284 [ # # ]: 0 : aScanlines.push_back(colToByte(rgb.g));
285 [ # # ]: 0 : aScanlines.push_back(colToByte(rgb.b));
286 [ # # ]: 0 : aScanlines.push_back( 0xff );
287 : :
288 : 0 : p +=colorMap->getNumPixelComps();
289 : : }
290 : : }
291 : :
292 : :
293 : : // now fill in the mask data
294 : :
295 : : // CAUTION: originally this was done in one single loop
296 : : // it caused merry chaos; the reason is that maskStr and str are
297 : : // not independent streams, it happens that reading one advances
298 : : // the other, too. Hence the two passes are imperative !
299 : :
300 : : // initialize mask stream
301 : : ImageStream* imgStrMask =
302 : : new ImageStream(maskStr,
303 : : maskWidth,
304 : : maskColorMap->getNumPixelComps(),
305 [ # # ][ # # ]: 0 : maskColorMap->getBits());
306 : :
307 [ # # ]: 0 : imgStrMask->reset();
308 [ # # ]: 0 : for( int y = 0; y < maskHeight; ++y )
309 : : {
310 [ # # ]: 0 : pm = imgStrMask->getLine();
311 [ # # ]: 0 : for( int x = 0; x < maskWidth; ++x )
312 : : {
313 [ # # ]: 0 : maskColorMap->getGray(pm,&alpha);
314 : 0 : pm += maskColorMap->getNumPixelComps();
315 : : int nIndex = (y*height/maskHeight) * (width*4+1) + // mapped line
316 : 0 : (x*width/maskWidth)*4 + 1 + 3 // mapped column
317 : : ;
318 [ # # ]: 0 : aScanlines[ nIndex ] = colToByte(alpha);
319 : : }
320 : : }
321 : :
322 [ # # ][ # # ]: 0 : delete imgStr;
323 [ # # ][ # # ]: 0 : delete imgStrMask;
324 : :
325 : : // begind IDAT chunk for scanline data
326 [ # # ]: 0 : size_t nIdx = startChunk( "IDAT", o_rOutputBuf );
327 : : // compress scanlines
328 [ # # ][ # # ]: 0 : deflateBuffer( &aScanlines[0], aScanlines.size(), o_rOutputBuf );
329 : : // end IDAT chunk
330 [ # # ]: 0 : endChunk( nIdx, o_rOutputBuf );
331 : : // output IEND
332 [ # # ]: 0 : appendIEND( o_rOutputBuf );
333 : 0 : }
334 : :
335 : : // one bit mask; 0 bits opaque
336 : 0 : void PngHelper::createPng( OutputBuffer& o_rOutputBuf,
337 : : Stream* str,
338 : : int width, int height, GfxImageColorMap* colorMap,
339 : : Stream* maskStr,
340 : : int maskWidth, int maskHeight,
341 : : bool maskInvert
342 : : )
343 : : {
344 [ # # ]: 0 : appendFileHeader( o_rOutputBuf );
345 [ # # ]: 0 : appendIHDR( o_rOutputBuf, width, height, 8, 6 ); // RGBA image
346 : :
347 : : // initialize stream
348 : : Guchar *p;
349 : : GfxRGB rgb;
350 : : ImageStream* imgStr =
351 : : new ImageStream(str,
352 : : width,
353 : : colorMap->getNumPixelComps(),
354 [ # # ][ # # ]: 0 : colorMap->getBits());
355 [ # # ]: 0 : imgStr->reset();
356 : :
357 : : // create scan line data buffer
358 [ # # ]: 0 : OutputBuffer aScanlines;
359 [ # # ]: 0 : aScanlines.reserve( width*height*4 + height );
360 : :
361 [ # # ]: 0 : for( int y=0; y<height; ++y)
362 : : {
363 [ # # ]: 0 : aScanlines.push_back( 0 );
364 [ # # ]: 0 : p = imgStr->getLine();
365 [ # # ]: 0 : for( int x=0; x<width; ++x)
366 : : {
367 [ # # ]: 0 : colorMap->getRGB(p, &rgb);
368 [ # # ]: 0 : aScanlines.push_back(colToByte(rgb.r));
369 [ # # ]: 0 : aScanlines.push_back(colToByte(rgb.g));
370 [ # # ]: 0 : aScanlines.push_back(colToByte(rgb.b));
371 [ # # ]: 0 : aScanlines.push_back( 0xff );
372 : :
373 : 0 : p +=colorMap->getNumPixelComps();
374 : : }
375 : : }
376 : :
377 : :
378 : : // now fill in the mask data
379 : :
380 : : // CAUTION: originally this was done in one single loop
381 : : // it caused merry chaos; the reason is that maskStr and str are
382 : : // not independent streams, it happens that reading one advances
383 : : // the other, too. Hence the two passes are imperative !
384 : :
385 : : // initialize mask stream
386 : : ImageStream* imgStrMask =
387 [ # # ][ # # ]: 0 : new ImageStream(maskStr, maskWidth, 1, 1);
388 : :
389 [ # # ]: 0 : imgStrMask->reset();
390 [ # # ]: 0 : for( int y = 0; y < maskHeight; ++y )
391 : : {
392 [ # # ]: 0 : for( int x = 0; x < maskWidth; ++x )
393 : : {
394 : 0 : Guchar aPixel = 0;
395 [ # # ]: 0 : imgStrMask->getPixel( &aPixel );
396 : : int nIndex = (y*height/maskHeight) * (width*4+1) + // mapped line
397 : 0 : (x*width/maskWidth)*4 + 1 + 3 // mapped column
398 : : ;
399 [ # # ]: 0 : if( maskInvert )
400 [ # # ][ # # ]: 0 : aScanlines[ nIndex ] = aPixel ? 0xff : 0x00;
401 : : else
402 [ # # ][ # # ]: 0 : aScanlines[ nIndex ] = aPixel ? 0x00 : 0xff;
403 : : }
404 : : }
405 : :
406 [ # # ][ # # ]: 0 : delete imgStr;
407 [ # # ][ # # ]: 0 : delete imgStrMask;
408 : :
409 : : // begind IDAT chunk for scanline data
410 [ # # ]: 0 : size_t nIdx = startChunk( "IDAT", o_rOutputBuf );
411 : : // compress scanlines
412 [ # # ][ # # ]: 0 : deflateBuffer( &aScanlines[0], aScanlines.size(), o_rOutputBuf );
413 : : // end IDAT chunk
414 [ # # ]: 0 : endChunk( nIdx, o_rOutputBuf );
415 : : // output IEND
416 [ # # ]: 0 : appendIEND( o_rOutputBuf );
417 : 0 : }
418 : :
419 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|