xref: /AOO42X/main/filter/source/graphicfilter/egif/egif.cxx (revision b1c5455db1639c48e26c568e4fa7ee78ca5d60ee)
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