DrawTargetSkia.cpp (74425B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "DrawTargetSkia.h" 8 #include "SourceSurfaceSkia.h" 9 #include "ScaledFontBase.h" 10 #include "FilterNodeSoftware.h" 11 #include "HelpersSkia.h" 12 13 #include "mozilla/CheckedInt.h" 14 #include "mozilla/StaticPrefs_gfx.h" 15 #include "mozilla/Vector.h" 16 17 #include "skia/include/core/SkBitmap.h" 18 #include "skia/include/core/SkCanvas.h" 19 #include "skia/include/core/SkFont.h" 20 #include "skia/include/core/SkSurface.h" 21 #include "skia/include/core/SkTextBlob.h" 22 #include "skia/include/core/SkTypeface.h" 23 #include "skia/include/effects/SkGradientShader.h" 24 #include "skia/include/core/SkColorFilter.h" 25 #include "skia/include/core/SkRegion.h" 26 #include "skia/include/effects/SkImageFilters.h" 27 #include "skia/include/private/base/SkMalloc.h" 28 #include "skia/src/core/SkEffectPriv.h" 29 #include "skia/src/core/SkRasterPipeline.h" 30 #include "skia/src/core/SkWriteBuffer.h" 31 #include "skia/src/shaders/SkEmptyShader.h" 32 #include "Blur.h" 33 #include "Logging.h" 34 #include "Tools.h" 35 #include "PathHelpers.h" 36 #include "PathSkia.h" 37 #include "Swizzle.h" 38 #include <algorithm> 39 #include <cmath> 40 41 #ifdef XP_DARWIN 42 # include "BorrowedContext.h" 43 # include <CoreGraphics/CGBitmapContext.h> 44 #endif 45 46 #ifdef XP_WIN 47 # include "ScaledFontDWrite.h" 48 #endif 49 50 namespace mozilla { 51 52 void RefPtrTraits<SkSurface>::Release(SkSurface* aSurface) { 53 SkSafeUnref(aSurface); 54 } 55 56 void RefPtrTraits<SkSurface>::AddRef(SkSurface* aSurface) { 57 SkSafeRef(aSurface); 58 } 59 60 } // namespace mozilla 61 62 namespace mozilla::gfx { 63 64 class GradientStopsSkia : public GradientStops { 65 public: 66 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsSkia, override) 67 68 GradientStopsSkia(const std::vector<GradientStop>& aStops, uint32_t aNumStops, 69 ExtendMode aExtendMode) 70 : mCount(aNumStops), mExtendMode(aExtendMode) { 71 if (mCount == 0) { 72 return; 73 } 74 75 // Skia gradients always require a stop at 0.0 and 1.0, insert these if 76 // we don't have them. 77 uint32_t shift = 0; 78 if (aStops[0].offset != 0) { 79 mCount++; 80 shift = 1; 81 } 82 if (aStops[aNumStops - 1].offset != 1) { 83 mCount++; 84 } 85 mColors.resize(mCount); 86 mPositions.resize(mCount); 87 if (aStops[0].offset != 0) { 88 mColors[0] = ColorToSkColor(aStops[0].color, 1.0); 89 mPositions[0] = 0; 90 } 91 for (uint32_t i = 0; i < aNumStops; i++) { 92 mColors[i + shift] = ColorToSkColor(aStops[i].color, 1.0); 93 mPositions[i + shift] = SkFloatToScalar(aStops[i].offset); 94 } 95 if (aStops[aNumStops - 1].offset != 1) { 96 mColors[mCount - 1] = ColorToSkColor(aStops[aNumStops - 1].color, 1.0); 97 mPositions[mCount - 1] = SK_Scalar1; 98 } 99 } 100 101 BackendType GetBackendType() const override { return BackendType::SKIA; } 102 103 std::vector<SkColor> mColors; 104 std::vector<SkScalar> mPositions; 105 int mCount; 106 ExtendMode mExtendMode; 107 }; 108 109 /** 110 * When constructing a temporary SkImage via GetSkImageForSurface, we may also 111 * have to construct a temporary DataSourceSurface, which must live as long as 112 * the SkImage. We attach this temporary surface to the image's pixelref, so 113 * that it can be released once the pixelref is freed. 114 */ 115 static void ReleaseTemporarySurface(const void* aPixels, void* aContext) { 116 DataSourceSurface* surf = static_cast<DataSourceSurface*>(aContext); 117 if (surf) { 118 surf->Release(); 119 } 120 } 121 122 static void ReleaseTemporaryMappedSurface(const void* aPixels, void* aContext) { 123 DataSourceSurface* surf = static_cast<DataSourceSurface*>(aContext); 124 if (surf) { 125 surf->Unmap(); 126 surf->Release(); 127 } 128 } 129 130 static void WriteRGBXFormat(uint8_t* aData, const IntSize& aSize, 131 const int32_t aStride, SurfaceFormat aFormat) { 132 if (aFormat != SurfaceFormat::B8G8R8X8 || aSize.IsEmpty()) { 133 return; 134 } 135 136 SwizzleData(aData, aStride, SurfaceFormat::X8R8G8B8_UINT32, aData, aStride, 137 SurfaceFormat::A8R8G8B8_UINT32, aSize); 138 } 139 140 #ifdef DEBUG 141 static IntRect CalculateSurfaceBounds(const IntSize& aSize, const Rect* aBounds, 142 const Matrix* aMatrix) { 143 IntRect surfaceBounds(IntPoint(0, 0), aSize); 144 if (!aBounds) { 145 return surfaceBounds; 146 } 147 148 MOZ_ASSERT(aMatrix); 149 Matrix inverse(*aMatrix); 150 if (!inverse.Invert()) { 151 return surfaceBounds; 152 } 153 154 IntRect bounds; 155 Rect sampledBounds = inverse.TransformBounds(*aBounds); 156 if (!sampledBounds.ToIntRect(&bounds)) { 157 return surfaceBounds; 158 } 159 160 return surfaceBounds.Intersect(bounds); 161 } 162 163 static const int kARGBAlphaOffset = 164 SurfaceFormat::A8R8G8B8_UINT32 == SurfaceFormat::B8G8R8A8 ? 3 : 0; 165 166 static bool VerifyRGBXFormat(uint8_t* aData, const IntSize& aSize, 167 const int32_t aStride, SurfaceFormat aFormat) { 168 if (aFormat != SurfaceFormat::B8G8R8X8 || aSize.IsEmpty()) { 169 return true; 170 } 171 // We should've initialized the data to be opaque already 172 // On debug builds, verify that this is actually true. 173 int height = aSize.height; 174 int width = aSize.width * 4; 175 176 for (int row = 0; row < height; ++row) { 177 for (int column = 0; column < width; column += 4) { 178 if (aData[column + kARGBAlphaOffset] != 0xFF) { 179 gfxCriticalError() << "RGBX pixel at (" << column << "," << row 180 << ") in " << width << "x" << height 181 << " surface is not opaque: " << int(aData[column]) 182 << "," << int(aData[column + 1]) << "," 183 << int(aData[column + 2]) << "," 184 << int(aData[column + 3]); 185 } 186 } 187 aData += aStride; 188 } 189 190 return true; 191 } 192 193 // Since checking every pixel is expensive, this only checks the four corners 194 // and center of a surface that their alpha value is 0xFF. 195 static bool VerifyRGBXCorners(uint8_t* aData, const IntSize& aSize, 196 const int32_t aStride, SurfaceFormat aFormat, 197 const Rect* aBounds = nullptr, 198 const Matrix* aMatrix = nullptr) { 199 if (aFormat != SurfaceFormat::B8G8R8X8 || aSize.IsEmpty()) { 200 return true; 201 } 202 203 IntRect bounds = CalculateSurfaceBounds(aSize, aBounds, aMatrix); 204 if (bounds.IsEmpty()) { 205 return true; 206 } 207 208 const int height = bounds.Height(); 209 const int width = bounds.Width(); 210 const int pixelSize = 4; 211 MOZ_ASSERT(aSize.width * pixelSize <= aStride); 212 213 const int translation = bounds.Y() * aStride + bounds.X() * pixelSize; 214 const int topLeft = translation; 215 const int topRight = topLeft + (width - 1) * pixelSize; 216 const int bottomLeft = translation + (height - 1) * aStride; 217 const int bottomRight = bottomLeft + (width - 1) * pixelSize; 218 219 // Lastly the center pixel 220 const int middleRowHeight = height / 2; 221 const int middleRowWidth = (width / 2) * pixelSize; 222 const int middle = translation + aStride * middleRowHeight + middleRowWidth; 223 224 const int offsets[] = {topLeft, topRight, bottomRight, bottomLeft, middle}; 225 for (int offset : offsets) { 226 if (aData[offset + kARGBAlphaOffset] != 0xFF) { 227 int row = offset / aStride; 228 int column = (offset % aStride) / pixelSize; 229 gfxCriticalError() << "RGBX corner pixel at (" << column << "," << row 230 << ") in " << aSize.width << "x" << aSize.height 231 << " surface, bounded by " 232 << "(" << bounds.X() << "," << bounds.Y() << "," 233 << width << "," << height 234 << ") is not opaque: " << int(aData[offset]) << "," 235 << int(aData[offset + 1]) << "," 236 << int(aData[offset + 2]) << "," 237 << int(aData[offset + 3]); 238 } 239 } 240 241 return true; 242 } 243 #endif 244 245 static sk_sp<SkImage> GetSkImageForSurface(SourceSurface* aSurface, 246 Maybe<MutexAutoLock>* aLock, 247 const Rect* aBounds = nullptr, 248 const Matrix* aMatrix = nullptr) { 249 if (!aSurface) { 250 gfxDebug() << "Creating null Skia image from null SourceSurface"; 251 return nullptr; 252 } 253 254 if (aSurface->GetType() == SurfaceType::SKIA) { 255 return static_cast<SourceSurfaceSkia*>(aSurface)->GetImage(aLock); 256 } 257 258 RefPtr<DataSourceSurface> dataSurface = aSurface->GetDataSurface(); 259 if (!dataSurface) { 260 gfxWarning() << "Failed getting DataSourceSurface for Skia image"; 261 return nullptr; 262 } 263 264 DataSourceSurface::MappedSurface map; 265 void (*releaseProc)(const void*, void*); 266 if (dataSurface->GetType() == SurfaceType::DATA_SHARED_WRAPPER) { 267 // Technically all surfaces should be mapped and unmapped explicitly but it 268 // appears SourceSurfaceSkia and DataSourceSurfaceWrapper have issues with 269 // this. For now, we just map SourceSurfaceSharedDataWrapper to ensure we 270 // don't unmap the data during the transaction (for blob images). 271 if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) { 272 gfxWarning() << "Failed mapping DataSourceSurface for Skia image"; 273 return nullptr; 274 } 275 releaseProc = ReleaseTemporaryMappedSurface; 276 } else { 277 map.mData = dataSurface->GetData(); 278 map.mStride = dataSurface->Stride(); 279 releaseProc = ReleaseTemporarySurface; 280 } 281 282 if (!map.mData || map.mStride <= 0) { 283 gfxWarning() << "Failed mapping DataSourceSurface for Skia image"; 284 return nullptr; 285 } 286 287 DataSourceSurface* surf = dataSurface.forget().take(); 288 289 // Skia doesn't support RGBX surfaces so ensure that the alpha value is opaque 290 // white. 291 MOZ_ASSERT(VerifyRGBXCorners(map.mData, surf->GetSize(), map.mStride, 292 surf->GetFormat(), aBounds, aMatrix)); 293 294 SkPixmap pixmap(MakeSkiaImageInfo(surf->GetSize(), surf->GetFormat()), 295 map.mData, map.mStride); 296 sk_sp<SkImage> image = SkImages::RasterFromPixmap(pixmap, releaseProc, surf); 297 if (!image) { 298 releaseProc(map.mData, surf); 299 gfxDebug() << "Failed making Skia raster image for temporary surface"; 300 } 301 302 return image; 303 } 304 305 DrawTargetSkia::DrawTargetSkia() 306 : mCanvas(nullptr), 307 mSnapshot(nullptr), 308 mSnapshotLock{"DrawTargetSkia::mSnapshotLock"} 309 #ifdef MOZ_WIDGET_COCOA 310 , 311 mCG(nullptr), 312 mColorSpace(nullptr), 313 mCanvasData(nullptr), 314 mCGSize(0, 0), 315 mNeedLayer(false) 316 #endif 317 { 318 } 319 320 DrawTargetSkia::~DrawTargetSkia() { 321 if (mSnapshot) { 322 MutexAutoLock lock(mSnapshotLock); 323 // We're going to go away, hand our SkSurface to the SourceSurface. 324 mSnapshot->GiveSurface(mSurface.forget().take()); 325 } 326 327 #ifdef MOZ_WIDGET_COCOA 328 if (mCG) { 329 CGContextRelease(mCG); 330 mCG = nullptr; 331 } 332 333 if (mColorSpace) { 334 CGColorSpaceRelease(mColorSpace); 335 mColorSpace = nullptr; 336 } 337 #endif 338 } 339 340 already_AddRefed<SourceSurface> DrawTargetSkia::Snapshot( 341 SurfaceFormat aFormat) { 342 // Without this lock, this could cause us to get out a snapshot and race with 343 // Snapshot::~Snapshot() actually destroying itself. 344 MutexAutoLock lock(mSnapshotLock); 345 if (mSnapshot && aFormat != mSnapshot->GetFormat()) { 346 if (!mSnapshot->hasOneRef()) { 347 mSnapshot->DrawTargetWillChange(); 348 } 349 mSnapshot = nullptr; 350 } 351 RefPtr<SourceSurfaceSkia> snapshot = mSnapshot; 352 if (mSurface && !snapshot) { 353 snapshot = new SourceSurfaceSkia(); 354 sk_sp<SkImage> image; 355 // If the surface is raster, making a snapshot may trigger a pixel copy. 356 // Instead, try to directly make a raster image referencing the surface 357 // pixels. 358 SkPixmap pixmap; 359 if (mSurface->peekPixels(&pixmap)) { 360 image = SkImages::RasterFromPixmap(pixmap, nullptr, nullptr); 361 } else { 362 image = mSurface->makeImageSnapshot(); 363 } 364 if (!snapshot->InitFromImage(image, aFormat, this)) { 365 return nullptr; 366 } 367 mSnapshot = snapshot; 368 } 369 370 return snapshot.forget(); 371 } 372 373 already_AddRefed<SourceSurface> DrawTargetSkia::GetBackingSurface() { 374 if (mBackingSurface) { 375 RefPtr<SourceSurface> snapshot = mBackingSurface; 376 return snapshot.forget(); 377 } 378 return Snapshot(); 379 } 380 381 bool DrawTargetSkia::LockBits(uint8_t** aData, IntSize* aSize, int32_t* aStride, 382 SurfaceFormat* aFormat, IntPoint* aOrigin) { 383 SkImageInfo info; 384 size_t rowBytes; 385 SkIPoint origin; 386 void* pixels = mCanvas->accessTopLayerPixels(&info, &rowBytes, &origin); 387 if (!pixels || 388 // Ensure the layer is at the origin if required. 389 (!aOrigin && !origin.isZero())) { 390 return false; 391 } 392 393 MarkChanged(); 394 395 *aData = reinterpret_cast<uint8_t*>(pixels); 396 *aSize = IntSize(info.width(), info.height()); 397 *aStride = int32_t(rowBytes); 398 *aFormat = SkiaColorTypeToGfxFormat(info.colorType(), info.alphaType()); 399 if (aOrigin) { 400 *aOrigin = IntPoint(origin.x(), origin.y()); 401 } 402 return true; 403 } 404 405 void DrawTargetSkia::ReleaseBits(uint8_t* aData) {} 406 407 static void ReleaseImage(const void* aPixels, void* aContext) { 408 SkImage* image = static_cast<SkImage*>(aContext); 409 SkSafeUnref(image); 410 } 411 412 static sk_sp<SkImage> ExtractSubset(sk_sp<SkImage> aImage, 413 const IntRect& aRect) { 414 SkIRect subsetRect = IntRectToSkIRect(aRect); 415 if (aImage->bounds() == subsetRect) { 416 return aImage; 417 } 418 // makeSubset is slow, so prefer to use SkPixmap::extractSubset where 419 // possible. 420 SkPixmap pixmap, subsetPixmap; 421 if (aImage->peekPixels(&pixmap) && 422 pixmap.extractSubset(&subsetPixmap, subsetRect)) { 423 // Release the original image reference so only the subset image keeps it 424 // alive. 425 return SkImages::RasterFromPixmap(subsetPixmap, ReleaseImage, 426 aImage.release()); 427 } 428 return aImage->makeSubset(nullptr, subsetRect, SkImage::RequiredProperties()); 429 } 430 431 static void FreeAlphaPixels(void* aBuf, void*) { sk_free(aBuf); } 432 433 static void FreeAlphaImage(const void*, void* aBuf) { sk_free(aBuf); } 434 435 static sk_sp<SkImage> ExtractAlphaImage(const sk_sp<SkImage>& aImage, 436 bool aAllowReuse = false) { 437 SkPixmap pixmap; 438 if (aAllowReuse && aImage->isAlphaOnly()) { 439 return aImage; 440 } 441 SkImageInfo info = SkImageInfo::MakeA8(aImage->width(), aImage->height()); 442 // Skia does not fully allocate the last row according to stride. 443 // Since some of our algorithms (i.e. blur) depend on this, we must allocate 444 // the bitmap pixels manually. 445 size_t stride = GetAlignedStride<4>(info.width(), info.bytesPerPixel()); 446 if (stride) { 447 CheckedInt<size_t> size = stride; 448 size *= info.height(); 449 if (size.isValid()) { 450 void* buf = sk_malloc_flags(size.value(), 0); 451 if (buf) { 452 SkPixmap pixmap(info, buf, stride); 453 if (aImage->readPixels(pixmap, 0, 0)) { 454 if (sk_sp<SkImage> result = 455 SkImages::RasterFromPixmap(pixmap, FreeAlphaImage, buf)) { 456 return result; 457 } 458 } 459 sk_free(buf); 460 } 461 } 462 } 463 464 gfxWarning() << "Failed reading alpha pixels for Skia bitmap"; 465 return nullptr; 466 } 467 468 static void SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, 469 Maybe<MutexAutoLock>& aLock, Float aAlpha = 1.0, 470 const SkMatrix* aMatrix = nullptr, 471 const Rect* aBounds = nullptr) { 472 switch (aPattern.GetType()) { 473 case PatternType::COLOR: { 474 DeviceColor color = static_cast<const ColorPattern&>(aPattern).mColor; 475 aPaint.setColor(ColorToSkColor(color, aAlpha)); 476 break; 477 } 478 case PatternType::LINEAR_GRADIENT: { 479 if (StaticPrefs::gfx_skia_dithering_AtStartup()) { 480 aPaint.setDither(true); 481 } 482 const LinearGradientPattern& pat = 483 static_cast<const LinearGradientPattern&>(aPattern); 484 GradientStopsSkia* stops = 485 pat.mStops && pat.mStops->GetBackendType() == BackendType::SKIA 486 ? static_cast<GradientStopsSkia*>(pat.mStops.get()) 487 : nullptr; 488 if (!stops || stops->mCount < 2 || !pat.mBegin.IsFinite() || 489 !pat.mEnd.IsFinite() || pat.mBegin == pat.mEnd) { 490 aPaint.setColor(SK_ColorTRANSPARENT); 491 } else { 492 SkTileMode mode = ExtendModeToTileMode(stops->mExtendMode, Axis::BOTH); 493 SkPoint points[2]; 494 points[0] = SkPoint::Make(SkFloatToScalar(pat.mBegin.x), 495 SkFloatToScalar(pat.mBegin.y)); 496 points[1] = SkPoint::Make(SkFloatToScalar(pat.mEnd.x), 497 SkFloatToScalar(pat.mEnd.y)); 498 499 SkMatrix mat; 500 GfxMatrixToSkiaMatrix(pat.mMatrix, mat); 501 if (aMatrix) { 502 mat.postConcat(*aMatrix); 503 } 504 sk_sp<SkShader> shader = SkGradientShader::MakeLinear( 505 points, &stops->mColors.front(), &stops->mPositions.front(), 506 stops->mCount, mode, 0, &mat); 507 if (shader) { 508 aPaint.setShader(shader); 509 } else { 510 aPaint.setColor(SK_ColorTRANSPARENT); 511 } 512 } 513 break; 514 } 515 case PatternType::RADIAL_GRADIENT: { 516 const RadialGradientPattern& pat = 517 static_cast<const RadialGradientPattern&>(aPattern); 518 GradientStopsSkia* stops = 519 pat.mStops && pat.mStops->GetBackendType() == BackendType::SKIA 520 ? static_cast<GradientStopsSkia*>(pat.mStops.get()) 521 : nullptr; 522 if (!stops || stops->mCount < 2 || !pat.mCenter1.IsFinite() || 523 !std::isfinite(pat.mRadius1) || !pat.mCenter2.IsFinite() || 524 !std::isfinite(pat.mRadius2) || 525 (pat.mCenter1 == pat.mCenter2 && pat.mRadius1 == pat.mRadius2)) { 526 aPaint.setColor(SK_ColorTRANSPARENT); 527 } else { 528 SkTileMode mode = ExtendModeToTileMode(stops->mExtendMode, Axis::BOTH); 529 SkPoint points[2]; 530 points[0] = SkPoint::Make(SkFloatToScalar(pat.mCenter1.x), 531 SkFloatToScalar(pat.mCenter1.y)); 532 points[1] = SkPoint::Make(SkFloatToScalar(pat.mCenter2.x), 533 SkFloatToScalar(pat.mCenter2.y)); 534 535 SkMatrix mat; 536 GfxMatrixToSkiaMatrix(pat.mMatrix, mat); 537 if (aMatrix) { 538 mat.postConcat(*aMatrix); 539 } 540 sk_sp<SkShader> shader = SkGradientShader::MakeTwoPointConical( 541 points[0], SkFloatToScalar(pat.mRadius1), points[1], 542 SkFloatToScalar(pat.mRadius2), &stops->mColors.front(), 543 &stops->mPositions.front(), stops->mCount, mode, 0, &mat); 544 if (shader) { 545 aPaint.setShader(shader); 546 } else { 547 aPaint.setColor(SK_ColorTRANSPARENT); 548 } 549 } 550 break; 551 } 552 case PatternType::CONIC_GRADIENT: { 553 const ConicGradientPattern& pat = 554 static_cast<const ConicGradientPattern&>(aPattern); 555 GradientStopsSkia* stops = 556 pat.mStops && pat.mStops->GetBackendType() == BackendType::SKIA 557 ? static_cast<GradientStopsSkia*>(pat.mStops.get()) 558 : nullptr; 559 if (!stops || stops->mCount < 2 || !pat.mCenter.IsFinite() || 560 !std::isfinite(pat.mAngle)) { 561 aPaint.setColor(SK_ColorTRANSPARENT); 562 } else { 563 SkMatrix mat; 564 GfxMatrixToSkiaMatrix(pat.mMatrix, mat); 565 if (aMatrix) { 566 mat.postConcat(*aMatrix); 567 } 568 569 SkScalar cx = SkFloatToScalar(pat.mCenter.x); 570 SkScalar cy = SkFloatToScalar(pat.mCenter.y); 571 572 // Skia's sweep gradient angles are relative to the x-axis, not the 573 // y-axis. 574 Float angle = (pat.mAngle * 180.0 / M_PI) - 90.0; 575 if (angle != 0.0) { 576 mat.preRotate(angle, cx, cy); 577 } 578 579 SkTileMode mode = ExtendModeToTileMode(stops->mExtendMode, Axis::BOTH); 580 sk_sp<SkShader> shader = SkGradientShader::MakeSweep( 581 cx, cy, &stops->mColors.front(), &stops->mPositions.front(), 582 stops->mCount, mode, 360 * pat.mStartOffset, 360 * pat.mEndOffset, 583 0, &mat); 584 585 if (shader) { 586 aPaint.setShader(shader); 587 } else { 588 aPaint.setColor(SK_ColorTRANSPARENT); 589 } 590 } 591 break; 592 } 593 case PatternType::SURFACE: { 594 const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern); 595 Matrix offsetMatrix = pat.mMatrix; 596 if (pat.mSurface) { 597 offsetMatrix.PreTranslate(pat.mSurface->GetRect().TopLeft()); 598 } 599 sk_sp<SkImage> image = 600 GetSkImageForSurface(pat.mSurface, &aLock, aBounds, &offsetMatrix); 601 if (!image) { 602 aPaint.setColor(SK_ColorTRANSPARENT); 603 break; 604 } 605 606 SkMatrix mat; 607 GfxMatrixToSkiaMatrix(offsetMatrix, mat); 608 if (aMatrix) { 609 mat.postConcat(*aMatrix); 610 } 611 612 if (!pat.mSamplingRect.IsEmpty()) { 613 image = ExtractSubset(image, pat.mSamplingRect); 614 if (!image) { 615 aPaint.setColor(SK_ColorTRANSPARENT); 616 break; 617 } 618 mat.preTranslate(pat.mSamplingRect.X(), pat.mSamplingRect.Y()); 619 } 620 621 SkTileMode xTile = ExtendModeToTileMode(pat.mExtendMode, Axis::X_AXIS); 622 SkTileMode yTile = ExtendModeToTileMode(pat.mExtendMode, Axis::Y_AXIS); 623 624 SkFilterMode filterMode = pat.mSamplingFilter == SamplingFilter::POINT 625 ? SkFilterMode::kNearest 626 : SkFilterMode::kLinear; 627 628 sk_sp<SkShader> shader = 629 image->makeShader(xTile, yTile, SkSamplingOptions(filterMode), mat); 630 if (shader) { 631 aPaint.setShader(shader); 632 } else { 633 gfxDebug() << "Failed creating Skia surface shader: x-tile=" 634 << (int)xTile << " y-tile=" << (int)yTile 635 << " matrix=" << (mat.isFinite() ? "finite" : "non-finite"); 636 aPaint.setColor(SK_ColorTRANSPARENT); 637 } 638 break; 639 } 640 } 641 } 642 643 static inline Rect GetClipBounds(SkCanvas* aCanvas) { 644 // Use a manually transformed getClipDeviceBounds instead of 645 // getClipBounds because getClipBounds inflates the the bounds 646 // by a pixel in each direction to compensate for antialiasing. 647 SkIRect deviceBounds; 648 if (!aCanvas->getDeviceClipBounds(&deviceBounds)) { 649 return Rect(); 650 } 651 SkMatrix inverseCTM; 652 if (!aCanvas->getTotalMatrix().invert(&inverseCTM)) { 653 return Rect(); 654 } 655 SkRect localBounds; 656 inverseCTM.mapRect(&localBounds, SkRect::Make(deviceBounds)); 657 return SkRectToRect(localBounds); 658 } 659 660 struct AutoPaintSetup { 661 AutoPaintSetup(SkCanvas* aCanvas, const DrawOptions& aOptions, 662 const Pattern& aPattern, const Rect* aMaskBounds = nullptr, 663 const SkMatrix* aMatrix = nullptr, 664 const Rect* aSourceBounds = nullptr) 665 : mNeedsRestore(false), mAlpha(1.0) { 666 Init(aCanvas, aOptions, aMaskBounds, false); 667 SetPaintPattern(mPaint, aPattern, mLock, mAlpha, aMatrix, aSourceBounds); 668 } 669 670 AutoPaintSetup(SkCanvas* aCanvas, const DrawOptions& aOptions, 671 const Rect* aMaskBounds = nullptr, bool aForceGroup = false) 672 : mNeedsRestore(false), mAlpha(1.0) { 673 Init(aCanvas, aOptions, aMaskBounds, aForceGroup); 674 } 675 676 ~AutoPaintSetup() { 677 if (mNeedsRestore) { 678 mCanvas->restore(); 679 } 680 } 681 682 void Init(SkCanvas* aCanvas, const DrawOptions& aOptions, 683 const Rect* aMaskBounds, bool aForceGroup) { 684 mPaint.setBlendMode(GfxOpToSkiaOp(aOptions.mCompositionOp)); 685 mCanvas = aCanvas; 686 687 // TODO: Can we set greyscale somehow? 688 if (aOptions.mAntialiasMode != AntialiasMode::NONE) { 689 mPaint.setAntiAlias(true); 690 } else { 691 mPaint.setAntiAlias(false); 692 } 693 694 bool needsGroup = 695 aForceGroup || 696 (!IsOperatorBoundByMask(aOptions.mCompositionOp) && 697 (!aMaskBounds || !aMaskBounds->Contains(GetClipBounds(aCanvas)))); 698 699 // TODO: We could skip the temporary for operator_source and just 700 // clear the clip rect. The other operators would be harder 701 // but could be worth it to skip pushing a group. 702 if (needsGroup) { 703 mPaint.setBlendMode(SkBlendMode::kSrcOver); 704 SkPaint temp; 705 temp.setBlendMode(GfxOpToSkiaOp(aOptions.mCompositionOp)); 706 temp.setAlpha(ColorFloatToByte(aOptions.mAlpha)); 707 // TODO: Get a rect here 708 SkCanvas::SaveLayerRec rec(nullptr, &temp, 709 SkCanvas::kPreserveLCDText_SaveLayerFlag); 710 mCanvas->saveLayer(rec); 711 mNeedsRestore = true; 712 } else { 713 mPaint.setAlpha(ColorFloatToByte(aOptions.mAlpha)); 714 mAlpha = aOptions.mAlpha; 715 } 716 } 717 718 // TODO: Maybe add an operator overload to access this easier? 719 SkPaint mPaint; 720 bool mNeedsRestore; 721 SkCanvas* mCanvas; 722 Maybe<MutexAutoLock> mLock; 723 Float mAlpha; 724 }; 725 726 void DrawTargetSkia::Flush() {} 727 728 void DrawTargetSkia::DrawSurface(SourceSurface* aSurface, const Rect& aDest, 729 const Rect& aSource, 730 const DrawSurfaceOptions& aSurfOptions, 731 const DrawOptions& aOptions) { 732 if (aSource.IsEmpty()) { 733 return; 734 } 735 736 MarkChanged(); 737 738 Maybe<MutexAutoLock> lock; 739 sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock); 740 if (!image) { 741 return; 742 } 743 744 SkRect destRect = RectToSkRect(aDest); 745 SkRect sourceRect = RectToSkRect(aSource - aSurface->GetRect().TopLeft()); 746 bool forceGroup = 747 image->isAlphaOnly() && aOptions.mCompositionOp != CompositionOp::OP_OVER; 748 749 AutoPaintSetup paint(mCanvas, aOptions, &aDest, forceGroup); 750 751 SkFilterMode filterMode = 752 aSurfOptions.mSamplingFilter == SamplingFilter::POINT 753 ? SkFilterMode::kNearest 754 : SkFilterMode::kLinear; 755 756 mCanvas->drawImageRect(image, sourceRect, destRect, 757 SkSamplingOptions(filterMode), &paint.mPaint, 758 SkCanvas::kStrict_SrcRectConstraint); 759 } 760 761 DrawTargetType DrawTargetSkia::GetType() const { 762 return DrawTargetType::SOFTWARE_RASTER; 763 } 764 765 void DrawTargetSkia::DrawFilter(FilterNode* aNode, const Rect& aSourceRect, 766 const Point& aDestPoint, 767 const DrawOptions& aOptions) { 768 if (!aNode || aNode->GetBackendType() != FILTER_BACKEND_SOFTWARE) { 769 return; 770 } 771 FilterNodeSoftware* filter = static_cast<FilterNodeSoftware*>(aNode); 772 filter->Draw(this, aSourceRect, aDestPoint, aOptions); 773 } 774 775 void DrawTargetSkia::DrawSurfaceWithShadow(SourceSurface* aSurface, 776 const Point& aDest, 777 const ShadowOptions& aShadow, 778 CompositionOp aOperator) { 779 if (aSurface->GetSize().IsEmpty()) { 780 return; 781 } 782 783 MarkChanged(); 784 785 Maybe<MutexAutoLock> lock; 786 sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock); 787 if (!image) { 788 return; 789 } 790 791 mCanvas->save(); 792 mCanvas->resetMatrix(); 793 794 SkPaint paint; 795 paint.setBlendMode(GfxOpToSkiaOp(aOperator)); 796 797 // bug 1201272 798 // We can't use the SkDropShadowImageFilter here because it applies the xfer 799 // mode first to render the bitmap to a temporary layer, and then implicitly 800 // uses src-over to composite the resulting shadow. 801 // The canvas spec, however, states that the composite op must be used to 802 // composite the resulting shadow, so we must instead use a SkBlurImageFilter 803 // to blur the image ourselves. 804 805 SkPaint shadowPaint; 806 shadowPaint.setBlendMode(GfxOpToSkiaOp(aOperator)); 807 808 auto shadowDest = IntPoint::Round(aDest + aShadow.mOffset); 809 810 sk_sp<SkImageFilter> blurFilter( 811 SkImageFilters::Blur(aShadow.mSigma, aShadow.mSigma, nullptr)); 812 813 shadowPaint.setImageFilter(blurFilter); 814 shadowPaint.setColor(ColorToSkColor(aShadow.mColor, 1.0f)); 815 816 // Extract the alpha channel of the image into a bitmap. If the image is A8 817 // format already, then we can directly reuse the bitmap rather than create a 818 // new one as the surface only needs to be drawn from once. 819 if (sk_sp<SkImage> alphaImage = ExtractAlphaImage(image, true)) { 820 mCanvas->drawImage(alphaImage, shadowDest.x, shadowDest.y, 821 SkSamplingOptions(SkFilterMode::kLinear), &shadowPaint); 822 } 823 824 if (aSurface->GetFormat() != SurfaceFormat::A8) { 825 // Composite the original image after the shadow 826 auto dest = IntPoint::Round(aDest); 827 mCanvas->drawImage(image, dest.x, dest.y, 828 SkSamplingOptions(SkFilterMode::kLinear), &paint); 829 } 830 831 mCanvas->restore(); 832 } 833 834 void DrawTargetSkia::Blur(const GaussianBlur& aBlur) { 835 if (mSurface) { 836 MarkChanged(); 837 if (aBlur.BlurSkSurface(mSurface)) { 838 return; 839 } 840 } 841 DrawTarget::Blur(aBlur); 842 } 843 844 void DrawTargetSkia::FillRect(const Rect& aRect, const Pattern& aPattern, 845 const DrawOptions& aOptions) { 846 // The sprite blitting path in Skia can be faster than the shader blitter for 847 // operators other than source (or source-over with opaque surface). So, when 848 // possible/beneficial, route to DrawSurface which will use the sprite 849 // blitter. 850 if (aPattern.GetType() == PatternType::SURFACE && 851 aOptions.mCompositionOp != CompositionOp::OP_SOURCE) { 852 const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern); 853 // Verify there is a valid surface and a pattern matrix without skew. 854 if (pat.mSurface && 855 (aOptions.mCompositionOp != CompositionOp::OP_OVER || 856 GfxFormatToSkiaAlphaType(pat.mSurface->GetFormat()) != 857 kOpaque_SkAlphaType) && 858 !pat.mMatrix.HasNonAxisAlignedTransform()) { 859 // Bound the sampling to smaller of the bounds or the sampling rect. 860 IntRect surfaceBounds = pat.mSurface->GetRect(); 861 IntRect srcRect(IntPoint(0, 0), surfaceBounds.Size()); 862 if (!pat.mSamplingRect.IsEmpty()) { 863 srcRect = srcRect.Intersect(pat.mSamplingRect); 864 } 865 srcRect.MoveBy(surfaceBounds.TopLeft()); 866 // Transform the destination rectangle by the inverse of the pattern 867 // matrix so that it is in pattern space like the source rectangle. 868 Rect patRect = aRect - pat.mMatrix.GetTranslation(); 869 patRect.Scale(1.0f / pat.mMatrix._11, 1.0f / pat.mMatrix._22); 870 // Verify the pattern rectangle will not tile or clamp. 871 if (!patRect.IsEmpty() && srcRect.Contains(RoundedOut(patRect))) { 872 // The pattern is a surface with an axis-aligned source rectangle 873 // fitting entirely in its bounds, so just treat it as a DrawSurface. 874 DrawSurface(pat.mSurface, aRect, patRect, 875 DrawSurfaceOptions(pat.mSamplingFilter), aOptions); 876 return; 877 } 878 } 879 } 880 881 MarkChanged(); 882 SkRect rect = RectToSkRect(aRect); 883 AutoPaintSetup paint(mCanvas, aOptions, aPattern, &aRect, nullptr, &aRect); 884 885 mCanvas->drawRect(rect, paint.mPaint); 886 } 887 888 void DrawTargetSkia::Stroke(const Path* aPath, const Pattern& aPattern, 889 const StrokeOptions& aStrokeOptions, 890 const DrawOptions& aOptions) { 891 MarkChanged(); 892 MOZ_ASSERT(aPath, "Null path"); 893 if (aPath->GetBackendType() != BackendType::SKIA) { 894 return; 895 } 896 897 const PathSkia* skiaPath = static_cast<const PathSkia*>(aPath); 898 899 AutoPaintSetup paint(mCanvas, aOptions, aPattern); 900 if (!StrokeOptionsToPaint(paint.mPaint, aStrokeOptions)) { 901 return; 902 } 903 904 if (!skiaPath->GetPath().isFinite()) { 905 return; 906 } 907 908 mCanvas->drawPath(skiaPath->GetPath(), paint.mPaint); 909 } 910 911 static Double DashPeriodLength(const StrokeOptions& aStrokeOptions) { 912 Double length = 0; 913 for (size_t i = 0; i < aStrokeOptions.mDashLength; i++) { 914 length += aStrokeOptions.mDashPattern[i]; 915 } 916 if (aStrokeOptions.mDashLength & 1) { 917 // "If an odd number of values is provided, then the list of values is 918 // repeated to yield an even number of values." 919 // Double the length. 920 length += length; 921 } 922 return length; 923 } 924 925 static inline Double RoundDownToMultiple(Double aValue, Double aFactor) { 926 return floor(aValue / aFactor) * aFactor; 927 } 928 929 static Rect UserSpaceStrokeClip(const IntRect& aDeviceClip, 930 const Matrix& aTransform, 931 const StrokeOptions& aStrokeOptions) { 932 Matrix inverse = aTransform; 933 if (!inverse.Invert()) { 934 return Rect(); 935 } 936 Rect deviceClip(aDeviceClip); 937 deviceClip.Inflate(MaxStrokeExtents(aStrokeOptions, aTransform)); 938 return inverse.TransformBounds(deviceClip); 939 } 940 941 static Rect ShrinkClippedStrokedRect(const Rect& aStrokedRect, 942 const IntRect& aDeviceClip, 943 const Matrix& aTransform, 944 const StrokeOptions& aStrokeOptions) { 945 Rect userSpaceStrokeClip = 946 UserSpaceStrokeClip(aDeviceClip, aTransform, aStrokeOptions); 947 RectDouble strokedRectDouble(aStrokedRect.X(), aStrokedRect.Y(), 948 aStrokedRect.Width(), aStrokedRect.Height()); 949 RectDouble intersection = strokedRectDouble.Intersect( 950 RectDouble(userSpaceStrokeClip.X(), userSpaceStrokeClip.Y(), 951 userSpaceStrokeClip.Width(), userSpaceStrokeClip.Height())); 952 Double dashPeriodLength = DashPeriodLength(aStrokeOptions); 953 if (intersection.IsEmpty() || dashPeriodLength == 0.0f) { 954 return Rect(intersection.X(), intersection.Y(), intersection.Width(), 955 intersection.Height()); 956 } 957 958 // Reduce the rectangle side lengths in multiples of the dash period length 959 // so that the visible dashes stay in the same place. 960 MarginDouble insetBy = strokedRectDouble - intersection; 961 insetBy.top = RoundDownToMultiple(insetBy.top, dashPeriodLength); 962 insetBy.right = RoundDownToMultiple(insetBy.right, dashPeriodLength); 963 insetBy.bottom = RoundDownToMultiple(insetBy.bottom, dashPeriodLength); 964 insetBy.left = RoundDownToMultiple(insetBy.left, dashPeriodLength); 965 966 strokedRectDouble.Deflate(insetBy); 967 return Rect(strokedRectDouble.X(), strokedRectDouble.Y(), 968 strokedRectDouble.Width(), strokedRectDouble.Height()); 969 } 970 971 void DrawTargetSkia::StrokeRect(const Rect& aRect, const Pattern& aPattern, 972 const StrokeOptions& aStrokeOptions, 973 const DrawOptions& aOptions) { 974 // Stroking large rectangles with dashes is expensive with Skia (fixed 975 // overhead based on the number of dashes, regardless of whether the dashes 976 // are visible), so we try to reduce the size of the stroked rectangle as 977 // much as possible before passing it on to Skia. 978 Rect rect = aRect; 979 if (aStrokeOptions.mDashLength > 0 && !rect.IsEmpty()) { 980 IntRect deviceClip(IntPoint(0, 0), mSize); 981 SkIRect clipBounds; 982 if (mCanvas->getDeviceClipBounds(&clipBounds)) { 983 deviceClip = SkIRectToIntRect(clipBounds); 984 } 985 rect = 986 ShrinkClippedStrokedRect(rect, deviceClip, mTransform, aStrokeOptions); 987 if (rect.IsEmpty()) { 988 return; 989 } 990 } 991 992 MarkChanged(); 993 AutoPaintSetup paint(mCanvas, aOptions, aPattern); 994 if (!StrokeOptionsToPaint(paint.mPaint, aStrokeOptions)) { 995 return; 996 } 997 998 mCanvas->drawRect(RectToSkRect(rect), paint.mPaint); 999 } 1000 1001 void DrawTargetSkia::StrokeLine(const Point& aStart, const Point& aEnd, 1002 const Pattern& aPattern, 1003 const StrokeOptions& aStrokeOptions, 1004 const DrawOptions& aOptions) { 1005 MarkChanged(); 1006 AutoPaintSetup paint(mCanvas, aOptions, aPattern); 1007 if (!StrokeOptionsToPaint(paint.mPaint, aStrokeOptions)) { 1008 return; 1009 } 1010 1011 mCanvas->drawLine(SkFloatToScalar(aStart.x), SkFloatToScalar(aStart.y), 1012 SkFloatToScalar(aEnd.x), SkFloatToScalar(aEnd.y), 1013 paint.mPaint); 1014 } 1015 1016 void DrawTargetSkia::Fill(const Path* aPath, const Pattern& aPattern, 1017 const DrawOptions& aOptions) { 1018 MarkChanged(); 1019 if (!aPath || aPath->GetBackendType() != BackendType::SKIA) { 1020 return; 1021 } 1022 1023 const PathSkia* skiaPath = static_cast<const PathSkia*>(aPath); 1024 1025 AutoPaintSetup paint(mCanvas, aOptions, aPattern); 1026 1027 if (!skiaPath->GetPath().isFinite()) { 1028 return; 1029 } 1030 1031 mCanvas->drawPath(skiaPath->GetPath(), paint.mPaint); 1032 } 1033 1034 #ifdef XP_DARWIN 1035 static inline CGAffineTransform GfxMatrixToCGAffineTransform(const Matrix& m) { 1036 CGAffineTransform t; 1037 t.a = m._11; 1038 t.b = m._12; 1039 t.c = m._21; 1040 t.d = m._22; 1041 t.tx = m._31; 1042 t.ty = m._32; 1043 return t; 1044 } 1045 1046 /*** 1047 * We have to do a lot of work to draw glyphs with CG because 1048 * CG assumes that the origin of rects are in the bottom left 1049 * while every other DrawTarget assumes the top left is the origin. 1050 * This means we have to transform the CGContext to have rects 1051 * actually be applied in top left fashion. We do this by: 1052 * 1053 * 1) Translating the context up by the height of the canvas 1054 * 2) Flipping the context by the Y axis so it's upside down. 1055 * 1056 * These two transforms put the origin in the top left. 1057 * Transforms are better understood thinking about them from right to left order 1058 * (mathematically). 1059 * 1060 * Consider a point we want to draw at (0, 10) in normal cartesian planes with 1061 * a box of (100, 100). in CG terms, this would be at (0, 10). 1062 * Positive Y values point up. 1063 * In our DrawTarget terms, positive Y values point down, so (0, 10) would be 1064 * at (0, 90) in cartesian plane terms. That means our point at (0, 10) in 1065 * DrawTarget terms should end up at (0, 90). How does this work with the 1066 * current transforms? 1067 * 1068 * Going right to left with the transforms, a CGPoint of (0, 10) has cartesian 1069 * coordinates of (0, 10). The first flip of the Y axis puts the point now at 1070 * (0, -10); Next, we translate the context up by the size of the canvas 1071 * (Positive Y values go up in CG coordinates but down in our draw target 1072 * coordinates). Since our canvas size is (100, 100), the resulting coordinate 1073 * becomes (0, 90), which is what we expect from our DrawTarget code. These two 1074 * transforms put the CG context equal to what every other DrawTarget expects. 1075 * 1076 * Next, we need two more transforms for actual text. IF we left the transforms 1077 * as is, the text would be drawn upside down, so we need another flip of the Y 1078 * axis to draw the text right side up. However, with only the flip, the text 1079 * would be drawn in the wrong place. Thus we also have to invert the Y position 1080 * of the glyphs to get them in the right place. 1081 * 1082 * Thus we have the following transforms: 1083 * 1) Translation of the context up 1084 * 2) Flipping the context around the Y axis 1085 * 3) Flipping the context around the Y axis 1086 * 4) Inverting the Y position of each glyph 1087 * 1088 * We cannot cancel out (2) and (3) as we have to apply the clips and transforms 1089 * of DrawTargetSkia between (2) and (3). 1090 * 1091 * Consider the example letter P, drawn at (0, 20) in CG coordinates in a 1092 * (100, 100) rect. 1093 * Again, going right to left of the transforms. We'd get: 1094 * 1095 * 1) The letter P drawn at (0, -20) due to the inversion of the Y axis 1096 * 2) The letter P upside down (b) at (0, 20) due to the second flip 1097 * 3) The letter P right side up at (0, -20) due to the first flip 1098 * 4) The letter P right side up at (0, 80) due to the translation 1099 * 1100 * tl;dr - CGRects assume origin is bottom left, DrawTarget rects assume top 1101 * left. 1102 */ 1103 static bool SetupCGContext(DrawTargetSkia* aDT, CGContextRef aCGContext, 1104 SkCanvas* aCanvas, const IntPoint& aOrigin, 1105 const IntSize& aSize, bool aClipped) { 1106 // DrawTarget expects the origin to be at the top left, but CG 1107 // expects it to be at the bottom left. Transform to set the origin to 1108 // the top left. Have to set this before we do anything else. 1109 // This is transform (1) up top 1110 CGContextTranslateCTM(aCGContext, -aOrigin.x, aOrigin.y + aSize.height); 1111 1112 // Transform (2) from the comments. 1113 CGContextScaleCTM(aCGContext, 1, -1); 1114 1115 // Want to apply clips BEFORE the transform since the transform 1116 // will apply to the clips we apply. 1117 if (aClipped) { 1118 SkRegion clipRegion; 1119 aCanvas->temporary_internal_getRgnClip(&clipRegion); 1120 Vector<CGRect, 8> rects; 1121 for (SkRegion::Iterator it(clipRegion); !it.done(); it.next()) { 1122 const SkIRect& rect = it.rect(); 1123 if (!rects.append( 1124 CGRectMake(rect.x(), rect.y(), rect.width(), rect.height()))) { 1125 break; 1126 } 1127 } 1128 if (rects.length()) { 1129 CGContextClipToRects(aCGContext, rects.begin(), rects.length()); 1130 } 1131 } 1132 1133 CGContextConcatCTM(aCGContext, 1134 GfxMatrixToCGAffineTransform(aDT->GetTransform())); 1135 return true; 1136 } 1137 // End long comment about transforms. 1138 1139 // The context returned from this method will have the origin 1140 // in the top left and will have applied all the neccessary clips 1141 // and transforms to the CGContext. See the comment above 1142 // SetupCGContext. 1143 CGContextRef DrawTargetSkia::BorrowCGContext(const DrawOptions& aOptions) { 1144 // Since we can't replay Skia clips, we have to use a layer if we have a 1145 // complex clip. After saving a layer, the SkCanvas queries for needing a 1146 // layer change so save if we pushed a layer. 1147 mNeedLayer = !mCanvas->isClipEmpty() && !mCanvas->isClipRect(); 1148 if (mNeedLayer) { 1149 SkPaint paint; 1150 paint.setBlendMode(SkBlendMode::kSrc); 1151 SkCanvas::SaveLayerRec rec(nullptr, &paint, 1152 SkCanvas::kInitWithPrevious_SaveLayerFlag); 1153 mCanvas->saveLayer(rec); 1154 } 1155 1156 uint8_t* data = nullptr; 1157 int32_t stride; 1158 SurfaceFormat format; 1159 IntSize size; 1160 IntPoint origin; 1161 if (!LockBits(&data, &size, &stride, &format, &origin)) { 1162 NS_WARNING("Could not lock skia bits to wrap CG around"); 1163 return nullptr; 1164 } 1165 1166 if (!mNeedLayer && (data == mCanvasData) && mCG && (mCGSize == size)) { 1167 // If our canvas data still points to the same data, 1168 // we can reuse the CG Context 1169 CGContextSetAlpha(mCG, aOptions.mAlpha); 1170 CGContextSetShouldAntialias(mCG, 1171 aOptions.mAntialiasMode != AntialiasMode::NONE); 1172 CGContextSaveGState(mCG); 1173 SetupCGContext(this, mCG, mCanvas, origin, size, true); 1174 return mCG; 1175 } 1176 1177 if (!mColorSpace) { 1178 mColorSpace = (format == SurfaceFormat::A8) ? CGColorSpaceCreateDeviceGray() 1179 : CGColorSpaceCreateDeviceRGB(); 1180 } 1181 1182 if (mCG) { 1183 // Release the old CG context since it's no longer valid. 1184 CGContextRelease(mCG); 1185 } 1186 1187 mCanvasData = data; 1188 mCGSize = size; 1189 1190 uint32_t bitmapInfo = 1191 (format == SurfaceFormat::A8) 1192 ? kCGImageAlphaOnly 1193 : kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host; 1194 1195 mCG = CGBitmapContextCreateWithData( 1196 mCanvasData, mCGSize.width, mCGSize.height, 8, /* bits per component */ 1197 stride, mColorSpace, bitmapInfo, NULL, /* Callback when released */ 1198 NULL); 1199 if (!mCG) { 1200 if (mNeedLayer) { 1201 mCanvas->restore(); 1202 } 1203 ReleaseBits(mCanvasData); 1204 NS_WARNING("Could not create bitmap around skia data\n"); 1205 return nullptr; 1206 } 1207 1208 CGContextSetAlpha(mCG, aOptions.mAlpha); 1209 CGContextSetShouldAntialias(mCG, 1210 aOptions.mAntialiasMode != AntialiasMode::NONE); 1211 CGContextSetShouldSmoothFonts(mCG, true); 1212 CGContextSetTextDrawingMode(mCG, kCGTextFill); 1213 CGContextSaveGState(mCG); 1214 SetupCGContext(this, mCG, mCanvas, origin, size, !mNeedLayer); 1215 return mCG; 1216 } 1217 1218 void DrawTargetSkia::ReturnCGContext(CGContextRef aCGContext) { 1219 MOZ_ASSERT(aCGContext == mCG); 1220 ReleaseBits(mCanvasData); 1221 CGContextRestoreGState(aCGContext); 1222 1223 if (mNeedLayer) { 1224 // A layer was used for clipping and is about to be popped by the restore. 1225 // Make sure the CG context referencing it is released first so the popped 1226 // layer doesn't accidentally get used. 1227 if (mCG) { 1228 CGContextRelease(mCG); 1229 mCG = nullptr; 1230 } 1231 mCanvas->restore(); 1232 } 1233 } 1234 1235 CGContextRef BorrowedCGContext::BorrowCGContextFromDrawTarget(DrawTarget* aDT) { 1236 DrawTargetSkia* skiaDT = static_cast<DrawTargetSkia*>(aDT); 1237 return skiaDT->BorrowCGContext(DrawOptions()); 1238 } 1239 1240 void BorrowedCGContext::ReturnCGContextToDrawTarget(DrawTarget* aDT, 1241 CGContextRef cg) { 1242 DrawTargetSkia* skiaDT = static_cast<DrawTargetSkia*>(aDT); 1243 skiaDT->ReturnCGContext(cg); 1244 } 1245 #endif 1246 1247 static bool CanDrawFont(ScaledFont* aFont) { 1248 switch (aFont->GetType()) { 1249 case FontType::FREETYPE: 1250 case FontType::FONTCONFIG: 1251 case FontType::MAC: 1252 case FontType::GDI: 1253 case FontType::DWRITE: 1254 return true; 1255 default: 1256 return false; 1257 } 1258 } 1259 1260 void DrawTargetSkia::DrawGlyphs(ScaledFont* aFont, const GlyphBuffer& aBuffer, 1261 const Pattern& aPattern, 1262 const StrokeOptions* aStrokeOptions, 1263 const DrawOptions& aOptions, 1264 SkShader* aShader) { 1265 if (!CanDrawFont(aFont)) { 1266 return; 1267 } 1268 1269 MarkChanged(); 1270 1271 ScaledFontBase* skiaFont = static_cast<ScaledFontBase*>(aFont); 1272 SkTypeface* typeface = skiaFont->GetSkTypeface(); 1273 if (!typeface) { 1274 return; 1275 } 1276 1277 AutoPaintSetup paint(mCanvas, aOptions, aPattern); 1278 if (aStrokeOptions && !StrokeOptionsToPaint(paint.mPaint, *aStrokeOptions)) { 1279 return; 1280 } 1281 1282 AntialiasMode aaMode = aFont->GetDefaultAAMode(); 1283 if (aOptions.mAntialiasMode != AntialiasMode::DEFAULT) { 1284 aaMode = aOptions.mAntialiasMode; 1285 } 1286 bool aaEnabled = aaMode != AntialiasMode::NONE; 1287 paint.mPaint.setAntiAlias(aaEnabled); 1288 1289 SkFont font(sk_ref_sp(typeface), SkFloatToScalar(skiaFont->mSize)); 1290 1291 bool useSubpixelAA = 1292 GetPermitSubpixelAA() && 1293 (aaMode == AntialiasMode::DEFAULT || aaMode == AntialiasMode::SUBPIXEL); 1294 font.setEdging(useSubpixelAA ? SkFont::Edging::kSubpixelAntiAlias 1295 : (aaEnabled ? SkFont::Edging::kAntiAlias 1296 : SkFont::Edging::kAlias)); 1297 1298 skiaFont->SetupSkFontDrawOptions(font); 1299 1300 if (aShader) { 1301 paint.mPaint.setShader(sk_ref_sp(aShader)); 1302 } 1303 1304 // Limit the amount of internal batch allocations Skia does. 1305 const uint32_t kMaxGlyphBatchSize = 8192; 1306 1307 for (uint32_t offset = 0; offset < aBuffer.mNumGlyphs;) { 1308 uint32_t batchSize = 1309 std::min(aBuffer.mNumGlyphs - offset, kMaxGlyphBatchSize); 1310 SkTextBlobBuilder builder; 1311 auto runBuffer = builder.allocRunPos(font, batchSize); 1312 for (uint32_t i = 0; i < batchSize; i++, offset++) { 1313 runBuffer.glyphs[i] = aBuffer.mGlyphs[offset].mIndex; 1314 runBuffer.points()[i] = PointToSkPoint(aBuffer.mGlyphs[offset].mPosition); 1315 } 1316 1317 sk_sp<SkTextBlob> text = builder.make(); 1318 mCanvas->drawTextBlob(text, 0, 0, paint.mPaint); 1319 } 1320 } 1321 1322 // This shader overrides the luminance color used to generate the preblend 1323 // tables for glyphs, without actually changing the rasterized color. This is 1324 // necesary for subpixel AA blending which requires both the mask and color 1325 // as separate inputs. 1326 class GlyphMaskShader : public SkEmptyShader { 1327 public: 1328 explicit GlyphMaskShader(const DeviceColor& aColor) 1329 : mColor({aColor.r, aColor.g, aColor.b, aColor.a}) {} 1330 1331 bool onAsLuminanceColor(SkColor4f* aLum) const override { 1332 *aLum = mColor; 1333 return true; 1334 } 1335 1336 bool isOpaque() const override { return true; } 1337 bool isConstant(SkColor4f* color) const override { 1338 if (color) *color = SkColor4f{1, 1, 1, 1}; 1339 return true; 1340 } 1341 1342 void flatten(SkWriteBuffer& buffer) const override { 1343 buffer.writeColor4f(mColor); 1344 } 1345 1346 bool appendStages(const SkStageRec& rec, 1347 const SkShaders::MatrixRec&) const override { 1348 rec.fPipeline->appendConstantColor(rec.fAlloc, 1349 SkColor4f{1, 1, 1, 1}.premul().vec()); 1350 return true; 1351 } 1352 1353 private: 1354 SkColor4f mColor; 1355 }; 1356 1357 void DrawTargetSkia::DrawGlyphMask(ScaledFont* aFont, 1358 const GlyphBuffer& aBuffer, 1359 const DeviceColor& aColor, 1360 const StrokeOptions* aStrokeOptions, 1361 const DrawOptions& aOptions) { 1362 // Draw a mask using the GlyphMaskShader that can be used for subpixel AA 1363 // but that uses the gamma preblend weighting of the given color, even though 1364 // the mask itself does not use that color. 1365 sk_sp<GlyphMaskShader> shader = sk_make_sp<GlyphMaskShader>(aColor); 1366 DrawGlyphs(aFont, aBuffer, ColorPattern(DeviceColor(1, 1, 1, 1)), 1367 aStrokeOptions, aOptions, shader.get()); 1368 } 1369 1370 Maybe<Rect> DrawTargetSkia::GetGlyphLocalBounds( 1371 ScaledFont* aFont, const GlyphBuffer& aBuffer, const Pattern& aPattern, 1372 const StrokeOptions* aStrokeOptions, const DrawOptions& aOptions) { 1373 if (!CanDrawFont(aFont)) { 1374 return Nothing(); 1375 } 1376 1377 ScaledFontBase* skiaFont = static_cast<ScaledFontBase*>(aFont); 1378 SkTypeface* typeface = skiaFont->GetSkTypeface(); 1379 if (!typeface) { 1380 return Nothing(); 1381 } 1382 1383 AutoPaintSetup paint(mCanvas, aOptions, aPattern); 1384 if (aStrokeOptions && !StrokeOptionsToPaint(paint.mPaint, *aStrokeOptions)) { 1385 return Nothing(); 1386 } 1387 1388 AntialiasMode aaMode = aFont->GetDefaultAAMode(); 1389 if (aOptions.mAntialiasMode != AntialiasMode::DEFAULT) { 1390 aaMode = aOptions.mAntialiasMode; 1391 } 1392 bool aaEnabled = aaMode != AntialiasMode::NONE; 1393 paint.mPaint.setAntiAlias(aaEnabled); 1394 1395 SkFont font(sk_ref_sp(typeface), SkFloatToScalar(skiaFont->mSize)); 1396 1397 bool useSubpixelAA = 1398 GetPermitSubpixelAA() && 1399 (aaMode == AntialiasMode::DEFAULT || aaMode == AntialiasMode::SUBPIXEL); 1400 font.setEdging(useSubpixelAA ? SkFont::Edging::kSubpixelAntiAlias 1401 : (aaEnabled ? SkFont::Edging::kAntiAlias 1402 : SkFont::Edging::kAlias)); 1403 1404 skiaFont->SetupSkFontDrawOptions(font); 1405 1406 // Limit the amount of internal batch allocations Skia does. 1407 const uint32_t kMaxGlyphBatchSize = 8192; 1408 1409 // Avoid using TextBlobBuilder for bounds computations as the conservative 1410 // bounds can be wrong due to buggy font metrics. Instead, explicitly compute 1411 // tight bounds directly with the SkFont. 1412 Vector<SkGlyphID, 32> glyphs; 1413 Vector<SkRect, 32> rects; 1414 Rect bounds; 1415 for (uint32_t offset = 0; offset < aBuffer.mNumGlyphs;) { 1416 uint32_t batchSize = 1417 std::min(aBuffer.mNumGlyphs - offset, kMaxGlyphBatchSize); 1418 if (glyphs.resizeUninitialized(batchSize) && 1419 rects.resizeUninitialized(batchSize)) { 1420 for (uint32_t i = 0; i < batchSize; i++) { 1421 glyphs[i] = aBuffer.mGlyphs[offset + i].mIndex; 1422 } 1423 font.getBounds({glyphs.begin(), batchSize}, {rects.begin(), batchSize}, 1424 nullptr); 1425 for (uint32_t i = 0; i < batchSize; i++) { 1426 bounds = bounds.Union(SkRectToRect(rects[i]) + 1427 aBuffer.mGlyphs[offset + i].mPosition); 1428 } 1429 } 1430 offset += batchSize; 1431 } 1432 1433 SkRect storage; 1434 bounds = SkRectToRect( 1435 paint.mPaint.computeFastBounds(RectToSkRect(bounds), &storage)); 1436 1437 if (bounds.IsEmpty()) { 1438 return Nothing(); 1439 } 1440 1441 // Inflate the bounds to account for potential font hinting. 1442 bounds.Inflate(1); 1443 return Some(bounds); 1444 } 1445 1446 void DrawTargetSkia::FillGlyphs(ScaledFont* aFont, const GlyphBuffer& aBuffer, 1447 const Pattern& aPattern, 1448 const DrawOptions& aOptions) { 1449 DrawGlyphs(aFont, aBuffer, aPattern, nullptr, aOptions); 1450 } 1451 1452 void DrawTargetSkia::StrokeGlyphs(ScaledFont* aFont, const GlyphBuffer& aBuffer, 1453 const Pattern& aPattern, 1454 const StrokeOptions& aStrokeOptions, 1455 const DrawOptions& aOptions) { 1456 DrawGlyphs(aFont, aBuffer, aPattern, &aStrokeOptions, aOptions); 1457 } 1458 1459 void DrawTargetSkia::Mask(const Pattern& aSource, const Pattern& aMask, 1460 const DrawOptions& aOptions) { 1461 Maybe<MutexAutoLock> lock; 1462 SkPaint maskPaint; 1463 SetPaintPattern(maskPaint, aMask, lock); 1464 1465 sk_sp<SkShader> maskShader(maskPaint.refShader()); 1466 if (!maskShader && maskPaint.getAlpha() != 0xFF) { 1467 if (maskPaint.getAlpha() == 0) { 1468 return; 1469 } 1470 maskShader = SkShaders::Color(maskPaint.getColor()); 1471 if (!maskShader) { 1472 gfxDebug() << "Failed creating Skia clip shader for Mask"; 1473 return; 1474 } 1475 } 1476 1477 MarkChanged(); 1478 AutoPaintSetup paint(mCanvas, aOptions, aSource); 1479 1480 mCanvas->save(); 1481 if (maskShader) { 1482 mCanvas->clipShader(maskShader); 1483 } 1484 1485 mCanvas->drawPaint(paint.mPaint); 1486 1487 mCanvas->restore(); 1488 } 1489 1490 void DrawTargetSkia::MaskSurface(const Pattern& aSource, SourceSurface* aMask, 1491 Point aOffset, const DrawOptions& aOptions) { 1492 Maybe<MutexAutoLock> lock; 1493 sk_sp<SkImage> maskImage = GetSkImageForSurface(aMask, &lock); 1494 if (!maskImage) { 1495 gfxDebug() << "Failed get Skia mask image for MaskSurface"; 1496 return; 1497 } 1498 1499 SkMatrix maskOffset = SkMatrix::Translate( 1500 PointToSkPoint(aOffset + Point(aMask->GetRect().TopLeft()))); 1501 sk_sp<SkShader> maskShader = maskImage->makeShader( 1502 SkTileMode::kClamp, SkTileMode::kClamp, 1503 SkSamplingOptions(SkFilterMode::kLinear), maskOffset); 1504 if (!maskShader) { 1505 gfxDebug() << "Failed creating Skia clip shader for MaskSurface"; 1506 return; 1507 } 1508 1509 MarkChanged(); 1510 AutoPaintSetup paint(mCanvas, aOptions, aSource); 1511 1512 mCanvas->save(); 1513 mCanvas->clipShader(maskShader); 1514 1515 mCanvas->drawRect(RectToSkRect(Rect(aMask->GetRect()) + aOffset), 1516 paint.mPaint); 1517 1518 mCanvas->restore(); 1519 } 1520 1521 bool DrawTarget::Draw3DTransformedSurface(SourceSurface* aSurface, 1522 const Matrix4x4& aMatrix) { 1523 // Composite the 3D transform with the DT's transform. 1524 Matrix4x4 fullMat = aMatrix * Matrix4x4::From2D(mTransform); 1525 if (fullMat.IsSingular()) { 1526 return false; 1527 } 1528 // Transform the surface bounds and clip to this DT. 1529 IntRect xformBounds = RoundedOut(fullMat.TransformAndClipBounds( 1530 Rect(Point(0, 0), Size(aSurface->GetSize())), 1531 Rect(Point(0, 0), Size(GetSize())))); 1532 if (xformBounds.IsEmpty()) { 1533 return true; 1534 } 1535 // Offset the matrix by the transformed origin. 1536 fullMat.PostTranslate(-xformBounds.X(), -xformBounds.Y(), 0); 1537 1538 // Read in the source data. 1539 Maybe<MutexAutoLock> lock; 1540 sk_sp<SkImage> srcImage = GetSkImageForSurface(aSurface, &lock); 1541 if (!srcImage) { 1542 return true; 1543 } 1544 1545 // Set up an intermediate destination surface only the size of the transformed 1546 // bounds. Try to pass through the source's format unmodified in both the BGRA 1547 // and ARGB cases. 1548 RefPtr<DataSourceSurface> dstSurf = Factory::CreateDataSourceSurface( 1549 xformBounds.Size(), 1550 !srcImage->isOpaque() ? aSurface->GetFormat() 1551 : SurfaceFormat::A8R8G8B8_UINT32, 1552 true); 1553 if (!dstSurf) { 1554 return false; 1555 } 1556 1557 DataSourceSurface::ScopedMap map(dstSurf, DataSourceSurface::READ_WRITE); 1558 if (!map.IsMapped()) { 1559 return false; 1560 } 1561 std::unique_ptr<SkCanvas> dstCanvas(SkCanvas::MakeRasterDirect( 1562 SkImageInfo::Make(xformBounds.Width(), xformBounds.Height(), 1563 GfxFormatToSkiaColorType(dstSurf->GetFormat()), 1564 kPremul_SkAlphaType), 1565 map.GetData(), map.GetStride())); 1566 if (!dstCanvas) { 1567 return false; 1568 } 1569 1570 // Do the transform. 1571 SkPaint paint; 1572 paint.setAntiAlias(true); 1573 paint.setBlendMode(SkBlendMode::kSrc); 1574 1575 SkMatrix xform; 1576 GfxMatrixToSkiaMatrix(fullMat, xform); 1577 dstCanvas->setMatrix(xform); 1578 1579 dstCanvas->drawImage(srcImage, 0, 0, SkSamplingOptions(SkFilterMode::kLinear), 1580 &paint); 1581 1582 // Temporarily reset the DT's transform, since it has already been composed 1583 // above. 1584 Matrix origTransform = mTransform; 1585 SetTransform(Matrix()); 1586 1587 // Draw the transformed surface within the transformed bounds. 1588 DrawSurface(dstSurf, Rect(xformBounds), 1589 Rect(Point(0, 0), Size(xformBounds.Size()))); 1590 1591 SetTransform(origTransform); 1592 1593 return true; 1594 } 1595 1596 bool DrawTargetSkia::Draw3DTransformedSurface(SourceSurface* aSurface, 1597 const Matrix4x4& aMatrix) { 1598 if (aMatrix.IsSingular()) { 1599 return false; 1600 } 1601 1602 MarkChanged(); 1603 1604 Maybe<MutexAutoLock> lock; 1605 sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock); 1606 if (!image) { 1607 return true; 1608 } 1609 1610 mCanvas->save(); 1611 1612 SkPaint paint; 1613 paint.setAntiAlias(true); 1614 1615 SkMatrix xform; 1616 GfxMatrixToSkiaMatrix(aMatrix, xform); 1617 mCanvas->concat(xform); 1618 1619 mCanvas->drawImage(image, 0, 0, SkSamplingOptions(SkFilterMode::kLinear), 1620 &paint); 1621 1622 mCanvas->restore(); 1623 1624 return true; 1625 } 1626 1627 already_AddRefed<SourceSurface> DrawTargetSkia::CreateSourceSurfaceFromData( 1628 unsigned char* aData, const IntSize& aSize, int32_t aStride, 1629 SurfaceFormat aFormat) const { 1630 RefPtr<SourceSurfaceSkia> newSurf = new SourceSurfaceSkia(); 1631 1632 if (!newSurf->InitFromData(aData, aSize, aStride, aFormat)) { 1633 gfxDebug() << *this 1634 << ": Failure to create source surface from data. Size: " 1635 << aSize; 1636 return nullptr; 1637 } 1638 1639 return newSurf.forget(); 1640 } 1641 1642 already_AddRefed<DrawTarget> DrawTargetSkia::CreateSimilarDrawTarget( 1643 const IntSize& aSize, SurfaceFormat aFormat) const { 1644 RefPtr<DrawTargetSkia> target = new DrawTargetSkia(); 1645 #ifdef DEBUG 1646 if (!IsBackedByPixels(mCanvas)) { 1647 // If our canvas is backed by vector storage such as PDF then we want to 1648 // create a new DrawTarget with similar storage to avoid losing fidelity 1649 // (fidelity will be lost if the returned DT is Snapshot()'ed and drawn 1650 // back onto us since a raster will be drawn instead of vector commands). 1651 NS_WARNING("Not backed by pixels - we need to handle PDF backed SkCanvas"); 1652 } 1653 #endif 1654 1655 if (!target->Init(aSize, aFormat)) { 1656 return nullptr; 1657 } 1658 return target.forget(); 1659 } 1660 1661 bool DrawTargetSkia::CanCreateSimilarDrawTarget(const IntSize& aSize, 1662 SurfaceFormat aFormat) const { 1663 auto minmaxPair = std::minmax(aSize.width, aSize.height); 1664 return minmaxPair.first > 0 && 1665 size_t(minmaxPair.second) < GetMaxSurfaceSize(); 1666 } 1667 1668 RefPtr<DrawTarget> DrawTargetSkia::CreateClippedDrawTarget( 1669 const Rect& aBounds, SurfaceFormat aFormat) { 1670 SkIRect clipBounds; 1671 1672 RefPtr<DrawTarget> result; 1673 // Doing this save()/restore() dance is wasteful 1674 mCanvas->save(); 1675 if (!aBounds.IsEmpty()) { 1676 mCanvas->clipRect(RectToSkRect(aBounds), SkClipOp::kIntersect, true); 1677 } 1678 if (mCanvas->getDeviceClipBounds(&clipBounds)) { 1679 RefPtr<DrawTarget> dt = CreateSimilarDrawTarget( 1680 IntSize(clipBounds.width(), clipBounds.height()), aFormat); 1681 if (dt) { 1682 result = gfx::Factory::CreateOffsetDrawTarget( 1683 dt, IntPoint(clipBounds.x(), clipBounds.y())); 1684 if (result) { 1685 result->SetTransform(mTransform); 1686 } 1687 } 1688 } else { 1689 // Everything is clipped but we still want some kind of surface 1690 result = CreateSimilarDrawTarget(IntSize(1, 1), aFormat); 1691 } 1692 mCanvas->restore(); 1693 return result; 1694 } 1695 1696 already_AddRefed<SourceSurface> 1697 DrawTargetSkia::OptimizeSourceSurfaceForUnknownAlpha( 1698 SourceSurface* aSurface) const { 1699 if (aSurface->GetType() == SurfaceType::SKIA) { 1700 RefPtr<SourceSurface> surface(aSurface); 1701 return surface.forget(); 1702 } 1703 1704 if (RefPtr<DataSourceSurface> dataSurface = aSurface->GetDataSurface()) { 1705 DataSourceSurface::ScopedMap map(dataSurface, 1706 DataSourceSurface::READ_WRITE); 1707 if (map.IsMapped()) { 1708 // For plugins, GDI can sometimes just write 0 to the alpha channel 1709 // even for RGBX formats. In this case, we have to manually write 1710 // the alpha channel to make Skia happy with RGBX and in case GDI 1711 // writes some bad data. Luckily, this only happens on plugins. 1712 WriteRGBXFormat(map.GetData(), dataSurface->GetSize(), map.GetStride(), 1713 dataSurface->GetFormat()); 1714 return dataSurface.forget(); 1715 } 1716 } 1717 1718 return nullptr; 1719 } 1720 1721 already_AddRefed<SourceSurface> DrawTargetSkia::OptimizeSourceSurface( 1722 SourceSurface* aSurface) const { 1723 if (aSurface->GetType() == SurfaceType::SKIA) { 1724 RefPtr<SourceSurface> surface(aSurface); 1725 return surface.forget(); 1726 } 1727 1728 // If we're not using skia-gl then drawing doesn't require any 1729 // uploading, so any data surface is fine. Call GetDataSurface 1730 // to trigger any required readback so that it only happens 1731 // once. 1732 if (RefPtr<DataSourceSurface> dataSurface = aSurface->GetDataSurface()) { 1733 #ifdef DEBUG 1734 DataSourceSurface::ScopedMap map(dataSurface, DataSourceSurface::READ); 1735 if (map.IsMapped()) { 1736 MOZ_ASSERT(VerifyRGBXFormat(map.GetData(), dataSurface->GetSize(), 1737 map.GetStride(), dataSurface->GetFormat())); 1738 } 1739 #endif 1740 return dataSurface.forget(); 1741 } 1742 1743 return nullptr; 1744 } 1745 1746 already_AddRefed<SourceSurface> 1747 DrawTargetSkia::CreateSourceSurfaceFromNativeSurface( 1748 const NativeSurface& aSurface) const { 1749 return nullptr; 1750 } 1751 1752 void DrawTargetSkia::CopySurface(SourceSurface* aSurface, 1753 const IntRect& aSourceRect, 1754 const IntPoint& aDestination) { 1755 MarkChanged(); 1756 1757 Maybe<MutexAutoLock> lock; 1758 sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock); 1759 if (!image) { 1760 return; 1761 } 1762 1763 SkPixmap srcPixmap; 1764 if (!image->peekPixels(&srcPixmap)) { 1765 return; 1766 } 1767 1768 // Ensure the source rect intersects the surface bounds. 1769 IntRect offsetSrcRect = aSourceRect - aSurface->GetRect().TopLeft(); 1770 IntRect srcRect = 1771 offsetSrcRect.Intersect(SkIRectToIntRect(srcPixmap.bounds())); 1772 // Move the destination offset to match the altered source rect. 1773 IntPoint dstOffset = 1774 aDestination + (srcRect.TopLeft() - offsetSrcRect.TopLeft()); 1775 // Then ensure the dest rect intersect the canvas bounds. 1776 IntRect dstRect = IntRect(dstOffset, srcRect.Size()).Intersect(GetRect()); 1777 // Move the source rect to match the altered dest rect. 1778 srcRect += dstRect.TopLeft() - dstOffset; 1779 srcRect.SizeTo(dstRect.Size()); 1780 1781 if (!srcPixmap.extractSubset(&srcPixmap, IntRectToSkIRect(srcRect))) { 1782 return; 1783 } 1784 1785 mCanvas->writePixels(srcPixmap.info(), srcPixmap.addr(), srcPixmap.rowBytes(), 1786 dstRect.x, dstRect.y); 1787 } 1788 1789 static inline SkPixelGeometry GetSkPixelGeometry() { 1790 switch (Factory::GetSubpixelOrder()) { 1791 case SubpixelOrder::BGR: 1792 return kBGR_H_SkPixelGeometry; 1793 case SubpixelOrder::VBGR: 1794 return kBGR_V_SkPixelGeometry; 1795 case SubpixelOrder::VRGB: 1796 return kRGB_V_SkPixelGeometry; 1797 case SubpixelOrder::RGB: 1798 default: 1799 return kRGB_H_SkPixelGeometry; 1800 } 1801 } 1802 1803 template <typename T> 1804 [[nodiscard]] static already_AddRefed<T> AsRefPtr(sk_sp<T>&& aSkPtr) { 1805 return already_AddRefed<T>(aSkPtr.release()); 1806 } 1807 1808 bool DrawTargetSkia::Init(const IntSize& aSize, SurfaceFormat aFormat) { 1809 if (size_t(std::max(aSize.width, aSize.height)) > GetMaxSurfaceSize()) { 1810 return false; 1811 } 1812 1813 // we need to have surfaces that have a stride aligned to 4 for interop with 1814 // cairo 1815 SkImageInfo info = MakeSkiaImageInfo(aSize, aFormat); 1816 if (info.bytesPerPixel() != BytesPerPixel(aFormat)) { 1817 return false; 1818 } 1819 size_t stride = GetAlignedStride<4>(info.width(), info.bytesPerPixel()); 1820 if (!stride || stride < info.minRowBytes64()) { 1821 return false; 1822 } 1823 SkSurfaceProps props(0, GetSkPixelGeometry()); 1824 1825 if (aFormat == SurfaceFormat::A8) { 1826 // Skia does not fully allocate the last row according to stride. 1827 // Since some of our algorithms (i.e. blur) depend on this, we must allocate 1828 // the bitmap pixels manually. 1829 CheckedInt<size_t> size = stride; 1830 size *= info.height(); 1831 if (!size.isValid()) { 1832 return false; 1833 } 1834 void* buf = sk_malloc_flags(size.value(), SK_MALLOC_ZERO_INITIALIZE); 1835 if (!buf) { 1836 return false; 1837 } 1838 mSurface = AsRefPtr(SkSurfaces::WrapPixels( 1839 info, buf, stride, FreeAlphaPixels, nullptr, &props)); 1840 } else { 1841 mSurface = AsRefPtr(SkSurfaces::Raster(info, stride, &props)); 1842 } 1843 if (!mSurface) { 1844 return false; 1845 } 1846 1847 mSize = aSize; 1848 mFormat = aFormat; 1849 mCanvas = mSurface->getCanvas(); 1850 SetPermitSubpixelAA(IsOpaque(mFormat)); 1851 1852 if (info.isOpaque()) { 1853 mCanvas->clear(SK_ColorBLACK); 1854 } 1855 mIsClear = true; 1856 return true; 1857 } 1858 1859 bool DrawTargetSkia::Init(SkCanvas* aCanvas) { 1860 mCanvas = aCanvas; 1861 1862 SkImageInfo imageInfo = mCanvas->imageInfo(); 1863 1864 // If the canvas is backed by pixels we clear it to be on the safe side. If 1865 // it's not (for example, for PDF output) we don't. 1866 if (IsBackedByPixels(mCanvas)) { 1867 SkColor clearColor = 1868 imageInfo.isOpaque() ? SK_ColorBLACK : SK_ColorTRANSPARENT; 1869 mCanvas->clear(clearColor); 1870 mIsClear = true; 1871 } 1872 1873 SkISize size = mCanvas->getBaseLayerSize(); 1874 mSize.width = size.width(); 1875 mSize.height = size.height(); 1876 mFormat = 1877 SkiaColorTypeToGfxFormat(imageInfo.colorType(), imageInfo.alphaType()); 1878 SetPermitSubpixelAA(IsOpaque(mFormat)); 1879 return true; 1880 } 1881 1882 bool DrawTargetSkia::Init(unsigned char* aData, const IntSize& aSize, 1883 int32_t aStride, SurfaceFormat aFormat, 1884 bool aUninitialized, bool aIsClear) { 1885 MOZ_ASSERT((aFormat != SurfaceFormat::B8G8R8X8) || aUninitialized || 1886 VerifyRGBXFormat(aData, aSize, aStride, aFormat)); 1887 1888 SkImageInfo info = MakeSkiaImageInfo(aSize, aFormat); 1889 if (info.bytesPerPixel() != BytesPerPixel(aFormat) || aStride <= 0 || 1890 size_t(aStride) < info.minRowBytes64()) { 1891 return false; 1892 } 1893 1894 SkSurfaceProps props(0, GetSkPixelGeometry()); 1895 mSurface = AsRefPtr(SkSurfaces::WrapPixels(info, aData, aStride, &props)); 1896 if (!mSurface) { 1897 return false; 1898 } 1899 1900 mSize = aSize; 1901 mFormat = aFormat; 1902 mCanvas = mSurface->getCanvas(); 1903 SetPermitSubpixelAA(IsOpaque(mFormat)); 1904 mIsClear = aIsClear; 1905 return true; 1906 } 1907 1908 bool DrawTargetSkia::Init(RefPtr<DataSourceSurface>&& aSurface) { 1909 auto map = 1910 new DataSourceSurface::ScopedMap(aSurface, DataSourceSurface::READ_WRITE); 1911 if (!map->IsMapped()) { 1912 delete map; 1913 return false; 1914 } 1915 1916 SurfaceFormat format = aSurface->GetFormat(); 1917 IntSize size = aSurface->GetSize(); 1918 MOZ_ASSERT((format != SurfaceFormat::B8G8R8X8) || 1919 VerifyRGBXFormat(map->GetData(), size, map->GetStride(), format)); 1920 1921 SkImageInfo info = MakeSkiaImageInfo(size, format); 1922 if (info.bytesPerPixel() != BytesPerPixel(format) || 1923 size_t(map->GetStride()) < info.minRowBytes64()) { 1924 delete map; 1925 return false; 1926 } 1927 1928 SkSurfaceProps props(0, GetSkPixelGeometry()); 1929 mSurface = AsRefPtr(SkSurfaces::WrapPixels( 1930 MakeSkiaImageInfo(size, format), map->GetData(), map->GetStride(), 1931 DrawTargetSkia::ReleaseMappedSkSurface, map, &props)); 1932 if (!mSurface) { 1933 delete map; 1934 return false; 1935 } 1936 1937 // map is now owned by mSurface 1938 mBackingSurface = std::move(aSurface); 1939 mSize = size; 1940 mFormat = format; 1941 mCanvas = mSurface->getCanvas(); 1942 SetPermitSubpixelAA(IsOpaque(format)); 1943 return true; 1944 } 1945 1946 /* static */ void DrawTargetSkia::ReleaseMappedSkSurface(void* aPixels, 1947 void* aContext) { 1948 auto map = reinterpret_cast<DataSourceSurface::ScopedMap*>(aContext); 1949 delete map; 1950 } 1951 1952 void DrawTargetSkia::SetTransform(const Matrix& aTransform) { 1953 SkMatrix mat; 1954 GfxMatrixToSkiaMatrix(aTransform, mat); 1955 mCanvas->setMatrix(mat); 1956 mTransform = aTransform; 1957 } 1958 1959 void* DrawTargetSkia::GetNativeSurface(NativeSurfaceType aType) { 1960 return nullptr; 1961 } 1962 1963 already_AddRefed<PathBuilder> DrawTargetSkia::CreatePathBuilder( 1964 FillRule aFillRule) const { 1965 return PathBuilderSkia::Create(aFillRule); 1966 } 1967 1968 void DrawTargetSkia::ClearRect(const Rect& aRect) { 1969 if (mIsClear) { 1970 return; 1971 } 1972 1973 MarkChanged(); 1974 SkPaint paint; 1975 paint.setAntiAlias(true); 1976 paint.setColor((mFormat == SurfaceFormat::B8G8R8X8) ? SK_ColorBLACK 1977 : SK_ColorTRANSPARENT); 1978 paint.setBlendMode(SkBlendMode::kSrc); 1979 mCanvas->drawRect(RectToSkRect(aRect), paint); 1980 } 1981 1982 void DrawTargetSkia::PushClip(const Path* aPath) { 1983 if (aPath->GetBackendType() != BackendType::SKIA) { 1984 return; 1985 } 1986 1987 const PathSkia* skiaPath = static_cast<const PathSkia*>(aPath); 1988 mCanvas->save(); 1989 mCanvas->clipPath(skiaPath->GetPath(), SkClipOp::kIntersect, true); 1990 } 1991 1992 void DrawTargetSkia::PushDeviceSpaceClipRects(const IntRect* aRects, 1993 uint32_t aCount) { 1994 // Build a region by unioning all the rects together. 1995 SkRegion region; 1996 for (uint32_t i = 0; i < aCount; i++) { 1997 region.op(IntRectToSkIRect(aRects[i]), SkRegion::kUnion_Op); 1998 } 1999 2000 // Clip with the resulting region. clipRegion does not transform 2001 // this region by the current transform, unlike the other SkCanvas 2002 // clip methods, so it is just passed through in device-space. 2003 mCanvas->save(); 2004 mCanvas->clipRegion(region, SkClipOp::kIntersect); 2005 } 2006 2007 void DrawTargetSkia::PushClipRect(const Rect& aRect) { 2008 SkRect rect = RectToSkRect(aRect); 2009 2010 mCanvas->save(); 2011 mCanvas->clipRect(rect, SkClipOp::kIntersect, true); 2012 } 2013 2014 void DrawTargetSkia::PopClip() { 2015 mCanvas->restore(); 2016 SetTransform(GetTransform()); 2017 } 2018 2019 bool DrawTargetSkia::RemoveAllClips() { 2020 mCanvas->restoreToCount(1); 2021 SetTransform(GetTransform()); 2022 return true; 2023 } 2024 2025 // Get clip bounds in device space for the clipping region. By default, only 2026 // bounds for simple (empty or rect) regions are reported. If explicitly 2027 // allowed, the bounds will be reported for complex (all other) regions as well. 2028 Maybe<IntRect> DrawTargetSkia::GetDeviceClipRect(bool aAllowComplex) const { 2029 if (mCanvas->isClipEmpty()) { 2030 return Some(IntRect()); 2031 } 2032 if (aAllowComplex || mCanvas->isClipRect()) { 2033 SkIRect deviceBounds; 2034 if (mCanvas->getDeviceClipBounds(&deviceBounds)) { 2035 return Some(SkIRectToIntRect(deviceBounds)); 2036 } 2037 } 2038 return Nothing(); 2039 } 2040 2041 bool DrawTargetSkia::IsClipEmpty() const { return mCanvas->isClipEmpty(); } 2042 2043 void DrawTargetSkia::PushLayer(bool aOpaque, Float aOpacity, 2044 SourceSurface* aMask, 2045 const Matrix& aMaskTransform, 2046 const IntRect& aBounds, bool aCopyBackground) { 2047 PushLayerWithBlend(aOpaque, aOpacity, aMask, aMaskTransform, aBounds, 2048 aCopyBackground, CompositionOp::OP_OVER); 2049 } 2050 2051 void DrawTargetSkia::PushLayerWithBlend(bool aOpaque, Float aOpacity, 2052 SourceSurface* aMask, 2053 const Matrix& aMaskTransform, 2054 const IntRect& aBounds, 2055 bool aCopyBackground, 2056 CompositionOp aCompositionOp) { 2057 SkPaint paint; 2058 2059 paint.setAlpha(ColorFloatToByte(aOpacity)); 2060 paint.setBlendMode(GfxOpToSkiaOp(aCompositionOp)); 2061 2062 // aBounds is supplied in device space, but SaveLayerRec wants local space. 2063 SkRect bounds = SkRect::MakeEmpty(); 2064 if (!aBounds.IsEmpty()) { 2065 Matrix inverseTransform = mTransform; 2066 if (inverseTransform.Invert()) { 2067 bounds = RectToSkRect(inverseTransform.TransformBounds(Rect(aBounds))); 2068 } 2069 } 2070 2071 // We don't pass a lock object to GetSkImageForSurface here, to force a 2072 // copy of the data if this is a copy-on-write snapshot. If we instead held 2073 // the lock until the corresponding PopLayer, we'd risk deadlocking if someone 2074 // tried to touch the originating DrawTarget while the layer was pushed. 2075 sk_sp<SkImage> clipImage = GetSkImageForSurface(aMask, nullptr); 2076 bool usedMask = false; 2077 if (bool(clipImage)) { 2078 Rect maskBounds(aMask->GetRect()); 2079 sk_sp<SkShader> shader = clipImage->makeShader( 2080 SkTileMode::kClamp, SkTileMode::kClamp, 2081 SkSamplingOptions(SkFilterMode::kLinear), 2082 SkMatrix::Translate(PointToSkPoint(maskBounds.TopLeft()))); 2083 if (shader) { 2084 usedMask = true; 2085 mCanvas->save(); 2086 2087 auto oldMatrix = mCanvas->getLocalToDevice(); 2088 SkMatrix clipMatrix; 2089 GfxMatrixToSkiaMatrix(aMaskTransform, clipMatrix); 2090 mCanvas->concat(clipMatrix); 2091 2092 mCanvas->clipRect(RectToSkRect(maskBounds)); 2093 mCanvas->clipShader(shader); 2094 2095 mCanvas->setMatrix(oldMatrix); 2096 } else { 2097 gfxDebug() << "Failed to create Skia clip shader for PushLayerWithBlend"; 2098 } 2099 } 2100 2101 PushedLayer layer(GetPermitSubpixelAA(), usedMask ? aMask : nullptr); 2102 mPushedLayers.push_back(layer); 2103 2104 SkCanvas::SaveLayerRec saveRec( 2105 aBounds.IsEmpty() ? nullptr : &bounds, &paint, nullptr, 2106 SkCanvas::kPreserveLCDText_SaveLayerFlag | 2107 (aCopyBackground ? SkCanvas::kInitWithPrevious_SaveLayerFlag : 0)); 2108 2109 mCanvas->saveLayer(saveRec); 2110 2111 SetPermitSubpixelAA(aOpaque); 2112 2113 #ifdef MOZ_WIDGET_COCOA 2114 CGContextRelease(mCG); 2115 mCG = nullptr; 2116 #endif 2117 } 2118 2119 void DrawTargetSkia::PopLayer() { 2120 MOZ_RELEASE_ASSERT(!mPushedLayers.empty()); 2121 2122 MarkChanged(); 2123 2124 const PushedLayer& layer = mPushedLayers.back(); 2125 2126 mCanvas->restore(); 2127 2128 if (layer.mMask) { 2129 mCanvas->restore(); 2130 } 2131 2132 SetTransform(GetTransform()); 2133 SetPermitSubpixelAA(layer.mOldPermitSubpixelAA); 2134 2135 mPushedLayers.pop_back(); 2136 2137 #ifdef MOZ_WIDGET_COCOA 2138 CGContextRelease(mCG); 2139 mCG = nullptr; 2140 #endif 2141 } 2142 2143 already_AddRefed<GradientStops> DrawTargetSkia::CreateGradientStops( 2144 GradientStop* aStops, uint32_t aNumStops, ExtendMode aExtendMode) const { 2145 std::vector<GradientStop> stops; 2146 stops.resize(aNumStops); 2147 for (uint32_t i = 0; i < aNumStops; i++) { 2148 stops[i] = aStops[i]; 2149 } 2150 std::stable_sort(stops.begin(), stops.end()); 2151 2152 return MakeAndAddRef<GradientStopsSkia>(stops, aNumStops, aExtendMode); 2153 } 2154 2155 already_AddRefed<FilterNode> DrawTargetSkia::CreateFilter(FilterType aType) { 2156 return FilterNodeSoftware::Create(aType); 2157 } 2158 2159 void DrawTargetSkia::DetachAllSnapshots() { 2160 // I'm not entirely certain whether this lock is needed, as multiple threads 2161 // should never modify the DrawTarget at the same time anyway, but this seems 2162 // like the safest. 2163 MutexAutoLock lock(mSnapshotLock); 2164 if (mSnapshot) { 2165 if (mSnapshot->hasOneRef()) { 2166 // No owners outside of this DrawTarget's own reference. Just dump it. 2167 mSnapshot = nullptr; 2168 return; 2169 } 2170 2171 mSnapshot->DrawTargetWillChange(); 2172 mSnapshot = nullptr; 2173 2174 // Handle copying of any image snapshots bound to the surface. 2175 if (mSurface) { 2176 mSurface->notifyContentWillChange(SkSurface::kRetain_ContentChangeMode); 2177 } 2178 } 2179 } 2180 2181 void DrawTargetSkia::MarkChanged() { 2182 DetachAllSnapshots(); 2183 mIsClear = false; 2184 } 2185 2186 } // namespace mozilla::gfx