Skip to content

Commit

Permalink
Settle on the pixel type
Browse files Browse the repository at this point in the history
  • Loading branch information
not-avail committed Jun 27, 2018
1 parent c38a38e commit 61e0881
Show file tree
Hide file tree
Showing 10 changed files with 163 additions and 89 deletions.
Binary file added assets/colorspace.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed assets/rustlang.bmp
Binary file not shown.
Binary file added assets/rustlang.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
132 changes: 96 additions & 36 deletions examples/demo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use libremarkable::battery;
use libremarkable::input::{gpio, multitouch, wacom, InputDevice};

use libremarkable::image;
use libremarkable::image::GenericImage;
use libremarkable::image::{GenericImage, ImageBuffer};

use std::process::Command;

Expand Down Expand Up @@ -267,7 +267,11 @@ fn on_save_canvas(app: &mut appctx::ApplicationContext, _element: UIElementHandl
Err(err) => println!("Failed to dump buffer: {0}", err),
Ok(buff) => {
let mut hist = SAVED_CANVAS.lock().unwrap();
*hist = Some(storage::CompressedCanvasState::new(buff));
*hist = Some(storage::CompressedCanvasState::new(
buff.as_slice(),
CANVAS_REGION.height,
CANVAS_REGION.width,
));
}
};
end_bench!(save_canvas);
Expand All @@ -279,7 +283,10 @@ fn on_zoom_out(app: &mut appctx::ApplicationContext, _element: UIElementHandle)
match framebuffer.dump_region(CANVAS_REGION) {
Err(err) => println!("Failed to dump buffer: {0}", err),
Ok(buff) => {
let resized = image::DynamicImage::ImageLumaA8(buff).resize(
let resized = image::DynamicImage::ImageRgb8(
from_rgb565_to_rgb8(CANVAS_REGION.width, CANVAS_REGION.height, buff.as_slice())
.unwrap(),
).resize(
(CANVAS_REGION.width as f32 / 1.25f32) as u32,
(CANVAS_REGION.height as f32 / 1.25f32) as u32,
image::imageops::Nearest,
Expand All @@ -291,51 +298,66 @@ fn on_zoom_out(app: &mut appctx::ApplicationContext, _element: UIElementHandle)
new_image.invert();

// Copy the resized image into the subimage
new_image = image::DynamicImage::ImageLumaA8(new_image.to_luma_alpha());
new_image.copy_from(&resized, CANVAS_REGION.width / 8, CANVAS_REGION.height / 8);

// Restore the new_image but as luma_alpha8.
match framebuffer.restore_region(CANVAS_REGION, &new_image.as_luma_alpha8().unwrap()) {
Err(e) => println!("Error while restoring region: {0}", e),
Ok(_) => {
framebuffer.partial_refresh(
&CANVAS_REGION,
PartialRefreshMode::Async,
waveform_mode::WAVEFORM_MODE_GC16_FAST,
display_temp::TEMP_USE_REMARKABLE_DRAW,
dither_mode::EPDC_FLAG_USE_DITHERING_PASSTHROUGH,
0,
false,
);
}
};
framebuffer.draw_image(
&new_image.as_rgb8().unwrap(),
CANVAS_REGION.top as usize,
CANVAS_REGION.left as usize,
);
framebuffer.partial_refresh(
&CANVAS_REGION,
PartialRefreshMode::Async,
waveform_mode::WAVEFORM_MODE_GC16_FAST,
display_temp::TEMP_USE_REMARKABLE_DRAW,
dither_mode::EPDC_FLAG_USE_DITHERING_PASSTHROUGH,
0,
false,
);
}
};
end_bench!(zoom_out);
}

fn from_rgb565_to_rgb8(w: u32, h: u32, buff: &[u8]) -> Option<image::RgbImage> {
// rgb565 is the input so it is 16bits (2 bytes) per pixel
let input_bytespp = 2;
let input_line_len = w * input_bytespp;
if h * input_line_len != buff.len() as u32 {
return None;
}
Some(ImageBuffer::from_fn(w, h, |x, y| {
let in_index: usize = ((y * input_line_len) + ((input_bytespp * x) as u32)) as usize;
let data = color::NATIVE_COMPONENTS(buff[in_index], buff[in_index + 1]).to_rgb8();
image::Rgb(data)
}))
}

fn on_blur_canvas(app: &mut appctx::ApplicationContext, _element: UIElementHandle) {
start_bench!(stopwatch, blur_canvas);
let framebuffer = app.get_framebuffer_ref();
match framebuffer.dump_region(CANVAS_REGION) {
Err(err) => println!("Failed to dump buffer: {0}", err),
Ok(buff) => {
let mut dynamic = image::DynamicImage::ImageLumaA8(buff);
dynamic = dynamic.blur(0.6f32);
match framebuffer.restore_region(CANVAS_REGION, &dynamic.as_luma_alpha8().unwrap()) {
Err(e) => println!("Error while restoring region: {0}", e),
Ok(_) => {
framebuffer.partial_refresh(
&CANVAS_REGION,
PartialRefreshMode::Async,
waveform_mode::WAVEFORM_MODE_GC16_FAST,
display_temp::TEMP_USE_REMARKABLE_DRAW,
dither_mode::EPDC_FLAG_USE_DITHERING_PASSTHROUGH,
0,
false,
);
}
};
let dynamic = image::DynamicImage::ImageRgb8(
from_rgb565_to_rgb8(CANVAS_REGION.width, CANVAS_REGION.height, buff.as_slice())
.unwrap(),
).blur(0.6f32);

framebuffer.draw_image(
&dynamic.as_rgb8().unwrap(),
CANVAS_REGION.top as usize,
CANVAS_REGION.left as usize,
);
framebuffer.partial_refresh(
&CANVAS_REGION,
PartialRefreshMode::Async,
waveform_mode::WAVEFORM_MODE_GC16_FAST,
display_temp::TEMP_USE_REMARKABLE_DRAW,
dither_mode::EPDC_FLAG_USE_DITHERING_PASSTHROUGH,
0,
false,
);
}
};
end_bench!(blur_canvas);
Expand Down Expand Up @@ -567,7 +589,7 @@ fn main() {
*/
onclick: Some(on_touch_rustlogo),
inner: UIElement::Image {
img: image::load_from_memory(include_bytes!("../assets/rustlang.bmp")).unwrap(),
img: image::load_from_memory(include_bytes!("../assets/rustlang.png")).unwrap(),
},
..Default::default()
},
Expand All @@ -591,6 +613,24 @@ fn main() {
},
);

app.add_element(
"colortest-rgb",
UIElementWrapper {
y: 210,
x: 450,
refresh: UIConstraintRefresh::Refresh,

onclick: Some(draw_color_test_rgb),
inner: UIElement::Text {
foreground: color::BLACK,
text: "Show RGB Color Test Image".to_owned(),
scale: 35,
border_px: 3,
},
..Default::default()
},
);

// Zoom Out Button
app.add_element(
"zoomoutButton",
Expand Down Expand Up @@ -1010,3 +1050,23 @@ fn main() {
app.dispatch_events(true, true, true);
clock_thread.join().unwrap();
}

fn draw_color_test_rgb(app: &mut appctx::ApplicationContext, _element: UIElementHandle) {
let fb = app.get_framebuffer_ref();

let img_rgb565 = image::load_from_memory(include_bytes!("../assets/colorspace.png")).unwrap();
fb.draw_image(
&img_rgb565.as_rgb8().unwrap(),
CANVAS_REGION.top as usize,
CANVAS_REGION.left as usize,
);
fb.partial_refresh(
&CANVAS_REGION,
PartialRefreshMode::Wait,
waveform_mode::WAVEFORM_MODE_GC16,
display_temp::TEMP_USE_PAPYRUS,
dither_mode::EPDC_FLAG_USE_DITHERING_PASSTHROUGH,
0,
false,
);
}
5 changes: 4 additions & 1 deletion src/appctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,10 @@ impl<'a> ApplicationContext<'a> {
refresh: UIConstraintRefresh,
) -> mxcfb_rect {
let framebuffer = self.get_framebuffer_ref();
let draw_area = framebuffer.draw_grayscale_image(&img, y, x);
let draw_area = match img {
image::DynamicImage::ImageRgb8(ref rgb) => framebuffer.draw_image(rgb, y, x),
other => framebuffer.draw_image(&other.to_rgb(), y, x),
};
let marker = match refresh {
UIConstraintRefresh::Refresh | UIConstraintRefresh::RefreshAndWait => framebuffer
.partial_refresh(
Expand Down
42 changes: 28 additions & 14 deletions src/framebuffer/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ pub enum color {
WHITE,
NATIVE_COMPONENTS(u8, u8),
RGB(u8, u8, u8),

/// 0-255 -- 0 will yield black and 255 will yield white
GRAY(u8),
}
Expand All @@ -57,8 +56,25 @@ impl color {
color::NATIVE_COMPONENTS(c[0], c[1])
}

pub fn to_rgb565(&self) -> [u8; 2] {
self.as_native()
}

pub fn to_rgb8(&self) -> [u8; 3] {
// Components reversed because of the device
let components = self.as_native();

let mut combined: u16 = (components[1] as u16) << 8;
combined = combined | (components[0] as u16);

let red = (((combined & 0b1111_1000_0000_0000) >> 11) << 3) as u8;
let green = (((combined & 0b0000_0111_1110_0000) >> 5) << 2) as u8;
let blue = ((combined & 0b0000_0000_0001_1111) << 3) as u8;

[red, green, blue]
}

pub fn as_native(&self) -> [u8; 2] {
// No need to over-optimize here and return a reference because 4 x u8 (1byte) = 4bytes
match self {
&color::BLACK => [0x00, 0x00],
&color::RED => [0xF8, 0x00],
Expand All @@ -67,23 +83,21 @@ impl color {
&color::WHITE => [0xFF, 0xFF],
&color::GRAY(level) => [level, level],
&color::NATIVE_COMPONENTS(c1, c2) => [c1, c2],
&color::RGB(r, g, b) => {
// Simply can be referred to as `rgb565_le`. Due to the nature of the display it is a LumaA8.
//
// u8 + u8 for lumaA8 ==> yields 16bits per pixel:
&color::RGB(r8, g8, b8) => {
// Simply can be referred to as `rgb565_le`
//
// red : offset = 11, length =5, msb_right = 0
// green : offset = 5, length =6, msb_right = 0
// blue : offset = 0, length =5, msb_right = 0
//
let mut red_comp: u16 = r as u16;
red_comp = ((red_comp & 0b11111) << 11).into();

let mut out: u16 = red_comp | ((g & 0b111111) << 5) as u16 | (b & 0b11111) as u16;

let ms: u8 = (out & 0xFF00) as u8;
let ls: u8 = (out & 0x00FF) as u8;
[ms, ls]
let r5 = ((r8 as u16) >> 3) as u8;
let g6 = ((g8 as u16) >> 2) as u8;
let b5 = ((b8 as u16) >> 3) as u8;

[
(((g6 & 0b000111) << 5) | b5),
((r5 << 3) | ((g6 & 0b111000) >> 3)),
]
}
}
}
Expand Down
9 changes: 4 additions & 5 deletions src/framebuffer/draw.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std;

use image::DynamicImage;
use image::GenericImage;
use image::RgbImage;
use libc;
use line_drawing;
use rusttype::{point, Scale};
Expand Down Expand Up @@ -46,12 +45,12 @@ fn sample_bezier(startpt: (f32, f32), ctrlpt: (f32, f32), endpt: (f32, f32)) ->
}

impl<'a> framebuffer::FramebufferDraw for core::Framebuffer<'a> {
fn draw_grayscale_image(&mut self, img: &DynamicImage, top: usize, left: usize) -> mxcfb_rect {
for (x, y, pixel) in img.to_luma().enumerate_pixels() {
fn draw_image(&mut self, img: &RgbImage, top: usize, left: usize) -> mxcfb_rect {
for (x, y, pixel) in img.enumerate_pixels() {
self.write_pixel(
top + y as usize,
left + x as usize,
color::GRAY(pixel.data[0]),
color::RGB(pixel.data[0], pixel.data[1], pixel.data[2]),
);
}
return mxcfb_rect {
Expand Down
31 changes: 19 additions & 12 deletions src/framebuffer/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
use framebuffer;
use framebuffer::common;

use image::{GrayAlphaImage, ImageBuffer};

impl<'a> framebuffer::FramebufferIO for framebuffer::core::Framebuffer<'a> {
fn write_frame(&mut self, frame: &[u8]) {
unsafe {
Expand Down Expand Up @@ -60,7 +58,7 @@ impl<'a> framebuffer::FramebufferIO for framebuffer::core::Framebuffer<'a> {
}
}

fn dump_region(&self, rect: common::mxcfb_rect) -> Result<GrayAlphaImage, &'static str> {
fn dump_region(&self, rect: common::mxcfb_rect) -> Result<Vec<u8>, &'static str> {
if rect.width == 0 || rect.height == 0 {
return Err("Unable to dump a region with zero height/width");
}
Expand Down Expand Up @@ -92,13 +90,14 @@ impl<'a> framebuffer::FramebufferIO for framebuffer::core::Framebuffer<'a> {
unsafe {
outbuffer.set_len(written);
}
return Ok(ImageBuffer::from_raw(rect.width, rect.height, outbuffer).unwrap());

return Ok(outbuffer);
}

fn restore_region(
&mut self,
rect: common::mxcfb_rect,
data: &GrayAlphaImage,
data: &[u8],
) -> Result<u32, &'static str> {
if rect.width == 0 || rect.height == 0 {
return Err("Unable to restore a region with zero height/width");
Expand All @@ -110,16 +109,24 @@ impl<'a> framebuffer::FramebufferIO for framebuffer::core::Framebuffer<'a> {
return Err("Horizontally out of bounds");
}

let bytespp = (self.var_screen_info.bits_per_pixel / 8) as usize;
if data.len() as u32 != rect.width * rect.height * bytespp as u32 {
return Err("Cannot restore region due to mismatched size");
}

let line_length = self.fix_screen_info.line_length as u32;
let chunk_size = bytespp * rect.width as usize;
let outbuffer = self.frame.data();
let inbuffer = data.as_ptr();
let mut written: u32 = 0;
for y in 0..rect.height {
for x in 0..rect.width {
self.write_pixel(
(rect.top + y) as usize,
(rect.left + x) as usize,
common::color::from_native(data.get_pixel(x, y).data),
);
written += 1;
let curr_index = (y + rect.top) * line_length + (bytespp * rect.left as usize) as u32;
unsafe {
outbuffer
.add(curr_index as usize)
.copy_from(inbuffer.add(written as usize), chunk_size);
}
written += chunk_size as u32;
}
return Ok(written);
}
Expand Down
17 changes: 7 additions & 10 deletions src/framebuffer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,22 @@ pub trait FramebufferIO {
fn read_pixel(&self, y: usize, x: usize) -> common::color;
/// Reads the value at offset `ofst` from the mmapp'ed framebuffer region
fn read_offset(&self, ofst: isize) -> u8;
/// Dumps the contents of the specified rectangle into an `image::ImageBuffer<image::LumaA<u8>, Vec<u8>>`
fn dump_region(&self, rect: common::mxcfb_rect) -> Result<image::GrayAlphaImage, &'static str>;
/// Restores into the framebuffer the contents of the specified rectangle from an `image::ImageBuffer<image::LumaA<u8>, Vec<u8>>`
/// Dumps the contents of the specified rectangle into a `Vec<u8>` from which
/// you can later create a CompressedCanvasState or pass to restore_region().
/// The pixel format is rgb565_le.
fn dump_region(&self, rect: common::mxcfb_rect) -> Result<Vec<u8>, &'static str>;
/// Restores into the framebuffer the contents of the specified rectangle from a u8 slice
fn restore_region(
&mut self,
rect: common::mxcfb_rect,
data: &image::GrayAlphaImage,
data: &[u8],
) -> Result<u32, &'static str>;
}

pub mod draw;
pub trait FramebufferDraw {
/// Draws `img` at y=top, x=left coordinates with 1:1 scaling
fn draw_grayscale_image(
&mut self,
img: &image::DynamicImage,
top: usize,
left: usize,
) -> common::mxcfb_rect;
fn draw_image(&mut self, img: &image::RgbImage, top: usize, left: usize) -> common::mxcfb_rect;
/// Draws a straight line
fn draw_line(
&mut self,
Expand Down
Loading

0 comments on commit 61e0881

Please sign in to comment.