backported imago from eradicate
[dosdemo] / src / image.c
diff --git a/src/image.c b/src/image.c
new file mode 100644 (file)
index 0000000..d4288a6
--- /dev/null
@@ -0,0 +1,219 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <imago2.h>
+#include "image.h"
+#include "treestor.h"
+#include "util.h"
+
+int load_image(struct image *img, const char *fname)
+{
+       FILE *fp;
+       char sig[8];
+       uint16_t width, height;
+
+       memset(img, 0, sizeof *img);
+
+       if(!(fp = fopen(fname, "rb"))) {
+               fprintf(stderr, "load_image: failed to open file: %s: %s\n", fname, strerror(errno));
+               return -1;
+       }
+       if(fread(sig, 1, 8, fp) < 8) {
+               fprintf(stderr, "unexpected EOF while reading: %s\n", fname);
+               fclose(fp);
+               return -1;
+       }
+
+       if(memcmp(sig, "IDUMP565", 8) != 0) {
+               goto not565;
+       }
+
+       /* it's a 565 dump, read it and return */
+       if(!fread(&width, 2, 1, fp) || !fread(&height, 2, 1, fp)) {
+               fprintf(stderr, "unexpected EOF while reading: %s\n", fname);
+               fclose(fp);
+               return -1;
+       }
+#ifdef BUILD_BIGENDIAN
+       width = BSWAP16(width);
+       height = BSWAP16(height);
+#endif
+
+       if(!(img->pixels = malloc(width * height * 2))) {
+               fprintf(stderr, "failed to allocate %dx%d pixel buffer for %s\n", width, height, fname);
+               fclose(fp);
+               return -1;
+       }
+       if(fread(img->pixels, 2, width * height, fp) < width * height) {
+               fprintf(stderr, "unexpected EOF while reading: %s\n", fname);
+               free(img->pixels);
+               img->pixels = 0;
+               fclose(fp);
+               return -1;
+       }
+
+#ifdef BUILD_BIGENDIAN
+       {
+               int i, npix = width * height;
+               for(i=0; i<npix; i++) {
+                       uint16_t p = img->pixels[i];
+                       img->pixels[i] = BSWAP16(p);
+               }
+       }
+#endif
+
+       fclose(fp);
+       img->width = width;
+       img->height = height;
+       return 0;
+not565:
+       fclose(fp);
+
+       if(memcmp(sig, "animtex", 7) == 0) {
+               /* it's an animated texture. read metadata, and recurse with the new name*/
+               struct ts_node *root, *node;
+               const char *imgfname;
+               int fps, num_frames = 0;
+
+               if(!(root = ts_load(fname))) {
+                       fprintf(stderr, "failed to load animation %s\n", fname);
+                       return -1;
+               }
+               if(!(imgfname = ts_lookup_str(root, "animtex.image", 0))) {
+                       fprintf(stderr, "animtex %s missing `image` attribute\n", fname);
+                       ts_free_tree(root);
+                       return -1;
+               }
+               if(strcmp(imgfname, fname) == 0) {
+                       fprintf(stderr, "animtex %s is silly...\n", fname);
+                       ts_free_tree(root);
+                       return -1;
+               }
+
+               if(load_image(img, imgfname) == -1) {
+                       ts_free_tree(root);
+                       return -1;
+               }
+
+               fps = ts_lookup_int(root, "animtex.framerate", 25);
+               img->frame_interval = 1.0f / (float)fps;
+
+               /* count frames */
+               node = root->child_list;
+               while(node) {
+                       if(strcmp(node->name, "frame") == 0) {
+                               num_frames++;
+                       }
+                       node = node->next;
+               }
+
+               if(!(img->uoffs = malloc(2 * num_frames * sizeof *img->uoffs))) {
+                       fprintf(stderr, "animtex %s: failed to allocate uvoffset array for %d frames\n", fname, num_frames);
+                       free(img->pixels);
+                       ts_free_tree(root);
+                       return -1;
+               }
+               img->voffs = img->uoffs + num_frames;
+
+               num_frames = 0;
+               node = root->child_list;
+               while(node) {
+                       if(strcmp(node->name, "frame") == 0) {
+                               float *v = ts_get_attr_vec(node, "uvoffset", 0);
+                               if(v) {
+                                       img->uoffs[num_frames] = v[0];
+                                       img->voffs[num_frames++] = v[1];
+                               } else {
+                                       fprintf(stderr, "animtex %s: ignoring frame without uvoffset\n", fname);
+                               }
+                       }
+                       node = node->next;
+               }
+
+               img->num_frames = num_frames;
+
+               ts_free_tree(root);
+               return 0;
+       }
+
+       /* just a regular image */
+       if(!(img->pixels = img_load_pixels(fname, &img->width, &img->height, IMG_FMT_RGB565))) {
+               fprintf(stderr, "failed to load image: %s\n", fname);
+               return -1;
+       }
+       return 0;
+}
+
+int dump_image(struct image *img, const char *fname)
+{
+       FILE *fp;
+       uint16_t width, height;
+
+#ifdef BUILD_BIGENDIAN
+       int i, npix = img->width * img->height;
+       width = BSWAP16(img->width);
+       height = BSWAP16(img->height);
+#else
+       width = img->width;
+       height = img->height;
+#endif
+
+       if(!(fp = fopen(fname, "wb"))) {
+               fprintf(stderr, "dump_image: failed to open %s: %s\n", fname, strerror(errno));
+               return -1;
+       }
+       fwrite("IDUMP565", 1, 8, fp);
+       fwrite(&width, 2, 1, fp);
+       fwrite(&height, 2, 1, fp);
+
+#ifdef BUILD_BIGENDIAN
+       for(i=0; i<npix; i++) {
+               uint16_t p = BSWAP16(img->pixels[i]);
+               fwrite(&p, 2, 1, fp);
+       }
+#else
+       fwrite(img->pixels, 2, img->width * img->height, fp);
+#endif
+       fclose(fp);
+       return 0;
+}
+
+void destroy_image(struct image *img)
+{
+       if(img) {
+               img_free_pixels(img->pixels);
+               img->pixels = 0;
+       }
+}
+
+int load_cubemap(struct image *cube, const char *fname_fmt)
+{
+       int i;
+       char dirstr[3] = {0};
+       char fname[256];
+
+       for(i=0; i<6; i++) {
+               dirstr[0] = i & 1 ? 'n' : 'p';
+               dirstr[1] = i < 2 ? 'x' : (i < 4 ? 'y' : 'z');
+               sprintf(fname, fname_fmt, dirstr);
+               if(load_image(cube + i, fname) == -1) {
+                       while(--i >= 0) {
+                               destroy_image(cube + i);
+                       }
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+void destroy_cubemap(struct image *cube)
+{
+       int i;
+
+       if(!cube) return;
+
+       for(i=0; i<6; i++) {
+               destroy_image(cube + i);
+       }
+}