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