/* 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 sx1, sy1, sx2, sy2; uint8_t high; uint8_t *img, *img_start = image->image; const uint8_t *gly = glyph->image, *gly_start; 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; } /* Top left corner of glyph on image */ sx1 = (int16_t)(x + glyph->x); sy1 = (int16_t)(y + glyph->y); /* Post-bottom right corner of glyph on image */ sx2 = (int16_t)(sx1 + (int16_t)glyph->width); sy2 = (int16_t)(sy1 + (int16_t)glyph->height); if (sx2 <= 0 || sy2 <= 0) return 0; /* First pixel in image to draw on */ x1 = sx1 < 0 ? 0 : (uint16_t)sx1; y1 = sy1 < 0 ? 0 : (uint16_t)sy1; if (x1 >= image->width || y1 >= image->height) return 0; /* Post-last pixel in image to draw on */ sx2 = (int16_t)(sx1 + (int16_t)glyph->width); sy2 = (int16_t)(sy1 + (int16_t)glyph->height); x2 = (uint16_t)sx2 < image->width ? (uint16_t)sx2 : image->width; y2 = (uint16_t)sy2 < image->height ? (uint16_t)sy2 : image->height; /* First pixel in glyph to draw */ startc = (uint16_t)((int16_t)x1 - sx1); startr = (uint16_t)((int16_t)y1 - sy1); /* Post-last pixel in glyph to draw*/ endc = (uint16_t)((int16_t)x2 - sx1); endr = (uint16_t)((int16_t)y2 - sy1); 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 == LIBSKRIFT_SUBPIXEL) ? 3 : 1; gly_linesize = (size_t)glyph->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; gly_start = gly; 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; gly = gly_start; 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; }