Files
libreoffice/vcl/source/gdi/impgraph.cxx
Miklos Vajna 0098563895 tdf#128632 vcl image lazy-load: take unit into account when setting size
It makes no sense to set the size of an image based on the swap info
when the swap info unit and the actually loaded image's unit doesn't
match.

Converting the size would be also an option, but let's wait for the
first case when a custom size is actually needed for mismatching units.

Change-Id: I96b5c237f0be5587bb2f938faf3c69fa0e1d4a5c
Reviewed-on: https://gerrit.libreoffice.org/83122
Tested-by: Jenkins
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
2019-11-19 09:04:03 +01:00

2062 lines
62 KiB
C++

/* -*- 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 <comphelper/processfactory.hxx>
#include <tools/fract.hxx>
#include <tools/vcompat.hxx>
#include <tools/urlobj.hxx>
#include <tools/stream.hxx>
#include <ucbhelper/content.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/ucb/CommandAbortedException.hpp>
#include <com/sun/star/ucb/ContentCreationException.hpp>
#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' )
const sal_uInt32 nPdfMagic((sal_uInt32('p') << 24) | (sal_uInt32('d') << 16) | (sal_uInt32('f') << 8) | sal_uInt32('0'));
using namespace com::sun::star;
struct ImpSwapFile
{
INetURLObject aSwapURL;
OUString maOriginURL;
~ImpSwapFile();
};
class ReaderData
{
public:
Size maPreviewSize;
};
GraphicReader::GraphicReader()
{
}
GraphicReader::~GraphicReader()
{
}
void GraphicReader::DisablePreviewMode()
{
if( mpReaderData )
mpReaderData->maPreviewSize = Size( 0, 0 );
}
void GraphicReader::SetPreviewSize( const Size& rSize )
{
if( !mpReaderData )
mpReaderData.reset( new ReaderData );
mpReaderData->maPreviewSize = rSize;
}
Size GraphicReader::GetPreviewSize() const
{
Size aSize( 0, 0 );
if( mpReaderData )
aSize = mpReaderData->maPreviewSize;
return aSize;
}
GraphicID::GraphicID(ImpGraphic const & rGraphic)
{
rGraphic.ensureAvailable();
mnID1 = static_cast<sal_uLong>(rGraphic.ImplGetType()) << 28;
mnID2 = mnID3 = mnID4 = 0;
if (rGraphic.ImplGetType() == GraphicType::Bitmap)
{
if (rGraphic.getVectorGraphicData().get())
{
const VectorGraphicDataPtr& rVectorGraphicDataPtr = rGraphic.getVectorGraphicData();
const basegfx::B2DRange& rRange = rVectorGraphicDataPtr->getRange();
mnID1 |= rVectorGraphicDataPtr->getVectorGraphicDataArrayLength();
mnID2 = basegfx::fround(rRange.getWidth());
mnID3 = basegfx::fround(rRange.getHeight());
mnID4 = vcl_get_checksum(0, rVectorGraphicDataPtr->getVectorGraphicDataArray().getConstArray(), rVectorGraphicDataPtr->getVectorGraphicDataArrayLength());
}
else if (rGraphic.hasPdfData())
{
std::shared_ptr<std::vector<sal_Int8>> pPdfData = rGraphic.getPdfData();
const BitmapEx& rBmpEx = rGraphic.ImplGetBitmapExRef();
mnID1 |= (rGraphic.mnPageNumber & 0x0fffffff);
mnID2 = rBmpEx.GetSizePixel().Width();
mnID3 = rBmpEx.GetSizePixel().Height();
mnID4 = vcl_get_checksum(0, pPdfData->data(), pPdfData->size());
}
else if (rGraphic.ImplIsAnimated())
{
const Animation aAnimation(rGraphic.ImplGetAnimation());
mnID1 |= ( aAnimation.Count() & 0x0fffffff );
mnID2 = aAnimation.GetDisplaySizePixel().Width();
mnID3 = aAnimation.GetDisplaySizePixel().Height();
mnID4 = rGraphic.ImplGetChecksum();
}
else
{
const BitmapEx aBmpEx(rGraphic.ImplGetBitmapEx(GraphicConversionParameters()));
mnID1 |= ( ( ( static_cast<sal_uLong>(aBmpEx.GetTransparentType()) << 8 ) | ( aBmpEx.IsAlpha() ? 1 : 0 ) ) & 0x0fffffff );
mnID2 = aBmpEx.GetSizePixel().Width();
mnID3 = aBmpEx.GetSizePixel().Height();
mnID4 = rGraphic.ImplGetChecksum();
}
}
else if (rGraphic.ImplGetType() == GraphicType::GdiMetafile)
{
const GDIMetaFile& rMtf = rGraphic.ImplGetGDIMetaFile();
mnID1 |= ( rMtf.GetActionSize() & 0x0fffffff );
mnID2 = rMtf.GetPrefSize().Width();
mnID3 = rMtf.GetPrefSize().Height();
mnID4 = rGraphic.ImplGetChecksum();
}
}
OString GraphicID::getIDString() const
{
static const char aHexData[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
sal_Int32 nShift, nIndex = 0;
sal_Int32 nLen = 24 + (2 * BITMAP_CHECKSUM_SIZE);
OStringBuffer aHexStr(nLen);
aHexStr.setLength(nLen);
for( nShift = 28; nShift >= 0; nShift -= 4 )
aHexStr[nIndex++] = aHexData[ ( mnID1 >> static_cast<sal_uInt32>(nShift) ) & 0xf ];
for( nShift = 28; nShift >= 0; nShift -= 4 )
aHexStr[nIndex++] = aHexData[ ( mnID2 >> static_cast<sal_uInt32>(nShift) ) & 0xf ];
for( nShift = 28; nShift >= 0; nShift -= 4 )
aHexStr[nIndex++] = aHexData[ ( mnID3 >> static_cast<sal_uInt32>(nShift) ) & 0xf ];
for( nShift = ( 8 * BITMAP_CHECKSUM_SIZE ) - 4; nShift >= 0; nShift -= 4 )
aHexStr[nIndex++] = aHexData[ ( mnID4 >> static_cast<sal_uInt32>(nShift) ) & 0xf ];
return aHexStr.makeStringAndClear();
}
ImpGraphic::ImpGraphic() :
meType ( GraphicType::NONE ),
mnSizeBytes ( 0 ),
mbSwapOut ( false ),
mbDummyContext ( false ),
maLastUsed (std::chrono::high_resolution_clock::now()),
mbPrepared ( false ),
mnPageNumber(-1)
{
}
ImpGraphic::ImpGraphic(const ImpGraphic& rImpGraphic)
: maMetaFile(rImpGraphic.maMetaFile)
, maEx(rImpGraphic.maEx)
, 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)
, mpPdfData(rImpGraphic.mpPdfData)
, maGraphicExternalLink(rImpGraphic.maGraphicExternalLink)
, maLastUsed (std::chrono::high_resolution_clock::now())
, mbPrepared (rImpGraphic.mbPrepared)
, mnPageNumber(rImpGraphic.mnPageNumber)
{
if( rImpGraphic.mpAnimation )
{
mpAnimation = std::make_unique<Animation>( *rImpGraphic.mpAnimation );
maEx = mpAnimation->GetBitmapEx();
}
}
ImpGraphic::ImpGraphic(ImpGraphic&& rImpGraphic) noexcept
: maMetaFile(std::move(rImpGraphic.maMetaFile))
, maEx(std::move(rImpGraphic.maEx))
, 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))
, mpPdfData(std::move(rImpGraphic.mpPdfData))
, maGraphicExternalLink(rImpGraphic.maGraphicExternalLink)
, maLastUsed (std::chrono::high_resolution_clock::now())
, mbPrepared (rImpGraphic.mbPrepared)
, mnPageNumber(rImpGraphic.mnPageNumber)
{
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),
mnPageNumber(-1)
{
}
ImpGraphic::ImpGraphic( const Bitmap& rBitmap ) :
maEx ( rBitmap ),
meType ( !rBitmap.IsEmpty() ? GraphicType::Bitmap : GraphicType::NONE ),
mnSizeBytes ( 0 ),
mbSwapOut ( false ),
mbDummyContext ( false ),
maLastUsed (std::chrono::high_resolution_clock::now()),
mbPrepared (false),
mnPageNumber(-1)
{
}
ImpGraphic::ImpGraphic( const BitmapEx& rBitmapEx ) :
maEx ( rBitmapEx ),
meType ( !rBitmapEx.IsEmpty() ? GraphicType::Bitmap : GraphicType::NONE ),
mnSizeBytes ( 0 ),
mbSwapOut ( false ),
mbDummyContext ( false ),
maLastUsed (std::chrono::high_resolution_clock::now()),
mbPrepared (false),
mnPageNumber(-1)
{
}
ImpGraphic::ImpGraphic(const VectorGraphicDataPtr& rVectorGraphicDataPtr)
: meType( rVectorGraphicDataPtr.get() ? GraphicType::Bitmap : GraphicType::NONE ),
mnSizeBytes( 0 ),
mbSwapOut( false ),
mbDummyContext ( false ),
maVectorGraphicData(rVectorGraphicDataPtr),
maLastUsed (std::chrono::high_resolution_clock::now()),
mbPrepared (false),
mnPageNumber(-1)
{
}
ImpGraphic::ImpGraphic( const Animation& rAnimation ) :
maEx ( 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),
mnPageNumber(-1)
{
}
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),
mnPageNumber(-1)
{
}
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;
mnPageNumber = rImpGraphic.mnPageNumber;
maGraphicExternalLink = rImpGraphic.maGraphicExternalLink;
mpAnimation.reset();
if ( rImpGraphic.mpAnimation )
{
mpAnimation = std::make_unique<Animation>( *rImpGraphic.mpAnimation );
maEx = mpAnimation->GetBitmapEx();
}
else
{
maEx = rImpGraphic.maEx;
}
mbSwapOut = rImpGraphic.mbSwapOut;
mpSwapFile = rImpGraphic.mpSwapFile;
mbPrepared = rImpGraphic.mbPrepared;
mpGfxLink = rImpGraphic.mpGfxLink;
maVectorGraphicData = rImpGraphic.maVectorGraphicData;
mpPdfData = rImpGraphic.mpPdfData;
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;
mnPageNumber = rImpGraphic.mnPageNumber;
mpAnimation = std::move(rImpGraphic.mpAnimation);
maEx = std::move(rImpGraphic.maEx);
mbSwapOut = rImpGraphic.mbSwapOut;
mpSwapFile = std::move(rImpGraphic.mpSwapFile);
mpGfxLink = std::move(rImpGraphic.mpGfxLink);
maVectorGraphicData = std::move(rImpGraphic.maVectorGraphicData);
mpPdfData = std::move(rImpGraphic.mpPdfData);
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);
rImpGraphic.mnPageNumber = -1;
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.get())
{
if(maVectorGraphicData == rImpGraphic.maVectorGraphicData)
{
// equal instances
bRet = true;
}
else if(rImpGraphic.maVectorGraphicData)
{
// equal content
bRet = (*maVectorGraphicData) == (*rImpGraphic.maVectorGraphicData);
}
}
else if (mpPdfData && !mpPdfData->empty())
{
bRet = (rImpGraphic.mpPdfData && *mpPdfData == *rImpGraphic.mpPdfData);
}
else if( mpAnimation )
{
if( rImpGraphic.mpAnimation && ( *rImpGraphic.mpAnimation == *mpAnimation ) )
bRet = true;
}
else if( !rImpGraphic.mpAnimation && ( rImpGraphic.maEx == maEx ) )
{
bRet = true;
}
}
break;
default:
break;
}
}
return bRet;
}
const VectorGraphicDataPtr& ImpGraphic::getVectorGraphicData() const
{
ensureAvailable();
return maVectorGraphicData;
}
void ImpGraphic::setPdfData(const std::shared_ptr<std::vector<sal_Int8>>& rPdfData)
{
ensureAvailable();
mpPdfData = rPdfData;
}
const std::shared_ptr<std::vector<sal_Int8>> & ImpGraphic::getPdfData() const
{
ensureAvailable();
return mpPdfData;
}
void ImpGraphic::ImplCreateSwapInfo()
{
if (!ImplIsSwapOut())
{
maSwapInfo.maPrefMapMode = ImplGetPrefMapMode();
maSwapInfo.maPrefSize = ImplGetPrefSize();
maSwapInfo.mbIsAnimated = ImplIsAnimated();
maSwapInfo.mbIsEPS = ImplIsEPS();
maSwapInfo.mbIsTransparent = ImplIsTransparent();
maSwapInfo.mbIsAlpha = ImplIsAlpha();
maSwapInfo.mnAnimationLoopCount = ImplGetAnimationLoopCount();
}
}
void ImpGraphic::ImplClearGraphics()
{
maEx.Clear();
maMetaFile.Clear();
mpAnimation.reset();
mpGfxLink.reset();
maVectorGraphicData.reset();
mpPdfData.reset();
}
ImpSwapFile::~ImpSwapFile()
{
try
{
::ucbhelper::Content aCnt( aSwapURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ),
css::uno::Reference< css::ucb::XCommandEnvironment >(),
comphelper::getProcessComponentContext() );
aCnt.executeCommand( "delete", css::uno::makeAny( true ) );
}
catch( const css::ucb::ContentCreationException& )
{
}
catch( const css::uno::RuntimeException& )
{
}
catch( const css::ucb::CommandAbortedException& )
{
}
catch( const css::uno::Exception& )
{
}
}
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.get())
{
bRet = mpAnimation ? mpAnimation->IsTransparent() : maEx.IsTransparent();
}
return bRet;
}
bool ImpGraphic::ImplIsAlpha() const
{
bool bRet(false);
if (mbSwapOut)
{
bRet = maSwapInfo.mbIsAlpha;
}
else if (maVectorGraphicData.get())
{
bRet = true;
}
else if (meType == GraphicType::Bitmap)
{
bRet = (nullptr == mpAnimation && maEx.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();
}
Bitmap ImpGraphic::ImplGetBitmap(const GraphicConversionParameters& rParameters) const
{
Bitmap aRetBmp;
ensureAvailable();
if( meType == GraphicType::Bitmap )
{
if(maVectorGraphicData.get() && maEx.IsEmpty())
{
// use maEx as local buffer for rendered svg
const_cast< ImpGraphic* >(this)->maEx = maVectorGraphicData->getReplacement();
}
const BitmapEx& rRetBmpEx = ( mpAnimation ? mpAnimation->GetBitmapEx() : maEx );
const Color aReplaceColor( COL_WHITE );
aRetBmp = rRetBmpEx.GetBitmap( &aReplaceColor );
if(rParameters.getSizePixel().Width() || rParameters.getSizePixel().Height())
aRetBmp.Scale(rParameters.getSizePixel());
}
else if( ( meType != GraphicType::Default ) && ImplIsSupportedGraphic() )
{
if(maEx.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 maEx as local buffer for rendered metafile
const_cast< ImpGraphic* >(this)->maEx = aVDev->GetBitmapEx( Point(), aVDev->GetOutputSizePixel() );
}
}
aRetBmp = maEx.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.get() && maEx.IsEmpty())
{
// use maEx as local buffer for rendered svg
const_cast< ImpGraphic* >(this)->maEx = maVectorGraphicData->getReplacement();
}
aRetBmpEx = ( mpAnimation ? mpAnimation->GetBitmapEx() : maEx );
if(rParameters.getSizePixel().Width() || rParameters.getSizePixel().Height())
{
aRetBmpEx.Scale(
rParameters.getSizePixel(),
BmpScaleFlag::Fast);
}
}
else if( ( meType != GraphicType::Default ) && ImplIsSupportedGraphic() )
{
if(maEx.IsEmpty())
{
const ImpGraphic aMonoMask( maMetaFile.GetMonochromeMtf( COL_BLACK ) );
// use maEx as local buffer for rendered metafile
const_cast< ImpGraphic* >(this)->maEx = BitmapEx(ImplGetBitmap(rParameters), aMonoMask.ImplGetBitmap(rParameters));
}
aRetBmpEx = maEx;
}
return aRetBmpEx;
}
Animation ImpGraphic::ImplGetAnimation() const
{
Animation aAnimation;
ensureAvailable();
if( mpAnimation )
aAnimation = *mpAnimation;
return aAnimation;
}
const BitmapEx& ImpGraphic::ImplGetBitmapExRef() const
{
ensureAvailable();
return maEx;
}
const GDIMetaFile& ImpGraphic::ImplGetGDIMetaFile() const
{
ensureAvailable();
if (!maMetaFile.GetActionSize()
&& maVectorGraphicData.get()
&& (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.get() && !maEx)
{
// use maEx as local buffer for rendered svg
pThat->maEx = maVectorGraphicData->getReplacement();
}
// #123983# directly create a metafile with the same PrefSize and PrefMapMode
// the bitmap has, this will be an always correct metafile
if(maEx.IsTransparent())
{
pThat->maMetaFile.AddAction(new MetaBmpExScaleAction(Point(), maEx.GetPrefSize(), maEx));
}
else
{
pThat->maMetaFile.AddAction(new MetaBmpScaleAction(Point(), maEx.GetPrefSize(), maEx.GetBitmap()));
}
pThat->maMetaFile.Stop();
pThat->maMetaFile.WindStart();
pThat->maMetaFile.SetPrefSize(maEx.GetPrefSize());
pThat->maMetaFile.SetPrefMapMode(maEx.GetPrefMapMode());
}
return maMetaFile;
}
Size ImpGraphic::ImplGetSizePixel() const
{
Size aSize;
if (ImplIsSwapOut())
aSize = maSwapInfo.maSizePixel;
else
aSize = ImplGetBitmapEx(GraphicConversionParameters()).GetSizePixel();
return aSize;
}
Size ImpGraphic::ImplGetPrefSize() const
{
Size aSize;
if (ImplIsSwapOut())
{
aSize = maSwapInfo.maPrefSize;
}
else
{
switch( meType )
{
case GraphicType::NONE:
case GraphicType::Default:
break;
case GraphicType::Bitmap:
{
if(maVectorGraphicData.get() && maEx.IsEmpty())
{
// svg not yet buffered in maEx, return size derived from range
const basegfx::B2DRange& rRange = maVectorGraphicData->getRange();
aSize = Size(basegfx::fround(rRange.getWidth()), basegfx::fround(rRange.getHeight()));
}
else
{
aSize = maEx.GetPrefSize();
if( !aSize.Width() || !aSize.Height() )
{
aSize = maEx.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.get() && maEx.IsEmpty())
{
// use maEx as local buffer for rendered svg
maEx = maVectorGraphicData->getReplacement();
}
// #108077# Push through pref size to animation object,
// will be lost on copy otherwise
if( ImplIsAnimated() )
{
const_cast< BitmapEx& >(mpAnimation->GetBitmapEx()).SetPrefSize( rPrefSize );
}
maEx.SetPrefSize( rPrefSize );
}
break;
default:
{
if( ImplIsSupportedGraphic() )
maMetaFile.SetPrefSize( rPrefSize );
}
break;
}
}
MapMode ImpGraphic::ImplGetPrefMapMode() const
{
MapMode aMapMode;
if (ImplIsSwapOut())
{
aMapMode = maSwapInfo.maPrefMapMode;
}
else
{
switch( meType )
{
case GraphicType::NONE:
case GraphicType::Default:
break;
case GraphicType::Bitmap:
{
if(maVectorGraphicData.get() && maEx.IsEmpty())
{
// svg not yet buffered in maEx, return default PrefMapMode
aMapMode = MapMode(MapUnit::Map100thMM);
}
else
{
const Size aSize( maEx.GetPrefSize() );
if ( aSize.Width() && aSize.Height() )
aMapMode = maEx.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.get())
{
// ignore for Vector Graphic Data. If this is really used (except the grfcache)
// it can be extended by using maEx as buffer for maVectorGraphicData->getReplacement()
}
else
{
// #108077# Push through pref mapmode to animation object,
// will be lost on copy otherwise
if( ImplIsAnimated() )
{
const_cast< BitmapEx& >(mpAnimation->GetBitmapEx()).SetPrefMapMode( rPrefMapMode );
}
maEx.SetPrefMapMode( rPrefMapMode );
}
}
break;
default:
{
if( ImplIsSupportedGraphic() )
maMetaFile.SetPrefMapMode( rPrefMapMode );
}
break;
}
}
sal_uLong ImpGraphic::ImplGetSizeBytes() const
{
if( 0 == mnSizeBytes )
{
if (mbPrepared)
ensureAvailable();
if( meType == GraphicType::Bitmap )
{
if(maVectorGraphicData.get())
{
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() : maEx.GetSizeBytes();
}
}
else if( meType == GraphicType::GdiMetafile )
{
mnSizeBytes = maMetaFile.GetSizeBytes();
}
}
return mnSizeBytes;
}
void ImpGraphic::ImplDraw( OutputDevice* pOutDev, const Point& rDestPt ) const
{
ensureAvailable();
if( ImplIsSupportedGraphic() && !ImplIsSwapOut() )
{
switch( meType )
{
case GraphicType::Default:
break;
case GraphicType::Bitmap:
{
if(maVectorGraphicData.get() && !maEx)
{
// use maEx as local buffer for rendered svg
const_cast< ImpGraphic* >(this)->maEx = maVectorGraphicData->getReplacement();
}
if ( mpAnimation )
{
mpAnimation->Draw( pOutDev, rDestPt );
}
else
{
maEx.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() && !ImplIsSwapOut() )
{
switch( meType )
{
case GraphicType::Default:
break;
case GraphicType::Bitmap:
{
if(maVectorGraphicData.get() && maEx.IsEmpty())
{
// use maEx as local buffer for rendered svg
const_cast< ImpGraphic* >(this)->maEx = maVectorGraphicData->getReplacement();
}
if( mpAnimation )
{
mpAnimation->Draw( pOutDev, rDestPt, rDestSize );
}
else
{
maEx.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() && !ImplIsSwapOut() && mpAnimation )
mpAnimation->Start( pOutDev, rDestPt, rDestSize, nExtraData, pFirstFrameOutDev );
}
void ImpGraphic::ImplStopAnimation( OutputDevice* pOutDev, long nExtraData )
{
ensureAvailable();
if( ImplIsSupportedGraphic() && !ImplIsSwapOut() && 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
std::unique_ptr<VersionCompat> pCompat( new VersionCompat( 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.get() && maEx.IsEmpty())
{
// use maEx as local buffer for rendered svg
maEx = maVectorGraphicData->getReplacement();
}
maEx.SetSizePixel(aSize);
if( aMapMode != MapMode() )
{
maEx.SetPrefMapMode( aMapMode );
maEx.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 )
{
bool bRet = false;
ensureAvailable();
if( ( meType != GraphicType::NONE ) && ( meType != GraphicType::Default ) && !ImplIsSwapOut() )
{
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
std::unique_ptr<VersionCompat> pCompat( new VersionCompat( 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() );
}
// 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::ImplSwapOut()
{
bool bRet = false;
if( !ImplIsSwapOut() )
{
::utl::TempFile aTempFile;
const INetURLObject aTmpURL( aTempFile.GetURL() );
if( !aTmpURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ).isEmpty() )
{
std::unique_ptr<SvStream> xOStm;
try
{
xOStm = ::utl::UcbStreamHelper::CreateStream( aTmpURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::READWRITE | StreamMode::SHARE_DENYWRITE );
}
catch( const css::uno::Exception& )
{
}
if( xOStm )
{
xOStm->SetVersion( SOFFICE_FILEFORMAT_50 );
xOStm->SetCompressMode( SvStreamCompressFlags::NATIVE );
bRet = ImplSwapOut( xOStm.get() );
if( bRet )
{
mpSwapFile = std::make_unique<ImpSwapFile>();
mpSwapFile->aSwapURL = aTmpURL;
mpSwapFile->maOriginURL = getOriginURL();
}
else
{
xOStm.reset();
try
{
::ucbhelper::Content aCnt( aTmpURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ),
css::uno::Reference< css::ucb::XCommandEnvironment >(),
comphelper::getProcessComponentContext() );
aCnt.executeCommand( "delete", css::uno::makeAny( true ) );
}
catch( const css::ucb::ContentCreationException& )
{
}
catch( const css::uno::RuntimeException& )
{
}
catch( const css::ucb::CommandAbortedException& )
{
}
catch( const css::uno::Exception& )
{
}
}
}
}
}
if (bRet)
vcl::graphic::Manager::get().swappedOut(this);
return bRet;
}
bool ImpGraphic::ImplSwapOut( SvStream* xOStm )
{
bool bRet = false;
if( xOStm )
{
xOStm->SetBufferSize( GRAPHIC_STREAMBUFSIZE );
if( !xOStm->GetError() && ImplWriteEmbedded( *xOStm ) )
{
xOStm->Flush();
if( !xOStm->GetError() )
{
ImplCreateSwapInfo();
ImplClearGraphics();
bRet = mbSwapOut = true;
}
}
}
else
{
SAL_WARN("vcl.gdi", "Graphic SwapOut: No stream for swap out!");
}
return bRet;
}
bool ImpGraphic::ensureAvailable() const
{
auto pThis = const_cast<ImpGraphic*>(this);
if (ImplIsSwapOut())
return pThis->ImplSwapIn();
pThis->maLastUsed = std::chrono::high_resolution_clock::now();
return true;
}
bool ImpGraphic::loadPrepared()
{
Graphic aGraphic;
if (mpGfxLink->LoadNative(aGraphic))
{
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;
}
return false;
}
bool ImpGraphic::ImplSwapIn()
{
bool bRet = false;
if (!ImplIsSwapOut())
return bRet;
if (mbPrepared)
{
bRet = loadPrepared();
}
else
{
OUString aSwapURL;
if( mpSwapFile )
aSwapURL = mpSwapFile->aSwapURL.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 = ImplSwapIn( xIStm.get() );
xIStm.reset();
if (mpSwapFile)
setOriginURL(mpSwapFile->maOriginURL);
mpSwapFile.reset();
}
}
}
if (bRet)
vcl::graphic::Manager::get().swappedIn(this);
return bRet;
}
bool ImpGraphic::ImplSwapIn( SvStream* xIStm )
{
bool bRet = false;
if( xIStm )
{
xIStm->SetBufferSize( GRAPHIC_STREAMBUFSIZE );
if( !xIStm->GetError() )
{
//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() && !ImplIsSwapOut() )
{
switch( meType )
{
case GraphicType::Default:
break;
case GraphicType::Bitmap:
{
if(maVectorGraphicData)
nRet = maVectorGraphicData->GetChecksum();
else if (mpPdfData && !mpPdfData->empty())
// Include the PDF data in the checksum, so a metafile with
// and without PDF data is considered to be different.
nRet = vcl_get_checksum(nRet, mpPdfData->data(), mpPdfData->size());
else if( mpAnimation )
nRet = mpAnimation->GetChecksum();
else
nRet = maEx.GetChecksum();
}
break;
default:
nRet = maMetaFile.GetChecksum();
break;
}
}
mnChecksum = nRet;
return nRet;
}
bool ImpGraphic::ImplExportNative( SvStream& rOStm ) const
{
bool bResult = false;
ensureAvailable();
if( !rOStm.GetError() )
{
if( !ImplIsSwapOut() )
{
if( mpGfxLink && mpGfxLink->IsNative() )
bResult = mpGfxLink->ExportNative( rOStm );
else
{
WriteImpGraphic( rOStm, *this );
bResult = ( rOStm.GetError() == ERRCODE_NONE );
}
}
else
rOStm.SetError( SVSTREAM_GENERALERROR );
}
return bResult;
}
static std::map<BitmapChecksum, std::shared_ptr<std::vector<sal_Int8>>> sPdfDataCache;
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
std::unique_ptr<VersionCompat> pCompat(new VersionCompat( rIStm, StreamMode::READ ));
pCompat.reset(); // destructor writes stuff into the header
ReadGfxLink( rIStm, 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
const sal_uInt32 nSvgMagic((sal_uInt32('s') << 24) | (sal_uInt32('v') << 16) | (sal_uInt32('g') << 8) | sal_uInt32('0'));
const sal_uInt32 nWmfMagic((sal_uInt32('w') << 24) | (sal_uInt32('m') << 16) | (sal_uInt32('f') << 8) | sal_uInt32('0'));
const sal_uInt32 nEmfMagic((sal_uInt32('e') << 24) | (sal_uInt32('m') << 16) | (sal_uInt32('f') << 8) | sal_uInt32('0'));
sal_uInt32 nMagic;
rIStm.Seek(nStmPos1);
rIStm.ResetError();
rIStm.ReadUInt32( nMagic );
if (nSvgMagic == nMagic || nWmfMagic == nMagic || nEmfMagic == 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 (nWmfMagic == nMagic)
{
aDataType = VectorGraphicDataType::Wmf;
}
else if (nEmfMagic == nMagic)
{
aDataType = VectorGraphicDataType::Emf;
}
VectorGraphicDataPtr aVectorGraphicDataPtr(new VectorGraphicData(aNewData, aPath, aDataType));
rImpGraphic = aVectorGraphicDataPtr;
}
}
}
else if (nMagic == nPdfMagic)
{
// Stream in PDF data.
BitmapChecksum nPdfId = 0;
rIStm.ReadUInt64(nPdfId);
rImpGraphic.mnPageNumber = 0;
rIStm.ReadInt32(rImpGraphic.mnPageNumber);
auto it = sPdfDataCache.find(nPdfId);
assert(it != sPdfDataCache.end());
rImpGraphic.mpPdfData = it->second;
Bitmap aBitmap;
rImpGraphic.maEx = aBitmap;
std::vector<Bitmap> aBitmaps;
if (vcl::RenderPDFBitmaps(rImpGraphic.mpPdfData->data(), rImpGraphic.mpPdfData->size(), aBitmaps, rImpGraphic.mnPageNumber, 1) == 1)
rImpGraphic.maEx = aBitmaps[0];
rImpGraphic.meType = GraphicType::Bitmap;
}
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.ImplIsSwapOut())
{
rOStm.SetError( SVSTREAM_GENERALERROR );
return;
}
if( ( rOStm.GetVersion() >= SOFFICE_FILEFORMAT_50 ) &&
( rOStm.GetCompressMode() & SvStreamCompressFlags::NATIVE ) &&
rImpGraphic.mpGfxLink && rImpGraphic.mpGfxLink->IsNative() &&
!rImpGraphic.hasPdfData())
{
// native format
rOStm.WriteUInt32( NATIVE_FORMAT_50 );
// write compat info
std::unique_ptr<VersionCompat> pCompat(new VersionCompat( rOStm, StreamMode::WRITE, 1 ));
pCompat.reset(); // destructor writes stuff into the header
rImpGraphic.mpGfxLink->SetPrefMapMode( rImpGraphic.ImplGetPrefMapMode() );
rImpGraphic.mpGfxLink->SetPrefSize( rImpGraphic.ImplGetPrefSize() );
WriteGfxLink( rOStm, *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().get())
{
// 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:
{
const sal_uInt32 nWmfMagic((sal_uInt32('w') << 24) | (sal_uInt32('m') << 16) | (sal_uInt32('f') << 8) | sal_uInt32('0'));
rOStm.WriteUInt32(nWmfMagic);
break;
}
case VectorGraphicDataType::Emf:
{
const sal_uInt32 nEmfMagic((sal_uInt32('e') << 24) | (sal_uInt32('m') << 16) | (sal_uInt32('f') << 8) | sal_uInt32('0'));
rOStm.WriteUInt32(nEmfMagic);
break;
}
default: // case VectorGraphicDataType::Svg:
{
const sal_uInt32 nSvgMagic((sal_uInt32('s') << 24) | (sal_uInt32('v') << 16) | (sal_uInt32('g') << 8) | sal_uInt32('0'));
rOStm.WriteUInt32(nSvgMagic);
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.hasPdfData())
{
BitmapChecksum nPdfId = vcl_get_checksum(0, rImpGraphic.mpPdfData->data(), rImpGraphic.mpPdfData->size());
if (sPdfDataCache.find(nPdfId) == sPdfDataCache.end())
sPdfDataCache.emplace(nPdfId, rImpGraphic.mpPdfData);
// Stream out PDF data.
rOStm.WriteUInt32(nPdfMagic);
rOStm.WriteUInt64(nPdfId);
rOStm.WriteInt32(rImpGraphic.mnPageNumber);
}
else if( rImpGraphic.ImplIsAnimated())
{
WriteAnimation( rOStm, *rImpGraphic.mpAnimation );
}
else
{
WriteDIBBitmapEx(rImpGraphic.maEx, rOStm);
}
}
break;
default:
{
if( rImpGraphic.ImplIsSupportedGraphic() )
WriteGDIMetaFile( rOStm, rImpGraphic.maMetaFile );
}
break;
}
rOStm.SetEndian( nOldFormat );
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */