[whatwg] Drawing shapes on canvas and feedback thereon

Rik Cabanier cabanier at gmail.com
Fri Nov 29 15:16:43 PST 2013


On Mon, Nov 25, 2013 at 3:55 PM, Ian Hickson <ian at hixie.ch> wrote:

>
> On Sat, 28 Sep 2013, Rik Cabanier wrote:
> > On Fri, Sep 27, 2013 at 2:08 PM, Ian Hickson <ian at hixie.ch> wrote:
> > > On Thu, 5 Sep 2013, Rik Cabanier wrote:
> > > > On Thu, Sep 5, 2013 at 3:22 PM, Ian Hickson <ian at hixie.ch> wrote:
> > > > > On Sat, 10 Aug 2013, Rik Cabanier wrote:
> > > > > >
> > > > > > I was wondering if this is something that happens in Flash as
> > > > > > well. It turns out that there's an option called "hinting: Keep
> > > > > > stroke anchors on full pixels to prevent blurry lines." [...]
> > > > > >
> > > > > > I think canvas should have a similar feature...
> > > > >
> > > > > Can you elaborate on how exactly you would want this to work? How
> > > > > would you avoid the alignment and distortion problems when
> > > > > applying this to anything less trivial than a rectangle?
> > > >
> > > > Basically, this would *just* move the control points and the width
> > > > of paths so the strokes are always aligned to the pixel grid (This
> > > > would take pixel density and transformations into account). After
> > > > this, you would draw as usual.
> > >
> > > Can you define "aligned to the pixel grid"?
> > >
> > > If I have a line from x1,y to x2,y, followed by an arc from x2,y back
> > > to x1,y with radius r, what should happen and why?
> >
> > Align the anchor points of all the segments. Don't change any of the
> > anti-aliasing behavior.
>
> How does this differ from simply always using integers for coordinates?
>

It would simplify the process for the developer as it might be difficult to
determine what an "integer" coordinate is, especially if there is a complex
CTM in effect.
For instance, if the scale is .5, you'd have to round to a multiple of 2.

> > What if they're draw as separate paths?
> >
> > I'm unsure if I follow. That shouldn't make a different. What might be
> > different however, is if you draw a diagonal line in 1 segment or 2
> > since the middle point will be aligned to the grid in the latter case.
>
> Consider a case like this:
>
>    http://goo.gl/VFsLqj
>
> How do you keep the diagonal line exactly touching the arc?
>

You would not :-)
If the pixel that  would cover over 50%, it would be filled.

If the developer is interested in geometric precision, he shouldn't request
sharp lines.


> On Wed, 16 Oct 2013, Rik Cabanier wrote:
> > > > >
> > > > > Once the Path API is implemented, we can add any number of
> > > > > features, such as adding a path so as to form the union of both
> > > > > paths, adding a path so as to form the intersection of both paths,
> > > > > etc.
> > > >
> > > > OK. In order to make that possible, 'addPath',
> > > > 'addPathByStrokingPath', 'addText' and 'addTextByStrokingText'
> > > > should be cut from the interface.
> >
> > Paths are a construct that can be filled with a fill rule or stroked.
> > After this, they are no longer paths but regions of a certain color.
>
> That's one way to design the API, but it doesn't seem to be the only way.
> Another way is the way the API is currently written: you have paths, and
> when you fill them, you get pixels. No need for the intermediate "region"
> concept. I don't understand why you don't like that approach. You can do
> unions and so forth with just paths, no need for regions.
>

How would you do a union with paths?
If you mean that you can just aggregate the segments, sure but that doesn't
seem very useful.


>
>
> > The same is true for text. (Your recent addition that describes a stroke
> > as sweeping a line following the path, is starting to align with that.)
>
> I don't understand why the new description would be in any way different
> than the previous one. It's clearer, hopefully, and maybe less ambiguous
> in some cases, but it's still just describing converting one path into
> another path.
>

that is true. Now that I studied the algorithm a bit more, I can see that
it's not not substantially different and that it creates several paths (NOT
1 path) that are carefully drawn with the correct winding so you don't get
unexpected behavior.

> The path object should represent the path in the graphics state. You
> > can't add a stroked path or text outline to the graphics state and then
> > fill/stroke it.
>
> Why not?
>

As designed today, you could fill it, as long as you use non-zero winding.
If you use even-odd, the results will be very wrong. (ie where joins and
line segments meet, there will be white regions)

Stroking will be completely wrong too, because joins and end caps are drawn
separately, so they would be stroked as separate paths. This will not give
you the effect of a double-stroked path.


>
> > > > So please remove the APIs such as AddPathByStrokingPath that rely on
> > > > these difficult algorithms.
> >
> > Those APIs have to know how to turn strokes and text into paths (which
> > is not trivial and no UA does by default). In addition if you want to
> > have useful output (= not getting unexpected interactions of the winding
> > rules), the APIs would need to use planarization for stroke and text
> > outlines. See
> > http://blogs.adobe.com/webplatform/2013/01/31/revised-canvas-paths/ for
> > a description of the issue.
>
> We seem to be going around in circles. We're in agreement that eventually
> we should add APIs for combining paths such that we get the equivalent of
> the union of their fill regions. I agree that converting text into paths
> is non-trivial (lots of stuff browsers do is non-trivial, that's kind of
> the point -- if it was trivial, we could leave it for authors). But I
> don't see how we get from there to you wanting the existing APIs removed.
>

I want them removed because they will most likely not behave in the way
that an author expects. When he "adds" 2 paths, he wouldn't expect that
there is 'interference' between them.

>
> On Sun, 27 Oct 2013, Rik Cabanier wrote:
> >
> > I think we should update the algorithm [at
> >
> http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#trace-a-path
> > ] so the problem is broken into stages: [...]
>
> Before saying what you want, please describe the problem.
>

You can ignore this. This was settled in subsequents threads.


>
>
> On Mon, 28 Oct 2013, Justin Novosad wrote:
> >
> > If I understand correctly, the need to produce a path that is winding
> > agnostic is for cases where we are not stroking to the display. It is
> > for cases where the API produces a stored path that could be further
> > manipulated.  The only examples of this that come to my mind right now
> > are the addPathByStrokingPath and addPathByStrokingText methods of the
> > Path object interface.  Is that what you had in mind?  Perhaps the
> > requirement for generating winding agnostic paths should be specific to
> > those two methods?
>
> The only methods for which the spec currently requires user agents to
> ensure that they create only paths that wind clockwise, and for which this
> has any practical impact as far as I can tell, are the Path methods
> addText(), addPathByStrokingText(), and addPathByStrokingPath().
>

don't forget tracing a path.

On Sat, 2 Nov 2013, Rik Cabanier wrote:
> >
> > Sorry, I misunderstood. Yes, we want to move to separate path/shape
> > objects that can be set atomically (=much faster) on the canvas context.
>
> Why? With Path objects, you don't have to set them at all -- just paint
> straight from the Path object using stroke(path) or fill(path).
>

true.


>
>
> On Mon, 4 Nov 2013, Jürg Lehni wrote:
> >
> > I like this feature a lot. One advantage to not underestimate is the
> > amount of effort it takes to change existing code to make use off the
> > new Path feature, while staying backward compatible with older browsers
> > that don't implement this spec. For example, in Paper.js it took only
> > three added lines of code to use cached paths if they exist rather than
> > redrawing them each time.
>
> Being able to get a Path from the context if it exists seems reasonable,
> though it's non-trivial to define what that is given the way that
> transforms affect the context implied path.
>

Why is it non-trivial? UA have to calculate this path (wrt the current CTM
anyway).


>
>
> On Mon, 4 Nov 2013, Rik Cabanier wrote:
> >
> > In light of this, does anyone have objections to these 2 new methods:
> >
> > Path getCurrentPath();
> > void setCurrentPath(Path);
>
> (The right question is "does anyone want these methods". The bar is
> higher than just no objections.)
>
> What's the use case for setCurrentPath()?
>

It would negate the need for a 'fill(path)', 'stroke(path)' and
'clip(path)'.
It also make it more intuitive since you can call:
ctx.setCurrentPath(ctx2.getCurrentPath());


>
> The functionality of a getCurrentPath() method seems reasonable for the
> reasons Jürg gave above.
>
> Another way to do it would be to have the Path constructor take a
> rendering context object. That would be more consistent with how Path
> objects work so far.
>

true.


>
>
> On Mon, 4 Nov 2013, Robert O'Callahan wrote:
> >
> > If you return a path in user-space, what do you get if you call
> > getCurrentPath with a singular transform?
> >   ctx.moveTo(0,0);
> >   ctx.lineTo(1,1);
> >   ctx.scale(0,0);
> >   var p = ctx.getCurrentPath();
> > ?
>
> The scale() call here has no effect. The default path is affected by
> transforms when you add the path segments, not when you paint. It would be
> consistent to do the same when converting it to a Path object.
>

No, the CTM definitely affects what you paint. Stroke width and gradients
for instance.


>
> This would, of course, mean that this wouldn't have the expected effect
> with transforms, since they'd be amplified:
>
>    c.beginPath();
>    c.scale(2,2);
>    c.moveTo(0,0);
>    c.lineTo(1,1);
>    var p = new Path(c);
>    c.stroke(); // strokes the current default path from 0,0 to 2,2
>    c.stroke(p); // strokes p from 0,0 to 4,4 !
>
>    c.resetTransform();
>    c.stroke(p); // strokes the current default path from 0,0 to 2,2
>

No, these will look the same. 'new Path(c)' establishes the same path in
the same user coordinate space.


>
>
> On Mon, 4 Nov 2013, Rik Cabanier wrote:
> >
> > However, for your example, I'm unsure what the right solution is. The
> > canvas specification is silent on what the behavior is for
> > non-invertible matrices.
>
> What question do you think the spec doesn't answer?
>
> > I think setting scale(0,0) or another matrix operation that is not
> > reversible, should remove drawing operations from the state because:
> > - how would you stroke with such a matrix?
>
> You'd get a point.
>

How would you get a point? the width is scaled to 0.


>
> > - how do patterns operate? the same for gradient fills.
>
> This is defined:
>
> "If a radial gradient or repeated pattern is used when the transformation
> matrix is singular, the resulting style must be transparent black
> (otherwise the gradient or pattern would be collapsed to a point or line,
> leaving the other pixels undefined). Linear gradients and solid colors
> always define all points even with singular tranformation matrices."
>

How is this better than not drawing?



More information about the whatwg mailing list