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"
34 #include "PathHelpers.h"
40 #ifdef MOZ_WIDGET_COCOA
41 # include "BorrowedContext.h"
42 # include <ApplicationServices/ApplicationServices.h>
46 # include "ScaledFontDWrite.h"
51 void RefPtrTraits
<SkSurface
>::Release(SkSurface
* aSurface
) {
52 SkSafeUnref(aSurface
);
55 void RefPtrTraits
<SkSurface
>::AddRef(SkSurface
* aSurface
) {
59 } // namespace mozilla
61 namespace mozilla::gfx
{
63 class GradientStopsSkia
: public GradientStops
{
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
) {
74 // Skia gradients always require a stop at 0.0 and 1.0, insert these if
75 // we don't have them.
77 if (aStops
[0].offset
!= 0) {
81 if (aStops
[aNumStops
- 1].offset
!= 1) {
84 mColors
.resize(mCount
);
85 mPositions
.resize(mCount
);
86 if (aStops
[0].offset
!= 0) {
87 mColors
[0] = ColorToSkColor(aStops
[0].color
, 1.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
;
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
);
121 static void ReleaseTemporaryMappedSurface(const void* aPixels
, void* aContext
) {
122 DataSourceSurface
* surf
= static_cast<DataSourceSurface
*>(aContext
);
129 static void WriteRGBXFormat(uint8_t* aData
, const IntSize
& aSize
,
130 const int32_t aStride
, SurfaceFormat aFormat
) {
131 if (aFormat
!= SurfaceFormat::B8G8R8X8
|| aSize
.IsEmpty()) {
135 SwizzleData(aData
, aStride
, SurfaceFormat::X8R8G8B8_UINT32
, aData
, aStride
,
136 SurfaceFormat::A8R8G8B8_UINT32
, aSize
);
140 static IntRect
CalculateSurfaceBounds(const IntSize
& aSize
, const Rect
* aBounds
,
141 const Matrix
* aMatrix
) {
142 IntRect
surfaceBounds(IntPoint(0, 0), aSize
);
144 return surfaceBounds
;
148 Matrix
inverse(*aMatrix
);
149 if (!inverse
.Invert()) {
150 return surfaceBounds
;
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()) {
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]);
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()) {
202 IntRect bounds
= CalculateSurfaceBounds(aSize
, aBounds
, aMatrix
);
203 if (bounds
.IsEmpty()) {
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]);
244 static sk_sp
<SkImage
> GetSkImageForSurface(SourceSurface
* aSurface
,
245 Maybe
<MutexAutoLock
>* aLock
,
246 const Rect
* aBounds
= nullptr,
247 const Matrix
* aMatrix
= nullptr) {
249 gfxDebug() << "Creating null Skia image from null SourceSurface";
253 if (aSurface
->GetType() == SurfaceType::SKIA
) {
254 return static_cast<SourceSurfaceSkia
*>(aSurface
)->GetImage(aLock
);
257 RefPtr
<DataSourceSurface
> dataSurface
= aSurface
->GetDataSurface();
259 gfxWarning() << "Failed getting DataSourceSurface for Skia image";
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";
274 releaseProc
= ReleaseTemporaryMappedSurface
;
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
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
);
292 releaseProc(map
.mData
, surf
);
293 gfxDebug() << "Failed making Skia raster image for temporary surface";
299 DrawTargetSkia::DrawTargetSkia()
302 mSnapshotLock
{"DrawTargetSkia::mSnapshotLock"}
303 #ifdef MOZ_WIDGET_COCOA
306 mColorSpace(nullptr),
307 mCanvasData(nullptr),
314 DrawTargetSkia::~DrawTargetSkia() {
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
323 CGContextRelease(mCG
);
328 CGColorSpaceRelease(mColorSpace
);
329 mColorSpace
= nullptr;
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();
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
353 if (mSurface
->peekPixels(&pixmap
)) {
354 image
= SkImages::RasterFromPixmap(pixmap
, nullptr, nullptr);
356 image
= mSurface
->makeImageSnapshot();
358 if (!snapshot
->InitFromImage(image
, aFormat
, this)) {
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();
375 bool DrawTargetSkia::LockBits(uint8_t** aData
, IntSize
* aSize
, int32_t* aStride
,
376 SurfaceFormat
* aFormat
, IntPoint
* aOrigin
) {
380 void* pixels
= mCanvas
->accessTopLayerPixels(&info
, &rowBytes
, &origin
);
382 // Ensure the layer is at the origin if required.
383 (!aOrigin
&& !origin
.isZero())) {
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());
394 *aOrigin
= IntPoint(origin
.x(), origin
.y());
399 void DrawTargetSkia::ReleaseBits(uint8_t* aData
) {}
401 static void ReleaseImage(const void* aPixels
, void* aContext
) {
402 SkImage
* image
= static_cast<SkImage
*>(aContext
);
406 static sk_sp
<SkImage
> ExtractSubset(sk_sp
<SkImage
> aImage
,
407 const IntRect
& aRect
) {
408 SkIRect subsetRect
= IntRectToSkIRect(aRect
);
409 if (aImage
->bounds() == subsetRect
) {
412 // makeSubset is slow, so prefer to use SkPixmap::extractSubset where
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
419 return SkImages::RasterFromPixmap(subsetPixmap
, ReleaseImage
,
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) {
431 if (aAllowReuse
&& aImage
->isAlphaOnly() && aImage
->peekPixels(&pixmap
)) {
433 bitmap
.installPixels(pixmap
.info(), pixmap
.writable_addr(),
435 *aResultBitmap
= bitmap
;
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());
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.
449 if (size
.isValid()) {
450 void* buf
= sk_malloc_flags(size
.value(), 0);
453 if (bitmap
.installPixels(info
, buf
, stride
, FreeAlphaPixels
, nullptr) &&
454 aImage
->readPixels(bitmap
.info(), bitmap
.getPixels(),
455 bitmap
.rowBytes(), 0, 0)) {
456 *aResultBitmap
= bitmap
;
463 gfxWarning() << "Failed reading alpha pixels for Skia bitmap";
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
));
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())
484 if (!stops
|| stops
->mCount
< 2 || !pat
.mBegin
.IsFinite() ||
485 !pat
.mEnd
.IsFinite() || pat
.mBegin
== pat
.mEnd
) {
486 aPaint
.setColor(SK_ColorTRANSPARENT
);
488 SkTileMode mode
= ExtendModeToTileMode(stops
->mExtendMode
, Axis::BOTH
);
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
));
496 GfxMatrixToSkiaMatrix(pat
.mMatrix
, mat
);
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
);
504 aPaint
.setShader(shader
);
506 aPaint
.setColor(SK_ColorTRANSPARENT
);
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())
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
);
524 SkTileMode mode
= ExtendModeToTileMode(stops
->mExtendMode
, Axis::BOTH
);
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
));
532 GfxMatrixToSkiaMatrix(pat
.mMatrix
, mat
);
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
);
541 aPaint
.setShader(shader
);
543 aPaint
.setColor(SK_ColorTRANSPARENT
);
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())
555 if (!stops
|| stops
->mCount
< 2 || !pat
.mCenter
.IsFinite() ||
556 !std::isfinite(pat
.mAngle
)) {
557 aPaint
.setColor(SK_ColorTRANSPARENT
);
560 GfxMatrixToSkiaMatrix(pat
.mMatrix
, mat
);
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
570 Float angle
= (pat
.mAngle
* 180.0 / M_PI
) - 90.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
,
582 aPaint
.setShader(shader
);
584 aPaint
.setColor(SK_ColorTRANSPARENT
);
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
);
594 aPaint
.setColor(SK_ColorTRANSPARENT
);
599 GfxMatrixToSkiaMatrix(pat
.mMatrix
, mat
);
601 mat
.postConcat(*aMatrix
);
604 if (!pat
.mSamplingRect
.IsEmpty()) {
605 image
= ExtractSubset(image
, pat
.mSamplingRect
);
607 aPaint
.setColor(SK_ColorTRANSPARENT
);
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
);
623 aPaint
.setShader(shader
);
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
);
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
)) {
644 if (!aCanvas
->getTotalMatrix().invert(&inverseCTM
)) {
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
);
674 void Init(SkCanvas
* aCanvas
, const DrawOptions
& aOptions
,
675 const Rect
* aMaskBounds
, bool aForceGroup
) {
676 mPaint
.setBlendMode(GfxOpToSkiaOp(aOptions
.mCompositionOp
));
679 // TODO: Can we set greyscale somehow?
680 if (aOptions
.mAntialiasMode
!= AntialiasMode::NONE
) {
681 mPaint
.setAntiAlias(true);
683 mPaint
.setAntiAlias(false);
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.
695 mPaint
.setBlendMode(SkBlendMode::kSrcOver
);
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;
705 mPaint
.setAlpha(ColorFloatToByte(aOptions
.mAlpha
));
706 mAlpha
= aOptions
.mAlpha
;
710 // TODO: Maybe add an operator overload to access this easier?
714 Maybe
<MutexAutoLock
> mLock
;
718 void DrawTargetSkia::Flush() {}
720 void DrawTargetSkia::DrawSurface(SourceSurface
* aSurface
, const Rect
& aDest
,
722 const DrawSurfaceOptions
& aSurfOptions
,
723 const DrawOptions
& aOptions
) {
724 if (aSource
.IsEmpty()) {
730 Maybe
<MutexAutoLock
> lock
;
731 sk_sp
<SkImage
> image
= GetSkImageForSurface(aSurface
, &lock
);
736 SkRect destRect
= RectToSkRect(aDest
);
737 SkRect sourceRect
= RectToSkRect(aSource
- aSurface
->GetRect().TopLeft());
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
) {
763 FilterNodeSoftware
* filter
= static_cast<FilterNodeSoftware
*>(aNode
);
764 filter
->Draw(this, aSourceRect
, aDestPoint
, aOptions
);
767 void DrawTargetSkia::DrawSurfaceWithShadow(SourceSurface
* aSurface
,
769 const ShadowOptions
& aShadow
,
770 CompositionOp aOperator
) {
771 if (aSurface
->GetSize().IsEmpty()) {
777 Maybe
<MutexAutoLock
> lock
;
778 sk_sp
<SkImage
> image
= GetSkImageForSurface(aSurface
, &lock
);
784 mCanvas
->resetMatrix();
787 paint
.setBlendMode(GfxOpToSkiaOp(aOperator
));
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.
798 shadowPaint
.setBlendMode(GfxOpToSkiaOp(aOperator
));
800 auto shadowDest
= IntPoint::Round(aDest
+ aShadow
.mOffset
);
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
,
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
);
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
);
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
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.
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
);
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
) {
888 MOZ_ASSERT(aPath
, "Null path");
889 if (aPath
->GetBackendType() != BackendType::SKIA
) {
893 const PathSkia
* skiaPath
= static_cast<const PathSkia
*>(aPath
);
895 AutoPaintSetup
paint(mCanvas
, aOptions
, aPattern
);
896 if (!StrokeOptionsToPaint(paint
.mPaint
, aStrokeOptions
)) {
900 if (!skiaPath
->GetPath().isFinite()) {
904 mCanvas
->drawPath(skiaPath
->GetPath(), paint
.mPaint
);
907 static Double
DashPeriodLength(const StrokeOptions
& aStrokeOptions
) {
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.
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()) {
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.
975 if (aStrokeOptions
.mDashLength
> 0 && !rect
.IsEmpty()) {
976 IntRect
deviceClip(IntPoint(0, 0), mSize
);
978 if (mCanvas
->getDeviceClipBounds(&clipBounds
)) {
979 deviceClip
= SkIRectToIntRect(clipBounds
);
982 ShrinkClippedStrokedRect(rect
, deviceClip
, mTransform
, aStrokeOptions
);
983 if (rect
.IsEmpty()) {
989 AutoPaintSetup
paint(mCanvas
, aOptions
, aPattern
);
990 if (!StrokeOptionsToPaint(paint
.mPaint
, aStrokeOptions
)) {
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
) {
1002 AutoPaintSetup
paint(mCanvas
, aOptions
, aPattern
);
1003 if (!StrokeOptionsToPaint(paint
.mPaint
, aStrokeOptions
)) {
1007 mCanvas
->drawLine(SkFloatToScalar(aStart
.x
), SkFloatToScalar(aStart
.y
),
1008 SkFloatToScalar(aEnd
.x
), SkFloatToScalar(aEnd
.y
),
1012 void DrawTargetSkia::Fill(const Path
* aPath
, const Pattern
& aPattern
,
1013 const DrawOptions
& aOptions
) {
1015 if (!aPath
|| aPath
->GetBackendType() != BackendType::SKIA
) {
1019 const PathSkia
* skiaPath
= static_cast<const PathSkia
*>(aPath
);
1021 AutoPaintSetup
paint(mCanvas
, aOptions
, aPattern
);
1023 if (!skiaPath
->GetPath().isFinite()) {
1027 mCanvas
->drawPath(skiaPath
->GetPath(), paint
.mPaint
);
1030 #ifdef MOZ_WIDGET_COCOA
1031 static inline CGAffineTransform
GfxMatrixToCGAffineTransform(const Matrix
& m
) {
1032 CGAffineTransform t
;
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
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
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
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.
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();
1120 CGRectMake(rect
.x(), rect
.y(), rect
.width(), rect
.height()))) {
1124 if (rects
.length()) {
1125 CGContextClipToRects(aCGContext
, rects
.begin(), rects
.length());
1129 CGContextConcatCTM(aCGContext
,
1130 GfxMatrixToCGAffineTransform(aDT
->GetTransform()));
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
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();
1146 paint
.setBlendMode(SkBlendMode::kSrc
);
1147 SkCanvas::SaveLayerRec
rec(nullptr, &paint
,
1148 SkCanvas::kInitWithPrevious_SaveLayerFlag
);
1149 mCanvas
->saveLayer(rec
);
1152 uint8_t* data
= nullptr;
1154 SurfaceFormat format
;
1157 if (!LockBits(&data
, &size
, &stride
, &format
, &origin
)) {
1158 NS_WARNING("Could not lock skia bits to wrap CG around");
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);
1174 mColorSpace
= (format
== SurfaceFormat::A8
) ? CGColorSpaceCreateDeviceGray()
1175 : CGColorSpaceCreateDeviceRGB();
1179 // Release the old CG context since it's no longer valid.
1180 CGContextRelease(mCG
);
1186 uint32_t bitmapInfo
=
1187 (format
== SurfaceFormat::A8
)
1189 : kCGImageAlphaPremultipliedFirst
| kCGBitmapByteOrder32Host
;
1191 mCG
= CGBitmapContextCreateWithData(
1192 mCanvasData
, mCGSize
.width
, mCGSize
.height
, 8, /* bits per component */
1193 stride
, mColorSpace
, bitmapInfo
, NULL
, /* Callback when released */
1199 ReleaseBits(mCanvasData
);
1200 NS_WARNING("Could not create bitmap around skia data\n");
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
);
1214 void DrawTargetSkia::ReturnCGContext(CGContextRef aCGContext
) {
1215 MOZ_ASSERT(aCGContext
== mCG
);
1216 ReleaseBits(mCanvasData
);
1217 CGContextRestoreGState(aCGContext
);
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.
1224 CGContextRelease(mCG
);
1231 CGContextRef
BorrowedCGContext::BorrowCGContextFromDrawTarget(DrawTarget
* aDT
) {
1232 DrawTargetSkia
* skiaDT
= static_cast<DrawTargetSkia
*>(aDT
);
1233 return skiaDT
->BorrowCGContext(DrawOptions());
1236 void BorrowedCGContext::ReturnCGContextToDrawTarget(DrawTarget
* aDT
,
1238 DrawTargetSkia
* skiaDT
= static_cast<DrawTargetSkia
*>(aDT
);
1239 skiaDT
->ReturnCGContext(cg
);
1243 static bool CanDrawFont(ScaledFont
* aFont
) {
1244 switch (aFont
->GetType()) {
1245 case FontType::FREETYPE
:
1246 case FontType::FONTCONFIG
:
1249 case FontType::DWRITE
:
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
)) {
1267 ScaledFontBase
* skiaFont
= static_cast<ScaledFontBase
*>(aFont
);
1268 SkTypeface
* typeface
= skiaFont
->GetSkTypeface();
1273 AutoPaintSetup
paint(mCanvas
, aOptions
, aPattern
);
1274 if (aStrokeOptions
&& !StrokeOptionsToPaint(paint
.mPaint
, *aStrokeOptions
)) {
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
);
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
{
1324 explicit GlyphMaskShader(const DeviceColor
& aColor
)
1325 : mColor({aColor
.r
, aColor
.g
, aColor
.b
, aColor
.a
}) {}
1327 bool onAsLuminanceColor(SkColor4f
* aLum
) const override
{
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());
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
)) {
1370 ScaledFontBase
* skiaFont
= static_cast<ScaledFontBase
*>(aFont
);
1371 SkTypeface
* typeface
= skiaFont
->GetSkTypeface();
1376 AutoPaintSetup
paint(mCanvas
, aOptions
, aPattern
);
1377 if (aStrokeOptions
&& !StrokeOptionsToPaint(paint
.mPaint
, *aStrokeOptions
)) {
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
;
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
;
1426 bounds
= SkRectToRect(
1427 paint
.mPaint
.computeFastBounds(RectToSkRect(bounds
), &storage
));
1429 if (bounds
.IsEmpty()) {
1433 // Inflate the bounds to account for potential font hinting.
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
;
1455 SetPaintPattern(maskPaint
, aMask
, lock
);
1457 sk_sp
<SkShader
> maskShader(maskPaint
.refShader());
1458 if (!maskShader
&& maskPaint
.getAlpha() != 0xFF) {
1459 if (maskPaint
.getAlpha() == 0) {
1462 maskShader
= SkShaders::Color(maskPaint
.getColor());
1464 gfxDebug() << "Failed creating Skia clip shader for Mask";
1470 AutoPaintSetup
paint(mCanvas
, aOptions
, aSource
);
1474 mCanvas
->clipShader(maskShader
);
1477 mCanvas
->drawPaint(paint
.mPaint
);
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
);
1492 gfxDebug() << "Failed creating Skia clip shader for MaskSurface";
1497 AutoPaintSetup
paint(mCanvas
, aOptions
, aSource
);
1500 mCanvas
->clipShader(maskShader
);
1502 mCanvas
->drawRect(RectToSkRect(Rect(aMask
->GetRect()) + aOffset
),
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()) {
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()) {
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
);
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
1535 RefPtr
<DataSourceSurface
> dstSurf
= Factory::CreateDataSourceSurface(
1537 !srcImage
->isOpaque() ? aSurface
->GetFormat()
1538 : SurfaceFormat::A8R8G8B8_UINT32
,
1544 DataSourceSurface::ScopedMap
map(dstSurf
, DataSourceSurface::READ_WRITE
);
1545 if (!map
.IsMapped()) {
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()));
1557 // Do the transform.
1559 paint
.setAntiAlias(true);
1560 paint
.setBlendMode(SkBlendMode::kSrc
);
1563 GfxMatrixToSkiaMatrix(fullMat
, xform
);
1564 dstCanvas
->setMatrix(xform
);
1566 dstCanvas
->drawImage(srcImage
, 0, 0, SkSamplingOptions(SkFilterMode::kLinear
),
1569 // Temporarily reset the DT's transform, since it has already been composed
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
);
1583 bool DrawTargetSkia::Draw3DTransformedSurface(SourceSurface
* aSurface
,
1584 const Matrix4x4
& aMatrix
) {
1585 if (aMatrix
.IsSingular()) {
1591 Maybe
<MutexAutoLock
> lock
;
1592 sk_sp
<SkImage
> image
= GetSkImageForSurface(aSurface
, &lock
);
1600 paint
.setAntiAlias(true);
1603 GfxMatrixToSkiaMatrix(aMatrix
, xform
);
1604 mCanvas
->concat(xform
);
1606 mCanvas
->drawImage(image
, 0, 0, SkSamplingOptions(SkFilterMode::kLinear
),
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
)) {
1621 << ": Failure to create source surface from data. Size: "
1626 return newSurf
.forget();
1629 already_AddRefed
<DrawTarget
> DrawTargetSkia::CreateSimilarDrawTarget(
1630 const IntSize
& aSize
, SurfaceFormat aFormat
) const {
1631 RefPtr
<DrawTargetSkia
> target
= new DrawTargetSkia();
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");
1642 if (!target
->Init(aSize
, aFormat
)) {
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
) {
1659 RefPtr
<DrawTarget
> result
;
1660 // Doing this save()/restore() dance is wasteful
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
);
1669 result
= gfx::Factory::CreateOffsetDrawTarget(
1670 dt
, IntPoint(clipBounds
.x(), clipBounds
.y()));
1672 result
->SetTransform(mTransform
);
1676 // Everything is clipped but we still want some kind of surface
1677 result
= CreateSimilarDrawTarget(IntSize(1, 1), aFormat
);
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();
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
1719 if (RefPtr
<DataSourceSurface
> dataSurface
= aSurface
->GetDataSurface()) {
1721 DataSourceSurface::ScopedMap
map(dataSurface
, DataSourceSurface::READ
);
1722 if (map
.IsMapped()) {
1723 MOZ_ASSERT(VerifyRGBXFormat(map
.GetData(), dataSurface
->GetSize(),
1724 map
.GetStride(), dataSurface
->GetFormat()));
1727 return dataSurface
.forget();
1733 already_AddRefed
<SourceSurface
>
1734 DrawTargetSkia::CreateSourceSurfaceFromNativeSurface(
1735 const NativeSurface
& aSurface
) const {
1739 void DrawTargetSkia::CopySurface(SourceSurface
* aSurface
,
1740 const IntRect
& aSourceRect
,
1741 const IntPoint
& aDestination
) {
1744 Maybe
<MutexAutoLock
> lock
;
1745 sk_sp
<SkImage
> image
= GetSkImageForSurface(aSurface
, &lock
);
1751 if (!image
->peekPixels(&srcPixmap
)) {
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
))) {
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()) {
1789 // we need to have surfaces that have a stride aligned to 4 for interop with
1791 SkImageInfo info
= MakeSkiaImageInfo(aSize
, aFormat
);
1792 size_t stride
= GetAlignedStride
<4>(info
.width(), info
.bytesPerPixel());
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.
1807 if (!size
.isValid()) {
1810 void* buf
= sk_malloc_flags(size
.value(), SK_MALLOC_ZERO_INITIALIZE
);
1814 mSurface
= AsRefPtr(SkSurfaces::WrapPixels(
1815 info
, buf
, stride
, FreeAlphaPixels
, nullptr, &props
));
1817 mSurface
= AsRefPtr(SkSurfaces::Raster(info
, stride
, &props
));
1825 mCanvas
= mSurface
->getCanvas();
1826 SetPermitSubpixelAA(IsOpaque(mFormat
));
1828 if (info
.isOpaque()) {
1829 mCanvas
->clear(SK_ColorBLACK
);
1834 bool DrawTargetSkia::Init(SkCanvas
* 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();
1851 SkiaColorTypeToGfxFormat(imageInfo
.colorType(), imageInfo
.alphaType());
1852 SetPermitSubpixelAA(IsOpaque(mFormat
));
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
));
1871 mCanvas
= mSurface
->getCanvas();
1872 SetPermitSubpixelAA(IsOpaque(mFormat
));
1876 bool DrawTargetSkia::Init(RefPtr
<DataSourceSurface
>&& aSurface
) {
1878 new DataSourceSurface::ScopedMap(aSurface
, DataSourceSurface::READ_WRITE
);
1879 if (!map
->IsMapped()) {
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
));
1898 // map is now owned by mSurface
1899 mBackingSurface
= std::move(aSurface
);
1902 mCanvas
= mSurface
->getCanvas();
1903 SetPermitSubpixelAA(IsOpaque(format
));
1907 /* static */ void DrawTargetSkia::ReleaseMappedSkSurface(void* aPixels
,
1909 auto map
= reinterpret_cast<DataSourceSurface::ScopedMap
*>(aContext
);
1913 void DrawTargetSkia::SetTransform(const Matrix
& aTransform
) {
1915 GfxMatrixToSkiaMatrix(aTransform
, mat
);
1916 mCanvas
->setMatrix(mat
);
1917 mTransform
= aTransform
;
1920 void* DrawTargetSkia::GetNativeSurface(NativeSurfaceType aType
) {
1924 already_AddRefed
<PathBuilder
> DrawTargetSkia::CreatePathBuilder(
1925 FillRule aFillRule
) const {
1926 return PathBuilderSkia::Create(aFillRule
);
1929 void DrawTargetSkia::ClearRect(const Rect
& aRect
) {
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
) {
1944 const PathSkia
* skiaPath
= static_cast<const PathSkia
*>(aPath
);
1946 mCanvas
->clipPath(skiaPath
->GetPath(), SkClipOp::kIntersect
, true);
1949 void DrawTargetSkia::PushDeviceSpaceClipRects(const IntRect
* aRects
,
1951 // Build a region by unioning all the rects together.
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.
1961 mCanvas
->clipRegion(region
, SkClipOp::kIntersect
);
1964 void DrawTargetSkia::PushClipRect(const Rect
& aRect
) {
1965 SkRect rect
= RectToSkRect(aRect
);
1968 mCanvas
->clipRect(rect
, SkClipOp::kIntersect
, true);
1971 void DrawTargetSkia::PopClip() {
1973 SetTransform(GetTransform());
1976 bool DrawTargetSkia::RemoveAllClips() {
1977 mCanvas
->restoreToCount(1);
1978 SetTransform(GetTransform());
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
));
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
) {
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())));
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
);
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
);
2074 void DrawTargetSkia::PopLayer() {
2075 MOZ_RELEASE_ASSERT(!mPushedLayers
.empty());
2079 const PushedLayer
& layer
= mPushedLayers
.back();
2087 SetTransform(GetTransform());
2088 SetPermitSubpixelAA(layer
.mOldPermitSubpixelAA
);
2090 mPushedLayers
.pop_back();
2092 #ifdef MOZ_WIDGET_COCOA
2093 CGContextRelease(mCG
);
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
2118 MutexAutoLock
lock(mSnapshotLock
);
2120 if (mSnapshot
->hasOneRef()) {
2121 // No owners outside of this DrawTarget's own reference. Just dump it.
2122 mSnapshot
= nullptr;
2126 mSnapshot
->DrawTargetWillChange();
2127 mSnapshot
= nullptr;
2129 // Handle copying of any image snapshots bound to the surface.
2131 mSurface
->notifyContentWillChange(SkSurface::kRetain_ContentChangeMode
);
2136 } // namespace mozilla::gfx