tor-browser

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

PathRecording.cpp (14652B)


      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 "PathRecording.h"
      8 #include "DrawEventRecorder.h"
      9 #include "RecordedEventImpl.h"
     10 
     11 namespace mozilla {
     12 namespace gfx {
     13 
     14 inline Maybe<float> PathOps::ArcParams::GetRadius() const {
     15  // Do a quick check for a uniform scale and/or translation transform. In the
     16  // worst case scenario, failing just causes a fallback to ArcToBezier.
     17  if (transform._11 == transform._22 && transform._12 == 0.0f &&
     18      transform._21 == 0.0f && transform._11 > 0.0f) {
     19    return Some(transform._11);
     20  }
     21  return Nothing();
     22 }
     23 
     24 inline void PathOps::ArcParams::ToSink(PathSink& aPathSink,
     25                                       bool aAntiClockwise) const {
     26  if (Maybe<float> radius = GetRadius()) {
     27    aPathSink.Arc(GetOrigin(), *radius, startAngle, endAngle, aAntiClockwise);
     28  } else {
     29    ArcToBezier(&aPathSink, Point(), Size(1.0f, 1.0f), startAngle, endAngle,
     30                aAntiClockwise, 0.0f, transform);
     31  }
     32 }
     33 
     34 #define NEXT_PARAMS(_type)                                        \
     35  const _type params = *reinterpret_cast<const _type*>(nextByte); \
     36  nextByte += sizeof(_type);
     37 
     38 bool PathOps::StreamToSink(PathSink& aPathSink) const {
     39  if (mPathData.empty()) {
     40    return true;
     41  }
     42 
     43  const uint8_t* nextByte = mPathData.data();
     44  const uint8_t* end = nextByte + mPathData.size();
     45  while (nextByte < end) {
     46    const OpType opType = *reinterpret_cast<const OpType*>(nextByte);
     47    nextByte += sizeof(OpType);
     48    switch (opType) {
     49      case OpType::OP_MOVETO: {
     50        NEXT_PARAMS(Point)
     51        aPathSink.MoveTo(params);
     52        break;
     53      }
     54      case OpType::OP_LINETO: {
     55        NEXT_PARAMS(Point)
     56        aPathSink.LineTo(params);
     57        break;
     58      }
     59      case OpType::OP_BEZIERTO: {
     60        NEXT_PARAMS(ThreePoints)
     61        aPathSink.BezierTo(params.p1, params.p2, params.p3);
     62        break;
     63      }
     64      case OpType::OP_QUADRATICBEZIERTO: {
     65        NEXT_PARAMS(TwoPoints)
     66        aPathSink.QuadraticBezierTo(params.p1, params.p2);
     67        break;
     68      }
     69      case OpType::OP_ARC_CW:
     70      case OpType::OP_ARC_CCW: {
     71        NEXT_PARAMS(ArcParams)
     72        params.ToSink(aPathSink, opType == OpType::OP_ARC_CCW);
     73        break;
     74      }
     75      case OpType::OP_CLOSE:
     76        aPathSink.Close();
     77        break;
     78      default:
     79        return false;
     80    }
     81  }
     82 
     83  return true;
     84 }
     85 
     86 #define CHECKED_NEXT_PARAMS(_type)      \
     87  if (nextByte + sizeof(_type) > end) { \
     88    return false;                       \
     89  }                                     \
     90  NEXT_PARAMS(_type)
     91 
     92 bool PathOps::CheckedStreamToSink(PathSink& aPathSink) const {
     93  if (mPathData.empty()) {
     94    return true;
     95  }
     96 
     97  const uint8_t* nextByte = mPathData.data();
     98  const uint8_t* end = nextByte + mPathData.size();
     99  while (true) {
    100    if (nextByte == end) {
    101      break;
    102    }
    103 
    104    if (nextByte + sizeof(OpType) > end) {
    105      return false;
    106    }
    107 
    108    const OpType opType = *reinterpret_cast<const OpType*>(nextByte);
    109    nextByte += sizeof(OpType);
    110    switch (opType) {
    111      case OpType::OP_MOVETO: {
    112        CHECKED_NEXT_PARAMS(Point)
    113        aPathSink.MoveTo(params);
    114        break;
    115      }
    116      case OpType::OP_LINETO: {
    117        CHECKED_NEXT_PARAMS(Point)
    118        aPathSink.LineTo(params);
    119        break;
    120      }
    121      case OpType::OP_BEZIERTO: {
    122        CHECKED_NEXT_PARAMS(ThreePoints)
    123        aPathSink.BezierTo(params.p1, params.p2, params.p3);
    124        break;
    125      }
    126      case OpType::OP_QUADRATICBEZIERTO: {
    127        CHECKED_NEXT_PARAMS(TwoPoints)
    128        aPathSink.QuadraticBezierTo(params.p1, params.p2);
    129        break;
    130      }
    131      case OpType::OP_ARC_CW:
    132      case OpType::OP_ARC_CCW: {
    133        CHECKED_NEXT_PARAMS(ArcParams)
    134        params.ToSink(aPathSink, opType == OpType::OP_ARC_CCW);
    135        break;
    136      }
    137      case OpType::OP_CLOSE:
    138        aPathSink.Close();
    139        break;
    140      default:
    141        return false;
    142    }
    143  }
    144 
    145  return true;
    146 }
    147 #undef CHECKED_NEXT_PARAMS
    148 
    149 PathOps PathOps::TransformedCopy(const Matrix& aTransform) const {
    150  PathOps newPathOps;
    151  newPathOps.mPathData.reserve(mPathData.size());
    152  const uint8_t* nextByte = mPathData.data();
    153  const uint8_t* end = nextByte + mPathData.size();
    154  while (nextByte < end) {
    155    const OpType opType = *reinterpret_cast<const OpType*>(nextByte);
    156    nextByte += sizeof(OpType);
    157    switch (opType) {
    158      case OpType::OP_MOVETO: {
    159        NEXT_PARAMS(Point)
    160        newPathOps.MoveTo(aTransform.TransformPoint(params));
    161        break;
    162      }
    163      case OpType::OP_LINETO: {
    164        NEXT_PARAMS(Point)
    165        newPathOps.LineTo(aTransform.TransformPoint(params));
    166        break;
    167      }
    168      case OpType::OP_BEZIERTO: {
    169        NEXT_PARAMS(ThreePoints)
    170        newPathOps.BezierTo(aTransform.TransformPoint(params.p1),
    171                            aTransform.TransformPoint(params.p2),
    172                            aTransform.TransformPoint(params.p3));
    173        break;
    174      }
    175      case OpType::OP_QUADRATICBEZIERTO: {
    176        NEXT_PARAMS(TwoPoints)
    177        newPathOps.QuadraticBezierTo(aTransform.TransformPoint(params.p1),
    178                                     aTransform.TransformPoint(params.p2));
    179        break;
    180      }
    181      case OpType::OP_ARC_CW:
    182      case OpType::OP_ARC_CCW: {
    183        NEXT_PARAMS(ArcParams)
    184        newPathOps.Arc(params.transform * aTransform, params.startAngle,
    185                       params.endAngle, opType == OpType::OP_ARC_CCW);
    186        break;
    187      }
    188      case OpType::OP_CLOSE:
    189        newPathOps.Close();
    190        break;
    191      default:
    192        MOZ_CRASH("We control mOpTypes, so this should never happen.");
    193    }
    194  }
    195 
    196  return newPathOps;
    197 }
    198 
    199 #define MODIFY_NEXT_PARAMS(_type)                      \
    200  _type& params = *reinterpret_cast<_type*>(nextByte); \
    201  nextByte += sizeof(_type);
    202 
    203 void PathOps::TransformInPlace(const Matrix& aTransform) {
    204  uint8_t* nextByte = mPathData.data();
    205  uint8_t* end = nextByte + mPathData.size();
    206  while (nextByte < end) {
    207    const OpType opType = *reinterpret_cast<const OpType*>(nextByte);
    208    nextByte += sizeof(OpType);
    209    switch (opType) {
    210      case OpType::OP_MOVETO: {
    211        MODIFY_NEXT_PARAMS(Point)
    212        params = aTransform.TransformPoint(params);
    213        break;
    214      }
    215      case OpType::OP_LINETO: {
    216        MODIFY_NEXT_PARAMS(Point)
    217        params = aTransform.TransformPoint(params);
    218        break;
    219      }
    220      case OpType::OP_BEZIERTO: {
    221        MODIFY_NEXT_PARAMS(ThreePoints)
    222        params.p1 = aTransform.TransformPoint(params.p1);
    223        params.p2 = aTransform.TransformPoint(params.p2);
    224        params.p3 = aTransform.TransformPoint(params.p3);
    225        break;
    226      }
    227      case OpType::OP_QUADRATICBEZIERTO: {
    228        MODIFY_NEXT_PARAMS(TwoPoints)
    229        params.p1 = aTransform.TransformPoint(params.p1);
    230        params.p2 = aTransform.TransformPoint(params.p2);
    231        break;
    232      }
    233      case OpType::OP_ARC_CW:
    234      case OpType::OP_ARC_CCW: {
    235        MODIFY_NEXT_PARAMS(ArcParams)
    236        params.transform *= aTransform;
    237        break;
    238      }
    239      case OpType::OP_CLOSE:
    240        break;
    241      default:
    242        MOZ_CRASH("We control mOpTypes, so this should never happen.");
    243    }
    244  }
    245 }
    246 
    247 Maybe<Circle> PathOps::AsCircle() const {
    248  if (mPathData.empty()) {
    249    return Nothing();
    250  }
    251 
    252  const uint8_t* nextByte = mPathData.data();
    253  const uint8_t* end = nextByte + mPathData.size();
    254  const OpType opType = *reinterpret_cast<const OpType*>(nextByte);
    255  nextByte += sizeof(OpType);
    256  if (opType == OpType::OP_ARC_CW || opType == OpType::OP_ARC_CCW) {
    257    NEXT_PARAMS(ArcParams)
    258    if (fabs(fabs(params.startAngle - params.endAngle) - 2 * M_PI) < 1e-6) {
    259      if (Maybe<float> radius = params.GetRadius()) {
    260        // we have a full circle
    261        if (nextByte < end) {
    262          const OpType nextOpType = *reinterpret_cast<const OpType*>(nextByte);
    263          nextByte += sizeof(OpType);
    264          if (nextOpType == OpType::OP_CLOSE) {
    265            if (nextByte == end) {
    266              return Some(Circle{params.GetOrigin(), *radius, true});
    267            }
    268          }
    269        } else {
    270          // the circle wasn't closed
    271          return Some(Circle{params.GetOrigin(), *radius, false});
    272        }
    273      }
    274    }
    275  }
    276 
    277  return Nothing();
    278 }
    279 
    280 Maybe<Line> PathOps::AsLine() const {
    281  if (mPathData.empty()) {
    282    return Nothing();
    283  }
    284 
    285  Line retval;
    286 
    287  const uint8_t* nextByte = mPathData.data();
    288  const uint8_t* end = nextByte + mPathData.size();
    289  OpType opType = *reinterpret_cast<const OpType*>(nextByte);
    290  nextByte += sizeof(OpType);
    291 
    292  if (opType == OpType::OP_MOVETO) {
    293    MOZ_ASSERT(nextByte != end);
    294 
    295    NEXT_PARAMS(Point)
    296    retval.origin = params;
    297  } else {
    298    return Nothing();
    299  }
    300 
    301  if (nextByte >= end) {
    302    return Nothing();
    303  }
    304 
    305  opType = *reinterpret_cast<const OpType*>(nextByte);
    306  nextByte += sizeof(OpType);
    307 
    308  if (opType == OpType::OP_LINETO) {
    309    MOZ_ASSERT(nextByte != end);
    310 
    311    NEXT_PARAMS(Point)
    312 
    313    if (nextByte == end) {
    314      retval.destination = params;
    315      return Some(retval);
    316    }
    317  }
    318 
    319  return Nothing();
    320 }
    321 #undef NEXT_PARAMS
    322 
    323 size_t PathOps::NumberOfOps() const {
    324  size_t size = 0;
    325  const uint8_t* nextByte = mPathData.data();
    326  const uint8_t* end = nextByte + mPathData.size();
    327  while (nextByte < end) {
    328    size++;
    329    const OpType opType = *reinterpret_cast<const OpType*>(nextByte);
    330    nextByte += sizeof(OpType);
    331    switch (opType) {
    332      case OpType::OP_MOVETO:
    333        nextByte += sizeof(Point);
    334        break;
    335      case OpType::OP_LINETO:
    336        nextByte += sizeof(Point);
    337        break;
    338      case OpType::OP_BEZIERTO:
    339        nextByte += sizeof(ThreePoints);
    340        break;
    341      case OpType::OP_QUADRATICBEZIERTO:
    342        nextByte += sizeof(TwoPoints);
    343        break;
    344      case OpType::OP_ARC_CW:
    345      case OpType::OP_ARC_CCW:
    346        nextByte += sizeof(ArcParams);
    347        break;
    348      case OpType::OP_CLOSE:
    349        break;
    350      default:
    351        MOZ_CRASH("We control mOpTypes, so this should never happen.");
    352    }
    353  }
    354 
    355  return size;
    356 }
    357 
    358 bool PathOps::IsEmpty() const {
    359  const uint8_t* nextByte = mPathData.data();
    360  const uint8_t* end = nextByte + mPathData.size();
    361  while (nextByte < end) {
    362    const OpType opType = *reinterpret_cast<const OpType*>(nextByte);
    363    nextByte += sizeof(OpType);
    364    switch (opType) {
    365      case OpType::OP_MOVETO:
    366        nextByte += sizeof(Point);
    367        break;
    368      case OpType::OP_CLOSE:
    369        break;
    370      default:
    371        return false;
    372    }
    373  }
    374  return true;
    375 }
    376 
    377 void PathBuilderRecording::MoveTo(const Point& aPoint) {
    378  mPathOps.MoveTo(aPoint);
    379  mBeginPoint = aPoint;
    380  mCurrentPoint = aPoint;
    381 }
    382 
    383 void PathBuilderRecording::LineTo(const Point& aPoint) {
    384  mPathOps.LineTo(aPoint);
    385  mCurrentPoint = aPoint;
    386 }
    387 
    388 void PathBuilderRecording::BezierTo(const Point& aCP1, const Point& aCP2,
    389                                    const Point& aCP3) {
    390  mPathOps.BezierTo(aCP1, aCP2, aCP3);
    391  mCurrentPoint = aCP3;
    392 }
    393 
    394 void PathBuilderRecording::QuadraticBezierTo(const Point& aCP1,
    395                                             const Point& aCP2) {
    396  mPathOps.QuadraticBezierTo(aCP1, aCP2);
    397  mCurrentPoint = aCP2;
    398 }
    399 
    400 void PathBuilderRecording::Close() {
    401  mPathOps.Close();
    402  mCurrentPoint = mBeginPoint;
    403 }
    404 
    405 void PathBuilderRecording::Arc(const Point& aOrigin, float aRadius,
    406                               float aStartAngle, float aEndAngle,
    407                               bool aAntiClockwise) {
    408  mPathOps.Arc(aOrigin, aRadius, aStartAngle, aEndAngle, aAntiClockwise);
    409 
    410  mCurrentPoint = aOrigin + Point(cosf(aEndAngle), sinf(aEndAngle)) * aRadius;
    411 }
    412 
    413 already_AddRefed<Path> PathBuilderRecording::Finish() {
    414  return MakeAndAddRef<PathRecording>(mBackendType, std::move(mPathOps),
    415                                      mFillRule, mCurrentPoint, mBeginPoint);
    416 }
    417 
    418 PathRecording::PathRecording(BackendType aBackend, PathOps&& aOps,
    419                             FillRule aFillRule, const Point& aCurrentPoint,
    420                             const Point& aBeginPoint)
    421    : mBackendType(aBackend),
    422      mPathOps(std::move(aOps)),
    423      mFillRule(aFillRule),
    424      mCurrentPoint(aCurrentPoint),
    425      mBeginPoint(aBeginPoint) {}
    426 
    427 PathRecording::~PathRecording() {
    428  for (size_t i = 0; i < mStoredRecorders.size(); i++) {
    429    mStoredRecorders[i]->RemoveStoredObject(this);
    430    mStoredRecorders[i]->RecordEvent(RecordedPathDestruction(this));
    431  }
    432 }
    433 
    434 void PathRecording::EnsurePath() const {
    435  if (mPath) {
    436    return;
    437  }
    438  if (RefPtr<PathBuilder> pathBuilder =
    439          Factory::CreatePathBuilder(mBackendType, mFillRule)) {
    440    if (!mPathOps.StreamToSink(*pathBuilder)) {
    441      MOZ_ASSERT(false, "Failed to stream PathOps to PathBuilder");
    442    } else {
    443      mPath = pathBuilder->Finish();
    444      MOZ_ASSERT(!!mPath, "Failed finishing Path from PathBuilder");
    445    }
    446  } else {
    447    MOZ_ASSERT(false, "Failed to create PathBuilder for PathRecording");
    448  }
    449 }
    450 
    451 already_AddRefed<PathBuilder> PathRecording::CopyToBuilder(
    452    FillRule aFillRule) const {
    453  RefPtr<PathBuilderRecording> recording =
    454      new PathBuilderRecording(mBackendType, PathOps(mPathOps), aFillRule);
    455  recording->SetCurrentPoint(mCurrentPoint);
    456  recording->SetBeginPoint(mBeginPoint);
    457  return recording.forget();
    458 }
    459 
    460 already_AddRefed<PathBuilder> PathRecording::TransformedCopyToBuilder(
    461    const Matrix& aTransform, FillRule aFillRule) const {
    462  RefPtr<PathBuilderRecording> recording = new PathBuilderRecording(
    463      mBackendType, mPathOps.TransformedCopy(aTransform), aFillRule);
    464  recording->SetCurrentPoint(aTransform.TransformPoint(mCurrentPoint));
    465  recording->SetBeginPoint(aTransform.TransformPoint(mBeginPoint));
    466  return recording.forget();
    467 }
    468 
    469 already_AddRefed<PathBuilder> PathRecording::MoveToBuilder(FillRule aFillRule) {
    470  RefPtr<PathBuilderRecording> recording =
    471      new PathBuilderRecording(mBackendType, std::move(mPathOps), aFillRule);
    472  recording->SetCurrentPoint(mCurrentPoint);
    473  recording->SetBeginPoint(mBeginPoint);
    474  return recording.forget();
    475 }
    476 
    477 already_AddRefed<PathBuilder> PathRecording::TransformedMoveToBuilder(
    478    const Matrix& aTransform, FillRule aFillRule) {
    479  mPathOps.TransformInPlace(aTransform);
    480  RefPtr<PathBuilderRecording> recording =
    481      new PathBuilderRecording(mBackendType, std::move(mPathOps), aFillRule);
    482  recording->SetCurrentPoint(aTransform.TransformPoint(mCurrentPoint));
    483  recording->SetBeginPoint(aTransform.TransformPoint(mBeginPoint));
    484  return recording.forget();
    485 }
    486 
    487 }  // namespace gfx
    488 }  // namespace mozilla