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