Update configs. IGNORE BROKEN CHANGESETS CLOSED TREE NO BUG a=release ba=release
[gecko.git] / gfx / 2d / DrawTargetSkia.cpp
blobbc6b6d0d343665c4c517fa62a41d223809804b73
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 "skia/src/core/SkEffectPriv.h"
28 #include "skia/src/core/SkRasterPipeline.h"
29 #include "skia/src/core/SkWriteBuffer.h"
30 #include "skia/src/shaders/SkEmptyShader.h"
31 #include "Blur.h"
32 #include "Logging.h"
33 #include "Tools.h"
34 #include "PathHelpers.h"
35 #include "PathSkia.h"
36 #include "Swizzle.h"
37 #include <algorithm>
38 #include <cmath>
40 #ifdef MOZ_WIDGET_COCOA
41 # include "BorrowedContext.h"
42 # include <ApplicationServices/ApplicationServices.h>
43 #endif
45 #ifdef XP_WIN
46 # include "ScaledFontDWrite.h"
47 #endif
49 namespace mozilla {
51 void RefPtrTraits<SkSurface>::Release(SkSurface* aSurface) {
52 SkSafeUnref(aSurface);
55 void RefPtrTraits<SkSurface>::AddRef(SkSurface* aSurface) {
56 SkSafeRef(aSurface);
59 } // namespace mozilla
61 namespace mozilla::gfx {
63 class GradientStopsSkia : public GradientStops {
64 public:
65 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsSkia, override)
67 GradientStopsSkia(const std::vector<GradientStop>& aStops, uint32_t aNumStops,
68 ExtendMode aExtendMode)
69 : mCount(aNumStops), mExtendMode(aExtendMode) {
70 if (mCount == 0) {
71 return;
74 // Skia gradients always require a stop at 0.0 and 1.0, insert these if
75 // we don't have them.
76 uint32_t shift = 0;
77 if (aStops[0].offset != 0) {
78 mCount++;
79 shift = 1;
81 if (aStops[aNumStops - 1].offset != 1) {
82 mCount++;
84 mColors.resize(mCount);
85 mPositions.resize(mCount);
86 if (aStops[0].offset != 0) {
87 mColors[0] = ColorToSkColor(aStops[0].color, 1.0);
88 mPositions[0] = 0;
90 for (uint32_t i = 0; i < aNumStops; i++) {
91 mColors[i + shift] = ColorToSkColor(aStops[i].color, 1.0);
92 mPositions[i + shift] = SkFloatToScalar(aStops[i].offset);
94 if (aStops[aNumStops - 1].offset != 1) {
95 mColors[mCount - 1] = ColorToSkColor(aStops[aNumStops - 1].color, 1.0);
96 mPositions[mCount - 1] = SK_Scalar1;
100 BackendType GetBackendType() const override { return BackendType::SKIA; }
102 std::vector<SkColor> mColors;
103 std::vector<SkScalar> mPositions;
104 int mCount;
105 ExtendMode mExtendMode;
109 * When constructing a temporary SkImage via GetSkImageForSurface, we may also
110 * have to construct a temporary DataSourceSurface, which must live as long as
111 * the SkImage. We attach this temporary surface to the image's pixelref, so
112 * that it can be released once the pixelref is freed.
114 static void ReleaseTemporarySurface(const void* aPixels, void* aContext) {
115 DataSourceSurface* surf = static_cast<DataSourceSurface*>(aContext);
116 if (surf) {
117 surf->Release();
121 static void ReleaseTemporaryMappedSurface(const void* aPixels, void* aContext) {
122 DataSourceSurface* surf = static_cast<DataSourceSurface*>(aContext);
123 if (surf) {
124 surf->Unmap();
125 surf->Release();
129 static void WriteRGBXFormat(uint8_t* aData, const IntSize& aSize,
130 const int32_t aStride, SurfaceFormat aFormat) {
131 if (aFormat != SurfaceFormat::B8G8R8X8 || aSize.IsEmpty()) {
132 return;
135 SwizzleData(aData, aStride, SurfaceFormat::X8R8G8B8_UINT32, aData, aStride,
136 SurfaceFormat::A8R8G8B8_UINT32, aSize);
139 #ifdef DEBUG
140 static IntRect CalculateSurfaceBounds(const IntSize& aSize, const Rect* aBounds,
141 const Matrix* aMatrix) {
142 IntRect surfaceBounds(IntPoint(0, 0), aSize);
143 if (!aBounds) {
144 return surfaceBounds;
147 MOZ_ASSERT(aMatrix);
148 Matrix inverse(*aMatrix);
149 if (!inverse.Invert()) {
150 return surfaceBounds;
153 IntRect bounds;
154 Rect sampledBounds = inverse.TransformBounds(*aBounds);
155 if (!sampledBounds.ToIntRect(&bounds)) {
156 return surfaceBounds;
159 return surfaceBounds.Intersect(bounds);
162 static const int kARGBAlphaOffset =
163 SurfaceFormat::A8R8G8B8_UINT32 == SurfaceFormat::B8G8R8A8 ? 3 : 0;
165 static bool VerifyRGBXFormat(uint8_t* aData, const IntSize& aSize,
166 const int32_t aStride, SurfaceFormat aFormat) {
167 if (aFormat != SurfaceFormat::B8G8R8X8 || aSize.IsEmpty()) {
168 return true;
170 // We should've initialized the data to be opaque already
171 // On debug builds, verify that this is actually true.
172 int height = aSize.height;
173 int width = aSize.width * 4;
175 for (int row = 0; row < height; ++row) {
176 for (int column = 0; column < width; column += 4) {
177 if (aData[column + kARGBAlphaOffset] != 0xFF) {
178 gfxCriticalError() << "RGBX pixel at (" << column << "," << row
179 << ") in " << width << "x" << height
180 << " surface is not opaque: " << int(aData[column])
181 << "," << int(aData[column + 1]) << ","
182 << int(aData[column + 2]) << ","
183 << int(aData[column + 3]);
186 aData += aStride;
189 return true;
192 // Since checking every pixel is expensive, this only checks the four corners
193 // and center of a surface that their alpha value is 0xFF.
194 static bool VerifyRGBXCorners(uint8_t* aData, const IntSize& aSize,
195 const int32_t aStride, SurfaceFormat aFormat,
196 const Rect* aBounds = nullptr,
197 const Matrix* aMatrix = nullptr) {
198 if (aFormat != SurfaceFormat::B8G8R8X8 || aSize.IsEmpty()) {
199 return true;
202 IntRect bounds = CalculateSurfaceBounds(aSize, aBounds, aMatrix);
203 if (bounds.IsEmpty()) {
204 return true;
207 const int height = bounds.Height();
208 const int width = bounds.Width();
209 const int pixelSize = 4;
210 MOZ_ASSERT(aSize.width * pixelSize <= aStride);
212 const int translation = bounds.Y() * aStride + bounds.X() * pixelSize;
213 const int topLeft = translation;
214 const int topRight = topLeft + (width - 1) * pixelSize;
215 const int bottomLeft = translation + (height - 1) * aStride;
216 const int bottomRight = bottomLeft + (width - 1) * pixelSize;
218 // Lastly the center pixel
219 const int middleRowHeight = height / 2;
220 const int middleRowWidth = (width / 2) * pixelSize;
221 const int middle = translation + aStride * middleRowHeight + middleRowWidth;
223 const int offsets[] = {topLeft, topRight, bottomRight, bottomLeft, middle};
224 for (int offset : offsets) {
225 if (aData[offset + kARGBAlphaOffset] != 0xFF) {
226 int row = offset / aStride;
227 int column = (offset % aStride) / pixelSize;
228 gfxCriticalError() << "RGBX corner pixel at (" << column << "," << row
229 << ") in " << aSize.width << "x" << aSize.height
230 << " surface, bounded by "
231 << "(" << bounds.X() << "," << bounds.Y() << ","
232 << width << "," << height
233 << ") is not opaque: " << int(aData[offset]) << ","
234 << int(aData[offset + 1]) << ","
235 << int(aData[offset + 2]) << ","
236 << int(aData[offset + 3]);
240 return true;
242 #endif
244 static sk_sp<SkImage> GetSkImageForSurface(SourceSurface* aSurface,
245 Maybe<MutexAutoLock>* aLock,
246 const Rect* aBounds = nullptr,
247 const Matrix* aMatrix = nullptr) {
248 if (!aSurface) {
249 gfxDebug() << "Creating null Skia image from null SourceSurface";
250 return nullptr;
253 if (aSurface->GetType() == SurfaceType::SKIA) {
254 return static_cast<SourceSurfaceSkia*>(aSurface)->GetImage(aLock);
257 RefPtr<DataSourceSurface> dataSurface = aSurface->GetDataSurface();
258 if (!dataSurface) {
259 gfxWarning() << "Failed getting DataSourceSurface for Skia image";
260 return nullptr;
263 DataSourceSurface::MappedSurface map;
264 void (*releaseProc)(const void*, void*);
265 if (dataSurface->GetType() == SurfaceType::DATA_SHARED_WRAPPER) {
266 // Technically all surfaces should be mapped and unmapped explicitly but it
267 // appears SourceSurfaceSkia and DataSourceSurfaceWrapper have issues with
268 // this. For now, we just map SourceSurfaceSharedDataWrapper to ensure we
269 // don't unmap the data during the transaction (for blob images).
270 if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
271 gfxWarning() << "Failed mapping DataSourceSurface for Skia image";
272 return nullptr;
274 releaseProc = ReleaseTemporaryMappedSurface;
275 } else {
276 map.mData = dataSurface->GetData();
277 map.mStride = dataSurface->Stride();
278 releaseProc = ReleaseTemporarySurface;
281 DataSourceSurface* surf = dataSurface.forget().take();
283 // Skia doesn't support RGBX surfaces so ensure that the alpha value is opaque
284 // white.
285 MOZ_ASSERT(VerifyRGBXCorners(map.mData, surf->GetSize(), map.mStride,
286 surf->GetFormat(), aBounds, aMatrix));
288 SkPixmap pixmap(MakeSkiaImageInfo(surf->GetSize(), surf->GetFormat()),
289 map.mData, map.mStride);
290 sk_sp<SkImage> image = SkImages::RasterFromPixmap(pixmap, releaseProc, surf);
291 if (!image) {
292 releaseProc(map.mData, surf);
293 gfxDebug() << "Failed making Skia raster image for temporary surface";
296 return image;
299 DrawTargetSkia::DrawTargetSkia()
300 : mCanvas(nullptr),
301 mSnapshot(nullptr),
302 mSnapshotLock{"DrawTargetSkia::mSnapshotLock"}
303 #ifdef MOZ_WIDGET_COCOA
305 mCG(nullptr),
306 mColorSpace(nullptr),
307 mCanvasData(nullptr),
308 mCGSize(0, 0),
309 mNeedLayer(false)
310 #endif
314 DrawTargetSkia::~DrawTargetSkia() {
315 if (mSnapshot) {
316 MutexAutoLock lock(mSnapshotLock);
317 // We're going to go away, hand our SkSurface to the SourceSurface.
318 mSnapshot->GiveSurface(mSurface.forget().take());
321 #ifdef MOZ_WIDGET_COCOA
322 if (mCG) {
323 CGContextRelease(mCG);
324 mCG = nullptr;
327 if (mColorSpace) {
328 CGColorSpaceRelease(mColorSpace);
329 mColorSpace = nullptr;
331 #endif
334 already_AddRefed<SourceSurface> DrawTargetSkia::Snapshot(
335 SurfaceFormat aFormat) {
336 // Without this lock, this could cause us to get out a snapshot and race with
337 // Snapshot::~Snapshot() actually destroying itself.
338 MutexAutoLock lock(mSnapshotLock);
339 if (mSnapshot && aFormat != mSnapshot->GetFormat()) {
340 if (!mSnapshot->hasOneRef()) {
341 mSnapshot->DrawTargetWillChange();
343 mSnapshot = nullptr;
345 RefPtr<SourceSurfaceSkia> snapshot = mSnapshot;
346 if (mSurface && !snapshot) {
347 snapshot = new SourceSurfaceSkia();
348 sk_sp<SkImage> image;
349 // If the surface is raster, making a snapshot may trigger a pixel copy.
350 // Instead, try to directly make a raster image referencing the surface
351 // pixels.
352 SkPixmap pixmap;
353 if (mSurface->peekPixels(&pixmap)) {
354 image = SkImages::RasterFromPixmap(pixmap, nullptr, nullptr);
355 } else {
356 image = mSurface->makeImageSnapshot();
358 if (!snapshot->InitFromImage(image, aFormat, this)) {
359 return nullptr;
361 mSnapshot = snapshot;
364 return snapshot.forget();
367 already_AddRefed<SourceSurface> DrawTargetSkia::GetBackingSurface() {
368 if (mBackingSurface) {
369 RefPtr<SourceSurface> snapshot = mBackingSurface;
370 return snapshot.forget();
372 return Snapshot();
375 bool DrawTargetSkia::LockBits(uint8_t** aData, IntSize* aSize, int32_t* aStride,
376 SurfaceFormat* aFormat, IntPoint* aOrigin) {
377 SkImageInfo info;
378 size_t rowBytes;
379 SkIPoint origin;
380 void* pixels = mCanvas->accessTopLayerPixels(&info, &rowBytes, &origin);
381 if (!pixels ||
382 // Ensure the layer is at the origin if required.
383 (!aOrigin && !origin.isZero())) {
384 return false;
387 MarkChanged();
389 *aData = reinterpret_cast<uint8_t*>(pixels);
390 *aSize = IntSize(info.width(), info.height());
391 *aStride = int32_t(rowBytes);
392 *aFormat = SkiaColorTypeToGfxFormat(info.colorType(), info.alphaType());
393 if (aOrigin) {
394 *aOrigin = IntPoint(origin.x(), origin.y());
396 return true;
399 void DrawTargetSkia::ReleaseBits(uint8_t* aData) {}
401 static void ReleaseImage(const void* aPixels, void* aContext) {
402 SkImage* image = static_cast<SkImage*>(aContext);
403 SkSafeUnref(image);
406 static sk_sp<SkImage> ExtractSubset(sk_sp<SkImage> aImage,
407 const IntRect& aRect) {
408 SkIRect subsetRect = IntRectToSkIRect(aRect);
409 if (aImage->bounds() == subsetRect) {
410 return aImage;
412 // makeSubset is slow, so prefer to use SkPixmap::extractSubset where
413 // possible.
414 SkPixmap pixmap, subsetPixmap;
415 if (aImage->peekPixels(&pixmap) &&
416 pixmap.extractSubset(&subsetPixmap, subsetRect)) {
417 // Release the original image reference so only the subset image keeps it
418 // alive.
419 return SkImages::RasterFromPixmap(subsetPixmap, ReleaseImage,
420 aImage.release());
422 return aImage->makeSubset(nullptr, subsetRect);
425 static void FreeAlphaPixels(void* aBuf, void*) { sk_free(aBuf); }
427 static bool ExtractAlphaBitmap(const sk_sp<SkImage>& aImage,
428 SkBitmap* aResultBitmap,
429 bool aAllowReuse = false) {
430 SkPixmap pixmap;
431 if (aAllowReuse && aImage->isAlphaOnly() && aImage->peekPixels(&pixmap)) {
432 SkBitmap bitmap;
433 bitmap.installPixels(pixmap.info(), pixmap.writable_addr(),
434 pixmap.rowBytes());
435 *aResultBitmap = bitmap;
436 return true;
438 SkImageInfo info = SkImageInfo::MakeA8(aImage->width(), aImage->height());
439 // Skia does not fully allocate the last row according to stride.
440 // Since some of our algorithms (i.e. blur) depend on this, we must allocate
441 // the bitmap pixels manually.
442 size_t stride = GetAlignedStride<4>(info.width(), info.bytesPerPixel());
443 if (stride) {
444 CheckedInt<size_t> size = stride;
445 size *= info.height();
446 // We need to leave room for an additional 3 bytes for a potential overrun
447 // in our blurring code.
448 size += 3;
449 if (size.isValid()) {
450 void* buf = sk_malloc_flags(size.value(), 0);
451 if (buf) {
452 SkBitmap bitmap;
453 if (bitmap.installPixels(info, buf, stride, FreeAlphaPixels, nullptr) &&
454 aImage->readPixels(bitmap.info(), bitmap.getPixels(),
455 bitmap.rowBytes(), 0, 0)) {
456 *aResultBitmap = bitmap;
457 return true;
463 gfxWarning() << "Failed reading alpha pixels for Skia bitmap";
464 return false;
467 static void SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern,
468 Maybe<MutexAutoLock>& aLock, Float aAlpha = 1.0,
469 const SkMatrix* aMatrix = nullptr,
470 const Rect* aBounds = nullptr) {
471 switch (aPattern.GetType()) {
472 case PatternType::COLOR: {
473 DeviceColor color = static_cast<const ColorPattern&>(aPattern).mColor;
474 aPaint.setColor(ColorToSkColor(color, aAlpha));
475 break;
477 case PatternType::LINEAR_GRADIENT: {
478 const LinearGradientPattern& pat =
479 static_cast<const LinearGradientPattern&>(aPattern);
480 GradientStopsSkia* stops =
481 pat.mStops && pat.mStops->GetBackendType() == BackendType::SKIA
482 ? static_cast<GradientStopsSkia*>(pat.mStops.get())
483 : nullptr;
484 if (!stops || stops->mCount < 2 || !pat.mBegin.IsFinite() ||
485 !pat.mEnd.IsFinite() || pat.mBegin == pat.mEnd) {
486 aPaint.setColor(SK_ColorTRANSPARENT);
487 } else {
488 SkTileMode mode = ExtendModeToTileMode(stops->mExtendMode, Axis::BOTH);
489 SkPoint points[2];
490 points[0] = SkPoint::Make(SkFloatToScalar(pat.mBegin.x),
491 SkFloatToScalar(pat.mBegin.y));
492 points[1] = SkPoint::Make(SkFloatToScalar(pat.mEnd.x),
493 SkFloatToScalar(pat.mEnd.y));
495 SkMatrix mat;
496 GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
497 if (aMatrix) {
498 mat.postConcat(*aMatrix);
500 sk_sp<SkShader> shader = SkGradientShader::MakeLinear(
501 points, &stops->mColors.front(), &stops->mPositions.front(),
502 stops->mCount, mode, 0, &mat);
503 if (shader) {
504 aPaint.setShader(shader);
505 } else {
506 aPaint.setColor(SK_ColorTRANSPARENT);
509 break;
511 case PatternType::RADIAL_GRADIENT: {
512 const RadialGradientPattern& pat =
513 static_cast<const RadialGradientPattern&>(aPattern);
514 GradientStopsSkia* stops =
515 pat.mStops && pat.mStops->GetBackendType() == BackendType::SKIA
516 ? static_cast<GradientStopsSkia*>(pat.mStops.get())
517 : nullptr;
518 if (!stops || stops->mCount < 2 || !pat.mCenter1.IsFinite() ||
519 !std::isfinite(pat.mRadius1) || !pat.mCenter2.IsFinite() ||
520 !std::isfinite(pat.mRadius2) ||
521 (pat.mCenter1 == pat.mCenter2 && pat.mRadius1 == pat.mRadius2)) {
522 aPaint.setColor(SK_ColorTRANSPARENT);
523 } else {
524 SkTileMode mode = ExtendModeToTileMode(stops->mExtendMode, Axis::BOTH);
525 SkPoint points[2];
526 points[0] = SkPoint::Make(SkFloatToScalar(pat.mCenter1.x),
527 SkFloatToScalar(pat.mCenter1.y));
528 points[1] = SkPoint::Make(SkFloatToScalar(pat.mCenter2.x),
529 SkFloatToScalar(pat.mCenter2.y));
531 SkMatrix mat;
532 GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
533 if (aMatrix) {
534 mat.postConcat(*aMatrix);
536 sk_sp<SkShader> shader = SkGradientShader::MakeTwoPointConical(
537 points[0], SkFloatToScalar(pat.mRadius1), points[1],
538 SkFloatToScalar(pat.mRadius2), &stops->mColors.front(),
539 &stops->mPositions.front(), stops->mCount, mode, 0, &mat);
540 if (shader) {
541 aPaint.setShader(shader);
542 } else {
543 aPaint.setColor(SK_ColorTRANSPARENT);
546 break;
548 case PatternType::CONIC_GRADIENT: {
549 const ConicGradientPattern& pat =
550 static_cast<const ConicGradientPattern&>(aPattern);
551 GradientStopsSkia* stops =
552 pat.mStops && pat.mStops->GetBackendType() == BackendType::SKIA
553 ? static_cast<GradientStopsSkia*>(pat.mStops.get())
554 : nullptr;
555 if (!stops || stops->mCount < 2 || !pat.mCenter.IsFinite() ||
556 !std::isfinite(pat.mAngle)) {
557 aPaint.setColor(SK_ColorTRANSPARENT);
558 } else {
559 SkMatrix mat;
560 GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
561 if (aMatrix) {
562 mat.postConcat(*aMatrix);
565 SkScalar cx = SkFloatToScalar(pat.mCenter.x);
566 SkScalar cy = SkFloatToScalar(pat.mCenter.y);
568 // Skia's sweep gradient angles are relative to the x-axis, not the
569 // y-axis.
570 Float angle = (pat.mAngle * 180.0 / M_PI) - 90.0;
571 if (angle != 0.0) {
572 mat.preRotate(angle, cx, cy);
575 SkTileMode mode = ExtendModeToTileMode(stops->mExtendMode, Axis::BOTH);
576 sk_sp<SkShader> shader = SkGradientShader::MakeSweep(
577 cx, cy, &stops->mColors.front(), &stops->mPositions.front(),
578 stops->mCount, mode, 360 * pat.mStartOffset, 360 * pat.mEndOffset,
579 0, &mat);
581 if (shader) {
582 aPaint.setShader(shader);
583 } else {
584 aPaint.setColor(SK_ColorTRANSPARENT);
587 break;
589 case PatternType::SURFACE: {
590 const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern);
591 sk_sp<SkImage> image =
592 GetSkImageForSurface(pat.mSurface, &aLock, aBounds, &pat.mMatrix);
593 if (!image) {
594 aPaint.setColor(SK_ColorTRANSPARENT);
595 break;
598 SkMatrix mat;
599 GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
600 if (aMatrix) {
601 mat.postConcat(*aMatrix);
604 if (!pat.mSamplingRect.IsEmpty()) {
605 image = ExtractSubset(image, pat.mSamplingRect);
606 if (!image) {
607 aPaint.setColor(SK_ColorTRANSPARENT);
608 break;
610 mat.preTranslate(pat.mSamplingRect.X(), pat.mSamplingRect.Y());
613 SkTileMode xTile = ExtendModeToTileMode(pat.mExtendMode, Axis::X_AXIS);
614 SkTileMode yTile = ExtendModeToTileMode(pat.mExtendMode, Axis::Y_AXIS);
616 SkFilterMode filterMode = pat.mSamplingFilter == SamplingFilter::POINT
617 ? SkFilterMode::kNearest
618 : SkFilterMode::kLinear;
620 sk_sp<SkShader> shader =
621 image->makeShader(xTile, yTile, SkSamplingOptions(filterMode), mat);
622 if (shader) {
623 aPaint.setShader(shader);
624 } else {
625 gfxDebug() << "Failed creating Skia surface shader: x-tile="
626 << (int)xTile << " y-tile=" << (int)yTile
627 << " matrix=" << (mat.isFinite() ? "finite" : "non-finite");
628 aPaint.setColor(SK_ColorTRANSPARENT);
630 break;
635 static inline Rect GetClipBounds(SkCanvas* aCanvas) {
636 // Use a manually transformed getClipDeviceBounds instead of
637 // getClipBounds because getClipBounds inflates the the bounds
638 // by a pixel in each direction to compensate for antialiasing.
639 SkIRect deviceBounds;
640 if (!aCanvas->getDeviceClipBounds(&deviceBounds)) {
641 return Rect();
643 SkMatrix inverseCTM;
644 if (!aCanvas->getTotalMatrix().invert(&inverseCTM)) {
645 return Rect();
647 SkRect localBounds;
648 inverseCTM.mapRect(&localBounds, SkRect::Make(deviceBounds));
649 return SkRectToRect(localBounds);
652 struct AutoPaintSetup {
653 AutoPaintSetup(SkCanvas* aCanvas, const DrawOptions& aOptions,
654 const Pattern& aPattern, const Rect* aMaskBounds = nullptr,
655 const SkMatrix* aMatrix = nullptr,
656 const Rect* aSourceBounds = nullptr)
657 : mNeedsRestore(false), mAlpha(1.0) {
658 Init(aCanvas, aOptions, aMaskBounds, false);
659 SetPaintPattern(mPaint, aPattern, mLock, mAlpha, aMatrix, aSourceBounds);
662 AutoPaintSetup(SkCanvas* aCanvas, const DrawOptions& aOptions,
663 const Rect* aMaskBounds = nullptr, bool aForceGroup = false)
664 : mNeedsRestore(false), mAlpha(1.0) {
665 Init(aCanvas, aOptions, aMaskBounds, aForceGroup);
668 ~AutoPaintSetup() {
669 if (mNeedsRestore) {
670 mCanvas->restore();
674 void Init(SkCanvas* aCanvas, const DrawOptions& aOptions,
675 const Rect* aMaskBounds, bool aForceGroup) {
676 mPaint.setBlendMode(GfxOpToSkiaOp(aOptions.mCompositionOp));
677 mCanvas = aCanvas;
679 // TODO: Can we set greyscale somehow?
680 if (aOptions.mAntialiasMode != AntialiasMode::NONE) {
681 mPaint.setAntiAlias(true);
682 } else {
683 mPaint.setAntiAlias(false);
686 bool needsGroup =
687 aForceGroup ||
688 (!IsOperatorBoundByMask(aOptions.mCompositionOp) &&
689 (!aMaskBounds || !aMaskBounds->Contains(GetClipBounds(aCanvas))));
691 // TODO: We could skip the temporary for operator_source and just
692 // clear the clip rect. The other operators would be harder
693 // but could be worth it to skip pushing a group.
694 if (needsGroup) {
695 mPaint.setBlendMode(SkBlendMode::kSrcOver);
696 SkPaint temp;
697 temp.setBlendMode(GfxOpToSkiaOp(aOptions.mCompositionOp));
698 temp.setAlpha(ColorFloatToByte(aOptions.mAlpha));
699 // TODO: Get a rect here
700 SkCanvas::SaveLayerRec rec(nullptr, &temp,
701 SkCanvas::kPreserveLCDText_SaveLayerFlag);
702 mCanvas->saveLayer(rec);
703 mNeedsRestore = true;
704 } else {
705 mPaint.setAlpha(ColorFloatToByte(aOptions.mAlpha));
706 mAlpha = aOptions.mAlpha;
710 // TODO: Maybe add an operator overload to access this easier?
711 SkPaint mPaint;
712 bool mNeedsRestore;
713 SkCanvas* mCanvas;
714 Maybe<MutexAutoLock> mLock;
715 Float mAlpha;
718 void DrawTargetSkia::Flush() {}
720 void DrawTargetSkia::DrawSurface(SourceSurface* aSurface, const Rect& aDest,
721 const Rect& aSource,
722 const DrawSurfaceOptions& aSurfOptions,
723 const DrawOptions& aOptions) {
724 if (aSource.IsEmpty()) {
725 return;
728 MarkChanged();
730 Maybe<MutexAutoLock> lock;
731 sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
732 if (!image) {
733 return;
736 SkRect destRect = RectToSkRect(aDest);
737 SkRect sourceRect = RectToSkRect(aSource - aSurface->GetRect().TopLeft());
738 bool forceGroup =
739 image->isAlphaOnly() && aOptions.mCompositionOp != CompositionOp::OP_OVER;
741 AutoPaintSetup paint(mCanvas, aOptions, &aDest, forceGroup);
743 SkFilterMode filterMode =
744 aSurfOptions.mSamplingFilter == SamplingFilter::POINT
745 ? SkFilterMode::kNearest
746 : SkFilterMode::kLinear;
748 mCanvas->drawImageRect(image, sourceRect, destRect,
749 SkSamplingOptions(filterMode), &paint.mPaint,
750 SkCanvas::kStrict_SrcRectConstraint);
753 DrawTargetType DrawTargetSkia::GetType() const {
754 return DrawTargetType::SOFTWARE_RASTER;
757 void DrawTargetSkia::DrawFilter(FilterNode* aNode, const Rect& aSourceRect,
758 const Point& aDestPoint,
759 const DrawOptions& aOptions) {
760 if (!aNode || aNode->GetBackendType() != FILTER_BACKEND_SOFTWARE) {
761 return;
763 FilterNodeSoftware* filter = static_cast<FilterNodeSoftware*>(aNode);
764 filter->Draw(this, aSourceRect, aDestPoint, aOptions);
767 void DrawTargetSkia::DrawSurfaceWithShadow(SourceSurface* aSurface,
768 const Point& aDest,
769 const ShadowOptions& aShadow,
770 CompositionOp aOperator) {
771 if (aSurface->GetSize().IsEmpty()) {
772 return;
775 MarkChanged();
777 Maybe<MutexAutoLock> lock;
778 sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
779 if (!image) {
780 return;
783 mCanvas->save();
784 mCanvas->resetMatrix();
786 SkPaint paint;
787 paint.setBlendMode(GfxOpToSkiaOp(aOperator));
789 // bug 1201272
790 // We can't use the SkDropShadowImageFilter here because it applies the xfer
791 // mode first to render the bitmap to a temporary layer, and then implicitly
792 // uses src-over to composite the resulting shadow.
793 // The canvas spec, however, states that the composite op must be used to
794 // composite the resulting shadow, so we must instead use a SkBlurImageFilter
795 // to blur the image ourselves.
797 SkPaint shadowPaint;
798 shadowPaint.setBlendMode(GfxOpToSkiaOp(aOperator));
800 auto shadowDest = IntPoint::Round(aDest + aShadow.mOffset);
802 SkBitmap blurMask;
803 // Extract the alpha channel of the image into a bitmap. If the image is A8
804 // format already, then we can directly reuse the bitmap rather than create a
805 // new one as the surface only needs to be drawn from once.
806 if (ExtractAlphaBitmap(image, &blurMask, true)) {
807 // Prefer using our own box blur instead of Skia's. It currently performs
808 // much better than SkBlurImageFilter or SkBlurMaskFilter on the CPU.
809 AlphaBoxBlur blur(Rect(0, 0, blurMask.width(), blurMask.height()),
810 int32_t(blurMask.rowBytes()), aShadow.mSigma,
811 aShadow.mSigma);
812 blur.Blur(reinterpret_cast<uint8_t*>(blurMask.getPixels()));
813 blurMask.notifyPixelsChanged();
815 shadowPaint.setColor(ColorToSkColor(aShadow.mColor, 1.0f));
817 mCanvas->drawImage(blurMask.asImage(), shadowDest.x, shadowDest.y,
818 SkSamplingOptions(SkFilterMode::kLinear), &shadowPaint);
819 } else {
820 sk_sp<SkImageFilter> blurFilter(
821 SkImageFilters::Blur(aShadow.mSigma, aShadow.mSigma, nullptr));
822 sk_sp<SkColorFilter> colorFilter(SkColorFilters::Blend(
823 ColorToSkColor(aShadow.mColor, 1.0f), SkBlendMode::kSrcIn));
825 shadowPaint.setImageFilter(blurFilter);
826 shadowPaint.setColorFilter(colorFilter);
828 mCanvas->drawImage(image, shadowDest.x, shadowDest.y,
829 SkSamplingOptions(SkFilterMode::kLinear), &shadowPaint);
832 if (aSurface->GetFormat() != SurfaceFormat::A8) {
833 // Composite the original image after the shadow
834 auto dest = IntPoint::Round(aDest);
835 mCanvas->drawImage(image, dest.x, dest.y,
836 SkSamplingOptions(SkFilterMode::kLinear), &paint);
839 mCanvas->restore();
842 void DrawTargetSkia::FillRect(const Rect& aRect, const Pattern& aPattern,
843 const DrawOptions& aOptions) {
844 // The sprite blitting path in Skia can be faster than the shader blitter for
845 // operators other than source (or source-over with opaque surface). So, when
846 // possible/beneficial, route to DrawSurface which will use the sprite
847 // blitter.
848 if (aPattern.GetType() == PatternType::SURFACE &&
849 aOptions.mCompositionOp != CompositionOp::OP_SOURCE) {
850 const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern);
851 // Verify there is a valid surface and a pattern matrix without skew.
852 if (pat.mSurface &&
853 (aOptions.mCompositionOp != CompositionOp::OP_OVER ||
854 GfxFormatToSkiaAlphaType(pat.mSurface->GetFormat()) !=
855 kOpaque_SkAlphaType) &&
856 !pat.mMatrix.HasNonAxisAlignedTransform()) {
857 // Bound the sampling to smaller of the bounds or the sampling rect.
858 IntRect srcRect(IntPoint(0, 0), pat.mSurface->GetSize());
859 if (!pat.mSamplingRect.IsEmpty()) {
860 srcRect = srcRect.Intersect(pat.mSamplingRect);
862 // Transform the destination rectangle by the inverse of the pattern
863 // matrix so that it is in pattern space like the source rectangle.
864 Rect patRect = aRect - pat.mMatrix.GetTranslation();
865 patRect.Scale(1.0f / pat.mMatrix._11, 1.0f / pat.mMatrix._22);
866 // Verify the pattern rectangle will not tile or clamp.
867 if (!patRect.IsEmpty() && srcRect.Contains(RoundedOut(patRect))) {
868 // The pattern is a surface with an axis-aligned source rectangle
869 // fitting entirely in its bounds, so just treat it as a DrawSurface.
870 DrawSurface(pat.mSurface, aRect, patRect,
871 DrawSurfaceOptions(pat.mSamplingFilter), aOptions);
872 return;
877 MarkChanged();
878 SkRect rect = RectToSkRect(aRect);
879 AutoPaintSetup paint(mCanvas, aOptions, aPattern, &aRect, nullptr, &aRect);
881 mCanvas->drawRect(rect, paint.mPaint);
884 void DrawTargetSkia::Stroke(const Path* aPath, const Pattern& aPattern,
885 const StrokeOptions& aStrokeOptions,
886 const DrawOptions& aOptions) {
887 MarkChanged();
888 MOZ_ASSERT(aPath, "Null path");
889 if (aPath->GetBackendType() != BackendType::SKIA) {
890 return;
893 const PathSkia* skiaPath = static_cast<const PathSkia*>(aPath);
895 AutoPaintSetup paint(mCanvas, aOptions, aPattern);
896 if (!StrokeOptionsToPaint(paint.mPaint, aStrokeOptions)) {
897 return;
900 if (!skiaPath->GetPath().isFinite()) {
901 return;
904 mCanvas->drawPath(skiaPath->GetPath(), paint.mPaint);
907 static Double DashPeriodLength(const StrokeOptions& aStrokeOptions) {
908 Double length = 0;
909 for (size_t i = 0; i < aStrokeOptions.mDashLength; i++) {
910 length += aStrokeOptions.mDashPattern[i];
912 if (aStrokeOptions.mDashLength & 1) {
913 // "If an odd number of values is provided, then the list of values is
914 // repeated to yield an even number of values."
915 // Double the length.
916 length += length;
918 return length;
921 static inline Double RoundDownToMultiple(Double aValue, Double aFactor) {
922 return floor(aValue / aFactor) * aFactor;
925 static Rect UserSpaceStrokeClip(const IntRect& aDeviceClip,
926 const Matrix& aTransform,
927 const StrokeOptions& aStrokeOptions) {
928 Matrix inverse = aTransform;
929 if (!inverse.Invert()) {
930 return Rect();
932 Rect deviceClip(aDeviceClip);
933 deviceClip.Inflate(MaxStrokeExtents(aStrokeOptions, aTransform));
934 return inverse.TransformBounds(deviceClip);
937 static Rect ShrinkClippedStrokedRect(const Rect& aStrokedRect,
938 const IntRect& aDeviceClip,
939 const Matrix& aTransform,
940 const StrokeOptions& aStrokeOptions) {
941 Rect userSpaceStrokeClip =
942 UserSpaceStrokeClip(aDeviceClip, aTransform, aStrokeOptions);
943 RectDouble strokedRectDouble(aStrokedRect.X(), aStrokedRect.Y(),
944 aStrokedRect.Width(), aStrokedRect.Height());
945 RectDouble intersection = strokedRectDouble.Intersect(
946 RectDouble(userSpaceStrokeClip.X(), userSpaceStrokeClip.Y(),
947 userSpaceStrokeClip.Width(), userSpaceStrokeClip.Height()));
948 Double dashPeriodLength = DashPeriodLength(aStrokeOptions);
949 if (intersection.IsEmpty() || dashPeriodLength == 0.0f) {
950 return Rect(intersection.X(), intersection.Y(), intersection.Width(),
951 intersection.Height());
954 // Reduce the rectangle side lengths in multiples of the dash period length
955 // so that the visible dashes stay in the same place.
956 MarginDouble insetBy = strokedRectDouble - intersection;
957 insetBy.top = RoundDownToMultiple(insetBy.top, dashPeriodLength);
958 insetBy.right = RoundDownToMultiple(insetBy.right, dashPeriodLength);
959 insetBy.bottom = RoundDownToMultiple(insetBy.bottom, dashPeriodLength);
960 insetBy.left = RoundDownToMultiple(insetBy.left, dashPeriodLength);
962 strokedRectDouble.Deflate(insetBy);
963 return Rect(strokedRectDouble.X(), strokedRectDouble.Y(),
964 strokedRectDouble.Width(), strokedRectDouble.Height());
967 void DrawTargetSkia::StrokeRect(const Rect& aRect, const Pattern& aPattern,
968 const StrokeOptions& aStrokeOptions,
969 const DrawOptions& aOptions) {
970 // Stroking large rectangles with dashes is expensive with Skia (fixed
971 // overhead based on the number of dashes, regardless of whether the dashes
972 // are visible), so we try to reduce the size of the stroked rectangle as
973 // much as possible before passing it on to Skia.
974 Rect rect = aRect;
975 if (aStrokeOptions.mDashLength > 0 && !rect.IsEmpty()) {
976 IntRect deviceClip(IntPoint(0, 0), mSize);
977 SkIRect clipBounds;
978 if (mCanvas->getDeviceClipBounds(&clipBounds)) {
979 deviceClip = SkIRectToIntRect(clipBounds);
981 rect =
982 ShrinkClippedStrokedRect(rect, deviceClip, mTransform, aStrokeOptions);
983 if (rect.IsEmpty()) {
984 return;
988 MarkChanged();
989 AutoPaintSetup paint(mCanvas, aOptions, aPattern);
990 if (!StrokeOptionsToPaint(paint.mPaint, aStrokeOptions)) {
991 return;
994 mCanvas->drawRect(RectToSkRect(rect), paint.mPaint);
997 void DrawTargetSkia::StrokeLine(const Point& aStart, const Point& aEnd,
998 const Pattern& aPattern,
999 const StrokeOptions& aStrokeOptions,
1000 const DrawOptions& aOptions) {
1001 MarkChanged();
1002 AutoPaintSetup paint(mCanvas, aOptions, aPattern);
1003 if (!StrokeOptionsToPaint(paint.mPaint, aStrokeOptions)) {
1004 return;
1007 mCanvas->drawLine(SkFloatToScalar(aStart.x), SkFloatToScalar(aStart.y),
1008 SkFloatToScalar(aEnd.x), SkFloatToScalar(aEnd.y),
1009 paint.mPaint);
1012 void DrawTargetSkia::Fill(const Path* aPath, const Pattern& aPattern,
1013 const DrawOptions& aOptions) {
1014 MarkChanged();
1015 if (!aPath || aPath->GetBackendType() != BackendType::SKIA) {
1016 return;
1019 const PathSkia* skiaPath = static_cast<const PathSkia*>(aPath);
1021 AutoPaintSetup paint(mCanvas, aOptions, aPattern);
1023 if (!skiaPath->GetPath().isFinite()) {
1024 return;
1027 mCanvas->drawPath(skiaPath->GetPath(), paint.mPaint);
1030 #ifdef MOZ_WIDGET_COCOA
1031 static inline CGAffineTransform GfxMatrixToCGAffineTransform(const Matrix& m) {
1032 CGAffineTransform t;
1033 t.a = m._11;
1034 t.b = m._12;
1035 t.c = m._21;
1036 t.d = m._22;
1037 t.tx = m._31;
1038 t.ty = m._32;
1039 return t;
1042 /***
1043 * We have to do a lot of work to draw glyphs with CG because
1044 * CG assumes that the origin of rects are in the bottom left
1045 * while every other DrawTarget assumes the top left is the origin.
1046 * This means we have to transform the CGContext to have rects
1047 * actually be applied in top left fashion. We do this by:
1049 * 1) Translating the context up by the height of the canvas
1050 * 2) Flipping the context by the Y axis so it's upside down.
1052 * These two transforms put the origin in the top left.
1053 * Transforms are better understood thinking about them from right to left order
1054 * (mathematically).
1056 * Consider a point we want to draw at (0, 10) in normal cartesian planes with
1057 * a box of (100, 100). in CG terms, this would be at (0, 10).
1058 * Positive Y values point up.
1059 * In our DrawTarget terms, positive Y values point down, so (0, 10) would be
1060 * at (0, 90) in cartesian plane terms. That means our point at (0, 10) in
1061 * DrawTarget terms should end up at (0, 90). How does this work with the
1062 * current transforms?
1064 * Going right to left with the transforms, a CGPoint of (0, 10) has cartesian
1065 * coordinates of (0, 10). The first flip of the Y axis puts the point now at
1066 * (0, -10); Next, we translate the context up by the size of the canvas
1067 * (Positive Y values go up in CG coordinates but down in our draw target
1068 * coordinates). Since our canvas size is (100, 100), the resulting coordinate
1069 * becomes (0, 90), which is what we expect from our DrawTarget code. These two
1070 * transforms put the CG context equal to what every other DrawTarget expects.
1072 * Next, we need two more transforms for actual text. IF we left the transforms
1073 * as is, the text would be drawn upside down, so we need another flip of the Y
1074 * axis to draw the text right side up. However, with only the flip, the text
1075 * would be drawn in the wrong place. Thus we also have to invert the Y position
1076 * of the glyphs to get them in the right place.
1078 * Thus we have the following transforms:
1079 * 1) Translation of the context up
1080 * 2) Flipping the context around the Y axis
1081 * 3) Flipping the context around the Y axis
1082 * 4) Inverting the Y position of each glyph
1084 * We cannot cancel out (2) and (3) as we have to apply the clips and transforms
1085 * of DrawTargetSkia between (2) and (3).
1087 * Consider the example letter P, drawn at (0, 20) in CG coordinates in a
1088 * (100, 100) rect.
1089 * Again, going right to left of the transforms. We'd get:
1091 * 1) The letter P drawn at (0, -20) due to the inversion of the Y axis
1092 * 2) The letter P upside down (b) at (0, 20) due to the second flip
1093 * 3) The letter P right side up at (0, -20) due to the first flip
1094 * 4) The letter P right side up at (0, 80) due to the translation
1096 * tl;dr - CGRects assume origin is bottom left, DrawTarget rects assume top
1097 * left.
1099 static bool SetupCGContext(DrawTargetSkia* aDT, CGContextRef aCGContext,
1100 SkCanvas* aCanvas, const IntPoint& aOrigin,
1101 const IntSize& aSize, bool aClipped) {
1102 // DrawTarget expects the origin to be at the top left, but CG
1103 // expects it to be at the bottom left. Transform to set the origin to
1104 // the top left. Have to set this before we do anything else.
1105 // This is transform (1) up top
1106 CGContextTranslateCTM(aCGContext, -aOrigin.x, aOrigin.y + aSize.height);
1108 // Transform (2) from the comments.
1109 CGContextScaleCTM(aCGContext, 1, -1);
1111 // Want to apply clips BEFORE the transform since the transform
1112 // will apply to the clips we apply.
1113 if (aClipped) {
1114 SkRegion clipRegion;
1115 aCanvas->temporary_internal_getRgnClip(&clipRegion);
1116 Vector<CGRect, 8> rects;
1117 for (SkRegion::Iterator it(clipRegion); !it.done(); it.next()) {
1118 const SkIRect& rect = it.rect();
1119 if (!rects.append(
1120 CGRectMake(rect.x(), rect.y(), rect.width(), rect.height()))) {
1121 break;
1124 if (rects.length()) {
1125 CGContextClipToRects(aCGContext, rects.begin(), rects.length());
1129 CGContextConcatCTM(aCGContext,
1130 GfxMatrixToCGAffineTransform(aDT->GetTransform()));
1131 return true;
1133 // End long comment about transforms.
1135 // The context returned from this method will have the origin
1136 // in the top left and will have applied all the neccessary clips
1137 // and transforms to the CGContext. See the comment above
1138 // SetupCGContext.
1139 CGContextRef DrawTargetSkia::BorrowCGContext(const DrawOptions& aOptions) {
1140 // Since we can't replay Skia clips, we have to use a layer if we have a
1141 // complex clip. After saving a layer, the SkCanvas queries for needing a
1142 // layer change so save if we pushed a layer.
1143 mNeedLayer = !mCanvas->isClipEmpty() && !mCanvas->isClipRect();
1144 if (mNeedLayer) {
1145 SkPaint paint;
1146 paint.setBlendMode(SkBlendMode::kSrc);
1147 SkCanvas::SaveLayerRec rec(nullptr, &paint,
1148 SkCanvas::kInitWithPrevious_SaveLayerFlag);
1149 mCanvas->saveLayer(rec);
1152 uint8_t* data = nullptr;
1153 int32_t stride;
1154 SurfaceFormat format;
1155 IntSize size;
1156 IntPoint origin;
1157 if (!LockBits(&data, &size, &stride, &format, &origin)) {
1158 NS_WARNING("Could not lock skia bits to wrap CG around");
1159 return nullptr;
1162 if (!mNeedLayer && (data == mCanvasData) && mCG && (mCGSize == size)) {
1163 // If our canvas data still points to the same data,
1164 // we can reuse the CG Context
1165 CGContextSetAlpha(mCG, aOptions.mAlpha);
1166 CGContextSetShouldAntialias(mCG,
1167 aOptions.mAntialiasMode != AntialiasMode::NONE);
1168 CGContextSaveGState(mCG);
1169 SetupCGContext(this, mCG, mCanvas, origin, size, true);
1170 return mCG;
1173 if (!mColorSpace) {
1174 mColorSpace = (format == SurfaceFormat::A8) ? CGColorSpaceCreateDeviceGray()
1175 : CGColorSpaceCreateDeviceRGB();
1178 if (mCG) {
1179 // Release the old CG context since it's no longer valid.
1180 CGContextRelease(mCG);
1183 mCanvasData = data;
1184 mCGSize = size;
1186 uint32_t bitmapInfo =
1187 (format == SurfaceFormat::A8)
1188 ? kCGImageAlphaOnly
1189 : kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
1191 mCG = CGBitmapContextCreateWithData(
1192 mCanvasData, mCGSize.width, mCGSize.height, 8, /* bits per component */
1193 stride, mColorSpace, bitmapInfo, NULL, /* Callback when released */
1194 NULL);
1195 if (!mCG) {
1196 if (mNeedLayer) {
1197 mCanvas->restore();
1199 ReleaseBits(mCanvasData);
1200 NS_WARNING("Could not create bitmap around skia data\n");
1201 return nullptr;
1204 CGContextSetAlpha(mCG, aOptions.mAlpha);
1205 CGContextSetShouldAntialias(mCG,
1206 aOptions.mAntialiasMode != AntialiasMode::NONE);
1207 CGContextSetShouldSmoothFonts(mCG, true);
1208 CGContextSetTextDrawingMode(mCG, kCGTextFill);
1209 CGContextSaveGState(mCG);
1210 SetupCGContext(this, mCG, mCanvas, origin, size, !mNeedLayer);
1211 return mCG;
1214 void DrawTargetSkia::ReturnCGContext(CGContextRef aCGContext) {
1215 MOZ_ASSERT(aCGContext == mCG);
1216 ReleaseBits(mCanvasData);
1217 CGContextRestoreGState(aCGContext);
1219 if (mNeedLayer) {
1220 // A layer was used for clipping and is about to be popped by the restore.
1221 // Make sure the CG context referencing it is released first so the popped
1222 // layer doesn't accidentally get used.
1223 if (mCG) {
1224 CGContextRelease(mCG);
1225 mCG = nullptr;
1227 mCanvas->restore();
1231 CGContextRef BorrowedCGContext::BorrowCGContextFromDrawTarget(DrawTarget* aDT) {
1232 DrawTargetSkia* skiaDT = static_cast<DrawTargetSkia*>(aDT);
1233 return skiaDT->BorrowCGContext(DrawOptions());
1236 void BorrowedCGContext::ReturnCGContextToDrawTarget(DrawTarget* aDT,
1237 CGContextRef cg) {
1238 DrawTargetSkia* skiaDT = static_cast<DrawTargetSkia*>(aDT);
1239 skiaDT->ReturnCGContext(cg);
1241 #endif
1243 static bool CanDrawFont(ScaledFont* aFont) {
1244 switch (aFont->GetType()) {
1245 case FontType::FREETYPE:
1246 case FontType::FONTCONFIG:
1247 case FontType::MAC:
1248 case FontType::GDI:
1249 case FontType::DWRITE:
1250 return true;
1251 default:
1252 return false;
1256 void DrawTargetSkia::DrawGlyphs(ScaledFont* aFont, const GlyphBuffer& aBuffer,
1257 const Pattern& aPattern,
1258 const StrokeOptions* aStrokeOptions,
1259 const DrawOptions& aOptions,
1260 SkShader* aShader) {
1261 if (!CanDrawFont(aFont)) {
1262 return;
1265 MarkChanged();
1267 ScaledFontBase* skiaFont = static_cast<ScaledFontBase*>(aFont);
1268 SkTypeface* typeface = skiaFont->GetSkTypeface();
1269 if (!typeface) {
1270 return;
1273 AutoPaintSetup paint(mCanvas, aOptions, aPattern);
1274 if (aStrokeOptions && !StrokeOptionsToPaint(paint.mPaint, *aStrokeOptions)) {
1275 return;
1278 AntialiasMode aaMode = aFont->GetDefaultAAMode();
1279 if (aOptions.mAntialiasMode != AntialiasMode::DEFAULT) {
1280 aaMode = aOptions.mAntialiasMode;
1282 bool aaEnabled = aaMode != AntialiasMode::NONE;
1283 paint.mPaint.setAntiAlias(aaEnabled);
1285 SkFont font(sk_ref_sp(typeface), SkFloatToScalar(skiaFont->mSize));
1287 bool useSubpixelAA =
1288 GetPermitSubpixelAA() &&
1289 (aaMode == AntialiasMode::DEFAULT || aaMode == AntialiasMode::SUBPIXEL);
1290 font.setEdging(useSubpixelAA ? SkFont::Edging::kSubpixelAntiAlias
1291 : (aaEnabled ? SkFont::Edging::kAntiAlias
1292 : SkFont::Edging::kAlias));
1294 skiaFont->SetupSkFontDrawOptions(font);
1296 if (aShader) {
1297 paint.mPaint.setShader(sk_ref_sp(aShader));
1300 // Limit the amount of internal batch allocations Skia does.
1301 const uint32_t kMaxGlyphBatchSize = 8192;
1303 for (uint32_t offset = 0; offset < aBuffer.mNumGlyphs;) {
1304 uint32_t batchSize =
1305 std::min(aBuffer.mNumGlyphs - offset, kMaxGlyphBatchSize);
1306 SkTextBlobBuilder builder;
1307 auto runBuffer = builder.allocRunPos(font, batchSize);
1308 for (uint32_t i = 0; i < batchSize; i++, offset++) {
1309 runBuffer.glyphs[i] = aBuffer.mGlyphs[offset].mIndex;
1310 runBuffer.points()[i] = PointToSkPoint(aBuffer.mGlyphs[offset].mPosition);
1313 sk_sp<SkTextBlob> text = builder.make();
1314 mCanvas->drawTextBlob(text, 0, 0, paint.mPaint);
1318 // This shader overrides the luminance color used to generate the preblend
1319 // tables for glyphs, without actually changing the rasterized color. This is
1320 // necesary for subpixel AA blending which requires both the mask and color
1321 // as separate inputs.
1322 class GlyphMaskShader : public SkEmptyShader {
1323 public:
1324 explicit GlyphMaskShader(const DeviceColor& aColor)
1325 : mColor({aColor.r, aColor.g, aColor.b, aColor.a}) {}
1327 bool onAsLuminanceColor(SkColor4f* aLum) const override {
1328 *aLum = mColor;
1329 return true;
1332 bool isOpaque() const override { return true; }
1333 bool isConstant() const override { return true; }
1335 void flatten(SkWriteBuffer& buffer) const override {
1336 buffer.writeColor4f(mColor);
1339 bool appendStages(const SkStageRec& rec,
1340 const SkShaders::MatrixRec&) const override {
1341 rec.fPipeline->appendConstantColor(rec.fAlloc,
1342 SkColor4f{1, 1, 1, 1}.premul().vec());
1343 return true;
1346 private:
1347 SkColor4f mColor;
1350 void DrawTargetSkia::DrawGlyphMask(ScaledFont* aFont,
1351 const GlyphBuffer& aBuffer,
1352 const DeviceColor& aColor,
1353 const StrokeOptions* aStrokeOptions,
1354 const DrawOptions& aOptions) {
1355 // Draw a mask using the GlyphMaskShader that can be used for subpixel AA
1356 // but that uses the gamma preblend weighting of the given color, even though
1357 // the mask itself does not use that color.
1358 sk_sp<GlyphMaskShader> shader = sk_make_sp<GlyphMaskShader>(aColor);
1359 DrawGlyphs(aFont, aBuffer, ColorPattern(DeviceColor(1, 1, 1, 1)),
1360 aStrokeOptions, aOptions, shader.get());
1363 Maybe<Rect> DrawTargetSkia::GetGlyphLocalBounds(
1364 ScaledFont* aFont, const GlyphBuffer& aBuffer, const Pattern& aPattern,
1365 const StrokeOptions* aStrokeOptions, const DrawOptions& aOptions) {
1366 if (!CanDrawFont(aFont)) {
1367 return Nothing();
1370 ScaledFontBase* skiaFont = static_cast<ScaledFontBase*>(aFont);
1371 SkTypeface* typeface = skiaFont->GetSkTypeface();
1372 if (!typeface) {
1373 return Nothing();
1376 AutoPaintSetup paint(mCanvas, aOptions, aPattern);
1377 if (aStrokeOptions && !StrokeOptionsToPaint(paint.mPaint, *aStrokeOptions)) {
1378 return Nothing();
1381 AntialiasMode aaMode = aFont->GetDefaultAAMode();
1382 if (aOptions.mAntialiasMode != AntialiasMode::DEFAULT) {
1383 aaMode = aOptions.mAntialiasMode;
1385 bool aaEnabled = aaMode != AntialiasMode::NONE;
1386 paint.mPaint.setAntiAlias(aaEnabled);
1388 SkFont font(sk_ref_sp(typeface), SkFloatToScalar(skiaFont->mSize));
1390 bool useSubpixelAA =
1391 GetPermitSubpixelAA() &&
1392 (aaMode == AntialiasMode::DEFAULT || aaMode == AntialiasMode::SUBPIXEL);
1393 font.setEdging(useSubpixelAA ? SkFont::Edging::kSubpixelAntiAlias
1394 : (aaEnabled ? SkFont::Edging::kAntiAlias
1395 : SkFont::Edging::kAlias));
1397 skiaFont->SetupSkFontDrawOptions(font);
1399 // Limit the amount of internal batch allocations Skia does.
1400 const uint32_t kMaxGlyphBatchSize = 8192;
1402 // Avoid using TextBlobBuilder for bounds computations as the conservative
1403 // bounds can be wrong due to buggy font metrics. Instead, explicitly compute
1404 // tight bounds directly with the SkFont.
1405 Vector<SkGlyphID, 32> glyphs;
1406 Vector<SkRect, 32> rects;
1407 Rect bounds;
1408 for (uint32_t offset = 0; offset < aBuffer.mNumGlyphs;) {
1409 uint32_t batchSize =
1410 std::min(aBuffer.mNumGlyphs - offset, kMaxGlyphBatchSize);
1411 if (glyphs.resizeUninitialized(batchSize) &&
1412 rects.resizeUninitialized(batchSize)) {
1413 for (uint32_t i = 0; i < batchSize; i++) {
1414 glyphs[i] = aBuffer.mGlyphs[offset + i].mIndex;
1416 font.getBounds(glyphs.begin(), batchSize, rects.begin(), nullptr);
1417 for (uint32_t i = 0; i < batchSize; i++) {
1418 bounds = bounds.Union(SkRectToRect(rects[i]) +
1419 aBuffer.mGlyphs[offset + i].mPosition);
1422 offset += batchSize;
1425 SkRect storage;
1426 bounds = SkRectToRect(
1427 paint.mPaint.computeFastBounds(RectToSkRect(bounds), &storage));
1429 if (bounds.IsEmpty()) {
1430 return Nothing();
1433 // Inflate the bounds to account for potential font hinting.
1434 bounds.Inflate(1);
1435 return Some(bounds);
1438 void DrawTargetSkia::FillGlyphs(ScaledFont* aFont, const GlyphBuffer& aBuffer,
1439 const Pattern& aPattern,
1440 const DrawOptions& aOptions) {
1441 DrawGlyphs(aFont, aBuffer, aPattern, nullptr, aOptions);
1444 void DrawTargetSkia::StrokeGlyphs(ScaledFont* aFont, const GlyphBuffer& aBuffer,
1445 const Pattern& aPattern,
1446 const StrokeOptions& aStrokeOptions,
1447 const DrawOptions& aOptions) {
1448 DrawGlyphs(aFont, aBuffer, aPattern, &aStrokeOptions, aOptions);
1451 void DrawTargetSkia::Mask(const Pattern& aSource, const Pattern& aMask,
1452 const DrawOptions& aOptions) {
1453 Maybe<MutexAutoLock> lock;
1454 SkPaint maskPaint;
1455 SetPaintPattern(maskPaint, aMask, lock);
1457 sk_sp<SkShader> maskShader(maskPaint.refShader());
1458 if (!maskShader && maskPaint.getAlpha() != 0xFF) {
1459 if (maskPaint.getAlpha() == 0) {
1460 return;
1462 maskShader = SkShaders::Color(maskPaint.getColor());
1463 if (!maskShader) {
1464 gfxDebug() << "Failed creating Skia clip shader for Mask";
1465 return;
1469 MarkChanged();
1470 AutoPaintSetup paint(mCanvas, aOptions, aSource);
1472 mCanvas->save();
1473 if (maskShader) {
1474 mCanvas->clipShader(maskShader);
1477 mCanvas->drawPaint(paint.mPaint);
1479 mCanvas->restore();
1482 void DrawTargetSkia::MaskSurface(const Pattern& aSource, SourceSurface* aMask,
1483 Point aOffset, const DrawOptions& aOptions) {
1484 Maybe<MutexAutoLock> lock;
1485 sk_sp<SkImage> maskImage = GetSkImageForSurface(aMask, &lock);
1486 SkMatrix maskOffset = SkMatrix::Translate(
1487 PointToSkPoint(aOffset + Point(aMask->GetRect().TopLeft())));
1488 sk_sp<SkShader> maskShader = maskImage->makeShader(
1489 SkTileMode::kClamp, SkTileMode::kClamp,
1490 SkSamplingOptions(SkFilterMode::kLinear), maskOffset);
1491 if (!maskShader) {
1492 gfxDebug() << "Failed creating Skia clip shader for MaskSurface";
1493 return;
1496 MarkChanged();
1497 AutoPaintSetup paint(mCanvas, aOptions, aSource);
1499 mCanvas->save();
1500 mCanvas->clipShader(maskShader);
1502 mCanvas->drawRect(RectToSkRect(Rect(aMask->GetRect()) + aOffset),
1503 paint.mPaint);
1505 mCanvas->restore();
1508 bool DrawTarget::Draw3DTransformedSurface(SourceSurface* aSurface,
1509 const Matrix4x4& aMatrix) {
1510 // Composite the 3D transform with the DT's transform.
1511 Matrix4x4 fullMat = aMatrix * Matrix4x4::From2D(mTransform);
1512 if (fullMat.IsSingular()) {
1513 return false;
1515 // Transform the surface bounds and clip to this DT.
1516 IntRect xformBounds = RoundedOut(fullMat.TransformAndClipBounds(
1517 Rect(Point(0, 0), Size(aSurface->GetSize())),
1518 Rect(Point(0, 0), Size(GetSize()))));
1519 if (xformBounds.IsEmpty()) {
1520 return true;
1522 // Offset the matrix by the transformed origin.
1523 fullMat.PostTranslate(-xformBounds.X(), -xformBounds.Y(), 0);
1525 // Read in the source data.
1526 Maybe<MutexAutoLock> lock;
1527 sk_sp<SkImage> srcImage = GetSkImageForSurface(aSurface, &lock);
1528 if (!srcImage) {
1529 return true;
1532 // Set up an intermediate destination surface only the size of the transformed
1533 // bounds. Try to pass through the source's format unmodified in both the BGRA
1534 // and ARGB cases.
1535 RefPtr<DataSourceSurface> dstSurf = Factory::CreateDataSourceSurface(
1536 xformBounds.Size(),
1537 !srcImage->isOpaque() ? aSurface->GetFormat()
1538 : SurfaceFormat::A8R8G8B8_UINT32,
1539 true);
1540 if (!dstSurf) {
1541 return false;
1544 DataSourceSurface::ScopedMap map(dstSurf, DataSourceSurface::READ_WRITE);
1545 if (!map.IsMapped()) {
1546 return false;
1548 std::unique_ptr<SkCanvas> dstCanvas(SkCanvas::MakeRasterDirect(
1549 SkImageInfo::Make(xformBounds.Width(), xformBounds.Height(),
1550 GfxFormatToSkiaColorType(dstSurf->GetFormat()),
1551 kPremul_SkAlphaType),
1552 map.GetData(), map.GetStride()));
1553 if (!dstCanvas) {
1554 return false;
1557 // Do the transform.
1558 SkPaint paint;
1559 paint.setAntiAlias(true);
1560 paint.setBlendMode(SkBlendMode::kSrc);
1562 SkMatrix xform;
1563 GfxMatrixToSkiaMatrix(fullMat, xform);
1564 dstCanvas->setMatrix(xform);
1566 dstCanvas->drawImage(srcImage, 0, 0, SkSamplingOptions(SkFilterMode::kLinear),
1567 &paint);
1569 // Temporarily reset the DT's transform, since it has already been composed
1570 // above.
1571 Matrix origTransform = mTransform;
1572 SetTransform(Matrix());
1574 // Draw the transformed surface within the transformed bounds.
1575 DrawSurface(dstSurf, Rect(xformBounds),
1576 Rect(Point(0, 0), Size(xformBounds.Size())));
1578 SetTransform(origTransform);
1580 return true;
1583 bool DrawTargetSkia::Draw3DTransformedSurface(SourceSurface* aSurface,
1584 const Matrix4x4& aMatrix) {
1585 if (aMatrix.IsSingular()) {
1586 return false;
1589 MarkChanged();
1591 Maybe<MutexAutoLock> lock;
1592 sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
1593 if (!image) {
1594 return true;
1597 mCanvas->save();
1599 SkPaint paint;
1600 paint.setAntiAlias(true);
1602 SkMatrix xform;
1603 GfxMatrixToSkiaMatrix(aMatrix, xform);
1604 mCanvas->concat(xform);
1606 mCanvas->drawImage(image, 0, 0, SkSamplingOptions(SkFilterMode::kLinear),
1607 &paint);
1609 mCanvas->restore();
1611 return true;
1614 already_AddRefed<SourceSurface> DrawTargetSkia::CreateSourceSurfaceFromData(
1615 unsigned char* aData, const IntSize& aSize, int32_t aStride,
1616 SurfaceFormat aFormat) const {
1617 RefPtr<SourceSurfaceSkia> newSurf = new SourceSurfaceSkia();
1619 if (!newSurf->InitFromData(aData, aSize, aStride, aFormat)) {
1620 gfxDebug() << *this
1621 << ": Failure to create source surface from data. Size: "
1622 << aSize;
1623 return nullptr;
1626 return newSurf.forget();
1629 already_AddRefed<DrawTarget> DrawTargetSkia::CreateSimilarDrawTarget(
1630 const IntSize& aSize, SurfaceFormat aFormat) const {
1631 RefPtr<DrawTargetSkia> target = new DrawTargetSkia();
1632 #ifdef DEBUG
1633 if (!IsBackedByPixels(mCanvas)) {
1634 // If our canvas is backed by vector storage such as PDF then we want to
1635 // create a new DrawTarget with similar storage to avoid losing fidelity
1636 // (fidelity will be lost if the returned DT is Snapshot()'ed and drawn
1637 // back onto us since a raster will be drawn instead of vector commands).
1638 NS_WARNING("Not backed by pixels - we need to handle PDF backed SkCanvas");
1640 #endif
1642 if (!target->Init(aSize, aFormat)) {
1643 return nullptr;
1645 return target.forget();
1648 bool DrawTargetSkia::CanCreateSimilarDrawTarget(const IntSize& aSize,
1649 SurfaceFormat aFormat) const {
1650 auto minmaxPair = std::minmax(aSize.width, aSize.height);
1651 return minmaxPair.first > 0 &&
1652 size_t(minmaxPair.second) < GetMaxSurfaceSize();
1655 RefPtr<DrawTarget> DrawTargetSkia::CreateClippedDrawTarget(
1656 const Rect& aBounds, SurfaceFormat aFormat) {
1657 SkIRect clipBounds;
1659 RefPtr<DrawTarget> result;
1660 // Doing this save()/restore() dance is wasteful
1661 mCanvas->save();
1662 if (!aBounds.IsEmpty()) {
1663 mCanvas->clipRect(RectToSkRect(aBounds), SkClipOp::kIntersect, true);
1665 if (mCanvas->getDeviceClipBounds(&clipBounds)) {
1666 RefPtr<DrawTarget> dt = CreateSimilarDrawTarget(
1667 IntSize(clipBounds.width(), clipBounds.height()), aFormat);
1668 if (dt) {
1669 result = gfx::Factory::CreateOffsetDrawTarget(
1670 dt, IntPoint(clipBounds.x(), clipBounds.y()));
1671 if (result) {
1672 result->SetTransform(mTransform);
1675 } else {
1676 // Everything is clipped but we still want some kind of surface
1677 result = CreateSimilarDrawTarget(IntSize(1, 1), aFormat);
1679 mCanvas->restore();
1680 return result;
1683 already_AddRefed<SourceSurface>
1684 DrawTargetSkia::OptimizeSourceSurfaceForUnknownAlpha(
1685 SourceSurface* aSurface) const {
1686 if (aSurface->GetType() == SurfaceType::SKIA) {
1687 RefPtr<SourceSurface> surface(aSurface);
1688 return surface.forget();
1691 if (RefPtr<DataSourceSurface> dataSurface = aSurface->GetDataSurface()) {
1692 DataSourceSurface::ScopedMap map(dataSurface,
1693 DataSourceSurface::READ_WRITE);
1694 if (map.IsMapped()) {
1695 // For plugins, GDI can sometimes just write 0 to the alpha channel
1696 // even for RGBX formats. In this case, we have to manually write
1697 // the alpha channel to make Skia happy with RGBX and in case GDI
1698 // writes some bad data. Luckily, this only happens on plugins.
1699 WriteRGBXFormat(map.GetData(), dataSurface->GetSize(), map.GetStride(),
1700 dataSurface->GetFormat());
1701 return dataSurface.forget();
1705 return nullptr;
1708 already_AddRefed<SourceSurface> DrawTargetSkia::OptimizeSourceSurface(
1709 SourceSurface* aSurface) const {
1710 if (aSurface->GetType() == SurfaceType::SKIA) {
1711 RefPtr<SourceSurface> surface(aSurface);
1712 return surface.forget();
1715 // If we're not using skia-gl then drawing doesn't require any
1716 // uploading, so any data surface is fine. Call GetDataSurface
1717 // to trigger any required readback so that it only happens
1718 // once.
1719 if (RefPtr<DataSourceSurface> dataSurface = aSurface->GetDataSurface()) {
1720 #ifdef DEBUG
1721 DataSourceSurface::ScopedMap map(dataSurface, DataSourceSurface::READ);
1722 if (map.IsMapped()) {
1723 MOZ_ASSERT(VerifyRGBXFormat(map.GetData(), dataSurface->GetSize(),
1724 map.GetStride(), dataSurface->GetFormat()));
1726 #endif
1727 return dataSurface.forget();
1730 return nullptr;
1733 already_AddRefed<SourceSurface>
1734 DrawTargetSkia::CreateSourceSurfaceFromNativeSurface(
1735 const NativeSurface& aSurface) const {
1736 return nullptr;
1739 void DrawTargetSkia::CopySurface(SourceSurface* aSurface,
1740 const IntRect& aSourceRect,
1741 const IntPoint& aDestination) {
1742 MarkChanged();
1744 Maybe<MutexAutoLock> lock;
1745 sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
1746 if (!image) {
1747 return;
1750 SkPixmap srcPixmap;
1751 if (!image->peekPixels(&srcPixmap)) {
1752 return;
1755 // Ensure the source rect intersects the surface bounds.
1756 IntRect srcRect = aSourceRect.Intersect(SkIRectToIntRect(srcPixmap.bounds()));
1757 // Move the destination offset to match the altered source rect.
1758 IntPoint dstOffset =
1759 aDestination + (srcRect.TopLeft() - aSourceRect.TopLeft());
1760 // Then ensure the dest rect intersect the canvas bounds.
1761 IntRect dstRect = IntRect(dstOffset, srcRect.Size()).Intersect(GetRect());
1762 // Move the source rect to match the altered dest rect.
1763 srcRect += dstRect.TopLeft() - dstOffset;
1764 srcRect.SizeTo(dstRect.Size());
1766 if (!srcPixmap.extractSubset(&srcPixmap, IntRectToSkIRect(srcRect))) {
1767 return;
1770 mCanvas->writePixels(srcPixmap.info(), srcPixmap.addr(), srcPixmap.rowBytes(),
1771 dstRect.x, dstRect.y);
1774 static inline SkPixelGeometry GetSkPixelGeometry() {
1775 return Factory::GetBGRSubpixelOrder() ? kBGR_H_SkPixelGeometry
1776 : kRGB_H_SkPixelGeometry;
1779 template <typename T>
1780 [[nodiscard]] static already_AddRefed<T> AsRefPtr(sk_sp<T>&& aSkPtr) {
1781 return already_AddRefed<T>(aSkPtr.release());
1784 bool DrawTargetSkia::Init(const IntSize& aSize, SurfaceFormat aFormat) {
1785 if (size_t(std::max(aSize.width, aSize.height)) > GetMaxSurfaceSize()) {
1786 return false;
1789 // we need to have surfaces that have a stride aligned to 4 for interop with
1790 // cairo
1791 SkImageInfo info = MakeSkiaImageInfo(aSize, aFormat);
1792 size_t stride = GetAlignedStride<4>(info.width(), info.bytesPerPixel());
1793 if (!stride) {
1794 return false;
1796 SkSurfaceProps props(0, GetSkPixelGeometry());
1798 if (aFormat == SurfaceFormat::A8) {
1799 // Skia does not fully allocate the last row according to stride.
1800 // Since some of our algorithms (i.e. blur) depend on this, we must allocate
1801 // the bitmap pixels manually.
1802 CheckedInt<size_t> size = stride;
1803 size *= info.height();
1804 // We need to leave room for an additional 3 bytes for a potential overrun
1805 // in our blurring code.
1806 size += 3;
1807 if (!size.isValid()) {
1808 return false;
1810 void* buf = sk_malloc_flags(size.value(), SK_MALLOC_ZERO_INITIALIZE);
1811 if (!buf) {
1812 return false;
1814 mSurface = AsRefPtr(SkSurfaces::WrapPixels(
1815 info, buf, stride, FreeAlphaPixels, nullptr, &props));
1816 } else {
1817 mSurface = AsRefPtr(SkSurfaces::Raster(info, stride, &props));
1819 if (!mSurface) {
1820 return false;
1823 mSize = aSize;
1824 mFormat = aFormat;
1825 mCanvas = mSurface->getCanvas();
1826 SetPermitSubpixelAA(IsOpaque(mFormat));
1828 if (info.isOpaque()) {
1829 mCanvas->clear(SK_ColorBLACK);
1831 return true;
1834 bool DrawTargetSkia::Init(SkCanvas* aCanvas) {
1835 mCanvas = aCanvas;
1837 SkImageInfo imageInfo = mCanvas->imageInfo();
1839 // If the canvas is backed by pixels we clear it to be on the safe side. If
1840 // it's not (for example, for PDF output) we don't.
1841 if (IsBackedByPixels(mCanvas)) {
1842 SkColor clearColor =
1843 imageInfo.isOpaque() ? SK_ColorBLACK : SK_ColorTRANSPARENT;
1844 mCanvas->clear(clearColor);
1847 SkISize size = mCanvas->getBaseLayerSize();
1848 mSize.width = size.width();
1849 mSize.height = size.height();
1850 mFormat =
1851 SkiaColorTypeToGfxFormat(imageInfo.colorType(), imageInfo.alphaType());
1852 SetPermitSubpixelAA(IsOpaque(mFormat));
1853 return true;
1856 bool DrawTargetSkia::Init(unsigned char* aData, const IntSize& aSize,
1857 int32_t aStride, SurfaceFormat aFormat,
1858 bool aUninitialized) {
1859 MOZ_ASSERT((aFormat != SurfaceFormat::B8G8R8X8) || aUninitialized ||
1860 VerifyRGBXFormat(aData, aSize, aStride, aFormat));
1862 SkSurfaceProps props(0, GetSkPixelGeometry());
1863 mSurface = AsRefPtr(SkSurfaces::WrapPixels(MakeSkiaImageInfo(aSize, aFormat),
1864 aData, aStride, &props));
1865 if (!mSurface) {
1866 return false;
1869 mSize = aSize;
1870 mFormat = aFormat;
1871 mCanvas = mSurface->getCanvas();
1872 SetPermitSubpixelAA(IsOpaque(mFormat));
1873 return true;
1876 bool DrawTargetSkia::Init(RefPtr<DataSourceSurface>&& aSurface) {
1877 auto map =
1878 new DataSourceSurface::ScopedMap(aSurface, DataSourceSurface::READ_WRITE);
1879 if (!map->IsMapped()) {
1880 delete map;
1881 return false;
1884 SurfaceFormat format = aSurface->GetFormat();
1885 IntSize size = aSurface->GetSize();
1886 MOZ_ASSERT((format != SurfaceFormat::B8G8R8X8) ||
1887 VerifyRGBXFormat(map->GetData(), size, map->GetStride(), format));
1889 SkSurfaceProps props(0, GetSkPixelGeometry());
1890 mSurface = AsRefPtr(SkSurfaces::WrapPixels(
1891 MakeSkiaImageInfo(size, format), map->GetData(), map->GetStride(),
1892 DrawTargetSkia::ReleaseMappedSkSurface, map, &props));
1893 if (!mSurface) {
1894 delete map;
1895 return false;
1898 // map is now owned by mSurface
1899 mBackingSurface = std::move(aSurface);
1900 mSize = size;
1901 mFormat = format;
1902 mCanvas = mSurface->getCanvas();
1903 SetPermitSubpixelAA(IsOpaque(format));
1904 return true;
1907 /* static */ void DrawTargetSkia::ReleaseMappedSkSurface(void* aPixels,
1908 void* aContext) {
1909 auto map = reinterpret_cast<DataSourceSurface::ScopedMap*>(aContext);
1910 delete map;
1913 void DrawTargetSkia::SetTransform(const Matrix& aTransform) {
1914 SkMatrix mat;
1915 GfxMatrixToSkiaMatrix(aTransform, mat);
1916 mCanvas->setMatrix(mat);
1917 mTransform = aTransform;
1920 void* DrawTargetSkia::GetNativeSurface(NativeSurfaceType aType) {
1921 return nullptr;
1924 already_AddRefed<PathBuilder> DrawTargetSkia::CreatePathBuilder(
1925 FillRule aFillRule) const {
1926 return PathBuilderSkia::Create(aFillRule);
1929 void DrawTargetSkia::ClearRect(const Rect& aRect) {
1930 MarkChanged();
1931 SkPaint paint;
1932 paint.setAntiAlias(true);
1933 paint.setColor((mFormat == SurfaceFormat::B8G8R8X8) ? SK_ColorBLACK
1934 : SK_ColorTRANSPARENT);
1935 paint.setBlendMode(SkBlendMode::kSrc);
1936 mCanvas->drawRect(RectToSkRect(aRect), paint);
1939 void DrawTargetSkia::PushClip(const Path* aPath) {
1940 if (aPath->GetBackendType() != BackendType::SKIA) {
1941 return;
1944 const PathSkia* skiaPath = static_cast<const PathSkia*>(aPath);
1945 mCanvas->save();
1946 mCanvas->clipPath(skiaPath->GetPath(), SkClipOp::kIntersect, true);
1949 void DrawTargetSkia::PushDeviceSpaceClipRects(const IntRect* aRects,
1950 uint32_t aCount) {
1951 // Build a region by unioning all the rects together.
1952 SkRegion region;
1953 for (uint32_t i = 0; i < aCount; i++) {
1954 region.op(IntRectToSkIRect(aRects[i]), SkRegion::kUnion_Op);
1957 // Clip with the resulting region. clipRegion does not transform
1958 // this region by the current transform, unlike the other SkCanvas
1959 // clip methods, so it is just passed through in device-space.
1960 mCanvas->save();
1961 mCanvas->clipRegion(region, SkClipOp::kIntersect);
1964 void DrawTargetSkia::PushClipRect(const Rect& aRect) {
1965 SkRect rect = RectToSkRect(aRect);
1967 mCanvas->save();
1968 mCanvas->clipRect(rect, SkClipOp::kIntersect, true);
1971 void DrawTargetSkia::PopClip() {
1972 mCanvas->restore();
1973 SetTransform(GetTransform());
1976 bool DrawTargetSkia::RemoveAllClips() {
1977 mCanvas->restoreToCount(1);
1978 SetTransform(GetTransform());
1979 return true;
1982 // Get clip bounds in device space for the clipping region. By default, only
1983 // bounds for simple (empty or rect) regions are reported. If explicitly
1984 // allowed, the bounds will be reported for complex (all other) regions as well.
1985 Maybe<IntRect> DrawTargetSkia::GetDeviceClipRect(bool aAllowComplex) const {
1986 if (mCanvas->isClipEmpty()) {
1987 return Some(IntRect());
1989 if (aAllowComplex || mCanvas->isClipRect()) {
1990 SkIRect deviceBounds;
1991 if (mCanvas->getDeviceClipBounds(&deviceBounds)) {
1992 return Some(SkIRectToIntRect(deviceBounds));
1995 return Nothing();
1998 void DrawTargetSkia::PushLayer(bool aOpaque, Float aOpacity,
1999 SourceSurface* aMask,
2000 const Matrix& aMaskTransform,
2001 const IntRect& aBounds, bool aCopyBackground) {
2002 PushLayerWithBlend(aOpaque, aOpacity, aMask, aMaskTransform, aBounds,
2003 aCopyBackground, CompositionOp::OP_OVER);
2006 void DrawTargetSkia::PushLayerWithBlend(bool aOpaque, Float aOpacity,
2007 SourceSurface* aMask,
2008 const Matrix& aMaskTransform,
2009 const IntRect& aBounds,
2010 bool aCopyBackground,
2011 CompositionOp aCompositionOp) {
2012 SkPaint paint;
2014 paint.setAlpha(ColorFloatToByte(aOpacity));
2015 paint.setBlendMode(GfxOpToSkiaOp(aCompositionOp));
2017 // aBounds is supplied in device space, but SaveLayerRec wants local space.
2018 SkRect bounds = SkRect::MakeEmpty();
2019 if (!aBounds.IsEmpty()) {
2020 Matrix inverseTransform = mTransform;
2021 if (inverseTransform.Invert()) {
2022 bounds = RectToSkRect(inverseTransform.TransformBounds(Rect(aBounds)));
2026 // We don't pass a lock object to GetSkImageForSurface here, to force a
2027 // copy of the data if this is a copy-on-write snapshot. If we instead held
2028 // the lock until the corresponding PopLayer, we'd risk deadlocking if someone
2029 // tried to touch the originating DrawTarget while the layer was pushed.
2030 sk_sp<SkImage> clipImage = GetSkImageForSurface(aMask, nullptr);
2031 bool usedMask = false;
2032 if (bool(clipImage)) {
2033 Rect maskBounds(aMask->GetRect());
2034 sk_sp<SkShader> shader = clipImage->makeShader(
2035 SkTileMode::kClamp, SkTileMode::kClamp,
2036 SkSamplingOptions(SkFilterMode::kLinear),
2037 SkMatrix::Translate(PointToSkPoint(maskBounds.TopLeft())));
2038 if (shader) {
2039 usedMask = true;
2040 mCanvas->save();
2042 auto oldMatrix = mCanvas->getLocalToDevice();
2043 SkMatrix clipMatrix;
2044 GfxMatrixToSkiaMatrix(aMaskTransform, clipMatrix);
2045 mCanvas->concat(clipMatrix);
2047 mCanvas->clipRect(RectToSkRect(maskBounds));
2048 mCanvas->clipShader(shader);
2050 mCanvas->setMatrix(oldMatrix);
2051 } else {
2052 gfxDebug() << "Failed to create Skia clip shader for PushLayerWithBlend";
2056 PushedLayer layer(GetPermitSubpixelAA(), usedMask ? aMask : nullptr);
2057 mPushedLayers.push_back(layer);
2059 SkCanvas::SaveLayerRec saveRec(
2060 aBounds.IsEmpty() ? nullptr : &bounds, &paint, nullptr,
2061 SkCanvas::kPreserveLCDText_SaveLayerFlag |
2062 (aCopyBackground ? SkCanvas::kInitWithPrevious_SaveLayerFlag : 0));
2064 mCanvas->saveLayer(saveRec);
2066 SetPermitSubpixelAA(aOpaque);
2068 #ifdef MOZ_WIDGET_COCOA
2069 CGContextRelease(mCG);
2070 mCG = nullptr;
2071 #endif
2074 void DrawTargetSkia::PopLayer() {
2075 MOZ_RELEASE_ASSERT(!mPushedLayers.empty());
2077 MarkChanged();
2079 const PushedLayer& layer = mPushedLayers.back();
2081 mCanvas->restore();
2083 if (layer.mMask) {
2084 mCanvas->restore();
2087 SetTransform(GetTransform());
2088 SetPermitSubpixelAA(layer.mOldPermitSubpixelAA);
2090 mPushedLayers.pop_back();
2092 #ifdef MOZ_WIDGET_COCOA
2093 CGContextRelease(mCG);
2094 mCG = nullptr;
2095 #endif
2098 already_AddRefed<GradientStops> DrawTargetSkia::CreateGradientStops(
2099 GradientStop* aStops, uint32_t aNumStops, ExtendMode aExtendMode) const {
2100 std::vector<GradientStop> stops;
2101 stops.resize(aNumStops);
2102 for (uint32_t i = 0; i < aNumStops; i++) {
2103 stops[i] = aStops[i];
2105 std::stable_sort(stops.begin(), stops.end());
2107 return MakeAndAddRef<GradientStopsSkia>(stops, aNumStops, aExtendMode);
2110 already_AddRefed<FilterNode> DrawTargetSkia::CreateFilter(FilterType aType) {
2111 return FilterNodeSoftware::Create(aType);
2114 void DrawTargetSkia::MarkChanged() {
2115 // I'm not entirely certain whether this lock is needed, as multiple threads
2116 // should never modify the DrawTarget at the same time anyway, but this seems
2117 // like the safest.
2118 MutexAutoLock lock(mSnapshotLock);
2119 if (mSnapshot) {
2120 if (mSnapshot->hasOneRef()) {
2121 // No owners outside of this DrawTarget's own reference. Just dump it.
2122 mSnapshot = nullptr;
2123 return;
2126 mSnapshot->DrawTargetWillChange();
2127 mSnapshot = nullptr;
2129 // Handle copying of any image snapshots bound to the surface.
2130 if (mSurface) {
2131 mSurface->notifyContentWillChange(SkSurface::kRetain_ContentChangeMode);
2136 } // namespace mozilla::gfx