1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: gifread.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_svtools.hxx"
37 #include "gifread.hxx"
43 #define NO_PENDING( rStm ) ( ( rStm ).GetError() != ERRCODE_IO_PENDING )
49 GIFReader::GIFReader( SvStream
& rStm
) :
55 nLastPos ( rStm
.Tell() ),
57 nLogHeight100 ( 0UL ),
59 eActAction ( GLOBAL_HEADER_READING
),
60 bGCTransparent ( FALSE
),
61 bImGraphicReady ( FALSE
)
63 maUpperName
= UniString::CreateFromAscii( "SVIGIF", 6 );
64 pSrcBuf
= new BYTE
[ 256 ];
65 ClearImageExtensions();
68 // ------------------------------------------------------------------------
70 GIFReader::~GIFReader()
72 aImGraphic
.SetContext( NULL
);
75 aBmp1
.ReleaseAccess( pAcc1
);
78 aBmp8
.ReleaseAccess( pAcc8
);
83 // ------------------------------------------------------------------------
85 void GIFReader::ClearImageExtensions()
87 nGCDisposalMethod
= 0;
88 bGCTransparent
= FALSE
;
92 // ------------------------------------------------------------------------
94 BOOL
GIFReader::CreateBitmaps( long nWidth
, long nHeight
, BitmapPalette
* pPal
,
95 BOOL bWatchForBackgroundColor
)
97 const Size
aSize( nWidth
, nHeight
);
101 const Color
aWhite( COL_WHITE
);
103 aBmp1
= Bitmap( aSize
, 1 );
105 if( !aAnimation
.Count() )
106 aBmp1
.Erase( aWhite
);
108 pAcc1
= aBmp1
.AcquireWriteAccess();
112 cTransIndex1
= (BYTE
) pAcc1
->GetBestPaletteIndex( aWhite
);
113 cNonTransIndex1
= cTransIndex1
? 0 : 1;
121 aBmp8
= Bitmap( aSize
, 8, pPal
);
123 if( !!aBmp8
&& bWatchForBackgroundColor
&& aAnimation
.Count() )
124 aBmp8
.Erase( (*pPal
)[ nBackgroundColor
] );
126 aBmp8
.Erase( Color( COL_WHITE
) );
128 pAcc8
= aBmp8
.AcquireWriteAccess();
129 bStatus
= ( pAcc8
!= NULL
);
135 // ------------------------------------------------------------------------
137 BOOL
GIFReader::ReadGlobalHeader()
144 rIStm
.Read( pBuf
, 6 );
145 if( NO_PENDING( rIStm
) )
148 if( !strcmp( pBuf
, "GIF87a" ) || !strcmp( pBuf
, "GIF89a" ) )
150 rIStm
.Read( pBuf
, 7 );
151 if( NO_PENDING( rIStm
) )
153 SvMemoryStream aMemStm
;
155 aMemStm
.SetBuffer( pBuf
, 7, FALSE
, 7 );
156 aMemStm
>> nGlobalWidth
;
157 aMemStm
>> nGlobalHeight
;
159 aMemStm
>> nBackgroundColor
;
162 bGlobalPalette
= (BOOL
) ( nRF
& 0x80 );
165 ReadPaletteEntries( &aGPalette
, 1 << ( ( nRF
& 7 ) + 1 ) );
167 nBackgroundColor
= 0;
169 if( NO_PENDING( rIStm
) )
180 // ------------------------------------------------------------------------
182 void GIFReader::ReadPaletteEntries( BitmapPalette
* pPal
, ULONG nCount
)
184 const ULONG nLen
= 3UL * nCount
;
185 BYTE
* pBuf
= new BYTE
[ nLen
];
187 rIStm
.Read( pBuf
, nLen
);
188 if( NO_PENDING( rIStm
) )
192 for( ULONG i
= 0UL; i
< nCount
; )
194 BitmapColor
& rColor
= (*pPal
)[ (USHORT
) i
++ ];
196 rColor
.SetRed( *pTmp
++ );
197 rColor
.SetGreen( *pTmp
++ );
198 rColor
.SetBlue( *pTmp
++ );
201 // nach Moeglichkeit noch einige Standardfarben unterbringen
204 (*pPal
)[ 255UL ] = Color( COL_WHITE
);
207 (*pPal
)[ 254UL ] = Color( COL_BLACK
);
214 // ------------------------------------------------------------------------
216 BOOL
GIFReader::ReadExtension()
222 BOOL bOverreadDataBlocks
= FALSE
;
226 if( NO_PENDING( rIStm
) )
233 // 'Graphic Control Extension'
240 rIStm
>> nGCTransparentIndex
;
243 if ( NO_PENDING( rIStm
) )
245 nGCDisposalMethod
= ( cFlags
>> 2) & 7;
246 bGCTransparent
= ( cFlags
& 1 ) ? TRUE
: FALSE
;
247 bStatus
= ( cSize
== 4 ) && ( cByte
== 0 );
253 // Application-Extension
256 if ( NO_PENDING( rIStm
) )
258 // default diese Extension ueberlesen
259 bOverreadDataBlocks
= TRUE
;
261 // Appl.-Extension hat Laenge 11
267 rIStm
.Read( aAppId
.AllocBuffer( 8 ), 8 );
268 rIStm
.Read( aAppCode
.AllocBuffer( 3 ), 3 );
271 // NetScape-Extension
272 if( aAppId
== "NETSCAPE" && aAppCode
== "2.0" && cSize
== 3 )
282 nLoops
|= ( (USHORT
) cByte
<< 8 );
285 bStatus
= ( cByte
== 0 );
286 bRet
= NO_PENDING( rIStm
);
287 bOverreadDataBlocks
= FALSE
;
289 // Netscape interpretiert den LoopCount
290 // als reine Anzahl der _Wiederholungen_;
291 // bei uns ist es die Gesamtanzahl der
299 else if ( aAppId
== "STARDIV " && aAppCode
== "5.0" && cSize
== 9 )
306 rIStm
>> nLogWidth100
>> nLogHeight100
;
308 bStatus
= ( cByte
== 0 );
309 bRet
= NO_PENDING( rIStm
);
310 bOverreadDataBlocks
= FALSE
;
321 // alles andere ueberlesen
323 bOverreadDataBlocks
= TRUE
;
327 // Sub-Blocks ueberlesen
328 if ( bOverreadDataBlocks
)
331 while( cSize
&& bStatus
&& !rIStm
.IsEof() )
333 USHORT nCount
= (USHORT
) cSize
+ 1;
334 char* pBuffer
= new char[ nCount
];
337 rIStm
.Read( pBuffer
, nCount
);
338 if( NO_PENDING( rIStm
) )
340 cSize
= (BYTE
) pBuffer
[ cSize
];
354 // ------------------------------------------------------------------------
356 BOOL
GIFReader::ReadLocalHeader()
361 rIStm
.Read( pBuf
, 9 );
362 if( NO_PENDING( rIStm
) )
364 SvMemoryStream aMemStm
;
368 aMemStm
.SetBuffer( (char*) pBuf
, 9, FALSE
, 9 );
369 aMemStm
>> nImagePosX
;
370 aMemStm
>> nImagePosY
;
371 aMemStm
>> nImageWidth
;
372 aMemStm
>> nImageHeight
;
375 // Falls Interlaced, ersten Startwert vorgeben
376 bInterlaced
= ( ( nFlags
& 0x40 ) == 0x40 );
383 ReadPaletteEntries( pPal
, 1 << ( (nFlags
& 7 ) + 1 ) );
388 // Falls alles soweit eingelesen werden konnte, kann
389 // nun das lokale Bild angelegt werden;
390 // es wird uebergeben, ob der BackgroundColorIndex evtl.
391 // beruecksichtigt werden soll ( wenn Globale Farbtab. und
392 // diese auch fuer dieses Bild gilt )
393 if( NO_PENDING( rIStm
) )
395 CreateBitmaps( nImageWidth
, nImageHeight
, pPal
, bGlobalPalette
&& ( pPal
== &aGPalette
) );
403 // ------------------------------------------------------------------------
405 ULONG
GIFReader::ReadNextBlock()
415 else if ( NO_PENDING( rIStm
) )
417 if ( cBlockSize
== 0 )
421 rIStm
.Read( pSrcBuf
, cBlockSize
);
423 if( NO_PENDING( rIStm
) )
430 HPBYTE pTarget
= pDecomp
->DecompressBlock( pSrcBuf
, cBlockSize
, nRead
, bEOI
);
432 nRet
= ( bEOI
? 3 : 1 );
434 if( nRead
&& !bOverreadBlock
)
435 FillImages( pTarget
, nRead
);
437 rtl_freeMemory( pTarget
);
446 // ------------------------------------------------------------------------
448 void GIFReader::FillImages( HPBYTE pBytes
, ULONG nCount
)
450 for( ULONG i
= 0UL; i
< nCount
; i
++ )
452 if( nImageX
>= nImageWidth
)
458 // falls Interlaced, werden die Zeilen kopiert
459 if( nLastInterCount
)
461 long nMinY
= Min( (long) nLastImageY
+ 1, (long) nImageHeight
- 1 );
462 long nMaxY
= Min( (long) nLastImageY
+ nLastInterCount
, (long) nImageHeight
- 1 );
464 // letzte gelesene Zeile kopieren, wenn Zeilen
465 // nicht zusanmmenfallen ( kommt vorm wenn wir am Ende des Bildes sind )
466 if( ( nMinY
> nLastImageY
) && ( nLastImageY
< ( nImageHeight
- 1 ) ) )
468 HPBYTE pScanline8
= pAcc8
->GetScanline( nYAcc
);
469 ULONG nSize8
= pAcc8
->GetScanlineSize();
470 HPBYTE pScanline1
= 0;
475 pScanline1
= pAcc1
->GetScanline( nYAcc
);
476 nSize1
= pAcc1
->GetScanlineSize();
479 for( long j
= nMinY
; j
<= nMaxY
; j
++ )
481 memcpy( pAcc8
->GetScanline( j
), pScanline8
, nSize8
);
484 memcpy( pAcc1
->GetScanline( j
), pScanline1
, nSize1
);
489 nT1
= ( ++nImageY
) << 3;
492 if( nT1
>= nImageHeight
)
494 nT2
= nImageY
- ( ( nImageHeight
+ 7 ) >> 3 );
495 nT1
= ( nT2
<< 3 ) + 4;
498 if( nT1
>= nImageHeight
)
500 nT2
-= ( nImageHeight
+ 3 ) >> 3;
501 nT1
= ( nT2
<< 2 ) + 2;
504 if( nT1
>= nImageHeight
)
506 nT2
-= ( nImageHeight
+ 1 ) >> 2;
507 nT1
= ( nT2
<< 1 ) + 1;
513 nLastImageY
= (USHORT
) nT1
;
518 nLastImageY
= ++nImageY
;
522 // Zeile faengt von vorne an
526 if( nImageY
< nImageHeight
)
528 const BYTE cTmp
= pBytes
[ i
];
532 if( cTmp
== nGCTransparentIndex
)
533 pAcc1
->SetPixel( nYAcc
, nImageX
++, cTransIndex1
);
536 pAcc8
->SetPixel( nYAcc
, nImageX
, cTmp
);
537 pAcc1
->SetPixel( nYAcc
, nImageX
++, cNonTransIndex1
);
541 pAcc8
->SetPixel( nYAcc
, nImageX
++, cTmp
);
545 bOverreadBlock
= TRUE
;
551 // ------------------------------------------------------------------------
553 void GIFReader::CreateNewBitmaps()
555 AnimationBitmap aAnimBmp
;
557 aBmp8
.ReleaseAccess( pAcc8
);
562 aBmp1
.ReleaseAccess( pAcc1
);
564 aAnimBmp
.aBmpEx
= BitmapEx( aBmp8
, aBmp1
);
567 aAnimBmp
.aBmpEx
= BitmapEx( aBmp8
);
569 aAnimBmp
.aPosPix
= Point( nImagePosX
, nImagePosY
);
570 aAnimBmp
.aSizePix
= Size( nImageWidth
, nImageHeight
);
571 aAnimBmp
.nWait
= ( nTimer
!= 65535 ) ? nTimer
: ANIMATION_TIMEOUT_ON_CLICK
;
572 aAnimBmp
.bUserInput
= FALSE
;
574 if( nGCDisposalMethod
== 2 )
575 aAnimBmp
.eDisposal
= DISPOSE_BACK
;
576 else if( nGCDisposalMethod
== 3 )
577 aAnimBmp
.eDisposal
= DISPOSE_PREVIOUS
;
579 aAnimBmp
.eDisposal
= DISPOSE_NOT
;
581 aAnimation
.Insert( aAnimBmp
);
583 if( aAnimation
.Count() == 1 )
585 aAnimation
.SetDisplaySizePixel( Size( nGlobalWidth
, nGlobalHeight
) );
586 aAnimation
.SetLoopCount( nLoops
);
590 // ------------------------------------------------------------------------
592 const Graphic
& GIFReader::GetIntermediateGraphic()
594 // Intermediate-Graphic nur erzeugen, wenn schon
595 // Daten vorliegen, aber die Graphic noch nicht
596 // vollstaendig eingelesen wurde
597 if ( bImGraphicReady
&& !aAnimation
.Count() )
601 aBmp8
.ReleaseAccess( pAcc8
);
603 if ( bGCTransparent
)
605 aBmp1
.ReleaseAccess( pAcc1
);
606 aImGraphic
= BitmapEx( aBmp8
, aBmp1
);
608 pAcc1
= aBmp1
.AcquireWriteAccess();
609 bStatus
= bStatus
&& ( pAcc1
!= NULL
);
614 pAcc8
= aBmp8
.AcquireWriteAccess();
615 bStatus
= bStatus
&& ( pAcc8
!= NULL
);
621 // ------------------------------------------------------------------------
623 BOOL
GIFReader::ProcessGIF()
629 eActAction
= ABORT_READING
;
631 // Stream an die richtige Stelle bringen
632 rIStm
.Seek( nLastPos
);
636 // naechsten Marker lesen
637 case( MARKER_READING
):
644 eActAction
= END_READING
;
645 else if( NO_PENDING( rIStm
) )
650 eActAction
= EXTENSION_READING
;
651 else if( cByte
== ',' )
652 eActAction
= LOCAL_HEADER_READING
;
653 else if( cByte
== ';' )
654 eActAction
= END_READING
;
656 eActAction
= ABORT_READING
;
661 // ScreenDescriptor lesen
662 case( GLOBAL_HEADER_READING
):
664 if( ( bRead
= ReadGlobalHeader() ) == TRUE
)
666 ClearImageExtensions();
667 eActAction
= MARKER_READING
;
674 case( EXTENSION_READING
):
676 if( ( bRead
= ReadExtension() ) == TRUE
)
677 eActAction
= MARKER_READING
;
682 // Image-Descriptor lesen
683 case( LOCAL_HEADER_READING
):
685 if( ( bRead
= ReadLocalHeader() ) == TRUE
)
687 nYAcc
= nImageX
= nImageY
= 0;
688 eActAction
= FIRST_BLOCK_READING
;
694 // ersten Datenblock lesen
695 case( FIRST_BLOCK_READING
):
702 eActAction
= ABORT_READING
;
703 else if( cDataSize
> 12 )
705 else if( NO_PENDING( rIStm
) )
708 pDecomp
= new GIFLZWDecompressor( cDataSize
);
709 eActAction
= NEXT_BLOCK_READING
;
710 bOverreadBlock
= FALSE
;
713 eActAction
= FIRST_BLOCK_READING
;
717 // naechsten Datenblock lesen
718 case( NEXT_BLOCK_READING
):
720 USHORT nLastX
= nImageX
;
721 USHORT nLastY
= nImageY
;
722 ULONG nRet
= ReadNextBlock();
724 // Return: 0:Pending / 1:OK; / 2:OK und letzter Block: / 3:EOI / 4:HardAbort
731 bImGraphicReady
= TRUE
;
732 eActAction
= NEXT_BLOCK_READING
;
733 bOverreadBlock
= FALSE
;
741 eActAction
= MARKER_READING
;
742 ClearImageExtensions();
744 else if( nRet
== 3UL )
746 eActAction
= NEXT_BLOCK_READING
;
747 bOverreadBlock
= TRUE
;
753 eActAction
= ABORT_READING
;
754 ClearImageExtensions();
766 // ein Fehler trat auf
767 case( ABORT_READING
):
770 eActAction
= END_READING
;
778 // Stream an die richtige Stelle bringen,
779 // falls Daten gelesen werden konnten
780 // entweder alte Position oder aktuelle Position
782 nLastPos
= rIStm
.Tell();
787 // ------------------------------------------------------------------------
789 ReadState
GIFReader::ReadGIF( Graphic
& rGraphic
)
791 ReadState eReadState
;
795 while( ProcessGIF() && ( eActAction
!= END_READING
) ) {}
798 eReadState
= GIFREAD_ERROR
;
799 else if( eActAction
== END_READING
)
800 eReadState
= GIFREAD_OK
;
803 if ( rIStm
.GetError() == ERRCODE_IO_PENDING
)
806 eReadState
= GIFREAD_NEED_MORE
;
809 if( aAnimation
.Count() == 1 )
811 rGraphic
= aAnimation
.Get( 0 ).aBmpEx
;
813 if( nLogWidth100
&& nLogHeight100
)
815 rGraphic
.SetPrefSize( Size( nLogWidth100
, nLogHeight100
) );
816 rGraphic
.SetPrefMapMode( MAP_100TH_MM
);
820 rGraphic
= aAnimation
;
830 BOOL
ImportGIF( SvStream
& rStm
, Graphic
& rGraphic
)
832 GIFReader
* pGIFReader
= (GIFReader
*) rGraphic
.GetContext();
833 USHORT nOldFormat
= rStm
.GetNumberFormatInt();
834 ReadState eReadState
;
837 rStm
.SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN
);
840 pGIFReader
= new GIFReader( rStm
);
842 rGraphic
.SetContext( NULL
);
843 eReadState
= pGIFReader
->ReadGIF( rGraphic
);
845 if( eReadState
== GIFREAD_ERROR
)
850 else if( eReadState
== GIFREAD_OK
)
854 rGraphic
= pGIFReader
->GetIntermediateGraphic();
855 rGraphic
.SetContext( pGIFReader
);
858 rStm
.SetNumberFormatInt( nOldFormat
);