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