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 .
21 #include <vcl/graph.hxx>
22 #include <vcl/svapp.hxx>
23 #include <vcl/msgbox.hxx>
24 #include <vcl/window.hxx>
25 #include <svl/solar.hrc>
26 #include <vcl/fltcall.hxx>
27 #include <vcl/FilterConfigItem.hxx>
28 #include "giflzwc.hxx"
38 BitmapReadAccess
* m_pAcc
;
39 sal_uLong nMinPercent
;
40 sal_uLong nMaxPercent
;
41 sal_uLong nLastPercent
;
44 sal_Int32 nInterlaced
;
46 sal_Bool bTransparent
;
48 void MayCallback( sal_uLong nPercent
);
49 void WriteSignature( sal_Bool bGIF89a
);
50 void WriteGlobalHeader( const Size
& rSize
);
51 void WriteLoopExtension( const Animation
& rAnimation
);
52 void WriteLogSizeExtension( const Size
& rSize100
);
53 void WriteImageExtension( long nTimer
, Disposal eDisposal
);
54 void WriteLocalHeader();
57 void WriteTerminator();
59 sal_Bool
CreateAccess( const BitmapEx
& rBmpEx
);
62 void WriteAnimation( const Animation
& rAnimation
);
63 void WriteBitmapEx( const BitmapEx
& rBmpEx
, const Point
& rPoint
, sal_Bool bExtended
,
64 long nTimer
= 0, Disposal eDisposal
= DISPOSE_NOT
);
66 com::sun::star::uno::Reference
< com::sun::star::task::XStatusIndicator
> xStatusIndicator
;
70 GIFWriter(SvStream
&rStream
);
73 sal_Bool
WriteGIF( const Graphic
& rGraphic
, FilterConfigItem
* pConfigItem
);
76 GIFWriter::GIFWriter(SvStream
&rStream
)
84 // ------------------------------------------------------------------------
86 sal_Bool
GIFWriter::WriteGIF(const Graphic
& rGraphic
, FilterConfigItem
* pFilterConfigItem
)
88 if ( pFilterConfigItem
)
90 xStatusIndicator
= pFilterConfigItem
->GetStatusIndicator();
91 if ( xStatusIndicator
.is() )
94 xStatusIndicator
->start( aMsg
, 100 );
99 const MapMode
aMap( rGraphic
.GetPrefMapMode() );
100 sal_Bool bLogSize
= ( aMap
.GetMapUnit() != MAP_PIXEL
);
103 aSize100
= Application::GetDefaultDevice()->LogicToLogic( rGraphic
.GetPrefSize(), aMap
, MAP_100TH_MM
);
110 if ( pFilterConfigItem
)
111 nInterlaced
= pFilterConfigItem
->ReadInt32( "Interlaced", 0 );
113 m_rGIF
.SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN
);
115 if( rGraphic
.IsAnimated() )
117 const Animation
& rAnimation
= rGraphic
.GetAnimation();
119 WriteSignature( sal_True
);
123 WriteGlobalHeader( rAnimation
.GetDisplaySizePixel() );
127 WriteLoopExtension( rAnimation
);
130 WriteAnimation( rAnimation
);
136 const sal_Bool bGrafTrans
= rGraphic
.IsTransparent();
141 aBmpEx
= rGraphic
.GetBitmapEx();
143 aBmpEx
= BitmapEx( rGraphic
.GetBitmap() );
148 WriteSignature( bGrafTrans
|| bLogSize
);
152 WriteGlobalHeader( aBmpEx
.GetSizePixel() );
155 WriteBitmapEx( aBmpEx
, Point(), bGrafTrans
);
162 WriteLogSizeExtension( aSize100
);
167 if ( xStatusIndicator
.is() )
168 xStatusIndicator
->end();
173 // ------------------------------------------------------------------------
175 void GIFWriter::WriteBitmapEx( const BitmapEx
& rBmpEx
, const Point
& rPoint
,
176 sal_Bool bExtended
, long nTimer
, Disposal eDisposal
)
178 if( CreateAccess( rBmpEx
) )
184 WriteImageExtension( nTimer
, eDisposal
);
203 // ------------------------------------------------------------------------
205 void GIFWriter::WriteAnimation( const Animation
& rAnimation
)
207 const sal_uInt16 nCount
= rAnimation
.Count();
211 const double fStep
= 100. / nCount
;
214 nMaxPercent
= (sal_uLong
) fStep
;
216 for( sal_uInt16 i
= 0; i
< nCount
; i
++ )
218 const AnimationBitmap
& rAnimBmp
= rAnimation
.Get( i
);
220 WriteBitmapEx( rAnimBmp
.aBmpEx
, rAnimBmp
.aPosPix
, sal_True
,
221 rAnimBmp
.nWait
, rAnimBmp
.eDisposal
);
222 nMinPercent
= nMaxPercent
;
223 nMaxPercent
= (sal_uLong
) ( nMaxPercent
+ fStep
);
228 // ------------------------------------------------------------------------
230 void GIFWriter::MayCallback( sal_uLong nPercent
)
232 if ( xStatusIndicator
.is() )
234 if( nPercent
>= nLastPercent
+ 3 )
236 nLastPercent
= nPercent
;
237 if ( nPercent
<= 100 )
238 xStatusIndicator
->setValue( nPercent
);
243 // ------------------------------------------------------------------------
245 sal_Bool
GIFWriter::CreateAccess( const BitmapEx
& rBmpEx
)
249 Bitmap
aMask( rBmpEx
.GetMask() );
251 aAccBmp
= rBmpEx
.GetBitmap();
252 bTransparent
= sal_False
;
256 if( aAccBmp
.Convert( BMP_CONVERSION_8BIT_TRANS
) )
258 aMask
.Convert( BMP_CONVERSION_1BIT_THRESHOLD
);
259 aAccBmp
.Replace( aMask
, BMP_COL_TRANS
);
260 bTransparent
= sal_True
;
263 aAccBmp
.Convert( BMP_CONVERSION_8BIT_COLORS
);
266 aAccBmp
.Convert( BMP_CONVERSION_8BIT_COLORS
);
268 m_pAcc
= aAccBmp
.AcquireReadAccess();
277 // ------------------------------------------------------------------------
279 void GIFWriter::DestroyAccess()
281 aAccBmp
.ReleaseAccess( m_pAcc
);
285 // ------------------------------------------------------------------------
287 void GIFWriter::WriteSignature( sal_Bool bGIF89a
)
291 m_rGIF
.Write( bGIF89a
? "GIF89a" : "GIF87a" , 6 );
293 if( m_rGIF
.GetError() )
298 // ------------------------------------------------------------------------
300 void GIFWriter::WriteGlobalHeader( const Size
& rSize
)
305 const sal_uInt16 nWidth
= (sal_uInt16
) rSize
.Width();
306 const sal_uInt16 nHeight
= (sal_uInt16
) rSize
.Height();
307 const sal_uInt8 cFlags
= 128 | ( 7 << 4 );
313 m_rGIF
<< (sal_uInt8
) 0x00;
314 m_rGIF
<< (sal_uInt8
) 0x00;
316 // write dummy palette with two entries (black/white);
317 // we do this only because of a bug in Photoshop, since those can't
318 // read pictures without a global color palette
319 m_rGIF
<< (sal_uInt16
) 0;
320 m_rGIF
<< (sal_uInt16
) 255;
321 m_rGIF
<< (sal_uInt16
) 65535;
323 if( m_rGIF
.GetError() )
328 // ------------------------------------------------------------------------
330 void GIFWriter::WriteLoopExtension( const Animation
& rAnimation
)
332 DBG_ASSERT( rAnimation
.Count() > 0, "Animation has no bitmaps!" );
334 sal_uInt16 nLoopCount
= (sal_uInt16
) rAnimation
.GetLoopCount();
336 // if only one run should take place
337 // the LoopExtension won't be written
338 // The default in this case is a single run
339 if( nLoopCount
!= 1 )
341 // Netscape interprets the LoopCount
342 // as the sole number of _repetitions_
346 const sal_uInt8 cLoByte
= (const sal_uInt8
) nLoopCount
;
347 const sal_uInt8 cHiByte
= (const sal_uInt8
) ( nLoopCount
>> 8 );
349 m_rGIF
<< (sal_uInt8
) 0x21;
350 m_rGIF
<< (sal_uInt8
) 0xff;
351 m_rGIF
<< (sal_uInt8
) 0x0b;
352 m_rGIF
.Write( "NETSCAPE2.0", 11 );
353 m_rGIF
<< (sal_uInt8
) 0x03;
354 m_rGIF
<< (sal_uInt8
) 0x01;
357 m_rGIF
<< (sal_uInt8
) 0x00;
361 // ------------------------------------------------------------------------
363 void GIFWriter::WriteLogSizeExtension( const Size
& rSize100
)
365 // writer PrefSize in 100th-mm as ApplicationExtension
366 if( rSize100
.Width() && rSize100
.Height() )
368 m_rGIF
<< (sal_uInt8
) 0x21;
369 m_rGIF
<< (sal_uInt8
) 0xff;
370 m_rGIF
<< (sal_uInt8
) 0x0b;
371 m_rGIF
.Write( "STARDIV 5.0", 11 );
372 m_rGIF
<< (sal_uInt8
) 0x09;
373 m_rGIF
<< (sal_uInt8
) 0x01;
374 m_rGIF
<< (sal_uInt32
) rSize100
.Width();
375 m_rGIF
<< (sal_uInt32
) rSize100
.Height();
376 m_rGIF
<< (sal_uInt8
) 0x00;
380 // ------------------------------------------------------------------------
382 void GIFWriter::WriteImageExtension( long nTimer
, Disposal eDisposal
)
386 const sal_uInt16 nDelay
= (sal_uInt16
) nTimer
;
387 sal_uInt8 cFlags
= 0;
389 // set Transparency-Flag
393 // set Disposal-value
394 if( eDisposal
== DISPOSE_BACK
)
395 cFlags
|= ( 2 << 2 );
396 else if( eDisposal
== DISPOSE_PREVIOUS
)
397 cFlags
|= ( 3 << 2 );
399 m_rGIF
<< (sal_uInt8
) 0x21;
400 m_rGIF
<< (sal_uInt8
) 0xf9;
401 m_rGIF
<< (sal_uInt8
) 0x04;
404 m_rGIF
<< (sal_uInt8
) m_pAcc
->GetBestPaletteIndex( BMP_COL_TRANS
);
405 m_rGIF
<< (sal_uInt8
) 0x00;
407 if( m_rGIF
.GetError() )
412 // ------------------------------------------------------------------------
414 void GIFWriter::WriteLocalHeader()
418 const sal_uInt16 nPosX
= (sal_uInt16
) nActX
;
419 const sal_uInt16 nPosY
= (sal_uInt16
) nActY
;
420 const sal_uInt16 nWidth
= (sal_uInt16
) m_pAcc
->Width();
421 const sal_uInt16 nHeight
= (sal_uInt16
) m_pAcc
->Height();
422 sal_uInt8 cFlags
= (sal_uInt8
) ( m_pAcc
->GetBitCount() - 1 );
424 // set Interlaced-Flag
428 // set Flag for the local color palette
431 // alles rausschreiben
432 m_rGIF
<< (sal_uInt8
) 0x2c;
439 if( m_rGIF
.GetError() )
444 // ------------------------------------------------------------------------
446 void GIFWriter::WritePalette()
448 if( bStatus
&& m_pAcc
->HasPalette() )
450 const sal_uInt16 nCount
= m_pAcc
->GetPaletteEntryCount();
451 const sal_uInt16 nMaxCount
= ( 1 << m_pAcc
->GetBitCount() );
453 for ( sal_uInt16 i
= 0; i
< nCount
; i
++ )
455 const BitmapColor
& rColor
= m_pAcc
->GetPaletteColor( i
);
457 m_rGIF
<< rColor
.GetRed();
458 m_rGIF
<< rColor
.GetGreen();
459 m_rGIF
<< rColor
.GetBlue();
462 // fill up the rest with 0
463 if( nCount
< nMaxCount
)
464 m_rGIF
.SeekRel( ( nMaxCount
- nCount
) * 3 );
466 if( m_rGIF
.GetError() )
471 // ------------------------------------------------------------------------
473 void GIFWriter::WriteAccess()
475 GIFLZWCompressor aCompressor
;
476 const long nWidth
= m_pAcc
->Width();
477 const long nHeight
= m_pAcc
->Height();
478 sal_uInt8
* pBuffer
= NULL
;
479 const sal_uLong nFormat
= m_pAcc
->GetScanlineFormat();
480 sal_Bool bNative
= ( BMP_FORMAT_8BIT_PAL
== nFormat
);
483 pBuffer
= new sal_uInt8
[ nWidth
];
485 if( bStatus
&& ( 8 == m_pAcc
->GetBitCount() ) && m_pAcc
->HasPalette() )
487 aCompressor
.StartCompression( m_rGIF
, m_pAcc
->GetBitCount() );
491 for( long i
= 0; i
< nHeight
; ++i
)
499 nT
= i
- ( ( nHeight
+ 7 ) >> 3 );
504 nT
-= ( nHeight
+ 3 ) >> 3;
505 nY
= ( nT
<< 2 ) + 2;
509 nT
-= ( ( nHeight
+ 1 ) >> 2 );
510 nY
= ( nT
<< 1 ) + 1;
519 aCompressor
.Compress( m_pAcc
->GetScanline( nY
), nWidth
);
522 for( long nX
= 0L; nX
< nWidth
; nX
++ )
523 pBuffer
[ nX
] = m_pAcc
->GetPixelIndex( nY
, nX
);
525 aCompressor
.Compress( pBuffer
, nWidth
);
528 if ( m_rGIF
.GetError() )
531 MayCallback( nMinPercent
+ ( nMaxPercent
- nMinPercent
) * i
/ nHeight
);
537 aCompressor
.EndCompression();
539 if ( m_rGIF
.GetError() )
546 // ------------------------------------------------------------------------
548 void GIFWriter::WriteTerminator()
552 m_rGIF
<< (sal_uInt8
) 0x3b;
554 if( m_rGIF
.GetError() )
559 // ------------------------------------------------------------------------
561 // this needs to be kept in sync with
562 // ImpFilterLibCacheEntry::GetImportFunction() from
563 // vcl/source/filter/graphicfilter.cxx
564 #if defined(DISABLE_DYNLOADING)
565 #define GraphicExport egiGraphicExport
568 extern "C" SAL_DLLPUBLIC_EXPORT sal_Bool SAL_CALL
569 GraphicExport( SvStream
& rStream
, Graphic
& rGraphic
,
570 FilterConfigItem
* pConfigItem
, sal_Bool
)
572 GIFWriter
aWriter(rStream
);
573 return aWriter
.WriteGIF(rGraphic
, pConfigItem
);
576 // ------------------------------------------------------------------------
579 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */