bump product version to 4.1.6.2
[LibreOffice.git] / filter / source / graphicfilter / egif / egif.cxx
blob74b82a6daf972093842cd7e35b2e4700d157f496
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 #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"
30 // -------------
31 // - GIFWriter -
32 // -------------
34 class GIFWriter
36 Bitmap aAccBmp;
37 SvStream& m_rGIF;
38 BitmapReadAccess* m_pAcc;
39 sal_uLong nMinPercent;
40 sal_uLong nMaxPercent;
41 sal_uLong nLastPercent;
42 long nActX;
43 long nActY;
44 sal_Int32 nInterlaced;
45 sal_Bool bStatus;
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();
55 void WritePalette();
56 void WriteAccess();
57 void WriteTerminator();
59 sal_Bool CreateAccess( const BitmapEx& rBmpEx );
60 void DestroyAccess();
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;
68 public:
70 GIFWriter(SvStream &rStream);
71 ~GIFWriter() {}
73 sal_Bool WriteGIF( const Graphic& rGraphic, FilterConfigItem* pConfigItem );
76 GIFWriter::GIFWriter(SvStream &rStream)
77 : m_rGIF(rStream)
78 , m_pAcc(NULL)
79 , nActX(0)
80 , nActY(0)
84 // ------------------------------------------------------------------------
86 sal_Bool GIFWriter::WriteGIF(const Graphic& rGraphic, FilterConfigItem* pFilterConfigItem)
88 if ( pFilterConfigItem )
90 xStatusIndicator = pFilterConfigItem->GetStatusIndicator();
91 if ( xStatusIndicator.is() )
93 OUString aMsg;
94 xStatusIndicator->start( aMsg, 100 );
98 Size aSize100;
99 const MapMode aMap( rGraphic.GetPrefMapMode() );
100 sal_Bool bLogSize = ( aMap.GetMapUnit() != MAP_PIXEL );
102 if( bLogSize )
103 aSize100 = Application::GetDefaultDevice()->LogicToLogic( rGraphic.GetPrefSize(), aMap, MAP_100TH_MM );
105 bStatus = sal_True;
106 nLastPercent = 0;
107 nInterlaced = 0;
108 m_pAcc = NULL;
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 );
121 if ( bStatus )
123 WriteGlobalHeader( rAnimation.GetDisplaySizePixel() );
125 if( bStatus )
127 WriteLoopExtension( rAnimation );
129 if( bStatus )
130 WriteAnimation( rAnimation );
134 else
136 const sal_Bool bGrafTrans = rGraphic.IsTransparent();
138 BitmapEx aBmpEx;
140 if( bGrafTrans )
141 aBmpEx = rGraphic.GetBitmapEx();
142 else
143 aBmpEx = BitmapEx( rGraphic.GetBitmap() );
145 nMinPercent = 0;
146 nMaxPercent = 100;
148 WriteSignature( bGrafTrans || bLogSize );
150 if( bStatus )
152 WriteGlobalHeader( aBmpEx.GetSizePixel() );
154 if( bStatus )
155 WriteBitmapEx( aBmpEx, Point(), bGrafTrans );
159 if( bStatus )
161 if( bLogSize )
162 WriteLogSizeExtension( aSize100 );
164 WriteTerminator();
167 if ( xStatusIndicator.is() )
168 xStatusIndicator->end();
170 return bStatus;
173 // ------------------------------------------------------------------------
175 void GIFWriter::WriteBitmapEx( const BitmapEx& rBmpEx, const Point& rPoint,
176 sal_Bool bExtended, long nTimer, Disposal eDisposal )
178 if( CreateAccess( rBmpEx ) )
180 nActX = rPoint.X();
181 nActY = rPoint.Y();
183 if( bExtended )
184 WriteImageExtension( nTimer, eDisposal );
186 if( bStatus )
188 WriteLocalHeader();
190 if( bStatus )
192 WritePalette();
194 if( bStatus )
195 WriteAccess();
199 DestroyAccess();
203 // ------------------------------------------------------------------------
205 void GIFWriter::WriteAnimation( const Animation& rAnimation )
207 const sal_uInt16 nCount = rAnimation.Count();
209 if( nCount )
211 const double fStep = 100. / nCount;
213 nMinPercent = 0L;
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 )
247 if( bStatus )
249 Bitmap aMask( rBmpEx.GetMask() );
251 aAccBmp = rBmpEx.GetBitmap();
252 bTransparent = sal_False;
254 if( !!aMask )
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;
262 else
263 aAccBmp.Convert( BMP_CONVERSION_8BIT_COLORS );
265 else
266 aAccBmp.Convert( BMP_CONVERSION_8BIT_COLORS );
268 m_pAcc = aAccBmp.AcquireReadAccess();
270 if( !m_pAcc )
271 bStatus = sal_False;
274 return bStatus;
277 // ------------------------------------------------------------------------
279 void GIFWriter::DestroyAccess()
281 aAccBmp.ReleaseAccess( m_pAcc );
282 m_pAcc = NULL;
285 // ------------------------------------------------------------------------
287 void GIFWriter::WriteSignature( sal_Bool bGIF89a )
289 if( bStatus )
291 m_rGIF.Write( bGIF89a ? "GIF89a" : "GIF87a" , 6 );
293 if( m_rGIF.GetError() )
294 bStatus = sal_False;
298 // ------------------------------------------------------------------------
300 void GIFWriter::WriteGlobalHeader( const Size& rSize )
302 if( bStatus )
304 // 256 colors
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 );
309 // write values
310 m_rGIF << nWidth;
311 m_rGIF << nHeight;
312 m_rGIF << cFlags;
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() )
324 bStatus = sal_False;
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_
343 if( nLoopCount )
344 nLoopCount--;
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;
355 m_rGIF << cLoByte;
356 m_rGIF << cHiByte;
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 )
384 if( bStatus )
386 const sal_uInt16 nDelay = (sal_uInt16) nTimer;
387 sal_uInt8 cFlags = 0;
389 // set Transparency-Flag
390 if( bTransparent )
391 cFlags |= 1;
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;
402 m_rGIF << cFlags;
403 m_rGIF << nDelay;
404 m_rGIF << (sal_uInt8) m_pAcc->GetBestPaletteIndex( BMP_COL_TRANS );
405 m_rGIF << (sal_uInt8) 0x00;
407 if( m_rGIF.GetError() )
408 bStatus = sal_False;
412 // ------------------------------------------------------------------------
414 void GIFWriter::WriteLocalHeader()
416 if( bStatus )
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
425 if( nInterlaced )
426 cFlags |= 0x40;
428 // set Flag for the local color palette
429 cFlags |= 0x80;
431 // alles rausschreiben
432 m_rGIF << (sal_uInt8) 0x2c;
433 m_rGIF << nPosX;
434 m_rGIF << nPosY;
435 m_rGIF << nWidth;
436 m_rGIF << nHeight;
437 m_rGIF << cFlags;
439 if( m_rGIF.GetError() )
440 bStatus = sal_False;
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() )
467 bStatus = sal_False;
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 );
482 if( !bNative )
483 pBuffer = new sal_uInt8[ nWidth ];
485 if( bStatus && ( 8 == m_pAcc->GetBitCount() ) && m_pAcc->HasPalette() )
487 aCompressor.StartCompression( m_rGIF, m_pAcc->GetBitCount() );
489 long nY, nT;
491 for( long i = 0; i < nHeight; ++i )
493 if( nInterlaced )
495 nY = i << 3;
497 if( nY >= nHeight )
499 nT = i - ( ( nHeight + 7 ) >> 3 );
500 nY= ( nT << 3 ) + 4;
502 if( nY >= nHeight )
504 nT -= ( nHeight + 3 ) >> 3;
505 nY = ( nT << 2 ) + 2;
507 if ( nY >= nHeight )
509 nT -= ( ( nHeight + 1 ) >> 2 );
510 nY = ( nT << 1 ) + 1;
515 else
516 nY = i;
518 if( bNative )
519 aCompressor.Compress( m_pAcc->GetScanline( nY ), nWidth );
520 else
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() )
529 bStatus = sal_False;
531 MayCallback( nMinPercent + ( nMaxPercent - nMinPercent ) * i / nHeight );
533 if( !bStatus )
534 break;
537 aCompressor.EndCompression();
539 if ( m_rGIF.GetError() )
540 bStatus = sal_False;
543 delete[] pBuffer;
546 // ------------------------------------------------------------------------
548 void GIFWriter::WriteTerminator()
550 if( bStatus )
552 m_rGIF << (sal_uInt8) 0x3b;
554 if( m_rGIF.GetError() )
555 bStatus = sal_False;
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
566 #endif
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: */