Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 :
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 <svtools/fltcall.hxx>
27 : #include <svtools/FilterConfigItem.hxx>
28 : #include "giflzwc.hxx"
29 :
30 : // -------------
31 : // - GIFWriter -
32 : // -------------
33 :
34 : class GIFWriter
35 : {
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;
47 :
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();
58 :
59 : sal_Bool CreateAccess( const BitmapEx& rBmpEx );
60 : void DestroyAccess();
61 :
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 );
65 :
66 : com::sun::star::uno::Reference< com::sun::star::task::XStatusIndicator > xStatusIndicator;
67 :
68 : public:
69 :
70 : GIFWriter(SvStream &rStream);
71 0 : ~GIFWriter() {}
72 :
73 : sal_Bool WriteGIF( const Graphic& rGraphic, FilterConfigItem* pConfigItem );
74 : };
75 :
76 0 : GIFWriter::GIFWriter(SvStream &rStream)
77 : : m_rGIF(rStream)
78 : , m_pAcc(NULL)
79 : , nActX(0)
80 0 : , nActY(0)
81 : {
82 0 : }
83 :
84 : // ------------------------------------------------------------------------
85 :
86 0 : sal_Bool GIFWriter::WriteGIF(const Graphic& rGraphic, FilterConfigItem* pFilterConfigItem)
87 : {
88 0 : if ( pFilterConfigItem )
89 : {
90 0 : xStatusIndicator = pFilterConfigItem->GetStatusIndicator();
91 0 : if ( xStatusIndicator.is() )
92 : {
93 0 : rtl::OUString aMsg;
94 0 : xStatusIndicator->start( aMsg, 100 );
95 : }
96 : }
97 :
98 0 : Size aSize100;
99 0 : const MapMode aMap( rGraphic.GetPrefMapMode() );
100 0 : sal_Bool bLogSize = ( aMap.GetMapUnit() != MAP_PIXEL );
101 :
102 0 : if( bLogSize )
103 0 : aSize100 = Application::GetDefaultDevice()->LogicToLogic( rGraphic.GetPrefSize(), aMap, MAP_100TH_MM );
104 :
105 0 : bStatus = sal_True;
106 0 : nLastPercent = 0;
107 0 : nInterlaced = 0;
108 0 : m_pAcc = NULL;
109 :
110 0 : if ( pFilterConfigItem )
111 0 : nInterlaced = pFilterConfigItem->ReadInt32( String( RTL_CONSTASCII_USTRINGPARAM( "Interlaced" ) ), 0 );
112 :
113 0 : m_rGIF.SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN );
114 :
115 0 : if( rGraphic.IsAnimated() )
116 : {
117 0 : const Animation& rAnimation = rGraphic.GetAnimation();
118 :
119 0 : WriteSignature( sal_True );
120 :
121 0 : if ( bStatus )
122 : {
123 0 : WriteGlobalHeader( rAnimation.GetDisplaySizePixel() );
124 :
125 0 : if( bStatus )
126 : {
127 0 : WriteLoopExtension( rAnimation );
128 :
129 0 : if( bStatus )
130 0 : WriteAnimation( rAnimation );
131 : }
132 0 : }
133 : }
134 : else
135 : {
136 0 : const sal_Bool bGrafTrans = rGraphic.IsTransparent();
137 :
138 0 : BitmapEx aBmpEx;
139 :
140 0 : if( bGrafTrans )
141 0 : aBmpEx = rGraphic.GetBitmapEx();
142 : else
143 0 : aBmpEx = BitmapEx( rGraphic.GetBitmap() );
144 :
145 0 : nMinPercent = 0;
146 0 : nMaxPercent = 100;
147 :
148 0 : WriteSignature( bGrafTrans || bLogSize );
149 :
150 0 : if( bStatus )
151 : {
152 0 : WriteGlobalHeader( aBmpEx.GetSizePixel() );
153 :
154 0 : if( bStatus )
155 0 : WriteBitmapEx( aBmpEx, Point(), bGrafTrans );
156 0 : }
157 : }
158 :
159 0 : if( bStatus )
160 : {
161 0 : if( bLogSize )
162 0 : WriteLogSizeExtension( aSize100 );
163 :
164 0 : WriteTerminator();
165 : }
166 :
167 0 : if ( xStatusIndicator.is() )
168 0 : xStatusIndicator->end();
169 :
170 0 : return bStatus;
171 : }
172 :
173 : // ------------------------------------------------------------------------
174 :
175 0 : void GIFWriter::WriteBitmapEx( const BitmapEx& rBmpEx, const Point& rPoint,
176 : sal_Bool bExtended, long nTimer, Disposal eDisposal )
177 : {
178 0 : if( CreateAccess( rBmpEx ) )
179 : {
180 0 : nActX = rPoint.X();
181 0 : nActY = rPoint.Y();
182 :
183 0 : if( bExtended )
184 0 : WriteImageExtension( nTimer, eDisposal );
185 :
186 0 : if( bStatus )
187 : {
188 0 : WriteLocalHeader();
189 :
190 0 : if( bStatus )
191 : {
192 0 : WritePalette();
193 :
194 0 : if( bStatus )
195 0 : WriteAccess();
196 : }
197 : }
198 :
199 0 : DestroyAccess();
200 : }
201 0 : }
202 :
203 : // ------------------------------------------------------------------------
204 :
205 0 : void GIFWriter::WriteAnimation( const Animation& rAnimation )
206 : {
207 0 : const sal_uInt16 nCount = rAnimation.Count();
208 :
209 0 : if( nCount )
210 : {
211 0 : const double fStep = 100. / nCount;
212 :
213 0 : nMinPercent = 0L;
214 0 : nMaxPercent = (sal_uLong) fStep;
215 :
216 0 : for( sal_uInt16 i = 0; i < nCount; i++ )
217 : {
218 0 : const AnimationBitmap& rAnimBmp = rAnimation.Get( i );
219 :
220 : WriteBitmapEx( rAnimBmp.aBmpEx, rAnimBmp.aPosPix, sal_True,
221 0 : rAnimBmp.nWait, rAnimBmp.eDisposal );
222 0 : nMinPercent = nMaxPercent;
223 0 : nMaxPercent = (sal_uLong) ( nMaxPercent + fStep );
224 : }
225 : }
226 0 : }
227 :
228 : // ------------------------------------------------------------------------
229 :
230 0 : void GIFWriter::MayCallback( sal_uLong nPercent )
231 : {
232 0 : if ( xStatusIndicator.is() )
233 : {
234 0 : if( nPercent >= nLastPercent + 3 )
235 : {
236 0 : nLastPercent = nPercent;
237 0 : if ( nPercent <= 100 )
238 0 : xStatusIndicator->setValue( nPercent );
239 : }
240 : }
241 0 : }
242 :
243 : // ------------------------------------------------------------------------
244 :
245 0 : sal_Bool GIFWriter::CreateAccess( const BitmapEx& rBmpEx )
246 : {
247 0 : if( bStatus )
248 : {
249 0 : Bitmap aMask( rBmpEx.GetMask() );
250 :
251 0 : aAccBmp = rBmpEx.GetBitmap();
252 0 : bTransparent = sal_False;
253 :
254 0 : if( !!aMask )
255 : {
256 0 : if( aAccBmp.Convert( BMP_CONVERSION_8BIT_TRANS ) )
257 : {
258 0 : aMask.Convert( BMP_CONVERSION_1BIT_THRESHOLD );
259 0 : aAccBmp.Replace( aMask, BMP_COL_TRANS );
260 0 : bTransparent = sal_True;
261 : }
262 : else
263 0 : aAccBmp.Convert( BMP_CONVERSION_8BIT_COLORS );
264 : }
265 : else
266 0 : aAccBmp.Convert( BMP_CONVERSION_8BIT_COLORS );
267 :
268 0 : m_pAcc = aAccBmp.AcquireReadAccess();
269 :
270 0 : if( !m_pAcc )
271 0 : bStatus = sal_False;
272 : }
273 :
274 0 : return bStatus;
275 : }
276 :
277 : // ------------------------------------------------------------------------
278 :
279 0 : void GIFWriter::DestroyAccess()
280 : {
281 0 : aAccBmp.ReleaseAccess( m_pAcc );
282 0 : m_pAcc = NULL;
283 0 : }
284 :
285 : // ------------------------------------------------------------------------
286 :
287 0 : void GIFWriter::WriteSignature( sal_Bool bGIF89a )
288 : {
289 0 : if( bStatus )
290 : {
291 0 : m_rGIF.Write( bGIF89a ? "GIF89a" : "GIF87a" , 6 );
292 :
293 0 : if( m_rGIF.GetError() )
294 0 : bStatus = sal_False;
295 : }
296 0 : }
297 :
298 : // ------------------------------------------------------------------------
299 :
300 0 : void GIFWriter::WriteGlobalHeader( const Size& rSize )
301 : {
302 0 : if( bStatus )
303 : {
304 : // 256 colors
305 0 : const sal_uInt16 nWidth = (sal_uInt16) rSize.Width();
306 0 : const sal_uInt16 nHeight = (sal_uInt16) rSize.Height();
307 0 : const sal_uInt8 cFlags = 128 | ( 7 << 4 );
308 :
309 : // write values
310 0 : m_rGIF << nWidth;
311 0 : m_rGIF << nHeight;
312 0 : m_rGIF << cFlags;
313 0 : m_rGIF << (sal_uInt8) 0x00;
314 0 : m_rGIF << (sal_uInt8) 0x00;
315 :
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 0 : m_rGIF << (sal_uInt16) 0;
320 0 : m_rGIF << (sal_uInt16) 255;
321 0 : m_rGIF << (sal_uInt16) 65535;
322 :
323 0 : if( m_rGIF.GetError() )
324 0 : bStatus = sal_False;
325 : }
326 0 : }
327 :
328 : // ------------------------------------------------------------------------
329 :
330 0 : void GIFWriter::WriteLoopExtension( const Animation& rAnimation )
331 : {
332 : DBG_ASSERT( rAnimation.Count() > 0, "Animation has no bitmaps!" );
333 :
334 0 : sal_uInt16 nLoopCount = (sal_uInt16) rAnimation.GetLoopCount();
335 :
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 0 : if( nLoopCount != 1 )
340 : {
341 : // Netscape interprets the LoopCount
342 : // as the sole number of _repetitions_
343 0 : if( nLoopCount )
344 0 : nLoopCount--;
345 :
346 0 : const sal_uInt8 cLoByte = (const sal_uInt8) nLoopCount;
347 0 : const sal_uInt8 cHiByte = (const sal_uInt8) ( nLoopCount >> 8 );
348 :
349 0 : m_rGIF << (sal_uInt8) 0x21;
350 0 : m_rGIF << (sal_uInt8) 0xff;
351 0 : m_rGIF << (sal_uInt8) 0x0b;
352 0 : m_rGIF.Write( "NETSCAPE2.0", 11 );
353 0 : m_rGIF << (sal_uInt8) 0x03;
354 0 : m_rGIF << (sal_uInt8) 0x01;
355 0 : m_rGIF << cLoByte;
356 0 : m_rGIF << cHiByte;
357 0 : m_rGIF << (sal_uInt8) 0x00;
358 : }
359 0 : }
360 :
361 : // ------------------------------------------------------------------------
362 :
363 0 : void GIFWriter::WriteLogSizeExtension( const Size& rSize100 )
364 : {
365 : // writer PrefSize in 100th-mm as ApplicationExtension
366 0 : if( rSize100.Width() && rSize100.Height() )
367 : {
368 0 : m_rGIF << (sal_uInt8) 0x21;
369 0 : m_rGIF << (sal_uInt8) 0xff;
370 0 : m_rGIF << (sal_uInt8) 0x0b;
371 0 : m_rGIF.Write( "STARDIV 5.0", 11 );
372 0 : m_rGIF << (sal_uInt8) 0x09;
373 0 : m_rGIF << (sal_uInt8) 0x01;
374 0 : m_rGIF << (sal_uInt32) rSize100.Width();
375 0 : m_rGIF << (sal_uInt32) rSize100.Height();
376 0 : m_rGIF << (sal_uInt8) 0x00;
377 : }
378 0 : }
379 :
380 : // ------------------------------------------------------------------------
381 :
382 0 : void GIFWriter::WriteImageExtension( long nTimer, Disposal eDisposal )
383 : {
384 0 : if( bStatus )
385 : {
386 0 : const sal_uInt16 nDelay = (sal_uInt16) nTimer;
387 0 : sal_uInt8 cFlags = 0;
388 :
389 : // set Transparency-Flag
390 0 : if( bTransparent )
391 0 : cFlags |= 1;
392 :
393 : // set Disposal-value
394 0 : if( eDisposal == DISPOSE_BACK )
395 0 : cFlags |= ( 2 << 2 );
396 0 : else if( eDisposal == DISPOSE_PREVIOUS )
397 0 : cFlags |= ( 3 << 2 );
398 :
399 0 : m_rGIF << (sal_uInt8) 0x21;
400 0 : m_rGIF << (sal_uInt8) 0xf9;
401 0 : m_rGIF << (sal_uInt8) 0x04;
402 0 : m_rGIF << cFlags;
403 0 : m_rGIF << nDelay;
404 0 : m_rGIF << (sal_uInt8) m_pAcc->GetBestPaletteIndex( BMP_COL_TRANS );
405 0 : m_rGIF << (sal_uInt8) 0x00;
406 :
407 0 : if( m_rGIF.GetError() )
408 0 : bStatus = sal_False;
409 : }
410 0 : }
411 :
412 : // ------------------------------------------------------------------------
413 :
414 0 : void GIFWriter::WriteLocalHeader()
415 : {
416 0 : if( bStatus )
417 : {
418 0 : const sal_uInt16 nPosX = (sal_uInt16) nActX;
419 0 : const sal_uInt16 nPosY = (sal_uInt16) nActY;
420 0 : const sal_uInt16 nWidth = (sal_uInt16) m_pAcc->Width();
421 0 : const sal_uInt16 nHeight = (sal_uInt16) m_pAcc->Height();
422 0 : sal_uInt8 cFlags = (sal_uInt8) ( m_pAcc->GetBitCount() - 1 );
423 :
424 : // set Interlaced-Flag
425 0 : if( nInterlaced )
426 0 : cFlags |= 0x40;
427 :
428 : // set Flag for the local color palette
429 0 : cFlags |= 0x80;
430 :
431 : // alles rausschreiben
432 0 : m_rGIF << (sal_uInt8) 0x2c;
433 0 : m_rGIF << nPosX;
434 0 : m_rGIF << nPosY;
435 0 : m_rGIF << nWidth;
436 0 : m_rGIF << nHeight;
437 0 : m_rGIF << cFlags;
438 :
439 0 : if( m_rGIF.GetError() )
440 0 : bStatus = sal_False;
441 : }
442 0 : }
443 :
444 : // ------------------------------------------------------------------------
445 :
446 0 : void GIFWriter::WritePalette()
447 : {
448 0 : if( bStatus && m_pAcc->HasPalette() )
449 : {
450 0 : const sal_uInt16 nCount = m_pAcc->GetPaletteEntryCount();
451 0 : const sal_uInt16 nMaxCount = ( 1 << m_pAcc->GetBitCount() );
452 :
453 0 : for ( sal_uInt16 i = 0; i < nCount; i++ )
454 : {
455 0 : const BitmapColor& rColor = m_pAcc->GetPaletteColor( i );
456 :
457 0 : m_rGIF << rColor.GetRed();
458 0 : m_rGIF << rColor.GetGreen();
459 0 : m_rGIF << rColor.GetBlue();
460 : }
461 :
462 : // fill up the rest with 0
463 0 : if( nCount < nMaxCount )
464 0 : m_rGIF.SeekRel( ( nMaxCount - nCount ) * 3 );
465 :
466 0 : if( m_rGIF.GetError() )
467 0 : bStatus = sal_False;
468 : }
469 0 : }
470 :
471 : // ------------------------------------------------------------------------
472 :
473 0 : void GIFWriter::WriteAccess()
474 : {
475 0 : GIFLZWCompressor aCompressor;
476 0 : const long nWidth = m_pAcc->Width();
477 0 : const long nHeight = m_pAcc->Height();
478 0 : sal_uInt8* pBuffer = NULL;
479 0 : const sal_uLong nFormat = m_pAcc->GetScanlineFormat();
480 0 : sal_Bool bNative = ( BMP_FORMAT_8BIT_PAL == nFormat );
481 :
482 0 : if( !bNative )
483 0 : pBuffer = new sal_uInt8[ nWidth ];
484 :
485 0 : if( bStatus && ( 8 == m_pAcc->GetBitCount() ) && m_pAcc->HasPalette() )
486 : {
487 0 : aCompressor.StartCompression( m_rGIF, m_pAcc->GetBitCount() );
488 :
489 : long nY, nT;
490 :
491 0 : for( long i = 0; i < nHeight; ++i )
492 : {
493 0 : if( nInterlaced )
494 : {
495 0 : nY = i << 3;
496 :
497 0 : if( nY >= nHeight )
498 : {
499 0 : nT = i - ( ( nHeight + 7 ) >> 3 );
500 0 : nY= ( nT << 3 ) + 4;
501 :
502 0 : if( nY >= nHeight )
503 : {
504 0 : nT -= ( nHeight + 3 ) >> 3;
505 0 : nY = ( nT << 2 ) + 2;
506 :
507 0 : if ( nY >= nHeight )
508 : {
509 0 : nT -= ( ( nHeight + 1 ) >> 2 );
510 0 : nY = ( nT << 1 ) + 1;
511 : }
512 : }
513 : }
514 : }
515 : else
516 0 : nY = i;
517 :
518 0 : if( bNative )
519 0 : aCompressor.Compress( m_pAcc->GetScanline( nY ), nWidth );
520 : else
521 : {
522 0 : for( long nX = 0L; nX < nWidth; nX++ )
523 0 : pBuffer[ nX ] = (sal_uInt8) m_pAcc->GetPixel( nY, nX );
524 :
525 0 : aCompressor.Compress( pBuffer, nWidth );
526 : }
527 :
528 0 : if ( m_rGIF.GetError() )
529 0 : bStatus = sal_False;
530 :
531 0 : MayCallback( nMinPercent + ( nMaxPercent - nMinPercent ) * i / nHeight );
532 :
533 0 : if( !bStatus )
534 0 : break;
535 : }
536 :
537 0 : aCompressor.EndCompression();
538 :
539 0 : if ( m_rGIF.GetError() )
540 0 : bStatus = sal_False;
541 : }
542 :
543 0 : delete[] pBuffer;
544 0 : }
545 :
546 : // ------------------------------------------------------------------------
547 :
548 0 : void GIFWriter::WriteTerminator()
549 : {
550 0 : if( bStatus )
551 : {
552 0 : m_rGIF << (sal_uInt8) 0x3b;
553 :
554 0 : if( m_rGIF.GetError() )
555 0 : bStatus = sal_False;
556 : }
557 0 : }
558 :
559 : // ------------------------------------------------------------------------
560 :
561 : #ifdef DISABLE_DYNLOADING
562 : #define GraphicExport egiGraphicExport
563 : #endif
564 :
565 : extern "C" SAL_DLLPUBLIC_EXPORT sal_Bool SAL_CALL
566 0 : GraphicExport( SvStream& rStream, Graphic& rGraphic,
567 : FilterConfigItem* pConfigItem, sal_Bool )
568 : {
569 0 : GIFWriter aWriter(rStream);
570 0 : return aWriter.WriteGIF(rGraphic, pConfigItem);
571 : }
572 :
573 : // ------------------------------------------------------------------------
574 :
575 :
576 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|