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