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