diff options
Diffstat (limited to 'vcl')
-rw-r--r-- | vcl/Library_vcl.mk | 11 | ||||
-rw-r--r-- | vcl/source/graphic/GraphicObject.cxx | 1119 | ||||
-rw-r--r-- | vcl/source/graphic/GraphicObject2.cxx | 2066 | ||||
-rw-r--r-- | vcl/source/graphic/UnoGraphic.cxx | 206 | ||||
-rw-r--r-- | vcl/source/graphic/UnoGraphic.hxx | 87 | ||||
-rw-r--r-- | vcl/source/graphic/UnoGraphicDescriptor.cxx | 405 | ||||
-rw-r--r-- | vcl/source/graphic/UnoGraphicDescriptor.hxx | 120 | ||||
-rw-r--r-- | vcl/source/graphic/UnoGraphicObject.cxx | 118 | ||||
-rw-r--r-- | vcl/source/graphic/UnoGraphicProvider.cxx | 847 | ||||
-rw-r--r-- | vcl/source/graphic/UnoGraphicTransformer.cxx | 144 | ||||
-rw-r--r-- | vcl/source/graphic/UnoGraphicTransformer.hxx | 59 | ||||
-rw-r--r-- | vcl/source/graphic/grfattr.cxx | 63 | ||||
-rw-r--r-- | vcl/source/graphic/grfcache.cxx | 1221 | ||||
-rw-r--r-- | vcl/source/graphic/grfcache.hxx | 136 | ||||
-rw-r--r-- | vcl/vcl.common.component | 30 |
15 files changed, 6632 insertions, 0 deletions
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk index 0aecb5e16a1c..a777f238a070 100644 --- a/vcl/Library_vcl.mk +++ b/vcl/Library_vcl.mk @@ -19,6 +19,8 @@ $(eval $(call gb_Library_Library,vcl)) +$(eval $(call gb_Library_set_componentfile,vcl,vcl/vcl.common)) + ifeq ($(OS),MACOSX) $(eval $(call gb_Library_set_componentfile,vcl,vcl/vcl.macosx)) else ifeq ($(OS),WNT) @@ -301,6 +303,15 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\ vcl/source/gdi/scrptrun \ vcl/source/gdi/CommonSalLayout \ vcl/source/graphic/GraphicLoader \ + vcl/source/graphic/GraphicObject \ + vcl/source/graphic/GraphicObject2 \ + vcl/source/graphic/grfcache \ + vcl/source/graphic/grfattr \ + vcl/source/graphic/UnoGraphic \ + vcl/source/graphic/UnoGraphicDescriptor \ + vcl/source/graphic/UnoGraphicObject \ + vcl/source/graphic/UnoGraphicProvider \ + vcl/source/graphic/UnoGraphicTransformer \ vcl/source/bitmap/bitmap \ vcl/source/bitmap/bitmapfilter \ vcl/source/bitmap/bitmappaint \ diff --git a/vcl/source/graphic/GraphicObject.cxx b/vcl/source/graphic/GraphicObject.cxx new file mode 100644 index 000000000000..a4dbddea1cae --- /dev/null +++ b/vcl/source/graphic/GraphicObject.cxx @@ -0,0 +1,1119 @@ +/* -*- 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 <officecfg/Office/Common.hxx> +#include <osl/file.hxx> +#include <tools/vcompat.hxx> +#include <tools/fract.hxx> +#include <tools/helpers.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <unotools/tempfile.hxx> +#include <unotools/configmgr.hxx> +#include <vcl/svapp.hxx> +#include <vcl/cvtgrf.hxx> +#include <vcl/metaact.hxx> +#include <vcl/virdev.hxx> +#include <vcl/GraphicObject.hxx> + +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <memory> + +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; + +GraphicManager* GraphicObject::mpGlobalMgr = nullptr; + +struct GrfSimpleCacheObj +{ + Graphic maGraphic; + GraphicAttr maAttr; + + GrfSimpleCacheObj( const Graphic& rGraphic, const GraphicAttr& rAttr ) : + maGraphic( rGraphic ), maAttr( rAttr ) {} +}; + +// unique increasing ID for being able to detect the GraphicObject with the +// oldest last data changes +static sal_uLong aIncrementingTimeOfLastDataChange = 1; + +void GraphicObject::ImplAfterDataChange() +{ + // set unique timestamp ID of last data change + mnDataChangeTimeStamp = aIncrementingTimeOfLastDataChange++; + + // check memory footprint of all GraphicObjects managed and evtl. take action + mpGlobalMgr->ImplCheckSizeOfSwappedInGraphics(this); +} + +GraphicObject::GraphicObject() + : mbAutoSwapped(false) + , mbIsInSwapIn(false) + , mbIsInSwapOut(false) +{ + ImplEnsureGraphicManager(); + ImplAssignGraphicData(); + mpGlobalMgr->ImplRegisterObj(*this, maGraphic, nullptr, nullptr); +} + +GraphicObject::GraphicObject(const Graphic& rGraphic) + : maGraphic(rGraphic) + , mbAutoSwapped(false) + , mbIsInSwapIn(false) + , mbIsInSwapOut(false) +{ + ImplEnsureGraphicManager(); + ImplAssignGraphicData(); + mpGlobalMgr->ImplRegisterObj(*this, maGraphic, nullptr, nullptr); +} + +GraphicObject::GraphicObject(const GraphicObject& rGraphicObj) + : maGraphic(rGraphicObj.GetGraphic()) + , maAttr(rGraphicObj.maAttr) + , maPrefMapMode(rGraphicObj.maPrefMapMode) + , maLink(rGraphicObj.maLink) + , maUserData(rGraphicObj.maUserData) + , mbAutoSwapped(false) + , mbIsInSwapIn(false) + , mbIsInSwapOut(false) +{ + ImplAssignGraphicData(); + mpGlobalMgr->ImplRegisterObj(*this, maGraphic, nullptr, &rGraphicObj); + if( rGraphicObj.HasUserData() && rGraphicObj.IsSwappedOut() ) + SetSwapState(); +} + +GraphicObject::GraphicObject(const OString& rUniqueID) + : mbAutoSwapped(false) + , mbIsInSwapIn(false) + , mbIsInSwapOut(false) +{ + ImplEnsureGraphicManager(); + + // assign default properties + ImplAssignGraphicData(); + + mpGlobalMgr->ImplRegisterObj(*this, maGraphic, &rUniqueID, nullptr); + + // update properties + ImplAssignGraphicData(); +} + +GraphicObject::~GraphicObject() +{ + mpGlobalMgr->ImplUnregisterObj( *this ); + + if (!mpGlobalMgr->ImplHasObjects()) + { + delete mpGlobalMgr; + mpGlobalMgr = nullptr; + } +} + +void GraphicObject::ImplAssignGraphicData() +{ + maPrefSize = maGraphic.GetPrefSize(); + maPrefMapMode = maGraphic.GetPrefMapMode(); + mnSizeBytes = maGraphic.GetSizeBytes(); + meType = maGraphic.GetType(); + mbTransparent = maGraphic.IsTransparent(); + mbAnimated = maGraphic.IsAnimated(); + mbEPS = maGraphic.IsEPS(); + mnAnimationLoopCount = ( mbAnimated ? maGraphic.GetAnimationLoopCount() : 0 ); + + // Handle evtl. needed AfterDataChanges + ImplAfterDataChange(); +} + +void GraphicObject::ImplEnsureGraphicManager() +{ + if (mpGlobalMgr) + return; + + sal_uLong nCacheSize = 20000; + sal_uLong nMaxObjCacheSize = 20000; + sal_uLong nTimeoutSeconds = 20000; + if (!utl::ConfigManager::IsFuzzing()) + { + try + { + nCacheSize = officecfg::Office::Common::Cache::GraphicManager::TotalCacheSize::get(); + nMaxObjCacheSize = officecfg::Office::Common::Cache::GraphicManager::ObjectCacheSize::get(); + nTimeoutSeconds = officecfg::Office::Common::Cache::GraphicManager::ObjectReleaseTime::get(); + } + catch (...) + { + } + } + mpGlobalMgr = new GraphicManager(nCacheSize, nMaxObjCacheSize); + mpGlobalMgr->SetCacheTimeout(nTimeoutSeconds); +} + +void GraphicObject::ImplAutoSwapIn() +{ + if( !IsSwappedOut() ) + return; + + { + mbIsInSwapIn = true; + + if( maGraphic.SwapIn() ) + mbAutoSwapped = false; + else + { + SvStream* pStream = GetSwapStream(); + + if( GRFMGR_AUTOSWAPSTREAM_NONE != pStream ) + { + if( GRFMGR_AUTOSWAPSTREAM_LINK == pStream ) + { + if( HasLink() ) + { + OUString aURLStr; + + if( osl::FileBase::getFileURLFromSystemPath( GetLink(), aURLStr ) == osl::FileBase::E_None ) + { + std::unique_ptr<SvStream> pIStm(::utl::UcbStreamHelper::CreateStream( aURLStr, StreamMode::READ )); + + if( pIStm ) + { + ReadGraphic( *pIStm, maGraphic ); + mbAutoSwapped = ( maGraphic.GetType() != GraphicType::NONE ); + } + } + } + } + else if( GRFMGR_AUTOSWAPSTREAM_TEMP == pStream ) + mbAutoSwapped = !maGraphic.SwapIn(); + else if( GRFMGR_AUTOSWAPSTREAM_LOADED == pStream ) + mbAutoSwapped = maGraphic.IsSwapOut(); + else + { + mbAutoSwapped = !maGraphic.SwapIn( pStream ); + delete pStream; + } + } + else + { + DBG_ASSERT( ( GraphicType::NONE == meType ) || ( GraphicType::Default == meType ), + "GraphicObject::ImplAutoSwapIn: could not get stream to swap in graphic! (=>KA)" ); + } + } + + mbIsInSwapIn = false; + + if (!mbAutoSwapped) + mpGlobalMgr->ImplGraphicObjectWasSwappedIn( *this ); + } + ImplAssignGraphicData(); +} + +bool GraphicObject::ImplGetCropParams( OutputDevice const * pOut, 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 sal_uInt16 nRot10 = pAttr->GetRotation() % 3600; + const Point aOldOrigin( rPt ); + const MapMode aMap100( MapUnit::Map100thMM ); + Size aSize100; + long nTotalWidth, nTotalHeight; + + if( nRot10 ) + { + aClipPoly.Rotate( rPt, nRot10 ); + bRectClipRegion = false; + } + else + bRectClipRegion = true; + + rClipPolyPoly = aClipPoly; + + if (maGraphic.GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel) + aSize100 = Application::GetDefaultDevice()->PixelToLogic( maGraphic.GetPrefSize(), aMap100 ); + else + { + MapMode m(maGraphic.GetPrefMapMode()); + aSize100 = pOut->LogicToLogic( maGraphic.GetPrefSize(), &m, &aMap100 ); + } + + nTotalWidth = aSize100.Width() - pAttr->GetLeftCrop() - pAttr->GetRightCrop(); + nTotalHeight = aSize100.Height() - pAttr->GetTopCrop() - pAttr->GetBottomCrop(); + + if( aSize100.Width() > 0 && aSize100.Height() > 0 && nTotalWidth > 0 && nTotalHeight > 0 ) + { + double fScale = static_cast<double>(aSize100.Width()) / nTotalWidth; + const long nNewLeft = -FRound( ( ( pAttr->GetMirrorFlags() & BmpMirrorFlags::Horizontal ) ? pAttr->GetRightCrop() : pAttr->GetLeftCrop() ) * fScale ); + const 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 long nNewTop = -FRound( ( ( pAttr->GetMirrorFlags() & BmpMirrorFlags::Vertical ) ? pAttr->GetBottomCrop() : pAttr->GetTopCrop() ) * fScale ); + const 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 ) + { + mpGlobalMgr->ImplUnregisterObj( *this ); + + maSwapStreamHdl = Link<const GraphicObject*, SvStream*>(); + mxSimpleCache.reset(); + + maGraphic = rGraphicObj.GetGraphic(); + maAttr = rGraphicObj.maAttr; + maLink = rGraphicObj.maLink; + maUserData = rGraphicObj.maUserData; + ImplAssignGraphicData(); + mbAutoSwapped = false; + mpGlobalMgr->ImplRegisterObj( *this, maGraphic, nullptr, &rGraphicObj ); + if( rGraphicObj.HasUserData() && rGraphicObj.IsSwappedOut() ) + SetSwapState(); + } + + return *this; +} + +bool GraphicObject::operator==( const GraphicObject& rGraphicObj ) const +{ + return( ( rGraphicObj.maGraphic == maGraphic ) && + ( rGraphicObj.maAttr == maAttr ) && + ( rGraphicObj.GetLink() == GetLink() ) ); +} + +OString GraphicObject::GetUniqueID() const +{ + if ( !IsInSwapIn() && IsEPS() ) + const_cast<GraphicObject*>(this)->FireSwapInRequest(); + + return mpGlobalMgr->ImplGetUniqueID(*this); +} + +SvStream* GraphicObject::GetSwapStream() const +{ + if( HasSwapStreamHdl() ) + return maSwapStreamHdl.Call( this ); + else + return GRFMGR_AUTOSWAPSTREAM_NONE; +} + +void GraphicObject::SetAttr( const GraphicAttr& rAttr ) +{ + maAttr = rAttr; + + if (mxSimpleCache && (mxSimpleCache->maAttr != rAttr)) + mxSimpleCache.reset(); +} + +void GraphicObject::SetLink() +{ + maLink.clear(); +} + +void GraphicObject::SetLink( const OUString& rLink ) +{ + maLink = rLink; +} + +void GraphicObject::SetUserData() +{ + maUserData.clear(); +} + +void GraphicObject::SetUserData( const OUString& rUserData ) +{ + maUserData = rUserData; + if( !rUserData.isEmpty() ) + SetSwapState(); +} + +static sal_uInt32 GetCacheTimeInMs() +{ + if (utl::ConfigManager::IsFuzzing()) + return 20000; + + const sal_uInt32 nSeconds = + officecfg::Office::Common::Cache::GraphicManager::ObjectReleaseTime::get( + comphelper::getProcessComponentContext()); + + return nSeconds * 1000; +} + +void GraphicObject::SetSwapStreamHdl(const Link<const GraphicObject*, SvStream*>& rHdl) +{ + maSwapStreamHdl = rHdl; + + sal_uInt32 const nSwapOutTimeout(GetCacheTimeInMs()); + if (nSwapOutTimeout) + { + if (!mxSwapOutTimer) + { + mxSwapOutTimer.reset(new Timer("svtools::GraphicObject mpSwapOutTimer")); + mxSwapOutTimer->SetInvokeHandler( LINK( this, GraphicObject, ImplAutoSwapOutHdl ) ); + } + + mxSwapOutTimer->SetTimeout( nSwapOutTimeout ); + mxSwapOutTimer->Start(); + } + else + { + mxSwapOutTimer.reset(); + } +} + +void GraphicObject::FireSwapInRequest() +{ + ImplAutoSwapIn(); +} + +void GraphicObject::FireSwapOutRequest() +{ + ImplAutoSwapOutHdl( nullptr ); +} + +bool GraphicObject::IsCached( OutputDevice const * pOut, const Size& rSz, + const GraphicAttr* pAttr, GraphicManagerDrawFlags nFlags ) const +{ + bool bRet; + + if( nFlags & GraphicManagerDrawFlags::CACHED ) + { + Point aPt; + Size aSz( rSz ); + if ( pAttr && pAttr->IsCropped() ) + { + tools::PolyPolygon aClipPolyPoly; + bool bRectClip; + ImplGetCropParams( pOut, aPt, aSz, pAttr, aClipPolyPoly, bRectClip ); + } + bRet = mpGlobalMgr->IsInCache( pOut, aPt, aSz, *this, ( pAttr ? *pAttr : GetAttr() ) ); + } + else + bRet = false; + + return bRet; +} + +bool GraphicObject::Draw( OutputDevice* pOut, const Point& rPt, const Size& rSz, + const GraphicAttr* pAttr, GraphicManagerDrawFlags nFlags ) +{ + GraphicAttr aAttr( pAttr ? *pAttr : GetAttr() ); + Point aPt( rPt ); + Size aSz( rSz ); + const DrawModeFlags nOldDrawMode = pOut->GetDrawMode(); + bool bCropped = aAttr.IsCropped(); + bool bCached = false; + bool bRet; + + // #i29534# Provide output rects for PDF writer + tools::Rectangle aCropRect; + + if( !( GraphicManagerDrawFlags::USE_DRAWMODE_SETTINGS & nFlags ) ) + pOut->SetDrawMode( nOldDrawMode & ~DrawModeFlags( DrawModeFlags::SettingsLine | DrawModeFlags::SettingsFill | DrawModeFlags::SettingsText | DrawModeFlags::SettingsGradient ) ); + + // mirrored horizontically + 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( pOut, aPt, aSz, &aAttr, aClipPolyPoly, bRectClip ); + + pOut->Push( PushFlags::CLIPREGION ); + + if( bCrop ) + { + if( bRectClip ) + { + // #i29534# Store crop rect for later forwarding to + // PDF writer + aCropRect = aClipPolyPoly.GetBoundRect(); + pOut->IntersectClipRegion( aCropRect ); + } + else + { + pOut->IntersectClipRegion(vcl::Region(aClipPolyPoly)); + } + } + } + + bRet = mpGlobalMgr->DrawObj( pOut, aPt, aSz, *this, aAttr, nFlags, bCached ); + + if( bCropped ) + pOut->Pop(); + + pOut->SetDrawMode( nOldDrawMode ); + + // #i29534# Moved below OutDev restoration, to avoid multiple swap-ins + // (code above needs to call GetGraphic twice) + if( bCached ) + { + if (mxSwapOutTimer) + mxSwapOutTimer->Start(); + else + FireSwapOutRequest(); + } + + return bRet; +} + +void GraphicObject::DrawTiled( OutputDevice* pOut, const tools::Rectangle& rArea, const Size& rSize, + const Size& rOffset, GraphicManagerDrawFlags nFlags, int nTileCacheSize1D ) +{ + if( pOut == nullptr || rSize.Width() == 0 || rSize.Height() == 0 ) + return; + + const MapMode aOutMapMode( pOut->GetMapMode() ); + const MapMode aMapMode( aOutMapMode.GetMapUnit(), Point(), aOutMapMode.GetScaleX(), aOutMapMode.GetScaleY() ); + // #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( 1L, pOut->LogicToPixel( rSize, aOutMapMode ).Width() ), + ::std::max( 1L, pOut->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( pOut, rArea, aOutTileSize, rOffset, nullptr, nFlags, nTileCacheSize1D ); +} + +bool GraphicObject::StartAnimation( OutputDevice* pOut, const Point& rPt, const Size& rSz, + long nExtraData, + OutputDevice* pFirstFrameOutDev ) +{ + bool bRet = false; + + GetGraphic(); + + if( !IsSwappedOut() ) + { + const GraphicAttr aAttr( GetAttr() ); + + if( mbAnimated ) + { + Point aPt( rPt ); + Size aSz( rSz ); + bool bCropped = aAttr.IsCropped(); + + if( bCropped ) + { + tools::PolyPolygon aClipPolyPoly; + bool bRectClip; + const bool bCrop = ImplGetCropParams( pOut, aPt, aSz, &aAttr, aClipPolyPoly, bRectClip ); + + pOut->Push( PushFlags::CLIPREGION ); + + if( bCrop ) + { + if( bRectClip ) + pOut->IntersectClipRegion( aClipPolyPoly.GetBoundRect() ); + else + pOut->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(pOut, aPt, aSz, nExtraData, pFirstFrameOutDev); + + if( bCropped ) + pOut->Pop(); + + bRet = true; + } + else + bRet = Draw( pOut, rPt, rSz, &aAttr ); + } + + return bRet; +} + +void GraphicObject::StopAnimation( OutputDevice* pOut, long nExtraData ) +{ + if (mxSimpleCache) + mxSimpleCache->maGraphic.StopAnimation(pOut, nExtraData); +} + +const Graphic& GraphicObject::GetGraphic() const +{ + GraphicObject *pThis = const_cast<GraphicObject*>(this); + (void)pThis->SwapIn(); + + //fdo#50697 If we've been asked to provide the graphic, then reset + //the cache timeout to start from now and not remain at the + //time of creation + // restart SwapOut timer; this is like touching in a cache to reset to the full timeout value + if (pThis->mxSwapOutTimer && pThis->mxSwapOutTimer->IsActive()) + { + pThis->mxSwapOutTimer->Stop(); + pThis->mxSwapOutTimer->Start(); + } + + return maGraphic; +} + +void GraphicObject::SetGraphic( const Graphic& rGraphic, const GraphicObject* pCopyObj ) +{ + mpGlobalMgr->ImplUnregisterObj( *this ); + + if (mxSwapOutTimer) + mxSwapOutTimer->Stop(); + + maGraphic = rGraphic; + mbAutoSwapped = false; + ImplAssignGraphicData(); + maLink.clear(); + mxSimpleCache.reset(); + + mpGlobalMgr->ImplRegisterObj( *this, maGraphic, nullptr, pCopyObj); + + if (mxSwapOutTimer) + mxSwapOutTimer->Start(); + + +} + +void GraphicObject::SetGraphic( const Graphic& rGraphic, const OUString& rLink ) +{ + // in case we are called from a situation where rLink and maLink are the same thing, + // we need a copy because SetGraphic clears maLink + OUString sLinkCopy = rLink; + SetGraphic( rGraphic ); + maLink = sLinkCopy; +} + +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<long>(static_cast<double>(rDestSize.Width()) * (1.0 + (aCropLeftTop.Width() + aCropRightBottom.Width()) / aSrcSize.Width()) + .5), + static_cast<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<long>(static_cast<double>(aMtfMapMode.GetOrigin().X()) + rDestSize.Width() * aCropLeftTop.Width() / (aSrcSize.Width() - aCropLeftTop.Width() - aCropRightBottom.Width()) + .5), + static_cast<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 it's 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 ) + { + AnimationBitmap aAnimBmp( aAnim.Get( nFrame ) ); + + if( !aCropRect.IsInside( tools::Rectangle(aAnimBmp.aPosPix, aAnimBmp.aSizePix) ) ) + { + // setup actual cropping (relative to frame position) + tools::Rectangle aCropRectRel( aCropRect ); + aCropRectRel.Move( -aAnimBmp.aPosPix.X(), + -aAnimBmp.aPosPix.Y() ); + + // cropping affects this frame, apply it then + // do _not_ apply enlargement, this is done below + ImplTransformBitmap( aAnimBmp.aBmpEx, rAttr, Size(), Size(), + aCropRectRel, rDestSize, false ); + + aAnim.Replace( aAnimBmp, 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 ) + { + AnimationBitmap aAnimBmp( aAnim.Get( nFrame ) ); + + aAnimBmp.aPosPix += aPosOffset; + + aAnim.Replace( aAnimBmp, 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 // TODO: Change to Impl +{ + GetGraphic(); + + Graphic aGraphic; + GraphicAttr aAttr( pAttr ? *pAttr : GetAttr() ); + + if( maGraphic.IsSupportedGraphic() && !maGraphic.IsSwapOut() ) + { + if( aAttr.IsSpecialDrawMode() || aAttr.IsAdjusted() || aAttr.IsMirrored() || aAttr.IsRotated() || aAttr.IsTransparent() ) + { + if( GetType() == GraphicType::Bitmap ) + { + if( IsAnimated() ) + { + Animation aAnimation( maGraphic.GetAnimation() ); + GraphicManager::ImplAdjust( aAnimation, aAttr, GraphicAdjustmentFlags::ALL ); + aAnimation.SetLoopCount( mnAnimationLoopCount ); + aGraphic = aAnimation; + } + else + { + BitmapEx aBmpEx( maGraphic.GetBitmapEx() ); + GraphicManager::ImplAdjust( aBmpEx, aAttr, GraphicAdjustmentFlags::ALL ); + aGraphic = aBmpEx; + } + } + else + { + GDIMetaFile aMtf( maGraphic.GetGDIMetaFile() ); + GraphicManager::ImplAdjust( aMtf, aAttr, GraphicAdjustmentFlags::ALL ); + aGraphic = aMtf; + } + } + else + { + if( ( GetType() == GraphicType::Bitmap ) && IsAnimated() ) + { + Animation aAnimation( maGraphic.GetAnimation() ); + aAnimation.SetLoopCount( mnAnimationLoopCount ); + aGraphic = aAnimation; + } + else + aGraphic = maGraphic; + } + } + + return aGraphic; +} + +bool GraphicObject::SwapOut() +{ + const bool bRet = !mbAutoSwapped && maGraphic.SwapOut(); + + if (bRet) + mpGlobalMgr->ImplGraphicObjectWasSwappedOut( *this ); + + return bRet; +} + +bool GraphicObject::SwapOut( SvStream* pOStm ) +{ + bool bRet = false; + try + { + bRet = !mbAutoSwapped; + // swap out as a link + if( pOStm == GRFMGR_AUTOSWAPSTREAM_LINK ) + { + maGraphic.SwapOutAsLink(); + } + else + { + bRet = bRet && maGraphic.SwapOut( pOStm ); + } + + if (bRet) + mpGlobalMgr->ImplGraphicObjectWasSwappedOut(*this); + } + catch(...) + { + SAL_WARN( "svtools", "GraphicObject::SwapIn exception"); + } + return bRet; +} + +bool GraphicObject::SwapIn() +{ + bool bRet = false; + try + { + if( mbAutoSwapped ) + { + ImplAutoSwapIn(); + bRet = true; + } + else + { + bRet = maGraphic.SwapIn(); + + if (bRet) + mpGlobalMgr->ImplGraphicObjectWasSwappedIn(*this); + } + + if( bRet ) + { + ImplAssignGraphicData(); + } + } + catch (...) + { + SAL_WARN( "svtools", "GraphicObject::SwapIn exception"); + } + + return bRet; +} + +void GraphicObject::SetSwapState() +{ + if( !IsSwappedOut() ) + { + mbAutoSwapped = true; + + mpGlobalMgr->ImplGraphicObjectWasSwappedOut(*this); + } +} + +IMPL_LINK_NOARG(GraphicObject, ImplAutoSwapOutHdl, Timer *, void) +{ + if( !IsSwappedOut() ) + { + mbIsInSwapOut = true; + + SvStream* pStream = GetSwapStream(); + + if( GRFMGR_AUTOSWAPSTREAM_NONE != pStream ) + { + if( GRFMGR_AUTOSWAPSTREAM_LINK == pStream ) + mbAutoSwapped = SwapOut( GRFMGR_AUTOSWAPSTREAM_LINK ); + else + { + if( GRFMGR_AUTOSWAPSTREAM_TEMP == pStream ) + mbAutoSwapped = SwapOut(); + else + { + mbAutoSwapped = SwapOut( pStream ); + delete pStream; + } + } + } + + mbIsInSwapOut = false; + } + + if (mxSwapOutTimer) + mxSwapOutTimer->Start(); +} + +#define UNO_NAME_GRAPHOBJ_URLPREFIX "vnd.sun.star.GraphicObject:" + +bool GraphicObject::isGraphicObjectUniqueIdURL(OUString const & rURL) +{ + const OUString aPrefix(UNO_NAME_GRAPHOBJ_URLPREFIX); + return rURL.startsWith(aPrefix); +} + +void +GraphicObject::InspectForGraphicObjectImageURL( const Reference< XInterface >& xIf, std::vector< OUString >& rvEmbedImgUrls ) +{ + static const char sImageURL[] = "ImageURL"; + Reference< XPropertySet > xProps( xIf, UNO_QUERY ); + if ( xProps.is() ) + { + + if ( xProps->getPropertySetInfo()->hasPropertyByName( sImageURL ) ) + { + OUString sURL; + xProps->getPropertyValue( sImageURL ) >>= sURL; + if ( !sURL.isEmpty() && sURL.startsWith( UNO_NAME_GRAPHOBJ_URLPREFIX ) ) + rvEmbedImgUrls.push_back( sURL ); + } + } + Reference< XNameContainer > xContainer( xIf, UNO_QUERY ); + if ( xContainer.is() ) + { + Sequence< OUString > sNames = xContainer->getElementNames(); + sal_Int32 nContainees = sNames.getLength(); + for ( sal_Int32 index = 0; index < nContainees; ++index ) + { + Reference< XInterface > xCtrl; + xContainer->getByName( sNames[ index ] ) >>= xCtrl; + InspectForGraphicObjectImageURL( xCtrl, rvEmbedImgUrls ); + } + } +} + +// 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 000000000000..c6a7e688d9af --- /dev/null +++ b/vcl/source/graphic/GraphicObject2.cxx @@ -0,0 +1,2066 @@ +/* -*- 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 <cstdlib> + +#include <vcl/bitmapaccess.hxx> +#include <tools/poly.hxx> +#include <vcl/outdev.hxx> +#include <vcl/window.hxx> +#include <vcl/gdimtf.hxx> +#include <vcl/metaact.hxx> +#include <vcl/metric.hxx> +#include <vcl/animate.hxx> +#include <vcl/alpha.hxx> +#include <vcl/virdev.hxx> +#include "grfcache.hxx" +#include <vcl/GraphicObject.hxx> +#include <memory> + + +#define WATERMARK_LUM_OFFSET 50 +#define WATERMARK_CON_OFFSET -70 + +namespace { + +constexpr sal_uInt8 MAP(long cVal0, long cVal1, long nFrac) { + return static_cast<sal_uInt8>(((cVal0<<20)+nFrac*(cVal1-cVal0))>>20); +} + +} + +GraphicManager::GraphicManager( sal_uLong nCacheSize, sal_uLong nMaxObjCacheSize ) : + mnUsedSize(0), + mpCache( new GraphicCache( nCacheSize, nMaxObjCacheSize ) ) +{ +} + +GraphicManager::~GraphicManager() +{ + assert(maObjList.empty()); + mpCache.reset(); +} + +void GraphicManager::SetMaxCacheSize( sal_uLong nNewCacheSize ) +{ + mpCache->SetMaxDisplayCacheSize( nNewCacheSize ); +} + +sal_uLong GraphicManager::GetMaxCacheSize() const +{ + return mpCache->GetMaxDisplayCacheSize(); +} + +void GraphicManager::SetCacheTimeout( sal_uLong nTimeoutSeconds ) +{ + mpCache->SetCacheTimeout( nTimeoutSeconds ); +} + +bool GraphicManager::IsInCache( OutputDevice const * pOut, const Point& rPt, + const Size& rSz, const GraphicObject& rObj, + const GraphicAttr& rAttr ) const +{ + return mpCache->IsInDisplayCache( pOut, rPt, rSz, rObj, rAttr ); +} + +bool GraphicManager::DrawObj( OutputDevice* pOut, const Point& rPt, const Size& rSz, + GraphicObject const & rObj, const GraphicAttr& rAttr, + const GraphicManagerDrawFlags nFlags, bool& rCached ) +{ + Point aPt( rPt ); + Size aSz( rSz ); + bool bRet = false; + + rCached = false; + + if( ( rObj.GetType() == GraphicType::Bitmap ) || ( rObj.GetType() == GraphicType::GdiMetafile ) ) + { + // create output and fill cache + + if( rObj.IsAnimated() || ( pOut->GetOutDevType() == OUTDEV_PRINTER ) || + ( !( nFlags & GraphicManagerDrawFlags::NO_SUBSTITUTE ) && + ( ( nFlags & GraphicManagerDrawFlags::SUBSTITUTE ) || + !( nFlags & GraphicManagerDrawFlags::CACHED ) || + ( pOut->GetConnectMetaFile() && !pOut->IsOutputEnabled() ) ) ) ) + { + // simple output of transformed graphic + const Graphic aGraphic( rObj.GetTransformedGraphic( &rAttr ) ); + + if( aGraphic.IsSupportedGraphic() ) + { + const sal_uInt16 nRot10 = rAttr.GetRotation() % 3600; + + 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( pOut, aPt, aSz ); + } + + bRet = true; + } + + if( !bRet ) + { + // cached/direct drawing + if( !mpCache->DrawDisplayCacheObj( pOut, aPt, aSz, rObj, rAttr ) ) + bRet = ImplDraw( pOut, aPt, aSz, rObj, rAttr, rCached ); + else + bRet = rCached = true; + } + } + + return bRet; +} + +void GraphicManager::ImplRegisterObj( const GraphicObject& rObj, Graphic& rSubstitute, + const OString* pID, const GraphicObject* pCopyObj ) +{ + assert(maObjList.find(const_cast<GraphicObject*>(&rObj)) == maObjList.end()); + + maObjList.emplace( const_cast<GraphicObject*>(&rObj) ); + + mpCache->AddGraphicObject( rObj, rSubstitute, pID, pCopyObj ); + if( !rObj.IsSwappedOut() ) + mnUsedSize += rObj.maGraphic.GetSizeBytes(); +} + +void GraphicManager::ImplUnregisterObj( const GraphicObject& rObj ) +{ + mpCache->ReleaseGraphicObject( rObj ); + if( !rObj.IsSwappedOut() ) + { + assert(mnUsedSize >= rObj.maGraphic.GetSizeBytes()); + mnUsedSize -= rObj.maGraphic.GetSizeBytes(); + } + if ( 0 < maObjList.erase( const_cast<GraphicObject*>(&rObj) ) ) + return; + + assert(false); // surely it should have been registered? +} + +void GraphicManager::ImplGraphicObjectWasSwappedOut( const GraphicObject& rObj ) +{ + mpCache->GraphicObjectWasSwappedOut( rObj ); + assert(mnUsedSize >= rObj.GetSizeBytes()); + mnUsedSize -= rObj.GetSizeBytes(); +} + +OString GraphicManager::ImplGetUniqueID( const GraphicObject& rObj ) const +{ + return mpCache->GetUniqueID( rObj ); +} + +namespace +{ + struct simpleSortByDataChangeTimeStamp + { + bool operator() (GraphicObject const * p1, GraphicObject const * p2) const + { + return p1->GetDataChangeTimeStamp() < p2->GetDataChangeTimeStamp(); + } + }; +} // end of anonymous namespace + +void GraphicManager::ImplCheckSizeOfSwappedInGraphics(const GraphicObject* pGraphicToIgnore) +{ + // detect maximum allowed memory footprint. Use the user-settings of MaxCacheSize (defaulted + // to 200MB). + const sal_uLong nMaxCacheSize(GetMaxCacheSize()); + + if(mnUsedSize <= nMaxCacheSize) + return; + + // Copy the object list for now, because maObjList can change in the meantime unexpectedly. + std::vector< GraphicObject* > aCandidates(maObjList.begin(), maObjList.end()); + // if we use more currently, sort by last DataChangeTimeStamp + // sort by DataChangeTimeStamp so that the oldest get removed first + std::sort(aCandidates.begin(), aCandidates.end(), simpleSortByDataChangeTimeStamp()); + + for(decltype(aCandidates)::size_type a(0); mnUsedSize >= nMaxCacheSize && a < aCandidates.size(); a++) + { + // swap out until we have no more or the goal to use less than nMaxCacheSize + // is reached + GraphicObject* pObj = aCandidates[a]; + if( pObj == pGraphicToIgnore ) + { + continue; + } + if (maObjList.find(pObj) == maObjList.end()) + { + // object has been deleted when swapping out another one + continue; + } + + // do not swap out when we have less than 16KB data objects + if(pObj->GetSizeBytes() >= (16 * 1024)) + { + pObj->FireSwapOutRequest(); + } + } +} + +void GraphicManager::ImplGraphicObjectWasSwappedIn( const GraphicObject& rObj ) +{ + mpCache->GraphicObjectWasSwappedIn( rObj ); + mnUsedSize += rObj.maGraphic.GetSizeBytes(); +} + +bool GraphicManager::ImplDraw( OutputDevice* pOut, const Point& rPt, + const Size& rSz, GraphicObject const & rObj, + const GraphicAttr& rAttr, + bool& rCached ) +{ + const Graphic& rGraphic = rObj.GetGraphic(); + bool bRet = false; + + if( rGraphic.IsSupportedGraphic() && !rObj.IsSwappedOut() ) + { + if( GraphicType::Bitmap == rGraphic.GetType() ) + { + const BitmapEx aSrcBmpEx( rGraphic.GetBitmapEx() ); + + // #i46805# No point in caching a bitmap that is rendered + // via RectFill on the OutDev + if( !(pOut->GetDrawMode() & ( DrawModeFlags::BlackBitmap | DrawModeFlags::WhiteBitmap )) && + mpCache->IsDisplayCacheable( pOut, rPt, rSz, rObj, rAttr ) ) + { + BitmapEx aDstBmpEx; + + if( ImplCreateOutput( pOut, rPt, rSz, aSrcBmpEx, rAttr, &aDstBmpEx ) ) + { + rCached = mpCache->CreateDisplayCacheObj( pOut, rPt, rSz, rObj, rAttr, aDstBmpEx ); + bRet = true; + } + } + + if( !bRet ) + bRet = ImplCreateOutput( pOut, rPt, rSz, aSrcBmpEx, rAttr ); + } + else + { + const GDIMetaFile& rSrcMtf = rGraphic.GetGDIMetaFile(); + + if( mpCache->IsDisplayCacheable( pOut, rPt, rSz, rObj, rAttr ) ) + { + GDIMetaFile aDstMtf; + BitmapEx aContainedBmpEx; + + if( ImplCreateOutput( pOut, rPt, rSz, rSrcMtf, rAttr, aDstMtf, aContainedBmpEx ) ) + { + if( !!aContainedBmpEx ) + { + // Use bitmap output method, if metafile basically contains only a single + // bitmap (allows caching the resulting pixmap). + BitmapEx aDstBmpEx; + + if( ImplCreateOutput( pOut, rPt, rSz, aContainedBmpEx, rAttr, &aDstBmpEx ) ) + { + rCached = mpCache->CreateDisplayCacheObj( pOut, rPt, rSz, rObj, rAttr, aDstBmpEx ); + bRet = true; + } + } + else + { + rCached = mpCache->CreateDisplayCacheObj( pOut, rPt, rSz, rObj, rAttr, aDstMtf ); + bRet = true; + } + } + } + + if( !bRet ) + { + const Graphic aGraphic( rObj.GetTransformedGraphic( &rAttr ) ); + + if( aGraphic.IsSupportedGraphic() ) + { + aGraphic.Draw( pOut, rPt, rSz ); + bRet = true; + } + } + } + } + + return bRet; +} + +bool ImplCreateRotatedScaled( const BitmapEx& rBmpEx, const GraphicAttr& rAttributes, + sal_uInt16 nRot10, const Size& rUnrotatedSzPix, + long nStartX, long nEndX, long nStartY, long nEndY, + BitmapEx& rOutBmpEx ) +{ + const long aUnrotatedWidth = rUnrotatedSzPix.Width(); + const long aUnrotatedHeight = rUnrotatedSzPix.Height(); + const long aBitmapWidth = rBmpEx.GetSizePixel().Width(); + const long aBitmapHeight = rBmpEx.GetSizePixel().Height(); + + long nTmpX, nTmpY, nTmpFX, nTmpFY, nTmp; + double fTmp; + + bool bHMirr( rAttributes.GetMirrorFlags() & BmpMirrorFlags::Horizontal ); + bool bVMirr( rAttributes.GetMirrorFlags() & BmpMirrorFlags::Vertical ); + + std::unique_ptr<long[]> pMapIX(new long[ aUnrotatedWidth ]); + std::unique_ptr<long[]> pMapFX(new long[ aUnrotatedWidth ]); + std::unique_ptr<long[]> pMapIY(new long[ aUnrotatedHeight ]); + std::unique_ptr<long[]> pMapFY(new long[ aUnrotatedHeight ]); + + double fRevScaleX; + double fRevScaleY; + + bool scaleByAveraging = false; + + if(aBitmapWidth > 1 && aUnrotatedWidth > 1) + { + fRevScaleX = static_cast<double>( aBitmapWidth - 1 ) / static_cast<double>( aUnrotatedWidth - 1 ); + // create horizontal mapping table + long x; + for( x = 0, nTmpX = aBitmapWidth - 1, nTmp = aBitmapWidth - 2 >= 0 ? aBitmapWidth -2 : 0L; x < aUnrotatedWidth; x++ ) + { + fTmp = x * fRevScaleX; + + if( bHMirr ) + fTmp = nTmpX - fTmp; + + pMapIX[ x ] = MinMax( fTmp, 0, nTmp ); + pMapFX[ x ] = static_cast<long>( ( fTmp - pMapIX[ x ] ) * 1048576.0 ); + } + scaleByAveraging |= fRevScaleX > 5.0/3.0; + } + else + { + if(aBitmapWidth == 1) + { + fRevScaleX = 1.0 / static_cast<double>(aUnrotatedWidth); + for ( long x = 0; x < aUnrotatedWidth ; x++) + { + pMapIX[x] = 0; + pMapFX[x] = 0; + } + scaleByAveraging = true; + } + else + { + fRevScaleX = static_cast<double>(aBitmapWidth) / static_cast<double>(aUnrotatedWidth); + fTmp = static_cast<double>(aBitmapWidth) / 2.0; + + pMapIX[ 0 ] = static_cast<long>(fTmp); + pMapFX[ 0 ] = static_cast<long>( ( fTmp - pMapIX[ 0 ] ) * 1048576.0 ); + scaleByAveraging = true; + } + } + if(aBitmapHeight > 1 && aUnrotatedHeight > 1) + { + fRevScaleY = static_cast<double>( aBitmapHeight - 1 ) / static_cast<double>( aUnrotatedHeight - 1 ); + // create vertical mapping table + long y; + for( y = 0, nTmpY = aBitmapHeight - 1, nTmp = aBitmapHeight - 2 >= 0 ? aBitmapHeight - 2 : 0L; y < aUnrotatedHeight; y++ ) + { + fTmp = y * fRevScaleY; + + if( bVMirr ) + fTmp = nTmpY - fTmp; + + pMapIY[ y ] = MinMax( fTmp, 0, nTmp ); + pMapFY[ y ] = static_cast<long>( ( fTmp - pMapIY[ y ] ) * 1048576.0 ); + } + scaleByAveraging |= fRevScaleY > 5.0/3.0; + } + else + { + if(aBitmapHeight == 1) + { + fRevScaleY = 1.0 / static_cast<double>(aUnrotatedHeight); + for (long y = 0; y < aUnrotatedHeight; ++y) + { + pMapIY[y] = 0; + pMapFY[y] = 0; + } + scaleByAveraging = true; + } + else + { + fRevScaleY = static_cast<double>(aBitmapHeight) / static_cast<double>(aUnrotatedHeight); + fTmp = static_cast<double>(aBitmapHeight) / 2.0; + + pMapIY[ 0 ] = static_cast<long>(fTmp); + pMapFY[ 0 ] = static_cast<long>( ( fTmp - pMapIY[ 0 ] ) * 1048576.0 ); + scaleByAveraging = true; + } + } + + Bitmap aBmp( rBmpEx.GetBitmap() ); + Bitmap aOutBmp; + BitmapReadAccess* pReadAccess = aBmp.AcquireReadAccess(); + BitmapWriteAccess* pWriteAccess; + + const double fCosAngle = cos( nRot10 * F_PI1800 ); + const double fSinAngle = sin( nRot10 * F_PI1800 ); + const long aTargetWidth = nEndX - nStartX + 1; + const long aTargetHeight = nEndY - nStartY + 1; + std::unique_ptr<long[]> pCosX(new long[ aTargetWidth ]); + std::unique_ptr<long[]> pSinX(new long[ aTargetWidth ]); + std::unique_ptr<long[]> pCosY(new long[ aTargetHeight ]); + std::unique_ptr<long[]> pSinY(new long[ aTargetHeight ]); + long nUnRotX, nUnRotY, nSinY, nCosY; + sal_uInt8 cR0, cG0, cB0, cR1, cG1, cB1; + bool bRet = false; + + tools::Polygon aPoly( tools::Rectangle( Point(), rUnrotatedSzPix ) ); + aPoly.Rotate( Point(), nRot10 ); + tools::Rectangle aNewBound( aPoly.GetBoundRect() ); + + // create horizontal mapping table + long x; + for( x = 0, nTmpX = aNewBound.Left() + nStartX; x < aTargetWidth; x++ ) + { + pCosX[ x ] = FRound( fCosAngle * ( fTmp = nTmpX++ << 8 ) ); + pSinX[ x ] = FRound( fSinAngle * fTmp ); + } + + // create vertical mapping table + long y; + for( y = 0, nTmpY = aNewBound.Top() + nStartY; y < aTargetHeight; y++ ) + { + pCosY[ y ] = FRound( fCosAngle * ( fTmp = nTmpY++ << 8 ) ); + pSinY[ y ] = FRound( fSinAngle * fTmp ); + } + + if( pReadAccess ) + { + aOutBmp = Bitmap( Size( aTargetWidth, aTargetHeight ), 24 ); + pWriteAccess = aOutBmp.AcquireWriteAccess(); + + if( pWriteAccess ) + { + BitmapColor aColRes; + + if ( !scaleByAveraging ) + { + if( pReadAccess->HasPalette() ) + { + for( y = 0; y < aTargetHeight; y++ ) + { + nSinY = pSinY[ y ]; + nCosY = pCosY[ y ]; + + Scanline pScanline = pWriteAccess->GetScanline( y ); + for( x = 0; x < aTargetWidth; x++ ) + { + nUnRotX = ( pCosX[ x ] - nSinY ) >> 8; + nUnRotY = ( pSinX[ x ] + nCosY ) >> 8; + + if( ( nUnRotX >= 0 ) && ( nUnRotX < aUnrotatedWidth ) && + ( nUnRotY >= 0 ) && ( nUnRotY < aUnrotatedHeight ) ) + { + nTmpX = pMapIX[ nUnRotX ]; + nTmpFX = pMapFX[ nUnRotX ]; + nTmpY = pMapIY[ nUnRotY ]; + nTmpFY = pMapFY[ nUnRotY ]; + + const BitmapColor& rCol0 = pReadAccess->GetPaletteColor( pReadAccess->GetPixelIndex( nTmpY, nTmpX ) ); + const BitmapColor& rCol1 = pReadAccess->GetPaletteColor( pReadAccess->GetPixelIndex( nTmpY, ++nTmpX ) ); + cR0 = MAP( rCol0.GetRed(), rCol1.GetRed(), nTmpFX ); + cG0 = MAP( rCol0.GetGreen(), rCol1.GetGreen(), nTmpFX ); + cB0 = MAP( rCol0.GetBlue(), rCol1.GetBlue(), nTmpFX ); + + const BitmapColor& rCol3 = pReadAccess->GetPaletteColor( pReadAccess->GetPixelIndex( ++nTmpY, nTmpX ) ); + const BitmapColor& rCol2 = pReadAccess->GetPaletteColor( pReadAccess->GetPixelIndex( nTmpY, --nTmpX ) ); + cR1 = MAP( rCol2.GetRed(), rCol3.GetRed(), nTmpFX ); + cG1 = MAP( rCol2.GetGreen(), rCol3.GetGreen(), nTmpFX ); + cB1 = MAP( rCol2.GetBlue(), rCol3.GetBlue(), nTmpFX ); + + aColRes.SetRed( MAP( cR0, cR1, nTmpFY ) ); + aColRes.SetGreen( MAP( cG0, cG1, nTmpFY ) ); + aColRes.SetBlue( MAP( cB0, cB1, nTmpFY ) ); + pWriteAccess->SetPixelOnData( pScanline, x, aColRes ); + } + } + } + } + else + { + BitmapColor aCol0, aCol1; + + for( y = 0; y < aTargetHeight; y++ ) + { + nSinY = pSinY[ y ]; + nCosY = pCosY[ y ]; + + Scanline pScanline = pWriteAccess->GetScanline( y ); + for( x = 0; x < aTargetWidth; x++ ) + { + nUnRotX = ( pCosX[ x ] - nSinY ) >> 8; + nUnRotY = ( pSinX[ x ] + nCosY ) >> 8; + + if( ( nUnRotX >= 0 ) && ( nUnRotX < aUnrotatedWidth ) && + ( nUnRotY >= 0 ) && ( nUnRotY < aUnrotatedHeight ) ) + { + nTmpX = pMapIX[ nUnRotX ]; + nTmpFX = pMapFX[ nUnRotX ]; + nTmpY = pMapIY[ nUnRotY ]; + nTmpFY = pMapFY[ nUnRotY ]; + + aCol0 = pReadAccess->GetPixel( nTmpY, nTmpX ); + aCol1 = pReadAccess->GetPixel( nTmpY, ++nTmpX ); + cR0 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTmpFX ); + cG0 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTmpFX ); + cB0 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTmpFX ); + + aCol1 = pReadAccess->GetPixel( ++nTmpY, nTmpX ); + aCol0 = pReadAccess->GetPixel( nTmpY, --nTmpX ); + cR1 = MAP( aCol0.GetRed(), aCol1.GetRed(), nTmpFX ); + cG1 = MAP( aCol0.GetGreen(), aCol1.GetGreen(), nTmpFX ); + cB1 = MAP( aCol0.GetBlue(), aCol1.GetBlue(), nTmpFX ); + + aColRes.SetRed( MAP( cR0, cR1, nTmpFY ) ); + aColRes.SetGreen( MAP( cG0, cG1, nTmpFY ) ); + aColRes.SetBlue( MAP( cB0, cB1, nTmpFY ) ); + pWriteAccess->SetPixelOnData( pScanline, x, aColRes ); + } + } + } + } + } + else // scaleByAveraging + { + double aSumRed, aSumGreen, aSumBlue, aCount; + BitmapColor aColor; + BitmapColor aResultColor; + + for( y = 0; y < aTargetHeight; y++ ) + { + nSinY = pSinY[ y ]; + nCosY = pCosY[ y ]; + + Scanline pScanline = pWriteAccess->GetScanline( y ); + for( x = 0; x < aTargetWidth; x++ ) + { + double aUnrotatedX = ( pCosX[ x ] - nSinY ) / 256.0; + double aUnrotatedY = ( pSinX[ x ] + nCosY ) / 256.0; + + if ( bHMirr ) + aUnrotatedX = aUnrotatedWidth - aUnrotatedX - 1; + if ( bVMirr ) + aUnrotatedY = aUnrotatedHeight - aUnrotatedY - 1; + + if( ( aUnrotatedX >= 0 ) && ( aUnrotatedX < aUnrotatedWidth ) && + ( aUnrotatedY >= 0 ) && ( aUnrotatedY < aUnrotatedHeight ) ) + { + double dYStart = ((aUnrotatedY + 0.5) * fRevScaleY) - 0.5; + double dYEnd = ((aUnrotatedY + 1.5) * fRevScaleY) - 0.5; + + int yStart = MinMax( dYStart, 0, aBitmapHeight - 1); + int yEnd = MinMax( dYEnd, 0, aBitmapHeight - 1); + + double dXStart = ((aUnrotatedX + 0.5) * fRevScaleX) - 0.5; + double dXEnd = ((aUnrotatedX + 1.5) * fRevScaleX) - 0.5; + + int xStart = MinMax( dXStart, 0, aBitmapWidth - 1); + int xEnd = MinMax( dXEnd, 0, aBitmapWidth - 1); + + aSumRed = aSumGreen = aSumBlue = 0.0; + aCount = 0; + + for (int yIn = yStart; yIn <= yEnd; yIn++) + { + Scanline pScanlineRead = pReadAccess->GetScanline( yIn ); + for (int xIn = xStart; xIn <= xEnd; xIn++) + { + if( pReadAccess->HasPalette() ) + aColor = pReadAccess->GetPaletteColor( pReadAccess->GetIndexFromData( pScanlineRead, xIn ) ); + else + aColor = pReadAccess->GetPixelFromData( pScanlineRead, xIn ); + + aSumRed += aColor.GetRed(); + aSumGreen += aColor.GetGreen(); + aSumBlue += aColor.GetBlue(); + + aCount++; + } + } + + aResultColor.SetRed( MinMax( aSumRed / aCount, 0, 255) ); + aResultColor.SetGreen( MinMax( aSumGreen / aCount, 0, 255) ); + aResultColor.SetBlue( MinMax( aSumBlue / aCount, 0, 255) ); + + pWriteAccess->SetPixelOnData( pScanline, x, aResultColor ); + } + } + } + } + + Bitmap::ReleaseAccess( pWriteAccess ); + bRet = true; + } + + Bitmap::ReleaseAccess( pReadAccess ); + } + + // mask processing + if( bRet && ( rBmpEx.IsTransparent() || ( nRot10 != 0 && nRot10 != 900 && nRot10 != 1800 && nRot10 != 2700 ) ) ) + { + bRet = false; + + if( rBmpEx.IsAlpha() ) + { + AlphaMask aAlpha( rBmpEx.GetAlpha() ); + AlphaMask aOutAlpha; + + pReadAccess = aAlpha.AcquireReadAccess(); + + if( pReadAccess ) + { + aOutAlpha = AlphaMask( Size( aTargetWidth, aTargetHeight ) ); + pWriteAccess = aOutAlpha.AcquireWriteAccess(); + + if( pWriteAccess ) + { + if( pReadAccess->GetScanlineFormat() == ScanlineFormat::N8BitPal && + pWriteAccess->GetScanlineFormat() == ScanlineFormat::N8BitPal ) + { + if ( !scaleByAveraging ) + { + Scanline pLine0, pLine1, pLineW; + + for( long nY = 0; nY < aTargetHeight; nY++ ) + { + nSinY = pSinY[ nY ]; + nCosY = pCosY[ nY ]; + pLineW = pWriteAccess->GetScanline( nY ); + + for( long nX = 0; nX < aTargetWidth; nX++ ) + { + nUnRotX = ( pCosX[ nX ] - nSinY ) >> 8; + nUnRotY = ( pSinX[ nX ] + nCosY ) >> 8; + + if( ( nUnRotX >= 0 ) && ( nUnRotX < aUnrotatedWidth ) && + ( nUnRotY >= 0 ) && ( nUnRotY < aUnrotatedHeight ) ) + { + nTmpX = pMapIX[ nUnRotX ]; + nTmpFX = pMapFX[ nUnRotX ]; + nTmpY = pMapIY[ nUnRotY ]; + nTmpFY = pMapFY[ nUnRotY ]; + + pLine0 = pReadAccess->GetScanline( nTmpY++ ); + pLine1 = pReadAccess->GetScanline( nTmpY ); + + const long nAlpha0 = pLine0[ nTmpX ]; + const long nAlpha2 = pLine1[ nTmpX++ ]; + const long nAlpha1 = pLine0[ nTmpX ]; + const long nAlpha3 = pLine1[ nTmpX ]; + const long n0 = MAP( nAlpha0, nAlpha1, nTmpFX ); + const long n1 = MAP( nAlpha2, nAlpha3, nTmpFX ); + + *pLineW++ = MAP( n0, n1, nTmpFY ); + } + else + *pLineW++ = 255; + } + } + } + else // scaleByAveraging + { + const BitmapColor aTrans( pWriteAccess->GetBestMatchingColor( COL_WHITE ) ); + BitmapColor aResultColor( 0 ); + double aSum, aCount; + + for( y = 0; y < aTargetHeight; y++ ) + { + nSinY = pSinY[ y ]; + nCosY = pCosY[ y ]; + Scanline pScanline = pWriteAccess->GetScanline( y ); + + for( x = 0; x < aTargetWidth; x++ ) + { + + double aUnrotatedX = ( pCosX[ x ] - nSinY ) / 256.0; + double aUnrotatedY = ( pSinX[ x ] + nCosY ) / 256.0; + + if ( bHMirr ) + aUnrotatedX = aUnrotatedWidth - aUnrotatedX - 1; + if ( bVMirr ) + aUnrotatedY = aUnrotatedHeight - aUnrotatedY - 1; + + if( ( aUnrotatedX >= 0 ) && ( aUnrotatedX < aUnrotatedWidth ) && + ( aUnrotatedY >= 0 ) && ( aUnrotatedY < aUnrotatedHeight ) ) + { + double dYStart = ((aUnrotatedY + 0.5) * fRevScaleY) - 0.5; + double dYEnd = ((aUnrotatedY + 1.5) * fRevScaleY) - 0.5; + + int yStart = MinMax( dYStart, 0, aBitmapHeight - 1); + int yEnd = MinMax( dYEnd, 0, aBitmapHeight - 1); + + double dXStart = ((aUnrotatedX + 0.5) * fRevScaleX) - 0.5; + double dXEnd = ((aUnrotatedX + 1.5) * fRevScaleX) - 0.5; + + int xStart = MinMax( dXStart, 0, aBitmapWidth - 1); + int xEnd = MinMax( dXEnd, 0, aBitmapWidth - 1); + + aSum = 0.0; + aCount = 0; + + for (int yIn = yStart; yIn <= yEnd; yIn++) + { + Scanline pScanlineRead = pReadAccess->GetScanline( yIn ); + for (int xIn = xStart; xIn <= xEnd; xIn++) + { + aSum += pReadAccess->GetPixelFromData( pScanlineRead, xIn ).GetIndex(); + aCount++; + } + } + aResultColor.SetIndex( MinMax( aSum / aCount, 0, 255) ); + pWriteAccess->SetPixelOnData( pScanline, x, aResultColor ); + } + else + { + pWriteAccess->SetPixelOnData( pScanline, x, aTrans ); + } + } + } + } + } + else + { + const BitmapColor aTrans( pWriteAccess->GetBestMatchingColor( COL_WHITE ) ); + BitmapColor aAlphaVal( 0 ); + + for( long nY = 0; nY < aTargetHeight; nY++ ) + { + nSinY = pSinY[ nY ]; + nCosY = pCosY[ nY ]; + Scanline pScanline = pWriteAccess->GetScanline( nY ); + + for( long nX = 0; nX < aTargetWidth; nX++ ) + { + nUnRotX = ( pCosX[ nX ] - nSinY ) >> 8; + nUnRotY = ( pSinX[ nX ] + nCosY ) >> 8; + + if( ( nUnRotX >= 0 ) && ( nUnRotX < aUnrotatedWidth ) && + ( nUnRotY >= 0 ) && ( nUnRotY < aUnrotatedHeight ) ) + { + nTmpX = pMapIX[ nUnRotX ]; + nTmpFX = pMapFX[ nUnRotX ]; + nTmpY = pMapIY[ nUnRotY ]; + nTmpFY = pMapFY[ nUnRotY ]; + + const long nAlpha0 = pReadAccess->GetPixel( nTmpY, nTmpX ).GetIndex(); + const long nAlpha1 = pReadAccess->GetPixel( nTmpY, ++nTmpX ).GetIndex(); + const long nAlpha3 = pReadAccess->GetPixel( ++nTmpY, nTmpX ).GetIndex(); + const long nAlpha2 = pReadAccess->GetPixel( nTmpY, --nTmpX ).GetIndex(); + const long n0 = MAP( nAlpha0, nAlpha1, nTmpFX ); + const long n1 = MAP( nAlpha2, nAlpha3, nTmpFX ); + + aAlphaVal.SetIndex( MAP( n0, n1, nTmpFY ) ); + pWriteAccess->SetPixelOnData( pScanline, nX, aAlphaVal ); + } + else + pWriteAccess->SetPixelOnData( pScanline, nX, aTrans ); + } + } + } + + aOutAlpha.ReleaseAccess( pWriteAccess ); + bRet = true; + } + + aAlpha.ReleaseAccess( pReadAccess ); + } + + if( bRet ) + rOutBmpEx = BitmapEx( aOutBmp, aOutAlpha ); + } + else + { + Bitmap aOutMsk( Size( aTargetWidth, aTargetHeight ), 1 ); + pWriteAccess = aOutMsk.AcquireWriteAccess(); + + if( pWriteAccess ) + { + Bitmap aMsk( rBmpEx.GetMask() ); + const BitmapColor aB( pWriteAccess->GetBestMatchingColor( COL_BLACK ) ); + const BitmapColor aW( pWriteAccess->GetBestMatchingColor( COL_WHITE ) ); + BitmapReadAccess* pMAcc = nullptr; + + if( !aMsk || ( ( pMAcc = aMsk.AcquireReadAccess() ) != nullptr ) ) + { + std::unique_ptr<long[]> pMapLX(new long[ aUnrotatedWidth ]); + std::unique_ptr<long[]> pMapLY(new long[ aUnrotatedHeight ]); + BitmapColor aTestB; + + if( pMAcc ) + aTestB = pMAcc->GetBestMatchingColor( COL_BLACK ); + + // create new horizontal mapping table + for( long nX = 0; nX < aUnrotatedWidth; nX++ ) + pMapLX[ nX ] = FRound( static_cast<double>(pMapIX[ nX ]) + pMapFX[ nX ] / 1048576.0 ); + + // create new vertical mapping table + for( long nY = 0; nY < aUnrotatedHeight; nY++ ) + pMapLY[ nY ] = FRound( static_cast<double>(pMapIY[ nY ]) + pMapFY[ nY ] / 1048576.0 ); + + // do mask rotation + for( long nY = 0; nY < aTargetHeight; nY++ ) + { + nSinY = pSinY[ nY ]; + nCosY = pCosY[ nY ]; + Scanline pScanline = pWriteAccess->GetScanline( nY ); + + for( long nX = 0; nX < aTargetWidth; nX++ ) + { + nUnRotX = ( pCosX[ nX ] - nSinY ) >> 8; + nUnRotY = ( pSinX[ nX ] + nCosY ) >> 8; + + if( ( nUnRotX >= 0 ) && ( nUnRotX < aUnrotatedWidth ) && + ( nUnRotY >= 0 ) && ( nUnRotY < aUnrotatedHeight ) ) + { + if( pMAcc ) + { + if( pMAcc->GetPixel( pMapLY[ nUnRotY ], pMapLX[ nUnRotX ] ) == aTestB ) + pWriteAccess->SetPixelOnData( pScanline, nX, aB ); + else + pWriteAccess->SetPixelOnData( pScanline, nX, aW ); + } + else + pWriteAccess->SetPixelOnData( pScanline, nX, aB ); + } + else + pWriteAccess->SetPixelOnData( pScanline, nX, aW ); + } + } + + pMapLX.reset(); + pMapLY.reset(); + + if( pMAcc ) + Bitmap::ReleaseAccess( pMAcc ); + + bRet = true; + } + + Bitmap::ReleaseAccess( pWriteAccess ); + } + + if( bRet ) + rOutBmpEx = BitmapEx( aOutBmp, aOutMsk ); + } + + if( !bRet ) + rOutBmpEx = aOutBmp; + } + else + rOutBmpEx = aOutBmp; + + return bRet; +} + +bool GraphicManager::ImplCreateOutput( OutputDevice* pOutputDevice, + const Point& rPoint, const Size& rSize, + const BitmapEx& rBitmapEx, const GraphicAttr& rAttributes, + BitmapEx* pBmpEx ) +{ + sal_uInt16 nRot10 = rAttributes.GetRotation() % 3600; + + Point aOutputPointPix; + Size aOutputSizePix; + Point aUnrotatedPointPix( pOutputDevice->LogicToPixel( rPoint ) ); + Size aUnrotatedSizePix( pOutputDevice->LogicToPixel( rSize ) ); + + bool bRet = false; + + if( nRot10 ) + { + tools::Polygon aPoly( tools::Rectangle( rPoint, rSize ) ); + aPoly.Rotate( rPoint, nRot10 ); + const tools::Rectangle aRotBoundRect( aPoly.GetBoundRect() ); + aOutputPointPix = pOutputDevice->LogicToPixel( aRotBoundRect.TopLeft() ); + aOutputSizePix = pOutputDevice->LogicToPixel( aRotBoundRect.GetSize() ); + } + else + { + aOutputPointPix = aUnrotatedPointPix; + aOutputSizePix = aUnrotatedSizePix; + } + + if( aUnrotatedSizePix.Width() && aUnrotatedSizePix.Height() ) + { + BitmapEx aOutBmpEx; + Point aOutPoint; + Size aOutSize; + const Size& rBmpSzPix = rBitmapEx.GetSizePixel(); + const long nW = rBmpSzPix.Width(); + const long nH = rBmpSzPix.Height(); + long nStartX = -1, nStartY = -1, nEndX = -1, nEndY = -1; + bool bHMirr( rAttributes.GetMirrorFlags() & BmpMirrorFlags::Horizontal ); + bool bVMirr( rAttributes.GetMirrorFlags() & BmpMirrorFlags::Vertical ); + + // calculate output sizes + if( !pBmpEx ) + { + tools::Rectangle aOutRect( Point(), pOutputDevice->GetOutputSizePixel() ); + tools::Rectangle aBmpRect( aOutputPointPix, aOutputSizePix ); + + if( pOutputDevice->GetOutDevType() == OUTDEV_WINDOW ) + { + const vcl::Region aPaintRgn( static_cast<vcl::Window*>( pOutputDevice )->GetPaintRegion() ); + if( !aPaintRgn.IsNull() ) + aOutRect.Intersection( pOutputDevice->LogicToPixel( aPaintRgn.GetBoundRect() ) ); + } + + aOutRect.Intersection( aBmpRect ); + + if( !aOutRect.IsEmpty() ) + { + aOutPoint = pOutputDevice->PixelToLogic( aOutRect.TopLeft() ); + aOutSize = pOutputDevice->PixelToLogic( aOutRect.GetSize() ); + nStartX = aOutRect.Left() - aBmpRect.Left(); + nStartY = aOutRect.Top() - aBmpRect.Top(); + nEndX = aOutRect.Right() - aBmpRect.Left(); + nEndY = aOutRect.Bottom() - aBmpRect.Top(); + } + else + { + nStartX = -1; // invalid + } + } + else + { + aOutPoint = pOutputDevice->PixelToLogic( aOutputPointPix ); + aOutSize = pOutputDevice->PixelToLogic( aOutputSizePix ); + nStartX = nStartY = 0; + nEndX = aOutputSizePix.Width() - 1; + nEndY = aOutputSizePix.Height() - 1; + } + + // do transformation + if( nStartX >= 0 ) + { + const bool bSimple = ( 1 == nW || 1 == nH ); + + if( nRot10 ) + { + if( bSimple ) + { + bRet = ( aOutBmpEx = rBitmapEx ).Scale( aUnrotatedSizePix ); + + if( bRet ) + aOutBmpEx.Rotate( nRot10, COL_TRANSPARENT ); + } + else + { + bRet = ImplCreateRotatedScaled( rBitmapEx, rAttributes, + nRot10, aUnrotatedSizePix, + nStartX, nEndX, nStartY, nEndY, + aOutBmpEx ); + } + } + else + { + if( !bHMirr && !bVMirr && aOutputSizePix == rBmpSzPix ) + { + aOutPoint = pOutputDevice->PixelToLogic( aOutputPointPix ); + aOutSize = pOutputDevice->PixelToLogic( aOutputSizePix ); + aOutBmpEx = rBitmapEx; + bRet = true; + } + else + { + if( bSimple ) + { + bRet = ( aOutBmpEx = rBitmapEx ).Scale( Size( nEndX - nStartX + 1, nEndY - nStartY + 1 ) ); + } + else + { + bRet = ImplCreateRotatedScaled( rBitmapEx, rAttributes, + nRot10, aUnrotatedSizePix, + nStartX, nEndX, nStartY, nEndY, + aOutBmpEx ); + } + } + } + + if( bRet ) + { + // Attribute adjustment if necessary + if( rAttributes.IsSpecialDrawMode() || rAttributes.IsAdjusted() || rAttributes.IsTransparent() ) + ImplAdjust( aOutBmpEx, rAttributes, GraphicAdjustmentFlags::DRAWMODE | GraphicAdjustmentFlags::COLORS | GraphicAdjustmentFlags::TRANSPARENCY ); + + // OutDev adjustment if necessary + if( pOutputDevice->GetOutDevType() != OUTDEV_PRINTER && pOutputDevice->GetBitCount() <= 8 && aOutBmpEx.GetBitCount() >= 8 ) + aOutBmpEx.Dither(); + } + } + + // Create output + if( bRet ) + { + if( !pBmpEx ) + pOutputDevice->DrawBitmapEx( aOutPoint, aOutSize, aOutBmpEx ); + else + { + if( !rAttributes.IsTransparent() && !aOutBmpEx.IsAlpha() ) + aOutBmpEx = BitmapEx( aOutBmpEx.GetBitmap().CreateDisplayBitmap( pOutputDevice ), aOutBmpEx.GetMask() ); + + pOutputDevice->DrawBitmapEx( aOutPoint, aOutSize, *pBmpEx = aOutBmpEx ); + } + } + } + + return bRet; +} + +// This function checks whether the bitmap is usable for skipping +// mtf rendering by using just this one bitmap (i.e. in case the metafile +// contains just this one pixmap that covers the entire metafile area). +static BitmapEx checkMetadataBitmap( const BitmapEx& rBmpEx, + Point rSrcPoint, + Size rSrcSize, + const Point& rDestPoint, + const Size& rDestSize, + const Size& rRefSize, + bool& o_rbNonBitmapActionEncountered ) +{ +// NOTE: If you do changes in this function, change checkMetadataBitmap() in grfcache.cxx too. + BitmapEx aBmpEx; + if( rSrcSize == Size()) + rSrcSize = rBmpEx.GetSizePixel(); + + if( rDestPoint != Point( 0, 0 )) + { // The pixmap in the metafile has an offset (and so would not cover) + // the entire result -> fall back to mtf rendering. + o_rbNonBitmapActionEncountered = true; + return aBmpEx; + } + if( rDestSize != rRefSize ) + { // The pixmap is not fullscale (does not cover the entire metafile area). + // HACK: The code here should refuse to use the bitmap directly + // and fall back to mtf rendering, but there seem to be metafiles + // that do not specify exactly their area (the Windows API requires apps + // the specify it manually, the rectangle is specified as topleft/bottomright + // rather than topleft/size [which may be confusing], and the docs + // on the exact meaning are somewhat confusing as well), so if it turns + // out this metafile really contains just one bitmap and no other painting, + // and if the sizes almost match, just use the pixmap (which will be scaled + // to fit exactly the requested size, so there should not be any actual problem + // caused by this small difference). This will allow caching of the resulting + // (scaled) pixmap, which can make a noticeable performance difference. + if( rBmpEx.GetSizePixel().Width() > 100 && rBmpEx.GetSizePixel().Height() > 100 + && std::abs( rDestSize.Width() - rRefSize.Width()) < 5 + && std::abs( rDestSize.Height() - rRefSize.Height()) < 5 ) + ; // ok, assume it's close enough + else + { // fall back to mtf rendering + o_rbNonBitmapActionEncountered = true; + return aBmpEx; + } + } + + aBmpEx = rBmpEx; + + if( (rSrcPoint.X() != 0 && rSrcPoint.Y() != 0) || + rSrcSize != rBmpEx.GetSizePixel() ) + { + // crop bitmap to given source rectangle (no + // need to copy and convert the whole bitmap) + const tools::Rectangle aCropRect( rSrcPoint, + rSrcSize ); + aBmpEx.Crop( aCropRect ); + } + + return aBmpEx; +} + +bool GraphicManager::ImplCreateOutput( OutputDevice* pOut, + const Point& rPt, const Size& rSz, + const GDIMetaFile& rMtf, const GraphicAttr& rAttr, + GDIMetaFile& rOutMtf, BitmapEx& rOutBmpEx ) +{ + const Size aNewSize( rMtf.GetPrefSize() ); + + rOutMtf = rMtf; + + // Count bitmap actions, and flag actions that paint, but + // are no bitmaps. + sal_Int32 nNumBitmaps(0); + bool bNonBitmapActionEncountered(false); + if( aNewSize.Width() && aNewSize.Height() && rSz.Width() && rSz.Height() ) + { + const double fGrfWH = static_cast<double>(aNewSize.Width()) / aNewSize.Height(); + const double fOutWH = static_cast<double>(rSz.Width()) / rSz.Height(); + + const double fScaleX = fOutWH / fGrfWH; + const double fScaleY = 1.0; + + const MapMode& rPrefMapMode( rMtf.GetPrefMapMode() ); + const Size rSizePix( pOut->LogicToPixel( aNewSize, rPrefMapMode ) ); + +// NOTE: If you do changes in this function, check GraphicDisplayCacheEntry::IsCacheableAsBitmap +// in grfcache.cxx too. + + // Determine whether the metafile basically displays + // a single bitmap (in which case that bitmap is simply used directly + // instead of playing the metafile). Note that + // the solution, as implemented here, is quite suboptimal (the + // cases where a mtf consisting basically of a single bitmap, + // that fail to pass the test below, are probably frequent). A + // better solution would involve FSAA, but that's currently + // expensive, and might trigger bugs on display drivers, if + // VDevs get bigger than the actual screen. + sal_uInt32 nCurPos; + MetaAction* pAct; + for( nCurPos = 0, pAct = rOutMtf.FirstAction(); pAct; + pAct = rOutMtf.NextAction(), nCurPos++ ) + { + MetaAction* pModAct = nullptr; + switch( pAct->GetType() ) + { + case MetaActionType::FONT: + { + // taking care of font width default if scaling metafile. + MetaFontAction* pA = static_cast<MetaFontAction*>(pAct); + vcl::Font aFont( pA->GetFont() ); + if ( !aFont.GetAverageFontWidth() ) + { + FontMetric aFontMetric( pOut->GetFontMetric( aFont ) ); + aFont.SetAverageFontWidth( aFontMetric.GetAverageFontWidth() ); + pModAct = new MetaFontAction( aFont ); + } + SAL_FALLTHROUGH; + } + case MetaActionType::NONE: + SAL_FALLTHROUGH; + + // OutDev state changes (which don't affect bitmap + // output) + case MetaActionType::LINECOLOR: + SAL_FALLTHROUGH; + case MetaActionType::FILLCOLOR: + SAL_FALLTHROUGH; + case MetaActionType::TEXTCOLOR: + SAL_FALLTHROUGH; + case MetaActionType::TEXTFILLCOLOR: + SAL_FALLTHROUGH; + case MetaActionType::TEXTALIGN: + SAL_FALLTHROUGH; + case MetaActionType::TEXTLINECOLOR: + SAL_FALLTHROUGH; + case MetaActionType::TEXTLINE: + SAL_FALLTHROUGH; + case MetaActionType::PUSH: + SAL_FALLTHROUGH; + case MetaActionType::POP: + SAL_FALLTHROUGH; + case MetaActionType::LAYOUTMODE: + SAL_FALLTHROUGH; + case MetaActionType::TEXTLANGUAGE: + SAL_FALLTHROUGH; + case MetaActionType::COMMENT: + break; + + // bitmap output methods + case MetaActionType::BMP: + if( !nNumBitmaps && !bNonBitmapActionEncountered ) + { + MetaBmpAction* pAction = static_cast<MetaBmpAction*>(pAct); + + rOutBmpEx = checkMetadataBitmap( + BitmapEx( pAction->GetBitmap()), + Point(), Size(), + pOut->LogicToPixel( pAction->GetPoint(), + rPrefMapMode ), + pAction->GetBitmap().GetSizePixel(), + rSizePix, + bNonBitmapActionEncountered ); + } + ++nNumBitmaps; + break; + + case MetaActionType::BMPSCALE: + if( !nNumBitmaps && !bNonBitmapActionEncountered ) + { + MetaBmpScaleAction* pAction = static_cast<MetaBmpScaleAction*>(pAct); + + rOutBmpEx = checkMetadataBitmap( + BitmapEx( pAction->GetBitmap()), + Point(), Size(), + pOut->LogicToPixel( pAction->GetPoint(), + rPrefMapMode ), + pOut->LogicToPixel( pAction->GetSize(), + rPrefMapMode ), + rSizePix, + bNonBitmapActionEncountered ); + } + ++nNumBitmaps; + break; + + case MetaActionType::BMPSCALEPART: + if( !nNumBitmaps && !bNonBitmapActionEncountered ) + { + MetaBmpScalePartAction* pAction = static_cast<MetaBmpScalePartAction*>(pAct); + + rOutBmpEx = checkMetadataBitmap( + BitmapEx( pAction->GetBitmap() ), + pAction->GetSrcPoint(), + pAction->GetSrcSize(), + pOut->LogicToPixel( pAction->GetDestPoint(), + rPrefMapMode ), + pOut->LogicToPixel( pAction->GetDestSize(), + rPrefMapMode ), + rSizePix, + bNonBitmapActionEncountered ); + } + ++nNumBitmaps; + break; + + case MetaActionType::BMPEX: + if( !nNumBitmaps && !bNonBitmapActionEncountered ) + { + MetaBmpExAction* pAction = static_cast<MetaBmpExAction*>(pAct); + + rOutBmpEx = checkMetadataBitmap( + pAction->GetBitmapEx(), + Point(), Size(), + pOut->LogicToPixel( pAction->GetPoint(), + rPrefMapMode ), + pAction->GetBitmapEx().GetSizePixel(), + rSizePix, + bNonBitmapActionEncountered ); + } + ++nNumBitmaps; + break; + + case MetaActionType::BMPEXSCALE: + if( !nNumBitmaps && !bNonBitmapActionEncountered ) + { + MetaBmpExScaleAction* pAction = static_cast<MetaBmpExScaleAction*>(pAct); + + rOutBmpEx = checkMetadataBitmap( + pAction->GetBitmapEx(), + Point(), Size(), + pOut->LogicToPixel( pAction->GetPoint(), + rPrefMapMode ), + pOut->LogicToPixel( pAction->GetSize(), + rPrefMapMode ), + rSizePix, + bNonBitmapActionEncountered ); + } + ++nNumBitmaps; + break; + + case MetaActionType::BMPEXSCALEPART: + if( !nNumBitmaps && !bNonBitmapActionEncountered ) + { + MetaBmpExScalePartAction* pAction = static_cast<MetaBmpExScalePartAction*>(pAct); + + rOutBmpEx = checkMetadataBitmap( pAction->GetBitmapEx(), + pAction->GetSrcPoint(), + pAction->GetSrcSize(), + pOut->LogicToPixel( pAction->GetDestPoint(), + rPrefMapMode ), + pOut->LogicToPixel( pAction->GetDestSize(), + rPrefMapMode ), + rSizePix, + bNonBitmapActionEncountered ); + } + ++nNumBitmaps; + break; + + // these actions actually output something (that's + // different from a bitmap) + case MetaActionType::RASTEROP: + if( static_cast<MetaRasterOpAction*>(pAct)->GetRasterOp() == RasterOp::OverPaint ) + break; + SAL_FALLTHROUGH; + case MetaActionType::PIXEL: + SAL_FALLTHROUGH; + case MetaActionType::POINT: + SAL_FALLTHROUGH; + case MetaActionType::LINE: + SAL_FALLTHROUGH; + case MetaActionType::RECT: + SAL_FALLTHROUGH; + case MetaActionType::ROUNDRECT: + SAL_FALLTHROUGH; + case MetaActionType::ELLIPSE: + SAL_FALLTHROUGH; + case MetaActionType::ARC: + SAL_FALLTHROUGH; + case MetaActionType::PIE: + SAL_FALLTHROUGH; + case MetaActionType::CHORD: + SAL_FALLTHROUGH; + case MetaActionType::POLYLINE: + SAL_FALLTHROUGH; + case MetaActionType::POLYGON: + SAL_FALLTHROUGH; + case MetaActionType::POLYPOLYGON: + SAL_FALLTHROUGH; + + case MetaActionType::TEXT: + SAL_FALLTHROUGH; + case MetaActionType::TEXTARRAY: + SAL_FALLTHROUGH; + case MetaActionType::STRETCHTEXT: + SAL_FALLTHROUGH; + case MetaActionType::TEXTRECT: + SAL_FALLTHROUGH; + + case MetaActionType::MASK: + SAL_FALLTHROUGH; + case MetaActionType::MASKSCALE: + SAL_FALLTHROUGH; + case MetaActionType::MASKSCALEPART: + SAL_FALLTHROUGH; + + case MetaActionType::GRADIENT: + SAL_FALLTHROUGH; + case MetaActionType::HATCH: + SAL_FALLTHROUGH; + case MetaActionType::WALLPAPER: + SAL_FALLTHROUGH; + + case MetaActionType::Transparent: + SAL_FALLTHROUGH; + case MetaActionType::EPS: + SAL_FALLTHROUGH; + case MetaActionType::FLOATTRANSPARENT: + SAL_FALLTHROUGH; + case MetaActionType::GRADIENTEX: + SAL_FALLTHROUGH; + + // OutDev state changes that _do_ affect bitmap + // output + case MetaActionType::CLIPREGION: + SAL_FALLTHROUGH; + case MetaActionType::ISECTRECTCLIPREGION: + SAL_FALLTHROUGH; + case MetaActionType::ISECTREGIONCLIPREGION: + SAL_FALLTHROUGH; + case MetaActionType::MOVECLIPREGION: + SAL_FALLTHROUGH; + + case MetaActionType::MAPMODE: + SAL_FALLTHROUGH; + case MetaActionType::REFPOINT: + SAL_FALLTHROUGH; + default: + bNonBitmapActionEncountered = true; + break; + } + if ( pModAct ) + { + MetaAction* pDeleteAction = rOutMtf.ReplaceAction( pModAct, nCurPos ); + assert(pDeleteAction); + pDeleteAction->Delete(); + } + else + { + if( pAct->GetRefCount() > 1 ) + { + MetaAction* pDeleteAction = rOutMtf.ReplaceAction( pModAct = pAct->Clone(), nCurPos ); + assert(pDeleteAction); + pDeleteAction->Delete(); + } + else + pModAct = pAct; + } + pModAct->Scale( fScaleX, fScaleY ); + } + rOutMtf.SetPrefSize( Size( FRound( aNewSize.Width() * fScaleX ), + FRound( aNewSize.Height() * fScaleY ) ) ); + } + + if( nNumBitmaps != 1 || bNonBitmapActionEncountered ) + { + if( rAttr.IsSpecialDrawMode() || rAttr.IsAdjusted() || rAttr.IsMirrored() || rAttr.IsRotated() || rAttr.IsTransparent() ) + ImplAdjust( rOutMtf, rAttr, GraphicAdjustmentFlags::ALL ); + + ImplDraw( pOut, rPt, rSz, rOutMtf, rAttr ); + rOutBmpEx = BitmapEx(); + } + + return true; +} + +void GraphicManager::ImplAdjust( 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(aAttr.GetTransparency()); + } +} + +void GraphicManager::ImplAdjust( 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 GraphicManager::ImplAdjust( 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" ); + } +} + +void GraphicManager::ImplDraw( OutputDevice* pOut, const Point& rPt, const Size& rSz, + const GDIMetaFile& rMtf, const GraphicAttr& rAttr ) +{ + sal_uInt16 nRot10 = rAttr.GetRotation() % 3600; + Point aOutPt( rPt ); + Size aOutSz( rSz ); + + if( nRot10 ) + { + tools::Polygon aPoly( tools::Rectangle( aOutPt, aOutSz ) ); + + aPoly.Rotate( aOutPt, nRot10 ); + const tools::Rectangle aRotBoundRect( aPoly.GetBoundRect() ); + aOutPt = aRotBoundRect.TopLeft(); + aOutSz = aRotBoundRect.GetSize(); + } + + pOut->Push( PushFlags::CLIPREGION ); + pOut->IntersectClipRegion( tools::Rectangle( aOutPt, aOutSz ) ); + + const_cast<GDIMetaFile&>(rMtf).WindStart(); + const_cast<GDIMetaFile&>(rMtf).Play( pOut, aOutPt, aOutSz ); + const_cast<GDIMetaFile&>(rMtf).WindStart(); + + pOut->Pop(); +} + +struct ImplTileInfo +{ + ImplTileInfo() : aTileTopLeft(), aNextTileTopLeft(), aTileSizePixel(), 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, GraphicManagerDrawFlags nFlags ) +{ + // 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, nFlags, 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, + GraphicManagerDrawFlags nFlags, 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, nFlags, aTileInfo ) ) + { + // extract generated tile -> see comment on the first loop below + BitmapEx aTileBitmap( rVDev.GetBitmap( aTileInfo.aTileTopLeft, aTileInfo.aTileSizePixel ) ); + + xTmpGraphic.reset(new GraphicObject(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, nFlags)) + 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( 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, nFlags)) + return false; + + aCurrPos.AdjustY(aTileInfo.aTileSizePixel.Height() ); + } + +#ifdef DBG_TEST + rVDev.DrawEllipse( 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, nFlags)) + 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( 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* pOut, const tools::Rectangle& rArea, const Size& rSizePixel, + const Size& rOffset, const GraphicAttr* pAttr, GraphicManagerDrawFlags nFlags, int nTileCacheSize1D ) +{ + const MapMode aOutMapMode( pOut->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.get(), nNumTilesInCacheX, + nNumTilesInCacheY, rSizePixel, pAttr, nFlags ) ) + { + BitmapEx aTileBitmap( aVDev->GetBitmap( Point(0,0), aVDev->GetOutputSize() ) ); + + // draw alpha content, if any + if( IsTransparent() ) + { + GraphicObject aAlphaGraphic; + + if( GetGraphic().IsAlpha() ) + aAlphaGraphic.SetGraphic( GetGraphic().GetBitmapEx().GetAlpha().GetBitmap() ); + else + aAlphaGraphic.SetGraphic( GetGraphic().GetBitmapEx().GetMask() ); + + if( aAlphaGraphic.ImplRenderTempTile( *aVDev.get(), nNumTilesInCacheX, + nNumTilesInCacheY, rSizePixel, pAttr, nFlags ) ) + { + // 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() ).CreateMask( COL_WHITE ) ); + } + } + + // paint generated tile + GraphicObject aTmpGraphic( aTileBitmap ); + bRet = aTmpGraphic.ImplDrawTiled( pOut, rArea, + aTileBitmap.GetSizePixel(), + rOffset, pAttr, nFlags, nTileCacheSize1D ); + } + } + else + { + const Size aOutOffset( pOut->LogicToPixel( rOffset, aOutMapMode ) ); + const tools::Rectangle aOutArea( pOut->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( pOut->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() ); + + pOut->Push( PushFlags::CLIPREGION ); + pOut->IntersectClipRegion( rArea ); + + // Paint all tiles + + + bRet = ImplDrawTiled( *pOut, 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, nFlags ); + + pOut->Pop(); + } + + return bRet; +} + +bool GraphicObject::ImplDrawTiled( OutputDevice& rOut, const Point& rPosPixel, + int nNumTilesX, int nNumTilesY, + const Size& rTileSizePixel, const GraphicAttr* pAttr, GraphicManagerDrawFlags nFlags ) +{ + 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, nFlags ); + + 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.IsTransparent() ) + { + if( rBmpEx.IsAlpha() ) + aBmpEx2 = BitmapEx( rBmpEx.GetBitmap(), rBmpEx.GetAlpha() ); + else + aBmpEx2 = BitmapEx( rBmpEx.GetBitmap(), rBmpEx.GetMask() ); + } + else + { + // #104115# Generate mask bitmap and init to zero + Bitmap aMask( aBmpSize, 1 ); + aMask.Erase( Color(0,0,0) ); + + // #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.SetSizePixel( Size(nPadTotalWidth, nPadTotalHeight) ); + aBmpEx2.Erase( Color(0xFF,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 && !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/UnoGraphic.cxx b/vcl/source/graphic/UnoGraphic.cxx new file mode 100644 index 000000000000..5821a254da92 --- /dev/null +++ b/vcl/source/graphic/UnoGraphic.cxx @@ -0,0 +1,206 @@ +/* -*- 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 <vcl/svapp.hxx> +#include <com/sun/star/graphic/GraphicType.hpp> +#include <com/sun/star/graphic/XGraphicTransformer.hpp> +#include <vcl/dibtools.hxx> +#include "UnoGraphic.hxx" +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <string.h> + +using namespace com::sun::star; + +namespace unographic { + +Graphic::Graphic() : + maGraphic() +{ +} + +Graphic::~Graphic() throw() +{ +} + +void Graphic::init( const ::Graphic& rGraphic ) + throw() +{ + maGraphic = ::Graphic(rGraphic); + unographic::GraphicDescriptor::init(maGraphic); +} + +uno::Any SAL_CALL Graphic::queryAggregation( 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<lang::XUnoTunnel>::get()) + aAny <<= uno::Reference< lang::XUnoTunnel >(this); + else + aAny = ::unographic::GraphicDescriptor::queryAggregation( rType ); + + return aAny; +} + +uno::Any SAL_CALL Graphic::queryInterface( const uno::Type & rType ) +{ + css::uno::Any aReturn = ::unographic::GraphicDescriptor::queryInterface( rType ); + if ( !aReturn.hasValue() ) + aReturn = ::cppu::queryInterface ( rType, static_cast< graphic::XGraphicTransformer*>( this ) ); + return aReturn; +} + +void SAL_CALL Graphic::acquire() + throw() +{ + unographic::GraphicDescriptor::acquire(); +} + +void SAL_CALL Graphic::release() throw() +{ + unographic::GraphicDescriptor::release(); +} + +OUString SAL_CALL Graphic::getImplementationName() +{ + return OUString( "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() ); + uno::Sequence< OUString > aNew { "com.sun.star.graphic.Graphic" }; + sal_Int32 nOldCount = aRet.getLength(); + + aRet.realloc( nOldCount + aNew.getLength() ); + + for( sal_Int32 i = 0; i < aNew.getLength(); ++i ) + aRet[ nOldCount++ ] = aNew[ i ]; + + return aRet; +} + +uno::Sequence< uno::Type > SAL_CALL Graphic::getTypes() +{ + uno::Sequence< uno::Type > aRet( ::unographic::GraphicDescriptor::getTypes() ); + sal_Int32 nOldCount = aRet.getLength(); + + aRet.realloc( nOldCount + 2 ); + aRet[ nOldCount ] = cppu::UnoType<graphic::XGraphic>::get(); + aRet[ nOldCount+1 ] = cppu::UnoType<awt::XBitmap>::get(); + + return aRet; +} + +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) + { + ::GraphicType eType = maGraphic.GetType(); + + if (eType != ::GraphicType::NONE) + { + cRet = (eType == ::GraphicType::Bitmap) ? graphic::GraphicType::PIXEL + : graphic::GraphicType::VECTOR; + } + } + + return cRet; +} + +// XBitmap + +awt::Size SAL_CALL Graphic::getSize() +{ + SolarMutexGuard aGuard; + + Size aVclSize; + if (!!maGraphic && maGraphic.GetType() != ::GraphicType::NONE) + { + aVclSize = maGraphic.GetSizePixel(); + } + return awt::Size(aVclSize.Width(), aVclSize.Height()); +} + +uno::Sequence<sal_Int8> SAL_CALL Graphic::getDIB() +{ + SolarMutexGuard aGuard; + + if (!!maGraphic && maGraphic.GetType() != ::GraphicType::NONE) + { + 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 && maGraphic.GetType() != ::GraphicType::NONE) + { + SvMemoryStream aMemoryStream; + + WriteDIB(maGraphic.GetBitmapEx().GetMask(), aMemoryStream, false, true); + return css::uno::Sequence<sal_Int8>( static_cast<sal_Int8 const *>(aMemoryStream.GetData()), aMemoryStream.Tell() ); + } + else + { + return uno::Sequence<sal_Int8>(); + } +} + +const ::Graphic* Graphic::getImplementation( const uno::Reference< uno::XInterface >& rxIFace ) + throw() +{ + uno::Reference< lang::XUnoTunnel > xTunnel( rxIFace, uno::UNO_QUERY ); + return( xTunnel.is() ? reinterpret_cast< ::Graphic* >( xTunnel->getSomething( ::Graphic::getUnoTunnelId() ) ) : nullptr ); +} + +sal_Int64 SAL_CALL Graphic::getSomething( const uno::Sequence< sal_Int8 >& rId ) +{ + return( ( rId.getLength() == 16 && 0 == memcmp( ::Graphic::getUnoTunnelId().getConstArray(), rId.getConstArray(), 16 ) ) ? + reinterpret_cast<sal_Int64>(&maGraphic) : 0 ); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/graphic/UnoGraphic.hxx b/vcl/source/graphic/UnoGraphic.hxx new file mode 100644 index 000000000000..f987d902eace --- /dev/null +++ b/vcl/source/graphic/UnoGraphic.hxx @@ -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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_VCL_SOURCE_GRAPHIC_GRAPHIC_HXX +#define INCLUDED_VCL_SOURCE_GRAPHIC_GRAPHIC_HXX + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> +#include <com/sun/star/lang/XUnoTunnel.hpp> +#include <com/sun/star/awt/XBitmap.hpp> + +#include "UnoGraphicDescriptor.hxx" +#include "UnoGraphicTransformer.hxx" + +#include <vcl/graph.hxx> + +namespace unographic { + +class Graphic : public css::graphic::XGraphic, + public css::awt::XBitmap, + public css::lang::XUnoTunnel, + public ::unographic::GraphicDescriptor, + public ::unographic::GraphicTransformer +{ +public: + Graphic(); + virtual ~Graphic() throw() override; + + using ::unographic::GraphicDescriptor::init; + void init( const ::Graphic& rGraphic ) throw(); + + static const ::Graphic* getImplementation( const css::uno::Reference< css::uno::XInterface >& rxIFace ) throw(); + +protected: + + // XInterface + virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type & rType ) override; + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() throw() override; + virtual void SAL_CALL release() throw() override; + + // 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; + + // XGraphic + virtual ::sal_Int8 SAL_CALL getType( ) override; + + // XBitmap + virtual css::awt::Size SAL_CALL getSize( ) override; + virtual css::uno::Sequence< ::sal_Int8 > SAL_CALL getDIB( ) override; + virtual css::uno::Sequence< ::sal_Int8 > SAL_CALL getMaskDIB( ) override; + + // XUnoTunnel + virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& rId ) override; + +private: + + ::Graphic maGraphic; +}; + +} + +#endif + +/* 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 000000000000..cdc54568d387 --- /dev/null +++ b/vcl/source/graphic/UnoGraphicDescriptor.cxx @@ -0,0 +1,405 @@ +/* -*- 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 "UnoGraphicDescriptor.hxx" + +#include <unotools/ucbstreamhelper.hxx> +#include <vcl/graphicfilter.hxx> +#include <svl/itemprop.hxx> +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/supportsservice.hxx> + +#include <com/sun/star/beans/PropertyState.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/graphic/GraphicType.hpp> + +#include <vcl/graph.hxx> +#include <vcl/svapp.hxx> +#include <memory> + + +enum class UnoGraphicProperty +{ + GraphicType = 1 + , MimeType = 2 + , SizePixel = 3 + , Size100thMM = 4 + , BitsPerPixel = 5 + , Transparent = 6 + , Alpha = 7 + , Animated = 8 +}; + + +using namespace ::com::sun::star; + +namespace unographic { + + +GraphicDescriptor::GraphicDescriptor() : + ::comphelper::PropertySetHelper( createPropertySetInfo() ), + mpGraphic( nullptr ), + meType( GraphicType::NONE ), + mnBitsPerPixel ( 0 ), + mbTransparent ( false ) +{ +} + +GraphicDescriptor::~GraphicDescriptor() + throw() +{ +} + +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; + + const char* pMimeType = nullptr; + sal_uInt8 cType = graphic::GraphicType::EMPTY; + + switch( aDescriptor.GetFileFormat() ) + { + case GraphicFileFormat::BMP: pMimeType = MIMETYPE_BMP; cType = graphic::GraphicType::PIXEL; break; + case GraphicFileFormat::GIF: pMimeType = MIMETYPE_GIF; cType = graphic::GraphicType::PIXEL; break; + case GraphicFileFormat::JPG: pMimeType = MIMETYPE_JPG; cType = graphic::GraphicType::PIXEL; break; + case GraphicFileFormat::PCD: pMimeType = MIMETYPE_PCD; cType = graphic::GraphicType::PIXEL; break; + case GraphicFileFormat::PCX: pMimeType = MIMETYPE_PCX; cType = graphic::GraphicType::PIXEL; break; + case GraphicFileFormat::PNG: pMimeType = MIMETYPE_PNG; cType = graphic::GraphicType::PIXEL; break; + case GraphicFileFormat::TIF: pMimeType = MIMETYPE_TIF; cType = graphic::GraphicType::PIXEL; break; + case GraphicFileFormat::XBM: pMimeType = MIMETYPE_XBM; cType = graphic::GraphicType::PIXEL; break; + case GraphicFileFormat::XPM: pMimeType = MIMETYPE_XPM; cType = graphic::GraphicType::PIXEL; break; + case GraphicFileFormat::PBM: pMimeType = MIMETYPE_PBM; cType = graphic::GraphicType::PIXEL; break; + case GraphicFileFormat::PGM: pMimeType = MIMETYPE_PGM; cType = graphic::GraphicType::PIXEL; break; + case GraphicFileFormat::PPM: pMimeType = MIMETYPE_PPM; cType = graphic::GraphicType::PIXEL; break; + case GraphicFileFormat::RAS: pMimeType = MIMETYPE_RAS; cType = graphic::GraphicType::PIXEL; break; + case GraphicFileFormat::TGA: pMimeType = MIMETYPE_TGA; cType = graphic::GraphicType::PIXEL; break; + case GraphicFileFormat::PSD: pMimeType = MIMETYPE_PSD; cType = graphic::GraphicType::PIXEL; break; + + case GraphicFileFormat::EPS: pMimeType = MIMETYPE_EPS; cType = graphic::GraphicType::VECTOR; break; + case GraphicFileFormat::DXF: pMimeType = MIMETYPE_DXF; cType = graphic::GraphicType::VECTOR; break; + case GraphicFileFormat::MET: pMimeType = MIMETYPE_MET; cType = graphic::GraphicType::VECTOR; break; + case GraphicFileFormat::PCT: pMimeType = MIMETYPE_PCT; cType = graphic::GraphicType::VECTOR; break; + case GraphicFileFormat::SVM: pMimeType = MIMETYPE_SVM; cType = graphic::GraphicType::VECTOR; break; + case GraphicFileFormat::WMF: pMimeType = MIMETYPE_WMF; cType = graphic::GraphicType::VECTOR; break; + case GraphicFileFormat::EMF: pMimeType = MIMETYPE_EMF; cType = graphic::GraphicType::VECTOR; break; + case GraphicFileFormat::SVG: pMimeType = MIMETYPE_SVG; cType = graphic::GraphicType::VECTOR; break; + + default: + break; + } + + if( graphic::GraphicType::EMPTY != cType ) + { + meType = ( ( graphic::GraphicType::PIXEL == cType ) ? GraphicType::Bitmap : GraphicType::GdiMetafile ); + maMimeType = OUString( pMimeType, strlen(pMimeType), RTL_TEXTENCODING_ASCII_US ); + maSizePixel = aDescriptor.GetSizePixel(); + maSize100thMM = aDescriptor.GetSize_100TH_MM(); + mnBitsPerPixel = aDescriptor.GetBitsPerPixel(); + mbTransparent = ( graphic::GraphicType::VECTOR == cType ); + } +} + + +uno::Any SAL_CALL GraphicDescriptor::queryAggregation( 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 = OWeakAggObject::queryAggregation( rType ); + + return aAny; +} + + +uno::Any SAL_CALL GraphicDescriptor::queryInterface( const uno::Type & rType ) +{ + return OWeakAggObject::queryInterface( rType ); +} + + +void SAL_CALL GraphicDescriptor::acquire() + throw() +{ + OWeakAggObject::acquire(); +} + + +void SAL_CALL GraphicDescriptor::release() + throw() +{ + OWeakAggObject::release(); +} + + +OUString SAL_CALL GraphicDescriptor::getImplementationName() +{ + return OUString( "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() +{ + uno::Sequence< uno::Type > aTypes( 6 ); + uno::Type* pTypes = aTypes.getArray(); + + *pTypes++ = cppu::UnoType<uno::XAggregation>::get(); + *pTypes++ = cppu::UnoType<lang::XServiceInfo>::get(); + *pTypes++ = cppu::UnoType<lang::XTypeProvider>::get(); + *pTypes++ = cppu::UnoType<beans::XPropertySet>::get(); + *pTypes++ = cppu::UnoType<beans::XPropertyState>::get(); + *pTypes++ = 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(), 0, css::uno::Type(), 0, 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->IsLink() ) + { + const char* pMimeType; + + switch( mpGraphic->GetLink().GetType() ) + { + case GfxLinkType::NativeGif: pMimeType = MIMETYPE_GIF; break; + + // #i15508# added BMP type for better exports (checked, works) + case GfxLinkType::NativeBmp: pMimeType = MIMETYPE_BMP; break; + + case GfxLinkType::NativeJpg: pMimeType = MIMETYPE_JPG; break; + case GfxLinkType::NativePng: pMimeType = MIMETYPE_PNG; break; + case GfxLinkType::NativeWmf: pMimeType = MIMETYPE_WMF; break; + case GfxLinkType::NativeMet: pMimeType = MIMETYPE_MET; break; + case GfxLinkType::NativePct: pMimeType = MIMETYPE_PCT; break; + + // added Svg mimetype support + case GfxLinkType::NativeSvg: pMimeType = MIMETYPE_SVG; break; + case GfxLinkType::NativePdf: pMimeType = MIMETYPE_PDF; break; + + default: + pMimeType = nullptr; + break; + } + + if( pMimeType ) + aMimeType = OUString::createFromAscii( pMimeType ); + } + + 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->GetBitmapEx().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 ) + nBitsPerPixel = mpGraphic->GetBitmapEx().GetBitmap().GetBitCount(); + } + 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; + } + + ++ppEntries; + ++pValues; + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/graphic/UnoGraphicDescriptor.hxx b/vcl/source/graphic/UnoGraphicDescriptor.hxx new file mode 100644 index 000000000000..f163a5023c2a --- /dev/null +++ b/vcl/source/graphic/UnoGraphicDescriptor.hxx @@ -0,0 +1,120 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_VCL_SOURCE_GRAPHIC_DESCRIPTOR_HXX +#define INCLUDED_VCL_SOURCE_GRAPHIC_DESCRIPTOR_HXX + +#include <comphelper/propertysethelper.hxx> +#include <com/sun/star/lang/XServiceInfo.hpp> + +#include <comphelper/propertysetinfo.hxx> +#include <vcl/graph.hxx> + +#define MIMETYPE_BMP "image/x-MS-bmp" +#define MIMETYPE_GIF "image/gif" +#define MIMETYPE_JPG "image/jpeg" +#define MIMETYPE_PCD "image/x-photo-cd" +#define MIMETYPE_PCX "image/x-pcx" +#define MIMETYPE_PNG "image/png" +#define MIMETYPE_TIF "image/tiff" +#define MIMETYPE_XBM "image/x-xbitmap" +#define MIMETYPE_XPM "image/x-xpixmap" +#define MIMETYPE_PBM "image/x-portable-bitmap" +#define MIMETYPE_PGM "image/x-portable-graymap" +#define MIMETYPE_PPM "image/x-portable-pixmap" +#define MIMETYPE_RAS "image/x-cmu-raster" +#define MIMETYPE_TGA "image/x-targa" +#define MIMETYPE_PSD "image/vnd.adobe.photoshop" +#define MIMETYPE_EPS "image/x-eps" +#define MIMETYPE_DXF "image/vnd.dxf" +#define MIMETYPE_MET "image/x-met" +#define MIMETYPE_PCT "image/x-pict" +#define MIMETYPE_SVM "image/x-svm" +#define MIMETYPE_WMF "image/x-wmf" +#define MIMETYPE_EMF "image/x-emf" +#define MIMETYPE_SVG "image/svg+xml" +#define MIMETYPE_PDF "application/pdf" +#define MIMETYPE_VCLGRAPHIC "image/x-vclgraphic" + +namespace comphelper { class PropertySetInfo; } +namespace com { namespace sun { namespace star { namespace io { class XInputStream; } } } } + +class Graphic; + +namespace unographic { + +class GraphicDescriptor : public ::cppu::OWeakAggObject, + public css::lang::XServiceInfo, + public css::lang::XTypeProvider, + public ::comphelper::PropertySetHelper +{ +public: + + GraphicDescriptor(); + virtual ~GraphicDescriptor() throw() override; + + void init( const ::Graphic& rGraphic ); + void init( const OUString& rURL ); + void init( const css::uno::Reference< css::io::XInputStream >& rxIStm, const OUString& rURL ); + +protected: + + static rtl::Reference<::comphelper::PropertySetInfo> createPropertySetInfo(); + + // XInterface + virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type & rType ) override; + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() throw() override; + virtual void SAL_CALL release() throw() override; + + // 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; + + // PropertySetHelper + virtual void _setPropertyValues( const comphelper::PropertyMapEntry** ppEntries, const css::uno::Any* pValues ) override; + virtual void _getPropertyValues( const comphelper::PropertyMapEntry** ppEntries, css::uno::Any* pValue ) override; + +private: + + const ::Graphic* mpGraphic; + GraphicType meType; + OUString maMimeType; + Size maSizePixel; + Size maSize100thMM; + sal_uInt16 mnBitsPerPixel; + bool mbTransparent; + + GraphicDescriptor( const GraphicDescriptor& rDescriptor ) = delete; + + GraphicDescriptor& operator=( const GraphicDescriptor& ) = delete; + + void implCreate( SvStream& rIStm, const OUString* pPath ); +}; + +} + +#endif + +/* 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 000000000000..8b1a1fa5e91e --- /dev/null +++ b/vcl/source/graphic/UnoGraphicObject.cxx @@ -0,0 +1,118 @@ +/* -*- 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/IllegalArgumentException.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <vcl/GraphicObject.hxx> +#include <rtl/ref.hxx> + +using namespace com::sun::star; + +namespace { + +typedef ::cppu::WeakImplHelper< graphic::XGraphicObject, css::lang::XServiceInfo > GObjectAccess_BASE; + // Simple uno wrapper around the GraphicObject class to allow basic + // access. ( and solves a horrible cyclic link problem between + // goodies/toolkit/extensions ) +class GObjectImpl : public GObjectAccess_BASE +{ + ::osl::Mutex m_aMutex; + std::unique_ptr< GraphicObject > mpGObject; +public: + /// @throws uno::RuntimeException + explicit GObjectImpl(uno::Sequence< uno::Any > const & args); + + // XGraphicObject + virtual uno::Reference< graphic::XGraphic > SAL_CALL getGraphic() override; + virtual void SAL_CALL setGraphic( const uno::Reference< graphic::XGraphic >& _graphic ) override; + OUString SAL_CALL getUniqueID() override; + + virtual OUString SAL_CALL getImplementationName() override + { + return OUString("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 + { + uno::Sequence<OUString> aRet { "com.sun.star.graphic.GraphicObject" }; + return aRet; + } +}; + +GObjectImpl::GObjectImpl(const uno::Sequence< uno::Any >& args) +{ + if ( args.getLength() == 1 ) + { + OUString sId; + if ( !( args[ 0 ] >>= sId ) || sId.isEmpty() ) + throw lang::IllegalArgumentException(); + OString bsId(OUStringToOString(sId, RTL_TEXTENCODING_UTF8)); + mpGObject.reset( new GraphicObject( bsId ) ); + } + else + mpGObject.reset( new GraphicObject() ); +} + +uno::Reference< graphic::XGraphic > SAL_CALL GObjectImpl::getGraphic() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( !mpGObject.get() ) + throw uno::RuntimeException(); + return mpGObject->GetGraphic().GetXGraphic(); +} + +void SAL_CALL GObjectImpl::setGraphic( const uno::Reference< graphic::XGraphic >& _graphic ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + if ( !mpGObject.get() ) + throw uno::RuntimeException(); + Graphic aGraphic( _graphic ); + mpGObject->SetGraphic( aGraphic ); +} + +OUString SAL_CALL GObjectImpl::getUniqueID() +{ + ::osl::MutexGuard aGuard( m_aMutex ); + OUString sId; + if ( mpGObject.get() ) + sId = OStringToOUString(mpGObject->GetUniqueID(), RTL_TEXTENCODING_ASCII_US); + return sId; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_graphic_GraphicObject_get_implementation( + SAL_UNUSED_PARAMETER css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &arguments) +{ + return cppu::acquire(new GObjectImpl(arguments)); +} + +/* 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 000000000000..1c4595e7aeda --- /dev/null +++ b/vcl/source/graphic/UnoGraphicProvider.cxx @@ -0,0 +1,847 @@ +/* -*- 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 <vcl/svapp.hxx> +#include <vcl/image.hxx> +#include <vcl/metaact.hxx> +#include <vcl/msgbox.hxx> +#include <vcl/imagerepository.hxx> +#include <unotools/resmgr.hxx> +#include <tools/fract.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <vcl/graphicfilter.hxx> +#include <vcl/wmf.hxx> +#include <svl/solar.hrc> +#include <vcl/virdev.hxx> +#include <vcl/settings.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 "UnoGraphicDescriptor.hxx" +#include "UnoGraphic.hxx" +#include <rtl/ref.hxx> +#include <vcl/GraphicObject.hxx> +#include <vcl/dibtools.hxx> +#include <comphelper/sequence.hxx> +#include <memory> +#include <svtools/ehdl.hxx> + +using namespace com::sun::star; + +namespace { + +#define UNO_NAME_GRAPHOBJ_URLPREFIX "vnd.sun.star.GraphicObject:" + +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( const OUString& rResourceURL ); + static css::uno::Reference< css::graphic::XGraphic > implLoadGraphicObject( const OUString& rResourceURL ); + static css::uno::Reference< css::graphic::XGraphic > implLoadRepositoryImage( const OUString& 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( const OUString& rResourceURL ); +}; + +GraphicProvider::GraphicProvider() +{ +} + +OUString SAL_CALL GraphicProvider::getImplementationName() +{ + return OUString( "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() +{ + uno::Sequence<OUString> aSeq { "com.sun.star.graphic.GraphicProvider" }; + return aSeq; +} + +uno::Sequence< uno::Type > SAL_CALL GraphicProvider::getTypes() +{ + uno::Sequence< uno::Type > aTypes( 3 ); + uno::Type* pTypes = aTypes.getArray(); + + *pTypes++ = cppu::UnoType<lang::XServiceInfo>::get(); + *pTypes++ = cppu::UnoType<lang::XTypeProvider>::get(); + *pTypes++ = 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::implLoadGraphicObject( const OUString& rResourceURL ) +{ + uno::Reference< ::graphic::XGraphic > xRet; + if( rResourceURL.startsWith( UNO_NAME_GRAPHOBJ_URLPREFIX ) ) + { + // graphic manager url + OUString aTmpStr( rResourceURL.copy( sizeof( UNO_NAME_GRAPHOBJ_URLPREFIX ) - 1 ) ); + OString aUniqueID(OUStringToOString(aTmpStr, + RTL_TEXTENCODING_UTF8)); + GraphicObject aGrafObj(aUniqueID); + // I don't call aGrafObj.GetXGraphic because it will call us back + // into implLoadMemory ( with "private:memorygraphic" test ) + ::unographic::Graphic* pUnoGraphic = new ::unographic::Graphic; + pUnoGraphic->init( aGrafObj.GetGraphic() ); + xRet = pUnoGraphic; + } + return xRet; +} + +uno::Reference< ::graphic::XGraphic > GraphicProvider::implLoadMemory( const OUString& rResourceURL ) +{ + uno::Reference< ::graphic::XGraphic > xRet; + sal_Int32 nIndex = 0; + + if( rResourceURL.getToken( 0, '/', nIndex ) == "private:memorygraphic" ) + { + sal_Int64 nGraphicAddress = rResourceURL.getToken( 0, '/', nIndex ).toInt64(); + + if( nGraphicAddress ) + { + ::unographic::Graphic* pUnoGraphic = new ::unographic::Graphic; + + pUnoGraphic->init( *reinterpret_cast< ::Graphic* >( nGraphicAddress ) ); + xRet = pUnoGraphic; + } + } + + return xRet; +} + + +uno::Reference< ::graphic::XGraphic > GraphicProvider::implLoadRepositoryImage( const OUString& rResourceURL ) +{ + uno::Reference< ::graphic::XGraphic > xRet; + sal_Int32 nIndex = 0; + + if( rResourceURL.getToken( 0, '/', nIndex ) == "private:graphicrepository" ) + { + OUString sPathName( rResourceURL.copy( nIndex ) ); + BitmapEx aBitmap; + if ( vcl::ImageRepository::loadImage( sPathName, aBitmap ) ) + { + xRet = Graphic(aBitmap).GetXGraphic(); + } + } + return xRet; +} + + +uno::Reference< ::graphic::XGraphic > GraphicProvider::implLoadStandardImage( const OUString& rResourceURL ) +{ + uno::Reference< ::graphic::XGraphic > xRet; + sal_Int32 nIndex = 0; + + if( rResourceURL.getToken( 0, '/', nIndex ) == "private:standardimage" ) + { + OUString sImageName( rResourceURL.copy( nIndex ) ); + if ( sImageName == "info" ) + { + xRet = Graphic(GetStandardInfoBoxImage().GetBitmapEx()).GetXGraphic(); + } + else if ( sImageName == "warning" ) + { + xRet = Graphic(GetStandardWarningBoxImage().GetBitmapEx()).GetXGraphic(); + } + else if ( sImageName == "error" ) + { + xRet = Graphic(GetStandardErrorBoxImage().GetBitmapEx()).GetXGraphic(); + } + else if ( sImageName == "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.getLength() ) + { + 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() ) + { + ::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( sal_Int32 i = 0; ( i < rMediaProperties.getLength() ) && !xRet.is(); ++i ) + { + const OUString aName( rMediaProperties[ i ].Name ); + const uno::Any aValue( rMediaProperties[ i ].Value ); + + if (aName == "URL") + { + aValue >>= aURL; + } + else if (aName == "InputStream") + { + aValue >>= xIStm; + } + else if (aName == "Bitmap") + { + aValue >>= xBtm; + } + } + + SolarMutexGuard g; + + if( xIStm.is() ) + { + 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 = implLoadGraphicObject( aURL ); + + if ( !xGraphic.is() ) + xGraphic = implLoadRepositoryImage( aURL ); + + if ( !xGraphic.is() ) + xGraphic = implLoadStandardImage( aURL ); + + if( xGraphic.is() ) + { + xRet.set( xGraphic, uno::UNO_QUERY ); + } + else + { + 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; + std::unique_ptr<SvStream> pIStm; + + uno::Reference< io::XInputStream > xIStm; + uno::Reference< awt::XBitmap >xBtm; + + uno::Sequence< ::beans::PropertyValue > aFilterData; + + for( sal_Int32 i = 0; ( i < rMediaProperties.getLength() ) && !pIStm && !xRet.is(); ++i ) + { + const OUString aName( rMediaProperties[ i ].Name ); + const uno::Any aValue( rMediaProperties[ i ].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; + } + } + + // Check for the goal width and height if they are defined + sal_uInt16 nExtWidth = 0; + sal_uInt16 nExtHeight = 0; + sal_uInt16 nExtMapMode = 0; + for( sal_Int32 i = 0; i < aFilterData.getLength(); ++i ) + { + const OUString aName( aFilterData[ i ].Name ); + const uno::Any aValue( aFilterData[ i ].Value ); + + if (aName == "ExternalWidth") + { + aValue >>= nExtWidth; + } + else if (aName == "ExternalHeight") + { + aValue >>= nExtHeight; + } + else if (aName == "ExternalMapMode") + { + aValue >>= nExtMapMode; + } + } + + SolarMutexGuard g; + + if( xIStm.is() ) + { + pIStm.reset(::utl::UcbStreamHelper::CreateStream( xIStm )); + } + else if( !aPath.isEmpty() ) + { + xRet = implLoadMemory( aPath ); + + if( !xRet.is() ) + xRet = implLoadGraphicObject( aPath ); + + if ( !xRet.is() ) + xRet = implLoadRepositoryImage( aPath ); + + if ( !xRet.is() ) + xRet = implLoadStandardImage( aPath ); + + if( !xRet.is() ) + pIStm.reset(::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; + WmfExternal *pExtHeader = nullptr; + if ( nExtMapMode > 0 ) + pExtHeader = &aExtHeader; + + ErrCode error = rFilter.ImportGraphic( aVCLGraphic, aPath, *pIStm, + GRFILTER_FORMAT_DONTKNOW, nullptr, GraphicFilterImportFlags::NONE, pExtHeader ); + if( (error == ERRCODE_NONE ) && + ( aVCLGraphic.GetType() != GraphicType::NONE ) ) + { + ::unographic::Graphic* pUnoGraphic = new ::unographic::Graphic; + + pUnoGraphic->init( aVCLGraphic ); + xRet = pUnoGraphic; + } + else{ + SAL_WARN("svtools", "Could not create graphic: " << error); + } + } + } + + return xRet; +} + +uno::Sequence< uno::Reference<graphic::XGraphic> > SAL_CALL GraphicProvider::queryGraphics(const uno::Sequence< uno::Sequence<beans::PropertyValue> >& rMediaPropertiesSeq) +{ + SolarMutexGuard aGuard; + + // Turn properties into streams. + std::vector< std::shared_ptr<SvStream> > aStreams; + for (const auto& rMediaProperties : rMediaPropertiesSeq) + { + SvStream* pStream = nullptr; + uno::Reference<io::XInputStream> xStream; + + for (sal_Int32 i = 0; rMediaProperties.getLength(); ++i) + { + if (rMediaProperties[i].Name == "InputStream") + { + rMediaProperties[i].Value >>= xStream; + if (xStream.is()) + pStream = utl::UcbStreamHelper::CreateStream(xStream); + break; + } + } + + aStreams.push_back(std::shared_ptr<SvStream>(pStream)); + + } + + // Import: streams to graphics. + std::vector< std::shared_ptr<Graphic> > aGraphics; + GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter(); + rFilter.ImportGraphics(aGraphics, 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) + { + auto 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, 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( sal_Int32 i = 0; i < rFilterData.getLength(); ++i ) + { + const OUString aName( rFilterData[ i ].Name ); + const uno::Any aValue( rFilterData[ i ].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().get()) + { + // 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 ); + aBmpEx = pScaleAction->GetBitmap(); + aPos = pScaleAction->GetPoint(); + aSize = pScaleAction->GetSize(); + } + else + { + MetaBmpExScaleAction* pScaleAction = dynamic_cast< MetaBmpExScaleAction* >( pAction ); + 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() ) ); + + MetaAction* pNewAction; + if ( pAction->GetType() == MetaActionType::BMPSCALE ) + pNewAction = new MetaBmpScaleAction ( aPos, aSize, aGraphic.GetBitmap() ); + else + pNewAction = new MetaBmpExScaleAction( aPos, aSize, aGraphic.GetBitmapEx() ); + + MetaAction* pDeleteAction = aMtf.ReplaceAction( pNewAction, i ); + if(pDeleteAction) + pDeleteAction->Delete(); + 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 ) +{ + SolarMutexGuard g; + + std::unique_ptr<SvStream> pOStm; + OUString aPath; + sal_Int32 i; + + for( i = 0; ( i < rMediaProperties.getLength() ) && !pOStm; ++i ) + { + const OUString aName( rMediaProperties[ i ].Name ); + const uno::Any aValue( rMediaProperties[ i ].Value ); + + if (aName == "URL") + { + OUString aURL; + + aValue >>= aURL; + pOStm.reset(::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.reset(::utl::UcbStreamHelper::CreateStream( xOStm )); + } + } + + if( !pOStm ) + return; + + uno::Sequence< beans::PropertyValue > aFilterDataSeq; + const char* pFilterShortName = nullptr; + + for( i = 0; i < rMediaProperties.getLength(); ++i ) + { + const OUString aName( rMediaProperties[ i ].Name ); + const uno::Any aValue( rMediaProperties[ i ].Value ); + + if (aName == "FilterData") + { + aValue >>= aFilterDataSeq; + } + else if (aName == "MimeType") + { + OUString aMimeType; + + aValue >>= aMimeType; + + if (aMimeType == MIMETYPE_BMP) + pFilterShortName = "bmp"; + else if (aMimeType == MIMETYPE_EPS) + pFilterShortName = "eps"; + else if (aMimeType == MIMETYPE_GIF) + pFilterShortName = "gif"; + else if (aMimeType == MIMETYPE_JPG) + pFilterShortName = "jpg"; + else if (aMimeType == MIMETYPE_MET) + pFilterShortName = "met"; + else if (aMimeType == MIMETYPE_PNG) + pFilterShortName = "png"; + else if (aMimeType == MIMETYPE_PCT) + pFilterShortName = "pct"; + else if (aMimeType == MIMETYPE_PBM) + pFilterShortName = "pbm"; + else if (aMimeType == MIMETYPE_PGM) + pFilterShortName = "pgm"; + else if (aMimeType == MIMETYPE_PPM) + pFilterShortName = "ppm"; + else if (aMimeType == MIMETYPE_RAS) + pFilterShortName = "ras"; + else if (aMimeType == MIMETYPE_SVM) + pFilterShortName = "svm"; + else if (aMimeType == MIMETYPE_TIF) + pFilterShortName = "tif"; + else if (aMimeType == MIMETYPE_EMF) + pFilterShortName = "emf"; + else if (aMimeType == MIMETYPE_WMF) + pFilterShortName = "wmf"; + else if (aMimeType == MIMETYPE_XPM) + pFilterShortName = "xpm"; + else if (aMimeType == MIMETYPE_SVG) + pFilterShortName = "svg"; + else if (aMimeType == MIMETYPE_VCLGRAPHIC) + pFilterShortName = MIMETYPE_VCLGRAPHIC; + } + } + + if( !pFilterShortName ) + return; + + ::GraphicFilter& rFilter = ::GraphicFilter::GetGraphicFilter(); + + { + const uno::Reference< XInterface > xIFace( rxGraphic, uno::UNO_QUERY ); + const ::Graphic* pGraphic = ::unographic::Graphic::getImplementation( xIFace ); + + 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( 0 == strcmp( pFilterShortName, MIMETYPE_VCLGRAPHIC ) ) + WriteGraphic( aMemStrm, aGraphic ); + else + { + rFilter.ExportGraphic( aGraphic, aPath, aMemStrm, + rFilter.GetExportFormatNumberForShortName( OUString::createFromAscii( pFilterShortName ) ), + ( aFilterDataSeq.getLength() ? &aFilterDataSeq : nullptr ) ); + } + aMemStrm.Seek( STREAM_SEEK_TO_END ); + pOStm->WriteBytes( aMemStrm.GetData(), aMemStrm.Tell() ); + } + } +} + +} + +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/UnoGraphicTransformer.cxx b/vcl/source/graphic/UnoGraphicTransformer.cxx new file mode 100644 index 000000000000..5f268e29f176 --- /dev/null +++ b/vcl/source/graphic/UnoGraphicTransformer.cxx @@ -0,0 +1,144 @@ +/* -*- 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 <vcl/svapp.hxx> +#include <vcl/image.hxx> +#include <vcl/metaact.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <svl/solar.hrc> +#include <vcl/salbtype.hxx> +#include <vcl/virdev.hxx> +#include <vcl/bitmapaccess.hxx> +#include <com/sun/star/text/GraphicCrop.hpp> + +#include "UnoGraphicTransformer.hxx" + +using namespace com::sun::star; + +namespace unographic { + + +GraphicTransformer::GraphicTransformer() +{ +} + + +GraphicTransformer::~GraphicTransformer() +{ +} + + +// XGraphicTransformer +uno::Reference< graphic::XGraphic > SAL_CALL GraphicTransformer::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(static_cast< sal_uInt8 >(nColorFrom), static_cast< sal_uInt8 >(nColorFrom >> 8), static_cast< sal_uInt8 >(nColorFrom >> 16)); + BitmapColor aBmpColorTo( static_cast< sal_uInt8 >(nColorTo), static_cast< sal_uInt8 >(nColorTo >> 8), static_cast< sal_uInt8 >(nColorTo >> 16)); + + Color aColorFrom(aBmpColorFrom.GetColor()); + Color aColorTo(aBmpColorTo.GetColor()); + + const sal_uInt8 cIndexFrom = aBmpColorFrom.GetBlueOrIndex(); + + if (aGraphic.GetType() == GraphicType::Bitmap || + aGraphic.GetType() == GraphicType::GdiMetafile) + { + BitmapEx aBitmapEx(aGraphic.GetBitmapEx()); + Bitmap aBitmap(aBitmapEx.GetBitmap()); + + if (aBitmapEx.IsAlpha()) + { + aBitmapEx.setAlphaFrom( cIndexFrom, nAlphaTo ); + aBitmapEx.Replace(aColorFrom, aColorTo, nTolerance); + aReturnGraphic = ::Graphic(aBitmapEx); + } + else if (aBitmapEx.IsTransparent()) + { + if (nAlphaTo == sal::static_int_cast< sal_Int8 >(0xff)) + { + Bitmap aMask(aBitmapEx.GetMask()); + Bitmap aMask2(aBitmap.CreateMask(aColorFrom, nTolerance)); + aMask.CombineSimple(aMask2, BmpCombine::Or); + aBitmap.Replace(aColorFrom, aColorTo, nTolerance); + aReturnGraphic = ::Graphic(BitmapEx(aBitmap, aMask)); + } + else + { + aBitmapEx.setAlphaFrom(cIndexFrom, 0xff - nAlphaTo); + aBitmapEx.Replace(aColorFrom, aColorTo, nTolerance); + aReturnGraphic = ::Graphic(aBitmapEx); + } + } + else + { + if ((nAlphaTo == 0) || (nAlphaTo == sal::static_int_cast< sal_Int8 >(0xff))) + { + Bitmap aMask(aBitmap.CreateMask(aColorFrom, nTolerance)); + aBitmap.Replace(aColorFrom, aColorTo, nTolerance); + aReturnGraphic = ::Graphic(BitmapEx(aBitmap, aMask)); + } + else + { + aBitmapEx.setAlphaFrom(cIndexFrom, nAlphaTo); + aBitmapEx.Replace(aColorFrom, aColorTo, nTolerance); + aReturnGraphic = ::Graphic(aBitmapEx); + } + } + } + + aReturnGraphic.setOriginURL(aGraphic.getOriginURL()); + return aReturnGraphic.GetXGraphic(); +} + +uno::Reference< graphic::XGraphic > SAL_CALL GraphicTransformer::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.GetAlpha() ); + Bitmap aBitmap( aBitmapEx.GetBitmap() ); + BmpFilterParam aFilter( static_cast<sal_uLong>(nColorOne), static_cast<sal_uLong>(nColorTwo) ); + aBitmap.Filter( BmpFilter::DuoTone, &aFilter ); + aReturnGraphic = ::Graphic( BitmapEx( aBitmap, aMask ) ); + aReturnGraphic.setOriginURL(aGraphic.getOriginURL()); + return aReturnGraphic.GetXGraphic(); +} + +uno::Reference< graphic::XGraphic > SAL_CALL GraphicTransformer::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/UnoGraphicTransformer.hxx b/vcl/source/graphic/UnoGraphicTransformer.hxx new file mode 100644 index 000000000000..7814c3707597 --- /dev/null +++ b/vcl/source/graphic/UnoGraphicTransformer.hxx @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_VCL_SOURCE_GRAPHIC_TRANSFORMER_HXX +#define INCLUDED_VCL_SOURCE_GRAPHIC_TRANSFORMER_HXX + +#include <cppuhelper/implbase1.hxx> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/graphic/XGraphicTransformer.hpp> + +namespace unographic { + + +typedef ::cppu::WeakAggImplHelper1< + css::graphic::XGraphicTransformer + > GraphicTransformer_UnoImplHelper1; +class GraphicTransformer : public GraphicTransformer_UnoImplHelper1 +{ + public: + + GraphicTransformer(); + virtual ~GraphicTransformer() override; + + // XGraphicTransformer + virtual css::uno::Reference< css::graphic::XGraphic > SAL_CALL colorChange( + const css::uno::Reference< css::graphic::XGraphic >& rGraphic, + sal_Int32 nColorFrom, sal_Int8 nTolerance, sal_Int32 nColorTo, sal_Int8 nAlphaTo ) override; + + virtual css::uno::Reference< css::graphic::XGraphic > SAL_CALL applyDuotone( + const css::uno::Reference< css::graphic::XGraphic >& rGraphic, + sal_Int32 nColorOne, sal_Int32 nColorTwo ) override; + + virtual css::uno::Reference< css::graphic::XGraphic > SAL_CALL applyBrightnessContrast( + const css::uno::Reference< css::graphic::XGraphic >& rxGraphic, + sal_Int32 nBrightness, sal_Int32 nContrast, sal_Bool mso ) override; + +}; + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/graphic/grfattr.cxx b/vcl/source/graphic/grfattr.cxx new file mode 100644 index 000000000000..923c2ea9e55a --- /dev/null +++ b/vcl/source/graphic/grfattr.cxx @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <tools/vcompat.hxx> +#include <vcl/GraphicObject.hxx> + + +GraphicAttr::GraphicAttr() : + mfGamma ( 1.0 ), + mnMirrFlags ( BmpMirrorFlags::NONE ), + mnLeftCrop ( 0 ), + mnTopCrop ( 0 ), + mnRightCrop ( 0 ), + mnBottomCrop ( 0 ), + mnRotate10 ( 0 ), + mnContPercent ( 0 ), + mnLumPercent ( 0 ), + mnRPercent ( 0 ), + mnGPercent ( 0 ), + mnBPercent ( 0 ), + mbInvert ( false ), + mcTransparency ( 0 ), + meDrawMode ( GraphicDrawMode::Standard ) +{ +} + +bool GraphicAttr::operator==( const GraphicAttr& rAttr ) const +{ + return( ( mfGamma == rAttr.mfGamma ) && + ( mnMirrFlags == rAttr.mnMirrFlags ) && + ( mnLeftCrop == rAttr.mnLeftCrop ) && + ( mnTopCrop == rAttr.mnTopCrop ) && + ( mnRightCrop == rAttr.mnRightCrop ) && + ( mnBottomCrop == rAttr.mnBottomCrop ) && + ( mnRotate10 == rAttr.mnRotate10 ) && + ( mnContPercent == rAttr.mnContPercent ) && + ( mnLumPercent == rAttr.mnLumPercent ) && + ( mnRPercent == rAttr.mnRPercent ) && + ( mnGPercent == rAttr.mnGPercent ) && + ( mnBPercent == rAttr.mnBPercent ) && + ( mbInvert == rAttr.mbInvert ) && + ( mcTransparency == rAttr.mcTransparency ) && + ( meDrawMode == rAttr.meDrawMode ) ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/graphic/grfcache.cxx b/vcl/source/graphic/grfcache.cxx new file mode 100644 index 000000000000..53fa0f268abe --- /dev/null +++ b/vcl/source/graphic/grfcache.cxx @@ -0,0 +1,1221 @@ +/* -*- 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 <cstdlib> + +#include <salhelper/timer.hxx> +#include <vcl/GraphicObject.hxx> +#include <tools/debug.hxx> +#include <vcl/metaact.hxx> +#include <vcl/outdev.hxx> +#include <tools/poly.hxx> +#include <rtl/strbuf.hxx> +#include "grfcache.hxx" +#include <rtl/crc.h> +#include <memory> + +#define MAX_BMP_EXTENT 4096 + +using namespace com::sun::star; + +static const char aHexData[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + +class GraphicID +{ +private: + + sal_uInt32 mnID1; + sal_uInt32 mnID2; + sal_uInt32 mnID3; + BitmapChecksum mnID4; + +public: + explicit GraphicID( const GraphicObject& rObj ); + + bool operator==( const GraphicID& rID ) const + { + return( rID.mnID1 == mnID1 && rID.mnID2 == mnID2 && + rID.mnID3 == mnID3 && rID.mnID4 == mnID4 ); + } + + OString GetIDString() const; + bool IsEmpty() const { return( 0 == mnID4 ); } +}; + +GraphicID::GraphicID( const GraphicObject& rObj ) +{ + const Graphic& rGraphic = rObj.GetGraphic(); + + mnID1 = static_cast<sal_uLong>(rGraphic.GetType()) << 28; + + switch( rGraphic.GetType() ) + { + case GraphicType::Bitmap: + { + if(rGraphic.getVectorGraphicData().get()) + { + const VectorGraphicDataPtr& rVectorGraphicDataPtr = rGraphic.getVectorGraphicData(); + const basegfx::B2DRange& rRange = rVectorGraphicDataPtr->getRange(); + + mnID1 |= rVectorGraphicDataPtr->getVectorGraphicDataArrayLength(); + mnID2 = basegfx::fround(rRange.getWidth()); + mnID3 = basegfx::fround(rRange.getHeight()); + mnID4 = vcl_get_checksum(0, rVectorGraphicDataPtr->getVectorGraphicDataArray().getConstArray(), rVectorGraphicDataPtr->getVectorGraphicDataArrayLength()); + } + else if( rGraphic.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() ); + + mnID1 |= ( ( ( static_cast<sal_uLong>(aBmpEx.GetTransparentType()) << 8 ) | ( aBmpEx.IsAlpha() ? 1 : 0 ) ) & 0x0fffffff ); + mnID2 = aBmpEx.GetSizePixel().Width(); + mnID3 = aBmpEx.GetSizePixel().Height(); + mnID4 = rGraphic.GetChecksum(); + } + } + break; + + case GraphicType::GdiMetafile: + { + const GDIMetaFile& rMtf = rGraphic.GetGDIMetaFile(); + + mnID1 |= ( rMtf.GetActionSize() & 0x0fffffff ); + mnID2 = rMtf.GetPrefSize().Width(); + mnID3 = rMtf.GetPrefSize().Height(); + mnID4 = rGraphic.GetChecksum(); + } + break; + + default: + mnID2 = mnID3 = mnID4 = 0; + break; + } +} + +OString GraphicID::GetIDString() const +{ + OStringBuffer aHexStr; + sal_Int32 nShift, nIndex = 0; + aHexStr.setLength(24 + (2 * BITMAP_CHECKSUM_SIZE)); + + 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(); +} + +class GraphicCacheEntry +{ +private: + + std::vector< GraphicObject* > maGraphicObjectList; + + GraphicID maID; + GfxLink maGfxLink; + std::unique_ptr<BitmapEx> mpBmpEx; + std::unique_ptr<GDIMetaFile> mpMtf; + std::unique_ptr<Animation> mpAnimation; + bool mbSwappedAll; + + // VectorGraphicData support + VectorGraphicDataPtr maVectorGraphicData; + uno::Sequence<sal_Int8> maPdfData; + + bool ImplInit( const GraphicObject& rObj ); + void ImplFillSubstitute( Graphic& rSubstitute ); + +public: + + explicit GraphicCacheEntry( const GraphicObject& rObj ); + ~GraphicCacheEntry(); + + const GraphicID& GetID() const { return maID; } + + void AddGraphicObjectReference( const GraphicObject& rObj, Graphic& rSubstitute ); + bool ReleaseGraphicObjectReference( const GraphicObject& rObj ); + size_t GetGraphicObjectReferenceCount() { return maGraphicObjectList.size(); } + bool HasGraphicObjectReference( const GraphicObject& rObj ); + + void TryToSwapIn(); + void GraphicObjectWasSwappedOut(); + void GraphicObjectWasSwappedIn( const GraphicObject& rObj ); +}; + +GraphicCacheEntry::GraphicCacheEntry( const GraphicObject& rObj ) : + maID ( rObj ), + mbSwappedAll ( true ) +{ + mbSwappedAll = !ImplInit( rObj ); + maGraphicObjectList.push_back( const_cast<GraphicObject*>(&rObj) ); +} + +GraphicCacheEntry::~GraphicCacheEntry() +{ + DBG_ASSERT( + maGraphicObjectList.empty(), + "GraphicCacheEntry::~GraphicCacheEntry(): Not all GraphicObjects are removed from this entry" + ); +} + +bool GraphicCacheEntry::ImplInit( const GraphicObject& rObj ) +{ + bool bRet = false; + + if( !rObj.IsSwappedOut() ) + { + const Graphic& rGraphic = rObj.GetGraphic(); + + mpBmpEx.reset(); + mpMtf.reset(); + mpAnimation.reset(); + + switch( rGraphic.GetType() ) + { + case GraphicType::Bitmap: + { + if(rGraphic.getVectorGraphicData().get()) + { + maVectorGraphicData = rGraphic.getVectorGraphicData(); + } + else if( rGraphic.IsAnimated() ) + { + mpAnimation.reset(new Animation( rGraphic.GetAnimation() )); + } + else + { + mpBmpEx.reset(new BitmapEx( rGraphic.GetBitmapEx() )); + if (rGraphic.getPdfData().hasElements()) + maPdfData = rGraphic.getPdfData(); + } + } + break; + + case GraphicType::GdiMetafile: + { + mpMtf.reset(new GDIMetaFile( rGraphic.GetGDIMetaFile() )); + } + break; + + default: + DBG_ASSERT( GetID().IsEmpty(), "GraphicCacheEntry::ImplInit: Could not initialize graphic! (=>KA)" ); + break; + } + + if( rGraphic.IsLink() ) + maGfxLink = rGraphic.GetLink(); + else + maGfxLink = GfxLink(); + + bRet = true; + } + + return bRet; +} + +void GraphicCacheEntry::ImplFillSubstitute( Graphic& rSubstitute ) +{ + // create substitute for graphic; + const Size aPrefSize( rSubstitute.GetPrefSize() ); + const MapMode aPrefMapMode( rSubstitute.GetPrefMapMode() ); + const Link<Animation*,void> aAnimationNotifyHdl( rSubstitute.GetAnimationNotifyHdl() ); + const GraphicType eOldType = rSubstitute.GetType(); + const bool bDefaultType = ( rSubstitute.GetType() == GraphicType::Default ); + + if( rSubstitute.IsLink() && ( GfxLinkType::NONE == maGfxLink.GetType() ) ) + maGfxLink = rSubstitute.GetLink(); + + if(maVectorGraphicData.get()) + { + rSubstitute = maVectorGraphicData; + } + else if( mpBmpEx ) + { + rSubstitute = *mpBmpEx; + if (maPdfData.hasElements()) + rSubstitute.setPdfData(maPdfData); + } + else if( mpAnimation ) + { + rSubstitute = *mpAnimation; + } + else if( mpMtf ) + { + rSubstitute = *mpMtf; + } + else + { + rSubstitute.Clear(); + } + + if( eOldType != GraphicType::NONE ) + { + rSubstitute.SetPrefSize( aPrefSize ); + rSubstitute.SetPrefMapMode( aPrefMapMode ); + rSubstitute.SetAnimationNotifyHdl( aAnimationNotifyHdl ); + } + + if( GfxLinkType::NONE != maGfxLink.GetType() ) + { + rSubstitute.SetLink( maGfxLink ); + } + + if( bDefaultType ) + { + rSubstitute.SetDefaultType(); + } +} + +void GraphicCacheEntry::AddGraphicObjectReference( const GraphicObject& rObj, Graphic& rSubstitute ) +{ + if( mbSwappedAll ) + mbSwappedAll = !ImplInit( rObj ); + + OUString rOriginURL = rObj.GetGraphic().getOriginURL(); + ImplFillSubstitute( rSubstitute ); + rSubstitute.setOriginURL(rOriginURL); + maGraphicObjectList.push_back( const_cast<GraphicObject*>(&rObj) ); +} + +bool GraphicCacheEntry::ReleaseGraphicObjectReference( const GraphicObject& rObj ) +{ + for( + auto it = maGraphicObjectList.begin(); + it != maGraphicObjectList.end(); + ++it + ) { + if( &rObj == *it ) + { + maGraphicObjectList.erase( it ); + return true; + } + } + + return false; +} + +bool GraphicCacheEntry::HasGraphicObjectReference( const GraphicObject& rObj ) +{ + bool bRet = false; + + for( size_t i = 0, n = maGraphicObjectList.size(); ( i < n ) && !bRet; ++i ) + if( &rObj == maGraphicObjectList[ i ] ) + bRet = true; + + return bRet; +} + +void GraphicCacheEntry::TryToSwapIn() +{ + if( mbSwappedAll && !maGraphicObjectList.empty() ) + maGraphicObjectList.front()->FireSwapInRequest(); +} + +void GraphicCacheEntry::GraphicObjectWasSwappedOut() +{ + mbSwappedAll = true; + + for( size_t i = 0, n = maGraphicObjectList.size(); ( i < n ) && mbSwappedAll; ++i ) + if( !maGraphicObjectList[ i ]->IsSwappedOut() ) + mbSwappedAll = false; + + if( !mbSwappedAll ) + return; + + mpBmpEx.reset(); + mpMtf.reset(); + mpAnimation.reset(); + + // #119176# also reset VectorGraphicData + maVectorGraphicData.reset(); + maPdfData = uno::Sequence<sal_Int8>(); +} + +void GraphicCacheEntry::GraphicObjectWasSwappedIn( const GraphicObject& rObj ) +{ + if( mbSwappedAll ) + mbSwappedAll = !ImplInit( rObj ); +} + +class GraphicDisplayCacheEntry +{ +private: + + ::salhelper::TTimeValue maReleaseTime; + const GraphicCacheEntry* mpRefCacheEntry; + std::unique_ptr<GDIMetaFile> mpMtf; + std::unique_ptr<BitmapEx> mpBmpEx; + GraphicAttr maAttr; + Size maOutSizePix; + sal_uLong mnCacheSize; + DrawModeFlags mnOutDevDrawMode; + sal_uInt16 mnOutDevBitCount; + + static bool IsCacheableAsBitmap( const GDIMetaFile& rMtf, OutputDevice const * pOut, const Size& rSz ); + + // Copy assignment is forbidden and not implemented. + GraphicDisplayCacheEntry (const GraphicDisplayCacheEntry &) = delete; + GraphicDisplayCacheEntry & operator= (const GraphicDisplayCacheEntry &) = delete; + +public: + + static sal_uLong GetNeededSize( OutputDevice const * pOut, const Point& rPt, const Size& rSz, + const GraphicObject& rObj, const GraphicAttr& rAttr ); + +public: + + GraphicDisplayCacheEntry( const GraphicCacheEntry* pRefCacheEntry, + OutputDevice const * pOut, const Point& rPt, const Size& rSz, + const GraphicObject& rObj, const GraphicAttr& rAttr, + const BitmapEx& rBmpEx ) : + mpRefCacheEntry( pRefCacheEntry ), + mpBmpEx( new BitmapEx( rBmpEx ) ), + maAttr( rAttr ), maOutSizePix( pOut->LogicToPixel( rSz ) ), + mnCacheSize( GetNeededSize( pOut, rPt, rSz, rObj, rAttr ) ), + mnOutDevDrawMode( pOut->GetDrawMode() ), + mnOutDevBitCount( pOut->GetBitCount() ) + { + } + + GraphicDisplayCacheEntry( const GraphicCacheEntry* pRefCacheEntry, + OutputDevice const * pOut, const Point& rPt, const Size& rSz, + const GraphicObject& rObj, const GraphicAttr& rAttr, + const GDIMetaFile& rMtf ) : + mpRefCacheEntry( pRefCacheEntry ), + mpMtf( new GDIMetaFile( rMtf ) ), + maAttr( rAttr ), maOutSizePix( pOut->LogicToPixel( rSz ) ), + mnCacheSize( GetNeededSize( pOut, rPt, rSz, rObj, rAttr ) ), + mnOutDevDrawMode( pOut->GetDrawMode() ), + mnOutDevBitCount( pOut->GetBitCount() ) + { + } + + + sal_uLong GetCacheSize() const { return mnCacheSize; } + const GraphicCacheEntry* GetReferencedCacheEntry() const { return mpRefCacheEntry; } + + void SetReleaseTime( const ::salhelper::TTimeValue& rReleaseTime ) { maReleaseTime = rReleaseTime; } + const ::salhelper::TTimeValue& GetReleaseTime() const { return maReleaseTime; } + + bool Matches( OutputDevice const * pOut, const Point& /*rPtPixel*/, const Size& rSzPixel, + const GraphicCacheEntry* pCacheEntry, const GraphicAttr& rAttr ) const + { + // #i46805# Additional match + // criteria: outdev draw mode and + // bit count. One cannot reuse + // this cache object, if it's + // e.g. generated for + // DrawModeFlags::GrayBitmap. + return( ( pCacheEntry == mpRefCacheEntry ) && + ( maAttr == rAttr ) && + ( ( maOutSizePix == rSzPixel ) || ( !maOutSizePix.Width() && !maOutSizePix.Height() ) ) && + ( pOut->GetBitCount() == mnOutDevBitCount ) && + ( pOut->GetDrawMode() == mnOutDevDrawMode ) ); + } + + void Draw( OutputDevice* pOut, const Point& rPt, const Size& rSz ) const; +}; + +// This whole function is based on checkMetadataBitmap() from grfmgr2.cxx, see that one for details. +// If you do changes here, change the original function too. +static void checkMetadataBitmap( const BitmapEx& rBmpEx, + Point /*rSrcPoint*/, + Size rSrcSize, + const Point& rDestPoint, + const Size& rDestSize, + const Size& rRefSize, + bool& o_rbNonBitmapActionEncountered ) +{ + if( rSrcSize == Size()) + rSrcSize = rBmpEx.GetSizePixel(); + + if( rDestPoint != Point( 0, 0 )) + { + o_rbNonBitmapActionEncountered = true; + return; + } + if( rDestSize != rRefSize ) + { if( rBmpEx.GetSizePixel().Width() > 100 && rBmpEx.GetSizePixel().Height() > 100 + && std::abs( rDestSize.Width() - rRefSize.Width()) < 5 + && std::abs( rDestSize.Height() - rRefSize.Height()) < 5 ) + ; // ok, assume it's close enough + else + { // fall back to mtf rendering + o_rbNonBitmapActionEncountered = true; + return; + } + } +} + +// This function is based on GraphicManager::ImplCreateOutput(), in fact it mostly copies +// it, the difference is that this one does not create anything, it only checks if +// ImplCreateOutput() would use the optimization of using the single bitmap. +// If you do changes here, change the original function too. +bool GraphicDisplayCacheEntry::IsCacheableAsBitmap( const GDIMetaFile& rMtf, + OutputDevice const * pOut, const Size& rSz ) +{ + const Size aNewSize( rMtf.GetPrefSize() ); + GDIMetaFile rOutMtf = rMtf; + + // Count bitmap actions, and flag actions that paint, but + // are no bitmaps. + sal_Int32 nNumBitmaps(0); + bool bNonBitmapActionEncountered(false); + if( aNewSize.Width() && aNewSize.Height() && rSz.Width() && rSz.Height() ) + { + const MapMode& rPrefMapMode( rMtf.GetPrefMapMode() ); + const Size rSizePix( pOut->LogicToPixel( aNewSize, rPrefMapMode ) ); + + sal_uInt32 nCurPos; + MetaAction* pAct; + for( nCurPos = 0, pAct = rOutMtf.FirstAction(); pAct; + pAct = rOutMtf.NextAction(), nCurPos++ ) + { + switch( pAct->GetType() ) + { + case MetaActionType::FONT: + // FALLTHROUGH intended + case MetaActionType::NONE: + // FALLTHROUGH intended + + // OutDev state changes (which don't affect bitmap + // output) + case MetaActionType::LINECOLOR: + // FALLTHROUGH intended + case MetaActionType::FILLCOLOR: + // FALLTHROUGH intended + case MetaActionType::TEXTCOLOR: + // FALLTHROUGH intended + case MetaActionType::TEXTFILLCOLOR: + // FALLTHROUGH intended + case MetaActionType::TEXTALIGN: + // FALLTHROUGH intended + case MetaActionType::TEXTLINECOLOR: + // FALLTHROUGH intended + case MetaActionType::TEXTLINE: + // FALLTHROUGH intended + case MetaActionType::PUSH: + // FALLTHROUGH intended + case MetaActionType::POP: + // FALLTHROUGH intended + case MetaActionType::LAYOUTMODE: + // FALLTHROUGH intended + case MetaActionType::TEXTLANGUAGE: + // FALLTHROUGH intended + case MetaActionType::COMMENT: + break; + + // bitmap output methods + case MetaActionType::BMP: + if( !nNumBitmaps && !bNonBitmapActionEncountered ) + { + MetaBmpAction* pAction = static_cast<MetaBmpAction*>(pAct); + + checkMetadataBitmap( + BitmapEx( pAction->GetBitmap()), + Point(), Size(), + pOut->LogicToPixel( pAction->GetPoint(), + rPrefMapMode ), + pAction->GetBitmap().GetSizePixel(), + rSizePix, + bNonBitmapActionEncountered ); + } + ++nNumBitmaps; + break; + + case MetaActionType::BMPSCALE: + if( !nNumBitmaps && !bNonBitmapActionEncountered ) + { + MetaBmpScaleAction* pAction = static_cast<MetaBmpScaleAction*>(pAct); + + checkMetadataBitmap( + BitmapEx( pAction->GetBitmap()), + Point(), Size(), + pOut->LogicToPixel( pAction->GetPoint(), + rPrefMapMode ), + pOut->LogicToPixel( pAction->GetSize(), + rPrefMapMode ), + rSizePix, + bNonBitmapActionEncountered ); + } + ++nNumBitmaps; + break; + + case MetaActionType::BMPSCALEPART: + if( !nNumBitmaps && !bNonBitmapActionEncountered ) + { + MetaBmpScalePartAction* pAction = static_cast<MetaBmpScalePartAction*>(pAct); + + checkMetadataBitmap( BitmapEx( pAction->GetBitmap() ), + pAction->GetSrcPoint(), + pAction->GetSrcSize(), + pOut->LogicToPixel( pAction->GetDestPoint(), + rPrefMapMode ), + pOut->LogicToPixel( pAction->GetDestSize(), + rPrefMapMode ), + rSizePix, + bNonBitmapActionEncountered ); + } + ++nNumBitmaps; + break; + + case MetaActionType::BMPEX: + if( !nNumBitmaps && !bNonBitmapActionEncountered ) + { + MetaBmpExAction* pAction = static_cast<MetaBmpExAction*>(pAct); + + checkMetadataBitmap( + pAction->GetBitmapEx(), + Point(), Size(), + pOut->LogicToPixel( pAction->GetPoint(), + rPrefMapMode ), + pAction->GetBitmapEx().GetSizePixel(), + rSizePix, + bNonBitmapActionEncountered ); + } + ++nNumBitmaps; + break; + + case MetaActionType::BMPEXSCALE: + if( !nNumBitmaps && !bNonBitmapActionEncountered ) + { + MetaBmpExScaleAction* pAction = static_cast<MetaBmpExScaleAction*>(pAct); + + checkMetadataBitmap( + pAction->GetBitmapEx(), + Point(), Size(), + pOut->LogicToPixel( pAction->GetPoint(), + rPrefMapMode ), + pOut->LogicToPixel( pAction->GetSize(), + rPrefMapMode ), + rSizePix, + bNonBitmapActionEncountered ); + } + ++nNumBitmaps; + break; + + case MetaActionType::BMPEXSCALEPART: + if( !nNumBitmaps && !bNonBitmapActionEncountered ) + { + MetaBmpExScalePartAction* pAction = static_cast<MetaBmpExScalePartAction*>(pAct); + + checkMetadataBitmap( pAction->GetBitmapEx(), + pAction->GetSrcPoint(), + pAction->GetSrcSize(), + pOut->LogicToPixel( pAction->GetDestPoint(), + rPrefMapMode ), + pOut->LogicToPixel( pAction->GetDestSize(), + rPrefMapMode ), + rSizePix, + bNonBitmapActionEncountered ); + } + ++nNumBitmaps; + break; + + // these actions actually output something (that's + // different from a bitmap) + case MetaActionType::RASTEROP: + if( static_cast<MetaRasterOpAction*>(pAct)->GetRasterOp() == RasterOp::OverPaint ) + break; + SAL_FALLTHROUGH; + case MetaActionType::PIXEL: + SAL_FALLTHROUGH; + case MetaActionType::POINT: + SAL_FALLTHROUGH; + case MetaActionType::LINE: + SAL_FALLTHROUGH; + case MetaActionType::RECT: + SAL_FALLTHROUGH; + case MetaActionType::ROUNDRECT: + SAL_FALLTHROUGH; + case MetaActionType::ELLIPSE: + SAL_FALLTHROUGH; + case MetaActionType::ARC: + SAL_FALLTHROUGH; + case MetaActionType::PIE: + SAL_FALLTHROUGH; + case MetaActionType::CHORD: + SAL_FALLTHROUGH; + case MetaActionType::POLYLINE: + SAL_FALLTHROUGH; + case MetaActionType::POLYGON: + SAL_FALLTHROUGH; + case MetaActionType::POLYPOLYGON: + SAL_FALLTHROUGH; + + case MetaActionType::TEXT: + SAL_FALLTHROUGH; + case MetaActionType::TEXTARRAY: + SAL_FALLTHROUGH; + case MetaActionType::STRETCHTEXT: + SAL_FALLTHROUGH; + case MetaActionType::TEXTRECT: + SAL_FALLTHROUGH; + + case MetaActionType::MASK: + SAL_FALLTHROUGH; + case MetaActionType::MASKSCALE: + SAL_FALLTHROUGH; + case MetaActionType::MASKSCALEPART: + SAL_FALLTHROUGH; + + case MetaActionType::GRADIENT: + SAL_FALLTHROUGH; + case MetaActionType::HATCH: + SAL_FALLTHROUGH; + case MetaActionType::WALLPAPER: + SAL_FALLTHROUGH; + + case MetaActionType::Transparent: + SAL_FALLTHROUGH; + case MetaActionType::EPS: + SAL_FALLTHROUGH; + case MetaActionType::FLOATTRANSPARENT: + SAL_FALLTHROUGH; + case MetaActionType::GRADIENTEX: + SAL_FALLTHROUGH; + + // OutDev state changes that _do_ affect bitmap + // output + case MetaActionType::CLIPREGION: + SAL_FALLTHROUGH; + case MetaActionType::ISECTRECTCLIPREGION: + SAL_FALLTHROUGH; + case MetaActionType::ISECTREGIONCLIPREGION: + SAL_FALLTHROUGH; + case MetaActionType::MOVECLIPREGION: + SAL_FALLTHROUGH; + + case MetaActionType::MAPMODE: + SAL_FALLTHROUGH; + case MetaActionType::REFPOINT: + SAL_FALLTHROUGH; + default: + bNonBitmapActionEncountered = true; + break; + } + } + } + return nNumBitmaps == 1 && !bNonBitmapActionEncountered; +} + +sal_uLong GraphicDisplayCacheEntry::GetNeededSize( OutputDevice const * pOut, const Point& /*rPt*/, const Size& rSz, + const GraphicObject& rObj, const GraphicAttr& rAttr ) +{ + const Graphic& rGraphic = rObj.GetGraphic(); + const GraphicType eType = rGraphic.GetType(); + + bool canCacheAsBitmap = false; + if( GraphicType::Bitmap == eType ) + canCacheAsBitmap = true; + else if( GraphicType::GdiMetafile == eType ) + canCacheAsBitmap = IsCacheableAsBitmap( rGraphic.GetGDIMetaFile(), pOut, rSz ); + else + return 0; + if( canCacheAsBitmap ) + { + const Size aOutSizePix( pOut->LogicToPixel( rSz ) ); + const long nBitCount = pOut->GetBitCount(); + + if( ( aOutSizePix.Width() > MAX_BMP_EXTENT ) || + ( aOutSizePix.Height() > MAX_BMP_EXTENT ) ) + { + return ULONG_MAX; + } + else if( nBitCount ) + { + sal_uLong nNeededSize = aOutSizePix.Width() * aOutSizePix.Height() * nBitCount / 8; + if( rObj.IsTransparent() || ( rAttr.GetRotation() % 3600 ) ) + nNeededSize += nNeededSize / nBitCount; + return nNeededSize; + } + else + { + OSL_FAIL( "GraphicDisplayCacheEntry::GetNeededSize(): pOut->GetBitCount() == 0" ); + return 256000; + } + } + else + return rGraphic.GetSizeBytes(); +} + +void GraphicDisplayCacheEntry::Draw( OutputDevice* pOut, const Point& rPt, const Size& rSz ) const +{ + if( mpMtf ) + GraphicManager::ImplDraw( pOut, rPt, rSz, *mpMtf, maAttr ); + else if( mpBmpEx ) + { + if( maAttr.IsRotated() ) + { + tools::Polygon aPoly( tools::Rectangle( rPt, rSz ) ); + + aPoly.Rotate( rPt, maAttr.GetRotation() % 3600 ); + const tools::Rectangle aRotBoundRect( aPoly.GetBoundRect() ); + pOut->DrawBitmapEx( aRotBoundRect.TopLeft(), aRotBoundRect.GetSize(), *mpBmpEx ); + } + else + pOut->DrawBitmapEx( rPt, rSz, *mpBmpEx ); + } +} + +GraphicCache::GraphicCache( sal_uLong nDisplayCacheSize, sal_uLong nMaxObjDisplayCacheSize ) : + maReleaseTimer ( "svtools::GraphicCache maReleaseTimer" ), + mnReleaseTimeoutSeconds ( 0 ), + mnMaxDisplaySize ( nDisplayCacheSize ), + mnMaxObjDisplaySize ( nMaxObjDisplayCacheSize ), + mnUsedDisplaySize ( 0 ) +{ + maReleaseTimer.SetInvokeHandler( LINK( this, GraphicCache, ReleaseTimeoutHdl ) ); + maReleaseTimer.SetTimeout( 10000 ); + maReleaseTimer.SetDebugName( "svtools::GraphicCache maReleaseTimer" ); + maReleaseTimer.Start(); +} + +GraphicCache::~GraphicCache() +{ + DBG_ASSERT( !maGraphicCache.size(), "GraphicCache::~GraphicCache(): there are some GraphicObjects in cache" ); + DBG_ASSERT( maDisplayCache.empty(), "GraphicCache::~GraphicCache(): there are some GraphicObjects in display cache" ); +} + +void GraphicCache::AddGraphicObject( + const GraphicObject& rObj, + Graphic& rSubstitute, + const OString* pID, + const GraphicObject* pCopyObj +) +{ + bool bInserted = false; + + if( !rObj.IsSwappedOut() + && ( pID + || ( pCopyObj + && ( pCopyObj->GetType() != GraphicType::NONE ) + ) + || ( rObj.GetType() != GraphicType::NONE ) + ) + ) + { + if( pCopyObj + && !maGraphicCache.empty() + ) + { + for (auto const& elem : maGraphicCache) + { + if( elem->HasGraphicObjectReference( *pCopyObj ) ) + { + elem->AddGraphicObjectReference( rObj, rSubstitute ); + bInserted = true; + break; + } + } + } + + if( !bInserted ) + { + std::unique_ptr< GraphicID > apID; + + if( !pID ) + { + apID.reset( new GraphicID( rObj ) ); + } + + for (auto const& elem : maGraphicCache) + { + const GraphicID& rEntryID = elem->GetID(); + + if( pID ) + { + if( rEntryID.GetIDString() == *pID ) + { + elem->TryToSwapIn(); + + // since pEntry->TryToSwapIn can modify our current list, we have to + // iterate from beginning to add a reference to the appropriate + // CacheEntry object; after this, quickly jump out of the outer iteration + for (auto const& subelem : maGraphicCache) + { + const GraphicID& rID = subelem->GetID(); + + if( rID.GetIDString() == *pID ) + { + subelem->AddGraphicObjectReference( rObj, rSubstitute ); + bInserted = true; + break; + } + } + + if( !bInserted ) + { + maGraphicCache.push_back( new GraphicCacheEntry( rObj ) ); + bInserted = true; + } + } + } + else + { + if( rEntryID == *apID ) + { + elem->AddGraphicObjectReference( rObj, rSubstitute ); + bInserted = true; + } + } + + if(bInserted) + break; + } + } + } + + if( !bInserted ) + maGraphicCache.push_back( new GraphicCacheEntry( rObj ) ); +} + +void GraphicCache::ReleaseGraphicObject( const GraphicObject& rObj ) +{ + // Release cached object + bool bRemoved = false; + GraphicCacheEntryVector::iterator it = maGraphicCache.begin(); + while (!bRemoved && it != maGraphicCache.end()) + { + bRemoved = (*it)->ReleaseGraphicObjectReference( rObj ); + + if( bRemoved && (0 == (*it)->GetGraphicObjectReferenceCount()) ) + { + // if graphic cache entry has no more references, + // the corresponding display cache object can be removed + GraphicDisplayCacheEntryVector::iterator it2 = maDisplayCache.begin(); + while( it2 != maDisplayCache.end() ) + { + GraphicDisplayCacheEntry* pDisplayEntry = *it2; + if( pDisplayEntry->GetReferencedCacheEntry() == *it ) + { + mnUsedDisplaySize -= pDisplayEntry->GetCacheSize(); + it2 = maDisplayCache.erase( it2 ); + delete pDisplayEntry; + } + else + ++it2; + } + + // delete graphic cache entry + delete *it; + it = maGraphicCache.erase( it ); + } + else + ++it; + } + + DBG_ASSERT( bRemoved, "GraphicCache::ReleaseGraphicObject(...): GraphicObject not found in cache" ); +} + +void GraphicCache::GraphicObjectWasSwappedOut( const GraphicObject& rObj ) +{ + // notify cache that rObj is swapped out (and can thus be pruned + // from the cache) + GraphicCacheEntry* pEntry = ImplGetCacheEntry( rObj ); + + if( pEntry ) + pEntry->GraphicObjectWasSwappedOut(); +} + +void GraphicCache::GraphicObjectWasSwappedIn( const GraphicObject& rObj ) +{ + GraphicCacheEntry* pEntry = ImplGetCacheEntry( rObj ); + + if( pEntry ) + { + if( pEntry->GetID().IsEmpty() ) + { + ReleaseGraphicObject( rObj ); + AddGraphicObject( rObj, const_cast<Graphic&>(rObj.GetGraphic()), nullptr, nullptr ); + } + else + pEntry->GraphicObjectWasSwappedIn( rObj ); + } +} + +void GraphicCache::SetMaxDisplayCacheSize( sal_uLong nNewCacheSize ) +{ + mnMaxDisplaySize = nNewCacheSize; + + if( GetMaxDisplayCacheSize() < GetUsedDisplayCacheSize() ) + ImplFreeDisplayCacheSpace( GetUsedDisplayCacheSize() - GetMaxDisplayCacheSize() ); +} + +void GraphicCache::SetCacheTimeout( sal_uLong nTimeoutSeconds ) +{ + if( mnReleaseTimeoutSeconds == nTimeoutSeconds ) + return; + + ::salhelper::TTimeValue aReleaseTime; + + if( ( mnReleaseTimeoutSeconds = nTimeoutSeconds ) != 0 ) + { + osl_getSystemTime( &aReleaseTime ); + aReleaseTime.addTime( ::salhelper::TTimeValue( nTimeoutSeconds, 0 ) ); + } + + for (auto const& elem : maDisplayCache) + { + elem->SetReleaseTime( aReleaseTime ); + } +} + +bool GraphicCache::IsDisplayCacheable( OutputDevice const * pOut, const Point& rPt, const Size& rSz, + const GraphicObject& rObj, const GraphicAttr& rAttr ) const +{ + return( GraphicDisplayCacheEntry::GetNeededSize( pOut, rPt, rSz, rObj, rAttr ) <= + GetMaxObjDisplayCacheSize() ); +} + +bool GraphicCache::IsInDisplayCache( OutputDevice const * pOut, const Point& rPt, const Size& rSz, + const GraphicObject& rObj, const GraphicAttr& rAttr ) const +{ + const Point aPtPixel( pOut->LogicToPixel( rPt ) ); + const Size aSzPixel( pOut->LogicToPixel( rSz ) ); + const GraphicCacheEntry* pCacheEntry = const_cast<GraphicCache*>(this)->ImplGetCacheEntry( rObj ); + bool bFound = false; + + if( pCacheEntry ) + { + for (auto const& elem : maDisplayCache) + { + if( elem->Matches( pOut, aPtPixel, aSzPixel, pCacheEntry, rAttr ) ) + { + bFound = true; + break; + } + } + } + + return bFound; +} + +OString GraphicCache::GetUniqueID( const GraphicObject& rObj ) const +{ + OString aRet; + GraphicCacheEntry* pEntry = const_cast<GraphicCache*>(this)->ImplGetCacheEntry( rObj ); + + // ensure that the entry is correctly initialized (it has to be read at least once) + if( pEntry && pEntry->GetID().IsEmpty() ) + { + pEntry->TryToSwapIn(); + // do another call to ImplGetCacheEntry in case of modified entry list + pEntry = const_cast<GraphicCache*>(this)->ImplGetCacheEntry( rObj ); + } + + if( pEntry ) + aRet = pEntry->GetID().GetIDString(); + + return aRet; +} + +bool GraphicCache::CreateDisplayCacheObj( OutputDevice const * pOut, const Point& rPt, const Size& rSz, + const GraphicObject& rObj, const GraphicAttr& rAttr, + const BitmapEx& rBmpEx ) +{ + const sal_uLong nNeededSize = GraphicDisplayCacheEntry::GetNeededSize( pOut, rPt, rSz, rObj, rAttr ); + bool bRet = false; + + if( nNeededSize <= GetMaxObjDisplayCacheSize() ) + { + if( nNeededSize > GetFreeDisplayCacheSize() ) + ImplFreeDisplayCacheSpace( nNeededSize - GetFreeDisplayCacheSize() ); + + GraphicDisplayCacheEntry* pNewEntry = new GraphicDisplayCacheEntry( ImplGetCacheEntry( rObj ), + pOut, rPt, rSz, rObj, rAttr, rBmpEx ); + + if( GetCacheTimeout() ) + { + ::salhelper::TTimeValue aReleaseTime; + + osl_getSystemTime( &aReleaseTime ); + aReleaseTime.addTime( ::salhelper::TTimeValue( GetCacheTimeout(), 0 ) ); + pNewEntry->SetReleaseTime( aReleaseTime ); + } + + maDisplayCache.push_back( pNewEntry ); + mnUsedDisplaySize += pNewEntry->GetCacheSize(); + bRet = true; + } + + return bRet; +} + +bool GraphicCache::CreateDisplayCacheObj( OutputDevice const * pOut, const Point& rPt, const Size& rSz, + const GraphicObject& rObj, const GraphicAttr& rAttr, + const GDIMetaFile& rMtf ) +{ + const sal_uLong nNeededSize = GraphicDisplayCacheEntry::GetNeededSize( pOut, rPt, rSz, rObj, rAttr ); + bool bRet = false; + + if( nNeededSize <= GetMaxObjDisplayCacheSize() ) + { + if( nNeededSize > GetFreeDisplayCacheSize() ) + ImplFreeDisplayCacheSpace( nNeededSize - GetFreeDisplayCacheSize() ); + + GraphicDisplayCacheEntry* pNewEntry = new GraphicDisplayCacheEntry( ImplGetCacheEntry( rObj ), + pOut, rPt, rSz, rObj, rAttr, rMtf ); + + if( GetCacheTimeout() ) + { + ::salhelper::TTimeValue aReleaseTime; + + osl_getSystemTime( &aReleaseTime ); + aReleaseTime.addTime( ::salhelper::TTimeValue( GetCacheTimeout(), 0 ) ); + pNewEntry->SetReleaseTime( aReleaseTime ); + } + + maDisplayCache.push_back( pNewEntry ); + mnUsedDisplaySize += pNewEntry->GetCacheSize(); + bRet = true; + } + + return bRet; +} + +bool GraphicCache::DrawDisplayCacheObj( OutputDevice* pOut, const Point& rPt, const Size& rSz, + const GraphicObject& rObj, const GraphicAttr& rAttr ) +{ + const Point aPtPixel( pOut->LogicToPixel( rPt ) ); + const Size aSzPixel( pOut->LogicToPixel( rSz ) ); + const GraphicCacheEntry* pCacheEntry = ImplGetCacheEntry( rObj ); + GraphicDisplayCacheEntry* pDisplayCacheEntry = nullptr; + GraphicDisplayCacheEntryVector::iterator it = maDisplayCache.begin(); + bool bRet = false; + + while( !bRet && it != maDisplayCache.end() ) + { + pDisplayCacheEntry = *it; + if( pDisplayCacheEntry->Matches( pOut, aPtPixel, aSzPixel, pCacheEntry, rAttr ) ) + { + ::salhelper::TTimeValue aReleaseTime; + + // put found object at last used position + it = maDisplayCache.erase( it ); + maDisplayCache.push_back( pDisplayCacheEntry ); + + if( GetCacheTimeout() ) + { + osl_getSystemTime( &aReleaseTime ); + aReleaseTime.addTime( ::salhelper::TTimeValue( GetCacheTimeout(), 0 ) ); + } + + pDisplayCacheEntry->SetReleaseTime( aReleaseTime ); + bRet = true; + } + else + ++it; + } + + if( bRet ) + pDisplayCacheEntry->Draw( pOut, rPt, rSz ); + + return bRet; +} + +bool GraphicCache::ImplFreeDisplayCacheSpace( sal_uLong nSizeToFree ) +{ + sal_uLong nFreedSize = 0; + + if( nSizeToFree ) + { + GraphicDisplayCacheEntryVector::iterator it = maDisplayCache.begin(); + + if( nSizeToFree > mnUsedDisplaySize ) + nSizeToFree = mnUsedDisplaySize; + + while( it != maDisplayCache.end() ) + { + GraphicDisplayCacheEntry* pCacheObj = *it; + + nFreedSize += pCacheObj->GetCacheSize(); + mnUsedDisplaySize -= pCacheObj->GetCacheSize(); + it = maDisplayCache.erase( it ); + delete pCacheObj; + + if( nFreedSize >= nSizeToFree ) + break; + } + } + + return( nFreedSize >= nSizeToFree ); +} + +GraphicCacheEntry* GraphicCache::ImplGetCacheEntry( const GraphicObject& rObj ) +{ + GraphicCacheEntry* pRet = nullptr; + + for (auto const& elem : maGraphicCache) + { + if( elem->HasGraphicObjectReference( rObj ) ) + { + return elem; + } + } + + return pRet; +} + +IMPL_LINK( GraphicCache, ReleaseTimeoutHdl, Timer*, pTimer, void ) +{ + pTimer->Stop(); + + ::salhelper::TTimeValue aCurTime; + GraphicDisplayCacheEntryVector::iterator it = maDisplayCache.begin(); + + osl_getSystemTime( &aCurTime ); + + while( it != maDisplayCache.end() ) + { + GraphicDisplayCacheEntry* pDisplayEntry = *it; + const ::salhelper::TTimeValue& rReleaseTime = pDisplayEntry->GetReleaseTime(); + + if( !rReleaseTime.isEmpty() && ( rReleaseTime < aCurTime ) ) + { + mnUsedDisplaySize -= pDisplayEntry->GetCacheSize(); + it = maDisplayCache.erase( it ); + delete pDisplayEntry; + } + else + ++it; + } + + pTimer->Start(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/graphic/grfcache.hxx b/vcl/source/graphic/grfcache.hxx new file mode 100644 index 000000000000..7eec013ce521 --- /dev/null +++ b/vcl/source/graphic/grfcache.hxx @@ -0,0 +1,136 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_VCL_SOURCE_GRAPHIC_GRFCACHE_HXX +#define INCLUDED_VCL_SOURCE_GRAPHIC_GRFCACHE_HXX + +#include <vcl/graph.hxx> +#include <vcl/timer.hxx> +#include <vector> + + +class GraphicAttr; +class GraphicCacheEntry; +class GraphicDisplayCacheEntry; +class GraphicObject; + +class GraphicCache +{ +private: + typedef std::vector< GraphicCacheEntry* > GraphicCacheEntryVector; + typedef std::vector< GraphicDisplayCacheEntry* > GraphicDisplayCacheEntryVector; + + Timer maReleaseTimer; + GraphicCacheEntryVector maGraphicCache; + GraphicDisplayCacheEntryVector maDisplayCache; + sal_uLong mnReleaseTimeoutSeconds; + sal_uLong mnMaxDisplaySize; + sal_uLong mnMaxObjDisplaySize; + sal_uLong mnUsedDisplaySize; + + bool ImplFreeDisplayCacheSpace( sal_uLong nSizeToFree ); + GraphicCacheEntry* ImplGetCacheEntry( const GraphicObject& rObj ); + + + DECL_LINK( ReleaseTimeoutHdl, Timer*, void ); + +public: + + GraphicCache( + sal_uLong nDisplayCacheSize, + sal_uLong nMaxObjDisplayCacheSize + ); + + ~GraphicCache(); + +public: + + void AddGraphicObject( + const GraphicObject& rObj, + Graphic& rSubstitute, + const OString* pID, + const GraphicObject* pCopyObj + ); + + void ReleaseGraphicObject( const GraphicObject& rObj ); + + void GraphicObjectWasSwappedOut( const GraphicObject& rObj ); + void GraphicObjectWasSwappedIn( const GraphicObject& rObj ); + + OString GetUniqueID( const GraphicObject& rObj ) const; + +public: + + void SetMaxDisplayCacheSize( sal_uLong nNewCacheSize ); + sal_uLong GetMaxDisplayCacheSize() const { return mnMaxDisplaySize; }; + + sal_uLong GetMaxObjDisplayCacheSize() const { return mnMaxObjDisplaySize; } + + sal_uLong GetUsedDisplayCacheSize() const { return mnUsedDisplaySize; } + sal_uLong GetFreeDisplayCacheSize() const { return( mnMaxDisplaySize - mnUsedDisplaySize ); } + + void SetCacheTimeout( sal_uLong nTimeoutSeconds ); + sal_uLong GetCacheTimeout() const { return mnReleaseTimeoutSeconds; } + + bool IsDisplayCacheable( + OutputDevice const * pOut, + const Point& rPt, + const Size& rSz, + const GraphicObject& rObj, + const GraphicAttr& rAttr + ) const; + + bool IsInDisplayCache( + OutputDevice const * pOut, + const Point& rPt, + const Size& rSz, + const GraphicObject& rObj, + const GraphicAttr& rAttr + ) const; + + bool CreateDisplayCacheObj( + OutputDevice const * pOut, + const Point& rPt, + const Size& rSz, + const GraphicObject& rObj, + const GraphicAttr& rAttr, + const BitmapEx& rBmpEx + ); + + bool CreateDisplayCacheObj( + OutputDevice const * pOut, + const Point& rPt, + const Size& rSz, + const GraphicObject& rObj, + const GraphicAttr& rAttr, + const GDIMetaFile& rMtf + ); + + bool DrawDisplayCacheObj( + OutputDevice* pOut, + const Point& rPt, + const Size& rSz, + const GraphicObject& rObj, + const GraphicAttr& rAttr + ); +}; + +#endif // INCLUDED_VCL_SOURCE_GRAPHIC_GRFCACHE_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/vcl.common.component b/vcl/vcl.common.component new file mode 100644 index 000000000000..d5f7829242ac --- /dev/null +++ b/vcl/vcl.common.component @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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 . +--> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + prefix="vcl" xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.graphic.GraphicProvider" + constructor="com_sun_star_comp_graphic_GraphicProvider_get_implementation"> + <service name="com.sun.star.graphic.GraphicProvider"/> + </implementation> + <implementation name="com.sun.star.graphic.GraphicObject" + constructor="com_sun_star_graphic_GraphicObject_get_implementation"> + <service name="com.sun.star.graphic.GraphicObject"/> + </implementation> +</component> |