+ return -1;
+}
+
+static unsigned char **allocframebuffer(size_t width, size_t height)
+{
+ unsigned char **new, *tmp;
+ size_t i;
+
+ tmp = calloc(height, width);
+ if (!tmp) {
+ lbx_errno = -errno;
+ return NULL;
+ }
+
+ new = malloc(height * sizeof *new);
+ if (!new) {
+ lbx_errno = -errno;
+ free(tmp);
+ return NULL;
+ }
+
+ for (i = 0; i < height; i++) {
+ new[i] = tmp + i * width;
+ }
+
+ return new;
+}
+
+static unsigned char **read_raw_frame(struct lbx_image *img, int frame)
+{
+ unsigned long size = img->width * img->height;
+
+ assert(img->flags & FLAG_RAW);
+
+ if (img->fops->seek(img->f, img->offsets[frame], SEEK_SET)) {
+ lbx_errno = -errno;
+ return NULL;
+ }
+
+ if (img->fops->read(img->framedata[0], size, img->f) != size) {
+ lbx_errno = -errno;
+ if (img->fops->eof(img->f))
+ lbx_errno = LBX_EEOF;
+ return NULL;
+ }
+ memset(img->mask[0], 1, size);
+
+ if (img->fops->tell(img->f) > img->offsets[frame+1]) {
+ lbx_errno = LBX_EFORMAT;
+ return NULL;
+ }
+
+ return img->framedata;
+}
+
+unsigned char **lbximg_getframe(struct lbx_image *img, int frame)
+{
+ if (frame >= img->frames || frame < 0) {
+ lbx_errno = LBX_ERANGE;
+ return NULL;
+ }
+
+ if (!img->framedata) {
+ img->framedata = allocframebuffer(img->width, img->height);
+ if (!img->framedata)
+ return NULL;
+ }
+
+ if (!img->mask) {
+ img->mask = allocframebuffer(img->width, img->height);
+ if (!img->mask)
+ return NULL;
+ }
+
+ if (img->flags & FLAG_RAW)
+ return read_raw_frame(img, frame);
+
+ if ((img->flags & FLAG_OVERWRITE)
+ || (img->chunk && !(frame % img->chunk))) {
+ /* Clear the slate. */
+ img->currentframe = -1;
+ memset(img->framedata[0], 0, img->width * img->height);
+ memset(img->mask[0], 0, img->width * img->height);