Re: [whatwg] Drawing shapes on canvas

On Fri, Jan 24, 2014 at 12:08 PM, Joe Gregorio <jcgregorio@google.com>wrote:

> Rik,
>   From the Skia perspective we have the following feedback on the proposal.
>

Thanks for your feedback!


>
>   While we can see how Shape2D can be implemented, it isn't clear that it's
>   such a large performance benefit over what Path provides, so we aren't
>   opposed to Shape2D, but don't believe implementing Path should be held up
>   for the sake of Shape2D.  Path itself is a huge win for performance over
>   having only a single current default path and has utility without the
> need
>   for Shape2D.
>

I agree.
Maybe it's OK to have a staged approach where we introduce the concept of
Path first and possible add shapes later.


>   In general we would like to see a layered approach to drawing objects,
>   starting with something primitive like Path, and at the most general a
>   Display List[1] that contains the full set of capabilities of
>   CanvasRenderingContext2D.  That layering could be done in two or three
>   objects, either [Path, DisplayList] or [Path, Shape2D, DisplayList]. In
> all
>   cases you can use the lower level objects to construct higher level
> objects,
>   i.e. use one or more Paths to build a Shape2D, and use Paths and Shape2Ds
> to
>   define a DisplayList.
>

That is an interesting concept. Do you have a more detailed proposal or a
prototype in the works?
At first blush, this seems more like streamlined SVG :-)

  What we'd like to see happen:
>     1. Review the Path add* methods and possibly simplify them, see below.
>     2. Keep the rest of the Path object and the Path related methods on the
>        CanvasRenderingContext2D.
>     3. If Shape2D moves forward do it by adding different versions of fill,
>        clip, etc to CanvasRenderingContext2D, such as:
>
>        void fill(optional CanvasFillRule fillRule = "nonzero");
>        void fill(Path path, optional CanvasFillRule fillRule = "nonzero");
>        void fill(Shape2D shape);
>
>     4. Possibly work on a DisplayList design, but only after some
>        experimentation.
>
>   We have some particular feedback on the Shape2D design that is inline
> below:
>
> [1] http://en.wikipedia.org/wiki/Display_list
>
> > All,
> >
> > around a year ago, I wrote a blog post [1] that introduced a new 'Shape'
> > class that described a filled or stroked region or an area of text.
> Java2D
> > has a similar concept that they call 'Area' [2].
> >
> > We've had some discussions but it doesn't look like there was any sort of
> > conclusion. I'd like to pick it back up now that we have a partial
> > implementation of the Path object and people are starting to look into
> > extending it.
> >
> > I'll reiterate my proposal:
> > 1. remove all the addxxx methods from the Path2D object [3]
> > Path object are just containers for segments. Aggregating segments will
> > generally not give the desired results since the segments will interact
> > (see [1]).
> > AddPath *could* be kept if people see a strong use case.
>
> The add* methods could be simplified to:
>
>   void addPath(Path path, SVGMatrix? transformation);
>   void addPathByStrokingPath(Path path, CanvasDrawingStyles styles,
> SVGMatrix? transformation);
>   void addText(DOMString text, CanvasDrawingStyles styles, SVGMatrix?
> transformation, unrestricted double x, unrestricted double y, optional
> unrestricted double maxWidth);
>   void addTextAlongPath(DOMString text, CanvasDrawingStyles styles,
> SVGMatrix? transformation, Path path, optional unrestricted double
> maxWidth);
>
> The functionality of the addPathByStrokingText methods below can be done by
> applying the above methods.
>
>   void addPathByStrokingText(DOMString text, CanvasDrawingStyles styles,
> SVGMatrix? transformation, unrestricted double x, unrestricted double y,
> optional unrestricted double maxWidth);
>   void addPathByStrokingText(DOMString text, CanvasDrawingStyles styles,
> SVGMatrix? transformation, Path path, optional unrestricted double
> maxWidth);
>

My issue with this add* methods is that they likely will not do what an
author expects since the path segments will interact in unexpected ways.
In addition, I think no browser follows the spec for "tracing a path" [1].
Since paths will interact with the segments that that algorithm generates,
we will get into interoperability issues.


> >
> > 2. introduce a new class 'Shape2D'
> > Interface:
> >
> > [Constructor,
> >   Constructor(Path2D , CanvasWindingRule = "nonzero"),
> >   Constructor(Path2D , CanvasDrawingStyles, SVGMatrix?), // strokes a
> path
> >   Constructor(DomString text, CanvasDrawingStyles, SVGMatrix?,
> unrestricted
> > double, unrestricted double, boolean isStroked = false, optional
> > unrestricted double)]
> > interface Shape2D{
> >     Shape2D transform(matrix); // returns a transformed path
>
> Why not do this as another constructor?
>
>   Constructor(Shape2D, SVGMatrix)
>

No particular reason. It's a bit more compact to do it with a method so you
don't have to call 'new'.


>
> >     Shape2D add(Shape2D); // returns a path that is the union of the 2
> paths
>
> Just to clarify, add() means a union of the two shapes, with no
> side-effects
> resulting from winding rules of the individual path?
>

Yes


>
> Shape2D seems reasonable and useful just as an immutable capture of all
> "coverage" aspects of geometry. If add() is hard for platforms to support,
> or
> is expensive and clients don't know that, perhaps we can leave it out of
> this
> version. If we really want to have add(), why not include the full
> compliment
> of Set operations  [ diff, xor, intersect ], which are no harder to
> implement
> (afaik) once you've implemented add().
>

The reason I didn't add them, is that it's straightforward for
implementation that don't have access to such a library.
For instance, if there was a union of a fill and a stroke, you could call
'fill' and 'stroke' under the hood.


>
> > }
> >
> > This class will represent a painted area. Because it knows the winding
> and
> > stroking rules, the browser will be able to do expensive math in advance.
> > It can also cache the region on the GPU.
> > constructors:
> > a. default constructor that creates an empty region
> > b. constructor that take a path and a winding rule. This represents a
> > filled region
> > c. constructor that takes a path, canvasDrawingStyles object and a
> matrix.
> > This represent a stroked region.
> > d. constructor that takes text + canvasDrawingStyles. This represent a
> > region of filled or stroked text.
> >
> > methods:
> > a. transform -> transform the shape by the matrix and returns a new shape
> > b. add -> add the region of the shape to the current shape and return as
> a
> > new shape
> >
> > 3. Add new methods:
> fill(Shape2D)/clip(Shape2D)/isPointInShape(Shape2D,...)
> >
> > 4. remove stroke(path), fill(path), clip(path), isPointInPath(path,...)
> >
> > 5. HitRegionOptions takes a Shape2D instead of a path + winding rule
> >
> > What do people think?
> >
> >
> > 1: http://blogs.adobe.com/webplatform/2013/01/31/revised-canvas-paths/
> > 2: http://docs.oracle.com/javase/tutorial/2d/advanced/complexshapes.html
> > 3:
>

1:
http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#trace-a-path

Received on Monday, 27 January 2014 18:26:53 UTC