JDK-8092128 : [Canvas] Implement paint/damage drawing model for FX Canvas
  • Type: Enhancement
  • Component: javafx
  • Sub-Component: graphics
  • Affected Version: 8u20
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2014-03-26
  • Updated: 2018-09-05
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbdUnresolved
Related Reports
Relates :  
Relates :  
Description
Canvas is a control that allows direct drawing and works best for content that is drawn statically.  However, when content is drawn infrequently, application code is forced to redraw the entire canvas or implement code to understand what has changed and draw less.  Further, it is sometimes unclear when application code should draw to a canvas.  It can draw at any time and this is somewhat unstructured.

The paint/damage model is well understood in immediate mode drawing toolkits and might be a useful addition to FX.  In addition to solving the "draw less" problem, it might allow clients with immediate mode drawing code from other toolkits to port to FX canvas more easily.

The paint/damage model involved a damaged region that accumulates areas that need to be redrawn (typically rectangular areas).  When there is no input activity in the UI (such as mouse and keyboard), a paint request is issued and the damage is cleared.  Areas that need to be drawn are added to the damage by both the system and application code.  When asked to draw, application code is provided with a graphics context that is clipped to the damaged area.

Not all of the paint/damage concepts will necessarily apply to FX Canvas.  However, the "draw less" and "when should I draw" problem for application code needs to be addressed.
Comments
[In a comment that was moved/deleted, persistence was raised as an issue, so the following comment pertains to that] Persistence seems to be an implementation detail to me. This JIRA is exploring a paint/damage drawing model where this model is intended to help the application programmer manage drawing to a canvas. Canvases are supposed to be persistent regardless of drawing model. For the sake of discussion, let's assume a standard painting model where a paint event is issued. There is nothing to stop a programmer from getting a graphics contents outside of the paint event, drawing something, then adding a paint handler and damaging certain areas. If the programmer does not redraw the areas that are asked for in the paint event and the what is drawn is inconsistent, then this is the problem of the programmer. If it is too hard to implement this sort of mixed mode drawing, then we could look at ways of restricting drawing in canvases to paint and non-paint models. One idea would be to simply throw away add drawing that is done outside of a paint event if the canvas is using a paint model. Going back to the persistence issue and considering our example, whatever was drawn outside the paint event or inside the paint event should simply be persisted.
02-04-2014

Another thing to consider is the initialization of the GraphicsContext. Currently the GCtx starts out initialized to default values when the Canvas is created and maintains all changes even across calls to cv.getGCtx(). This matches the persistent state of the GCtx used in HTML5. Many paint models construct a brand new GCtx for every paint call (this is somewhat controllable in Windows where you can set attributes to control whether HDC's keep state between uses). The GCtx model we adopted from HTML5 has save/restore that can be used to isolate state changes for some drawing operations, but it does not save/restore all state (notably the path is left intact across a save/restore boundary). Also, from the NeWS days of using the PostScript model for window repair, save/restore mismatches ended up causing problems. I seem to recall that one of the few items of input that James Gosling gave us when we created Java2D was not to use save/restore. ;) Note that this issue also arises when discussing persistent vs. non-persistent canvases in RT-36475 and how it will manage notifications for damage repair. My proposal would be that cv.getGCtx still returns a GCtx in the "last used" state, but that we add a "reset()" method to GCtx that resets it to all default values, erases the save/restore stack and clears the path and that can be used to start paint requests on an "even playing field".
02-04-2014

[Comment about how this issue interacts with a mechanism for activating or enabling a non-persistence mode in FX Canvas was moved to RT-36475...]
02-04-2014

[Comment about how this issue interacts with a mechanism for activating or enabling a non-persistence mode in FX Canvas was moved to RT-36475...]
02-04-2014

What I am describing is the classic paint model. I think the part that might not be clear is "where do the paint requests come from?" The first paint request might come from the system when the Canvas is first displayed. Subsequent paint requests can come from either the system or from a "repaint" method on Canvas. The "repaint" method can take a rectangle that is added to any damage that the system might already have or any other calls to "repaint" that the application may have already made. This was talked about in the description section of this JIRA: > Areas that need to be drawn are added to the damage by both the system and application code.
31-03-2014

BTW, I am not opposed to having retained-mode Canvas and paint-mode Canvas (and making it illegal to draw in both). I'm just exploring the issues. The fact that we will have API that can be mixed means that we are going to have to define what we do in this case anyway. If it can be something consistent that draws, then that would generally be preferred.
31-03-2014

@Jim, not true: >The entire reason for proposing this feature came from discussions about the potential resource usage pitfalls of making canvases >persistent and how to alleviate that cost in the future. Please read the description section of this JIRA. HTML5 canvases are persistent so the fact that ours have problems are simply bugs. This JIRA is about supporting paint/damage for application code.
31-03-2014

@Kevin This is not necessarily the case at all. It is conceptually quite possible to draw directly and also in response to paint. Why should both not be persisted until the application indicates that an area is damaged and need to be redrawn? Win32 allows applications to draw both inside and outside of WM_PAINT. Mac and other systems that do double buffering also support paint callbacks. The classic case: When a smaller area of a control is damaged, the pixels that were on the screen before hand remain and a paint event is issued clipped to the new damage. The application draws only within the damage and everything is consistent. By definition, the part that is not damaged is retained (not the individual draw commands, but the "backing store").
31-03-2014

Another attribute to consider is erasure of the damaged rectangle. Since we have no current concept of Canvas background color we can erase it to transparent, or leave it in an unknown state and rely on the developer to clear it themselves at the start of each callback. I have no opinion one way or the other about this. Note that growing a canvas would have the default effect of adding transparent pixels on the right and bottom sides, but that is more of a result of the fact that uninitialized textures being cleared to transparency by our allocation system than by an explicit clear in the Canvas resizing code.
31-03-2014

The entire reason for proposing this feature came from discussions about the potential resource usage pitfalls of making canvases persistent and how to alleviate that cost in the future. There is no way to implement non-persistence without a callback mechanism and the typical paint/damage callbacks of traditional window systems were the most familiar model to mimic (not necessarily the details of whether it is an overridable method or a callback interface or an event handler, most of which can be found in various forms in the implementations of various window systems).
31-03-2014

I don't quite understand what you are getting at. Fundamentally, a repaint / damage model is telling an application that the Canvas -- at least the part that was damaged -- is not persistent. So persistence is not an implementation detail or an independent choice, but a fundamental property of the drawing mode. The two drawing models being discussed are: 1) Application draws when it has new content This is today's rendering model and is persistent by design and specification. 2) Application draws in response to repaint request / damage This is the newly proposed model and by its very nature means that Canvas is not persistent. Otherwise there is no meaning (except maybe for the "application just resized the canvas to be bigger" case) in telling the user that all or part of the canvas is damaged. What would damage even mean if the Canvas were persistent? An application renders into a Canvas using one of the above two modes. Ideally, not both during the life of the Canvas (meaning it is an immutable property of the Canvas), but certainly not both at the same time in any case.
31-03-2014