diff --git a/examples/graphx/shapes/autotest.json b/examples/graphx/shapes/autotest.json index b72820457..b09931c22 100644 --- a/examples/graphx/shapes/autotest.json +++ b/examples/graphx/shapes/autotest.json @@ -22,10 +22,12 @@ "key|enter", "hashWait|5", "key|enter", - "delay|2000", "hashWait|6", "key|enter", - "hashWait|7" + "delay|2000", + "hashWait|7", + "key|enter", + "hashWait|8" ], "hashes": { @@ -37,41 +39,48 @@ "expected_CRCs": [ "8DE8E699" ] }, "2": + { + "description": "Test ellipse display", + "start": "vram_start", + "size": "vram_8_size", + "expected_CRCs": [ "2138C183" ] + }, + "3": { "description": "Test rectangle outline display", "start": "vram_start", "size": "vram_8_size", "expected_CRCs": [ "6390F38B" ] }, - "3": + "4": { "description": "Test rectangle fill display", "start": "vram_start", "size": "vram_8_size", "expected_CRCs": [ "163F3B60" ] }, - "4": + "5": { "description": "Test triangle fill display", "start": "vram_start", "size": "vram_8_size", "expected_CRCs": [ "A1154AE5" ] }, - "5": + "6": { "description": "Test line display", "start": "vram_start", "size": "vram_8_size", "expected_CRCs": [ "08AE93E1" ] }, - "6": + "7": { "description": "Test pixel display", "start": "vram_start", "size": "vram_8_size", "expected_CRCs": [ "B6CCDD04" ] }, - "7": + "8": { "description": "Test program exit", "start": "vram_start", diff --git a/examples/graphx/shapes/src/main.c b/examples/graphx/shapes/src/main.c index 0a4b3f78b..4fa3a96a1 100644 --- a/examples/graphx/shapes/src/main.c +++ b/examples/graphx/shapes/src/main.c @@ -38,6 +38,25 @@ int main(void) y += i + 3; } + /* Waits for a key */ + while (!os_GetCSC()); + + /* Clear the screen */ + gfx_FillScreen(255); + + /* Ellipse Drawing */ + gfx_SetColor(56); + for (i = 10; i < 50; i += 2) + { + gfx_Ellipse_NoClip(100, 100, i, 40); + } + gfx_SetColor(195); + gfx_Ellipse(300, 35, 140, 80); + gfx_SetColor(5); + gfx_FillEllipse_NoClip(200, 150, 120, 50); + gfx_SetColor(210); + gfx_FillEllipse(25, 200, 60, 90); + /* Waits for a key */ while (!os_GetCSC()); diff --git a/src/graphx/graphx.asm b/src/graphx/graphx.asm index d41307d07..c74949107 100644 --- a/src/graphx/graphx.asm +++ b/src/graphx/graphx.asm @@ -2,7 +2,7 @@ include '../include/library.inc' ;------------------------------------------------------------------------------- -library 'GRAPHX', 11 +library 'GRAPHX', 12 ;------------------------------------------------------------------------------- ; no dependencies @@ -131,8 +131,19 @@ library 'GRAPHX', 11 export gfx_Wait ;------------------------------------------------------------------------------- ; v10 functions +;------------------------------------------------------------------------------- + +;------------------------------------------------------------------------------- +; v11 functions ;------------------------------------------------------------------------------- export gfx_CopyRectangle +;------------------------------------------------------------------------------- +; v12 functions +;------------------------------------------------------------------------------- + export gfx_Ellipse + export gfx_Ellipse_NoClip + export gfx_FillEllipse + export gfx_FillEllipse_NoClip ;------------------------------------------------------------------------------- LcdSize := ti.lcdWidth*ti.lcdHeight @@ -636,6 +647,7 @@ _SetPixel_NoWait: ld hl,-ti.lcdHeight add hl,de ret c ; return if out of bounds +_SetPixel_NoClip_NoWait: ld hl,(CurrentBuffer) add hl,bc ld d,ti.lcdWidth / 2 @@ -1135,6 +1147,375 @@ assert .LcdSizeH and ti.lcdIntLNBU dec sp ; sp -= 3 to match pop hl later jr _WriteWaitQuickSMC +;------------------------------------------------------------------------------- +gfx_FillEllipse_NoClip: + ld hl,gfx_HorizLine_NoClip + db $FD ; ld hl,* -> ld iy,* + +;------------------------------------------------------------------------------- +gfx_FillEllipse: + ld hl,gfx_HorizLine + ld (_ellipse_line_routine_1),hl + ld (_ellipse_line_routine_2),hl + ld hl,_ellipse_draw_line + ld (_ellipse_loop_draw_2),hl + ld (_ellipse_loop_draw_3),hl + ld hl,_ellipse_ret + ld (_ellipse_loop_draw_1),hl + jr _Ellipse + +;------------------------------------------------------------------------------- +gfx_Ellipse_NoClip: + ld hl,_SetPixel_NoClip_NoWait + db $FD ; ld hl,* -> ld iy,* + +;------------------------------------------------------------------------------- +gfx_Ellipse: + ld hl,_SetPixel_NoWait + ld (_ellipse_pixel_routine_1),hl + ld (_ellipse_pixel_routine_2),hl + ld (_ellipse_pixel_routine_3),hl + ld (_ellipse_pixel_routine_4),hl + ld hl,_ellipse_draw_pixels + ld (_ellipse_loop_draw_1),hl + ld (_ellipse_loop_draw_3),hl + ld hl,_ellipse_ret + ld (_ellipse_loop_draw_2),hl + +el_x := 3 ; Current X coordinate of the ellipse +el_y := 6 ; Current Y coordinate of the ellipse +el_a2 := 9 ; X radius squared +el_b2 := 12 ; Y radius squared +el_fa2 := 15 ; X radius squared * 4 +el_fb2 := 18 ; Y radius squared * 4 +el_sigma := 21 ; Sigma +el_sigma_1 := 24 ; Offset to be added to sigma in loop 1 +el_sigma_2 := 27 ; Offset to be added to sigma in loop 2 +el_temp1 := 30 ; X radius as "int" instead of "uint8_t" +el_comp_a := 33 ; b2 * x +el_comp_b := 36 ; a2 * y +el_sigma_diff1 := 39 ; Offset to be added to sigma in loop 1 +el_sigma_diff2 := 42 ; Offset to be added to sigma in loop 2 + +_Ellipse: +; Draws an ellipse, either filled or not, either clipped or not +; Arguments: +; arg0 : X coordinate (ix+6) +; arg1 : Y coordinate (ix+9) +; arg2 : X radius (ix+12) +; arg3 : Y radius (ix+15) +; Returns: +; None + push ix + ld ix,0 + add ix,sp + lea hl,ix - 42 + ld sp,hl + +; First, setup all the variables + ld a,(ix + 12) + or a,a + jr nz,.valid_x_radius +.return: + ld sp,ix + pop ix + ret +.valid_x_radius: + ld l,a + ld h,a + mlt hl + ld (ix - el_a2),hl ; int a2 = a * a; + add hl,hl + ld (ix - el_sigma_diff2),hl; Save a2 * 2 for later + add hl,hl + ld (ix - el_fa2),hl ; int fa2 = 4 * a2; + ld a,(ix + 15) + or a,a + jr z,.return ; Make sure Y radius is not 0 + ld e,a + ld d,1 + mlt de + ld (ix - el_y),de ; int y = b; + ld hl,(ix - el_a2) + ld d,l + ld l,e + mlt de + mlt hl + add hl,hl + add hl,hl + add hl,hl + add hl,hl + add hl,hl + add hl,hl + add hl,hl + add hl,hl + add hl,de + add hl,hl + add hl,hl + ex de,hl + ld hl,(ix - el_fa2) + or a,a + sbc hl,de + ld (ix - el_sigma_1),hl ; int sigma_add_1 = fa2 * (1 - b); + ld l,a + ld h,a + mlt hl + ld (ix - el_b2),hl ; int b2 = b * b; + add hl,hl + ld (ix - el_sigma_diff1),hl ; Save b2 * 2 for later + add hl,hl + ld (ix - el_fb2),hl ; int fb2 = 4 * b2; + ld c,a + ld b,2 + mlt bc + or a,a + sbc hl,hl + ld (ix - el_x),hl ; int x = 0; + ld (ix - el_comp_a),hl + inc hl + sbc hl,bc + ld bc,(ix - el_a2) + call _MultiplyHLBC + ld bc,(ix - el_b2) + add hl,bc + add hl,bc + ld (ix - el_sigma),hl ; int sigma = 2 * b2 + a2 * (1 - 2 * b); + ld e,(ix + 12) + ld d,1 + mlt de + ld (ix - el_temp1),de ; Save int a for later + or a,a + sbc hl,hl + inc hl + sbc hl,de + ld bc,(ix - el_fb2) + call _MultiplyHLBC + ld (ix - el_sigma_2),hl ; int sigma_add_2 = fb2 * (1 - a); + + ld hl,(ix - el_a2) + ld bc,(ix - el_y) + call _MultiplyHLBC + ld (ix - el_comp_b),hl + + wait_quick + +.main_loop1: + call 0 +_ellipse_loop_draw_1 := $-3 + +; Eventually change sigma and y + ld hl,(ix - el_sigma) + add hl,hl + jr c,.loop1_jump ; if (sigma >= 0) { + + call 0 +_ellipse_loop_draw_2 := $-3 + + ld hl,(ix - el_sigma) ; sigma += sigma_add_1; + ld de,(ix - el_sigma_1) + add hl,de + ld (ix - el_sigma),hl + ld hl,(ix - el_fa2) + add hl,de + ld (ix - el_sigma_1),hl ; sigma_add_1 += fa2; + ld hl, (ix - el_y) + dec hl + ld (ix - el_y),hl ; y--; + ld hl,(ix - el_comp_b) + ld de,(ix - el_a2) + or a,a + sbc hl,de + ld (ix - el_comp_b),hl +.loop1_jump: ; } +; Change sigma and increment x + ld hl,(ix - el_sigma_diff1) + ld de,(ix - el_fb2) + add hl,de + ld (ix - el_sigma_diff1),hl + ld de,(ix - el_sigma) + add hl,de + ld (ix - el_sigma),hl ; sigma += b2 * (4 * x + 6); + ld hl,(ix - el_x) + inc hl + ld (ix - el_x),hl ; x++; + +; Update the comparison operands + ld hl,(ix - el_comp_a) + ld de,(ix - el_b2) + add hl,de + ld (ix - el_comp_a),hl + ld de,(ix - el_comp_b) + +; And compare + ld bc,0x800000 ; b2 * x <= a2 * y so hl <= de + add hl,bc + ex de,hl ; de <= hl + add hl,bc + or a,a + sbc hl,de + jq nc,.main_loop1 + +; Update few variables for the next loop + ld hl, (ix - el_temp1) + ld (ix - el_x),hl ; x = a + ld e,l + or a,a + sbc hl,hl + ld (ix - el_y),hl ; y = 0 + ld (ix - el_comp_a),hl + ld d,2 + mlt de + inc hl + sbc hl,de + ld bc,(ix - el_b2) + call _MultiplyHLBC + ld de,(ix - el_a2) + add hl,de + add hl,de + ld (ix - el_sigma), hl + + ld hl,(ix - el_b2) + ld bc,(ix - el_temp1) + call _MultiplyHLBC + ld (ix - el_comp_b),hl + +.main_loop2: + call 0 +_ellipse_loop_draw_3 := $-3 + +; Eventually update sigma and x + ld hl,(ix - el_sigma) + add hl,hl + jr c,.loop2_jump ; if (sigma >= 0) { + ld hl,(ix - el_sigma) + ld de,(ix - el_sigma_2) + add hl,de + ld (ix - el_sigma),hl ; sigma += sigma_add_2; + ld hl,(ix - el_fb2) + add hl,de + ld (ix - el_sigma_2),hl ; sigma_add_2 += fb2; + ld hl, (ix - el_x) + dec hl + ld (ix - el_x),hl ; x--; + ld hl,(ix - el_comp_b) + ld de,(ix - el_b2) + or a,a + sbc hl,de + ld (ix - el_comp_b),hl +.loop2_jump: +; Change sigma and increment y + ld hl,(ix - el_sigma_diff2) + ld de,(ix - el_fa2) + add hl,de + ld (ix - el_sigma_diff2),hl + ld de,(ix - el_sigma) + add hl,de + ld (ix - el_sigma),hl ; sigma += a2 * (4 * y + 6); + ld hl,(ix - el_y) + inc hl + ld (ix - el_y),hl ; y++; + ld hl,(ix - el_comp_a) + ld de,(ix - el_a2) + add hl,de + ld (ix - el_comp_a),hl + +; Compare the boolean operators + ld de,(ix - el_comp_b) + ld bc,0x800000 + add hl,bc + ex de,hl + add hl,bc + or a,a + sbc hl,de + jq nc,.main_loop2 + + ld sp,ix + pop ix +_ellipse_ret: + ret + +_ellipse_draw_pixels: +; bc = x coordinate +; e = y coordinate + ld hl,(ix + 9) + ld de,(ix - el_y) + add hl,de + ex de,hl + push de + ld hl,(ix + 6) + ld bc,(ix - el_x) + add hl,bc + push hl + pop bc + call _SetPixel_NoWait ; xc + x, yc + y +_ellipse_pixel_routine_1 := $-3 + pop de + ld hl,(ix + 6) + ld bc,(ix - el_x) + or a,a + sbc hl,bc + push hl + pop bc + push bc + call _SetPixel_NoWait ; xc - x, yc + y +_ellipse_pixel_routine_2 := $-3 + pop bc + ld hl,(ix + 9) + ld de,(ix - el_y) + or a,a + sbc hl,de + ex de,hl + push de + call _SetPixel_NoWait ; xc - x, yc - y +_ellipse_pixel_routine_3 := $-3 + pop de + ld hl,(ix + 6) + ld bc,(ix - el_x) + add hl,bc + push hl + pop bc + jp _SetPixel_NoWait ; xc + x, yc - y +_ellipse_pixel_routine_4 := $-3 + +_ellipse_draw_line: + ld hl,(ix - el_x) + add hl,hl + push hl + ld hl,(ix + 9) + ld de,(ix - el_y) + or a,a + sbc hl,de + push hl + ld hl,(ix + 6) + ld de,(ix - el_x) + or a,a + sbc hl,de + push hl + call 0 +_ellipse_line_routine_1 := $-3 + pop hl + pop hl + pop hl + ld hl,(ix - el_x) + add hl,hl + push hl + ld hl,(ix + 9) + ld de,(ix - el_y) + add hl,de + push hl + ld hl,(ix + 6) + ld de,(ix - el_x) + or a,a + sbc hl,de + push hl + call 0 +_ellipse_line_routine_2 := $-3 + pop hl + pop hl + pop hl + ret + + ;------------------------------------------------------------------------------- gfx_Circle: ; Draws a clipped circle outline diff --git a/src/graphx/graphx.h b/src/graphx/graphx.h index 7dad1321a..9179ca1a6 100644 --- a/src/graphx/graphx.h +++ b/src/graphx/graphx.h @@ -35,6 +35,7 @@ * @author Jacob "jacobly" Young * @author Zachary "Runer112" Wassall * @author Patrick "tr1p1ea" Prendergast + * @author Peter "PT_" Tillema * @author "grosged" */ @@ -756,6 +757,72 @@ void gfx_FillCircle_NoClip(uint24_t x, #define gfx_Circle_NoClip(x, y, radius) \ gfx_Circle((x), (y), (radius)) +/** + * Draws an unclipped filled ellipse. + * + * This is measured from the top left origin of the screen. + * Performs faster than using gfx_FillEllipse, but can cause corruption if used outside the bounds of the screen. + * + * @warning + * When using too large radii (> 128) the upper and lower half of the ellipse might get corrupted due to + * integer overflow. + * + * @param x X coordinate of the center. + * @param y Y coordinate of the center. + * @param a The horizontal radius of the ellipse. + * @param b The vertical radius of the ellipse. + */ +void gfx_FillEllipse_NoClip(int x, int y, int a, int b); + +/** + * Draws a filled ellipse. + * + * This is measured from the top left origin of the screen. + * + * @warning + * When using too large radii (> 128) the upper and lower half of the ellipse might get corrupted due to + * integer overflow. + * + * @param x X coordinate of the center. + * @param y Y coordinate of the center. + * @param a The horizontal radius of the ellipse. + * @param b The vertical radius of the ellipse. + */ +void gfx_FillEllipse(int x, int y, int a, int b); + +/** + * Draws an unclipped ellipse. + * + * This is measured from the top left origin of the screen. + * Performs faster than using gfx_FillEllipse, but can cause corruption if used outside the bounds of the screen. + * + * @warning + * When using too large radii (> 128) the upper and lower half of the ellipse might get corrupted due to + * integer overflow. + * + * @param x X coordinate of the center. + * @param y Y coordinate of the center. + * @param a The horizontal radius of the ellipse. + * @param b The vertical radius of the ellipse. + */ +void gfx_Ellipse_NoClip(int x, int y, int a, int b); + +/** + * Draws an ellipse. + * + * This is measured from the top left origin of the screen. + * + * @warning + * When using too large radii (> 128) the upper and lower half of the ellipse might get corrupted due to + * integer overflow. + * + * @param x X coordinate of the center. + * @param y Y coordinate of the center. + * @param a The horizontal radius of the ellipse. + * @param b The vertical radius of the ellipse. + */ +void gfx_Ellipse(int x, int y, int a, int b); + /** * Draws a clipped polygon outline. *