LibreOffice Module vcl (master)  1
gifread.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  * Licensed to the Apache Software Foundation (ASF) under one or more
12  * contributor license agreements. See the NOTICE file distributed
13  * with this work for additional information regarding copyright
14  * ownership. The ASF licenses this file to you under the Apache
15  * License, Version 2.0 (the "License"); you may not use this file
16  * except in compliance with the License. You may obtain a copy of
17  * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <sal/log.hxx>
21 #include <tools/stream.hxx>
22 #include "decode.hxx"
23 #include "gifread.hxx"
24 #include <memory>
25 #include <bitmapwriteaccess.hxx>
26 
27 #define NO_PENDING( rStm ) ( ( rStm ).GetError() != ERRCODE_IO_PENDING )
28 
30 {
39 };
40 
42 {
46 };
47 
48 class GIFLZWDecompressor;
49 
50 class SvStream;
51 
52 class GIFReader : public GraphicReader
53 {
55  sal_uInt64 nAnimationByteSize;
62  std::vector<sal_uInt8> aSrcBuf;
63  std::unique_ptr<GIFLZWDecompressor> pDecomp;
66  long nYAcc;
67  long nLastPos;
68  sal_uInt64 nMaxStreamData;
69  sal_uInt32 nLogWidth100;
70  sal_uInt32 nLogHeight100;
71  sal_uInt16 nTimer;
72  sal_uInt16 nGlobalWidth; // maximum imagewidth from header
73  sal_uInt16 nGlobalHeight; // maximum imageheight from header
74  sal_uInt16 nImageWidth; // maximum screenwidth from header
75  sal_uInt16 nImageHeight; // maximum screenheight from header
76  sal_uInt16 nImagePosX;
77  sal_uInt16 nImagePosY;
78  sal_uInt16 nImageX; // maximum screenwidth from header
79  sal_uInt16 nImageY; // maximum screenheight from header
80  sal_uInt16 nLastImageY;
81  sal_uInt16 nLastInterCount;
82  sal_uInt16 nLoops;
84  bool bStatus;
85  bool bGCTransparent; // is the image transparent, if yes:
90  sal_uInt8 nBackgroundColor; // backgroundcolour
91  sal_uInt8 nGCTransparentIndex; // pixels of this index are transparent
92  sal_uInt8 nGCDisposalMethod; // 'Disposal Method' (see GIF docs)
95 
96  void ReadPaletteEntries( BitmapPalette* pPal, sal_uLong nCount );
97  void ClearImageExtensions();
98  void CreateBitmaps( long nWidth, long nHeight, BitmapPalette* pPal, bool bWatchForBackgroundColor );
99  bool ReadGlobalHeader();
100  bool ReadExtension();
101  bool ReadLocalHeader();
103  void FillImages( const sal_uInt8* pBytes, sal_uLong nCount );
104  void CreateNewBitmaps();
105  bool ProcessGIF();
106 
107 public:
108 
109  ReadState ReadGIF( Graphic& rGraphic );
110  bool ReadIsAnimated();
112 
113  explicit GIFReader( SvStream& rStm );
114 };
115 
117  : nAnimationByteSize(0)
118  , nAnimationMinFileData(0)
119  , aGPalette ( 256 )
120  , aLPalette ( 256 )
121  , rIStm ( rStm )
122  , nYAcc ( 0 )
123  , nLastPos ( rStm.Tell() )
124  , nMaxStreamData( rStm.remainingSize() )
125  , nLogWidth100 ( 0 )
126  , nLogHeight100 ( 0 )
127  , nGlobalWidth ( 0 )
128  , nGlobalHeight ( 0 )
129  , nImageWidth ( 0 )
130  , nImageHeight ( 0 )
131  , nImagePosX ( 0 )
132  , nImagePosY ( 0 )
133  , nImageX ( 0 )
134  , nImageY ( 0 )
135  , nLastImageY ( 0 )
136  , nLastInterCount ( 0 )
137  , nLoops ( 1 )
138  , eActAction ( GLOBAL_HEADER_READING )
139  , bStatus ( false )
140  , bGCTransparent ( false )
141  , bInterlaced ( false)
142  , bOverreadBlock ( false )
143  , bImGraphicReady ( false )
144  , bGlobalPalette ( false )
145  , nBackgroundColor ( 0 )
146  , nGCTransparentIndex ( 0 )
147  , cTransIndex1 ( 0 )
148  , cNonTransIndex1 ( 0 )
149 {
150  maUpperName = "SVIGIF";
151  aSrcBuf.resize(256); // Memory buffer for ReadNextBlock
153 }
154 
156 {
157  nGCDisposalMethod = 0;
158  bGCTransparent = false;
159  nTimer = 0;
160 }
161 
162 void GIFReader::CreateBitmaps(long nWidth, long nHeight, BitmapPalette* pPal,
163  bool bWatchForBackgroundColor)
164 {
165  const Size aSize(nWidth, nHeight);
166 
167  sal_uInt64 nCombinedPixSize = nWidth * nHeight;
168  if (bGCTransparent)
169  nCombinedPixSize += (nCombinedPixSize/8);
170 
171  // "Overall data compression asymptotically approaches 3839 × 8 / 12 = 2559 1/3"
172  // so assume compression of 1:2560 is possible
173  // (http://cloudinary.com/blog/a_one_color_image_is_worth_two_thousand_words suggests
174  // 1:1472.88 [184.11 x 8] is more realistic)
175 
176  sal_uInt64 nMinFileData = nWidth * nHeight / 2560;
177 
178  nMinFileData += nAnimationMinFileData;
179  nCombinedPixSize += nAnimationByteSize;
180 
181  if (nMaxStreamData < nMinFileData)
182  {
183  //there is nowhere near enough data in this stream to fill the claimed dimensions
184  SAL_WARN("vcl.filter", "in gif frame index " << aAnimation.Count() << " gif claims dimensions " << nWidth << " x " << nHeight <<
185  " but filesize of " << nMaxStreamData << " is surely insufficiently large to fill all frame images");
186  bStatus = false;
187  return;
188  }
189 
190  // Don't bother allocating a bitmap of a size that would fail on a
191  // 32-bit system. We have at least one unit tests that is expected
192  // to fail (loading a 65535*65535 size GIF
193  // svtools/qa/cppunit/data/gif/fail/CVE-2008-5937-1.gif), but
194  // which doesn't fail on 64-bit macOS at least. Why the loading
195  // fails on 64-bit Linux, no idea.
196  if (nCombinedPixSize >= SAL_MAX_INT32/3*2)
197  {
198  bStatus = false;
199  return;
200  }
201 
202  if (!aSize.Width() || !aSize.Height())
203  {
204  bStatus = false;
205  return;
206  }
207 
208  if (bGCTransparent)
209  {
210  const Color aWhite(COL_WHITE);
211 
212  aBmp1 = Bitmap(aSize, 1);
213 
214  if (!aAnimation.Count())
215  aBmp1.Erase(aWhite);
216 
218 
219  if (pAcc1)
220  {
221  cTransIndex1 = static_cast<sal_uInt8>(pAcc1->GetBestPaletteIndex(aWhite));
222  cNonTransIndex1 = cTransIndex1 ? 0 : 1;
223  }
224  else
225  {
226  bStatus = false;
227  }
228  }
229 
230  if (bStatus)
231  {
232  aBmp8 = Bitmap(aSize, 8, pPal);
233 
234  if (!!aBmp8 && bWatchForBackgroundColor && aAnimation.Count())
235  aBmp8.Erase((*pPal)[nBackgroundColor]);
236  else
238 
240  bStatus = bool(pAcc8);
241  }
242 }
243 
245 {
246  char pBuf[ 7 ];
247  sal_uInt8 nRF;
248  sal_uInt8 nAspect;
249  bool bRet = false;
250 
251  rIStm.ReadBytes( pBuf, 6 );
252  if( NO_PENDING( rIStm ) )
253  {
254  pBuf[ 6 ] = 0;
255  if( !strcmp( pBuf, "GIF87a" ) || !strcmp( pBuf, "GIF89a" ) )
256  {
257  rIStm.ReadBytes( pBuf, 7 );
258  if( NO_PENDING( rIStm ) )
259  {
260  SvMemoryStream aMemStm;
261 
262  aMemStm.SetBuffer( pBuf, 7, 7 );
263  aMemStm.ReadUInt16( nGlobalWidth );
264  aMemStm.ReadUInt16( nGlobalHeight );
265  aMemStm.ReadUChar( nRF );
266  aMemStm.ReadUChar( nBackgroundColor );
267  aMemStm.ReadUChar( nAspect );
268 
269  bGlobalPalette = ( nRF & 0x80 );
270 
271  if( bGlobalPalette )
272  ReadPaletteEntries( &aGPalette, sal_uLong(1) << ( ( nRF & 7 ) + 1 ) );
273  else
274  nBackgroundColor = 0;
275 
276  if( NO_PENDING( rIStm ) )
277  bRet = true;
278  }
279  }
280  else
281  bStatus = false;
282  }
283 
284  return bRet;
285 }
286 
288 {
289  sal_uLong nLen = 3 * nCount;
290  const sal_uInt64 nMaxPossible = rIStm.remainingSize();
291  if (nLen > nMaxPossible)
292  nLen = nMaxPossible;
293  std::unique_ptr<sal_uInt8[]> pBuf(new sal_uInt8[ nLen ]);
294  std::size_t nRead = rIStm.ReadBytes(pBuf.get(), nLen);
295  nCount = nRead/3UL;
296  if( NO_PENDING( rIStm ) )
297  {
298  sal_uInt8* pTmp = pBuf.get();
299 
300  for (sal_uLong i = 0; i < nCount; ++i)
301  {
302  BitmapColor& rColor = (*pPal)[i];
303 
304  rColor.SetRed( *pTmp++ );
305  rColor.SetGreen( *pTmp++ );
306  rColor.SetBlue( *pTmp++ );
307  }
308 
309  // if possible accommodate some standard colours
310  if( nCount < 256 )
311  {
312  (*pPal)[ 255UL ] = COL_WHITE;
313 
314  if( nCount < 255 )
315  (*pPal)[ 254UL ] = COL_BLACK;
316  }
317  }
318 }
319 
321 {
322  bool bRet = false;
323 
324  // Extension-Label
325  sal_uInt8 cFunction(0);
326  rIStm.ReadUChar( cFunction );
327  if( NO_PENDING( rIStm ) )
328  {
329  bool bOverreadDataBlocks = false;
330  sal_uInt8 cSize(0);
331  // Block length
332  rIStm.ReadUChar( cSize );
333  switch( cFunction )
334  {
335  // 'Graphic Control Extension'
336  case 0xf9 :
337  {
338  sal_uInt8 cFlags(0);
339  rIStm.ReadUChar(cFlags);
342  sal_uInt8 cByte(0);
343  rIStm.ReadUChar(cByte);
344 
345  if ( NO_PENDING( rIStm ) )
346  {
347  nGCDisposalMethod = ( cFlags >> 2) & 7;
348  bGCTransparent = ( cFlags & 1 );
349  bStatus = ( cSize == 4 ) && ( cByte == 0 );
350  bRet = true;
351  }
352  }
353  break;
354 
355  // Application extension
356  case 0xff :
357  {
358  if ( NO_PENDING( rIStm ) )
359  {
360  // by default overread this extension
361  bOverreadDataBlocks = true;
362 
363  // Appl. extension has length 11
364  if ( cSize == 0x0b )
365  {
366  OString aAppId = read_uInt8s_ToOString(rIStm, 8);
367  OString aAppCode = read_uInt8s_ToOString(rIStm, 3);
368  rIStm.ReadUChar( cSize );
369 
370  // NetScape-Extension
371  if( aAppId == "NETSCAPE" && aAppCode == "2.0" && cSize == 3 )
372  {
373  sal_uInt8 cByte(0);
374  rIStm.ReadUChar( cByte );
375 
376  // Loop-Extension
377  if ( cByte == 0x01 )
378  {
379  rIStm.ReadUChar( cByte );
380  nLoops = cByte;
381  rIStm.ReadUChar( cByte );
382  nLoops |= ( static_cast<sal_uInt16>(cByte) << 8 );
383  rIStm.ReadUChar( cByte );
384 
385  bStatus = ( cByte == 0 );
386  bRet = NO_PENDING( rIStm );
387  bOverreadDataBlocks = false;
388 
389  // Netscape interprets the loop count
390  // as pure number of _repeats_;
391  // here it is the total number of loops
392  if( nLoops )
393  nLoops++;
394  }
395  else
396  rIStm.SeekRel( -1 );
397  }
398  else if ( aAppId == "STARDIV " && aAppCode == "5.0" && cSize == 9 )
399  {
400  sal_uInt8 cByte(0);
401  rIStm.ReadUChar( cByte );
402 
403  // Loop extension
404  if ( cByte == 0x01 )
405  {
407  rIStm.ReadUChar( cByte );
408  bStatus = ( cByte == 0 );
409  bRet = NO_PENDING( rIStm );
410  bOverreadDataBlocks = false;
411  }
412  else
413  rIStm.SeekRel( -1 );
414  }
415 
416  }
417  }
418  }
419  break;
420 
421  // overread everything else
422  default:
423  bOverreadDataBlocks = true;
424  break;
425  }
426 
427  // overread sub-blocks
428  if ( bOverreadDataBlocks )
429  {
430  bRet = true;
431  while( cSize && bStatus && !rIStm.eof() )
432  {
433  sal_uInt16 nCount = static_cast<sal_uInt16>(cSize) + 1;
434  const sal_uInt64 nMaxPossible = rIStm.remainingSize();
435  if (nCount > nMaxPossible)
436  nCount = nMaxPossible;
437 
438  if (nCount)
439  rIStm.SeekRel( nCount - 1 ); // Skip subblock data
440 
441  bRet = false;
442  std::size_t nRead = rIStm.ReadBytes(&cSize, 1);
443  if (NO_PENDING(rIStm) && nRead == 1)
444  {
445  bRet = true;
446  }
447  else
448  cSize = 0;
449  }
450  }
451  }
452 
453  return bRet;
454 }
455 
457 {
458  sal_uInt8 pBuf[ 9 ];
459  bool bRet = false;
460 
461  std::size_t nRead = rIStm.ReadBytes(pBuf, 9);
462  if (NO_PENDING(rIStm) && nRead == 9)
463  {
464  SvMemoryStream aMemStm;
465  BitmapPalette* pPal;
466 
467  aMemStm.SetBuffer( pBuf, 9, 9 );
468  aMemStm.ReadUInt16( nImagePosX );
469  aMemStm.ReadUInt16( nImagePosY );
470  aMemStm.ReadUInt16( nImageWidth );
471  aMemStm.ReadUInt16( nImageHeight );
472  sal_uInt8 nFlags(0);
473  aMemStm.ReadUChar(nFlags);
474 
475  // if interlaced, first define startvalue
476  bInterlaced = ( ( nFlags & 0x40 ) == 0x40 );
477  nLastInterCount = 7;
478  nLastImageY = 0;
479 
480  if( nFlags & 0x80 )
481  {
482  pPal = &aLPalette;
483  ReadPaletteEntries( pPal, sal_uLong(1) << ( (nFlags & 7 ) + 1 ) );
484  }
485  else
486  pPal = &aGPalette;
487 
488  // if we could read everything, we will create the local image;
489  // if the global colour table is valid for the image, we will
490  // consider the BackGroudColorIndex.
491  if( NO_PENDING( rIStm ) )
492  {
494  bRet = true;
495  }
496  }
497 
498  return bRet;
499 }
500 
502 {
503  sal_uLong nRet = 0;
504  sal_uLong nRead;
505  sal_uInt8 cBlockSize;
506 
507  rIStm.ReadUChar( cBlockSize );
508 
509  if ( rIStm.eof() )
510  nRet = 4;
511  else if ( NO_PENDING( rIStm ) )
512  {
513  if ( cBlockSize == 0 )
514  nRet = 2;
515  else
516  {
517  rIStm.ReadBytes( aSrcBuf.data(), cBlockSize );
518 
519  if( NO_PENDING( rIStm ) )
520  {
521  if( bOverreadBlock )
522  nRet = 3;
523  else
524  {
525  bool bEOI;
526  sal_uInt8* pTarget = pDecomp->DecompressBlock( aSrcBuf.data(), cBlockSize, nRead, bEOI );
527 
528  nRet = ( bEOI ? 3 : 1 );
529 
530  if( nRead && !bOverreadBlock )
531  FillImages( pTarget, nRead );
532 
533  std::free( pTarget );
534  }
535  }
536  }
537  }
538 
539  return nRet;
540 }
541 
542 void GIFReader::FillImages( const sal_uInt8* pBytes, sal_uLong nCount )
543 {
544  for( sal_uLong i = 0; i < nCount; i++ )
545  {
546  if( nImageX >= nImageWidth )
547  {
548  if( bInterlaced )
549  {
550  long nT1;
551 
552  // lines will be copied if interlaced
553  if( nLastInterCount )
554  {
555  long nMinY = std::min( static_cast<long>(nLastImageY) + 1, static_cast<long>(nImageHeight) - 1 );
556  long nMaxY = std::min( static_cast<long>(nLastImageY) + nLastInterCount, static_cast<long>(nImageHeight) - 1 );
557 
558  // copy last line read, if lines do not coincide
559  // ( happens at the end of the image )
560  if( ( nMinY > nLastImageY ) && ( nLastImageY < ( nImageHeight - 1 ) ) )
561  {
562  sal_uInt8* pScanline8 = pAcc8->GetScanline( nYAcc );
563  sal_uInt32 nSize8 = pAcc8->GetScanlineSize();
564  sal_uInt8* pScanline1 = nullptr;
565  sal_uInt32 nSize1 = 0;
566 
567  if( bGCTransparent )
568  {
569  pScanline1 = pAcc1->GetScanline( nYAcc );
570  nSize1 = pAcc1->GetScanlineSize();
571  }
572 
573  for( long j = nMinY; j <= nMaxY; j++ )
574  {
575  memcpy( pAcc8->GetScanline( j ), pScanline8, nSize8 );
576 
577  if( bGCTransparent )
578  memcpy( pAcc1->GetScanline( j ), pScanline1, nSize1 );
579  }
580  }
581  }
582 
583  nT1 = ( ++nImageY ) << 3;
584  nLastInterCount = 7;
585 
586  if( nT1 >= nImageHeight )
587  {
588  long nT2 = nImageY - ( ( nImageHeight + 7 ) >> 3 );
589  nT1 = ( nT2 << 3 ) + 4;
590  nLastInterCount = 3;
591 
592  if( nT1 >= nImageHeight )
593  {
594  nT2 -= ( nImageHeight + 3 ) >> 3;
595  nT1 = ( nT2 << 2 ) + 2;
596  nLastInterCount = 1;
597 
598  if( nT1 >= nImageHeight )
599  {
600  nT2 -= ( nImageHeight + 1 ) >> 2;
601  nT1 = ( nT2 << 1 ) + 1;
602  nLastInterCount = 0;
603  }
604  }
605  }
606 
607  nLastImageY = static_cast<sal_uInt16>(nT1);
608  nYAcc = nT1;
609  }
610  else
611  {
612  nLastImageY = ++nImageY;
613  nYAcc = nImageY;
614  }
615 
616  // line starts from the beginning
617  nImageX = 0;
618  }
619 
620  if( nImageY < nImageHeight )
621  {
622  const sal_uInt8 cTmp = pBytes[ i ];
623 
624  if( bGCTransparent )
625  {
626  if( cTmp == nGCTransparentIndex )
628  else
629  {
630  pAcc8->SetPixelIndex( nYAcc, nImageX, cTmp );
632  }
633  }
634  else
635  pAcc8->SetPixelIndex( nYAcc, nImageX++, cTmp );
636  }
637  else
638  {
639  bOverreadBlock = true;
640  break;
641  }
642  }
643 }
644 
646 {
647  AnimationBitmap aAnimationBitmap;
648 
649  pAcc8.reset();
650 
651  if( bGCTransparent )
652  {
653  pAcc1.reset();
654  aAnimationBitmap.maBitmapEx = BitmapEx( aBmp8, aBmp1 );
655  }
656  else
657  aAnimationBitmap.maBitmapEx = BitmapEx( aBmp8 );
658 
659  aAnimationBitmap.maPositionPixel = Point( nImagePosX, nImagePosY );
660  aAnimationBitmap.maSizePixel = Size( nImageWidth, nImageHeight );
661  aAnimationBitmap.mnWait = ( nTimer != 65535 ) ? nTimer : ANIMATION_TIMEOUT_ON_CLICK;
662  aAnimationBitmap.mbUserInput = false;
663 
664  // tdf#104121 . Internet Explorer, Firefox, Chrome and Safari all set a minimum default playback speed.
665  // IE10 Consumer Preview sets default of 100ms for rates less that 20ms. We do the same
666  if (aAnimationBitmap.mnWait < 2) // 20ms, specified in 100's of a second
667  aAnimationBitmap.mnWait = 10;
668 
669  if( nGCDisposalMethod == 2 )
670  aAnimationBitmap.meDisposal = Disposal::Back;
671  else if( nGCDisposalMethod == 3 )
672  aAnimationBitmap.meDisposal = Disposal::Previous;
673  else
674  aAnimationBitmap.meDisposal = Disposal::Not;
675 
676  nAnimationByteSize += aAnimationBitmap.maBitmapEx.GetSizeBytes();
677  nAnimationMinFileData += static_cast<sal_uInt64>(nImageWidth) * nImageHeight / 2560;
678  aAnimation.Insert(aAnimationBitmap);
679 
680  if( aAnimation.Count() == 1 )
681  {
684  }
685 }
686 
688 {
689  Graphic aImGraphic;
690 
691  // only create intermediate graphic, if data is available
692  // but graphic still not completely read
693  if ( bImGraphicReady && !aAnimation.Count() )
694  {
695  pAcc8.reset();
696 
697  if ( bGCTransparent )
698  {
699  pAcc1.reset();
700  aImGraphic = BitmapEx( aBmp8, aBmp1 );
701 
703  bStatus = bStatus && pAcc1;
704  }
705  else
706  aImGraphic = aBmp8;
707 
709  bStatus = bStatus && pAcc8;
710  }
711 
712  return aImGraphic;
713 }
714 
716 {
717  bool bRead = false;
718  bool bEnd = false;
719 
720  if ( !bStatus )
722 
723  // set stream to right position
724  rIStm.Seek( nLastPos );
725 
726  switch( eActAction )
727  {
728  // read next marker
729  case MARKER_READING:
730  {
731  sal_uInt8 cByte;
732 
733  rIStm.ReadUChar( cByte );
734 
735  if( rIStm.eof() )
737  else if( NO_PENDING( rIStm ) )
738  {
739  bRead = true;
740 
741  if( cByte == '!' )
743  else if( cByte == ',' )
745  else if( cByte == ';' )
747  else
749  }
750  }
751  break;
752 
753  // read ScreenDescriptor
755  {
756  bRead = ReadGlobalHeader();
757  if( bRead )
758  {
761  }
762  }
763  break;
764 
765  // read extension
766  case EXTENSION_READING:
767  {
768  bRead = ReadExtension();
769  if( bRead )
771  }
772  break;
773 
774  // read Image-Descriptor
776  {
777  bRead = ReadLocalHeader();
778  if( bRead )
779  {
780  nYAcc = nImageX = nImageY = 0;
782  }
783  }
784  break;
785 
786  // read first data block
787  case FIRST_BLOCK_READING:
788  {
789  sal_uInt8 cDataSize;
790 
791  rIStm.ReadUChar( cDataSize );
792 
793  if( rIStm.eof() )
795  else if( cDataSize > 12 )
796  bStatus = false;
797  else if( NO_PENDING( rIStm ) )
798  {
799  bRead = true;
800  pDecomp = std::make_unique<GIFLZWDecompressor>( cDataSize );
802  bOverreadBlock = false;
803  }
804  else
806  }
807  break;
808 
809  // read next data block
810  case NEXT_BLOCK_READING:
811  {
812  sal_uInt16 nLastX = nImageX;
813  sal_uInt16 nLastY = nImageY;
814  sal_uLong nRet = ReadNextBlock();
815 
816  // Return: 0:Pending / 1:OK; / 2:OK and last block: / 3:EOI / 4:HardAbort
817  if( nRet )
818  {
819  bRead = true;
820 
821  if ( nRet == 1 )
822  {
823  bImGraphicReady = true;
825  bOverreadBlock = false;
826  }
827  else
828  {
829  if( nRet == 2 )
830  {
831  pDecomp.reset();
835  }
836  else if( nRet == 3 )
837  {
839  bOverreadBlock = true;
840  }
841  else
842  {
843  pDecomp.reset();
847  }
848  }
849  }
850  else
851  {
852  nImageX = nLastX;
853  nImageY = nLastY;
854  }
855  }
856  break;
857 
858  // an error occurred
859  case ABORT_READING:
860  {
861  bEnd = true;
863  }
864  break;
865 
866  default:
867  break;
868  }
869 
870  // set stream to right position,
871  // if data could be read put it at the old
872  // position otherwise at the actual one
873  if( bRead || bEnd )
874  nLastPos = rIStm.Tell();
875 
876  return bRead;
877 }
878 
880 {
881  ReadState eReadState;
882 
883  bStatus = true;
884 
885  while( ProcessGIF() && ( eActAction != END_READING ) ) {}
886 
887  if( !bStatus )
888  eReadState = GIFREAD_ERROR;
889  else if( eActAction == END_READING )
890  eReadState = GIFREAD_OK;
891  else
892  {
893  if ( rIStm.GetError() == ERRCODE_IO_PENDING )
894  rIStm.ResetError();
895 
896  eReadState = GIFREAD_NEED_MORE;
897  }
898 
899  if (eReadState == GIFREAD_OK)
900  return aAnimation.Count() > 1;
901  return false;
902 }
903 
905 {
906  ReadState eReadState;
907 
908  bStatus = true;
909 
910  while( ProcessGIF() && ( eActAction != END_READING ) ) {}
911 
912  if( !bStatus )
913  eReadState = GIFREAD_ERROR;
914  else if( eActAction == END_READING )
915  eReadState = GIFREAD_OK;
916  else
917  {
918  if ( rIStm.GetError() == ERRCODE_IO_PENDING )
919  rIStm.ResetError();
920 
921  eReadState = GIFREAD_NEED_MORE;
922  }
923 
924  if( aAnimation.Count() == 1 )
925  {
926  rGraphic = aAnimation.Get(0).maBitmapEx;
927 
928  if( nLogWidth100 && nLogHeight100 )
929  {
930  rGraphic.SetPrefSize( Size( nLogWidth100, nLogHeight100 ) );
931  rGraphic.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
932  }
933  }
934  else
935  rGraphic = aAnimation;
936 
937  return eReadState;
938 }
939 
941 {
942  GIFReader aReader(rStm);
943 
944  SvStreamEndian nOldFormat = rStm.GetEndian();
945  rStm.SetEndian(SvStreamEndian::LITTLE);
946  bool bResult = aReader.ReadIsAnimated();
947  rStm.SetEndian(nOldFormat);
948 
949  return bResult;
950 }
951 
952 VCL_DLLPUBLIC bool ImportGIF( SvStream & rStm, Graphic& rGraphic )
953 {
954  std::shared_ptr<GraphicReader> pContext = rGraphic.GetContext();
955  rGraphic.SetContext(nullptr);
956  GIFReader* pGIFReader = dynamic_cast<GIFReader*>( pContext.get() );
957  if (!pGIFReader)
958  {
959  pContext = std::make_shared<GIFReader>( rStm );
960  pGIFReader = static_cast<GIFReader*>( pContext.get() );
961  }
962 
963  SvStreamEndian nOldFormat = rStm.GetEndian();
964  rStm.SetEndian( SvStreamEndian::LITTLE );
965 
966  bool bRet = true;
967 
968  ReadState eReadState = pGIFReader->ReadGIF(rGraphic);
969 
970  if (eReadState == GIFREAD_ERROR)
971  {
972  bRet = false;
973  }
974  else if (eReadState == GIFREAD_NEED_MORE)
975  {
976  rGraphic = pGIFReader->GetIntermediateGraphic();
977  rGraphic.SetContext(pContext);
978  }
979 
980  rStm.SetEndian(nOldFormat);
981 
982  return bRet;
983 }
984 
985 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
long Width() const
void SetLoopCount(const sal_uInt32 nLoopCount)
Definition: Animation.cxx:435
GIFAction eActAction
Definition: gifread.cxx:83
constexpr::Color COL_BLACK(0x00, 0x00, 0x00)
void SetBlue(sal_uInt8 nBlue)
sal_uInt16 nLastInterCount
Definition: gifread.cxx:81
Scanline GetScanline(long nY) const
SvStream & ReadUInt16(sal_uInt16 &rUInt16)
void SetContext(const std::shared_ptr< GraphicReader > &pReader)
Definition: graph.cxx:493
long Height() const
sal_uInt16 nImageX
Definition: gifread.cxx:78
#define NO_PENDING(rStm)
Definition: gifread.cxx:27
sal_uInt32 nLogHeight100
Definition: gifread.cxx:70
#define VCL_DLLPUBLIC
Definition: dllapi.h:29
sal_uInt8 nBackgroundColor
Definition: gifread.cxx:90
sal_uInt16 nImagePosY
Definition: gifread.cxx:77
void ReadPaletteEntries(BitmapPalette *pPal, sal_uLong nCount)
Definition: gifread.cxx:287
GIFReader(SvStream &rStm)
Definition: gifread.cxx:116
sal_uIntPtr sal_uLong
void SetPrefMapMode(const MapMode &rPrefMapMode)
Definition: graph.cxx:391
sal_uInt16 nImagePosX
Definition: gifread.cxx:76
vcl::ScopedBitmapAccess< BitmapWriteAccess, Bitmap,&Bitmap::AcquireWriteAccess > BitmapScopedWriteAccess
std::shared_ptr< GraphicReader > & GetContext()
Definition: graph.cxx:488
sal_uInt64 Seek(sal_uInt64 nPos)
ReadState
Definition: gifread.cxx:41
bool ReadGlobalHeader()
Definition: gifread.cxx:244
std::unique_ptr< GIFLZWDecompressor > pDecomp
Definition: gifread.cxx:63
sal_uInt16 GetBestPaletteIndex(const BitmapColor &rBitmapColor) const
Definition: bmpacc.cxx:81
Animation aAnimation
Definition: gifread.cxx:54
OUString maUpperName
Definition: graph.hxx:68
sal_uInt16 nGlobalWidth
Definition: gifread.cxx:72
long nYAcc
Definition: gifread.cxx:66
bool ReadIsAnimated()
Definition: gifread.cxx:879
sal_uInt64 SeekRel(sal_Int64 nPos)
sal_uInt16 nImageHeight
Definition: gifread.cxx:75
sal_uInt16 nImageWidth
Definition: gifread.cxx:74
bool bInterlaced
Definition: gifread.cxx:86
ErrCode GetError() const
bool ProcessGIF()
Definition: gifread.cxx:715
bool eof() const
long nLastPos
Definition: gifread.cxx:67
std::vector< sal_uInt8 > aSrcBuf
Definition: gifread.cxx:62
sal_uInt16 nGlobalHeight
Definition: gifread.cxx:73
VCL_DLLPUBLIC bool ImportGIF(SvStream &rStm, Graphic &rGraphic)
Definition: gifread.cxx:952
sal_uInt8 cNonTransIndex1
Definition: gifread.cxx:94
sal_uInt64 remainingSize()
void CreateBitmaps(long nWidth, long nHeight, BitmapPalette *pPal, bool bWatchForBackgroundColor)
Definition: gifread.cxx:162
SvStream & ReadUInt32(sal_uInt32 &rUInt32)
BitmapScopedWriteAccess pAcc8
Definition: gifread.cxx:64
ReadState ReadGIF(Graphic &rGraphic)
Definition: gifread.cxx:904
sal_uInt64 nAnimationMinFileData
Definition: gifread.cxx:56
#define SAL_MAX_INT32
sal_uInt32 GetScanlineSize() const
Graphic GetIntermediateGraphic()
Definition: gifread.cxx:687
bool bStatus
Definition: gifread.cxx:84
int i
bool ReadLocalHeader()
Definition: gifread.cxx:456
sal_uInt16 nLastImageY
Definition: gifread.cxx:80
sal_uInt8 cTransIndex1
Definition: gifread.cxx:93
bool bGCTransparent
Definition: gifread.cxx:85
SvStream & rIStm
Definition: gifread.cxx:61
sal_uLong GetSizeBytes() const
Definition: bitmapex.cxx:282
void SetRed(sal_uInt8 nRed)
SvStream & ReadUChar(unsigned char &rChar)
#define ANIMATION_TIMEOUT_ON_CLICK
Definition: Animation.hxx:27
sal_uInt16 nTimer
Definition: gifread.cxx:71
sal_uInt64 nMaxStreamData
Definition: gifread.cxx:68
const AnimationBitmap & Get(sal_uInt16 nAnimation) const
Definition: Animation.cxx:414
std::size_t ReadBytes(void *pData, std::size_t nSize)
void FillImages(const sal_uInt8 *pBytes, sal_uLong nCount)
Definition: gifread.cxx:542
Bitmap aBmp1
Definition: gifread.cxx:58
SvStreamEndian GetEndian() const
sal_uInt16 nLoops
Definition: gifread.cxx:82
void SetDisplaySizePixel(const Size &rSize)
Definition: Animation.hxx:56
sal_uInt8 nGCDisposalMethod
Definition: gifread.cxx:92
bool Insert(const AnimationBitmap &rAnimationBitmap)
Definition: Animation.cxx:391
void CreateNewBitmaps()
Definition: gifread.cxx:645
unsigned char sal_uInt8
sal_uInt64 nAnimationByteSize
Definition: gifread.cxx:55
sal_uInt8 nGCTransparentIndex
Definition: gifread.cxx:91
void SetGreen(sal_uInt8 nGreen)
void SetEndian(SvStreamEndian SvStreamEndian)
Bitmap aBmp8
Definition: gifread.cxx:57
sal_uInt64 Tell() const
constexpr::Color COL_WHITE(0xFF, 0xFF, 0xFF)
void SetPrefSize(const Size &rPrefSize)
Definition: graph.cxx:380
bool bImGraphicReady
Definition: gifread.cxx:88
bool Erase(const Color &rFillColor)
Fill the entire bitmap with the given color.
Definition: bitmappaint.cxx:34
#define ERRCODE_IO_PENDING
Definition: errcode.hxx:227
GIFAction
Definition: gifread.cxx:29
size_t Count() const
Definition: Animation.hxx:69
#define SAL_WARN(area, stream)
SvStreamEndian
BitmapPalette aGPalette
Definition: gifread.cxx:59
virtual void ResetError()
bool bOverreadBlock
Definition: gifread.cxx:87
BitmapPalette aLPalette
Definition: gifread.cxx:60
VCL_DLLPUBLIC bool IsGIFAnimated(SvStream &rStm)
Definition: gifread.cxx:940
void SetBuffer(void *pBuf, std::size_t nSize, std::size_t nEOF)
sal_uLong ReadNextBlock()
Definition: gifread.cxx:501
sal_uInt32 nLogWidth100
Definition: gifread.cxx:69
bool bGlobalPalette
Definition: gifread.cxx:89
bool ReadExtension()
Definition: gifread.cxx:320
OString read_uInt8s_ToOString(SvStream &rStrm, std::size_t nLen)
sal_uInt16 nImageY
Definition: gifread.cxx:79
void ClearImageExtensions()
Definition: gifread.cxx:155
void SetPixelIndex(long nY, long nX, sal_uInt8 cIndex)
BitmapScopedWriteAccess pAcc1
Definition: gifread.cxx:65