| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_vcl.hxx" |
| |
| #include <vcl/pngread.hxx> |
| |
| #include <cmath> |
| #include <rtl/crc.h> |
| #include <rtl/memory.h> |
| #include <rtl/alloc.h> |
| #include <tools/zcodec.hxx> |
| #include <tools/stream.hxx> |
| #include <vcl/bmpacc.hxx> |
| #include <vcl/svapp.hxx> |
| #include <vcl/alpha.hxx> |
| #include <osl/endian.h> |
| |
| // ----------- |
| // - Defines - |
| // ----------- |
| |
| #define PNGCHUNK_IHDR 0x49484452 |
| #define PNGCHUNK_PLTE 0x504c5445 |
| #define PNGCHUNK_IDAT 0x49444154 |
| #define PNGCHUNK_IEND 0x49454e44 |
| #define PNGCHUNK_bKGD 0x624b4744 |
| #define PNGCHUNK_cHRM 0x6348524d |
| #define PNGCHUNK_gAMA 0x67414d41 |
| #define PNGCHUNK_hIST 0x68495354 |
| #define PNGCHUNK_pHYs 0x70485973 |
| #define PNGCHUNK_sBIT 0x73425420 |
| #define PNGCHUNK_tIME 0x74494d45 |
| #define PNGCHUNK_tEXt 0x74455874 |
| #define PNGCHUNK_tRNS 0x74524e53 |
| #define PNGCHUNK_zTXt 0x7a545874 |
| #define PMGCHUNG_msOG 0x6d734f47 // Microsoft Office Animated GIF |
| |
| #define VIEWING_GAMMA 2.35 |
| #define DISPLAY_GAMMA 1.0 |
| |
| namespace vcl |
| { |
| // ----------- |
| // - statics - |
| // ----------- |
| |
| // ------------------------------------------------------------------------------ |
| |
| static const sal_uInt8 mpDefaultColorTable[ 256 ] = |
| { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, |
| 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, |
| 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, |
| 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, |
| 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, |
| 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, |
| 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, |
| 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, |
| 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, |
| 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, |
| 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, |
| 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, |
| 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, |
| 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, |
| 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, |
| 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff |
| }; |
| |
| // ------------- |
| // - PNGReaderImpl - |
| // ------------- |
| |
| class PNGReaderImpl |
| { |
| private: |
| SvStream& mrPNGStream; |
| sal_uInt16 mnOrigStreamMode; |
| |
| std::vector< vcl::PNGReader::ChunkData > maChunkSeq; |
| std::vector< vcl::PNGReader::ChunkData >::iterator maChunkIter; |
| std::vector< sal_uInt8 >::iterator maDataIter; |
| |
| Bitmap* mpBmp; |
| BitmapWriteAccess* mpAcc; |
| Bitmap* mpMaskBmp; |
| AlphaMask* mpAlphaMask; |
| BitmapWriteAccess* mpMaskAcc; |
| ZCodec* mpZCodec; |
| sal_uInt8* mpInflateInBuf; // as big as the size of a scanline + alphachannel + 1 |
| sal_uInt8* mpScanPrior; // pointer to the latest scanline |
| sal_uInt8* mpTransTab; // for transparency in images with palette colortype |
| sal_uInt8* mpScanCurrent; // pointer into the current scanline |
| sal_uInt8* mpColorTable; // |
| sal_Size mnStreamSize; // estimate of PNG file size |
| sal_uInt32 mnChunkType; // Type of current PNG chunk |
| sal_Int32 mnChunkLen; // Length of current PNG chunk |
| Size maOrigSize; // pixel size of the full image |
| Size maTargetSize; // pixel size of the result image |
| Size maPhysSize; // prefered size in MAP_100TH_MM units |
| sal_uInt32 mnBPP; // number of bytes per pixel |
| sal_uInt32 mnScansize; // max size of scanline |
| sal_uInt32 mnYpos; // latest y position in full image |
| int mnPass; // if interlaced the latest pass ( 1..7 ) else 7 |
| sal_uInt32 mnXStart; // the starting X for the current pass |
| sal_uInt32 mnXAdd; // the increment for input images X coords for the current pass |
| sal_uInt32 mnYAdd; // the increment for input images Y coords for the current pass |
| int mnPreviewShift; // shift to convert orig image coords into preview image coords |
| int mnPreviewMask; // == ((1 << mnPreviewShift) - 1) |
| sal_uInt16 mnIStmOldMode; |
| sal_uInt16 mnTargetDepth; // pixel depth of target bitmap |
| sal_uInt8 mnTransRed; |
| sal_uInt8 mnTransGreen; |
| sal_uInt8 mnTransBlue; |
| sal_uInt8 mnPngDepth; // pixel depth of PNG data |
| sal_uInt8 mnColorType; |
| sal_uInt8 mnCompressionType; |
| sal_uInt8 mnFilterType; |
| sal_uInt8 mnInterlaceType; |
| BitmapColor mcTranspColor; // transparency mask's transparency "color" |
| BitmapColor mcOpaqueColor; // transparency mask's opaque "color" |
| sal_Bool mbTransparent; // graphic includes an tRNS Chunk or an alpha Channel |
| sal_Bool mbAlphaChannel; // is true for ColorType 4 and 6 |
| sal_Bool mbRGBTriple; |
| sal_Bool mbPalette; // sal_False if we need a Palette |
| sal_Bool mbGrayScale; |
| sal_Bool mbzCodecInUse; |
| sal_Bool mbStatus; |
| sal_Bool mbIDAT; // sal_True if finished with enough IDAT chunks |
| sal_Bool mbGamma; // sal_True if Gamma Correction available |
| sal_Bool mbpHYs; // sal_True if pysical size of pixel available |
| sal_Bool mbIgnoreGammaChunk; |
| |
| #ifdef DBG_UTIL |
| // do some checks in debug mode |
| sal_uInt32 mnAllocSizeScanline; |
| sal_uInt32 mnAllocSizeScanlineAlpha; |
| #endif |
| // the temporary Scanline (and alpha) for direct scanline copy to Bitmap |
| sal_uInt8* mpScanline; |
| sal_uInt8* mpScanlineAlpha; |
| |
| bool ReadNextChunk(); |
| void ReadRemainingChunks(); |
| void SkipRemainingChunks(); |
| |
| void ImplSetPixel( sal_uInt32 y, sal_uInt32 x, const BitmapColor & ); |
| void ImplSetPixel( sal_uInt32 y, sal_uInt32 x, sal_uInt8 nPalIndex ); |
| void ImplSetTranspPixel( sal_uInt32 y, sal_uInt32 x, const BitmapColor &, sal_Bool bTrans ); |
| void ImplSetAlphaPixel( sal_uInt32 y, sal_uInt32 x, sal_uInt8 nPalIndex, sal_uInt8 nAlpha ); |
| void ImplSetAlphaPixel( sal_uInt32 y, sal_uInt32 x, const BitmapColor&, sal_uInt8 nAlpha ); |
| void ImplReadIDAT(); |
| bool ImplPreparePass(); |
| void ImplApplyFilter(); |
| void ImplDrawScanline( sal_uInt32 nXStart, sal_uInt32 nXAdd ); |
| sal_Bool ImplReadTransparent(); |
| void ImplGetGamma(); |
| void ImplGetBackground(); |
| sal_uInt8 ImplScaleColor(); |
| sal_Bool ImplReadHeader( const Size& rPreviewSizeHint ); |
| sal_Bool ImplReadPalette(); |
| void ImplGetGrayPalette( sal_uInt16 ); |
| sal_uInt32 ImplReadsal_uInt32(); |
| |
| public: |
| |
| PNGReaderImpl( SvStream& ); |
| ~PNGReaderImpl(); |
| |
| BitmapEx GetBitmapEx( const Size& rPreviewSizeHint ); |
| const std::vector< PNGReader::ChunkData >& GetAllChunks(); |
| void SetIgnoreGammaChunk( sal_Bool bIgnore ){ mbIgnoreGammaChunk = bIgnore; }; |
| }; |
| |
| // ------------------------------------------------------------------------------ |
| |
| PNGReaderImpl::PNGReaderImpl( SvStream& rPNGStream ) |
| : mrPNGStream( rPNGStream ), |
| mpBmp ( NULL ), |
| mpAcc ( NULL ), |
| mpMaskBmp ( NULL ), |
| mpAlphaMask ( NULL ), |
| mpMaskAcc ( NULL ), |
| mpZCodec ( new ZCodec( DEFAULT_IN_BUFSIZE, DEFAULT_OUT_BUFSIZE, MAX_MEM_USAGE ) ), |
| mpInflateInBuf ( NULL ), |
| mpScanPrior ( NULL ), |
| mpTransTab ( NULL ), |
| mpColorTable ( (sal_uInt8*) mpDefaultColorTable ), |
| mnColorType( 0xFF ), |
| mbPalette( false ), |
| mbzCodecInUse ( sal_False ), |
| mbStatus( sal_True), |
| mbIDAT( sal_False ), |
| mbGamma ( sal_False ), |
| mbpHYs ( sal_False ), |
| mbIgnoreGammaChunk ( sal_False ), |
| #ifdef DBG_UTIL |
| mnAllocSizeScanline(0), |
| mnAllocSizeScanlineAlpha(0), |
| #endif |
| mpScanline(0), |
| mpScanlineAlpha(0) |
| { |
| // prepare the PNG data stream |
| mnOrigStreamMode = mrPNGStream.GetNumberFormatInt(); |
| mrPNGStream.SetNumberFormatInt( NUMBERFORMAT_INT_BIGENDIAN ); |
| |
| // prepare the chunk reader |
| maChunkSeq.reserve( 16 ); |
| maChunkIter = maChunkSeq.begin(); |
| |
| // estimate PNG file size (to allow sanity checks) |
| const sal_Size nStreamPos = mrPNGStream.Tell(); |
| mrPNGStream.Seek( STREAM_SEEK_TO_END ); |
| mnStreamSize = mrPNGStream.Tell(); |
| mrPNGStream.Seek( nStreamPos ); |
| |
| // check the PNG header magic |
| sal_uInt32 nDummy = 0; |
| mrPNGStream >> nDummy; |
| mbStatus = (nDummy == 0x89504e47); |
| mrPNGStream >> nDummy; |
| mbStatus &= (nDummy == 0x0d0a1a0a); |
| |
| mnPreviewShift = 0; |
| mnPreviewMask = (1 << mnPreviewShift) - 1; |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| PNGReaderImpl::~PNGReaderImpl() |
| { |
| mrPNGStream.SetNumberFormatInt( mnOrigStreamMode ); |
| |
| if ( mbzCodecInUse ) |
| mpZCodec->EndCompression(); |
| |
| if( mpColorTable != mpDefaultColorTable ) |
| delete[] mpColorTable; |
| |
| delete mpBmp; |
| delete mpAlphaMask; |
| delete mpMaskBmp; |
| delete[] mpTransTab; |
| delete[] mpInflateInBuf; |
| delete[] mpScanPrior; |
| delete mpZCodec; |
| |
| delete[] mpScanline; |
| delete[] mpScanlineAlpha; |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| bool PNGReaderImpl::ReadNextChunk() |
| { |
| if( maChunkIter == maChunkSeq.end() ) |
| { |
| // get the next chunk from the stream |
| |
| // unless we are at the end of the PNG stream |
| if( mrPNGStream.IsEof() || (mrPNGStream.GetError() != ERRCODE_NONE) ) |
| return false; |
| if( !maChunkSeq.empty() && (maChunkSeq.back().nType == PNGCHUNK_IEND) ) |
| return false; |
| |
| PNGReader::ChunkData aDummyChunk; |
| maChunkIter = maChunkSeq.insert( maChunkSeq.end(), aDummyChunk ); |
| PNGReader::ChunkData& rChunkData = *maChunkIter; |
| |
| // read the chunk header |
| mrPNGStream >> mnChunkLen >> mnChunkType; |
| rChunkData.nType = mnChunkType; |
| |
| // #128377#/#149343# sanity check for chunk length |
| if( mnChunkLen < 0 ) |
| return false; |
| const sal_Size nStreamPos = mrPNGStream.Tell(); |
| if( nStreamPos + mnChunkLen >= mnStreamSize ) |
| return false; |
| |
| // calculate chunktype CRC (swap it back to original byte order) |
| sal_uInt32 nChunkType = mnChunkType;; |
| #if defined(__LITTLEENDIAN) || defined(OSL_LITENDIAN) |
| nChunkType = SWAPLONG( nChunkType ); |
| #endif |
| sal_uInt32 nCRC32 = rtl_crc32( 0, &nChunkType, 4 ); |
| |
| // read the chunk data and check the CRC |
| if( mnChunkLen && !mrPNGStream.IsEof() ) |
| { |
| rChunkData.aData.resize( mnChunkLen ); |
| |
| sal_Int32 nBytesRead = 0; |
| do { |
| sal_uInt8* pPtr = &rChunkData.aData[ nBytesRead ]; |
| nBytesRead += mrPNGStream.Read( pPtr, mnChunkLen - nBytesRead ); |
| } while ( ( nBytesRead < mnChunkLen ) && ( mrPNGStream.GetError() == ERRCODE_NONE ) ); |
| |
| nCRC32 = rtl_crc32( nCRC32, &rChunkData.aData[ 0 ], mnChunkLen ); |
| maDataIter = rChunkData.aData.begin(); |
| } |
| sal_uInt32 nCheck; |
| mrPNGStream >> nCheck; |
| if( nCRC32 != nCheck ) |
| return false; |
| } |
| else |
| { |
| // the next chunk was already read |
| mnChunkType = (*maChunkIter).nType; |
| mnChunkLen = (*maChunkIter).aData.size(); |
| maDataIter = (*maChunkIter).aData.begin(); |
| } |
| |
| ++maChunkIter; |
| if( mnChunkType == PNGCHUNK_IEND ) |
| return false; |
| return true; |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| // read the remaining chunks from mrPNGStream |
| void PNGReaderImpl::ReadRemainingChunks() |
| { |
| while( ReadNextChunk() ) ; |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| // move position of mrPNGStream to the end of the file |
| void PNGReaderImpl::SkipRemainingChunks() |
| { |
| // nothing to skip if the last chunk was read |
| if( !maChunkSeq.empty() && (maChunkSeq.back().nType == PNGCHUNK_IEND) ) |
| return; |
| |
| // read from the stream until the IEND chunk is found |
| const sal_Size nStreamPos = mrPNGStream.Tell(); |
| while( !mrPNGStream.IsEof() && (mrPNGStream.GetError() == ERRCODE_NONE) ) |
| { |
| mrPNGStream >> mnChunkLen >> mnChunkType; |
| if( mnChunkLen < 0 ) |
| break; |
| if( nStreamPos + mnChunkLen >= mnStreamSize ) |
| break; |
| mrPNGStream.SeekRel( mnChunkLen + 4 ); // skip data + CRC |
| if( mnChunkType == PNGCHUNK_IEND ) |
| break; |
| } |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| const std::vector< vcl::PNGReader::ChunkData >& PNGReaderImpl::GetAllChunks() |
| { |
| ReadRemainingChunks(); |
| return maChunkSeq; |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| BitmapEx PNGReaderImpl::GetBitmapEx( const Size& rPreviewSizeHint ) |
| { |
| // reset to the first chunk |
| maChunkIter = maChunkSeq.begin(); |
| |
| // read the first chunk which must be the IHDR chunk |
| ReadNextChunk(); |
| mbStatus = (mnChunkType == PNGCHUNK_IHDR) && ImplReadHeader( rPreviewSizeHint ); |
| |
| // parse the chunks |
| while( mbStatus && !mbIDAT && ReadNextChunk() ) |
| { |
| switch( mnChunkType ) |
| { |
| case PNGCHUNK_IHDR : |
| { |
| mbStatus = false; // only one IHDR possible |
| } |
| break; |
| |
| case PNGCHUNK_gAMA : // the gamma chunk must precede |
| { // the 'IDAT' and also the 'PLTE'(if available ) |
| if ( !mbIgnoreGammaChunk && ( mbIDAT == sal_False ) ) |
| ImplGetGamma(); |
| } |
| break; |
| |
| case PNGCHUNK_PLTE : |
| { |
| if ( !mbPalette ) |
| mbStatus = ImplReadPalette(); |
| } |
| break; |
| |
| case PNGCHUNK_tRNS : |
| { |
| if ( !mbIDAT ) // the tRNS chunk must precede the IDAT |
| mbStatus = ImplReadTransparent(); |
| } |
| break; |
| |
| case PNGCHUNK_bKGD : // the background chunk must appear |
| { |
| if ( ( mbIDAT == sal_False ) && mbPalette ) // before the 'IDAT' and after the |
| ImplGetBackground(); // PLTE(if available ) chunk. |
| } |
| break; |
| |
| case PNGCHUNK_IDAT : |
| { |
| if ( !mpInflateInBuf ) // taking care that the header has properly been read |
| mbStatus = sal_False; |
| else if ( !mbIDAT ) // the gfx is finished, but there may be left a zlibCRC of about 4Bytes |
| ImplReadIDAT(); |
| } |
| break; |
| |
| case PNGCHUNK_pHYs : |
| { |
| if ( !mbIDAT && mnChunkLen == 9 ) |
| { |
| sal_uInt32 nXPixelPerMeter = ImplReadsal_uInt32(); |
| sal_uInt32 nYPixelPerMeter = ImplReadsal_uInt32(); |
| |
| sal_uInt8 nUnitSpecifier = *maDataIter++; |
| if( (nUnitSpecifier == 1) && nXPixelPerMeter && nXPixelPerMeter ) |
| { |
| mbpHYs = sal_True; |
| |
| // convert into MAP_100TH_MM |
| maPhysSize.Width() = (sal_Int32)( (100000.0 * maOrigSize.Width()) / nXPixelPerMeter ); |
| maPhysSize.Height() = (sal_Int32)( (100000.0 * maOrigSize.Height()) / nYPixelPerMeter ); |
| } |
| } |
| } |
| break; |
| |
| case PNGCHUNK_IEND: |
| mbStatus = mbIDAT; // there is a problem if the image is not complete yet |
| break; |
| } |
| } |
| |
| // release write access of the bitmaps |
| if ( mpAcc ) |
| mpBmp->ReleaseAccess( mpAcc ), mpAcc = NULL; |
| |
| if ( mpMaskAcc ) |
| { |
| if ( mpAlphaMask ) |
| mpAlphaMask->ReleaseAccess( mpMaskAcc ); |
| else if ( mpMaskBmp ) |
| mpMaskBmp->ReleaseAccess( mpMaskAcc ); |
| |
| mpMaskAcc = NULL; |
| } |
| |
| // return the resulting BitmapEx |
| BitmapEx aRet; |
| |
| if( !mbStatus || !mbIDAT ) |
| aRet.Clear(); |
| else |
| { |
| if ( mpAlphaMask ) |
| aRet = BitmapEx( *mpBmp, *mpAlphaMask ); |
| else if ( mpMaskBmp ) |
| aRet = BitmapEx( *mpBmp, *mpMaskBmp ); |
| else |
| aRet = *mpBmp; |
| |
| if ( mbpHYs && maPhysSize.Width() && maPhysSize.Height() ) |
| { |
| aRet.SetPrefMapMode( MAP_100TH_MM ); |
| aRet.SetPrefSize( maPhysSize ); |
| } |
| |
| #if 0 |
| // TODO: make sure nobody depends on the stream being after the IEND chunks |
| // => let them do ReadChunks before |
| ReadRemainingChunks(); |
| #endif |
| } |
| |
| return aRet; |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| sal_Bool PNGReaderImpl::ImplReadHeader( const Size& rPreviewSizeHint ) |
| { |
| if( mnChunkLen < 13 ) |
| return sal_False; |
| |
| maOrigSize.Width() = ImplReadsal_uInt32(); |
| maOrigSize.Height() = ImplReadsal_uInt32(); |
| |
| if ( !maOrigSize.Width() || !maOrigSize.Height() ) |
| return sal_False; |
| |
| mnPngDepth = *(maDataIter++); |
| mnColorType = *(maDataIter++); |
| |
| mnCompressionType = *(maDataIter++); |
| if( mnCompressionType != 0 ) // unknown compression type |
| return sal_False; |
| |
| mnFilterType = *(maDataIter++); |
| if( mnFilterType != 0 ) // unknown filter type |
| return sal_False; |
| |
| mnInterlaceType = *(maDataIter++); |
| switch ( mnInterlaceType ) // filter type valid ? |
| { |
| case 0 : // progressive image |
| mnPass = 7; |
| break; |
| case 1 : // Adam7-interlaced image |
| mnPass = 0; |
| break; |
| default: |
| return sal_False; |
| } |
| |
| mbPalette = sal_True; |
| mbIDAT = mbAlphaChannel = mbTransparent = sal_False; |
| mbGrayScale = mbRGBTriple = sal_False; |
| mnTargetDepth = mnPngDepth; |
| sal_uInt64 nScansize64 = ( ( static_cast< sal_uInt64 >( maOrigSize.Width() ) * mnPngDepth ) + 7 ) >> 3; |
| |
| // valid color types are 0,2,3,4 & 6 |
| switch ( mnColorType ) |
| { |
| case 0 : // each pixel is a grayscale |
| { |
| switch ( mnPngDepth ) |
| { |
| case 2 : // 2bit target not available -> use four bits |
| mnTargetDepth = 4; // we have to expand the bitmap |
| mbGrayScale = sal_True; |
| break; |
| case 16 : |
| mnTargetDepth = 8; // we have to reduce the bitmap |
| // fall through |
| case 1 : |
| case 4 : |
| case 8 : |
| mbGrayScale = sal_True; |
| break; |
| default : |
| return sal_False; |
| } |
| } |
| break; |
| |
| case 2 : // each pixel is an RGB triple |
| { |
| mbRGBTriple = sal_True; |
| nScansize64 *= 3; |
| switch ( mnPngDepth ) |
| { |
| case 16 : // we have to reduce the bitmap |
| case 8 : |
| mnTargetDepth = 24; |
| break; |
| default : |
| return sal_False; |
| } |
| } |
| break; |
| |
| case 3 : // each pixel is a palette index |
| { |
| switch ( mnPngDepth ) |
| { |
| case 2 : |
| mnTargetDepth = 4; // we have to expand the bitmap |
| // fall through |
| case 1 : |
| case 4 : |
| case 8 : |
| mbPalette = sal_False; |
| break; |
| default : |
| return sal_False; |
| } |
| } |
| break; |
| |
| case 4 : // each pixel is a grayscale sample followed by an alpha sample |
| { |
| nScansize64 *= 2; |
| mbAlphaChannel = sal_True; |
| switch ( mnPngDepth ) |
| { |
| case 16 : |
| mnTargetDepth = 8; // we have to reduce the bitmap |
| case 8 : |
| mbGrayScale = sal_True; |
| break; |
| default : |
| return sal_False; |
| } |
| } |
| break; |
| |
| case 6 : // each pixel is an RGB triple followed by an alpha sample |
| { |
| mbRGBTriple = sal_True; |
| nScansize64 *= 4; |
| mbAlphaChannel = sal_True; |
| switch (mnPngDepth ) |
| { |
| case 16 : // we have to reduce the bitmap |
| case 8 : |
| mnTargetDepth = 24; |
| break; |
| default : |
| return sal_False; |
| } |
| } |
| break; |
| |
| default : |
| return sal_False; |
| } |
| |
| mnBPP = static_cast< sal_uInt32 >( nScansize64 / maOrigSize.Width() ); |
| if ( !mnBPP ) |
| mnBPP = 1; |
| |
| nScansize64++; // each scanline includes one filterbyte |
| |
| if ( nScansize64 > SAL_MAX_UINT32 ) |
| return sal_False; |
| |
| mnScansize = static_cast< sal_uInt32 >( nScansize64 ); |
| |
| // TODO: switch between both scanlines instead of copying |
| mpInflateInBuf = new (std::nothrow) sal_uInt8[ mnScansize ]; |
| mpScanCurrent = mpInflateInBuf; |
| mpScanPrior = new (std::nothrow) sal_uInt8[ mnScansize ]; |
| |
| if ( !mpInflateInBuf || !mpScanPrior ) |
| return sal_False; |
| |
| // calculate target size from original size and the preview hint |
| if( rPreviewSizeHint.Width() || rPreviewSizeHint.Height() ) |
| { |
| Size aPreviewSize( rPreviewSizeHint.Width(), rPreviewSizeHint.Height() ); |
| maTargetSize = maOrigSize; |
| |
| if( aPreviewSize.Width() == 0 ) { |
| aPreviewSize.setWidth( ( maOrigSize.Width()*aPreviewSize.Height() )/maOrigSize.Height() ); |
| if( aPreviewSize.Width() <= 0 ) |
| aPreviewSize.setWidth( 1 ); |
| } else if( aPreviewSize.Height() == 0 ) { |
| aPreviewSize.setHeight( ( maOrigSize.Height()*aPreviewSize.Width() )/maOrigSize.Width() ); |
| if( aPreviewSize.Height() <= 0 ) |
| aPreviewSize.setHeight( 1 ); |
| } |
| |
| if( aPreviewSize.Width() < maOrigSize.Width() && aPreviewSize.Height() < maOrigSize.Height() ) { |
| OSL_TRACE("preview size %dx%d", aPreviewSize.Width(), aPreviewSize.Height() ); |
| |
| for( int i = 1; i < 5; ++i ) |
| { |
| if( (maTargetSize.Width() >> i) < aPreviewSize.Width() ) |
| break; |
| if( (maTargetSize.Height() >> i) < aPreviewSize.Height() ) |
| break; |
| mnPreviewShift = i; |
| } |
| mnPreviewMask = (1 << mnPreviewShift) - 1; |
| } |
| } |
| |
| maTargetSize.Width() = (maOrigSize.Width() + mnPreviewMask) >> mnPreviewShift; |
| maTargetSize.Height() = (maOrigSize.Height() + mnPreviewMask) >> mnPreviewShift; |
| |
| mpBmp = new Bitmap( maTargetSize, mnTargetDepth ); |
| mpAcc = mpBmp->AcquireWriteAccess(); |
| if( !mpAcc ) |
| return sal_False; |
| |
| mpBmp->SetSourceSizePixel( maOrigSize ); |
| |
| if ( mbAlphaChannel ) |
| { |
| mpAlphaMask = new AlphaMask( maTargetSize ); |
| mpAlphaMask->Erase( 128 ); |
| mpMaskAcc = mpAlphaMask->AcquireWriteAccess(); |
| if( !mpMaskAcc ) |
| return sal_False; |
| } |
| |
| if ( mbGrayScale ) |
| ImplGetGrayPalette( mnPngDepth ); |
| |
| ImplPreparePass(); |
| |
| return sal_True; |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| void PNGReaderImpl::ImplGetGrayPalette( sal_uInt16 nBitDepth ) |
| { |
| if( nBitDepth > 8 ) |
| nBitDepth = 8; |
| |
| sal_uInt16 nPaletteEntryCount = 1 << nBitDepth; |
| sal_uInt32 nAdd = nBitDepth ? 256 / (nPaletteEntryCount - 1) : 0; |
| |
| // no bitdepth==2 available |
| // but bitdepth==4 with two unused bits is close enough |
| if( nBitDepth == 2 ) |
| nPaletteEntryCount = 16; |
| |
| mpAcc->SetPaletteEntryCount( nPaletteEntryCount ); |
| for ( sal_uInt32 i = 0, nStart = 0; nStart < 256; i++, nStart += nAdd ) |
| mpAcc->SetPaletteColor( (sal_uInt16)i, BitmapColor( mpColorTable[ nStart ], |
| mpColorTable[ nStart ], mpColorTable[ nStart ] ) ); |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| sal_Bool PNGReaderImpl::ImplReadPalette() |
| { |
| sal_uInt16 nCount = static_cast<sal_uInt16>( mnChunkLen / 3 ); |
| |
| if ( ( ( mnChunkLen % 3 ) == 0 ) && ( ( 0 < nCount ) && ( nCount <= 256 ) ) && mpAcc ) |
| { |
| mbPalette = sal_True; |
| mpAcc->SetPaletteEntryCount( (sal_uInt16) nCount ); |
| |
| for ( sal_uInt16 i = 0; i < nCount; i++ ) |
| { |
| sal_uInt8 nRed = mpColorTable[ *maDataIter++ ]; |
| sal_uInt8 nGreen = mpColorTable[ *maDataIter++ ]; |
| sal_uInt8 nBlue = mpColorTable[ *maDataIter++ ]; |
| mpAcc->SetPaletteColor( i, Color( nRed, nGreen, nBlue ) ); |
| } |
| } |
| else |
| mbStatus = sal_False; |
| |
| return mbStatus; |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| sal_Bool PNGReaderImpl::ImplReadTransparent() |
| { |
| bool bNeedAlpha = false; |
| |
| if ( mpTransTab == NULL ) |
| { |
| switch ( mnColorType ) |
| { |
| case 0 : |
| { |
| if ( mnChunkLen == 2 ) |
| { |
| mpTransTab = new sal_uInt8[ 256 ]; |
| rtl_fillMemory( mpTransTab, 256, 0xff ); |
| // color type 0 and 4 is always greyscale, |
| // so the return value can be used as index |
| sal_uInt8 nIndex = ImplScaleColor(); |
| mpTransTab[ nIndex ] = 0; |
| mbTransparent = true; |
| } |
| } |
| break; |
| |
| case 2 : |
| { |
| if ( mnChunkLen == 6 ) |
| { |
| mnTransRed = ImplScaleColor(); |
| mnTransGreen = ImplScaleColor(); |
| mnTransBlue = ImplScaleColor(); |
| mbTransparent = true; |
| } |
| } |
| break; |
| |
| case 3 : |
| { |
| if ( mnChunkLen <= 256 ) |
| { |
| mpTransTab = new sal_uInt8 [ 256 ]; |
| rtl_fillMemory( mpTransTab, 256, 0xff ); |
| rtl_copyMemory( mpTransTab, &(*maDataIter), mnChunkLen ); |
| maDataIter += mnChunkLen; |
| mbTransparent = true; |
| // need alpha transparency if not on/off masking |
| for( int i = 0; i < mnChunkLen; ++i ) |
| bNeedAlpha |= (mpTransTab[i]!=0x00) && (mpTransTab[i]!=0xFF); |
| } |
| } |
| break; |
| } |
| } |
| |
| if( mbTransparent && !mbAlphaChannel && !mpMaskBmp ) |
| { |
| if( bNeedAlpha) |
| { |
| mpAlphaMask = new AlphaMask( maTargetSize ); |
| mpMaskAcc = mpAlphaMask->AcquireWriteAccess(); |
| } |
| else |
| { |
| mpMaskBmp = new Bitmap( maTargetSize, 1 ); |
| mpMaskAcc = mpMaskBmp->AcquireWriteAccess(); |
| } |
| mbTransparent = (mpMaskAcc != NULL); |
| if( !mbTransparent ) |
| return sal_False; |
| mcOpaqueColor = BitmapColor( 0x00 ); |
| mcTranspColor = BitmapColor( 0xFF ); |
| mpMaskAcc->Erase( 0x00 ); |
| } |
| |
| return sal_True; |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| void PNGReaderImpl::ImplGetGamma() |
| { |
| if( mnChunkLen < 4 ) |
| return; |
| |
| sal_uInt32 nGammaValue = ImplReadsal_uInt32(); |
| double fGamma = ( ( VIEWING_GAMMA / DISPLAY_GAMMA ) * ( (double)nGammaValue / 100000 ) ); |
| double fInvGamma = ( fGamma <= 0.0 || fGamma > 10.0 ) ? 1.0 : ( 1.0 / fGamma ); |
| |
| if ( fInvGamma != 1.0 ) |
| { |
| mbGamma = sal_True; |
| |
| if ( mpColorTable == mpDefaultColorTable ) |
| mpColorTable = new sal_uInt8[ 256 ]; |
| |
| for ( sal_Int32 i = 0; i < 256; i++ ) |
| mpColorTable[ i ] = (sal_uInt8)(pow((double)i/255.0, fInvGamma) * 255.0 + 0.5); |
| |
| if ( mbGrayScale ) |
| ImplGetGrayPalette( mnPngDepth ); |
| } |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| void PNGReaderImpl::ImplGetBackground() |
| { |
| switch ( mnColorType ) |
| { |
| case 3 : |
| { |
| if ( mnChunkLen == 1 ) |
| { |
| sal_uInt16 nCol = *maDataIter++; |
| if ( nCol < mpAcc->GetPaletteEntryCount() ) |
| { |
| mpAcc->Erase( mpAcc->GetPaletteColor( (sal_uInt8)nCol ) ); |
| break; |
| } |
| } |
| } |
| break; |
| |
| case 0 : |
| case 4 : |
| { |
| if ( mnChunkLen == 2 ) |
| { |
| // the color type 0 and 4 is always greyscale, |
| // so the return value can be used as index |
| sal_uInt8 nIndex = ImplScaleColor(); |
| mpAcc->Erase( mpAcc->GetPaletteColor( nIndex ) ); |
| } |
| } |
| break; |
| |
| case 2 : |
| case 6 : |
| { |
| if ( mnChunkLen == 6 ) |
| { |
| sal_uInt8 nRed = ImplScaleColor(); |
| sal_uInt8 nGreen = ImplScaleColor(); |
| sal_uInt8 nBlue = ImplScaleColor(); |
| mpAcc->Erase( Color( nRed, nGreen, nBlue ) ); |
| } |
| } |
| break; |
| } |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| // for color type 0 and 4 (greyscale) the return value is always index to the color |
| // 2 and 6 (RGB) the return value is always the 8 bit color component |
| sal_uInt8 PNGReaderImpl::ImplScaleColor() |
| { |
| sal_uInt32 nMask = ( ( 1 << mnPngDepth ) - 1 ); |
| sal_uInt16 nCol = ( *maDataIter++ << 8 ); |
| |
| nCol += *maDataIter++ & (sal_uInt16)nMask; |
| |
| if ( mnPngDepth > 8 ) // convert 16bit graphics to 8 |
| nCol >>= 8; |
| |
| return (sal_uInt8) nCol; |
| } |
| |
| // ------------------------------------------------------------------------ |
| // ImplReadIDAT reads as much image data as needed |
| |
| void PNGReaderImpl::ImplReadIDAT() |
| { |
| if( mnChunkLen > 0 ) |
| { |
| if ( mbzCodecInUse == sal_False ) |
| { |
| mbzCodecInUse = sal_True; |
| mpZCodec->BeginCompression( ZCODEC_PNG_DEFAULT ); |
| } |
| mpZCodec->SetBreak( mnChunkLen ); |
| SvMemoryStream aIStrm( &(*maDataIter), mnChunkLen, STREAM_READ ); |
| |
| while ( ( mpZCodec->GetBreak() ) ) |
| { |
| // get bytes needed to fill the current scanline |
| sal_Int32 nToRead = mnScansize - (mpScanCurrent - mpInflateInBuf); |
| sal_Int32 nRead = mpZCodec->ReadAsynchron( aIStrm, mpScanCurrent, nToRead ); |
| if ( nRead < 0 ) |
| { |
| mbStatus = sal_False; |
| break; |
| } |
| if ( nRead < nToRead ) |
| { |
| mpScanCurrent += nRead; // more ZStream data in the next IDAT chunk |
| break; |
| } |
| else // this scanline is Finished |
| { |
| mpScanCurrent = mpInflateInBuf; |
| ImplApplyFilter(); |
| |
| ImplDrawScanline( mnXStart, mnXAdd ); |
| mnYpos += mnYAdd; |
| } |
| |
| if ( mnYpos >= (sal_uInt32)maOrigSize.Height() ) |
| { |
| if( (mnPass < 7) && mnInterlaceType ) |
| if( ImplPreparePass() ) |
| continue; |
| mbIDAT = true; |
| break; |
| } |
| } |
| } |
| |
| if( mbIDAT ) |
| { |
| mpZCodec->EndCompression(); |
| mbzCodecInUse = sal_False; |
| } |
| } |
| |
| // --------------------------------------------------------------------------------------------------- |
| |
| bool PNGReaderImpl::ImplPreparePass() |
| { |
| struct InterlaceParams{ int mnXStart, mnYStart, mnXAdd, mnYAdd; }; |
| static const InterlaceParams aInterlaceParams[8] = |
| { |
| // non-interlaced |
| { 0, 0, 1, 1 }, |
| // Adam7-interlaced |
| { 0, 0, 8, 8 }, // pass 1 |
| { 4, 0, 8, 8 }, // pass 2 |
| { 0, 4, 4, 8 }, // pass 3 |
| { 2, 0, 4, 4 }, // pass 4 |
| { 0, 2, 2, 4 }, // pass 5 |
| { 1, 0, 2, 2 }, // pass 6 |
| { 0, 1, 1, 2 } // pass 7 |
| }; |
| |
| const InterlaceParams* pParam = &aInterlaceParams[ 0 ]; |
| if( mnInterlaceType ) |
| { |
| while( ++mnPass <= 7 ) |
| { |
| pParam = &aInterlaceParams[ mnPass ]; |
| |
| // skip this pass if the original image is too small for it |
| if( (pParam->mnXStart < maOrigSize.Width()) |
| && (pParam->mnYStart < maOrigSize.Height()) ) |
| break; |
| } |
| if( mnPass > 7 ) |
| return false; |
| |
| // skip the last passes if possible (for scaled down target images) |
| if( mnPreviewMask & (pParam->mnXStart | pParam->mnYStart) ) |
| return false; |
| } |
| |
| mnYpos = pParam->mnYStart; |
| mnXStart = pParam->mnXStart; |
| mnXAdd = pParam->mnXAdd; |
| mnYAdd = pParam->mnYAdd; |
| |
| // in Interlace mode the size of scanline is not constant |
| // so first we calculate the number of entrys |
| long nScanWidth = (maOrigSize.Width() - mnXStart + mnXAdd - 1) / mnXAdd; |
| mnScansize = nScanWidth; |
| |
| if( mbRGBTriple ) |
| mnScansize = 3 * nScanWidth; |
| |
| if( mbAlphaChannel ) |
| mnScansize += nScanWidth; |
| |
| // convert to width in bytes |
| mnScansize = ( mnScansize*mnPngDepth + 7 ) >> 3; |
| |
| ++mnScansize; // scan size also needs room for the filtertype byte |
| rtl_zeroMemory( mpScanPrior, mnScansize ); |
| |
| return true; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // ImplApplyFilter writes the complete Scanline (nY) |
| // in interlace mode the parameter nXStart and nXAdd are non-zero |
| |
| void PNGReaderImpl::ImplApplyFilter() |
| { |
| OSL_ASSERT( mnScansize >= mnBPP + 1 ); |
| const sal_uInt8* const pScanEnd = mpInflateInBuf + mnScansize; |
| |
| sal_uInt8 nFilterType = *mpInflateInBuf; // the filter type may change each scanline |
| switch ( nFilterType ) |
| { |
| default: // unknown Scanline Filter Type |
| case 0: // Filter Type "None" |
| // we let the pixels pass and display the data unfiltered |
| break; |
| |
| case 1: // Scanline Filter Type "Sub" |
| { |
| sal_uInt8* p1 = mpInflateInBuf + 1; |
| const sal_uInt8* p2 = p1; |
| p1 += mnBPP; |
| |
| // use left pixels |
| do |
| *p1 = static_cast<sal_uInt8>( *p1 + *(p2++) ); |
| while( ++p1 < pScanEnd ); |
| } |
| break; |
| |
| case 2: // Scanline Filter Type "Up" |
| { |
| sal_uInt8* p1 = mpInflateInBuf + 1; |
| const sal_uInt8* p2 = mpScanPrior + 1; |
| |
| // use pixels from prior line |
| while( p1 < pScanEnd ) |
| { |
| *p1 = static_cast<sal_uInt8>( *p1 + *(p2++) ); |
| ++p1; |
| } |
| } |
| break; |
| |
| case 3: // Scanline Filter Type "Average" |
| { |
| sal_uInt8* p1 = mpInflateInBuf + 1; |
| const sal_uInt8* p2 = mpScanPrior + 1; |
| const sal_uInt8* p3 = p1; |
| |
| // use one pixel from prior line |
| for( int n = mnBPP; --n >= 0; ++p1, ++p2) |
| *p1 = static_cast<sal_uInt8>( *p1 + (*p2 >> 1) ); |
| |
| // predict by averaging the left and prior line pixels |
| while( p1 < pScanEnd ) |
| { |
| *p1 = static_cast<sal_uInt8>( *p1 + ((*(p2++) + *(p3++)) >> 1) ); |
| ++p1; |
| } |
| } |
| break; |
| |
| case 4: // Scanline Filter Type "PaethPredictor" |
| { |
| sal_uInt8* p1 = mpInflateInBuf + 1; |
| const sal_uInt8* p2 = mpScanPrior + 1; |
| const sal_uInt8* p3 = p1; |
| const sal_uInt8* p4 = p2; |
| |
| // use one pixel from prior line |
| for( int n = mnBPP; --n >= 0; ++p1) |
| *p1 = static_cast<sal_uInt8>( *p1 + *(p2++) ); |
| |
| // predict by using the left and the prior line pixels |
| while( p1 < pScanEnd ) |
| { |
| int na = *(p2++); |
| int nb = *(p3++); |
| int nc = *(p4++); |
| |
| int npa = nb - (int)nc; |
| int npb = na - (int)nc; |
| int npc = npa + npb; |
| |
| if( npa < 0 ) |
| npa =-npa; |
| if( npb < 0 ) |
| npb =-npb; |
| if( npc < 0 ) |
| npc =-npc; |
| |
| if( npa > npb ) |
| na = nb, npa = npb; |
| if( npa > npc ) |
| na = nc; |
| |
| *p1 = static_cast<sal_uInt8>( *p1 + na ); |
| ++p1; |
| } |
| } |
| break; |
| } |
| |
| rtl_copyMemory( mpScanPrior, mpInflateInBuf, mnScansize ); |
| } |
| |
| // --------------------------------------------------------------------------------------------------- |
| // ImplDrawScanlines draws the complete Scanline (nY) into the target bitmap |
| // In interlace mode the parameter nXStart and nXAdd append to the currently used pass |
| |
| void PNGReaderImpl::ImplDrawScanline( sal_uInt32 nXStart, sal_uInt32 nXAdd ) |
| { |
| // optimization for downscaling |
| if( mnYpos & mnPreviewMask ) |
| return; |
| if( nXStart & mnPreviewMask ) |
| return; |
| |
| // convert nY to pixel units in the target image |
| // => TODO; also do this for nX here instead of in the ImplSet*Pixel() methods |
| const sal_uInt32 nY = mnYpos >> mnPreviewShift; |
| |
| const sal_uInt8* pTmp = mpInflateInBuf + 1; |
| if ( mpAcc->HasPalette() ) // alphachannel is not allowed by pictures including palette entries |
| { |
| switch ( mpAcc->GetBitCount() ) |
| { |
| case 1 : |
| { |
| if ( mbTransparent ) |
| { |
| for ( sal_Int32 nX = nXStart, nShift = 0; nX < maOrigSize.Width(); nX += nXAdd ) |
| { |
| sal_uInt8 nCol; |
| nShift = (nShift - 1) & 7; |
| if ( nShift == 0 ) |
| nCol = *(pTmp++); |
| else |
| nCol = static_cast<sal_uInt8>( *pTmp >> nShift ); |
| nCol &= 1; |
| |
| ImplSetAlphaPixel( nY, nX, nCol, mpTransTab[ nCol ] ); |
| } |
| } |
| else |
| { // BMP_FORMAT_1BIT_MSB_PAL |
| for ( sal_Int32 nX = nXStart, nShift = 0; nX < maOrigSize.Width(); nX += nXAdd ) |
| { |
| nShift = (nShift - 1) & 7; |
| |
| sal_uInt8 nCol; |
| if ( nShift == 0 ) |
| nCol = *(pTmp++); |
| else |
| nCol = static_cast<sal_uInt8>( *pTmp >> nShift ); |
| nCol &= 1; |
| |
| ImplSetPixel( nY, nX, nCol ); |
| } |
| } |
| } |
| break; |
| |
| case 4 : |
| { |
| if ( mbTransparent ) |
| { |
| if ( mnPngDepth == 4 ) // check if source has a two bit pixel format |
| { |
| for ( sal_Int32 nX = nXStart, nXIndex = 0; nX < maOrigSize.Width(); nX += nXAdd, ++nXIndex ) |
| { |
| if( nXIndex & 1 ) |
| { |
| ImplSetAlphaPixel( nY, nX, *pTmp & 0x0f, mpTransTab[ *pTmp & 0x0f ] ); |
| pTmp++; |
| } |
| else |
| { |
| ImplSetAlphaPixel( nY, nX, ( *pTmp >> 4 ) & 0x0f, mpTransTab[ *pTmp >> 4 ] ); |
| } |
| } |
| } |
| else // if ( mnPngDepth == 2 ) |
| { |
| for ( sal_Int32 nX = nXStart, nXIndex = 0; nX < maOrigSize.Width(); nX += nXAdd, nXIndex++ ) |
| { |
| sal_uInt8 nCol; |
| switch( nXIndex & 3 ) |
| { |
| case 0 : |
| nCol = *pTmp >> 6; |
| break; |
| |
| case 1 : |
| nCol = ( *pTmp >> 4 ) & 0x03 ; |
| break; |
| |
| case 2 : |
| nCol = ( *pTmp >> 2 ) & 0x03; |
| break; |
| |
| case 3 : |
| nCol = ( *pTmp++ ) & 0x03; |
| break; |
| |
| default: // get rid of nCol uninitialized warning |
| nCol = 0; |
| break; |
| } |
| |
| ImplSetAlphaPixel( nY, nX, nCol, mpTransTab[ nCol ] ); |
| } |
| } |
| } |
| else |
| { |
| if ( mnPngDepth == 4 ) // maybe the source is a two bitmap graphic |
| { // BMP_FORMAT_4BIT_LSN_PAL |
| for ( sal_Int32 nX = nXStart, nXIndex = 0; nX < maOrigSize.Width(); nX += nXAdd, nXIndex++ ) |
| { |
| if( nXIndex & 1 ) |
| ImplSetPixel( nY, nX, *pTmp++ & 0x0f ); |
| else |
| ImplSetPixel( nY, nX, ( *pTmp >> 4 ) & 0x0f ); |
| } |
| } |
| else // if ( mnPngDepth == 2 ) |
| { |
| for ( sal_Int32 nX = nXStart, nXIndex = 0; nX < maOrigSize.Width(); nX += nXAdd, nXIndex++ ) |
| { |
| switch( nXIndex & 3 ) |
| { |
| case 0 : |
| ImplSetPixel( nY, nX, *pTmp >> 6 ); |
| break; |
| |
| case 1 : |
| ImplSetPixel( nY, nX, ( *pTmp >> 4 ) & 0x03 ); |
| break; |
| |
| case 2 : |
| ImplSetPixel( nY, nX, ( *pTmp >> 2 ) & 0x03 ); |
| break; |
| |
| case 3 : |
| ImplSetPixel( nY, nX, *pTmp++ & 0x03 ); |
| break; |
| } |
| } |
| } |
| } |
| } |
| break; |
| |
| case 8 : |
| { |
| if ( mbAlphaChannel ) |
| { |
| if ( mnPngDepth == 8 ) // maybe the source is a 16 bit grayscale |
| { |
| for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 2 ) |
| ImplSetAlphaPixel( nY, nX, pTmp[ 0 ], pTmp[ 1 ] ); |
| } |
| else |
| { |
| for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 4 ) |
| ImplSetAlphaPixel( nY, nX, pTmp[ 0 ], pTmp[ 2 ] ); |
| } |
| } |
| else if ( mbTransparent ) |
| { |
| if ( mnPngDepth == 8 ) // maybe the source is a 16 bit grayscale |
| { |
| for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp++ ) |
| ImplSetAlphaPixel( nY, nX, *pTmp, mpTransTab[ *pTmp ] ); |
| } |
| else |
| { |
| for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 2 ) |
| ImplSetAlphaPixel( nY, nX, *pTmp, mpTransTab[ *pTmp ] ); |
| } |
| } |
| else // neither alpha nor transparency |
| { |
| if ( mnPngDepth == 8 ) // maybe the source is a 16 bit grayscale |
| { |
| if( nXAdd == 1 && mnPreviewShift == 0 ) // copy raw line data if possible |
| { |
| int nLineBytes = maOrigSize.Width(); |
| mpAcc->CopyScanline( nY, pTmp, BMP_FORMAT_8BIT_PAL, nLineBytes ); |
| pTmp += nLineBytes; |
| } |
| else |
| { |
| for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd ) |
| ImplSetPixel( nY, nX, *pTmp++ ); |
| } |
| } |
| else |
| { |
| for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 2 ) |
| ImplSetPixel( nY, nX, *pTmp ); |
| } |
| } |
| } |
| break; |
| |
| default : |
| mbStatus = sal_False; |
| break; |
| } |
| } |
| else // no palette => truecolor |
| { |
| // #122985# Added fast-lane implementations using CopyScanline with direct supported mem formats |
| static bool bCkeckDirectScanline(true); |
| |
| if( mbAlphaChannel ) |
| { |
| // has RGB + alpha |
| if ( mnPngDepth == 8 ) // maybe the source has 16 bit per sample |
| { |
| // BMP_FORMAT_32BIT_TC_RGBA |
| // only use DirectScanline when we have no preview shifting stuff and accesses to content and alpha |
| const bool bDoDirectScanline( |
| bCkeckDirectScanline && !nXStart && 1 == nXAdd && !mnPreviewShift && mpAcc && mpMaskAcc); |
| const bool bCustomColorTable(mpColorTable != mpDefaultColorTable); |
| |
| if(bDoDirectScanline) |
| { |
| // allocate scanlines on demand, reused for next line |
| if(!mpScanline) |
| { |
| #ifdef DBG_UTIL |
| mnAllocSizeScanline = maOrigSize.Width() * 3; |
| #endif |
| mpScanline = new sal_uInt8[maOrigSize.Width() * 3]; |
| } |
| |
| if(!mpScanlineAlpha) |
| { |
| #ifdef DBG_UTIL |
| mnAllocSizeScanlineAlpha = maOrigSize.Width(); |
| #endif |
| mpScanlineAlpha = new sal_uInt8[maOrigSize.Width()]; |
| } |
| } |
| |
| if(bDoDirectScanline) |
| { |
| OSL_ENSURE(mpScanline, "No Scanline allocated (!)"); |
| OSL_ENSURE(mpScanlineAlpha, "No ScanlineAlpha allocated (!)"); |
| #ifdef DBG_UTIL |
| OSL_ENSURE(mnAllocSizeScanline >= maOrigSize.Width() * 3, "Allocated Scanline too small (!)"); |
| OSL_ENSURE(mnAllocSizeScanlineAlpha >= maOrigSize.Width(), "Allocated ScanlineAlpha too small (!)"); |
| #endif |
| sal_uInt8* pScanline(mpScanline); |
| sal_uInt8* pScanlineAlpha(mpScanlineAlpha); |
| |
| for(sal_Int32 nX(0); nX < maOrigSize.Width(); nX++, pTmp += 4) |
| { |
| // prepare content line as BGR by reordering when copying |
| // do not forget to invert alpha (source is alpha, target is opacity) |
| if(bCustomColorTable) |
| { |
| *pScanline++ = mpColorTable[pTmp[2]]; |
| *pScanline++ = mpColorTable[pTmp[1]]; |
| *pScanline++ = mpColorTable[pTmp[0]]; |
| *pScanlineAlpha++ = ~pTmp[3]; |
| } |
| else |
| { |
| *pScanline++ = pTmp[2]; |
| *pScanline++ = pTmp[1]; |
| *pScanline++ = pTmp[0]; |
| *pScanlineAlpha++ = ~pTmp[3]; |
| } |
| } |
| |
| // copy scanlines directly to bitmaps for content and alpha; use the formats which |
| // are able to copy directly to BitmapBuffer |
| mpAcc->CopyScanline(nY, mpScanline, BMP_FORMAT_24BIT_TC_BGR, maOrigSize.Width() * 3); |
| mpMaskAcc->CopyScanline(nY, mpScanlineAlpha, BMP_FORMAT_8BIT_PAL, maOrigSize.Width()); |
| } |
| else |
| { |
| for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 4 ) |
| { |
| if(bCustomColorTable) |
| { |
| ImplSetAlphaPixel( |
| nY, |
| nX, |
| BitmapColor( |
| mpColorTable[ pTmp[ 0 ] ], |
| mpColorTable[ pTmp[ 1 ] ], |
| mpColorTable[ pTmp[ 2 ] ]), |
| pTmp[ 3 ]); |
| } |
| else |
| { |
| ImplSetAlphaPixel( |
| nY, |
| nX, |
| BitmapColor( |
| pTmp[0], |
| pTmp[1], |
| pTmp[2]), |
| pTmp[3]); |
| } |
| } |
| } |
| } |
| else |
| { |
| // BMP_FORMAT_64BIT_TC_RGBA |
| for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 8 ) |
| { |
| ImplSetAlphaPixel( |
| nY, |
| nX, |
| BitmapColor( |
| mpColorTable[ pTmp[ 0 ] ], |
| mpColorTable[ pTmp[ 2 ] ], |
| mpColorTable[ pTmp[ 4 ] ]), |
| pTmp[6]); |
| } |
| } |
| } |
| else if( mbTransparent ) // has RGB + transparency |
| { |
| // BMP_FORMAT_24BIT_TC_RGB |
| // no support currently for DirectScanline, found no real usages in current PNGs, may be added on demand |
| if ( mnPngDepth == 8 ) // maybe the source has 16 bit per sample |
| { |
| for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 3 ) |
| { |
| sal_uInt8 nRed = pTmp[ 0 ]; |
| sal_uInt8 nGreen = pTmp[ 1 ]; |
| sal_uInt8 nBlue = pTmp[ 2 ]; |
| sal_Bool bTransparent = ( ( nRed == mnTransRed ) |
| && ( nGreen == mnTransGreen ) |
| && ( nBlue == mnTransBlue ) ); |
| |
| ImplSetTranspPixel( nY, nX, BitmapColor( mpColorTable[ nRed ], |
| mpColorTable[ nGreen ], |
| mpColorTable[ nBlue ] ), bTransparent ); |
| } |
| } |
| else |
| { |
| // BMP_FORMAT_48BIT_TC_RGB |
| for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 6 ) |
| { |
| sal_uInt8 nRed = pTmp[ 0 ]; |
| sal_uInt8 nGreen = pTmp[ 2 ]; |
| sal_uInt8 nBlue = pTmp[ 4 ]; |
| sal_Bool bTransparent = ( ( nRed == mnTransRed ) |
| && ( nGreen == mnTransGreen ) |
| && ( nBlue == mnTransBlue ) ); |
| |
| ImplSetTranspPixel( nY, nX, BitmapColor( mpColorTable[ nRed ], |
| mpColorTable[ nGreen ], |
| mpColorTable[ nBlue ] ), bTransparent ); |
| } |
| } |
| } |
| else // has RGB but neither alpha nor transparency |
| { |
| // BMP_FORMAT_24BIT_TC_RGB |
| // only use DirectScanline when we have no preview shifting stuff and access to content |
| const bool bDoDirectScanline( |
| bCkeckDirectScanline && !nXStart && 1 == nXAdd && !mnPreviewShift && mpAcc); |
| const bool bCustomColorTable(mpColorTable != mpDefaultColorTable); |
| |
| if(bDoDirectScanline && !mpScanline) |
| { |
| // allocate scanlines on demand, reused for next line |
| #ifdef DBG_UTIL |
| mnAllocSizeScanline = maOrigSize.Width() * 3; |
| #endif |
| mpScanline = new sal_uInt8[maOrigSize.Width() * 3]; |
| } |
| |
| if ( mnPngDepth == 8 ) // maybe the source has 16 bit per sample |
| { |
| if(bDoDirectScanline) |
| { |
| OSL_ENSURE(mpScanline, "No Scanline allocated (!)"); |
| #ifdef DBG_UTIL |
| OSL_ENSURE(mnAllocSizeScanline >= maOrigSize.Width() * 3, "Allocated Scanline too small (!)"); |
| #endif |
| sal_uInt8* pScanline(mpScanline); |
| |
| for(sal_Int32 nX(0); nX < maOrigSize.Width(); nX++, pTmp += 3) |
| { |
| // prepare content line as BGR by reordering when copying |
| if(bCustomColorTable) |
| { |
| *pScanline++ = mpColorTable[pTmp[2]]; |
| *pScanline++ = mpColorTable[pTmp[1]]; |
| *pScanline++ = mpColorTable[pTmp[0]]; |
| } |
| else |
| { |
| *pScanline++ = pTmp[2]; |
| *pScanline++ = pTmp[1]; |
| *pScanline++ = pTmp[0]; |
| } |
| } |
| |
| // copy scanline directly to bitmap for content; use the format which is able to |
| // copy directly to BitmapBuffer |
| mpAcc->CopyScanline(nY, mpScanline, BMP_FORMAT_24BIT_TC_BGR, maOrigSize.Width() * 3); |
| } |
| else |
| { |
| for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 3 ) |
| { |
| if(bCustomColorTable) |
| { |
| ImplSetPixel( |
| nY, |
| nX, |
| BitmapColor( |
| mpColorTable[ pTmp[ 0 ] ], |
| mpColorTable[ pTmp[ 1 ] ], |
| mpColorTable[ pTmp[ 2 ] ])); |
| } |
| else |
| { |
| ImplSetPixel( |
| nY, |
| nX, |
| BitmapColor( |
| pTmp[0], |
| pTmp[1], |
| pTmp[2])); |
| } |
| } |
| } |
| } |
| else |
| { |
| // BMP_FORMAT_48BIT_TC_RGB |
| // no support currently for DirectScanline, found no real usages in current PNGs, may be added on demand |
| for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 6 ) |
| { |
| ImplSetPixel( |
| nY, |
| nX, |
| BitmapColor( |
| mpColorTable[ pTmp[ 0 ] ], |
| mpColorTable[ pTmp[ 2 ] ], |
| mpColorTable[ pTmp[ 4 ] ])); |
| } |
| } |
| } |
| } |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| void PNGReaderImpl::ImplSetPixel( sal_uInt32 nY, sal_uInt32 nX, const BitmapColor& rBitmapColor ) |
| { |
| // TODO: get preview mode checks out of inner loop |
| if( nX & mnPreviewMask ) |
| return; |
| nX >>= mnPreviewShift; |
| |
| mpAcc->SetPixel( nY, nX, rBitmapColor ); |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| void PNGReaderImpl::ImplSetPixel( sal_uInt32 nY, sal_uInt32 nX, sal_uInt8 nPalIndex ) |
| { |
| // TODO: get preview mode checks out of inner loop |
| if( nX & mnPreviewMask ) |
| return; |
| nX >>= mnPreviewShift; |
| |
| mpAcc->SetPixelIndex( nY, nX, nPalIndex ); |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| void PNGReaderImpl::ImplSetTranspPixel( sal_uInt32 nY, sal_uInt32 nX, const BitmapColor& rBitmapColor, sal_Bool bTrans ) |
| { |
| // TODO: get preview mode checks out of inner loop |
| if( nX & mnPreviewMask ) |
| return; |
| nX >>= mnPreviewShift; |
| |
| mpAcc->SetPixel( nY, nX, rBitmapColor ); |
| |
| if ( bTrans ) |
| mpMaskAcc->SetPixel( nY, nX, mcTranspColor ); |
| else |
| mpMaskAcc->SetPixel( nY, nX, mcOpaqueColor ); |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| void PNGReaderImpl::ImplSetAlphaPixel( sal_uInt32 nY, sal_uInt32 nX, |
| sal_uInt8 nPalIndex, sal_uInt8 nAlpha ) |
| { |
| // TODO: get preview mode checks out of inner loop |
| if( nX & mnPreviewMask ) |
| return; |
| nX >>= mnPreviewShift; |
| |
| mpAcc->SetPixelIndex( nY, nX, nPalIndex ); |
| mpMaskAcc->SetPixelIndex( nY, nX, ~nAlpha ); |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| void PNGReaderImpl::ImplSetAlphaPixel( sal_uInt32 nY, sal_uInt32 nX, |
| const BitmapColor& rBitmapColor, sal_uInt8 nAlpha ) |
| { |
| // TODO: get preview mode checks out of inner loop |
| if( nX & mnPreviewMask ) |
| return; |
| nX >>= mnPreviewShift; |
| |
| mpAcc->SetPixel( nY, nX, rBitmapColor ); |
| mpMaskAcc->SetPixelIndex( nY, nX, ~nAlpha ); |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| sal_uInt32 PNGReaderImpl::ImplReadsal_uInt32() |
| { |
| sal_uInt32 nRet; |
| nRet = *maDataIter++; |
| nRet <<= 8; |
| nRet |= *maDataIter++; |
| nRet <<= 8; |
| nRet |= *maDataIter++; |
| nRet <<= 8; |
| nRet |= *maDataIter++; |
| return nRet; |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| // ------------- |
| // - PNGReader - |
| // ------------- |
| |
| PNGReader::PNGReader( SvStream& rIStm ) : |
| mpImpl( new ::vcl::PNGReaderImpl( rIStm ) ) |
| { |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| PNGReader::~PNGReader() |
| { |
| delete mpImpl; |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| BitmapEx PNGReader::Read( const Size& i_rPreviewSizeHint ) |
| { |
| return mpImpl->GetBitmapEx( i_rPreviewSizeHint ); |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| const std::vector< vcl::PNGReader::ChunkData >& PNGReader::GetChunks() const |
| { |
| return mpImpl->GetAllChunks(); |
| } |
| |
| // ------------------------------------------------------------------------ |
| |
| void PNGReader::SetIgnoreGammaChunk( sal_Bool b ) |
| { |
| mpImpl->SetIgnoreGammaChunk( b ); |
| } |
| |
| |
| } // namespace vcl |