Files
kichikuou_system3-sdl2/src/sys/ags_text.cpp
T

538 lines
14 KiB
C++
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
ALICE SOFT SYSTEM 3 for Win32
[ AGS - text ]
*/
#include <algorithm>
#include <limits.h>
#include <string.h>
#include "ags.h"
#include "nact.h"
#include "encoding.h"
#include "texthook.h"
namespace {
bool antialias = true;
int convert_to_zenkaku(int code)
{
switch(code) {
case u' ': return u' ';
case u'!': return u'!';
case u'"': return u'”';
case u'#': return u'#';
case u'$': return u'$';
case u'%': return u'%';
case u'&': return u'&';
case u'\'': return u'’';
case u'(': return u'(';
case u')': return u')';
case u'*': return u'*';
case u'+': return u'+';
case u',': return u',';
case u'-': return u'-';
case u'.': return u'.';
case u'/': return u'/';
case u'0': return u'0';
case u'1': return u'1';
case u'2': return u'2';
case u'3': return u'3';
case u'4': return u'4';
case u'5': return u'5';
case u'6': return u'6';
case u'7': return u'7';
case u'8': return u'8';
case u'9': return u'9';
case u':': return u':';
case u';': return u';';
case u'<': return u'<';
case u'=': return u'=';
case u'>': return u'>';
case u'?': return u'?';
case u'@': return u'@';
case u'A': return u'A';
case u'B': return u'B';
case u'C': return u'C';
case u'D': return u'D';
case u'E': return u'E';
case u'F': return u'F';
case u'G': return u'G';
case u'H': return u'H';
case u'I': return u'I';
case u'J': return u'J';
case u'K': return u'K';
case u'L': return u'L';
case u'M': return u'M';
case u'N': return u'N';
case u'O': return u'O';
case u'P': return u'P';
case u'Q': return u'Q';
case u'R': return u'R';
case u'S': return u'S';
case u'T': return u'T';
case u'U': return u'U';
case u'V': return u'V';
case u'W': return u'W';
case u'X': return u'X';
case u'Y': return u'Y';
case u'Z': return u'Z';
case u'[': return u'[';
case u'\\': return u'¥';
case u']': return u']';
case u'^': return u'^';
case u'_': return u'_';
case u'`': return u'‘';
case u'a': return u'a';
case u'b': return u'b';
case u'c': return u'c';
case u'd': return u'd';
case u'e': return u'e';
case u'f': return u'f';
case u'g': return u'g';
case u'h': return u'h';
case u'i': return u'i';
case u'j': return u'j';
case u'k': return u'k';
case u'l': return u'l';
case u'm': return u'm';
case u'n': return u'n';
case u'o': return u'o';
case u'p': return u'p';
case u'q': return u'q';
case u'r': return u'r';
case u's': return u's';
case u't': return u't';
case u'u': return u'u';
case u'v': return u'v';
case u'w': return u'w';
case u'x': return u'x';
case u'y': return u'y';
case u'z': return u'z';
case u'{': return u'{';
case u'|': return u'|';
case u'}': return u'}';
case u'~': return u'~';
case u'。': return u'。';
case u'「': return u'「';
case u'」': return u'」';
case u'、': return u'、';
case u'・': return u'・';
case u'ヲ': return u'を';
case u'ァ': return u'ぁ';
case u'ィ': return u'ぃ';
case u'ゥ': return u'ぅ';
case u'ェ': return u'ぇ';
case u'ォ': return u'ぉ';
case u'ャ': return u'ゃ';
case u'ュ': return u'ゅ';
case u'ョ': return u'ょ';
case u'ッ': return u'っ';
case u'ー': return u'ー';
case u'ア': return u'あ';
case u'イ': return u'い';
case u'ウ': return u'う';
case u'エ': return u'え';
case u'オ': return u'お';
case u'カ': return u'か';
case u'キ': return u'き';
case u'ク': return u'く';
case u'ケ': return u'け';
case u'コ': return u'こ';
case u'サ': return u'さ';
case u'シ': return u'し';
case u'ス': return u'す';
case u'セ': return u'せ';
case u'ソ': return u'そ';
case u'タ': return u'た';
case u'チ': return u'ち';
case u'ツ': return u'つ';
case u'テ': return u'て';
case u'ト': return u'と';
case u'ナ': return u'な';
case u'ニ': return u'に';
case u'ヌ': return u'ぬ';
case u'ネ': return u'ね';
case u'ノ': return u'の';
case u'ハ': return u'は';
case u'ヒ': return u'ひ';
case u'フ': return u'ふ';
case u'ヘ': return u'へ';
case u'ホ': return u'ほ';
case u'マ': return u'ま';
case u'ミ': return u'み';
case u'ム': return u'む';
case u'メ': return u'め';
case u'モ': return u'も';
case u'ヤ': return u'や';
case u'ユ': return u'ゆ';
case u'ヨ': return u'よ';
case u'ラ': return u'ら';
case u'リ': return u'り';
case u'ル': return u'る';
case u'レ': return u'れ';
case u'ロ': return u'ろ';
case u'ワ': return u'わ';
case u'ン': return u'ん';
case u'゙': return u'゛';
case u'゚': return u'゜';
}
return code;
}
int convert_to_hankaku(int code)
{
switch(code) {
case u' ': return u' ';
case u'!': return u'!';
case u'”': return u'"';
case u'#': return u'#';
case u'$': return u'$';
case u'%': return u'%';
case u'&': return u'&';
case u'’': return u'\'';
case u'(': return u'(';
case u')': return u')';
case u'*': return u'*';
case u'+': return u'+';
case u',': return u',';
case u'-': return u'-';
case u'.': return u'.';
case u'/': return u'/';
case u'0': return u'0';
case u'1': return u'1';
case u'2': return u'2';
case u'3': return u'3';
case u'4': return u'4';
case u'5': return u'5';
case u'6': return u'6';
case u'7': return u'7';
case u'8': return u'8';
case u'9': return u'9';
case u':': return u':';
case u';': return u';';
case u'<': return u'<';
case u'=': return u'=';
case u'>': return u'>';
case u'?': return u'?';
case u'@': return u'@';
case u'A': return u'A';
case u'B': return u'B';
case u'C': return u'C';
case u'D': return u'D';
case u'E': return u'E';
case u'F': return u'F';
case u'G': return u'G';
case u'H': return u'H';
case u'I': return u'I';
case u'J': return u'J';
case u'K': return u'K';
case u'L': return u'L';
case u'M': return u'M';
case u'N': return u'N';
case u'O': return u'O';
case u'P': return u'P';
case u'Q': return u'Q';
case u'R': return u'R';
case u'S': return u'S';
case u'T': return u'T';
case u'U': return u'U';
case u'V': return u'V';
case u'W': return u'W';
case u'X': return u'X';
case u'Y': return u'Y';
case u'Z': return u'Z';
case u'[': return u'[';
case u'¥': return u'\\';
case u']': return u']';
case u'^': return u'^';
case u'_': return u'_';
case u'‘': return u'`';
case u'a': return u'a';
case u'b': return u'b';
case u'c': return u'c';
case u'd': return u'd';
case u'e': return u'e';
case u'f': return u'f';
case u'g': return u'g';
case u'h': return u'h';
case u'i': return u'i';
case u'j': return u'j';
case u'k': return u'k';
case u'l': return u'l';
case u'm': return u'm';
case u'n': return u'n';
case u'o': return u'o';
case u'p': return u'p';
case u'q': return u'q';
case u'r': return u'r';
case u's': return u's';
case u't': return u't';
case u'u': return u'u';
case u'v': return u'v';
case u'w': return u'w';
case u'x': return u'x';
case u'y': return u'y';
case u'z': return u'z';
case u'{': return u'{';
case u'|': return u'|';
case u'}': return u'}';
case u'~': return u'~';
case u'。': return u'。';
case u'「': return u'「';
case u'」': return u'」';
case u'、': return u'、';
case u'・': return u'・';
case u'を': return u'ヲ';
case u'ぁ': return u'ァ';
case u'ぃ': return u'ィ';
case u'ぅ': return u'ゥ';
case u'ぇ': return u'ェ';
case u'ぉ': return u'ォ';
case u'ゃ': return u'ャ';
case u'ゅ': return u'ュ';
case u'ょ': return u'ョ';
case u'っ': return u'ッ';
case u'ー': return u'ー';
case u'あ': return u'ア';
case u'い': return u'イ';
case u'う': return u'ウ';
case u'え': return u'エ';
case u'お': return u'オ';
case u'か': return u'カ';
case u'き': return u'キ';
case u'く': return u'ク';
case u'け': return u'ケ';
case u'こ': return u'コ';
case u'さ': return u'サ';
case u'し': return u'シ';
case u'す': return u'ス';
case u'せ': return u'セ';
case u'そ': return u'ソ';
case u'た': return u'タ';
case u'ち': return u'チ';
case u'つ': return u'ツ';
case u'て': return u'テ';
case u'と': return u'ト';
case u'な': return u'ナ';
case u'に': return u'ニ';
case u'ぬ': return u'ヌ';
case u'ね': return u'ネ';
case u'の': return u'ノ';
case u'は': return u'ハ';
case u'ひ': return u'ヒ';
case u'ふ': return u'フ';
case u'へ': return u'ヘ';
case u'ほ': return u'ホ';
case u'ま': return u'マ';
case u'み': return u'ミ';
case u'む': return u'ム';
case u'め': return u'メ';
case u'も': return u'モ';
case u'や': return u'ヤ';
case u'ゆ': return u'ユ';
case u'よ': return u'ヨ';
case u'ら': return u'ラ';
case u'り': return u'リ';
case u'る': return u'ル';
case u'れ': return u'レ';
case u'ろ': return u'ロ';
case u'わ': return u'ワ';
case u'ん': return u'ン';
case u'゛': return u'゙';
case u'゜': return u'゚';
}
return code;
}
} // namespace
void AGS::draw_text(const char* string, bool text_wait)
{
if (!*string)
return;
uint8 antialias_cache[256*7];
if (antialias)
memset(antialias_cache, 0, 256);
TextContext& ctx = draw_menu ? menu : text;
int screen = draw_menu ? 2 : dest_screen;
int dest_x = ctx.pos.x;
int dest_y = ctx.pos.y;
if (ctx.current_line_height < ctx.font_size)
ctx.current_line_height = ctx.font_size;
TTF_Font* font = NULL;
switch (ctx.font_size) {
case 16: font = hFont16; break;
case 24: font = hFont24; break;
case 32: font = hFont32; break;
case 48: font = hFont48; break;
case 64: font = hFont64; break;
}
int ascent = TTF_FontAscent(font);
int descent = TTF_FontDescent(font);
// Adjust dest_y if the font height is larger than the specified size.
dest_y -= (ascent - descent - ctx.font_size) / 2;
while (*string) {
int code = g_nact->encoding->next_codepoint(&string);
if(draw_hankaku) {
code = convert_to_hankaku(code);
} else {
code = convert_to_zenkaku(code);
}
// 文字出力
if (GAIJI_FIRST <= code && code <= GAIJI_LAST) {
// Use unadjusted dest_y here.
draw_gaiji(screen, dest_x, ctx.pos.y, gaiji[code - GAIJI_FIRST], ctx.font_size, ctx.font_color);
dest_x += ctx.font_size;
} else {
if (!draw_menu)
texthook_character(g_nact->get_scenario_page(), code);
if (antialias)
draw_char_antialias(screen, dest_x, dest_y, code, font, ctx.font_color, antialias_cache);
else
draw_char(screen, dest_x, dest_y, code, font, ctx.font_color);
int miny, maxy, advance;
TTF_GlyphMetrics(font, code, NULL, NULL, &miny, &maxy, &advance);
// Some fonts report incorrect Ascent/Descent value so we need to fix them.
if (miny < descent) descent = miny;
if (maxy > ascent) ascent = maxy;
dest_x += advance;
}
if (!draw_menu && text_wait && code != ' ') {
// 画面更新
if(screen == 0)
draw_screen(ctx.pos.x, dest_y, dest_x - ctx.pos.x, ascent - descent);
ctx.pos.x = dest_x;
// Wait
g_nact->text_wait();
}
}
if (!draw_menu && !text_wait) {
// 画面更新
if(screen == 0)
draw_screen(ctx.pos.x, dest_y, dest_x - ctx.pos.x, ascent - descent);
}
ctx.pos.x = dest_x;
}
void AGS::draw_char(int dest, int dest_x, int dest_y, uint16 code, TTF_Font* font, uint8 color)
{
// パターン取得
SDL_Color white = {0xff, 0xff, 0xff};
SDL_Surface* fs = TTF_RenderGlyph_Solid(font, code, white);
// パターン出力
for (int y = std::max(0, -dest_y); y < fs->h && dest_y + y < 480; y++) {
uint8_t* pattern = (uint8_t*)fs->pixels + fs->pitch * y;
for(int x = 0; x < fs->w && dest_x + x < 640; x++) {
if(pattern[x] != 0) {
vram[dest][dest_y + y][dest_x + x] = color;
}
}
}
SDL_FreeSurface(fs);
}
int AGS::nearest_color(int r, int g, int b) {
int i, col, mind = INT_MAX;
for (i = 0; i < 256; i++) {
int dr = r - palR(i);
int dg = g - palG(i);
int db = b - palB(i);
int d = dr*dr*30 + dg*dg*59 + db*db*11;
if (d < mind) {
mind = d;
col = i;
}
}
return col;
}
void AGS::draw_char_antialias(int dest, int dest_x, int dest_y, uint16 code, TTF_Font* font, uint8 color, uint8 cache[])
{
// パターン取得
SDL_Color black = {0, 0, 0};
SDL_Color white = {0xff, 0xff, 0xff};
SDL_Surface* fs = TTF_RenderGlyph_Shaded(font, code, white, black);
// パターン出力
for (int y = std::max(0, -dest_y); y < fs->h && dest_y + y < 480; y++) {
uint8_t* pattern = (uint8_t*)fs->pixels + fs->pitch * y;
uint8_t* dp = &vram[dest][dest_y + y][dest_x];
for(int x = 0; x < fs->w && dest_x + x < 640; x++, dp++) {
uint8 bg = *dp;
int alpha = pattern[x] >> 5;
if (alpha == 0) {
// Transparent, do nothing
} else if (alpha == 7) {
*dp = color;
} else if (cache[bg] & 1 << alpha) {
*dp = cache[alpha << 8 | bg];
} else {
cache[bg] |= 1 << alpha;
int c = nearest_color((palR(color) * alpha + palR(bg) * (7 - alpha)) / 7,
(palG(color) * alpha + palG(bg) * (7 - alpha)) / 7,
(palB(color) * alpha + palB(bg) * (7 - alpha)) / 7);
cache[alpha << 8 | bg] = c;
*dp = c;
}
}
}
SDL_FreeSurface(fs);
}
void AGS::draw_gaiji(int dest, int dest_x, int dest_y, const uint8_t bitmap[32], int size, uint8 color)
{
bool pattern[16][16];
// パターン取得
for(int y = 0; y < 16; y++) {
uint8 l = bitmap[y * 2 + 0];
uint8 r = bitmap[y * 2 + 1];
pattern[y][ 0] = ((l & 0x80) != 0);
pattern[y][ 1] = ((l & 0x40) != 0);
pattern[y][ 2] = ((l & 0x20) != 0);
pattern[y][ 3] = ((l & 0x10) != 0);
pattern[y][ 4] = ((l & 0x08) != 0);
pattern[y][ 5] = ((l & 0x04) != 0);
pattern[y][ 6] = ((l & 0x02) != 0);
pattern[y][ 7] = ((l & 0x01) != 0);
pattern[y][ 8] = ((r & 0x80) != 0);
pattern[y][ 9] = ((r & 0x40) != 0);
pattern[y][10] = ((r & 0x20) != 0);
pattern[y][11] = ((r & 0x10) != 0);
pattern[y][12] = ((r & 0x08) != 0);
pattern[y][13] = ((r & 0x04) != 0);
pattern[y][14] = ((r & 0x02) != 0);
pattern[y][15] = ((r & 0x01) != 0);
}
// パターン出力
for(int y = 0; y < size && dest_y + y < 480; y++) {
for(int x = 0; x < size && dest_x + x < 640; x++) {
if(pattern[(y * 16) / size][(x * 16) / size]) {
vram[dest][dest_y + y][dest_x + x] = color;
}
}
}
}
extern "C" {
void EMSCRIPTEN_KEEPALIVE ags_setAntialiasedStringMode(int on) {
antialias = on != 0;
}
}