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