embedded-graphics / embedded-graphics Goto Github PK
View Code? Open in Web Editor NEWA no_std graphics library for embedded applications
License: Apache License 2.0
A no_std graphics library for embedded applications
License: Apache License 2.0
The documentation of the Drawable trait states:
/// Marks an object as "drawable". Must be implemented for all graphics objects
pub trait Drawable {}
Is this still true? The current code doesn't seem to use this trait and the only references (other than empty implementations) are comments referring to a non existent draw() method.
Hi,
fn translate(&self, by: Coord) -> Self {
Self {
offset: (self.offset.0 + by.0, self.offset.1 + by.1),
..*self
}
}
While looking into the project i got a bit confused by the part above.
Why does translate always make a modified "clone" of the object instead of modifying itself?
I would have expected sth like the following, where you need to explicitly clone the object if you still need it:
fn translate(&mut self, by: Coord) {
self.offset = (self.offset.0 + by.0, self.offset.1 + by.1)
}
Once there is positioning support, there should be some general way of transforming Drawables using the no_std mat
library. .with_transform()
perhaps?
After testing my crate for a few hours I realised that the new crates.io 0.4.2 is not the same as the github master 0.4.2 release.
Somehow the patch hasn't arrived on crates.io even though there was the new release.
Easy way to test it:
Go to the simulator and use the embedded-graphics version from crates.io instead of the local one and then run cargo run --example fonts
It should be possible to get all fancy and have rounded corners on rectangles.
This crate is in a good enough position to be released IMO. Needs some final docs/wrapup and it can go out. Cc rust-embedded-community/ssd1306#59.
From this tweet, it should be possible to do negative translations.
Continue of rust-embedded/wg#22 (comment).
This is assorted thought, to discuss general library improvements, prior to create separate issues. At first, i'd like to say, that i don't invent something new. Everything described below were already implemented in other languages, and i only point to missed things. I strongly recommend to investigate u8g2 sources and wiki as source of ideas. That doesn't mean it's ideal, but it has a lot of useful solutions like font formats.
Basic things (to be honest, what i need fisrt to migrate from C to Rust :)
new
This does not include all supported u8g2 features. Need time to collect more details and analyze other libs.
Answers to your questions in rust-embedded/wg#22 (comment):
The fonts are stored as 1 byte per 8 pixels, so I'm not sure how much more compact they could get without using some sort of runtime decoding which I'd like to steer away from.
See https://github.com/olikraus/u8g2/wiki/u8g2fontformat
0..9
and .
chars to save space.May be, using u8g2 fonts format can be convenient, because you will be able to use existing tools until native ready.
Probably, fonts & tools could be in separate crate to avoid licences mixture.
I'm not sure what you mean for embedded-graphics as it doesn't use a framebuffer at all. Can you elaborate?
embedded-graphics
itself does not use framebuffre, but ssd1306 has it https://github.com/jamwaffles/ssd1306/blob/master/src/mode/graphics.rs#L46.
Also, it may be useful to optimize display bulk writes and to stack async API calls effectively:
eg
.draw_line(...)
.draw_text(...)
.draw_anything(...)
.flush() // <- future
That's only a semantic example, convenient async api is a separate topic to discuss. Also result can affect decision, if embedded-graphics
should have direct access to framebuffer or not.
I don't know your plans on this library. To be honest, i have some pressure from existing projects support, and would like to avoid direct participation in code development. But i could help to analyze existing solutions & suggest possible end-user api/tools designs, because have some experience with such things.
Let me know what do you think.
Code like this fails to compile:
fn multi() -> impl Iterator<Item = Pixel<PixelColorU8>> {
let line = Line::new(Coord::new(0, 1), Coord::new(2, 3))
.with_stroke(Some(1u8.into()))
.into_iter();
let circle = Circle::new(Coord::new(5, 5), 3)
.with_stroke(Some(1u8.into()))
.into_iter();
line.chain(circle)
}
With the following error:
error[E0515]: cannot return value referencing temporary value
--> src/main.rs:46:5
|
38 | let line = Line::new(Coord::new(0, 1), Coord::new(2, 3))
| ________________-
39 | | .with_stroke(Some(1u8.into()))
| |______________________________________- temporary value created here
...
46 | line.chain(circle)
| ^^^^^^^^^^^^^^^^^^ returns a value referencing data owned by the current function
error: aborting due to previous error
It should work! Composing iterators (and caching the result if desired) is a key ergonomic feature of e-g. It currently doesn't work with a function call.
For example the DisplayRotation-Enum (https://jamwaffles.github.io/ssd1306/master/ssd1306/displayrotation/enum.DisplayRotation.html) is gonna be implemented by many of the consumers of this crate. Would it be useful to directly add this here or is that too much off-topic?
It makes no sense to pull in tinytga
and tinybmp
by default. These should be optional features of embedded graphics.
TODO
Closes #91
Drawing a circle in the rust-meetup-simple-clock repo produces a circle that appears to be one pixel too tall. This may be a display driver bug with ssd1306, but in case it is a bug with e-g, this issue is now open.
Add the no_missing_docs
guard and write some "MVP" docs.
It's currently possible to chain drawable objects together like this:
let chained = Rect::new((0, 0), (1, 1), 1)
.into_iter()
.chain(Circle::new((2, 2), 1, 1).into_iter());
disp.draw(chained);
however it's not very ergonomic. It also makes doing transforms on the group as a whole quite difficult. To solve this, there should be a DrawableBuilder
(or other named...) struct with an API that allows the following:
let chained = DrawableBuilder::new()
.append(Rect::new((0, 0), (1, 1), 1))
.append(Circle::new((2, 2), 1, 1));
disp.draw(chained.into_iter());
It should also implement the Transform
trait so that the following is possible:
let chained = DrawableBuilder::new()
.append(Rect::new((0, 0), (1, 1), 1))
.append(Circle::new((2, 2), 1, 1))
.translate((10, 10))
.finish();
disp.draw(chained.into_iter());
I've run into an issue where the fontbuilder iterator will loop indefinitely under certain translation conditions.
Here's a minimal example:
display.draw(Font6x12::render_str("Testing string")
.with_stroke(Some(0xF1FA_u16.into()))
.translate(Coord::new(0, -12))
.into_iter());
display.flush();
if a negative y
translation happens to be equal to the height of the font, it will loop indefinitely in here: https://github.com/jamwaffles/embedded-graphics/blob/master/embedded-graphics/src/fonts/font_builder.rs#L193
This is a more valid use of the PixelColorU*
pattern - a pixel value that wraps a single u16
to provide an R5G6B5 colour type (and some util functions) commonly found in embedded/low bit depth display applications.
That's a sandbox to discuss requirements for new font format, tools and related things.
Fonts:
.rs
files (they should define complex data structures). Also, text format will allow to embed comments with source/copyrigth/license infoTools / data sources:
ttf2bdf
utility)Feedback needed. Add missed things.
There should be a prelude that makes using the crate easier.
Right now a 0
value is off, anything else is on. It should threshold at 127 to create a better approximation of a passed 8BPP image on a 1BPP display.
Use nom in no_std
mode to parse BMP images. It should produce an iterator to stay true to the minimal-alloc philosophy of embedded_graphics.
Nalgebra now supports no_std
. It should therefore be pretty easy to include in embedded-graphics
behind a nalgebra
feature. At minimum, it should redefine the Coord
type to use a Nalgebra Vector2
.
translate
implies that a relative translation is being performed, however this is not the case. Instead, an absolute position for the top-left corner of an object is given. For example, thing.translate(-1, -1)
positions thing
at (-1, -1)
. This change opens up the API to a proper translate()
where something like thing.position(10, 10).translate(5, 5)
would result in thing
being at position (15, 15)
.
This crate should use docs.rs instead of self-hosted docs.
Maybe it's just me but the triangle and the rectangle from the graphics example in the SSD1306
crate seem to be missing a pixel each in the lower right corner.
This is to reduce the number of errors due to expected but non-existent behaviour when using plain integers with embedded graphics.
Missing impls:
impl From<u16> for PixelColorU32 { /* ... */ }
It should be possible to create a primitive using a terser, macro-based syntax. The exact syntax is as yet undefined, but it should be possible to define the primitive's dimensions (start/end coords, radius, etc) as well as styles in an extensible way.
Add edition = "2018"
to all crates in repository and fix any new errors.
It should be possible to create an open and closed line loop. Filled polygons can be added in a separate issue.
Going with a builder-ish interface, Drawable should implement something like .with_position(pos: Coord)
so that anything can be moved around.
The current implementation is incorrect; two bytes [ 0xcc, 0x00 ]
that represent a single 16 bit pixel value are currently interpreted as 0x00cc_u16
which is incorrect; it should be 0xcc00
.
Failing test case:
let image: Image16BPP<PixelColorU16> = Image16BPP::new(
&[
0xff, 0x00, 0x00, 0x00, 0xbb, 0x00, //
0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, //
0xee, 0x00, 0x00, 0x00, 0xaa, 0x00,
],
3,
3,
)
.translate(Coord::new(-1, -1));
let mut it = image.into_iter();
assert_eq!(
it.next(),
Some(Pixel(UnsignedCoord::new(0, 0), 0xcc00_u16.into()))
);
assert_eq!(
it.next(),
Some(Pixel(UnsignedCoord::new(1, 0), 0x0000_u16.into()))
);
assert_eq!(
it.next(),
Some(Pixel(UnsignedCoord::new(0, 1), 0x0000_u16.into()))
);
assert_eq!(
it.next(),
Some(Pixel(UnsignedCoord::new(1, 1), 0xaa00_u16.into()))
);
As noticed in #62 (comment), there are missing docs on at least the image pages. The description for Image16BPP
also describes it as an 8 bit image, which is obviously wrong.
It seems something broke in the SSD1306 driver in the last few hours:
Compiling ssd1306 v0.1.0 (file:///Users/egger/OSS/ssd1306)
error[E0061]: this function takes 3 parameters but 4 parameters were supplied
--> examples/image_i2c.rs:56:14
|
56 | let im = Image1BPP::new(include_bytes!("./rust.raw"), 64, 64, (32, 0));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 3 parameters
error[E0061]: this function takes 1 parameter but 2 parameters were supplied
--> examples/text_i2c.rs:51:15
|
51 | disp.draw(Font6x8::render_str("Hello world!", (0, 0)).into_iter());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 1 parameter
error[E0061]: this function takes 3 parameters but 4 parameters were supplied
--> examples/rotation_i2c.rs:67:14
|
67 | let im = Image1BPP::new(
| ______________^
68 | | include_bytes!("./rust.raw"),
69 | | 64,
70 | | 64,
71 | | (w as u32 / 2 - 64 / 2, h as u32 / 2 - 64 / 2),
72 | | );
| |_____^ expected 3 parameters
error[E0061]: this function takes 1 parameter but 2 parameters were supplied
--> examples/text_i2c.rs:52:15
|
52 | disp.draw(Font6x8::render_str("Hello Rust!", (0, 16)).into_iter());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 1 parameter
For example, instead of just setting the border colour on a rect with Rect::new((0, 0), (10, 10), 1)
, it would be useful to be able to style all primitives::*
objects.
There should be a Stylable
trait that provides a with_style()
method.
As a first step, styling should be able to set:
1
/0
)Some(1)
, Some(0)
, None)with a Style
struct. This struct should be applicable to any embedded-graphics drawable object. Unused styles should be ignored. For example, setting the fill colour of a Font
makes no sense, so would be ignored. Likewise, setting the text colour for a Rect
would also be ignored.
It should also have a Default
so that only properties to change have to be supplied. This also allows easier future expansion. Struct property names should loosely follow the CSS spec, but this is not required if it doesn't make sense.
Example usage:
// Unfilled rect with solid border
let filledSquare = Rect::new((0, 0), (10, 10))
.with_style(Style { border_color: 1 });
// Filled rect with clear, thicker border
let filledSquare = Rect::new((0, 0), (10, 10))
.with_style(Style { border_color: 0, border_width: 2, background_color: Some(1) });
#45 adds stroke width support to all primitives except lines. This is unexpected for the user and bad from a feature perspective so should be fixed.
It should be possible to get hold of an iterator to only generate pixels for part of the screen, as well as the top left and bottom right coordinates of that bounding box. Primitives like lines that don't update their entire bounding box should probably just return their default iterator which gives the same behaviour. The method to get the bounding box should still return the correct values though.
As surfaced in #51, there's a strange artifact when using a background for a font:
The black rectangle (black font on black background) to the middle left has its first row shifted by one which obviously shouldn't happen.
U8g2 has a pretty big collection of fonts. It would be great to have support for them in embedded-graphics. This issue is a tracking issue of sorts to define what should be done as a minimum first step towards u8g2 font support.
Any code that aims to close this issue should:
.raw
files here.The first implementation will be non-ideal, but will show where things can be fixed or improved with more optimisations and tooling in the future.
CC also #28 for some desired future features.
A sized object in this context is an object (line, image, etc) where its size and offset in pixels is known. This will be everything currently included in embedded_graphics, but should be exposed as a separate SizedDrawing
trait accompanying Drawing
so that either graphics objects or displays that don't support setting a draw area don't have to implement it. Many displays support a partial screen draw to a given area which is much more efficient than updating the entire screen. It also makes using screens without a huge display buffer easier. A - actually, no. This will make it a hard requirement which, for example, tiny devices with no framebuffer might not be able to fulfill.SizedDrawing
object should also implement Drawing
for compatibility reasons.
Ensure a link is added to the doc landing page next to the "bla bla all you need to do is impl Drawing
bla bla" section.
The PixelColor
trait is implemented for both PixelColorUx
and ux
(with x = 8, 16, 32). Why are the PixelColorUx
types used instead of the unwrapped variants?
The PixelColorUx
types don't seem to add any additional semantic information. Maybe it would be beneficial to add predefined types for commonly used pixel formats, like a newtype for RGB565 or an enum for 1 bpp displays.
For epaper displays you can set the draw order to X ascending than Y ascending or Y than X for a region, but if you iterate over a rendered string from this library it gives X to Y for each character, then resets the Y range for the next character. If it went Y to X you could iterate directly to the device without a buffer. So basically going left to right rather than top to bottom for each character. I'm trying to use a chip with 20Kb of SRAM, so not enough for a framebuffer.
Hi all,
I've been working on a SSD1351 driver crate based on the work done with the ssd1306. I am at the point where I want to start integrating embedded_graphics traits for more advanced graphic work. The SSD1351 being a 16bit colour display driver the current u8 for colour is not going to work, and in the future this crate should probably be able to support even higher colour depths.
Just wanted to kick off some discussion on the matter :)
From #22, a first step towards more efficient/better fonts is to compress their binary representations in flash. RLE seems easy enough to do. Now that some more font processing needs to happen (PNG -> 8BPP -> RLE), this work should create a tool to help out. It will replace convert_1bpp.sh
with a Rust program.
Hello,
std
is of course unavailable in embedded so I can't use the normal number to string conversion, what is the best practice for drawing integers and floats? EG, I have an f32 that I want to display to 4 significant figures. If this doesn't exist I'd be more than happy to write it.
Thanks :)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.