How to paint gradient color following a stroke path?

1,060 views
Skip to first unread message

Jiulong Wang

unread,
Oct 12, 2020, 2:26:52 AM10/12/20
to skia-discuss
Browsed through all path effects and shaders, couldn't find an easy way to do it.

Say I have a path, I'd like to stroke it with a color that is a function of path length.  In OpenGL I used to put length data into vertex and use that information to manipulate color in fragment shader.  But it seems in Skia I can't easily modify the vertex data.

Wondering if there's any smart way to archive this effect using Skia public APIs...

Thanks!

Mike Reed

unread,
Oct 12, 2020, 9:50:23 AM10/12/20
to skia-d...@googlegroups.com
For a straight line, you can align a linear gradient along the line to get this effect.

For any other stroke shape (e.g. curves, multiple line segments, etc.) skia doesn't have a built-in way to have a gradient "follow" the stroke/arc-length.

SkRuntimeEffect is a new, general purpose, way to write a custom SkShader, where you can write any code you want. It doesn't have access to the stroke/arc-length data for the path being drawn, but you might look at it, in case you see some other way to get the effect you want.


--
You received this message because you are subscribed to the Google Groups "skia-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to skia-discuss...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/skia-discuss/cd914176-a086-408e-bd3a-765b4e106918n%40googlegroups.com.

Brian Osman

unread,
Oct 12, 2020, 9:56:37 AM10/12/20
to skia-d...@googlegroups.com
We also have some (non-public) code for triangulating a path (in src/gpu/GrTriangulator). We've talked a few times about finding a way to expose that functionality, but it's currently tied pretty closely to the parts of Skia that use it. If you *do* have vertex data for your path (and some way to know stroke distance of each vertex), you can draw it with SkCanvas::drawVertices, which allows setting a color per-vertex.

Jiulong Wang

unread,
Oct 15, 2020, 2:15:50 AM10/15/20
to skia-d...@googlegroups.com
Thanks Mike and Brian!

We do use Skia's path triangulator (src/gpu/GrTriangulator) to speed up path rendering in Android especially when canvas scale is large, which has significant performance improvement over default path rendering.  But after SkPaint::getFilledPath and GrTriangulator::PathToVertices, it is not easy to map vertices back to original stroke to get length information for color assignment.

I guess for this particular effect I'm better off generating vertex data with color directly from the stroke path...



You received this message because you are subscribed to a topic in the Google Groups "skia-discuss" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/skia-discuss/gQvvYusrqTY/unsubscribe.
To unsubscribe from this group and all its topics, send an email to skia-discuss...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/skia-discuss/CAMcBjo70XTy1_oy%3DxibkQEJkn-zNCN4zYLDxgV3UY6Xg6Pwebw%40mail.gmail.com.

Christopher Dalton

unread,
Oct 16, 2020, 11:28:15 AM10/16/20
to skia-d...@googlegroups.com
Hi again Jiulong!

I think the brand new, not-yet-enabled GrStrokeTessellateShader can do what you want.

Screen Shot 2020-10-16 at 9.08.14 AM.png

It works by tessellating a bunch of orthogonal edges all along the path, with higher concentrations around higher curvature. If you aren't afraid to get your hands dirty inside Skia, you could change your code locally to emit gradient y-coordinates from 0..1 or -1..1 down the orthogonal edge, and then compute your length along the curve (or just use the T value?) for your gradient x-coordinate. You will need to think through some ambiguity for areas where the curvature is so tight that the inner side of the curve overlaps itself, but otherwise I think this is promising. We have certainly talked about adding first class support for "stroke coordinates" right inside of Skia, but it's still pretty far out on the road map.

Also: this code currently relies on hardware tessellation and MSAA. AFAIK, many if not most android devices only spoof hardware tessellation by implementing it on CPU, so performance might not be very good. I'm actively developing a mode that does this same thing with indirect draws instead of tessellation. There are also plans to use this same concept for analytic coverage instead of MSAA, but those are far out enough that I wouldn't count on them yet. (GL_EXT_multisampled_render_to_texture is well supported, used by Skia, and extremely fast on Android!)

Christopher Dalton

unread,
Oct 16, 2020, 2:25:36 PM10/16/20
to skia-d...@googlegroups.com
Here is a crude demo for how stroke coordinates might work with the tessellator: https://skia-review.googlesource.com/c/skia/+/327877

Screen Shot 2020-10-16 at 12.21.41 PM.png

Self intersection is a problem, but If interested we can talk more about how you can get a robust solution locally, and about Skia work that can be done to move in this direction.

Jiulong Wang

unread,
Oct 18, 2020, 11:44:15 PM10/18/20
to skia-d...@googlegroups.com
Hi Chris,

Thanks so much for putting this together!  This is the exact effect I'm looking for.  But looking at the code, seems like it's buried deep in the GPU path rendering pipeline.  I don't see an easy way to expose the interface for application code to utilize this effect...  Also, will that require additional work for this effect to work in other backends such as PDF?

The GrStrokePatchBuilder however looks very promising... I think it should be easier to change the output from chunks of GrBuffer to SkVertices, and then add necessary data for custom SKSL based shaders.  The result from the wire frame in your post looks much better than my homemade naive tessellator. :)

I think adding some details of our scenario might be helpful, esp. if Skia is considering enhancements to path API such as "stroke coordinate" which sounds very promising for our scenario.

For this particular effect, we want to add an "ephemeral stroke" feature where users can draw strokes with a stylus but the stroke will disappear after a short period of time.  We want a smooth disappear effect so fading out along the stroke would be ideal.  Before moving to Skia we put the length information on vertex z value, extract it in vertex shader and use that to modify alpha value in the fragment shader.  In Skia we first implemented a similar effect using Skia's particle system, but it has issues like not guaranteed to be a continuous stroke...

Another feature we would like to have in the path or stroke API is the ability to dynamically change stroke width, based on things like pressure, speed, or even angle of the stylus, to provide a higher fidelity drawing experience than uniformly stroked path.  I haven't started working on it but SkStrokeRec related code seems to be well encapsulated and customizable for that purpose.





paul....@gmail.com

unread,
Oct 19, 2020, 8:36:46 AM10/19/20
to skia-discuss
I see this sort of request often in the SVG topic of Stack Overflow: People asking to be able to apply a gradient along and across a stroke.  A "pipe" effect is a common example.

If somehow the concept of "stroke coordinate space" could be achieved, allowing shaders (including those with a localMatrix) to be applied to the stroke, that would enable some incredible effects.

Paul



Christopher Dalton

unread,
Oct 19, 2020, 1:00:18 PM10/19/20
to skia-d...@googlegroups.com
>> Also, will that require additional work for this effect to work in other backends such as PDF?

I don't believe that PDF (or any other backend) has the ability to express a gradient following a stroke, short of breaking the path up yourself into short segments ahead of time.

>> The GrStrokePatchBuilder however looks very promising...

I suppose you could make that work. Do you care about being able to zoom? If not then with a lot of effort you could decrease the number of "tessellation segments" to 1 and have GrStrokePatchBuilder effectively chop into "lineTos". It would be quite slow though, and I foresee this requiring a lot of engineering effort. For example, handling joins and cusps would be a challenge. Unless you absolutely need PDF support, I think your best bet would be to run with that CL in the short term. Long term I do think Skia wants to support "stroke coordinates".

>> Another feature we would like to have in the path or stroke API is the ability to dynamically change stroke width

This is another feature that would be easy to implement with tessellation. At each point we simply outset the orthogonal edge based on a stroke with function.

Devin Kerr

unread,
Jun 2, 2022, 10:26:00 PM6/2/22
to skia-discuss
Has there been any new work toward gradients along stroke paths since this original discussion? This would be very useful.
Reply all
Reply to author
Forward
0 new messages