diff options
Diffstat (limited to 'vcl/source/gdi/pngread.cxx')
-rw-r--r-- | vcl/source/gdi/pngread.cxx | 1579 |
1 files changed, 1579 insertions, 0 deletions
diff --git a/vcl/source/gdi/pngread.cxx b/vcl/source/gdi/pngread.cxx new file mode 100644 index 000000000000..11971db34378 --- /dev/null +++ b/vcl/source/gdi/pngread.cxx @@ -0,0 +1,1579 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 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 BYTE 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; + BYTE* mpInflateInBuf; // as big as the size of a scanline + alphachannel + 1 + BYTE* mpScanPrior; // pointer to the latest scanline + BYTE* mpTransTab; // for transparency in images with palette colortype + BYTE* mpScanCurrent; // pointer into the current scanline + BYTE* 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) + USHORT mnIStmOldMode; + USHORT mnTargetDepth; // pixel depth of target bitmap + BYTE mnTransRed; + BYTE mnTransGreen; + BYTE mnTransBlue; + BYTE mnPngDepth; // pixel depth of PNG data + BYTE mnColorType; + BYTE mnCompressionType; + BYTE mnFilterType; + BYTE mnInterlaceType; + BitmapColor mcTranspColor; // transparency mask's transparency "color" + BitmapColor mcOpaqueColor; // transparency mask's opaque "color" + BOOL mbTransparent; // graphic includes an tRNS Chunk or an alpha Channel + BOOL mbAlphaChannel; // is true for ColorType 4 and 6 + BOOL mbRGBTriple; + BOOL mbPalette; // FALSE if we need a Palette + BOOL mbGrayScale; + BOOL mbzCodecInUse; + BOOL mbStatus; + BOOL mbIDAT; // TRUE if finished with enough IDAT chunks + BOOL mbGamma; // TRUE if Gamma Correction available + BOOL mbpHYs; // TRUE if pysical size of pixel available + sal_Bool mbIgnoreGammaChunk; + + bool ReadNextChunk(); + void ReadRemainingChunks(); + void SkipRemainingChunks(); + + void ImplSetPixel( sal_uInt32 y, sal_uInt32 x, const BitmapColor & ); + void ImplSetPixel( sal_uInt32 y, sal_uInt32 x, BYTE nPalIndex ); + void ImplSetTranspPixel( sal_uInt32 y, sal_uInt32 x, const BitmapColor &, BOOL bTrans ); + void ImplSetAlphaPixel( sal_uInt32 y, sal_uInt32 x, BYTE nPalIndex, BYTE nAlpha ); + void ImplSetAlphaPixel( sal_uInt32 y, sal_uInt32 x, const BitmapColor&, BYTE nAlpha ); + void ImplReadIDAT(); + bool ImplPreparePass(); + void ImplApplyFilter(); + void ImplDrawScanline( sal_uInt32 nXStart, sal_uInt32 nXAdd ); + BOOL ImplReadTransparent(); + void ImplGetGamma(); + void ImplGetBackground(); + BYTE ImplScaleColor(); + BOOL ImplReadHeader( const Size& rPreviewSizeHint ); + 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 ), + mbzCodecInUse ( sal_False ), + mbStatus( TRUE), + mbIDAT( FALSE ), + mbGamma ( sal_False ), + mbpHYs ( sal_False ), + mbIgnoreGammaChunk ( sal_False ) +{ + // 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; +} + +// ------------------------------------------------------------------------ + +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(); + + // parse the chunks + while( mbStatus && !mbIDAT && ReadNextChunk() ) + { + switch( mnChunkType ) + { + case PNGCHUNK_IHDR : + { + mbStatus = ImplReadHeader( rPreviewSizeHint ); + } + break; + + case PNGCHUNK_gAMA : // the gamma chunk must precede + { // the 'IDAT' and also the 'PLTE'(if available ) + if ( !mbIgnoreGammaChunk && ( mbIDAT == 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 == FALSE ) && mbPalette ) // before the 'IDAT' and after the + ImplGetBackground(); // PLTE(if available ) chunk. + } + break; + + case PNGCHUNK_IDAT : + { + 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; +} + +// ------------------------------------------------------------------------ + +BOOL PNGReaderImpl::ImplReadHeader( const Size& rPreviewSizeHint ) +{ + if( mnChunkLen < 13 ) + return FALSE; + + maOrigSize.Width() = ImplReadsal_uInt32(); + maOrigSize.Height() = ImplReadsal_uInt32(); + + if ( !maOrigSize.Width() || !maOrigSize.Height() ) + return FALSE; + + mnPngDepth = *(maDataIter++); + mnColorType = *(maDataIter++); + + mnCompressionType = *(maDataIter++); + if( mnCompressionType != 0 ) // unknown compression type + return FALSE; + + mnFilterType = *(maDataIter++); + if( mnFilterType != 0 ) // unknown filter type + return 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 FALSE; + } + + mbPalette = TRUE; + mbIDAT = mbAlphaChannel = mbTransparent = FALSE; + mbGrayScale = mbRGBTriple = FALSE; + mnTargetDepth = mnPngDepth; + mnScansize = ( ( 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 = TRUE; + break; + case 16 : + mnTargetDepth = 8; // we have to reduce the bitmap + // fall through + case 1 : + case 4 : + case 8 : + mbGrayScale = TRUE; + break; + default : + return FALSE; + } + } + break; + + case 2 : // each pixel is an RGB triple + { + mbRGBTriple = TRUE; + mnScansize *= 3; + switch ( mnPngDepth ) + { + case 16 : // we have to reduce the bitmap + case 8 : + mnTargetDepth = 24; + break; + default : + return 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 = FALSE; + break; + default : + return FALSE; + } + } + break; + + case 4 : // each pixel is a grayscale sample followed by an alpha sample + { + mnScansize *= 2; + mbAlphaChannel = TRUE; + switch ( mnPngDepth ) + { + case 16 : + mnTargetDepth = 8; // we have to reduce the bitmap + case 8 : + mbGrayScale = TRUE; + break; + default : + return FALSE; + } + } + break; + + case 6 : // each pixel is an RGB triple followed by an alpha sample + { + mbRGBTriple = TRUE; + mnScansize *= 4; + mbAlphaChannel = TRUE; + switch (mnPngDepth ) + { + case 16 : // we have to reduce the bitmap + case 8 : + mnTargetDepth = 24; + break; + default : + return FALSE; + } + } + break; + + default : + return FALSE; + } + + mnBPP = mnScansize / maOrigSize.Width(); + if ( !mnBPP ) + mnBPP = 1; + + mnScansize++; // each scanline includes one filterbyte + + // TODO: switch between both scanlines instead of copying + mpInflateInBuf = new BYTE[ mnScansize ]; + mpScanCurrent = mpInflateInBuf; + mpScanPrior = new BYTE[ mnScansize ]; + + // 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 FALSE; + + mpBmp->SetSourceSizePixel( maOrigSize ); + + if ( mbAlphaChannel ) + { + mpAlphaMask = new AlphaMask( maTargetSize ); + mpAlphaMask->Erase( 128 ); + mpMaskAcc = mpAlphaMask->AcquireWriteAccess(); + if( !mpMaskAcc ) + return FALSE; + } + + if ( mbGrayScale ) + ImplGetGrayPalette( mnPngDepth ); + + ImplPreparePass(); + + return 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( (USHORT)i, BitmapColor( mpColorTable[ nStart ], + mpColorTable[ nStart ], mpColorTable[ nStart ] ) ); +} + +// ------------------------------------------------------------------------ + +BOOL PNGReaderImpl::ImplReadPalette() +{ + sal_uInt16 nCount = static_cast<sal_uInt16>( mnChunkLen / 3 ); + + if ( ( ( mnChunkLen % 3 ) == 0 ) && ( ( 0 < nCount ) && ( nCount <= 256 ) ) && mpAcc ) + { + mbPalette = TRUE; + mpAcc->SetPaletteEntryCount( (USHORT) nCount ); + + for ( sal_uInt16 i = 0; i < nCount; i++ ) + { + BYTE nRed = mpColorTable[ *maDataIter++ ]; + BYTE nGreen = mpColorTable[ *maDataIter++ ]; + BYTE nBlue = mpColorTable[ *maDataIter++ ]; + mpAcc->SetPaletteColor( i, Color( nRed, nGreen, nBlue ) ); + } + } + else + mbStatus = FALSE; + + return mbStatus; +} + +// ------------------------------------------------------------------------ + +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 BYTE [ 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 FALSE; + mcOpaqueColor = BitmapColor( 0x00 ); + mcTranspColor = BitmapColor( 0xFF ); + mpMaskAcc->Erase( 0x00 ); + } + + return 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 = 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 ) + { + UINT16 nCol = *maDataIter++; + if ( nCol < mpAcc->GetPaletteEntryCount() ) + { + mpAcc->Erase( mpAcc->GetPaletteColor( (BYTE)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 == FALSE ) + { + mbzCodecInUse = 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 = 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 = 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 BYTE* const pScanEnd = mpInflateInBuf + mnScansize; + + BYTE 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" + { + BYTE* p1 = mpInflateInBuf + 1; + const BYTE* p2 = p1; + p1 += mnBPP; + + // use left pixels + do + *p1 = static_cast<BYTE>( *p1 + *(p2++) ); + while( ++p1 < pScanEnd ); + } + break; + + case 2: // Scanline Filter Type "Up" + { + BYTE* p1 = mpInflateInBuf + 1; + const BYTE* p2 = mpScanPrior + 1; + + // use pixels from prior line + while( p1 < pScanEnd ) + { + *p1 = static_cast<BYTE>( *p1 + *(p2++) ); + ++p1; + } + } + break; + + case 3: // Scanline Filter Type "Average" + { + BYTE* p1 = mpInflateInBuf + 1; + const BYTE* p2 = mpScanPrior + 1; + const BYTE* p3 = p1; + + // use one pixel from prior line + for( int n = mnBPP; --n >= 0; ++p1, ++p2) + *p1 = static_cast<BYTE>( *p1 + (*p2 >> 1) ); + + // predict by averaging the left and prior line pixels + while( p1 < pScanEnd ) + { + *p1 = static_cast<BYTE>( *p1 + ((*(p2++) + *(p3++)) >> 1) ); + ++p1; + } + } + break; + + case 4: // Scanline Filter Type "PaethPredictor" + { + BYTE* p1 = mpInflateInBuf + 1; + const BYTE* p2 = mpScanPrior + 1; + const BYTE* p3 = p1; + const BYTE* p4 = p2; + + // use one pixel from prior line + for( int n = mnBPP; --n >= 0; ++p1) + *p1 = static_cast<BYTE>( *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<BYTE>( *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 BYTE* 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 ) + { + BYTE nCol; + nShift = (nShift - 1) & 7; + if ( nShift == 0 ) + nCol = *(pTmp++); + else + nCol = static_cast<BYTE>( *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; + + BYTE nCol; + if ( nShift == 0 ) + nCol = *(pTmp++); + else + nCol = static_cast<BYTE>( *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++ ) + { + BYTE 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 = FALSE; + break; + } + } + else // no palette => truecolor + { + if( mbAlphaChannel ) // has RGB + alpha + { // BMP_FORMAT_32BIT_TC_RGBA + if ( mnPngDepth == 8 ) // maybe the source has 16 bit per sample + { + if ( mpColorTable != mpDefaultColorTable ) + { + for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 4 ) + ImplSetAlphaPixel( nY, nX, BitmapColor( mpColorTable[ pTmp[ 0 ] ], + mpColorTable[ pTmp[ 1 ] ], + mpColorTable[ pTmp[ 2 ] ] ), pTmp[ 3 ] ); + } + else + { +// if ( nXAdd == 1 && mnPreviewShift == 0 ) // copy raw line data if possible +// { +// int nLineBytes = 4 * maOrigSize.Width(); +// mpAcc->CopyScanline( nY, pTmp, BMP_FORMAT_32BIT_TC_RGBA, nLineBytes ); +// pTmp += nLineBytes; +// } +// else + { + for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 4 ) + 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 + 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 + if ( mnPngDepth == 8 ) // maybe the source has 16 bit per sample + { + if ( mpColorTable != mpDefaultColorTable ) + { + for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 3 ) + ImplSetPixel( nY, nX, BitmapColor( mpColorTable[ pTmp[ 0 ] ], + mpColorTable[ pTmp[ 1 ] ], + mpColorTable[ pTmp[ 2 ] ] ) ); + } + else + { + if( nXAdd == 1 && mnPreviewShift == 0 ) // copy raw line data if possible + { + int nLineBytes = maOrigSize.Width() * 3; + mpAcc->CopyScanline( nY, pTmp, BMP_FORMAT_24BIT_TC_RGB, nLineBytes ); + pTmp += nLineBytes; + } + else + { + for ( sal_Int32 nX = nXStart; nX < maOrigSize.Width(); nX += nXAdd, pTmp += 3 ) + ImplSetPixel( nY, nX, BitmapColor( pTmp[0], pTmp[1], pTmp[2] ) ); + } + } + } + else + { // BMP_FORMAT_48BIT_TC_RGB + 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, BYTE nPalIndex ) +{ + // TODO: get preview mode checks out of inner loop + if( nX & mnPreviewMask ) + return; + nX >>= mnPreviewShift; + + mpAcc->SetPixel( nY, nX, nPalIndex ); +} + +// ------------------------------------------------------------------------ + +void PNGReaderImpl::ImplSetTranspPixel( sal_uInt32 nY, sal_uInt32 nX, const BitmapColor& rBitmapColor, 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, + BYTE nPalIndex, BYTE nAlpha ) +{ + // TODO: get preview mode checks out of inner loop + if( nX & mnPreviewMask ) + return; + nX >>= mnPreviewShift; + + mpAcc->SetPixel( nY, nX, nPalIndex ); + mpMaskAcc->SetPixel( nY, nX, ~nAlpha ); +} + +// ------------------------------------------------------------------------ + +void PNGReaderImpl::ImplSetAlphaPixel( sal_uInt32 nY, sal_uInt32 nX, + const BitmapColor& rBitmapColor, BYTE nAlpha ) +{ + // TODO: get preview mode checks out of inner loop + if( nX & mnPreviewMask ) + return; + nX >>= mnPreviewShift; + + mpAcc->SetPixel( nY, nX, rBitmapColor ); + mpMaskAcc->SetPixel( 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 |