1 /*
2  * Copyright (c) 2018-2019 Hanspeter Portner (dev@open-music-kontrollers.ch)
3  *
4  * This is free software: you can redistribute it and/or modify
5  * it under the terms of the Artistic License 2.0 as published by
6  * The Perl Foundation.
7  *
8  * This source is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * Artistic License 2.0 for more details.
12  *
13  * You should have received a copy of the Artistic License 2.0
14  * along the source as a COPYING file. If not, obtain it from
15  * http://www.perlfoundation.org/artistic_license_2_0.
16  */
17 
18 #include <stdint.h>
19 #include <stdbool.h>
20 #include <string.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <stddef.h>
24 #include <inttypes.h>
25 #include <limits.h>
26 #include <assert.h>
27 #if defined(_WIN32)
28 #	include <winsock2.h>
29 #else
30 #	include <arpa/inet.h>
31 #endif
32 
33 #include "core_internal.h"
34 #include <d2tk/hash.h>
35 
36 #if D2TK_FONTCONFIG
37 #	include <fontconfig/fontconfig.h>
38 #endif
39 
40 #define _D2TK_SPRITES_MAX			0x10000 //FIXME how big?
41 #define _D2TK_SPRITES_MASK		(_D2TK_SPRITES_MAX - 1)
42 #define _D2TK_SPRITES_TTL			0x100
43 
44 #define _D2TK_MEMCACHES_MAX		0x10000 //FIXME how big?
45 #define _D2TK_MEMCACHES_MASK	(_D2TK_MEMCACHES_MAX - 1)
46 #define _D2TK_MEMCACHES_TTL		0x100
47 
48 typedef struct _d2tk_mem_t d2tk_mem_t;
49 typedef struct _d2tk_bitmap_t d2tk_bitmap_t;
50 typedef struct _d2tk_sprite_t d2tk_sprite_t;
51 typedef struct _d2tk_memcache_t d2tk_memcache_t;
52 typedef struct _d2tk_widget_body_t d2tk_widget_body_t;
53 
54 struct _d2tk_mem_t {
55 	size_t size;
56 	size_t offset;
57 	uint8_t *buf;
58 };
59 
60 struct _d2tk_bitmap_t {
61 	size_t size;
62 	uint32_t *pixels;
63 	uint32_t *template;
64 	size_t nfills;
65 	d2tk_coord_t x0;
66 	d2tk_coord_t x1;
67 	d2tk_coord_t y0;
68 	d2tk_coord_t y1;
69 };
70 
71 struct _d2tk_sprite_t {
72 	uint64_t hash;
73 	uintptr_t body;
74 	uint32_t type;
75 	uint32_t ttl;
76 };
77 
78 struct _d2tk_memcache_t {
79 	uint64_t hash;
80 	uintptr_t body;
81 	uint32_t ttl;
82 };
83 
84 struct _d2tk_widget_body_t {
85 	size_t size;
86 	uint8_t buf [];
87 };
88 
89 struct _d2tk_widget_t {
90 	size_t ref;
91 	uintptr_t *body;
92 };
93 
94 struct _d2tk_core_t {
95 	const d2tk_core_driver_t *driver;
96 	void *data;
97 
98 	d2tk_coord_t w;
99 	d2tk_coord_t h;
100 
101 	struct {
102 		d2tk_coord_t x;
103 		d2tk_coord_t y;
104 	} ref;
105 
106 	d2tk_mem_t mem [2];
107 	bool curmem;
108 
109 	bool full_refresh;
110 
111 	d2tk_bitmap_t bitmap;
112 
113 	uint32_t bg_color;
114 
115 	struct {
116 		uint32_t sprites;
117 		uint32_t memcaches;
118 	} ttl;
119 
120 	d2tk_sprite_t sprites [_D2TK_SPRITES_MAX];
121 	d2tk_memcache_t memcaches [_D2TK_MEMCACHES_MAX];
122 
123 	ssize_t parent;
124 };
125 
126 const size_t d2tk_widget_sz = sizeof(d2tk_widget_t);
127 
128 D2TK_API void
d2tk_rect_shrink_x(d2tk_rect_t * dst,const d2tk_rect_t * src,d2tk_coord_t brd)129 d2tk_rect_shrink_x(d2tk_rect_t *dst, const d2tk_rect_t *src,
130 	d2tk_coord_t brd)
131 {
132 	dst->x = src->x + brd;
133 	dst->y = src->y;
134 	brd <<= 1;
135 	dst->w = src->w - brd;
136 	dst->h = src->h;
137 }
138 
139 D2TK_API void
d2tk_rect_shrink_y(d2tk_rect_t * dst,const d2tk_rect_t * src,d2tk_coord_t brd)140 d2tk_rect_shrink_y(d2tk_rect_t *dst, const d2tk_rect_t *src,
141 	d2tk_coord_t brd)
142 {
143 	dst->x = src->x;
144 	dst->y = src->y + brd;
145 	brd <<= 1;
146 	dst->w = src->w;
147 	dst->h = src->h - brd;
148 }
149 
150 D2TK_API void
d2tk_rect_shrink(d2tk_rect_t * dst,const d2tk_rect_t * src,d2tk_coord_t brd)151 d2tk_rect_shrink(d2tk_rect_t *dst, const d2tk_rect_t *src,
152 	d2tk_coord_t brd)
153 {
154 	dst->x = src->x + brd;
155 	dst->y = src->y + brd;
156 	brd <<= 1;
157 	dst->w = src->w - brd;
158 	dst->h = src->h - brd;
159 }
160 
161 uintptr_t *
d2tk_core_get_sprite(d2tk_core_t * core,uint64_t hash,uint8_t type)162 d2tk_core_get_sprite(d2tk_core_t *core, uint64_t hash, uint8_t type)
163 {
164 	for(unsigned i = 0; i < _D2TK_SPRITES_MAX; i++)
165 	{
166 		const unsigned j = (hash + i*i) & _D2TK_SPRITES_MASK;
167 		d2tk_sprite_t *sprite = &core->sprites[j];
168 
169 		if(sprite->body) // sprite is already taken
170 		{
171 			// is this the sprite we're looking for?
172 			if( (sprite->hash == hash) && (sprite->type == type) )
173 			{
174 				sprite->ttl = core->ttl.sprites;
175 				sprite->type = type;
176 				return &sprite->body;
177 			}
178 			else // not our sprite
179 			{
180 				continue;
181 			}
182 		}
183 
184 		// empty sprite, ready to be taken
185 		sprite->hash = hash;
186 		sprite->ttl = core->ttl.sprites;
187 		sprite->type = type;
188 		return &sprite->body;
189 	}
190 
191 	return NULL; // out of memory FIXME increase memory and remap existing sprites
192 }
193 
194 static inline void
_d2tk_sprites_free(d2tk_core_t * core)195 _d2tk_sprites_free(d2tk_core_t *core)
196 {
197 	for(unsigned i = 0; i < _D2TK_SPRITES_MAX; i++)
198 	{
199 		d2tk_sprite_t *sprite = &core->sprites[i];
200 
201 		if(!sprite->hash)
202 		{
203 			continue;
204 		}
205 
206 		if(sprite->body)
207 		{
208 			core->driver->sprite_free(core->data, sprite->type, sprite->body);
209 			sprite->type = 0;
210 			sprite->body = 0;
211 		}
212 
213 		sprite->hash = 0;
214 	}
215 }
216 
217 static inline void
_d2tk_sprites_gc(d2tk_core_t * core)218 _d2tk_sprites_gc(d2tk_core_t *core)
219 {
220 	for(unsigned i = 0; i < _D2TK_SPRITES_MAX; i++)
221 	{
222 		d2tk_sprite_t *sprite = &core->sprites[i];
223 
224 		if(!sprite->hash || (--sprite->ttl > 0) )
225 		{
226 			continue;
227 		}
228 
229 		if(sprite->body)
230 		{
231 #if D2TK_DEBUG
232 			fprintf(stderr, "\tgc sprites (%08"PRIx64")\n", sprite->hash);
233 #endif
234 			core->driver->sprite_free(core->data, sprite->type, sprite->body);
235 			sprite->type = 0;
236 			sprite->body = 0;
237 		}
238 
239 		sprite->hash = 0;
240 	}
241 }
242 
243 static inline void
_d2tk_mem_init(d2tk_mem_t * mem,size_t size)244 _d2tk_mem_init(d2tk_mem_t *mem, size_t size)
245 {
246 	mem->size = size;
247 	mem->offset = 0;
248 	mem->buf = malloc(mem->size);
249 }
250 
251 static inline void
_d2tk_mem_deinit(d2tk_mem_t * mem)252 _d2tk_mem_deinit(d2tk_mem_t *mem)
253 {
254 	mem->size = 0;
255 	mem->offset = 0;
256 	free(mem->buf);
257 	mem->buf = NULL;
258 }
259 
260 static inline void
_d2tk_mem_reset(d2tk_mem_t * mem)261 _d2tk_mem_reset(d2tk_mem_t *mem)
262 {
263 	mem->offset = 0;
264 	memset(mem->buf, 0x0, mem->size);
265 }
266 
267 static inline uintptr_t *
_d2tk_core_get_memcache(d2tk_core_t * core,uint64_t hash)268 _d2tk_core_get_memcache(d2tk_core_t *core, uint64_t hash)
269 {
270 	for(unsigned i = 0; i < _D2TK_MEMCACHES_MAX; i++)
271 	{
272 		const unsigned j = (hash + i*i) & _D2TK_MEMCACHES_MASK;
273 		d2tk_memcache_t *memcache = &core->memcaches[j];
274 
275 		if(memcache->body) // memcache is already taken
276 		{
277 			// is this the memcache we're looking for?
278 			if(memcache->hash == hash)
279 			{
280 				memcache->ttl = core->ttl.memcaches;
281 				return &memcache->body;
282 			}
283 			else // not our memcache
284 			{
285 				continue;
286 			}
287 		}
288 
289 		// empty memcache, ready to be taken
290 		memcache->hash = hash;
291 		memcache->ttl = core->ttl.memcaches;
292 		return &memcache->body;
293 	}
294 
295 	return NULL; // out of memory FIXME increase memory and remap existing memcaches
296 }
297 
298 static inline void
_d2tk_memcaches_free(d2tk_core_t * core)299 _d2tk_memcaches_free(d2tk_core_t *core)
300 {
301 	for(unsigned i = 0; i < _D2TK_MEMCACHES_MAX; i++)
302 	{
303 		d2tk_memcache_t *memcache = &core->memcaches[i];
304 
305 		if(!memcache->hash)
306 		{
307 			continue;
308 		}
309 
310 		if(memcache->body)
311 		{
312 			d2tk_widget_body_t *body = (d2tk_widget_body_t *)memcache->body;
313 
314 			free(body);
315 			memcache->body = 0;
316 		}
317 
318 		memcache->hash = 0;
319 	}
320 }
321 
322 static inline void
_d2tk_memcaches_gc(d2tk_core_t * core)323 _d2tk_memcaches_gc(d2tk_core_t *core)
324 {
325 	for(unsigned i = 0; i < _D2TK_MEMCACHES_MAX; i++)
326 	{
327 		d2tk_memcache_t *memcache = &core->memcaches[i];
328 
329 		if(!memcache->hash || (--memcache->ttl > 0) )
330 		{
331 			continue;
332 		}
333 
334 		if(memcache->body)
335 		{
336 #if D2TK_DEBUG
337 			fprintf(stderr, "\tgc memcaches (%08"PRIx64")\n", memcache->hash);
338 #endif
339 			d2tk_widget_body_t *body = (d2tk_widget_body_t *)memcache->body;
340 
341 			free(body);
342 			memcache->body = 0;
343 		}
344 
345 		memcache->hash = 0;
346 	}
347 }
348 
349 static inline void
_d2tk_bitmap_template_refill(d2tk_core_t * core)350 _d2tk_bitmap_template_refill(d2tk_core_t *core)
351 {
352 	d2tk_bitmap_t *bitmap = &core->bitmap;
353 
354 	for(d2tk_coord_t x = 0; x < core->w; x++)
355 	{
356 		bitmap->template[x] = core->bg_color;
357 	}
358 }
359 
360 static inline void
_d2tk_bitmap_resize(d2tk_core_t * core,d2tk_coord_t w,d2tk_coord_t h)361 _d2tk_bitmap_resize(d2tk_core_t *core, d2tk_coord_t w, d2tk_coord_t h)
362 {
363 	d2tk_bitmap_t *bitmap = &core->bitmap;
364 
365 	const size_t stride = w*sizeof(uint32_t);
366 	bitmap->size = h*stride;
367 	bitmap->pixels = realloc(bitmap->pixels, bitmap->size);
368 	bitmap->template = realloc(bitmap->template, stride);
369 	_d2tk_bitmap_template_refill(core);
370 }
371 
372 static inline void
_d2tk_bitmap_deinit(d2tk_bitmap_t * bitmap)373 _d2tk_bitmap_deinit(d2tk_bitmap_t *bitmap)
374 {
375 	free(bitmap->pixels);
376 	bitmap->pixels = NULL;
377 	free(bitmap->template);
378 	bitmap->template = NULL;
379 	bitmap->size = 0;
380 	bitmap->nfills = 0;
381 }
382 
383 static inline void
_d2tk_bitmap_reset(d2tk_core_t * core)384 _d2tk_bitmap_reset(d2tk_core_t *core)
385 {
386 	d2tk_bitmap_t *bitmap = &core->bitmap;
387 
388 	// x1/y1 may be wrong after window shrink
389 	const d2tk_coord_t x1 = bitmap->x1 < core->w
390 		? bitmap->x1
391 		: core->w;
392 	const d2tk_coord_t y1 = bitmap->y1 < core->h
393 		? bitmap->y1
394 		: core->h;
395 
396 	const size_t stride = (x1 - bitmap->x0)*sizeof(uint32_t);
397 
398 	for(d2tk_coord_t y = bitmap->y0, Y = y*core->w; y < y1; y++, Y+=core->w)
399 	{
400 		memset(&bitmap->pixels[Y + bitmap->x0], 0x0, stride);
401 	}
402 
403 	bitmap->nfills = 0;
404 	bitmap->x0 = INT_MAX;
405 	bitmap->x1 = INT_MIN;
406 	bitmap->y0 = INT_MAX;
407 	bitmap->y1 = INT_MIN;
408 }
409 
410 static inline void
_d2tk_clip_clip(d2tk_core_t * core,d2tk_clip_t * dst,const d2tk_clip_t * src)411 _d2tk_clip_clip(d2tk_core_t *core, d2tk_clip_t *dst, const d2tk_clip_t *src)
412 {
413 	*dst = *src;
414 
415 	if(dst->x0 < 0)
416 	{
417 		dst->x0 = 0;
418 	}
419 	if(dst->x1 < 0)
420 	{
421 		dst->x1 = 0;
422 	}
423 
424 	if(dst->y0 < 0)
425 	{
426 		dst->y0 = 0;
427 	}
428 	if(dst->y1 < 0)
429 	{
430 		dst->y1 = 0;
431 	}
432 
433 	if(dst->x0 >= core->w)
434 	{
435 		dst->x0 = core->w - 1;
436 	}
437 	if(dst->x1 >= core->w)
438 	{
439 		dst->x1 = core->w - 1;
440 	}
441 
442 	if(dst->y0 >= core->h)
443 	{
444 		dst->y0 = core->h - 1;
445 	}
446 	if(dst->y1 >= core->h)
447 	{
448 		dst->y1 = core->h - 1;
449 	}
450 }
451 
452 static inline void
_d2tk_bitmap_fill(d2tk_core_t * core,const d2tk_clip_t * clip)453 _d2tk_bitmap_fill(d2tk_core_t *core, const d2tk_clip_t *clip)
454 {
455 	d2tk_bitmap_t *bitmap = &core->bitmap;
456 
457 	d2tk_clip_t dst;
458 	_d2tk_clip_clip(core, &dst, clip);
459 
460 	const size_t stride = (dst.x1 - dst.x0)*sizeof(uint32_t);
461 
462 	for(d2tk_coord_t y = dst.y0, Y = y*core->w; y < dst.y1; y++, Y+=core->w)
463 	{
464 		memcpy(&bitmap->pixels[Y + dst.x0], bitmap->template, stride);
465 	}
466 
467 	// update area of interest
468 	if(dst.x0 < bitmap->x0)
469 	{
470 		bitmap->x0 = dst.x0;
471 	}
472 
473 	if(dst.x1 > bitmap->x1)
474 	{
475 		bitmap->x1 = dst.x1;
476 	}
477 
478 	if(dst.y0 < bitmap->y0)
479 	{
480 		bitmap->y0 = dst.y0;
481 	}
482 
483 	if(dst.y1 > bitmap->y1)
484 	{
485 		bitmap->y1 = dst.y1;
486 	}
487 
488 	bitmap->nfills++;
489 }
490 
491 static inline d2tk_com_t *
_d2tk_com_begin(d2tk_com_t * com)492 _d2tk_com_begin(d2tk_com_t *com)
493 {
494 	d2tk_com_t *bbox = (d2tk_com_t *)((uint8_t *)com + sizeof(d2tk_com_t)
495 		+ D2TK_PAD_SIZE(sizeof(d2tk_body_bbox_t)));
496 
497 	return bbox;
498 }
499 
500 static inline d2tk_com_t *
_d2tk_com_get_end(d2tk_com_t * com)501 _d2tk_com_get_end(d2tk_com_t *com)
502 {
503 	return (d2tk_com_t *)((uint8_t *)com + sizeof(d2tk_com_t)
504 		+ com->size);
505 }
506 
507 static inline bool
_d2tk_com_not_end(d2tk_com_t * end,d2tk_com_t * bbox)508 _d2tk_com_not_end(d2tk_com_t *end, d2tk_com_t *bbox)
509 {
510 	return bbox < end;
511 }
512 
513 static inline d2tk_com_t *
_d2tk_com_next(d2tk_com_t * bbox)514 _d2tk_com_next(d2tk_com_t *bbox)
515 {
516 	d2tk_com_t *nxt = (d2tk_com_t *)((uint8_t *)bbox + sizeof(d2tk_com_t)
517 		+ D2TK_PAD_SIZE(bbox->size));
518 
519 	return nxt;
520 }
521 
522 const d2tk_com_t *
d2tk_com_begin_const(const d2tk_com_t * com)523 d2tk_com_begin_const(const d2tk_com_t *com)
524 {
525 	return _d2tk_com_begin((d2tk_com_t *)com);
526 }
527 
528 const d2tk_com_t *
d2tk_com_get_end_const(const d2tk_com_t * com)529 d2tk_com_get_end_const(const d2tk_com_t *com)
530 {
531 	return _d2tk_com_get_end((d2tk_com_t *)com);
532 }
533 
534 bool
d2tk_com_not_end_const(const d2tk_com_t * end,const d2tk_com_t * bbox)535 d2tk_com_not_end_const(const d2tk_com_t *end, const d2tk_com_t *bbox)
536 {
537 	return _d2tk_com_not_end((d2tk_com_t *)end, (d2tk_com_t *)bbox);
538 }
539 
540 const d2tk_com_t *
d2tk_com_next_const(const d2tk_com_t * bbox)541 d2tk_com_next_const(const d2tk_com_t *bbox)
542 {
543 	return _d2tk_com_next((d2tk_com_t *)bbox);
544 }
545 
546 #define D2TK_COM_FOREACH_FROM(COM, FROM, BBOX) \
547 	for(d2tk_com_t *(BBOX) = (FROM), \
548 			*__end = _d2tk_com_get_end((COM)); \
549 		_d2tk_com_not_end(__end, (BBOX)); \
550 		(BBOX) = _d2tk_com_next((BBOX)))
551 
552 #define D2TK_COM_FOREACH(COM, BBOX) \
553 	for(d2tk_com_t *(BBOX) = _d2tk_com_begin((COM)), \
554 			*__end = _d2tk_com_get_end((COM)); \
555 		_d2tk_com_not_end(__end, (BBOX)); \
556 		(BBOX) = _d2tk_com_next((BBOX)))
557 
558 static inline void
_d2tk_bbox_mask(d2tk_core_t * core,d2tk_com_t * com)559 _d2tk_bbox_mask(d2tk_core_t *core, d2tk_com_t *com)
560 {
561 	d2tk_body_bbox_t *body = &com->body->bbox;
562 
563 	if(body->container)
564 	{
565 		D2TK_COM_FOREACH(com, bbox)
566 		{
567 			_d2tk_bbox_mask(core, bbox);
568 		}
569 	}
570 	else
571 	{
572 		_d2tk_bitmap_fill(core, &body->clip);
573 	}
574 
575 	body->dirty = true;
576 }
577 
578 uint32_t *
d2tk_core_get_pixels(d2tk_core_t * core,d2tk_rect_t * rect)579 d2tk_core_get_pixels(d2tk_core_t *core, d2tk_rect_t *rect)
580 {
581 	if(rect)
582 	{
583 		rect->x = core->bitmap.x0;
584 		rect->y = core->bitmap.y0;
585 		rect->w = core->bitmap.x1 - core->bitmap.x0;
586 		rect->h = core->bitmap.y1 - core->bitmap.y0;
587 	}
588 
589 	return core->bitmap.pixels;
590 }
591 
592 static inline bool
_d2tk_bitmap_query(d2tk_core_t * core,d2tk_body_bbox_t * body)593 _d2tk_bitmap_query(d2tk_core_t *core, d2tk_body_bbox_t *body)
594 {
595 	const d2tk_clip_t *clip = &body->clip;
596 
597 	d2tk_clip_t dst;
598 	_d2tk_clip_clip(core, &dst, clip);
599 
600 	for(d2tk_coord_t y = dst.y0, Y = y*core->w; y < dst.y1; y++, Y+=core->w)
601 	{
602 		for(d2tk_coord_t x = dst.x0; x < dst.x1; x++)
603 		{
604 			if(core->bitmap.pixels[Y + x])
605 			{
606 				body->dirty = true;
607 				return true;
608 			}
609 		}
610 	}
611 
612 	return false;
613 }
614 
615 static inline void
_d2tk_mem_compact(d2tk_mem_t * mem)616 _d2tk_mem_compact(d2tk_mem_t *mem)
617 {
618 	if(mem->offset == 0)
619 	{
620 		return;
621 	}
622 
623 	for(size_t nsize = (mem->size >> 1);
624 		mem->offset <= nsize;
625 		nsize >>= 1)
626 	{
627 		uint8_t *nbuf = realloc(mem->buf, nsize);
628 		assert(nbuf);
629 
630 		mem->buf = nbuf;
631 		mem->size = nsize;
632 	}
633 }
634 
635 static inline d2tk_com_t *
_d2tk_mem_get_com(d2tk_mem_t * mem)636 _d2tk_mem_get_com(d2tk_mem_t *mem)
637 {
638 	return (d2tk_com_t *)&mem->buf[0];
639 }
640 
641 static inline uint8_t *
_d2tk_mem_append_request(d2tk_mem_t * mem,size_t len)642 _d2tk_mem_append_request(d2tk_mem_t *mem, size_t len)
643 {
644 	const size_t padlen = D2TK_PAD_SIZE(len);
645 
646 	for(size_t msize = mem->offset + padlen, nsize = (mem->size << 1);
647 		mem->size < msize;
648 		nsize <<= 1)
649 	{
650 		uint8_t *nbuf = realloc(mem->buf, nsize);
651 		assert(nbuf);
652 
653 		memset(&nbuf[mem->size], 0x0, mem->size);
654 
655 		mem->buf = nbuf;
656 		mem->size = nsize;
657 	}
658 
659 	return &mem->buf[mem->offset];
660 }
661 
662 static inline void
_d2tk_mem_append_advance(d2tk_mem_t * mem,size_t len)663 _d2tk_mem_append_advance(d2tk_mem_t *mem, size_t len)
664 {
665 	const size_t padlen = D2TK_PAD_SIZE(len);
666 
667 	mem->offset += padlen;
668 }
669 
670 static inline void
_d2tk_append_simple(d2tk_core_t * core,d2tk_instr_t type)671 _d2tk_append_simple(d2tk_core_t *core, d2tk_instr_t type)
672 {
673 	const size_t len = sizeof(d2tk_com_t);
674 	d2tk_mem_t *mem = &core->mem[core->curmem];
675 	d2tk_com_t *com = (d2tk_com_t *)_d2tk_mem_append_request(mem, len);
676 
677 	if(com)
678 	{
679 		com->size = 0;
680 		com->instr = type;
681 
682 		_d2tk_mem_append_advance(mem, len);
683 	}
684 }
685 
686 static inline d2tk_body_t *
_d2tk_append_request(d2tk_core_t * core,size_t size,d2tk_instr_t type)687 _d2tk_append_request(d2tk_core_t *core, size_t size, d2tk_instr_t type)
688 {
689 	const size_t len = sizeof(d2tk_com_t) + size;
690 	d2tk_mem_t *mem = &core->mem[core->curmem];
691 	d2tk_com_t *com = (d2tk_com_t *)_d2tk_mem_append_request(mem, len);
692 
693 	if(com)
694 	{
695 		com->size = size;
696 		com->instr = type;
697 
698 		return com->body;
699 	}
700 
701 	return NULL;
702 }
703 
704 static inline void
_d2tk_append_advance(d2tk_core_t * core,size_t size)705 _d2tk_append_advance(d2tk_core_t *core, size_t size)
706 {
707 	const size_t len = sizeof(d2tk_com_t) + size;
708 	d2tk_mem_t *mem = &core->mem[core->curmem];
709 
710 	_d2tk_mem_append_advance(mem, len);
711 }
712 
713 D2TK_API void
d2tk_core_move_to(d2tk_core_t * core,d2tk_coord_t x,d2tk_coord_t y)714 d2tk_core_move_to(d2tk_core_t *core, d2tk_coord_t x, d2tk_coord_t y)
715 {
716 	const size_t len = sizeof(d2tk_body_move_to_t);
717 	d2tk_body_t *body = _d2tk_append_request(core, len, D2TK_INSTR_MOVE_TO);
718 
719 	if(body)
720 	{
721 		body->move_to.x = x;
722 		body->move_to.y = y;
723 
724 		body->move_to.x -= core->ref.x;
725 		body->move_to.y -= core->ref.y;
726 
727 		_d2tk_append_advance(core, len);
728 	}
729 }
730 
731 D2TK_API void
d2tk_core_line_to(d2tk_core_t * core,d2tk_coord_t x,d2tk_coord_t y)732 d2tk_core_line_to(d2tk_core_t *core, d2tk_coord_t x, d2tk_coord_t y)
733 {
734 	const size_t len = sizeof(d2tk_body_line_to_t);
735 	d2tk_body_t *body = _d2tk_append_request(core, len, D2TK_INSTR_LINE_TO);
736 
737 	if(body)
738 	{
739 		body->line_to.x = x;
740 		body->line_to.y = y;
741 
742 		body->line_to.x -= core->ref.x;
743 		body->line_to.y -= core->ref.y;
744 
745 		_d2tk_append_advance(core, len);
746 	}
747 }
748 
749 D2TK_API void
d2tk_core_rect(d2tk_core_t * core,const d2tk_rect_t * rect)750 d2tk_core_rect(d2tk_core_t *core, const d2tk_rect_t *rect)
751 {
752 	const size_t len = sizeof(d2tk_body_rect_t);
753 	d2tk_body_t *body = _d2tk_append_request(core, len, D2TK_INSTR_RECT);
754 
755 	if(body)
756 	{
757 		body->rect.x = rect->x;
758 		body->rect.y = rect->y;
759 		body->rect.w = rect->w;
760 		body->rect.h = rect->h;
761 
762 		body->rect.x -= core->ref.x;
763 		body->rect.y -= core->ref.y;
764 
765 		_d2tk_append_advance(core, len);
766 	}
767 }
768 
769 D2TK_API void
d2tk_core_rounded_rect(d2tk_core_t * core,const d2tk_rect_t * rect,d2tk_coord_t r)770 d2tk_core_rounded_rect(d2tk_core_t *core, const d2tk_rect_t *rect,
771 	d2tk_coord_t r)
772 {
773 	const size_t len = sizeof(d2tk_body_rounded_rect_t);
774 	d2tk_body_t *body = _d2tk_append_request(core, len, D2TK_INSTR_ROUNDED_RECT);
775 
776 	if(body)
777 	{
778 		body->rounded_rect.x = rect->x;
779 		body->rounded_rect.y = rect->y;
780 		body->rounded_rect.w = rect->w;
781 		body->rounded_rect.h = rect->h;
782 		body->rounded_rect.r = r;
783 
784 		body->rounded_rect.x -= core->ref.x;
785 		body->rounded_rect.y -= core->ref.y;
786 
787 		_d2tk_append_advance(core, len);
788 	}
789 }
790 
791 D2TK_API void
d2tk_core_arc(d2tk_core_t * core,d2tk_coord_t x,d2tk_coord_t y,d2tk_coord_t r,d2tk_coord_t a,d2tk_coord_t b,bool cw)792 d2tk_core_arc(d2tk_core_t *core, d2tk_coord_t x, d2tk_coord_t y, d2tk_coord_t r,
793 	d2tk_coord_t a, d2tk_coord_t b, bool cw)
794 {
795 	const size_t len = sizeof(d2tk_body_arc_t);
796 	d2tk_body_t *body = _d2tk_append_request(core, len, D2TK_INSTR_ARC);
797 
798 	if(body)
799 	{
800 		body->arc.x = x;
801 		body->arc.y = y;
802 		body->arc.r = r;
803 		body->arc.a = a;
804 		body->arc.b = b;
805 		body->arc.cw = cw;
806 
807 		body->arc.x -= core->ref.x;
808 		body->arc.y -= core->ref.y;
809 
810 		_d2tk_append_advance(core, len);
811 	}
812 }
813 
814 D2TK_API void
d2tk_core_curve_to(d2tk_core_t * core,d2tk_coord_t x1,d2tk_coord_t y1,d2tk_coord_t x2,d2tk_coord_t y2,d2tk_coord_t x3,d2tk_coord_t y3)815 d2tk_core_curve_to(d2tk_core_t *core, d2tk_coord_t x1, d2tk_coord_t y1,
816 	d2tk_coord_t x2, d2tk_coord_t y2, d2tk_coord_t x3, d2tk_coord_t y3)
817 {
818 	const size_t len = sizeof(d2tk_body_curve_to_t);
819 	d2tk_body_t *body = _d2tk_append_request(core, len, D2TK_INSTR_CURVE_TO);
820 
821 	if(body)
822 	{
823 		body->curve_to.x1 = x1;
824 		body->curve_to.y1 = y1;
825 		body->curve_to.x2 = x2;
826 		body->curve_to.y2 = y2;
827 		body->curve_to.x3 = x3;
828 		body->curve_to.y3 = y3;
829 
830 		body->curve_to.x1 -= core->ref.x;
831 		body->curve_to.x2 -= core->ref.x;
832 		body->curve_to.x3 -= core->ref.x;
833 
834 		body->curve_to.y1 -= core->ref.y;
835 		body->curve_to.y2 -= core->ref.y;
836 		body->curve_to.y3 -= core->ref.y;
837 
838 		_d2tk_append_advance(core, len);
839 	}
840 }
841 
842 void
d2tk_core_set_bg_color(d2tk_core_t * core,uint32_t rgba)843 d2tk_core_set_bg_color(d2tk_core_t *core, uint32_t rgba)
844 {
845 	core->bg_color = htonl(rgba);
846 	_d2tk_bitmap_template_refill(core);
847 }
848 
849 uint32_t
d2tk_core_get_bg_color(d2tk_core_t * core)850 d2tk_core_get_bg_color(d2tk_core_t *core)
851 {
852 	return ntohl(core->bg_color);
853 }
854 
855 D2TK_API void
d2tk_core_color(d2tk_core_t * core,uint32_t rgba)856 d2tk_core_color(d2tk_core_t *core, uint32_t rgba)
857 {
858 	const size_t len = sizeof(d2tk_body_color_t);
859 	d2tk_body_t *body = _d2tk_append_request(core, len, D2TK_INSTR_COLOR);
860 
861 	if(body)
862 	{
863 		body->color.rgba = rgba;
864 
865 		_d2tk_append_advance(core, len);
866 	}
867 }
868 
869 D2TK_API void
d2tk_core_linear_gradient(d2tk_core_t * core,const d2tk_point_t point[2],const uint32_t rgba[2])870 d2tk_core_linear_gradient(d2tk_core_t *core, const d2tk_point_t point [2],
871 	const uint32_t rgba [2])
872 {
873 	const size_t len = sizeof(d2tk_body_linear_gradient_t);
874 	d2tk_body_t *body = _d2tk_append_request(core, len, D2TK_INSTR_LINEAR_GRADIENT);
875 
876 	if(body)
877 	{
878 		body->linear_gradient.p[0].x = point[0].x;
879 		body->linear_gradient.p[0].y = point[0].y;
880 		body->linear_gradient.p[1].x = point[1].x;
881 		body->linear_gradient.p[1].y = point[1].y;
882 		body->linear_gradient.rgba[0] = rgba[0];
883 		body->linear_gradient.rgba[1] = rgba[1];
884 
885 		body->linear_gradient.p[0].x -= core->ref.x;
886 		body->linear_gradient.p[0].y -= core->ref.y;
887 		body->linear_gradient.p[1].x -= core->ref.x;
888 		body->linear_gradient.p[1].y -= core->ref.y;
889 
890 		_d2tk_append_advance(core, len);
891 	}
892 }
893 
894 D2TK_API void
d2tk_core_stroke(d2tk_core_t * core)895 d2tk_core_stroke(d2tk_core_t *core)
896 {
897 	_d2tk_append_simple(core, D2TK_INSTR_STROKE);
898 }
899 
900 D2TK_API void
d2tk_core_fill(d2tk_core_t * core)901 d2tk_core_fill(d2tk_core_t *core)
902 {
903 	_d2tk_append_simple(core, D2TK_INSTR_FILL);
904 }
905 
906 D2TK_API void
d2tk_core_save(d2tk_core_t * core)907 d2tk_core_save(d2tk_core_t *core)
908 {
909 	_d2tk_append_simple(core, D2TK_INSTR_SAVE);
910 }
911 
912 D2TK_API void
d2tk_core_restore(d2tk_core_t * core)913 d2tk_core_restore(d2tk_core_t *core)
914 {
915 	_d2tk_append_simple(core, D2TK_INSTR_RESTORE);
916 }
917 
918 D2TK_API void
d2tk_core_rotate(d2tk_core_t * core,d2tk_coord_t deg)919 d2tk_core_rotate(d2tk_core_t *core, d2tk_coord_t deg)
920 {
921 	const size_t len = sizeof(d2tk_body_rotate_t);
922 	d2tk_body_t *body = _d2tk_append_request(core, len, D2TK_INSTR_ROTATE);
923 
924 	if(body)
925 	{
926 		body->rotate.deg = deg;
927 
928 		_d2tk_append_advance(core, len);
929 	}
930 }
931 
932 D2TK_API d2tk_widget_t *
d2tk_core_widget_begin(d2tk_core_t * core,uint64_t hash,d2tk_widget_t * widget)933 d2tk_core_widget_begin(d2tk_core_t *core, uint64_t hash, d2tk_widget_t *widget)
934 {
935 	widget->body = _d2tk_core_get_memcache(core, hash);
936 
937 	if(!widget->body) // out-of-memory
938 	{
939 		return NULL;
940 	}
941 
942 	if(*widget->body) // bluntly use cached widget instruction buffer
943 	{
944 		d2tk_mem_t *mem = &core->mem[core->curmem];
945 		const d2tk_widget_body_t *body = (const d2tk_widget_body_t *)*widget->body;
946 		uint8_t *dst = _d2tk_mem_append_request(mem, body->size);
947 
948 		if(dst)
949 		{
950 			memcpy(dst, body->buf, body->size);
951 			_d2tk_mem_append_advance(mem, body->size);
952 		}
953 
954 		widget->ref = 0;
955 
956 		return NULL;
957 	}
958 
959 	// store current offset of instruction buffer
960 	d2tk_mem_t *mem = &core->mem[core->curmem];
961 	const size_t ref = mem->offset;
962 
963 	widget->ref = ref;
964 
965 	return widget;
966 }
967 
968 D2TK_API bool
d2tk_core_widget_not_end(d2tk_core_t * core,d2tk_widget_t * widget)969 d2tk_core_widget_not_end(d2tk_core_t *core __attribute__((unused)),
970 	d2tk_widget_t *widget)
971 {
972 	return widget ? true : false;
973 }
974 
975 D2TK_API d2tk_widget_t *
d2tk_core_widget_next(d2tk_core_t * core,d2tk_widget_t * widget)976 d2tk_core_widget_next(d2tk_core_t *core, d2tk_widget_t *widget)
977 {
978 	d2tk_mem_t *mem = &core->mem[core->curmem];
979 	const size_t ref = mem->offset;
980 
981 	const size_t buf_sz = ref - widget->ref;
982 	const size_t body_sz = sizeof(d2tk_widget_body_t) + buf_sz;
983 	d2tk_widget_body_t *body = malloc(body_sz);
984 
985 	// copy widget instruction buffer to cache
986 	if(body)
987 	{
988 		body->size = buf_sz;
989 		memcpy(body->buf, &mem->buf[widget->ref], buf_sz);
990 
991 		// actually store in cache
992 		*widget->body = (uintptr_t)body;
993 	}
994 
995 	return NULL;
996 }
997 
998 static inline ssize_t
_d2tk_core_bbox_push(d2tk_core_t * core,bool cached,bool container,const d2tk_rect_t * rect)999 _d2tk_core_bbox_push(d2tk_core_t *core, bool cached, bool container,
1000 	const d2tk_rect_t *rect)
1001 {
1002 	d2tk_mem_t *mem = &core->mem[core->curmem];
1003 	const size_t ref = mem->offset;
1004 	const size_t len = sizeof(d2tk_body_bbox_t);
1005 	d2tk_body_t *body = _d2tk_append_request(core, len, D2TK_INSTR_BBOX);
1006 
1007 	if(body)
1008 	{
1009 		body->bbox.hash = 0;
1010 		body->bbox.cached = cached;
1011 		body->bbox.container = container;
1012 		body->bbox.dirty = false;
1013 		body->bbox.clip.x0 = rect->x;
1014 		body->bbox.clip.y0 = rect->y;
1015 		body->bbox.clip.x1 = rect->x + rect->w;
1016 		body->bbox.clip.y1 = rect->y + rect->h;
1017 		body->bbox.clip.w = rect->w;
1018 		body->bbox.clip.h = rect->h;
1019 
1020 		core->ref.x = rect->x;
1021 		core->ref.y = rect->y;
1022 
1023 		_d2tk_append_advance(core, len);
1024 		return ref;
1025 	}
1026 
1027 	return -1;
1028 }
1029 
1030 D2TK_API ssize_t
d2tk_core_bbox_push(d2tk_core_t * core,bool cached,const d2tk_rect_t * rect)1031 d2tk_core_bbox_push(d2tk_core_t *core, bool cached, const d2tk_rect_t *rect)
1032 {
1033 	return _d2tk_core_bbox_push(core, cached, false, rect);
1034 }
1035 
1036 D2TK_API ssize_t
d2tk_core_bbox_container_push(d2tk_core_t * core,bool cached,const d2tk_rect_t * rect)1037 d2tk_core_bbox_container_push(d2tk_core_t *core, bool cached,
1038 	const d2tk_rect_t *rect)
1039 {
1040 	return _d2tk_core_bbox_push(core, cached, true, rect);
1041 }
1042 
1043 D2TK_API void
d2tk_core_bbox_pop(d2tk_core_t * core,ssize_t ref)1044 d2tk_core_bbox_pop(d2tk_core_t *core, ssize_t ref)
1045 {
1046 	d2tk_mem_t *mem = &core->mem[core->curmem];
1047 	d2tk_com_t *com = (d2tk_com_t *)&mem->buf[ref];
1048 	const size_t len = mem->offset - ref;
1049 
1050 	com->size = len - sizeof(d2tk_com_t);
1051 	// hash over instructions exclusive position
1052 	com->body->bbox.hash = d2tk_hash(&com->body->bbox.clip.w,
1053 		len - offsetof(d2tk_clip_t, w));
1054 
1055 	core->ref.x = 0;
1056 	core->ref.y = 0;
1057 }
1058 
1059 D2TK_API void
d2tk_core_begin_path(d2tk_core_t * core)1060 d2tk_core_begin_path(d2tk_core_t *core)
1061 {
1062 	_d2tk_append_simple(core, D2TK_INSTR_BEGIN_PATH);
1063 }
1064 
1065 D2TK_API void
d2tk_core_close_path(d2tk_core_t * core)1066 d2tk_core_close_path(d2tk_core_t *core)
1067 {
1068 	_d2tk_append_simple(core, D2TK_INSTR_CLOSE_PATH);
1069 }
1070 
1071 D2TK_API void
d2tk_core_scissor(d2tk_core_t * core,const d2tk_rect_t * rect)1072 d2tk_core_scissor(d2tk_core_t *core, const d2tk_rect_t *rect)
1073 {
1074 	const size_t len = sizeof(d2tk_body_scissor_t);
1075 	d2tk_body_t *body = _d2tk_append_request(core, len, D2TK_INSTR_SCISSOR);
1076 
1077 	if(body)
1078 	{
1079 		body->scissor.x = rect->x;
1080 		body->scissor.y = rect->y;
1081 		body->scissor.w = rect->w;
1082 		body->scissor.h = rect->h;
1083 
1084 		body->scissor.x -= core->ref.x;
1085 		body->scissor.y -= core->ref.y;
1086 
1087 		_d2tk_append_advance(core, len);
1088 	}
1089 }
1090 
1091 D2TK_API void
d2tk_core_reset_scissor(d2tk_core_t * core)1092 d2tk_core_reset_scissor(d2tk_core_t *core)
1093 {
1094 	_d2tk_append_simple(core, D2TK_INSTR_RESET_SCISSOR);
1095 }
1096 
1097 D2TK_API void
d2tk_core_font_size(d2tk_core_t * core,d2tk_coord_t size)1098 d2tk_core_font_size(d2tk_core_t *core, d2tk_coord_t size)
1099 {
1100 	const size_t len = sizeof(d2tk_body_font_size_t);
1101 	d2tk_body_t *body = _d2tk_append_request(core, len, D2TK_INSTR_FONT_SIZE);
1102 
1103 	if(body)
1104 	{
1105 		body->font_size.size = size;
1106 
1107 		_d2tk_append_advance(core, len);
1108 	}
1109 }
1110 
1111 D2TK_API void
d2tk_core_font_face(d2tk_core_t * core,size_t sz,const char * face)1112 d2tk_core_font_face(d2tk_core_t *core, size_t sz, const char *face)
1113 {
1114 	const size_t len = sizeof(d2tk_body_font_face_t) + sz;
1115 	d2tk_body_t *body = _d2tk_append_request(core, len, D2TK_INSTR_FONT_FACE);
1116 
1117 	if(body)
1118 	{
1119 		memcpy(body->font_face.face, face, sz);
1120 		body->font_face.face[sz] = '\0';
1121 
1122 		_d2tk_append_advance(core, len);
1123 	}
1124 }
1125 
1126 D2TK_API void
d2tk_core_text(d2tk_core_t * core,const d2tk_rect_t * rect,size_t sz,const char * text,d2tk_align_t align)1127 d2tk_core_text(d2tk_core_t *core, const d2tk_rect_t *rect, size_t sz,
1128 	const char *text, d2tk_align_t align)
1129 {
1130 	const size_t len = sizeof(d2tk_body_text_t) + sz;
1131 	d2tk_body_t *body = _d2tk_append_request(core, len, D2TK_INSTR_TEXT);
1132 
1133 	if(body)
1134 	{
1135 		body->text.x = rect->x;
1136 		body->text.y = rect->y;
1137 		body->text.w = rect->w;
1138 		body->text.h = rect->h;
1139 		body->text.align = align;
1140 		memcpy(body->text.text, text, sz);
1141 		body->text.text[sz] = '\0';
1142 
1143 		body->text.x -= core->ref.x;
1144 		body->text.y -= core->ref.y;
1145 
1146 		_d2tk_append_advance(core, len);
1147 	}
1148 }
1149 
1150 D2TK_API void
d2tk_core_image(d2tk_core_t * core,const d2tk_rect_t * rect,size_t sz,const char * path,d2tk_align_t align)1151 d2tk_core_image(d2tk_core_t *core, const d2tk_rect_t *rect, size_t sz,
1152 	const char *path, d2tk_align_t align)
1153 {
1154 	const size_t len = sizeof(d2tk_body_image_t) + sz;
1155 	d2tk_body_t *body = _d2tk_append_request(core, len, D2TK_INSTR_IMAGE);
1156 
1157 	if(body)
1158 	{
1159 		body->image.x = rect->x;
1160 		body->image.y = rect->y;
1161 		body->image.w = rect->w;
1162 		body->image.h = rect->h;
1163 		body->image.align = align;
1164 		memcpy(body->image.path, path, sz);
1165 		body->image.path[sz] = '\0';
1166 
1167 		body->image.x -= core->ref.x;
1168 		body->image.y -= core->ref.y;
1169 
1170 		_d2tk_append_advance(core, len);
1171 	}
1172 }
1173 
1174 D2TK_API void
d2tk_core_bitmap(d2tk_core_t * core,const d2tk_rect_t * rect,uint32_t w,uint32_t h,uint32_t stride,const uint32_t * argb,uint64_t rev,d2tk_align_t align)1175 d2tk_core_bitmap(d2tk_core_t *core, const d2tk_rect_t *rect, uint32_t w,
1176 	uint32_t h, uint32_t stride, const uint32_t *argb, uint64_t rev,
1177 	d2tk_align_t align)
1178 {
1179 	const size_t len = sizeof(d2tk_body_bitmap_t);
1180 	d2tk_body_t *body = _d2tk_append_request(core, len, D2TK_INSTR_BITMAP);
1181 
1182 	if(body)
1183 	{
1184 		body->bitmap.x = rect->x;
1185 		body->bitmap.y = rect->y;
1186 		body->bitmap.w = rect->w;
1187 		body->bitmap.h = rect->h;
1188 		body->bitmap.align = align;
1189 		body->bitmap.surf.w = w;
1190 		body->bitmap.surf.h = h;
1191 		body->bitmap.surf.stride = stride;
1192 		body->bitmap.surf.argb = argb;
1193 		body->bitmap.surf.rev = rev;
1194 
1195 		body->bitmap.x -= core->ref.x;
1196 		body->bitmap.y -= core->ref.y;
1197 
1198 		_d2tk_append_advance(core, len);
1199 	}
1200 }
1201 
1202 D2TK_API void
d2tk_core_custom(d2tk_core_t * core,const d2tk_rect_t * rect,uint64_t dhash,const void * data,d2tk_core_custom_t custom)1203 d2tk_core_custom(d2tk_core_t *core, const d2tk_rect_t *rect, uint64_t dhash,
1204 	const void *data, d2tk_core_custom_t custom)
1205 {
1206 	const size_t len = sizeof(d2tk_body_custom_t);
1207 	d2tk_body_t *body = _d2tk_append_request(core, len, D2TK_INSTR_CUSTOM);
1208 
1209 	if(body)
1210 	{
1211 		body->custom.x = rect->x;
1212 		body->custom.y = rect->y;
1213 		body->custom.w = rect->w;
1214 		body->custom.h = rect->h;
1215 		body->custom.dhash = dhash;
1216 		body->custom.data = data;
1217 		body->custom.custom = custom;
1218 
1219 		body->custom.x -= core->ref.x;
1220 		body->custom.y -= core->ref.y;
1221 
1222 		_d2tk_append_advance(core, len);
1223 	}
1224 }
1225 
1226 D2TK_API void
d2tk_core_stroke_width(d2tk_core_t * core,d2tk_coord_t width)1227 d2tk_core_stroke_width(d2tk_core_t *core, d2tk_coord_t width)
1228 {
1229 	const size_t len = sizeof(d2tk_body_stroke_width_t);
1230 	d2tk_body_t *body = _d2tk_append_request(core, len, D2TK_INSTR_STROKE_WIDTH);
1231 
1232 	if(body)
1233 	{
1234 		body->stroke_width.width = width;
1235 
1236 		_d2tk_append_advance(core, len);
1237 	}
1238 }
1239 
1240 D2TK_API int
d2tk_core_pre(d2tk_core_t * core,void * pctx)1241 d2tk_core_pre(d2tk_core_t *core, void *pctx)
1242 {
1243 	d2tk_mem_t *curmem = &core->mem[core->curmem];
1244 
1245 	_d2tk_mem_reset(curmem);
1246 
1247 	core->parent = d2tk_core_bbox_container_push(core, 0,
1248 		&D2TK_RECT(0, 0, core->w, core->h));
1249 
1250 	return core->driver->context(core->data, pctx);
1251 }
1252 
1253 static inline bool
_d2tk_com_equal_container(const d2tk_com_t * curcom,const d2tk_com_t * oldcom)1254 _d2tk_com_equal_container(const d2tk_com_t *curcom, const d2tk_com_t *oldcom)
1255 {
1256 	const d2tk_body_bbox_t *curbbox = &curcom->body->bbox;
1257 	const d2tk_body_bbox_t *oldbbox = &oldcom->body->bbox;
1258 
1259 	if(  (curcom->instr == oldcom->instr)
1260 		&& (curbbox->clip.x0 == oldbbox->clip.x0)
1261 		&& (curbbox->clip.y0 == oldbbox->clip.y0) )
1262 	{
1263 		if(  (curcom->size == oldcom->size)
1264 			&& (curbbox->hash == oldbbox->hash) )
1265 		{
1266 			return true;
1267 		}
1268 		else if(curbbox->container && oldbbox->container)
1269 		{
1270 			return true;
1271 		}
1272 	}
1273 
1274 	return false;
1275 }
1276 
1277 static inline bool
_d2tk_com_equal(const d2tk_com_t * curcom,const d2tk_com_t * oldcom)1278 _d2tk_com_equal(const d2tk_com_t *curcom, const d2tk_com_t *oldcom)
1279 {
1280 	const d2tk_body_bbox_t *curbbox = &curcom->body->bbox;
1281 	const d2tk_body_bbox_t *oldbbox = &oldcom->body->bbox;
1282 
1283 	if(  (curcom->instr == oldcom->instr)
1284 		&& (curbbox->clip.x0 == oldbbox->clip.x0)
1285 		&& (curbbox->clip.y0 == oldbbox->clip.y0)
1286 		&& (curcom->size == oldcom->size)
1287 		&& (curbbox->hash == oldbbox->hash) )
1288 	{
1289 		return true;
1290 	}
1291 
1292 	return false;
1293 }
1294 
1295 static inline void
_d2tk_diff(d2tk_core_t * core,d2tk_com_t * curcom_ref,d2tk_com_t * oldcom_ref)1296 _d2tk_diff(d2tk_core_t *core, d2tk_com_t *curcom_ref, d2tk_com_t *oldcom_ref)
1297 {
1298 	// look for (dis)appeared instructions
1299 	d2tk_com_t *tmpcom = _d2tk_com_begin(curcom_ref);
1300 
1301 	D2TK_COM_FOREACH(oldcom_ref, oldcom)
1302 	{
1303 		if(oldcom->instr != D2TK_INSTR_BBOX)
1304 		{
1305 			continue;
1306 		}
1307 
1308 		bool match = false;
1309 
1310 		D2TK_COM_FOREACH_FROM(curcom_ref, tmpcom, curcom)
1311 		{
1312 			if(curcom->instr != D2TK_INSTR_BBOX)
1313 			{
1314 				continue;
1315 			}
1316 
1317 			// check for matching size, instruction, hash and position
1318 			if(_d2tk_com_equal_container(curcom, oldcom))
1319 			{
1320 				for(d2tk_com_t *curcom2 = tmpcom;
1321 					curcom2 != curcom;
1322 					curcom2 = _d2tk_com_next(curcom2))
1323 				{
1324 					if(curcom2->instr != D2TK_INSTR_BBOX)
1325 					{
1326 						continue;
1327 					}
1328 
1329 #if D2TK_DEBUG
1330 					d2tk_body_bbox_t *curbbox2 = &curcom2->body->bbox;
1331 
1332 					fprintf(stderr,
1333 						"\t   appeared (%i %i %i %i %i %i 0x%08"PRIx32")\n",
1334 						curbbox2->clip.x0, curbbox2->clip.y0,
1335 						curbbox2->clip.x1, curbbox2->clip.y1,
1336 						curcom2->size, curcom2->instr,
1337 						curbbox2->hash);
1338 #endif
1339 
1340 					_d2tk_bbox_mask(core, curcom2);
1341 				}
1342 
1343 				if(curcom->body->bbox.container && oldcom->body->bbox.container)
1344 				{
1345 #if D2TK_DEBUG
1346 					fprintf(stderr, "\t   comparing nested containers\n");
1347 #endif
1348 					_d2tk_diff(core, curcom, oldcom);
1349 				}
1350 
1351 				match = true;
1352 				tmpcom = _d2tk_com_next(curcom);
1353 
1354 				break;
1355 			}
1356 		}
1357 
1358 		if(!match)
1359 		{
1360 #if D2TK_DEBUG
1361 			d2tk_body_bbox_t *oldbbox = &oldcom->body->bbox;
1362 
1363 			fprintf(stderr,
1364 				"\tdisappeared (%i %i %i %i %i %i 0x%08"PRIx32")\n",
1365 				oldbbox->clip.x0, oldbbox->clip.y0,
1366 				oldbbox->clip.x1, oldbbox->clip.y1,
1367 				oldcom->size, oldcom->instr,
1368 				oldbbox->hash);
1369 #endif
1370 
1371 			_d2tk_bbox_mask(core, oldcom);
1372 		}
1373 	}
1374 
1375 	D2TK_COM_FOREACH_FROM(curcom_ref, tmpcom, curcom2)
1376 	{
1377 		if(curcom2->instr != D2TK_INSTR_BBOX)
1378 		{
1379 			continue;
1380 		}
1381 
1382 #if D2TK_DEBUG
1383 		d2tk_body_bbox_t *curbbox2 = &curcom2->body->bbox;
1384 
1385 		fprintf(stderr,
1386 			"\t   appeared (%i %i %i %i %i %i 0x%08"PRIx32")\n",
1387 			curbbox2->clip.x0, curbbox2->clip.y0,
1388 			curbbox2->clip.x1, curbbox2->clip.y1,
1389 			curcom2->size, curcom2->instr,
1390 			curbbox2->hash);
1391 #endif
1392 
1393 		_d2tk_bbox_mask(core, curcom2);
1394 	}
1395 }
1396 
1397 D2TK_API void
d2tk_core_post(d2tk_core_t * core)1398 d2tk_core_post(d2tk_core_t *core)
1399 {
1400 	d2tk_mem_t *oldmem = &core->mem[!core->curmem];
1401 	d2tk_mem_t *curmem = &core->mem[core->curmem];
1402 	d2tk_bitmap_t *bitmap = &core->bitmap;
1403 
1404 	d2tk_core_bbox_pop(core, core->parent);
1405 
1406 	_d2tk_mem_compact(curmem);
1407 
1408 	d2tk_com_t *curcom = _d2tk_mem_get_com(curmem);
1409 	d2tk_com_t *oldcom = _d2tk_mem_get_com(oldmem);
1410 
1411 	// reset num of clipping clips
1412 	_d2tk_bitmap_reset(core);
1413 
1414 	if(core->full_refresh)
1415 	{
1416 #if D2TK_DEBUG
1417 		fprintf(stderr,
1418 			"\tfull_refresh (%"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32")\n",
1419 			0, 0, core->w, core->h);
1420 #endif
1421 
1422 		_d2tk_sprites_free(core);
1423 		_d2tk_memcaches_free(core);
1424 	}
1425 	else if(!_d2tk_com_equal(curcom, oldcom))
1426 	{
1427 		_d2tk_diff(core, curcom, oldcom);
1428 	}
1429 
1430 	if(bitmap->nfills || core->full_refresh)
1431 	{
1432 		const d2tk_clip_t *aoi = NULL;
1433 
1434 		if(core->full_refresh)
1435 		{
1436 			d2tk_clip_t tmp;
1437 
1438 			tmp.x0 = 0;
1439 			tmp.y0 = 0;
1440 			tmp.x1 = core->w;
1441 			tmp.y1 = core->h;
1442 			tmp.w = core->w;
1443 			tmp.h = core->h;
1444 
1445 			_d2tk_bitmap_fill(core, &tmp);
1446 		}
1447 		else
1448 		{
1449 			static d2tk_clip_t tmp;
1450 
1451 			tmp.x0 = bitmap->x0;
1452 			tmp.y0 = bitmap->y0;
1453 			tmp.x1 = bitmap->x1;
1454 			tmp.y1 = bitmap->y1;
1455 			tmp.w = bitmap->x1 - bitmap->x0;
1456 			tmp.h = bitmap->y1 - bitmap->y0;
1457 
1458 			aoi = &tmp;
1459 		}
1460 
1461 #if D2TK_DEBUG
1462 		fprintf(stderr, "\tnfills: %zu\n", bitmap->nfills);
1463 #endif
1464 		for(unsigned pass = 0; pass < 2; pass++)
1465 		{
1466 			core->driver->pre(core->data, core, core->w, core->h, pass);
1467 
1468 			d2tk_com_t *curcom = _d2tk_mem_get_com(curmem);
1469 
1470 			D2TK_COM_FOREACH(curcom, com)
1471 			{
1472 				d2tk_body_bbox_t *body = &com->body->bbox;
1473 
1474 				if(pass == 0)
1475 				{
1476 					if(aoi && !body->dirty)
1477 					{
1478 						if(  ( (body->clip.x0 >= aoi->x1) || (aoi->x0 >= body->clip.x1) )
1479 							|| ( (body->clip.y0 >= aoi->y1) || (aoi->y0 >= body->clip.y1) ) )
1480 						{
1481 							continue; // not in area-of-interest
1482 						}
1483 
1484 						if(!_d2tk_bitmap_query(core, body))
1485 						{
1486 							continue;
1487 						}
1488 					}
1489 				}
1490 				else if(pass == 1)
1491 				{
1492 					if(aoi && !body->dirty)
1493 					{
1494 						continue; // not in area-of-interest
1495 					}
1496 				}
1497 
1498 				const d2tk_clip_t *clip = NULL;
1499 
1500 				if(aoi)
1501 				{
1502 					static d2tk_clip_t tmp;
1503 
1504 					// derive minimal intersecting rectangle
1505 					tmp.x0 = aoi->x0 < body->clip.x0
1506 						? body->clip.x0
1507 						: aoi->x0;
1508 					tmp.x1 = aoi->x1 > body->clip.x1
1509 						? body->clip.x1
1510 						: aoi->x1;
1511 					tmp.y0 = aoi->y0 < body->clip.y0
1512 						? body->clip.y0
1513 						: aoi->y0;
1514 					tmp.y1 = aoi->y1 > body->clip.y1
1515 						? body->clip.y1
1516 						: aoi->y1;
1517 					tmp.w = tmp.x1 - tmp.x0;
1518 					tmp.h = tmp.y1 - tmp.y0;
1519 
1520 #if 0 // seems not to be needed
1521 					if(pass == 0)
1522 					{
1523 						_d2tk_bitmap_fill(core, &tmp);
1524 					}
1525 #endif
1526 
1527 					clip = &tmp;
1528 				}
1529 
1530 				/*
1531 				fprintf(stderr, "=%li:%p= %i %i %i %i\n", i, (void *)body,
1532 					body->clip.x0, body->clip.y0, body->clip.w, body->clip.h);
1533 				if(clip)
1534 				{
1535 					fprintf(stderr, ":%li:%p: %i %i %i %i\n", i, (void *)body,
1536 						clip->x0, clip->y0, clip->x1 - clip->x0, clip->y1 - clip->y0);
1537 				}
1538 				*/
1539 
1540 				core->driver->process(core->data, core, com, body->clip.x0,
1541 					body->clip.y0, clip, pass);
1542 			}
1543 
1544 			if(!core->driver->post(core->data, core, core->w, core->h, pass))
1545 			{
1546 				break; // does NOT need 2nd pass
1547 			}
1548 		}
1549 	}
1550 
1551 	core->driver->end(core->data, core, core->w, core->h);
1552 
1553 	_d2tk_sprites_gc(core);
1554 	_d2tk_memcaches_gc(core);
1555 
1556 	core->full_refresh = false;
1557 	core->curmem = !core->curmem;
1558 }
1559 
1560 D2TK_API d2tk_core_t *
d2tk_core_new(const d2tk_core_driver_t * driver,void * data)1561 d2tk_core_new(const d2tk_core_driver_t *driver, void *data)
1562 {
1563 	d2tk_core_t *core = calloc(1, sizeof(d2tk_core_t));
1564 	if(!core)
1565 	{
1566 		return NULL;
1567 	}
1568 
1569 	core->driver = driver;
1570 	core->data = data;
1571 
1572 	_d2tk_mem_init(&core->mem[0], 8192);
1573 	_d2tk_mem_init(&core->mem[1], 8192);
1574 
1575 	{
1576 		core->curmem = 0;
1577 
1578 		_d2tk_mem_reset(&core->mem[core->curmem]);
1579 		const ssize_t ref = d2tk_core_bbox_container_push(core, 0,
1580 			&D2TK_RECT(0, 0, core->w, core->h));
1581 		d2tk_core_bbox_pop(core, ref);
1582 	}
1583 
1584 	{
1585 		core->curmem = 1;
1586 
1587 		_d2tk_mem_reset(&core->mem[core->curmem]);
1588 		const ssize_t ref = d2tk_core_bbox_container_push(core, 0,
1589 			&D2TK_RECT(0, 0, core->w, core->h));
1590 		d2tk_core_bbox_pop(core, ref);
1591 	}
1592 
1593 	core->curmem = 0;
1594 
1595 	core->ttl.sprites = _D2TK_SPRITES_TTL;
1596 	core->ttl.memcaches = _D2TK_MEMCACHES_TTL;
1597 
1598 	return core;
1599 }
1600 
1601 D2TK_API void
d2tk_core_set_ttls(d2tk_core_t * core,uint32_t sprites,uint32_t memcaches)1602 d2tk_core_set_ttls(d2tk_core_t *core, uint32_t sprites, uint32_t memcaches)
1603 {
1604 	core->ttl.sprites  = sprites;
1605 	core->ttl.memcaches = memcaches;
1606 }
1607 
1608 D2TK_API void
d2tk_core_free(d2tk_core_t * core)1609 d2tk_core_free(d2tk_core_t *core)
1610 {
1611 	_d2tk_mem_deinit(&core->mem[0]);
1612 	_d2tk_mem_deinit(&core->mem[1]);
1613 	_d2tk_bitmap_deinit(&core->bitmap);
1614 	_d2tk_sprites_free(core);
1615 	_d2tk_memcaches_free(core);
1616 
1617 	free(core);
1618 }
1619 
1620 D2TK_API void
d2tk_core_set_dimensions(d2tk_core_t * core,d2tk_coord_t w,d2tk_coord_t h)1621 d2tk_core_set_dimensions(d2tk_core_t *core, d2tk_coord_t w, d2tk_coord_t h)
1622 {
1623 	core->w = w;
1624 	core->h = h;
1625 	d2tk_core_set_full_refresh(core);
1626 	_d2tk_bitmap_resize(core, w, h);
1627 }
1628 
1629 D2TK_API void
d2tk_core_get_dimensions(d2tk_core_t * core,d2tk_coord_t * w,d2tk_coord_t * h)1630 d2tk_core_get_dimensions(d2tk_core_t *core, d2tk_coord_t *w, d2tk_coord_t *h)
1631 {
1632 	if(w)
1633 	{
1634 		*w = core->w;
1635 	}
1636 
1637 	if(h)
1638 	{
1639 		*h = core->h;
1640 	}
1641 }
1642 
1643 D2TK_API void
d2tk_core_set_full_refresh(d2tk_core_t * core)1644 d2tk_core_set_full_refresh(d2tk_core_t *core)
1645 {
1646 	core->full_refresh = true;
1647 }
1648 
1649 int
d2tk_core_get_font_path(d2tk_core_t * core,const char * bundle_path,const char * rel_path,size_t abs_len,char * abs_path)1650 d2tk_core_get_font_path(d2tk_core_t *core, const char *bundle_path,
1651 	const char *rel_path, size_t abs_len, char *abs_path)
1652 {
1653 	int ret = 1;
1654 
1655 #if D2TK_FONTCONFIG
1656 	(void)core;
1657 	(void)bundle_path;
1658 	FcChar8 pattern [PATH_MAX];
1659 
1660 	snprintf((char *)pattern, sizeof(pattern), "%s:fontformat=TrueType", rel_path);
1661 
1662 	FcConfig *config = FcInitLoadConfigAndFonts();
1663 	FcPattern *pat = FcNameParse(pattern);
1664 	FcConfigSubstitute(config, pat, FcMatchPattern);
1665 	FcDefaultSubstitute(pat);
1666 
1667 	//FcPatternPrint(pat);
1668 
1669 	FcResult result;
1670 	FcPattern *font = FcFontMatch(config, pat, &result);
1671 	if(font)
1672 	{
1673 		FcChar8 *file = NULL;
1674 		if(FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch)
1675 		{
1676 			snprintf(abs_path, abs_len, "%s", file);
1677 			ret = 0;
1678 		}
1679 
1680 		FcPatternDestroy(font);
1681 	}
1682 
1683 	FcPatternDestroy(pat);
1684 
1685 	FcConfigDestroy(config);
1686 #else
1687 	(void)core;
1688 	snprintf(abs_path, abs_len, "%s%s.ttf", bundle_path, rel_path);
1689 	ret = 0;
1690 #endif
1691 
1692 	return ret;
1693 }
1694 
1695 D2TK_API int
d2tk_core_text_extent(d2tk_core_t * core,size_t len,const char * buf,d2tk_coord_t h)1696 d2tk_core_text_extent(d2tk_core_t *core, size_t len, const char *buf,
1697 	d2tk_coord_t h)
1698 {
1699 	return core->driver->text_extent(core->data, len, buf, h);
1700 }
1701