Line data Source code
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 <vcl/pngread.hxx>
21 :
22 : #include <cmath>
23 : #include <rtl/crc.h>
24 : #include <rtl/alloc.h>
25 : #include <tools/zcodec.hxx>
26 : #include <tools/stream.hxx>
27 : #include <vcl/bmpacc.hxx>
28 : #include <vcl/svapp.hxx>
29 : #include <vcl/alpha.hxx>
30 : #include <osl/endian.h>
31 :
32 : #define PNGCHUNK_IHDR 0x49484452
33 : #define PNGCHUNK_PLTE 0x504c5445
34 : #define PNGCHUNK_IDAT 0x49444154
35 : #define PNGCHUNK_IEND 0x49454e44
36 : #define PNGCHUNK_bKGD 0x624b4744
37 : #define PNGCHUNK_gAMA 0x67414d41
38 : #define PNGCHUNK_pHYs 0x70485973
39 : #define PNGCHUNK_tRNS 0x74524e53
40 :
41 : #define VIEWING_GAMMA 2.35
42 : #define DISPLAY_GAMMA 1.0
43 :
44 : namespace vcl
45 : {
46 :
47 : static const sal_uInt8 mpDefaultColorTable[ 256 ] =
48 : { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
49 : 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
50 : 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
51 : 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
52 : 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
53 : 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
54 : 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
55 : 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
56 : 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
57 : 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
58 : 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
59 : 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
60 : 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
61 : 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
62 : 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
63 : 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
64 : };
65 :
66 : class PNGReaderImpl
67 : {
68 : private:
69 : SvStream& mrPNGStream;
70 : sal_uInt16 mnOrigStreamMode;
71 :
72 : std::vector< vcl::PNGReader::ChunkData > maChunkSeq;
73 : std::vector< vcl::PNGReader::ChunkData >::iterator maChunkIter;
74 : std::vector< sal_uInt8 >::iterator maDataIter;
75 :
76 : Bitmap* mpBmp;
77 : BitmapWriteAccess* mpAcc;
78 : Bitmap* mpMaskBmp;
79 : AlphaMask* mpAlphaMask;
80 : BitmapWriteAccess* mpMaskAcc;
81 : ZCodec* mpZCodec;
82 : sal_uInt8* mpInflateInBuf; // as big as the size of a scanline + alphachannel + 1
83 : sal_uInt8* mpScanPrior; // pointer to the latest scanline
84 : sal_uInt8* mpTransTab; // for transparency in images with palette colortype
85 : sal_uInt8* mpScanCurrent; // pointer into the current scanline
86 : sal_uInt8* mpColorTable;
87 : sal_Size mnStreamSize; // estimate of PNG file size
88 : sal_uInt32 mnChunkType; // Type of current PNG chunk
89 : sal_Int32 mnChunkLen; // Length of current PNG chunk
90 : Size maOrigSize; // pixel size of the full image
91 : Size maTargetSize; // pixel size of the result image
92 : Size maPhysSize; // preferred size in MAP_100TH_MM units
93 : sal_uInt32 mnBPP; // number of bytes per pixel
94 : sal_uInt32 mnScansize; // max size of scanline
95 : sal_uInt32 mnYpos; // latest y position in full image
96 : int mnPass; // if interlaced the latest pass ( 1..7 ) else 7
97 : sal_uInt32 mnXStart; // the starting X for the current pass
98 : sal_uInt32 mnXAdd; // the increment for input images X coords for the current pass
99 : sal_uInt32 mnYAdd; // the increment for input images Y coords for the current pass
100 : int mnPreviewShift; // shift to convert orig image coords into preview image coords
101 : int mnPreviewMask; // == ((1 << mnPreviewShift) - 1)
102 : sal_uInt16 mnTargetDepth; // pixel depth of target bitmap
103 : sal_uInt8 mnTransRed;
104 : sal_uInt8 mnTransGreen;
105 : sal_uInt8 mnTransBlue;
106 : sal_uInt8 mnPngDepth; // pixel depth of PNG data
107 : sal_uInt8 mnColorType;
108 : sal_uInt8 mnCompressionType;
109 : sal_uInt8 mnFilterType;
110 : sal_uInt8 mnInterlaceType;
111 : BitmapColor mcTranspColor; // transparency mask's transparency "color"
112 : BitmapColor mcOpaqueColor; // transparency mask's opaque "color"
113 : bool mbTransparent; // graphic includes an tRNS Chunk or an alpha Channel
114 : bool mbAlphaChannel; // is true for ColorType 4 and 6
115 : bool mbRGBTriple;
116 : bool mbPalette; // false if we need a Palette
117 : bool mbGrayScale;
118 : bool mbzCodecInUse;
119 : bool mbStatus;
120 : bool mbIDAT; // true if finished with enough IDAT chunks
121 : bool mbGamma; // true if Gamma Correction available
122 : bool mbpHYs; // true if pysical size of pixel available
123 : bool mbIgnoreGammaChunk;
124 :
125 : #if OSL_DEBUG_LEVEL > 0
126 : // do some checks in debug mode
127 : sal_Int32 mnAllocSizeScanline;
128 : sal_Int32 mnAllocSizeScanlineAlpha;
129 : #endif
130 : // the temporary Scanline (and alpha) for direct scanline copy to Bitmap
131 : sal_uInt8* mpScanline;
132 : sal_uInt8* mpScanlineAlpha;
133 :
134 : bool ReadNextChunk();
135 : void ReadRemainingChunks();
136 :
137 : void ImplSetPixel( sal_uInt32 y, sal_uInt32 x, const BitmapColor & );
138 : void ImplSetPixel( sal_uInt32 y, sal_uInt32 x, sal_uInt8 nPalIndex );
139 : void ImplSetTranspPixel( sal_uInt32 y, sal_uInt32 x, const BitmapColor &, bool bTrans );
140 : void ImplSetAlphaPixel( sal_uInt32 y, sal_uInt32 x, sal_uInt8 nPalIndex, sal_uInt8 nAlpha );
141 : void ImplSetAlphaPixel( sal_uInt32 y, sal_uInt32 x, const BitmapColor&, sal_uInt8 nAlpha );
142 : void ImplReadIDAT();
143 : bool ImplPreparePass();
144 : void ImplApplyFilter();
145 : void ImplDrawScanline( sal_uInt32 nXStart, sal_uInt32 nXAdd );
146 : bool ImplReadTransparent();
147 : void ImplGetGamma();
148 : void ImplGetBackground();
149 : sal_uInt8 ImplScaleColor();
150 : bool ImplReadHeader( const Size& rPreviewSizeHint );
151 : bool ImplReadPalette();
152 : void ImplGetGrayPalette( sal_uInt16 );
153 : sal_uInt32 ImplReadsal_uInt32();
154 :
155 : public:
156 :
157 : PNGReaderImpl( SvStream& );
158 : ~PNGReaderImpl();
159 :
160 : BitmapEx GetBitmapEx( const Size& rPreviewSizeHint );
161 : const std::vector< PNGReader::ChunkData >& GetAllChunks();
162 0 : void SetIgnoreGammaChunk( bool bIgnore ){ mbIgnoreGammaChunk = bIgnore; };
163 : };
164 :
165 0 : PNGReaderImpl::PNGReaderImpl( SvStream& rPNGStream )
166 : : mrPNGStream( rPNGStream ),
167 : mpBmp ( NULL ),
168 : mpAcc ( NULL ),
169 : mpMaskBmp ( NULL ),
170 : mpAlphaMask ( NULL ),
171 : mpMaskAcc ( NULL ),
172 0 : mpZCodec ( new ZCodec( DEFAULT_IN_BUFSIZE, DEFAULT_OUT_BUFSIZE, MAX_MEM_USAGE ) ),
173 : mpInflateInBuf ( NULL ),
174 : mpScanPrior ( NULL ),
175 : mpTransTab ( NULL ),
176 : mpScanCurrent ( NULL ),
177 : mpColorTable ( (sal_uInt8*) mpDefaultColorTable ),
178 : mnPass ( 0 ),
179 : mbTransparent( false ),
180 : mbAlphaChannel( false ),
181 : mbRGBTriple( false ),
182 : mbPalette( false ),
183 : mbGrayScale( false ),
184 : mbzCodecInUse ( false ),
185 : mbStatus( true ),
186 : mbIDAT( false ),
187 : mbGamma ( false ),
188 : mbpHYs ( false ),
189 : mbIgnoreGammaChunk ( false ),
190 : #if OSL_DEBUG_LEVEL > 0
191 : mnAllocSizeScanline(0),
192 : mnAllocSizeScanlineAlpha(0),
193 : #endif
194 : mpScanline(0),
195 0 : mpScanlineAlpha(0)
196 : {
197 : // prepare the PNG data stream
198 0 : mnOrigStreamMode = mrPNGStream.GetNumberFormatInt();
199 0 : mrPNGStream.SetNumberFormatInt( NUMBERFORMAT_INT_BIGENDIAN );
200 :
201 : // prepare the chunk reader
202 0 : maChunkSeq.reserve( 16 );
203 0 : maChunkIter = maChunkSeq.begin();
204 :
205 : // estimate PNG file size (to allow sanity checks)
206 0 : const sal_Size nStreamPos = mrPNGStream.Tell();
207 0 : mrPNGStream.Seek( STREAM_SEEK_TO_END );
208 0 : mnStreamSize = mrPNGStream.Tell();
209 0 : mrPNGStream.Seek( nStreamPos );
210 :
211 : // check the PNG header magic
212 0 : sal_uInt32 nDummy = 0;
213 0 : mrPNGStream.ReadUInt32( nDummy );
214 0 : mbStatus = (nDummy == 0x89504e47);
215 0 : mrPNGStream.ReadUInt32( nDummy );
216 0 : mbStatus = (nDummy == 0x0d0a1a0a) && mbStatus;
217 :
218 0 : mnPreviewShift = 0;
219 0 : mnPreviewMask = (1 << mnPreviewShift) - 1;
220 0 : }
221 :
222 0 : PNGReaderImpl::~PNGReaderImpl()
223 : {
224 0 : mrPNGStream.SetNumberFormatInt( mnOrigStreamMode );
225 :
226 0 : if ( mbzCodecInUse )
227 0 : mpZCodec->EndCompression();
228 :
229 0 : if( mpColorTable != mpDefaultColorTable )
230 0 : delete[] mpColorTable;
231 :
232 0 : delete mpBmp;
233 0 : delete mpAlphaMask;
234 0 : delete mpMaskBmp;
235 0 : delete[] mpTransTab;
236 0 : delete[] mpInflateInBuf;
237 0 : delete[] mpScanPrior;
238 0 : delete mpZCodec;
239 :
240 0 : delete[] mpScanline;
241 0 : delete[] mpScanlineAlpha;
242 0 : }
243 :
244 0 : bool PNGReaderImpl::ReadNextChunk()
245 : {
246 0 : if( maChunkIter == maChunkSeq.end() )
247 : {
248 : // get the next chunk from the stream
249 :
250 : // unless we are at the end of the PNG stream
251 0 : if( mrPNGStream.IsEof() || (mrPNGStream.GetError() != ERRCODE_NONE) )
252 0 : return false;
253 0 : if( !maChunkSeq.empty() && (maChunkSeq.back().nType == PNGCHUNK_IEND) )
254 0 : return false;
255 :
256 0 : PNGReader::ChunkData aDummyChunk;
257 0 : maChunkIter = maChunkSeq.insert( maChunkSeq.end(), aDummyChunk );
258 0 : PNGReader::ChunkData& rChunkData = *maChunkIter;
259 :
260 : // read the chunk header
261 0 : mrPNGStream.ReadInt32( mnChunkLen ).ReadUInt32( mnChunkType );
262 0 : rChunkData.nType = mnChunkType;
263 :
264 : // fdo#61847 truncate over-long, trailing chunks
265 0 : const sal_Size nStreamPos = mrPNGStream.Tell();
266 0 : if( mnChunkLen < 0 || nStreamPos + mnChunkLen >= mnStreamSize )
267 0 : mnChunkLen = mnStreamSize - nStreamPos;
268 :
269 : // calculate chunktype CRC (swap it back to original byte order)
270 0 : sal_uInt32 nChunkType = mnChunkType;
271 : #if defined(__LITTLEENDIAN) || defined(OSL_LITENDIAN)
272 0 : nChunkType = OSL_SWAPDWORD( nChunkType );
273 : #endif
274 0 : sal_uInt32 nCRC32 = rtl_crc32( 0, &nChunkType, 4 );
275 :
276 : // read the chunk data and check the CRC
277 0 : if( mnChunkLen && !mrPNGStream.IsEof() )
278 : {
279 0 : rChunkData.aData.resize( mnChunkLen );
280 :
281 0 : sal_Int32 nBytesRead = 0;
282 0 : do {
283 0 : sal_uInt8* pPtr = &rChunkData.aData[ nBytesRead ];
284 0 : nBytesRead += mrPNGStream.Read( pPtr, mnChunkLen - nBytesRead );
285 0 : } while ( ( nBytesRead < mnChunkLen ) && ( mrPNGStream.GetError() == ERRCODE_NONE ) );
286 :
287 0 : nCRC32 = rtl_crc32( nCRC32, &rChunkData.aData[ 0 ], mnChunkLen );
288 0 : maDataIter = rChunkData.aData.begin();
289 : }
290 0 : sal_uInt32 nCheck(0);
291 0 : mrPNGStream.ReadUInt32( nCheck );
292 0 : if( nCRC32 != nCheck )
293 0 : return false;
294 : }
295 : else
296 : {
297 : // the next chunk was already read
298 0 : mnChunkType = (*maChunkIter).nType;
299 0 : mnChunkLen = (*maChunkIter).aData.size();
300 0 : maDataIter = (*maChunkIter).aData.begin();
301 : }
302 :
303 0 : ++maChunkIter;
304 0 : if( mnChunkType == PNGCHUNK_IEND )
305 0 : return false;
306 0 : return true;
307 : }
308 :
309 : // read the remaining chunks from mrPNGStream
310 0 : void PNGReaderImpl::ReadRemainingChunks()
311 : {
312 0 : while( ReadNextChunk() ) ;
313 0 : }
314 :
315 0 : const std::vector< vcl::PNGReader::ChunkData >& PNGReaderImpl::GetAllChunks()
316 : {
317 0 : ReadRemainingChunks();
318 0 : return maChunkSeq;
319 : }
320 :
321 0 : BitmapEx PNGReaderImpl::GetBitmapEx( const Size& rPreviewSizeHint )
322 : {
323 : // reset to the first chunk
324 0 : maChunkIter = maChunkSeq.begin();
325 :
326 : // first chunk must be IDHR
327 0 : if( mbStatus && ReadNextChunk() )
328 : {
329 0 : if (mnChunkType == PNGCHUNK_IHDR)
330 0 : mbStatus = ImplReadHeader( rPreviewSizeHint );
331 : else
332 0 : mbStatus = false;
333 : }
334 :
335 : // parse the remaining chunks
336 : bool bRetFromNextChunk;
337 0 : while( mbStatus && !mbIDAT && (bRetFromNextChunk = ReadNextChunk()) )
338 : {
339 0 : switch( mnChunkType )
340 : {
341 : case PNGCHUNK_IHDR :
342 : {
343 0 : mbStatus = false; //IHDR should only appear as the first chunk
344 : }
345 0 : break;
346 :
347 : case PNGCHUNK_gAMA : // the gamma chunk must precede
348 : { // the 'IDAT' and also the 'PLTE'(if available )
349 0 : if ( !mbIgnoreGammaChunk && !mbIDAT )
350 0 : ImplGetGamma();
351 : }
352 0 : break;
353 :
354 : case PNGCHUNK_PLTE :
355 : {
356 0 : if ( !mbPalette )
357 0 : mbStatus = ImplReadPalette();
358 : }
359 0 : break;
360 :
361 : case PNGCHUNK_tRNS :
362 : {
363 0 : if ( !mbIDAT ) // the tRNS chunk must precede the IDAT
364 0 : mbStatus = ImplReadTransparent();
365 : }
366 0 : break;
367 :
368 : case PNGCHUNK_bKGD : // the background chunk must appear
369 : {
370 0 : if ( !mbIDAT && mbPalette ) // before the 'IDAT' and after the
371 0 : ImplGetBackground(); // PLTE(if available ) chunk.
372 : }
373 0 : break;
374 :
375 : case PNGCHUNK_IDAT :
376 : {
377 0 : if ( !mpInflateInBuf ) // taking care that the header has properly been read
378 0 : mbStatus = false;
379 0 : else if ( !mbIDAT ) // the gfx is finished, but there may be left a zlibCRC of about 4Bytes
380 0 : ImplReadIDAT();
381 : }
382 0 : break;
383 :
384 : case PNGCHUNK_pHYs :
385 : {
386 0 : if ( !mbIDAT && mnChunkLen == 9 )
387 : {
388 0 : sal_uInt32 nXPixelPerMeter = ImplReadsal_uInt32();
389 0 : sal_uInt32 nYPixelPerMeter = ImplReadsal_uInt32();
390 :
391 0 : sal_uInt8 nUnitSpecifier = *maDataIter++;
392 0 : if( (nUnitSpecifier == 1) && nXPixelPerMeter && nYPixelPerMeter )
393 : {
394 0 : mbpHYs = true;
395 :
396 : // convert into MAP_100TH_MM
397 0 : maPhysSize.Width() = (sal_Int32)( (100000.0 * maOrigSize.Width()) / nXPixelPerMeter );
398 0 : maPhysSize.Height() = (sal_Int32)( (100000.0 * maOrigSize.Height()) / nYPixelPerMeter );
399 : }
400 : }
401 : }
402 0 : break;
403 :
404 : case PNGCHUNK_IEND:
405 0 : mbStatus = mbIDAT; // there is a problem if the image is not complete yet
406 0 : break;
407 : }
408 : }
409 :
410 : // release write access of the bitmaps
411 0 : if ( mpAcc )
412 0 : mpBmp->ReleaseAccess( mpAcc ), mpAcc = NULL;
413 :
414 0 : if ( mpMaskAcc )
415 : {
416 0 : if ( mpAlphaMask )
417 0 : mpAlphaMask->ReleaseAccess( mpMaskAcc );
418 0 : else if ( mpMaskBmp )
419 0 : mpMaskBmp->ReleaseAccess( mpMaskAcc );
420 :
421 0 : mpMaskAcc = NULL;
422 : }
423 :
424 : // return the resulting BitmapEx
425 0 : BitmapEx aRet;
426 :
427 0 : if( !mbStatus || !mbIDAT )
428 0 : aRet.Clear();
429 : else
430 : {
431 0 : if ( mpAlphaMask )
432 0 : aRet = BitmapEx( *mpBmp, *mpAlphaMask );
433 0 : else if ( mpMaskBmp )
434 0 : aRet = BitmapEx( *mpBmp, *mpMaskBmp );
435 : else
436 0 : aRet = *mpBmp;
437 :
438 0 : if ( mbpHYs && maPhysSize.Width() && maPhysSize.Height() )
439 : {
440 0 : aRet.SetPrefMapMode( MAP_100TH_MM );
441 0 : aRet.SetPrefSize( maPhysSize );
442 : }
443 : }
444 0 : return aRet;
445 : }
446 :
447 0 : bool PNGReaderImpl::ImplReadHeader( const Size& rPreviewSizeHint )
448 : {
449 0 : if( mnChunkLen < 13 )
450 0 : return false;
451 :
452 0 : maOrigSize.Width() = ImplReadsal_uInt32();
453 0 : maOrigSize.Height() = ImplReadsal_uInt32();
454 :
455 0 : if (maOrigSize.Width() <= 0 || maOrigSize.Height() <= 0)
456 0 : return false;
457 :
458 0 : mnPngDepth = *(maDataIter++);
459 0 : mnColorType = *(maDataIter++);
460 :
461 0 : mnCompressionType = *(maDataIter++);
462 0 : if( mnCompressionType != 0 ) // unknown compression type
463 0 : return false;
464 :
465 0 : mnFilterType = *(maDataIter++);
466 0 : if( mnFilterType != 0 ) // unknown filter type
467 0 : return false;
468 :
469 0 : mnInterlaceType = *(maDataIter++);
470 0 : switch ( mnInterlaceType ) // filter type valid ?
471 : {
472 : case 0 : // progressive image
473 0 : mnPass = 7;
474 0 : break;
475 : case 1 : // Adam7-interlaced image
476 0 : mnPass = 0;
477 0 : break;
478 : default:
479 0 : return false;
480 : }
481 :
482 0 : mbPalette = true;
483 0 : mbIDAT = mbAlphaChannel = mbTransparent = false;
484 0 : mbGrayScale = mbRGBTriple = false;
485 0 : mnTargetDepth = mnPngDepth;
486 0 : sal_uInt64 nScansize64 = ( ( static_cast< sal_uInt64 >( maOrigSize.Width() ) * mnPngDepth ) + 7 ) >> 3;
487 :
488 : // valid color types are 0,2,3,4 & 6
489 0 : switch ( mnColorType )
490 : {
491 : case 0 : // each pixel is a grayscale
492 : {
493 0 : switch ( mnPngDepth )
494 : {
495 : case 2 : // 2bit target not available -> use four bits
496 0 : mnTargetDepth = 4; // we have to expand the bitmap
497 0 : mbGrayScale = true;
498 0 : break;
499 : case 16 :
500 0 : mnTargetDepth = 8; // we have to reduce the bitmap
501 : // fall through
502 : case 1 :
503 : case 4 :
504 : case 8 :
505 0 : mbGrayScale = true;
506 0 : break;
507 : default :
508 0 : return false;
509 : }
510 : }
511 0 : break;
512 :
513 : case 2 : // each pixel is an RGB triple
514 : {
515 0 : mbRGBTriple = true;
516 0 : nScansize64 *= 3;
517 0 : switch ( mnPngDepth )
518 : {
519 : case 16 : // we have to reduce the bitmap
520 : case 8 :
521 0 : mnTargetDepth = 24;
522 0 : break;
523 : default :
524 0 : return false;
525 : }
526 : }
527 0 : break;
528 :
529 : case 3 : // each pixel is a palette index
530 : {
531 0 : switch ( mnPngDepth )
532 : {
533 : case 2 :
534 0 : mnTargetDepth = 4; // we have to expand the bitmap
535 : // fall through
536 : case 1 :
537 : case 4 :
538 : case 8 :
539 0 : mbPalette = false;
540 0 : break;
541 : default :
542 0 : return false;
543 : }
544 : }
545 0 : break;
546 :
547 : case 4 : // each pixel is a grayscale sample followed by an alpha sample
548 : {
549 0 : nScansize64 *= 2;
550 0 : mbAlphaChannel = true;
551 0 : switch ( mnPngDepth )
552 : {
553 : case 16 :
554 0 : mnTargetDepth = 8; // we have to reduce the bitmap
555 : case 8 :
556 0 : mbGrayScale = true;
557 0 : break;
558 : default :
559 0 : return false;
560 : }
561 : }
562 0 : break;
563 :
564 : case 6 : // each pixel is an RGB triple followed by an alpha sample
565 : {
566 0 : mbRGBTriple = true;
567 0 : nScansize64 *= 4;
568 0 : mbAlphaChannel = true;
569 0 : switch (mnPngDepth )
570 : {
571 : case 16 : // we have to reduce the bitmap
572 : case 8 :
573 0 : mnTargetDepth = 24;
574 0 : break;
575 : default :
576 0 : return false;
577 : }
578 : }
579 0 : break;
580 :
581 : default :
582 0 : return false;
583 : }
584 :
585 0 : mnBPP = static_cast< sal_uInt32 >( nScansize64 / maOrigSize.Width() );
586 0 : if ( !mnBPP )
587 0 : mnBPP = 1;
588 :
589 0 : nScansize64++; // each scanline includes one filterbyte
590 :
591 0 : if ( nScansize64 > SAL_MAX_UINT32 )
592 0 : return false;
593 :
594 0 : mnScansize = static_cast< sal_uInt32 >( nScansize64 );
595 :
596 : // calculate target size from original size and the preview hint
597 0 : if( rPreviewSizeHint.Width() || rPreviewSizeHint.Height() )
598 : {
599 0 : Size aPreviewSize( rPreviewSizeHint.Width(), rPreviewSizeHint.Height() );
600 0 : maTargetSize = maOrigSize;
601 :
602 0 : if( aPreviewSize.Width() == 0 ) {
603 0 : aPreviewSize.setWidth( ( maOrigSize.Width()*aPreviewSize.Height() )/maOrigSize.Height() );
604 0 : if( aPreviewSize.Width() <= 0 )
605 0 : aPreviewSize.setWidth( 1 );
606 0 : } else if( aPreviewSize.Height() == 0 ) {
607 0 : aPreviewSize.setHeight( ( maOrigSize.Height()*aPreviewSize.Width() )/maOrigSize.Width() );
608 0 : if( aPreviewSize.Height() <= 0 )
609 0 : aPreviewSize.setHeight( 1 );
610 : }
611 :
612 0 : if( aPreviewSize.Width() < maOrigSize.Width() && aPreviewSize.Height() < maOrigSize.Height() ) {
613 : OSL_TRACE("preview size %ldx%ld", aPreviewSize.Width(), aPreviewSize.Height() );
614 :
615 0 : for( int i = 1; i < 5; ++i )
616 : {
617 0 : if( (maTargetSize.Width() >> i) < aPreviewSize.Width() )
618 0 : break;
619 0 : if( (maTargetSize.Height() >> i) < aPreviewSize.Height() )
620 0 : break;
621 0 : mnPreviewShift = i;
622 : }
623 0 : mnPreviewMask = (1 << mnPreviewShift) - 1;
624 : }
625 : }
626 :
627 0 : maTargetSize.Width() = (maOrigSize.Width() + mnPreviewMask) >> mnPreviewShift;
628 0 : maTargetSize.Height() = (maOrigSize.Height() + mnPreviewMask) >> mnPreviewShift;
629 :
630 : //round bits up to nearest multiple of 8 and divide by 8 to get num of bytes per pixel
631 0 : int nBytesPerPixel = ((mnTargetDepth + 7) & ~7)/8;
632 :
633 : //stupidly big, forget about it
634 0 : if (maTargetSize.Width() >= SAL_MAX_INT32 / nBytesPerPixel / maTargetSize.Height())
635 : {
636 : SAL_WARN( "vcl.gdi", "overlarge png dimensions: " <<
637 : maTargetSize.Width() << " x " << maTargetSize.Height() << " depth: " << mnTargetDepth);
638 0 : return false;
639 : }
640 :
641 : // TODO: switch between both scanlines instead of copying
642 0 : mpInflateInBuf = new (std::nothrow) sal_uInt8[ mnScansize ];
643 0 : mpScanCurrent = mpInflateInBuf;
644 0 : mpScanPrior = new (std::nothrow) sal_uInt8[ mnScansize ];
645 :
646 0 : if ( !mpInflateInBuf || !mpScanPrior )
647 0 : return false;
648 :
649 0 : mpBmp = new Bitmap( maTargetSize, mnTargetDepth );
650 0 : mpAcc = mpBmp->AcquireWriteAccess();
651 0 : if( !mpAcc )
652 0 : return false;
653 :
654 0 : if ( mbAlphaChannel )
655 : {
656 0 : mpAlphaMask = new AlphaMask( maTargetSize );
657 0 : mpAlphaMask->Erase( 128 );
658 0 : mpMaskAcc = mpAlphaMask->AcquireWriteAccess();
659 0 : if( !mpMaskAcc )
660 0 : return false;
661 : }
662 :
663 0 : if ( mbGrayScale )
664 0 : ImplGetGrayPalette( mnPngDepth );
665 :
666 0 : ImplPreparePass();
667 :
668 0 : return true;
669 : }
670 :
671 0 : void PNGReaderImpl::ImplGetGrayPalette( sal_uInt16 nBitDepth )
672 : {
673 0 : if( nBitDepth > 8 )
674 0 : nBitDepth = 8;
675 :
676 0 : sal_uInt16 nPaletteEntryCount = 1 << nBitDepth;
677 0 : sal_uInt32 nAdd = nBitDepth ? 256 / (nPaletteEntryCount - 1) : 0;
678 :
679 : // no bitdepth==2 available
680 : // but bitdepth==4 with two unused bits is close enough
681 0 : if( nBitDepth == 2 )
682 0 : nPaletteEntryCount = 16;
683 :
684 0 : mpAcc->SetPaletteEntryCount( nPaletteEntryCount );
685 0 : for ( sal_uInt32 i = 0, nStart = 0; nStart < 256; i++, nStart += nAdd )
686 0 : mpAcc->SetPaletteColor( (sal_uInt16)i, BitmapColor( mpColorTable[ nStart ],
687 0 : mpColorTable[ nStart ], mpColorTable[ nStart ] ) );
688 0 : }
689 :
690 0 : bool PNGReaderImpl::ImplReadPalette()
691 : {
692 0 : sal_uInt16 nCount = static_cast<sal_uInt16>( mnChunkLen / 3 );
693 :
694 0 : if ( ( ( mnChunkLen % 3 ) == 0 ) && ( ( 0 < nCount ) && ( nCount <= 256 ) ) && mpAcc )
695 : {
696 0 : mbPalette = true;
697 0 : mpAcc->SetPaletteEntryCount( (sal_uInt16) nCount );
698 :
699 0 : for ( sal_uInt16 i = 0; i < nCount; i++ )
700 : {
701 0 : sal_uInt8 nRed = mpColorTable[ *maDataIter++ ];
702 0 : sal_uInt8 nGreen = mpColorTable[ *maDataIter++ ];
703 0 : sal_uInt8 nBlue = mpColorTable[ *maDataIter++ ];
704 0 : mpAcc->SetPaletteColor( i, Color( nRed, nGreen, nBlue ) );
705 0 : }
706 : }
707 : else
708 0 : mbStatus = false;
709 :
710 0 : return mbStatus;
711 : }
712 :
713 0 : bool PNGReaderImpl::ImplReadTransparent()
714 : {
715 0 : bool bNeedAlpha = false;
716 :
717 0 : if ( mpTransTab == NULL )
718 : {
719 0 : switch ( mnColorType )
720 : {
721 : case 0 :
722 : {
723 0 : if ( mnChunkLen == 2 )
724 : {
725 0 : mpTransTab = new sal_uInt8[ 256 ];
726 0 : memset( mpTransTab, 0xff, 256);
727 : // color type 0 and 4 is always greyscale,
728 : // so the return value can be used as index
729 0 : sal_uInt8 nIndex = ImplScaleColor();
730 0 : mpTransTab[ nIndex ] = 0;
731 0 : mbTransparent = true;
732 : }
733 : }
734 0 : break;
735 :
736 : case 2 :
737 : {
738 0 : if ( mnChunkLen == 6 )
739 : {
740 0 : mnTransRed = ImplScaleColor();
741 0 : mnTransGreen = ImplScaleColor();
742 0 : mnTransBlue = ImplScaleColor();
743 0 : mbTransparent = true;
744 : }
745 : }
746 0 : break;
747 :
748 : case 3 :
749 : {
750 0 : if ( mnChunkLen <= 256 )
751 : {
752 0 : mbTransparent = true;
753 0 : mpTransTab = new sal_uInt8 [ 256 ];
754 0 : memset( mpTransTab, 0xff, 256 );
755 0 : if (mnChunkLen > 0)
756 : {
757 0 : memcpy( mpTransTab, &(*maDataIter), mnChunkLen );
758 0 : maDataIter += mnChunkLen;
759 : // need alpha transparency if not on/off masking
760 0 : for( int i = 0; i < mnChunkLen; ++i )
761 0 : bNeedAlpha |= (mpTransTab[i]!=0x00) && (mpTransTab[i]!=0xFF);
762 : }
763 : }
764 : }
765 0 : break;
766 : }
767 : }
768 :
769 0 : if( mbTransparent && !mbAlphaChannel && !mpMaskBmp )
770 : {
771 0 : if( bNeedAlpha)
772 : {
773 0 : mpAlphaMask = new AlphaMask( maTargetSize );
774 0 : mpMaskAcc = mpAlphaMask->AcquireWriteAccess();
775 : }
776 : else
777 : {
778 0 : mpMaskBmp = new Bitmap( maTargetSize, 1 );
779 0 : mpMaskAcc = mpMaskBmp->AcquireWriteAccess();
780 : }
781 0 : mbTransparent = (mpMaskAcc != NULL);
782 0 : if( !mbTransparent )
783 0 : return false;
784 0 : mcOpaqueColor = BitmapColor( 0x00 );
785 0 : mcTranspColor = BitmapColor( 0xFF );
786 0 : mpMaskAcc->Erase( 0x00 );
787 : }
788 :
789 0 : return true;
790 : }
791 :
792 0 : void PNGReaderImpl::ImplGetGamma()
793 : {
794 0 : if( mnChunkLen < 4 )
795 0 : return;
796 :
797 0 : sal_uInt32 nGammaValue = ImplReadsal_uInt32();
798 0 : double fGamma = ( ( VIEWING_GAMMA / DISPLAY_GAMMA ) * ( (double)nGammaValue / 100000 ) );
799 0 : double fInvGamma = ( fGamma <= 0.0 || fGamma > 10.0 ) ? 1.0 : ( 1.0 / fGamma );
800 :
801 0 : if ( fInvGamma != 1.0 )
802 : {
803 0 : mbGamma = true;
804 :
805 0 : if ( mpColorTable == mpDefaultColorTable )
806 0 : mpColorTable = new sal_uInt8[ 256 ];
807 :
808 0 : for ( sal_Int32 i = 0; i < 256; i++ )
809 0 : mpColorTable[ i ] = (sal_uInt8)(pow((double)i/255.0, fInvGamma) * 255.0 + 0.5);
810 :
811 0 : if ( mbGrayScale )
812 0 : ImplGetGrayPalette( mnPngDepth );
813 : }
814 : }
815 :
816 0 : void PNGReaderImpl::ImplGetBackground()
817 : {
818 0 : switch ( mnColorType )
819 : {
820 : case 3 :
821 : {
822 0 : if ( mnChunkLen == 1 )
823 : {
824 0 : sal_uInt16 nCol = *maDataIter++;
825 0 : if ( nCol < mpAcc->GetPaletteEntryCount() )
826 : {
827 0 : mpAcc->Erase( mpAcc->GetPaletteColor( (sal_uInt8)nCol ) );
828 0 : break;
829 : }
830 : }
831 : }
832 0 : break;
833 :
834 : case 0 :
835 : case 4 :
836 : {
837 0 : if ( mnChunkLen == 2 )
838 : {
839 : // the color type 0 and 4 is always greyscale,
840 : // so the return value can be used as index
841 0 : sal_uInt8 nIndex = ImplScaleColor();
842 0 : mpAcc->Erase( mpAcc->GetPaletteColor( nIndex ) );
843 : }
844 : }
845 0 : break;
846 :
847 : case 2 :
848 : case 6 :
849 : {
850 0 : if ( mnChunkLen == 6 )
851 : {
852 0 : sal_uInt8 nRed = ImplScaleColor();
853 0 : sal_uInt8 nGreen = ImplScaleColor();
854 0 : sal_uInt8 nBlue = ImplScaleColor();
855 0 : mpAcc->Erase( Color( nRed, nGreen, nBlue ) );
856 : }
857 : }
858 0 : break;
859 : }
860 0 : }
861 :
862 : // for color type 0 and 4 (greyscale) the return value is always index to the color
863 : // 2 and 6 (RGB) the return value is always the 8 bit color component
864 0 : sal_uInt8 PNGReaderImpl::ImplScaleColor()
865 : {
866 0 : sal_uInt32 nMask = ( ( 1 << mnPngDepth ) - 1 );
867 0 : sal_uInt16 nCol = ( *maDataIter++ << 8 );
868 :
869 0 : nCol += *maDataIter++ & (sal_uInt16)nMask;
870 :
871 0 : if ( mnPngDepth > 8 ) // convert 16bit graphics to 8
872 0 : nCol >>= 8;
873 :
874 0 : return (sal_uInt8) nCol;
875 : }
876 :
877 : // ImplReadIDAT reads as much image data as needed
878 :
879 0 : void PNGReaderImpl::ImplReadIDAT()
880 : {
881 0 : if( mnChunkLen > 0 )
882 : {
883 0 : if ( !mbzCodecInUse )
884 : {
885 0 : mbzCodecInUse = true;
886 0 : mpZCodec->BeginCompression( ZCODEC_PNG_DEFAULT );
887 : }
888 0 : mpZCodec->SetBreak( mnChunkLen );
889 0 : SvMemoryStream aIStrm( &(*maDataIter), mnChunkLen, STREAM_READ );
890 :
891 0 : while ( ( mpZCodec->GetBreak() ) )
892 : {
893 : // get bytes needed to fill the current scanline
894 0 : sal_Int32 nToRead = mnScansize - (mpScanCurrent - mpInflateInBuf);
895 0 : sal_Int32 nRead = mpZCodec->ReadAsynchron( aIStrm, mpScanCurrent, nToRead );
896 0 : if ( nRead < 0 )
897 : {
898 0 : mbStatus = false;
899 0 : break;
900 : }
901 0 : if ( nRead < nToRead )
902 : {
903 0 : mpScanCurrent += nRead; // more ZStream data in the next IDAT chunk
904 0 : break;
905 : }
906 : else // this scanline is Finished
907 : {
908 0 : mpScanCurrent = mpInflateInBuf;
909 0 : ImplApplyFilter();
910 :
911 0 : ImplDrawScanline( mnXStart, mnXAdd );
912 0 : mnYpos += mnYAdd;
913 : }
914 :
915 0 : if ( mnYpos >= (sal_uInt32)maOrigSize.Height() )
916 : {
917 0 : if( (mnPass < 7) && mnInterlaceType )
918 0 : if( ImplPreparePass() )
919 0 : continue;
920 0 : mbIDAT = true;
921 0 : break;
922 : }
923 0 : }
924 : }
925 :
926 0 : if( mbIDAT )
927 : {
928 0 : mpZCodec->EndCompression();
929 0 : mbzCodecInUse = false;
930 : }
931 0 : }
932 :
933 0 : bool PNGReaderImpl::ImplPreparePass()
934 : {
935 : struct InterlaceParams{ int mnXStart, mnYStart, mnXAdd, mnYAdd; };
936 : static const InterlaceParams aInterlaceParams[8] =
937 : {
938 : // non-interlaced
939 : { 0, 0, 1, 1 },
940 : // Adam7-interlaced
941 : { 0, 0, 8, 8 }, // pass 1
942 : { 4, 0, 8, 8 }, // pass 2
943 : { 0, 4, 4, 8 }, // pass 3
944 : { 2, 0, 4, 4 }, // pass 4
945 : { 0, 2, 2, 4 }, // pass 5
946 : { 1, 0, 2, 2 }, // pass 6
947 : { 0, 1, 1, 2 } // pass 7
948 : };
949 :
950 0 : const InterlaceParams* pParam = &aInterlaceParams[ 0 ];
951 0 : if( mnInterlaceType )
952 : {
953 0 : while( ++mnPass <= 7 )
954 : {
955 0 : pParam = &aInterlaceParams[ mnPass ];
956 :
957 : // skip this pass if the original image is too small for it
958 0 : if( (pParam->mnXStart < maOrigSize.Width())
959 0 : && (pParam->mnYStart < maOrigSize.Height()) )
960 0 : break;
961 : }
962 0 : if( mnPass > 7 )
963 0 : return false;
964 :
965 : // skip the last passes if possible (for scaled down target images)
966 0 : if( mnPreviewMask & (pParam->mnXStart | pParam->mnYStart) )
967 0 : return false;
968 : }
969 :
970 0 : mnYpos = pParam->mnYStart;
971 0 : mnXStart = pParam->mnXStart;
972 0 : mnXAdd = pParam->mnXAdd;
973 0 : mnYAdd = pParam->mnYAdd;
974 :
975 : // in Interlace mode the size of scanline is not constant
976 : // so first we calculate the number of entrys
977 0 : long nScanWidth = (maOrigSize.Width() - mnXStart + mnXAdd - 1) / mnXAdd;
978 0 : mnScansize = nScanWidth;
979 :
980 0 : if( mbRGBTriple )
981 0 : mnScansize = 3 * nScanWidth;
982 :
983 0 : if( mbAlphaChannel )
984 0 : mnScansize += nScanWidth;
985 :
986 : // convert to width in bytes
987 0 : mnScansize = ( mnScansize*mnPngDepth + 7 ) >> 3;
988 :
989 0 : ++mnScansize; // scan size also needs room for the filtertype byte
990 0 : memset( mpScanPrior, 0, mnScansize );
991 :
992 0 : return true;
993 : }
994 :
995 : // ImplApplyFilter writes the complete Scanline (nY)
996 : // in interlace mode the parameter nXStart and nXAdd are non-zero
997 :
998 0 : void PNGReaderImpl::ImplApplyFilter()
999 : {
1000 : OSL_ASSERT( mnScansize >= mnBPP + 1 );
1001 0 : const sal_uInt8* const pScanEnd = mpInflateInBuf + mnScansize;
1002 :
1003 0 : sal_uInt8 nFilterType = *mpInflateInBuf; // the filter type may change each scanline
1004 0 : switch ( nFilterType )
1005 : {
1006 : default: // unknown Scanline Filter Type
1007 : case 0: // Filter Type "None"
1008 : // we let the pixels pass and display the data unfiltered
1009 0 : break;
1010 :
1011 : case 1: // Scanline Filter Type "Sub"
1012 : {
1013 0 : sal_uInt8* p1 = mpInflateInBuf + 1;
1014 0 : const sal_uInt8* p2 = p1;
1015 0 : p1 += mnBPP;
1016 :
1017 : // use left pixels
1018 0 : do
1019 0 : *p1 = static_cast<sal_uInt8>( *p1 + *(p2++) );
1020 : while( ++p1 < pScanEnd );
1021 : }
1022 0 : break;
1023 :
1024 : case 2: // Scanline Filter Type "Up"
1025 : {
1026 0 : sal_uInt8* p1 = mpInflateInBuf + 1;
1027 0 : const sal_uInt8* p2 = mpScanPrior + 1;
1028 :
1029 : // use pixels from prior line
1030 0 : while( p1 < pScanEnd )
1031 : {
1032 0 : *p1 = static_cast<sal_uInt8>( *p1 + *(p2++) );
1033 0 : ++p1;
1034 : }
1035 : }
1036 0 : break;
1037 :
1038 : case 3: // Scanline Filter Type "Average"
1039 : {
1040 0 : sal_uInt8* p1 = mpInflateInBuf + 1;
1041 0 : const sal_uInt8* p2 = mpScanPrior + 1;
1042 0 : const sal_uInt8* p3 = p1;
1043 :
1044 : // use one pixel from prior line
1045 0 : for( int n = mnBPP; --n >= 0; ++p1, ++p2)
1046 0 : *p1 = static_cast<sal_uInt8>( *p1 + (*p2 >> 1) );
1047 :
1048 : // predict by averaging the left and prior line pixels
1049 0 : while( p1 < pScanEnd )
1050 : {
1051 0 : *p1 = static_cast<sal_uInt8>( *p1 + ((*(p2++) + *(p3++)) >> 1) );
1052 0 : ++p1;
1053 : }
1054 : }
1055 0 : break;
1056 :
1057 : case 4: // Scanline Filter Type "PaethPredictor"
1058 : {
1059 0 : sal_uInt8* p1 = mpInflateInBuf + 1;
1060 0 : const sal_uInt8* p2 = mpScanPrior + 1;
1061 0 : const sal_uInt8* p3 = p1;
1062 0 : const sal_uInt8* p4 = p2;
1063 :
1064 : // use one pixel from prior line
1065 0 : for( int n = mnBPP; --n >= 0; ++p1)
1066 0 : *p1 = static_cast<sal_uInt8>( *p1 + *(p2++) );
1067 :
1068 : // predict by using the left and the prior line pixels
1069 0 : while( p1 < pScanEnd )
1070 : {
1071 0 : int na = *(p2++);
1072 0 : int nb = *(p3++);
1073 0 : int nc = *(p4++);
1074 :
1075 0 : int npa = nb - (int)nc;
1076 0 : int npb = na - (int)nc;
1077 0 : int npc = npa + npb;
1078 :
1079 0 : if( npa < 0 )
1080 0 : npa =-npa;
1081 0 : if( npb < 0 )
1082 0 : npb =-npb;
1083 0 : if( npc < 0 )
1084 0 : npc =-npc;
1085 :
1086 0 : if( npa > npb )
1087 0 : na = nb, npa = npb;
1088 0 : if( npa > npc )
1089 0 : na = nc;
1090 :
1091 0 : *p1 = static_cast<sal_uInt8>( *p1 + na );
1092 0 : ++p1;
1093 : }
1094 : }
1095 0 : break;
1096 : }
1097 :
1098 0 : memcpy( mpScanPrior, mpInflateInBuf, mnScansize );
1099 0 : }
1100 :
1101 : // ImplDrawScanlines draws the complete Scanline (nY) into the target bitmap
1102 : // In interlace mode the parameter nXStart and nXAdd append to the currently used pass
1103 :
1104 0 : void PNGReaderImpl::ImplDrawScanline( sal_uInt32 nXStart, sal_uInt32 nXAdd )
1105 : {
1106 : // optimization for downscaling
1107 0 : if( mnYpos & mnPreviewMask )
1108 0 : return;
1109 0 : if( nXStart & mnPreviewMask )
1110 0 : return;
1111 :
1112 : // convert nY to pixel units in the target image
1113 : // => TODO; also do this for nX here instead of in the ImplSet*Pixel() methods
1114 0 : const sal_uInt32 nY = mnYpos >> mnPreviewShift;
1115 :
1116 0 : const sal_uInt8* pTmp = mpInflateInBuf + 1;
1117 0 : if ( mpAcc->HasPalette() ) // alphachannel is not allowed by pictures including palette entries
1118 : {
1119 0 : switch ( mpAcc->GetBitCount() )
1120 : {
1121 : case 1 :
1122 : {
1123 0 : if ( mbTransparent )
1124 : {
1125 0 : for ( sal_Int32 nX = nXStart, nShift = 0; nX < maOrigSize.Width(); nX += nXAdd )
1126 : {
1127 : sal_uInt8 nCol;
1128 0 : nShift = (nShift - 1) & 7;
1129 0 : if ( nShift == 0 )
1130 0 : nCol = *(pTmp++);
1131 : else
1132 0 : nCol = static_cast<sal_uInt8>( *pTmp >> nShift );
1133 0 : nCol &= 1;
1134 :
1135 0 : ImplSetAlphaPixel( nY, nX, nCol, mpTransTab[ nCol ] );
1136 : }
1137 : }
1138 : else
1139 : { // BMP_FORMAT_1BIT_MSB_PAL
1140 0 : for ( sal_Int32 nX = nXStart, nShift = 0; nX < maOrigSize.Width(); nX += nXAdd )
1141 : {
1142 0 : nShift = (nShift - 1) & 7;
1143 :
1144 : sal_uInt8 nCol;
1145 0 : if ( nShift == 0 )
1146 0 : nCol = *(pTmp++);
1147 : else
1148 0 : nCol = static_cast<sal_uInt8>( *pTmp >> nShift );
1149 0 : nCol &= 1;
1150 :
1151 0 : ImplSetPixel( nY, nX, nCol );
1152 : }
1153 : }
1154 : }
1155 0 : break;
1156 :
1157 : case 4 :
1158 : {
1159 0 : if ( mbTransparent )
1160 : {
1161 0 : if ( mnPngDepth == 4 ) // check if source has a two bit pixel format
1162 : {
1163 0 : for ( sal_Int32 nX = nXStart, nXIndex = 0; nX < maOrigSize.Width(); nX += nXAdd, ++nXIndex )
1164 : {
1165 0 : if( nXIndex & 1 )
1166 : {
1167 0 : ImplSetAlphaPixel( nY, nX, *pTmp & 0x0f, mpTransTab[ *pTmp & 0x0f ] );
1168 0 : pTmp++;
1169 : }
1170 : else
1171 : {
1172 0 : ImplSetAlphaPixel( nY, nX, ( *pTmp >> 4 ) & 0x0f, mpTransTab[ *pTmp >> 4 ] );
1173 : }
1174 : }
1175 : }
1176 : else // if ( mnPngDepth == 2 )
1177 : {
1178 0 : for ( sal_Int32 nX = nXStart, nXIndex = 0; nX < maOrigSize.Width(); nX += nXAdd, nXIndex++ )
1179 : {
1180 : sal_uInt8 nCol;
1181 0 : switch( nXIndex & 3 )
1182 : {
1183 : case 0 :
1184 0 : nCol = *pTmp >> 6;
1185 0 : break;
1186 :
1187 : case 1 :
1188 0 : nCol = ( *pTmp >> 4 ) & 0x03 ;
1189 0 : break;
1190 :
1191 : case 2 :
1192 0 : nCol = ( *pTmp >> 2 ) & 0x03;
1193 0 : break;
1194 :
1195 : case 3 :
1196 0 : nCol = ( *pTmp++ ) & 0x03;
1197 0 : break;
1198 :
1199 : default: // get rid of nCol uninitialized warning
1200 0 : nCol = 0;
1201 0 : break;
1202 : }
1203 :
1204 0 : ImplSetAlphaPixel( nY, nX, nCol, mpTransTab[ nCol ] );
1205 : }
1206 : }
1207 : }
1208 : else
1209 : {
1210 0 : if ( mnPngDepth == 4 ) // maybe the source is a two bitmap graphic
1211 : { // BMP_FORMAT_4BIT_LSN_PAL
1212 0 : for ( sal_Int32 nX = nXStart, nXIndex = 0; nX < maOrigSize.Width(); nX += nXAdd, nXIndex++ )
1213 : {
1214 0 : if( nXIndex & 1 )
1215 0 : ImplSetPixel( nY, nX, *pTmp++ & 0x0f );
1216 : else
1217 0 : ImplSetPixel( nY, nX, ( *pTmp >> 4 ) & 0x0f );
1218 : }
1219 : }
1220 : else // if ( mnPngDepth == 2 )
1221 : {
1222 0 : for ( sal_Int32 nX = nXStart, nXIndex = 0; nX < maOrigSize.Width(); nX += nXAdd, nXIndex++ )
1223 : {
1224 0 : switch( nXIndex & 3 )
1225 : {
1226 : case 0 :
1227 0 : ImplSetPixel( nY, nX, *pTmp >> 6 );
1228 0 : break;
1229 :
1230 : case 1 :
1231 0 : ImplSetPixel( nY, nX, ( *pTmp >> 4 ) & 0x03 );
1232 0 : break;
1233 :
1234 : case 2 :
1235 0 : ImplSetPixel( nY, nX, ( *pTmp >> 2 ) & 0x03 );
1236 0 : break;
1237 :
1238 : case 3 :
1239 0 : ImplSetPixel( nY, nX, *pTmp++ & 0x03 );
1240 0 : break;
1241 : }
1242 : }
1243 : }
1244 : }
1245 : }
1246 0 : break;
1247 :
1248 : case 8 :
1249 : {
1250 0 : if ( mbAlphaChannel )
1251 : {
1252 0 : if ( mnPngDepth == 8 ) // maybe the source is a 16 bit grayscale
1253 : {
1254 0 : for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 2 )
1255 0 : ImplSetAlphaPixel( nY, nX, pTmp[ 0 ], pTmp[ 1 ] );
1256 : }
1257 : else
1258 : {
1259 0 : for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 4 )
1260 0 : ImplSetAlphaPixel( nY, nX, pTmp[ 0 ], pTmp[ 2 ] );
1261 : }
1262 : }
1263 0 : else if ( mbTransparent )
1264 : {
1265 0 : if ( mnPngDepth == 8 ) // maybe the source is a 16 bit grayscale
1266 : {
1267 0 : for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp++ )
1268 0 : ImplSetAlphaPixel( nY, nX, *pTmp, mpTransTab[ *pTmp ] );
1269 : }
1270 : else
1271 : {
1272 0 : for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 2 )
1273 0 : ImplSetAlphaPixel( nY, nX, *pTmp, mpTransTab[ *pTmp ] );
1274 : }
1275 : }
1276 : else // neither alpha nor transparency
1277 : {
1278 0 : if ( mnPngDepth == 8 ) // maybe the source is a 16 bit grayscale
1279 : {
1280 0 : if( nXAdd == 1 && mnPreviewShift == 0 ) // copy raw line data if possible
1281 : {
1282 0 : int nLineBytes = maOrigSize.Width();
1283 0 : mpAcc->CopyScanline( nY, pTmp, BMP_FORMAT_8BIT_PAL, nLineBytes );
1284 0 : pTmp += nLineBytes;
1285 : }
1286 : else
1287 : {
1288 0 : for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd )
1289 0 : ImplSetPixel( nY, nX, *pTmp++ );
1290 : }
1291 : }
1292 : else
1293 : {
1294 0 : for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 2 )
1295 0 : ImplSetPixel( nY, nX, *pTmp );
1296 : }
1297 : }
1298 : }
1299 0 : break;
1300 :
1301 : default :
1302 0 : mbStatus = false;
1303 0 : break;
1304 : }
1305 : }
1306 : else // no palette => truecolor
1307 : {
1308 : // #i122985# Added fast-lane implementations using CopyScanline with direct supported mem formats
1309 : static bool bCkeckDirectScanline(true);
1310 :
1311 0 : if( mbAlphaChannel )
1312 : {
1313 : // has RGB + alpha
1314 0 : if ( mnPngDepth == 8 ) // maybe the source has 16 bit per sample
1315 : {
1316 : // BMP_FORMAT_32BIT_TC_RGBA
1317 : // only use DirectScanline when we have no preview shifting stuff and accesses to content and alpha
1318 : const bool bDoDirectScanline(
1319 0 : bCkeckDirectScanline && !nXStart && 1 == nXAdd && !mnPreviewShift && mpAcc && mpMaskAcc);
1320 0 : const bool bCustomColorTable(mpColorTable != mpDefaultColorTable);
1321 :
1322 0 : if(bDoDirectScanline)
1323 : {
1324 : // allocate scanlines on demand, reused for next line
1325 0 : if(!mpScanline)
1326 : {
1327 : #if OSL_DEBUG_LEVEL > 0
1328 : mnAllocSizeScanline = maOrigSize.Width() * 3;
1329 : #endif
1330 0 : mpScanline = new sal_uInt8[maOrigSize.Width() * 3];
1331 : }
1332 :
1333 0 : if(!mpScanlineAlpha)
1334 : {
1335 : #if OSL_DEBUG_LEVEL > 0
1336 : mnAllocSizeScanlineAlpha = maOrigSize.Width();
1337 : #endif
1338 0 : mpScanlineAlpha = new sal_uInt8[maOrigSize.Width()];
1339 : }
1340 : }
1341 :
1342 0 : if(bDoDirectScanline)
1343 : {
1344 : OSL_ENSURE(mpScanline, "No Scanline allocated (!)");
1345 : OSL_ENSURE(mpScanlineAlpha, "No ScanlineAlpha allocated (!)");
1346 : OSL_ENSURE(mnAllocSizeScanline >= maOrigSize.Width() * 3, "Allocated Scanline too small (!)");
1347 : OSL_ENSURE(mnAllocSizeScanlineAlpha >= maOrigSize.Width(), "Allocated ScanlineAlpha too small (!)");
1348 0 : sal_uInt8* pScanline(mpScanline);
1349 0 : sal_uInt8* pScanlineAlpha(mpScanlineAlpha);
1350 :
1351 0 : for (sal_Int32 nX(0); nX < maOrigSize.Width(); nX++, pTmp += 4)
1352 : {
1353 : // prepare content line as BGR by reordering when copying
1354 : // do not forget to invert alpha (source is alpha, target is opacity)
1355 0 : if(bCustomColorTable)
1356 : {
1357 0 : *pScanline++ = mpColorTable[pTmp[2]];
1358 0 : *pScanline++ = mpColorTable[pTmp[1]];
1359 0 : *pScanline++ = mpColorTable[pTmp[0]];
1360 0 : *pScanlineAlpha++ = ~pTmp[3];
1361 : }
1362 : else
1363 : {
1364 0 : *pScanline++ = pTmp[2];
1365 0 : *pScanline++ = pTmp[1];
1366 0 : *pScanline++ = pTmp[0];
1367 0 : *pScanlineAlpha++ = ~pTmp[3];
1368 : }
1369 : }
1370 :
1371 : // copy scanlines directly to bitmaps for content and alpha; use the formats which
1372 : // are able to copy directly to BitmapBuffer
1373 0 : mpAcc->CopyScanline(nY, mpScanline, BMP_FORMAT_24BIT_TC_BGR, maOrigSize.Width() * 3);
1374 0 : mpMaskAcc->CopyScanline(nY, mpScanlineAlpha, BMP_FORMAT_8BIT_PAL, maOrigSize.Width());
1375 : }
1376 : else
1377 : {
1378 0 : for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 4 )
1379 : {
1380 0 : if(bCustomColorTable)
1381 : {
1382 : ImplSetAlphaPixel(
1383 : nY,
1384 : nX,
1385 : BitmapColor(
1386 0 : mpColorTable[ pTmp[ 0 ] ],
1387 0 : mpColorTable[ pTmp[ 1 ] ],
1388 0 : mpColorTable[ pTmp[ 2 ] ]),
1389 0 : pTmp[ 3 ]);
1390 : }
1391 : else
1392 : {
1393 : ImplSetAlphaPixel(
1394 : nY,
1395 : nX,
1396 : BitmapColor(
1397 0 : pTmp[0],
1398 0 : pTmp[1],
1399 0 : pTmp[2]),
1400 0 : pTmp[3]);
1401 : }
1402 : }
1403 : }
1404 : }
1405 : else
1406 : {
1407 : // BMP_FORMAT_64BIT_TC_RGBA
1408 0 : for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 8 )
1409 : {
1410 : ImplSetAlphaPixel(
1411 : nY,
1412 : nX,
1413 : BitmapColor(
1414 0 : mpColorTable[ pTmp[ 0 ] ],
1415 0 : mpColorTable[ pTmp[ 2 ] ],
1416 0 : mpColorTable[ pTmp[ 4 ] ]),
1417 0 : pTmp[6]);
1418 : }
1419 : }
1420 : }
1421 0 : else if( mbTransparent ) // has RGB + transparency
1422 : {
1423 : // BMP_FORMAT_24BIT_TC_RGB
1424 : // no support currently for DirectScanline, found no real usages in current PNGs, may be added on demand
1425 0 : if ( mnPngDepth == 8 ) // maybe the source has 16 bit per sample
1426 : {
1427 0 : for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 3 )
1428 : {
1429 0 : sal_uInt8 nRed = pTmp[ 0 ];
1430 0 : sal_uInt8 nGreen = pTmp[ 1 ];
1431 0 : sal_uInt8 nBlue = pTmp[ 2 ];
1432 0 : bool bTransparent = ( ( nRed == mnTransRed )
1433 0 : && ( nGreen == mnTransGreen )
1434 0 : && ( nBlue == mnTransBlue ) );
1435 :
1436 0 : ImplSetTranspPixel( nY, nX, BitmapColor( mpColorTable[ nRed ],
1437 0 : mpColorTable[ nGreen ],
1438 0 : mpColorTable[ nBlue ] ), bTransparent );
1439 : }
1440 : }
1441 : else
1442 : {
1443 : // BMP_FORMAT_48BIT_TC_RGB
1444 0 : for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 6 )
1445 : {
1446 0 : sal_uInt8 nRed = pTmp[ 0 ];
1447 0 : sal_uInt8 nGreen = pTmp[ 2 ];
1448 0 : sal_uInt8 nBlue = pTmp[ 4 ];
1449 0 : bool bTransparent = ( ( nRed == mnTransRed )
1450 0 : && ( nGreen == mnTransGreen )
1451 0 : && ( nBlue == mnTransBlue ) );
1452 :
1453 0 : ImplSetTranspPixel( nY, nX, BitmapColor( mpColorTable[ nRed ],
1454 0 : mpColorTable[ nGreen ],
1455 0 : mpColorTable[ nBlue ] ), bTransparent );
1456 : }
1457 : }
1458 : }
1459 : else // has RGB but neither alpha nor transparency
1460 : {
1461 : // BMP_FORMAT_24BIT_TC_RGB
1462 : // only use DirectScanline when we have no preview shifting stuff and access to content
1463 : const bool bDoDirectScanline(
1464 0 : bCkeckDirectScanline && !nXStart && 1 == nXAdd && !mnPreviewShift);
1465 0 : const bool bCustomColorTable(mpColorTable != mpDefaultColorTable);
1466 :
1467 0 : if(bDoDirectScanline && !mpScanline)
1468 : {
1469 : // allocate scanlines on demand, reused for next line
1470 : #if OSL_DEBUG_LEVEL > 0
1471 : mnAllocSizeScanline = maOrigSize.Width() * 3;
1472 : #endif
1473 0 : mpScanline = new sal_uInt8[maOrigSize.Width() * 3];
1474 : }
1475 :
1476 0 : if ( mnPngDepth == 8 ) // maybe the source has 16 bit per sample
1477 : {
1478 0 : if(bDoDirectScanline)
1479 : {
1480 : OSL_ENSURE(mpScanline, "No Scanline allocated (!)");
1481 : OSL_ENSURE(mnAllocSizeScanline >= maOrigSize.Width() * 3, "Allocated Scanline too small (!)");
1482 0 : sal_uInt8* pScanline(mpScanline);
1483 :
1484 0 : for (sal_Int32 nX(0); nX < maOrigSize.Width(); nX++, pTmp += 3)
1485 : {
1486 : // prepare content line as BGR by reordering when copying
1487 0 : if(bCustomColorTable)
1488 : {
1489 0 : *pScanline++ = mpColorTable[pTmp[2]];
1490 0 : *pScanline++ = mpColorTable[pTmp[1]];
1491 0 : *pScanline++ = mpColorTable[pTmp[0]];
1492 : }
1493 : else
1494 : {
1495 0 : *pScanline++ = pTmp[2];
1496 0 : *pScanline++ = pTmp[1];
1497 0 : *pScanline++ = pTmp[0];
1498 : }
1499 : }
1500 :
1501 : // copy scanline directly to bitmap for content; use the format which is able to
1502 : // copy directly to BitmapBuffer
1503 0 : mpAcc->CopyScanline(nY, mpScanline, BMP_FORMAT_24BIT_TC_BGR, maOrigSize.Width() * 3);
1504 : }
1505 : else
1506 : {
1507 0 : for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 3 )
1508 : {
1509 0 : if(bCustomColorTable)
1510 : {
1511 : ImplSetPixel(
1512 : nY,
1513 : nX,
1514 : BitmapColor(
1515 0 : mpColorTable[ pTmp[ 0 ] ],
1516 0 : mpColorTable[ pTmp[ 1 ] ],
1517 0 : mpColorTable[ pTmp[ 2 ] ]));
1518 : }
1519 : else
1520 : {
1521 : ImplSetPixel(
1522 : nY,
1523 : nX,
1524 : BitmapColor(
1525 0 : pTmp[0],
1526 0 : pTmp[1],
1527 0 : pTmp[2]));
1528 : }
1529 : }
1530 : }
1531 : }
1532 : else
1533 : {
1534 : // BMP_FORMAT_48BIT_TC_RGB
1535 : // no support currently for DirectScanline, found no real usages in current PNGs, may be added on demand
1536 0 : for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 6 )
1537 : {
1538 : ImplSetPixel(
1539 : nY,
1540 : nX,
1541 : BitmapColor(
1542 0 : mpColorTable[ pTmp[ 0 ] ],
1543 0 : mpColorTable[ pTmp[ 2 ] ],
1544 0 : mpColorTable[ pTmp[ 4 ] ]));
1545 : }
1546 : }
1547 : }
1548 : }
1549 : }
1550 :
1551 0 : void PNGReaderImpl::ImplSetPixel( sal_uInt32 nY, sal_uInt32 nX, const BitmapColor& rBitmapColor )
1552 : {
1553 : // TODO: get preview mode checks out of inner loop
1554 0 : if( nX & mnPreviewMask )
1555 0 : return;
1556 0 : nX >>= mnPreviewShift;
1557 :
1558 0 : mpAcc->SetPixel( nY, nX, rBitmapColor );
1559 : }
1560 :
1561 0 : void PNGReaderImpl::ImplSetPixel( sal_uInt32 nY, sal_uInt32 nX, sal_uInt8 nPalIndex )
1562 : {
1563 : // TODO: get preview mode checks out of inner loop
1564 0 : if( nX & mnPreviewMask )
1565 0 : return;
1566 0 : nX >>= mnPreviewShift;
1567 :
1568 0 : mpAcc->SetPixelIndex( nY, nX, nPalIndex );
1569 : }
1570 :
1571 0 : void PNGReaderImpl::ImplSetTranspPixel( sal_uInt32 nY, sal_uInt32 nX, const BitmapColor& rBitmapColor, bool bTrans )
1572 : {
1573 : // TODO: get preview mode checks out of inner loop
1574 0 : if( nX & mnPreviewMask )
1575 0 : return;
1576 0 : nX >>= mnPreviewShift;
1577 :
1578 0 : mpAcc->SetPixel( nY, nX, rBitmapColor );
1579 :
1580 0 : if ( bTrans )
1581 0 : mpMaskAcc->SetPixel( nY, nX, mcTranspColor );
1582 : else
1583 0 : mpMaskAcc->SetPixel( nY, nX, mcOpaqueColor );
1584 : }
1585 :
1586 0 : void PNGReaderImpl::ImplSetAlphaPixel( sal_uInt32 nY, sal_uInt32 nX,
1587 : sal_uInt8 nPalIndex, sal_uInt8 nAlpha )
1588 : {
1589 : // TODO: get preview mode checks out of inner loop
1590 0 : if( nX & mnPreviewMask )
1591 0 : return;
1592 0 : nX >>= mnPreviewShift;
1593 :
1594 0 : mpAcc->SetPixelIndex( nY, nX, nPalIndex );
1595 0 : mpMaskAcc->SetPixelIndex( nY, nX, ~nAlpha );
1596 : }
1597 :
1598 0 : void PNGReaderImpl::ImplSetAlphaPixel( sal_uInt32 nY, sal_uInt32 nX,
1599 : const BitmapColor& rBitmapColor, sal_uInt8 nAlpha )
1600 : {
1601 : // TODO: get preview mode checks out of inner loop
1602 0 : if( nX & mnPreviewMask )
1603 0 : return;
1604 0 : nX >>= mnPreviewShift;
1605 :
1606 0 : mpAcc->SetPixel( nY, nX, rBitmapColor );
1607 0 : mpMaskAcc->SetPixelIndex( nY, nX, ~nAlpha );
1608 : }
1609 :
1610 0 : sal_uInt32 PNGReaderImpl::ImplReadsal_uInt32()
1611 : {
1612 : sal_uInt32 nRet;
1613 0 : nRet = *maDataIter++;
1614 0 : nRet <<= 8;
1615 0 : nRet |= *maDataIter++;
1616 0 : nRet <<= 8;
1617 0 : nRet |= *maDataIter++;
1618 0 : nRet <<= 8;
1619 0 : nRet |= *maDataIter++;
1620 0 : return nRet;
1621 : }
1622 :
1623 0 : PNGReader::PNGReader( SvStream& rIStm ) :
1624 0 : mpImpl( new ::vcl::PNGReaderImpl( rIStm ) )
1625 : {
1626 0 : }
1627 :
1628 0 : PNGReader::~PNGReader()
1629 : {
1630 0 : delete mpImpl;
1631 0 : }
1632 :
1633 0 : BitmapEx PNGReader::Read( const Size& i_rPreviewSizeHint )
1634 : {
1635 0 : return mpImpl->GetBitmapEx( i_rPreviewSizeHint );
1636 : }
1637 :
1638 0 : const std::vector< vcl::PNGReader::ChunkData >& PNGReader::GetChunks() const
1639 : {
1640 0 : return mpImpl->GetAllChunks();
1641 : }
1642 :
1643 0 : void PNGReader::SetIgnoreGammaChunk( bool b )
1644 : {
1645 0 : mpImpl->SetIgnoreGammaChunk( b );
1646 0 : }
1647 :
1648 : } // namespace vcl
1649 :
1650 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|