diff --git a/Cargo.toml b/Cargo.toml index 74ccd5e..9f1ed7a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "it8951" -version = "0.3.0" +version = "0.4.0" edition = "2021" license = "MIT" description = "A IT8951 E-Paper driver" diff --git a/README.md b/README.md index b83a73e..9ef684d 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,29 @@ This crate is mainly developed for the waveshare 7.8" epaper display using spi: https://www.waveshare.com/wiki/7.8inch_e-Paper_HAT The driver uses the embedded_hal traits as hardware abstraction layer. -This driver can be used with the embedded graphics trait. +This driver can be used with the embedded graphics trait, currently only supporing Gray4 (16bit grayscale). ## Details -- IT8951 has a image load engine which can convert pixel data before storing it in the local frame buffer +- IT8951 has a image load engine which can convert pixel data before storing it in the local frame buffer. - It is possible to read and write the memory directly without using the image load engine -- **Important** Data must be always aligned to 16bit words! \ No newline at end of file +- **Important** Data must be always aligned to 16bit words! +- The crates uses the alloc feature to allocate memory on the heap: + - Firmware and LUT version string read from the controller + - Staging buffers to write pixel to the controller. The buffers are allocated as needed, but only one buffer at a time and with up to 1kByte of size. + - When reading controller memory a staging buffer with the size of of the requested data is created. + + +## TODOs +- Support Gray2 and Gray8 with embedded-graphics +- Support display engine fill area +- Support display engine 1 bit per pixel mode +- Support static buffer allocations + +## Changelog + +### 0.4.0 +- **Public API** `new` expects a `Config` parameter to set timeout and buffer size. Default is implemented with timeouts of 15s and buffer size is 1024 Bytes. +- Buffer data type changed from u16 to u8 + - **Public API**: `load_image_area`, `load_image`, and `memory_burst_write` functions are now using u8 as buffer type + - Memory usage is reduced by half (1kByte max. instead of 2kByte) +- **Behavior** Calling `init` no longer clears the eink display. Instead call `reset` directly. diff --git a/examples/esp32/src/main.rs b/examples/esp32/src/main.rs index 7a6959d..c299ff2 100644 --- a/examples/esp32/src/main.rs +++ b/examples/esp32/src/main.rs @@ -2,6 +2,7 @@ use esp_idf_hal::{delay::Ets, gpio::PinDriver, prelude::*, spi::*}; use esp_idf_sys as _; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported use it8951::{interface::*, *}; use embedded_graphics::{prelude::*, primitives::{Rectangle, PrimitiveStyle}, pixelcolor::Gray4}; +use it8951::Config; fn main() -> ! { // Bind the log crate to the ESP Logging facilities @@ -33,7 +34,7 @@ fn main() -> ! { reset, Ets, ); - let mut epd = IT8951::new(display_interface).init(1605).unwrap(); + let mut epd = IT8951::new(display_interface, Config::default()).init(1605).unwrap(); log::info!("Initialized display: {:?}", epd.get_dev_info()); diff --git a/examples/test_eink.rs b/examples/test_eink.rs index fa68c59..063e2c6 100644 --- a/examples/test_eink.rs +++ b/examples/test_eink.rs @@ -1,3 +1,4 @@ +use it8951::Config; use linux_embedded_hal::gpio_cdev::{Chip, LineRequestFlags}; use linux_embedded_hal::spidev::{SpiModeFlags, SpidevOptions}; use linux_embedded_hal::{CdevPin, Delay, SpidevDevice}; @@ -34,7 +35,9 @@ fn main() -> Result<(), Box> { let busy = CdevPin::new(busy_input_handle)?; let driver = it8951::interface::IT8951SPIInterface::new(spi, busy, rst, Delay); - let mut epd = it8951::IT8951::new(driver).init(1670).unwrap(); + let mut epd = it8951::IT8951::new(driver, Config::default()) + .init(1670) + .unwrap(); println!( "Reset and initalized E-Ink Display: \n\r {:?}", diff --git a/src/area_serializer.rs b/src/area_serializer.rs index 2f39927..575c215 100644 --- a/src/area_serializer.rs +++ b/src/area_serializer.rs @@ -9,21 +9,17 @@ use embedded_graphics_core::{ pub struct AreaSerializer { area: Rectangle, rows_per_step: usize, - buffer: Vec, + buffer: Vec, } impl AreaSerializer { - pub fn new(area: Rectangle, color: Gray4) -> Self { - let max_entries: usize = 512; // 1 KByte - - Self::with_buffer_size(area, color, max_entries) - } - pub fn with_buffer_size(area: Rectangle, color: Gray4, buffer_size: usize) -> Self { - let raw_color: u16 = color.luma() as u16; - let data_entry = raw_color << 12 | raw_color << 8 | raw_color << 4 | raw_color; + pub fn new(area: Rectangle, color: Gray4, buffer_size: usize) -> Self { + let raw_color = color.luma(); + let data_entry = raw_color << 4 | raw_color; + assert!(buffer_size % 2 == 0, "Buffer size must be aligned to u16"); // calculate the buffer size - let entries_per_row = get_entires_per_row(area) as usize; + let entries_per_row = get_entires_per_row(area) as usize * 2; // convert length from u16 to u8 let rows_per_step = (buffer_size / entries_per_row).min(area.size.height as usize); assert!(rows_per_step > 0, "Buffer size to small for one row"); let buffer = vec![data_entry; entries_per_row * rows_per_step]; @@ -50,7 +46,7 @@ impl<'a> AreaSerializerIterator<'a> { } impl<'a> Iterator for AreaSerializerIterator<'a> { - type Item = (AreaImgInfo, &'a [u16]); + type Item = (AreaImgInfo, &'a [u8]); fn next(&mut self) -> Option { let area_height = self.area_serializer.area.size.height; @@ -97,7 +93,11 @@ mod tests { height: 1, }, }; - let area_s = AreaSerializer::new(area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA)); + let area_s = AreaSerializer::new( + area.intersection(&BOUNDING_BOX_DEFAULT), + Gray4::new(0xA), + 1024, + ); let mut s = AreaSerializerIterator::new(&area_s); assert_eq!( s.next(), @@ -108,7 +108,7 @@ mod tests { area_w: 1, area_h: 1 }, - [0xAAAAu16].as_slice() + [0xAA, 0xAA].as_slice() )) ); assert_eq!(s.next(), None); @@ -124,7 +124,11 @@ mod tests { height: 1, }, }; - let area_s = AreaSerializer::new(area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA)); + let area_s = AreaSerializer::new( + area.intersection(&BOUNDING_BOX_DEFAULT), + Gray4::new(0xA), + 1024, + ); let mut s = AreaSerializerIterator::new(&area_s); assert_eq!( s.next(), @@ -135,7 +139,7 @@ mod tests { area_w: 1, area_h: 1 }, - [0xAAAAu16].as_slice() + [0xAA, 0xAA].as_slice() )) ); assert_eq!(s.next(), None); @@ -150,7 +154,11 @@ mod tests { height: 1, }, }; - let area_s = AreaSerializer::new(area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA)); + let area_s = AreaSerializer::new( + area.intersection(&BOUNDING_BOX_DEFAULT), + Gray4::new(0xA), + 1024, + ); let mut s = AreaSerializerIterator::new(&area_s); assert_eq!( s.next(), @@ -161,7 +169,7 @@ mod tests { area_w: 1, area_h: 1 }, - [0xAAAAu16].as_slice() + [0xAA, 0xAA].as_slice() )) ); assert_eq!(s.next(), None); @@ -176,7 +184,11 @@ mod tests { height: 1, }, }; - let area_s = AreaSerializer::new(area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA)); + let area_s = AreaSerializer::new( + area.intersection(&BOUNDING_BOX_DEFAULT), + Gray4::new(0xA), + 1024, + ); let mut s = AreaSerializerIterator::new(&area_s); assert_eq!( s.next(), @@ -187,7 +199,7 @@ mod tests { area_w: 1, area_h: 1 }, - [0xAAAAu16].as_slice() + [0xAA, 0xAA].as_slice() )) ); assert_eq!(s.next(), None); @@ -203,7 +215,11 @@ mod tests { height: 1, }, }; - let area_s = AreaSerializer::new(area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA)); + let area_s = AreaSerializer::new( + area.intersection(&BOUNDING_BOX_DEFAULT), + Gray4::new(0xA), + 1024, + ); let mut s = AreaSerializerIterator::new(&area_s); assert_eq!( @@ -215,7 +231,7 @@ mod tests { area_w: 4, area_h: 1 }, - [0xAAAAu16].as_slice() + [0xAA, 0xAA].as_slice() )) ); assert_eq!(s.next(), None); @@ -231,7 +247,11 @@ mod tests { height: 1, }, }; - let area_s = AreaSerializer::new(area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA)); + let area_s = AreaSerializer::new( + area.intersection(&BOUNDING_BOX_DEFAULT), + Gray4::new(0xA), + 1024, + ); let mut s = AreaSerializerIterator::new(&area_s); assert_eq!( s.next(), @@ -242,7 +262,7 @@ mod tests { area_w: 3, area_h: 1 }, - [0xAAAAu16, 0xAAAAu16].as_slice() + [0xAA, 0xAA, 0xAA, 0xAA].as_slice() )) ); assert_eq!(s.next(), None); @@ -258,11 +278,8 @@ mod tests { height: 2, }, }; - let area_s = AreaSerializer::with_buffer_size( - area.intersection(&BOUNDING_BOX_DEFAULT), - Gray4::new(0xA), - 1, - ); + let area_s = + AreaSerializer::new(area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA), 2); let mut s = AreaSerializerIterator::new(&area_s); assert_eq!( s.next(), @@ -273,7 +290,7 @@ mod tests { area_w: 4, area_h: 1 }, - [0xAAAAu16].as_slice() + [0xAA, 0xAA].as_slice() )) ); assert_eq!( @@ -285,7 +302,7 @@ mod tests { area_w: 4, area_h: 1 }, - [0xAAAAu16].as_slice() + [0xAA, 0xAA].as_slice() )) ); assert_eq!(s.next(), None); @@ -301,11 +318,8 @@ mod tests { height: 2, }, }; - let area_s = AreaSerializer::with_buffer_size( - area.intersection(&BOUNDING_BOX_DEFAULT), - Gray4::new(0xA), - 2, - ); + let area_s = + AreaSerializer::new(area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA), 4); let mut s = AreaSerializerIterator::new(&area_s); assert_eq!( s.next(), @@ -316,7 +330,7 @@ mod tests { area_w: 3, area_h: 1 }, - [0xAAAAu16, 0xAAAAu16].as_slice() + [0xAA, 0xAA, 0xAA, 0xAA].as_slice() )) ); assert_eq!( @@ -328,7 +342,7 @@ mod tests { area_w: 3, area_h: 1 }, - [0xAAAAu16, 0xAAAAu16].as_slice() + [0xAA, 0xAA, 0xAA, 0xAA].as_slice() )) ); assert_eq!(s.next(), None); @@ -344,7 +358,11 @@ mod tests { height: 2, }, }; - let area_s = AreaSerializer::new(area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA)); + let area_s = AreaSerializer::new( + area.intersection(&BOUNDING_BOX_DEFAULT), + Gray4::new(0xA), + 1024, + ); let mut s = AreaSerializerIterator::new(&area_s); assert_eq!( s.next(), @@ -355,7 +373,7 @@ mod tests { area_w: 4, area_h: 2 }, - [0xAAAAu16, 0xAAAAu16].as_slice() + [0xAA, 0xAA, 0xAA, 0xAA].as_slice() )) ); assert_eq!(s.next(), None); @@ -371,7 +389,11 @@ mod tests { height: 2, }, }; - let area_s = AreaSerializer::new(area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA)); + let area_s = AreaSerializer::new( + area.intersection(&BOUNDING_BOX_DEFAULT), + Gray4::new(0xA), + 1024, + ); let mut s = AreaSerializerIterator::new(&area_s); assert_eq!( s.next(), @@ -382,7 +404,7 @@ mod tests { area_w: 3, area_h: 2 }, - [0xAAAAu16, 0xAAAAu16, 0xAAAAu16, 0xAAAAu16].as_slice() + [0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA].as_slice() )) ); assert_eq!(s.next(), None); @@ -398,7 +420,11 @@ mod tests { height: 2, }, }; - let area_s = AreaSerializer::new(area.intersection(&BOUNDING_BOX_DEFAULT), Gray4::new(0xA)); + let area_s = AreaSerializer::new( + area.intersection(&BOUNDING_BOX_DEFAULT), + Gray4::new(0xA), + 1024, + ); let mut s = AreaSerializerIterator::new(&area_s); assert_eq!( s.next(), @@ -409,7 +435,7 @@ mod tests { area_w: 2, area_h: 1 }, - [0xAAAAu16].as_slice() + [0xAA, 0xAA].as_slice() )) ); assert_eq!(s.next(), None); diff --git a/src/interface.rs b/src/interface.rs index 25cc0cd..001efaa 100644 --- a/src/interface.rs +++ b/src/interface.rs @@ -1,8 +1,9 @@ //! Contains the controller interface use embedded_hal::{ + delay::*, digital::{InputPin, OutputPin}, - {delay::*, spi::SpiDevice}, + spi::{Operation, SpiDevice}, }; /// Interface Error @@ -14,19 +15,26 @@ pub enum Error { GPIOError, /// The display busy check timed out BusyTimeout, + /// Buffer alignment incorrect + BufferAlignment, } /// Trait to describe the interface with the controller /// The controller supports different hardware interfaces like i2c, usb, spi and i80 pub trait IT8951Interface { + /// set wait timeout + /// internally used by the library + fn set_busy_timeout(&mut self, timeout: core::time::Duration); + /// active wait while the controller is busy and no new transactions should be issued fn wait_while_busy(&mut self) -> Result<(), Error>; /// write a 16bit value to the controller fn write_data(&mut self, data: u16) -> Result<(), Error>; - /// write mutliple 16bit values to the controller - fn write_multi_data(&mut self, data: &[u16]) -> Result<(), Error>; + /// write multiple 16bit values to the controller + /// data must be aligned to u16! + fn write_multi_data(&mut self, data: &[u8]) -> Result<(), Error>; /// issue a command on the controller fn write_command(&mut self, cmd: u16) -> Result<(), Error>; @@ -60,6 +68,7 @@ pub struct IT8951SPIInterface { busy: BUSY, rst: RST, delay: DELAY, + timeout: core::time::Duration, } impl IT8951SPIInterface @@ -81,6 +90,7 @@ where busy, rst, delay, + timeout: core::time::Duration::default(), } } } @@ -92,6 +102,10 @@ where RST: OutputPin, DELAY: DelayNs, { + fn set_busy_timeout(&mut self, timeout: core::time::Duration) { + self.timeout = timeout + } + fn wait_while_busy(&mut self) -> Result<(), Error> { let mut counter = 0u64; while self.busy.is_low().map_err(|_| Error::GPIOError)? { @@ -119,19 +133,18 @@ where Ok(()) } - fn write_multi_data(&mut self, data: &[u16]) -> Result<(), Error> { + fn write_multi_data(&mut self, data: &[u8]) -> Result<(), Error> { self.wait_while_busy()?; - // Write Data: - // 0x0000 -> Prefix for a Data Write - let mut buf = vec![0u8; data.len()*2 + 2 /*write data prefix */]; - - for index in 0..data.len() { - buf[index * 2 + 2] = (data[index] >> 8) as u8; - buf[index * 2 + 2 + 1] = data[index] as u8; - } + if data.len() % 2 > 0 { + return Err(Error::BufferAlignment); + }; - if self.spi.write(&buf).is_err() { + if self + .spi + .transaction(&mut [Operation::Write(&[0x00, 0x00]), Operation::Write(data)]) + .is_err() + { return Err(Error::SpiError); } diff --git a/src/lib.rs b/src/lib.rs index 95159a2..5f29282 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,6 +37,29 @@ impl From for Error { } } +/// Driver configuration +pub struct Config { + /// Timeout for the internal display engine + pub timeout_display_engine: core::time::Duration, + /// Timeout for the busy pin + pub timeout_interface: core::time::Duration, + /// Max buffer size in bytes for staging buffers + /// The buffer should be large enough to at least contain the pixels of a complete row + /// The buffer must be aligned to u16 + /// The used IT8951 interface must support to write a complete buffer at once + pub max_buffer_size: usize, +} + +impl Default for Config { + fn default() -> Self { + Self { + timeout_display_engine: core::time::Duration::from_secs(15), + timeout_interface: core::time::Duration::from_secs(15), + max_buffer_size: 1024, + } + } +} + /// Device Info Struct /// Describes the connected display #[derive(Debug, Clone)] @@ -103,21 +126,25 @@ pub struct IT8951 { interface: IT8951Interface, dev_info: Option, marker: core::marker::PhantomData, + config: Config, } impl IT8951 { /// Creates a new controller driver object /// Call init afterwards to initalize the controller - pub fn new(interface: IT8951Interface) -> Self { + pub fn new(mut interface: IT8951Interface, config: Config) -> Self { + interface.set_busy_timeout(config.timeout_interface); IT8951 { interface, dev_info: None, marker: PhantomData {}, + config, } } /// Initalize the driver and resets the display /// VCOM should be given on your display + /// Since version 0.4.0, this function no longer resets the display pub fn init(mut self, vcom: u16) -> Result, Error> { self.interface.reset()?; @@ -125,6 +152,7 @@ impl IT8951 { interface: self.interface, dev_info: self.dev_info, marker: PhantomData {}, + config: self.config, } .sys_run()?; @@ -139,19 +167,22 @@ impl IT8951 { it8951.dev_info = Some(dev_info); - it8951.reset()?; - Ok(it8951) } /// Create a new Driver for are already active and initalized driver /// This can be usefull if the device was still powered on, but the uC restarts. - /// VCOM should be given on your display - pub fn attach(interface: IT8951Interface) -> Result, Error> { + pub fn attach( + mut interface: IT8951Interface, + config: Config, + ) -> Result, Error> { + interface.set_busy_timeout(config.timeout_interface); + let mut it8951 = IT8951 { interface, dev_info: None, marker: PhantomData {}, + config, } .sys_run()?; @@ -176,53 +207,22 @@ impl IT8951 { /// initalize the frame buffer and clear the display to white pub fn reset(&mut self) -> Result<(), Error> { - self.clear_frame_buffer(0xF)?; + self.clear(Gray4::WHITE)?; self.display(WaveformMode::Init)?; Ok(()) } - /// set all pixel of the frame buffer to the value of raw_color - /// raw color must be in range 0..16 - fn clear_frame_buffer(&mut self, raw_color: u16) -> Result<(), Error> { - let dev_info = self.get_dev_info(); - let width = dev_info.panel_width; - let height = dev_info.panel_height; - let mem_addr = dev_info.memory_address; - - let data_entry = raw_color << 12 | raw_color << 8 | raw_color << 4 | raw_color; - - // we need to split the data in multiple transfers to keep the buffer size small - for w in 0..height { - self.load_image_area( - mem_addr, - MemoryConverterSetting { - endianness: memory_converter_settings::MemoryConverterEndianness::LittleEndian, - bit_per_pixel: - memory_converter_settings::MemoryConverterBitPerPixel::BitsPerPixel4, - rotation: memory_converter_settings::MemoryConverterRotation::Rotate0, - }, - &AreaImgInfo { - area_x: 0, - area_y: w, - area_w: width, - area_h: 1, - }, - &vec![data_entry; width as usize / 4], - )?; - } - Ok(()) - } - // load image functions ------------------------------------------------------------------------------------------ /// Loads a full frame into the controller frame buffer using the pixel preprocessor /// Warning: For the most usecases, the underlying spi transfer ist not capable to transfer a complete frame /// split the frame into multiple areas and use load_image_area instead + /// Data must be aligned to u16! pub fn load_image( &mut self, target_mem_addr: u32, image_settings: MemoryConverterSetting, - data: &[u16], + data: &[u8], ) -> Result<(), Error> { self.set_target_memory_addr(target_mem_addr)?; @@ -245,7 +245,7 @@ impl IT8951 { target_mem_addr: u32, image_settings: MemoryConverterSetting, area_info: &AreaImgInfo, - data: &[u16], + data: &[u8], ) -> Result<(), Error> { self.set_target_memory_addr(target_mem_addr)?; @@ -302,7 +302,8 @@ impl IT8951 { } /// Writes a buffer of u16 values to the given memory address in the controller ram - pub fn memory_burst_write(&mut self, memory_address: u32, data: &[u16]) -> Result<(), Error> { + /// Buffer needs to be aligned to u16! + pub fn memory_burst_write(&mut self, memory_address: u32, data: &[u8]) -> Result<(), Error> { let args = [ memory_address as u16, (memory_address >> 16) as u16, @@ -390,9 +391,10 @@ impl IT8951 { // misc ------------------------------------------------------------------------------------------------ fn wait_for_display_ready(&mut self) -> Result<(), Error> { + let timeout = self.config.timeout_display_engine.as_micros() as u64; let mut counter = 0u64; while 0 != self.read_register(register::LUTAFSR)? { - if counter > 10_000_000u64 { + if counter > timeout { return Err(Error::DisplayEngineTimeout); } counter += 1; @@ -409,6 +411,7 @@ impl IT8951 { interface: self.interface, dev_info: self.dev_info, marker: PhantomData {}, + config: self.config, }) } @@ -420,6 +423,7 @@ impl IT8951 { interface: self.interface, dev_info: self.dev_info, marker: PhantomData {}, + config: self.config, }) } @@ -494,6 +498,7 @@ impl IT8951 DrawTarget for IT8951 Result<(), Self::Error> { - let raw_color = color.luma() as u16; - self.clear_frame_buffer(raw_color) + let info = self.get_dev_info(); + + self.fill_solid( + &Rectangle::new( + Point::zero(), + Size { + width: info.panel_width as u32, + height: info.panel_height as u32, + }, + ), + color, + ) } fn fill_solid(&mut self, area: &Rectangle, color: Self::Color) -> Result<(), Self::Error> { - let a = AreaSerializer::new(*area, color); + let a = AreaSerializer::new(*area, color, self.config.max_buffer_size); let area_iter = AreaSerializerIterator::new(&a); for (area_img_info, buffer) in area_iter { @@ -534,7 +549,11 @@ impl DrawTarget for IT8951 DrawTarget for IT8951= 0 && coord.x < width) || (coord.y >= 0 || coord.y < height) { - let data: u16 = (color.luma() as u16) << ((coord.x % 4) * 4); + let mut data = [0x00, 0x00]; + + let value: u8 = color.luma() << ((coord.x % 2) * 4); + // little endian layout + // [P3, P2 | P1, P0] + if coord.x % 4 > 1 { + // pixel 2 and 3 + data[0] = value; + } else { + // pixel 0 and 1 + data[1] = value; + } self.load_image_area( dev_info.memory_address, @@ -568,7 +598,7 @@ impl DrawTarget for IT8951>> { } impl>> PixelSerializer { - pub fn new(area: Rectangle, pixels: I) -> Self { + pub fn new(area: Rectangle, pixels: I, size: usize) -> Self { PixelSerializer { area, pixels, row: 0, - // 512 * 2 Bytes = 1kByte - max_entries: 512, - } - } - // max buffer size in 16bit words - // TODO make variable buffer size available via public api - #[allow(unused)] - pub fn with_buffer_max_words(self, size: usize) -> Self { - Self { + // 1kByte max_entries: size, - ..self } } } impl>> Iterator for PixelSerializer { - type Item = (AreaImgInfo, Vec); + type Item = (AreaImgInfo, Vec); fn next(&mut self) -> Option { if self.row >= self.area.size.height as usize { @@ -47,19 +40,25 @@ impl>> Iterator for PixelSerializer { let start_row = self.row; // prepare buffer with enough capacity - let entries_per_row = get_entires_per_row(self.area) as usize; + let entries_per_row = get_entires_per_row(self.area) as usize * 2; // convert length to bytes let max_rows = (self.max_entries / entries_per_row).min(self.area.size.height as usize); assert!(max_rows > 0, "Buffer size to small for one row"); - //let mut bytes = Vec::with_capacity(entries_per_row * max_rows); - let mut bytes = vec![0x0000; entries_per_row * max_rows]; + let mut bytes = vec![0x00; entries_per_row * max_rows]; // add all pixels to buffer for Pixel(point, color) in self.pixels.by_ref() { - let byte_pos = ((point.x - (self.area.top_left.x / 4 * 4)) / 4) as usize + // calculate the which u16 (pair of two bytes) the pixel is in + let u16_pos = ((point.x - (self.area.top_left.x / 4 * 4)) / 2) as usize + entries_per_row * (self.row - start_row); - let bit_pos = (point.x % 4) * 4; - bytes[byte_pos] |= (color.luma() as u16) << bit_pos; + // swap last pixel to map little endian behavior + let byte_pos = u16_pos.bitxor(0x00001); + + // little endian layout + // [P3, P2 | P1, P0] + let bit_pos = (point.x % 2) * 4; + + bytes[byte_pos] |= (color.luma()) << bit_pos; // end of row if point.x >= self.area.top_left.x + self.area.size.width as i32 - 1 { @@ -127,6 +126,7 @@ mod tests { BOUNDING_BOX_DEFAULT, vec![Gray4::new(0xF)].into_iter(), ), + 1024, ); assert_eq!( s.next(), @@ -137,7 +137,7 @@ mod tests { area_w: 1, area_h: 1 }, - vec![0x000F] + vec![0x00, 0x0F] )) ); assert_eq!(s.next(), None); @@ -160,6 +160,7 @@ mod tests { BOUNDING_BOX_DEFAULT, vec![Gray4::new(0x1)].into_iter(), ), + 1024, ); assert_eq!( s.next(), @@ -170,7 +171,7 @@ mod tests { area_w: 1, area_h: 1 }, - vec![0x0010] + vec![0x00, 0x10] )) ); assert_eq!(s.next(), None); @@ -192,6 +193,7 @@ mod tests { BOUNDING_BOX_DEFAULT, vec![Gray4::new(0x4)].into_iter(), ), + 1024, ); assert_eq!( s.next(), @@ -202,7 +204,7 @@ mod tests { area_w: 1, area_h: 1 }, - vec![0x0400] + vec![0x04, 0x00] )) ); assert_eq!(s.next(), None); @@ -224,6 +226,7 @@ mod tests { BOUNDING_BOX_DEFAULT, vec![Gray4::new(0xC)].into_iter(), ), + 1024, ); assert_eq!( s.next(), @@ -234,7 +237,7 @@ mod tests { area_w: 1, area_h: 1 }, - vec![0xC000] + vec![0xC0, 0x00] )) ); assert_eq!(s.next(), None); @@ -263,6 +266,7 @@ mod tests { ] .into_iter(), ), + 1024, ); assert_eq!( s.next(), @@ -273,7 +277,7 @@ mod tests { area_w: 4, area_h: 1 }, - vec![0xDCBA] + vec![0xDC, 0xBA] )) ); assert_eq!(s.next(), None); @@ -296,6 +300,7 @@ mod tests { BOUNDING_BOX_DEFAULT, vec![Gray4::new(0xC), Gray4::new(0xD), Gray4::new(0xE)].into_iter(), ), + 1024, ); assert_eq!( s.next(), @@ -306,7 +311,7 @@ mod tests { area_w: 3, area_h: 1 }, - vec![0xC000, 0x00ED] + vec![0xC0, 0x00, 0x00, 0xED] )) ); assert_eq!(s.next(), None); @@ -339,8 +344,8 @@ mod tests { ] .into_iter(), ), - ) - .with_buffer_max_words(1); + 2, + ); assert_eq!( s.next(), Some(( @@ -350,7 +355,7 @@ mod tests { area_w: 4, area_h: 1 }, - vec![0xDCBA] + vec![0xDC, 0xBA] )) ); assert_eq!( @@ -362,7 +367,7 @@ mod tests { area_w: 4, area_h: 1 }, - vec![0x4321] + vec![0x43, 0x21] )) ); assert_eq!(s.next(), None); @@ -393,8 +398,8 @@ mod tests { ] .into_iter(), ), - ) - .with_buffer_max_words(2); + 4, + ); assert_eq!( s.next(), Some(( @@ -404,7 +409,7 @@ mod tests { area_w: 3, area_h: 1 }, - vec![0xC000, 0x00ED] + vec![0xC0, 0x00, 0x00, 0xED] )) ); assert_eq!( @@ -416,7 +421,7 @@ mod tests { area_w: 3, area_h: 1 }, - vec![0x1000, 0x0032] + vec![0x10, 0x00, 0x00, 0x32] )) ); assert_eq!(s.next(), None); @@ -449,6 +454,7 @@ mod tests { ] .into_iter(), ), + 1024, ); assert_eq!( s.next(), @@ -459,7 +465,7 @@ mod tests { area_w: 4, area_h: 2 }, - vec![0xDCBA, 0x4321] + vec![0xDC, 0xBA, 0x43, 0x21] )) ); assert_eq!(s.next(), None); @@ -490,6 +496,7 @@ mod tests { ] .into_iter(), ), + 1024, ); assert_eq!( s.next(), @@ -500,7 +507,7 @@ mod tests { area_w: 3, area_h: 2 }, - vec![0xC000, 0x00ED, 0x1000, 0x0032] + vec![0xC0, 0x00, 0x00, 0xED, 0x10, 0x00, 0x00, 0x32] )) ); assert_eq!(s.next(), None); @@ -531,6 +538,7 @@ mod tests { ] .into_iter(), ), + 1024, ); assert_eq!( s.next(), @@ -541,7 +549,7 @@ mod tests { area_w: 2, area_h: 1 }, - vec![0x0032] + vec![0x00, 0x32] )) ); assert_eq!(s.next(), None);