fixup! feat(core): introduce new drawing library

matejcik/lifetimehell
cepetr 2 weeks ago
parent 3182c753c3
commit 3f0217ae31

@ -6,9 +6,17 @@ use crate::ui::{
shape::{Bitmap, BitmapFormat, BitmapView},
};
pub type BitBlt = ffi::gfx_bitblt_t;
/// Waits for the DMA2D peripheral transfer to complete.
pub fn wait_for_transfer() {
// SAFETY:
// `ffi::dma2d_wait()` is always safe to call.
#[cfg(feature = "dma2d")]
unsafe {
ffi::dma2d_wait()
}
}
impl Default for BitBlt {
impl Default for ffi::gfx_bitblt_t {
fn default() -> Self {
Self {
width: 0,
@ -28,29 +36,39 @@ impl Default for BitBlt {
}
}
impl BitBlt {
/// Sets the destination bitmap.
// This is very private interface (almost completely unsafe) to
// preparing a `gfx_bitblt_t` structure for the bitblt operations.
impl ffi::gfx_bitblt_t {
/// Sets the destination bitmap pointer and stride.
///
/// Be sure that clipping rectangle specified in the `new_fill()` or
/// `new_copy()` method is completely inside the destination bitmap.
fn with_dst(self, dst: &mut Bitmap) -> Self {
// Ensure that the destination rectangle is completely inside the
// destination bitmap.
/// # SAFETY
/// 1) Ensure that caller holds mutable reference to the destination bitmap
/// until the `gfx_bitblt_t` is dropped.
unsafe fn with_dst(self, dst: &mut Bitmap) -> Self {
// This ensures that the destination rectangle is
// completely inside the destination bitmap
assert!(dst.width() as u16 >= self.dst_x + self.width);
assert!(dst.height() as u16 >= self.dst_y + self.height);
Self {
// SAFETY:
// Lines between `dst_y` and`dst_y + height` are inside
// the destination bitmap.
// the destination bitmap. See asserts above.
dst_row: unsafe { dst.row_ptr(self.dst_y) },
dst_stride: dst.stride() as u16,
..self
}
}
// Sets the destination rectangle.
fn with_rect(self, r: Rect) -> Self {
/// Sets the coordinates of the destination rectangle inside the destination
/// bitmap.
///
/// # SAFETY
/// 1) Ensure that the rectangle is completely inside the destination
/// bitmap.
/// 2) If the copy or blend operation is used, ensure that the rectangle
/// is completely filled with source bitmap or its part.
unsafe fn with_rect(self, r: Rect) -> Self {
Self {
width: r.width() as u16,
height: r.height() as u16,
@ -62,9 +80,14 @@ impl BitBlt {
/// Sets the source bitmap
///
/// `x` and `y` specify the offset applied to the source bitmap and
/// must be inside the source bitmap.
fn with_src(self, bitmap: &Bitmap, x: i16, y: i16) -> Self {
/// `x` and `y` specifies offset inside the source bitmap
///
/// # SAFETY
/// 1) Ensure that `x` and `y` are inside the source bitmap.
/// 2) Source bitmap complete covers destination rectangle.
/// 3) Ensure that caller holds mutable reference to the source bitmap.
/// until the `gfx_bitblt_t` is dropped.
unsafe fn with_src(self, bitmap: &Bitmap, x: i16, y: i16) -> Self {
let bitmap_stride = match bitmap.format() {
BitmapFormat::MONO1P => bitmap.width() as u16, // packed bits
_ => bitmap.stride() as u16,
@ -72,8 +95,7 @@ impl BitBlt {
Self {
// SAFETY:
// it's safe if source rectangle is properly clipped
// (ensured by the `BitBltCopy::new()`).
// It's safe if `y` is inside the bitmap (see note above)
src_row: unsafe { bitmap.row_ptr(y as u16) },
src_stride: bitmap_stride,
src_x: x as u16,
@ -107,21 +129,11 @@ impl BitBlt {
..self
}
}
/// Waits for the DMA2D peripheral transfer to complete.
pub fn wait_for_transfer() {
// SAFETY:
// `ffi::dma2d_wait()` is always safe to call.
#[cfg(feature = "dma2d")]
unsafe {
ffi::dma2d_wait()
}
}
}
/// Rectangle filling operation.
pub struct BitBltFill {
bitblt: BitBlt,
bitblt: ffi::gfx_bitblt_t,
}
impl BitBltFill {
@ -139,10 +151,17 @@ impl BitBltFill {
let r = r.clamp(clip);
if !r.is_empty() {
Some(Self {
bitblt: BitBlt::default()
.with_rect(r)
.with_fg(color)
.with_alpha(alpha),
// SAFETY:
// The only unsafe operation is `.with_rect()`, which is safe
// as long as the rectangle is completely inside the destination bitmap.
// We will set the destination bitmap later, so for now, we can
// safely assume that the rectangle is within the bitmap.
bitblt: unsafe {
ffi::gfx_bitblt_t::default()
.with_rect(r)
.with_fg(color)
.with_alpha(alpha)
},
})
} else {
None
@ -154,8 +173,11 @@ impl BitBltFill {
/// Destination bitmap must be in RGB565 format
pub fn rgb565_fill(&self, dst: &mut Bitmap) {
assert!(dst.format() == BitmapFormat::RGB565);
// SAFETY:
// - The destination bitmap is in the correct format.
// - The destination rectangle is completely inside the destination bitmap.
unsafe { ffi::gfx_rgb565_fill(&self.bitblt.with_dst(dst)) };
dst.mark_dma_pending()
dst.mark_dma_pending();
}
/// Fills a rectangle in the destination bitmap with the specified color.
@ -163,8 +185,11 @@ impl BitBltFill {
/// The destination bitmap is in the RGBA8888 format.
pub fn rgba8888_fill(&self, dst: &mut Bitmap) {
assert!(dst.format() == BitmapFormat::RGBA8888);
// SAFETY:
// - The destination bitmap is in the correct format.
// - The destination rectangle is completely inside the destination bitmap.
unsafe { ffi::gfx_rgba8888_fill(&self.bitblt.with_dst(dst)) };
dst.mark_dma_pending()
dst.mark_dma_pending();
}
/// Fills a rectangle in the destination bitmap with the specified color.
@ -172,20 +197,27 @@ impl BitBltFill {
/// The destination bitmap is in the MONO8 format.
pub fn mono8_fill(&self, dst: &mut Bitmap) {
assert!(dst.format() == BitmapFormat::RGBA8888);
// SAFETY:
// - The destination bitmap is in the correct format.
// - The destination rectangle is completely inside the destination bitmap.
unsafe { ffi::gfx_mono8_fill(&self.bitblt.with_dst(dst)) };
dst.mark_dma_pending()
dst.mark_dma_pending();
}
/// Fills a rectangle on the display with the specified color.
#[cfg(all(not(feature = "xframebuffer"), feature = "new_rendering"))]
pub fn display_fill(&self) {
assert!(self.bitblt.dst_x + self.bitblt.width <= ffi::DISPLAY_RESX as u16);
assert!(self.bitblt.dst_y + self.bitblt.height <= ffi::DISPLAY_RESY as u16);
// SAFETY:
// - The destination rectangle is completely inside the display.
unsafe { ffi::display_fill(&self.bitblt) };
}
}
/// Rectangle copying or blending operation.
pub struct BitBltCopy<'a> {
bitblt: BitBlt,
bitblt: ffi::gfx_bitblt_t,
src: &'a BitmapView<'a>,
}
@ -233,11 +265,23 @@ impl<'a> BitBltCopy<'a> {
if !r.is_empty() {
Some(Self {
bitblt: BitBlt::default()
.with_rect(r)
.with_src(src.bitmap, offset.x, offset.y)
.with_bg(src.bg_color)
.with_fg(src.fg_color),
// SAFETY:
// The only unsafe operations are `.with_rect()` and `.with_src()`:
// - We can safely assume that the destination rectangle is inside the destination
// bitmap. This will be verified later in `.with_dst()`.
// - The `x`, `y` offsets are within the source bitmap, as ensured by the preceding
// code.
// - The source bitmap completely covers the destination rectangle, which is also
// ensured by the preceding code.
// - We hold a non-mutable reference to the source bitmap in the `BitBltCopy`
// structure.
bitblt: unsafe {
ffi::gfx_bitblt_t::default()
.with_rect(r)
.with_src(src.bitmap, offset.x, offset.y)
.with_bg(src.bg_color)
.with_fg(src.fg_color)
},
src,
})
} else {
@ -250,18 +294,17 @@ impl<'a> BitBltCopy<'a> {
pub fn rgb565_copy(&self, dst: &mut Bitmap) {
assert!(dst.format() == BitmapFormat::RGB565);
let bitblt = self.bitblt.with_dst(dst);
// SAFETY:
// - The destination and source bitmaps are in the correct formats.
// - Source and destination coordinates are properly clipped, which is ensured
// by the `new()` and `with_dst()` methods.
// - BitBlt structure is properly initialized.
// - Source and destination coordinates are properly clipped
// - The DMA pending flag is set for both bitmaps after operations.
match self.src.format() {
BitmapFormat::MONO4 => unsafe { ffi::gfx_rgb565_copy_mono4(&bitblt) },
BitmapFormat::RGB565 => unsafe { ffi::gfx_rgb565_copy_rgb565(&bitblt) },
_ => unimplemented!(),
unsafe {
let bitblt = self.bitblt.with_dst(dst);
match self.src.format() {
BitmapFormat::MONO4 => ffi::gfx_rgb565_copy_mono4(&bitblt),
BitmapFormat::RGB565 => ffi::gfx_rgb565_copy_rgb565(&bitblt),
_ => unimplemented!(),
}
}
self.src.bitmap.mark_dma_pending();
@ -273,17 +316,16 @@ impl<'a> BitBltCopy<'a> {
pub fn rgb565_blend(&self, dst: &mut Bitmap) {
assert!(dst.format() == BitmapFormat::RGB565);
let bitblt = self.bitblt.with_dst(dst);
// SAFETY:
// - The destination and source bitmaps are in the correct formats.
// - Source and destination coordinates are properly clipped, which is ensured
// by the `new()` and `with_dst()` methods.
// - BitBlt structure is properly initialized.
// - Source and destination coordinates are properly clipped
// - The DMA pending flag is set for both bitmaps after operations.
match self.src.format() {
BitmapFormat::MONO4 => unsafe { ffi::gfx_rgb565_blend_mono4(&bitblt) },
_ => unimplemented!(),
unsafe {
let bitblt = self.bitblt.with_dst(dst);
match self.src.format() {
BitmapFormat::MONO4 => ffi::gfx_rgb565_blend_mono4(&bitblt),
_ => unimplemented!(),
}
}
self.src.bitmap.mark_dma_pending();
@ -295,19 +337,18 @@ impl<'a> BitBltCopy<'a> {
pub fn rgba8888_copy(&self, dst: &mut Bitmap) {
assert!(dst.format() == BitmapFormat::RGBA8888);
let bitblt = self.bitblt.with_dst(dst);
// SAFETY:
// - The destination and source bitmaps are in the correct formats.
// - Source and destination coordinates are properly clipped, which is ensured
// by the `new()` and `with_dst()` methods.
// - BitBlt structure is properly initialized.
// - Source and destination coordinates are properly clipped
// - The DMA pending flag is set for both bitmaps after operations.
match self.src.format() {
BitmapFormat::MONO4 => unsafe { ffi::gfx_rgba8888_copy_mono4(&bitblt) },
BitmapFormat::RGB565 => unsafe { ffi::gfx_rgba8888_copy_rgb565(&bitblt) },
BitmapFormat::RGBA8888 => unsafe { ffi::gfx_rgba8888_copy_rgba8888(&bitblt) },
_ => unimplemented!(),
unsafe {
let bitblt = self.bitblt.with_dst(dst);
match self.src.format() {
BitmapFormat::MONO4 => ffi::gfx_rgba8888_copy_mono4(&bitblt),
BitmapFormat::RGB565 => ffi::gfx_rgba8888_copy_rgb565(&bitblt),
BitmapFormat::RGBA8888 => ffi::gfx_rgba8888_copy_rgba8888(&bitblt),
_ => unimplemented!(),
}
}
self.src.bitmap.mark_dma_pending();
@ -319,17 +360,16 @@ impl<'a> BitBltCopy<'a> {
pub fn rgba8888_blend(&self, dst: &mut Bitmap) {
assert!(dst.format() == BitmapFormat::RGBA8888);
let bitblt = self.bitblt.with_dst(dst);
// SAFETY:
// - The destination and source bitmaps are in the correct formats.
// - Source and destination coordinates are properly clipped, which is ensured
// by the `new()` and `with_dst()` methods.
// - BitBlt structure is properly initialized.
// - Source and destination coordinates are properly clipped
// - The DMA pending flag is set for both bitmaps after operations.
match self.src.format() {
BitmapFormat::MONO4 => unsafe { ffi::gfx_rgba8888_blend_mono4(&bitblt) },
_ => unimplemented!(),
unsafe {
let bitblt = self.bitblt.with_dst(dst);
match self.src.format() {
BitmapFormat::MONO4 => ffi::gfx_rgba8888_blend_mono4(&bitblt),
_ => unimplemented!(),
}
}
self.src.bitmap.mark_dma_pending();
@ -341,19 +381,17 @@ impl<'a> BitBltCopy<'a> {
pub fn mono8_copy(&self, dst: &mut Bitmap) {
assert!(dst.format() == BitmapFormat::MONO8);
let bitblt = self.bitblt.with_dst(dst);
// SAFETY:
// - The destination and source bitmaps are in the correct formats.
// - Source and destination coordinates are properly clipped, which is ensured
// by the `new()` and `with_dst()` methods.
// - BitBlt structure is properly initialized.
// - Source and destination coordinates are properly clipped
// - The DMA pending flag is set for both bitmaps after operations.
match self.src.format() {
BitmapFormat::MONO1P => unsafe { ffi::gfx_mono8_copy_mono1p(&bitblt) },
BitmapFormat::MONO4 => unsafe { ffi::gfx_mono8_copy_mono4(&bitblt) },
_ => unimplemented!(),
unsafe {
let bitblt = self.bitblt.with_dst(dst);
match self.src.format() {
BitmapFormat::MONO1P => ffi::gfx_mono8_copy_mono1p(&bitblt),
BitmapFormat::MONO4 => ffi::gfx_mono8_copy_mono4(&bitblt),
_ => unimplemented!(),
}
}
self.src.bitmap.mark_dma_pending();
@ -365,19 +403,17 @@ impl<'a> BitBltCopy<'a> {
pub fn mono8_blend(&self, dst: &mut Bitmap) {
assert!(dst.format() == BitmapFormat::MONO8);
let bitblt = self.bitblt.with_dst(dst);
// SAFETY:
// - The destination and source bitmaps are in the correct formats.
// - Source and destination coordinates are properly clipped, which is ensured
// by the `new()` and `with_dst()` methods.
// - BitBlt structure is properly initialized.
// - Source and destination coordinates are properly clipped
// - The DMA pending flag is set for both bitmaps after operations.
match self.src.format() {
BitmapFormat::MONO1P => unsafe { ffi::gfx_mono8_blend_mono1p(&bitblt) },
BitmapFormat::MONO4 => unsafe { ffi::gfx_mono8_blend_mono4(&bitblt) },
_ => unimplemented!(),
unsafe {
let bitblt = self.bitblt.with_dst(dst);
match self.src.format() {
BitmapFormat::MONO1P => ffi::gfx_mono8_blend_mono1p(&bitblt),
BitmapFormat::MONO4 => ffi::gfx_mono8_blend_mono4(&bitblt),
_ => unimplemented!(),
}
}
self.src.bitmap.mark_dma_pending();
@ -389,16 +425,19 @@ impl<'a> BitBltCopy<'a> {
/// - The source bitmap uses the RGB565 format.
#[cfg(all(not(feature = "xframebuffer"), feature = "new_rendering"))]
pub fn display_copy(&self) {
assert!(self.bitblt.dst_x + self.bitblt.width <= ffi::DISPLAY_RESX as u16);
assert!(self.bitblt.dst_y + self.bitblt.height <= ffi::DISPLAY_RESY as u16);
// SAFETY:
// - The source bitmap is in the correct formats.
// - Source and destination coordinates are properly clipped, which is ensured
// by the `new()` and `with_dst()` methods.
// - BitBlt structure is properly initialized.
// - Source and destination coordinates are properly clipped
// - The destination rectangle is completely inside the display.
// - The DMA pending flag is set for src bitmap after operations.
match self.src.format() {
BitmapFormat::RGB565 => unsafe { ffi::display_copy_rgb565(&self.bitblt) },
_ => unimplemented!(),
unsafe {
match self.src.format() {
BitmapFormat::RGB565 => ffi::display_copy_rgb565(&self.bitblt),
_ => unimplemented!(),
}
}
self.src.bitmap.mark_dma_pending();

@ -1,4 +1,4 @@
use crate::trezorhal::bitblt::BitBlt;
use crate::trezorhal::bitblt;
use crate::ui::{display::Color, geometry::Offset};
@ -278,7 +278,7 @@ impl<'a> Bitmap<'a> {
/// Waits until DMA operation is finished
fn wait_for_dma(&self) {
if self.dma_pending.get() {
BitBlt::wait_for_transfer();
bitblt::wait_for_transfer();
self.dma_pending.set(false);
}
}

Loading…
Cancel
Save