LCOV - code coverage report
Current view: top level - gfx/2d - DrawTargetSkia.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 429 838 51.2 %
Date: 2017-07-14 16:53:18 Functions: 52 78 66.7 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13