tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

PathSkia.cpp (9657B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "PathSkia.h"
      8 #include "HelpersSkia.h"
      9 #include "PathHelpers.h"
     10 #include "skia/include/core/SkPathUtils.h"
     11 #include "skia/src/core/SkGeometry.h"
     12 
     13 namespace mozilla::gfx {
     14 
     15 already_AddRefed<PathBuilder> PathBuilderSkia::Create(FillRule aFillRule) {
     16  return MakeAndAddRef<PathBuilderSkia>(aFillRule);
     17 }
     18 
     19 PathBuilderSkia::PathBuilderSkia(SkPath&& aPath, FillRule aFillRule,
     20                                 const Point& aCurrentPoint,
     21                                 const Point& aBeginPoint)
     22    : mPath(aPath) {
     23  SetFillRule(aFillRule);
     24  SetCurrentPoint(aCurrentPoint);
     25  SetBeginPoint(aBeginPoint);
     26 }
     27 
     28 PathBuilderSkia::PathBuilderSkia(FillRule aFillRule) { SetFillRule(aFillRule); }
     29 
     30 void PathBuilderSkia::SetFillRule(FillRule aFillRule) {
     31  mFillRule = aFillRule;
     32  if (mFillRule == FillRule::FILL_WINDING) {
     33    mPath.setFillType(SkPathFillType::kWinding);
     34  } else {
     35    mPath.setFillType(SkPathFillType::kEvenOdd);
     36  }
     37 }
     38 
     39 void PathBuilderSkia::MoveTo(const Point& aPoint) {
     40  mPath.moveTo(SkFloatToScalar(aPoint.x), SkFloatToScalar(aPoint.y));
     41  mCurrentPoint = aPoint;
     42  mBeginPoint = aPoint;
     43 }
     44 
     45 void PathBuilderSkia::LineTo(const Point& aPoint) {
     46  if (!mPath.countPoints()) {
     47    MoveTo(aPoint);
     48  } else {
     49    mPath.lineTo(SkFloatToScalar(aPoint.x), SkFloatToScalar(aPoint.y));
     50  }
     51  mCurrentPoint = aPoint;
     52 }
     53 
     54 void PathBuilderSkia::BezierTo(const Point& aCP1, const Point& aCP2,
     55                               const Point& aCP3) {
     56  if (!mPath.countPoints()) {
     57    MoveTo(aCP1);
     58  }
     59  mPath.cubicTo(SkFloatToScalar(aCP1.x), SkFloatToScalar(aCP1.y),
     60                SkFloatToScalar(aCP2.x), SkFloatToScalar(aCP2.y),
     61                SkFloatToScalar(aCP3.x), SkFloatToScalar(aCP3.y));
     62  mCurrentPoint = aCP3;
     63 }
     64 
     65 void PathBuilderSkia::QuadraticBezierTo(const Point& aCP1, const Point& aCP2) {
     66  if (!mPath.countPoints()) {
     67    MoveTo(aCP1);
     68  }
     69  mPath.quadTo(SkFloatToScalar(aCP1.x), SkFloatToScalar(aCP1.y),
     70               SkFloatToScalar(aCP2.x), SkFloatToScalar(aCP2.y));
     71  mCurrentPoint = aCP2;
     72 }
     73 
     74 void PathBuilderSkia::Close() {
     75  mPath.close();
     76  mCurrentPoint = mBeginPoint;
     77 }
     78 
     79 void PathBuilderSkia::Arc(const Point& aOrigin, float aRadius,
     80                          float aStartAngle, float aEndAngle,
     81                          bool aAntiClockwise) {
     82  ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle,
     83              aAntiClockwise);
     84 }
     85 
     86 already_AddRefed<Path> PathBuilderSkia::Finish() {
     87  RefPtr<Path> path =
     88      MakeAndAddRef<PathSkia>(mPath, mFillRule, mCurrentPoint, mBeginPoint);
     89  mCurrentPoint = Point(0.0, 0.0);
     90  mBeginPoint = Point(0.0, 0.0);
     91  return path.forget();
     92 }
     93 
     94 void PathBuilderSkia::AppendPath(const SkPath& aPath) { mPath.addPath(aPath); }
     95 
     96 already_AddRefed<PathBuilder> PathSkia::CopyToBuilder(
     97    FillRule aFillRule) const {
     98  return MakeAndAddRef<PathBuilderSkia>(SkPath(mPath), aFillRule, mCurrentPoint,
     99                                        mBeginPoint);
    100 }
    101 
    102 already_AddRefed<PathBuilder> PathSkia::TransformedCopyToBuilder(
    103    const Matrix& aTransform, FillRule aFillRule) const {
    104  SkMatrix matrix;
    105  GfxMatrixToSkiaMatrix(aTransform, matrix);
    106  SkPath path(mPath);
    107  path.transform(matrix);
    108  return MakeAndAddRef<PathBuilderSkia>(
    109      std::move(path), aFillRule, aTransform.TransformPoint(mCurrentPoint),
    110      aTransform.TransformPoint(mBeginPoint));
    111 }
    112 
    113 already_AddRefed<PathBuilder> PathSkia::MoveToBuilder(FillRule aFillRule) {
    114  return MakeAndAddRef<PathBuilderSkia>(std::move(mPath), aFillRule,
    115                                        mCurrentPoint, mBeginPoint);
    116 }
    117 
    118 already_AddRefed<PathBuilder> PathSkia::TransformedMoveToBuilder(
    119    const Matrix& aTransform, FillRule aFillRule) {
    120  SkMatrix matrix;
    121  GfxMatrixToSkiaMatrix(aTransform, matrix);
    122  mPath.transform(matrix);
    123  return MakeAndAddRef<PathBuilderSkia>(
    124      std::move(mPath), aFillRule, aTransform.TransformPoint(mCurrentPoint),
    125      aTransform.TransformPoint(mBeginPoint));
    126 }
    127 
    128 static bool SkPathContainsPoint(const SkPath& aPath, const Point& aPoint,
    129                                const Matrix& aTransform) {
    130  Matrix inverse = aTransform;
    131  if (!inverse.Invert()) {
    132    return false;
    133  }
    134 
    135  SkPoint point = PointToSkPoint(inverse.TransformPoint(aPoint));
    136  return aPath.contains(point.fX, point.fY);
    137 }
    138 
    139 bool PathSkia::ContainsPoint(const Point& aPoint,
    140                             const Matrix& aTransform) const {
    141  if (!mPath.isFinite()) {
    142    return false;
    143  }
    144 
    145  return SkPathContainsPoint(mPath, aPoint, aTransform);
    146 }
    147 
    148 bool PathSkia::GetFillPath(const StrokeOptions& aStrokeOptions,
    149                           const Matrix& aTransform, SkPath& aFillPath,
    150                           const Maybe<Rect>& aClipRect) const {
    151  SkPaint paint;
    152  if (!StrokeOptionsToPaint(paint, aStrokeOptions)) {
    153    return false;
    154  }
    155 
    156  SkMatrix skiaMatrix;
    157  GfxMatrixToSkiaMatrix(aTransform, skiaMatrix);
    158 
    159  Maybe<SkRect> cullRect;
    160  if (aClipRect.isSome()) {
    161    cullRect = Some(RectToSkRect(aClipRect.ref()));
    162  }
    163 
    164  return skpathutils::FillPathWithPaint(mPath, paint, &aFillPath,
    165                                        cullRect.ptrOr(nullptr), skiaMatrix);
    166 }
    167 
    168 bool PathSkia::StrokeContainsPoint(const StrokeOptions& aStrokeOptions,
    169                                   const Point& aPoint,
    170                                   const Matrix& aTransform) const {
    171  if (!mPath.isFinite()) {
    172    return false;
    173  }
    174 
    175  SkPath strokePath;
    176  if (!GetFillPath(aStrokeOptions, aTransform, strokePath)) {
    177    return false;
    178  }
    179 
    180  return SkPathContainsPoint(strokePath, aPoint, aTransform);
    181 }
    182 
    183 Rect PathSkia::GetBounds(const Matrix& aTransform) const {
    184  if (!mPath.isFinite()) {
    185    return Rect();
    186  }
    187 
    188  Rect bounds = SkRectToRect(mPath.computeTightBounds());
    189  return aTransform.TransformBounds(bounds);
    190 }
    191 
    192 Rect PathSkia::GetStrokedBounds(const StrokeOptions& aStrokeOptions,
    193                                const Matrix& aTransform) const {
    194  if (!mPath.isFinite()) {
    195    return Rect();
    196  }
    197 
    198  SkPath fillPath;
    199  if (!GetFillPath(aStrokeOptions, aTransform, fillPath)) {
    200    return Rect();
    201  }
    202 
    203  Rect bounds = SkRectToRect(fillPath.computeTightBounds());
    204  return aTransform.TransformBounds(bounds);
    205 }
    206 
    207 Rect PathSkia::GetFastBounds(const Matrix& aTransform,
    208                             const StrokeOptions* aStrokeOptions) const {
    209  if (!mPath.isFinite()) {
    210    return Rect();
    211  }
    212  SkRect bounds = mPath.getBounds();
    213  if (aStrokeOptions) {
    214    // If the path is stroked, ensure that the bounds are inflated by any
    215    // relevant options such as line width. Avoid using dash path effects
    216    // for performance and to ensure computeFastStrokeBounds succeeds.
    217    SkPaint paint;
    218    if (!StrokeOptionsToPaint(paint, *aStrokeOptions, false)) {
    219      return Rect();
    220    }
    221    SkRect outBounds = SkRect::MakeEmpty();
    222    bounds = paint.computeFastStrokeBounds(bounds, &outBounds);
    223  }
    224  return aTransform.TransformBounds(SkRectToRect(bounds));
    225 }
    226 
    227 int ConvertConicToQuads(const Point& aP0, const Point& aP1, const Point& aP2,
    228                        float aWeight, std::vector<Point>& aQuads) {
    229  SkConic conic(PointToSkPoint(aP0), PointToSkPoint(aP1), PointToSkPoint(aP2),
    230                aWeight);
    231  int pow2 = conic.computeQuadPOW2(0.25f);
    232  aQuads.resize(1 + 2 * (1 << pow2));
    233  int numQuads =
    234      conic.chopIntoQuadsPOW2(reinterpret_cast<SkPoint*>(&aQuads[0]), pow2);
    235  if (numQuads < 1 << pow2) {
    236    aQuads.resize(1 + 2 * numQuads);
    237  }
    238  return numQuads;
    239 }
    240 
    241 void PathSkia::StreamToSink(PathSink* aSink) const {
    242  SkPath::RawIter iter(mPath);
    243 
    244  SkPoint points[4];
    245  SkPath::Verb currentVerb;
    246  while ((currentVerb = iter.next(points)) != SkPath::kDone_Verb) {
    247    switch (currentVerb) {
    248      case SkPath::kMove_Verb:
    249        aSink->MoveTo(SkPointToPoint(points[0]));
    250        break;
    251      case SkPath::kLine_Verb:
    252        aSink->LineTo(SkPointToPoint(points[1]));
    253        break;
    254      case SkPath::kCubic_Verb:
    255        aSink->BezierTo(SkPointToPoint(points[1]), SkPointToPoint(points[2]),
    256                        SkPointToPoint(points[3]));
    257        break;
    258      case SkPath::kQuad_Verb:
    259        aSink->QuadraticBezierTo(SkPointToPoint(points[1]),
    260                                 SkPointToPoint(points[2]));
    261        break;
    262      case SkPath::kConic_Verb: {
    263        std::vector<Point> quads;
    264        int numQuads = ConvertConicToQuads(
    265            SkPointToPoint(points[0]), SkPointToPoint(points[1]),
    266            SkPointToPoint(points[2]), iter.conicWeight(), quads);
    267        for (int i = 0; i < numQuads; i++) {
    268          aSink->QuadraticBezierTo(quads[2 * i + 1], quads[2 * i + 2]);
    269        }
    270        break;
    271      }
    272      case SkPath::kClose_Verb:
    273        aSink->Close();
    274        break;
    275      default:
    276        MOZ_ASSERT(false);
    277        // Unexpected verb found in path!
    278    }
    279  }
    280 }
    281 
    282 Maybe<Rect> PathSkia::AsRect() const {
    283  SkRect skiaRect;
    284  if (mPath.isRect(&skiaRect)) {
    285    Rect rect = SkRectToRect(skiaRect);
    286    // Ensure that the conversion between Skia rect and Moz2D rect is not lossy
    287    // due to floating-point precision errors.
    288    if (RectToSkRect(rect) == skiaRect) {
    289      return Some(rect);
    290    }
    291  }
    292  return Nothing();
    293 }
    294 
    295 bool PathSkia::IsEmpty() const {
    296  // Move/Close/Done segments are not included in the mask so as long as any
    297  // flag is set, we know that the path is non-empty.
    298  return mPath.getSegmentMasks() == 0;
    299 }
    300 
    301 }  // namespace mozilla::gfx