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 31 #include <bitmap/BitmapWriteAccess.hxx> 32 #include <bitmap/bmpfast.hxx> 33 #include <drawmode.hxx> 34 #include <salbmp.hxx> 35 #include <salgdi.hxx> 36 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 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 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 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 191 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 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 if (ImplLogicToDevicePixel(aOutSz).IsEmpty()) // nothing to draw 318 return; 319 320 { 321 Point aRelPt = aOutPt + Point(mnOutOffX, mnOutOffY); 322 SalTwoRect aTR( 323 rSrcPtPixel.X(), rSrcPtPixel.Y(), 324 rSrcSizePixel.Width(), rSrcSizePixel.Height(), 325 aRelPt.X(), aRelPt.Y(), 326 aOutSz.Width(), aOutSz.Height()); 327 328 Bitmap bitmap(rBmp); 329 AlphaMask alpha(rAlpha); 330 if(bHMirr || bVMirr) 331 { 332 bitmap.Mirror(mirrorFlags); 333 alpha.Mirror(mirrorFlags); 334 } 335 SalBitmap* pSalSrcBmp = bitmap.ImplGetSalBitmap().get(); 336 SalBitmap* pSalAlphaBmp = alpha.ImplGetSalBitmap().get(); 337 338 // #i83087# Naturally, system alpha blending (SalGraphics::DrawAlphaBitmap) cannot work 339 // with separate alpha VDev 340 341 // try to blend the alpha bitmap with the alpha virtual device 342 if (mpAlphaVDev) 343 { 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); 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() 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 399 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 421 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] = MinMax(nDstLocation + tools::Long(fTemp), 0, nSampleRange); 434 pMapOffset[i] = static_cast<tools::Long>((fTemp - pMap[i]) * 128.0); 435 } 436 } 437 438 public: 439 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 470 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 563 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 586 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 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 Bitmap::ScopedReadAccess pBitmapReadAccess(const_cast<Bitmap&>(rBitmap)); 654 AlphaMask::ScopedReadAccess pAlphaReadAccess(const_cast<AlphaMask&>(rAlpha)); 655 656 DBG_ASSERT( pAlphaReadAccess->GetScanlineFormat() == ScanlineFormat::N8BitPal, 657 "OutputDevice::ImplDrawAlpha(): non-8bit alpha no longer supported!" ); 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 709 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 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 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 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 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 // vcl stores transparency, not alpha - invert it 771 const sal_uInt8 nSrcAlpha = 255 - pA->GetPixelIndex( nMapY, nMapX ); 772 const sal_uInt8 nDstAlpha = 255 - pAlphaW->GetPixelIndex( nY, nX ); 773 774 // Perform porter-duff compositing 'over' operation 775 776 // Co = Cs + Cd*(1-As) 777 // Ad = As + Ad*(1-As) 778 nResAlpha = static_cast<int>(nSrcAlpha) + static_cast<int>(nDstAlpha) - static_cast<int>(nDstAlpha)*nSrcAlpha/255; 779 780 aDstCol.SetRed( CalcColor( aSrcCol.GetRed(), nSrcAlpha, nDstAlpha, nResAlpha, aDstCol.GetRed() ) ); 781 aDstCol.SetBlue( CalcColor( aSrcCol.GetBlue(), nSrcAlpha, nDstAlpha, nResAlpha, aDstCol.GetBlue() ) ); 782 aDstCol.SetGreen( CalcColor( aSrcCol.GetGreen(), nSrcAlpha, nDstAlpha, nResAlpha, aDstCol.GetGreen() ) ); 783 784 return aDstCol; 785 } 786 } 787 788 void OutputDevice::BlendBitmap( 789 const SalTwoRect& rPosAry, 790 const Bitmap& rBmp ) 791 { 792 mpGraphics->BlendBitmap( rPosAry, *rBmp.ImplGetSalBitmap(), *this ); 793 } 794 795 Bitmap OutputDevice::BlendBitmapWithAlpha( 796 Bitmap& aBmp, 797 BitmapReadAccess const * pP, 798 BitmapReadAccess const * pA, 799 const tools::Rectangle& aDstRect, 800 const sal_Int32 nOffY, 801 const sal_Int32 nDstHeight, 802 const sal_Int32 nOffX, 803 const sal_Int32 nDstWidth, 804 const sal_Int32* pMapX, 805 const sal_Int32* pMapY ) 806 807 { 808 BitmapColor aDstCol; 809 Bitmap res; 810 int nX, nY; 811 sal_uInt8 nResAlpha; 812 813 SAL_WARN_IF( !mpAlphaVDev, "vcl.gdi", "BlendBitmapWithAlpha(): call me only with valid alpha VirtualDevice!" ); 814 815 bool bOldMapMode( mpAlphaVDev->IsMapModeEnabled() ); 816 mpAlphaVDev->EnableMapMode(false); 817 818 Bitmap aAlphaBitmap( mpAlphaVDev->GetBitmap( aDstRect.TopLeft(), aDstRect.GetSize() ) ); 819 BitmapScopedWriteAccess pAlphaW(aAlphaBitmap); 820 821 if( GetBitCount() <= 8 ) 822 { 823 Bitmap aDither(aBmp.GetSizePixel(), vcl::PixelFormat::N8_BPP); 824 BitmapColor aIndex( 0 ); 825 Bitmap::ScopedReadAccess pB(aBmp); 826 BitmapScopedWriteAccess pW(aDither); 827 828 if (pB && pP && pA && pW && pAlphaW) 829 { 830 int nOutY; 831 832 for( nY = 0, nOutY = nOffY; nY < nDstHeight; nY++, nOutY++ ) 833 { 834 const tools::Long nMapY = pMapY[ nY ]; 835 const tools::Long nModY = ( nOutY & 0x0FL ) << 4; 836 int nOutX; 837 838 Scanline pScanline = pW->GetScanline(nY); 839 Scanline pScanlineAlpha = pAlphaW->GetScanline(nY); 840 for( nX = 0, nOutX = nOffX; nX < nDstWidth; nX++, nOutX++ ) 841 { 842 const tools::Long nMapX = pMapX[ nX ]; 843 const sal_uLong nD = nVCLDitherLut[ nModY | ( nOutX & 0x0FL ) ]; 844 845 aDstCol = AlphaBlend( nX, nY, nMapX, nMapY, pP, pA, pB.get(), pAlphaW.get(), nResAlpha ); 846 847 aIndex.SetIndex( static_cast<sal_uInt8>( nVCLRLut[ ( nVCLLut[ aDstCol.GetRed() ] + nD ) >> 16 ] + 848 nVCLGLut[ ( nVCLLut[ aDstCol.GetGreen() ] + nD ) >> 16 ] + 849 nVCLBLut[ ( nVCLLut[ aDstCol.GetBlue() ] + nD ) >> 16 ] ) ); 850 pW->SetPixelOnData( pScanline, nX, aIndex ); 851 852 aIndex.SetIndex( static_cast<sal_uInt8>( nVCLRLut[ ( nVCLLut[ 255-nResAlpha ] + nD ) >> 16 ] + 853 nVCLGLut[ ( nVCLLut[ 255-nResAlpha ] + nD ) >> 16 ] + 854 nVCLBLut[ ( nVCLLut[ 255-nResAlpha ] + nD ) >> 16 ] ) ); 855 pAlphaW->SetPixelOnData( pScanlineAlpha, nX, aIndex ); 856 } 857 } 858 } 859 pB.reset(); 860 pW.reset(); 861 res = aDither; 862 } 863 else 864 { 865 BitmapScopedWriteAccess pB(aBmp); 866 if (pB && pP && pA && pAlphaW) 867 { 868 for( nY = 0; nY < nDstHeight; nY++ ) 869 { 870 const tools::Long nMapY = pMapY[ nY ]; 871 Scanline pScanlineB = pB->GetScanline(nY); 872 Scanline pScanlineAlpha = pAlphaW->GetScanline(nY); 873 874 for( nX = 0; nX < nDstWidth; nX++ ) 875 { 876 const tools::Long nMapX = pMapX[ nX ]; 877 aDstCol = AlphaBlend( nX, nY, nMapX, nMapY, pP, pA, pB.get(), pAlphaW.get(), nResAlpha ); 878 879 pB->SetPixelOnData(pScanlineB, nX, pB->GetBestMatchingColor(aDstCol)); 880 pAlphaW->SetPixelOnData(pScanlineAlpha, nX, pB->GetBestMatchingColor(Color(255L-nResAlpha, 255L-nResAlpha, 255L-nResAlpha))); 881 } 882 } 883 } 884 pB.reset(); 885 res = aBmp; 886 } 887 888 pAlphaW.reset(); 889 mpAlphaVDev->DrawBitmap( aDstRect.TopLeft(), aAlphaBitmap ); 890 mpAlphaVDev->EnableMapMode( bOldMapMode ); 891 892 return res; 893 } 894 895 Bitmap OutputDevice::BlendBitmap( 896 Bitmap& aBmp, 897 BitmapReadAccess const * pP, 898 BitmapReadAccess const * pA, 899 const sal_Int32 nOffY, 900 const sal_Int32 nDstHeight, 901 const sal_Int32 nOffX, 902 const sal_Int32 nDstWidth, 903 const tools::Rectangle& aBmpRect, 904 const Size& aOutSz, 905 const bool bHMirr, 906 const bool bVMirr, 907 const sal_Int32* pMapX, 908 const sal_Int32* pMapY ) 909 { 910 BitmapColor aDstCol; 911 Bitmap res; 912 int nX, nY; 913 914 if( GetBitCount() <= 8 ) 915 { 916 Bitmap aDither(aBmp.GetSizePixel(), vcl::PixelFormat::N8_BPP); 917 BitmapColor aIndex( 0 ); 918 Bitmap::ScopedReadAccess pB(aBmp); 919 BitmapScopedWriteAccess pW(aDither); 920 921 if( pB && pP && pA && pW ) 922 { 923 int nOutY; 924 925 for( nY = 0, nOutY = nOffY; nY < nDstHeight; nY++, nOutY++ ) 926 { 927 tools::Long nMapY = pMapY[ nY ]; 928 if (bVMirr) 929 { 930 nMapY = aBmpRect.Bottom() - nMapY; 931 } 932 const tools::Long nModY = ( nOutY & 0x0FL ) << 4; 933 int nOutX; 934 935 Scanline pScanline = pW->GetScanline(nY); 936 Scanline pScanlineAlpha = pA->GetScanline(nMapY); 937 for( nX = 0, nOutX = nOffX; nX < nDstWidth; nX++, nOutX++ ) 938 { 939 tools::Long nMapX = pMapX[ nX ]; 940 if (bHMirr) 941 { 942 nMapX = aBmpRect.Right() - nMapX; 943 } 944 const sal_uLong nD = nVCLDitherLut[ nModY | ( nOutX & 0x0FL ) ]; 945 946 aDstCol = pB->GetColor( nY, nX ); 947 aDstCol.Merge( pP->GetColor( nMapY, nMapX ), pA->GetIndexFromData( pScanlineAlpha, nMapX ) ); 948 aIndex.SetIndex( static_cast<sal_uInt8>( nVCLRLut[ ( nVCLLut[ aDstCol.GetRed() ] + nD ) >> 16 ] + 949 nVCLGLut[ ( nVCLLut[ aDstCol.GetGreen() ] + nD ) >> 16 ] + 950 nVCLBLut[ ( nVCLLut[ aDstCol.GetBlue() ] + nD ) >> 16 ] ) ); 951 pW->SetPixelOnData( pScanline, nX, aIndex ); 952 } 953 } 954 } 955 956 pB.reset(); 957 pW.reset(); 958 res = aDither; 959 } 960 else 961 { 962 BitmapScopedWriteAccess pB(aBmp); 963 964 bool bFastBlend = false; 965 if( pP && pA && pB && !bHMirr && !bVMirr ) 966 { 967 SalTwoRect aTR(aBmpRect.Left(), aBmpRect.Top(), aBmpRect.GetWidth(), aBmpRect.GetHeight(), 968 nOffX, nOffY, aOutSz.Width(), aOutSz.Height()); 969 970 bFastBlend = ImplFastBitmapBlending( *pB,*pP,*pA, aTR ); 971 } 972 973 if( pP && pA && pB && !bFastBlend ) 974 { 975 switch( pP->GetScanlineFormat() ) 976 { 977 case ScanlineFormat::N8BitPal: 978 { 979 for( nY = 0; nY < nDstHeight; nY++ ) 980 { 981 tools::Long nMapY = pMapY[ nY ]; 982 if ( bVMirr ) 983 { 984 nMapY = aBmpRect.Bottom() - nMapY; 985 } 986 Scanline pPScan = pP->GetScanline( nMapY ); 987 Scanline pAScan = pA->GetScanline( nMapY ); 988 Scanline pBScan = pB->GetScanline( nY ); 989 990 for( nX = 0; nX < nDstWidth; nX++ ) 991 { 992 tools::Long nMapX = pMapX[ nX ]; 993 994 if ( bHMirr ) 995 { 996 nMapX = aBmpRect.Right() - nMapX; 997 } 998 aDstCol = pB->GetPixelFromData( pBScan, nX ); 999 aDstCol.Merge( pP->GetPaletteColor( pPScan[ nMapX ] ), pAScan[ nMapX ] ); 1000 pB->SetPixelOnData( pBScan, nX, aDstCol ); 1001 } 1002 } 1003 } 1004 break; 1005 1006 default: 1007 { 1008 1009 for( nY = 0; nY < nDstHeight; nY++ ) 1010 { 1011 tools::Long nMapY = pMapY[ nY ]; 1012 1013 if ( bVMirr ) 1014 { 1015 nMapY = aBmpRect.Bottom() - nMapY; 1016 } 1017 Scanline pAScan = pA->GetScanline( nMapY ); 1018 Scanline pBScan = pB->GetScanline(nY); 1019 for( nX = 0; nX < nDstWidth; nX++ ) 1020 { 1021 tools::Long nMapX = pMapX[ nX ]; 1022 1023 if ( bHMirr ) 1024 { 1025 nMapX = aBmpRect.Right() - nMapX; 1026 } 1027 aDstCol = pB->GetPixelFromData( pBScan, nX ); 1028 aDstCol.Merge( pP->GetColor( nMapY, nMapX ), pAScan[ nMapX ] ); 1029 pB->SetPixelOnData( pBScan, nX, aDstCol ); 1030 } 1031 } 1032 } 1033 break; 1034 } 1035 } 1036 1037 pB.reset(); 1038 res = aBmp; 1039 } 1040 1041 return res; 1042 } 1043 1044 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 1045
