@@ -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+
569754pub ( crate ) fn draw_sub_image (
570755 mut caller : C ,
571756 ptr : u32 ,
0 commit comments