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