summaryrefslogtreecommitdiff
path: root/vcl
diff options
context:
space:
mode:
authorTomaž Vajngerl <tomaz.vajngerl@collabora.co.uk>2021-02-15 12:30:25 +0900
committerTomaž Vajngerl <quikee@gmail.com>2021-02-18 04:12:27 +0100
commit2270a58d7b25e683449c180576b7647f8d5256f4 (patch)
treeddc6c7b8ede3c0aa9430bfacd3fe09733e920eac /vcl
parentfb1aa71ca3f7bec3fa6a10c4c3dec37987b213a2 (diff)
Move GIF writer from filter module into VCL
Change-Id: I8db3ca0f7953b44791bda47534220902931fab8d Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111023 Tested-by: Jenkins Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
Diffstat (limited to 'vcl')
-rw-r--r--vcl/CppunitTest_vcl_graphic_test.mk3
-rw-r--r--vcl/Library_vcl.mk2
-rw-r--r--vcl/commonfuzzer.mk1
-rw-r--r--vcl/inc/filter/GifWriter.hxx28
-rw-r--r--vcl/source/filter/FilterConfigCache.cxx13
-rw-r--r--vcl/source/filter/egif/egif.cxx549
-rw-r--r--vcl/source/filter/egif/giflzwc.cxx225
-rw-r--r--vcl/source/filter/egif/giflzwc.hxx55
-rw-r--r--vcl/source/filter/graphicfilter.cxx47
9 files changed, 876 insertions, 47 deletions
diff --git a/vcl/CppunitTest_vcl_graphic_test.mk b/vcl/CppunitTest_vcl_graphic_test.mk
index 9221fd474fdd..901714eb820f 100644
--- a/vcl/CppunitTest_vcl_graphic_test.mk
+++ b/vcl/CppunitTest_vcl_graphic_test.mk
@@ -53,7 +53,4 @@ $(eval $(call gb_CppunitTest_use_vcl,vcl_graphic_test))
$(eval $(call gb_CppunitTest_use_rdb,vcl_graphic_test,services))
$(eval $(call gb_CppunitTest_use_configuration,vcl_graphic_test))
-# we need to explicitly depend on Library_gie because it's dynamically loaded for .gif
-$(call gb_CppunitTest_get_target,vcl_graphic_test) : $(call gb_Library_get_target,gie)
-
# vim: set noet sw=4 ts=4:
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk
index 696112f34b03..10a4ef7ad48d 100644
--- a/vcl/Library_vcl.mk
+++ b/vcl/Library_vcl.mk
@@ -427,6 +427,8 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\
vcl/source/components/dtranscomp \
vcl/source/components/factory \
vcl/source/components/fontident \
+ vcl/source/filter/egif/egif \
+ vcl/source/filter/egif/giflzwc \
vcl/source/filter/eps/eps \
vcl/source/filter/etiff/etiff \
vcl/source/filter/FilterConfigCache \
diff --git a/vcl/commonfuzzer.mk b/vcl/commonfuzzer.mk
index 136c7759d1f1..bcabe85ce26c 100644
--- a/vcl/commonfuzzer.mk
+++ b/vcl/commonfuzzer.mk
@@ -98,7 +98,6 @@ fuzzer_core_libraries = \
chartcontroller \
chartcore \
sm \
- gie \
oox \
proxyfac \
reflection \
diff --git a/vcl/inc/filter/GifWriter.hxx b/vcl/inc/filter/GifWriter.hxx
new file mode 100644
index 000000000000..6512c4bf6f2f
--- /dev/null
+++ b/vcl/inc/filter/GifWriter.hxx
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/graph.hxx>
+#include <vcl/FilterConfigItem.hxx>
+
+VCL_DLLPUBLIC bool ExportGifGraphic(SvStream& rStream, Graphic& rGraphic,
+ FilterConfigItem* pFilterConfigItem);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/FilterConfigCache.cxx b/vcl/source/filter/FilterConfigCache.cxx
index d8ab4d44e0a8..411d44a5801d 100644
--- a/vcl/source/filter/FilterConfigCache.cxx
+++ b/vcl/source/filter/FilterConfigCache.cxx
@@ -41,10 +41,11 @@ using namespace ::com::sun::star::configuration ;
const char* FilterConfigCache::FilterConfigCacheEntry::InternalPixelFilterNameList[] =
{
- IMP_BMP, IMP_GIF, IMP_PNG, IMP_JPEG, IMP_XBM, IMP_XPM,
- EXP_BMP, EXP_JPEG, EXP_PNG, IMP_MOV, IMP_TIFF, EXP_TIFF,
- IMP_TGA, IMP_PICT, IMP_MET, IMP_RAS, IMP_PCX, IMP_PSD,
- IMP_PCD, IMP_PBM, IMP_DXF, nullptr
+ IMP_BMP, IMP_GIF, IMP_PNG, IMP_JPEG, IMP_TIFF,
+ IMP_XBM, IMP_XPM, IMP_TGA, IMP_PICT, IMP_MET, IMP_RAS,
+ IMP_PCX, IMP_MOV, IMP_PSD, IMP_PCD, IMP_PBM, IMP_DXF,
+ EXP_BMP, EXP_GIF, EXP_PNG, EXP_JPEG, EXP_TIFF,
+ nullptr
};
const char* FilterConfigCache::FilterConfigCacheEntry::InternalVectorFilterNameList[] =
@@ -56,7 +57,7 @@ const char* FilterConfigCache::FilterConfigCacheEntry::InternalVectorFilterNameL
const char* FilterConfigCache::FilterConfigCacheEntry::ExternalPixelFilterNameList[] =
{
- "egi", "epb", "epg", "epp", nullptr
+ nullptr
};
void FilterConfigCache::FilterConfigCacheEntry::CreateFilterName( const OUString& rUserDataEntry )
@@ -225,7 +226,7 @@ const char* FilterConfigCache::InternalFilterListForSvxLight[] =
"eps","1","SVIEPS",
"eps","2","SVEEPS",
"gif","1","SVIGIF",
- "gif","2","egi",
+ "gif","2","SVEGIF",
"jpg","1","SVIJPEG",
"jpg","2","SVEJPEG",
"mov","1","SVMOV",
diff --git a/vcl/source/filter/egif/egif.cxx b/vcl/source/filter/egif/egif.cxx
new file mode 100644
index 000000000000..bfd41d952b4c
--- /dev/null
+++ b/vcl/source/filter/egif/egif.cxx
@@ -0,0 +1,549 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <tools/stream.hxx>
+#include <tools/debug.hxx>
+#include <vcl/BitmapReadAccess.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/FilterConfigItem.hxx>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include "giflzwc.hxx"
+#include <memory>
+#include <filter/GifWriter.hxx>
+
+namespace {
+
+class GIFWriter
+{
+ Bitmap aAccBmp;
+ SvStream& m_rGIF;
+ BitmapReadAccess* m_pAcc;
+ sal_uInt32 nMinPercent;
+ sal_uInt32 nMaxPercent;
+ sal_uInt32 nLastPercent;
+ tools::Long nActX;
+ tools::Long nActY;
+ sal_Int32 nInterlaced;
+ bool bStatus;
+ bool bTransparent;
+
+ void MayCallback(sal_uInt32 nPercent);
+ void WriteSignature( bool bGIF89a );
+ void WriteGlobalHeader( const Size& rSize );
+ void WriteLoopExtension( const Animation& rAnimation );
+ void WriteLogSizeExtension( const Size& rSize100 );
+ void WriteImageExtension( tools::Long nTimer, Disposal eDisposal );
+ void WriteLocalHeader();
+ void WritePalette();
+ void WriteAccess();
+ void WriteTerminator();
+
+ bool CreateAccess( const BitmapEx& rBmpEx );
+ void DestroyAccess();
+
+ void WriteAnimation( const Animation& rAnimation );
+ void WriteBitmapEx( const BitmapEx& rBmpEx, const Point& rPoint, bool bExtended,
+ tools::Long nTimer = 0, Disposal eDisposal = Disposal::Not );
+
+ css::uno::Reference< css::task::XStatusIndicator > xStatusIndicator;
+
+public:
+
+ explicit GIFWriter(SvStream &rStream);
+
+ bool WriteGIF( const Graphic& rGraphic, FilterConfigItem* pConfigItem );
+};
+
+}
+
+GIFWriter::GIFWriter(SvStream &rStream)
+ : m_rGIF(rStream)
+ , m_pAcc(nullptr)
+ , nMinPercent(0)
+ , nMaxPercent(0)
+ , nLastPercent(0)
+ , nActX(0)
+ , nActY(0)
+ , nInterlaced(0)
+ , bStatus(false)
+ , bTransparent(false)
+{
+}
+
+
+bool GIFWriter::WriteGIF(const Graphic& rGraphic, FilterConfigItem* pFilterConfigItem)
+{
+ if ( pFilterConfigItem )
+ {
+ xStatusIndicator = pFilterConfigItem->GetStatusIndicator();
+ if ( xStatusIndicator.is() )
+ {
+ xStatusIndicator->start( OUString(), 100 );
+ }
+ }
+
+ Size aSize100;
+ const MapMode aMap( rGraphic.GetPrefMapMode() );
+ bool bLogSize = ( aMap.GetMapUnit() != MapUnit::MapPixel );
+
+ if( bLogSize )
+ aSize100 = OutputDevice::LogicToLogic(rGraphic.GetPrefSize(), aMap, MapMode(MapUnit::Map100thMM));
+
+ bStatus = true;
+ nLastPercent = 0;
+ nInterlaced = 0;
+ m_pAcc = nullptr;
+
+ if ( pFilterConfigItem )
+ nInterlaced = pFilterConfigItem->ReadInt32( "Interlaced", 0 );
+
+ m_rGIF.SetEndian( SvStreamEndian::LITTLE );
+
+ if( rGraphic.IsAnimated() )
+ {
+ const Animation& rAnimation = rGraphic.GetAnimation();
+
+ WriteSignature( true );
+
+ if ( bStatus )
+ {
+ WriteGlobalHeader( rAnimation.GetDisplaySizePixel() );
+
+ if( bStatus )
+ {
+ WriteLoopExtension( rAnimation );
+
+ if( bStatus )
+ WriteAnimation( rAnimation );
+ }
+ }
+ }
+ else
+ {
+ const bool bGrafTrans = rGraphic.IsTransparent();
+
+ BitmapEx aBmpEx = rGraphic.GetBitmapEx();
+
+ nMinPercent = 0;
+ nMaxPercent = 100;
+
+ WriteSignature( bGrafTrans || bLogSize );
+
+ if( bStatus )
+ {
+ WriteGlobalHeader( aBmpEx.GetSizePixel() );
+
+ if( bStatus )
+ WriteBitmapEx( aBmpEx, Point(), bGrafTrans );
+ }
+ }
+
+ if( bStatus )
+ {
+ if( bLogSize )
+ WriteLogSizeExtension( aSize100 );
+
+ WriteTerminator();
+ }
+
+ if ( xStatusIndicator.is() )
+ xStatusIndicator->end();
+
+ return bStatus;
+}
+
+
+void GIFWriter::WriteBitmapEx( const BitmapEx& rBmpEx, const Point& rPoint,
+ bool bExtended, tools::Long nTimer, Disposal eDisposal )
+{
+ if( !CreateAccess( rBmpEx ) )
+ return;
+
+ nActX = rPoint.X();
+ nActY = rPoint.Y();
+
+ if( bExtended )
+ WriteImageExtension( nTimer, eDisposal );
+
+ if( bStatus )
+ {
+ WriteLocalHeader();
+
+ if( bStatus )
+ {
+ WritePalette();
+
+ if( bStatus )
+ WriteAccess();
+ }
+ }
+
+ DestroyAccess();
+}
+
+
+void GIFWriter::WriteAnimation( const Animation& rAnimation )
+{
+ const sal_uInt16 nCount = rAnimation.Count();
+
+ if( !nCount )
+ return;
+
+ const double fStep = 100. / nCount;
+
+ nMinPercent = 0;
+ nMaxPercent = static_cast<sal_uInt32>(fStep);
+
+ for( sal_uInt16 i = 0; i < nCount; i++ )
+ {
+ const AnimationBitmap& rAnimationBitmap = rAnimation.Get( i );
+
+ WriteBitmapEx(rAnimationBitmap.maBitmapEx, rAnimationBitmap.maPositionPixel, true,
+ rAnimationBitmap.mnWait, rAnimationBitmap.meDisposal );
+ nMinPercent = nMaxPercent;
+ nMaxPercent = static_cast<sal_uInt32>(nMaxPercent + fStep);
+ }
+}
+
+
+void GIFWriter::MayCallback(sal_uInt32 nPercent)
+{
+ if ( xStatusIndicator.is() )
+ {
+ if( nPercent >= nLastPercent + 3 )
+ {
+ nLastPercent = nPercent;
+ if ( nPercent <= 100 )
+ xStatusIndicator->setValue( nPercent );
+ }
+ }
+}
+
+
+bool GIFWriter::CreateAccess( const BitmapEx& rBmpEx )
+{
+ if( bStatus )
+ {
+ Bitmap aMask( rBmpEx.GetMask() );
+
+ aAccBmp = rBmpEx.GetBitmap();
+ bTransparent = false;
+
+ if( !!aMask )
+ {
+ if( aAccBmp.Convert( BmpConversion::N8BitTrans ) )
+ {
+ aMask.Convert( BmpConversion::N1BitThreshold );
+ aAccBmp.Replace( aMask, BMP_COL_TRANS );
+ bTransparent = true;
+ }
+ else
+ aAccBmp.Convert( BmpConversion::N8BitColors );
+ }
+ else
+ aAccBmp.Convert( BmpConversion::N8BitColors );
+
+ m_pAcc = aAccBmp.AcquireReadAccess();
+
+ if( !m_pAcc )
+ bStatus = false;
+ }
+
+ return bStatus;
+}
+
+
+void GIFWriter::DestroyAccess()
+{
+ Bitmap::ReleaseAccess( m_pAcc );
+ m_pAcc = nullptr;
+}
+
+
+void GIFWriter::WriteSignature( bool bGIF89a )
+{
+ if( bStatus )
+ {
+ m_rGIF.WriteBytes(bGIF89a ? "GIF89a" : "GIF87a" , 6);
+
+ if( m_rGIF.GetError() )
+ bStatus = false;
+ }
+}
+
+
+void GIFWriter::WriteGlobalHeader( const Size& rSize )
+{
+ if( !bStatus )
+ return;
+
+ // 256 colors
+ const sal_uInt16 nWidth = static_cast<sal_uInt16>(rSize.Width());
+ const sal_uInt16 nHeight = static_cast<sal_uInt16>(rSize.Height());
+ const sal_uInt8 cFlags = 128 | ( 7 << 4 );
+
+ // write values
+ m_rGIF.WriteUInt16( nWidth );
+ m_rGIF.WriteUInt16( nHeight );
+ m_rGIF.WriteUChar( cFlags );
+ m_rGIF.WriteUChar( 0x00 );
+ m_rGIF.WriteUChar( 0x00 );
+
+ // write dummy palette with two entries (black/white);
+ // we do this only because of a bug in Photoshop, since those can't
+ // read pictures without a global color palette
+ m_rGIF.WriteUInt16( 0 );
+ m_rGIF.WriteUInt16( 255 );
+ m_rGIF.WriteUInt16( 65535 );
+
+ if( m_rGIF.GetError() )
+ bStatus = false;
+}
+
+
+void GIFWriter::WriteLoopExtension( const Animation& rAnimation )
+{
+ DBG_ASSERT( rAnimation.Count() > 0, "Animation has no bitmaps!" );
+
+ sal_uInt16 nLoopCount = static_cast<sal_uInt16>(rAnimation.GetLoopCount());
+
+ // if only one run should take place
+ // the LoopExtension won't be written
+ // The default in this case is a single run
+ if( nLoopCount == 1 )
+ return;
+
+ // Netscape interprets the LoopCount
+ // as the sole number of _repetitions_
+ if( nLoopCount )
+ nLoopCount--;
+
+ const sal_uInt8 cLoByte = static_cast<sal_uInt8>(nLoopCount);
+ const sal_uInt8 cHiByte = static_cast<sal_uInt8>( nLoopCount >> 8 );
+
+ m_rGIF.WriteUChar( 0x21 );
+ m_rGIF.WriteUChar( 0xff );
+ m_rGIF.WriteUChar( 0x0b );
+ m_rGIF.WriteBytes( "NETSCAPE2.0", 11 );
+ m_rGIF.WriteUChar( 0x03 );
+ m_rGIF.WriteUChar( 0x01 );
+ m_rGIF.WriteUChar( cLoByte );
+ m_rGIF.WriteUChar( cHiByte );
+ m_rGIF.WriteUChar( 0x00 );
+}
+
+
+void GIFWriter::WriteLogSizeExtension( const Size& rSize100 )
+{
+ // writer PrefSize in 100th-mm as ApplicationExtension
+ if( rSize100.Width() && rSize100.Height() )
+ {
+ m_rGIF.WriteUChar( 0x21 );
+ m_rGIF.WriteUChar( 0xff );
+ m_rGIF.WriteUChar( 0x0b );
+ m_rGIF.WriteBytes( "STARDIV 5.0", 11 );
+ m_rGIF.WriteUChar( 0x09 );
+ m_rGIF.WriteUChar( 0x01 );
+ m_rGIF.WriteUInt32( rSize100.Width() );
+ m_rGIF.WriteUInt32( rSize100.Height() );
+ m_rGIF.WriteUChar( 0x00 );
+ }
+}
+
+
+void GIFWriter::WriteImageExtension( tools::Long nTimer, Disposal eDisposal )
+{
+ if( !bStatus )
+ return;
+
+ const sal_uInt16 nDelay = static_cast<sal_uInt16>(nTimer);
+ sal_uInt8 cFlags = 0;
+
+ // set Transparency-Flag
+ if( bTransparent )
+ cFlags |= 1;
+
+ // set Disposal-value
+ if( eDisposal == Disposal::Back )
+ cFlags |= ( 2 << 2 );
+ else if( eDisposal == Disposal::Previous )
+ cFlags |= ( 3 << 2 );
+
+ m_rGIF.WriteUChar( 0x21 );
+ m_rGIF.WriteUChar( 0xf9 );
+ m_rGIF.WriteUChar( 0x04 );
+ m_rGIF.WriteUChar( cFlags );
+ m_rGIF.WriteUInt16( nDelay );
+ m_rGIF.WriteUChar( m_pAcc->GetBestPaletteIndex( BMP_COL_TRANS ) );
+ m_rGIF.WriteUChar( 0x00 );
+
+ if( m_rGIF.GetError() )
+ bStatus = false;
+}
+
+
+void GIFWriter::WriteLocalHeader()
+{
+ if( !bStatus )
+ return;
+
+ const sal_uInt16 nPosX = static_cast<sal_uInt16>(nActX);
+ const sal_uInt16 nPosY = static_cast<sal_uInt16>(nActY);
+ const sal_uInt16 nWidth = static_cast<sal_uInt16>(m_pAcc->Width());
+ const sal_uInt16 nHeight = static_cast<sal_uInt16>(m_pAcc->Height());
+ sal_uInt8 cFlags = static_cast<sal_uInt8>( m_pAcc->GetBitCount() - 1 );
+
+ // set Interlaced-Flag
+ if( nInterlaced )
+ cFlags |= 0x40;
+
+ // set Flag for the local color palette
+ cFlags |= 0x80;
+
+ m_rGIF.WriteUChar( 0x2c );
+ m_rGIF.WriteUInt16( nPosX );
+ m_rGIF.WriteUInt16( nPosY );
+ m_rGIF.WriteUInt16( nWidth );
+ m_rGIF.WriteUInt16( nHeight );
+ m_rGIF.WriteUChar( cFlags );
+
+ if( m_rGIF.GetError() )
+ bStatus = false;
+}
+
+
+void GIFWriter::WritePalette()
+{
+ if( !(bStatus && m_pAcc->HasPalette()) )
+ return;
+
+ const sal_uInt16 nCount = m_pAcc->GetPaletteEntryCount();
+ const sal_uInt16 nMaxCount = ( 1 << m_pAcc->GetBitCount() );
+
+ for ( sal_uInt16 i = 0; i < nCount; i++ )
+ {
+ const BitmapColor& rColor = m_pAcc->GetPaletteColor( i );
+
+ m_rGIF.WriteUChar( rColor.GetRed() );
+ m_rGIF.WriteUChar( rColor.GetGreen() );
+ m_rGIF.WriteUChar( rColor.GetBlue() );
+ }
+
+ // fill up the rest with 0
+ if( nCount < nMaxCount )
+ m_rGIF.SeekRel( ( nMaxCount - nCount ) * 3 );
+
+ if( m_rGIF.GetError() )
+ bStatus = false;
+}
+
+
+void GIFWriter::WriteAccess()
+{
+ GIFLZWCompressor aCompressor;
+ const tools::Long nWidth = m_pAcc->Width();
+ const tools::Long nHeight = m_pAcc->Height();
+ std::unique_ptr<sal_uInt8[]> pBuffer;
+ bool bNative = m_pAcc->GetScanlineFormat() == ScanlineFormat::N8BitPal;
+
+ if( !bNative )
+ pBuffer.reset(new sal_uInt8[ nWidth ]);
+
+ if( !(bStatus && ( 8 == m_pAcc->GetBitCount() ) && m_pAcc->HasPalette()) )
+ return;
+
+ aCompressor.StartCompression( m_rGIF, m_pAcc->GetBitCount() );
+
+ tools::Long nY, nT;
+
+ for( tools::Long i = 0; i < nHeight; ++i )
+ {
+ if( nInterlaced )
+ {
+ nY = i << 3;
+
+ if( nY >= nHeight )
+ {
+ nT = i - ( ( nHeight + 7 ) >> 3 );
+ nY= ( nT << 3 ) + 4;
+
+ if( nY >= nHeight )
+ {
+ nT -= ( nHeight + 3 ) >> 3;
+ nY = ( nT << 2 ) + 2;
+
+ if ( nY >= nHeight )
+ {
+ nT -= ( ( nHeight + 1 ) >> 2 );
+ nY = ( nT << 1 ) + 1;
+ }
+ }
+ }
+ }
+ else
+ nY = i;
+
+ if( bNative )
+ aCompressor.Compress( m_pAcc->GetScanline( nY ), nWidth );
+ else
+ {
+ Scanline pScanline = m_pAcc->GetScanline( nY );
+ for( tools::Long nX = 0; nX < nWidth; nX++ )
+ pBuffer[ nX ] = m_pAcc->GetIndexFromData( pScanline, nX );
+
+ aCompressor.Compress( pBuffer.get(), nWidth );
+ }
+
+ if ( m_rGIF.GetError() )
+ bStatus = false;
+
+ MayCallback( nMinPercent + ( nMaxPercent - nMinPercent ) * i / nHeight );
+
+ if( !bStatus )
+ break;
+ }
+
+ aCompressor.EndCompression();
+
+ if ( m_rGIF.GetError() )
+ bStatus = false;
+}
+
+
+void GIFWriter::WriteTerminator()
+{
+ if( bStatus )
+ {
+ m_rGIF.WriteUChar( 0x3b );
+
+ if( m_rGIF.GetError() )
+ bStatus = false;
+ }
+}
+
+
+bool ExportGifGraphic(SvStream& rStream, Graphic& rGraphic, FilterConfigItem* pConfigItem)
+{
+ GIFWriter aWriter(rStream);
+ return aWriter.WriteGIF(rGraphic, pConfigItem);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/egif/giflzwc.cxx b/vcl/source/filter/egif/giflzwc.cxx
new file mode 100644
index 000000000000..41c65d2da4c2
--- /dev/null
+++ b/vcl/source/filter/egif/giflzwc.cxx
@@ -0,0 +1,225 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <tools/stream.hxx>
+#include "giflzwc.hxx"
+#include <array>
+
+
+class GIFImageDataOutputStream
+{
+private:
+
+ void FlushBlockBuf();
+ inline void FlushBitsBufsFullBytes();
+
+ SvStream& rStream;
+ std::array<sal_uInt8, 255>
+ pBlockBuf;
+ sal_uInt8 nBlockBufSize;
+ sal_uInt32 nBitsBuf;
+ sal_uInt16 nBitsBufSize;
+
+public:
+
+ GIFImageDataOutputStream( SvStream & rGIF, sal_uInt8 nLZWDataSize );
+ ~GIFImageDataOutputStream();
+
+ inline void WriteBits( sal_uInt16 nCode, sal_uInt16 nCodeLen );
+};
+
+
+inline void GIFImageDataOutputStream::FlushBitsBufsFullBytes()
+{
+ while (nBitsBufSize>=8)
+ {
+ if( nBlockBufSize==255 )
+ FlushBlockBuf();
+
+ pBlockBuf[nBlockBufSize++] = static_cast<sal_uInt8>(nBitsBuf);
+ nBitsBuf >>= 8;
+ nBitsBufSize -= 8;
+ }
+}
+
+
+inline void GIFImageDataOutputStream::WriteBits( sal_uInt16 nCode, sal_uInt16 nCodeLen )
+{
+ if( nBitsBufSize+nCodeLen>32 )
+ FlushBitsBufsFullBytes();
+
+ nBitsBuf |= static_cast<sal_uInt32>(nCode) << nBitsBufSize;
+ nBitsBufSize = nBitsBufSize + nCodeLen;
+}
+
+
+GIFImageDataOutputStream::GIFImageDataOutputStream( SvStream & rGIF, sal_uInt8 nLZWDataSize ) :
+ rStream(rGIF), nBlockBufSize(0), nBitsBuf(0), nBitsBufSize(0)
+{
+ rStream.WriteUChar( nLZWDataSize );
+}
+
+
+GIFImageDataOutputStream::~GIFImageDataOutputStream()
+{
+ WriteBits(0,7);
+ FlushBitsBufsFullBytes();
+ FlushBlockBuf();
+ rStream.WriteUChar( 0 );
+}
+
+
+void GIFImageDataOutputStream::FlushBlockBuf()
+{
+ if( nBlockBufSize )
+ {
+ rStream.WriteUChar( nBlockBufSize );
+ rStream.WriteBytes(pBlockBuf.data(), nBlockBufSize);
+ nBlockBufSize = 0;
+ }
+}
+
+
+struct GIFLZWCTreeNode
+{
+
+ GIFLZWCTreeNode* pBrother; // next node which has the same father
+ GIFLZWCTreeNode* pFirstChild; // first
+ sal_uInt16 nCode; // the code for the string of pixel values which comes about
+ sal_uInt16 nValue; // the pixel value
+};
+
+
+GIFLZWCompressor::GIFLZWCompressor()
+ : pPrefix(nullptr), nDataSize(0), nClearCode(0),
+ nEOICode(0), nTableSize(0), nCodeSize(0)
+{
+}
+
+
+GIFLZWCompressor::~GIFLZWCompressor()
+{
+ if (pIDOS!=nullptr) EndCompression();
+}
+
+
+void GIFLZWCompressor::StartCompression( SvStream& rGIF, sal_uInt16 nPixelSize )
+{
+ if( pIDOS )
+ return;
+
+ sal_uInt16 i;
+
+ nDataSize = nPixelSize;
+
+ if( nDataSize < 2 )
+ nDataSize=2;
+
+ nClearCode=1<<nDataSize;
+ nEOICode=nClearCode+1;
+ nTableSize=nEOICode+1;
+ nCodeSize=nDataSize+1;
+
+ pIDOS.reset(new GIFImageDataOutputStream(rGIF,static_cast<sal_uInt8>(nDataSize)));
+ pTable.reset(new GIFLZWCTreeNode[4096]);
+
+ for (i=0; i<4096; i++)
+ {
+ pTable[i].pBrother = pTable[i].pFirstChild = nullptr;
+ pTable[i].nCode = i;
+ pTable[i].nValue = static_cast<sal_uInt8>( i );
+ }
+
+ pPrefix = nullptr;
+ pIDOS->WriteBits( nClearCode,nCodeSize );
+}
+
+void GIFLZWCompressor::Compress(sal_uInt8* pSrc, sal_uInt32 nSize)
+{
+ if( !pIDOS )
+ return;
+
+ GIFLZWCTreeNode* p;
+ sal_uInt16 i;
+ sal_uInt8 nV;
+
+ if( !pPrefix && nSize )
+ {
+ pPrefix=&pTable[*pSrc++];
+ nSize--;
+ }
+
+ while( nSize )
+ {
+ nSize--;
+ nV=*pSrc++;
+ for( p=pPrefix->pFirstChild; p!=nullptr; p=p->pBrother )
+ {
+ if (p->nValue==nV)
+ break;
+ }
+
+ if( p)
+ pPrefix=p;
+ else
+ {
+ pIDOS->WriteBits(pPrefix->nCode,nCodeSize);
+
+ if (nTableSize==4096)
+ {
+ pIDOS->WriteBits(nClearCode,nCodeSize);
+
+ for (i=0; i<nClearCode; i++)
+ pTable[i].pFirstChild=nullptr;
+
+ nCodeSize=nDataSize+1;
+ nTableSize=nEOICode+1;
+ }
+ else
+ {
+ if(nTableSize==static_cast<sal_uInt16>(1<<nCodeSize))
+ nCodeSize++;
+
+ p=&pTable[nTableSize++];
+ p->pBrother=pPrefix->pFirstChild;
+ pPrefix->pFirstChild=p;
+ p->nValue=nV;
+ p->pFirstChild=nullptr;
+ }
+
+ pPrefix=&pTable[nV];
+ }
+ }
+}
+
+void GIFLZWCompressor::EndCompression()
+{
+ if( pIDOS )
+ {
+ if( pPrefix )
+ pIDOS->WriteBits(pPrefix->nCode,nCodeSize);
+
+ pIDOS->WriteBits( nEOICode,nCodeSize );
+ pTable.reset();
+ pIDOS.reset();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/egif/giflzwc.hxx b/vcl/source/filter/egif/giflzwc.hxx
new file mode 100644
index 000000000000..057710c85230
--- /dev/null
+++ b/vcl/source/filter/egif/giflzwc.hxx
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_FILTER_SOURCE_GRAPHICFILTER_EGIF_GIFLZWC_HXX
+#define INCLUDED_FILTER_SOURCE_GRAPHICFILTER_EGIF_GIFLZWC_HXX
+
+#include <vcl/mapmod.hxx>
+
+
+class GIFImageDataOutputStream;
+struct GIFLZWCTreeNode;
+
+
+class GIFLZWCompressor
+{
+private:
+
+ std::unique_ptr<GIFImageDataOutputStream> pIDOS;
+ std::unique_ptr<GIFLZWCTreeNode[]> pTable;
+ GIFLZWCTreeNode* pPrefix;
+ sal_uInt16 nDataSize;
+ sal_uInt16 nClearCode;
+ sal_uInt16 nEOICode;
+ sal_uInt16 nTableSize;
+ sal_uInt16 nCodeSize;
+
+public:
+
+ GIFLZWCompressor();
+ ~GIFLZWCompressor();
+
+ void StartCompression( SvStream& rGIF, sal_uInt16 nPixelSize );
+ void Compress(sal_uInt8* pSrc, sal_uInt32 nSize);
+ void EndCompression();
+};
+
+#endif // INCLUDED_FILTER_SOURCE_GRAPHICFILTER_EGIF_GIFLZWC_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/graphicfilter.cxx b/vcl/source/filter/graphicfilter.cxx
index b6cf71d078f6..6b6408076e80 100644
--- a/vcl/source/filter/graphicfilter.cxx
+++ b/vcl/source/filter/graphicfilter.cxx
@@ -60,6 +60,7 @@
#include <filter/PcdReader.hxx>
#include <filter/PbmReader.hxx>
#include <filter/DxfReader.hxx>
+#include <filter/GifWriter.hxx>
#include <osl/module.hxx>
#include <com/sun/star/uno/Reference.h>
#include <com/sun/star/awt/Size.hpp>
@@ -1950,12 +1951,6 @@ ErrCode GraphicFilter::ExportGraphic( const Graphic& rGraphic, const INetURLObje
return nRetValue;
}
-#ifdef DISABLE_DYNLOADING
-
-extern "C" bool egiGraphicExport( SvStream& rStream, Graphic& rGraphic, FilterConfigItem* pConfigItem );
-
-#endif
-
ErrCode GraphicFilter::ExportGraphic( const Graphic& rGraphic, const OUString& rPath,
SvStream& rOStm, sal_uInt16 nFormat, const css::uno::Sequence< css::beans::PropertyValue >* pFilterData )
{
@@ -1983,7 +1978,6 @@ ErrCode GraphicFilter::ExportGraphic( const Graphic& rGraphic, const OUString& r
FilterConfigItem aConfigItem( pFilterData );
OUString aFilterName( pConfig->GetExportFilterName( nFormat ) );
- OUString aExternalFilterName(pConfig->GetExternalFilterName(nFormat, true));
ErrCode nStatus = ERRCODE_NONE;
GraphicType eType;
Graphic aGraphic = ImpGetScaledGraphic( rGraphic, aConfigItem );
@@ -2045,7 +2039,7 @@ ErrCode GraphicFilter::ExportGraphic( const Graphic& rGraphic, const OUString& r
if( rOStm.GetError() )
nStatus = ERRCODE_GRFILTER_IOERROR;
}
- if (aFilterName.equalsIgnoreAsciiCase(EXP_TIFF))
+ else if (aFilterName.equalsIgnoreAsciiCase(EXP_TIFF))
{
if (!ExportTiffGraphicImport(rOStm, aGraphic, &aConfigItem))
nStatus = ERRCODE_GRFILTER_FORMATERROR;
@@ -2053,6 +2047,14 @@ ErrCode GraphicFilter::ExportGraphic( const Graphic& rGraphic, const OUString& r
if( rOStm.GetError() )
nStatus = ERRCODE_GRFILTER_IOERROR;
}
+ else if (aFilterName.equalsIgnoreAsciiCase(EXP_GIF))
+ {
+ if (!ExportGifGraphic(rOStm, aGraphic, &aConfigItem))
+ nStatus = ERRCODE_GRFILTER_FORMATERROR;
+
+ if( rOStm.GetError() )
+ nStatus = ERRCODE_GRFILTER_IOERROR;
+ }
else if( aFilterName.equalsIgnoreAsciiCase( EXP_SVMETAFILE ) )
{
sal_Int32 nVersion = aConfigItem.ReadInt32( "Version", 0 ) ;
@@ -2278,35 +2280,6 @@ ErrCode GraphicFilter::ExportGraphic( const Graphic& rGraphic, const OUString& r
else
nStatus = ERRCODE_GRFILTER_FILTERERROR;
}
- else
- {
- sal_Int32 nIdx {aFilterPath.isEmpty() ? -1 : 0};
- while (nIdx>=0)
- {
-#ifndef DISABLE_DYNLOADING
- OUString aPhysicalName( ImpCreateFullFilterPath( aFilterPath.getToken(0, ';', nIdx), aFilterName ) );
- osl::Module aLibrary( aPhysicalName );
-
- PFilterCall pFunc = nullptr;
- if (aExternalFilterName == "egi")
- pFunc = reinterpret_cast<PFilterCall>(aLibrary.getFunctionSymbol("egiGraphicExport"));
- // Execute dialog in DLL
- #else
- --nIdx; // Just one iteration
- PFilterCall pFunc = NULL;
- if (aExternalFilterName == "egi")
- pFunc = egiGraphicExport;
- #endif
- if( pFunc )
- {
- if ( !(*pFunc)( rOStm, aGraphic, &aConfigItem ) )
- nStatus = ERRCODE_GRFILTER_FORMATERROR;
- break;
- }
- else
- nStatus = ERRCODE_GRFILTER_FILTERERROR;
- }
- }
}
if( nStatus != ERRCODE_NONE )
{