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