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