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