Expand description
§Impeller
Impeller is a 2D vector graphics renderer used in Flutter. Impeller can also be used standalone (without flutter) with its C API. This crate provides a safe rust wrapper around the C API (and also the raw bindings).
§What can it do?
- draw 2D shapes like paths (lines/curvies), rectangles, circles, etc.
- draw AND layout text.
- draw effects like blurs, shadows, color blending etc.
- clipping using any shape.
§Where do I want to use it?
- UI libraries are the best use-case.
- 2D games? easy to embed Impeller into any opengl/vk/metal app.
§Docs
The docs and examples of this crate are good enough for really basic drawing (eg: drawing a rect). But this is nowhere enough for real world usage. They will tell you how to use a object, but now why or where you would use this.
For example, pretty much NONE of the enums are documented. We will slowly improve the situation, but until then, the below resources should help cover the gaps. Most rust item docs will also contain direct links to their counterparts in dart/skiasharp/react-native-skia.
§Dart:Ui Docs
Impeller is actually designed for dart/flutter, so, it is the best place to find documentation. Most of the object and function names are same in rust/dart, so you can easily translate the docs from dart to rust. eg: StrokeCap Enum.
Some names are different though. DisplayListBuilder is the combination of PictureRecorder and Canvas items in dart. DisplayList is Picture in dart.
It also has the best explanation for BlendMode with lots of pictures.
One jpeg screenshot is worth a thousand words of text documentation. - Albert Einstein
§React Native Skia Docs
Technically, this is documenting Skia. But both Impeller and Skia have such a large overlap in terminology, design and functionality that a lot of learning transfers over seamlessly.
It also provides a lot of images showing the different options (eg: MaskFilters Blur).
§SkiaSharp docs
Again, skia. But lots of guide-level docs for people who are starting out with Vector graphics.
§Why Impeller?
- Blazingly? Fast - It is used in Flutter, so, you know it will be maintained and improved continuously. The focus is also on consistency to keep everything running smooth.
- Great text rendering AND layout - The rust ecosystem is severely lacking when it comes to text. Impeller should cover most of your needs.
- Simple Object Model: The object model is very simple and takes like 5 minutes to learn. see Object Model
- Easy to Embed - Any (opengl/vk/mtl)-based app/game can embed Impeller in less than 10 lines.
- Fast compilation - The only bottleneck is network speed to download the prebuilt libs.
And even that can be amortized with the
cache_libsfeature. see Features - Easy Bindings - The C API is really easy and allows us to “auto-generate” bindings. So, if we are trying to generate lua or wasm bindings, this is a huge QoL feature.
§Why not Impeller?
- Impeller is written in C++ and we do not support building from source. We use pre-built static/shared libraries instead.
- No support for d3d and no fallback software renderer.
- No powerful features like custom shaders. use Skia-rs instead.
- As the bindings are not widely used yet, we may still have some bugs.
§How to use the crate
For libraries who are just “using” the API, all you need to do is just use the crate with no features.
The final binary/executable should enable the prebuilt_libs feature to download the prebuilt libraries from github releases and link them.
NOTE: We use curl to download and tar (or unzip on linux) to extract the archives. linux, mac and windows (10+) will have these by default.
§Features
prebuilt_libs- Downloads the prebuilt libraries from github releases and links them to your project.static_link- If enabled, we will link static libraries. only available on linux/windows. All other platforms will need to use shared libraries or provide their own (see Custom Linking).debug_static_link- If enabled, we will use the unstripped static libs with debug info (useful for debugging). only available on linux/windows just likestatic_link.cache_libs- If enabled, we will cache the prebuilt-libs in.impeller_cachedirectory inside your project directory (parent dir oftarget). Add/.impeller_cacheto.gitignore, if you enable this feature.- You can customize cache directory path with
IMPELLER_CACHE_DIRenv variable. And also use this to provide your own custom built libs. - caching avoids redownloading after
cargo cleansaving bandwidth and this in turns also makes the builds faster. - You also get to inspect the downloaded archives in the cache to debug any errors.
- You can customize cache directory path with
§Safety
I try to keep the bindings sound, but graphics programming is just really really unsafe. Especially around the handling of context/surface lifetimes.
Objects like textures/contexts are inherently linked to platform resources (like openGL context or vulkan device). So, they must ALL be destroyed before you destroy the underlying window or opengl context. That is the sole reason creating them is unsafe.
§Custom Linking
When you want to link in your own custom impeller library:
- Enable
cache_libsfeature to make it use libraries from a cached directory. - set
IMPELLER_CACHE_DIRenvironment variable to manually set the location of the cache directory. - Inside that directory, create
targetos_targetarch(eg:linux_x64) directory for dynamic libs andtargetos_targetarch_static_profile(eg:linux_x64_static_release) directory for static libs. - depending on the features
static_linkanddebug_static_linkyou might need to createtargetos_targetarch_static_debugdirectory, build script will search for dynamic libs or release static libs or debug static libs (useful for debugging).
§API
In a normal application, you usually have the phases of initialization, event loop and cleanup.
§Initialization
The initialization phase is usually the one where you create singletons. For example:
- Create a Context (opengl/vk/metal) with the respective
newfunctions. This is used to:- create Surface/VkSwapChain (usually wrapping the default framebuffer).
- create a Texture from raw pixel data
- Create a TypographyContext (and register any custom fonts you may want to use).
§Event Loop
Impeller has two core objects: Paint and DisplayListBuilder. You need to understand both of them, to use this library.
DisplayListBuilder acts like a canvas on to which we “draw” using methods like DisplayListBuilder::draw_rect. Internally, it builds up a list of draw commands, so that we can execute those commands later.
The draw command is modified by three things before being added to the list:
- Transformation: decides the final position/size/shape of the draw command.
- clipping: how much of the draw command is effectively visible.
- Paint: further details of the draw command like color to use or other effects like blur.
§Transformation and Clip
The builder maintains an internal “stack” of transformation matrices and clip rects, manipulated by DisplayListBuilder::save and DisplayListBuilder::restore.
You can modify the current transformation with DisplayListBuilder::scale, DisplayListBuilder::translate and DisplayListBuilder::rotate. You can directly set a transformation matrix with DisplayListBuilder::set_transform too or append additional matrix with DisplayListBuilder::transform. These will be applied to the draw commands that follow, until you use DisplayListBuilder::restore (to pop off the stack) or DisplayListBuilder::reset_transform to just set the current matrix to identity.
You can modify the current clip with clip and DisplayListBuilder::clip_path. The clip_op argument ClipOperation controls whether you want the draw commands to only be visible inside or outside the clip shape.
§Paint
The Paint is used to configure the details of draw commands like:
- Color, Transparency, Blend mode
- Draw style (filled rect or a stroked rect), width of the stroke
- Filters for blurs, color tinting a texture draw etc..
Finally, once you have added all the draw commands, you build a DisplayList with DisplayListBuilder::build.
DisplayList is an immutable, self-contained and reusable copy of draw commands that you can execute/replay on to a surface (or add it to another DisplayListBuilder).
You can use it as a reusable piece of drawing logic. And as you can append it to DisplayListBuilder, it will be affected by the transformation/clip of the builder.
For example, you can buid a DisplayList for a button background, and all button widgets can reuse that for drawing different sized/rotated/positioned buttons.
After building up this mega displaylist that contains your entire render tree, you draw it to a Surface/VkSwapChain with Surface::draw_display_list and preset it using Surface::present.
§Cleanup
During the cleanup phase, you drop the all the objects including singletons. And then,you destroy the window.
§Shapes
Drawing most shapes like rect/circle is pretty simple, but there’s a few that are a bit more complex.
§Paths
You use PathBuilder to build a list of path commands (start here, move to A, move to B etc..) and eventually call PathBuilder::copy_path_new or PathBuilder::take_path_new to create a Path.
A Path is an immutable and reusable list of lines, curves and other path commands. Each path contains zero or more sub-paths. So, a path can contain two independent circles or lines. You draw it using DisplayListBuilder::draw_path.
§Paragraphs
A ParagraphBuilder is used to record a bunch of text sections with different styles and then, build an immutable and reusable Paragraph.
- TypographyContext holds your fonts and provides them to ParagraphBuilder.
- ParagraphBuilder to record text (with different pieces of text using different styles like colors/fonts/sizes etc..)
- You layout text with ParagraphBuilder::build using some max width and build a Paragraph
- You draw Paragraph with DisplayListBuilder::draw_paragraph.
ParagraphBuilder also maintains an internal stack of text styles. So, you can push a style, add some text which will be rendered using that style and then, pop the style to go back to previous style.
§Textures
You can create a Texture from raw pixel data or adopt an opengl texture using a Context.
While you can just draw a texture (image) directly using DisplayListBuilder::draw_texture, you can also use ImageFilter or ColorFilter of the Paint object to apply fancy effects when sampling a texture.
§Filters/Sources etc..
The other objects are there mostly to add fancy effects like blur, color tint, color gradients etc.. Read the respective docs for more details.
NOTE: The crate currently has very little API for some structs like matrices or rects. Contributions welcome :)
Structs§
- Color
- Color
Filter - Color filters are functions that take two colors and mix them to produce a single color. This color is then merged with the destination during blending.
- Color
Matrix - A 4x5 matrix using row-major storage used for transforming color values.
- Color
Source - Color sources are functions that generate colors for each texture element covered by a draw call. The colors for each element can be generated using a mathematical function (to produce gradients for example) or sampled from a texture.
- Context
- An Impeller graphics context. Contexts are platform and client-rendering-API specific.
- Display
List - Display lists represent encoded rendering intent (draw commands). These objects are immutable, reusable, thread-safe, and context-agnostic.
- Display
List Builder - Display list builders allow for the incremental creation of display lists.
- Fragment
Program - A fragment shader is a small program that is authored in GLSL and compiled using impellerc that runs on each pixel covered by a polygon and allows the user to configure how it is shaded.
- Glyph
Info - Describes the metrics of glyphs in a paragraph line.
- Image
Filter - Image filters are functions that are applied regions of a texture to produce a single color. Contrast this with color filters that operate independently on a per-pixel basis. The generated color is then merged with the destination during blending.
- Impeller
Version - The current Impeller API version.
- Line
Metrics - Describes the metrics of lines in a fully laid out paragraph.
- Mask
Filter - Mask filters are functions that are applied over a shape after it has been drawn but before it has been blended into the final image.
- Paint
- Paints control the behavior of draw calls encoded in a display list.
- Paragraph
- An immutable, fully laid out paragraph.
- Paragraph
Builder - Paragraph builders allow for the creation of fully laid out paragraphs (which themselves are immutable).
- Paragraph
Style - Specified when building a paragraph, paragraph styles are managed in a stack with specify text properties to apply to text that is added to the paragraph builder.
- Path
- Represents a two-dimensional path that is immutable and graphics context agnostic.
- Path
Builder - Path builders allow for the incremental building up of paths.
- Range
- Rounding
Radii - Represents the rounding radii of a rounded rect.
- Surface
- A surface represents a render target for Impeller to direct the rendering intent specified the form of display lists to.
- Text
Decoration Type - The types of text decoration to apply to text.
- Texture
- A reference to a texture whose data is resident on the GPU. These can be referenced in draw calls and paints.
- Typography
Context - Typography contexts allow for the layout and rendering of text.
- VkSwap
Chain - The primary form of WSI when using a Vulkan context, these swapchains use
the
VK_KHR_surfaceVulkan extension. - Vulkan
Info
Enums§
- Blend
Mode - https://api.flutter.dev/flutter/dart-ui/BlendMode.html
- Blur
Style - https://api.flutter.dev/flutter/dart-ui/BlurStyle.html
- Clip
Operation - https://api.flutter.dev/flutter/dart-ui/ClipOp.html
- Color
Space - https://api.flutter.dev/flutter/dart-ui/ColorSpace.html
- Draw
Style - https://api.flutter.dev/flutter/dart-ui/PaintingStyle.html
- Fill
Type - https://api.flutter.dev/flutter/dart-ui/PathFillType.html
- Font
Style - https://api.flutter.dev/flutter/dart-ui/FontStyle.html
- Font
Weight - https://api.flutter.dev/flutter/dart-ui/FontWeight-class.html
- Pixel
Format - Layout of color components of the pixels
- Stroke
Cap - https://api.flutter.dev/flutter/dart-ui/StrokeCap.html
- Stroke
Join - https://api.flutter.dev/flutter/dart-ui/StrokeJoin.html
- Text
Alignment - https://api.flutter.dev/flutter/dart-ui/TextAlign.html
- Text
Decoration Style - https://api.flutter.dev/flutter/dart-ui/TextDecorationStyle.html
- Text
Direction - https://api.flutter.dev/flutter/dart-ui/TextDirection.html
- Texture
Sampling - The sampling mode to use when drawing a texture.
- Tile
Mode - https://api.flutter.dev/flutter/dart-ui/TileMode.html
Constants§
- FLUTTER_
ARTEFACT_ COMMIT - The commit hash of the prebuilt library artefacts used by this crate.
Functions§
- flutter_
mip_ count - based on the size, it will calculate a suitable mipcount. This function skips 1x1 mip levels because that’s what flutter does.