LibreOffice Module vcl (master)  1
bitmapex.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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 <config_features.h>
21 
22 #include <rtl/math.hxx>
23 #include <comphelper/lok.hxx>
25 
26 #include <vcl/canvastools.hxx>
27 #include <vcl/metaact.hxx>
28 #include <vcl/virdev.hxx>
29 
30 #include <drawmode.hxx>
31 #include <salgdi.hxx>
32 
33 void OutputDevice::DrawBitmapEx( const Point& rDestPt,
34  const BitmapEx& rBitmapEx )
35 {
36  assert(!is_double_buffered_window());
37 
38  if( ImplIsRecordLayout() )
39  return;
40 
41  if( !rBitmapEx.IsAlpha() )
42  {
43  DrawBitmap( rDestPt, rBitmapEx.GetBitmap() );
44  }
45  else
46  {
47  const Size aSizePix( rBitmapEx.GetSizePixel() );
48  DrawBitmapEx( rDestPt, PixelToLogic( aSizePix ), Point(), aSizePix, rBitmapEx, MetaActionType::BMPEX );
49  }
50 }
51 
52 void OutputDevice::DrawBitmapEx( const Point& rDestPt, const Size& rDestSize,
53  const BitmapEx& rBitmapEx )
54 {
55  assert(!is_double_buffered_window());
56 
57  if( ImplIsRecordLayout() )
58  return;
59 
60  if ( !rBitmapEx.IsAlpha() )
61  {
62  DrawBitmap( rDestPt, rDestSize, rBitmapEx.GetBitmap() );
63  }
64  else
65  {
66  DrawBitmapEx( rDestPt, rDestSize, Point(), rBitmapEx.GetSizePixel(), rBitmapEx, MetaActionType::BMPEXSCALE );
67  }
68 }
69 
70 void OutputDevice::DrawBitmapEx( const Point& rDestPt, const Size& rDestSize,
71  const Point& rSrcPtPixel, const Size& rSrcSizePixel,
72  const BitmapEx& rBitmapEx)
73 {
74  assert(!is_double_buffered_window());
75 
76  if( ImplIsRecordLayout() )
77  return;
78 
79  if ( !rBitmapEx.IsAlpha() )
80  {
81  DrawBitmap( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, rBitmapEx.GetBitmap() );
82  }
83  else
84  {
85  DrawBitmapEx( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, rBitmapEx, MetaActionType::BMPEXSCALEPART );
86  }
87 }
88 
89 void OutputDevice::DrawBitmapEx( const Point& rDestPt, const Size& rDestSize,
90  const Point& rSrcPtPixel, const Size& rSrcSizePixel,
91  const BitmapEx& rBitmapEx, const MetaActionType nAction )
92 {
93  assert(!is_double_buffered_window());
94 
95  if( ImplIsRecordLayout() )
96  return;
97 
98  if( !rBitmapEx.IsAlpha() )
99  {
100  DrawBitmap( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, rBitmapEx.GetBitmap() );
101  }
102  else
103  {
104  if ( RasterOp::Invert == meRasterOp )
105  {
106  DrawRect( tools::Rectangle( rDestPt, rDestSize ) );
107  return;
108  }
109 
110  BitmapEx aBmpEx(vcl::drawmode::GetBitmapEx(rBitmapEx, GetDrawMode()));
111 
112  if ( mpMetaFile )
113  {
114  switch( nAction )
115  {
117  mpMetaFile->AddAction( new MetaBmpExAction( rDestPt, aBmpEx ) );
118  break;
119 
121  mpMetaFile->AddAction( new MetaBmpExScaleAction( rDestPt, rDestSize, aBmpEx ) );
122  break;
123 
125  mpMetaFile->AddAction( new MetaBmpExScalePartAction( rDestPt, rDestSize,
126  rSrcPtPixel, rSrcSizePixel, aBmpEx ) );
127  break;
128 
129  default: break;
130  }
131  }
132 
133  if ( !IsDeviceOutputNecessary() )
134  return;
135 
136  if ( !mpGraphics && !AcquireGraphics() )
137  return;
138 
139  if ( mbInitClipRegion )
140  InitClipRegion();
141 
142  if ( mbOutputClipped )
143  return;
144 
145  DrawDeviceBitmapEx( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, aBmpEx );
146  }
147 }
148 
149 BitmapEx OutputDevice::GetBitmapEx( const Point& rSrcPt, const Size& rSize ) const
150 {
151 
152  // #110958# Extract alpha value from VDev, if any
153  if( mpAlphaVDev )
154  {
155  Bitmap aAlphaBitmap( mpAlphaVDev->GetBitmap( rSrcPt, rSize ) );
156 
157  // ensure 8 bit alpha
158  if (aAlphaBitmap.getPixelFormat() > vcl::PixelFormat::N8_BPP)
160 
161  return BitmapEx(GetBitmap( rSrcPt, rSize ), AlphaMask( aAlphaBitmap ) );
162  }
163  else
164  return BitmapEx(GetBitmap( rSrcPt, rSize ));
165 }
166 
167 void OutputDevice::DrawDeviceBitmapEx( const Point& rDestPt, const Size& rDestSize,
168  const Point& rSrcPtPixel, const Size& rSrcSizePixel,
169  BitmapEx& rBitmapEx )
170 {
171  assert(!is_double_buffered_window());
172 
173  if (rBitmapEx.IsAlpha())
174  {
175  DrawDeviceAlphaBitmap(rBitmapEx.GetBitmap(), rBitmapEx.GetAlpha(), rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel);
176  }
177  else if (!rBitmapEx.IsEmpty())
178  {
179  SalTwoRect aPosAry(rSrcPtPixel.X(), rSrcPtPixel.Y(), rSrcSizePixel.Width(), rSrcSizePixel.Height(),
180  ImplLogicXToDevicePixel(rDestPt.X()), ImplLogicYToDevicePixel(rDestPt.Y()),
181  ImplLogicWidthToDevicePixel(rDestSize.Width()),
182  ImplLogicHeightToDevicePixel(rDestSize.Height()));
183 
184  const BmpMirrorFlags nMirrFlags = AdjustTwoRect(aPosAry, rBitmapEx.GetSizePixel());
185 
186  if (aPosAry.mnSrcWidth && aPosAry.mnSrcHeight && aPosAry.mnDestWidth && aPosAry.mnDestHeight)
187  {
188 
189  if (nMirrFlags != BmpMirrorFlags::NONE)
190  rBitmapEx.Mirror(nMirrFlags);
191 
192  const SalBitmap* pSalSrcBmp = rBitmapEx.ImplGetBitmapSalBitmap().get();
193  std::shared_ptr<SalBitmap> xMaskBmp = rBitmapEx.maAlphaMask.ImplGetSalBitmap();
194 
195  if (xMaskBmp)
196  {
197  bool bTryDirectPaint(pSalSrcBmp);
198 
199  if (bTryDirectPaint && mpGraphics->DrawAlphaBitmap(aPosAry, *pSalSrcBmp, *xMaskBmp, *this))
200  {
201  // tried to paint as alpha directly. If this worked, we are done (except
202  // alpha, see below)
203  }
204  else
205  {
206  // #4919452# reduce operation area to bounds of
207  // cliprect. since masked transparency involves
208  // creation of a large vdev and copying the screen
209  // content into that (slooow read from framebuffer),
210  // that should considerably increase performance for
211  // large bitmaps and small clippings.
212 
213  // Note that this optimization is a workaround for a
214  // Writer peculiarity, namely, to decompose background
215  // graphics into myriads of disjunct, tiny
216  // rectangles. That otherwise kills us here, since for
217  // transparent output, SAL always prepares the whole
218  // bitmap, if aPosAry contains the whole bitmap (and
219  // it's _not_ to blame for that).
220 
221  // Note the call to ImplPixelToDevicePixel(), since
222  // aPosAry already contains the mnOutOff-offsets, they
223  // also have to be applied to the region
224  tools::Rectangle aClipRegionBounds( ImplPixelToDevicePixel(maRegion).GetBoundRect() );
225 
226  // TODO: Also respect scaling (that's a bit tricky,
227  // since the source points have to move fractional
228  // amounts (which is not possible, thus has to be
229  // emulated by increases copy area)
230  // const double nScaleX( aPosAry.mnDestWidth / aPosAry.mnSrcWidth );
231  // const double nScaleY( aPosAry.mnDestHeight / aPosAry.mnSrcHeight );
232 
233  // for now, only identity scales allowed
234  if (!aClipRegionBounds.IsEmpty() &&
235  aPosAry.mnDestWidth == aPosAry.mnSrcWidth &&
236  aPosAry.mnDestHeight == aPosAry.mnSrcHeight)
237  {
238  // now intersect dest rect with clip region
239  aClipRegionBounds.Intersection(tools::Rectangle(aPosAry.mnDestX,
240  aPosAry.mnDestY,
241  aPosAry.mnDestX + aPosAry.mnDestWidth - 1,
242  aPosAry.mnDestY + aPosAry.mnDestHeight - 1));
243 
244  // Note: I could theoretically optimize away the
245  // DrawBitmap below, if the region is empty
246  // here. Unfortunately, cannot rule out that
247  // somebody relies on the side effects.
248  if (!aClipRegionBounds.IsEmpty())
249  {
250  aPosAry.mnSrcX += aClipRegionBounds.Left() - aPosAry.mnDestX;
251  aPosAry.mnSrcY += aClipRegionBounds.Top() - aPosAry.mnDestY;
252  aPosAry.mnSrcWidth = aClipRegionBounds.GetWidth();
253  aPosAry.mnSrcHeight = aClipRegionBounds.GetHeight();
254 
255  aPosAry.mnDestX = aClipRegionBounds.Left();
256  aPosAry.mnDestY = aClipRegionBounds.Top();
257  aPosAry.mnDestWidth = aClipRegionBounds.GetWidth();
258  aPosAry.mnDestHeight = aClipRegionBounds.GetHeight();
259  }
260  }
261 
262  mpGraphics->DrawBitmap(aPosAry, *pSalSrcBmp, *xMaskBmp, *this);
263  }
264 
265  // #110958# Paint mask to alpha channel. Luckily, the
266  // black and white representation of the mask maps to
267  // the alpha channel
268 
269  // #i25167# Restrict mask painting to _opaque_ areas
270  // of the mask, otherwise we spoil areas where no
271  // bitmap content was ever visible. Interestingly
272  // enough, this can be achieved by taking the mask as
273  // the transparency mask of itself
274  if (mpAlphaVDev)
275  mpAlphaVDev->DrawBitmapEx(rDestPt,
276  rDestSize,
277  BitmapEx(rBitmapEx.GetAlpha(),
278  rBitmapEx.GetAlpha()));
279  }
280  else
281  {
282  mpGraphics->DrawBitmap(aPosAry, *pSalSrcBmp, *this);
283 
284  if (mpAlphaVDev)
285  {
286  // #i32109#: Make bitmap area opaque
287  mpAlphaVDev->ImplFillOpaqueRectangle( tools::Rectangle(rDestPt, rDestSize) );
288  }
289  }
290  }
291  }
292 }
293 
295  const basegfx::B2DHomMatrix& aFullTransform,
296  const BitmapEx& rBitmapEx,
297  double fAlpha)
298 {
299  assert(!is_double_buffered_window());
300 
301  bool bDone = false;
302 
303  // try to paint directly
304  const basegfx::B2DPoint aNull(aFullTransform * basegfx::B2DPoint(0.0, 0.0));
305  const basegfx::B2DPoint aTopX(aFullTransform * basegfx::B2DPoint(1.0, 0.0));
306  const basegfx::B2DPoint aTopY(aFullTransform * basegfx::B2DPoint(0.0, 1.0));
307  SalBitmap* pSalSrcBmp = rBitmapEx.GetBitmap().ImplGetSalBitmap().get();
308  Bitmap aAlphaBitmap;
309 
310  if(rBitmapEx.IsAlpha())
311  {
312  aAlphaBitmap = rBitmapEx.GetAlpha();
313  }
314  else if (mpAlphaVDev)
315  {
316  aAlphaBitmap = AlphaMask(rBitmapEx.GetSizePixel());
317  aAlphaBitmap.Erase(COL_BLACK); // opaque
318  }
319 
320  SalBitmap* pSalAlphaBmp = aAlphaBitmap.ImplGetSalBitmap().get();
321 
323  aNull,
324  aTopX,
325  aTopY,
326  *pSalSrcBmp,
327  pSalAlphaBmp,
328  fAlpha,
329  *this);
330 
331  if (mpAlphaVDev)
332  {
333  // Merge bitmap alpha to alpha device
334  AlphaMask aAlpha(rBitmapEx.GetSizePixel());
335  aAlpha.Erase( ( 1 - fAlpha ) * 255 );
336  mpAlphaVDev->DrawTransformBitmapExDirect(aFullTransform, BitmapEx(aAlpha, aAlphaBitmap));
337  }
338 
339  return bDone;
340 };
341 
343  const basegfx::B2DHomMatrix& aFullTransform,
344  basegfx::B2DRange &aVisibleRange,
345  double &fMaximumArea)
346 {
347  // limit TargetRange to existing pixels (if pixel device)
348  // first get discrete range of object
349  basegfx::B2DRange aFullPixelRange(aVisibleRange);
350 
351  aFullPixelRange.transform(aFullTransform);
352 
353  if(basegfx::fTools::equalZero(aFullPixelRange.getWidth()) || basegfx::fTools::equalZero(aFullPixelRange.getHeight()))
354  {
355  // object is outside of visible area
356  return false;
357  }
358 
359  // now get discrete target pixels; start with OutDev pixel size and evtl.
360  // intersect with active clipping area
361  basegfx::B2DRange aOutPixel(
362  0.0,
363  0.0,
366 
367  if(IsClipRegion())
368  {
369  tools::Rectangle aRegionRectangle(GetActiveClipRegion().GetBoundRect());
370 
371  // caution! Range from rectangle, one too much (!)
372  aRegionRectangle.AdjustRight(-1);
373  aRegionRectangle.AdjustBottom(-1);
374  aOutPixel.intersect( vcl::unotools::b2DRectangleFromRectangle(aRegionRectangle) );
375  }
376 
377  if(aOutPixel.isEmpty())
378  {
379  // no active output area
380  return false;
381  }
382 
383  // if aFullPixelRange is not completely inside of aOutPixel,
384  // reduction of target pixels is possible
385  basegfx::B2DRange aVisiblePixelRange(aFullPixelRange);
386 
387  if(!aOutPixel.isInside(aFullPixelRange))
388  {
389  aVisiblePixelRange.intersect(aOutPixel);
390 
391  if(aVisiblePixelRange.isEmpty())
392  {
393  // nothing in visible part, reduces to nothing
394  return false;
395  }
396 
397  // aVisiblePixelRange contains the reduced output area in
398  // discrete coordinates. To make it useful everywhere, make it relative to
399  // the object range
400  basegfx::B2DHomMatrix aMakeVisibleRangeRelative;
401 
402  aVisibleRange = aVisiblePixelRange;
403  aMakeVisibleRangeRelative.translate(
404  -aFullPixelRange.getMinX(),
405  -aFullPixelRange.getMinY());
406  aMakeVisibleRangeRelative.scale(
407  1.0 / aFullPixelRange.getWidth(),
408  1.0 / aFullPixelRange.getHeight());
409  aVisibleRange.transform(aMakeVisibleRangeRelative);
410  }
411 
412  // for pixel devices, do *not* limit size, else OutputDevice::DrawDeviceAlphaBitmap
413  // will create another, badly scaled bitmap to do the job. Nonetheless, do a
414  // maximum clipping of something big (1600x1280x2). Add 1.0 to avoid rounding
415  // errors in rough estimations
416  const double fNewMaxArea(aVisiblePixelRange.getWidth() * aVisiblePixelRange.getHeight());
417 
418  fMaximumArea = std::min(4096000.0, fNewMaxArea + 1.0);
419 
420  return true;
421 }
422 
423 // MM02 add some test class to get a simple timer-based output to be able
424 // to check if it gets faster - and how much. Uncomment next line or set
425 // DO_TIME_TEST for compile time if you want to use it
426 // #define DO_TIME_TEST
427 #ifdef DO_TIME_TEST
428 #include <tools/time.hxx>
429 struct LocalTimeTest
430 {
431  const sal_uInt64 nStartTime;
432  LocalTimeTest() : nStartTime(tools::Time::GetSystemTicks()) {}
433  ~LocalTimeTest()
434  {
435  const sal_uInt64 nEndTime(tools::Time::GetSystemTicks());
436  const sal_uInt64 nDiffTime(nEndTime - nStartTime);
437 
438  if(nDiffTime > 0)
439  {
440  OStringBuffer aOutput("Time: ");
441  OString aNumber(OString::number(nDiffTime));
442  aOutput.append(aNumber);
443  OSL_FAIL(aOutput.getStr());
444  }
445  }
446 };
447 #endif
448 
450  const basegfx::B2DHomMatrix& rTransformation,
451  const BitmapEx& rBitmapEx,
452  double fAlpha)
453 {
454  assert(!is_double_buffered_window());
455 
456  if( ImplIsRecordLayout() )
457  return;
458 
459  if(rBitmapEx.IsEmpty())
460  return;
461 
462  if(rtl::math::approxEqual( fAlpha, 0.0 ))
463  return;
464 
465  // MM02 compared to other public methods of OutputDevice
466  // this test was missing and led to zero-ptr-accesses
467  if ( !mpGraphics && !AcquireGraphics() )
468  return;
469 
470  if ( mbInitClipRegion )
471  InitClipRegion();
472 
473  const bool bMetafile(nullptr != mpMetaFile);
474  /*
475  tdf#135325 typically in these OutputDevice methods, for the in
476  record-to-metafile case the MetaFile is already written to before the
477  test against mbOutputClipped to determine that output to the current
478  device would result in no visual output. In this case the metafile is
479  written after the test, so we must continue past mbOutputClipped if
480  recording to a metafile. It's typical to record with a device of nominal
481  size and play back later against something of a totally different size.
482  */
483  if (mbOutputClipped && !bMetafile)
484  return;
485 
486 #ifdef DO_TIME_TEST
487  // MM02 start time test when some data (not for trivial stuff). Will
488  // trigger and show data when leaving this method by destructing helper
489  static const char* pEnableBitmapDrawTimerTimer(getenv("SAL_ENABLE_TIMER_BITMAPDRAW"));
490  static bool bUseTimer(nullptr != pEnableBitmapDrawTimerTimer);
491  std::unique_ptr<LocalTimeTest> aTimeTest(
492  bUseTimer && rBitmapEx.GetSizeBytes() > 10000
493  ? new LocalTimeTest()
494  : nullptr);
495 #endif
496 
497  BitmapEx bitmapEx = rBitmapEx;
498 
499  const bool bInvert(RasterOp::Invert == meRasterOp);
501  const bool bTryDirectPaint(!bInvert && !bBitmapChangedColor && !bMetafile);
502  // tdf#130768 CAUTION(!) using GetViewTransformation() is *not* enough here, it may
503  // be that mnOutOffX/mnOutOffY is used - see AOO bug 75163, mentioned at
504  // ImplGetDeviceTransformation declaration
505  basegfx::B2DHomMatrix aFullTransform(ImplGetDeviceTransformation() * rTransformation);
506 
507  // First try to handle additional alpha blending, either directly, or modify the bitmap.
508  if(!rtl::math::approxEqual( fAlpha, 1.0 ))
509  {
510  if(bTryDirectPaint)
511  {
512  if(DrawTransformBitmapExDirect(aFullTransform, bitmapEx, fAlpha))
513  {
514  // we are done
515  return;
516  }
517  }
518  // Apply the alpha manually.
519  sal_uInt8 nColor( static_cast<sal_uInt8>( ::basegfx::fround( 255.0*(1.0 - fAlpha) + .5) ) );
520  AlphaMask aAlpha( bitmapEx.GetSizePixel(), &nColor );
521  if( bitmapEx.IsAlpha())
522  aAlpha.BlendWith( bitmapEx.GetAlpha());
523  bitmapEx = BitmapEx( bitmapEx.GetBitmap(), aAlpha );
524  }
525  if(rtl::math::approxEqual( fAlpha, 1.0 ))
526  fAlpha = 1.0; // avoid the need for approxEqual in backends
527 
528  if(bTryDirectPaint && mpGraphics->HasFastDrawTransformedBitmap() && DrawTransformBitmapExDirect(aFullTransform, bitmapEx))
529  return;
530 
531  // decompose matrix to check rotation and shear
532  basegfx::B2DVector aScale, aTranslate;
533  double fRotate, fShearX;
534  rTransformation.decompose(aScale, aTranslate, fRotate, fShearX);
535  const bool bRotated(!basegfx::fTools::equalZero(fRotate));
536  const bool bSheared(!basegfx::fTools::equalZero(fShearX));
537  const bool bMirroredX(basegfx::fTools::less(aScale.getX(), 0.0));
538  const bool bMirroredY(basegfx::fTools::less(aScale.getY(), 0.0));
539 
540  if(!bRotated && !bSheared && !bMirroredX && !bMirroredY)
541  {
542  // with no rotation, shear or mirroring it can be mapped to DrawBitmapEx
543  // do *not* execute the mirroring here, it's done in the fallback
544  // #i124580# the correct DestSize needs to be calculated based on MaxXY values
545  Point aDestPt(basegfx::fround(aTranslate.getX()), basegfx::fround(aTranslate.getY()));
546  const Size aDestSize(
547  basegfx::fround(aScale.getX() + aTranslate.getX()) - aDestPt.X(),
548  basegfx::fround(aScale.getY() + aTranslate.getY()) - aDestPt.Y());
549  const Point aOrigin = GetMapMode().GetOrigin();
550  if (!bMetafile && comphelper::LibreOfficeKit::isActive() && GetMapMode().GetMapUnit() != MapUnit::MapPixel)
551  {
552  aDestPt.Move(aOrigin.getX(), aOrigin.getY());
553  EnableMapMode(false);
554  }
555 
556  DrawBitmapEx(aDestPt, aDestSize, bitmapEx);
557  if (!bMetafile && comphelper::LibreOfficeKit::isActive() && GetMapMode().GetMapUnit() != MapUnit::MapPixel)
558  {
559  EnableMapMode();
560  aDestPt.Move(-aOrigin.getX(), -aOrigin.getY());
561  }
562  return;
563  }
564 
565  if(bTryDirectPaint && DrawTransformBitmapExDirect(aFullTransform, bitmapEx))
566  {
567  // we are done
568  return;
569  }
570 
571  // take the fallback when no rotate and shear, but mirror (else we would have done this above)
572  if(!bRotated && !bSheared)
573  {
574  // with no rotation or shear it can be mapped to DrawBitmapEx
575  // do *not* execute the mirroring here, it's done in the fallback
576  // #i124580# the correct DestSize needs to be calculated based on MaxXY values
577  const Point aDestPt(basegfx::fround(aTranslate.getX()), basegfx::fround(aTranslate.getY()));
578  const Size aDestSize(
579  basegfx::fround(aScale.getX() + aTranslate.getX()) - aDestPt.X(),
580  basegfx::fround(aScale.getY() + aTranslate.getY()) - aDestPt.Y());
581 
582  DrawBitmapEx(aDestPt, aDestSize, bitmapEx);
583  return;
584  }
585 
586  // at this point we are either sheared or rotated or both
587  assert(bSheared || bRotated);
588 
589  // fallback; create transformed bitmap the hard way (back-transform
590  // the pixels) and paint
591  basegfx::B2DRange aVisibleRange(0.0, 0.0, 1.0, 1.0);
592 
593  // limit maximum area to something looking good for non-pixel-based targets (metafile, printer)
594  // by using a fixed minimum (allow at least, but no need to utilize) for good smoothing and an area
595  // dependent of original size for good quality when e.g. rotated/sheared. Still, limit to a maximum
596  // to avoid crashes/resource problems (ca. 1500x3000 here)
597  const Size& rOriginalSizePixel(bitmapEx.GetSizePixel());
598  const double fOrigArea(rOriginalSizePixel.Width() * rOriginalSizePixel.Height() * 0.5);
599  const double fOrigAreaScaled(fOrigArea * 1.44);
600  double fMaximumArea(std::clamp(fOrigAreaScaled, 1000000.0, 4500000.0));
601 
602  if(!bMetafile)
603  {
604  if ( !TransformAndReduceBitmapExToTargetRange( aFullTransform, aVisibleRange, fMaximumArea ) )
605  return;
606  }
607 
608  if(aVisibleRange.isEmpty())
609  return;
610 
611  BitmapEx aTransformed(bitmapEx);
612 
613  // #122923# when the result needs an alpha channel due to being rotated or sheared
614  // and thus uncovering areas, add these channels so that the own transformer (used
615  // in getTransformed) also creates a transformed alpha channel
616  if(!aTransformed.IsAlpha() && (bSheared || bRotated))
617  {
618  // parts will be uncovered, extend aTransformed with a mask bitmap
619  const Bitmap aContent(aTransformed.GetBitmap());
620 
621  AlphaMask aMaskBmp(aContent.GetSizePixel());
622  aMaskBmp.Erase(0);
623 
624  aTransformed = BitmapEx(aContent, aMaskBmp);
625  }
626 
627  // Remove scaling from aFulltransform: we transform due to shearing or rotation, scaling
628  // will happen according to aDestSize.
629  basegfx::B2DVector aFullScale, aFullTranslate;
630  double fFullRotate, fFullShearX;
631  aFullTransform.decompose(aFullScale, aFullTranslate, fFullRotate, fFullShearX);
632  // Require positive scaling, negative scaling would loose horizontal or vertical flip.
633  if (aFullScale.getX() > 0 && aFullScale.getY() > 0)
634  {
636  rOriginalSizePixel.getWidth() / aFullScale.getX(),
637  rOriginalSizePixel.getHeight() / aFullScale.getY());
638  aFullTransform *= aTransform;
639  }
640 
641  double fSourceRatio = 1.0;
642  if (rOriginalSizePixel.getHeight() != 0)
643  {
644  fSourceRatio = rOriginalSizePixel.getWidth() / rOriginalSizePixel.getHeight();
645  }
646  double fTargetRatio = 1.0;
647  if (aFullScale.getY() != 0)
648  {
649  fTargetRatio = aFullScale.getX() / aFullScale.getY();
650  }
651  bool bAspectRatioKept = rtl::math::approxEqual(fSourceRatio, fTargetRatio);
652  if (bSheared || !bAspectRatioKept)
653  {
654  // Not only rotation, or scaling does not keep aspect ratio.
655  aTransformed = aTransformed.getTransformed(
656  aFullTransform,
657  aVisibleRange,
658  fMaximumArea);
659  }
660  else
661  {
662  // Just rotation, can do that directly.
663  fFullRotate = fmod(fFullRotate * -1, 2 * M_PI);
664  if (fFullRotate < 0)
665  {
666  fFullRotate += 2 * M_PI;
667  }
668  Degree10 nAngle10(basegfx::fround(basegfx::rad2deg<10>(fFullRotate)));
669  aTransformed.Rotate(nAngle10, COL_TRANSPARENT);
670  }
671  basegfx::B2DRange aTargetRange(0.0, 0.0, 1.0, 1.0);
672 
673  // get logic object target range
674  aTargetRange.transform(rTransformation);
675 
676  // get from unified/relative VisibleRange to logoc one
677  aVisibleRange.transform(
679  aTargetRange.getRange(),
680  aTargetRange.getMinimum()));
681 
682  // extract point and size; do not remove size, the bitmap may have been prepared reduced by purpose
683  // #i124580# the correct DestSize needs to be calculated based on MaxXY values
684  const Point aDestPt(basegfx::fround(aVisibleRange.getMinX()), basegfx::fround(aVisibleRange.getMinY()));
685  const Size aDestSize(
686  basegfx::fround(aVisibleRange.getMaxX()) - aDestPt.X(),
687  basegfx::fround(aVisibleRange.getMaxY()) - aDestPt.Y());
688 
689  DrawBitmapEx(aDestPt, aDestSize, aTransformed);
690 }
691 
692 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
BitmapEx GetBitmapEx(BitmapEx const &rBitmapEx, DrawModeFlags nDrawMode)
Definition: drawmode.cxx:224
double getY() const
SAL_DLLPRIVATE bool ImplIsRecordLayout() const
Definition: outdev.cxx:702
virtual bool DrawTransformBitmapExDirect(const basegfx::B2DHomMatrix &aFullTransform, const BitmapEx &rBitmapEx, double fAlpha=1.0)
Transform and draw a bitmap directly.
Definition: bitmapex.cxx:294
bool HasFastDrawTransformedBitmap() const
double getHeight() const
tools::Rectangle & Intersection(const tools::Rectangle &rRect)
tools::Long AdjustRight(tools::Long nHorzMoveDelta)
bool equalZero(const T &rfVal)
constexpr tools::Long Left() const
void DrawBitmapEx(const Point &rDestPt, const BitmapEx &rBitmapEx)
Definition: bitmapex.cxx:33
tools::Long mnDestX
Definition: salgtype.hxx:44
DrawModeFlags mnDrawMode
Definition: outdev.hxx:226
const std::shared_ptr< SalBitmap > & ImplGetSalBitmap() const
constexpr::Color COL_TRANSPARENT(ColorTransparency, 0xFF, 0xFF, 0xFF, 0xFF)
BmpMirrorFlags AdjustTwoRect(SalTwoRect &rTwoRect, const Size &rSizePix)
Definition: rect.cxx:341
vcl::Region maRegion
Definition: outdev.hxx:231
BitmapEx getTransformed(const basegfx::B2DHomMatrix &rTransformation, const basegfx::B2DRange &rVisibleRange, double fMaximumArea) const
Create transformed Bitmap.
Definition: BitmapEx.cxx:775
bool mbOutputClipped
Definition: outdev.hxx:251
SAL_DLLPRIVATE bool is_double_buffered_window() const
const MapMode & GetMapMode() const
Definition: outdev.hxx:1567
B2DVector getRange() const
tools::Long mnSrcHeight
Definition: salgtype.hxx:43
tools::Long mnSrcWidth
Definition: salgtype.hxx:42
void EnableMapMode(bool bEnable=true)
Definition: map.cxx:586
double getMaxX() const
bool DrawTransformedBitmap(const basegfx::B2DPoint &rNull, const basegfx::B2DPoint &rX, const basegfx::B2DPoint &rY, const SalBitmap &rSourceBitmap, const SalBitmap *pAlphaBitmap, double fAlpha, const OutputDevice &rOutDev)
tools::Long mnDestWidth
Definition: salgtype.hxx:46
constexpr tools::Long Width() const
bool IsAlpha() const
Definition: BitmapEx.cxx:193
double getWidth() const
SAL_DLLPRIVATE tools::Long ImplLogicXToDevicePixel(tools::Long nX) const
Convert a logical X coordinate to a device pixel's X coordinate.
Definition: map.cxx:306
bool isInside(const B2DTuple &rTuple) const
BASEGFX_DLLPUBLIC void transform(const B2DHomMatrix &rMatrix)
B2DHomMatrix createScaleB2DHomMatrix(double fScaleX, double fScaleY)
void DrawBitmap(const SalTwoRect &rPosAry, const SalBitmap &rSalBitmap, const OutputDevice &rOutDev)
virtual Bitmap GetBitmap(const Point &rSrcPt, const Size &rSize) const
constexpr tools::Long GetWidth() const
B2DHomMatrix createScaleTranslateB2DHomMatrix(double fScaleX, double fScaleY, double fTranslateX, double fTranslateY)
MetaActionType
double getMaxY() const
tools::Long AdjustBottom(tools::Long nVertMoveDelta)
B2IRange fround(const B2DRange &rRange)
void DrawRect(const tools::Rectangle &rRect)
Definition: rect.cxx:51
constexpr bool IsEmpty() const
bool Mirror(BmpMirrorFlags nMirrorFlags)
Mirror the bitmap.
Definition: BitmapEx.cxx:259
SalGraphics * mpGraphics
Graphics context to draw on.
Definition: outdev.hxx:188
SAL_DLLPRIVATE basegfx::B2DHomMatrix ImplGetDeviceTransformation() const
Get device transformation.
Definition: map.cxx:869
void DrawTransformedBitmapEx(const basegfx::B2DHomMatrix &rTransformation, const BitmapEx &rBitmapEx, double fAlpha=1.0)
Draw BitmapEx transformed.
Definition: bitmapex.cxx:449
bool isEmpty() const
bool IsEmpty() const
Definition: BitmapEx.cxx:177
void BlendWith(const Bitmap &rOther)
Definition: alpha.cxx:83
tools::Long mnDestHeight
Definition: salgtype.hxx:47
void Erase(sal_uInt8 cTransparency)
Definition: alpha.cxx:78
bool IsClipRegion() const
Definition: outdev.hxx:558
bool decompose(B2DTuple &rScale, B2DTuple &rTranslate, double &rRotate, double &rShearX) const
virtual bool AcquireGraphics() const =0
Acquire a graphics device that the output device uses to draw on.
SAL_DLLPRIVATE tools::Long ImplLogicWidthToDevicePixel(tools::Long nWidth) const
Convert a logical width to a width in units of device pixels.
Definition: map.cxx:324
SAL_DLLPRIVATE void ImplFillOpaqueRectangle(const tools::Rectangle &rRect)
Used for alpha VDev, to set areas to opaque.
Definition: virdev.cxx:341
virtual void DrawDeviceBitmapEx(const Point &rDestPt, const Size &rDestSize, const Point &rSrcPtPixel, const Size &rSrcSizePixel, BitmapEx &rBitmapEx)
Definition: bitmapex.cxx:167
void scale(double fX, double fY)
constexpr tools::Long Top() const
RasterOp meRasterOp
Definition: outdev.hxx:238
Size GetOutputSizePixel() const
Definition: outdev.hxx:317
DrawModeFlags GetDrawMode() const
Definition: outdev.hxx:490
Bitmap maAlphaMask
Definition: bitmapex.hxx:466
void DrawBitmap(const Point &rDestPt, const Bitmap &rBitmap)
SAL_DLLPRIVATE void DrawDeviceAlphaBitmap(const Bitmap &rBmp, const AlphaMask &rAlpha, const Point &rDestPt, const Size &rDestSize, const Point &rSrcPtPixel, const Size &rSrcSizePixel)
bool Rotate(Degree10 nAngle10, const Color &rFillColor)
Rotate bitmap by the specified angle.
Definition: BitmapEx.cxx:316
void intersect(const B2DRange &rRange)
tools::Long mnSrcX
Definition: salgtype.hxx:40
tools::Long mnSrcY
Definition: salgtype.hxx:41
bool DrawAlphaBitmap(const SalTwoRect &, const SalBitmap &rSourceBitmap, const SalBitmap &rAlphaBitmap, const OutputDevice &rOutDev)
basegfx::B2DRange b2DRectangleFromRectangle(const ::tools::Rectangle &rRect)
MapUnit GetMapUnit()
Bitmap GetBitmap(Color aTransparentReplaceColor) const
Definition: BitmapEx.cxx:203
SAL_WARN_UNUSED_RESULT Point PixelToLogic(const Point &rDevicePt) const
Definition: map.cxx:1109
SAL_DLLPRIVATE std::shared_ptr< SalBitmap > const & ImplGetBitmapSalBitmap() const
Definition: bitmapex.hxx:451
VclPtr< VirtualDevice > mpAlphaVDev
Definition: outdev.hxx:202
SAL_DLLPRIVATE vcl::Region ImplPixelToDevicePixel(const vcl::Region &rRegion) const
Convert a region in pixel units to a region in device pixel units and coords.
Definition: map.cxx:576
double getMinY() const
AlphaMask GetAlpha() const
Definition: BitmapEx.cxx:215
constexpr tools::Long Height() const
unsigned char sal_uInt8
bool less(const T &rfValA, const T &rfValB)
bool Convert(BmpConversion eConversion)
Convert bitmap format.
void AddAction(const rtl::Reference< MetaAction > &pAction)
Definition: gdimtf.cxx:562
virtual vcl::Region GetActiveClipRegion() const
bool mbInitClipRegion
Definition: outdev.hxx:258
sal_Int64 GetSizeBytes() const
Definition: BitmapEx.cxx:220
B2DPoint getMinimum() const
virtual void InitClipRegion()
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_BLACK
const Point & GetOrigin() const
Definition: mapmod.cxx:140
static sal_uInt64 GetSystemTicks()
void translate(double fX, double fY)
bool Erase(const Color &rFillColor)
Fill the entire bitmap with the given color.
Definition: bitmappaint.cxx:34
BitmapEx GetBitmapEx(const Point &rSrcPt, const Size &rSize) const
Query extended bitmap (with alpha channel, if available).
Definition: bitmapex.cxx:149
virtual bool TransformAndReduceBitmapExToTargetRange(const basegfx::B2DHomMatrix &aFullTransform, basegfx::B2DRange &aVisibleRange, double &fMaximumArea)
Transform and reduce the area that needs to be drawn of the bitmap and return the new visible range a...
Definition: bitmapex.cxx:342
double getX() const
double getMinX() const
SAL_DLLPRIVATE tools::Long ImplLogicYToDevicePixel(tools::Long nY) const
Convert a logical Y coordinate to a device pixel's Y coordinate.
Definition: map.cxx:315
tools::Long mnDestY
Definition: salgtype.hxx:45
const Size & GetSizePixel() const
Definition: bitmapex.hxx:73
bool IsDeviceOutputNecessary() const
Definition: outdev.hxx:484
SAL_DLLPRIVATE tools::Long ImplLogicHeightToDevicePixel(tools::Long nHeight) const
Convert a logical height to a height in units of device pixels.
Definition: map.cxx:332
GDIMetaFile * mpMetaFile
Definition: outdev.hxx:191
constexpr tools::Long GetHeight() const
BmpMirrorFlags