Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Add shapeSmoothingEnabled option for CanvasRenderingContext2D #9192

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

nornagon
Copy link

@nornagon nornagon commented Apr 20, 2023

Fixes #3181

(See WHATWG Working Mode: Changes for more details.)


/acknowledgements.html ( diff )
/canvas.html ( diff )
/index.html ( diff )

Copy link
Member

@Kaiido Kaiido left a comment

Choose a reason for hiding this comment

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

Thank you very much for getting this moving.

Not an exhaustive review yet, and definitely not authoritative, but I wonder if this would need some deeper definition.
For instance we could hook into the fill and stroke steps. This might be the occasion to finally define the fill algorithm that they both do call (see #8145). But maybe this could be handled in a follow-up PR if that's too much work.

Another thing that might require discussion: it's relatively unclear if this should affect clip() or not.

cc @whatwg/canvas


<dd>
<p>Returns whether the fill and stroke of paths should be smoothed ("anti-aliased") when drawn,
if their pixels don't line up exactly with the display.</p>
Copy link
Member

Choose a reason for hiding this comment

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

My build complained about "may" -> "can".

Copy link
Author

Choose a reason for hiding this comment

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

I'm not quite sure why that would be? This line has neither of those words...

Copy link
Member

Choose a reason for hiding this comment

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

My review got completely mixed up by github with a prior one I started against the first commit, so all the line numbers are wrong, sorry. It used to be correct on the "files changed" tab but isn't anymore.
Anyway, the "may" in question is at line 68211

the user agent may choose to fill [...]

<div w-nodev>

<p>Objects that implement the <code>CanvasShapeSmoothing</code> interface have attributes that
control how shape smoothing is performed.</p>
Copy link
Member

Choose a reason for hiding this comment

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

The expression "shape smoothing" seems a bit vague here. Maybe it's best we talk about "rendering" in the authoritative parts? "Smoothing" could also refer to the act of converting angles to curves. To be clear, I still think "shapeSmoothinEnabled" is the right name for the method given imageSmoothingEnabled, it's really just here that I might prefer something clearer.

Copy link
Author

Choose a reason for hiding this comment

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

Makes sense. How about:

Suggested change
control how shape smoothing is performed.</p>
control how shape rasterization is performed.</p>

as this is really about the conversion of the shape into pixels?

Copy link
Member

Choose a reason for hiding this comment

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

Yes sounds better to me. Hopefully one day we'd have a formal step by step algo where this would fit in. But for now that might do.

<p>When false, the user agent may choose to fill and stroke paths without smoothing
("anti-aliasing").</p>

<p>Can be set, to change whether filled and stroked paths are smoothed (true) or, at the
Copy link
Member

Choose a reason for hiding this comment

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

"at the discretion of ..." sounds a bit funny. What about we call it a "hint" directly?
Maybe something along

Can be set, to hint to the user agent whether filled and stroked paths should be smoothed (true), or not (false).

Copy link
Author

Choose a reason for hiding this comment

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

"at the discretion of" follows the language in CanvasFontKerning, though I don't see that language anywhere else. I'm happy to follow your suggestion, though :)

<dt><code data-x=""><var>context</var>.<span subdfn data-x="dom-context-2d-shapeSmoothingEnabled">shapeSmoothingEnabled</span> [ = <var>value</var> ]</code></dt>

<dd>
<p>When false, the user agent may choose to fill and stroke paths without smoothing
Copy link
Member

@junov junov Apr 21, 2023

Choose a reason for hiding this comment

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

I support the idea that this option should be a hint, since UAs are not require to implement any form of anti-aliasing, but I think the wording should be imperative when it comes to disabling anti-aliasing since there are important use cases that require anti-aliasing to be disabled. In particular, the use case of drawing meshes without seams requires AA to be disabled. This has been a long-standing source of pain for developers who've been forced to limit their apps to use pixel-aligned rects, or switch to WebGL.

Copy link
Member

Choose a reason for hiding this comment

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

If this attribute were an enum instead of a bool, the "imperativeness" and "hint-ness" of the option could be implied in the names of the options in the enum. For example, the options could be "default" and "pixelated".

Copy link
Member

Choose a reason for hiding this comment

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

Based on past feature requests, I know many developers would want this option to apply to drawImage(), and IMHO it should also apply to fillRect and strokeRect, for consistency.

Copy link
Member

Choose a reason for hiding this comment

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

I agree this should also apply to strokeRect() and fillRect(), and I believe that if we do hook to the currently undefined fill operation I talked about in my previous comment we'd get them all in one place.

However I'm a bit curious about this drawImage() idea. How would that cohabit with imageSmoothing[Enabled|Quality]? Would one overrule the other? Based on what?

As for the attribute being an enum directly, I personally have no strong opinion, it came like that as a kind of symmetry with the existing imageSmoothing[...] attributes, but it's true that it's not necessary we follow this path. In that case we could also choose to get closer to SVG and their shape-rendering attribute.

Copy link
Author

Choose a reason for hiding this comment

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

I don't think this option should affect image smoothing, as @Kaiido says we already have imageSmoothingEnabled for that.

Copy link
Author

Choose a reason for hiding this comment

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

As for hintness, it seems like @junov is suggesting we flip the optionality here, to make drawing shapes with AA be optional when shapeSmoothingEnabled = true, but drawing shapes without AA be required when shapeSmoothingEnabled = false.

I agree with @junov that this is can be important for maintaining authorial intent. The initial suggestion to phrase this as a hint to the UA was from @mysteryDate here. SVG's shape-rendering attribute is also phrased as a hint.

My feeling is that phrasing shape smoothing as a hint is okay; there are several other properties of canvas that clearly convey authorial intent but are phrased as a "hint" in the spec. I don't see this one as being particularly different.

As for getting closer to SVG's shape-rendering, that seems not unreasonable. I'm not really sure what the difference here between crispEdges and optimizeSpeed is supposed to be, but it looks like Chromium treats them the same (both meaning "do not anti-alias"). I don't think I have enough domain expertise here to know if this is needless complexity or clever future-proofing. Perhaps @mysteryDate could weigh in?

@@ -63139,6 +63140,11 @@ interface mixin <dfn interface>CanvasImageSmoothing</dfn> {
-->
};

interface mixin <dfn interface>CanvasShapeSmoothing</dfn> {
// shape smoothing
attribute boolean <span data-x="dom-context-2d-shapeSmoothingEnabled">shapeSmoothingEnabled</span>; // (default true)
Copy link
Member

Choose a reason for hiding this comment

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

Let's not repeat the mistake of imageSmoothingEnabled. Please make this attribute an enum and just call it "shapeSmoothing" so that it can be extended in the future if necessary.

Copy link
Member

Choose a reason for hiding this comment

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

FWIW, future extension of this feature might include:

  • controlling qualitiy vs performance tradeoffs (number of MSAA samples, choice of analytical AA algorithm, etc.)
  • disallowing subpixel AA to avoid artifacts when images are re-scaled or composited

Copy link
Author

Choose a reason for hiding this comment

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

If we go in the enum direction I'm inclined to follow SVG's shape-rendering, e.g.

enum ShapeRenderingValue { "auto", "optimizeSpeed", "crispEdges", "geometricPrecision" }
interface mixin CanvasShapeRendering {
  attribute ShapeRenderingValue shapeRendering; // (default "auto")
}

How would you imagine those future extensions would be added? As additional options in the enum?

Copy link
Member

@junov junov left a comment

Choose a reason for hiding this comment

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

Thanks for helping to move this forward.

<dt><code data-x=""><var>context</var>.<span subdfn data-x="dom-context-2d-shapeSmoothingEnabled">shapeSmoothingEnabled</span> [ = <var>value</var> ]</code></dt>

<dd>
<p>When false, the user agent may choose to fill and stroke paths without smoothing
Copy link
Member

Choose a reason for hiding this comment

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

If this attribute were an enum instead of a bool, the "imperativeness" and "hint-ness" of the option could be implied in the names of the options in the enum. For example, the options could be "default" and "pixelated".

<dt><code data-x=""><var>context</var>.<span subdfn data-x="dom-context-2d-shapeSmoothingEnabled">shapeSmoothingEnabled</span> [ = <var>value</var> ]</code></dt>

<dd>
<p>When false, the user agent may choose to fill and stroke paths without smoothing
Copy link
Member

Choose a reason for hiding this comment

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

Based on past feature requests, I know many developers would want this option to apply to drawImage(), and IMHO it should also apply to fillRect and strokeRect, for consistency.

@@ -63139,6 +63140,11 @@ interface mixin <dfn interface>CanvasImageSmoothing</dfn> {
-->
};

interface mixin <dfn interface>CanvasShapeSmoothing</dfn> {
// shape smoothing
attribute boolean <span data-x="dom-context-2d-shapeSmoothingEnabled">shapeSmoothingEnabled</span>; // (default true)
Copy link
Member

Choose a reason for hiding this comment

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

FWIW, future extension of this feature might include:

  • controlling qualitiy vs performance tradeoffs (number of MSAA samples, choice of analytical AA algorithm, etc.)
  • disallowing subpixel AA to avoid artifacts when images are re-scaled or composited

@nornagon
Copy link
Author

@Kaiido I think it would make sense to mention this property in a more formal description of the fill algorithm, but I don't think this change should be blocked on that.

@Kaiido
Copy link
Member

Kaiido commented Apr 28, 2023

I did play a bit with the currently proposed chromium patch, and I believe a few more Path2D consumers should be affected.

As I presumed, it's a bit surprising at use that clip() isn't, and while one could manage something doing manually compositing + fill(), I believe it would be better for clip() to use that rule directly.

Then, it doesn't affect isPointInPath() and isPointInStroke() either. Here I'm quite convinced that it should. Otherwise we could end up in situations like this, which in the current chromium CL does say that the red pixel is not in the path, when visually it clearly is.

screenshot of test canvas showing a black aliased shape, with a red rectangle over said shape near an aliasing edge

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

Drawing crisp lines in canvas
3 participants