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