Document what the functions actually do instead of that they can fail
The Doxygen comments were generated from the declarations, so 217 @throws lines across 21 headers read "When the corresponding validation or operation fails" and told a caller nothing beyond the status name. The @param lines were the same shape: every output was "Output destination populated by the function", every instance "Object to initialize, inspect, or modify". Rewritten against the implementations, following the pattern libakstdlib already uses: - @throws names the condition. akgl_sprite_load_json separated AKERR_KEY (absent) from AKERR_TYPE (present, wrong type) from AKERR_OUTOFBOUNDS (filename too long, or array indexed past its end), and gained AKGL_ERR_SDL and AKGL_ERR_HEAP, which it raises and never declared. - Parameters say whether they are required, what a NULL means, and what is written on a failure path. Where an argument is not checked, the doc says so: akgl_heap_next_actor's dest is a crash on NULL, not an error, and akgl_render_2d_frame_start dereferences self before testing it. - The conventions move up to the file blocks so the per-function docs stay short. json_helpers.h states once that absence is an error here and that json_t * results are borrowed; heap.h explains the pool model and the acquire asymmetry; physics.h carries the thrust/environmental/velocity table. - Struct fields, enum values, macros and exported globals are documented, including the dead ones - sprite_w/sprite_h, movetimer, p_scale and timer_gravity are read by nothing, and say so. Also fixes ten comments in error.h and audio.h that opened with /** rather than /**<, so Doxygen attached them to the following entity and rendered the text as part of the macro's value. Verified against the generated HTML. Comments only - no declaration changed. Doxygen builds clean under WARN_AS_ERROR, scripts/reindent.sh --check passes, 19/19 suites pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,18 @@
|
||||
/**
|
||||
* @file sprite.h
|
||||
* @brief Declares the public sprite API.
|
||||
* @brief Spritesheets (one texture, many frames) and the animations cut out of them.
|
||||
*
|
||||
* The split is deliberate: an akgl_SpriteSheet owns the `SDL_Texture` and the
|
||||
* frame grid, and any number of akgl_Sprite animations point at the same sheet
|
||||
* and name the frame indices they use. Loading two sprites from the same image
|
||||
* loads the image once -- akgl_sprite_load_json looks the sheet up in
|
||||
* #AKGL_REGISTRY_SPRITESHEET by resolved path before creating one.
|
||||
*
|
||||
* Both are pool objects (akgl_heap_next_sprite, akgl_heap_next_spritesheet) and
|
||||
* both publish themselves in a registry under their name, which is how
|
||||
* akgl_character_sprite_add and the tilemap loader find them. Because loading a
|
||||
* sheet uploads a texture, the renderer has to exist first -- so
|
||||
* akgl_render_init2d, or akgl_game_init, before any of this.
|
||||
*/
|
||||
|
||||
#ifndef _AKGL_SPRITE_H_
|
||||
@@ -21,67 +33,138 @@
|
||||
|
||||
/** @brief Stores a loaded spritesheet texture and frame geometry. */
|
||||
typedef struct {
|
||||
uint8_t refcount;
|
||||
SDL_Texture *texture;
|
||||
char name[AKGL_SPRITE_SHEET_MAX_FILENAME_LENGTH];
|
||||
uint16_t sprite_w;
|
||||
uint16_t sprite_h;
|
||||
uint8_t refcount; /**< Pool bookkeeping; 0 means the slot is free. Destroys the texture when it reaches 0. */
|
||||
SDL_Texture *texture; /**< The whole sheet as one GPU texture. Owned by this struct. */
|
||||
char name[AKGL_SPRITE_SHEET_MAX_FILENAME_LENGTH]; /**< Registry key: the resolved path the image was loaded from. */
|
||||
uint16_t sprite_w; /**< Frame width. Vestigial: nothing in the library writes or reads it; the grid comes from akgl_Sprite::width. */
|
||||
uint16_t sprite_h; /**< Frame height. Vestigial, same as sprite_w. */
|
||||
} akgl_SpriteSheet;
|
||||
|
||||
/** @brief Describes an animated sprite and its spritesheet reference. */
|
||||
typedef struct {
|
||||
uint8_t refcount;
|
||||
uint8_t frameids[AKGL_SPRITE_MAX_FRAMES]; // which IDs on the spritesheet belong to our frames
|
||||
uint32_t frames; // how many frames are in this animation
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t speed; // how many milliseconds a given sprite frame should be visible before cycling
|
||||
bool loop; // when this sprite is done playing, it should immediately start again
|
||||
bool loopReverse; // when this sprite is done playing, it should go in reverse order through its frames
|
||||
char name[AKGL_SPRITE_MAX_NAME_LENGTH];
|
||||
akgl_SpriteSheet *sheet;
|
||||
uint8_t refcount; /**< Pool bookkeeping; 0 means the slot is free. One reference per character that maps it. */
|
||||
uint8_t frameids[AKGL_SPRITE_MAX_FRAMES]; /**< Frame numbers on the sheet, in playback order. Counted left to right, then wrapping to the next row. */
|
||||
uint32_t frames; /**< How many entries of frameids are in use. */
|
||||
uint32_t width; /**< Frame width in pixels; also the horizontal stride used to find a frame on the sheet. */
|
||||
uint32_t height; /**< Frame height in pixels; the vertical stride once frames wrap to the next row. */
|
||||
uint32_t speed; /**< Nanoseconds one frame is held. Read from JSON in milliseconds and scaled by #AKGL_TIME_ONESEC_MS, which despite its name is nanoseconds-per-millisecond. TODO.md item 6. */
|
||||
bool loop; /**< Restart from frame 0 when the last frame is reached, instead of holding it. */
|
||||
bool loopReverse; /**< Play back down to frame 0 instead of jumping to it -- a ping-pong loop. */
|
||||
char name[AKGL_SPRITE_MAX_NAME_LENGTH]; /**< Registry key, from the JSON `name` field. */
|
||||
akgl_SpriteSheet *sheet; /**< The sheet these frames are cut from. Borrowed, not owned. */
|
||||
} akgl_Sprite;
|
||||
|
||||
// initializes a new sprite to use the given sheet and otherwise sets to zero
|
||||
/**
|
||||
* @brief Sprite initialize.
|
||||
* @param spr Sprite object to initialize.
|
||||
* @param name Registry key or human-readable object name.
|
||||
* @param sheet Spritesheet used by the sprite.
|
||||
* @brief Zero a pooled sprite, bind it to a sheet, and publish it in the sprite registry.
|
||||
*
|
||||
* Sets the name and the sheet and takes the first reference; everything else --
|
||||
* frame list, geometry, speed, loop flags -- is left at zero for the caller, or
|
||||
* akgl_sprite_load_json, to fill in. The sheet is borrowed, so this does *not*
|
||||
* take a reference on it: releasing the sheet out from under a live sprite
|
||||
* leaves a dangling pointer.
|
||||
*
|
||||
* @param spr Pooled sprite to initialize, normally from akgl_heap_next_sprite.
|
||||
* Required. Any previous contents are discarded.
|
||||
* @param name Registry key, NUL-terminated. Required. Copied at a fixed
|
||||
* #AKGL_SPRITE_MAX_NAME_LENGTH bytes, so a shorter string reads
|
||||
* past its end -- pass a name from an akgl_String or another
|
||||
* buffer of at least that size. An existing entry with the same
|
||||
* name is silently replaced.
|
||||
* @param sheet The spritesheet this sprite's frames come from. Required.
|
||||
* @return `NULL` on success, otherwise an error context owned by the caller.
|
||||
* @throws AKERR_KEY When the corresponding validation or operation fails.
|
||||
* @throws AKERR_NULLPOINTER When the corresponding validation or operation fails.
|
||||
* @throws AKERR_NULLPOINTER If @p spr, @p name, or @p sheet is `NULL`.
|
||||
* @throws AKERR_KEY If the sprite cannot be written into #AKGL_REGISTRY_SPRITE
|
||||
* -- in practice, because akgl_registry_init has not run.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_sprite_initialize(akgl_Sprite *spr, char *name, akgl_SpriteSheet *sheet);
|
||||
// loads a given image file into a new spritesheet
|
||||
/**
|
||||
* @brief Spritesheet initialize.
|
||||
* @param sheet Spritesheet used by the sprite.
|
||||
* @param sprite_w Width of one sprite frame in pixels.
|
||||
* @param sprite_h Height of one sprite frame in pixels.
|
||||
* @param filename Path to the source asset or JSON document.
|
||||
* @brief Load an image file as a texture and publish it as a spritesheet.
|
||||
*
|
||||
* Uploads @p filename through SDL_image using the global `renderer`, so the
|
||||
* renderer must already be initialized. The sheet is registered in
|
||||
* #AKGL_REGISTRY_SPRITESHEET under @p filename, which is the key
|
||||
* akgl_sprite_load_json looks it up by to avoid loading the same image twice.
|
||||
*
|
||||
* @param sheet Pooled spritesheet to initialize, normally from
|
||||
* akgl_heap_next_spritesheet. Required. Zeroed first, so a
|
||||
* texture it already held is leaked rather than destroyed.
|
||||
* @param sprite_w Frame width in pixels. Accepted and currently discarded --
|
||||
* the frame grid actually used at draw time comes from the
|
||||
* akgl_Sprite's `width`/`height`, not from here.
|
||||
* @param sprite_h Frame height in pixels. Same caveat as @p sprite_w.
|
||||
* @param filename Path to the image, in any format SDL_image can decode.
|
||||
* Required. Doubles as the registry key, so callers should pass
|
||||
* an already-resolved path; two spellings of the same file are
|
||||
* two sheets. Truncated at
|
||||
* #AKGL_SPRITE_SHEET_MAX_FILENAME_LENGTH.
|
||||
* @return `NULL` on success, otherwise an error context owned by the caller.
|
||||
* @throws AKERR_KEY When the corresponding validation or operation fails.
|
||||
* @throws AKERR_NULLPOINTER When the corresponding validation or operation fails.
|
||||
* @throws AKGL_ERR_SDL When the corresponding validation or operation fails.
|
||||
* @throws AKERR_NULLPOINTER If @p sheet or @p filename is `NULL`.
|
||||
* @throws AKGL_ERR_SDL If the image cannot be loaded -- missing, unreadable, or
|
||||
* a format SDL_image was not built with. The message carries
|
||||
* `SDL_GetError()`.
|
||||
* @throws AKERR_KEY If the sheet cannot be written into
|
||||
* #AKGL_REGISTRY_SPRITESHEET.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_spritesheet_initialize(akgl_SpriteSheet *sheet, int sprite_w, int sprite_h, char *filename);
|
||||
/**
|
||||
* @brief Sprite load json.
|
||||
* @param filename Path to the source asset or JSON document.
|
||||
* @brief Build a sprite -- and, if needed, its spritesheet -- from a JSON definition file.
|
||||
*
|
||||
* Reads `name`, `width`, `height`, `speed` (seconds, scaled to milliseconds),
|
||||
* `loop`, `loopReverse`, a `frames` array of frame indices, and a `spritesheet`
|
||||
* object holding `filename`, `frame_width`, and `frame_height`. The sheet's
|
||||
* `filename` is resolved *relative to the directory of @p filename*, so a sprite
|
||||
* definition can sit next to its image and move with it.
|
||||
*
|
||||
* If a spritesheet with that resolved path is already registered it is reused;
|
||||
* otherwise one is claimed from the pool and loaded. On failure both the sprite
|
||||
* and any sheet loaded for it are released again.
|
||||
*
|
||||
* @param filename Path to the JSON document. Required. Must be shorter than
|
||||
* #AKGL_MAX_STRING_LENGTH -- it is copied into a pooled string
|
||||
* so `dirname` can be taken without modifying the caller's
|
||||
* buffer.
|
||||
* @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 AKERR_NULLPOINTER If @p filename is `NULL`, or if the file cannot be
|
||||
* opened or does not parse. The message carries jansson's line number
|
||||
* and text.
|
||||
* @throws AKERR_OUTOFBOUNDS If @p filename is at least #AKGL_MAX_STRING_LENGTH
|
||||
* bytes long, or if the `frames` array is indexed past its end.
|
||||
* @throws AKERR_KEY If a required key is absent, or if the sprite or sheet
|
||||
* cannot be added to its registry. The message names the key.
|
||||
* @throws AKERR_TYPE If a key is present with the wrong JSON type -- `speed` as
|
||||
* a string, `frames` as an object.
|
||||
* @throws AKGL_ERR_SDL If the spritesheet image fails to load.
|
||||
* @throws AKGL_ERR_HEAP If the sprite, spritesheet, or string pool is exhausted.
|
||||
*
|
||||
* @note The `frames` array is not bounded against #AKGL_SPRITE_MAX_FRAMES. A
|
||||
* definition with more than 16 frames writes past `frameids` into the
|
||||
* rest of the struct.
|
||||
*/
|
||||
akerr_ErrorContext AKERR_NOIGNORE *akgl_sprite_load_json(char *filename);
|
||||
|
||||
/**
|
||||
* @brief Sprite sheet coords for frame.
|
||||
* @param self Backend or object instance to operate on.
|
||||
* @param srccoords Output source rectangle for the selected frame.
|
||||
* @param frameid Sprite frame index to resolve.
|
||||
* @brief Work out where one animation frame sits on its spritesheet.
|
||||
*
|
||||
* Frames are numbered in reading order across the sheet: multiply the frame
|
||||
* number by the sprite width, then wrap whole rows off the right-hand edge of
|
||||
* the texture and step down by the sprite height for each. Callers use the
|
||||
* result as the source rectangle for `SDL_RenderTexture`.
|
||||
*
|
||||
* @param self The sprite whose sheet and frame geometry to use. Required,
|
||||
* along with its `sheet`.
|
||||
* @param srccoords Receives the frame's rectangle on the sheet, in pixels.
|
||||
* Required -- the return value is the error context.
|
||||
* @param frameid Index into the sprite's `frameids` array, not a frame number
|
||||
* on the sheet. Not bounds-checked against `frames` or
|
||||
* #AKGL_SPRITE_MAX_FRAMES; an out-of-range index reads a
|
||||
* neighbouring struct member and yields a nonsense rectangle
|
||||
* rather than an error.
|
||||
* @return `NULL` on success, otherwise an error context owned by the caller.
|
||||
* @throws AKERR_NULLPOINTER When the corresponding validation or operation fails.
|
||||
* @throws AKERR_NULLPOINTER If @p self, @p srccoords, or `self->sheet` is
|
||||
* `NULL`. Note that `sheet->texture` is dereferenced without a check,
|
||||
* so a registered-but-unloaded sheet is a crash rather than an error.
|
||||
*/
|
||||
akerr_ErrorContext *akgl_sprite_sheet_coords_for_frame(akgl_Sprite *self, SDL_FRect *srccoords, uint8_t frameid);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user