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