diff options
Diffstat (limited to 'src/blind-from-image.c')
| -rw-r--r-- | src/blind-from-image.c | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/src/blind-from-image.c b/src/blind-from-image.c new file mode 100644 index 0000000..9c2d047 --- /dev/null +++ b/src/blind-from-image.c @@ -0,0 +1,273 @@ +/* See LICENSE file for copyright and license details. */ +#include "util.h" + +#include <arpa/inet.h> +#include <sys/wait.h> +#include <inttypes.h> +#include <string.h> +#include <unistd.h> + +USAGE("[-h] [-f | -p]") + +static char buf[BUFSIZ]; +static char width[3 * sizeof(size_t) + 1] = {0}; +static char height[3 * sizeof(size_t) + 1] = {0}; +static const char *conv_fail_msg = "convertion failed, if converting a farbfeld file, try -f"; +static size_t pixel_size; +static double value_max; +static double (*get_value)(char **bufp); +static void (*convert)(size_t n); +static int with_alpha = 1; +static int with_colour = 1; + +static double +get_value_u8(char** bufp) +{ + uint8_t value = *(uint8_t *)(*bufp); + *bufp += 1; + return value / value_max; +} + +static double +get_value_u16(char** bufp) +{ + uint16_t value = ntohs(*(uint16_t *)(*bufp)); + *bufp += 2; + return value / value_max; +} + +static double +get_value_u32(char** bufp) +{ + uint32_t value = ntohl(*(uint32_t *)(*bufp)); + *bufp += 4; + return value / value_max; +} + +static double +get_value_u64(char** bufp) +{ + uint64_t value; + value = (uint64_t)(buf[0]) << 56; + value |= (uint64_t)(buf[1]) << 48; + value |= (uint64_t)(buf[2]) << 40; + value |= (uint64_t)(buf[3]) << 32; + value |= (uint64_t)(buf[4]) << 24; + value |= (uint64_t)(buf[5]) << 16; + value |= (uint64_t)(buf[6]) << 8; + value |= (uint64_t)(buf[7]); + *bufp += 8; + return value / value_max; +} + +static void +from_srgb(size_t n) +{ + double red, green, blue, pixel[4]; + size_t ptr; + char *p; + for (ptr = 0; ptr + pixel_size <= n; ptr += pixel_size) { + p = buf + ptr; + red = srgb_decode(get_value(&p)); + green = with_colour ? srgb_decode(get_value(&p)) : red; + blue = with_colour ? srgb_decode(get_value(&p)) : red; + pixel[3] = with_alpha ? get_value(&p) : 1; + srgb_to_ciexyz(red, green, blue, pixel + 0, pixel + 1, pixel + 2); + ewriteall(STDOUT_FILENO, pixel, sizeof(pixel), "<stdout>"); + } +} + +static size_t +farbfeld_head(int fd, const char *fname) +{ + if (ereadall(fd, buf, 16, fname) != 16) + eprintf("%s\n", conv_fail_msg); + if (memcmp(buf, "farbfeld", 8)) + eprintf("%s\n", conv_fail_msg); + sprintf(width, "%"PRIu32, ntohl(*(uint32_t *)(buf + 8))); + sprintf(height, "%"PRIu32, ntohl(*(uint32_t *)(buf + 12))); + pixel_size = 4 * sizeof(uint16_t); + value_max = UINT16_MAX; + get_value = get_value_u16; + convert = from_srgb; + return 0; +} + +static size_t +pam_head(int fd, const char *fname) +{ + size_t ptr; + char *p; + unsigned long long int maxval = UINT8_MAX; + for (ptr = 0;;) { + ptr += ereadall(fd, buf + ptr, (size_t)buf - ptr, fname); + for (;;) { + p = memchr(buf, '\n', ptr); + if (!p) { + if (ptr == sizeof(buf)) + eprintf("%s\n", conv_fail_msg); + break; + } + *p++ = '\0'; + if (strstr(buf, "WIDTH ") == buf) { + if (*width || !buf[6] || strlen(buf + 6) >= sizeof(width)) + eprintf("%s\n", conv_fail_msg); + strcpy(width, buf + 6); + } else if (strstr(buf, "HEIGHT ") == buf) { + if (*height || !buf[7] || strlen(buf + 7) >= sizeof(height)) + eprintf("%s\n", conv_fail_msg); + strcpy(height, buf + 7); + } else if (strstr(buf, "MAXVAL ") == buf) { + if (tollu(buf + 7, 0, UINT64_MAX, &maxval)) { + if (errno != ERANGE) + eprintf("%s\n", conv_fail_msg); + eprintf("image uses greater colour resolution than supported\n"); + } else if (!maxval) { + eprintf("%s\n", conv_fail_msg); + } + } else if (strstr(buf, "TUPLTYPE ") == buf) { + if (!strcmp(buf, "TUPLTYPE BLACKANDWHITE")) + maxval = 1, with_colour = 0, with_alpha = 0; + else if (!strcmp(buf, "TUPLTYPE BLACKANDWHITE_ALPHA")) + maxval = 1, with_colour = 0, with_alpha = 1; + else if (!strcmp(buf, "TUPLTYPE GRAYSCALE")) + with_colour = 0, with_alpha = 0; + else if (!strcmp(buf, "TUPLTYPE GRAYSCALE_ALPHA")) + with_colour = 0, with_alpha = 1; + else if (!strcmp(buf, "TUPLTYPE RGB")) + with_colour = 1, with_alpha = 0; + else if (!strcmp(buf, "TUPLTYPE RGB_ALPHA")) + with_colour = 1, with_alpha = 1; + else + eprintf("image uses an unsupported tuple type: %s\n", buf + sizeof("TUPLTYPE")); + } else if (!strcmp(buf, "ENDHDR")) { + memmove(buf, p, ptr -= (size_t)(p - buf)); + goto header_done; + } + memmove(buf, p, ptr -= (size_t)(p - buf)); + } + } +header_done: + if (maxval < (size_t)UINT8_MAX) { + pixel_size = sizeof(uint8_t); + get_value = get_value_u8; + } else if (maxval < (size_t)UINT16_MAX) { + pixel_size = sizeof(uint16_t); + get_value = get_value_u16; + } else if (maxval < (size_t)UINT32_MAX) { + pixel_size = sizeof(uint32_t); + get_value = get_value_u32; + } else { + pixel_size = sizeof(uint64_t); + get_value = get_value_u64; + } + value_max = maxval; + pixel_size *= (with_colour ? 3 : 1) + with_alpha; + convert = from_srgb; + return ptr; +} + +int +main(int argc, char *argv[]) +{ + int status, pipe_rw[2], i, old_fd, forked = 0; + int headless = 0, farbfeld = 0, pam = 0; + pid_t pid = 0; + size_t off, n; + ssize_t r; + const char *file = "<subprocess>"; + const char *conv_fail_msg = "convertion failed, if converting a farbfeld file, try -f"; + + ARGBEGIN { + case 'f': + farbfeld = 1; + break; + case 'h': + headless = 1; + break; + case 'p': + pam = 1; + break; + default: + usage(); + } ARGEND; + + if (argc || (farbfeld && pam)) + usage(); + + if (farbfeld) + conv_fail_msg = "not a valid farbfeld file, try without -f"; + else if (pam) + conv_fail_msg = "not a valid RGBA portable arbitrary map file, try without -p"; + else + forked = 1; + + if (forked) { + file = "<stdin>"; + pipe_rw[0] = STDIN_FILENO; + goto after_fork; + } + + if (pipe(pipe_rw)) + eprintf("pipe:"); + if (pipe_rw[0] == STDIN_FILENO || pipe_rw[1] == STDIN_FILENO) + eprintf("no stdin open\n"); + if (pipe_rw[0] == STDOUT_FILENO || pipe_rw[1] == STDOUT_FILENO) + eprintf("no stdout open\n"); + for (i = 0; i < 2; i++) { + if (pipe_rw[i] == STDERR_FILENO) { + pipe_rw[i] = dup(old_fd = pipe_rw[i]); + if (pipe_rw[i] < 0) + eprintf("dup:"); + close(old_fd); + } + } + + pid = fork(); + if (pid < 0) + eprintf("fork:"); + + if (!pid) { + close(pipe_rw[0]); + if (dup2(pipe_rw[1], STDOUT_FILENO) == -1) + eprintf("dup2:"); + close(pipe_rw[1]); + /* XXX Is there a way to convert directly to raw XYZ? (Would avoid gamut truncation) */ + execlp("convert", "convert", "-", "-depth", "32", "-alpha", "activate", "pam:-", NULL); + eprintf("exec convert:"); + } + + close(pipe_rw[1]); +after_fork: + + if (farbfeld) + n = farbfeld_head(pipe_rw[0], file); + else + n = pam_head(pipe_rw[0], file); + + if (!*width || !*height) + eprintf("%s\n", conv_fail_msg); + + if (!headless) { + printf("1 %s %s xyza\n%cuivf", width, height, 0); + efflush(stdout, "<stdout>"); + } + + for (;;) { + convert(n); + off = n - (n % pixel_size); + memmove(buf, buf + off, n -= off); + r = read(pipe_rw[0], buf + n, sizeof(buf) - n); + if (r < 0) + eprintf("read %s:", file); + if (r == 0) + break; + n += (size_t)r; + } + + if (!forked) + return 0; + close(pipe_rw[0]); + while (waitpid(pid, &status, 0) != pid); + return !!status; +} |
