From e000205b18bc4f5f7d17cd235ac8ee594d518888 Mon Sep 17 00:00:00 2001 From: Brendan Fletcher Date: Sun, 12 Apr 2026 16:44:38 -0400 Subject: [PATCH 1/2] Add overlap support for gfx_CopyRectangle and share code with gfx_BlitRectangle --- .../graphx/copy_rectangle_overlap/.gitignore | 7 + .../copy_rectangle_overlap/autotest.json | 63 ++++++ .../graphx/copy_rectangle_overlap/makefile | 15 ++ .../graphx/copy_rectangle_overlap/readme.md | 4 + .../graphx/copy_rectangle_overlap/src/main.c | 118 +++++++++++ src/graphx/graphx.asm | 184 +++++++++--------- src/graphx/graphx.h | 4 +- 7 files changed, 298 insertions(+), 97 deletions(-) create mode 100644 examples/library_examples/graphx/copy_rectangle_overlap/.gitignore create mode 100644 examples/library_examples/graphx/copy_rectangle_overlap/autotest.json create mode 100644 examples/library_examples/graphx/copy_rectangle_overlap/makefile create mode 100644 examples/library_examples/graphx/copy_rectangle_overlap/readme.md create mode 100644 examples/library_examples/graphx/copy_rectangle_overlap/src/main.c diff --git a/examples/library_examples/graphx/copy_rectangle_overlap/.gitignore b/examples/library_examples/graphx/copy_rectangle_overlap/.gitignore new file mode 100644 index 000000000..4cc0a8af5 --- /dev/null +++ b/examples/library_examples/graphx/copy_rectangle_overlap/.gitignore @@ -0,0 +1,7 @@ +obj/ +bin/ +src/gfx/*.c +src/gfx/*.h +src/gfx/*.8xv +.DS_Store +convimg.yaml.lst diff --git a/examples/library_examples/graphx/copy_rectangle_overlap/autotest.json b/examples/library_examples/graphx/copy_rectangle_overlap/autotest.json new file mode 100644 index 000000000..1bd4d3884 --- /dev/null +++ b/examples/library_examples/graphx/copy_rectangle_overlap/autotest.json @@ -0,0 +1,63 @@ +{ + "transfer_files": + [ + "bin/DEMO.8xp" + ], + "target": + { + "name": "DEMO", + "isASM": true + }, + "sequence": + [ + "action|launch", + "delay|300", + "hashWait|1", + "key|enter", + "hashWait|2", + "key|enter", + "hashWait|3", + "key|enter", + "hashWait|4", + "key|enter", + "hashWait|5" + ], + "hashes": + { + "1": + { + "description": "Horizontal overlap right", + "start": "vram_start", + "size": "vram_8_size", + "expected_CRCs": [ "9D4993C1" ] + }, + "2": + { + "description": "Horizontal overlap left", + "start": "vram_start", + "size": "vram_8_size", + "expected_CRCs": [ "BBD76F1C" ] + }, + "3": + { + "description": "Vertical overlap down", + "start": "vram_start", + "size": "vram_8_size", + "expected_CRCs": [ "7C8FC7BE" ] + }, + "4": + { + "description": "Vertical overlap up", + "start": "vram_start", + "size": "vram_8_size", + "expected_CRCs": [ "1C46A70A" ] + }, + "5": + { + "description": "Test program exit", + "start": "vram_start", + "size": "vram_16_size", + "expected_CRCs": [ "FFAF89BA", "101734A5", "9DA19F44", "A32840C8", "349F4775" ] + } + } +} diff --git a/examples/library_examples/graphx/copy_rectangle_overlap/makefile b/examples/library_examples/graphx/copy_rectangle_overlap/makefile new file mode 100644 index 000000000..b869526cc --- /dev/null +++ b/examples/library_examples/graphx/copy_rectangle_overlap/makefile @@ -0,0 +1,15 @@ +# ---------------------------- +# Makefile Options +# ---------------------------- + +NAME = DEMO +ICON = icon.png +DESCRIPTION = "CE C Toolchain Demo" +COMPRESSED = NO + +CFLAGS = -Wall -Wextra -Oz +CXXFLAGS = -Wall -Wextra -Oz + +# ---------------------------- + +include $(shell cedev-config --makefile) diff --git a/examples/library_examples/graphx/copy_rectangle_overlap/readme.md b/examples/library_examples/graphx/copy_rectangle_overlap/readme.md new file mode 100644 index 000000000..780377af5 --- /dev/null +++ b/examples/library_examples/graphx/copy_rectangle_overlap/readme.md @@ -0,0 +1,4 @@ +### Copy Rectangle Overlap Demo + +This example demonstrates `gfx_CopyRectangle` on overlapping regions of the same buffer. +It shows horizontal and vertical moves in both directions using memmove-like behavior. diff --git a/examples/library_examples/graphx/copy_rectangle_overlap/src/main.c b/examples/library_examples/graphx/copy_rectangle_overlap/src/main.c new file mode 100644 index 000000000..a3882e892 --- /dev/null +++ b/examples/library_examples/graphx/copy_rectangle_overlap/src/main.c @@ -0,0 +1,118 @@ +#include +#include +#include + +static void draw_base_pattern(void) +{ + uint8_t stripe; + + gfx_FillScreen(239); + + gfx_SetColor(26); + gfx_FillRectangle_NoClip(0, 0, GFX_LCD_WIDTH, 14); + gfx_SetColor(27); + gfx_FillRectangle_NoClip(0, GFX_LCD_HEIGHT - 12, GFX_LCD_WIDTH, 12); + + for (stripe = 0; stripe < 6; stripe++) + { + static const uint8_t colors[6] = { 224, 9, 27, 56, 186, 250 }; + uint24_t x = 24 + stripe * 40; + + gfx_SetColor(colors[stripe]); + gfx_FillRectangle_NoClip(x, 24, 40, 120); + + gfx_SetColor(0); + gfx_VertLine_NoClip(x, 24, 120); + gfx_VertLine_NoClip(x + 39, 24, 120); + + gfx_SetColor(255); + gfx_FillRectangle_NoClip(x + 6, 34 + stripe * 12, 28, 6); + gfx_FillRectangle_NoClip(x + 6, 108 - stripe * 10, 28, 6); + } + + gfx_SetColor(0); + gfx_Rectangle_NoClip(23, 23, 241, 121); + + gfx_SetTextFGColor(255); + gfx_SetTextBGColor(26); + gfx_PrintStringXY("gfx_CopyRectangle overlap demo", 6, 3); + + gfx_SetTextFGColor(255); + gfx_SetTextBGColor(27); + gfx_PrintStringXY("Black border = SRC White border = DST", 6, GFX_LCD_HEIGHT - 10); +} + +static void draw_overlay(const char *title, + uint24_t src_x, + uint8_t src_y, + uint24_t dst_x, + uint8_t dst_y, + uint24_t width, + uint8_t height) +{ + gfx_SetColor(0); + gfx_Rectangle_NoClip(src_x - 1, src_y - 1, width + 2, height + 2); + + gfx_SetColor(255); + gfx_Rectangle_NoClip(dst_x - 1, dst_y - 1, width + 2, height + 2); + + gfx_SetColor(255); + gfx_FillRectangle_NoClip(src_x + 2, src_y + 2, 24, 10); + gfx_SetColor(0); + gfx_Rectangle_NoClip(src_x + 2, src_y + 2, 24, 10); + gfx_SetTextFGColor(0); + gfx_SetTextBGColor(255); + gfx_PrintStringXY("SRC", src_x + 4, src_y + 3); + + gfx_SetColor(255); + gfx_FillRectangle_NoClip(dst_x + 2, dst_y + 2, 24, 10); + gfx_SetColor(0); + gfx_Rectangle_NoClip(dst_x + 2, dst_y + 2, 24, 10); + gfx_SetTextFGColor(0); + gfx_SetTextBGColor(255); + gfx_PrintStringXY("DST", dst_x + 4, dst_y + 3); + + gfx_SetColor(52); + gfx_FillRectangle_NoClip(188, 2, 130, 10); + gfx_SetTextFGColor(255); + gfx_SetTextBGColor(52); + gfx_PrintStringXY(title, 192, 3); +} + +static void show_stage(void) +{ + gfx_BlitBuffer(); + while (!os_GetCSC()) + { + } +} + +int main(void) +{ + gfx_Begin(); + gfx_SetDrawBuffer(); + + draw_base_pattern(); + gfx_CopyRectangle(gfx_buffer, gfx_buffer, 24, 18, 72, 18, 180, 64); + draw_overlay("Move right", 24, 18, 72, 18, 180, 64); + show_stage(); + + draw_base_pattern(); + gfx_CopyRectangle(gfx_buffer, gfx_buffer, 72, 18, 24, 18, 180, 64); + draw_overlay("Move left", 72, 18, 24, 18, 180, 64); + show_stage(); + + draw_base_pattern(); + gfx_CopyRectangle(gfx_buffer, gfx_buffer, 30, 24, 30, 60, 128, 96); + draw_overlay("Move down", 30, 24, 30, 60, 128, 96); + show_stage(); + + draw_base_pattern(); + gfx_CopyRectangle(gfx_buffer, gfx_buffer, 30, 60, 30, 24, 128, 96); + draw_overlay("Move up", 30, 60, 30, 24, 128, 96); + show_stage(); + + gfx_End(); + + return 0; +} diff --git a/src/graphx/graphx.asm b/src/graphx/graphx.asm index d48675bfa..3f7214f29 100644 --- a/src/graphx/graphx.asm +++ b/src/graphx/graphx.asm @@ -1058,25 +1058,17 @@ gfx_SetDraw: ; arg0: buffer or screen ; Returns: ; None - pop de - ex (sp), hl - ld a, l - or a, a - ld hl, (ti.mpLcdBase) ; get current base - ld bc, ti.vRam - jr z, .match - sbc hl, bc - jr nz, .swap ; if not the same, swap -.set: - ld bc, ti.vRam + LcdSize -.swap: - ld (CurrentBuffer), bc + ld hl, 3 + add hl, sp + dec (hl) + ld hl, (ti.mpLcdBase) + jr nz, .screen ex de, hl - jp (hl) -.match: - sbc hl, bc - jr z, .swap ; if the same, swap - jr .set + ld hl, (ti.vRam + (ti.vRam + LcdSize)) and $FFFFFF + sbc hl, de +.screen: + ld (CurrentBuffer), hl + ret ;------------------------------------------------------------------------------- gfx_GetDraw: @@ -1085,8 +1077,10 @@ gfx_GetDraw: ; None ; Returns: ; Returns true if drawing on the buffer - ld a, (ti.mpLcdBase+2) ; comparing upper byte only is sufficient - ld hl, CurrentBuffer+2 + ld hl, CurrentBuffer+2 ; comparing upper byte only is sufficient + ld a, (hl) + assert CurrentBuffer and -$100 = ti.mpLcdRange + ld l, ti.lcdBase+2 xor a, (hl) ; always 0 or 1 ret @@ -2242,7 +2236,7 @@ gfx_BlitLines: mlt hl add hl, hl ; hl -> offset to start at push hl - call util.getbuffer ; determine blit buffers + call util.getbuffer.no_carry ; determine blit buffers pop bc add hl, bc ex de, hl @@ -2264,45 +2258,42 @@ gfx_BlitRectangle: ; None ld iy, 0 add iy, sp - ld de, (iy + 6) ; de = x coordinate - ld l, (iy + 9) ; l = y coordinate - ld h, ti.lcdWidth / 2 - mlt hl - add hl, hl - add hl, de ; hl = amount to increment - push hl ; save amount to increment ld a, (iy + 3) ; a = buffer to blit from - call util.getbuffer ; determine blit buffers - pop bc - add hl, bc + call util.getbuffer.no_carry ; determine blit buffers + sbc hl, de ex de, hl + ld bc, (iy + 6) ; bc = x coordinate add hl, bc - ex de, hl - ld bc, (iy + 12) ; the width of things - ld (.width), bc + ld c, (iy + 9) ; c = y coordinate + ld b, ti.lcdWidth / 2 + mlt bc + add hl, bc + add hl, bc + ex de, hl ; de = start of copy dst + ld a, (iy + 15) ; a = rectangle height + ld iy, (iy + 12) ; iy = rectangle width +.copy_forward_add: + add hl, de ; hl = start of copy src +.copy_forward: push hl + lea bc, iy ld hl, ti.lcdWidth - or a, a - sbc hl, bc ; change in width for rectangle - ld (.delta), hl - pop hl - ld a, (iy + 15) - ld iy, 0 - add iy, de + or a,a + sbc hl, bc + ex (sp), hl + ex (sp), ix ; ix = rectangle stride call gfx_Wait .loop: - ld bc, 0 ; smc for speedz -.width := $-3 + lea bc, iy ; copy width ldir - inc b - ld c, $40 ; increment to next line - add iy, bc - lea de, iy - ld bc, 0 ; increment to next line -.delta := $-3 + lea bc, ix ; increment to next line add hl, bc + ex de, hl + add hl, bc + ex de, hl dec a jr nz, .loop + pop ix ret ;------------------------------------------------------------------------------- @@ -2321,54 +2312,60 @@ gfx_CopyRectangle: ; None ld iy, 0 add iy, sp - ld de, (iy + 9) ; de = x coordinate src - ld l, (iy + 12) ; l = y coordinate src - ld h, ti.lcdWidth / 2 - mlt hl - add hl, hl - add hl, de ; hl = offset in src - push hl - ld a, (iy + 3) ; a = buffer src - call util.getbuffer - pop bc - add hl, bc ; hl = start of copy src - push hl - ld de, (iy + 15) ; de = x coordinate dst - ld l, (iy + 18) ; l = y coordinate dst - ld h, ti.lcdWidth / 2 - mlt hl - add hl, hl - add hl, de ; hl = offset in dst - push hl ld a, (iy + 6) ; a = buffer dst - call util.getbuffer - pop bc + call util.getbuffer.no_carry ; hl = dst buffer, de = opposite buffer + cp a, (iy + 3) ; compare to buffer src + jr nz, .different_buffers + push hl + pop de +.different_buffers: ; de = src buffer + ld bc, (iy + 15) ; bc = x coordinate dst + add hl, bc + ld c, (iy + 18) ; c = y coordinate dst + ld b, ti.lcdWidth / 2 + mlt bc + add hl, bc add hl, bc ex de, hl ; de = start of copy dst - ld bc, (iy + 21) ; rectangle width - ld (.width), bc - ld hl, ti.lcdWidth - or a, a - sbc hl, bc ; rectangle stride - ld (.stride), hl - pop hl ; hl = start of copy src - ld a, (iy + 24) - ld iy, 0 - add iy, de + ld bc, (iy + 9) ; bc = x coordinate src + add hl, bc + ld c, (iy + 12) ; c = y coordinate src + ld b, ti.lcdWidth / 2 + mlt bc + add hl, bc + add hl, bc ; hl = start of copy src + ld a, (iy + 24) ; a = rectangle height + ld iy, (iy + 21) ; iy = rectangle width + ; always copy forward between different buffers to reduce tearing + jr nz, gfx_BlitRectangle.copy_forward + ; copy forward if src offset >= dst offset + sbc hl, de + jr nc, gfx_BlitRectangle.copy_forward_add + ld c, a ; c = rectangle height + ld b, ti.lcdWidth / 2 + mlt bc + ex de, hl + add hl, bc + add hl, bc + dec hl + ex de, hl ; de = end of copy dst + add hl, de ; hl = end of copy src + push ix + lea ix, iy + ld bc, -ti.lcdWidth + add ix, bc ; ix = negative rectangle stride call gfx_Wait .loop: - ld bc, 0 ; smc for speedz -.width := $-3 - ldir - inc b - ld c, $40 - add iy, bc - lea de, iy - ld bc, 0 ; increment to next line -.stride := $-3 + lea bc, ix ; decrement to previous line + add hl, bc + ex de, hl add hl, bc + ex de, hl + lea bc, iy ; copy width + lddr dec a jr nz, .loop + pop ix ret ;------------------------------------------------------------------------------- @@ -6893,14 +6890,11 @@ smcWord _YMaxMinus1 ;------------------------------------------------------------------------------- util.getbuffer: - ld hl, ti.vRam + LcdSize - ld de, (ti.mpLcdBase) or a, a +.no_carry: + ld hl, (ti.vRam + (ti.vRam + LcdSize)) and $FFFFFF + ld de, (ti.mpLcdBase) sbc hl, de - add hl, de - jr nz, .check - ld hl, ti.vRam -.check: or a, a ; if 0, copy buffer to screen ret nz ex de, hl diff --git a/src/graphx/graphx.h b/src/graphx/graphx.h index bd46e82cb..fb08e6f61 100644 --- a/src/graphx/graphx.h +++ b/src/graphx/graphx.h @@ -1148,9 +1148,9 @@ void gfx_BlitRectangle(gfx_location_t src, /** * Copies a rectangular region between graphics buffers or to the same graphics buffer. - * The behavior is undefined when the rectangles overlap. + * Overlapping rectangles are supported, using memmove-like copy semantics. * No clipping is performed as it is a copy not a draw. - * @param[in] src Graphics buffer to copy from.i + * @param[in] src Graphics buffer to copy from. * @param[in] dst Graphics buffer to copy to. * @param[in] src_x X coordinate on src. * @param[in] src_y Y coordinate on src. From 82ca257e399237ccaba8ece55d9d83cd0a7adaeb Mon Sep 17 00:00:00 2001 From: zerico <71151164+ZERICO2005@users.noreply.github.com> Date: Thu, 30 Apr 2026 14:00:43 -0600 Subject: [PATCH 2/2] bump GRAPHX version (due to gfx_CopyRectangle) --- src/graphx/graphx.asm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graphx/graphx.asm b/src/graphx/graphx.asm index 3f7214f29..a9cc7aee2 100644 --- a/src/graphx/graphx.asm +++ b/src/graphx/graphx.asm @@ -2,7 +2,7 @@ include '../include/library.inc' ;------------------------------------------------------------------------------- -library GRAPHX, 13 +library GRAPHX, 14 ;------------------------------------------------------------------------------- ; no dependencies