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