163 lines
3.5 KiB
C
163 lines
3.5 KiB
C
#include <errno.h>
|
|
#include <stdlib.h>
|
|
|
|
#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);
|
|
}
|