cid#1606940 Check of thread-shared field evades lock acquisition
[LibreOffice.git] / vcl / source / filter / egif / egif.cxx
bloba8a12e674ea64ad4014a1fb024a208f51c40076e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 #include <tools/stream.hxx>
22 #include <tools/debug.hxx>
23 #include <vcl/BitmapReadAccess.hxx>
24 #include <vcl/graph.hxx>
25 #include <vcl/outdev.hxx>
26 #include <vcl/FilterConfigItem.hxx>
27 #include <com/sun/star/task/XStatusIndicator.hpp>
28 #include "giflzwc.hxx"
29 #include <memory>
30 #include <filter/GifWriter.hxx>
32 namespace {
34 class GIFWriter
36 Bitmap aAccBmp;
37 SvStream& m_rGIF;
38 BitmapScopedReadAccess m_pAcc;
39 sal_uInt32 nMinPercent;
40 sal_uInt32 nMaxPercent;
41 sal_uInt32 nLastPercent;
42 tools::Long nActX;
43 tools::Long nActY;
44 sal_Int32 nInterlaced;
45 bool bStatus;
46 bool bTransparent;
48 void MayCallback(sal_uInt32 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( tools::Long nTimer, Disposal eDisposal );
54 void WriteLocalHeader();
55 void WritePalette();
56 void WriteAccess();
57 void WriteTerminator();
59 bool CreateAccess( const BitmapEx& rBmpEx );
60 void DestroyAccess();
62 void WriteAnimation( const Animation& rAnimation );
63 void WriteBitmapEx( const BitmapEx& rBmpEx, const Point& rPoint, bool bExtended,
64 tools::Long nTimer = 0, Disposal eDisposal = Disposal::Not );
66 css::uno::Reference< css::task::XStatusIndicator > xStatusIndicator;
68 public:
70 explicit GIFWriter(SvStream &rStream);
72 bool WriteGIF( const Graphic& rGraphic, FilterConfigItem* pConfigItem );
77 GIFWriter::GIFWriter(SvStream &rStream)
78 : m_rGIF(rStream)
79 , nMinPercent(0)
80 , nMaxPercent(0)
81 , nLastPercent(0)
82 , nActX(0)
83 , nActY(0)
84 , nInterlaced(0)
85 , bStatus(false)
86 , bTransparent(false)
91 bool GIFWriter::WriteGIF(const Graphic& rGraphic, FilterConfigItem* pFilterConfigItem)
93 if ( pFilterConfigItem )
95 xStatusIndicator = pFilterConfigItem->GetStatusIndicator();
96 if ( xStatusIndicator.is() )
98 xStatusIndicator->start( OUString(), 100 );
102 Size aSize100;
103 const MapMode aMap( rGraphic.GetPrefMapMode() );
104 bool bLogSize = ( aMap.GetMapUnit() != MapUnit::MapPixel );
106 if( bLogSize )
107 aSize100 = OutputDevice::LogicToLogic(rGraphic.GetPrefSize(), aMap, MapMode(MapUnit::Map100thMM));
109 bStatus = true;
110 nLastPercent = 0;
111 nInterlaced = 0;
112 m_pAcc.reset();
114 if ( pFilterConfigItem )
115 nInterlaced = pFilterConfigItem->ReadInt32( u"Interlaced"_ustr, 0 );
117 m_rGIF.SetEndian( SvStreamEndian::LITTLE );
119 if( rGraphic.IsAnimated() )
121 const Animation aAnimation = rGraphic.GetAnimation();
123 WriteSignature( true );
125 if ( bStatus )
127 WriteGlobalHeader( aAnimation.GetDisplaySizePixel() );
129 if( bStatus )
131 WriteLoopExtension( aAnimation );
133 if( bStatus )
134 WriteAnimation( aAnimation );
138 else
140 const bool bGrafTrans = rGraphic.IsTransparent();
142 BitmapEx aBmpEx = rGraphic.GetBitmapEx();
144 nMinPercent = 0;
145 nMaxPercent = 100;
147 WriteSignature( bGrafTrans || bLogSize );
149 if( bStatus )
151 WriteGlobalHeader( aBmpEx.GetSizePixel() );
153 if( bStatus )
154 WriteBitmapEx( aBmpEx, Point(), bGrafTrans );
158 if( bStatus )
160 if( bLogSize )
161 WriteLogSizeExtension( aSize100 );
163 WriteTerminator();
166 if ( xStatusIndicator.is() )
167 xStatusIndicator->end();
169 return bStatus;
173 void GIFWriter::WriteBitmapEx( const BitmapEx& rBmpEx, const Point& rPoint,
174 bool bExtended, tools::Long nTimer, Disposal eDisposal )
176 if( !CreateAccess( rBmpEx ) )
177 return;
179 nActX = rPoint.X();
180 nActY = rPoint.Y();
182 if( bExtended )
183 WriteImageExtension( nTimer, eDisposal );
185 if( bStatus )
187 WriteLocalHeader();
189 if( bStatus )
191 WritePalette();
193 if( bStatus )
194 WriteAccess();
198 DestroyAccess();
202 void GIFWriter::WriteAnimation( const Animation& rAnimation )
204 const sal_uInt16 nCount = rAnimation.Count();
206 if( !nCount )
207 return;
209 const double fStep = 100. / nCount;
211 nMinPercent = 0;
212 nMaxPercent = static_cast<sal_uInt32>(fStep);
214 for( sal_uInt16 i = 0; i < nCount; i++ )
216 const AnimationFrame& rAnimationFrame = rAnimation.Get( i );
218 WriteBitmapEx(rAnimationFrame.maBitmapEx, rAnimationFrame.maPositionPixel, true,
219 rAnimationFrame.mnWait, rAnimationFrame.meDisposal );
220 nMinPercent = nMaxPercent;
221 nMaxPercent = static_cast<sal_uInt32>(nMaxPercent + fStep);
226 void GIFWriter::MayCallback(sal_uInt32 nPercent)
228 if ( xStatusIndicator.is() )
230 if( nPercent >= nLastPercent + 3 )
232 nLastPercent = nPercent;
233 if ( nPercent <= 100 )
234 xStatusIndicator->setValue( nPercent );
240 bool GIFWriter::CreateAccess( const BitmapEx& rBmpEx )
242 if( bStatus )
244 AlphaMask aMask( rBmpEx.GetAlphaMask() );
246 aAccBmp = rBmpEx.GetBitmap();
247 bTransparent = false;
249 if( !aMask.IsEmpty() )
251 if( aAccBmp.Convert( BmpConversion::N8BitTrans ) )
253 aMask.Convert( BmpConversion::N1BitThreshold );
254 aMask.Invert();
255 aAccBmp.ReplaceMask( aMask, BMP_COL_TRANS );
256 bTransparent = true;
258 else
259 aAccBmp.Convert( BmpConversion::N8BitColors );
261 else
262 aAccBmp.Convert( BmpConversion::N8BitColors );
264 m_pAcc = aAccBmp;
266 if( !m_pAcc )
267 bStatus = false;
270 return bStatus;
274 void GIFWriter::DestroyAccess()
276 m_pAcc.reset();
280 void GIFWriter::WriteSignature( bool bGIF89a )
282 if( bStatus )
284 m_rGIF.WriteBytes(bGIF89a ? "GIF89a" : "GIF87a" , 6);
286 if( m_rGIF.GetError() )
287 bStatus = false;
292 void GIFWriter::WriteGlobalHeader( const Size& rSize )
294 if( !bStatus )
295 return;
297 // 256 colors
298 const sal_uInt16 nWidth = static_cast<sal_uInt16>(rSize.Width());
299 const sal_uInt16 nHeight = static_cast<sal_uInt16>(rSize.Height());
300 const sal_uInt8 cFlags = 128 | ( 7 << 4 );
302 // write values
303 m_rGIF.WriteUInt16( nWidth );
304 m_rGIF.WriteUInt16( nHeight );
305 m_rGIF.WriteUChar( cFlags );
306 m_rGIF.WriteUChar( 0x00 );
307 m_rGIF.WriteUChar( 0x00 );
309 // write dummy palette with two entries (black/white);
310 // we do this only because of a bug in Photoshop, since those can't
311 // read pictures without a global color palette
312 m_rGIF.WriteUInt16( 0 );
313 m_rGIF.WriteUInt16( 255 );
314 m_rGIF.WriteUInt16( 65535 );
316 if( m_rGIF.GetError() )
317 bStatus = false;
321 void GIFWriter::WriteLoopExtension( const Animation& rAnimation )
323 DBG_ASSERT( rAnimation.Count() > 0, "Animation has no bitmaps!" );
325 sal_uInt16 nLoopCount = static_cast<sal_uInt16>(rAnimation.GetLoopCount());
327 // if only one run should take place
328 // the LoopExtension won't be written
329 // The default in this case is a single run
330 if( nLoopCount == 1 )
331 return;
333 // Netscape interprets the LoopCount
334 // as the sole number of _repetitions_
335 if( nLoopCount )
336 nLoopCount--;
338 const sal_uInt8 cLoByte = static_cast<sal_uInt8>(nLoopCount);
339 const sal_uInt8 cHiByte = static_cast<sal_uInt8>( nLoopCount >> 8 );
341 m_rGIF.WriteUChar( 0x21 );
342 m_rGIF.WriteUChar( 0xff );
343 m_rGIF.WriteUChar( 0x0b );
344 m_rGIF.WriteBytes( "NETSCAPE2.0", 11 );
345 m_rGIF.WriteUChar( 0x03 );
346 m_rGIF.WriteUChar( 0x01 );
347 m_rGIF.WriteUChar( cLoByte );
348 m_rGIF.WriteUChar( cHiByte );
349 m_rGIF.WriteUChar( 0x00 );
353 void GIFWriter::WriteLogSizeExtension( const Size& rSize100 )
355 // writer PrefSize in 100th-mm as ApplicationExtension
356 if( rSize100.Width() && rSize100.Height() )
358 m_rGIF.WriteUChar( 0x21 );
359 m_rGIF.WriteUChar( 0xff );
360 m_rGIF.WriteUChar( 0x0b );
361 m_rGIF.WriteBytes( "STARDIV 5.0", 11 );
362 m_rGIF.WriteUChar( 0x09 );
363 m_rGIF.WriteUChar( 0x01 );
364 m_rGIF.WriteUInt32( rSize100.Width() );
365 m_rGIF.WriteUInt32( rSize100.Height() );
366 m_rGIF.WriteUChar( 0x00 );
371 void GIFWriter::WriteImageExtension( tools::Long nTimer, Disposal eDisposal )
373 if( !bStatus )
374 return;
376 const sal_uInt16 nDelay = static_cast<sal_uInt16>(nTimer);
377 sal_uInt8 cFlags = 0;
379 // set Transparency-Flag
380 if( bTransparent )
381 cFlags |= 1;
383 // set Disposal-value
384 if( eDisposal == Disposal::Back )
385 cFlags |= ( 2 << 2 );
386 else if( eDisposal == Disposal::Previous )
387 cFlags |= ( 3 << 2 );
389 m_rGIF.WriteUChar( 0x21 );
390 m_rGIF.WriteUChar( 0xf9 );
391 m_rGIF.WriteUChar( 0x04 );
392 m_rGIF.WriteUChar( cFlags );
393 m_rGIF.WriteUInt16( nDelay );
394 m_rGIF.WriteUChar( static_cast<sal_uInt8>(m_pAcc->GetBestPaletteIndex( BMP_COL_TRANS )) );
395 m_rGIF.WriteUChar( 0x00 );
397 if( m_rGIF.GetError() )
398 bStatus = false;
402 void GIFWriter::WriteLocalHeader()
404 if( !bStatus )
405 return;
407 const sal_uInt16 nPosX = static_cast<sal_uInt16>(nActX);
408 const sal_uInt16 nPosY = static_cast<sal_uInt16>(nActY);
409 const sal_uInt16 nWidth = static_cast<sal_uInt16>(m_pAcc->Width());
410 const sal_uInt16 nHeight = static_cast<sal_uInt16>(m_pAcc->Height());
411 sal_uInt8 cFlags = static_cast<sal_uInt8>( m_pAcc->GetBitCount() - 1 );
413 // set Interlaced-Flag
414 if( nInterlaced )
415 cFlags |= 0x40;
417 // set Flag for the local color palette
418 cFlags |= 0x80;
420 m_rGIF.WriteUChar( 0x2c );
421 m_rGIF.WriteUInt16( nPosX );
422 m_rGIF.WriteUInt16( nPosY );
423 m_rGIF.WriteUInt16( nWidth );
424 m_rGIF.WriteUInt16( nHeight );
425 m_rGIF.WriteUChar( cFlags );
427 if( m_rGIF.GetError() )
428 bStatus = false;
432 void GIFWriter::WritePalette()
434 if( !(bStatus && m_pAcc->HasPalette()) )
435 return;
437 const sal_uInt16 nCount = m_pAcc->GetPaletteEntryCount();
438 const sal_uInt16 nMaxCount = ( 1 << m_pAcc->GetBitCount() );
440 for ( sal_uInt16 i = 0; i < nCount; i++ )
442 const BitmapColor& rColor = m_pAcc->GetPaletteColor( i );
444 m_rGIF.WriteUChar( rColor.GetRed() );
445 m_rGIF.WriteUChar( rColor.GetGreen() );
446 m_rGIF.WriteUChar( rColor.GetBlue() );
449 // fill up the rest with 0
450 if( nCount < nMaxCount )
451 m_rGIF.SeekRel( ( nMaxCount - nCount ) * 3 );
453 if( m_rGIF.GetError() )
454 bStatus = false;
458 void GIFWriter::WriteAccess()
460 GIFLZWCompressor aCompressor;
461 const tools::Long nWidth = m_pAcc->Width();
462 const tools::Long nHeight = m_pAcc->Height();
463 std::unique_ptr<sal_uInt8[]> pBuffer;
464 bool bNative = m_pAcc->GetScanlineFormat() == ScanlineFormat::N8BitPal;
466 if( !bNative )
467 pBuffer.reset(new sal_uInt8[ nWidth ]);
469 assert(bStatus && "should not calling here if status is bad");
470 assert( 8 == m_pAcc->GetBitCount() && m_pAcc->HasPalette()
471 && "by the time we get here, the image should be in palette format");
472 if( !(bStatus && ( 8 == m_pAcc->GetBitCount() ) && m_pAcc->HasPalette()) )
473 return;
475 aCompressor.StartCompression( m_rGIF, m_pAcc->GetBitCount() );
477 tools::Long nY, nT;
479 for( tools::Long i = 0; i < nHeight; ++i )
481 if( nInterlaced )
483 nY = i << 3;
485 if( nY >= nHeight )
487 nT = i - ( ( nHeight + 7 ) >> 3 );
488 nY= ( nT << 3 ) + 4;
490 if( nY >= nHeight )
492 nT -= ( nHeight + 3 ) >> 3;
493 nY = ( nT << 2 ) + 2;
495 if ( nY >= nHeight )
497 nT -= ( ( nHeight + 1 ) >> 2 );
498 nY = ( nT << 1 ) + 1;
503 else
504 nY = i;
506 if( bNative )
507 aCompressor.Compress( m_pAcc->GetScanline( nY ), nWidth );
508 else
510 Scanline pScanline = m_pAcc->GetScanline( nY );
511 for( tools::Long nX = 0; nX < nWidth; nX++ )
512 pBuffer[ nX ] = m_pAcc->GetIndexFromData( pScanline, nX );
514 aCompressor.Compress( pBuffer.get(), nWidth );
517 if ( m_rGIF.GetError() )
518 bStatus = false;
520 MayCallback( nMinPercent + ( nMaxPercent - nMinPercent ) * i / nHeight );
522 if( !bStatus )
523 break;
526 aCompressor.EndCompression();
528 if ( m_rGIF.GetError() )
529 bStatus = false;
533 void GIFWriter::WriteTerminator()
535 if( bStatus )
537 m_rGIF.WriteUChar( 0x3b );
539 if( m_rGIF.GetError() )
540 bStatus = false;
545 bool ExportGifGraphic(SvStream& rStream, const Graphic& rGraphic, FilterConfigItem* pConfigItem)
547 GIFWriter aWriter(rStream);
548 return aWriter.WriteGIF(rGraphic, pConfigItem);
551 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */