1 /* -*- C++ -*-
2  *
3  *  FontInfo.cpp - Font information storage class of ONScripter
4  *
5  *  Copyright (c) 2001-2020 Ogapee. All rights reserved.
6  *
7  *  ogapee@aqua.dti2.ne.jp
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2 of the License, or
12  *  (at your option) any later version.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the Free Software
21  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23 
24 #include "FontInfo.h"
25 #include <stdio.h>
26 #include <SDL_ttf.h>
27 
28 #if defined(PSP)
29 #include <string.h>
30 #include <stdlib.h>
31 extern int psp_power_resume_number;
32 #endif
33 
34 static struct FontContainer{
35     FontContainer *next;
36     int size;
37     TTF_Font *font[2];
38 #if defined(PSP)
39     SDL_RWops *rw_ops;
40     int power_resume_number;
41     char name[256];
42 #endif
43 
FontContainerFontContainer44     FontContainer(){
45         size = 0;
46         next = NULL;
47         font[0] = font[1] = NULL;
48 #if defined(PSP)
49         rw_ops = NULL;
50         power_resume_number = 0;
51 #endif
52     };
53 } root_font_container;
54 
FontInfo()55 FontInfo::FontInfo()
56 {
57     ttf_font[0] = ttf_font[1] = NULL;
58 
59     color[0]        = color[1]        = color[2]        = 0xff;
60     on_color[0]     = on_color[1]     = on_color[2]     = 0xff;
61     off_color[0]    = off_color[1]    = off_color[2]    = 0xaa;
62     nofile_color[0] = 0x55;
63     nofile_color[1] = 0x55;
64     nofile_color[2] = 0x99;
65     rubyon_flag = false;
66 
67     reset(NULL);
68 }
69 
reset(Encoding * enc)70 void FontInfo::reset(Encoding *enc)
71 {
72     this->enc = enc;
73     tateyoko_mode = YOKO_MODE;
74     clear();
75 
76     is_bold = true;
77     is_shadow = true;
78     is_transparent = true;
79     is_newline_accepted = false;
80 
81     is_line_space_fixed = false;
82 }
83 
openFont(char * font_file,int ratio1,int ratio2)84 void *FontInfo::openFont( char *font_file, int ratio1, int ratio2 )
85 {
86     int font_size = font_size_xy[1];
87     if (enc->getEncoding() != Encoding::CODE_UTF8 &&
88         font_size_xy[0] < font_size_xy[1])
89         font_size = font_size_xy[0];
90 
91     FontContainer *fc = &root_font_container;
92     while( fc->next ){
93         if ( fc->next->size == font_size ) break;
94         fc = fc->next;
95     }
96     if ( !fc->next ){
97         fc->next = new FontContainer();
98         fc->next->size = font_size;
99         FILE *fp = fopen( font_file, "r" );
100         if ( fp == NULL ) return NULL;
101         fclose( fp );
102 #if defined(PSP)
103         fc->next->rw_ops = SDL_RWFromFile(font_file, "r");
104         fc->next->font[0] = TTF_OpenFontRW( fc->next->rw_ops, SDL_TRUE, font_size * ratio1 / ratio2 );
105 #if (SDL_TTF_MAJOR_VERSION>=2) && (SDL_TTF_MINOR_VERSION>=0) && (SDL_TTF_PATCHLEVEL>=10)
106         fc->next->font[1] = TTF_OpenFontRW( fc->next->rw_ops, SDL_TRUE, font_size * ratio1 / ratio2 );
107         TTF_SetFontOutline(fc->next->font[1], 1);
108 #endif
109         fc->next->power_resume_number = psp_power_resume_number;
110         strcpy(fc->next->name, font_file);
111 #else
112         fc->next->font[0] = TTF_OpenFont( font_file, font_size * ratio1 / ratio2 );
113 #if (SDL_TTF_MAJOR_VERSION>=2) && (SDL_TTF_MINOR_VERSION>=0) && (SDL_TTF_PATCHLEVEL>=10)
114         fc->next->font[1] = TTF_OpenFont( font_file, font_size * ratio1 / ratio2 );
115         TTF_SetFontOutline(fc->next->font[1], 1);
116 #endif
117 #endif
118     }
119 #if defined(PSP)
120     else if (fc->next->power_resume_number != psp_power_resume_number){
121         FILE *fp = fopen(fc->next->name, "r");
122         fc->next->rw_ops->hidden.stdio.fp = fp;
123         fc->next->power_resume_number = psp_power_resume_number;
124     }
125 #endif
126 
127     ttf_font[0] = (void*)fc->next->font[0];
128     ttf_font[1] = (void*)fc->next->font[1];
129 
130     return fc->next->font;
131 }
132 
setTateyokoMode(int tateyoko_mode)133 void FontInfo::setTateyokoMode( int tateyoko_mode )
134 {
135     this->tateyoko_mode = tateyoko_mode;
136     clear();
137 }
138 
getTateyokoMode()139 int FontInfo::getTateyokoMode()
140 {
141     return tateyoko_mode;
142 }
143 
getRemainingLine()144 int FontInfo::getRemainingLine()
145 {
146     if (tateyoko_mode == YOKO_MODE)
147         return num_xy[1] - xy[1]/2;
148     else
149         return num_xy[1] - num_xy[0] + xy[0]/2 + 1;
150 }
151 
toggleStyle(int style)152 void FontInfo::toggleStyle(int style)
153 {
154     for (int i=0; i<2; i++){
155         if (ttf_font[i] == NULL) continue;
156         int old_style = TTF_GetFontStyle((TTF_Font*)ttf_font[i]);
157         int new_style = old_style ^ style;
158         TTF_SetFontStyle((TTF_Font*)ttf_font[i], new_style);
159     }
160 }
161 
x(bool use_ruby_offset)162 int FontInfo::x(bool use_ruby_offset)
163 {
164     int x = xy[0]*pitch_xy[0]/2 + top_xy[0] + line_offset_xy[0];
165     if (use_ruby_offset && rubyon_flag && tateyoko_mode == TATE_MODE)
166         x += font_size_xy[0] - pitch_xy[0];
167     return x;
168 }
169 
y(bool use_ruby_offset)170 int FontInfo::y(bool use_ruby_offset)
171 {
172     int pitch_y = pitch_xy[1];
173     if (!is_line_space_fixed &&
174         enc->getEncoding() == Encoding::CODE_UTF8 && ttf_font[0])
175         pitch_y += TTF_FontLineSkip((const TTF_Font*)ttf_font[0]) - font_size_xy[1];
176     int y = xy[1]*pitch_y/2 + top_xy[1] + line_offset_xy[1];
177     if (use_ruby_offset && rubyon_flag && tateyoko_mode == YOKO_MODE &&
178         enc->getEncoding() == Encoding::CODE_CP932)
179             y += pitch_xy[1] - font_size_xy[1];
180     return y;
181 }
182 
setXY(int x,int y)183 void FontInfo::setXY( int x, int y )
184 {
185     if ( x != -1 ) xy[0] = x*2;
186     if ( y != -1 ) xy[1] = y*2;
187 }
188 
clear()189 void FontInfo::clear()
190 {
191     if (tateyoko_mode == YOKO_MODE)
192         setXY(0, 0);
193     else
194         setXY(num_xy[0]-1, 0);
195     line_offset_xy[0] = line_offset_xy[1] = 0;
196 }
197 
newLine()198 void FontInfo::newLine()
199 {
200     if (tateyoko_mode == YOKO_MODE){
201         xy[0] = 0;
202         xy[1] += 2;
203     }
204     else{
205         xy[0] -= 2;
206         xy[1] = 0;
207     }
208     line_offset_xy[0] = line_offset_xy[1] = 0;
209 }
210 
setLineArea(const char * buf)211 void FontInfo::setLineArea(const char *buf)
212 {
213     if (enc->getEncoding() == Encoding::CODE_UTF8){
214         int w = 0;
215         while(buf[0]){
216             int n = enc->getBytes(buf[0]);
217             unsigned short unicode = enc->getUTF16(buf);
218 
219             int minx, maxx, miny, maxy, advanced;
220             TTF_GlyphMetrics((TTF_Font*)ttf_font[0], unicode,
221                              &minx, &maxx, &miny, &maxy, &advanced);
222 
223             w += advanced + pitch_xy[tateyoko_mode] - font_size_xy[tateyoko_mode];
224             buf += n;
225         }
226         num_xy[tateyoko_mode] = w * 2 / pitch_xy[tateyoko_mode] + 1;
227     }
228     else{
229         num_xy[tateyoko_mode] = strlen(buf)/2 + 1;
230     }
231     num_xy[1-tateyoko_mode] = 1;
232 }
233 
isEndOfLine(float margin)234 bool FontInfo::isEndOfLine(float margin)
235 {
236     if (xy[tateyoko_mode] + margin >= num_xy[tateyoko_mode]*2) return true;
237 
238     return false;
239 }
240 
isLineEmpty()241 bool FontInfo::isLineEmpty()
242 {
243     if (xy[tateyoko_mode] == 0) return true;
244 
245     return false;
246 }
247 
advanceCharInHankaku(float offset)248 void FontInfo::advanceCharInHankaku(float offset)
249 {
250     xy[tateyoko_mode] += offset;
251 }
252 
addLineOffset(int offset)253 void FontInfo::addLineOffset(int offset)
254 {
255     line_offset_xy[tateyoko_mode] += offset;
256 }
257 
calcUpdatedArea(int start_xy[2],int ratio1,int ratio2)258 SDL_Rect FontInfo::calcUpdatedArea(int start_xy[2], int ratio1, int ratio2)
259 {
260     SDL_Rect rect;
261 
262     if (tateyoko_mode == YOKO_MODE){
263         int pitch_y = pitch_xy[1];
264         if (enc->getEncoding() == Encoding::CODE_UTF8 && ttf_font[0])
265             pitch_y += TTF_FontLineSkip((const TTF_Font*)ttf_font[0]) - font_size_xy[1];
266         if (start_xy[1] == xy[1]){
267             rect.x = top_xy[0] + pitch_xy[0]*start_xy[0]/2;
268             rect.w = pitch_xy[0]*(xy[0]-start_xy[0])/2+1;
269         }
270         else{
271             rect.x = top_xy[0];
272             rect.w = pitch_xy[0]*num_xy[0];
273         }
274         rect.y = top_xy[1] + start_xy[1]*pitch_y/2;
275         rect.h = pitch_y + pitch_y*(xy[1]-start_xy[1])/2;
276         if (rubyon_flag && enc->getEncoding() == Encoding::CODE_CP932)
277             rect.h += pitch_xy[1] - font_size_xy[1];
278     }
279     else{
280         rect.x = top_xy[0] + pitch_xy[0]*xy[0]/2;
281         rect.w = font_size_xy[0] + pitch_xy[0]*(start_xy[0]-xy[0])/2;
282         if (rubyon_flag) rect.w += font_size_xy[0]-pitch_xy[0];
283         if (start_xy[0] == xy[0]){
284             rect.y = top_xy[1] + pitch_xy[1]*start_xy[1]/2;
285             rect.h = pitch_xy[1]*(xy[1]-start_xy[1])/2+1;
286         }
287         else{
288             rect.y = top_xy[1];
289             rect.h = pitch_xy[1]*num_xy[1];
290         }
291         num_xy[0] = (xy[0]-start_xy[0])/2+1;
292     }
293 
294     return rect;
295 }
296 
addShadeArea(SDL_Rect & rect,int dx,int dy,int dw,int dh)297 void FontInfo::addShadeArea(SDL_Rect &rect, int dx, int dy, int dw, int dh)
298 {
299     rect.x += dx;
300     rect.y += dy;
301     rect.w += dw;
302     rect.h += dh;
303 }
304 
initRuby(FontInfo & body_info,int body_count,int ruby_count)305 int FontInfo::initRuby(FontInfo &body_info, int body_count, int ruby_count)
306 {
307     if ((tateyoko_mode == YOKO_MODE &&
308          body_count + body_info.xy[0]/2 >= body_info.num_xy[0]-1) ||
309         (tateyoko_mode == TATE_MODE &&
310          body_count + body_info.xy[1]/2 > body_info.num_xy[1]))
311         body_info.newLine();
312 
313     top_xy[0] = body_info.x();
314     top_xy[1] = body_info.y();
315     pitch_xy[0] = font_size_xy[0];
316     pitch_xy[1] = font_size_xy[1];
317 
318     int margin=0;
319 
320     if (tateyoko_mode == YOKO_MODE){
321         top_xy[1] -= font_size_xy[1];
322         num_xy[0] = ruby_count;
323         num_xy[1] = 1;
324     }
325     else{
326         top_xy[0] += body_info.font_size_xy[0];
327         num_xy[0] = 1;
328         num_xy[1] = ruby_count;
329     }
330 
331     if (ruby_count*font_size_xy[tateyoko_mode] >= body_count*body_info.pitch_xy[tateyoko_mode]){
332         margin = (ruby_count*font_size_xy[tateyoko_mode] - body_count*body_info.pitch_xy[tateyoko_mode] + 1)/2;
333     }
334     else{
335         int offset = 0;
336         if (ruby_count > 0)
337             offset = (body_count*body_info.pitch_xy[tateyoko_mode] - ruby_count*font_size_xy[tateyoko_mode] + ruby_count) / (ruby_count*2);
338         top_xy[tateyoko_mode] += offset;
339         pitch_xy[tateyoko_mode] += offset*2;
340     }
341     body_info.line_offset_xy[tateyoko_mode] += margin;
342 
343     clear();
344 
345     return margin;
346 }
347