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