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