aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile8
-rw-r--r--apply-glyph.h141
-rw-r--r--common.h14
-rw-r--r--config.mk7
-rw-r--r--libskrift.h111
-rw-r--r--libskrift_apply_glyph.c219
-rw-r--r--libskrift_create_context.c8
-rw-r--r--libskrift_format_settings.c36
-rw-r--r--libskrift_get_grapheme_glyph.c42
-rw-r--r--libskrift_merge_glyphs.c22
-rw-r--r--libskrift_srgb_postprocess.c27
-rw-r--r--libskrift_srgb_preprocess.c27
-rw-r--r--srgb-gamma.h145
13 files changed, 740 insertions, 67 deletions
diff --git a/Makefile b/Makefile
index 07a9aaf..dd440d0 100644
--- a/Makefile
+++ b/Makefile
@@ -4,9 +4,11 @@ CONFIGFILE = config.mk
include $(CONFIGFILE)
OBJ =\
+ libskrift_apply_glyph.o\
libskrift_calculate_dpi.o\
libskrift_close_font.o\
libskrift_create_context.o\
+ libskrift_format_settings.o\
libskrift_free_context.o\
libskrift_get_grapheme_glyph.o\
libskrift_get_cluster_glyph.o\
@@ -18,13 +20,17 @@ OBJ =\
libskrift_open_font_fd.o\
libskrift_open_font_file.o\
libskrift_open_font_mem.o\
- libskrift_points_to_pixels.o
+ libskrift_points_to_pixels.o\
+ libskrift_srgb_postprocess.o\
+ libskrift_srgb_preprocess.o
LIB_HDR =\
libskrift.h
HDR =\
common.h\
+ apply-glyph.h\
+ srgb-gamma.h\
$(LIB_HDR)
all: libskrift.a demo
diff --git a/apply-glyph.h b/apply-glyph.h
new file mode 100644
index 0000000..09cb9aa
--- /dev/null
+++ b/apply-glyph.h
@@ -0,0 +1,141 @@
+/* See LICENSE file for copyright and license details. */
+
+#ifndef NARROW_FLOAT_TYPE
+# define NARROW_FLOAT_TYPE double
+# define NARROW_FLOAT_TYPE_AUTO
+#endif
+#ifndef WIDE_FLOAT_TYPE
+# define WIDE_FLOAT_TYPE NARROW_FLOAT_TYPE
+# define WIDE_FLOAT_TYPE_AUTO
+#endif
+#ifndef TYPE
+# define TYPE NARROW_FLOAT_TYPE
+# define TYPE_AUTO
+# define TYPEMAX 1
+#else
+# define TYPEMAX ((TYPE)~(TYPE)0)
+#endif
+#ifndef RMAP
+# define RMAP(Y) ((NARROW_FLOAT_TYPE)((WIDE_FLOAT_TYPE)(Y) / TYPEMAX))
+# define RMAP_AUTO
+#endif
+#define MAP(Y) (TYPE)((WIDE_FLOAT_TYPE)(Y) * TYPEMAX + (WIDE_FLOAT_TYPE)0.5f)
+#define R (&((TYPE *)&img[i])[settings.rpos])
+#define G (&((TYPE *)&img[i])[settings.gpos])
+#define B (&((TYPE *)&img[i])[settings.bpos])
+#define A (&((TYPE *)&img[i])[settings.apos])
+
+do {
+ NARROW_FLOAT_TYPE opacity = (NARROW_FLOAT_TYPE)colour->opacity / 255;
+ NARROW_FLOAT_TYPE al, alpha = (NARROW_FLOAT_TYPE)colour->alpha / 255;
+ NARROW_FLOAT_TYPE re, red = (NARROW_FLOAT_TYPE)colour->red / 255;
+ NARROW_FLOAT_TYPE gr, green = (NARROW_FLOAT_TYPE)colour->green / 255;
+ NARROW_FLOAT_TYPE bl, blue = (NARROW_FLOAT_TYPE)colour->blue / 255;
+ NARROW_FLOAT_TYPE transparency;
+
+ if (settings.apos < 0) {
+ if (gly_psize > 1) {
+ for (img = img_start, r = startr; r < endr; r++, img += img_linesize, gly += gly_linesize) {
+ for (c = startc, i = 0; c < endc; c += gly_psize, i += psize) {
+ *R = MAP(RMAP(*R) * (1 - opacity * gly[c + ri]) + gly[c + ri] * red);
+ *G = MAP(RMAP(*G) * (1 - opacity * gly[c + gi]) + gly[c + gi] * green);
+ *B = MAP(RMAP(*B) * (1 - opacity * gly[c + bi]) + gly[c + bi] * blue);
+ }
+ }
+ } else {
+ for (img = img_start, r = startr; r < endr; r++, img += img_linesize, gly += gly_linesize) {
+ for (c = startc, i = 0; c < endc; c += gly_psize, i += psize) {
+ transparency = 1 - opacity * gly[c + ri];
+ *R = MAP(RMAP(*R) * transparency + gly[c + ri] * red);
+ *G = MAP(RMAP(*G) * transparency + gly[c + gi] * green);
+ *B = MAP(RMAP(*B) * transparency + gly[c + bi] * blue);
+ }
+ }
+ }
+ } else if (image->premultiplied) {
+ if (gly_psize > 1) {
+ for (img = img_start, r = startr; r < endr; r++, img += img_linesize, gly += gly_linesize) {
+ for (c = startc, i = 0; c < endc; c += gly_psize, i += psize) {
+ high = gly[c + ri] > gly[c + gi] ? gly[c + ri] : gly[c + gi];
+ high = high > gly[c + bi] ? high : gly[c + bi];
+ *R = MAP(RMAP(*R) * (1 - opacity * gly[c + ri]) + gly[c + ri] * red);
+ *G = MAP(RMAP(*G) * (1 - opacity * gly[c + gi]) + gly[c + gi] * green);
+ *B = MAP(RMAP(*B) * (1 - opacity * gly[c + bi]) + gly[c + bi] * blue);
+ *A = MAP(RMAP(*A) * (1 - opacity * high) + high * alpha);
+ }
+ }
+ } else {
+ for (img = img_start, r = startr; r < endr; r++, img += img_linesize, gly += gly_linesize) {
+ for (c = startc, i = 0; c < endc; c += gly_psize, i += psize) {
+ transparency = 1 - opacity * gly[c + ri];
+ *R = MAP(RMAP(*R) * transparency + gly[c + ri] * red);
+ *G = MAP(RMAP(*G) * transparency + gly[c + ri] * green);
+ *B = MAP(RMAP(*B) * transparency + gly[c + ri] * blue);
+ *A = MAP(RMAP(*A) * transparency + gly[c + ri] * alpha);
+ }
+ }
+ }
+ } else {
+ if (gly_psize > 1) {
+ for (img = img_start, r = startr; r < endr; r++, img += img_linesize, gly += gly_linesize) {
+ for (c = startc, i = 0; c < endc; c += gly_psize, i += psize) {
+ high = gly[c + ri] > gly[c + gi] ? gly[c + ri] : gly[c + gi];
+ high = high > gly[c + bi] ? high : gly[c + bi];
+ re = RMAP(*R) * RMAP(*A) * (1 - opacity * gly[c + ri]) + gly[c + ri] * red;
+ gr = RMAP(*G) * RMAP(*A) * (1 - opacity * gly[c + gi]) + gly[c + gi] * green;
+ bl = RMAP(*B) * RMAP(*A) * (1 - opacity * gly[c + bi]) + gly[c + bi] * blue;
+ al = RMAP(*A) * (1 - opacity * high) + high * alpha;
+ if (fpclassify(al) != FP_ZERO) {
+ *R = MAP(re);
+ *G = MAP(gr);
+ *B = MAP(bl);
+ *A = MAP(al);
+ } else {
+ *R = *G = *B = *A = 0;
+ }
+ }
+ }
+ } else {
+ for (img = img_start, r = startr; r < endr; r++, img += img_linesize, gly += gly_linesize) {
+ for (c = startc, i = 0; c < endc; c += gly_psize, i += psize) {
+ transparency = 1 - opacity * gly[c + ri];
+ re = RMAP(*R) * RMAP(*A) * transparency + gly[c + ri] * red;
+ gr = RMAP(*G) * RMAP(*A) * transparency + gly[c + ri] * green;
+ bl = RMAP(*B) * RMAP(*A) * transparency + gly[c + ri] * blue;
+ al = RMAP(*A) * transparency + gly[c + ri] * alpha;
+ if (fpclassify(al) != FP_ZERO) {
+ *R = MAP(re);
+ *G = MAP(gr);
+ *B = MAP(bl);
+ *A = MAP(al);
+ } else {
+ *R = *G = *B = *A = 0;
+ }
+ }
+ }
+ }
+ }
+} while (0);
+
+#ifdef NARROW_FLOAT_TYPE_AUTO
+# undef NARROW_FLOAT_TYPE_AUTO
+# undef NARROW_FLOAT_TYPE
+#endif
+#ifdef WIDE_FLOAT_TYPE_AUTO
+# undef WIDE_FLOAT_TYPE_AUTO
+# undef WIDE_FLOAT_TYPE
+#endif
+#ifdef TYPE_AUTO
+# undef TYPE_AUTO
+# undef TYPE
+#endif
+#undef TYPEMAX
+#ifdef RMAP_AUTO
+# undef RMAP_AUTO
+# undef RMAP
+#endif
+#undef MAP
+#undef R
+#undef G
+#undef B
+#undef A
diff --git a/common.h b/common.h
index 615e9c2..5015efd 100644
--- a/common.h
+++ b/common.h
@@ -2,6 +2,7 @@
#include <sys/mman.h>
#include <sys/stat.h>
+#include <endian.h>
#include <errno.h>
#include <math.h>
#include <stdlib.h>
@@ -14,6 +15,8 @@
#define MIN(A, B) ((A) < (B) ? (A) : (B))
#define MAX(A, B) ((A) > (B) ? (A) : (B))
+#define LEN(ARR) (sizeof(ARR) / sizeof(*(ARR)))
+
struct libskrift_font {
SFT_Font *font;
void *memory_free;
@@ -31,3 +34,14 @@ struct libskrift_context {
size_t nfonts;
LIBSKRIFT_FONT *fonts[];
};
+
+struct format_settings {
+ int float_type;
+ int8_t apos;
+ int8_t rpos;
+ int8_t gpos;
+ int8_t bpos;
+ size_t spsize;
+};
+
+extern const struct format_settings libskrift_format_settings[LIBSKRIFT_RGBA_LONG_DOUBLE + 1];
diff --git a/config.mk b/config.mk
index de63532..20f94ed 100644
--- a/config.mk
+++ b/config.mk
@@ -3,7 +3,12 @@ MANPREFIX = $(PREFIX)/share/man
DEMO_FONT = /usr/share/fonts/liberation/LiberationSans-Regular.ttf
-CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_GNU_SOURCE '-DDEMO_FONT="$(DEMO_FONT)"'
+MERGE_STYLE = MAX
+# MAX: The max value of the glyph (minimum legal result, insignificantly slower than SUM)
+# OR: The bitwise OR of the glyph values (within legal range, fastest)
+# SUM: The saturated sum of the glyph values (maximum legal result)
+
+CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_GNU_SOURCE -D$(MERGE_STYLE)_MERGE '-DDEMO_FONT="$(DEMO_FONT)"'
CFLAGS = -std=c99 -Wall -g
LDFLAGS = -lschrift -lm -lgrapheme
diff --git a/libskrift.h b/libskrift.h
index 8745e09..93adb79 100644
--- a/libskrift.h
+++ b/libskrift.h
@@ -22,6 +22,49 @@ typedef struct libskrift_font LIBSKRIFT_FONT;
typedef uint_least32_t libskrift_codepoint_t;
+enum libskrift_format {
+ LIBSKRIFT_RAW,
+ LIBSKRIFT_R8G8B8,
+ LIBSKRIFT_X8R8G8B8,
+ LIBSKRIFT_A8R8G8B8,
+ LIBSKRIFT_R8G8B8A8,
+ LIBSKRIFT_R16G16B16,
+ LIBSKRIFT_X16R16G16B16,
+ LIBSKRIFT_A16R16G16B16,
+ LIBSKRIFT_R16G16B16A16,
+ LIBSKRIFT_R32G32B32,
+ LIBSKRIFT_X32R32G32B32,
+ LIBSKRIFT_A32R32G32B32,
+ LIBSKRIFT_R32G32B32A32,
+ LIBSKRIFT_R64G64B64,
+ LIBSKRIFT_X64R64G64B64,
+ LIBSKRIFT_A64R64G64B64,
+ LIBSKRIFT_R64G64B64A64,
+ LIBSKRIFT_RGB_FLOAT,
+ LIBSKRIFT_ARGB_FLOAT,
+ LIBSKRIFT_RGBA_FLOAT,
+ LIBSKRIFT_RGB_DOUBLE,
+ LIBSKRIFT_ARGB_DOUBLE,
+ LIBSKRIFT_RGBA_DOUBLE,
+ LIBSKRIFT_RGB_LONG_DOUBLE,
+ LIBSKRIFT_ARGB_LONG_DOUBLE,
+ LIBSKRIFT_RGBA_LONG_DOUBLE
+};
+
+enum libskrift_endian {
+ LIBSKRIFT_HOST_PIXEL,
+ LIBSKRIFT_NETWORK_PIXEL,
+ LIBSKRIFT_REVERSE_NETWORK_PIXEL,
+ LIBSKRIFT_HOST_SUBPIXEL,
+ LIBSKRIFT_NETWORK_SUBPIXEL,
+ LIBSKRIFT_REVERSE_NETWORK_SUBPIXEL
+
+#define LIBSKRIFT_BE_PIXEL LIBSKRIFT_NETWORK_PIXEL
+#define LIBSKRIFT_BE_SUBPIXEL LIBSKRIFT_NETWORK_SUBPIXEL
+#define LIBSKRIFT_LE_PIXEL LIBSKRIFT_REVERSE_NETWORK_PIXEL
+#define LIBSKRIFT_LE_SUBPIXEL LIBSKRIFT_REVERSE_NETWORK_SUBPIXEL
+};
+
enum libskrift_subpixel_order {
LIBSKRIFT_OTHER, /* LIBSKRIFT_NONE */
LIBSKRIFT_RGB,
@@ -43,23 +86,24 @@ enum libskrift_hinting {
LIBSKRIFT_FULL = 100
};
-#define LIBSKRIFT_CORRECT_GAMMA 0x00000001U
-#define LIBSKRIFT_REMOVE_GAMMA 0x00000002U
-#define LIBSKRIFT_Y_INCREASES_UPWARDS 0x00000004U /* SFT_DOWNWARD_Y otherwise */
-#define LIBSKRIFT_FLIP_TEXT 0x00000008U
-#define LIBSKRIFT_MIRROR_TEXT 0x00000010U
-#define LIBSKRIFT_MIRROR_CHARS 0x00000020U
-#define LIBSKRIFT_TRANSPOSE_TEXT 0x00000040U
-#define LIBSKRIFT_TRANSPOSE_CHARS 0x00000080U
-#define LIBSKRIFT_NO_LIGATURES 0x00000100U
-#define LIBSKRIFT_ADVANCE_TO_GRID 0x00000200U
-#define LIBSKRIFT_REGRESS_TO_GRID 0x00000400U /* Combine with LIBSKRIFT_ADVANCE_TO_GRID for closest alternative */
-#define LIBSKRIFT_USE_SUBPIXEL_GRID 0x00000800U
-#define LIBSKRIFT_VERTICAL_TEXT 0x00001000U
-#define LIBSKRIFT_AUTOHINTING 0x00002000U /* Use autohinter even if hint information exists */
-#define LIBSKRIFT_NO_AUTOHINTING 0x00004000U /* Use autohinter if no hint information exist */
-#define LIBSKRIFT_AUTOKERNING 0x00008000U /* Use autokerner even if kerning information exists */
-#define LIBSKRIFT_NO_AUTOKERNING 0x00010000U /* Use autokerner if no kerning information exist */
+#define LIBSKRIFT_REMOVE_GAMMA 0x00000001L
+#define LIBSKRIFT_Y_INCREASES_UPWARDS 0x00000002L /* SFT_DOWNWARD_Y otherwise */
+#define LIBSKRIFT_FLIP_TEXT 0x00000004L
+#define LIBSKRIFT_MIRROR_TEXT 0x00000008L
+#define LIBSKRIFT_MIRROR_CHARS 0x00000010L
+#define LIBSKRIFT_TRANSPOSE_TEXT 0x00000020L
+#define LIBSKRIFT_TRANSPOSE_CHARS 0x00000040L
+#define LIBSKRIFT_NO_LIGATURES 0x00000080L
+#define LIBSKRIFT_ADVANCE_CHAR_TO_GRID 0x00000100L
+#define LIBSKRIFT_REGRESS_CHAR_TO_GRID 0x00000200L /* Combine with LIBSKRIFT_ADVANCE_CHAR_TO_GRID for closest alternative */
+#define LIBSKRIFT_ADVANCE_WORD_TO_GRID 0x00000400L
+#define LIBSKRIFT_REGRESS_WORD_TO_GRID 0x00000800L /* Combine with LIBSKRIFT_ADVANCE_WORD_TO_GRID for closest alternative */
+#define LIBSKRIFT_USE_SUBPIXEL_GRID 0x00001000L
+#define LIBSKRIFT_VERTICAL_TEXT 0x00002000L
+#define LIBSKRIFT_AUTOHINTING 0x00004000L /* Use autohinter even if hint information exists */
+#define LIBSKRIFT_NO_AUTOHINTING 0x00008000L /* Use autohinter if no hint information exist */
+#define LIBSKRIFT_AUTOKERNING 0x00010000L /* Use autokerner even if kerning information exists */
+#define LIBSKRIFT_NO_AUTOKERNING 0x00020000L /* Use autokerner if no kerning information exist */
struct libskrift_rendering {
int struct_version;
@@ -97,6 +141,25 @@ struct libskrift_saved_grapheme {
size_t len;
};
+struct libskrift_image {
+ enum libskrift_format format;
+ enum libskrift_endian endian;
+ int premultiplied;
+ uint16_t width;
+ uint16_t height;
+ void (*preprocess)(struct libskrift_image *image, size_t x, size_t y, size_t width, size_t height);
+ void (*postprocess)(struct libskrift_image *image, size_t x, size_t y, size_t width, size_t height);
+ void *image;
+};
+
+struct libskrift_colour {
+ double opacity; /* [0, 1] */
+ double alpha; /* [0, .opacity] */
+ double red; /* [0, .alpha] */
+ double green; /* [0, .alpha] */
+ double blue; /* [0, .alpha] */
+};
+
#define LIBSKRIFT_DEFAULT_RENDERING {\
.struct_version = LIBSKRIFT_RENDERING_STRUCT_VERSION,\
@@ -183,6 +246,18 @@ ssize_t libskrift_get_cluster_glyph(LIBSKRIFT_CONTEXT *, const char *, struct li
double, double, struct libskrift_glyph **);
_LIBSKRIFT_GCC_ONLY(__attribute__((__nonnull__)))
-int libskrift_merge_glyphs(LIBSKRIFT_CONTEXT *, struct libskrift_glyph *, struct libskrift_glyph *, struct libskrift_glyph **);
+int libskrift_merge_glyphs(LIBSKRIFT_CONTEXT *, const struct libskrift_glyph *, const struct libskrift_glyph *,
+ struct libskrift_glyph **);
+
+_LIBSKRIFT_GCC_ONLY(__attribute__((__nonnull__(1, 2, 6))))
+int libskrift_apply_glyph(LIBSKRIFT_CONTEXT *, const struct libskrift_glyph *, const struct libskrift_colour *,
+ int16_t, int16_t, struct libskrift_image *);
+
+
+_LIBSKRIFT_GCC_ONLY(__attribute__((__nonnull__)))
+void libskrift_srgb_preprocess(struct libskrift_image *, size_t, size_t, size_t, size_t);
+
+_LIBSKRIFT_GCC_ONLY(__attribute__((__nonnull__)))
+void libskrift_srgb_postprocess(struct libskrift_image *, size_t, size_t, size_t, size_t);
#endif
diff --git a/libskrift_apply_glyph.c b/libskrift_apply_glyph.c
new file mode 100644
index 0000000..05770ce
--- /dev/null
+++ b/libskrift_apply_glyph.c
@@ -0,0 +1,219 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+static const float uint8_rmap[] = {
+ 0/255.f, 1/255.f, 2/255.f, 3/255.f, 4/255.f, 5/255.f, 6/255.f, 7/255.f,
+ 8/255.f, 9/255.f, 10/255.f, 11/255.f, 12/255.f, 13/255.f, 14/255.f, 15/255.f,
+ 16/255.f, 17/255.f, 18/255.f, 19/255.f, 20/255.f, 21/255.f, 22/255.f, 23/255.f,
+ 24/255.f, 25/255.f, 26/255.f, 27/255.f, 28/255.f, 29/255.f, 30/255.f, 31/255.f,
+ 32/255.f, 33/255.f, 34/255.f, 35/255.f, 36/255.f, 37/255.f, 38/255.f, 39/255.f,
+ 40/255.f, 41/255.f, 42/255.f, 43/255.f, 44/255.f, 45/255.f, 46/255.f, 47/255.f,
+ 48/255.f, 49/255.f, 50/255.f, 51/255.f, 52/255.f, 53/255.f, 54/255.f, 55/255.f,
+ 56/255.f, 57/255.f, 58/255.f, 59/255.f, 60/255.f, 61/255.f, 62/255.f, 63/255.f,
+ 64/255.f, 65/255.f, 66/255.f, 67/255.f, 68/255.f, 69/255.f, 70/255.f, 71/255.f,
+ 72/255.f, 73/255.f, 74/255.f, 75/255.f, 76/255.f, 77/255.f, 78/255.f, 79/255.f,
+ 80/255.f, 81/255.f, 82/255.f, 83/255.f, 84/255.f, 85/255.f, 86/255.f, 87/255.f,
+ 88/255.f, 89/255.f, 90/255.f, 91/255.f, 92/255.f, 93/255.f, 94/255.f, 95/255.f,
+ 96/255.f, 97/255.f, 98/255.f, 99/255.f, 100/255.f, 101/255.f, 102/255.f, 103/255.f,
+ 104/255.f, 105/255.f, 106/255.f, 107/255.f, 108/255.f, 109/255.f, 110/255.f, 111/255.f,
+ 112/255.f, 113/255.f, 114/255.f, 115/255.f, 116/255.f, 117/255.f, 118/255.f, 119/255.f,
+ 120/255.f, 121/255.f, 122/255.f, 123/255.f, 124/255.f, 125/255.f, 126/255.f, 127/255.f,
+ 128/255.f, 129/255.f, 130/255.f, 131/255.f, 132/255.f, 133/255.f, 134/255.f, 135/255.f,
+ 136/255.f, 137/255.f, 138/255.f, 139/255.f, 140/255.f, 141/255.f, 142/255.f, 143/255.f,
+ 144/255.f, 145/255.f, 146/255.f, 147/255.f, 148/255.f, 149/255.f, 150/255.f, 151/255.f,
+ 152/255.f, 153/255.f, 154/255.f, 155/255.f, 156/255.f, 157/255.f, 158/255.f, 159/255.f,
+ 160/255.f, 161/255.f, 162/255.f, 163/255.f, 164/255.f, 165/255.f, 166/255.f, 167/255.f,
+ 168/255.f, 169/255.f, 170/255.f, 171/255.f, 172/255.f, 173/255.f, 174/255.f, 175/255.f,
+ 176/255.f, 177/255.f, 178/255.f, 179/255.f, 180/255.f, 181/255.f, 182/255.f, 183/255.f,
+ 184/255.f, 185/255.f, 186/255.f, 187/255.f, 188/255.f, 189/255.f, 190/255.f, 191/255.f,
+ 192/255.f, 193/255.f, 194/255.f, 195/255.f, 196/255.f, 197/255.f, 198/255.f, 199/255.f,
+ 200/255.f, 201/255.f, 202/255.f, 203/255.f, 204/255.f, 205/255.f, 206/255.f, 207/255.f,
+ 208/255.f, 209/255.f, 210/255.f, 211/255.f, 212/255.f, 213/255.f, 214/255.f, 215/255.f,
+ 216/255.f, 217/255.f, 218/255.f, 219/255.f, 220/255.f, 221/255.f, 222/255.f, 223/255.f,
+ 224/255.f, 225/255.f, 226/255.f, 227/255.f, 228/255.f, 229/255.f, 230/255.f, 231/255.f,
+ 232/255.f, 233/255.f, 234/255.f, 235/255.f, 236/255.f, 237/255.f, 238/255.f, 239/255.f,
+ 240/255.f, 241/255.f, 242/255.f, 243/255.f, 244/255.f, 245/255.f, 246/255.f, 247/255.f,
+ 248/255.f, 249/255.f, 250/255.f, 251/255.f, 252/255.f, 253/255.f, 254/255.f, 255/255.f
+};
+
+int
+libskrift_apply_glyph(LIBSKRIFT_CONTEXT *ctx, const struct libskrift_glyph *glyph, const struct libskrift_colour *colour,
+ int16_t x, int16_t y, struct libskrift_image *image)
+{
+ struct format_settings settings;
+ size_t psize, startr, startc, endr, endc, r, c, n, j;
+ size_t img_linesize, gly_linesize, i, gly_psize, usize;
+ size_t ri, gi, bi, rj, gj, bj;
+ uint16_t x1, y1, x2, y2;
+ int16_t sx2, sy2;
+ uint8_t high;
+ uint8_t *img, *img_start = image->image;
+ const uint8_t *gly = glyph->image;
+ const uint16_t u16 = 0x0102;
+ const uint32_t u32 = 0x01020304L;
+ const uint64_t u64 = 0x0102030405060708LL;
+
+ if (image->format == LIBSKRIFT_RAW || (unsigned int)image->format > (unsigned int)LEN(libskrift_format_settings)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ settings = libskrift_format_settings[image->format];
+ psize = settings.spsize * (size_t)(4 - (settings.apos <= -2));
+
+ if ((unsigned int)image->endian > LIBSKRIFT_REVERSE_NETWORK_SUBPIXEL ||
+ (settings.float_type && (unsigned int)image->endian < LIBSKRIFT_HOST_SUBPIXEL) ||
+ (!colour && settings.float_type) ||
+ (image->endian % 3 && psize != 1 && psize != 2 && psize != 4 && psize != 8)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Drawing area on image */
+ sx2 = (int16_t)(x + (int16_t)glyph->width);
+ sy2 = (int16_t)(y + (int16_t)glyph->height);
+ x1 = x < 0 ? 0 : (uint16_t)x;
+ y1 = y < 0 ? 0 : (uint16_t)y;
+ if (sx2 <= 0 || sy2 <= 0 || x1 >= image->width || y1 >= image->height)
+ return 0;
+ x2 = (uint16_t)sx2 < image->width ? (uint16_t)sx2 : image->width;
+ y2 = (uint16_t)sy2 < image->height ? (uint16_t)sy2 : image->height;
+ sx2 = (int16_t)x2;
+ sy2 = (int16_t)y2;
+
+ /* Drawing area on glyph */
+ startc = x <= 0 ? 0 : (size_t)-x;
+ startr = y <= 0 ? 0 : (size_t)-y;
+ endc = x2 < image->width ? glyph->width : (uint16_t)(sx2 - x);
+ endr = y2 < image->height ? glyph->height : (uint16_t)(sy2 - y);
+
+ img_linesize = (size_t)image->width * psize;
+ img_start += (size_t)y1 * img_linesize;
+ img_start += (size_t)x1 * psize;
+
+ gly_psize = ctx->rendering.smoothing ? 3 : 1;
+ gly_linesize = (size_t)image->width * gly_psize;
+ gly += startr * gly_linesize;
+
+ usize = image->endian >= LIBSKRIFT_HOST_SUBPIXEL ? settings.spsize : psize;
+
+#define CHECK_ENDIAN(UPPER, LOWER, BITS)\
+ ((image->endian == LIBSKRIFT_##UPPER##_PIXEL || image->endian == LIBSKRIFT_##UPPER##_SUBPIXEL) &&\
+ usize == sizeof(uint##BITS##_t) &&\
+ LOWER##BITS##toh(u##BITS) != u##BITS)
+
+#define EACH_UNIT\
+ img = img_start, r = startr; r < endr; r++, img += img_linesize)\
+ for (n = (endc - startc) * psize, i = 0; i < n; i += usize
+
+ /* Convert endian in image to host endian */
+ if (CHECK_ENDIAN(BE, be, 16)) for (EACH_UNIT) *(uint16_t *)&img[i] = be16toh(*(uint16_t *)&img[i]);
+ else if (CHECK_ENDIAN(BE, be, 32)) for (EACH_UNIT) *(uint32_t *)&img[i] = be32toh(*(uint32_t *)&img[i]);
+ else if (CHECK_ENDIAN(BE, be, 64)) for (EACH_UNIT) *(uint64_t *)&img[i] = be64toh(*(uint64_t *)&img[i]);
+ else if (CHECK_ENDIAN(LE, le, 16)) for (EACH_UNIT) *(uint16_t *)&img[i] = le16toh(*(uint16_t *)&img[i]);
+ else if (CHECK_ENDIAN(LE, le, 32)) for (EACH_UNIT) *(uint32_t *)&img[i] = le32toh(*(uint32_t *)&img[i]);
+ else if (CHECK_ENDIAN(LE, le, 64)) for (EACH_UNIT) *(uint64_t *)&img[i] = le64toh(*(uint64_t *)&img[i]);
+
+ /* Preprocess (e.g. remove gamma or colour model conversion) */
+ if (image->preprocess)
+ image->preprocess(image, (size_t)x1, (size_t)y1, (size_t)(x2 - x1), (size_t)(y2 - y1));
+
+ /* Apply glyph */
+ if (ctx->rendering.smoothing != LIBSKRIFT_SUBPIXEL)
+ ri = 0, gi = 0, bi = 0;
+ else if (ctx->subpixel_bgr)
+ bi = 0, gi = 1, ri = 2;
+ else
+ ri = 0, gi = 1, bi = 2;
+ startc *= gly_psize;
+ endc *= gly_psize;
+ if (colour) {
+ switch (settings.float_type) {
+ case 0:
+ switch (settings.spsize) {
+ case 1:
+#define TYPE uint8_t
+#define NARROW_FLOAT_TYPE float
+#define RMAP(Y) uint8_rmap[Y]
+#include "apply-glyph.h"
+#undef NARROW_FLOAT_TYPE
+#undef RMAP
+#undef TYPE
+ break;
+ case 2:
+#define TYPE uint16_t
+#include "apply-glyph.h"
+#undef TYPE
+ break;
+ case 4:
+#define TYPE uint32_t
+#include "apply-glyph.h"
+#undef TYPE
+ break;
+ default:
+#define TYPE uint64_t
+#define WIDE_FLOAT_TYPE long double
+#include "apply-glyph.h"
+#undef TYPE
+#undef WIDE_FLOAT_TYPE
+ break;
+ }
+ break;
+ case 1:
+#define NARROW_FLOAT_TYPE float
+#include "apply-glyph.h"
+#undef NARROW_FLOAT_TYPE
+ break;
+ case 2:
+#define NARROW_FLOAT_TYPE double
+#include "apply-glyph.h"
+#undef NARROW_FLOAT_TYPE
+ break;
+ default:
+#define NARROW_FLOAT_TYPE long double
+#include "apply-glyph.h"
+#undef NARROW_FLOAT_TYPE
+ break;
+ }
+ } else {
+ /* Optimised version for opaque black-on-white/white-on-black, assumes no glyph overlap */
+ rj = (size_t)settings.rpos * settings.spsize;
+ gj = (size_t)settings.gpos * settings.spsize;
+ bj = (size_t)settings.bpos * settings.spsize;
+ for (img = img_start, r = startr; r < endr; r++, img += img_linesize, gly += gly_linesize) {
+ for (c = startc, i = 0; c < endc; c += gly_psize, i += psize) {
+ img[i + rj] ^= gly[c + ri];
+ img[i + gj] ^= gly[c + gi];
+ img[i + bj] ^= gly[c + bi];
+ }
+ }
+ for (j = 1; j < settings.spsize; j++) {
+ ri = rj + j;
+ gi = gj + j;
+ bi = bj + j;
+ for (img = img_start, r = startr; r < endr; r++, img += img_linesize, gly += gly_linesize) {
+ for (c = startc, i = 0; c < endc; c += gly_psize, i += psize) {
+ img[i + ri] = img[i + rj];
+ img[i + gi] = img[i + gj];
+ img[i + bi] = img[i + bj];
+ }
+ }
+ }
+ }
+ startc /= gly_psize;
+ endc /= gly_psize;
+
+ /* Postprocess (e.g. add gamma or colour model conversion) */
+ if (image->postprocess)
+ image->postprocess(image, (size_t)x1, (size_t)y1, (size_t)(x2 - x1), (size_t)(y2 - y1));
+
+ /* Convert endian in image from host endian */
+ if (CHECK_ENDIAN(BE, be, 16)) for (EACH_UNIT) *(uint16_t *)&img[i] = htobe16(*(uint16_t *)&img[i]);
+ else if (CHECK_ENDIAN(BE, be, 32)) for (EACH_UNIT) *(uint32_t *)&img[i] = htobe32(*(uint32_t *)&img[i]);
+ else if (CHECK_ENDIAN(BE, be, 64)) for (EACH_UNIT) *(uint64_t *)&img[i] = htobe64(*(uint64_t *)&img[i]);
+ else if (CHECK_ENDIAN(LE, le, 16)) for (EACH_UNIT) *(uint16_t *)&img[i] = htole16(*(uint16_t *)&img[i]);
+ else if (CHECK_ENDIAN(LE, le, 32)) for (EACH_UNIT) *(uint32_t *)&img[i] = htole32(*(uint32_t *)&img[i]);
+ else if (CHECK_ENDIAN(LE, le, 64)) for (EACH_UNIT) *(uint64_t *)&img[i] = htole64(*(uint64_t *)&img[i]);
+
+ return 0;
+}
diff --git a/libskrift_create_context.c b/libskrift_create_context.c
index 2528221..fd8d086 100644
--- a/libskrift_create_context.c
+++ b/libskrift_create_context.c
@@ -5,12 +5,9 @@
LIBSKRIFT_NO_AUTOHINTING |\
LIBSKRIFT_NO_AUTOKERNING)
-#define IMPLEMENTED_FLAGS (LIBSKRIFT_CORRECT_GAMMA |\
- LIBSKRIFT_REMOVE_GAMMA |\
+#define IMPLEMENTED_FLAGS (LIBSKRIFT_REMOVE_GAMMA |\
FORCED_FLAGS) /* libschrift does not add gamma, so not handling is required */
-#define HAVE_MULTIPLE_FLAGS(HAVE, CHECK) (((HAVE) & (CHECK)) & (((HAVE) & (CHECK)) - 1))
-
#define COPY_ARRAY(DEST_STRUCT, SRC_STRUCT, FIELD)\
memcpy((DEST_STRUCT).FIELD, (SRC_STRUCT).FIELD, sizeof((SRC_STRUCT).FIELD))
@@ -30,8 +27,7 @@ libskrift_create_context(LIBSKRIFT_CONTEXT **ctxp, LIBSKRIFT_FONT **fonts, size_
}
if (rendering) {
- if (HAVE_MULTIPLE_FLAGS(rendering->flags, LIBSKRIFT_CORRECT_GAMMA | LIBSKRIFT_REMOVE_GAMMA) ||
- !rendering->grid_fineness) {
+ if (!rendering->grid_fineness) {
errno = EINVAL;
return -1;
}
diff --git a/libskrift_format_settings.c b/libskrift_format_settings.c
new file mode 100644
index 0000000..aaa8255
--- /dev/null
+++ b/libskrift_format_settings.c
@@ -0,0 +1,36 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+#define RGBA 3, 0, 1, 2
+#define RGB -2, 0, 1, 2
+#define XRGB -1, 1, 2, 3
+#define ARGB 0, 1, 2, 3
+
+const struct format_settings libskrift_format_settings[] = {
+ [LIBSKRIFT_R8G8B8] = {0, RGB, sizeof(uint8_t)},
+ [LIBSKRIFT_X8R8G8B8] = {0, XRGB, sizeof(uint8_t)},
+ [LIBSKRIFT_A8R8G8B8] = {0, ARGB, sizeof(uint8_t)},
+ [LIBSKRIFT_R8G8B8A8] = {0, RGBA, sizeof(uint8_t)},
+ [LIBSKRIFT_R16G16B16] = {0, RGB, sizeof(uint16_t)},
+ [LIBSKRIFT_X16R16G16B16] = {0, XRGB, sizeof(uint16_t)},
+ [LIBSKRIFT_A16R16G16B16] = {0, ARGB, sizeof(uint16_t)},
+ [LIBSKRIFT_R16G16B16A16] = {0, RGBA, sizeof(uint16_t)},
+ [LIBSKRIFT_R32G32B32] = {0, RGB, sizeof(uint32_t)},
+ [LIBSKRIFT_X32R32G32B32] = {0, XRGB, sizeof(uint32_t)},
+ [LIBSKRIFT_A32R32G32B32] = {0, ARGB, sizeof(uint32_t)},
+ [LIBSKRIFT_R32G32B32A32] = {0, RGBA, sizeof(uint32_t)},
+ [LIBSKRIFT_R64G64B64] = {0, RGB, sizeof(uint64_t)},
+ [LIBSKRIFT_X64R64G64B64] = {0, XRGB, sizeof(uint64_t)},
+ [LIBSKRIFT_A64R64G64B64] = {0, ARGB, sizeof(uint64_t)},
+ [LIBSKRIFT_R64G64B64A64] = {0, RGBA, sizeof(uint64_t)},
+ [LIBSKRIFT_RGB_FLOAT] = {1, RGB, sizeof(float)},
+ [LIBSKRIFT_ARGB_FLOAT] = {1, ARGB, sizeof(float)},
+ [LIBSKRIFT_RGBA_FLOAT] = {1, RGBA, sizeof(float)},
+ [LIBSKRIFT_RGB_DOUBLE] = {2, RGB, sizeof(double)},
+ [LIBSKRIFT_ARGB_DOUBLE] = {2, ARGB, sizeof(double)},
+ [LIBSKRIFT_RGBA_DOUBLE] = {2, RGBA, sizeof(double)},
+ [LIBSKRIFT_RGB_LONG_DOUBLE] = {3, RGB, sizeof(long double)},
+ [LIBSKRIFT_ARGB_LONG_DOUBLE] = {3, ARGB, sizeof(long double)},
+ [LIBSKRIFT_RGBA_LONG_DOUBLE] = {3, RGBA, sizeof(long double)}
+ /* REMEMBER that element count is specified in common.h */
+};
diff --git a/libskrift_get_grapheme_glyph.c b/libskrift_get_grapheme_glyph.c
index cdaee2d..14f9a1b 100644
--- a/libskrift_get_grapheme_glyph.c
+++ b/libskrift_get_grapheme_glyph.c
@@ -1,34 +1,15 @@
/* See LICENSE file for copyright and license details. */
#include "common.h"
-static const uint8_t gamma_map[] = {
- 0, 13, 22, 28, 34, 38, 42, 46, 50, 53, 56, 59, 61, 64, 66, 69,
- 71, 73, 75, 77, 79, 81, 83, 85, 86, 88, 90, 92, 93, 95, 96, 98,
- 99, 101, 102, 104, 105, 106, 108, 109, 110, 112, 113, 114, 115, 117, 118, 119,
- 120, 121, 122, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136,
- 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 148, 149, 150, 151,
- 152, 153, 154, 155, 155, 156, 157, 158, 159, 159, 160, 161, 162, 163, 163, 164,
- 165, 166, 167, 167, 168, 169, 170, 170, 171, 172, 173, 173, 174, 175, 175, 176,
- 177, 178, 178, 179, 180, 180, 181, 182, 182, 183, 184, 185, 185, 186, 187, 187,
- 188, 189, 189, 190, 190, 191, 192, 192, 193, 194, 194, 195, 196, 196, 197, 197,
- 198, 199, 199, 200, 200, 201, 202, 202, 203, 203, 204, 205, 205, 206, 206, 207,
- 208, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214, 214, 215, 215, 216,
- 216, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, 222, 223, 223, 224, 224,
- 225, 226, 226, 227, 227, 228, 228, 229, 229, 230, 230, 231, 231, 232, 232, 233,
- 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, 238, 239, 239, 240, 240,
- 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 246, 246, 247, 247, 248,
- 248, 249, 249, 250, 250, 251, 251, 251, 252, 252, 253, 253, 254, 254, 255, 255
-};
-
int
libskrift_get_grapheme_glyph(LIBSKRIFT_CONTEXT *ctx, libskrift_codepoint_t codepoint,
double cursor_x, double cursor_y, struct libskrift_glyph **glyphp)
{
struct SFT_Char sft_chr;
- size_t size = 1, width0, width1, width2, width3, osize, off, r, c, i;
+ size_t size = 1, width1, width2, width3, osize, off, r, c, i;
int top = 0, left = 0, right = 0, bottom = 0;
uint16_t width, height, vmul = 1, hmul = 1;
- uint8_t *image, *in_image, t;
+ uint8_t *image, *in_image;
memset(&sft_chr, 0, sizeof(sft_chr));
@@ -80,18 +61,9 @@ libskrift_get_grapheme_glyph(LIBSKRIFT_CONTEXT *ctx, libskrift_codepoint_t codep
image += width3;
in_image += sft_chr.width;
}
- if (ctx->subpixel_bgr) {
- image = (*glyphp)->image;
- for (i = 0; i < size; i += 3) {
- t = image[i + 0];
- image[i + 0] = image[i + 2];
- image[i + 2] = t;
- }
- }
} else if (ctx->subpixel_vertically && height) {
- width0 = (size_t)width * (ctx->subpixel_bgr ? 2 : 0);
width1 = (size_t)width * 1;
- width2 = (size_t)width * (ctx->subpixel_bgr ? 0 : 2);
+ width2 = (size_t)width * 2;
width3 = (size_t)width * 3;
in_image = realloc(in_image, size);
if (!in_image) {
@@ -112,7 +84,7 @@ libskrift_get_grapheme_glyph(LIBSKRIFT_CONTEXT *ctx, libskrift_codepoint_t codep
memset(&in_image[osize], 0, size - osize);
for (i = r = 0; r < height; r++) {
for (c = 0; c < width; c++, i += 3) {
- image[i + 0] = in_image[c + width0];
+ image[i + 0] = in_image[c];
image[i + 1] = in_image[c + width1];
image[i + 2] = in_image[c + width2];
}
@@ -122,12 +94,6 @@ libskrift_get_grapheme_glyph(LIBSKRIFT_CONTEXT *ctx, libskrift_codepoint_t codep
memcpy(image, in_image, size);
}
- if (ctx->rendering.flags & LIBSKRIFT_CORRECT_GAMMA) {
- image = (*glyphp)->image;
- for (i = 0; i < size; i++)
- image[i] = gamma_map[image[i]];
- }
-
free(sft_chr.image);
return 0;
}
diff --git a/libskrift_merge_glyphs.c b/libskrift_merge_glyphs.c
index 48b1e5a..6c62fc8 100644
--- a/libskrift_merge_glyphs.c
+++ b/libskrift_merge_glyphs.c
@@ -4,14 +4,14 @@
/* TODO How common are grapheme clusters with more than two glyphs? Should we need a variadic version? */
int
-libskrift_merge_glyphs(LIBSKRIFT_CONTEXT *ctx, struct libskrift_glyph *glyph1,
- struct libskrift_glyph *glyph2, struct libskrift_glyph **glyphp)
+libskrift_merge_glyphs(LIBSKRIFT_CONTEXT *ctx, const struct libskrift_glyph *glyph1,
+ const struct libskrift_glyph *glyph2, struct libskrift_glyph **glyphp)
{
int16_t x1a, x1b, x2a, x2b, y1a, y1b, y2a, y2b, x1, x2, y1, y2;
size_t width, height, r, c, size, psize;
size_t src_off, dest_off, src_linesize, dest_linesize;
- psize = glyph1->size / ((size_t)glyph1->width * (size_t)glyph1->height);
+ psize = ctx->rendering.smoothing ? 3 : 1;
x1a = glyph1->x;
x1b = glyph2->x;
@@ -54,15 +54,31 @@ libskrift_merge_glyphs(LIBSKRIFT_CONTEXT *ctx, struct libskrift_glyph *glyph1,
src_linesize = glyph2->width * psize;
dest_off = (size_t)(glyph2->y - y1) * dest_linesize;
dest_off += (size_t)(glyph2->x - x1) * psize;
+
+ /* TODO only use merging on actual overlap */
+#ifndef OR_MERGE
if (ctx->rendering.smoothing) {
+#ifdef SUM_MERGE
+ unsigned sum;
+ for (r = src_off = 0; r < glyph2->height; r++, dest_off += dest_linesize, src_off += src_linesize) {
+ for (c = 0; c < src_linesize; c++) {
+ sum = (unsigned)(*glyphp)->image[dest_off + c] + (unsigned)glyph2->image[src_off + c];
+ (*glyphp)->image[dest_off + c] = (uint8_t)(sum | ((sum & 0x100) - 1));
+ }
+ }
+#else
for (r = src_off = 0; r < glyph2->height; r++, dest_off += dest_linesize, src_off += src_linesize)
for (c = 0; c < src_linesize; c++)
(*glyphp)->image[dest_off + c] = MAX((*glyphp)->image[dest_off + c], glyph2->image[src_off + c]);
+#endif
} else {
+#endif
for (r = src_off = 0; r < glyph2->height; r++, dest_off += dest_linesize, src_off += src_linesize)
for (c = 0; c < src_linesize; c++)
(*glyphp)->image[dest_off + c] |= glyph2->image[src_off + c];
+#ifndef OR_MERGE
}
+#endif
return 0;
}
diff --git a/libskrift_srgb_postprocess.c b/libskrift_srgb_postprocess.c
new file mode 100644
index 0000000..3e994c1
--- /dev/null
+++ b/libskrift_srgb_postprocess.c
@@ -0,0 +1,27 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+#define GAMMA(TYPE, POW) (t <= (TYPE)0.0031308f ? (TYPE)12.92f * t : (TYPE)1.055f * POW(t, 1 / (TYPE)2.4f) - (TYPE)0.055f)
+
+static const uint8_t gamma_u8[] = {
+ 0, 13, 22, 28, 34, 38, 42, 46, 50, 53, 56, 59, 61, 64, 66, 69,
+ 71, 73, 75, 77, 79, 81, 83, 85, 86, 88, 90, 92, 93, 95, 96, 98,
+ 99, 101, 102, 104, 105, 106, 108, 109, 110, 112, 113, 114, 115, 117, 118, 119,
+ 120, 121, 122, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136,
+ 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 148, 149, 150, 151,
+ 152, 153, 154, 155, 155, 156, 157, 158, 159, 159, 160, 161, 162, 163, 163, 164,
+ 165, 166, 167, 167, 168, 169, 170, 170, 171, 172, 173, 173, 174, 175, 175, 176,
+ 177, 178, 178, 179, 180, 180, 181, 182, 182, 183, 184, 185, 185, 186, 187, 187,
+ 188, 189, 189, 190, 190, 191, 192, 192, 193, 194, 194, 195, 196, 196, 197, 197,
+ 198, 199, 199, 200, 200, 201, 202, 202, 203, 203, 204, 205, 205, 206, 206, 207,
+ 208, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214, 214, 215, 215, 216,
+ 216, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, 222, 223, 223, 224, 224,
+ 225, 226, 226, 227, 227, 228, 228, 229, 229, 230, 230, 231, 231, 232, 232, 233,
+ 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, 238, 239, 239, 240, 240,
+ 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 246, 246, 247, 247, 248,
+ 248, 249, 249, 250, 250, 251, 251, 251, 252, 252, 253, 253, 254, 254, 255, 255
+};
+
+#define FUNCTION_NAME libskrift_srgb_preprocess
+
+#include "srgb-gamma.h"
diff --git a/libskrift_srgb_preprocess.c b/libskrift_srgb_preprocess.c
new file mode 100644
index 0000000..e290fc9
--- /dev/null
+++ b/libskrift_srgb_preprocess.c
@@ -0,0 +1,27 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+#define GAMMA(TYPE, POW) (t <= (TYPE)0.04045f ? t / (TYPE)12.92f : POW((t + (TYPE)0.055f) / (TYPE)1.055f, (TYPE)2.4f))
+
+static const uint8_t gamma_u8[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3,
+ 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7,
+ 8, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 12, 12, 12, 13,
+ 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 17, 18, 18, 19, 19, 20,
+ 20, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 27, 27, 28, 29, 29,
+ 30, 30, 31, 32, 32, 33, 34, 35, 35, 36, 37, 37, 38, 39, 40, 41,
+ 41, 42, 43, 44, 45, 45, 46, 47, 48, 49, 50, 51, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
+ 71, 72, 73, 74, 76, 77, 78, 79, 80, 81, 82, 84, 85, 86, 87, 88,
+ 90, 91, 92, 93, 95, 96, 97, 99, 100, 101, 103, 104, 105, 107, 108, 109,
+ 111, 112, 114, 115, 116, 118, 119, 121, 122, 124, 125, 127, 128, 130, 131, 133,
+ 134, 136, 138, 139, 141, 142, 144, 146, 147, 149, 151, 152, 154, 156, 157, 159,
+ 161, 163, 164, 166, 168, 170, 171, 173, 175, 177, 179, 181, 183, 184, 186, 188,
+ 190, 192, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212, 214, 216, 218, 220,
+ 222, 224, 226, 229, 231, 233, 235, 237, 239, 242, 244, 246, 248, 250, 253, 255
+};
+
+#define FUNCTION_NAME libskrift_srgb_preprocess
+
+#include "srgb-gamma.h"
diff --git a/srgb-gamma.h b/srgb-gamma.h
new file mode 100644
index 0000000..3388497
--- /dev/null
+++ b/srgb-gamma.h
@@ -0,0 +1,145 @@
+/* See LICENSE file for copyright and license details. */
+
+static inline long double
+gamma_ullf(long double t)
+{
+ return GAMMA(long double, powl);
+}
+
+static inline double
+gamma_ulf(double t)
+{
+ return GAMMA(double, pow);
+}
+
+static inline float
+gamma_uf(float t)
+{
+ return GAMMA(float, powf);
+}
+
+static inline long double
+gamma_llf(long double t)
+{
+ return t >= 0 ? gamma_ullf(t) : -gamma_ullf(-t);
+}
+
+static inline double
+gamma_lf(double t)
+{
+ return t >= 0 ? gamma_ulf(t) : -gamma_ulf(-t);
+}
+
+static inline float
+gamma_f(float t)
+{
+ return t >= 0 ? gamma_uf(t) : -gamma_uf(-t);
+}
+
+static inline uint64_t
+gamma_u64(uint64_t t)
+{
+ return (uint64_t)(gamma_ullf((long double)t / UINT64_MAX) * UINT64_MAX);
+}
+
+static inline uint32_t
+gamma_u32(uint32_t t)
+{
+ return (uint32_t)(gamma_ulf((double)t / UINT32_MAX) * UINT32_MAX);
+}
+
+static inline uint16_t
+gamma_u16(uint32_t t)
+{
+ return (uint16_t)(gamma_ulf((double)t / UINT16_MAX) * UINT16_MAX);
+}
+
+void
+FUNCTION_NAME(struct libskrift_image *image, size_t x, size_t y, size_t width, size_t height)
+{
+ struct format_settings settings;
+ uint8_t *img = image->image;
+ size_t linesize, psize;
+
+ settings = libskrift_format_settings[image->format];
+ psize = settings.spsize * (size_t)(4 - (settings.apos <= -2));
+ linesize = (size_t)image->width * psize;
+ img += y * linesize + x * psize;
+ width *= psize;
+
+ switch (settings.float_type) {
+ case 0:
+ switch (settings.spsize) {
+ case 1:
+ for (y = 0; y < height; y++, img += linesize) {
+ for (x = 0; x < width; x += psize) {
+ ((uint8_t *)&img[x])[settings.rpos] = gamma_u8[((uint8_t *)&img[x])[settings.rpos]];
+ ((uint8_t *)&img[x])[settings.gpos] = gamma_u8[((uint8_t *)&img[x])[settings.gpos]];
+ ((uint8_t *)&img[x])[settings.bpos] = gamma_u8[((uint8_t *)&img[x])[settings.bpos]];
+ }
+ }
+ break;
+
+ case 2:
+ for (y = 0; y < height; y++, img += linesize) {
+ for (x = 0; x < width; x += psize) {
+ ((uint16_t *)&img[x])[settings.rpos] = gamma_u16(((uint16_t *)&img[x])[settings.rpos]);
+ ((uint16_t *)&img[x])[settings.gpos] = gamma_u16(((uint16_t *)&img[x])[settings.gpos]);
+ ((uint16_t *)&img[x])[settings.bpos] = gamma_u16(((uint16_t *)&img[x])[settings.bpos]);
+ }
+ }
+ break;
+
+ case 4:
+ for (y = 0; y < height; y++, img += linesize) {
+ for (x = 0; x < width; x += psize) {
+ ((uint32_t *)&img[x])[settings.rpos] = gamma_u32(((uint32_t *)&img[x])[settings.rpos]);
+ ((uint32_t *)&img[x])[settings.gpos] = gamma_u32(((uint32_t *)&img[x])[settings.gpos]);
+ ((uint32_t *)&img[x])[settings.bpos] = gamma_u32(((uint32_t *)&img[x])[settings.bpos]);
+ }
+ }
+ break;
+
+ default:
+ for (y = 0; y < height; y++, img += linesize) {
+ for (x = 0; x < width; x += psize) {
+ ((uint64_t *)&img[x])[settings.rpos] = gamma_u64(((uint64_t *)&img[x])[settings.rpos]);
+ ((uint64_t *)&img[x])[settings.gpos] = gamma_u64(((uint64_t *)&img[x])[settings.gpos]);
+ ((uint64_t *)&img[x])[settings.bpos] = gamma_u64(((uint64_t *)&img[x])[settings.bpos]);
+ }
+ }
+ break;
+ }
+ break;
+
+ case 1:
+ for (y = 0; y < height; y++, img += linesize) {
+ for (x = 0; x < width; x += psize) {
+ ((float *)&img[x])[settings.rpos] = gamma_f(((float *)&img[x])[settings.rpos]);
+ ((float *)&img[x])[settings.gpos] = gamma_f(((float *)&img[x])[settings.gpos]);
+ ((float *)&img[x])[settings.bpos] = gamma_f(((float *)&img[x])[settings.bpos]);
+ }
+ }
+ break;
+
+ case 2:
+ for (y = 0; y < height; y++, img += linesize) {
+ for (x = 0; x < width; x += psize) {
+ ((double *)&img[x])[settings.rpos] = gamma_lf(((double *)&img[x])[settings.rpos]);
+ ((double *)&img[x])[settings.gpos] = gamma_lf(((double *)&img[x])[settings.gpos]);
+ ((double *)&img[x])[settings.bpos] = gamma_lf(((double *)&img[x])[settings.bpos]);
+ }
+ }
+ break;
+
+ default:
+ for (y = 0; y < height; y++, img += linesize) {
+ for (x = 0; x < width; x += psize) {
+ ((long double *)&img[x])[settings.rpos] = gamma_llf(((long double *)&img[x])[settings.rpos]);
+ ((long double *)&img[x])[settings.gpos] = gamma_llf(((long double *)&img[x])[settings.gpos]);
+ ((long double *)&img[x])[settings.bpos] = gamma_llf(((long double *)&img[x])[settings.bpos]);
+ }
+ }
+ break;
+ }
+}