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/. */
6
7 #include "DrawTargetSkia.h"
8 #include "SourceSurfaceSkia.h"
9 #include "ScaledFontBase.h"
10 #include "FilterNodeSoftware.h"
11 #include "HelpersSkia.h"
12
13 #include "mozilla/ArrayUtils.h"
14 #include "mozilla/CheckedInt.h"
15 #include "mozilla/Vector.h"
16
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"
26 #include "Blur.h"
27 #include "Logging.h"
28 #include "Tools.h"
29 #include "DataSurfaceHelpers.h"
30 #include "PathHelpers.h"
31 #include "PathSkia.h"
32 #include "Swizzle.h"
33 #include <algorithm>
34
35 #ifdef MOZ_WIDGET_COCOA
36 # include "BorrowedContext.h"
37 # include <ApplicationServices/ApplicationServices.h>
38 #endif
39
40 #ifdef XP_WIN
41 # include "ScaledFontDWrite.h"
42 #endif
43
44 namespace mozilla {
45
Release(SkSurface * aSurface)46 void RefPtrTraits<SkSurface>::Release(SkSurface* aSurface) {
47 SkSafeUnref(aSurface);
48 }
49
AddRef(SkSurface * aSurface)50 void RefPtrTraits<SkSurface>::AddRef(SkSurface* aSurface) {
51 SkSafeRef(aSurface);
52 }
53
54 } // namespace mozilla
55
56 namespace mozilla::gfx {
57
58 class GradientStopsSkia : public GradientStops {
59 public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsSkia,override)60 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsSkia, override)
61
62 GradientStopsSkia(const std::vector<GradientStop>& aStops, uint32_t aNumStops,
63 ExtendMode aExtendMode)
64 : mCount(aNumStops), mExtendMode(aExtendMode) {
65 if (mCount == 0) {
66 return;
67 }
68
69 // Skia gradients always require a stop at 0.0 and 1.0, insert these if
70 // we don't have them.
71 uint32_t shift = 0;
72 if (aStops[0].offset != 0) {
73 mCount++;
74 shift = 1;
75 }
76 if (aStops[aNumStops - 1].offset != 1) {
77 mCount++;
78 }
79 mColors.resize(mCount);
80 mPositions.resize(mCount);
81 if (aStops[0].offset != 0) {
82 mColors[0] = ColorToSkColor(aStops[0].color, 1.0);
83 mPositions[0] = 0;
84 }
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);
88 }
89 if (aStops[aNumStops - 1].offset != 1) {
90 mColors[mCount - 1] = ColorToSkColor(aStops[aNumStops - 1].color, 1.0);
91 mPositions[mCount - 1] = SK_Scalar1;
92 }
93 }
94
GetBackendType() const95 BackendType GetBackendType() const override { return BackendType::SKIA; }
96
97 std::vector<SkColor> mColors;
98 std::vector<SkScalar> mPositions;
99 int mCount;
100 ExtendMode mExtendMode;
101 };
102
103 /**
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.
108 */
ReleaseTemporarySurface(const void * aPixels,void * aContext)109 static void ReleaseTemporarySurface(const void* aPixels, void* aContext) {
110 DataSourceSurface* surf = static_cast<DataSourceSurface*>(aContext);
111 if (surf) {
112 surf->Release();
113 }
114 }
115
ReleaseTemporaryMappedSurface(const void * aPixels,void * aContext)116 static void ReleaseTemporaryMappedSurface(const void* aPixels, void* aContext) {
117 DataSourceSurface* surf = static_cast<DataSourceSurface*>(aContext);
118 if (surf) {
119 surf->Unmap();
120 surf->Release();
121 }
122 }
123
WriteRGBXFormat(uint8_t * aData,const IntSize & aSize,const int32_t aStride,SurfaceFormat aFormat)124 static void WriteRGBXFormat(uint8_t* aData, const IntSize& aSize,
125 const int32_t aStride, SurfaceFormat aFormat) {
126 if (aFormat != SurfaceFormat::B8G8R8X8 || aSize.IsEmpty()) {
127 return;
128 }
129
130 SwizzleData(aData, aStride, SurfaceFormat::X8R8G8B8_UINT32, aData, aStride,
131 SurfaceFormat::A8R8G8B8_UINT32, aSize);
132 }
133
134 #ifdef DEBUG
CalculateSurfaceBounds(const IntSize & aSize,const Rect * aBounds,const Matrix * aMatrix)135 static IntRect CalculateSurfaceBounds(const IntSize& aSize, const Rect* aBounds,
136 const Matrix* aMatrix) {
137 IntRect surfaceBounds(IntPoint(0, 0), aSize);
138 if (!aBounds) {
139 return surfaceBounds;
140 }
141
142 MOZ_ASSERT(aMatrix);
143 Matrix inverse(*aMatrix);
144 if (!inverse.Invert()) {
145 return surfaceBounds;
146 }
147
148 IntRect bounds;
149 Rect sampledBounds = inverse.TransformBounds(*aBounds);
150 if (!sampledBounds.ToIntRect(&bounds)) {
151 return surfaceBounds;
152 }
153
154 return surfaceBounds.Intersect(bounds);
155 }
156
157 static const int kARGBAlphaOffset = 0;
158
VerifyRGBXFormat(uint8_t * aData,const IntSize & aSize,const int32_t aStride,SurfaceFormat aFormat)159 static bool VerifyRGBXFormat(uint8_t* aData, const IntSize& aSize,
160 const int32_t aStride, SurfaceFormat aFormat) {
161 if (aFormat != SurfaceFormat::B8G8R8X8 || aSize.IsEmpty()) {
162 return true;
163 }
164 // We should've initialized the data to be opaque already
165 // On debug builds, verify that this is actually true.
166 int height = aSize.height;
167 int width = aSize.width * 4;
168
169 for (int row = 0; row < height; ++row) {
170 for (int column = 0; column < width; column += 4) {
171 if (aData[column + kARGBAlphaOffset] != 0xFF) {
172 gfxCriticalError() << "RGBX pixel at (" << column << "," << row
173 << ") in " << width << "x" << height
174 << " surface is not opaque: " << int(aData[column])
175 << "," << int(aData[column + 1]) << ","
176 << int(aData[column + 2]) << ","
177 << int(aData[column + 3]);
178 }
179 }
180 aData += aStride;
181 }
182
183 return true;
184 }
185
186 // Since checking every pixel is expensive, this only checks the four corners
187 // and center of a surface that their alpha value is 0xFF.
VerifyRGBXCorners(uint8_t * aData,const IntSize & aSize,const int32_t aStride,SurfaceFormat aFormat,const Rect * aBounds=nullptr,const Matrix * aMatrix=nullptr)188 static bool VerifyRGBXCorners(uint8_t* aData, const IntSize& aSize,
189 const int32_t aStride, SurfaceFormat aFormat,
190 const Rect* aBounds = nullptr,
191 const Matrix* aMatrix = nullptr) {
192 if (aFormat != SurfaceFormat::B8G8R8X8 || aSize.IsEmpty()) {
193 return true;
194 }
195
196 IntRect bounds = CalculateSurfaceBounds(aSize, aBounds, aMatrix);
197 if (bounds.IsEmpty()) {
198 return true;
199 }
200
201 const int height = bounds.Height();
202 const int width = bounds.Width();
203 const int pixelSize = 4;
204 MOZ_ASSERT(aSize.width * pixelSize <= aStride);
205
206 const int translation = bounds.Y() * aStride + bounds.X() * pixelSize;
207 const int topLeft = translation;
208 const int topRight = topLeft + (width - 1) * pixelSize;
209 const int bottomLeft = translation + (height - 1) * aStride;
210 const int bottomRight = bottomLeft + (width - 1) * pixelSize;
211
212 // Lastly the center pixel
213 const int middleRowHeight = height / 2;
214 const int middleRowWidth = (width / 2) * pixelSize;
215 const int middle = translation + aStride * middleRowHeight + middleRowWidth;
216
217 const int offsets[] = {topLeft, topRight, bottomRight, bottomLeft, middle};
218 for (int offset : offsets) {
219 if (aData[offset + kARGBAlphaOffset] != 0xFF) {
220 int row = offset / aStride;
221 int column = (offset % aStride) / pixelSize;
222 gfxCriticalError() << "RGBX corner pixel at (" << column << "," << row
223 << ") in " << aSize.width << "x" << aSize.height
224 << " surface, bounded by "
225 << "(" << bounds.X() << "," << bounds.Y() << ","
226 << width << "," << height
227 << ") is not opaque: " << int(aData[offset]) << ","
228 << int(aData[offset + 1]) << ","
229 << int(aData[offset + 2]) << ","
230 << int(aData[offset + 3]);
231 }
232 }
233
234 return true;
235 }
236 #endif
237
GetSkImageForSurface(SourceSurface * aSurface,Maybe<MutexAutoLock> * aLock,const Rect * aBounds=nullptr,const Matrix * aMatrix=nullptr)238 static sk_sp<SkImage> GetSkImageForSurface(SourceSurface* aSurface,
239 Maybe<MutexAutoLock>* aLock,
240 const Rect* aBounds = nullptr,
241 const Matrix* aMatrix = nullptr) {
242 if (!aSurface) {
243 gfxDebug() << "Creating null Skia image from null SourceSurface";
244 return nullptr;
245 }
246
247 if (aSurface->GetType() == SurfaceType::SKIA) {
248 return static_cast<SourceSurfaceSkia*>(aSurface)->GetImage(aLock);
249 }
250
251 RefPtr<DataSourceSurface> dataSurface = aSurface->GetDataSurface();
252 if (!dataSurface) {
253 gfxWarning() << "Failed getting DataSourceSurface for Skia image";
254 return nullptr;
255 }
256
257 DataSourceSurface::MappedSurface map;
258 SkImage::RasterReleaseProc releaseProc;
259 if (dataSurface->GetType() == SurfaceType::DATA_SHARED_WRAPPER) {
260 // Technically all surfaces should be mapped and unmapped explicitly but it
261 // appears SourceSurfaceSkia and DataSourceSurfaceWrapper have issues with
262 // this. For now, we just map SourceSurfaceSharedDataWrapper to ensure we
263 // don't unmap the data during the transaction (for blob images).
264 if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
265 gfxWarning() << "Failed mapping DataSourceSurface for Skia image";
266 return nullptr;
267 }
268 releaseProc = ReleaseTemporaryMappedSurface;
269 } else {
270 map.mData = dataSurface->GetData();
271 map.mStride = dataSurface->Stride();
272 releaseProc = ReleaseTemporarySurface;
273 }
274
275 DataSourceSurface* surf = dataSurface.forget().take();
276
277 // Skia doesn't support RGBX surfaces so ensure that the alpha value is opaque
278 // white.
279 MOZ_ASSERT(VerifyRGBXCorners(map.mData, surf->GetSize(), map.mStride,
280 surf->GetFormat(), aBounds, aMatrix));
281
282 SkPixmap pixmap(MakeSkiaImageInfo(surf->GetSize(), surf->GetFormat()),
283 map.mData, map.mStride);
284 sk_sp<SkImage> image = SkImage::MakeFromRaster(pixmap, releaseProc, surf);
285 if (!image) {
286 releaseProc(map.mData, surf);
287 gfxDebug() << "Failed making Skia raster image for temporary surface";
288 }
289
290 return image;
291 }
292
DrawTargetSkia()293 DrawTargetSkia::DrawTargetSkia()
294 : mCanvas(nullptr), mSnapshot(nullptr), mSnapshotLock {
295 "DrawTargetSkia::mSnapshotLock"
296 }
297 #ifdef MOZ_WIDGET_COCOA
298 , mCG(nullptr), mColorSpace(nullptr), mCanvasData(nullptr), mCGSize(0, 0),
299 mNeedLayer(false)
300 #endif
301 {
302 }
303
~DrawTargetSkia()304 DrawTargetSkia::~DrawTargetSkia() {
305 if (mSnapshot) {
306 MutexAutoLock lock(mSnapshotLock);
307 // We're going to go away, hand our SkSurface to the SourceSurface.
308 mSnapshot->GiveSurface(mSurface.forget().take());
309 }
310
311 #ifdef MOZ_WIDGET_COCOA
312 if (mCG) {
313 CGContextRelease(mCG);
314 mCG = nullptr;
315 }
316
317 if (mColorSpace) {
318 CGColorSpaceRelease(mColorSpace);
319 mColorSpace = nullptr;
320 }
321 #endif
322 }
323
Snapshot(SurfaceFormat aFormat)324 already_AddRefed<SourceSurface> DrawTargetSkia::Snapshot(
325 SurfaceFormat aFormat) {
326 // Without this lock, this could cause us to get out a snapshot and race with
327 // Snapshot::~Snapshot() actually destroying itself.
328 MutexAutoLock lock(mSnapshotLock);
329 if (mSnapshot && aFormat != mSnapshot->GetFormat()) {
330 if (!mSnapshot->hasOneRef()) {
331 mSnapshot->DrawTargetWillChange();
332 }
333 mSnapshot = nullptr;
334 }
335 RefPtr<SourceSurfaceSkia> snapshot = mSnapshot;
336 if (mSurface && !snapshot) {
337 snapshot = new SourceSurfaceSkia();
338 sk_sp<SkImage> image;
339 // If the surface is raster, making a snapshot may trigger a pixel copy.
340 // Instead, try to directly make a raster image referencing the surface
341 // pixels.
342 SkPixmap pixmap;
343 if (mSurface->peekPixels(&pixmap)) {
344 image = SkImage::MakeFromRaster(pixmap, nullptr, nullptr);
345 } else {
346 image = mSurface->makeImageSnapshot();
347 }
348 if (!snapshot->InitFromImage(image, aFormat, this)) {
349 return nullptr;
350 }
351 mSnapshot = snapshot;
352 }
353
354 return snapshot.forget();
355 }
356
GetBackingSurface()357 already_AddRefed<SourceSurface> DrawTargetSkia::GetBackingSurface() {
358 if (mBackingSurface) {
359 RefPtr<SourceSurface> snapshot = mBackingSurface;
360 return snapshot.forget();
361 }
362 return Snapshot();
363 }
364
LockBits(uint8_t ** aData,IntSize * aSize,int32_t * aStride,SurfaceFormat * aFormat,IntPoint * aOrigin)365 bool DrawTargetSkia::LockBits(uint8_t** aData, IntSize* aSize, int32_t* aStride,
366 SurfaceFormat* aFormat, IntPoint* aOrigin) {
367 SkImageInfo info;
368 size_t rowBytes;
369 SkIPoint origin;
370 void* pixels = mCanvas->accessTopLayerPixels(&info, &rowBytes, &origin);
371 if (!pixels ||
372 // Ensure the layer is at the origin if required.
373 (!aOrigin && !origin.isZero())) {
374 return false;
375 }
376
377 MarkChanged();
378
379 *aData = reinterpret_cast<uint8_t*>(pixels);
380 *aSize = IntSize(info.width(), info.height());
381 *aStride = int32_t(rowBytes);
382 *aFormat = SkiaColorTypeToGfxFormat(info.colorType(), info.alphaType());
383 if (aOrigin) {
384 *aOrigin = IntPoint(origin.x(), origin.y());
385 }
386 return true;
387 }
388
ReleaseBits(uint8_t * aData)389 void DrawTargetSkia::ReleaseBits(uint8_t* aData) {}
390
ReleaseImage(const void * aPixels,void * aContext)391 static void ReleaseImage(const void* aPixels, void* aContext) {
392 SkImage* image = static_cast<SkImage*>(aContext);
393 SkSafeUnref(image);
394 }
395
ExtractSubset(sk_sp<SkImage> aImage,const IntRect & aRect)396 static sk_sp<SkImage> ExtractSubset(sk_sp<SkImage> aImage,
397 const IntRect& aRect) {
398 SkIRect subsetRect = IntRectToSkIRect(aRect);
399 if (aImage->bounds() == subsetRect) {
400 return aImage;
401 }
402 // makeSubset is slow, so prefer to use SkPixmap::extractSubset where
403 // possible.
404 SkPixmap pixmap, subsetPixmap;
405 if (aImage->peekPixels(&pixmap) &&
406 pixmap.extractSubset(&subsetPixmap, subsetRect)) {
407 // Release the original image reference so only the subset image keeps it
408 // alive.
409 return SkImage::MakeFromRaster(subsetPixmap, ReleaseImage,
410 aImage.release());
411 }
412 return aImage->makeSubset(subsetRect);
413 }
414
FreeBitmapPixels(void * aBuf,void *)415 static void FreeBitmapPixels(void* aBuf, void*) { sk_free(aBuf); }
416
ExtractAlphaBitmap(const sk_sp<SkImage> & aImage,SkBitmap * aResultBitmap)417 static bool ExtractAlphaBitmap(const sk_sp<SkImage>& aImage,
418 SkBitmap* aResultBitmap) {
419 SkImageInfo info = SkImageInfo::MakeA8(aImage->width(), aImage->height());
420 // Skia does not fully allocate the last row according to stride.
421 // Since some of our algorithms (i.e. blur) depend on this, we must allocate
422 // the bitmap pixels manually.
423 size_t stride = SkAlign4(info.minRowBytes());
424 CheckedInt<size_t> size = stride;
425 size *= info.height();
426 if (size.isValid()) {
427 void* buf = sk_malloc_flags(size.value(), 0);
428 if (buf) {
429 SkBitmap bitmap;
430 if (bitmap.installPixels(info, buf, stride, FreeBitmapPixels, nullptr) &&
431 aImage->readPixels(bitmap.info(), bitmap.getPixels(),
432 bitmap.rowBytes(), 0, 0)) {
433 *aResultBitmap = bitmap;
434 return true;
435 }
436 }
437 }
438
439 gfxWarning() << "Failed reading alpha pixels for Skia bitmap";
440 return false;
441 }
442
ExtractAlphaForSurface(SourceSurface * aSurface,Maybe<MutexAutoLock> & aLock)443 static sk_sp<SkImage> ExtractAlphaForSurface(SourceSurface* aSurface,
444 Maybe<MutexAutoLock>& aLock) {
445 sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &aLock);
446 if (!image) {
447 return nullptr;
448 }
449 if (image->isAlphaOnly()) {
450 return image;
451 }
452
453 SkBitmap bitmap;
454 if (!ExtractAlphaBitmap(image, &bitmap)) {
455 return nullptr;
456 }
457
458 // Mark the bitmap immutable so that it will be shared rather than copied.
459 bitmap.setImmutable();
460 return SkImage::MakeFromBitmap(bitmap);
461 }
462
SetPaintPattern(SkPaint & aPaint,const Pattern & aPattern,Maybe<MutexAutoLock> & aLock,Float aAlpha=1.0,const SkMatrix * aMatrix=nullptr,const Rect * aBounds=nullptr)463 static void SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern,
464 Maybe<MutexAutoLock>& aLock, Float aAlpha = 1.0,
465 const SkMatrix* aMatrix = nullptr,
466 const Rect* aBounds = nullptr) {
467 switch (aPattern.GetType()) {
468 case PatternType::COLOR: {
469 DeviceColor color = static_cast<const ColorPattern&>(aPattern).mColor;
470 aPaint.setColor(ColorToSkColor(color, aAlpha));
471 break;
472 }
473 case PatternType::LINEAR_GRADIENT: {
474 const LinearGradientPattern& pat =
475 static_cast<const LinearGradientPattern&>(aPattern);
476 GradientStopsSkia* stops =
477 static_cast<GradientStopsSkia*>(pat.mStops.get());
478 if (!stops || stops->mCount < 2 || !pat.mBegin.IsFinite() ||
479 !pat.mEnd.IsFinite() || pat.mBegin == pat.mEnd) {
480 aPaint.setColor(SK_ColorTRANSPARENT);
481 } else {
482 SkTileMode mode = ExtendModeToTileMode(stops->mExtendMode, Axis::BOTH);
483 SkPoint points[2];
484 points[0] = SkPoint::Make(SkFloatToScalar(pat.mBegin.x),
485 SkFloatToScalar(pat.mBegin.y));
486 points[1] = SkPoint::Make(SkFloatToScalar(pat.mEnd.x),
487 SkFloatToScalar(pat.mEnd.y));
488
489 SkMatrix mat;
490 GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
491 if (aMatrix) {
492 mat.postConcat(*aMatrix);
493 }
494 sk_sp<SkShader> shader = SkGradientShader::MakeLinear(
495 points, &stops->mColors.front(), &stops->mPositions.front(),
496 stops->mCount, mode, 0, &mat);
497 if (shader) {
498 aPaint.setShader(shader);
499 } else {
500 aPaint.setColor(SK_ColorTRANSPARENT);
501 }
502 }
503 break;
504 }
505 case PatternType::RADIAL_GRADIENT: {
506 const RadialGradientPattern& pat =
507 static_cast<const RadialGradientPattern&>(aPattern);
508 GradientStopsSkia* stops =
509 static_cast<GradientStopsSkia*>(pat.mStops.get());
510 if (!stops || stops->mCount < 2 || !pat.mCenter1.IsFinite() ||
511 !IsFinite(pat.mRadius1) || !pat.mCenter2.IsFinite() ||
512 !IsFinite(pat.mRadius2) ||
513 (pat.mCenter1 == pat.mCenter2 && pat.mRadius1 == pat.mRadius2)) {
514 aPaint.setColor(SK_ColorTRANSPARENT);
515 } else {
516 SkTileMode mode = ExtendModeToTileMode(stops->mExtendMode, Axis::BOTH);
517 SkPoint points[2];
518 points[0] = SkPoint::Make(SkFloatToScalar(pat.mCenter1.x),
519 SkFloatToScalar(pat.mCenter1.y));
520 points[1] = SkPoint::Make(SkFloatToScalar(pat.mCenter2.x),
521 SkFloatToScalar(pat.mCenter2.y));
522
523 SkMatrix mat;
524 GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
525 if (aMatrix) {
526 mat.postConcat(*aMatrix);
527 }
528 sk_sp<SkShader> shader = SkGradientShader::MakeTwoPointConical(
529 points[0], SkFloatToScalar(pat.mRadius1), points[1],
530 SkFloatToScalar(pat.mRadius2), &stops->mColors.front(),
531 &stops->mPositions.front(), stops->mCount, mode, 0, &mat);
532 if (shader) {
533 aPaint.setShader(shader);
534 } else {
535 aPaint.setColor(SK_ColorTRANSPARENT);
536 }
537 }
538 break;
539 }
540 case PatternType::CONIC_GRADIENT: {
541 const ConicGradientPattern& pat =
542 static_cast<const ConicGradientPattern&>(aPattern);
543 GradientStopsSkia* stops =
544 static_cast<GradientStopsSkia*>(pat.mStops.get());
545 if (!stops || stops->mCount < 2 || !pat.mCenter.IsFinite() ||
546 !IsFinite(pat.mAngle)) {
547 aPaint.setColor(SK_ColorTRANSPARENT);
548 } else {
549 SkMatrix mat;
550 GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
551 if (aMatrix) {
552 mat.postConcat(*aMatrix);
553 }
554
555 SkScalar cx = SkFloatToScalar(pat.mCenter.x);
556 SkScalar cy = SkFloatToScalar(pat.mCenter.y);
557
558 // Skia's sweep gradient angles are relative to the x-axis, not the
559 // y-axis.
560 Float angle = (pat.mAngle * 180.0 / M_PI) - 90.0;
561 if (angle != 0.0) {
562 mat.preRotate(angle, cx, cy);
563 }
564
565 SkTileMode mode = ExtendModeToTileMode(stops->mExtendMode, Axis::BOTH);
566 sk_sp<SkShader> shader = SkGradientShader::MakeSweep(
567 cx, cy, &stops->mColors.front(), &stops->mPositions.front(),
568 stops->mCount, mode, 360 * pat.mStartOffset, 360 * pat.mEndOffset,
569 0, &mat);
570
571 if (shader) {
572 aPaint.setShader(shader);
573 } else {
574 aPaint.setColor(SK_ColorTRANSPARENT);
575 }
576 }
577 break;
578 }
579 case PatternType::SURFACE: {
580 const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern);
581 sk_sp<SkImage> image =
582 GetSkImageForSurface(pat.mSurface, &aLock, aBounds, &pat.mMatrix);
583 if (!image) {
584 aPaint.setColor(SK_ColorTRANSPARENT);
585 break;
586 }
587
588 SkMatrix mat;
589 GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
590 if (aMatrix) {
591 mat.postConcat(*aMatrix);
592 }
593
594 if (!pat.mSamplingRect.IsEmpty()) {
595 image = ExtractSubset(image, pat.mSamplingRect);
596 if (!image) {
597 aPaint.setColor(SK_ColorTRANSPARENT);
598 break;
599 }
600 mat.preTranslate(pat.mSamplingRect.X(), pat.mSamplingRect.Y());
601 }
602
603 SkTileMode xTile = ExtendModeToTileMode(pat.mExtendMode, Axis::X_AXIS);
604 SkTileMode yTile = ExtendModeToTileMode(pat.mExtendMode, Axis::Y_AXIS);
605
606 sk_sp<SkShader> shader = image->makeShader(xTile, yTile, &mat);
607 if (shader) {
608 aPaint.setShader(shader);
609 } else {
610 gfxDebug() << "Failed creating Skia surface shader: x-tile="
611 << (int)xTile << " y-tile=" << (int)yTile
612 << " matrix=" << (mat.isFinite() ? "finite" : "non-finite");
613 aPaint.setColor(SK_ColorTRANSPARENT);
614 }
615
616 if (pat.mSamplingFilter == SamplingFilter::POINT) {
617 aPaint.setFilterQuality(kNone_SkFilterQuality);
618 }
619 break;
620 }
621 }
622 }
623
GetClipBounds(SkCanvas * aCanvas)624 static inline Rect GetClipBounds(SkCanvas* aCanvas) {
625 // Use a manually transformed getClipDeviceBounds instead of
626 // getClipBounds because getClipBounds inflates the the bounds
627 // by a pixel in each direction to compensate for antialiasing.
628 SkIRect deviceBounds;
629 if (!aCanvas->getDeviceClipBounds(&deviceBounds)) {
630 return Rect();
631 }
632 SkMatrix inverseCTM;
633 if (!aCanvas->getTotalMatrix().invert(&inverseCTM)) {
634 return Rect();
635 }
636 SkRect localBounds;
637 inverseCTM.mapRect(&localBounds, SkRect::Make(deviceBounds));
638 return SkRectToRect(localBounds);
639 }
640
641 struct AutoPaintSetup {
AutoPaintSetupmozilla::gfx::AutoPaintSetup642 AutoPaintSetup(SkCanvas* aCanvas, const DrawOptions& aOptions,
643 const Pattern& aPattern, const Rect* aMaskBounds = nullptr,
644 const SkMatrix* aMatrix = nullptr,
645 const Rect* aSourceBounds = nullptr)
646 : mNeedsRestore(false), mAlpha(1.0) {
647 Init(aCanvas, aOptions, aMaskBounds, false);
648 SetPaintPattern(mPaint, aPattern, mLock, mAlpha, aMatrix, aSourceBounds);
649 }
650
AutoPaintSetupmozilla::gfx::AutoPaintSetup651 AutoPaintSetup(SkCanvas* aCanvas, const DrawOptions& aOptions,
652 const Rect* aMaskBounds = nullptr, bool aForceGroup = false)
653 : mNeedsRestore(false), mAlpha(1.0) {
654 Init(aCanvas, aOptions, aMaskBounds, aForceGroup);
655 }
656
~AutoPaintSetupmozilla::gfx::AutoPaintSetup657 ~AutoPaintSetup() {
658 if (mNeedsRestore) {
659 mCanvas->restore();
660 }
661 }
662
Initmozilla::gfx::AutoPaintSetup663 void Init(SkCanvas* aCanvas, const DrawOptions& aOptions,
664 const Rect* aMaskBounds, bool aForceGroup) {
665 mPaint.setBlendMode(GfxOpToSkiaOp(aOptions.mCompositionOp));
666 mCanvas = aCanvas;
667
668 // TODO: Can we set greyscale somehow?
669 if (aOptions.mAntialiasMode != AntialiasMode::NONE) {
670 mPaint.setAntiAlias(true);
671 } else {
672 mPaint.setAntiAlias(false);
673 }
674
675 bool needsGroup =
676 aForceGroup ||
677 (!IsOperatorBoundByMask(aOptions.mCompositionOp) &&
678 (!aMaskBounds || !aMaskBounds->Contains(GetClipBounds(aCanvas))));
679
680 // TODO: We could skip the temporary for operator_source and just
681 // clear the clip rect. The other operators would be harder
682 // but could be worth it to skip pushing a group.
683 if (needsGroup) {
684 mPaint.setBlendMode(SkBlendMode::kSrcOver);
685 SkPaint temp;
686 temp.setBlendMode(GfxOpToSkiaOp(aOptions.mCompositionOp));
687 temp.setAlpha(ColorFloatToByte(aOptions.mAlpha));
688 // TODO: Get a rect here
689 SkCanvas::SaveLayerRec rec(nullptr, &temp,
690 SkCanvas::kPreserveLCDText_SaveLayerFlag);
691 mCanvas->saveLayer(rec);
692 mNeedsRestore = true;
693 } else {
694 mPaint.setAlpha(ColorFloatToByte(aOptions.mAlpha));
695 mAlpha = aOptions.mAlpha;
696 }
697 mPaint.setFilterQuality(kLow_SkFilterQuality);
698 }
699
700 // TODO: Maybe add an operator overload to access this easier?
701 SkPaint mPaint;
702 bool mNeedsRestore;
703 SkCanvas* mCanvas;
704 Maybe<MutexAutoLock> mLock;
705 Float mAlpha;
706 };
707
Flush()708 void DrawTargetSkia::Flush() { mCanvas->flush(); }
709
DrawSurface(SourceSurface * aSurface,const Rect & aDest,const Rect & aSource,const DrawSurfaceOptions & aSurfOptions,const DrawOptions & aOptions)710 void DrawTargetSkia::DrawSurface(SourceSurface* aSurface, const Rect& aDest,
711 const Rect& aSource,
712 const DrawSurfaceOptions& aSurfOptions,
713 const DrawOptions& aOptions) {
714 if (aSource.IsEmpty()) {
715 return;
716 }
717
718 MarkChanged();
719
720 Maybe<MutexAutoLock> lock;
721 sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
722 if (!image) {
723 return;
724 }
725
726 SkRect destRect = RectToSkRect(aDest);
727 SkRect sourceRect = RectToSkRect(aSource - aSurface->GetRect().TopLeft());
728 bool forceGroup =
729 image->isAlphaOnly() && aOptions.mCompositionOp != CompositionOp::OP_OVER;
730
731 AutoPaintSetup paint(mCanvas, aOptions, &aDest, forceGroup);
732 if (aSurfOptions.mSamplingFilter == SamplingFilter::POINT) {
733 paint.mPaint.setFilterQuality(kNone_SkFilterQuality);
734 }
735
736 mCanvas->drawImageRect(image, sourceRect, destRect, &paint.mPaint);
737 }
738
GetType() const739 DrawTargetType DrawTargetSkia::GetType() const {
740 return DrawTargetType::SOFTWARE_RASTER;
741 }
742
DrawFilter(FilterNode * aNode,const Rect & aSourceRect,const Point & aDestPoint,const DrawOptions & aOptions)743 void DrawTargetSkia::DrawFilter(FilterNode* aNode, const Rect& aSourceRect,
744 const Point& aDestPoint,
745 const DrawOptions& aOptions) {
746 FilterNodeSoftware* filter = static_cast<FilterNodeSoftware*>(aNode);
747 filter->Draw(this, aSourceRect, aDestPoint, aOptions);
748 }
749
DrawSurfaceWithShadow(SourceSurface * aSurface,const Point & aDest,const DeviceColor & aColor,const Point & aOffset,Float aSigma,CompositionOp aOperator)750 void DrawTargetSkia::DrawSurfaceWithShadow(SourceSurface* aSurface,
751 const Point& aDest,
752 const DeviceColor& aColor,
753 const Point& aOffset, Float aSigma,
754 CompositionOp aOperator) {
755 if (aSurface->GetSize().IsEmpty()) {
756 return;
757 }
758
759 MarkChanged();
760
761 Maybe<MutexAutoLock> lock;
762 sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
763 if (!image) {
764 return;
765 }
766
767 mCanvas->save();
768 mCanvas->resetMatrix();
769
770 SkPaint paint;
771 paint.setBlendMode(GfxOpToSkiaOp(aOperator));
772
773 // bug 1201272
774 // We can't use the SkDropShadowImageFilter here because it applies the xfer
775 // mode first to render the bitmap to a temporary layer, and then implicitly
776 // uses src-over to composite the resulting shadow.
777 // The canvas spec, however, states that the composite op must be used to
778 // composite the resulting shadow, so we must instead use a SkBlurImageFilter
779 // to blur the image ourselves.
780
781 SkPaint shadowPaint;
782 shadowPaint.setBlendMode(GfxOpToSkiaOp(aOperator));
783
784 auto shadowDest = IntPoint::Round(aDest + aOffset);
785
786 SkBitmap blurMask;
787 if (ExtractAlphaBitmap(image, &blurMask)) {
788 // Prefer using our own box blur instead of Skia's. It currently performs
789 // much better than SkBlurImageFilter or SkBlurMaskFilter on the CPU.
790 AlphaBoxBlur blur(Rect(0, 0, blurMask.width(), blurMask.height()),
791 int32_t(blurMask.rowBytes()), aSigma, aSigma);
792 blur.Blur(reinterpret_cast<uint8_t*>(blurMask.getPixels()));
793 blurMask.notifyPixelsChanged();
794
795 shadowPaint.setColor(ColorToSkColor(aColor, 1.0f));
796
797 mCanvas->drawBitmap(blurMask, shadowDest.x, shadowDest.y, &shadowPaint);
798 } else {
799 sk_sp<SkImageFilter> blurFilter(
800 SkBlurImageFilter::Make(aSigma, aSigma, nullptr));
801 sk_sp<SkColorFilter> colorFilter(SkColorFilters::Blend(
802 ColorToSkColor(aColor, 1.0f), SkBlendMode::kSrcIn));
803
804 shadowPaint.setImageFilter(blurFilter);
805 shadowPaint.setColorFilter(colorFilter);
806
807 mCanvas->drawImage(image, shadowDest.x, shadowDest.y, &shadowPaint);
808 }
809
810 if (aSurface->GetFormat() != SurfaceFormat::A8) {
811 // Composite the original image after the shadow
812 auto dest = IntPoint::Round(aDest);
813 mCanvas->drawImage(image, dest.x, dest.y, &paint);
814 }
815
816 mCanvas->restore();
817 }
818
FillRect(const Rect & aRect,const Pattern & aPattern,const DrawOptions & aOptions)819 void DrawTargetSkia::FillRect(const Rect& aRect, const Pattern& aPattern,
820 const DrawOptions& aOptions) {
821 // The sprite blitting path in Skia can be faster than the shader blitter for
822 // operators other than source (or source-over with opaque surface). So, when
823 // possible/beneficial, route to DrawSurface which will use the sprite
824 // blitter.
825 if (aPattern.GetType() == PatternType::SURFACE &&
826 aOptions.mCompositionOp != CompositionOp::OP_SOURCE) {
827 const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern);
828 // Verify there is a valid surface and a pattern matrix without skew.
829 if (pat.mSurface &&
830 (aOptions.mCompositionOp != CompositionOp::OP_OVER ||
831 GfxFormatToSkiaAlphaType(pat.mSurface->GetFormat()) !=
832 kOpaque_SkAlphaType) &&
833 !pat.mMatrix.HasNonAxisAlignedTransform()) {
834 // Bound the sampling to smaller of the bounds or the sampling rect.
835 IntRect srcRect(IntPoint(0, 0), pat.mSurface->GetSize());
836 if (!pat.mSamplingRect.IsEmpty()) {
837 srcRect = srcRect.Intersect(pat.mSamplingRect);
838 }
839 // Transform the destination rectangle by the inverse of the pattern
840 // matrix so that it is in pattern space like the source rectangle.
841 Rect patRect = aRect - pat.mMatrix.GetTranslation();
842 patRect.Scale(1.0f / pat.mMatrix._11, 1.0f / pat.mMatrix._22);
843 // Verify the pattern rectangle will not tile or clamp.
844 if (!patRect.IsEmpty() && srcRect.Contains(RoundedOut(patRect))) {
845 // The pattern is a surface with an axis-aligned source rectangle
846 // fitting entirely in its bounds, so just treat it as a DrawSurface.
847 DrawSurface(pat.mSurface, aRect, patRect,
848 DrawSurfaceOptions(pat.mSamplingFilter), aOptions);
849 return;
850 }
851 }
852 }
853
854 MarkChanged();
855 SkRect rect = RectToSkRect(aRect);
856 AutoPaintSetup paint(mCanvas, aOptions, aPattern, &aRect, nullptr, &aRect);
857
858 mCanvas->drawRect(rect, paint.mPaint);
859 }
860
Stroke(const Path * aPath,const Pattern & aPattern,const StrokeOptions & aStrokeOptions,const DrawOptions & aOptions)861 void DrawTargetSkia::Stroke(const Path* aPath, const Pattern& aPattern,
862 const StrokeOptions& aStrokeOptions,
863 const DrawOptions& aOptions) {
864 MarkChanged();
865 MOZ_ASSERT(aPath, "Null path");
866 if (aPath->GetBackendType() != BackendType::SKIA) {
867 return;
868 }
869
870 const PathSkia* skiaPath = static_cast<const PathSkia*>(aPath);
871
872 AutoPaintSetup paint(mCanvas, aOptions, aPattern);
873 if (!StrokeOptionsToPaint(paint.mPaint, aStrokeOptions)) {
874 return;
875 }
876
877 if (!skiaPath->GetPath().isFinite()) {
878 return;
879 }
880
881 mCanvas->drawPath(skiaPath->GetPath(), paint.mPaint);
882 }
883
DashPeriodLength(const StrokeOptions & aStrokeOptions)884 static Double DashPeriodLength(const StrokeOptions& aStrokeOptions) {
885 Double length = 0;
886 for (size_t i = 0; i < aStrokeOptions.mDashLength; i++) {
887 length += aStrokeOptions.mDashPattern[i];
888 }
889 if (aStrokeOptions.mDashLength & 1) {
890 // "If an odd number of values is provided, then the list of values is
891 // repeated to yield an even number of values."
892 // Double the length.
893 length += length;
894 }
895 return length;
896 }
897
RoundDownToMultiple(Double aValue,Double aFactor)898 static inline Double RoundDownToMultiple(Double aValue, Double aFactor) {
899 return floor(aValue / aFactor) * aFactor;
900 }
901
UserSpaceStrokeClip(const IntRect & aDeviceClip,const Matrix & aTransform,const StrokeOptions & aStrokeOptions)902 static Rect UserSpaceStrokeClip(const IntRect& aDeviceClip,
903 const Matrix& aTransform,
904 const StrokeOptions& aStrokeOptions) {
905 Matrix inverse = aTransform;
906 if (!inverse.Invert()) {
907 return Rect();
908 }
909 Rect deviceClip(aDeviceClip);
910 deviceClip.Inflate(MaxStrokeExtents(aStrokeOptions, aTransform));
911 return inverse.TransformBounds(deviceClip);
912 }
913
ShrinkClippedStrokedRect(const Rect & aStrokedRect,const IntRect & aDeviceClip,const Matrix & aTransform,const StrokeOptions & aStrokeOptions)914 static Rect ShrinkClippedStrokedRect(const Rect& aStrokedRect,
915 const IntRect& aDeviceClip,
916 const Matrix& aTransform,
917 const StrokeOptions& aStrokeOptions) {
918 Rect userSpaceStrokeClip =
919 UserSpaceStrokeClip(aDeviceClip, aTransform, aStrokeOptions);
920 RectDouble strokedRectDouble(aStrokedRect.X(), aStrokedRect.Y(),
921 aStrokedRect.Width(), aStrokedRect.Height());
922 RectDouble intersection = strokedRectDouble.Intersect(
923 RectDouble(userSpaceStrokeClip.X(), userSpaceStrokeClip.Y(),
924 userSpaceStrokeClip.Width(), userSpaceStrokeClip.Height()));
925 Double dashPeriodLength = DashPeriodLength(aStrokeOptions);
926 if (intersection.IsEmpty() || dashPeriodLength == 0.0f) {
927 return Rect(intersection.X(), intersection.Y(), intersection.Width(),
928 intersection.Height());
929 }
930
931 // Reduce the rectangle side lengths in multiples of the dash period length
932 // so that the visible dashes stay in the same place.
933 MarginDouble insetBy = strokedRectDouble - intersection;
934 insetBy.top = RoundDownToMultiple(insetBy.top, dashPeriodLength);
935 insetBy.right = RoundDownToMultiple(insetBy.right, dashPeriodLength);
936 insetBy.bottom = RoundDownToMultiple(insetBy.bottom, dashPeriodLength);
937 insetBy.left = RoundDownToMultiple(insetBy.left, dashPeriodLength);
938
939 strokedRectDouble.Deflate(insetBy);
940 return Rect(strokedRectDouble.X(), strokedRectDouble.Y(),
941 strokedRectDouble.Width(), strokedRectDouble.Height());
942 }
943
StrokeRect(const Rect & aRect,const Pattern & aPattern,const StrokeOptions & aStrokeOptions,const DrawOptions & aOptions)944 void DrawTargetSkia::StrokeRect(const Rect& aRect, const Pattern& aPattern,
945 const StrokeOptions& aStrokeOptions,
946 const DrawOptions& aOptions) {
947 // Stroking large rectangles with dashes is expensive with Skia (fixed
948 // overhead based on the number of dashes, regardless of whether the dashes
949 // are visible), so we try to reduce the size of the stroked rectangle as
950 // much as possible before passing it on to Skia.
951 Rect rect = aRect;
952 if (aStrokeOptions.mDashLength > 0 && !rect.IsEmpty()) {
953 IntRect deviceClip(IntPoint(0, 0), mSize);
954 SkIRect clipBounds;
955 if (mCanvas->getDeviceClipBounds(&clipBounds)) {
956 deviceClip = SkIRectToIntRect(clipBounds);
957 }
958 rect =
959 ShrinkClippedStrokedRect(rect, deviceClip, mTransform, aStrokeOptions);
960 if (rect.IsEmpty()) {
961 return;
962 }
963 }
964
965 MarkChanged();
966 AutoPaintSetup paint(mCanvas, aOptions, aPattern);
967 if (!StrokeOptionsToPaint(paint.mPaint, aStrokeOptions)) {
968 return;
969 }
970
971 mCanvas->drawRect(RectToSkRect(rect), paint.mPaint);
972 }
973
StrokeLine(const Point & aStart,const Point & aEnd,const Pattern & aPattern,const StrokeOptions & aStrokeOptions,const DrawOptions & aOptions)974 void DrawTargetSkia::StrokeLine(const Point& aStart, const Point& aEnd,
975 const Pattern& aPattern,
976 const StrokeOptions& aStrokeOptions,
977 const DrawOptions& aOptions) {
978 MarkChanged();
979 AutoPaintSetup paint(mCanvas, aOptions, aPattern);
980 if (!StrokeOptionsToPaint(paint.mPaint, aStrokeOptions)) {
981 return;
982 }
983
984 mCanvas->drawLine(SkFloatToScalar(aStart.x), SkFloatToScalar(aStart.y),
985 SkFloatToScalar(aEnd.x), SkFloatToScalar(aEnd.y),
986 paint.mPaint);
987 }
988
Fill(const Path * aPath,const Pattern & aPattern,const DrawOptions & aOptions)989 void DrawTargetSkia::Fill(const Path* aPath, const Pattern& aPattern,
990 const DrawOptions& aOptions) {
991 MarkChanged();
992 if (!aPath || aPath->GetBackendType() != BackendType::SKIA) {
993 return;
994 }
995
996 const PathSkia* skiaPath = static_cast<const PathSkia*>(aPath);
997
998 AutoPaintSetup paint(mCanvas, aOptions, aPattern);
999
1000 if (!skiaPath->GetPath().isFinite()) {
1001 return;
1002 }
1003
1004 mCanvas->drawPath(skiaPath->GetPath(), paint.mPaint);
1005 }
1006
1007 #ifdef MOZ_WIDGET_COCOA
GfxMatrixToCGAffineTransform(const Matrix & m)1008 static inline CGAffineTransform GfxMatrixToCGAffineTransform(const Matrix& m) {
1009 CGAffineTransform t;
1010 t.a = m._11;
1011 t.b = m._12;
1012 t.c = m._21;
1013 t.d = m._22;
1014 t.tx = m._31;
1015 t.ty = m._32;
1016 return t;
1017 }
1018
1019 /***
1020 * We have to do a lot of work to draw glyphs with CG because
1021 * CG assumes that the origin of rects are in the bottom left
1022 * while every other DrawTarget assumes the top left is the origin.
1023 * This means we have to transform the CGContext to have rects
1024 * actually be applied in top left fashion. We do this by:
1025 *
1026 * 1) Translating the context up by the height of the canvas
1027 * 2) Flipping the context by the Y axis so it's upside down.
1028 *
1029 * These two transforms put the origin in the top left.
1030 * Transforms are better understood thinking about them from right to left order
1031 * (mathematically).
1032 *
1033 * Consider a point we want to draw at (0, 10) in normal cartesian planes with
1034 * a box of (100, 100). in CG terms, this would be at (0, 10).
1035 * Positive Y values point up.
1036 * In our DrawTarget terms, positive Y values point down, so (0, 10) would be
1037 * at (0, 90) in cartesian plane terms. That means our point at (0, 10) in
1038 * DrawTarget terms should end up at (0, 90). How does this work with the
1039 * current transforms?
1040 *
1041 * Going right to left with the transforms, a CGPoint of (0, 10) has cartesian
1042 * coordinates of (0, 10). The first flip of the Y axis puts the point now at
1043 * (0, -10); Next, we translate the context up by the size of the canvas
1044 * (Positive Y values go up in CG coordinates but down in our draw target
1045 * coordinates). Since our canvas size is (100, 100), the resulting coordinate
1046 * becomes (0, 90), which is what we expect from our DrawTarget code. These two
1047 * transforms put the CG context equal to what every other DrawTarget expects.
1048 *
1049 * Next, we need two more transforms for actual text. IF we left the transforms
1050 * as is, the text would be drawn upside down, so we need another flip of the Y
1051 * axis to draw the text right side up. However, with only the flip, the text
1052 * would be drawn in the wrong place. Thus we also have to invert the Y position
1053 * of the glyphs to get them in the right place.
1054 *
1055 * Thus we have the following transforms:
1056 * 1) Translation of the context up
1057 * 2) Flipping the context around the Y axis
1058 * 3) Flipping the context around the Y axis
1059 * 4) Inverting the Y position of each glyph
1060 *
1061 * We cannot cancel out (2) and (3) as we have to apply the clips and transforms
1062 * of DrawTargetSkia between (2) and (3).
1063 *
1064 * Consider the example letter P, drawn at (0, 20) in CG coordinates in a
1065 * (100, 100) rect.
1066 * Again, going right to left of the transforms. We'd get:
1067 *
1068 * 1) The letter P drawn at (0, -20) due to the inversion of the Y axis
1069 * 2) The letter P upside down (b) at (0, 20) due to the second flip
1070 * 3) The letter P right side up at (0, -20) due to the first flip
1071 * 4) The letter P right side up at (0, 80) due to the translation
1072 *
1073 * tl;dr - CGRects assume origin is bottom left, DrawTarget rects assume top
1074 * left.
1075 */
SetupCGContext(DrawTargetSkia * aDT,CGContextRef aCGContext,SkCanvas * aCanvas,const IntPoint & aOrigin,const IntSize & aSize,bool aClipped)1076 static bool SetupCGContext(DrawTargetSkia* aDT, CGContextRef aCGContext,
1077 SkCanvas* aCanvas, const IntPoint& aOrigin,
1078 const IntSize& aSize, bool aClipped) {
1079 // DrawTarget expects the origin to be at the top left, but CG
1080 // expects it to be at the bottom left. Transform to set the origin to
1081 // the top left. Have to set this before we do anything else.
1082 // This is transform (1) up top
1083 CGContextTranslateCTM(aCGContext, -aOrigin.x, aOrigin.y + aSize.height);
1084
1085 // Transform (2) from the comments.
1086 CGContextScaleCTM(aCGContext, 1, -1);
1087
1088 // Want to apply clips BEFORE the transform since the transform
1089 // will apply to the clips we apply.
1090 if (aClipped) {
1091 SkRegion clipRegion;
1092 aCanvas->temporary_internal_getRgnClip(&clipRegion);
1093 Vector<CGRect, 8> rects;
1094 for (SkRegion::Iterator it(clipRegion); !it.done(); it.next()) {
1095 const SkIRect& rect = it.rect();
1096 if (!rects.append(
1097 CGRectMake(rect.x(), rect.y(), rect.width(), rect.height()))) {
1098 break;
1099 }
1100 }
1101 if (rects.length()) {
1102 CGContextClipToRects(aCGContext, rects.begin(), rects.length());
1103 }
1104 }
1105
1106 CGContextConcatCTM(aCGContext,
1107 GfxMatrixToCGAffineTransform(aDT->GetTransform()));
1108 return true;
1109 }
1110 // End long comment about transforms.
1111
1112 // The context returned from this method will have the origin
1113 // in the top left and will have applied all the neccessary clips
1114 // and transforms to the CGContext. See the comment above
1115 // SetupCGContext.
BorrowCGContext(const DrawOptions & aOptions)1116 CGContextRef DrawTargetSkia::BorrowCGContext(const DrawOptions& aOptions) {
1117 // Since we can't replay Skia clips, we have to use a layer if we have a
1118 // complex clip. After saving a layer, the SkCanvas queries for needing a
1119 // layer change so save if we pushed a layer.
1120 mNeedLayer = !mCanvas->isClipEmpty() && !mCanvas->isClipRect();
1121 if (mNeedLayer) {
1122 SkPaint paint;
1123 paint.setBlendMode(SkBlendMode::kSrc);
1124 SkCanvas::SaveLayerRec rec(nullptr, &paint,
1125 SkCanvas::kInitWithPrevious_SaveLayerFlag);
1126 mCanvas->saveLayer(rec);
1127 }
1128
1129 uint8_t* data = nullptr;
1130 int32_t stride;
1131 SurfaceFormat format;
1132 IntSize size;
1133 IntPoint origin;
1134 if (!LockBits(&data, &size, &stride, &format, &origin)) {
1135 NS_WARNING("Could not lock skia bits to wrap CG around");
1136 return nullptr;
1137 }
1138
1139 if (!mNeedLayer && (data == mCanvasData) && mCG && (mCGSize == size)) {
1140 // If our canvas data still points to the same data,
1141 // we can reuse the CG Context
1142 CGContextSetAlpha(mCG, aOptions.mAlpha);
1143 CGContextSetShouldAntialias(mCG,
1144 aOptions.mAntialiasMode != AntialiasMode::NONE);
1145 CGContextSaveGState(mCG);
1146 SetupCGContext(this, mCG, mCanvas, origin, size, true);
1147 return mCG;
1148 }
1149
1150 if (!mColorSpace) {
1151 mColorSpace = (format == SurfaceFormat::A8) ? CGColorSpaceCreateDeviceGray()
1152 : CGColorSpaceCreateDeviceRGB();
1153 }
1154
1155 if (mCG) {
1156 // Release the old CG context since it's no longer valid.
1157 CGContextRelease(mCG);
1158 }
1159
1160 mCanvasData = data;
1161 mCGSize = size;
1162
1163 uint32_t bitmapInfo =
1164 (format == SurfaceFormat::A8)
1165 ? kCGImageAlphaOnly
1166 : kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
1167
1168 mCG = CGBitmapContextCreateWithData(
1169 mCanvasData, mCGSize.width, mCGSize.height, 8, /* bits per component */
1170 stride, mColorSpace, bitmapInfo, NULL, /* Callback when released */
1171 NULL);
1172 if (!mCG) {
1173 if (mNeedLayer) {
1174 mCanvas->restore();
1175 }
1176 ReleaseBits(mCanvasData);
1177 NS_WARNING("Could not create bitmap around skia data\n");
1178 return nullptr;
1179 }
1180
1181 CGContextSetAlpha(mCG, aOptions.mAlpha);
1182 CGContextSetShouldAntialias(mCG,
1183 aOptions.mAntialiasMode != AntialiasMode::NONE);
1184 CGContextSetShouldSmoothFonts(mCG, true);
1185 CGContextSetTextDrawingMode(mCG, kCGTextFill);
1186 CGContextSaveGState(mCG);
1187 SetupCGContext(this, mCG, mCanvas, origin, size, !mNeedLayer);
1188 return mCG;
1189 }
1190
ReturnCGContext(CGContextRef aCGContext)1191 void DrawTargetSkia::ReturnCGContext(CGContextRef aCGContext) {
1192 MOZ_ASSERT(aCGContext == mCG);
1193 ReleaseBits(mCanvasData);
1194 CGContextRestoreGState(aCGContext);
1195
1196 if (mNeedLayer) {
1197 // A layer was used for clipping and is about to be popped by the restore.
1198 // Make sure the CG context referencing it is released first so the popped
1199 // layer doesn't accidentally get used.
1200 if (mCG) {
1201 CGContextRelease(mCG);
1202 mCG = nullptr;
1203 }
1204 mCanvas->restore();
1205 }
1206 }
1207
BorrowCGContextFromDrawTarget(DrawTarget * aDT)1208 CGContextRef BorrowedCGContext::BorrowCGContextFromDrawTarget(DrawTarget* aDT) {
1209 DrawTargetSkia* skiaDT = static_cast<DrawTargetSkia*>(aDT);
1210 return skiaDT->BorrowCGContext(DrawOptions());
1211 }
1212
ReturnCGContextToDrawTarget(DrawTarget * aDT,CGContextRef cg)1213 void BorrowedCGContext::ReturnCGContextToDrawTarget(DrawTarget* aDT,
1214 CGContextRef cg) {
1215 DrawTargetSkia* skiaDT = static_cast<DrawTargetSkia*>(aDT);
1216 skiaDT->ReturnCGContext(cg);
1217 }
1218 #endif
1219
CanDrawFont(ScaledFont * aFont)1220 static bool CanDrawFont(ScaledFont* aFont) {
1221 switch (aFont->GetType()) {
1222 case FontType::FREETYPE:
1223 case FontType::FONTCONFIG:
1224 case FontType::MAC:
1225 case FontType::GDI:
1226 case FontType::DWRITE:
1227 return true;
1228 default:
1229 return false;
1230 }
1231 }
1232
DrawGlyphs(ScaledFont * aFont,const GlyphBuffer & aBuffer,const Pattern & aPattern,const StrokeOptions * aStrokeOptions,const DrawOptions & aOptions)1233 void DrawTargetSkia::DrawGlyphs(ScaledFont* aFont, const GlyphBuffer& aBuffer,
1234 const Pattern& aPattern,
1235 const StrokeOptions* aStrokeOptions,
1236 const DrawOptions& aOptions) {
1237 if (!CanDrawFont(aFont)) {
1238 return;
1239 }
1240
1241 MarkChanged();
1242
1243 ScaledFontBase* skiaFont = static_cast<ScaledFontBase*>(aFont);
1244 SkTypeface* typeface = skiaFont->GetSkTypeface();
1245 if (!typeface) {
1246 return;
1247 }
1248
1249 AutoPaintSetup paint(mCanvas, aOptions, aPattern);
1250 if (aStrokeOptions && !StrokeOptionsToPaint(paint.mPaint, *aStrokeOptions)) {
1251 return;
1252 }
1253
1254 AntialiasMode aaMode = aFont->GetDefaultAAMode();
1255 if (aOptions.mAntialiasMode != AntialiasMode::DEFAULT) {
1256 aaMode = aOptions.mAntialiasMode;
1257 }
1258 bool aaEnabled = aaMode != AntialiasMode::NONE;
1259 paint.mPaint.setAntiAlias(aaEnabled);
1260
1261 SkFont font(sk_ref_sp(typeface), SkFloatToScalar(skiaFont->mSize));
1262
1263 bool useSubpixelAA =
1264 GetPermitSubpixelAA() &&
1265 (aaMode == AntialiasMode::DEFAULT || aaMode == AntialiasMode::SUBPIXEL);
1266 font.setEdging(useSubpixelAA ? SkFont::Edging::kSubpixelAntiAlias
1267 : (aaEnabled ? SkFont::Edging::kAntiAlias
1268 : SkFont::Edging::kAlias));
1269
1270 skiaFont->SetupSkFontDrawOptions(font);
1271
1272 // Limit the amount of internal batch allocations Skia does.
1273 const uint32_t kMaxGlyphBatchSize = 8192;
1274
1275 for (uint32_t offset = 0; offset < aBuffer.mNumGlyphs;) {
1276 uint32_t batchSize =
1277 std::min(aBuffer.mNumGlyphs - offset, kMaxGlyphBatchSize);
1278 SkTextBlobBuilder builder;
1279 auto runBuffer = builder.allocRunPos(font, batchSize);
1280 for (uint32_t i = 0; i < batchSize; i++, offset++) {
1281 runBuffer.glyphs[i] = aBuffer.mGlyphs[offset].mIndex;
1282 runBuffer.points()[i] = PointToSkPoint(aBuffer.mGlyphs[offset].mPosition);
1283 }
1284
1285 sk_sp<SkTextBlob> text = builder.make();
1286 mCanvas->drawTextBlob(text, 0, 0, paint.mPaint);
1287 }
1288 }
1289
GetGlyphLocalBounds(ScaledFont * aFont,const GlyphBuffer & aBuffer,const Pattern & aPattern,const StrokeOptions * aStrokeOptions,const DrawOptions & aOptions)1290 Maybe<Rect> DrawTargetSkia::GetGlyphLocalBounds(
1291 ScaledFont* aFont, const GlyphBuffer& aBuffer, const Pattern& aPattern,
1292 const StrokeOptions* aStrokeOptions, const DrawOptions& aOptions) {
1293 if (!CanDrawFont(aFont)) {
1294 return Nothing();
1295 }
1296
1297 ScaledFontBase* skiaFont = static_cast<ScaledFontBase*>(aFont);
1298 SkTypeface* typeface = skiaFont->GetSkTypeface();
1299 if (!typeface) {
1300 return Nothing();
1301 }
1302
1303 AutoPaintSetup paint(mCanvas, aOptions, aPattern);
1304 if (aStrokeOptions && !StrokeOptionsToPaint(paint.mPaint, *aStrokeOptions)) {
1305 return Nothing();
1306 }
1307
1308 AntialiasMode aaMode = aFont->GetDefaultAAMode();
1309 if (aOptions.mAntialiasMode != AntialiasMode::DEFAULT) {
1310 aaMode = aOptions.mAntialiasMode;
1311 }
1312 bool aaEnabled = aaMode != AntialiasMode::NONE;
1313 paint.mPaint.setAntiAlias(aaEnabled);
1314
1315 SkFont font(sk_ref_sp(typeface), SkFloatToScalar(skiaFont->mSize));
1316
1317 bool useSubpixelAA =
1318 GetPermitSubpixelAA() &&
1319 (aaMode == AntialiasMode::DEFAULT || aaMode == AntialiasMode::SUBPIXEL);
1320 font.setEdging(useSubpixelAA ? SkFont::Edging::kSubpixelAntiAlias
1321 : (aaEnabled ? SkFont::Edging::kAntiAlias
1322 : SkFont::Edging::kAlias));
1323
1324 skiaFont->SetupSkFontDrawOptions(font);
1325
1326 // Limit the amount of internal batch allocations Skia does.
1327 const uint32_t kMaxGlyphBatchSize = 8192;
1328
1329 Rect bounds;
1330 for (uint32_t offset = 0; offset < aBuffer.mNumGlyphs;) {
1331 uint32_t batchSize =
1332 std::min(aBuffer.mNumGlyphs - offset, kMaxGlyphBatchSize);
1333 SkTextBlobBuilder builder;
1334 auto runBuffer = builder.allocRunPos(font, batchSize);
1335 for (uint32_t i = 0; i < batchSize; i++, offset++) {
1336 runBuffer.glyphs[i] = aBuffer.mGlyphs[offset].mIndex;
1337 runBuffer.points()[i] = PointToSkPoint(aBuffer.mGlyphs[offset].mPosition);
1338 }
1339
1340 sk_sp<SkTextBlob> text = builder.make();
1341 bounds = bounds.Union(SkRectToRect(text->bounds()));
1342 }
1343
1344 if (bounds.IsEmpty()) {
1345 return Nothing();
1346 }
1347
1348 return Some(bounds);
1349 }
1350
FillGlyphs(ScaledFont * aFont,const GlyphBuffer & aBuffer,const Pattern & aPattern,const DrawOptions & aOptions)1351 void DrawTargetSkia::FillGlyphs(ScaledFont* aFont, const GlyphBuffer& aBuffer,
1352 const Pattern& aPattern,
1353 const DrawOptions& aOptions) {
1354 DrawGlyphs(aFont, aBuffer, aPattern, nullptr, aOptions);
1355 }
1356
StrokeGlyphs(ScaledFont * aFont,const GlyphBuffer & aBuffer,const Pattern & aPattern,const StrokeOptions & aStrokeOptions,const DrawOptions & aOptions)1357 void DrawTargetSkia::StrokeGlyphs(ScaledFont* aFont, const GlyphBuffer& aBuffer,
1358 const Pattern& aPattern,
1359 const StrokeOptions& aStrokeOptions,
1360 const DrawOptions& aOptions) {
1361 DrawGlyphs(aFont, aBuffer, aPattern, &aStrokeOptions, aOptions);
1362 }
1363
Mask(const Pattern & aSource,const Pattern & aMask,const DrawOptions & aOptions)1364 void DrawTargetSkia::Mask(const Pattern& aSource, const Pattern& aMask,
1365 const DrawOptions& aOptions) {
1366 SkIRect maskBounds;
1367 if (!mCanvas->getDeviceClipBounds(&maskBounds)) {
1368 return;
1369 }
1370 SkPoint maskOrigin;
1371 maskOrigin.iset(maskBounds.fLeft, maskBounds.fTop);
1372
1373 SkMatrix maskMatrix = mCanvas->getTotalMatrix();
1374 maskMatrix.postTranslate(-maskOrigin.fX, -maskOrigin.fY);
1375
1376 MarkChanged();
1377 AutoPaintSetup paint(mCanvas, aOptions, aSource, nullptr, &maskMatrix);
1378
1379 Maybe<MutexAutoLock> lock;
1380 SkPaint maskPaint;
1381 SetPaintPattern(maskPaint, aMask, lock);
1382
1383 SkBitmap maskBitmap;
1384 if (!maskBitmap.tryAllocPixelsFlags(
1385 SkImageInfo::MakeA8(maskBounds.width(), maskBounds.height()),
1386 SkBitmap::kZeroPixels_AllocFlag)) {
1387 return;
1388 }
1389
1390 SkCanvas maskCanvas(maskBitmap);
1391 maskCanvas.setMatrix(maskMatrix);
1392 maskCanvas.drawPaint(maskPaint);
1393
1394 mCanvas->save();
1395 mCanvas->resetMatrix();
1396
1397 mCanvas->drawBitmap(maskBitmap, maskOrigin.fX, maskOrigin.fY, &paint.mPaint);
1398
1399 mCanvas->restore();
1400 }
1401
MaskSurface(const Pattern & aSource,SourceSurface * aMask,Point aOffset,const DrawOptions & aOptions)1402 void DrawTargetSkia::MaskSurface(const Pattern& aSource, SourceSurface* aMask,
1403 Point aOffset, const DrawOptions& aOptions) {
1404 MarkChanged();
1405
1406 SkMatrix invOffset = SkMatrix::MakeTrans(SkFloatToScalar(-aOffset.x),
1407 SkFloatToScalar(-aOffset.y));
1408 AutoPaintSetup paint(mCanvas, aOptions, aSource, nullptr, &invOffset);
1409
1410 Maybe<MutexAutoLock> lock;
1411 sk_sp<SkImage> alphaMask = ExtractAlphaForSurface(aMask, lock);
1412 if (!alphaMask) {
1413 gfxDebug() << *this << ": MaskSurface() failed to extract alpha for mask";
1414 return;
1415 }
1416
1417 mCanvas->drawImage(alphaMask, aOffset.x + aMask->GetRect().x,
1418 aOffset.y + aMask->GetRect().y, &paint.mPaint);
1419 }
1420
Draw3DTransformedSurface(SourceSurface * aSurface,const Matrix4x4 & aMatrix)1421 bool DrawTarget::Draw3DTransformedSurface(SourceSurface* aSurface,
1422 const Matrix4x4& aMatrix) {
1423 // Composite the 3D transform with the DT's transform.
1424 Matrix4x4 fullMat = aMatrix * Matrix4x4::From2D(mTransform);
1425 if (fullMat.IsSingular()) {
1426 return false;
1427 }
1428 // Transform the surface bounds and clip to this DT.
1429 IntRect xformBounds = RoundedOut(fullMat.TransformAndClipBounds(
1430 Rect(Point(0, 0), Size(aSurface->GetSize())),
1431 Rect(Point(0, 0), Size(GetSize()))));
1432 if (xformBounds.IsEmpty()) {
1433 return true;
1434 }
1435 // Offset the matrix by the transformed origin.
1436 fullMat.PostTranslate(-xformBounds.X(), -xformBounds.Y(), 0);
1437
1438 // Read in the source data.
1439 Maybe<MutexAutoLock> lock;
1440 sk_sp<SkImage> srcImage = GetSkImageForSurface(aSurface, &lock);
1441 if (!srcImage) {
1442 return true;
1443 }
1444
1445 // Set up an intermediate destination surface only the size of the transformed
1446 // bounds. Try to pass through the source's format unmodified in both the BGRA
1447 // and ARGB cases.
1448 RefPtr<DataSourceSurface> dstSurf = Factory::CreateDataSourceSurface(
1449 xformBounds.Size(),
1450 !srcImage->isOpaque() ? aSurface->GetFormat()
1451 : SurfaceFormat::A8R8G8B8_UINT32,
1452 true);
1453 if (!dstSurf) {
1454 return false;
1455 }
1456
1457 DataSourceSurface::ScopedMap map(dstSurf, DataSourceSurface::READ_WRITE);
1458 std::unique_ptr<SkCanvas> dstCanvas(SkCanvas::MakeRasterDirect(
1459 SkImageInfo::Make(xformBounds.Width(), xformBounds.Height(),
1460 GfxFormatToSkiaColorType(dstSurf->GetFormat()),
1461 kPremul_SkAlphaType),
1462 map.GetData(), map.GetStride()));
1463 if (!dstCanvas) {
1464 return false;
1465 }
1466
1467 // Do the transform.
1468 SkPaint paint;
1469 paint.setAntiAlias(true);
1470 paint.setFilterQuality(kLow_SkFilterQuality);
1471 paint.setBlendMode(SkBlendMode::kSrc);
1472
1473 SkMatrix xform;
1474 GfxMatrixToSkiaMatrix(fullMat, xform);
1475 dstCanvas->setMatrix(xform);
1476
1477 dstCanvas->drawImage(srcImage, 0, 0, &paint);
1478 dstCanvas->flush();
1479
1480 // Temporarily reset the DT's transform, since it has already been composed
1481 // above.
1482 Matrix origTransform = mTransform;
1483 SetTransform(Matrix());
1484
1485 // Draw the transformed surface within the transformed bounds.
1486 DrawSurface(dstSurf, Rect(xformBounds),
1487 Rect(Point(0, 0), Size(xformBounds.Size())));
1488
1489 SetTransform(origTransform);
1490
1491 return true;
1492 }
1493
Draw3DTransformedSurface(SourceSurface * aSurface,const Matrix4x4 & aMatrix)1494 bool DrawTargetSkia::Draw3DTransformedSurface(SourceSurface* aSurface,
1495 const Matrix4x4& aMatrix) {
1496 if (aMatrix.IsSingular()) {
1497 return false;
1498 }
1499
1500 MarkChanged();
1501
1502 Maybe<MutexAutoLock> lock;
1503 sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
1504 if (!image) {
1505 return true;
1506 }
1507
1508 mCanvas->save();
1509
1510 SkPaint paint;
1511 paint.setAntiAlias(true);
1512 paint.setFilterQuality(kLow_SkFilterQuality);
1513
1514 SkMatrix xform;
1515 GfxMatrixToSkiaMatrix(aMatrix, xform);
1516 mCanvas->concat(xform);
1517
1518 mCanvas->drawImage(image, 0, 0, &paint);
1519
1520 mCanvas->restore();
1521
1522 return true;
1523 }
1524
CreateSourceSurfaceFromData(unsigned char * aData,const IntSize & aSize,int32_t aStride,SurfaceFormat aFormat) const1525 already_AddRefed<SourceSurface> DrawTargetSkia::CreateSourceSurfaceFromData(
1526 unsigned char* aData, const IntSize& aSize, int32_t aStride,
1527 SurfaceFormat aFormat) const {
1528 RefPtr<SourceSurfaceSkia> newSurf = new SourceSurfaceSkia();
1529
1530 if (!newSurf->InitFromData(aData, aSize, aStride, aFormat)) {
1531 gfxDebug() << *this
1532 << ": Failure to create source surface from data. Size: "
1533 << aSize;
1534 return nullptr;
1535 }
1536
1537 return newSurf.forget();
1538 }
1539
CreateSimilarDrawTarget(const IntSize & aSize,SurfaceFormat aFormat) const1540 already_AddRefed<DrawTarget> DrawTargetSkia::CreateSimilarDrawTarget(
1541 const IntSize& aSize, SurfaceFormat aFormat) const {
1542 RefPtr<DrawTargetSkia> target = new DrawTargetSkia();
1543 #ifdef DEBUG
1544 if (!IsBackedByPixels(mCanvas)) {
1545 // If our canvas is backed by vector storage such as PDF then we want to
1546 // create a new DrawTarget with similar storage to avoid losing fidelity
1547 // (fidelity will be lost if the returned DT is Snapshot()'ed and drawn
1548 // back onto us since a raster will be drawn instead of vector commands).
1549 NS_WARNING("Not backed by pixels - we need to handle PDF backed SkCanvas");
1550 }
1551 #endif
1552
1553 if (!target->Init(aSize, aFormat)) {
1554 return nullptr;
1555 }
1556 return target.forget();
1557 }
1558
CanCreateSimilarDrawTarget(const IntSize & aSize,SurfaceFormat aFormat) const1559 bool DrawTargetSkia::CanCreateSimilarDrawTarget(const IntSize& aSize,
1560 SurfaceFormat aFormat) const {
1561 auto minmaxPair = std::minmax(aSize.width, aSize.height);
1562 return minmaxPair.first > 0 &&
1563 size_t(minmaxPair.second) < GetMaxSurfaceSize();
1564 }
1565
CreateClippedDrawTarget(const Rect & aBounds,SurfaceFormat aFormat)1566 RefPtr<DrawTarget> DrawTargetSkia::CreateClippedDrawTarget(
1567 const Rect& aBounds, SurfaceFormat aFormat) {
1568 SkIRect clipBounds;
1569
1570 RefPtr<DrawTarget> result;
1571 // Doing this save()/restore() dance is wasteful
1572 mCanvas->save();
1573 if (!aBounds.IsEmpty()) {
1574 mCanvas->clipRect(RectToSkRect(aBounds), SkClipOp::kIntersect, true);
1575 }
1576 if (mCanvas->getDeviceClipBounds(&clipBounds)) {
1577 RefPtr<DrawTarget> dt = CreateSimilarDrawTarget(
1578 IntSize(clipBounds.width(), clipBounds.height()), aFormat);
1579 result = gfx::Factory::CreateOffsetDrawTarget(
1580 dt, IntPoint(clipBounds.x(), clipBounds.y()));
1581 result->SetTransform(mTransform);
1582 } else {
1583 // Everything is clipped but we still want some kind of surface
1584 result = CreateSimilarDrawTarget(IntSize(1, 1), aFormat);
1585 }
1586 mCanvas->restore();
1587 return result;
1588 }
1589
1590 already_AddRefed<SourceSurface>
OptimizeSourceSurfaceForUnknownAlpha(SourceSurface * aSurface) const1591 DrawTargetSkia::OptimizeSourceSurfaceForUnknownAlpha(
1592 SourceSurface* aSurface) const {
1593 if (aSurface->GetType() == SurfaceType::SKIA) {
1594 RefPtr<SourceSurface> surface(aSurface);
1595 return surface.forget();
1596 }
1597
1598 RefPtr<DataSourceSurface> dataSurface = aSurface->GetDataSurface();
1599 DataSourceSurface::ScopedMap map(dataSurface, DataSourceSurface::READ_WRITE);
1600
1601 // For plugins, GDI can sometimes just write 0 to the alpha channel
1602 // even for RGBX formats. In this case, we have to manually write
1603 // the alpha channel to make Skia happy with RGBX and in case GDI
1604 // writes some bad data. Luckily, this only happens on plugins.
1605 WriteRGBXFormat(map.GetData(), dataSurface->GetSize(), map.GetStride(),
1606 dataSurface->GetFormat());
1607 return dataSurface.forget();
1608 }
1609
OptimizeSourceSurface(SourceSurface * aSurface) const1610 already_AddRefed<SourceSurface> DrawTargetSkia::OptimizeSourceSurface(
1611 SourceSurface* aSurface) const {
1612 if (aSurface->GetType() == SurfaceType::SKIA) {
1613 RefPtr<SourceSurface> surface(aSurface);
1614 return surface.forget();
1615 }
1616
1617 // If we're not using skia-gl then drawing doesn't require any
1618 // uploading, so any data surface is fine. Call GetDataSurface
1619 // to trigger any required readback so that it only happens
1620 // once.
1621 RefPtr<DataSourceSurface> dataSurface = aSurface->GetDataSurface();
1622 #ifdef DEBUG
1623 DataSourceSurface::ScopedMap map(dataSurface, DataSourceSurface::READ);
1624 MOZ_ASSERT(VerifyRGBXFormat(map.GetData(), dataSurface->GetSize(),
1625 map.GetStride(), dataSurface->GetFormat()));
1626 #endif
1627 return dataSurface.forget();
1628 }
1629
1630 already_AddRefed<SourceSurface>
CreateSourceSurfaceFromNativeSurface(const NativeSurface & aSurface) const1631 DrawTargetSkia::CreateSourceSurfaceFromNativeSurface(
1632 const NativeSurface& aSurface) const {
1633 return nullptr;
1634 }
1635
BlendSurface(SourceSurface * aSurface,const IntRect & aSourceRect,const IntPoint & aDestination,CompositionOp aOperator)1636 void DrawTargetSkia::BlendSurface(SourceSurface* aSurface,
1637 const IntRect& aSourceRect,
1638 const IntPoint& aDestination,
1639 CompositionOp aOperator) {
1640 MarkChanged();
1641
1642 Maybe<MutexAutoLock> lock;
1643 sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
1644 if (!image) {
1645 return;
1646 }
1647
1648 mCanvas->save();
1649 mCanvas->setMatrix(SkMatrix::MakeTrans(SkIntToScalar(aDestination.x),
1650 SkIntToScalar(aDestination.y)));
1651 mCanvas->clipRect(SkRect::MakeIWH(aSourceRect.Width(), aSourceRect.Height()),
1652 SkClipOp::kReplace_deprecated);
1653
1654 SkPaint paint;
1655 if (aOperator == CompositionOp::OP_SOURCE) {
1656 if (!image->isOpaque()) {
1657 // Keep the xfermode as SOURCE_OVER for opaque bitmaps
1658 // http://code.google.com/p/skia/issues/detail?id=628
1659 paint.setBlendMode(SkBlendMode::kSrc);
1660 }
1661 // drawImage with A8 images ends up doing a mask operation
1662 // so we need to clear before
1663 if (image->isAlphaOnly()) {
1664 mCanvas->clear(SK_ColorTRANSPARENT);
1665 }
1666 } else {
1667 paint.setBlendMode(GfxOpToSkiaOp(aOperator));
1668 }
1669
1670 mCanvas->drawImage(image, -SkIntToScalar(aSourceRect.X()),
1671 -SkIntToScalar(aSourceRect.Y()), &paint);
1672 mCanvas->restore();
1673 }
1674
GetSkPixelGeometry()1675 static inline SkPixelGeometry GetSkPixelGeometry() {
1676 return Factory::GetBGRSubpixelOrder() ? kBGR_H_SkPixelGeometry
1677 : kRGB_H_SkPixelGeometry;
1678 }
1679
1680 template <typename T>
AsRefPtr(sk_sp<T> && aSkPtr)1681 [[nodiscard]] static already_AddRefed<T> AsRefPtr(sk_sp<T>&& aSkPtr) {
1682 return already_AddRefed<T>(aSkPtr.release());
1683 }
1684
Init(const IntSize & aSize,SurfaceFormat aFormat)1685 bool DrawTargetSkia::Init(const IntSize& aSize, SurfaceFormat aFormat) {
1686 if (size_t(std::max(aSize.width, aSize.height)) > GetMaxSurfaceSize()) {
1687 return false;
1688 }
1689
1690 // we need to have surfaces that have a stride aligned to 4 for interop with
1691 // cairo
1692 SkImageInfo info = MakeSkiaImageInfo(aSize, aFormat);
1693 size_t stride = SkAlign4(info.minRowBytes());
1694 SkSurfaceProps props(0, GetSkPixelGeometry());
1695 mSurface = AsRefPtr(SkSurface::MakeRaster(info, stride, &props));
1696 if (!mSurface) {
1697 return false;
1698 }
1699
1700 mSize = aSize;
1701 mFormat = aFormat;
1702 mCanvas = mSurface->getCanvas();
1703 SetPermitSubpixelAA(IsOpaque(mFormat));
1704
1705 if (info.isOpaque()) {
1706 mCanvas->clear(SK_ColorBLACK);
1707 }
1708 return true;
1709 }
1710
Init(SkCanvas * aCanvas)1711 bool DrawTargetSkia::Init(SkCanvas* aCanvas) {
1712 mCanvas = aCanvas;
1713
1714 SkImageInfo imageInfo = mCanvas->imageInfo();
1715
1716 // If the canvas is backed by pixels we clear it to be on the safe side. If
1717 // it's not (for example, for PDF output) we don't.
1718 if (IsBackedByPixels(mCanvas)) {
1719 SkColor clearColor =
1720 imageInfo.isOpaque() ? SK_ColorBLACK : SK_ColorTRANSPARENT;
1721 mCanvas->clear(clearColor);
1722 }
1723
1724 SkISize size = mCanvas->getBaseLayerSize();
1725 mSize.width = size.width();
1726 mSize.height = size.height();
1727 mFormat =
1728 SkiaColorTypeToGfxFormat(imageInfo.colorType(), imageInfo.alphaType());
1729 SetPermitSubpixelAA(IsOpaque(mFormat));
1730 return true;
1731 }
1732
Init(unsigned char * aData,const IntSize & aSize,int32_t aStride,SurfaceFormat aFormat,bool aUninitialized)1733 bool DrawTargetSkia::Init(unsigned char* aData, const IntSize& aSize,
1734 int32_t aStride, SurfaceFormat aFormat,
1735 bool aUninitialized) {
1736 MOZ_ASSERT((aFormat != SurfaceFormat::B8G8R8X8) || aUninitialized ||
1737 VerifyRGBXFormat(aData, aSize, aStride, aFormat));
1738
1739 SkSurfaceProps props(0, GetSkPixelGeometry());
1740 mSurface = AsRefPtr(SkSurface::MakeRasterDirect(
1741 MakeSkiaImageInfo(aSize, aFormat), aData, aStride, &props));
1742 if (!mSurface) {
1743 return false;
1744 }
1745
1746 mSize = aSize;
1747 mFormat = aFormat;
1748 mCanvas = mSurface->getCanvas();
1749 SetPermitSubpixelAA(IsOpaque(mFormat));
1750 return true;
1751 }
1752
Init(RefPtr<DataSourceSurface> && aSurface)1753 bool DrawTargetSkia::Init(RefPtr<DataSourceSurface>&& aSurface) {
1754 auto map =
1755 new DataSourceSurface::ScopedMap(aSurface, DataSourceSurface::READ_WRITE);
1756 if (!map->IsMapped()) {
1757 delete map;
1758 return false;
1759 }
1760
1761 SurfaceFormat format = aSurface->GetFormat();
1762 IntSize size = aSurface->GetSize();
1763 MOZ_ASSERT((format != SurfaceFormat::B8G8R8X8) ||
1764 VerifyRGBXFormat(map->GetData(), size, map->GetStride(), format));
1765
1766 SkSurfaceProps props(0, GetSkPixelGeometry());
1767 mSurface = AsRefPtr(SkSurface::MakeRasterDirectReleaseProc(
1768 MakeSkiaImageInfo(size, format), map->GetData(), map->GetStride(),
1769 DrawTargetSkia::ReleaseMappedSkSurface, map, &props));
1770 if (!mSurface) {
1771 delete map;
1772 return false;
1773 }
1774
1775 // map is now owned by mSurface
1776 mBackingSurface = std::move(aSurface);
1777 mSize = size;
1778 mFormat = format;
1779 mCanvas = mSurface->getCanvas();
1780 SetPermitSubpixelAA(IsOpaque(format));
1781 return true;
1782 }
1783
ReleaseMappedSkSurface(void * aPixels,void * aContext)1784 /* static */ void DrawTargetSkia::ReleaseMappedSkSurface(void* aPixels,
1785 void* aContext) {
1786 auto map = reinterpret_cast<DataSourceSurface::ScopedMap*>(aContext);
1787 delete map;
1788 }
1789
SetTransform(const Matrix & aTransform)1790 void DrawTargetSkia::SetTransform(const Matrix& aTransform) {
1791 SkMatrix mat;
1792 GfxMatrixToSkiaMatrix(aTransform, mat);
1793 mCanvas->setMatrix(mat);
1794 mTransform = aTransform;
1795 }
1796
GetNativeSurface(NativeSurfaceType aType)1797 void* DrawTargetSkia::GetNativeSurface(NativeSurfaceType aType) {
1798 return nullptr;
1799 }
1800
CreatePathBuilder(FillRule aFillRule) const1801 already_AddRefed<PathBuilder> DrawTargetSkia::CreatePathBuilder(
1802 FillRule aFillRule) const {
1803 return MakeAndAddRef<PathBuilderSkia>(aFillRule);
1804 }
1805
Clear(const Rect * aRect)1806 void DrawTargetSkia::Clear(const Rect* aRect) {
1807 MarkChanged();
1808 mCanvas->save();
1809 if (aRect) {
1810 // If a local-space clip rect is supplied, then restrict clearing to that.
1811 mCanvas->clipRect(RectToSkRect(*aRect), SkClipOp::kIntersect, true);
1812 } else {
1813 // Otherwise, clear the entire surface.
1814 mCanvas->resetMatrix();
1815 mCanvas->clipRect(IntRectToSkRect(GetRect()),
1816 SkClipOp::kReplace_deprecated);
1817 }
1818 SkColor clearColor = (mFormat == SurfaceFormat::B8G8R8X8)
1819 ? SK_ColorBLACK
1820 : SK_ColorTRANSPARENT;
1821 mCanvas->clear(clearColor);
1822 mCanvas->restore();
1823 }
1824
PushClip(const Path * aPath)1825 void DrawTargetSkia::PushClip(const Path* aPath) {
1826 if (aPath->GetBackendType() != BackendType::SKIA) {
1827 return;
1828 }
1829
1830 const PathSkia* skiaPath = static_cast<const PathSkia*>(aPath);
1831 mCanvas->save();
1832 mCanvas->clipPath(skiaPath->GetPath(), SkClipOp::kIntersect, true);
1833 }
1834
PushDeviceSpaceClipRects(const IntRect * aRects,uint32_t aCount)1835 void DrawTargetSkia::PushDeviceSpaceClipRects(const IntRect* aRects,
1836 uint32_t aCount) {
1837 // Build a region by unioning all the rects together.
1838 SkRegion region;
1839 for (uint32_t i = 0; i < aCount; i++) {
1840 region.op(IntRectToSkIRect(aRects[i]), SkRegion::kUnion_Op);
1841 }
1842
1843 // Clip with the resulting region. clipRegion does not transform
1844 // this region by the current transform, unlike the other SkCanvas
1845 // clip methods, so it is just passed through in device-space.
1846 mCanvas->save();
1847 mCanvas->clipRegion(region, SkClipOp::kIntersect);
1848 }
1849
PushClipRect(const Rect & aRect)1850 void DrawTargetSkia::PushClipRect(const Rect& aRect) {
1851 SkRect rect = RectToSkRect(aRect);
1852
1853 mCanvas->save();
1854 mCanvas->clipRect(rect, SkClipOp::kIntersect, true);
1855 }
1856
PopClip()1857 void DrawTargetSkia::PopClip() {
1858 mCanvas->restore();
1859 SetTransform(GetTransform());
1860 }
1861
GetDeviceClipRect() const1862 Maybe<Rect> DrawTargetSkia::GetDeviceClipRect() const {
1863 if (mCanvas->isClipEmpty()) {
1864 return Some(Rect());
1865 }
1866 if (mCanvas->isClipRect()) {
1867 SkIRect deviceBounds;
1868 if (mCanvas->getDeviceClipBounds(&deviceBounds)) {
1869 return Some(Rect(SkIRectToIntRect(deviceBounds)));
1870 }
1871 }
1872 return Nothing();
1873 }
1874
PushLayer(bool aOpaque,Float aOpacity,SourceSurface * aMask,const Matrix & aMaskTransform,const IntRect & aBounds,bool aCopyBackground)1875 void DrawTargetSkia::PushLayer(bool aOpaque, Float aOpacity,
1876 SourceSurface* aMask,
1877 const Matrix& aMaskTransform,
1878 const IntRect& aBounds, bool aCopyBackground) {
1879 PushLayerWithBlend(aOpaque, aOpacity, aMask, aMaskTransform, aBounds,
1880 aCopyBackground, CompositionOp::OP_OVER);
1881 }
1882
PushLayerWithBlend(bool aOpaque,Float aOpacity,SourceSurface * aMask,const Matrix & aMaskTransform,const IntRect & aBounds,bool aCopyBackground,CompositionOp aCompositionOp)1883 void DrawTargetSkia::PushLayerWithBlend(bool aOpaque, Float aOpacity,
1884 SourceSurface* aMask,
1885 const Matrix& aMaskTransform,
1886 const IntRect& aBounds,
1887 bool aCopyBackground,
1888 CompositionOp aCompositionOp) {
1889 PushedLayer layer(GetPermitSubpixelAA(), aMask);
1890 mPushedLayers.push_back(layer);
1891
1892 SkPaint paint;
1893
1894 paint.setAlpha(ColorFloatToByte(aOpacity));
1895 paint.setBlendMode(GfxOpToSkiaOp(aCompositionOp));
1896
1897 // aBounds is supplied in device space, but SaveLayerRec wants local space.
1898 SkRect bounds = IntRectToSkRect(aBounds);
1899 if (!bounds.isEmpty()) {
1900 SkMatrix inverseCTM;
1901 if (mCanvas->getTotalMatrix().invert(&inverseCTM)) {
1902 inverseCTM.mapRect(&bounds);
1903 } else {
1904 bounds.setEmpty();
1905 }
1906 }
1907
1908 // We don't pass a lock object to GetSkImageForSurface here, to force a
1909 // copy of the data if this is a copy-on-write snapshot. If we instead held
1910 // the lock until the corresponding PopLayer, we'd risk deadlocking if someone
1911 // tried to touch the originating DrawTarget while the layer was pushed.
1912 sk_sp<SkImage> clipImage =
1913 aMask ? GetSkImageForSurface(aMask, nullptr) : nullptr;
1914 SkMatrix clipMatrix;
1915 GfxMatrixToSkiaMatrix(aMaskTransform, clipMatrix);
1916 if (aMask) {
1917 clipMatrix.preTranslate(aMask->GetRect().X(), aMask->GetRect().Y());
1918 }
1919
1920 SkCanvas::SaveLayerRec saveRec(
1921 aBounds.IsEmpty() ? nullptr : &bounds, &paint, nullptr, clipImage.get(),
1922 &clipMatrix,
1923 SkCanvas::kPreserveLCDText_SaveLayerFlag |
1924 (aCopyBackground ? SkCanvas::kInitWithPrevious_SaveLayerFlag : 0));
1925
1926 #if MOZ_BIG_ENDIAN()
1927 // Pushing a layer where an aMask is defined produces wrong output.
1928 // We _should_ endian swap the data, but I couldn't find a workable way to do so
1929 // Therefore I deactivate those layers in the meantime.
1930 // The result is: Tab-titles that are longer than the available space should be faded out.
1931 // The fading doesn't work, so we deactivate the fading-effect here.
1932 if (!aMask)
1933 #endif
1934 mCanvas->saveLayer(saveRec);
1935
1936 SetPermitSubpixelAA(aOpaque);
1937
1938 #ifdef MOZ_WIDGET_COCOA
1939 CGContextRelease(mCG);
1940 mCG = nullptr;
1941 #endif
1942 }
1943
PopLayer()1944 void DrawTargetSkia::PopLayer() {
1945 MarkChanged();
1946
1947 MOZ_ASSERT(!mPushedLayers.empty());
1948 const PushedLayer& layer = mPushedLayers.back();
1949
1950 mCanvas->restore();
1951
1952 SetTransform(GetTransform());
1953 SetPermitSubpixelAA(layer.mOldPermitSubpixelAA);
1954
1955 mPushedLayers.pop_back();
1956
1957 #ifdef MOZ_WIDGET_COCOA
1958 CGContextRelease(mCG);
1959 mCG = nullptr;
1960 #endif
1961 }
1962
CreateGradientStops(GradientStop * aStops,uint32_t aNumStops,ExtendMode aExtendMode) const1963 already_AddRefed<GradientStops> DrawTargetSkia::CreateGradientStops(
1964 GradientStop* aStops, uint32_t aNumStops, ExtendMode aExtendMode) const {
1965 std::vector<GradientStop> stops;
1966 stops.resize(aNumStops);
1967 for (uint32_t i = 0; i < aNumStops; i++) {
1968 stops[i] = aStops[i];
1969 }
1970 std::stable_sort(stops.begin(), stops.end());
1971
1972 return MakeAndAddRef<GradientStopsSkia>(stops, aNumStops, aExtendMode);
1973 }
1974
CreateFilter(FilterType aType)1975 already_AddRefed<FilterNode> DrawTargetSkia::CreateFilter(FilterType aType) {
1976 return FilterNodeSoftware::Create(aType);
1977 }
1978
MarkChanged()1979 void DrawTargetSkia::MarkChanged() {
1980 // I'm not entirely certain whether this lock is needed, as multiple threads
1981 // should never modify the DrawTarget at the same time anyway, but this seems
1982 // like the safest.
1983 MutexAutoLock lock(mSnapshotLock);
1984 if (mSnapshot) {
1985 if (mSnapshot->hasOneRef()) {
1986 // No owners outside of this DrawTarget's own reference. Just dump it.
1987 mSnapshot = nullptr;
1988 return;
1989 }
1990
1991 mSnapshot->DrawTargetWillChange();
1992 mSnapshot = nullptr;
1993
1994 // Handle copying of any image snapshots bound to the surface.
1995 if (mSurface) {
1996 mSurface->notifyContentWillChange(SkSurface::kRetain_ContentChangeMode);
1997 }
1998 }
1999 }
2000
2001 } // namespace mozilla::gfx
2002