diff options
Diffstat (limited to 'gfx/2d/HelpersSkia.h')
-rw-r--r-- | gfx/2d/HelpersSkia.h | 360 |
1 files changed, 360 insertions, 0 deletions
diff --git a/gfx/2d/HelpersSkia.h b/gfx/2d/HelpersSkia.h new file mode 100644 index 0000000000..7f7bf6fbc1 --- /dev/null +++ b/gfx/2d/HelpersSkia.h @@ -0,0 +1,360 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOZILLA_GFX_HELPERSSKIA_H_ +#define MOZILLA_GFX_HELPERSSKIA_H_ + +#include "2D.h" +#include "skia/include/core/SkCanvas.h" +#include "skia/include/core/SkPathEffect.h" +#include "skia/include/core/SkPathTypes.h" +#include "skia/include/core/SkShader.h" +#include "skia/include/effects/SkDashPathEffect.h" +#include "mozilla/Assertions.h" +#include <cmath> +#include <vector> +#include "nsDebug.h" + +namespace mozilla { +namespace gfx { + +static inline SkColorType GfxFormatToSkiaColorType(SurfaceFormat format) { + switch (format) { + case SurfaceFormat::B8G8R8A8: + return kBGRA_8888_SkColorType; + case SurfaceFormat::B8G8R8X8: + // We probably need to do something here. + return kBGRA_8888_SkColorType; + case SurfaceFormat::R5G6B5_UINT16: + return kRGB_565_SkColorType; + case SurfaceFormat::A8: + return kAlpha_8_SkColorType; + case SurfaceFormat::R8G8B8A8: + return kRGBA_8888_SkColorType; + case SurfaceFormat::A8R8G8B8: + MOZ_DIAGNOSTIC_ASSERT(false, "A8R8G8B8 unsupported by Skia"); + return kRGBA_8888_SkColorType; + default: + MOZ_DIAGNOSTIC_ASSERT(false, "Unknown surface format"); + return kRGBA_8888_SkColorType; + } +} + +static inline SurfaceFormat SkiaColorTypeToGfxFormat( + SkColorType aColorType, SkAlphaType aAlphaType = kPremul_SkAlphaType) { + switch (aColorType) { + case kBGRA_8888_SkColorType: + return aAlphaType == kOpaque_SkAlphaType ? SurfaceFormat::B8G8R8X8 + : SurfaceFormat::B8G8R8A8; + case kRGB_565_SkColorType: + return SurfaceFormat::R5G6B5_UINT16; + case kAlpha_8_SkColorType: + return SurfaceFormat::A8; + default: + return SurfaceFormat::B8G8R8A8; + } +} + +static inline SkAlphaType GfxFormatToSkiaAlphaType(SurfaceFormat format) { + switch (format) { + case SurfaceFormat::B8G8R8X8: + case SurfaceFormat::R5G6B5_UINT16: + return kOpaque_SkAlphaType; + default: + return kPremul_SkAlphaType; + } +} + +static inline SkImageInfo MakeSkiaImageInfo(const IntSize& aSize, + SurfaceFormat aFormat) { + return SkImageInfo::Make(aSize.width, aSize.height, + GfxFormatToSkiaColorType(aFormat), + GfxFormatToSkiaAlphaType(aFormat)); +} + +static inline void GfxMatrixToSkiaMatrix(const Matrix& mat, SkMatrix& retval) { + retval.setAll(SkFloatToScalar(mat._11), SkFloatToScalar(mat._21), + SkFloatToScalar(mat._31), SkFloatToScalar(mat._12), + SkFloatToScalar(mat._22), SkFloatToScalar(mat._32), 0, 0, + SK_Scalar1); +} + +static inline void GfxMatrixToSkiaMatrix(const Matrix4x4& aMatrix, + SkMatrix& aResult) { + aResult.setAll(SkFloatToScalar(aMatrix._11), SkFloatToScalar(aMatrix._21), + SkFloatToScalar(aMatrix._41), SkFloatToScalar(aMatrix._12), + SkFloatToScalar(aMatrix._22), SkFloatToScalar(aMatrix._42), + SkFloatToScalar(aMatrix._14), SkFloatToScalar(aMatrix._24), + SkFloatToScalar(aMatrix._44)); +} + +static inline SkPaint::Cap CapStyleToSkiaCap(CapStyle aCap) { + switch (aCap) { + case CapStyle::BUTT: + return SkPaint::kButt_Cap; + case CapStyle::ROUND: + return SkPaint::kRound_Cap; + case CapStyle::SQUARE: + return SkPaint::kSquare_Cap; + } + return SkPaint::kDefault_Cap; +} + +static inline SkPaint::Join JoinStyleToSkiaJoin(JoinStyle aJoin) { + switch (aJoin) { + case JoinStyle::BEVEL: + return SkPaint::kBevel_Join; + case JoinStyle::ROUND: + return SkPaint::kRound_Join; + case JoinStyle::MITER: + case JoinStyle::MITER_OR_BEVEL: + return SkPaint::kMiter_Join; + } + return SkPaint::kDefault_Join; +} + +static inline bool StrokeOptionsToPaint(SkPaint& aPaint, + const StrokeOptions& aOptions, + bool aUsePathEffects = true) { + // Skia renders 0 width strokes with a width of 1 (and in black), + // so we should just skip the draw call entirely. + // Skia does not handle non-finite line widths. + if (!aOptions.mLineWidth || !std::isfinite(aOptions.mLineWidth)) { + return false; + } + aPaint.setStrokeWidth(SkFloatToScalar(aOptions.mLineWidth)); + aPaint.setStrokeMiter(SkFloatToScalar(aOptions.mMiterLimit)); + aPaint.setStrokeCap(CapStyleToSkiaCap(aOptions.mLineCap)); + aPaint.setStrokeJoin(JoinStyleToSkiaJoin(aOptions.mLineJoin)); + + if (aOptions.mDashLength > 0 && aUsePathEffects) { + // Skia only supports dash arrays that are multiples of 2. + uint32_t dashCount; + + if (aOptions.mDashLength % 2 == 0) { + dashCount = aOptions.mDashLength; + } else { + dashCount = aOptions.mDashLength * 2; + } + + std::vector<SkScalar> pattern; + pattern.resize(dashCount); + + for (uint32_t i = 0; i < dashCount; i++) { + pattern[i] = + SkFloatToScalar(aOptions.mDashPattern[i % aOptions.mDashLength]); + } + + auto dash = SkDashPathEffect::Make(&pattern.front(), dashCount, + SkFloatToScalar(aOptions.mDashOffset)); + aPaint.setPathEffect(dash); + } + + aPaint.setStyle(SkPaint::kStroke_Style); + return true; +} + +static inline SkBlendMode GfxOpToSkiaOp(CompositionOp op) { + switch (op) { + case CompositionOp::OP_CLEAR: + return SkBlendMode::kClear; + case CompositionOp::OP_OVER: + return SkBlendMode::kSrcOver; + case CompositionOp::OP_ADD: + return SkBlendMode::kPlus; + case CompositionOp::OP_ATOP: + return SkBlendMode::kSrcATop; + case CompositionOp::OP_OUT: + return SkBlendMode::kSrcOut; + case CompositionOp::OP_IN: + return SkBlendMode::kSrcIn; + case CompositionOp::OP_SOURCE: + return SkBlendMode::kSrc; + case CompositionOp::OP_DEST_IN: + return SkBlendMode::kDstIn; + case CompositionOp::OP_DEST_OUT: + return SkBlendMode::kDstOut; + case CompositionOp::OP_DEST_OVER: + return SkBlendMode::kDstOver; + case CompositionOp::OP_DEST_ATOP: + return SkBlendMode::kDstATop; + case CompositionOp::OP_XOR: + return SkBlendMode::kXor; + case CompositionOp::OP_MULTIPLY: + return SkBlendMode::kMultiply; + case CompositionOp::OP_SCREEN: + return SkBlendMode::kScreen; + case CompositionOp::OP_OVERLAY: + return SkBlendMode::kOverlay; + case CompositionOp::OP_DARKEN: + return SkBlendMode::kDarken; + case CompositionOp::OP_LIGHTEN: + return SkBlendMode::kLighten; + case CompositionOp::OP_COLOR_DODGE: + return SkBlendMode::kColorDodge; + case CompositionOp::OP_COLOR_BURN: + return SkBlendMode::kColorBurn; + case CompositionOp::OP_HARD_LIGHT: + return SkBlendMode::kHardLight; + case CompositionOp::OP_SOFT_LIGHT: + return SkBlendMode::kSoftLight; + case CompositionOp::OP_DIFFERENCE: + return SkBlendMode::kDifference; + case CompositionOp::OP_EXCLUSION: + return SkBlendMode::kExclusion; + case CompositionOp::OP_HUE: + return SkBlendMode::kHue; + case CompositionOp::OP_SATURATION: + return SkBlendMode::kSaturation; + case CompositionOp::OP_COLOR: + return SkBlendMode::kColor; + case CompositionOp::OP_LUMINOSITY: + return SkBlendMode::kLuminosity; + case CompositionOp::OP_COUNT: + break; + } + + return SkBlendMode::kSrcOver; +} + +/* There's quite a bit of inconsistency about + * whether float colors should be rounded with .5f. + * We choose to do it to match cairo which also + * happens to match the Direct3D specs */ +static inline U8CPU ColorFloatToByte(Float color) { + // XXX: do a better job converting to int + return U8CPU(color * 255.f + .5f); +}; + +static inline SkColor ColorToSkColor(const DeviceColor& color, Float aAlpha) { + return SkColorSetARGB(ColorFloatToByte(color.a * aAlpha), + ColorFloatToByte(color.r), ColorFloatToByte(color.g), + ColorFloatToByte(color.b)); +} + +static inline SkPoint PointToSkPoint(const Point& aPoint) { + return SkPoint::Make(SkFloatToScalar(aPoint.x), SkFloatToScalar(aPoint.y)); +} + +static inline SkRect RectToSkRect(const Rect& aRect) { + return SkRect::MakeXYWH( + SkFloatToScalar(aRect.X()), SkFloatToScalar(aRect.Y()), + SkFloatToScalar(aRect.Width()), SkFloatToScalar(aRect.Height())); +} + +static inline SkRect IntRectToSkRect(const IntRect& aRect) { + return SkRect::MakeXYWH(SkIntToScalar(aRect.X()), SkIntToScalar(aRect.Y()), + SkIntToScalar(aRect.Width()), + SkIntToScalar(aRect.Height())); +} + +static inline SkIRect RectToSkIRect(const Rect& aRect) { + return SkIRect::MakeXYWH(int32_t(aRect.X()), int32_t(aRect.Y()), + int32_t(aRect.Width()), int32_t(aRect.Height())); +} + +static inline SkIRect IntRectToSkIRect(const IntRect& aRect) { + return SkIRect::MakeXYWH(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height()); +} + +static inline IntRect SkIRectToIntRect(const SkIRect& aRect) { + return IntRect(aRect.x(), aRect.y(), aRect.width(), aRect.height()); +} + +static inline Point SkPointToPoint(const SkPoint& aPoint) { + return Point(SkScalarToFloat(aPoint.x()), SkScalarToFloat(aPoint.y())); +} + +static inline Rect SkRectToRect(const SkRect& aRect) { + return Rect(SkScalarToFloat(aRect.x()), SkScalarToFloat(aRect.y()), + SkScalarToFloat(aRect.width()), SkScalarToFloat(aRect.height())); +} + +static inline SkTileMode ExtendModeToTileMode(ExtendMode aMode, Axis aAxis) { + switch (aMode) { + case ExtendMode::CLAMP: + return SkTileMode::kClamp; + case ExtendMode::REPEAT: + return SkTileMode::kRepeat; + case ExtendMode::REFLECT: + return SkTileMode::kMirror; + case ExtendMode::REPEAT_X: { + return aAxis == Axis::X_AXIS ? SkTileMode::kRepeat : SkTileMode::kClamp; + } + case ExtendMode::REPEAT_Y: { + return aAxis == Axis::Y_AXIS ? SkTileMode::kRepeat : SkTileMode::kClamp; + } + } + return SkTileMode::kClamp; +} + +static inline SkFontHinting GfxHintingToSkiaHinting(FontHinting aHinting) { + switch (aHinting) { + case FontHinting::NONE: + return SkFontHinting::kNone; + case FontHinting::LIGHT: + return SkFontHinting::kSlight; + case FontHinting::NORMAL: + return SkFontHinting::kNormal; + case FontHinting::FULL: + return SkFontHinting::kFull; + } + return SkFontHinting::kNormal; +} + +static inline FillRule GetFillRule(SkPathFillType aFillType) { + switch (aFillType) { + case SkPathFillType::kWinding: + return FillRule::FILL_WINDING; + case SkPathFillType::kEvenOdd: + return FillRule::FILL_EVEN_ODD; + case SkPathFillType::kInverseWinding: + case SkPathFillType::kInverseEvenOdd: + default: + NS_WARNING("Unsupported fill type\n"); + break; + } + + return FillRule::FILL_EVEN_ODD; +} + +/** + * Returns true if the canvas is backed by pixels. Returns false if the canvas + * wraps an SkPDFDocument, for example. + * + * Note: It is not clear whether the test used to implement this function may + * result in it returning false in some circumstances even when the canvas + * _is_ pixel backed. In other words maybe it is possible for such a canvas to + * have kUnknown_SkPixelGeometry? + */ +static inline bool IsBackedByPixels(const SkCanvas* aCanvas) { + SkSurfaceProps props(0, kUnknown_SkPixelGeometry); + if (!aCanvas->getProps(&props) || + props.pixelGeometry() == kUnknown_SkPixelGeometry) { + return false; + } + return true; +} + +/** + * Computes appropriate resolution scale to be used with SkPath::getFillPath + * based on the scaling of the supplied transform. + */ +float ComputeResScaleForStroking(const Matrix& aTransform); + +/** + * This is a wrapper around SkGeometry's SkConic that can be used to convert + * conic sections in an SkPath to a sequence of quadratic curves. The quads + * vector is organized such that for the Nth quad, it's control points are + * 2*N, 2*N+1, 2*N+2. This function returns the resulting number of quads. + */ +int ConvertConicToQuads(const Point& aP0, const Point& aP1, const Point& aP2, + float aWeight, std::vector<Point>& aQuads); + +} // namespace gfx +} // namespace mozilla + +#endif /* MOZILLA_GFX_HELPERSSKIA_H_ */ |