Re: [whatwg] Canvas and Paths

2014-03-17 Thread Rik Cabanier

 On Mon, 10 Mar 2014, Tab Atkins Jr. wrote:
 
  This is also my question.  Given that generating a path for a particular
  context isn't a magic bullet *anyway* (because the details of the
  context can change), I don't understand why caching isn't the answer.

 On Mon, 10 Mar 2014, Rik Cabanier wrote:
 
  At usage time, the path could be retargeted to a new backend.

 If the backend changes, knowing the backend at creation time doesn't help.

 If it doesn't, then the cost seems to be the same either way.


  I don't think that should be done as a cached copy since that would
  require too many resources. I will see if this is an acceptable solution
  for mozilla.

 How many resources could a path possibly take?


 On Mon, 10 Mar 2014, Justin Novosad wrote:
 
  Isn't caching ideal for that situation? In the case of re-targeting, you
  can either replace the cached encoding, or append the new encoding to a
  collection of cached encodings.  Both of those options seem more
  effective than to stick to an encoding type that was baked-in at
  construction time. It may also be great to have a heuristic to chose
  whether to discard the previously cached re-encoding. Something like: if
  we are re-encoding because the destination backing type changed due to a
  resize, then discard previous encodings; if re-encoding because the path
  is drawn to multiple canvases, then retain multiple cached encodings.

 That makes sense to me.


FYI
The Firefox people agreed to a solution that retargets the path if its
backend doesn't match with the canvas context backend.
There's no need to change to current API.


[whatwg] Canvas and Paths

2014-03-12 Thread Ian Hickson

On Thu, 28 Nov 2013, Rik Cabanier wrote:
 On Thu, Nov 28, 2013 at 8:30 AM, Jürg Lehni li...@scratchdisk.com 
 wrote:
 
  I meant to say that it I think it would make more sense if the path 
  was in the current transformation matrix, so it would represent the 
  same coordinate values in which it was drawn, and could be used in the 
  same 'context' of transformations applied to the drawing context later 
  on.
 
 No worries, it *is* confusing. For instance, if you emit coordinates and 
 then scale the matrix by 2, those coordinates from getCurrentPath will 
 have a scale of .5 applied.

That's rather confusing, and a pretty good reason not to have a way to go 
from the current default path to an explicit Path, IMHO.

Transformations affect the building of the current default path at each 
step of the way, which is really a very confusing API. The Path API on the 
other hand doesn't have this problem -- it has no transformation matrix. 
It's only when you use Path objects that they get transformed.


  So this is not how most implementations currently have it defined.
 
 I'm unsure what you mean. Browser implementations? If so, they 
 definitely do store the path in user coordinates. The spec currently 
 says otherwise [1] though.

I'm not sure what you're referring to here.


 It would be a very fast way to set a cached path in the graphics state

What would the purpose of this be? You can just pass the path to the 
relevant functions instead, no?


 Another use case is to allow authors to quickly migrate to hit regions.
 
 ctx.beginPath(); ctx.lineTo(...); ...; ctx.fill();
 ... // lots of complex drawing operation for a control
 ctx.beginPath(); ctx.lineTo(...); ...; ctx.stroke();
 
 
 To migrate that to a region (with my proposed shape interface [1]):
 
 var s = new Shape();
 
 ctx.beginPath(); ctx.lineTo(...); ...; ctx.fill(); s.add(new
 Shape(ctx.currentPath));
 ...
 ctx.beginPath(); ctx.lineTo(...); ...; ctx.stroke(); s.add(new
 Shape(ctx.currentPath, ctx.currentDrawingStyle));
 
 ctx.addHitRegion({shape: s, id: control});

Why not just add ctx.addHitRegion() calls after the fill and stroke calls?


On Fri, 6 Dec 2013, Jürg Lehni wrote:
 
 Instead of using getCurrentPath and setCurrentPath methods as a 
 solution, this could perhaps be solved by returning the internal path 
 instead of a copy, but with a flag that would prevent further 
 alterations on it.
 
 The setter of the currentPath accessor / data member could then make the 
 copy instead when a new path is to be set.
 
 This would also make sense from a a caching point of view, where storing 
 the currentPath for caching might not actually mean that it will be used 
 again in the future (e.g. because the path's geometry changes completely 
 on each frame of an animation), so copying only when setting would 
 postpone the actual work of having to make the copy, and would help 
 memory consummation and performance.

I don't really understand the use case here.


On Fri, 29 Nov 2013, Rik Cabanier wrote:
 On Mon, Nov 25, 2013 at 3:55 PM, Ian Hickson i...@hixie.ch wrote:
  On Sat, 28 Sep 2013, Rik Cabanier wrote:
   On Fri, Sep 27, 2013 at 2:08 PM, Ian Hickson i...@hixie.ch wrote:
On Thu, 5 Sep 2013, Rik Cabanier wrote:
 On Thu, Sep 5, 2013 at 3:22 PM, Ian Hickson i...@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.

I don't really understand when the snapping happens in this proposal. 
Consider a rotation transformation matrix. How exactly does the snapping 
work? In a space that's transformed by 45 degress, how would lines drawn 
at 44, 45, and 46 degrees to the horizontal, all 

Re: [whatwg] Canvas and Paths

2014-03-12 Thread Rik Cabanier
On Wed, Mar 12, 2014 at 3:44 PM, Ian Hickson i...@hixie.ch wrote:


 On Thu, 28 Nov 2013, Rik Cabanier wrote:
  On Thu, Nov 28, 2013 at 8:30 AM, Jürg Lehni li...@scratchdisk.com
  wrote:
  
   I meant to say that it I think it would make more sense if the path
   was in the current transformation matrix, so it would represent the
   same coordinate values in which it was drawn, and could be used in the
   same 'context' of transformations applied to the drawing context later
   on.
 
  No worries, it *is* confusing. For instance, if you emit coordinates and
  then scale the matrix by 2, those coordinates from getCurrentPath will
  have a scale of .5 applied.

 That's rather confusing, and a pretty good reason not to have a way to go
 from the current default path to an explicit Path, IMHO.

 Transformations affect the building of the current default path at each
 step of the way, which is really a very confusing API. The Path API on the
 other hand doesn't have this problem -- it has no transformation matrix.
 It's only when you use Path objects that they get transformed.


This happens transparently to the author so it's not confusing.
For instance:

ctx.rect(0,0,10,10);

ctx.scale(2,2); - should not affect geometry of the previous rect
ctx.stroke(); - linewidth is scaled by 2, but rect is still 10x10



   So this is not how most implementations currently have it defined.
 
  I'm unsure what you mean. Browser implementations? If so, they
  definitely do store the path in user coordinates. The spec currently
  says otherwise [1] though.

 I'm not sure what you're referring to here.


All graphics backends for canvas that I can inspect, don't apply the CTM to
the current path when you call a painting operator.
Instead, the path is passed as segments in the current CTM and the graphics
library will apply the transform to the segments.


  It would be a very fast way to set a cached path in the graphics state

 What would the purpose of this be? You can just pass the path to the
 relevant functions instead, no?


I think we can defer the currentPath discussion for now.


  Another use case is to allow authors to quickly migrate to hit regions.
 
  ctx.beginPath(); ctx.lineTo(...); ...; ctx.fill();
  ... // lots of complex drawing operation for a control
  ctx.beginPath(); ctx.lineTo(...); ...; ctx.stroke();
 
 
  To migrate that to a region (with my proposed shape interface [1]):
 
  var s = new Shape();
 
  ctx.beginPath(); ctx.lineTo(...); ...; ctx.fill(); s.add(new
  Shape(ctx.currentPath));
  ...
  ctx.beginPath(); ctx.lineTo(...); ...; ctx.stroke(); s.add(new
  Shape(ctx.currentPath, ctx.currentDrawingStyle));
 
  ctx.addHitRegion({shape: s, id: control});

 Why not just add ctx.addHitRegion() calls after the fill and stroke calls?


That does not work as the second addHitRegion will remove the control and
id from the first one.
The 'add' operation is needed to get a union of the region shapes.


 On Fri, 6 Dec 2013, Jürg Lehni wrote:
 
  Instead of using getCurrentPath and setCurrentPath methods as a
  solution, this could perhaps be solved by returning the internal path
  instead of a copy, but with a flag that would prevent further
  alterations on it.
 
  The setter of the currentPath accessor / data member could then make the
  copy instead when a new path is to be set.
 
  This would also make sense from a a caching point of view, where storing
  the currentPath for caching might not actually mean that it will be used
  again in the future (e.g. because the path's geometry changes completely
  on each frame of an animation), so copying only when setting would
  postpone the actual work of having to make the copy, and would help
  memory consummation and performance.

 I don't really understand the use case here.


Jurg was just talking about an optimization (so you don't have to make an
internal copy)


Re: [whatwg] Canvas and Paths

2014-03-12 Thread Rik Cabanier

   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.

 You say, here are some paths, here are some fill rules, here are some
 operations you should perform, now give me back a path that describes the
 result given a particular fill rule.


I think your collapsing a couple of different concepts here:

path + fillrule - shape
union of shapes - shape
shape can be converted to a path



 A shape is just a path with a fill rule, essentially.


So, a path can now have a fillrule? Sorry, that makes no sense.
A path is just a set of segments; there should be no magic


 Anything you can do
 with one you can do with the other.


No.
You can't add segments from one shape to another as shapes represent
regions.
Likewise, you can't union, intersect or xor path segments.


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)

 I think wrong here implies a value judgement that's unwarranted.


Wrong meaning:
 if the author has a bunch of geometry and wants to put it in 1 path object
so he can just execute 1 fill operation, he might be under the impression
that adding the geometry will just work.
There are very few use cases where you want to add partial path segments
together but I agree that there are some cases that it's useful to have.


  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.

 I don't understand why you think joins and end caps are drawn separately.
 That is not what the spec requires.


Sure it does, for instance from
http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#trace-a-path
:

The round value means that a filled arc connecting the two aforementioned
corners of the join, abutting (and not overlapping) the aforementioned
triangle, with the diameter equal to the line width and the origin at the
point of the join, must be added at joins.


If you mean, drawn with a separate fill call, yes that is true.
What I meant was that they are drawn as a separate closed path that will
interact with other paths as soon as there are different winding rules or
holes.


   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.

 I don't see why not. It's exactly what happens today if you were to just
 add the same path creation statements together into the current default
 path and fill or stroke that.


Sure but who does that?
These two operations don't look the same:

 ...// draw geometry A

ctx.fill();

...// draw geometry B

ctx.fill();

and

...// draw geometry A
...// draw geometry B
ctx.fill();



   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.

 That's addPathByStrokingPath(). (stroke() is essentially just
 addPathByStrokingText() followed by a fill().)


   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)'.

 That's not a use case, it's just a different way of implementing the same
 use case. Why is it better? It seems distinctly more awkward and less
 idiomatic to have to set an implicit property and then call a method, than
 just call the method with the path.

 Analogously, we don't say:

Math.angle = Math.PI * 3;
var x = Math.sin();

 ...we say:

var x = Math.sin(Math.PI * 3);


  It also make it more intuitive 

Re: [whatwg] Canvas and Paths

2014-03-12 Thread Rik Cabanier


 I don't think the arguments for removing these are compelling. The
 problems with the APIs have been addressed (e.g. there's no ambiguity in
 the case of overlapping text), the use cases are clear (e.g. drawing text
 around an arc or drawing a label along a line graph's line), and the API
 now supports the constructs to do unions of fill regions.


 Where is the union of fill regions specified? All I see is segments
 aggregation.


I found this in the spec:
http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-path-merge
Unfortunately, it is not enough because it doesn't let you aggregate paths
with different winding rules.

It also can't be implemented efficiently (or at all) since it relies on a
planarizer.