From: John Tsiombikas Date: Tue, 17 Dec 2019 04:21:30 +0000 (+0200) Subject: added csprite tool for compiled sprites X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?p=dosdemo;a=commitdiff_plain;h=02d3f6c78597600d2b100748aa54241228b9ca09 added csprite tool for compiled sprites --- diff --git a/tools/csprite/Makefile b/tools/csprite/Makefile new file mode 100644 index 0000000..fa1448d --- /dev/null +++ b/tools/csprite/Makefile @@ -0,0 +1,13 @@ +src = $(wildcard src/*.c) +obj = $(src:.c=.o) +bin = csprite + +CFLAGS = -pedantic -Wall -g +LDFLAGS = -lpng -lz + +$(bin): $(obj) + $(CC) -o $@ $(obj) $(LDFLAGS) + +.PHONY: clean +clean: + rm -f $(obj) $(bin) diff --git a/tools/csprite/src/image.c b/tools/csprite/src/image.c new file mode 100644 index 0000000..18e8609 --- /dev/null +++ b/tools/csprite/src/image.c @@ -0,0 +1,247 @@ +#include +#include +#include +#include +#include +#include +#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; iscansz); + 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; iheight; 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; iheight; 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; ibpp / 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 +#include +#include +#include +#include +#include +#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= 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; ipixels + y * img->scansz + x; + struct csop *ops, *optr; + + ops = optr = alloca((xsz + 1) * ysz * sizeof *ops); + + for(i=0; i 0) { + optr++->op = CSOP_ENDL; + } + + for(j=0; j= 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; iop) { + 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; jlen / 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] \n", argv0); + printf("Options:\n"); + printf(" -s,-size : tile size (default: whole image)\n"); + printf(" -r,-rect : use rectangle of the input image (default: whole image)\n"); + printf(" -coffset : colormap offset [0, 255] (default: 0)\n"); + printf(" -fbpitch : target framebuffer pitch (scanline size in bytes)\n"); + printf(" -k,-key : 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"); +}