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