fixup! feat(core): introduce new drawing library

pull/3644/head
cepetr 3 months ago
parent dfd6dd4b8b
commit 924664aeb3

@ -131,6 +131,31 @@ static inline gdc_color16_t gdc_color16_blend_a4(gdc_color16_t fg,
return (r << 11) | (g << 5) | b;
}
// Blends foreground and background colors with 4-bit alpha
//
// Returns a color in 16-bit format
//
// If alpha is 0, the function returns the background color
// If alpha is 15, the function returns the foreground color
static inline gdc_color16_t gdc_color16_blend_a8(gdc_color16_t fg,
gdc_color16_t bg,
uint8_t alpha) {
uint16_t fg_r = (fg & 0xF800) >> 11;
uint16_t bg_r = (bg & 0xF800) >> 11;
uint16_t r = (fg_r * alpha + (bg_r * (255 - alpha))) / 255;
uint16_t fg_g = (fg & 0x07E0) >> 5;
uint16_t bg_g = (bg & 0x07E0) >> 5;
uint16_t g = (fg_g * alpha + (bg_g * (255 - alpha))) / 255;
uint16_t fg_b = (fg & 0x001F) >> 0;
uint16_t bg_b = (bg & 0x001F) >> 0;
uint16_t b = (fg_b * alpha + (bg_b * (255 - alpha))) / 255;
return (r << 11) | (g << 5) | b;
}
// Blends foreground and background colors with 4-bit alpha
//
// Returns a color in 32-bit format
@ -188,6 +213,31 @@ static inline gdc_color16_t gdc_color16_blend_a4(gdc_color32_t fg,
return gdc_color16_rgb(r, g, b)
}
// Blends foreground and background colors with 8-bit alpha
//
// Returns a color in 16-bit format
//
// If alpha is 0, the function returns the background color
// If alpha is 255, the function returns the foreground color
static inline gdc_color16_t gdc_color16_blend_a8(gdc_color32_t fg,
gdc_color32_t bg,
uint8_t alpha) {
uint16_t fg_r = (fg & 0x00FF0000) >> 16;
uint16_t bg_r = (bg & 0x00FF0000) >> 16;
uint16_t r = (fg_r * alpha + (bg_r * (255 - alpha))) / 255;
uint16_t fg_g = (fg & 0x0000FF00) >> 8;
uint16_t bg_g = (bg & 0x0000FF00) >> 8;
uint16_t g = (fg_g * alpha + (bg_g * (255 - alpha))) / 255;
uint16_t fg_b = (fg & 0x000000FF) >> 0;
uint16_t bg_b = (bg & 0x000000FF) >> 0;
uint16_t b = (fg_b * alpha + (bg_b * (255 - alpha))) / 255;
return gdc_color16_rgb(r, g, b)
}
// Blends foreground and background colors with 4-bit alpha
//
// Returns a color in 32-bit format

@ -75,6 +75,7 @@ bool gdc_fill_rect(gdc_t* gdc, gdc_rect_t rect, gdc_color_t color) {
// Source bitmap
.src_fg = color,
.src_alpha = 255,
};
gdc_wait_for_pending_ops(gdc);
@ -116,6 +117,7 @@ bool gdc_draw_bitmap(gdc_t* gdc, gdc_rect_t rect, const gdc_bitmap_ref_t* src) {
.src_stride = src->bitmap->stride,
.src_fg = src->fg_color,
.src_bg = src->bg_color,
.src_alpha = 255,
};
gdc_wait_for_pending_ops(gdc);
@ -167,6 +169,7 @@ bool gdc_draw_blended(gdc_t* gdc, gdc_rect_t rect,
.src_y = clip.src_y,
.src_stride = src->bitmap->stride,
.src_fg = src->fg_color,
.src_alpha = 255,
};
gdc_wait_for_pending_ops(gdc);

@ -36,14 +36,15 @@ typedef struct {
uint16_t dst_stride;
// Source bitmap
// Used for copying and blending, but src_fg color
// is also used for fill operation
// Used for copying and blending, but src_fg & src_alpha
// fields are also used for fill operation
void* src_row;
uint16_t src_x;
uint16_t src_y;
uint16_t src_stride;
gdc_color_t src_fg;
gdc_color_t src_bg;
uint8_t src_alpha;
} dma2d_params_t;

@ -34,13 +34,23 @@ bool rgb565_fill(const dma2d_params_t* dp) {
uint16_t* dst_ptr = (uint16_t*)dp->dst_row + dp->dst_x;
uint16_t height = dp->height;
while (height-- > 0) {
for (int x = 0; x < dp->width; x++) {
dst_ptr[x] = dp->src_fg;
if (dp->src_alpha == 255) {
while (height-- > 0) {
for (int x = 0; x < dp->width; x++) {
dst_ptr[x] = dp->src_fg;
}
dst_ptr += dp->dst_stride / sizeof(*dst_ptr);
}
}
else {
uint8_t alpha = dp->src_alpha;
while (height-- > 0) {
for (int x = 0; x < dp->width; x++) {
dst_ptr[x] = gdc_color16_blend_a8(dp->src_fg, dst_ptr[x], alpha);
}
dst_ptr += dp->dst_stride / sizeof(*dst_ptr);
}
dst_ptr += dp->dst_stride / sizeof(*dst_ptr);
}
return true;
}
}

@ -214,8 +214,8 @@ impl<'a> Bitmap<'a> {
/// Fills a rectangle with the specified color.
///
/// The function is aplicable only on bitmaps with RGB565 format.
pub fn rgb565_fill(&mut self, r: Rect, clip: Rect, color: Color) {
if let Some(dma2d) = Dma2d::new_fill(r, clip, color) {
pub fn rgb565_fill(&mut self, r: Rect, clip: Rect, color: Color, alpha: u8) {
if let Some(dma2d) = Dma2d::new_fill(r, clip, color, alpha) {
let dma2d = dma2d.with_dst(self);
unsafe {
ffi::rgb565_fill(&dma2d);
@ -266,8 +266,8 @@ impl<'a> Bitmap<'a> {
/// Fills a rectangle with the specified color.
///
/// The function is aplicable only on bitmaps with RGBA888 format.
pub fn rgba8888_fill(&mut self, r: Rect, clip: Rect, color: Color) {
if let Some(dma2d) = Dma2d::new_fill(r, clip, color) {
pub fn rgba8888_fill(&mut self, r: Rect, clip: Rect, color: Color, alpha: u8) {
if let Some(dma2d) = Dma2d::new_fill(r, clip, color, alpha) {
let dma2d = dma2d.with_dst(self);
unsafe {
ffi::rgba8888_fill(&dma2d);
@ -317,8 +317,8 @@ impl<'a> Bitmap<'a> {
/// Fills a rectangle with the specified color.
///
/// The function is aplicable only on bitmaps with RGB565 format.
pub fn mono8_fill(&mut self, r: Rect, clip: Rect, color: Color) {
if let Some(dma2d) = Dma2d::new_fill(r, clip, color) {
pub fn mono8_fill(&mut self, r: Rect, clip: Rect, color: Color, alpha: u8) {
if let Some(dma2d) = Dma2d::new_fill(r, clip, color, alpha) {
let dma2d = dma2d.with_dst(self);
unsafe {
ffi::mono8_fill(&dma2d);
@ -457,10 +457,15 @@ impl<'a> BitmapView<'a> {
pub type Dma2d = ffi::dma2d_params_t;
impl Dma2d {
pub fn new_fill(r: Rect, clip: Rect, color: Color) -> Option<Self> {
pub fn new_fill(r: Rect, clip: Rect, color: Color, alpha: u8) -> Option<Self> {
let r = r.intersect(clip);
if !r.is_empty() {
Some(Self::default().with_rect(r).with_fg(color))
Some(
Self::default()
.with_rect(r)
.with_fg(color)
.with_alpha(alpha),
)
} else {
None
}
@ -532,6 +537,7 @@ impl Dma2d {
src_stride: 0,
src_x: 0,
src_y: 0,
src_alpha: 255,
}
}
@ -573,11 +579,18 @@ impl Dma2d {
..self
}
}
fn with_alpha(self, alpha: u8) -> Self {
Self {
src_alpha: alpha,
..self
}
}
}
impl Dma2d {
pub fn wnd565_fill(r: Rect, clip: Rect, color: Color) {
if let Some(dma2d) = Dma2d::new_fill(r, clip, color) {
if let Some(dma2d) = Dma2d::new_fill(r, clip, color, 255) {
unsafe { ffi::wnd565_fill(&dma2d) };
}
}

@ -58,11 +58,11 @@ pub trait BasicCanvas {
}
/// Draws a filled rectangle with the specified color.
fn fill_rect(&mut self, r: Rect, color: Color);
fn fill_rect(&mut self, r: Rect, color: Color, alpha: u8);
/// Fills the canvas background with the specified color.
fn fill_background(&mut self, color: Color) {
self.fill_rect(self.viewport().clip, color);
self.fill_rect(self.viewport().clip, color, 255);
}
/// Draws a bitmap of bitmap into to the rectangle.
@ -76,10 +76,11 @@ pub trait Canvas: BasicCanvas {
/// Draw a pixel at specified coordinates.
fn draw_pixel(&mut self, pt: Point, color: Color);
/// Draws a single pixel and blends its color with the background
/// If alpha == 255, the (foreground) pixel color is used
/// If 0 < alpha << 255, pixel and backround colors are blended
/// If alpha == 0, the background color is used
/// Draws a single pixel and blends its color with the background.
///
/// - If alpha == 255, the (foreground) pixel color is used.
/// - If 0 < alpha << 255, pixel and backround colors are blended.
/// - If alpha == 0, the background color is used.
fn blend_pixel(&mut self, pt: Point, color: Color, alpha: u8);
/// Blends a bitmap with the canvas background
@ -111,7 +112,7 @@ pub trait Canvas: BasicCanvas {
let pt_l = Point::new(r.x0 + radius - p.u, r.y0 + radius - p.v);
let pt_r = Point::new(r.x1 - radius + p.u - 1, r.y0 + radius - p.v);
if p.v == radius && p.last {
self.fill_rect(Rect::new(pt_l, pt_r.onright().under()), color);
self.fill_rect(Rect::new(pt_l, pt_r.onright().under()), color, 255);
} else {
self.draw_pixel(pt_l, color);
self.draw_pixel(pt_r, color);
@ -126,7 +127,7 @@ pub trait Canvas: BasicCanvas {
};
if self.viewport().contains(b) {
for p in circle_points(radius) {
for p in circle_points(radius).take_while(|p| p.u < p.v) {
let pt_l = Point::new(r.x0 + radius - p.v, r.y0 + radius - p.u);
let pt_r = Point::new(r.x1 - radius + p.v - 1, r.y0 + radius - p.u);
self.draw_pixel(pt_l, color);
@ -142,6 +143,7 @@ pub trait Canvas: BasicCanvas {
y1: r.y1 - radius - 1,
},
color,
255,
);
self.fill_rect(
@ -152,6 +154,7 @@ pub trait Canvas: BasicCanvas {
y1: r.y1 - radius - 1,
},
color,
255,
);
let b = Rect {
@ -161,7 +164,7 @@ pub trait Canvas: BasicCanvas {
};
if self.viewport().contains(b) {
for p in circle_points(radius) {
for p in circle_points(radius).take_while(|p| p.u < p.v) {
let pt_l = Point::new(r.x0 + radius - p.v, r.y1 - radius - 1 + p.u);
let pt_r = Point::new(r.x1 - radius + p.v - 1, r.y1 - radius - 1 + p.u);
self.draw_pixel(pt_l, color);
@ -180,7 +183,7 @@ pub trait Canvas: BasicCanvas {
let pt_r = Point::new(r.x1 - radius + p.u - 1, r.y1 - radius - 1 + p.v);
if p.v == radius && p.last {
self.fill_rect(Rect::new(pt_l, pt_r.onright().under()), color);
self.fill_rect(Rect::new(pt_l, pt_r.onright().under()), color, 255);
} else {
self.draw_pixel(pt_l, color);
self.draw_pixel(pt_r, color);
@ -191,7 +194,7 @@ pub trait Canvas: BasicCanvas {
/// Draws filled rectangle with rounded corners.
#[cfg(not(feature = "ui_antialiasing"))]
fn fill_round_rect(&mut self, r: Rect, radius: i16, color: Color) {
fn fill_round_rect(&mut self, r: Rect, radius: i16, color: Color, alpha: u8) {
let split = unwrap!(circle_points(radius).last()).v;
let b = Rect {
@ -204,7 +207,7 @@ pub trait Canvas: BasicCanvas {
if p.last {
let pt_l = Point::new(r.x0 + radius - p.u, r.y0 + radius - p.v);
let pt_r = Point::new(r.x1 - radius + p.u - 1, r.y0 + radius - p.v);
self.fill_rect(Rect::new(pt_l, pt_r.onright().under()), color);
self.fill_rect(Rect::new(pt_l, pt_r.onright().under()), color, alpha);
}
}
}
@ -216,10 +219,10 @@ pub trait Canvas: BasicCanvas {
};
if self.viewport().contains(b) {
for p in circle_points(radius) {
for p in circle_points(radius).take_while(|p| p.u < p.v) {
let pt_l = Point::new(r.x0 + radius - p.v, r.y0 + radius - p.u);
let pt_r = Point::new(r.x1 - radius + p.v - 1, r.y0 + radius - p.u);
self.fill_rect(Rect::new(pt_l, pt_r.onright().under()), color);
self.fill_rect(Rect::new(pt_l, pt_r.onright().under()), color, alpha);
}
}
@ -231,6 +234,7 @@ pub trait Canvas: BasicCanvas {
y1: r.y1 - radius - 1,
},
color,
alpha,
);
let b = Rect {
@ -240,10 +244,10 @@ pub trait Canvas: BasicCanvas {
};
if self.viewport().contains(b) {
for p in circle_points(radius) {
for p in circle_points(radius).take_while(|p| p.u < p.v) {
let pt_l = Point::new(r.x0 + radius - p.v, r.y1 - radius - 1 + p.u);
let pt_r = Point::new(r.x1 - radius + p.v - 1, r.y1 - radius - 1 + p.u);
self.fill_rect(Rect::new(pt_l, pt_r.onright().under()), color);
self.fill_rect(Rect::new(pt_l, pt_r.onright().under()), color, alpha);
}
}
@ -257,7 +261,7 @@ pub trait Canvas: BasicCanvas {
if p.last {
let pt_l = Point::new(r.x0 + radius - p.u, r.y1 - radius - 1 + p.v);
let pt_r = Point::new(r.x1 - radius + p.u - 1, r.y1 - radius - 1 + p.v);
self.fill_rect(Rect::new(pt_l, pt_r.onright().under()), color);
self.fill_rect(Rect::new(pt_l, pt_r.onright().under()), color, alpha);
}
}
}
@ -265,7 +269,7 @@ pub trait Canvas: BasicCanvas {
/// Draws filled rectangle with antialiased rounded corners.
#[cfg(feature = "ui_antialiasing")]
fn fill_round_rect(&mut self, r: Rect, radius: i16, color: Color) {
fn fill_round_rect(&mut self, r: Rect, radius: i16, color: Color, alpha: u8) {
let split = unwrap!(circle_points(radius).last()).v;
let b = Rect {
@ -273,16 +277,18 @@ pub trait Canvas: BasicCanvas {
..r
};
let alpha_mul = |a: u8| -> u8 { ((a as u16 * alpha as u16) / 255) as u8 };
if self.viewport().contains(b) {
for p in circle_points(radius) {
let pt_l = Point::new(r.x0 + radius - p.u, r.y0 + radius - p.v);
let pt_r = Point::new(r.x1 - radius + p.u - 1, r.y0 + radius - p.v);
self.blend_pixel(pt_l, color, p.frac);
self.blend_pixel(pt_r, color, p.frac);
self.blend_pixel(pt_l, color, alpha_mul(p.frac));
self.blend_pixel(pt_r, color, alpha_mul(p.frac));
if p.first {
let inner = Rect::new(pt_l.onright(), pt_r.under());
self.fill_rect(inner, color);
self.fill_rect(inner, color, alpha);
}
}
}
@ -294,14 +300,14 @@ pub trait Canvas: BasicCanvas {
};
if self.viewport().contains(b) {
for p in circle_points(radius) {
for p in circle_points(radius).take_while(|p| p.u < p.v) {
let pt_l = Point::new(r.x0 + radius - p.v, r.y0 + radius - p.u);
let pt_r = Point::new(r.x1 - radius + p.v - 1, r.y0 + radius - p.u);
self.blend_pixel(pt_l, color, p.frac);
self.blend_pixel(pt_r, color, p.frac);
self.blend_pixel(pt_l, color, alpha_mul(p.frac));
self.blend_pixel(pt_r, color, alpha_mul(p.frac));
let inner = Rect::new(pt_l.onright(), pt_r.under());
self.fill_rect(inner, color);
self.fill_rect(inner, color, alpha);
}
}
@ -313,6 +319,7 @@ pub trait Canvas: BasicCanvas {
y1: r.y1 - radius - 1,
},
color,
alpha,
);
let b = Rect {
@ -322,14 +329,14 @@ pub trait Canvas: BasicCanvas {
};
if self.viewport().contains(b) {
for p in circle_points(radius) {
for p in circle_points(radius).take_while(|p| p.u < p.v) {
let pt_l = Point::new(r.x0 + radius - p.v, r.y1 - radius - 1 + p.u);
let pt_r = Point::new(r.x1 - radius + p.v - 1, r.y1 - radius - 1 + p.u);
self.blend_pixel(pt_l, color, p.frac);
self.blend_pixel(pt_r, color, p.frac);
self.blend_pixel(pt_l, color, alpha_mul(p.frac));
self.blend_pixel(pt_r, color, alpha_mul(p.frac));
let b = Rect::new(pt_l.onright(), pt_r.under());
self.fill_rect(b, color);
self.fill_rect(b, color, alpha);
}
}
@ -341,13 +348,13 @@ pub trait Canvas: BasicCanvas {
if self.viewport().contains(b) {
for p in circle_points(radius) {
let pt_l = Point::new(r.x0 + radius - p.u, r.y1 - radius - 1 + p.v);
self.blend_pixel(pt_l, color, p.frac);
self.blend_pixel(pt_l, color, alpha_mul(p.frac));
let pt_r = Point::new(r.x1 - radius + p.u - 1, r.y1 - radius - 1 + p.v);
self.blend_pixel(pt_r, color, p.frac);
self.blend_pixel(pt_r, color, alpha_mul(p.frac));
if p.first {
let b = Rect::new(pt_l.onright(), pt_r.under());
self.fill_rect(b, color);
self.fill_rect(b, color, alpha);
}
}
}
@ -378,7 +385,7 @@ pub trait Canvas: BasicCanvas {
);
if self.viewport().contains(r) {
for p in circle_points(radius) {
for p in circle_points(radius).take_while(|p| p.u < p.v) {
let pt_l = Point::new(center.x - p.v, center.y - p.u);
let pt_r = Point::new(center.x + p.v, center.y - p.u);
self.draw_pixel(pt_l, color);
@ -387,12 +394,12 @@ pub trait Canvas: BasicCanvas {
}
let r = Rect::new(
Point::new(center.x - radius, center.y),
Point::new(center.x - radius, center.y + 1),
Point::new(center.x + radius + 1, center.y + split + 1),
);
if self.viewport().contains(r) {
for p in circle_points(radius) {
for p in circle_points(radius).skip(1).take_while(|p| p.u < p.v) {
let pt_l = Point::new(center.x - p.v, center.y + p.u);
let pt_r = Point::new(center.x + p.v, center.y + p.u);
self.draw_pixel(pt_l, color);
@ -442,7 +449,7 @@ pub trait Canvas: BasicCanvas {
);
if self.viewport().contains(r) {
for p in circle_points(radius) {
for p in circle_points(radius).take_while(|p| p.u < p.v) {
let pt_l = Point::new(center.x - p.v, center.y - p.u);
self.blend_pixel(pt_l, color, p.frac);
self.blend_pixel(pt_l.onright(), color, 255 - p.frac);
@ -453,12 +460,12 @@ pub trait Canvas: BasicCanvas {
}
let r = Rect::new(
Point::new(center.x - radius, center.y),
Point::new(center.x - radius, center.y + 1),
Point::new(center.x + radius + 1, center.y + split + 1),
);
if self.viewport().contains(r) {
for p in circle_points(radius) {
for p in circle_points(radius).skip(1).take_while(|p| p.u < p.v) {
let pt_l = Point::new(center.x - p.v, center.y + p.u);
self.blend_pixel(pt_l, color, p.frac);
self.blend_pixel(pt_l.onright(), color, 255 - p.frac);
@ -489,6 +496,7 @@ pub trait Canvas: BasicCanvas {
#[cfg(not(feature = "ui_antialiasing"))]
fn fill_circle(&mut self, center: Point, radius: i16, color: Color) {
let split = unwrap!(circle_points(radius).last()).v;
let alpha = 255;
let r = Rect::new(
Point::new(center.x - radius, center.y - radius),
@ -500,7 +508,7 @@ pub trait Canvas: BasicCanvas {
if p.last {
let pt_l = Point::new(center.x - p.u, center.y - p.v);
let pt_r = Point::new(center.x + p.u, center.y - p.v);
self.fill_rect(Rect::new(pt_l, pt_r.onright().under()), color);
self.fill_rect(Rect::new(pt_l, pt_r.onright().under()), color, alpha);
}
}
}
@ -511,23 +519,23 @@ pub trait Canvas: BasicCanvas {
);
if self.viewport().contains(r) {
for p in circle_points(radius) {
for p in circle_points(radius).take_while(|p| p.u < p.v) {
let pt_l = Point::new(center.x - p.v, center.y - p.u);
let pt_r = Point::new(center.x + p.v, center.y - p.u);
self.fill_rect(Rect::new(pt_l, pt_r.onright().under()), color);
self.fill_rect(Rect::new(pt_l, pt_r.onright().under()), color, alpha);
}
}
let r = Rect::new(
Point::new(center.x - radius, center.y),
Point::new(center.x - radius, center.y + 1),
Point::new(center.x + radius + 1, center.y + split + 1),
);
if self.viewport().contains(r) {
for p in circle_points(radius) {
for p in circle_points(radius).skip(1).take_while(|p| p.u < p.v) {
let pt_l = Point::new(center.x - p.v, center.y + p.u);
let pt_r = Point::new(center.x + p.v, center.y + p.u);
self.fill_rect(Rect::new(pt_l, pt_r.onright().under()), color);
self.fill_rect(Rect::new(pt_l, pt_r.onright().under()), color, alpha);
}
}
@ -541,7 +549,7 @@ pub trait Canvas: BasicCanvas {
if p.last {
let pt_l = Point::new(center.x - p.u, center.y + p.v);
let pt_r = Point::new(center.x + p.u, center.y + p.v);
self.fill_rect(Rect::new(pt_l, pt_r.onright().under()), color);
self.fill_rect(Rect::new(pt_l, pt_r.onright().under()), color, alpha);
}
}
}
@ -553,6 +561,9 @@ pub trait Canvas: BasicCanvas {
fn fill_circle(&mut self, center: Point, radius: i16, color: Color) {
let split = unwrap!(circle_points(radius).last()).v;
let alpha = 255;
let alpha_mul = |a: u8| -> u8 { ((a as u16 * alpha as u16) / 255) as u8 };
let r = Rect::new(
Point::new(center.x - radius, center.y - radius),
Point::new(center.x + radius + 1, center.y - split + 1),
@ -562,12 +573,14 @@ pub trait Canvas: BasicCanvas {
for p in circle_points(radius) {
let pt_l = Point::new(center.x - p.u, center.y - p.v);
let pt_r = Point::new(center.x + p.u, center.y - p.v);
self.blend_pixel(pt_l, color, p.frac);
self.blend_pixel(pt_r, color, p.frac);
self.blend_pixel(pt_l, color, alpha_mul(p.frac));
if pt_l != pt_r {
self.blend_pixel(pt_r, color, alpha_mul(p.frac));
}
if p.first {
let r = Rect::new(pt_l.onright(), pt_r.under());
self.fill_rect(r, color);
self.fill_rect(r, color, alpha);
}
}
}
@ -578,31 +591,31 @@ pub trait Canvas: BasicCanvas {
);
if self.viewport().contains(r) {
for p in circle_points(radius) {
for p in circle_points(radius).take_while(|p| p.u < p.v) {
let pt_l = Point::new(center.x - p.v, center.y - p.u);
let pt_r = Point::new(center.x + p.v, center.y - p.u);
self.blend_pixel(pt_l, color, p.frac);
self.blend_pixel(pt_r, color, p.frac);
self.blend_pixel(pt_l, color, alpha_mul(p.frac));
self.blend_pixel(pt_r, color, alpha_mul(p.frac));
let r = Rect::new(pt_l.onright(), pt_r.under());
self.fill_rect(r, color);
self.fill_rect(r, color, alpha);
}
}
let r = Rect::new(
Point::new(center.x - radius, center.y),
Point::new(center.x - radius, center.y + 1),
Point::new(center.x + radius + 1, center.y + split + 1),
);
if self.viewport().contains(r) {
for p in circle_points(radius) {
for p in circle_points(radius).skip(1).take_while(|p| p.u < p.v) {
let pt_l = Point::new(center.x - p.v, center.y + p.u);
let pt_r = Point::new(center.x + p.v, center.y + p.u);
self.blend_pixel(pt_l, color, p.frac);
self.blend_pixel(pt_r, color, p.frac);
self.blend_pixel(pt_l, color, alpha_mul(p.frac));
self.blend_pixel(pt_r, color, alpha_mul(p.frac));
let r = Rect::new(pt_l.onright(), pt_r.under());
self.fill_rect(r, color);
self.fill_rect(r, color, alpha);
}
}
@ -615,12 +628,14 @@ pub trait Canvas: BasicCanvas {
for p in circle_points(radius) {
let pt_l = Point::new(center.x - p.u, center.y + p.v);
let pt_r = Point::new(center.x + p.u, center.y + p.v);
self.blend_pixel(pt_l, color, p.frac);
self.blend_pixel(pt_r, color, p.frac);
if pt_l != pt_r {
self.blend_pixel(pt_l, color, alpha_mul(p.frac));
}
self.blend_pixel(pt_r, color, alpha_mul(p.frac));
if p.first {
let r = Rect::new(pt_l.onright(), pt_r.under());
self.fill_rect(r, color);
self.fill_rect(r, color, alpha);
}
}
}
@ -635,8 +650,11 @@ pub trait Canvas: BasicCanvas {
mut end: i16,
color: Color,
) {
start = start.clamp(0, PI4 * 8);
end = end.clamp(0, PI4 * 8);
start = (PI4 * 8 + start % (PI4 * 8)) % (PI4 * 8);
end = (PI4 * 8 + end % (PI4 * 8)) % (PI4 * 8);
let alpha = 255;
let alpha_mul = |a: u8| -> u8 { ((a as u16 * alpha as u16) / 255) as u8 };
if start != end {
// The algorithm fills everything except the middle point ;-)
@ -663,24 +681,24 @@ pub trait Canvas: BasicCanvas {
// The function is special for each octant using 4 different axes of symmetry
let filler = &mut |p1: Option<Point>, p1_frac, p2: Point, p2_frac| {
let p2: Point = center + p2.rot(octant).into();
self.blend_pixel(p2, color, p2_frac);
self.blend_pixel(p2, color, alpha_mul(p2_frac));
if let Some(p1) = p1 {
let p1: Point = center + p1.rot(octant).into();
let ofs = Point::new(-1, 0).rot(octant);
self.blend_pixel(p1 + ofs.into(), color, p1_frac);
self.blend_pixel(p1 + ofs.into(), color, alpha_mul(p1_frac));
if ofs.x + ofs.y < 0 {
if ofs.x != 0 {
self.fill_rect(Rect::new(p1, p2.under()), color);
self.fill_rect(Rect::new(p1, p2.under()), color, alpha);
} else {
self.fill_rect(Rect::new(p1, p2.onright()), color);
self.fill_rect(Rect::new(p1, p2.onright()), color, alpha);
}
} else {
let p1 = p1 + ofs.into();
let p2 = p2 + ofs.into();
if ofs.x != 0 {
self.fill_rect(Rect::new(p2, p1.under()), color);
self.fill_rect(Rect::new(p2, p1.under()), color, alpha);
} else {
self.fill_rect(Rect::new(p2, p1.onright()), color);
self.fill_rect(Rect::new(p2, p1.onright()), color, alpha);
}
}
}
@ -723,7 +741,7 @@ pub trait Canvas: BasicCanvas {
fill_octant(radius, 0, sin(PI4), filler);
} else {
// Partial fill
if end < angle + PI4 {
if (end > angle) && (end < angle + PI4) {
// Fill up to `end`
fill_octant(radius, sin(corr(0)), sin(corr(end - angle)), filler);
}
@ -756,12 +774,19 @@ fn fill_octant(
// Intersection of the p1 line and the circle
let p1_start = unwrap!(iter.next());
for _ in 1..(u2 - u1) {
iter.next();
}
// Intersection of the p1 line and the circle
let p2_start = iter.next().unwrap_or(p1_start);
let mut p2_start = p1_start;
loop {
if let Some(p) = iter.next() {
if p.u > u2 {
break;
}
p2_start = p;
} else {
break;
}
}
// Flag if we draw section up to 45degs
let join_flag = iter.next().is_none();

@ -48,9 +48,9 @@ impl<'a> BasicCanvas for Mono8Canvas<'a> {
self.bitmap.size()
}
fn fill_rect(&mut self, r: Rect, color: Color) {
fn fill_rect(&mut self, r: Rect, color: Color, alpha: u8) {
let r = r.translate(self.viewport.origin);
self.bitmap.mono8_fill(r, self.viewport.clip, color);
self.bitmap.mono8_fill(r, self.viewport.clip, color, alpha);
}
fn draw_bitmap(&mut self, r: Rect, bitmap: BitmapView) {

@ -48,9 +48,9 @@ impl<'a> BasicCanvas for Rgb565Canvas<'a> {
self.viewport = viewport.absolute_clip(self.bounds());
}
fn fill_rect(&mut self, r: Rect, color: Color) {
fn fill_rect(&mut self, r: Rect, color: Color, alpha: u8) {
let r = r.translate(self.viewport.origin);
self.bitmap.rgb565_fill(r, self.viewport.clip, color);
self.bitmap.rgb565_fill(r, self.viewport.clip, color, alpha);
}
fn draw_bitmap(&mut self, r: Rect, bitmap: BitmapView) {

@ -48,9 +48,10 @@ impl<'a> BasicCanvas for Rgba8888Canvas<'a> {
self.viewport = viewport.absolute_clip(self.bounds());
}
fn fill_rect(&mut self, r: Rect, color: Color) {
fn fill_rect(&mut self, r: Rect, color: Color, alpha: u8) {
let r = r.translate(self.viewport.origin);
self.bitmap.rgba8888_fill(r, self.viewport.clip, color);
self.bitmap
.rgba8888_fill(r, self.viewport.clip, color, alpha);
}
fn draw_bitmap(&mut self, r: Rect, bitmap: BitmapView) {

@ -62,7 +62,7 @@ impl<'s> Shape<'s> for HorizontalLine {
// Solid line
let size = Offset::new(self.length, self.thickness as i16);
let r = Rect::from_top_left_and_size(self.pos, size);
canvas.fill_rect(r, self.color);
canvas.fill_rect(r, self.color, 255);
} else {
// Dotted line
let thickness = self.thickness as i16;
@ -71,7 +71,7 @@ impl<'s> Shape<'s> for HorizontalLine {
self.pos + Offset::x(x),
Offset::uniform(thickness),
);
canvas.fill_rect(r, self.color);
canvas.fill_rect(r, self.color, 255);
}
}
}

@ -101,7 +101,7 @@ impl<'s> Shape<'s> for LoaderCircular {
fn draw(&mut self, canvas: &mut dyn Canvas, _cache: &DrawingCache) {
for c in self.cells().iter() {
canvas.fill_rect(self.cell_rect(*c), self.color);
canvas.fill_rect(self.cell_rect(*c), self.color, 255);
}
}
}

@ -58,17 +58,17 @@ impl LoaderStarry {
fn draw_large_star(&self, canvas: &mut dyn Canvas, offset: Offset) {
let r = Rect::from_center_and_size(self.pos + offset, Offset::uniform(STAR_LARGE));
canvas.fill_round_rect(r, 2, self.color);
canvas.fill_round_rect(r, 2, self.color, 255);
}
fn draw_medium_star(&self, canvas: &mut dyn Canvas, offset: Offset) {
let r = Rect::from_center_and_size(self.pos + offset, Offset::uniform(STAR_MEDIUM));
canvas.fill_round_rect(r, 1, self.color);
canvas.fill_round_rect(r, 1, self.color, 255);
}
fn draw_small_star(&self, canvas: &mut dyn Canvas, offset: Offset) {
let r = Rect::from_center_and_size(self.pos + offset, Offset::uniform(STAR_SMALL));
canvas.fill_rect(r, self.color);
canvas.fill_rect(r, self.color, 255);
}
}

@ -16,6 +16,8 @@ pub struct Bar {
thickness: i16,
/// Corner radius (default 0)
radius: i16,
/// Alpha (default 255)
alpha: u8,
}
impl Bar {
@ -26,6 +28,7 @@ impl Bar {
bg_color: None,
thickness: 1,
radius: 0,
alpha: 255,
}
}
@ -51,6 +54,10 @@ impl Bar {
Self { thickness, ..self }
}
pub fn with_alpha(self, alpha: u8) -> Self {
Self { alpha, ..self }
}
pub fn render<'s>(self, renderer: &mut impl Renderer<'s>) {
renderer.render_shape(self);
}
@ -80,22 +87,22 @@ impl Shape<'_> for Bar {
// outline
if th > 0 {
let r = self.area;
canvas.fill_rect(Rect { y1: r.y0 + th, ..r }, fg_color);
canvas.fill_rect(Rect { x1: r.x0 + th, ..r }, fg_color);
canvas.fill_rect(Rect { x0: r.x1 - th, ..r }, fg_color);
canvas.fill_rect(Rect { y0: r.y1 - th, ..r }, fg_color);
canvas.fill_rect(Rect { y1: r.y0 + th, ..r }, fg_color, self.alpha);
canvas.fill_rect(Rect { x1: r.x0 + th, ..r }, fg_color, self.alpha);
canvas.fill_rect(Rect { x0: r.x1 - th, ..r }, fg_color, self.alpha);
canvas.fill_rect(Rect { y0: r.y1 - th, ..r }, fg_color, self.alpha);
}
}
if let Some(bg_color) = self.bg_color {
// background
let bg_r = self.area.shrink(th);
canvas.fill_rect(bg_r, bg_color);
canvas.fill_rect(bg_r, bg_color, self.alpha);
}
} else {
if let Some(fg_color) = self.fg_color {
if th > 0 {
if self.bg_color.is_some() {
canvas.fill_round_rect(self.area, self.radius, fg_color);
canvas.fill_round_rect(self.area, self.radius, fg_color, self.alpha);
} else {
#[cfg(not(feature = "ui_antialiasing"))]
canvas.draw_round_rect(self.area, self.radius, fg_color);
@ -104,7 +111,7 @@ impl Shape<'_> for Bar {
}
if let Some(bg_color) = self.bg_color {
let bg_r = self.area.shrink(th);
canvas.fill_round_rect(bg_r, self.radius, bg_color);
canvas.fill_round_rect(bg_r, self.radius, bg_color, self.alpha);
}
}
}

@ -66,7 +66,7 @@ impl BasicCanvas for DisplayModelT {
self.size
}
fn fill_rect(&mut self, r: Rect, color: Color) {
fn fill_rect(&mut self, r: Rect, color: Color, _alpha: u8) {
let r = r.translate(self.viewport.origin);
Dma2d::wnd565_fill(r, self.viewport.clip, color);
}

@ -20,6 +20,8 @@ pub struct Text<'a> {
font: Font,
// Horizontal alignment
align: Alignment,
// Final bounds calculated when rendered
bounds: Rect,
}
impl<'a> Text<'a> {
@ -32,6 +34,7 @@ impl<'a> Text<'a> {
color: Color::white(),
font: Font::NORMAL,
align: Alignment::Start,
bounds: Rect::zero(),
}
}
@ -47,7 +50,8 @@ impl<'a> Text<'a> {
Self { align, ..self }
}
pub fn render<'r>(self, renderer: &mut impl Renderer<'r>) {
pub fn render<'r>(mut self, renderer: &mut impl Renderer<'r>) {
self.bounds = self.calc_bounds();
renderer.render_shape(self);
}
@ -61,27 +65,29 @@ impl<'a> Text<'a> {
Alignment::End => Point::new(self.pos.x - self.font.text_width(self.text), self.pos.y),
}
}
}
impl<'a> Shape<'_> for Text<'a> {
fn bounds(&self, _cache: &DrawingCache) -> Rect {
fn calc_bounds(&self) -> Rect {
let pos = self.aligned_pos();
let max_ascent = self.font.text_max_height() - self.font.text_baseline();
let max_descent = self.font.text_baseline();
let (ascent, descent) = self.font.visible_text_height_ex(self.text);
Rect {
x0: pos.x,
y0: pos.y - max_ascent,
y0: pos.y - ascent,
x1: pos.x + self.font.text_width(self.text),
y1: pos.y + max_descent,
y1: pos.y + descent,
}
}
}
impl<'a> Shape<'_> for Text<'a> {
fn bounds(&self, _cache: &DrawingCache) -> Rect {
self.bounds
}
fn cleanup(&mut self, _cache: &DrawingCache) {}
fn draw(&mut self, canvas: &mut dyn Canvas, cache: &DrawingCache) {
let max_height = self.font.max_height();
let baseline = self.font.text_baseline();
let mut r = self.bounds(cache);
let max_ascent = self.pos.y - r.y0;
// TODO: optimize text clipping, use canvas.viewport()
@ -96,7 +102,7 @@ impl<'a> Shape<'_> for Text<'a> {
.with_fg(self.color)
.with_offset(Offset::new(
-glyph.bearing_x,
-(max_height - baseline - glyph.bearing_y),
-(max_ascent - glyph.bearing_y),
));
canvas.blend_bitmap(r, glyph_view);
@ -115,3 +121,15 @@ impl<'a, 's> ShapeClone<'s> for Text<'a> {
Some(clone.uninit.init(Text { text, ..self }))
}
}
impl Font {
fn visible_text_height_ex(&self, text: &str) -> (i16, i16) {
let (mut ascent, mut descent) = (0, 0);
for c in text.chars() {
let glyph = self.get_glyph(c);
ascent = ascent.max(glyph.bearing_y);
descent = descent.max(glyph.height - glyph.bearing_y);
}
(ascent, descent)
}
}

@ -32,7 +32,7 @@ bool dma2d_accessible(const void* ptr) {
// TODO:: valid only for STM32F42x
const void* ccm_start = (const void*)0x10000000;
const void* ccm_end = (const void*)0x1000FFFF;
return !(ptr >= ccm_start || ptr <= ccm_end);
return !(ptr >= ccm_start && ptr <= ccm_end);
}
void dma2d_wait(void) {
@ -43,15 +43,28 @@ void dma2d_wait(void) {
bool dma2d_rgb565_fill(const dma2d_params_t* dp) {
dma2d_wait();
dma2d_handle.Init.ColorMode = DMA2D_OUTPUT_RGB565;
dma2d_handle.Init.Mode = DMA2D_R2M;
dma2d_handle.Init.OutputOffset =
dp->dst_stride / sizeof(uint16_t) - dp->width;
HAL_DMA2D_Init(&dma2d_handle);
if (dp->src_alpha == 255) {
dma2d_handle.Init.ColorMode = DMA2D_OUTPUT_RGB565;
dma2d_handle.Init.Mode = DMA2D_R2M;
dma2d_handle.Init.OutputOffset =
dp->dst_stride / sizeof(uint16_t) - dp->width;
HAL_DMA2D_Init(&dma2d_handle);
HAL_DMA2D_Start(&dma2d_handle, gdc_color_to_color32(dp->src_fg),
(uint32_t)dp->dst_row + dp->dst_x * sizeof(uint16_t),
dp->width, dp->height);
HAL_DMA2D_Start(&dma2d_handle, gdc_color_to_color32(dp->src_fg),
(uint32_t)dp->dst_row + dp->dst_x * sizeof(uint16_t),
dp->width, dp->height);
} else {
// STM32F4 can not accelerate blending with the fixed color
uint16_t* dst_ptr = (uint16_t*)dp->dst_row + dp->dst_x;
uint16_t height = dp->height;
uint8_t alpha = dp->src_alpha;
while (height-- > 0) {
for (int x = 0; x < dp->width; x++) {
dst_ptr[x] = gdc_color16_blend_a8(dp->src_fg, dst_ptr[x], alpha);
}
dst_ptr += dp->dst_stride / sizeof(*dst_ptr);
}
}
return true;
}

Loading…
Cancel
Save