Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Do not convert an open path to a closed rect. #45903

Merged
merged 2 commits into from
Sep 15, 2023

Conversation

matanlurey
Copy link
Contributor

Fixes flutter/flutter#134816.


Confirmed this only happens when utilizing DisplayListBuilder. Consider:

TEST_P(DisplayListTest, CanDrawAnOpenPath) {
  flutter::DisplayListBuilder builder;
  flutter::DlPaint paint;

  paint.setColor(flutter::DlColor::kRed());
  paint.setDrawStyle(flutter::DlDrawStyle::kStroke);
  paint.setStrokeWidth(10);

  builder.Translate(300, 300);

  // Move to (50, 50) and draw lines from:
  // 1. (50, height)
  // 2. (width, height)
  // 3. (width, 50)
  SkPath path;
  path.moveTo(50, 50);
  path.lineTo(50, 100);
  path.lineTo(100, 100);
  path.lineTo(100, 50);
  builder.DrawPath(path, paint);

  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}

Screenshot 2023-09-15 at 2 06 53 PM


The bug is in dl_dispatcher.cc:

// |flutter::DlOpReceiver|
void DlDispatcher::drawPath(const SkPath& path) {
  SkRect rect;
  SkRRect rrect;
  SkRect oval;

  if (path.isRect(&rect)) {
    canvas_.DrawRect(skia_conversions::ToRect(rect), paint_);
  } else if (path.isRRect(&rrect) && rrect.isSimple()) {
    canvas_.DrawRRect(skia_conversions::ToRect(rrect.rect()),
                      rrect.getSimpleRadii().fX, paint_);
  } else if (path.isOval(&oval) && oval.width() == oval.height()) {
    canvas_.DrawCircle(skia_conversions::ToPoint(oval.center()),
                       oval.width() * 0.5, paint_);
  } else {
    canvas_.DrawPath(skia_conversions::ToPath(path), paint_);
  }
}

Note the documentation for isRect:

/** Returns true if SkPath is equivalent to SkRect when filled.
    If false: rect, isClosed, and direction are unchanged.
    If true: rect, isClosed, and direction are written to if not nullptr.

    rect may be smaller than the SkPath bounds. SkPath bounds may include kMove_Verb points
    that do not alter the area drawn by the returned rect.

    @param rect       storage for bounds of SkRect; may be nullptr
    @param isClosed   storage set to true if SkPath is closed; may be nullptr
    @param direction  storage set to SkRect direction; may be nullptr
    @return           true if SkPath contains SkRect

    example: https://fiddle.skia.org/c/@Path_isRect
*/
bool isRect(SkRect* rect, bool* isClosed = nullptr, SkPathDirection* direction = nullptr) const;

... the isClosed is critical, without checking that we're doing an incorrect optimization.

I'll send a fix and test.

Copy link
Member

@bdero bdero left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@flutter-dashboard
Copy link

Golden file changes have been found for this pull request. Click here to view and triage (e.g. because this is an intentional change).

If you are still iterating on this change and are not ready to resolve the images on the Flutter Gold dashboard, consider marking this PR as a draft pull request above. You will still be able to view image results on the dashboard, commenting will be silenced, and the check will not try to resolve itself until marked ready for review.

Changes reported for pull request #45903 at sha b15c6cc

@matanlurey matanlurey removed the request for review from jonahwilliams September 15, 2023 22:29
@matanlurey matanlurey merged commit 97705b8 into flutter:main Sep 15, 2023
@matanlurey matanlurey deleted the impeller-dl-path-open branch September 15, 2023 22:29
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Sep 16, 2023
fluttermirroringbot pushed a commit to flutter/flutter that referenced this pull request Sep 16, 2023
…ions) (#134877)

Manual roll requested by zra@google.com

flutter/engine@30b7e9d...cdcbdcc

2023-09-16 jonahwilliams@google.com Revert "[Impeller] construct text
frames on UI thread." (flutter/engine#45910)
2023-09-16 skia-flutter-autoroll@skia.org Roll Skia from 7c179932cc06 to
c19cc483c619 (1 revision) (flutter/engine#45911)
2023-09-15 skia-flutter-autoroll@skia.org Roll Skia from 917fd16e6f26 to
7c179932cc06 (1 revision) (flutter/engine#45907)
2023-09-15 skia-flutter-autoroll@skia.org Roll Skia from 0057898979a1 to
917fd16e6f26 (1 revision) (flutter/engine#45906)
2023-09-15 matanlurey@users.noreply.github.com Do not convert an open
path to a closed rect. (flutter/engine#45903)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-engine-flutter-autoroll
Please CC bdero@google.com,rmistry@google.com,zra@google.com on the
revert to ensure that a human
is aware of the problem.

To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
Mairramer pushed a commit to Mairramer/flutter that referenced this pull request Oct 10, 2023
…ions) (flutter#134877)

Manual roll requested by zra@google.com

flutter/engine@30b7e9d...cdcbdcc

2023-09-16 jonahwilliams@google.com Revert "[Impeller] construct text
frames on UI thread." (flutter/engine#45910)
2023-09-16 skia-flutter-autoroll@skia.org Roll Skia from 7c179932cc06 to
c19cc483c619 (1 revision) (flutter/engine#45911)
2023-09-15 skia-flutter-autoroll@skia.org Roll Skia from 917fd16e6f26 to
7c179932cc06 (1 revision) (flutter/engine#45907)
2023-09-15 skia-flutter-autoroll@skia.org Roll Skia from 0057898979a1 to
917fd16e6f26 (1 revision) (flutter/engine#45906)
2023-09-15 matanlurey@users.noreply.github.com Do not convert an open
path to a closed rect. (flutter/engine#45903)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-engine-flutter-autoroll
Please CC bdero@google.com,rmistry@google.com,zra@google.com on the
revert to ensure that a human
is aware of the problem.

To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
harryterkelsen pushed a commit that referenced this pull request Oct 23, 2023
Fixes flutter/flutter#134816.

---

Confirmed this only happens when utilizing `DisplayListBuilder`.
Consider:

```cc
TEST_P(DisplayListTest, CanDrawAnOpenPath) {
  flutter::DisplayListBuilder builder;
  flutter::DlPaint paint;

  paint.setColor(flutter::DlColor::kRed());
  paint.setDrawStyle(flutter::DlDrawStyle::kStroke);
  paint.setStrokeWidth(10);

  builder.Translate(300, 300);

  // Move to (50, 50) and draw lines from:
  // 1. (50, height)
  // 2. (width, height)
  // 3. (width, 50)
  SkPath path;
  path.moveTo(50, 50);
  path.lineTo(50, 100);
  path.lineTo(100, 100);
  path.lineTo(100, 50);
  builder.DrawPath(path, paint);

  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}
```

![Screenshot 2023-09-15 at 2 06 53
PM](https://github.com/flutter/flutter/assets/168174/3a3868e1-f1d1-4e29-affa-4bd32d26ccdc)

---

The bug is in `dl_dispatcher.cc`:

```cc
// |flutter::DlOpReceiver|
void DlDispatcher::drawPath(const SkPath& path) {
  SkRect rect;
  SkRRect rrect;
  SkRect oval;

  if (path.isRect(&rect)) {
    canvas_.DrawRect(skia_conversions::ToRect(rect), paint_);
  } else if (path.isRRect(&rrect) && rrect.isSimple()) {
    canvas_.DrawRRect(skia_conversions::ToRect(rrect.rect()),
                      rrect.getSimpleRadii().fX, paint_);
  } else if (path.isOval(&oval) && oval.width() == oval.height()) {
    canvas_.DrawCircle(skia_conversions::ToPoint(oval.center()),
                       oval.width() * 0.5, paint_);
  } else {
    canvas_.DrawPath(skia_conversions::ToPath(path), paint_);
  }
}
```

Note the documentation for `isRect`:

```cc
/** Returns true if SkPath is equivalent to SkRect when filled.
    If false: rect, isClosed, and direction are unchanged.
    If true: rect, isClosed, and direction are written to if not nullptr.

    rect may be smaller than the SkPath bounds. SkPath bounds may include kMove_Verb points
    that do not alter the area drawn by the returned rect.

    @param rect       storage for bounds of SkRect; may be nullptr
    @param isClosed   storage set to true if SkPath is closed; may be nullptr
    @param direction  storage set to SkRect direction; may be nullptr
    @return           true if SkPath contains SkRect

    example: https://fiddle.skia.org/c/@Path_isRect
*/
bool isRect(SkRect* rect, bool* isClosed = nullptr, SkPathDirection* direction = nullptr) const;
```

... the `isClosed` is critical, without checking that we're doing an
incorrect optimization.

I'll send a fix and test.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
No open projects
Archived in project
Development

Successfully merging this pull request may close these issues.

CustomPaint drawPath in iOS the Path will automatically close itself.
2 participants