diff --git a/cucul/cucul.c b/cucul/cucul.c index 67ab114..31066d0 100644 --- a/cucul/cucul.c +++ b/cucul/cucul.c @@ -54,6 +54,13 @@ cucul_canvas_t * cucul_create_canvas(unsigned int width, unsigned int height) cv->chars = NULL; cv->attr = NULL; + cv->frame = 0; + cv->framecount = 1; + cv->allchars = malloc(sizeof(uint32_t *)); + cv->allattr = malloc(sizeof(uint32_t *)); + cv->allchars[0] = NULL; + cv->allattr[0] = NULL; + /* Initialise to a default size. 80x32 is arbitrary but matches AAlib's * default X11 window. When a graphic driver attaches to us, it can set * a different size. */ @@ -169,10 +176,15 @@ char const *cucul_get_color_name(unsigned int color) */ void cucul_free_canvas(cucul_canvas_t *cv) { + unsigned int f; + _cucul_end_dither(); - free(cv->chars); - free(cv->attr); + for(f = 0; f < cv->framecount; f++) + { + free(cv->allchars[f]); + free(cv->allattr[f]); + } free(cv); } @@ -196,7 +208,7 @@ int cucul_rand(int min, int max) void _cucul_set_canvas_size(cucul_canvas_t *cv, unsigned int width, unsigned int height) { - unsigned int x, y, old_width, old_height, new_size, old_size; + unsigned int x, y, f, old_width, old_height, new_size, old_size; old_width = cv->width; old_height = cv->height; @@ -209,8 +221,13 @@ void _cucul_set_canvas_size(cucul_canvas_t *cv, unsigned int width, /* Step 1: if new area is bigger, resize the memory area now. */ if(new_size > old_size) { - cv->chars = realloc(cv->chars, new_size * sizeof(uint32_t)); - cv->attr = realloc(cv->attr, new_size * sizeof(uint32_t)); + for(f = 0; f < cv->framecount; f++) + { + cv->allchars[f] = realloc(cv->allchars[f], + new_size * sizeof(uint32_t)); + cv->allattr[f] = realloc(cv->allattr[f], + new_size * sizeof(uint32_t)); + } } /* Step 2: move line data if necessary. */ @@ -224,19 +241,25 @@ void _cucul_set_canvas_size(cucul_canvas_t *cv, unsigned int width, /* New width is bigger than old width, which means we need to * copy lines starting from the bottom of the screen otherwise * we will overwrite information. */ - for(y = height < old_height ? height : old_height; y--; ) + for(f = 0; f < cv->framecount; f++) { - for(x = old_width; x--; ) + uint32_t *chars = cv->allchars[f]; + uint32_t *attr = cv->allattr[f]; + + for(y = height < old_height ? height : old_height; y--; ) { - cv->chars[y * width + x] = cv->chars[y * old_width + x]; - cv->attr[y * width + x] = cv->attr[y * old_width + x]; + for(x = old_width; x--; ) + { + chars[y * width + x] = chars[y * old_width + x]; + attr[y * width + x] = attr[y * old_width + x]; + } + + /* Zero the end of the line */ + for(x = width - old_width; x--; ) + chars[y * width + old_width + x] = (uint32_t)' '; + memset(attr + y * width + old_width, 0, + (width - old_width) * 4); } - - /* Zero the end of the line */ - for(x = width - old_width; x--; ) - cv->chars[y * width + old_width + x] = (uint32_t)' '; - memset(cv->attr + y * width + old_width, 0, - (width - old_width) * 4); } } else @@ -245,12 +268,18 @@ void _cucul_set_canvas_size(cucul_canvas_t *cv, unsigned int width, * the first line, it is already in place. */ unsigned int lines = height < old_height ? height : old_height; - for(y = 1; y < lines; y++) + for(f = 0; f < cv->framecount; f++) { - for(x = 0; x < width; x++) + uint32_t *chars = cv->allchars[f]; + uint32_t *attr = cv->allattr[f]; + + for(y = 1; y < lines; y++) { - cv->chars[y * width + x] = cv->chars[y * old_width + x]; - cv->attr[y * width + x] = cv->attr[y * old_width + x]; + for(x = 0; x < width; x++) + { + chars[y * width + x] = chars[y * old_width + x]; + attr[y * width + x] = attr[y * old_width + x]; + } } } } @@ -258,18 +287,33 @@ void _cucul_set_canvas_size(cucul_canvas_t *cv, unsigned int width, /* Step 3: fill the bottom of the new screen if necessary. */ if(height > old_height) { - /* Zero the bottom of the screen */ - for(x = (height - old_height) * width; x--; ) - cv->chars[old_height * width + x] = (uint32_t)' '; - memset(cv->attr + old_height * width, 0, - (height - old_height) * width * 4); + for(f = 0; f < cv->framecount; f++) + { + uint32_t *chars = cv->allchars[f]; + uint32_t *attr = cv->allattr[f]; + + /* Zero the bottom of the screen */ + for(x = (height - old_height) * width; x--; ) + chars[old_height * width + x] = (uint32_t)' '; + memset(attr + old_height * width, 0, + (height - old_height) * width * 4); + } } /* Step 4: if new area is smaller, resize memory area now. */ if(new_size <= old_size) { - cv->chars = realloc(cv->chars, new_size * sizeof(uint32_t)); - cv->attr = realloc(cv->attr, new_size * sizeof(uint32_t)); + for(f = 0; f < cv->framecount; f++) + { + cv->allchars[f] = realloc(cv->allchars[f], + new_size * sizeof(uint32_t)); + cv->allattr[f] = realloc(cv->allattr[f], + new_size * sizeof(uint32_t)); + } } + + /* Reset the current frame shortcut */ + cv->chars = cv->allchars[cv->frame]; + cv->attr = cv->allattr[cv->frame]; } diff --git a/cucul/cucul.h b/cucul/cucul.h index 36aee45..179232a 100644 --- a/cucul/cucul.h +++ b/cucul/cucul.h @@ -33,8 +33,6 @@ extern "C" /** \e libcucul context */ typedef struct cucul_canvas cucul_canvas_t; -/** sprite structure */ -typedef struct cucul_sprite cucul_sprite_t; /** dither structure */ typedef struct cucul_dither cucul_dither_t; /** data buffer structure */ @@ -143,20 +141,16 @@ void cucul_draw_thin_triangle(cucul_canvas_t *, int, int, int, int, int, int); void cucul_fill_triangle(cucul_canvas_t *, int, int, int, int, int, int, char const *); /* @} */ -/** \defgroup sprite libcucul sprite handling +/** \defgroup frame libcucul canvas frame handling * - * These functions provide high level routines for sprite loading, animation - * and rendering. + * These functions provide high level routines for canvas frame insertion, + * removal, copying etc. * * @{ */ -cucul_sprite_t * cucul_load_sprite(char const *); -int cucul_get_sprite_frames(cucul_sprite_t const *); -int cucul_get_sprite_width(cucul_sprite_t const *, int); -int cucul_get_sprite_height(cucul_sprite_t const *, int); -int cucul_get_sprite_dx(cucul_sprite_t const *, int); -int cucul_get_sprite_dy(cucul_sprite_t const *, int); -void cucul_draw_sprite(cucul_canvas_t *, int, int, cucul_sprite_t const *, int); -void cucul_free_sprite(cucul_sprite_t *); +unsigned int cucul_get_canvas_frame_count(cucul_canvas_t *); +void cucul_set_canvas_frame(cucul_canvas_t *, unsigned int); +void cucul_create_canvas_frame(cucul_canvas_t *, unsigned int); +void cucul_free_canvas_frame(cucul_canvas_t *, unsigned int); /* @} */ /** \defgroup dither libcucul bitmap dithering @@ -166,9 +160,9 @@ void cucul_free_sprite(cucul_sprite_t *); * * @{ */ cucul_dither_t *cucul_create_dither(unsigned int, unsigned int, - unsigned int, unsigned int, - unsigned int, unsigned int, - unsigned int, unsigned int); + unsigned int, unsigned int, + unsigned int, unsigned int, + unsigned int, unsigned int); void cucul_set_dither_palette(cucul_dither_t *, unsigned int r[], unsigned int g[], unsigned int b[], unsigned int a[]); diff --git a/cucul/cucul_internals.h b/cucul/cucul_internals.h index 1cf74d8..d037177 100644 --- a/cucul/cucul_internals.h +++ b/cucul/cucul_internals.h @@ -27,12 +27,19 @@ typedef long unsigned int uintptr_t; struct cucul_canvas { - /* Context size */ + /* Canvas size */ unsigned int width, height; + /* Shortcut to the active frame */ uint32_t *chars; uint32_t *attr; + /* Frame information */ + unsigned int frame, framecount; + uint32_t **allchars; + uint32_t **allattr; + + /* Painting context */ uint16_t fgcolor; uint16_t bgcolor; diff --git a/cucul/sprite.c b/cucul/sprite.c index 7ca2ad7..e627166 100644 --- a/cucul/sprite.c +++ b/cucul/sprite.c @@ -12,7 +12,7 @@ */ /* - * This file contains a small framework for sprite loading and blitting. + * This file contains a small framework for canvas frame management. */ #include "config.h" @@ -26,288 +26,134 @@ #include "cucul.h" #include "cucul_internals.h" -#if !defined(_DOXYGEN_SKIP_ME) -struct cucul_frame -{ - int w, h; - int dx, dy; - char *chars; - int *color; -}; - -struct cucul_sprite -{ - int nf; - struct cucul_frame *frames; -}; -#endif - -/** \brief Allocate a sprite loaded from a file. +/** \brief Get the number of frames in a canvas. * - * \param file The filename. - * \return The sprite, or NULL if an error occured. - */ -cucul_sprite_t *cucul_load_sprite(char const *file) -{ - char buf[BUFSIZ]; - cucul_sprite_t *sprite; - FILE *fd; - - fd = fopen(file, "r"); - if(fd == NULL) - return NULL; - - sprite = malloc(sizeof(cucul_sprite_t)); - if(sprite == NULL) - goto sprite_alloc_failed; - - sprite->nf = 0; - sprite->frames = NULL; - - while(!feof(fd)) - { - int x, y; - int w = 0, h = 0, dx = 0, dy = 0; - struct cucul_frame *frame; - - /* Get width and height */ - if(!fgets(buf, BUFSIZ, fd)) - break; - - sscanf(buf, "%i %i %i %i", &w, &h, &dx, &dy); - if(w <= 0 || h <= 0 || w > BUFSIZ / 2) - break; - - if(sprite->nf) - { - void *tmp = realloc(sprite->frames, - (sprite->nf + 1) * sizeof(struct cucul_frame)); - if(tmp == NULL) - goto frame_failed; - sprite->frames = tmp; - sprite->nf++; - } - else - { - sprite->frames = malloc((sprite->nf + 1) * sizeof(struct cucul_frame)); - if(sprite->frames == NULL) - goto sprite_failed; - sprite->nf++; - } - - frame = &sprite->frames[sprite->nf - 1]; - - frame->w = w; - frame->h = h; - frame->dx = dx; - frame->dy = dy; - frame->chars = malloc(w * h * sizeof(char)); - if(frame->chars == NULL) - { - sprite->nf--; - goto frame_failed; - } - frame->color = malloc(w * h * sizeof(int)); - if(frame->color == NULL) - { - free(frame->chars); - sprite->nf--; - goto frame_failed; - } - - for(y = 0; y < h; y++) - { - if(!fgets(buf, BUFSIZ, fd)) - goto frame_failed; - - for(x = 0; x < w && buf[x] && buf[x] != '\r' && buf[x] != '\n'; x++) - frame->chars[w * y + x] = buf[x]; - - for(; x < w; x++) - frame->chars[w * y + x] = ' '; - } - - for(y = 0; y < h; y++) - { - if(!fgets(buf, BUFSIZ, fd)) - goto frame_failed; - - for(x = 0; x < w && buf[x] && buf[x] != '\r' && buf[x] != '\n'; x++) - frame->color[w * y + x] = buf[x] - 'a'; - - for(; x < w; x++) - frame->color[w * y + x] = ' ' - 'a'; - } - - continue; - } - - if(sprite->nf == 0) - goto sprite_failed; - - fclose(fd); - return sprite; - -frame_failed: - while(sprite->nf) - { - free(sprite->frames[sprite->nf - 1].color); - free(sprite->frames[sprite->nf - 1].chars); - sprite->nf--; - } -sprite_failed: - free(sprite); -sprite_alloc_failed: - fclose(fd); - return NULL; -} - -/** \brief Return the number of frames in a sprite. + * This function returns the current canvas frame count. * - * \param sprite The sprite. - * \return The number of frames. + * \param cv A libcucul canvas + * \return The frame count */ -int cucul_get_sprite_frames(cucul_sprite_t const *sprite) +unsigned int cucul_get_canvas_frame_count(cucul_canvas_t *cv) { - if(sprite == NULL) - return 0; - - return sprite->nf; + return cv->framecount; } -/** \brief Return the width of a sprite. +/** \brief Activate a given canvas frame. * - * \param sprite The sprite. - * \param f The frame index. - * \return The width of the given frame of the sprite. - */ -int cucul_get_sprite_width(cucul_sprite_t const *sprite, int f) -{ - if(sprite == NULL) - return 0; - - if(f < 0 || f >= sprite->nf) - return 0; - - return sprite->frames[f].w; -} - -/** \brief Return the height of a sprite. + * This function sets the active canvas frame. All subsequent drawing + * operations will be performed on that frame. The current painting + * context set by cucul_set_color() or cucul_set_truecolor() is inherited. + * + * If the frame index is outside the canvas' frame range, nothing happens. * - * \param sprite The sprite. - * \param f The frame index. - * \return The height of the given frame of the sprite. + * \param cv A libcucul canvas + * \param frame The canvas frame to activate */ -int cucul_get_sprite_height(cucul_sprite_t const *sprite, int f) +void cucul_set_canvas_frame(cucul_canvas_t *cv, unsigned int frame) { - if(sprite == NULL) - return 0; + if(frame >= cv->framecount) + return; - if(f < 0 || f >= sprite->nf) - return 0; + cv->frame = frame; - return sprite->frames[f].h; + cv->chars = cv->allchars[cv->frame]; + cv->attr = cv->allattr[cv->frame]; } -/** \brief Return the X coordinate of a sprite's handle. +/** \brief Add a frame to a canvas. * - * \param sprite The sprite. - * \param f The frame index. - * \return The X coordinate of the given frame's handle. + * This function creates a new frame within the given canvas. Its contents + * are copied from the currently active frame. + * + * The frame index indicates where the frame should be inserted. Valid + * values range from 0 to the current canvas frame count. If the frame + * index is greater the or equals the current canvas frame count, the new + * frame is appended at the end of the canvas. + * + * The active frame does not change, but its index may be renumbered due + * to the insertion. + * + * \param cv A libcucul canvas + * \param frame The index where to insert the new frame */ -int cucul_get_sprite_dx(cucul_sprite_t const *sprite, int f) +void cucul_create_canvas_frame(cucul_canvas_t *cv, unsigned int frame) { - if(sprite == NULL) - return 0; + unsigned int size = cv->width * cv->height * sizeof(uint32_t); + unsigned int f; - if(f < 0 || f >= sprite->nf) - return 0; + if(frame > cv->framecount) + frame = cv->framecount; - return sprite->frames[f].dx; -} + cv->framecount++; + cv->allchars = realloc(cv->allchars, sizeof(uint32_t *) * cv->framecount); + cv->allattr = realloc(cv->allattr, sizeof(uint32_t *) * cv->framecount); -/** \brief Return the Y coordinate of a sprite's handle. - * - * \param sprite The sprite. - * \param f The frame index. - * \return The Y coordinate of the given frame's handle. - */ -int cucul_get_sprite_dy(cucul_sprite_t const *sprite, int f) -{ - if(sprite == NULL) - return 0; + for(f = cv->framecount - 1; f > frame; f--) + { + cv->allchars[f] = cv->allchars[f - 1]; + cv->allattr[f] = cv->allattr[f - 1]; + } + + cv->allchars[frame] = malloc(size); + memcpy(cv->allchars[frame], cv->chars, size); + cv->allattr[frame] = malloc(size); + memcpy(cv->allattr[frame], cv->attr, size); - if(f < 0 || f >= sprite->nf) - return 0; + if(cv->frame >= frame) + cv->frame++; - return sprite->frames[f].dy; + cv->chars = cv->allchars[cv->frame]; + cv->attr = cv->allattr[cv->frame]; } -/** \brief Draw a sprite's specific frame at the given coordinates. If the - * frame does not exist, nothing is displayed. +/** \brief Remove a frame from a canvas. + * + * This function deletes a frame from a given canvas. + * + * It is not legal to remove the last frame from a canvas. Such a request + * will be ignored by cucul_free_canvas_frame(). + * + * The frame index indicates the frame to delete. Valid values range from + * 0 to the current canvas frame count minus 1. If the frame index is + * greater the or equals the current canvas frame count, the last frame + * is deleted. + * + * If the active frame is deleted, frame 0 becomes the new active frame. + * Otherwise, the active frame does not change, but its index may be + * renumbered due to the deletion. * * \param cv A libcucul canvas - * \param x The X coordinate. - * \param y The Y coordinate. - * \param sprite The sprite. - * \param f The frame index. - * \return void + * \param frame The index of the frame to delete */ -void cucul_draw_sprite(cucul_canvas_t *cv, int x, int y, - cucul_sprite_t const *sprite, int f) +void cucul_free_canvas_frame(cucul_canvas_t *cv, unsigned int frame) { - int i, j; - unsigned int oldfg, oldbg; - struct cucul_frame *frame; + unsigned int f; - if(sprite == NULL) + if(frame >= cv->framecount) return; - if(f < 0 || f >= sprite->nf) + if(cv->framecount == 1) return; - frame = &sprite->frames[f]; + free(cv->allchars[frame]); + free(cv->allattr[frame]); - oldfg = cv->fgcolor; - oldbg = cv->bgcolor; - - for(j = 0; j < frame->h; j++) + for(f = frame + 1; f < cv->framecount; f++) { - for(i = 0; i < frame->w; i++) - { - int col = frame->color[frame->w * j + i]; - if(col >= 0) - { - cucul_set_color(cv, col, CUCUL_COLOR_BLACK); - cucul_putchar(cv, x + i - frame->dx, y + j - frame->dy, - frame->chars[frame->w * j + i]); - } - } + cv->allchars[f - 1] = cv->allchars[f]; + cv->allattr[f - 1] = cv->allattr[f]; } - cucul_set_color(cv, oldfg, oldbg); -} + cv->framecount--; + cv->allchars = realloc(cv->allchars, sizeof(uint32_t *) * cv->framecount); + cv->allattr = realloc(cv->allattr, sizeof(uint32_t *) * cv->framecount); -/** \brief Free the memory associated with a sprite. - * - * \param sprite The sprite to be freed. - * \return void - */ -void cucul_free_sprite(cucul_sprite_t *sprite) -{ - int i; - - if(sprite == NULL) - return; - - for(i = sprite->nf; i--;) - { - struct cucul_frame *frame = &sprite->frames[i]; - free(frame->chars); - free(frame->color); - } + if(cv->frame > frame) + cv->frame--; + else if(cv->frame == frame) + cv->frame = 0; - free(sprite->frames); - free(sprite); + cv->chars = cv->allchars[cv->frame]; + cv->attr = cv->allattr[cv->frame]; }