1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_filter.hxx"
26
27 #include <vcl/graph.hxx>
28 #include <vcl/svapp.hxx>
29 #include <vcl/msgbox.hxx>
30 #include <vcl/window.hxx>
31 #include <svl/solar.hrc>
32 #include <svtools/fltcall.hxx>
33 #include <svtools/FilterConfigItem.hxx>
34 #include "giflzwc.hxx"
35
36 // -------------
37 // - GIFWriter -
38 // -------------
39
40 class GIFWriter
41 {
42 Bitmap aAccBmp;
43 BitmapReadAccess* pAcc;
44 SvStream* pGIF;
45 sal_uLong nMinPercent;
46 sal_uLong nMaxPercent;
47 sal_uLong nLastPercent;
48 long nActX;
49 long nActY;
50 sal_Int32 nInterlaced;
51 sal_Bool bStatus;
52 sal_Bool bTransparent;
53
54 void MayCallback( sal_uLong nPercent );
55 void WriteSignature( sal_Bool bGIF89a );
56 void WriteGlobalHeader( const Size& rSize );
57 void WriteLoopExtension( const Animation& rAnimation );
58 void WriteLogSizeExtension( const Size& rSize100 );
59 void WriteImageExtension( long nTimer, Disposal eDisposal );
60 void WriteLocalHeader();
61 void WritePalette();
62 void WriteAccess();
63 void WriteTerminator();
64
65 sal_Bool CreateAccess( const BitmapEx& rBmpEx );
66 void DestroyAccess();
67
68 void WriteAnimation( const Animation& rAnimation );
69 void WriteBitmapEx( const BitmapEx& rBmpEx, const Point& rPoint, sal_Bool bExtended,
70 long nTimer = 0, Disposal eDisposal = DISPOSE_NOT );
71
72 com::sun::star::uno::Reference< com::sun::star::task::XStatusIndicator > xStatusIndicator;
73
74 public:
75
GIFWriter()76 GIFWriter() {}
~GIFWriter()77 ~GIFWriter() {}
78
79 sal_Bool WriteGIF( const Graphic& rGraphic, SvStream& rGIF,
80 FilterConfigItem* pConfigItem );
81 };
82
83 // ------------------------------------------------------------------------
84
WriteGIF(const Graphic & rGraphic,SvStream & rGIF,FilterConfigItem * pFilterConfigItem)85 sal_Bool GIFWriter::WriteGIF( const Graphic& rGraphic, SvStream& rGIF,
86 FilterConfigItem* pFilterConfigItem )
87 {
88 if ( pFilterConfigItem )
89 {
90 xStatusIndicator = pFilterConfigItem->GetStatusIndicator();
91 if ( xStatusIndicator.is() )
92 {
93 rtl::OUString aMsg;
94 xStatusIndicator->start( aMsg, 100 );
95 }
96 }
97
98 Size aSize100;
99 const MapMode aMap( rGraphic.GetPrefMapMode() );
100 sal_Bool bLogSize = ( aMap.GetMapUnit() != MAP_PIXEL );
101
102 if( bLogSize )
103 aSize100 = Application::GetDefaultDevice()->LogicToLogic( rGraphic.GetPrefSize(), aMap, MAP_100TH_MM );
104
105 pGIF = &rGIF;
106 bStatus = sal_True;
107 nLastPercent = 0;
108 nInterlaced = 0;
109 pAcc = NULL;
110
111 if ( pFilterConfigItem )
112 nInterlaced = pFilterConfigItem->ReadInt32( String( RTL_CONSTASCII_USTRINGPARAM( "Interlaced" ) ), 0 );
113
114 pGIF->SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN );
115
116 if( rGraphic.IsAnimated() )
117 {
118 const Animation& rAnimation = rGraphic.GetAnimation();
119
120 WriteSignature( sal_True );
121
122 if ( bStatus )
123 {
124 WriteGlobalHeader( rAnimation.GetDisplaySizePixel() );
125
126 if( bStatus )
127 {
128 WriteLoopExtension( rAnimation );
129
130 if( bStatus )
131 WriteAnimation( rAnimation );
132 }
133 }
134 }
135 else
136 {
137 const sal_Bool bGrafTrans = rGraphic.IsTransparent();
138
139 BitmapEx aBmpEx;
140
141 if( bGrafTrans )
142 aBmpEx = rGraphic.GetBitmapEx();
143 else
144 aBmpEx = BitmapEx( rGraphic.GetBitmap() );
145
146 nMinPercent = 0;
147 nMaxPercent = 100;
148
149 WriteSignature( bGrafTrans || bLogSize );
150
151 if( bStatus )
152 {
153 WriteGlobalHeader( aBmpEx.GetSizePixel() );
154
155 if( bStatus )
156 WriteBitmapEx( aBmpEx, Point(), bGrafTrans );
157 }
158 }
159
160 if( bStatus )
161 {
162 if( bLogSize )
163 WriteLogSizeExtension( aSize100 );
164
165 WriteTerminator();
166 }
167
168 if ( xStatusIndicator.is() )
169 xStatusIndicator->end();
170
171 return bStatus;
172 }
173
174 // ------------------------------------------------------------------------
175
WriteBitmapEx(const BitmapEx & rBmpEx,const Point & rPoint,sal_Bool bExtended,long nTimer,Disposal eDisposal)176 void GIFWriter::WriteBitmapEx( const BitmapEx& rBmpEx, const Point& rPoint,
177 sal_Bool bExtended, long nTimer, Disposal eDisposal )
178 {
179 if( CreateAccess( rBmpEx ) )
180 {
181 nActX = rPoint.X();
182 nActY = rPoint.Y();
183
184 if( bExtended )
185 WriteImageExtension( nTimer, eDisposal );
186
187 if( bStatus )
188 {
189 WriteLocalHeader();
190
191 if( bStatus )
192 {
193 WritePalette();
194
195 if( bStatus )
196 WriteAccess();
197 }
198 }
199
200 DestroyAccess();
201 }
202 }
203
204 // ------------------------------------------------------------------------
205
WriteAnimation(const Animation & rAnimation)206 void GIFWriter::WriteAnimation( const Animation& rAnimation )
207 {
208 const sal_uInt16 nCount = rAnimation.Count();
209
210 if( nCount )
211 {
212 const double fStep = 100. / nCount;
213
214 nMinPercent = 0L;
215 nMaxPercent = (sal_uLong) fStep;
216
217 for( sal_uInt16 i = 0; i < nCount; i++ )
218 {
219 const AnimationBitmap& rAnimBmp = rAnimation.Get( i );
220
221 WriteBitmapEx( rAnimBmp.aBmpEx, rAnimBmp.aPosPix, sal_True,
222 rAnimBmp.nWait, rAnimBmp.eDisposal );
223 nMinPercent = nMaxPercent;
224 nMaxPercent = (sal_uLong) ( nMaxPercent + fStep );
225 }
226 }
227 }
228
229 // ------------------------------------------------------------------------
230
MayCallback(sal_uLong nPercent)231 void GIFWriter::MayCallback( sal_uLong nPercent )
232 {
233 if ( xStatusIndicator.is() )
234 {
235 if( nPercent >= nLastPercent + 3 )
236 {
237 nLastPercent = nPercent;
238 if ( nPercent <= 100 )
239 xStatusIndicator->setValue( nPercent );
240 }
241 }
242 }
243
244 // ------------------------------------------------------------------------
245
CreateAccess(const BitmapEx & rBmpEx)246 sal_Bool GIFWriter::CreateAccess( const BitmapEx& rBmpEx )
247 {
248 if( bStatus )
249 {
250 Bitmap aMask( rBmpEx.GetMask() );
251
252 aAccBmp = rBmpEx.GetBitmap();
253 bTransparent = sal_False;
254
255 if( !!aMask )
256 {
257 if( aAccBmp.Convert( BMP_CONVERSION_8BIT_TRANS ) )
258 {
259 aMask.Convert( BMP_CONVERSION_1BIT_THRESHOLD );
260 aAccBmp.Replace( aMask, BMP_COL_TRANS );
261 bTransparent = sal_True;
262 }
263 else
264 aAccBmp.Convert( BMP_CONVERSION_8BIT_COLORS );
265 }
266 else
267 aAccBmp.Convert( BMP_CONVERSION_8BIT_COLORS );
268
269 pAcc = aAccBmp.AcquireReadAccess();
270
271 if( !pAcc )
272 bStatus = sal_False;
273 }
274
275 return bStatus;
276 }
277
278 // ------------------------------------------------------------------------
279
DestroyAccess()280 void GIFWriter::DestroyAccess()
281 {
282 aAccBmp.ReleaseAccess( pAcc );
283 pAcc = NULL;
284 }
285
286 // ------------------------------------------------------------------------
287
WriteSignature(sal_Bool bGIF89a)288 void GIFWriter::WriteSignature( sal_Bool bGIF89a )
289 {
290 if( bStatus )
291 {
292 pGIF->Write( bGIF89a ? "GIF89a" : "GIF87a" , 6 );
293
294 if( pGIF->GetError() )
295 bStatus = sal_False;
296 }
297 }
298
299 // ------------------------------------------------------------------------
300
WriteGlobalHeader(const Size & rSize)301 void GIFWriter::WriteGlobalHeader( const Size& rSize )
302 {
303 if( bStatus )
304 {
305 // 256 Farben
306 const sal_uInt16 nWidth = (sal_uInt16) rSize.Width();
307 const sal_uInt16 nHeight = (sal_uInt16) rSize.Height();
308 const sal_uInt8 cFlags = 128 | ( 7 << 4 );
309
310 // Werte rausschreiben
311 *pGIF << nWidth;
312 *pGIF << nHeight;
313 *pGIF << cFlags;
314 *pGIF << (sal_uInt8) 0x00;
315 *pGIF << (sal_uInt8) 0x00;
316
317 // Dummy-Palette mit zwei Eintraegen (Schwarz/Weiss) schreiben;
318 // dieses nur wegen Photoshop-Bug, da die keine Bilder ohne
319 // globale Farbpalette lesen koennen
320 *pGIF << (sal_uInt16) 0;
321 *pGIF << (sal_uInt16) 255;
322 *pGIF << (sal_uInt16) 65535;
323
324 if( pGIF->GetError() )
325 bStatus = sal_False;
326 }
327 }
328
329 // ------------------------------------------------------------------------
330
WriteLoopExtension(const Animation & rAnimation)331 void GIFWriter::WriteLoopExtension( const Animation& rAnimation )
332 {
333 DBG_ASSERT( rAnimation.Count() > 0, "Animation has no bitmaps!" );
334
335 sal_uInt16 nLoopCount = (sal_uInt16) rAnimation.GetLoopCount();
336
337 // falls nur ein Durchlauf stattfinden soll,
338 // wird keine LoopExtension geschrieben;
339 // Default ist dann immer ein Durchlauf
340 if( nLoopCount != 1 )
341 {
342 // Netscape interpretiert den LoopCount
343 // als reine Anzahl der _Wiederholungen_
344 if( nLoopCount )
345 nLoopCount--;
346
347 const sal_uInt8 cLoByte = (const sal_uInt8) nLoopCount;
348 const sal_uInt8 cHiByte = (const sal_uInt8) ( nLoopCount >> 8 );
349
350 *pGIF << (sal_uInt8) 0x21;
351 *pGIF << (sal_uInt8) 0xff;
352 *pGIF << (sal_uInt8) 0x0b;
353 pGIF->Write( "NETSCAPE2.0", 11 );
354 *pGIF << (sal_uInt8) 0x03;
355 *pGIF << (sal_uInt8) 0x01;
356 *pGIF << cLoByte;
357 *pGIF << cHiByte;
358 *pGIF << (sal_uInt8) 0x00;
359 }
360 }
361
362 // ------------------------------------------------------------------------
363
WriteLogSizeExtension(const Size & rSize100)364 void GIFWriter::WriteLogSizeExtension( const Size& rSize100 )
365 {
366 // PrefSize in 100th-mm als ApplicationExtension schreiben
367 if( rSize100.Width() && rSize100.Height() )
368 {
369 *pGIF << (sal_uInt8) 0x21;
370 *pGIF << (sal_uInt8) 0xff;
371 *pGIF << (sal_uInt8) 0x0b;
372 pGIF->Write( "STARDIV 5.0", 11 );
373 *pGIF << (sal_uInt8) 0x09;
374 *pGIF << (sal_uInt8) 0x01;
375 *pGIF << (sal_uInt32) rSize100.Width();
376 *pGIF << (sal_uInt32) rSize100.Height();
377 *pGIF << (sal_uInt8) 0x00;
378 }
379 }
380
381 // ------------------------------------------------------------------------
382
WriteImageExtension(long nTimer,Disposal eDisposal)383 void GIFWriter::WriteImageExtension( long nTimer, Disposal eDisposal )
384 {
385 if( bStatus )
386 {
387 const sal_uInt16 nDelay = (sal_uInt16) nTimer;
388 sal_uInt8 cFlags = 0;
389
390 // Transparent-Flag setzen
391 if( bTransparent )
392 cFlags |= 1;
393
394 // Disposal-Wert setzen
395 if( eDisposal == DISPOSE_BACK )
396 cFlags |= ( 2 << 2 );
397 else if( eDisposal == DISPOSE_PREVIOUS )
398 cFlags |= ( 3 << 2 );
399
400 *pGIF << (sal_uInt8) 0x21;
401 *pGIF << (sal_uInt8) 0xf9;
402 *pGIF << (sal_uInt8) 0x04;
403 *pGIF << cFlags;
404 *pGIF << nDelay;
405 *pGIF << (sal_uInt8) pAcc->GetBestPaletteIndex( BMP_COL_TRANS );
406 *pGIF << (sal_uInt8) 0x00;
407
408 if( pGIF->GetError() )
409 bStatus = sal_False;
410 }
411 }
412
413 // ------------------------------------------------------------------------
414
WriteLocalHeader()415 void GIFWriter::WriteLocalHeader()
416 {
417 if( bStatus )
418 {
419 const sal_uInt16 nPosX = (sal_uInt16) nActX;
420 const sal_uInt16 nPosY = (sal_uInt16) nActY;
421 const sal_uInt16 nWidth = (sal_uInt16) pAcc->Width();
422 const sal_uInt16 nHeight = (sal_uInt16) pAcc->Height();
423 sal_uInt8 cFlags = (sal_uInt8) ( pAcc->GetBitCount() - 1 );
424
425 // Interlaced-Flag setzen
426 if( nInterlaced )
427 cFlags |= 0x40;
428
429 // Flag fuer lokale Farbpalette setzen
430 cFlags |= 0x80;
431
432 // alles rausschreiben
433 *pGIF << (sal_uInt8) 0x2c;
434 *pGIF << nPosX;
435 *pGIF << nPosY;
436 *pGIF << nWidth;
437 *pGIF << nHeight;
438 *pGIF << cFlags;
439
440 if( pGIF->GetError() )
441 bStatus = sal_False;
442 }
443 }
444
445 // ------------------------------------------------------------------------
446
WritePalette()447 void GIFWriter::WritePalette()
448 {
449 if( bStatus && pAcc->HasPalette() )
450 {
451 const sal_uInt16 nCount = pAcc->GetPaletteEntryCount();
452 const sal_uInt16 nMaxCount = ( 1 << pAcc->GetBitCount() );
453
454 for ( sal_uInt16 i = 0; i < nCount; i++ )
455 {
456 const BitmapColor& rColor = pAcc->GetPaletteColor( i );
457
458 *pGIF << rColor.GetRed();
459 *pGIF << rColor.GetGreen();
460 *pGIF << rColor.GetBlue();
461 }
462
463 // Rest mit 0 auffuellen
464 if( nCount < nMaxCount )
465 pGIF->SeekRel( ( nMaxCount - nCount ) * 3 );
466
467 if( pGIF->GetError() )
468 bStatus = sal_False;
469 }
470 }
471
472 // ------------------------------------------------------------------------
473
WriteAccess()474 void GIFWriter::WriteAccess()
475 {
476 GIFLZWCompressor aCompressor;
477 const long nWidth = pAcc->Width();
478 const long nHeight = pAcc->Height();
479 sal_uInt8* pBuffer = NULL;
480 const sal_uLong nFormat = pAcc->GetScanlineFormat();
481 long nY;
482 long nT;
483 long i;
484 sal_Bool bNative = ( BMP_FORMAT_8BIT_PAL == nFormat );
485
486 if( !bNative )
487 pBuffer = new sal_uInt8[ nWidth ];
488
489 if( bStatus && ( 8 == pAcc->GetBitCount() ) && pAcc->HasPalette() )
490 {
491 aCompressor.StartCompression( *pGIF, pAcc->GetBitCount() );
492
493 for( i = 0; i < nHeight; i++ )
494 {
495 if( nInterlaced )
496 {
497 nY = i << 3;
498
499 if( nY >= nHeight )
500 {
501 nT = i - ( ( nHeight + 7 ) >> 3 );
502 nY= ( nT << 3 ) + 4;
503
504 if( nY >= nHeight )
505 {
506 nT -= ( nHeight + 3 ) >> 3;
507 nY = ( nT << 2 ) + 2;
508
509 if ( nY >= nHeight )
510 {
511 nT -= ( ( nHeight + 1 ) >> 2 );
512 nY = ( nT << 1 ) + 1;
513 }
514 }
515 }
516 }
517 else
518 nY = i;
519
520 if( bNative )
521 aCompressor.Compress( pAcc->GetScanline( nY ), nWidth );
522 else
523 {
524 for( long nX = 0L; nX < nWidth; nX++ )
525 pBuffer[ nX ] = pAcc->GetPixelIndex( nY, nX );
526
527 aCompressor.Compress( pBuffer, nWidth );
528 }
529
530 if ( pGIF->GetError() )
531 bStatus = sal_False;
532
533 MayCallback( nMinPercent + ( nMaxPercent - nMinPercent ) * i / nHeight );
534
535 if( !bStatus )
536 break;
537 }
538
539 aCompressor.EndCompression();
540
541 if ( pGIF->GetError() )
542 bStatus = sal_False;
543 }
544
545 delete[] pBuffer;
546 }
547
548 // ------------------------------------------------------------------------
549
WriteTerminator()550 void GIFWriter::WriteTerminator()
551 {
552 if( bStatus )
553 {
554 *pGIF << (sal_uInt8) 0x3b;
555
556 if( pGIF->GetError() )
557 bStatus = sal_False;
558 }
559 }
560
561 // ------------------------------------------------------------------------
562
GraphicExport(SvStream & rStream,Graphic & rGraphic,FilterConfigItem * pConfigItem,sal_Bool)563 extern "C" sal_Bool __LOADONCALLAPI GraphicExport( SvStream& rStream, Graphic& rGraphic,
564 FilterConfigItem* pConfigItem, sal_Bool )
565 {
566 return GIFWriter().WriteGIF( rGraphic, rStream, pConfigItem );
567 }
568
569 // ------------------------------------------------------------------------
570