1 /******************************************************************************/
2 /* Mednafen - Multi-system Emulator                                           */
3 /******************************************************************************/
4 /* text.cpp:
5 **  Copyright (C) 2005-2017 Mednafen Team
6 **
7 ** This program is free software; you can redistribute it and/or
8 ** modify it under the terms of the GNU General Public License
9 ** as published by the Free Software Foundation; either version 2
10 ** of the License, or (at your option) any later version.
11 **
12 ** This program is distributed in the hope that it will be useful,
13 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 ** GNU General Public License for more details.
16 **
17 ** You should have received a copy of the GNU General Public License
18 ** along with this program; if not, write to the Free Software Foundation, Inc.,
19 ** 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 */
21 
22 /*
23 ** European-centric fixed-width bitmap font text rendering, with some CJK support.
24 */
25 
26 #include "video-common.h"
27 #include <mednafen/string/string.h>
28 #include "font-data.h"
29 
30 namespace Mednafen
31 {
32 
33 static const struct
34 {
35         uint8 glyph_width;
36         uint8 glyph_height;
37 	int8 extension;
38         uint8 entry_bsize;
39 	const uint8* base_ptr;
40 } FontDescriptors[_MDFN_FONT_COUNT] =
41 {
42  { 5, 7, 	-1,			sizeof(FontData5x7[0]),		&FontData5x7[0].data[0] },
43  { 6, 9,	-1,			sizeof(FontData6x9[0]),		&FontData6x9[0].data[0] },
44 /*
45  { 6, 10,	-1,			sizeof(FontData6x10[0]),	&FontData6x10[0].data[0] },
46 */
47  { 6, 12,	-1,			sizeof(FontData6x12[0]),	&FontData6x12[0].data[0] },
48  #ifdef WANT_INTERNAL_CJK
49  { 6, 13,	MDFN_FONT_12x13,	sizeof(FontData6x13[0]),	&FontData6x13[0].data[0] },
50  { 9, 18,	MDFN_FONT_18x18,	sizeof(FontData9x18[0]),	&FontData9x18[0].data[0] },
51  { 12, 13, 	-1,			sizeof(FontData12x13[0]),	&FontData12x13[0].data[0] },
52  { 18, 18, 	-1,			sizeof(FontData18x18[0]),	&FontData18x18[0].data[0] },
53  #else
54  { 6, 13,	-1,			sizeof(FontData6x13[0]),	&FontData6x13[0].data[0] },
55  { 9, 18,	-1,			sizeof(FontData9x18[0]),	&FontData9x18[0].data[0] },
56  #endif
57 };
58 
59 static uint16 FontDataIndexCache[_MDFN_FONT_COUNT][65536];
60 
GetFontHeight(const unsigned fontid)61 uint32 GetFontHeight(const unsigned fontid)
62 {
63  return FontDescriptors[fontid].glyph_height;
64 }
65 
66 template<typename T>
BuildIndexCache(const unsigned wf,const T * const fsd,const size_t fsd_count)67 static void BuildIndexCache(const unsigned wf, const T* const fsd, const size_t fsd_count)
68 {
69  for(size_t i = 0; i < fsd_count; i++)
70   FontDataIndexCache[wf][fsd[i].glyph_num] = i;
71 }
72 
MDFN_InitFontData(void)73 void MDFN_InitFontData(void)
74 {
75  memset(FontDataIndexCache, 0xFF, sizeof(FontDataIndexCache));
76 
77  BuildIndexCache(MDFN_FONT_5x7,        FontData5x7,  FontData5x7_Size / sizeof(font5x7));
78  BuildIndexCache(MDFN_FONT_6x9,        FontData6x9,  FontData6x9_Size / sizeof(font6x9));
79 // BuildIndexCache(MDFN_FONT_6x10,       FontData6x10, FontData6x10_Size / sizeof(font6x10));
80  BuildIndexCache(MDFN_FONT_6x12,       FontData6x12, FontData6x12_Size / sizeof(font6x12));
81  BuildIndexCache(MDFN_FONT_6x13_12x13, FontData6x13, FontData6x13_Size / sizeof(font6x13));
82  BuildIndexCache(MDFN_FONT_9x18_18x18, FontData9x18, FontData9x18_Size / sizeof(font9x18));
83 
84  #ifdef WANT_INTERNAL_CJK
85  BuildIndexCache(MDFN_FONT_12x13,      FontData12x13, FontData12x13_Size / sizeof(font12x13));
86  BuildIndexCache(MDFN_FONT_18x18,      FontData18x18, FontData18x18_Size / sizeof(font18x18));
87  #endif
88 }
89 
utf32_strlen(const char32_t * s)90 static size_t utf32_strlen(const char32_t *s)
91 {
92  size_t ret = 0;
93 
94  while(*s++) ret++;
95 
96  return(ret);
97 }
98 
DecodeGlyph(char32_t thisglyph,const uint8 ** glyph_ptr,uint8 * glyph_width,uint8 * glyph_ov_width,uint32 fontid)99 static void DecodeGlyph(char32_t thisglyph, const uint8 **glyph_ptr, uint8 *glyph_width, uint8 *glyph_ov_width, uint32 fontid)
100 {
101  bool GlyphFound = false;
102  uint32 recurse_fontid = fontid;
103 
104  //if(thisglyph < 0x20)
105  // thisglyph = 0x2400 + thisglyph;
106 
107  while(!GlyphFound)
108  {
109   if(thisglyph < 0x10000 && FontDataIndexCache[recurse_fontid][thisglyph] != 0xFFFF)
110   {
111    *glyph_ptr = FontDescriptors[recurse_fontid].base_ptr + (FontDescriptors[recurse_fontid].entry_bsize * FontDataIndexCache[recurse_fontid][thisglyph]);
112    *glyph_width = FontDescriptors[recurse_fontid].glyph_width;
113    GlyphFound = true;
114   }
115   else if(FontDescriptors[recurse_fontid].extension != -1)
116    recurse_fontid = FontDescriptors[recurse_fontid].extension;
117   else
118    break;
119  }
120 
121  if(!GlyphFound)
122  {
123   *glyph_ptr = FontDescriptors[fontid].base_ptr + (FontDescriptors[fontid].entry_bsize * FontDataIndexCache[fontid][0xFFFD]);
124   *glyph_width = FontDescriptors[fontid].glyph_width;
125  }
126 
127  if((thisglyph >= 0x0300 && thisglyph <= 0x036F) || (thisglyph >= 0xFE20 && thisglyph <= 0xFE2F))
128   *glyph_ov_width = 0;
129  //else if(MDFN_UNLIKELY(thisglyph < 0x20))
130  //{
131  // if(thisglyph == '\b')	(If enabling this, need to change all glyph_ov_width types to int8)
132  // {
133  //  glyph_width[x] = 0;
134  //  glyph_ov_width[x] = std::max<int64>(-(int64)ret, -FontDescriptors[fontid].glyph_width);
135  //}
136  //}
137  else
138   *glyph_ov_width = *glyph_width;
139 }
140 
GetTextPixLength(const char32_t * text,const size_t text_len,const uint32 fontid)141 static uint32 GetTextPixLength(const char32_t* text, const size_t text_len, const uint32 fontid)
142 {
143  uint32 ret = 0;
144 
145  for(size_t i = 0; i < text_len; i++)
146  {
147   const uint8 *glyph_ptr;
148   uint8 glyph_width;
149   uint8 glyph_ov_width;
150 
151   DecodeGlyph(text[i], &glyph_ptr, &glyph_width, &glyph_ov_width, fontid);
152   ret += (i == (text_len - 1)) ? glyph_width : glyph_ov_width;
153  }
154  return ret;
155 }
156 
GetTextPixLength(const char * text,uint32 fontid)157 uint32 GetTextPixLength(const char* text, uint32 fontid)
158 {
159  uint32 max_glyph_len = strlen((char *)text);
160 
161  if(MDFN_LIKELY(max_glyph_len > 0))
162  {
163   size_t dlen = max_glyph_len;
164   std::unique_ptr<char32_t[]> utf32_text_d;
165   char32_t utf32_text_l[256];
166   char32_t* utf32_text = (256 < dlen) ? (utf32_text_d.reset(new char32_t[dlen]), utf32_text_d.get()) : utf32_text_l;
167 
168   UTF8_to_UTF32(text, max_glyph_len, utf32_text, &dlen);
169 
170   return GetTextPixLength(utf32_text, std::min<size_t>(max_glyph_len, dlen), fontid);
171  }
172 
173  return 0;
174 }
175 
GetTextPixLength(const char32_t * text,uint32 fontid)176 uint32 GetTextPixLength(const char32_t* text, uint32 fontid)
177 {
178  const uint32 text_len = utf32_strlen(text);
179 
180  if(MDFN_LIKELY(text_len > 0))
181   return GetTextPixLength(text, text_len, fontid);
182 
183  return 0;
184 }
185 
186 template<typename T>
DoRealDraw(T * const surfp,uint32 pitch,const int32 x,const int32 y,const int32 bx0,const int32 bx1,const int32 by0,const int32 by1,uint32 fgcolor,const char32_t * const text,const size_t text_len,const uint32 fontid)187 static uint32 DoRealDraw(T* const surfp, uint32 pitch, const int32 x, const int32 y, const int32 bx0, const int32 bx1, const int32 by0, const int32 by1, uint32 fgcolor, const char32_t* const text, const size_t text_len, const uint32 fontid)
188 {
189  const uint32 glyph_height = FontDescriptors[fontid].glyph_height;
190  uint32 gy_start = std::min<int64>(glyph_height, std::max<int64>(0, (int64)by0 - y));
191  uint32 gy_bound = std::min<int64>(glyph_height, std::max<int64>(0, (int64)by1 - y));
192  T* dest = surfp + y * pitch + x;
193  uint32 ret = 0;
194 
195  for(size_t i = 0; i < text_len; i++)
196  {
197   const uint8* glyph_ptr;
198   uint8 glyph_width;
199   uint8 glyph_ov_width;
200 
201   DecodeGlyph(text[i], &glyph_ptr, &glyph_width, &glyph_ov_width, fontid);
202   //
203   //
204   //
205   uint32 gx_start = std::min<int64>(glyph_width, std::max<int64>(0, (int64)bx0 - x - ret));
206   uint32 gx_bound = std::min<int64>(glyph_width, std::max<int64>(0, (int64)bx1 - x - ret));
207   size_t sd_inc = (glyph_width >> 3) + 1;
208   const uint8* sd = glyph_ptr + (sd_inc * gy_start);
209   T* dd = dest + (gy_start * pitch);
210 
211   //printf("x=%d, y=%d --- %d %d\n", x, y, gx_start, gx_bound);
212 
213   for(uint32 gy = gy_start; MDFN_LIKELY(gy < gy_bound); gy++)
214   {
215    for(uint32 gx = gx_start; MDFN_LIKELY(gx < gx_bound); gx++)
216    {
217     if((sd[gx >> 3] << (gx & 0x7)) & 0x80)
218      dd[gx] = fgcolor;
219    }
220    dd += pitch;
221    sd += sd_inc;
222   }
223 
224   dest += glyph_ov_width;
225   ret += (i == (text_len - 1)) ? glyph_width : glyph_ov_width;
226  }
227 
228  return ret;
229 }
230 
DrawTextUTF32(MDFN_Surface * surf,const MDFN_Rect * cr,int32 x,int32 y,const char32_t * text,const size_t text_len,uint32 color,uint32 shadcolor,uint32 fontid,uint32 hcenterw,const bool shadow)231 static uint32 DrawTextUTF32(MDFN_Surface* surf, const MDFN_Rect* cr, int32 x, int32 y, const char32_t* text, const size_t text_len, uint32 color, uint32 shadcolor, uint32 fontid, uint32 hcenterw, const bool shadow)
232 {
233  int32 bx0, bx1;
234  int32 by0, by1;
235 
236  if(cr)
237  {
238   bx0 = std::max<int32>(0, cr->x);
239   bx1 = std::min<int64>(surf->w, std::max<int64>(0, (int64)cr->x + cr->w));
240 
241   by0 = std::max<int32>(0, cr->y);
242   by1 = std::min<int64>(surf->h, std::max<int64>(0, (int64)cr->y + cr->h));
243  }
244  else
245  {
246   bx0 = 0;
247   bx1 = surf->w;
248 
249   by0 = 0;
250   by1 = surf->h;
251  }
252 
253  //
254  //
255  //
256  if(!text_len)
257   return 0;
258 
259  if(hcenterw)
260  {
261   uint32 pixwidth = GetTextPixLength(text, text_len, fontid);
262 
263   if(hcenterw > pixwidth)
264    x += (int32)(hcenterw - pixwidth) / 2;
265  }
266 
267  switch(surf->format.bpp)
268  {
269   default:
270 	return 0;
271 
272   case 8:
273 	if(shadow)
274 	 DoRealDraw(surf->pix<uint8>(), surf->pitchinpix, x + 1, y + 1, bx0, bx1, by0, by1, shadcolor, text, text_len, fontid);
275 
276 	return DoRealDraw(surf->pix<uint8>(), surf->pitchinpix, x, y, bx0, bx1, by0, by1, color, text, text_len, fontid);
277 
278   case 16:
279 	if(shadow)
280 	 DoRealDraw(surf->pix<uint16>(), surf->pitchinpix, x + 1, y + 1, bx0, bx1, by0, by1, shadcolor, text, text_len, fontid);
281 
282 	return DoRealDraw(surf->pix<uint16>(), surf->pitchinpix, x, y, bx0, bx1, by0, by1, color, text, text_len, fontid);
283 
284   case 32:
285 	if(shadow)
286 	 DoRealDraw(surf->pix<uint32>(), surf->pitchinpix, x + 1, y + 1, bx0, bx1, by0, by1, shadcolor, text, text_len, fontid);
287 
288 	return DoRealDraw(surf->pix<uint32>(), surf->pitchinpix, x, y, bx0, bx1, by0, by1, color, text, text_len, fontid);
289  }
290 }
291 
DrawTextUTF8(MDFN_Surface * surf,const MDFN_Rect * cr,int32 x,int32 y,const char * text,const size_t text_len,uint32 color,uint32 shadcolor,uint32 fontid,uint32 hcenterw,const bool shadow)292 static uint32 DrawTextUTF8(MDFN_Surface* surf, const MDFN_Rect* cr, int32 x, int32 y, const char* text, const size_t text_len, uint32 color, uint32 shadcolor, uint32 fontid, uint32 hcenterw, const bool shadow)
293 {
294  size_t dlen = text_len;
295  std::unique_ptr<char32_t[]> utf32_text_d;
296  char32_t utf32_text_l[256];
297  char32_t* utf32_text = (256 < dlen) ? (utf32_text_d.reset(new char32_t[dlen]), utf32_text_d.get()) : utf32_text_l;
298 
299  UTF8_to_UTF32(text, text_len, utf32_text, &dlen);
300 
301  return DrawTextUTF32(surf, cr, x, y, utf32_text, std::min<size_t>(text_len, dlen), color, shadcolor, fontid, hcenterw, shadow);
302 }
303 
DrawText(MDFN_Surface * surf,int32 x,int32 y,const char * text,uint32 color,uint32 fontid,uint32 hcenterw)304 uint32 DrawText(MDFN_Surface* surf, int32 x, int32 y, const char* text, uint32 color, uint32 fontid, uint32 hcenterw)
305 {
306  return DrawTextUTF8(surf, nullptr, x, y, text, strlen(text), color, 0, fontid, hcenterw, false);
307 }
308 
DrawText(MDFN_Surface * surf,const MDFN_Rect & cr,int32 x,int32 y,const char * text,uint32 color,uint32 fontid,uint32 hcenterw)309 uint32 DrawText(MDFN_Surface* surf, const MDFN_Rect& cr, int32 x, int32 y, const char* text, uint32 color, uint32 fontid, uint32 hcenterw)
310 {
311  return DrawTextUTF8(surf, &cr, x, y, text, strlen(text), color, 0, fontid, hcenterw, false);
312 }
313 
DrawTextShadow(MDFN_Surface * surf,int32 x,int32 y,const char * text,uint32 color,uint32 shadcolor,uint32 fontid,uint32 hcenterw)314 uint32 DrawTextShadow(MDFN_Surface* surf, int32 x, int32 y, const char* text, uint32 color, uint32 shadcolor, uint32 fontid, uint32 hcenterw)
315 {
316  return DrawTextUTF8(surf, nullptr, x, y, text, strlen(text), color, shadcolor, fontid, hcenterw, true);
317 }
318 
DrawTextShadow(MDFN_Surface * surf,const MDFN_Rect & cr,int32 x,int32 y,const char * text,uint32 color,uint32 shadcolor,uint32 fontid,uint32 hcenterw)319 uint32 DrawTextShadow(MDFN_Surface* surf, const MDFN_Rect& cr, int32 x, int32 y, const char* text, uint32 color, uint32 shadcolor, uint32 fontid, uint32 hcenterw)
320 {
321  return DrawTextUTF8(surf, &cr, x, y, text, strlen(text), color, shadcolor, fontid, hcenterw, true);
322 }
323 
324 //
325 //
326 //
DrawText(MDFN_Surface * surf,int32 x,int32 y,const char32_t * text,uint32 color,uint32 fontid,uint32 hcenterw)327 uint32 DrawText(MDFN_Surface* surf, int32 x, int32 y, const char32_t* text, uint32 color, uint32 fontid, uint32 hcenterw)
328 {
329  return DrawTextUTF32(surf, nullptr, x, y, text, utf32_strlen(text), color, 0, fontid, hcenterw, false);
330 }
331 
DrawText(MDFN_Surface * surf,const MDFN_Rect & cr,int32 x,int32 y,const char32_t * text,uint32 color,uint32 fontid,uint32 hcenterw)332 uint32 DrawText(MDFN_Surface* surf, const MDFN_Rect& cr, int32 x, int32 y, const char32_t* text, uint32 color, uint32 fontid, uint32 hcenterw)
333 {
334  return DrawTextUTF32(surf, &cr, x, y, text, utf32_strlen(text), color, 0, fontid, hcenterw, false);
335 }
336 
DrawTextShadow(MDFN_Surface * surf,int32 x,int32 y,const char32_t * text,uint32 color,uint32 shadcolor,uint32 fontid,uint32 hcenterw)337 uint32 DrawTextShadow(MDFN_Surface* surf, int32 x, int32 y, const char32_t* text, uint32 color, uint32 shadcolor, uint32 fontid, uint32 hcenterw)
338 {
339  return DrawTextUTF32(surf, nullptr, x, y, text, utf32_strlen(text), color, shadcolor, fontid, hcenterw, true);
340 }
341 
DrawTextShadow(MDFN_Surface * surf,const MDFN_Rect & cr,int32 x,int32 y,const char32_t * text,uint32 color,uint32 shadcolor,uint32 fontid,uint32 hcenterw)342 uint32 DrawTextShadow(MDFN_Surface* surf, const MDFN_Rect& cr, int32 x, int32 y, const char32_t* text, uint32 color, uint32 shadcolor, uint32 fontid, uint32 hcenterw)
343 {
344  return DrawTextUTF32(surf, &cr, x, y, text, utf32_strlen(text), color, shadcolor, fontid, hcenterw, true);
345 }
346 
347 }
348