summaryrefslogtreecommitdiffstats
path: root/vcl/source/gdi/impgraph.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/source/gdi/impgraph.cxx')
-rw-r--r--vcl/source/gdi/impgraph.cxx1882
1 files changed, 1882 insertions, 0 deletions
diff --git a/vcl/source/gdi/impgraph.cxx b/vcl/source/gdi/impgraph.cxx
new file mode 100644
index 000000000..e06663706
--- /dev/null
+++ b/vcl/source/gdi/impgraph.cxx
@@ -0,0 +1,1882 @@
+/* -*- 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 <sal/config.h>
+#include <sal/log.hxx>
+
+#include <comphelper/fileformat.h>
+#include <o3tl/deleter.hxx>
+#include <tools/fract.hxx>
+#include <tools/vcompat.hxx>
+#include <tools/urlobj.hxx>
+#include <tools/stream.hxx>
+#include <unotools/ucbhelper.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <unotools/tempfile.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/gfxlink.hxx>
+#include <vcl/cvtgrf.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/metaact.hxx>
+#include <impgraph.hxx>
+#include <com/sun/star/graphic/XPrimitive2D.hpp>
+#include <vcl/dibtools.hxx>
+#include <map>
+#include <memory>
+#include <vcl/gdimetafiletools.hxx>
+#include <TypeSerializer.hxx>
+#include <vcl/pdfread.hxx>
+
+#define GRAPHIC_MTFTOBMP_MAXEXT 2048
+#define GRAPHIC_STREAMBUFSIZE 8192UL
+
+#define SYS_WINMETAFILE 0x00000003L
+#define SYS_WNTMETAFILE 0x00000004L
+#define SYS_OS2METAFILE 0x00000005L
+#define SYS_MACMETAFILE 0x00000006L
+
+#define GRAPHIC_FORMAT_50 COMPAT_FORMAT( 'G', 'R', 'F', '5' )
+#define NATIVE_FORMAT_50 COMPAT_FORMAT( 'N', 'A', 'T', '5' )
+
+namespace {
+
+constexpr sal_uInt32 constSvgMagic((sal_uInt32('s') << 24) | (sal_uInt32('v') << 16) | (sal_uInt32('g') << 8) | sal_uInt32('0'));
+constexpr sal_uInt32 constWmfMagic((sal_uInt32('w') << 24) | (sal_uInt32('m') << 16) | (sal_uInt32('f') << 8) | sal_uInt32('0'));
+constexpr sal_uInt32 constEmfMagic((sal_uInt32('e') << 24) | (sal_uInt32('m') << 16) | (sal_uInt32('f') << 8) | sal_uInt32('0'));
+constexpr sal_uInt32 constPdfMagic((sal_uInt32('s') << 24) | (sal_uInt32('v') << 16) | (sal_uInt32('g') << 8) | sal_uInt32('0'));
+
+}
+
+using namespace com::sun::star;
+
+class ImpSwapFile
+{
+private:
+ INetURLObject maSwapURL;
+ OUString maOriginURL;
+
+public:
+ ImpSwapFile(INetURLObject const & rSwapURL, OUString const & rOriginURL)
+ : maSwapURL(rSwapURL)
+ , maOriginURL(rOriginURL)
+ {
+ }
+
+ ~ImpSwapFile() COVERITY_NOEXCEPT_FALSE
+ {
+ utl::UCBContentHelper::Kill(maSwapURL.GetMainURL(INetURLObject::DecodeMechanism::NONE));
+ }
+
+ INetURLObject getSwapURL()
+ {
+ return maSwapURL;
+ }
+
+ OUString getSwapURLString()
+ {
+ return maSwapURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+ }
+
+ OUString const & getOriginURL() { return maOriginURL; }
+
+ std::unique_ptr<SvStream> openOutputStream()
+ {
+ OUString sSwapURL = getSwapURLString();
+ if (!sSwapURL.isEmpty())
+ {
+ try
+ {
+ return utl::UcbStreamHelper::CreateStream(sSwapURL, StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE);
+ }
+ catch (const css::uno::Exception&)
+ {
+ }
+ }
+ return std::unique_ptr<SvStream>();
+ }
+};
+
+OUString ImpGraphic::getSwapFileURL()
+{
+ if (mpSwapFile)
+ return mpSwapFile->getSwapURL().GetMainURL(INetURLObject::DecodeMechanism::NONE);
+ return OUString();
+}
+
+ImpGraphic::ImpGraphic() :
+ meType ( GraphicType::NONE ),
+ mnSizeBytes ( 0 ),
+ mbSwapOut ( false ),
+ mbDummyContext ( false ),
+ maLastUsed (std::chrono::high_resolution_clock::now()),
+ mbPrepared ( false )
+{
+}
+
+ImpGraphic::ImpGraphic(const ImpGraphic& rImpGraphic)
+ : maMetaFile(rImpGraphic.maMetaFile)
+ , maBitmapEx(rImpGraphic.maBitmapEx)
+ , maSwapInfo(rImpGraphic.maSwapInfo)
+ , mpContext(rImpGraphic.mpContext)
+ , mpSwapFile(rImpGraphic.mpSwapFile)
+ , mpGfxLink(rImpGraphic.mpGfxLink)
+ , meType(rImpGraphic.meType)
+ , mnSizeBytes(rImpGraphic.mnSizeBytes)
+ , mbSwapOut(rImpGraphic.mbSwapOut)
+ , mbDummyContext(rImpGraphic.mbDummyContext)
+ , maVectorGraphicData(rImpGraphic.maVectorGraphicData)
+ , maGraphicExternalLink(rImpGraphic.maGraphicExternalLink)
+ , maLastUsed (std::chrono::high_resolution_clock::now())
+ , mbPrepared (rImpGraphic.mbPrepared)
+{
+ if( rImpGraphic.mpAnimation )
+ {
+ mpAnimation = std::make_unique<Animation>( *rImpGraphic.mpAnimation );
+ maBitmapEx = mpAnimation->GetBitmapEx();
+ }
+}
+
+ImpGraphic::ImpGraphic(ImpGraphic&& rImpGraphic) noexcept
+ : maMetaFile(std::move(rImpGraphic.maMetaFile))
+ , maBitmapEx(std::move(rImpGraphic.maBitmapEx))
+ , maSwapInfo(std::move(rImpGraphic.maSwapInfo))
+ , mpAnimation(std::move(rImpGraphic.mpAnimation))
+ , mpContext(std::move(rImpGraphic.mpContext))
+ , mpSwapFile(std::move(rImpGraphic.mpSwapFile))
+ , mpGfxLink(std::move(rImpGraphic.mpGfxLink))
+ , meType(rImpGraphic.meType)
+ , mnSizeBytes(rImpGraphic.mnSizeBytes)
+ , mbSwapOut(rImpGraphic.mbSwapOut)
+ , mbDummyContext(rImpGraphic.mbDummyContext)
+ , maVectorGraphicData(std::move(rImpGraphic.maVectorGraphicData))
+ , maGraphicExternalLink(rImpGraphic.maGraphicExternalLink)
+ , maLastUsed (std::chrono::high_resolution_clock::now())
+ , mbPrepared (rImpGraphic.mbPrepared)
+{
+ rImpGraphic.ImplClear();
+ rImpGraphic.mbDummyContext = false;
+}
+
+ImpGraphic::ImpGraphic(GraphicExternalLink const & rGraphicExternalLink) :
+ meType ( GraphicType::Default ),
+ mnSizeBytes ( 0 ),
+ mbSwapOut ( false ),
+ mbDummyContext ( false ),
+ maGraphicExternalLink(rGraphicExternalLink),
+ maLastUsed (std::chrono::high_resolution_clock::now()),
+ mbPrepared (false)
+{
+}
+
+ImpGraphic::ImpGraphic( const Bitmap& rBitmap ) :
+ maBitmapEx ( rBitmap ),
+ meType ( !rBitmap.IsEmpty() ? GraphicType::Bitmap : GraphicType::NONE ),
+ mnSizeBytes ( 0 ),
+ mbSwapOut ( false ),
+ mbDummyContext ( false ),
+ maLastUsed (std::chrono::high_resolution_clock::now()),
+ mbPrepared (false)
+{
+}
+
+ImpGraphic::ImpGraphic( const BitmapEx& rBitmapEx ) :
+ maBitmapEx ( rBitmapEx ),
+ meType ( !rBitmapEx.IsEmpty() ? GraphicType::Bitmap : GraphicType::NONE ),
+ mnSizeBytes ( 0 ),
+ mbSwapOut ( false ),
+ mbDummyContext ( false ),
+ maLastUsed (std::chrono::high_resolution_clock::now()),
+ mbPrepared (false)
+{
+}
+
+ImpGraphic::ImpGraphic(const std::shared_ptr<VectorGraphicData>& rVectorGraphicDataPtr)
+: meType( rVectorGraphicDataPtr ? GraphicType::Bitmap : GraphicType::NONE ),
+ mnSizeBytes( 0 ),
+ mbSwapOut( false ),
+ mbDummyContext ( false ),
+ maVectorGraphicData(rVectorGraphicDataPtr),
+ maLastUsed (std::chrono::high_resolution_clock::now()),
+ mbPrepared (false)
+{
+}
+
+ImpGraphic::ImpGraphic( const Animation& rAnimation ) :
+ maBitmapEx ( rAnimation.GetBitmapEx() ),
+ mpAnimation ( std::make_unique<Animation>( rAnimation ) ),
+ meType ( GraphicType::Bitmap ),
+ mnSizeBytes ( 0 ),
+ mbSwapOut ( false ),
+ mbDummyContext ( false ),
+ maLastUsed (std::chrono::high_resolution_clock::now()),
+ mbPrepared (false)
+{
+}
+
+ImpGraphic::ImpGraphic( const GDIMetaFile& rMtf ) :
+ maMetaFile ( rMtf ),
+ meType ( GraphicType::GdiMetafile ),
+ mnSizeBytes ( 0 ),
+ mbSwapOut ( false ),
+ mbDummyContext ( false ),
+ maLastUsed (std::chrono::high_resolution_clock::now()),
+ mbPrepared (false)
+{
+}
+
+ImpGraphic::~ImpGraphic()
+{
+ vcl::graphic::Manager::get().unregisterGraphic(this);
+}
+
+ImpGraphic& ImpGraphic::operator=( const ImpGraphic& rImpGraphic )
+{
+ if( &rImpGraphic != this )
+ {
+ sal_Int64 aOldSizeBytes = mnSizeBytes;
+
+ maMetaFile = rImpGraphic.maMetaFile;
+ meType = rImpGraphic.meType;
+ mnSizeBytes = rImpGraphic.mnSizeBytes;
+
+ maSwapInfo = rImpGraphic.maSwapInfo;
+ mpContext = rImpGraphic.mpContext;
+ mbDummyContext = rImpGraphic.mbDummyContext;
+ maGraphicExternalLink = rImpGraphic.maGraphicExternalLink;
+
+ mpAnimation.reset();
+
+ if ( rImpGraphic.mpAnimation )
+ {
+ mpAnimation = std::make_unique<Animation>( *rImpGraphic.mpAnimation );
+ maBitmapEx = mpAnimation->GetBitmapEx();
+ }
+ else
+ {
+ maBitmapEx = rImpGraphic.maBitmapEx;
+ }
+
+ mbSwapOut = rImpGraphic.mbSwapOut;
+ mpSwapFile = rImpGraphic.mpSwapFile;
+ mbPrepared = rImpGraphic.mbPrepared;
+
+ mpGfxLink = rImpGraphic.mpGfxLink;
+
+ maVectorGraphicData = rImpGraphic.maVectorGraphicData;
+ maLastUsed = std::chrono::high_resolution_clock::now();
+
+ vcl::graphic::Manager::get().changeExisting(this, aOldSizeBytes);
+ }
+
+ return *this;
+}
+
+ImpGraphic& ImpGraphic::operator=(ImpGraphic&& rImpGraphic)
+{
+ sal_Int64 aOldSizeBytes = mnSizeBytes;
+
+ maMetaFile = std::move(rImpGraphic.maMetaFile);
+ meType = rImpGraphic.meType;
+ mnSizeBytes = rImpGraphic.mnSizeBytes;
+ maSwapInfo = std::move(rImpGraphic.maSwapInfo);
+ mpContext = std::move(rImpGraphic.mpContext);
+ mbDummyContext = rImpGraphic.mbDummyContext;
+ mpAnimation = std::move(rImpGraphic.mpAnimation);
+ maBitmapEx = std::move(rImpGraphic.maBitmapEx);
+ mbSwapOut = rImpGraphic.mbSwapOut;
+ mpSwapFile = std::move(rImpGraphic.mpSwapFile);
+ mpGfxLink = std::move(rImpGraphic.mpGfxLink);
+ maVectorGraphicData = std::move(rImpGraphic.maVectorGraphicData);
+ maGraphicExternalLink = rImpGraphic.maGraphicExternalLink;
+ mbPrepared = rImpGraphic.mbPrepared;
+
+ rImpGraphic.ImplClear();
+ rImpGraphic.mbDummyContext = false;
+ maLastUsed = std::chrono::high_resolution_clock::now();
+
+ vcl::graphic::Manager::get().changeExisting(this, aOldSizeBytes);
+
+ return *this;
+}
+
+bool ImpGraphic::operator==( const ImpGraphic& rImpGraphic ) const
+{
+ bool bRet = false;
+
+ if( this == &rImpGraphic )
+ bRet = true;
+ else if (mbPrepared && rImpGraphic.mbPrepared)
+ {
+ bRet = (*mpGfxLink == *rImpGraphic.mpGfxLink);
+ }
+ else if (isAvailable() && rImpGraphic.isAvailable())
+ {
+ switch( meType )
+ {
+ case GraphicType::NONE:
+ bRet = true;
+ break;
+
+ case GraphicType::GdiMetafile:
+ {
+ if( rImpGraphic.maMetaFile == maMetaFile )
+ bRet = true;
+ }
+ break;
+
+ case GraphicType::Bitmap:
+ {
+ if(maVectorGraphicData)
+ {
+ if(maVectorGraphicData == rImpGraphic.maVectorGraphicData)
+ {
+ // equal instances
+ bRet = true;
+ }
+ else if(rImpGraphic.maVectorGraphicData)
+ {
+ // equal content
+ bRet = (*maVectorGraphicData) == (*rImpGraphic.maVectorGraphicData);
+ }
+ }
+ else if( mpAnimation )
+ {
+ if( rImpGraphic.mpAnimation && ( *rImpGraphic.mpAnimation == *mpAnimation ) )
+ bRet = true;
+ }
+ else if( !rImpGraphic.mpAnimation && ( rImpGraphic.maBitmapEx == maBitmapEx ) )
+ {
+ bRet = true;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return bRet;
+}
+
+const std::shared_ptr<VectorGraphicData>& ImpGraphic::getVectorGraphicData() const
+{
+ ensureAvailable();
+
+ return maVectorGraphicData;
+}
+
+void ImpGraphic::createSwapInfo()
+{
+ if (isSwappedOut())
+ return;
+
+ maSwapInfo.maPrefMapMode = ImplGetPrefMapMode();
+ maSwapInfo.maPrefSize = ImplGetPrefSize();
+ maSwapInfo.mbIsAnimated = ImplIsAnimated();
+ maSwapInfo.mbIsEPS = ImplIsEPS();
+ maSwapInfo.mbIsTransparent = ImplIsTransparent();
+ maSwapInfo.mbIsAlpha = ImplIsAlpha();
+ maSwapInfo.mnAnimationLoopCount = ImplGetAnimationLoopCount();
+}
+
+void ImpGraphic::ImplClearGraphics()
+{
+ maBitmapEx.Clear();
+ maMetaFile.Clear();
+ mpAnimation.reset();
+ mpGfxLink.reset();
+ maVectorGraphicData.reset();
+}
+
+void ImpGraphic::ImplSetPrepared(bool bAnimated, const Size* pSizeHint)
+{
+ mbPrepared = true;
+ mbSwapOut = true;
+ meType = GraphicType::Bitmap;
+
+ SvMemoryStream aMemoryStream(const_cast<sal_uInt8*>(mpGfxLink->GetData()), mpGfxLink->GetDataSize(), StreamMode::READ | StreamMode::WRITE);
+
+ if (pSizeHint)
+ {
+ maSwapInfo.maPrefSize = *pSizeHint;
+ maSwapInfo.maPrefMapMode = MapMode(MapUnit::Map100thMM);
+ }
+
+ GraphicDescriptor aDescriptor(aMemoryStream, nullptr);
+ if (aDescriptor.Detect(true))
+ {
+ if (!pSizeHint)
+ {
+ // If we have logic size, work with that, as later pixel -> logic
+ // conversion will work with the output device DPI, not the graphic
+ // DPI.
+ Size aLogSize = aDescriptor.GetSize_100TH_MM();
+ if (aLogSize.getWidth() && aLogSize.getHeight())
+ {
+ maSwapInfo.maPrefSize = aLogSize;
+ maSwapInfo.maPrefMapMode = MapMode(MapUnit::Map100thMM);
+ }
+ else
+ {
+ maSwapInfo.maPrefSize = aDescriptor.GetSizePixel();
+ maSwapInfo.maPrefMapMode = MapMode(MapUnit::MapPixel);
+ }
+ }
+
+ maSwapInfo.maSizePixel = aDescriptor.GetSizePixel();
+ maSwapInfo.mbIsTransparent = aDescriptor.IsTransparent();
+ maSwapInfo.mbIsAlpha = aDescriptor.IsAlpha();
+ } else {
+ maSwapInfo.mbIsTransparent = false;
+ maSwapInfo.mbIsAlpha = false;
+ }
+
+ maSwapInfo.mnAnimationLoopCount = 0;
+ maSwapInfo.mbIsEPS = false;
+ maSwapInfo.mbIsAnimated = bAnimated;
+}
+
+void ImpGraphic::ImplClear()
+{
+ mpSwapFile.reset();
+ mbSwapOut = false;
+ mbPrepared = false;
+
+ // cleanup
+ ImplClearGraphics();
+ meType = GraphicType::NONE;
+ sal_Int64 nOldSize = mnSizeBytes;
+ mnSizeBytes = 0;
+ vcl::graphic::Manager::get().changeExisting(this, nOldSize);
+ maGraphicExternalLink.msURL.clear();
+}
+
+void ImpGraphic::ImplSetDefaultType()
+{
+ ImplClear();
+ meType = GraphicType::Default;
+}
+
+bool ImpGraphic::ImplIsSupportedGraphic() const
+{
+ return( meType != GraphicType::NONE );
+}
+
+bool ImpGraphic::ImplIsTransparent() const
+{
+ bool bRet(true);
+
+ if (mbSwapOut)
+ {
+ bRet = maSwapInfo.mbIsTransparent;
+ }
+ else if (meType == GraphicType::Bitmap && !maVectorGraphicData)
+ {
+ bRet = mpAnimation ? mpAnimation->IsTransparent() : maBitmapEx.IsTransparent();
+ }
+
+ return bRet;
+}
+
+bool ImpGraphic::ImplIsAlpha() const
+{
+ bool bRet(false);
+
+ if (mbSwapOut)
+ {
+ bRet = maSwapInfo.mbIsAlpha;
+ }
+ else if (maVectorGraphicData)
+ {
+ bRet = true;
+ }
+ else if (meType == GraphicType::Bitmap)
+ {
+ bRet = (nullptr == mpAnimation && maBitmapEx.IsAlpha());
+ }
+
+ return bRet;
+}
+
+bool ImpGraphic::ImplIsAnimated() const
+{
+ return mbSwapOut ? maSwapInfo.mbIsAnimated : mpAnimation != nullptr;
+}
+
+bool ImpGraphic::ImplIsEPS() const
+{
+ if (mbSwapOut)
+ return maSwapInfo.mbIsEPS;
+
+ return( ( meType == GraphicType::GdiMetafile ) &&
+ ( maMetaFile.GetActionSize() > 0 ) &&
+ ( maMetaFile.GetAction( 0 )->GetType() == MetaActionType::EPS ) );
+}
+
+bool ImpGraphic::isAvailable() const
+{
+ return !mbPrepared && !mbSwapOut;
+}
+
+bool ImpGraphic::makeAvailable()
+{
+ return ensureAvailable();
+}
+
+BitmapEx ImpGraphic::getVectorGraphicReplacement() const
+{
+ BitmapEx aRet = maVectorGraphicData->getReplacement();
+
+ if (maExPrefSize.getWidth() && maExPrefSize.getHeight())
+ {
+ aRet.SetPrefSize(maExPrefSize);
+ }
+
+ return aRet;
+}
+
+Bitmap ImpGraphic::ImplGetBitmap(const GraphicConversionParameters& rParameters) const
+{
+ Bitmap aRetBmp;
+
+ ensureAvailable();
+
+ if( meType == GraphicType::Bitmap )
+ {
+ if(maVectorGraphicData && maBitmapEx.IsEmpty())
+ {
+ // use maBitmapEx as local buffer for rendered svg
+ const_cast< ImpGraphic* >(this)->maBitmapEx = getVectorGraphicReplacement();
+ }
+
+ const BitmapEx& rRetBmpEx = ( mpAnimation ? mpAnimation->GetBitmapEx() : maBitmapEx );
+
+ aRetBmp = rRetBmpEx.GetBitmap( COL_WHITE );
+
+ if(rParameters.getSizePixel().Width() || rParameters.getSizePixel().Height())
+ aRetBmp.Scale(rParameters.getSizePixel());
+ }
+ else if( ( meType != GraphicType::Default ) && ImplIsSupportedGraphic() )
+ {
+ if(maBitmapEx.IsEmpty())
+ {
+ // calculate size
+ ScopedVclPtrInstance< VirtualDevice > aVDev;
+ Size aDrawSize(aVDev->LogicToPixel(maMetaFile.GetPrefSize(), maMetaFile.GetPrefMapMode()));
+
+ if(rParameters.getSizePixel().Width() && rParameters.getSizePixel().Height())
+ {
+ // apply given size if exists
+ aDrawSize = rParameters.getSizePixel();
+ }
+
+ if(aDrawSize.Width() && aDrawSize.Height() && !rParameters.getUnlimitedSize()
+ && (aDrawSize.Width() > GRAPHIC_MTFTOBMP_MAXEXT || aDrawSize.Height() > GRAPHIC_MTFTOBMP_MAXEXT))
+ {
+ // limit bitmap size to a maximum of GRAPHIC_MTFTOBMP_MAXEXT x GRAPHIC_MTFTOBMP_MAXEXT
+ double fWH(static_cast<double>(aDrawSize.Width()) / static_cast<double>(aDrawSize.Height()));
+
+ if(fWH <= 1.0)
+ {
+ aDrawSize.setWidth(basegfx::fround(GRAPHIC_MTFTOBMP_MAXEXT * fWH));
+ aDrawSize.setHeight(GRAPHIC_MTFTOBMP_MAXEXT);
+ }
+ else
+ {
+ aDrawSize.setWidth(GRAPHIC_MTFTOBMP_MAXEXT);
+ aDrawSize.setHeight(basegfx::fround(GRAPHIC_MTFTOBMP_MAXEXT / fWH));
+ }
+ }
+
+ // calculate pixel size. Normally, it's the same as aDrawSize, but may
+ // need to be extended when hairlines are on the right or bottom edge
+ Size aPixelSize(aDrawSize);
+
+ if(GraphicType::GdiMetafile == ImplGetType())
+ {
+ // get hairline and full bound rect
+ tools::Rectangle aHairlineRect;
+ const tools::Rectangle aRect(maMetaFile.GetBoundRect(*aVDev, &aHairlineRect));
+
+ if(!aRect.IsEmpty() && !aHairlineRect.IsEmpty())
+ {
+ // expand if needed to allow bottom and right hairlines to be added
+ if(aRect.Right() == aHairlineRect.Right())
+ {
+ aPixelSize.setWidth(aPixelSize.getWidth() + 1);
+ }
+
+ if(aRect.Bottom() == aHairlineRect.Bottom())
+ {
+ aPixelSize.setHeight(aPixelSize.getHeight() + 1);
+ }
+ }
+ }
+
+ if(aVDev->SetOutputSizePixel(aPixelSize))
+ {
+ if(rParameters.getAntiAliase())
+ {
+ aVDev->SetAntialiasing(aVDev->GetAntialiasing() | AntialiasingFlags::EnableB2dDraw);
+ }
+
+ if(rParameters.getSnapHorVerLines())
+ {
+ aVDev->SetAntialiasing(aVDev->GetAntialiasing() | AntialiasingFlags::PixelSnapHairline);
+ }
+
+ ImplDraw( aVDev.get(), Point(), aDrawSize );
+
+ // use maBitmapEx as local buffer for rendered metafile
+ const_cast< ImpGraphic* >(this)->maBitmapEx = aVDev->GetBitmapEx( Point(), aVDev->GetOutputSizePixel() );
+ }
+ }
+
+ aRetBmp = maBitmapEx.GetBitmap();
+ }
+
+ if( !!aRetBmp )
+ {
+ aRetBmp.SetPrefMapMode( ImplGetPrefMapMode() );
+ aRetBmp.SetPrefSize( ImplGetPrefSize() );
+ }
+
+ return aRetBmp;
+}
+
+BitmapEx ImpGraphic::ImplGetBitmapEx(const GraphicConversionParameters& rParameters) const
+{
+ BitmapEx aRetBmpEx;
+
+ ensureAvailable();
+
+ if( meType == GraphicType::Bitmap )
+ {
+ if(maVectorGraphicData && maBitmapEx.IsEmpty())
+ {
+ // use maBitmapEx as local buffer for rendered svg
+ const_cast< ImpGraphic* >(this)->maBitmapEx = getVectorGraphicReplacement();
+ }
+
+ aRetBmpEx = ( mpAnimation ? mpAnimation->GetBitmapEx() : maBitmapEx );
+
+ if(rParameters.getSizePixel().Width() || rParameters.getSizePixel().Height())
+ {
+ aRetBmpEx.Scale(
+ rParameters.getSizePixel(),
+ BmpScaleFlag::Fast);
+ }
+ }
+ else if( ( meType != GraphicType::Default ) && ImplIsSupportedGraphic() )
+ {
+ if(maBitmapEx.IsEmpty())
+ {
+ const ImpGraphic aMonoMask( maMetaFile.GetMonochromeMtf( COL_BLACK ) );
+
+ // use maBitmapEx as local buffer for rendered metafile
+ const_cast< ImpGraphic* >(this)->maBitmapEx = BitmapEx(ImplGetBitmap(rParameters), aMonoMask.ImplGetBitmap(rParameters));
+ }
+
+ aRetBmpEx = maBitmapEx;
+ }
+
+ return aRetBmpEx;
+}
+
+Animation ImpGraphic::ImplGetAnimation() const
+{
+ Animation aAnimation;
+
+ ensureAvailable();
+ if( mpAnimation )
+ aAnimation = *mpAnimation;
+
+ return aAnimation;
+}
+
+const BitmapEx& ImpGraphic::ImplGetBitmapExRef() const
+{
+ ensureAvailable();
+ return maBitmapEx;
+}
+
+const GDIMetaFile& ImpGraphic::ImplGetGDIMetaFile() const
+{
+ ensureAvailable();
+ if (!maMetaFile.GetActionSize()
+ && maVectorGraphicData
+ && (VectorGraphicDataType::Emf == maVectorGraphicData->getVectorGraphicDataType()
+ || VectorGraphicDataType::Wmf == maVectorGraphicData->getVectorGraphicDataType()))
+ {
+ // If we have a Emf/Wmf VectorGraphic object, we
+ // need a way to get the Metafile data out of the primitive
+ // representation. Use a strict virtual hook (MetafileAccessor)
+ // to access the MetafilePrimitive2D directly. Also see comments in
+ // XEmfParser about this.
+ const std::deque< css::uno::Reference< css::graphic::XPrimitive2D > > aSequence(maVectorGraphicData->getPrimitive2DSequence());
+
+ if (1 == aSequence.size())
+ {
+ // try to cast to MetafileAccessor implementation
+ const css::uno::Reference< css::graphic::XPrimitive2D > xReference(aSequence[0]);
+ const MetafileAccessor* pMetafileAccessor = dynamic_cast< const MetafileAccessor* >(xReference.get());
+
+ if (pMetafileAccessor)
+ {
+ // it is a MetafileAccessor implementation, get Metafile
+ pMetafileAccessor->accessMetafile(const_cast< ImpGraphic* >(this)->maMetaFile);
+ }
+ }
+ }
+
+ if (GraphicType::Bitmap == meType && !maMetaFile.GetActionSize())
+ {
+ // #i119735#
+ // Use the local maMetaFile as container for a metafile-representation
+ // of the bitmap graphic. This will be done only once, thus be buffered.
+ // I checked all usages of maMetaFile, it is only used when type is not
+ // GraphicType::Bitmap. In operator= it will get copied, thus buffering will
+ // survive copying (change this if not wanted)
+ ImpGraphic* pThat = const_cast< ImpGraphic* >(this);
+
+ if(maVectorGraphicData && !maBitmapEx)
+ {
+ // use maBitmapEx as local buffer for rendered svg
+ pThat->maBitmapEx = getVectorGraphicReplacement();
+ }
+
+ // #123983# directly create a metafile with the same PrefSize and PrefMapMode
+ // the bitmap has, this will be an always correct metafile
+ if(maBitmapEx.IsTransparent())
+ {
+ pThat->maMetaFile.AddAction(new MetaBmpExScaleAction(Point(), maBitmapEx.GetPrefSize(), maBitmapEx));
+ }
+ else
+ {
+ pThat->maMetaFile.AddAction(new MetaBmpScaleAction(Point(), maBitmapEx.GetPrefSize(), maBitmapEx.GetBitmap()));
+ }
+
+ pThat->maMetaFile.Stop();
+ pThat->maMetaFile.WindStart();
+ pThat->maMetaFile.SetPrefSize(maBitmapEx.GetPrefSize());
+ pThat->maMetaFile.SetPrefMapMode(maBitmapEx.GetPrefMapMode());
+ }
+
+ return maMetaFile;
+}
+
+Size ImpGraphic::ImplGetSizePixel() const
+{
+ Size aSize;
+
+ if (isSwappedOut())
+ aSize = maSwapInfo.maSizePixel;
+ else
+ aSize = ImplGetBitmapEx(GraphicConversionParameters()).GetSizePixel();
+
+ return aSize;
+}
+
+Size ImpGraphic::ImplGetPrefSize() const
+{
+ Size aSize;
+
+ if (isSwappedOut())
+ {
+ aSize = maSwapInfo.maPrefSize;
+ }
+ else
+ {
+ switch( meType )
+ {
+ case GraphicType::NONE:
+ case GraphicType::Default:
+ break;
+
+ case GraphicType::Bitmap:
+ {
+ if(maVectorGraphicData && maBitmapEx.IsEmpty())
+ {
+ if (!maExPrefSize.getWidth() || !maExPrefSize.getHeight())
+ {
+ // svg not yet buffered in maBitmapEx, return size derived from range
+ const basegfx::B2DRange& rRange = maVectorGraphicData->getRange();
+
+ aSize = Size(basegfx::fround(rRange.getWidth()), basegfx::fround(rRange.getHeight()));
+ }
+ else
+ {
+ aSize = maExPrefSize;
+ }
+ }
+ else
+ {
+ aSize = maBitmapEx.GetPrefSize();
+
+ if( !aSize.Width() || !aSize.Height() )
+ {
+ aSize = maBitmapEx.GetSizePixel();
+ }
+ }
+ }
+ break;
+
+ default:
+ {
+ if( ImplIsSupportedGraphic() )
+ aSize = maMetaFile.GetPrefSize();
+ }
+ break;
+ }
+ }
+
+ return aSize;
+}
+
+void ImpGraphic::ImplSetPrefSize( const Size& rPrefSize )
+{
+ ensureAvailable();
+
+ switch( meType )
+ {
+ case GraphicType::NONE:
+ case GraphicType::Default:
+ break;
+
+ case GraphicType::Bitmap:
+ {
+ // used when importing a writer FlyFrame with SVG as graphic, added conversion
+ // to allow setting the PrefSize at the BitmapEx to hold it
+ if(maVectorGraphicData && maBitmapEx.IsEmpty())
+ {
+ maExPrefSize = rPrefSize;
+ }
+
+ // #108077# Push through pref size to animation object,
+ // will be lost on copy otherwise
+ if( ImplIsAnimated() )
+ {
+ const_cast< BitmapEx& >(mpAnimation->GetBitmapEx()).SetPrefSize( rPrefSize );
+ }
+
+ if (!maExPrefSize.getWidth() || !maExPrefSize.getHeight())
+ {
+ maBitmapEx.SetPrefSize( rPrefSize );
+ }
+ }
+ break;
+
+ default:
+ {
+ if( ImplIsSupportedGraphic() )
+ maMetaFile.SetPrefSize( rPrefSize );
+ }
+ break;
+ }
+}
+
+MapMode ImpGraphic::ImplGetPrefMapMode() const
+{
+ MapMode aMapMode;
+
+ if (isSwappedOut())
+ {
+ aMapMode = maSwapInfo.maPrefMapMode;
+ }
+ else
+ {
+ switch( meType )
+ {
+ case GraphicType::NONE:
+ case GraphicType::Default:
+ break;
+
+ case GraphicType::Bitmap:
+ {
+ if(maVectorGraphicData && maBitmapEx.IsEmpty())
+ {
+ // svg not yet buffered in maBitmapEx, return default PrefMapMode
+ aMapMode = MapMode(MapUnit::Map100thMM);
+ }
+ else
+ {
+ const Size aSize( maBitmapEx.GetPrefSize() );
+
+ if ( aSize.Width() && aSize.Height() )
+ aMapMode = maBitmapEx.GetPrefMapMode();
+ }
+ }
+ break;
+
+ default:
+ {
+ if( ImplIsSupportedGraphic() )
+ return maMetaFile.GetPrefMapMode();
+ }
+ break;
+ }
+ }
+
+ return aMapMode;
+}
+
+void ImpGraphic::ImplSetPrefMapMode( const MapMode& rPrefMapMode )
+{
+ ensureAvailable();
+
+ switch( meType )
+ {
+ case GraphicType::NONE:
+ case GraphicType::Default:
+ break;
+
+ case GraphicType::Bitmap:
+ {
+ if(maVectorGraphicData)
+ {
+ // ignore for Vector Graphic Data. If this is really used (except the grfcache)
+ // it can be extended by using maBitmapEx as buffer for getVectorGraphicReplacement()
+ }
+ else
+ {
+ // #108077# Push through pref mapmode to animation object,
+ // will be lost on copy otherwise
+ if( ImplIsAnimated() )
+ {
+ const_cast< BitmapEx& >(mpAnimation->GetBitmapEx()).SetPrefMapMode( rPrefMapMode );
+ }
+
+ maBitmapEx.SetPrefMapMode( rPrefMapMode );
+ }
+ }
+ break;
+
+ default:
+ {
+ if( ImplIsSupportedGraphic() )
+ maMetaFile.SetPrefMapMode( rPrefMapMode );
+ }
+ break;
+ }
+}
+
+sal_uLong ImpGraphic::ImplGetSizeBytes() const
+{
+ if( mnSizeBytes )
+ return mnSizeBytes;
+
+ if (mbPrepared)
+ ensureAvailable();
+
+ if( meType == GraphicType::Bitmap )
+ {
+ if(maVectorGraphicData)
+ {
+ std::pair<VectorGraphicData::State, size_t> tmp(maVectorGraphicData->getSizeBytes());
+ if (VectorGraphicData::State::UNPARSED == tmp.first)
+ {
+ return tmp.second; // don't cache it until Vector Graphic Data is parsed
+ }
+ mnSizeBytes = tmp.second;
+ }
+ else
+ {
+ mnSizeBytes = mpAnimation ? mpAnimation->GetSizeBytes() : maBitmapEx.GetSizeBytes();
+ }
+ }
+ else if( meType == GraphicType::GdiMetafile )
+ {
+ mnSizeBytes = maMetaFile.GetSizeBytes();
+ }
+
+ return mnSizeBytes;
+}
+
+void ImpGraphic::ImplDraw( OutputDevice* pOutDev, const Point& rDestPt ) const
+{
+ ensureAvailable();
+ if( !ImplIsSupportedGraphic() || isSwappedOut() )
+ return;
+
+ switch( meType )
+ {
+ case GraphicType::Default:
+ break;
+
+ case GraphicType::Bitmap:
+ {
+ if(maVectorGraphicData && !maBitmapEx)
+ {
+ // use maEx as local buffer for rendered svg
+ const_cast< ImpGraphic* >(this)->maBitmapEx = getVectorGraphicReplacement();
+ }
+
+ if ( mpAnimation )
+ {
+ mpAnimation->Draw( pOutDev, rDestPt );
+ }
+ else
+ {
+ maBitmapEx.Draw( pOutDev, rDestPt );
+ }
+ }
+ break;
+
+ default:
+ ImplDraw( pOutDev, rDestPt, maMetaFile.GetPrefSize() );
+ break;
+ }
+}
+
+void ImpGraphic::ImplDraw( OutputDevice* pOutDev,
+ const Point& rDestPt, const Size& rDestSize ) const
+{
+ ensureAvailable();
+ if( !ImplIsSupportedGraphic() || isSwappedOut() )
+ return;
+
+ switch( meType )
+ {
+ case GraphicType::Default:
+ break;
+
+ case GraphicType::Bitmap:
+ {
+ if(maVectorGraphicData && maBitmapEx.IsEmpty())
+ {
+ // use maEx as local buffer for rendered svg
+ const_cast< ImpGraphic* >(this)->maBitmapEx = getVectorGraphicReplacement();
+ }
+
+ if( mpAnimation )
+ {
+ mpAnimation->Draw( pOutDev, rDestPt, rDestSize );
+ }
+ else
+ {
+ maBitmapEx.Draw( pOutDev, rDestPt, rDestSize );
+ }
+ }
+ break;
+
+ default:
+ {
+ const_cast<ImpGraphic*>(this)->maMetaFile.WindStart();
+ const_cast<ImpGraphic*>(this)->maMetaFile.Play( pOutDev, rDestPt, rDestSize );
+ const_cast<ImpGraphic*>(this)->maMetaFile.WindStart();
+ }
+ break;
+ }
+}
+
+void ImpGraphic::ImplStartAnimation( OutputDevice* pOutDev, const Point& rDestPt,
+ const Size& rDestSize, long nExtraData,
+ OutputDevice* pFirstFrameOutDev )
+{
+ ensureAvailable();
+
+ if( ImplIsSupportedGraphic() && !isSwappedOut() && mpAnimation )
+ mpAnimation->Start( pOutDev, rDestPt, rDestSize, nExtraData, pFirstFrameOutDev );
+}
+
+void ImpGraphic::ImplStopAnimation( OutputDevice* pOutDev, long nExtraData )
+{
+ ensureAvailable();
+
+ if( ImplIsSupportedGraphic() && !isSwappedOut() && mpAnimation )
+ mpAnimation->Stop( pOutDev, nExtraData );
+}
+
+void ImpGraphic::ImplSetAnimationNotifyHdl( const Link<Animation*,void>& rLink )
+{
+ ensureAvailable();
+
+ if( mpAnimation )
+ mpAnimation->SetNotifyHdl( rLink );
+}
+
+Link<Animation*,void> ImpGraphic::ImplGetAnimationNotifyHdl() const
+{
+ Link<Animation*,void> aLink;
+
+ ensureAvailable();
+
+ if( mpAnimation )
+ aLink = mpAnimation->GetNotifyHdl();
+
+ return aLink;
+}
+
+sal_uInt32 ImpGraphic::ImplGetAnimationLoopCount() const
+{
+ if (mbSwapOut)
+ return maSwapInfo.mnAnimationLoopCount;
+
+ return mpAnimation ? mpAnimation->GetLoopCount() : 0;
+}
+
+void ImpGraphic::ImplSetContext( const std::shared_ptr<GraphicReader>& pReader )
+{
+ mpContext = pReader;
+ mbDummyContext = false;
+}
+
+bool ImpGraphic::ImplReadEmbedded( SvStream& rIStm )
+{
+ ensureAvailable();
+
+ MapMode aMapMode;
+ Size aSize;
+ sal_uInt32 nId;
+ sal_Int32 nType;
+ const SvStreamEndian nOldFormat = rIStm.GetEndian();
+ bool bRet = false;
+
+ rIStm.SetEndian( SvStreamEndian::LITTLE );
+ rIStm.ReadUInt32( nId );
+
+ // check version
+ if( GRAPHIC_FORMAT_50 == nId )
+ {
+ // read new style header
+ VersionCompat aCompat( rIStm, StreamMode::READ );
+
+ rIStm.ReadInt32( nType );
+ sal_Int32 nLen;
+ rIStm.ReadInt32( nLen );
+ TypeSerializer aSerializer(rIStm);
+ aSerializer.readSize(aSize);
+ ReadMapMode( rIStm, aMapMode );
+ }
+ else
+ {
+ // read old style header
+ sal_Int32 nWidth, nHeight;
+ sal_Int32 nMapMode, nScaleNumX, nScaleDenomX;
+ sal_Int32 nScaleNumY, nScaleDenomY, nOffsX, nOffsY;
+
+ rIStm.SeekRel( -4 );
+
+ sal_Int32 nLen;
+ rIStm.ReadInt32( nType ).ReadInt32( nLen ).ReadInt32( nWidth ).ReadInt32( nHeight );
+ rIStm.ReadInt32( nMapMode ).ReadInt32( nScaleNumX ).ReadInt32( nScaleDenomX ).ReadInt32( nScaleNumY );
+ rIStm.ReadInt32( nScaleDenomY ).ReadInt32( nOffsX ).ReadInt32( nOffsY );
+
+ // swapped
+ if( nType > 100 )
+ {
+ nType = OSL_SWAPDWORD( nType );
+ nWidth = OSL_SWAPDWORD( nWidth );
+ nHeight = OSL_SWAPDWORD( nHeight );
+ nMapMode = OSL_SWAPDWORD( nMapMode );
+ nScaleNumX = OSL_SWAPDWORD( nScaleNumX );
+ nScaleDenomX = OSL_SWAPDWORD( nScaleDenomX );
+ nScaleNumY = OSL_SWAPDWORD( nScaleNumY );
+ nScaleDenomY = OSL_SWAPDWORD( nScaleDenomY );
+ nOffsX = OSL_SWAPDWORD( nOffsX );
+ nOffsY = OSL_SWAPDWORD( nOffsY );
+ }
+
+ aSize = Size( nWidth, nHeight );
+ aMapMode = MapMode( static_cast<MapUnit>(nMapMode), Point( nOffsX, nOffsY ),
+ Fraction( nScaleNumX, nScaleDenomX ),
+ Fraction( nScaleNumY, nScaleDenomY ) );
+ }
+
+ meType = static_cast<GraphicType>(nType);
+
+ if( meType != GraphicType::NONE )
+ {
+ if( meType == GraphicType::Bitmap )
+ {
+ if(maVectorGraphicData && maBitmapEx.IsEmpty())
+ {
+ // use maBitmapEx as local buffer for rendered svg
+ maBitmapEx = getVectorGraphicReplacement();
+ }
+
+ maBitmapEx.SetSizePixel(aSize);
+
+ if( aMapMode != MapMode() )
+ {
+ maBitmapEx.SetPrefMapMode( aMapMode );
+ maBitmapEx.SetPrefSize( aSize );
+ }
+ }
+ else
+ {
+ maMetaFile.SetPrefMapMode( aMapMode );
+ maMetaFile.SetPrefSize( aSize );
+ }
+
+ if( meType == GraphicType::Bitmap || meType == GraphicType::GdiMetafile )
+ {
+ ReadImpGraphic( rIStm, *this );
+ bRet = rIStm.GetError() == ERRCODE_NONE;
+ }
+ else if( sal::static_int_cast<sal_uLong>(meType) >= SYS_WINMETAFILE
+ && sal::static_int_cast<sal_uLong>(meType) <= SYS_MACMETAFILE )
+ {
+ Graphic aSysGraphic;
+ ConvertDataFormat nCvtType;
+
+ switch( sal::static_int_cast<sal_uLong>(meType) )
+ {
+ case SYS_WINMETAFILE:
+ case SYS_WNTMETAFILE: nCvtType = ConvertDataFormat::WMF; break;
+ case SYS_OS2METAFILE: nCvtType = ConvertDataFormat::MET; break;
+ case SYS_MACMETAFILE: nCvtType = ConvertDataFormat::PCT; break;
+
+ default:
+ nCvtType = ConvertDataFormat::Unknown;
+ break;
+ }
+
+ if( nType && GraphicConverter::Import( rIStm, aSysGraphic, nCvtType ) == ERRCODE_NONE )
+ {
+ *this = ImpGraphic( aSysGraphic.GetGDIMetaFile() );
+ bRet = rIStm.GetError() == ERRCODE_NONE;
+ }
+ else
+ meType = GraphicType::Default;
+ }
+
+ if( bRet )
+ {
+ ImplSetPrefMapMode( aMapMode );
+ ImplSetPrefSize( aSize );
+ }
+ }
+ else
+ bRet = true;
+
+ rIStm.SetEndian( nOldFormat );
+
+ return bRet;
+}
+
+bool ImpGraphic::ImplWriteEmbedded( SvStream& rOStm )
+{
+ ensureAvailable();
+
+ if( ( meType == GraphicType::NONE ) || ( meType == GraphicType::Default ) || isSwappedOut() )
+ return false;
+
+ const MapMode aMapMode( ImplGetPrefMapMode() );
+ const Size aSize( ImplGetPrefSize() );
+ const SvStreamEndian nOldFormat = rOStm.GetEndian();
+ sal_uLong nDataFieldPos;
+
+ rOStm.SetEndian( SvStreamEndian::LITTLE );
+
+ // write correct version ( old style/new style header )
+ if( rOStm.GetVersion() >= SOFFICE_FILEFORMAT_50 )
+ {
+ // write ID for new format (5.0)
+ rOStm.WriteUInt32( GRAPHIC_FORMAT_50 );
+
+ // write new style header
+ VersionCompat aCompat( rOStm, StreamMode::WRITE, 1 );
+
+ rOStm.WriteInt32( static_cast<sal_Int32>(meType) );
+
+ // data size is updated later
+ nDataFieldPos = rOStm.Tell();
+ rOStm.WriteInt32( 0 );
+
+ TypeSerializer aSerializer(rOStm);
+ aSerializer.writeSize(aSize);
+
+ WriteMapMode( rOStm, aMapMode );
+ }
+ else
+ {
+ // write old style (<=4.0) header
+ rOStm.WriteInt32( static_cast<sal_Int32>(meType) );
+
+ // data size is updated later
+ nDataFieldPos = rOStm.Tell();
+ rOStm.WriteInt32( 0 );
+ rOStm.WriteInt32( aSize.Width() );
+ rOStm.WriteInt32( aSize.Height() );
+ rOStm.WriteInt32( static_cast<sal_uInt16>(aMapMode.GetMapUnit()) );
+ rOStm.WriteInt32( aMapMode.GetScaleX().GetNumerator() );
+ rOStm.WriteInt32( aMapMode.GetScaleX().GetDenominator() );
+ rOStm.WriteInt32( aMapMode.GetScaleY().GetNumerator() );
+ rOStm.WriteInt32( aMapMode.GetScaleY().GetDenominator() );
+ rOStm.WriteInt32( aMapMode.GetOrigin().X() );
+ rOStm.WriteInt32( aMapMode.GetOrigin().Y() );
+ }
+
+ bool bRet = false;
+ // write data block
+ if( !rOStm.GetError() )
+ {
+ const sal_uLong nDataStart = rOStm.Tell();
+
+ if( ImplIsSupportedGraphic() )
+ WriteImpGraphic( rOStm, *this );
+
+ if( !rOStm.GetError() )
+ {
+ const sal_uLong nStmPos2 = rOStm.Tell();
+ rOStm.Seek( nDataFieldPos );
+ rOStm.WriteInt32( nStmPos2 - nDataStart );
+ rOStm.Seek( nStmPos2 );
+ bRet = true;
+ }
+ }
+
+ rOStm.SetEndian( nOldFormat );
+
+ return bRet;
+}
+
+bool ImpGraphic::swapOut()
+{
+ if (isSwappedOut())
+ return false;
+
+ // Create a temp filename for the swap file
+ utl::TempFile aTempFile;
+ const INetURLObject aTempFileURL(aTempFile.GetURL());
+
+ // Create a swap file
+ std::shared_ptr<ImpSwapFile> pSwapFile(new ImpSwapFile(aTempFileURL, getOriginURL()), o3tl::default_delete<ImpSwapFile>());
+
+ bool bResult = false;
+
+ // Open a stream to write the swap file to
+ {
+ std::unique_ptr<SvStream> xOutputStream = pSwapFile->openOutputStream();
+
+ if (!xOutputStream)
+ return false;
+
+ // Write to stream
+ xOutputStream->SetVersion(SOFFICE_FILEFORMAT_50);
+ xOutputStream->SetCompressMode(SvStreamCompressFlags::NATIVE);
+ xOutputStream->SetBufferSize(GRAPHIC_STREAMBUFSIZE);
+
+ if (!xOutputStream->GetError() && ImplWriteEmbedded(*xOutputStream))
+ {
+ xOutputStream->Flush();
+ bResult = !xOutputStream->GetError();
+ }
+ }
+
+ // Check if writing was successful
+ if (bResult)
+ {
+ // We have swapped out, so can clean memory and prepare swap info
+ createSwapInfo();
+ ImplClearGraphics();
+
+ mpSwapFile = std::move(pSwapFile);
+ mbSwapOut = true;
+
+ // Signal to manager that we have swapped out
+ vcl::graphic::Manager::get().swappedOut(this);
+ }
+
+ return bResult;
+}
+
+bool ImpGraphic::ensureAvailable() const
+{
+ auto pThis = const_cast<ImpGraphic*>(this);
+
+ if (isSwappedOut())
+ return pThis->swapIn();
+
+ pThis->maLastUsed = std::chrono::high_resolution_clock::now();
+ return true;
+}
+
+bool ImpGraphic::loadPrepared()
+{
+ Graphic aGraphic;
+ if (!mpGfxLink->LoadNative(aGraphic))
+ return false;
+
+ GraphicExternalLink aLink = maGraphicExternalLink;
+
+ Size aPrefSize = maSwapInfo.maPrefSize;
+ MapMode aPrefMapMode = maSwapInfo.maPrefMapMode;
+ *this = *aGraphic.ImplGetImpGraphic();
+ if (aPrefSize.getWidth() && aPrefSize.getHeight() && aPrefMapMode == ImplGetPrefMapMode())
+ {
+ // Use custom preferred size if it was set when the graphic was still unloaded.
+ // Only set the size in case the unloaded and loaded unit matches.
+ ImplSetPrefSize(aPrefSize);
+ }
+
+ maGraphicExternalLink = aLink;
+
+ return true;
+}
+
+bool ImpGraphic::swapIn()
+{
+ bool bRet = false;
+
+ if (!isSwappedOut())
+ return bRet;
+
+ if (mbPrepared)
+ {
+ bRet = loadPrepared();
+ }
+ else
+ {
+ OUString aSwapURL;
+
+ if( mpSwapFile )
+ aSwapURL = mpSwapFile->getSwapURL().GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ if( !aSwapURL.isEmpty() )
+ {
+ std::unique_ptr<SvStream> xIStm;
+ try
+ {
+ xIStm = ::utl::UcbStreamHelper::CreateStream( aSwapURL, StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE );
+ }
+ catch( const css::uno::Exception& )
+ {
+ }
+
+ if( xIStm )
+ {
+ xIStm->SetVersion( SOFFICE_FILEFORMAT_50 );
+ xIStm->SetCompressMode( SvStreamCompressFlags::NATIVE );
+
+ bRet = swapInFromStream(xIStm.get());
+ xIStm.reset();
+ if (mpSwapFile)
+ setOriginURL(mpSwapFile->getOriginURL());
+ mpSwapFile.reset();
+ }
+ }
+ }
+
+ if (bRet)
+ vcl::graphic::Manager::get().swappedIn(this);
+
+ return bRet;
+}
+
+bool ImpGraphic::swapInFromStream(SvStream* xIStm)
+{
+ bool bRet = false;
+
+ if( !xIStm )
+ return false;
+
+ xIStm->SetBufferSize( GRAPHIC_STREAMBUFSIZE );
+
+ if( xIStm->GetError() )
+ return false;
+
+ //keep the swap file alive, because its quite possibly the backing storage
+ //for xIStm
+ std::shared_ptr<ImpSwapFile> xSwapFile(std::move(mpSwapFile));
+ assert(!mpSwapFile);
+
+ std::shared_ptr<GraphicReader> xContext(std::move(mpContext));
+ assert(!mpContext);
+
+ bool bDummyContext = mbDummyContext;
+ mbDummyContext = false;
+
+ bRet = ImplReadEmbedded( *xIStm );
+
+ //restore ownership of the swap file and context
+ mpSwapFile = std::move(xSwapFile);
+ mpContext = std::move(xContext);
+ mbDummyContext = bDummyContext;
+
+ if (!bRet)
+ {
+ //throw away swapfile, etc.
+ ImplClear();
+ }
+
+ mbSwapOut = false;
+
+ return bRet;
+}
+
+void ImpGraphic::ImplSetLink(const std::shared_ptr<GfxLink>& rGfxLink)
+{
+ ensureAvailable();
+
+ mpGfxLink = rGfxLink;
+}
+
+std::shared_ptr<GfxLink> ImpGraphic::ImplGetSharedGfxLink() const
+{
+ return mpGfxLink;
+}
+
+GfxLink ImpGraphic::ImplGetLink()
+{
+ ensureAvailable();
+
+ return( mpGfxLink ? *mpGfxLink : GfxLink() );
+}
+
+bool ImpGraphic::ImplIsLink() const
+{
+ return ( bool(mpGfxLink) );
+}
+
+BitmapChecksum ImpGraphic::ImplGetChecksum() const
+{
+ if (mnChecksum != 0)
+ return mnChecksum;
+
+ BitmapChecksum nRet = 0;
+
+ ensureAvailable();
+
+ if( ImplIsSupportedGraphic() && !isSwappedOut() )
+ {
+ switch( meType )
+ {
+ case GraphicType::Default:
+ break;
+
+ case GraphicType::Bitmap:
+ {
+ if(maVectorGraphicData)
+ nRet = maVectorGraphicData->GetChecksum();
+ else if( mpAnimation )
+ nRet = mpAnimation->GetChecksum();
+ else
+ nRet = maBitmapEx.GetChecksum();
+ }
+ break;
+
+ default:
+ nRet = maMetaFile.GetChecksum();
+ break;
+ }
+ }
+
+ mnChecksum = nRet;
+ return nRet;
+}
+
+bool ImpGraphic::ImplExportNative( SvStream& rOStm ) const
+{
+ ensureAvailable();
+
+ if( rOStm.GetError() )
+ return false;
+
+ bool bResult = false;
+
+ if( !isSwappedOut() )
+ {
+ if( mpGfxLink && mpGfxLink->IsNative() )
+ bResult = mpGfxLink->ExportNative( rOStm );
+ else
+ {
+ WriteImpGraphic( rOStm, *this );
+ bResult = ( rOStm.GetError() == ERRCODE_NONE );
+ }
+ }
+ else
+ rOStm.SetError( SVSTREAM_GENERALERROR );
+
+ return bResult;
+}
+
+sal_Int32 ImpGraphic::getPageNumber() const
+{
+ if (maVectorGraphicData)
+ return maVectorGraphicData->getPageIndex();
+ return -1;
+}
+
+void ReadImpGraphic( SvStream& rIStm, ImpGraphic& rImpGraphic )
+{
+ if (rIStm.GetError())
+ return;
+
+ const sal_uLong nStmPos1 = rIStm.Tell();
+ sal_uInt32 nTmp;
+
+ rImpGraphic.ImplClear();
+
+ // read Id
+ rIStm.ReadUInt32( nTmp );
+
+ // if there is no more data, avoid further expensive
+ // reading which will create VDevs and other stuff, just to
+ // read nothing. CAUTION: Eof is only true AFTER reading another
+ // byte, a speciality of SvMemoryStream (!)
+ if (!rIStm.good())
+ return;
+
+ if (NATIVE_FORMAT_50 == nTmp)
+ {
+ Graphic aGraphic;
+ GfxLink aLink;
+
+ // read compat info, destructor writes stuff into the header
+ {
+ VersionCompat aCompat( rIStm, StreamMode::READ );
+ }
+
+ TypeSerializer aSerializer(rIStm);
+ aSerializer.readGfxLink(aLink);
+
+ // set dummy link to avoid creation of additional link after filtering;
+ // we set a default link to avoid unnecessary swapping of native data
+ aGraphic.SetGfxLink(std::make_shared<GfxLink>());
+
+ if( !rIStm.GetError() && aLink.LoadNative( aGraphic ) )
+ {
+ // set link only, if no other link was set
+ const bool bSetLink = !rImpGraphic.mpGfxLink;
+
+ // assign graphic
+ rImpGraphic = *aGraphic.ImplGetImpGraphic();
+
+ if( aLink.IsPrefMapModeValid() )
+ rImpGraphic.ImplSetPrefMapMode( aLink.GetPrefMapMode() );
+
+ if( aLink.IsPrefSizeValid() )
+ rImpGraphic.ImplSetPrefSize( aLink.GetPrefSize() );
+
+ if( bSetLink )
+ rImpGraphic.ImplSetLink(std::make_shared<GfxLink>(aLink));
+ }
+ else
+ {
+ rIStm.Seek( nStmPos1 );
+ rIStm.SetError( ERRCODE_IO_WRONGFORMAT );
+ }
+ return;
+ }
+
+ BitmapEx aBmpEx;
+ const SvStreamEndian nOldFormat = rIStm.GetEndian();
+
+ rIStm.SeekRel( -4 );
+ rIStm.SetEndian( SvStreamEndian::LITTLE );
+ ReadDIBBitmapEx(aBmpEx, rIStm);
+
+ if( !rIStm.GetError() )
+ {
+ sal_uInt32 nMagic1(0), nMagic2(0);
+ sal_uLong nActPos = rIStm.Tell();
+
+ rIStm.ReadUInt32( nMagic1 ).ReadUInt32( nMagic2 );
+ rIStm.Seek( nActPos );
+
+ rImpGraphic = ImpGraphic( aBmpEx );
+
+ if( !rIStm.GetError() && ( 0x5344414e == nMagic1 ) && ( 0x494d4931 == nMagic2 ) )
+ {
+ rImpGraphic.mpAnimation = std::make_unique<Animation>();
+ ReadAnimation( rIStm, *rImpGraphic.mpAnimation );
+
+ // #108077# manually set loaded BmpEx to Animation
+ // (which skips loading its BmpEx if already done)
+ rImpGraphic.mpAnimation->SetBitmapEx(aBmpEx);
+ }
+ else
+ rIStm.ResetError();
+ }
+ else
+ {
+ GDIMetaFile aMtf;
+
+ rIStm.Seek( nStmPos1 );
+ rIStm.ResetError();
+ ReadGDIMetaFile( rIStm, aMtf );
+
+ if( !rIStm.GetError() )
+ {
+ rImpGraphic = aMtf;
+ }
+ else
+ {
+ ErrCode nOrigError = rIStm.GetErrorCode();
+ // try to stream in Svg defining data (length, byte array and evtl. path)
+ // See below (operator<<) for more information
+ sal_uInt32 nMagic;
+ rIStm.Seek(nStmPos1);
+ rIStm.ResetError();
+ rIStm.ReadUInt32( nMagic );
+
+ if (constSvgMagic == nMagic || constWmfMagic == nMagic || constEmfMagic == nMagic || constPdfMagic == nMagic)
+ {
+ sal_uInt32 nVectorGraphicDataArrayLength(0);
+ rIStm.ReadUInt32(nVectorGraphicDataArrayLength);
+
+ if (nVectorGraphicDataArrayLength)
+ {
+ VectorGraphicDataArray aNewData(nVectorGraphicDataArrayLength);
+
+ rIStm.ReadBytes(aNewData.getArray(), nVectorGraphicDataArrayLength);
+ OUString aPath = rIStm.ReadUniOrByteString(rIStm.GetStreamCharSet());
+
+ if (!rIStm.GetError())
+ {
+ VectorGraphicDataType aDataType(VectorGraphicDataType::Svg);
+
+ if (constWmfMagic == nMagic)
+ {
+ aDataType = VectorGraphicDataType::Wmf;
+ }
+ else if (constEmfMagic == nMagic)
+ {
+ aDataType = VectorGraphicDataType::Emf;
+ }
+ else if (constPdfMagic == nMagic)
+ {
+ aDataType = VectorGraphicDataType::Pdf;
+ }
+
+ auto aVectorGraphicDataPtr = std::make_shared<VectorGraphicData>(aNewData, aPath, aDataType);
+ rImpGraphic = aVectorGraphicDataPtr;
+ }
+ }
+ }
+ else
+ {
+ rIStm.SetError(nOrigError);
+ }
+
+ rIStm.Seek(nStmPos1);
+ }
+ }
+
+ rIStm.SetEndian( nOldFormat );
+}
+
+void WriteImpGraphic(SvStream& rOStm, const ImpGraphic& rImpGraphic)
+{
+ if (rOStm.GetError())
+ return;
+
+ rImpGraphic.ensureAvailable();
+
+ if (rImpGraphic.isSwappedOut())
+ {
+ rOStm.SetError( SVSTREAM_GENERALERROR );
+ return;
+ }
+
+ if( ( rOStm.GetVersion() >= SOFFICE_FILEFORMAT_50 ) &&
+ ( rOStm.GetCompressMode() & SvStreamCompressFlags::NATIVE ) &&
+ rImpGraphic.mpGfxLink && rImpGraphic.mpGfxLink->IsNative())
+ {
+ // native format
+ rOStm.WriteUInt32( NATIVE_FORMAT_50 );
+
+ // write compat info, destructor writes stuff into the header
+ {
+ VersionCompat aCompat( rOStm, StreamMode::WRITE, 1 );
+ }
+ rImpGraphic.mpGfxLink->SetPrefMapMode( rImpGraphic.ImplGetPrefMapMode() );
+ rImpGraphic.mpGfxLink->SetPrefSize( rImpGraphic.ImplGetPrefSize() );
+ TypeSerializer aSerializer(rOStm);
+ aSerializer.writeGfxLink(*rImpGraphic.mpGfxLink);
+ }
+ else
+ {
+ // own format
+ const SvStreamEndian nOldFormat = rOStm.GetEndian();
+ rOStm.SetEndian( SvStreamEndian::LITTLE );
+
+ switch( rImpGraphic.ImplGetType() )
+ {
+ case GraphicType::NONE:
+ case GraphicType::Default:
+ break;
+
+ case GraphicType::Bitmap:
+ {
+ if(rImpGraphic.getVectorGraphicData())
+ {
+ // stream out Vector Graphic defining data (length, byte array and evtl. path)
+ // this is used e.g. in swapping out graphic data and in transporting it over UNO API
+ // as sequence of bytes, but AFAIK not written anywhere to any kind of file, so it should be
+ // no problem to extend it; only used at runtime
+ switch (rImpGraphic.getVectorGraphicData()->getVectorGraphicDataType())
+ {
+ case VectorGraphicDataType::Wmf:
+ {
+ rOStm.WriteUInt32(constWmfMagic);
+ break;
+ }
+ case VectorGraphicDataType::Emf:
+ {
+ rOStm.WriteUInt32(constEmfMagic);
+ break;
+ }
+ case VectorGraphicDataType::Svg:
+ {
+ rOStm.WriteUInt32(constSvgMagic);
+ break;
+ }
+ case VectorGraphicDataType::Pdf:
+ {
+ rOStm.WriteUInt32(constPdfMagic);
+ break;
+ }
+ }
+
+ rOStm.WriteUInt32( rImpGraphic.getVectorGraphicData()->getVectorGraphicDataArrayLength() );
+ rOStm.WriteBytes(rImpGraphic.getVectorGraphicData()->getVectorGraphicDataArray().getConstArray(),
+ rImpGraphic.getVectorGraphicData()->getVectorGraphicDataArrayLength());
+ rOStm.WriteUniOrByteString(rImpGraphic.getVectorGraphicData()->getPath(),
+ rOStm.GetStreamCharSet());
+ }
+ else if( rImpGraphic.ImplIsAnimated())
+ {
+ WriteAnimation( rOStm, *rImpGraphic.mpAnimation );
+ }
+ else
+ {
+ WriteDIBBitmapEx(rImpGraphic.maBitmapEx, rOStm);
+ }
+ }
+ break;
+
+ default:
+ {
+ if( rImpGraphic.ImplIsSupportedGraphic() )
+ WriteGDIMetaFile( rOStm, rImpGraphic.maMetaFile );
+ }
+ break;
+ }
+
+ rOStm.SetEndian( nOldFormat );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */