steed/src/steed/graphics.c

4166 lines
79 KiB
C

#include "externals.h"
#include "internals.h"
#include <SDL.h>
#include <SDL_image.h>
#include <SDL_ttf.h>
#include <SDL_mutex.h>
#include "SDL_rotozoom.h"
#include "SDL_gfxBlitFunc.h"
#include "SDL_anigif.h"
#include "sdl_idf.h"
#define Surf(p) ((SDL_Surface *)p)
static SDL_Surface *screen = NULL;
static cache_t images = NULL;
static struct {
const char *name;
unsigned long val;
} cnames[] = {
{"aliceblue", 0xf0f8ff},
{"antiquewhite", 0xfaebd7},
{"aqua", 0x00ffff},
{"aquamarine", 0x7fffd4},
{"azure", 0xf0ffff},
{"beige", 0xf5f5dc},
{"bisque", 0xffe4c4},
{"black", 0x000000},
{"blanchedalmond", 0xffebcd},
{"blue", 0x0000ff},
{"blueviolet", 0x8a2be2},
{"brown", 0xa52a2a},
{"burlywood", 0xdeb887},
{"cadetblue", 0x5f9ea0},
{"chartreuse", 0x7fff00},
{"chocolate", 0xd2691e},
{"coral", 0xff7f50},
{"cornflowerblue", 0x6495ed},
{"cornsilk", 0xfff8dc},
{"crimson", 0xdc143c},
{"cyan", 0x00ffff},
{"darkblue", 0x00008b},
{"darkcyan", 0x008b8b},
{"darkgoldenrod", 0xb8860b},
{"darkgray", 0xa9a9a9},
{"darkgreen", 0x006400},
{"darkkhaki", 0xbdb76b},
{"darkmagenta", 0x8b008b},
{"darkolivegreen", 0x556b2f},
{"darkorange", 0xff8c00},
{"darkorchid", 0x9932cc},
{"darkred", 0x8b0000},
{"darksalmon", 0xe9967a},
{"darkseagreen", 0x8fbc8f},
{"darkslateblue", 0x483d8b},
{"darkslategray", 0x2f4f4f},
{"darkturquoise", 0x00ced1},
{"darkviolet", 0x9400d3},
{"deeppink", 0xff1493},
{"deepskyblue", 0x00bfff},
{"dimgray", 0x696969},
{"dodgerblue", 0x1e90ff},
{"feldspar", 0xd19275},
{"firebrick", 0xb22222},
{"floralwhite", 0xfffaf0},
{"forestgreen", 0x228b22},
{"fuchsia", 0xff00ff},
{"gainsboro", 0xdcdcdc},
{"ghostwhite", 0xf8f8ff},
{"gold", 0xffd700},
{"goldenrod", 0xdaa520},
{"gray", 0x808080},
{"green", 0x008000},
{"greenyellow", 0xadff2f},
{"honeydew", 0xf0fff0},
{"hotpink", 0xff69b4},
{"indianred", 0xcd5c5c},
{"indigo", 0x4b0082},
{"ivory", 0xfffff0},
{"khaki", 0xf0e68c},
{"lavender", 0xe6e6fa},
{"lavenderblush", 0xfff0f5},
{"lawngreen", 0x7cfc00},
{"lemonchiffon", 0xfffacd},
{"lightblue", 0xadd8e6},
{"lightcoral", 0xf08080},
{"lightcyan", 0xe0ffff},
{"lightgoldenrodyellow", 0xfafad2},
{"lightgrey", 0xd3d3d3},
{"lightgreen", 0x90ee90},
{"lightpink", 0xffb6c1},
{"lightsalmon", 0xffa07a},
{"lightseagreen", 0x20b2aa},
{"lightskyblue", 0x87cefa},
{"lightslateblue", 0x8470ff},
{"lightslategray", 0x778899},
{"lightsteelblue", 0xb0c4de},
{"lightyellow", 0xffffe0},
{"lime", 0x00ff00},
{"limegreen", 0x32cd32},
{"linen", 0xfaf0e6},
{"magenta", 0xff00ff},
{"maroon", 0x800000},
{"mediumaquamarine", 0x66cdaa},
{"mediumblue", 0x0000cd},
{"mediumorchid", 0xba55d3},
{"mediumpurple", 0x9370d8},
{"mediumseagreen", 0x3cb371},
{"mediumslateblue", 0x7b68ee},
{"mediumspringgreen", 0x00fa9a},
{"mediumturquoise", 0x48d1cc},
{"mediumvioletred", 0xc71585},
{"midnightblue", 0x191970},
{"mintcream", 0xf5fffa},
{"mistyrose", 0xffe4e1},
{"moccasin", 0xffe4b5},
{"navajowhite", 0xffdead},
{"navy", 0x000080},
{"oldlace", 0xfdf5e6},
{"olive", 0x808000},
{"olivedrab", 0x6b8e23},
{"orange", 0xffa500},
{"orangered", 0xff4500},
{"orchid", 0xda70d6},
{"palegoldenrod", 0xeee8aa},
{"palegreen", 0x98fb98},
{"paleturquoise", 0xafeeee},
{"palevioletred", 0xd87093},
{"papayawhip", 0xffefd5},
{"peachpuff", 0xffdab9},
{"peru", 0xcd853f},
{"pink", 0xffc0cb},
{"plum", 0xdda0dd},
{"powderblue", 0xb0e0e6},
{"purple", 0x800080},
{"red", 0xff0000},
{"rosybrown", 0xbc8f8f},
{"royalblue", 0x4169e1},
{"saddlebrown", 0x8b4513},
{"salmon", 0xfa8072},
{"sandybrown", 0xf4a460},
{"seagreen", 0x2e8b57},
{"seashell", 0xfff5ee},
{"sienna", 0xa0522d},
{"silver", 0xc0c0c0},
{"skyblue", 0x87ceeb},
{"slateblue", 0x6a5acd},
{"slategray", 0x708090},
{"snow", 0xfffafa},
{"springgreen", 0x00ff7f},
{"steelblue", 0x4682b4},
{"tan", 0xd2b48c},
{"teal", 0x008080},
{"thistle", 0xd8bfd8},
{"tomato", 0xff6347},
{"turquoise", 0x40e0d0},
{"violet", 0xee82ee},
{"violetred", 0xd02090},
{"wheat", 0xf5deb3},
{"white", 0xffffff},
{"whitesmoke", 0xf5f5f5},
{"yellow", 0xffff00},
{"yellowgreen", 0x9acd32},
{NULL, 0x0},
};
int gfx_parse_color (
const char *spec,
color_t *def)
{
int n, i;
int r, g, b;
char c;
if (!spec)
return -1;
n = strlen (spec);
if (*spec == '#') {
/*
* RGB
*/
spec++;
n--;
if (n != 3 && n != 6 && n != 9 && n != 12)
return -1;
n /= 3;
g = b = 0;
do {
r = g;
g = b;
b = 0;
for (i = n; --i >= 0; ) {
c = *spec++;
b <<= 4;
if (c >= '0' && c <= '9')
b |= c - '0';
else if (c >= 'A' && c <= 'F')
b |= c - ('A' - 10);
else if (c >= 'a' && c <= 'f')
b |= c - ('a' - 10);
else return (0);
}
} while (*spec != '\0');
n <<= 2;
// n = 16 - n;
if (def) {
def->r = r;
def->g = g;
def->b = b;
}
return 0;
}
for (i=0; cnames[i].name; i++) {
if (!strcmp(cnames[i].name, spec)) {
if (def) {
def->r = (cnames[i].val & 0xff0000) >> 16;
def->g = (cnames[i].val & 0x00ff00) >> 8;
def->b = (cnames[i].val & 0x0000ff);
}
return 0;
}
}
return -1;
}
struct _anigif_t;
struct agspawn {
SDL_Rect clip;
img_t bg;
int x;
int y;
};
#define AGSPAWN_BLOCK 8
struct _anigif_t {
struct _anigif_t *next;
struct _anigif_t *prev;
int cur_frame;
int nr_frames;
int loop;
int drawn;
int active;
int delay;
int spawn_nr;
struct agspawn *spawn;
AG_Frame frames[0];
};
typedef struct _anigif_t *anigif_t;
extern int timer_counter;
static int anigif_spawn(anigif_t ag, int x, int y, int w, int h)
{
int nr;
SDL_Rect clip;
SDL_GetClipRect(screen, &clip);
//gfx_free_image(ag->bg);
if (!ag->spawn && !(ag->spawn = malloc(AGSPAWN_BLOCK * sizeof(struct agspawn))))
return -1;
nr = ag->spawn_nr + 1;
if (!(nr % AGSPAWN_BLOCK)) { /* grow */
void *p = realloc(ag->spawn, AGSPAWN_BLOCK * sizeof(struct agspawn) *
((nr / AGSPAWN_BLOCK) + 1));
if (!p)
return -1;
ag->spawn = p;
}
ag->spawn[ag->spawn_nr].x = x;
ag->spawn[ag->spawn_nr].y = y;
ag->spawn[ag->spawn_nr].clip = clip;
ag->spawn[ag->spawn_nr].bg = gfx_grab_screen(x, y, w, h);
ag->spawn_nr = nr;
return 0;
}
static anigif_t anim_gifs = NULL;
static int anigif_drawn_nr = 0;
static anigif_t anigif_find(anigif_t g)
{
anigif_t p;
for (p = anim_gifs; p; p = p->next) {
if (p == g)
return p;
}
return NULL;
}
static void anigif_disposal(anigif_t g)
{
SDL_Rect dest;
SDL_Rect clip;
int i = 0;
img_t *img = NULL;
AG_Frame *frame;
frame = &g->frames[g->cur_frame];
SDL_GetClipRect(screen, &clip);
dest.x = 0; //g->x;
dest.y = 0; //g->y;
switch (frame->disposal) {
case AG_DISPOSE_NA:
case AG_DISPOSE_NONE: /* just show next frame */
break;
case AG_DISPOSE_RESTORE_BACKGROUND:
// img = g->bg;
// dest.w = Surf(img)->w;
// dest.h = Surf(img)->h;
break;
case AG_DISPOSE_RESTORE_PREVIOUS:
if (g->cur_frame) {
img = (img_t*)(g->frames[g->cur_frame - 1].surface);
dest.w = g->frames[g->cur_frame - 1].surface->w;
dest.h = g->frames[g->cur_frame - 1].surface->h;
dest.x += g->frames[g->cur_frame - 1].x;
dest.y += g->frames[g->cur_frame - 1].y;
}
break;
}
for (i = 0; i < g->spawn_nr; i++) {
SDL_Rect dst;
SDL_SetClipRect(screen, &g->spawn[i].clip);
dst = dest;
dst.x += g->spawn[i].x;
dst.y += g->spawn[i].y;
if (frame->disposal == AG_DISPOSE_RESTORE_BACKGROUND) {
img = g->spawn[i].bg;
dst.w = Surf(img)->w;
dst.h = Surf(img)->h;
}
if (img) { /* draw bg */
SDL_BlitSurface(Surf(img), NULL, screen, &dst);
}
}
SDL_SetClipRect(screen, &clip);
}
static void anigif_frame(anigif_t g)
{
int i;
SDL_Rect dest;
SDL_Rect clip;
AG_Frame *frame;
frame = &g->frames[g->cur_frame];
SDL_GetClipRect(screen, &clip);
dest.w = frame->surface->w;
dest.h = frame->surface->h;
for (i = 0; i < g->spawn_nr; i++) {
dest.x = g->spawn[i].x + frame->x;
dest.y = g->spawn[i].y + frame->y;
SDL_SetClipRect(screen, &g->spawn[i].clip);
SDL_BlitSurface(frame->surface, NULL, screen, &dest);
}
g->delay = timer_counter;
SDL_SetClipRect(screen, &clip);
}
static anigif_t is_anigif(img_t img)
{
anigif_t p;
for (p = anim_gifs; p; p = p->next) {
if (p->frames[0].surface == img)
return p;
}
return NULL;
}
static anigif_t anigif_add(anigif_t g)
{
anigif_t p;
p = anigif_find(g);
if (p) {
return p;
}
if (!anim_gifs) {
anim_gifs = g;
g->next = NULL;
g->prev = NULL;
return g;
}
for (p = anim_gifs; p && p->next; p = p->next);
p->next = g;
g->next = NULL;
g->prev = p;
return g;
}
static anigif_t anigif_del(anigif_t g)
{
if (g->prev == NULL)
anim_gifs = g->next;
else
g->prev->next = g->next;
if (g->next)
g->next->prev = g->prev;
return g;
}
static void anigif_free_spawn(anigif_t g)
{
int i;
for (i = 0; i < g->spawn_nr; i++)
gfx_free_image(g->spawn[i].bg);
if (g->spawn) {
free(g->spawn);
g->spawn = NULL;
g->spawn_nr = 0;
}
}
static void anigif_free(anigif_t g)
{
AG_FreeSurfaces(g->frames, g->nr_frames);
anigif_free_spawn(g);
free(g);
}
void gfx_free_image(img_t p)
{
anigif_t ag;
if (!p)
return;
if (!cache_forget(images, p))
return; /* cached sprite */
if ((ag = is_anigif(p))) {
if (ag->drawn)
anigif_drawn_nr --;
anigif_del(ag);
anigif_free(ag);
return;
}
SDL_FreeSurface((SDL_Surface *)p);
}
int gfx_img_w(img_t pixmap)
{
if (!pixmap)
return 0;
return Surf(pixmap)->w;
}
int gfx_img_h(img_t pixmap)
{
if (!pixmap)
return 0;
return Surf(pixmap)->h;
}
void gfx_noclip(void)
{
SDL_SetClipRect(screen, NULL);
}
void gfx_getclip(int *x, int *y, int *w, int *h)
{
SDL_Rect clip;
if (!screen)
return;
SDL_GetClipRect(screen, &clip);
if (x)
*x = clip.x;
if (y)
*y = clip.y;
if (w)
*w = clip.w;
if (h)
*h = clip.h;
}
void gfx_clip(int x, int y, int w, int h)
{
SDL_Rect src;
src.x = x;
src.y = y;
src.w = w;
src.h = h;
SDL_SetClipRect(screen, &src);
}
img_t gfx_new(int w, int h)
{
SDL_Surface *dst;
if (!screen) {
Uint32 rmask, gmask, bmask, amask;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
rmask = 0xff000000;
gmask = 0x00ff0000;
bmask = 0x0000ff00;
amask = 0x000000ff;
#else
rmask = 0x000000ff;
gmask = 0x0000ff00;
bmask = 0x00ff0000;
amask = 0xff000000;
#endif
dst = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_SRCALPHA, w, h,
32,
rmask,
gmask,
bmask,
amask);
} else {
dst = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_SRCALPHA, w, h,
screen->format->BitsPerPixel,
screen->format->Rmask,
screen->format->Gmask,
screen->format->Bmask,
screen->format->Amask);
}
return dst;
}
void gfx_img_fill(img_t img, int x, int y, int w, int h, color_t col)
{
SDL_Rect dest;
dest.x = x;
dest.y = y;
dest.w = w;
dest.h = h;
SDL_FillRect(img, &dest, SDL_MapRGB(((SDL_Surface*)img)->format, col.r, col.g, col.b));
}
void gfx_fill(int x, int y, int w, int h, color_t col)
{
gfx_img_fill(screen, x, y, w, h, col);
}
img_t gfx_screen(img_t nscreen)
{
img_t img;
if (nscreen) {
img = screen;
screen = nscreen;
return img;
}
return screen;
}
img_t gfx_grab_screen(int x, int y, int w, int h)
{
SDL_Rect dst, src;
SDL_Surface *img = SDL_CreateRGBSurface(screen->flags, w, h, screen->format->BitsPerPixel,
screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, screen->format->Amask);
if (!img)
return NULL;
src.x = x;
src.y = y;
src.w = w;
src.h = h;
dst.x = 0;
dst.y = 0;
dst.w = w;
dst.h = h;
SDL_BlitSurface(screen, &src, img, &dst);
return img;
}
img_t gfx_display_alpha(img_t src)
{
SDL_Surface* res;
if (!src)
return NULL;
if (!screen)
return src;
if (is_anigif(Surf(src))) /* already optimized */
return src;
res = SDL_DisplayFormatAlpha(Surf(src));
if (!res)
return src;
gfx_free_image(src);
return res;
}
int gfx_get_pixel(img_t src, int x, int y, color_t *color)
{
Uint8 r, g, b, a;
Uint32 col = 0;
Uint8 *ptr;
int bpp;
SDL_Surface *img = Surf(src);
if (!img)
return -1;
if (x >= img->w || y >= img->h || x < 0 || y < 0)
return -1;
if (SDL_LockSurface(img))
return -1;
if (img->format)
bpp = img->format->BytesPerPixel;
else
bpp = 1; /* hack? */
ptr = (Uint8*)img->pixels;
ptr += img->pitch * y;
ptr += x * bpp;
memcpy(&col, ptr, bpp);
SDL_UnlockSurface(img);
if (color)
SDL_GetRGBA(col, img->format, &r, &g, &b, &a);
if (color) {
color->r = r;
color->g = g;
color->b = b;
color->a = a;
}
return 0;
}
int gfx_set_pixel(img_t src, int x, int y, color_t color)
{
int bpp;
Uint32 col;
Uint8 *ptr;
SDL_Surface *img = Surf(src);
if (!img)
return -1;
if (x >= img->w || y >= img->h || x < 0 || y < 0)
return -1;
if (SDL_LockSurface(img))
return -1;
if (img->format)
bpp = img->format->BytesPerPixel;
else
bpp = 1; /* hack? */
ptr = (Uint8*)img->pixels;
ptr += img->pitch * y;
ptr += x * bpp;
col = SDL_MapRGBA(img->format, color.r, color.g, color.b, color.a);
memcpy(ptr, &col, bpp);
SDL_UnlockSurface(img);
return 0;
}
img_t gfx_alpha_img(img_t src, int alpha)
{
Uint8 *ptr;
Uint32 col;
int size;
int bpp;
SDL_Surface *img;
if (screen)
img = SDL_DisplayFormatAlpha((SDL_Surface*)src);
else
img = gfx_new(Surf(src)->w, Surf(src)->h);
if (!img)
return NULL;
if (img->format)
bpp = img->format->BytesPerPixel;
else
bpp = 1;
SDL_SetAlpha(img, SDL_SRCALPHA, SDL_ALPHA_OPAQUE);
if (SDL_LockSurface(img) == 0) {
int w = img->w;
ptr = (Uint8*)img->pixels;
size = img->w * img->h;
while (size --) {
Uint8 r, g, b, a;
memcpy(&col, ptr, bpp);
SDL_GetRGBA(col, img->format, &r, &g, &b, &a);
col = SDL_MapRGBA(img->format, r, g, b, a * alpha / 255);
memcpy(ptr, &col, bpp);
ptr += bpp;
w --;
if (!w) {
w = img->w;
ptr += img->pitch;
ptr -= w * bpp;
}
}
SDL_UnlockSurface(img);
}
return img;
}
void gfx_set_alpha(img_t src, int alpha)
{
#if SDL_VERSION_ATLEAST(1,3,0)
SDL_SetSurfaceAlphaMod((SDL_Surface *)src, alpha);
if (alpha == 0xff)
SDL_SetSurfaceBlendMode((SDL_Surface *)src, SDL_BLENDMODE_NONE);
else
SDL_SetSurfaceBlendMode((SDL_Surface *)src, SDL_BLENDMODE_BLEND);
#else
SDL_SetAlpha((SDL_Surface *)src, SDL_SRCALPHA, alpha);
#endif
}
void gfx_unset_alpha(img_t src)
{
SDL_SetAlpha((SDL_Surface *)src, 0, SDL_ALPHA_OPAQUE);
}
img_t gfx_combine(img_t src, img_t dst)
{
img_t new;
new = SDL_DisplayFormatAlpha(dst);
if (!new)
return NULL;
SDL_BlitSurface((SDL_Surface *)src, NULL, (SDL_Surface *)new, NULL);
return new;
}
static img_t img_pad(char *fname)
{
int l,r,t,b, rc;
img_t img, img2;
SDL_Rect to;
char *p = fname;
p += strcspn(p, ",");
if (*p != ',')
return NULL;
p ++;
rc = sscanf(fname, "%d %d %d %d,", &t, &r, &b, &l);
if (rc == 1) {
r = b = l = t;
} else if (rc == 2) {
b = t; l = r;
} else if (rc == 3) {
l = r;
} else if (rc == 4) {
;
} else
return NULL;
img = gfx_load_image(p);
if (!img)
return NULL;
img2 = gfx_new(gfx_img_w(img) + l + r, gfx_img_h(img) + t + b);
if (!img2) {
gfx_free_image(img);
return NULL;
} else {
img_t img = gfx_alpha_img(img2, 0);
if (img) {
gfx_free_image(img2);
img2 = img;
}
}
to.x = l;
to.y = t;
SDL_gfxBlitRGBA(img, NULL, img2, &to);
gfx_free_image(img);
return img2;
}
static img_t _gfx_load_combined_image(char *filename);
/* blank:WxH */
static img_t _gfx_load_special_image(char *f, int combined)
{
int alpha = 0;
int blank = 0;
char *filename;
img_t img, img2;
char *pc = NULL, *pt = NULL;
int wh[2] = { 0, 0 };
if (!f)
return NULL;
if (!(f = filename = strdup(f)))
return NULL;
if (!strncmp(filename, "blank:", 6)) {
filename += 6;
blank = 1;
} else if (!strncmp(filename, "spr:", 4) && !combined) {
// filename += 4;
img2 = cache_get(images, filename);
// fprintf(stderr, "get:%s %p\n", filename, img2);
goto out;
} else if (!strncmp(filename, "box:", 4)) {
filename += 4;
alpha = 255;
} else if (!strncmp(filename, "pad:", 4)) {
filename += 4;
img2 = img_pad(filename);
goto out;
} else if (!strncmp(filename, "comb:", 5)) {
filename += 5;
img2 = _gfx_load_combined_image(filename);
goto out;
} else
goto err;
if (strchr(filename, ';'))
goto err; /* combined */
if (blank)
goto skip;
pc = filename + strcspn(filename, ",");
if (*pc == ',') {
*pc = 0;
pc ++;
pt = pc + strcspn(pc, ",");
if (*pt == ',') {
*pt = 0;
pt ++;
} else
pt = NULL;
} else
pc = NULL;
skip:
if (parse_mode(filename, wh))
goto err;
if (wh[0] <= 0 || wh[1] <= 0)
goto err;
img = gfx_new(wh[0], wh[1]);
if (!img)
goto err;
if (pc) {
color_t col = { .r = 255, .g = 255, .b = 255 };
gfx_parse_color(pc, &col);
gfx_img_fill(img, 0, 0, wh[0], wh[1], col);
}
if (pt)
alpha = atoi(pt);
img2 = gfx_alpha_img(img, alpha);
gfx_free_image(img);
out:
free(f);
return img2;
err:
free(f);
return NULL;
}
cache_t gfx_image_cache(void)
{
return images;
}
static img_t _gfx_load_image(char *filename, int combined)
{
SDL_RWops *rw;
SDL_Surface *img;
int nr = 0;
filename = strip(filename);
img = _gfx_load_special_image(filename, combined);
if (img)
return img;
if (strstr(filename,".gif") || strstr(filename,".GIF"))
nr = AG_LoadGIF(filename, NULL, 0, NULL);
if (nr > 1) { /* anigif logic */
int loop = 0;
anigif_t agif = malloc(sizeof(struct _anigif_t) + nr * sizeof(AG_Frame));
if (!agif)
return NULL;
memset(agif, 0, sizeof(struct _anigif_t) + nr * sizeof(AG_Frame));
AG_LoadGIF(filename, agif->frames, nr, &loop);
AG_NormalizeSurfacesToDisplayFormat( agif->frames, nr);
agif->loop = loop;
agif->nr_frames = nr;
anigif_add(agif);
// fprintf(stderr, "anigif: %s %p\n", filename, agif->frames[0].surface);
return agif->frames[0].surface;
}
rw = RWFromIdf(game_idf, filename);
if (!rw || !(img = IMG_Load_RW(rw, 1)))
return NULL;
if (img->format->BitsPerPixel == 32) { /* hack for 32 bit BMP :( */
SDL_RWops *rwop;
rwop = RWFromIdf(game_idf, filename);
if (rwop) {
if (IMG_isBMP(rwop))
SDL_SetAlpha(img, 0, SDL_ALPHA_OPAQUE);
SDL_RWclose(rwop);
}
}
img = gfx_display_alpha(img);
return img;
}
/* x.png;a.png@1,2;b.png@3,4 */
static img_t _gfx_load_combined_image(char *filename)
{
char *str;
char *p, *ep;
img_t base = NULL, img = NULL;
p = str = strdup(filename);
if (!str)
return NULL;
ep = p + strcspn(p, ";");
if (*ep != ';')
goto err; /* first image is a base image */
*ep = 0;
base = _gfx_load_image(strip(p), 1);
if (!base)
goto err;
p = ep + 1;
while (*p) {
int x = 0, y = 0, c = 0;
SDL_Rect to;
ep = p + strcspn(p, ";@");
if (*ep == '@') {
*ep = 0; ep ++;
if (*ep == 'c') {
c = 1;
ep ++;
}
sscanf(ep, "%d,%d", &x, &y);
ep += strcspn(ep, ";");
if (*ep)
ep ++;
} else if (*ep == ';') {
*ep = 0; ep ++;
} else if (*ep) {
goto err;
}
img = _gfx_load_image(strip(p), 1);
if (img) {
to.x = x; to.y = y;
if (c) {
to.x -= gfx_img_w(img) / 2;
to.y -= gfx_img_h(img) / 2;
}
to.w = to.h = 0;
SDL_gfxBlitRGBA(img, NULL, base, &to);
gfx_free_image(img);
}
p = ep;
}
free(str);
return base;
err:
gfx_free_image(base);
free(str);
return NULL;
}
img_t gfx_load_image(char *filename)
{
img_t img = NULL;
if (!filename)
return NULL;
/* if (!access(filename, R_OK)) */
img = _gfx_load_image(filename, 0);
if (!img)
img = _gfx_load_combined_image(filename);
if (!img)
fprintf(stderr, "Can not load image: '%s'\n", filename);
return img;
}
void gfx_draw_bg(img_t p, int x, int y, int width, int height)
{
SDL_Surface *pixbuf = (SDL_Surface *)p;
SDL_Rect dest, src;
src.x = x;
src.y = y;
src.w = width;
src.h = height;
dest.x = x;
dest.y = y;
dest.w = width;
dest.h = height;
SDL_BlitSurface(pixbuf, &src, screen, &dest);
}
void gfx_draw_from(img_t p, int x, int y, int width, int height, img_t to, int xx, int yy)
{
SDL_Surface *pixbuf = (SDL_Surface *)p;
SDL_Surface *scr = (SDL_Surface *)to;
SDL_Rect dest, src;
if (!scr)
scr = screen;
src.x = x;
src.y = y;
src.w = width;
src.h = height;
dest.x = xx;
dest.y = yy;
dest.w = width;
dest.h = height;
SDL_BlitSurface(pixbuf, &src, scr, &dest);
}
void gfx_copy_from(img_t p, int x, int y, int width, int height, img_t to, int xx, int yy)
{
SDL_Surface *pixbuf = (SDL_Surface *)p;
SDL_Surface *scr = (SDL_Surface *)to;
SDL_Rect dest, src;
if (!scr)
scr = screen;
src.x = x;
src.y = y;
src.w = width;
src.h = height;
dest.x = xx;
dest.y = yy;
dest.w = width;
dest.h = height;
gfx_unset_alpha(pixbuf);
SDL_BlitSurface(pixbuf, &src, scr, &dest);
gfx_set_alpha(pixbuf, 255);
}
void gfx_draw(img_t p, int x, int y)
{
anigif_t ag;
SDL_Surface *pixbuf = (SDL_Surface *)p;
SDL_Rect dest;
dest.x = x;
dest.y = y;
dest.w = pixbuf->w;
dest.h = pixbuf->h;
if (!DIRECT_MODE) /* no gifs in direct mode */
ag = is_anigif(pixbuf);
else
ag = NULL;
if (ag) {
anigif_spawn(ag, x, y, dest.w, dest.h);
if (!ag->drawn)
anigif_drawn_nr ++;
ag->drawn = 1;
ag->active = 1;
anigif_frame(ag);
return;
}
SDL_BlitSurface(pixbuf, NULL, screen, &dest);
}
void gfx_stop_gif(img_t p)
{
anigif_t ag;
ag = is_anigif(p);
if (ag)
ag->active = 0;
}
void gfx_dispose_gif(img_t p)
{
anigif_t ag;
ag = is_anigif(p);
if (ag) {
if (ag->drawn)
anigif_drawn_nr --;
ag->drawn = 0;
anigif_free_spawn(ag);
}
}
void gfx_start_gif(img_t p)
{
anigif_t ag;
ag = is_anigif(p);
if (ag)
ag->active = 1;
}
int gfx_frame_gif(img_t img)
{
anigif_t ag;
ag = is_anigif(img);
if (!ag)
return 0;
if (!ag->drawn || !ag->active)
return 0;
if (ag->loop == -1)
return 0;
if ((timer_counter - ag->delay) < (ag->frames[ag->cur_frame].delay / HZ))
return 0;
if (ag->cur_frame != ag->nr_frames - 1 || ag->loop > 1 || !ag->loop)
anigif_disposal(ag);
ag->cur_frame ++;
if (ag->cur_frame >= ag->nr_frames) {
if (!ag->loop || ag->loop > 1)
ag->cur_frame = 0;
else
ag->cur_frame --; /* last one */
if (ag->loop) {
ag->loop --;
if (!ag->loop)
ag->loop = -1; /* disabled */
}
}
if (ag->loop != -1)
anigif_frame(ag);
return 1;
}
int gfx_is_drawn_gifs(void)
{
return anigif_drawn_nr;
}
void gfx_update_gif(img_t img)
{
int i = 0;
anigif_t ag;
ag = is_anigif(img);
if (!ag)
return;
if (!ag->drawn || !ag->active)
return;
for (i = 0; i < ag->spawn_nr; i++) {
gfx_update(ag->spawn[i].x, ag->spawn[i].y,
gfx_img_w(img), gfx_img_h(img));
}
}
void gfx_draw_wh(img_t p, int x, int y, int w, int h)
{
SDL_Surface *pixbuf = (SDL_Surface *)p;
SDL_Rect dest, src;
src.x = 0;
src.y = 0;
src.w = w;
src.h = h;
dest.x = x;
dest.y = y;
dest.w = w;
dest.h = h;
SDL_BlitSurface(pixbuf, &src, screen, &dest);
}
static SDL_Color bgcol = { .r = 0, .g = 0, .b = 0 };
void gfx_bg(color_t col)
{
bgcol.r = col.r;
bgcol.g = col.g;
bgcol.b = col.b;
}
void gfx_clear(int x, int y, int w, int h)
{
SDL_Rect dest;
dest.x = x;
dest.y = y;
dest.w = w;
dest.h = h;
SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, bgcol.r, bgcol.g, bgcol.b));
}
int gfx_width = -1;
int gfx_height = -1;
int gfx_fs = -1;
static SDL_Rect** vid_modes = NULL;
static SDL_Rect m640x480 = { .w = 640, .h = 480 };
static SDL_Rect m800x480 = { .w = 800, .h = 480 };
static SDL_Rect m800x600 = { .w = 800, .h = 600 };
static SDL_Rect m1024x768 = { .w = 1024, .h = 768 };
static SDL_Rect m1280x800 = { .w = 1280, .h = 800 };
static SDL_Rect* std_modes[] = { &m640x480, &m800x480, &m800x600, &m1024x768, &m1280x800, NULL };
int gfx_modes(void)
{
int i = 0;
SDL_Rect** modes;
#ifdef __APPLE__
modes = SDL_ListModes(NULL, SDL_FULLSCREEN | SDL_SWSURFACE | SDL_ANYFORMAT);
#else
modes = SDL_ListModes(NULL, SDL_FULLSCREEN | SDL_HWSURFACE | SDL_DOUBLEBUF | SDL_ANYFORMAT);
#endif
if (modes == (SDL_Rect**)0)/* no modes */
return 0;
if (modes == (SDL_Rect**)-1) {
vid_modes = std_modes;
return 5;
}
for (i = 0; modes[i]; ++i);
vid_modes = modes;
return i;
}
int gfx_get_mode(int n, int *w, int *h)
{
if (!vid_modes)
gfx_modes();
if (!vid_modes || !vid_modes[n])
return -1;
if (w)
*w = vid_modes[n]->w;
if (h)
*h = vid_modes[n]->h;
return 0;
}
int gfx_prev_mode(int *w, int *h)
{
int ww, hh, i = 0;
if (!w || !h)
return -1;
while ((*w != -1 && *h != -1) &&
!gfx_get_mode(i, &ww, &hh)) {
if (ww == *w && hh == *h)
break;
i ++;
}
if (*w == -1 || *h == -1)
i = gfx_modes();
if (!i)
return -1;
i --;
if (gfx_get_mode(i, &ww, &hh))
return -1;
*w = ww; *h = hh;
return 0;
}
int gfx_next_mode(int *w, int *h)
{
int ww, hh, i = 0;
if (!w || !h)
return -1;
while ((*w != -1 && *h != -1) &&
!gfx_get_mode(i, &ww, &hh)) {
i ++;
if (ww == *w && hh == *h)
break;
}
if (gfx_get_mode(i, &ww, &hh))
return -1;
*w = ww; *h = hh;
return 0;
}
int gfx_get_max_mode(int *w, int *h)
{
#ifdef MAEMO
*w = 800;
*h = 480;
#else
int ww = 0, hh = 0;
int i = 0;
*w = 0;
*h = 0;
if (!vid_modes)
gfx_modes();
if (!vid_modes)
return -1;
while (!gfx_get_mode(i, &ww, &hh)) {
if ((ww * hh >= (*w) * (*h)) && ww > (*w)) {
*w = ww;
*h = hh;
}
i ++;
}
#endif
return 0;
}
int gfx_set_mode(int w, int h, int fs)
{
if (gfx_width == w && gfx_height == h && gfx_fs == fs)
return 0; /* already done */
gfx_fs = fs;
gfx_width = w;
gfx_height = h;
SDL_ShowCursor(SDL_DISABLE);
#ifdef S60
screen = SDL_SetVideoMode(gfx_width, gfx_height, 0, SDL_ANYFORMAT | SDL_HWSURFACE | ( ( fs ) ? SDL_FULLSCREEN : 0 ) );
#else
#ifdef ANDROID
screen = SDL_SetVideoMode(gfx_width, gfx_height, 0, SDL_HWSURFACE | ( ( fs ) ? SDL_FULLSCREEN : 0 ) );
#else
#ifdef MAEMO
screen = SDL_SetVideoMode(gfx_width, gfx_height, 16, SDL_DOUBLEBUF | SDL_HWSURFACE | ( ( fs ) ? SDL_FULLSCREEN : 0 ) );
#else
#ifdef __APPLE__
screen = SDL_SetVideoMode(gfx_width, gfx_height, (fs)?32:0, SDL_SWSURFACE | ( ( fs ) ? SDL_FULLSCREEN : 0 ) );
if (screen == NULL) /* ok, fallback to anyformat */
screen = SDL_SetVideoMode(gfx_width, gfx_height, 0, SDL_ANYFORMAT | SDL_SWSURFACE | ( ( fs ) ? SDL_FULLSCREEN : 0 ) );
#else
#ifndef _WIN32_WCE
#if SDL_VERSION_ATLEAST(1,3,0)
screen = SDL_SetVideoMode(gfx_width, gfx_height, 32, SDL_DOUBLEBUF | SDL_HWSURFACE | ( ( fs ) ? SDL_FULLSCREEN : 0 ) );
#else
screen = SDL_SetVideoMode(gfx_width, gfx_height, (fs)?32:0, SDL_DOUBLEBUF | SDL_HWSURFACE | ( ( fs ) ? SDL_FULLSCREEN : 0 ) );
#endif
if (screen == NULL) /* ok, fallback to anyformat */
#endif
screen = SDL_SetVideoMode(gfx_width, gfx_height, 0, SDL_ANYFORMAT | SDL_HWSURFACE | ( ( fs ) ? SDL_FULLSCREEN : 0 ) );
#endif
#endif
#endif
#endif
if (screen == NULL) {
fprintf(stderr, "Unable to set %dx%d video: %s\n", w, h, SDL_GetError());
return -1;
}
fprintf(stderr,"Video mode: %dx%d@%dbpp\n", screen->w, screen->h, screen->format->BitsPerPixel);
gfx_clear(0, 0, gfx_width, gfx_height);
return 0;
}
static SDL_Surface *icon = NULL;
int gfx_video_init(void)
{
char title[128] = "";//4096 chars?! my HD monitor is not THAT big, you know.
strcpy( title, "Steed v." );
strcat( title, VERSION );
if (TTF_Init()) {
fprintf(stderr, "Can't init TTF subsystem.\n");
return -1;
}
SDL_WM_SetCaption( title, title );
#ifndef ICON_PATH
#define ICON_PATH "./icon"
#endif
char iconpath[128];
strcpy( iconpath, ICON_PATH);
strcat( iconpath, "/sdl_instead.png");
icon = IMG_Load( iconpath );
if ( icon ) {
SDL_WM_SetIcon( icon, NULL );
}
return 0;
}
void gfx_flip(void)
{
SDL_Flip(screen);
}
void gfx_update(int x, int y, int w, int h) {
// SDL_Flip(screen);
if (x < 0) {
w += x;
x = 0;
}
if (y < 0) {
h += y;
y = 0;
}
if (w < 0 || h < 0)
return;
if (x >= gfx_width || y >= gfx_height)
return;
if (x + w > gfx_width) {
w = gfx_width - x;
}
if (y + h > gfx_height) {
h = gfx_height - y;
}
SDL_UpdateRect(screen, x, y, w, h);
}
void gfx_video_done(void)
{
if (icon)
SDL_FreeSurface(icon);
screen = NULL;
TTF_Quit();
}
img_t gfx_scale(img_t src, float xscale, float yscale)
{
anigif_t ag;
if ((ag = is_anigif(Surf(src)))) {
int i;
for (i = 0; i < ag->nr_frames; i ++) {
SDL_Surface *s = zoomSurface(ag->frames[i].surface, xscale, yscale, 1);
if (i)
SDL_FreeSurface(ag->frames[i].surface);
ag->frames[i].surface = s;
ag->frames[i].x = (float)(ag->frames[i].x) * xscale;
ag->frames[i].y = (float)(ag->frames[i].y) * yscale;
}
return ag->frames[0].surface;
}
return (img_t)zoomSurface((SDL_Surface *)src, xscale, yscale, 1);
}
img_t gfx_rotate(img_t src, float angle)
{
anigif_t ag;
float rangle = angle * (M_PI / 180.0);
if ((ag = is_anigif(Surf(src)))) {
int i;
int w,h;
float x, y, x1, y1;
w = gfx_img_w(src);
h = gfx_img_h(src);
for (i = 0; i < ag->nr_frames; i ++) {
SDL_Surface *s = rotozoomSurface(ag->frames[i].surface, angle, 1.0, 11);
if (i)
SDL_FreeSurface(ag->frames[i].surface);
ag->frames[i].surface = s;
x = (float)(ag->frames[i].x) - w / 2;
y = (float)(ag->frames[i].y) - h / 2;
x1 = x*cos(rangle) - y*sin(rangle);
y1 = y*cos(rangle) + x*sin(rangle);
ag->frames[i].x = x1 + w / 2;
ag->frames[i].y = y1 + h / 2;
}
return ag->frames[0].surface;
}
return (img_t)rotozoomSurface(Surf(src), angle, 1.0, 1);
}
#define FN_REG 0
#define FN_BOLD 1
#define FN_ITALIC 2
#define FN_ITALICBOLD 3
#define FN_MAX 4
struct fnt {
TTF_Font *fn;
TTF_Font *fonts[FN_MAX];
int style;
};
/* prefix{regular,italic, bold, bolditalic}.ttf */
static int parse_fn(const char *f, char *files[])
{
int e;
int nr = 0;
int elen;
const char *ep = f;
const char *s = f;
int pref = strcspn(f, "{");
if (!f[pref])
goto no;
f += pref + 1;
ep = f;
ep += strcspn(f, "}");
if (!*ep)
goto no;
ep ++;
elen = strlen(ep);
while (1) {
f += strspn(f, " \t");
e = strcspn(f, ",}");
if (!e) { /* empty subst */
files[nr] = NULL;
goto skip;
}
files[nr] = malloc(e + pref + elen + 1);
if (!files[nr])
break;
if (pref)
memcpy(files[nr], s, pref);
if (e)
memcpy(files[nr] + pref, f, e);
if (elen)
memcpy(files[nr] + pref + e, ep, elen);
*(files[nr] + pref + e + elen) = 0;
skip:
nr ++;
if (!f[e] || f[e] == '}')
break;
f += e + 1;
if (nr >=4)
break;
}
return nr;
no:
files[0] = strdup(s);
return (files[0])?1:0;
}
fnt_t fnt_load(const char *fname, int size)
{
TTF_Font *fn;
struct fnt *h;
int i, n = 0;
char *files[4] = { NULL, NULL, NULL, NULL };
h = malloc(sizeof(struct fnt));
if (!h)
return NULL;
h->fonts[0] = h->fonts[1] = h->fonts[2] = h->fonts[3] = NULL;
n = parse_fn(fname, files);
if (!n)
goto err;
for (i = 0; i < n; i++) {
fn = NULL;
if (!is_empty(files[i])) {
SDL_RWops *rw = RWFromIdf(game_idf, files[i]);
if (!rw || !(fn = TTF_OpenFontRW(rw, 1, size))) {
fprintf(stderr, "Can not load font: '%s'\n", files[i]);
}
}
if (!fn && i == 0) /* no regular */
goto err;
#ifdef TTF_HINTING_LIGHT
if (fn) /* todo? */
TTF_SetFontHinting(fn, TTF_HINTING_LIGHT);
#endif
h->fonts[i] = fn;
}
h->fn = h->fonts[FN_REG];
for (i = 0; i < n; i++)
free(files[i]);
return (fnt_t) h;
err:
for (i = 0; i < n; i++)
free(files[i]);
fnt_free(h);
return NULL;
}
void fnt_style(fnt_t fn, int style)
{
struct fnt *h = (struct fnt*)fn;
if (!h)
return;
h->style = style;
if ((style & TTF_STYLE_BOLD) && (style & TTF_STYLE_ITALIC)) {
if (h->fonts[FN_ITALICBOLD]) {
h->fn = h->fonts[FN_ITALICBOLD];
style &= ~TTF_STYLE_BOLD;
style &= ~TTF_STYLE_ITALIC;
} else
h->fn = h->fonts[FN_REG];
} else if ((style & TTF_STYLE_BOLD)) {
if (h->fonts[FN_BOLD]) {
h->fn = h->fonts[FN_BOLD];
style &= ~TTF_STYLE_BOLD;
} else
h->fn = h->fonts[FN_REG];
} else if ((style & TTF_STYLE_ITALIC)) {
if (h->fonts[FN_ITALIC]) {
h->fn = h->fonts[FN_ITALIC];
style &= ~TTF_STYLE_ITALIC;
} else
h->fn = h->fonts[FN_REG];
} else {
h->fn = h->fonts[FN_REG];
}
TTF_SetFontStyle((TTF_Font *)h->fn, style);
}
img_t fnt_render(fnt_t fn, const char *p, color_t col)
{
SDL_Color scol = { .r = col.r, .g = col.g, .b = col.b };
struct fnt *h = (struct fnt*)fn;
if (!h)
return NULL;
return TTF_RenderUTF8_Blended((TTF_Font *)h->fn, p, scol);
}
int fnt_height(fnt_t fn)
{
struct fnt *h = (struct fnt*)fn;
if (!fn)
return 0;
return TTF_FontHeight((TTF_Font *)(h->fonts[FN_REG]));
}
void fnt_free(fnt_t fnt)
{
int i;
struct fnt *h = (struct fnt*)fnt;
if (!fnt)
return;
for (i = 0; i < FN_MAX; i++) {
if (h->fonts[i])
TTF_CloseFont((TTF_Font *)h->fonts[i]);
}
free(h);
}
void txt_draw(fnt_t fnt, const char *txt, int x, int y, color_t col)
{
img_t s = fnt_render(fnt, txt, col);
gfx_draw(s, x, y);
}
#if 0
int txt_width(fnt_t fnt, const char *txt)
{
const char *p = txt;
int c = 0;
int w = 0;
Uint16 u = 0;
struct fnt *f = (struct fnt*)fnt;
if (!f)
return 0;
while (*p) {
if (!c) {
if (! (*p & 0x80)) { // ascii
c = 1;
u = *p & 0x7f;
} else {
if ((*p & 0xe0) == 0xc0) {
c = 2;
u = *p & 0x1f;
} else if ((*p & 0xf0) == 0xe0) {
c = 3;
u = *p & 0xf;
} else if ((*p & 0xf8) == 0xf0) {
c = 4;
u = *p & 0x3;
} else {
c = 1;
u = *p & 0x7f; /* fallback */
}
}
} else {
if ((*p & 0xc0) != 0x80) {
c = 1;
u = *p & 0x7f; /* fallback */
} else {
u <<= 6;
u |= *p & 0x3f;
}
}
c --;
if (!c) {
int adv = 0;
TTF_GlyphMetrics(f->fn, u, NULL, NULL, NULL, NULL, &adv);
w += adv;
}
p ++;
}
return w;
}
#endif
void txt_size(fnt_t fnt, const char *txt, int *w, int *h)
{
int ww, hh;
struct fnt *f = (struct fnt*)fnt;
TTF_SizeUTF8((TTF_Font *)f->fn, txt, &ww, &hh);
if (w)
*w = ww;
if (h)
*h = hh;
}
struct word;
struct line;
struct xref;
struct word {
int style;
int x;
int w;
int unbrake;
int valign;
int img_align;
char *word;
img_t img;
struct word *next; /* in line */
struct line *line;
struct xref *xref;
img_t prerend;
img_t hlprerend;
};
img_t word_image(word_t v)
{
struct word *w = (struct word*)v;
if (!w)
return NULL;
return w->img;
}
struct word *word_new(const char *str)
{
struct word *w;
w = malloc(sizeof(struct word));
if (!w)
return NULL;
w->word = strdup(str);
w->next = NULL;
w->x = 0;
w->w = 0;
w->valign = 0;
w->line = NULL;
w->xref = NULL;
w->style = 0;
w->img = NULL;
w->img_align = 0;
w->unbrake = 0;
w->prerend = NULL;
w->hlprerend = NULL;
return w;
}
struct line {
int x;
int y;
int h;
int w;
int num;
int align;
int pos;
int tabx;
int al_tabx;
struct word *words;
struct line *next;
struct line *prev;
struct layout *layout;
};
static int vertical_align(struct word *w, int *hh);
int word_geom(word_t v, int *x, int *y, int *w, int *h)
{
int xx, yy, ww, hh;
struct line *line;
struct word *word = (struct word*)v;
if (!word || !word->line)
return -1;
line = word->line;
xx = word->x + line->x;
ww = word->w;
yy = line->y;
yy += vertical_align(v, &hh);
if (x)
*x = xx;
if (y)
*y = yy;
if (w)
*w = ww;
if (h)
*h = hh;
return 0;
}
struct line *line_new(void)
{
struct line *l;
l = malloc(sizeof(struct line));
if (!l)
return NULL;
l->words = NULL;
l->next = NULL;
l->prev = NULL;
l->x = 0;
l->w = 0;
l->y = 0;
l->h = 0;
l->num = 0;
l->tabx = -1;
l->al_tabx = ALIGN_LEFT;
l->layout = NULL;
l->align = 0;
l->pos = 0;
return l;
}
int line_empty(struct line *line)
{
struct word *w;
w = line->words;
while (w) {
if (w->img_align) {
w = w->next;
continue;
}
return 0;
}
return 1;
}
static int line_margin(struct line *line)
{
struct word *w;
w = line->words;
while (w) {
if (w->img_align)
return 1;
w = w->next;
}
return 0;
}
static struct word *next_word(struct word *w)
{
while (w->next && w->next->img_align) /* skip margins */
w = w->next;
return w->next;
}
void line_justify(struct line *line, int width)
{
int x = 0;
int last_margin = 0;
int last_unbrake = 0;
struct word *w;
int sp, spm, lw = 0;
int lnum = 0;
if (!line || line->num <= 1 /*|| width <= line->w*/)
return;
w = line->words;
while (w) {
lw += w->w;
if (last_margin && w->unbrake)
w->unbrake = last_unbrake;
if (!w->unbrake && !w->img_align)
lnum ++;
if (!last_margin && w->img_align)
last_unbrake = w->unbrake;
last_margin = w->img_align;
w = w->next;
}
if (lnum <=1 )
return;
w = line->words;
sp = (width - lw) / (lnum - 1);
spm = (width - lw) % (lnum - 1);
while (w) {
if (!w->img_align) {
w->x = x;
if (next_word(w) && next_word(w)->unbrake)
x += w->w;
else {
x += w->w + sp + ((spm)?1:0);
if (spm)
spm --;
}
}
w = w->next;
}
}
void line_right(struct line *line, int width)
{
struct word *w;
int sp;
if (!line || line->num == 0)
return;
sp = width - line->w;
w = line->words;
while (w) {
if (!w->img_align) {
w->x += sp;
}
w = w->next;
}
}
void line_center(struct line *line, int width)
{
struct word *w;
int sp;
if (!line || line->num == 0)
return;
sp = (width - line->w)/2;
w = line->words;
while (w) {
if (!w->img_align) {
w->x += sp;
}
w = w->next;
}
}
void line_align(struct line *line, int width, int style, int nl)
{
if (style == ALIGN_JUSTIFY) {
if (nl)
return;
return line_justify(line, width);
}
if (style == ALIGN_CENTER)
return line_center(line, width);
if (style == ALIGN_LEFT)
return;
if (style == ALIGN_RIGHT)
return line_right(line, width);
}
void word_free(struct word *word);
void line_free(struct line *line)
{
struct word *w;
if (!line)
return;
w = line->words;
while (w) {
struct word *ow = w;
w = w->next;
word_free(ow);
}
free(line);
}
void line_add_word(struct line *l, struct word *word)
{
struct word *w = l->words;
l->num ++;
word->line = l;
if (!l->words) {
l->words = word;
return;
}
while (w->next)
w = w->next;
w->next = word;
return;
}
struct image;
struct image {
struct image *next;
char *name;
img_t image;
int free_it;
};
struct image *image_new(const char *name, img_t img)
{
struct image *g = malloc(sizeof(struct image));
if (!g)
return NULL;
g->image = img;
g->name = strdup(name);
g->next = NULL;
g->free_it = 0;
return g;
}
void image_free(struct image *image)
{
if (!image)
return;
if (image->name)
free(image->name);
if (image->free_it)
gfx_free_image(image->image);
free(image);
}
struct textbox;
#define ALIGN_NEST 16
struct margin;
struct margin {
struct margin *next;
int w;
int h;
int y;
int align;
struct word *word;
};
struct margin *margin_new(void)
{
struct margin *m = malloc(sizeof(struct margin));
if (!m)
return NULL;
m->w = m->h = m->align = 0;
m->next = NULL;
return m;
}
void margin_free(struct margin *m)
{
if (m)
free(m);
}
struct layout {
fnt_t fn;
float fn_height;
color_t col;
color_t lcol;
color_t acol;
struct image *images;
struct xref *xrefs;
struct line *lines;
struct textbox *box;
struct margin *margin;
int w;
int h;
int align;
int valign;
int saved_align[ALIGN_NEST];
int saved_valign[ALIGN_NEST];
int acnt;
int vcnt;
int style;
int scnt[4];
int lstyle;
cache_t img_cache;
cache_t prerend_cache;
cache_t hlprerend_cache;
};
struct xref {
struct xref *next;
struct xref *prev;
struct word **words;
struct layout *layout;
char *link;
int num;
int active;
};
struct textbox {
struct layout *lay;
struct line *line;
int off;
int w;
int h;
};
void word_free(struct word *word)
{
if (!word)
return;
// if (word->img)
// gfx_free_image(word->img);
if (word->word)
free(word->word);
if (word->prerend) {
cache_forget(word->line->layout->prerend_cache, word->prerend);
// SDL_FreeSurface(word->prerend);
}
if (word->hlprerend) {
cache_forget(word->line->layout->hlprerend_cache, word->hlprerend);
// SDL_FreeSurface(word->hlprerend);
}
word->hlprerend = word->prerend = NULL;
free(word);
}
struct xref *xref_new(char *link)
{
struct xref *p;
p = malloc(sizeof(struct xref));
if (!p)
return NULL;
if (link)
p->link = strdup(link);
else
p->link = NULL;
p->num = 0;
p->layout = NULL;
p->next = NULL;
p->prev = NULL;
p->active = 0;
p->words = NULL;
return p;
}
void xref_add_word(struct xref *xref, struct word *word)
{
xref->words = realloc(xref->words, (xref->num + 1) * sizeof(struct word*));
xref->words[xref->num ++] = word;
word->xref = xref;
}
void xref_free(struct xref *xref)
{
if (xref->link)
free(xref->link);
if (xref->words)
free(xref->words);
free(xref);
}
void layout_add_line(struct layout *layout, struct line *line)
{
struct line *l = layout->lines;
line->layout = layout;
// if (line->w > layout->w)
// layout->w = line->w;
if (!l) {
layout->lines = line;
line->prev = NULL;
return;
}
while (l->next)
l = l->next;
l->next = line;
line->prev = l;
return;
}
void layout_add_margin(struct layout *layout, struct margin *margin)
{
struct margin *m = layout->margin;
if (!m) {
layout->margin = margin;
return;
}
while (m->next)
m = m->next;
m->next = margin;
return;
}
#if 1
static int layout_skip_margin(struct layout *layout, int y)
{
struct margin *m = layout->margin;
int my = y;
if (!m)
return y;
while (m) {
if (m->y + m->h > my)
my = m->y + m->h;
m = m->next;
}
if (y < my)
y = my;
return y;
}
#endif
static int layout_find_margin(struct layout *layout, int y, int *w)
{
struct margin *m = layout->margin;
int xpos = 0;
int rpos = layout->w;
if (!m) {
if (w)
*w = layout->w;
return 0;
}
while (m) {
if (y >= m->y && y < m->y + m->h) {
if (m->align == ALIGN_LEFT)
xpos = (xpos < m->w)?m->w:xpos;
else
rpos = (rpos > layout->w - m->w)?layout->w - m->w:rpos;
}
m = m->next;
}
if (w)
*w = rpos - xpos;
return xpos;
}
struct image *_layout_lookup_image(struct layout *layout, const char *name)
{
struct image *g = layout->images;
for (g = layout->images; g; g = g->next) {
if (!strcmp(g->name, name))
return g;
}
return NULL;
}
img_t txt_layout_images(layout_t lay, void **v)
{
struct image **g = (struct image **)v;
struct layout *layout = (struct layout *)lay;
if (!layout)
return NULL;
if (!*g)
*g = layout->images;
else
*g = (*g)->next;
if (!*g)
return NULL;
return (*g)->image;
}
void layout_add_image(struct layout *layout, struct image *image)
{
struct image *g = layout->images;
if (!g) {
layout->images = image;
return;
}
while (g->next)
g = g->next;
g->next = image;
return;
}
img_t layout_lookup_image(struct layout *layout, char *name)
{
struct image *g = _layout_lookup_image(layout, name);
return (g)?g->image: NULL;
}
void layout_add_xref(struct layout *layout, struct xref *xref)
{
struct xref *x = layout->xrefs;
xref->layout = layout;
if (!x) {
layout->xrefs = xref;
xref->prev = NULL;
return;
}
while (x->next)
x = x->next;
x->next = xref;
xref->prev = x;
return;
}
void sdl_surface_free(void *p)
{
SDL_FreeSurface(p);
}
struct layout *layout_new(fnt_t fn, int w, int h)
{
struct layout *l;
l = malloc(sizeof(struct layout));
if (!l)
return NULL;
l->lines = NULL;
l->images = NULL;
l->w = w;
l->h = h;
l->fn = fn;
l->fn_height = 1.0f;
l->align = ALIGN_JUSTIFY;
l->valign = 0;
l->style = 0;
l->lstyle = 0;
l->xrefs = NULL;
l->margin = NULL;
l->col = gfx_col(0, 0, 0);
l->lcol = gfx_col(0, 0, 255);
l->acol = gfx_col(255, 0, 0);
l->box = NULL;
l->img_cache = cache_init(0, gfx_free_image);
l->prerend_cache = cache_init(0, sdl_surface_free);
l->hlprerend_cache = cache_init(0, sdl_surface_free);
memset(l->scnt, 0, sizeof(l->scnt));
memset(l->saved_align, 0, sizeof(l->saved_align));
memset(l->saved_valign, 0, sizeof(l->saved_valign));
l->acnt = 0;
l->vcnt = 0;
return l;
}
void txt_layout_size(layout_t lay, int *w, int *h)
{
struct layout *layout = (struct layout *)lay;
if (!lay)
return;
if (w)
*w = layout->w;
if (h)
*h = layout->h;
}
void txt_layout_set_size(layout_t lay, int w, int h)
{
struct layout *layout = (struct layout *)lay;
if (!lay)
return;
layout->w = w;
layout->h = h;
}
int txt_layout_add_img(layout_t lay, const char *name, img_t img)
{
struct layout *layout = (struct layout *)lay;
struct image *image;
image = _layout_lookup_image(layout, name);
if (image) { /* overwrite */
image->image = img;
return 0;
}
image = image_new(name, img);
if (!image)
return -1;
layout_add_image(layout, image);
return 0;
}
void _txt_layout_free(layout_t lay)
{
struct layout *layout = (struct layout *)lay;
struct line *l;
struct image *g;
struct xref *x;
struct margin *m;
if (!layout)
return;
l = layout->lines;
while (l) {
struct line *ol = l;
l = l->next;
line_free(ol);
}
x = layout->xrefs;
while (x) {
struct xref *ox = x;
x = x->next;
xref_free(ox);
}
m = layout->margin;
while (m) {
struct margin *om = m;
m = m->next;
margin_free(om);
}
g = layout->images;
while (g) {
struct image *og = g;
g = g->next;
if (!cache_have(layout->img_cache, og->image))
og->free_it = 0; /* do not free from cache */
cache_forget(layout->img_cache, og->image);
image_free(og);
}
layout->images = NULL;
layout->xrefs = NULL;
layout->lines = NULL;
layout->margin = NULL;
memset(layout->scnt, 0, sizeof(layout->scnt));
memset(layout->saved_align, 0, sizeof(layout->saved_align));
memset(layout->saved_valign, 0, sizeof(layout->saved_valign));
layout->acnt = 0;
layout->vcnt = 0;
}
word_t txt_layout_words_ex(layout_t lay, struct line *line, word_t v, int off, int height)
{
struct layout *layout = (struct layout*)lay;
struct word *w = (struct word*)v;
if (!lay)
return NULL;
if (!w) {
if (!line)
line = layout->lines;
if (!line)
return NULL;
w = line->words;
} else {
line = w->line;
w = w->next;
}
for (; (!w || (line->y + line->h) < off) && line->next; line = line->next, w = line->words);
if ((line->y + line->h) < off)
w = NULL;
else if (height >= 0 && line->y - off > height)
w = NULL;
return w;
}
word_t txt_layout_words(layout_t lay, word_t v)
{
return txt_layout_words_ex(lay, NULL, v, 0, -1);
}
void txt_layout_free(layout_t lay)
{
struct layout *layout = (struct layout *)lay;
_txt_layout_free(lay);
if (lay) {
cache_free(layout->img_cache);
cache_free(layout->prerend_cache);
cache_free(layout->hlprerend_cache);
layout->img_cache = NULL;
layout->prerend_cache = NULL;
layout->hlprerend_cache = NULL;
free(lay);
}
}
#define TOKEN_NONE 0x000
#define TOKEN_A 0x001
#define TOKEN_B 0x002
#define TOKEN_I 0x004
#define TOKEN_U 0x008
#define TOKEN_S 0x010
#define TOKEN_C 0x020
#define TOKEN_R 0x040
#define TOKEN_J 0x080
#define TOKEN_L 0x100
#define TOKEN_T 0x200
#define TOKEN_D 0x400
#define TOKEN_M 0x800
#define TOKEN_X 0x1000
#define TOKEN_CLOSE 0x2000
#define TOKEN(x) (x & 0x1fff)
int get_token(const char *ptr, char **eptr, char **val, int *sp)
{
char *ep, *p;
int closing = 0;
*eptr = NULL;
p = (char*)ptr;
ptr += strspn(ptr, " \t");
if (sp) {
*sp = 0;
if (p != ptr)
*sp = 1;
}
if (val)
*val = NULL;
if (!*ptr)
return 0;
if (*ptr != '<')
return 0;
ptr ++;
if (*ptr == '/') {
closing = 1;
if (!ptr[1] || ptr[2] != '>')
return 0;
ptr ++;
}
switch (*ptr) {
case 'x':
if (ptr[1] != ':')
return 0;
ptr += 2;
ep = find_in_esc(ptr, "\\>");
if (*ep != '>')
return 0;
if (val) {
p = malloc(ep - ptr + 1);
if (!p)
return 0;
memcpy(p, ptr, ep - ptr);
p[ep - ptr] = 0;
*val = p;
}
*eptr = ep + 1;
return TOKEN_X;
case 'a':
if (closing) {
*eptr = (char*)ptr + 2;
return TOKEN_A | TOKEN_CLOSE;
}
if (ptr[1] != ':') {
return 0;
}
ptr += 2;
// ep = (char*)ptr + strcspn(ptr, ">");
ep = find_in_esc(ptr, "\\>");
if (*ep != '>') {
return 0;
}
if (val) {
p = malloc(ep - ptr + 1);
if (!p)
return 0;
memcpy(p, ptr, ep - ptr);
p[ep - ptr] = 0;
parse_esc_string(p, val);
if (*val)
free(p);
else
*val = p;
}
*eptr = ep + 1;
return TOKEN_A;
case 'b':
if (closing) {
*eptr = (char*)ptr + 2;
return TOKEN_B | TOKEN_CLOSE;
}
if (ptr[1] == '>') {
*eptr = (char*)ptr + 2;
return TOKEN_B;
}
break;
case 'i':
if (closing) {
*eptr = (char*)ptr + 2;
return TOKEN_I | TOKEN_CLOSE;
}
if (ptr[1] == '>') {
*eptr = (char*)ptr + 2;
return TOKEN_I;
}
break;
case 's':
if (closing) {
*eptr = (char*)ptr + 2;
return TOKEN_S | TOKEN_CLOSE;
}
if (ptr[1] == '>') {
*eptr = (char*)ptr + 2;
return TOKEN_S;
}
break;
case 't':
if (closing) {
*eptr = (char*)ptr + 2;
return TOKEN_T | TOKEN_CLOSE;
}
if (ptr[1] == '>') {
*eptr = (char*)ptr + 2;
return TOKEN_T;
}
break;
case 'd':
if (closing) {
*eptr = (char*)ptr + 2;
return TOKEN_D | TOKEN_CLOSE;
}
if (ptr[1] == '>') {
*eptr = (char*)ptr + 2;
return TOKEN_D;
}
break;
case 'm':
if (closing) {
*eptr = (char*)ptr + 2;
return TOKEN_M | TOKEN_CLOSE;
}
if (ptr[1] == '>') {
*eptr = (char*)ptr + 2;
return TOKEN_M;
}
break;
case 'u':
if (closing) {
*eptr = (char*)ptr + 2;
return TOKEN_U | TOKEN_CLOSE;
}
if (ptr[1] == '>') {
*eptr = (char*)ptr + 2;
return TOKEN_U;
}
break;
case 'c':
if (closing) {
*eptr = (char*)ptr + 2;
return TOKEN_C | TOKEN_CLOSE;
}
if (ptr[1] == '>') {
*eptr = (char*)ptr + 2;
return TOKEN_C;
}
break;
case 'r':
if (closing) {
*eptr = (char*)ptr + 2;
return TOKEN_R | TOKEN_CLOSE;
}
if (ptr[1] == '>') {
*eptr = (char*)ptr + 2;
return TOKEN_R;
}
break;
case 'j':
if (closing) {
*eptr = (char*)ptr + 2;
return TOKEN_J | TOKEN_CLOSE;
}
if (ptr[1] == '>') {
*eptr = (char*)ptr + 2;
return TOKEN_J;
}
break;
case 'l':
if (closing) {
*eptr = (char*)ptr + 2;
return TOKEN_L | TOKEN_CLOSE;
}
if (ptr[1] == '>') {
*eptr = (char*)ptr + 2;
return TOKEN_L;
}
break;
}
return 0;
}
static int is_delim(int c)
{
switch(c) {
case '.':
case ',':
case ':':
case '!':
case '+':
case '-':
case '?':
case '/':
return 1;
}
return 0;
}
static int process_word_token(const char *p, char **eptr, char ch)
{
char *ep;
if (eptr)
*eptr = (char*)p;
if (!p)
return 0;
if (p[0] != '<' || p[1] != ch || p[2] != ':')
return 0;
p += 3;
ep = find_in_esc(p, "\\>");
if (*ep != '>')
return 0;
if (eptr)
*eptr = ep + 1;
return (ep - p + 1);
}
static int word_img(const char *p, char **eptr)
{
return process_word_token(p, eptr, 'g');
}
static int word_token(const char *p, char **eptr)
{
return process_word_token(p, eptr, 'w');
}
static const char *lookup_token_or_sp(const char *ptr)
{
char *eptr;
const char *p = ptr;
while (*p) {
p += strcspn(p, " .,:!+-?/<\t\n");
if (*p != '<' ) {
while (is_delim(*p))
p ++;
/* if (is_delim(*p))
p ++; */
return p;
}
if (!get_token(p, &eptr, NULL, NULL)) {
if (word_img(p, &eptr)) {
if (p == ptr) /* first one */
p = eptr;
return p;
} else if (word_token(p, &eptr)) {
if (p == ptr) /* first one */
p = eptr;
return p;
}
p ++;
continue;
}
return p;
}
return ptr;
}
#define BREAK_NONE 0
#define BREAK_SPACE 1
static char *get_word(const char *ptr, char **eptr, int *sp)
{
const char *ep;
char *o;
size_t sz;
*eptr = NULL;
o = (char*)ptr;
ptr += strspn(ptr, " \t");
if (sp) {
*sp = BREAK_NONE;
if (o != ptr)
*sp = BREAK_SPACE;
}
if (!*ptr)
return NULL;
ep = lookup_token_or_sp(ptr);
// ep += strcspn(ep, " \t\n");
sz = ep - ptr;
o = malloc(sz + 1);
memcpy(o, ptr, sz);
o[sz] = 0;
sz = word_img(ptr, eptr);
if (sz)
return o;
sz = word_token(ptr, eptr);
if (sz)
return o;
*eptr = (char*)ep;
return o;
}
void layout_debug(struct layout *layout)
{
struct line *line;
struct word *word;
line = layout->lines;
while (line) {
printf("%d of %d)", line->y, line->num);
word = line->words;
while (word) {
printf("%d)%s ", word->x, word->word);
word = word->next;
}
printf("\n");
line = line->next;
}
}
void txt_layout_color(layout_t lay, color_t fg)
{
struct layout *layout = (struct layout*)lay;
if (!lay)
return;
layout->col = fg;
}
void txt_layout_font_height(layout_t lay, float height)
{
struct layout *layout = (struct layout*)lay;
if (!lay)
return;
layout->fn_height = height;
}
void txt_layout_link_color(layout_t lay, color_t link)
{
struct layout *layout = (struct layout*)lay;
if (!lay)
return;
layout->lcol = link;
}
void txt_layout_active_color(layout_t lay, color_t link)
{
struct layout *layout = (struct layout*)lay;
if (!lay)
return;
layout->acol = link;
}
void txt_layout_link_style(layout_t lay, int style)
{
struct layout *layout = (struct layout*)lay;
if (!lay)
return;
layout->lstyle = style;
}
static char *word_cache_string(struct word *w, Uint32 style)
{
char *p;
int len = 0;
len = (w->word)?strlen(w->word):0;
len += 16;
p = malloc(len);
if (!p)
return NULL;
sprintf(p, "%s-%08x", (w->word)?w->word:"", style);
return p;
}
static void word_render(struct layout *layout, struct word *word, int x, int y)
{
char *wc = NULL;
img_t s;
img_t prerend = NULL;
img_t hlprerend = NULL;
color_t fgcol = { .r = layout->col.r, .g = layout->col.g, .b = layout->col.b };
color_t lcol = { .r = layout->lcol.r, .g = layout->lcol.g, .b = layout->lcol.b };
color_t acol = { .r = layout->acol.r, .g = layout->acol.g, .b = layout->acol.b };
Uint32 style;
if (!word->xref) {
style = (fgcol.r << 24) + (fgcol.g << 16) + (fgcol.b << 8);
} else if (word->xref->active) {
style = (acol.r << 24) + (acol.g << 16) + (acol.b << 8);
} else {
style = (lcol.r << 24) + (lcol.g << 16) + (lcol.b << 8);
}
if (word->xref && !word->style) {
fnt_style(layout->fn, layout->lstyle);
wc = word_cache_string(word, layout->lstyle | style);
} else {
fnt_style(layout->fn, word->style);
wc = word_cache_string(word, word->style | style);
}
if (!wc)
return;
if (!word->xref) {
if (!word->prerend) {
prerend = cache_get(layout->prerend_cache, wc);
if (!prerend) {
word->prerend = fnt_render(layout->fn, word->word, fgcol);
word->prerend = gfx_display_alpha(word->prerend);
cache_add(layout->prerend_cache, wc, word->prerend);
} else {
word->prerend = prerend;
}
}
s = word->prerend;
} else if (word->xref->active) {
if (!word->hlprerend) {
hlprerend = cache_get(layout->hlprerend_cache, wc);
if (!hlprerend) {
word->hlprerend = fnt_render(layout->fn, word->word, acol);
word->hlprerend = gfx_display_alpha(word->hlprerend);
cache_add(layout->hlprerend_cache, wc, word->hlprerend);
} else {
word->hlprerend = hlprerend;
}
}
s = word->hlprerend;
} else {
if (!word->prerend) {
prerend = cache_get(layout->prerend_cache, wc);
if (!prerend) {
word->prerend = fnt_render(layout->fn, word->word, lcol);
word->prerend = gfx_display_alpha(word->prerend);
cache_add(layout->prerend_cache, wc, word->prerend);
} else {
word->prerend = prerend;
}
}
s = word->prerend;
}
free(wc);
if (!s)
return;
gfx_draw(s, x, y);
}
fnt_t txt_layout_font(layout_t lay)
{
struct layout *layout = lay;
if (!lay)
return NULL;
return layout->fn;
}
static int vertical_align(struct word *w, int *hh)
{
int h;
struct line *line = w->line;
struct layout *layout = line->layout;
if (w->img)
h = gfx_img_h(w->img);
else
h = fnt_height(layout->fn);
if (hh)
*hh = h;
if (w->img && w->img_align)
return 0;
if (w->valign == ALIGN_TOP)
return 0;
else if (w->valign == ALIGN_BOTTOM)
return line->h - h;
return (line->h - h) / 2;
}
static void word_image_render(struct word *word, int x, int y, clear_fn clear, update_fn update)
{
struct line *line = word->line;
struct layout *layout = line->layout;
int yy;
if (clear && !word->xref)
return;
yy = vertical_align(word, NULL);
if (clear) {
if (word->img) {
if (word->img_align)
clear(x + word->x, y + line->y + yy, gfx_img_w(word->img), gfx_img_h(word->img));
else
clear(x + line->x + word->x, y + line->y + yy, gfx_img_w(word->img), gfx_img_h(word->img));
} else
clear(x + line->x + word->x, y + line->y/* + yy*/, word->w, line->h);
}
if (word->img) {
if (word->img_align)
gfx_draw(word->img, x + word->x, y + line->y + yy);
else
gfx_draw(word->img, x + line->x + word->x, y + line->y + yy);
if (update)
update(x + word->x, y + line->y + yy, gfx_img_w(word->img), gfx_img_h(word->img));
} else {
word_render(layout, word, x + line->x + word->x, y + yy + line->y);
if (update)
update(x + line->x + word->x, y + line->y + yy, word->w, line->h);
}
}
void xref_update(xref_t pxref, int x, int y, clear_fn clear, update_fn update)
{
int i;
struct xref *xref = (struct xref*)pxref;
struct layout *layout;
struct word *word;
if (!xref)
return;
layout = xref->layout;
if (layout->box) {
gfx_clip(x, y, layout->box->w, layout->box->h);
y -= (layout->box)->off;
}
for (i = 0; i < xref->num; i ++) {
word = xref->words[i];
if (!word->img_align)
word_image_render(word, x, y, clear, update);
}
gfx_noclip();
}
void txt_layout_draw_ex(layout_t lay, struct line *line, int x, int y, int off, int height, clear_fn clear)
{
void *v;
img_t img;
struct layout *layout = (struct layout*)lay;
struct margin *margin;
struct word *word;
// line = layout->lines;
// gfx_clip(x, y, layout->w, layout->h);
if (!lay)
return;
for (v = NULL; (img = txt_layout_images(lay, &v)); )
gfx_dispose_gif(img);
for (margin = layout->margin; margin; margin = margin->next) {
if (margin->y + margin->h < off)
continue;
if (margin->y - off > height)
continue;
word_image_render(margin->word, x, y, clear, NULL);
}
if (!line)
line = layout->lines;
for (; line; line= line->next) {
if ((line->y + line->h) < off)
continue;
if (line->y - off > height)
break;
for (word = line->words; word; word = word->next ) {
if (!word->img_align)
word_image_render(word, x, y, clear, NULL);
}
}
cache_shrink(layout->prerend_cache);
cache_shrink(layout->hlprerend_cache);
cache_shrink(layout->img_cache);
// gfx_noclip();
}
void txt_layout_draw(layout_t lay, int x, int y)
{
struct layout *layout = (struct layout*)lay;
txt_layout_draw_ex(lay, NULL, x, y, 0, layout->h, 0);
}
textbox_t txt_box(int w, int h)
{
struct textbox *box;
box = malloc(sizeof(struct textbox));
if (!box)
return NULL;
box->lay = NULL; //(struct layout*)lay;
box->w = w;
box->h = h;
box->off = 0;
box->line = NULL; //(box->lay)->lines;
return box;
}
void txt_box_norm(textbox_t tbox)
{
struct textbox *box = (struct textbox *)tbox;
struct line *line;
int off = box->off;
if (!tbox || !box->lay)
return;
box->line = box->lay->lines;
for (line = box->line; line; line = line->next) {
if (off < line->h)
break;
off -= line->h;
box->line = line;
}
}
layout_t txt_box_layout(textbox_t tbox)
{
struct textbox *box = (struct textbox *)tbox;
if (!box)
return NULL;
return box->lay;
}
void txt_box_set(textbox_t tbox, layout_t lay)
{
struct textbox *box = (struct textbox *)tbox;
if (!box)
return;
box->lay = (struct layout*)lay;
box->off = 0;
if (lay)
box->lay->box = box;
txt_box_norm(tbox);
}
void txt_box_resize(textbox_t tbox, int w, int h)
{
struct textbox *box = (struct textbox *)tbox;
if (!tbox)
return;
box->w = w;
box->h = h;
txt_box_norm(tbox);
}
void txt_box_size(textbox_t tbox, int *w, int *h)
{
struct textbox *box = (struct textbox *)tbox;
if (!tbox)
return;
if (w)
*w = box->w;
if (h)
*h = box->h;
}
void txt_box_scroll_next(textbox_t tbox, int disp)
{
int off, h;
struct textbox *box = (struct textbox *)tbox;
struct line *line;
if (!tbox)
return;
line = box->line;
if (!line)
return;
txt_layout_real_size(box->lay, NULL, &h);
if (h - box->off < box->h)
return;
off = h - box->off - box->h;
if (disp > off)
disp = off;
off = box->off - line->y; /* offset from cur line */
off += disp; /* needed offset */
while (line->next && off >= line->next->y - line->y) {
off -= (line->next->y - line->y);
line = line->next;
}
box->line = line;
box->off = line->y + off;
}
void txt_box_scroll_prev(textbox_t tbox, int disp)
{
int off;
struct textbox *box = (struct textbox *)tbox;
struct line *line;
if (!tbox)
return;
line = box->line;
if (!line)
return;
off = box->off - line->y; /* offset from cur line */
off -= disp; /* offset from current line */
while (line->prev && off < 0) {
line = line->prev;
off += (line->next->y - line->y);
}
box->line = line;
box->off = line->y + off;
if (box->off <0)
box->off = 0;
}
void txt_box_scroll(textbox_t tbox, int disp)
{
if (!tbox)
return;
if (disp >0)
return txt_box_scroll_next(tbox, disp);
else if (disp <0)
return txt_box_scroll_prev(tbox, -disp);
}
void txt_box_next_line(textbox_t tbox)
{
struct textbox *box = (struct textbox *)tbox;
struct line *line;
if (!box || !box->lay)
return;
line = box->line;
if (!line)
return;
// txt_box_norm(tbox);
if (box->lay->h - box->off < box->h)
return;
line = line->next;
if (line) {
box->off = line->y;
box->line = line;
}
}
void txt_box_prev_line(textbox_t tbox)
{
struct textbox *box = (struct textbox *)tbox;
struct line *line;
if (!box || !box->lay)
return;
line = box->line;
if (!line)
return;
line = line->prev;
if (line) {
box->line = line;
box->off = line->y;
} else
box->off = 0;
}
int txt_box_off(textbox_t tbox)
{
struct textbox *box = (struct textbox *)tbox;
if (!box)
return -1;
return box->off;
}
void txt_box_next(textbox_t tbox)
{
struct textbox *box = (struct textbox *)tbox;
struct line *line;
if (!tbox)
return;
line = box->line;
if (!line)
return;
for (; line; line = line->next) {
if ((line->y + line->h - box->off) >= box->h)
break;
}
if (line) {
box->off += (line->y - box->off);
box->line = line;
}
}
void txt_box_prev(textbox_t tbox)
{
struct textbox *box = (struct textbox *)tbox;
struct layout *lay;
struct line *line;
if (!tbox)
return;
lay = box->lay;
if (!lay)
return;
line = box->line;
if (!line)
return;
for (; line; line = line->prev) {
if ((box->off - line->y) >= box->h)
break;
}
if (!line) {
box->off = 0;
box->line = lay->lines;
return;
}
box->off = line->y;
box->line = line;
}
xref_t txt_box_xrefs(textbox_t tbox)
{
struct textbox *box = (struct textbox*)tbox;
struct xref *xref = NULL;
struct word *word = NULL;
struct line *line;
if (!tbox)
return NULL;
for (line = box->line; line; line = line->next) {
if (line->y < box->off)
continue; /* too high */
if (line->y + line->h > box->h + box->off)
break; /* bottom */
for (word = line->words; word; word = word->next) {
xref = word->xref;
if (!xref || word->img_align)
continue;
return xref;
}
}
return xref;
}
xref_t txt_box_xref(textbox_t tbox, int x, int y)
{
struct textbox *box = (struct textbox*)tbox;
struct xref *xref = NULL;
struct word *word = NULL;
struct line *line;
if (!tbox)
return NULL;
y += box->off;
if (x < 0)
return NULL;
if (y < 0)
return NULL;
if (x >= box->w)
return NULL;
for (line = box->line; line; line = line->next) {
int hh, yy;
if (y < line->y)
break;
if (y > line->y + line->h)
continue;
for (word = line->words; word; word = word->next) {
yy = vertical_align(word, &hh);
if (y < line->y + yy || y > line->y + yy + hh)
continue;
if (x < line->x + word->x)
continue;
xref = word->xref;
if (!xref)
continue;
if (x < line->x + word->x + word->w)
break;
if (word->next && word->next->xref == xref && x < line->x + word->next->x + word->next->w) {
yy = vertical_align(word->next, &hh);
if (y < line->y + yy || y > line->y + yy + hh)
continue;
break;
}
}
}
if (word && xref) {
return xref;
}
return NULL;
}
void txt_box_free(textbox_t tbox)
{
if (!tbox)
return;
free(tbox);
}
img_t txt_box_render(textbox_t tbox)
{
SDL_Surface *old_screen;
img_t dst;
struct textbox *box = (struct textbox *)tbox;
if (!tbox)
return NULL;
dst = gfx_new(box->w, box->h);
if (!dst)
return NULL;
old_screen = screen;
screen = (SDL_Surface*)dst;
gfx_clear(0, 0, box->w, box->h);
// gfx_clip(0, 0, box->w, box->h);
// printf("line: %d\n", box->line->y);
txt_layout_draw_ex(box->lay, box->line, 0, - box->off, box->off, box->h, NULL);
// gfx_noclip();
screen = old_screen;
return dst;
}
void txt_box_draw(textbox_t tbox, int x, int y)
{
struct textbox *box = (struct textbox *)tbox;
if (!tbox)
return;
gfx_clip(x, y, box->w, box->h);
// printf("line: %d\n", box->line->y);
txt_layout_draw_ex(box->lay, box->line, x, y - box->off, box->off, box->h, NULL);
gfx_noclip();
}
void txt_box_update_links(textbox_t tbox, int x, int y, clear_fn clear)
{
struct textbox *box = (struct textbox *)tbox;
if (!tbox)
return;
gfx_clip(x, y, box->w, box->h);
// printf("line: %d\n", box->line->y);
txt_layout_draw_ex(box->lay, box->line, x, y - box->off, box->off, box->h, clear);
gfx_noclip();
}
void txt_layout_update_links(layout_t layout, int x, int y, clear_fn clear)
{
struct layout *lay = (struct layout *)layout;
// gfx_clip(x, y, box->w, box->h);
// printf("line: %d\n", box->line->y);
txt_layout_draw_ex(lay, lay->lines, x, y, 0, lay->h, clear);
// gfx_noclip();
}
img_t get_img(struct layout *layout, char *p, int *al)
{
int len;
int align;
img_t img;
struct image *image;
int escaped = 0;
*al = 0;
len = word_img(p, NULL);
if (!len)
return NULL;
p += 3;
p[len - 1] = 0;
align = strcspn(p, "|");
if (!p[align])
align = 0;
else {
if (!strcmp(p + align + 1, "left"))
*al = ALIGN_LEFT;
else if (!strcmp(p + align + 1, "right"))
*al = ALIGN_RIGHT;
if (*al) {
p[align] = 0;
if (align && p[align - 1] == '\\') {
p[align - 1] = 0;
escaped = 1;
}
}
}
img = layout_lookup_image(layout, p);
if (img)
goto out;
img = cache_get(layout->img_cache, p);
if (!img) {
unix_path(p);
if (!(img = gfx_load_image(p)))
goto out;
theme_img_scale(&img); /* bad style, no gfx layer :( */
}
image = image_new(p, img);
if (!image) {
gfx_free_image(img);
img = NULL;
} else {
layout_add_image(layout, image);
image->free_it = 1; /* free on layout destroy */
// if (gfx_img_w(img) <= GFX_MAX_CACHED_W && gfx_img_h(img) <= GFX_MAX_CACHED_H)
cache_add(layout->img_cache, p, img);
}
out:
if (align) {
p[align] = '|';
if (escaped)
p[align - 1] = '\\';
}
p[len - 1] = '>';
if (!img)
*al = 0;
return img;
}
static char *get_word_token(char *p, int *token)
{
int len;
char *r;
char *val = NULL;
len = word_token(p, NULL);
if (token)
*token = 0;
if (!len)
return p;
if (token)
*token = 1;
p[len - 1 + 3] = 0;
r = strdup((p + 3));
parse_esc_string(r, &val);
free(p);
if (val) {
free(r);
r = val;
}
return r;
}
char *process_token(char *ptr, struct layout *layout, struct line *line, struct xref **xref, int *sp)
{
int token;
char *val = NULL;
int bit = 0;
int al = 0;
int *cnt = NULL;
char *eptr;
token = get_token(ptr, &eptr, &val, sp);
if (!token)
return NULL;
if (TOKEN(token) == TOKEN_B) {
cnt = &layout->scnt[0];
bit = TTF_STYLE_BOLD;
} else if (TOKEN(token) == TOKEN_I) {
cnt = &layout->scnt[1];
bit = TTF_STYLE_ITALIC;
} else if (TOKEN(token) == TOKEN_U) {
cnt = &layout->scnt[2];
bit = TTF_STYLE_UNDERLINE;
} else if (TOKEN(token) == TOKEN_S) {
cnt = &layout->scnt[3];
#ifdef TTF_STYLE_STRIKETHROUGH
bit = TTF_STYLE_STRIKETHROUGH;
#else
bit = TTF_STYLE_ITALIC;
#endif
}
if (bit) {
if (token & TOKEN_CLOSE) {
-- (*cnt);
if (*cnt < 0) /* fuzzy */
*cnt = 0;
if (!*cnt)
layout->style &= ~bit;
} else {
++ (*cnt);
layout->style |= bit;
}
goto out;
}
if (TOKEN(token) == TOKEN_L)
al = ALIGN_LEFT;
else if (TOKEN(token) == TOKEN_R)
al = ALIGN_RIGHT;
else if (TOKEN(token) == TOKEN_C)
al = ALIGN_CENTER;
else if (TOKEN(token) == TOKEN_J)
al = ALIGN_JUSTIFY;
if (al) {
if (token & TOKEN_CLOSE) {
layout->acnt --;
if (layout->acnt <0)
layout->acnt = 0;
layout->align = layout->saved_align[layout->acnt];
} else {
layout->saved_align[layout->acnt] = layout->align;
layout->acnt ++;
if (layout->acnt >= ALIGN_NEST)
layout->acnt = ALIGN_NEST - 1;
layout->align = al;
line->align = al;
}
goto out;
}
al = 0;
if (TOKEN(token) == TOKEN_T)
al = ALIGN_TOP;
else if (TOKEN(token) == TOKEN_D)
al = ALIGN_BOTTOM;
else if (TOKEN(token) == TOKEN_M)
al = ALIGN_MIDDLE;
if (al) {
if (token & TOKEN_CLOSE) {
layout->vcnt --;
if (layout->vcnt <0)
layout->vcnt = 0;
layout->valign = layout->saved_valign[layout->vcnt];
} else {
layout->saved_valign[layout->vcnt] = layout->valign;
layout->vcnt ++;
if (layout->vcnt >= ALIGN_NEST)
layout->vcnt = ALIGN_NEST - 1;
layout->valign = al;
}
goto out;
}
if (TOKEN(token) == TOKEN_X) {
int xpos;
if (strchr(val, '%') && sscanf(val, "%d%%", &xpos) == 1) {
xpos = layout->w * xpos / 100;
} else {
xpos = atoi(val);
xpos = xpos * game_theme.scale;
}
line->tabx = xpos;
line->al_tabx = ALIGN_LEFT;
if (strstr(val, "right"))
line->al_tabx = ALIGN_RIGHT;
else if (strstr(val, "center"))
line->al_tabx = ALIGN_CENTER;
goto out;
}
if (TOKEN(token) == TOKEN_A) {
if (token & TOKEN_CLOSE) {
if (*xref)
layout_add_xref(layout, *xref);
*xref = NULL;
} else {
if (*xref) {
eptr = NULL;
goto out;
}
*xref = xref_new(val);
}
}
out:
if (val)
free(val);
return eptr;
}
int get_unbrakable_len(struct layout *layout, const char *ptr)
{
int w = 0;
int ww = 0;
char *p, *eptr;
while (ptr && *ptr) {
int sp, sp2 = 0;
while (get_token(ptr, &eptr, NULL, &sp)) {
if (sp)
sp2 ++;
ptr = eptr;
}
if (sp2)
return w;
p = get_word(ptr, &eptr, &sp);
if (!p)
return w;
if (sp || !*p || word_img(p, NULL) || word_token(p, NULL)) {
free(p);
return w;
}
txt_size(layout->fn, p, &ww, NULL);
ptr = eptr;
w += ww;
if (!*p)
ptr ++;
else if (is_delim(*(ptr - 1))) {
free(p);
break;
}
free(p);
}
return w;
}
int txt_layout_pos2off(layout_t lay, int pos, int *hh)
{
int off = 0;
struct line *line;
struct layout *layout = (struct layout*)lay;
if (!layout)
return 0;
for (line = layout->lines; line && (line->pos <= pos); line = line->next) {
off = line->y; // + line->h;
if (hh)
*hh = line->h;
}
return off;
}
void _txt_layout_add(layout_t lay, char *txt)
{
int sp = 0;
int saved_style;
int width;
int img_align;
struct margin *m;
struct line *line, *lastline = NULL;
struct layout *layout = (struct layout*)lay;
char *p, *eptr;
char *ptr = txt;
struct xref *xref = NULL;
int w, h = 0, nl = 0;
int spw;
img_t img = NULL;
if (!layout || !layout->fn)
return;
saved_style = layout->style;
fnt_style(layout->fn, 0);
txt_size(layout->fn, " ", &spw, NULL);
for (line = layout->lines; line; line = line->next) {
lastline = line;
lastline->pos = 0;
}
if (!lastline) {
line = line_new();
if (!line)
goto err;
line->h = h;
line->align = layout->align;
} else {
line = lastline;
}
line->x = layout_find_margin(layout, line->y, &width);
while (ptr && *ptr) {
struct word *word;
int sp2, addlen = 0;
int wtok;
eptr = process_token(ptr, layout, line, &xref, &sp2);
if (eptr) {
ptr = eptr;
if (xref && layout->style == saved_style)
fnt_style(layout->fn, layout->lstyle); // & ~TTF_STYLE_ITALIC);
else
fnt_style(layout->fn, layout->style);// & ~TTF_STYLE_ITALIC);
if (!ptr || !*ptr)
break;
if (sp2)
sp = -1;
continue;
}
if (sp == -1) {
p = get_word(ptr, &eptr, NULL);
sp = 1;
} else
p = get_word(ptr, &eptr, &sp);
if (!p)
break;
img = get_img(layout, p, &img_align);
if (!img_align) /* margins reset */
addlen = get_unbrakable_len(layout, eptr);
wtok = 0;
if (img) {
w = gfx_img_w(img);
h = gfx_img_h(img);
if (img_align) {
if (!line_margin(line)) {
line->y = layout_skip_margin(layout, line->y);
width = layout->w;
line->x = 0;
}
if (width - w <= 0)
img_align = 0;
}
} else {
p = get_word_token(p, &wtok);
txt_size(layout->fn, p, &w, &h);
h *= layout->fn_height;
}
nl = (wtok)?0:!*p;
if (!line->h && !img_align && line_empty(line)) /* first word ? */
line->h = h;
if (img_align && !line->w)
line->h = 0;
#if 0
if (!nl) {
int ww = width - (line->w + ((sp && line->w)?spw:0) + addlen);
p = word_hyphen(layout, p, ww, &eptr, &w);
}
#endif
if ((line->num && (line->w + ((sp && line->w)?spw:0) + w + addlen) > width) || nl) {
struct line *ol = line;
h = 0; /* reset h for new line */
if ((layout->h) && (line->y + line->h) >= layout->h)
break;
if (line != lastline) {
layout_add_line(layout, line);
}
line_align(line, width, line->align, nl);
line = line_new();
if (!line) {
free(p);
goto err;
}
line->align = layout->align;
line->h = 0;//h;
line->y = ol->y + ol->h;
// line->x = 0;
line->x = layout_find_margin(layout, line->y, &width);
// fprintf(stderr,"%d %d\n", line->x, width);
if (nl) {
ptr = eptr + 1;
}
free(p);
// ptr = eptr;
line->pos = (int)(ptr - txt);
continue;
}
if (h > line->h && !img_align)
line->h = h;
word = word_new(p);
if (!word) {
line_free(line);
goto err;
}
word->valign = layout->valign;
if (!sp && !line_empty(line))
word->unbrake = 1;
word->style = layout->style;
if (line->w && !word->unbrake)
line->w += spw;
word->w = w;
word->x = line->w;
if (line->tabx > 0) {
word->x = line->tabx - line->x;
if (line->al_tabx == ALIGN_RIGHT)
word->x -= word->w;
else if (line->al_tabx == ALIGN_CENTER)
word->x -= word->w/2;
if (word->x + word->w > width)
word->x = width - word->w;
if (word->x < line->w)
word->x = line->w;
else
line->w = word->x;
line->tabx = -1;
line->align = ALIGN_LEFT;
}
word->img = img;
word->img_align = img_align;
if (img_align && (m = margin_new())) {
int x2, w2;
x2 = layout_find_margin(layout, line->y, &w2);
if (img_align == ALIGN_LEFT) {
line->x += w;
m->w = x2 + w;
}
else
m->w = layout->w - x2 - w2 + w;
// fprintf(stderr,"w: %d %d %d\n", width, w, width - w);
width -= w;
m->h = h;
m->y = line->y;
m->align = img_align;
m->word = word;
word->w = 0;
if (img_align == ALIGN_LEFT)
word->x = x2;
else
word->x = x2 + w2 - w;
h = 0;
w = 0;
layout_add_margin(layout, m);
}
// if (line->w)
// w += spw;
line_add_word(line, word);
if (xref)
xref_add_word(xref, word);
line->w += w;
if (nl)
eptr ++;
ptr = eptr;
free(p);
}
if (layout->h == 0)
layout->h = line->y + line->h;
// if (line->num) {
if (line != lastline)
layout_add_line(layout, line);
line_align(line, width, line->align, nl);
// } else
// line_free(line);
if (xref)
layout_add_xref(layout, xref);
layout->style = saved_style;
return;
err:
txt_layout_free(layout);
return;
}
void txt_layout_add(layout_t lay, char *txt)
{
struct layout *layout = (struct layout*)lay;
if (layout)
layout->h = 0;
_txt_layout_add(lay, txt);
}
xref_t xref_next(xref_t x)
{
if (!x)
return NULL;
return ((struct xref*)x)->next;
}
xref_t xref_prev(xref_t x)
{
if (!x)
return NULL;
return ((struct xref*)x)->prev;
}
xref_t txt_layout_xrefs(layout_t lay)
{
struct layout *layout = (struct layout*)lay;
if (!layout)
return NULL;
return layout->xrefs;
}
int xref_position(xref_t x, int *xc, int *yc)
{
int i;
int w = 0;
struct line *line = NULL;
struct word *word = NULL;
struct xref *xref = (struct xref*)x;
if (!xref || !xref->num)
return -1;
for (i = 0; i < xref->num; i ++) {
word = xref->words[i];
if (!word->img_align)
w += word->w;
}
w = w/2;
for (i = 0; i < xref->num; i ++) {
word = xref->words[i];
if (word->img_align)
continue;
line = word->line;
w -= word->w;
if (w < 0)
break;
}
if (!line || !word)
return -1;
if (xc)
*xc = line->x + word->x + (word->w + w);
if (yc)
*yc = line->y + line->h / 2;
return 0;
}
xref_t txt_layout_xref(layout_t lay, int x, int y)
{
struct layout *layout = (struct layout*)lay;
struct xref *xref;
struct word *word;
struct line *line;
int i;
if (!lay || x < 0 || y < 0)
return NULL;
for (xref = layout->xrefs; xref; xref = xref->next) {
for (i = 0; i < xref->num; i ++) {
int hh,yy;
word = xref->words[i];
line = word->line;
if (word->img_align)
continue;
if (y < line->y || y > line->y + line->h)
continue;
yy = vertical_align(word, &hh);
if (y < line->y + yy || y > line->y + yy + hh)
continue;
if (x < line->x + word->x)
continue;
if (x <= line->x + word->x + word->w)
return xref;
if (word->next && word->next->xref == xref && x < line->x + word->next->x + word->next->w) {
yy = vertical_align(word->next, &hh);
if (y < line->y + yy || y > line->y + yy + hh)
continue;
return xref;
}
}
}
return NULL;
}
layout_t xref_layout(xref_t x)
{
struct xref *xref = (struct xref*)x;
if (!xref)
return NULL;
return xref->layout;
}
char *xref_get_text(xref_t x)
{
struct xref *xref = (struct xref*)x;
if (!xref)
return NULL;
return xref->link;
}
void xref_set_active(xref_t x, int val)
{
struct xref *xref = (struct xref*)x;
if (!xref)
return;
xref->active = val;
}
int xref_get_active(xref_t x)
{
struct xref *xref = (struct xref*)x;
if (!xref)
return 0;
return xref->active;
}
layout_t txt_layout(fnt_t fn, int align, int width, int height)
{
struct layout *layout;
layout = layout_new(fn, width, height);
if (!layout)
return NULL;
layout->align = align;
// _txt_layout_add(layout, txt);
return layout;
}
void txt_layout_set(layout_t lay, char *txt)
{
struct layout *layout = (struct layout*)lay;
if (!layout)
return;
layout->h = 0;
_txt_layout_free(lay);
_txt_layout_add(lay, txt);
}
void txt_layout_real_size(layout_t lay, int *pw, int *ph)
{
int w = 0;
int h = 0;
struct margin *margin;
struct line *line;
struct layout *layout = (struct layout*)lay;
if (!layout)
return;
for (line = layout->lines; line; line = line->next) {
while (!line->num && line->next)
line = line->next;
if (line->w > w)
w = line->w;
if (line->num && line->y + line->h > h)
h = line->y + line->h;
}
for (margin = layout->margin; margin; margin = margin->next) {
if (margin->y + margin->h > h)
h = margin->y + margin->h;
}
if (pw)
*pw = w;
if (ph)
*ph = h;
}
void gfx_cursor(int *xp, int *yp)
{
int x, y;
SDL_GetMouseState(&x, &y);
if (xp)
*xp = x;
if (yp)
*yp = y;
}
void gfx_warp_cursor(int x, int y)
{
SDL_WarpMouse(x, y);
}
int ALPHA_STEPS = 4;
static int fade_step_nr = -1;
int gfx_fading(void)
{
return (fade_step_nr != -1);
}
img_t *fade_bg = NULL;
static void update_gfx(void *aux)
{
img_t img = (img_t) aux;
if (fade_step_nr == -1 || !img || !fade_bg)
return;
game_cursor(CURSOR_CLEAR);
gfx_set_alpha(img, (255 * (fade_step_nr + 1)) / ALPHA_STEPS);
gfx_draw(fade_bg, 0, 0);
gfx_draw(img, 0, 0);
game_cursor(CURSOR_DRAW);
gfx_flip();
fade_step_nr ++;
if (fade_step_nr == ALPHA_STEPS) {
fade_step_nr = -1;
}
}
static Uint32 update(Uint32 interval, void *aux)
{
push_user_event(update_gfx, aux);
return interval;
}
extern void nsleep(int delay);
void gfx_change_screen(img_t src, int steps)
{
struct inp_event ev;
SDL_TimerID han;
if (steps <= 1) {
gfx_draw(src, 0, 0);
game_cursor(CURSOR_DRAW);
gfx_flip();
return;
}
memset(&ev, 0, sizeof(ev));
ALPHA_STEPS = steps;
fade_step_nr = 0;
fade_bg = gfx_grab_screen(0, 0, gfx_width, gfx_height);
if (!fade_bg) /* ok, i like kernel logic. No memory, but we must work! */
return;
han = SDL_AddTimer(60, update, src);
while (input(&ev, 1) >=0 && gfx_fading()) /* just wait for change */
game_cursor(CURSOR_ON);
SDL_RemoveTimer(han);
gfx_free_image(fade_bg);
fade_bg = NULL;
}
int gfx_init(void)
{
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0) {
fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
return -1;
}
if (!(images = cache_init(-1, gfx_free_image))) {
fprintf(stderr, "Can't init cache subsystem.\n");
gfx_done();
return -1;
}
return 0;
}
void gfx_done(void)
{
cache_free(images);
images = NULL;
SDL_Quit();
}
gtimer_t gfx_add_timer(int delay, int (*fn)(int, void*), void *aux)
{
#if SDL_VERSION_ATLEAST(1,3,0)
return (gtimer_t)SDL_AddTimer(delay, (SDL_TimerCallback)fn, aux);
#else
return (gtimer_t)SDL_AddTimer(delay, (SDL_NewTimerCallback)fn, aux);
#endif
}
void gfx_del_timer(gtimer_t han)
{
if (han)
SDL_RemoveTimer((SDL_TimerID)han);
}
unsigned long gfx_ticks(void)
{
return SDL_GetTicks();
}