Coverage Report

Created: 2018-09-25 14:53

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