1 /*
2 * Copyright 2009-2021 Peter Kosyh <p.kosyh at gmail.com>
3 *
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 *
23 */
24
25 #include "externals.h"
26 #include "internals.h"
27
28 #include <SDL.h>
29 #include <SDL_image.h>
30 #include <SDL_ttf.h>
31
32 #ifdef _USE_HARFBUZZ
33 #include <hb.h>
34 #include <glib.h>
35 #endif
36
37 #include <SDL_mutex.h>
38 #if SDL_VERSION_ATLEAST(2,0,0)
39 #include "SDL2_rotozoom.h"
40 #define SDL_SRCALPHA 0
41 #else
42 #include "SDL_rotozoom.h"
43 #endif
44 #include "SDL_gfxBlitFunc.h"
45 #include "SDL_gif.h"
46
47 #define IMG_ANIM 1
48 struct _img_t {
49 SDL_Surface *s;
50 /* SDL_Texture *t; */
51 int flags;
52 void *aux;
53 };
54 #define Surf(p) (((p)?(((img_t)(p))->s):NULL))
55
56 #ifndef SDL_malloc
57 #define SDL_malloc malloc
58 #endif
59
60 #ifndef SDL_free
61 #define SDL_free free
62 #endif
63
64 static img_t screen = NULL;
65 static cache_t images = NULL;
66
67 static struct {
68 const char *name;
69 unsigned long val;
70 } cnames[] = {
71 {"aliceblue", 0xf0f8ff},
72 {"antiquewhite", 0xfaebd7},
73 {"aqua", 0x00ffff},
74 {"aquamarine", 0x7fffd4},
75 {"azure", 0xf0ffff},
76 {"beige", 0xf5f5dc},
77 {"bisque", 0xffe4c4},
78 {"black", 0x000000},
79 {"blanchedalmond", 0xffebcd},
80 {"blue", 0x0000ff},
81 {"blueviolet", 0x8a2be2},
82 {"brown", 0xa52a2a},
83 {"burlywood", 0xdeb887},
84 {"cadetblue", 0x5f9ea0},
85 {"chartreuse", 0x7fff00},
86 {"chocolate", 0xd2691e},
87 {"coral", 0xff7f50},
88 {"cornflowerblue", 0x6495ed},
89 {"cornsilk", 0xfff8dc},
90 {"crimson", 0xdc143c},
91 {"cyan", 0x00ffff},
92 {"darkblue", 0x00008b},
93 {"darkcyan", 0x008b8b},
94 {"darkgoldenrod", 0xb8860b},
95 {"darkgray", 0xa9a9a9},
96 {"darkgrey", 0xa9a9a9},
97 {"darkgreen", 0x006400},
98 {"darkkhaki", 0xbdb76b},
99 {"darkmagenta", 0x8b008b},
100 {"darkolivegreen", 0x556b2f},
101 {"darkorange", 0xff8c00},
102 {"darkorchid", 0x9932cc},
103 {"darkred", 0x8b0000},
104 {"darksalmon", 0xe9967a},
105 {"darkseagreen", 0x8fbc8f},
106 {"darkslateblue", 0x483d8b},
107 {"darkslategray", 0x2f4f4f},
108 {"darkslategrey", 0x2f4f4f},
109 {"darkturquoise", 0x00ced1},
110 {"darkviolet", 0x9400d3},
111 {"deeppink", 0xff1493},
112 {"deepskyblue", 0x00bfff},
113 {"dimgray", 0x696969},
114 {"dimgrey", 0x696969},
115 {"dodgerblue", 0x1e90ff},
116 {"feldspar", 0xd19275},
117 {"firebrick", 0xb22222},
118 {"floralwhite", 0xfffaf0},
119 {"forestgreen", 0x228b22},
120 {"fuchsia", 0xff00ff},
121 {"gainsboro", 0xdcdcdc},
122 {"ghostwhite", 0xf8f8ff},
123 {"gold", 0xffd700},
124 {"goldenrod", 0xdaa520},
125 {"gray", 0x808080},
126 {"grey", 0x808080},
127 {"green", 0x008000},
128 {"greenyellow", 0xadff2f},
129 {"honeydew", 0xf0fff0},
130 {"hotpink", 0xff69b4},
131 {"indianred", 0xcd5c5c},
132 {"indigo", 0x4b0082},
133 {"ivory", 0xfffff0},
134 {"khaki", 0xf0e68c},
135 {"lavender", 0xe6e6fa},
136 {"lavenderblush", 0xfff0f5},
137 {"lawngreen", 0x7cfc00},
138 {"lemonchiffon", 0xfffacd},
139 {"lightblue", 0xadd8e6},
140 {"lightcoral", 0xf08080},
141 {"lightcyan", 0xe0ffff},
142 {"lightgoldenrodyellow", 0xfafad2},
143 {"lightgray", 0xd3d3d3},
144 {"lightgrey", 0xd3d3d3},
145 {"lightgreen", 0x90ee90},
146 {"lightpink", 0xffb6c1},
147 {"lightsalmon", 0xffa07a},
148 {"lightseagreen", 0x20b2aa},
149 {"lightskyblue", 0x87cefa},
150 {"lightslateblue", 0x8470ff},
151 {"lightslategray", 0x778899},
152 {"lightslategrey", 0x778899},
153 {"lightsteelblue", 0xb0c4de},
154 {"lightyellow", 0xffffe0},
155 {"lime", 0x00ff00},
156 {"limegreen", 0x32cd32},
157 {"linen", 0xfaf0e6},
158 {"magenta", 0xff00ff},
159 {"maroon", 0x800000},
160 {"mediumaquamarine", 0x66cdaa},
161 {"mediumblue", 0x0000cd},
162 {"mediumorchid", 0xba55d3},
163 {"mediumpurple", 0x9370d8},
164 {"mediumseagreen", 0x3cb371},
165 {"mediumslateblue", 0x7b68ee},
166 {"mediumspringgreen", 0x00fa9a},
167 {"mediumturquoise", 0x48d1cc},
168 {"mediumvioletred", 0xc71585},
169 {"midnightblue", 0x191970},
170 {"mintcream", 0xf5fffa},
171 {"mistyrose", 0xffe4e1},
172 {"moccasin", 0xffe4b5},
173 {"navajowhite", 0xffdead},
174 {"navy", 0x000080},
175 {"oldlace", 0xfdf5e6},
176 {"olive", 0x808000},
177 {"olivedrab", 0x6b8e23},
178 {"orange", 0xffa500},
179 {"orangered", 0xff4500},
180 {"orchid", 0xda70d6},
181 {"palegoldenrod", 0xeee8aa},
182 {"palegreen", 0x98fb98},
183 {"paleturquoise", 0xafeeee},
184 {"palevioletred", 0xd87093},
185 {"papayawhip", 0xffefd5},
186 {"peachpuff", 0xffdab9},
187 {"peru", 0xcd853f},
188 {"pink", 0xffc0cb},
189 {"plum", 0xdda0dd},
190 {"powderblue", 0xb0e0e6},
191 {"purple", 0x800080},
192 {"red", 0xff0000},
193 {"rosybrown", 0xbc8f8f},
194 {"royalblue", 0x4169e1},
195 {"saddlebrown", 0x8b4513},
196 {"salmon", 0xfa8072},
197 {"sandybrown", 0xf4a460},
198 {"seagreen", 0x2e8b57},
199 {"seashell", 0xfff5ee},
200 {"sienna", 0xa0522d},
201 {"silver", 0xc0c0c0},
202 {"skyblue", 0x87ceeb},
203 {"slateblue", 0x6a5acd},
204 {"slategray", 0x708090},
205 {"slategrey", 0x708090},
206 {"snow", 0xfffafa},
207 {"springgreen", 0x00ff7f},
208 {"steelblue", 0x4682b4},
209 {"tan", 0xd2b48c},
210 {"teal", 0x008080},
211 {"thistle", 0xd8bfd8},
212 {"tomato", 0xff6347},
213 {"turquoise", 0x40e0d0},
214 {"violet", 0xee82ee},
215 {"violetred", 0xd02090},
216 {"wheat", 0xf5deb3},
217 {"white", 0xffffff},
218 {"whitesmoke", 0xf5f5f5},
219 {"yellow", 0xffff00},
220 {"yellowgreen", 0x9acd32},
221 {NULL, 0x0},
222 };
223
gfx_col(int r,int g,int b)224 color_t gfx_col(int r, int g, int b)
225 {
226 color_t col;
227 col.r = r;
228 col.g = g;
229 col.b = b;
230 col.a = 0;
231 return col;
232 }
233
gfx_parse_color(const char * spec,color_t * def)234 int gfx_parse_color (
235 const char *spec,
236 color_t *def)
237 {
238 int n, i;
239 int r, g, b;
240 char c;
241
242 if (!spec)
243 return -1;
244 spec += strspn(spec, " \t");
245 n = strlen (spec);
246
247 if (*spec == '#') {
248 /*
249 * RGB
250 */
251 spec++;
252 n--;
253 if (n != 3 && n != 6 && n != 9 && n != 12)
254 return -1;
255 n /= 3;
256 g = b = 0;
257 do {
258 r = g;
259 g = b;
260 b = 0;
261 for (i = n; --i >= 0; ) {
262 c = *spec++;
263 b <<= 4;
264 if (c >= '0' && c <= '9')
265 b |= c - '0';
266 else if (c >= 'A' && c <= 'F')
267 b |= c - ('A' - 10);
268 else if (c >= 'a' && c <= 'f')
269 b |= c - ('a' - 10);
270 else return (0);
271 }
272 } while (*spec != '\0');
273 if (def) {
274 def->r = r;
275 def->g = g;
276 def->b = b;
277 }
278 return 0;
279 }
280
281 for (i=0; cnames[i].name; i++) {
282 if (!strcmp(cnames[i].name, spec)) {
283 if (def) {
284 def->r = (cnames[i].val & 0xff0000) >> 16;
285 def->g = (cnames[i].val & 0x00ff00) >> 8;
286 def->b = (cnames[i].val & 0x0000ff);
287 }
288 return 0;
289 }
290 }
291 return -1;
292 }
293
294 struct _anim_t;
295
296 struct anspawn {
297 SDL_Rect clip;
298 img_t bg;
299 int x;
300 int y;
301 };
302
303 #define ANSPAWN_BLOCK 8
304 struct _anim_t {
305 struct _anim_t *next;
306 struct _anim_t *prev;
307 int cur_frame;
308 int nr_frames;
309 int loop;
310 int drawn;
311 int active;
312 unsigned long delay;
313 int spawn_nr;
314 struct anspawn *spawn;
315 Animation_t *anim;
316 };
317
318 typedef struct _anim_t *anim_t;
319
anim_spawn(anim_t ag,int x,int y,int w,int h)320 static int anim_spawn(anim_t ag, int x, int y, int w, int h)
321 {
322 int nr;
323 SDL_Rect clip;
324 SDL_GetClipRect(Surf(screen), &clip);
325 /* gfx_free_image(ag->bg); */
326 if (!ag->spawn && !(ag->spawn = malloc(ANSPAWN_BLOCK * sizeof(struct anspawn))))
327 return -1;
328 nr = ag->spawn_nr + 1;
329 if (!(nr % ANSPAWN_BLOCK)) { /* grow */
330 void *p = realloc(ag->spawn, ANSPAWN_BLOCK * sizeof(struct anspawn) *
331 ((nr / ANSPAWN_BLOCK) + 1));
332 if (!p)
333 return -1;
334 ag->spawn = p;
335 }
336 ag->spawn[ag->spawn_nr].x = x;
337 ag->spawn[ag->spawn_nr].y = y;
338 ag->spawn[ag->spawn_nr].clip = clip;
339 ag->spawn[ag->spawn_nr].bg = gfx_grab_screen(x, y, w, h);
340 ag->spawn_nr = nr;
341 return 0;
342 }
343
344 static anim_t anim_list = NULL;
345
346 static int anim_drawn_nr = 0;
347
anim_find(anim_t g)348 static anim_t anim_find(anim_t g)
349 {
350 anim_t p;
351 for (p = anim_list; p; p = p->next) {
352 if (p == g)
353 return p;
354 }
355 return NULL;
356 }
357
anim_disposal(anim_t g)358 static void anim_disposal(anim_t g)
359 {
360 SDL_Rect dest;
361 SDL_Rect clip;
362 int i = 0;
363 SDL_Surface *img = NULL;
364
365 dest.x = 0;
366 dest.y = 0;
367 dest.w = g->anim->w;
368 dest.h = g->anim->h;
369
370 SDL_GetClipRect(Surf(screen), &clip);
371
372 for (i = 0; i < g->spawn_nr; i++) {
373 SDL_Rect dst;
374 SDL_SetClipRect(Surf(screen), &g->spawn[i].clip);
375 dst = dest;
376 dst.x += g->spawn[i].x;
377 dst.y += g->spawn[i].y;
378 img = Surf(g->spawn[i].bg);
379 if (img) {
380 dst.w = img->w;
381 dst.h = img->h;
382 /* draw bg */
383 SDL_BlitSurface(img, NULL, Surf(screen), &dst);
384 }
385 }
386 SDL_SetClipRect(Surf(screen), &clip);
387 }
388
anim_frame(anim_t g)389 static void anim_frame(anim_t g)
390 {
391 int i;
392 SDL_Rect dest;
393 SDL_Rect clip;
394 SDL_Surface *frame;
395 frame = g->anim->frames[g->cur_frame];
396
397 SDL_GetClipRect(Surf(screen), &clip);
398
399 dest.w = g->anim->w;
400 dest.h = g->anim->h;
401
402 for (i = 0; i < g->spawn_nr; i++) {
403 dest.x = g->spawn[i].x;
404 dest.y = g->spawn[i].y;
405 SDL_SetClipRect(Surf(screen), &g->spawn[i].clip);
406 SDL_BlitSurface(frame, NULL, Surf(screen), &dest);
407 }
408 if (!g->active) /* initial draw */
409 g->delay = timer_counter;
410 SDL_SetClipRect(Surf(screen), &clip);
411 }
412
is_anim(img_t img)413 static anim_t is_anim(img_t img)
414 {
415 if (img && (img->flags & IMG_ANIM))
416 return (anim_t)(img->aux);
417 return NULL;
418 }
419
anim_add(anim_t g)420 static anim_t anim_add(anim_t g)
421 {
422 anim_t p;
423 p = anim_find(g);
424 if (p)
425 return p;
426 if (!anim_list) {
427 anim_list = g;
428 g->next = NULL;
429 g->prev = NULL;
430 return g;
431 }
432 for (p = anim_list; p && p->next; p = p->next);
433 p->next = g;
434 g->next = NULL;
435 g->prev = p;
436 return g;
437 }
438
anim_del(anim_t g)439 static anim_t anim_del(anim_t g)
440 {
441 if (g->prev == NULL)
442 anim_list = g->next;
443 else
444 g->prev->next = g->next;
445 if (g->next)
446 g->next->prev = g->prev;
447 return g;
448 }
449
anim_free_spawn(anim_t g)450 static void anim_free_spawn(anim_t g)
451 {
452 int i;
453 for (i = 0; i < g->spawn_nr; i++)
454 gfx_free_image(g->spawn[i].bg);
455 if (g->spawn) {
456 free(g->spawn);
457 g->spawn = NULL;
458 g->spawn_nr = 0;
459 }
460 }
461
anim_free(anim_t g)462 static void anim_free(anim_t g)
463 {
464 FreeAnimation(g->anim);
465 anim_free_spawn(g);
466 free(g);
467 }
468
gfx_free_img(img_t p)469 static void gfx_free_img(img_t p)
470 {
471 if (!p)
472 return;
473 SDL_FreeSurface(Surf(p));
474 /* todo texture */
475 SDL_free(p);
476 }
477
gfx_free_image(img_t p)478 void gfx_free_image(img_t p)
479 {
480 anim_t ag;
481 if (!p)
482 return;
483 if (!cache_forget(images, p))
484 return; /* cached sprite */
485 if ((ag = is_anim(p))) {
486 if (ag->drawn)
487 anim_drawn_nr --;
488 anim_del(ag);
489 anim_free(ag);
490 return;
491 }
492 gfx_free_img(p);
493 }
494
gfx_cache_free_image(void * p)495 void gfx_cache_free_image(void *p)
496 {
497 gfx_free_image((img_t)p);
498 }
499
gfx_img_w(img_t pixmap)500 int gfx_img_w(img_t pixmap)
501 {
502 if (!pixmap)
503 return 0;
504 return Surf(pixmap)->w;
505 }
506
gfx_img_h(img_t pixmap)507 int gfx_img_h(img_t pixmap)
508 {
509 if (!pixmap)
510 return 0;
511 return Surf(pixmap)->h;
512 }
513
514
gfx_getclip(int * x,int * y,int * w,int * h)515 void gfx_getclip(int *x, int *y, int *w, int *h)
516 {
517 SDL_Rect clip;
518 if (!screen)
519 return;
520 SDL_GetClipRect(Surf(screen), &clip);
521 if (x)
522 *x = clip.x;
523 if (y)
524 *y = clip.y;
525 if (w)
526 *w = clip.w;
527 if (h)
528 *h = clip.h;
529 }
530
gfx_img_noclip(img_t img)531 void gfx_img_noclip(img_t img)
532 {
533 SDL_SetClipRect(Surf(img), NULL);
534 }
535
gfx_noclip(void)536 void gfx_noclip(void)
537 {
538 gfx_img_noclip(screen);
539 }
540
gfx_img_clip(img_t img,int x,int y,int w,int h)541 void gfx_img_clip(img_t img, int x, int y, int w, int h)
542 {
543 SDL_Rect src;
544 src.x = x;
545 src.y = y;
546 src.w = w;
547 src.h = h;
548 SDL_SetClipRect(Surf(img), &src);
549 }
550
gfx_clip(int x,int y,int w,int h)551 void gfx_clip(int x, int y, int w, int h)
552 {
553 gfx_img_clip(screen, x, y, w, h);
554 }
555
556 #define GFX_IMG(v) gfx_new_img(v, 0, NULL, 0)
557 #define GFX_IMG_REL(v) gfx_new_img(v, 0, NULL, 1)
558
gfx_new_img(SDL_Surface * s,int fl,void * data,int release)559 static img_t gfx_new_img(SDL_Surface *s, int fl, void *data, int release)
560 {
561 img_t i;
562 if (!s)
563 return NULL;
564 i = SDL_malloc(sizeof(struct _img_t));
565 if (i) {
566 i->s = s;
567 /* i->t = NULL; */
568 i->flags = fl;
569 i->aux = data;
570 } else if (release && s) {
571 SDL_FreeSurface(s);
572 }
573 return i;
574 }
575
gfx_new_rgba(int w,int h)576 img_t gfx_new_rgba(int w, int h)
577 {
578 SDL_Surface *dst;
579 Uint32 rmask, gmask, bmask, amask;
580 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
581 rmask = 0xff000000;
582 gmask = 0x00ff0000;
583 bmask = 0x0000ff00;
584 amask = 0x000000ff;
585 #else
586 rmask = 0x000000ff;
587 gmask = 0x0000ff00;
588 bmask = 0x00ff0000;
589 amask = 0xff000000;
590 #endif
591 dst = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_SRCALPHA, w, h,
592 32,
593 rmask,
594 gmask,
595 bmask,
596 amask);
597 #if SDL_VERSION_ATLEAST(2,0,0)
598 if (dst)
599 SDL_SetSurfaceBlendMode(dst, SDL_BLENDMODE_BLEND);
600 #endif
601 if (dst)
602 return GFX_IMG_REL(dst);
603 return NULL;
604 }
605
gfx_dup(img_t src)606 img_t gfx_dup(img_t src)
607 {
608 SDL_Surface *dst;
609 if (!src)
610 return NULL;
611 dst = SDL_ConvertSurface(Surf(src), Surf(src)->format, Surf(src)->flags);
612 if (!dst)
613 return NULL;
614 return GFX_IMG_REL(dst);
615 }
616
gfx_new_from(int w,int h,unsigned char * pixels)617 img_t gfx_new_from(int w, int h, unsigned char *pixels)
618 {
619 SDL_Surface *dst;
620 Uint32 rmask, gmask, bmask, amask;
621 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
622 rmask = 0xff000000;
623 gmask = 0x00ff0000;
624 bmask = 0x0000ff00;
625 amask = 0x000000ff;
626 #else
627 rmask = 0x000000ff;
628 gmask = 0x0000ff00;
629 bmask = 0x00ff0000;
630 amask = 0xff000000;
631 #endif
632 dst = SDL_CreateRGBSurfaceFrom(pixels, w, h, 32, w * 4, rmask, gmask, bmask, amask);
633 #if SDL_VERSION_ATLEAST(2,0,0)
634 if (dst)
635 SDL_SetSurfaceBlendMode(dst, SDL_BLENDMODE_BLEND);
636 #endif
637 if (dst)
638 return GFX_IMG_REL(dst);
639 return NULL;
640 }
641
gfx_new(int w,int h)642 img_t gfx_new(int w, int h)
643 {
644 SDL_Surface *dst;
645 if (!screen) {
646 return gfx_new_rgba(w, h);
647 } else {
648 dst = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_SRCALPHA, w, h,
649 Surf(screen)->format->BitsPerPixel,
650 Surf(screen)->format->Rmask,
651 Surf(screen)->format->Gmask,
652 Surf(screen)->format->Bmask,
653 Surf(screen)->format->Amask);
654 }
655 #if SDL_VERSION_ATLEAST(2,0,0)
656 if (dst)
657 SDL_SetSurfaceBlendMode(dst, SDL_BLENDMODE_NONE);
658 #endif
659 if (dst)
660 return GFX_IMG_REL(dst);
661 return NULL;
662 }
663
gfx_img_fill(img_t img,int x,int y,int w,int h,color_t col)664 void gfx_img_fill(img_t img, int x, int y, int w, int h, color_t col)
665 {
666 SDL_Rect dest;
667 if (!img)
668 return;
669 dest.x = x;
670 dest.y = y;
671 dest.w = w;
672 dest.h = h;
673 SDL_FillRect(Surf(img), &dest, SDL_MapRGB((Surf(img))->format, col.r, col.g, col.b));
674 }
675
gfx_fill(int x,int y,int w,int h,color_t col)676 void gfx_fill(int x, int y, int w, int h, color_t col)
677 {
678 gfx_img_fill(screen, x, y, w, h, col);
679 }
680
gfx_screen(img_t nscreen)681 img_t gfx_screen(img_t nscreen)
682 {
683 img_t img;
684 if (nscreen) {
685 img = screen;
686 screen = nscreen;
687 return img;
688 }
689 return screen;
690 }
691
gfx_grab_screen(int x,int y,int w,int h)692 img_t gfx_grab_screen(int x, int y, int w, int h)
693 {
694 SDL_Rect dst, src;
695 SDL_Surface *s;
696 int a;
697 img_t img;
698 if (!screen)
699 return NULL;
700 s = SDL_CreateRGBSurface(Surf(screen)->flags, w, h, Surf(screen)->format->BitsPerPixel,
701 Surf(screen)->format->Rmask, Surf(screen)->format->Gmask, Surf(screen)->format->Bmask, Surf(screen)->format->Amask);
702 if (!s)
703 return NULL;
704 src.x = x;
705 src.y = y;
706 src.w = w;
707 src.h = h;
708 dst.x = 0;
709 dst.y = 0;
710 dst.w = w;
711 dst.h = h;
712 /* SDL_SetSurfaceBlendMode(screen, SDL_BLENDMODE_NONE);
713 SDL_SetSurfaceBlendMode(img, SDL_BLENDMODE_NONE); */
714 a = gfx_unset_alpha(screen);
715 SDL_BlitSurface(Surf(screen), &src, s, &dst);
716 gfx_set_alpha(screen, a);
717 img = GFX_IMG_REL(s);
718 if (!img)
719 return NULL;
720 gfx_unset_alpha(img);
721 return img;
722 }
723
724 #if SDL_VERSION_ATLEAST(2,0,0)
725 static SDL_RendererInfo SDL_VideoRendererInfo;
726
SDL_DisplayFormat(SDL_Surface * surface)727 SDL_Surface *SDL_DisplayFormat(SDL_Surface * surface)
728 {
729 SDL_PixelFormat *format;
730 SDL_Surface *converted;
731
732 if (!screen) {
733 fprintf(stderr, "No video mode has been set.\n");
734 return NULL;
735 }
736 format = Surf(screen)->format;
737 converted = SDL_ConvertSurface(surface, format, 0);
738 return converted;
739 }
740
SDL_DisplayFormatAlpha(SDL_Surface * surface)741 SDL_Surface *SDL_DisplayFormatAlpha(SDL_Surface * surface)
742 {
743 SDL_Surface *converted;
744 converted = SDL_DisplayFormat(surface);
745 if (converted)
746 SDL_SetSurfaceBlendMode(converted, SDL_BLENDMODE_BLEND);
747 return converted;
748 }
749
750 #endif
gfx_display_alpha(img_t src)751 img_t gfx_display_alpha(img_t src)
752 {
753 SDL_Surface* res;
754 if (!src)
755 return NULL;
756 if (!screen)
757 return src;
758 if (is_anim(src)) /* already optimized */
759 return src;
760 #if SDL_VERSION_ATLEAST(2,0,0)
761 if (Surf(screen)->format == Surf(src)->format) { /* fast path! */
762 SDL_SetSurfaceBlendMode(Surf(src), SDL_BLENDMODE_BLEND);
763 return src;
764 }
765 #endif
766 res = SDL_DisplayFormatAlpha(Surf(src));
767 if (!res)
768 return src;
769 gfx_free_image(src);
770 return GFX_IMG_REL(res);
771 }
772
gfx_get_pixel(img_t src,int x,int y,color_t * color)773 int gfx_get_pixel(img_t src, int x, int y, color_t *color)
774 {
775 Uint8 r, g, b, a;
776 Uint32 col = 0;
777 Uint8 *ptr;
778 int bpp;
779 SDL_Surface *img = Surf(src);
780 if (!img)
781 return -1;
782
783 if (x >= img->w || y >= img->h || x < 0 || y < 0)
784 return -1;
785
786 if (SDL_LockSurface(img))
787 return -1;
788
789 if (img->format)
790 bpp = img->format->BytesPerPixel;
791 else
792 bpp = 1; /* hack? */
793
794 ptr = (Uint8*)img->pixels;
795 ptr += img->pitch * y;
796 ptr += x * bpp;
797
798 memcpy(&col, ptr, bpp);
799
800 SDL_UnlockSurface(img);
801 if (color)
802 SDL_GetRGBA(col, img->format, &r, &g, &b, &a);
803
804 if (color) {
805 color->r = r;
806 color->g = g;
807 color->b = b;
808 color->a = a;
809 }
810 return 0;
811 }
812
gfx_set_pixel(img_t src,int x,int y,color_t color)813 int gfx_set_pixel(img_t src, int x, int y, color_t color)
814 {
815 int bpp;
816 Uint32 col;
817 Uint8 *ptr;
818 SDL_Surface *img = Surf(src);
819 if (!img)
820 return -1;
821
822 if (x >= img->w || y >= img->h || x < 0 || y < 0)
823 return -1;
824
825 if (SDL_LockSurface(img))
826 return -1;
827
828 if (img->format)
829 bpp = img->format->BytesPerPixel;
830 else
831 bpp = 1; /* hack? */
832
833 ptr = (Uint8*)img->pixels;
834 ptr += img->pitch * y;
835 ptr += x * bpp;
836 col = SDL_MapRGBA(img->format, color.r, color.g, color.b, color.a);
837 memcpy(ptr, &col, bpp);
838
839 SDL_UnlockSurface(img);
840 return 0;
841 }
842
gfx_put_pixels(img_t src)843 void gfx_put_pixels(img_t src)
844 {
845 SDL_Surface *img = Surf(src);
846 SDL_UnlockSurface(img);
847 return;
848 }
849
gfx_get_pixels(img_t src)850 unsigned char *gfx_get_pixels(img_t src)
851 {
852 Uint8 *ptr;
853 SDL_Surface *img = Surf(src);
854
855 if (!img)
856 return NULL;
857
858 if (SDL_LockSurface(img))
859 return NULL;
860
861 ptr = (unsigned char*)img->pixels;
862 return ptr;
863 }
gfx_alpha_img(img_t src,int alpha)864 img_t gfx_alpha_img(img_t src, int alpha)
865 {
866 Uint8 *ptr;
867 Uint32 col;
868 int size;
869 int bpp;
870
871 img_t img = NULL;
872 if (!src)
873 return NULL;
874 if (screen) {
875 SDL_Surface *s = SDL_DisplayFormatAlpha(Surf(src));
876 if (s)
877 img = GFX_IMG_REL(s);
878 } else
879 img = gfx_new(Surf(src)->w, Surf(src)->h);
880 if (!img)
881 return NULL;
882
883 if (Surf(img)->format)
884 bpp = Surf(img)->format->BytesPerPixel;
885 else
886 bpp = 1;
887
888 gfx_set_alpha(img, SDL_ALPHA_OPAQUE);
889
890 if (SDL_LockSurface(Surf(img)) == 0) {
891 int w = Surf(img)->w;
892 ptr = (Uint8*)(Surf(img)->pixels);
893 size = Surf(img)->w * Surf(img)->h;
894 while (size --) {
895 Uint8 r, g, b, a;
896 memcpy(&col, ptr, bpp);
897 SDL_GetRGBA(col, Surf(img)->format, &r, &g, &b, &a);
898 col = SDL_MapRGBA(Surf(img)->format, r, g, b, a * alpha / SDL_ALPHA_OPAQUE);
899 memcpy(ptr, &col, bpp);
900 ptr += bpp;
901 w --;
902 if (!w) {
903 w = Surf(img)->w;
904 ptr += Surf(img)->pitch;
905 ptr -= w * bpp;
906 }
907 }
908 SDL_UnlockSurface(Surf(img));
909 }
910 return img;
911 }
912
gfx_set_colorkey(img_t src,color_t col)913 void gfx_set_colorkey(img_t src, color_t col)
914 {
915 Uint32 c;
916 SDL_Surface *s = Surf(src);
917 if (!s)
918 return;
919 c = SDL_MapRGB(s->format, col.r, col.g, col.b);
920 #if SDL_VERSION_ATLEAST(2,0,0)
921 SDL_SetColorKey(s, SDL_TRUE, c);
922 #else
923 SDL_SetColorKey(s, SDL_SRCCOLORKEY, c);
924 #endif
925 /* gfx_unset_alpha(src); */
926 }
927
gfx_unset_colorkey(img_t src)928 void gfx_unset_colorkey(img_t src)
929 {
930 #if SDL_VERSION_ATLEAST(2,0,0)
931 SDL_SetColorKey(Surf(src), SDL_FALSE, 0);
932 #else
933 SDL_SetColorKey(Surf(src), 0, 0);
934 #endif
935 /* gfx_set_alpha(src, SDL_ALPHA_OPAQUE); */
936 }
937
gfx_set_alpha(img_t src,int alpha)938 void gfx_set_alpha(img_t src, int alpha)
939 {
940 if (alpha < 0)
941 return;
942 #if SDL_VERSION_ATLEAST(1,3,0)
943 /* if (Surf(src)->format->Amask)
944 alpha = SDL_ALPHA_OPAQUE; */
945 SDL_SetSurfaceAlphaMod(Surf(src), alpha);
946 /* SDL_SetSurfaceBlendMode((SDL_Surface *)src, SDL_BLENDMODE_NONE);
947 else */
948 SDL_SetSurfaceBlendMode(Surf(src), SDL_BLENDMODE_BLEND);
949 #else
950 SDL_SetAlpha(Surf(src), SDL_SRCALPHA, alpha);
951 #endif
952 }
953
gfx_unset_alpha(img_t src)954 int gfx_unset_alpha(img_t src)
955 {
956 int alpha = -1;
957 #if SDL_VERSION_ATLEAST(1,3,0)
958 SDL_BlendMode blendMode;
959 Uint8 sdl_alpha = SDL_ALPHA_OPAQUE;
960 alpha = SDL_GetSurfaceBlendMode(Surf(src), &blendMode);
961 if (blendMode == SDL_BLENDMODE_BLEND) {
962 SDL_GetSurfaceAlphaMod(Surf(src), &sdl_alpha);
963 alpha = sdl_alpha;
964 }
965 SDL_SetSurfaceAlphaMod(Surf(src), SDL_ALPHA_OPAQUE);
966 SDL_SetSurfaceBlendMode(Surf(src), SDL_BLENDMODE_NONE);
967 #else
968 if ((Surf(src)->flags) & SDL_SRCALPHA)
969 alpha = Surf(src)->format->alpha;
970 SDL_SetAlpha(Surf(src), 0, SDL_ALPHA_OPAQUE);
971 #endif
972 return alpha;
973 }
974
gfx_combine(img_t src,img_t dst)975 img_t gfx_combine(img_t src, img_t dst)
976 {
977 img_t new;
978 SDL_Surface *s;
979 s = SDL_DisplayFormatAlpha(Surf(dst));
980 if (!s)
981 return NULL;
982 new = GFX_IMG_REL(s);
983 if (new)
984 SDL_BlitSurface(Surf(src), NULL, Surf(new), NULL);
985 return new;
986 }
987
img_pad(char * fname)988 static img_t img_pad(char *fname)
989 {
990 int l,r,t,b, rc;
991 img_t img, img2;
992 SDL_Rect to;
993 char *p = fname;
994 p += strcspn(p, ",");
995 if (*p != ',')
996 return NULL;
997 p ++;
998 rc = sscanf(fname, "%d %d %d %d,", &t, &r, &b, &l);
999 if (rc == 1) {
1000 r = b = l = t;
1001 } else if (rc == 2) {
1002 b = t; l = r;
1003 } else if (rc == 3) {
1004 l = r;
1005 } else if (rc == 4) {
1006 ;
1007 } else
1008 return NULL;
1009 img = gfx_load_image(p);
1010 if (!img)
1011 return NULL;
1012 img2 = gfx_new(gfx_img_w(img) + l + r, gfx_img_h(img) + t + b);
1013 if (!img2) {
1014 gfx_free_image(img);
1015 return NULL;
1016 } else {
1017 img_t img = gfx_alpha_img(img2, 0);
1018 if (img) {
1019 gfx_free_image(img2);
1020 img2 = img;
1021 }
1022 }
1023 to.x = l;
1024 to.y = t;
1025 SDL_gfxBlitRGBA(Surf(img), NULL, Surf(img2), &to);
1026 gfx_free_image(img);
1027 return img2;
1028 }
1029
1030 static img_t _gfx_load_combined_image(char *filename);
1031
1032 /* blank:WxH */
_gfx_load_special_image(char * f,int combined)1033 static img_t _gfx_load_special_image(char *f, int combined)
1034 {
1035 int alpha = 0;
1036 int blank = 0;
1037 char *filename;
1038 img_t img, img2;
1039 char *pc = NULL, *pt = NULL;
1040 int wh[2] = { 0, 0 };
1041 if (!f)
1042 return NULL;
1043
1044 if (!(f = filename = strdup(f)))
1045 return NULL;
1046
1047 if (!strncmp(filename, "blank:", 6)) {
1048 filename += 6;
1049 blank = 1;
1050 } else if (!strncmp(filename, "spr:", 4) && !combined) {
1051 /* filename += 4; */
1052 img2 = cache_get(images, filename);
1053 /* fprintf(stderr, "get:%s %p\n", filename, img2); */
1054 goto out;
1055 } else if (!strncmp(filename, "box:", 4)) {
1056 filename += 4;
1057 alpha = SDL_ALPHA_OPAQUE;
1058 } else if (!strncmp(filename, "pad:", 4)) {
1059 filename += 4;
1060 img2 = img_pad(filename);
1061 goto out;
1062 } else if (!strncmp(filename, "comb:", 5)) {
1063 filename += 5;
1064 img2 = _gfx_load_combined_image(filename);
1065 goto out;
1066 } else
1067 goto err;
1068
1069 if (strchr(filename, ';'))
1070 goto err; /* combined */
1071
1072 if (blank)
1073 goto skip;
1074 pc = filename + strcspn(filename, ",");
1075 if (*pc == ',') {
1076 *pc = 0;
1077 pc ++;
1078 pt = pc + strcspn(pc, ",");
1079 if (*pt == ',') {
1080 *pt = 0;
1081 pt ++;
1082 } else
1083 pt = NULL;
1084 } else
1085 pc = NULL;
1086 skip:
1087 if (parse_mode(filename, wh))
1088 goto err;
1089 if (wh[0] <= 0 || wh[1] <= 0)
1090 goto err;
1091 img = gfx_new(wh[0], wh[1]);
1092 if (!img)
1093 goto err;
1094
1095 if (pc) {
1096 color_t col = { .r = 255, .g = 255, .b = 255 };
1097 gfx_parse_color(pc, &col);
1098 gfx_img_fill(img, 0, 0, wh[0], wh[1], col);
1099 }
1100 if (pt)
1101 alpha = atoi(pt);
1102
1103 img2 = gfx_alpha_img(img, alpha);
1104 gfx_free_image(img);
1105 out:
1106 free(f);
1107 return img2;
1108 err:
1109 free(f);
1110 return NULL;
1111 }
1112
gfx_image_cache(void)1113 cache_t gfx_image_cache(void)
1114 {
1115 return images;
1116 }
1117
anim_new(Animation_t * anim)1118 static anim_t anim_new(Animation_t *anim)
1119 {
1120 anim_t ag = malloc(sizeof(struct _anim_t));
1121 if (!ag)
1122 return NULL;
1123 memset(ag, 0, sizeof(struct _anim_t));
1124 ag->anim = anim;
1125 ag->nr_frames = anim->count;
1126 ag->loop = anim->loop;
1127 return ag;
1128 }
1129
anim_clone(anim_t ag)1130 static anim_t anim_clone(anim_t ag)
1131 {
1132 int i;
1133 anim_t nag = malloc(sizeof(struct _anim_t));
1134 if (!nag)
1135 return NULL;
1136 memcpy(nag, ag, sizeof(struct _anim_t));
1137 nag->cur_frame = 0;
1138 nag->drawn = 0;
1139 nag->active = 0;
1140 nag->delay = 0;
1141 nag->spawn_nr = 0;
1142 nag->spawn = NULL;
1143 nag->anim = malloc(sizeof(Animation_t));
1144 if (!nag->anim)
1145 goto err;
1146 memcpy(nag->anim, ag->anim, sizeof(Animation_t));
1147 nag->anim->delays = malloc(sizeof(int) * nag->nr_frames);
1148 nag->anim->frames = malloc(sizeof(SDL_Surface *) * nag->nr_frames);
1149 if (!nag->anim->delays || !nag->anim->frames)
1150 goto err;
1151 for (i = 0; i < nag->nr_frames; i ++) {
1152 nag->anim->frames[i] = ag->anim->frames[i];
1153 nag->anim->delays[i] = ag->anim->delays[i];
1154 }
1155 return nag;
1156 err:
1157 if (nag->anim) {
1158 if (nag->anim->delays)
1159 free(nag->anim->delays);
1160 if (nag->anim->frames)
1161 free(nag->anim->frames);
1162 free(nag->anim);
1163 }
1164 free(nag);
1165 return NULL;
1166 }
1167
_gfx_load_image(char * filename,int combined)1168 static img_t _gfx_load_image(char *filename, int combined)
1169 {
1170 SDL_RWops *rw;
1171 img_t img;
1172 Animation_t *anim = NULL;
1173 filename = strip(filename);
1174 img = _gfx_load_special_image(filename, combined);
1175 if (img)
1176 return img;
1177 if (strstr(filename,".gif") || strstr(filename,".GIF")) /* only agif now */
1178 anim = GIF_LoadAnim(filename);
1179 if (anim) { /* animation logic */
1180 anim_t ag = anim_new(anim);
1181 if (!ag)
1182 return NULL;
1183 anim_add(ag);
1184 img = gfx_new_img(ag->anim->frames[0], IMG_ANIM, ag, 0);
1185 if (!img) {
1186 anim_del(ag);
1187 anim_free(ag);
1188 }
1189 return img;
1190 }
1191 rw = RWFromIdf(instead_idf(), filename);
1192
1193 if (!rw || !(img = GFX_IMG_REL(IMG_Load_RW(rw, 1))))
1194 return NULL;
1195
1196 if (Surf(img)->format->BitsPerPixel == 32) { /* hack for 32 bit BMP :( */
1197 SDL_RWops *rwop;
1198 rwop = RWFromIdf(instead_idf(), filename);
1199 if (rwop) {
1200 if (IMG_isBMP(rwop))
1201 /* SDL_SetAlpha(img, 0, SDL_ALPHA_OPAQUE); */
1202 gfx_unset_alpha(img);
1203 SDL_RWclose(rwop);
1204 }
1205 }
1206 img = gfx_display_alpha(img);
1207 return img;
1208 }
1209
1210 /* x.png;a.png@1,2;b.png@3,4 */
_gfx_load_combined_image(char * filename)1211 static img_t _gfx_load_combined_image(char *filename)
1212 {
1213 char *str;
1214 char *p, *ep;
1215 img_t base = NULL, img = NULL;
1216 p = str = strdup(filename);
1217 if (!str)
1218 return NULL;
1219 ep = p + strcspn(p, ";");
1220 if (*ep != ';')
1221 goto err; /* first image is a base image */
1222 *ep = 0;
1223
1224 base = _gfx_load_image(strip(p), 1);
1225 if (!base)
1226 goto err;
1227 p = ep + 1;
1228 while (*p) {
1229 int x = 0, y = 0, c = 0;
1230 SDL_Rect to;
1231 ep = p + strcspn(p, ";@");
1232 if (*ep == '@') {
1233 *ep = 0; ep ++;
1234 if (*ep == 'c') {
1235 c = 1;
1236 ep ++;
1237 }
1238 sscanf(ep, "%d,%d", &x, &y);
1239 ep += strcspn(ep, ";");
1240 if (*ep)
1241 ep ++;
1242 } else if (*ep == ';') {
1243 *ep = 0; ep ++;
1244 } else if (*ep) {
1245 goto err;
1246 }
1247 img = _gfx_load_image(strip(p), 1);
1248 if (!img)
1249 goto err;
1250 to.x = x; to.y = y;
1251 if (c) {
1252 to.x -= gfx_img_w(img) / 2;
1253 to.y -= gfx_img_h(img) / 2;
1254 }
1255 to.w = to.h = 0;
1256 SDL_gfxBlitRGBA(Surf(img), NULL, Surf(base), &to);
1257 gfx_free_image(img);
1258 p = ep;
1259 }
1260 free(str);
1261 return base;
1262 err:
1263 gfx_free_image(base);
1264 free(str);
1265 return NULL;
1266 }
1267
gfx_load_image(char * filename)1268 img_t gfx_load_image(char *filename)
1269 {
1270 img_t img = NULL;
1271 if (!filename)
1272 return NULL;
1273 /* if (!access(filename, R_OK)) */
1274 img = _gfx_load_image(filename, 0);
1275 if (!img)
1276 img = _gfx_load_combined_image(filename);
1277 return img;
1278 }
1279
gfx_draw_bg(img_t p,int x,int y,int width,int height)1280 void gfx_draw_bg(img_t p, int x, int y, int width, int height)
1281 {
1282 int a;
1283 SDL_Surface *pixbuf = Surf(p);
1284 SDL_Rect dest, src;
1285 if (!p)
1286 return;
1287 src.x = x;
1288 src.y = y;
1289 src.w = width;
1290 src.h = height;
1291 dest.x = x;
1292 dest.y = y;
1293 dest.w = width;
1294 dest.h = height;
1295 a = gfx_unset_alpha(p);
1296 SDL_BlitSurface(pixbuf, &src, Surf(screen), &dest);
1297 gfx_set_alpha(p, a);
1298 }
1299
gfx_draw_from_alpha(img_t s,int x,int y,int w,int h,img_t d,int xx,int yy,int alpha)1300 void gfx_draw_from_alpha(img_t s, int x, int y, int w, int h, img_t d, int xx, int yy, int alpha)
1301 {
1302 #if SDL_VERSION_ATLEAST(1,3,0)
1303 SDL_BlendMode blendMode;
1304 Uint8 sdl_alpha = SDL_ALPHA_OPAQUE;
1305 SDL_GetSurfaceBlendMode(Surf(s), &blendMode);
1306 SDL_GetSurfaceAlphaMod(Surf(s), &sdl_alpha);
1307
1308 SDL_SetSurfaceAlphaMod(Surf(s), alpha);
1309 SDL_SetSurfaceBlendMode(Surf(s), SDL_BLENDMODE_BLEND);
1310
1311 gfx_draw_from(s, x, y, w, h, d, xx, yy);
1312 SDL_SetSurfaceBlendMode(Surf(s), blendMode);
1313 SDL_SetSurfaceAlphaMod(Surf(s), sdl_alpha);
1314 #else
1315 img_t img;
1316 img = gfx_alpha_img(s, alpha);
1317 gfx_draw_from((img)?img:s, x, y, w, h, d, xx, yy);
1318 gfx_free_image(img);
1319 #endif
1320 }
1321
gfx_draw_from(img_t p,int x,int y,int width,int height,img_t to,int xx,int yy)1322 void gfx_draw_from(img_t p, int x, int y, int width, int height, img_t to, int xx, int yy)
1323 {
1324 SDL_Surface *pixbuf = Surf(p);
1325 SDL_Surface *scr = Surf(to);
1326 SDL_Rect dest, src;
1327 if (!p)
1328 return;
1329 if (!scr)
1330 scr = Surf(screen);
1331 src.x = x;
1332 src.y = y;
1333 src.w = width;
1334 src.h = height;
1335 dest.x = xx;
1336 dest.y = yy;
1337 dest.w = width;
1338 dest.h = height;
1339 SDL_BlitSurface(pixbuf, &src, scr, &dest);
1340 }
1341
gfx_compose_from(img_t p,int x,int y,int width,int height,img_t to,int xx,int yy)1342 void gfx_compose_from(img_t p, int x, int y, int width, int height, img_t to, int xx, int yy)
1343 {
1344 SDL_Surface *pixbuf = Surf(p);
1345 SDL_Surface *scr = Surf(to);
1346 SDL_Rect dest, src;
1347 if (!scr)
1348 scr = Surf(screen);
1349 src.x = x;
1350 src.y = y;
1351 src.w = width;
1352 src.h = height;
1353 dest.x = xx;
1354 dest.y = yy;
1355 dest.w = width;
1356 dest.h = height;
1357 SDL_gfxBlitRGBA(pixbuf, &src, scr, &dest);
1358 }
1359
gfx_copy(img_t p,int x,int y)1360 void gfx_copy(img_t p, int x, int y)
1361 {
1362 int a;
1363 SDL_Surface *pixbuf = Surf(p);
1364 SDL_Rect dest;
1365 if (!p)
1366 return;
1367 dest.x = x;
1368 dest.y = y;
1369 dest.w = pixbuf->w;
1370 dest.h = pixbuf->h;
1371 a = gfx_unset_alpha(p);
1372 SDL_BlitSurface(pixbuf, NULL, Surf(screen), &dest);
1373 gfx_set_alpha(p, a);
1374 }
1375
gfx_copy_from(img_t p,int x,int y,int width,int height,img_t to,int xx,int yy)1376 void gfx_copy_from(img_t p, int x, int y, int width, int height, img_t to, int xx, int yy)
1377 {
1378 int a;
1379 SDL_Surface *pixbuf = Surf(p);
1380 SDL_Surface *scr = Surf(to);
1381 SDL_Rect dest, src;
1382 if (!p)
1383 return;
1384 if (!scr)
1385 scr = Surf(screen);
1386 src.x = x;
1387 src.y = y;
1388 src.w = width;
1389 src.h = height;
1390 dest.x = xx;
1391 dest.y = yy;
1392 dest.w = width;
1393 dest.h = height;
1394 a = gfx_unset_alpha(p);
1395 SDL_BlitSurface(pixbuf, &src, scr, &dest);
1396 gfx_set_alpha(p, a);
1397 }
1398
gfx_draw(img_t p,int x,int y)1399 void gfx_draw(img_t p, int x, int y)
1400 {
1401 anim_t ag;
1402 SDL_Surface *pixbuf = Surf(p);
1403 SDL_Rect dest;
1404 if (!p)
1405 return;
1406 dest.x = x;
1407 dest.y = y;
1408 dest.w = pixbuf->w;
1409 dest.h = pixbuf->h;
1410 if (!DIRECT_MODE) /* no anim in direct mode */
1411 ag = is_anim(p);
1412 else
1413 ag = NULL;
1414 if (ag) {
1415 anim_spawn(ag, x, y, dest.w, dest.h);
1416 if (!ag->drawn)
1417 anim_drawn_nr ++;
1418 anim_frame(ag);
1419 ag->drawn = 1;
1420 ag->active = 1;
1421 return;
1422 }
1423 SDL_BlitSurface(pixbuf, NULL, Surf(screen), &dest);
1424 }
1425
gfx_stop_anim(img_t p)1426 void gfx_stop_anim(img_t p)
1427 {
1428 anim_t ag;
1429 ag = is_anim(p);
1430 if (ag)
1431 ag->active = 0;
1432 }
1433
gfx_dispose_anim(img_t p)1434 void gfx_dispose_anim(img_t p)
1435 {
1436 anim_t ag;
1437 ag = is_anim(p);
1438 if (ag) {
1439 if (ag->drawn)
1440 anim_drawn_nr --;
1441 ag->drawn = 0;
1442 anim_free_spawn(ag);
1443 }
1444 }
1445
gfx_start_anim(img_t p)1446 void gfx_start_anim(img_t p)
1447 {
1448 anim_t ag;
1449 ag = is_anim(p);
1450 if (ag)
1451 ag->active = 1;
1452 }
1453
gfx_frame_anim(img_t img)1454 int gfx_frame_anim(img_t img)
1455 {
1456 anim_t ag;
1457 ag = is_anim(img);
1458
1459 if (!ag)
1460 return 0;
1461
1462 if (!ag->drawn || !ag->active)
1463 return 0;
1464 if (ag->loop == -1)
1465 return 0;
1466
1467 if ((int)(timer_counter - ag->delay) < (ag->anim->delays[ag->cur_frame] / HZ))
1468 return 0;
1469
1470 if (ag->cur_frame != ag->nr_frames - 1 || ag->loop > 1 || !ag->loop)
1471 anim_disposal(ag);
1472
1473 ag->cur_frame ++;
1474
1475 if (ag->cur_frame >= ag->nr_frames) {
1476 if (!ag->loop || ag->loop > 1)
1477 ag->cur_frame = 0;
1478 else
1479 ag->cur_frame --; /* last one */
1480 if (ag->loop) {
1481 ag->loop --;
1482 if (!ag->loop)
1483 ag->loop = -1; /* disabled */
1484 }
1485
1486 }
1487 if (ag->loop != -1)
1488 anim_frame(ag);
1489
1490 return 1;
1491 }
1492
gfx_is_drawn_anims(void)1493 int gfx_is_drawn_anims(void)
1494 {
1495 return anim_drawn_nr;
1496 }
1497
gfx_update_anim(img_t img,update_fn update)1498 void gfx_update_anim(img_t img, update_fn update)
1499 {
1500 int i = 0;
1501 anim_t ag;
1502 ag = is_anim(img);
1503 if (!ag)
1504 return;
1505 if (!ag->drawn || !ag->active)
1506 return;
1507 for (i = 0; i < ag->spawn_nr; i++) {
1508 update(ag->spawn[i].x, ag->spawn[i].y,
1509 gfx_img_w(img), gfx_img_h(img));
1510 }
1511 }
1512
gfx_draw_wh(img_t p,int x,int y,int w,int h)1513 void gfx_draw_wh(img_t p, int x, int y, int w, int h)
1514 {
1515 SDL_Surface *pixbuf = Surf(p);
1516 SDL_Rect dest, src;
1517 src.x = 0;
1518 src.y = 0;
1519 src.w = w;
1520 src.h = h;
1521 dest.x = x;
1522 dest.y = y;
1523 dest.w = w;
1524 dest.h = h;
1525 SDL_BlitSurface(pixbuf, &src, Surf(screen), &dest);
1526 }
1527 static SDL_Color bgcol = { .r = 0, .g = 0, .b = 0 };
1528 static SDL_Color brdcol = { .r = 0, .g = 0, .b = 0 };
1529 static SDL_Rect brd = { .x = 0, .y = 0, .w = -1, .h = -1 };
1530
gfx_bg(int x,int y,int w,int h,color_t col,color_t bcol)1531 void gfx_bg(int x, int y, int w, int h, color_t col, color_t bcol)
1532 {
1533 brd.x = x;
1534 brd.y = y;
1535 brd.w = w;
1536 brd.h = h;
1537 bgcol.r = col.r;
1538 bgcol.g = col.g;
1539 bgcol.b = col.b;
1540 brdcol.r = bcol.r;
1541 brdcol.g = bcol.g;
1542 brdcol.b = bcol.b;
1543 }
1544
gfx_clear(int x,int y,int w,int h)1545 void gfx_clear(int x, int y, int w, int h)
1546 {
1547 int dx, dy;
1548 SDL_Rect dest;
1549 SDL_Surface *s = Surf(screen);
1550 if (!s)
1551 return;
1552 dest.x = x;
1553 dest.y = y;
1554 dest.w = w;
1555 dest.h = h;
1556 if (x < brd.x || y < brd.y || x + w >= brd.x + brd.w || y + h >= brd.y + brd.h) {
1557 SDL_FillRect(s, &dest, SDL_MapRGB(s->format, brdcol.r, brdcol.g, brdcol.b));
1558 dx = brd.x - x;
1559 dy = brd.y - y;
1560 if (dx > 0) {
1561 dest.x += dx;
1562 dest.w -= dx;
1563 }
1564 if (dy > 0) {
1565 dest.y += dy;
1566 dest.h -= dy;
1567 }
1568 dx = (brd.x + brd.w) - (dest.x + dest.w);
1569 dy = (brd.y + brd.h) - (dest.y + dest.h);
1570 if (dx < 0)
1571 dest.w += dx;
1572 if (dy < 0)
1573 dest.h += dy;
1574 if (dest.w > 0 && dest.h > 0)
1575 SDL_FillRect(s, &dest, SDL_MapRGB(s->format, bgcol.r, bgcol.g, bgcol.b));
1576 } else
1577 SDL_FillRect(s, &dest, SDL_MapRGB(s->format, bgcol.r, bgcol.g, bgcol.b));
1578 }
1579
1580 int gfx_width = -1;
1581 int gfx_height = -1;
1582 int gfx_fs = -1;
1583
1584 static SDL_Rect** vid_modes = NULL;
1585 static SDL_Rect m640x480 = { .w = 640, .h = 480 };
1586 static SDL_Rect m800x480 = { .w = 800, .h = 480 };
1587 static SDL_Rect m800x600 = { .w = 800, .h = 600 };
1588 static SDL_Rect m1024x768 = { .w = 1024, .h = 768 };
1589 static SDL_Rect m1280x800 = { .w = 1280, .h = 800 };
1590
1591 static SDL_Rect* std_modes[] = { &m640x480, &m800x480, &m800x600, &m1024x768, &m1280x800, NULL };
1592 #if SDL_VERSION_ATLEAST(2,0,0)
1593 static int SDL_CurrentDisplay = 0;
1594
SelectVideoDisplay()1595 static void SelectVideoDisplay()
1596 {
1597 const char *variable = SDL_getenv("SDL_VIDEO_FULLSCREEN_DISPLAY");
1598 if ( !variable ) {
1599 variable = SDL_getenv("SDL_VIDEO_FULLSCREEN_HEAD");
1600 }
1601 if ( variable ) {
1602 SDL_CurrentDisplay = SDL_atoi(variable);
1603 }
1604 }
1605
SDL_ListModes(const SDL_PixelFormat * format,Uint32 flags)1606 static SDL_Rect **SDL_ListModes(const SDL_PixelFormat * format, Uint32 flags)
1607 {
1608 SDL_DisplayMode disp_mode;
1609 int i, nmodes;
1610 SDL_Rect **modes;
1611 SDL_Rect **new_modes;
1612 Uint32 Rmask, Gmask, Bmask, Amask;
1613 int bpp;
1614
1615 SelectVideoDisplay();
1616
1617 SDL_GetDesktopDisplayMode(SDL_CurrentDisplay, &disp_mode);
1618 SDL_PixelFormatEnumToMasks(disp_mode.format, &bpp, &Rmask, &Gmask, &Bmask,
1619 &Amask);
1620 if (format)
1621 bpp = format->BitsPerPixel;
1622 nmodes = 0;
1623 modes = NULL;
1624 for (i = 0; i < SDL_GetNumDisplayModes(SDL_CurrentDisplay); ++i) {
1625 SDL_DisplayMode mode;
1626 if (SDL_GetDisplayMode(SDL_CurrentDisplay, i, &mode) < 0)
1627 continue;
1628
1629 if (!mode.w || !mode.h) {
1630 continue;
1631 }
1632 /* fprintf(stderr, "Mode: %d %d %d %d\n", bpp, SDL_BITSPERPIXEL(mode.format), mode.w, mode.h); */
1633 if ((unsigned int)bpp < SDL_BITSPERPIXEL(mode.format)) {
1634 continue;
1635 }
1636
1637 if (mode.w > disp_mode.w || mode.h > disp_mode.h) { /* skip large modes */
1638 if (mode.w > disp_mode.h || mode.h > disp_mode.w) /* landscape ? */
1639 continue;
1640 }
1641
1642 if (nmodes > 0 && modes[nmodes - 1]->w == mode.w
1643 && modes[nmodes - 1]->h == mode.h) {
1644 continue;
1645 }
1646 new_modes = SDL_realloc(modes, (nmodes + 2) * sizeof(*modes));
1647 if (!new_modes)
1648 goto out;
1649 modes = new_modes;
1650 modes[nmodes] = (SDL_Rect *) SDL_malloc(sizeof(SDL_Rect));
1651 if (!modes[nmodes])
1652 goto out;
1653 modes[nmodes]->x = 0;
1654 modes[nmodes]->y = 0;
1655 modes[nmodes]->w = mode.w;
1656 modes[nmodes]->h = mode.h;
1657 ++nmodes;
1658 }
1659 if (!modes) /* no modes found */
1660 return (SDL_Rect **) (-1);
1661 if (modes) {
1662 modes[nmodes] = NULL;
1663 }
1664 return modes;
1665 out:
1666 for (i = 0; i < nmodes; i++)
1667 SDL_free(modes[i]);
1668 SDL_free(modes);
1669 return NULL;
1670 }
1671 #endif
1672
1673 extern char *modes_sw;
1674
gfx_parse_modes(void)1675 static int gfx_parse_modes(void)
1676 {
1677 const char *p = modes_sw;
1678 int nr = 0;
1679 int mode[2];
1680 int i = 0;
1681
1682 if (!modes_sw)
1683 return 0;
1684
1685 while (*p) {
1686 p += strcspn(p, ",");
1687 if (*p)
1688 p ++;
1689 nr ++;
1690 }
1691 if (!nr)
1692 return 0;
1693 vid_modes = SDL_malloc(sizeof(SDL_Rect *) * (nr + 1)); /* array of pointers */
1694 if (!vid_modes)
1695 return 0;
1696 p = modes_sw;
1697 nr = 0;
1698 while (*p) {
1699 char m[64];
1700 SDL_Rect *r;
1701 size_t s;
1702 s = strcspn(p, ",");
1703 memset(m, 0, sizeof(m));
1704 memcpy(m, p, (s > sizeof(m))? sizeof(m): s);
1705 m[sizeof(m) - 1] = 0;
1706 if (parse_mode(m, &mode))
1707 break;
1708 r = SDL_malloc(sizeof(SDL_Rect));
1709 if (!r)
1710 goto err;
1711 vid_modes[nr ++] = r;
1712 r->w = mode[0];
1713 r->h = mode[1];
1714 fprintf(stderr, "Available mode: %dx%d\n", r->w, r->h);
1715 p += strcspn(p, ",");
1716 if (*p)
1717 p ++;
1718 }
1719 vid_modes[nr] = NULL;
1720 return nr;
1721 err:
1722 for (i = 0; i < nr; i++) {
1723 SDL_free(vid_modes[i]);
1724 }
1725 SDL_free(vid_modes);
1726 vid_modes = NULL;
1727 return 0;
1728 }
1729
gfx_modes(void)1730 int gfx_modes(void)
1731 {
1732 int i = 0;
1733 SDL_Rect** modes;
1734 if ((i = gfx_parse_modes()))
1735 return i;
1736 #if SDL_VERSION_ATLEAST(2,0,0)
1737 modes = SDL_ListModes(NULL, 0);
1738 #else
1739 #ifdef __APPLE__
1740 modes = SDL_ListModes(NULL, SDL_FULLSCREEN | SDL_SWSURFACE | SDL_ANYFORMAT);
1741 #else
1742 modes = SDL_ListModes(NULL, SDL_FULLSCREEN | SDL_HWSURFACE | SDL_DOUBLEBUF | SDL_ANYFORMAT);
1743 #endif
1744 #endif
1745 if (modes == (SDL_Rect**)0)/* no modes */
1746 return 0;
1747 if (modes == (SDL_Rect**)-1) {
1748 vid_modes = std_modes;
1749 return 5;
1750 }
1751 for (i = 0; modes[i]; ++i);
1752 vid_modes = modes;
1753 return i;
1754 }
1755
gfx_get_mode(int n,int * w,int * h)1756 int gfx_get_mode(int n, int *w, int *h)
1757 {
1758 if (!vid_modes)
1759 gfx_modes();
1760
1761 if (!vid_modes || !vid_modes[n])
1762 return -1;
1763 if (w)
1764 *w = vid_modes[n]->w;
1765 if (h)
1766 *h = vid_modes[n]->h;
1767 return 0;
1768 }
1769
gfx_prev_mode(int * w,int * h)1770 int gfx_prev_mode(int *w, int *h)
1771 {
1772 int ww, hh, i = 0;
1773
1774 if (!w || !h)
1775 return -1;
1776
1777
1778 while ((*w != -1 && *h != -1) &&
1779 !gfx_get_mode(i, &ww, &hh)) {
1780 if (ww == *w && hh == *h)
1781 break;
1782 i ++;
1783 }
1784
1785 if (*w == -1 || *h == -1)
1786 i = gfx_modes();
1787
1788 if (!i)
1789 return -1;
1790 i --;
1791 if (gfx_get_mode(i, &ww, &hh))
1792 return -1;
1793 *w = ww; *h = hh;
1794 return 0;
1795 }
1796
gfx_next_mode(int * w,int * h)1797 int gfx_next_mode(int *w, int *h)
1798 {
1799 int ww, hh, i = 0;
1800
1801 if (!w || !h)
1802 return -1;
1803
1804 while ((*w != -1 && *h != -1) &&
1805 !gfx_get_mode(i, &ww, &hh)) {
1806 i ++;
1807 if (ww == *w && hh == *h)
1808 break;
1809 }
1810
1811 if (gfx_get_mode(i, &ww, &hh))
1812 return -1;
1813 *w = ww; *h = hh;
1814 return 0;
1815 }
1816 #if SDL_VERSION_ATLEAST(2,0,0)
1817 #if defined(ANDROID) || defined(IOS)
1818 static int current_gfx_w = - 1;
1819 static int current_gfx_h = - 1;
1820 #endif
1821 #endif
1822
1823 #if defined(ANDROID)
1824 extern void get_screen_size(int *w, int *h);
1825 #endif
1826
gfx_get_max_mode(int * w,int * h,int o)1827 int gfx_get_max_mode(int *w, int *h, int o)
1828 {
1829 int ww = 0, hh = 0;
1830 int i = 0;
1831 #ifdef MAEMO
1832 *w = 800;
1833 *h = 480;
1834 #else
1835 #if SDL_VERSION_ATLEAST(2,0,0)
1836 SDL_DisplayMode desktop_mode;
1837 #if defined(ANDROID)
1838 if (o == MODE_ANY) {
1839 get_screen_size(w, h);
1840 return 0;
1841 }
1842 #endif
1843 #if defined(IOS)
1844 if (o == MODE_ANY && current_gfx_w != -1) {
1845 *w = current_gfx_w;
1846 *h = current_gfx_h;
1847 return 0;
1848 }
1849 #endif
1850 #ifdef _USE_SWROTATE
1851 if (!SDL_GetDesktopDisplayMode(SDL_CurrentDisplay, &desktop_mode)) {
1852 if ((o == MODE_H && desktop_mode.w < desktop_mode.h) ||
1853 (o == MODE_V && desktop_mode.w > desktop_mode.h)) {
1854 *w = desktop_mode.h;
1855 *h = desktop_mode.w;
1856 } else {
1857 *w = desktop_mode.w;
1858 *h = desktop_mode.h;
1859 }
1860 return 0;
1861 }
1862 #endif
1863 if (o == MODE_ANY && !SDL_GetDesktopDisplayMode(SDL_CurrentDisplay, &desktop_mode)) {
1864 *w = desktop_mode.w;
1865 *h = desktop_mode.h;
1866 return 0;
1867 }
1868 #endif
1869 *w = 0;
1870 *h = 0;
1871
1872 if (!vid_modes)
1873 gfx_modes();
1874
1875 if (!vid_modes)
1876 return -1;
1877
1878 while (!gfx_get_mode(i, &ww, &hh)) {
1879 if ((ww * hh >= (*w) * (*h))) {
1880 if (o == MODE_ANY || (o == MODE_H && ww >= hh) ||
1881 (o == MODE_V && hh > ww)) {
1882 *w = ww;
1883 *h = hh;
1884 }
1885 }
1886 i ++;
1887 }
1888 #endif
1889 if (*w == 0 || *h == 0) /* no suitable mode */
1890 return -1;
1891 return 0;
1892 }
1893
gfx_check_mode(int w,int h)1894 int gfx_check_mode(int w, int h)
1895 {
1896 #if defined(IOS) || defined(ANDROID)
1897 return 0;
1898 #else
1899 int ww = 0, hh = 0;
1900 int i = 0;
1901 if (!vid_modes)
1902 gfx_modes();
1903
1904 if (!vid_modes)
1905 return -1;
1906
1907 while (!gfx_get_mode(i, &ww, &hh)) {
1908 if (ww == w && hh == h)
1909 return 0;
1910 i ++;
1911 }
1912 return -1;
1913 #endif
1914 }
1915
1916 static SDL_Surface *icon = NULL;
1917 extern int software_sw;
1918 extern int glhack_sw;
1919
1920 #if SDL_VERSION_ATLEAST(2,0,0)
1921 #ifdef _USE_SWROTATE
1922 static int gfx_flip_rotate = 0;
1923 #endif
1924 SDL_Window *SDL_VideoWindow = NULL;
1925 static SDL_Texture *SDL_VideoTexture = NULL;
1926 static SDL_Surface *SDL_VideoSurface = NULL;
1927 static SDL_Renderer *Renderer = NULL;
GetEnvironmentWindowPosition(int w,int h,int * x,int * y)1928 static void GetEnvironmentWindowPosition(int w, int h, int *x, int *y)
1929 {
1930 const char *window = SDL_getenv("SDL_VIDEO_WINDOW_POS");
1931 const char *center = SDL_getenv("SDL_VIDEO_CENTERED");
1932 if (window) {
1933 if (SDL_sscanf(window, "%d,%d", x, y) == 2) {
1934 return;
1935 }
1936 if (SDL_strcmp(window, "center") == 0) {
1937 center = window;
1938 }
1939 }
1940 if (center) {
1941 SDL_DisplayMode mode;
1942 SDL_GetDesktopDisplayMode(SDL_CurrentDisplay, &mode);
1943 *x = (mode.w - w) / 2;
1944 *y = (mode.h - h) / 2;
1945 }
1946 }
1947
CreateVideoSurface(SDL_Texture * texture)1948 static SDL_Surface *CreateVideoSurface(SDL_Texture * texture)
1949 {
1950 SDL_Surface *surface;
1951 Uint32 format;
1952 int w, h;
1953 int bpp;
1954 Uint32 Rmask, Gmask, Bmask, Amask;
1955 /* void *pixels;
1956 int pitch; */
1957
1958 if (SDL_QueryTexture(texture, &format, NULL, &w, &h) < 0) {
1959 return NULL;
1960 }
1961
1962 if (!SDL_PixelFormatEnumToMasks(format, &bpp, &Rmask, &Gmask, &Bmask, &Amask)) {
1963 fprintf(stderr, "Unknown texture format.\n");
1964 return NULL;
1965 }
1966
1967 surface =
1968 SDL_CreateRGBSurface(0, w, h, bpp, Rmask, Gmask, Bmask, Amask);
1969 SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_NONE);
1970 return surface;
1971 }
1972
1973 static int mouse_x = -1;
1974 static int mouse_y = -1;
1975
mouse_watcher(void * userdata,SDL_Event * event)1976 static int mouse_watcher(void *userdata, SDL_Event *event)
1977 {
1978 #ifdef _USE_SWROTATE
1979 if (gfx_flip_rotate) {
1980 switch (event->type) {
1981 case SDL_MOUSEBUTTONUP:
1982 case SDL_MOUSEBUTTONDOWN:
1983 mouse_y = gfx_height - event->button.x;
1984 mouse_x = event->button.y;
1985 event->button.x = mouse_x;
1986 event->button.y = mouse_y;
1987 break;
1988 case SDL_MOUSEMOTION:
1989 mouse_y = gfx_height - event->motion.x;
1990 mouse_x = event->motion.y;
1991 event->motion.x = mouse_x;
1992 event->motion.y = mouse_y;
1993 break;
1994 case SDL_FINGERMOTION:
1995 case SDL_FINGERUP:
1996 case SDL_FINGERDOWN:
1997 #ifdef SAILFISHOS /* sailfish has broken touch events */
1998 mouse_x = event->tfinger.y;
1999 mouse_y = gfx_height - event->tfinger.x;
2000 #endif
2001 break;
2002
2003 default:
2004 break;
2005 }
2006 return 0;
2007 }
2008 #endif
2009 switch (event->type) {
2010 case SDL_MOUSEBUTTONUP:
2011 case SDL_MOUSEBUTTONDOWN:
2012 mouse_x = event->button.x;
2013 mouse_y = event->button.y;
2014 break;
2015 case SDL_MOUSEMOTION:
2016 mouse_x = event->motion.x;
2017 mouse_y = event->motion.y;
2018 break;
2019 default:
2020 break;
2021 }
2022 return 0;
2023 }
2024
gfx_real_size(int * ww,int * hh)2025 void gfx_real_size(int *ww, int *hh)
2026 {
2027 int w, h;
2028 SDL_GetWindowSize(SDL_VideoWindow, &w, &h);
2029 if (ww)
2030 *ww = w;
2031 if (hh)
2032 *hh = h;
2033 }
2034
gfx_finger_pos_scale(float x,float y,int * ox,int * oy,int norm)2035 void gfx_finger_pos_scale(float x, float y, int *ox, int *oy, int norm)
2036 {
2037 int xx = 0, yy = 0;
2038 #ifndef SAILFISHOS
2039 int w, h;
2040 float sx, sy;
2041 SDL_Rect rect;
2042
2043 if (!norm) { /* do not normalize? */
2044 w = gfx_width;
2045 h = gfx_height;
2046 sx = 1.0f;
2047 sy = 1.0f;
2048 rect.x = 0;
2049 rect.y = 0;
2050 } else {
2051 SDL_GetWindowSize(SDL_VideoWindow, &w, &h);
2052 SDL_RenderGetViewport(Renderer, &rect);
2053 SDL_RenderGetScale(Renderer, &sx, &sy);
2054 }
2055
2056 if (sx != 0) {
2057 x = x * w;
2058 xx = x / sx - rect.x;
2059 }
2060 if (sy != 0) {
2061 y = y * h;
2062 yy = y / sy - rect.y;
2063 }
2064 #else
2065 xx = (int)x; /* broken touch in SFOS */
2066 yy = (int)y;
2067 #endif
2068 #ifdef _USE_SWROTATE
2069 if (gfx_flip_rotate) {
2070 if (ox)
2071 *ox = yy;
2072 if (oy)
2073 *oy = gfx_height - xx;
2074 } else {
2075 #endif
2076 if (ox)
2077 *ox = xx;
2078 if (oy)
2079 *oy = yy;
2080 #ifdef _USE_SWROTATE
2081 }
2082 #endif
2083 }
2084
2085 #ifdef _USE_SWROTATE
rotate_landscape(void)2086 void rotate_landscape(void)
2087 {
2088 SDL_DisplayMode desktop_mode;
2089 SDL_GetDesktopDisplayMode(SDL_CurrentDisplay, &desktop_mode);
2090 gfx_flip_rotate = (desktop_mode.w < desktop_mode.h);
2091 #ifdef SAILFISHOS
2092 SDL_SetHint(SDL_HINT_QTWAYLAND_CONTENT_ORIENTATION, "landscape");
2093 #endif
2094 }
2095
rotate_portrait(void)2096 void rotate_portrait(void)
2097 {
2098 SDL_DisplayMode desktop_mode;
2099 SDL_GetDesktopDisplayMode(SDL_CurrentDisplay, &desktop_mode);
2100 gfx_flip_rotate = (desktop_mode.w > desktop_mode.h);
2101 #ifdef SAILFISHOS
2102 SDL_SetHint(SDL_HINT_QTWAYLAND_CONTENT_ORIENTATION, "portrait");
2103 #endif
2104 }
2105
unlock_rotation(void)2106 void unlock_rotation(void)
2107 {
2108 gfx_flip_rotate = 0;
2109 #ifdef SAILFISHOS
2110 SDL_SetHint(SDL_HINT_QTWAYLAND_CONTENT_ORIENTATION, "primary");
2111 #endif
2112 }
2113 #endif
2114
gfx_set_mode(int w,int h,int fs)2115 int gfx_set_mode(int w, int h, int fs)
2116 {
2117 int i;
2118 int vsync = SDL_RENDERER_PRESENTVSYNC;
2119 int window_x = SDL_WINDOWPOS_UNDEFINED;
2120 int window_y = SDL_WINDOWPOS_UNDEFINED;
2121 int win_w;
2122 int win_h; int sw_fallback = 0;
2123 int max_mode_w = 0;
2124 int max_mode_h = 0;
2125
2126 SDL_DisplayMode desktop_mode;
2127
2128 char title[4096];
2129 char *t;
2130
2131 strcpy(title, "INSTEAD - " );
2132 strcat(title, VERSION );
2133 win_w = w * scale_sw; win_h = h * scale_sw;
2134 gfx_get_max_mode(&max_mode_w, &max_mode_h, MODE_ANY); /* get current window size */
2135 #if defined(IOS) || defined(ANDROID) || defined(MAEMO) || defined(_WIN32_WCE) || defined(S60) || defined(WINRT) || defined(SAILFISHOS)
2136 fs = 1; /* always fs for mobiles */
2137 #endif
2138 if (fs && !software_sw) {
2139 win_w = max_mode_w;
2140 win_h = max_mode_h;
2141 }
2142 if (gfx_width == w && gfx_height == h && gfx_fs == fs) {
2143 game_reset_name();
2144 #if defined(ANDROID)
2145 if (SDL_VideoWindow) /* see gfx_set_mode call from input.c */
2146 #else
2147 if (SDL_VideoWindow && !fs)
2148 #endif
2149 SDL_SetWindowSize(SDL_VideoWindow, win_w, win_h);
2150 goto done; /* already done */
2151 }
2152 SelectVideoDisplay();
2153 SDL_GetDesktopDisplayMode(SDL_CurrentDisplay, &desktop_mode);
2154
2155 if (vid_modes && vid_modes != std_modes) {
2156 for (i = 0; vid_modes[i]; i++)
2157 SDL_free(vid_modes[i]);
2158 SDL_free(vid_modes);
2159 }
2160 vid_modes = NULL;
2161
2162 if (screen)
2163 gfx_free_image(screen);
2164
2165 /* if (SDL_VideoTexture)
2166 SDL_DestroyTexture(SDL_VideoTexture); */
2167
2168 if (Renderer)
2169 SDL_DestroyRenderer(Renderer);
2170
2171 screen = NULL;
2172 Renderer = NULL;
2173 SDL_VideoTexture = NULL;
2174
2175 if (SDL_VideoWindow) {
2176 SDL_GetWindowPosition(SDL_VideoWindow, &window_x, &window_y);
2177 SDL_DestroyWindow(SDL_VideoWindow);
2178 SDL_VideoWindow = NULL;
2179 if ((gfx_fs == 1 && !fs) || (window_x == 0 || window_y == 0)) { /* return from fullscreen */
2180 window_x = SDL_WINDOWPOS_CENTERED;
2181 window_y = SDL_WINDOWPOS_CENTERED;
2182 }
2183 } else
2184 GetEnvironmentWindowPosition(win_w, win_h, &window_x, &window_y);
2185
2186 if (desktop_mode.w <= win_w || fs)
2187 window_x = 0;
2188 if (desktop_mode.h <= win_h || fs)
2189 window_y = 0;
2190 t = game_reset_name();
2191 if (!t)
2192 t = title;
2193 if (glhack_sw) {
2194 fprintf(stderr, "glhack: %d\n", glhack_sw);
2195 /* fix for hackish samsung devices */
2196 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, (glhack_sw / 100) % 10);
2197 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, (glhack_sw / 10) % 10);
2198 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, glhack_sw % 10);
2199 }
2200 #if defined(IOS) || defined(ANDROID) || defined(WINRT) || defined(SAILFISHOS)
2201 SDL_VideoWindow = SDL_CreateWindow(t, window_x, window_y, win_w, win_h,
2202 SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS | SDL_WINDOW_RESIZABLE
2203 #if defined(ANDROID)
2204 | SDL_WINDOW_FULLSCREEN_DESKTOP
2205 #endif
2206 );
2207 if (!SDL_VideoWindow) {
2208 fprintf(stderr, "Fallback to software window.\n");
2209 SDL_VideoWindow = SDL_CreateWindow(t, window_x, window_y, win_w, win_h,
2210 SDL_WINDOW_BORDERLESS | SDL_WINDOW_RESIZABLE);
2211 }
2212 #else
2213 if (!software_sw) /* try to using scale */
2214 SDL_VideoWindow = SDL_CreateWindow(t, window_x, window_y, win_w, win_h,
2215 SDL_WINDOW_SHOWN | ((fs)?SDL_WINDOW_FULLSCREEN:(resizable_sw?SDL_WINDOW_RESIZABLE:0)) | SDL_WINDOW_OPENGL);
2216 if (!SDL_VideoWindow) { /* try simple window */
2217 fprintf(stderr, "Fallback to software window.\n");
2218 win_w = w; win_h = h;
2219 SDL_VideoWindow = SDL_CreateWindow(t, window_x, window_y, win_w, win_h,
2220 SDL_WINDOW_SHOWN | ((fs)?SDL_WINDOW_FULLSCREEN:0));
2221 }
2222 #endif
2223 if (SDL_VideoWindow == NULL) {
2224 fprintf(stderr, "Unable to create %dx%d window: %s\n", win_w, win_h, SDL_GetError());
2225 return -1;
2226 }
2227 if (icon)
2228 SDL_SetWindowIcon(SDL_VideoWindow, icon);
2229
2230 if (SDL_SetWindowDisplayMode(SDL_VideoWindow, (fs)?NULL:&desktop_mode) < 0) {
2231 fprintf(stderr, "Unable to set display mode: %s\n", SDL_GetError());
2232 /* return -1; */
2233 }
2234 if (!vsync_sw)
2235 vsync = 0;
2236 retry:
2237 if (software_sw ||
2238 (!(Renderer = SDL_CreateRenderer(SDL_VideoWindow, -1,
2239 SDL_RENDERER_ACCELERATED | vsync | SDL_RENDERER_TARGETTEXTURE)) &&
2240 !(Renderer = SDL_CreateRenderer(SDL_VideoWindow, -1,
2241 SDL_RENDERER_ACCELERATED)))) {
2242 fprintf(stderr, "Fallback to software renderer.\n");
2243 sw_fallback = 1;
2244 if (!(Renderer = SDL_CreateRenderer(SDL_VideoWindow, -1, SDL_RENDERER_SOFTWARE))) {
2245 fprintf(stderr, "Unable to create renderer: %s\n", SDL_GetError());
2246 return -1;
2247 }
2248 }
2249 SDL_GetRendererInfo(Renderer, &SDL_VideoRendererInfo);
2250 SDL_VideoTexture = SDL_CreateTexture(Renderer, SDL_PIXELFORMAT_ARGB8888,
2251 SDL_TEXTUREACCESS_STREAMING, w, h);
2252 if (!SDL_VideoTexture) {
2253 fprintf(stderr, "Unable to create texture: %s\n", SDL_GetError());
2254 if (!sw_fallback) { /* one more chance */
2255 SDL_DestroyRenderer(Renderer);
2256 software_sw = 1;
2257 goto retry;
2258 }
2259 return -1;
2260 }
2261 SDL_VideoSurface = CreateVideoSurface(SDL_VideoTexture);
2262 if (!SDL_VideoSurface) {
2263 fprintf(stderr, "Unable to create screen surface: %s\n", SDL_GetError());
2264 return -1;
2265 }
2266 /* Set a default screen palette */
2267 #if 0
2268 if (SDL_VideoSurface->format->palette) {
2269 /* SDL_VideoSurface->flags |= SDL_HWPALETTE;
2270 SDL_DitherColors(SDL_VideoSurface->format->palette->colors,
2271 SDL_VideoSurface->format->BitsPerPixel);
2272 SDL_AddPaletteWatch(SDL_VideoSurface->format->palette,
2273 SDL_VideoPaletteChanged, SDL_VideoSurface); */
2274 SDL_SetPaletteColors(SDL_VideoSurface->format->palette,
2275 SDL_VideoSurface->format->palette->colors, 0,
2276 SDL_VideoSurface->format->palette->ncolors);
2277 }
2278 #endif
2279 if (!nocursor_sw)
2280 SDL_ShowCursor(SDL_DISABLE);
2281
2282 gfx_fs = fs;
2283 gfx_width = w;
2284 gfx_height = h;
2285 screen = GFX_IMG_REL(SDL_VideoSurface);
2286 if (!screen) {
2287 fprintf(stderr, "Can't alloc screen!\n");
2288 return -1;
2289 }
2290
2291 #ifdef _USE_SWROTATE
2292 if (gfx_flip_rotate)
2293 SDL_RenderSetLogicalSize(Renderer, h, w);
2294 else
2295 #endif
2296 SDL_RenderSetLogicalSize(Renderer, w, h);
2297 #if SDL_VERSION_ATLEAST(2,0,0)
2298 SDL_DelEventWatch(mouse_watcher, NULL);
2299 SDL_AddEventWatch(mouse_watcher, NULL);
2300 #endif
2301 fprintf(stderr, "Video mode: %dx%d@%dbpp (%s)\n", Surf(screen)->w, Surf(screen)->h, Surf(screen)->format->BitsPerPixel, SDL_VideoRendererInfo.name);
2302 done:
2303 SDL_SetRenderDrawBlendMode(Renderer, SDL_BLENDMODE_NONE);
2304 SDL_SetRenderDrawColor(Renderer, 0, 0, 0, 255);
2305 SDL_RenderClear(Renderer);
2306 SDL_RenderPresent(Renderer);
2307 SDL_FillRect(SDL_VideoSurface, NULL, SDL_MapRGB(SDL_VideoSurface->format, 0, 0, 0));
2308 return 0;
2309 }
2310 #else
gfx_set_mode(int w,int h,int fs)2311 int gfx_set_mode(int w, int h, int fs)
2312 {
2313 int hw = (software_sw)?0:SDL_HWSURFACE;
2314 SDL_Surface *scr;
2315 game_reset_name();
2316 if (gfx_width == w && gfx_height == h && gfx_fs == fs) {
2317 return 0; /* already done */
2318 }
2319 vid_modes = NULL;
2320 gfx_fs = fs;
2321 gfx_width = w;
2322 gfx_height = h;
2323 if (!nocursor_sw)
2324 SDL_ShowCursor(SDL_DISABLE);
2325 #ifdef S60
2326 scr = SDL_SetVideoMode(gfx_width, gfx_height, 0, SDL_ANYFORMAT | hw | ( ( fs ) ? SDL_FULLSCREEN : 0 ) );
2327 #else
2328 #ifdef ANDROID
2329 scr = SDL_SetVideoMode(gfx_width, gfx_height, 0, hw | ( ( fs ) ? SDL_FULLSCREEN : 0 ) );
2330 #else
2331 #ifdef MAEMO
2332 scr = SDL_SetVideoMode(gfx_width, gfx_height, 16, SDL_DOUBLEBUF | hw | ( ( fs ) ? SDL_FULLSCREEN : 0 ) );
2333 #else
2334 #ifdef __APPLE__
2335 scr = SDL_SetVideoMode(gfx_width, gfx_height, (fs)?32:0, SDL_SWSURFACE | ( ( fs ) ? SDL_FULLSCREEN : 0 ) );
2336 if (scr == NULL) /* ok, fallback to anyformat */
2337 scr = SDL_SetVideoMode(gfx_width, gfx_height, 0, SDL_ANYFORMAT | SDL_SWSURFACE | ( ( fs ) ? SDL_FULLSCREEN : 0 ) );
2338 #else
2339 #if !defined(_WIN32_WCE) && !defined(WINRT)
2340 #if SDL_VERSION_ATLEAST(1,3,0)
2341 scr = SDL_SetVideoMode(gfx_width, gfx_height, 32, SDL_DOUBLEBUF | hw | ( ( fs ) ? SDL_FULLSCREEN : 0 ) );
2342 #else
2343 scr = SDL_SetVideoMode(gfx_width, gfx_height, (fs)?32:0, SDL_DOUBLEBUF | hw | ( ( fs ) ? SDL_FULLSCREEN : 0 ) );
2344 #endif
2345 if (scr == NULL) /* ok, fallback to anyformat */
2346 #endif
2347 scr = SDL_SetVideoMode(gfx_width, gfx_height, 0, SDL_ANYFORMAT | hw | ( ( fs ) ? SDL_FULLSCREEN : 0 ) );
2348 #endif
2349 #endif
2350 #endif
2351 #endif
2352 screen = GFX_IMG_REL(scr);
2353 if (scr == NULL || screen == NULL) {
2354 fprintf(stderr, "Unable to set %dx%d video: %s\n", w, h, SDL_GetError());
2355 return -1;
2356 }
2357 fprintf(stderr,"Video mode: %dx%d@%dbpp\n", scr->w, scr->h, scr->format->BitsPerPixel);
2358 gfx_clear(0, 0, gfx_width, gfx_height);
2359 return 0;
2360 }
2361 #endif
gfx_video_init(void)2362 int gfx_video_init(void)
2363 {
2364 #if !SDL_VERSION_ATLEAST(2,0,0)
2365 char title[4096];
2366
2367 strcpy( title, "INSTEAD - " );
2368 strcat( title, VERSION );
2369 #endif
2370 if (TTF_Init()) {
2371 fprintf(stderr, "Can't init TTF subsystem.\n");
2372 return -1;
2373 }
2374 #if !SDL_VERSION_ATLEAST(2,0,0)
2375 SDL_WM_SetCaption( title, title );
2376 #endif
2377 #ifndef ICON_PATH
2378 #define ICON_PATH "./icon"
2379 #endif
2380
2381 icon = IMG_Load( ICON_PATH"/sdl_instead_1.png" );
2382 #if !SDL_VERSION_ATLEAST(2,0,0)
2383 if ( icon ) {
2384 SDL_WM_SetIcon( icon, NULL );
2385 }
2386 #endif
2387 return 0;
2388 }
2389
2390 #if SDL_VERSION_ATLEAST(2,0,0)
2391
2392 static int queue_x1 = -1;
2393 static int queue_y1 = -1;
2394 static int queue_x2 = -1;
2395 static int queue_y2 = -1;
2396 static int queue_dirty = 0;
2397 static SDL_Texture *cursor = NULL;
2398
2399
2400 static int cursor_xc = 0;
2401 static int cursor_yc = 0;
2402
2403 static int cursor_w;
2404 static int cursor_h;
2405 static int cursor_on = 1;
2406
gfx_set_cursor(img_t cur,int xc,int yc)2407 void gfx_set_cursor(img_t cur, int xc, int yc)
2408 {
2409 if (cursor)
2410 SDL_DestroyTexture(cursor);
2411
2412 if (!cur) {
2413 cursor = NULL;
2414 cursor_w = 0;
2415 cursor_h = 0;
2416 return;
2417 }
2418 cursor = SDL_CreateTextureFromSurface(Renderer, Surf(cur));
2419
2420 if (!cursor)
2421 return;
2422
2423 cursor_w = gfx_img_w(cur);
2424 cursor_h = gfx_img_h(cur);
2425
2426 cursor_xc = xc;
2427 cursor_yc = yc;
2428 SDL_SetTextureBlendMode(cursor, SDL_BLENDMODE_BLEND);
2429 }
2430
gfx_show_cursor(int on)2431 void gfx_show_cursor(int on)
2432 {
2433 cursor_on = on;
2434 }
2435
gfx_render_copy(SDL_Texture * texture,SDL_Rect * dst,int clear)2436 static void gfx_render_copy(SDL_Texture *texture, SDL_Rect *dst, int clear)
2437 {
2438 #ifdef _USE_SWROTATE
2439 SDL_Rect r2;
2440 SDL_Point r;
2441 int w, h;
2442 if (gfx_flip_rotate) {
2443 if (clear)
2444 SDL_RenderClear(Renderer);
2445 SDL_QueryTexture(texture, NULL, NULL, &w, &h);
2446 r2.x = 0; r2.y = -h;
2447 r2.w = w; r2.h = h;
2448 r.x = 0; r.y = h;
2449 SDL_RenderCopyEx(Renderer, texture, NULL, &r2, 90, &r, 0);
2450 return;
2451 }
2452 #endif
2453 if (SDL_VideoRendererInfo.flags & SDL_RENDERER_ACCELERATED) {
2454 if (clear)
2455 SDL_RenderClear(Renderer);
2456 SDL_RenderCopy(Renderer, texture, NULL, NULL);
2457 } else
2458 SDL_RenderCopy(Renderer, texture, dst, dst);
2459 }
2460
gfx_render_cursor(void)2461 static void gfx_render_cursor(void)
2462 {
2463 int cursor_x = 0;
2464 int cursor_y = 0;
2465
2466 SDL_Rect rect;
2467 #ifdef _USE_SWROTATE
2468 SDL_Point r;
2469 #endif
2470 if (!cursor_on || !mouse_focus() || nocursor_sw)
2471 return;
2472
2473 gfx_cursor(&cursor_x, &cursor_y);
2474
2475 #ifdef _USE_SWROTATE
2476 if (gfx_flip_rotate) {
2477 int tmp = cursor_x;
2478 cursor_x = gfx_height - cursor_y;
2479 cursor_y = tmp;
2480 r.x = cursor_xc;
2481 r.y = cursor_yc;
2482 }
2483 #endif
2484
2485 cursor_x -= cursor_xc;
2486 cursor_y -= cursor_yc;
2487
2488 rect.x = cursor_x;
2489 rect.y = cursor_y;
2490 rect.w = cursor_w; /* - 1; */ /* SDL 2.0 hack? */
2491 rect.h = cursor_h; /* - 1; */
2492 #ifdef _USE_SWROTATE
2493 if (gfx_flip_rotate)
2494 SDL_RenderCopyEx(Renderer, cursor, NULL, &rect, 90, &r, 0);
2495 else
2496 #endif
2497 SDL_RenderCopy(Renderer, cursor, NULL, &rect);
2498 }
2499
2500 static void SDL_UpdateRect(SDL_Surface * screen, Sint32 x, Sint32 y, Uint32 w, Uint32 h);
2501
SDL_Flip(SDL_Surface * screen)2502 int SDL_Flip(SDL_Surface * screen)
2503 {
2504 SDL_Rect rect;
2505 int pitch, psize;
2506 unsigned char *pixels;
2507 if (!screen)
2508 return 0;
2509 pitch = screen->pitch;
2510 psize = screen->format->BytesPerPixel;
2511 pixels = screen->pixels;
2512 if (queue_dirty) {
2513 rect.x = queue_x1;
2514 rect.y = queue_y1;
2515 rect.w = queue_x2 - queue_x1;
2516 rect.h = queue_y2 - queue_y1;
2517
2518 pixels += pitch * queue_y1 + queue_x1 * psize;
2519 SDL_UpdateTexture(SDL_VideoTexture, &rect, pixels, pitch);
2520 gfx_render_copy(SDL_VideoTexture, &rect, 1);
2521 SDL_RenderPresent(Renderer);
2522 }
2523 queue_x1 = queue_y1 = queue_x2 = queue_y2 = -1;
2524 queue_dirty = 0;
2525 return 0;
2526 }
2527
SDL_UpdateRect(SDL_Surface * screen,Sint32 x,Sint32 y,Uint32 w,Uint32 h)2528 static void SDL_UpdateRect(SDL_Surface * screen, Sint32 x, Sint32 y, Uint32 w, Uint32 h)
2529 {
2530 if (queue_x1 < 0 || x < queue_x1)
2531 queue_x1 = x;
2532 if (queue_y1 < 0 || y < queue_y1)
2533 queue_y1 = y;
2534 if ((Sint32)(x + w) > queue_x2)
2535 queue_x2 = (Sint32)(x + w);
2536 if ((Sint32)(y + h) > queue_y2)
2537 queue_y2 = (Sint32)(y + h);
2538 queue_dirty = 1;
2539 }
2540 #else
gfx_set_cursor(img_t cur,int xc,int yc)2541 void gfx_set_cursor(img_t cur, int xc, int yc)
2542 {
2543 return;
2544 }
2545 #endif
2546
gfx_flip(void)2547 void gfx_flip(void)
2548 {
2549 #if SDL_VERSION_ATLEAST(2,0,0)
2550 queue_x1 = queue_y1 = 0;
2551 queue_x2 = gfx_width;
2552 queue_y2 = gfx_height;
2553 queue_dirty = 1;
2554 #else
2555 SDL_Flip(Surf(screen));
2556 #endif
2557 }
2558
gfx_resize(int w,int h)2559 void gfx_resize(int w, int h)
2560 {
2561 #if SDL_VERSION_ATLEAST(2,0,0)
2562 #if defined(ANDROID) || defined(IOS)
2563 current_gfx_w = w;
2564 current_gfx_h = h;
2565 #endif
2566 #endif
2567 }
2568
gfx_commit(void)2569 void gfx_commit(void)
2570 {
2571 #if SDL_VERSION_ATLEAST(2,0,0)
2572 SDL_Flip(Surf(screen));
2573 #endif
2574 }
2575
gfx_update(int x,int y,int w,int h)2576 void gfx_update(int x, int y, int w, int h) {
2577 if (x < 0) {
2578 w += x;
2579 x = 0;
2580 }
2581 if (y < 0) {
2582 h += y;
2583 y = 0;
2584 }
2585 if (w < 0 || h < 0)
2586 return;
2587 if (x >= gfx_width || y >= gfx_height)
2588 return;
2589 if (x + w > gfx_width) {
2590 w = gfx_width - x;
2591 }
2592 if (y + h > gfx_height) {
2593 h = gfx_height - y;
2594 }
2595 SDL_UpdateRect(Surf(screen), x, y, w, h);
2596 }
2597
gfx_video_done(void)2598 void gfx_video_done(void)
2599 {
2600 if (icon)
2601 SDL_FreeSurface(icon);
2602 screen = NULL;
2603 TTF_Quit();
2604 }
2605
gfx_scale(img_t src,float xscale,float yscale,int smooth)2606 img_t gfx_scale(img_t src, float xscale, float yscale, int smooth)
2607 {
2608 anim_t ag;
2609 if ((ag = is_anim(src))) {
2610 int i;
2611 int err = 0;
2612 img_t img = NULL;
2613 anim_t ag2;
2614 ag2 = anim_clone(ag);
2615
2616 if (!ag2)
2617 return NULL;
2618
2619 for (i = 0; i < ag->nr_frames; i ++) {
2620 SDL_Surface *s;
2621 s = (err) ? NULL : zoomSurface(ag->anim->frames[i], xscale, yscale, 1);
2622
2623 if (!s) {
2624 err ++;
2625 ag2->anim->frames[i] = NULL;
2626 continue;
2627 }
2628 ag2->anim->frames[i] = s;
2629 }
2630 if (err) {
2631 anim_free(ag2);
2632 return NULL;
2633 }
2634 anim_add(ag2); /* scaled anim added */
2635 img = gfx_new_img(ag2->anim->frames[0], IMG_ANIM, ag2, 0);
2636 if (!img) {
2637 anim_del(ag2);
2638 anim_free(ag2);
2639 }
2640 return img;
2641 }
2642 return GFX_IMG_REL(zoomSurface(Surf(src), xscale, yscale, smooth));
2643 }
2644
gfx_rotate(img_t src,float angle,int smooth)2645 img_t gfx_rotate(img_t src, float angle, int smooth)
2646 {
2647 anim_t ag;
2648
2649 if ((ag = is_anim(src))) {
2650 int i;
2651 int err = 0;
2652 img_t img = NULL;
2653
2654 anim_t ag2;
2655
2656 ag2 = anim_clone(ag);
2657
2658 if (!ag2)
2659 return NULL;
2660
2661 for (i = 0; i < ag->nr_frames; i ++) {
2662 SDL_Surface *s;
2663 s = (err) ? NULL : rotozoomSurface(ag->anim->frames[i], angle, 1.0f, smooth);
2664
2665 if (!s) {
2666 err ++;
2667 ag2->anim->frames[i] = NULL;
2668 continue;
2669 }
2670 ag2->anim->frames[i] = s;
2671 }
2672 if (err) {
2673 anim_free(ag2);
2674 return NULL;
2675 }
2676 anim_add(ag2); /* rotated anim added */
2677 img = gfx_new_img(ag2->anim->frames[0], IMG_ANIM, ag2, 0);
2678 if (!img) {
2679 anim_del(ag2);
2680 anim_free(ag2);
2681 }
2682 return img;
2683 }
2684 return GFX_IMG_REL(rotozoomSurface(Surf(src), angle, 1.0f, smooth));
2685 }
2686
2687 #define FN_REG 0
2688 #define FN_BOLD 1
2689 #define FN_ITALIC 2
2690 #define FN_ITALICBOLD 3
2691 #define FN_MAX 4
2692 struct fnt {
2693 TTF_Font *fn;
2694 TTF_Font *fonts[FN_MAX];
2695 int style;
2696 };
2697
2698 /* prefix{regular,italic, bold, bolditalic}.ttf */
parse_fn(const char * f,char * files[])2699 static int parse_fn(const char *f, char *files[])
2700 {
2701 int e;
2702 int nr = 0;
2703 int elen;
2704 const char *ep = f;
2705 const char *s = f;
2706
2707 int pref = strcspn(f, "{");
2708 if (!f[pref])
2709 goto no;
2710 f += pref + 1;
2711 ep = f;
2712 ep += strcspn(f, "}");
2713 if (!*ep)
2714 goto no;
2715 ep ++;
2716 elen = strlen(ep);
2717 while (1) {
2718 f += strspn(f, " \t");
2719 e = strcspn(f, ",}");
2720 if (!e) { /* empty subst */
2721 files[nr] = NULL;
2722 goto skip;
2723 }
2724 files[nr] = malloc(e + pref + elen + 1);
2725 if (!files[nr])
2726 break;
2727 if (pref)
2728 memcpy(files[nr], s, pref);
2729 if (e)
2730 memcpy(files[nr] + pref, f, e);
2731 if (elen)
2732 memcpy(files[nr] + pref + e, ep, elen);
2733 *(files[nr] + pref + e + elen) = 0;
2734 skip:
2735 nr ++;
2736 if (!f[e] || f[e] == '}')
2737 break;
2738 f += e + 1;
2739 if (nr >=4)
2740 break;
2741 }
2742 return nr;
2743 no:
2744 files[0] = strdup(s);
2745 return (files[0])?1:0;
2746 }
2747
2748 extern int hinting_sw;
2749
fnt_load(const char * fname,int size)2750 fnt_t fnt_load(const char *fname, int size)
2751 {
2752 TTF_Font *fn;
2753 struct fnt *h;
2754 int i, n = 0;
2755 char *files[4] = { NULL, NULL, NULL, NULL };
2756 h = malloc(sizeof(struct fnt));
2757 if (!h)
2758 return NULL;
2759 h->fonts[0] = h->fonts[1] = h->fonts[2] = h->fonts[3] = NULL;
2760 n = parse_fn(fname, files);
2761 if (!n)
2762 goto err;
2763 for (i = 0; i < n; i++) {
2764 fn = NULL;
2765 if (!is_empty(files[i])) {
2766 SDL_RWops *rw = RWFromIdf(instead_idf(), files[i]);
2767 if (!rw || !(fn = TTF_OpenFontRW(rw, 1, size))) {
2768 fprintf(stderr, "Can not load font: '%s'\n", files[i]);
2769 }
2770 }
2771 if (!fn && i == 0) /* no regular */
2772 goto err;
2773 #ifdef TTF_HINTING_LIGHT
2774 if (fn) {
2775 switch (hinting_sw) {
2776 case 0:
2777 TTF_SetFontHinting(fn, TTF_HINTING_NORMAL);
2778 break;
2779 case 1:
2780 TTF_SetFontHinting(fn, TTF_HINTING_LIGHT);
2781 break;
2782 case 2:
2783 TTF_SetFontHinting(fn, TTF_HINTING_MONO);
2784 break;
2785 case 3:
2786 TTF_SetFontHinting(fn, TTF_HINTING_NONE);
2787 break;
2788 default:
2789 break;
2790 }
2791 }
2792 #endif
2793 h->fonts[i] = fn;
2794 }
2795 h->fn = h->fonts[FN_REG];
2796 for (i = 0; i < n; i++)
2797 free(files[i]);
2798 return (fnt_t) h;
2799 err:
2800 for (i = 0; i < n; i++)
2801 free(files[i]);
2802 fnt_free(h);
2803 return NULL;
2804 }
2805
fnt_style(fnt_t fn,int style)2806 void fnt_style(fnt_t fn, int style)
2807 {
2808 struct fnt *h = (struct fnt*)fn;
2809 if (!h)
2810 return;
2811 h->style = style;
2812 if ((style & TTF_STYLE_BOLD) && (style & TTF_STYLE_ITALIC)) {
2813 if (h->fonts[FN_ITALICBOLD]) {
2814 h->fn = h->fonts[FN_ITALICBOLD];
2815 style &= ~TTF_STYLE_BOLD;
2816 style &= ~TTF_STYLE_ITALIC;
2817 } else
2818 h->fn = h->fonts[FN_REG];
2819 } else if ((style & TTF_STYLE_BOLD)) {
2820 if (h->fonts[FN_BOLD]) {
2821 h->fn = h->fonts[FN_BOLD];
2822 style &= ~TTF_STYLE_BOLD;
2823 } else
2824 h->fn = h->fonts[FN_REG];
2825 } else if ((style & TTF_STYLE_ITALIC)) {
2826 if (h->fonts[FN_ITALIC]) {
2827 h->fn = h->fonts[FN_ITALIC];
2828 style &= ~TTF_STYLE_ITALIC;
2829 } else
2830 h->fn = h->fonts[FN_REG];
2831 } else {
2832 h->fn = h->fonts[FN_REG];
2833 }
2834 TTF_SetFontStyle((TTF_Font *)h->fn, style);
2835 }
2836
fnt_render(fnt_t fn,const char * p,color_t col)2837 img_t fnt_render(fnt_t fn, const char *p, color_t col)
2838 {
2839 SDL_Color scol = { .r = col.r, .g = col.g, .b = col.b };
2840 struct fnt *h = (struct fnt*)fn;
2841 if (!h)
2842 return NULL;
2843 return GFX_IMG_REL(TTF_RenderUTF8_Blended((TTF_Font *)h->fn, p, scol));
2844 }
2845
fnt_height(fnt_t fn)2846 int fnt_height(fnt_t fn)
2847 {
2848 struct fnt *h = (struct fnt*)fn;
2849 if (!fn)
2850 return 0;
2851 return TTF_FontHeight((TTF_Font *)(h->fonts[FN_REG]));
2852 }
2853
fnt_free(fnt_t fnt)2854 void fnt_free(fnt_t fnt)
2855 {
2856 int i;
2857 struct fnt *h = (struct fnt*)fnt;
2858 if (!fnt)
2859 return;
2860 for (i = 0; i < FN_MAX; i++) {
2861 if (h->fonts[i])
2862 TTF_CloseFont((TTF_Font *)h->fonts[i]);
2863 }
2864 free(h);
2865 }
2866
txt_draw(fnt_t fnt,const char * txt,int x,int y,color_t col)2867 void txt_draw(fnt_t fnt, const char *txt, int x, int y, color_t col)
2868 {
2869 img_t s = fnt_render(fnt, txt, col);
2870 if (!s)
2871 return;
2872 gfx_draw(s, x, y);
2873 }
2874
2875 #if 0
2876 int txt_width(fnt_t fnt, const char *txt)
2877 {
2878 const char *p = txt;
2879 int c = 0;
2880 int w = 0;
2881 Uint16 u = 0;
2882 struct fnt *f = (struct fnt*)fnt;
2883 if (!f)
2884 return 0;
2885 while (*p) {
2886 if (!c) {
2887 if (! (*p & 0x80)) { /* ascii */
2888 c = 1;
2889 u = *p & 0x7f;
2890 } else {
2891 if ((*p & 0xe0) == 0xc0) {
2892 c = 2;
2893 u = *p & 0x1f;
2894 } else if ((*p & 0xf0) == 0xe0) {
2895 c = 3;
2896 u = *p & 0xf;
2897 } else if ((*p & 0xf8) == 0xf0) {
2898 c = 4;
2899 u = *p & 0x3;
2900 } else {
2901 c = 1;
2902 u = *p & 0x7f; /* fallback */
2903 }
2904 }
2905 } else {
2906 if ((*p & 0xc0) != 0x80) {
2907 c = 1;
2908 u = *p & 0x7f; /* fallback */
2909 } else {
2910 u <<= 6;
2911 u |= *p & 0x3f;
2912 }
2913 }
2914 c --;
2915 if (!c) {
2916 int adv = 0;
2917 TTF_GlyphMetrics(f->fn, u, NULL, NULL, NULL, NULL, &adv);
2918 w += adv;
2919 }
2920 p ++;
2921 }
2922 return w;
2923 }
2924 #endif
txt_size(fnt_t fnt,const char * txt,int * w,int * h)2925 void txt_size(fnt_t fnt, const char *txt, int *w, int *h)
2926 {
2927 int ww, hh;
2928 struct fnt *f = (struct fnt*)fnt;
2929 TTF_SizeUTF8((TTF_Font *)f->fn, txt, &ww, &hh);
2930 if (w)
2931 *w = ww;
2932 if (h)
2933 *h = hh;
2934 }
2935
2936 struct word;
2937 struct line;
2938 struct xref;
2939
2940 struct word {
2941 int style;
2942 int x;
2943 int w;
2944 int unbrake;
2945 int valign;
2946 int img_align;
2947
2948 /* Direction and Script (Language) of the word */
2949 int rtl;
2950 int script; /* See HarfBuzz hb_script_t */
2951 int isalpha; /* Whether this word contains alphabets */
2952
2953 char *word;
2954 img_t img;
2955 struct word *next; /* in line */
2956 struct word *prev; /* in line */
2957 struct line *line;
2958 struct xref *xref;
2959 img_t prerend;
2960 img_t hlprerend;
2961 };
2962
2963
word_image(word_t v)2964 img_t word_image(word_t v)
2965 {
2966 struct word *w = (struct word*)v;
2967 if (!w)
2968 return NULL;
2969 return w->img;
2970 }
2971
2972 #ifdef _USE_HARFBUZZ
hb_dir(int rtl)2973 static int hb_dir(int rtl)
2974 {
2975 return rtl ? HB_DIRECTION_RTL:HB_DIRECTION_LTR;
2976 }
2977 #endif
2978
2979 /* This function detects and configures direction, script and type of a word. */
word_detect_rtl(struct word * w,int mode)2980 static void word_detect_rtl(struct word *w, int mode)
2981 {
2982 #ifdef _USE_HARFBUZZ
2983 const char *str = w->word;
2984 int rc;
2985 unsigned long sym = 0;
2986 if (mode == 0) /* force ltr mode */
2987 return;
2988 /* Find the first alphanumeric utf8 character for a meaningful direction
2989 or use direction of the first character.
2990 */
2991 while ((rc = get_utf8(str, &sym))) {
2992 if (g_unichar_isalpha(sym))
2993 break;
2994 str += rc;
2995 }
2996 /* Is this made of alphabets? */
2997 w->isalpha = g_unichar_isalpha(sym);
2998 if (mode == 1) { /* force rtl */
2999 w->rtl = 1;
3000 w->script = HB_SCRIPT_COMMON;
3001 return;
3002 }
3003 switch(g_unichar_get_script(sym)) {
3004 case G_UNICODE_SCRIPT_HEBREW:
3005 w->rtl = !g_unichar_isdigit(sym);
3006 w->script = HB_SCRIPT_HEBREW;
3007 /* Fall through */
3008 case G_UNICODE_SCRIPT_ARABIC:
3009 w->rtl = !g_unichar_isdigit(sym);
3010 w->script = HB_SCRIPT_ARABIC;
3011 break;
3012 default:
3013 w->rtl = 0;
3014 w->script = HB_SCRIPT_COMMON;
3015 }
3016 #endif
3017 }
3018
word_new(const char * str)3019 struct word *word_new(const char *str)
3020 {
3021 struct word *w;
3022 w = malloc(sizeof(struct word));
3023 if (!w)
3024 return NULL;
3025 w->word = strdup(str);
3026 w->next = NULL;
3027 w->x = 0;
3028 w->w = 0;
3029 w->valign = 0;
3030 w->line = NULL;
3031 w->xref = NULL;
3032 w->style = 0;
3033 w->img = NULL;
3034 w->img_align = 0;
3035 w->unbrake = 0;
3036 w->prerend = NULL;
3037 w->hlprerend = NULL;
3038
3039 w->rtl = 0;
3040 w->script = 0;
3041 w->isalpha = 0;
3042
3043 return w;
3044 }
3045
3046 struct line {
3047 int x;
3048 int y;
3049 int h;
3050 int w;
3051 int num;
3052 int align;
3053 int pos;
3054 int tabx;
3055 int al_tabx;
3056 int taby;
3057 int al_taby;
3058
3059 /* Each line could be RTL or LTR regardless of its script */
3060 int rtl;
3061
3062 struct word *words;
3063 struct line *next;
3064 struct line *prev;
3065 struct layout *layout;
3066 };
3067
3068 static int vertical_align(struct word *w, int *hh);
3069
3070 static int word_pos_x(struct word *word);
3071
word_geom(word_t v,int * x,int * y,int * w,int * h)3072 int word_geom(word_t v, int *x, int *y, int *w, int *h)
3073 {
3074 int xx, yy, ww, hh;
3075 struct line *line;
3076 struct word *word = (struct word*)v;
3077 if (!word || !word->line)
3078 return -1;
3079 line = word->line;
3080 xx = word_pos_x(word);
3081 ww = word->w;
3082 yy = line->y;
3083 yy += vertical_align(v, &hh);
3084 if (x)
3085 *x = xx;
3086 if (y)
3087 *y = yy;
3088 if (w)
3089 *w = ww;
3090 if (h)
3091 *h = hh;
3092 return 0;
3093 }
3094
line_new(void)3095 struct line *line_new(void)
3096 {
3097 struct line *l;
3098 l = malloc(sizeof(struct line));
3099 if (!l)
3100 return NULL;
3101 l->words = NULL;
3102 l->next = NULL;
3103 l->prev = NULL;
3104 l->x = 0;
3105 l->w = 0;
3106 l->y = 0;
3107 l->h = 0;
3108 l->num = 0;
3109 l->tabx = -1;
3110 l->al_tabx = ALIGN_LEFT;
3111 l->taby = -1;
3112 l->al_taby = ALIGN_BOTTOM;
3113 l->layout = NULL;
3114 l->align = 0;
3115 l->pos = 0;
3116 l->rtl = 0;
3117 return l;
3118 }
3119
line_empty(struct line * line)3120 int line_empty(struct line *line)
3121 {
3122 struct word *w;
3123 w = line->words;
3124 while (w) {
3125 if (w->img_align) {
3126 w = w->next;
3127 continue;
3128 }
3129 return 0;
3130 }
3131 return 1;
3132 }
3133
line_margin(struct line * line)3134 static int line_margin(struct line *line)
3135 {
3136 struct word *w;
3137 w = line->words;
3138 while (w) {
3139 if (w->img_align)
3140 return 1;
3141 w = w->next;
3142 }
3143 return 0;
3144 }
3145
next_word(struct word * w)3146 static struct word *next_word(struct word *w)
3147 {
3148 while (w->next && w->next->img_align) /* skip margins */
3149 w = w->next;
3150 return w->next;
3151 }
3152
line_justify(struct line * line,int width)3153 void line_justify(struct line *line, int width)
3154 {
3155 int x = 0;
3156 int last_margin = 0;
3157 int last_unbrake = 0;
3158 struct word *w;
3159 int sp, spm, lw = 0;
3160 int lnum = 0;
3161 if (!line || line->num <= 1 /*|| width <= line->w*/)
3162 return;
3163 w = line->words;
3164 while (w) {
3165 lw += w->w;
3166 if (last_margin && w->unbrake)
3167 w->unbrake = last_unbrake;
3168
3169 if (!w->unbrake && !w->img_align)
3170 lnum ++;
3171
3172 if (!last_margin && w->img_align)
3173 last_unbrake = w->unbrake;
3174 last_margin = w->img_align;
3175 w = w->next;
3176 }
3177 if (lnum <=1 )
3178 return;
3179 w = line->words;
3180 sp = (width - lw) / (lnum - 1);
3181 spm = (width - lw) % (lnum - 1);
3182 while (w) {
3183 if (!w->img_align) {
3184 w->x = x;
3185 if (next_word(w) && next_word(w)->unbrake)
3186 x += w->w;
3187 else {
3188 x += w->w + sp + ((spm)?1:0);
3189 if (spm)
3190 spm --;
3191 }
3192 }
3193 w = w->next;
3194 }
3195 }
3196
line_right(struct line * line,int width)3197 void line_right(struct line *line, int width)
3198 {
3199 struct word *w;
3200 int sp;
3201 if (!line || line->num == 0)
3202 return;
3203 sp = width - line->w;
3204 w = line->words;
3205 while (w) {
3206 if (!w->img_align) {
3207 w->x += sp;
3208 }
3209 w = w->next;
3210 }
3211 }
line_center(struct line * line,int width)3212 void line_center(struct line *line, int width)
3213 {
3214 struct word *w;
3215 int sp;
3216 if (!line || line->num == 0)
3217 return;
3218 sp = (width - line->w)/2;
3219 w = line->words;
3220 while (w) {
3221 if (!w->img_align) {
3222 w->x += sp;
3223 }
3224 w = w->next;
3225 }
3226 }
3227
line_align(struct line * line,int width,int style,int nl)3228 void line_align(struct line *line, int width, int style, int nl)
3229 {
3230 if (style == ALIGN_JUSTIFY) {
3231 if (nl)
3232 return;
3233 line_justify(line, width);
3234 return;
3235 }
3236 if (style == ALIGN_CENTER) {
3237 line_center(line, width);
3238 return;
3239 }
3240 if (style == ALIGN_LEFT)
3241 return;
3242 if (style == ALIGN_RIGHT) {
3243 line_right(line, width);
3244 return;
3245 }
3246 }
3247
3248 void word_free(struct word *word);
3249
line_free(struct line * line)3250 void line_free(struct line *line)
3251 {
3252 struct word *w;
3253 if (!line)
3254 return;
3255 w = line->words;
3256 while (w) {
3257 struct word *ow = w;
3258 w = w->next;
3259 word_free(ow);
3260 }
3261 free(line);
3262 }
3263
line_add_word(struct line * l,struct word * word)3264 void line_add_word(struct line *l, struct word *word)
3265 {
3266 struct word *w = l->words;
3267 l->num ++;
3268 word->line = l;
3269 if (!l->words) {
3270 l->words = word;
3271 word->prev = word;
3272
3273 /* This is the first word in this line. Let's use its direction
3274 for the line too. Ideally, something like fribidi should be
3275 used for mixing directions however. */
3276 l->rtl = word->rtl;
3277 return;
3278 }
3279 w = w->prev;
3280 w->next = word;
3281 word->prev = w;
3282 l->words->prev = word;
3283 return;
3284 }
3285
3286 struct image;
3287 struct image {
3288 struct image *next;
3289 char *name;
3290 img_t image;
3291 int free_it;
3292 };
3293
image_new(const char * name,img_t img)3294 struct image *image_new(const char *name, img_t img)
3295 {
3296 struct image *g = malloc(sizeof(struct image));
3297 if (!g)
3298 return NULL;
3299 g->image = img;
3300 g->name = strdup(name);
3301 g->next = NULL;
3302 g->free_it = 0;
3303 return g;
3304 }
3305
image_free(struct image * image)3306 void image_free(struct image *image)
3307 {
3308 if (!image)
3309 return;
3310 if (image->name)
3311 free(image->name);
3312 if (image->free_it)
3313 gfx_free_image(image->image);
3314 free(image);
3315 }
3316
3317 struct textbox;
3318
3319 #define ALIGN_NEST 16
3320 struct margin;
3321
3322 struct margin {
3323 struct margin *next;
3324 int w;
3325 int h;
3326 int y;
3327 int align;
3328 struct word *word;
3329 };
3330
margin_new(void)3331 struct margin *margin_new(void)
3332 {
3333 struct margin *m = malloc(sizeof(struct margin));
3334 if (!m)
3335 return NULL;
3336 m->w = m->h = m->align = 0;
3337 m->next = NULL;
3338 return m;
3339 }
3340
margin_free(struct margin * m)3341 void margin_free(struct margin *m)
3342 {
3343 if (m)
3344 free(m);
3345 }
3346
3347 struct layout {
3348 fnt_t fn;
3349 float fn_height;
3350 color_t col;
3351 color_t lcol;
3352 color_t acol;
3353 struct image *images;
3354 struct xref *xrefs;
3355 struct line *lines;
3356 struct line *anchor;
3357 struct textbox *box;
3358 struct margin *margin;
3359 int w;
3360 int h;
3361 int align;
3362 int valign;
3363 int saved_align[ALIGN_NEST];
3364 int saved_valign[ALIGN_NEST];
3365 int acnt;
3366 int vcnt;
3367 int style;
3368 int rtl;
3369 int scnt[4];
3370 int lstyle;
3371 cache_t img_cache;
3372 cache_t prerend_cache;
3373 cache_t hlprerend_cache;
3374 };
3375
3376 struct xref {
3377 struct xref *next;
3378 struct xref *prev;
3379 struct word **words;
3380 struct layout *layout;
3381 char *link;
3382 int num;
3383 int active;
3384 };
3385 struct textbox {
3386 struct layout *lay;
3387 struct line *line;
3388 int off;
3389 int w;
3390 int h;
3391 };
3392
word_free(struct word * word)3393 void word_free(struct word *word)
3394 {
3395 if (!word)
3396 return;
3397 /* if (word->img)
3398 gfx_free_image(word->img); */
3399 if (word->word)
3400 free(word->word);
3401
3402 if (word->prerend) {
3403 cache_forget(word->line->layout->prerend_cache, word->prerend);
3404 /* SDL_FreeSurface(word->prerend); */
3405 }
3406
3407 if (word->hlprerend) {
3408 cache_forget(word->line->layout->hlprerend_cache, word->hlprerend);
3409 /* SDL_FreeSurface(word->hlprerend); */
3410 }
3411 word->hlprerend = word->prerend = NULL;
3412 free(word);
3413 }
3414
xref_new(char * link)3415 struct xref *xref_new(char *link)
3416 {
3417 struct xref *p;
3418 p = malloc(sizeof(struct xref));
3419 if (!p)
3420 return NULL;
3421 if (link)
3422 p->link = strdup(link);
3423 else
3424 p->link = NULL;
3425 p->num = 0;
3426 p->layout = NULL;
3427 p->next = NULL;
3428 p->prev = NULL;
3429 p->active = 0;
3430 p->words = NULL;
3431 return p;
3432 }
3433
xref_add_word(struct xref * xref,struct word * word)3434 int xref_add_word(struct xref *xref, struct word *word)
3435 {
3436 struct word **new_words;
3437
3438 new_words = realloc(xref->words, (xref->num + 1) * sizeof(struct word*));
3439 if (!new_words)
3440 return -1;
3441
3442 xref->words = new_words;
3443 xref->words[xref->num ++] = word;
3444 word->xref = xref;
3445
3446 return 0;
3447 }
3448
xref_free(struct xref * xref)3449 void xref_free(struct xref *xref)
3450 {
3451 if (xref->link)
3452 free(xref->link);
3453 if (xref->words)
3454 free(xref->words);
3455 free(xref);
3456 }
3457
layout_add_line(struct layout * layout,struct line * line)3458 void layout_add_line(struct layout *layout, struct line *line)
3459 {
3460 struct line *l = layout->lines;
3461 line->layout = layout;
3462 if (!l) {
3463 layout->lines = line;
3464 line->prev = line;
3465 return;
3466 }
3467 l = l->prev;
3468 l->next = line;
3469 line->prev = l;
3470 layout->lines->prev = line;
3471 return;
3472 }
3473
layout_add_margin(struct layout * layout,struct margin * margin)3474 void layout_add_margin(struct layout *layout, struct margin *margin)
3475 {
3476 struct margin *m = layout->margin;
3477 if (!m) {
3478 layout->margin = margin;
3479 return;
3480 }
3481 while (m->next)
3482 m = m->next;
3483 m->next = margin;
3484 return;
3485 }
3486
3487 #if 1
layout_skip_margin(struct layout * layout,int y)3488 static int layout_skip_margin(struct layout *layout, int y)
3489 {
3490 struct margin *m = layout->margin;
3491 int my = y;
3492
3493 if (!m)
3494 return y;
3495
3496 while (m) {
3497 if (m->y + m->h > my)
3498 my = m->y + m->h;
3499 m = m->next;
3500 }
3501 if (y < my)
3502 y = my;
3503 return y;
3504 }
3505 #endif
3506
margin_rebase(struct layout * layout)3507 static void margin_rebase(struct layout *layout)
3508 {
3509 struct margin *m = layout->margin;
3510 if (!m)
3511 return;
3512
3513 while (m) {
3514 m->y = m->word->line->y;
3515 m = m->next;
3516 }
3517 }
3518
layout_find_margin(struct layout * layout,int y,int * w)3519 static int layout_find_margin(struct layout *layout, int y, int *w)
3520 {
3521 struct margin *m = layout->margin;
3522 int xpos = 0;
3523 int rpos = layout->w;
3524 if (!m) {
3525 if (w)
3526 *w = layout->w;
3527 return 0;
3528 }
3529 while (m) {
3530 if (y >= m->y && y < m->y + m->h) {
3531 if (m->align == ALIGN_LEFT)
3532 xpos = (xpos < m->w)?m->w:xpos;
3533 else
3534 rpos = (rpos > layout->w - m->w)?layout->w - m->w:rpos;
3535 }
3536 m = m->next;
3537 }
3538 if (w)
3539 *w = rpos - xpos;
3540 return xpos;
3541 }
3542
_layout_lookup_image(struct layout * layout,const char * name)3543 struct image *_layout_lookup_image(struct layout *layout, const char *name)
3544 {
3545 struct image *g;
3546 for (g = layout->images; g; g = g->next) {
3547 if (!strcmp(g->name, name))
3548 return g;
3549 }
3550 return NULL;
3551 }
3552
txt_layout_images(layout_t lay,void ** v)3553 img_t txt_layout_images(layout_t lay, void **v)
3554 {
3555 struct image **g = (struct image **)v;
3556 struct layout *layout = (struct layout *)lay;
3557
3558 if (!layout)
3559 return NULL;
3560
3561 if (!*v)
3562 *v = layout->images;
3563 else
3564 *v = (*g)->next;
3565 if (!*v)
3566 return NULL;
3567 return (*g)->image;
3568 }
3569
txt_layout_box(layout_t lay)3570 textbox_t txt_layout_box(layout_t lay)
3571 {
3572 struct layout *layout = (struct layout *)lay;
3573 return layout->box;
3574 }
3575
layout_add_image(struct layout * layout,struct image * image)3576 void layout_add_image(struct layout *layout, struct image *image)
3577 {
3578 struct image *g = layout->images;
3579 if (!g) {
3580 layout->images = image;
3581 return;
3582 }
3583 while (g->next)
3584 g = g->next;
3585 g->next = image;
3586 return;
3587 }
3588
layout_lookup_image(struct layout * layout,char * name)3589 img_t layout_lookup_image(struct layout *layout, char *name)
3590 {
3591 struct image *g = _layout_lookup_image(layout, name);
3592 return (g)?g->image: NULL;
3593 }
3594
layout_add_xref(struct layout * layout,struct xref * xref)3595 void layout_add_xref(struct layout *layout, struct xref *xref)
3596 {
3597 struct xref *x = layout->xrefs;
3598 xref->layout = layout;
3599 if (!x) {
3600 layout->xrefs = xref;
3601 xref->prev = xref;
3602 return;
3603 }
3604 x = x->prev;
3605 x->next = xref;
3606 xref->prev = x;
3607 layout->xrefs->prev = xref;
3608 return;
3609 }
3610
sdl_surface_free(void * p)3611 static void sdl_surface_free(void *p)
3612 {
3613 gfx_free_img((img_t)p);
3614 }
3615
layout_new(fnt_t fn,int w,int h)3616 struct layout *layout_new(fnt_t fn, int w, int h)
3617 {
3618 struct layout *l;
3619 l = malloc(sizeof(struct layout));
3620 if (!l)
3621 return NULL;
3622 l->lines = NULL;
3623 l->anchor = NULL;
3624 l->images = NULL;
3625 l->w = w;
3626 l->h = h;
3627 l->fn = fn;
3628 l->fn_height = 1.0f;
3629 l->align = ALIGN_JUSTIFY;
3630 l->valign = 0;
3631 l->style = 0;
3632 l->lstyle = 0;
3633 l->rtl = 0;
3634 l->xrefs = NULL;
3635 l->margin = NULL;
3636 l->col = gfx_col(0, 0, 0);
3637 l->lcol = gfx_col(0, 0, 255);
3638 l->acol = gfx_col(255, 0, 0);
3639 l->box = NULL;
3640 l->img_cache = cache_init(0, gfx_cache_free_image);
3641 l->prerend_cache = cache_init(0, sdl_surface_free);
3642 l->hlprerend_cache = cache_init(0, sdl_surface_free);
3643 memset(l->scnt, 0, sizeof(l->scnt));
3644 memset(l->saved_align, 0, sizeof(l->saved_align));
3645 memset(l->saved_valign, 0, sizeof(l->saved_valign));
3646 l->acnt = 0;
3647 l->vcnt = 0;
3648 return l;
3649 }
txt_layout_size(layout_t lay,int * w,int * h)3650 void txt_layout_size(layout_t lay, int *w, int *h)
3651 {
3652 struct layout *layout = (struct layout *)lay;
3653 if (!lay)
3654 return;
3655 if (w)
3656 *w = layout->w;
3657 if (h)
3658 *h = layout->h;
3659 }
3660
txt_layout_set_size(layout_t lay,int w,int h)3661 void txt_layout_set_size(layout_t lay, int w, int h)
3662 {
3663 struct layout *layout = (struct layout *)lay;
3664 if (!lay)
3665 return;
3666 layout->w = w;
3667 layout->h = h;
3668 }
3669
txt_layout_add_img(layout_t lay,const char * name,img_t img)3670 int txt_layout_add_img(layout_t lay, const char *name, img_t img)
3671 {
3672 struct layout *layout = (struct layout *)lay;
3673 struct image *image;
3674 image = _layout_lookup_image(layout, name);
3675 if (image) { /* overwrite */
3676 image->image = img;
3677 return 0;
3678 }
3679 image = image_new(name, img);
3680 if (!image)
3681 return -1;
3682 layout_add_image(layout, image);
3683 return 0;
3684 }
3685
_txt_layout_free(layout_t lay)3686 void _txt_layout_free(layout_t lay)
3687 {
3688 struct layout *layout = (struct layout *)lay;
3689 struct line *l;
3690 struct image *g;
3691 struct xref *x;
3692 struct margin *m;
3693 if (!layout)
3694 return;
3695 l = layout->lines;
3696 while (l) {
3697 struct line *ol = l;
3698 l = l->next;
3699 line_free(ol);
3700 }
3701 x = layout->xrefs;
3702 while (x) {
3703 struct xref *ox = x;
3704 x = x->next;
3705 xref_free(ox);
3706 }
3707 m = layout->margin;
3708 while (m) {
3709 struct margin *om = m;
3710 m = m->next;
3711 margin_free(om);
3712 }
3713 g = layout->images;
3714 while (g) {
3715 struct image *og = g;
3716 g = g->next;
3717 if (!cache_have(layout->img_cache, og->image))
3718 og->free_it = 0; /* do not free from cache */
3719 cache_forget(layout->img_cache, og->image);
3720 image_free(og);
3721 }
3722 layout->images = NULL;
3723 layout->xrefs = NULL;
3724 layout->lines = NULL;
3725 layout->anchor = NULL;
3726 layout->margin = NULL;
3727
3728 memset(layout->scnt, 0, sizeof(layout->scnt));
3729 memset(layout->saved_align, 0, sizeof(layout->saved_align));
3730 memset(layout->saved_valign, 0, sizeof(layout->saved_valign));
3731 layout->acnt = 0;
3732 layout->vcnt = 0;
3733 }
3734
txt_layout_words_ex(layout_t lay,struct line * line,word_t v,int off,int height)3735 word_t txt_layout_words_ex(layout_t lay, struct line *line, word_t v, int off, int height)
3736 {
3737 struct layout *layout = (struct layout*)lay;
3738 struct word *w = (struct word*)v;
3739
3740 if (!lay)
3741 return NULL;
3742
3743 if (!w) {
3744 if (!line)
3745 line = layout->lines;
3746 if (!line)
3747 return NULL;
3748 w = line->words;
3749 } else {
3750 line = w->line;
3751 w = w->next;
3752 }
3753
3754 for (; (!w || (line->y + line->h) < off) && line->next; line = line->next, w = line->words);
3755 if ((line->y + line->h) < off)
3756 w = NULL;
3757 else if (height >= 0 && line->y - off > height)
3758 w = NULL;
3759 return w;
3760 }
3761
txt_layout_words(layout_t lay,word_t v)3762 word_t txt_layout_words(layout_t lay, word_t v)
3763 {
3764 return txt_layout_words_ex(lay, NULL, v, 0, -1);
3765 }
3766
txt_layout_free(layout_t lay)3767 void txt_layout_free(layout_t lay)
3768 {
3769 struct layout *layout = (struct layout *)lay;
3770 _txt_layout_free(lay);
3771 if (lay) {
3772 cache_free(layout->img_cache);
3773 cache_free(layout->prerend_cache);
3774 cache_free(layout->hlprerend_cache);
3775 layout->img_cache = NULL;
3776 layout->prerend_cache = NULL;
3777 layout->hlprerend_cache = NULL;
3778 free(lay);
3779 }
3780 }
3781
3782 #define TOKEN_NONE 0x000
3783 #define TOKEN_A 0x001
3784 #define TOKEN_B 0x002
3785 #define TOKEN_I 0x004
3786 #define TOKEN_U 0x008
3787 #define TOKEN_S 0x010
3788 #define TOKEN_C 0x020
3789 #define TOKEN_R 0x040
3790 #define TOKEN_J 0x080
3791 #define TOKEN_L 0x100
3792 #define TOKEN_T 0x200
3793 #define TOKEN_D 0x400
3794 #define TOKEN_M 0x800
3795 #define TOKEN_X 0x1000
3796 #define TOKEN_Y 0x2000
3797 #define TOKEN_CLOSE 0x4000
3798 #define TOKEN(x) (x & 0x3fff)
3799
gfx_get_token(const char * ptr,char ** eptr,char ** val,int * sp)3800 int gfx_get_token(const char *ptr, char **eptr, char **val, int *sp)
3801 {
3802 int y_token = 0;
3803 char *ep, *p;
3804 int closing = 0;
3805 if (eptr)
3806 *eptr = NULL;
3807 p = (char*)ptr;
3808 ptr += strspn(ptr, " \t");
3809 if (sp) {
3810 *sp = 0;
3811 if (p != ptr)
3812 *sp = 1;
3813 }
3814 if (val)
3815 *val = NULL;
3816 if (!*ptr)
3817 return 0;
3818 if (*ptr != '<')
3819 return 0;
3820 ptr ++;
3821 if (*ptr == '/') {
3822 closing = 1;
3823 if (!ptr[1] || ptr[2] != '>')
3824 return 0;
3825 ptr ++;
3826 }
3827 switch (*ptr) {
3828 case 'y':
3829 y_token = 1;
3830 /* Fall through */
3831 case 'x':
3832 if (ptr[1] != ':')
3833 return 0;
3834 ptr += 2;
3835 ep = find_in_esc(ptr, "\\>");
3836 if (*ep != '>')
3837 return 0;
3838 if (val) {
3839 p = malloc(ep - ptr + 1);
3840 if (!p)
3841 return 0;
3842 memcpy(p, ptr, ep - ptr);
3843 p[ep - ptr] = 0;
3844 *val = p;
3845 }
3846 if (eptr)
3847 *eptr = ep + 1;
3848 return (y_token)?TOKEN_Y:TOKEN_X;
3849 case 'a':
3850 if (closing) {
3851 if (eptr)
3852 *eptr = (char*)ptr + 2;
3853 return TOKEN_A | TOKEN_CLOSE;
3854 }
3855 if (ptr[1] != ':') {
3856 return 0;
3857 }
3858 ptr += 2;
3859 /* ep = (char*)ptr + strcspn(ptr, ">"); */
3860 ep = find_in_esc(ptr, "\\>");
3861 if (!ep || *ep != '>') {
3862 return 0;
3863 }
3864 if (val) {
3865 p = malloc(ep - ptr + 1);
3866 if (!p)
3867 return 0;
3868 memcpy(p, ptr, ep - ptr);
3869 p[ep - ptr] = 0;
3870 parse_esc_string(p, val);
3871 if (*val)
3872 free(p);
3873 else
3874 *val = p;
3875 }
3876 if (eptr)
3877 *eptr = ep + 1;
3878 return TOKEN_A;
3879 case 'b':
3880 if (closing) {
3881 if (eptr)
3882 *eptr = (char*)ptr + 2;
3883 return TOKEN_B | TOKEN_CLOSE;
3884 }
3885 if (ptr[1] == '>') {
3886 if (eptr)
3887 *eptr = (char*)ptr + 2;
3888 return TOKEN_B;
3889 }
3890 break;
3891 case 'i':
3892 if (closing) {
3893 if (eptr)
3894 *eptr = (char*)ptr + 2;
3895 return TOKEN_I | TOKEN_CLOSE;
3896 }
3897 if (ptr[1] == '>') {
3898 if (eptr)
3899 *eptr = (char*)ptr + 2;
3900 return TOKEN_I;
3901 }
3902 break;
3903 case 's':
3904 if (closing) {
3905 if (eptr)
3906 *eptr = (char*)ptr + 2;
3907 return TOKEN_S | TOKEN_CLOSE;
3908 }
3909 if (ptr[1] == '>') {
3910 if (eptr)
3911 *eptr = (char*)ptr + 2;
3912 return TOKEN_S;
3913 }
3914 break;
3915 case 't':
3916 if (closing) {
3917 if (eptr)
3918 *eptr = (char*)ptr + 2;
3919 return TOKEN_T | TOKEN_CLOSE;
3920 }
3921 if (ptr[1] == '>') {
3922 if (eptr)
3923 *eptr = (char*)ptr + 2;
3924 return TOKEN_T;
3925 }
3926 break;
3927 case 'd':
3928 if (closing) {
3929 if (eptr)
3930 *eptr = (char*)ptr + 2;
3931 return TOKEN_D | TOKEN_CLOSE;
3932 }
3933 if (ptr[1] == '>') {
3934 if (eptr)
3935 *eptr = (char*)ptr + 2;
3936 return TOKEN_D;
3937 }
3938 break;
3939 case 'm':
3940 if (closing) {
3941 if (eptr)
3942 *eptr = (char*)ptr + 2;
3943 return TOKEN_M | TOKEN_CLOSE;
3944 }
3945 if (ptr[1] == '>') {
3946 if (eptr)
3947 *eptr = (char*)ptr + 2;
3948 return TOKEN_M;
3949 }
3950 break;
3951 case 'u':
3952 if (closing) {
3953 if (eptr)
3954 *eptr = (char*)ptr + 2;
3955 return TOKEN_U | TOKEN_CLOSE;
3956 }
3957 if (ptr[1] == '>') {
3958 if (eptr)
3959 *eptr = (char*)ptr + 2;
3960 return TOKEN_U;
3961 }
3962 break;
3963 case 'c':
3964 if (closing) {
3965 if (eptr)
3966 *eptr = (char*)ptr + 2;
3967 return TOKEN_C | TOKEN_CLOSE;
3968 }
3969 if (ptr[1] == '>') {
3970 if (eptr)
3971 *eptr = (char*)ptr + 2;
3972 return TOKEN_C;
3973 }
3974 break;
3975 case 'r':
3976 if (closing) {
3977 if (eptr)
3978 *eptr = (char*)ptr + 2;
3979 return TOKEN_R | TOKEN_CLOSE;
3980 }
3981 if (ptr[1] == '>') {
3982 if (eptr)
3983 *eptr = (char*)ptr + 2;
3984 return TOKEN_R;
3985 }
3986 break;
3987 case 'j':
3988 if (closing) {
3989 if (eptr)
3990 *eptr = (char*)ptr + 2;
3991 return TOKEN_J | TOKEN_CLOSE;
3992 }
3993 if (ptr[1] == '>') {
3994 if (eptr)
3995 *eptr = (char*)ptr + 2;
3996 return TOKEN_J;
3997 }
3998 break;
3999 case 'l':
4000 if (closing) {
4001 if (eptr)
4002 *eptr = (char*)ptr + 2;
4003 return TOKEN_L | TOKEN_CLOSE;
4004 }
4005 if (ptr[1] == '>') {
4006 if (eptr)
4007 *eptr = (char*)ptr + 2;
4008 return TOKEN_L;
4009 }
4010 break;
4011 }
4012 return 0;
4013 }
4014
is_delim(int c)4015 static int is_delim(int c)
4016 {
4017 switch(c) {
4018 case '.':
4019 case ',':
4020 case ':':
4021 case '!':
4022 case '+':
4023 case '-':
4024 case '?':
4025 case '/':
4026 return 1;
4027 }
4028 return 0;
4029 }
4030
process_word_token(const char * p,char ** eptr,char ch)4031 static int process_word_token(const char *p, char **eptr, char ch)
4032 {
4033 char *ep;
4034 if (eptr)
4035 *eptr = (char*)p;
4036 if (!p)
4037 return 0;
4038 if (p[0] != '<' || p[1] != ch || p[2] != ':')
4039 return 0;
4040 p += 3;
4041 ep = find_in_esc(p, "\\>");
4042 if (*ep != '>')
4043 return 0;
4044 if (eptr)
4045 *eptr = ep + 1;
4046 return (ep - p + 1);
4047 }
4048
word_img(const char * p,char ** eptr)4049 static int word_img(const char *p, char **eptr)
4050 {
4051 return process_word_token(p, eptr, 'g');
4052 }
4053
word_token(const char * p,char ** eptr)4054 static int word_token(const char *p, char **eptr)
4055 {
4056 return process_word_token(p, eptr, 'w');
4057 }
4058
lookup_cjk(const char * ptr,int limit)4059 static int lookup_cjk(const char *ptr, int limit)
4060 {
4061 unsigned long sym;
4062 int off = 0, rc;
4063 while ((rc = get_utf8(ptr, &sym))) {
4064 if (is_cjk(sym))
4065 return off;
4066 off += rc;
4067 ptr += rc;
4068 if (off >= limit)
4069 break;
4070 }
4071 return off;
4072 }
4073
cjk_here(const char * ptr)4074 static int cjk_here(const char *ptr)
4075 {
4076 unsigned long sym;
4077 int rc;
4078 rc = get_utf8(ptr, &sym);
4079 if (is_cjk(sym))
4080 return rc;
4081 return 0;
4082 }
4083
lookup_token_or_sp(const char * ptr)4084 static const char *lookup_token_or_sp(const char *ptr)
4085 {
4086 char *eptr;
4087 const char *p = ptr;
4088 while (*p) {
4089 int cjk, rc;
4090 rc = strcspn(p, " .,:!+-?/<\t\n");
4091 cjk = lookup_cjk(p, rc);
4092 if (p[cjk] && cjk < rc) { /* cjk symbol found! */
4093 rc = cjk;
4094 if (!rc)
4095 rc += get_utf8(p, NULL);
4096 }
4097 p += rc;
4098 if (*p != '<' ) {
4099 while (is_delim(*p))
4100 p ++;
4101 /* if (is_delim(*p))
4102 p ++; */
4103 return p;
4104 }
4105
4106 if (!gfx_get_token(p, &eptr, NULL, NULL)) {
4107 if (word_img(p, &eptr)) {
4108 if (p == ptr) /* first one */
4109 p = eptr;
4110 return p;
4111 } else if (word_token(p, &eptr)) {
4112 if (p == ptr) /* first one */
4113 p = eptr;
4114 return p;
4115 }
4116 p ++;
4117 continue;
4118 }
4119 return p;
4120 }
4121 return ptr;
4122 }
4123
4124 #define BREAK_NONE 0
4125 #define BREAK_SPACE 1
4126
get_word(const char * ptr,char ** eptr,int * sp)4127 static char *get_word(const char *ptr, char **eptr, int *sp)
4128 {
4129 const char *ep;
4130 char *o;
4131 size_t sz;
4132 *eptr = NULL;
4133 o = (char*)ptr;
4134 ptr += strspn(ptr, " \t");
4135 if (sp) {
4136 *sp = BREAK_NONE;
4137 if (o != ptr)
4138 *sp = BREAK_SPACE;
4139 }
4140 if (!*ptr)
4141 return NULL;
4142
4143 ep = lookup_token_or_sp(ptr);
4144 /* ep += strcspn(ep, " \t\n"); */
4145 sz = ep - ptr;
4146 o = malloc(sz + 1);
4147 memcpy(o, ptr, sz);
4148 o[sz] = 0;
4149
4150 sz = word_img(ptr, eptr);
4151 if (sz)
4152 return o;
4153 sz = word_token(ptr, eptr);
4154 if (sz)
4155 return o;
4156 *eptr = (char*)ep;
4157 return o;
4158 }
4159
layout_debug(struct layout * layout)4160 void layout_debug(struct layout *layout)
4161 {
4162 struct line *line;
4163 struct word *word;
4164 line = layout->lines;
4165 while (line) {
4166 printf("%d of %d)", line->y, line->num);
4167 word = line->words;
4168 while (word) {
4169 printf("%d)%s ", word->x, word->word);
4170 word = word->next;
4171 }
4172 printf("\n");
4173 line = line->next;
4174 }
4175 }
4176
txt_layout_color(layout_t lay,color_t fg)4177 void txt_layout_color(layout_t lay, color_t fg)
4178 {
4179 struct layout *layout = (struct layout*)lay;
4180 if (!lay)
4181 return;
4182 layout->col = fg;
4183 }
4184
txt_layout_rtl(layout_t lay,int rtl)4185 void txt_layout_rtl(layout_t lay, int rtl)
4186 {
4187 struct layout *layout = (struct layout*)lay;
4188 if (!lay)
4189 return;
4190 layout->rtl = rtl;
4191 }
4192
txt_layout_font_height(layout_t lay,float height)4193 void txt_layout_font_height(layout_t lay, float height)
4194 {
4195 struct layout *layout = (struct layout*)lay;
4196 if (!lay)
4197 return;
4198 layout->fn_height = height;
4199 }
4200
txt_layout_link_color(layout_t lay,color_t link)4201 void txt_layout_link_color(layout_t lay, color_t link)
4202 {
4203 struct layout *layout = (struct layout*)lay;
4204 if (!lay)
4205 return;
4206 layout->lcol = link;
4207 }
txt_layout_active_color(layout_t lay,color_t link)4208 void txt_layout_active_color(layout_t lay, color_t link)
4209 {
4210 struct layout *layout = (struct layout*)lay;
4211 if (!lay)
4212 return;
4213 layout->acol = link;
4214 }
txt_layout_link_style(layout_t lay,int style)4215 void txt_layout_link_style(layout_t lay, int style)
4216 {
4217 struct layout *layout = (struct layout*)lay;
4218 if (!lay)
4219 return;
4220 layout->lstyle = style;
4221 }
4222
word_cache_string(struct word * w,Uint32 style)4223 static char *word_cache_string(struct word *w, Uint32 style)
4224 {
4225 char *p;
4226 int len = 0;
4227 len = (w->word)?strlen(w->word):0;
4228 len += 16;
4229 p = malloc(len);
4230 if (!p)
4231 return NULL;
4232 snprintf(p, len, "%s-%08x", (w->word)?w->word:"", style);
4233 return p;
4234 }
4235
word_render(struct layout * layout,struct word * word,int x,int y)4236 static void word_render(struct layout *layout, struct word *word, int x, int y)
4237 {
4238 char *wc = NULL;
4239 img_t s;
4240 img_t prerend = NULL;
4241 img_t hlprerend = NULL;
4242 color_t fgcol = { .r = layout->col.r, .g = layout->col.g, .b = layout->col.b };
4243 color_t lcol = { .r = layout->lcol.r, .g = layout->lcol.g, .b = layout->lcol.b };
4244 color_t acol = { .r = layout->acol.r, .g = layout->acol.g, .b = layout->acol.b };
4245 Uint32 style;
4246
4247 if (!word->xref) {
4248 style = (fgcol.r << 24) + (fgcol.g << 16) + (fgcol.b << 8);
4249 } else if (word->xref->active) {
4250 style = (acol.r << 24) + (acol.g << 16) + (acol.b << 8);
4251 } else {
4252 style = (lcol.r << 24) + (lcol.g << 16) + (lcol.b << 8);
4253 }
4254
4255 if (word->xref && !word->style) {
4256 fnt_style(layout->fn, layout->lstyle);
4257 wc = word_cache_string(word, layout->lstyle | style);
4258 } else {
4259 fnt_style(layout->fn, word->style);
4260 wc = word_cache_string(word, word->style | style);
4261 }
4262 if (!wc)
4263 return;
4264 #ifdef _USE_HARFBUZZ
4265 /* Set the language and script for SDL_ttf */
4266 TTF_SetDirection(hb_dir(word->rtl));
4267 TTF_SetScript(word->script);
4268 #endif
4269 if (!word->xref) {
4270 if (!word->prerend) {
4271 prerend = cache_get(layout->prerend_cache, wc);
4272 if (!prerend) {
4273 word->prerend = fnt_render(layout->fn, word->word, fgcol);
4274 word->prerend = gfx_display_alpha(word->prerend);
4275 cache_add(layout->prerend_cache, wc, word->prerend);
4276 } else {
4277 word->prerend = prerend;
4278 }
4279 }
4280 s = word->prerend;
4281 } else if (word->xref->active) {
4282 if (!word->hlprerend) {
4283 hlprerend = cache_get(layout->hlprerend_cache, wc);
4284 if (!hlprerend) {
4285 word->hlprerend = fnt_render(layout->fn, word->word, acol);
4286 word->hlprerend = gfx_display_alpha(word->hlprerend);
4287 cache_add(layout->hlprerend_cache, wc, word->hlprerend);
4288 } else {
4289 word->hlprerend = hlprerend;
4290 }
4291 }
4292 s = word->hlprerend;
4293 } else {
4294 if (!word->prerend) {
4295 prerend = cache_get(layout->prerend_cache, wc);
4296 if (!prerend) {
4297 word->prerend = fnt_render(layout->fn, word->word, lcol);
4298 word->prerend = gfx_display_alpha(word->prerend);
4299 cache_add(layout->prerend_cache, wc, word->prerend);
4300 } else {
4301 word->prerend = prerend;
4302 }
4303 }
4304 s = word->prerend;
4305 }
4306 free(wc);
4307 #ifdef _USE_HARFBUZZ
4308 /* Drop to defaults */
4309 TTF_SetDirection(HB_DIRECTION_LTR);
4310 TTF_SetScript(HB_SCRIPT_COMMON);
4311 #endif
4312 if (!s)
4313 return;
4314 gfx_draw(s, x, y);
4315 }
4316
txt_layout_font(layout_t lay)4317 fnt_t txt_layout_font(layout_t lay)
4318 {
4319 struct layout *layout = lay;
4320 if (!lay)
4321 return NULL;
4322 return layout->fn;
4323 }
4324
vertical_align(struct word * w,int * hh)4325 static int vertical_align(struct word *w, int *hh)
4326 {
4327 int h;
4328 struct line *line = w->line;
4329 struct layout *layout = line->layout;
4330 if (w->img)
4331 h = gfx_img_h(w->img);
4332 else
4333 h = fnt_height(layout->fn);
4334 if (hh)
4335 *hh = h;
4336
4337 if (w->img && w->img_align)
4338 return 0;
4339
4340 if (w->valign == ALIGN_TOP)
4341 return 0;
4342 else if (w->valign == ALIGN_BOTTOM)
4343 return line->h - h;
4344 return (line->h - h) / 2;
4345 }
4346
4347 /* relative position in line */
line_pos(struct line * line,int x)4348 static int line_pos(struct line *line, int x)
4349 {
4350 if (!line->rtl)
4351 return x;
4352 return line->layout->w - line->x - x;
4353 }
4354
4355 /* absolute position of word */
word_pos_x(struct word * word)4356 static int word_pos_x(struct word *word)
4357 {
4358 struct line *line = word->line;
4359 struct layout *layout = line->layout;
4360 if (word->img && word->img_align) {
4361 if (line->rtl)
4362 return layout->w - word->x - gfx_img_w(word->img);
4363 return word->x;
4364 }
4365 if (!line->rtl) /* fast path */
4366 return line->x + word->x;
4367 if (word->img)
4368 return layout->w - line->x - word->x - gfx_img_w(word->img);
4369 return layout->w - line->x - word->x - word->w;
4370 }
4371
word_image_render(struct word * word,int x,int y,clear_fn clear,update_fn update)4372 static void word_image_render(struct word *word, int x, int y, clear_fn clear, update_fn update)
4373 {
4374 struct line *line = word->line;
4375 struct layout *layout = line->layout;
4376 int yy; int posx = word_pos_x(word);
4377
4378 if (clear && !word->xref)
4379 return;
4380 yy = vertical_align(word, NULL);
4381 if (clear) {
4382 if (word->img) {
4383 clear(x + posx, y + line->y + yy, gfx_img_w(word->img), gfx_img_h(word->img));
4384 } else
4385 clear(x + posx, y + line->y/* + yy*/, word->w, line->h);
4386 }
4387 if (word->img) {
4388 /* We have an image to draw */
4389 gfx_draw(word->img, x + posx, y + line->y + yy);
4390 if (update)
4391 update(x + posx, y + line->y + yy, gfx_img_w(word->img), gfx_img_h(word->img));
4392 } else {
4393 word_render(layout, word, x + posx, y + yy + line->y);
4394 if (update)
4395 update(x + posx, y + line->y + yy, word->w, line->h);
4396 }
4397 }
4398
xref_update(xref_t pxref,int x,int y,clear_fn clear,update_fn update)4399 void xref_update(xref_t pxref, int x, int y, clear_fn clear, update_fn update)
4400 {
4401 int i;
4402 struct xref *xref = (struct xref*)pxref;
4403 struct layout *layout;
4404 struct word *word;
4405 if (!xref)
4406 return;
4407
4408 layout = xref->layout;
4409 if (layout->box) {
4410 gfx_clip(x, y, layout->box->w, layout->box->h);
4411 y -= (layout->box)->off;
4412 }
4413
4414 for (i = 0; i < xref->num; i ++) {
4415 word = xref->words[i];
4416 if (!word->img_align)
4417 word_image_render(word, x, y, clear, update);
4418 }
4419 gfx_noclip();
4420 }
4421
4422 /* Draws everything */
txt_layout_draw_ex(layout_t lay,struct line * line,int x,int y,int off,int height,clear_fn clear)4423 void txt_layout_draw_ex(layout_t lay, struct line *line, int x, int y, int off, int height, clear_fn clear)
4424 {
4425 void *v;
4426 img_t img;
4427 struct layout *layout = (struct layout*)lay;
4428 struct margin *margin;
4429 struct word *word;
4430 /* line = layout->lines;
4431 gfx_clip(x, y, layout->w, layout->h); */
4432 if (!lay)
4433 return;
4434 for (v = NULL; (img = txt_layout_images(lay, &v)); )
4435 gfx_dispose_anim(img);
4436
4437 for (margin = layout->margin; margin; margin = margin->next) {
4438 if (margin->y + margin->h < off)
4439 continue;
4440 if (margin->y - off > height)
4441 continue;
4442 word_image_render(margin->word, x, y, clear, NULL);
4443 }
4444 if (!line)
4445 line = layout->lines;
4446 for (; line; line= line->next) {
4447 if ((line->y + line->h) < off)
4448 continue;
4449 if (line->y - off > height)
4450 break;
4451 for (word = line->words; word; word = word->next ) {
4452 if (!word->img_align)
4453 word_image_render(word, x, y, clear, NULL);
4454 }
4455 }
4456 cache_shrink(layout->prerend_cache);
4457 cache_shrink(layout->hlprerend_cache);
4458 cache_shrink(layout->img_cache);
4459 /* gfx_noclip(); */
4460 }
4461
txt_layout_draw(layout_t lay,int x,int y)4462 void txt_layout_draw(layout_t lay, int x, int y)
4463 {
4464 struct layout *layout = (struct layout*)lay;
4465 txt_layout_draw_ex(lay, NULL, x, y, 0, layout->h, 0);
4466 }
4467
4468
txt_box(int w,int h)4469 textbox_t txt_box(int w, int h)
4470 {
4471 struct textbox *box;
4472 box = malloc(sizeof(struct textbox));
4473 if (!box)
4474 return NULL;
4475 box->lay = NULL; /* (struct layout*)lay; */
4476 box->w = w;
4477 box->h = h;
4478 box->off = 0;
4479 box->line = NULL; /* (box->lay)->lines; */
4480 return box;
4481 }
4482
4483
txt_box_norm(textbox_t tbox)4484 void txt_box_norm(textbox_t tbox)
4485 {
4486 struct textbox *box = (struct textbox *)tbox;
4487 struct line *line;
4488 int off;
4489 if (!tbox || !box->lay)
4490 return;
4491 off = box->off;
4492 box->line = box->lay->lines;
4493 for (line = box->line; line; line = line->next) {
4494 if (off < line->h)
4495 break;
4496 off -= line->h;
4497 box->line = line;
4498 }
4499 }
4500
txt_box_layout(textbox_t tbox)4501 layout_t txt_box_layout(textbox_t tbox)
4502 {
4503 struct textbox *box = (struct textbox *)tbox;
4504 if (!box)
4505 return NULL;
4506 return box->lay;
4507 }
4508
txt_box_set(textbox_t tbox,layout_t lay)4509 void txt_box_set(textbox_t tbox, layout_t lay)
4510 {
4511 struct textbox *box = (struct textbox *)tbox;
4512 if (!box)
4513 return;
4514 box->lay = (struct layout*)lay;
4515 box->off = 0;
4516 if (lay)
4517 box->lay->box = box;
4518 txt_box_norm(tbox);
4519 }
4520
txt_box_resize(textbox_t tbox,int w,int h)4521 void txt_box_resize(textbox_t tbox, int w, int h)
4522 {
4523 struct textbox *box = (struct textbox *)tbox;
4524 if (!tbox)
4525 return;
4526 if (w < 0)
4527 w = 0;
4528 if (h < 0)
4529 h = 0;
4530 box->w = w;
4531 box->h = h;
4532 txt_box_norm(tbox);
4533 }
4534
txt_box_size(textbox_t tbox,int * w,int * h)4535 void txt_box_size(textbox_t tbox, int *w, int *h)
4536 {
4537 struct textbox *box = (struct textbox *)tbox;
4538 if (!tbox)
4539 return;
4540 if (w)
4541 *w = box->w;
4542 if (h)
4543 *h = box->h;
4544 }
4545
txt_box_scroll_next(textbox_t tbox,int disp)4546 void txt_box_scroll_next(textbox_t tbox, int disp)
4547 {
4548 int off, h;
4549 struct textbox *box = (struct textbox *)tbox;
4550 struct line *line;
4551 if (!tbox)
4552 return;
4553 line = box->line;
4554 if (!line)
4555 return;
4556
4557 txt_box_real_size(box, NULL, &h);
4558
4559 if (h - box->off < box->h)
4560 return;
4561
4562 off = h - box->off - box->h;
4563 if (disp > off)
4564 disp = off;
4565
4566 off = box->off - line->y; /* offset from cur line */
4567 off += disp; /* needed offset */
4568
4569 while (line->next && off >= line->next->y - line->y) {
4570 off -= (line->next->y - line->y);
4571 line = line->next;
4572 }
4573 box->line = line;
4574 box->off = line->y + off;
4575 }
4576
txt_box_scroll_prev(textbox_t tbox,int disp)4577 void txt_box_scroll_prev(textbox_t tbox, int disp)
4578 {
4579 int off;
4580 struct textbox *box = (struct textbox *)tbox;
4581 struct line *line;
4582 struct layout *l;
4583 if (!tbox)
4584 return;
4585 l = box->lay;
4586 line = box->line;
4587 if (!line)
4588 return;
4589 off = box->off - line->y; /* offset from cur line */
4590 off -= disp; /* offset from current line */
4591
4592 while (line != l->lines && off < 0) {
4593 line = line->prev;
4594 off += (line->next->y - line->y);
4595 }
4596
4597 box->line = line;
4598 box->off = line->y + off;
4599
4600 if (box->off <0)
4601 box->off = 0;
4602 }
4603
txt_box_scroll(textbox_t tbox,int disp)4604 void txt_box_scroll(textbox_t tbox, int disp)
4605 {
4606 if (!tbox)
4607 return;
4608 if (disp >0) {
4609 txt_box_scroll_next(tbox, disp);
4610 return;
4611 }
4612 else if (disp <0) {
4613 txt_box_scroll_prev(tbox, -disp);
4614 return;
4615 }
4616 }
4617
txt_box_next_line(textbox_t tbox)4618 void txt_box_next_line(textbox_t tbox)
4619 {
4620 struct textbox *box = (struct textbox *)tbox;
4621 struct line *line;
4622 if (!box || !box->lay)
4623 return;
4624 line = box->line;
4625 if (!line)
4626 return;
4627 /* txt_box_norm(tbox); */
4628 if (box->lay->h - box->off < box->h)
4629 return;
4630 line = line->next;
4631 if (line) {
4632 box->off = line->y;
4633 box->line = line;
4634 }
4635 }
4636
txt_box_prev_line(textbox_t tbox)4637 void txt_box_prev_line(textbox_t tbox)
4638 {
4639 struct textbox *box = (struct textbox *)tbox;
4640 struct line *line;
4641 struct layout *l;
4642 if (!box || !box->lay)
4643 return;
4644 l = box->lay;
4645 line = box->line;
4646 if (!line)
4647 return;
4648 if (line != l->lines) {
4649 line = line->prev;
4650 box->line = line;
4651 box->off = line->y;
4652 } else
4653 box->off = 0;
4654 }
4655
txt_box_off(textbox_t tbox)4656 int txt_box_off(textbox_t tbox)
4657 {
4658 struct textbox *box = (struct textbox *)tbox;
4659 if (!box)
4660 return -1;
4661 return box->off;
4662 }
4663
txt_box_next(textbox_t tbox)4664 void txt_box_next(textbox_t tbox)
4665 {
4666 struct textbox *box = (struct textbox *)tbox;
4667 struct line *line;
4668 if (!tbox)
4669 return;
4670 line = box->line;
4671 if (!line)
4672 return;
4673 for (; line; line = line->next) {
4674 if ((line->y + line->h - box->off) >= box->h)
4675 break;
4676 }
4677 if (line) {
4678 box->off += (line->y - box->off);
4679 box->line = line;
4680 }
4681 }
4682
txt_box_prev(textbox_t tbox)4683 void txt_box_prev(textbox_t tbox)
4684 {
4685 struct textbox *box = (struct textbox *)tbox;
4686 struct layout *lay;
4687 struct line *line;
4688 if (!tbox)
4689 return;
4690 lay = box->lay;
4691 if (!lay)
4692 return;
4693 line = box->line;
4694 if (!line)
4695 return;
4696 for (; line != lay->lines; line = line->prev) {
4697 if ((box->off - line->y) >= box->h)
4698 break;
4699 }
4700 if (line == lay->lines) {
4701 box->off = 0;
4702 box->line = lay->lines;
4703 return;
4704 }
4705 box->off = line->y;
4706 box->line = line;
4707 }
4708
txt_box_xrefs(textbox_t tbox)4709 xref_t txt_box_xrefs(textbox_t tbox)
4710 {
4711 struct textbox *box = (struct textbox*)tbox;
4712 struct xref *xref = NULL;
4713 struct word *word = NULL;
4714 struct line *line;
4715 if (!tbox)
4716 return NULL;
4717 for (line = box->line; line; line = line->next) {
4718 if (line->y < box->off)
4719 continue; /* too high */
4720 if (line->y + line->h > box->h + box->off)
4721 break; /* bottom */
4722 for (word = line->words; word; word = word->next) {
4723 xref = word->xref;
4724 if (!xref || word->img_align)
4725 continue;
4726 return xref;
4727 }
4728 }
4729 return xref;
4730 }
4731
txt_box_xref(textbox_t tbox,int x,int y)4732 xref_t txt_box_xref(textbox_t tbox, int x, int y)
4733 {
4734 struct textbox *box = (struct textbox*)tbox;
4735 struct xref *xref = NULL;
4736 struct word *word = NULL;
4737 struct line *line;
4738 if (!tbox)
4739 return NULL;
4740 y += box->off;
4741 if (x < 0)
4742 return NULL;
4743 if (y < 0)
4744 return NULL;
4745 if (x >= box->w)
4746 return NULL;
4747
4748 /* Process each word in each line */
4749 for (line = box->line; line; line = line->next) {
4750 int hh, yy, lx;
4751 if (y < line->y)
4752 break;
4753 if (y > line->y + line->h)
4754 continue;
4755 lx = line_pos(line, x);
4756 for (word = line->words; word; word = word->next) {
4757 yy = vertical_align(word, &hh);
4758 if (y < line->y + yy || y > line->y + yy + hh)
4759 continue;
4760 if (lx < line->x + word->x)
4761 continue;
4762 xref = word->xref;
4763
4764 /* Go back. Found nothing. */
4765 if (!xref)
4766 continue;
4767 if (lx < line->x + word->x + word->w)
4768 break;
4769 if (word->next && word->next->xref == xref && lx < line->x + word->next->x + word->next->w) {
4770 yy = vertical_align(word->next, &hh);
4771 if (y < line->y + yy || y > line->y + yy + hh)
4772 continue;
4773 break;
4774 }
4775 }
4776 }
4777 if (word && xref) {
4778 /* We found a highlighted word. */
4779 return xref;
4780 }
4781 return NULL;
4782 }
4783
txt_box_free(textbox_t tbox)4784 void txt_box_free(textbox_t tbox)
4785 {
4786 if (!tbox)
4787 return;
4788 free(tbox);
4789 }
4790
txt_box_render(textbox_t tbox)4791 img_t txt_box_render(textbox_t tbox)
4792 {
4793 img_t old_screen;
4794 img_t dst;
4795 struct textbox *box = (struct textbox *)tbox;
4796 if (!tbox)
4797 return NULL;
4798 dst = gfx_new(box->w, box->h);
4799 if (!dst)
4800 return NULL;
4801 old_screen = screen;
4802 screen = dst;
4803 gfx_clear(0, 0, box->w, box->h);
4804 /* gfx_clip(0, 0, box->w, box->h);
4805 printf("line: %d\n", box->line->y); */
4806 txt_layout_draw_ex(box->lay, box->line, 0, - box->off, box->off, box->h, NULL);
4807 /* gfx_noclip(); */
4808 screen = old_screen;
4809 return dst;
4810 }
4811
4812 /* Draws game content */
txt_box_draw(textbox_t tbox,int x,int y)4813 void txt_box_draw(textbox_t tbox, int x, int y)
4814 {
4815 struct textbox *box = (struct textbox *)tbox;
4816 if (!tbox)
4817 return;
4818 gfx_clip(x, y, box->w, box->h);
4819 /* printf("line: %d\n", box->line->y); */
4820 txt_layout_draw_ex(box->lay, box->line, x, y - box->off, box->off, box->h, NULL);
4821 gfx_noclip();
4822 }
4823
txt_box_update_links(textbox_t tbox,int x,int y,clear_fn clear)4824 void txt_box_update_links(textbox_t tbox, int x, int y, clear_fn clear)
4825 {
4826 struct textbox *box = (struct textbox *)tbox;
4827 if (!tbox)
4828 return;
4829 gfx_clip(x, y, box->w, box->h);
4830 /* printf("line: %d\n", box->line->y); */
4831 txt_layout_draw_ex(box->lay, box->line, x, y - box->off, box->off, box->h, clear);
4832 gfx_noclip();
4833 }
4834
4835
txt_layout_update_links(layout_t layout,int x,int y,clear_fn clear)4836 void txt_layout_update_links(layout_t layout, int x, int y, clear_fn clear)
4837 {
4838 struct layout *lay = (struct layout *)layout;
4839 /* gfx_clip(x, y, box->w, box->h);
4840 printf("line: %d\n", box->line->y); */
4841 txt_layout_draw_ex(lay, lay->lines, x, y, 0, lay->h, clear);
4842 /* gfx_noclip(); */
4843 }
4844
get_img(struct layout * layout,char * p,int * al)4845 img_t get_img(struct layout *layout, char *p, int *al)
4846 {
4847 int len;
4848 int align;
4849 img_t img;
4850 struct image *image;
4851 int escaped = 0;
4852 *al = 0;
4853 len = word_img(p, NULL);
4854 if (!len)
4855 return NULL;
4856 p += 3;
4857 p[len - 1] = 0;
4858 align = strcspn(p, "|");
4859 if (!p[align])
4860 align = 0;
4861 else {
4862 if (!strcmp(p + align + 1, "left"))
4863 *al = ALIGN_LEFT;
4864 else if (!strcmp(p + align + 1, "right"))
4865 *al = ALIGN_RIGHT;
4866 if (*al) {
4867 p[align] = 0;
4868 if (align && p[align - 1] == '\\') {
4869 p[align - 1] = 0;
4870 escaped = 1;
4871 }
4872 }
4873 }
4874 img = layout_lookup_image(layout, p);
4875 if (img)
4876 goto out;
4877 img = cache_get(layout->img_cache, p);
4878 if (!img) {
4879 unix_path(p);
4880 if (!(img = gfx_load_image(p))) {
4881 game_res_err_msg(p, debug_sw);
4882 goto out;
4883 }
4884 theme_img_scale(&img); /* bad style, no gfx layer :( */
4885 }
4886 image = image_new(p, img);
4887 if (!image) {
4888 gfx_free_image(img);
4889 img = NULL;
4890 } else {
4891 layout_add_image(layout, image);
4892 image->free_it = 1; /* free on layout destroy */
4893 /* if (gfx_img_w(img) <= GFX_MAX_CACHED_W && gfx_img_h(img) <= GFX_MAX_CACHED_H) */
4894 cache_add(layout->img_cache, p, img);
4895 }
4896 out:
4897 if (align) {
4898 p[align] = '|';
4899 if (escaped)
4900 p[align - 1] = '\\';
4901 }
4902 p[len - 1] = '>';
4903 if (!img)
4904 *al = 0;
4905 return img;
4906 }
4907
get_word_token(char * p,int * token)4908 static char *get_word_token(char *p, int *token)
4909 {
4910 int len;
4911 char *r;
4912 char *val = NULL;
4913 len = word_token(p, NULL);
4914 if (token)
4915 *token = 0;
4916 if (!len)
4917 return p;
4918 if (token)
4919 *token = 1;
4920 p[len - 1 + 3] = 0;
4921 r = strdup((p + 3));
4922 parse_esc_string(r, &val);
4923 free(p);
4924 if (val) {
4925 free(r);
4926 r = val;
4927 }
4928 return r;
4929 }
4930
process_token(char * ptr,struct layout * layout,struct line * line,struct xref ** xref,int * sp)4931 char *process_token(char *ptr, struct layout *layout, struct line *line, struct xref **xref, int *sp)
4932 {
4933
4934 int token;
4935 char *val = NULL;
4936 int bit = 0;
4937 int al = 0;
4938 int *cnt = NULL;
4939 char *eptr;
4940
4941 token = gfx_get_token(ptr, &eptr, &val, sp);
4942 if (!token)
4943 return NULL;
4944 if (TOKEN(token) == TOKEN_B) {
4945 cnt = &layout->scnt[0];
4946 bit = TTF_STYLE_BOLD;
4947 } else if (TOKEN(token) == TOKEN_I) {
4948 cnt = &layout->scnt[1];
4949 bit = TTF_STYLE_ITALIC;
4950 } else if (TOKEN(token) == TOKEN_U) {
4951 cnt = &layout->scnt[2];
4952 bit = TTF_STYLE_UNDERLINE;
4953 } else if (TOKEN(token) == TOKEN_S) {
4954 cnt = &layout->scnt[3];
4955 #ifdef TTF_STYLE_STRIKETHROUGH
4956 bit = TTF_STYLE_STRIKETHROUGH;
4957 #else
4958 bit = TTF_STYLE_ITALIC;
4959 #endif
4960 }
4961
4962 if (bit) {
4963 if (token & TOKEN_CLOSE) {
4964 -- (*cnt);
4965 if (*cnt < 0) /* fuzzy */
4966 *cnt = 0;
4967 if (!*cnt)
4968 layout->style &= ~bit;
4969 } else {
4970 ++ (*cnt);
4971 layout->style |= bit;
4972 }
4973 goto out;
4974 }
4975
4976 if (TOKEN(token) == TOKEN_L)
4977 al = ALIGN_LEFT;
4978 else if (TOKEN(token) == TOKEN_R)
4979 al = ALIGN_RIGHT;
4980 else if (TOKEN(token) == TOKEN_C)
4981 al = ALIGN_CENTER;
4982 else if (TOKEN(token) == TOKEN_J)
4983 al = ALIGN_JUSTIFY;
4984
4985 if (al) {
4986 if (token & TOKEN_CLOSE) {
4987 layout->acnt --;
4988 if (layout->acnt <0)
4989 layout->acnt = 0;
4990 layout->align = layout->saved_align[layout->acnt];
4991 } else {
4992 layout->saved_align[layout->acnt] = layout->align;
4993 layout->acnt ++;
4994 if (layout->acnt >= ALIGN_NEST)
4995 layout->acnt = ALIGN_NEST - 1;
4996 layout->align = al;
4997 line->align = al;
4998 }
4999 goto out;
5000 }
5001
5002 al = 0;
5003
5004 if (TOKEN(token) == TOKEN_T)
5005 al = ALIGN_TOP;
5006 else if (TOKEN(token) == TOKEN_D)
5007 al = ALIGN_BOTTOM;
5008 else if (TOKEN(token) == TOKEN_M)
5009 al = ALIGN_MIDDLE;
5010
5011 if (al) {
5012 if (token & TOKEN_CLOSE) {
5013 layout->vcnt --;
5014 if (layout->vcnt <0)
5015 layout->vcnt = 0;
5016 layout->valign = layout->saved_valign[layout->vcnt];
5017 } else {
5018 layout->saved_valign[layout->vcnt] = layout->valign;
5019 layout->vcnt ++;
5020 if (layout->vcnt >= ALIGN_NEST)
5021 layout->vcnt = ALIGN_NEST - 1;
5022 layout->valign = al;
5023 }
5024 goto out;
5025 }
5026 if (TOKEN(token) == TOKEN_X || TOKEN(token) == TOKEN_Y) {
5027 int pos;
5028 pos = atoi(val) * game_theme.scale;
5029 if (strchr(val, '%') && sscanf(val, "%d%%", &pos) == 1) {
5030 if (TOKEN(token) == TOKEN_Y) {
5031 if (layout->box)
5032 pos = layout->box->h * pos / 100;
5033 } else
5034 pos = layout->w * pos / 100;
5035 }
5036 if (TOKEN(token) == TOKEN_X) {
5037 line->tabx = pos;
5038 line->al_tabx = ALIGN_LEFT;
5039 if (strstr(val, "right"))
5040 line->al_tabx = ALIGN_RIGHT;
5041 else if (strstr(val, "center"))
5042 line->al_tabx = ALIGN_CENTER;
5043 } else {
5044 line->taby = pos;
5045 line->al_taby = ALIGN_BOTTOM;
5046 if (strstr(val, "top"))
5047 line->al_taby = ALIGN_TOP;
5048 else if (strstr(val, "middle"))
5049 line->al_taby = ALIGN_MIDDLE;
5050 }
5051 goto out;
5052 }
5053 if (TOKEN(token) == TOKEN_A) {
5054 if (token & TOKEN_CLOSE) {
5055 if (*xref)
5056 layout_add_xref(layout, *xref);
5057 *xref = NULL;
5058 } else {
5059 if (*xref) {
5060 eptr = NULL;
5061 goto out;
5062 }
5063 if (!strcmp(val, "#")) { /* jump anchor */
5064 layout->anchor = line;
5065 } else {
5066 *xref = xref_new(val);
5067 }
5068 }
5069 }
5070 out:
5071 if (val)
5072 free(val);
5073
5074 return eptr;
5075 }
5076
get_unbreakable_len(struct layout * layout,const char * ptr)5077 int get_unbreakable_len(struct layout *layout, const char *ptr)
5078 {
5079 int w = 0;
5080 int ww = 0;
5081 char *p, *eptr;
5082 while (ptr && *ptr) {
5083 int sp;
5084 while (gfx_get_token(ptr, &eptr, NULL, &sp)) {
5085 if (sp)
5086 return w;
5087 ptr = eptr;
5088 }
5089 p = get_word(ptr, &eptr, &sp);
5090 if (!p)
5091 return w;
5092
5093 if (sp || !*p || cjk_here(p) || word_img(p, NULL) || (word_token(p, NULL) > 1)) {
5094 free(p);
5095 return w;
5096 }
5097
5098 txt_size(layout->fn, p, &ww, NULL);
5099
5100 ptr = eptr;
5101 w += ww;
5102 if (!*p)
5103 ptr ++;
5104 else if (is_delim(*(ptr - 1))) {
5105 free(p);
5106 break;
5107 }
5108 free(p);
5109 }
5110 return w;
5111 }
5112
txt_layout_pos2off(layout_t lay,int pos,int * hh)5113 int txt_layout_pos2off(layout_t lay, int pos, int *hh)
5114 {
5115 int off = 0;
5116 struct line *line;
5117 struct layout *layout = (struct layout*)lay;
5118 if (!layout)
5119 return 0;
5120 for (line = layout->lines; line && (line->pos <= pos); line = line->next) {
5121 off = line->y; /* + line->h; */
5122 if (hh)
5123 *hh = line->h;
5124 }
5125 return off;
5126 }
5127
txt_layout_anchor(layout_t lay,int * hh)5128 int txt_layout_anchor(layout_t lay, int *hh)
5129 {
5130 int off;
5131 struct line *line;
5132 struct layout *layout = (struct layout*)lay;
5133 if (!layout)
5134 return -1;
5135 line = layout->anchor;
5136 if (!line)
5137 return -1;
5138 off = line->y; /* + line->h; */
5139 if (hh)
5140 *hh = line->h;
5141 return off;
5142 }
5143
line_y(layout_t lay,struct line * line)5144 static void line_y(layout_t lay, struct line *line)
5145 {
5146 int y = line->taby;
5147 if (line->taby < 0)
5148 return;
5149 if (line->al_taby == ALIGN_BOTTOM) {
5150 y -= line->h;
5151 } else if (line->al_taby == ALIGN_MIDDLE)
5152 y -= line->h/2;
5153 line->taby = -1;
5154 if (y > line->y)
5155 line->y = y;
5156 margin_rebase(lay);
5157 }
5158
word_x(struct line * line,struct word * word,int width)5159 static void word_x(struct line *line, struct word *word, int width)
5160 {
5161 if (line->tabx < 0)
5162 return;
5163 word->x = line->tabx - line->x;
5164 if (line->al_tabx == ALIGN_RIGHT)
5165 word->x -= word->w;
5166 else if (line->al_tabx == ALIGN_CENTER)
5167 word->x -= word->w/2;
5168
5169 if (word->x + word->w > width)
5170 word->x = width - word->w;
5171 if (word->x < line->w)
5172 word->x = line->w;
5173 else
5174 line->w = word->x;
5175 line->tabx = -1;
5176 line->align = ALIGN_LEFT;
5177 }
5178
5179 #ifdef _USE_HARFBUZZ
layout_lines_dir(struct layout * layout)5180 static void layout_lines_dir(struct layout *layout)
5181 {
5182 /* Set direction of each line based on the first non-image,
5183 alphabet word in that line. */
5184 struct word *word = NULL;
5185 struct line *ln = NULL;
5186 for (ln = layout->lines; ln; ln = ln->next) {
5187 for (word = ln->words; word; word = word->next ) {
5188 /* Continue until we get a word with some text (not letters or symbols) */
5189 if (!word->isalpha)
5190 continue;
5191
5192 if (!word->img) {
5193 ln->rtl = word->rtl;
5194 break;
5195 }
5196 }
5197 }
5198 }
5199 #endif
5200
_txt_layout_add(layout_t lay,char * txt)5201 void _txt_layout_add(layout_t lay, char *txt)
5202 {
5203 int sp = 0;
5204 int saved_style;
5205 int width;
5206 int img_align;
5207 struct margin *m;
5208
5209 struct line *line, *lastline = NULL;
5210 struct layout *layout = (struct layout*)lay;
5211 char *p, *eptr;
5212 char *ptr = txt;
5213 struct xref *xref = NULL;
5214 int w = 0, h = 0, nl = 0;
5215 int spw;
5216 img_t img = NULL;
5217 if (!layout || !layout->fn)
5218 return;
5219 saved_style = layout->style;
5220 fnt_style(layout->fn, 0);
5221 txt_size(layout->fn, " ", &spw, NULL);
5222
5223 if (layout->lines) {
5224 lastline = layout->lines->prev;
5225 lastline->pos = 0;
5226 }
5227
5228 if (!lastline) {
5229 line = line_new();
5230 if (!line)
5231 goto err;
5232 line->h = h;
5233 line->align = layout->align;
5234 } else {
5235 line = lastline;
5236 }
5237
5238 line->x = layout_find_margin(layout, line->y, &width);
5239
5240 while (ptr && *ptr) {
5241 struct word *word;
5242 int sp2, addlen = 0;
5243 int wtok;
5244 eptr = process_token(ptr, layout, line, &xref, &sp2);
5245 if (eptr) {
5246 ptr = eptr;
5247 if (xref && layout->style == saved_style)
5248 fnt_style(layout->fn, layout->lstyle); /* & ~TTF_STYLE_ITALIC); */
5249 else
5250 fnt_style(layout->fn, layout->style);/* & ~TTF_STYLE_ITALIC); */
5251
5252 if (!ptr || !*ptr)
5253 break;
5254 if (sp2)
5255 sp = -1;
5256 continue;
5257 }
5258 if (sp == -1) {
5259 p = get_word(ptr, &eptr, NULL);
5260 sp = 1;
5261 } else
5262 p = get_word(ptr, &eptr, &sp);
5263
5264 if (!p)
5265 break;
5266 img = get_img(layout, p, &img_align);
5267 if (!img_align) /* margins reset */
5268 addlen = get_unbreakable_len(layout, eptr);
5269
5270 wtok = 0;
5271 if (img) {
5272 w = gfx_img_w(img);
5273 h = gfx_img_h(img);
5274 if (img_align) {
5275 if (!line_margin(line)) {
5276 line->y = layout_skip_margin(layout, line->y);
5277 width = layout->w;
5278 line->x = 0;
5279 }
5280 if (width - w <= 0)
5281 img_align = 0;
5282 }
5283 } else {
5284 p = get_word_token(p, &wtok);
5285 if (wtok && *p == 0)
5286 sp = 1;
5287 #ifdef _USE_HARFBUZZ
5288 /* Correct size depends on the script and direction.
5289 Set them correctly before calling txt_size */
5290 word = word_new(p);
5291 if (word) {
5292 word_detect_rtl(word, layout->rtl);
5293 TTF_SetDirection(hb_dir(word->rtl));
5294 TTF_SetScript(word->script);
5295 txt_size(layout->fn, p, &w, &h);
5296 TTF_SetDirection(HB_DIRECTION_LTR);
5297 TTF_SetScript(HB_SCRIPT_COMMON);
5298 free(word);
5299 }
5300 #else
5301 txt_size(layout->fn, p, &w, &h);
5302 #endif
5303 h *= layout->fn_height;
5304 }
5305 nl = (wtok)?0:!*p;
5306
5307 if (!line->h && !img_align && line_empty(line)) /* first word ? */
5308 line->h = h;
5309
5310 if (img_align && !line->w)
5311 line->h = 0;
5312 #if 0
5313 if (!nl) {
5314 int ww = width - (line->w + ((sp && line->w)?spw:0) + addlen);
5315 p = word_hyphen(layout, p, ww, &eptr, &w);
5316 }
5317 #endif
5318 if ((line->num && (line->w + ((sp && line->w)?spw:0) + w + addlen) > width) || nl) {
5319 struct line *ol = line;
5320 h = 0; /* reset h for new line */
5321 if ((layout->h) && (line->y + line->h) >= layout->h)
5322 break;
5323 if (line != lastline) {
5324 layout_add_line(layout, line);
5325 }
5326 line_y(layout, line);
5327 line_align(line, width, line->align, nl);
5328
5329 line = line_new();
5330 if (!line) {
5331 free(p);
5332 goto err;
5333 }
5334 line->align = layout->align;
5335 line->h = 0;/* h; */
5336 line->y = ol->y + ol->h;
5337
5338 /* line->x = 0; */
5339 line->x = layout_find_margin(layout, line->y, &width);
5340 /* fprintf(stderr,"%d %d\n", line->x, width); */
5341 if (nl) {
5342 ptr = eptr + 1;
5343 }
5344 free(p);
5345 /* ptr = eptr; */
5346 line->pos = (int)(ptr - txt);
5347 continue;
5348 }
5349
5350
5351 if (h > line->h && !img_align)
5352 line->h = h;
5353
5354 word = word_new(p);
5355 if (!word) {
5356 line_free(line);
5357 free(p);
5358 goto err;
5359 }
5360 word_detect_rtl(word, layout->rtl);
5361 word->valign = layout->valign;
5362 if (!sp && !line_empty(line))
5363 word->unbrake = 1;
5364
5365 word->style = layout->style;
5366
5367 if (line->w && !word->unbrake)
5368 line->w += spw;
5369
5370 word->w = w;
5371 word->x = line->w;
5372
5373 word_x(line, word, width);
5374
5375 word->img = img;
5376 word->img_align = img_align;
5377
5378 if (img_align && (m = margin_new())) {
5379 int x2, w2;
5380
5381 x2 = layout_find_margin(layout, line->y, &w2);
5382
5383 if (img_align == ALIGN_LEFT) {
5384 line->x += w;
5385 m->w = x2 + w;
5386 }
5387 else
5388 m->w = layout->w - x2 - w2 + w;
5389 /* fprintf(stderr,"w: %d %d %d\n", width, w, width - w); */
5390 width -= w;
5391 m->h = h;
5392 m->y = line->y;
5393 m->align = img_align;
5394 m->word = word;
5395 word->w = 0;
5396 if (img_align == ALIGN_LEFT)
5397 word->x = x2;
5398 else
5399 word->x = x2 + w2 - w;
5400 h = 0;
5401 w = 0;
5402 layout_add_margin(layout, m);
5403 }
5404
5405 /* if (line->w)
5406 w += spw; */
5407
5408 line_add_word(line, word);
5409
5410 if (xref)
5411 xref_add_word(xref, word);
5412
5413 line->w += w;
5414
5415 if (nl)
5416 eptr ++;
5417 ptr = eptr;
5418 free(p);
5419 }
5420 #ifdef _USE_HARFBUZZ
5421 layout_lines_dir(layout);
5422 #endif
5423 if (layout->h == 0)
5424 layout->h = line->y + line->h;
5425
5426 /* if (line->num) { */
5427 if (line != lastline) {
5428 layout_add_line(layout, line);
5429 }
5430 line_y(layout, line);
5431 line_align(line, width, line->align, nl);
5432 /* } else
5433 line_free(line); */
5434 if (xref)
5435 layout_add_xref(layout, xref);
5436 layout->style = saved_style;
5437 return;
5438 err:
5439 txt_layout_free(layout);
5440 return;
5441 }
5442
txt_layout_add(layout_t lay,char * txt)5443 void txt_layout_add(layout_t lay, char *txt)
5444 {
5445 struct layout *layout = (struct layout*)lay;
5446 if (layout)
5447 layout->h = 0;
5448 _txt_layout_add(lay, txt);
5449 }
5450
xref_next(xref_t x)5451 xref_t xref_next(xref_t x)
5452 {
5453 if (!x)
5454 return NULL;
5455 return ((struct xref*)x)->next;
5456 }
5457
xref_prev(xref_t x)5458 xref_t xref_prev(xref_t x)
5459 {
5460 struct layout *l;
5461 if (!x)
5462 return NULL;
5463 l = ((struct xref*)x)->layout;
5464 if (x == l->xrefs) /* last one */
5465 return NULL;
5466 x = ((struct xref*)x)->prev;
5467 return x;
5468 }
5469
txt_layout_xrefs(layout_t lay)5470 xref_t txt_layout_xrefs(layout_t lay)
5471 {
5472 struct layout *layout = (struct layout*)lay;
5473 if (!layout)
5474 return NULL;
5475 return layout->xrefs;
5476 }
5477
xref_valid(xref_t x)5478 int xref_valid(xref_t x)
5479 {
5480 struct xref *xref = (struct xref*)x;
5481 if (!xref)
5482 return -1;
5483 if (!xref->num)
5484 return -1;
5485 return 0;
5486 }
5487
xref_position(xref_t x,int * xc,int * yc)5488 int xref_position(xref_t x, int *xc, int *yc)
5489 {
5490 int i;
5491 int w = 0;
5492 struct line *line = NULL;
5493 struct word *word = NULL;
5494 struct xref *xref = (struct xref*)x;
5495
5496 if (xref_valid(x))
5497 return -1;
5498
5499 for (i = 0; i < xref->num; i ++) {
5500 word = xref->words[i];
5501 if (!word->img_align)
5502 w += word->w;
5503 }
5504
5505 w = w/2;
5506
5507 for (i = 0; i < xref->num; i ++) {
5508 word = xref->words[i];
5509 if (word->img_align)
5510 continue;
5511 line = word->line;
5512 w -= word->w;
5513 if (w < 0)
5514 break;
5515 }
5516
5517 if (!line || !word)
5518 return -1;
5519
5520 if (xc)
5521 *xc = line_pos(line, line->x + word->x + word->w + w);
5522 if (yc)
5523 *yc = line->y + line->h / 2;
5524 return 0;
5525 }
txt_layout_xref(layout_t lay,int x,int y)5526 xref_t txt_layout_xref(layout_t lay, int x, int y)
5527 {
5528 struct layout *layout = (struct layout*)lay;
5529 struct xref *xref;
5530 struct word *word;
5531 struct line *line;
5532 int i;
5533 if (!lay || x < 0 || y < 0)
5534 return NULL;
5535 for (xref = layout->xrefs; xref; xref = xref->next) {
5536 for (i = 0; i < xref->num; i ++) {
5537 int hh, yy, lx;
5538 word = xref->words[i];
5539 line = word->line;
5540 lx = line_pos(line, x);
5541 if (word->img_align)
5542 continue;
5543 if (y < line->y || y > line->y + line->h)
5544 continue;
5545 yy = vertical_align(word, &hh);
5546 if (y < line->y + yy || y > line->y + yy + hh)
5547 continue;
5548 if (lx < line->x + word->x)
5549 continue;
5550 if (lx <= line->x + word->x + word->w)
5551 return xref;
5552 if (word->next && word->next->xref == xref && lx < line->x + word->next->x + word->next->w) {
5553 yy = vertical_align(word->next, &hh);
5554 if (y < line->y + yy || y > line->y + yy + hh)
5555 continue;
5556 return xref;
5557 }
5558 }
5559 }
5560 return NULL;
5561 }
5562
xref_layout(xref_t x)5563 layout_t xref_layout(xref_t x)
5564 {
5565 struct xref *xref = (struct xref*)x;
5566 if (!xref)
5567 return NULL;
5568 return xref->layout;
5569 }
5570
xref_get_text(xref_t x)5571 char *xref_get_text(xref_t x)
5572 {
5573 struct xref *xref = (struct xref*)x;
5574 if (!xref)
5575 return NULL;
5576 return xref->link;
5577 }
5578
xref_set_active(xref_t x,int val)5579 void xref_set_active(xref_t x, int val)
5580 {
5581 struct xref *xref = (struct xref*)x;
5582 if (!xref)
5583 return;
5584 xref->active = val;
5585 }
5586
xref_get_active(xref_t x)5587 int xref_get_active(xref_t x)
5588 {
5589 struct xref *xref = (struct xref*)x;
5590 if (!xref)
5591 return 0;
5592 return xref->active;
5593 }
5594
5595
txt_layout(fnt_t fn,int align,int width,int height)5596 layout_t txt_layout(fnt_t fn, int align, int width, int height)
5597 {
5598 struct layout *layout;
5599 layout = layout_new(fn, width, height);
5600 if (!layout)
5601 return NULL;
5602 layout->align = align;
5603 /* _txt_layout_add(layout, txt); */
5604 return layout;
5605 }
5606
txt_layout_set(layout_t lay,char * txt)5607 void txt_layout_set(layout_t lay, char *txt)
5608 {
5609 struct layout *layout = (struct layout*)lay;
5610 if (!layout)
5611 return;
5612 layout->h = 0;
5613 _txt_layout_free(lay);
5614 _txt_layout_add(lay, txt);
5615 }
_txt_layout_real_size(layout_t lay,struct line * line,int * pw,int * ph)5616 void _txt_layout_real_size(layout_t lay, struct line *line, int *pw, int *ph)
5617 {
5618 int w = 0;
5619 int h = 0;
5620 struct margin *margin;
5621 struct layout *layout = (struct layout*)lay;
5622 if (!layout)
5623 return;
5624 if (!line)
5625 line = layout->lines;
5626 for ( ; line; line = line->next) {
5627 while (!line->num && line->next)
5628 line = line->next;
5629 if (line->w > w)
5630 w = line->w;
5631 if (line->num && line->y + line->h > h)
5632 h = line->y + line->h;
5633 }
5634
5635 for (margin = layout->margin; margin; margin = margin->next) {
5636 if (margin->y + margin->h > h)
5637 h = margin->y + margin->h;
5638 }
5639
5640 if (pw)
5641 *pw = w;
5642 if (ph)
5643 *ph = h;
5644 }
5645
5646
txt_layout_real_size(layout_t lay,int * pw,int * ph)5647 void txt_layout_real_size(layout_t lay, int *pw, int *ph)
5648 {
5649 _txt_layout_real_size(lay, NULL, pw, ph);
5650 }
5651
txt_box_real_size(textbox_t box,int * pw,int * ph)5652 void txt_box_real_size(textbox_t box, int *pw, int *ph)
5653 {
5654 if (!box)
5655 return;
5656 if (pw)
5657 _txt_layout_real_size(txt_box_layout(box), NULL, pw, ph);
5658 else {/* faster path */
5659 struct line *line = ((struct layout*)txt_box_layout(box))->lines;
5660 struct line *lines = line;
5661 if (lines)
5662 line = lines->prev;
5663 for (; line != lines && !line->num; line = line->prev);
5664 _txt_layout_real_size(txt_box_layout(box), line, NULL, ph);
5665 }
5666 }
5667
gfx_cursor(int * xp,int * yp)5668 int gfx_cursor(int *xp, int *yp)
5669 {
5670 int x, y;
5671 #if SDL_VERSION_ATLEAST(2,0,0)
5672 x = mouse_x;
5673 y = mouse_y;
5674 #else
5675 SDL_GetMouseState(&x, &y);
5676 #endif
5677 if (xp)
5678 *xp = x;
5679 if (yp)
5680 *yp = y;
5681 return SDL_GetMouseState(NULL, NULL);
5682 }
5683
gfx_warp_cursor(int x,int y)5684 void gfx_warp_cursor(int x, int y)
5685 {
5686 #if SDL_VERSION_ATLEAST(2,0,0)
5687 float sx, sy;
5688 SDL_Rect rect;
5689 #ifdef _USE_SWROTATE
5690 if (gfx_flip_rotate) { /* TODO? */
5691 int tmp;
5692 tmp = y;
5693 y = x;
5694 x = gfx_height - tmp;
5695 }
5696 #endif
5697 SDL_RenderGetViewport(Renderer, &rect);
5698 SDL_RenderGetScale(Renderer, &sx, &sy);
5699 x = (x + rect.x) * sx;
5700 y = (y + rect.y) * sy;
5701 SDL_WarpMouseInWindow(SDL_VideoWindow, x, y);
5702 #else
5703 SDL_WarpMouse(x, y);
5704 #endif
5705 }
5706
5707 static int ALPHA_STEPS = 4;
5708 static volatile int fade_step_nr = -1;
5709
gfx_fading(void)5710 int gfx_fading(void)
5711 {
5712 return (fade_step_nr != -1);
5713 }
5714
5715 static img_t fade_bg = NULL;
5716 static img_t fade_fg = NULL;
5717 static void *fade_aux = NULL;
5718 static void (*fade_cb)(void *) = NULL;
5719 static SDL_TimerID fade_timer;
5720 static long gfx_change_nr = 0;
5721 #if SDL_VERSION_ATLEAST(2,0,0)
5722 static SDL_Texture *fade_bg_texture = NULL;
5723 static SDL_Texture *fade_fg_texture = NULL;
5724 #endif
update_gfx(void)5725 static void update_gfx(void)
5726 {
5727 img_t img = fade_fg;
5728 if (fade_step_nr == -1 || !img || !fade_bg || !fade_fg)
5729 return;
5730 #if !SDL_VERSION_ATLEAST(2,0,0)
5731 game_cursor(CURSOR_CLEAR);
5732 gfx_set_alpha(img, (SDL_ALPHA_OPAQUE * (fade_step_nr + 1)) / ALPHA_STEPS);
5733 gfx_draw(fade_bg, 0, 0);
5734 gfx_draw(img, 0, 0);
5735 game_cursor(CURSOR_DRAW);
5736 gfx_flip();
5737 #else
5738 gfx_render_copy(fade_bg_texture, NULL, 1);
5739 SDL_SetTextureAlphaMod(fade_fg_texture, (SDL_ALPHA_OPAQUE * (fade_step_nr + 1)) / ALPHA_STEPS);
5740 gfx_render_copy(fade_fg_texture, NULL, 0);
5741 if (game_cursor_show)
5742 gfx_render_cursor();
5743 SDL_RenderPresent(Renderer);
5744 #endif
5745 fade_step_nr ++;
5746 if (fade_step_nr == ALPHA_STEPS)
5747 fade_step_nr = -1;
5748 }
5749
gfx_change_screen_step(void * aux)5750 static void gfx_change_screen_step(void *aux)
5751 {
5752 gfx_change_nr --;
5753 if (gfx_fading()) {
5754 update_gfx();
5755 #if !SDL_VERSION_ATLEAST(2,0,0)
5756 game_cursor(CURSOR_ON);
5757 gfx_commit();
5758 #endif
5759 }
5760 if (gfx_fading())
5761 return;
5762 gfx_cancel_change_screen();
5763 return;
5764 }
5765
update(Uint32 interval,void * aux)5766 static Uint32 update(Uint32 interval, void *aux)
5767 {
5768 if (!gfx_fading())
5769 return 0;
5770 if (gfx_change_nr > 0)
5771 return interval;
5772 gfx_change_nr ++;
5773 push_user_event(gfx_change_screen_step, NULL);
5774 return interval;
5775 }
5776
gfx_cancel_change_screen(void)5777 void gfx_cancel_change_screen(void)
5778 {
5779 if (!fade_bg)
5780 return;
5781
5782 fade_step_nr = -1;
5783
5784 SDL_RemoveTimer(fade_timer);
5785 #if SDL_VERSION_ATLEAST(2,0,0)
5786 SDL_DestroyTexture(fade_fg_texture);
5787 SDL_DestroyTexture(fade_bg_texture);
5788 #endif
5789 game_cursor(CURSOR_CLEAR);
5790 gfx_copy(fade_fg, 0, 0);
5791 game_cursor(CURSOR_ON);
5792 gfx_flip();
5793 gfx_commit();
5794 gfx_free_image(fade_bg);
5795 fade_bg = NULL;
5796 if (fade_cb)
5797 fade_cb(fade_aux);
5798 }
5799
gfx_change_screen(img_t src,int steps,void (* callback)(void *),void * aux)5800 void gfx_change_screen(img_t src, int steps, void (*callback)(void *), void *aux)
5801 {
5802 struct inp_event ev;
5803 if (steps <= 1 || !opt_fading) {
5804 gfx_copy(src, 0, 0);
5805 game_cursor(CURSOR_ON);
5806 gfx_flip();
5807 if (callback)
5808 callback(aux);
5809 return;
5810 }
5811 gfx_change_nr = 0;
5812 fade_fg = NULL;
5813 fade_aux = aux;
5814 fade_cb = callback;
5815 fade_bg = gfx_grab_screen(0, 0, gfx_width, gfx_height);
5816
5817 if (!fade_bg) /* ok, i like kernel logic. No memory, but we must work! */
5818 return;
5819
5820 fade_fg = src;
5821
5822 #if SDL_VERSION_ATLEAST(2,0,0)
5823 fade_bg_texture = SDL_CreateTextureFromSurface(Renderer, Surf(fade_bg));
5824 if (!fade_bg_texture)
5825 goto err;
5826 SDL_SetTextureAlphaMod(fade_bg_texture, SDL_ALPHA_OPAQUE);
5827 SDL_SetTextureBlendMode(fade_bg_texture, SDL_BLENDMODE_NONE);
5828 fade_fg_texture = SDL_CreateTextureFromSurface(Renderer, Surf(src));
5829 if (!fade_fg_texture)
5830 goto err2;
5831 SDL_SetTextureBlendMode(fade_fg_texture, SDL_BLENDMODE_BLEND);
5832 #endif
5833 memset(&ev, 0, sizeof(ev));
5834 ALPHA_STEPS = steps;
5835 fade_step_nr = 0;
5836 fade_timer = SDL_AddTimer(60, update, NULL);
5837 return;
5838 #if SDL_VERSION_ATLEAST(2,0,0)
5839 err2:
5840 SDL_DestroyTexture(fade_bg_texture);
5841 err:
5842 gfx_free_image(fade_bg);
5843 fade_bg = NULL;
5844 return;
5845 #endif
5846 }
5847
gfx_init(void)5848 int gfx_init(void)
5849 {
5850 #if SDL_VERSION_ATLEAST(2,0,0)
5851 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
5852 if (render_sw)
5853 SDL_SetHint(SDL_HINT_RENDER_DRIVER, render_sw);
5854 #if defined(_WIN32) /* do not use buggy D3D by default: fullscreen problem with NVidia */
5855 else
5856 SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
5857 #endif
5858 #endif
5859 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0) {
5860 fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
5861 return -1;
5862 }
5863 if (!(images = cache_init(-1, gfx_cache_free_image))) {
5864 fprintf(stderr, "Can't init cache subsystem.\n");
5865 gfx_done();
5866 return -1;
5867 }
5868 /* SDL_DisableScreenSaver(); */
5869 return 0;
5870 }
5871
gfx_done(void)5872 void gfx_done(void)
5873 {
5874 #if SDL_VERSION_ATLEAST(2,0,0)
5875 if (screen)
5876 gfx_free_image(screen);
5877 /* if (SDL_VideoTexture)
5878 SDL_DestroyTexture(SDL_VideoTexture); */
5879 if (Renderer)
5880 SDL_DestroyRenderer(Renderer);
5881 if (SDL_VideoWindow)
5882 SDL_DestroyWindow(SDL_VideoWindow);
5883 #endif
5884 cache_free(images);
5885 images = NULL;
5886 SDL_Quit();
5887 }
5888
gfx_add_timer(int delay,int (* fn)(int,void *),void * aux)5889 gtimer_t gfx_add_timer(int delay, int (*fn)(int, void*), void *aux)
5890 {
5891 #if SDL_VERSION_ATLEAST(1,3,0)
5892 return (gtimer_t)SDL_AddTimer(delay, (SDL_TimerCallback)fn, aux);
5893 #else
5894 return (gtimer_t)SDL_AddTimer(delay, (SDL_NewTimerCallback)fn, aux);
5895 #endif
5896 }
5897
gfx_del_timer(gtimer_t han)5898 void gfx_del_timer(gtimer_t han)
5899 {
5900 if (han)
5901 SDL_RemoveTimer((SDL_TimerID)han);
5902 }
5903
gfx_ticks(void)5904 unsigned long gfx_ticks(void)
5905 {
5906 return SDL_GetTicks();
5907 }
5908
gfx_pending(void)5909 int gfx_pending(void)
5910 {
5911 #if SDL_VERSION_ATLEAST(2,0,0)
5912 return queue_dirty;
5913 #else
5914 return 0;
5915 #endif
5916 }
5917
gfx_set_title(const char * title)5918 int gfx_set_title(const char *title)
5919 {
5920 char stitle[4096];
5921 #if !defined(S60)
5922 if (!title) {
5923 #endif
5924 strcpy( stitle, "INSTEAD - " );
5925 strcat( stitle, VERSION );
5926 title = stitle;
5927 #if !defined(S60)
5928 }
5929 #endif
5930 #if !SDL_VERSION_ATLEAST(2,0,0)
5931 if (screen)
5932 SDL_WM_SetCaption(title, title);
5933 #else
5934 if (SDL_VideoWindow)
5935 SDL_SetWindowTitle(SDL_VideoWindow, title);
5936 #endif
5937 return 0;
5938 }
5939
gfx_set_icon(img_t ic)5940 int gfx_set_icon(img_t ic)
5941 {
5942 #if SDL_VERSION_ATLEAST(2,0,0)
5943 if (SDL_VideoWindow) {
5944 if (ic)
5945 SDL_SetWindowIcon(SDL_VideoWindow, Surf(ic));
5946 else if (icon)
5947 SDL_SetWindowIcon(SDL_VideoWindow, icon);
5948 }
5949 #else
5950 /* not works for SDL < 2 */
5951 #endif
5952 return 0;
5953 }
5954
gfx_get_dpi(void)5955 float gfx_get_dpi(void)
5956 {
5957 float hdpi = 96.0f;
5958 if (dpi_sw > 0)
5959 return (float)dpi_sw;
5960 #if SDL_VERSION_ATLEAST(2,0,4)
5961 if (SDL_GetDisplayDPI(SDL_CurrentDisplay, NULL, &hdpi, NULL))
5962 hdpi = 96.0f;
5963 #endif
5964 return hdpi;
5965 }
5966