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 {
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 {
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 {
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 )
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 {
263 
264  if( ImplIsRecordLayout() )
265  return;
266 
267  if( TransparentType::NONE == rBitmapEx.GetTransparentType() )
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 {
282 
283  if( ImplIsRecordLayout() )
284  return;
285 
286  if ( TransparentType::NONE == rBitmapEx.GetTransparentType() )
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 {
302 
303  if( ImplIsRecordLayout() )
304  return;
305 
306  if( TransparentType::NONE == rBitmapEx.GetTransparentType() )
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(), 1 );
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.GetMask() );
349  }
350  }
351  else if( !!aBmpEx )
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  {
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.GetBitCount() > 8 )
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 {
513 
514  if (rBitmapEx.IsAlpha())
515  {
516  DrawDeviceAlphaBitmap(rBitmapEx.GetBitmap(), rBitmapEx.GetAlpha(), rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel);
517  }
518  else if (!!rBitmapEx)
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.ImplGetMaskSalBitmap();
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.GetMask(),
619  rBitmapEx.GetMask()));
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 {
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
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 {
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 {
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.IsTransparent())
1070  {
1071  if(rBitmapEx.IsAlpha())
1072  {
1073  aAlphaBitmap = rBitmapEx.GetAlpha();
1074  }
1075  else
1076  {
1077  aAlphaBitmap = rBitmapEx.GetMask();
1078  }
1079  }
1080  else if (mpAlphaVDev)
1081  {
1082  aAlphaBitmap = AlphaMask(rBitmapEx.GetSizePixel());
1083  aAlphaBitmap.Erase(COL_BLACK); // opaque
1084  }
1085 
1086  SalBitmap* pSalAlphaBmp = aAlphaBitmap.ImplGetSalBitmap().get();
1087 
1089  aNull,
1090  aTopX,
1091  aTopY,
1092  *pSalSrcBmp,
1093  pSalAlphaBmp,
1094  fAlpha,
1095  *this);
1096 
1097  if (mpAlphaVDev)
1098  {
1099  // Merge bitmap alpha to alpha device
1100  AlphaMask aAlpha(rBitmapEx.GetSizePixel());
1101  aAlpha.Erase( ( 1 - fAlpha ) * 255 );
1102  mpAlphaVDev->DrawTransformBitmapExDirect(aFullTransform, BitmapEx(aAlpha, aAlphaBitmap));
1103  }
1104 
1105  return bDone;
1106 };
1107 
1109  const basegfx::B2DHomMatrix& aFullTransform,
1110  basegfx::B2DRange &aVisibleRange,
1111  double &fMaximumArea)
1112 {
1113  // limit TargetRange to existing pixels (if pixel device)
1114  // first get discrete range of object
1115  basegfx::B2DRange aFullPixelRange(aVisibleRange);
1116 
1117  aFullPixelRange.transform(aFullTransform);
1118 
1119  if(basegfx::fTools::equalZero(aFullPixelRange.getWidth()) || basegfx::fTools::equalZero(aFullPixelRange.getHeight()))
1120  {
1121  // object is outside of visible area
1122  return false;
1123  }
1124 
1125  // now get discrete target pixels; start with OutDev pixel size and evtl.
1126  // intersect with active clipping area
1127  basegfx::B2DRange aOutPixel(
1128  0.0,
1129  0.0,
1132 
1133  if(IsClipRegion())
1134  {
1135  tools::Rectangle aRegionRectangle(GetActiveClipRegion().GetBoundRect());
1136 
1137  // caution! Range from rectangle, one too much (!)
1138  aRegionRectangle.AdjustRight(-1);
1139  aRegionRectangle.AdjustBottom(-1);
1140  aOutPixel.intersect( vcl::unotools::b2DRectangleFromRectangle(aRegionRectangle) );
1141  }
1142 
1143  if(aOutPixel.isEmpty())
1144  {
1145  // no active output area
1146  return false;
1147  }
1148 
1149  // if aFullPixelRange is not completely inside of aOutPixel,
1150  // reduction of target pixels is possible
1151  basegfx::B2DRange aVisiblePixelRange(aFullPixelRange);
1152 
1153  if(!aOutPixel.isInside(aFullPixelRange))
1154  {
1155  aVisiblePixelRange.intersect(aOutPixel);
1156 
1157  if(aVisiblePixelRange.isEmpty())
1158  {
1159  // nothing in visible part, reduces to nothing
1160  return false;
1161  }
1162 
1163  // aVisiblePixelRange contains the reduced output area in
1164  // discrete coordinates. To make it useful everywhere, make it relative to
1165  // the object range
1166  basegfx::B2DHomMatrix aMakeVisibleRangeRelative;
1167 
1168  aVisibleRange = aVisiblePixelRange;
1169  aMakeVisibleRangeRelative.translate(
1170  -aFullPixelRange.getMinX(),
1171  -aFullPixelRange.getMinY());
1172  aMakeVisibleRangeRelative.scale(
1173  1.0 / aFullPixelRange.getWidth(),
1174  1.0 / aFullPixelRange.getHeight());
1175  aVisibleRange.transform(aMakeVisibleRangeRelative);
1176  }
1177 
1178  // for pixel devices, do *not* limit size, else OutputDevice::DrawDeviceAlphaBitmap
1179  // will create another, badly scaled bitmap to do the job. Nonetheless, do a
1180  // maximum clipping of something big (1600x1280x2). Add 1.0 to avoid rounding
1181  // errors in rough estimations
1182  const double fNewMaxArea(aVisiblePixelRange.getWidth() * aVisiblePixelRange.getHeight());
1183 
1184  fMaximumArea = std::min(4096000.0, fNewMaxArea + 1.0);
1185 
1186  return true;
1187 }
1188 
1189 // MM02 add some test class to get a simple timer-based output to be able
1190 // to check if it gets faster - and how much. Uncomment next line or set
1191 // DO_TIME_TEST for compile time if you want to use it
1192 // #define DO_TIME_TEST
1193 #ifdef DO_TIME_TEST
1194 #include <tools/time.hxx>
1195 struct LocalTimeTest
1196 {
1197  const sal_uInt64 nStartTime;
1198  LocalTimeTest() : nStartTime(tools::Time::GetSystemTicks()) {}
1199  ~LocalTimeTest()
1200  {
1201  const sal_uInt64 nEndTime(tools::Time::GetSystemTicks());
1202  const sal_uInt64 nDiffTime(nEndTime - nStartTime);
1203 
1204  if(nDiffTime > 0)
1205  {
1206  OStringBuffer aOutput("Time: ");
1207  OString aNumber(OString::number(nDiffTime));
1208  aOutput.append(aNumber);
1209  OSL_FAIL(aOutput.getStr());
1210  }
1211  }
1212 };
1213 #endif
1214 
1216  const basegfx::B2DHomMatrix& rTransformation,
1217  const BitmapEx& rBitmapEx,
1218  double fAlpha)
1219 {
1221 
1222  if( ImplIsRecordLayout() )
1223  return;
1224 
1225  if(rBitmapEx.IsEmpty())
1226  return;
1227 
1228  if(rtl::math::approxEqual( fAlpha, 0.0 ))
1229  return;
1230 
1231  // MM02 compared to other public methods of OutputDevice
1232  // this test was missing and led to zero-ptr-accesses
1233  if ( !mpGraphics && !AcquireGraphics() )
1234  return;
1235 
1236  if ( mbInitClipRegion )
1237  InitClipRegion();
1238 
1239  const bool bMetafile(nullptr != mpMetaFile);
1240  /*
1241  tdf#135325 typically in these OutputDevice methods, for the in
1242  record-to-metafile case the MetaFile is already written to before the
1243  test against mbOutputClipped to determine that output to the current
1244  device would result in no visual output. In this case the metafile is
1245  written after the test, so we must continue past mbOutputClipped if
1246  recording to a metafile. It's typical to record with a device of nominal
1247  size and play back later against something of a totally different size.
1248  */
1249  if (mbOutputClipped && !bMetafile)
1250  return;
1251 
1252 #ifdef DO_TIME_TEST
1253  // MM02 start time test when some data (not for trivial stuff). Will
1254  // trigger and show data when leaving this method by destructing helper
1255  static const char* pEnableBitmapDrawTimerTimer(getenv("SAL_ENABLE_TIMER_BITMAPDRAW"));
1256  static bool bUseTimer(nullptr != pEnableBitmapDrawTimerTimer);
1257  std::unique_ptr<LocalTimeTest> aTimeTest(
1258  bUseTimer && rBitmapEx.GetSizeBytes() > 10000
1259  ? new LocalTimeTest()
1260  : nullptr);
1261 #endif
1262 
1263  BitmapEx bitmapEx = rBitmapEx;
1264 
1265  const bool bInvert(RasterOp::Invert == meRasterOp);
1267  const bool bTryDirectPaint(!bInvert && !bBitmapChangedColor && !bMetafile);
1268  // tdf#130768 CAUTION(!) using GetViewTransformation() is *not* enough here, it may
1269  // be that mnOutOffX/mnOutOffY is used - see AOO bug 75163, mentioned at
1270  // ImplGetDeviceTransformation declaration
1271  basegfx::B2DHomMatrix aFullTransform(ImplGetDeviceTransformation() * rTransformation);
1272 
1273  // First try to handle additional alpha blending, either directly, or modify the bitmap.
1274  if(!rtl::math::approxEqual( fAlpha, 1.0 ))
1275  {
1276  if(bTryDirectPaint)
1277  {
1278  if(DrawTransformBitmapExDirect(aFullTransform, bitmapEx, fAlpha))
1279  {
1280  // we are done
1281  return;
1282  }
1283  }
1284  // Apply the alpha manually.
1285  sal_uInt8 nColor( static_cast<sal_uInt8>( ::basegfx::fround( 255.0*(1.0 - fAlpha) + .5) ) );
1286  AlphaMask aAlpha( bitmapEx.GetSizePixel(), &nColor );
1287  if( bitmapEx.IsTransparent())
1288  aAlpha.BlendWith( bitmapEx.GetAlpha());
1289  bitmapEx = BitmapEx( bitmapEx.GetBitmap(), aAlpha );
1290  }
1291  if(rtl::math::approxEqual( fAlpha, 1.0 ))
1292  fAlpha = 1.0; // avoid the need for approxEqual in backends
1293 
1294  if(bTryDirectPaint && mpGraphics->HasFastDrawTransformedBitmap() && DrawTransformBitmapExDirect(aFullTransform, bitmapEx))
1295  return;
1296 
1297  // decompose matrix to check rotation and shear
1298  basegfx::B2DVector aScale, aTranslate;
1299  double fRotate, fShearX;
1300  rTransformation.decompose(aScale, aTranslate, fRotate, fShearX);
1301  const bool bRotated(!basegfx::fTools::equalZero(fRotate));
1302  const bool bSheared(!basegfx::fTools::equalZero(fShearX));
1303  const bool bMirroredX(basegfx::fTools::less(aScale.getX(), 0.0));
1304  const bool bMirroredY(basegfx::fTools::less(aScale.getY(), 0.0));
1305 
1306  if(!bRotated && !bSheared && !bMirroredX && !bMirroredY)
1307  {
1308  // with no rotation, shear or mirroring it can be mapped to DrawBitmapEx
1309  // do *not* execute the mirroring here, it's done in the fallback
1310  // #i124580# the correct DestSize needs to be calculated based on MaxXY values
1311  Point aDestPt(basegfx::fround(aTranslate.getX()), basegfx::fround(aTranslate.getY()));
1312  const Size aDestSize(
1313  basegfx::fround(aScale.getX() + aTranslate.getX()) - aDestPt.X(),
1314  basegfx::fround(aScale.getY() + aTranslate.getY()) - aDestPt.Y());
1315  const Point aOrigin = GetMapMode().GetOrigin();
1316  if (!bMetafile && comphelper::LibreOfficeKit::isActive() && GetMapMode().GetMapUnit() != MapUnit::MapPixel)
1317  {
1318  aDestPt.Move(aOrigin.getX(), aOrigin.getY());
1319  EnableMapMode(false);
1320  }
1321 
1322  DrawBitmapEx(aDestPt, aDestSize, bitmapEx);
1323  if (!bMetafile && comphelper::LibreOfficeKit::isActive() && GetMapMode().GetMapUnit() != MapUnit::MapPixel)
1324  {
1325  EnableMapMode();
1326  aDestPt.Move(-aOrigin.getX(), -aOrigin.getY());
1327  }
1328  return;
1329  }
1330 
1331  if(bTryDirectPaint && DrawTransformBitmapExDirect(aFullTransform, bitmapEx))
1332  {
1333  // we are done
1334  return;
1335  }
1336 
1337  // take the fallback when no rotate and shear, but mirror (else we would have done this above)
1338  if(!bRotated && !bSheared)
1339  {
1340  // with no rotation or shear it can be mapped to DrawBitmapEx
1341  // do *not* execute the mirroring here, it's done in the fallback
1342  // #i124580# the correct DestSize needs to be calculated based on MaxXY values
1343  const Point aDestPt(basegfx::fround(aTranslate.getX()), basegfx::fround(aTranslate.getY()));
1344  const Size aDestSize(
1345  basegfx::fround(aScale.getX() + aTranslate.getX()) - aDestPt.X(),
1346  basegfx::fround(aScale.getY() + aTranslate.getY()) - aDestPt.Y());
1347 
1348  DrawBitmapEx(aDestPt, aDestSize, bitmapEx);
1349  return;
1350  }
1351 
1352  // at this point we are either sheared or rotated or both
1353  assert(bSheared || bRotated);
1354 
1355  // fallback; create transformed bitmap the hard way (back-transform
1356  // the pixels) and paint
1357  basegfx::B2DRange aVisibleRange(0.0, 0.0, 1.0, 1.0);
1358 
1359  // limit maximum area to something looking good for non-pixel-based targets (metafile, printer)
1360  // by using a fixed minimum (allow at least, but no need to utilize) for good smoothing and an area
1361  // dependent of original size for good quality when e.g. rotated/sheared. Still, limit to a maximum
1362  // to avoid crashes/resource problems (ca. 1500x3000 here)
1363  const Size& rOriginalSizePixel(bitmapEx.GetSizePixel());
1364  const double fOrigArea(rOriginalSizePixel.Width() * rOriginalSizePixel.Height() * 0.5);
1365  const double fOrigAreaScaled(fOrigArea * 1.44);
1366  double fMaximumArea(std::clamp(fOrigAreaScaled, 1000000.0, 4500000.0));
1367 
1368  if(!bMetafile)
1369  {
1370  if ( !TransformAndReduceBitmapExToTargetRange( aFullTransform, aVisibleRange, fMaximumArea ) )
1371  return;
1372  }
1373 
1374  if(aVisibleRange.isEmpty())
1375  return;
1376 
1377  BitmapEx aTransformed(bitmapEx);
1378 
1379  // #122923# when the result needs an alpha channel due to being rotated or sheared
1380  // and thus uncovering areas, add these channels so that the own transformer (used
1381  // in getTransformed) also creates a transformed alpha channel
1382  if(!aTransformed.IsTransparent() && (bSheared || bRotated))
1383  {
1384  // parts will be uncovered, extend aTransformed with a mask bitmap
1385  const Bitmap aContent(aTransformed.GetBitmap());
1386 
1387  AlphaMask aMaskBmp(aContent.GetSizePixel());
1388  aMaskBmp.Erase(0);
1389 
1390  aTransformed = BitmapEx(aContent, aMaskBmp);
1391  }
1392 
1393  // Remove scaling from aFulltransform: we transform due to shearing or rotation, scaling
1394  // will happen according to aDestSize.
1395  basegfx::B2DVector aFullScale, aFullTranslate;
1396  double fFullRotate, fFullShearX;
1397  aFullTransform.decompose(aFullScale, aFullTranslate, fFullRotate, fFullShearX);
1398  // Require positive scaling, negative scaling would loose horizontal or vertical flip.
1399  if (aFullScale.getX() > 0 && aFullScale.getY() > 0)
1400  {
1402  rOriginalSizePixel.getWidth() / aFullScale.getX(),
1403  rOriginalSizePixel.getHeight() / aFullScale.getY());
1404  aFullTransform *= aTransform;
1405  }
1406 
1407  double fSourceRatio = 1.0;
1408  if (rOriginalSizePixel.getHeight() != 0)
1409  {
1410  fSourceRatio = rOriginalSizePixel.getWidth() / rOriginalSizePixel.getHeight();
1411  }
1412  double fTargetRatio = 1.0;
1413  if (aFullScale.getY() != 0)
1414  {
1415  fTargetRatio = aFullScale.getX() / aFullScale.getY();
1416  }
1417  bool bAspectRatioKept = rtl::math::approxEqual(fSourceRatio, fTargetRatio);
1418  if (bSheared || !bAspectRatioKept)
1419  {
1420  // Not only rotation, or scaling does not keep aspect ratio.
1421  aTransformed = aTransformed.getTransformed(
1422  aFullTransform,
1423  aVisibleRange,
1424  fMaximumArea);
1425  }
1426  else
1427  {
1428  // Just rotation, can do that directly.
1429  fFullRotate = fmod(fFullRotate * -1, F_2PI);
1430  if (fFullRotate < 0)
1431  {
1432  fFullRotate += F_2PI;
1433  }
1434  Degree10 nAngle10(basegfx::fround(basegfx::rad2deg(fFullRotate) * 10));
1435  aTransformed.Rotate(nAngle10, COL_TRANSPARENT);
1436  }
1437  basegfx::B2DRange aTargetRange(0.0, 0.0, 1.0, 1.0);
1438 
1439  // get logic object target range
1440  aTargetRange.transform(rTransformation);
1441 
1442  // get from unified/relative VisibleRange to logoc one
1443  aVisibleRange.transform(
1445  aTargetRange.getRange(),
1446  aTargetRange.getMinimum()));
1447 
1448  // extract point and size; do not remove size, the bitmap may have been prepared reduced by purpose
1449  // #i124580# the correct DestSize needs to be calculated based on MaxXY values
1450  const Point aDestPt(basegfx::fround(aVisibleRange.getMinX()), basegfx::fround(aVisibleRange.getMinY()));
1451  const Size aDestSize(
1452  basegfx::fround(aVisibleRange.getMaxX()) - aDestPt.X(),
1453  basegfx::fround(aVisibleRange.getMaxY()) - aDestPt.Y());
1454 
1455  DrawBitmapEx(aDestPt, aDestSize, aTransformed);
1456 }
1457 
1459 {
1460  if( ImplIsRecordLayout() )
1461  return false;
1462 
1463  if ( !mpGraphics && !AcquireGraphics() )
1464  return false;
1465 
1467 }
1468 
1469 void OutputDevice::DrawImage( const Point& rPos, const Image& rImage, DrawImageFlags nStyle )
1470 {
1472 
1473  DrawImage( rPos, Size(), rImage, nStyle );
1474 }
1475 
1476 void OutputDevice::DrawImage( const Point& rPos, const Size& rSize,
1477  const Image& rImage, DrawImageFlags nStyle )
1478 {
1480 
1481  bool bIsSizeValid = !rSize.IsEmpty();
1482 
1483  if (!ImplIsRecordLayout())
1484  {
1485  Image& rNonConstImage = const_cast<Image&>(rImage);
1486  if (bIsSizeValid)
1487  rNonConstImage.Draw(this, rPos, nStyle, &rSize);
1488  else
1489  rNonConstImage.Draw(this, rPos, nStyle);
1490  }
1491 }
1492 
1493 namespace
1494 {
1495  // Co = Cs + Cd*(1-As) premultiplied alpha -or-
1496  // Co = (AsCs + AdCd*(1-As)) / Ao
1497  sal_uInt8 CalcColor( const sal_uInt8 nSourceColor, const sal_uInt8 nSourceAlpha,
1498  const sal_uInt8 nDstAlpha, const sal_uInt8 nResAlpha, const sal_uInt8 nDestColor )
1499  {
1500  int c = nResAlpha ? ( static_cast<int>(nSourceAlpha)*nSourceColor + static_cast<int>(nDstAlpha)*nDestColor -
1501  static_cast<int>(nDstAlpha)*nDestColor*nSourceAlpha/255 ) / static_cast<int>(nResAlpha) : 0;
1502  return sal_uInt8( c );
1503  }
1504 
1505  BitmapColor AlphaBlend( int nX, int nY,
1506  const tools::Long nMapX,
1507  const tools::Long nMapY,
1508  BitmapReadAccess const * pP,
1509  BitmapReadAccess const * pA,
1510  BitmapReadAccess const * pB,
1511  BitmapWriteAccess const * pAlphaW,
1512  sal_uInt8& nResAlpha )
1513  {
1514  BitmapColor aDstCol,aSrcCol;
1515  aSrcCol = pP->GetColor( nMapY, nMapX );
1516  aDstCol = pB->GetColor( nY, nX );
1517 
1518  // vcl stores transparency, not alpha - invert it
1519  const sal_uInt8 nSrcAlpha = 255 - pA->GetPixelIndex( nMapY, nMapX );
1520  const sal_uInt8 nDstAlpha = 255 - pAlphaW->GetPixelIndex( nY, nX );
1521 
1522  // Perform porter-duff compositing 'over' operation
1523 
1524  // Co = Cs + Cd*(1-As)
1525  // Ad = As + Ad*(1-As)
1526  nResAlpha = static_cast<int>(nSrcAlpha) + static_cast<int>(nDstAlpha) - static_cast<int>(nDstAlpha)*nSrcAlpha/255;
1527 
1528  aDstCol.SetRed( CalcColor( aSrcCol.GetRed(), nSrcAlpha, nDstAlpha, nResAlpha, aDstCol.GetRed() ) );
1529  aDstCol.SetBlue( CalcColor( aSrcCol.GetBlue(), nSrcAlpha, nDstAlpha, nResAlpha, aDstCol.GetBlue() ) );
1530  aDstCol.SetGreen( CalcColor( aSrcCol.GetGreen(), nSrcAlpha, nDstAlpha, nResAlpha, aDstCol.GetGreen() ) );
1531 
1532  return aDstCol;
1533  }
1534 }
1535 
1537  const SalTwoRect& rPosAry,
1538  const Bitmap& rBmp )
1539 {
1540  mpGraphics->BlendBitmap( rPosAry, *rBmp.ImplGetSalBitmap(), *this );
1541 }
1542 
1544  Bitmap& aBmp,
1545  BitmapReadAccess const * pP,
1546  BitmapReadAccess const * pA,
1547  const tools::Rectangle& aDstRect,
1548  const sal_Int32 nOffY,
1549  const sal_Int32 nDstHeight,
1550  const sal_Int32 nOffX,
1551  const sal_Int32 nDstWidth,
1552  const tools::Long* pMapX,
1553  const tools::Long* pMapY )
1554 
1555 {
1556  BitmapColor aDstCol;
1557  Bitmap res;
1558  int nX, nY;
1559  sal_uInt8 nResAlpha;
1560 
1561  SAL_WARN_IF( !mpAlphaVDev, "vcl.gdi", "BlendBitmapWithAlpha(): call me only with valid alpha VirtualDevice!" );
1562 
1563  bool bOldMapMode( mpAlphaVDev->IsMapModeEnabled() );
1564  mpAlphaVDev->EnableMapMode(false);
1565 
1566  Bitmap aAlphaBitmap( mpAlphaVDev->GetBitmap( aDstRect.TopLeft(), aDstRect.GetSize() ) );
1567  BitmapScopedWriteAccess pAlphaW(aAlphaBitmap);
1568 
1569  if( GetBitCount() <= 8 )
1570  {
1571  Bitmap aDither( aBmp.GetSizePixel(), 8 );
1572  BitmapColor aIndex( 0 );
1573  Bitmap::ScopedReadAccess pB(aBmp);
1574  BitmapScopedWriteAccess pW(aDither);
1575 
1576  if (pB && pP && pA && pW && pAlphaW)
1577  {
1578  int nOutY;
1579 
1580  for( nY = 0, nOutY = nOffY; nY < nDstHeight; nY++, nOutY++ )
1581  {
1582  const tools::Long nMapY = pMapY[ nY ];
1583  const tools::Long nModY = ( nOutY & 0x0FL ) << 4;
1584  int nOutX;
1585 
1586  Scanline pScanline = pW->GetScanline(nY);
1587  Scanline pScanlineAlpha = pAlphaW->GetScanline(nY);
1588  for( nX = 0, nOutX = nOffX; nX < nDstWidth; nX++, nOutX++ )
1589  {
1590  const tools::Long nMapX = pMapX[ nX ];
1591  const sal_uLong nD = nVCLDitherLut[ nModY | ( nOutX & 0x0FL ) ];
1592 
1593  aDstCol = AlphaBlend( nX, nY, nMapX, nMapY, pP, pA, pB.get(), pAlphaW.get(), nResAlpha );
1594 
1595  aIndex.SetIndex( static_cast<sal_uInt8>( nVCLRLut[ ( nVCLLut[ aDstCol.GetRed() ] + nD ) >> 16 ] +
1596  nVCLGLut[ ( nVCLLut[ aDstCol.GetGreen() ] + nD ) >> 16 ] +
1597  nVCLBLut[ ( nVCLLut[ aDstCol.GetBlue() ] + nD ) >> 16 ] ) );
1598  pW->SetPixelOnData( pScanline, nX, aIndex );
1599 
1600  aIndex.SetIndex( static_cast<sal_uInt8>( nVCLRLut[ ( nVCLLut[ 255-nResAlpha ] + nD ) >> 16 ] +
1601  nVCLGLut[ ( nVCLLut[ 255-nResAlpha ] + nD ) >> 16 ] +
1602  nVCLBLut[ ( nVCLLut[ 255-nResAlpha ] + nD ) >> 16 ] ) );
1603  pAlphaW->SetPixelOnData( pScanlineAlpha, nX, aIndex );
1604  }
1605  }
1606  }
1607  pB.reset();
1608  pW.reset();
1609  res = aDither;
1610  }
1611  else
1612  {
1613  BitmapScopedWriteAccess pB(aBmp);
1614  if (pB && pP && pA && pAlphaW)
1615  {
1616  for( nY = 0; nY < nDstHeight; nY++ )
1617  {
1618  const tools::Long nMapY = pMapY[ nY ];
1619  Scanline pScanlineB = pB->GetScanline(nY);
1620  Scanline pScanlineAlpha = pAlphaW->GetScanline(nY);
1621 
1622  for( nX = 0; nX < nDstWidth; nX++ )
1623  {
1624  const tools::Long nMapX = pMapX[ nX ];
1625  aDstCol = AlphaBlend( nX, nY, nMapX, nMapY, pP, pA, pB.get(), pAlphaW.get(), nResAlpha );
1626 
1627  pB->SetPixelOnData(pScanlineB, nX, pB->GetBestMatchingColor(aDstCol));
1628  pAlphaW->SetPixelOnData(pScanlineAlpha, nX, pB->GetBestMatchingColor(Color(255L-nResAlpha, 255L-nResAlpha, 255L-nResAlpha)));
1629  }
1630  }
1631  }
1632  pB.reset();
1633  res = aBmp;
1634  }
1635 
1636  pAlphaW.reset();
1637  mpAlphaVDev->DrawBitmap( aDstRect.TopLeft(), aAlphaBitmap );
1638  mpAlphaVDev->EnableMapMode( bOldMapMode );
1639 
1640  return res;
1641 }
1642 
1644  Bitmap& aBmp,
1645  BitmapReadAccess const * pP,
1646  BitmapReadAccess const * pA,
1647  const sal_Int32 nOffY,
1648  const sal_Int32 nDstHeight,
1649  const sal_Int32 nOffX,
1650  const sal_Int32 nDstWidth,
1651  const tools::Rectangle& aBmpRect,
1652  const Size& aOutSz,
1653  const bool bHMirr,
1654  const bool bVMirr,
1655  const tools::Long* pMapX,
1656  const tools::Long* pMapY )
1657 {
1658  BitmapColor aDstCol;
1659  Bitmap res;
1660  int nX, nY;
1661 
1662  if( GetBitCount() <= 8 )
1663  {
1664  Bitmap aDither( aBmp.GetSizePixel(), 8 );
1665  BitmapColor aIndex( 0 );
1666  Bitmap::ScopedReadAccess pB(aBmp);
1667  BitmapScopedWriteAccess pW(aDither);
1668 
1669  if( pB && pP && pA && pW )
1670  {
1671  int nOutY;
1672 
1673  for( nY = 0, nOutY = nOffY; nY < nDstHeight; nY++, nOutY++ )
1674  {
1675  tools::Long nMapY = pMapY[ nY ];
1676  if (bVMirr)
1677  {
1678  nMapY = aBmpRect.Bottom() - nMapY;
1679  }
1680  const tools::Long nModY = ( nOutY & 0x0FL ) << 4;
1681  int nOutX;
1682 
1683  Scanline pScanline = pW->GetScanline(nY);
1684  Scanline pScanlineAlpha = pA->GetScanline(nMapY);
1685  for( nX = 0, nOutX = nOffX; nX < nDstWidth; nX++, nOutX++ )
1686  {
1687  tools::Long nMapX = pMapX[ nX ];
1688  if (bHMirr)
1689  {
1690  nMapX = aBmpRect.Right() - nMapX;
1691  }
1692  const sal_uLong nD = nVCLDitherLut[ nModY | ( nOutX & 0x0FL ) ];
1693 
1694  aDstCol = pB->GetColor( nY, nX );
1695  aDstCol.Merge( pP->GetColor( nMapY, nMapX ), pA->GetIndexFromData( pScanlineAlpha, nMapX ) );
1696  aIndex.SetIndex( static_cast<sal_uInt8>( nVCLRLut[ ( nVCLLut[ aDstCol.GetRed() ] + nD ) >> 16 ] +
1697  nVCLGLut[ ( nVCLLut[ aDstCol.GetGreen() ] + nD ) >> 16 ] +
1698  nVCLBLut[ ( nVCLLut[ aDstCol.GetBlue() ] + nD ) >> 16 ] ) );
1699  pW->SetPixelOnData( pScanline, nX, aIndex );
1700  }
1701  }
1702  }
1703 
1704  pB.reset();
1705  pW.reset();
1706  res = aDither;
1707  }
1708  else
1709  {
1710  BitmapScopedWriteAccess pB(aBmp);
1711 
1712  bool bFastBlend = false;
1713  if( pP && pA && pB && !bHMirr && !bVMirr )
1714  {
1715  SalTwoRect aTR(aBmpRect.Left(), aBmpRect.Top(), aBmpRect.GetWidth(), aBmpRect.GetHeight(),
1716  nOffX, nOffY, aOutSz.Width(), aOutSz.Height());
1717 
1718  bFastBlend = ImplFastBitmapBlending( *pB,*pP,*pA, aTR );
1719  }
1720 
1721  if( pP && pA && pB && !bFastBlend )
1722  {
1723  switch( pP->GetScanlineFormat() )
1724  {
1726  {
1727  for( nY = 0; nY < nDstHeight; nY++ )
1728  {
1729  tools::Long nMapY = pMapY[ nY ];
1730  if ( bVMirr )
1731  {
1732  nMapY = aBmpRect.Bottom() - nMapY;
1733  }
1734  Scanline pPScan = pP->GetScanline( nMapY );
1735  Scanline pAScan = pA->GetScanline( nMapY );
1736  Scanline pBScan = pB->GetScanline( nY );
1737 
1738  for( nX = 0; nX < nDstWidth; nX++ )
1739  {
1740  tools::Long nMapX = pMapX[ nX ];
1741 
1742  if ( bHMirr )
1743  {
1744  nMapX = aBmpRect.Right() - nMapX;
1745  }
1746  aDstCol = pB->GetPixelFromData( pBScan, nX );
1747  aDstCol.Merge( pP->GetPaletteColor( pPScan[ nMapX ] ), pAScan[ nMapX ] );
1748  pB->SetPixelOnData( pBScan, nX, aDstCol );
1749  }
1750  }
1751  }
1752  break;
1753 
1754  default:
1755  {
1756 
1757  for( nY = 0; nY < nDstHeight; nY++ )
1758  {
1759  tools::Long nMapY = pMapY[ nY ];
1760 
1761  if ( bVMirr )
1762  {
1763  nMapY = aBmpRect.Bottom() - nMapY;
1764  }
1765  Scanline pAScan = pA->GetScanline( nMapY );
1766  Scanline pBScan = pB->GetScanline(nY);
1767  for( nX = 0; nX < nDstWidth; nX++ )
1768  {
1769  tools::Long nMapX = pMapX[ nX ];
1770 
1771  if ( bHMirr )
1772  {
1773  nMapX = aBmpRect.Right() - nMapX;
1774  }
1775  aDstCol = pB->GetPixelFromData( pBScan, nX );
1776  aDstCol.Merge( pP->GetColor( nMapY, nMapX ), pAScan[ nMapX ] );
1777  pB->SetPixelOnData( pBScan, nX, aDstCol );
1778  }
1779  }
1780  }
1781  break;
1782  }
1783  }
1784 
1785  pB.reset();
1786  res = aBmp;
1787  }
1788 
1789  return res;
1790 }
1791 
1792 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Point TopLeft() const
sal_uInt8 GetIndexFromData(const sal_uInt8 *pData, tools::Long nX) const
Bitmap GetMask() const
Definition: BitmapEx.cxx:253
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:632
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)
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:45
DrawModeFlags mnDrawMode
Definition: outdev.hxx:354
const std::shared_ptr< SalBitmap > & ImplGetSalBitmap() const
Definition: bitmap.hxx:525
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:336
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:1682
vcl::Region maRegion
Definition: outdev.hxx:359
double getX() const
BitmapEx getTransformed(const basegfx::B2DHomMatrix &rTransformation, const basegfx::B2DRange &rVisibleRange, double fMaximumArea) const
Create transformed Bitmap.
Definition: BitmapEx.cxx:932
bool mbOutputClipped
Definition: outdev.hxx:379
tools::Long mnOutOffY
Output offset for device output in pixel (pseudo window offset within window system's frames) ...
Definition: outdev.hxx:343
SAL_DLLPRIVATE bool is_double_buffered_window() const
const MapMode & GetMapMode() const
Definition: outdev.hxx:1688
double getY() const
B2DVector getRange() const
tools::Long mnSrcHeight
Definition: salgtype.hxx:44
tools::Long GetWidth() const
tools::Long mnSrcWidth
Definition: salgtype.hxx:43
void EnableMapMode(bool bEnable=true)
Definition: map.cxx:508
Size GetSizePixel() const
bool mbMap
Definition: outdev.hxx:374
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:449
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:47
void Draw(OutputDevice *pOutDev, const Point &rPos, DrawImageFlags nStyle, const Size *pSize=nullptr)
Definition: Image.cxx:116
bool IsEmpty() const
bool Crop(const tools::Rectangle &rRectPixel)
Crop the bitmap.
void SetPixelOnData(sal_uInt8 *pData, tools::Long nX, const BitmapColor &rBitmapColor)
ScanlineFormat
Definition: Scanline.hxx:28
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:221
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
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
bool isInside(const B2DTuple &rTuple) const
BASEGFX_DLLPUBLIC void transform(const B2DHomMatrix &rMatrix)
B2DHomMatrix createScaleB2DHomMatrix(double fScaleX, double fScaleY)
Scanline GetScanline(tools::Long nY) const
virtual bool CanSubsampleBitmap() const
Definition: outdev.hxx:1505
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
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
static bool less(const double &rfValA, const double &rfValB)
bool Mirror(BmpMirrorFlags nMirrorFlags)
Mirror the bitmap.
Definition: BitmapEx.cxx:328
SalGraphics * mpGraphics
Graphics context to draw on.
Definition: outdev.hxx:315
SAL_DLLPRIVATE basegfx::B2DHomMatrix ImplGetDeviceTransformation() const
Get device transformation.
Definition: map.cxx:791
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:198
void BlendWith(const Bitmap &rOther)
Definition: alpha.cxx:144
tools::Long mnDestHeight
Definition: salgtype.hxx:48
void SetLineColor()
sal_uInt8 * Scanline
Definition: Scanline.hxx:25
const sal_uLong nVCLBLut[6]
bool IsRTLEnabled() const
Definition: outdev.hxx:1364
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:679
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
static bool equalZero(const double &rfVal)
tools::Long mnOutHeight
Definition: outdev.hxx:345
void SetFillColor()
tools::Long Width() const
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:348
sal_uLong GetSizeBytes() const
Definition: BitmapEx.cxx:277
void SetRed(sal_uInt8 nRed)
TransparentType GetTransparentType() const
Definition: bitmapex.hxx:74
void scale(double fX, double fY)
bool ImplFastBitmapBlending(BitmapWriteAccess const &rDstWA, const BitmapReadAccess &rSrcRA, const BitmapReadAccess &rMskRA, const SalTwoRect &rTR)
Definition: bmpfast.cxx:677
BmpMirrorFlags
Definition: bitmap.hxx:41
RasterOp meRasterOp
Definition: outdev.hxx:366
Size GetOutputSizePixel() const
Definition: outdev.hxx:441
virtual sal_uInt16 GetBitCount() const
Definition: outdev.cxx:302
bool IsTransparent() const
Definition: BitmapEx.cxx:216
tools::Long Top() const
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:385
void intersect(const B2DRange &rRange)
Size GetSize() const
tools::Long mnSrcX
Definition: salgtype.hxx:41
tools::Long mnSrcY
Definition: salgtype.hxx:42
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()
SAL_DLLPRIVATE std::shared_ptr< SalBitmap > const & ImplGetMaskSalBitmap() const
Definition: bitmapex.hxx:461
void SetEmpty()
Bitmap GetBitmap(Color aTransparentReplaceColor) const
Definition: BitmapEx.cxx:231
Point PixelToLogic(const Point &rDevicePt) const
Definition: map.cxx:1021
Point LogicToPixel(const Point &rLogicPt) const
Definition: map.cxx:800
SAL_DLLPRIVATE std::shared_ptr< SalBitmap > const & ImplGetBitmapSalBitmap() const
Definition: bitmapex.hxx:460
const sal_uLong nVCLGLut[6]
sal_uInt8 GetGreen() const
VclPtr< VirtualDevice > mpAlphaVDev
Definition: outdev.hxx:330
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:498
double getMinY() const
VCL_DLLPUBLIC bool isVCLSkiaEnabled()
AlphaMask GetAlpha() const
Definition: BitmapEx.cxx:263
std::deque< AttacherIndex_Impl > aIndex
#define SAL_WARN_IF(condition, area, stream)
unsigned char sal_uInt8
bool Scale(const Size &rNewSize, BmpScaleFlag nScaleFlag=BmpScaleFlag::Default)
Scale the bitmap.
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:564
void SetGreen(sal_uInt8 nGreen)
virtual vcl::Region GetActiveClipRegion() const
bool mbInitClipRegion
Definition: outdev.hxx:386
Definition: image.hxx:39
virtual void DrawDeviceBitmap(const Point &rDestPt, const Size &rDestSize, const Point &rSrcPtPixel, const Size &rSrcSizePixel, BitmapEx &rBitmapEx)
tools::Long Height() const
B2DPoint getMinimum() const
virtual void InitClipRegion()
bool IsEmpty() const
Definition: bitmap.hxx:556
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_BLACK
BitmapColor GetPixelFromData(const sal_uInt8 *pData, tools::Long nX) const
const Point & GetOrigin() const
Definition: mapmod.cxx:170
static sal_uInt64 GetSystemTicks()
void translate(double fX, double fY)
tools::Long mnOutWidth
Definition: outdev.hxx:344
bool Erase(const Color &rFillColor)
Fill the entire bitmap with the given color.
Definition: bitmappaint.cxx:34
void setHeight(tools::Long nHeight)
tools::Long GetHeight() const
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:341
const sal_uLong nVCLRLut[6]
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:46
const Size & GetSizePixel() const
Definition: bitmapex.hxx:84
bool IsDeviceOutputNecessary() const
Definition: outdev.hxx:605
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
BitmapColor GetBestMatchingColor(const BitmapColor &rBitmapColor)
bool HasFastDrawTransformedBitmap() const
Return true if DrawTransformedBitmapEx() is fast.
DrawImageFlags
Definition: outdev.hxx:171
tools::Long Right() const
GDIMetaFile * mpMetaFile
Definition: outdev.hxx:318
std::shared_ptr< SalBitmap > GetBitmap(tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, const OutputDevice &rOutDev)
SAL_DLLPRIVATE void ImplSetSalBitmap(const std::shared_ptr< SalBitmap > &xImpBmp)