From 79934f2608c16a427c82f4c5119452348798639f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98rjan=20Malde?= Date: Sun, 23 Jan 2022 13:02:19 +0100 Subject: [PATCH] initial commit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ørjan Malde --- .gitignore | 2 + Makefile | 15 ++++ ase.c | 200 ++++++++++++++++++++++++++++++++++++++++++++++++++++ color.h | 12 ++++ gimp.c | 12 ++++ gimp.h | 7 ++ lab.c | 83 ++++++++++++++++++++++ readbytes.h | 27 +++++++ 8 files changed, 358 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 ase.c create mode 100644 color.h create mode 100644 gimp.c create mode 100644 gimp.h create mode 100644 lab.c create mode 100644 readbytes.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8b68ae4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.o +*.gpl diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f927c48 --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +CC := gcc +CFLAGS_ANSI = -ansi -pedantic +CFLAGS := -Wall -Wextra -O2 +LDFLAGS := +LIBS := -lm + +all: aseconv + +aseconv: ase.c lab.c gimp.c + $(CC) $(CFLAGS) $(CFLAGS_ANSI) $? -o $@ $(LDFLAGS) $(LIBS) + +clean: aseconv + rm -f $? + +.PHONY: clean diff --git a/ase.c b/ase.c new file mode 100644 index 0000000..5126451 --- /dev/null +++ b/ase.c @@ -0,0 +1,200 @@ +#include +#include +#include +#include +#include +#include + +#include "color.h" +#include "readbytes.h" +#include "gimp.h" + +float bytes_to_float(const unsigned char *bytes); +unsigned int bytes_to_rgb(const unsigned char *bytes); +struct rgb from_cmyk(const unsigned char *bytes); + +int main(int argc, const char *argv[]) +{ + char *p; + int count; + FILE *out, *in; + float f1, f2, f3; + char filename[512]; + unsigned int r, g, b; + struct rgb rgb_colors; + unsigned char buf[1024]; + unsigned short chunk_size; + const char *ase = "ASEF", *rgb = "RGB ", *cmyk = "CMYK", *lab = "LAB ", *gray = "Gray"; + + if(argc != 2) + { + printf("usage: %s \n", argv[0]); + return 1; + } + + strcpy(filename, argv[1]); + p = strrchr(filename, '.'); + + if(!p || p[1] == '\0' || p[2] == '\0' || p[3] == '\0') + { + printf("usage: %s \n", argv[0]); + return 1; + } + + p[1] = 'g'; + p[2] = 'p'; + p[3] = 'l'; + p[4] = '\0'; + + if(!(in = fopen(argv[1], "rb"))) + { + printf("%s: %s\n", argv[0], strerror(errno)); + return 1; + } + + if(!(out = fopen(filename, "wb"))) + { + printf("%s: %s\n", argv[0], strerror(errno)); + return 1; + } + + /* read header, version and chunk count */ + fread(buf, 1, 12, in); + + if(memcmp(ase, buf, 4)) + { + puts("not an ASE file"); + exit(1); + } + + /* TODO: read ASE version, currently at buf 4-7 */ +#if 0 + printf("%x\n", buf[8]); printf("%x\n", buf[9]); printf("%x\n", buf[10]); printf("%x\n", buf[11]); +#endif + + count = read_uint32_be(&buf[8]); + printf("chunk count: %d\n", count); + + /* CHUNK types: */ + /* palette name = 0xC0010000 */ + /* mystery = 0xC0020000 */ + /* swatch = 0x00010000 */ + + gimp_emit_header(out); + + while(count) + { + if(fread(buf, 1, 6, in) != 6) + { + puts("ERROR: truncated chunk"); + break; + } + + /* palette name chunk, no valuable data to be found, skip */ + if(!memcmp(&buf[0], "\xC0\x01", 2)) + { + chunk_size = read_uint16_be(&buf[4]); + fseek(in, chunk_size, SEEK_CUR); + } + /* swatch chunk */ + else if(!memcmp(&buf[0], "\x00\x01", 2)) + { + chunk_size = read_uint16_be(&buf[4]); + fread(buf, 1, chunk_size, in); + + if(!memcmp(rgb, &buf[chunk_size - 18], 4)) + { + r = bytes_to_float(&buf[chunk_size - 14]) * 255; + g = bytes_to_float(&buf[chunk_size - 10]) * 255; + b = bytes_to_float(&buf[chunk_size - 6]) * 255; + } + else if(!memcmp(cmyk, &buf[chunk_size - 22], 4)) + { + rgb_colors = from_cmyk(&buf[chunk_size - 18]); + r = rgb_colors.r; + g = rgb_colors.g; + b = rgb_colors.b; + } + else if(!memcmp(gray, &buf[chunk_size - 10], 4)) + { + r = bytes_to_float(&buf[chunk_size - 6]) * 255; + g = r; + b = r; + } + else if(!memcmp(lab, &buf[chunk_size - 18], 4)) + { + /* approximate conversion */ + f1 = bytes_to_float(&buf[chunk_size - 14]); + f2 = bytes_to_float(&buf[chunk_size - 10]); + f3 = bytes_to_float(&buf[chunk_size - 6]); + rgb_colors = from_lab(f1 * 100, f2, f3); + r = rgb_colors.r; + g = rgb_colors.g; + b = rgb_colors.b; + } + else + { + puts("FIXME: unsupported color sequences"); + count--; + continue; + } + + gimp_emit_line(out, r, g, b); + } + /* I don't even know */ + else if(!memcmp(&buf[0], "\xC0\x02", 2)) + { + /* these mystery chunks appear to only ever be empty */ + /* do nothing... */ + } + else + { + /* end of the line */ + break; + } + count--; + } + + if(count != 0) + printf("BUG: %d chunks remain\n", count); + + return 0; +} + +float bytes_to_float(const unsigned char *bytes) +{ + unsigned int u; + float f; + u = read_uint32_be(bytes); + memcpy(&f, &u, 4); + return f; +} + +struct rgb from_cmyk(const unsigned char *bytes) +{ + struct rgb color; + float f1, f2, f3, f4; + uint32_t u1, u2, u3, u4; + + u1 = read_uint32_be(&bytes[0]); + u2 = read_uint32_be(&bytes[4]); + u3 = read_uint32_be(&bytes[8]); + u4 = read_uint32_be(&bytes[12]); + + memcpy(&f1, &u1, 4); + memcpy(&f2, &u2, 4); + memcpy(&f3, &u3, 4); + memcpy(&f4, &u4, 4); + +#if 0 + color.r = (uint32_t)round(255 * (1 - f1) * (1 - f4)); + color.g = (uint32_t)round(255 * (1 - f2) * (1 - f4)); + color.b = (uint32_t)round(255 * (1 - f3) * (1 - f4)); +#else + color.r = (uint32_t)floor((255 * (1 - f1) * (1 - f4)) + 0.5); + color.g = (uint32_t)floor((255 * (1 - f2) * (1 - f4)) + 0.5); + color.b = (uint32_t)floor((255 * (1 - f3) * (1 - f4)) + 0.5); +#endif + + return color; +} diff --git a/color.h b/color.h new file mode 100644 index 0000000..843231c --- /dev/null +++ b/color.h @@ -0,0 +1,12 @@ +#ifndef COLOR_H +#define COLOR_H + +struct rgb +{ + unsigned int r, g, b; +}; + +struct rgb from_hsv(float hue, float saturation, float value); +struct rgb from_lab(float l, float a, float b); + +#endif diff --git a/gimp.c b/gimp.c new file mode 100644 index 0000000..41b3cb9 --- /dev/null +++ b/gimp.c @@ -0,0 +1,12 @@ +#include + +void gimp_emit_header(FILE *file) +{ + fprintf(file, "GIMP Palette\nName: RGB palette\nColumns: 10\n"); +} + +void gimp_emit_line(FILE *file, unsigned int r, unsigned int g, unsigned int b) +{ + fprintf(file, "%u%c%u%c%u%c%c%02x%02x%02x%c", r, 0x09, g, 0x09, b, 0x09, '#', r, g, b, 0x0a); + printf("%u%c%u%c%u%c%c%02x%02x%02x%c", r, 0x09, g, 0x09, b, 0x09, '#', r, g, b, 0x0a); +} diff --git a/gimp.h b/gimp.h new file mode 100644 index 0000000..a312e15 --- /dev/null +++ b/gimp.h @@ -0,0 +1,7 @@ +#ifndef GIMP_H +#define GIMP_H + +void gimp_emit_header(FILE *file); +void gimp_emit_line(FILE *file, unsigned int r, unsigned int g, unsigned int b); + +#endif diff --git a/lab.c b/lab.c new file mode 100644 index 0000000..72d76ed --- /dev/null +++ b/lab.c @@ -0,0 +1,83 @@ +#include + +#include "color.h" + +struct rgb from_lab(float l, float a, float b) +{ + struct rgb color; + float z, y, x, fr, fg, fb; + + /* L*a*b* -> XYZ */ + + y = (l + 16) / 116; + x = a / 500 + y; + z = y - b / 200; + + if(x * x * x > 0.008856) + x = x * x * x; + else + x = (x - 16 / 116) / 7.787; + + if(y * y * y > 0.008856) + y = y * y * y; + else + y = (y - 16 / 116) / 7.787; + + if(z * z * z > 0.008856) + z = z * z * z; + else + z = (z - 16 / 116) / 7.787; + + x = x * 95.047; + y = y * 100.000; + z = z * 108.883; + + /* XYZ -> RGB */ + + x = x / 100; + y = y / 100; + z = z / 100; + + fr = x * 3.2406 + y * -1.5372 + z * -0.4986; + fg = x * -0.9689 + y * 1.8758 + z * 0.0415; + fb = x * 0.0557 + y * -0.2040 + z * 1.0570; + + if(fr > 0.0031308) + fr = 1.055 * pow(fr, 1 / 2.4) - 0.055; + else + fr = 12.92 * fr; + if(fg > 0.0031308) + fg = 1.055 * pow(fg, 1 / 2.4) - 0.055; + else + fg = 12.92 * fg; + if(fb > 0.0031308) + fb = 1.055 * pow(fb, 1 / 2.4) - 0.055; + else + fb = 12.92 * fb; + + if(fr > 0) + if(fr < 1) + color.r = fr * 255; + else + color.r = 255; + else + color.r = 0; + + if(fg > 0) + if(fg < 1) + color.g = fg * 255; + else + color.g = 255; + else + color.g = 0; + + if(fb > 0) + if(fb < 1) + color.b = fb * 255; + else + color.b = 255; + else + color.b = 0; + + return color; +} diff --git a/readbytes.h b/readbytes.h new file mode 100644 index 0000000..5ebabf4 --- /dev/null +++ b/readbytes.h @@ -0,0 +1,27 @@ +#ifndef INCLUDE_READBYTES_H +#define INCLUDE_READBYTES_H + +#include +#include + +uint32_t read_uint32_be(const unsigned char *bytes) +{ + return((bytes[3]) | (bytes[2] << 8) | (bytes[1] << 16) | (bytes[0] << 24)); +} + +uint32_t read_uint32_le(const unsigned char *bytes) +{ + return((bytes[0]) | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24)); +} + +uint16_t read_uint16_be(const unsigned char *bytes) +{ + return((bytes[0] << 8) | (bytes[1])); +} + +uint16_t read_uint16_le(const unsigned char *bytes) +{ + return((bytes[0]) | (bytes[1] << 8)); +} + +#endif