tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

DrawTargetSkia.cpp (74425B)


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