2026-07-30 01:10:31 -04:00
|
|
|
/**
|
|
|
|
|
* @file draw.h
|
|
|
|
|
* @brief Declares the public draw API.
|
Draw shapes immediately against a renderer
BASIC 7.0's graphics verbs -- DRAW, BOX, CIRCLE, PAINT, SSHAPE and GSHAPE -- all
plot against the current screen right now rather than adding to a scene, and
draw.h had exactly one function in it. This adds akgl_draw_point, _line, _rect,
_filled_rect, _circle, _flood_fill, _copy_region and _paste_region, each taking
the akgl_RenderBackend the host already initialized.
Color is an argument rather than a current-color global, so there is no second
copy of state to disagree with the caller's own, and each call restores the
renderer's draw color when it is done. SDL3 has no circle and no flood fill: the
circle is a midpoint circle, and the fill reads the target back, walks the
region with a fixed 4096-entry span stack, and blits back only the bounding box
of what changed. Exhausting that stack reports AKERR_OUTOFBOUNDS and says in the
header that the region is left partially filled.
tests/draw.c draws into a 64x64 software renderer under the dummy video driver
and reads the pixels back, so it needs no display and no offscreen harness.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 07:05:36 -04:00
|
|
|
*
|
|
|
|
|
* Immediate-mode plotting against whichever renderer the caller hands in. This
|
|
|
|
|
* is the shape a BASIC-style graphics vocabulary needs -- DRAW, BOX, CIRCLE,
|
|
|
|
|
* PAINT, SSHAPE and GSHAPE all say "put this on the screen now" rather than
|
|
|
|
|
* "add this to the scene" -- and it sits alongside the actor and tilemap
|
|
|
|
|
* rendering rather than replacing it.
|
|
|
|
|
*
|
|
|
|
|
* Every entry point takes its color as an argument instead of reading a
|
|
|
|
|
* current-color global. A caller that has a notion of a current color (a BASIC
|
|
|
|
|
* COLOR statement, say) already owns that state and does not need the library
|
|
|
|
|
* to keep a second copy that can disagree with it. The renderer's own draw
|
|
|
|
|
* color is saved and restored around each call, so drawing a line never changes
|
|
|
|
|
* what the next SDL_RenderClear() paints.
|
2026-07-30 01:10:31 -04:00
|
|
|
*/
|
|
|
|
|
|
2025-08-03 10:07:35 -04:00
|
|
|
#ifndef _DRAW_H_
|
|
|
|
|
#define _DRAW_H_
|
|
|
|
|
|
Draw shapes immediately against a renderer
BASIC 7.0's graphics verbs -- DRAW, BOX, CIRCLE, PAINT, SSHAPE and GSHAPE -- all
plot against the current screen right now rather than adding to a scene, and
draw.h had exactly one function in it. This adds akgl_draw_point, _line, _rect,
_filled_rect, _circle, _flood_fill, _copy_region and _paste_region, each taking
the akgl_RenderBackend the host already initialized.
Color is an argument rather than a current-color global, so there is no second
copy of state to disagree with the caller's own, and each call restores the
renderer's draw color when it is done. SDL3 has no circle and no flood fill: the
circle is a midpoint circle, and the fill reads the target back, walks the
region with a fixed 4096-entry span stack, and blits back only the bounding box
of what changed. Exhausting that stack reports AKERR_OUTOFBOUNDS and says in the
header that the region is left partially filled.
tests/draw.c draws into a 64x64 software renderer under the dummy video driver
and reads the pixels back, so it needs no display and no offscreen harness.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 07:05:36 -04:00
|
|
|
#include <SDL3/SDL.h>
|
|
|
|
|
|
|
|
|
|
#include <akerror.h>
|
|
|
|
|
|
|
|
|
|
#include <akgl/renderer.h>
|
|
|
|
|
#include <akgl/types.h>
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Spans akgl_draw_flood_fill() will hold while walking a region.
|
|
|
|
|
*
|
|
|
|
|
* The fill keeps a fixed stack of horizontal runs still to be examined rather
|
|
|
|
|
* than recursing per pixel. A region complicated enough to need more than this
|
|
|
|
|
* many pending runs at once reports AKERR_OUTOFBOUNDS instead of overflowing;
|
|
|
|
|
* an ordinary convex or moderately concave shape needs a few dozen.
|
|
|
|
|
*/
|
|
|
|
|
#define AKGL_DRAW_MAX_FLOOD_SPANS 4096
|
|
|
|
|
|
2026-07-30 01:10:31 -04:00
|
|
|
/**
|
|
|
|
|
* @brief Draw background.
|
|
|
|
|
* @param w Destination width.
|
|
|
|
|
* @param h Destination height.
|
|
|
|
|
*/
|
2026-05-06 23:18:42 -04:00
|
|
|
void akgl_draw_background(int w, int h);
|
2025-08-03 10:07:35 -04:00
|
|
|
|
Draw shapes immediately against a renderer
BASIC 7.0's graphics verbs -- DRAW, BOX, CIRCLE, PAINT, SSHAPE and GSHAPE -- all
plot against the current screen right now rather than adding to a scene, and
draw.h had exactly one function in it. This adds akgl_draw_point, _line, _rect,
_filled_rect, _circle, _flood_fill, _copy_region and _paste_region, each taking
the akgl_RenderBackend the host already initialized.
Color is an argument rather than a current-color global, so there is no second
copy of state to disagree with the caller's own, and each call restores the
renderer's draw color when it is done. SDL3 has no circle and no flood fill: the
circle is a midpoint circle, and the fill reads the target back, walks the
region with a fixed 4096-entry span stack, and blits back only the bounding box
of what changed. Exhausting that stack reports AKERR_OUTOFBOUNDS and says in the
header that the region is left partially filled.
tests/draw.c draws into a 64x64 software renderer under the dummy video driver
and reads the pixels back, so it needs no display and no offscreen harness.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 07:05:36 -04:00
|
|
|
/**
|
|
|
|
|
* @brief Plot a single pixel.
|
|
|
|
|
* @param self Backend or object instance to operate on.
|
|
|
|
|
* @param x Horizontal destination coordinate.
|
|
|
|
|
* @param y Vertical destination coordinate.
|
|
|
|
|
* @param color Color to draw with.
|
|
|
|
|
* @return `NULL` on success, otherwise an error context owned by the caller.
|
|
|
|
|
* @throws AKERR_NULLPOINTER When the corresponding validation or operation fails.
|
|
|
|
|
* @throws AKGL_ERR_SDL When the corresponding validation or operation fails.
|
|
|
|
|
*/
|
|
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_point(akgl_RenderBackend *self, float32_t x, float32_t y, SDL_Color color);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Draw a line between two points.
|
|
|
|
|
* @param self Backend or object instance to operate on.
|
|
|
|
|
* @param x1 Horizontal coordinate of the first endpoint.
|
|
|
|
|
* @param y1 Vertical coordinate of the first endpoint.
|
|
|
|
|
* @param x2 Horizontal coordinate of the second endpoint.
|
|
|
|
|
* @param y2 Vertical coordinate of the second endpoint.
|
|
|
|
|
* @param color Color to draw with.
|
|
|
|
|
* @return `NULL` on success, otherwise an error context owned by the caller.
|
|
|
|
|
* @throws AKERR_NULLPOINTER When the corresponding validation or operation fails.
|
|
|
|
|
* @throws AKGL_ERR_SDL When the corresponding validation or operation fails.
|
|
|
|
|
*/
|
|
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_line(akgl_RenderBackend *self, float32_t x1, float32_t y1, float32_t x2, float32_t y2, SDL_Color color);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Draw the outline of a rectangle.
|
|
|
|
|
* @param self Backend or object instance to operate on.
|
|
|
|
|
* @param rect Rectangle to outline.
|
|
|
|
|
* @param color Color to draw with.
|
|
|
|
|
* @return `NULL` on success, otherwise an error context owned by the caller.
|
|
|
|
|
* @throws AKERR_NULLPOINTER When the corresponding validation or operation fails.
|
|
|
|
|
* @throws AKGL_ERR_SDL When the corresponding validation or operation fails.
|
|
|
|
|
*/
|
|
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_rect(akgl_RenderBackend *self, SDL_FRect *rect, SDL_Color color);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Fill a rectangle.
|
|
|
|
|
* @param self Backend or object instance to operate on.
|
|
|
|
|
* @param rect Rectangle to fill.
|
|
|
|
|
* @param color Color to draw with.
|
|
|
|
|
* @return `NULL` on success, otherwise an error context owned by the caller.
|
|
|
|
|
* @throws AKERR_NULLPOINTER When the corresponding validation or operation fails.
|
|
|
|
|
* @throws AKGL_ERR_SDL When the corresponding validation or operation fails.
|
|
|
|
|
*/
|
|
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_filled_rect(akgl_RenderBackend *self, SDL_FRect *rect, SDL_Color color);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Draw the outline of a circle.
|
|
|
|
|
*
|
|
|
|
|
* SDL3 has no circle primitive, so this plots one with the midpoint circle
|
|
|
|
|
* algorithm -- integer arithmetic, eight-way symmetry, one pass per octant. A
|
|
|
|
|
* radius of zero draws the center pixel and nothing else.
|
|
|
|
|
*
|
|
|
|
|
* @param self Backend or object instance to operate on.
|
|
|
|
|
* @param x Horizontal coordinate of the center.
|
|
|
|
|
* @param y Vertical coordinate of the center.
|
|
|
|
|
* @param radius Circle radius in pixels.
|
|
|
|
|
* @param color Color to draw with.
|
|
|
|
|
* @return `NULL` on success, otherwise an error context owned by the caller.
|
|
|
|
|
* @throws AKERR_NULLPOINTER When the corresponding validation or operation fails.
|
|
|
|
|
* @throws AKERR_OUTOFBOUNDS When the corresponding validation or operation fails.
|
|
|
|
|
* @throws AKGL_ERR_SDL When the corresponding validation or operation fails.
|
|
|
|
|
*/
|
|
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_circle(akgl_RenderBackend *self, float32_t x, float32_t y, float32_t radius, SDL_Color color);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Flood the connected region containing one pixel with a color.
|
|
|
|
|
*
|
|
|
|
|
* SDL3 has no flood fill either, and unlike the shape primitives it cannot be
|
|
|
|
|
* done on the GPU side: the region is defined by what is already on the screen.
|
|
|
|
|
* This reads the render target back, walks the region on the CPU with a
|
|
|
|
|
* bounded span stack, and blits the result over the area it touched.
|
|
|
|
|
*
|
|
|
|
|
* Filling a region that already holds @p color is a no-op rather than an error.
|
|
|
|
|
* A seed outside the render target reports AKERR_OUTOFBOUNDS.
|
|
|
|
|
*
|
|
|
|
|
* @param self Backend or object instance to operate on.
|
|
|
|
|
* @param x Horizontal coordinate of the seed pixel.
|
|
|
|
|
* @param y Vertical coordinate of the seed pixel.
|
|
|
|
|
* @param color Color to fill with.
|
|
|
|
|
* @return `NULL` on success, otherwise an error context owned by the caller.
|
|
|
|
|
* @throws AKERR_NULLPOINTER When the corresponding validation or operation fails.
|
|
|
|
|
* @throws AKERR_OUTOFBOUNDS When the corresponding validation or operation fails.
|
|
|
|
|
* @throws AKGL_ERR_SDL When the corresponding validation or operation fails.
|
|
|
|
|
*/
|
|
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_flood_fill(akgl_RenderBackend *self, int x, int y, SDL_Color color);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Read a rectangle of the render target into a surface.
|
|
|
|
|
*
|
|
|
|
|
* The save half of SSHAPE/GSHAPE. When `*dest` is `NULL` the function allocates
|
|
|
|
|
* the surface and the caller owns it from then on -- release it with
|
|
|
|
|
* SDL_DestroySurface(). When `*dest` already points at a surface of exactly
|
|
|
|
|
* @p src's dimensions the pixels are copied into it instead, so a caller
|
|
|
|
|
* saving the same region repeatedly does not churn allocations.
|
|
|
|
|
*
|
|
|
|
|
* @param self Backend or object instance to operate on.
|
|
|
|
|
* @param src Rectangle of the render target to read.
|
|
|
|
|
* @param dest Output destination populated by the function.
|
|
|
|
|
* @return `NULL` on success, otherwise an error context owned by the caller.
|
|
|
|
|
* @throws AKERR_NULLPOINTER When the corresponding validation or operation fails.
|
|
|
|
|
* @throws AKERR_OUTOFBOUNDS When the corresponding validation or operation fails.
|
|
|
|
|
* @throws AKGL_ERR_SDL When the corresponding validation or operation fails.
|
|
|
|
|
*/
|
|
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_copy_region(akgl_RenderBackend *self, SDL_Rect *src, SDL_Surface **dest);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Draw a saved surface back onto the render target.
|
|
|
|
|
*
|
|
|
|
|
* The restore half of SSHAPE/GSHAPE, taking what akgl_draw_copy_region()
|
|
|
|
|
* produced. The surface is not consumed and may be pasted as many times as the
|
|
|
|
|
* caller likes.
|
|
|
|
|
*
|
|
|
|
|
* @param self Backend or object instance to operate on.
|
|
|
|
|
* @param src Surface to draw.
|
|
|
|
|
* @param x Horizontal destination coordinate.
|
|
|
|
|
* @param y Vertical destination coordinate.
|
|
|
|
|
* @return `NULL` on success, otherwise an error context owned by the caller.
|
|
|
|
|
* @throws AKERR_NULLPOINTER When the corresponding validation or operation fails.
|
|
|
|
|
* @throws AKGL_ERR_SDL When the corresponding validation or operation fails.
|
|
|
|
|
*/
|
|
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_paste_region(akgl_RenderBackend *self, SDL_Surface *src, float32_t x, float32_t y);
|
|
|
|
|
|
2025-08-03 10:07:35 -04:00
|
|
|
#endif //_DRAW_H_
|