You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
99 lines
4.6 KiB
99 lines
4.6 KiB
---
|
|
title: 'SkSL & Runtime Effects'
|
|
linkTitle: 'SkSL'
|
|
---
|
|
|
|
## <span id="overview">Overview</span>
|
|
|
|
**SkSL** is Skia's
|
|
[shading language](https://en.wikipedia.org/wiki/Shading_language).
|
|
**`SkRuntimeEffect`** is a Skia C++ object that can be used to create `SkShader`
|
|
and `SkColorFilter` objects with behavior controlled by SkSL code.
|
|
|
|
You can experiment with SkSL at https://shaders.skia.org/. The syntax is very
|
|
similar to GLSL. When using SkSL effects in your Skia application, there are
|
|
important differences (from GLSL) to remember. Most of these differences are
|
|
because of one basic fact: **With GPU shading languages, you are programming a
|
|
stage of the
|
|
[GPU pipeline](https://www.khronos.org/opengl/wiki/Rendering_Pipeline_Overview).
|
|
With SkSL, you are programming a stage of the Skia pipeline.**
|
|
|
|
---
|
|
|
|
## <span id="children">Sampling other SkShaders</span>
|
|
|
|
In GLSL, a fragment shader can sample a texture. With runtime effects, the
|
|
object that you bind (in C++) and sample (in SkSL) is an `SkShader`. Skia has
|
|
simple methods for creating an `SkShader` from an `SkImage`, so it's easy to use
|
|
images in your runtime effects:
|
|
|
|
<fiddle-embed name='194aa388494b7cdfa57a01968b5cf1ee'></fiddle-embed>
|
|
|
|
Because the object you bind and sample is an `SkShader`, you can directly use
|
|
any Skia shader, without necessarily turning it into an image (texture) first.
|
|
For example, you can sample a linear gradient:
|
|
|
|
<fiddle-embed name='381b785f1ca50a0335be1bfe74c2f421'></fiddle-embed>
|
|
|
|
You can even sample another runtime effect:
|
|
|
|
<fiddle-embed name='13b446d926326481b340842f05014a9c'></fiddle-embed>
|
|
|
|
---
|
|
|
|
## <span id="premul">Premultiplied Alpha</span>
|
|
|
|
When dealing with transparent colors, there are two (common)
|
|
[possible representations](https://en.wikipedia.org/wiki/Alpha_compositing#Straight_versus_premultiplied).
|
|
Skia calls these _unpremultiplied_ (what Wikipedia calls _straight_), and
|
|
_premultiplied_. In the Skia pipeline, every `SkShader` returns premultiplied
|
|
colors.
|
|
|
|
If you're familiar with OpenGL blending, you can think of it in terms of the
|
|
blend equation. For common alpha blending (called
|
|
[source-over](https://developer.android.com/reference/android/graphics/PorterDuff.Mode#SRC_OVER)),
|
|
you would normally configure your blend function as
|
|
`(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)`. Skia defines source-over blending as
|
|
if the blend function were `(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)`.
|
|
|
|
Skia's use of premultiplied alpha implies:
|
|
|
|
- If you start with an unpremultiplied `SkImage` (like a PNG), turn that into an
|
|
`SkImageShader`, and sample that shader... the resulting colors will be
|
|
`[R*A, G*A, B*A, A]`, **not** `[R, G, B, A]`.
|
|
- If your SkSL will return transparent colors, it must be sure to multiply the
|
|
`RGB` by `A`.
|
|
- For more complex shaders, you must understand which of your colors are
|
|
premultiplied vs. unpremultiplied. Many operations don't make sense if you mix
|
|
both kinds of color together.
|
|
|
|
Skia _enforces_ that the color produced by your SkSL is a valid premultiplied
|
|
color. In other words, `RGB <= A`. If your SkSL returns colors where that is not
|
|
true, they will be clamped, leading to incorrect colors. The image below
|
|
demonstrates this: properly premultiplied colors produce a smooth gradient as
|
|
alpha decreases. Unpremultipled colors cause the gradient to display
|
|
incorrectly, with a shift in hue as the alpha changes. This hue shift is caused
|
|
by Skia clamping the color's RGB values to its alpha.
|
|
|
|
<fiddle-embed name='e97da657941673896ea6b55703463d8a'></fiddle-embed>
|
|
|
|
---
|
|
|
|
## <span id="coords">Coordinate Spaces</span>
|
|
|
|
To understand how coordinates work in SkSL, you first need to understand
|
|
[how they work in Skia](/docs/user/coordinates). If you're comfortable with Skia's coordinate
|
|
spaces, then just remember that the coordinates supplied to your `main()` are **local**
|
|
coordinates. They will be relative to the coordinate space of the `SkShader`. This will match the
|
|
local space of the canvas and any `localMatrix` transformations. Additionally, if the shader is
|
|
invoked by another, that parent shader may modify them arbitrarily.
|
|
|
|
In addition, the `SkShader` produced from an `SkImage` does not use normalized coordinates (like a
|
|
texture in GLSL). It uses `(0, 0)` in the upper-left corner, and `(w, h)` in the bottom-right
|
|
corner. Normally, this is exactly what you want. If you're sampling an `SkImageShader` with
|
|
coordinates based on the ones passed to you, the scale is correct. However, if you want to adjust
|
|
those coordinates (to do some kind of re-mapping of the image), remember that the coordinates are
|
|
scaled up to the dimensions of the image:
|
|
|
|
<fiddle-embed name='492ddaa829dd1ff3f1358659cd58557b'></fiddle-embed>
|