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 sal_uInt16 nRot10 = rAttr.GetRotation() % 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 sal_uInt16 nRot10 = pAttr->GetRotation() % 3600;
357  const Point aOldOrigin( rPt );
358  const MapMode aMap100( MapUnit::Map100thMM );
359  Size aSize100;
360  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.Width() > 0 && aSize100.Height() > 0 && nTotalWidth > 0 && nTotalHeight > 0 )
384  {
385  double fScale = static_cast<double>(aSize100.Width()) / nTotalWidth;
386  const long nNewLeft = -FRound( ( ( pAttr->GetMirrorFlags() & BmpMirrorFlags::Horizontal ) ? pAttr->GetRightCrop() : pAttr->GetLeftCrop() ) * fScale );
387  const 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 long nNewTop = -FRound( ( ( pAttr->GetMirrorFlags() & BmpMirrorFlags::Vertical ) ? pAttr->GetBottomCrop() : pAttr->GetTopCrop() ) * fScale );
395  const 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 
469  // #i29534# Provide output rects for PDF writer
470  tools::Rectangle aCropRect;
471 
473 
474  // mirrored horizontically
475  if( aSz.Width() < 0 )
476  {
477  aPt.AdjustX(aSz.Width() + 1 );
478  aSz.setWidth( -aSz.Width() );
480  }
481 
482  // mirrored vertically
483  if( aSz.Height() < 0 )
484  {
485  aPt.AdjustY(aSz.Height() + 1 );
486  aSz.setHeight( -aSz.Height() );
488  }
489 
490  if( bCropped )
491  {
492  tools::PolyPolygon aClipPolyPoly;
493  bool bRectClip;
494  const bool bCrop = ImplGetCropParams( pOut, aPt, aSz, &aAttr, aClipPolyPoly, bRectClip );
495 
496  pOut->Push( PushFlags::CLIPREGION );
497 
498  if( bCrop )
499  {
500  if( bRectClip )
501  {
502  // #i29534# Store crop rect for later forwarding to
503  // PDF writer
504  aCropRect = aClipPolyPoly.GetBoundRect();
505  pOut->IntersectClipRegion( aCropRect );
506  }
507  else
508  {
509  pOut->IntersectClipRegion(vcl::Region(aClipPolyPoly));
510  }
511  }
512  }
513 
514  bRet = lclDrawObj(pOut, aPt, aSz, *this, aAttr);
515 
516  if( bCropped )
517  pOut->Pop();
518 
519  pOut->SetDrawMode( nOldDrawMode );
520 
521  return bRet;
522 }
523 
524 void GraphicObject::DrawTiled( OutputDevice* pOut, const tools::Rectangle& rArea, const Size& rSize,
525  const Size& rOffset, int nTileCacheSize1D )
526 {
527  if( pOut == nullptr || rSize.Width() == 0 || rSize.Height() == 0 )
528  return;
529 
530  const MapMode aOutMapMode( pOut->GetMapMode() );
531  // #106258# Clamp size to 1 for zero values. This is okay, since
532  // logical size of zero is handled above already
533  const Size aOutTileSize( ::std::max( 1L, pOut->LogicToPixel( rSize, aOutMapMode ).Width() ),
534  ::std::max( 1L, pOut->LogicToPixel( rSize, aOutMapMode ).Height() ) );
535 
536  //#i69780 clip final tile size to a sane max size
537  while ((static_cast<sal_Int64>(rSize.Width()) * nTileCacheSize1D) > SAL_MAX_UINT16)
538  nTileCacheSize1D /= 2;
539  while ((static_cast<sal_Int64>(rSize.Height()) * nTileCacheSize1D) > SAL_MAX_UINT16)
540  nTileCacheSize1D /= 2;
541 
542  ImplDrawTiled( pOut, rArea, aOutTileSize, rOffset, nullptr, nTileCacheSize1D );
543 }
544 
545 bool GraphicObject::StartAnimation( OutputDevice* pOut, const Point& rPt, const Size& rSz,
546  long nExtraData,
547  OutputDevice* pFirstFrameOutDev )
548 {
549  bool bRet = false;
550 
551  GetGraphic();
552 
553  const GraphicAttr aAttr( GetAttr() );
554 
555  if (IsAnimated())
556  {
557  Point aPt( rPt );
558  Size aSz( rSz );
559  bool bCropped = aAttr.IsCropped();
560 
561  if( bCropped )
562  {
563  tools::PolyPolygon aClipPolyPoly;
564  bool bRectClip;
565  const bool bCrop = ImplGetCropParams( pOut, aPt, aSz, &aAttr, aClipPolyPoly, bRectClip );
566 
567  pOut->Push( PushFlags::CLIPREGION );
568 
569  if( bCrop )
570  {
571  if( bRectClip )
572  pOut->IntersectClipRegion( aClipPolyPoly.GetBoundRect() );
573  else
574  pOut->IntersectClipRegion(vcl::Region(aClipPolyPoly));
575  }
576  }
577 
578  if (!mxSimpleCache || (mxSimpleCache->maAttr != aAttr) || pFirstFrameOutDev)
579  {
580  mxSimpleCache.reset(new GrfSimpleCacheObj(GetTransformedGraphic(&aAttr), aAttr));
581  mxSimpleCache->maGraphic.SetAnimationNotifyHdl(GetGraphic().GetAnimationNotifyHdl());
582  }
583 
584  mxSimpleCache->maGraphic.StartAnimation(pOut, aPt, aSz, nExtraData, pFirstFrameOutDev);
585 
586  if( bCropped )
587  pOut->Pop();
588 
589  bRet = true;
590  }
591  else
592  bRet = Draw( pOut, rPt, rSz, &aAttr );
593 
594  return bRet;
595 }
596 
597 void GraphicObject::StopAnimation( OutputDevice* pOut, long nExtraData )
598 {
599  if (mxSimpleCache)
600  mxSimpleCache->maGraphic.StopAnimation(pOut, nExtraData);
601 }
602 
604 {
605  return maGraphic;
606 }
607 
608 void GraphicObject::SetGraphic( const Graphic& rGraphic, const GraphicObject* /*pCopyObj*/)
609 {
610  maGraphic = rGraphic;
611 }
612 
613 void GraphicObject::SetGraphic( const Graphic& rGraphic, const OUString& /*rLink*/ )
614 {
615  SetGraphic( rGraphic );
616 }
617 
618 Graphic GraphicObject::GetTransformedGraphic( const Size& rDestSize, const MapMode& rDestMap, const GraphicAttr& rAttr ) const
619 {
620  // #104550# Extracted from svx/source/svdraw/svdograf.cxx
621  Graphic aTransGraphic( GetGraphic() );
622  const GraphicType eType = GetType();
623  const Size aSrcSize( aTransGraphic.GetPrefSize() );
624 
625  // #104115# Convert the crop margins to graphic object mapmode
626  const MapMode aMapGraph( aTransGraphic.GetPrefMapMode() );
627  const MapMode aMap100( MapUnit::Map100thMM );
628 
629  Size aCropLeftTop;
630  Size aCropRightBottom;
631 
632  if( GraphicType::GdiMetafile == eType )
633  {
634  GDIMetaFile aMtf( aTransGraphic.GetGDIMetaFile() );
635 
636  if (aMapGraph.GetMapUnit() == MapUnit::MapPixel)
637  {
638  // crops are in 1/100th mm -> to aMapGraph -> to MapUnit::MapPixel
640  Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()),
641  aMap100);
642  aCropRightBottom = Application::GetDefaultDevice()->LogicToPixel(
643  Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()),
644  aMap100);
645  }
646  else
647  {
648  // crops are in GraphicObject units -> to aMapGraph
649  aCropLeftTop = OutputDevice::LogicToLogic(
650  Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()),
651  aMap100,
652  aMapGraph);
653  aCropRightBottom = OutputDevice::LogicToLogic(
654  Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()),
655  aMap100,
656  aMapGraph);
657  }
658 
659  // #104115# If the metafile is cropped, give it a special
660  // treatment: clip against the remaining area, scale up such
661  // that this area later fills the desired size, and move the
662  // origin to the upper left edge of that area.
663  if( rAttr.IsCropped() )
664  {
665  const MapMode aMtfMapMode( aMtf.GetPrefMapMode() );
666 
667  tools::Rectangle aClipRect( aMtfMapMode.GetOrigin().X() + aCropLeftTop.Width(),
668  aMtfMapMode.GetOrigin().Y() + aCropLeftTop.Height(),
669  aMtfMapMode.GetOrigin().X() + aSrcSize.Width() - aCropRightBottom.Width(),
670  aMtfMapMode.GetOrigin().Y() + aSrcSize.Height() - aCropRightBottom.Height() );
671 
672  // #104115# To correctly crop rotated metafiles, clip by view rectangle
673  aMtf.AddAction( new MetaISectRectClipRegionAction( aClipRect ), 0 );
674 
675  // #104115# To crop the metafile, scale larger than the output rectangle
676  aMtf.Scale( static_cast<double>(rDestSize.Width()) / (aSrcSize.Width() - aCropLeftTop.Width() - aCropRightBottom.Width()),
677  static_cast<double>(rDestSize.Height()) / (aSrcSize.Height() - aCropLeftTop.Height() - aCropRightBottom.Height()) );
678 
679  // #104115# Adapt the pref size by hand (scale changes it
680  // proportionally, but we want it to be smaller than the
681  // former size, to crop the excess out)
682  aMtf.SetPrefSize( Size( static_cast<long>(static_cast<double>(rDestSize.Width()) * (1.0 + (aCropLeftTop.Width() + aCropRightBottom.Width()) / aSrcSize.Width()) + .5),
683  static_cast<long>(static_cast<double>(rDestSize.Height()) * (1.0 + (aCropLeftTop.Height() + aCropRightBottom.Height()) / aSrcSize.Height()) + .5) ) );
684 
685  // #104115# Adapt the origin of the new mapmode, such that it
686  // is shifted to the place where the cropped output starts
687  Point aNewOrigin( static_cast<long>(static_cast<double>(aMtfMapMode.GetOrigin().X()) + rDestSize.Width() * aCropLeftTop.Width() / (aSrcSize.Width() - aCropLeftTop.Width() - aCropRightBottom.Width()) + .5),
688  static_cast<long>(static_cast<double>(aMtfMapMode.GetOrigin().Y()) + rDestSize.Height() * aCropLeftTop.Height() / (aSrcSize.Height() - aCropLeftTop.Height() - aCropRightBottom.Height()) + .5) );
689  MapMode aNewMap( rDestMap );
690  aNewMap.SetOrigin( OutputDevice::LogicToLogic(aNewOrigin, aMtfMapMode, rDestMap) );
691  aMtf.SetPrefMapMode( aNewMap );
692  }
693  else
694  {
695  aMtf.Scale( Fraction( rDestSize.Width(), aSrcSize.Width() ), Fraction( rDestSize.Height(), aSrcSize.Height() ) );
696  aMtf.SetPrefMapMode( rDestMap );
697  }
698 
699  aTransGraphic = aMtf;
700  }
701  else if( GraphicType::Bitmap == eType )
702  {
703  BitmapEx aBitmapEx( aTransGraphic.GetBitmapEx() );
704  tools::Rectangle aCropRect;
705 
706  // convert crops to pixel
707  if(rAttr.IsCropped())
708  {
709  if (aMapGraph.GetMapUnit() == MapUnit::MapPixel)
710  {
711  // crops are in 1/100th mm -> to MapUnit::MapPixel
713  Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()),
714  aMap100);
715  aCropRightBottom = Application::GetDefaultDevice()->LogicToPixel(
716  Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()),
717  aMap100);
718  }
719  else
720  {
721  // crops are in GraphicObject units -> to MapUnit::MapPixel
723  Size(rAttr.GetLeftCrop(), rAttr.GetTopCrop()),
724  aMapGraph);
725  aCropRightBottom = Application::GetDefaultDevice()->LogicToPixel(
726  Size(rAttr.GetRightCrop(), rAttr.GetBottomCrop()),
727  aMapGraph);
728  }
729 
730  // convert from prefmapmode to pixel
731  Size aSrcSizePixel(
732  Application::GetDefaultDevice()->LogicToPixel(
733  aSrcSize,
734  aMapGraph));
735 
736  if(rAttr.IsCropped()
737  && (aSrcSizePixel.Width() != aBitmapEx.GetSizePixel().Width() || aSrcSizePixel.Height() != aBitmapEx.GetSizePixel().Height())
738  && aSrcSizePixel.Width())
739  {
740  // the size in pixels calculated from Graphic's internal MapMode (aTransGraphic.GetPrefMapMode())
741  // and its internal size (aTransGraphic.GetPrefSize()) is different from its real pixel size.
742  // This can be interpreted as this values to be set wrong, but needs to be corrected since e.g.
743  // existing cropping is calculated based on this logic values already.
744  // aBitmapEx.Scale(aSrcSizePixel);
745 
746  // another possibility is to adapt the values created so far with a factor; this
747  // will keep the original Bitmap untouched and thus quality will not change
748  // caution: convert to double first, else pretty big errors may occur
749  const double fFactorX(static_cast<double>(aBitmapEx.GetSizePixel().Width()) / aSrcSizePixel.Width());
750  const double fFactorY(static_cast<double>(aBitmapEx.GetSizePixel().Height()) / aSrcSizePixel.Height());
751 
752  aCropLeftTop.setWidth( basegfx::fround(aCropLeftTop.Width() * fFactorX) );
753  aCropLeftTop.setHeight( basegfx::fround(aCropLeftTop.Height() * fFactorY) );
754  aCropRightBottom.setWidth( basegfx::fround(aCropRightBottom.Width() * fFactorX) );
755  aCropRightBottom.setHeight( basegfx::fround(aCropRightBottom.Height() * fFactorY) );
756 
757  aSrcSizePixel = aBitmapEx.GetSizePixel();
758  }
759 
760  // setup crop rectangle in pixel
761  aCropRect = tools::Rectangle( aCropLeftTop.Width(), aCropLeftTop.Height(),
762  aSrcSizePixel.Width() - aCropRightBottom.Width(),
763  aSrcSizePixel.Height() - aCropRightBottom.Height() );
764  }
765 
766  // #105641# Also crop animations
767  if( aTransGraphic.IsAnimated() )
768  {
769  Animation aAnim( aTransGraphic.GetAnimation() );
770 
771  for( size_t nFrame=0; nFrame<aAnim.Count(); ++nFrame )
772  {
773  AnimationBitmap aAnimationBitmap( aAnim.Get( nFrame ) );
774 
775  if( !aCropRect.IsInside( tools::Rectangle(aAnimationBitmap.maPositionPixel, aAnimationBitmap.maSizePixel) ) )
776  {
777  // setup actual cropping (relative to frame position)
778  tools::Rectangle aCropRectRel( aCropRect );
779  aCropRectRel.Move( -aAnimationBitmap.maPositionPixel.X(),
780  -aAnimationBitmap.maPositionPixel.Y() );
781 
782  // cropping affects this frame, apply it then
783  // do _not_ apply enlargement, this is done below
784  ImplTransformBitmap( aAnimationBitmap.maBitmapEx, rAttr, Size(), Size(),
785  aCropRectRel, rDestSize, false );
786 
787  aAnim.Replace( aAnimationBitmap, nFrame );
788  }
789  // else: bitmap completely within crop area,
790  // i.e. nothing is cropped away
791  }
792 
793  // now, apply enlargement (if any) through global animation size
794  if( aCropLeftTop.Width() < 0 ||
795  aCropLeftTop.Height() < 0 ||
796  aCropRightBottom.Width() < 0 ||
797  aCropRightBottom.Height() < 0 )
798  {
799  Size aNewSize( aAnim.GetDisplaySizePixel() );
800  aNewSize.AdjustWidth(aCropRightBottom.Width() < 0 ? -aCropRightBottom.Width() : 0 );
801  aNewSize.AdjustWidth(aCropLeftTop.Width() < 0 ? -aCropLeftTop.Width() : 0 );
802  aNewSize.AdjustHeight(aCropRightBottom.Height() < 0 ? -aCropRightBottom.Height() : 0 );
803  aNewSize.AdjustHeight(aCropLeftTop.Height() < 0 ? -aCropLeftTop.Height() : 0 );
804  aAnim.SetDisplaySizePixel( aNewSize );
805  }
806 
807  // if topleft has changed, we must move all frames to the
808  // right and bottom, resp.
809  if( aCropLeftTop.Width() < 0 ||
810  aCropLeftTop.Height() < 0 )
811  {
812  Point aPosOffset( aCropLeftTop.Width() < 0 ? -aCropLeftTop.Width() : 0,
813  aCropLeftTop.Height() < 0 ? -aCropLeftTop.Height() : 0 );
814 
815  for( size_t nFrame=0; nFrame<aAnim.Count(); ++nFrame )
816  {
817  AnimationBitmap aAnimationBitmap( aAnim.Get( nFrame ) );
818 
819  aAnimationBitmap.maPositionPixel += aPosOffset;
820 
821  aAnim.Replace( aAnimationBitmap, nFrame );
822  }
823  }
824 
825  aTransGraphic = aAnim;
826  }
827  else
828  {
829  ImplTransformBitmap( aBitmapEx, rAttr, aCropLeftTop, aCropRightBottom,
830  aCropRect, rDestSize, true );
831 
832  aTransGraphic = aBitmapEx;
833  }
834 
835  aTransGraphic.SetPrefSize( rDestSize );
836  aTransGraphic.SetPrefMapMode( rDestMap );
837  }
838 
839  GraphicObject aGrfObj( aTransGraphic );
840  aTransGraphic = aGrfObj.GetTransformedGraphic( &rAttr );
841 
842  return aTransGraphic;
843 }
844 
846 {
847  GetGraphic();
848 
849  Graphic aGraphic;
850  GraphicAttr aAttr( pAttr ? *pAttr : GetAttr() );
851 
853  {
854  if( aAttr.IsSpecialDrawMode() || aAttr.IsAdjusted() || aAttr.IsMirrored() || aAttr.IsRotated() || aAttr.IsTransparent() )
855  {
856  if( GetType() == GraphicType::Bitmap )
857  {
858  if( IsAnimated() )
859  {
860  Animation aAnimation( maGraphic.GetAnimation() );
861  lclImplAdjust( aAnimation, aAttr, GraphicAdjustmentFlags::ALL );
862  aAnimation.SetLoopCount(maGraphic.GetAnimationLoopCount());
863  aGraphic = aAnimation;
864  }
865  else
866  {
867  BitmapEx aBmpEx( maGraphic.GetBitmapEx() );
868  lclImplAdjust( aBmpEx, aAttr, GraphicAdjustmentFlags::ALL );
869  aGraphic = aBmpEx;
870  }
871  }
872  else
873  {
875  lclImplAdjust( aMtf, aAttr, GraphicAdjustmentFlags::ALL );
876  aGraphic = aMtf;
877  }
878  }
879  else
880  {
881  if( ( GetType() == GraphicType::Bitmap ) && IsAnimated() )
882  {
883  Animation aAnimation( maGraphic.GetAnimation() );
885  aGraphic = aAnimation;
886  }
887  else
888  aGraphic = maGraphic;
889  }
890  }
891 
892  return aGraphic;
893 }
894 
895 bool GraphicObject::isGraphicObjectUniqueIdURL(OUString const & rURL)
896 {
897  const OUString aPrefix("vnd.sun.star.GraphicObject:");
898  return rURL.startsWith(aPrefix);
899 }
900 
901 // calculate scalings between real image size and logic object size. This
902 // is necessary since the crop values are relative to original bitmap size
904  double fWidth,
905  double fHeight,
906  double fLeftCrop,
907  double fTopCrop,
908  double fRightCrop,
909  double fBottomCrop) const
910 {
911  const MapMode aMapMode100thmm(MapUnit::Map100thMM);
912  Size aBitmapSize(GetPrefSize());
913  double fFactorX(1.0);
914  double fFactorY(1.0);
915 
916  if(MapUnit::MapPixel == GetPrefMapMode().GetMapUnit())
917  {
918  aBitmapSize = Application::GetDefaultDevice()->PixelToLogic(aBitmapSize, aMapMode100thmm);
919  }
920  else
921  {
922  aBitmapSize = OutputDevice::LogicToLogic(aBitmapSize, GetPrefMapMode(), aMapMode100thmm);
923  }
924 
925  const double fDivX(aBitmapSize.Width() - fLeftCrop - fRightCrop);
926  const double fDivY(aBitmapSize.Height() - fTopCrop - fBottomCrop);
927 
928  if(!basegfx::fTools::equalZero(fDivX))
929  {
930  fFactorX = fabs(fWidth) / fDivX;
931  }
932 
933  if(!basegfx::fTools::equalZero(fDivY))
934  {
935  fFactorY = fabs(fHeight) / fDivY;
936  }
937 
938  return basegfx::B2DVector(fFactorX,fFactorY);
939 }
940 
941 
942 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Point TopLeft() const
long Width() const
void Mirror(BmpMirrorFlags nMirrorFlags)
Definition: gdimtf.cxx:575
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
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
Point LogicToLogic(const Point &rPtSource, const MapMode *pMapModeSource, const MapMode *pMapModeDest) const
Definition: map.cxx:1668
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:35
bool IsSupportedGraphic() const
Definition: graph.cxx:324
bool IsMirrored() const
void SetMirrorFlags(BmpMirrorFlags nMirrFlags)
const MapMode & GetMapMode() const
Definition: outdev.hxx:1681
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:1059
constexpr::Color COL_TRANSPARENT(0xFF, 0xFF, 0xFF, 0xFF)
void Rotate(long nAngle10)
Definition: gdimtf.cxx:799
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:2052
const GDIMetaFile & GetGDIMetaFile() const
Definition: graph.cxx:359
Size GetPrefSize() const
bool IsTransparent() const
Sequence< NamedValue > maUserData
B2IRange fround(const B2DRange &rRange)
bool IsAnimated() const
Definition: graph.cxx:339
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:354
bool Mirror(BmpMirrorFlags nMirrorFlags)
Mirror the bitmap.
Definition: bitmapex.cxx:333
DocumentType eType
void AdjustTransparency(sal_uInt8 cTrans)
Definition: bitmapex.cxx:1565
long GetLeftCrop() const
bool IsCropped() const
bool IsSpecialDrawMode() const
GraphicType GetType() const
Definition: graph.cxx:313
class SAL_NO_VTABLE XPropertySet
void Convert(MtfConversion eConversion)
Definition: gdimtf.cxx:2133
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:601
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:1179
Point LogicToPixel(const Point &rLogicPt) const
Definition: map.cxx:934
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:585
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
tuple m
void Push(PushFlags nFlags=PushFlags::ALL)
Definition: outdevstate.cxx:60
void setWidth(long nWidth)
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:531
bool IsTransparent() const
Reference< XGraphic > xGraphic
const Graphic maGraphic
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