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