diff options
Diffstat (limited to 'vcl/source/graphic')
-rw-r--r-- | vcl/source/graphic/BinaryDataContainer.cxx | 189 | ||||
-rw-r--r-- | vcl/source/graphic/BinaryDataContainerTools.cxx | 28 | ||||
-rw-r--r-- | vcl/source/graphic/GraphicID.cxx | 101 | ||||
-rw-r--r-- | vcl/source/graphic/GraphicLoader.cxx | 44 | ||||
-rw-r--r-- | vcl/source/graphic/GraphicObject.cxx | 935 | ||||
-rw-r--r-- | vcl/source/graphic/GraphicObject2.cxx | 503 | ||||
-rw-r--r-- | vcl/source/graphic/GraphicReader.cxx | 29 | ||||
-rw-r--r-- | vcl/source/graphic/Manager.cxx | 317 | ||||
-rw-r--r-- | vcl/source/graphic/UnoBinaryDataContainer.cxx | 22 | ||||
-rw-r--r-- | vcl/source/graphic/UnoGraphic.cxx | 261 | ||||
-rw-r--r-- | vcl/source/graphic/UnoGraphicDescriptor.cxx | 421 | ||||
-rw-r--r-- | vcl/source/graphic/UnoGraphicMapper.cxx | 87 | ||||
-rw-r--r-- | vcl/source/graphic/UnoGraphicObject.cxx | 101 | ||||
-rw-r--r-- | vcl/source/graphic/UnoGraphicProvider.cxx | 837 | ||||
-rw-r--r-- | vcl/source/graphic/VectorGraphicLoader.cxx | 26 | ||||
-rw-r--r-- | vcl/source/graphic/VectorGraphicSearch.cxx | 307 |
16 files changed, 4208 insertions, 0 deletions
diff --git a/vcl/source/graphic/BinaryDataContainer.cxx b/vcl/source/graphic/BinaryDataContainer.cxx new file mode 100644 index 0000000000..89ae5eb8da --- /dev/null +++ b/vcl/source/graphic/BinaryDataContainer.cxx @@ -0,0 +1,189 @@ +/* -*- 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/. + * + */ + +#include <vcl/BinaryDataContainer.hxx> +#include <o3tl/hash_combine.hxx> +#include <unotools/tempfile.hxx> +#include <comphelper/lok.hxx> +#include <comphelper/seqstream.hxx> +#include <sal/log.hxx> + +struct BinaryDataContainer::Impl +{ + // temp file to store the data out of RAM if necessary + std::unique_ptr<utl::TempFileFast> mpFile; + // the binary data + std::shared_ptr<std::vector<sal_uInt8>> mpData; + + Impl(SvStream& stream, size_t size) { readData(stream, size); } + + /// Populate mpData from the stream + void readData(SvStream& stream, size_t size) + { + auto pData = std::make_shared<std::vector<sal_uInt8>>(size); + if (stream.ReadBytes(pData->data(), pData->size()) == size) + mpData = std::move(pData); + } + + /// ensure the data is in-RAM + void ensureSwappedIn() + { + if (mpData || !mpFile) + return; + + auto pStream = mpFile->GetStream(StreamMode::READ); + pStream->Seek(0); + readData(*pStream, pStream->remainingSize()); + + // Horrifying data loss ... + SAL_WARN_IF(pStream->GetError(), "vcl", + "Inconsistent system - failed to swap image back in"); + } + + void swapOut() + { + if (mpFile) + { + // we already have it swapped out. + mpData.reset(); + return; + } + + if (!mpData || mpData->empty()) + return; + + mpFile.reset(new utl::TempFileFast()); + auto pStream = mpFile->GetStream(StreamMode::READWRITE); + + pStream->WriteBytes(mpData->data(), mpData->size()); + + mpData.reset(); + } +}; + +BinaryDataContainer::BinaryDataContainer(SvStream& stream, size_t size) + : mpImpl(new Impl(stream, size)) +{ +} + +size_t BinaryDataContainer::calculateHash() const +{ + size_t nSeed = 0; + if (mpImpl && mpImpl->mpData && !mpImpl->mpData->empty()) + { + o3tl::hash_combine(nSeed, getSize()); + for (sal_uInt8 const& rByte : *mpImpl->mpData) + o3tl::hash_combine(nSeed, rByte); + } + return nSeed; +} + +css::uno::Sequence<sal_Int8> BinaryDataContainer::getCopyAsByteSequence() const +{ + if (isEmpty()) + return css::uno::Sequence<sal_Int8>(); + assert(mpImpl); + + css::uno::Sequence<sal_Int8> aData(getSize()); + + std::copy(mpImpl->mpData->cbegin(), mpImpl->mpData->cend(), aData.getArray()); + + return aData; +} + +namespace +{ +/* + * Hold a reference on the internal state in case we swap out + * and free the vector while someone holds an SvStream pointer. + */ +class ReferencedMemoryStream : public SvMemoryStream +{ + std::shared_ptr<std::vector<sal_uInt8>> mpData; + +public: + ReferencedMemoryStream(const std::shared_ptr<std::vector<sal_uInt8>>& pData) + : SvMemoryStream(pData->data(), pData->size(), StreamMode::READ) + , mpData(pData) + { + } +}; + +class ReferencedXInputStream : public comphelper::MemoryInputStream +{ + std::shared_ptr<std::vector<sal_uInt8>> mpData; + +public: + ReferencedXInputStream(const std::shared_ptr<std::vector<sal_uInt8>>& pData) + : comphelper::MemoryInputStream(reinterpret_cast<const sal_Int8*>(pData->data()), + pData->size()) + , mpData(pData) + { + } +}; +} + +std::shared_ptr<SvStream> BinaryDataContainer::getAsStream() +{ + ensureSwappedIn(); // TODO: transfer in streamed chunks + return std::make_shared<ReferencedMemoryStream>(mpImpl->mpData); +} + +css::uno::Reference<css::io::XInputStream> BinaryDataContainer::getAsXInputStream() +{ + ensureSwappedIn(); // TODO: transfer in streamed chunks + return new ReferencedXInputStream(mpImpl->mpData); +} + +std::size_t BinaryDataContainer::writeToStream(SvStream& rStream) const +{ + ensureSwappedIn(); // TODO: transfer in streamed chunks + return rStream.WriteBytes(getData(), getSize()); +} + +size_t BinaryDataContainer::getSize() const +{ + ensureSwappedIn(); + return mpImpl && mpImpl->mpData ? mpImpl->mpData->size() : 0; +} + +size_t BinaryDataContainer::getSizeBytes() const +{ + return mpImpl && mpImpl->mpData ? mpImpl->mpData->size() : 0; +} + +bool BinaryDataContainer::isEmpty() const +{ + ensureSwappedIn(); + return !mpImpl || !mpImpl->mpData || mpImpl->mpData->empty(); +} + +const sal_uInt8* BinaryDataContainer::getData() const +{ + ensureSwappedIn(); + return mpImpl && mpImpl->mpData ? mpImpl->mpData->data() : nullptr; +} + +void BinaryDataContainer::ensureSwappedIn() const +{ + if (mpImpl) + mpImpl->ensureSwappedIn(); +} + +void BinaryDataContainer::swapOut() const +{ + // Only bother reducing memory footprint in kit mode - for mobile/online etc. + if (!mpImpl || !comphelper::LibreOfficeKit::isActive()) + return; + + mpImpl->swapOut(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/graphic/BinaryDataContainerTools.cxx b/vcl/source/graphic/BinaryDataContainerTools.cxx new file mode 100644 index 0000000000..c643803313 --- /dev/null +++ b/vcl/source/graphic/BinaryDataContainerTools.cxx @@ -0,0 +1,28 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#include <vcl/BinaryDataContainerTools.hxx> +#include <graphic/UnoBinaryDataContainer.hxx> + +namespace vcl +{ +BinaryDataContainer convertUnoBinaryDataContainer( + const css::uno::Reference<css::util::XBinaryDataContainer>& rxBinaryDataContainer) +{ + BinaryDataContainer aBinaryDataContainer; + UnoBinaryDataContainer* pUnoBinaryDataContainer + = dynamic_cast<UnoBinaryDataContainer*>(rxBinaryDataContainer.get()); + if (pUnoBinaryDataContainer) + aBinaryDataContainer = pUnoBinaryDataContainer->getBinaryDataContainer(); + return aBinaryDataContainer; +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/graphic/GraphicID.cxx b/vcl/source/graphic/GraphicID.cxx new file mode 100644 index 0000000000..d78acea530 --- /dev/null +++ b/vcl/source/graphic/GraphicID.cxx @@ -0,0 +1,101 @@ +/* -*- 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 <graphic/GraphicID.hxx> + +#include <impgraph.hxx> +#include <rtl/crc.h> +#include <rtl/strbuf.hxx> + +GraphicID::GraphicID(ImpGraphic const& rGraphic) +{ + rGraphic.ensureAvailable(); + + mnID1 = static_cast<sal_uLong>(rGraphic.getType()) << 28; + mnID2 = mnID3 = mnID4 = 0; + + if (rGraphic.getType() == GraphicType::Bitmap) + { + auto const& rVectorGraphicDataPtr = rGraphic.getVectorGraphicData(); + if (rVectorGraphicDataPtr) + { + const basegfx::B2DRange& rRange = rVectorGraphicDataPtr->getRange(); + + mnID1 |= rVectorGraphicDataPtr->getBinaryDataContainer().getSize(); + mnID2 = basegfx::fround(rRange.getWidth()); + mnID3 = basegfx::fround(rRange.getHeight()); + mnID4 = rtl_crc32(0, rVectorGraphicDataPtr->getBinaryDataContainer().getData(), + rVectorGraphicDataPtr->getBinaryDataContainer().getSize()); + } + else if (rGraphic.isAnimated()) + { + const Animation aAnimation(rGraphic.getAnimation()); + + mnID1 |= (aAnimation.Count() & 0x0fffffff); + mnID2 = aAnimation.GetDisplaySizePixel().Width(); + mnID3 = aAnimation.GetDisplaySizePixel().Height(); + mnID4 = rGraphic.getChecksum(); + } + else + { + const BitmapEx aBmpEx(rGraphic.getBitmapEx(GraphicConversionParameters())); + + mnID1 |= aBmpEx.IsAlpha() ? 1 : 0; + mnID2 = aBmpEx.GetSizePixel().Width(); + mnID3 = aBmpEx.GetSizePixel().Height(); + mnID4 = rGraphic.getChecksum(); + } + } + else if (rGraphic.getType() == GraphicType::GdiMetafile) + { + const GDIMetaFile& rMtf = rGraphic.getGDIMetaFile(); + + mnID1 |= (rMtf.GetActionSize() & 0x0fffffff); + mnID2 = rMtf.GetPrefSize().Width(); + mnID3 = rMtf.GetPrefSize().Height(); + mnID4 = rGraphic.getChecksum(); + } +} + +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(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/graphic/GraphicLoader.cxx b/vcl/source/graphic/GraphicLoader.cxx new file mode 100644 index 0000000000..63d35efb68 --- /dev/null +++ b/vcl/source/graphic/GraphicLoader.cxx @@ -0,0 +1,44 @@ +/* -*- 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/. + * + */ + +#include <vcl/GraphicLoader.hxx> + +#include <com/sun/star/awt/XWindow.hpp> +#include <unotools/ucbstreamhelper.hxx> +#include <vcl/graphicfilter.hxx> +#include <vcl/weld.hxx> + +using namespace css; + +namespace vcl::graphic +{ +Graphic loadFromURL(OUString const& rURL, weld::Window* pParentWin) +{ + Graphic aGraphic; + + std::unique_ptr<SvStream> pInputStream = utl::UcbStreamHelper::CreateStream( + rURL, StreamMode::READ, pParentWin ? pParentWin->GetXWindow() : nullptr); + + if (pInputStream) + { + GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter(); + + ErrCode nError + = rFilter.ImportGraphic(aGraphic, rURL, *pInputStream, GRFILTER_FORMAT_DONTKNOW, + nullptr, GraphicFilterImportFlags::NONE); + if (nError != ERRCODE_NONE || aGraphic.GetType() == GraphicType::NONE) + return Graphic(); + } + + return aGraphic; +} +} // end vcl::graphic + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/graphic/GraphicObject.cxx b/vcl/source/graphic/GraphicObject.cxx new file mode 100644 index 0000000000..7abf3b3d3b --- /dev/null +++ b/vcl/source/graphic/GraphicObject.cxx @@ -0,0 +1,935 @@ +/* -*- 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 <algorithm> + +#include <o3tl/string_view.hxx> +#include <osl/diagnose.h> +#include <tools/fract.hxx> +#include <tools/helpers.hxx> +#include <utility> +#include <vcl/svapp.hxx> +#include <vcl/metaact.hxx> +#include <vcl/GraphicObject.hxx> +#include <vcl/GraphicLoader.hxx> +#include <vcl/outdev.hxx> + +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> +#include <memory> + + +using namespace css; +using com::sun::star::uno::Reference; +using com::sun::star::uno::XInterface; +using com::sun::star::uno::UNO_QUERY; +using com::sun::star::uno::Sequence; +using com::sun::star::container::XNameContainer; +using com::sun::star::beans::XPropertySet; + +#define WATERMARK_LUM_OFFSET 50 +#define WATERMARK_CON_OFFSET -70 + +namespace vcl::graphic +{ + +void SearchForGraphics(uno::Reference<uno::XInterface> const & xInterface, + std::vector<uno::Reference<css::graphic::XGraphic>> & raGraphicList) +{ + uno::Reference<beans::XPropertySet> xPropertySet(xInterface, UNO_QUERY); + if (xPropertySet.is()) + { + if (xPropertySet->getPropertySetInfo()->hasPropertyByName("ImageURL")) + { + OUString sURL; + xPropertySet->getPropertyValue("ImageURL") >>= sURL; + if (!sURL.isEmpty() && !GraphicObject::isGraphicObjectUniqueIdURL(sURL)) + { + Graphic aGraphic = vcl::graphic::loadFromURL(sURL); + if (!aGraphic.IsNone()) + { + raGraphicList.push_back(aGraphic.GetXGraphic()); + } + } + } else if (xPropertySet->getPropertySetInfo()->hasPropertyByName("Graphic")) + { + uno::Reference<css::graphic::XGraphic> xGraphic; + xPropertySet->getPropertyValue("Graphic") >>= xGraphic; + if (xGraphic.is()) + { + raGraphicList.push_back(xGraphic); + } + } + } + Reference<XNameContainer> xContainer(xInterface, UNO_QUERY); + if (xContainer.is()) + { + const css::uno::Sequence<OUString> aElementNames = xContainer->getElementNames(); + for (OUString const & rName : aElementNames) + { + uno::Reference<XInterface> xInnerInterface; + xContainer->getByName(rName) >>= xInnerInterface; + SearchForGraphics(xInnerInterface, raGraphicList); + } + } +} + +} // end namespace vcl::graphic + +namespace +{ + +bool lclDrawObj(OutputDevice& rOut, const Point& rPt, const Size& rSz, + GraphicObject const & rObj, const GraphicAttr& rAttr) +{ + Point aPt( rPt ); + Size aSz( rSz ); + bool bRet = false; + + if( ( rObj.GetType() == GraphicType::Bitmap ) || ( rObj.GetType() == GraphicType::GdiMetafile ) ) + { + // simple output of transformed graphic + const Graphic aGraphic( rObj.GetTransformedGraphic( &rAttr ) ); + + if( aGraphic.IsSupportedGraphic() ) + { + const Degree10 nRot10 = rAttr.GetRotation() % 3600_deg10; + + if( nRot10 ) + { + tools::Polygon aPoly( tools::Rectangle( aPt, aSz ) ); + + aPoly.Rotate( aPt, nRot10 ); + const tools::Rectangle aRotBoundRect( aPoly.GetBoundRect() ); + aPt = aRotBoundRect.TopLeft(); + aSz = aRotBoundRect.GetSize(); + } + + aGraphic.Draw(rOut, aPt, aSz); + } + + bRet = true; + } + + return bRet; +} + +void lclImplAdjust( BitmapEx& rBmpEx, const GraphicAttr& rAttr, GraphicAdjustmentFlags nAdjustmentFlags ) +{ + GraphicAttr aAttr( rAttr ); + + if( ( nAdjustmentFlags & GraphicAdjustmentFlags::DRAWMODE ) && aAttr.IsSpecialDrawMode() ) + { + switch( aAttr.GetDrawMode() ) + { + case GraphicDrawMode::Mono: + rBmpEx.Convert( BmpConversion::N1BitThreshold ); + break; + + case GraphicDrawMode::Greys: + rBmpEx.Convert( BmpConversion::N8BitGreys ); + break; + + case GraphicDrawMode::Watermark: + { + aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET ); + aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET ); + } + break; + + default: + break; + } + } + + if( ( nAdjustmentFlags & GraphicAdjustmentFlags::COLORS ) && aAttr.IsAdjusted() ) + { + rBmpEx.Adjust( aAttr.GetLuminance(), aAttr.GetContrast(), + aAttr.GetChannelR(), aAttr.GetChannelG(), aAttr.GetChannelB(), + aAttr.GetGamma(), aAttr.IsInvert() ); + } + + if( ( nAdjustmentFlags & GraphicAdjustmentFlags::MIRROR ) && aAttr.IsMirrored() ) + { + rBmpEx.Mirror( aAttr.GetMirrorFlags() ); + } + + if( ( nAdjustmentFlags & GraphicAdjustmentFlags::ROTATE ) && aAttr.IsRotated() ) + { + rBmpEx.Rotate( aAttr.GetRotation(), COL_TRANSPARENT ); + } + + if( ( nAdjustmentFlags & GraphicAdjustmentFlags::TRANSPARENCY ) && aAttr.IsTransparent() ) + { + rBmpEx.AdjustTransparency(255 - aAttr.GetAlpha()); + } +} + +void lclImplAdjust( GDIMetaFile& rMtf, const GraphicAttr& rAttr, GraphicAdjustmentFlags nAdjustmentFlags ) +{ + GraphicAttr aAttr( rAttr ); + + if( ( nAdjustmentFlags & GraphicAdjustmentFlags::DRAWMODE ) && aAttr.IsSpecialDrawMode() ) + { + switch( aAttr.GetDrawMode() ) + { + case GraphicDrawMode::Mono: + rMtf.Convert( MtfConversion::N1BitThreshold ); + break; + + case GraphicDrawMode::Greys: + rMtf.Convert( MtfConversion::N8BitGreys ); + break; + + case GraphicDrawMode::Watermark: + { + aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET ); + aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET ); + } + break; + + default: + break; + } + } + + if( ( nAdjustmentFlags & GraphicAdjustmentFlags::COLORS ) && aAttr.IsAdjusted() ) + { + rMtf.Adjust( aAttr.GetLuminance(), aAttr.GetContrast(), + aAttr.GetChannelR(), aAttr.GetChannelG(), aAttr.GetChannelB(), + aAttr.GetGamma(), aAttr.IsInvert() ); + } + + if( ( nAdjustmentFlags & GraphicAdjustmentFlags::MIRROR ) && aAttr.IsMirrored() ) + { + rMtf.Mirror( aAttr.GetMirrorFlags() ); + } + + if( ( nAdjustmentFlags & GraphicAdjustmentFlags::ROTATE ) && aAttr.IsRotated() ) + { + rMtf.Rotate( aAttr.GetRotation() ); + } + + if( ( nAdjustmentFlags & GraphicAdjustmentFlags::TRANSPARENCY ) && aAttr.IsTransparent() ) + { + OSL_FAIL( "Missing implementation: Mtf-Transparency" ); + } +} + +void lclImplAdjust( Animation& rAnimation, const GraphicAttr& rAttr, GraphicAdjustmentFlags nAdjustmentFlags ) +{ + GraphicAttr aAttr( rAttr ); + + if( ( nAdjustmentFlags & GraphicAdjustmentFlags::DRAWMODE ) && aAttr.IsSpecialDrawMode() ) + { + switch( aAttr.GetDrawMode() ) + { + case GraphicDrawMode::Mono: + rAnimation.Convert( BmpConversion::N1BitThreshold ); + break; + + case GraphicDrawMode::Greys: + rAnimation.Convert( BmpConversion::N8BitGreys ); + break; + + case GraphicDrawMode::Watermark: + { + aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET ); + aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET ); + } + break; + + default: + break; + } + } + + if( ( nAdjustmentFlags & GraphicAdjustmentFlags::COLORS ) && aAttr.IsAdjusted() ) + { + rAnimation.Adjust( aAttr.GetLuminance(), aAttr.GetContrast(), + aAttr.GetChannelR(), aAttr.GetChannelG(), aAttr.GetChannelB(), + aAttr.GetGamma(), aAttr.IsInvert() ); + } + + if( ( nAdjustmentFlags & GraphicAdjustmentFlags::MIRROR ) && aAttr.IsMirrored() ) + { + rAnimation.Mirror( aAttr.GetMirrorFlags() ); + } + + if( ( nAdjustmentFlags & GraphicAdjustmentFlags::ROTATE ) && aAttr.IsRotated() ) + { + OSL_FAIL( "Missing implementation: Animation-Rotation" ); + } + + if( ( nAdjustmentFlags & GraphicAdjustmentFlags::TRANSPARENCY ) && aAttr.IsTransparent() ) + { + OSL_FAIL( "Missing implementation: Animation-Transparency" ); + } +} + +} // end anonymous namespace + +struct GrfSimpleCacheObj +{ + Graphic maGraphic; + GraphicAttr maAttr; + + GrfSimpleCacheObj( Graphic aGraphic, const GraphicAttr& rAttr ) : + maGraphic(std::move( aGraphic )), maAttr( rAttr ) {} +}; + +GraphicObject::GraphicObject() +{ +} + +GraphicObject::GraphicObject(Graphic aGraphic) + : maGraphic(std::move(aGraphic)) +{ +} + +GraphicObject::GraphicObject(const GraphicObject& rGraphicObj) + : maGraphic(rGraphicObj.GetGraphic()) + , maAttr(rGraphicObj.maAttr) + , maUserData(rGraphicObj.maUserData) +{ +} + +GraphicObject::~GraphicObject() +{ +} + +GraphicType GraphicObject::GetType() const +{ + return maGraphic.GetType(); +} + +Size GraphicObject::GetPrefSize() const +{ + return maGraphic.GetPrefSize(); +} + +MapMode GraphicObject::GetPrefMapMode() const +{ + return maGraphic.GetPrefMapMode(); +} + +bool GraphicObject::IsTransparent() const +{ + return maGraphic.IsTransparent(); +} + +bool GraphicObject::IsAnimated() const +{ + return maGraphic.IsAnimated(); +} + +bool GraphicObject::IsEPS() const +{ + return maGraphic.IsEPS(); +} + +bool GraphicObject::ImplGetCropParams(const OutputDevice& rOut, Point& rPt, Size& rSz, const GraphicAttr* pAttr, + tools::PolyPolygon& rClipPolyPoly, bool& bRectClipRegion) const +{ + bool bRet = false; + + if( GetType() != GraphicType::NONE ) + { + tools::Polygon aClipPoly( tools::Rectangle( rPt, rSz ) ); + const Degree10 nRot10 = pAttr->GetRotation() % 3600_deg10; + const Point aOldOrigin( rPt ); + const MapMode aMap100( MapUnit::Map100thMM ); + Size aSize100; + tools::Long nTotalWidth, nTotalHeight; + + if( nRot10 ) + { + aClipPoly.Rotate( rPt, nRot10 ); + bRectClipRegion = false; + } + else + bRectClipRegion = true; + + rClipPolyPoly = tools::PolyPolygon(aClipPoly); + + if (maGraphic.GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel) + aSize100 = Application::GetDefaultDevice()->PixelToLogic( maGraphic.GetPrefSize(), aMap100 ); + else + { + MapMode m(maGraphic.GetPrefMapMode()); + aSize100 = rOut.LogicToLogic( maGraphic.GetPrefSize(), &m, &aMap100 ); + } + + nTotalWidth = aSize100.Width() - pAttr->GetLeftCrop() - pAttr->GetRightCrop(); + nTotalHeight = aSize100.Height() - pAttr->GetTopCrop() - pAttr->GetBottomCrop(); + + if( !aSize100.IsEmpty() && nTotalWidth > 0 && nTotalHeight > 0 ) + { + double fScale = static_cast<double>(aSize100.Width()) / nTotalWidth; + const tools::Long nNewLeft = -FRound( ( ( pAttr->GetMirrorFlags() & BmpMirrorFlags::Horizontal ) ? pAttr->GetRightCrop() : pAttr->GetLeftCrop() ) * fScale ); + const tools::Long nNewRight = nNewLeft + FRound( aSize100.Width() * fScale ) - 1; + + fScale = static_cast<double>(rSz.Width()) / aSize100.Width(); + rPt.AdjustX(FRound( nNewLeft * fScale ) ); + rSz.setWidth( FRound( ( nNewRight - nNewLeft + 1 ) * fScale ) ); + + fScale = static_cast<double>(aSize100.Height()) / nTotalHeight; + const tools::Long nNewTop = -FRound( ( ( pAttr->GetMirrorFlags() & BmpMirrorFlags::Vertical ) ? pAttr->GetBottomCrop() : pAttr->GetTopCrop() ) * fScale ); + const tools::Long nNewBottom = nNewTop + FRound( aSize100.Height() * fScale ) - 1; + + fScale = static_cast<double>(rSz.Height()) / aSize100.Height(); + rPt.AdjustY(FRound( nNewTop * fScale ) ); + rSz.setHeight( FRound( ( nNewBottom - nNewTop + 1 ) * fScale ) ); + + if( nRot10 ) + { + tools::Polygon aOriginPoly( 1 ); + + aOriginPoly[ 0 ] = rPt; + aOriginPoly.Rotate( aOldOrigin, nRot10 ); + rPt = aOriginPoly[ 0 ]; + } + + bRet = true; + } + } + + return bRet; +} + +GraphicObject& GraphicObject::operator=( const GraphicObject& rGraphicObj ) +{ + if( &rGraphicObj != this ) + { + mxSimpleCache.reset(); + maGraphic = rGraphicObj.GetGraphic(); + maAttr = rGraphicObj.maAttr; + maUserData = rGraphicObj.maUserData; + } + + return *this; +} + +bool GraphicObject::operator==( const GraphicObject& rGraphicObj ) const +{ + return rGraphicObj.maGraphic == maGraphic + && rGraphicObj.maAttr == maAttr; +} + +OString GraphicObject::GetUniqueID() const +{ + return GetGraphic().getUniqueID(); +} + +void GraphicObject::SetAttr( const GraphicAttr& rAttr ) +{ + maAttr = rAttr; + + if (mxSimpleCache && (mxSimpleCache->maAttr != rAttr)) + mxSimpleCache.reset(); +} + +void GraphicObject::SetUserData() +{ + maUserData.clear(); +} + +void GraphicObject::SetUserData( const OUString& rUserData ) +{ + maUserData = rUserData; +} + +bool GraphicObject::Draw(OutputDevice& rOut, const Point& rPt, const Size& rSz, + const GraphicAttr* pAttr) const +{ + GraphicAttr aAttr( pAttr ? *pAttr : GetAttr() ); + Point aPt( rPt ); + Size aSz( rSz ); + const DrawModeFlags nOldDrawMode = rOut.GetDrawMode(); + bool bCropped = aAttr.IsCropped(); + bool bRet; + + rOut.SetDrawMode(nOldDrawMode & ~DrawModeFlags( DrawModeFlags::SettingsLine | DrawModeFlags::SettingsFill | DrawModeFlags::SettingsText | DrawModeFlags::SettingsGradient )); + + // mirrored horizontally + if( aSz.Width() < 0 ) + { + aPt.AdjustX(aSz.Width() + 1 ); + aSz.setWidth( -aSz.Width() ); + aAttr.SetMirrorFlags( aAttr.GetMirrorFlags() ^ BmpMirrorFlags::Horizontal ); + } + + // mirrored vertically + if( aSz.Height() < 0 ) + { + aPt.AdjustY(aSz.Height() + 1 ); + aSz.setHeight( -aSz.Height() ); + aAttr.SetMirrorFlags( aAttr.GetMirrorFlags() ^ BmpMirrorFlags::Vertical ); + } + + if( bCropped ) + { + tools::PolyPolygon aClipPolyPoly; + bool bRectClip; + const bool bCrop = ImplGetCropParams(rOut, aPt, aSz, &aAttr, aClipPolyPoly, bRectClip); + + rOut.Push(vcl::PushFlags::CLIPREGION); + + if( bCrop ) + { + if( bRectClip ) + { + // #i29534# Store crop rect for later forwarding to + // PDF writer + tools::Rectangle aCropRect = aClipPolyPoly.GetBoundRect(); + rOut.IntersectClipRegion(aCropRect); + } + else + { + rOut.IntersectClipRegion(vcl::Region(aClipPolyPoly)); + } + } + } + + bRet = lclDrawObj(rOut, aPt, aSz, *this, aAttr); + + if( bCropped ) + rOut.Pop(); + + rOut.SetDrawMode( nOldDrawMode ); + + return bRet; +} + +void GraphicObject::DrawTiled(OutputDevice& rOut, const tools::Rectangle& rArea, const Size& rSize, + const Size& rOffset, int nTileCacheSize1D) +{ + if (rSize.IsEmpty()) + return; + + const MapMode aOutMapMode(rOut.GetMapMode()); + // #106258# Clamp size to 1 for zero values. This is okay, since + // logical size of zero is handled above already + const Size aOutTileSize( ::std::max( tools::Long(1), rOut.LogicToPixel( rSize, aOutMapMode ).Width() ), + ::std::max( tools::Long(1), rOut.LogicToPixel( rSize, aOutMapMode ).Height() ) ); + + //#i69780 clip final tile size to a sane max size + while ((static_cast<sal_Int64>(rSize.Width()) * nTileCacheSize1D) > SAL_MAX_UINT16) + nTileCacheSize1D /= 2; + while ((static_cast<sal_Int64>(rSize.Height()) * nTileCacheSize1D) > SAL_MAX_UINT16) + nTileCacheSize1D /= 2; + + ImplDrawTiled(rOut, rArea, aOutTileSize, rOffset, nullptr, nTileCacheSize1D); +} + +bool GraphicObject::StartAnimation(OutputDevice& rOut, const Point& rPt, const Size& rSz, + tools::Long nRendererId, + OutputDevice* pFirstFrameOutDev) +{ + bool bRet = false; + + GetGraphic(); + + const GraphicAttr aAttr( GetAttr() ); + + if (IsAnimated()) + { + Point aPt( rPt ); + Size aSz( rSz ); + bool bCropped = aAttr.IsCropped(); + + if( bCropped ) + { + tools::PolyPolygon aClipPolyPoly; + bool bRectClip; + const bool bCrop = ImplGetCropParams(rOut, aPt, aSz, &aAttr, aClipPolyPoly, bRectClip); + + rOut.Push(vcl::PushFlags::CLIPREGION); + + if( bCrop ) + { + if( bRectClip ) + rOut.IntersectClipRegion(aClipPolyPoly.GetBoundRect()); + else + rOut.IntersectClipRegion(vcl::Region(aClipPolyPoly)); + } + } + + if (!mxSimpleCache || (mxSimpleCache->maAttr != aAttr) || pFirstFrameOutDev) + { + mxSimpleCache.reset(new GrfSimpleCacheObj(GetTransformedGraphic(&aAttr), aAttr)); + mxSimpleCache->maGraphic.SetAnimationNotifyHdl(GetGraphic().GetAnimationNotifyHdl()); + } + + mxSimpleCache->maGraphic.StartAnimation(rOut, aPt, aSz, nRendererId, pFirstFrameOutDev); + + if( bCropped ) + rOut.Pop(); + + bRet = true; + } + else + bRet = Draw(rOut, rPt, rSz, &aAttr); + + return bRet; +} + +void GraphicObject::StopAnimation( const OutputDevice* pOut, tools::Long nRendererId ) +{ + if (mxSimpleCache) + mxSimpleCache->maGraphic.StopAnimation(pOut, nRendererId); +} + +const Graphic& GraphicObject::GetGraphic() const +{ + return maGraphic; +} + +void GraphicObject::SetGraphic( const Graphic& rGraphic) +{ + maGraphic = rGraphic; +} + +Graphic GraphicObject::GetTransformedGraphic( const Size& rDestSize, const MapMode& rDestMap, const GraphicAttr& rAttr ) const +{ + // #104550# Extracted from svx/source/svdraw/svdograf.cxx + Graphic aTransGraphic( GetGraphic() ); + const GraphicType eType = GetType(); + const Size aSrcSize( aTransGraphic.GetPrefSize() ); + + // #104115# Convert the crop margins to graphic object mapmode + const MapMode aMapGraph( aTransGraphic.GetPrefMapMode() ); + const MapMode aMap100( MapUnit::Map100thMM ); + + Size aCropLeftTop; + Size aCropRightBottom; + + if( GraphicType::GdiMetafile == eType ) + { + GDIMetaFile aMtf( aTransGraphic.GetGDIMetaFile() ); + + if (aMapGraph.GetMapUnit() == MapUnit::MapPixel) + { + // crops are in 1/100th mm -> to aMapGraph -> to MapUnit::MapPixel + aCropLeftTop = Application::GetDefaultDevice()->LogicToPixel( + Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()), + aMap100); + aCropRightBottom = Application::GetDefaultDevice()->LogicToPixel( + Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()), + aMap100); + } + else + { + // crops are in GraphicObject units -> to aMapGraph + aCropLeftTop = OutputDevice::LogicToLogic( + Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()), + aMap100, + aMapGraph); + aCropRightBottom = OutputDevice::LogicToLogic( + Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()), + aMap100, + aMapGraph); + } + + // #104115# If the metafile is cropped, give it a special + // treatment: clip against the remaining area, scale up such + // that this area later fills the desired size, and move the + // origin to the upper left edge of that area. + if( rAttr.IsCropped() ) + { + const MapMode aMtfMapMode( aMtf.GetPrefMapMode() ); + + tools::Rectangle aClipRect( aMtfMapMode.GetOrigin().X() + aCropLeftTop.Width(), + aMtfMapMode.GetOrigin().Y() + aCropLeftTop.Height(), + aMtfMapMode.GetOrigin().X() + aSrcSize.Width() - aCropRightBottom.Width(), + aMtfMapMode.GetOrigin().Y() + aSrcSize.Height() - aCropRightBottom.Height() ); + + // #104115# To correctly crop rotated metafiles, clip by view rectangle + aMtf.AddAction( new MetaISectRectClipRegionAction( aClipRect ), 0 ); + + // #104115# To crop the metafile, scale larger than the output rectangle + aMtf.Scale( static_cast<double>(rDestSize.Width()) / (aSrcSize.Width() - aCropLeftTop.Width() - aCropRightBottom.Width()), + static_cast<double>(rDestSize.Height()) / (aSrcSize.Height() - aCropLeftTop.Height() - aCropRightBottom.Height()) ); + + // #104115# Adapt the pref size by hand (scale changes it + // proportionally, but we want it to be smaller than the + // former size, to crop the excess out) + aMtf.SetPrefSize( Size( static_cast<tools::Long>(static_cast<double>(rDestSize.Width()) * (1.0 + (aCropLeftTop.Width() + aCropRightBottom.Width()) / aSrcSize.Width()) + .5), + static_cast<tools::Long>(static_cast<double>(rDestSize.Height()) * (1.0 + (aCropLeftTop.Height() + aCropRightBottom.Height()) / aSrcSize.Height()) + .5) ) ); + + // #104115# Adapt the origin of the new mapmode, such that it + // is shifted to the place where the cropped output starts + Point aNewOrigin( static_cast<tools::Long>(static_cast<double>(aMtfMapMode.GetOrigin().X()) + rDestSize.Width() * aCropLeftTop.Width() / (aSrcSize.Width() - aCropLeftTop.Width() - aCropRightBottom.Width()) + .5), + static_cast<tools::Long>(static_cast<double>(aMtfMapMode.GetOrigin().Y()) + rDestSize.Height() * aCropLeftTop.Height() / (aSrcSize.Height() - aCropLeftTop.Height() - aCropRightBottom.Height()) + .5) ); + MapMode aNewMap( rDestMap ); + aNewMap.SetOrigin( OutputDevice::LogicToLogic(aNewOrigin, aMtfMapMode, rDestMap) ); + aMtf.SetPrefMapMode( aNewMap ); + } + else + { + aMtf.Scale( Fraction( rDestSize.Width(), aSrcSize.Width() ), Fraction( rDestSize.Height(), aSrcSize.Height() ) ); + aMtf.SetPrefMapMode( rDestMap ); + } + + aTransGraphic = aMtf; + } + else if( GraphicType::Bitmap == eType ) + { + BitmapEx aBitmapEx( aTransGraphic.GetBitmapEx() ); + tools::Rectangle aCropRect; + + // convert crops to pixel + if(rAttr.IsCropped()) + { + if (aMapGraph.GetMapUnit() == MapUnit::MapPixel) + { + // crops are in 1/100th mm -> to MapUnit::MapPixel + aCropLeftTop = Application::GetDefaultDevice()->LogicToPixel( + Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()), + aMap100); + aCropRightBottom = Application::GetDefaultDevice()->LogicToPixel( + Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()), + aMap100); + } + else + { + // crops are in GraphicObject units -> to MapUnit::MapPixel + aCropLeftTop = Application::GetDefaultDevice()->LogicToPixel( + Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()), + aMapGraph); + aCropRightBottom = Application::GetDefaultDevice()->LogicToPixel( + Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()), + aMapGraph); + } + + // convert from prefmapmode to pixel + Size aSrcSizePixel( + Application::GetDefaultDevice()->LogicToPixel( + aSrcSize, + aMapGraph)); + + if(rAttr.IsCropped() + && (aSrcSizePixel.Width() != aBitmapEx.GetSizePixel().Width() || aSrcSizePixel.Height() != aBitmapEx.GetSizePixel().Height()) + && aSrcSizePixel.Width()) + { + // the size in pixels calculated from Graphic's internal MapMode (aTransGraphic.GetPrefMapMode()) + // and its internal size (aTransGraphic.GetPrefSize()) is different from its real pixel size. + // This can be interpreted as this values to be set wrong, but needs to be corrected since e.g. + // existing cropping is calculated based on this logic values already. + // aBitmapEx.Scale(aSrcSizePixel); + + // another possibility is to adapt the values created so far with a factor; this + // will keep the original Bitmap untouched and thus quality will not change + // caution: convert to double first, else pretty big errors may occur + const double fFactorX(static_cast<double>(aBitmapEx.GetSizePixel().Width()) / aSrcSizePixel.Width()); + const double fFactorY(static_cast<double>(aBitmapEx.GetSizePixel().Height()) / aSrcSizePixel.Height()); + + aCropLeftTop.setWidth( basegfx::fround(aCropLeftTop.Width() * fFactorX) ); + aCropLeftTop.setHeight( basegfx::fround(aCropLeftTop.Height() * fFactorY) ); + aCropRightBottom.setWidth( basegfx::fround(aCropRightBottom.Width() * fFactorX) ); + aCropRightBottom.setHeight( basegfx::fround(aCropRightBottom.Height() * fFactorY) ); + + aSrcSizePixel = aBitmapEx.GetSizePixel(); + } + + // setup crop rectangle in pixel + aCropRect = tools::Rectangle( aCropLeftTop.Width(), aCropLeftTop.Height(), + aSrcSizePixel.Width() - aCropRightBottom.Width(), + aSrcSizePixel.Height() - aCropRightBottom.Height() ); + } + + // #105641# Also crop animations + if( aTransGraphic.IsAnimated() ) + { + Animation aAnim( aTransGraphic.GetAnimation() ); + + for( size_t nFrame=0; nFrame<aAnim.Count(); ++nFrame ) + { + AnimationFrame aAnimationFrame( aAnim.Get( nFrame ) ); + + if( !aCropRect.Contains( tools::Rectangle(aAnimationFrame.maPositionPixel, aAnimationFrame.maSizePixel) ) ) + { + // setup actual cropping (relative to frame position) + tools::Rectangle aCropRectRel( aCropRect ); + aCropRectRel.Move( -aAnimationFrame.maPositionPixel.X(), + -aAnimationFrame.maPositionPixel.Y() ); + + // cropping affects this frame, apply it then + // do _not_ apply enlargement, this is done below + ImplTransformBitmap( aAnimationFrame.maBitmapEx, rAttr, Size(), Size(), + aCropRectRel, rDestSize, false ); + + aAnim.Replace( aAnimationFrame, nFrame ); + } + // else: bitmap completely within crop area, + // i.e. nothing is cropped away + } + + // now, apply enlargement (if any) through global animation size + if( aCropLeftTop.Width() < 0 || + aCropLeftTop.Height() < 0 || + aCropRightBottom.Width() < 0 || + aCropRightBottom.Height() < 0 ) + { + Size aNewSize( aAnim.GetDisplaySizePixel() ); + aNewSize.AdjustWidth(aCropRightBottom.Width() < 0 ? -aCropRightBottom.Width() : 0 ); + aNewSize.AdjustWidth(aCropLeftTop.Width() < 0 ? -aCropLeftTop.Width() : 0 ); + aNewSize.AdjustHeight(aCropRightBottom.Height() < 0 ? -aCropRightBottom.Height() : 0 ); + aNewSize.AdjustHeight(aCropLeftTop.Height() < 0 ? -aCropLeftTop.Height() : 0 ); + aAnim.SetDisplaySizePixel( aNewSize ); + } + + // if topleft has changed, we must move all frames to the + // right and bottom, resp. + if( aCropLeftTop.Width() < 0 || + aCropLeftTop.Height() < 0 ) + { + Point aPosOffset( aCropLeftTop.Width() < 0 ? -aCropLeftTop.Width() : 0, + aCropLeftTop.Height() < 0 ? -aCropLeftTop.Height() : 0 ); + + for( size_t nFrame=0; nFrame<aAnim.Count(); ++nFrame ) + { + AnimationFrame aAnimationFrame( aAnim.Get( nFrame ) ); + + aAnimationFrame.maPositionPixel += aPosOffset; + + aAnim.Replace( aAnimationFrame, nFrame ); + } + } + + aTransGraphic = aAnim; + } + else + { + ImplTransformBitmap( aBitmapEx, rAttr, aCropLeftTop, aCropRightBottom, + aCropRect, rDestSize, true ); + + aTransGraphic = aBitmapEx; + } + + aTransGraphic.SetPrefSize( rDestSize ); + aTransGraphic.SetPrefMapMode( rDestMap ); + } + + GraphicObject aGrfObj( aTransGraphic ); + aTransGraphic = aGrfObj.GetTransformedGraphic( &rAttr ); + + return aTransGraphic; +} + +Graphic GraphicObject::GetTransformedGraphic( const GraphicAttr* pAttr ) const +{ + GetGraphic(); + + Graphic aGraphic; + GraphicAttr aAttr( pAttr ? *pAttr : GetAttr() ); + + if (maGraphic.IsSupportedGraphic()) + { + if( aAttr.IsSpecialDrawMode() || aAttr.IsAdjusted() || aAttr.IsMirrored() || aAttr.IsRotated() || aAttr.IsTransparent() ) + { + if( GetType() == GraphicType::Bitmap ) + { + if( IsAnimated() ) + { + Animation aAnimation( maGraphic.GetAnimation() ); + lclImplAdjust( aAnimation, aAttr, GraphicAdjustmentFlags::ALL ); + aAnimation.SetLoopCount(maGraphic.GetAnimationLoopCount()); + aGraphic = aAnimation; + } + else + { + BitmapEx aBmpEx( maGraphic.GetBitmapEx() ); + lclImplAdjust( aBmpEx, aAttr, GraphicAdjustmentFlags::ALL ); + aGraphic = aBmpEx; + } + } + else + { + GDIMetaFile aMtf( maGraphic.GetGDIMetaFile() ); + lclImplAdjust( aMtf, aAttr, GraphicAdjustmentFlags::ALL ); + aGraphic = aMtf; + } + } + else + { + if( ( GetType() == GraphicType::Bitmap ) && IsAnimated() ) + { + Animation aAnimation( maGraphic.GetAnimation() ); + aAnimation.SetLoopCount(maGraphic.GetAnimationLoopCount()); + aGraphic = aAnimation; + } + else + aGraphic = maGraphic; + } + } + + return aGraphic; +} + +bool GraphicObject::isGraphicObjectUniqueIdURL(std::u16string_view rURL) +{ + return o3tl::starts_with(rURL, u"vnd.sun.star.GraphicObject:"); +} + +// calculate scalings between real image size and logic object size. This +// is necessary since the crop values are relative to original bitmap size +basegfx::B2DVector GraphicObject::calculateCropScaling( + double fWidth, + double fHeight, + double fLeftCrop, + double fTopCrop, + double fRightCrop, + double fBottomCrop) const +{ + const MapMode aMapMode100thmm(MapUnit::Map100thMM); + Size aBitmapSize(GetPrefSize()); + double fFactorX(1.0); + double fFactorY(1.0); + + if(MapUnit::MapPixel == GetPrefMapMode().GetMapUnit()) + { + aBitmapSize = Application::GetDefaultDevice()->PixelToLogic(aBitmapSize, aMapMode100thmm); + } + else + { + aBitmapSize = OutputDevice::LogicToLogic(aBitmapSize, GetPrefMapMode(), aMapMode100thmm); + } + + const double fDivX(aBitmapSize.Width() - fLeftCrop - fRightCrop); + const double fDivY(aBitmapSize.Height() - fTopCrop - fBottomCrop); + + if(!basegfx::fTools::equalZero(fDivX)) + { + fFactorX = fabs(fWidth) / fDivX; + } + + if(!basegfx::fTools::equalZero(fDivY)) + { + fFactorY = fabs(fHeight) / fDivY; + } + + return basegfx::B2DVector(fFactorX,fFactorY); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/graphic/GraphicObject2.cxx b/vcl/source/graphic/GraphicObject2.cxx new file mode 100644 index 0000000000..a8dd186a3d --- /dev/null +++ b/vcl/source/graphic/GraphicObject2.cxx @@ -0,0 +1,503 @@ +/* -*- 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 <tools/gen.hxx> +#include <vcl/outdev.hxx> +#include <vcl/alpha.hxx> +#include <vcl/virdev.hxx> +#include <vcl/GraphicObject.hxx> +#include <memory> + +struct ImplTileInfo +{ + ImplTileInfo() : nTilesEmptyX(0), nTilesEmptyY(0) {} + + Point aTileTopLeft; // top, left position of the rendered tile + Point aNextTileTopLeft; // top, left position for next recursion + // level's tile + Size aTileSizePixel; // size of the generated tile (might + // differ from + // aNextTileTopLeft-aTileTopLeft, because + // this is nExponent*prevTileSize. The + // generated tile is always nExponent + // times the previous tile, such that it + // can be used in the next stage. The + // required area coverage is often + // less. The extraneous area covered is + // later overwritten by the next stage) + int nTilesEmptyX; // number of original tiles empty right of + // this tile. This counts from + // aNextTileTopLeft, i.e. the additional + // area covered by aTileSizePixel is not + // considered here. This is for + // unification purposes, as the iterative + // calculation of the next level's empty + // tiles has to be based on this value. + int nTilesEmptyY; // as above, for Y +}; + + +bool GraphicObject::ImplRenderTempTile( VirtualDevice& rVDev, + int nNumTilesX, int nNumTilesY, + const Size& rTileSizePixel, + const GraphicAttr* pAttr ) +{ + // how many tiles to generate per recursion step + const int nExponent = 2; + + // determine MSB factor + int nMSBFactor( 1 ); + while( nNumTilesX / nMSBFactor != 0 || + nNumTilesY / nMSBFactor != 0 ) + { + nMSBFactor *= nExponent; + } + + // one less + if(nMSBFactor > 1) + { + nMSBFactor /= nExponent; + } + ImplTileInfo aTileInfo; + + // #105229# Switch off mapping (converting to logic and back to + // pixel might cause roundoff errors) + bool bOldMap( rVDev.IsMapModeEnabled() ); + rVDev.EnableMapMode( false ); + + bool bRet( ImplRenderTileRecursive( rVDev, nExponent, nMSBFactor, nNumTilesX, nNumTilesY, + nNumTilesX, nNumTilesY, rTileSizePixel, pAttr, aTileInfo ) ); + + rVDev.EnableMapMode( bOldMap ); + + return bRet; +} + +// define for debug drawings +//#define DBG_TEST + +// see header comment. this works similar to base conversion of a +// number, i.e. if the exponent is 10, then the number for every tile +// size is given by the decimal place of the corresponding decimal +// representation. +bool GraphicObject::ImplRenderTileRecursive( VirtualDevice& rVDev, int nExponent, int nMSBFactor, + int nNumOrigTilesX, int nNumOrigTilesY, + int nRemainderTilesX, int nRemainderTilesY, + const Size& rTileSizePixel, const GraphicAttr* pAttr, + ImplTileInfo& rTileInfo ) +{ + // gets loaded with our tile bitmap + std::unique_ptr<GraphicObject> xTmpGraphic; + GraphicObject* pTileGraphic; + + // stores a flag that renders the zero'th tile position + // (i.e. (0,0)+rCurrPos) only if we're at the bottom of the + // recursion stack. All other position already have that tile + // rendered, because the lower levels painted their generated tile + // there. + bool bNoFirstTileDraw( false ); + + // what's left when we're done with our tile size + const int nNewRemainderX( nRemainderTilesX % nMSBFactor ); + const int nNewRemainderY( nRemainderTilesY % nMSBFactor ); + + // gets filled out from the recursive call with info of what's + // been generated + ImplTileInfo aTileInfo; + + // check for recursion's end condition: LSB place reached? + if( nMSBFactor == 1 ) + { + pTileGraphic = this; + + // set initial tile size -> orig size + aTileInfo.aTileSizePixel = rTileSizePixel; + aTileInfo.nTilesEmptyX = nNumOrigTilesX; + aTileInfo.nTilesEmptyY = nNumOrigTilesY; + } + else if( ImplRenderTileRecursive( rVDev, nExponent, nMSBFactor/nExponent, + nNumOrigTilesX, nNumOrigTilesY, + nNewRemainderX, nNewRemainderY, + rTileSizePixel, pAttr, aTileInfo ) ) + { + // extract generated tile -> see comment on the first loop below + BitmapEx aTileBitmap( rVDev.GetBitmap( aTileInfo.aTileTopLeft, aTileInfo.aTileSizePixel ) ); + + xTmpGraphic.reset(new GraphicObject(std::move(aTileBitmap))); + pTileGraphic = xTmpGraphic.get(); + + // fill stripes left over from upstream levels: + + // x0000 + // 0 + // 0 + // 0 + // 0 + + // where x denotes the place filled by our recursive predecessors + + // check whether we have to fill stripes here. Although not + // obvious, there is one case where we can skip this step: if + // the previous recursion level (the one who filled our + // aTileInfo) had zero area to fill, then there are no white + // stripes left, naturally. This happens if the digit + // associated to that level has a zero, and can be checked via + // aTileTopLeft==aNextTileTopLeft. + if( aTileInfo.aTileTopLeft != aTileInfo.aNextTileTopLeft ) + { + // now fill one row from aTileInfo.aNextTileTopLeft.X() all + // the way to the right + // current output position while drawing + Point aCurrPos(aTileInfo.aNextTileTopLeft.X(), aTileInfo.aTileTopLeft.Y()); + for (int nX=0; nX < aTileInfo.nTilesEmptyX; nX += nMSBFactor) + { + if (!pTileGraphic->Draw(rVDev, aCurrPos, aTileInfo.aTileSizePixel, pAttr)) + return false; + + aCurrPos.AdjustX(aTileInfo.aTileSizePixel.Width() ); + } + +#ifdef DBG_TEST +// rVDev.SetFillCOL_WHITE ); + rVDev.SetFillColor(); + rVDev.SetLineColor( Color( 255 * nExponent / nMSBFactor, 255 - 255 * nExponent / nMSBFactor, 128 - 255 * nExponent / nMSBFactor ) ); + rVDev.DrawEllipse( tools::Rectangle(aTileInfo.aNextTileTopLeft.X(), aTileInfo.aTileTopLeft.Y(), + aTileInfo.aNextTileTopLeft.X() - 1 + (aTileInfo.nTilesEmptyX/nMSBFactor)*aTileInfo.aTileSizePixel.Width(), + aTileInfo.aTileTopLeft.Y() + aTileInfo.aTileSizePixel.Height() - 1) ); +#endif + + // now fill one column from aTileInfo.aNextTileTopLeft.Y() all + // the way to the bottom + aCurrPos.setX( aTileInfo.aTileTopLeft.X() ); + aCurrPos.setY( aTileInfo.aNextTileTopLeft.Y() ); + for (int nY=0; nY < aTileInfo.nTilesEmptyY; nY += nMSBFactor) + { + if (!pTileGraphic->Draw(rVDev, aCurrPos, aTileInfo.aTileSizePixel, pAttr)) + return false; + + aCurrPos.AdjustY(aTileInfo.aTileSizePixel.Height() ); + } + +#ifdef DBG_TEST + rVDev.DrawEllipse( tools::Rectangle(aTileInfo.aTileTopLeft.X(), aTileInfo.aNextTileTopLeft.Y(), + aTileInfo.aTileTopLeft.X() + aTileInfo.aTileSizePixel.Width() - 1, + aTileInfo.aNextTileTopLeft.Y() - 1 + (aTileInfo.nTilesEmptyY/nMSBFactor)*aTileInfo.aTileSizePixel.Height()) ); +#endif + } + else + { + // Thought that aTileInfo.aNextTileTopLeft tile has always + // been drawn already, but that's wrong: typically, + // _parts_ of that tile have been drawn, since the + // previous level generated the tile there. But when + // aTileInfo.aNextTileTopLeft!=aTileInfo.aTileTopLeft, the + // difference between these two values is missing in the + // lower right corner of this first tile. So, can do that + // only here. + bNoFirstTileDraw = true; + } + } + else + { + return false; + } + + // calc number of original tiles in our drawing area without + // remainder + nRemainderTilesX -= nNewRemainderX; + nRemainderTilesY -= nNewRemainderY; + + // fill tile info for calling method + rTileInfo.aTileTopLeft = aTileInfo.aNextTileTopLeft; + rTileInfo.aNextTileTopLeft = Point( rTileInfo.aTileTopLeft.X() + rTileSizePixel.Width()*nRemainderTilesX, + rTileInfo.aTileTopLeft.Y() + rTileSizePixel.Height()*nRemainderTilesY ); + rTileInfo.aTileSizePixel = Size( rTileSizePixel.Width()*nMSBFactor*nExponent, + rTileSizePixel.Height()*nMSBFactor*nExponent ); + rTileInfo.nTilesEmptyX = aTileInfo.nTilesEmptyX - nRemainderTilesX; + rTileInfo.nTilesEmptyY = aTileInfo.nTilesEmptyY - nRemainderTilesY; + + // init output position + Point aCurrPos = aTileInfo.aNextTileTopLeft; + + // fill our drawing area. Fill possibly more, to create the next + // bigger tile size -> see bitmap extraction above. This does no + // harm, since everything right or below our actual area is + // overdrawn by our caller. Just in case we're in the last level, + // we don't draw beyond the right or bottom border. + for (int nY=0; nY < aTileInfo.nTilesEmptyY && nY < nExponent*nMSBFactor; nY += nMSBFactor) + { + aCurrPos.setX( aTileInfo.aNextTileTopLeft.X() ); + + for (int nX=0; nX < aTileInfo.nTilesEmptyX && nX < nExponent*nMSBFactor; nX += nMSBFactor) + { + if( bNoFirstTileDraw ) + bNoFirstTileDraw = false; // don't draw first tile position + else if (!pTileGraphic->Draw(rVDev, aCurrPos, aTileInfo.aTileSizePixel, pAttr)) + return false; + + aCurrPos.AdjustX(aTileInfo.aTileSizePixel.Width() ); + } + + aCurrPos.AdjustY(aTileInfo.aTileSizePixel.Height() ); + } + +#ifdef DBG_TEST +// rVDev.SetFillCOL_WHITE ); + rVDev.SetFillColor(); + rVDev.SetLineColor( Color( 255 * nExponent / nMSBFactor, 255 - 255 * nExponent / nMSBFactor, 128 - 255 * nExponent / nMSBFactor ) ); + rVDev.DrawRect( tools::Rectangle((rTileInfo.aTileTopLeft.X())*rTileSizePixel.Width(), + (rTileInfo.aTileTopLeft.Y())*rTileSizePixel.Height(), + (rTileInfo.aNextTileTopLeft.X())*rTileSizePixel.Width()-1, + (rTileInfo.aNextTileTopLeft.Y())*rTileSizePixel.Height()-1) ); +#endif + + return true; +} + +bool GraphicObject::ImplDrawTiled(OutputDevice& rOut, const tools::Rectangle& rArea, const Size& rSizePixel, + const Size& rOffset, const GraphicAttr* pAttr, int nTileCacheSize1D) +{ + const MapMode aOutMapMode(rOut.GetMapMode()); + const MapMode aMapMode( aOutMapMode.GetMapUnit(), Point(), aOutMapMode.GetScaleX(), aOutMapMode.GetScaleY() ); + bool bRet( false ); + + // #i42643# Casting to Int64, to avoid integer overflow for + // huge-DPI output devices + if( GetGraphic().GetType() == GraphicType::Bitmap && + static_cast<sal_Int64>(rSizePixel.Width()) * rSizePixel.Height() < + static_cast<sal_Int64>(nTileCacheSize1D)*nTileCacheSize1D ) + { + // First combine very small bitmaps into a larger tile + + + ScopedVclPtrInstance< VirtualDevice > aVDev; + const int nNumTilesInCacheX( (nTileCacheSize1D + rSizePixel.Width()-1) / rSizePixel.Width() ); + const int nNumTilesInCacheY( (nTileCacheSize1D + rSizePixel.Height()-1) / rSizePixel.Height() ); + + aVDev->SetOutputSizePixel( Size( nNumTilesInCacheX*rSizePixel.Width(), + nNumTilesInCacheY*rSizePixel.Height() ) ); + aVDev->SetMapMode( aMapMode ); + + // draw bitmap content + if( ImplRenderTempTile( *aVDev, nNumTilesInCacheX, + nNumTilesInCacheY, rSizePixel, pAttr ) ) + { + BitmapEx aTileBitmap( aVDev->GetBitmap( Point(0,0), aVDev->GetOutputSize() ) ); + + // draw alpha content, if any + if( IsTransparent() ) + { + GraphicObject aAlphaGraphic; + + if( GetGraphic().IsAlpha() ) + aAlphaGraphic.SetGraphic(BitmapEx(GetGraphic().GetBitmapEx().GetAlphaMask().GetBitmap())); + else + aAlphaGraphic.SetGraphic(BitmapEx(Bitmap())); + + if( aAlphaGraphic.ImplRenderTempTile( *aVDev, nNumTilesInCacheX, + nNumTilesInCacheY, rSizePixel, pAttr ) ) + { + // Combine bitmap and alpha/mask + if( GetGraphic().IsAlpha() ) + aTileBitmap = BitmapEx( aTileBitmap.GetBitmap(), + AlphaMask( aVDev->GetBitmap( Point(0,0), aVDev->GetOutputSize() ) ) ); + else + aTileBitmap = BitmapEx( aTileBitmap.GetBitmap(), + aVDev->GetBitmap( Point(0,0), aVDev->GetOutputSize() ).CreateAlphaMask( COL_WHITE ) ); + } + } + + // paint generated tile + GraphicObject aTmpGraphic( aTileBitmap ); + bRet = aTmpGraphic.ImplDrawTiled(rOut, rArea, + aTileBitmap.GetSizePixel(), + rOffset, pAttr, nTileCacheSize1D); + } + } + else + { + const Size aOutOffset( rOut.LogicToPixel( rOffset, aOutMapMode ) ); + const tools::Rectangle aOutArea( rOut.LogicToPixel( rArea, aOutMapMode ) ); + + // number of invisible (because out-of-area) tiles + int nInvisibleTilesX; + int nInvisibleTilesY; + + // round towards -infty for negative offset + if( aOutOffset.Width() < 0 ) + nInvisibleTilesX = (aOutOffset.Width() - rSizePixel.Width() + 1) / rSizePixel.Width(); + else + nInvisibleTilesX = aOutOffset.Width() / rSizePixel.Width(); + + // round towards -infty for negative offset + if( aOutOffset.Height() < 0 ) + nInvisibleTilesY = (aOutOffset.Height() - rSizePixel.Height() + 1) / rSizePixel.Height(); + else + nInvisibleTilesY = aOutOffset.Height() / rSizePixel.Height(); + + // origin from where to 'virtually' start drawing in pixel + const Point aOutOrigin( rOut.LogicToPixel( Point( rArea.Left() - rOffset.Width(), + rArea.Top() - rOffset.Height() ) ) ); + // position in pixel from where to really start output + const Point aOutStart( aOutOrigin.X() + nInvisibleTilesX*rSizePixel.Width(), + aOutOrigin.Y() + nInvisibleTilesY*rSizePixel.Height() ); + + rOut.Push( vcl::PushFlags::CLIPREGION ); + rOut.IntersectClipRegion( rArea ); + + // Paint all tiles + + + bRet = ImplDrawTiled(rOut, aOutStart, + (aOutArea.GetWidth() + aOutArea.Left() - aOutStart.X() + rSizePixel.Width() - 1) / rSizePixel.Width(), + (aOutArea.GetHeight() + aOutArea.Top() - aOutStart.Y() + rSizePixel.Height() - 1) / rSizePixel.Height(), + rSizePixel, pAttr); + + rOut.Pop(); + } + + return bRet; +} + +bool GraphicObject::ImplDrawTiled( OutputDevice& rOut, const Point& rPosPixel, + int nNumTilesX, int nNumTilesY, + const Size& rTileSizePixel, const GraphicAttr* pAttr ) const +{ + Point aCurrPos( rPosPixel ); + Size aTileSizeLogic( rOut.PixelToLogic( rTileSizePixel ) ); + int nX, nY; + + // #107607# Use logical coordinates for metafile playing, too + bool bDrawInPixel( rOut.GetConnectMetaFile() == nullptr && GraphicType::Bitmap == GetType() ); + bool bRet = false; + + // #105229# Switch off mapping (converting to logic and back to + // pixel might cause roundoff errors) + bool bOldMap( rOut.IsMapModeEnabled() ); + + if( bDrawInPixel ) + rOut.EnableMapMode( false ); + + for( nY=0; nY < nNumTilesY; ++nY ) + { + aCurrPos.setX( rPosPixel.X() ); + + for( nX=0; nX < nNumTilesX; ++nX ) + { + // #105229# work with pixel coordinates here, mapping is disabled! + // #104004# don't disable mapping for metafile recordings + // #108412# don't quit the loop if one draw fails + + // update return value. This method should return true, if + // at least one of the looped Draws succeeded. + bRet |= Draw(rOut, + bDrawInPixel ? aCurrPos : rOut.PixelToLogic(aCurrPos), + bDrawInPixel ? rTileSizePixel : aTileSizeLogic, + pAttr); + + aCurrPos.AdjustX(rTileSizePixel.Width() ); + } + + aCurrPos.AdjustY(rTileSizePixel.Height() ); + } + + if( bDrawInPixel ) + rOut.EnableMapMode( bOldMap ); + + return bRet; +} + +void GraphicObject::ImplTransformBitmap( BitmapEx& rBmpEx, + const GraphicAttr& rAttr, + const Size& rCropLeftTop, + const Size& rCropRightBottom, + const tools::Rectangle& rCropRect, + const Size& rDstSize, + bool bEnlarge ) const +{ + // #107947# Extracted from svdograf.cxx + + // #104115# Crop the bitmap + if( rAttr.IsCropped() ) + { + rBmpEx.Crop( rCropRect ); + + // #104115# Negative crop sizes mean: enlarge bitmap and pad + if( bEnlarge && ( + rCropLeftTop.Width() < 0 || + rCropLeftTop.Height() < 0 || + rCropRightBottom.Width() < 0 || + rCropRightBottom.Height() < 0 ) ) + { + Size aBmpSize( rBmpEx.GetSizePixel() ); + sal_Int32 nPadLeft( rCropLeftTop.Width() < 0 ? -rCropLeftTop.Width() : 0 ); + sal_Int32 nPadTop( rCropLeftTop.Height() < 0 ? -rCropLeftTop.Height() : 0 ); + sal_Int32 nPadTotalWidth( aBmpSize.Width() + nPadLeft + (rCropRightBottom.Width() < 0 ? -rCropRightBottom.Width() : 0) ); + sal_Int32 nPadTotalHeight( aBmpSize.Height() + nPadTop + (rCropRightBottom.Height() < 0 ? -rCropRightBottom.Height() : 0) ); + + BitmapEx aBmpEx2; + + if( rBmpEx.IsAlpha() ) + { + aBmpEx2 = rBmpEx; + } + else + { + // #104115# Generate mask bitmap and init to zero + AlphaMask aMask(aBmpSize); + aMask.Erase(255); + + // #104115# Always generate transparent bitmap, we need the border transparent + aBmpEx2 = BitmapEx( rBmpEx.GetBitmap(), aMask ); + + // #104115# Add opaque mask to source bitmap, otherwise the destination remains transparent + rBmpEx = aBmpEx2; + } + + aBmpEx2.Scale(Size(nPadTotalWidth, nPadTotalHeight)); + aBmpEx2.Erase( Color(ColorAlpha,0,0,0,0) ); + aBmpEx2.CopyPixel( tools::Rectangle( Point(nPadLeft, nPadTop), aBmpSize ), tools::Rectangle( Point(0, 0), aBmpSize ), rBmpEx ); + rBmpEx = aBmpEx2; + } + } + + const Size aSizePixel( rBmpEx.GetSizePixel() ); + + if( rAttr.GetRotation() == 0_deg10 || IsAnimated() ) + return; + + if( !(aSizePixel.Width() && aSizePixel.Height() && rDstSize.Width() && rDstSize.Height()) ) + return; + + double fSrcWH = static_cast<double>(aSizePixel.Width()) / aSizePixel.Height(); + double fDstWH = static_cast<double>(rDstSize.Width()) / rDstSize.Height(); + double fScaleX = 1.0, fScaleY = 1.0; + + // always choose scaling to shrink bitmap + if( fSrcWH < fDstWH ) + fScaleY = aSizePixel.Width() / ( fDstWH * aSizePixel.Height() ); + else + fScaleX = fDstWH * aSizePixel.Height() / aSizePixel.Width(); + + rBmpEx.Scale( fScaleX, fScaleY ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/graphic/GraphicReader.cxx b/vcl/source/graphic/GraphicReader.cxx new file mode 100644 index 0000000000..9137ebd8a2 --- /dev/null +++ b/vcl/source/graphic/GraphicReader.cxx @@ -0,0 +1,29 @@ +/* -*- 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 <graphic/GraphicReader.hxx> + +#include <sal/config.h> +#include <sal/log.hxx> + +GraphicReader::GraphicReader() {} + +GraphicReader::~GraphicReader() {} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/graphic/Manager.cxx b/vcl/source/graphic/Manager.cxx new file mode 100644 index 0000000000..f77e3e3c40 --- /dev/null +++ b/vcl/source/graphic/Manager.cxx @@ -0,0 +1,317 @@ +/* -*- 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 <graphic/Manager.hxx> +#include <impgraph.hxx> +#include <sal/log.hxx> + +#include <officecfg/Office/Common.hxx> +#include <unotools/configmgr.hxx> + +using namespace css; + +namespace vcl::graphic +{ +namespace +{ +void setupConfigurationValuesIfPossible(sal_Int64& rMemoryLimit, + std::chrono::seconds& rAllowedIdleTime, bool& bSwapEnabled) +{ + if (utl::ConfigManager::IsFuzzing()) + return; + + try + { + using officecfg::Office::Common::Cache; + + rMemoryLimit = Cache::GraphicManager::GraphicMemoryLimit::get(); + rAllowedIdleTime + = std::chrono::seconds(Cache::GraphicManager::GraphicAllowedIdleTime::get()); + bSwapEnabled = Cache::GraphicManager::GraphicSwappingEnabled::get(); + } + catch (...) + { + } +} +} + +Manager& Manager::get() +{ + static Manager gStaticManager; + return gStaticManager; +} + +Manager::Manager() + : mnAllowedIdleTime(10) + , mbSwapEnabled(true) + , mbReducingGraphicMemory(false) + , mnMemoryLimit(300000000) + , mnUsedSize(0) + , maSwapOutTimer("graphic::Manager maSwapOutTimer") +{ + setupConfigurationValuesIfPossible(mnMemoryLimit, mnAllowedIdleTime, mbSwapEnabled); + + if (mbSwapEnabled) + { + maSwapOutTimer.SetInvokeHandler(LINK(this, Manager, SwapOutTimerHandler)); + maSwapOutTimer.SetTimeout(10000); + maSwapOutTimer.Start(); + } +} + +void Manager::loopGraphicsAndSwapOut(std::unique_lock<std::mutex>& rGuard, bool bDropAll) +{ + // make a copy of m_pImpGraphicList because if we swap out a svg, the svg + // filter may create more temp Graphics which are auto-added to + // m_pImpGraphicList invalidating a loop over m_pImpGraphicList, e.g. + // reexport of tdf118346-1.odg + o3tl::sorted_vector<ImpGraphic*> aImpGraphicList = m_pImpGraphicList; + + for (ImpGraphic* pEachImpGraphic : aImpGraphicList) + { + if (mnUsedSize < sal_Int64(mnMemoryLimit * 0.7) && !bDropAll) + return; + + if (pEachImpGraphic->isSwappedOut()) + continue; + + sal_Int64 nCurrentGraphicSize = getGraphicSizeBytes(pEachImpGraphic); + if (nCurrentGraphicSize > 100000 || bDropAll) + { + if (!pEachImpGraphic->mpContext) + { + auto aCurrent = std::chrono::high_resolution_clock::now(); + auto aDeltaTime = aCurrent - pEachImpGraphic->maLastUsed; + auto aSeconds = std::chrono::duration_cast<std::chrono::seconds>(aDeltaTime); + + if (aSeconds > mnAllowedIdleTime) + { + // unlock because svgio can call back into us + rGuard.unlock(); + pEachImpGraphic->swapOut(); + rGuard.lock(); + } + } + } + } +} + +void Manager::reduceGraphicMemory(std::unique_lock<std::mutex>& rGuard, bool bDropAll) +{ + // maMutex is locked in callers + + if (!mbSwapEnabled) + return; + + if (mnUsedSize < mnMemoryLimit && !bDropAll) + return; + + // avoid recursive reduceGraphicMemory on reexport of tdf118346-1.odg to odg + if (mbReducingGraphicMemory) + return; + + mbReducingGraphicMemory = true; + + loopGraphicsAndSwapOut(rGuard, bDropAll); + + sal_Int64 calculatedSize = 0; + for (ImpGraphic* pEachImpGraphic : m_pImpGraphicList) + { + if (!pEachImpGraphic->isSwappedOut()) + { + calculatedSize += getGraphicSizeBytes(pEachImpGraphic); + } + } + + if (calculatedSize != mnUsedSize) + { + assert(rGuard.owns_lock() && rGuard.mutex() == &maMutex); + // coverity[missing_lock: FALSE] - as above assert + mnUsedSize = calculatedSize; + } + + mbReducingGraphicMemory = false; +} + +void Manager::dropCache() +{ + std::unique_lock aGuard(maMutex); + + reduceGraphicMemory(aGuard, true); +} + +void Manager::dumpState(rtl::OStringBuffer& rState) +{ + std::unique_lock aGuard(maMutex); + + rState.append("\nImage Manager items:\t"); + rState.append(static_cast<sal_Int32>(m_pImpGraphicList.size())); + rState.append("\tsize:\t"); + rState.append(static_cast<sal_Int64>(mnUsedSize / 1024)); + rState.append("\tkb"); + + for (ImpGraphic* pEachImpGraphic : m_pImpGraphicList) + { + pEachImpGraphic->dumpState(rState); + } +} + +sal_Int64 Manager::getGraphicSizeBytes(const ImpGraphic* pImpGraphic) +{ + if (!pImpGraphic->isAvailable()) + return 0; + return pImpGraphic->getSizeBytes(); +} + +IMPL_LINK(Manager, SwapOutTimerHandler, Timer*, pTimer, void) +{ + std::unique_lock aGuard(maMutex); + + pTimer->Stop(); + reduceGraphicMemory(aGuard); + pTimer->Start(); +} + +void Manager::registerGraphic(const std::shared_ptr<ImpGraphic>& pImpGraphic) +{ + std::unique_lock aGuard(maMutex); + + // make some space first + if (mnUsedSize > mnMemoryLimit) + reduceGraphicMemory(aGuard); + + // Insert and update the used size (bytes) + assert(aGuard.owns_lock() && aGuard.mutex() == &maMutex); + // coverity[missing_lock: FALSE] - as above assert + mnUsedSize += getGraphicSizeBytes(pImpGraphic.get()); + m_pImpGraphicList.insert(pImpGraphic.get()); + + // calculate size of the graphic set + sal_Int64 calculatedSize = 0; + for (ImpGraphic* pEachImpGraphic : m_pImpGraphicList) + { + if (!pEachImpGraphic->isSwappedOut()) + { + calculatedSize += getGraphicSizeBytes(pEachImpGraphic); + } + } + + if (calculatedSize != mnUsedSize) + { + SAL_INFO_IF(calculatedSize != mnUsedSize, "vcl.gdi", + "Calculated size mismatch. Variable size is '" + << mnUsedSize << "' but calculated size is '" << calculatedSize << "'"); + mnUsedSize = calculatedSize; + } +} + +void Manager::unregisterGraphic(ImpGraphic* pImpGraphic) +{ + std::scoped_lock aGuard(maMutex); + + mnUsedSize -= getGraphicSizeBytes(pImpGraphic); + m_pImpGraphicList.erase(pImpGraphic); +} + +std::shared_ptr<ImpGraphic> Manager::copy(std::shared_ptr<ImpGraphic> const& rImpGraphicPtr) +{ + auto pReturn = std::make_shared<ImpGraphic>(*rImpGraphicPtr); + registerGraphic(pReturn); + return pReturn; +} + +std::shared_ptr<ImpGraphic> Manager::newInstance() +{ + auto pReturn = std::make_shared<ImpGraphic>(); + registerGraphic(pReturn); + return pReturn; +} + +std::shared_ptr<ImpGraphic> Manager::newInstance(std::shared_ptr<GfxLink> const& rGfxLink, + sal_Int32 nPageIndex) +{ + auto pReturn = std::make_shared<ImpGraphic>(rGfxLink, nPageIndex); + registerGraphic(pReturn); + return pReturn; +} + +std::shared_ptr<ImpGraphic> Manager::newInstance(const BitmapEx& rBitmapEx) +{ + auto pReturn = std::make_shared<ImpGraphic>(rBitmapEx); + registerGraphic(pReturn); + return pReturn; +} + +std::shared_ptr<ImpGraphic> Manager::newInstance(const Animation& rAnimation) +{ + auto pReturn = std::make_shared<ImpGraphic>(rAnimation); + registerGraphic(pReturn); + return pReturn; +} + +std::shared_ptr<ImpGraphic> +Manager::newInstance(const std::shared_ptr<VectorGraphicData>& rVectorGraphicDataPtr) +{ + auto pReturn = std::make_shared<ImpGraphic>(rVectorGraphicDataPtr); + registerGraphic(pReturn); + return pReturn; +} + +std::shared_ptr<ImpGraphic> Manager::newInstance(const GDIMetaFile& rMetaFile) +{ + auto pReturn = std::make_shared<ImpGraphic>(rMetaFile); + registerGraphic(pReturn); + return pReturn; +} + +std::shared_ptr<ImpGraphic> Manager::newInstance(const GraphicExternalLink& rGraphicLink) +{ + auto pReturn = std::make_shared<ImpGraphic>(rGraphicLink); + registerGraphic(pReturn); + return pReturn; +} + +void Manager::swappedIn(const ImpGraphic* pImpGraphic, sal_Int64 nSizeBytes) +{ + std::scoped_lock aGuard(maMutex); + if (pImpGraphic) + { + mnUsedSize += nSizeBytes; + } +} + +void Manager::swappedOut(const ImpGraphic* pImpGraphic, sal_Int64 nSizeBytes) +{ + std::scoped_lock aGuard(maMutex); + if (pImpGraphic) + { + mnUsedSize -= nSizeBytes; + } +} + +void Manager::changeExisting(const ImpGraphic* pImpGraphic, sal_Int64 nOldSizeBytes) +{ + std::scoped_lock aGuard(maMutex); + + mnUsedSize -= nOldSizeBytes; + mnUsedSize += getGraphicSizeBytes(pImpGraphic); +} +} // end vcl::graphic + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/graphic/UnoBinaryDataContainer.cxx b/vcl/source/graphic/UnoBinaryDataContainer.cxx new file mode 100644 index 0000000000..3fe277024a --- /dev/null +++ b/vcl/source/graphic/UnoBinaryDataContainer.cxx @@ -0,0 +1,22 @@ +/* -*- 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/. + * + */ + +#include <graphic/UnoBinaryDataContainer.hxx> + +#include <cppuhelper/queryinterface.hxx> + +using namespace css; + +css::uno::Sequence<sal_Int8> SAL_CALL UnoBinaryDataContainer::getCopyAsByteSequence() +{ + return maBinaryDataContainer.getCopyAsByteSequence(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/graphic/UnoGraphic.cxx b/vcl/source/graphic/UnoGraphic.cxx new file mode 100644 index 0000000000..9036f54452 --- /dev/null +++ b/vcl/source/graphic/UnoGraphic.cxx @@ -0,0 +1,261 @@ +/* -*- 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 <graphic/UnoGraphic.hxx> + +#include <tools/stream.hxx> +#include <vcl/svapp.hxx> +#include <com/sun/star/graphic/GraphicType.hpp> +#include <com/sun/star/graphic/XGraphicTransformer.hpp> +#include <vcl/dibtools.hxx> +#include <vcl/graph.hxx> +#include <vcl/BitmapColor.hxx> +#include <vcl/BitmapDuoToneFilter.hxx> +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/typeprovider.hxx> + +using namespace com::sun::star; + +namespace unographic { + +Graphic::Graphic() +{ +} + +Graphic::~Graphic() noexcept +{ +} + +void Graphic::init(const ::Graphic& rGraphic) +{ + maGraphic = rGraphic; + unographic::GraphicDescriptor::init(maGraphic); +} + +uno::Any SAL_CALL Graphic::queryInterface( const uno::Type & rType ) +{ + uno::Any aAny; + if( rType == cppu::UnoType<graphic::XGraphic>::get()) + aAny <<= uno::Reference< graphic::XGraphic >( this ); + else if( rType == cppu::UnoType<awt::XBitmap>::get()) + aAny <<= uno::Reference< awt::XBitmap >( this ); + else if( rType == cppu::UnoType<graphic::XGraphicTransformer>::get()) + aAny <<= uno::Reference< graphic::XGraphicTransformer >(this); + else + aAny = ::unographic::GraphicDescriptor::queryInterface( rType ); + + return aAny; +} + +void SAL_CALL Graphic::acquire() + noexcept +{ + unographic::GraphicDescriptor::acquire(); +} + +void SAL_CALL Graphic::release() noexcept +{ + unographic::GraphicDescriptor::release(); +} + +OUString SAL_CALL Graphic::getImplementationName() +{ + return "com.sun.star.comp.graphic.Graphic"; +} + +sal_Bool SAL_CALL Graphic::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService( this, rServiceName ); +} + +uno::Sequence< OUString > SAL_CALL Graphic::getSupportedServiceNames() +{ + uno::Sequence< OUString > aRet( ::unographic::GraphicDescriptor::getSupportedServiceNames() ); + const uno::Sequence< OUString > aNew { "com.sun.star.graphic.Graphic" }; + sal_Int32 nOldCount = aRet.getLength(); + + aRet.realloc( nOldCount + aNew.getLength() ); + + std::copy(aNew.begin(), aNew.end(), std::next(aRet.getArray(), nOldCount)); + + return aRet; +} + +uno::Sequence< uno::Type > SAL_CALL Graphic::getTypes() +{ + return cppu::OTypeCollection( + cppu::UnoType<graphic::XGraphic>::get(), + cppu::UnoType<awt::XBitmap>::get(), + ::unographic::GraphicDescriptor::getTypes() + ).getTypes(); +} + +uno::Sequence< sal_Int8 > SAL_CALL Graphic::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +sal_Int8 SAL_CALL Graphic::getType() +{ + sal_Int8 cRet = graphic::GraphicType::EMPTY; + + if (!maGraphic.IsNone()) + { + cRet = (maGraphic.GetType() == ::GraphicType::Bitmap) ? graphic::GraphicType::PIXEL + : graphic::GraphicType::VECTOR; + } + + return cRet; +} + +// XBitmap + +awt::Size SAL_CALL Graphic::getSize() +{ + SolarMutexGuard aGuard; + + Size aVclSize; + if (!maGraphic.IsNone()) + { + aVclSize = maGraphic.GetSizePixel(); + } + return awt::Size(aVclSize.Width(), aVclSize.Height()); +} + +uno::Sequence<sal_Int8> SAL_CALL Graphic::getDIB() +{ + SolarMutexGuard aGuard; + + if (!maGraphic.IsNone()) + { + SvMemoryStream aMemoryStream; + + WriteDIB(maGraphic.GetBitmapEx().GetBitmap(), aMemoryStream, false, true); + return css::uno::Sequence<sal_Int8>(static_cast<sal_Int8 const *>(aMemoryStream.GetData()), aMemoryStream.Tell()); + } + else + { + return uno::Sequence<sal_Int8>(); + } +} + +uno::Sequence<sal_Int8> SAL_CALL Graphic::getMaskDIB() +{ + SolarMutexGuard aGuard; + + if (!maGraphic.IsNone()) + { + SvMemoryStream aMemoryStream; + + WriteDIB(maGraphic.GetBitmapEx().GetAlphaMask().GetBitmap(), aMemoryStream, false, true); + return css::uno::Sequence<sal_Int8>( static_cast<sal_Int8 const *>(aMemoryStream.GetData()), aMemoryStream.Tell() ); + } + else + { + return uno::Sequence<sal_Int8>(); + } +} + +// XGraphicTransformer +uno::Reference< graphic::XGraphic > SAL_CALL Graphic::colorChange( + const uno::Reference< graphic::XGraphic >& rxGraphic, sal_Int32 nColorFrom, sal_Int8 nTolerance, sal_Int32 nColorTo, sal_Int8 nAlphaTo ) +{ + ::Graphic aGraphic(rxGraphic); + ::Graphic aReturnGraphic; + + BitmapColor aBmpColorFrom(Color(ColorTransparency, static_cast<sal_uInt32>(nColorFrom))); + BitmapColor aBmpColorTo(Color(ColorTransparency, static_cast<sal_uInt32>(nColorTo))); + + Color aColorFrom(aBmpColorFrom); + Color aColorTo(aBmpColorTo); + + const sal_uInt8 cIndexFrom = aBmpColorFrom.GetIndex(); + + //TODO This code convert GdiMetafile(vector graphic) to Bitmap, which cause to information lost + if (aGraphic.GetType() == GraphicType::Bitmap || + aGraphic.GetType() == GraphicType::GdiMetafile) + { + BitmapEx aBitmapEx(aGraphic.GetBitmapEx()); + + if (aBitmapEx.IsAlpha()) + { + aBitmapEx.ChangeColorAlpha( cIndexFrom, nAlphaTo ); + aBitmapEx.Replace(aColorFrom, aColorTo, nTolerance); + aReturnGraphic = ::Graphic(aBitmapEx); + } + else + { + if ((nAlphaTo == 0) || (nAlphaTo == sal::static_int_cast< sal_Int8 >(0xff))) + { + Bitmap aBitmap(aBitmapEx.GetBitmap()); + AlphaMask aMask(aBitmap.CreateAlphaMask(aColorFrom, nTolerance)); + aBitmap.Replace(aColorFrom, aColorTo, nTolerance); + aReturnGraphic = ::Graphic(BitmapEx(aBitmap, aMask)); + } + else + { + aBitmapEx.Replace(aColorFrom, aColorTo, nTolerance); + aReturnGraphic = ::Graphic(aBitmapEx); + } + } + } + + aReturnGraphic.setOriginURL(aGraphic.getOriginURL()); + return aReturnGraphic.GetXGraphic(); +} + +uno::Reference< graphic::XGraphic > SAL_CALL Graphic::applyDuotone( + const uno::Reference< graphic::XGraphic >& rxGraphic, sal_Int32 nColorOne, sal_Int32 nColorTwo ) +{ + ::Graphic aGraphic(rxGraphic); + ::Graphic aReturnGraphic; + + BitmapEx aBitmapEx( aGraphic.GetBitmapEx() ); + AlphaMask aMask( aBitmapEx.GetAlphaMask() ); + + BitmapEx aTmpBmpEx(aBitmapEx.GetBitmap()); + BitmapFilter::Filter(aTmpBmpEx, + BitmapDuoToneFilter( + Color(ColorTransparency, nColorOne), + Color(ColorTransparency, nColorTwo))); + + aReturnGraphic = ::Graphic( BitmapEx( aTmpBmpEx.GetBitmap(), aMask ) ); + aReturnGraphic.setOriginURL(aGraphic.getOriginURL()); + return aReturnGraphic.GetXGraphic(); +} + +uno::Reference< graphic::XGraphic > SAL_CALL Graphic::applyBrightnessContrast( + const uno::Reference< graphic::XGraphic >& rxGraphic, sal_Int32 nBrightness, sal_Int32 nContrast, sal_Bool mso ) +{ + ::Graphic aGraphic(rxGraphic); + ::Graphic aReturnGraphic; + + BitmapEx aBitmapEx(aGraphic.GetBitmapEx()); + aBitmapEx.Adjust(nBrightness, nContrast, 0, 0, 0, 0, false, mso); + aReturnGraphic = ::Graphic(aBitmapEx); + aReturnGraphic.setOriginURL(aGraphic.getOriginURL()); + return aReturnGraphic.GetXGraphic(); +} + + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/graphic/UnoGraphicDescriptor.cxx b/vcl/source/graphic/UnoGraphicDescriptor.cxx new file mode 100644 index 0000000000..e83ac98959 --- /dev/null +++ b/vcl/source/graphic/UnoGraphicDescriptor.cxx @@ -0,0 +1,421 @@ +/* -*- 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 <graphic/UnoGraphicDescriptor.hxx> + +#include <cppuhelper/weakagg.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <vcl/graphicfilter.hxx> +#include <cppuhelper/supportsservice.hxx> + +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/graphic/GraphicType.hpp> +#include <com/sun/star/io/XInputStream.hpp> + +#include <vcl/outdev.hxx> +#include <vcl/graph.hxx> +#include <vcl/svapp.hxx> +#include <memory> + +namespace { + +enum class UnoGraphicProperty +{ + GraphicType = 1 + , MimeType = 2 + , SizePixel = 3 + , Size100thMM = 4 + , BitsPerPixel = 5 + , Transparent = 6 + , Alpha = 7 + , Animated = 8 + , Linked = 9 + , OriginURL = 10 +}; + +} + +using namespace ::com::sun::star; + +namespace unographic { + + +GraphicDescriptor::GraphicDescriptor() : + ::comphelper::PropertySetHelper( createPropertySetInfo() ), + mpGraphic( nullptr ), + meType( GraphicType::NONE ), + mnBitsPerPixel ( 0 ), + mbTransparent ( false ) +{ +} + +GraphicDescriptor::~GraphicDescriptor() + noexcept +{ +} + +void GraphicDescriptor::init( const ::Graphic& rGraphic ) +{ + mpGraphic = &rGraphic; +} + +void GraphicDescriptor::init( const OUString& rURL ) +{ + std::unique_ptr<SvStream> pIStm(::utl::UcbStreamHelper::CreateStream( rURL, StreamMode::READ )); + + if( pIStm ) + implCreate( *pIStm, &rURL ); +} + +void GraphicDescriptor::init( const uno::Reference< io::XInputStream >& rxIStm, const OUString& rURL ) +{ + std::unique_ptr<SvStream> pIStm(::utl::UcbStreamHelper::CreateStream( rxIStm )); + + if( pIStm ) + implCreate( *pIStm, &rURL ); +} + +void GraphicDescriptor::implCreate( SvStream& rIStm, const OUString* pURL ) +{ + OUString aURL; + if( pURL ) + aURL = *pURL; + ::GraphicDescriptor aDescriptor( rIStm, &aURL ); + + mpGraphic = nullptr; + maMimeType.clear(); + meType = GraphicType::NONE; + mnBitsPerPixel = 0; + mbTransparent = false; + + if( !(aDescriptor.Detect( true ) && aDescriptor.GetFileFormat() != GraphicFileFormat::NOT) ) + return; + + OUString aMimeType; + sal_uInt8 cType = graphic::GraphicType::EMPTY; + + switch( aDescriptor.GetFileFormat() ) + { + case GraphicFileFormat::BMP: aMimeType = MIMETYPE_BMP; cType = graphic::GraphicType::PIXEL; break; + case GraphicFileFormat::GIF: aMimeType = MIMETYPE_GIF; cType = graphic::GraphicType::PIXEL; break; + case GraphicFileFormat::JPG: aMimeType = MIMETYPE_JPG; cType = graphic::GraphicType::PIXEL; break; + case GraphicFileFormat::PCD: aMimeType = MIMETYPE_PCD; cType = graphic::GraphicType::PIXEL; break; + case GraphicFileFormat::PCX: aMimeType = MIMETYPE_PCX; cType = graphic::GraphicType::PIXEL; break; + case GraphicFileFormat::PNG: aMimeType = MIMETYPE_PNG; cType = graphic::GraphicType::PIXEL; break; + case GraphicFileFormat::TIF: aMimeType = MIMETYPE_TIF; cType = graphic::GraphicType::PIXEL; break; + case GraphicFileFormat::XBM: aMimeType = MIMETYPE_XBM; cType = graphic::GraphicType::PIXEL; break; + case GraphicFileFormat::XPM: aMimeType = MIMETYPE_XPM; cType = graphic::GraphicType::PIXEL; break; + case GraphicFileFormat::PBM: aMimeType = MIMETYPE_PBM; cType = graphic::GraphicType::PIXEL; break; + case GraphicFileFormat::PGM: aMimeType = MIMETYPE_PGM; cType = graphic::GraphicType::PIXEL; break; + case GraphicFileFormat::PPM: aMimeType = MIMETYPE_PPM; cType = graphic::GraphicType::PIXEL; break; + case GraphicFileFormat::RAS: aMimeType = MIMETYPE_RAS; cType = graphic::GraphicType::PIXEL; break; + case GraphicFileFormat::TGA: aMimeType = MIMETYPE_TGA; cType = graphic::GraphicType::PIXEL; break; + case GraphicFileFormat::PSD: aMimeType = MIMETYPE_PSD; cType = graphic::GraphicType::PIXEL; break; + case GraphicFileFormat::WEBP: aMimeType = MIMETYPE_WEBP; cType = graphic::GraphicType::PIXEL; break; + + case GraphicFileFormat::EPS: aMimeType = MIMETYPE_EPS; cType = graphic::GraphicType::VECTOR; break; + case GraphicFileFormat::DXF: aMimeType = MIMETYPE_DXF; cType = graphic::GraphicType::VECTOR; break; + case GraphicFileFormat::MET: aMimeType = MIMETYPE_MET; cType = graphic::GraphicType::VECTOR; break; + case GraphicFileFormat::PCT: aMimeType = MIMETYPE_PCT; cType = graphic::GraphicType::VECTOR; break; + case GraphicFileFormat::SVM: aMimeType = MIMETYPE_SVM; cType = graphic::GraphicType::VECTOR; break; + case GraphicFileFormat::WMF: aMimeType = MIMETYPE_WMF; cType = graphic::GraphicType::VECTOR; break; + case GraphicFileFormat::WMZ: aMimeType = MIMETYPE_WMF; cType = graphic::GraphicType::VECTOR; break; + case GraphicFileFormat::EMF: aMimeType = MIMETYPE_EMF; cType = graphic::GraphicType::VECTOR; break; + case GraphicFileFormat::EMZ: aMimeType = MIMETYPE_EMF; cType = graphic::GraphicType::VECTOR; break; + case GraphicFileFormat::SVG: aMimeType = MIMETYPE_SVG; cType = graphic::GraphicType::VECTOR; break; + case GraphicFileFormat::SVGZ: aMimeType = MIMETYPE_SVG; cType = graphic::GraphicType::VECTOR; break; + + default: + break; + } + + if( graphic::GraphicType::EMPTY != cType ) + { + meType = ( ( graphic::GraphicType::PIXEL == cType ) ? GraphicType::Bitmap : GraphicType::GdiMetafile ); + maMimeType = aMimeType; + maSizePixel = aDescriptor.GetSizePixel(); + maSize100thMM = aDescriptor.GetSize_100TH_MM(); + mnBitsPerPixel = aDescriptor.GetBitsPerPixel(); + mbTransparent = ( graphic::GraphicType::VECTOR == cType ); + } +} + + +uno::Any SAL_CALL GraphicDescriptor::queryInterface( const uno::Type & rType ) +{ + uno::Any aAny; + + if( rType == cppu::UnoType<lang::XServiceInfo>::get()) + aAny <<= uno::Reference< lang::XServiceInfo >(this); + else if( rType == cppu::UnoType<lang::XTypeProvider>::get()) + aAny <<= uno::Reference< lang::XTypeProvider >(this); + else if( rType == cppu::UnoType<beans::XPropertySet>::get()) + aAny <<= uno::Reference< beans::XPropertySet >(this); + else if( rType == cppu::UnoType<beans::XPropertyState>::get()) + aAny <<= uno::Reference< beans::XPropertyState >(this); + else if( rType == cppu::UnoType<beans::XMultiPropertySet>::get()) + aAny <<= uno::Reference< beans::XMultiPropertySet >(this); + else + aAny = OWeakObject::queryInterface( rType ); + + return aAny; +} + + +void SAL_CALL GraphicDescriptor::acquire() + noexcept +{ + OWeakObject::acquire(); +} + + +void SAL_CALL GraphicDescriptor::release() + noexcept +{ + OWeakObject::release(); +} + + +OUString SAL_CALL GraphicDescriptor::getImplementationName() +{ + return "com.sun.star.comp.graphic.GraphicDescriptor"; +} + +sal_Bool SAL_CALL GraphicDescriptor::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + + +uno::Sequence< OUString > SAL_CALL GraphicDescriptor::getSupportedServiceNames() +{ + return { "com.sun.star.graphic.GraphicDescriptor" }; +} + + +uno::Sequence< uno::Type > SAL_CALL GraphicDescriptor::getTypes() +{ + static const uno::Sequence< uno::Type > aTypes { + cppu::UnoType<uno::XAggregation>::get(), + cppu::UnoType<lang::XServiceInfo>::get(), + cppu::UnoType<lang::XTypeProvider>::get(), + cppu::UnoType<beans::XPropertySet>::get(), + cppu::UnoType<beans::XPropertyState>::get(), + cppu::UnoType<beans::XMultiPropertySet>::get() }; + return aTypes; +} + +uno::Sequence< sal_Int8 > SAL_CALL GraphicDescriptor::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + + +rtl::Reference<::comphelper::PropertySetInfo> GraphicDescriptor::createPropertySetInfo() +{ + static ::comphelper::PropertyMapEntry const aEntries[] = + { + { OUString( "GraphicType" ), static_cast< sal_Int32 >( UnoGraphicProperty::GraphicType ), cppu::UnoType< sal_Int8 >::get(), beans::PropertyAttribute::READONLY, 0 }, + { OUString( "MimeType" ), static_cast< sal_Int32 >( UnoGraphicProperty::MimeType ), cppu::UnoType< OUString >::get(), beans::PropertyAttribute::READONLY, 0 }, + { OUString( "SizePixel" ), static_cast< sal_Int32 >( UnoGraphicProperty::SizePixel ), cppu::UnoType< awt::Size >::get(), beans::PropertyAttribute::READONLY, 0 }, + { OUString( "Size100thMM" ), static_cast< sal_Int32 >( UnoGraphicProperty::Size100thMM ), cppu::UnoType< awt::Size >::get(), beans::PropertyAttribute::READONLY, 0 }, + { OUString( "BitsPerPixel" ), static_cast< sal_Int32 >( UnoGraphicProperty::BitsPerPixel ), cppu::UnoType< sal_uInt8 >::get(), beans::PropertyAttribute::READONLY, 0 }, + { OUString( "Transparent" ), static_cast< sal_Int32 >( UnoGraphicProperty::Transparent ), cppu::UnoType< sal_Bool >::get(), beans::PropertyAttribute::READONLY, 0 }, + { OUString( "Alpha" ), static_cast< sal_Int32 >( UnoGraphicProperty::Alpha ), cppu::UnoType< sal_Bool >::get(), beans::PropertyAttribute::READONLY, 0 }, + { OUString( "Animated" ), static_cast< sal_Int32 >( UnoGraphicProperty::Animated ), cppu::UnoType< sal_Bool >::get(), beans::PropertyAttribute::READONLY, 0 }, + { OUString("Linked"), sal_Int32(UnoGraphicProperty::Linked), cppu::UnoType<sal_Bool>::get(), beans::PropertyAttribute::READONLY, 0 }, + { OUString("OriginURL"), sal_Int32(UnoGraphicProperty::OriginURL), cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0 }, + }; + + return rtl::Reference<::comphelper::PropertySetInfo>( new ::comphelper::PropertySetInfo(aEntries) ); +} + + +void GraphicDescriptor::_setPropertyValues( const comphelper::PropertyMapEntry** /*ppEntries*/, const uno::Any* /*pValues*/ ) +{ + // we only have readonly attributes +} + + +void GraphicDescriptor::_getPropertyValues( const comphelper::PropertyMapEntry** ppEntries, uno::Any* pValues ) +{ + SolarMutexGuard aGuard; + + while( *ppEntries ) + { + UnoGraphicProperty theProperty = static_cast< UnoGraphicProperty >( (*ppEntries)->mnHandle ); + switch( theProperty ) + { + case UnoGraphicProperty::GraphicType: + { + const GraphicType eType( mpGraphic ? mpGraphic->GetType() : meType ); + + *pValues <<= ( eType == GraphicType::Bitmap ? graphic::GraphicType::PIXEL : + ( eType == GraphicType::GdiMetafile ? graphic::GraphicType::VECTOR : + graphic::GraphicType::EMPTY ) ); + } + break; + + case UnoGraphicProperty::MimeType: + { + OUString aMimeType; + + if( mpGraphic ) + { + if( mpGraphic->IsGfxLink() ) + { + GfxLink aLink = mpGraphic->GetGfxLink(); + switch (aLink.GetType()) + { + case GfxLinkType::NativeGif: aMimeType = MIMETYPE_GIF; break; + + // #i15508# added BMP type for better exports (checked, works) + case GfxLinkType::NativeBmp: aMimeType = MIMETYPE_BMP; break; + + case GfxLinkType::NativeJpg: aMimeType = MIMETYPE_JPG; break; + case GfxLinkType::NativePng: aMimeType = MIMETYPE_PNG; break; + case GfxLinkType::NativeTif: aMimeType = MIMETYPE_TIF; break; + case GfxLinkType::NativeWmf: aMimeType = aLink.IsEMF() ? MIMETYPE_EMF : MIMETYPE_WMF; break; + case GfxLinkType::NativeMet: aMimeType = MIMETYPE_MET; break; + case GfxLinkType::NativePct: aMimeType = MIMETYPE_PCT; break; + case GfxLinkType::NativeWebp: aMimeType = MIMETYPE_WEBP; break; + + // added Svg mimetype support + case GfxLinkType::NativeSvg: aMimeType = MIMETYPE_SVG; break; + case GfxLinkType::NativePdf: aMimeType = MIMETYPE_PDF; break; + + default: + break; + } + } + + if( aMimeType.isEmpty() && ( mpGraphic->GetType() != GraphicType::NONE ) ) + aMimeType = MIMETYPE_VCLGRAPHIC; + } + else + aMimeType = maMimeType; + + *pValues <<= aMimeType; + } + break; + + case UnoGraphicProperty::SizePixel: + { + awt::Size aAWTSize( 0, 0 ); + + if( mpGraphic ) + { + if( mpGraphic->GetType() == GraphicType::Bitmap ) + { + const Size aSizePix( mpGraphic->GetSizePixel() ); + aAWTSize = awt::Size( aSizePix.Width(), aSizePix.Height() ); + } + } + else + aAWTSize = awt::Size( maSizePixel.Width(), maSizePixel.Height() ); + + *pValues <<= aAWTSize; + } + break; + + case UnoGraphicProperty::Size100thMM: + { + awt::Size aAWTSize( 0, 0 ); + + if( mpGraphic ) + { + if( mpGraphic->GetPrefMapMode().GetMapUnit() != MapUnit::MapPixel ) + { + const Size aSizeLog( OutputDevice::LogicToLogic( + mpGraphic->GetPrefSize(), + mpGraphic->GetPrefMapMode(), + MapMode(MapUnit::Map100thMM)) ); + aAWTSize = awt::Size( aSizeLog.Width(), aSizeLog.Height() ); + } + } + else + aAWTSize = awt::Size( maSize100thMM.Width(), maSize100thMM.Height() ); + + *pValues <<= aAWTSize; + } + break; + + case UnoGraphicProperty::BitsPerPixel: + { + sal_uInt16 nBitsPerPixel = 0; + + if( mpGraphic ) + { + if( mpGraphic->GetType() == GraphicType::Bitmap ) + { + auto ePixelFormat = mpGraphic->GetBitmapEx().GetBitmap().getPixelFormat(); + nBitsPerPixel = vcl::pixelFormatBitCount(ePixelFormat); + } + } + else + nBitsPerPixel = mnBitsPerPixel; + + *pValues <<= sal::static_int_cast< sal_Int8 >(nBitsPerPixel); + } + break; + + case UnoGraphicProperty::Transparent: + { + *pValues <<= mpGraphic ? mpGraphic->IsTransparent() : mbTransparent; + } + break; + + case UnoGraphicProperty::Alpha: + { + *pValues <<= mpGraphic && mpGraphic->IsAlpha(); + } + break; + + case UnoGraphicProperty::Animated: + { + *pValues <<= mpGraphic && mpGraphic->IsAnimated(); + } + break; + + case UnoGraphicProperty::Linked: + { + *pValues <<= mpGraphic && !mpGraphic->getOriginURL().isEmpty(); + } + break; + + case UnoGraphicProperty::OriginURL: + { + OUString aOriginURL; + if (mpGraphic) + aOriginURL = mpGraphic->getOriginURL(); + + *pValues <<= aOriginURL; + } + break; + } + + ++ppEntries; + ++pValues; + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/graphic/UnoGraphicMapper.cxx b/vcl/source/graphic/UnoGraphicMapper.cxx new file mode 100644 index 0000000000..6bde780976 --- /dev/null +++ b/vcl/source/graphic/UnoGraphicMapper.cxx @@ -0,0 +1,87 @@ +/* -*- 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/. + */ + +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/graphic/XGraphicMapper.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/unique_disposing_ptr.hxx> + +#include <memory> +#include <unordered_map> + +using namespace css; + +namespace +{ +class GraphicMapper : public cppu::WeakImplHelper<graphic::XGraphicMapper, lang::XServiceInfo> +{ +private: + std::unordered_map<OUString, uno::Reference<graphic::XGraphic>> maGraphicMap; + +public: + GraphicMapper() = default; + +protected: + // XServiceInfo + OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.comp.graphic.GraphicMapper"; + } + sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override + { + return { "com.sun.star.graphic.GraphicMapper" }; + } + + // XTypeProvider + css::uno::Sequence<css::uno::Type> SAL_CALL getTypes() override + { + static const uno::Sequence<uno::Type> aTypes{ + cppu::UnoType<lang::XServiceInfo>::get(), cppu::UnoType<lang::XTypeProvider>::get(), + cppu::UnoType<graphic::XGraphicMapper>::get() + }; + return aTypes; + } + css::uno::Sequence<sal_Int8> SAL_CALL getImplementationId() override + { + return css::uno::Sequence<sal_Int8>(); + } + + // XGraphicMapper + css::uno::Reference<css::graphic::XGraphic> SAL_CALL findGraphic(const OUString& rId) override + { + auto aIterator = maGraphicMap.find(rId); + + if (aIterator == maGraphicMap.end()) + return css::uno::Reference<css::graphic::XGraphic>(); + + return aIterator->second; + } + void SAL_CALL putGraphic(const OUString& rId, + css::uno::Reference<css::graphic::XGraphic> const& rGraphic) override + { + maGraphicMap.emplace(rId, rGraphic); + } +}; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_graphic_GraphicMapper_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new GraphicMapper); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/graphic/UnoGraphicObject.cxx b/vcl/source/graphic/UnoGraphicObject.cxx new file mode 100644 index 0000000000..73bbcef3ec --- /dev/null +++ b/vcl/source/graphic/UnoGraphicObject.cxx @@ -0,0 +1,101 @@ +/* -*- 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 <memory> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <com/sun/star/graphic/XGraphicObject.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <vcl/GraphicObject.hxx> +#include <mutex> + +using namespace css; + +namespace { + +typedef ::cppu::WeakImplHelper<graphic::XGraphicObject, css::lang::XServiceInfo> GraphicObject_BASE; + + // Simple uno wrapper around the GraphicObject class to allow basic + // access. ( and solves a horrible cyclic link problem between + // goodies/toolkit/extensions ) +class GraphicObjectImpl : public GraphicObject_BASE +{ + std::mutex m_aMutex; + std::optional<GraphicObject> mpGraphicObject; + +public: + /// @throws uno::RuntimeException + explicit GraphicObjectImpl(uno::Sequence<uno::Any> const & rArgs); + + // XGraphicObject + virtual uno::Reference<graphic::XGraphic> SAL_CALL getGraphic() override; + virtual void SAL_CALL setGraphic(uno::Reference<graphic::XGraphic> const & rxGraphic) override; + + virtual OUString SAL_CALL getImplementationName() override + { + return "com.sun.star.graphic.GraphicObject"; + } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override + { + return { "com.sun.star.graphic.GraphicObject" }; + } +}; + +GraphicObjectImpl::GraphicObjectImpl(const uno::Sequence<uno::Any>& /*rArgs*/) +{ + mpGraphicObject.emplace(); +} + +uno::Reference<graphic::XGraphic> SAL_CALL GraphicObjectImpl::getGraphic() +{ + std::scoped_lock aGuard(m_aMutex); + + if (!mpGraphicObject) + throw uno::RuntimeException(); + return mpGraphicObject->GetGraphic().GetXGraphic(); +} + +void SAL_CALL GraphicObjectImpl::setGraphic(uno::Reference<graphic::XGraphic> const & rxGraphic) +{ + std::scoped_lock aGuard(m_aMutex); + + if (!mpGraphicObject) + throw uno::RuntimeException(); + Graphic aGraphic(rxGraphic); + mpGraphicObject->SetGraphic(aGraphic); +} + +} // end anonymous namespace + +extern "C" SAL_DLLPUBLIC_EXPORT +css::uno::XInterface* com_sun_star_graphic_GraphicObject_get_implementation( + SAL_UNUSED_PARAMETER uno::XComponentContext*, + uno::Sequence<uno::Any> const & rArguments) +{ + return cppu::acquire(new GraphicObjectImpl(rArguments)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/graphic/UnoGraphicProvider.cxx b/vcl/source/graphic/UnoGraphicProvider.cxx new file mode 100644 index 0000000000..bceb9fbabe --- /dev/null +++ b/vcl/source/graphic/UnoGraphicProvider.cxx @@ -0,0 +1,837 @@ +/* -*- 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 <o3tl/string_view.hxx> +#include <vcl/svapp.hxx> +#include <vcl/image.hxx> +#include <vcl/metaact.hxx> +#include <imagerepository.hxx> +#include <tools/fract.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <vcl/graphicfilter.hxx> +#include <vcl/stdtext.hxx> +#include <vcl/wmfexternal.hxx> +#include <vcl/virdev.hxx> +#include <com/sun/star/awt/XBitmap.hpp> +#include <com/sun/star/graphic/XGraphicProvider2.hpp> +#include <com/sun/star/io/XStream.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/text/GraphicCrop.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <comphelper/fileformat.h> +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <sal/log.hxx> + +#include <graphic/UnoGraphicDescriptor.hxx> +#include <graphic/UnoGraphic.hxx> +#include <rtl/ref.hxx> +#include <vcl/dibtools.hxx> +#include <comphelper/sequence.hxx> +#include <memory> +#include <string_view> + +#include <vcl/TypeSerializer.hxx> + +using namespace com::sun::star; + +namespace { + +class GraphicProvider : public ::cppu::WeakImplHelper< css::graphic::XGraphicProvider2, + css::lang::XServiceInfo > +{ +public: + + GraphicProvider(); + +protected: + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override; + + // XGraphicProvider + virtual css::uno::Reference< css::beans::XPropertySet > SAL_CALL queryGraphicDescriptor( const css::uno::Sequence< css::beans::PropertyValue >& MediaProperties ) override; + virtual css::uno::Reference< css::graphic::XGraphic > SAL_CALL queryGraphic( const css::uno::Sequence< css::beans::PropertyValue >& MediaProperties ) override; + virtual void SAL_CALL storeGraphic( const css::uno::Reference< css::graphic::XGraphic >& Graphic, const css::uno::Sequence< css::beans::PropertyValue >& MediaProperties ) override; + + // XGraphicProvider2 + uno::Sequence< uno::Reference<graphic::XGraphic> > SAL_CALL queryGraphics(const uno::Sequence< uno::Sequence<beans::PropertyValue> >& MediaPropertiesSeq ) override; + +private: + + static css::uno::Reference< css::graphic::XGraphic > implLoadMemory( std::u16string_view rResourceURL ); + static css::uno::Reference< css::graphic::XGraphic > implLoadRepositoryImage( std::u16string_view rResourceURL ); + static css::uno::Reference< css::graphic::XGraphic > implLoadBitmap( const css::uno::Reference< css::awt::XBitmap >& rBitmap ); + static css::uno::Reference< css::graphic::XGraphic > implLoadStandardImage( std::u16string_view rResourceURL ); +}; + +GraphicProvider::GraphicProvider() +{ +} + +OUString SAL_CALL GraphicProvider::getImplementationName() +{ + return "com.sun.star.comp.graphic.GraphicProvider"; +} + +sal_Bool SAL_CALL GraphicProvider::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +uno::Sequence< OUString > SAL_CALL GraphicProvider::getSupportedServiceNames() +{ + return { "com.sun.star.graphic.GraphicProvider" }; +} + +uno::Sequence< uno::Type > SAL_CALL GraphicProvider::getTypes() +{ + static const uno::Sequence< uno::Type > aTypes { + cppu::UnoType<lang::XServiceInfo>::get(), + cppu::UnoType<lang::XTypeProvider>::get(), + cppu::UnoType<graphic::XGraphicProvider>::get() + }; + return aTypes; +} + +uno::Sequence< sal_Int8 > SAL_CALL GraphicProvider::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +uno::Reference< ::graphic::XGraphic > GraphicProvider::implLoadMemory( std::u16string_view rResourceURL ) +{ + uno::Reference< ::graphic::XGraphic > xRet; + sal_Int32 nIndex = 0; + + if( o3tl::getToken(rResourceURL, 0, '/', nIndex ) == u"private:memorygraphic" ) + { + sal_Int64 nGraphicAddress = o3tl::toInt64(o3tl::getToken(rResourceURL, 0, '/', nIndex )); + + if( nGraphicAddress ) + { + rtl::Reference<::unographic::Graphic> pUnoGraphic = new ::unographic::Graphic; + + pUnoGraphic->init( *reinterpret_cast< ::Graphic* >( nGraphicAddress ) ); + xRet = pUnoGraphic; + } + } + + return xRet; +} + +uno::Reference< ::graphic::XGraphic > GraphicProvider::implLoadRepositoryImage( std::u16string_view rResourceURL ) +{ + uno::Reference< ::graphic::XGraphic > xRet; + + std::u16string_view sPathName; + if( o3tl::starts_with(rResourceURL, u"private:graphicrepository/", &sPathName) ) + { + BitmapEx aBitmap; + if ( vcl::ImageRepository::loadImage( OUString(sPathName), aBitmap ) ) + { + Graphic aGraphic(aBitmap); + aGraphic.setOriginURL(OUString(rResourceURL)); + xRet = aGraphic.GetXGraphic(); + } + } + return xRet; +} + +uno::Reference< ::graphic::XGraphic > GraphicProvider::implLoadStandardImage( std::u16string_view rResourceURL ) +{ + uno::Reference< ::graphic::XGraphic > xRet; + + std::u16string_view sImageName; + if( o3tl::starts_with(rResourceURL, u"private:standardimage/", &sImageName) ) + { + if ( sImageName == u"info" ) + { + xRet = Graphic(GetStandardInfoBoxImage().GetBitmapEx()).GetXGraphic(); + } + else if ( sImageName == u"warning" ) + { + xRet = Graphic(GetStandardWarningBoxImage().GetBitmapEx()).GetXGraphic(); + } + else if ( sImageName == u"error" ) + { + xRet = Graphic(GetStandardErrorBoxImage().GetBitmapEx()).GetXGraphic(); + } + else if ( sImageName == u"query" ) + { + xRet = Graphic(GetStandardQueryBoxImage().GetBitmapEx()).GetXGraphic(); + } + } + return xRet; +} + + +uno::Reference< ::graphic::XGraphic > GraphicProvider::implLoadBitmap( const uno::Reference< awt::XBitmap >& xBtm ) +{ + uno::Reference< ::graphic::XGraphic > xRet; + uno::Sequence< sal_Int8 > aBmpSeq( xBtm->getDIB() ); + uno::Sequence< sal_Int8 > aMaskSeq( xBtm->getMaskDIB() ); + SvMemoryStream aBmpStream( aBmpSeq.getArray(), aBmpSeq.getLength(), StreamMode::READ ); + Bitmap aBmp; + BitmapEx aBmpEx; + + ReadDIB(aBmp, aBmpStream, true); + + if( aMaskSeq.hasElements() ) + { + SvMemoryStream aMaskStream( aMaskSeq.getArray(), aMaskSeq.getLength(), StreamMode::READ ); + Bitmap aMask; + + ReadDIB(aMask, aMaskStream, true); + aBmpEx = BitmapEx( aBmp, aMask ); + } + else + aBmpEx = BitmapEx( aBmp ); + + if( !aBmpEx.IsEmpty() ) + { + rtl::Reference<::unographic::Graphic> pUnoGraphic = new ::unographic::Graphic; + + pUnoGraphic->init( aBmpEx ); + xRet = pUnoGraphic; + } + return xRet; +} + +uno::Reference< beans::XPropertySet > SAL_CALL GraphicProvider::queryGraphicDescriptor( const uno::Sequence< beans::PropertyValue >& rMediaProperties ) +{ + uno::Reference< beans::XPropertySet > xRet; + + OUString aURL; + uno::Reference< io::XInputStream > xIStm; + uno::Reference< awt::XBitmap >xBtm; + + for( const auto& rMediaProperty : rMediaProperties ) + { + if (xRet.is()) + break; + + const OUString aName( rMediaProperty.Name ); + const uno::Any aValue( rMediaProperty.Value ); + + if (aName == "URL") + { + aValue >>= aURL; + } + else if (aName == "InputStream") + { + aValue >>= xIStm; + } + else if (aName == "Bitmap") + { + aValue >>= xBtm; + } + } + + SolarMutexGuard g; + + if( xIStm.is() ) + { + rtl::Reference<unographic::GraphicDescriptor> pDescriptor = new unographic::GraphicDescriptor; + pDescriptor->init( xIStm, aURL ); + xRet = pDescriptor; + } + else if( !aURL.isEmpty() ) + { + uno::Reference< ::graphic::XGraphic > xGraphic( implLoadMemory( aURL ) ); + + if ( !xGraphic.is() ) + xGraphic = implLoadRepositoryImage( aURL ); + + if ( !xGraphic.is() ) + xGraphic = implLoadStandardImage( aURL ); + + if( xGraphic.is() ) + { + xRet.set( xGraphic, uno::UNO_QUERY ); + } + else + { + rtl::Reference<unographic::GraphicDescriptor> pDescriptor = new unographic::GraphicDescriptor; + pDescriptor->init( aURL ); + xRet = pDescriptor; + } + } + else if( xBtm.is() ) + { + uno::Reference< ::graphic::XGraphic > xGraphic( implLoadBitmap( xBtm ) ); + if( xGraphic.is() ) + xRet.set( xGraphic, uno::UNO_QUERY ); + } + + return xRet; +} + + +uno::Reference< ::graphic::XGraphic > SAL_CALL GraphicProvider::queryGraphic( const uno::Sequence< ::beans::PropertyValue >& rMediaProperties ) +{ + uno::Reference< ::graphic::XGraphic > xRet; + OUString aPath; + + uno::Reference< io::XInputStream > xIStm; + uno::Reference< awt::XBitmap >xBtm; + + uno::Sequence< ::beans::PropertyValue > aFilterData; + + bool bLazyRead = false; + bool bLoadAsLink = false; + + for (const auto& rMediaProperty : rMediaProperties) + { + if (xRet.is()) + break; + + const OUString aName( rMediaProperty.Name ); + const uno::Any aValue( rMediaProperty.Value ); + + if (aName == "URL") + { + OUString aURL; + aValue >>= aURL; + aPath = aURL; + } + else if (aName == "InputStream") + { + aValue >>= xIStm; + } + else if (aName == "Bitmap") + { + aValue >>= xBtm; + } + else if (aName == "FilterData") + { + aValue >>= aFilterData; + } + else if (aName == "LazyRead") + { + aValue >>= bLazyRead; + } + else if (aName == "LoadAsLink") + { + aValue >>= bLoadAsLink; + } + } + + // Check for the goal width and height if they are defined + sal_uInt16 nExtWidth = 0; + sal_uInt16 nExtHeight = 0; + sal_uInt16 nExtMapMode = 0; + for( const auto& rProp : std::as_const(aFilterData) ) + { + const OUString aName( rProp.Name ); + const uno::Any aValue( rProp.Value ); + + if (aName == "ExternalWidth") + { + aValue >>= nExtWidth; + } + else if (aName == "ExternalHeight") + { + aValue >>= nExtHeight; + } + else if (aName == "ExternalMapMode") + { + aValue >>= nExtMapMode; + } + } + + SolarMutexGuard g; + + std::unique_ptr<SvStream> pIStm; + + if( xIStm.is() ) + { + pIStm = ::utl::UcbStreamHelper::CreateStream( xIStm ); + } + else if( !aPath.isEmpty() ) + { + xRet = implLoadMemory( aPath ); + + if ( !xRet.is() ) + xRet = implLoadRepositoryImage( aPath ); + + if ( !xRet.is() ) + xRet = implLoadStandardImage( aPath ); + + if( !xRet.is() ) + pIStm = ::utl::UcbStreamHelper::CreateStream( aPath, StreamMode::READ ); + } + else if( xBtm.is() ) + { + xRet = implLoadBitmap( xBtm ); + } + + if( pIStm ) + { + ::GraphicFilter& rFilter = ::GraphicFilter::GetGraphicFilter(); + + { + Graphic aVCLGraphic; + + // Define APM Header if goal height and width are defined + WmfExternal aExtHeader; + aExtHeader.xExt = nExtWidth; + aExtHeader.yExt = nExtHeight; + aExtHeader.mapMode = nExtMapMode; + if ( nExtMapMode > 0 ) + { + bLazyRead = false; + } + + ErrCode error = ERRCODE_NONE; + if (bLazyRead) + { + Graphic aGraphic = rFilter.ImportUnloadedGraphic(*pIStm); + if (!aGraphic.IsNone()) + aVCLGraphic = aGraphic; + } + if (aVCLGraphic.IsNone()) + error = rFilter.ImportGraphic(aVCLGraphic, aPath, *pIStm, GRFILTER_FORMAT_DONTKNOW, nullptr, GraphicFilterImportFlags::NONE); + + if( (error == ERRCODE_NONE ) && + ( aVCLGraphic.GetType() != GraphicType::NONE ) ) + { + if (!aPath.isEmpty() && bLoadAsLink) + aVCLGraphic.setOriginURL(aPath); + + rtl::Reference<::unographic::Graphic> pUnoGraphic = new ::unographic::Graphic; + + pUnoGraphic->init( aVCLGraphic ); + xRet = pUnoGraphic; + } + else{ + SAL_WARN("svtools", "Could not create graphic for:" << aPath << " error: " << error); + } + } + } + + return xRet; +} + +uno::Sequence< uno::Reference<graphic::XGraphic> > SAL_CALL GraphicProvider::queryGraphics(const uno::Sequence< uno::Sequence<beans::PropertyValue> >& rMediaPropertiesSeq) +{ + // Turn properties into streams. + std::vector< std::unique_ptr<SvStream> > aStreams; + for (const auto& rMediaProperties : rMediaPropertiesSeq) + { + std::unique_ptr<SvStream> pStream; + uno::Reference<io::XInputStream> xStream; + + auto pProp = std::find_if(rMediaProperties.begin(), rMediaProperties.end(), + [](const beans::PropertyValue& rProp) { return rProp.Name == "InputStream"; }); + if (pProp != rMediaProperties.end()) + { + pProp->Value >>= xStream; + if (xStream.is()) + pStream = utl::UcbStreamHelper::CreateStream(xStream); + } + + aStreams.push_back(std::move(pStream)); + } + + // Import: streams to graphics. + std::vector< std::shared_ptr<Graphic> > aGraphics; + GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter(); + rFilter.ImportGraphics(aGraphics, std::move(aStreams)); + + // Returning: graphics to UNO objects. + std::vector< uno::Reference<graphic::XGraphic> > aRet; + for (const auto& pGraphic : aGraphics) + { + uno::Reference<graphic::XGraphic> xGraphic; + + if (pGraphic) + { + rtl::Reference<unographic::Graphic> pUnoGraphic = new unographic::Graphic(); + pUnoGraphic->init(*pGraphic); + xGraphic = pUnoGraphic; + } + + aRet.push_back(xGraphic); + } + + return comphelper::containerToSequence(aRet); +} + +void ImplCalculateCropRect( ::Graphic const & rGraphic, const text::GraphicCrop& rGraphicCropLogic, tools::Rectangle& rGraphicCropPixel ) +{ + if ( !(rGraphicCropLogic.Left || rGraphicCropLogic.Top || rGraphicCropLogic.Right || rGraphicCropLogic.Bottom) ) + return; + + Size aSourceSizePixel( rGraphic.GetSizePixel() ); + if ( !(aSourceSizePixel.Width() && aSourceSizePixel.Height()) ) + return; + + if ( !(rGraphicCropLogic.Left || rGraphicCropLogic.Top || rGraphicCropLogic.Right || rGraphicCropLogic.Bottom) ) + return; + + Size aSize100thMM( 0, 0 ); + if( rGraphic.GetPrefMapMode().GetMapUnit() != MapUnit::MapPixel ) + { + aSize100thMM = OutputDevice::LogicToLogic(rGraphic.GetPrefSize(), rGraphic.GetPrefMapMode(), MapMode(MapUnit::Map100thMM)); + } + else + { + aSize100thMM = Application::GetDefaultDevice()->PixelToLogic(rGraphic.GetPrefSize(), MapMode(MapUnit::Map100thMM)); + } + if ( aSize100thMM.Width() && aSize100thMM.Height() ) + { + double fSourceSizePixelWidth = static_cast<double>(aSourceSizePixel.Width()); + double fSourceSizePixelHeight= static_cast<double>(aSourceSizePixel.Height()); + rGraphicCropPixel.SetLeft( static_cast< sal_Int32 >((fSourceSizePixelWidth * rGraphicCropLogic.Left ) / aSize100thMM.Width()) ); + rGraphicCropPixel.SetTop( static_cast< sal_Int32 >((fSourceSizePixelHeight * rGraphicCropLogic.Top ) / aSize100thMM.Height()) ); + rGraphicCropPixel.SetRight( static_cast< sal_Int32 >(( fSourceSizePixelWidth * ( aSize100thMM.Width() - rGraphicCropLogic.Right ) ) / aSize100thMM.Width() ) ); + rGraphicCropPixel.SetBottom( static_cast< sal_Int32 >(( fSourceSizePixelHeight * ( aSize100thMM.Height() - rGraphicCropLogic.Bottom ) ) / aSize100thMM.Height() ) ); + } +} + +void ImplApplyBitmapScaling( ::Graphic& rGraphic, sal_Int32 nPixelWidth, sal_Int32 nPixelHeight ) +{ + if ( nPixelWidth && nPixelHeight ) + { + BitmapEx aBmpEx( rGraphic.GetBitmapEx() ); + MapMode aPrefMapMode( aBmpEx.GetPrefMapMode() ); + Size aPrefSize( aBmpEx.GetPrefSize() ); + aBmpEx.Scale( Size( nPixelWidth, nPixelHeight ) ); + aBmpEx.SetPrefMapMode( aPrefMapMode ); + aBmpEx.SetPrefSize( aPrefSize ); + rGraphic = aBmpEx; + } +} + +void ImplApplyBitmapResolution( ::Graphic& rGraphic, sal_Int32 nImageResolution, const Size& rVisiblePixelSize, const awt::Size& rLogicalSize ) +{ + if ( !(nImageResolution && rLogicalSize.Width && rLogicalSize.Height) ) + return; + + const double fImageResolution = static_cast<double>( nImageResolution ); + const double fSourceDPIX = ( static_cast<double>(rVisiblePixelSize.Width()) * 2540.0 ) / static_cast<double>(rLogicalSize.Width); + const double fSourceDPIY = ( static_cast<double>(rVisiblePixelSize.Height()) * 2540.0 ) / static_cast<double>(rLogicalSize.Height); + const sal_Int32 nSourcePixelWidth( rGraphic.GetSizePixel().Width() ); + const sal_Int32 nSourcePixelHeight( rGraphic.GetSizePixel().Height() ); + const double fSourcePixelWidth = static_cast<double>( nSourcePixelWidth ); + const double fSourcePixelHeight= static_cast<double>( nSourcePixelHeight ); + + sal_Int32 nDestPixelWidth = nSourcePixelWidth; + sal_Int32 nDestPixelHeight = nSourcePixelHeight; + + // check, if the bitmap DPI exceeds the maximum DPI + if( fSourceDPIX > fImageResolution ) + { + nDestPixelWidth = static_cast<sal_Int32>(( fSourcePixelWidth * fImageResolution ) / fSourceDPIX); + if ( !nDestPixelWidth || ( nDestPixelWidth > nSourcePixelWidth ) ) + nDestPixelWidth = nSourcePixelWidth; + } + if ( fSourceDPIY > fImageResolution ) + { + nDestPixelHeight= static_cast<sal_Int32>(( fSourcePixelHeight* fImageResolution ) / fSourceDPIY); + if ( !nDestPixelHeight || ( nDestPixelHeight > nSourcePixelHeight ) ) + nDestPixelHeight = nSourcePixelHeight; + } + if ( ( nDestPixelWidth != nSourcePixelWidth ) || ( nDestPixelHeight != nSourcePixelHeight ) ) + ImplApplyBitmapScaling( rGraphic, nDestPixelWidth, nDestPixelHeight ); +} + +void ImplApplyFilterData( ::Graphic& rGraphic, const uno::Sequence< beans::PropertyValue >& rFilterData ) +{ + /* this method applies following attributes to the graphic, in the first step the + cropping area (logical size in 100thmm) is applied, in the second step the resolution + is applied, in the third step the graphic is scaled to the corresponding pixelsize. + if a parameter value is zero or not available the corresponding step will be skipped */ + + sal_Int32 nPixelWidth = 0; + sal_Int32 nPixelHeight= 0; + sal_Int32 nImageResolution = 0; + awt::Size aLogicalSize( 0, 0 ); + text::GraphicCrop aCropLogic( 0, 0, 0, 0 ); + bool bRemoveCropArea = true; + + for( const auto& rProp : rFilterData ) + { + const OUString aName( rProp.Name ); + const uno::Any aValue( rProp.Value ); + + if (aName == "PixelWidth") + aValue >>= nPixelWidth; + else if (aName == "PixelHeight") + aValue >>= nPixelHeight; + else if (aName == "LogicalSize") + aValue >>= aLogicalSize; + else if (aName == "GraphicCropLogic") + aValue >>= aCropLogic; + else if (aName == "RemoveCropArea") + aValue >>= bRemoveCropArea; + else if (aName == "ImageResolution") + aValue >>= nImageResolution; + } + if ( rGraphic.GetType() == GraphicType::Bitmap ) + { + if(rGraphic.getVectorGraphicData()) + { + // embedded Vector Graphic Data, no need to scale. Also no method to apply crop data currently + } + else + { + tools::Rectangle aCropPixel( Point( 0, 0 ), rGraphic.GetSizePixel() ); + ImplCalculateCropRect( rGraphic, aCropLogic, aCropPixel ); + if ( bRemoveCropArea ) + { + BitmapEx aBmpEx( rGraphic.GetBitmapEx() ); + aBmpEx.Crop( aCropPixel ); + rGraphic = aBmpEx; + } + Size aVisiblePixelSize( bRemoveCropArea ? rGraphic.GetSizePixel() : aCropPixel.GetSize() ); + ImplApplyBitmapResolution( rGraphic, nImageResolution, aVisiblePixelSize, aLogicalSize ); + ImplApplyBitmapScaling( rGraphic, nPixelWidth, nPixelHeight ); + } + } + else if ( ( rGraphic.GetType() == GraphicType::GdiMetafile ) && nImageResolution ) + { + ScopedVclPtrInstance< VirtualDevice > aDummyVDev; + GDIMetaFile aMtf( rGraphic.GetGDIMetaFile() ); + Size aMtfSize( OutputDevice::LogicToLogic(aMtf.GetPrefSize(), aMtf.GetPrefMapMode(), MapMode(MapUnit::Map100thMM)) ); + if ( aMtfSize.Width() && aMtfSize.Height() ) + { + MapMode aNewMapMode( MapUnit::Map100thMM ); + aNewMapMode.SetScaleX( Fraction( aLogicalSize.Width, aMtfSize.Width() ) ); + aNewMapMode.SetScaleY( Fraction( aLogicalSize.Height, aMtfSize.Height() ) ); + aDummyVDev->EnableOutput( false ); + aDummyVDev->SetMapMode( aNewMapMode ); + + for( size_t i = 0, nObjCount = aMtf.GetActionSize(); i < nObjCount; i++ ) + { + MetaAction* pAction = aMtf.GetAction( i ); + switch( pAction->GetType() ) + { + // only optimizing common bitmap actions: + case MetaActionType::MAPMODE: + { + pAction->Execute( aDummyVDev.get() ); + break; + } + case MetaActionType::PUSH: + { + const MetaPushAction* pA = static_cast<const MetaPushAction*>(pAction); + aDummyVDev->Push( pA->GetFlags() ); + break; + } + case MetaActionType::POP: + { + aDummyVDev->Pop(); + break; + } + case MetaActionType::BMPSCALE: + case MetaActionType::BMPEXSCALE: + { + BitmapEx aBmpEx; + Point aPos; + Size aSize; + if ( pAction->GetType() == MetaActionType::BMPSCALE ) + { + MetaBmpScaleAction* pScaleAction = dynamic_cast< MetaBmpScaleAction* >( pAction ); + assert(pScaleAction); + aBmpEx = pScaleAction->GetBitmap(); + aPos = pScaleAction->GetPoint(); + aSize = pScaleAction->GetSize(); + } + else + { + MetaBmpExScaleAction* pScaleAction = dynamic_cast< MetaBmpExScaleAction* >( pAction ); + assert(pScaleAction); + aBmpEx = pScaleAction->GetBitmapEx(); + aPos = pScaleAction->GetPoint(); + aSize = pScaleAction->GetSize(); + } + ::Graphic aGraphic( aBmpEx ); + const Size aSize100thmm( aDummyVDev->LogicToPixel( aSize ) ); + Size aSize100thmm2( aDummyVDev->PixelToLogic(aSize100thmm, MapMode(MapUnit::Map100thMM)) ); + + ImplApplyBitmapResolution( aGraphic, nImageResolution, + aGraphic.GetSizePixel(), awt::Size( aSize100thmm2.Width(), aSize100thmm2.Height() ) ); + + rtl::Reference<MetaAction> pNewAction = new MetaBmpExScaleAction( aPos, aSize, aGraphic.GetBitmapEx() ); + aMtf.ReplaceAction( pNewAction, i ); + break; + } + default: + case MetaActionType::BMP: + case MetaActionType::BMPSCALEPART: + case MetaActionType::BMPEX: + case MetaActionType::BMPEXSCALEPART: + case MetaActionType::MASK: + case MetaActionType::MASKSCALE: + break; + } + } + rGraphic = aMtf; + } + } +} + + +void SAL_CALL GraphicProvider::storeGraphic( const uno::Reference< ::graphic::XGraphic >& rxGraphic, const uno::Sequence< beans::PropertyValue >& rMediaProperties ) +{ + std::unique_ptr<SvStream> pOStm; + OUString aPath; + + for( const auto& rMediaProperty : rMediaProperties ) + { + const OUString aName( rMediaProperty.Name ); + const uno::Any aValue( rMediaProperty.Value ); + + if (aName == "URL") + { + OUString aURL; + + aValue >>= aURL; + pOStm = ::utl::UcbStreamHelper::CreateStream( aURL, StreamMode::WRITE | StreamMode::TRUNC ); + aPath = aURL; + } + else if (aName == "OutputStream") + { + uno::Reference< io::XStream > xOStm; + + aValue >>= xOStm; + + if( xOStm.is() ) + pOStm = ::utl::UcbStreamHelper::CreateStream( xOStm ); + } + + if( pOStm ) + break; + } + + if( !pOStm ) + return; + + uno::Sequence< beans::PropertyValue > aFilterDataSeq; + OUString sFilterShortName; + + for( const auto& rMediaProperty : rMediaProperties ) + { + const OUString aName( rMediaProperty.Name ); + const uno::Any aValue( rMediaProperty.Value ); + + if (aName == "FilterData") + { + aValue >>= aFilterDataSeq; + } + else if (aName == "MimeType") + { + OUString aMimeType; + + aValue >>= aMimeType; + + if (aMimeType == MIMETYPE_BMP) + sFilterShortName = "bmp"; + else if (aMimeType == MIMETYPE_EPS) + sFilterShortName = "eps"; + else if (aMimeType == MIMETYPE_GIF) + sFilterShortName = "gif"; + else if (aMimeType == MIMETYPE_JPG) + sFilterShortName = "jpg"; + else if (aMimeType == MIMETYPE_MET) + sFilterShortName = "met"; + else if (aMimeType == MIMETYPE_PNG) + sFilterShortName = "png"; + else if (aMimeType == MIMETYPE_PCT) + sFilterShortName = "pct"; + else if (aMimeType == MIMETYPE_PBM) + sFilterShortName = "pbm"; + else if (aMimeType == MIMETYPE_PGM) + sFilterShortName = "pgm"; + else if (aMimeType == MIMETYPE_PPM) + sFilterShortName = "ppm"; + else if (aMimeType == MIMETYPE_RAS) + sFilterShortName = "ras"; + else if (aMimeType == MIMETYPE_SVM) + sFilterShortName = "svm"; + else if (aMimeType == MIMETYPE_TIF) + sFilterShortName = "tif"; + else if (aMimeType == MIMETYPE_EMF) + sFilterShortName = "emf"; + else if (aMimeType == MIMETYPE_WMF) + sFilterShortName = "wmf"; + else if (aMimeType == MIMETYPE_XPM) + sFilterShortName = "xpm"; + else if (aMimeType == MIMETYPE_SVG) + sFilterShortName = "svg"; + else if (aMimeType == MIMETYPE_VCLGRAPHIC) + sFilterShortName = MIMETYPE_VCLGRAPHIC; + } + } + + if( sFilterShortName.isEmpty() ) + return; + + ::GraphicFilter& rFilter = ::GraphicFilter::GetGraphicFilter(); + + { + const uno::Reference< XInterface > xIFace( rxGraphic, uno::UNO_QUERY ); + const ::unographic::Graphic* pUnoGraphic = dynamic_cast<::unographic::Graphic*>(xIFace.get()); + const ::Graphic* pGraphic = pUnoGraphic ? &pUnoGraphic->GetGraphic() : nullptr; + + if( pGraphic && ( pGraphic->GetType() != GraphicType::NONE ) ) + { + ::Graphic aGraphic( *pGraphic ); + ImplApplyFilterData( aGraphic, aFilterDataSeq ); + + /* sj: using a temporary memory stream, because some graphic filters are seeking behind + stream end (which leads to an invalid argument exception then). */ + SvMemoryStream aMemStrm; + aMemStrm.SetVersion( SOFFICE_FILEFORMAT_CURRENT ); + if( sFilterShortName == MIMETYPE_VCLGRAPHIC ) + { + TypeSerializer aSerializer(aMemStrm); + aSerializer.writeGraphic(aGraphic); + } + else + { + rFilter.ExportGraphic( aGraphic, aPath, aMemStrm, + rFilter.GetExportFormatNumberForShortName( sFilterShortName ), + ( aFilterDataSeq.hasElements() ? &aFilterDataSeq : nullptr ) ); + } + pOStm->WriteBytes( aMemStrm.GetData(), aMemStrm.TellEnd() ); + } + } +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_graphic_GraphicProvider_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new GraphicProvider); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/graphic/VectorGraphicLoader.cxx b/vcl/source/graphic/VectorGraphicLoader.cxx new file mode 100644 index 0000000000..9976810923 --- /dev/null +++ b/vcl/source/graphic/VectorGraphicLoader.cxx @@ -0,0 +1,26 @@ +/* -*- 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/. + * + */ + +#include <tools/stream.hxx> +#include <graphic/VectorGraphicLoader.hxx> + +namespace vcl +{ +std::shared_ptr<VectorGraphicData> loadVectorGraphic(BinaryDataContainer const& rDataContainer, + VectorGraphicDataType eType) +{ + if (rDataContainer.isEmpty()) + return std::shared_ptr<VectorGraphicData>(); + + return std::make_shared<VectorGraphicData>(rDataContainer, eType); +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/graphic/VectorGraphicSearch.cxx b/vcl/source/graphic/VectorGraphicSearch.cxx new file mode 100644 index 0000000000..c2f6244175 --- /dev/null +++ b/vcl/source/graphic/VectorGraphicSearch.cxx @@ -0,0 +1,307 @@ +/* -*- 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/. + * + */ + +#include <utility> +#include <vcl/VectorGraphicSearch.hxx> + +#include <vcl/filter/PDFiumLibrary.hxx> +#include <tools/UnitConversion.hxx> + +#include <sal/config.h> + +namespace +{ +class SearchContext +{ +private: + std::unique_ptr<vcl::pdf::PDFiumDocument>& mpPdfDocument; + std::unique_ptr<vcl::pdf::PDFiumPage> mpPage; + std::unique_ptr<vcl::pdf::PDFiumTextPage> mpTextPage; + std::unique_ptr<vcl::pdf::PDFiumSearchHandle> mpSearchHandle; + +public: + sal_Int32 mnPageIndex; + int mnCurrentIndex; + OUString maSearchString; + VectorGraphicSearchOptions maOptions; + + SearchContext(std::unique_ptr<vcl::pdf::PDFiumDocument>& pPdfDocument, sal_Int32 nPageIndex) + : mpPdfDocument(pPdfDocument) + , mnPageIndex(nPageIndex) + , mnCurrentIndex(-1) + { + } + + ~SearchContext() + { + if (mpSearchHandle) + mpSearchHandle.reset(); + if (mpTextPage) + mpTextPage.reset(); + if (mpPage) + mpPage.reset(); + } + + basegfx::B2DSize getPageSize() + { + basegfx::B2DSize aSize; + if (!mpPdfDocument) + return aSize; + + basegfx::B2DSize aPDFSize = mpPdfDocument->getPageSize(mnPageIndex); + aSize = basegfx::B2DSize(convertPointToMm100(aPDFSize.getWidth()), + convertPointToMm100(aPDFSize.getHeight())); + return aSize; + } + + bool initialize(OUString const& rSearchString, VectorGraphicSearchOptions const& rOptions) + { + if (!mpPdfDocument) + return false; + + if (rSearchString == maSearchString) + return true; + + if (mpSearchHandle) + mpSearchHandle.reset(); + + if (mpTextPage) + mpTextPage.reset(); + + if (mpPage) + mpPage.reset(); + + maSearchString = rSearchString; + maOptions = rOptions; + + mpPage = mpPdfDocument->openPage(mnPageIndex); + if (!mpPage) + return false; + + mpTextPage = mpPage->getTextPage(); + if (!mpTextPage) + return false; + + // Index where to start to search. -1 => at the end + int nStartIndex = maOptions.meStartPosition == SearchStartPosition::End ? -1 : 0; + + if (mnCurrentIndex >= 0) + nStartIndex = mnCurrentIndex; + + // vcl::pdf::PDFFindFlags::MatchCase, vcl::pdf::PDFFindFlags::MatchWholeWord, vcl::pdf::PDFFindFlags::Consecutive + // vcl::pdf::PDFFindFlags::MatchCase - If not set, it will not match case by default. + // vcl::pdf::PDFFindFlags::MatchWholeWord - If not set, it will not match the whole word by default. + // vcl::pdf::PDFFindFlags::Consecutive - If not set, it will skip past the current match to look for the next match. + vcl::pdf::PDFFindFlags nSearchFlags{}; + if (maOptions.mbMatchCase) + nSearchFlags |= vcl::pdf::PDFFindFlags::MatchCase; + if (maOptions.mbMatchWholeWord) + nSearchFlags |= vcl::pdf::PDFFindFlags::MatchWholeWord; + + mpSearchHandle = mpTextPage->findStart(maSearchString, nSearchFlags, nStartIndex); + + return mpSearchHandle != nullptr; + } + + bool next() + { + if (mpSearchHandle && mpSearchHandle->findNext()) + { + mnCurrentIndex = index(); + return true; + } + return false; + } + + bool previous() + { + if (mpSearchHandle && mpSearchHandle->findPrev()) + { + mnCurrentIndex = index(); + return true; + } + return false; + } + + int index() + { + if (mpSearchHandle) + return mpSearchHandle->getSearchResultIndex(); + return -1; + } + + int size() + { + if (mpSearchHandle) + return mpSearchHandle->getSearchCount(); + return -1; + } + + std::vector<basegfx::B2DRectangle> getTextRectangles() + { + std::vector<basegfx::B2DRectangle> aRectangles; + + if (!mpTextPage || !mpSearchHandle) + return aRectangles; + + int nIndex = index(); + if (nIndex < 0) + return aRectangles; + + int nSize = size(); + if (nSize <= 0) + return aRectangles; + + double fPageHeight = getPageSize().getHeight(); + + for (int nCount = 0; nCount < nSize; nCount++) + { + basegfx::B2DRectangle aRectangle = mpTextPage->getCharBox(nIndex + nCount, fPageHeight); + if (!aRectangle.isEmpty()) + { + aRectangles.push_back(aRectangle); + } + } + + return aRectangles; + } +}; + +} // end anonymous namespace + +class VectorGraphicSearch::Implementation +{ +public: + std::shared_ptr<vcl::pdf::PDFium> mpPDFium; + std::unique_ptr<vcl::pdf::PDFiumDocument> mpPdfDocument; + + std::unique_ptr<SearchContext> mpSearchContext; + + Implementation() + : mpPDFium(vcl::pdf::PDFiumLibrary::get()) + { + } + + ~Implementation() { mpSearchContext.reset(); } +}; + +VectorGraphicSearch::VectorGraphicSearch(Graphic aGraphic) + : mpImplementation(std::make_unique<VectorGraphicSearch::Implementation>()) + , maGraphic(std::move(aGraphic)) +{ +} + +VectorGraphicSearch::~VectorGraphicSearch() { mpImplementation.reset(); } + +bool VectorGraphicSearch::search(OUString const& rSearchString, + VectorGraphicSearchOptions const& rOptions) +{ + if (!mpImplementation->mpPDFium) + { + return false; + } + + if (!mpImplementation->mpSearchContext) + { + auto pData = maGraphic.getVectorGraphicData(); + + if (pData && pData->getType() == VectorGraphicDataType::Pdf) + { + if (searchPDF(pData)) + { + return mpImplementation->mpSearchContext->initialize(rSearchString, rOptions); + } + } + return false; + } + return mpImplementation->mpSearchContext->initialize(rSearchString, rOptions); +} + +bool VectorGraphicSearch::searchPDF(std::shared_ptr<VectorGraphicData> const& rData) +{ + if (!mpImplementation->mpPDFium) + { + return false; + } + + mpImplementation->mpPdfDocument = mpImplementation->mpPDFium->openDocument( + rData->getBinaryDataContainer().getData(), rData->getBinaryDataContainer().getSize(), + OString()); + + if (!mpImplementation->mpPdfDocument) + { + //TODO: Handle failure to load. + switch (mpImplementation->mpPDFium->getLastErrorCode()) + { + case vcl::pdf::PDFErrorType::Success: + break; + case vcl::pdf::PDFErrorType::Unknown: + break; + case vcl::pdf::PDFErrorType::File: + break; + case vcl::pdf::PDFErrorType::Format: + break; + case vcl::pdf::PDFErrorType::Password: + break; + case vcl::pdf::PDFErrorType::Security: + break; + case vcl::pdf::PDFErrorType::Page: + break; + default: + break; + } + return false; + } + + sal_Int32 nPageIndex = std::max(rData->getPageIndex(), sal_Int32(0)); + + mpImplementation->mpSearchContext.reset( + new SearchContext(mpImplementation->mpPdfDocument, nPageIndex)); + return true; +} + +basegfx::B2DSize VectorGraphicSearch::pageSize() +{ + basegfx::B2DSize aSize; + if (mpImplementation->mpSearchContext) + aSize = mpImplementation->mpSearchContext->getPageSize(); + return aSize; +} + +bool VectorGraphicSearch::next() +{ + if (mpImplementation->mpSearchContext) + return mpImplementation->mpSearchContext->next(); + return false; +} + +bool VectorGraphicSearch::previous() +{ + if (mpImplementation->mpSearchContext) + return mpImplementation->mpSearchContext->previous(); + return false; +} + +int VectorGraphicSearch::index() +{ + if (mpImplementation->mpSearchContext) + return mpImplementation->mpSearchContext->index(); + return -1; +} + +std::vector<basegfx::B2DRectangle> VectorGraphicSearch::getTextRectangles() +{ + if (mpImplementation->mpSearchContext) + return mpImplementation->mpSearchContext->getTextRectangles(); + + return std::vector<basegfx::B2DRectangle>(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |