summaryrefslogtreecommitdiff
path: root/Build/source/texk/web2c/mfluadir/otfcc/lib/table/COLR.c
blob: 6f82b5296b33d451f5486bec9f28697d0ec76cc4 (plain)
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#include "COLR.h"

#include "support/util.h"
#include "bk/bkgraph.h"


static INLINE void initLayer(colr_Layer *layer) {
	Handle.init(&layer->glyph);
}
static INLINE void copyLayer(colr_Layer *dst, const colr_Layer *src) {
	Handle.copy(&dst->glyph, &src->glyph);
	dst->paletteIndex = src->paletteIndex;
}
static INLINE void disposeLayer(colr_Layer *layer) {
	Handle.dispose(&layer->glyph);
}
caryll_standardType(colr_Layer, colr_iLayer, initLayer, copyLayer, disposeLayer);
caryll_standardVectorImpl(colr_LayerList, colr_Layer, colr_iLayer, colr_iLayerList);

static INLINE void initMapping(colr_Mapping *mapping) {
	Handle.init(&mapping->glyph);
	colr_iLayerList.init(&mapping->layers);
}
static INLINE void copyMapping(colr_Mapping *dst, const colr_Mapping *src) {
	Handle.copy(&dst->glyph, &src->glyph);
	colr_iLayerList.copy(&dst->layers, &src->layers);
}
static INLINE void disposeMapping(colr_Mapping *mapping) {
	Handle.dispose(&mapping->glyph);
	colr_iLayerList.dispose(&mapping->layers);
}
caryll_standardType(colr_Mapping, colr_iMapping, initMapping, copyMapping, disposeMapping);
caryll_standardVectorImpl(table_COLR, colr_Mapping, colr_iMapping, table_iCOLR);

static const size_t baseGlyphRecLength = 6;
static const size_t layerRecLength = 4;

table_COLR *otfcc_readCOLR(const otfcc_Packet packet, const otfcc_Options *options) {
	table_COLR *colr = NULL;
	FOR_TABLE(OTFCC_CHR('C','O','L','R'), table) {
		if (table.length < 14) goto FAIL;
		uint16_t numBaseGlyphRecords = read_16u(table.data + 2);
		uint16_t numLayerRecords = read_16u(table.data + 12);
		uint32_t offsetBaseGlyphRecord = read_32u(table.data + 4);
		uint32_t offsetLayerRecord = read_32u(table.data + 8);
		if (table.length < offsetBaseGlyphRecord + baseGlyphRecLength * numBaseGlyphRecords)
			goto FAIL;
		if (table.length < offsetLayerRecord + layerRecLength * numLayerRecords) goto FAIL;

		// parse layer data
		glyphid_t *gids;
		colorid_t *colors;
		NEW(gids, numLayerRecords);
		NEW(colors, numLayerRecords);
		for (glyphid_t j = 0; j < numLayerRecords; j++) {
			gids[j] = read_16u(table.data + offsetLayerRecord + layerRecLength * j);
			colors[j] = read_16u(table.data + offsetLayerRecord + layerRecLength * j + 2);
		}
		// parse decomposition data
		colr = table_iCOLR.create();
		for (glyphid_t j = 0; j < numBaseGlyphRecords; j++) {
			colr_Mapping mapping;
			colr_iMapping.init(&mapping);
			uint16_t gid = read_16u(table.data + offsetBaseGlyphRecord + baseGlyphRecLength * j);
			uint16_t firstLayerIndex =
			    read_16u(table.data + offsetBaseGlyphRecord + baseGlyphRecLength * j + 2);
			uint16_t numLayers =
			    read_16u(table.data + offsetBaseGlyphRecord + baseGlyphRecLength * j + 4);

			glyph_handle baseGlyph = Handle.fromIndex(gid);
			Handle.move(&mapping.glyph, &baseGlyph);
			for (glyphid_t k = 0; k < numLayers; k++) {
				if (k + firstLayerIndex < numLayerRecords) {
					colr_iLayerList.push(&mapping.layers,
					                     (colr_Layer){
					                         .glyph = Handle.fromIndex(gids[k + firstLayerIndex]),
					                         .paletteIndex = colors[k + firstLayerIndex],
					                     });
				}
			}
			table_iCOLR.push(colr, mapping);
		}
		return colr;
	FAIL:
		logWarning("Table 'COLR' corrupted.\n");
		table_iCOLR.free(colr);
		colr = NULL;
	}
	return colr;
}

void otfcc_dumpCOLR(const table_COLR *colr, json_value *root, const otfcc_Options *options) {
	if (!colr) return;
	loggedStep("COLR") {
		json_value *_colr = json_array_new(colr->length);
		foreach (colr_Mapping *mapping, *colr) {
			json_value *_map = json_object_new(2);
			json_object_push(_map, "from", json_string_new(mapping->glyph.name));
			json_value *_layers = json_array_new(mapping->layers.length);
			foreach (colr_Layer *layer, mapping->layers) {
				json_value *_layer = json_object_new(2);
				json_object_push(_layer, "layer", json_string_new(layer->glyph.name));
				json_object_push(_layer, "paletteIndex", json_integer_new(layer->paletteIndex));
				json_array_push(_layers, _layer);
			}
			json_object_push(_map, "to", preserialize(_layers));
			json_array_push(_colr, _map);
		}
		json_object_push(root, "COLR", _colr);
	}
}

table_COLR *otfcc_parseCOLR(const json_value *root, const otfcc_Options *options) {
	json_value *_colr = NULL;
	if (!(_colr = json_obj_get_type(root, "COLR", json_array))) return NULL;
	table_COLR *colr = table_iCOLR.create();
	loggedStep("COLR") {
		for (glyphid_t j = 0; j < _colr->u.array.length; j++) {
			json_value *_mapping = _colr->u.array.values[j];
			if (!_mapping || _mapping->type != json_object) continue;
			json_value *_baseglyph = json_obj_get_type(_mapping, "from", json_string);
			json_value *_layers = json_obj_get_type(_mapping, "to", json_array);
			if (!_baseglyph || !_layers) continue;

			colr_Mapping m;
			colr_iMapping.init(&m);
			m.glyph =
			    Handle.fromName(sdsnewlen(_baseglyph->u.string.ptr, _baseglyph->u.string.length));
			for (glyphid_t k = 0; k < _layers->u.array.length; k++) {
				json_value *_layer = _layers->u.array.values[k];
				if (!_layer || _layer->type != json_object) continue;
				json_value *_layerglyph = json_obj_get_type(_layer, "layer", json_string);
				if (!_layerglyph) continue;
				colr_iLayerList.push(
				    &m.layers,
				    (colr_Layer){
				        .glyph = Handle.fromName(
				            sdsnewlen(_layerglyph->u.string.ptr, _layerglyph->u.string.length)),
				        .paletteIndex = json_obj_getint_fallback(_layer, "paletteIndex", 0xFFFF),
				    });
			}
			table_iCOLR.push(colr, m);
		}
	}
	return colr;
}

static int byGID(const colr_Mapping *a, const colr_Mapping *b) {
	return a->glyph.index - b->glyph.index;
}

caryll_Buffer *otfcc_buildCOLR(const table_COLR *_colr, const otfcc_Options *options) {
	if (!_colr || !_colr->length) return NULL;

	// sort base defs
	table_COLR colr;
	table_iCOLR.copy(&colr, _colr);
	table_iCOLR.sort(&colr, byGID);

	glyphid_t currentLayerIndex = 0;
	bk_Block *layerRecords = bk_new_Block(bkover);
	bk_Block *baseRecords = bk_new_Block(bkover);
	foreach (colr_Mapping *mapping, colr) {
		bk_push(baseRecords, b16, mapping->glyph.index, // GID
		        b16, currentLayerIndex,                 // firstLayerIndex
		        b16, mapping->layers.length,            // numLayers
		        bkover);
		foreach (colr_Layer *layer, mapping->layers) {
			bk_push(layerRecords, b16, layer->glyph.index, // GID
			        b16, layer->paletteIndex,              // paletteIndex
			        bkover);
			currentLayerIndex += 1;
		}
	}

	bk_Block *root = bk_new_Block(b16, 0,                 // Version
	                              b16, colr.length,       // numBaseGlyphRecords
	                              p32, baseRecords,       // offsetBaseGlyphRecord
	                              p32, layerRecords,      // offsetLayerRecord
	                              b16, currentLayerIndex, // numLayerRecords
	                              bkover);
	table_iCOLR.dispose(&colr);
	return bk_build_Block(root);
}