mirror of
https://github.com/kichikuou/xsystem35-sdl2.git
synced 2026-10-03 12:18:13 +03:00
242 lines
6.4 KiB
C
242 lines
6.4 KiB
C
/*
|
|
* Copyright (C) 1997-1998 Masaki Chikama (Wren) <chikama@kasumi.ipl.mech.nagoya-u.ac.jp>
|
|
* 1998- <masaki-c@is.aist-nara.ac.jp>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
*/
|
|
#include "config.h"
|
|
|
|
#include <limits.h>
|
|
#include <math.h>
|
|
#include <SDL.h>
|
|
#include <SDL_ttf.h>
|
|
|
|
#ifdef _WIN32
|
|
#include "win/resources.h"
|
|
#endif
|
|
|
|
#ifdef __EMSCRIPTEN__
|
|
#include <emscripten.h>
|
|
#endif
|
|
|
|
#include "portab.h"
|
|
#include "system.h"
|
|
#include "font.h"
|
|
#include "sdl_private.h"
|
|
#include "hacks.h"
|
|
|
|
typedef struct {
|
|
int size;
|
|
int type;
|
|
TTF_Font *id;
|
|
} FontTable;
|
|
|
|
#define FONTTABLEMAX 256
|
|
static FontTable fonttbl[FONTTABLEMAX];
|
|
static int fontcnt = 0;
|
|
|
|
static FontTable *fontset;
|
|
|
|
static struct {
|
|
boolean antialiase_on;
|
|
const char *name[FONTTYPEMAX];
|
|
int face[FONTTYPEMAX];
|
|
} this;
|
|
|
|
static void font_insert(int size, int type, TTF_Font *font) {
|
|
fonttbl[fontcnt].size = size;
|
|
fonttbl[fontcnt].type = type;
|
|
fonttbl[fontcnt].id = font;
|
|
|
|
if (fontcnt >= (FONTTABLEMAX -1)) {
|
|
WARNING("Font table is full.");
|
|
} else {
|
|
fontcnt++;
|
|
}
|
|
}
|
|
|
|
static FontTable *font_lookup(int size, int type) {
|
|
int i;
|
|
|
|
for (i = 0; i < fontcnt; i++) {
|
|
if (fonttbl[i].size == size && fonttbl[i].type == type) {
|
|
return &fonttbl[i];
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void font_select(int type, int size, int weight) {
|
|
FontTable *tbl;
|
|
|
|
if (NULL == (tbl = font_lookup(size, type))) {
|
|
TTF_Font *fs = TTF_OpenFontIndex(this.name[type], size, this.face[type]);
|
|
#ifdef __ANDROID__
|
|
// If `this.name[type]` is a custom font file specified in .xys35rc,
|
|
// SDL_RWFromFile used by TTF_OpenFontIndex does not work because it
|
|
// does not resolve relative path with the current directory. On the
|
|
// other hand, we can't just use fopen because SDL_RWFromFile can open
|
|
// apk assets and the default fonts are stored as assets.
|
|
if (!fs) {
|
|
FILE *fp = fopen(this.name[type], "r");
|
|
if (fp)
|
|
fs = TTF_OpenFontIndexRW(SDL_RWFromFP(fp, true), true, size, this.face[type]);
|
|
}
|
|
#endif
|
|
#ifdef _WIN32
|
|
SDL_RWops *r = open_resource(this.name[type], "fonts");
|
|
if (r)
|
|
fs = TTF_OpenFontIndexRW(r, true, size, this.face[type]);
|
|
#endif
|
|
if (!fs)
|
|
SYSERROR("Cannot open font %s", this.name[type]);
|
|
|
|
font_insert(size, type, fs);
|
|
fontset = &fonttbl[fontcnt - 1];
|
|
} else {
|
|
fontset = tbl;
|
|
}
|
|
TTF_SetFontStyle(fontset->id, weight > 5 ? TTF_STYLE_BOLD : TTF_STYLE_NORMAL);
|
|
}
|
|
|
|
SDL_Surface *font_get_glyph(const char *str_utf8) {
|
|
if (!fontset)
|
|
return NULL;
|
|
|
|
SDL_Surface *fs;
|
|
SDL_Color color = {255, 255, 255, 0};
|
|
if (this.antialiase_on) {
|
|
fs = TTF_RenderUTF8_Shaded(fontset->id, str_utf8, color, color);
|
|
} else {
|
|
fs = TTF_RenderUTF8_Solid(fontset->id, str_utf8, color);
|
|
}
|
|
if (!fs)
|
|
WARNING("Text rendering failed: %s", TTF_GetError());
|
|
|
|
return fs;
|
|
}
|
|
|
|
// SDL can't blit ARGB to an indexed bitmap properly, so we do it ourselves.
|
|
static void sdl_drawAntiAlias_8bpp(int dstx, int dsty, SDL_Surface *src, uint8_t col)
|
|
{
|
|
Uint8 cache[256*7];
|
|
memset(cache, 0, 256);
|
|
|
|
for (int y = 0; y < src->h && dsty + y < sdl_dib->h; y++) {
|
|
if (dsty + y < 0)
|
|
continue;
|
|
uint8_t *sp = (uint8_t*)src->pixels + y * src->pitch;
|
|
uint8_t *dp = (uint8_t*)sdl_dib->pixels + (dsty + y) * sdl_dib->pitch + dstx;
|
|
for (int x = 0; x < src->w && dstx + x < sdl_dib->w; x++) {
|
|
Uint8 r, g, b, alpha;
|
|
SDL_GetRGBA(*((Uint32*)sp), src->format, &r, &g, &b, &alpha);
|
|
alpha = alpha >> 5; // reduce bit depth
|
|
if (!alpha) {
|
|
// Transparent, do nothing
|
|
} else if (alpha == 7) {
|
|
*dp = col; // Fully opaque
|
|
} else if (cache[*dp] & 1 << alpha) {
|
|
*dp = cache[alpha << 8 | *dp]; // use cached value
|
|
} else {
|
|
// find nearest color in palette
|
|
cache[*dp] |= 1 << alpha;
|
|
int c = sdl_nearest_color(
|
|
(sdl_col[col].r * alpha + sdl_col[*dp].r * (7 - alpha)) / 7,
|
|
(sdl_col[col].g * alpha + sdl_col[*dp].g * (7 - alpha)) / 7,
|
|
(sdl_col[col].b * alpha + sdl_col[*dp].b * (7 - alpha)) / 7);
|
|
cache[alpha << 8 | *dp] = c;
|
|
*dp = c;
|
|
}
|
|
sp += src->format->BytesPerPixel;
|
|
dp++;
|
|
}
|
|
}
|
|
}
|
|
|
|
SDL_Rect font_draw_glyph(int x, int y, const char *str_utf8, uint8_t cl) {
|
|
SDL_Surface *fs;
|
|
SDL_Rect r_src, r_dst = {};
|
|
int w, h;
|
|
|
|
if (!*str_utf8)
|
|
return r_dst;
|
|
if (!fontset)
|
|
return r_dst;
|
|
|
|
bool antialias = this.antialiase_on;
|
|
// The post-effect in Rance 3 opening does not work properly if colors other
|
|
// than the specified text color (32) are used.
|
|
// https://github.com/kichikuou/xsystem35-sdl2/issues/54
|
|
// In Rance 3, text color 32 is only used in the opening.
|
|
if ((game_id == GAME_RANCE3 || game_id == GAME_RANCE3_ENG) && cl == 32)
|
|
antialias = false;
|
|
|
|
if (antialias) {
|
|
fs = TTF_RenderUTF8_Blended(fontset->id, str_utf8, sdl_col[cl]);
|
|
} else {
|
|
fs = TTF_RenderUTF8_Solid(fontset->id, str_utf8, sdl_col[cl]);
|
|
}
|
|
if (!fs) {
|
|
WARNING("Text rendering failed: %s", TTF_GetError());
|
|
return r_dst;
|
|
}
|
|
|
|
TTF_SizeUTF8(fontset->id, str_utf8, &w, &h);
|
|
// Center vertically to the box.
|
|
y -= (TTF_FontHeight(fontset->id) - fontset->size) / 2;
|
|
r_dst = (SDL_Rect){x, y, w, h};
|
|
|
|
if (sdl_dib->format->BitsPerPixel == 8 && antialias) {
|
|
sdl_drawAntiAlias_8bpp(x, y, fs, cl);
|
|
} else {
|
|
r_src = (SDL_Rect){0, 0, w, h};
|
|
SDL_BlitSurface(fs, &r_src, sdl_dib, &r_dst);
|
|
}
|
|
|
|
SDL_FreeSurface(fs);
|
|
if (r_dst.y < 0) {
|
|
r_dst.h += r_dst.y;
|
|
r_dst.y = 0;
|
|
}
|
|
return r_dst;
|
|
}
|
|
|
|
void font_init(void) {
|
|
this.antialiase_on = FALSE;
|
|
|
|
if (TTF_Init() == -1)
|
|
SYSERROR("Failed to intialize SDL_ttf: %s", TTF_GetError());
|
|
}
|
|
|
|
void font_set_name_and_index(int type, const char *name, int index) {
|
|
this.name[type] = name;
|
|
this.face[type] = index;
|
|
}
|
|
|
|
void font_set_antialias(boolean enable) {
|
|
this.antialiase_on = enable;
|
|
}
|
|
|
|
boolean font_get_antialias(void) {
|
|
return this.antialiase_on;
|
|
}
|
|
|
|
#ifdef __EMSCRIPTEN__
|
|
EM_ASYNC_JS(int, load_mincho_font, (void), {
|
|
await xsystem35.load_mincho_font();
|
|
});
|
|
#endif
|