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