tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / vcl / source / filter / igif / gifread.cxx
bloba402faab18711c525ecdaf28c8a6a0997bb809fe
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/log.hxx>
21 #include <tools/stream.hxx>
22 #include "decode.hxx"
23 #include "gifread.hxx"
24 #include <memory>
25 #include <vcl/BitmapWriteAccess.hxx>
27 namespace {
29 enum GIFAction
31 GLOBAL_HEADER_READING,
32 MARKER_READING,
33 EXTENSION_READING,
34 LOCAL_HEADER_READING,
35 FIRST_BLOCK_READING,
36 NEXT_BLOCK_READING,
37 ABORT_READING,
38 END_READING
41 enum ReadState
43 GIFREAD_OK,
44 GIFREAD_ERROR
49 class GIFLZWDecompressor;
51 class SvStream;
53 namespace {
55 class GIFReader
57 Animation aAnimation;
58 sal_uInt64 nAnimationByteSize;
59 sal_uInt64 nAnimationMinFileData;
60 Bitmap aBmp8;
61 Bitmap aBmp1;
62 BitmapPalette aGPalette;
63 BitmapPalette aLPalette;
64 SvStream& rIStm;
65 std::vector<sal_uInt8> aSrcBuf;
66 std::unique_ptr<GIFLZWDecompressor> pDecomp;
67 BitmapScopedWriteAccess pAcc8;
68 BitmapScopedWriteAccess pAcc1;
69 tools::Long nYAcc;
70 tools::Long nLastPos;
71 sal_uInt64 nMaxStreamData;
72 sal_uInt32 nLogWidth100;
73 sal_uInt32 nLogHeight100;
74 sal_uInt16 nTimer;
75 sal_uInt16 nGlobalWidth; // maximum imagewidth from header
76 sal_uInt16 nGlobalHeight; // maximum imageheight from header
77 sal_uInt16 nImageWidth; // maximum screenwidth from header
78 sal_uInt16 nImageHeight; // maximum screenheight from header
79 sal_uInt16 nImagePosX;
80 sal_uInt16 nImagePosY;
81 sal_uInt16 nImageX; // maximum screenwidth from header
82 sal_uInt16 nImageY; // maximum screenheight from header
83 sal_uInt16 nLastImageY;
84 sal_uInt16 nLastInterCount;
85 sal_uInt16 nLoops;
86 GIFAction eActAction;
87 bool bStatus;
88 bool bGCTransparent; // is the image transparent, if yes:
89 bool bInterlaced;
90 bool bOverreadBlock;
91 bool bGlobalPalette;
92 sal_uInt8 nBackgroundColor; // backgroundcolour
93 sal_uInt8 nGCTransparentIndex; // pixels of this index are transparent
94 sal_uInt8 nGCDisposalMethod; // 'Disposal Method' (see GIF docs)
95 sal_uInt8 cTransIndex1;
96 sal_uInt8 cNonTransIndex1;
97 sal_uLong nPaletteSize;
99 void ReadPaletteEntries( BitmapPalette* pPal, sal_uLong nCount );
100 void ClearImageExtensions();
101 void CreateBitmaps( tools::Long nWidth, tools::Long nHeight, BitmapPalette* pPal, bool bWatchForBackgroundColor );
102 bool ReadGlobalHeader();
103 bool ReadExtension();
104 bool ReadLocalHeader();
105 sal_uLong ReadNextBlock();
106 void FillImages( const sal_uInt8* pBytes, sal_uLong nCount );
107 void CreateNewBitmaps();
108 bool ProcessGIF();
110 public:
112 ReadState ReadGIF( Graphic& rGraphic );
113 bool ReadIsAnimated();
114 void GetLogicSize(Size& rLogicSize);
116 explicit GIFReader( SvStream& rStm );
121 GIFReader::GIFReader( SvStream& rStm )
122 : nAnimationByteSize(0)
123 , nAnimationMinFileData(0)
124 , aGPalette ( 256 )
125 , aLPalette ( 256 )
126 , rIStm ( rStm )
127 , nYAcc ( 0 )
128 , nLastPos ( rStm.Tell() )
129 , nMaxStreamData( rStm.remainingSize() )
130 , nLogWidth100 ( 0 )
131 , nLogHeight100 ( 0 )
132 , nGlobalWidth ( 0 )
133 , nGlobalHeight ( 0 )
134 , nImageWidth ( 0 )
135 , nImageHeight ( 0 )
136 , nImagePosX ( 0 )
137 , nImagePosY ( 0 )
138 , nImageX ( 0 )
139 , nImageY ( 0 )
140 , nLastImageY ( 0 )
141 , nLastInterCount ( 0 )
142 , nLoops ( 1 )
143 , eActAction ( GLOBAL_HEADER_READING )
144 , bStatus ( false )
145 , bGCTransparent ( false )
146 , bInterlaced ( false)
147 , bOverreadBlock ( false )
148 , bGlobalPalette ( false )
149 , nBackgroundColor ( 0 )
150 , nGCTransparentIndex ( 0 )
151 , cTransIndex1 ( 0 )
152 , cNonTransIndex1 ( 0 )
153 , nPaletteSize( 0 )
155 aSrcBuf.resize(256); // Memory buffer for ReadNextBlock
156 ClearImageExtensions();
159 void GIFReader::ClearImageExtensions()
161 nGCDisposalMethod = 0;
162 bGCTransparent = false;
163 nTimer = 0;
166 void GIFReader::CreateBitmaps(tools::Long nWidth, tools::Long nHeight, BitmapPalette* pPal,
167 bool bWatchForBackgroundColor)
169 const Size aSize(nWidth, nHeight);
171 sal_uInt64 nCombinedPixSize = nWidth * nHeight;
172 if (bGCTransparent)
173 nCombinedPixSize += (nCombinedPixSize/8);
175 // "Overall data compression asymptotically approaches 3839 × 8 / 12 = 2559 1/3"
176 // so assume compression of 1:2560 is possible
177 // (http://cloudinary.com/blog/a_one_color_image_is_worth_two_thousand_words suggests
178 // 1:1472.88 [184.11 x 8] is more realistic)
180 sal_uInt64 nMinFileData = nWidth * nHeight / 2560;
182 nMinFileData += nAnimationMinFileData;
183 nCombinedPixSize += nAnimationByteSize;
185 if (nMaxStreamData < nMinFileData)
187 //there is nowhere near enough data in this stream to fill the claimed dimensions
188 SAL_WARN("vcl.filter", "in gif frame index " << aAnimation.Count() << " gif claims dimensions " << nWidth << " x " << nHeight <<
189 " but filesize of " << nMaxStreamData << " is surely insufficiently large to fill all frame images");
190 bStatus = false;
191 return;
194 // Don't bother allocating a bitmap of a size that would fail on a
195 // 32-bit system. We have at least one unit tests that is expected
196 // to fail (loading a 65535*65535 size GIF
197 // svtools/qa/cppunit/data/gif/fail/CVE-2008-5937-1.gif), but
198 // which doesn't fail on 64-bit macOS at least. Why the loading
199 // fails on 64-bit Linux, no idea.
200 if (nCombinedPixSize >= SAL_MAX_INT32/3*2)
202 bStatus = false;
203 return;
206 if (!aSize.Width() || !aSize.Height())
208 bStatus = false;
209 return;
212 if (bGCTransparent)
214 const Color aWhite(COL_WHITE);
215 const Color aBlack(COL_BLACK);
217 aBmp1 = Bitmap(aSize, vcl::PixelFormat::N8_BPP, &Bitmap::GetGreyPalette(256));
219 if (!aAnimation.Count())
220 aBmp1.Erase(aBlack);
222 pAcc1 = aBmp1;
224 if (pAcc1)
226 // We have to make an AlphaMask from it, that needs to be inverted from transparency.
227 // It is faster to invert it here.
228 // So Non-Transparent color should be 0xff , and Transparent should be 0.
229 cNonTransIndex1 = static_cast<sal_uInt8>(pAcc1->GetBestPaletteIndex(aWhite));
230 cTransIndex1 = static_cast<sal_uInt8>(pAcc1->GetBestPaletteIndex(aBlack));
232 else
234 bStatus = false;
238 if (bStatus)
240 aBmp8 = Bitmap(aSize, vcl::PixelFormat::N8_BPP, pPal);
242 if (!aBmp8.IsEmpty() && bWatchForBackgroundColor && aAnimation.Count())
243 aBmp8.Erase((*pPal)[nBackgroundColor]);
244 else
245 aBmp8.Erase(COL_WHITE);
247 pAcc8 = aBmp8;
248 bStatus = bool(pAcc8);
252 bool GIFReader::ReadGlobalHeader()
254 char pBuf[ 7 ];
255 bool bRet = false;
257 auto nRead = rIStm.ReadBytes(pBuf, 6);
258 if (nRead == 6 && rIStm.good())
260 pBuf[ 6 ] = 0;
261 if( !strcmp( pBuf, "GIF87a" ) || !strcmp( pBuf, "GIF89a" ) )
263 nRead = rIStm.ReadBytes(pBuf, 7);
264 if (nRead == 7 && rIStm.good())
266 sal_uInt8 nAspect;
267 sal_uInt8 nRF;
268 SvMemoryStream aMemStm;
270 aMemStm.SetBuffer( pBuf, 7, 7 );
271 aMemStm.ReadUInt16( nGlobalWidth );
272 aMemStm.ReadUInt16( nGlobalHeight );
273 aMemStm.ReadUChar( nRF );
274 aMemStm.ReadUChar( nBackgroundColor );
275 aMemStm.ReadUChar( nAspect );
277 bGlobalPalette = ( nRF & 0x80 );
279 if( bGlobalPalette )
280 ReadPaletteEntries( &aGPalette, sal_uLong(1) << ( ( nRF & 7 ) + 1 ) );
281 else
282 nBackgroundColor = 0;
284 if (rIStm.good())
285 bRet = true;
288 else
289 bStatus = false;
292 return bRet;
295 void GIFReader::ReadPaletteEntries( BitmapPalette* pPal, sal_uLong nCount )
297 sal_uLong nLen = 3 * nCount;
298 const sal_uInt64 nMaxPossible = rIStm.remainingSize();
299 if (nLen > nMaxPossible)
300 nLen = nMaxPossible;
301 std::unique_ptr<sal_uInt8[]> pBuf(new sal_uInt8[ nLen ]);
302 std::size_t nRead = rIStm.ReadBytes(pBuf.get(), nLen);
303 nCount = nRead/3UL;
304 if (!rIStm.good())
305 return;
307 sal_uInt8* pTmp = pBuf.get();
309 for (sal_uLong i = 0; i < nCount; ++i)
311 BitmapColor& rColor = (*pPal)[i];
313 rColor.SetRed( *pTmp++ );
314 rColor.SetGreen( *pTmp++ );
315 rColor.SetBlue( *pTmp++ );
318 // if possible accommodate some standard colours
319 if( nCount < 256 )
321 (*pPal)[ 255UL ] = COL_WHITE;
323 if( nCount < 255 )
324 (*pPal)[ 254UL ] = COL_BLACK;
327 nPaletteSize = nCount;
330 bool GIFReader::ReadExtension()
332 bool bRet = false;
334 // Extension-Label
335 sal_uInt8 cFunction(0);
336 rIStm.ReadUChar( cFunction );
337 if (rIStm.good())
339 bool bOverreadDataBlocks = false;
340 sal_uInt8 cSize(0);
341 // Block length
342 rIStm.ReadUChar( cSize );
343 switch( cFunction )
345 // 'Graphic Control Extension'
346 case 0xf9 :
348 sal_uInt8 cFlags(0);
349 rIStm.ReadUChar(cFlags);
350 rIStm.ReadUInt16(nTimer);
351 rIStm.ReadUChar(nGCTransparentIndex);
352 sal_uInt8 cByte(0);
353 rIStm.ReadUChar(cByte);
355 if (rIStm.good())
357 nGCDisposalMethod = ( cFlags >> 2) & 7;
358 bGCTransparent = ( cFlags & 1 );
359 bStatus = ( cSize == 4 ) && ( cByte == 0 );
360 bRet = true;
363 break;
365 // Application extension
366 case 0xff :
368 if (rIStm.good())
370 // by default overread this extension
371 bOverreadDataBlocks = true;
373 // Appl. extension has length 11
374 if ( cSize == 0x0b )
376 OString aAppId = read_uInt8s_ToOString(rIStm, 8);
377 OString aAppCode = read_uInt8s_ToOString(rIStm, 3);
378 rIStm.ReadUChar( cSize );
380 // NetScape-Extension
381 if( aAppId == "NETSCAPE" && aAppCode == "2.0" && cSize == 3 )
383 sal_uInt8 cByte(0);
384 rIStm.ReadUChar( cByte );
386 // Loop-Extension
387 if ( cByte == 0x01 )
389 rIStm.ReadUChar( cByte );
390 nLoops = cByte;
391 rIStm.ReadUChar( cByte );
392 nLoops |= ( static_cast<sal_uInt16>(cByte) << 8 );
393 rIStm.ReadUChar( cByte );
395 bStatus = ( cByte == 0 );
396 bRet = rIStm.good();
397 bOverreadDataBlocks = false;
399 // Netscape interprets the loop count
400 // as pure number of _repeats_;
401 // here it is the total number of loops
402 if( nLoops )
403 nLoops++;
405 else
406 rIStm.SeekRel( -1 );
408 else if ( aAppId == "STARDIV " && aAppCode == "5.0" && cSize == 9 )
410 sal_uInt8 cByte(0);
411 rIStm.ReadUChar( cByte );
413 // Loop extension
414 if ( cByte == 0x01 )
416 rIStm.ReadUInt32( nLogWidth100 ).ReadUInt32( nLogHeight100 );
417 rIStm.ReadUChar( cByte );
418 bStatus = ( cByte == 0 );
419 bRet = rIStm.good();
420 bOverreadDataBlocks = false;
422 else
423 rIStm.SeekRel( -1 );
429 break;
431 // overread everything else
432 default:
433 bOverreadDataBlocks = true;
434 break;
437 // overread sub-blocks
438 if ( bOverreadDataBlocks )
440 bRet = true;
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;
448 if (nCount)
449 rIStm.SeekRel( nCount - 1 ); // Skip subblock data
451 bRet = false;
452 std::size_t nRead = rIStm.ReadBytes(&cSize, 1);
453 if (rIStm.good() && nRead == 1)
455 bRet = true;
457 else
458 cSize = 0;
463 return bRet;
466 bool GIFReader::ReadLocalHeader()
468 sal_uInt8 pBuf[ 9 ];
469 bool bRet = false;
471 std::size_t nRead = rIStm.ReadBytes(pBuf, 9);
472 if (rIStm.good() && nRead == 9)
474 SvMemoryStream aMemStm;
475 BitmapPalette* pPal;
477 aMemStm.SetBuffer( pBuf, 9, 9 );
478 aMemStm.ReadUInt16( nImagePosX );
479 aMemStm.ReadUInt16( nImagePosY );
480 aMemStm.ReadUInt16( nImageWidth );
481 aMemStm.ReadUInt16( nImageHeight );
482 sal_uInt8 nFlags(0);
483 aMemStm.ReadUChar(nFlags);
485 // if interlaced, first define startvalue
486 bInterlaced = ( ( nFlags & 0x40 ) == 0x40 );
487 nLastInterCount = 7;
488 nLastImageY = 0;
490 if( nFlags & 0x80 )
492 pPal = &aLPalette;
493 ReadPaletteEntries( pPal, sal_uLong(1) << ( (nFlags & 7 ) + 1 ) );
495 else
496 pPal = &aGPalette;
498 // if we could read everything, we will create the local image;
499 // if the global colour table is valid for the image, we will
500 // consider the BackGroundColorIndex.
501 if (rIStm.good())
503 CreateBitmaps( nImageWidth, nImageHeight, pPal, bGlobalPalette && ( pPal == &aGPalette ) );
504 bRet = true;
508 return bRet;
511 sal_uLong GIFReader::ReadNextBlock()
513 sal_uLong nRet = 0;
514 sal_uInt8 cBlockSize;
516 rIStm.ReadUChar( cBlockSize );
518 if ( rIStm.eof() )
519 nRet = 4;
520 else if (rIStm.good())
522 if ( cBlockSize == 0 )
523 nRet = 2;
524 else
526 rIStm.ReadBytes( aSrcBuf.data(), cBlockSize );
528 if (rIStm.good())
530 if( bOverreadBlock )
531 nRet = 3;
532 else
534 bool bEOI;
535 sal_uLong nRead;
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 );
549 return nRet;
552 void GIFReader::FillImages( const sal_uInt8* pBytes, sal_uLong nCount )
554 for( sal_uLong i = 0; i < nCount; i++ )
556 if( nImageX >= nImageWidth )
558 if( bInterlaced )
560 tools::Long nT1;
562 // lines will be copied if interlaced
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 );
568 // copy last line read, if lines do not coincide
569 // ( happens at the end of the image )
570 if( ( nMinY > nLastImageY ) && ( nLastImageY < ( nImageHeight - 1 ) ) )
572 sal_uInt8* pScanline8 = pAcc8->GetScanline( nYAcc );
573 sal_uInt32 nSize8 = pAcc8->GetScanlineSize();
574 sal_uInt8* pScanline1 = nullptr;
575 sal_uInt32 nSize1 = 0;
577 if( bGCTransparent )
579 pScanline1 = pAcc1->GetScanline( nYAcc );
580 nSize1 = pAcc1->GetScanlineSize();
583 for( tools::Long j = nMinY; j <= nMaxY; j++ )
585 memcpy( pAcc8->GetScanline( j ), pScanline8, nSize8 );
587 if( bGCTransparent )
588 memcpy( pAcc1->GetScanline( j ), pScanline1, nSize1 );
593 nT1 = ( ++nImageY ) << 3;
594 nLastInterCount = 7;
596 if( nT1 >= nImageHeight )
598 tools::Long nT2 = nImageY - ( ( nImageHeight + 7 ) >> 3 );
599 nT1 = ( nT2 << 3 ) + 4;
600 nLastInterCount = 3;
602 if( nT1 >= nImageHeight )
604 nT2 -= ( nImageHeight + 3 ) >> 3;
605 nT1 = ( nT2 << 2 ) + 2;
606 nLastInterCount = 1;
608 if( nT1 >= nImageHeight )
610 nT2 -= ( nImageHeight + 1 ) >> 2;
611 nT1 = ( nT2 << 1 ) + 1;
612 nLastInterCount = 0;
617 nLastImageY = static_cast<sal_uInt16>(nT1);
618 nYAcc = nT1;
620 else
622 nLastImageY = ++nImageY;
623 nYAcc = nImageY;
626 // line starts from the beginning
627 nImageX = 0;
630 if( nImageY < nImageHeight )
632 const sal_uInt8 cTmp = pBytes[ i ];
634 if( bGCTransparent )
636 if( cTmp == nGCTransparentIndex )
637 pAcc1->SetPixelIndex( nYAcc, nImageX++, cTransIndex1 );
638 else
640 pAcc8->SetPixelIndex( nYAcc, nImageX, cTmp );
641 pAcc1->SetPixelIndex( nYAcc, nImageX++, cNonTransIndex1 );
644 else
645 pAcc8->SetPixelIndex( nYAcc, nImageX++, cTmp );
647 else
649 bOverreadBlock = true;
650 break;
655 void GIFReader::CreateNewBitmaps()
657 AnimationFrame aAnimationFrame;
659 pAcc8.reset();
661 if( bGCTransparent )
663 pAcc1.reset();
664 AlphaMask aAlphaMask(aBmp1);
665 // No need to convert from transparency to alpha
666 // aBmp1 is already inverted
667 aAnimationFrame.maBitmapEx = BitmapEx( aBmp8, aAlphaMask );
669 else if( nPaletteSize > 2 )
671 // tdf#160690 set an opaque alpha mask for non-transparent frames
672 // Due to the switch from transparency to alpha in commit
673 // 81994cb2b8b32453a92bcb011830fcb884f22ff3, an empty alpha mask
674 // is treated as a completely transparent bitmap. So revert all
675 // of the previous commits for tdf#157576, tdf#157635, and tdf#157793
676 // and create a completely opaque bitmap instead.
677 // Note: this fix also fixes tdf#157576, tdf#157635, and tdf#157793.
678 AlphaMask aAlphaMask(aBmp8.GetSizePixel());
679 aAnimationFrame.maBitmapEx = BitmapEx( aBmp8, aAlphaMask );
681 else
683 // Don't apply the fix for tdf#160690 as it will cause 1 bit bitmaps
684 // in Word documents like the following test document to fail to be
685 // parsed correctly:
686 // sw/qa/extras/tiledrendering/data/tdf159626_yellowPatternFill.docx
687 aAnimationFrame.maBitmapEx = BitmapEx( aBmp8 );
690 aAnimationFrame.maPositionPixel = Point( nImagePosX, nImagePosY );
691 aAnimationFrame.maSizePixel = Size( nImageWidth, nImageHeight );
692 aAnimationFrame.mnWait = ( nTimer != 65535 ) ? nTimer : ANIMATION_TIMEOUT_ON_CLICK;
693 aAnimationFrame.mbUserInput = false;
695 // tdf#104121 . Internet Explorer, Firefox, Chrome and Safari all set a minimum default playback speed.
696 // IE10 Consumer Preview sets default of 100ms for rates less that 20ms. We do the same
697 if (aAnimationFrame.mnWait < 2) // 20ms, specified in 100's of a second
698 aAnimationFrame.mnWait = 10;
700 if( nGCDisposalMethod == 2 )
701 aAnimationFrame.meDisposal = Disposal::Back;
702 else if( nGCDisposalMethod == 3 )
703 aAnimationFrame.meDisposal = Disposal::Previous;
704 else
705 aAnimationFrame.meDisposal = Disposal::Not;
707 nAnimationByteSize += aAnimationFrame.maBitmapEx.GetSizeBytes();
708 nAnimationMinFileData += static_cast<sal_uInt64>(nImageWidth) * nImageHeight / 2560;
709 aAnimation.Insert(aAnimationFrame);
711 if( aAnimation.Count() == 1 )
713 aAnimation.SetDisplaySizePixel( Size( nGlobalWidth, nGlobalHeight ) );
714 aAnimation.SetLoopCount( nLoops );
718 bool GIFReader::ProcessGIF()
720 bool bRead = false;
721 bool bEnd = false;
723 if ( !bStatus )
724 eActAction = ABORT_READING;
726 // set stream to right position
727 rIStm.Seek( nLastPos );
729 switch( eActAction )
731 // read next marker
732 case MARKER_READING:
734 sal_uInt8 cByte;
736 rIStm.ReadUChar( cByte );
738 if( rIStm.eof() )
739 eActAction = END_READING;
740 else if (rIStm.good())
742 bRead = true;
744 if( cByte == '!' )
745 eActAction = EXTENSION_READING;
746 else if( cByte == ',' )
747 eActAction = LOCAL_HEADER_READING;
748 else if( cByte == ';' )
749 eActAction = END_READING;
750 else
751 eActAction = ABORT_READING;
754 break;
756 // read ScreenDescriptor
757 case GLOBAL_HEADER_READING:
759 bRead = ReadGlobalHeader();
760 if( bRead )
762 ClearImageExtensions();
763 eActAction = MARKER_READING;
766 break;
768 // read extension
769 case EXTENSION_READING:
771 bRead = ReadExtension();
772 if( bRead )
773 eActAction = MARKER_READING;
775 break;
777 // read Image-Descriptor
778 case LOCAL_HEADER_READING:
780 bRead = ReadLocalHeader();
781 if( bRead )
783 nYAcc = nImageX = nImageY = 0;
784 eActAction = FIRST_BLOCK_READING;
787 break;
789 // read first data block
790 case FIRST_BLOCK_READING:
792 sal_uInt8 cDataSize;
794 rIStm.ReadUChar( cDataSize );
796 if( rIStm.eof() )
797 eActAction = ABORT_READING;
798 else if( cDataSize > 12 )
799 bStatus = false;
800 else if (rIStm.good())
802 bRead = true;
803 pDecomp = std::make_unique<GIFLZWDecompressor>( cDataSize );
804 eActAction = NEXT_BLOCK_READING;
805 bOverreadBlock = false;
807 else
808 eActAction = FIRST_BLOCK_READING;
810 break;
812 // read next data block
813 case NEXT_BLOCK_READING:
815 sal_uInt16 nLastX = nImageX;
816 sal_uInt16 nLastY = nImageY;
817 sal_uLong nRet = ReadNextBlock();
819 // Return: 0:Pending / 1:OK; / 2:OK and last block: / 3:EOI / 4:HardAbort
820 if( nRet )
822 bRead = true;
824 if ( nRet == 1 )
826 eActAction = NEXT_BLOCK_READING;
827 bOverreadBlock = false;
829 else
831 if( nRet == 2 )
833 pDecomp.reset();
834 CreateNewBitmaps();
835 eActAction = MARKER_READING;
836 ClearImageExtensions();
838 else if( nRet == 3 )
840 eActAction = NEXT_BLOCK_READING;
841 bOverreadBlock = true;
843 else
845 pDecomp.reset();
846 CreateNewBitmaps();
847 eActAction = ABORT_READING;
848 ClearImageExtensions();
852 else
854 nImageX = nLastX;
855 nImageY = nLastY;
858 break;
860 // an error occurred
861 case ABORT_READING:
863 bEnd = true;
864 eActAction = END_READING;
866 break;
868 default:
869 break;
872 // set stream to right position,
873 // if data could be read put it at the old
874 // position otherwise at the actual one
875 if( bRead || bEnd )
876 nLastPos = rIStm.Tell();
878 return bRead;
881 bool GIFReader::ReadIsAnimated()
883 bStatus = true;
884 while (ProcessGIF() && eActAction != END_READING)
887 ReadState eReadState = GIFREAD_ERROR;
889 if (!bStatus)
890 eReadState = GIFREAD_ERROR;
891 else if (eActAction == END_READING)
892 eReadState = GIFREAD_OK;
894 if (eReadState == GIFREAD_OK)
895 return aAnimation.Count() > 1;
896 return false;
899 void GIFReader::GetLogicSize(Size& rLogicSize)
901 rLogicSize.setWidth(nLogWidth100);
902 rLogicSize.setHeight(nLogHeight100);
905 ReadState GIFReader::ReadGIF(Graphic& rGraphic)
907 bStatus = true;
909 while (ProcessGIF() && eActAction != END_READING)
912 ReadState eReadState = GIFREAD_ERROR;
914 if (!bStatus)
915 eReadState = GIFREAD_ERROR;
916 else if (eActAction == END_READING)
917 eReadState = GIFREAD_OK;
919 if (aAnimation.Count() == 1)
921 rGraphic = aAnimation.Get(0).maBitmapEx;
923 if( nLogWidth100 && nLogHeight100 )
925 rGraphic.SetPrefSize( Size( nLogWidth100, nLogHeight100 ) );
926 rGraphic.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
929 else
930 rGraphic = aAnimation;
932 return eReadState;
935 bool IsGIFAnimated(SvStream& rStream, Size& rLogicSize)
937 GIFReader aReader(rStream);
939 SvStreamEndian nOldFormat = rStream.GetEndian();
940 rStream.SetEndian(SvStreamEndian::LITTLE);
941 bool bResult = aReader.ReadIsAnimated();
942 aReader.GetLogicSize(rLogicSize);
943 rStream.SetEndian(nOldFormat);
945 return bResult;
948 VCL_DLLPUBLIC bool ImportGIF(SvStream & rStream, Graphic& rGraphic)
950 bool bReturn = false;
951 GIFReader aGIFReader(rStream);
953 SvStreamEndian nOldFormat = rStream.GetEndian();
954 rStream.SetEndian(SvStreamEndian::LITTLE);
956 ReadState eReadState = aGIFReader.ReadGIF(rGraphic);
958 if (eReadState == GIFREAD_OK)
959 bReturn = true;
961 rStream.SetEndian(nOldFormat);
963 return bReturn;
966 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */