LibreOffice Module vcl (master)  1
GraphicObject.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <algorithm>
23 
24 #include <o3tl/string_view.hxx>
25 #include <osl/diagnose.h>
26 #include <tools/fract.hxx>
27 #include <tools/helpers.hxx>
28 #include <vcl/svapp.hxx>
29 #include <vcl/metaact.hxx>
30 #include <vcl/GraphicObject.hxx>
31 #include <vcl/GraphicLoader.hxx>
32 #include <vcl/outdev.hxx>
33 
34 #include <com/sun/star/container/XNameContainer.hpp>
35 #include <com/sun/star/beans/XPropertySet.hpp>
36 #include <com/sun/star/graphic/XGraphic.hpp>
37 #include <memory>
38 
39 
40 using namespace css;
42 using com::sun::star::uno::XInterface;
43 using com::sun::star::uno::UNO_QUERY;
45 using com::sun::star::container::XNameContainer;
47 
48 #define WATERMARK_LUM_OFFSET 50
49 #define WATERMARK_CON_OFFSET -70
50 
51 namespace vcl::graphic
52 {
53 
54 void SearchForGraphics(uno::Reference<uno::XInterface> const & xInterface,
55  std::vector<uno::Reference<css::graphic::XGraphic>> & raGraphicList)
56 {
57  uno::Reference<beans::XPropertySet> xPropertySet(xInterface, UNO_QUERY);
58  if (xPropertySet.is())
59  {
60  if (xPropertySet->getPropertySetInfo()->hasPropertyByName("ImageURL"))
61  {
62  OUString sURL;
63  xPropertySet->getPropertyValue("ImageURL") >>= sURL;
64  if (!sURL.isEmpty() && !GraphicObject::isGraphicObjectUniqueIdURL(sURL))
65  {
66  Graphic aGraphic = vcl::graphic::loadFromURL(sURL);
67  if (!aGraphic.IsNone())
68  {
69  raGraphicList.push_back(aGraphic.GetXGraphic());
70  }
71  }
72  } else if (xPropertySet->getPropertySetInfo()->hasPropertyByName("Graphic"))
73  {
74  uno::Reference<css::graphic::XGraphic> xGraphic;
75  xPropertySet->getPropertyValue("Graphic") >>= xGraphic;
76  if (xGraphic.is())
77  {
78  raGraphicList.push_back(xGraphic);
79  }
80  }
81  }
82  Reference<XNameContainer> xContainer(xInterface, UNO_QUERY);
83  if (xContainer.is())
84  {
85  const css::uno::Sequence<OUString> aElementNames = xContainer->getElementNames();
86  for (OUString const & rName : aElementNames)
87  {
88  uno::Reference<XInterface> xInnerInterface;
89  xContainer->getByName(rName) >>= xInnerInterface;
90  SearchForGraphics(xInnerInterface, raGraphicList);
91  }
92  }
93 }
94 
95 } // end namespace vcl::graphic
96 
97 namespace
98 {
99 
100 bool lclDrawObj(OutputDevice& rOut, const Point& rPt, const Size& rSz,
101  GraphicObject const & rObj, const GraphicAttr& rAttr)
102 {
103  Point aPt( rPt );
104  Size aSz( rSz );
105  bool bRet = false;
106 
107  if( ( rObj.GetType() == GraphicType::Bitmap ) || ( rObj.GetType() == GraphicType::GdiMetafile ) )
108  {
109  // simple output of transformed graphic
110  const Graphic aGraphic( rObj.GetTransformedGraphic( &rAttr ) );
111 
112  if( aGraphic.IsSupportedGraphic() )
113  {
114  const Degree10 nRot10 = rAttr.GetRotation() % 3600_deg10;
115 
116  if( nRot10 )
117  {
118  tools::Polygon aPoly( tools::Rectangle( aPt, aSz ) );
119 
120  aPoly.Rotate( aPt, nRot10 );
121  const tools::Rectangle aRotBoundRect( aPoly.GetBoundRect() );
122  aPt = aRotBoundRect.TopLeft();
123  aSz = aRotBoundRect.GetSize();
124  }
125 
126  aGraphic.Draw(rOut, aPt, aSz);
127  }
128 
129  bRet = true;
130  }
131 
132  return bRet;
133 }
134 
135 void lclImplAdjust( BitmapEx& rBmpEx, const GraphicAttr& rAttr, GraphicAdjustmentFlags nAdjustmentFlags )
136 {
137  GraphicAttr aAttr( rAttr );
138 
139  if( ( nAdjustmentFlags & GraphicAdjustmentFlags::DRAWMODE ) && aAttr.IsSpecialDrawMode() )
140  {
141  switch( aAttr.GetDrawMode() )
142  {
145  break;
146 
149  break;
150 
152  {
153  aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET );
154  aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET );
155  }
156  break;
157 
158  default:
159  break;
160  }
161  }
162 
163  if( ( nAdjustmentFlags & GraphicAdjustmentFlags::COLORS ) && aAttr.IsAdjusted() )
164  {
165  rBmpEx.Adjust( aAttr.GetLuminance(), aAttr.GetContrast(),
166  aAttr.GetChannelR(), aAttr.GetChannelG(), aAttr.GetChannelB(),
167  aAttr.GetGamma(), aAttr.IsInvert() );
168  }
169 
170  if( ( nAdjustmentFlags & GraphicAdjustmentFlags::MIRROR ) && aAttr.IsMirrored() )
171  {
172  rBmpEx.Mirror( aAttr.GetMirrorFlags() );
173  }
174 
175  if( ( nAdjustmentFlags & GraphicAdjustmentFlags::ROTATE ) && aAttr.IsRotated() )
176  {
177  rBmpEx.Rotate( aAttr.GetRotation(), COL_TRANSPARENT );
178  }
179 
180  if( ( nAdjustmentFlags & GraphicAdjustmentFlags::TRANSPARENCY ) && aAttr.IsTransparent() )
181  {
182  rBmpEx.AdjustTransparency(255 - aAttr.GetAlpha());
183  }
184 }
185 
186 void lclImplAdjust( GDIMetaFile& rMtf, const GraphicAttr& rAttr, GraphicAdjustmentFlags nAdjustmentFlags )
187 {
188  GraphicAttr aAttr( rAttr );
189 
190  if( ( nAdjustmentFlags & GraphicAdjustmentFlags::DRAWMODE ) && aAttr.IsSpecialDrawMode() )
191  {
192  switch( aAttr.GetDrawMode() )
193  {
196  break;
197 
200  break;
201 
203  {
204  aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET );
205  aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET );
206  }
207  break;
208 
209  default:
210  break;
211  }
212  }
213 
214  if( ( nAdjustmentFlags & GraphicAdjustmentFlags::COLORS ) && aAttr.IsAdjusted() )
215  {
216  rMtf.Adjust( aAttr.GetLuminance(), aAttr.GetContrast(),
217  aAttr.GetChannelR(), aAttr.GetChannelG(), aAttr.GetChannelB(),
218  aAttr.GetGamma(), aAttr.IsInvert() );
219  }
220 
221  if( ( nAdjustmentFlags & GraphicAdjustmentFlags::MIRROR ) && aAttr.IsMirrored() )
222  {
223  rMtf.Mirror( aAttr.GetMirrorFlags() );
224  }
225 
226  if( ( nAdjustmentFlags & GraphicAdjustmentFlags::ROTATE ) && aAttr.IsRotated() )
227  {
228  rMtf.Rotate( aAttr.GetRotation() );
229  }
230 
231  if( ( nAdjustmentFlags & GraphicAdjustmentFlags::TRANSPARENCY ) && aAttr.IsTransparent() )
232  {
233  OSL_FAIL( "Missing implementation: Mtf-Transparency" );
234  }
235 }
236 
237 void lclImplAdjust( Animation& rAnimation, const GraphicAttr& rAttr, GraphicAdjustmentFlags nAdjustmentFlags )
238 {
239  GraphicAttr aAttr( rAttr );
240 
241  if( ( nAdjustmentFlags & GraphicAdjustmentFlags::DRAWMODE ) && aAttr.IsSpecialDrawMode() )
242  {
243  switch( aAttr.GetDrawMode() )
244  {
247  break;
248 
250  rAnimation.Convert( BmpConversion::N8BitGreys );
251  break;
252 
254  {
255  aAttr.SetLuminance( aAttr.GetLuminance() + WATERMARK_LUM_OFFSET );
256  aAttr.SetContrast( aAttr.GetContrast() + WATERMARK_CON_OFFSET );
257  }
258  break;
259 
260  default:
261  break;
262  }
263  }
264 
265  if( ( nAdjustmentFlags & GraphicAdjustmentFlags::COLORS ) && aAttr.IsAdjusted() )
266  {
267  rAnimation.Adjust( aAttr.GetLuminance(), aAttr.GetContrast(),
268  aAttr.GetChannelR(), aAttr.GetChannelG(), aAttr.GetChannelB(),
269  aAttr.GetGamma(), aAttr.IsInvert() );
270  }
271 
272  if( ( nAdjustmentFlags & GraphicAdjustmentFlags::MIRROR ) && aAttr.IsMirrored() )
273  {
274  rAnimation.Mirror( aAttr.GetMirrorFlags() );
275  }
276 
277  if( ( nAdjustmentFlags & GraphicAdjustmentFlags::ROTATE ) && aAttr.IsRotated() )
278  {
279  OSL_FAIL( "Missing implementation: Animation-Rotation" );
280  }
281 
282  if( ( nAdjustmentFlags & GraphicAdjustmentFlags::TRANSPARENCY ) && aAttr.IsTransparent() )
283  {
284  OSL_FAIL( "Missing implementation: Animation-Transparency" );
285  }
286 }
287 
288 } // end anonymous namespace
289 
291 {
294 
295  GrfSimpleCacheObj( const Graphic& rGraphic, const GraphicAttr& rAttr ) :
296  maGraphic( rGraphic ), maAttr( rAttr ) {}
297 };
298 
300 {
301 }
302 
304  : maGraphic(rGraphic)
305 {
306 }
307 
309  : maGraphic(rGraphicObj.GetGraphic())
310  , maAttr(rGraphicObj.maAttr)
311  , maUserData(rGraphicObj.maUserData)
312 {
313 }
314 
316 {
317 }
318 
320 {
321  return maGraphic.GetType();
322 }
323 
325 {
326  return maGraphic.GetPrefSize();
327 }
328 
330 {
331  return maGraphic.GetPrefMapMode();
332 }
333 
335 {
336  return maGraphic.IsTransparent();
337 }
338 
340 {
341  return maGraphic.IsAnimated();
342 }
343 
345 {
346  return maGraphic.IsEPS();
347 }
348 
349 bool GraphicObject::ImplGetCropParams(const OutputDevice& rOut, Point& rPt, Size& rSz, const GraphicAttr* pAttr,
350  tools::PolyPolygon& rClipPolyPoly, bool& bRectClipRegion) const
351 {
352  bool bRet = false;
353 
354  if( GetType() != GraphicType::NONE )
355  {
356  tools::Polygon aClipPoly( tools::Rectangle( rPt, rSz ) );
357  const Degree10 nRot10 = pAttr->GetRotation() % 3600_deg10;
358  const Point aOldOrigin( rPt );
359  const MapMode aMap100( MapUnit::Map100thMM );
360  Size aSize100;
361  tools::Long nTotalWidth, nTotalHeight;
362 
363  if( nRot10 )
364  {
365  aClipPoly.Rotate( rPt, nRot10 );
366  bRectClipRegion = false;
367  }
368  else
369  bRectClipRegion = true;
370 
371  rClipPolyPoly = aClipPoly;
372 
373  if (maGraphic.GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel)
375  else
376  {
378  aSize100 = rOut.LogicToLogic( maGraphic.GetPrefSize(), &m, &aMap100 );
379  }
380 
381  nTotalWidth = aSize100.Width() - pAttr->GetLeftCrop() - pAttr->GetRightCrop();
382  nTotalHeight = aSize100.Height() - pAttr->GetTopCrop() - pAttr->GetBottomCrop();
383 
384  if( !aSize100.IsEmpty() && nTotalWidth > 0 && nTotalHeight > 0 )
385  {
386  double fScale = static_cast<double>(aSize100.Width()) / nTotalWidth;
387  const tools::Long nNewLeft = -FRound( ( ( pAttr->GetMirrorFlags() & BmpMirrorFlags::Horizontal ) ? pAttr->GetRightCrop() : pAttr->GetLeftCrop() ) * fScale );
388  const tools::Long nNewRight = nNewLeft + FRound( aSize100.Width() * fScale ) - 1;
389 
390  fScale = static_cast<double>(rSz.Width()) / aSize100.Width();
391  rPt.AdjustX(FRound( nNewLeft * fScale ) );
392  rSz.setWidth( FRound( ( nNewRight - nNewLeft + 1 ) * fScale ) );
393 
394  fScale = static_cast<double>(aSize100.Height()) / nTotalHeight;
395  const tools::Long nNewTop = -FRound( ( ( pAttr->GetMirrorFlags() & BmpMirrorFlags::Vertical ) ? pAttr->GetBottomCrop() : pAttr->GetTopCrop() ) * fScale );
396  const tools::Long nNewBottom = nNewTop + FRound( aSize100.Height() * fScale ) - 1;
397 
398  fScale = static_cast<double>(rSz.Height()) / aSize100.Height();
399  rPt.AdjustY(FRound( nNewTop * fScale ) );
400  rSz.setHeight( FRound( ( nNewBottom - nNewTop + 1 ) * fScale ) );
401 
402  if( nRot10 )
403  {
404  tools::Polygon aOriginPoly( 1 );
405 
406  aOriginPoly[ 0 ] = rPt;
407  aOriginPoly.Rotate( aOldOrigin, nRot10 );
408  rPt = aOriginPoly[ 0 ];
409  }
410 
411  bRet = true;
412  }
413  }
414 
415  return bRet;
416 }
417 
419 {
420  if( &rGraphicObj != this )
421  {
422  mxSimpleCache.reset();
423  maGraphic = rGraphicObj.GetGraphic();
424  maAttr = rGraphicObj.maAttr;
425  maUserData = rGraphicObj.maUserData;
426  }
427 
428  return *this;
429 }
430 
431 bool GraphicObject::operator==( const GraphicObject& rGraphicObj ) const
432 {
433  return rGraphicObj.maGraphic == maGraphic
434  && rGraphicObj.maAttr == maAttr;
435 }
436 
438 {
439  return GetGraphic().getUniqueID();
440 }
441 
443 {
444  maAttr = rAttr;
445 
446  if (mxSimpleCache && (mxSimpleCache->maAttr != rAttr))
447  mxSimpleCache.reset();
448 }
449 
451 {
452  maUserData.clear();
453 }
454 
455 void GraphicObject::SetUserData( const OUString& rUserData )
456 {
457  maUserData = rUserData;
458 }
459 
460 bool GraphicObject::Draw(OutputDevice& rOut, const Point& rPt, const Size& rSz,
461  const GraphicAttr* pAttr)
462 {
463  GraphicAttr aAttr( pAttr ? *pAttr : GetAttr() );
464  Point aPt( rPt );
465  Size aSz( rSz );
466  const DrawModeFlags nOldDrawMode = rOut.GetDrawMode();
467  bool bCropped = aAttr.IsCropped();
468  bool bRet;
469 
471 
472  // mirrored horizontally
473  if( aSz.Width() < 0 )
474  {
475  aPt.AdjustX(aSz.Width() + 1 );
476  aSz.setWidth( -aSz.Width() );
478  }
479 
480  // mirrored vertically
481  if( aSz.Height() < 0 )
482  {
483  aPt.AdjustY(aSz.Height() + 1 );
484  aSz.setHeight( -aSz.Height() );
486  }
487 
488  if( bCropped )
489  {
490  tools::PolyPolygon aClipPolyPoly;
491  bool bRectClip;
492  const bool bCrop = ImplGetCropParams(rOut, aPt, aSz, &aAttr, aClipPolyPoly, bRectClip);
493 
495 
496  if( bCrop )
497  {
498  if( bRectClip )
499  {
500  // #i29534# Store crop rect for later forwarding to
501  // PDF writer
502  tools::Rectangle aCropRect = aClipPolyPoly.GetBoundRect();
503  rOut.IntersectClipRegion(aCropRect);
504  }
505  else
506  {
507  rOut.IntersectClipRegion(vcl::Region(aClipPolyPoly));
508  }
509  }
510  }
511 
512  bRet = lclDrawObj(rOut, aPt, aSz, *this, aAttr);
513 
514  if( bCropped )
515  rOut.Pop();
516 
517  rOut.SetDrawMode( nOldDrawMode );
518 
519  return bRet;
520 }
521 
522 void GraphicObject::DrawTiled(OutputDevice& rOut, const tools::Rectangle& rArea, const Size& rSize,
523  const Size& rOffset, int nTileCacheSize1D)
524 {
525  if (rSize.IsEmpty())
526  return;
527 
528  const MapMode aOutMapMode(rOut.GetMapMode());
529  // #106258# Clamp size to 1 for zero values. This is okay, since
530  // logical size of zero is handled above already
531  const Size aOutTileSize( ::std::max( tools::Long(1), rOut.LogicToPixel( rSize, aOutMapMode ).Width() ),
532  ::std::max( tools::Long(1), rOut.LogicToPixel( rSize, aOutMapMode ).Height() ) );
533 
534  //#i69780 clip final tile size to a sane max size
535  while ((static_cast<sal_Int64>(rSize.Width()) * nTileCacheSize1D) > SAL_MAX_UINT16)
536  nTileCacheSize1D /= 2;
537  while ((static_cast<sal_Int64>(rSize.Height()) * nTileCacheSize1D) > SAL_MAX_UINT16)
538  nTileCacheSize1D /= 2;
539 
540  ImplDrawTiled(rOut, rArea, aOutTileSize, rOffset, nullptr, nTileCacheSize1D);
541 }
542 
543 bool GraphicObject::StartAnimation(OutputDevice& rOut, const Point& rPt, const Size& rSz,
544  tools::Long nExtraData,
545  OutputDevice* pFirstFrameOutDev)
546 {
547  bool bRet = false;
548 
549  GetGraphic();
550 
551  const GraphicAttr aAttr( GetAttr() );
552 
553  if (IsAnimated())
554  {
555  Point aPt( rPt );
556  Size aSz( rSz );
557  bool bCropped = aAttr.IsCropped();
558 
559  if( bCropped )
560  {
561  tools::PolyPolygon aClipPolyPoly;
562  bool bRectClip;
563  const bool bCrop = ImplGetCropParams(rOut, aPt, aSz, &aAttr, aClipPolyPoly, bRectClip);
564 
566 
567  if( bCrop )
568  {
569  if( bRectClip )
570  rOut.IntersectClipRegion(aClipPolyPoly.GetBoundRect());
571  else
572  rOut.IntersectClipRegion(vcl::Region(aClipPolyPoly));
573  }
574  }
575 
576  if (!mxSimpleCache || (mxSimpleCache->maAttr != aAttr) || pFirstFrameOutDev)
577  {
578  mxSimpleCache.reset(new GrfSimpleCacheObj(GetTransformedGraphic(&aAttr), aAttr));
579  mxSimpleCache->maGraphic.SetAnimationNotifyHdl(GetGraphic().GetAnimationNotifyHdl());
580  }
581 
582  mxSimpleCache->maGraphic.StartAnimation(rOut, aPt, aSz, nExtraData, pFirstFrameOutDev);
583 
584  if( bCropped )
585  rOut.Pop();
586 
587  bRet = true;
588  }
589  else
590  bRet = Draw(rOut, rPt, rSz, &aAttr);
591 
592  return bRet;
593 }
594 
596 {
597  if (mxSimpleCache)
598  mxSimpleCache->maGraphic.StopAnimation(pOut, nExtraData);
599 }
600 
602 {
603  return maGraphic;
604 }
605 
606 void GraphicObject::SetGraphic( const Graphic& rGraphic)
607 {
608  maGraphic = rGraphic;
609 }
610 
611 Graphic GraphicObject::GetTransformedGraphic( const Size& rDestSize, const MapMode& rDestMap, const GraphicAttr& rAttr ) const
612 {
613  // #104550# Extracted from svx/source/svdraw/svdograf.cxx
614  Graphic aTransGraphic( GetGraphic() );
615  const GraphicType eType = GetType();
616  const Size aSrcSize( aTransGraphic.GetPrefSize() );
617 
618  // #104115# Convert the crop margins to graphic object mapmode
619  const MapMode aMapGraph( aTransGraphic.GetPrefMapMode() );
620  const MapMode aMap100( MapUnit::Map100thMM );
621 
622  Size aCropLeftTop;
623  Size aCropRightBottom;
624 
625  if( GraphicType::GdiMetafile == eType )
626  {
627  GDIMetaFile aMtf( aTransGraphic.GetGDIMetaFile() );
628 
629  if (aMapGraph.GetMapUnit() == MapUnit::MapPixel)
630  {
631  // crops are in 1/100th mm -> to aMapGraph -> to MapUnit::MapPixel
633  Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()),
634  aMap100);
635  aCropRightBottom = Application::GetDefaultDevice()->LogicToPixel(
636  Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()),
637  aMap100);
638  }
639  else
640  {
641  // crops are in GraphicObject units -> to aMapGraph
642  aCropLeftTop = OutputDevice::LogicToLogic(
643  Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()),
644  aMap100,
645  aMapGraph);
646  aCropRightBottom = OutputDevice::LogicToLogic(
647  Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()),
648  aMap100,
649  aMapGraph);
650  }
651 
652  // #104115# If the metafile is cropped, give it a special
653  // treatment: clip against the remaining area, scale up such
654  // that this area later fills the desired size, and move the
655  // origin to the upper left edge of that area.
656  if( rAttr.IsCropped() )
657  {
658  const MapMode aMtfMapMode( aMtf.GetPrefMapMode() );
659 
660  tools::Rectangle aClipRect( aMtfMapMode.GetOrigin().X() + aCropLeftTop.Width(),
661  aMtfMapMode.GetOrigin().Y() + aCropLeftTop.Height(),
662  aMtfMapMode.GetOrigin().X() + aSrcSize.Width() - aCropRightBottom.Width(),
663  aMtfMapMode.GetOrigin().Y() + aSrcSize.Height() - aCropRightBottom.Height() );
664 
665  // #104115# To correctly crop rotated metafiles, clip by view rectangle
666  aMtf.AddAction( new MetaISectRectClipRegionAction( aClipRect ), 0 );
667 
668  // #104115# To crop the metafile, scale larger than the output rectangle
669  aMtf.Scale( static_cast<double>(rDestSize.Width()) / (aSrcSize.Width() - aCropLeftTop.Width() - aCropRightBottom.Width()),
670  static_cast<double>(rDestSize.Height()) / (aSrcSize.Height() - aCropLeftTop.Height() - aCropRightBottom.Height()) );
671 
672  // #104115# Adapt the pref size by hand (scale changes it
673  // proportionally, but we want it to be smaller than the
674  // former size, to crop the excess out)
675  aMtf.SetPrefSize( Size( static_cast<tools::Long>(static_cast<double>(rDestSize.Width()) * (1.0 + (aCropLeftTop.Width() + aCropRightBottom.Width()) / aSrcSize.Width()) + .5),
676  static_cast<tools::Long>(static_cast<double>(rDestSize.Height()) * (1.0 + (aCropLeftTop.Height() + aCropRightBottom.Height()) / aSrcSize.Height()) + .5) ) );
677 
678  // #104115# Adapt the origin of the new mapmode, such that it
679  // is shifted to the place where the cropped output starts
680  Point aNewOrigin( static_cast<tools::Long>(static_cast<double>(aMtfMapMode.GetOrigin().X()) + rDestSize.Width() * aCropLeftTop.Width() / (aSrcSize.Width() - aCropLeftTop.Width() - aCropRightBottom.Width()) + .5),
681  static_cast<tools::Long>(static_cast<double>(aMtfMapMode.GetOrigin().Y()) + rDestSize.Height() * aCropLeftTop.Height() / (aSrcSize.Height() - aCropLeftTop.Height() - aCropRightBottom.Height()) + .5) );
682  MapMode aNewMap( rDestMap );
683  aNewMap.SetOrigin( OutputDevice::LogicToLogic(aNewOrigin, aMtfMapMode, rDestMap) );
684  aMtf.SetPrefMapMode( aNewMap );
685  }
686  else
687  {
688  aMtf.Scale( Fraction( rDestSize.Width(), aSrcSize.Width() ), Fraction( rDestSize.Height(), aSrcSize.Height() ) );
689  aMtf.SetPrefMapMode( rDestMap );
690  }
691 
692  aTransGraphic = aMtf;
693  }
694  else if( GraphicType::Bitmap == eType )
695  {
696  BitmapEx aBitmapEx( aTransGraphic.GetBitmapEx() );
697  tools::Rectangle aCropRect;
698 
699  // convert crops to pixel
700  if(rAttr.IsCropped())
701  {
702  if (aMapGraph.GetMapUnit() == MapUnit::MapPixel)
703  {
704  // crops are in 1/100th mm -> to MapUnit::MapPixel
706  Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()),
707  aMap100);
708  aCropRightBottom = Application::GetDefaultDevice()->LogicToPixel(
709  Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()),
710  aMap100);
711  }
712  else
713  {
714  // crops are in GraphicObject units -> to MapUnit::MapPixel
716  Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()),
717  aMapGraph);
718  aCropRightBottom = Application::GetDefaultDevice()->LogicToPixel(
719  Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()),
720  aMapGraph);
721  }
722 
723  // convert from prefmapmode to pixel
724  Size aSrcSizePixel(
725  Application::GetDefaultDevice()->LogicToPixel(
726  aSrcSize,
727  aMapGraph));
728 
729  if(rAttr.IsCropped()
730  && (aSrcSizePixel.Width() != aBitmapEx.GetSizePixel().Width() || aSrcSizePixel.Height() != aBitmapEx.GetSizePixel().Height())
731  && aSrcSizePixel.Width())
732  {
733  // the size in pixels calculated from Graphic's internal MapMode (aTransGraphic.GetPrefMapMode())
734  // and its internal size (aTransGraphic.GetPrefSize()) is different from its real pixel size.
735  // This can be interpreted as this values to be set wrong, but needs to be corrected since e.g.
736  // existing cropping is calculated based on this logic values already.
737  // aBitmapEx.Scale(aSrcSizePixel);
738 
739  // another possibility is to adapt the values created so far with a factor; this
740  // will keep the original Bitmap untouched and thus quality will not change
741  // caution: convert to double first, else pretty big errors may occur
742  const double fFactorX(static_cast<double>(aBitmapEx.GetSizePixel().Width()) / aSrcSizePixel.Width());
743  const double fFactorY(static_cast<double>(aBitmapEx.GetSizePixel().Height()) / aSrcSizePixel.Height());
744 
745  aCropLeftTop.setWidth( basegfx::fround(aCropLeftTop.Width() * fFactorX) );
746  aCropLeftTop.setHeight( basegfx::fround(aCropLeftTop.Height() * fFactorY) );
747  aCropRightBottom.setWidth( basegfx::fround(aCropRightBottom.Width() * fFactorX) );
748  aCropRightBottom.setHeight( basegfx::fround(aCropRightBottom.Height() * fFactorY) );
749 
750  aSrcSizePixel = aBitmapEx.GetSizePixel();
751  }
752 
753  // setup crop rectangle in pixel
754  aCropRect = tools::Rectangle( aCropLeftTop.Width(), aCropLeftTop.Height(),
755  aSrcSizePixel.Width() - aCropRightBottom.Width(),
756  aSrcSizePixel.Height() - aCropRightBottom.Height() );
757  }
758 
759  // #105641# Also crop animations
760  if( aTransGraphic.IsAnimated() )
761  {
762  Animation aAnim( aTransGraphic.GetAnimation() );
763 
764  for( size_t nFrame=0; nFrame<aAnim.Count(); ++nFrame )
765  {
766  AnimationBitmap aAnimationBitmap( aAnim.Get( nFrame ) );
767 
768  if( !aCropRect.Contains( tools::Rectangle(aAnimationBitmap.maPositionPixel, aAnimationBitmap.maSizePixel) ) )
769  {
770  // setup actual cropping (relative to frame position)
771  tools::Rectangle aCropRectRel( aCropRect );
772  aCropRectRel.Move( -aAnimationBitmap.maPositionPixel.X(),
773  -aAnimationBitmap.maPositionPixel.Y() );
774 
775  // cropping affects this frame, apply it then
776  // do _not_ apply enlargement, this is done below
777  ImplTransformBitmap( aAnimationBitmap.maBitmapEx, rAttr, Size(), Size(),
778  aCropRectRel, rDestSize, false );
779 
780  aAnim.Replace( aAnimationBitmap, nFrame );
781  }
782  // else: bitmap completely within crop area,
783  // i.e. nothing is cropped away
784  }
785 
786  // now, apply enlargement (if any) through global animation size
787  if( aCropLeftTop.Width() < 0 ||
788  aCropLeftTop.Height() < 0 ||
789  aCropRightBottom.Width() < 0 ||
790  aCropRightBottom.Height() < 0 )
791  {
792  Size aNewSize( aAnim.GetDisplaySizePixel() );
793  aNewSize.AdjustWidth(aCropRightBottom.Width() < 0 ? -aCropRightBottom.Width() : 0 );
794  aNewSize.AdjustWidth(aCropLeftTop.Width() < 0 ? -aCropLeftTop.Width() : 0 );
795  aNewSize.AdjustHeight(aCropRightBottom.Height() < 0 ? -aCropRightBottom.Height() : 0 );
796  aNewSize.AdjustHeight(aCropLeftTop.Height() < 0 ? -aCropLeftTop.Height() : 0 );
797  aAnim.SetDisplaySizePixel( aNewSize );
798  }
799 
800  // if topleft has changed, we must move all frames to the
801  // right and bottom, resp.
802  if( aCropLeftTop.Width() < 0 ||
803  aCropLeftTop.Height() < 0 )
804  {
805  Point aPosOffset( aCropLeftTop.Width() < 0 ? -aCropLeftTop.Width() : 0,
806  aCropLeftTop.Height() < 0 ? -aCropLeftTop.Height() : 0 );
807 
808  for( size_t nFrame=0; nFrame<aAnim.Count(); ++nFrame )
809  {
810  AnimationBitmap aAnimationBitmap( aAnim.Get( nFrame ) );
811 
812  aAnimationBitmap.maPositionPixel += aPosOffset;
813 
814  aAnim.Replace( aAnimationBitmap, nFrame );
815  }
816  }
817 
818  aTransGraphic = aAnim;
819  }
820  else
821  {
822  ImplTransformBitmap( aBitmapEx, rAttr, aCropLeftTop, aCropRightBottom,
823  aCropRect, rDestSize, true );
824 
825  aTransGraphic = aBitmapEx;
826  }
827 
828  aTransGraphic.SetPrefSize( rDestSize );
829  aTransGraphic.SetPrefMapMode( rDestMap );
830  }
831 
832  GraphicObject aGrfObj( aTransGraphic );
833  aTransGraphic = aGrfObj.GetTransformedGraphic( &rAttr );
834 
835  return aTransGraphic;
836 }
837 
839 {
840  GetGraphic();
841 
842  Graphic aGraphic;
843  GraphicAttr aAttr( pAttr ? *pAttr : GetAttr() );
844 
846  {
847  if( aAttr.IsSpecialDrawMode() || aAttr.IsAdjusted() || aAttr.IsMirrored() || aAttr.IsRotated() || aAttr.IsTransparent() )
848  {
849  if( GetType() == GraphicType::Bitmap )
850  {
851  if( IsAnimated() )
852  {
853  Animation aAnimation( maGraphic.GetAnimation() );
854  lclImplAdjust( aAnimation, aAttr, GraphicAdjustmentFlags::ALL );
855  aAnimation.SetLoopCount(maGraphic.GetAnimationLoopCount());
856  aGraphic = aAnimation;
857  }
858  else
859  {
860  BitmapEx aBmpEx( maGraphic.GetBitmapEx() );
861  lclImplAdjust( aBmpEx, aAttr, GraphicAdjustmentFlags::ALL );
862  aGraphic = aBmpEx;
863  }
864  }
865  else
866  {
868  lclImplAdjust( aMtf, aAttr, GraphicAdjustmentFlags::ALL );
869  aGraphic = aMtf;
870  }
871  }
872  else
873  {
874  if( ( GetType() == GraphicType::Bitmap ) && IsAnimated() )
875  {
876  Animation aAnimation( maGraphic.GetAnimation() );
878  aGraphic = aAnimation;
879  }
880  else
881  aGraphic = maGraphic;
882  }
883  }
884 
885  return aGraphic;
886 }
887 
888 bool GraphicObject::isGraphicObjectUniqueIdURL(std::u16string_view rURL)
889 {
890  return o3tl::starts_with(rURL, u"vnd.sun.star.GraphicObject:");
891 }
892 
893 // calculate scalings between real image size and logic object size. This
894 // is necessary since the crop values are relative to original bitmap size
896  double fWidth,
897  double fHeight,
898  double fLeftCrop,
899  double fTopCrop,
900  double fRightCrop,
901  double fBottomCrop) const
902 {
903  const MapMode aMapMode100thmm(MapUnit::Map100thMM);
904  Size aBitmapSize(GetPrefSize());
905  double fFactorX(1.0);
906  double fFactorY(1.0);
907 
908  if(MapUnit::MapPixel == GetPrefMapMode().GetMapUnit())
909  {
910  aBitmapSize = Application::GetDefaultDevice()->PixelToLogic(aBitmapSize, aMapMode100thmm);
911  }
912  else
913  {
914  aBitmapSize = OutputDevice::LogicToLogic(aBitmapSize, GetPrefMapMode(), aMapMode100thmm);
915  }
916 
917  const double fDivX(aBitmapSize.Width() - fLeftCrop - fRightCrop);
918  const double fDivY(aBitmapSize.Height() - fTopCrop - fBottomCrop);
919 
920  if(!basegfx::fTools::equalZero(fDivX))
921  {
922  fFactorX = fabs(fWidth) / fDivX;
923  }
924 
925  if(!basegfx::fTools::equalZero(fDivY))
926  {
927  fFactorY = fabs(fHeight) / fDivY;
928  }
929 
930  return basegfx::B2DVector(fFactorX,fFactorY);
931 }
932 
933 
934 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
tools::Long GetBottomCrop() const
void Mirror(BmpMirrorFlags nMirrorFlags)
Definition: gdimtf.cxx:594
void SetLoopCount(const sal_uInt32 nLoopCount)
Definition: Animation.cxx:420
BmpMirrorFlags GetMirrorFlags() const
void setWidth(tools::Long nWidth)
bool Adjust(short nLuminancePercent, short nContrastPercent, short nChannelRPercent, short nChannelGPercent, short nChannelBPercent, double fGamma=1.0, bool bInvert=false, bool msoBrightness=false)
Change various global color characteristics.
Definition: BitmapEx.cxx:491
SAL_WARN_UNUSED_RESULT Point LogicToLogic(const Point &rPtSource, const MapMode *pMapModeSource, const MapMode *pMapModeDest) const
Definition: map.cxx:1562
bool IsNone() const
Definition: graph.cxx:282
bool equalZero(const T &rfVal)
long Long
void SetPrefMapMode(const MapMode &rPrefMapMode)
Definition: graph.cxx:379
constexpr::Color COL_TRANSPARENT(ColorTransparency, 0xFF, 0xFF, 0xFF, 0xFF)
GraphicType
Definition: graph.hxx:34
bool IsSupportedGraphic() const
Definition: graph.cxx:304
bool IsMirrored() const
void SetMirrorFlags(BmpMirrorFlags nMirrFlags)
const MapMode & GetMapMode() const
Definition: outdev.hxx:1552
void Rotate(Degree10 nAngle10)
Definition: gdimtf.cxx:818
void IntersectClipRegion(const tools::Rectangle &rRect)
tools::Long GetTopCrop() const
bool IsTransparent() const
Definition: graph.cxx:309
GraphicAttr maAttr
#define WATERMARK_CON_OFFSET
Graphic loadFromURL(OUString const &rURL, weld::Window *pParentWin)
bool Convert(BmpConversion eConversion)
Convert bitmap format.
Definition: BitmapEx.cxx:374
Graphic GetTransformedGraphic(const Size &rDestSize, const MapMode &rDestMap, const GraphicAttr &rAttr) const
Get graphic transformed according to given attributes.
css::uno::Reference< css::graphic::XGraphic > GetXGraphic() const
Definition: graph.cxx:349
bool operator==(const GraphicObject &rCacheObj) const
constexpr bool starts_with(std::basic_string_view< charT, traits > sv, std::basic_string_view< charT, traits > x) noexcept
void SetDrawMode(DrawModeFlags nDrawMode)
Definition: outdev.cxx:360
static OutputDevice * GetDefaultDevice()
Get the default "device" (in this case the default window).
Definition: svapp.cxx:1069
constexpr tools::Long Width() const
OString GetUniqueID() const
#define SAL_MAX_UINT16
void Adjust(short nLuminancePercent, short nContrastPercent, short nChannelRPercent=0, short nChannelGPercent=0, short nChannelBPercent=0, double fGamma=1.0, bool bInvert=false, bool msoBrightness=false)
Definition: gdimtf.cxx:2068
const GDIMetaFile & GetGDIMetaFile() const
Definition: graph.cxx:339
void Pop()
Definition: stack.cxx:93
Size GetPrefSize() const
void StopAnimation(const OutputDevice *pOut=nullptr, tools::Long nExtraData=0)
bool IsTransparent() const
Sequence< NamedValue > maUserData
B2IRange fround(const B2DRange &rRange)
bool IsAnimated() const
Definition: graph.cxx:319
MapMode GetPrefMapMode() const
void Mirror(BmpMirrorFlags nMirrorFlags)
Definition: Animation.cxx:496
void Rotate(const Point &rCenter, double fSin, double fCos)
Animation GetAnimation() const
Definition: graph.cxx:334
bool Mirror(BmpMirrorFlags nMirrorFlags)
Mirror the bitmap.
Definition: BitmapEx.cxx:259
DocumentType eType
void AdjustTransparency(sal_uInt8 cTrans)
Definition: BitmapEx.cxx:1390
static bool isGraphicObjectUniqueIdURL(std::u16string_view rURL)
bool Draw(OutputDevice &rOut, const Point &rPt, const Size &rSz, const GraphicAttr *pAttr=nullptr)
bool IsCropped() const
bool IsEmpty() const
bool VCL_DLLPRIVATE ImplDrawTiled(OutputDevice &rOut, const tools::Rectangle &rArea, const Size &rSizePixel, const Size &rOffset, const GraphicAttr *pAttr, int nTileCacheSize1D)
bool IsSpecialDrawMode() const
GraphicType GetType() const
Definition: graph.cxx:293
bool StartAnimation(OutputDevice &rOut, const Point &rPt, const Size &rSz, tools::Long nExtraData=0, OutputDevice *pFirstFrameOutDev=nullptr)
class SAL_NO_VTABLE XPropertySet
void Convert(MtfConversion eConversion)
Definition: gdimtf.cxx:2149
tools::Long FRound(double fVal)
const GraphicAttr & GetAttr() const
bool IsRotated() const
tools::Long GetRightCrop() const
Some things multiple-inherit from VclAbstractDialog and OutputDevice, so we need to use virtual inher...
Definition: outdev.hxx:168
float u
GraphicAdjustmentFlags
#define WATERMARK_LUM_OFFSET
bool IsAdjusted() const
GrfSimpleCacheObj(const Graphic &rGraphic, const GraphicAttr &rAttr)
void DrawTiled(OutputDevice &rOut, const tools::Rectangle &rArea, const Size &rSize, const Size &rOffset, int nTileCacheSize1D=128)
Draw the graphic repeatedly into the given output rectangle.
void SetAttr(const GraphicAttr &rAttr)
MapUnit GetMapUnit() const
Definition: mapmod.cxx:138
BitmapEx GetBitmapEx(const GraphicConversionParameters &rParameters=GraphicConversionParameters()) const
Definition: graph.cxx:329
DrawModeFlags GetDrawMode() const
Definition: outdev.hxx:484
OUString maUserData
bool Rotate(Degree10 nAngle10, const Color &rFillColor)
Rotate bitmap by the specified angle.
Definition: BitmapEx.cxx:316
GraphicType GetType() const
constexpr Point TopLeft() const
MapUnit GetMapUnit()
SAL_WARN_UNUSED_RESULT Point PixelToLogic(const Point &rDevicePt) const
Definition: map.cxx:1107
SAL_WARN_UNUSED_RESULT Point LogicToPixel(const Point &rLogicPt) const
Definition: map.cxx:876
Size GetPrefSize() const
Definition: graph.cxx:363
const Graphic & GetGraphic() const
bool IsEPS() const
MapMode GetPrefMapMode() const
Definition: graph.cxx:374
constexpr tools::Long Height() const
sal_uInt32 GetAnimationLoopCount() const
Definition: graph.cxx:473
void SetGraphic(const Graphic &rGraphic)
OString getUniqueID() const
Definition: graph.cxx:551
tools::Long GetLeftCrop() const
tools::Long AdjustWidth(tools::Long n)
std::unique_ptr< GrfSimpleCacheObj > mxSimpleCache
void SetPrefSize(const Size &rPrefSize)
Definition: graph.cxx:368
GraphicObject & operator=(const GraphicObject &rCacheObj)
void setHeight(tools::Long nHeight)
void SearchForGraphics(uno::Reference< uno::XInterface > const &xInterface, std::vector< uno::Reference< css::graphic::XGraphic >> &raGraphicList)
void Move(tools::Long nHorzMoveDelta, tools::Long nVertMoveDelta)
tools::Rectangle GetBoundRect() const
void VCL_DLLPRIVATE ImplTransformBitmap(BitmapEx &rBmpEx, const GraphicAttr &rAttr, const Size &rCropLeftTop, const Size &rCropRightBottom, const tools::Rectangle &rCropRect, const Size &rDstSize, bool bEnlarge) const
basegfx::B2DVector calculateCropScaling(double fWidth, double fHeight, double fLeftCrop, double fTopCrop, double fRightCrop, double fBottomCrop) const
bool IsAnimated() const
tuple m
void Push(PushFlags nFlags=PushFlags::ALL)
Definition: stack.cxx:34
void Convert(BmpConversion eConversion)
Definition: Animation.cxx:432
void Adjust(short nLuminancePercent, short nContrastPercent, short nChannelRPercent, short nChannelGPercent, short nChannelBPercent, double fGamma=1.0, bool bInvert=false)
Definition: Animation.cxx:529
bool IsTransparent() const
DrawModeFlags
const Graphic maGraphic
Degree10 GetRotation() const
bool VCL_DLLPRIVATE ImplGetCropParams(const OutputDevice &rOut, Point &rPt, Size &rSz, const GraphicAttr *pAttr, tools::PolyPolygon &rClipPolyPoly, bool &bRectClipRegion) const
bool IsEPS() const
Definition: graph.cxx:324