/*
    zsdl.c - reusable SDL functions
    Copyright (C) 2011-2023 Ladislav Vaiz <ok1zia@nagano.cz>

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    version 2 as published by the Free Software Foundation.

*/

#include <libziaint.h>

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <zsdl.h>
#ifdef Z_HAVE_SDL
#include <SDL_syswm.h>
#endif


#ifdef Z_HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifdef Z_HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif

#include <errno.h>
#include <math.h>

#include <zdebug.h>
#include <ziconv.h>

#ifdef Z_HAVE_SDL
#include <glib.h>
//#include <SDL.h>

//#ifdef Z_MSC_MINGW_ANDROID
//double fabs(double x);
//#endif


#include <zpng.h>
#include <zthread.h>

#include "../data/font8x13.inc"
#include "../data/font9x16.inc"
#include "../data/font13x24.inc"
#include "../data/font18x32.inc"

#ifdef Z_HAVE_WINSOCK2_H
#include <winsock2.h>
#endif

#ifdef Z_HAVE_WINDOWS_H
#include <windows.h>
#endif

static struct zzsdl *zsdl = NULL;

#ifdef Z_MSC
#pragma warning(disable : 4996)
#define _CRT_SECURE_NO_WARNINGS
#endif

static short *outline9x16;


struct zzsdl *zsdl_init(SDL_Surface *screen, int font_h, int inverse, SDL_RWops* fontRW){

    if (zsdl) zsdl_free();
    zsdl = g_new0(struct zzsdl, 1);
	zsdl->screen = screen;
	zsdl->inverse = inverse;

#ifdef Z_HAVE_SDL2
	zsdl->renderer = SDL_CreateSoftwareRenderer(zsdl->screen);
	if (!zsdl->renderer) zinternal("Can't alloc software renderer");

	SDL_SetRenderDrawColor(zsdl->renderer, 0xFF, 0xFF, 0xFF, 0xFF);
	SDL_RenderClear(zsdl->renderer);

#endif

    switch(screen->format->BytesPerPixel){
        case 1:
            zsdl->putpixel = inverse ? z_putpixel8inv : z_putpixel8;
            break;
        case 2:
            zsdl->putpixel = inverse ? z_putpixel16inv : z_putpixel16;
            break;
        case 3:
            zsdl->putpixel = inverse ? z_putpixel24inv : z_putpixel24;
			if (screen->format->Bmask == 0xff) zsdl->antialiasing_supported = 1;
            break;
        case 4:
            zsdl->putpixel = inverse ? z_putpixel32inv : z_putpixel32;
			if (screen->format->Bmask == 0xff) zsdl->antialiasing_supported = 1;
            break;
    }

	zsdl->antialiasing = 0;
    //zsdl->antialiasing_supported = 0;

    zsdl->font_h = font_h;
    zsdl->font_w = zsdl_h2w(font_h);
#ifdef Z_HAVE_SDL_TTF
    TTF_Init();
    if (fontRW != NULL) {
        int w, h, pt = (int)(font_h * 0.75) + 1;
        zsdl->ttf = TTF_OpenFontRW(fontRW, 0, pt);
        if (zsdl->ttf != NULL) {
            TTF_SizeText(zsdl->ttf, "W", &w, &h);
            zsdl->font_w = w;
            zsdl->font_h = h;
        }
    }
    zsdl->ttf_iconv = iconv_open("UTF-8", "ISO8859-2");
    if (zsdl->ttf_iconv == (iconv_t)-1) zinternal("Can't create TTF iconv object UTF-8 to ISO8859-2");

#endif
    zsdl->pxformat = screen->format;

	outline9x16 = zfont_create_outline(font9x16, sizeof(font9x16), 16);

    //zsdl_font_save(screen);
	//zsdl_font_dump("font8x13.png", "font8x13.inc");
	//zsdl_font_dump("font9x16.png", "font9x16.inc");
 //   zsdl_font_dump("font13x24.png", "font13x24.inc");
	//zsdl_font_dump("font18x32.png", "font18x32.inc");
	//    zsdl_font_dump_skip_red("font8x13red.png", "font8x13.inc");
    return zsdl;
}


void zsdl_free(void){
    if (!zsdl) return;
	g_free(outline9x16);
#ifdef Z_HAVE_SDL2
	if (zsdl->renderer != NULL) SDL_DestroyRenderer(zsdl->renderer);
	if (zsdl->pxformat != NULL) SDL_FreeFormat(zsdl->pxformat);
#endif
#ifdef Z_HAVE_SDL_TTF
    if (zsdl->ttf != NULL) TTF_CloseFont(zsdl->ttf);
    iconv_close(zsdl->ttf_iconv);
#endif
	if (zsdl->to8b_cd != NULL) iconv_close(zsdl->to8b_cd);
	zsdl->to8b_cd = NULL;
	g_string_free(zsdl->to8b_gs, TRUE);

    g_free(zsdl);
    zsdl = NULL;
}

struct zzsdl *zsdl_instance(){
	return zsdl;
}

int zsdl_h2w(int font_h){
	switch (font_h){
		case 13:
			return 8;
		case 16: 
			return 9; 
		case 24:
			return 13;
        case 26:
            return 16;
        case 32:
            return 18;
        case 48:
            return 26;
        case 64:
            return 36;
		default:
			return (font_h * 9) / 16;
	}
}

SDL_Surface *z_create_surface(struct zzsdl *zsdl, int w, int h){
	SDL_Surface *surface;

	surface = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h,
		zsdl->pxformat->BitsPerPixel,
		zsdl->pxformat->Rmask,
		zsdl->pxformat->Gmask,
		zsdl->pxformat->Bmask,
	    zsdl->pxformat->Amask);
	return surface;
}

#ifdef Z_HAVE_SDL2
void SDL_UpdateRect(SDL_Surface *surface, int x, int y, int w, int h){
	/*static int iii = 0;
	SDL_Rect r;
	r.x = iii * 10;
	r.y = iii * 10;
	r.w = 10;
	r.h = 10;
	SDL_FillRect(zsdl->screen, &r, rand());
	iii++; */

	//SDL_RenderPresent(zsdl->renderer);
	SDL_UpdateWindowSurface(zsdl->window);
}
#endif

int zsdl_max_font_h(int w, int h, char *text){
	int ww;
	int l = strlen(text);

	ww = zsdl_h2w(64) * l;
	if (h >= 64 && w >= ww) return 64;

	ww = zsdl_h2w(48) * l;
	if (h >= 48 && w >= ww) return 48;

	ww = zsdl_h2w(32) * l;
	if (h >= 32 && w >= ww) return 32;

	ww = zsdl_h2w(26) * l;
	if (h >= 26 && w >= ww) return 26;

	ww = zsdl_h2w(24) * l;
	if (h >= 24 && w >= ww) return 24;

	ww = zsdl_h2w(16) * l;
	if (h >= 16 && w >= ww) return 16;

	return 13; 
}

short *zfont_create_outline(const short *font, int size_b, int font_h){
	int i, len = size_b / sizeof(short);
	short *out = (short *)g_malloc(size_b);

	for (i = 0; i < len; i++){
		out[i] = font[i];
		if ((i % font_h) != 0) out[i] |= font[i - 1];
		if (((i + 1) % font_h) != 0) out[i] |= font[i + 1];
	} 
	for (i = 0; i < len; i++){
		out[i] |= ((unsigned short)out[i] << 1) | ((unsigned short)out[i] >> 1);
	}

	return out;
}

/* returns 1 if pixel is out of rect */
int z_clip_pixel(SDL_Rect *clip, int x, int y){
    if (x < clip->x || 
        y < clip->y ||
        x >= clip -> x + clip->w || 
        y >= clip -> y + clip->h) return 1;
    return 0;
}

int z_overlapped_pixel(SDL_Rect *clip_rect, int x, int y){
    int ret;
    if (x >= clip_rect->x && 
        y >= clip_rect->y &&
        x <  clip_rect->x + clip_rect->w &&
        y <  clip_rect->y + clip_rect->h) ret=1;
    else
        ret=0;
   /* dbg("overlapper_pixel(%d, %d in %dx%d%+d%+d)=%d\n", 
            x, y, clip_rect->x, clip_rect->y, clip_rect->w, clip_rect->h, ret);*/
    return ret;
}


int z_overlapped_rect(SDL_Rect *a, SDL_Rect *b){
    int ret;

    if ( (a->x+a->w <= b->x) || /* a je vlevo od b */
         (b->x+b->w <= a->x) || /* b je vlevo od a */
         (a->y+a->h <= b->y) || /* a je nad b */
         (b->y+b->h <= a->y)) ret=0; /* b je nad a */
    else
        ret=1;
    
/*    dbg("overlapped_rect(%dx%d%+d%+d,%dx%d%+d%+d)=%d\n", a->x, a->y, a->w, a->h, b->x, b->y, b->w, b->h, ret);*/
    return ret;
}

/*
 *    X ->
 *  Y 0110 0010 1010
 *  | 0100 0000 1000
 *  V 0101 0001 1001
 */
 

int z_overlapped_line(SDL_Rect *clip_rect, int x1, int y1, int x2, int y2){
    int mask1, mask2;

    mask1=0;
    if (x1<clip_rect->x) mask1|=0x4;
    else if (x1>=clip_rect->x+clip_rect->w) mask1|=0x8;
    if (y1<clip_rect->y) mask1|=0x2;
    else if (y1>=clip_rect->y+clip_rect->h) mask1|=0x1;
    mask2=0;
    if (x2<clip_rect->x) mask2|=0x4;
    else if (x2>=clip_rect->x+clip_rect->w) mask2|=0x8;
    if (y2<clip_rect->y) mask2|=0x2;
    else if (y2>=clip_rect->y+clip_rect->h) mask2|=0x1;
    if ((mask1|mask2)==0) return 1;
    if (mask1&mask2) return 0;
    return 1;
}

    


void z_putpixel8(SDL_Surface *surface, int x, int y, int color){
    Uint8 *p;

    if (z_clip_pixel(&surface->clip_rect, x, y)) return;
    p = (Uint8 *) surface->pixels + y*surface->pitch + x;
    *p = color;
}

void z_putpixel16(SDL_Surface *surface, int x, int y, int color){
    Uint16 *p;

    if (z_clip_pixel(&surface->clip_rect, x, y)) return;
    p = (Uint16 *) surface->pixels + y*surface->pitch/2 + x;
    *p = color;
}

void z_putpixel24(SDL_Surface *surface, int x, int y, int color){
    Uint16 *p16;
    Uint8  *p8;

    if (z_clip_pixel(&surface->clip_rect, x, y)) return;
    p16 = (Uint16 *) ((Uint8 *)surface->pixels + y*surface->pitch + x*3);
    p8  = (Uint8 *)  (p16+1); 
    *p16 = color;     /* TODO Big endian? */
    *p8  = color>>16; 
}

void z_putpixel32(SDL_Surface *surface, int x, int y, int color){
    Uint32 *p;

    if (z_clip_pixel(&surface->clip_rect, x, y)) return;
    p = (Uint32 *) surface->pixels + y*surface->pitch/4 + x;
    *p = color;
}

void z_putpixel8inv(SDL_Surface *surface, int x, int y, int color){
    Uint8 *p;

    if (z_clip_pixel(&surface->clip_rect, x, y)) return;
    p = (Uint8 *) surface->pixels + y*surface->pitch + x;
    *p = ~color;
}

void z_putpixel16inv(SDL_Surface *surface, int x, int y, int color){
    Uint16 *p;

    if (z_clip_pixel(&surface->clip_rect, x, y)) return;
    p = (Uint16 *) surface->pixels + y*surface->pitch/2 + x;
    *p = ~color;
}

void z_putpixel24inv(SDL_Surface *surface, int x, int y, int color){
    Uint16 *p16;
    Uint8  *p8;

    if (z_clip_pixel(&surface->clip_rect, x, y)) return;
    p16 = (Uint16 *) ((Uint8 *)surface->pixels + y*surface->pitch + x*3);
    p8  = (Uint8 *)  (p16+1); 
    *p16 = ~color;     /* TODO Big endian? */
    *p8  = ~(color>>16); 
}

void z_putpixel32inv(SDL_Surface *surface, int x, int y, int color){
    Uint32 *p;

    if (z_clip_pixel(&surface->clip_rect, x, y)) return;
    p = (Uint32 *) surface->pixels + y*surface->pitch/4 + x;
    *p = ~color;
}

/*void z_putpixel32a(SDL_Surface *surface, double x, double y, int color){
    Uint32 *p;
	double xi, yi;

	for (xi = floor(x); xi <= ceil(x); xi += 1.0){
		for (yi = floor(y); yi <= ceil(y); yi += 1.0){
			if (z_clip_pixel(&surface->clip_rect, (int)xi, (int)yi)) return;
			p = (Uint32 *) surface->pixels + (int)y * surface->pitch / 4 + (int)x;
			*p = color;
		}
	}
} */

void z_putpixel_fmt(SDL_Surface *surface, int x, int y, int c, SDL_PixelFormat *format){
	Uint8 r, g, b, a;

	SDL_GetRGBA(c, format, &r, &g, &b, &a);
	c = SDL_MapRGBA(surface->format, r, g, b, a);
	z_putpixel(surface, x, y, c);
}

void z_putpixela(SDL_Surface *surface, int x, int y, int color, int a){
	int fr, fg, fb, b, br, bg, bb, c;

	fr = z_r(surface, color);
	fg = z_g(surface, color);
	fb = z_b(surface, color);

	b = z_getpixel(surface, x, y);
	br = z_r(surface, b);
	bg = z_g(surface, b);
	bb = z_b(surface, b);
	
	c = z_makecol(((fr * a) + (br * (255 - a))) / 255,
				  ((fg * a) + (bg * (255 - a))) / 255,
				  ((fb * a) + (bb * (255 - a))) / 255);

	z_putpixel(surface, x, y, c);

	/*for (xi = 0; xi < 16; xi++){
		for (yi = 0; yi < 16; yi++){
			z_putpixel(surface, 30 + x * 16 + xi, 30 + y * 16 + yi, c);
		}
	}*/

}

/*int z_putalpha(SDL_Surface *surface, int x, int y, Uint32 alpha){
    Uint32 *p;

    if (z_clip_pixel(&surface->clip_rect, x, y)) return;
    p = (Uint32 *) surface->pixels + y*surface->pitch/4 + x;
    *p = color;
	
} */
int z_getpixel8(SDL_Surface *surface, int x, int y){
    Uint8 *p;

    p = (Uint8 *) surface->pixels + y*surface->pitch + x;
    return *p;
}

int z_getpixel16(SDL_Surface *surface, int x, int y){
    Uint16 *p;

    p = (Uint16 *) surface->pixels + y*surface->pitch/2 + x;
    return *p;
}

int z_getpixel24(SDL_Surface *surface, int x, int y){
    Uint8 *p;
    p = (Uint8 *) surface->pixels + y*surface->pitch + x*3;
    return (p[0]) + ((p[1])<<8) + ((p[2])<<16);
/*    *p16 = color; */    /* TODO Big endian? */
/*    *p8  = color>>16; */
}

int z_getpixel32(SDL_Surface *surface, int x, int y){
    Uint32 *p;

    p = (Uint32 *) surface->pixels + y*surface->pitch/4 + x;
    return *p;
}



int z_getpixel(SDL_Surface *surface, int x, int y){
	if (x < 0 || y < 0 || x >= surface->w || y >= surface->h) return 0;

    switch(surface->format->BytesPerPixel){
        case 1:
            return z_getpixel8(surface, x, y);
        case 2:
            return z_getpixel16(surface, x, y);
        case 3:
            return z_getpixel24(surface, x, y);
        case 4:
            return z_getpixel32(surface, x, y);
        default:
            zinternal("z_getpixel: unknown BytesPerPixel %d\n", surface->format->BytesPerPixel);
            return 0;
    }
}

int z_getpixel_fmt(SDL_Surface *surface, int x, int y, SDL_PixelFormat *format){
	Uint8 r, g, b, a;
	int c = z_getpixel(surface, x, y);

	SDL_GetRGBA(c, surface->format, &r, &g, &b, &a);
	c = SDL_MapRGBA(format, r, g, b, a);
	return c;
}

//static char bits[]={1, 2, 4, 8, 16, 32, 64, 128};
static int sbits[]={1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576};


#if defined(Z_HAVE_SDL_TTF) && !defined(Z_HAVE_SDL2)
// very limited, only vertical stretch, only srcrect=NULL (taken from src)
int SDL_BlitScaled(SDL_Surface* src, const SDL_Rect* srcrect, SDL_Surface* dst, SDL_Rect* dstrect) {
    for (int y = 0; y < dstrect->h; y++) {
        int srcy = (y * src->h) / dstrect->h;
        SDL_Rect s = { .x = 0, .y = srcy, .w = src->w, .h = 1 };
        SDL_Rect d = { .x = dstrect->x, .y = dstrect->y + y, .w = dstrect->w, .h = 1 };
        SDL_BlitSurface(src, &s, dst, &d);
    }
}
#endif

int zsdl_print_ttf_char(SDL_Surface* surface, int *x, int y, int color, int bgcolor, int flags, unsigned char c) {
#ifdef Z_HAVE_SDL_TTF
    if (zsdl->ttf == NULL) return 0;
    if (c < 0x20) return 0;
    if (c >= 0x7f && c <= 0x9f) return 0;
    //if (c == 0xff) return 0;
    //if (c == 0xad) return 0;

    //if (flags & ZFONT_DOUBLEHB) return 1;

    char s[2];
    s[0] = c;
    s[1] = '\0';
    char utf[10];

    char* inptr = s;
    char* outptr = utf;
    size_t sizein = 1;
    size_t sizeout = sizeof(utf);
    size_t ret;

    ret = iconv(zsdl->ttf_iconv, &inptr, &sizein, &outptr, &sizeout);
    if (ret == (size_t)-1) {
        // no reason to fail
    }
    else {
        if (outptr != NULL) *outptr = '\0';
    }

    if (c == 0xad) {
        strcpy(utf, "\xE2\x89\xA1"); // triple bar horizontal
    }

#ifdef Z_HAVE_SDL1
#define a unused
#endif
    SDL_Color fg = { .r = z_r(surface, color), .g = z_g(surface, color), .b = z_b(surface, color), .a = SDL_ALPHA_OPAQUE };
    SDL_Color bg = { .r = z_r(surface, bgcolor), .g = z_g(surface, bgcolor), .b = z_b(surface, bgcolor), .a = SDL_ALPHA_OPAQUE };
#undef a
    SDL_Rect r = { .x = *x, .y = y, .w = zsdl->font_w, .h = zsdl->font_h };

    if ((flags & ZFONT_TRANSP) == 0) SDL_FillRect(surface, &r, bgcolor);

    SDL_Surface *src = TTF_RenderUTF8_Blended(zsdl->ttf, utf, fg);
   
    if (flags & ZFONT_OUTLINE) {
        flags &= ~(ZFONT_DOUBLEHB | ZFONT_DOUBLEHT);
        TTF_SetFontOutline(zsdl->ttf, 1);
        SDL_Surface *outline = TTF_RenderUTF8_Blended(zsdl->ttf, utf, bg);
        SDL_Rect ro = { .x = *x - 1, .y = y - 1 , .w = zsdl->font_w, .h = zsdl->font_h };
        SDL_BlitSurface(outline, NULL, surface, &ro);
        SDL_FreeSurface(outline);
        TTF_SetFontOutline(zsdl->ttf, 0);
    }

    if (flags & ZFONT_DOUBLEHT) {
        r.h *= 2;
        SDL_BlitScaled(src, NULL, surface, &r);
    }else if (flags & ZFONT_DOUBLEHB) {
        r.h *= 2;
        r.y -= zsdl->font_h;
        SDL_BlitScaled(src, NULL, surface, &r);
    }else{
        SDL_BlitSurface(src, NULL, surface, &r);
    }
    SDL_FreeSurface(src);
    *x += zsdl->font_w;
    return 1;
#endif
    return 0;
}

void zsdl_printf(SDL_Surface *surface, int x, int y, int color, int bgcolor, int flags, char *m, ...){
    int i, j, xx, fi, fj, fw, fh;
    char *c;
    const short *fs, *fs2;
    const int *fint;
    char *s, ss[2];
    va_list l;
	int font_h;
	int old_font_w = 0, old_font_h = 0;

	if (flags & ZFONT_OUTLINE){
		int flg = flags & ~ZFONT_OUTLINE;
		flg |= ZFONT_OUTLINE_FONT;
		flg |= ZFONT_TRANSP;

		va_start(l, m);
        s = g_strdup_vprintf(m, l);
        va_end(l);
		zsdl_printf(surface, x, y, bgcolor, 0, flg, "%s", s);
		g_free(s);
	}

	if (flags & ZFONT_USERHFLG)	{
		old_font_w = zsdl->font_w;
		old_font_h = zsdl->font_h;
		zsdl->font_h = (flags >> 16) & 0xff;
		zsdl->font_w = zsdl_h2w(zsdl->font_h);
	}	
	font_h = zsdl->font_h;
	    
	if (SDL_MUSTLOCK(surface)) SDL_LockSurface(surface);

    if (flags & ZFONT_CHAR){
        ss[0] = *m ? *m : ' ';
        ss[1] = '\0';
        s = ss;
    }else{
        va_start(l, m);
        s = g_strdup_vprintf(m, l);
        va_end(l);

		if (flags & ZFONT_UTF8){
			if (zsdl->to8b_cd == NULL) zsdl->to8b_cd = iconv_open("iso-8859-2//TRANSLIT", "utf-8");
            if (zsdl->to8b_cd == (iconv_t)-1) zinternal("Can't create iconv object iso-8859-2 to utf-8");

			if (zsdl->to8b_gs == NULL) zsdl->to8b_gs = g_string_sized_new(strlen(s));
            g_string_truncate(zsdl->to8b_gs, 0);
			ziconv(zsdl->to8b_cd, s, zsdl->to8b_gs);
			g_free(s);
			s = g_strdup(zsdl->to8b_gs->str);
		}
    }

    if (flags & ZFONT_CENTERX) x -= (strlen(s) * zsdl->font_w / 2);
    if (flags & ZFONT_CENTERY) y -= font_h / 2;
    if (flags & ZFONT_RIGHT)   x -= strlen(s) * zsdl->font_w;
	if (flags & (ZFONT_DOUBLEHT | ZFONT_DOUBLEHB)) font_h *= 2;
    
    xx=x;
    if (zsdl->font_h == 16){
        for (c = s; *c != '\0'; c++){
            if (zsdl_print_ttf_char(surface, &x, y, color, bgcolor, flags, *c)) continue;

			if (flags & ZFONT_OUTLINE_FONT)
	            fs = outline9x16 + ((unsigned char) *c) * 16;
			else
				fs = font9x16 + ((unsigned char) *c) * 16;

            for (i = 0; i < 16; i++){
                fi = i;
                if (flags & (ZFONT_DOUBLEHT | ZFONT_DOUBLEHB)) fi = fi / 2;
                if (flags & ZFONT_DOUBLEHB) fi += 16 / 2;
                for (j = 0; j < 9; j++){
                    if (fs[fi] & sbits[j])
                        z_putpixel(surface, x + j, y + i, color);
                    else
                        if (!(flags & ZFONT_TRANSP)) z_putpixel(surface, x + j, y + i, bgcolor);
                }
            }
            x += zsdl->font_w;
        }
    }else if (zsdl->font_h == 24){
        for (c = s; *c != '\0'; c++){
            if (zsdl_print_ttf_char(surface, &x, y, color, bgcolor, flags, *c)) continue;

            fs = font13x24 + ((unsigned char)*c) * 24;
            for (i = 0; i < 24; i++){
                fi = i;
                if (flags & (ZFONT_DOUBLEHT | ZFONT_DOUBLEHB)) fi = fi / 2;
                if (flags & ZFONT_DOUBLEHB) fi += 24 / 2;
                for (j = 0; j < 13; j++){
                    if (fs[fi] & sbits[j])
                        z_putpixel(surface, x + j, y + i, color);
                    else 
                        if (!(flags & ZFONT_TRANSP)) z_putpixel(surface, x + j, y + i, bgcolor);
                }
                //fs++;
            }
            x += zsdl->font_w;
        }
    }else if (zsdl->font_h == 13){
        for (c = s; *c != '\0'; c++){
            if (zsdl_print_ttf_char(surface, &x, y, color, bgcolor, flags, *c)) continue;
            
            fs = font8x13 + ((unsigned char) *c) * 13;
            for (i = 0; i < 13; i++){
                fi = i;
                if (flags & (ZFONT_DOUBLEHT | ZFONT_DOUBLEHB)) fi = fi / 2;
                if (flags & ZFONT_DOUBLEHB) fi += 13 / 2;
                for (j = 0; j < 8; j++){
                    if (fs[fi] & sbits[j])
                        z_putpixel(surface, x + j, y + i, color);
                    else
                        if (!(flags & ZFONT_TRANSP)) z_putpixel(surface, x + j, y + i, bgcolor);
                }
            }
            x += zsdl->font_w;
        }
    }else if (zsdl->font_h == 32){
        for (c = s; *c != '\0'; c++){
            if (zsdl_print_ttf_char(surface, &x, y, color, bgcolor, flags, *c)) continue;
        
            fint = font18x32 + ((unsigned char) *c) * 32;
            for (i = 0; i < 32; i++){
                fi = i;
                if (flags & (ZFONT_DOUBLEHT | ZFONT_DOUBLEHB)) fi = fi / 2;
                if (flags & ZFONT_DOUBLEHB) fi += 32 / 2;
                for (j = 0; j < 18; j++){
                    if (fint[fi] & sbits[j])
                        z_putpixel(surface, x + j, y + i, color);
                    else
                        if (!(flags & ZFONT_TRANSP)) z_putpixel(surface, x + j, y + i, bgcolor);
                }
            }
            x += zsdl->font_w;
        }
    }else if (zsdl->font_h >= 64){
		for (c = s; *c != '\0'; c++){
            if (zsdl_print_ttf_char(surface, &x, y, color, bgcolor, flags, *c)) continue;
            
            fint = font18x32 + ((unsigned char) *c) * 32;
            for (i = 0; i < zsdl->font_h; i++){
                fi = i * 32 / font_h;
				if (flags & ZFONT_DOUBLEHB) fi += 32 / 2;
                for (j = 0; j < zsdl->font_w; j++){
                    fj = j * 18 / (zsdl->font_w);

                    if (fint[fi] & sbits[fj])
                        z_putpixel(surface, x + j, y + i, color);
                    else
                        if (!(flags & ZFONT_TRANSP)) z_putpixel(surface, x + j, y + i, bgcolor);
                }
            }
            x += zsdl->font_w;
        }
	}else{
		fw = 9;
		fh = 16;
		fs2 = font9x16;
		if (zsdl->font_h >= 48){
			fw = 13;
			fh = 24;
			fs2 = font13x24;
		}
        for (c = s; *c != '\0'; c++){
            if (zsdl_print_ttf_char(surface, &x, y, color, bgcolor, flags, *c)) continue;
            
            fs = fs2 + ((unsigned char) *c) * fh;
            for (i = 0; i < zsdl->font_h; i++){
                fi = i * fh / font_h;
				if (flags & ZFONT_DOUBLEHB) fi += fh / 2;
                for (j = 0; j < zsdl->font_w; j++){
                    fj = j * fw / (zsdl->font_w);

                    if (fs[fi] & sbits[fj])
                        z_putpixel(surface, x + j, y + i, color);
                    else
                        if (!(flags & ZFONT_TRANSP)) z_putpixel(surface, x + j, y + i, bgcolor);
                }
            }
            x += zsdl->font_w;
        }
    }

	if (SDL_MUSTLOCK(surface)) SDL_UnlockSurface(surface);

	if (flags & ZFONT_SYNC) {
		int hh = font_h;
		int ww = xx + strlen(s) * zsdl->font_w;
		if (xx + ww >= surface->w) ww = surface->w - xx - 1;
		if (y + hh >= surface->h) hh = surface->h - y - 1;
		if (xx < 0) {
			ww += xx - 1;
			xx = 0;
		}
		if (y < 0) {
			hh += y - 1;
			y = 0;
		}
		SDL_UpdateRect(surface, xx, y, ww, font_h);
	}
    if (!(flags & ZFONT_CHAR)) g_free(s);
	if (flags & ZFONT_USERHFLG)	{
		zsdl->font_w = old_font_w;
		zsdl->font_h = old_font_h;
	}
}


void zsdl_fit(int *flg, int width, int height, char *str){
	int widths[] = { 32, 24, 16, 13, 0 };
	int i, fw, fh = 16;
	
	for (i = 0; widths[i] != 0; i++){
		fh = widths[i];
		if (fh > height) continue;
		fw = zsdl_h2w(fh);
		if (fw * (int)strlen(str) < width) break;
	}
	*flg |= ZFONT_USERH(fh);
}


void z_lineaa(SDL_Surface *surface, int x1, int y1, int x2, int y2, int color){
/*void DrawWuLine (CDC *pDC, short X0, short Y0, short X1, short Y1,
         short BaseColor, short NumLevels, unsigned short IntensityBits)*/
   unsigned short IntensityShift, ErrorAdj, ErrorAcc;
   unsigned short ErrorAccTemp, Weighting, WeightingComplementMask;
   short DeltaX, DeltaY, Temp, XDir;
   unsigned short IntensityBits = 8;//surface->format->BitsPerPixel / 3;
   short NumLevels = 256;
   
   /* Make sure the line runs top to bottom */
   if (y1 > y2) {
      Temp = y1; y1 = y2; y2 = Temp;
      Temp = x1; x1 = x2; x2 = Temp;
   }
   /* Draw the initial pixel, which is always exactly intersected by
      the line and so needs no weighting */
   z_putpixela(surface, x1, y1, color, 255);

   if ((DeltaX = x2 - x1) >= 0) {
      XDir = 1;
   } else {
      XDir = -1;
      DeltaX = -DeltaX; /* make DeltaX positive */
   }
   /* Special-case horizontal, vertical, and diagonal lines, which
      require no weighting because they go right through the center of
      every pixel */
   if ((DeltaY = y2 - y1) == 0) {
      /* Horizontal line */
      while (DeltaX-- != 0) {
         x1 += XDir;
         z_putpixel(surface, x1, y1, color);
      }
      return;
   }
   if (DeltaX == 0) {
      /* Vertical line */
      do {
         y1++;
         z_putpixel(surface, x1, y1, color);
      } while (--DeltaY != 0);
      return;
   }
   if (DeltaX == DeltaY) {
      /* Diagonal line */
      do {
         x1 += XDir;
         y1++;
         z_putpixel(surface, x1, y1, color);
      } while (--DeltaY != 0);
      return;
   }
   /* Line is not horizontal, diagonal, or vertical */
   ErrorAcc = 0;  /* initialize the line error accumulator to 0 */
   /* # of bits by which to shift ErrorAcc to get intensity level */
   IntensityShift = 16 - IntensityBits;
   /* Mask used to flip all bits in an intensity weighting, producing the
      result (1 - intensity weighting) */
   WeightingComplementMask = NumLevels - 1;
   /* Is this an X-major or Y-major line? */
   if (DeltaY > DeltaX) {
      /* Y-major line; calculate 16-bit fixed-point fractional part of a
         pixel that X advances each time Y advances 1 pixel, truncating the
         result so that we won't overrun the endpoint along the X axis */
      ErrorAdj = ((unsigned long) DeltaX << 16) / (unsigned long) DeltaY;
      /* Draw all pixels other than the first and last */
      while (--DeltaY) {
         ErrorAccTemp = ErrorAcc;   /* remember currrent accumulated error */
         ErrorAcc += ErrorAdj;      /* calculate error for next pixel */
         if (ErrorAcc <= ErrorAccTemp) {
            /* The error accumulator turned over, so advance the X coord */
            x1 += XDir;
         }
         y1++; /* Y-major, so always advance Y */
         /* The IntensityBits most significant bits of ErrorAcc give us the
            intensity weighting for this pixel, and the complement of the
            weighting for the paired pixel */
         Weighting = ErrorAcc >> IntensityShift;
         z_putpixela(surface, x1, y1, color, (Weighting ^ WeightingComplementMask));
         z_putpixela(surface, x1 + XDir, y1, color, Weighting);
      }
      /* Draw the final pixel, which is 
         always exactly intersected by the line
         and so needs no weighting */
      z_putpixela(surface, x2, y2, color, 255);
      return;
   }
   /* It's an X-major line; calculate 16-bit fixed-point fractional part of a
      pixel that Y advances each time X advances 1 pixel, truncating the
      result to avoid overrunning the endpoint along the X axis */
   ErrorAdj = ((unsigned long) DeltaY << 16) / (unsigned long) DeltaX;
   /* Draw all pixels other than the first and last */
   while (--DeltaX) {
      ErrorAccTemp = ErrorAcc;   /* remember currrent accumulated error */
      ErrorAcc += ErrorAdj;      /* calculate error for next pixel */
      if (ErrorAcc <= ErrorAccTemp) {
         /* The error accumulator turned over, so advance the Y coord */
         y1++;
      }
      x1 += XDir; /* X-major, so always advance X */
      /* The IntensityBits most significant bits of ErrorAcc give us the
         intensity weighting for this pixel, and the complement of the
         weighting for the paired pixel */
      Weighting = ErrorAcc >> IntensityShift;
      z_putpixela(surface, x1, y1, color, (Weighting ^ WeightingComplementMask));
      z_putpixela(surface, x1, y1 + 1, color, Weighting);
   }
   /* Draw the final pixel, which is always exactly intersected by the line
      and so needs no weighting */
   z_putpixela(surface, x2, y2, color, 255);
}
void z_line(SDL_Surface *surface, int x1, int y1, int x2, int y2, int color){
    int dx, dy, p;  
    int inc, tmp;

	if (zsdl->antialiasing) {
		z_lineaa(surface, x1, y1, x2, y2, color);
		return;
	}
    
    dx=abs(x1-x2);
    dy=abs(y1-y2);
    if (dx>=dy){
        p = 2*dy-dx;
        if (x1 >= x2){   
            tmp=x1;x1=x2;x2=tmp;
            tmp=y1;y1=y2;y2=tmp;
        }
        z_putpixel(surface, x1, y1, color);
        if (y2>=y1) inc=1;
        else inc=-1;
        
        while(x1 < x2){ 
            x1++;       
            if (p<0) 
                p += 2*dy;
            else{
                y1+=inc;
                p += 2*(dy-dx);
            }
            z_putpixel(surface, x1, y1, color);
        }
    }else{
        p = 2*dx-dy;
        if (y1 >= y2){
            tmp=x1;x1=x2;x2=tmp;
            tmp=y1;y1=y2;y2=tmp;
        }
        z_putpixel(surface, x1, y1, color);
        
        if (x2>=x1) inc=1;
        else inc=-1;
        
        while(y1 < y2){ 
            y1++;       
            if (p<0) 
                p += 2*dx;
            else{
                x1 += inc;
                p += 2*(dx-dy);
            }
            z_putpixel(surface, x1,y1, color);
        }
    }
} 

void z_do_line(SDL_Surface *surface, int x1, int y1, int x2, int y2, int color, 
        void (*func)(SDL_Surface *surface, int x, int y, int d) ){

    int dx, dy, p;  
    int inc, tmp;
    
    func(NULL, 0, 0, 0); // reset state (dashcnt)

    dx=abs(x1-x2);
    dy=abs(y1-y2);
    if (dx>=dy){
        p = 2*dy-dx;
        if (x1 >= x2){   
            tmp=x1;x1=x2;x2=tmp;
            tmp=y1;y1=y2;y2=tmp;
        }
        func(surface, x1, y1, color);
        if (y2>=y1) inc=1;
        else inc=-1;
        
        while(x1 < x2){ 
            x1++;       
            if (p<0) 
                p += 2*dy;
            else{
                y1+=inc;
                p += 2*(dy-dx);
            }
            func(surface, x1, y1, color);
        }
    }else{
        p = 2*dx-dy;
        if (y1 >= y2){
            tmp=x1;x1=x2;x2=tmp;
            tmp=y1;y1=y2;y2=tmp;
        }
        func(surface, x1, y1, color);
        
        if (x2>=x1) inc=1;
        else inc=-1;
        
        while(y1 < y2){ 
            y1++;       
            if (p<0) 
                p += 2*dx;
            else{
                x1 += inc;
                p += 2*(dx-dy);
            }
            func(surface, x1,y1, color);
        }
    }
} 

#define SWAP(a,b){ int tmp; \
    tmp=x##a; x##a=x##b; x##b=tmp;\
    tmp=y##a; y##a=y##b; y##b=tmp;}
    
void z_triangle(SDL_Surface *surface, int x1, int y1, int x2, int y2, int x3, int y3, int c){
    int a1,b1,c1, a2,b2,c2, a3,b3,c3, xx1, xx2,xx3,y;
    
    
    if (y2<y1) SWAP(1,2);
    if (y3<y1) { SWAP(1,3); SWAP(2,3); }
    if (y3<y2) SWAP(2,3);

    if (y1==y2 && y2==y3){ /* singularity, horizontal line */
#ifdef USE_RECT 
        SDL_Rect r;
        r.x=x1<x2?x1:x2;
        if (x3<r.x) r.x=x3;
        r.y=y1;
        r.w=x1>x2?x1:x2;
        if (x3>r.x) r.w=x3;
        r.w-=r.x;
        r.h=1;
        SDL_SetClipRect(surface, rect);   
        SDL_FillRect(surface, &r, c);
        SDL_SetClipRect(surface, NULL);   
#else        
        /* i don't want to sort X's :-) */
        z_line(surface, x1, y1, x2, y2, c);
        z_line(surface, x1, y1, x3, y3, c);
        z_line(surface, x3, y3, x2, y2, c);
#endif        
        return;
    }
    
    a1=y2-y3;
    b1=x3-x2;
    c1=y3*x2-x3*y2;
    
    a2=y1-y2;
    b2=x2-x1;
    c2=y2*x1-x2*y1;
    
    a3=y1-y3;
    b3=x3-x1;
    c3=y3*x1-x3*y1;

#ifdef USE_RECT 
    SDL_SetClipRect(surface, rect);   
#endif    
    for (y=y1;y<y2;y++){
#ifdef USE_RECT        
        SDL_Rect r;
#endif        
        xx2=-(b2*y+c2)/a2; /* a2=0 not reached because y1=y2 -> no iteration */
        xx3=-(b3*y+c3)/a3; /* a3=0 not reached because y1=y2 -> y1=y2=y3 */
#ifdef USE_RECT
        r.x=xx2<xx3?xx2:xx3;
        r.w=xx2<xx3?xx3-xx2:xx2-xx3;
        r.y=y;
        r.h=1;
        SDL_FillRect(gfx->surface, &r, c);
#else        
        z_line(surface, xx2, y, xx3, y, c);
#endif        
    }
    
    for (y=y2;y<y3;y++){
#ifdef USE_RECT        
        SDL_Rect r;
#endif        
        xx1=-(b1*y+c1)/a1; /* a1=0 not reached because y2=y3 -> no iteration */
        xx3=-(b3*y+c3)/a3;
#ifdef USE_RECT        
        r.x=xx1<xx3?xx1:xx3;
        r.w=xx1<xx3?xx3-xx1:xx1-xx3;
        r.y=y;
        r.h=1;
        SDL_FillRect(surface, &r, c);
#else        
        z_line(surface, xx1, y, xx3, y, c);
#endif        
    }
#ifdef USE_RECT    
    SDL_SetClipRect(surface, NULL);   
#endif    
	//c = z_makecol(255, 0, 0);
    //z_putpixel(surface, x3, y3, c);
	z_line(surface, x2, y2, x3, y3, c);
    
}

void z_triangle_net(SDL_Surface *surface, int c, int n, ...){
    va_list l;
	int x1, y1, x2, y2, x3, y3;

	if (n < 3) return;

    va_start(l, n);

	x1 = va_arg(l, int);
	y1 = va_arg(l, int);
	x2 = va_arg(l, int);
	y2 = va_arg(l, int);
	x3 = va_arg(l, int);
	y3 = va_arg(l, int);
	n-=3;

	while(1){
		z_triangle(surface, x1, y1, x2, y2, x3, y3, c);
		if (n == 0) break;

		n--;
		x1 = x2;
		y1 = y2;
		x2 = x3;
		y2 = y3;
		x3 = va_arg(l, int);
		y3 = va_arg(l, int);
	}
    va_end(l);
}


void z_circle(SDL_Surface *surface, int x, int y, int r, int c){
    int xx, yy, p;

/*    dbg("circle([%d,%d,%d,%d], %d, %d, %d, %x\n", rect->x, rect->y, rect->w, rect->h, x, y, r, c);*/
    p=1-r;
    yy=r;
    
    for (xx=0; xx<=yy; xx++){
        z_putpixel(surface, x+yy, y+xx, c);
        z_putpixel(surface, x+yy, y-xx, c);
        z_putpixel(surface, x-yy, y+xx, c);
        z_putpixel(surface, x-yy, y-xx, c);
                              
        z_putpixel(surface, x+xx, y+yy, c);
        z_putpixel(surface, x+xx, y-yy, c);
        z_putpixel(surface, x-xx, y+yy, c);
        z_putpixel(surface, x-xx, y-yy, c);

        if (p<0){
            p+=2*xx + 3;
        }else{
            p+=2*xx - 2*yy + 1;
            yy--;
        }
    }
}


static int dashcnt = 0;
void z_dashfce(SDL_Surface *surface, int x, int y, int d) {
    if (surface == NULL) {
        dashcnt = 0;
        return;
    }
    if (dashcnt++%6<3) return;
    z_putpixel(surface, x, y, d);
}

static int dotcnt = 0;
void z_dotfce(SDL_Surface* surface, int x, int y, int d) {
    if (surface == NULL) {
        dotcnt = 0;
        return;
    }
    if (dotcnt++ % 2 == 0) return;
    z_putpixel(surface, x, y, d);
}


void z_rect(SDL_Surface *surface, int x1, int y1, int x2, int y2, int c){
    z_line(surface, x1, y1, x2, y1, c);
    z_line(surface, x2, y1, x2, y2, c);
    z_line(surface, x1, y2, x2, y2, c);
    z_line(surface, x1, y1, x1, y2, c);
}

void z_rect2(SDL_Surface *surface, SDL_Rect *r, int c){
    z_rect(surface, r->x, r->y, r->x+r->w-1, r->y+r->h-1, c);
}

void z_cross(SDL_Surface *surface, int x, int y, int color, int zoom){
	//dbg("zoom=%d\n", zoom);
    if (zoom<2500){
        z_line(surface, x-1, y-1, x+1, y+1, color);
        z_line(surface, x-1, y+1, x+1, y-1, color);
        return;
    }
    if (zoom<4000){
		int c2;
        z_line(surface, x-2, y-2, x+2, y+2, color);
        z_line(surface, x-2, y+2, x+2, y-2, color);
		c2 = z_makecol((5 * z_r(surface, color)) / 10, 
					   (5 * z_g(surface, color)) / 10, 
					   (5 * z_b(surface, color)) / 10);
		z_putpixel(surface, x-1, y-2, c2);
		z_putpixel(surface, x+1, y-2, c2);
		z_putpixel(surface, x-2, y-1, c2);
		z_putpixel(surface, x,   y-1, c2);
		z_putpixel(surface, x+2, y-1, c2);
		z_putpixel(surface, x-1, y,   c2);
		z_putpixel(surface, x+1, y,   c2);
		z_putpixel(surface, x-2, y+1, c2);
		z_putpixel(surface, x,   y+1, c2);
		z_putpixel(surface, x+2, y+1, c2);
		z_putpixel(surface, x-1, y+2, c2);
		z_putpixel(surface, x+1, y+2, c2);

        return;
    }
    if (zoom<10000){
        z_line(surface, x-3, y-3, x+3, y+3, color);
        z_line(surface, x-2, y-3, x+3, y+2, color);
        z_line(surface, x-3, y-2, x+2, y+3, color);
        z_line(surface, x-3, y+3, x+3, y-3, color);
        z_line(surface, x-2, y+3, x+3, y-2, color);
        z_line(surface, x-3, y+2, x+2, y-3, color);
        return;
    }
    if (zoom>=10000){
        z_line(surface, x-4, y-4, x+4, y+4, color);
        z_line(surface, x-3, y-4, x+4, y+3, color);
        z_line(surface, x-4, y-3, x+3, y+4, color);
        z_line(surface, x-4, y+4, x+4, y-4, color);
        z_line(surface, x-3, y+4, x+4, y-3, color);
        z_line(surface, x-4, y+3, x+3, y-4, color);
        return;
    }
}

void z_pip(SDL_Surface *surface, int x, int y, int color1, int color2, int zoom){
    z_putpixel(surface, x, y-1, color1);
    z_putpixel(surface, x, y, color1);
    z_putpixel(surface, x, y+1, color1);
    z_putpixel(surface, x-1, y, color1);
    z_putpixel(surface, x+1, y, color1);

    z_putpixel(surface, x-1, y-1, color2);
    z_putpixel(surface, x+1, y-1, color2);
    z_putpixel(surface, x+1, y+1, color2);
    z_putpixel(surface, x-1, y+1, color2);
}


int z_makecol(int r, int g, int b){
    SDL_PixelFormat *format;

    format = zsdl->pxformat;
    return (((r>>format->Rloss)<<format->Rshift)&format->Rmask) |
           (((g>>format->Gloss)<<format->Gshift)&format->Gmask) |
           (((b>>format->Bloss)<<format->Bshift)&format->Bmask);
}

unsigned char z_r(SDL_Surface *surface, int color){
    Uint8 r, g, b;
    SDL_GetRGB(color, surface->format, &r, &g, &b);
    return r;
}

unsigned char z_g(SDL_Surface *surface, int color){
    Uint8 r, g, b;
    SDL_GetRGB(color, surface->format, &r, &g, &b);
    return g;
}

unsigned char z_b(SDL_Surface *surface, int color){
    Uint8 r, g, b;
    SDL_GetRGB(color, surface->format, &r, &g, &b);
    return b;
}

#ifdef Z_HAVE_SDL1
void z_dumpbitmap(SDL_Surface *screen, SDL_Surface *surface){
    SDL_Rect r;
    SDL_Surface *backup;
#ifdef SDL_SRCALPHA
    int screenalpha, surfacealpha;
#endif
    
    r.x = 0;
    r.y = 0;
    r.w = surface->w;
    r.h = surface->h;
    
    backup = SDL_CreateRGBSurface(SDL_SWSURFACE, surface->w, surface->h, 
                        zsdl->pxformat->BitsPerPixel,                               
                        zsdl->pxformat->Rmask, 
                        zsdl->pxformat->Gmask, 
                        zsdl->pxformat->Bmask, 
                        0);
    
    SDL_BlitSurface(screen, &r, backup, NULL);
#ifdef SDL_SRCALPHA
    screenalpha = screen->format->alpha;
    surfacealpha = surface->format->alpha;
#endif
    
    SDL_FillRect(screen, &r, z_makecol(50, 50, 50));
    SDL_UpdateRect(screen, r.x, r.y, r.w, r.h);
    usleep(100000);
    
    SDL_BlitSurface(surface, NULL, screen, NULL);
    SDL_UpdateRect(screen, r.x, r.y, r.w, r.h);
    
    usleep(300000);
    SDL_BlitSurface(backup, NULL, screen, NULL);
    SDL_UpdateRect(screen, r.x, r.y, r.w, r.h);
    
    SDL_FreeSurface(backup);
#ifdef SDL_SRCALPHA
    SDL_SetAlpha(screen, SDL_SRCALPHA, screenalpha);
    SDL_SetAlpha(surface, SDL_SRCALPHA, surfacealpha);
#endif
}
#endif

#ifdef Z_HAVE_SDL2
void z_dumpbitmap(SDL_Surface *unused, SDL_Surface *surface){
	SDL_Window *window = SDL_CreateWindow("z_dumpbitmap", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, surface->w, surface->h, 0);
	SDL_Surface *screen = SDL_GetWindowSurface(window);
	SDL_BlitSurface(surface, NULL, screen, NULL);
	SDL_UpdateWindowSurface(window);
}
#endif

void zsdl_font_save(SDL_Surface *screen){
#ifdef Z_HAVE_LIBPNG
    SDL_Surface *surface;
    int i, x, y;
    char *filename;
    int w, h, spacew, spaceh;

    w = zsdl->font_w;
    h = zsdl->font_h;
    spacew = w + 7;
    spaceh = h + 6;
    surface = SDL_CreateRGBSurface(SDL_SWSURFACE, spacew * 16, spaceh * 16, screen->format->BitsPerPixel,                               
              screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, 0);

    SDL_FillRect(surface, NULL, z_makecol(0x4f, 0x4f, 0x4f));

    for (i=0; i<256; i++){
        char ch;
        x = (i % 16) * spacew;
        y = (i / 16) * spaceh;
        ch = (char)i;
        zsdl_printf(surface, x, y, z_makecol(255, 255, 255), z_makecol(0, 0, 0), ZFONT_CHAR, &ch);
    }
#if 0
    for (i=0; i<256; i++){
//    for (i=1; i<2; i++){
//        char ch;
        int x0, y0, xd, yd;
        int white = z_makecol(255, 255, 255);
        int black = z_makecol(0, 0, 0);
        int red = z_makecol(255, 0, 0);
        x0 = (i % 16) * spacew;
        y0 = (i / 16) * spaceh;
        for (xd = 0; xd < w; xd += 2){
            for (yd = 0; yd < h; yd += 2){
                if (z_getpixel(surface, x0 + xd, y0 + yd) == white &&
                    z_getpixel(surface, x0 + xd + 2, y0 + yd + 2) == white){

                    if (z_getpixel(surface, x0 + xd + 2, y0 + yd + 1) == black)
                        z_putpixel(surface, x0 + xd + 2, y0 + yd + 1, red);
                    
                    if (z_getpixel(surface, x0 + xd + 1, y0 + yd + 2) == black)
                        z_putpixel(surface, x0 + xd + 1, y0 + yd + 2, red);
                }                                      
                
                if (z_getpixel(surface, x0 + xd, y0 + yd + 2) == white &&
                    z_getpixel(surface, x0 + xd + 2, y0 + yd) == white){

                    if (z_getpixel(surface, x0 + xd + 1, y0 + yd + 1) == black)
                        z_putpixel(surface, x0 + xd + 1, y0 + yd + 1, red);
                    
                    if (z_getpixel(surface, x0 + xd + 2, y0 + yd + 2) == black)
                        z_putpixel(surface, x0 + xd + 2, y0 + yd + 2, red);
                }
                
            }
        }
    }
    for (x = 0; x < surface->w; x++){
        for (y = 0; y < surface->h; y++){
            if (z_getpixel(surface, x, y) == z_makecol(255, 0, 0))
               z_putpixel(surface, x, y, z_makecol(255, 255, 255)); 
        }
    }

#endif

    filename = g_strdup_printf("font%dx%d.png", w, h);
    zpng_save(surface, filename, NULL);
    g_free(filename);

    SDL_FreeSurface(surface);
#endif    
}

int zsdl_font_dump(const char *pngfilename, const char *txtfilename){
    SDL_Surface *surface;
    int w, h, spacew, spaceh, i, x0, y0, x, y, z, c;
    FILE *f;
    int acc;

    surface = zpng_load(pngfilename);
    if (!surface) return -1;

    f = fopen(txtfilename, "wt");
    if (!f) return -2;

    spacew = surface->w / 16;
    spaceh = surface->h / 16;
    w = spacew - 7;
    h = spaceh - 6;

    fprintf(f, "const short font%dx%d[] = {\n", w, h);
    for (i = 0; i < 256; i++){
        x0 = (i % 16) * spacew;
        y0 = (i / 16) * spaceh;
        fprintf(f, "    ");
        for (y = 0; y < h; y++){
            acc = 0;
            z = 1;
            for (x = 0; x < w; x++){
                c = z_getpixel(surface, x0 + x, y0 + y);
                if (z_r(surface, c) > 0x80) acc |= z;
                z <<= 1;
            }
            if (y > 0) fprintf(f, ", ");
            fprintf(f, "0x%05x", acc);
        }
        fprintf(f, ",  // char %d\n", i);
    }
    fprintf(f, "};\n");
    SDL_FreeSurface(surface);
    fclose(f);
    return 0;
}


int zsdl_font_dump_skip_red(const char *pngfilename, const char *txtfilename){
    SDL_Surface *surface;
    int w, h, spacew, spaceh, i, x0, y0, x, y, z, c, red;
    FILE *f;
    short acc;

    surface = zpng_load(pngfilename);
    if (!surface) return -1;

    f = fopen(txtfilename, "wt");
    if (!f) return -2;

    spacew = surface->w / 16;
    spaceh = surface->h / 16;
    w = spacew - 7;
    h = spaceh - 6;
    red = z_makecol(255, 0, 0);

    dbg("pngfilename='%s'\n", pngfilename);
    fprintf(f, "const short font%dx%d[] = {\n", 8, 13);
    for (i = 0; i < 256; i++){
        int first = 1;
        x0 = (i % 16) * spacew;
        y0 = (i / 16) * spaceh;
        fprintf(f, "    ");
        for (y = 0; y < h; y++){
            acc = 0;
            z = 1;
            c = z_getpixel(surface, x0, y0 + y);
            if (i < 3) {
                //dbg("color[%d, %d]=%06x\n", x0, y0 + y, c);
            }

            if (c == red /* was 0x0000ff*/){
                dbg("red (char %02x, y %d)\n", i, y);
                continue;
            }
            for (x = 0; x < w; x++){
                c = z_getpixel(surface, x0 + x, y0 + y);
                if (z_r(surface, c) > 0x80) acc |= z;
                z <<= 1;
            }
            if (!first) fprintf(f, ", ");
            first = 0;
            fprintf(f, "0x%04x", acc);
            dbg("w y=%d\n", y);
        }
        fprintf(f, ",  // char %02X\n", i);
    }
    fprintf(f, "};\n");
    SDL_FreeSurface(surface);
    fclose(f);
    return 0;
}

#endif

struct zzsdl *zsdl_get(){
#ifdef Z_HAVE_SDL
    return zsdl;
#else
    return NULL;
#endif
}

#ifdef Z_HAVE_SDL

int zsdl_sys_key_repeat(int delay, int rate){
#ifdef Z_MSC_MINGW
	int wdelay;
	DWORD wrate;
	if (SystemParametersInfo(SPI_GETKEYBOARDDELAY, 0, &wdelay, 0)){
		switch (wdelay){
			case 0: delay = 250; break;
			case 1: delay = 500; break;
			case 2: delay = 750; break;
			case 3: delay = 1000; break;
			default: break; // keep default value
		}
	}

	if (SystemParametersInfo(SPI_GETKEYBOARDSPEED, 0, &wrate, 0)){
		if (wrate >= 0 && wrate <= 31){
			if (wrate < 2) wrate = 2;
			rate = wrate;
		}
	}
#endif
#ifdef Z_HAVE_SDL1
	SDL_EnableKeyRepeat(delay, rate);	// not in 2.0
#endif
	return 0;
}

#endif

#ifdef Z_ANDROID
    void SDL_ANDROID_DisableScreensaver(void);
#endif

void z_disable_screensaver(void){
#ifdef Z_MSC_MINGW
	int ret;
	ret = SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, FALSE, NULL, 0);
	/*ret = SendMessage(GetConsoleWindow(), WM_SYSCOMMAND, SC_MONITORPOWER, -1);
   	    -1	the display is powering on
		 1	the display is going to low power
		 2	the display is being shut off*/
#endif
#ifdef Z_UNIX
    char *term, *colorterm;
    int vcterm;
    
    term = getenv("TERM");
    if (!term) 
        vcterm = 1;
    else
        vcterm = (!strncmp(term, "con", 3) || !strncmp(term, "linux", 5));
    
    /* setterm -blank 0 */
    if (vcterm) {
        printf("\033[9;0]");
        fflush(stdout);
    }
    
#ifdef TIOCLINUX    
    char ioctlarg[2];
    /* setterm -powersave off */
    ioctlarg[0]=10;
    ioctlarg[1]=0; /* off */
    ioctl(0, TIOCLINUX, ioctlarg);    
#endif    
    
    colorterm = getenv("COLORTERM");
    if (!colorterm || strcmp(colorterm, "gnome-terminal") != 0){
        /* setterm -powerdown 0 */
        printf("\033[14;0]");
        fflush(stdout);
    }
    
    /* xset -dpms */
    if (getenv("DISPLAY")){
    //    system("xset -dpms ; xset s off");
        pid_t pid=fork();
        if (pid==0){
            int ret = execlp("xset", "xset", "s", "off", "-dpms", NULL);
            error("execlp xset failed, ret=%d errno=%d\n", ret, errno);
			exit(-1);
		}else{
            dbg("fork() for xset %d\n", pid);
        }
        waitpid(pid, NULL, 0);
    }
#endif
#ifdef Z_ANDROID
    SDL_ANDROID_DisableScreensaver();
#endif
}

#ifdef Z_HAVE_SDL

int zsdl_maximize(struct zzsdl *zsdl, int mode){ // 0=normal, 1=maximize, 2=toggle
#ifdef Z_MSC_MINGW
	SDL_SysWMinfo info;
	int cmd = SW_MAXIMIZE;
	WINDOWPLACEMENT plc;

	SDL_VERSION(&info.version);
	SDL_GetWindowWMInfo(zsdl->window, &info);	
	switch (mode){
		case 0: 
			cmd = SW_RESTORE;
			break;
		case 2:
			memset(&plc, 0, sizeof(plc));
			plc.length = sizeof(plc);
#ifdef Z_HAVE_SDL1
			if (!GetWindowPlacement(info.window, &plc)) break;
#endif
#ifdef Z_HAVE_SDL2
			if (!GetWindowPlacement(info.info.win.window, &plc)) break;
#endif

			if (plc.showCmd == SW_MAXIMIZE) cmd = SW_RESTORE;
			break;
	}
#ifdef Z_HAVE_SDL1
	ShowWindow(info.window, cmd);
#endif
#ifdef Z_HAVE_SDL2
	ShowWindow(info.info.win.window, cmd);
#endif
#endif
	return 0;
}

int zsdl_maximized(struct zzsdl *zsdl, int *w, int *h){
#ifdef Z_MSC_MINGW
	SDL_SysWMinfo info;
	int cmd = SW_MAXIMIZE;
	WINDOWPLACEMENT plc;

	SDL_VERSION(&info.version);
	SDL_GetWindowWMInfo(zsdl->window, &info);
	memset(&plc, 0, sizeof(plc));
	plc.length = sizeof(plc);
#ifdef Z_HAVE_SDL1
	if (!GetWindowPlacement(info.window, &plc)) return 0;
#endif
#ifdef Z_HAVE_SDL2
	if (!GetWindowPlacement(info.info.win.window, &plc)) return 0;
#endif

	if (w != NULL) *w = plc.rcNormalPosition.right - plc.rcNormalPosition.left;
	if (h != NULL) *h = plc.rcNormalPosition.bottom - plc.rcNormalPosition.top;
	return (plc.showCmd == SW_MAXIMIZE);
#else
	return 0;
#endif
	
}

int z_color_from_html(char *htmlcolor){
	unsigned int r, g, b, n;
	char *c = htmlcolor;
	if (*c == '#') c++;

	if (strlen(c) < 6) return -1;
	n = sscanf(c, "%02x%02x%02x", &r, &g, &b);
	if (n != 3) return -2;

	return z_makecol(r, g, b);
}


int z_line_intersect(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4, double *x, double *y){
	double a1, b1, c1, a3, b3, c3, d, ix, iy;

	a1 = y2 - y1;
	b1 = x1 - x2;
	c1 = 0 - a1 * x1 - b1 * y1;

	a3 = y4 - y3;
	b3 = x3 - x4;
	c3 = 0 - a3 * x3 - b3 * y3;

	d = a1 * b3 - a3 * b1;
	if (d == 0) return -1; // parallel

	ix = (- b3 * c1 + b1 * c3) / d;
	iy = (- a1 * c3 + a3 * c1) / d;

	if (fabs(y1 - y2) > fabs(x1 - x2)){
		if (iy < Z_MIN(y1, y2)) return 0; // intersect out of segment
		if (iy > Z_MAX(y1, y2)) return 0;
	}else{
		if (ix < Z_MIN(x1, x2)) return 0; // intersect out of segment
		if (ix > Z_MAX(x1, x2)) return 0;
	}

	if (fabs(y3 - y4) > fabs(x3 - x4)){
		if (iy < Z_MIN(y3, y4)) return 0; // intersect out of segment
		if (iy > Z_MAX(y3, y4)) return 0;
	}else{
		if (ix < Z_MIN(x3, x4)) return 0; // intersect out of segment
		if (ix > Z_MAX(x3, x4)) return 0;
	}

	if (x) *x = ix;
	if (y) *y = iy;

	return 1;
}

int z_point_is_in_quadrangle(double xp, double yp, double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4){
    double a, b, c, d1, d2;

    a = y2 - y1;
    b = x1 - x2;
    c = 0 - a * x1 - b * y1;
    d1 = a * xp + b * yp + c;

    a = y3 - y2;
    b = x2 - x3;
    c = 0 - a * x2 - b * y2;
    d2 = a * xp + b * yp + c;
    if (d1 < 0 && d2 > 0) return 0;
    if (d1 > 0 && d2 < 0) return 0;

    a = y4 - y3;
    b = x3 - x4;
    c = 0 - a * x3 - b * y3;
    d2 = a * xp + b * yp + c;
    if (d1 < 0 && d2 > 0) return 0;
    if (d1 > 0 && d2 < 0) return 0;

    a = y1 - y4;
    b = x4 - x1;
    c = 0 - a * x4 - b * y4;
    d2 = a * xp + b * yp + c;
    if (d1 < 0 && d2 > 0) return 0;
    if (d1 > 0 && d2 < 0) return 0;

    return 1;
}

#endif

