--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <png.h>
+#include "image.h"
+
+int alloc_image(struct image *img, int x, int y, int bpp)
+{
+ memset(img, 0, sizeof *img);
+ img->width = x;
+ img->height = y;
+ img->bpp = bpp;
+ img->scansz = img->pitch = x * bpp / 8;
+
+ if(!(img->pixels = malloc(y * img->scansz))) {
+ printf("failed to allocate %dx%d (%dbpp) pixel buffer\n", x, y, bpp);
+ return -1;
+ }
+
+ /* just a guess, assume the user will fill the details, but set up reasonable
+ * defaults just in case...
+ */
+ if(bpp <= 8) {
+ img->nchan = 1;
+ img->cmap_ncolors = 1 << bpp;
+ } else if(bpp <= 24) {
+ img->nchan = 3;
+ } else {
+ img->nchan = 4;
+ }
+ return 0;
+}
+
+int load_image(struct image *img, const char *fname)
+{
+ int i;
+ FILE *fp;
+ png_struct *png;
+ png_info *info;
+ int chan_bits, color_type;
+ png_uint_32 xsz, ysz;
+ png_color *palette;
+ unsigned char **scanline;
+ unsigned char *dptr;
+
+ if(!(fp = fopen(fname, "rb"))) {
+ printf("failed to open: %s: %s\n", fname, strerror(errno));
+ return -1;
+ }
+
+ if(!(png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0))) {
+ fclose(fp);
+ return -1;
+ }
+ if(!(info = png_create_info_struct(png))) {
+ fclose(fp);
+ png_destroy_read_struct(&png, 0, 0);
+ return -1;
+ }
+ if(setjmp(png_jmpbuf(png))) {
+ fclose(fp);
+ png_destroy_read_struct(&png, &info, 0);
+ return -1;
+ }
+
+ png_init_io(png, fp);
+ png_read_png(png, info, 0, 0);
+
+ png_get_IHDR(png, info, &xsz, &ysz, &chan_bits, &color_type, 0, 0, 0);
+ img->width = xsz;
+ img->height = ysz;
+ img->nchan = png_get_channels(png, info);
+ img->bpp = img->nchan * chan_bits;
+ img->scansz = img->pitch = xsz * img->bpp / 8;
+ img->cmap_ncolors = 0;
+
+ if(color_type == PNG_COLOR_TYPE_PALETTE) {
+ png_get_PLTE(png, info, &palette, &img->cmap_ncolors);
+ memcpy(img->cmap, palette, img->cmap_ncolors * sizeof *img->cmap);
+ }
+
+ if(!(img->pixels = malloc(ysz * img->scansz))) {
+ perror("failed to allocate pixel buffer");
+ fclose(fp);
+ png_destroy_read_struct(&png, &info, 0);
+ return -1;
+ }
+ dptr = img->pixels;
+
+ scanline = (unsigned char**)png_get_rows(png, info);
+ for(i=0; i<ysz; i++) {
+ memcpy(dptr, scanline[i], img->scansz);
+ dptr += img->pitch;
+ }
+
+ fclose(fp);
+ png_destroy_read_struct(&png, &info, 0);
+ return 0;
+}
+
+int save_image(struct image *img, const char *fname)
+{
+ int i, chan_bits, coltype;
+ FILE *fp;
+ png_struct *png;
+ png_info *info;
+ png_text txt;
+ unsigned char **scanline = 0;
+ unsigned char *pptr;
+
+ if(!(fp = fopen(fname, "wb"))) {
+ printf("save_image: failed to open: %s: %s\n", fname, strerror(errno));
+ return -1;
+ }
+
+ if(!(png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0))) {
+ fclose(fp);
+ return -1;
+ }
+ if(!(info = png_create_info_struct(png))) {
+ png_destroy_write_struct(&png, 0);
+ fclose(fp);
+ return -1;
+ }
+
+ txt.compression = PNG_TEXT_COMPRESSION_NONE;
+ txt.key = "Software";
+ txt.text = "img2tiles";
+ txt.text_length = 0;
+
+ if(setjmp(png_jmpbuf(png))) {
+ png_destroy_write_struct(&png, &info);
+ free(scanline);
+ fclose(fp);
+ return -1;
+ }
+
+ switch(img->nchan) {
+ case 1:
+ if(img->cmap_ncolors > 0) {
+ coltype = PNG_COLOR_TYPE_PALETTE;
+ } else {
+ coltype = PNG_COLOR_TYPE_GRAY;
+ }
+ break;
+ case 2:
+ coltype = PNG_COLOR_TYPE_GRAY_ALPHA;
+ break;
+ case 3:
+ coltype = PNG_COLOR_TYPE_RGB;
+ break;
+ case 4:
+ coltype = PNG_COLOR_TYPE_RGB_ALPHA;
+ break;
+ }
+
+ chan_bits = img->bpp / img->nchan;
+ png_set_IHDR(png, info, img->width, img->height, chan_bits, coltype, PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+ png_set_text(png, info, &txt, 1);
+
+ if(img->cmap_ncolors > 0) {
+ png_set_PLTE(png, info, (png_color*)img->cmap, img->cmap_ncolors);
+ }
+
+ if(!(scanline = malloc(img->height * sizeof *scanline))) {
+ png_destroy_write_struct(&png, &info);
+ fclose(fp);
+ return -1;
+ }
+
+ pptr = img->pixels;
+ for(i=0; i<img->height; i++) {
+ scanline[i] = pptr;
+ pptr += img->pitch;
+ }
+ png_set_rows(png, info, scanline);
+
+ png_init_io(png, fp);
+ png_write_png(png, info, 0, 0);
+ png_destroy_write_struct(&png, &info);
+ free(scanline);
+ fclose(fp);
+ return 0;
+}
+
+
+int cmp_image(struct image *a, struct image *b)
+{
+ int i;
+ unsigned char *aptr = a->pixels;
+ unsigned char *bptr = b->pixels;
+
+ if(a->width != b->width || a->height != b->height || a->bpp != b->bpp || a->nchan != b->nchan) {
+ return -1;
+ }
+
+ for(i=0; i<a->height; i++) {
+ if(memcmp(aptr, bptr, a->scansz) != 0) {
+ return -1;
+ }
+ aptr += a->pitch;
+ bptr += b->pitch;
+ }
+ return 0;
+}
+
+void blit_image(struct image *src, int sx, int sy, int w, int h, struct image *dst, int dx, int dy)
+{
+ int i;
+ unsigned char *sptr, *dptr;
+
+ assert(src->bpp == dst->bpp);
+ assert(src->nchan == dst->nchan);
+
+ if(sx < 0) { w += sx; sx = 0; }
+ if(sy < 0) { h += sy; sy = 0; }
+ if(dx < 0) { w += dx; sx -= dx; dx = 0; }
+ if(dy < 0) { h += dy; sy -= dy; dy = 0; }
+ if(sx + w >= src->width) w = src->width - sx;
+ if(sy + h >= src->height) h = src->height - sy;
+ if(dx + w >= dst->width) w = dst->width - dx;
+ if(dy + h >= dst->height) h = dst->height - dy;
+
+ if(w <= 0 || h <= 0) return;
+
+ sptr = src->pixels + sy * src->pitch + sx * src->bpp / 8;
+ dptr = dst->pixels + dy * dst->pitch + dx * dst->bpp / 8;
+
+ for(i=0; i<h; i++) {
+ memcpy(dptr, sptr, w * dst->bpp / 8);
+ dptr += dst->pitch;
+ sptr += src->pitch;
+ }
+}
+
+void image_color_offset(struct image *img, int offs)
+{
+ int i, sz = img->height * img->pitch;
+ unsigned char *pptr = img->pixels;
+
+ for(i=0; i<sz; i++) {
+ *pptr++ += offs;
+ }
+}
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <assert.h>
+#include <alloca.h>
+#include "image.h"
+
+struct rect {
+ int x, y, w, h;
+};
+
+enum { AS_GNU, AS_NASM };
+
+int csprite(struct image *img, int x, int y, int xsz, int ysz);
+int proc_sheet(const char *fname);
+void print_usage(const char *argv0);
+
+int tile_xsz, tile_ysz;
+struct rect rect;
+int cmap_offs;
+int ckey;
+int fbpitch = 320;
+const char *name = "sprite";
+int asyntax = AS_GNU;
+
+int main(int argc, char **argv)
+{
+ int i;
+ char *endp;
+
+ for(i=1; i<argc; i++) {
+ if(argv[i][0] == '-') {
+ if(strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "-size") == 0) {
+ if(sscanf(argv[++i], "%dx%d", &tile_xsz, &tile_ysz) != 2 ||
+ tile_xsz <= 0 || tile_ysz <= 0) {
+ fprintf(stderr, "%s must be followed by WIDTHxHEIGHT\n", argv[i - 1]);
+ return 1;
+ }
+
+ } else if(strcmp(argv[i], "-r") == 0 || strcmp(argv[i], "-rect") == 0) {
+ rect.x = rect.y = 0;
+ if(sscanf(argv[++i], "%dx%d+%d+%d", &rect.w, &rect.h, &rect.x, &rect.y) < 2 || rect.w <= 0 || rect.h <= 0) {
+ fprintf(stderr, "%s must be followed by WIDTHxHEIGHT+X+Y\n", argv[i - 1]);
+ return 1;
+ }
+
+ } else if(strcmp(argv[i], "-coffset") == 0) {
+ cmap_offs = strtol(argv[++i], &endp, 10);
+ if(endp == argv[i] || cmap_offs < 0 || cmap_offs >= 256) {
+ fprintf(stderr, "-coffset must be followed by a valid colormap offset\n");
+ return 1;
+ }
+
+ } else if(strcmp(argv[i], "-fbpitch") == 0) {
+ fbpitch = atoi(argv[++i]);
+ if(fbpitch <= 0) {
+ fprintf(stderr, "-fbpitch must be followed by a positive number\n");
+ return 1;
+ }
+
+ } else if(strcmp(argv[i], "-n") == 0 || strcmp(argv[i], "-name") == 0) {
+ name = argv[++i];
+ if(!name) {
+ fprintf(stderr, "%s must be followed by a name\n", argv[i - 1]);
+ return 1;
+ }
+
+ } else if(strcmp(argv[i], "-k") == 0 || strcmp(argv[i], "-key") == 0) {
+ ckey = strtol(argv[++i], &endp, 10);
+ if(endp == argv[i] || ckey < 0 || ckey >= 256) {
+ fprintf(stderr, "%s must be followed by a valid color key\n", argv[i - 1]);
+ return 1;
+ }
+
+ } else if(strcmp(argv[i], "-gas") == 0) {
+ asyntax = AS_GNU;
+
+ } else if(strcmp(argv[i], "-nasm") == 0) {
+ asyntax = AS_NASM;
+
+ } else if(strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "-help") == 0) {
+ print_usage(argv[0]);
+ return 0;
+
+ } else {
+ fprintf(stderr, "invalid option: %s\n", argv[i]);
+ print_usage(argv[0]);
+ return 1;
+ }
+
+ } else {
+ if(proc_sheet(argv[i]) == -1) {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+const char *prefixfmt[] = {
+ /* GNU assembler template */
+ "\t.global %s\n"
+ "%s:\n"
+ "\tmov 12(%%esp), %%eax\n"
+ "\tmov $%d, %%ecx\n"
+ "\tmul %%ecx\n"
+ "\tadd 8(%%esp), %%eax\n"
+ "\tadd 4(%%esp), %%eax\n"
+ "\tmov %%eax, %%edx\n"
+ "\tmov 16(%%esp), %%eax\n"
+ "\tjmp *tiletab(,%%eax,4)\n\n"
+ "tiletab:\n",
+
+ /* NASM template */
+ "\tglobl %s\n"
+ "%s:\n"
+ "\tmov eax, [esp + 12]\n"
+ "\tmov ecx. %d\n"
+ "\tmul ecx\n"
+ "\tadd eax, [esp + 8]\n"
+ "\tadd eax, [esp + 4]\n"
+ "\tmov edx, eax\n"
+ "\tmov eax, [esp + 16]\n"
+ "\tjmp [titletab + eax * 4]\n\n"
+ "titletab:\n"
+};
+
+int proc_sheet(const char *fname)
+{
+ int i, j, num_xtiles, num_ytiles, xsz, ysz, tidx;
+ struct image img;
+
+ if(load_image(&img, fname) == -1) {
+ fprintf(stderr, "failed to load image: %s\n", fname);
+ return -1;
+ }
+ if(rect.w <= 0) {
+ rect.w = img.width;
+ rect.h = img.height;
+ }
+
+ if(tile_xsz <= 0) {
+ num_xtiles = num_ytiles = 1;
+ xsz = rect.w;
+ ysz = rect.h;
+ } else {
+ num_xtiles = rect.w / tile_xsz;
+ num_ytiles = rect.h / tile_ysz;
+ xsz = tile_xsz;
+ ysz = tile_ysz;
+ }
+
+ printf(prefixfmt[asyntax], name, name, fbpitch);
+ for(i=0; i<num_ytiles*num_xtiles; i++) {
+ if(asyntax == AS_GNU) {
+ printf("\t.long tile%d\n", i);
+ } else {
+ printf("\tdd tile%d\n", i);
+ }
+ }
+ putchar('\n');
+
+ tidx = 0;
+ for(i=0; i<num_ytiles; i++) {
+ for(j=0; j<num_xtiles; j++) {
+ printf("tile%d:\n", tidx++);
+ csprite(&img, rect.x + j * xsz, rect.y + i * ysz, xsz, ysz);
+ }
+ }
+
+ return 0;
+}
+
+enum {
+ CSOP_SKIP,
+ CSOP_FILL,
+ CSOP_COPY,
+ CSOP_ENDL
+};
+
+struct csop {
+ unsigned char op, val;
+ int len;
+};
+
+int csprite(struct image *img, int x, int y, int xsz, int ysz)
+{
+ int i, j, numops, mode, new_mode, start, skip_acc;
+ unsigned char *pptr = img->pixels + y * img->scansz + x;
+ struct csop *ops, *optr;
+
+ ops = optr = alloca((xsz + 1) * ysz * sizeof *ops);
+
+ for(i=0; i<ysz; i++) {
+ mode = -1;
+ start = -1;
+
+ if(i > 0) {
+ optr++->op = CSOP_ENDL;
+ }
+
+ for(j=0; j<xsz; j++) {
+ if(*pptr == ckey) {
+ new_mode = CSOP_SKIP;
+ } else {
+ new_mode = CSOP_COPY;
+ }
+
+ if(new_mode != mode) {
+ if(mode != -1) {
+ assert(start >= 0);
+ optr->op = mode;
+ optr->len = j - start;
+ optr++;
+ }
+ mode = new_mode;
+ start = j;
+ }
+ pptr++;
+ }
+ pptr += img->scansz - xsz;
+
+ if(mode != -1) {
+ assert(start >= 0);
+ optr->op = mode;
+ optr->len = xsz - start;
+ optr++;
+ }
+ }
+ numops = optr - ops;
+
+ pptr = img->pixels + y * img->scansz + x;
+ optr = ops;
+ skip_acc = 0;
+ /* edx points to dest */
+ for(i=0; i<numops; i++) {
+ switch(optr->op) {
+ case CSOP_SKIP:
+ skip_acc += optr->len;
+ pptr += optr->len;
+ break;
+
+ case CSOP_ENDL:
+ skip_acc += fbpitch - xsz;
+ pptr += img->scansz - xsz;
+ break;
+
+ case CSOP_COPY:
+ if(skip_acc) {
+ if(asyntax == AS_GNU) {
+ printf("\tadd $%d, %%edx\n", skip_acc);
+ } else {
+ printf("\tadd edx, %d\n", skip_acc);
+ }
+ skip_acc = 0;
+ }
+
+ for(j=0; j<optr->len / 4; j++) {
+ if(asyntax == AS_GNU) {
+ printf("\tmovl $0x%x, %d(%%edx)\n", *(uint32_t*)pptr, j * 4);
+ } else {
+ printf("\tmov dword [edx + %d], 0x%x\n", j * 4, *(uint32_t*)pptr);
+ }
+ pptr += 4;
+ }
+ j *= 4;
+ switch(optr->len % 4) {
+ case 3:
+ if(asyntax == AS_GNU) {
+ printf("\tmovb $0x%x, %d(%%edx)\n", (unsigned int)*pptr++, j++);
+ } else {
+ printf("\tmov byte [edx + %d], 0x%x\n", j++, (unsigned int)*pptr++);
+ }
+ case 2:
+ if(asyntax == AS_GNU) {
+ printf("\tmovw $0x%x, %d(%%edx)\n", (unsigned int)*(uint16_t*)pptr, j);
+ } else {
+ printf("\tmov word [edx + %d], 0x%x\n", j, (unsigned int)*(uint16_t*)pptr);
+ }
+ pptr += 2;
+ j += 2;
+ break;
+ case 1:
+ if(asyntax == AS_GNU) {
+ printf("\tmovb $0x%x, %d(%%edx)\n", (unsigned int)*pptr++, j++);
+ } else {
+ printf("\tmov byte [edx + %d], 0x%x\n", j++, (unsigned int)*pptr++);
+ }
+ break;
+ }
+
+ skip_acc = optr->len;
+ break;
+
+ default:
+ printf("\t%c invalid op: %d\n", asyntax == AS_GNU ? '#' : ';', optr->op);
+ }
+ optr++;
+ }
+ printf("\tret\n");
+
+ return 0;
+}
+
+void print_usage(const char *argv0)
+{
+ printf("Usage: %s [options] <spritesheet>\n", argv0);
+ printf("Options:\n");
+ printf(" -s,-size <WxH>: tile size (default: whole image)\n");
+ printf(" -r,-rect <WxH+X+Y>: use rectangle of the input image (default: whole image)\n");
+ printf(" -coffset <offs>: colormap offset [0, 255] (default: 0)\n");
+ printf(" -fbpitch <pitch>: target framebuffer pitch (scanline size in bytes)\n");
+ printf(" -k,-key <color>: color-key for transparency (default: 0)\n");
+ printf(" -gas: output GNU assembler code (default)\n");
+ printf(" -nasm: output NASM-compatible code\n");
+ printf(" -h: print usage and exit\n");
+}