I am trying to implement a display driver wrapper in (no_std) Rust, that is responsible to write an sequence of pixels into a frame buffer. The pixel data is coming from the C world, and representing valid Rgb565 16bit pixels already. The pixel buffer is referred by void* pointer to the first pixel and the length is known. My intention is to create a Color::Rgb565 slice from the pixel data, and turning that into an interator and call embedded_graphics_core::draw_target::fill_contiguous() with that pixel slice.
My (unsafe) code:
if !color_p.is_null() {
let color_p = color_p as *const Rgb565;
let colors = core::slice::from_raw_parts(color_p, (w*h) as usize );
let colors_it = colors.iter();
display.fill_contiguous(&r, colors_it);
}
Unfortunately, the fill_contiguous() function has a very specific trait bound on the iterator:
fn fill_contiguous<I>(
&mut self,
area: &Rectangle,
colors: I,
) -> Result<(), Self::Error>
where I: IntoIterator<Item = Self::Color> { ... }
This leads to the following compile error, due to the fact that my iterator is returning references to elements, instead of the elements themself:
40 | display.fill_contiguous(&r, colors_it);
| --------------- ^^^^^^^^^ expected `Rgb565`, found `&Rgb565`
| |
| required by a bound introduced by this call
The compilation can be fixed easily by copying the pixels by modifying the 4th line of my code snippet using copied() on the iterator and the code is compiling and working fine then:
let colors_it = colors.iter().copied();
However, this unnecessary copy is highly undesirable for performance reasons, as this code is executed for every changed pixel at the refresh rate of the display. Is there any more elegant way to deal with this issue without copying every pixel?
let colors_it = colors.into_iter();.copied()is the way to make the copy as performant as possible by doing it for free during the iteration, whereas.into_iter()needs to make the copy in advance..into_iter()needs to make a copy. In fact, as.into_iter()takes ownership of self, I expect it to make no copy at all.into_itertakesselfby value, not by reference – that means thatselfhas to be moved into a function. From the hardware point of view, a move is a copy – in either case, you're reading all the bits from one place and writing them to another (the only distinction between moves and copies is as to whether hte original is still usable afterwards). The Rust compiler can sometimes optimise this sort of move out, but it doens't always (e.g. if you callinto_iteron an array, the resulting assembly code often actually does make the copy).