/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace css; using com::sun::star::uno::Reference; using com::sun::star::uno::XInterface; using com::sun::star::uno::UNO_QUERY; using com::sun::star::uno::Sequence; using com::sun::star::container::XNameContainer; using com::sun::star::beans::XPropertySet; #define WATERMARK_LUM_OFFSET 50 #define WATERMARK_CON_OFFSET -70 namespace vcl::graphic { void SearchForGraphics(uno::Reference const & xInterface, std::vector> & raGraphicList) { uno::Reference xPropertySet(xInterface, UNO_QUERY); if (xPropertySet.is()) { if (xPropertySet->getPropertySetInfo()->hasPropertyByName(u"ImageURL"_ustr)) { OUString sURL; xPropertySet->getPropertyValue(u"ImageURL"_ustr) >>= sURL; if (!sURL.isEmpty() && !GraphicObject::isGraphicObjectUniqueIdURL(sURL)) { Graphic aGraphic = vcl::graphic::loadFromURL(sURL); if (!aGraphic.IsNone()) { raGraphicList.push_back(aGraphic.GetXGraphic()); } } } else if (xPropertySet->getPropertySetInfo()->hasPropertyByName(u"Graphic"_ustr)) { uno::Reference xGraphic; xPropertySet->getPropertyValue(u"Graphic"_ustr) >>= xGraphic; if (xGraphic.is()) { raGraphicList.push_back(xGraphic); } } } Reference xContainer(xInterface, UNO_QUERY); if (xContainer.is()) { const css::uno::Sequence aElementNames = xContainer->getElementNames(); for (OUString const & rName : aElementNames) { uno::Reference xInnerInterface; xContainer->getByName(rName) >>= xInnerInterface; SearchForGraphics(xInnerInterface, raGraphicList); } } } } // end namespace vcl::graphic namespace { bool lclDrawObj(OutputDevice& rOut, const Point& rPt, const Size& rSz, GraphicObject const & rObj, const GraphicAttr& rAttr) { Point aPt( rPt ); Size aSz( rSz ); bool bRet = false; if( ( rObj.GetType() == GraphicType::Bitmap ) || ( rObj.GetType() == GraphicType::GdiMetafile ) ) { // simple output of transformed graphic const Graphic aGraphic( rObj.GetTransformedGraphic( &rAttr ) ); if( aGraphic.IsSupportedGraphic() ) { const Degree10 nRot10 = rAttr.GetRotation() % 3600_deg10; if( nRot10 ) { tools::Polygon aPoly( tools::Rectangle( aPt, aSz ) ); aPoly.Rotate( aPt, nRot10 ); const tools::Rectangle aRotBoundRect( aPoly.GetBoundRect() ); aPt = aRotBoundRect.TopLeft(); aSz = aRotBoundRect.GetSize(); } aGraphic.Draw(rOut, aPt, aSz); } bRet = true; } return bRet; } void lclImplAdjust( BitmapEx& rBmpEx, const GraphicAttr& rAttr, GraphicAdjustmentFlags nAdjustmentFlags ) { GraphicAttr aAttr( rAttr ); if( ( nAdjustmentFlags & GraphicAdjustmentFlags::DRAWMODE ) && aAttr.IsSpecialDrawMode() ) { switch( aAttr.GetDrawMode() ) { case GraphicDrawMode::Mono: rBmpEx.Convert( BmpConversion::N1BitThreshold ); break; case GraphicDrawMode::Greys: rBmpEx.Convert( BmpConversion::N8BitGreys ); break; case GraphicDrawMode::Watermark: { aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET ); aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET ); } break; default: break; } } if( ( nAdjustmentFlags & GraphicAdjustmentFlags::COLORS ) && aAttr.IsAdjusted() ) { rBmpEx.Adjust( aAttr.GetLuminance(), aAttr.GetContrast(), aAttr.GetChannelR(), aAttr.GetChannelG(), aAttr.GetChannelB(), aAttr.GetGamma(), aAttr.IsInvert() ); } if( ( nAdjustmentFlags & GraphicAdjustmentFlags::MIRROR ) && aAttr.IsMirrored() ) { rBmpEx.Mirror( aAttr.GetMirrorFlags() ); } if( ( nAdjustmentFlags & GraphicAdjustmentFlags::ROTATE ) && aAttr.IsRotated() ) { rBmpEx.Rotate( aAttr.GetRotation(), COL_TRANSPARENT ); } if( ( nAdjustmentFlags & GraphicAdjustmentFlags::TRANSPARENCY ) && aAttr.IsTransparent() ) { rBmpEx.AdjustTransparency(255 - aAttr.GetAlpha()); } } void lclImplAdjust( GDIMetaFile& rMtf, const GraphicAttr& rAttr, GraphicAdjustmentFlags nAdjustmentFlags ) { GraphicAttr aAttr( rAttr ); if( ( nAdjustmentFlags & GraphicAdjustmentFlags::DRAWMODE ) && aAttr.IsSpecialDrawMode() ) { switch( aAttr.GetDrawMode() ) { case GraphicDrawMode::Mono: rMtf.Convert( MtfConversion::N1BitThreshold ); break; case GraphicDrawMode::Greys: rMtf.Convert( MtfConversion::N8BitGreys ); break; case GraphicDrawMode::Watermark: { aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET ); aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET ); } break; default: break; } } if( ( nAdjustmentFlags & GraphicAdjustmentFlags::COLORS ) && aAttr.IsAdjusted() ) { rMtf.Adjust( aAttr.GetLuminance(), aAttr.GetContrast(), aAttr.GetChannelR(), aAttr.GetChannelG(), aAttr.GetChannelB(), aAttr.GetGamma(), aAttr.IsInvert() ); } if( ( nAdjustmentFlags & GraphicAdjustmentFlags::MIRROR ) && aAttr.IsMirrored() ) { rMtf.Mirror( aAttr.GetMirrorFlags() ); } if( ( nAdjustmentFlags & GraphicAdjustmentFlags::ROTATE ) && aAttr.IsRotated() ) { rMtf.Rotate( aAttr.GetRotation() ); } if( ( nAdjustmentFlags & GraphicAdjustmentFlags::TRANSPARENCY ) && aAttr.IsTransparent() ) { OSL_FAIL( "Missing implementation: Mtf-Transparency" ); } } void lclImplAdjust( Animation& rAnimation, const GraphicAttr& rAttr, GraphicAdjustmentFlags nAdjustmentFlags ) { GraphicAttr aAttr( rAttr ); if( ( nAdjustmentFlags & GraphicAdjustmentFlags::DRAWMODE ) && aAttr.IsSpecialDrawMode() ) { switch( aAttr.GetDrawMode() ) { case GraphicDrawMode::Mono: rAnimation.Convert( BmpConversion::N1BitThreshold ); break; case GraphicDrawMode::Greys: rAnimation.Convert( BmpConversion::N8BitGreys ); break; case GraphicDrawMode::Watermark: { aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET ); aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET ); } break; default: break; } } if( ( nAdjustmentFlags & GraphicAdjustmentFlags::COLORS ) && aAttr.IsAdjusted() ) { rAnimation.Adjust( aAttr.GetLuminance(), aAttr.GetContrast(), aAttr.GetChannelR(), aAttr.GetChannelG(), aAttr.GetChannelB(), aAttr.GetGamma(), aAttr.IsInvert() ); } if( ( nAdjustmentFlags & GraphicAdjustmentFlags::MIRROR ) && aAttr.IsMirrored() ) { rAnimation.Mirror( aAttr.GetMirrorFlags() ); } if( ( nAdjustmentFlags & GraphicAdjustmentFlags::ROTATE ) && aAttr.IsRotated() ) { OSL_FAIL( "Missing implementation: Animation-Rotation" ); } if( ( nAdjustmentFlags & GraphicAdjustmentFlags::TRANSPARENCY ) && aAttr.IsTransparent() ) { OSL_FAIL( "Missing implementation: Animation-Transparency" ); } } } // end anonymous namespace struct GrfSimpleCacheObj { Graphic maGraphic; GraphicAttr maAttr; GrfSimpleCacheObj( Graphic aGraphic, const GraphicAttr& rAttr ) : maGraphic(std::move( aGraphic )), maAttr( rAttr ) {} }; GraphicObject::GraphicObject() { } GraphicObject::GraphicObject(Graphic aGraphic) : maGraphic(std::move(aGraphic)) { } GraphicObject::GraphicObject(const GraphicObject& rGraphicObj) : maGraphic(rGraphicObj.GetGraphic()) , maAttr(rGraphicObj.maAttr) , maUserData(rGraphicObj.maUserData) { } GraphicObject::~GraphicObject() { } GraphicType GraphicObject::GetType() const { return maGraphic.GetType(); } Size GraphicObject::GetPrefSize() const { return maGraphic.GetPrefSize(); } MapMode GraphicObject::GetPrefMapMode() const { return maGraphic.GetPrefMapMode(); } bool GraphicObject::IsTransparent() const { return maGraphic.IsTransparent(); } bool GraphicObject::IsAnimated() const { return maGraphic.IsAnimated(); } bool GraphicObject::IsEPS() const { return maGraphic.IsEPS(); } bool GraphicObject::ImplGetCropParams(const OutputDevice& rOut, Point& rPt, Size& rSz, const GraphicAttr* pAttr, tools::PolyPolygon& rClipPolyPoly, bool& bRectClipRegion) const { bool bRet = false; if( GetType() != GraphicType::NONE ) { tools::Polygon aClipPoly( tools::Rectangle( rPt, rSz ) ); const Degree10 nRot10 = pAttr->GetRotation() % 3600_deg10; const Point aOldOrigin( rPt ); const MapMode aMap100( MapUnit::Map100thMM ); Size aSize100; tools::Long nTotalWidth, nTotalHeight; if( nRot10 ) { aClipPoly.Rotate( rPt, nRot10 ); bRectClipRegion = false; } else bRectClipRegion = true; rClipPolyPoly = tools::PolyPolygon(aClipPoly); if (maGraphic.GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel) aSize100 = Application::GetDefaultDevice()->PixelToLogic( maGraphic.GetPrefSize(), aMap100 ); else { MapMode m(maGraphic.GetPrefMapMode()); aSize100 = rOut.LogicToLogic( maGraphic.GetPrefSize(), &m, &aMap100 ); } nTotalWidth = aSize100.Width() - pAttr->GetLeftCrop() - pAttr->GetRightCrop(); nTotalHeight = aSize100.Height() - pAttr->GetTopCrop() - pAttr->GetBottomCrop(); if( !aSize100.IsEmpty() && nTotalWidth > 0 && nTotalHeight > 0 ) { double fScale = static_cast(aSize100.Width()) / nTotalWidth; const tools::Long nNewLeft = basegfx::fround( ( ( pAttr->GetMirrorFlags() & BmpMirrorFlags::Horizontal ) ? pAttr->GetRightCrop() : pAttr->GetLeftCrop() ) * -fScale ); const tools::Long nNewRight = nNewLeft + basegfx::fround( aSize100.Width() * fScale ) - 1; fScale = static_cast(rSz.Width()) / aSize100.Width(); rPt.AdjustX(basegfx::fround(nNewLeft * fScale)); rSz.setWidth(basegfx::fround((nNewRight - nNewLeft + 1) * fScale)); fScale = static_cast(aSize100.Height()) / nTotalHeight; const tools::Long nNewTop = basegfx::fround( ( ( pAttr->GetMirrorFlags() & BmpMirrorFlags::Vertical ) ? pAttr->GetBottomCrop() : pAttr->GetTopCrop() ) * -fScale ); const tools::Long nNewBottom = nNewTop + basegfx::fround( aSize100.Height() * fScale ) - 1; fScale = static_cast(rSz.Height()) / aSize100.Height(); rPt.AdjustY(basegfx::fround(nNewTop * fScale)); rSz.setHeight(basegfx::fround((nNewBottom - nNewTop + 1) * fScale)); if( nRot10 ) { tools::Polygon aOriginPoly( 1 ); aOriginPoly[ 0 ] = rPt; aOriginPoly.Rotate( aOldOrigin, nRot10 ); rPt = aOriginPoly[ 0 ]; } bRet = true; } } return bRet; } GraphicObject& GraphicObject::operator=( const GraphicObject& rGraphicObj ) { if( &rGraphicObj != this ) { mxSimpleCache.reset(); maGraphic = rGraphicObj.GetGraphic(); maAttr = rGraphicObj.maAttr; maUserData = rGraphicObj.maUserData; } return *this; } bool GraphicObject::operator==( const GraphicObject& rGraphicObj ) const { return rGraphicObj.maGraphic == maGraphic && rGraphicObj.maAttr == maAttr; } OString GraphicObject::GetUniqueID() const { return GetGraphic().getUniqueID(); } void GraphicObject::SetAttr( const GraphicAttr& rAttr ) { maAttr = rAttr; if (mxSimpleCache && (mxSimpleCache->maAttr != rAttr)) mxSimpleCache.reset(); } void GraphicObject::SetUserData() { maUserData.clear(); } void GraphicObject::SetUserData( const OUString& rUserData ) { maUserData = rUserData; } bool GraphicObject::Draw(OutputDevice& rOut, const Point& rPt, const Size& rSz, const GraphicAttr* pAttr) const { GraphicAttr aAttr( pAttr ? *pAttr : GetAttr() ); Point aPt( rPt ); Size aSz( rSz ); const DrawModeFlags nOldDrawMode = rOut.GetDrawMode(); bool bCropped = aAttr.IsCropped(); bool bRet; rOut.SetDrawMode(nOldDrawMode & ~DrawModeFlags( DrawModeFlags::SettingsLine | DrawModeFlags::SettingsFill | DrawModeFlags::SettingsText | DrawModeFlags::SettingsGradient )); // mirrored horizontally if( aSz.Width() < 0 ) { aPt.AdjustX(aSz.Width() + 1 ); aSz.setWidth( -aSz.Width() ); aAttr.SetMirrorFlags( aAttr.GetMirrorFlags() ^ BmpMirrorFlags::Horizontal ); } // mirrored vertically if( aSz.Height() < 0 ) { aPt.AdjustY(aSz.Height() + 1 ); aSz.setHeight( -aSz.Height() ); aAttr.SetMirrorFlags( aAttr.GetMirrorFlags() ^ BmpMirrorFlags::Vertical ); } if( bCropped ) { tools::PolyPolygon aClipPolyPoly; bool bRectClip; const bool bCrop = ImplGetCropParams(rOut, aPt, aSz, &aAttr, aClipPolyPoly, bRectClip); rOut.Push(vcl::PushFlags::CLIPREGION); if( bCrop ) { if( bRectClip ) { // #i29534# Store crop rect for later forwarding to // PDF writer tools::Rectangle aCropRect = aClipPolyPoly.GetBoundRect(); rOut.IntersectClipRegion(aCropRect); } else { rOut.IntersectClipRegion(vcl::Region(aClipPolyPoly)); } } } bRet = lclDrawObj(rOut, aPt, aSz, *this, aAttr); if( bCropped ) rOut.Pop(); rOut.SetDrawMode( nOldDrawMode ); return bRet; } void GraphicObject::DrawTiled(OutputDevice& rOut, const tools::Rectangle& rArea, const Size& rSize, const Size& rOffset, int nTileCacheSize1D) { if (rSize.IsEmpty()) return; const MapMode aOutMapMode(rOut.GetMapMode()); // #106258# Clamp size to 1 for zero values. This is okay, since // logical size of zero is handled above already const Size aOutTileSize( ::std::max( tools::Long(1), rOut.LogicToPixel( rSize, aOutMapMode ).Width() ), ::std::max( tools::Long(1), rOut.LogicToPixel( rSize, aOutMapMode ).Height() ) ); //#i69780 clip final tile size to a sane max size while ((static_cast(rSize.Width()) * nTileCacheSize1D) > SAL_MAX_UINT16) nTileCacheSize1D /= 2; while ((static_cast(rSize.Height()) * nTileCacheSize1D) > SAL_MAX_UINT16) nTileCacheSize1D /= 2; ImplDrawTiled(rOut, rArea, aOutTileSize, rOffset, nullptr, nTileCacheSize1D); } bool GraphicObject::StartAnimation(OutputDevice& rOut, const Point& rPt, const Size& rSz, tools::Long nRendererId, OutputDevice* pFirstFrameOutDev) { bool bRet = false; GetGraphic(); const GraphicAttr aAttr( GetAttr() ); if (IsAnimated()) { Point aPt( rPt ); Size aSz( rSz ); bool bCropped = aAttr.IsCropped(); if( bCropped ) { tools::PolyPolygon aClipPolyPoly; bool bRectClip; const bool bCrop = ImplGetCropParams(rOut, aPt, aSz, &aAttr, aClipPolyPoly, bRectClip); rOut.Push(vcl::PushFlags::CLIPREGION); if( bCrop ) { if( bRectClip ) rOut.IntersectClipRegion(aClipPolyPoly.GetBoundRect()); else rOut.IntersectClipRegion(vcl::Region(aClipPolyPoly)); } } if (!mxSimpleCache || (mxSimpleCache->maAttr != aAttr) || pFirstFrameOutDev) { mxSimpleCache.reset(new GrfSimpleCacheObj(GetTransformedGraphic(&aAttr), aAttr)); mxSimpleCache->maGraphic.SetAnimationNotifyHdl(GetGraphic().GetAnimationNotifyHdl()); } mxSimpleCache->maGraphic.StartAnimation(rOut, aPt, aSz, nRendererId, pFirstFrameOutDev); if( bCropped ) rOut.Pop(); bRet = true; } else bRet = Draw(rOut, rPt, rSz, &aAttr); return bRet; } void GraphicObject::StopAnimation( const OutputDevice* pOut, tools::Long nRendererId ) { if (mxSimpleCache) mxSimpleCache->maGraphic.StopAnimation(pOut, nRendererId); } const Graphic& GraphicObject::GetGraphic() const { return maGraphic; } void GraphicObject::SetGraphic( const Graphic& rGraphic) { maGraphic = rGraphic; } Graphic GraphicObject::GetTransformedGraphic( const Size& rDestSize, const MapMode& rDestMap, const GraphicAttr& rAttr ) const { // #104550# Extracted from svx/source/svdraw/svdograf.cxx Graphic aTransGraphic( GetGraphic() ); const GraphicType eType = GetType(); const Size aSrcSize( aTransGraphic.GetPrefSize() ); // #104115# Convert the crop margins to graphic object mapmode const MapMode aMapGraph( aTransGraphic.GetPrefMapMode() ); const MapMode aMap100( MapUnit::Map100thMM ); Size aCropLeftTop; Size aCropRightBottom; if( GraphicType::GdiMetafile == eType ) { GDIMetaFile aMtf( aTransGraphic.GetGDIMetaFile() ); if (aMapGraph.GetMapUnit() == MapUnit::MapPixel) { // crops are in 1/100th mm -> to aMapGraph -> to MapUnit::MapPixel aCropLeftTop = Application::GetDefaultDevice()->LogicToPixel( Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()), aMap100); aCropRightBottom = Application::GetDefaultDevice()->LogicToPixel( Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()), aMap100); } else { // crops are in GraphicObject units -> to aMapGraph aCropLeftTop = OutputDevice::LogicToLogic( Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()), aMap100, aMapGraph); aCropRightBottom = OutputDevice::LogicToLogic( Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()), aMap100, aMapGraph); } // #104115# If the metafile is cropped, give it a special // treatment: clip against the remaining area, scale up such // that this area later fills the desired size, and move the // origin to the upper left edge of that area. if( rAttr.IsCropped() ) { const MapMode aMtfMapMode( aMtf.GetPrefMapMode() ); tools::Rectangle aClipRect( aMtfMapMode.GetOrigin().X() + aCropLeftTop.Width(), aMtfMapMode.GetOrigin().Y() + aCropLeftTop.Height(), aMtfMapMode.GetOrigin().X() + aSrcSize.Width() - aCropRightBottom.Width(), aMtfMapMode.GetOrigin().Y() + aSrcSize.Height() - aCropRightBottom.Height() ); // #104115# To correctly crop rotated metafiles, clip by view rectangle aMtf.AddAction( new MetaISectRectClipRegionAction( aClipRect ), 0 ); // #104115# To crop the metafile, scale larger than the output rectangle aMtf.Scale( static_cast(rDestSize.Width()) / (aSrcSize.Width() - aCropLeftTop.Width() - aCropRightBottom.Width()), static_cast(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(static_cast(rDestSize.Width()) * (1.0 + (aCropLeftTop.Width() + aCropRightBottom.Width()) / aSrcSize.Width()) + .5), static_cast(static_cast(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(static_cast(aMtfMapMode.GetOrigin().X()) + rDestSize.Width() * aCropLeftTop.Width() / (aSrcSize.Width() - aCropLeftTop.Width() - aCropRightBottom.Width()) + .5), static_cast(static_cast(aMtfMapMode.GetOrigin().Y()) + rDestSize.Height() * aCropLeftTop.Height() / (aSrcSize.Height() - aCropLeftTop.Height() - aCropRightBottom.Height()) + .5) ); MapMode aNewMap( rDestMap ); aNewMap.SetOrigin( OutputDevice::LogicToLogic(aNewOrigin, aMtfMapMode, rDestMap) ); aMtf.SetPrefMapMode( aNewMap ); } else { aMtf.Scale( Fraction( rDestSize.Width(), aSrcSize.Width() ), Fraction( rDestSize.Height(), aSrcSize.Height() ) ); aMtf.SetPrefMapMode( rDestMap ); } aTransGraphic = aMtf; } else if( GraphicType::Bitmap == eType ) { BitmapEx aBitmapEx( aTransGraphic.GetBitmapEx() ); tools::Rectangle aCropRect; // convert crops to pixel if(rAttr.IsCropped()) { if (aMapGraph.GetMapUnit() == MapUnit::MapPixel) { // crops are in 1/100th mm -> to MapUnit::MapPixel aCropLeftTop = Application::GetDefaultDevice()->LogicToPixel( Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()), aMap100); aCropRightBottom = Application::GetDefaultDevice()->LogicToPixel( Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()), aMap100); } else { // crops are in GraphicObject units -> to MapUnit::MapPixel aCropLeftTop = Application::GetDefaultDevice()->LogicToPixel( Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()), aMapGraph); aCropRightBottom = Application::GetDefaultDevice()->LogicToPixel( Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()), aMapGraph); } // convert from prefmapmode to pixel Size aSrcSizePixel( Application::GetDefaultDevice()->LogicToPixel( aSrcSize, aMapGraph)); if(rAttr.IsCropped() && (aSrcSizePixel.Width() != aBitmapEx.GetSizePixel().Width() || aSrcSizePixel.Height() != aBitmapEx.GetSizePixel().Height()) && aSrcSizePixel.Width()) { // the size in pixels calculated from Graphic's internal MapMode (aTransGraphic.GetPrefMapMode()) // and its internal size (aTransGraphic.GetPrefSize()) is different from its real pixel size. // This can be interpreted as this values to be set wrong, but needs to be corrected since e.g. // existing cropping is calculated based on this logic values already. // aBitmapEx.Scale(aSrcSizePixel); // another possibility is to adapt the values created so far with a factor; this // will keep the original Bitmap untouched and thus quality will not change // caution: convert to double first, else pretty big errors may occur const double fFactorX(static_cast(aBitmapEx.GetSizePixel().Width()) / aSrcSizePixel.Width()); const double fFactorY(static_cast(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; nFramePixelToLogic(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: */