Skip to content

Commit 5c42bb6

Browse files
committed
[css-paint-api] first rough version of paint level 1.
1 parent 7f754e9 commit 5c42bb6

File tree

1 file changed

+212
-0
lines changed

1 file changed

+212
-0
lines changed

css-paint-api/Overview.bs

+212
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,215 @@ Editor: Shane Stephens, shanestephens@google.com
99
Editor: Ian Kilpatrick, ikilpatrick@chromium.org
1010
Editor: Dean Jackson, dino@apple.com
1111
</pre>
12+
13+
Introduction {#intro}
14+
=====================
15+
16+
The paint stage of CSS is responsible for painting the background, content and highlight of an element based on that element's geometry (as generated by the layout stage) and computed style.
17+
18+
This specification describes an API which allows developers to paint a part of an element in response to geometry / computed style changes.
19+
20+
Issue: Currently this spec doesn't provide any mechanism for setting clip, opacity, filter, etc. on either the context, background of an element. This would be nice to have.
21+
22+
Paint Invalidation {#paint-invalidation}
23+
==================================
24+
25+
At any point in time the output of a paint callback for an element may be either <dfn>paint-valid</dfn> or <dfn>paint-invalid</dfn>.
26+
27+
Issue: Describe output in a nicer way. This could be pixels, or a display list type object.
28+
29+
If the output is <a>paint-invalid</a> and the output should not be used for rendering, and can be safely disposed.
30+
31+
Invoking the paint callback will cause the output to become <a>paint-valid</a>.
32+
33+
The output of a paint callback will become <a>paint-invalid</a> when:
34+
* The geometry of an element/fragment (as determined by layout) changes.
35+
* The computed style of a property that the paint callback has registered a dependency on has changed.
36+
37+
Output which is <a>paint-valid</a> does not provide a guarantee that the paint callback is not invoked. Browsers may choose to invoke all callbacks every frame for simplicity.
38+
Thus to ensure cross-browser compatibility paint callbacks should be idempotent.
39+
40+
Output which is <a>paint-invalid</a> are not necessarily invoked on the current frame.
41+
For example if the element is outside the current viewport, the browser may choose to only invoke the callback when it appears in the viewport.
42+
43+
Issue: This describes everything in terms of element which is incorrect. Callbacks should be at the fragment level.
44+
45+
Issue: Describe what happens if a callback doesn't hit a frame timing boundary. I.e. just renders a transparent image instead?
46+
47+
Issue: Should paint callbacks support partial area invalidation?
48+
For example if the paint callback just invalidates 1/4 of the actual element, should we provide an API to just update that area?
49+
This may be expensive for memory/computation for display list based implementations.
50+
51+
Registering Custom Paint {#registering-custom-paint}
52+
====================================================
53+
54+
<pre class='idl'>
55+
interface PaintGlobalScope {
56+
// TODO once we have a spec for separate script execution contexts, make this inherit.
57+
// A PaintGlobalScope is inside of a 'worker', so not in the main script execution context.
58+
// A PaintGlobalScope and by extension custom paint callbacks don't have access to things like the DOM, XHRs, etc.
59+
};
60+
61+
interface CanvasRenderingContext2d {
62+
// TODO link to actual CanvasRenderingContext2d.
63+
};
64+
65+
interface Geometry {
66+
readonly attribute double width;
67+
readonly attribute double height;
68+
};
69+
70+
interface InputProperties {
71+
// Similar to ComputedStyle, but only includes properties listed in 'dependencies'.
72+
// Eventually provide typed access to properties instead of string based access.
73+
};
74+
75+
callback PaintCallback = void (CanvasRenderingContext2d context, Geometry geometry, InputProperties inputProperties);
76+
77+
dictionary VisualOverflowRectDict {
78+
float? top;
79+
float? bottom;
80+
float? left;
81+
float? right;
82+
};
83+
84+
dictionary PaintDescriptor {
85+
DOMString name;
86+
PaintCallback paintCallback;
87+
sequence&lt;DOMString&gt; dependencies;
88+
DOMString contextType;
89+
VisualOverflowRectDict visualOverflowRect;
90+
};
91+
92+
partial interface PaintGlobalScope {
93+
long registerPaint(PaintDescriptor paint);
94+
void unregisterPaint(long hookID);
95+
};
96+
</pre>
97+
98+
: <dfn dict-member for=PaintDescriptor>name</dfn>
99+
:: The name of the custom paint being defined. Any valid <<ident>> can be a custom paint name.
100+
101+
: <dfn dict-member for=PaintDescriptor>paintCallback</dfn>
102+
:: The custom paint callback.
103+
104+
Issue: This should probably be a class instead.
105+
106+
: <dfn dict-member for=PaintDescriptor>dependencies</dfn>
107+
:: The list of CSS properties (custom or native) which are provided to the custom paint callback. In addition when any of these properties changes, an element will become <a>paint-invalid</a>.
108+
109+
: <dfn dict-member for=PaintDescriptor>contextType</dfn>
110+
:: The type of context to provide for paint. Providing the "2d" will result in a <a href=''>CanvasRenderingContext2d</a>.
111+
112+
Issue: We probably won't support any others initially. Remove this?
113+
114+
: <dfn dict-member for=PaintDescriptor>visualOverflowRect</dfn>
115+
:: The amount of additional canvas to provide for drawing for the fragment.
116+
This additional canvas can be used for drawing outside the box determined by layout for achieving overflow effects like box-shadow.
117+
118+
Issue: This is not how CSS works. We probably need an additional callback to provide the visual overflow rect based on the InputProperties.
119+
This is another argument for making the API class based.
120+
121+
: <dfn method for=PaintGlobalScope>registerPaint(<var>paint</var>)</dfn>
122+
:: Registers a paint callback.
123+
124+
: <dfn method for=PaintGlobalScope>unregisterPaint(<var>hookID</var>)</dfn>
125+
:: Unregisters a paint callback based on the ID given by <a>registerPaint()</a>.
126+
127+
: <dfn argument for=PaintCallback>context</dfn>
128+
:: The paint callback is passed a new <a href=''>CanvasRenderingContext2d</a> each time it is invoked. Implicitly this means that there is no stored data, or state on the rendering context.
129+
For example you can't setup a clip on the context, and expect the same clip to be applied next time the function is called.
130+
131+
Several methods will be modified from the current CanvasRenderingContext2d. For example:
132+
* The <a href=''>canvas</a> accessor will throw a <a href=''>InvalidAccessError</a> as the context doesn't have a parent <a href=''>HTMLCanvasElement</a>.
133+
* <a href=''>getImageData()</a> and similar pixel data accessors will be no-ops.
134+
* <a href=''>addHitRegion()</a>, <a href=''>removeHitRegion()</a>, <a href=''>clearHitRegions()</a> will be no-ops.
135+
* <a href=''>drawFocusIfNeeded()</a> will be a no-op.
136+
137+
Note: The litmus test used for deciding above was: methods should become no-ops if they can, otherwise throw an error if they'd require an IDL change.
138+
139+
Issue: We should probably create a new interface for CanvasRenderingContext2d and use IDL mixins (like CanvasPathMethods) to share common interfaces.
140+
141+
: <dfn argument for=PaintCallback>geometry</dfn>
142+
:: The geometry information for the callback.
143+
144+
Issue: Decide what is needed here (in level 1). Obviously we need the width/height of the fragment.
145+
If we are going down the fragment route do we need continuation data? I.e. what edge is shared etc.
146+
Do we need children position data?
147+
Should make sure we don't rabbit hole too much on this, provide what we think is useful for v1, iterate in v2.
148+
149+
: <dfn argument for=PaintCallback>inputProperties</dfn>
150+
:: Similar to <a href=''>ComputedStyle</a>, but only includes properties listed in dependencies.
151+
152+
Issue: No way for image data to appear here yet. Everything string based.
153+
For example if you load the image data via. url('http://example.com/image.png') how does this appear in the paint callback?
154+
I think the answer to this is to provide this information in the Typed CSS Value OM.
155+
Will need access to a loaded bit, width, height, and a way to render into rendering context.
156+
157+
Issue: Should VisualOverflowRectDict just be based on longs instead? Do floats make sense?
158+
159+
Using Custom Paint {#using-custom-paint}
160+
========================================
161+
162+
The <<image-or-paint>> type extends the <<image>> type to allow the <<paint()>> function.
163+
164+
<pre class="prod"><dfn>&lt;image-or-paint&gt;</dfn> = <<image>> | <<paint()>></pre>
165+
166+
Issue: Should this just replace the <<image>> type instead? I don't think so as it'll mean that we can custom paint the cursor. Which I don't think we want.
167+
168+
The <<paint()>> function will lookup to see if there has been a paint callback registered with the same name.
169+
If it has the output of the paint callback should appear in the appropriate place.
170+
If there is no paint callback registered, the function output should be replaced with a transparent image, as if nothing was there.
171+
172+
Issue: Word-smith this better.
173+
174+
<pre class='prod'>
175+
<dfn>paint()</dfn> = paint( <<ident>> )
176+
</pre>
177+
178+
Issue: Change 'background-image', 'border-image' and 'content' to accept <<image-or-paint>> or <<paint()>>.
179+
180+
Examples {#examples}
181+
====================
182+
183+
Example 1: A colored circle. {#example-1}
184+
----------------------------------------
185+
186+
<pre class='lang-javascript'>
187+
// Inside PaintGlobalScope.
188+
this.registerPaint({
189+
name: 'circle',
190+
dependencies: ['--circle-color'],
191+
contextType: '2d',
192+
paintCallback: function(ctx, geom, properties) {
193+
// Change the fill color.
194+
var color = properties['--circle-color'];
195+
ctx.fillStyle = color;
196+
197+
// Determine the center point and radius.
198+
var x = geom.width / 2;
199+
var y = geom.height / 2;
200+
var radius = Math.min(x, y);
201+
202+
// Draw the circle \o/
203+
ctx.beginPath();
204+
ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
205+
ctx.fill();
206+
}
207+
});
208+
</pre>
209+
210+
<pre class='lang-markup'>
211+
&lt;div id="myElement"&gt;
212+
CSS is awesome.
213+
&lt;/div&gt;
214+
215+
&lt;style&gt;
216+
#myElement {
217+
--circle-color: red;
218+
background-image: paint(circle);
219+
}
220+
&lt;/style&gt;
221+
</pre>
222+
223+
Issue: How to do conic-gradient as a use-case?

0 commit comments

Comments
 (0)