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