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