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 #ifndef MOZILLA_GFX_HELPERSSKIA_H_
8 #define MOZILLA_GFX_HELPERSSKIA_H_
9 
10 #include "2D.h"
11 #include "skia/include/core/SkCanvas.h"
12 #include "skia/include/effects/SkDashPathEffect.h"
13 #include "skia/include/core/SkShader.h"
14 #include "mozilla/Assertions.h"
15 #include <vector>
16 #include "nsDebug.h"
17 
18 namespace mozilla {
19 namespace gfx {
20 
GfxFormatToSkiaColorType(SurfaceFormat format)21 static inline SkColorType GfxFormatToSkiaColorType(SurfaceFormat format) {
22   switch (format) {
23     case SurfaceFormat::B8G8R8A8:
24       return kBGRA_8888_SkColorType;
25     case SurfaceFormat::B8G8R8X8:
26       // We probably need to do something here.
27       return kBGRA_8888_SkColorType;
28     case SurfaceFormat::R5G6B5_UINT16:
29       return kRGB_565_SkColorType;
30     case SurfaceFormat::A8:
31       return kAlpha_8_SkColorType;
32     case SurfaceFormat::R8G8B8A8:
33       return kRGBA_8888_SkColorType;
34     case SurfaceFormat::A8R8G8B8:
35       MOZ_DIAGNOSTIC_ASSERT(false, "A8R8G8B8 unsupported by Skia");
36       return kRGBA_8888_SkColorType;
37     default:
38       MOZ_DIAGNOSTIC_ASSERT(false, "Unknown surface format");
39       return kRGBA_8888_SkColorType;
40   }
41 }
42 
43 static inline SurfaceFormat SkiaColorTypeToGfxFormat(
44     SkColorType aColorType, SkAlphaType aAlphaType = kPremul_SkAlphaType) {
45   switch (aColorType) {
46     case kBGRA_8888_SkColorType:
47       return aAlphaType == kOpaque_SkAlphaType ? SurfaceFormat::B8G8R8X8
48                                                : SurfaceFormat::B8G8R8A8;
49     case kRGB_565_SkColorType:
50       return SurfaceFormat::R5G6B5_UINT16;
51     case kAlpha_8_SkColorType:
52       return SurfaceFormat::A8;
53     default:
54       return SurfaceFormat::B8G8R8A8;
55   }
56 }
57 
GfxFormatToSkiaAlphaType(SurfaceFormat format)58 static inline SkAlphaType GfxFormatToSkiaAlphaType(SurfaceFormat format) {
59   switch (format) {
60     case SurfaceFormat::B8G8R8X8:
61     case SurfaceFormat::R5G6B5_UINT16:
62       return kOpaque_SkAlphaType;
63     default:
64       return kPremul_SkAlphaType;
65   }
66 }
67 
MakeSkiaImageInfo(const IntSize & aSize,SurfaceFormat aFormat)68 static inline SkImageInfo MakeSkiaImageInfo(const IntSize& aSize,
69                                             SurfaceFormat aFormat) {
70   return SkImageInfo::Make(aSize.width, aSize.height,
71                            GfxFormatToSkiaColorType(aFormat),
72                            GfxFormatToSkiaAlphaType(aFormat));
73 }
74 
GfxMatrixToSkiaMatrix(const Matrix & mat,SkMatrix & retval)75 static inline void GfxMatrixToSkiaMatrix(const Matrix& mat, SkMatrix& retval) {
76   retval.setAll(SkFloatToScalar(mat._11), SkFloatToScalar(mat._21),
77                 SkFloatToScalar(mat._31), SkFloatToScalar(mat._12),
78                 SkFloatToScalar(mat._22), SkFloatToScalar(mat._32), 0, 0,
79                 SK_Scalar1);
80 }
81 
GfxMatrixToSkiaMatrix(const Matrix4x4 & aMatrix,SkMatrix & aResult)82 static inline void GfxMatrixToSkiaMatrix(const Matrix4x4& aMatrix,
83                                          SkMatrix& aResult) {
84   aResult.setAll(SkFloatToScalar(aMatrix._11), SkFloatToScalar(aMatrix._21),
85                  SkFloatToScalar(aMatrix._41), SkFloatToScalar(aMatrix._12),
86                  SkFloatToScalar(aMatrix._22), SkFloatToScalar(aMatrix._42),
87                  SkFloatToScalar(aMatrix._14), SkFloatToScalar(aMatrix._24),
88                  SkFloatToScalar(aMatrix._44));
89 }
90 
CapStyleToSkiaCap(CapStyle aCap)91 static inline SkPaint::Cap CapStyleToSkiaCap(CapStyle aCap) {
92   switch (aCap) {
93     case CapStyle::BUTT:
94       return SkPaint::kButt_Cap;
95     case CapStyle::ROUND:
96       return SkPaint::kRound_Cap;
97     case CapStyle::SQUARE:
98       return SkPaint::kSquare_Cap;
99   }
100   return SkPaint::kDefault_Cap;
101 }
102 
JoinStyleToSkiaJoin(JoinStyle aJoin)103 static inline SkPaint::Join JoinStyleToSkiaJoin(JoinStyle aJoin) {
104   switch (aJoin) {
105     case JoinStyle::BEVEL:
106       return SkPaint::kBevel_Join;
107     case JoinStyle::ROUND:
108       return SkPaint::kRound_Join;
109     case JoinStyle::MITER:
110     case JoinStyle::MITER_OR_BEVEL:
111       return SkPaint::kMiter_Join;
112   }
113   return SkPaint::kDefault_Join;
114 }
115 
116 static inline bool StrokeOptionsToPaint(SkPaint& aPaint,
117                                         const StrokeOptions& aOptions,
118                                         bool aUsePathEffects = true) {
119   // Skia renders 0 width strokes with a width of 1 (and in black),
120   // so we should just skip the draw call entirely.
121   // Skia does not handle non-finite line widths.
122   if (!aOptions.mLineWidth || !IsFinite(aOptions.mLineWidth)) {
123     return false;
124   }
125   aPaint.setStrokeWidth(SkFloatToScalar(aOptions.mLineWidth));
126   aPaint.setStrokeMiter(SkFloatToScalar(aOptions.mMiterLimit));
127   aPaint.setStrokeCap(CapStyleToSkiaCap(aOptions.mLineCap));
128   aPaint.setStrokeJoin(JoinStyleToSkiaJoin(aOptions.mLineJoin));
129 
130   if (aOptions.mDashLength > 0 && aUsePathEffects) {
131     // Skia only supports dash arrays that are multiples of 2.
132     uint32_t dashCount;
133 
134     if (aOptions.mDashLength % 2 == 0) {
135       dashCount = aOptions.mDashLength;
136     } else {
137       dashCount = aOptions.mDashLength * 2;
138     }
139 
140     std::vector<SkScalar> pattern;
141     pattern.resize(dashCount);
142 
143     for (uint32_t i = 0; i < dashCount; i++) {
144       pattern[i] =
145           SkFloatToScalar(aOptions.mDashPattern[i % aOptions.mDashLength]);
146     }
147 
148     sk_sp<SkPathEffect> dash = SkDashPathEffect::Make(
149         &pattern.front(), dashCount, SkFloatToScalar(aOptions.mDashOffset));
150     aPaint.setPathEffect(dash);
151   }
152 
153   aPaint.setStyle(SkPaint::kStroke_Style);
154   return true;
155 }
156 
GfxOpToSkiaOp(CompositionOp op)157 static inline SkBlendMode GfxOpToSkiaOp(CompositionOp op) {
158   switch (op) {
159     case CompositionOp::OP_OVER:
160       return SkBlendMode::kSrcOver;
161     case CompositionOp::OP_ADD:
162       return SkBlendMode::kPlus;
163     case CompositionOp::OP_ATOP:
164       return SkBlendMode::kSrcATop;
165     case CompositionOp::OP_OUT:
166       return SkBlendMode::kSrcOut;
167     case CompositionOp::OP_IN:
168       return SkBlendMode::kSrcIn;
169     case CompositionOp::OP_SOURCE:
170       return SkBlendMode::kSrc;
171     case CompositionOp::OP_DEST_IN:
172       return SkBlendMode::kDstIn;
173     case CompositionOp::OP_DEST_OUT:
174       return SkBlendMode::kDstOut;
175     case CompositionOp::OP_DEST_OVER:
176       return SkBlendMode::kDstOver;
177     case CompositionOp::OP_DEST_ATOP:
178       return SkBlendMode::kDstATop;
179     case CompositionOp::OP_XOR:
180       return SkBlendMode::kXor;
181     case CompositionOp::OP_MULTIPLY:
182       return SkBlendMode::kMultiply;
183     case CompositionOp::OP_SCREEN:
184       return SkBlendMode::kScreen;
185     case CompositionOp::OP_OVERLAY:
186       return SkBlendMode::kOverlay;
187     case CompositionOp::OP_DARKEN:
188       return SkBlendMode::kDarken;
189     case CompositionOp::OP_LIGHTEN:
190       return SkBlendMode::kLighten;
191     case CompositionOp::OP_COLOR_DODGE:
192       return SkBlendMode::kColorDodge;
193     case CompositionOp::OP_COLOR_BURN:
194       return SkBlendMode::kColorBurn;
195     case CompositionOp::OP_HARD_LIGHT:
196       return SkBlendMode::kHardLight;
197     case CompositionOp::OP_SOFT_LIGHT:
198       return SkBlendMode::kSoftLight;
199     case CompositionOp::OP_DIFFERENCE:
200       return SkBlendMode::kDifference;
201     case CompositionOp::OP_EXCLUSION:
202       return SkBlendMode::kExclusion;
203     case CompositionOp::OP_HUE:
204       return SkBlendMode::kHue;
205     case CompositionOp::OP_SATURATION:
206       return SkBlendMode::kSaturation;
207     case CompositionOp::OP_COLOR:
208       return SkBlendMode::kColor;
209     case CompositionOp::OP_LUMINOSITY:
210       return SkBlendMode::kLuminosity;
211     default:
212       return SkBlendMode::kSrcOver;
213   }
214 }
215 
216 /* There's quite a bit of inconsistency about
217  * whether float colors should be rounded with .5f.
218  * We choose to do it to match cairo which also
219  * happens to match the Direct3D specs */
ColorFloatToByte(Float color)220 static inline U8CPU ColorFloatToByte(Float color) {
221   // XXX: do a better job converting to int
222   return U8CPU(color * 255.f + .5f);
223 };
224 
ColorToSkColor(const DeviceColor & color,Float aAlpha)225 static inline SkColor ColorToSkColor(const DeviceColor& color, Float aAlpha) {
226   return SkColorSetARGB(ColorFloatToByte(color.a * aAlpha),
227                         ColorFloatToByte(color.r), ColorFloatToByte(color.g),
228                         ColorFloatToByte(color.b));
229 }
230 
PointToSkPoint(const Point & aPoint)231 static inline SkPoint PointToSkPoint(const Point& aPoint) {
232   return SkPoint::Make(SkFloatToScalar(aPoint.x), SkFloatToScalar(aPoint.y));
233 }
234 
RectToSkRect(const Rect & aRect)235 static inline SkRect RectToSkRect(const Rect& aRect) {
236   return SkRect::MakeXYWH(
237       SkFloatToScalar(aRect.X()), SkFloatToScalar(aRect.Y()),
238       SkFloatToScalar(aRect.Width()), SkFloatToScalar(aRect.Height()));
239 }
240 
IntRectToSkRect(const IntRect & aRect)241 static inline SkRect IntRectToSkRect(const IntRect& aRect) {
242   return SkRect::MakeXYWH(SkIntToScalar(aRect.X()), SkIntToScalar(aRect.Y()),
243                           SkIntToScalar(aRect.Width()),
244                           SkIntToScalar(aRect.Height()));
245 }
246 
RectToSkIRect(const Rect & aRect)247 static inline SkIRect RectToSkIRect(const Rect& aRect) {
248   return SkIRect::MakeXYWH(int32_t(aRect.X()), int32_t(aRect.Y()),
249                            int32_t(aRect.Width()), int32_t(aRect.Height()));
250 }
251 
IntRectToSkIRect(const IntRect & aRect)252 static inline SkIRect IntRectToSkIRect(const IntRect& aRect) {
253   return SkIRect::MakeXYWH(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height());
254 }
255 
SkIRectToIntRect(const SkIRect & aRect)256 static inline IntRect SkIRectToIntRect(const SkIRect& aRect) {
257   return IntRect(aRect.x(), aRect.y(), aRect.width(), aRect.height());
258 }
259 
SkPointToPoint(const SkPoint & aPoint)260 static inline Point SkPointToPoint(const SkPoint& aPoint) {
261   return Point(SkScalarToFloat(aPoint.x()), SkScalarToFloat(aPoint.y()));
262 }
263 
SkRectToRect(const SkRect & aRect)264 static inline Rect SkRectToRect(const SkRect& aRect) {
265   return Rect(SkScalarToFloat(aRect.x()), SkScalarToFloat(aRect.y()),
266               SkScalarToFloat(aRect.width()), SkScalarToFloat(aRect.height()));
267 }
268 
ExtendModeToTileMode(ExtendMode aMode,Axis aAxis)269 static inline SkTileMode ExtendModeToTileMode(ExtendMode aMode, Axis aAxis) {
270   switch (aMode) {
271     case ExtendMode::CLAMP:
272       return SkTileMode::kClamp;
273     case ExtendMode::REPEAT:
274       return SkTileMode::kRepeat;
275     case ExtendMode::REFLECT:
276       return SkTileMode::kMirror;
277     case ExtendMode::REPEAT_X: {
278       return aAxis == Axis::X_AXIS ? SkTileMode::kRepeat : SkTileMode::kClamp;
279     }
280     case ExtendMode::REPEAT_Y: {
281       return aAxis == Axis::Y_AXIS ? SkTileMode::kRepeat : SkTileMode::kClamp;
282     }
283   }
284   return SkTileMode::kClamp;
285 }
286 
GfxHintingToSkiaHinting(FontHinting aHinting)287 static inline SkFontHinting GfxHintingToSkiaHinting(FontHinting aHinting) {
288   switch (aHinting) {
289     case FontHinting::NONE:
290       return SkFontHinting::kNone;
291     case FontHinting::LIGHT:
292       return SkFontHinting::kSlight;
293     case FontHinting::NORMAL:
294       return SkFontHinting::kNormal;
295     case FontHinting::FULL:
296       return SkFontHinting::kFull;
297   }
298   return SkFontHinting::kNormal;
299 }
300 
GetFillRule(SkPath::FillType aFillType)301 static inline FillRule GetFillRule(SkPath::FillType aFillType) {
302   switch (aFillType) {
303     case SkPath::kWinding_FillType:
304       return FillRule::FILL_WINDING;
305     case SkPath::kEvenOdd_FillType:
306       return FillRule::FILL_EVEN_ODD;
307     case SkPath::kInverseWinding_FillType:
308     case SkPath::kInverseEvenOdd_FillType:
309     default:
310       NS_WARNING("Unsupported fill type\n");
311       break;
312   }
313 
314   return FillRule::FILL_EVEN_ODD;
315 }
316 
317 /**
318  * Returns true if the canvas is backed by pixels.  Returns false if the canvas
319  * wraps an SkPDFDocument, for example.
320  *
321  * Note: It is not clear whether the test used to implement this function may
322  * result in it returning false in some circumstances even when the canvas
323  * _is_ pixel backed.  In other words maybe it is possible for such a canvas to
324  * have kUnknown_SkPixelGeometry?
325  */
IsBackedByPixels(const SkCanvas * aCanvas)326 static inline bool IsBackedByPixels(const SkCanvas* aCanvas) {
327   SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
328   if (!aCanvas->getProps(&props) ||
329       props.pixelGeometry() == kUnknown_SkPixelGeometry) {
330     return false;
331   }
332   return true;
333 }
334 
335 }  // namespace gfx
336 }  // namespace mozilla
337 
338 #endif /* MOZILLA_GFX_HELPERSSKIA_H_ */
339