#include #include #include "_ct_shared.h" #include "surface.h" #define ALLOC_COLS(w) ((glyph *)calloc(w, sizeof(glyph))) /* Allocates a new 2D surface buffer with ROWS=h, COLS=w. * Returns NULL on failure and sets errno, otherwise * a pointer to the new buffer is returned. */ static inline __attribute__((malloc, warn_unused_result)) glyph ** sfbuf(const pos w, const pos h) { glyph **buf; pos i; buf = (glyph **)malloc(sizeof(glyph *) * h); if (buf == NULL) return buf; for (i = 0; i < w; i++) { buf[i] = ALLOC_COLS(w); if (buf[i] == NULL) goto fail; } return buf; fail: // Immediately decrement since we know the // current index is where ALLOW_ROW() failed. while (--i >= 0) free(buf[i]); free(buf); return NULL; } /* Resize the width (columns) of a surface. * Returns 0 on success, or a non-zero errno on failure. * WARNING: Don't use sfseth directly, see sfresize() * WARNING: and how it optimises based on how height changes. */ static inline int sfsetw(const pos w1, surface *const s) { void *ptr; pos i, w0 = s->w; glyph **row; errno = 0; // sanity check if (w1 == w0) return OK; else if (w1 == 0) { return EINVAL; } for (i = 0, row = s->buf; i < s->h; i++, row++) { ptr = (void *)reallocarray(*row, w1, sizeof(glyph)); if (ptr == NULL) { return errno; } *row = (glyph *)ptr; } s->w = w1; return OK; } /* Resize the height (rows) of a surface. * Returns 0 on success, or a non-zero errno on failure. * WARNING: Don't use sfseth directly, see sfresize() * WARNING: and how it optimises based on how height changes. */ static inline int sfseth(const pos h1, surface *const s) { void *ptr; pos i, h0 = s->h; glyph **row; errno = 0; // sanity check if (h1 == h0) return OK; else if (h1 == 0) { return EINVAL; } // height decreased (free bottom rows) if (h1 < h0) { for (i = h1, row = s->buf; i < h0; i++, row++) { free(*row); } } // use ptr to avoid losing unfreed s->buf ptr = (void *)reallocarray(s->buf, h1, sizeof(glyph)); if (ptr == NULL) return errno; // ENOMEM X_X s->buf = (glyph **)ptr; s->h = h1; // height increased (alloc bottom rows) if (h1 > h0) { for (i = h0, row = s->buf; i < h1; i++, row++) { *row = ALLOC_COLS(s->w); if (*row == NULL) return errno; } } s->h = h1; return OK; } /* */ int sfresize(const pos w1, const pos h1, surface *const s) { int retv; /* Height is the 2D array's primary index dimension, * whether it grows/shrinks determines whether it is * optimal to resize width then height, or height then width. * ie Height increasing but doing sfseth() before sfsetw() * would result in new rows being allocated with the old width, * then resized again to the new width (not optimal). */ if (h1 > s->h) { if ((retv = sfsetw(w1, s)) || (retv = sfseth(h1, s))) return retv; } else if ((retv = sfseth(h1, s)) || (retv = sfsetw(w1, s))) return retv; return OK; } __attribute__((malloc, warn_unused_result)) surface *sfalloc(const pos x, const pos y, const pos w, const pos h) { surface *s; glyph **buf = sfbuf(w, h); if (buf == NULL) return NULL; s = (surface *)malloc(sizeof(struct ct_surface)); *s = (surface){ .x = x, .y = y, .w = w, .h = y, .vcx = 1, .vcy = 1, .buf = buf, }; return s; } void sffree(surface *const s) { pos i; glyph **row; for (i=0, row=s->buf; i < s->h; i++, row++) { free(*row); } free(s->buf); }