diff options
author | Tomaž Vajngerl <tomaz.vajngerl@collabora.co.uk> | 2021-02-15 12:30:25 +0900 |
---|---|---|
committer | Tomaž Vajngerl <quikee@gmail.com> | 2021-02-18 04:12:27 +0100 |
commit | 2270a58d7b25e683449c180576b7647f8d5256f4 (patch) | |
tree | ddc6c7b8ede3c0aa9430bfacd3fe09733e920eac /vcl | |
parent | fb1aa71ca3f7bec3fa6a10c4c3dec37987b213a2 (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.mk | 3 | ||||
-rw-r--r-- | vcl/Library_vcl.mk | 2 | ||||
-rw-r--r-- | vcl/commonfuzzer.mk | 1 | ||||
-rw-r--r-- | vcl/inc/filter/GifWriter.hxx | 28 | ||||
-rw-r--r-- | vcl/source/filter/FilterConfigCache.cxx | 13 | ||||
-rw-r--r-- | vcl/source/filter/egif/egif.cxx | 549 | ||||
-rw-r--r-- | vcl/source/filter/egif/giflzwc.cxx | 225 | ||||
-rw-r--r-- | vcl/source/filter/egif/giflzwc.hxx | 55 | ||||
-rw-r--r-- | vcl/source/filter/graphicfilter.cxx | 47 |
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 ) { |