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