Please note that this migration guide may be incomplete in some sections. If any missing or incorrect information is found, please open an issue or join the Matrix chatroom to bring it to our attention.
- Macros are removed
- Primitives
- Geometry
- Style module
- Text and fonts
- General
- Mock display
- The
embedded-graphics-core
crate - For display driver authors
- Image format support crates
- For text rendering crates
All text, primitive and style macros have been removed. To create text, primitives and styles, use the appropriate constructors or builders instead.
For example, a styled rectangle is now built like this:
- let filled_rect: Styled<Rectangle, PrimitiveStyle<Rgb565>> = egrectangle!(
- top_left = (10, 20),
- bottom_right = (30, 40),
- style = primitive_style!(stroke_color = Rgb565::RED, fill_color = Rgb565::GREEN)
- );
+ let filled_rect = Rectangle::with_corners(Point::new(10, 20), Point::new(30, 40))
+ .into_styled(
+ PrimitiveStyleBuilder::new()
+ .stroke_color(Rgb565::RED)
+ .fill_color(Rgb565::GREEN)
+ .build()
+ );
Previously, drawing a filled shape with a transparent stroke of non-zero width would bleed the fill under the stroke. This is changed in 0.7 to honor the stroke width and alignment, even if it is the stroke color is None
, allowing for filled shapes with transparent borders.
The stroke and fill color no longer affect the primitive's bounding box returned by Dimensions::bounding_box
. The stroke width is now always considered even if the stroke is transparent.
A circle is now defined by it's top-left corner and diameter. This has the advantage that circles with odd diameters are now also supported.
// Create a circle centered around (30, 30) with a diameter of 20px
use embedded_graphics::{geometry::Point, primitives::Circle};
- let circle = Circle::new(Point::new(30, 30), 10);
+ let circle = Circle::new(Point::new(20, 20), 20);
To create a circle from a center point and diameter, use Circle::with_center
:
use embedded_graphics::{geometry::Point, primitives::Circle};
- let circle = Circle::new(Point::new(20, 20), 5);
+ let circle = Circle::with_center(Point::new(20, 20), 10);
Rectangles are now defined by their top-left corner and size instead of the top-left and bottom-right corner.
use embedded_graphics::{geometry::{Point, Size}, primitives::Rectangle};
- let rectangle = Rectangle::new(Point::new(20, 30), Point::new(40, 50));
+ let rectangle = Rectangle::new(Point::new(20, 30), Size::new(20, 30));
To retain the old behavior, use Rectangle::with_corners
instead:
use embedded_graphics::{geometry::Point, primitives::Rectangle};
- let rectangle = Rectangle::new(Point::new(20, 30), Point::new(40, 50));
+ let rectangle = Rectangle::with_corners(Point::new(20, 30), Point::new(40, 50));
The vertices of a triangle are now stored in a single vertices
field with the type [Point; 3]
. Previously, they were stored in three separate fields p1
, p2
and p3
.
To access an individual vertex of a triangle, use triangle.vertices[]
.
use embedded_graphics::{prelude::*, primitives::Triangle};
let triangle = Triangle::new(Point::new(20, 30), Point::new(40, 50), Point::new(60, 70));
- let p1 = triangle.p1;
- let p2 = triangle.p2;
- let p3 = triangle.p3;
+ let p1 = triangle.vertices[0];
+ let p2 = triangle.vertices[1];
+ let p3 = triangle.vertices[2];
To create a triangle from a slice, use the new Triangle::from_slice
method:
use embedded_graphics::{geometry::{Point}, primitives::Triangle};
let points = [Point::new(20, 30), Point::new(40, 50), Point::new(60, 70)];
let triangle = Triangle::from_slice(&points);
It is no longer possible to create a triangle from an array of Point
s. Instead, pass a reference to Triangle::from_slice
.
Inconsistencies in the coordinate system, like an off by one error in the size of rectangles, have been fixed.
The three methods in the Dimensions
trait were replaced by a single bounding_box
method. This should return a Rectangle
which encompasses the entire shape.
The style
module has been removed. The items in it have been moved:
-
PrimitiveStyle
,PrimitiveStyleBuilder
andStyled
are now available in theembedded_graphics::primitives
module. -
TextStyle
andTextStyleBuilder
were renamed are now available underembedded_graphics::mono_font::{MonoTextStyle, MonoTextStyleBuilder}
.Note that usage with
Text
has changed. See the text changes section for more.
The fonts
module has been split into a text
and a mono_font
module. The text
module contains
a Text
drawable which can be used with different text renderers. The mono_font
module contains
a text renderer for monospaced fonts and the builtin fonts.
Text
drawable now use two style objects to define the output format. The first style object is
a character style, which defines parameters like the font and text color. This object is provided
by the used text renderer and the available settings will differ between different renderers.
For the builtin monospaced font support the character style is MonoTextStyle
, which replaces the
TextStyle
object from embedded-graphics 0.6.
The second style is the new TextStyle
which defines how the text should be laid out. Available
settings are horizontal alignment, baseline and line height. This style is independent of the used
text renderer.
The collection of builtin fonts are now sourced from public domain BDF fonts in the XOrg project. Due to this, they have slightly different dimensions and glyphs and so have changed names. Some sizes are not the same in the new set, but a rough mapping is as follows:
Old font | Visually closest new font |
---|---|
fonts::Font6x6 ![]() |
mono_font::ascii::FONT_4X6 ![]() |
fonts::Font6x8 ![]() |
mono_font::ascii::FONT_6X10 ![]() |
fonts::Font6x12 ![]() |
mono_font::ascii::FONT_6X13 ![]() |
fonts::Font8x16 ![]() |
mono_font::ascii::FONT_9X15_BOLD ![]() |
fonts::Font12x16 ![]() |
mono_font::ascii::FONT_10X20 ![]() |
fonts::Font24x32 |
The largest available new font is FONT_10X20 , which is significantly smaller than the old Font24x32 . Larger fonts are available in external crates. |
Note that all fonts are available in with different glyph subsets to support a wide variety of languages. The table above only shows the new fonts' ascii
variants. The new fonts tend to use a larger glyph height for the same cap height, which improves readability but may require some layout changes.
The default baseline for fonts is now the font's alphabetic baseline instead of the top of the glyph bounding box. To retain the 0.6 behavior and position text using its top-left corner, set the baseline
property to Baseline::Top
:
use embedded_graphics::text::{Baseline, TextStyle, TextStyleBuilder};
let style = TextStyle::with_baseline(Baseline::Top);
// OR
let style = TextStyleBuilder::new().baseline(Baseline::Top).build();
The Drawable
trait now uses an associated type for its pixel color instead of a type parameters.
An associated type, Output
, has also been added which can be used to return values
from drawing operations. The unit type ()
can be used if the draw
method doesn't need to return
anything, e.g. type Output = ();
- impl<'a, C: 'a> Drawable<C> for &Button<'a, C>
- where
- C: PixelColor + From<BinaryColor>,
- {
- fn draw<D>(self, display: &mut D) -> Result<(), D::Error> where D: DrawTarget<C> {
- // ...
- }
- }
+ impl<C> Drawable for Button<'_, C>
+ where
+ C: PixelColor + From<BinaryColor>,
+ {
+ type Color = C;
+
+ type Output = ();
+
+ fn draw<D>(&self, display: &mut D) -> Result<Self::Output, D::Error>
+ where
+ D: DrawTarget<Color = C>,
+ {
+ Rectangle::new(self.top_left, self.size)
+ .into_styled(PrimitiveStyle::with_fill(self.bg_color))
+ .draw(display)?;
+ Text::new(self.text, Point::new(6, 6))
+ .into_styled(TextStyle::new(Font6x8, self.fg_color))
+ .draw(display)
+ }
+ }
Styled primitives no longer implement IntoIterator
to create a pixel iterator. Use the new Styled::pixels
method instead.
For example, chaining two pixel iterators together now requires explicit calls to pixels()
:
+ use embedded_graphics::prelude::*;
let background = Rectangle::new(...);
let text = Text::new(...);
- background.into_iter().chain(&text)
+ background.pixels().chain(text.pixels())
The MockDisplay
, used often for unit testing, now checks for pixel overdraw and out of bounds drawing by default. These additional checks can be disabled by using the set_allow_overdraw
and set_allow_out_of_bounds_drawing
methods, if required.
The width
and height
methods have been removed. Use the bounding_box
method provided by the Dimensions
trait instead:
// Or: use embedded_graphics::prelude::*;
use embedded_graphics::geometry::Dimensions;
use embedded_graphics::mock_display::MockDisplay;
let display = MockDisplay::new();
let width = display.bounding_box().size.width;
let height = display.bounding_box().size.height;
An advanced visual representation of failing MockDisplay
assertions can be enabled by setting the EG_FANCY_PANIC
environment variable to 1
, for example, by calling EG_FANCY_PANIC=1 cargo test
.
To use EG_FANCY_PANIC
the new MockDisplay::assert_eq
and assert_eq_with_message
must be used instead of the assert_eq!
macro.
#[test]
fn check_equality() {
let expected = MockDisplay::from_pattern(&[ /* ... */ ]);
let mut display = MockDisplay::new();
Circle::new(Point::new(1, 1), 1)
.into_styled(PrimitiveStyle::with_fill(BinaryColor::On))
.draw(&mut display)?;
display.assert_eq(&expected);
}
The assert_pattern
and assert_pattern_with_message
can be used to check the display state against a pattern without using MockDisplay::from_pattern
and a separate assertion.
- #[test]
- fn tiny_circle_filled() {
- let mut display = MockDisplay::new();
-
- Circle::new(Point::new(1, 1), 1)
- .into_styled(PrimitiveStyle::with_fill(BinaryColor::On))
- .draw(&mut display)?;
-
- assert_eq!(
- display,
- MockDisplay::from_pattern(&[
- " # ",
- "###",
- " # "
- ])
- );
- }
+ #[test]
+ fn tiny_circle_filled() {
+ let mut display = MockDisplay::new();
+
+ Circle::new(Point::new(0, 0), 3)
+ .into_styled(PrimitiveStyle::with_fill(BinaryColor::On))
+ .draw(&mut display)
+ .unwrap();
+
+ display.assert_pattern(&[
+ " # ",
+ "###",
+ " # ",
+ ]);
+ }
Types that are required by other crates that extend the functionality of embedded-graphics
have been moved into the new embedded-graphics-core
crate. The core crate is intended to provide a more stable interface for display drivers and image libraries to make them work across multiple major releases of embedded-graphics
.
It is recommended that embedded-graphics
is used for applications and embedded-graphics-core
be used for crates that extend embedded graphics where possible. Note that some features required by e.g. image crates are currently only present in embedded-graphics
, so using embedded-graphics-core
is not always possible.
DrawTarget
now uses an associated type for the target color instead of a type parameter. As this can be a limitation versus older code which implements DrawTarget
for e.g. C: Into<Rgb565>
, the color_converted
method can be used to create a draw target which converts the drawable's color format to the display's color format.
The DrawTarget
trait now has an additional bound on the Dimensions
trait to replace the removed size
method. By using the Dimensions
trait the drawable area of a draw targets can be positioned freely and is no longer limited to start in the origin at (0, 0)
. But for display drivers it is recommended that the drawable area does start at (0, 0)
. To simplify implementation and provide a type level guarantee that the drawable area starts at the origin, OriginDimensions
can be implemented instead of Dimensions
. The Dimensions
trait is automatically implemented for all types that implement OriginDimensions
.
Note that Dimensions
and OriginDimensions
should be imported from embedded-graphics-core
, not embedded-graphics
. See the relevant section for more details.
All draw_*
methods to draw specific primitives (draw_circle
, draw_triangle
, etc) have been removed. These methods were hard to implement correctly and consistently between different drivers. The new lower level draw methods are easier to implement and still improve performance over pixel by pixel drawing.
-
draw_iter
Draws individual pixels to the display without a defined order. This is the only required method in this trait, however will likely be the slowest pixel drawing implementation as it cannot take advantage of hardware accelerated features (e.g. filling a given area with a solid color with
fill_solid
). -
fill_contiguous
Fills a given area with an iterator providing a contiguous stream of pixel colors. This may be used to efficiently draw an image or other non-transparent item to the display. The given pixel iterator can be assumed to be contiguous, iterating from top to bottom, each row left to right. This assumption potentially allows more efficient streaming of pixel data to a display.
-
fill_solid
Fills a given area with a solid color.
-
clear
Fills the entire display with a solid color.
These methods aim to be more compatible with hardware-accelerated drawing commands. Where possible, embedded-graphics drawables will use fill_contiguous
and fill_solid
to improve performance, however may fall back to draw_iter
by default.
To reduce duplication, please search the DrawTarget
documentation on https://docs.rs/embedded-graphics-core for more details on the usage and arguments of the above methods.
The following example updates the SSD1306
driver using the BinaryColor
color type.
- use crate::{
- drawable::Pixel,
- geometry::Size,
- pixelcolor::{PixelColor, BinaryColor},
- DrawTarget,
- };
-
- impl DrawTarget<BinaryColor> for Ssd1306 {
- type Error = core::convert::Infallible;
-
- fn draw_pixel(&mut self, pixel: Pixel<BinaryColor>) -> Result<(), Self::Error> {
- // ...
-
- Ok(())
- }
-
- fn size(&self) -> Size {
- // ...
- }
- }
+ use embedded_graphics_core::{
+ draw_target::DrawTarget,
+ geometry::{OriginDimensions, Size},
+ pixelcolor::{PixelColor, BinaryColor},
+ Pixel,
+ };
+
+ DrawTarget for Ssd1306 {
+ type Color = BinaryColor;
+ type Error = core::convert::Infallible;
+
+ fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
+ where
+ I: IntoIterator<Item = Pixel<Self::Color>>,
+ {
+ // ...
+
+ Ok(())
+ }
+ }
+
+ impl OriginDimensions for Ssd1306 {
+ fn size(&self) -> Size {
+ // ...
+ }
+ }
Image format support crates must now implement the ImageDrawable
and OriginDimensions
traits from embedded-graphics-core
to integrate with embedded-graphics.
The below examples shows an implementation for an imaginary MyRgb888Image
which uses 24 bit RGB color.
use embedded_graphics::{
draw_target::{DrawTarget, DrawTargetExt},
geometry::{OriginDimensions, Size},
image::ImageDrawable,
pixelcolor::{PixelColor, Rgb888},
primitives::Rectangle,
};
struct MyRgb888Image {
// ...
}
impl ImageDrawable for MyRgb888Image {
type Color = Rgb888;
fn draw<D>(&self, target: &mut D) -> Result<(), D::Error>
where
D: DrawTarget<Color = Rgb888>,
{
// Draw the image to the target, e.g. by calling `target.fill_contiguous` or by using another `Drawable`.
}
fn draw_sub_image<D>(&self, target: &mut D, area: &Rectangle) -> Result<(), D::Error>
where
D: DrawTarget<Color = Self::Color>,
{
// Delegate to the draw() method using a reduced draw target
self.draw(&mut target.translated(-area.top_left).clipped(area))
}
}
impl OriginDimensions for MyRgb888Image {
fn size(&self) -> Size {
// Return image width and height in pixels
}
}
Monospaced fonts no longer use a separate type per font and are now defined by using a MonoFont
object. In most applications fonts will be declared as a compile time constant, but fonts can now also be loaded or generated at runtime.
- // The font bitmap has 32 character glyphs per row.
- const CHARS_PER_ROW: u32 = 32;
-
- // Map a given character to an index in the glyph bitmap
- fn char_offset_impl(c: char) -> u32 {
- let fallback = '?' as u32 - ' ' as u32;
- if c < ' ' {
- return fallback;
- }
- if c <= '\u{007f}' {
- return c as u32 - ' ' as u32;
- }
- if c < '\u{00A0}' || c > 'ÿ' {
- return fallback;
- }
- c as u32 - ' ' as u32 - 32
- }
-
- #[derive(Debug, Copy, Clone)]
- pub struct ExampleFont {}
- impl Font for ExampleFont {
- const FONT_IMAGE: &'static [u8] = include_bytes!("../data/ExampleFont.raw");
- const CHARACTER_SIZE: Size = Size::new(5, 9);
- const FONT_IMAGE_WIDTH: u32 = Self::CHARACTER_SIZE.width * CHARS_PER_ROW;
-
- fn char_offset(c: char) -> u32 {
- char_offset_impl(c)
- }
- }
+ use embedded_graphics::{
+ geometry::Size,
+ image::ImageRaw,
+ mono_font::{mapping::ISO_8859_1, DecorationDimensions, MonoFont},
+ };
+
+ pub const EXAMPLE_FONT: MonoFont = MonoFont {
+ image: ImageRaw::new_binary(
+ // This example uses 32 characters per row, each character 5px across.
+ 32 * 5,
+ ),
+ glyph_mapping: &ISO_8859_1,
+ character_size: Size::new(5, 9),
+ character_spacing: 0,
+ baseline: 7,
+ underline: DecorationDimensions::new(8, 1),
+ strikethrough: DecorationDimensions::new(4, 1),
+ };
Custom mappings between characters and glyph positions can be used by using StrGlyphMapping
, using a function or implementing the GlyphMapping
trait:
use embedded_graphics::{
geometry::Size,
image::ImageRaw,
mono_font::{mapping::StrGlyphMapping, DecorationDimensions, MonoFont},
};
pub const EXAMPLE_FONT: MonoFont = MonoFont {
image: ImageRaw::new_binary(
include_bytes!("../data/digits.raw"),
// In this example, this equals 10 characters per row, each character 15px across.
10 * 15,
),
glyph_mapping: &StrGlyphMapping::new("\009", 0),
// or use a function:
// glyph_mapping: &digit_mapping,
character_size: Size::new(15, 30),
character_spacing: 5,
baseline: 29,
underline: DecorationDimensions::default_underline(30),
strikethrough: DecorationDimensions::default_strikethrough(30),
};
fn digit_mapping(c: char) -> usize {
if c >= '0' || c <= '9' {
c as usize - '0' as usize
} else {
0
}
}
Crates that handle text rendering more complex than simple monospace fonts should now implement the
CharacterStyle
and TextRenderer
traits. These are used for both text styling and layout.
Please refer to their respective docs for implementation details.
An implementation of more complex font rendering using BDF font files is available in the eg-bdf crate, which may be useful as a reference for other implementations.