/* ALICE SOFT SYSTEM 3 for Win32 [ AGS - text ] */ #include #include #include #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; } }