LibreOffice Module sdext (master) 1
pnghelper.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include "pnghelper.hxx"
21#include <sal/macros.h>
22
23#include <zlib.h>
24
25using namespace pdfi;
26
27// checksum helpers, courtesy of libpng.org
28
29/* Table of CRCs of all 8-bit messages. */
30sal_uInt32 PngHelper::crc_table[256];
31
32/* Flag: has the table been computed? Initially false. */
33bool PngHelper::bCRCTableInit = true;
34
35/* Make the table for a fast CRC. */
36void PngHelper::initCRCTable()
37{
38 for (sal_uInt32 n = 0; n < 256; n++)
39 {
40 sal_uInt32 c = n;
41 for (int k = 0; k < 8; k++)
42 {
43 if (c & 1)
44 c = 0xedb88320L ^ (c >> 1);
45 else
46 c = c >> 1;
47 }
48 crc_table[n] = c;
49 }
50 bCRCTableInit = false;
51}
52
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)). */
57
58void PngHelper::updateCRC( sal_uInt32& io_rCRC, const sal_uInt8* i_pBuf, size_t i_nLen )
59{
60 if( bCRCTableInit )
62
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;
67}
68
69sal_uInt32 PngHelper::getCRC( const sal_uInt8* i_pBuf, size_t i_nLen )
70{
71 sal_uInt32 nCRC = 0xffffffff;
72 updateCRC( nCRC, i_pBuf, i_nLen );
73 return nCRC ^ 0xffffffff;
74}
75
76sal_uInt32 PngHelper::deflateBuffer( const Output_t* i_pBuf, size_t i_nLen, OutputBuffer& o_rOut )
77{
78 size_t nOrigSize = o_rOut.size();
79
80 // prepare z stream
81 z_stream aStream;
82 aStream.zalloc = Z_NULL;
83 aStream.zfree = Z_NULL;
84 aStream.opaque = Z_NULL;
85 aStream.total_out = aStream.total_in = 0;
86 if (Z_OK != deflateInit(&aStream, Z_BEST_COMPRESSION))
87 return 0;
88 aStream.avail_in = uInt(i_nLen);
89 aStream.next_in = i_pBuf;
90
91 sal_uInt8 aOutBuf[ 32768 ];
92 do
93 {
94 aStream.avail_out = sizeof( aOutBuf );
95 aStream.next_out = aOutBuf;
96
97 if( deflate( &aStream, Z_FINISH ) == Z_STREAM_ERROR )
98 {
99 deflateEnd( &aStream );
100 // scrap the data of this broken stream
101 o_rOut.resize( nOrigSize );
102 return 0;
103 }
104
105 // append compressed bytes
106 sal_uInt32 nCompressedBytes = sizeof( aOutBuf ) - aStream.avail_out;
107 if( nCompressedBytes )
108 o_rOut.insert( o_rOut.end(), aOutBuf, aOutBuf+nCompressedBytes );
109
110 } while( aStream.avail_out == 0 );
111
112 // cleanup
113 deflateEnd( &aStream );
114
115 return sal_uInt32( o_rOut.size() - nOrigSize );
116}
117
119{
120 static const unsigned char aHeader[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
121
122 o_rOutputBuf.insert( o_rOutputBuf.end(), aHeader, aHeader + SAL_N_ELEMENTS(aHeader) );
123}
124
125size_t PngHelper::startChunk( const char* pChunkName, OutputBuffer& o_rOutputBuf )
126{
127 size_t nIndex = sal_uInt32( o_rOutputBuf.size() );
128 o_rOutputBuf.insert( o_rOutputBuf.end(), 4, Output_t(0) );
129 o_rOutputBuf.push_back( pChunkName[0] );
130 o_rOutputBuf.push_back( pChunkName[1] );
131 o_rOutputBuf.push_back( pChunkName[2] );
132 o_rOutputBuf.push_back( pChunkName[3] );
133 return nIndex;
134}
135
136void PngHelper::set( sal_uInt32 i_nValue, OutputBuffer& o_rOutputBuf, size_t i_nIndex )
137{
138 o_rOutputBuf[ i_nIndex ] = (i_nValue & 0xff000000) >> 24;
139 o_rOutputBuf[ i_nIndex+1 ] = (i_nValue & 0x00ff0000) >> 16;
140 o_rOutputBuf[ i_nIndex+2 ] = (i_nValue & 0x0000ff00) >> 8;
141 o_rOutputBuf[ i_nIndex+3 ] = (i_nValue & 0x000000ff);
142}
143
144void PngHelper::endChunk( size_t nStart, OutputBuffer& o_rOutputBuf )
145{
146 if( nStart+8 > o_rOutputBuf.size() )
147 return; // something broken is going on
148
149 // update chunk length
150 size_t nLen = o_rOutputBuf.size() - nStart;
151 sal_uInt32 nDataLen = sal_uInt32(nLen)-8;
152 set( nDataLen, o_rOutputBuf, nStart );
153
154 // append chunk crc
155 sal_uInt32 nChunkCRC = getCRC( reinterpret_cast<sal_uInt8*>(&o_rOutputBuf[nStart+4]), nLen-4 );
156 append( nChunkCRC, o_rOutputBuf );
157}
158
159void PngHelper::appendIHDR( OutputBuffer& o_rOutputBuf, int width, int height, int depth, int colortype )
160{
161 size_t nStart = startChunk( "IHDR", o_rOutputBuf );
162 append( width, o_rOutputBuf );
163 append( height, o_rOutputBuf );
164 o_rOutputBuf.push_back( Output_t(depth) );
165 o_rOutputBuf.push_back( Output_t(colortype) );
166 o_rOutputBuf.push_back( 0 ); // compression method deflate
167 o_rOutputBuf.push_back( 0 ); // filtering method 0 (default)
168 o_rOutputBuf.push_back( 0 ); // no interlacing
169 endChunk( nStart, o_rOutputBuf );
170}
171
173{
174 size_t nStart = startChunk( "IEND", o_rOutputBuf );
175 endChunk( nStart, o_rOutputBuf );
176}
177
179 Stream* str,
180 int width,
181 int height,
182 GfxRGB const & zeroColor,
183 GfxRGB const & oneColor,
184 bool bIsMask
185 )
186{
187 appendFileHeader( o_rOutputBuf );
188 appendIHDR( o_rOutputBuf, width, height, 1, 3 );
189
190 // write palette
191 size_t nIdx = startChunk( "PLTE", o_rOutputBuf );
192 // write colors 0 and 1
193 o_rOutputBuf.push_back(colToByte(zeroColor.r));
194 o_rOutputBuf.push_back(colToByte(zeroColor.g));
195 o_rOutputBuf.push_back(colToByte(zeroColor.b));
196 o_rOutputBuf.push_back(colToByte(oneColor.r));
197 o_rOutputBuf.push_back(colToByte(oneColor.g));
198 o_rOutputBuf.push_back(colToByte(oneColor.b));
199 // end PLTE chunk
200 endChunk( nIdx, o_rOutputBuf );
201
202 if( bIsMask )
203 {
204 // write tRNS chunk
205 nIdx = startChunk( "tRNS", o_rOutputBuf );
206 o_rOutputBuf.push_back( 0xff );
207 o_rOutputBuf.push_back( 0 );
208 // end tRNS chunk
209 endChunk( nIdx, o_rOutputBuf );
210 }
211
212 // create scan line data buffer
213 OutputBuffer aScanlines;
214 int nLineSize = (width + 7)/8;
215 aScanlines.reserve( nLineSize * height + height );
216
217 str->reset();
218 for( int y = 0; y < height; y++ )
219 {
220 // determine filter type (none) for this scanline
221 aScanlines.push_back( 0 );
222 for( int x = 0; x < nLineSize; x++ )
223 aScanlines.push_back( str->getChar() );
224 }
225
226 // begin IDAT chunk for scanline data
227 nIdx = startChunk( "IDAT", o_rOutputBuf );
228 // compress scanlines
229 deflateBuffer( aScanlines.data(), aScanlines.size(), o_rOutputBuf );
230 // end IDAT chunk
231 endChunk( nIdx, o_rOutputBuf );
232
233 // output IEND
234 appendIEND( o_rOutputBuf );
235}
236
238 Stream* str,
239 int width, int height, GfxImageColorMap* colorMap,
240 Stream* maskStr,
241 int maskWidth, int maskHeight, GfxImageColorMap* maskColorMap )
242{
243 appendFileHeader( o_rOutputBuf );
244 appendIHDR( o_rOutputBuf, width, height, 8, 6 ); // RGBA image
245
246 // initialize stream
247 unsigned char *p, *pm;
248 GfxRGB rgb;
249 GfxGray alpha;
250 std::unique_ptr<ImageStream> imgStr(
251 new ImageStream(str,
252 width,
253 colorMap->getNumPixelComps(),
254 colorMap->getBits()));
255 imgStr->reset();
256
257 // create scan line data buffer
258 OutputBuffer aScanlines;
259 aScanlines.reserve( width*height*4 + height );
260
261 for( int y=0; y<height; ++y)
262 {
263 aScanlines.push_back( 0 );
264 p = imgStr->getLine();
265 for( int x=0; x<width; ++x)
266 {
267 colorMap->getRGB(p, &rgb);
268 aScanlines.push_back(colToByte(rgb.r));
269 aScanlines.push_back(colToByte(rgb.g));
270 aScanlines.push_back(colToByte(rgb.b));
271 aScanlines.push_back( 0xff );
272
273 p +=colorMap->getNumPixelComps();
274 }
275 }
276
277
278 // now fill in the mask data
279
280 // CAUTION: originally this was done in one single loop
281 // it caused merry chaos; the reason is that maskStr and str are
282 // not independent streams, it happens that reading one advances
283 // the other, too. Hence the two passes are imperative !
284
285 // initialize mask stream
286 std::unique_ptr<ImageStream> imgStrMask(
287 new ImageStream(maskStr,
288 maskWidth,
289 maskColorMap->getNumPixelComps(),
290 maskColorMap->getBits()));
291
292 imgStrMask->reset();
293 for( int y = 0; y < maskHeight; ++y )
294 {
295 pm = imgStrMask->getLine();
296 for( int x = 0; x < maskWidth; ++x )
297 {
298 maskColorMap->getGray(pm,&alpha);
299 pm += maskColorMap->getNumPixelComps();
300 int nIndex = (y*height/maskHeight) * (width*4+1) + // mapped line
301 (x*width/maskWidth)*4 + 1 + 3 // mapped column
302 ;
303 aScanlines[ nIndex ] = colToByte(alpha);
304 }
305 }
306
307 imgStr.reset();
308 imgStrMask.reset();
309
310 // begind IDAT chunk for scanline data
311 size_t nIdx = startChunk( "IDAT", o_rOutputBuf );
312 // compress scanlines
313 deflateBuffer( aScanlines.data(), aScanlines.size(), o_rOutputBuf );
314 // end IDAT chunk
315 endChunk( nIdx, o_rOutputBuf );
316 // output IEND
317 appendIEND( o_rOutputBuf );
318}
319
320// one bit mask; 0 bits opaque
322 Stream* str,
323 int width, int height, GfxImageColorMap* colorMap,
324 Stream* maskStr,
325 int maskWidth, int maskHeight,
326 bool maskInvert
327 )
328{
329 appendFileHeader( o_rOutputBuf );
330 appendIHDR( o_rOutputBuf, width, height, 8, 6 ); // RGBA image
331
332 // initialize stream
333 unsigned char *p;
334 GfxRGB rgb;
335 std::unique_ptr<ImageStream> imgStr(
336 new ImageStream(str,
337 width,
338 colorMap->getNumPixelComps(),
339 colorMap->getBits()));
340 imgStr->reset();
341
342 // create scan line data buffer
343 OutputBuffer aScanlines;
344 aScanlines.reserve( width*height*4 + height );
345
346 for( int y=0; y<height; ++y)
347 {
348 aScanlines.push_back( 0 );
349 p = imgStr->getLine();
350 for( int x=0; x<width; ++x)
351 {
352 colorMap->getRGB(p, &rgb);
353 aScanlines.push_back(colToByte(rgb.r));
354 aScanlines.push_back(colToByte(rgb.g));
355 aScanlines.push_back(colToByte(rgb.b));
356 aScanlines.push_back( 0xff );
357
358 p +=colorMap->getNumPixelComps();
359 }
360 }
361
362
363 // now fill in the mask data
364
365 // CAUTION: originally this was done in one single loop
366 // it caused merry chaos; the reason is that maskStr and str are
367 // not independent streams, it happens that reading one advances
368 // the other, too. Hence the two passes are imperative !
369
370 // initialize mask stream
371 std::unique_ptr<ImageStream> imgStrMask(
372 new ImageStream(maskStr, maskWidth, 1, 1));
373
374 imgStrMask->reset();
375 for( int y = 0; y < maskHeight; ++y )
376 {
377 for( int x = 0; x < maskWidth; ++x )
378 {
379 unsigned char aPixel = 0;
380 imgStrMask->getPixel( &aPixel );
381 int nIndex = (y*height/maskHeight) * (width*4+1) + // mapped line
382 (x*width/maskWidth)*4 + 1 + 3 // mapped column
383 ;
384 if( maskInvert )
385 aScanlines[ nIndex ] = aPixel ? 0xff : 0x00;
386 else
387 aScanlines[ nIndex ] = aPixel ? 0x00 : 0xff;
388 }
389 }
390
391 imgStr.reset();
392 imgStrMask.reset();
393
394 // begind IDAT chunk for scanline data
395 size_t nIdx = startChunk( "IDAT", o_rOutputBuf );
396 // compress scanlines
397 deflateBuffer( aScanlines.data(), aScanlines.size(), o_rOutputBuf );
398 // end IDAT chunk
399 endChunk( nIdx, o_rOutputBuf );
400 // output IEND
401 appendIEND( o_rOutputBuf );
402}
403
404/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
static void appendFileHeader(OutputBuffer &o_rOutputBuf)
Definition: pnghelper.cxx:118
static void append(sal_uInt32 i_nValue, OutputBuffer &o_rOutputBuf)
Definition: pnghelper.hxx:40
static size_t startChunk(const char *pChunkName, OutputBuffer &o_rOut)
Definition: pnghelper.cxx:125
static void initCRCTable()
Definition: pnghelper.cxx:36
static void createPng(OutputBuffer &o_rOutputBuf, Stream *str, int width, int height, GfxRGB const &zeroColor, GfxRGB const &oneColor, bool bIsMask)
Definition: pnghelper.cxx:178
static bool bCRCTableInit
Definition: pnghelper.hxx:32
static void endChunk(size_t nStart, OutputBuffer &o_rOut)
Definition: pnghelper.cxx:144
static void set(sal_uInt32 i_nValue, OutputBuffer &o_rOutputBuf, size_t i_nIndex)
Definition: pnghelper.cxx:136
static sal_uInt32 getCRC(const sal_uInt8 *i_pBuf, size_t i_nLen)
Definition: pnghelper.cxx:69
static void appendIHDR(OutputBuffer &o_rOutputBuf, int width, int height, int depth, int colortype)
Definition: pnghelper.cxx:159
static sal_uInt32 deflateBuffer(const Output_t *i_pBuf, size_t i_nLen, OutputBuffer &o_rOut)
Definition: pnghelper.cxx:76
static void updateCRC(sal_uInt32 &io_rCRC, const sal_uInt8 *i_pBuf, size_t i_nLen)
Definition: pnghelper.cxx:58
static void appendIEND(OutputBuffer &o_rOutputBuf)
Definition: pnghelper.cxx:172
static sal_uInt32 crc_table[256]
Definition: pnghelper.hxx:31
float y
float x
sal_Int32 nIndex
void * p
sal_Int64 n
#define SAL_N_ELEMENTS(arr)
constexpr double alpha[nDetails]
std::vector< Output_t > OutputBuffer
unsigned char Output_t
unsigned char sal_uInt8