Bug 1758688 [wpt PR 33067] - [FedCM] Make revoke a non-static method, a=testonly
[gecko.git] / gfx / 2d / DrawTargetSkia.cpp
blobfb87d116a458108487292330d3b3656b5809b6a2
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/. */
7 #include "DrawTargetSkia.h"
8 #include "SourceSurfaceSkia.h"
9 #include "ScaledFontBase.h"
10 #include "FilterNodeSoftware.h"
11 #include "HelpersSkia.h"
13 #include "mozilla/ArrayUtils.h"
14 #include "mozilla/CheckedInt.h"
15 #include "mozilla/Vector.h"
17 #include "skia/include/core/SkCanvas.h"
18 #include "skia/include/core/SkFont.h"
19 #include "skia/include/core/SkSurface.h"
20 #include "skia/include/core/SkTextBlob.h"
21 #include "skia/include/core/SkTypeface.h"
22 #include "skia/include/effects/SkGradientShader.h"
23 #include "skia/include/core/SkColorFilter.h"
24 #include "skia/include/core/SkRegion.h"
25 #include "skia/include/effects/SkBlurImageFilter.h"
26 #include "Blur.h"
27 #include "Logging.h"
28 #include "Tools.h"
29 #include "DataSurfaceHelpers.h"
30 #include "PathHelpers.h"
31 #include "PathSkia.h"
32 #include "Swizzle.h"
33 #include <algorithm>
35 #ifdef MOZ_WIDGET_COCOA
36 # include "BorrowedContext.h"
37 # include <ApplicationServices/ApplicationServices.h>
38 #endif
40 #ifdef XP_WIN
41 # include "ScaledFontDWrite.h"
42 #endif
44 namespace mozilla {
46 void RefPtrTraits<SkSurface>::Release(SkSurface* aSurface) {
47 SkSafeUnref(aSurface);
50 void RefPtrTraits<SkSurface>::AddRef(SkSurface* aSurface) {
51 SkSafeRef(aSurface);
54 } // namespace mozilla
56 namespace mozilla::gfx {
58 class GradientStopsSkia : public GradientStops {
59 public:
60 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsSkia, override)
62 GradientStopsSkia(const std::vector<GradientStop>& aStops, uint32_t aNumStops,
63 ExtendMode aExtendMode)
64 : mCount(aNumStops), mExtendMode(aExtendMode) {
65 if (mCount == 0) {
66 return;
69 // Skia gradients always require a stop at 0.0 and 1.0, insert these if
70 // we don't have them.
71 uint32_t shift = 0;
72 if (aStops[0].offset != 0) {
73 mCount++;
74 shift = 1;
76 if (aStops[aNumStops - 1].offset != 1) {
77 mCount++;
79 mColors.resize(mCount);
80 mPositions.resize(mCount);
81 if (aStops[0].offset != 0) {
82 mColors[0] = ColorToSkColor(aStops[0].color, 1.0);
83 mPositions[0] = 0;
85 for (uint32_t i = 0; i < aNumStops; i++) {
86 mColors[i + shift] = ColorToSkColor(aStops[i].color, 1.0);
87 mPositions[i + shift] = SkFloatToScalar(aStops[i].offset);
89 if (aStops[aNumStops - 1].offset != 1) {
90 mColors[mCount - 1] = ColorToSkColor(aStops[aNumStops - 1].color, 1.0);
91 mPositions[mCount - 1] = SK_Scalar1;
95 BackendType GetBackendType() const override { return BackendType::SKIA; }
97 std::vector<SkColor> mColors;
98 std::vector<SkScalar> mPositions;
99 int mCount;
100 ExtendMode mExtendMode;
104 * When constructing a temporary SkImage via GetSkImageForSurface, we may also
105 * have to construct a temporary DataSourceSurface, which must live as long as
106 * the SkImage. We attach this temporary surface to the image's pixelref, so
107 * that it can be released once the pixelref is freed.
109 static void ReleaseTemporarySurface(const void* aPixels, void* aContext) {
110 DataSourceSurface* surf = static_cast<DataSourceSurface*>(aContext);
111 if (surf) {
112 surf->Release();
116 static void ReleaseTemporaryMappedSurface(const void* aPixels, void* aContext) {
117 DataSourceSurface* surf = static_cast<DataSourceSurface*>(aContext);
118 if (surf) {
119 surf->Unmap();
120 surf->Release();
124 static void WriteRGBXFormat(uint8_t* aData, const IntSize& aSize,
125 const int32_t aStride, SurfaceFormat aFormat) {
126 if (aFormat != SurfaceFormat::B8G8R8X8 || aSize.IsEmpty()) {
127 return;
130 SwizzleData(aData, aStride, SurfaceFormat::X8R8G8B8_UINT32, aData, aStride,
131 SurfaceFormat::A8R8G8B8_UINT32, aSize);
134 #ifdef DEBUG
135 static IntRect CalculateSurfaceBounds(const IntSize& aSize, const Rect* aBounds,
136 const Matrix* aMatrix) {
137 IntRect surfaceBounds(IntPoint(0, 0), aSize);
138 if (!aBounds) {
139 return surfaceBounds;
142 MOZ_ASSERT(aMatrix);
143 Matrix inverse(*aMatrix);
144 if (!inverse.Invert()) {
145 return surfaceBounds;
148 IntRect bounds;
149 Rect sampledBounds = inverse.TransformBounds(*aBounds);
150 if (!sampledBounds.ToIntRect(&bounds)) {
151 return surfaceBounds;
154 return surfaceBounds.Intersect(bounds);
157 static const int kARGBAlphaOffset =
158 SurfaceFormat::A8R8G8B8_UINT32 == SurfaceFormat::B8G8R8A8 ? 3 : 0;
160 static bool VerifyRGBXFormat(uint8_t* aData, const IntSize& aSize,
161 const int32_t aStride, SurfaceFormat aFormat) {
162 if (aFormat != SurfaceFormat::B8G8R8X8 || aSize.IsEmpty()) {
163 return true;
165 // We should've initialized the data to be opaque already
166 // On debug builds, verify that this is actually true.
167 int height = aSize.height;
168 int width = aSize.width * 4;
170 for (int row = 0; row < height; ++row) {
171 for (int column = 0; column < width; column += 4) {
172 if (aData[column + kARGBAlphaOffset] != 0xFF) {
173 gfxCriticalError() << "RGBX pixel at (" << column << "," << row
174 << ") in " << width << "x" << height
175 << " surface is not opaque: " << int(aData[column])
176 << "," << int(aData[column + 1]) << ","
177 << int(aData[column + 2]) << ","
178 << int(aData[column + 3]);
181 aData += aStride;
184 return true;
187 // Since checking every pixel is expensive, this only checks the four corners
188 // and center of a surface that their alpha value is 0xFF.
189 static bool VerifyRGBXCorners(uint8_t* aData, const IntSize& aSize,
190 const int32_t aStride, SurfaceFormat aFormat,
191 const Rect* aBounds = nullptr,
192 const Matrix* aMatrix = nullptr) {
193 if (aFormat != SurfaceFormat::B8G8R8X8 || aSize.IsEmpty()) {
194 return true;
197 IntRect bounds = CalculateSurfaceBounds(aSize, aBounds, aMatrix);
198 if (bounds.IsEmpty()) {
199 return true;
202 const int height = bounds.Height();
203 const int width = bounds.Width();
204 const int pixelSize = 4;
205 MOZ_ASSERT(aSize.width * pixelSize <= aStride);
207 const int translation = bounds.Y() * aStride + bounds.X() * pixelSize;
208 const int topLeft = translation;
209 const int topRight = topLeft + (width - 1) * pixelSize;
210 const int bottomLeft = translation + (height - 1) * aStride;
211 const int bottomRight = bottomLeft + (width - 1) * pixelSize;
213 // Lastly the center pixel
214 const int middleRowHeight = height / 2;
215 const int middleRowWidth = (width / 2) * pixelSize;
216 const int middle = translation + aStride * middleRowHeight + middleRowWidth;
218 const int offsets[] = {topLeft, topRight, bottomRight, bottomLeft, middle};
219 for (int offset : offsets) {
220 if (aData[offset + kARGBAlphaOffset] != 0xFF) {
221 int row = offset / aStride;
222 int column = (offset % aStride) / pixelSize;
223 gfxCriticalError() << "RGBX corner pixel at (" << column << "," << row
224 << ") in " << aSize.width << "x" << aSize.height
225 << " surface, bounded by "
226 << "(" << bounds.X() << "," << bounds.Y() << ","
227 << width << "," << height
228 << ") is not opaque: " << int(aData[offset]) << ","
229 << int(aData[offset + 1]) << ","
230 << int(aData[offset + 2]) << ","
231 << int(aData[offset + 3]);
235 return true;
237 #endif
239 static sk_sp<SkImage> GetSkImageForSurface(SourceSurface* aSurface,
240 Maybe<MutexAutoLock>* aLock,
241 const Rect* aBounds = nullptr,
242 const Matrix* aMatrix = nullptr) {
243 if (!aSurface) {
244 gfxDebug() << "Creating null Skia image from null SourceSurface";
245 return nullptr;
248 if (aSurface->GetType() == SurfaceType::SKIA) {
249 return static_cast<SourceSurfaceSkia*>(aSurface)->GetImage(aLock);
252 RefPtr<DataSourceSurface> dataSurface = aSurface->GetDataSurface();
253 if (!dataSurface) {
254 gfxWarning() << "Failed getting DataSourceSurface for Skia image";
255 return nullptr;
258 DataSourceSurface::MappedSurface map;
259 SkImage::RasterReleaseProc releaseProc;
260 if (dataSurface->GetType() == SurfaceType::DATA_SHARED_WRAPPER) {
261 // Technically all surfaces should be mapped and unmapped explicitly but it
262 // appears SourceSurfaceSkia and DataSourceSurfaceWrapper have issues with
263 // this. For now, we just map SourceSurfaceSharedDataWrapper to ensure we
264 // don't unmap the data during the transaction (for blob images).
265 if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
266 gfxWarning() << "Failed mapping DataSourceSurface for Skia image";
267 return nullptr;
269 releaseProc = ReleaseTemporaryMappedSurface;
270 } else {
271 map.mData = dataSurface->GetData();
272 map.mStride = dataSurface->Stride();
273 releaseProc = ReleaseTemporarySurface;
276 DataSourceSurface* surf = dataSurface.forget().take();
278 // Skia doesn't support RGBX surfaces so ensure that the alpha value is opaque
279 // white.
280 MOZ_ASSERT(VerifyRGBXCorners(map.mData, surf->GetSize(), map.mStride,
281 surf->GetFormat(), aBounds, aMatrix));
283 SkPixmap pixmap(MakeSkiaImageInfo(surf->GetSize(), surf->GetFormat()),
284 map.mData, map.mStride);
285 sk_sp<SkImage> image = SkImage::MakeFromRaster(pixmap, releaseProc, surf);
286 if (!image) {
287 releaseProc(map.mData, surf);
288 gfxDebug() << "Failed making Skia raster image for temporary surface";
291 return image;
294 DrawTargetSkia::DrawTargetSkia()
295 : mCanvas(nullptr), mSnapshot(nullptr), mSnapshotLock {
296 "DrawTargetSkia::mSnapshotLock"
298 #ifdef MOZ_WIDGET_COCOA
299 , mCG(nullptr), mColorSpace(nullptr), mCanvasData(nullptr), mCGSize(0, 0),
300 mNeedLayer(false)
301 #endif
305 DrawTargetSkia::~DrawTargetSkia() {
306 if (mSnapshot) {
307 MutexAutoLock lock(mSnapshotLock);
308 // We're going to go away, hand our SkSurface to the SourceSurface.
309 mSnapshot->GiveSurface(mSurface.forget().take());
312 #ifdef MOZ_WIDGET_COCOA
313 if (mCG) {
314 CGContextRelease(mCG);
315 mCG = nullptr;
318 if (mColorSpace) {
319 CGColorSpaceRelease(mColorSpace);
320 mColorSpace = nullptr;
322 #endif
325 already_AddRefed<SourceSurface> DrawTargetSkia::Snapshot(
326 SurfaceFormat aFormat) {
327 // Without this lock, this could cause us to get out a snapshot and race with
328 // Snapshot::~Snapshot() actually destroying itself.
329 MutexAutoLock lock(mSnapshotLock);
330 if (mSnapshot && aFormat != mSnapshot->GetFormat()) {
331 if (!mSnapshot->hasOneRef()) {
332 mSnapshot->DrawTargetWillChange();
334 mSnapshot = nullptr;
336 RefPtr<SourceSurfaceSkia> snapshot = mSnapshot;
337 if (mSurface && !snapshot) {
338 snapshot = new SourceSurfaceSkia();
339 sk_sp<SkImage> image;
340 // If the surface is raster, making a snapshot may trigger a pixel copy.
341 // Instead, try to directly make a raster image referencing the surface
342 // pixels.
343 SkPixmap pixmap;
344 if (mSurface->peekPixels(&pixmap)) {
345 image = SkImage::MakeFromRaster(pixmap, nullptr, nullptr);
346 } else {
347 image = mSurface->makeImageSnapshot();
349 if (!snapshot->InitFromImage(image, aFormat, this)) {
350 return nullptr;
352 mSnapshot = snapshot;
355 return snapshot.forget();
358 already_AddRefed<SourceSurface> DrawTargetSkia::GetBackingSurface() {
359 if (mBackingSurface) {
360 RefPtr<SourceSurface> snapshot = mBackingSurface;
361 return snapshot.forget();
363 return Snapshot();
366 bool DrawTargetSkia::LockBits(uint8_t** aData, IntSize* aSize, int32_t* aStride,
367 SurfaceFormat* aFormat, IntPoint* aOrigin) {
368 SkImageInfo info;
369 size_t rowBytes;
370 SkIPoint origin;
371 void* pixels = mCanvas->accessTopLayerPixels(&info, &rowBytes, &origin);
372 if (!pixels ||
373 // Ensure the layer is at the origin if required.
374 (!aOrigin && !origin.isZero())) {
375 return false;
378 MarkChanged();
380 *aData = reinterpret_cast<uint8_t*>(pixels);
381 *aSize = IntSize(info.width(), info.height());
382 *aStride = int32_t(rowBytes);
383 *aFormat = SkiaColorTypeToGfxFormat(info.colorType(), info.alphaType());
384 if (aOrigin) {
385 *aOrigin = IntPoint(origin.x(), origin.y());
387 return true;
390 void DrawTargetSkia::ReleaseBits(uint8_t* aData) {}
392 static void ReleaseImage(const void* aPixels, void* aContext) {
393 SkImage* image = static_cast<SkImage*>(aContext);
394 SkSafeUnref(image);
397 static sk_sp<SkImage> ExtractSubset(sk_sp<SkImage> aImage,
398 const IntRect& aRect) {
399 SkIRect subsetRect = IntRectToSkIRect(aRect);
400 if (aImage->bounds() == subsetRect) {
401 return aImage;
403 // makeSubset is slow, so prefer to use SkPixmap::extractSubset where
404 // possible.
405 SkPixmap pixmap, subsetPixmap;
406 if (aImage->peekPixels(&pixmap) &&
407 pixmap.extractSubset(&subsetPixmap, subsetRect)) {
408 // Release the original image reference so only the subset image keeps it
409 // alive.
410 return SkImage::MakeFromRaster(subsetPixmap, ReleaseImage,
411 aImage.release());
413 return aImage->makeSubset(subsetRect);
416 static void FreeBitmapPixels(void* aBuf, void*) { sk_free(aBuf); }
418 static bool ExtractAlphaBitmap(const sk_sp<SkImage>& aImage,
419 SkBitmap* aResultBitmap) {
420 SkImageInfo info = SkImageInfo::MakeA8(aImage->width(), aImage->height());
421 // Skia does not fully allocate the last row according to stride.
422 // Since some of our algorithms (i.e. blur) depend on this, we must allocate
423 // the bitmap pixels manually.
424 size_t stride = SkAlign4(info.minRowBytes());
425 CheckedInt<size_t> size = stride;
426 size *= info.height();
427 if (size.isValid()) {
428 void* buf = sk_malloc_flags(size.value(), 0);
429 if (buf) {
430 SkBitmap bitmap;
431 if (bitmap.installPixels(info, buf, stride, FreeBitmapPixels, nullptr) &&
432 aImage->readPixels(bitmap.info(), bitmap.getPixels(),
433 bitmap.rowBytes(), 0, 0)) {
434 *aResultBitmap = bitmap;
435 return true;
440 gfxWarning() << "Failed reading alpha pixels for Skia bitmap";
441 return false;
444 static sk_sp<SkImage> ExtractAlphaForSurface(SourceSurface* aSurface,
445 Maybe<MutexAutoLock>& aLock) {
446 sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &aLock);
447 if (!image) {
448 return nullptr;
450 if (image->isAlphaOnly()) {
451 return image;
454 SkBitmap bitmap;
455 if (!ExtractAlphaBitmap(image, &bitmap)) {
456 return nullptr;
459 // Mark the bitmap immutable so that it will be shared rather than copied.
460 bitmap.setImmutable();
461 return SkImage::MakeFromBitmap(bitmap);
464 static void SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern,
465 Maybe<MutexAutoLock>& aLock, Float aAlpha = 1.0,
466 const SkMatrix* aMatrix = nullptr,
467 const Rect* aBounds = nullptr) {
468 switch (aPattern.GetType()) {
469 case PatternType::COLOR: {
470 DeviceColor color = static_cast<const ColorPattern&>(aPattern).mColor;
471 aPaint.setColor(ColorToSkColor(color, aAlpha));
472 break;
474 case PatternType::LINEAR_GRADIENT: {
475 const LinearGradientPattern& pat =
476 static_cast<const LinearGradientPattern&>(aPattern);
477 GradientStopsSkia* stops =
478 static_cast<GradientStopsSkia*>(pat.mStops.get());
479 if (!stops || stops->mCount < 2 || !pat.mBegin.IsFinite() ||
480 !pat.mEnd.IsFinite() || pat.mBegin == pat.mEnd) {
481 aPaint.setColor(SK_ColorTRANSPARENT);
482 } else {
483 SkTileMode mode = ExtendModeToTileMode(stops->mExtendMode, Axis::BOTH);
484 SkPoint points[2];
485 points[0] = SkPoint::Make(SkFloatToScalar(pat.mBegin.x),
486 SkFloatToScalar(pat.mBegin.y));
487 points[1] = SkPoint::Make(SkFloatToScalar(pat.mEnd.x),
488 SkFloatToScalar(pat.mEnd.y));
490 SkMatrix mat;
491 GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
492 if (aMatrix) {
493 mat.postConcat(*aMatrix);
495 sk_sp<SkShader> shader = SkGradientShader::MakeLinear(
496 points, &stops->mColors.front(), &stops->mPositions.front(),
497 stops->mCount, mode, 0, &mat);
498 if (shader) {
499 aPaint.setShader(shader);
500 } else {
501 aPaint.setColor(SK_ColorTRANSPARENT);
504 break;
506 case PatternType::RADIAL_GRADIENT: {
507 const RadialGradientPattern& pat =
508 static_cast<const RadialGradientPattern&>(aPattern);
509 GradientStopsSkia* stops =
510 static_cast<GradientStopsSkia*>(pat.mStops.get());
511 if (!stops || stops->mCount < 2 || !pat.mCenter1.IsFinite() ||
512 !IsFinite(pat.mRadius1) || !pat.mCenter2.IsFinite() ||
513 !IsFinite(pat.mRadius2) ||
514 (pat.mCenter1 == pat.mCenter2 && pat.mRadius1 == pat.mRadius2)) {
515 aPaint.setColor(SK_ColorTRANSPARENT);
516 } else {
517 SkTileMode mode = ExtendModeToTileMode(stops->mExtendMode, Axis::BOTH);
518 SkPoint points[2];
519 points[0] = SkPoint::Make(SkFloatToScalar(pat.mCenter1.x),
520 SkFloatToScalar(pat.mCenter1.y));
521 points[1] = SkPoint::Make(SkFloatToScalar(pat.mCenter2.x),
522 SkFloatToScalar(pat.mCenter2.y));
524 SkMatrix mat;
525 GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
526 if (aMatrix) {
527 mat.postConcat(*aMatrix);
529 sk_sp<SkShader> shader = SkGradientShader::MakeTwoPointConical(
530 points[0], SkFloatToScalar(pat.mRadius1), points[1],
531 SkFloatToScalar(pat.mRadius2), &stops->mColors.front(),
532 &stops->mPositions.front(), stops->mCount, mode, 0, &mat);
533 if (shader) {
534 aPaint.setShader(shader);
535 } else {
536 aPaint.setColor(SK_ColorTRANSPARENT);
539 break;
541 case PatternType::CONIC_GRADIENT: {
542 const ConicGradientPattern& pat =
543 static_cast<const ConicGradientPattern&>(aPattern);
544 GradientStopsSkia* stops =
545 static_cast<GradientStopsSkia*>(pat.mStops.get());
546 if (!stops || stops->mCount < 2 || !pat.mCenter.IsFinite() ||
547 !IsFinite(pat.mAngle)) {
548 aPaint.setColor(SK_ColorTRANSPARENT);
549 } else {
550 SkMatrix mat;
551 GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
552 if (aMatrix) {
553 mat.postConcat(*aMatrix);
556 SkScalar cx = SkFloatToScalar(pat.mCenter.x);
557 SkScalar cy = SkFloatToScalar(pat.mCenter.y);
559 // Skia's sweep gradient angles are relative to the x-axis, not the
560 // y-axis.
561 Float angle = (pat.mAngle * 180.0 / M_PI) - 90.0;
562 if (angle != 0.0) {
563 mat.preRotate(angle, cx, cy);
566 SkTileMode mode = ExtendModeToTileMode(stops->mExtendMode, Axis::BOTH);
567 sk_sp<SkShader> shader = SkGradientShader::MakeSweep(
568 cx, cy, &stops->mColors.front(), &stops->mPositions.front(),
569 stops->mCount, mode, 360 * pat.mStartOffset, 360 * pat.mEndOffset,
570 0, &mat);
572 if (shader) {
573 aPaint.setShader(shader);
574 } else {
575 aPaint.setColor(SK_ColorTRANSPARENT);
578 break;
580 case PatternType::SURFACE: {
581 const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern);
582 sk_sp<SkImage> image =
583 GetSkImageForSurface(pat.mSurface, &aLock, aBounds, &pat.mMatrix);
584 if (!image) {
585 aPaint.setColor(SK_ColorTRANSPARENT);
586 break;
589 SkMatrix mat;
590 GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
591 if (aMatrix) {
592 mat.postConcat(*aMatrix);
595 if (!pat.mSamplingRect.IsEmpty()) {
596 image = ExtractSubset(image, pat.mSamplingRect);
597 if (!image) {
598 aPaint.setColor(SK_ColorTRANSPARENT);
599 break;
601 mat.preTranslate(pat.mSamplingRect.X(), pat.mSamplingRect.Y());
604 SkTileMode xTile = ExtendModeToTileMode(pat.mExtendMode, Axis::X_AXIS);
605 SkTileMode yTile = ExtendModeToTileMode(pat.mExtendMode, Axis::Y_AXIS);
607 sk_sp<SkShader> shader = image->makeShader(xTile, yTile, &mat);
608 if (shader) {
609 aPaint.setShader(shader);
610 } else {
611 gfxDebug() << "Failed creating Skia surface shader: x-tile="
612 << (int)xTile << " y-tile=" << (int)yTile
613 << " matrix=" << (mat.isFinite() ? "finite" : "non-finite");
614 aPaint.setColor(SK_ColorTRANSPARENT);
617 if (pat.mSamplingFilter == SamplingFilter::POINT) {
618 aPaint.setFilterQuality(kNone_SkFilterQuality);
620 break;
625 static inline Rect GetClipBounds(SkCanvas* aCanvas) {
626 // Use a manually transformed getClipDeviceBounds instead of
627 // getClipBounds because getClipBounds inflates the the bounds
628 // by a pixel in each direction to compensate for antialiasing.
629 SkIRect deviceBounds;
630 if (!aCanvas->getDeviceClipBounds(&deviceBounds)) {
631 return Rect();
633 SkMatrix inverseCTM;
634 if (!aCanvas->getTotalMatrix().invert(&inverseCTM)) {
635 return Rect();
637 SkRect localBounds;
638 inverseCTM.mapRect(&localBounds, SkRect::Make(deviceBounds));
639 return SkRectToRect(localBounds);
642 struct AutoPaintSetup {
643 AutoPaintSetup(SkCanvas* aCanvas, const DrawOptions& aOptions,
644 const Pattern& aPattern, const Rect* aMaskBounds = nullptr,
645 const SkMatrix* aMatrix = nullptr,
646 const Rect* aSourceBounds = nullptr)
647 : mNeedsRestore(false), mAlpha(1.0) {
648 Init(aCanvas, aOptions, aMaskBounds, false);
649 SetPaintPattern(mPaint, aPattern, mLock, mAlpha, aMatrix, aSourceBounds);
652 AutoPaintSetup(SkCanvas* aCanvas, const DrawOptions& aOptions,
653 const Rect* aMaskBounds = nullptr, bool aForceGroup = false)
654 : mNeedsRestore(false), mAlpha(1.0) {
655 Init(aCanvas, aOptions, aMaskBounds, aForceGroup);
658 ~AutoPaintSetup() {
659 if (mNeedsRestore) {
660 mCanvas->restore();
664 void Init(SkCanvas* aCanvas, const DrawOptions& aOptions,
665 const Rect* aMaskBounds, bool aForceGroup) {
666 mPaint.setBlendMode(GfxOpToSkiaOp(aOptions.mCompositionOp));
667 mCanvas = aCanvas;
669 // TODO: Can we set greyscale somehow?
670 if (aOptions.mAntialiasMode != AntialiasMode::NONE) {
671 mPaint.setAntiAlias(true);
672 } else {
673 mPaint.setAntiAlias(false);
676 bool needsGroup =
677 aForceGroup ||
678 (!IsOperatorBoundByMask(aOptions.mCompositionOp) &&
679 (!aMaskBounds || !aMaskBounds->Contains(GetClipBounds(aCanvas))));
681 // TODO: We could skip the temporary for operator_source and just
682 // clear the clip rect. The other operators would be harder
683 // but could be worth it to skip pushing a group.
684 if (needsGroup) {
685 mPaint.setBlendMode(SkBlendMode::kSrcOver);
686 SkPaint temp;
687 temp.setBlendMode(GfxOpToSkiaOp(aOptions.mCompositionOp));
688 temp.setAlpha(ColorFloatToByte(aOptions.mAlpha));
689 // TODO: Get a rect here
690 SkCanvas::SaveLayerRec rec(nullptr, &temp,
691 SkCanvas::kPreserveLCDText_SaveLayerFlag);
692 mCanvas->saveLayer(rec);
693 mNeedsRestore = true;
694 } else {
695 mPaint.setAlpha(ColorFloatToByte(aOptions.mAlpha));
696 mAlpha = aOptions.mAlpha;
698 mPaint.setFilterQuality(kLow_SkFilterQuality);
701 // TODO: Maybe add an operator overload to access this easier?
702 SkPaint mPaint;
703 bool mNeedsRestore;
704 SkCanvas* mCanvas;
705 Maybe<MutexAutoLock> mLock;
706 Float mAlpha;
709 void DrawTargetSkia::Flush() { mCanvas->flush(); }
711 void DrawTargetSkia::DrawSurface(SourceSurface* aSurface, const Rect& aDest,
712 const Rect& aSource,
713 const DrawSurfaceOptions& aSurfOptions,
714 const DrawOptions& aOptions) {
715 if (aSource.IsEmpty()) {
716 return;
719 MarkChanged();
721 Maybe<MutexAutoLock> lock;
722 sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
723 if (!image) {
724 return;
727 SkRect destRect = RectToSkRect(aDest);
728 SkRect sourceRect = RectToSkRect(aSource - aSurface->GetRect().TopLeft());
729 bool forceGroup =
730 image->isAlphaOnly() && aOptions.mCompositionOp != CompositionOp::OP_OVER;
732 AutoPaintSetup paint(mCanvas, aOptions, &aDest, forceGroup);
733 if (aSurfOptions.mSamplingFilter == SamplingFilter::POINT) {
734 paint.mPaint.setFilterQuality(kNone_SkFilterQuality);
737 mCanvas->drawImageRect(image, sourceRect, destRect, &paint.mPaint);
740 DrawTargetType DrawTargetSkia::GetType() const {
741 return DrawTargetType::SOFTWARE_RASTER;
744 void DrawTargetSkia::DrawFilter(FilterNode* aNode, const Rect& aSourceRect,
745 const Point& aDestPoint,
746 const DrawOptions& aOptions) {
747 FilterNodeSoftware* filter = static_cast<FilterNodeSoftware*>(aNode);
748 filter->Draw(this, aSourceRect, aDestPoint, aOptions);
751 void DrawTargetSkia::DrawSurfaceWithShadow(SourceSurface* aSurface,
752 const Point& aDest,
753 const DeviceColor& aColor,
754 const Point& aOffset, Float aSigma,
755 CompositionOp aOperator) {
756 if (aSurface->GetSize().IsEmpty()) {
757 return;
760 MarkChanged();
762 Maybe<MutexAutoLock> lock;
763 sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
764 if (!image) {
765 return;
768 mCanvas->save();
769 mCanvas->resetMatrix();
771 SkPaint paint;
772 paint.setBlendMode(GfxOpToSkiaOp(aOperator));
774 // bug 1201272
775 // We can't use the SkDropShadowImageFilter here because it applies the xfer
776 // mode first to render the bitmap to a temporary layer, and then implicitly
777 // uses src-over to composite the resulting shadow.
778 // The canvas spec, however, states that the composite op must be used to
779 // composite the resulting shadow, so we must instead use a SkBlurImageFilter
780 // to blur the image ourselves.
782 SkPaint shadowPaint;
783 shadowPaint.setBlendMode(GfxOpToSkiaOp(aOperator));
785 auto shadowDest = IntPoint::Round(aDest + aOffset);
787 SkBitmap blurMask;
788 if (ExtractAlphaBitmap(image, &blurMask)) {
789 // Prefer using our own box blur instead of Skia's. It currently performs
790 // much better than SkBlurImageFilter or SkBlurMaskFilter on the CPU.
791 AlphaBoxBlur blur(Rect(0, 0, blurMask.width(), blurMask.height()),
792 int32_t(blurMask.rowBytes()), aSigma, aSigma);
793 blur.Blur(reinterpret_cast<uint8_t*>(blurMask.getPixels()));
794 blurMask.notifyPixelsChanged();
796 shadowPaint.setColor(ColorToSkColor(aColor, 1.0f));
798 mCanvas->drawBitmap(blurMask, shadowDest.x, shadowDest.y, &shadowPaint);
799 } else {
800 sk_sp<SkImageFilter> blurFilter(
801 SkBlurImageFilter::Make(aSigma, aSigma, nullptr));
802 sk_sp<SkColorFilter> colorFilter(SkColorFilters::Blend(
803 ColorToSkColor(aColor, 1.0f), SkBlendMode::kSrcIn));
805 shadowPaint.setImageFilter(blurFilter);
806 shadowPaint.setColorFilter(colorFilter);
808 mCanvas->drawImage(image, shadowDest.x, shadowDest.y, &shadowPaint);
811 if (aSurface->GetFormat() != SurfaceFormat::A8) {
812 // Composite the original image after the shadow
813 auto dest = IntPoint::Round(aDest);
814 mCanvas->drawImage(image, dest.x, dest.y, &paint);
817 mCanvas->restore();
820 void DrawTargetSkia::FillRect(const Rect& aRect, const Pattern& aPattern,
821 const DrawOptions& aOptions) {
822 // The sprite blitting path in Skia can be faster than the shader blitter for
823 // operators other than source (or source-over with opaque surface). So, when
824 // possible/beneficial, route to DrawSurface which will use the sprite
825 // blitter.
826 if (aPattern.GetType() == PatternType::SURFACE &&
827 aOptions.mCompositionOp != CompositionOp::OP_SOURCE) {
828 const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern);
829 // Verify there is a valid surface and a pattern matrix without skew.
830 if (pat.mSurface &&
831 (aOptions.mCompositionOp != CompositionOp::OP_OVER ||
832 GfxFormatToSkiaAlphaType(pat.mSurface->GetFormat()) !=
833 kOpaque_SkAlphaType) &&
834 !pat.mMatrix.HasNonAxisAlignedTransform()) {
835 // Bound the sampling to smaller of the bounds or the sampling rect.
836 IntRect srcRect(IntPoint(0, 0), pat.mSurface->GetSize());
837 if (!pat.mSamplingRect.IsEmpty()) {
838 srcRect = srcRect.Intersect(pat.mSamplingRect);
840 // Transform the destination rectangle by the inverse of the pattern
841 // matrix so that it is in pattern space like the source rectangle.
842 Rect patRect = aRect - pat.mMatrix.GetTranslation();
843 patRect.Scale(1.0f / pat.mMatrix._11, 1.0f / pat.mMatrix._22);
844 // Verify the pattern rectangle will not tile or clamp.
845 if (!patRect.IsEmpty() && srcRect.Contains(RoundedOut(patRect))) {
846 // The pattern is a surface with an axis-aligned source rectangle
847 // fitting entirely in its bounds, so just treat it as a DrawSurface.
848 DrawSurface(pat.mSurface, aRect, patRect,
849 DrawSurfaceOptions(pat.mSamplingFilter), aOptions);
850 return;
855 MarkChanged();
856 SkRect rect = RectToSkRect(aRect);
857 AutoPaintSetup paint(mCanvas, aOptions, aPattern, &aRect, nullptr, &aRect);
859 mCanvas->drawRect(rect, paint.mPaint);
862 void DrawTargetSkia::Stroke(const Path* aPath, const Pattern& aPattern,
863 const StrokeOptions& aStrokeOptions,
864 const DrawOptions& aOptions) {
865 MarkChanged();
866 MOZ_ASSERT(aPath, "Null path");
867 if (aPath->GetBackendType() != BackendType::SKIA) {
868 return;
871 const PathSkia* skiaPath = static_cast<const PathSkia*>(aPath);
873 AutoPaintSetup paint(mCanvas, aOptions, aPattern);
874 if (!StrokeOptionsToPaint(paint.mPaint, aStrokeOptions)) {
875 return;
878 if (!skiaPath->GetPath().isFinite()) {
879 return;
882 mCanvas->drawPath(skiaPath->GetPath(), paint.mPaint);
885 static Double DashPeriodLength(const StrokeOptions& aStrokeOptions) {
886 Double length = 0;
887 for (size_t i = 0; i < aStrokeOptions.mDashLength; i++) {
888 length += aStrokeOptions.mDashPattern[i];
890 if (aStrokeOptions.mDashLength & 1) {
891 // "If an odd number of values is provided, then the list of values is
892 // repeated to yield an even number of values."
893 // Double the length.
894 length += length;
896 return length;
899 static inline Double RoundDownToMultiple(Double aValue, Double aFactor) {
900 return floor(aValue / aFactor) * aFactor;
903 static Rect UserSpaceStrokeClip(const IntRect& aDeviceClip,
904 const Matrix& aTransform,
905 const StrokeOptions& aStrokeOptions) {
906 Matrix inverse = aTransform;
907 if (!inverse.Invert()) {
908 return Rect();
910 Rect deviceClip(aDeviceClip);
911 deviceClip.Inflate(MaxStrokeExtents(aStrokeOptions, aTransform));
912 return inverse.TransformBounds(deviceClip);
915 static Rect ShrinkClippedStrokedRect(const Rect& aStrokedRect,
916 const IntRect& aDeviceClip,
917 const Matrix& aTransform,
918 const StrokeOptions& aStrokeOptions) {
919 Rect userSpaceStrokeClip =
920 UserSpaceStrokeClip(aDeviceClip, aTransform, aStrokeOptions);
921 RectDouble strokedRectDouble(aStrokedRect.X(), aStrokedRect.Y(),
922 aStrokedRect.Width(), aStrokedRect.Height());
923 RectDouble intersection = strokedRectDouble.Intersect(
924 RectDouble(userSpaceStrokeClip.X(), userSpaceStrokeClip.Y(),
925 userSpaceStrokeClip.Width(), userSpaceStrokeClip.Height()));
926 Double dashPeriodLength = DashPeriodLength(aStrokeOptions);
927 if (intersection.IsEmpty() || dashPeriodLength == 0.0f) {
928 return Rect(intersection.X(), intersection.Y(), intersection.Width(),
929 intersection.Height());
932 // Reduce the rectangle side lengths in multiples of the dash period length
933 // so that the visible dashes stay in the same place.
934 MarginDouble insetBy = strokedRectDouble - intersection;
935 insetBy.top = RoundDownToMultiple(insetBy.top, dashPeriodLength);
936 insetBy.right = RoundDownToMultiple(insetBy.right, dashPeriodLength);
937 insetBy.bottom = RoundDownToMultiple(insetBy.bottom, dashPeriodLength);
938 insetBy.left = RoundDownToMultiple(insetBy.left, dashPeriodLength);
940 strokedRectDouble.Deflate(insetBy);
941 return Rect(strokedRectDouble.X(), strokedRectDouble.Y(),
942 strokedRectDouble.Width(), strokedRectDouble.Height());
945 void DrawTargetSkia::StrokeRect(const Rect& aRect, const Pattern& aPattern,
946 const StrokeOptions& aStrokeOptions,
947 const DrawOptions& aOptions) {
948 // Stroking large rectangles with dashes is expensive with Skia (fixed
949 // overhead based on the number of dashes, regardless of whether the dashes
950 // are visible), so we try to reduce the size of the stroked rectangle as
951 // much as possible before passing it on to Skia.
952 Rect rect = aRect;
953 if (aStrokeOptions.mDashLength > 0 && !rect.IsEmpty()) {
954 IntRect deviceClip(IntPoint(0, 0), mSize);
955 SkIRect clipBounds;
956 if (mCanvas->getDeviceClipBounds(&clipBounds)) {
957 deviceClip = SkIRectToIntRect(clipBounds);
959 rect =
960 ShrinkClippedStrokedRect(rect, deviceClip, mTransform, aStrokeOptions);
961 if (rect.IsEmpty()) {
962 return;
966 MarkChanged();
967 AutoPaintSetup paint(mCanvas, aOptions, aPattern);
968 if (!StrokeOptionsToPaint(paint.mPaint, aStrokeOptions)) {
969 return;
972 mCanvas->drawRect(RectToSkRect(rect), paint.mPaint);
975 void DrawTargetSkia::StrokeLine(const Point& aStart, const Point& aEnd,
976 const Pattern& aPattern,
977 const StrokeOptions& aStrokeOptions,
978 const DrawOptions& aOptions) {
979 MarkChanged();
980 AutoPaintSetup paint(mCanvas, aOptions, aPattern);
981 if (!StrokeOptionsToPaint(paint.mPaint, aStrokeOptions)) {
982 return;
985 mCanvas->drawLine(SkFloatToScalar(aStart.x), SkFloatToScalar(aStart.y),
986 SkFloatToScalar(aEnd.x), SkFloatToScalar(aEnd.y),
987 paint.mPaint);
990 void DrawTargetSkia::Fill(const Path* aPath, const Pattern& aPattern,
991 const DrawOptions& aOptions) {
992 MarkChanged();
993 if (!aPath || aPath->GetBackendType() != BackendType::SKIA) {
994 return;
997 const PathSkia* skiaPath = static_cast<const PathSkia*>(aPath);
999 AutoPaintSetup paint(mCanvas, aOptions, aPattern);
1001 if (!skiaPath->GetPath().isFinite()) {
1002 return;
1005 mCanvas->drawPath(skiaPath->GetPath(), paint.mPaint);
1008 #ifdef MOZ_WIDGET_COCOA
1009 static inline CGAffineTransform GfxMatrixToCGAffineTransform(const Matrix& m) {
1010 CGAffineTransform t;
1011 t.a = m._11;
1012 t.b = m._12;
1013 t.c = m._21;
1014 t.d = m._22;
1015 t.tx = m._31;
1016 t.ty = m._32;
1017 return t;
1020 /***
1021 * We have to do a lot of work to draw glyphs with CG because
1022 * CG assumes that the origin of rects are in the bottom left
1023 * while every other DrawTarget assumes the top left is the origin.
1024 * This means we have to transform the CGContext to have rects
1025 * actually be applied in top left fashion. We do this by:
1027 * 1) Translating the context up by the height of the canvas
1028 * 2) Flipping the context by the Y axis so it's upside down.
1030 * These two transforms put the origin in the top left.
1031 * Transforms are better understood thinking about them from right to left order
1032 * (mathematically).
1034 * Consider a point we want to draw at (0, 10) in normal cartesian planes with
1035 * a box of (100, 100). in CG terms, this would be at (0, 10).
1036 * Positive Y values point up.
1037 * In our DrawTarget terms, positive Y values point down, so (0, 10) would be
1038 * at (0, 90) in cartesian plane terms. That means our point at (0, 10) in
1039 * DrawTarget terms should end up at (0, 90). How does this work with the
1040 * current transforms?
1042 * Going right to left with the transforms, a CGPoint of (0, 10) has cartesian
1043 * coordinates of (0, 10). The first flip of the Y axis puts the point now at
1044 * (0, -10); Next, we translate the context up by the size of the canvas
1045 * (Positive Y values go up in CG coordinates but down in our draw target
1046 * coordinates). Since our canvas size is (100, 100), the resulting coordinate
1047 * becomes (0, 90), which is what we expect from our DrawTarget code. These two
1048 * transforms put the CG context equal to what every other DrawTarget expects.
1050 * Next, we need two more transforms for actual text. IF we left the transforms
1051 * as is, the text would be drawn upside down, so we need another flip of the Y
1052 * axis to draw the text right side up. However, with only the flip, the text
1053 * would be drawn in the wrong place. Thus we also have to invert the Y position
1054 * of the glyphs to get them in the right place.
1056 * Thus we have the following transforms:
1057 * 1) Translation of the context up
1058 * 2) Flipping the context around the Y axis
1059 * 3) Flipping the context around the Y axis
1060 * 4) Inverting the Y position of each glyph
1062 * We cannot cancel out (2) and (3) as we have to apply the clips and transforms
1063 * of DrawTargetSkia between (2) and (3).
1065 * Consider the example letter P, drawn at (0, 20) in CG coordinates in a
1066 * (100, 100) rect.
1067 * Again, going right to left of the transforms. We'd get:
1069 * 1) The letter P drawn at (0, -20) due to the inversion of the Y axis
1070 * 2) The letter P upside down (b) at (0, 20) due to the second flip
1071 * 3) The letter P right side up at (0, -20) due to the first flip
1072 * 4) The letter P right side up at (0, 80) due to the translation
1074 * tl;dr - CGRects assume origin is bottom left, DrawTarget rects assume top
1075 * left.
1077 static bool SetupCGContext(DrawTargetSkia* aDT, CGContextRef aCGContext,
1078 SkCanvas* aCanvas, const IntPoint& aOrigin,
1079 const IntSize& aSize, bool aClipped) {
1080 // DrawTarget expects the origin to be at the top left, but CG
1081 // expects it to be at the bottom left. Transform to set the origin to
1082 // the top left. Have to set this before we do anything else.
1083 // This is transform (1) up top
1084 CGContextTranslateCTM(aCGContext, -aOrigin.x, aOrigin.y + aSize.height);
1086 // Transform (2) from the comments.
1087 CGContextScaleCTM(aCGContext, 1, -1);
1089 // Want to apply clips BEFORE the transform since the transform
1090 // will apply to the clips we apply.
1091 if (aClipped) {
1092 SkRegion clipRegion;
1093 aCanvas->temporary_internal_getRgnClip(&clipRegion);
1094 Vector<CGRect, 8> rects;
1095 for (SkRegion::Iterator it(clipRegion); !it.done(); it.next()) {
1096 const SkIRect& rect = it.rect();
1097 if (!rects.append(
1098 CGRectMake(rect.x(), rect.y(), rect.width(), rect.height()))) {
1099 break;
1102 if (rects.length()) {
1103 CGContextClipToRects(aCGContext, rects.begin(), rects.length());
1107 CGContextConcatCTM(aCGContext,
1108 GfxMatrixToCGAffineTransform(aDT->GetTransform()));
1109 return true;
1111 // End long comment about transforms.
1113 // The context returned from this method will have the origin
1114 // in the top left and will have applied all the neccessary clips
1115 // and transforms to the CGContext. See the comment above
1116 // SetupCGContext.
1117 CGContextRef DrawTargetSkia::BorrowCGContext(const DrawOptions& aOptions) {
1118 // Since we can't replay Skia clips, we have to use a layer if we have a
1119 // complex clip. After saving a layer, the SkCanvas queries for needing a
1120 // layer change so save if we pushed a layer.
1121 mNeedLayer = !mCanvas->isClipEmpty() && !mCanvas->isClipRect();
1122 if (mNeedLayer) {
1123 SkPaint paint;
1124 paint.setBlendMode(SkBlendMode::kSrc);
1125 SkCanvas::SaveLayerRec rec(nullptr, &paint,
1126 SkCanvas::kInitWithPrevious_SaveLayerFlag);
1127 mCanvas->saveLayer(rec);
1130 uint8_t* data = nullptr;
1131 int32_t stride;
1132 SurfaceFormat format;
1133 IntSize size;
1134 IntPoint origin;
1135 if (!LockBits(&data, &size, &stride, &format, &origin)) {
1136 NS_WARNING("Could not lock skia bits to wrap CG around");
1137 return nullptr;
1140 if (!mNeedLayer && (data == mCanvasData) && mCG && (mCGSize == size)) {
1141 // If our canvas data still points to the same data,
1142 // we can reuse the CG Context
1143 CGContextSetAlpha(mCG, aOptions.mAlpha);
1144 CGContextSetShouldAntialias(mCG,
1145 aOptions.mAntialiasMode != AntialiasMode::NONE);
1146 CGContextSaveGState(mCG);
1147 SetupCGContext(this, mCG, mCanvas, origin, size, true);
1148 return mCG;
1151 if (!mColorSpace) {
1152 mColorSpace = (format == SurfaceFormat::A8) ? CGColorSpaceCreateDeviceGray()
1153 : CGColorSpaceCreateDeviceRGB();
1156 if (mCG) {
1157 // Release the old CG context since it's no longer valid.
1158 CGContextRelease(mCG);
1161 mCanvasData = data;
1162 mCGSize = size;
1164 uint32_t bitmapInfo =
1165 (format == SurfaceFormat::A8)
1166 ? kCGImageAlphaOnly
1167 : kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
1169 mCG = CGBitmapContextCreateWithData(
1170 mCanvasData, mCGSize.width, mCGSize.height, 8, /* bits per component */
1171 stride, mColorSpace, bitmapInfo, NULL, /* Callback when released */
1172 NULL);
1173 if (!mCG) {
1174 if (mNeedLayer) {
1175 mCanvas->restore();
1177 ReleaseBits(mCanvasData);
1178 NS_WARNING("Could not create bitmap around skia data\n");
1179 return nullptr;
1182 CGContextSetAlpha(mCG, aOptions.mAlpha);
1183 CGContextSetShouldAntialias(mCG,
1184 aOptions.mAntialiasMode != AntialiasMode::NONE);
1185 CGContextSetShouldSmoothFonts(mCG, true);
1186 CGContextSetTextDrawingMode(mCG, kCGTextFill);
1187 CGContextSaveGState(mCG);
1188 SetupCGContext(this, mCG, mCanvas, origin, size, !mNeedLayer);
1189 return mCG;
1192 void DrawTargetSkia::ReturnCGContext(CGContextRef aCGContext) {
1193 MOZ_ASSERT(aCGContext == mCG);
1194 ReleaseBits(mCanvasData);
1195 CGContextRestoreGState(aCGContext);
1197 if (mNeedLayer) {
1198 // A layer was used for clipping and is about to be popped by the restore.
1199 // Make sure the CG context referencing it is released first so the popped
1200 // layer doesn't accidentally get used.
1201 if (mCG) {
1202 CGContextRelease(mCG);
1203 mCG = nullptr;
1205 mCanvas->restore();
1209 CGContextRef BorrowedCGContext::BorrowCGContextFromDrawTarget(DrawTarget* aDT) {
1210 DrawTargetSkia* skiaDT = static_cast<DrawTargetSkia*>(aDT);
1211 return skiaDT->BorrowCGContext(DrawOptions());
1214 void BorrowedCGContext::ReturnCGContextToDrawTarget(DrawTarget* aDT,
1215 CGContextRef cg) {
1216 DrawTargetSkia* skiaDT = static_cast<DrawTargetSkia*>(aDT);
1217 skiaDT->ReturnCGContext(cg);
1219 #endif
1221 static bool CanDrawFont(ScaledFont* aFont) {
1222 switch (aFont->GetType()) {
1223 case FontType::FREETYPE:
1224 case FontType::FONTCONFIG:
1225 case FontType::MAC:
1226 case FontType::GDI:
1227 case FontType::DWRITE:
1228 return true;
1229 default:
1230 return false;
1234 void DrawTargetSkia::DrawGlyphs(ScaledFont* aFont, const GlyphBuffer& aBuffer,
1235 const Pattern& aPattern,
1236 const StrokeOptions* aStrokeOptions,
1237 const DrawOptions& aOptions) {
1238 if (!CanDrawFont(aFont)) {
1239 return;
1242 MarkChanged();
1244 ScaledFontBase* skiaFont = static_cast<ScaledFontBase*>(aFont);
1245 SkTypeface* typeface = skiaFont->GetSkTypeface();
1246 if (!typeface) {
1247 return;
1250 AutoPaintSetup paint(mCanvas, aOptions, aPattern);
1251 if (aStrokeOptions && !StrokeOptionsToPaint(paint.mPaint, *aStrokeOptions)) {
1252 return;
1255 AntialiasMode aaMode = aFont->GetDefaultAAMode();
1256 if (aOptions.mAntialiasMode != AntialiasMode::DEFAULT) {
1257 aaMode = aOptions.mAntialiasMode;
1259 bool aaEnabled = aaMode != AntialiasMode::NONE;
1260 paint.mPaint.setAntiAlias(aaEnabled);
1262 SkFont font(sk_ref_sp(typeface), SkFloatToScalar(skiaFont->mSize));
1264 bool useSubpixelAA =
1265 GetPermitSubpixelAA() &&
1266 (aaMode == AntialiasMode::DEFAULT || aaMode == AntialiasMode::SUBPIXEL);
1267 font.setEdging(useSubpixelAA ? SkFont::Edging::kSubpixelAntiAlias
1268 : (aaEnabled ? SkFont::Edging::kAntiAlias
1269 : SkFont::Edging::kAlias));
1271 skiaFont->SetupSkFontDrawOptions(font);
1273 // Limit the amount of internal batch allocations Skia does.
1274 const uint32_t kMaxGlyphBatchSize = 8192;
1276 for (uint32_t offset = 0; offset < aBuffer.mNumGlyphs;) {
1277 uint32_t batchSize =
1278 std::min(aBuffer.mNumGlyphs - offset, kMaxGlyphBatchSize);
1279 SkTextBlobBuilder builder;
1280 auto runBuffer = builder.allocRunPos(font, batchSize);
1281 for (uint32_t i = 0; i < batchSize; i++, offset++) {
1282 runBuffer.glyphs[i] = aBuffer.mGlyphs[offset].mIndex;
1283 runBuffer.points()[i] = PointToSkPoint(aBuffer.mGlyphs[offset].mPosition);
1286 sk_sp<SkTextBlob> text = builder.make();
1287 mCanvas->drawTextBlob(text, 0, 0, paint.mPaint);
1291 Maybe<Rect> DrawTargetSkia::GetGlyphLocalBounds(
1292 ScaledFont* aFont, const GlyphBuffer& aBuffer, const Pattern& aPattern,
1293 const StrokeOptions* aStrokeOptions, const DrawOptions& aOptions) {
1294 if (!CanDrawFont(aFont)) {
1295 return Nothing();
1298 ScaledFontBase* skiaFont = static_cast<ScaledFontBase*>(aFont);
1299 SkTypeface* typeface = skiaFont->GetSkTypeface();
1300 if (!typeface) {
1301 return Nothing();
1304 AutoPaintSetup paint(mCanvas, aOptions, aPattern);
1305 if (aStrokeOptions && !StrokeOptionsToPaint(paint.mPaint, *aStrokeOptions)) {
1306 return Nothing();
1309 AntialiasMode aaMode = aFont->GetDefaultAAMode();
1310 if (aOptions.mAntialiasMode != AntialiasMode::DEFAULT) {
1311 aaMode = aOptions.mAntialiasMode;
1313 bool aaEnabled = aaMode != AntialiasMode::NONE;
1314 paint.mPaint.setAntiAlias(aaEnabled);
1316 SkFont font(sk_ref_sp(typeface), SkFloatToScalar(skiaFont->mSize));
1318 bool useSubpixelAA =
1319 GetPermitSubpixelAA() &&
1320 (aaMode == AntialiasMode::DEFAULT || aaMode == AntialiasMode::SUBPIXEL);
1321 font.setEdging(useSubpixelAA ? SkFont::Edging::kSubpixelAntiAlias
1322 : (aaEnabled ? SkFont::Edging::kAntiAlias
1323 : SkFont::Edging::kAlias));
1325 skiaFont->SetupSkFontDrawOptions(font);
1327 // Limit the amount of internal batch allocations Skia does.
1328 const uint32_t kMaxGlyphBatchSize = 8192;
1330 Rect bounds;
1331 for (uint32_t offset = 0; offset < aBuffer.mNumGlyphs;) {
1332 uint32_t batchSize =
1333 std::min(aBuffer.mNumGlyphs - offset, kMaxGlyphBatchSize);
1334 SkTextBlobBuilder builder;
1335 auto runBuffer = builder.allocRunPos(font, batchSize);
1336 for (uint32_t i = 0; i < batchSize; i++, offset++) {
1337 runBuffer.glyphs[i] = aBuffer.mGlyphs[offset].mIndex;
1338 runBuffer.points()[i] = PointToSkPoint(aBuffer.mGlyphs[offset].mPosition);
1341 sk_sp<SkTextBlob> text = builder.make();
1342 bounds = bounds.Union(SkRectToRect(text->bounds()));
1345 if (bounds.IsEmpty()) {
1346 return Nothing();
1349 return Some(bounds);
1352 void DrawTargetSkia::FillGlyphs(ScaledFont* aFont, const GlyphBuffer& aBuffer,
1353 const Pattern& aPattern,
1354 const DrawOptions& aOptions) {
1355 DrawGlyphs(aFont, aBuffer, aPattern, nullptr, aOptions);
1358 void DrawTargetSkia::StrokeGlyphs(ScaledFont* aFont, const GlyphBuffer& aBuffer,
1359 const Pattern& aPattern,
1360 const StrokeOptions& aStrokeOptions,
1361 const DrawOptions& aOptions) {
1362 DrawGlyphs(aFont, aBuffer, aPattern, &aStrokeOptions, aOptions);
1365 void DrawTargetSkia::Mask(const Pattern& aSource, const Pattern& aMask,
1366 const DrawOptions& aOptions) {
1367 SkIRect maskBounds;
1368 if (!mCanvas->getDeviceClipBounds(&maskBounds)) {
1369 return;
1371 SkPoint maskOrigin;
1372 maskOrigin.iset(maskBounds.fLeft, maskBounds.fTop);
1374 SkMatrix maskMatrix = mCanvas->getTotalMatrix();
1375 maskMatrix.postTranslate(-maskOrigin.fX, -maskOrigin.fY);
1377 MarkChanged();
1378 AutoPaintSetup paint(mCanvas, aOptions, aSource, nullptr, &maskMatrix);
1380 Maybe<MutexAutoLock> lock;
1381 SkPaint maskPaint;
1382 SetPaintPattern(maskPaint, aMask, lock);
1384 SkBitmap maskBitmap;
1385 if (!maskBitmap.tryAllocPixelsFlags(
1386 SkImageInfo::MakeA8(maskBounds.width(), maskBounds.height()),
1387 SkBitmap::kZeroPixels_AllocFlag)) {
1388 return;
1391 SkCanvas maskCanvas(maskBitmap);
1392 maskCanvas.setMatrix(maskMatrix);
1393 maskCanvas.drawPaint(maskPaint);
1395 mCanvas->save();
1396 mCanvas->resetMatrix();
1398 mCanvas->drawBitmap(maskBitmap, maskOrigin.fX, maskOrigin.fY, &paint.mPaint);
1400 mCanvas->restore();
1403 void DrawTargetSkia::MaskSurface(const Pattern& aSource, SourceSurface* aMask,
1404 Point aOffset, const DrawOptions& aOptions) {
1405 MarkChanged();
1407 SkMatrix invOffset = SkMatrix::MakeTrans(SkFloatToScalar(-aOffset.x),
1408 SkFloatToScalar(-aOffset.y));
1409 AutoPaintSetup paint(mCanvas, aOptions, aSource, nullptr, &invOffset);
1411 Maybe<MutexAutoLock> lock;
1412 sk_sp<SkImage> alphaMask = ExtractAlphaForSurface(aMask, lock);
1413 if (!alphaMask) {
1414 gfxDebug() << *this << ": MaskSurface() failed to extract alpha for mask";
1415 return;
1418 mCanvas->drawImage(alphaMask, aOffset.x + aMask->GetRect().x,
1419 aOffset.y + aMask->GetRect().y, &paint.mPaint);
1422 bool DrawTarget::Draw3DTransformedSurface(SourceSurface* aSurface,
1423 const Matrix4x4& aMatrix) {
1424 // Composite the 3D transform with the DT's transform.
1425 Matrix4x4 fullMat = aMatrix * Matrix4x4::From2D(mTransform);
1426 if (fullMat.IsSingular()) {
1427 return false;
1429 // Transform the surface bounds and clip to this DT.
1430 IntRect xformBounds = RoundedOut(fullMat.TransformAndClipBounds(
1431 Rect(Point(0, 0), Size(aSurface->GetSize())),
1432 Rect(Point(0, 0), Size(GetSize()))));
1433 if (xformBounds.IsEmpty()) {
1434 return true;
1436 // Offset the matrix by the transformed origin.
1437 fullMat.PostTranslate(-xformBounds.X(), -xformBounds.Y(), 0);
1439 // Read in the source data.
1440 Maybe<MutexAutoLock> lock;
1441 sk_sp<SkImage> srcImage = GetSkImageForSurface(aSurface, &lock);
1442 if (!srcImage) {
1443 return true;
1446 // Set up an intermediate destination surface only the size of the transformed
1447 // bounds. Try to pass through the source's format unmodified in both the BGRA
1448 // and ARGB cases.
1449 RefPtr<DataSourceSurface> dstSurf = Factory::CreateDataSourceSurface(
1450 xformBounds.Size(),
1451 !srcImage->isOpaque() ? aSurface->GetFormat()
1452 : SurfaceFormat::A8R8G8B8_UINT32,
1453 true);
1454 if (!dstSurf) {
1455 return false;
1458 DataSourceSurface::ScopedMap map(dstSurf, DataSourceSurface::READ_WRITE);
1459 std::unique_ptr<SkCanvas> dstCanvas(SkCanvas::MakeRasterDirect(
1460 SkImageInfo::Make(xformBounds.Width(), xformBounds.Height(),
1461 GfxFormatToSkiaColorType(dstSurf->GetFormat()),
1462 kPremul_SkAlphaType),
1463 map.GetData(), map.GetStride()));
1464 if (!dstCanvas) {
1465 return false;
1468 // Do the transform.
1469 SkPaint paint;
1470 paint.setAntiAlias(true);
1471 paint.setFilterQuality(kLow_SkFilterQuality);
1472 paint.setBlendMode(SkBlendMode::kSrc);
1474 SkMatrix xform;
1475 GfxMatrixToSkiaMatrix(fullMat, xform);
1476 dstCanvas->setMatrix(xform);
1478 dstCanvas->drawImage(srcImage, 0, 0, &paint);
1479 dstCanvas->flush();
1481 // Temporarily reset the DT's transform, since it has already been composed
1482 // above.
1483 Matrix origTransform = mTransform;
1484 SetTransform(Matrix());
1486 // Draw the transformed surface within the transformed bounds.
1487 DrawSurface(dstSurf, Rect(xformBounds),
1488 Rect(Point(0, 0), Size(xformBounds.Size())));
1490 SetTransform(origTransform);
1492 return true;
1495 bool DrawTargetSkia::Draw3DTransformedSurface(SourceSurface* aSurface,
1496 const Matrix4x4& aMatrix) {
1497 if (aMatrix.IsSingular()) {
1498 return false;
1501 MarkChanged();
1503 Maybe<MutexAutoLock> lock;
1504 sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
1505 if (!image) {
1506 return true;
1509 mCanvas->save();
1511 SkPaint paint;
1512 paint.setAntiAlias(true);
1513 paint.setFilterQuality(kLow_SkFilterQuality);
1515 SkMatrix xform;
1516 GfxMatrixToSkiaMatrix(aMatrix, xform);
1517 mCanvas->concat(xform);
1519 mCanvas->drawImage(image, 0, 0, &paint);
1521 mCanvas->restore();
1523 return true;
1526 already_AddRefed<SourceSurface> DrawTargetSkia::CreateSourceSurfaceFromData(
1527 unsigned char* aData, const IntSize& aSize, int32_t aStride,
1528 SurfaceFormat aFormat) const {
1529 RefPtr<SourceSurfaceSkia> newSurf = new SourceSurfaceSkia();
1531 if (!newSurf->InitFromData(aData, aSize, aStride, aFormat)) {
1532 gfxDebug() << *this
1533 << ": Failure to create source surface from data. Size: "
1534 << aSize;
1535 return nullptr;
1538 return newSurf.forget();
1541 already_AddRefed<DrawTarget> DrawTargetSkia::CreateSimilarDrawTarget(
1542 const IntSize& aSize, SurfaceFormat aFormat) const {
1543 RefPtr<DrawTargetSkia> target = new DrawTargetSkia();
1544 #ifdef DEBUG
1545 if (!IsBackedByPixels(mCanvas)) {
1546 // If our canvas is backed by vector storage such as PDF then we want to
1547 // create a new DrawTarget with similar storage to avoid losing fidelity
1548 // (fidelity will be lost if the returned DT is Snapshot()'ed and drawn
1549 // back onto us since a raster will be drawn instead of vector commands).
1550 NS_WARNING("Not backed by pixels - we need to handle PDF backed SkCanvas");
1552 #endif
1554 if (!target->Init(aSize, aFormat)) {
1555 return nullptr;
1557 return target.forget();
1560 bool DrawTargetSkia::CanCreateSimilarDrawTarget(const IntSize& aSize,
1561 SurfaceFormat aFormat) const {
1562 auto minmaxPair = std::minmax(aSize.width, aSize.height);
1563 return minmaxPair.first > 0 &&
1564 size_t(minmaxPair.second) < GetMaxSurfaceSize();
1567 RefPtr<DrawTarget> DrawTargetSkia::CreateClippedDrawTarget(
1568 const Rect& aBounds, SurfaceFormat aFormat) {
1569 SkIRect clipBounds;
1571 RefPtr<DrawTarget> result;
1572 // Doing this save()/restore() dance is wasteful
1573 mCanvas->save();
1574 if (!aBounds.IsEmpty()) {
1575 mCanvas->clipRect(RectToSkRect(aBounds), SkClipOp::kIntersect, true);
1577 if (mCanvas->getDeviceClipBounds(&clipBounds)) {
1578 RefPtr<DrawTarget> dt = CreateSimilarDrawTarget(
1579 IntSize(clipBounds.width(), clipBounds.height()), aFormat);
1580 result = gfx::Factory::CreateOffsetDrawTarget(
1581 dt, IntPoint(clipBounds.x(), clipBounds.y()));
1582 result->SetTransform(mTransform);
1583 } else {
1584 // Everything is clipped but we still want some kind of surface
1585 result = CreateSimilarDrawTarget(IntSize(1, 1), aFormat);
1587 mCanvas->restore();
1588 return result;
1591 already_AddRefed<SourceSurface>
1592 DrawTargetSkia::OptimizeSourceSurfaceForUnknownAlpha(
1593 SourceSurface* aSurface) const {
1594 if (aSurface->GetType() == SurfaceType::SKIA) {
1595 RefPtr<SourceSurface> surface(aSurface);
1596 return surface.forget();
1599 RefPtr<DataSourceSurface> dataSurface = aSurface->GetDataSurface();
1600 DataSourceSurface::ScopedMap map(dataSurface, DataSourceSurface::READ_WRITE);
1602 // For plugins, GDI can sometimes just write 0 to the alpha channel
1603 // even for RGBX formats. In this case, we have to manually write
1604 // the alpha channel to make Skia happy with RGBX and in case GDI
1605 // writes some bad data. Luckily, this only happens on plugins.
1606 WriteRGBXFormat(map.GetData(), dataSurface->GetSize(), map.GetStride(),
1607 dataSurface->GetFormat());
1608 return dataSurface.forget();
1611 already_AddRefed<SourceSurface> DrawTargetSkia::OptimizeSourceSurface(
1612 SourceSurface* aSurface) const {
1613 if (aSurface->GetType() == SurfaceType::SKIA) {
1614 RefPtr<SourceSurface> surface(aSurface);
1615 return surface.forget();
1618 // If we're not using skia-gl then drawing doesn't require any
1619 // uploading, so any data surface is fine. Call GetDataSurface
1620 // to trigger any required readback so that it only happens
1621 // once.
1622 RefPtr<DataSourceSurface> dataSurface = aSurface->GetDataSurface();
1623 #ifdef DEBUG
1624 DataSourceSurface::ScopedMap map(dataSurface, DataSourceSurface::READ);
1625 MOZ_ASSERT(VerifyRGBXFormat(map.GetData(), dataSurface->GetSize(),
1626 map.GetStride(), dataSurface->GetFormat()));
1627 #endif
1628 return dataSurface.forget();
1631 already_AddRefed<SourceSurface>
1632 DrawTargetSkia::CreateSourceSurfaceFromNativeSurface(
1633 const NativeSurface& aSurface) const {
1634 return nullptr;
1637 void DrawTargetSkia::BlendSurface(SourceSurface* aSurface,
1638 const IntRect& aSourceRect,
1639 const IntPoint& aDestination,
1640 CompositionOp aOperator) {
1641 MarkChanged();
1643 Maybe<MutexAutoLock> lock;
1644 sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
1645 if (!image) {
1646 return;
1649 mCanvas->save();
1650 mCanvas->setMatrix(SkMatrix::MakeTrans(SkIntToScalar(aDestination.x),
1651 SkIntToScalar(aDestination.y)));
1652 mCanvas->clipRect(SkRect::MakeIWH(aSourceRect.Width(), aSourceRect.Height()),
1653 SkClipOp::kReplace_deprecated);
1655 SkPaint paint;
1656 if (aOperator == CompositionOp::OP_SOURCE) {
1657 if (!image->isOpaque()) {
1658 // Keep the xfermode as SOURCE_OVER for opaque bitmaps
1659 // http://code.google.com/p/skia/issues/detail?id=628
1660 paint.setBlendMode(SkBlendMode::kSrc);
1662 // drawImage with A8 images ends up doing a mask operation
1663 // so we need to clear before
1664 if (image->isAlphaOnly()) {
1665 mCanvas->clear(SK_ColorTRANSPARENT);
1667 } else {
1668 paint.setBlendMode(GfxOpToSkiaOp(aOperator));
1671 mCanvas->drawImage(image, -SkIntToScalar(aSourceRect.X()),
1672 -SkIntToScalar(aSourceRect.Y()), &paint);
1673 mCanvas->restore();
1676 static inline SkPixelGeometry GetSkPixelGeometry() {
1677 return Factory::GetBGRSubpixelOrder() ? kBGR_H_SkPixelGeometry
1678 : kRGB_H_SkPixelGeometry;
1681 template <typename T>
1682 [[nodiscard]] static already_AddRefed<T> AsRefPtr(sk_sp<T>&& aSkPtr) {
1683 return already_AddRefed<T>(aSkPtr.release());
1686 bool DrawTargetSkia::Init(const IntSize& aSize, SurfaceFormat aFormat) {
1687 if (size_t(std::max(aSize.width, aSize.height)) > GetMaxSurfaceSize()) {
1688 return false;
1691 // we need to have surfaces that have a stride aligned to 4 for interop with
1692 // cairo
1693 SkImageInfo info = MakeSkiaImageInfo(aSize, aFormat);
1694 size_t stride = SkAlign4(info.minRowBytes());
1695 SkSurfaceProps props(0, GetSkPixelGeometry());
1696 mSurface = AsRefPtr(SkSurface::MakeRaster(info, stride, &props));
1697 if (!mSurface) {
1698 return false;
1701 mSize = aSize;
1702 mFormat = aFormat;
1703 mCanvas = mSurface->getCanvas();
1704 SetPermitSubpixelAA(IsOpaque(mFormat));
1706 if (info.isOpaque()) {
1707 mCanvas->clear(SK_ColorBLACK);
1709 return true;
1712 bool DrawTargetSkia::Init(SkCanvas* aCanvas) {
1713 mCanvas = aCanvas;
1715 SkImageInfo imageInfo = mCanvas->imageInfo();
1717 // If the canvas is backed by pixels we clear it to be on the safe side. If
1718 // it's not (for example, for PDF output) we don't.
1719 if (IsBackedByPixels(mCanvas)) {
1720 SkColor clearColor =
1721 imageInfo.isOpaque() ? SK_ColorBLACK : SK_ColorTRANSPARENT;
1722 mCanvas->clear(clearColor);
1725 SkISize size = mCanvas->getBaseLayerSize();
1726 mSize.width = size.width();
1727 mSize.height = size.height();
1728 mFormat =
1729 SkiaColorTypeToGfxFormat(imageInfo.colorType(), imageInfo.alphaType());
1730 SetPermitSubpixelAA(IsOpaque(mFormat));
1731 return true;
1734 bool DrawTargetSkia::Init(unsigned char* aData, const IntSize& aSize,
1735 int32_t aStride, SurfaceFormat aFormat,
1736 bool aUninitialized) {
1737 MOZ_ASSERT((aFormat != SurfaceFormat::B8G8R8X8) || aUninitialized ||
1738 VerifyRGBXFormat(aData, aSize, aStride, aFormat));
1740 SkSurfaceProps props(0, GetSkPixelGeometry());
1741 mSurface = AsRefPtr(SkSurface::MakeRasterDirect(
1742 MakeSkiaImageInfo(aSize, aFormat), aData, aStride, &props));
1743 if (!mSurface) {
1744 return false;
1747 mSize = aSize;
1748 mFormat = aFormat;
1749 mCanvas = mSurface->getCanvas();
1750 SetPermitSubpixelAA(IsOpaque(mFormat));
1751 return true;
1754 bool DrawTargetSkia::Init(RefPtr<DataSourceSurface>&& aSurface) {
1755 auto map =
1756 new DataSourceSurface::ScopedMap(aSurface, DataSourceSurface::READ_WRITE);
1757 if (!map->IsMapped()) {
1758 delete map;
1759 return false;
1762 SurfaceFormat format = aSurface->GetFormat();
1763 IntSize size = aSurface->GetSize();
1764 MOZ_ASSERT((format != SurfaceFormat::B8G8R8X8) ||
1765 VerifyRGBXFormat(map->GetData(), size, map->GetStride(), format));
1767 SkSurfaceProps props(0, GetSkPixelGeometry());
1768 mSurface = AsRefPtr(SkSurface::MakeRasterDirectReleaseProc(
1769 MakeSkiaImageInfo(size, format), map->GetData(), map->GetStride(),
1770 DrawTargetSkia::ReleaseMappedSkSurface, map, &props));
1771 if (!mSurface) {
1772 delete map;
1773 return false;
1776 // map is now owned by mSurface
1777 mBackingSurface = std::move(aSurface);
1778 mSize = size;
1779 mFormat = format;
1780 mCanvas = mSurface->getCanvas();
1781 SetPermitSubpixelAA(IsOpaque(format));
1782 return true;
1785 /* static */ void DrawTargetSkia::ReleaseMappedSkSurface(void* aPixels,
1786 void* aContext) {
1787 auto map = reinterpret_cast<DataSourceSurface::ScopedMap*>(aContext);
1788 delete map;
1791 void DrawTargetSkia::SetTransform(const Matrix& aTransform) {
1792 SkMatrix mat;
1793 GfxMatrixToSkiaMatrix(aTransform, mat);
1794 mCanvas->setMatrix(mat);
1795 mTransform = aTransform;
1798 void* DrawTargetSkia::GetNativeSurface(NativeSurfaceType aType) {
1799 return nullptr;
1802 already_AddRefed<PathBuilder> DrawTargetSkia::CreatePathBuilder(
1803 FillRule aFillRule) const {
1804 return MakeAndAddRef<PathBuilderSkia>(aFillRule);
1807 void DrawTargetSkia::Clear(const Rect* aRect) {
1808 MarkChanged();
1809 mCanvas->save();
1810 if (aRect) {
1811 // If a local-space clip rect is supplied, then restrict clearing to that.
1812 mCanvas->clipRect(RectToSkRect(*aRect), SkClipOp::kIntersect, true);
1813 } else {
1814 // Otherwise, clear the entire surface.
1815 mCanvas->resetMatrix();
1816 mCanvas->clipRect(IntRectToSkRect(GetRect()),
1817 SkClipOp::kReplace_deprecated);
1819 SkColor clearColor = (mFormat == SurfaceFormat::B8G8R8X8)
1820 ? SK_ColorBLACK
1821 : SK_ColorTRANSPARENT;
1822 mCanvas->clear(clearColor);
1823 mCanvas->restore();
1826 void DrawTargetSkia::PushClip(const Path* aPath) {
1827 if (aPath->GetBackendType() != BackendType::SKIA) {
1828 return;
1831 const PathSkia* skiaPath = static_cast<const PathSkia*>(aPath);
1832 mCanvas->save();
1833 mCanvas->clipPath(skiaPath->GetPath(), SkClipOp::kIntersect, true);
1836 void DrawTargetSkia::PushDeviceSpaceClipRects(const IntRect* aRects,
1837 uint32_t aCount) {
1838 // Build a region by unioning all the rects together.
1839 SkRegion region;
1840 for (uint32_t i = 0; i < aCount; i++) {
1841 region.op(IntRectToSkIRect(aRects[i]), SkRegion::kUnion_Op);
1844 // Clip with the resulting region. clipRegion does not transform
1845 // this region by the current transform, unlike the other SkCanvas
1846 // clip methods, so it is just passed through in device-space.
1847 mCanvas->save();
1848 mCanvas->clipRegion(region, SkClipOp::kIntersect);
1851 void DrawTargetSkia::PushClipRect(const Rect& aRect) {
1852 SkRect rect = RectToSkRect(aRect);
1854 mCanvas->save();
1855 mCanvas->clipRect(rect, SkClipOp::kIntersect, true);
1858 void DrawTargetSkia::PopClip() {
1859 mCanvas->restore();
1860 SetTransform(GetTransform());
1863 Maybe<Rect> DrawTargetSkia::GetDeviceClipRect() const {
1864 if (mCanvas->isClipEmpty()) {
1865 return Some(Rect());
1867 if (mCanvas->isClipRect()) {
1868 SkIRect deviceBounds;
1869 if (mCanvas->getDeviceClipBounds(&deviceBounds)) {
1870 return Some(Rect(SkIRectToIntRect(deviceBounds)));
1873 return Nothing();
1876 void DrawTargetSkia::PushLayer(bool aOpaque, Float aOpacity,
1877 SourceSurface* aMask,
1878 const Matrix& aMaskTransform,
1879 const IntRect& aBounds, bool aCopyBackground) {
1880 PushLayerWithBlend(aOpaque, aOpacity, aMask, aMaskTransform, aBounds,
1881 aCopyBackground, CompositionOp::OP_OVER);
1884 void DrawTargetSkia::PushLayerWithBlend(bool aOpaque, Float aOpacity,
1885 SourceSurface* aMask,
1886 const Matrix& aMaskTransform,
1887 const IntRect& aBounds,
1888 bool aCopyBackground,
1889 CompositionOp aCompositionOp) {
1890 PushedLayer layer(GetPermitSubpixelAA(), aMask);
1891 mPushedLayers.push_back(layer);
1893 SkPaint paint;
1895 paint.setAlpha(ColorFloatToByte(aOpacity));
1896 paint.setBlendMode(GfxOpToSkiaOp(aCompositionOp));
1898 // aBounds is supplied in device space, but SaveLayerRec wants local space.
1899 SkRect bounds = IntRectToSkRect(aBounds);
1900 if (!bounds.isEmpty()) {
1901 SkMatrix inverseCTM;
1902 if (mCanvas->getTotalMatrix().invert(&inverseCTM)) {
1903 inverseCTM.mapRect(&bounds);
1904 } else {
1905 bounds.setEmpty();
1909 // We don't pass a lock object to GetSkImageForSurface here, to force a
1910 // copy of the data if this is a copy-on-write snapshot. If we instead held
1911 // the lock until the corresponding PopLayer, we'd risk deadlocking if someone
1912 // tried to touch the originating DrawTarget while the layer was pushed.
1913 sk_sp<SkImage> clipImage =
1914 aMask ? GetSkImageForSurface(aMask, nullptr) : nullptr;
1915 SkMatrix clipMatrix;
1916 GfxMatrixToSkiaMatrix(aMaskTransform, clipMatrix);
1917 if (aMask) {
1918 clipMatrix.preTranslate(aMask->GetRect().X(), aMask->GetRect().Y());
1921 SkCanvas::SaveLayerRec saveRec(
1922 aBounds.IsEmpty() ? nullptr : &bounds, &paint, nullptr, clipImage.get(),
1923 &clipMatrix,
1924 SkCanvas::kPreserveLCDText_SaveLayerFlag |
1925 (aCopyBackground ? SkCanvas::kInitWithPrevious_SaveLayerFlag : 0));
1927 mCanvas->saveLayer(saveRec);
1929 SetPermitSubpixelAA(aOpaque);
1931 #ifdef MOZ_WIDGET_COCOA
1932 CGContextRelease(mCG);
1933 mCG = nullptr;
1934 #endif
1937 void DrawTargetSkia::PopLayer() {
1938 MarkChanged();
1940 MOZ_ASSERT(!mPushedLayers.empty());
1941 const PushedLayer& layer = mPushedLayers.back();
1943 mCanvas->restore();
1945 SetTransform(GetTransform());
1946 SetPermitSubpixelAA(layer.mOldPermitSubpixelAA);
1948 mPushedLayers.pop_back();
1950 #ifdef MOZ_WIDGET_COCOA
1951 CGContextRelease(mCG);
1952 mCG = nullptr;
1953 #endif
1956 already_AddRefed<GradientStops> DrawTargetSkia::CreateGradientStops(
1957 GradientStop* aStops, uint32_t aNumStops, ExtendMode aExtendMode) const {
1958 std::vector<GradientStop> stops;
1959 stops.resize(aNumStops);
1960 for (uint32_t i = 0; i < aNumStops; i++) {
1961 stops[i] = aStops[i];
1963 std::stable_sort(stops.begin(), stops.end());
1965 return MakeAndAddRef<GradientStopsSkia>(stops, aNumStops, aExtendMode);
1968 already_AddRefed<FilterNode> DrawTargetSkia::CreateFilter(FilterType aType) {
1969 return FilterNodeSoftware::Create(aType);
1972 void DrawTargetSkia::MarkChanged() {
1973 // I'm not entirely certain whether this lock is needed, as multiple threads
1974 // should never modify the DrawTarget at the same time anyway, but this seems
1975 // like the safest.
1976 MutexAutoLock lock(mSnapshotLock);
1977 if (mSnapshot) {
1978 if (mSnapshot->hasOneRef()) {
1979 // No owners outside of this DrawTarget's own reference. Just dump it.
1980 mSnapshot = nullptr;
1981 return;
1984 mSnapshot->DrawTargetWillChange();
1985 mSnapshot = nullptr;
1987 // Handle copying of any image snapshots bound to the surface.
1988 if (mSurface) {
1989 mSurface->notifyContentWillChange(SkSurface::kRetain_ContentChangeMode);
1994 } // namespace mozilla::gfx