aboutsummaryrefslogtreecommitdiffstats
path: root/src/libblind.c
diff options
context:
space:
mode:
authorMattias Andrée <maandree@kth.se>2017-12-07 23:48:45 +0100
committerMattias Andrée <maandree@kth.se>2017-12-07 23:48:45 +0100
commit4d7c506a29a5153a2b6ab903d2f4731bbab6abcb (patch)
tree67bdf5fa8d517cd9fc137f45567f4e312a0f4fc9 /src/libblind.c
parentblind-peek-head: fix minor compile-time bug (diff)
downloadblind-4d7c506a29a5153a2b6ab903d2f4731bbab6abcb.tar.gz
blind-4d7c506a29a5153a2b6ab903d2f4731bbab6abcb.tar.bz2
blind-4d7c506a29a5153a2b6ab903d2f4731bbab6abcb.tar.xz
Start on libblind
Signed-off-by: Mattias Andrée <maandree@kth.se>
Diffstat (limited to 'src/libblind.c')
-rw-r--r--src/libblind.c504
1 files changed, 504 insertions, 0 deletions
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 <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+
+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);
+}