From 4d7c506a29a5153a2b6ab903d2f4731bbab6abcb Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Thu, 7 Dec 2017 23:48:45 +0100 Subject: Start on libblind MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/libblind.c | 504 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 504 insertions(+) create mode 100644 src/libblind.c (limited to 'src/libblind.c') diff --git a/src/libblind.c b/src/libblind.c new file mode 100644 index 0000000..436c433 --- /dev/null +++ b/src/libblind.c @@ -0,0 +1,504 @@ +#define LIBBLIND_NO_NAMESPACE +#include "libblind.h" + +#include +#include +#include +#include +#include +#include + + +extern int writeall(int fd, const void *buf, size_t n); + + + +#if defined(POSIX_FADV_SEQUENTIAL) +# define fadvise_sequential(...) posix_fadvise(__VA_ARGS__, POSIX_FADV_SEQUENTIAL) +#else +# define fadvise_sequential(...) +#endif + + +#define MIN(A, B) ((A) < (B) ? (A) : (B)) +#define ELEMSOF(A) (sizeof(A) / sizeof(*(A))) + + +#define X(NUM, NAM, STR) ,STR +static const char *error_names[] = { + "Success", NULL + LIBBLIND_LIST_ERRORS(X) +}; +#undef X + + +const char * +libblind_strerror(int errnum) +{ + const char *ret; + errnum = -errnum; + if (errnum < 0 || errnum >= ELEMSOF(error_names)) + return "Unknown libblind error"; + ret = error_names[errnum]; + return ret ? ret : strerror(errno); +} + + +void +libblind_perror(const char *prefix, int errnum) +{ + fprintf(stderr, "%s%s%s\n", + prefix ? prefix : "", + prefix ? ": " : "", + libblind_strerror(errnum)); +} + + +const char * +libblind_get_pixfmt(const char *specified, const char *current) +{ + enum colour_space space = CIEXYZ; + enum alpha alpha = UNPREMULTIPLIED; + enum encoding encoding = UINT16; + int level = -1; + size_t n = strlen(specified); + + if ((n >= 2 && !strcmp(specified - 2, " f")) || + !strcmp(specified, "raw0") || !strcmp(specified, "raw1") || + !strcmp(specified, "raw2") || !strcmp(specified, "raw2a")) + return specified; + + if (!strcmp(current, "xyza")) space = CIEXYZ, encoding = DOUBLE; + else if (!strcmp(current, "xyza f")) space = CIEXYZ, encoding = FLOAT; + else if (!strcmp(current, "raw0")) level = 0; + else if (!strcmp(current, "raw1")) level = 1; + else if (!strcmp(current, "raw2")) level = 2, alpha = NO_ALPHA; + else if (!strcmp(current, "raw2a")) level = 2; + else if (!strcmp(current, "raw3")) level = 3, encoding = DOUBLE, alpha = NO_ALPHA; + else if (!strcmp(current, "raw3a")) level = 3, encoding = DOUBLE; + else if (!strcmp(current, "raw3 f")) level = 3, encoding = FLOAT, alpha = NO_ALPHA; + else if (!strcmp(current, "raw3a f")) level = 3, encoding = FLOAT; + else if (!strcmp(current, "raw4")) level = 4, encoding = DOUBLE, alpha = NO_ALPHA; + else if (!strcmp(current, "raw4a")) level = 4, encoding = DOUBLE; + else if (!strcmp(current, "raw4 f")) level = 4, encoding = FLOAT, alpha = NO_ALPHA; + else if (!strcmp(current, "raw4a f")) level = 4, encoding = FLOAT; + else if (!strcmp(current, "raw5")) level = 5, encoding = DOUBLE, alpha = NO_ALPHA; + else if (!strcmp(current, "raw5a")) level = 5, encoding = DOUBLE; + else if (!strcmp(current, "raw5 f")) level = 5, encoding = FLOAT, alpha = NO_ALPHA; + else if (!strcmp(current, "raw5a f")) level = 5, encoding = FLOAT; + else + return specified; + + if (!strcmp(specified, "f")) encoding = FLOAT; + else if (!strcmp(specified, "!f")) encoding = DOUBLE; + else if (!strcmp(specified, "xyza")) level = -1, alpha = UNPREMULTIPLIED, space = CIEXYZ; + else if (!strcmp(specified, "raw3")) level = 3, alpha = NO_ALPHA; + else if (!strcmp(specified, "raw3a")) level = 3, alpha = UNPREMULTIPLIED; + else if (!strcmp(specified, "raw4")) level = 4, alpha = NO_ALPHA; + else if (!strcmp(specified, "raw4a")) level = 4, alpha = UNPREMULTIPLIED; + else if (!strcmp(specified, "raw5")) level = 5, alpha = NO_ALPHA; + else if (!strcmp(specified, "raw5a")) level = 5, alpha = UNPREMULTIPLIED; + else if (!strcmp(specified, "xyza !f")) return "xyza"; + else if (!strcmp(specified, "raw3 !f")) return "raw3"; + else if (!strcmp(specified, "raw3a !f")) return "raw3a"; + else if (!strcmp(specified, "raw4 !f")) return "raw4"; + else if (!strcmp(specified, "raw4a !f")) return "raw4a"; + else if (!strcmp(specified, "raw5 !f")) return "raw5"; + else if (!strcmp(specified, "raw5a !f")) return "raw5a"; + else + return specified; + + if (level == 0 && encoding == UINT16) return "raw0"; + else if (level == 1 && encoding == UINT16) return "raw1"; + else if (level == 2 && encoding == UINT16) return alpha ? "raw2a" : "raw2"; + else if (level == 3 && encoding == DOUBLE) return alpha ? "raw3a" : "raw3"; + else if (level == 3 && encoding == FLOAT) return alpha ? "raw3a f" : "raw3 f"; + else if (level == 4 && encoding == DOUBLE) return alpha ? "raw4a" : "raw4"; + else if (level == 4 && encoding == FLOAT) return alpha ? "raw4a f" : "raw4 f"; + else if (level == 5 && encoding == DOUBLE) return alpha ? "raw5a" : "raw5"; + else if (level == 5 && encoding == FLOAT) return alpha ? "raw5a f" : "raw5 f"; + else if (level < 0 && space == CIEXYZ && alpha == UNPREMULTIPLIED) + return encoding == FLOAT ? "xyza f" : encoding == DOUBLE ? "xyza" : specified; + else + return specified; +} + + +int +libblind_set_pixfmt(struct libblind_stream *stream, const char *pixfmt) +{ +#define TEST_ENCODING_AGNOSTIC(FMT) (!strcmp(stream->pixfmt, FMT) || !strcmp(stream->pixfmt, FMT" f")) + + if (pixfmt) { + pixfmt = get_pixfmt(pixfmt, stream->pixfmt[0] ? stream->pixfmt : "xyza"); + if (strlen(pixfmt) >= sizeof(stream->pixfmt)) + return LIBBLIND_EPIXFMTNOSUPPORT; + strcpy(stream->pixfmt, pixfmt); + } + + stream->n_chan = 4; + stream->alpha = UNPREMULTIPLIED; + stream->encoding = DOUBLE; + stream->endian = HOST; + stream->alpha_chan = 3; + stream->luma_chan = -1; + + if (!strcmp(stream->pixfmt, "xyza")) { + stream->space = CIEXYZ; + } else if (!strcmp(stream->pixfmt, "xyza f")) { + stream->space = CIEXYZ; + stream->encoding = FLOAT; + } else if (!strcmp(stream->pixfmt, "raw0")) { + stream->space = YUV_NONLINEAR; + stream->encoding = UINT16; + stream->endian = LITTLE; + stream->alpha_chan = 0; + stream->luma_chan = 1; + } else if (!strcmp(stream->pixfmt, "raw1")) { + stream->space = YUV_NONLINEAR; + stream->encoding = UINT16; + stream->endian = LITTLE; + } else if (!strcmp(stream->pixfmt, "raw2a") || !strcmp(stream->pixfmt, "raw2")) { + stream->space = YUV_NONLINEAR; + stream->alpha = stream->pixfmt[4] == 'a' ? UNPREMULTIPLIED : NO_ALPHA; + stream->encoding = UINT16; + } else if (TEST_ENCODING_AGNOSTIC("raw3") || TEST_ENCODING_AGNOSTIC("raw3a")) { + stream->space = YUV_NONLINEAR; + stream->alpha = stream->pixfmt[4] == 'a' ? UNPREMULTIPLIED : NO_ALPHA; + stream->encoding = strlen(stream->pixfmt) > 5 ? FLOAT : DOUBLE; + } else if (TEST_ENCODING_AGNOSTIC("raw4") || TEST_ENCODING_AGNOSTIC("raw4a")) { + stream->space = SRGB_NONLINEAR; + stream->alpha = stream->pixfmt[4] == 'a' ? UNPREMULTIPLIED : NO_ALPHA; + stream->encoding = strlen(stream->pixfmt) > 5 ? FLOAT : DOUBLE; + } else if (TEST_ENCODING_AGNOSTIC("raw5") || TEST_ENCODING_AGNOSTIC("raw5a")) { + stream->space = SRGB; + stream->alpha = stream->pixfmt[4] == 'a' ? UNPREMULTIPLIED : NO_ALPHA; + stream->encoding = strlen(stream->pixfmt) > 5 ? FLOAT : DOUBLE; + } else { + return LIBBLIND_EPIXFMTNOSUPPORT; + } + + if (stream->alpha == NO_ALPHA) { + stream->n_chan -= 1; + stream->alpha_chan = -1; + } + + if (stream->luma_chan == -1) { + if (stream->space == CIEXYZ) + stream->luma_chan = 1; + else if (stream->space == YUV_NONLINEAR) + stream->luma_chan = 0; + } + + switch (stream->encoding) { + case FLOAT: + stream->chan_size = sizeof(float); + break; + case DOUBLE: + stream->chan_size = sizeof(double); + break; + case LONG_DOUBLE: + stream->chan_size = sizeof(long double); + break; + case UINT8: + stream->chan_size = sizeof(uint8_t); + break; + case UINT16: + stream->chan_size = sizeof(uint16_t); + break; + case UINT32: + stream->chan_size = sizeof(uint32_t); + break; + case UINT64: + stream->chan_size = sizeof(uint64_t); + break; + default: + return LIBBLIND_ECORRUPTSTATE; + } + + stream->pixel_size = stream->n_chan * stream->chan_size; + stream->row_size = stream->pixel_size * stream->width; + stream->col_size = stream->pixel_size * stream->height; + stream->frame_size = stream->pixel_size * stream->height * stream->width; + return 0; + +#undef TEST_ENCODING_AGNOSTIC +} + + +ssize_t +libblind_read_stream(struct libblind_stream *stream, size_t n) +{ + ssize_t r; + n = MIN(n, sizeof(stream->buf) - stream->ptr); + r = read(stream->fd, &stream->buf[stream->ptr], n); + if (r > 0) + stream->ptr += (size_t)r; + return r; + /* LIBBLIND_ESYSTEM == -1 and read(3p) explicitly specifies -1 on error */ +} + + +static inline int +get_dimension(size_t *out, const char *s, int dimerr) +{ + char *end; + int saved_errno = errno; + errno = 0; + *out = strtoul(s, &end, 10); + if (errno == ERANGE && *s != '-') + return dimerr; + errno = saved_errno; + return LIBBLIND_EBADFORMAT; +} + + +int +libblind_init_stream(struct libblind_stream *stream) +{ + char *p = NULL, *w, *h, *f; + size_t n; + ssize_t r; + int err; + + if (stream->fd >= 0) { + fadvise_sequential(stream->fd, 0, 0); + for (stream->ptr = 0; !p; p = memchr(stream->buf, '\n', stream->ptr)) + if ((r = libblind_read_stream(stream, SIZE_MAX)) <= 0) + return r ? r : LIBBLIND_EBADFORMAT; + } else { + p = memchr(stream->buf, '\n', stream->ptr); + } + + *p = '\0'; + if (!(w = strchr(stream->buf, ' ')) || + !(h = strchr(&w[1], ' ')) || + !(f = strchr(&h[1], ' '))) + return LIBBLIND_EBADFORMAT; + *w++ = *h++ = *f++ = '\0'; + + if (strlen(f) >= sizeof(stream->pixfmt)) + return LIBBLIND_EBADFORMAT; + strcpy(stream->pixfmt, f); + if ((err = get_dimension(&stream->frames, stream->buf, LIBBLIND_EVIDTOOLONG)) || + (err = get_dimension(&stream->width, w, LIBBLIND_EVIDTOOWIDE)) || + (err = get_dimension(&stream->height, h, LIBBLIND_EVIDTOOTALL))) + return err; + + if (!stream->width) + return LIBBLIND_EZEROWIDTH; + if (!stream->height) + return LIBBLIND_EZEROHEIGHT; + + n = (size_t)(p - stream->buf) + 1; + memmove(stream->buf, &stream->buf[n], stream->ptr -= n); + while (stream->ptr < 5) + if ((r = libblind_read_stream(stream, SIZE_MAX)) <= 0) + return r ? r : LIBBLIND_EBADFORMAT; + if (stream->buf[0] != '\0' || + stream->buf[1] != 'u' || stream->buf[2] != 'i' || + stream->buf[3] != 'v' || stream->buf[4] != 'f') + return LIBBLIND_EBADFORMAT; + memmove(stream->buf, &stream->buf[5], stream->ptr -= 5); + stream->headlen = n + 5; + + if ((err = libblind_set_pixfmt(stream, NULL))) + return err; + + stream->xptr = 0; + + return 0; +} + + +int +libblind_check_dimensions(const struct stream *stream, enum dimension dimensions) +{ + size_t n; + + if (!stream->pixel_size) + return LIBBLIND_ECORRUPTSTATE; + n = SIZE_MAX / stream->pixel_size; + + if ((dimensions & WIDTH) && stream->width > n) + return LIBBLIND_EFRMTOOWIDE; + if ((dimensions & HEIGHT) && stream->height > n) + return LIBBLIND_EFRMTOOTALL; + if (!stream->width || !stream->height) + return 0; + + if ((dimensions & (WIDTH | HEIGHT)) == (WIDTH | HEIGHT)) + if (stream->width > n / stream->height) + return LIBBLIND_EFRMTOOLARGE; + + if (!(dimensions & LENGTH)) + return 0; + if (dimensions & WIDTH) + n /= stream->width; + if (dimensions & HEIGHT) + n /= stream->height; + + if (stream->frames > n) + return LIBBLIND_EVIDTOOLARGE; + + return 0; +} + + +int +libblind_read_segment(struct stream *stream, void *buf, size_t n) +{ + char *buffer = buf; + ssize_t r; + size_t m; + + if (stream->ptr) { + m = MIN(stream->ptr, n); + memcpy(&buffer[stream->xptr], stream->buf, m); + memmove(stream->buf, &stream->buf[m], stream->ptr -= m); + stream->xptr += m; + } + + for (; stream->xptr < n; stream->xptr += (size_t)r) { + r = read(stream->fd, &buffer[stream->xptr], n - stream->xptr); + if (r < 0) + return LIBBLIND_ESYSTEM; + if (!r) { + if (!stream->xptr) + break; + return LIBBLIND_EINCOMPLETE; + } + } + + if (!stream->xptr) + return 0; + stream->xptr -= n; + return 1; +} + + +ssize_t +libblind_send_frames(struct libblind_stream *stream, int outfd, size_t frames, int *outfailed) +{ + size_t h, w, p, n, ret; + ssize_t r; + + for (ret = 0; ret < frames; ret++) { + for (p = stream->pixel_size; p; p--) { + for (h = stream->height; h; h--) { + for (w = stream->width; w; w -= n) { + if (!stream->ptr && (r = libblind_read_stream(stream, w)) <= 0) { + if (outfailed) + *outfailed = 0; + if (!r) + goto done; + return r; + } + n = MIN(stream->ptr, p); + if (outfd >= 0 && writeall(outfd, stream->buf, n)) { + if (outfailed) + *outfailed = 1; + return LIBBLIND_ESYSTEM; + } + memmove(stream->buf, &stream->buf[n], stream->ptr -= n); + } + } + } + } + + return ret; +done: + if (p != stream->pixel_size) + return (ssize_t)LIBBLIND_EINCOMPLETEFRAME; + /* Note that due to order of the nested for-loops, we don't + * know which unit is incomplete: pixel, row, or frame. */ + return ret; +} + + +ssize_t +libblind_send_rows(struct libblind_stream *stream, int outfd, size_t rows, int *outfailed) +{ + size_t w, p, n, ret; + ssize_t r; + + for (ret = 0; ret < rows; ret++) { + for (p = stream->pixel_size; p; p--) { + for (w = stream->width; w; w -= n) { + if (!stream->ptr && (r = libblind_read_stream(stream, w)) <= 0) { + if (outfailed) + *outfailed = 0; + if (!r) + goto done; + return r; + } + n = MIN(stream->ptr, p); + if (outfd >= 0 && writeall(outfd, stream->buf, n)) { + if (outfailed) + *outfailed = 1; + return LIBBLIND_ESYSTEM; + } + memmove(stream->buf, &stream->buf[n], stream->ptr -= n); + } + } + } + + return ret; +done: + if (p != stream->pixel_size) + return (ssize_t)LIBBLIND_EINCOMPLETEROW; + /* Note that due to order of the nested for-loops, we don't + * know whether a pixel is incomplete or just a row. */ + return ret; +} + + +ssize_t +libblind_send_pixels(struct libblind_stream *stream, int outfd, size_t pixels, int *outfailed) +{ + size_t p, n, ret; + ssize_t r; + + for (ret = 0; ret < pixels; ret++) { + for (p = stream->pixel_size; p; p -= n) { + if (!stream->ptr && (r = libblind_read_stream(stream, p)) <= 0) { + if (outfailed) + *outfailed = 0; + if (!r) + goto done; + return r; + } + n = MIN(stream->ptr, p); + if (outfd >= 0 && writeall(outfd, stream->buf, n)) { + if (outfailed) + *outfailed = 1; + return LIBBLIND_ESYSTEM; + } + memmove(stream->buf, &stream->buf[n], stream->ptr -= n); + } + } + + return ret; +done: + if (p != stream->pixel_size) + return (ssize_t)LIBBLIND_EINCOMPLETEPIX; + return ret; +} + + +int +libblind_send_stream(struct libblind_stream *stream, int outfd, int *outfailed) +{ + ssize_t r; + do { + if (writeall(outfd, stream->buf, stream->ptr)) { + if (outfailed) + *outfailed = 1; + return LIBBLIND_ESYSTEM; + } + stream->ptr = 0; + } while ((r = libblind_read_stream(stream, SIZE_MAX)) > 0); + if (outfailed) + *outfailed = 0; + return -(r < 0); +} -- cgit v1.2.3-70-g09d2