[whatwg] Hit region + fillRule concerns

2013-01-15 Thread Simon Sarris
I replied to Rik Cabanier over on the W3 mailing list and figured I should
post here as well since nobody seems to look at the W3 Canvas list.

For the record I'm referring to:
http://lists.w3.org/Archives/Public/public-canvas-api/2013JanMar/0001.html

-

The current *fillRule *rules in the specification seem incomplete or at
least ill-defined with respect to consideration of hit regions.

The new hit regions use a *Path* in the *HitRegionOptions* (the argument to
*addHitRegion*) in order to function, but its not clear what fill rule these
*Path* objects are using for hit-testing. Even-odd and winding fill rules
create different holes in a path, so it matters a good deal for hit testing.

There seem to be three possibilities as implemented:

   1. HitRegions only ever use winding paths. This seems like a bad idea.
   2. Whichever *fillRule* is defined when *context.addHitRegion* is called
   determines the *fillRule *for that hit region's *Path *permanently. This
   seems acceptable but confusing and should be clarified if it is currently
   the case.
   3. The fillRule of a hit region changes dynamically as
*context.fillRule* changes.
   This would be a nightmare.


I hope that right now it is #2, but the specification makes no mention of
this.

Actually, I'd prefer that either *HitRegionOptions *or the *Path *object
would need to have a *fillRule *attribute.

If the specification does adopt something similar to Cabanier's suggestions
then that would need to be done anyway.

In short, if fill() and *isPointInPath() are* changed to specify *fillRule*
, then *HitRegionOptions* (the argument to *addHitRegion*) or the *Path* given
in the options needs to change too.

Simon Sarris


Re: [whatwg] remove resetClip from the Canvas 2D spec

2013-01-29 Thread Simon Sarris
I opened the bug about this originally back in 2011, asking that it be
added (it was in March 2012 to the WHATWG spec). For the sake of the
discussion that bug is here:

https://www.w3.org/Bugs/Public/show_bug.cgi?id=14499

The reason is that (almost) every single piece of canvas context state can
be reset on its own (ie, to reset the fillStyle: ctx.fillStyle = 'black')

Yet the only part of the context state that cannot be reset on its own is
the clipping region. For this to be reset you must use save and restore,
which affect all state and are to be avoided in performance-minded
applications (games, diagramming libraries, etc).

So from a heavy canvas-user's perspective resetClip() has a definite
utility. On the other hand I can't comment on difficulty of implementation.


On Tue, Jan 29, 2013 at 8:00 PM, Rik Cabanier  wrote:

> All,
>
> we were looking at how resetClip [1] could be implemented in WebKit.
> Looking over the Core Graphics implementation, this feature can't be
> implemented without significant overhead. I also found an email from 2007
> where Maciej states the same concern. [2]
>
> Since no browser has implemented it, can it be removed from the spec?
>
> Rik
>
> 1:
>
> http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-resetclip
> 2: http://permalink.gmane.org/gmane.org.w3c.whatwg.discuss/10582
>


Re: [whatwg] remove resetClip from the Canvas 2D spec

2013-08-09 Thread Simon Sarris
I originally opened this bug/request for performance reasons, since I try
to avoid all use of save() and restore() on the context. As a refresher
resetting any piece of context state by itself is possible except for
clipping regions.

I do see Rik's point about the mess a resetClip() function would make with
save() and restore().

It also seems, if I am reading Rik's reasoning right (and its totally
possible I'm wrong here), that such a problem would not exist if we had a
setClip() function instead.

As an alternative I would propose something like setClip() or setClip(path)
(with the former working on the current path, just like
clip()/fill()/stroke() does).

The reason we need something else is because clip() can only ever get
smaller, since it takes the intersection of the current clipping region and
the current path. setClip() would not take the intersection, instead it
would override the current clipping region with the current path (or given
path if you prefer).

This setClip would still allow de-facto "resets" of the clipping state by
doing:

// most likely the transform would also be reset right before this, if it
was not already identity
// ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.beginPath();
ctx.rect(0, 0, canvas.width, canvas.height);
ctx.setClip();

Would such a set up make it easier to accommodate save and restore? (which
many people using this would not be using anyway). I'd expect it to be
similar to setTransform() in that regard, but maybe not.

More generally, does this seem more reasonable, or feasible?





On Fri, Aug 9, 2013 at 4:20 PM, Ian Hickson  wrote:

> On Fri, 9 Aug 2013, Stephen White wrote:
> >
> > Although Skia could support resetClip() via SkRegion::kReplace_Op, it's
> > problematic for the API in general, and I think we should avoid it.
> >
> > In particular, it makes it impossible to place a display list (SkPicture
> > in Skia parlance) inside a parent display list containing a clip and be
> > assured that the child will not draw outside the given region, since the
> > child display list can always resetClip() its way out of the parent's
> > clip. It probably also prevents culling optimizations for the same
> > reason.
> >
> > For example, if one used Skia to draw the entirety of a browser UI
> > including chrome and content, the resetClip() inside the web page
> > contents would overwrite the browser UI. Obviously we don't do that in
> > Chrome, but it goes some idea of the problem at the API level.
>
> This is a quite widely requested feature. What should we do to address
> this request instead?
>
> --
> Ian Hickson   U+1047E)\._.,--,'``.fL
> http://ln.hixie.ch/   U+263A/,   _.. \   _\  ;`._ ,.
> Things that are impossible just take longer.   `._.-(,_..'--(,_..'`-.;.'
>


Re: [whatwg] remove resetClip from the Canvas 2D spec

2013-08-12 Thread Simon Sarris
I think most performance-minded use cases will be fine with junov's idea
since they will not want to touch the stack in the first place.

Here's a simple use case: Suppose there are nested objects to be drawn,
Panels, TextBlocks, and other visual elements. Panels are containers that
draw a background and all of their children - and they contain any number
of Panels or TextBlocks or other elements, and TextBlocks set the context
font (to their font) and draw some text.

The drawing structure is hierarchical, and drawn elements may be offset
from their immediate parent. So a drawing hierarchy might look like this:

Panel(A)
Panel(B)
TextBlock
TextBlock

That is, Panel(A) contains Panel(B) and a TextBlock. And Panel(B) contains
another TextBlock. In practice, nesting could be much deeper and more
complicated.

Now suppose also that Panels have some settings, such as a maximum width,
that might cause their visual elements to be clipped. So a panel might need
to save(), clip(), draw all of its children, and then restore(). Nesting
means multiple levels of clipping, for instance with the above hierarchy it
might look like:

Panel(A)
clip (save)
drawChildren:
Panel(B)
clip (save)
drawChildren:
TextBlock
sets font X
fillText
restore
TextBlock
sets font X
fillText
restore

This is problematic, because it means:

1. I must use save() and restore(), which are slow in their own right
2. The usage of save() and restore() means that, even if all (or most) of
my fonts are set to the same value, I have to keep setting them over and
over. Setting the font is slow in practice, even if it is set to the same
value as before, and so it should be cached if at all possible. See:

http://jsperf.com/can-attribs
and
http://jsperf.com/cached-attributes

(fill/stroke styles should be cached too, but the performance diff is not
as drastic)

With a nested drawing structure,when using clipping, I am much less able to
cache the canvas font/fillStyle/strokeStyle. This hurts performance. It's
still possible, some of the time, but its harder to realize gains.

All the while this is happening I am translating, rotating, and scaling the
transformation matrix to position nested visual elements, but this is not a
problem since I can undo those either with setTransform or inverse
transforms, so they do not necessitate use of save() and restore().
Clipping has no such ability, to undo any clipping region I must clobber
the context state with a save and restore.

I hope that was clear. This is a real-world use case for a production
canvas diagramming library.

In general, junov's idea will work well for anyone who wants this because
they need to occasionally clip while keeping their webapp performant, since
those people are unlikely to be touching save() and restore() in the first
place.



On Mon, Aug 12, 2013 at 2:15 PM, Rik Cabanier  wrote:

> On Fri, Aug 9, 2013 at 1:40 PM, Justin Novosad  wrote:
>
> > On Fri, Aug 9, 2013 at 4:20 PM, Ian Hickson  wrote:
> >
> > >
> > > This is a quite widely requested feature. What should we do to address
> > > this request instead?
> > >
> > >
> > What if resetClip restored the clip to what it was at the save call that
> > created the current state stack level?
> > In other words, restore the clip, but without popping it off the
> > save/restore stack.
> >
>
> It would be good to hear specific use cases for 'resetClip' so we can make
> that call.
> I think your proposal could be made to work with Core Graphics.
>
>
> > Also, resetMatrix could be defined to do the same.
>
>
> Is that API defined somewhere?
>


Re: [whatwg] remove resetClip from the Canvas 2D spec

2013-08-12 Thread Simon Sarris
> Good point, I think part of the problem has to do with the fact that save
> is non-selective (saves all of the state).

Yes, since save() and restore() save and restore everything, it creates the
side effect of needing to set ctx.font/fillStyle/strokeStyle more often
than otherwise, which are slow to set, probably because of some CSS parser
activity, but I'm not wise enough to know.

If there was merely a way to save and restore the context, or perhaps some
other subset of state, that would probably work nicely too.


Re: [whatwg] remove resetClip from the Canvas 2D spec

2013-08-13 Thread Simon Sarris
> If this is strictly a performance issue, then we definitely should fix
that before adding new API, IMHO. It would be great to get some reduced
test cases where save()/restore() is a bottleneck.

I'd argue its not strictly a performance issue. More generally its awkward
that you can reset any piece of the canvas context state except the
clipping region. The clipping region alone requires you to clobber all
state in order to reset it.

You can set the fillStyle back to black, you can set the transformation
matrix back to identity, etc. But you can't set the clipping region back to
the entire canvas area without save()/restore() or can.width=can.width.
It's the only thing like that.

That ought to be considered bad on principle I think. It makes clipping a
really odd operation compared to anything else, and at the least it makes
the one part of the API unintuitive.


[whatwg] Canvas - Should setLineDash be a nullable sequence?

2013-08-20 Thread Simon Sarris
This is minor, but it did recently break formerly-working functionality in
Google Chrome, so maybe its worth a discussion.

The specification reads:

  void setLineDash(sequence segments); // default empty
  sequence getLineDash();

This means we *cannot* use:

ctx.setLineDash(null);

In Chrome 28 and previous (for at least 6 months) null was an allowed
value, but in Chrome 30 (at least) it switched to throwing a TypeError.
This is technically more correct, though lots of existing JavaScript code
will now stop working because of their change. This doubtless kills some
current JS libraries that were using null, such as mine, and now my
customers must upgrade or get errors when their clients use Chrome.

It's possible that Chrome did not mean to break functionality here, but to
their credit they are now following the spec to the letter. I wrote to them
about that here:

https://code.google.com/p/chromium/issues/detail?id=266948

But the Chrome team seems "shy" about using their bugtracker, so I don't
know if we'll get a response as to whether or not it was an intentional
change.

In any case, I think it would be better if setLineDash was defined in the
spec as a nullable sequence, so that when setting it back to nothing to
return to normal not-dashed path stroking (which could happen thousands of
times in a an animation frame) less stuff gets allocated in the draw loop.
JS authors can use a static empty array to avoid extra memory allocation,
but a brief look at the webkit code suggests (and I may be totally
incorrect here) that the array gets copied anyway, and that's no fun:

https://github.com/WebKit/webkit/blob/master/Source/WebCore/html/canvas/CanvasRenderingContext2D.cpp#L515

I think part of the issue is that setLineDash does two things. It sets the
dashing properties, but it also acts as the only way to enable/disable
dashing, I think that null seems appropriate.

For the record, AFAIK no other browser has implemented setLineDash yet,
though ctx.mozDash defaults to null and does accept null.

Simon Sarris


Re: [whatwg] Adding 2D Canvas features

2013-09-05 Thread Simon Sarris
Just FYI, Chrome now has a relatively new bug for "Canvas v5 API additions"
that references all the missing features that some posters were concerned
about.

The default take from this bug report is that all of these are greenlighted
to be worked on (no objections so far)

https://code.google.com/p/chromium/issues/detail?id=281529

This bug is now (as of a few days ago) referenced at chromestatus.com under
the Canvas feature's implementation status:

http://www.chromestatus.com/features/5100084685438976

Simon


[whatwg] Canvas - Somewhat inconsistent rules in the spec for drawImage

2013-09-20 Thread Simon Sarris
Summary: You can draw zero-sized Images and objects with zero-sized source
rects to the canvas context. You cannot draw zero-sized Canvases, you get
an InvalidStateError. According to the spec this is right, but I think
there should be more consistency in handling these cases.

drawImage can be called with an Image, a Canvas, or a Video element, so you
typically see calls like:

ctx.drawImage(imageReference, 0, 0);
ctx.drawImage(canvasReference, 0, 0);

99% of the time you can think of them as interchangeable. Let's talk about
our 1%ers.

I came across an interesting (and none-too-useful, but we can blame the
browser for that) error when drawing a canvas with zero width and height:

inMemoryCanvas.width = 0;
// Uncaught InvalidStateError: An attempt was made to use an object that is
not, or is no longer, usable.
ctx.drawImage(inMemoryCanvas, 0, 0);

According to the specification, this is the right error to throw:

> If the image argument is an HTMLCanvasElement object with either a
horizontal dimension or a vertical dimension equal to zero, then throw an
InvalidStateError exception, return aborted, and abort these steps.

The bothersome thing is that:

1. Zero-sized Images do not have any such error
2. calling drawImage with a source rectangle, using
context.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh), when the source
rectangle has zero width or height, does not give an error. Per the spec:

> If one of the sw or sh arguments is zero, abort these steps. Nothing is
painted.

So zero-sized sources are fine if they are an HTMLImageElement or a source
rectangle, but not fine if they are HTMLCanvasElement.

This seems inconsistent, and the error given seems odd too. It's not very
descriptive and not immediately clear what is wrong. This issue came to my
attention because a friend asked for help, who was drawing randomly-sized
canvases onto a main context. He suspected InvalidStateError was some
bizarre closure bug where part of the canvas state was being lost, but of
course the truth was more mundane - some but not all zero-sized objects
cannot be drawn to the canvas context.

Anywho, I recommend either considering all zero-sized sources as invalid,
and perhaps giving a more descriptive error, or simply allowing a
zero-sized canvas to be a valid drawImage argument so that it may run its
natural course (draw nothing), just like Image and zero-sized source rect
arguments.

Simon Sarris


~~~

For reference, below is a simple example showing both a zero-sized img
being drawn and a zero-sized canvas:

  // this 's width/height/naturalWidth/naturalHeight are all zero:
  var img = document.createElement('img');
  // Totally fine, no error:
  ctx.drawImage(img, 0, 0);

  var inMemoryCanvas = document.createElement('canvas');
  inMemoryCanvas.width = 0;
  inMemoryCanvas.height = 0;
  // Uncaught InvalidStateError: An attempt was made to use an object that
is not, or is no longer, usable.
  ctx.drawImage(inMemoryCanvas, 0, 0);


Re: [whatwg] Bicubic filtering on context.drawImage

2014-03-14 Thread Simon Sarris
On Fri, Mar 14, 2014 at 2:40 PM, Justin Novosad  wrote:
>
>
> Yes, and if we fixed it to make it prettier, people would complain about a
> performance regression. It is impossible to make everyone happy right now.
> Would be nice to have some kind of speed versus quality hint.


As a canvas/web author (not vendor) I agree with Justin. Quality is very
important for some canvas apps (image viewers/editors), performance is very
important for others (games).

Canvas fills a lot of roles, and leaving a decision like that up to
browsers where they are forced to pick one or the other in a utility
dichotomy. I don't think it's a good thing to leave "debatable" choices up
to browser vendors. It ought to be something solved at the spec level.

Either that or end users/programmers need to get really lucky and hope all
the browsers pick a similar method, because the alternative is a
(admittedly soft) version of "This site/webapp best viewed in Netscape
Navigator".

Simon Sarris


Re: [whatwg] Proposal: change 2D canvas currentTransform to getter method

2014-03-20 Thread Simon Sarris
On Thu, Mar 20, 2014 at 1:52 PM, Justin Novosad  wrote:

> Hello all,
>
> The recently added currentTransform attribute on CanvasRenderingContext2D
> gives shared access to the rendering context's transform. By "shared", I
> mean:
>
> a) this code modifies the CTM:
> var matrix = context.currentTransform;
> matrix.a = 2;
>
> b) In this code, the second line modifies matrix:
> var matrix = context.currentTransform;
> context.scale(2, 2);
>
> This behavior is probably not what most developers would expect.
> I would like to propose changing this to a getter method instead.  We
> already have a setter method (setTransform).
>
> In another thread entitled "Canvas Path.addPath SVGMatrix not optimal",
> Dirk Schulze proposed using the name getCTM, which would be consistent with
> the SVGLocatable interface, where getCTM returns an SVGMatrix. On the other
> hand, we could call it getTransform to be consistent with the existing
> setTransform on CRC2D. Opinions? Perhaps we should also have an overload of
> setTransform (or setCTM) that would take an SVGMatrix.
>
> First of all, have any browsers shipped currentTransform yet?
>
> Thoughts?
>
> -Justin Novosad


FF (at least Aurora/Nightlies) has for some time had mozCurrentTransform
(and mozCurrentTransformInverse), which return an Array (so not
spec-compliant, since spec wants SVGMatrix). It is not shared, so it does
not do what your a) and b) examples do.

I agree that changing it to a getter method would be better, it would be
more intuitive and clear for developers.


Re: [whatwg] Proposal: change 2D canvas currentTransform to getter method

2014-03-24 Thread Simon Sarris
On Mon, Mar 24, 2014 at 11:26 AM, Hwang, Dongseong <
dongseong.hw...@intel.com> wrote:

> Looking over this thread, we make a consensus not to
> expose currentTransform attribute.
>
> Now, all we have to decide is API
>
> Option 1,
> SVGMatrix getTransform();
> void setTransform(SVGMatrix);  <-- it overrides void
> setTransform(unrestricted double a, unrestricted double b, unrestricted
> double c, unrestricted double d, unrestricted double e, unrestricted double
> f);
>
> Option 2,
> SVGMatrix getCTM();
> void setCTM(SVGMatrix);
>
> Option 3,
> SVGMatrix getCurrentTransform();
> void setCurrentTransform(SVGMatrix);
>
> Which is the best?
>
> Greetings, DS
>
>
I'm heavily in favor of option 1.

I think using "Current" in the naming convention is silly. The transform
just as much a part of state as lineWidth/etc, but nobody would propose
naming lineWidth something like currentLineWidth! There's no way to get a
*non-current* transformation matrix (or lineWidth), so I think the
distinction is unnecessary.

CTM only seems like a good idea if we're worried that the name is too long,
but since "Current" is redundant/extraneous, I don't think an initialism is
worth the added layer of confusion.

Simon


Re: [whatwg] Books on canvas

2014-09-23 Thread Simon Sarris
Sorry, but the WHATWG mailing list is a list for feedback of WHATWG
specifications.

This is not the place for book or learning discussions.

Simon

On Tue, Sep 23, 2014 at 4:03 PM, L2L 2L  wrote:

> I'm reading this one book, but it's heavy in math equation. And other book
> seem to have framework--which I'm reducing to avoid using-- and suggestion?
> I don't mind using a custom class object,  as long as I can learn it inside
> and out.
>
> E-S4L
> N-S4L
> J-S4L