Bug 1876335 - use GRADLE_MAVEN_REPOSITORIES in more places. r=owlish,geckoview-review...
[gecko.git] / gfx / 2d / DrawTargetSkia.cpp
blob098ff811175e52c99b95253432fe02db43b5353f
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/CheckedInt.h"
14 #include "mozilla/Vector.h"
16 #include "skia/include/core/SkBitmap.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/SkImageFilters.h"
26 #include "skia/include/private/base/SkMalloc.h"
27 #include "Blur.h"
28 #include "Logging.h"
29 #include "Tools.h"
30 #include "PathHelpers.h"
31 #include "PathSkia.h"
32 #include "Swizzle.h"
33 #include <algorithm>
34 #include <cmath>
36 #ifdef MOZ_WIDGET_COCOA
37 # include "BorrowedContext.h"
38 # include <ApplicationServices/ApplicationServices.h>
39 #endif
41 #ifdef XP_WIN
42 # include "ScaledFontDWrite.h"
43 #endif
45 namespace mozilla {
47 void RefPtrTraits<SkSurface>::Release(SkSurface* aSurface) {
48 SkSafeUnref(aSurface);
51 void RefPtrTraits<SkSurface>::AddRef(SkSurface* aSurface) {
52 SkSafeRef(aSurface);
55 } // namespace mozilla
57 namespace mozilla::gfx {
59 class GradientStopsSkia : public GradientStops {
60 public:
61 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsSkia, override)
63 GradientStopsSkia(const std::vector<GradientStop>& aStops, uint32_t aNumStops,
64 ExtendMode aExtendMode)
65 : mCount(aNumStops), mExtendMode(aExtendMode) {
66 if (mCount == 0) {
67 return;
70 // Skia gradients always require a stop at 0.0 and 1.0, insert these if
71 // we don't have them.
72 uint32_t shift = 0;
73 if (aStops[0].offset != 0) {
74 mCount++;
75 shift = 1;
77 if (aStops[aNumStops - 1].offset != 1) {
78 mCount++;
80 mColors.resize(mCount);
81 mPositions.resize(mCount);
82 if (aStops[0].offset != 0) {
83 mColors[0] = ColorToSkColor(aStops[0].color, 1.0);
84 mPositions[0] = 0;
86 for (uint32_t i = 0; i < aNumStops; i++) {
87 mColors[i + shift] = ColorToSkColor(aStops[i].color, 1.0);
88 mPositions[i + shift] = SkFloatToScalar(aStops[i].offset);
90 if (aStops[aNumStops - 1].offset != 1) {
91 mColors[mCount - 1] = ColorToSkColor(aStops[aNumStops - 1].color, 1.0);
92 mPositions[mCount - 1] = SK_Scalar1;
96 BackendType GetBackendType() const override { return BackendType::SKIA; }
98 std::vector<SkColor> mColors;
99 std::vector<SkScalar> mPositions;
100 int mCount;
101 ExtendMode mExtendMode;
105 * When constructing a temporary SkImage via GetSkImageForSurface, we may also
106 * have to construct a temporary DataSourceSurface, which must live as long as
107 * the SkImage. We attach this temporary surface to the image's pixelref, so
108 * that it can be released once the pixelref is freed.
110 static void ReleaseTemporarySurface(const void* aPixels, void* aContext) {
111 DataSourceSurface* surf = static_cast<DataSourceSurface*>(aContext);
112 if (surf) {
113 surf->Release();
117 static void ReleaseTemporaryMappedSurface(const void* aPixels, void* aContext) {
118 DataSourceSurface* surf = static_cast<DataSourceSurface*>(aContext);
119 if (surf) {
120 surf->Unmap();
121 surf->Release();
125 static void WriteRGBXFormat(uint8_t* aData, const IntSize& aSize,
126 const int32_t aStride, SurfaceFormat aFormat) {
127 if (aFormat != SurfaceFormat::B8G8R8X8 || aSize.IsEmpty()) {
128 return;
131 SwizzleData(aData, aStride, SurfaceFormat::X8R8G8B8_UINT32, aData, aStride,
132 SurfaceFormat::A8R8G8B8_UINT32, aSize);
135 #ifdef DEBUG
136 static IntRect CalculateSurfaceBounds(const IntSize& aSize, const Rect* aBounds,
137 const Matrix* aMatrix) {
138 IntRect surfaceBounds(IntPoint(0, 0), aSize);
139 if (!aBounds) {
140 return surfaceBounds;
143 MOZ_ASSERT(aMatrix);
144 Matrix inverse(*aMatrix);
145 if (!inverse.Invert()) {
146 return surfaceBounds;
149 IntRect bounds;
150 Rect sampledBounds = inverse.TransformBounds(*aBounds);
151 if (!sampledBounds.ToIntRect(&bounds)) {
152 return surfaceBounds;
155 return surfaceBounds.Intersect(bounds);
158 static const int kARGBAlphaOffset =
159 SurfaceFormat::A8R8G8B8_UINT32 == SurfaceFormat::B8G8R8A8 ? 3 : 0;
161 static bool VerifyRGBXFormat(uint8_t* aData, const IntSize& aSize,
162 const int32_t aStride, SurfaceFormat aFormat) {
163 if (aFormat != SurfaceFormat::B8G8R8X8 || aSize.IsEmpty()) {
164 return true;
166 // We should've initialized the data to be opaque already
167 // On debug builds, verify that this is actually true.
168 int height = aSize.height;
169 int width = aSize.width * 4;
171 for (int row = 0; row < height; ++row) {
172 for (int column = 0; column < width; column += 4) {
173 if (aData[column + kARGBAlphaOffset] != 0xFF) {
174 gfxCriticalError() << "RGBX pixel at (" << column << "," << row
175 << ") in " << width << "x" << height
176 << " surface is not opaque: " << int(aData[column])
177 << "," << int(aData[column + 1]) << ","
178 << int(aData[column + 2]) << ","
179 << int(aData[column + 3]);
182 aData += aStride;
185 return true;
188 // Since checking every pixel is expensive, this only checks the four corners
189 // and center of a surface that their alpha value is 0xFF.
190 static bool VerifyRGBXCorners(uint8_t* aData, const IntSize& aSize,
191 const int32_t aStride, SurfaceFormat aFormat,
192 const Rect* aBounds = nullptr,
193 const Matrix* aMatrix = nullptr) {
194 if (aFormat != SurfaceFormat::B8G8R8X8 || aSize.IsEmpty()) {
195 return true;
198 IntRect bounds = CalculateSurfaceBounds(aSize, aBounds, aMatrix);
199 if (bounds.IsEmpty()) {
200 return true;
203 const int height = bounds.Height();
204 const int width = bounds.Width();
205 const int pixelSize = 4;
206 MOZ_ASSERT(aSize.width * pixelSize <= aStride);
208 const int translation = bounds.Y() * aStride + bounds.X() * pixelSize;
209 const int topLeft = translation;
210 const int topRight = topLeft + (width - 1) * pixelSize;
211 const int bottomLeft = translation + (height - 1) * aStride;
212 const int bottomRight = bottomLeft + (width - 1) * pixelSize;
214 // Lastly the center pixel
215 const int middleRowHeight = height / 2;
216 const int middleRowWidth = (width / 2) * pixelSize;
217 const int middle = translation + aStride * middleRowHeight + middleRowWidth;
219 const int offsets[] = {topLeft, topRight, bottomRight, bottomLeft, middle};
220 for (int offset : offsets) {
221 if (aData[offset + kARGBAlphaOffset] != 0xFF) {
222 int row = offset / aStride;
223 int column = (offset % aStride) / pixelSize;
224 gfxCriticalError() << "RGBX corner pixel at (" << column << "," << row
225 << ") in " << aSize.width << "x" << aSize.height
226 << " surface, bounded by "
227 << "(" << bounds.X() << "," << bounds.Y() << ","
228 << width << "," << height
229 << ") is not opaque: " << int(aData[offset]) << ","
230 << int(aData[offset + 1]) << ","
231 << int(aData[offset + 2]) << ","
232 << int(aData[offset + 3]);
236 return true;
238 #endif
240 static sk_sp<SkImage> GetSkImageForSurface(SourceSurface* aSurface,
241 Maybe<MutexAutoLock>* aLock,
242 const Rect* aBounds = nullptr,
243 const Matrix* aMatrix = nullptr) {
244 if (!aSurface) {
245 gfxDebug() << "Creating null Skia image from null SourceSurface";
246 return nullptr;
249 if (aSurface->GetType() == SurfaceType::SKIA) {
250 return static_cast<SourceSurfaceSkia*>(aSurface)->GetImage(aLock);
253 RefPtr<DataSourceSurface> dataSurface = aSurface->GetDataSurface();
254 if (!dataSurface) {
255 gfxWarning() << "Failed getting DataSourceSurface for Skia image";
256 return nullptr;
259 DataSourceSurface::MappedSurface map;
260 SkImage::RasterReleaseProc releaseProc;
261 if (dataSurface->GetType() == SurfaceType::DATA_SHARED_WRAPPER) {
262 // Technically all surfaces should be mapped and unmapped explicitly but it
263 // appears SourceSurfaceSkia and DataSourceSurfaceWrapper have issues with
264 // this. For now, we just map SourceSurfaceSharedDataWrapper to ensure we
265 // don't unmap the data during the transaction (for blob images).
266 if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
267 gfxWarning() << "Failed mapping DataSourceSurface for Skia image";
268 return nullptr;
270 releaseProc = ReleaseTemporaryMappedSurface;
271 } else {
272 map.mData = dataSurface->GetData();
273 map.mStride = dataSurface->Stride();
274 releaseProc = ReleaseTemporarySurface;
277 DataSourceSurface* surf = dataSurface.forget().take();
279 // Skia doesn't support RGBX surfaces so ensure that the alpha value is opaque
280 // white.
281 MOZ_ASSERT(VerifyRGBXCorners(map.mData, surf->GetSize(), map.mStride,
282 surf->GetFormat(), aBounds, aMatrix));
284 SkPixmap pixmap(MakeSkiaImageInfo(surf->GetSize(), surf->GetFormat()),
285 map.mData, map.mStride);
286 sk_sp<SkImage> image = SkImage::MakeFromRaster(pixmap, releaseProc, surf);
287 if (!image) {
288 releaseProc(map.mData, surf);
289 gfxDebug() << "Failed making Skia raster image for temporary surface";
292 return image;
295 DrawTargetSkia::DrawTargetSkia()
296 : mCanvas(nullptr),
297 mSnapshot(nullptr),
298 mSnapshotLock{"DrawTargetSkia::mSnapshotLock"}
299 #ifdef MOZ_WIDGET_COCOA
301 mCG(nullptr),
302 mColorSpace(nullptr),
303 mCanvasData(nullptr),
304 mCGSize(0, 0),
305 mNeedLayer(false)
306 #endif
310 DrawTargetSkia::~DrawTargetSkia() {
311 if (mSnapshot) {
312 MutexAutoLock lock(mSnapshotLock);
313 // We're going to go away, hand our SkSurface to the SourceSurface.
314 mSnapshot->GiveSurface(mSurface.forget().take());
317 #ifdef MOZ_WIDGET_COCOA
318 if (mCG) {
319 CGContextRelease(mCG);
320 mCG = nullptr;
323 if (mColorSpace) {
324 CGColorSpaceRelease(mColorSpace);
325 mColorSpace = nullptr;
327 #endif
330 already_AddRefed<SourceSurface> DrawTargetSkia::Snapshot(
331 SurfaceFormat aFormat) {
332 // Without this lock, this could cause us to get out a snapshot and race with
333 // Snapshot::~Snapshot() actually destroying itself.
334 MutexAutoLock lock(mSnapshotLock);
335 if (mSnapshot && aFormat != mSnapshot->GetFormat()) {
336 if (!mSnapshot->hasOneRef()) {
337 mSnapshot->DrawTargetWillChange();
339 mSnapshot = nullptr;
341 RefPtr<SourceSurfaceSkia> snapshot = mSnapshot;
342 if (mSurface && !snapshot) {
343 snapshot = new SourceSurfaceSkia();
344 sk_sp<SkImage> image;
345 // If the surface is raster, making a snapshot may trigger a pixel copy.
346 // Instead, try to directly make a raster image referencing the surface
347 // pixels.
348 SkPixmap pixmap;
349 if (mSurface->peekPixels(&pixmap)) {
350 image = SkImage::MakeFromRaster(pixmap, nullptr, nullptr);
351 } else {
352 image = mSurface->makeImageSnapshot();
354 if (!snapshot->InitFromImage(image, aFormat, this)) {
355 return nullptr;
357 mSnapshot = snapshot;
360 return snapshot.forget();
363 already_AddRefed<SourceSurface> DrawTargetSkia::GetBackingSurface() {
364 if (mBackingSurface) {
365 RefPtr<SourceSurface> snapshot = mBackingSurface;
366 return snapshot.forget();
368 return Snapshot();
371 bool DrawTargetSkia::LockBits(uint8_t** aData, IntSize* aSize, int32_t* aStride,
372 SurfaceFormat* aFormat, IntPoint* aOrigin) {
373 SkImageInfo info;
374 size_t rowBytes;
375 SkIPoint origin;
376 void* pixels = mCanvas->accessTopLayerPixels(&info, &rowBytes, &origin);
377 if (!pixels ||
378 // Ensure the layer is at the origin if required.
379 (!aOrigin && !origin.isZero())) {
380 return false;
383 MarkChanged();
385 *aData = reinterpret_cast<uint8_t*>(pixels);
386 *aSize = IntSize(info.width(), info.height());
387 *aStride = int32_t(rowBytes);
388 *aFormat = SkiaColorTypeToGfxFormat(info.colorType(), info.alphaType());
389 if (aOrigin) {
390 *aOrigin = IntPoint(origin.x(), origin.y());
392 return true;
395 void DrawTargetSkia::ReleaseBits(uint8_t* aData) {}
397 static void ReleaseImage(const void* aPixels, void* aContext) {
398 SkImage* image = static_cast<SkImage*>(aContext);
399 SkSafeUnref(image);
402 static sk_sp<SkImage> ExtractSubset(sk_sp<SkImage> aImage,
403 const IntRect& aRect) {
404 SkIRect subsetRect = IntRectToSkIRect(aRect);
405 if (aImage->bounds() == subsetRect) {
406 return aImage;
408 // makeSubset is slow, so prefer to use SkPixmap::extractSubset where
409 // possible.
410 SkPixmap pixmap, subsetPixmap;
411 if (aImage->peekPixels(&pixmap) &&
412 pixmap.extractSubset(&subsetPixmap, subsetRect)) {
413 // Release the original image reference so only the subset image keeps it
414 // alive.
415 return SkImage::MakeFromRaster(subsetPixmap, ReleaseImage,
416 aImage.release());
418 return aImage->makeSubset(subsetRect);
421 static void FreeAlphaPixels(void* aBuf, void*) { sk_free(aBuf); }
423 static bool ExtractAlphaBitmap(const sk_sp<SkImage>& aImage,
424 SkBitmap* aResultBitmap,
425 bool aAllowReuse = false) {
426 SkPixmap pixmap;
427 if (aAllowReuse && aImage->isAlphaOnly() && aImage->peekPixels(&pixmap)) {
428 SkBitmap bitmap;
429 bitmap.installPixels(pixmap.info(), pixmap.writable_addr(),
430 pixmap.rowBytes());
431 *aResultBitmap = bitmap;
432 return true;
434 SkImageInfo info = SkImageInfo::MakeA8(aImage->width(), aImage->height());
435 // Skia does not fully allocate the last row according to stride.
436 // Since some of our algorithms (i.e. blur) depend on this, we must allocate
437 // the bitmap pixels manually.
438 size_t stride = GetAlignedStride<4>(info.width(), info.bytesPerPixel());
439 if (stride) {
440 CheckedInt<size_t> size = stride;
441 size *= info.height();
442 // We need to leave room for an additional 3 bytes for a potential overrun
443 // in our blurring code.
444 size += 3;
445 if (size.isValid()) {
446 void* buf = sk_malloc_flags(size.value(), 0);
447 if (buf) {
448 SkBitmap bitmap;
449 if (bitmap.installPixels(info, buf, stride, FreeAlphaPixels, nullptr) &&
450 aImage->readPixels(bitmap.info(), bitmap.getPixels(),
451 bitmap.rowBytes(), 0, 0)) {
452 *aResultBitmap = bitmap;
453 return true;
459 gfxWarning() << "Failed reading alpha pixels for Skia bitmap";
460 return false;
463 static void SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern,
464 Maybe<MutexAutoLock>& aLock, Float aAlpha = 1.0,
465 const SkMatrix* aMatrix = nullptr,
466 const Rect* aBounds = nullptr) {
467 switch (aPattern.GetType()) {
468 case PatternType::COLOR: {
469 DeviceColor color = static_cast<const ColorPattern&>(aPattern).mColor;
470 aPaint.setColor(ColorToSkColor(color, aAlpha));
471 break;
473 case PatternType::LINEAR_GRADIENT: {
474 const LinearGradientPattern& pat =
475 static_cast<const LinearGradientPattern&>(aPattern);
476 GradientStopsSkia* stops =
477 pat.mStops && pat.mStops->GetBackendType() == BackendType::SKIA
478 ? static_cast<GradientStopsSkia*>(pat.mStops.get())
479 : nullptr;
480 if (!stops || stops->mCount < 2 || !pat.mBegin.IsFinite() ||
481 !pat.mEnd.IsFinite() || pat.mBegin == pat.mEnd) {
482 aPaint.setColor(SK_ColorTRANSPARENT);
483 } else {
484 SkTileMode mode = ExtendModeToTileMode(stops->mExtendMode, Axis::BOTH);
485 SkPoint points[2];
486 points[0] = SkPoint::Make(SkFloatToScalar(pat.mBegin.x),
487 SkFloatToScalar(pat.mBegin.y));
488 points[1] = SkPoint::Make(SkFloatToScalar(pat.mEnd.x),
489 SkFloatToScalar(pat.mEnd.y));
491 SkMatrix mat;
492 GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
493 if (aMatrix) {
494 mat.postConcat(*aMatrix);
496 sk_sp<SkShader> shader = SkGradientShader::MakeLinear(
497 points, &stops->mColors.front(), &stops->mPositions.front(),
498 stops->mCount, mode, 0, &mat);
499 if (shader) {
500 aPaint.setShader(shader);
501 } else {
502 aPaint.setColor(SK_ColorTRANSPARENT);
505 break;
507 case PatternType::RADIAL_GRADIENT: {
508 const RadialGradientPattern& pat =
509 static_cast<const RadialGradientPattern&>(aPattern);
510 GradientStopsSkia* stops =
511 pat.mStops && pat.mStops->GetBackendType() == BackendType::SKIA
512 ? static_cast<GradientStopsSkia*>(pat.mStops.get())
513 : nullptr;
514 if (!stops || stops->mCount < 2 || !pat.mCenter1.IsFinite() ||
515 !std::isfinite(pat.mRadius1) || !pat.mCenter2.IsFinite() ||
516 !std::isfinite(pat.mRadius2) ||
517 (pat.mCenter1 == pat.mCenter2 && pat.mRadius1 == pat.mRadius2)) {
518 aPaint.setColor(SK_ColorTRANSPARENT);
519 } else {
520 SkTileMode mode = ExtendModeToTileMode(stops->mExtendMode, Axis::BOTH);
521 SkPoint points[2];
522 points[0] = SkPoint::Make(SkFloatToScalar(pat.mCenter1.x),
523 SkFloatToScalar(pat.mCenter1.y));
524 points[1] = SkPoint::Make(SkFloatToScalar(pat.mCenter2.x),
525 SkFloatToScalar(pat.mCenter2.y));
527 SkMatrix mat;
528 GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
529 if (aMatrix) {
530 mat.postConcat(*aMatrix);
532 sk_sp<SkShader> shader = SkGradientShader::MakeTwoPointConical(
533 points[0], SkFloatToScalar(pat.mRadius1), points[1],
534 SkFloatToScalar(pat.mRadius2), &stops->mColors.front(),
535 &stops->mPositions.front(), stops->mCount, mode, 0, &mat);
536 if (shader) {
537 aPaint.setShader(shader);
538 } else {
539 aPaint.setColor(SK_ColorTRANSPARENT);
542 break;
544 case PatternType::CONIC_GRADIENT: {
545 const ConicGradientPattern& pat =
546 static_cast<const ConicGradientPattern&>(aPattern);
547 GradientStopsSkia* stops =
548 pat.mStops && pat.mStops->GetBackendType() == BackendType::SKIA
549 ? static_cast<GradientStopsSkia*>(pat.mStops.get())
550 : nullptr;
551 if (!stops || stops->mCount < 2 || !pat.mCenter.IsFinite() ||
552 !std::isfinite(pat.mAngle)) {
553 aPaint.setColor(SK_ColorTRANSPARENT);
554 } else {
555 SkMatrix mat;
556 GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
557 if (aMatrix) {
558 mat.postConcat(*aMatrix);
561 SkScalar cx = SkFloatToScalar(pat.mCenter.x);
562 SkScalar cy = SkFloatToScalar(pat.mCenter.y);
564 // Skia's sweep gradient angles are relative to the x-axis, not the
565 // y-axis.
566 Float angle = (pat.mAngle * 180.0 / M_PI) - 90.0;
567 if (angle != 0.0) {
568 mat.preRotate(angle, cx, cy);
571 SkTileMode mode = ExtendModeToTileMode(stops->mExtendMode, Axis::BOTH);
572 sk_sp<SkShader> shader = SkGradientShader::MakeSweep(
573 cx, cy, &stops->mColors.front(), &stops->mPositions.front(),
574 stops->mCount, mode, 360 * pat.mStartOffset, 360 * pat.mEndOffset,
575 0, &mat);
577 if (shader) {
578 aPaint.setShader(shader);
579 } else {
580 aPaint.setColor(SK_ColorTRANSPARENT);
583 break;
585 case PatternType::SURFACE: {
586 const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern);
587 sk_sp<SkImage> image =
588 GetSkImageForSurface(pat.mSurface, &aLock, aBounds, &pat.mMatrix);
589 if (!image) {
590 aPaint.setColor(SK_ColorTRANSPARENT);
591 break;
594 SkMatrix mat;
595 GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
596 if (aMatrix) {
597 mat.postConcat(*aMatrix);
600 if (!pat.mSamplingRect.IsEmpty()) {
601 image = ExtractSubset(image, pat.mSamplingRect);
602 if (!image) {
603 aPaint.setColor(SK_ColorTRANSPARENT);
604 break;
606 mat.preTranslate(pat.mSamplingRect.X(), pat.mSamplingRect.Y());
609 SkTileMode xTile = ExtendModeToTileMode(pat.mExtendMode, Axis::X_AXIS);
610 SkTileMode yTile = ExtendModeToTileMode(pat.mExtendMode, Axis::Y_AXIS);
612 SkFilterMode filterMode = pat.mSamplingFilter == SamplingFilter::POINT
613 ? SkFilterMode::kNearest
614 : SkFilterMode::kLinear;
616 sk_sp<SkShader> shader =
617 image->makeShader(xTile, yTile, SkSamplingOptions(filterMode), mat);
618 if (shader) {
619 aPaint.setShader(shader);
620 } else {
621 gfxDebug() << "Failed creating Skia surface shader: x-tile="
622 << (int)xTile << " y-tile=" << (int)yTile
623 << " matrix=" << (mat.isFinite() ? "finite" : "non-finite");
624 aPaint.setColor(SK_ColorTRANSPARENT);
626 break;
631 static inline Rect GetClipBounds(SkCanvas* aCanvas) {
632 // Use a manually transformed getClipDeviceBounds instead of
633 // getClipBounds because getClipBounds inflates the the bounds
634 // by a pixel in each direction to compensate for antialiasing.
635 SkIRect deviceBounds;
636 if (!aCanvas->getDeviceClipBounds(&deviceBounds)) {
637 return Rect();
639 SkMatrix inverseCTM;
640 if (!aCanvas->getTotalMatrix().invert(&inverseCTM)) {
641 return Rect();
643 SkRect localBounds;
644 inverseCTM.mapRect(&localBounds, SkRect::Make(deviceBounds));
645 return SkRectToRect(localBounds);
648 struct AutoPaintSetup {
649 AutoPaintSetup(SkCanvas* aCanvas, const DrawOptions& aOptions,
650 const Pattern& aPattern, const Rect* aMaskBounds = nullptr,
651 const SkMatrix* aMatrix = nullptr,
652 const Rect* aSourceBounds = nullptr)
653 : mNeedsRestore(false), mAlpha(1.0) {
654 Init(aCanvas, aOptions, aMaskBounds, false);
655 SetPaintPattern(mPaint, aPattern, mLock, mAlpha, aMatrix, aSourceBounds);
658 AutoPaintSetup(SkCanvas* aCanvas, const DrawOptions& aOptions,
659 const Rect* aMaskBounds = nullptr, bool aForceGroup = false)
660 : mNeedsRestore(false), mAlpha(1.0) {
661 Init(aCanvas, aOptions, aMaskBounds, aForceGroup);
664 ~AutoPaintSetup() {
665 if (mNeedsRestore) {
666 mCanvas->restore();
670 void Init(SkCanvas* aCanvas, const DrawOptions& aOptions,
671 const Rect* aMaskBounds, bool aForceGroup) {
672 mPaint.setBlendMode(GfxOpToSkiaOp(aOptions.mCompositionOp));
673 mCanvas = aCanvas;
675 // TODO: Can we set greyscale somehow?
676 if (aOptions.mAntialiasMode != AntialiasMode::NONE) {
677 mPaint.setAntiAlias(true);
678 } else {
679 mPaint.setAntiAlias(false);
682 bool needsGroup =
683 aForceGroup ||
684 (!IsOperatorBoundByMask(aOptions.mCompositionOp) &&
685 (!aMaskBounds || !aMaskBounds->Contains(GetClipBounds(aCanvas))));
687 // TODO: We could skip the temporary for operator_source and just
688 // clear the clip rect. The other operators would be harder
689 // but could be worth it to skip pushing a group.
690 if (needsGroup) {
691 mPaint.setBlendMode(SkBlendMode::kSrcOver);
692 SkPaint temp;
693 temp.setBlendMode(GfxOpToSkiaOp(aOptions.mCompositionOp));
694 temp.setAlpha(ColorFloatToByte(aOptions.mAlpha));
695 // TODO: Get a rect here
696 SkCanvas::SaveLayerRec rec(nullptr, &temp,
697 SkCanvas::kPreserveLCDText_SaveLayerFlag);
698 mCanvas->saveLayer(rec);
699 mNeedsRestore = true;
700 } else {
701 mPaint.setAlpha(ColorFloatToByte(aOptions.mAlpha));
702 mAlpha = aOptions.mAlpha;
706 // TODO: Maybe add an operator overload to access this easier?
707 SkPaint mPaint;
708 bool mNeedsRestore;
709 SkCanvas* mCanvas;
710 Maybe<MutexAutoLock> mLock;
711 Float mAlpha;
714 void DrawTargetSkia::Flush() { mCanvas->flush(); }
716 void DrawTargetSkia::DrawSurface(SourceSurface* aSurface, const Rect& aDest,
717 const Rect& aSource,
718 const DrawSurfaceOptions& aSurfOptions,
719 const DrawOptions& aOptions) {
720 if (aSource.IsEmpty()) {
721 return;
724 MarkChanged();
726 Maybe<MutexAutoLock> lock;
727 sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
728 if (!image) {
729 return;
732 SkRect destRect = RectToSkRect(aDest);
733 SkRect sourceRect = RectToSkRect(aSource - aSurface->GetRect().TopLeft());
734 bool forceGroup =
735 image->isAlphaOnly() && aOptions.mCompositionOp != CompositionOp::OP_OVER;
737 AutoPaintSetup paint(mCanvas, aOptions, &aDest, forceGroup);
739 SkFilterMode filterMode =
740 aSurfOptions.mSamplingFilter == SamplingFilter::POINT
741 ? SkFilterMode::kNearest
742 : SkFilterMode::kLinear;
744 mCanvas->drawImageRect(image, sourceRect, destRect,
745 SkSamplingOptions(filterMode), &paint.mPaint,
746 SkCanvas::kStrict_SrcRectConstraint);
749 DrawTargetType DrawTargetSkia::GetType() const {
750 return DrawTargetType::SOFTWARE_RASTER;
753 void DrawTargetSkia::DrawFilter(FilterNode* aNode, const Rect& aSourceRect,
754 const Point& aDestPoint,
755 const DrawOptions& aOptions) {
756 if (!aNode || aNode->GetBackendType() != FILTER_BACKEND_SOFTWARE) {
757 return;
759 FilterNodeSoftware* filter = static_cast<FilterNodeSoftware*>(aNode);
760 filter->Draw(this, aSourceRect, aDestPoint, aOptions);
763 void DrawTargetSkia::DrawSurfaceWithShadow(SourceSurface* aSurface,
764 const Point& aDest,
765 const ShadowOptions& aShadow,
766 CompositionOp aOperator) {
767 if (aSurface->GetSize().IsEmpty()) {
768 return;
771 MarkChanged();
773 Maybe<MutexAutoLock> lock;
774 sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
775 if (!image) {
776 return;
779 mCanvas->save();
780 mCanvas->resetMatrix();
782 SkPaint paint;
783 paint.setBlendMode(GfxOpToSkiaOp(aOperator));
785 // bug 1201272
786 // We can't use the SkDropShadowImageFilter here because it applies the xfer
787 // mode first to render the bitmap to a temporary layer, and then implicitly
788 // uses src-over to composite the resulting shadow.
789 // The canvas spec, however, states that the composite op must be used to
790 // composite the resulting shadow, so we must instead use a SkBlurImageFilter
791 // to blur the image ourselves.
793 SkPaint shadowPaint;
794 shadowPaint.setBlendMode(GfxOpToSkiaOp(aOperator));
796 auto shadowDest = IntPoint::Round(aDest + aShadow.mOffset);
798 SkBitmap blurMask;
799 // Extract the alpha channel of the image into a bitmap. If the image is A8
800 // format already, then we can directly reuse the bitmap rather than create a
801 // new one as the surface only needs to be drawn from once.
802 if (ExtractAlphaBitmap(image, &blurMask, true)) {
803 // Prefer using our own box blur instead of Skia's. It currently performs
804 // much better than SkBlurImageFilter or SkBlurMaskFilter on the CPU.
805 AlphaBoxBlur blur(Rect(0, 0, blurMask.width(), blurMask.height()),
806 int32_t(blurMask.rowBytes()), aShadow.mSigma,
807 aShadow.mSigma);
808 blur.Blur(reinterpret_cast<uint8_t*>(blurMask.getPixels()));
809 blurMask.notifyPixelsChanged();
811 shadowPaint.setColor(ColorToSkColor(aShadow.mColor, 1.0f));
813 mCanvas->drawImage(blurMask.asImage(), shadowDest.x, shadowDest.y,
814 SkSamplingOptions(SkFilterMode::kLinear), &shadowPaint);
815 } else {
816 sk_sp<SkImageFilter> blurFilter(
817 SkImageFilters::Blur(aShadow.mSigma, aShadow.mSigma, nullptr));
818 sk_sp<SkColorFilter> colorFilter(SkColorFilters::Blend(
819 ColorToSkColor(aShadow.mColor, 1.0f), SkBlendMode::kSrcIn));
821 shadowPaint.setImageFilter(blurFilter);
822 shadowPaint.setColorFilter(colorFilter);
824 mCanvas->drawImage(image, shadowDest.x, shadowDest.y,
825 SkSamplingOptions(SkFilterMode::kLinear), &shadowPaint);
828 if (aSurface->GetFormat() != SurfaceFormat::A8) {
829 // Composite the original image after the shadow
830 auto dest = IntPoint::Round(aDest);
831 mCanvas->drawImage(image, dest.x, dest.y,
832 SkSamplingOptions(SkFilterMode::kLinear), &paint);
835 mCanvas->restore();
838 void DrawTargetSkia::FillRect(const Rect& aRect, const Pattern& aPattern,
839 const DrawOptions& aOptions) {
840 // The sprite blitting path in Skia can be faster than the shader blitter for
841 // operators other than source (or source-over with opaque surface). So, when
842 // possible/beneficial, route to DrawSurface which will use the sprite
843 // blitter.
844 if (aPattern.GetType() == PatternType::SURFACE &&
845 aOptions.mCompositionOp != CompositionOp::OP_SOURCE) {
846 const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern);
847 // Verify there is a valid surface and a pattern matrix without skew.
848 if (pat.mSurface &&
849 (aOptions.mCompositionOp != CompositionOp::OP_OVER ||
850 GfxFormatToSkiaAlphaType(pat.mSurface->GetFormat()) !=
851 kOpaque_SkAlphaType) &&
852 !pat.mMatrix.HasNonAxisAlignedTransform()) {
853 // Bound the sampling to smaller of the bounds or the sampling rect.
854 IntRect srcRect(IntPoint(0, 0), pat.mSurface->GetSize());
855 if (!pat.mSamplingRect.IsEmpty()) {
856 srcRect = srcRect.Intersect(pat.mSamplingRect);
858 // Transform the destination rectangle by the inverse of the pattern
859 // matrix so that it is in pattern space like the source rectangle.
860 Rect patRect = aRect - pat.mMatrix.GetTranslation();
861 patRect.Scale(1.0f / pat.mMatrix._11, 1.0f / pat.mMatrix._22);
862 // Verify the pattern rectangle will not tile or clamp.
863 if (!patRect.IsEmpty() && srcRect.Contains(RoundedOut(patRect))) {
864 // The pattern is a surface with an axis-aligned source rectangle
865 // fitting entirely in its bounds, so just treat it as a DrawSurface.
866 DrawSurface(pat.mSurface, aRect, patRect,
867 DrawSurfaceOptions(pat.mSamplingFilter), aOptions);
868 return;
873 MarkChanged();
874 SkRect rect = RectToSkRect(aRect);
875 AutoPaintSetup paint(mCanvas, aOptions, aPattern, &aRect, nullptr, &aRect);
877 mCanvas->drawRect(rect, paint.mPaint);
880 void DrawTargetSkia::Stroke(const Path* aPath, const Pattern& aPattern,
881 const StrokeOptions& aStrokeOptions,
882 const DrawOptions& aOptions) {
883 MarkChanged();
884 MOZ_ASSERT(aPath, "Null path");
885 if (aPath->GetBackendType() != BackendType::SKIA) {
886 return;
889 const PathSkia* skiaPath = static_cast<const PathSkia*>(aPath);
891 AutoPaintSetup paint(mCanvas, aOptions, aPattern);
892 if (!StrokeOptionsToPaint(paint.mPaint, aStrokeOptions)) {
893 return;
896 if (!skiaPath->GetPath().isFinite()) {
897 return;
900 mCanvas->drawPath(skiaPath->GetPath(), paint.mPaint);
903 static Double DashPeriodLength(const StrokeOptions& aStrokeOptions) {
904 Double length = 0;
905 for (size_t i = 0; i < aStrokeOptions.mDashLength; i++) {
906 length += aStrokeOptions.mDashPattern[i];
908 if (aStrokeOptions.mDashLength & 1) {
909 // "If an odd number of values is provided, then the list of values is
910 // repeated to yield an even number of values."
911 // Double the length.
912 length += length;
914 return length;
917 static inline Double RoundDownToMultiple(Double aValue, Double aFactor) {
918 return floor(aValue / aFactor) * aFactor;
921 static Rect UserSpaceStrokeClip(const IntRect& aDeviceClip,
922 const Matrix& aTransform,
923 const StrokeOptions& aStrokeOptions) {
924 Matrix inverse = aTransform;
925 if (!inverse.Invert()) {
926 return Rect();
928 Rect deviceClip(aDeviceClip);
929 deviceClip.Inflate(MaxStrokeExtents(aStrokeOptions, aTransform));
930 return inverse.TransformBounds(deviceClip);
933 static Rect ShrinkClippedStrokedRect(const Rect& aStrokedRect,
934 const IntRect& aDeviceClip,
935 const Matrix& aTransform,
936 const StrokeOptions& aStrokeOptions) {
937 Rect userSpaceStrokeClip =
938 UserSpaceStrokeClip(aDeviceClip, aTransform, aStrokeOptions);
939 RectDouble strokedRectDouble(aStrokedRect.X(), aStrokedRect.Y(),
940 aStrokedRect.Width(), aStrokedRect.Height());
941 RectDouble intersection = strokedRectDouble.Intersect(
942 RectDouble(userSpaceStrokeClip.X(), userSpaceStrokeClip.Y(),
943 userSpaceStrokeClip.Width(), userSpaceStrokeClip.Height()));
944 Double dashPeriodLength = DashPeriodLength(aStrokeOptions);
945 if (intersection.IsEmpty() || dashPeriodLength == 0.0f) {
946 return Rect(intersection.X(), intersection.Y(), intersection.Width(),
947 intersection.Height());
950 // Reduce the rectangle side lengths in multiples of the dash period length
951 // so that the visible dashes stay in the same place.
952 MarginDouble insetBy = strokedRectDouble - intersection;
953 insetBy.top = RoundDownToMultiple(insetBy.top, dashPeriodLength);
954 insetBy.right = RoundDownToMultiple(insetBy.right, dashPeriodLength);
955 insetBy.bottom = RoundDownToMultiple(insetBy.bottom, dashPeriodLength);
956 insetBy.left = RoundDownToMultiple(insetBy.left, dashPeriodLength);
958 strokedRectDouble.Deflate(insetBy);
959 return Rect(strokedRectDouble.X(), strokedRectDouble.Y(),
960 strokedRectDouble.Width(), strokedRectDouble.Height());
963 void DrawTargetSkia::StrokeRect(const Rect& aRect, const Pattern& aPattern,
964 const StrokeOptions& aStrokeOptions,
965 const DrawOptions& aOptions) {
966 // Stroking large rectangles with dashes is expensive with Skia (fixed
967 // overhead based on the number of dashes, regardless of whether the dashes
968 // are visible), so we try to reduce the size of the stroked rectangle as
969 // much as possible before passing it on to Skia.
970 Rect rect = aRect;
971 if (aStrokeOptions.mDashLength > 0 && !rect.IsEmpty()) {
972 IntRect deviceClip(IntPoint(0, 0), mSize);
973 SkIRect clipBounds;
974 if (mCanvas->getDeviceClipBounds(&clipBounds)) {
975 deviceClip = SkIRectToIntRect(clipBounds);
977 rect =
978 ShrinkClippedStrokedRect(rect, deviceClip, mTransform, aStrokeOptions);
979 if (rect.IsEmpty()) {
980 return;
984 MarkChanged();
985 AutoPaintSetup paint(mCanvas, aOptions, aPattern);
986 if (!StrokeOptionsToPaint(paint.mPaint, aStrokeOptions)) {
987 return;
990 mCanvas->drawRect(RectToSkRect(rect), paint.mPaint);
993 void DrawTargetSkia::StrokeLine(const Point& aStart, const Point& aEnd,
994 const Pattern& aPattern,
995 const StrokeOptions& aStrokeOptions,
996 const DrawOptions& aOptions) {
997 MarkChanged();
998 AutoPaintSetup paint(mCanvas, aOptions, aPattern);
999 if (!StrokeOptionsToPaint(paint.mPaint, aStrokeOptions)) {
1000 return;
1003 mCanvas->drawLine(SkFloatToScalar(aStart.x), SkFloatToScalar(aStart.y),
1004 SkFloatToScalar(aEnd.x), SkFloatToScalar(aEnd.y),
1005 paint.mPaint);
1008 void DrawTargetSkia::Fill(const Path* aPath, const Pattern& aPattern,
1009 const DrawOptions& aOptions) {
1010 MarkChanged();
1011 if (!aPath || aPath->GetBackendType() != BackendType::SKIA) {
1012 return;
1015 const PathSkia* skiaPath = static_cast<const PathSkia*>(aPath);
1017 AutoPaintSetup paint(mCanvas, aOptions, aPattern);
1019 if (!skiaPath->GetPath().isFinite()) {
1020 return;
1023 mCanvas->drawPath(skiaPath->GetPath(), paint.mPaint);
1026 #ifdef MOZ_WIDGET_COCOA
1027 static inline CGAffineTransform GfxMatrixToCGAffineTransform(const Matrix& m) {
1028 CGAffineTransform t;
1029 t.a = m._11;
1030 t.b = m._12;
1031 t.c = m._21;
1032 t.d = m._22;
1033 t.tx = m._31;
1034 t.ty = m._32;
1035 return t;
1038 /***
1039 * We have to do a lot of work to draw glyphs with CG because
1040 * CG assumes that the origin of rects are in the bottom left
1041 * while every other DrawTarget assumes the top left is the origin.
1042 * This means we have to transform the CGContext to have rects
1043 * actually be applied in top left fashion. We do this by:
1045 * 1) Translating the context up by the height of the canvas
1046 * 2) Flipping the context by the Y axis so it's upside down.
1048 * These two transforms put the origin in the top left.
1049 * Transforms are better understood thinking about them from right to left order
1050 * (mathematically).
1052 * Consider a point we want to draw at (0, 10) in normal cartesian planes with
1053 * a box of (100, 100). in CG terms, this would be at (0, 10).
1054 * Positive Y values point up.
1055 * In our DrawTarget terms, positive Y values point down, so (0, 10) would be
1056 * at (0, 90) in cartesian plane terms. That means our point at (0, 10) in
1057 * DrawTarget terms should end up at (0, 90). How does this work with the
1058 * current transforms?
1060 * Going right to left with the transforms, a CGPoint of (0, 10) has cartesian
1061 * coordinates of (0, 10). The first flip of the Y axis puts the point now at
1062 * (0, -10); Next, we translate the context up by the size of the canvas
1063 * (Positive Y values go up in CG coordinates but down in our draw target
1064 * coordinates). Since our canvas size is (100, 100), the resulting coordinate
1065 * becomes (0, 90), which is what we expect from our DrawTarget code. These two
1066 * transforms put the CG context equal to what every other DrawTarget expects.
1068 * Next, we need two more transforms for actual text. IF we left the transforms
1069 * as is, the text would be drawn upside down, so we need another flip of the Y
1070 * axis to draw the text right side up. However, with only the flip, the text
1071 * would be drawn in the wrong place. Thus we also have to invert the Y position
1072 * of the glyphs to get them in the right place.
1074 * Thus we have the following transforms:
1075 * 1) Translation of the context up
1076 * 2) Flipping the context around the Y axis
1077 * 3) Flipping the context around the Y axis
1078 * 4) Inverting the Y position of each glyph
1080 * We cannot cancel out (2) and (3) as we have to apply the clips and transforms
1081 * of DrawTargetSkia between (2) and (3).
1083 * Consider the example letter P, drawn at (0, 20) in CG coordinates in a
1084 * (100, 100) rect.
1085 * Again, going right to left of the transforms. We'd get:
1087 * 1) The letter P drawn at (0, -20) due to the inversion of the Y axis
1088 * 2) The letter P upside down (b) at (0, 20) due to the second flip
1089 * 3) The letter P right side up at (0, -20) due to the first flip
1090 * 4) The letter P right side up at (0, 80) due to the translation
1092 * tl;dr - CGRects assume origin is bottom left, DrawTarget rects assume top
1093 * left.
1095 static bool SetupCGContext(DrawTargetSkia* aDT, CGContextRef aCGContext,
1096 SkCanvas* aCanvas, const IntPoint& aOrigin,
1097 const IntSize& aSize, bool aClipped) {
1098 // DrawTarget expects the origin to be at the top left, but CG
1099 // expects it to be at the bottom left. Transform to set the origin to
1100 // the top left. Have to set this before we do anything else.
1101 // This is transform (1) up top
1102 CGContextTranslateCTM(aCGContext, -aOrigin.x, aOrigin.y + aSize.height);
1104 // Transform (2) from the comments.
1105 CGContextScaleCTM(aCGContext, 1, -1);
1107 // Want to apply clips BEFORE the transform since the transform
1108 // will apply to the clips we apply.
1109 if (aClipped) {
1110 SkRegion clipRegion;
1111 aCanvas->temporary_internal_getRgnClip(&clipRegion);
1112 Vector<CGRect, 8> rects;
1113 for (SkRegion::Iterator it(clipRegion); !it.done(); it.next()) {
1114 const SkIRect& rect = it.rect();
1115 if (!rects.append(
1116 CGRectMake(rect.x(), rect.y(), rect.width(), rect.height()))) {
1117 break;
1120 if (rects.length()) {
1121 CGContextClipToRects(aCGContext, rects.begin(), rects.length());
1125 CGContextConcatCTM(aCGContext,
1126 GfxMatrixToCGAffineTransform(aDT->GetTransform()));
1127 return true;
1129 // End long comment about transforms.
1131 // The context returned from this method will have the origin
1132 // in the top left and will have applied all the neccessary clips
1133 // and transforms to the CGContext. See the comment above
1134 // SetupCGContext.
1135 CGContextRef DrawTargetSkia::BorrowCGContext(const DrawOptions& aOptions) {
1136 // Since we can't replay Skia clips, we have to use a layer if we have a
1137 // complex clip. After saving a layer, the SkCanvas queries for needing a
1138 // layer change so save if we pushed a layer.
1139 mNeedLayer = !mCanvas->isClipEmpty() && !mCanvas->isClipRect();
1140 if (mNeedLayer) {
1141 SkPaint paint;
1142 paint.setBlendMode(SkBlendMode::kSrc);
1143 SkCanvas::SaveLayerRec rec(nullptr, &paint,
1144 SkCanvas::kInitWithPrevious_SaveLayerFlag);
1145 mCanvas->saveLayer(rec);
1148 uint8_t* data = nullptr;
1149 int32_t stride;
1150 SurfaceFormat format;
1151 IntSize size;
1152 IntPoint origin;
1153 if (!LockBits(&data, &size, &stride, &format, &origin)) {
1154 NS_WARNING("Could not lock skia bits to wrap CG around");
1155 return nullptr;
1158 if (!mNeedLayer && (data == mCanvasData) && mCG && (mCGSize == size)) {
1159 // If our canvas data still points to the same data,
1160 // we can reuse the CG Context
1161 CGContextSetAlpha(mCG, aOptions.mAlpha);
1162 CGContextSetShouldAntialias(mCG,
1163 aOptions.mAntialiasMode != AntialiasMode::NONE);
1164 CGContextSaveGState(mCG);
1165 SetupCGContext(this, mCG, mCanvas, origin, size, true);
1166 return mCG;
1169 if (!mColorSpace) {
1170 mColorSpace = (format == SurfaceFormat::A8) ? CGColorSpaceCreateDeviceGray()
1171 : CGColorSpaceCreateDeviceRGB();
1174 if (mCG) {
1175 // Release the old CG context since it's no longer valid.
1176 CGContextRelease(mCG);
1179 mCanvasData = data;
1180 mCGSize = size;
1182 uint32_t bitmapInfo =
1183 (format == SurfaceFormat::A8)
1184 ? kCGImageAlphaOnly
1185 : kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
1187 mCG = CGBitmapContextCreateWithData(
1188 mCanvasData, mCGSize.width, mCGSize.height, 8, /* bits per component */
1189 stride, mColorSpace, bitmapInfo, NULL, /* Callback when released */
1190 NULL);
1191 if (!mCG) {
1192 if (mNeedLayer) {
1193 mCanvas->restore();
1195 ReleaseBits(mCanvasData);
1196 NS_WARNING("Could not create bitmap around skia data\n");
1197 return nullptr;
1200 CGContextSetAlpha(mCG, aOptions.mAlpha);
1201 CGContextSetShouldAntialias(mCG,
1202 aOptions.mAntialiasMode != AntialiasMode::NONE);
1203 CGContextSetShouldSmoothFonts(mCG, true);
1204 CGContextSetTextDrawingMode(mCG, kCGTextFill);
1205 CGContextSaveGState(mCG);
1206 SetupCGContext(this, mCG, mCanvas, origin, size, !mNeedLayer);
1207 return mCG;
1210 void DrawTargetSkia::ReturnCGContext(CGContextRef aCGContext) {
1211 MOZ_ASSERT(aCGContext == mCG);
1212 ReleaseBits(mCanvasData);
1213 CGContextRestoreGState(aCGContext);
1215 if (mNeedLayer) {
1216 // A layer was used for clipping and is about to be popped by the restore.
1217 // Make sure the CG context referencing it is released first so the popped
1218 // layer doesn't accidentally get used.
1219 if (mCG) {
1220 CGContextRelease(mCG);
1221 mCG = nullptr;
1223 mCanvas->restore();
1227 CGContextRef BorrowedCGContext::BorrowCGContextFromDrawTarget(DrawTarget* aDT) {
1228 DrawTargetSkia* skiaDT = static_cast<DrawTargetSkia*>(aDT);
1229 return skiaDT->BorrowCGContext(DrawOptions());
1232 void BorrowedCGContext::ReturnCGContextToDrawTarget(DrawTarget* aDT,
1233 CGContextRef cg) {
1234 DrawTargetSkia* skiaDT = static_cast<DrawTargetSkia*>(aDT);
1235 skiaDT->ReturnCGContext(cg);
1237 #endif
1239 static bool CanDrawFont(ScaledFont* aFont) {
1240 switch (aFont->GetType()) {
1241 case FontType::FREETYPE:
1242 case FontType::FONTCONFIG:
1243 case FontType::MAC:
1244 case FontType::GDI:
1245 case FontType::DWRITE:
1246 return true;
1247 default:
1248 return false;
1252 void DrawTargetSkia::DrawGlyphs(ScaledFont* aFont, const GlyphBuffer& aBuffer,
1253 const Pattern& aPattern,
1254 const StrokeOptions* aStrokeOptions,
1255 const DrawOptions& aOptions) {
1256 if (!CanDrawFont(aFont)) {
1257 return;
1260 MarkChanged();
1262 ScaledFontBase* skiaFont = static_cast<ScaledFontBase*>(aFont);
1263 SkTypeface* typeface = skiaFont->GetSkTypeface();
1264 if (!typeface) {
1265 return;
1268 AutoPaintSetup paint(mCanvas, aOptions, aPattern);
1269 if (aStrokeOptions && !StrokeOptionsToPaint(paint.mPaint, *aStrokeOptions)) {
1270 return;
1273 AntialiasMode aaMode = aFont->GetDefaultAAMode();
1274 if (aOptions.mAntialiasMode != AntialiasMode::DEFAULT) {
1275 aaMode = aOptions.mAntialiasMode;
1277 bool aaEnabled = aaMode != AntialiasMode::NONE;
1278 paint.mPaint.setAntiAlias(aaEnabled);
1280 SkFont font(sk_ref_sp(typeface), SkFloatToScalar(skiaFont->mSize));
1282 bool useSubpixelAA =
1283 GetPermitSubpixelAA() &&
1284 (aaMode == AntialiasMode::DEFAULT || aaMode == AntialiasMode::SUBPIXEL);
1285 font.setEdging(useSubpixelAA ? SkFont::Edging::kSubpixelAntiAlias
1286 : (aaEnabled ? SkFont::Edging::kAntiAlias
1287 : SkFont::Edging::kAlias));
1289 skiaFont->SetupSkFontDrawOptions(font);
1291 // Limit the amount of internal batch allocations Skia does.
1292 const uint32_t kMaxGlyphBatchSize = 8192;
1294 for (uint32_t offset = 0; offset < aBuffer.mNumGlyphs;) {
1295 uint32_t batchSize =
1296 std::min(aBuffer.mNumGlyphs - offset, kMaxGlyphBatchSize);
1297 SkTextBlobBuilder builder;
1298 auto runBuffer = builder.allocRunPos(font, batchSize);
1299 for (uint32_t i = 0; i < batchSize; i++, offset++) {
1300 runBuffer.glyphs[i] = aBuffer.mGlyphs[offset].mIndex;
1301 runBuffer.points()[i] = PointToSkPoint(aBuffer.mGlyphs[offset].mPosition);
1304 sk_sp<SkTextBlob> text = builder.make();
1305 mCanvas->drawTextBlob(text, 0, 0, paint.mPaint);
1309 Maybe<Rect> DrawTargetSkia::GetGlyphLocalBounds(
1310 ScaledFont* aFont, const GlyphBuffer& aBuffer, const Pattern& aPattern,
1311 const StrokeOptions* aStrokeOptions, const DrawOptions& aOptions) {
1312 if (!CanDrawFont(aFont)) {
1313 return Nothing();
1316 ScaledFontBase* skiaFont = static_cast<ScaledFontBase*>(aFont);
1317 SkTypeface* typeface = skiaFont->GetSkTypeface();
1318 if (!typeface) {
1319 return Nothing();
1322 AutoPaintSetup paint(mCanvas, aOptions, aPattern);
1323 if (aStrokeOptions && !StrokeOptionsToPaint(paint.mPaint, *aStrokeOptions)) {
1324 return Nothing();
1327 AntialiasMode aaMode = aFont->GetDefaultAAMode();
1328 if (aOptions.mAntialiasMode != AntialiasMode::DEFAULT) {
1329 aaMode = aOptions.mAntialiasMode;
1331 bool aaEnabled = aaMode != AntialiasMode::NONE;
1332 paint.mPaint.setAntiAlias(aaEnabled);
1334 SkFont font(sk_ref_sp(typeface), SkFloatToScalar(skiaFont->mSize));
1336 bool useSubpixelAA =
1337 GetPermitSubpixelAA() &&
1338 (aaMode == AntialiasMode::DEFAULT || aaMode == AntialiasMode::SUBPIXEL);
1339 font.setEdging(useSubpixelAA ? SkFont::Edging::kSubpixelAntiAlias
1340 : (aaEnabled ? SkFont::Edging::kAntiAlias
1341 : SkFont::Edging::kAlias));
1343 skiaFont->SetupSkFontDrawOptions(font);
1345 // Limit the amount of internal batch allocations Skia does.
1346 const uint32_t kMaxGlyphBatchSize = 8192;
1348 // Avoid using TextBlobBuilder for bounds computations as the conservative
1349 // bounds can be wrong due to buggy font metrics. Instead, explicitly compute
1350 // tight bounds directly with the SkFont.
1351 Vector<SkGlyphID, 32> glyphs;
1352 Vector<SkRect, 32> rects;
1353 Rect bounds;
1354 for (uint32_t offset = 0; offset < aBuffer.mNumGlyphs;) {
1355 uint32_t batchSize =
1356 std::min(aBuffer.mNumGlyphs - offset, kMaxGlyphBatchSize);
1357 if (glyphs.resizeUninitialized(batchSize) &&
1358 rects.resizeUninitialized(batchSize)) {
1359 for (uint32_t i = 0; i < batchSize; i++) {
1360 glyphs[i] = aBuffer.mGlyphs[offset + i].mIndex;
1362 font.getBounds(glyphs.begin(), batchSize, rects.begin(), nullptr);
1363 for (uint32_t i = 0; i < batchSize; i++) {
1364 bounds = bounds.Union(SkRectToRect(rects[i]) +
1365 aBuffer.mGlyphs[offset + i].mPosition);
1368 offset += batchSize;
1371 SkRect storage;
1372 bounds = SkRectToRect(
1373 paint.mPaint.computeFastBounds(RectToSkRect(bounds), &storage));
1375 if (bounds.IsEmpty()) {
1376 return Nothing();
1379 return Some(bounds);
1382 void DrawTargetSkia::FillGlyphs(ScaledFont* aFont, const GlyphBuffer& aBuffer,
1383 const Pattern& aPattern,
1384 const DrawOptions& aOptions) {
1385 DrawGlyphs(aFont, aBuffer, aPattern, nullptr, aOptions);
1388 void DrawTargetSkia::StrokeGlyphs(ScaledFont* aFont, const GlyphBuffer& aBuffer,
1389 const Pattern& aPattern,
1390 const StrokeOptions& aStrokeOptions,
1391 const DrawOptions& aOptions) {
1392 DrawGlyphs(aFont, aBuffer, aPattern, &aStrokeOptions, aOptions);
1395 void DrawTargetSkia::Mask(const Pattern& aSource, const Pattern& aMask,
1396 const DrawOptions& aOptions) {
1397 Maybe<MutexAutoLock> lock;
1398 SkPaint maskPaint;
1399 SetPaintPattern(maskPaint, aMask, lock);
1401 sk_sp<SkShader> maskShader(maskPaint.getShader());
1402 if (!maskShader && maskPaint.getAlpha() != 0xFF) {
1403 if (maskPaint.getAlpha() == 0) {
1404 return;
1406 maskShader = SkShaders::Color(maskPaint.getColor());
1407 if (!maskShader) {
1408 gfxDebug() << "Failed creating Skia clip shader for Mask";
1409 return;
1413 MarkChanged();
1414 AutoPaintSetup paint(mCanvas, aOptions, aSource);
1416 mCanvas->save();
1417 if (maskShader) {
1418 mCanvas->clipShader(maskShader);
1421 mCanvas->drawPaint(paint.mPaint);
1423 mCanvas->restore();
1426 void DrawTargetSkia::MaskSurface(const Pattern& aSource, SourceSurface* aMask,
1427 Point aOffset, const DrawOptions& aOptions) {
1428 Maybe<MutexAutoLock> lock;
1429 sk_sp<SkImage> maskImage = GetSkImageForSurface(aMask, &lock);
1430 SkMatrix maskOffset = SkMatrix::Translate(
1431 PointToSkPoint(aOffset + Point(aMask->GetRect().TopLeft())));
1432 sk_sp<SkShader> maskShader = maskImage->makeShader(
1433 SkTileMode::kClamp, SkTileMode::kClamp,
1434 SkSamplingOptions(SkFilterMode::kLinear), maskOffset);
1435 if (!maskShader) {
1436 gfxDebug() << "Failed creating Skia clip shader for MaskSurface";
1437 return;
1440 MarkChanged();
1441 AutoPaintSetup paint(mCanvas, aOptions, aSource);
1443 mCanvas->save();
1444 mCanvas->clipShader(maskShader);
1446 mCanvas->drawRect(RectToSkRect(Rect(aMask->GetRect()) + aOffset),
1447 paint.mPaint);
1449 mCanvas->restore();
1452 bool DrawTarget::Draw3DTransformedSurface(SourceSurface* aSurface,
1453 const Matrix4x4& aMatrix) {
1454 // Composite the 3D transform with the DT's transform.
1455 Matrix4x4 fullMat = aMatrix * Matrix4x4::From2D(mTransform);
1456 if (fullMat.IsSingular()) {
1457 return false;
1459 // Transform the surface bounds and clip to this DT.
1460 IntRect xformBounds = RoundedOut(fullMat.TransformAndClipBounds(
1461 Rect(Point(0, 0), Size(aSurface->GetSize())),
1462 Rect(Point(0, 0), Size(GetSize()))));
1463 if (xformBounds.IsEmpty()) {
1464 return true;
1466 // Offset the matrix by the transformed origin.
1467 fullMat.PostTranslate(-xformBounds.X(), -xformBounds.Y(), 0);
1469 // Read in the source data.
1470 Maybe<MutexAutoLock> lock;
1471 sk_sp<SkImage> srcImage = GetSkImageForSurface(aSurface, &lock);
1472 if (!srcImage) {
1473 return true;
1476 // Set up an intermediate destination surface only the size of the transformed
1477 // bounds. Try to pass through the source's format unmodified in both the BGRA
1478 // and ARGB cases.
1479 RefPtr<DataSourceSurface> dstSurf = Factory::CreateDataSourceSurface(
1480 xformBounds.Size(),
1481 !srcImage->isOpaque() ? aSurface->GetFormat()
1482 : SurfaceFormat::A8R8G8B8_UINT32,
1483 true);
1484 if (!dstSurf) {
1485 return false;
1488 DataSourceSurface::ScopedMap map(dstSurf, DataSourceSurface::READ_WRITE);
1489 if (!map.IsMapped()) {
1490 return false;
1492 std::unique_ptr<SkCanvas> dstCanvas(SkCanvas::MakeRasterDirect(
1493 SkImageInfo::Make(xformBounds.Width(), xformBounds.Height(),
1494 GfxFormatToSkiaColorType(dstSurf->GetFormat()),
1495 kPremul_SkAlphaType),
1496 map.GetData(), map.GetStride()));
1497 if (!dstCanvas) {
1498 return false;
1501 // Do the transform.
1502 SkPaint paint;
1503 paint.setAntiAlias(true);
1504 paint.setBlendMode(SkBlendMode::kSrc);
1506 SkMatrix xform;
1507 GfxMatrixToSkiaMatrix(fullMat, xform);
1508 dstCanvas->setMatrix(xform);
1510 dstCanvas->drawImage(srcImage, 0, 0, SkSamplingOptions(SkFilterMode::kLinear),
1511 &paint);
1512 dstCanvas->flush();
1514 // Temporarily reset the DT's transform, since it has already been composed
1515 // above.
1516 Matrix origTransform = mTransform;
1517 SetTransform(Matrix());
1519 // Draw the transformed surface within the transformed bounds.
1520 DrawSurface(dstSurf, Rect(xformBounds),
1521 Rect(Point(0, 0), Size(xformBounds.Size())));
1523 SetTransform(origTransform);
1525 return true;
1528 bool DrawTargetSkia::Draw3DTransformedSurface(SourceSurface* aSurface,
1529 const Matrix4x4& aMatrix) {
1530 if (aMatrix.IsSingular()) {
1531 return false;
1534 MarkChanged();
1536 Maybe<MutexAutoLock> lock;
1537 sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
1538 if (!image) {
1539 return true;
1542 mCanvas->save();
1544 SkPaint paint;
1545 paint.setAntiAlias(true);
1547 SkMatrix xform;
1548 GfxMatrixToSkiaMatrix(aMatrix, xform);
1549 mCanvas->concat(xform);
1551 mCanvas->drawImage(image, 0, 0, SkSamplingOptions(SkFilterMode::kLinear),
1552 &paint);
1554 mCanvas->restore();
1556 return true;
1559 already_AddRefed<SourceSurface> DrawTargetSkia::CreateSourceSurfaceFromData(
1560 unsigned char* aData, const IntSize& aSize, int32_t aStride,
1561 SurfaceFormat aFormat) const {
1562 RefPtr<SourceSurfaceSkia> newSurf = new SourceSurfaceSkia();
1564 if (!newSurf->InitFromData(aData, aSize, aStride, aFormat)) {
1565 gfxDebug() << *this
1566 << ": Failure to create source surface from data. Size: "
1567 << aSize;
1568 return nullptr;
1571 return newSurf.forget();
1574 already_AddRefed<DrawTarget> DrawTargetSkia::CreateSimilarDrawTarget(
1575 const IntSize& aSize, SurfaceFormat aFormat) const {
1576 RefPtr<DrawTargetSkia> target = new DrawTargetSkia();
1577 #ifdef DEBUG
1578 if (!IsBackedByPixels(mCanvas)) {
1579 // If our canvas is backed by vector storage such as PDF then we want to
1580 // create a new DrawTarget with similar storage to avoid losing fidelity
1581 // (fidelity will be lost if the returned DT is Snapshot()'ed and drawn
1582 // back onto us since a raster will be drawn instead of vector commands).
1583 NS_WARNING("Not backed by pixels - we need to handle PDF backed SkCanvas");
1585 #endif
1587 if (!target->Init(aSize, aFormat)) {
1588 return nullptr;
1590 return target.forget();
1593 bool DrawTargetSkia::CanCreateSimilarDrawTarget(const IntSize& aSize,
1594 SurfaceFormat aFormat) const {
1595 auto minmaxPair = std::minmax(aSize.width, aSize.height);
1596 return minmaxPair.first > 0 &&
1597 size_t(minmaxPair.second) < GetMaxSurfaceSize();
1600 RefPtr<DrawTarget> DrawTargetSkia::CreateClippedDrawTarget(
1601 const Rect& aBounds, SurfaceFormat aFormat) {
1602 SkIRect clipBounds;
1604 RefPtr<DrawTarget> result;
1605 // Doing this save()/restore() dance is wasteful
1606 mCanvas->save();
1607 if (!aBounds.IsEmpty()) {
1608 mCanvas->clipRect(RectToSkRect(aBounds), SkClipOp::kIntersect, true);
1610 if (mCanvas->getDeviceClipBounds(&clipBounds)) {
1611 RefPtr<DrawTarget> dt = CreateSimilarDrawTarget(
1612 IntSize(clipBounds.width(), clipBounds.height()), aFormat);
1613 if (dt) {
1614 result = gfx::Factory::CreateOffsetDrawTarget(
1615 dt, IntPoint(clipBounds.x(), clipBounds.y()));
1616 if (result) {
1617 result->SetTransform(mTransform);
1620 } else {
1621 // Everything is clipped but we still want some kind of surface
1622 result = CreateSimilarDrawTarget(IntSize(1, 1), aFormat);
1624 mCanvas->restore();
1625 return result;
1628 already_AddRefed<SourceSurface>
1629 DrawTargetSkia::OptimizeSourceSurfaceForUnknownAlpha(
1630 SourceSurface* aSurface) const {
1631 if (aSurface->GetType() == SurfaceType::SKIA) {
1632 RefPtr<SourceSurface> surface(aSurface);
1633 return surface.forget();
1636 if (RefPtr<DataSourceSurface> dataSurface = aSurface->GetDataSurface()) {
1637 DataSourceSurface::ScopedMap map(dataSurface,
1638 DataSourceSurface::READ_WRITE);
1639 if (map.IsMapped()) {
1640 // For plugins, GDI can sometimes just write 0 to the alpha channel
1641 // even for RGBX formats. In this case, we have to manually write
1642 // the alpha channel to make Skia happy with RGBX and in case GDI
1643 // writes some bad data. Luckily, this only happens on plugins.
1644 WriteRGBXFormat(map.GetData(), dataSurface->GetSize(), map.GetStride(),
1645 dataSurface->GetFormat());
1646 return dataSurface.forget();
1650 return nullptr;
1653 already_AddRefed<SourceSurface> DrawTargetSkia::OptimizeSourceSurface(
1654 SourceSurface* aSurface) const {
1655 if (aSurface->GetType() == SurfaceType::SKIA) {
1656 RefPtr<SourceSurface> surface(aSurface);
1657 return surface.forget();
1660 // If we're not using skia-gl then drawing doesn't require any
1661 // uploading, so any data surface is fine. Call GetDataSurface
1662 // to trigger any required readback so that it only happens
1663 // once.
1664 if (RefPtr<DataSourceSurface> dataSurface = aSurface->GetDataSurface()) {
1665 #ifdef DEBUG
1666 DataSourceSurface::ScopedMap map(dataSurface, DataSourceSurface::READ);
1667 if (map.IsMapped()) {
1668 MOZ_ASSERT(VerifyRGBXFormat(map.GetData(), dataSurface->GetSize(),
1669 map.GetStride(), dataSurface->GetFormat()));
1671 #endif
1672 return dataSurface.forget();
1675 return nullptr;
1678 already_AddRefed<SourceSurface>
1679 DrawTargetSkia::CreateSourceSurfaceFromNativeSurface(
1680 const NativeSurface& aSurface) const {
1681 return nullptr;
1684 void DrawTargetSkia::CopySurface(SourceSurface* aSurface,
1685 const IntRect& aSourceRect,
1686 const IntPoint& aDestination) {
1687 MarkChanged();
1689 Maybe<MutexAutoLock> lock;
1690 sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
1691 if (!image) {
1692 return;
1695 SkPixmap srcPixmap;
1696 if (!image->peekPixels(&srcPixmap)) {
1697 return;
1700 // Ensure the source rect intersects the surface bounds.
1701 IntRect srcRect = aSourceRect.Intersect(SkIRectToIntRect(srcPixmap.bounds()));
1702 // Move the destination offset to match the altered source rect.
1703 IntPoint dstOffset =
1704 aDestination + (srcRect.TopLeft() - aSourceRect.TopLeft());
1705 // Then ensure the dest rect intersect the canvas bounds.
1706 IntRect dstRect = IntRect(dstOffset, srcRect.Size()).Intersect(GetRect());
1707 // Move the source rect to match the altered dest rect.
1708 srcRect += dstRect.TopLeft() - dstOffset;
1709 srcRect.SizeTo(dstRect.Size());
1711 if (!srcPixmap.extractSubset(&srcPixmap, IntRectToSkIRect(srcRect))) {
1712 return;
1715 mCanvas->writePixels(srcPixmap.info(), srcPixmap.addr(), srcPixmap.rowBytes(),
1716 dstRect.x, dstRect.y);
1719 static inline SkPixelGeometry GetSkPixelGeometry() {
1720 return Factory::GetBGRSubpixelOrder() ? kBGR_H_SkPixelGeometry
1721 : kRGB_H_SkPixelGeometry;
1724 template <typename T>
1725 [[nodiscard]] static already_AddRefed<T> AsRefPtr(sk_sp<T>&& aSkPtr) {
1726 return already_AddRefed<T>(aSkPtr.release());
1729 bool DrawTargetSkia::Init(const IntSize& aSize, SurfaceFormat aFormat) {
1730 if (size_t(std::max(aSize.width, aSize.height)) > GetMaxSurfaceSize()) {
1731 return false;
1734 // we need to have surfaces that have a stride aligned to 4 for interop with
1735 // cairo
1736 SkImageInfo info = MakeSkiaImageInfo(aSize, aFormat);
1737 size_t stride = GetAlignedStride<4>(info.width(), info.bytesPerPixel());
1738 if (!stride) {
1739 return false;
1741 SkSurfaceProps props(0, GetSkPixelGeometry());
1743 if (aFormat == SurfaceFormat::A8) {
1744 // Skia does not fully allocate the last row according to stride.
1745 // Since some of our algorithms (i.e. blur) depend on this, we must allocate
1746 // the bitmap pixels manually.
1747 CheckedInt<size_t> size = stride;
1748 size *= info.height();
1749 // We need to leave room for an additional 3 bytes for a potential overrun
1750 // in our blurring code.
1751 size += 3;
1752 if (!size.isValid()) {
1753 return false;
1755 void* buf = sk_malloc_flags(size.value(), SK_MALLOC_ZERO_INITIALIZE);
1756 if (!buf) {
1757 return false;
1759 mSurface = AsRefPtr(SkSurface::MakeRasterDirectReleaseProc(
1760 info, buf, stride, FreeAlphaPixels, nullptr, &props));
1761 } else {
1762 mSurface = AsRefPtr(SkSurface::MakeRaster(info, stride, &props));
1764 if (!mSurface) {
1765 return false;
1768 mSize = aSize;
1769 mFormat = aFormat;
1770 mCanvas = mSurface->getCanvas();
1771 SetPermitSubpixelAA(IsOpaque(mFormat));
1773 if (info.isOpaque()) {
1774 mCanvas->clear(SK_ColorBLACK);
1776 return true;
1779 bool DrawTargetSkia::Init(SkCanvas* aCanvas) {
1780 mCanvas = aCanvas;
1782 SkImageInfo imageInfo = mCanvas->imageInfo();
1784 // If the canvas is backed by pixels we clear it to be on the safe side. If
1785 // it's not (for example, for PDF output) we don't.
1786 if (IsBackedByPixels(mCanvas)) {
1787 SkColor clearColor =
1788 imageInfo.isOpaque() ? SK_ColorBLACK : SK_ColorTRANSPARENT;
1789 mCanvas->clear(clearColor);
1792 SkISize size = mCanvas->getBaseLayerSize();
1793 mSize.width = size.width();
1794 mSize.height = size.height();
1795 mFormat =
1796 SkiaColorTypeToGfxFormat(imageInfo.colorType(), imageInfo.alphaType());
1797 SetPermitSubpixelAA(IsOpaque(mFormat));
1798 return true;
1801 bool DrawTargetSkia::Init(unsigned char* aData, const IntSize& aSize,
1802 int32_t aStride, SurfaceFormat aFormat,
1803 bool aUninitialized) {
1804 MOZ_ASSERT((aFormat != SurfaceFormat::B8G8R8X8) || aUninitialized ||
1805 VerifyRGBXFormat(aData, aSize, aStride, aFormat));
1807 SkSurfaceProps props(0, GetSkPixelGeometry());
1808 mSurface = AsRefPtr(SkSurface::MakeRasterDirect(
1809 MakeSkiaImageInfo(aSize, aFormat), aData, aStride, &props));
1810 if (!mSurface) {
1811 return false;
1814 mSize = aSize;
1815 mFormat = aFormat;
1816 mCanvas = mSurface->getCanvas();
1817 SetPermitSubpixelAA(IsOpaque(mFormat));
1818 return true;
1821 bool DrawTargetSkia::Init(RefPtr<DataSourceSurface>&& aSurface) {
1822 auto map =
1823 new DataSourceSurface::ScopedMap(aSurface, DataSourceSurface::READ_WRITE);
1824 if (!map->IsMapped()) {
1825 delete map;
1826 return false;
1829 SurfaceFormat format = aSurface->GetFormat();
1830 IntSize size = aSurface->GetSize();
1831 MOZ_ASSERT((format != SurfaceFormat::B8G8R8X8) ||
1832 VerifyRGBXFormat(map->GetData(), size, map->GetStride(), format));
1834 SkSurfaceProps props(0, GetSkPixelGeometry());
1835 mSurface = AsRefPtr(SkSurface::MakeRasterDirectReleaseProc(
1836 MakeSkiaImageInfo(size, format), map->GetData(), map->GetStride(),
1837 DrawTargetSkia::ReleaseMappedSkSurface, map, &props));
1838 if (!mSurface) {
1839 delete map;
1840 return false;
1843 // map is now owned by mSurface
1844 mBackingSurface = std::move(aSurface);
1845 mSize = size;
1846 mFormat = format;
1847 mCanvas = mSurface->getCanvas();
1848 SetPermitSubpixelAA(IsOpaque(format));
1849 return true;
1852 /* static */ void DrawTargetSkia::ReleaseMappedSkSurface(void* aPixels,
1853 void* aContext) {
1854 auto map = reinterpret_cast<DataSourceSurface::ScopedMap*>(aContext);
1855 delete map;
1858 void DrawTargetSkia::SetTransform(const Matrix& aTransform) {
1859 SkMatrix mat;
1860 GfxMatrixToSkiaMatrix(aTransform, mat);
1861 mCanvas->setMatrix(mat);
1862 mTransform = aTransform;
1865 void* DrawTargetSkia::GetNativeSurface(NativeSurfaceType aType) {
1866 return nullptr;
1869 already_AddRefed<PathBuilder> DrawTargetSkia::CreatePathBuilder(
1870 FillRule aFillRule) const {
1871 return PathBuilderSkia::Create(aFillRule);
1874 void DrawTargetSkia::ClearRect(const Rect& aRect) {
1875 MarkChanged();
1876 mCanvas->save();
1877 // Restrict clearing to the clip region if requested
1878 mCanvas->clipRect(RectToSkRect(aRect), SkClipOp::kIntersect, true);
1879 SkColor clearColor = (mFormat == SurfaceFormat::B8G8R8X8)
1880 ? SK_ColorBLACK
1881 : SK_ColorTRANSPARENT;
1882 mCanvas->clear(clearColor);
1883 mCanvas->restore();
1886 void DrawTargetSkia::PushClip(const Path* aPath) {
1887 if (aPath->GetBackendType() != BackendType::SKIA) {
1888 return;
1891 const PathSkia* skiaPath = static_cast<const PathSkia*>(aPath);
1892 mCanvas->save();
1893 mCanvas->clipPath(skiaPath->GetPath(), SkClipOp::kIntersect, true);
1896 void DrawTargetSkia::PushDeviceSpaceClipRects(const IntRect* aRects,
1897 uint32_t aCount) {
1898 // Build a region by unioning all the rects together.
1899 SkRegion region;
1900 for (uint32_t i = 0; i < aCount; i++) {
1901 region.op(IntRectToSkIRect(aRects[i]), SkRegion::kUnion_Op);
1904 // Clip with the resulting region. clipRegion does not transform
1905 // this region by the current transform, unlike the other SkCanvas
1906 // clip methods, so it is just passed through in device-space.
1907 mCanvas->save();
1908 mCanvas->clipRegion(region, SkClipOp::kIntersect);
1911 void DrawTargetSkia::PushClipRect(const Rect& aRect) {
1912 SkRect rect = RectToSkRect(aRect);
1914 mCanvas->save();
1915 mCanvas->clipRect(rect, SkClipOp::kIntersect, true);
1918 void DrawTargetSkia::PopClip() {
1919 mCanvas->restore();
1920 SetTransform(GetTransform());
1923 bool DrawTargetSkia::RemoveAllClips() {
1924 mCanvas->restoreToCount(1);
1925 SetTransform(GetTransform());
1926 return true;
1929 // Get clip bounds in device space for the clipping region. By default, only
1930 // bounds for simple (empty or rect) regions are reported. If explicitly
1931 // allowed, the bounds will be reported for complex (all other) regions as well.
1932 Maybe<IntRect> DrawTargetSkia::GetDeviceClipRect(bool aAllowComplex) const {
1933 if (mCanvas->isClipEmpty()) {
1934 return Some(IntRect());
1936 if (aAllowComplex || mCanvas->isClipRect()) {
1937 SkIRect deviceBounds;
1938 if (mCanvas->getDeviceClipBounds(&deviceBounds)) {
1939 return Some(SkIRectToIntRect(deviceBounds));
1942 return Nothing();
1945 void DrawTargetSkia::PushLayer(bool aOpaque, Float aOpacity,
1946 SourceSurface* aMask,
1947 const Matrix& aMaskTransform,
1948 const IntRect& aBounds, bool aCopyBackground) {
1949 PushLayerWithBlend(aOpaque, aOpacity, aMask, aMaskTransform, aBounds,
1950 aCopyBackground, CompositionOp::OP_OVER);
1953 void DrawTargetSkia::PushLayerWithBlend(bool aOpaque, Float aOpacity,
1954 SourceSurface* aMask,
1955 const Matrix& aMaskTransform,
1956 const IntRect& aBounds,
1957 bool aCopyBackground,
1958 CompositionOp aCompositionOp) {
1959 SkPaint paint;
1961 paint.setAlpha(ColorFloatToByte(aOpacity));
1962 paint.setBlendMode(GfxOpToSkiaOp(aCompositionOp));
1964 // aBounds is supplied in device space, but SaveLayerRec wants local space.
1965 SkRect bounds = SkRect::MakeEmpty();
1966 if (!aBounds.IsEmpty()) {
1967 Matrix inverseTransform = mTransform;
1968 if (inverseTransform.Invert()) {
1969 bounds = RectToSkRect(inverseTransform.TransformBounds(Rect(aBounds)));
1973 // We don't pass a lock object to GetSkImageForSurface here, to force a
1974 // copy of the data if this is a copy-on-write snapshot. If we instead held
1975 // the lock until the corresponding PopLayer, we'd risk deadlocking if someone
1976 // tried to touch the originating DrawTarget while the layer was pushed.
1977 sk_sp<SkImage> clipImage = GetSkImageForSurface(aMask, nullptr);
1978 bool usedMask = false;
1979 if (bool(clipImage)) {
1980 Rect maskBounds(aMask->GetRect());
1981 sk_sp<SkShader> shader = clipImage->makeShader(
1982 SkTileMode::kClamp, SkTileMode::kClamp,
1983 SkSamplingOptions(SkFilterMode::kLinear),
1984 SkMatrix::Translate(PointToSkPoint(maskBounds.TopLeft())));
1985 if (shader) {
1986 usedMask = true;
1987 mCanvas->save();
1989 auto oldMatrix = mCanvas->getLocalToDevice();
1990 SkMatrix clipMatrix;
1991 GfxMatrixToSkiaMatrix(aMaskTransform, clipMatrix);
1992 mCanvas->concat(clipMatrix);
1994 mCanvas->clipRect(RectToSkRect(maskBounds));
1995 mCanvas->clipShader(shader);
1997 mCanvas->setMatrix(oldMatrix);
1998 } else {
1999 gfxDebug() << "Failed to create Skia clip shader for PushLayerWithBlend";
2003 PushedLayer layer(GetPermitSubpixelAA(), usedMask ? aMask : nullptr);
2004 mPushedLayers.push_back(layer);
2006 SkCanvas::SaveLayerRec saveRec(
2007 aBounds.IsEmpty() ? nullptr : &bounds, &paint, nullptr,
2008 SkCanvas::kPreserveLCDText_SaveLayerFlag |
2009 (aCopyBackground ? SkCanvas::kInitWithPrevious_SaveLayerFlag : 0));
2011 mCanvas->saveLayer(saveRec);
2013 SetPermitSubpixelAA(aOpaque);
2015 #ifdef MOZ_WIDGET_COCOA
2016 CGContextRelease(mCG);
2017 mCG = nullptr;
2018 #endif
2021 void DrawTargetSkia::PopLayer() {
2022 MOZ_RELEASE_ASSERT(!mPushedLayers.empty());
2024 MarkChanged();
2026 const PushedLayer& layer = mPushedLayers.back();
2028 mCanvas->restore();
2030 if (layer.mMask) {
2031 mCanvas->restore();
2034 SetTransform(GetTransform());
2035 SetPermitSubpixelAA(layer.mOldPermitSubpixelAA);
2037 mPushedLayers.pop_back();
2039 #ifdef MOZ_WIDGET_COCOA
2040 CGContextRelease(mCG);
2041 mCG = nullptr;
2042 #endif
2045 already_AddRefed<GradientStops> DrawTargetSkia::CreateGradientStops(
2046 GradientStop* aStops, uint32_t aNumStops, ExtendMode aExtendMode) const {
2047 std::vector<GradientStop> stops;
2048 stops.resize(aNumStops);
2049 for (uint32_t i = 0; i < aNumStops; i++) {
2050 stops[i] = aStops[i];
2052 std::stable_sort(stops.begin(), stops.end());
2054 return MakeAndAddRef<GradientStopsSkia>(stops, aNumStops, aExtendMode);
2057 already_AddRefed<FilterNode> DrawTargetSkia::CreateFilter(FilterType aType) {
2058 return FilterNodeSoftware::Create(aType);
2061 void DrawTargetSkia::MarkChanged() {
2062 // I'm not entirely certain whether this lock is needed, as multiple threads
2063 // should never modify the DrawTarget at the same time anyway, but this seems
2064 // like the safest.
2065 MutexAutoLock lock(mSnapshotLock);
2066 if (mSnapshot) {
2067 if (mSnapshot->hasOneRef()) {
2068 // No owners outside of this DrawTarget's own reference. Just dump it.
2069 mSnapshot = nullptr;
2070 return;
2073 mSnapshot->DrawTargetWillChange();
2074 mSnapshot = nullptr;
2076 // Handle copying of any image snapshots bound to the surface.
2077 if (mSurface) {
2078 mSurface->notifyContentWillChange(SkSurface::kRetain_ContentChangeMode);
2083 } // namespace mozilla::gfx