skia2/site/docs/user/coordinates.md
Brian Osman 0351d5af88 Add user docs about Skia coordinate spaces
Includes a general explanation of device vs. local, how shaders don't
move with geometry, and how to use canvas transforms. Also shows
localMatrix on an SkShader. Finally, links to that from the SkSL page,
and adds SkSL-specific notes about coordinates.

Bug: skia:11763
Change-Id: I31462aff66ff8514392098b972a10e7c96e4f254
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/393516
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
2021-04-09 13:50:19 +00:00

3.4 KiB

title linkTitle
Skia Coordinate Spaces Coordinates

Overview

Skia generally refers to two different coordinate spaces: device and local. Device coordinates are defined by the surface (or other device) that you're rendering to. They range from (0, 0) in the upper-left corner of the surface, to (w, h) in the bottom-right corner - they are effectively measured in pixels.


Local Coordinates

The local coordinate space is how all geometry and shaders are supplied to the SkCanvas. By default, the local and device coordinate systems are the same. This means that geometry is typically specified in pixel units. Here, we position a rectangle at (100, 50), and specify that it is 50 units wide and tall:

Local coordinates are also used to define and evaluate any SkShader on the paint. Here, we define a linear gradient shader that goes from green (when x == 0) to blue (when x == 50):


Shaders Do Not Move With Geometry

Now, let's try to draw the gradient-filled square at (100, 50):

What happened? Remember, the local coordinate space has not changed. The origin is still in the upper-left corner of the surface. We have specified that the geometry should be positioned at (100, 50), but the SkShader is still producing a gradient as x goes from 0 to 50. We have slid the rectangle across the gradient defined by the SkShader. Shaders do not move with the geometry.


Transforming Local Coordinate Space

To get the desired effect, we could create a new gradient shader, with the positions moved to 100 and 150. That makes our shaders difficult to reuse. Instead, we can use methods on SkCanvas to change the local coordinate space. This causes all local coordinates (geometry and shaders) to be evaluated in the new space defined by the canvas' transformation matrix:


Transforming Shader Coordinate Space

Finally, it is possible to transform the coordinate space of the SkShader, relative to the canvas local coordinate space. To do this, you supply a localMatrix parameter when creating the SkShader. In this situation, the geometry is transformed by the SkCanvas matrix. The SkShader is transformed by the SkCanvas matrix and the localMatrix for that shader. The other way to think about this: The localMatrix defines a transform that maps the shader's coordinates to the coordinate space of the geometry.

To help illustrate the difference, here's our gradient-filled box. It's first been translated 50 units over and down. Then, we apply a 45 degree rotation (pivoting on the center of the box) to the canvas. This rotates the geometry of the box, and the gradient inside it:

Compare that to the second example. We still translate 50 units over and down. Here, though, we apply the 45 degree rotation only to the shader, by specifying it as a localMatrix to the SkGradientShader::MakeLinear function. Now, the box remains un-rotated, but the gradient rotates inside the box: