Skip to content

Commit 19fa5ec

Browse files
committed
graphics.draw_nine_slice
1 parent 61dcb32 commit 19fa5ec

2 files changed

Lines changed: 192 additions & 2 deletions

File tree

src/host/graphics.rs

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,191 @@ pub(crate) fn draw_sub_tile(
566566
}
567567
}
568568

569+
/// Render the image using 9-slice scaling.
570+
///
571+
/// https://en.wikipedia.org/wiki/9-slice_scaling
572+
pub(crate) fn draw_nine_slice(
573+
mut caller: C,
574+
ptr: u32,
575+
len: u32,
576+
// Screen area to fill.
577+
x: i32,
578+
y: i32,
579+
w: i32,
580+
h: i32,
581+
// The area of the middle square on the image.
582+
mid_x: i32,
583+
mid_y: i32,
584+
mid_width: u32,
585+
mid_height: u32,
586+
) {
587+
let state = caller.data_mut();
588+
state.called = "graphics.draw_nine_slice";
589+
590+
// Retrieve the raw data from memory.
591+
let state = caller.data_mut();
592+
let Some(memory) = state.memory else {
593+
state.log_error(HostError::MemoryNotFound);
594+
return;
595+
};
596+
let (data, state) = memory.data_and_store_mut(&mut caller);
597+
let Some(image_bytes) = load_image(state, data, ptr, len) else {
598+
return;
599+
};
600+
601+
let width = u16::from_le_bytes([image_bytes[1], image_bytes[2]]) as u32;
602+
if width == 0 {
603+
state.log_error("image has zero width");
604+
return;
605+
}
606+
let transp = u8::from_le_bytes([image_bytes[3]]);
607+
let image_bytes = &image_bytes[IMG_HEADER..];
608+
if !(image_bytes.len() * 2).is_multiple_of(width as usize) {
609+
state.log_error(HostError::InvalidWidth);
610+
return;
611+
}
612+
613+
// TODO: support canvas
614+
if state.canvas.is_some() {
615+
state.log_error("images cannot be drawn on canvas yet");
616+
return;
617+
}
618+
619+
let height = image_bytes.len() as u32 * 2 / width;
620+
let mut image = ParsedImage {
621+
bytes: image_bytes,
622+
width,
623+
transp,
624+
sub: None,
625+
};
626+
627+
// Make sure that all segments of 9-slice fully fit into the area.
628+
// This limitation might be relaxed or removed in the future
629+
// when the implementation handles such cases correctly
630+
// (if this can be done without losing performance).
631+
if width > w as u32 {
632+
state.log_error("9-slice width is bigger than width of the target area");
633+
return;
634+
}
635+
if height > h as u32 {
636+
state.log_error("9-slice height is bigger than width of the target area");
637+
return;
638+
}
639+
if !(w as u32 - width).is_multiple_of(mid_width) {
640+
state.log_error("drawing area width cannot be tiled with 9-slice middle segment");
641+
return;
642+
}
643+
if !(h as u32 - height).is_multiple_of(mid_height) {
644+
state.log_error("drawing area height cannot be tiled with 9-slice middle segment");
645+
return;
646+
}
647+
648+
// Segment corners.
649+
let x1 = 0i32;
650+
// TODO: handle mid_x being outside the image.
651+
let x2 = mid_x;
652+
let x3 = x2 + mid_width as i32;
653+
let x4 = width as i32;
654+
let y1 = 0i32;
655+
let y2 = mid_y;
656+
let y3 = y2 + mid_height as i32;
657+
let y4 = height as i32;
658+
659+
// Segment sizes.
660+
let wl = (x2 - x1) as u32;
661+
let wm = (x3 - x2) as u32;
662+
let wr = (x4 - x3) as u32;
663+
let ht = (y2 - y1) as u32;
664+
let hm = (y3 - y2) as u32;
665+
let hb = (y4 - y3) as u32;
666+
667+
// Top-left corner.
668+
{
669+
let size = Size::new(wl, ht);
670+
let point = Point::new(x1, y1);
671+
image.sub = Some(Rectangle::new(point, size));
672+
let point = Point::new(x, y);
673+
image.render(point, &mut state.frame);
674+
}
675+
// Top-right corner.
676+
{
677+
let size = Size::new(wr, ht);
678+
let point = Point::new(x3, y1);
679+
image.sub = Some(Rectangle::new(point, size));
680+
let point = Point::new(x + w - wr as i32, y);
681+
image.render(point, &mut state.frame);
682+
}
683+
// Bottom-left corner.
684+
{
685+
let size = Size::new(wl, hb);
686+
let point = Point::new(x1, y3);
687+
image.sub = Some(Rectangle::new(point, size));
688+
let point = Point::new(x, y + h - hb as i32);
689+
image.render(point, &mut state.frame);
690+
}
691+
// Bottom-right corner.
692+
{
693+
let size = Size::new(wr, hb);
694+
let point = Point::new(x3, y3);
695+
image.sub = Some(Rectangle::new(point, size));
696+
let point = Point::new(x + w - wr as i32, y + h - hb as i32);
697+
image.render(point, &mut state.frame);
698+
}
699+
700+
// Top edge.
701+
let range_x = (x + mid_x..x + w - wr as i32).step_by(mid_width as usize);
702+
{
703+
let size = Size::new(wm, ht);
704+
let point = Point::new(x2, y1);
705+
image.sub = Some(Rectangle::new(point, size));
706+
for sx in range_x.clone() {
707+
image.render(Point::new(sx, y), &mut state.frame);
708+
}
709+
}
710+
// Bottom edge.
711+
{
712+
let size = Size::new(wm, hb);
713+
let point = Point::new(x2, y3);
714+
image.sub = Some(Rectangle::new(point, size));
715+
let sy = y + h - hb as i32;
716+
for sx in range_x.clone() {
717+
image.render(Point::new(sx, sy), &mut state.frame);
718+
}
719+
}
720+
// Left edge.
721+
let range_y = (y + mid_y..y + h - hb as i32).step_by(mid_height as usize);
722+
{
723+
let size = Size::new(wl, hm);
724+
let point = Point::new(x1, y2);
725+
image.sub = Some(Rectangle::new(point, size));
726+
for sy in range_y.clone() {
727+
image.render(Point::new(x, sy), &mut state.frame);
728+
}
729+
}
730+
// Right edge.
731+
{
732+
let size = Size::new(wr, hm);
733+
let point = Point::new(x3, y2);
734+
image.sub = Some(Rectangle::new(point, size));
735+
let sx = x + w - wr as i32;
736+
for sy in range_y.clone() {
737+
image.render(Point::new(sx, sy), &mut state.frame);
738+
}
739+
}
740+
741+
// Middle.
742+
{
743+
let size = Size::new(wm, hm);
744+
let point = Point::new(x2, y2);
745+
image.sub = Some(Rectangle::new(point, size));
746+
for sy in range_y {
747+
for sx in range_x.clone() {
748+
image.render(Point::new(sx, sy), &mut state.frame);
749+
}
750+
}
751+
}
752+
}
753+
569754
pub(crate) fn draw_sub_image(
570755
mut caller: C,
571756
ptr: u32,

src/linking.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ fn select_graphics_external<'a>(
8888
let func = match fn_name {
8989
"clear_screen" => Func::wrap(ctx, graphics::clear_screen),
9090
"set_color" => Func::wrap(ctx, graphics::set_color),
91+
"set_canvas" => Func::wrap(ctx, graphics::set_canvas),
92+
"unset_canvas" => Func::wrap(ctx, graphics::unset_canvas),
93+
94+
// Primitives (shapes).
9195
"draw_point" => Func::wrap(ctx, graphics::draw_point),
9296
"draw_line" => Func::wrap(ctx, graphics::draw_line),
9397
"draw_rect" => Func::wrap(ctx, graphics::draw_rect),
@@ -97,13 +101,14 @@ fn select_graphics_external<'a>(
97101
"draw_triangle" => Func::wrap(ctx, graphics::draw_triangle),
98102
"draw_arc" => Func::wrap(ctx, graphics::draw_arc),
99103
"draw_sector" => Func::wrap(ctx, graphics::draw_sector),
104+
105+
// Binary content from RAM (text, images, etc).
100106
"draw_qr" => Func::wrap(ctx, graphics::draw_qr),
101107
"draw_text" => Func::wrap(ctx, graphics::draw_text),
102108
"draw_image" => Func::wrap(ctx, graphics::draw_image),
109+
"draw_nine_slice" => Func::wrap(ctx, graphics::draw_nine_slice),
103110
"draw_sub_tile" => Func::wrap(ctx, graphics::draw_sub_tile),
104111
"draw_sub_image" => Func::wrap(ctx, graphics::draw_sub_image),
105-
"set_canvas" => Func::wrap(ctx, graphics::set_canvas),
106-
"unset_canvas" => Func::wrap(ctx, graphics::unset_canvas),
107112
_ => return None,
108113
};
109114
Some(func)

0 commit comments

Comments
 (0)