28 #define NO_PENDING( rStm ) ( ( rStm ).GetError() != ERRCODE_IO_PENDING )
34 GLOBAL_HEADER_READING,
62 sal_uInt64 nAnimationByteSize;
63 sal_uInt64 nAnimationMinFileData;
69 std::vector<sal_uInt8> aSrcBuf;
70 std::unique_ptr<GIFLZWDecompressor> pDecomp;
75 sal_uInt64 nMaxStreamData;
76 sal_uInt32 nLogWidth100;
77 sal_uInt32 nLogHeight100;
79 sal_uInt16 nGlobalWidth;
80 sal_uInt16 nGlobalHeight;
81 sal_uInt16 nImageWidth;
82 sal_uInt16 nImageHeight;
83 sal_uInt16 nImagePosX;
84 sal_uInt16 nImagePosY;
87 sal_uInt16 nLastImageY;
88 sal_uInt16 nLastInterCount;
104 void ClearImageExtensions();
106 bool ReadGlobalHeader();
107 bool ReadExtension();
108 bool ReadLocalHeader();
111 void CreateNewBitmaps();
117 bool ReadIsAnimated();
118 void GetLogicSize(
Size& rLogicSize);
119 Graphic GetIntermediateGraphic();
121 explicit GIFReader(
SvStream& rStm );
126 GIFReader::GIFReader(
SvStream& rStm )
127 : nAnimationByteSize(0)
128 , nAnimationMinFileData(0)
133 , nLastPos ( rStm.Tell() )
134 , nMaxStreamData( rStm.remainingSize() )
136 , nLogHeight100 ( 0 )
138 , nGlobalHeight ( 0 )
146 , nLastInterCount ( 0 )
148 , eActAction ( GLOBAL_HEADER_READING )
150 , bGCTransparent ( false )
151 , bInterlaced ( false)
152 , bOverreadBlock ( false )
153 , bImGraphicReady ( false )
154 , bGlobalPalette ( false )
155 , nBackgroundColor ( 0 )
156 , nGCTransparentIndex ( 0 )
158 , cNonTransIndex1 ( 0 )
160 maUpperName =
"SVIGIF";
162 ClearImageExtensions();
165 void GIFReader::ClearImageExtensions()
167 nGCDisposalMethod = 0;
168 bGCTransparent =
false;
173 bool bWatchForBackgroundColor)
175 const Size aSize(nWidth, nHeight);
177 sal_uInt64 nCombinedPixSize = nWidth * nHeight;
179 nCombinedPixSize += (nCombinedPixSize/8);
186 sal_uInt64 nMinFileData = nWidth * nHeight / 2560;
188 nMinFileData += nAnimationMinFileData;
189 nCombinedPixSize += nAnimationByteSize;
191 if (nMaxStreamData < nMinFileData)
194 SAL_WARN(
"vcl.filter",
"in gif frame index " << aAnimation.Count() <<
" gif claims dimensions " << nWidth <<
" x " << nHeight <<
195 " but filesize of " << nMaxStreamData <<
" is surely insufficiently large to fill all frame images");
206 if (nCombinedPixSize >= SAL_MAX_INT32/3*2)
212 if (!aSize.Width() || !aSize.Height())
220 const Color aWhite(COL_WHITE);
224 if (!aAnimation.Count())
231 cTransIndex1 =
static_cast<sal_uInt8>(pAcc1->GetBestPaletteIndex(aWhite));
232 cNonTransIndex1 = cTransIndex1 ? 0 : 1;
244 if (!aBmp8.IsEmpty() && bWatchForBackgroundColor && aAnimation.Count())
245 aBmp8.Erase((*pPal)[nBackgroundColor]);
247 aBmp8.Erase(COL_WHITE);
250 bStatus = bool(pAcc8);
254 bool GIFReader::ReadGlobalHeader()
259 rIStm.ReadBytes( pBuf, 6 );
263 if( !strcmp( pBuf,
"GIF87a" ) || !strcmp( pBuf,
"GIF89a" ) )
265 rIStm.ReadBytes( pBuf, 7 );
279 bGlobalPalette = ( nRF & 0x80 );
282 ReadPaletteEntries( &aGPalette,
sal_uLong(1) << ( ( nRF & 7 ) + 1 ) );
284 nBackgroundColor = 0;
300 const sal_uInt64 nMaxPossible = rIStm.remainingSize();
301 if (nLen > nMaxPossible)
303 std::unique_ptr<sal_uInt8[]> pBuf(
new sal_uInt8[ nLen ]);
304 std::size_t nRead = rIStm.ReadBytes(pBuf.get(), nLen);
330 bool GIFReader::ReadExtension()
336 rIStm.ReadUChar( cFunction );
339 bool bOverreadDataBlocks =
false;
342 rIStm.ReadUChar( cSize );
349 rIStm.ReadUChar(cFlags);
350 rIStm.ReadUInt16(nTimer);
351 rIStm.ReadUChar(nGCTransparentIndex);
353 rIStm.ReadUChar(cByte);
357 nGCDisposalMethod = ( cFlags >> 2) & 7;
358 bGCTransparent = ( cFlags & 1 );
359 bStatus = ( cSize == 4 ) && ( cByte == 0 );
371 bOverreadDataBlocks =
true;
378 rIStm.ReadUChar( cSize );
381 if( aAppId ==
"NETSCAPE" && aAppCode ==
"2.0" && cSize == 3 )
384 rIStm.ReadUChar( cByte );
389 rIStm.ReadUChar( cByte );
391 rIStm.ReadUChar( cByte );
392 nLoops |= (
static_cast<sal_uInt16
>(cByte) << 8 );
393 rIStm.ReadUChar( cByte );
395 bStatus = ( cByte == 0 );
397 bOverreadDataBlocks =
false;
408 else if ( aAppId ==
"STARDIV " && aAppCode ==
"5.0" && cSize == 9 )
411 rIStm.ReadUChar( cByte );
416 rIStm.ReadUInt32( nLogWidth100 ).ReadUInt32( nLogHeight100 );
417 rIStm.ReadUChar( cByte );
418 bStatus = ( cByte == 0 );
420 bOverreadDataBlocks =
false;
433 bOverreadDataBlocks =
true;
438 if ( bOverreadDataBlocks )
441 while( cSize && bStatus && !rIStm.eof() )
443 sal_uInt16 nCount =
static_cast<sal_uInt16
>(cSize) + 1;
444 const sal_uInt64 nMaxPossible = rIStm.remainingSize();
445 if (nCount > nMaxPossible)
446 nCount = nMaxPossible;
449 rIStm.SeekRel( nCount - 1 );
452 std::size_t nRead = rIStm.ReadBytes(&cSize, 1);
466 bool GIFReader::ReadLocalHeader()
471 std::size_t nRead = rIStm.ReadBytes(pBuf, 9);
486 bInterlaced = ( ( nFlags & 0x40 ) == 0x40 );
493 ReadPaletteEntries( pPal,
sal_uLong(1) << ( (nFlags & 7 ) + 1 ) );
503 CreateBitmaps( nImageWidth, nImageHeight, pPal, bGlobalPalette && ( pPal == &aGPalette ) );
516 rIStm.ReadUChar( cBlockSize );
522 if ( cBlockSize == 0 )
526 rIStm.ReadBytes( aSrcBuf.data(), cBlockSize );
536 sal_uInt8* pTarget = pDecomp->DecompressBlock( aSrcBuf.data(), cBlockSize, nRead, bEOI );
538 nRet = ( bEOI ? 3 : 1 );
540 if( nRead && !bOverreadBlock )
541 FillImages( pTarget, nRead );
543 std::free( pTarget );
556 if( nImageX >= nImageWidth )
563 if( nLastInterCount )
565 tools::Long nMinY = std::min( static_cast<tools::Long>(nLastImageY) + 1, static_cast<tools::Long>(nImageHeight) - 1 );
566 tools::Long nMaxY = std::min( static_cast<tools::Long>(nLastImageY) + nLastInterCount, static_cast<tools::Long>(nImageHeight) - 1 );
570 if( ( nMinY > nLastImageY ) && ( nLastImageY < ( nImageHeight - 1 ) ) )
572 sal_uInt8* pScanline8 = pAcc8->GetScanline( nYAcc );
573 sal_uInt32 nSize8 = pAcc8->GetScanlineSize();
575 sal_uInt32 nSize1 = 0;
579 pScanline1 = pAcc1->GetScanline( nYAcc );
580 nSize1 = pAcc1->GetScanlineSize();
585 memcpy( pAcc8->GetScanline( j ), pScanline8, nSize8 );
588 memcpy( pAcc1->GetScanline( j ), pScanline1, nSize1 );
593 nT1 = ( ++nImageY ) << 3;
596 if( nT1 >= nImageHeight )
598 tools::Long nT2 = nImageY - ( ( nImageHeight + 7 ) >> 3 );
599 nT1 = ( nT2 << 3 ) + 4;
602 if( nT1 >= nImageHeight )
604 nT2 -= ( nImageHeight + 3 ) >> 3;
605 nT1 = ( nT2 << 2 ) + 2;
608 if( nT1 >= nImageHeight )
610 nT2 -= ( nImageHeight + 1 ) >> 2;
611 nT1 = ( nT2 << 1 ) + 1;
617 nLastImageY =
static_cast<sal_uInt16
>(nT1);
622 nLastImageY = ++nImageY;
630 if( nImageY < nImageHeight )
636 if( cTmp == nGCTransparentIndex )
637 pAcc1->SetPixelIndex( nYAcc, nImageX++, cTransIndex1 );
640 pAcc8->SetPixelIndex( nYAcc, nImageX, cTmp );
641 pAcc1->SetPixelIndex( nYAcc, nImageX++, cNonTransIndex1 );
645 pAcc8->SetPixelIndex( nYAcc, nImageX++, cTmp );
649 bOverreadBlock =
true;
655 void GIFReader::CreateNewBitmaps()
676 if (aAnimationBitmap.
mnWait < 2)
677 aAnimationBitmap.
mnWait = 10;
679 if( nGCDisposalMethod == 2 )
681 else if( nGCDisposalMethod == 3 )
687 nAnimationMinFileData +=
static_cast<sal_uInt64
>(nImageWidth) * nImageHeight / 2560;
688 aAnimation.Insert(aAnimationBitmap);
690 if( aAnimation.Count() == 1 )
692 aAnimation.SetDisplaySizePixel(
Size( nGlobalWidth, nGlobalHeight ) );
693 aAnimation.SetLoopCount( nLoops );
697 Graphic GIFReader::GetIntermediateGraphic()
703 if ( bImGraphicReady && !aAnimation.Count() )
707 if ( bGCTransparent )
710 aImGraphic =
BitmapEx( aBmp8, aBmp1 );
713 bStatus = bStatus && pAcc1;
719 bStatus = bStatus && pAcc8;
725 bool GIFReader::ProcessGIF()
731 eActAction = ABORT_READING;
734 rIStm.Seek( nLastPos );
743 rIStm.ReadUChar( cByte );
746 eActAction = END_READING;
752 eActAction = EXTENSION_READING;
753 else if( cByte ==
',' )
754 eActAction = LOCAL_HEADER_READING;
755 else if( cByte ==
';' )
756 eActAction = END_READING;
758 eActAction = ABORT_READING;
764 case GLOBAL_HEADER_READING:
766 bRead = ReadGlobalHeader();
769 ClearImageExtensions();
770 eActAction = MARKER_READING;
776 case EXTENSION_READING:
778 bRead = ReadExtension();
780 eActAction = MARKER_READING;
785 case LOCAL_HEADER_READING:
787 bRead = ReadLocalHeader();
790 nYAcc = nImageX = nImageY = 0;
791 eActAction = FIRST_BLOCK_READING;
797 case FIRST_BLOCK_READING:
801 rIStm.ReadUChar( cDataSize );
804 eActAction = ABORT_READING;
805 else if( cDataSize > 12 )
810 pDecomp = std::make_unique<GIFLZWDecompressor>( cDataSize );
811 eActAction = NEXT_BLOCK_READING;
812 bOverreadBlock =
false;
815 eActAction = FIRST_BLOCK_READING;
820 case NEXT_BLOCK_READING:
822 sal_uInt16 nLastX = nImageX;
823 sal_uInt16 nLastY = nImageY;
833 bImGraphicReady =
true;
834 eActAction = NEXT_BLOCK_READING;
835 bOverreadBlock =
false;
843 eActAction = MARKER_READING;
844 ClearImageExtensions();
848 eActAction = NEXT_BLOCK_READING;
849 bOverreadBlock =
true;
855 eActAction = ABORT_READING;
856 ClearImageExtensions();
872 eActAction = END_READING;
884 nLastPos = rIStm.Tell();
889 bool GIFReader::ReadIsAnimated()
895 while( ProcessGIF() && ( eActAction != END_READING ) ) {}
898 eReadState = GIFREAD_ERROR;
899 else if( eActAction == END_READING )
900 eReadState = GIFREAD_OK;
906 eReadState = GIFREAD_NEED_MORE;
909 if (eReadState == GIFREAD_OK)
910 return aAnimation.Count() > 1;
914 void GIFReader::GetLogicSize(
Size& rLogicSize)
926 while( ProcessGIF() && ( eActAction != END_READING ) ) {}
929 eReadState = GIFREAD_ERROR;
930 else if( eActAction == END_READING )
931 eReadState = GIFREAD_OK;
937 eReadState = GIFREAD_NEED_MORE;
940 if( aAnimation.Count() == 1 )
942 rGraphic = aAnimation.Get(0).maBitmapEx;
944 if( nLogWidth100 && nLogHeight100 )
951 rGraphic = aAnimation;
958 GIFReader aReader(rStm);
962 bool bResult = aReader.ReadIsAnimated();
963 aReader.GetLogicSize(rLogicSize);
973 GIFReader* pGIFReader =
dynamic_cast<GIFReader*
>( pContext.get() );
976 pContext = std::make_shared<GIFReader>( rStm );
977 pGIFReader =
static_cast<GIFReader*
>( pContext.get() );
981 rStm.
SetEndian( SvStreamEndian::LITTLE );
985 ReadState eReadState = pGIFReader->ReadGIF(rGraphic);
987 if (eReadState == GIFREAD_ERROR)
991 else if (eReadState == GIFREAD_NEED_MORE)
993 rGraphic = pGIFReader->GetIntermediateGraphic();
void SetBlue(sal_uInt8 nBlue)
void setWidth(tools::Long nWidth)
SvStream & ReadUInt16(sal_uInt16 &rUInt16)
void SetPrefMapMode(const MapMode &rPrefMapMode)
This template handles BitmapAccess the RAII way.
vcl::ScopedBitmapAccess< BitmapWriteAccess, Bitmap,&Bitmap::AcquireWriteAccess > BitmapScopedWriteAccess
void SetReaderContext(const std::shared_ptr< GraphicReader > &pReader)
VCL_DLLPUBLIC bool ImportGIF(SvStream &rStm, Graphic &rGraphic)
sal_uLong GetSizeBytes() const
void SetRed(sal_uInt8 nRed)
SvStream & ReadUChar(unsigned char &rChar)
#define ANIMATION_TIMEOUT_ON_CLICK
SvStreamEndian GetEndian() const
void SetGreen(sal_uInt8 nGreen)
void SetEndian(SvStreamEndian SvStreamEndian)
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_WHITE
std::shared_ptr< GraphicReader > & GetReaderContext()
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_BLACK
void SetPrefSize(const Size &rPrefSize)
void setHeight(tools::Long nHeight)
#define ERRCODE_IO_PENDING
#define SAL_WARN(area, stream)
void SetBuffer(void *pBuf, std::size_t nSize, std::size_t nEOF)
OString read_uInt8s_ToOString(SvStream &rStrm, std::size_t nLen)
bool IsGIFAnimated(SvStream &rStm, Size &rLogicSize)