# HG changeset patch # User John Tsiombikas # Date 1500114392 -10800 # Node ID bfd8aa2ca393f58098620f2477f29d8506ee62d8 initial commit diff -r 000000000000 -r bfd8aa2ca393 Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile Sat Jul 15 13:26:32 2017 +0300 @@ -0,0 +1,13 @@ +src = $(wildcard src/*.c) +obj = $(src:.c=.o) + +bin = ilbm2bin + +LDFLAGS = -lm + +$(bin): $(obj) + $(CC) -o $@ $(obj) $(LDFLAGS) + +.PHONY: clean +clean: + rm -f $(obj) $(bin) diff -r 000000000000 -r bfd8aa2ca393 src/image.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/image.c Sat Jul 15 13:26:32 2017 +0300 @@ -0,0 +1,485 @@ +/* +colcycle - color cycling image viewer +Copyright (C) 2016-2017 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#include +#include +#include +#include +#include +#include +#if defined(__WATCOMC__) || defined(_MSC_VER) || defined(WIN32) +#include +#else +#include +#endif +#include "image.h" +#include "imagelbm.h" + +#ifndef M_PI +#define M_PI 3.141593 +#endif + +static int flatten_crange_list(struct image *img); + +int gen_test_image(struct image *img) +{ + int i, j; + unsigned char *pptr; + + img->width = 640; + img->height = 480; + img->bpp = 8; + + if(!(img->range = malloc(sizeof *img->range))) { + return -1; + } + img->num_ranges = 1; + img->range[0].low = 0; + img->range[0].high = 255; + img->range[0].cmode = CYCLE_NORMAL; + img->range[0].rate = 5000; + + if(!(img->pixels = malloc(img->width * img->height))) { + free(img->range); + return -1; + } + + for(i=0; i<256; i++) { + float theta = M_PI * 2.0 * (float)i / 256.0; + float r = cos(theta) * 0.5 + 0.5; + float g = sin(theta) * 0.5 + 0.5; + float b = -cos(theta) * 0.5 + 0.5; + img->palette[i].r = (int)(r * 255.0); + img->palette[i].g = (int)(g * 255.0); + img->palette[i].b = (int)(b * 255.0); + } + + pptr = img->pixels; + for(i=0; iheight; i++) { + int c = (i << 8) / img->height; + for(j=0; jwidth; j++) { + int chess = ((i >> 6) & 1) == ((j >> 6) & 1); + *pptr++ = (chess ? c : c + 128) & 0xff; + } + } + return 0; +} + +#define MAX_TOKEN_SIZE 256 +static int image_block(FILE *fp, struct image *img); +static int nextc = -1; +static char token[MAX_TOKEN_SIZE]; + +int load_image(struct image *img, const char *fname) +{ + FILE *fp; + int c; + + if(!(fp = fopen(fname, "rb"))) { + fprintf(stderr, "failed to open file: %s: %s\n", fname, strerror(errno)); + return -1; + } + + if(file_is_lbm(fp)) { + if(load_image_lbm(img, fp) == -1) { + fclose(fp); + return -1; + } + fclose(fp); + flatten_crange_list(img); + return 0; + } + + /* find the start of the root block */ + while((c = fgetc(fp)) != -1 && c != '{'); + if(feof(fp)) { + fprintf(stderr, "invalid image format, no image block found: %s\n", fname); + return -1; + } + + nextc = c; /* prime the pump with the extra char we read above */ + if(image_block(fp, img) == -1) { + fprintf(stderr, "failed to read image block from: %s\n", fname); + fclose(fp); + return -1; + } + fclose(fp); + + flatten_crange_list(img); + return 0; +} + +void destroy_image(struct image *img) +{ + if(img) { + free(img->pixels); + free(img->range); + memset(img, 0, sizeof *img); + } +} + +static int flatten_crange_list(struct image *img) +{ + struct colrange *list = img->range; + struct colrange *rptr; + + if(img->num_ranges <= 0) { + return 0; + } + + if(!(img->range = malloc(img->num_ranges * sizeof *img->range))) { + perror("flatten_crange_list: failed to allocate range array\n"); + return -1; + } + + rptr = img->range; + while(list) { + struct colrange *rng = list; + list = list->next; + *rptr++ = *rng; + free(rng); + } + return 0; +} + +/* ---- parser ---- */ + +enum { + TOKEN_NUM, + TOKEN_NAME, + TOKEN_STR +}; + +static int next_char(FILE *fp) +{ + while((nextc = fgetc(fp)) != -1 && isspace(nextc)); + return nextc; +} + +static int next_token(FILE *fp) +{ + char *ptr; + + if(nextc == -1) { + return -1; + } + + switch(nextc) { + case '{': + case '}': + case ',': + case '[': + case ']': + case ':': + token[0] = nextc; + token[1] = 0; + nextc = next_char(fp); + return token[0]; + + case '\'': + ptr = token; + nextc = next_char(fp); + while(nextc != -1 && nextc != '\'') { + *ptr++ = nextc; + nextc = fgetc(fp); + } + nextc = next_char(fp); + return TOKEN_STR; + + default: + break; + } + + if(isalpha(nextc)) { + ptr = token; + while(nextc != -1 && isalpha(nextc)) { + *ptr++ = nextc; + nextc = next_char(fp); + } + *ptr = 0; + return TOKEN_NAME; + } + if(isdigit(nextc)) { + ptr = token; + while(nextc != -1 && isdigit(nextc)) { + *ptr++ = nextc; + nextc = next_char(fp); + } + *ptr = 0; + return TOKEN_NUM; + } + + token[0] = nextc; + token[1] = 0; + fprintf(stderr, "next_token: unexpected character: %c\n", nextc); + return -1; +} + +static int expect(FILE *fp, int tok) +{ + if(next_token(fp) != tok) { + return 0; + } + return 1; +} + +static const char *toktypestr(int tok) +{ + static char buf[] = "' '"; + switch(tok) { + case TOKEN_NUM: + return "number"; + case TOKEN_NAME: + return "name"; + case TOKEN_STR: + return "string"; + default: + break; + } + buf[1] = tok; + return buf; +} + +#define EXPECT(fp, x) \ + do { \ + if(!expect(fp, x)) { \ + fprintf(stderr, "%s: expected: %s, found: %s\n", __FUNCTION__, toktypestr(x), token); \ + return -1; \ + } \ + } while(0) + +static int palette(FILE *fp, struct image *img) +{ + int tok, cidx = 0; + struct color col, *cptr; + + EXPECT(fp, '['); + + while((tok = next_token(fp)) == '[') { + cptr = cidx < 256 ? &img->palette[cidx] : &col; + ++cidx; + + EXPECT(fp, TOKEN_NUM); + cptr->r = atoi(token); + EXPECT(fp, ','); + EXPECT(fp, TOKEN_NUM); + cptr->g = atoi(token); + EXPECT(fp, ','); + EXPECT(fp, TOKEN_NUM); + cptr->b = atoi(token); + EXPECT(fp, ']'); + + if(nextc == ',') { + next_token(fp); /* skip comma */ + } + } + + if(tok != ']') { + fprintf(stderr, "palette must be closed by a ']' token\n"); + return -1; + } + return 0; +} + +static int crange(FILE *fp, struct colrange *rng) +{ + int val; + char name[MAX_TOKEN_SIZE]; + + EXPECT(fp, '{'); + + while(nextc != -1 && nextc != '}') { + EXPECT(fp, TOKEN_NAME); + strcpy(name, token); + EXPECT(fp, ':'); + EXPECT(fp, TOKEN_NUM); + val = atoi(token); + + if(strcmp(name, "reverse") == 0) { + rng->cmode = val; + } else if(strcmp(name, "rate") == 0) { + rng->rate = val; + } else if(strcmp(name, "low") == 0) { + rng->low = val; + } else if(strcmp(name, "high") == 0) { + rng->high = val; + } else { + fprintf(stderr, "invalid attribute %s in cycles range\n", name); + return -1; + } + + if(nextc == ',') { + next_token(fp); + } + } + + EXPECT(fp, '}'); + return 0; +} + +static int cycles(FILE *fp, struct image *img) +{ + struct colrange *list = 0, *rng; + + EXPECT(fp, '['); + + img->num_ranges = 0; + while(nextc == '{') { + if(!(rng = malloc(sizeof *rng))) { + perror("failed to allocate color range"); + goto err; + } + if(crange(fp, rng) == -1) { + free(rng); + goto err; + } + if(rng->low != rng->high && rng->rate > 0) { + rng->next = list; + list = rng; + ++img->num_ranges; + } else { + free(rng); + } + + if(nextc == ',') { + next_token(fp); /* eat the comma */ + } + } + + img->range = list; + + if(!expect(fp, ']')) { + fprintf(stderr, "cycles: missing closing bracket\n"); + goto err; + } + return 0; + +err: + while(list) { + rng = list; + list = list->next; + free(rng); + } + img->num_ranges = 0; + return -1; +} + +static int pixels(FILE *fp, struct image *img) +{ + int tok, num_pixels; + unsigned char *pptr; + + if(img->width <= 0 || img->height <= 0) { + fprintf(stderr, "pixel block found before defining the image dimensions!\n"); + return -1; + } + num_pixels = img->width * img->height; + if(!(img->pixels = malloc(num_pixels))) { + perror("failed to allocate pixels"); + return -1; + } + pptr = img->pixels; + + EXPECT(fp, '['); + + while((tok = next_token(fp)) == TOKEN_NUM) { + if(pptr - img->pixels >= num_pixels) { + pptr = 0; + fprintf(stderr, "more pixel data provided than the specified image dimensions\n"); + } + if(!pptr) continue; + *pptr++ = atoi(token); + + if(nextc == ',') { + next_token(fp); /* eat comma */ + } + } + + if(tok != ']') { + printf("pixels: missing closing bracket\n"); + return -1; + } + return 0; +} + +static int attribute(FILE *fp, struct image *img) +{ + char *attr_name; + + if(!expect(fp, TOKEN_NAME)) { + return -1; + } + attr_name = alloca(strlen(token) + 1); + strcpy(attr_name, token); + + if(!expect(fp, ':')) { + return -1; + } + + if(strcmp(attr_name, "filename") == 0) { + if(!expect(fp, TOKEN_STR)) { + fprintf(stderr, "attribute: filename should be a string\n"); + return -1; + } + + } else if(strcmp(attr_name, "width") == 0 || strcmp(attr_name, "height") == 0) { + int *dst = attr_name[0] == 'w' ? &img->width : &img->height; + if(!expect(fp, TOKEN_NUM)) { + fprintf(stderr, "attribute: %s should be a number\n", attr_name); + return -1; + } + *dst = atoi(token); + + } else if(strcmp(attr_name, "colors") == 0) { + if(palette(fp, img) == -1) { + fprintf(stderr, "attribute: colors should be a palette\n"); + return -1; + } + + } else if(strcmp(attr_name, "cycles") == 0) { + if(cycles(fp, img) == -1) { + fprintf(stderr, "attribute: cycles should be a list of palranges\n"); + return -1; + } + + } else if(strcmp(attr_name, "pixels") == 0) { + if(pixels(fp, img) == -1) { + fprintf(stderr, "attribute: pixels should be a list of indices\n"); + return -1; + } + + } else { + fprintf(stderr, "unknown attribute: %s\n", attr_name); + return -1; + } + return 0; +} + +static int image_block(FILE *fp, struct image *img) +{ + EXPECT(fp, '{'); + + img->width = img->height = -1; + img->bpp = 8; + + while(attribute(fp, img) != -1) { + if(nextc == ',') { + next_token(fp); /* eat comma */ + } + } + return 0; +} diff -r 000000000000 -r bfd8aa2ca393 src/image.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/image.h Sat Jul 15 13:26:32 2017 +0300 @@ -0,0 +1,54 @@ +/* +colcycle - color cycling image viewer +Copyright (C) 2016 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef IMAGE_H_ +#define IMAGE_H_ + +enum cycle_mode { + CYCLE_NORMAL = 0, + CYCLE_UNUSED, /* ? */ + CYCLE_REVERSE = 2, + CYCLE_PINGPONG = 3, + CYCLE_SINE_HALF = 4, /* sine -> [0, range/2] */ + CYCLE_SINE = 5 /* sine -> [0, range] */ +}; + +struct color { + unsigned char r, g, b; +}; + +struct colrange { + int low, high; + int cmode; + unsigned int rate; + struct colrange *next; +}; + +struct image { + int width, height; + int bpp; + struct color palette[256]; + struct colrange *range; + int num_ranges; + unsigned char *pixels; +}; + +int gen_test_image(struct image *img); +int load_image(struct image *img, const char *fname); +void destroy_image(struct image *img); + +#endif /* IMAGE_H_ */ diff -r 000000000000 -r bfd8aa2ca393 src/imagelbm.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/imagelbm.c Sat Jul 15 13:26:32 2017 +0300 @@ -0,0 +1,463 @@ +/* +colcycle - color cycling image viewer +Copyright (C) 2016-2017 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#include +#include +#include +#include +#include +#if defined(__WATCOMC__) || defined(_MSC_VER) +#include +#else +#include +#endif +#include "imagelbm.h" + +#ifdef __GNUC__ +#define PACKED __attribute__((packed)) +#endif + +#ifdef __BYTE_ORDER__ +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define LENDIAN +#else +#define BENDIAN +#endif +#endif + +#define MKID(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) + +#define IS_IFF_CONTAINER(id) ((id) == IFF_FORM || (id) == IFF_CAT || (id) == IFF_LIST) + +enum { + IFF_FORM = MKID('F', 'O', 'R', 'M'), + IFF_CAT = MKID('C', 'A', 'T', ' '), + IFF_LIST = MKID('L', 'I', 'S', 'T'), + IFF_ILBM = MKID('I', 'L', 'B', 'M'), + IFF_PBM = MKID('P', 'B', 'M', ' '), + IFF_BMHD = MKID('B', 'M', 'H', 'D'), + IFF_CMAP = MKID('C', 'M', 'A', 'P'), + IFF_BODY = MKID('B', 'O', 'D', 'Y'), + IFF_CRNG = MKID('C', 'R', 'N', 'G') +}; + + +struct chdr { + uint32_t id; + uint32_t size; +}; + +#ifdef __WATCOMC__ +#pragma push(pack, 1) +#endif +struct bitmap_header { + uint16_t width, height; + int16_t xoffs, yoffs; + uint8_t nplanes; + uint8_t masking; + uint8_t compression; + uint8_t padding; + uint16_t colorkey; + uint8_t aspect_num, aspect_denom; + int16_t pgwidth, pgheight; +} PACKED; +#ifdef __WATCOMC__ +#pragma pop(pack) +#endif + +enum { + MASK_NONE, + MASK_PLANE, + MASK_COLORKEY, + MASK_LASSO +}; + +struct crng { + uint16_t padding; + uint16_t rate; + uint16_t flags; + uint8_t low, high; +}; + +enum { + CRNG_ENABLE = 1, + CRNG_REVERSE = 2 +}; + +static int read_header(FILE *fp, struct chdr *hdr); +static int read_ilbm_pbm(FILE *fp, uint32_t type, uint32_t size, struct image *img); +static int read_bmhd(FILE *fp, struct bitmap_header *bmhd); +static int read_crng(FILE *fp, struct crng *crng); +static int read_body_ilbm(FILE *fp, struct bitmap_header *bmhd, struct image *img); +static int read_body_pbm(FILE *fp, struct bitmap_header *bmhd, struct image *img); +static int read_compressed_scanline(FILE *fp, unsigned char *scanline, int width); +static int read16(FILE *fp, uint16_t *res); +static int read32(FILE *fp, uint32_t *res); +static uint16_t swap16(uint16_t x); +static uint32_t swap32(uint32_t x); + +int file_is_lbm(FILE *fp) +{ + uint32_t type; + struct chdr hdr; + + while(read_header(fp, &hdr) != -1) { + if(IS_IFF_CONTAINER(hdr.id)) { + if(read32(fp, &type) == -1) { + break; + } + + if(type == IFF_ILBM || type == IFF_PBM ) { + rewind(fp); + return 1; + } + hdr.size -= sizeof type; /* so we will seek fwd correctly */ + } + fseek(fp, hdr.size, SEEK_CUR); + } + fseek(fp, 0, SEEK_SET); + return 0; +} + +void print_chunkid(uint32_t id) +{ + char str[5] = {0}; +#ifdef LENDIAN + id = swap32(id); +#endif + memcpy(str, &id, 4); + puts(str); +} + +int load_image_lbm(struct image *img, FILE *fp) +{ + uint32_t type; + struct chdr hdr; + + while(read_header(fp, &hdr) != -1) { + if(IS_IFF_CONTAINER(hdr.id)) { + if(read32(fp, &type) == -1) { + break; + } + hdr.size -= sizeof type; /* to compensate for having advanced 4 more bytes */ + + if(type == IFF_ILBM) { + if(read_ilbm_pbm(fp, type, hdr.size, img) == -1) { + return -1; + } + return 0; + } + if(type == IFF_PBM) { + if(read_ilbm_pbm(fp, type, hdr.size, img) == -1) { + return -1; + } + return 0; + } + } + fseek(fp, hdr.size, SEEK_CUR); + } + return 0; +} + +static int read_header(FILE *fp, struct chdr *hdr) +{ + if(fread(hdr, 1, sizeof *hdr, fp) < sizeof *hdr) { + return -1; + } +#ifdef LENDIAN + hdr->id = swap32(hdr->id); + hdr->size = swap32(hdr->size); +#endif + return 0; +} + +static int read_ilbm_pbm(FILE *fp, uint32_t type, uint32_t size, struct image *img) +{ + int i, res = -1; + struct chdr hdr; + struct bitmap_header bmhd; + struct crng crng; + struct colrange *crnode; + unsigned char pal[3 * 256]; + unsigned char *pptr; + long start = ftell(fp); + + memset(img, 0, sizeof *img); + + while(read_header(fp, &hdr) != -1 && ftell(fp) - start < size) { + switch(hdr.id) { + case IFF_BMHD: + assert(hdr.size == 20); + if(read_bmhd(fp, &bmhd) == -1) { + return -1; + } + img->width = bmhd.width; + img->height = bmhd.height; + img->bpp = bmhd.nplanes; + if(bmhd.nplanes > 8) { + fprintf(stderr, "%d planes found, only paletized LBM files supported\n", bmhd.nplanes); + return -1; + } + if(!(img->pixels = malloc(img->width * img->height))) { + fprintf(stderr, "failed to allocate %dx%d image\n", img->width, img->height); + return -1; + } + break; + + case IFF_CMAP: + assert(hdr.size / 3 <= 256); + + if(fread(pal, 1, hdr.size, fp) < hdr.size) { + fprintf(stderr, "failed to read colormap\n"); + return -1; + } + pptr = pal; + for(i=0; i<256; i++) { + img->palette[i].r = *pptr++; + img->palette[i].g = *pptr++; + img->palette[i].b = *pptr++; + } + break; + + case IFF_CRNG: + assert(hdr.size == sizeof crng); + + if(read_crng(fp, &crng) == -1) { + fprintf(stderr, "failed to read color cycling range chunk\n"); + return -1; + } + if(crng.low != crng.high && crng.rate > 0) { + if(!(crnode = malloc(sizeof *crnode))) { + fprintf(stderr, "failed to allocate color range node\n"); + return -1; + } + crnode->low = crng.low; + crnode->high = crng.high; + crnode->cmode = (crng.flags & CRNG_REVERSE) ? CYCLE_REVERSE : CYCLE_NORMAL; + crnode->rate = crng.rate; + crnode->next = img->range; + img->range = crnode; + ++img->num_ranges; + } + break; + + case IFF_BODY: + if(!img->pixels) { + fprintf(stderr, "malformed ILBM image: encountered BODY chunk before BMHD\n"); + return -1; + } + if(type == IFF_ILBM) { + if(read_body_ilbm(fp, &bmhd, img) == -1) { + fprintf(stderr, "failed to read interleaved pixel data\n"); + return -1; + } + } else { + assert(type == IFF_PBM); + if(read_body_pbm(fp, &bmhd, img) == -1) { + fprintf(stderr, "failed to read linear pixel data\n"); + return -1; + } + } + res = 0; /* sucessfully read image */ + break; + + default: + /* skip unknown chunks */ + fseek(fp, hdr.size, SEEK_CUR); + if(ftell(fp) & 1) { + /* chunks must start at even offsets */ + fseek(fp, 1, SEEK_CUR); + } + } + } + + return res; +} + + +static int read_bmhd(FILE *fp, struct bitmap_header *bmhd) +{ + if(fread(bmhd, sizeof *bmhd, 1, fp) < 1) { + return -1; + } +#ifdef LENDIAN + bmhd->width = swap16(bmhd->width); + bmhd->height = swap16(bmhd->height); + bmhd->xoffs = swap16(bmhd->xoffs); + bmhd->yoffs = swap16(bmhd->yoffs); + bmhd->colorkey = swap16(bmhd->colorkey); + bmhd->pgwidth = swap16(bmhd->pgwidth); + bmhd->pgheight = swap16(bmhd->pgheight); +#endif + return 0; +} + +static int read_crng(FILE *fp, struct crng *crng) +{ + if(fread(crng, sizeof *crng, 1, fp) < 1) { + return -1; + } +#ifdef LENDIAN + crng->rate = swap16(crng->rate); + crng->flags = swap16(crng->flags); +#endif + return 0; +} + +/* scanline: [bp0 row][bp1 row]...[bpN-1 row][opt mask row] + * each uncompressed row is width / 8 bytes + */ +static int read_body_ilbm(FILE *fp, struct bitmap_header *bmhd, struct image *img) +{ + int i, j, k, bitidx; + int rowsz = img->width / 8; + unsigned char *src, *dest = img->pixels; + unsigned char *rowbuf = alloca(rowsz); + + assert(bmhd->width == img->width); + assert(bmhd->height == img->height); + assert(img->pixels); + + for(i=0; iheight; i++) { + + memset(dest, 0, img->width); /* clear the whole scanline to OR bits into place */ + + for(j=0; jnplanes; j++) { + // read a row corresponding to bitplane j + if(bmhd->compression) { + if(read_compressed_scanline(fp, rowbuf, rowsz) == -1) { + return -1; + } + } else { + if(fread(rowbuf, 1, rowsz, fp) < rowsz) { + return -1; + } + } + + // distribute all bits across the linear output scanline + src = rowbuf; + bitidx = 0; + + for(k=0; kwidth; k++) { + dest[k] |= ((*src >> (7 - bitidx)) & 1) << j; + + if(++bitidx >= 8) { + bitidx = 0; + ++src; + } + } + } + + if(bmhd->masking & MASK_PLANE) { + /* skip the mask (1bpp) */ + fseek(fp, rowsz, SEEK_CUR); + } + + dest += img->width; + } + return 0; +} + +static int read_body_pbm(FILE *fp, struct bitmap_header *bmhd, struct image *img) +{ + int i; + int npixels = img->width * img->height; + unsigned char *dptr = img->pixels; + + assert(bmhd->width == img->width); + assert(bmhd->height == img->height); + assert(img->pixels); + + if(bmhd->compression) { + for(i=0; iheight; i++) { + if(read_compressed_scanline(fp, dptr, img->width) == -1) { + return -1; + } + dptr += img->width; + } + + } else { + /* uncompressed */ + if(fread(img->pixels, 1, npixels, fp) < npixels) { + return -1; + } + } + return 0; +} + +static int read_compressed_scanline(FILE *fp, unsigned char *scanline, int width) +{ + int i, count, x = 0; + signed char ctl; + + while(x < width) { + if(fread(&ctl, 1, 1, fp) < 1) return -1; + + if(ctl == -128) continue; + + if(ctl >= 0) { + count = ctl + 1; + if(fread(scanline, 1, count, fp) < count) return -1; + scanline += count; + + } else { + unsigned char pixel; + count = 1 - ctl; + if(fread(&pixel, 1, 1, fp) < 1) return -1; + + for(i=0; i> 8); +} + +static uint32_t swap32(uint32_t x) +{ + return (x << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | (x >> 24); +} diff -r 000000000000 -r bfd8aa2ca393 src/imagelbm.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/imagelbm.h Sat Jul 15 13:26:32 2017 +0300 @@ -0,0 +1,27 @@ +/* +colcycle - color cycling image viewer +Copyright (C) 2016-2017 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef IMAGE_LBM_H_ +#define IMAGE_LBM_H_ + +#include +#include "image.h" + +int file_is_lbm(FILE *fp); +int load_image_lbm(struct image *img, FILE *fp); + +#endif /* IMAGE_LBM_H_ */ diff -r 000000000000 -r bfd8aa2ca393 src/main.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main.c Sat Jul 15 13:26:32 2017 +0300 @@ -0,0 +1,156 @@ +#include +#include +#include +#include +#include "image.h" + +static int proc_image(const char *fname); +static const char *modestr(int cmode); +static void output_planar(FILE *fp, struct image *img); +static void output_chunky(FILE *fp, struct image *img); + +static int planar = 0; + +int main(int argc, char **argv) +{ + int i; + + for(i=1; i ... \n", argv[0]); + printf("Options:\n"); + printf(" -p: output image in planar format (default)\n"); + printf(" -c: output image in chunky format\n"); + printf(" -h: print usage and exit\n"); + return 0; + + default: + fprintf(stderr, "invalid option: %s\n", argv[i]); + return 1; + } + } else { + fprintf(stderr, "invalid option: %s\n", argv[i]); + } + } else { + if(proc_image(argv[i]) == -1) { + return 1; + } + } + } + return 0; +} + +static int proc_image(const char *fname) +{ + int i; + struct image img; + char *outfname, *suffix; + FILE *fp; + + if(load_image(&img, fname) == -1) { + fprintf(stderr, "failed to load image: %s\n", fname); + return -1; + } + + if(!(outfname = malloc(strlen(fname) + 4))) { + perror("failed to allocate output filename"); + return -1; + } + strcpy(outfname, fname); + if(!(suffix = strrchr(outfname, '.'))) { + suffix = outfname + strlen(outfname); + } + strcpy(suffix, ".bin"); + + printf("processing %s -> %s\n", fname, outfname); + + if(!(fp = fopen(outfname, "wb"))) { + fprintf(stderr, "failed to open output file: %s: %s\n", outfname, strerror(errno)); + destroy_image(&img); + free(outfname); + return -1; + } + + printf(" bits per pixel: %d\n", img.bpp); + if(img.num_ranges) { + printf(" color ranges:\n"); + for(i=0; ipixels; + + for(i=0; ibpp; i++) { + for(y=0; yheight; y++) { + for(x=0; xwidth; x++) { + acc |= (((*pptr++ >> i) & 1) << acc_bit); + if(--acc_bit == 0) { + fputc(acc, fp); + acc_bit = 7; + acc = 0; + } + } + } + } +} + +static void output_chunky(FILE *fp, struct image *img) +{ + int i, j; + unsigned char *pptr = img->pixels; + + for(i=0; iheight; i++) { + for(j=0; jwidth; j++) { + fputc(*pptr++, fp); + } + } +} + +static const char *modestr(int cmode) +{ + switch(cmode) { + case CYCLE_NORMAL: + return "forward"; + case CYCLE_REVERSE: + return "reverse"; + case CYCLE_PINGPONG: + return "ping-pong"; + case CYCLE_SINE_HALF: + return "half-sine"; + case CYCLE_SINE: + return "sine"; + default: + break; + } + return "unknown"; +}