1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
24 #include "gifread.hxx"
26 #define NO_PENDING( rStm ) ( ( rStm ).GetError() != ERRCODE_IO_PENDING )
28 GIFReader::GIFReader( SvStream
& rStm
) :
36 nLastPos ( rStm
.Tell() ),
38 nLogHeight100 ( 0UL ),
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
);
59 aBmp1
.ReleaseAccess( pAcc1
);
62 aBmp8
.ReleaseAccess( pAcc8
);
67 void GIFReader::ClearImageExtensions()
69 nGCDisposalMethod
= 0;
70 bGCTransparent
= sal_False
;
74 sal_Bool
GIFReader::CreateBitmaps( long nWidth
, long nHeight
, BitmapPalette
* pPal
,
75 sal_Bool bWatchForBackgroundColor
)
77 const Size
aSize( nWidth
, nHeight
);
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)
95 const Color
aWhite( COL_WHITE
);
97 aBmp1
= Bitmap( aSize
, 1 );
99 if( !aAnimation
.Count() )
100 aBmp1
.Erase( aWhite
);
102 pAcc1
= aBmp1
.AcquireWriteAccess();
106 cTransIndex1
= (sal_uInt8
) pAcc1
->GetBestPaletteIndex( aWhite
);
107 cNonTransIndex1
= cTransIndex1
? 0 : 1;
115 aBmp8
= Bitmap( aSize
, 8, pPal
);
117 if( !!aBmp8
&& bWatchForBackgroundColor
&& aAnimation
.Count() )
118 aBmp8
.Erase( (*pPal
)[ nBackgroundColor
] );
120 aBmp8
.Erase( Color( COL_WHITE
) );
122 pAcc8
= aBmp8
.AcquireWriteAccess();
123 bStatus
= ( pAcc8
!= NULL
);
129 sal_Bool
GIFReader::ReadGlobalHeader()
134 sal_Bool bRet
= sal_False
;
136 rIStm
.Read( pBuf
, 6 );
137 if( NO_PENDING( rIStm
) )
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
;
151 aMemStm
>> nBackgroundColor
;
154 bGlobalPalette
= (sal_Bool
) ( nRF
& 0x80 );
157 ReadPaletteEntries( &aGPalette
, 1 << ( ( nRF
& 7 ) + 1 ) );
159 nBackgroundColor
= 0;
161 if( NO_PENDING( rIStm
) )
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
194 (*pPal
)[ 255UL ] = Color( COL_WHITE
);
197 (*pPal
)[ 254UL ] = Color( COL_BLACK
);
204 sal_Bool
GIFReader::ReadExtension()
209 sal_Bool bRet
= sal_False
;
210 sal_Bool bOverreadDataBlocks
= sal_False
;
214 if( NO_PENDING( rIStm
) )
221 // 'Graphic Control Extension'
228 rIStm
>> nGCTransparentIndex
;
231 if ( NO_PENDING( rIStm
) )
233 nGCDisposalMethod
= ( cFlags
>> 2) & 7;
234 bGCTransparent
= ( cFlags
& 1 ) ? sal_True
: sal_False
;
235 bStatus
= ( cSize
== 4 ) && ( cByte
== 0 );
241 // Application-Extension
244 if ( NO_PENDING( rIStm
) )
246 // default diese Extension ueberlesen
247 bOverreadDataBlocks
= sal_True
;
249 // Appl.-Extension hat Laenge 11
252 OString aAppId
= read_uInt8s_ToOString(rIStm
, 8);
253 OString aAppCode
= read_uInt8s_ToOString(rIStm
, 3);
256 // NetScape-Extension
257 if( aAppId
== "NETSCAPE" && aAppCode
== "2.0" && cSize
== 3 )
267 nLoops
|= ( (sal_uInt16
) cByte
<< 8 );
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
284 else if ( aAppId
== "STARDIV " && aAppCode
== "5.0" && cSize
== 9 )
291 rIStm
>> nLogWidth100
>> nLogHeight100
;
293 bStatus
= ( cByte
== 0 );
294 bRet
= NO_PENDING( rIStm
);
295 bOverreadDataBlocks
= sal_False
;
306 // alles andere ueberlesen
308 bOverreadDataBlocks
= sal_True
;
312 // Sub-Blocks ueberlesen
313 if ( bOverreadDataBlocks
)
316 while( cSize
&& bStatus
&& !rIStm
.IsEof() )
318 sal_uInt16 nCount
= (sal_uInt16
) cSize
+ 1;
319 char* pBuffer
= new char[ nCount
];
322 rIStm
.Read( pBuffer
, nCount
);
323 if( NO_PENDING( rIStm
) )
325 cSize
= (sal_uInt8
) pBuffer
[ cSize
];
339 sal_Bool
GIFReader::ReadLocalHeader()
342 sal_Bool bRet
= sal_False
;
344 rIStm
.Read( pBuf
, 9 );
345 if( NO_PENDING( rIStm
) )
347 SvMemoryStream aMemStm
;
351 aMemStm
.SetBuffer( (char*) pBuf
, 9, sal_False
, 9 );
352 aMemStm
>> nImagePosX
;
353 aMemStm
>> nImagePosY
;
354 aMemStm
>> nImageWidth
;
355 aMemStm
>> nImageHeight
;
358 // Falls Interlaced, ersten Startwert vorgeben
359 bInterlaced
= ( ( nFlags
& 0x40 ) == 0x40 );
366 ReadPaletteEntries( pPal
, 1 << ( (nFlags
& 7 ) + 1 ) );
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
) );
386 sal_uLong
GIFReader::ReadNextBlock()
388 sal_uLong nRet
= 0UL;
390 sal_uInt8 cBlockSize
;
396 else if ( NO_PENDING( rIStm
) )
398 if ( cBlockSize
== 0 )
402 rIStm
.Read( pSrcBuf
, cBlockSize
);
404 if( NO_PENDING( rIStm
) )
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
);
427 void GIFReader::FillImages( HPBYTE pBytes
, sal_uLong nCount
)
429 for( sal_uLong i
= 0UL; i
< nCount
; i
++ )
431 if( nImageX
>= nImageWidth
)
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;
454 pScanline1
= pAcc1
->GetScanline( nYAcc
);
455 nSize1
= pAcc1
->GetScanlineSize();
458 for( long j
= nMinY
; j
<= nMaxY
; j
++ )
460 memcpy( pAcc8
->GetScanline( j
), pScanline8
, nSize8
);
463 memcpy( pAcc1
->GetScanline( j
), pScanline1
, nSize1
);
468 nT1
= ( ++nImageY
) << 3;
471 if( nT1
>= nImageHeight
)
473 long nT2
= nImageY
- ( ( nImageHeight
+ 7 ) >> 3 );
474 nT1
= ( nT2
<< 3 ) + 4;
477 if( nT1
>= nImageHeight
)
479 nT2
-= ( nImageHeight
+ 3 ) >> 3;
480 nT1
= ( nT2
<< 2 ) + 2;
483 if( nT1
>= nImageHeight
)
485 nT2
-= ( nImageHeight
+ 1 ) >> 2;
486 nT1
= ( nT2
<< 1 ) + 1;
492 nLastImageY
= (sal_uInt16
) nT1
;
497 nLastImageY
= ++nImageY
;
501 // Zeile faengt von vorne an
505 if( nImageY
< nImageHeight
)
507 const sal_uInt8 cTmp
= pBytes
[ i
];
511 if( cTmp
== nGCTransparentIndex
)
512 pAcc1
->SetPixelIndex( nYAcc
, nImageX
++, cTransIndex1
);
515 pAcc8
->SetPixelIndex( nYAcc
, nImageX
, cTmp
);
516 pAcc1
->SetPixelIndex( nYAcc
, nImageX
++, cNonTransIndex1
);
520 pAcc8
->SetPixelIndex( nYAcc
, nImageX
++, cTmp
);
524 bOverreadBlock
= sal_True
;
530 void GIFReader::CreateNewBitmaps()
532 AnimationBitmap aAnimBmp
;
534 aBmp8
.ReleaseAccess( pAcc8
);
539 aBmp1
.ReleaseAccess( pAcc1
);
541 aAnimBmp
.aBmpEx
= BitmapEx( aBmp8
, aBmp1
);
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
;
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() )
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
);
589 pAcc8
= aBmp8
.AcquireWriteAccess();
590 bStatus
= bStatus
&& ( pAcc8
!= NULL
);
596 sal_Bool
GIFReader::ProcessGIF()
598 sal_Bool bRead
= sal_False
;
599 sal_Bool bEnd
= sal_False
;
602 eActAction
= ABORT_READING
;
604 // Stream an die richtige Stelle bringen
605 rIStm
.Seek( nLastPos
);
609 // naechsten Marker lesen
610 case( MARKER_READING
):
617 eActAction
= END_READING
;
618 else if( NO_PENDING( rIStm
) )
623 eActAction
= EXTENSION_READING
;
624 else if( cByte
== ',' )
625 eActAction
= LOCAL_HEADER_READING
;
626 else if( cByte
== ';' )
627 eActAction
= END_READING
;
629 eActAction
= ABORT_READING
;
634 // ScreenDescriptor lesen
635 case( GLOBAL_HEADER_READING
):
637 if( ( bRead
= ReadGlobalHeader() ) == sal_True
)
639 ClearImageExtensions();
640 eActAction
= MARKER_READING
;
647 case( EXTENSION_READING
):
649 if( ( bRead
= ReadExtension() ) == sal_True
)
650 eActAction
= MARKER_READING
;
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
;
667 // ersten Datenblock lesen
668 case( FIRST_BLOCK_READING
):
675 eActAction
= ABORT_READING
;
676 else if( cDataSize
> 12 )
678 else if( NO_PENDING( rIStm
) )
681 pDecomp
= new GIFLZWDecompressor( cDataSize
);
682 eActAction
= NEXT_BLOCK_READING
;
683 bOverreadBlock
= sal_False
;
686 eActAction
= FIRST_BLOCK_READING
;
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
704 bImGraphicReady
= sal_True
;
705 eActAction
= NEXT_BLOCK_READING
;
706 bOverreadBlock
= sal_False
;
714 eActAction
= MARKER_READING
;
715 ClearImageExtensions();
717 else if( nRet
== 3UL )
719 eActAction
= NEXT_BLOCK_READING
;
720 bOverreadBlock
= sal_True
;
726 eActAction
= ABORT_READING
;
727 ClearImageExtensions();
739 // ein Fehler trat auf
740 case( ABORT_READING
):
743 eActAction
= END_READING
;
751 // Stream an die richtige Stelle bringen,
752 // falls Daten gelesen werden konnten
753 // entweder alte Position oder aktuelle Position
755 nLastPos
= rIStm
.Tell();
760 ReadState
GIFReader::ReadGIF( Graphic
& rGraphic
)
762 ReadState eReadState
;
766 while( ProcessGIF() && ( eActAction
!= END_READING
) ) {}
769 eReadState
= GIFREAD_ERROR
;
770 else if( eActAction
== END_READING
)
771 eReadState
= GIFREAD_OK
;
774 if ( rIStm
.GetError() == ERRCODE_IO_PENDING
)
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
);
791 rGraphic
= aAnimation
;
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
);
806 pGIFReader
= new GIFReader( rStm
);
808 rGraphic
.SetContext( NULL
);
809 eReadState
= pGIFReader
->ReadGIF( rGraphic
);
811 if( eReadState
== GIFREAD_ERROR
)
816 else if( eReadState
== GIFREAD_OK
)
820 rGraphic
= pGIFReader
->GetIntermediateGraphic();
821 rGraphic
.SetContext( pGIFReader
);
824 rStm
.SetNumberFormatInt( nOldFormat
);
829 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */