LibreOffice Module vcl (master)  1
outdev/bitmap.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 <cassert>
21 #include <cstdlib>
22 
23 #include <vcl/bitmap.hxx>
24 #include <vcl/bitmapex.hxx>
26 #include <vcl/canvastools.hxx>
27 #include <vcl/gdimtf.hxx>
28 #include <vcl/metaact.hxx>
29 #include <config_features.h>
30 #include <vcl/skia/SkiaHelper.hxx>
31 #include <vcl/outdev.hxx>
32 #include <vcl/virdev.hxx>
33 #include <vcl/image.hxx>
35 
37 #include <bitmap/bmpfast.hxx>
38 #include <salgdi.hxx>
39 #include <salbmp.hxx>
40 
42 #include <memory>
43 #include <comphelper/lok.hxx>
44 #include <sal/log.hxx>
45 #include <osl/diagnose.h>
46 #include <tools/helpers.hxx>
47 #include <tools/debug.hxx>
48 #include <rtl/math.hxx>
49 #include <o3tl/unit_conversion.hxx>
50 
51 #include <vcl/dibtools.hxx>
52 #include <tools/stream.hxx>
53 
54 void OutputDevice::DrawBitmap( const Point& rDestPt, const Bitmap& rBitmap )
55 {
56  assert(!is_double_buffered_window());
57 
58  const Size aSizePix( rBitmap.GetSizePixel() );
59  DrawBitmap( rDestPt, PixelToLogic( aSizePix ), Point(), aSizePix, rBitmap, MetaActionType::BMP );
60 }
61 
62 void OutputDevice::DrawBitmap( const Point& rDestPt, const Size& rDestSize, const Bitmap& rBitmap )
63 {
64  assert(!is_double_buffered_window());
65 
66  DrawBitmap( rDestPt, rDestSize, Point(), rBitmap.GetSizePixel(), rBitmap, MetaActionType::BMPSCALE );
67 }
68 
69 
70 void OutputDevice::DrawBitmap( const Point& rDestPt, const Size& rDestSize,
71  const Point& rSrcPtPixel, const Size& rSrcSizePixel,
72  const Bitmap& rBitmap, const MetaActionType nAction )
73 {
74  assert(!is_double_buffered_window());
75 
76  if( ImplIsRecordLayout() )
77  return;
78 
80  {
81  DrawRect( tools::Rectangle( rDestPt, rDestSize ) );
82  return;
83  }
84 
85  Bitmap aBmp( rBitmap );
86 
89  {
91  {
92  sal_uInt8 cCmpVal;
93 
95  cCmpVal = 0;
96  else
97  cCmpVal = 255;
98 
99  Color aCol( cCmpVal, cCmpVal, cCmpVal );
101  SetLineColor( aCol );
102  SetFillColor( aCol );
103  DrawRect( tools::Rectangle( rDestPt, rDestSize ) );
104  Pop();
105  return;
106  }
107  else if( !aBmp.IsEmpty() )
108  {
111  }
112  }
113 
114  if ( mpMetaFile )
115  {
116  switch( nAction )
117  {
118  case MetaActionType::BMP:
119  mpMetaFile->AddAction( new MetaBmpAction( rDestPt, aBmp ) );
120  break;
121 
123  mpMetaFile->AddAction( new MetaBmpScaleAction( rDestPt, rDestSize, aBmp ) );
124  break;
125 
127  mpMetaFile->AddAction( new MetaBmpScalePartAction(
128  rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, aBmp ) );
129  break;
130 
131  default: break;
132  }
133  }
134 
135  if ( !IsDeviceOutputNecessary() )
136  return;
137 
138  if ( !mpGraphics && !AcquireGraphics() )
139  return;
140 
141  if ( mbInitClipRegion )
142  InitClipRegion();
143 
144  if ( mbOutputClipped )
145  return;
146 
147  if( !aBmp.IsEmpty() )
148  {
149  SalTwoRect aPosAry(rSrcPtPixel.X(), rSrcPtPixel.Y(), rSrcSizePixel.Width(), rSrcSizePixel.Height(),
150  ImplLogicXToDevicePixel(rDestPt.X()), ImplLogicYToDevicePixel(rDestPt.Y()),
151  ImplLogicWidthToDevicePixel(rDestSize.Width()),
152  ImplLogicHeightToDevicePixel(rDestSize.Height()));
153 
154  if ( aPosAry.mnSrcWidth && aPosAry.mnSrcHeight && aPosAry.mnDestWidth && aPosAry.mnDestHeight )
155  {
156  const BmpMirrorFlags nMirrFlags = AdjustTwoRect( aPosAry, aBmp.GetSizePixel() );
157 
158  if ( nMirrFlags != BmpMirrorFlags::NONE )
159  aBmp.Mirror( nMirrFlags );
160 
161  if ( aPosAry.mnSrcWidth && aPosAry.mnSrcHeight && aPosAry.mnDestWidth && aPosAry.mnDestHeight )
162  {
163  if (nAction == MetaActionType::BMPSCALE && CanSubsampleBitmap())
164  {
165  const double nScaleX = aPosAry.mnDestWidth / static_cast<double>(aPosAry.mnSrcWidth);
166  const double nScaleY = aPosAry.mnDestHeight / static_cast<double>(aPosAry.mnSrcHeight);
167 
168  // If subsampling, use Bitmap::Scale() for subsampling of better quality.
169  if ( nScaleX < 1.0 || nScaleY < 1.0 )
170  {
171  aBmp.Scale(nScaleX, nScaleY);
172  aPosAry.mnSrcWidth = aPosAry.mnDestWidth;
173  aPosAry.mnSrcHeight = aPosAry.mnDestHeight;
174  }
175  }
176 
177  mpGraphics->DrawBitmap( aPosAry, *aBmp.ImplGetSalBitmap(), *this );
178  }
179  }
180  }
181 
182  if( mpAlphaVDev )
183  {
184  // #i32109#: Make bitmap area opaque
185  mpAlphaVDev->ImplFillOpaqueRectangle( tools::Rectangle(rDestPt, rDestSize) );
186  }
187 }
188 
190  const Point& rSrcPt, const Size& rSrcSz,
191  const Bitmap& rBmp, tools::Long nMaxBmpDPIX, tools::Long nMaxBmpDPIY )
192 {
193  Bitmap aBmp( rBmp );
194 
195  if( !aBmp.IsEmpty() )
196  {
197  const tools::Rectangle aBmpRect( Point(), aBmp.GetSizePixel() );
198  tools::Rectangle aSrcRect( rSrcPt, rSrcSz );
199 
200  // do cropping if necessary
201  if( aSrcRect.Intersection( aBmpRect ) != aBmpRect )
202  {
203  if( !aSrcRect.IsEmpty() )
204  aBmp.Crop( aSrcRect );
205  else
206  aBmp.SetEmpty();
207  }
208 
209  if( !aBmp.IsEmpty() )
210  {
211  // do downsampling if necessary
212  Size aDstSizeTwip( PixelToLogic(LogicToPixel(rDstSz), MapMode(MapUnit::MapTwip)) );
213 
214  // #103209# Normalize size (mirroring has to happen outside of this method)
215  aDstSizeTwip = Size( std::abs(aDstSizeTwip.Width()), std::abs(aDstSizeTwip.Height()) );
216 
217  const Size aBmpSize( aBmp.GetSizePixel() );
218  const double fBmpPixelX = aBmpSize.Width();
219  const double fBmpPixelY = aBmpSize.Height();
220  const double fMaxPixelX
221  = o3tl::convert<double>(aDstSizeTwip.Width(), o3tl::Length::twip, o3tl::Length::in)
222  * nMaxBmpDPIX;
223  const double fMaxPixelY
224  = o3tl::convert<double>(aDstSizeTwip.Height(), o3tl::Length::twip, o3tl::Length::in)
225  * nMaxBmpDPIY;
226 
227  // check, if the bitmap DPI exceeds the maximum DPI (allow 4 pixel rounding tolerance)
228  if( ( ( fBmpPixelX > ( fMaxPixelX + 4 ) ) ||
229  ( fBmpPixelY > ( fMaxPixelY + 4 ) ) ) &&
230  ( fBmpPixelY > 0.0 ) && ( fMaxPixelY > 0.0 ) )
231  {
232  // do scaling
233  Size aNewBmpSize;
234  const double fBmpWH = fBmpPixelX / fBmpPixelY;
235  const double fMaxWH = fMaxPixelX / fMaxPixelY;
236 
237  if( fBmpWH < fMaxWH )
238  {
239  aNewBmpSize.setWidth( FRound( fMaxPixelY * fBmpWH ) );
240  aNewBmpSize.setHeight( FRound( fMaxPixelY ) );
241  }
242  else if( fBmpWH > 0.0 )
243  {
244  aNewBmpSize.setWidth( FRound( fMaxPixelX ) );
245  aNewBmpSize.setHeight( FRound( fMaxPixelX / fBmpWH) );
246  }
247 
248  if( aNewBmpSize.Width() && aNewBmpSize.Height() )
249  aBmp.Scale( aNewBmpSize );
250  else
251  aBmp.SetEmpty();
252  }
253  }
254  }
255 
256  return aBmp;
257 }
258 
259 void OutputDevice::DrawBitmapEx( const Point& rDestPt,
260  const BitmapEx& rBitmapEx )
261 {
262  assert(!is_double_buffered_window());
263 
264  if( ImplIsRecordLayout() )
265  return;
266 
267  if( !rBitmapEx.IsAlpha() )
268  {
269  DrawBitmap( rDestPt, rBitmapEx.GetBitmap() );
270  }
271  else
272  {
273  const Size aSizePix( rBitmapEx.GetSizePixel() );
274  DrawBitmapEx( rDestPt, PixelToLogic( aSizePix ), Point(), aSizePix, rBitmapEx, MetaActionType::BMPEX );
275  }
276 }
277 
278 void OutputDevice::DrawBitmapEx( const Point& rDestPt, const Size& rDestSize,
279  const BitmapEx& rBitmapEx )
280 {
281  assert(!is_double_buffered_window());
282 
283  if( ImplIsRecordLayout() )
284  return;
285 
286  if ( !rBitmapEx.IsAlpha() )
287  {
288  DrawBitmap( rDestPt, rDestSize, rBitmapEx.GetBitmap() );
289  }
290  else
291  {
292  DrawBitmapEx( rDestPt, rDestSize, Point(), rBitmapEx.GetSizePixel(), rBitmapEx, MetaActionType::BMPEXSCALE );
293  }
294 }
295 
296 
297 void OutputDevice::DrawBitmapEx( const Point& rDestPt, const Size& rDestSize,
298  const Point& rSrcPtPixel, const Size& rSrcSizePixel,
299  const BitmapEx& rBitmapEx, const MetaActionType nAction )
300 {
301  assert(!is_double_buffered_window());
302 
303  if( ImplIsRecordLayout() )
304  return;
305 
306  if( !rBitmapEx.IsAlpha() )
307  {
308  DrawBitmap( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, rBitmapEx.GetBitmap() );
309  }
310  else
311  {
312  if ( RasterOp::Invert == meRasterOp )
313  {
314  DrawRect( tools::Rectangle( rDestPt, rDestSize ) );
315  return;
316  }
317 
318  BitmapEx aBmpEx( rBitmapEx );
319 
322  {
324  {
325  Bitmap aColorBmp(aBmpEx.GetSizePixel(), vcl::PixelFormat::N1_BPP);
326  sal_uInt8 cCmpVal;
327 
329  cCmpVal = 0;
330  else
331  cCmpVal = 255;
332 
333  aColorBmp.Erase( Color( cCmpVal, cCmpVal, cCmpVal ) );
334 
335  if( aBmpEx.IsAlpha() )
336  {
337  // Create one-bit mask out of alpha channel, by
338  // thresholding it at alpha=0.5. As
339  // DRAWMODE_BLACK/WHITEBITMAP requires monochrome
340  // output, having alpha-induced grey levels is not
341  // acceptable.
342  BitmapEx aMaskEx(aBmpEx.GetAlpha().GetBitmap());
344  aBmpEx = BitmapEx(aColorBmp, aMaskEx.GetBitmap());
345  }
346  else
347  {
348  aBmpEx = BitmapEx( aColorBmp, aBmpEx.GetAlpha() );
349  }
350  }
351  else if( !aBmpEx.IsEmpty() )
352  {
355  }
356  }
357 
358  if ( mpMetaFile )
359  {
360  switch( nAction )
361  {
363  mpMetaFile->AddAction( new MetaBmpExAction( rDestPt, aBmpEx ) );
364  break;
365 
367  mpMetaFile->AddAction( new MetaBmpExScaleAction( rDestPt, rDestSize, aBmpEx ) );
368  break;
369 
371  mpMetaFile->AddAction( new MetaBmpExScalePartAction( rDestPt, rDestSize,
372  rSrcPtPixel, rSrcSizePixel, aBmpEx ) );
373  break;
374 
375  default: break;
376  }
377  }
378 
379  if ( !IsDeviceOutputNecessary() )
380  return;
381 
382  if ( !mpGraphics && !AcquireGraphics() )
383  return;
384 
385  if ( mbInitClipRegion )
386  InitClipRegion();
387 
388  if ( mbOutputClipped )
389  return;
390 
391  DrawDeviceBitmap( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, aBmpEx );
392  }
393 }
394 
395 Bitmap OutputDevice::GetBitmap( const Point& rSrcPt, const Size& rSize ) const
396 {
397  Bitmap aBmp;
398  tools::Long nX = ImplLogicXToDevicePixel( rSrcPt.X() );
399  tools::Long nY = ImplLogicYToDevicePixel( rSrcPt.Y() );
400  tools::Long nWidth = ImplLogicWidthToDevicePixel( rSize.Width() );
401  tools::Long nHeight = ImplLogicHeightToDevicePixel( rSize.Height() );
402 
403  if ( mpGraphics || AcquireGraphics() )
404  {
405  assert(mpGraphics);
406 
407  if ( nWidth > 0 && nHeight > 0 && nX <= (mnOutWidth + mnOutOffX) && nY <= (mnOutHeight + mnOutOffY))
408  {
409  tools::Rectangle aRect( Point( nX, nY ), Size( nWidth, nHeight ) );
410  bool bClipped = false;
411 
412  // X-Coordinate outside of draw area?
413  if ( nX < mnOutOffX )
414  {
415  nWidth -= ( mnOutOffX - nX );
416  nX = mnOutOffX;
417  bClipped = true;
418  }
419 
420  // Y-Coordinate outside of draw area?
421  if ( nY < mnOutOffY )
422  {
423  nHeight -= ( mnOutOffY - nY );
424  nY = mnOutOffY;
425  bClipped = true;
426  }
427 
428  // Width outside of draw area?
429  if ( (nWidth + nX) > (mnOutWidth + mnOutOffX) )
430  {
431  nWidth = mnOutOffX + mnOutWidth - nX;
432  bClipped = true;
433  }
434 
435  // Height outside of draw area?
436  if ( (nHeight + nY) > (mnOutHeight + mnOutOffY) )
437  {
438  nHeight = mnOutOffY + mnOutHeight - nY;
439  bClipped = true;
440  }
441 
442  if ( bClipped )
443  {
444  // If the visible part has been clipped, we have to create a
445  // Bitmap with the correct size in which we copy the clipped
446  // Bitmap to the correct position.
448 
449  if ( aVDev->SetOutputSizePixel( aRect.GetSize() ) )
450  {
451  if ( aVDev->mpGraphics || aVDev->AcquireGraphics() )
452  {
453  if ( (nWidth > 0) && (nHeight > 0) )
454  {
455  SalTwoRect aPosAry(nX, nY, nWidth, nHeight,
456  (aRect.Left() < mnOutOffX) ? (mnOutOffX - aRect.Left()) : 0L,
457  (aRect.Top() < mnOutOffY) ? (mnOutOffY - aRect.Top()) : 0L,
458  nWidth, nHeight);
459  aVDev->mpGraphics->CopyBits(aPosAry, *mpGraphics, *this, *this);
460  }
461  else
462  {
463  OSL_ENSURE(false, "CopyBits with zero or negative width or height");
464  }
465 
466  aBmp = aVDev->GetBitmap( Point(), aVDev->GetOutputSizePixel() );
467  }
468  else
469  bClipped = false;
470  }
471  else
472  bClipped = false;
473  }
474 
475  if ( !bClipped )
476  {
477  std::shared_ptr<SalBitmap> pSalBmp = mpGraphics->GetBitmap( nX, nY, nWidth, nHeight, *this );
478 
479  if( pSalBmp )
480  {
481  aBmp.ImplSetSalBitmap(pSalBmp);
482  }
483  }
484  }
485  }
486 
487  return aBmp;
488 }
489 
490 BitmapEx OutputDevice::GetBitmapEx( const Point& rSrcPt, const Size& rSize ) const
491 {
492 
493  // #110958# Extract alpha value from VDev, if any
494  if( mpAlphaVDev )
495  {
496  Bitmap aAlphaBitmap( mpAlphaVDev->GetBitmap( rSrcPt, rSize ) );
497 
498  // ensure 8 bit alpha
499  if (aAlphaBitmap.getPixelFormat() > vcl::PixelFormat::N8_BPP)
501 
502  return BitmapEx(GetBitmap( rSrcPt, rSize ), AlphaMask( aAlphaBitmap ) );
503  }
504  else
505  return BitmapEx(GetBitmap( rSrcPt, rSize ));
506 }
507 
508 void OutputDevice::DrawDeviceBitmap( const Point& rDestPt, const Size& rDestSize,
509  const Point& rSrcPtPixel, const Size& rSrcSizePixel,
510  BitmapEx& rBitmapEx )
511 {
512  assert(!is_double_buffered_window());
513 
514  if (rBitmapEx.IsAlpha())
515  {
516  DrawDeviceAlphaBitmap(rBitmapEx.GetBitmap(), rBitmapEx.GetAlpha(), rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel);
517  }
518  else if (!rBitmapEx.IsEmpty())
519  {
520  SalTwoRect aPosAry(rSrcPtPixel.X(), rSrcPtPixel.Y(), rSrcSizePixel.Width(), rSrcSizePixel.Height(),
521  ImplLogicXToDevicePixel(rDestPt.X()), ImplLogicYToDevicePixel(rDestPt.Y()),
522  ImplLogicWidthToDevicePixel(rDestSize.Width()),
523  ImplLogicHeightToDevicePixel(rDestSize.Height()));
524 
525  const BmpMirrorFlags nMirrFlags = AdjustTwoRect(aPosAry, rBitmapEx.GetSizePixel());
526 
527  if (aPosAry.mnSrcWidth && aPosAry.mnSrcHeight && aPosAry.mnDestWidth && aPosAry.mnDestHeight)
528  {
529 
530  if (nMirrFlags != BmpMirrorFlags::NONE)
531  rBitmapEx.Mirror(nMirrFlags);
532 
533  const SalBitmap* pSalSrcBmp = rBitmapEx.ImplGetBitmapSalBitmap().get();
534  std::shared_ptr<SalBitmap> xMaskBmp = rBitmapEx.maAlphaMask.ImplGetSalBitmap();
535 
536  if (xMaskBmp)
537  {
538  bool bTryDirectPaint(pSalSrcBmp);
539 
540  if (bTryDirectPaint && mpGraphics->DrawAlphaBitmap(aPosAry, *pSalSrcBmp, *xMaskBmp, *this))
541  {
542  // tried to paint as alpha directly. If this worked, we are done (except
543  // alpha, see below)
544  }
545  else
546  {
547  // #4919452# reduce operation area to bounds of
548  // cliprect. since masked transparency involves
549  // creation of a large vdev and copying the screen
550  // content into that (slooow read from framebuffer),
551  // that should considerably increase performance for
552  // large bitmaps and small clippings.
553 
554  // Note that this optimization is a workaround for a
555  // Writer peculiarity, namely, to decompose background
556  // graphics into myriads of disjunct, tiny
557  // rectangles. That otherwise kills us here, since for
558  // transparent output, SAL always prepares the whole
559  // bitmap, if aPosAry contains the whole bitmap (and
560  // it's _not_ to blame for that).
561 
562  // Note the call to ImplPixelToDevicePixel(), since
563  // aPosAry already contains the mnOutOff-offsets, they
564  // also have to be applied to the region
565  tools::Rectangle aClipRegionBounds( ImplPixelToDevicePixel(maRegion).GetBoundRect() );
566 
567  // TODO: Also respect scaling (that's a bit tricky,
568  // since the source points have to move fractional
569  // amounts (which is not possible, thus has to be
570  // emulated by increases copy area)
571  // const double nScaleX( aPosAry.mnDestWidth / aPosAry.mnSrcWidth );
572  // const double nScaleY( aPosAry.mnDestHeight / aPosAry.mnSrcHeight );
573 
574  // for now, only identity scales allowed
575  if (!aClipRegionBounds.IsEmpty() &&
576  aPosAry.mnDestWidth == aPosAry.mnSrcWidth &&
577  aPosAry.mnDestHeight == aPosAry.mnSrcHeight)
578  {
579  // now intersect dest rect with clip region
580  aClipRegionBounds.Intersection(tools::Rectangle(aPosAry.mnDestX,
581  aPosAry.mnDestY,
582  aPosAry.mnDestX + aPosAry.mnDestWidth - 1,
583  aPosAry.mnDestY + aPosAry.mnDestHeight - 1));
584 
585  // Note: I could theoretically optimize away the
586  // DrawBitmap below, if the region is empty
587  // here. Unfortunately, cannot rule out that
588  // somebody relies on the side effects.
589  if (!aClipRegionBounds.IsEmpty())
590  {
591  aPosAry.mnSrcX += aClipRegionBounds.Left() - aPosAry.mnDestX;
592  aPosAry.mnSrcY += aClipRegionBounds.Top() - aPosAry.mnDestY;
593  aPosAry.mnSrcWidth = aClipRegionBounds.GetWidth();
594  aPosAry.mnSrcHeight = aClipRegionBounds.GetHeight();
595 
596  aPosAry.mnDestX = aClipRegionBounds.Left();
597  aPosAry.mnDestY = aClipRegionBounds.Top();
598  aPosAry.mnDestWidth = aClipRegionBounds.GetWidth();
599  aPosAry.mnDestHeight = aClipRegionBounds.GetHeight();
600  }
601  }
602 
603  mpGraphics->DrawBitmap(aPosAry, *pSalSrcBmp, *xMaskBmp, *this);
604  }
605 
606  // #110958# Paint mask to alpha channel. Luckily, the
607  // black and white representation of the mask maps to
608  // the alpha channel
609 
610  // #i25167# Restrict mask painting to _opaque_ areas
611  // of the mask, otherwise we spoil areas where no
612  // bitmap content was ever visible. Interestingly
613  // enough, this can be achieved by taking the mask as
614  // the transparency mask of itself
615  if (mpAlphaVDev)
616  mpAlphaVDev->DrawBitmapEx(rDestPt,
617  rDestSize,
618  BitmapEx(rBitmapEx.GetAlpha(),
619  rBitmapEx.GetAlpha()));
620  }
621  else
622  {
623  mpGraphics->DrawBitmap(aPosAry, *pSalSrcBmp, *this);
624 
625  if (mpAlphaVDev)
626  {
627  // #i32109#: Make bitmap area opaque
628  mpAlphaVDev->ImplFillOpaqueRectangle( tools::Rectangle(rDestPt, rDestSize) );
629  }
630  }
631  }
632  }
633 }
634 
635 void OutputDevice::DrawDeviceAlphaBitmap( const Bitmap& rBmp, const AlphaMask& rAlpha,
636  const Point& rDestPt, const Size& rDestSize,
637  const Point& rSrcPtPixel, const Size& rSrcSizePixel )
638 {
639  assert(!is_double_buffered_window());
640 
641  Point aOutPt(LogicToPixel(rDestPt));
642  Size aOutSz(LogicToPixel(rDestSize));
644 
645  const bool bHMirr = aOutSz.Width() < 0;
646  const bool bVMirr = aOutSz.Height() < 0;
647 
648  ClipToPaintRegion(aDstRect);
649 
650  BmpMirrorFlags mirrorFlags = BmpMirrorFlags::NONE;
651  if (bHMirr)
652  {
653  aOutSz.setWidth( -aOutSz.Width() );
654  aOutPt.AdjustX( -(aOutSz.Width() - 1) );
655  mirrorFlags |= BmpMirrorFlags::Horizontal;
656  }
657 
658  if (bVMirr)
659  {
660  aOutSz.setHeight( -aOutSz.Height() );
661  aOutPt.AdjustY( -(aOutSz.Height() - 1) );
662  mirrorFlags |= BmpMirrorFlags::Vertical;
663  }
664 
665  if (aDstRect.Intersection(tools::Rectangle(aOutPt, aOutSz)).IsEmpty())
666  return;
667 
668  {
669  Point aRelPt = aOutPt + Point(mnOutOffX, mnOutOffY);
670  SalTwoRect aTR(
671  rSrcPtPixel.X(), rSrcPtPixel.Y(),
672  rSrcSizePixel.Width(), rSrcSizePixel.Height(),
673  aRelPt.X(), aRelPt.Y(),
674  aOutSz.Width(), aOutSz.Height());
675 
676  Bitmap bitmap(rBmp);
677  AlphaMask alpha(rAlpha);
678  if(bHMirr || bVMirr)
679  {
680  bitmap.Mirror(mirrorFlags);
681  alpha.Mirror(mirrorFlags);
682  }
683  SalBitmap* pSalSrcBmp = bitmap.ImplGetSalBitmap().get();
684  SalBitmap* pSalAlphaBmp = alpha.ImplGetSalBitmap().get();
685 
686  // #i83087# Naturally, system alpha blending (SalGraphics::DrawAlphaBitmap) cannot work
687  // with separate alpha VDev
688 
689  // try to blend the alpha bitmap with the alpha virtual device
690  if (mpAlphaVDev)
691  {
692  Bitmap aAlphaBitmap( mpAlphaVDev->GetBitmap( aRelPt, aOutSz ) );
693  if (SalBitmap* pSalAlphaBmp2 = aAlphaBitmap.ImplGetSalBitmap().get())
694  {
695  if (mpGraphics->BlendAlphaBitmap(aTR, *pSalSrcBmp, *pSalAlphaBmp, *pSalAlphaBmp2, *this))
696  {
697  mpAlphaVDev->BlendBitmap(aTR, rAlpha);
698  return;
699  }
700  }
701  }
702  else
703  {
704  if (mpGraphics->DrawAlphaBitmap(aTR, *pSalSrcBmp, *pSalAlphaBmp, *this))
705  return;
706  }
707 
708  // we need to make sure Skia never reaches this slow code path
709  assert(!SkiaHelper::isVCLSkiaEnabled());
710  }
711 
712  tools::Rectangle aBmpRect(Point(), rBmp.GetSizePixel());
713  if (!aBmpRect.Intersection(tools::Rectangle(rSrcPtPixel, rSrcSizePixel)).IsEmpty())
714  {
715  Point auxOutPt(LogicToPixel(rDestPt));
716  Size auxOutSz(LogicToPixel(rDestSize));
717 
718  // HACK: The function is broken with alpha vdev and mirroring, mirror here.
719  Bitmap bitmap(rBmp);
720  AlphaMask alpha(rAlpha);
721  if(mpAlphaVDev && (bHMirr || bVMirr))
722  {
723  bitmap.Mirror(mirrorFlags);
724  alpha.Mirror(mirrorFlags);
725  auxOutPt = aOutPt;
726  auxOutSz = aOutSz;
727  }
728  DrawDeviceAlphaBitmapSlowPath(bitmap, alpha, aDstRect, aBmpRect, auxOutSz, auxOutPt);
729  }
730 }
731 
732 namespace
733 {
734 
735 struct LinearScaleContext
736 {
737  std::unique_ptr<tools::Long[]> mpMapX;
738  std::unique_ptr<tools::Long[]> mpMapY;
739 
740  std::unique_ptr<tools::Long[]> mpMapXOffset;
741  std::unique_ptr<tools::Long[]> mpMapYOffset;
742 
743  LinearScaleContext(tools::Rectangle const & aDstRect, tools::Rectangle const & aBitmapRect,
744  Size const & aOutSize, tools::Long nOffX, tools::Long nOffY)
745 
746  : mpMapX(new tools::Long[aDstRect.GetWidth()])
747  , mpMapY(new tools::Long[aDstRect.GetHeight()])
748  , mpMapXOffset(new tools::Long[aDstRect.GetWidth()])
749  , mpMapYOffset(new tools::Long[aDstRect.GetHeight()])
750  {
751  const tools::Long nSrcWidth = aBitmapRect.GetWidth();
752  const tools::Long nSrcHeight = aBitmapRect.GetHeight();
753 
754  generateSimpleMap(
755  nSrcWidth, aDstRect.GetWidth(), aBitmapRect.Left(),
756  aOutSize.Width(), nOffX, mpMapX.get(), mpMapXOffset.get());
757 
758  generateSimpleMap(
759  nSrcHeight, aDstRect.GetHeight(), aBitmapRect.Top(),
760  aOutSize.Height(), nOffY, mpMapY.get(), mpMapYOffset.get());
761  }
762 
763 private:
764 
765  static void generateSimpleMap(tools::Long nSrcDimension, tools::Long nDstDimension, tools::Long nDstLocation,
766  tools::Long nOutDimension, tools::Long nOffset, tools::Long* pMap, tools::Long* pMapOffset)
767  {
768 
769  const double fReverseScale = (std::abs(nOutDimension) > 1) ? (nSrcDimension - 1) / double(std::abs(nOutDimension) - 1) : 0.0;
770 
771  tools::Long nSampleRange = std::max(tools::Long(0), nSrcDimension - 2);
772 
773  for (tools::Long i = 0; i < nDstDimension; i++)
774  {
775  double fTemp = std::abs((nOffset + i) * fReverseScale);
776 
777  pMap[i] = MinMax(nDstLocation + tools::Long(fTemp), 0, nSampleRange);
778  pMapOffset[i] = static_cast<tools::Long>((fTemp - pMap[i]) * 128.0);
779  }
780  }
781 
782 public:
783  bool blendBitmap(
784  const BitmapWriteAccess* pDestination,
785  const BitmapReadAccess* pSource,
786  const BitmapReadAccess* pSourceAlpha,
787  const tools::Long nDstWidth,
788  const tools::Long nDstHeight)
789  {
790  if (pSource && pSourceAlpha && pDestination)
791  {
792  ScanlineFormat nSourceFormat = pSource->GetScanlineFormat();
793  ScanlineFormat nDestinationFormat = pDestination->GetScanlineFormat();
794 
795  switch (nSourceFormat)
796  {
799  {
800  if ( (nSourceFormat == ScanlineFormat::N24BitTcBgr && nDestinationFormat == ScanlineFormat::N32BitTcBgra)
801  || (nSourceFormat == ScanlineFormat::N24BitTcRgb && nDestinationFormat == ScanlineFormat::N32BitTcRgba))
802  {
803  blendBitmap24(pDestination, pSource, pSourceAlpha, nDstWidth, nDstHeight);
804  return true;
805  }
806  }
807  break;
808  default: break;
809  }
810  }
811  return false;
812  }
813 
814  void blendBitmap24(
815  const BitmapWriteAccess* pDestination,
816  const BitmapReadAccess* pSource,
817  const BitmapReadAccess* pSourceAlpha,
818  const tools::Long nDstWidth,
819  const tools::Long nDstHeight)
820  {
821  Scanline pLine0, pLine1;
822  Scanline pLineAlpha0, pLineAlpha1;
823  Scanline pColorSample1, pColorSample2;
824  Scanline pDestScanline;
825 
826  tools::Long nColor1Line1, nColor2Line1, nColor3Line1;
827  tools::Long nColor1Line2, nColor2Line2, nColor3Line2;
828  tools::Long nAlphaLine1, nAlphaLine2;
829 
830  sal_uInt8 nColor1, nColor2, nColor3, nAlpha;
831 
832  for (tools::Long nY = 0; nY < nDstHeight; nY++)
833  {
834  const tools::Long nMapY = mpMapY[nY];
835  const tools::Long nMapFY = mpMapYOffset[nY];
836 
837  pLine0 = pSource->GetScanline(nMapY);
838  // tdf#95481 guard nMapY + 1 to be within bounds
839  pLine1 = (nMapY + 1 < pSource->Height()) ? pSource->GetScanline(nMapY + 1) : pLine0;
840 
841  pLineAlpha0 = pSourceAlpha->GetScanline(nMapY);
842  // tdf#95481 guard nMapY + 1 to be within bounds
843  pLineAlpha1 = (nMapY + 1 < pSourceAlpha->Height()) ? pSourceAlpha->GetScanline(nMapY + 1) : pLineAlpha0;
844 
845  pDestScanline = pDestination->GetScanline(nY);
846 
847  for (tools::Long nX = 0; nX < nDstWidth; nX++)
848  {
849  const tools::Long nMapX = mpMapX[nX];
850  const tools::Long nMapFX = mpMapXOffset[nX];
851 
852  pColorSample1 = pLine0 + 3 * nMapX;
853  pColorSample2 = (nMapX + 1 < pSource->Width()) ? pColorSample1 + 3 : pColorSample1;
854  nColor1Line1 = (static_cast<tools::Long>(*pColorSample1) << 7) + nMapFX * (static_cast<tools::Long>(*pColorSample2) - *pColorSample1);
855 
856  pColorSample1++;
857  pColorSample2++;
858  nColor2Line1 = (static_cast<tools::Long>(*pColorSample1) << 7) + nMapFX * (static_cast<tools::Long>(*pColorSample2) - *pColorSample1);
859 
860  pColorSample1++;
861  pColorSample2++;
862  nColor3Line1 = (static_cast<tools::Long>(*pColorSample1) << 7) + nMapFX * (static_cast<tools::Long>(*pColorSample2) - *pColorSample1);
863 
864  pColorSample1 = pLine1 + 3 * nMapX;
865  pColorSample2 = (nMapX + 1 < pSource->Width()) ? pColorSample1 + 3 : pColorSample1;
866  nColor1Line2 = (static_cast<tools::Long>(*pColorSample1) << 7) + nMapFX * (static_cast<tools::Long>(*pColorSample2) - *pColorSample1);
867 
868  pColorSample1++;
869  pColorSample2++;
870  nColor2Line2 = (static_cast<tools::Long>(*pColorSample1) << 7) + nMapFX * (static_cast<tools::Long>(*pColorSample2) - *pColorSample1);
871 
872  pColorSample1++;
873  pColorSample2++;
874  nColor3Line2 = (static_cast<tools::Long>(*pColorSample1) << 7) + nMapFX * (static_cast<tools::Long>(*pColorSample2) - *pColorSample1);
875 
876  pColorSample1 = pLineAlpha0 + nMapX;
877  pColorSample2 = (nMapX + 1 < pSourceAlpha->Width()) ? pColorSample1 + 1 : pColorSample1;
878  nAlphaLine1 = (static_cast<tools::Long>(*pColorSample1) << 7) + nMapFX * (static_cast<tools::Long>(*pColorSample2) - *pColorSample1);
879 
880  pColorSample1 = pLineAlpha1 + nMapX;
881  pColorSample2 = (nMapX + 1 < pSourceAlpha->Width()) ? pColorSample1 + 1 : pColorSample1;
882  nAlphaLine2 = (static_cast<tools::Long>(*pColorSample1) << 7) + nMapFX * (static_cast<tools::Long>(*pColorSample2) - *pColorSample1);
883 
884  nColor1 = (nColor1Line1 + nMapFY * ((nColor1Line2 >> 7) - (nColor1Line1 >> 7))) >> 7;
885  nColor2 = (nColor2Line1 + nMapFY * ((nColor2Line2 >> 7) - (nColor2Line1 >> 7))) >> 7;
886  nColor3 = (nColor3Line1 + nMapFY * ((nColor3Line2 >> 7) - (nColor3Line1 >> 7))) >> 7;
887 
888  nAlpha = (nAlphaLine1 + nMapFY * ((nAlphaLine2 >> 7) - (nAlphaLine1 >> 7))) >> 7;
889 
890  *pDestScanline = color::ColorChannelMerge(*pDestScanline, nColor1, nAlpha);
891  pDestScanline++;
892  *pDestScanline = color::ColorChannelMerge(*pDestScanline, nColor2, nAlpha);
893  pDestScanline++;
894  *pDestScanline = color::ColorChannelMerge(*pDestScanline, nColor3, nAlpha);
895  pDestScanline++;
896  pDestScanline++;
897  }
898  }
899  }
900 };
901 
902 struct TradScaleContext
903 {
904  std::unique_ptr<tools::Long[]> mpMapX;
905  std::unique_ptr<tools::Long[]> mpMapY;
906 
907  TradScaleContext(tools::Rectangle const & aDstRect, tools::Rectangle const & aBitmapRect,
908  Size const & aOutSize, tools::Long nOffX, tools::Long nOffY)
909 
910  : mpMapX(new tools::Long[aDstRect.GetWidth()])
911  , mpMapY(new tools::Long[aDstRect.GetHeight()])
912  {
913  const tools::Long nSrcWidth = aBitmapRect.GetWidth();
914  const tools::Long nSrcHeight = aBitmapRect.GetHeight();
915 
916  const bool bHMirr = aOutSize.Width() < 0;
917  const bool bVMirr = aOutSize.Height() < 0;
918 
919  generateSimpleMap(
920  nSrcWidth, aDstRect.GetWidth(), aBitmapRect.Left(),
921  aOutSize.Width(), nOffX, bHMirr, mpMapX.get());
922 
923  generateSimpleMap(
924  nSrcHeight, aDstRect.GetHeight(), aBitmapRect.Top(),
925  aOutSize.Height(), nOffY, bVMirr, mpMapY.get());
926  }
927 
928 private:
929 
930  static void generateSimpleMap(tools::Long nSrcDimension, tools::Long nDstDimension, tools::Long nDstLocation,
931  tools::Long nOutDimension, tools::Long nOffset, bool bMirror, tools::Long* pMap)
932  {
933  tools::Long nMirrorOffset = 0;
934 
935  if (bMirror)
936  nMirrorOffset = (nDstLocation << 1) + nSrcDimension - 1;
937 
938  for (tools::Long i = 0; i < nDstDimension; ++i, ++nOffset)
939  {
940  pMap[i] = nDstLocation + nOffset * nSrcDimension / nOutDimension;
941  if (bMirror)
942  pMap[i] = nMirrorOffset - pMap[i];
943  }
944  }
945 };
946 
947 
948 } // end anonymous namespace
949 
951  const AlphaMask& rAlpha, tools::Rectangle aDstRect, tools::Rectangle aBmpRect, Size const & aOutSize, Point const & aOutPoint)
952 {
953  assert(!is_double_buffered_window());
954 
955  VirtualDevice* pOldVDev = mpAlphaVDev;
956 
957  const bool bHMirr = aOutSize.Width() < 0;
958  const bool bVMirr = aOutSize.Height() < 0;
959 
960  // The scaling in this code path produces really ugly results - it
961  // does the most trivial scaling with no smoothing.
962  GDIMetaFile* pOldMetaFile = mpMetaFile;
963  const bool bOldMap = mbMap;
964 
965  mpMetaFile = nullptr; // fdo#55044 reset before GetBitmap!
966  mbMap = false;
967 
968  Bitmap aBmp(GetBitmap(aDstRect.TopLeft(), aDstRect.GetSize()));
969 
970  // #109044# The generated bitmap need not necessarily be
971  // of aDstRect dimensions, it's internally clipped to
972  // window bounds. Thus, we correct the dest size here,
973  // since we later use it (in nDstWidth/Height) for pixel
974  // access)
975  // #i38887# reading from screen may sometimes fail
976  if (aBmp.ImplGetSalBitmap())
977  {
978  aDstRect.SetSize(aBmp.GetSizePixel());
979  }
980 
981  const tools::Long nDstWidth = aDstRect.GetWidth();
982  const tools::Long nDstHeight = aDstRect.GetHeight();
983 
984  // calculate offset in original bitmap
985  // in RTL case this is a little more complicated since the contents of the
986  // bitmap is not mirrored (it never is), however the paint region and bmp region
987  // are in mirrored coordinates, so the intersection of (aOutPt,aOutSz) with these
988  // is content wise somewhere else and needs to take mirroring into account
989  const tools::Long nOffX = IsRTLEnabled()
990  ? aOutSize.Width() - aDstRect.GetWidth() - (aDstRect.Left() - aOutPoint.X())
991  : aDstRect.Left() - aOutPoint.X();
992 
993  const tools::Long nOffY = aDstRect.Top() - aOutPoint.Y();
994 
995  TradScaleContext aTradContext(aDstRect, aBmpRect, aOutSize, nOffX, nOffY);
996 
997  Bitmap::ScopedReadAccess pBitmapReadAccess(const_cast<Bitmap&>(rBitmap));
998  AlphaMask::ScopedReadAccess pAlphaReadAccess(const_cast<AlphaMask&>(rAlpha));
999 
1000  DBG_ASSERT( pAlphaReadAccess->GetScanlineFormat() == ScanlineFormat::N8BitPal,
1001  "OutputDevice::ImplDrawAlpha(): non-8bit alpha no longer supported!" );
1002 
1003  // #i38887# reading from screen may sometimes fail
1004  if (aBmp.ImplGetSalBitmap())
1005  {
1006  Bitmap aNewBitmap;
1007 
1008  if (mpAlphaVDev)
1009  {
1010  aNewBitmap = BlendBitmapWithAlpha(
1011  aBmp, pBitmapReadAccess.get(), pAlphaReadAccess.get(),
1012  aDstRect,
1013  nOffY, nDstHeight,
1014  nOffX, nDstWidth,
1015  aTradContext.mpMapX.get(), aTradContext.mpMapY.get() );
1016  }
1017  else
1018  {
1019  LinearScaleContext aLinearContext(aDstRect, aBmpRect, aOutSize, nOffX, nOffY);
1020 
1021  if (aLinearContext.blendBitmap( BitmapScopedWriteAccess(aBmp).get(), pBitmapReadAccess.get(), pAlphaReadAccess.get(),
1022  nDstWidth, nDstHeight))
1023  {
1024  aNewBitmap = aBmp;
1025  }
1026  else
1027  {
1028  aNewBitmap = BlendBitmap(
1029  aBmp, pBitmapReadAccess.get(), pAlphaReadAccess.get(),
1030  nOffY, nDstHeight,
1031  nOffX, nDstWidth,
1032  aBmpRect, aOutSize,
1033  bHMirr, bVMirr,
1034  aTradContext.mpMapX.get(), aTradContext.mpMapY.get() );
1035  }
1036  }
1037 
1038  // #110958# Disable alpha VDev, we're doing the necessary
1039  // stuff explicitly further below
1040  if (mpAlphaVDev)
1041  mpAlphaVDev = nullptr;
1042 
1043  DrawBitmap(aDstRect.TopLeft(), aNewBitmap);
1044 
1045  // #110958# Enable alpha VDev again
1046  mpAlphaVDev = pOldVDev;
1047  }
1048 
1049  mbMap = bOldMap;
1050  mpMetaFile = pOldMetaFile;
1051 }
1052 
1054  const basegfx::B2DHomMatrix& aFullTransform,
1055  const BitmapEx& rBitmapEx,
1056  double fAlpha)
1057 {
1058  assert(!is_double_buffered_window());
1059 
1060  bool bDone = false;
1061 
1062  // try to paint directly
1063  const basegfx::B2DPoint aNull(aFullTransform * basegfx::B2DPoint(0.0, 0.0));
1064  const basegfx::B2DPoint aTopX(aFullTransform * basegfx::B2DPoint(1.0, 0.0));
1065  const basegfx::B2DPoint aTopY(aFullTransform * basegfx::B2DPoint(0.0, 1.0));
1066  SalBitmap* pSalSrcBmp = rBitmapEx.GetBitmap().ImplGetSalBitmap().get();
1067  Bitmap aAlphaBitmap;
1068 
1069  if(rBitmapEx.IsAlpha())
1070  {
1071  aAlphaBitmap = rBitmapEx.GetAlpha();
1072  }
1073  else if (mpAlphaVDev)
1074  {
1075  aAlphaBitmap = AlphaMask(rBitmapEx.GetSizePixel());
1076  aAlphaBitmap.Erase(COL_BLACK); // opaque
1077  }
1078 
1079  SalBitmap* pSalAlphaBmp = aAlphaBitmap.ImplGetSalBitmap().get();
1080 
1082  aNull,
1083  aTopX,
1084  aTopY,
1085  *pSalSrcBmp,
1086  pSalAlphaBmp,
1087  fAlpha,
1088  *this);
1089 
1090  if (mpAlphaVDev)
1091  {
1092  // Merge bitmap alpha to alpha device
1093  AlphaMask aAlpha(rBitmapEx.GetSizePixel());
1094  aAlpha.Erase( ( 1 - fAlpha ) * 255 );
1095  mpAlphaVDev->DrawTransformBitmapExDirect(aFullTransform, BitmapEx(aAlpha, aAlphaBitmap));
1096  }
1097 
1098  return bDone;
1099 };
1100 
1102  const basegfx::B2DHomMatrix& aFullTransform,
1103  basegfx::B2DRange &aVisibleRange,
1104  double &fMaximumArea)
1105 {
1106  // limit TargetRange to existing pixels (if pixel device)
1107  // first get discrete range of object
1108  basegfx::B2DRange aFullPixelRange(aVisibleRange);
1109 
1110  aFullPixelRange.transform(aFullTransform);
1111 
1112  if(basegfx::fTools::equalZero(aFullPixelRange.getWidth()) || basegfx::fTools::equalZero(aFullPixelRange.getHeight()))
1113  {
1114  // object is outside of visible area
1115  return false;
1116  }
1117 
1118  // now get discrete target pixels; start with OutDev pixel size and evtl.
1119  // intersect with active clipping area
1120  basegfx::B2DRange aOutPixel(
1121  0.0,
1122  0.0,
1125 
1126  if(IsClipRegion())
1127  {
1128  tools::Rectangle aRegionRectangle(GetActiveClipRegion().GetBoundRect());
1129 
1130  // caution! Range from rectangle, one too much (!)
1131  aRegionRectangle.AdjustRight(-1);
1132  aRegionRectangle.AdjustBottom(-1);
1133  aOutPixel.intersect( vcl::unotools::b2DRectangleFromRectangle(aRegionRectangle) );
1134  }
1135 
1136  if(aOutPixel.isEmpty())
1137  {
1138  // no active output area
1139  return false;
1140  }
1141 
1142  // if aFullPixelRange is not completely inside of aOutPixel,
1143  // reduction of target pixels is possible
1144  basegfx::B2DRange aVisiblePixelRange(aFullPixelRange);
1145 
1146  if(!aOutPixel.isInside(aFullPixelRange))
1147  {
1148  aVisiblePixelRange.intersect(aOutPixel);
1149 
1150  if(aVisiblePixelRange.isEmpty())
1151  {
1152  // nothing in visible part, reduces to nothing
1153  return false;
1154  }
1155 
1156  // aVisiblePixelRange contains the reduced output area in
1157  // discrete coordinates. To make it useful everywhere, make it relative to
1158  // the object range
1159  basegfx::B2DHomMatrix aMakeVisibleRangeRelative;
1160 
1161  aVisibleRange = aVisiblePixelRange;
1162  aMakeVisibleRangeRelative.translate(
1163  -aFullPixelRange.getMinX(),
1164  -aFullPixelRange.getMinY());
1165  aMakeVisibleRangeRelative.scale(
1166  1.0 / aFullPixelRange.getWidth(),
1167  1.0 / aFullPixelRange.getHeight());
1168  aVisibleRange.transform(aMakeVisibleRangeRelative);
1169  }
1170 
1171  // for pixel devices, do *not* limit size, else OutputDevice::DrawDeviceAlphaBitmap
1172  // will create another, badly scaled bitmap to do the job. Nonetheless, do a
1173  // maximum clipping of something big (1600x1280x2). Add 1.0 to avoid rounding
1174  // errors in rough estimations
1175  const double fNewMaxArea(aVisiblePixelRange.getWidth() * aVisiblePixelRange.getHeight());
1176 
1177  fMaximumArea = std::min(4096000.0, fNewMaxArea + 1.0);
1178 
1179  return true;
1180 }
1181 
1182 // MM02 add some test class to get a simple timer-based output to be able
1183 // to check if it gets faster - and how much. Uncomment next line or set
1184 // DO_TIME_TEST for compile time if you want to use it
1185 // #define DO_TIME_TEST
1186 #ifdef DO_TIME_TEST
1187 #include <tools/time.hxx>
1188 struct LocalTimeTest
1189 {
1190  const sal_uInt64 nStartTime;
1191  LocalTimeTest() : nStartTime(tools::Time::GetSystemTicks()) {}
1192  ~LocalTimeTest()
1193  {
1194  const sal_uInt64 nEndTime(tools::Time::GetSystemTicks());
1195  const sal_uInt64 nDiffTime(nEndTime - nStartTime);
1196 
1197  if(nDiffTime > 0)
1198  {
1199  OStringBuffer aOutput("Time: ");
1200  OString aNumber(OString::number(nDiffTime));
1201  aOutput.append(aNumber);
1202  OSL_FAIL(aOutput.getStr());
1203  }
1204  }
1205 };
1206 #endif
1207 
1209  const basegfx::B2DHomMatrix& rTransformation,
1210  const BitmapEx& rBitmapEx,
1211  double fAlpha)
1212 {
1213  assert(!is_double_buffered_window());
1214 
1215  if( ImplIsRecordLayout() )
1216  return;
1217 
1218  if(rBitmapEx.IsEmpty())
1219  return;
1220 
1221  if(rtl::math::approxEqual( fAlpha, 0.0 ))
1222  return;
1223 
1224  // MM02 compared to other public methods of OutputDevice
1225  // this test was missing and led to zero-ptr-accesses
1226  if ( !mpGraphics && !AcquireGraphics() )
1227  return;
1228 
1229  if ( mbInitClipRegion )
1230  InitClipRegion();
1231 
1232  const bool bMetafile(nullptr != mpMetaFile);
1233  /*
1234  tdf#135325 typically in these OutputDevice methods, for the in
1235  record-to-metafile case the MetaFile is already written to before the
1236  test against mbOutputClipped to determine that output to the current
1237  device would result in no visual output. In this case the metafile is
1238  written after the test, so we must continue past mbOutputClipped if
1239  recording to a metafile. It's typical to record with a device of nominal
1240  size and play back later against something of a totally different size.
1241  */
1242  if (mbOutputClipped && !bMetafile)
1243  return;
1244 
1245 #ifdef DO_TIME_TEST
1246  // MM02 start time test when some data (not for trivial stuff). Will
1247  // trigger and show data when leaving this method by destructing helper
1248  static const char* pEnableBitmapDrawTimerTimer(getenv("SAL_ENABLE_TIMER_BITMAPDRAW"));
1249  static bool bUseTimer(nullptr != pEnableBitmapDrawTimerTimer);
1250  std::unique_ptr<LocalTimeTest> aTimeTest(
1251  bUseTimer && rBitmapEx.GetSizeBytes() > 10000
1252  ? new LocalTimeTest()
1253  : nullptr);
1254 #endif
1255 
1256  BitmapEx bitmapEx = rBitmapEx;
1257 
1258  const bool bInvert(RasterOp::Invert == meRasterOp);
1260  const bool bTryDirectPaint(!bInvert && !bBitmapChangedColor && !bMetafile);
1261  // tdf#130768 CAUTION(!) using GetViewTransformation() is *not* enough here, it may
1262  // be that mnOutOffX/mnOutOffY is used - see AOO bug 75163, mentioned at
1263  // ImplGetDeviceTransformation declaration
1264  basegfx::B2DHomMatrix aFullTransform(ImplGetDeviceTransformation() * rTransformation);
1265 
1266  // First try to handle additional alpha blending, either directly, or modify the bitmap.
1267  if(!rtl::math::approxEqual( fAlpha, 1.0 ))
1268  {
1269  if(bTryDirectPaint)
1270  {
1271  if(DrawTransformBitmapExDirect(aFullTransform, bitmapEx, fAlpha))
1272  {
1273  // we are done
1274  return;
1275  }
1276  }
1277  // Apply the alpha manually.
1278  sal_uInt8 nColor( static_cast<sal_uInt8>( ::basegfx::fround( 255.0*(1.0 - fAlpha) + .5) ) );
1279  AlphaMask aAlpha( bitmapEx.GetSizePixel(), &nColor );
1280  if( bitmapEx.IsAlpha())
1281  aAlpha.BlendWith( bitmapEx.GetAlpha());
1282  bitmapEx = BitmapEx( bitmapEx.GetBitmap(), aAlpha );
1283  }
1284  if(rtl::math::approxEqual( fAlpha, 1.0 ))
1285  fAlpha = 1.0; // avoid the need for approxEqual in backends
1286 
1287  if(bTryDirectPaint && mpGraphics->HasFastDrawTransformedBitmap() && DrawTransformBitmapExDirect(aFullTransform, bitmapEx))
1288  return;
1289 
1290  // decompose matrix to check rotation and shear
1291  basegfx::B2DVector aScale, aTranslate;
1292  double fRotate, fShearX;
1293  rTransformation.decompose(aScale, aTranslate, fRotate, fShearX);
1294  const bool bRotated(!basegfx::fTools::equalZero(fRotate));
1295  const bool bSheared(!basegfx::fTools::equalZero(fShearX));
1296  const bool bMirroredX(basegfx::fTools::less(aScale.getX(), 0.0));
1297  const bool bMirroredY(basegfx::fTools::less(aScale.getY(), 0.0));
1298 
1299  if(!bRotated && !bSheared && !bMirroredX && !bMirroredY)
1300  {
1301  // with no rotation, shear or mirroring it can be mapped to DrawBitmapEx
1302  // do *not* execute the mirroring here, it's done in the fallback
1303  // #i124580# the correct DestSize needs to be calculated based on MaxXY values
1304  Point aDestPt(basegfx::fround(aTranslate.getX()), basegfx::fround(aTranslate.getY()));
1305  const Size aDestSize(
1306  basegfx::fround(aScale.getX() + aTranslate.getX()) - aDestPt.X(),
1307  basegfx::fround(aScale.getY() + aTranslate.getY()) - aDestPt.Y());
1308  const Point aOrigin = GetMapMode().GetOrigin();
1309  if (!bMetafile && comphelper::LibreOfficeKit::isActive() && GetMapMode().GetMapUnit() != MapUnit::MapPixel)
1310  {
1311  aDestPt.Move(aOrigin.getX(), aOrigin.getY());
1312  EnableMapMode(false);
1313  }
1314 
1315  DrawBitmapEx(aDestPt, aDestSize, bitmapEx);
1316  if (!bMetafile && comphelper::LibreOfficeKit::isActive() && GetMapMode().GetMapUnit() != MapUnit::MapPixel)
1317  {
1318  EnableMapMode();
1319  aDestPt.Move(-aOrigin.getX(), -aOrigin.getY());
1320  }
1321  return;
1322  }
1323 
1324  if(bTryDirectPaint && DrawTransformBitmapExDirect(aFullTransform, bitmapEx))
1325  {
1326  // we are done
1327  return;
1328  }
1329 
1330  // take the fallback when no rotate and shear, but mirror (else we would have done this above)
1331  if(!bRotated && !bSheared)
1332  {
1333  // with no rotation or shear it can be mapped to DrawBitmapEx
1334  // do *not* execute the mirroring here, it's done in the fallback
1335  // #i124580# the correct DestSize needs to be calculated based on MaxXY values
1336  const Point aDestPt(basegfx::fround(aTranslate.getX()), basegfx::fround(aTranslate.getY()));
1337  const Size aDestSize(
1338  basegfx::fround(aScale.getX() + aTranslate.getX()) - aDestPt.X(),
1339  basegfx::fround(aScale.getY() + aTranslate.getY()) - aDestPt.Y());
1340 
1341  DrawBitmapEx(aDestPt, aDestSize, bitmapEx);
1342  return;
1343  }
1344 
1345  // at this point we are either sheared or rotated or both
1346  assert(bSheared || bRotated);
1347 
1348  // fallback; create transformed bitmap the hard way (back-transform
1349  // the pixels) and paint
1350  basegfx::B2DRange aVisibleRange(0.0, 0.0, 1.0, 1.0);
1351 
1352  // limit maximum area to something looking good for non-pixel-based targets (metafile, printer)
1353  // by using a fixed minimum (allow at least, but no need to utilize) for good smoothing and an area
1354  // dependent of original size for good quality when e.g. rotated/sheared. Still, limit to a maximum
1355  // to avoid crashes/resource problems (ca. 1500x3000 here)
1356  const Size& rOriginalSizePixel(bitmapEx.GetSizePixel());
1357  const double fOrigArea(rOriginalSizePixel.Width() * rOriginalSizePixel.Height() * 0.5);
1358  const double fOrigAreaScaled(fOrigArea * 1.44);
1359  double fMaximumArea(std::clamp(fOrigAreaScaled, 1000000.0, 4500000.0));
1360 
1361  if(!bMetafile)
1362  {
1363  if ( !TransformAndReduceBitmapExToTargetRange( aFullTransform, aVisibleRange, fMaximumArea ) )
1364  return;
1365  }
1366 
1367  if(aVisibleRange.isEmpty())
1368  return;
1369 
1370  BitmapEx aTransformed(bitmapEx);
1371 
1372  // #122923# when the result needs an alpha channel due to being rotated or sheared
1373  // and thus uncovering areas, add these channels so that the own transformer (used
1374  // in getTransformed) also creates a transformed alpha channel
1375  if(!aTransformed.IsAlpha() && (bSheared || bRotated))
1376  {
1377  // parts will be uncovered, extend aTransformed with a mask bitmap
1378  const Bitmap aContent(aTransformed.GetBitmap());
1379 
1380  AlphaMask aMaskBmp(aContent.GetSizePixel());
1381  aMaskBmp.Erase(0);
1382 
1383  aTransformed = BitmapEx(aContent, aMaskBmp);
1384  }
1385 
1386  // Remove scaling from aFulltransform: we transform due to shearing or rotation, scaling
1387  // will happen according to aDestSize.
1388  basegfx::B2DVector aFullScale, aFullTranslate;
1389  double fFullRotate, fFullShearX;
1390  aFullTransform.decompose(aFullScale, aFullTranslate, fFullRotate, fFullShearX);
1391  // Require positive scaling, negative scaling would loose horizontal or vertical flip.
1392  if (aFullScale.getX() > 0 && aFullScale.getY() > 0)
1393  {
1395  rOriginalSizePixel.getWidth() / aFullScale.getX(),
1396  rOriginalSizePixel.getHeight() / aFullScale.getY());
1397  aFullTransform *= aTransform;
1398  }
1399 
1400  double fSourceRatio = 1.0;
1401  if (rOriginalSizePixel.getHeight() != 0)
1402  {
1403  fSourceRatio = rOriginalSizePixel.getWidth() / rOriginalSizePixel.getHeight();
1404  }
1405  double fTargetRatio = 1.0;
1406  if (aFullScale.getY() != 0)
1407  {
1408  fTargetRatio = aFullScale.getX() / aFullScale.getY();
1409  }
1410  bool bAspectRatioKept = rtl::math::approxEqual(fSourceRatio, fTargetRatio);
1411  if (bSheared || !bAspectRatioKept)
1412  {
1413  // Not only rotation, or scaling does not keep aspect ratio.
1414  aTransformed = aTransformed.getTransformed(
1415  aFullTransform,
1416  aVisibleRange,
1417  fMaximumArea);
1418  }
1419  else
1420  {
1421  // Just rotation, can do that directly.
1422  fFullRotate = fmod(fFullRotate * -1, F_2PI);
1423  if (fFullRotate < 0)
1424  {
1425  fFullRotate += F_2PI;
1426  }
1427  Degree10 nAngle10(basegfx::fround(basegfx::rad2deg(fFullRotate) * 10));
1428  aTransformed.Rotate(nAngle10, COL_TRANSPARENT);
1429  }
1430  basegfx::B2DRange aTargetRange(0.0, 0.0, 1.0, 1.0);
1431 
1432  // get logic object target range
1433  aTargetRange.transform(rTransformation);
1434 
1435  // get from unified/relative VisibleRange to logoc one
1436  aVisibleRange.transform(
1438  aTargetRange.getRange(),
1439  aTargetRange.getMinimum()));
1440 
1441  // extract point and size; do not remove size, the bitmap may have been prepared reduced by purpose
1442  // #i124580# the correct DestSize needs to be calculated based on MaxXY values
1443  const Point aDestPt(basegfx::fround(aVisibleRange.getMinX()), basegfx::fround(aVisibleRange.getMinY()));
1444  const Size aDestSize(
1445  basegfx::fround(aVisibleRange.getMaxX()) - aDestPt.X(),
1446  basegfx::fround(aVisibleRange.getMaxY()) - aDestPt.Y());
1447 
1448  DrawBitmapEx(aDestPt, aDestSize, aTransformed);
1449 }
1450 
1452 {
1453  if( ImplIsRecordLayout() )
1454  return false;
1455 
1456  if (!mpGraphics && !AcquireGraphics())
1457  return false;
1458  assert(mpGraphics);
1459 
1461 }
1462 
1463 void OutputDevice::DrawImage( const Point& rPos, const Image& rImage, DrawImageFlags nStyle )
1464 {
1465  assert(!is_double_buffered_window());
1466 
1467  DrawImage( rPos, Size(), rImage, nStyle );
1468 }
1469 
1470 void OutputDevice::DrawImage( const Point& rPos, const Size& rSize,
1471  const Image& rImage, DrawImageFlags nStyle )
1472 {
1473  assert(!is_double_buffered_window());
1474 
1475  bool bIsSizeValid = !rSize.IsEmpty();
1476 
1477  if (!ImplIsRecordLayout())
1478  {
1479  Image& rNonConstImage = const_cast<Image&>(rImage);
1480  if (bIsSizeValid)
1481  rNonConstImage.Draw(this, rPos, nStyle, &rSize);
1482  else
1483  rNonConstImage.Draw(this, rPos, nStyle);
1484  }
1485 }
1486 
1487 namespace
1488 {
1489  // Co = Cs + Cd*(1-As) premultiplied alpha -or-
1490  // Co = (AsCs + AdCd*(1-As)) / Ao
1491  sal_uInt8 CalcColor( const sal_uInt8 nSourceColor, const sal_uInt8 nSourceAlpha,
1492  const sal_uInt8 nDstAlpha, const sal_uInt8 nResAlpha, const sal_uInt8 nDestColor )
1493  {
1494  int c = nResAlpha ? ( static_cast<int>(nSourceAlpha)*nSourceColor + static_cast<int>(nDstAlpha)*nDestColor -
1495  static_cast<int>(nDstAlpha)*nDestColor*nSourceAlpha/255 ) / static_cast<int>(nResAlpha) : 0;
1496  return sal_uInt8( c );
1497  }
1498 
1499  BitmapColor AlphaBlend( int nX, int nY,
1500  const tools::Long nMapX,
1501  const tools::Long nMapY,
1502  BitmapReadAccess const * pP,
1503  BitmapReadAccess const * pA,
1504  BitmapReadAccess const * pB,
1505  BitmapWriteAccess const * pAlphaW,
1506  sal_uInt8& nResAlpha )
1507  {
1508  BitmapColor aDstCol,aSrcCol;
1509  aSrcCol = pP->GetColor( nMapY, nMapX );
1510  aDstCol = pB->GetColor( nY, nX );
1511 
1512  // vcl stores transparency, not alpha - invert it
1513  const sal_uInt8 nSrcAlpha = 255 - pA->GetPixelIndex( nMapY, nMapX );
1514  const sal_uInt8 nDstAlpha = 255 - pAlphaW->GetPixelIndex( nY, nX );
1515 
1516  // Perform porter-duff compositing 'over' operation
1517 
1518  // Co = Cs + Cd*(1-As)
1519  // Ad = As + Ad*(1-As)
1520  nResAlpha = static_cast<int>(nSrcAlpha) + static_cast<int>(nDstAlpha) - static_cast<int>(nDstAlpha)*nSrcAlpha/255;
1521 
1522  aDstCol.SetRed( CalcColor( aSrcCol.GetRed(), nSrcAlpha, nDstAlpha, nResAlpha, aDstCol.GetRed() ) );
1523  aDstCol.SetBlue( CalcColor( aSrcCol.GetBlue(), nSrcAlpha, nDstAlpha, nResAlpha, aDstCol.GetBlue() ) );
1524  aDstCol.SetGreen( CalcColor( aSrcCol.GetGreen(), nSrcAlpha, nDstAlpha, nResAlpha, aDstCol.GetGreen() ) );
1525 
1526  return aDstCol;
1527  }
1528 }
1529 
1531  const SalTwoRect& rPosAry,
1532  const Bitmap& rBmp )
1533 {
1534  mpGraphics->BlendBitmap( rPosAry, *rBmp.ImplGetSalBitmap(), *this );
1535 }
1536 
1538  Bitmap& aBmp,
1539  BitmapReadAccess const * pP,
1540  BitmapReadAccess const * pA,
1541  const tools::Rectangle& aDstRect,
1542  const sal_Int32 nOffY,
1543  const sal_Int32 nDstHeight,
1544  const sal_Int32 nOffX,
1545  const sal_Int32 nDstWidth,
1546  const tools::Long* pMapX,
1547  const tools::Long* pMapY )
1548 
1549 {
1550  BitmapColor aDstCol;
1551  Bitmap res;
1552  int nX, nY;
1553  sal_uInt8 nResAlpha;
1554 
1555  SAL_WARN_IF( !mpAlphaVDev, "vcl.gdi", "BlendBitmapWithAlpha(): call me only with valid alpha VirtualDevice!" );
1556 
1557  bool bOldMapMode( mpAlphaVDev->IsMapModeEnabled() );
1558  mpAlphaVDev->EnableMapMode(false);
1559 
1560  Bitmap aAlphaBitmap( mpAlphaVDev->GetBitmap( aDstRect.TopLeft(), aDstRect.GetSize() ) );
1561  BitmapScopedWriteAccess pAlphaW(aAlphaBitmap);
1562 
1563  if( GetBitCount() <= 8 )
1564  {
1565  Bitmap aDither(aBmp.GetSizePixel(), vcl::PixelFormat::N8_BPP);
1566  BitmapColor aIndex( 0 );
1567  Bitmap::ScopedReadAccess pB(aBmp);
1568  BitmapScopedWriteAccess pW(aDither);
1569 
1570  if (pB && pP && pA && pW && pAlphaW)
1571  {
1572  int nOutY;
1573 
1574  for( nY = 0, nOutY = nOffY; nY < nDstHeight; nY++, nOutY++ )
1575  {
1576  const tools::Long nMapY = pMapY[ nY ];
1577  const tools::Long nModY = ( nOutY & 0x0FL ) << 4;
1578  int nOutX;
1579 
1580  Scanline pScanline = pW->GetScanline(nY);
1581  Scanline pScanlineAlpha = pAlphaW->GetScanline(nY);
1582  for( nX = 0, nOutX = nOffX; nX < nDstWidth; nX++, nOutX++ )
1583  {
1584  const tools::Long nMapX = pMapX[ nX ];
1585  const sal_uLong nD = nVCLDitherLut[ nModY | ( nOutX & 0x0FL ) ];
1586 
1587  aDstCol = AlphaBlend( nX, nY, nMapX, nMapY, pP, pA, pB.get(), pAlphaW.get(), nResAlpha );
1588 
1589  aIndex.SetIndex( static_cast<sal_uInt8>( nVCLRLut[ ( nVCLLut[ aDstCol.GetRed() ] + nD ) >> 16 ] +
1590  nVCLGLut[ ( nVCLLut[ aDstCol.GetGreen() ] + nD ) >> 16 ] +
1591  nVCLBLut[ ( nVCLLut[ aDstCol.GetBlue() ] + nD ) >> 16 ] ) );
1592  pW->SetPixelOnData( pScanline, nX, aIndex );
1593 
1594  aIndex.SetIndex( static_cast<sal_uInt8>( nVCLRLut[ ( nVCLLut[ 255-nResAlpha ] + nD ) >> 16 ] +
1595  nVCLGLut[ ( nVCLLut[ 255-nResAlpha ] + nD ) >> 16 ] +
1596  nVCLBLut[ ( nVCLLut[ 255-nResAlpha ] + nD ) >> 16 ] ) );
1597  pAlphaW->SetPixelOnData( pScanlineAlpha, nX, aIndex );
1598  }
1599  }
1600  }
1601  pB.reset();
1602  pW.reset();
1603  res = aDither;
1604  }
1605  else
1606  {
1607  BitmapScopedWriteAccess pB(aBmp);
1608  if (pB && pP && pA && pAlphaW)
1609  {
1610  for( nY = 0; nY < nDstHeight; nY++ )
1611  {
1612  const tools::Long nMapY = pMapY[ nY ];
1613  Scanline pScanlineB = pB->GetScanline(nY);
1614  Scanline pScanlineAlpha = pAlphaW->GetScanline(nY);
1615 
1616  for( nX = 0; nX < nDstWidth; nX++ )
1617  {
1618  const tools::Long nMapX = pMapX[ nX ];
1619  aDstCol = AlphaBlend( nX, nY, nMapX, nMapY, pP, pA, pB.get(), pAlphaW.get(), nResAlpha );
1620 
1621  pB->SetPixelOnData(pScanlineB, nX, pB->GetBestMatchingColor(aDstCol));
1622  pAlphaW->SetPixelOnData(pScanlineAlpha, nX, pB->GetBestMatchingColor(Color(255L-nResAlpha, 255L-nResAlpha, 255L-nResAlpha)));
1623  }
1624  }
1625  }
1626  pB.reset();
1627  res = aBmp;
1628  }
1629 
1630  pAlphaW.reset();
1631  mpAlphaVDev->DrawBitmap( aDstRect.TopLeft(), aAlphaBitmap );
1632  mpAlphaVDev->EnableMapMode( bOldMapMode );
1633 
1634  return res;
1635 }
1636 
1638  Bitmap& aBmp,
1639  BitmapReadAccess const * pP,
1640  BitmapReadAccess const * pA,
1641  const sal_Int32 nOffY,
1642  const sal_Int32 nDstHeight,
1643  const sal_Int32 nOffX,
1644  const sal_Int32 nDstWidth,
1645  const tools::Rectangle& aBmpRect,
1646  const Size& aOutSz,
1647  const bool bHMirr,
1648  const bool bVMirr,
1649  const tools::Long* pMapX,
1650  const tools::Long* pMapY )
1651 {
1652  BitmapColor aDstCol;
1653  Bitmap res;
1654  int nX, nY;
1655 
1656  if( GetBitCount() <= 8 )
1657  {
1658  Bitmap aDither(aBmp.GetSizePixel(), vcl::PixelFormat::N8_BPP);
1659  BitmapColor aIndex( 0 );
1660  Bitmap::ScopedReadAccess pB(aBmp);
1661  BitmapScopedWriteAccess pW(aDither);
1662 
1663  if( pB && pP && pA && pW )
1664  {
1665  int nOutY;
1666 
1667  for( nY = 0, nOutY = nOffY; nY < nDstHeight; nY++, nOutY++ )
1668  {
1669  tools::Long nMapY = pMapY[ nY ];
1670  if (bVMirr)
1671  {
1672  nMapY = aBmpRect.Bottom() - nMapY;
1673  }
1674  const tools::Long nModY = ( nOutY & 0x0FL ) << 4;
1675  int nOutX;
1676 
1677  Scanline pScanline = pW->GetScanline(nY);
1678  Scanline pScanlineAlpha = pA->GetScanline(nMapY);
1679  for( nX = 0, nOutX = nOffX; nX < nDstWidth; nX++, nOutX++ )
1680  {
1681  tools::Long nMapX = pMapX[ nX ];
1682  if (bHMirr)
1683  {
1684  nMapX = aBmpRect.Right() - nMapX;
1685  }
1686  const sal_uLong nD = nVCLDitherLut[ nModY | ( nOutX & 0x0FL ) ];
1687 
1688  aDstCol = pB->GetColor( nY, nX );
1689  aDstCol.Merge( pP->GetColor( nMapY, nMapX ), pA->GetIndexFromData( pScanlineAlpha, nMapX ) );
1690  aIndex.SetIndex( static_cast<sal_uInt8>( nVCLRLut[ ( nVCLLut[ aDstCol.GetRed() ] + nD ) >> 16 ] +
1691  nVCLGLut[ ( nVCLLut[ aDstCol.GetGreen() ] + nD ) >> 16 ] +
1692  nVCLBLut[ ( nVCLLut[ aDstCol.GetBlue() ] + nD ) >> 16 ] ) );
1693  pW->SetPixelOnData( pScanline, nX, aIndex );
1694  }
1695  }
1696  }
1697 
1698  pB.reset();
1699  pW.reset();
1700  res = aDither;
1701  }
1702  else
1703  {
1704  BitmapScopedWriteAccess pB(aBmp);
1705 
1706  bool bFastBlend = false;
1707  if( pP && pA && pB && !bHMirr && !bVMirr )
1708  {
1709  SalTwoRect aTR(aBmpRect.Left(), aBmpRect.Top(), aBmpRect.GetWidth(), aBmpRect.GetHeight(),
1710  nOffX, nOffY, aOutSz.Width(), aOutSz.Height());
1711 
1712  bFastBlend = ImplFastBitmapBlending( *pB,*pP,*pA, aTR );
1713  }
1714 
1715  if( pP && pA && pB && !bFastBlend )
1716  {
1717  switch( pP->GetScanlineFormat() )
1718  {
1720  {
1721  for( nY = 0; nY < nDstHeight; nY++ )
1722  {
1723  tools::Long nMapY = pMapY[ nY ];
1724  if ( bVMirr )
1725  {
1726  nMapY = aBmpRect.Bottom() - nMapY;
1727  }
1728  Scanline pPScan = pP->GetScanline( nMapY );
1729  Scanline pAScan = pA->GetScanline( nMapY );
1730  Scanline pBScan = pB->GetScanline( nY );
1731 
1732  for( nX = 0; nX < nDstWidth; nX++ )
1733  {
1734  tools::Long nMapX = pMapX[ nX ];
1735 
1736  if ( bHMirr )
1737  {
1738  nMapX = aBmpRect.Right() - nMapX;
1739  }
1740  aDstCol = pB->GetPixelFromData( pBScan, nX );
1741  aDstCol.Merge( pP->GetPaletteColor( pPScan[ nMapX ] ), pAScan[ nMapX ] );
1742  pB->SetPixelOnData( pBScan, nX, aDstCol );
1743  }
1744  }
1745  }
1746  break;
1747 
1748  default:
1749  {
1750 
1751  for( nY = 0; nY < nDstHeight; nY++ )
1752  {
1753  tools::Long nMapY = pMapY[ nY ];
1754 
1755  if ( bVMirr )
1756  {
1757  nMapY = aBmpRect.Bottom() - nMapY;
1758  }
1759  Scanline pAScan = pA->GetScanline( nMapY );
1760  Scanline pBScan = pB->GetScanline(nY);
1761  for( nX = 0; nX < nDstWidth; nX++ )
1762  {
1763  tools::Long nMapX = pMapX[ nX ];
1764 
1765  if ( bHMirr )
1766  {
1767  nMapX = aBmpRect.Right() - nMapX;
1768  }
1769  aDstCol = pB->GetPixelFromData( pBScan, nX );
1770  aDstCol.Merge( pP->GetColor( nMapY, nMapX ), pAScan[ nMapX ] );
1771  pB->SetPixelOnData( pBScan, nX, aDstCol );
1772  }
1773  }
1774  }
1775  break;
1776  }
1777  }
1778 
1779  pB.reset();
1780  res = aBmp;
1781  }
1782 
1783  return res;
1784 }
1785 
1786 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
double getY() const
sal_uInt8 GetIndexFromData(const sal_uInt8 *pData, tools::Long nX) const
tools::Long Height() const
sal_uInt8 GetPixelIndex(tools::Long nY, tools::Long nX) const
bool BlendBitmap(const SalTwoRect &rPosAry, const SalBitmap &rSalBitmap, const OutputDevice &rOutDev)
sal_uInt8 GetRed() const
SAL_DLLPRIVATE bool ImplIsRecordLayout() const
Definition: outdev.cxx:651
virtual bool DrawTransformBitmapExDirect(const basegfx::B2DHomMatrix &aFullTransform, const BitmapEx &rBitmapEx, double fAlpha=1.0)
Transform and draw a bitmap directly.
void SetBlue(sal_uInt8 nBlue)
void DrawImage(const Point &rPos, const Image &rImage, DrawImageFlags nStyle=DrawImageFlags::NONE)
This is an overloaded member function, provided for convenience. It differs from the above function o...
bool HasFastDrawTransformedBitmap() const
double getHeight() const
tools::Rectangle & Intersection(const tools::Rectangle &rRect)
tools::Long AdjustRight(tools::Long nHorzMoveDelta)
void setWidth(tools::Long nWidth)
virtual void ClipToPaintRegion(tools::Rectangle &rDstRect)
void Merge(const Color &rMergeColor, sal_uInt8 cTransparency)
bool equalZero(const T &rfVal)
void DrawBitmapEx(const Point &rDestPt, const BitmapEx &rBitmapEx)
This is an overloaded member function, provided for convenience. It differs from the above function o...
tools::Long mnDestX
Definition: salgtype.hxx:44
DrawModeFlags mnDrawMode
Definition: outdev.hxx:216
const std::shared_ptr< SalBitmap > & ImplGetSalBitmap() const
Definition: bitmap.hxx:510
Bitmap const & GetBitmap() const
Definition: alpha.cxx:73
sal_uIntPtr sal_uLong
long Long
constexpr::Color COL_TRANSPARENT(ColorTransparency, 0xFF, 0xFF, 0xFF, 0xFF)
BmpMirrorFlags AdjustTwoRect(SalTwoRect &rTwoRect, const Size &rSizePix)
Definition: rect.cxx:341
std::enable_if< std::is_signed< T >::value||std::is_floating_point< T >::value, long >::type MinMax(T nVal, tools::Long nMin, tools::Long nMax)
bool IsMapModeEnabled() const
Definition: outdev.hxx:1556
vcl::Region maRegion
Definition: outdev.hxx:221
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:241
tools::Long mnOutOffY
Output offset for device output in pixel (pseudo window offset within window system's frames) ...
Definition: outdev.hxx:205
SAL_DLLPRIVATE bool is_double_buffered_window() const
const MapMode & GetMapMode() const
Definition: outdev.hxx:1562
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:543
Size GetSizePixel() const
bool mbMap
Definition: outdev.hxx:236
SAL_DLLPRIVATE Bitmap GetDownsampledBitmap(const Size &rDstSz, const Point &rSrcPt, const Size &rSrcSz, const Bitmap &rBmp, tools::Long nMaxBmpDPIX, tools::Long nMaxBmpDPIY)
Retrieve downsampled and cropped bitmap.
double getMaxX() const
constexpr double rad2deg(double v)
bool Convert(BmpConversion eConversion)
Convert bitmap format.
Definition: BitmapEx.cxx:374
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
void Draw(OutputDevice *pOutDev, const Point &rPos, DrawImageFlags nStyle, const Size *pSize=nullptr)
Definition: Image.cxx:116
constexpr tools::Long Width() const
bool Crop(const tools::Rectangle &rRectPixel)
Crop the bitmap.
ScanlineFormat
Definition: Scanline.hxx:29
SAL_DLLPRIVATE void DrawDeviceAlphaBitmapSlowPath(const Bitmap &rBitmap, const AlphaMask &rAlpha, tools::Rectangle aDstRect, tools::Rectangle aBmpRect, Size const &aOutSz, Point const &aOutPt)
bool IsAlpha() const
Definition: BitmapEx.cxx:193
bool Mirror(BmpMirrorFlags nMirrorFlags)
Mirror the bitmap.
double getWidth() const
tools::Long Left() 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:296
bool isInside(const B2DTuple &rTuple) const
BASEGFX_DLLPUBLIC void transform(const B2DHomMatrix &rMatrix)
B2DHomMatrix createScaleB2DHomMatrix(double fScaleX, double fScaleY)
DrawImageFlags
Scanline GetScanline(tools::Long nY) const
virtual bool CanSubsampleBitmap() const
Definition: outdev.hxx:1379
SAL_DLLPRIVATE void BlendBitmap(const SalTwoRect &rPosAry, const Bitmap &rBmp)
tools::Long Bottom() const
void DrawBitmap(const SalTwoRect &rPosAry, const SalBitmap &rSalBitmap, const OutputDevice &rOutDev)
static bool Filter(BitmapEx &rBmpEx, BitmapFilter const &rFilter)
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
const sal_uLong nVCLLut[256]
SAL_DLLPRIVATE Bitmap BlendBitmapWithAlpha(Bitmap &aBmp, BitmapReadAccess const *pP, BitmapReadAccess const *pA, const tools::Rectangle &aDstRect, const sal_Int32 nOffY, const sal_Int32 nDstHeight, const sal_Int32 nOffX, const sal_Int32 nDstWidth, const tools::Long *pMapX, const tools::Long *pMapY)
double getMaxY() const
tools::Long AdjustBottom(tools::Long nVertMoveDelta)
sal_uInt8 GetBlue() const
B2IRange fround(const B2DRange &rRange)
bool BlendAlphaBitmap(const SalTwoRect &rPosAry, const SalBitmap &rSalSrcBitmap, const SalBitmap &rSalMaskBitmap, const SalBitmap &rSalAlphaBitmap, const OutputDevice &rOutDev)
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:175
SAL_DLLPRIVATE basegfx::B2DHomMatrix ImplGetDeviceTransformation() const
Get device transformation.
Definition: map.cxx:826
void DrawTransformedBitmapEx(const basegfx::B2DHomMatrix &rTransformation, const BitmapEx &rBitmapEx, double fAlpha=1.0)
Draw BitmapEx transformed.
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 SetLineColor()
sal_uInt8 * Scanline
Definition: Scanline.hxx:26
const sal_uLong nVCLBLut[6]
bool IsRTLEnabled() const
Definition: outdev.hxx:1238
bool IsEmpty() const
#define DBG_ASSERT(sCon, aError)
void Erase(sal_uInt8 cTransparency)
Definition: alpha.cxx:78
int i
bool IsClipRegion() const
Definition: outdev.hxx:549
ScanlineFormat GetScanlineFormat() const
bool decompose(B2DTuple &rScale, B2DTuple &rTranslate, double &rRotate, double &rShearX) const
tools::Long FRound(double fVal)
virtual bool AcquireGraphics() const =0
Acquire a graphics device that the output device uses to draw on.
BitmapColor GetColor(tools::Long nY, tools::Long nX) const
void SetSize(const Size &rSize)
#define F_2PI
tools::Long mnOutHeight
Definition: outdev.hxx:207
void SetFillColor()
tools::Long Width() const
SAL_DLLPRIVATE tools::Long ImplLogicWidthToDevicePixel(tools::Long nWidth) const
Convert a logical width to a width in units of device pixels.
Definition: map.cxx:314
SAL_DLLPRIVATE void ImplFillOpaqueRectangle(const tools::Rectangle &rRect)
Used for alpha VDev, to set areas to opaque.
Definition: virdev.cxx:337
void SetRed(sal_uInt8 nRed)
void scale(double fX, double fY)
bool ImplFastBitmapBlending(BitmapWriteAccess const &rDstWA, const BitmapReadAccess &rSrcRA, const BitmapReadAccess &rMskRA, const SalTwoRect &rTR)
Definition: bmpfast.cxx:671
BmpMirrorFlags
Definition: bitmap.hxx:41
RasterOp meRasterOp
Definition: outdev.hxx:228
Size GetOutputSizePixel() const
Definition: outdev.hxx:303
virtual sal_uInt16 GetBitCount() const
Definition: outdev.cxx:315
tools::Long Top() const
Bitmap maAlphaMask
Definition: bitmapex.hxx:466
void DrawBitmap(const Point &rDestPt, const Bitmap &rBitmap)
This is an overloaded member function, provided for convenience. It differs from the above function o...
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
constexpr Point TopLeft() const
tools::Long mnSrcY
Definition: salgtype.hxx:41
bool DrawAlphaBitmap(const SalTwoRect &, const SalBitmap &rSourceBitmap, const SalBitmap &rAlphaBitmap, const OutputDevice &rOutDev)
void SetIndex(sal_uInt8 cIndex)
Definition: BitmapColor.hxx:75
basegfx::B2DRange b2DRectangleFromRectangle(const ::tools::Rectangle &rRect)
MapUnit GetMapUnit()
void SetEmpty()
Bitmap GetBitmap(Color aTransparentReplaceColor) const
Definition: BitmapEx.cxx:203
SAL_WARN_UNUSED_RESULT Point PixelToLogic(const Point &rDevicePt) const
Definition: map.cxx:1066
SAL_WARN_UNUSED_RESULT Point LogicToPixel(const Point &rLogicPt) const
Definition: map.cxx:835
SAL_DLLPRIVATE std::shared_ptr< SalBitmap > const & ImplGetBitmapSalBitmap() const
Definition: bitmapex.hxx:451
const sal_uLong nVCLGLut[6]
sal_uInt8 GetGreen() const
VclPtr< VirtualDevice > mpAlphaVDev
Definition: outdev.hxx:192
constexpr Size GetSize() const
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:533
double getMinY() const
VCL_DLLPUBLIC bool isVCLSkiaEnabled()
AlphaMask GetAlpha() const
Definition: BitmapEx.cxx:215
std::deque< AttacherIndex_Impl > aIndex
#define SAL_WARN_IF(condition, area, stream)
constexpr tools::Long Height() const
unsigned char sal_uInt8
bool Scale(const Size &rNewSize, BmpScaleFlag nScaleFlag=BmpScaleFlag::Default)
Scale the bitmap.
bool less(const T &rfValA, const T &rfValB)
bool Convert(BmpConversion eConversion)
Convert bitmap format.
constexpr sal_uInt8 ColorChannelMerge(sal_uInt8 nDst, sal_uInt8 nSrc, sal_uInt8 nSrcTrans)
void AddAction(const rtl::Reference< MetaAction > &pAction)
Definition: gdimtf.cxx:563
void SetGreen(sal_uInt8 nGreen)
virtual vcl::Region GetActiveClipRegion() const
bool mbInitClipRegion
Definition: outdev.hxx:248
Definition: image.hxx:39
sal_Int64 GetSizeBytes() const
Definition: BitmapEx.cxx:220
virtual void DrawDeviceBitmap(const Point &rDestPt, const Size &rDestSize, const Point &rSrcPtPixel, const Size &rSrcSizePixel, BitmapEx &rBitmapEx)
B2DPoint getMinimum() const
virtual void InitClipRegion()
bool IsEmpty() const
Definition: bitmap.hxx:535
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)
tools::Long mnOutWidth
Definition: outdev.hxx:206
bool Erase(const Color &rFillColor)
Fill the entire bitmap with the given color.
Definition: bitmappaint.cxx:34
void setHeight(tools::Long nHeight)
BitmapEx GetBitmapEx(const Point &rSrcPt, const Size &rSize) const
Query extended bitmap (with alpha channel, if available).
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...
tools::Long mnOutOffX
Output offset for device output in pixel (pseudo window offset within window system's frames) ...
Definition: outdev.hxx:203
const sal_uLong nVCLRLut[6]
double getX() const
double getMinX() const
const BitmapColor & GetPaletteColor(sal_uInt16 nColor) 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:305
const sal_uLong nVCLDitherLut[256]
tools::Long mnDestY
Definition: salgtype.hxx:45
const Size & GetSizePixel() const
Definition: bitmapex.hxx:73
bool IsDeviceOutputNecessary() const
Definition: outdev.hxx:475
void Push(PushFlags nFlags=PushFlags::ALL)
Definition: outdevstate.cxx:59
SAL_DLLPRIVATE tools::Long ImplLogicHeightToDevicePixel(tools::Long nHeight) const
Convert a logical height to a height in units of device pixels.
Definition: map.cxx:322
bool HasFastDrawTransformedBitmap() const
Return true if DrawTransformedBitmapEx() is fast.
tools::Long Right() const
GDIMetaFile * mpMetaFile
Definition: outdev.hxx:178
std::shared_ptr< SalBitmap > GetBitmap(tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, const OutputDevice &rOutDev)
constexpr tools::Long GetHeight() const
SAL_DLLPRIVATE void ImplSetSalBitmap(const std::shared_ptr< SalBitmap > &xImpBmp)