Branch libreoffice-5-0-4
[LibreOffice.git] / vcl / source / filter / igif / gifread.cxx
blob3526a0bcaf5b15d68af7f9ed226b5975cb189e46
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #define _GIFPRIVATE
22 #include "decode.hxx"
23 #include "gifread.hxx"
24 #include <boost/scoped_array.hpp>
26 #define NO_PENDING( rStm ) ( ( rStm ).GetError() != ERRCODE_IO_PENDING )
28 GIFReader::GIFReader( SvStream& rStm )
29 : aGPalette ( 256 )
30 , aLPalette ( 256 )
31 , rIStm ( rStm )
32 , pDecomp ( NULL )
33 , pAcc8 ( NULL )
34 , pAcc1 ( NULL )
35 , nYAcc ( 0 )
36 , nLastPos ( rStm.Tell() )
37 , nLogWidth100 ( 0UL )
38 , nLogHeight100 ( 0UL )
39 , nGlobalWidth ( 0 )
40 , nGlobalHeight ( 0 )
41 , nImageWidth ( 0 )
42 , nImageHeight ( 0 )
43 , nImagePosX ( 0 )
44 , nImagePosY ( 0 )
45 , nImageX ( 0 )
46 , nImageY ( 0 )
47 , nLastImageY ( 0 )
48 , nLastInterCount ( 0 )
49 , nLoops ( 1 )
50 , eActAction ( GLOBAL_HEADER_READING )
51 , bStatus ( false )
52 , bGCTransparent ( false )
53 , bInterlaced ( false)
54 , bOverreadBlock ( false )
55 , bImGraphicReady ( false )
56 , bGlobalPalette ( false )
57 , nBackgroundColor ( 0 )
58 , nGCTransparentIndex ( 0 )
59 , cTransIndex1 ( 0 )
60 , cNonTransIndex1 ( 0 )
62 maUpperName = "SVIGIF";
63 pSrcBuf = new sal_uInt8[ 256 ];
64 ClearImageExtensions();
67 GIFReader::~GIFReader()
69 aImGraphic.SetContext( NULL );
71 if( pAcc1 )
72 Bitmap::ReleaseAccess( pAcc1 );
74 if( pAcc8 )
75 Bitmap::ReleaseAccess( pAcc8 );
77 delete[] pSrcBuf;
80 void GIFReader::ClearImageExtensions()
82 nGCDisposalMethod = 0;
83 bGCTransparent = false;
84 nTimer = 0;
87 bool GIFReader::CreateBitmaps( long nWidth, long nHeight, BitmapPalette* pPal,
88 bool bWatchForBackgroundColor )
90 const Size aSize( nWidth, nHeight );
92 #ifdef __LP64__
93 // Don't bother allocating a bitmap of a size that would fail on a
94 // 32-bit system. We have at least one unit tests that is expected
95 // to fail (loading a 65535*65535 size GIF
96 // svtools/qa/cppunit/data/gif/fail/CVE-2008-5937-1.gif), but
97 // which doesn't fail on 64-bit Mac OS X at least. Why the loading
98 // fails on 64-bit Linux, no idea.
99 if (nWidth >= 64000 && nHeight >= 64000)
101 bStatus = false;
102 return bStatus;
104 #endif
106 if( bGCTransparent )
108 const Color aWhite( COL_WHITE );
110 aBmp1 = Bitmap( aSize, 1 );
112 if( !aAnimation.Count() )
113 aBmp1.Erase( aWhite );
115 pAcc1 = aBmp1.AcquireWriteAccess();
117 if( pAcc1 )
119 cTransIndex1 = (sal_uInt8) pAcc1->GetBestPaletteIndex( aWhite );
120 cNonTransIndex1 = cTransIndex1 ? 0 : 1;
122 else
123 bStatus = false;
126 if( bStatus )
128 aBmp8 = Bitmap( aSize, 8, pPal );
130 if( !!aBmp8 && bWatchForBackgroundColor && aAnimation.Count() )
131 aBmp8.Erase( (*pPal)[ nBackgroundColor ] );
132 else
133 aBmp8.Erase( Color( COL_WHITE ) );
135 pAcc8 = aBmp8.AcquireWriteAccess();
136 bStatus = ( pAcc8 != NULL );
139 return bStatus;
142 bool GIFReader::ReadGlobalHeader()
144 char pBuf[ 7 ];
145 sal_uInt8 nRF;
146 sal_uInt8 nAspect;
147 bool bRet = false;
149 rIStm.Read( pBuf, 6 );
150 if( NO_PENDING( rIStm ) )
152 pBuf[ 6 ] = 0;
153 if( !strcmp( pBuf, "GIF87a" ) || !strcmp( pBuf, "GIF89a" ) )
155 rIStm.Read( pBuf, 7 );
156 if( NO_PENDING( rIStm ) )
158 SvMemoryStream aMemStm;
160 aMemStm.SetBuffer( pBuf, 7, false, 7 );
161 aMemStm.ReadUInt16( nGlobalWidth );
162 aMemStm.ReadUInt16( nGlobalHeight );
163 aMemStm.ReadUChar( nRF );
164 aMemStm.ReadUChar( nBackgroundColor );
165 aMemStm.ReadUChar( nAspect );
167 bGlobalPalette = ( nRF & 0x80 );
169 if( bGlobalPalette )
170 ReadPaletteEntries( &aGPalette, 1 << ( ( nRF & 7 ) + 1 ) );
171 else
172 nBackgroundColor = 0;
174 if( NO_PENDING( rIStm ) )
175 bRet = true;
178 else
179 bStatus = false;
182 return bRet;
185 void GIFReader::ReadPaletteEntries( BitmapPalette* pPal, sal_uLong nCount )
187 sal_uLong nLen = 3UL * nCount;
188 const sal_uInt64 nMaxPossible = rIStm.remainingSize();
189 if (nLen > nMaxPossible)
190 nLen = nMaxPossible;
191 boost::scoped_array<sal_uInt8> pBuf(new sal_uInt8[ nLen ]);
192 sal_Size nRead = rIStm.Read(pBuf.get(), nLen);
193 nCount = nRead/3UL;
194 if( NO_PENDING( rIStm ) )
196 sal_uInt8* pTmp = pBuf.get();
198 for (sal_uLong i = 0UL; i < nCount; ++i)
200 BitmapColor& rColor = (*pPal)[i];
202 rColor.SetRed( *pTmp++ );
203 rColor.SetGreen( *pTmp++ );
204 rColor.SetBlue( *pTmp++ );
207 // if possible accommodate some standard colours
208 if( nCount < 256UL )
210 (*pPal)[ 255UL ] = Color( COL_WHITE );
212 if( nCount < 255UL )
213 (*pPal)[ 254UL ] = Color( COL_BLACK );
218 bool GIFReader::ReadExtension()
220 bool bRet = false;
221 bool bOverreadDataBlocks = false;
223 // Extension-Label
224 sal_uInt8 cFunction(0);
225 rIStm.ReadUChar( cFunction );
226 if( NO_PENDING( rIStm ) )
228 sal_uInt8 cSize(0);
229 // Block length
230 rIStm.ReadUChar( cSize );
232 switch( cFunction )
234 // 'Graphic Control Extension'
235 case( 0xf9 ) :
237 sal_uInt8 cFlags(0);
238 rIStm.ReadUChar(cFlags);
239 rIStm.ReadUInt16(nTimer);
240 rIStm.ReadUChar(nGCTransparentIndex);
241 sal_uInt8 cByte(0);
242 rIStm.ReadUChar(cByte);
244 if ( NO_PENDING( rIStm ) )
246 nGCDisposalMethod = ( cFlags >> 2) & 7;
247 bGCTransparent = ( cFlags & 1 );
248 bStatus = ( cSize == 4 ) && ( cByte == 0 );
249 bRet = true;
252 break;
254 // Application extension
255 case ( 0xff ) :
257 if ( NO_PENDING( rIStm ) )
259 // by default overread this extension
260 bOverreadDataBlocks = true;
262 // Appl. extension has length 11
263 if ( cSize == 0x0b )
265 OString aAppId = read_uInt8s_ToOString(rIStm, 8);
266 OString aAppCode = read_uInt8s_ToOString(rIStm, 3);
267 rIStm.ReadUChar( cSize );
269 // NetScape-Extension
270 if( aAppId == "NETSCAPE" && aAppCode == "2.0" && cSize == 3 )
272 sal_uInt8 cByte(0);
273 rIStm.ReadUChar( cByte );
275 // Loop-Extension
276 if ( cByte == 0x01 )
278 rIStm.ReadUChar( cByte );
279 nLoops = cByte;
280 rIStm.ReadUChar( cByte );
281 nLoops |= ( (sal_uInt16) cByte << 8 );
282 rIStm.ReadUChar( cByte );
284 bStatus = ( cByte == 0 );
285 bRet = NO_PENDING( rIStm );
286 bOverreadDataBlocks = false;
288 // Netscape interpretes the loop count
289 // as pure number of _repeats_;
290 // here it is the total number of loops
291 if( nLoops )
292 nLoops++;
294 else
295 rIStm.SeekRel( -1 );
297 else if ( aAppId == "STARDIV " && aAppCode == "5.0" && cSize == 9 )
299 sal_uInt8 cByte(0);
300 rIStm.ReadUChar( cByte );
302 // Loop extension
303 if ( cByte == 0x01 )
305 rIStm.ReadUInt32( nLogWidth100 ).ReadUInt32( nLogHeight100 );
306 rIStm.ReadUChar( cByte );
307 bStatus = ( cByte == 0 );
308 bRet = NO_PENDING( rIStm );
309 bOverreadDataBlocks = false;
311 else
312 rIStm.SeekRel( -1 );
318 break;
320 // overread everything else
321 default:
322 bOverreadDataBlocks = true;
323 break;
326 // overread sub-blocks
327 if ( bOverreadDataBlocks )
329 bRet = true;
330 while( cSize && bStatus && !rIStm.IsEof() )
332 sal_uInt16 nCount = (sal_uInt16) cSize + 1;
333 const sal_uInt64 nMaxPossible = rIStm.remainingSize();
334 if (nCount > nMaxPossible)
335 nCount = nMaxPossible;
336 boost::scoped_array<sal_uInt8> pBuffer(new sal_uInt8[nCount]);
338 bRet = false;
339 sal_Size nRead = rIStm.Read(pBuffer.get(), nCount);
340 if (NO_PENDING(rIStm) && cSize < nRead)
342 cSize = pBuffer[cSize];
343 bRet = true;
345 else
346 cSize = 0;
351 return bRet;
354 bool GIFReader::ReadLocalHeader()
356 sal_uInt8 pBuf[ 9 ];
357 bool bRet = false;
359 sal_Size nRead = rIStm.Read(pBuf, 9);
360 if (NO_PENDING(rIStm) && nRead == 9)
362 SvMemoryStream aMemStm;
363 BitmapPalette* pPal;
365 aMemStm.SetBuffer( pBuf, 9, false, 9 );
366 aMemStm.ReadUInt16( nImagePosX );
367 aMemStm.ReadUInt16( nImagePosY );
368 aMemStm.ReadUInt16( nImageWidth );
369 aMemStm.ReadUInt16( nImageHeight );
370 sal_uInt8 nFlags(0);
371 aMemStm.ReadUChar(nFlags);
373 // if interlaced, first define startvalue
374 bInterlaced = ( ( nFlags & 0x40 ) == 0x40 );
375 nLastInterCount = 7;
376 nLastImageY = 0;
378 if( nFlags & 0x80 )
380 pPal = &aLPalette;
381 ReadPaletteEntries( pPal, 1 << ( (nFlags & 7 ) + 1 ) );
383 else
384 pPal = &aGPalette;
386 // if we could read everything, we will create the local image;
387 // if the global colour table is valid for the image, we will
388 // consider the BackGroudColorIndex.
389 if( NO_PENDING( rIStm ) )
391 CreateBitmaps( nImageWidth, nImageHeight, pPal, bGlobalPalette && ( pPal == &aGPalette ) );
392 bRet = true;
396 return bRet;
399 sal_uLong GIFReader::ReadNextBlock()
401 sal_uLong nRet = 0UL;
402 sal_uLong nRead;
403 sal_uInt8 cBlockSize;
405 rIStm.ReadUChar( cBlockSize );
407 if ( rIStm.IsEof() )
408 nRet = 4UL;
409 else if ( NO_PENDING( rIStm ) )
411 if ( cBlockSize == 0 )
412 nRet = 2UL;
413 else
415 rIStm.Read( pSrcBuf, cBlockSize );
417 if( NO_PENDING( rIStm ) )
419 if( bOverreadBlock )
420 nRet = 3UL;
421 else
423 bool bEOI;
424 HPBYTE pTarget = pDecomp->DecompressBlock( pSrcBuf, cBlockSize, nRead, bEOI );
426 nRet = ( bEOI ? 3 : 1 );
428 if( nRead && !bOverreadBlock )
429 FillImages( pTarget, nRead );
431 rtl_freeMemory( pTarget );
437 return nRet;
440 void GIFReader::FillImages( HPBYTE pBytes, sal_uLong nCount )
442 for( sal_uLong i = 0UL; i < nCount; i++ )
444 if( nImageX >= nImageWidth )
446 if( bInterlaced )
448 long nT1;
450 // lines will be copied if interlaced
451 if( nLastInterCount )
453 long nMinY = std::min( (long) nLastImageY + 1, (long) nImageHeight - 1 );
454 long nMaxY = std::min( (long) nLastImageY + nLastInterCount, (long) nImageHeight - 1 );
456 // copy last line read, if lines do not coincide
457 // ( happens at the end of the image )
458 if( ( nMinY > nLastImageY ) && ( nLastImageY < ( nImageHeight - 1 ) ) )
460 HPBYTE pScanline8 = pAcc8->GetScanline( nYAcc );
461 sal_uLong nSize8 = pAcc8->GetScanlineSize();
462 HPBYTE pScanline1 = 0;
463 sal_uLong nSize1 = 0;
465 if( bGCTransparent )
467 pScanline1 = pAcc1->GetScanline( nYAcc );
468 nSize1 = pAcc1->GetScanlineSize();
471 for( long j = nMinY; j <= nMaxY; j++ )
473 memcpy( pAcc8->GetScanline( j ), pScanline8, nSize8 );
475 if( bGCTransparent )
476 memcpy( pAcc1->GetScanline( j ), pScanline1, nSize1 );
481 nT1 = ( ++nImageY ) << 3;
482 nLastInterCount = 7;
484 if( nT1 >= nImageHeight )
486 long nT2 = nImageY - ( ( nImageHeight + 7 ) >> 3 );
487 nT1 = ( nT2 << 3 ) + 4;
488 nLastInterCount = 3;
490 if( nT1 >= nImageHeight )
492 nT2 -= ( nImageHeight + 3 ) >> 3;
493 nT1 = ( nT2 << 2 ) + 2;
494 nLastInterCount = 1;
496 if( nT1 >= nImageHeight )
498 nT2 -= ( nImageHeight + 1 ) >> 2;
499 nT1 = ( nT2 << 1 ) + 1;
500 nLastInterCount = 0;
505 nLastImageY = (sal_uInt16) nT1;
506 nYAcc = nT1;
508 else
510 nLastImageY = ++nImageY;
511 nYAcc = nImageY;
514 // line starts from the beginning
515 nImageX = 0;
518 if( nImageY < nImageHeight )
520 const sal_uInt8 cTmp = pBytes[ i ];
522 if( bGCTransparent )
524 if( cTmp == nGCTransparentIndex )
525 pAcc1->SetPixelIndex( nYAcc, nImageX++, cTransIndex1 );
526 else
528 pAcc8->SetPixelIndex( nYAcc, nImageX, cTmp );
529 pAcc1->SetPixelIndex( nYAcc, nImageX++, cNonTransIndex1 );
532 else
533 pAcc8->SetPixelIndex( nYAcc, nImageX++, cTmp );
535 else
537 bOverreadBlock = true;
538 break;
543 void GIFReader::CreateNewBitmaps()
545 AnimationBitmap aAnimBmp;
547 Bitmap::ReleaseAccess( pAcc8 );
548 pAcc8 = NULL;
550 if( bGCTransparent )
552 Bitmap::ReleaseAccess( pAcc1 );
553 pAcc1 = NULL;
554 aAnimBmp.aBmpEx = BitmapEx( aBmp8, aBmp1 );
556 else
557 aAnimBmp.aBmpEx = BitmapEx( aBmp8 );
559 aAnimBmp.aPosPix = Point( nImagePosX, nImagePosY );
560 aAnimBmp.aSizePix = Size( nImageWidth, nImageHeight );
561 aAnimBmp.nWait = ( nTimer != 65535 ) ? nTimer : ANIMATION_TIMEOUT_ON_CLICK;
562 aAnimBmp.bUserInput = false;
564 if( nGCDisposalMethod == 2 )
565 aAnimBmp.eDisposal = DISPOSE_BACK;
566 else if( nGCDisposalMethod == 3 )
567 aAnimBmp.eDisposal = DISPOSE_PREVIOUS;
568 else
569 aAnimBmp.eDisposal = DISPOSE_NOT;
571 aAnimation.Insert( aAnimBmp );
573 if( aAnimation.Count() == 1 )
575 aAnimation.SetDisplaySizePixel( Size( nGlobalWidth, nGlobalHeight ) );
576 aAnimation.SetLoopCount( nLoops );
580 const Graphic& GIFReader::GetIntermediateGraphic()
582 // only create intermediate graphic, if data is available
583 // but graphic still not completely read
584 if ( bImGraphicReady && !aAnimation.Count() )
586 Bitmap aBmp;
588 Bitmap::ReleaseAccess( pAcc8 );
590 if ( bGCTransparent )
592 Bitmap::ReleaseAccess( pAcc1 );
593 aImGraphic = BitmapEx( aBmp8, aBmp1 );
595 pAcc1 = aBmp1.AcquireWriteAccess();
596 bStatus = bStatus && ( pAcc1 != NULL );
598 else
599 aImGraphic = aBmp8;
601 pAcc8 = aBmp8.AcquireWriteAccess();
602 bStatus = bStatus && ( pAcc8 != NULL );
605 return aImGraphic;
608 bool GIFReader::ProcessGIF()
610 bool bRead = false;
611 bool bEnd = false;
613 if ( !bStatus )
614 eActAction = ABORT_READING;
616 // set stream to right position
617 rIStm.Seek( nLastPos );
619 switch( eActAction )
621 // read next marker
622 case( MARKER_READING ):
624 sal_uInt8 cByte;
626 rIStm.ReadUChar( cByte );
628 if( rIStm.IsEof() )
629 eActAction = END_READING;
630 else if( NO_PENDING( rIStm ) )
632 bRead = true;
634 if( cByte == '!' )
635 eActAction = EXTENSION_READING;
636 else if( cByte == ',' )
637 eActAction = LOCAL_HEADER_READING;
638 else if( cByte == ';' )
639 eActAction = END_READING;
640 else
641 eActAction = ABORT_READING;
644 break;
646 // read ScreenDescriptor
647 case( GLOBAL_HEADER_READING ):
649 if( ( bRead = ReadGlobalHeader() ) )
651 ClearImageExtensions();
652 eActAction = MARKER_READING;
655 break;
657 // read extension
658 case( EXTENSION_READING ):
660 if( ( bRead = ReadExtension() ) )
661 eActAction = MARKER_READING;
663 break;
665 // read Image-Descriptor
666 case( LOCAL_HEADER_READING ):
668 if( ( bRead = ReadLocalHeader() ) )
670 nYAcc = nImageX = nImageY = 0;
671 eActAction = FIRST_BLOCK_READING;
674 break;
676 // read first data block
677 case( FIRST_BLOCK_READING ):
679 sal_uInt8 cDataSize;
681 rIStm.ReadUChar( cDataSize );
683 if( rIStm.IsEof() )
684 eActAction = ABORT_READING;
685 else if( cDataSize > 12 )
686 bStatus = false;
687 else if( NO_PENDING( rIStm ) )
689 bRead = true;
690 pDecomp = new GIFLZWDecompressor( cDataSize );
691 eActAction = NEXT_BLOCK_READING;
692 bOverreadBlock = false;
694 else
695 eActAction = FIRST_BLOCK_READING;
697 break;
699 // read next data block
700 case( NEXT_BLOCK_READING ):
702 sal_uInt16 nLastX = nImageX;
703 sal_uInt16 nLastY = nImageY;
704 sal_uLong nRet = ReadNextBlock();
706 // Return: 0:Pending / 1:OK; / 2:OK and last block: / 3:EOI / 4:HardAbort
707 if( nRet )
709 bRead = true;
711 if ( nRet == 1UL )
713 bImGraphicReady = true;
714 eActAction = NEXT_BLOCK_READING;
715 bOverreadBlock = false;
717 else
719 if( nRet == 2UL )
721 delete pDecomp;
722 CreateNewBitmaps();
723 eActAction = MARKER_READING;
724 ClearImageExtensions();
726 else if( nRet == 3UL )
728 eActAction = NEXT_BLOCK_READING;
729 bOverreadBlock = true;
731 else
733 delete pDecomp;
734 CreateNewBitmaps();
735 eActAction = ABORT_READING;
736 ClearImageExtensions();
740 else
742 nImageX = nLastX;
743 nImageY = nLastY;
746 break;
748 // an error occurred
749 case( ABORT_READING ):
751 bEnd = true;
752 eActAction = END_READING;
754 break;
756 default:
757 break;
760 // set stream to right position,
761 // if data could be read put it a the old
762 // position otherwise at the actual one
763 if( bRead || bEnd )
764 nLastPos = rIStm.Tell();
766 return bRead;
769 ReadState GIFReader::ReadGIF( Graphic& rGraphic )
771 ReadState eReadState;
773 bStatus = true;
775 while( ProcessGIF() && ( eActAction != END_READING ) ) {}
777 if( !bStatus )
778 eReadState = GIFREAD_ERROR;
779 else if( eActAction == END_READING )
780 eReadState = GIFREAD_OK;
781 else
783 if ( rIStm.GetError() == ERRCODE_IO_PENDING )
784 rIStm.ResetError();
786 eReadState = GIFREAD_NEED_MORE;
789 if( aAnimation.Count() == 1 )
791 rGraphic = aAnimation.Get( 0 ).aBmpEx;
793 if( nLogWidth100 && nLogHeight100 )
795 rGraphic.SetPrefSize( Size( nLogWidth100, nLogHeight100 ) );
796 rGraphic.SetPrefMapMode( MAP_100TH_MM );
799 else
800 rGraphic = aAnimation;
802 return eReadState;
805 bool ImportGIF( SvStream & rStm, Graphic& rGraphic )
807 GIFReader* pGIFReader = static_cast<GIFReader*>(rGraphic.GetContext());
808 SvStreamEndian nOldFormat = rStm.GetEndian();
809 ReadState eReadState;
810 bool bRet = true;
812 rStm.SetEndian( SvStreamEndian::LITTLE );
814 if( !pGIFReader )
815 pGIFReader = new GIFReader( rStm );
817 rGraphic.SetContext( NULL );
818 eReadState = pGIFReader->ReadGIF( rGraphic );
820 if( eReadState == GIFREAD_ERROR )
822 bRet = false;
823 delete pGIFReader;
825 else if( eReadState == GIFREAD_OK )
826 delete pGIFReader;
827 else
829 rGraphic = pGIFReader->GetIntermediateGraphic();
830 rGraphic.SetContext( pGIFReader );
833 rStm.SetEndian( nOldFormat );
835 return bRet;
838 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */