bump product version to 4.1.6.2
[LibreOffice.git] / vcl / source / filter / igif / gifread.cxx
blobf4f52949c268e2e63c77cd0a3867689059d320de
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 .
21 #define _GIFPRIVATE
23 #include "decode.hxx"
24 #include "gifread.hxx"
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 nLoops ( 1 ),
44 eActAction ( GLOBAL_HEADER_READING ),
45 bGCTransparent ( sal_False ),
46 bImGraphicReady ( sal_False )
48 maUpperName = OUString("SVIGIF");
49 pSrcBuf = new sal_uInt8[ 256 ];
50 ClearImageExtensions();
54 GIFReader::~GIFReader()
56 aImGraphic.SetContext( NULL );
58 if( pAcc1 )
59 aBmp1.ReleaseAccess( pAcc1 );
61 if( pAcc8 )
62 aBmp8.ReleaseAccess( pAcc8 );
64 delete[] pSrcBuf;
67 void GIFReader::ClearImageExtensions()
69 nGCDisposalMethod = 0;
70 bGCTransparent = sal_False;
71 nTimer = 0;
74 sal_Bool GIFReader::CreateBitmaps( long nWidth, long nHeight, BitmapPalette* pPal,
75 sal_Bool bWatchForBackgroundColor )
77 const Size aSize( nWidth, nHeight );
79 #ifdef __LP64__
80 // Don't bother allocating a bitmap of a size that would fail on a
81 // 32-bit system. We have at least one unit tests that is expected
82 // to fail (loading a 65535*65535 size GIF
83 // svtools/qa/cppunit/data/gif/fail/CVE-2008-5937-1.gif), but
84 // which doesn't fail on 64-bit Mac OS X at least. Why the loading
85 // fails on 64-bit Linux, no idea.
86 if (nWidth >= 64000 && nHeight >= 64000)
88 bStatus = sal_False;
89 return bStatus;
91 #endif
93 if( bGCTransparent )
95 const Color aWhite( COL_WHITE );
97 aBmp1 = Bitmap( aSize, 1 );
99 if( !aAnimation.Count() )
100 aBmp1.Erase( aWhite );
102 pAcc1 = aBmp1.AcquireWriteAccess();
104 if( pAcc1 )
106 cTransIndex1 = (sal_uInt8) pAcc1->GetBestPaletteIndex( aWhite );
107 cNonTransIndex1 = cTransIndex1 ? 0 : 1;
109 else
110 bStatus = sal_False;
113 if( bStatus )
115 aBmp8 = Bitmap( aSize, 8, pPal );
117 if( !!aBmp8 && bWatchForBackgroundColor && aAnimation.Count() )
118 aBmp8.Erase( (*pPal)[ nBackgroundColor ] );
119 else
120 aBmp8.Erase( Color( COL_WHITE ) );
122 pAcc8 = aBmp8.AcquireWriteAccess();
123 bStatus = ( pAcc8 != NULL );
126 return bStatus;
129 sal_Bool GIFReader::ReadGlobalHeader()
131 char pBuf[ 7 ];
132 sal_uInt8 nRF;
133 sal_uInt8 nAspect;
134 sal_Bool bRet = sal_False;
136 rIStm.Read( pBuf, 6 );
137 if( NO_PENDING( rIStm ) )
139 pBuf[ 6 ] = 0;
140 if( !strcmp( pBuf, "GIF87a" ) || !strcmp( pBuf, "GIF89a" ) )
142 rIStm.Read( pBuf, 7 );
143 if( NO_PENDING( rIStm ) )
145 SvMemoryStream aMemStm;
147 aMemStm.SetBuffer( pBuf, 7, sal_False, 7 );
148 aMemStm >> nGlobalWidth;
149 aMemStm >> nGlobalHeight;
150 aMemStm >> nRF;
151 aMemStm >> nBackgroundColor;
152 aMemStm >> nAspect;
154 bGlobalPalette = (sal_Bool) ( nRF & 0x80 );
156 if( bGlobalPalette )
157 ReadPaletteEntries( &aGPalette, 1 << ( ( nRF & 7 ) + 1 ) );
158 else
159 nBackgroundColor = 0;
161 if( NO_PENDING( rIStm ) )
162 bRet = sal_True;
165 else
166 bStatus = sal_False;
169 return bRet;
172 void GIFReader::ReadPaletteEntries( BitmapPalette* pPal, sal_uLong nCount )
174 const sal_uLong nLen = 3UL * nCount;
175 sal_uInt8* pBuf = new sal_uInt8[ nLen ];
177 rIStm.Read( pBuf, nLen );
178 if( NO_PENDING( rIStm ) )
180 sal_uInt8* pTmp = pBuf;
182 for( sal_uLong i = 0UL; i < nCount; )
184 BitmapColor& rColor = (*pPal)[ (sal_uInt16) i++ ];
186 rColor.SetRed( *pTmp++ );
187 rColor.SetGreen( *pTmp++ );
188 rColor.SetBlue( *pTmp++ );
191 // nach Moeglichkeit noch einige Standardfarben unterbringen
192 if( nCount < 256UL )
194 (*pPal)[ 255UL ] = Color( COL_WHITE );
196 if( nCount < 255UL )
197 (*pPal)[ 254UL ] = Color( COL_BLACK );
201 delete[] pBuf;
204 sal_Bool GIFReader::ReadExtension()
206 sal_uInt8 cFunction;
207 sal_uInt8 cSize;
208 sal_uInt8 cByte;
209 sal_Bool bRet = sal_False;
210 sal_Bool bOverreadDataBlocks = sal_False;
212 // Extension-Label
213 rIStm >> cFunction;
214 if( NO_PENDING( rIStm ) )
216 // Block-Laenge
217 rIStm >> cSize;
219 switch( cFunction )
221 // 'Graphic Control Extension'
222 case( 0xf9 ) :
224 sal_uInt8 cFlags;
226 rIStm >> cFlags;
227 rIStm >> nTimer;
228 rIStm >> nGCTransparentIndex;
229 rIStm >> cByte;
231 if ( NO_PENDING( rIStm ) )
233 nGCDisposalMethod = ( cFlags >> 2) & 7;
234 bGCTransparent = ( cFlags & 1 ) ? sal_True : sal_False;
235 bStatus = ( cSize == 4 ) && ( cByte == 0 );
236 bRet = sal_True;
239 break;
241 // Application-Extension
242 case ( 0xff ) :
244 if ( NO_PENDING( rIStm ) )
246 // default diese Extension ueberlesen
247 bOverreadDataBlocks = sal_True;
249 // Appl.-Extension hat Laenge 11
250 if ( cSize == 0x0b )
252 OString aAppId = read_uInt8s_ToOString(rIStm, 8);
253 OString aAppCode = read_uInt8s_ToOString(rIStm, 3);
254 rIStm >> cSize;
256 // NetScape-Extension
257 if( aAppId == "NETSCAPE" && aAppCode == "2.0" && cSize == 3 )
259 rIStm >> cByte;
261 // Loop-Extension
262 if ( cByte == 0x01 )
264 rIStm >> cByte;
265 nLoops = cByte;
266 rIStm >> cByte;
267 nLoops |= ( (sal_uInt16) cByte << 8 );
268 rIStm >> cByte;
270 bStatus = ( cByte == 0 );
271 bRet = NO_PENDING( rIStm );
272 bOverreadDataBlocks = sal_False;
274 // Netscape interpretiert den LoopCount
275 // als reine Anzahl der _Wiederholungen_;
276 // bei uns ist es die Gesamtanzahl der
277 // Durchlaeufe
278 if( nLoops )
279 nLoops++;
281 else
282 rIStm.SeekRel( -1 );
284 else if ( aAppId == "STARDIV " && aAppCode == "5.0" && cSize == 9 )
286 rIStm >> cByte;
288 // Loop-Extension
289 if ( cByte == 0x01 )
291 rIStm >> nLogWidth100 >> nLogHeight100;
292 rIStm >> cByte;
293 bStatus = ( cByte == 0 );
294 bRet = NO_PENDING( rIStm );
295 bOverreadDataBlocks = sal_False;
297 else
298 rIStm.SeekRel( -1 );
304 break;
306 // alles andere ueberlesen
307 default:
308 bOverreadDataBlocks = sal_True;
309 break;
312 // Sub-Blocks ueberlesen
313 if ( bOverreadDataBlocks )
315 bRet = sal_True;
316 while( cSize && bStatus && !rIStm.IsEof() )
318 sal_uInt16 nCount = (sal_uInt16) cSize + 1;
319 char* pBuffer = new char[ nCount ];
321 bRet = sal_False;
322 rIStm.Read( pBuffer, nCount );
323 if( NO_PENDING( rIStm ) )
325 cSize = (sal_uInt8) pBuffer[ cSize ];
326 bRet = sal_True;
328 else
329 cSize = 0;
331 delete[] pBuffer;
336 return bRet;
339 sal_Bool GIFReader::ReadLocalHeader()
341 sal_uInt8 pBuf[ 9 ];
342 sal_Bool bRet = sal_False;
344 rIStm.Read( pBuf, 9 );
345 if( NO_PENDING( rIStm ) )
347 SvMemoryStream aMemStm;
348 BitmapPalette* pPal;
349 sal_uInt8 nFlags;
351 aMemStm.SetBuffer( (char*) pBuf, 9, sal_False, 9 );
352 aMemStm >> nImagePosX;
353 aMemStm >> nImagePosY;
354 aMemStm >> nImageWidth;
355 aMemStm >> nImageHeight;
356 aMemStm >> nFlags;
358 // Falls Interlaced, ersten Startwert vorgeben
359 bInterlaced = ( ( nFlags & 0x40 ) == 0x40 );
360 nLastInterCount = 7;
361 nLastImageY = 0;
363 if( nFlags & 0x80 )
365 pPal = &aLPalette;
366 ReadPaletteEntries( pPal, 1 << ( (nFlags & 7 ) + 1 ) );
368 else
369 pPal = &aGPalette;
371 // Falls alles soweit eingelesen werden konnte, kann
372 // nun das lokale Bild angelegt werden;
373 // es wird uebergeben, ob der BackgroundColorIndex evtl.
374 // beruecksichtigt werden soll ( wenn Globale Farbtab. und
375 // diese auch fuer dieses Bild gilt )
376 if( NO_PENDING( rIStm ) )
378 CreateBitmaps( nImageWidth, nImageHeight, pPal, bGlobalPalette && ( pPal == &aGPalette ) );
379 bRet = sal_True;
383 return bRet;
386 sal_uLong GIFReader::ReadNextBlock()
388 sal_uLong nRet = 0UL;
389 sal_uLong nRead;
390 sal_uInt8 cBlockSize;
392 rIStm >> cBlockSize;
394 if ( rIStm.IsEof() )
395 nRet = 4UL;
396 else if ( NO_PENDING( rIStm ) )
398 if ( cBlockSize == 0 )
399 nRet = 2UL;
400 else
402 rIStm.Read( pSrcBuf, cBlockSize );
404 if( NO_PENDING( rIStm ) )
406 if( bOverreadBlock )
407 nRet = 3UL;
408 else
410 sal_Bool bEOI;
411 HPBYTE pTarget = pDecomp->DecompressBlock( pSrcBuf, cBlockSize, nRead, bEOI );
413 nRet = ( bEOI ? 3 : 1 );
415 if( nRead && !bOverreadBlock )
416 FillImages( pTarget, nRead );
418 rtl_freeMemory( pTarget );
424 return nRet;
427 void GIFReader::FillImages( HPBYTE pBytes, sal_uLong nCount )
429 for( sal_uLong i = 0UL; i < nCount; i++ )
431 if( nImageX >= nImageWidth )
433 if( bInterlaced )
435 long nT1;
437 // falls Interlaced, werden die Zeilen kopiert
438 if( nLastInterCount )
440 long nMinY = std::min( (long) nLastImageY + 1, (long) nImageHeight - 1 );
441 long nMaxY = std::min( (long) nLastImageY + nLastInterCount, (long) nImageHeight - 1 );
443 // letzte gelesene Zeile kopieren, wenn Zeilen
444 // nicht zusanmmenfallen ( kommt vorm wenn wir am Ende des Bildes sind )
445 if( ( nMinY > nLastImageY ) && ( nLastImageY < ( nImageHeight - 1 ) ) )
447 HPBYTE pScanline8 = pAcc8->GetScanline( nYAcc );
448 sal_uLong nSize8 = pAcc8->GetScanlineSize();
449 HPBYTE pScanline1 = 0;
450 sal_uLong nSize1 = 0;
452 if( bGCTransparent )
454 pScanline1 = pAcc1->GetScanline( nYAcc );
455 nSize1 = pAcc1->GetScanlineSize();
458 for( long j = nMinY; j <= nMaxY; j++ )
460 memcpy( pAcc8->GetScanline( j ), pScanline8, nSize8 );
462 if( bGCTransparent )
463 memcpy( pAcc1->GetScanline( j ), pScanline1, nSize1 );
468 nT1 = ( ++nImageY ) << 3;
469 nLastInterCount = 7;
471 if( nT1 >= nImageHeight )
473 long nT2 = nImageY - ( ( nImageHeight + 7 ) >> 3 );
474 nT1 = ( nT2 << 3 ) + 4;
475 nLastInterCount = 3;
477 if( nT1 >= nImageHeight )
479 nT2 -= ( nImageHeight + 3 ) >> 3;
480 nT1 = ( nT2 << 2 ) + 2;
481 nLastInterCount = 1;
483 if( nT1 >= nImageHeight )
485 nT2 -= ( nImageHeight + 1 ) >> 2;
486 nT1 = ( nT2 << 1 ) + 1;
487 nLastInterCount = 0;
492 nLastImageY = (sal_uInt16) nT1;
493 nYAcc = nT1;
495 else
497 nLastImageY = ++nImageY;
498 nYAcc = nImageY;
501 // Zeile faengt von vorne an
502 nImageX = 0;
505 if( nImageY < nImageHeight )
507 const sal_uInt8 cTmp = pBytes[ i ];
509 if( bGCTransparent )
511 if( cTmp == nGCTransparentIndex )
512 pAcc1->SetPixelIndex( nYAcc, nImageX++, cTransIndex1 );
513 else
515 pAcc8->SetPixelIndex( nYAcc, nImageX, cTmp );
516 pAcc1->SetPixelIndex( nYAcc, nImageX++, cNonTransIndex1 );
519 else
520 pAcc8->SetPixelIndex( nYAcc, nImageX++, cTmp );
522 else
524 bOverreadBlock = sal_True;
525 break;
530 void GIFReader::CreateNewBitmaps()
532 AnimationBitmap aAnimBmp;
534 aBmp8.ReleaseAccess( pAcc8 );
535 pAcc8 = NULL;
537 if( bGCTransparent )
539 aBmp1.ReleaseAccess( pAcc1 );
540 pAcc1 = NULL;
541 aAnimBmp.aBmpEx = BitmapEx( aBmp8, aBmp1 );
543 else
544 aAnimBmp.aBmpEx = BitmapEx( aBmp8 );
546 aAnimBmp.aPosPix = Point( nImagePosX, nImagePosY );
547 aAnimBmp.aSizePix = Size( nImageWidth, nImageHeight );
548 aAnimBmp.nWait = ( nTimer != 65535 ) ? nTimer : ANIMATION_TIMEOUT_ON_CLICK;
549 aAnimBmp.bUserInput = sal_False;
551 if( nGCDisposalMethod == 2 )
552 aAnimBmp.eDisposal = DISPOSE_BACK;
553 else if( nGCDisposalMethod == 3 )
554 aAnimBmp.eDisposal = DISPOSE_PREVIOUS;
555 else
556 aAnimBmp.eDisposal = DISPOSE_NOT;
558 aAnimation.Insert( aAnimBmp );
560 if( aAnimation.Count() == 1 )
562 aAnimation.SetDisplaySizePixel( Size( nGlobalWidth, nGlobalHeight ) );
563 aAnimation.SetLoopCount( nLoops );
567 const Graphic& GIFReader::GetIntermediateGraphic()
569 // Intermediate-Graphic nur erzeugen, wenn schon
570 // Daten vorliegen, aber die Graphic noch nicht
571 // vollstaendig eingelesen wurde
572 if ( bImGraphicReady && !aAnimation.Count() )
574 Bitmap aBmp;
576 aBmp8.ReleaseAccess( pAcc8 );
578 if ( bGCTransparent )
580 aBmp1.ReleaseAccess( pAcc1 );
581 aImGraphic = BitmapEx( aBmp8, aBmp1 );
583 pAcc1 = aBmp1.AcquireWriteAccess();
584 bStatus = bStatus && ( pAcc1 != NULL );
586 else
587 aImGraphic = aBmp8;
589 pAcc8 = aBmp8.AcquireWriteAccess();
590 bStatus = bStatus && ( pAcc8 != NULL );
593 return aImGraphic;
596 sal_Bool GIFReader::ProcessGIF()
598 sal_Bool bRead = sal_False;
599 sal_Bool bEnd = sal_False;
601 if ( !bStatus )
602 eActAction = ABORT_READING;
604 // Stream an die richtige Stelle bringen
605 rIStm.Seek( nLastPos );
607 switch( eActAction )
609 // naechsten Marker lesen
610 case( MARKER_READING ):
612 sal_uInt8 cByte;
614 rIStm >> cByte;
616 if( rIStm.IsEof() )
617 eActAction = END_READING;
618 else if( NO_PENDING( rIStm ) )
620 bRead = sal_True;
622 if( cByte == '!' )
623 eActAction = EXTENSION_READING;
624 else if( cByte == ',' )
625 eActAction = LOCAL_HEADER_READING;
626 else if( cByte == ';' )
627 eActAction = END_READING;
628 else
629 eActAction = ABORT_READING;
632 break;
634 // ScreenDescriptor lesen
635 case( GLOBAL_HEADER_READING ):
637 if( ( bRead = ReadGlobalHeader() ) == sal_True )
639 ClearImageExtensions();
640 eActAction = MARKER_READING;
643 break;
646 // Extension lesen
647 case( EXTENSION_READING ):
649 if( ( bRead = ReadExtension() ) == sal_True )
650 eActAction = MARKER_READING;
652 break;
655 // Image-Descriptor lesen
656 case( LOCAL_HEADER_READING ):
658 if( ( bRead = ReadLocalHeader() ) == sal_True )
660 nYAcc = nImageX = nImageY = 0;
661 eActAction = FIRST_BLOCK_READING;
664 break;
667 // ersten Datenblock lesen
668 case( FIRST_BLOCK_READING ):
670 sal_uInt8 cDataSize;
672 rIStm >> cDataSize;
674 if( rIStm.IsEof() )
675 eActAction = ABORT_READING;
676 else if( cDataSize > 12 )
677 bStatus = sal_False;
678 else if( NO_PENDING( rIStm ) )
680 bRead = sal_True;
681 pDecomp = new GIFLZWDecompressor( cDataSize );
682 eActAction = NEXT_BLOCK_READING;
683 bOverreadBlock = sal_False;
685 else
686 eActAction = FIRST_BLOCK_READING;
688 break;
690 // naechsten Datenblock lesen
691 case( NEXT_BLOCK_READING ):
693 sal_uInt16 nLastX = nImageX;
694 sal_uInt16 nLastY = nImageY;
695 sal_uLong nRet = ReadNextBlock();
697 // Return: 0:Pending / 1:OK; / 2:OK und letzter Block: / 3:EOI / 4:HardAbort
698 if( nRet )
700 bRead = sal_True;
702 if ( nRet == 1UL )
704 bImGraphicReady = sal_True;
705 eActAction = NEXT_BLOCK_READING;
706 bOverreadBlock = sal_False;
708 else
710 if( nRet == 2UL )
712 delete pDecomp;
713 CreateNewBitmaps();
714 eActAction = MARKER_READING;
715 ClearImageExtensions();
717 else if( nRet == 3UL )
719 eActAction = NEXT_BLOCK_READING;
720 bOverreadBlock = sal_True;
722 else
724 delete pDecomp;
725 CreateNewBitmaps();
726 eActAction = ABORT_READING;
727 ClearImageExtensions();
731 else
733 nImageX = nLastX;
734 nImageY = nLastY;
737 break;
739 // ein Fehler trat auf
740 case( ABORT_READING ):
742 bEnd = sal_True;
743 eActAction = END_READING;
745 break;
747 default:
748 break;
751 // Stream an die richtige Stelle bringen,
752 // falls Daten gelesen werden konnten
753 // entweder alte Position oder aktuelle Position
754 if( bRead || bEnd )
755 nLastPos = rIStm.Tell();
757 return bRead;
760 ReadState GIFReader::ReadGIF( Graphic& rGraphic )
762 ReadState eReadState;
764 bStatus = sal_True;
766 while( ProcessGIF() && ( eActAction != END_READING ) ) {}
768 if( !bStatus )
769 eReadState = GIFREAD_ERROR;
770 else if( eActAction == END_READING )
771 eReadState = GIFREAD_OK;
772 else
774 if ( rIStm.GetError() == ERRCODE_IO_PENDING )
775 rIStm.ResetError();
777 eReadState = GIFREAD_NEED_MORE;
780 if( aAnimation.Count() == 1 )
782 rGraphic = aAnimation.Get( 0 ).aBmpEx;
784 if( nLogWidth100 && nLogHeight100 )
786 rGraphic.SetPrefSize( Size( nLogWidth100, nLogHeight100 ) );
787 rGraphic.SetPrefMapMode( MAP_100TH_MM );
790 else
791 rGraphic = aAnimation;
793 return eReadState;
796 sal_Bool ImportGIF( SvStream & rStm, Graphic& rGraphic )
798 GIFReader* pGIFReader = (GIFReader*) rGraphic.GetContext();
799 sal_uInt16 nOldFormat = rStm.GetNumberFormatInt();
800 ReadState eReadState;
801 sal_Bool bRet = sal_True;
803 rStm.SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN );
805 if( !pGIFReader )
806 pGIFReader = new GIFReader( rStm );
808 rGraphic.SetContext( NULL );
809 eReadState = pGIFReader->ReadGIF( rGraphic );
811 if( eReadState == GIFREAD_ERROR )
813 bRet = sal_False;
814 delete pGIFReader;
816 else if( eReadState == GIFREAD_OK )
817 delete pGIFReader;
818 else
820 rGraphic = pGIFReader->GetIntermediateGraphic();
821 rGraphic.SetContext( pGIFReader );
824 rStm.SetNumberFormatInt( nOldFormat );
826 return bRet;
829 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */