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