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