[whatwg] Hit region + fillRule concerns
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
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
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
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
> 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
> 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?
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
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
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
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
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
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
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