aboutsummaryrefslogtreecommitdiffstats
path: root/libskrift_merge_glyphs.c
blob: 820d0d47af71d0593a07fdb79ca29b6d91c4515a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
/* See LICENSE file for copyright and license details. */
#include "common.h"

/* TODO How common are grapheme clusters with more than two glyphs? Should we need a variadic version? */

int
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;
	const struct libskrift_glyph *t;

	psize = ctx->rendering.smoothing ? 3 : 1;

	if (!(glyph1->width | glyph1->height)) {
		t = glyph1;
		glyph1 = glyph2;
		glyph2 = t;
	}
	if (!(glyph2->width | glyph2->height)) {
		size = FLEXSTRUCTSIZE(struct libskrift_glyph, image, glyph1->size);
		*glyphp = calloc(1, size);
		if (!*glyphp)
			return -1;
		memcpy(*glyphp, glyph1, size);
		return 0;
	}

	x1a = glyph1->x;
	x1b = glyph2->x;
	y1a = glyph1->y;
	y1b = glyph2->y;

	x2a = (int16_t)(x1a + (int16_t)glyph1->width);
	x2b = (int16_t)(x1b + (int16_t)glyph2->width);
	y2a = (int16_t)(y1a + (int16_t)glyph1->height);
	y2b = (int16_t)(y1b + (int16_t)glyph2->height);

	x1 = MIN(x1a, x1b);
	y1 = MIN(y1a, y1b);
	x2 = MAX(x2a, x2b);
	y2 = MAX(y2a, y2b);

	size = psize;
	size *= width  = (uint16_t)(x2 - x1);
	size *= height = (uint16_t)(y2 - y1);

	*glyphp = calloc(1, FLEXSTRUCTSIZE(struct libskrift_glyph, image, size));
	if (!*glyphp)
		return -1;

	(*glyphp)->advance = glyph1->advance + glyph2->advance;
	(*glyphp)->x       = x1;
	(*glyphp)->y       = y1;
	(*glyphp)->width   = (uint16_t)width;
	(*glyphp)->height  = (uint16_t)height;
	(*glyphp)->size    = size;

	dest_linesize = width * psize;

	src_linesize = glyph1->width * psize;
	dest_off  = (size_t)(glyph1->y - y1) * dest_linesize;
	dest_off += (size_t)(glyph1->x - x1) * psize;
	for (r = src_off = 0; r < glyph1->height; r++, dest_off += dest_linesize, src_off += src_linesize)
		memcpy(&(*glyphp)->image[dest_off], &glyph1->image[src_off], src_linesize);

	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;
}