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
30namespace {
31
32enum GIFAction
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
44enum ReadState
45{
46 GIFREAD_OK,
47 GIFREAD_ERROR,
48 GIFREAD_NEED_MORE
49};
50
51}
52
54
55class SvStream;
56
57namespace {
58
59class 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
114public:
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
126GIFReader::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
165void GIFReader::ClearImageExtensions()
166{
167 nGCDisposalMethod = 0;
168 bGCTransparent = false;
169 nTimer = 0;
170}
171
172void 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
254bool GIFReader::ReadGlobalHeader()
255{
256 char pBuf[ 7 ];
257 bool bRet = false;
258
259 auto nRead = rIStm.ReadBytes(pBuf, 6);
260 if (nRead == 6 && NO_PENDING(rIStm))
261 {
262 pBuf[ 6 ] = 0;
263 if( !strcmp( pBuf, "GIF87a" ) || !strcmp( pBuf, "GIF89a" ) )
264 {
265 nRead = rIStm.ReadBytes(pBuf, 7);
266 if (nRead == 7 && 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
297void 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
330bool 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
466bool 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
511sal_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
552void 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
655void 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
697Graphic 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
725bool 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
889bool 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
914void GIFReader::GetLogicSize(Size& rLogicSize)
915{
916 rLogicSize.setWidth(nLogWidth100);
917 rLogicSize.setHeight(nLogHeight100);
918}
919
920ReadState 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
956bool 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
969VCL_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: */
#define ANIMATION_TIMEOUT_ON_CLICK
Definition: Animation.hxx:28
vcl::ScopedBitmapAccess< BitmapWriteAccess, Bitmap, &Bitmap::AcquireWriteAccess > BitmapScopedWriteAccess
ReadState
Definition: JpegReader.hxx:32
sal_Int64 GetSizeBytes() const
Definition: BitmapEx.cxx:220
void SetGreen(sal_uInt8 nGreen)
void SetRed(sal_uInt8 nRed)
void SetBlue(sal_uInt8 nBlue)
void SetPrefMapMode(const MapMode &rPrefMapMode)
Definition: graph.cxx:379
void SetReaderContext(const std::shared_ptr< GraphicReader > &pReader)
Definition: graph.cxx:483
std::shared_ptr< GraphicReader > & GetReaderContext()
Definition: graph.cxx:478
void SetPrefSize(const Size &rPrefSize)
Definition: graph.cxx:368
void setWidth(tools::Long nWidth)
void setHeight(tools::Long nHeight)
void SetBuffer(void *pBuf, std::size_t nSize, std::size_t nEOF)
void SetEndian(SvStreamEndian SvStreamEndian)
SvStreamEndian GetEndian() const
SvStream & ReadUInt16(sal_uInt16 &rUInt16)
SvStream & ReadUChar(unsigned char &rChar)
This template handles BitmapAccess the RAII way.
constexpr ::Color COL_WHITE(0xFF, 0xFF, 0xFF)
constexpr ::Color COL_BLACK(0x00, 0x00, 0x00)
int nCount
#define VCL_DLLPUBLIC
Definition: dllapi.h:29
#define ERRCODE_IO_PENDING
Definition: errcode.hxx:225
#define NO_PENDING(rStm)
Definition: gifread.cxx:28
VCL_DLLPUBLIC bool ImportGIF(SvStream &rStm, Graphic &rGraphic)
Definition: gifread.cxx:969
bool IsGIFAnimated(SvStream &rStm, Size &rLogicSize)
Definition: gifread.cxx:956
#define SAL_WARN(area, stream)
int i
long Long
sal_uIntPtr sal_uLong
TOOLS_DLLPUBLIC OString read_uInt8s_ToOString(SvStream &rStrm, std::size_t nUnits)
SvStreamEndian
tools::Long mnWait
unsigned char sal_uInt8