/* -*- 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/make_shared.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 <utility>
#include <vcl/filter/SvmReader.hxx>
#include <vcl/filter/SvmWriter.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 <drawinglayer/primitive2d/baseprimitive2d.hxx>
#include <vcl/dibtools.hxx>
#include <map>
#include <memory>
#include <vcl/gdimetafiletools.hxx>
#include <vcl/TypeSerializer.hxx>
#include <vcl/pdfread.hxx>
#include <graphic/VectorGraphicLoader.hxx>
 
#define GRAPHIC_MTFTOBMP_MAXEXT     2048
#define GRAPHIC_STREAMBUFSIZE       8192UL
 
#define SWAP_FORMAT_ID COMPAT_FORMAT( 'S', 'W', 'A', 'P' )
 
using namespace com::sun::star;
 
 
class ImpSwapFile
{
private:
    utl::TempFileFast maTempFile;
    OUString maOriginURL;
 
public:
    ImpSwapFile(OUString aOriginURL)
        : maOriginURL(std::move(aOriginURL))
    {
    }
 
    SvStream* getStream() { return maTempFile.GetStream(StreamMode::READWRITE); }
    OUString const & getOriginURL() const { return maOriginURL; }
};
 
SvStream* ImpGraphic::getSwapFileStream() const
{
    if (mpSwapFile)
        return mpSwapFile->getStream();
    return nullptr;
}
 
ImpGraphic::ImpGraphic(bool bDefault)
    : MemoryManaged(false)
    , meType(bDefault ? GraphicType::Default : GraphicType::NONE)
{
}
 
ImpGraphic::ImpGraphic(const ImpGraphic& rImpGraphic)
    : MemoryManaged(rImpGraphic)
    , maCachedBitmap(rImpGraphic.maCachedBitmap)
    , maMetaFile(rImpGraphic.maMetaFile)
    , mpBitmapContainer(rImpGraphic.mpBitmapContainer)
    , maSwapInfo(rImpGraphic.maSwapInfo)
    , mpSwapFile(rImpGraphic.mpSwapFile)
    , mpGfxLink(rImpGraphic.mpGfxLink)
    , maVectorGraphicData(rImpGraphic.maVectorGraphicData)
    , meType(rImpGraphic.meType)
    , mnSizeBytes(rImpGraphic.mnSizeBytes)
    , mbSwapOut(rImpGraphic.mbSwapOut)
    , mbDummyContext(rImpGraphic.mbDummyContext)
    , maGraphicExternalLink(rImpGraphic.maGraphicExternalLink)
    , mbPrepared(rImpGraphic.mbPrepared)
{
    updateCurrentSizeInBytes(mnSizeBytes);
 
    // Special case for animations
    if (rImpGraphic.mpAnimationContainer)
    {
        mpAnimationContainer = std::make_shared<AnimationContainer>(rImpGraphic.mpAnimationContainer->maAnimation);
        maCachedBitmap = mpAnimationContainer->maAnimation.GetBitmap();
    }
}
 
ImpGraphic::ImpGraphic(ImpGraphic&& rImpGraphic) noexcept
    : MemoryManaged(rImpGraphic)
    , maCachedBitmap(std::move(rImpGraphic.maCachedBitmap))
    , maMetaFile(std::move(rImpGraphic.maMetaFile))
    , mpBitmapContainer(std::move(rImpGraphic.mpBitmapContainer))
    , maSwapInfo(std::move(rImpGraphic.maSwapInfo))
    , mpAnimationContainer(std::move(rImpGraphic.mpAnimationContainer))
    , mpSwapFile(std::move(rImpGraphic.mpSwapFile))
    , mpGfxLink(std::move(rImpGraphic.mpGfxLink))
    , maVectorGraphicData(std::move(rImpGraphic.maVectorGraphicData))
    , meType(rImpGraphic.meType)
    , mnSizeBytes(rImpGraphic.mnSizeBytes)
    , mbSwapOut(rImpGraphic.mbSwapOut)
    , mbDummyContext(rImpGraphic.mbDummyContext)
    , maGraphicExternalLink(rImpGraphic.maGraphicExternalLink)
    , mbPrepared (rImpGraphic.mbPrepared)
{
    updateCurrentSizeInBytes(mnSizeBytes);
 
    rImpGraphic.clear();
    rImpGraphic.mbDummyContext = false;
}
 
ImpGraphic::ImpGraphic(std::shared_ptr<GfxLink> xGfxLink, sal_Int32 nPageIndex)
    : MemoryManaged(true)
    , mpGfxLink(std::move(xGfxLink))
    , meType(GraphicType::Bitmap)
    , mbSwapOut(true)
{
    maSwapInfo.mbIsTransparent = true;
    maSwapInfo.mbIsAlpha = true;
    maSwapInfo.mbIsEPS = false;
    maSwapInfo.mbIsAnimated = false;
    maSwapInfo.mnAnimationLoopCount = 0;
    maSwapInfo.mnPageIndex = nPageIndex;
 
    ensureCurrentSizeInBytes();
}
 
ImpGraphic::ImpGraphic(GraphicExternalLink aGraphicExternalLink)
    : MemoryManaged(true)
    , meType(GraphicType::Default)
    , maGraphicExternalLink(std::move(aGraphicExternalLink))
{
    ensureCurrentSizeInBytes();
}
 
ImpGraphic::ImpGraphic(const Bitmap& rBitmap)
    : MemoryManaged(!rBitmap.IsEmpty())
    , mpBitmapContainer(new BitmapContainer(rBitmap))
    , meType(rBitmap.IsEmpty() ? GraphicType::NONE : GraphicType::Bitmap)
{
    ensureCurrentSizeInBytes();
}
 
ImpGraphic::ImpGraphic(const std::shared_ptr<VectorGraphicData>& rVectorGraphicDataPtr)
    : MemoryManaged(bool(rVectorGraphicDataPtr))
    , maVectorGraphicData(rVectorGraphicDataPtr)
    , meType(rVectorGraphicDataPtr ? GraphicType::Bitmap : GraphicType::NONE)
{
    ensureCurrentSizeInBytes();
}
 
ImpGraphic::ImpGraphic(const Animation& rAnimation)
    : MemoryManaged(true)
    , maCachedBitmap(rAnimation.GetBitmap())
    , mpAnimationContainer(std::make_shared<AnimationContainer>(rAnimation))
    , meType(GraphicType::Bitmap)
{
    ensureCurrentSizeInBytes();
}
 
ImpGraphic::ImpGraphic(const GDIMetaFile& rMetafile)
    : MemoryManaged(true)
    , maMetaFile(rMetafile)
    , meType(GraphicType::GdiMetafile)
{
    ensureCurrentSizeInBytes();
}
 
ImpGraphic::~ImpGraphic()
{
}
 
ImpGraphic& ImpGraphic::operator=(const ImpGraphic& rImpGraphic)
{
    if( &rImpGraphic != this )
    {
        maMetaFile = rImpGraphic.maMetaFile;
        meType = rImpGraphic.meType;
        mnSizeBytes = rImpGraphic.mnSizeBytes;
        updateCurrentSizeInBytes(mnSizeBytes);
 
        maSwapInfo = rImpGraphic.maSwapInfo;
        mbDummyContext = rImpGraphic.mbDummyContext;
        maGraphicExternalLink = rImpGraphic.maGraphicExternalLink;
 
        mpAnimationContainer.reset();
        if (rImpGraphic.mpAnimationContainer)
        {
            mpAnimationContainer = std::make_shared<AnimationContainer>(*rImpGraphic.mpAnimationContainer);
            maCachedBitmap = mpAnimationContainer->maAnimation.GetBitmap();
        }
        else
        {
            maCachedBitmap = rImpGraphic.maCachedBitmap;
        }
 
        mpBitmapContainer.reset();
        if (rImpGraphic.mpBitmapContainer)
            mpBitmapContainer = rImpGraphic.mpBitmapContainer;
 
        mbSwapOut = rImpGraphic.mbSwapOut;
        mpSwapFile = rImpGraphic.mpSwapFile;
        mbPrepared = rImpGraphic.mbPrepared;
 
        mpGfxLink = rImpGraphic.mpGfxLink;
 
        maVectorGraphicData.reset();
        if (rImpGraphic.maVectorGraphicData)
            maVectorGraphicData = rImpGraphic.maVectorGraphicData;
 
        resetLastUsed();
 
        changeExisting(mnSizeBytes);
    }
 
    return *this;
}
 
ImpGraphic& ImpGraphic::operator=(ImpGraphic&& rImpGraphic)
{
    maMetaFile = std::move(rImpGraphic.maMetaFile);
    meType = rImpGraphic.meType;
    mnSizeBytes = rImpGraphic.mnSizeBytes;
    maSwapInfo = std::move(rImpGraphic.maSwapInfo);
    mbDummyContext = rImpGraphic.mbDummyContext;
    maCachedBitmap = std::move(rImpGraphic.maCachedBitmap);
    mpAnimationContainer = std::move(rImpGraphic.mpAnimationContainer);
    mpBitmapContainer = std::move(rImpGraphic.mpBitmapContainer);
    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.clear();
    rImpGraphic.mbDummyContext = false;
    resetLastUsed();
 
    changeExisting(mnSizeBytes);
 
    return *this;
}
 
bool ImpGraphic::operator==( const ImpGraphic& rOther ) const
{
    if( this == &rOther )
        return true;
 
    if (mbPrepared && rOther.mbPrepared)
        return (*mpGfxLink == *rOther.mpGfxLink);
 
    if (!isAvailable() || !rOther.isAvailable())
        return false;
 
    if ( meType != rOther.meType )
        return false;
 
    bool bRet = false;
    switch( meType )
    {
        case GraphicType::NONE:
        case GraphicType::Default:
            return true;
 
        case GraphicType::GdiMetafile:
            return ( rOther.maMetaFile == maMetaFile );
 
        case GraphicType::Bitmap:
        {
            if (maVectorGraphicData)
            {
                if (maVectorGraphicData == rOther.maVectorGraphicData)
                    // equal instances
                    bRet = true;
                else if (rOther.maVectorGraphicData)
                    // equal content
                    bRet = (*maVectorGraphicData) == (*rOther.maVectorGraphicData);
            }
            else if (mpAnimationContainer && rOther.mpAnimationContainer && (*mpAnimationContainer == *rOther.mpAnimationContainer))
            {
                bRet = true;
            }
            else if (mpBitmapContainer && rOther.mpBitmapContainer && (*mpBitmapContainer == *rOther.mpBitmapContainer))
            {
                bRet = true;
            }
        }
        break;
    }
 
    return bRet;
}
 
const std::shared_ptr<VectorGraphicData>& ImpGraphic::getVectorGraphicData() const
{
    ensureAvailable();
 
    return maVectorGraphicData;
}
 
void BitmapContainer::createSwapInfo(SwapInfo& rSwapInfo)
{
    rSwapInfo.maSizePixel = maBitmap.GetSizePixel();
 
    rSwapInfo.maPrefMapMode = getPrefMapMode();
    rSwapInfo.maPrefSize = getPrefSize();
    rSwapInfo.mbIsAnimated = false;
    rSwapInfo.mbIsEPS = false;
    rSwapInfo.mbIsTransparent = isAlpha();
    rSwapInfo.mbIsAlpha = isAlpha();
    rSwapInfo.mnAnimationLoopCount = 0;
    rSwapInfo.mnPageIndex = -1;
}
 
void AnimationContainer::createSwapInfo(SwapInfo& rSwapInfo)
{
    rSwapInfo.maSizePixel = maAnimation.GetBitmap().GetSizePixel();
 
    rSwapInfo.maPrefMapMode = getPrefMapMode();
    rSwapInfo.maPrefSize = getPrefSize();
    rSwapInfo.mbIsAnimated = true;
    rSwapInfo.mbIsEPS = false;
    rSwapInfo.mbIsTransparent = isTransparent();
    rSwapInfo.mbIsAlpha = false;
    rSwapInfo.mnAnimationLoopCount = getLoopCount();
    rSwapInfo.mnPageIndex = -1;
}
 
void ImpGraphic::createSwapInfo()
{
    if (isSwappedOut())
        return;
 
    if (mpBitmapContainer)
    {
        return mpBitmapContainer->createSwapInfo(maSwapInfo);
    }
    else if (mpAnimationContainer)
    {
        return mpAnimationContainer->createSwapInfo(maSwapInfo);
    }
 
    else if (!maCachedBitmap.IsEmpty())
        maSwapInfo.maSizePixel = maCachedBitmap.GetSizePixel();
    else
        maSwapInfo.maSizePixel = Size();
 
    maSwapInfo.maPrefMapMode = getPrefMapMode();
    maSwapInfo.maPrefSize = getPrefSize();
    maSwapInfo.mbIsAnimated = isAnimated();
    maSwapInfo.mbIsEPS = isEPS();
    maSwapInfo.mbIsTransparent = isTransparent();
    maSwapInfo.mbIsAlpha = isAlpha();
    maSwapInfo.mnAnimationLoopCount = getAnimationLoopCount();
    maSwapInfo.mnPageIndex = getPageNumber();
}
 
void ImpGraphic::clearGraphics()
{
    maCachedBitmap = Bitmap();
    mpBitmapContainer.reset();
    maMetaFile.Clear();
    mpAnimationContainer.reset();
    maVectorGraphicData.reset();
}
 
void ImpGraphic::setPrepared(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 (aDescriptor.GetPreferredLogSize() && aDescriptor.GetPreferredMapMode())
            {
                maSwapInfo.maPrefSize = *aDescriptor.GetPreferredLogSize();
                maSwapInfo.maPrefMapMode = *aDescriptor.GetPreferredMapMode();
            }
            else 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;
 
    if (maVectorGraphicData)
        maSwapInfo.mnPageIndex = maVectorGraphicData->getPageIndex();
 
    // tdf#167007 Add animated graphic to cache when prepared
    // For some reason, after an animation has been swapped out by
    // MemoryManager::loopAndReduceMemory(), the animation repeatedly
    // creates a new ImpGraphic instance, swaps it in, but it never
    // gets registered in the cache. Since it is not in the cache, new
    // ImpGraphic instances get deleted almost immediately after they
    // are created.
    // So prevent immediate deletion by ensuring that animated
    // ImpGraphic instances are registered when they are prepared.
    if (maSwapInfo.mbIsAnimated)
        registerIntoManager();
}
 
void ImpGraphic::clear()
{
    mpSwapFile.reset();
    mbSwapOut = false;
    mbPrepared = false;
 
    // cleanup
    clearGraphics();
    meType = GraphicType::NONE;
    mnSizeBytes = 0;
 
    changeExisting(mnSizeBytes);
    maGraphicExternalLink.msURL.clear();
}
 
bool ImpGraphic::isSupportedGraphic() const
{
    return meType != GraphicType::NONE;
}
 
bool ImpGraphic::isTransparent() const
{
    if (mbSwapOut)
    {
        return maSwapInfo.mbIsTransparent;
    }
    else if (meType == GraphicType::Bitmap)
    {
        if (maVectorGraphicData)
            return true;
        else if (mpBitmapContainer)
            return mpBitmapContainer->isAlpha();
        else if (mpAnimationContainer)
            return mpAnimationContainer->isTransparent();
    }
 
    return true;
}
 
bool ImpGraphic::isAlpha() const
{
    if (mbSwapOut)
        return maSwapInfo.mbIsAlpha;
 
    if (meType == GraphicType::Bitmap)
    {
        if (maVectorGraphicData)
            return true;
        else if (mpBitmapContainer)
            return mpBitmapContainer->isAlpha();
    }
    return false;
}
 
bool ImpGraphic::isAnimated() const
{
    return mbSwapOut ? maSwapInfo.mbIsAnimated : mpAnimationContainer != nullptr;
}
 
bool ImpGraphic::isEPS() 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();
}
 
void ImpGraphic::updateBitmapFromVectorGraphic(const Size& pixelSize) const
{
    assert (maVectorGraphicData);
    auto* pThisRW = const_cast<ImpGraphic*>(this);
    // use maBitmapEx as local buffer for rendered vector image
    if (pixelSize.Width() && pixelSize.Height())
    {
        if (maCachedBitmap.IsEmpty() || maCachedBitmap.GetSizePixel() != pixelSize)
            pThisRW->maCachedBitmap = maVectorGraphicData->getBitmap(pixelSize);
    }
    else // maVectorGraphicData caches the replacement, so updating unconditionally is cheap
    {
        pThisRW->maCachedBitmap = maVectorGraphicData->getReplacement();
    }
 
    if (maExPrefSize.getWidth() && maExPrefSize.getHeight())
        pThisRW->maCachedBitmap.SetPrefSize(maExPrefSize);
}
 
Bitmap ImpGraphic::getBitmap(const GraphicConversionParameters& rParameters) const
{
    Bitmap aRetBmp;
 
    ensureAvailable();
 
    if (meType == GraphicType::Bitmap)
    {
        if (!mpAnimationContainer && maVectorGraphicData)
            updateBitmapFromVectorGraphic(rParameters.getSizePixel());
 
        if (mpAnimationContainer)
            aRetBmp = mpAnimationContainer->maAnimation.GetBitmap();
        else if (mpBitmapContainer)
            aRetBmp = mpBitmapContainer->maBitmap;
        else
            aRetBmp = maCachedBitmap;
 
        if (rParameters.getSizePixel().Width() || rParameters.getSizePixel().Height())
            aRetBmp.Scale(rParameters.getSizePixel());
    }
    else if( ( meType != GraphicType::Default ) && isSupportedGraphic() )
    {
        if (maCachedBitmap.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<tools::Long>(GRAPHIC_MTFTOBMP_MAXEXT * fWH));
                    aDrawSize.setHeight(GRAPHIC_MTFTOBMP_MAXEXT);
                }
                else
                {
                    aDrawSize.setWidth(GRAPHIC_MTFTOBMP_MAXEXT);
                    aDrawSize.setHeight(basegfx::fround<tools::Long>(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 == getType())
            {
                // tdf#126319 Removed correction based on hairline-at-the-extremes of
                // the metafile. The task shows that this is no longer sufficient since
                // less hairlines get used in general - what is good, but breaks that
                // old fix. Anyways, hairlines are a left-over from non-AA times
                // when it was not possible to paint lines taller than one pixel.
                // This might need to be corrected further using primitives and
                // the possibility to get better-quality ranges for correction. For
                // now, always add that one pixel.
                aPixelSize.setWidth(aPixelSize.getWidth() + 1);
                aPixelSize.setHeight(aPixelSize.getHeight() + 1);
            }
 
            if(aVDev->SetOutputSizePixel(aPixelSize))
            {
                if(rParameters.getAntiAliase())
                {
                    aVDev->SetAntialiasing(aVDev->GetAntialiasing() | AntialiasingFlags::Enable);
                }
 
                if(rParameters.getSnapHorVerLines())
                {
                    aVDev->SetAntialiasing(aVDev->GetAntialiasing() | AntialiasingFlags::PixelSnapHairline);
                }
 
                draw(*aVDev, Point(), aDrawSize);
 
                // use maBitmapEx as local buffer for rendered metafile
                const_cast<ImpGraphic*>(this)->maCachedBitmap = aVDev->GetBitmap( Point(), aVDev->GetOutputSizePixel() );
            }
        }
 
        aRetBmp = maCachedBitmap;
    }
 
    if( !aRetBmp.IsEmpty() )
    {
        aRetBmp.SetPrefMapMode(getPrefMapMode());
        aRetBmp.SetPrefSize(getPrefSize());
    }
 
    return aRetBmp;
}
 
Animation ImpGraphic::getAnimation() const
{
    Animation aAnimation;
 
    ensureAvailable();
 
    if (mpAnimationContainer)
        aAnimation = mpAnimationContainer->maAnimation;
 
    return aAnimation;
}
 
const Bitmap& ImpGraphic::getBitmapRef() const
{
    ensureAvailable();
 
    if (mpBitmapContainer)
        return mpBitmapContainer->getBitmapRef();
    else
        return maCachedBitmap;
}
 
const GDIMetaFile& ImpGraphic::getGDIMetaFile() const
{
    ensureAvailable();
    if (!maMetaFile.GetActionSize()
        && maVectorGraphicData
        && (VectorGraphicDataType::Emf == maVectorGraphicData->getType()
            || VectorGraphicDataType::Wmf == maVectorGraphicData->getType()))
    {
        // 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]);
            auto pUnoPrimitive = static_cast< const drawinglayer::primitive2d::UnoPrimitive2D* >(xReference.get());
            if (pUnoPrimitive)
            {
                const MetafileAccessor* pMetafileAccessor = dynamic_cast< const MetafileAccessor* >(pUnoPrimitive->getBasePrimitive2D().get());
 
                if (pMetafileAccessor)
                {
                    // it is a MetafileAccessor implementation, get Metafile
                    pMetafileAccessor->accessMetafile(const_cast< ImpGraphic* >(this)->maMetaFile);
                }
            }
        }
    }
 
    if (GraphicType::Bitmap == meType && !maMetaFile.GetActionSize())
    {
        if (maVectorGraphicData)
            updateBitmapFromVectorGraphic();
 
        // #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);
 
        Bitmap aBitmap = mpBitmapContainer ? mpBitmapContainer->maBitmap : maCachedBitmap;
 
        // #123983# directly create a metafile with the same PrefSize and PrefMapMode
        // the bitmap has, this will be an always correct metafile
        if (aBitmap.HasAlpha())
        {
            pThat->maMetaFile.AddAction(new MetaBmpExScaleAction(Point(), aBitmap.GetPrefSize(), aBitmap));
        }
        else
        {
            pThat->maMetaFile.AddAction(new MetaBmpScaleAction(Point(), aBitmap.GetPrefSize(), aBitmap));
        }
 
        pThat->maMetaFile.Stop();
        pThat->maMetaFile.WindStart();
        pThat->maMetaFile.SetPrefSize(aBitmap.GetPrefSize());
        pThat->maMetaFile.SetPrefMapMode(aBitmap.GetPrefMapMode());
    }
 
    return maMetaFile;
}
 
Size ImpGraphic::getSizePixel() const
{
    Size aSize;
 
    if (isSwappedOut())
        aSize = maSwapInfo.maSizePixel;
    else
        aSize = getBitmap(GraphicConversionParameters()).GetSizePixel();
 
    return aSize;
}
 
Size ImpGraphic::getPrefSize() const
{
    Size aSize;
 
    if (isSwappedOut())
    {
        aSize = maSwapInfo.maPrefSize;
    }
    else
    {
        switch (meType)
        {
            case GraphicType::Bitmap:
            {
                if (maVectorGraphicData)
                {
                    if (maCachedBitmap.IsEmpty())
                    {
                        if (!maExPrefSize.getWidth() || !maExPrefSize.getHeight())
                        {
                            // svg not yet buffered in maBitmapEx, return size derived from range
                            const basegfx::B2DRange& rRange = maVectorGraphicData->getRange();
 
    #ifdef MACOSX
                            // tdf#157680 scale down estimated size of embedded PDF
                            // For some unknown reason, the embedded PDF sizes
                            // are 20x larger than expected. This only occurs on
                            // macOS so possibly there is some special conversion
                            // from MapUnit::MapPoint to MapUnit::MapTwip elsewhere
                            // in the code.
                            if (maVectorGraphicData->getType() == VectorGraphicDataType::Pdf)
                                aSize = Size(basegfx::fround(rRange.getWidth() / 20.0f), basegfx::fround(rRange.getHeight() / 20.0f));
                            else
    #endif
                                aSize = Size(basegfx::fround<tools::Long>(rRange.getWidth()), basegfx::fround<tools::Long>(rRange.getHeight()));
                        }
                        else
                        {
                            aSize = maExPrefSize;
                        }
                    }
                    else
                    {
                        aSize = maCachedBitmap.GetPrefSize();
 
                        if (!aSize.Width() || !aSize.Height())
                            aSize = maCachedBitmap.GetSizePixel();
                    }
                }
                else if (mpBitmapContainer)
                {
                    aSize = mpBitmapContainer->getPrefSize();
                }
                else if (mpAnimationContainer)
                {
                    aSize = mpAnimationContainer->getPrefSize();
                }
            }
            break;
 
            case GraphicType::GdiMetafile:
            {
                aSize = maMetaFile.GetPrefSize();
            }
            break;
 
            case GraphicType::NONE:
            case GraphicType::Default:
                break;
        }
    }
 
    return aSize;
}
 
void ImpGraphic::setValuesForPrefSize(const Size& rPrefSize)
{
    switch (meType)
    {
        case GraphicType::Bitmap:
        {
            // used when importing a writer FlyFrame with SVG as graphic, added conversion
            // to allow setting the PrefSize at the Bitmap to hold it
            if (maVectorGraphicData)
            {
                maExPrefSize = rPrefSize;
                maCachedBitmap.SetPrefSize(rPrefSize);
            }
            // #108077# Push through pref size to animation object,
            // will be lost on copy otherwise
            else if (mpAnimationContainer)
            {
                const_cast<Bitmap&>(mpAnimationContainer->maAnimation.GetBitmap()).SetPrefSize(rPrefSize);
                maCachedBitmap.SetPrefSize(rPrefSize);
            }
            else if (mpBitmapContainer)
            {
                mpBitmapContainer->maBitmap.SetPrefSize(rPrefSize);
            }
        }
        break;
 
        case GraphicType::GdiMetafile:
        {
            if (isSupportedGraphic())
                maMetaFile.SetPrefSize(rPrefSize);
        }
        break;
 
        case GraphicType::NONE:
        case GraphicType::Default:
            break;
    }
}
 
void ImpGraphic::setPrefSize(const Size& rPrefSize)
{
    ensureAvailable();
    setValuesForPrefSize(rPrefSize);
}
 
MapMode ImpGraphic::getPrefMapMode() const
{
    MapMode aMapMode;
 
    if (isSwappedOut())
    {
        aMapMode = maSwapInfo.maPrefMapMode;
    }
    else
    {
        switch (meType)
        {
            case GraphicType::Bitmap:
            {
                if (maVectorGraphicData)
                {
                    if (maCachedBitmap.IsEmpty())
                    {
                        // svg not yet buffered in maBitmapEx, return default PrefMapMode
                        aMapMode = MapMode(MapUnit::Map100thMM);
                    }
                    else
                    {
                        const Size aSize = maCachedBitmap.GetPrefSize();
                        if (aSize.Width() && aSize.Height())
                            aMapMode = maCachedBitmap.GetPrefMapMode();
                    }
                }
                else if (mpBitmapContainer)
                {
                    aMapMode = mpBitmapContainer->getPrefMapMode();
                }
                else if (mpAnimationContainer)
                {
                    aMapMode = mpAnimationContainer->getPrefMapMode();
                }
            }
            break;
 
            case GraphicType::GdiMetafile:
            {
                return maMetaFile.GetPrefMapMode();
            }
            break;
 
            case GraphicType::NONE:
            case GraphicType::Default:
                break;
        }
    }
 
    return aMapMode;
}
 
void ImpGraphic::setValuesForPrefMapMod(const MapMode& rPrefMapMode)
{
    switch (meType)
    {
        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 updateBitmapFromVectorGraphic()
            }
 
            // #108077# Push through pref mapmode to animation object,
            // will be lost on copy otherwise
            else if (mpAnimationContainer)
            {
                const_cast<Bitmap&>(mpAnimationContainer->maAnimation.GetBitmap()).SetPrefMapMode(rPrefMapMode);
                maCachedBitmap.SetPrefMapMode(rPrefMapMode);
            }
            else if (mpBitmapContainer)
            {
                mpBitmapContainer->maBitmap.SetPrefMapMode(rPrefMapMode);
            }
        }
        break;
 
        case GraphicType::GdiMetafile:
        {
            maMetaFile.SetPrefMapMode(rPrefMapMode);
        }
        break;
 
        case GraphicType::NONE:
        case GraphicType::Default:
            break;
    }
}
 
void ImpGraphic::setPrefMapMode(const MapMode& rPrefMapMode)
{
    ensureAvailable();
    setValuesForPrefMapMod(rPrefMapMode);
}
 
void ImpGraphic::ensureCurrentSizeInBytes()
{
    if (isAvailable())
        changeExisting(getSizeBytes());
    else
        changeExisting(0);
}
 
sal_Int64 ImpGraphic::getSizeBytes() const
{
    if (mnSizeBytes > 0)
        return mnSizeBytes;
 
    if (mbPrepared)
        ensureAvailable();
 
    switch (meType)
    {
        case GraphicType::Bitmap:
        {
            if (maVectorGraphicData)
            {
                std::pair<VectorGraphicData::State, size_t> aPair(maVectorGraphicData->getSizeBytes());
                if (VectorGraphicData::State::UNPARSED == aPair.first)
                {
                    return aPair.second; // don't cache it until Vector Graphic Data is parsed
                }
                mnSizeBytes = aPair.second;
            }
            else
            {
                if (mpAnimationContainer)
                    mnSizeBytes = mpAnimationContainer->getSizeBytes();
                else if (mpBitmapContainer)
                    mnSizeBytes = mpBitmapContainer->getSizeBytes();
            }
        }
        break;
 
        case GraphicType::GdiMetafile:
        {
            mnSizeBytes = maMetaFile.GetSizeBytes();
        }
        break;
 
        case GraphicType::NONE:
        case GraphicType::Default:
            break;
    }
 
    return mnSizeBytes;
}
 
void ImpGraphic::draw(OutputDevice& rOutDev,
                      const Point& rDestPt, const Size& rDestSize) const
{
    ensureAvailable();
 
    if (isSwappedOut())
        return;
 
    switch (meType)
    {
        case GraphicType::Bitmap:
        {
            if (mpAnimationContainer)
            {
                mpAnimationContainer->maAnimation.Draw(rOutDev, rDestPt, rDestSize);
            }
            else if (mpBitmapContainer)
            {
                mpBitmapContainer->getBitmapRef().Draw(&rOutDev, rDestPt, rDestSize);
            }
            else if (maVectorGraphicData)
            {
                updateBitmapFromVectorGraphic(rOutDev.LogicToPixel(rDestSize));
                getBitmapRef().Draw(&rOutDev, rDestPt, rDestSize);
            }
        }
        break;
 
        case GraphicType::GdiMetafile:
        {
            const_cast<ImpGraphic*>(this)->maMetaFile.WindStart();
            const_cast<ImpGraphic*>(this)->maMetaFile.Play(rOutDev, rDestPt, rDestSize);
            const_cast<ImpGraphic*>(this)->maMetaFile.WindStart();
        }
        break;
 
        case GraphicType::Default:
        case GraphicType::NONE:
            break;
    }
}
 
void ImpGraphic::startAnimation(OutputDevice& rOutDev, const Point& rDestPt,
                                const Size& rDestSize, tools::Long nRendererId,
                                OutputDevice* pFirstFrameOutDev )
{
    ensureAvailable();
 
    if (isSupportedGraphic() && !isSwappedOut() && mpAnimationContainer)
        mpAnimationContainer->maAnimation.Start(rOutDev, rDestPt, rDestSize, nRendererId, pFirstFrameOutDev);
}
 
void ImpGraphic::stopAnimation( const OutputDevice* pOutDev, tools::Long nRendererId )
{
    ensureAvailable();
 
    if (isSupportedGraphic() && !isSwappedOut() && mpAnimationContainer)
        mpAnimationContainer->maAnimation.Stop( pOutDev, nRendererId );
}
 
void ImpGraphic::setAnimationNotifyHdl( const Link<Animation*,void>& rLink )
{
    ensureAvailable();
 
    if (mpAnimationContainer)
        mpAnimationContainer->maAnimation.SetNotifyHdl( rLink );
}
 
Link<Animation*,void> ImpGraphic::getAnimationNotifyHdl() const
{
    Link<Animation*,void> aLink;
 
    ensureAvailable();
 
    if (mpAnimationContainer)
        aLink = mpAnimationContainer->maAnimation.GetNotifyHdl();
 
    return aLink;
}
 
sal_uInt32 ImpGraphic::getAnimationLoopCount() const
{
    if (mbSwapOut)
        return maSwapInfo.mnAnimationLoopCount;
 
    return mpAnimationContainer ? mpAnimationContainer->getLoopCount() : 0;
}
 
bool ImpGraphic::swapInContent(SvStream& rStream)
{
    bool bRet = false;
 
    sal_uInt32 nId;
    sal_Int32 nType;
    sal_Int32 nLength;
 
    rStream.ReadUInt32(nId);
 
    // check version
    if (SWAP_FORMAT_ID != nId)
    {
        SAL_WARN("vcl", "Incompatible swap file!");
        return false;
    }
 
    rStream.ReadInt32(nType);
    rStream.ReadInt32(nLength);
 
    meType = static_cast<GraphicType>(nType);
 
    if (meType == GraphicType::NONE  || meType == GraphicType::Default)
    {
        return true;
    }
    else
    {
        bRet = swapInGraphic(rStream);
    }
 
    return bRet;
}
 
bool ImpGraphic::swapOutGraphic(SvStream& rStream)
{
    if (rStream.GetError())
        return false;
 
    ensureAvailable();
 
    if (isSwappedOut())
    {
        rStream.SetError(SVSTREAM_GENERALERROR);
        return false;
    }
 
    switch (meType)
    {
        case GraphicType::GdiMetafile:
        {
            if(!rStream.GetError())
            {
                SvmWriter aWriter(rStream);
                aWriter.Write(maMetaFile);
            }
        }
        break;
 
        case GraphicType::Bitmap:
        {
            if (maVectorGraphicData)
            {
                rStream.WriteInt32(sal_Int32(GraphicContentType::Vector));
                // 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 (maVectorGraphicData->getType())
                {
                    case VectorGraphicDataType::Wmf:
                    {
                        rStream.WriteUInt32(constWmfMagic);
                        break;
                    }
                    case VectorGraphicDataType::Emf:
                    {
                        rStream.WriteUInt32(constEmfMagic);
                        break;
                    }
                    case VectorGraphicDataType::Svg:
                    {
                        rStream.WriteUInt32(constSvgMagic);
                        break;
                    }
                    case VectorGraphicDataType::Pdf:
                    {
                        rStream.WriteUInt32(constPdfMagic);
                        break;
                    }
                }
 
                rStream.WriteUInt32(maVectorGraphicData->getBinaryDataContainer().getSize());
                maVectorGraphicData->getBinaryDataContainer().writeToStream(rStream);
            }
            else if (mpAnimationContainer)
            {
                rStream.WriteInt32(sal_Int32(GraphicContentType::Animation));
                WriteAnimation(rStream, mpAnimationContainer->maAnimation);
            }
            else if (mpBitmapContainer)
            {
                rStream.WriteInt32(sal_Int32(GraphicContentType::Bitmap));
                WriteDIBBitmapEx(mpBitmapContainer->maBitmap, rStream);
            }
        }
        break;
 
        case GraphicType::NONE:
        case GraphicType::Default:
            break;
    }
 
    if (mpGfxLink)
        mpGfxLink->getDataContainer().swapOut();
 
    return true;
}
 
bool ImpGraphic::swapOutContent(SvStream& rStream)
{
    ensureAvailable();
 
    bool bRet = false;
 
    if (meType == GraphicType::NONE || meType == GraphicType::Default || isSwappedOut())
        return false;
 
    sal_uLong nDataFieldPos;
 
    // Write the SWAP ID
    rStream.WriteUInt32(SWAP_FORMAT_ID);
 
    rStream.WriteInt32(static_cast<sal_Int32>(meType));
 
    // data size is updated later
    nDataFieldPos = rStream.Tell();
    rStream.WriteInt32(0);
 
    // write data block
    const sal_uInt64 nDataStart = rStream.Tell();
 
    swapOutGraphic(rStream);
 
    if (!rStream.GetError())
    {
        // Write the written length th the header
        const sal_uInt64 nCurrentPosition = rStream.Tell();
        rStream.Seek(nDataFieldPos);
        rStream.WriteInt32(nCurrentPosition - nDataStart);
        rStream.Seek(nCurrentPosition);
        bRet = true;
    }
 
    return bRet;
}
 
bool ImpGraphic::swapOut()
{
    if (isSwappedOut())
        return false;
 
    bool bResult = false;
 
    // We have GfxLink so we have the source available
    if (mpGfxLink && mpGfxLink->IsNative())
    {
        createSwapInfo();
 
        clearGraphics();
 
        // reset the swap file
        mpSwapFile.reset();
 
        mpGfxLink->getDataContainer().swapOut();
 
        // mark as swapped out
        mbSwapOut = true;
 
        bResult = true;
    }
    else
    {
        // Create a swap file
        auto pSwapFile = std::make_shared<ImpSwapFile>(getOriginURL());
 
        // Open a stream to write the swap file to
        {
            SvStream* pOutputStream = pSwapFile->getStream();
 
            if (!pOutputStream)
                return false;
 
            // Write to stream
            pOutputStream->SetVersion(SOFFICE_FILEFORMAT_50);
            pOutputStream->SetCompressMode(SvStreamCompressFlags::NATIVE);
            pOutputStream->SetBufferSize(GRAPHIC_STREAMBUFSIZE);
 
            if (!pOutputStream->GetError() && swapOutContent(*pOutputStream))
            {
                pOutputStream->FlushBuffer();
                bResult = !pOutputStream->GetError();
            }
        }
 
        // Check if writing was successful
        if (bResult)
        {
            // We have swapped out, so can clean memory and prepare swap info
            createSwapInfo();
            clearGraphics();
 
            mpSwapFile = std::move(pSwapFile);
            mbSwapOut = true;
        }
    }
 
    if (bResult)
    {
        // Signal to manager that we have swapped out
        swappedOut(0);
    }
 
    return bResult;
}
 
bool ImpGraphic::ensureAvailable() const
{
    bool bResult = true;
 
    if (isSwappedOut())
    {
        auto pThis = const_cast<ImpGraphic*>(this);
        pThis->registerIntoManager();
 
        bResult = pThis->swapIn();
    }
 
    resetLastUsed();
    return bResult;
}
 
void ImpGraphic::updateFromLoadedGraphic(const ImpGraphic* pGraphic)
{
    if (mbPrepared)
    {
        GraphicExternalLink aLink = maGraphicExternalLink;
        Size aPrefSize = maSwapInfo.maPrefSize;
        MapMode aPrefMapMode = maSwapInfo.maPrefMapMode;
        *this = *pGraphic;
        if (aPrefSize.getWidth() && aPrefSize.getHeight() && aPrefMapMode == getPrefMapMode())
        {
            // 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.
            setPrefSize(aPrefSize);
        }
        maGraphicExternalLink = std::move(aLink);
    }
    else
    {
        // Move over only graphic content
        mpAnimationContainer.reset();
 
        if (pGraphic->mpAnimationContainer)
        {
            mpAnimationContainer = std::make_shared<AnimationContainer>(*pGraphic->mpAnimationContainer);
            maCachedBitmap = mpAnimationContainer->maAnimation.GetBitmap();
        }
        else if (pGraphic->mpBitmapContainer)
        {
            mpBitmapContainer = pGraphic->mpBitmapContainer;
        }
        else
        {
            maCachedBitmap = pGraphic->maCachedBitmap;
        }
 
        maMetaFile = pGraphic->maMetaFile;
        maVectorGraphicData = pGraphic->maVectorGraphicData;
 
        // Set to 0, to force recalculation
        mnSizeBytes = 0;
        mnChecksum = 0;
 
        restoreFromSwapInfo();
 
        mbSwapOut = false;
    }
}
 
void ImpGraphic::dumpState(rtl::OStringBuffer &rState)
{
    if (meType == GraphicType::NONE && mnSizeBytes == 0)
        return; // uninteresting.
 
    rState.append("\n\t");
 
    if (mbSwapOut)
        rState.append("swapped\t");
    else
        rState.append("loaded\t");
 
    rState.append(static_cast<sal_Int32>(meType));
    rState.append("\tsize:\t");
    rState.append(mnSizeBytes);
    rState.append("\tgfxl:\t");
    rState.append(static_cast<sal_Int64>(mpGfxLink ? mpGfxLink->getSizeBytes() : -1));
    rState.append("\t");
    rState.append(static_cast<sal_Int32>(maSwapInfo.maSizePixel.Width()));
    rState.append("x");
    rState.append(static_cast<sal_Int32>(maSwapInfo.maSizePixel.Height()));
    rState.append("\t");
    rState.append(static_cast<sal_Int32>(maExPrefSize.Width()));
    rState.append("x");
    rState.append(static_cast<sal_Int32>(maExPrefSize.Height()));
}
 
void ImpGraphic::restoreFromSwapInfo()
{
    setValuesForPrefMapMod(maSwapInfo.maPrefMapMode);
    setValuesForPrefSize(maSwapInfo.maPrefSize);
 
    if (maVectorGraphicData)
    {
        maVectorGraphicData->setPageIndex(maSwapInfo.mnPageIndex);
    }
}
 
namespace
{
 
std::optional<VectorGraphicDataType> lclConvertToVectorGraphicType(GfxLink const & rLink)
{
    switch(rLink.GetType())
    {
        case GfxLinkType::NativePdf:
            return VectorGraphicDataType::Pdf;
 
        case GfxLinkType::NativeWmf:
            if (rLink.IsEMF())
                return VectorGraphicDataType::Emf;
            else
                return VectorGraphicDataType::Wmf;
 
        case GfxLinkType::NativeSvg:
            return VectorGraphicDataType::Svg;
 
        default:
            break;
    }
    return std::optional<VectorGraphicDataType>();
}
 
} // end namespace
 
bool ImpGraphic::swapIn()
{
    if (!isSwappedOut())
        return false;
 
    bool bReturn = false;
 
    if (mbPrepared)
    {
        Graphic aGraphic;
        if (!mpGfxLink->LoadNative(aGraphic, getPageNumber()))
            return false;
 
        updateFromLoadedGraphic(aGraphic.ImplGetImpGraphic());
 
        resetLastUsed();
        bReturn = true;
    }
    else if (mpGfxLink && mpGfxLink->IsNative())
    {
        std::optional<VectorGraphicDataType> oType = lclConvertToVectorGraphicType(*mpGfxLink);
        if (oType)
        {
            maVectorGraphicData = vcl::loadVectorGraphic(mpGfxLink->getDataContainer(), *oType);
 
            // Set to 0, to force recalculation
            mnSizeBytes = 0;
            mnChecksum = 0;
 
            restoreFromSwapInfo();
 
            mbSwapOut = false;
        }
        else
        {
            Graphic aGraphic;
            if (!mpGfxLink->LoadNative(aGraphic))
                return false;
 
            ImpGraphic* pImpGraphic = aGraphic.ImplGetImpGraphic();
            if (meType != pImpGraphic->meType)
                return false;
 
            updateFromLoadedGraphic(pImpGraphic);
        }
 
        resetLastUsed();
        bReturn = true;
    }
    else
    {
        SvStream* pStream = nullptr;
 
        if (mpSwapFile)
            pStream = mpSwapFile->getStream();
 
        if (pStream)
        {
            pStream->SetVersion(SOFFICE_FILEFORMAT_50);
            pStream->SetCompressMode(SvStreamCompressFlags::NATIVE);
            pStream->SetBufferSize(GRAPHIC_STREAMBUFSIZE);
            pStream->Seek(STREAM_SEEK_TO_BEGIN);
 
            bReturn = swapInFromStream(*pStream);
 
            restoreFromSwapInfo();
 
            setOriginURL(mpSwapFile->getOriginURL());
 
            mpSwapFile.reset();
        }
    }
 
    if (bReturn)
    {
        swappedIn(getSizeBytes());
    }
 
    return bReturn;
}
 
bool ImpGraphic::swapInFromStream(SvStream& rStream)
{
    bool bRet = false;
 
    if (rStream.GetError())
        return false;
 
    clearGraphics();
    mnSizeBytes = 0;
    mnChecksum = 0;
 
    bRet = swapInContent(rStream);
 
    if (!bRet)
    {
        //throw away swapfile, etc.
        clear();
    }
 
    mbSwapOut = false;
 
    return bRet;
}
 
bool ImpGraphic::swapInGraphic(SvStream& rStream)
{
    bool bReturn = false;
 
    if (rStream.GetError())
        return bReturn;
 
    if (meType == GraphicType::Bitmap)
    {
        sal_Int32 nContentType = -1;
        rStream.ReadInt32(nContentType);
        if (nContentType < 0)
            return false;
 
        auto eContentType = static_cast<GraphicContentType>(nContentType);
 
        switch (eContentType)
        {
            case GraphicContentType::Bitmap:
            {
                Bitmap aBitmap;
                ReadDIBBitmapEx(aBitmap, rStream);
                if (!rStream.GetError())
                {
                    mpBitmapContainer = std::make_shared<BitmapContainer>(aBitmap);
                    bReturn = true;
                }
            }
            break;
 
            case GraphicContentType::Animation:
            {
                Animation aAnimation;
                ReadAnimation(rStream, aAnimation);
                if (!rStream.GetError())
                {
                    mpAnimationContainer = std::make_shared<AnimationContainer>(aAnimation);
                    maCachedBitmap = mpAnimationContainer->maAnimation.GetBitmap();
                    bReturn = true;
                }
            }
            break;
 
            case GraphicContentType::Vector:
            {
                // try to stream in Svg defining data (length, byte array and evtl. path)
                // See below (operator<<) for more information
                sal_uInt32 nMagic;
                rStream.ReadUInt32(nMagic);
 
                if (constSvgMagic == nMagic || constWmfMagic == nMagic || constEmfMagic == nMagic || constPdfMagic == nMagic)
                {
                    sal_uInt32 nVectorGraphicDataSize(0);
                    rStream.ReadUInt32(nVectorGraphicDataSize);
 
                    if (nVectorGraphicDataSize)
                    {
                        BinaryDataContainer aDataContainer(rStream, nVectorGraphicDataSize);
 
                        if (rStream.GetError())
                            return false;
 
                        VectorGraphicDataType aDataType;
 
                        switch (nMagic)
                        {
                            case constSvgMagic:
                                aDataType = VectorGraphicDataType::Svg;
                                break;
                            case constWmfMagic:
                                aDataType = VectorGraphicDataType::Wmf;
                                break;
                            case constEmfMagic:
                                aDataType = VectorGraphicDataType::Emf;
                                break;
                            case constPdfMagic:
                                aDataType = VectorGraphicDataType::Pdf;
                                break;
                            default:
                                return false;
                        }
 
                        auto aVectorGraphicDataPtr = std::make_shared<VectorGraphicData>(aDataContainer, aDataType);
 
                        if (!rStream.GetError())
                        {
                            maVectorGraphicData = std::move(aVectorGraphicDataPtr);
                            bReturn = true;
                        }
                    }
                }
            }
            break;
        }
    }
    else if (meType == GraphicType::GdiMetafile)
    {
        GDIMetaFile aMetaFile;
        SvmReader aReader(rStream);
        aReader.Read(aMetaFile);
        if (!rStream.GetError())
        {
            maMetaFile = aMetaFile;
            bReturn = true;
        }
    }
    return bReturn;
}
 
void ImpGraphic::setGfxLink(const std::shared_ptr<GfxLink>& rGfxLink)
{
    ensureAvailable();
 
    mpGfxLink = rGfxLink;
}
 
const std::shared_ptr<GfxLink> & ImpGraphic::getSharedGfxLink() const
{
    return mpGfxLink;
}
 
GfxLink ImpGraphic::getGfxLink() const
{
    ensureAvailable();
 
    return( mpGfxLink ? *mpGfxLink : GfxLink() );
}
 
bool ImpGraphic::isGfxLink() const
{
    return ( bool(mpGfxLink) );
}
 
BitmapChecksum ImpGraphic::getChecksum() const
{
    if (mnChecksum != 0)
        return mnChecksum;
 
    ensureAvailable();
 
    switch (meType)
    {
        case GraphicType::NONE:
        case GraphicType::Default:
            break;
 
        case GraphicType::Bitmap:
        {
            if (maVectorGraphicData)
                mnChecksum = maVectorGraphicData->GetChecksum();
            else if (mpAnimationContainer)
                mnChecksum = mpAnimationContainer->getChecksum();
            else if (mpBitmapContainer)
                mnChecksum = mpBitmapContainer->getChecksum();
        }
        break;
 
        case GraphicType::GdiMetafile:
        {
            mnChecksum = SvmWriter::GetChecksum(maMetaFile);
        }
        break;
    }
    return mnChecksum;
}
 
sal_Int32 ImpGraphic::getPageNumber() const
{
    if (isSwappedOut())
        return maSwapInfo.mnPageIndex;
 
    if (maVectorGraphicData)
        return maVectorGraphicData->getPageIndex();
    return -1;
}
 
bool ImpGraphic::canReduceMemory() const
{
    return !isSwappedOut();
}
 
bool ImpGraphic::reduceMemory()
{
    return swapOut();
}
 
std::chrono::high_resolution_clock::time_point ImpGraphic::getLastUsed() const
{
    return maLastUsed;
}
 
void ImpGraphic::resetLastUsed() const
{
    maLastUsed = std::chrono::high_resolution_clock::now();
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.