sw a11y: clang-format SidebarWinAccessible code
[LibreOffice.git] / vcl / source / graphic / GraphicObject.cxx
blobdccd0c959fbf16aa9b03b0e3dc0aad3b86ea1a44
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
22 #include <algorithm>
24 #include <o3tl/string_view.hxx>
25 #include <osl/diagnose.h>
26 #include <tools/fract.hxx>
27 #include <tools/helpers.hxx>
28 #include <utility>
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>
38 #include <memory>
41 using namespace css;
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))
63 OUString sURL;
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;
77 if (xGraphic.is())
79 raGraphicList.push_back(xGraphic);
83 Reference<XNameContainer> xContainer(xInterface, UNO_QUERY);
84 if (xContainer.is())
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
98 namespace
101 bool lclDrawObj(OutputDevice& rOut, const Point& rPt, const Size& rSz,
102 GraphicObject const & rObj, const GraphicAttr& rAttr)
104 Point aPt( rPt );
105 Size aSz( rSz );
106 bool bRet = false;
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;
117 if( nRot10 )
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);
130 bRet = true;
133 return bRet;
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 );
146 break;
148 case GraphicDrawMode::Greys:
149 rBmpEx.Convert( BmpConversion::N8BitGreys );
150 break;
152 case GraphicDrawMode::Watermark:
154 aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET );
155 aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET );
157 break;
159 default:
160 break;
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 );
197 break;
199 case GraphicDrawMode::Greys:
200 rMtf.Convert( MtfConversion::N8BitGreys );
201 break;
203 case GraphicDrawMode::Watermark:
205 aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET );
206 aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET );
208 break;
210 default:
211 break;
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 );
248 break;
250 case GraphicDrawMode::Greys:
251 rAnimation.Convert( BmpConversion::N8BitGreys );
252 break;
254 case GraphicDrawMode::Watermark:
256 aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET );
257 aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET );
259 break;
261 default:
262 break;
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
293 Graphic maGraphic;
294 GraphicAttr maAttr;
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
353 bool bRet = false;
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 );
361 Size aSize100;
362 tools::Long nTotalWidth, nTotalHeight;
364 if( nRot10 )
366 aClipPoly.Rotate( rPt, nRot10 );
367 bRectClipRegion = false;
369 else
370 bRectClipRegion = true;
372 rClipPolyPoly = tools::PolyPolygon(aClipPoly);
374 if (maGraphic.GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel)
375 aSize100 = Application::GetDefaultDevice()->PixelToLogic( maGraphic.GetPrefSize(), aMap100 );
376 else
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));
403 if( nRot10 )
405 tools::Polygon aOriginPoly( 1 );
407 aOriginPoly[ 0 ] = rPt;
408 aOriginPoly.Rotate( aOldOrigin, nRot10 );
409 rPt = aOriginPoly[ 0 ];
412 bRet = true;
416 return bRet;
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;
429 return *this;
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 )
445 maAttr = rAttr;
447 if (mxSimpleCache && (mxSimpleCache->maAttr != rAttr))
448 mxSimpleCache.reset();
451 void GraphicObject::SetUserData()
453 maUserData.clear();
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() );
465 Point aPt( rPt );
466 Size aSz( rSz );
467 const DrawModeFlags nOldDrawMode = rOut.GetDrawMode();
468 bool bCropped = aAttr.IsCropped();
469 bool bRet;
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 );
489 if( bCropped )
491 tools::PolyPolygon aClipPolyPoly;
492 bool bRectClip;
493 const bool bCrop = ImplGetCropParams(rOut, aPt, aSz, &aAttr, aClipPolyPoly, bRectClip);
495 rOut.Push(vcl::PushFlags::CLIPREGION);
497 if( bCrop )
499 if( bRectClip )
501 // #i29534# Store crop rect for later forwarding to
502 // PDF writer
503 tools::Rectangle aCropRect = aClipPolyPoly.GetBoundRect();
504 rOut.IntersectClipRegion(aCropRect);
506 else
508 rOut.IntersectClipRegion(vcl::Region(aClipPolyPoly));
513 bRet = lclDrawObj(rOut, aPt, aSz, *this, aAttr);
515 if( bCropped )
516 rOut.Pop();
518 rOut.SetDrawMode( nOldDrawMode );
520 return bRet;
523 void GraphicObject::DrawTiled(OutputDevice& rOut, const tools::Rectangle& rArea, const Size& rSize,
524 const Size& rOffset, int nTileCacheSize1D)
526 if (rSize.IsEmpty())
527 return;
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)
548 bool bRet = false;
550 GetGraphic();
552 const GraphicAttr aAttr( GetAttr() );
554 if (IsAnimated())
556 Point aPt( rPt );
557 Size aSz( rSz );
558 bool bCropped = aAttr.IsCropped();
560 if( bCropped )
562 tools::PolyPolygon aClipPolyPoly;
563 bool bRectClip;
564 const bool bCrop = ImplGetCropParams(rOut, aPt, aSz, &aAttr, aClipPolyPoly, bRectClip);
566 rOut.Push(vcl::PushFlags::CLIPREGION);
568 if( bCrop )
570 if( bRectClip )
571 rOut.IntersectClipRegion(aClipPolyPoly.GetBoundRect());
572 else
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);
585 if( bCropped )
586 rOut.Pop();
588 bRet = true;
590 else
591 bRet = Draw(rOut, rPt, rSz, &aAttr);
593 return bRet;
596 void GraphicObject::StopAnimation( const OutputDevice* pOut, tools::Long nRendererId )
598 if (mxSimpleCache)
599 mxSimpleCache->maGraphic.StopAnimation(pOut, nRendererId);
602 const Graphic& GraphicObject::GetGraphic() const
604 return maGraphic;
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 );
623 Size aCropLeftTop;
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()),
635 aMap100);
636 aCropRightBottom = Application::GetDefaultDevice()->LogicToPixel(
637 Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()),
638 aMap100);
640 else
642 // crops are in GraphicObject units -> to aMapGraph
643 aCropLeftTop = OutputDevice::LogicToLogic(
644 Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()),
645 aMap100,
646 aMapGraph);
647 aCropRightBottom = OutputDevice::LogicToLogic(
648 Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()),
649 aMap100,
650 aMapGraph);
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 );
687 else
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()),
708 aMap100);
709 aCropRightBottom = Application::GetDefaultDevice()->LogicToPixel(
710 Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()),
711 aMap100);
713 else
715 // crops are in GraphicObject units -> to MapUnit::MapPixel
716 aCropLeftTop = Application::GetDefaultDevice()->LogicToPixel(
717 Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()),
718 aMapGraph);
719 aCropRightBottom = Application::GetDefaultDevice()->LogicToPixel(
720 Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()),
721 aMapGraph);
724 // convert from prefmapmode to pixel
725 Size aSrcSizePixel(
726 Application::GetDefaultDevice()->LogicToPixel(
727 aSrcSize,
728 aMapGraph));
730 if(rAttr.IsCropped()
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;
821 else
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
841 GetGraphic();
843 Graphic aGraphic;
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 )
852 if( IsAnimated() )
854 Animation aAnimation( maGraphic.GetAnimation() );
855 lclImplAdjust( aAnimation, aAttr, GraphicAdjustmentFlags::ALL );
856 aAnimation.SetLoopCount(maGraphic.GetAnimationLoopCount());
857 aGraphic = aAnimation;
859 else
861 BitmapEx aBmpEx( maGraphic.GetBitmapEx() );
862 lclImplAdjust( aBmpEx, aAttr, GraphicAdjustmentFlags::ALL );
863 aGraphic = aBmpEx;
866 else
868 GDIMetaFile aMtf( maGraphic.GetGDIMetaFile() );
869 lclImplAdjust( aMtf, aAttr, GraphicAdjustmentFlags::ALL );
870 aGraphic = aMtf;
873 else
875 if( ( GetType() == GraphicType::Bitmap ) && IsAnimated() )
877 Animation aAnimation( maGraphic.GetAnimation() );
878 aAnimation.SetLoopCount(maGraphic.GetAnimationLoopCount());
879 aGraphic = aAnimation;
881 else
882 aGraphic = maGraphic;
886 return aGraphic;
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(
897 double fWidth,
898 double fHeight,
899 double fLeftCrop,
900 double fTopCrop,
901 double fRightCrop,
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);
913 else
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: */