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

Conversation

ds84182
Copy link
Contributor

@ds84182 ds84182 commented Nov 15, 2019

Closes flutter/flutter#44134

web_ui does not implement dithering, since it's more of a hint to the underlying renderer.

Can test with the following code snippet:

import 'dart:typed_data';
import 'dart:ui';

void onFrame(_) {
  final pictureRecorder = PictureRecorder();
  final canvas = Canvas(pictureRecorder);
  canvas.drawPaint(Paint()
    ..dither = true
    ..shader = Gradient.linear(Offset.zero,
        Offset(window.physicalSize.width, window.physicalSize.height), [
          Color(0xFF4C4D52),
          Color(0xFF202124),
        ]));
  final picture = pictureRecorder.endRecording();
  final sceneBuilder = SceneBuilder()
    ..pushTransform(Float64List(16)
      ..[0] = 1.0
      ..[5] = 1.0
      ..[10] = 1.0
      ..[15] = 1.0)
    ..addPicture(Offset.zero, picture)
    ..pop();
  final scene = sceneBuilder.build();

  window.render(scene);

  window.scheduleFrame();
}

void main() {
  window.onBeginFrame = onFrame;

  window.scheduleFrame();
}

Without dithering, the banding is very obvious on all devices. With dithering, the banding disappears completely.

@googlebot
Copy link

We found a Contributor License Agreement for you (the sender of this pull request), but were unable to find agreements for all the commit author(s) or Co-authors. If you authored these, maybe you used a different email address in the git commits than was used to sign the CLA (login here to double check)? If these were authored by someone else, then they will need to sign a CLA as well, and confirm that they're okay with these being contributed to Google.
In order to pass this check, please resolve this problem and then comment @googlebot I fixed it.. If the bot doesn't comment, it means it doesn't think anything has changed.

ℹ️ Googlers: Go here for more info.

1 similar comment
@googlebot
Copy link

We found a Contributor License Agreement for you (the sender of this pull request), but were unable to find agreements for all the commit author(s) or Co-authors. If you authored these, maybe you used a different email address in the git commits than was used to sign the CLA (login here to double check)? If these were authored by someone else, then they will need to sign a CLA as well, and confirm that they're okay with these being contributed to Google.
In order to pass this check, please resolve this problem and then comment @googlebot I fixed it.. If the bot doesn't comment, it means it doesn't think anything has changed.

ℹ️ Googlers: Go here for more info.

@googlebot
Copy link

CLAs look good, thanks!

ℹ️ Googlers: Go here for more info.

1 similar comment
@googlebot
Copy link

CLAs look good, thanks!

ℹ️ Googlers: Go here for more info.

@Hixie
Copy link
Contributor

Hixie commented Nov 20, 2019

I'd rather we didn't expose this. We should just make the gradients look good regardless. We shouldn't need the developer to control this.

/// Some implementations may choose to ignore this completely, if they're
/// unable to control dithering.
bool get dither => false; // Not implemented on web
set dither(bool value) {}
Copy link
Contributor

Choose a reason for hiding this comment

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

if we do expose this, we should implement it on web.
if we do expose this and don't implement it on web, we should still make the property mutable, so that code that relies on it mutating doesn't randomly break on web.

@ds84182
Copy link
Contributor Author

ds84182 commented Nov 20, 2019

@Hixie

I'd rather we didn't expose this. We should just make the gradients look good regardless. We shouldn't need the developer to control this.

I agree, however it might be a large change to do. It doesn't seem to break hard-stop gradients (like those used for the overflow indicator).

if we do expose this, we should implement it on web

I somewhat agree, but maybe the name of the property should be changed to ditherHint. The main issue is that the dithering flag is just a hint, and Skia will choose if it should dither or not. This also extends to web browsers, although I'm not 100% sure because I don't think Chrome will like being run on an RGB565 display but I'm willing to bet dithering gets enabled then.

If not supporting it on web is fine, I can add the backing field to web_ui Paint. The documentation already explains that the hint may be ignored.

@ds84182
Copy link
Contributor Author

ds84182 commented Nov 21, 2019

After some discussion I've moved over to a static field so it can be enabled and disabled globally. The dither property has been made private for now, and it defaults to the value of Paint.enableDithering at construction time. Apps that want to opt into dithered gradients should set Paint.enabledDithering before they do any drawing.

Still isn't enabled by default, but enabling it by default shouldn't have noticeable performance impacts. Most of the concern comes from whether having non-dithered gradients is a valid usecase.

@Hixie
Copy link
Contributor

Hixie commented Dec 2, 2019

This will need tests.

I assume the new global is temporary? Can we mark it as deprecated? What's the timeframe by which we plan to flip the default and remove it?

@ds84182
Copy link
Contributor Author

ds84182 commented Dec 3, 2019

@Hixie

This will need tests.

Haven't added tests to the engine repository before. Where should the test live? I was browsing around for paint related tests, but I didn't see any.

I assume the new global is temporary? Can we mark it as deprecated? What's the timeframe by which we plan to flip the default and remove it?

The global value is temporary, since you would either want to opt in entirely or opt out entirely. However, we don't know if this is the right default behavior to have. Initially, it would be great to initialize this to true in PaintingBinding.initInstances on the master branch to estimate impact, and if apps need to opt-out they can set the value to false after their call to runApp.

But as of right now, the whole issue is finding out if apps rely on non-dithered gradients, and giving their developers an avenue to reach out if a default behavior change (which is a breaking change) needs a better, more permanent, escape hatch.

This may also have a very very slight performance impact for gradients heavy apps on less-powerful devices, if used by default. It isn't a big deal for low-end Android devices, but there might be smaller near-embedded devices (like Raspberry Pi) that might be affected by the change.

@Hixie
Copy link
Contributor

Hixie commented Dec 4, 2019

@liyuqian @cbracken can you help @ds84182 with writing tests for this? Thanks!

But as of right now, the whole issue is finding out if apps rely on non-dithered gradients, and giving their developers an avenue to reach out if a default behavior change (which is a breaking change) needs a better, more permanent, escape hatch.

Seems reasonable.

This may also have a very very slight performance impact for gradients heavy apps on less-powerful devices, if used by default. It isn't a big deal for low-end Android devices, but there might be smaller near-embedded devices (like Raspberry Pi) that might be affected by the change.

Presumably gradient-heavy apps are the ones most likely to want dithering...

I wouldn't worry about the lower-end devices, we don't support those anyway. The Nest Home Hub is basically the lowest-end device I would worry about, as far as this feature goes.

@liyuqian
Copy link
Contributor

liyuqian commented Dec 5, 2019

@ds84182 : maybe put your test in https://github.com/flutter/engine/blob/master/testing/dart/canvas_test.dart ? There's already some Dart code that exercises Paint creation, Canvas drawing, and even image comparison.

@ds84182
Copy link
Contributor Author

ds84182 commented Dec 9, 2019

Added two image comparison tests, PTAL

Copy link
Contributor

@liyuqian liyuqian left a comment

Choose a reason for hiding this comment

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

LGTM! The dithered gradient golden image looks great to me. Can't wait to actually see them in the Flutter app on my phone. Currently the circular gradient animation of loading images does look quite banded. Any ETA on when I can see them in your app? 😃

/// Constructs an empty [Paint] object with all fields initialized to
/// their defaults.
Paint();

Copy link
Contributor

Choose a reason for hiding this comment

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

this should be unnecessary, FWIW. The constructor is normally implied.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When it was omitted one of the web UI lints complained

Copy link
Contributor

Choose a reason for hiding this comment

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

Remove the one in lib/ui/painting.dart too and the lint should go away.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That constructor can't be removed because its used to set _dither to true if enableDithering is true. I could use cascade syntax on _data's initializer, but readability suffers a little.

Copy link
Contributor

@liyuqian liyuqian Dec 11, 2019

Choose a reason for hiding this comment

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

I see. Ping @jonahwilliams and @yjbanov to see if the web tools (the lint) can be relaxed in this case. If not, I'm Ok with just having this dummy constructor. My experience is that there's already a lot of dummy/duplicate code in lib/web_ui. The ideal future would be that we don't need any duplicate code in lib/web_ui, and this issue would go away naturally.

Copy link
Contributor

Choose a reason for hiding this comment

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

Let's keep this one. We are splitting the implementation between HTML and CanvasKit (#14320), and this constructor is becoming a factory, which we cannot omit.

@ds84182 ds84182 merged commit 80d80ff into flutter:master Dec 11, 2019
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Dec 13, 2019
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Dec 13, 2019
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Dec 13, 2019
iskakaushik pushed a commit to flutter/flutter that referenced this pull request Dec 13, 2019
* fb9dfe0 [fuchsia] Move async_get_default_dispatcher include to the header (flutter/engine#14351)

* 3ebb7b4 Roll src/third_party/skia 75799967be60..3517aa7b14ad (3 commits) (flutter/engine#14345)

* 2713225 Remove duplicate and outdated src/third_party/dart/tools/sdks entry from DEPS. (flutter/engine#14340)

* 80d80ff Add ability to control dithering on Paint (flutter/engine#13868)

* 8595361 Conditionally use offscreen root surface only when needed

* 0a40f3d Assert that arc end caps on canvases with root surface transformations are drawn correctly. (flutter/engine#14359)

* d698d96 Fix missing timeline event of flutter engine's startup time (flutter/engine#14319)

* 9dc23b8 Fix missing API stream when record event in systrace (flutter/engine#14323)

* 9e4c6ad Fix CGMutablePathRef memory leaks when the path is invalid. (flutter/engine#14275)

* fc8cafb objcdoc fix for some ambiguity (flutter/engine#14367)

* 9bafb3c [tests] Use distinct begin and end times (flutter/engine#14361)

* 897ce34 Roll src/third_party/skia 3517aa7b14ad..826484f2631f (18 commits) (flutter/engine#14375)

* 1ce85be [flutter_runner] Enable Skia tracing by default on Fuchsia (flutter/engine#13457)

* a7b6ee5 Smart quote/dash configuration support in iOS (flutter/engine#13863)

* 48ba39c Roll fuchsia/sdk/core/mac-amd64 from otkJA... to SlgE8... (flutter/engine#14407)

* 0081e8c Remove unused _TypeNone enum field. (flutter/engine#14440)

* d8edfea Roll src/third_party/dart d9fa37e85d5c..45db29709547 (48 commits) (flutter/engine#14453)

* f650bca Refactoring text editing. Strategy pattern is used to handle different browser/operating system and a11y behavior. (flutter/engine#14131)

* 4275b49 Fix type in build_fuchsia_artifacts (flutter/engine#14452)

* 0c24f3d Roll src/third_party/skia 51b99659ed82..c514e7d9be6e (13 commits) (flutter/engine#14457)

* ffbe2a4 [testing] Move test vsync waiters to their own TUs (flutter/engine#14456)

* 181ad4e Use futures to images used for comparison with fixtures in embedder unit-tests. (flutter/engine#14465)

* e0e0ac0 [testing] Make vsync waiters pluggable in shell_unittests (flutter/engine#14463)
@lukepighetti
Copy link

lukepighetti commented Feb 25, 2020

What’s the status of this PR? I’m having banding issues on an app I’m working on. It’s very noticeable when you start dealing with gradients based on blacks. I agree that gradients should just look good from the start but they clearly don’t and so dithering would be an ideal shim especially since it appears to be low hanging fruit.

@liyuqian
Copy link
Contributor

@lukepighetti : this has been rolled into Flutter framework on flutter/flutter@bcacd06. It has not made to the last stable release, but it's in our beta (v1.14.6) and master branches.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add a dither parameter to dart:ui's Paint class
6 participants