xref: /core/vcl/source/outdev/bitmap.cxx (revision 29df1afa)
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 <config_features.h>
21 
22 #include <osl/diagnose.h>
23 #include <tools/debug.hxx>
24 #include <tools/helpers.hxx>
25 
26 #include <vcl/image.hxx>
27 #include <vcl/metaact.hxx>
28 #include <vcl/skia/SkiaHelper.hxx>
29 #include <vcl/virdev.hxx>
30 #include <vcl/BitmapWriteAccess.hxx>
31 
32 #include <bitmap/bmpfast.hxx>
33 #include <drawmode.hxx>
34 #include <salbmp.hxx>
35 #include <salgdi.hxx>
36 
DrawBitmap(const Point & rDestPt,const Bitmap & rBitmap)37 void OutputDevice::DrawBitmap( const Point& rDestPt, const Bitmap& rBitmap )
38 {
39     assert(!is_double_buffered_window());
40 
41     const Size aSizePix( rBitmap.GetSizePixel() );
42     DrawBitmap( rDestPt, PixelToLogic( aSizePix ), Point(), aSizePix, rBitmap, MetaActionType::BMP );
43 }
44 
DrawBitmap(const Point & rDestPt,const Size & rDestSize,const Bitmap & rBitmap)45 void OutputDevice::DrawBitmap( const Point& rDestPt, const Size& rDestSize, const Bitmap& rBitmap )
46 {
47     assert(!is_double_buffered_window());
48 
49     DrawBitmap( rDestPt, rDestSize, Point(), rBitmap.GetSizePixel(), rBitmap, MetaActionType::BMPSCALE );
50 }
51 
DrawBitmap(const Point & rDestPt,const Size & rDestSize,const Point & rSrcPtPixel,const Size & rSrcSizePixel,const Bitmap & rBitmap)52 void OutputDevice::DrawBitmap( const Point& rDestPt, const Size& rDestSize,
53                                    const Point& rSrcPtPixel, const Size& rSrcSizePixel,
54                                    const Bitmap& rBitmap)
55 {
56     assert(!is_double_buffered_window());
57 
58     DrawBitmap( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, rBitmap, MetaActionType::BMPSCALEPART );
59 }
60 
DrawBitmap(const Point & rDestPt,const Size & rDestSize,const Point & rSrcPtPixel,const Size & rSrcSizePixel,const Bitmap & rBitmap,const MetaActionType nAction)61 void OutputDevice::DrawBitmap( const Point& rDestPt, const Size& rDestSize,
62                                    const Point& rSrcPtPixel, const Size& rSrcSizePixel,
63                                    const Bitmap& rBitmap, const MetaActionType nAction )
64 {
65     assert(!is_double_buffered_window());
66 
67     if( ImplIsRecordLayout() )
68         return;
69 
70     if ( RasterOp::Invert == meRasterOp )
71     {
72         DrawRect( tools::Rectangle( rDestPt, rDestSize ) );
73         return;
74     }
75 
76     Bitmap aBmp( rBitmap );
77 
78     if ( mnDrawMode & ( DrawModeFlags::BlackBitmap | DrawModeFlags::WhiteBitmap |
79                              DrawModeFlags::GrayBitmap ) )
80     {
81         if ( mnDrawMode & ( DrawModeFlags::BlackBitmap | DrawModeFlags::WhiteBitmap ) )
82         {
83             sal_uInt8 cCmpVal;
84 
85             if ( mnDrawMode & DrawModeFlags::BlackBitmap )
86                 cCmpVal = 0;
87             else
88                 cCmpVal = 255;
89 
90             Color aCol( cCmpVal, cCmpVal, cCmpVal );
91             Push( vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR );
92             SetLineColor( aCol );
93             SetFillColor( aCol );
94             DrawRect( tools::Rectangle( rDestPt, rDestSize ) );
95             Pop();
96             return;
97         }
98         else if( !aBmp.IsEmpty() )
99         {
100             if ( mnDrawMode & DrawModeFlags::GrayBitmap )
101                 aBmp.Convert( BmpConversion::N8BitGreys );
102         }
103     }
104 
105     if ( mpMetaFile )
106     {
107         switch( nAction )
108         {
109             case MetaActionType::BMP:
110                 mpMetaFile->AddAction( new MetaBmpAction( rDestPt, aBmp ) );
111             break;
112 
113             case MetaActionType::BMPSCALE:
114                 mpMetaFile->AddAction( new MetaBmpScaleAction( rDestPt, rDestSize, aBmp ) );
115             break;
116 
117             case MetaActionType::BMPSCALEPART:
118                 mpMetaFile->AddAction( new MetaBmpScalePartAction(
119                     rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, aBmp ) );
120             break;
121 
122             default: break;
123         }
124     }
125 
126     if ( !IsDeviceOutputNecessary() )
127         return;
128 
129     if (!mpGraphics && !AcquireGraphics())
130         return;
131     assert(mpGraphics);
132 
133     if ( mbInitClipRegion )
134         InitClipRegion();
135 
136     if ( mbOutputClipped )
137         return;
138 
139     if( !aBmp.IsEmpty() )
140     {
141         SalTwoRect aPosAry(rSrcPtPixel.X(), rSrcPtPixel.Y(), rSrcSizePixel.Width(), rSrcSizePixel.Height(),
142                            ImplLogicXToDevicePixel(rDestPt.X()), ImplLogicYToDevicePixel(rDestPt.Y()),
143                            ImplLogicWidthToDevicePixel(rDestSize.Width()),
144                            ImplLogicHeightToDevicePixel(rDestSize.Height()));
145 
146         if ( aPosAry.mnSrcWidth && aPosAry.mnSrcHeight && aPosAry.mnDestWidth && aPosAry.mnDestHeight )
147         {
148             const BmpMirrorFlags nMirrFlags = AdjustTwoRect( aPosAry, aBmp.GetSizePixel() );
149 
150             if ( nMirrFlags != BmpMirrorFlags::NONE )
151                 aBmp.Mirror( nMirrFlags );
152 
153             if ( aPosAry.mnSrcWidth && aPosAry.mnSrcHeight && aPosAry.mnDestWidth && aPosAry.mnDestHeight )
154             {
155                 if (nAction == MetaActionType::BMPSCALE && CanSubsampleBitmap())
156                 {
157                     double nScaleX = aPosAry.mnDestWidth  / static_cast<double>(aPosAry.mnSrcWidth);
158                     double nScaleY = aPosAry.mnDestHeight / static_cast<double>(aPosAry.mnSrcHeight);
159 
160                     // If subsampling, use Bitmap::Scale() for subsampling of better quality.
161 
162                     // but hidpi surfaces like the cairo one have their own scale, so don't downscale
163                     // past the surface scaling which can retain the extra detail
164                     double fScale(1.0);
165                     if (mpGraphics->ShouldDownscaleIconsAtSurface(&fScale))
166                     {
167                         nScaleX *= fScale;
168                         nScaleY *= fScale;
169                     }
170 
171                     if ( nScaleX < 1.0 || nScaleY < 1.0 )
172                     {
173                         aBmp.Scale(nScaleX, nScaleY);
174                         aPosAry.mnSrcWidth = aPosAry.mnDestWidth * fScale;
175                         aPosAry.mnSrcHeight = aPosAry.mnDestHeight * fScale;
176                     }
177                 }
178 
179                 mpGraphics->DrawBitmap( aPosAry, *aBmp.ImplGetSalBitmap(), *this );
180             }
181         }
182     }
183 
184     if( mpAlphaVDev )
185     {
186         // #i32109#: Make bitmap area opaque
187         mpAlphaVDev->ImplFillOpaqueRectangle( tools::Rectangle(rDestPt, rDestSize) );
188     }
189 }
190 
GetBitmap(const Point & rSrcPt,const Size & rSize) const191 Bitmap OutputDevice::GetBitmap( const Point& rSrcPt, const Size& rSize ) const
192 {
193     if ( !mpGraphics && !AcquireGraphics() )
194         return Bitmap();
195 
196     assert(mpGraphics);
197 
198     tools::Long    nX = ImplLogicXToDevicePixel( rSrcPt.X() );
199     tools::Long    nY = ImplLogicYToDevicePixel( rSrcPt.Y() );
200     tools::Long    nWidth = ImplLogicWidthToDevicePixel( rSize.Width() );
201     tools::Long    nHeight = ImplLogicHeightToDevicePixel( rSize.Height() );
202     if ( nWidth <= 0 || nHeight <= 0 || nX > (mnOutWidth + mnOutOffX) || nY > (mnOutHeight + mnOutOffY))
203         return Bitmap();
204 
205     Bitmap  aBmp;
206     tools::Rectangle   aRect( Point( nX, nY ), Size( nWidth, nHeight ) );
207     bool bClipped = false;
208 
209     // X-Coordinate outside of draw area?
210     if ( nX < mnOutOffX )
211     {
212         nWidth -= ( mnOutOffX - nX );
213         nX = mnOutOffX;
214         bClipped = true;
215     }
216 
217     // Y-Coordinate outside of draw area?
218     if ( nY < mnOutOffY )
219     {
220         nHeight -= ( mnOutOffY - nY );
221         nY = mnOutOffY;
222         bClipped = true;
223     }
224 
225     // Width outside of draw area?
226     if ( (nWidth + nX) > (mnOutWidth + mnOutOffX) )
227     {
228         nWidth  = mnOutOffX + mnOutWidth - nX;
229         bClipped = true;
230     }
231 
232     // Height outside of draw area?
233     if ( (nHeight + nY) > (mnOutHeight + mnOutOffY) )
234     {
235         nHeight = mnOutOffY + mnOutHeight - nY;
236         bClipped = true;
237     }
238 
239     if ( bClipped )
240     {
241         // If the visible part has been clipped, we have to create a
242         // Bitmap with the correct size in which we copy the clipped
243         // Bitmap to the correct position.
244         ScopedVclPtrInstance< VirtualDevice > aVDev(  *this  );
245 
246         if ( aVDev->SetOutputSizePixel( aRect.GetSize() ) )
247         {
248             if ( aVDev->mpGraphics || aVDev->AcquireGraphics() )
249             {
250                 if ( (nWidth > 0) && (nHeight > 0) )
251                 {
252                     SalTwoRect aPosAry(nX, nY, nWidth, nHeight,
253                                       (aRect.Left() < mnOutOffX) ? (mnOutOffX - aRect.Left()) : 0L,
254                                       (aRect.Top() < mnOutOffY) ? (mnOutOffY - aRect.Top()) : 0L,
255                                       nWidth, nHeight);
256                     aVDev->mpGraphics->CopyBits(aPosAry, *mpGraphics, *this, *this);
257                 }
258                 else
259                 {
260                     OSL_ENSURE(false, "CopyBits with zero or negative width or height");
261                 }
262 
263                 aBmp = aVDev->GetBitmap( Point(), aVDev->GetOutputSizePixel() );
264             }
265             else
266                 bClipped = false;
267         }
268         else
269             bClipped = false;
270     }
271 
272     if ( !bClipped )
273     {
274         std::shared_ptr<SalBitmap> pSalBmp = mpGraphics->GetBitmap( nX, nY, nWidth, nHeight, *this );
275 
276         if( pSalBmp )
277         {
278             aBmp.ImplSetSalBitmap(pSalBmp);
279         }
280     }
281 
282     return aBmp;
283 }
284 
DrawDeviceAlphaBitmap(const Bitmap & rBmp,const AlphaMask & rAlpha,const Point & rDestPt,const Size & rDestSize,const Point & rSrcPtPixel,const Size & rSrcSizePixel)285 void OutputDevice::DrawDeviceAlphaBitmap( const Bitmap& rBmp, const AlphaMask& rAlpha,
286                                     const Point& rDestPt, const Size& rDestSize,
287                                     const Point& rSrcPtPixel, const Size& rSrcSizePixel )
288 {
289     assert(!is_double_buffered_window());
290 
291     Point     aOutPt(LogicToPixel(rDestPt));
292     Size      aOutSz(LogicToPixel(rDestSize));
293     tools::Rectangle aDstRect(Point(), GetOutputSizePixel());
294 
295     const bool bHMirr = aOutSz.Width() < 0;
296     const bool bVMirr = aOutSz.Height() < 0;
297 
298     ClipToPaintRegion(aDstRect);
299 
300     BmpMirrorFlags mirrorFlags = BmpMirrorFlags::NONE;
301     if (bHMirr)
302     {
303         aOutSz.setWidth( -aOutSz.Width() );
304         aOutPt.AdjustX( -(aOutSz.Width() - 1) );
305         mirrorFlags |= BmpMirrorFlags::Horizontal;
306     }
307 
308     if (bVMirr)
309     {
310         aOutSz.setHeight( -aOutSz.Height() );
311         aOutPt.AdjustY( -(aOutSz.Height() - 1) );
312         mirrorFlags |= BmpMirrorFlags::Vertical;
313     }
314 
315     if (aDstRect.Intersection(tools::Rectangle(aOutPt, aOutSz)).IsEmpty())
316         return;
317 
318     {
319         Point aRelPt = aOutPt + Point(mnOutOffX, mnOutOffY);
320         SalTwoRect aTR(
321             rSrcPtPixel.X(), rSrcPtPixel.Y(),
322             rSrcSizePixel.Width(), rSrcSizePixel.Height(),
323             aRelPt.X(), aRelPt.Y(),
324             aOutSz.Width(), aOutSz.Height());
325 
326         Bitmap bitmap(rBmp);
327         AlphaMask alpha(rAlpha);
328         if(bHMirr || bVMirr)
329         {
330             bitmap.Mirror(mirrorFlags);
331             alpha.Mirror(mirrorFlags);
332         }
333         SalBitmap* pSalSrcBmp = bitmap.ImplGetSalBitmap().get();
334         SalBitmap* pSalAlphaBmp = alpha.GetBitmap().ImplGetSalBitmap().get();
335 
336         // #i83087# Naturally, system alpha blending (SalGraphics::DrawAlphaBitmap) cannot work
337         // with separate alpha VDev
338 
339         // try to blend the alpha bitmap with the alpha virtual device
340         if (mpAlphaVDev)
341         {
342             if (ImplLogicToDevicePixel(aOutSz).IsEmpty()) // nothing to draw
343                 return;
344             Bitmap aAlphaBitmap( mpAlphaVDev->GetBitmap( aRelPt, aOutSz ) );
345             if (SalBitmap* pSalAlphaBmp2 = aAlphaBitmap.ImplGetSalBitmap().get())
346             {
347                 if (mpGraphics->BlendAlphaBitmap(aTR, *pSalSrcBmp, *pSalAlphaBmp, *pSalAlphaBmp2, *this))
348                 {
349                     mpAlphaVDev->BlendBitmap(aTR, rAlpha.GetBitmap());
350                     return;
351                 }
352             }
353         }
354         else
355         {
356             if (mpGraphics->DrawAlphaBitmap(aTR, *pSalSrcBmp, *pSalAlphaBmp, *this))
357                 return;
358         }
359 
360         // we need to make sure Skia never reaches this slow code path
361         // (but do not fail in no-op cases)
362         assert(!SkiaHelper::isVCLSkiaEnabled() || !SkiaHelper::isAlphaMaskBlendingEnabled()
363             || tools::Rectangle(Point(), rBmp.GetSizePixel())
364                 .Intersection(tools::Rectangle(rSrcPtPixel, rSrcSizePixel)).IsEmpty()
365             || mpAlphaVDev->LogicToPixel(mpAlphaVDev->GetOutputSizePixel()).IsEmpty());
366     }
367 
368     tools::Rectangle aBmpRect(Point(), rBmp.GetSizePixel());
369     if (aBmpRect.Intersection(tools::Rectangle(rSrcPtPixel, rSrcSizePixel)).IsEmpty())
370         return;
371 
372     Point     auxOutPt(LogicToPixel(rDestPt));
373     Size      auxOutSz(LogicToPixel(rDestSize));
374 
375     // HACK: The function is broken with alpha vdev and mirroring, mirror here.
376     Bitmap bitmap(rBmp);
377     AlphaMask alpha(rAlpha);
378     if(mpAlphaVDev && (bHMirr || bVMirr))
379     {
380         bitmap.Mirror(mirrorFlags);
381         alpha.Mirror(mirrorFlags);
382         auxOutPt = aOutPt;
383         auxOutSz = aOutSz;
384     }
385     DrawDeviceAlphaBitmapSlowPath(bitmap, alpha, aDstRect, aBmpRect, auxOutSz, auxOutPt);
386 }
387 
388 namespace
389 {
390 
391 struct LinearScaleContext
392 {
393     std::unique_ptr<sal_Int32[]> mpMapX;
394     std::unique_ptr<sal_Int32[]> mpMapY;
395 
396     std::unique_ptr<sal_Int32[]> mpMapXOffset;
397     std::unique_ptr<sal_Int32[]> mpMapYOffset;
398 
LinearScaleContext__anonf8a6448d0111::LinearScaleContext399     LinearScaleContext(tools::Rectangle const & aDstRect, tools::Rectangle const & aBitmapRect,
400                  Size const & aOutSize, tools::Long nOffX, tools::Long nOffY)
401 
402         : mpMapX(new sal_Int32[aDstRect.GetWidth()])
403         , mpMapY(new sal_Int32[aDstRect.GetHeight()])
404         , mpMapXOffset(new sal_Int32[aDstRect.GetWidth()])
405         , mpMapYOffset(new sal_Int32[aDstRect.GetHeight()])
406     {
407         const tools::Long nSrcWidth = aBitmapRect.GetWidth();
408         const tools::Long nSrcHeight = aBitmapRect.GetHeight();
409 
410         generateSimpleMap(
411             nSrcWidth,  aDstRect.GetWidth(), aBitmapRect.Left(),
412             aOutSize.Width(),  nOffX, mpMapX.get(), mpMapXOffset.get());
413 
414         generateSimpleMap(
415             nSrcHeight, aDstRect.GetHeight(), aBitmapRect.Top(),
416             aOutSize.Height(), nOffY, mpMapY.get(), mpMapYOffset.get());
417     }
418 
419 private:
420 
generateSimpleMap__anonf8a6448d0111::LinearScaleContext421     static void generateSimpleMap(tools::Long nSrcDimension, tools::Long nDstDimension, tools::Long nDstLocation,
422                                   tools::Long nOutDimension, tools::Long nOffset, sal_Int32* pMap, sal_Int32* pMapOffset)
423     {
424 
425         const double fReverseScale = (std::abs(nOutDimension) > 1) ? (nSrcDimension - 1) / double(std::abs(nOutDimension) - 1) : 0.0;
426 
427         tools::Long nSampleRange = std::max(tools::Long(0), nSrcDimension - 2);
428 
429         for (tools::Long i = 0; i < nDstDimension; i++)
430         {
431             double fTemp = std::abs((nOffset + i) * fReverseScale);
432 
433             pMap[i] = std::clamp(nDstLocation + tools::Long(fTemp), tools::Long(0), nSampleRange);
434             pMapOffset[i] = static_cast<tools::Long>((fTemp - pMap[i]) * 128.0);
435         }
436     }
437 
438 public:
blendBitmap__anonf8a6448d0111::LinearScaleContext439     bool blendBitmap(
440             const BitmapWriteAccess* pDestination,
441             const BitmapReadAccess*  pSource,
442             const BitmapReadAccess*  pSourceAlpha,
443             const tools::Long nDstWidth,
444             const tools::Long nDstHeight)
445     {
446         if (!pSource || !pSourceAlpha || !pDestination)
447             return false;
448 
449         ScanlineFormat nSourceFormat = pSource->GetScanlineFormat();
450         ScanlineFormat nDestinationFormat = pDestination->GetScanlineFormat();
451 
452         switch (nSourceFormat)
453         {
454             case ScanlineFormat::N24BitTcRgb:
455             case ScanlineFormat::N24BitTcBgr:
456             {
457                 if ( (nSourceFormat == ScanlineFormat::N24BitTcBgr && nDestinationFormat == ScanlineFormat::N32BitTcBgra)
458                   || (nSourceFormat == ScanlineFormat::N24BitTcRgb && nDestinationFormat == ScanlineFormat::N32BitTcRgba))
459                 {
460                     blendBitmap24(pDestination, pSource, pSourceAlpha, nDstWidth, nDstHeight);
461                     return true;
462                 }
463             }
464             break;
465             default: break;
466         }
467         return false;
468     }
469 
blendBitmap24__anonf8a6448d0111::LinearScaleContext470     void blendBitmap24(
471             const BitmapWriteAccess*  pDestination,
472             const BitmapReadAccess*   pSource,
473             const BitmapReadAccess*   pSourceAlpha,
474             const tools::Long nDstWidth,
475             const tools::Long nDstHeight)
476     {
477         Scanline pLine0, pLine1;
478         Scanline pLineAlpha0, pLineAlpha1;
479         Scanline pColorSample1, pColorSample2;
480         Scanline pDestScanline;
481 
482         tools::Long nColor1Line1, nColor2Line1, nColor3Line1;
483         tools::Long nColor1Line2, nColor2Line2, nColor3Line2;
484         tools::Long nAlphaLine1, nAlphaLine2;
485 
486         sal_uInt8 nColor1, nColor2, nColor3, nAlpha;
487 
488         for (tools::Long nY = 0; nY < nDstHeight; nY++)
489         {
490             const tools::Long nMapY  = mpMapY[nY];
491             const tools::Long nMapFY = mpMapYOffset[nY];
492 
493             pLine0 = pSource->GetScanline(nMapY);
494             // tdf#95481 guard nMapY + 1 to be within bounds
495             pLine1 = (nMapY + 1 < pSource->Height()) ? pSource->GetScanline(nMapY + 1) : pLine0;
496 
497             pLineAlpha0 = pSourceAlpha->GetScanline(nMapY);
498             // tdf#95481 guard nMapY + 1 to be within bounds
499             pLineAlpha1 = (nMapY + 1 < pSourceAlpha->Height()) ? pSourceAlpha->GetScanline(nMapY + 1) : pLineAlpha0;
500 
501             pDestScanline = pDestination->GetScanline(nY);
502 
503             for (tools::Long nX = 0; nX < nDstWidth; nX++)
504             {
505                 const tools::Long nMapX = mpMapX[nX];
506                 const tools::Long nMapFX = mpMapXOffset[nX];
507 
508                 pColorSample1 = pLine0 + 3 * nMapX;
509                 pColorSample2 = (nMapX + 1 < pSource->Width()) ? pColorSample1 + 3 : pColorSample1;
510                 nColor1Line1 = (static_cast<tools::Long>(*pColorSample1) << 7) + nMapFX * (static_cast<tools::Long>(*pColorSample2) - *pColorSample1);
511 
512                 pColorSample1++;
513                 pColorSample2++;
514                 nColor2Line1 = (static_cast<tools::Long>(*pColorSample1) << 7) + nMapFX * (static_cast<tools::Long>(*pColorSample2) - *pColorSample1);
515 
516                 pColorSample1++;
517                 pColorSample2++;
518                 nColor3Line1 = (static_cast<tools::Long>(*pColorSample1) << 7) + nMapFX * (static_cast<tools::Long>(*pColorSample2) - *pColorSample1);
519 
520                 pColorSample1 = pLine1 + 3 * nMapX;
521                 pColorSample2 = (nMapX + 1 < pSource->Width()) ? pColorSample1 + 3 : pColorSample1;
522                 nColor1Line2 = (static_cast<tools::Long>(*pColorSample1) << 7) + nMapFX * (static_cast<tools::Long>(*pColorSample2) - *pColorSample1);
523 
524                 pColorSample1++;
525                 pColorSample2++;
526                 nColor2Line2 = (static_cast<tools::Long>(*pColorSample1) << 7) + nMapFX * (static_cast<tools::Long>(*pColorSample2) - *pColorSample1);
527 
528                 pColorSample1++;
529                 pColorSample2++;
530                 nColor3Line2 = (static_cast<tools::Long>(*pColorSample1) << 7) + nMapFX * (static_cast<tools::Long>(*pColorSample2) - *pColorSample1);
531 
532                 pColorSample1 = pLineAlpha0 + nMapX;
533                 pColorSample2 = (nMapX + 1 < pSourceAlpha->Width()) ? pColorSample1 + 1 : pColorSample1;
534                 nAlphaLine1 = (static_cast<tools::Long>(*pColorSample1) << 7) + nMapFX * (static_cast<tools::Long>(*pColorSample2) - *pColorSample1);
535 
536                 pColorSample1 = pLineAlpha1 + nMapX;
537                 pColorSample2 = (nMapX + 1 < pSourceAlpha->Width()) ? pColorSample1 + 1 : pColorSample1;
538                 nAlphaLine2 = (static_cast<tools::Long>(*pColorSample1) << 7) + nMapFX * (static_cast<tools::Long>(*pColorSample2) - *pColorSample1);
539 
540                 nColor1 = (nColor1Line1 + nMapFY * ((nColor1Line2 >> 7) - (nColor1Line1 >> 7))) >> 7;
541                 nColor2 = (nColor2Line1 + nMapFY * ((nColor2Line2 >> 7) - (nColor2Line1 >> 7))) >> 7;
542                 nColor3 = (nColor3Line1 + nMapFY * ((nColor3Line2 >> 7) - (nColor3Line1 >> 7))) >> 7;
543 
544                 nAlpha  = (nAlphaLine1  + nMapFY * ((nAlphaLine2  >> 7) - (nAlphaLine1 >> 7))) >> 7;
545 
546                 *pDestScanline = color::ColorChannelMerge(*pDestScanline, nColor1, nAlpha);
547                 pDestScanline++;
548                 *pDestScanline = color::ColorChannelMerge(*pDestScanline, nColor2, nAlpha);
549                 pDestScanline++;
550                 *pDestScanline = color::ColorChannelMerge(*pDestScanline, nColor3, nAlpha);
551                 pDestScanline++;
552                 pDestScanline++;
553             }
554         }
555     }
556 };
557 
558 struct TradScaleContext
559 {
560     std::unique_ptr<sal_Int32[]> mpMapX;
561     std::unique_ptr<sal_Int32[]> mpMapY;
562 
TradScaleContext__anonf8a6448d0111::TradScaleContext563     TradScaleContext(tools::Rectangle const & aDstRect, tools::Rectangle const & aBitmapRect,
564                  Size const & aOutSize, tools::Long nOffX, tools::Long nOffY)
565 
566         : mpMapX(new sal_Int32[aDstRect.GetWidth()])
567         , mpMapY(new sal_Int32[aDstRect.GetHeight()])
568     {
569         const tools::Long nSrcWidth = aBitmapRect.GetWidth();
570         const tools::Long nSrcHeight = aBitmapRect.GetHeight();
571 
572         const bool bHMirr = aOutSize.Width() < 0;
573         const bool bVMirr = aOutSize.Height() < 0;
574 
575         generateSimpleMap(
576             nSrcWidth, aDstRect.GetWidth(), aBitmapRect.Left(),
577             aOutSize.Width(), nOffX, bHMirr, mpMapX.get());
578 
579         generateSimpleMap(
580             nSrcHeight, aDstRect.GetHeight(), aBitmapRect.Top(),
581             aOutSize.Height(), nOffY, bVMirr, mpMapY.get());
582     }
583 
584 private:
585 
generateSimpleMap__anonf8a6448d0111::TradScaleContext586     static void generateSimpleMap(tools::Long nSrcDimension, tools::Long nDstDimension, tools::Long nDstLocation,
587                                   tools::Long nOutDimension, tools::Long nOffset, bool bMirror, sal_Int32* pMap)
588     {
589         tools::Long nMirrorOffset = 0;
590 
591         if (bMirror)
592             nMirrorOffset = (nDstLocation << 1) + nSrcDimension - 1;
593 
594         for (tools::Long i = 0; i < nDstDimension; ++i, ++nOffset)
595         {
596             pMap[i] = nDstLocation + nOffset * nSrcDimension / nOutDimension;
597             if (bMirror)
598                 pMap[i] = nMirrorOffset - pMap[i];
599         }
600     }
601 };
602 
603 
604 } // end anonymous namespace
605 
DrawDeviceAlphaBitmapSlowPath(const Bitmap & rBitmap,const AlphaMask & rAlpha,tools::Rectangle aDstRect,tools::Rectangle aBmpRect,Size const & aOutSize,Point const & aOutPoint)606 void OutputDevice::DrawDeviceAlphaBitmapSlowPath(const Bitmap& rBitmap,
607     const AlphaMask& rAlpha, tools::Rectangle aDstRect, tools::Rectangle aBmpRect, Size const & aOutSize, Point const & aOutPoint)
608 {
609     assert(!is_double_buffered_window());
610 
611     VirtualDevice* pOldVDev = mpAlphaVDev;
612 
613     const bool  bHMirr = aOutSize.Width() < 0;
614     const bool  bVMirr = aOutSize.Height() < 0;
615 
616     // The scaling in this code path produces really ugly results - it
617     // does the most trivial scaling with no smoothing.
618     GDIMetaFile* pOldMetaFile = mpMetaFile;
619     const bool   bOldMap = mbMap;
620 
621     mpMetaFile = nullptr; // fdo#55044 reset before GetBitmap!
622     mbMap = false;
623 
624     Bitmap aBmp(GetBitmap(aDstRect.TopLeft(), aDstRect.GetSize()));
625 
626     // #109044# The generated bitmap need not necessarily be
627     // of aDstRect dimensions, it's internally clipped to
628     // window bounds. Thus, we correct the dest size here,
629     // since we later use it (in nDstWidth/Height) for pixel
630     // access)
631     // #i38887# reading from screen may sometimes fail
632     if (aBmp.ImplGetSalBitmap())
633     {
634         aDstRect.SetSize(aBmp.GetSizePixel());
635     }
636 
637     const tools::Long nDstWidth = aDstRect.GetWidth();
638     const tools::Long nDstHeight = aDstRect.GetHeight();
639 
640     // calculate offset in original bitmap
641     // in RTL case this is a little more complicated since the contents of the
642     // bitmap is not mirrored (it never is), however the paint region and bmp region
643     // are in mirrored coordinates, so the intersection of (aOutPt,aOutSz) with these
644     // is content wise somewhere else and needs to take mirroring into account
645     const tools::Long nOffX = IsRTLEnabled()
646                             ? aOutSize.Width() - aDstRect.GetWidth() - (aDstRect.Left() - aOutPoint.X())
647                             : aDstRect.Left() - aOutPoint.X();
648 
649     const tools::Long nOffY = aDstRect.Top() - aOutPoint.Y();
650 
651     TradScaleContext aTradContext(aDstRect, aBmpRect, aOutSize, nOffX, nOffY);
652 
653     BitmapScopedReadAccess pBitmapReadAccess(rBitmap);
654     BitmapScopedReadAccess pAlphaReadAccess(rAlpha);
655 
656     SAL_WARN_IF(pAlphaReadAccess->GetScanlineFormat() != ScanlineFormat::N8BitPal, "vcl.gdi", "non-8bit alpha no longer supported!");
657     assert(pAlphaReadAccess->GetScanlineFormat() == ScanlineFormat::N8BitPal);
658 
659     // #i38887# reading from screen may sometimes fail
660     if (aBmp.ImplGetSalBitmap())
661     {
662         Bitmap aNewBitmap;
663 
664         if (mpAlphaVDev)
665         {
666             aNewBitmap = BlendBitmapWithAlpha(
667                             aBmp, pBitmapReadAccess.get(), pAlphaReadAccess.get(),
668                             aDstRect,
669                             nOffY, nDstHeight,
670                             nOffX, nDstWidth,
671                             aTradContext.mpMapX.get(), aTradContext.mpMapY.get() );
672         }
673         else
674         {
675             LinearScaleContext aLinearContext(aDstRect, aBmpRect, aOutSize, nOffX, nOffY);
676 
677             if (aLinearContext.blendBitmap( BitmapScopedWriteAccess(aBmp).get(), pBitmapReadAccess.get(), pAlphaReadAccess.get(),
678                     nDstWidth, nDstHeight))
679             {
680                 aNewBitmap = aBmp;
681             }
682             else
683             {
684                 aNewBitmap = BlendBitmap(
685                             aBmp, pBitmapReadAccess.get(), pAlphaReadAccess.get(),
686                             nOffY, nDstHeight,
687                             nOffX, nDstWidth,
688                             aBmpRect, aOutSize,
689                             bHMirr, bVMirr,
690                             aTradContext.mpMapX.get(), aTradContext.mpMapY.get() );
691             }
692         }
693 
694         // #110958# Disable alpha VDev, we're doing the necessary
695         // stuff explicitly further below
696         if (mpAlphaVDev)
697             mpAlphaVDev = nullptr;
698 
699         DrawBitmap(aDstRect.TopLeft(), aNewBitmap);
700 
701         // #110958# Enable alpha VDev again
702         mpAlphaVDev = pOldVDev;
703     }
704 
705     mbMap = bOldMap;
706     mpMetaFile = pOldMetaFile;
707 }
708 
HasFastDrawTransformedBitmap() const709 bool OutputDevice::HasFastDrawTransformedBitmap() const
710 {
711     if( ImplIsRecordLayout() )
712         return false;
713 
714     if (!mpGraphics && !AcquireGraphics())
715         return false;
716     assert(mpGraphics);
717 
718     return mpGraphics->HasFastDrawTransformedBitmap();
719 }
720 
DrawImage(const Point & rPos,const Image & rImage,DrawImageFlags nStyle)721 void OutputDevice::DrawImage( const Point& rPos, const Image& rImage, DrawImageFlags nStyle )
722 {
723     assert(!is_double_buffered_window());
724 
725     DrawImage( rPos, Size(), rImage, nStyle );
726 }
727 
DrawImage(const Point & rPos,const Size & rSize,const Image & rImage,DrawImageFlags nStyle)728 void OutputDevice::DrawImage( const Point& rPos, const Size& rSize,
729                               const Image& rImage, DrawImageFlags nStyle )
730 {
731     assert(!is_double_buffered_window());
732 
733     bool bIsSizeValid = !rSize.IsEmpty();
734 
735     if (!ImplIsRecordLayout())
736     {
737         Image& rNonConstImage = const_cast<Image&>(rImage);
738         if (bIsSizeValid)
739             rNonConstImage.Draw(this, rPos, nStyle, &rSize);
740         else
741             rNonConstImage.Draw(this, rPos, nStyle);
742     }
743 }
744 
745 namespace
746 {
747     // Co = Cs + Cd*(1-As) premultiplied alpha -or-
748     // Co = (AsCs + AdCd*(1-As)) / Ao
CalcColor(const sal_uInt8 nSourceColor,const sal_uInt8 nSourceAlpha,const sal_uInt8 nDstAlpha,const sal_uInt8 nResAlpha,const sal_uInt8 nDestColor)749     sal_uInt8 CalcColor( const sal_uInt8 nSourceColor, const sal_uInt8 nSourceAlpha,
750                                 const sal_uInt8 nDstAlpha, const sal_uInt8 nResAlpha, const sal_uInt8 nDestColor )
751     {
752         int c = nResAlpha ? ( static_cast<int>(nSourceAlpha)*nSourceColor + static_cast<int>(nDstAlpha)*nDestColor -
753                               static_cast<int>(nDstAlpha)*nDestColor*nSourceAlpha/255 ) / static_cast<int>(nResAlpha) : 0;
754         return sal_uInt8( c );
755     }
756 
AlphaBlend(int nX,int nY,const tools::Long nMapX,const tools::Long nMapY,BitmapReadAccess const * pP,BitmapReadAccess const * pA,BitmapReadAccess const * pB,BitmapWriteAccess const * pAlphaW,sal_uInt8 & nResAlpha)757     BitmapColor AlphaBlend( int nX,               int nY,
758                                    const tools::Long            nMapX,
759                                    const tools::Long            nMapY,
760                                    BitmapReadAccess const *  pP,
761                                    BitmapReadAccess const *  pA,
762                                    BitmapReadAccess const *  pB,
763                                    BitmapWriteAccess const * pAlphaW,
764                                    sal_uInt8&            nResAlpha )
765     {
766         BitmapColor aDstCol,aSrcCol;
767         aSrcCol = pP->GetColor( nMapY, nMapX );
768         aDstCol = pB->GetColor( nY, nX );
769 
770         const sal_uInt8 nSrcAlpha = pA->GetPixelIndex( nMapY, nMapX );
771         const sal_uInt8 nDstAlpha = pAlphaW->GetPixelIndex( nY, nX );
772 
773         // Perform porter-duff compositing 'over' operation
774 
775         // Co = Cs + Cd*(1-As)
776         // Ad = As + Ad*(1-As)
777         nResAlpha = static_cast<int>(nSrcAlpha) + static_cast<int>(nDstAlpha) - static_cast<int>(nDstAlpha)*nSrcAlpha/255;
778 
779         aDstCol.SetRed( CalcColor( aSrcCol.GetRed(), nSrcAlpha, nDstAlpha, nResAlpha, aDstCol.GetRed() ) );
780         aDstCol.SetBlue( CalcColor( aSrcCol.GetBlue(), nSrcAlpha, nDstAlpha, nResAlpha, aDstCol.GetBlue() ) );
781         aDstCol.SetGreen( CalcColor( aSrcCol.GetGreen(), nSrcAlpha, nDstAlpha, nResAlpha, aDstCol.GetGreen() ) );
782 
783         return aDstCol;
784     }
785 }
786 
BlendBitmap(const SalTwoRect & rPosAry,const Bitmap & rBmp)787 void OutputDevice::BlendBitmap(
788             const SalTwoRect&   rPosAry,
789             const Bitmap&       rBmp )
790 {
791     mpGraphics->BlendBitmap( rPosAry, *rBmp.ImplGetSalBitmap(), *this );
792 }
793 
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 sal_Int32 * pMapX,const sal_Int32 * pMapY)794 Bitmap OutputDevice::BlendBitmapWithAlpha(
795             Bitmap&             aBmp,
796             BitmapReadAccess const *   pP,
797             BitmapReadAccess const *   pA,
798             const tools::Rectangle&    aDstRect,
799             const sal_Int32     nOffY,
800             const sal_Int32     nDstHeight,
801             const sal_Int32     nOffX,
802             const sal_Int32     nDstWidth,
803             const sal_Int32*    pMapX,
804             const sal_Int32*    pMapY )
805 
806 {
807     BitmapColor aDstCol;
808     Bitmap      res;
809     int         nX, nY;
810     sal_uInt8   nResAlpha;
811 
812     SAL_WARN_IF( !mpAlphaVDev, "vcl.gdi", "BlendBitmapWithAlpha(): call me only with valid alpha VirtualDevice!" );
813 
814     bool bOldMapMode( mpAlphaVDev->IsMapModeEnabled() );
815     mpAlphaVDev->EnableMapMode(false);
816 
817     Bitmap aAlphaBitmap( mpAlphaVDev->GetBitmap( aDstRect.TopLeft(), aDstRect.GetSize() ) );
818     BitmapScopedWriteAccess pAlphaW(aAlphaBitmap);
819 
820     if( GetBitCount() <= 8 )
821     {
822         Bitmap aDither(aBmp.GetSizePixel(), vcl::PixelFormat::N8_BPP);
823         BitmapColor         aIndex( 0 );
824         BitmapScopedReadAccess pB(aBmp);
825         BitmapScopedWriteAccess pW(aDither);
826 
827         if (pB && pP && pA && pW && pAlphaW)
828         {
829             int nOutY;
830 
831             for( nY = 0, nOutY = nOffY; nY < nDstHeight; nY++, nOutY++ )
832             {
833                 const tools::Long nMapY = pMapY[ nY ];
834                 const tools::Long nModY = ( nOutY & 0x0FL ) << 4;
835                 int nOutX;
836 
837                 Scanline pScanline = pW->GetScanline(nY);
838                 Scanline pScanlineAlpha = pAlphaW->GetScanline(nY);
839                 for( nX = 0, nOutX = nOffX; nX < nDstWidth; nX++, nOutX++ )
840                 {
841                     const tools::Long  nMapX = pMapX[ nX ];
842                     const sal_uLong nD = nVCLDitherLut[ nModY | ( nOutX & 0x0FL ) ];
843 
844                     aDstCol = AlphaBlend( nX, nY, nMapX, nMapY, pP, pA, pB.get(), pAlphaW.get(), nResAlpha );
845 
846                     aIndex.SetIndex( static_cast<sal_uInt8>( nVCLRLut[ ( nVCLLut[ aDstCol.GetRed() ] + nD ) >> 16 ] +
847                                               nVCLGLut[ ( nVCLLut[ aDstCol.GetGreen() ] + nD ) >> 16 ] +
848                                               nVCLBLut[ ( nVCLLut[ aDstCol.GetBlue() ] + nD ) >> 16 ] ) );
849                     pW->SetPixelOnData( pScanline, nX, aIndex );
850 
851                     aIndex.SetIndex( static_cast<sal_uInt8>( nVCLRLut[ ( nVCLLut[ nResAlpha ] + nD ) >> 16 ] +
852                                                    nVCLGLut[ ( nVCLLut[ nResAlpha ] + nD ) >> 16 ] +
853                                                    nVCLBLut[ ( nVCLLut[ nResAlpha ] + nD ) >> 16 ] ) );
854                     pAlphaW->SetPixelOnData( pScanlineAlpha, nX, aIndex );
855                 }
856             }
857         }
858         pB.reset();
859         pW.reset();
860         res = aDither;
861     }
862     else
863     {
864         BitmapScopedWriteAccess pB(aBmp);
865         if (pB && pP && pA && pAlphaW)
866         {
867             for( nY = 0; nY < nDstHeight; nY++ )
868             {
869                 const tools::Long  nMapY = pMapY[ nY ];
870                 Scanline pScanlineB = pB->GetScanline(nY);
871                 Scanline pScanlineAlpha = pAlphaW->GetScanline(nY);
872 
873                 for( nX = 0; nX < nDstWidth; nX++ )
874                 {
875                     const tools::Long nMapX = pMapX[ nX ];
876                     aDstCol = AlphaBlend( nX, nY, nMapX, nMapY, pP, pA, pB.get(), pAlphaW.get(), nResAlpha );
877 
878                     pB->SetPixelOnData(pScanlineB, nX, pB->GetBestMatchingColor(aDstCol));
879                     pAlphaW->SetPixelOnData(pScanlineAlpha, nX, pB->GetBestMatchingColor(Color(nResAlpha, nResAlpha, nResAlpha)));
880                 }
881             }
882         }
883         pB.reset();
884         res = aBmp;
885     }
886 
887     pAlphaW.reset();
888     mpAlphaVDev->DrawBitmap( aDstRect.TopLeft(), aAlphaBitmap );
889     mpAlphaVDev->EnableMapMode( bOldMapMode );
890 
891     return res;
892 }
893 
BlendBitmap(Bitmap & aBmp,BitmapReadAccess const * pP,BitmapReadAccess const * pA,const sal_Int32 nOffY,const sal_Int32 nDstHeight,const sal_Int32 nOffX,const sal_Int32 nDstWidth,const tools::Rectangle & aBmpRect,const Size & aOutSz,const bool bHMirr,const bool bVMirr,const sal_Int32 * pMapX,const sal_Int32 * pMapY)894 Bitmap OutputDevice::BlendBitmap(
895             Bitmap&             aBmp,
896             BitmapReadAccess const * pP,
897             BitmapReadAccess const * pA,
898             const sal_Int32     nOffY,
899             const sal_Int32     nDstHeight,
900             const sal_Int32     nOffX,
901             const sal_Int32     nDstWidth,
902             const tools::Rectangle&    aBmpRect,
903             const Size&         aOutSz,
904             const bool          bHMirr,
905             const bool          bVMirr,
906             const sal_Int32*    pMapX,
907             const sal_Int32*    pMapY )
908 {
909     if( !pP || !pA )
910         return aBmp;
911 
912     if( GetBitCount() <= 8 )
913     {
914         Bitmap aDither(aBmp.GetSizePixel(), vcl::PixelFormat::N8_BPP);
915         BitmapColor         aIndex( 0 );
916         BitmapScopedReadAccess pB(aBmp);
917         BitmapScopedWriteAccess pW(aDither);
918 
919         for( int nY = 0, nOutY = nOffY; nY < nDstHeight; nY++, nOutY++ )
920         {
921             tools::Long nMapY = pMapY[ nY ];
922             if (bVMirr)
923             {
924                 nMapY = aBmpRect.Bottom() - nMapY;
925             }
926             const tools::Long nModY = ( nOutY & 0x0FL ) << 4;
927 
928             Scanline pScanline = pW->GetScanline(nY);
929             Scanline pScanlineAlpha = pA->GetScanline(nMapY);
930             for( int nX = 0, nOutX = nOffX; nX < nDstWidth; nX++, nOutX++ )
931             {
932                 tools::Long  nMapX = pMapX[ nX ];
933                 if (bHMirr)
934                 {
935                     nMapX = aBmpRect.Right() - nMapX;
936                 }
937                 const sal_uLong nD = nVCLDitherLut[ nModY | ( nOutX & 0x0FL ) ];
938 
939                 BitmapColor aDstCol = pB->GetColor( nY, nX );
940                 aDstCol.Merge( pP->GetColor( nMapY, nMapX ), 255 - pA->GetIndexFromData( pScanlineAlpha, nMapX ) );
941                 aIndex.SetIndex( static_cast<sal_uInt8>( nVCLRLut[ ( nVCLLut[ aDstCol.GetRed() ] + nD ) >> 16 ] +
942                                           nVCLGLut[ ( nVCLLut[ aDstCol.GetGreen() ] + nD ) >> 16 ] +
943                                           nVCLBLut[ ( nVCLLut[ aDstCol.GetBlue() ] + nD ) >> 16 ] ) );
944                 pW->SetPixelOnData( pScanline, nX, aIndex );
945             }
946         }
947 
948         pB.reset();
949         pW.reset();
950         return aDither;
951     }
952 
953     BitmapScopedWriteAccess pB(aBmp);
954 
955     bool bFastBlend = false;
956     if (!bHMirr && !bVMirr)
957     {
958         SalTwoRect aTR(aBmpRect.Left(), aBmpRect.Top(), aBmpRect.GetWidth(), aBmpRect.GetHeight(),
959                         nOffX, nOffY, aOutSz.Width(), aOutSz.Height());
960 
961         bFastBlend = ImplFastBitmapBlending(*pB, *pP, *pA, aTR);
962     }
963 
964     if (!bFastBlend)
965     {
966         for (int nY = 0; nY < nDstHeight; nY++)
967         {
968             tools::Long  nMapY = pMapY[nY];
969 
970             if (bVMirr)
971                 nMapY = aBmpRect.Bottom() - nMapY;
972 
973             Scanline pAScan = pA->GetScanline(nMapY);
974             Scanline pBScan = pB->GetScanline(nY);
975             for(int nX = 0; nX < nDstWidth; nX++)
976             {
977                 tools::Long nMapX = pMapX[nX];
978 
979                 if (bHMirr)
980                     nMapX = aBmpRect.Right() - nMapX;
981 
982                 BitmapColor aDstCol = pB->GetPixelFromData(pBScan, nX);
983                 aDstCol.Merge(pP->GetColor(nMapY, nMapX), pAScan[nMapX]);
984                 pB->SetPixelOnData(pBScan, nX, aDstCol);
985             }
986         }
987     }
988 
989     pB.reset();
990     return aBmp;
991 }
992 
993 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
994