1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
24 #include <o3tl/string_view.hxx>
25 #include <osl/diagnose.h>
26 #include <tools/fract.hxx>
27 #include <tools/helpers.hxx>
29 #include <vcl/svapp.hxx>
30 #include <vcl/metaact.hxx>
31 #include <vcl/GraphicObject.hxx>
32 #include <vcl/GraphicLoader.hxx>
33 #include <vcl/outdev.hxx>
35 #include <com/sun/star/container/XNameContainer.hpp>
36 #include <com/sun/star/beans/XPropertySet.hpp>
37 #include <com/sun/star/graphic/XGraphic.hpp>
42 using com::sun::star::uno::Reference
;
43 using com::sun::star::uno::XInterface
;
44 using com::sun::star::uno::UNO_QUERY
;
45 using com::sun::star::uno::Sequence
;
46 using com::sun::star::container::XNameContainer
;
47 using com::sun::star::beans::XPropertySet
;
49 #define WATERMARK_LUM_OFFSET 50
50 #define WATERMARK_CON_OFFSET -70
52 namespace vcl::graphic
55 void SearchForGraphics(uno::Reference
<uno::XInterface
> const & xInterface
,
56 std::vector
<uno::Reference
<css::graphic::XGraphic
>> & raGraphicList
)
58 uno::Reference
<beans::XPropertySet
> xPropertySet(xInterface
, UNO_QUERY
);
59 if (xPropertySet
.is())
61 if (xPropertySet
->getPropertySetInfo()->hasPropertyByName(u
"ImageURL"_ustr
))
64 xPropertySet
->getPropertyValue(u
"ImageURL"_ustr
) >>= sURL
;
65 if (!sURL
.isEmpty() && !GraphicObject::isGraphicObjectUniqueIdURL(sURL
))
67 Graphic aGraphic
= vcl::graphic::loadFromURL(sURL
);
68 if (!aGraphic
.IsNone())
70 raGraphicList
.push_back(aGraphic
.GetXGraphic());
73 } else if (xPropertySet
->getPropertySetInfo()->hasPropertyByName(u
"Graphic"_ustr
))
75 uno::Reference
<css::graphic::XGraphic
> xGraphic
;
76 xPropertySet
->getPropertyValue(u
"Graphic"_ustr
) >>= xGraphic
;
79 raGraphicList
.push_back(xGraphic
);
83 Reference
<XNameContainer
> xContainer(xInterface
, UNO_QUERY
);
86 const css::uno::Sequence
<OUString
> aElementNames
= xContainer
->getElementNames();
87 for (OUString
const & rName
: aElementNames
)
89 uno::Reference
<XInterface
> xInnerInterface
;
90 xContainer
->getByName(rName
) >>= xInnerInterface
;
91 SearchForGraphics(xInnerInterface
, raGraphicList
);
96 } // end namespace vcl::graphic
101 bool lclDrawObj(OutputDevice
& rOut
, const Point
& rPt
, const Size
& rSz
,
102 GraphicObject
const & rObj
, const GraphicAttr
& rAttr
)
108 if( ( rObj
.GetType() == GraphicType::Bitmap
) || ( rObj
.GetType() == GraphicType::GdiMetafile
) )
110 // simple output of transformed graphic
111 const Graphic
aGraphic( rObj
.GetTransformedGraphic( &rAttr
) );
113 if( aGraphic
.IsSupportedGraphic() )
115 const Degree10 nRot10
= rAttr
.GetRotation() % 3600_deg10
;
119 tools::Polygon
aPoly( tools::Rectangle( aPt
, aSz
) );
121 aPoly
.Rotate( aPt
, nRot10
);
122 const tools::Rectangle
aRotBoundRect( aPoly
.GetBoundRect() );
123 aPt
= aRotBoundRect
.TopLeft();
124 aSz
= aRotBoundRect
.GetSize();
127 aGraphic
.Draw(rOut
, aPt
, aSz
);
136 void lclImplAdjust( BitmapEx
& rBmpEx
, const GraphicAttr
& rAttr
, GraphicAdjustmentFlags nAdjustmentFlags
)
138 GraphicAttr
aAttr( rAttr
);
140 if( ( nAdjustmentFlags
& GraphicAdjustmentFlags::DRAWMODE
) && aAttr
.IsSpecialDrawMode() )
142 switch( aAttr
.GetDrawMode() )
144 case GraphicDrawMode::Mono
:
145 rBmpEx
.Convert( BmpConversion::N1BitThreshold
);
148 case GraphicDrawMode::Greys
:
149 rBmpEx
.Convert( BmpConversion::N8BitGreys
);
152 case GraphicDrawMode::Watermark
:
154 aAttr
.SetLuminance( aAttr
.GetLuminance() + WATERMARK_LUM_OFFSET
);
155 aAttr
.SetContrast( aAttr
.GetContrast() + WATERMARK_CON_OFFSET
);
164 if( ( nAdjustmentFlags
& GraphicAdjustmentFlags::COLORS
) && aAttr
.IsAdjusted() )
166 rBmpEx
.Adjust( aAttr
.GetLuminance(), aAttr
.GetContrast(),
167 aAttr
.GetChannelR(), aAttr
.GetChannelG(), aAttr
.GetChannelB(),
168 aAttr
.GetGamma(), aAttr
.IsInvert() );
171 if( ( nAdjustmentFlags
& GraphicAdjustmentFlags::MIRROR
) && aAttr
.IsMirrored() )
173 rBmpEx
.Mirror( aAttr
.GetMirrorFlags() );
176 if( ( nAdjustmentFlags
& GraphicAdjustmentFlags::ROTATE
) && aAttr
.IsRotated() )
178 rBmpEx
.Rotate( aAttr
.GetRotation(), COL_TRANSPARENT
);
181 if( ( nAdjustmentFlags
& GraphicAdjustmentFlags::TRANSPARENCY
) && aAttr
.IsTransparent() )
183 rBmpEx
.AdjustTransparency(255 - aAttr
.GetAlpha());
187 void lclImplAdjust( GDIMetaFile
& rMtf
, const GraphicAttr
& rAttr
, GraphicAdjustmentFlags nAdjustmentFlags
)
189 GraphicAttr
aAttr( rAttr
);
191 if( ( nAdjustmentFlags
& GraphicAdjustmentFlags::DRAWMODE
) && aAttr
.IsSpecialDrawMode() )
193 switch( aAttr
.GetDrawMode() )
195 case GraphicDrawMode::Mono
:
196 rMtf
.Convert( MtfConversion::N1BitThreshold
);
199 case GraphicDrawMode::Greys
:
200 rMtf
.Convert( MtfConversion::N8BitGreys
);
203 case GraphicDrawMode::Watermark
:
205 aAttr
.SetLuminance( aAttr
.GetLuminance() + WATERMARK_LUM_OFFSET
);
206 aAttr
.SetContrast( aAttr
.GetContrast() + WATERMARK_CON_OFFSET
);
215 if( ( nAdjustmentFlags
& GraphicAdjustmentFlags::COLORS
) && aAttr
.IsAdjusted() )
217 rMtf
.Adjust( aAttr
.GetLuminance(), aAttr
.GetContrast(),
218 aAttr
.GetChannelR(), aAttr
.GetChannelG(), aAttr
.GetChannelB(),
219 aAttr
.GetGamma(), aAttr
.IsInvert() );
222 if( ( nAdjustmentFlags
& GraphicAdjustmentFlags::MIRROR
) && aAttr
.IsMirrored() )
224 rMtf
.Mirror( aAttr
.GetMirrorFlags() );
227 if( ( nAdjustmentFlags
& GraphicAdjustmentFlags::ROTATE
) && aAttr
.IsRotated() )
229 rMtf
.Rotate( aAttr
.GetRotation() );
232 if( ( nAdjustmentFlags
& GraphicAdjustmentFlags::TRANSPARENCY
) && aAttr
.IsTransparent() )
234 OSL_FAIL( "Missing implementation: Mtf-Transparency" );
238 void lclImplAdjust( Animation
& rAnimation
, const GraphicAttr
& rAttr
, GraphicAdjustmentFlags nAdjustmentFlags
)
240 GraphicAttr
aAttr( rAttr
);
242 if( ( nAdjustmentFlags
& GraphicAdjustmentFlags::DRAWMODE
) && aAttr
.IsSpecialDrawMode() )
244 switch( aAttr
.GetDrawMode() )
246 case GraphicDrawMode::Mono
:
247 rAnimation
.Convert( BmpConversion::N1BitThreshold
);
250 case GraphicDrawMode::Greys
:
251 rAnimation
.Convert( BmpConversion::N8BitGreys
);
254 case GraphicDrawMode::Watermark
:
256 aAttr
.SetLuminance( aAttr
.GetLuminance() + WATERMARK_LUM_OFFSET
);
257 aAttr
.SetContrast( aAttr
.GetContrast() + WATERMARK_CON_OFFSET
);
266 if( ( nAdjustmentFlags
& GraphicAdjustmentFlags::COLORS
) && aAttr
.IsAdjusted() )
268 rAnimation
.Adjust( aAttr
.GetLuminance(), aAttr
.GetContrast(),
269 aAttr
.GetChannelR(), aAttr
.GetChannelG(), aAttr
.GetChannelB(),
270 aAttr
.GetGamma(), aAttr
.IsInvert() );
273 if( ( nAdjustmentFlags
& GraphicAdjustmentFlags::MIRROR
) && aAttr
.IsMirrored() )
275 rAnimation
.Mirror( aAttr
.GetMirrorFlags() );
278 if( ( nAdjustmentFlags
& GraphicAdjustmentFlags::ROTATE
) && aAttr
.IsRotated() )
280 OSL_FAIL( "Missing implementation: Animation-Rotation" );
283 if( ( nAdjustmentFlags
& GraphicAdjustmentFlags::TRANSPARENCY
) && aAttr
.IsTransparent() )
285 OSL_FAIL( "Missing implementation: Animation-Transparency" );
289 } // end anonymous namespace
291 struct GrfSimpleCacheObj
296 GrfSimpleCacheObj( Graphic aGraphic
, const GraphicAttr
& rAttr
) :
297 maGraphic(std::move( aGraphic
)), maAttr( rAttr
) {}
300 GraphicObject::GraphicObject()
304 GraphicObject::GraphicObject(Graphic aGraphic
)
305 : maGraphic(std::move(aGraphic
))
309 GraphicObject::GraphicObject(const GraphicObject
& rGraphicObj
)
310 : maGraphic(rGraphicObj
.GetGraphic())
311 , maAttr(rGraphicObj
.maAttr
)
312 , maUserData(rGraphicObj
.maUserData
)
316 GraphicObject::~GraphicObject()
320 GraphicType
GraphicObject::GetType() const
322 return maGraphic
.GetType();
325 Size
GraphicObject::GetPrefSize() const
327 return maGraphic
.GetPrefSize();
330 MapMode
GraphicObject::GetPrefMapMode() const
332 return maGraphic
.GetPrefMapMode();
335 bool GraphicObject::IsTransparent() const
337 return maGraphic
.IsTransparent();
340 bool GraphicObject::IsAnimated() const
342 return maGraphic
.IsAnimated();
345 bool GraphicObject::IsEPS() const
347 return maGraphic
.IsEPS();
350 bool GraphicObject::ImplGetCropParams(const OutputDevice
& rOut
, Point
& rPt
, Size
& rSz
, const GraphicAttr
* pAttr
,
351 tools::PolyPolygon
& rClipPolyPoly
, bool& bRectClipRegion
) const
355 if( GetType() != GraphicType::NONE
)
357 tools::Polygon
aClipPoly( tools::Rectangle( rPt
, rSz
) );
358 const Degree10 nRot10
= pAttr
->GetRotation() % 3600_deg10
;
359 const Point
aOldOrigin( rPt
);
360 const MapMode
aMap100( MapUnit::Map100thMM
);
362 tools::Long nTotalWidth
, nTotalHeight
;
366 aClipPoly
.Rotate( rPt
, nRot10
);
367 bRectClipRegion
= false;
370 bRectClipRegion
= true;
372 rClipPolyPoly
= tools::PolyPolygon(aClipPoly
);
374 if (maGraphic
.GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel
)
375 aSize100
= Application::GetDefaultDevice()->PixelToLogic( maGraphic
.GetPrefSize(), aMap100
);
378 MapMode
m(maGraphic
.GetPrefMapMode());
379 aSize100
= rOut
.LogicToLogic( maGraphic
.GetPrefSize(), &m
, &aMap100
);
382 nTotalWidth
= aSize100
.Width() - pAttr
->GetLeftCrop() - pAttr
->GetRightCrop();
383 nTotalHeight
= aSize100
.Height() - pAttr
->GetTopCrop() - pAttr
->GetBottomCrop();
385 if( !aSize100
.IsEmpty() && nTotalWidth
> 0 && nTotalHeight
> 0 )
387 double fScale
= static_cast<double>(aSize100
.Width()) / nTotalWidth
;
388 const tools::Long nNewLeft
= basegfx::fround
<tools::Long
>( ( ( pAttr
->GetMirrorFlags() & BmpMirrorFlags::Horizontal
) ? pAttr
->GetRightCrop() : pAttr
->GetLeftCrop() ) * -fScale
);
389 const tools::Long nNewRight
= nNewLeft
+ basegfx::fround
<tools::Long
>( aSize100
.Width() * fScale
) - 1;
391 fScale
= static_cast<double>(rSz
.Width()) / aSize100
.Width();
392 rPt
.AdjustX(basegfx::fround
<tools::Long
>(nNewLeft
* fScale
));
393 rSz
.setWidth(basegfx::fround
<tools::Long
>((nNewRight
- nNewLeft
+ 1) * fScale
));
395 fScale
= static_cast<double>(aSize100
.Height()) / nTotalHeight
;
396 const tools::Long nNewTop
= basegfx::fround
<tools::Long
>( ( ( pAttr
->GetMirrorFlags() & BmpMirrorFlags::Vertical
) ? pAttr
->GetBottomCrop() : pAttr
->GetTopCrop() ) * -fScale
);
397 const tools::Long nNewBottom
= nNewTop
+ basegfx::fround
<tools::Long
>( aSize100
.Height() * fScale
) - 1;
399 fScale
= static_cast<double>(rSz
.Height()) / aSize100
.Height();
400 rPt
.AdjustY(basegfx::fround
<tools::Long
>(nNewTop
* fScale
));
401 rSz
.setHeight(basegfx::fround
<tools::Long
>((nNewBottom
- nNewTop
+ 1) * fScale
));
405 tools::Polygon
aOriginPoly( 1 );
407 aOriginPoly
[ 0 ] = rPt
;
408 aOriginPoly
.Rotate( aOldOrigin
, nRot10
);
409 rPt
= aOriginPoly
[ 0 ];
419 GraphicObject
& GraphicObject::operator=( const GraphicObject
& rGraphicObj
)
421 if( &rGraphicObj
!= this )
423 mxSimpleCache
.reset();
424 maGraphic
= rGraphicObj
.GetGraphic();
425 maAttr
= rGraphicObj
.maAttr
;
426 maUserData
= rGraphicObj
.maUserData
;
432 bool GraphicObject::operator==( const GraphicObject
& rGraphicObj
) const
434 return rGraphicObj
.maGraphic
== maGraphic
435 && rGraphicObj
.maAttr
== maAttr
;
438 OString
GraphicObject::GetUniqueID() const
440 return GetGraphic().getUniqueID();
443 void GraphicObject::SetAttr( const GraphicAttr
& rAttr
)
447 if (mxSimpleCache
&& (mxSimpleCache
->maAttr
!= rAttr
))
448 mxSimpleCache
.reset();
451 void GraphicObject::SetUserData()
456 void GraphicObject::SetUserData( const OUString
& rUserData
)
458 maUserData
= rUserData
;
461 bool GraphicObject::Draw(OutputDevice
& rOut
, const Point
& rPt
, const Size
& rSz
,
462 const GraphicAttr
* pAttr
) const
464 GraphicAttr
aAttr( pAttr
? *pAttr
: GetAttr() );
467 const DrawModeFlags nOldDrawMode
= rOut
.GetDrawMode();
468 bool bCropped
= aAttr
.IsCropped();
471 rOut
.SetDrawMode(nOldDrawMode
& ~DrawModeFlags( DrawModeFlags::SettingsLine
| DrawModeFlags::SettingsFill
| DrawModeFlags::SettingsText
| DrawModeFlags::SettingsGradient
));
473 // mirrored horizontally
474 if( aSz
.Width() < 0 )
476 aPt
.AdjustX(aSz
.Width() + 1 );
477 aSz
.setWidth( -aSz
.Width() );
478 aAttr
.SetMirrorFlags( aAttr
.GetMirrorFlags() ^ BmpMirrorFlags::Horizontal
);
481 // mirrored vertically
482 if( aSz
.Height() < 0 )
484 aPt
.AdjustY(aSz
.Height() + 1 );
485 aSz
.setHeight( -aSz
.Height() );
486 aAttr
.SetMirrorFlags( aAttr
.GetMirrorFlags() ^ BmpMirrorFlags::Vertical
);
491 tools::PolyPolygon aClipPolyPoly
;
493 const bool bCrop
= ImplGetCropParams(rOut
, aPt
, aSz
, &aAttr
, aClipPolyPoly
, bRectClip
);
495 rOut
.Push(vcl::PushFlags::CLIPREGION
);
501 // #i29534# Store crop rect for later forwarding to
503 tools::Rectangle aCropRect
= aClipPolyPoly
.GetBoundRect();
504 rOut
.IntersectClipRegion(aCropRect
);
508 rOut
.IntersectClipRegion(vcl::Region(aClipPolyPoly
));
513 bRet
= lclDrawObj(rOut
, aPt
, aSz
, *this, aAttr
);
518 rOut
.SetDrawMode( nOldDrawMode
);
523 void GraphicObject::DrawTiled(OutputDevice
& rOut
, const tools::Rectangle
& rArea
, const Size
& rSize
,
524 const Size
& rOffset
, int nTileCacheSize1D
)
529 const MapMode
aOutMapMode(rOut
.GetMapMode());
530 // #106258# Clamp size to 1 for zero values. This is okay, since
531 // logical size of zero is handled above already
532 const Size
aOutTileSize( ::std::max( tools::Long(1), rOut
.LogicToPixel( rSize
, aOutMapMode
).Width() ),
533 ::std::max( tools::Long(1), rOut
.LogicToPixel( rSize
, aOutMapMode
).Height() ) );
535 //#i69780 clip final tile size to a sane max size
536 while ((static_cast<sal_Int64
>(rSize
.Width()) * nTileCacheSize1D
) > SAL_MAX_UINT16
)
537 nTileCacheSize1D
/= 2;
538 while ((static_cast<sal_Int64
>(rSize
.Height()) * nTileCacheSize1D
) > SAL_MAX_UINT16
)
539 nTileCacheSize1D
/= 2;
541 ImplDrawTiled(rOut
, rArea
, aOutTileSize
, rOffset
, nullptr, nTileCacheSize1D
);
544 bool GraphicObject::StartAnimation(OutputDevice
& rOut
, const Point
& rPt
, const Size
& rSz
,
545 tools::Long nRendererId
,
546 OutputDevice
* pFirstFrameOutDev
)
552 const GraphicAttr
aAttr( GetAttr() );
558 bool bCropped
= aAttr
.IsCropped();
562 tools::PolyPolygon aClipPolyPoly
;
564 const bool bCrop
= ImplGetCropParams(rOut
, aPt
, aSz
, &aAttr
, aClipPolyPoly
, bRectClip
);
566 rOut
.Push(vcl::PushFlags::CLIPREGION
);
571 rOut
.IntersectClipRegion(aClipPolyPoly
.GetBoundRect());
573 rOut
.IntersectClipRegion(vcl::Region(aClipPolyPoly
));
577 if (!mxSimpleCache
|| (mxSimpleCache
->maAttr
!= aAttr
) || pFirstFrameOutDev
)
579 mxSimpleCache
.reset(new GrfSimpleCacheObj(GetTransformedGraphic(&aAttr
), aAttr
));
580 mxSimpleCache
->maGraphic
.SetAnimationNotifyHdl(GetGraphic().GetAnimationNotifyHdl());
583 mxSimpleCache
->maGraphic
.StartAnimation(rOut
, aPt
, aSz
, nRendererId
, pFirstFrameOutDev
);
591 bRet
= Draw(rOut
, rPt
, rSz
, &aAttr
);
596 void GraphicObject::StopAnimation( const OutputDevice
* pOut
, tools::Long nRendererId
)
599 mxSimpleCache
->maGraphic
.StopAnimation(pOut
, nRendererId
);
602 const Graphic
& GraphicObject::GetGraphic() const
607 void GraphicObject::SetGraphic( const Graphic
& rGraphic
)
609 maGraphic
= rGraphic
;
612 Graphic
GraphicObject::GetTransformedGraphic( const Size
& rDestSize
, const MapMode
& rDestMap
, const GraphicAttr
& rAttr
) const
614 // #104550# Extracted from svx/source/svdraw/svdograf.cxx
615 Graphic
aTransGraphic( GetGraphic() );
616 const GraphicType eType
= GetType();
617 const Size
aSrcSize( aTransGraphic
.GetPrefSize() );
619 // #104115# Convert the crop margins to graphic object mapmode
620 const MapMode
aMapGraph( aTransGraphic
.GetPrefMapMode() );
621 const MapMode
aMap100( MapUnit::Map100thMM
);
624 Size aCropRightBottom
;
626 if( GraphicType::GdiMetafile
== eType
)
628 GDIMetaFile
aMtf( aTransGraphic
.GetGDIMetaFile() );
630 if (aMapGraph
.GetMapUnit() == MapUnit::MapPixel
)
632 // crops are in 1/100th mm -> to aMapGraph -> to MapUnit::MapPixel
633 aCropLeftTop
= Application::GetDefaultDevice()->LogicToPixel(
634 Size(rAttr
.GetLeftCrop(), rAttr
.GetTopCrop()),
636 aCropRightBottom
= Application::GetDefaultDevice()->LogicToPixel(
637 Size(rAttr
.GetRightCrop(), rAttr
.GetBottomCrop()),
642 // crops are in GraphicObject units -> to aMapGraph
643 aCropLeftTop
= OutputDevice::LogicToLogic(
644 Size(rAttr
.GetLeftCrop(), rAttr
.GetTopCrop()),
647 aCropRightBottom
= OutputDevice::LogicToLogic(
648 Size(rAttr
.GetRightCrop(), rAttr
.GetBottomCrop()),
653 // #104115# If the metafile is cropped, give it a special
654 // treatment: clip against the remaining area, scale up such
655 // that this area later fills the desired size, and move the
656 // origin to the upper left edge of that area.
657 if( rAttr
.IsCropped() )
659 const MapMode
aMtfMapMode( aMtf
.GetPrefMapMode() );
661 tools::Rectangle
aClipRect( aMtfMapMode
.GetOrigin().X() + aCropLeftTop
.Width(),
662 aMtfMapMode
.GetOrigin().Y() + aCropLeftTop
.Height(),
663 aMtfMapMode
.GetOrigin().X() + aSrcSize
.Width() - aCropRightBottom
.Width(),
664 aMtfMapMode
.GetOrigin().Y() + aSrcSize
.Height() - aCropRightBottom
.Height() );
666 // #104115# To correctly crop rotated metafiles, clip by view rectangle
667 aMtf
.AddAction( new MetaISectRectClipRegionAction( aClipRect
), 0 );
669 // #104115# To crop the metafile, scale larger than the output rectangle
670 aMtf
.Scale( static_cast<double>(rDestSize
.Width()) / (aSrcSize
.Width() - aCropLeftTop
.Width() - aCropRightBottom
.Width()),
671 static_cast<double>(rDestSize
.Height()) / (aSrcSize
.Height() - aCropLeftTop
.Height() - aCropRightBottom
.Height()) );
673 // #104115# Adapt the pref size by hand (scale changes it
674 // proportionally, but we want it to be smaller than the
675 // former size, to crop the excess out)
676 aMtf
.SetPrefSize( Size( static_cast<tools::Long
>(static_cast<double>(rDestSize
.Width()) * (1.0 + (aCropLeftTop
.Width() + aCropRightBottom
.Width()) / aSrcSize
.Width()) + .5),
677 static_cast<tools::Long
>(static_cast<double>(rDestSize
.Height()) * (1.0 + (aCropLeftTop
.Height() + aCropRightBottom
.Height()) / aSrcSize
.Height()) + .5) ) );
679 // #104115# Adapt the origin of the new mapmode, such that it
680 // is shifted to the place where the cropped output starts
681 Point
aNewOrigin( static_cast<tools::Long
>(static_cast<double>(aMtfMapMode
.GetOrigin().X()) + rDestSize
.Width() * aCropLeftTop
.Width() / (aSrcSize
.Width() - aCropLeftTop
.Width() - aCropRightBottom
.Width()) + .5),
682 static_cast<tools::Long
>(static_cast<double>(aMtfMapMode
.GetOrigin().Y()) + rDestSize
.Height() * aCropLeftTop
.Height() / (aSrcSize
.Height() - aCropLeftTop
.Height() - aCropRightBottom
.Height()) + .5) );
683 MapMode
aNewMap( rDestMap
);
684 aNewMap
.SetOrigin( OutputDevice::LogicToLogic(aNewOrigin
, aMtfMapMode
, rDestMap
) );
685 aMtf
.SetPrefMapMode( aNewMap
);
689 aMtf
.Scale( Fraction( rDestSize
.Width(), aSrcSize
.Width() ), Fraction( rDestSize
.Height(), aSrcSize
.Height() ) );
690 aMtf
.SetPrefMapMode( rDestMap
);
693 aTransGraphic
= aMtf
;
695 else if( GraphicType::Bitmap
== eType
)
697 BitmapEx
aBitmapEx( aTransGraphic
.GetBitmapEx() );
698 tools::Rectangle aCropRect
;
700 // convert crops to pixel
701 if(rAttr
.IsCropped())
703 if (aMapGraph
.GetMapUnit() == MapUnit::MapPixel
)
705 // crops are in 1/100th mm -> to MapUnit::MapPixel
706 aCropLeftTop
= Application::GetDefaultDevice()->LogicToPixel(
707 Size(rAttr
.GetLeftCrop(), rAttr
.GetTopCrop()),
709 aCropRightBottom
= Application::GetDefaultDevice()->LogicToPixel(
710 Size(rAttr
.GetRightCrop(), rAttr
.GetBottomCrop()),
715 // crops are in GraphicObject units -> to MapUnit::MapPixel
716 aCropLeftTop
= Application::GetDefaultDevice()->LogicToPixel(
717 Size(rAttr
.GetLeftCrop(), rAttr
.GetTopCrop()),
719 aCropRightBottom
= Application::GetDefaultDevice()->LogicToPixel(
720 Size(rAttr
.GetRightCrop(), rAttr
.GetBottomCrop()),
724 // convert from prefmapmode to pixel
726 Application::GetDefaultDevice()->LogicToPixel(
731 && (aSrcSizePixel
.Width() != aBitmapEx
.GetSizePixel().Width() || aSrcSizePixel
.Height() != aBitmapEx
.GetSizePixel().Height())
732 && aSrcSizePixel
.Width())
734 // the size in pixels calculated from Graphic's internal MapMode (aTransGraphic.GetPrefMapMode())
735 // and its internal size (aTransGraphic.GetPrefSize()) is different from its real pixel size.
736 // This can be interpreted as this values to be set wrong, but needs to be corrected since e.g.
737 // existing cropping is calculated based on this logic values already.
738 // aBitmapEx.Scale(aSrcSizePixel);
740 // another possibility is to adapt the values created so far with a factor; this
741 // will keep the original Bitmap untouched and thus quality will not change
742 // caution: convert to double first, else pretty big errors may occur
743 const double fFactorX(static_cast<double>(aBitmapEx
.GetSizePixel().Width()) / aSrcSizePixel
.Width());
744 const double fFactorY(static_cast<double>(aBitmapEx
.GetSizePixel().Height()) / aSrcSizePixel
.Height());
746 aCropLeftTop
.setWidth( basegfx::fround
<tools::Long
>(aCropLeftTop
.Width() * fFactorX
) );
747 aCropLeftTop
.setHeight( basegfx::fround
<tools::Long
>(aCropLeftTop
.Height() * fFactorY
) );
748 aCropRightBottom
.setWidth( basegfx::fround
<tools::Long
>(aCropRightBottom
.Width() * fFactorX
) );
749 aCropRightBottom
.setHeight( basegfx::fround
<tools::Long
>(aCropRightBottom
.Height() * fFactorY
) );
751 aSrcSizePixel
= aBitmapEx
.GetSizePixel();
754 // setup crop rectangle in pixel
755 aCropRect
= tools::Rectangle( aCropLeftTop
.Width(), aCropLeftTop
.Height(),
756 aSrcSizePixel
.Width() - aCropRightBottom
.Width(),
757 aSrcSizePixel
.Height() - aCropRightBottom
.Height() );
760 // #105641# Also crop animations
761 if( aTransGraphic
.IsAnimated() )
763 Animation
aAnim( aTransGraphic
.GetAnimation() );
765 for( size_t nFrame
=0; nFrame
<aAnim
.Count(); ++nFrame
)
767 AnimationFrame
aAnimationFrame( aAnim
.Get( nFrame
) );
769 if( !aCropRect
.Contains( tools::Rectangle(aAnimationFrame
.maPositionPixel
, aAnimationFrame
.maSizePixel
) ) )
771 // setup actual cropping (relative to frame position)
772 tools::Rectangle
aCropRectRel( aCropRect
);
773 aCropRectRel
.Move( -aAnimationFrame
.maPositionPixel
.X(),
774 -aAnimationFrame
.maPositionPixel
.Y() );
776 // cropping affects this frame, apply it then
777 // do _not_ apply enlargement, this is done below
778 ImplTransformBitmap( aAnimationFrame
.maBitmapEx
, rAttr
, Size(), Size(),
779 aCropRectRel
, rDestSize
, false );
781 aAnim
.Replace( aAnimationFrame
, nFrame
);
783 // else: bitmap completely within crop area,
784 // i.e. nothing is cropped away
787 // now, apply enlargement (if any) through global animation size
788 if( aCropLeftTop
.Width() < 0 ||
789 aCropLeftTop
.Height() < 0 ||
790 aCropRightBottom
.Width() < 0 ||
791 aCropRightBottom
.Height() < 0 )
793 Size
aNewSize( aAnim
.GetDisplaySizePixel() );
794 aNewSize
.AdjustWidth(aCropRightBottom
.Width() < 0 ? -aCropRightBottom
.Width() : 0 );
795 aNewSize
.AdjustWidth(aCropLeftTop
.Width() < 0 ? -aCropLeftTop
.Width() : 0 );
796 aNewSize
.AdjustHeight(aCropRightBottom
.Height() < 0 ? -aCropRightBottom
.Height() : 0 );
797 aNewSize
.AdjustHeight(aCropLeftTop
.Height() < 0 ? -aCropLeftTop
.Height() : 0 );
798 aAnim
.SetDisplaySizePixel( aNewSize
);
801 // if topleft has changed, we must move all frames to the
802 // right and bottom, resp.
803 if( aCropLeftTop
.Width() < 0 ||
804 aCropLeftTop
.Height() < 0 )
806 Point
aPosOffset( aCropLeftTop
.Width() < 0 ? -aCropLeftTop
.Width() : 0,
807 aCropLeftTop
.Height() < 0 ? -aCropLeftTop
.Height() : 0 );
809 for( size_t nFrame
=0; nFrame
<aAnim
.Count(); ++nFrame
)
811 AnimationFrame
aAnimationFrame( aAnim
.Get( nFrame
) );
813 aAnimationFrame
.maPositionPixel
+= aPosOffset
;
815 aAnim
.Replace( aAnimationFrame
, nFrame
);
819 aTransGraphic
= aAnim
;
823 ImplTransformBitmap( aBitmapEx
, rAttr
, aCropLeftTop
, aCropRightBottom
,
824 aCropRect
, rDestSize
, true );
826 aTransGraphic
= aBitmapEx
;
829 aTransGraphic
.SetPrefSize( rDestSize
);
830 aTransGraphic
.SetPrefMapMode( rDestMap
);
833 GraphicObject
aGrfObj( aTransGraphic
);
834 aTransGraphic
= aGrfObj
.GetTransformedGraphic( &rAttr
);
836 return aTransGraphic
;
839 Graphic
GraphicObject::GetTransformedGraphic( const GraphicAttr
* pAttr
) const
844 GraphicAttr
aAttr( pAttr
? *pAttr
: GetAttr() );
846 if (maGraphic
.IsSupportedGraphic())
848 if( aAttr
.IsSpecialDrawMode() || aAttr
.IsAdjusted() || aAttr
.IsMirrored() || aAttr
.IsRotated() || aAttr
.IsTransparent() )
850 if( GetType() == GraphicType::Bitmap
)
854 Animation
aAnimation( maGraphic
.GetAnimation() );
855 lclImplAdjust( aAnimation
, aAttr
, GraphicAdjustmentFlags::ALL
);
856 aAnimation
.SetLoopCount(maGraphic
.GetAnimationLoopCount());
857 aGraphic
= aAnimation
;
861 BitmapEx
aBmpEx( maGraphic
.GetBitmapEx() );
862 lclImplAdjust( aBmpEx
, aAttr
, GraphicAdjustmentFlags::ALL
);
868 GDIMetaFile
aMtf( maGraphic
.GetGDIMetaFile() );
869 lclImplAdjust( aMtf
, aAttr
, GraphicAdjustmentFlags::ALL
);
875 if( ( GetType() == GraphicType::Bitmap
) && IsAnimated() )
877 Animation
aAnimation( maGraphic
.GetAnimation() );
878 aAnimation
.SetLoopCount(maGraphic
.GetAnimationLoopCount());
879 aGraphic
= aAnimation
;
882 aGraphic
= maGraphic
;
889 bool GraphicObject::isGraphicObjectUniqueIdURL(std::u16string_view rURL
)
891 return o3tl::starts_with(rURL
, u
"vnd.sun.star.GraphicObject:");
894 // calculate scalings between real image size and logic object size. This
895 // is necessary since the crop values are relative to original bitmap size
896 basegfx::B2DVector
GraphicObject::calculateCropScaling(
902 double fBottomCrop
) const
904 const MapMode
aMapMode100thmm(MapUnit::Map100thMM
);
905 Size
aBitmapSize(GetPrefSize());
906 double fFactorX(1.0);
907 double fFactorY(1.0);
909 if(MapUnit::MapPixel
== GetPrefMapMode().GetMapUnit())
911 aBitmapSize
= Application::GetDefaultDevice()->PixelToLogic(aBitmapSize
, aMapMode100thmm
);
915 aBitmapSize
= OutputDevice::LogicToLogic(aBitmapSize
, GetPrefMapMode(), aMapMode100thmm
);
918 const double fDivX(aBitmapSize
.Width() - fLeftCrop
- fRightCrop
);
919 const double fDivY(aBitmapSize
.Height() - fTopCrop
- fBottomCrop
);
921 if(!basegfx::fTools::equalZero(fDivX
))
923 fFactorX
= fabs(fWidth
) / fDivX
;
926 if(!basegfx::fTools::equalZero(fDivY
))
928 fFactorY
= fabs(fHeight
) / fDivY
;
931 return basegfx::B2DVector(fFactorX
,fFactorY
);
935 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */