diff options
author | Jacek Antonelli | 2008-08-15 23:44:46 -0500 |
---|---|---|
committer | Jacek Antonelli | 2008-08-15 23:44:46 -0500 |
commit | 38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch) | |
tree | adca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/llrender/llfontgl.cpp | |
parent | README.txt (diff) | |
download | meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.zip meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.gz meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.bz2 meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.xz |
Second Life viewer sources 1.13.2.12
Diffstat (limited to 'linden/indra/llrender/llfontgl.cpp')
-rw-r--r-- | linden/indra/llrender/llfontgl.cpp | 1453 |
1 files changed, 1453 insertions, 0 deletions
diff --git a/linden/indra/llrender/llfontgl.cpp b/linden/indra/llrender/llfontgl.cpp new file mode 100644 index 0000000..2774e8d --- /dev/null +++ b/linden/indra/llrender/llfontgl.cpp | |||
@@ -0,0 +1,1453 @@ | |||
1 | /** | ||
2 | * @file llfontgl.cpp | ||
3 | * @brief Wrapper around FreeType | ||
4 | * | ||
5 | * Copyright (c) 2001-2007, Linden Research, Inc. | ||
6 | * | ||
7 | * The source code in this file ("Source Code") is provided by Linden Lab | ||
8 | * to you under the terms of the GNU General Public License, version 2.0 | ||
9 | * ("GPL"), unless you have obtained a separate licensing agreement | ||
10 | * ("Other License"), formally executed by you and Linden Lab. Terms of | ||
11 | * the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
12 | * online at http://secondlife.com/developers/opensource/gplv2 | ||
13 | * | ||
14 | * There are special exceptions to the terms and conditions of the GPL as | ||
15 | * it is applied to this Source Code. View the full text of the exception | ||
16 | * in the file doc/FLOSS-exception.txt in this software distribution, or | ||
17 | * online at http://secondlife.com/developers/opensource/flossexception | ||
18 | * | ||
19 | * By copying, modifying or distributing this software, you acknowledge | ||
20 | * that you have read and understood your obligations described above, | ||
21 | * and agree to abide by those obligations. | ||
22 | * | ||
23 | * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
24 | * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
25 | * COMPLETENESS OR PERFORMANCE. | ||
26 | */ | ||
27 | |||
28 | #include "linden_common.h" | ||
29 | |||
30 | #include <boost/tokenizer.hpp> | ||
31 | |||
32 | #include "llfont.h" | ||
33 | #include "llfontgl.h" | ||
34 | #include "llgl.h" | ||
35 | #include "v4color.h" | ||
36 | |||
37 | const S32 BOLD_OFFSET = 1; | ||
38 | |||
39 | // static class members | ||
40 | F32 LLFontGL::sVertDPI = 96.f; | ||
41 | F32 LLFontGL::sHorizDPI = 96.f; | ||
42 | F32 LLFontGL::sScaleX = 1.f; | ||
43 | F32 LLFontGL::sScaleY = 1.f; | ||
44 | LLString LLFontGL::sAppDir; | ||
45 | |||
46 | LLFontGL* LLFontGL::sMonospace = NULL; | ||
47 | LLFontGL* LLFontGL::sSansSerifSmall = NULL; | ||
48 | LLFontGL* LLFontGL::sSansSerif = NULL; | ||
49 | LLFontGL* LLFontGL::sSansSerifBig = NULL; | ||
50 | LLFontGL* LLFontGL::sSansSerifHuge = NULL; | ||
51 | LLFontGL* LLFontGL::sSansSerifBold = NULL; | ||
52 | LLFontList* LLFontGL::sSSFallback = NULL; | ||
53 | LLFontList* LLFontGL::sSSSmallFallback = NULL; | ||
54 | LLFontList* LLFontGL::sSSBigFallback = NULL; | ||
55 | LLFontList* LLFontGL::sSSHugeFallback = NULL; | ||
56 | LLFontList* LLFontGL::sSSBoldFallback = NULL; | ||
57 | LLColor4 LLFontGL::sShadowColor(0.f, 0.f, 0.f, 1.f); | ||
58 | |||
59 | LLCoordFont LLFontGL::sCurOrigin; | ||
60 | std::vector<LLCoordFont> LLFontGL::sOriginStack; | ||
61 | |||
62 | LLFontGL*& gExtCharFont = LLFontGL::sSansSerif; | ||
63 | |||
64 | const F32 EXT_X_BEARING = 1.f; | ||
65 | const F32 EXT_Y_BEARING = 0.f; | ||
66 | const F32 EXT_KERNING = 1.f; | ||
67 | const F32 PIXEL_BORDER_THRESHOLD = 0.0001f; | ||
68 | const F32 PIXEL_CORRECTION_DISTANCE = 0.01f; | ||
69 | |||
70 | const F32 PAD_AMT = 0.5f; | ||
71 | |||
72 | F32 llfont_round_x(F32 x) | ||
73 | { | ||
74 | //return llfloor((x-LLFontGL::sCurOrigin.mX)/LLFontGL::sScaleX+0.5f)*LLFontGL::sScaleX+LLFontGL::sCurOrigin.mX; | ||
75 | //return llfloor(x/LLFontGL::sScaleX+0.5f)*LLFontGL::sScaleY; | ||
76 | return x; | ||
77 | } | ||
78 | |||
79 | F32 llfont_round_y(F32 y) | ||
80 | { | ||
81 | //return llfloor((y-LLFontGL::sCurOrigin.mY)/LLFontGL::sScaleY+0.5f)*LLFontGL::sScaleY+LLFontGL::sCurOrigin.mY; | ||
82 | //return llfloor(y+0.5f); | ||
83 | return y; | ||
84 | } | ||
85 | |||
86 | // static | ||
87 | U8 LLFontGL::getStyleFromString(const LLString &style) | ||
88 | { | ||
89 | S32 ret = 0; | ||
90 | if (style.find("NORMAL") != style.npos) | ||
91 | { | ||
92 | ret |= NORMAL; | ||
93 | } | ||
94 | if (style.find("BOLD") != style.npos) | ||
95 | { | ||
96 | ret |= BOLD; | ||
97 | } | ||
98 | if (style.find("ITALIC") != style.npos) | ||
99 | { | ||
100 | ret |= ITALIC; | ||
101 | } | ||
102 | if (style.find("UNDERLINE") != style.npos) | ||
103 | { | ||
104 | ret |= UNDERLINE; | ||
105 | } | ||
106 | return ret; | ||
107 | } | ||
108 | |||
109 | LLFontGL::LLFontGL() | ||
110 | : LLFont() | ||
111 | { | ||
112 | init(); | ||
113 | clearEmbeddedChars(); | ||
114 | } | ||
115 | |||
116 | LLFontGL::LLFontGL(const LLFontGL &source) | ||
117 | { | ||
118 | llerrs << "Not implemented!" << llendl; | ||
119 | } | ||
120 | |||
121 | LLFontGL::~LLFontGL() | ||
122 | { | ||
123 | mImageGLp = NULL; | ||
124 | mRawImageGLp = NULL; | ||
125 | clearEmbeddedChars(); | ||
126 | } | ||
127 | |||
128 | void LLFontGL::init() | ||
129 | { | ||
130 | if (mImageGLp.isNull()) | ||
131 | { | ||
132 | mImageGLp = new LLImageGL(FALSE); | ||
133 | //RN: use nearest mipmap filtering to obviate the need to do pixel-accurate positioning | ||
134 | mImageGLp->bind(); | ||
135 | mImageGLp->setMipFilterNearest(TRUE, TRUE); | ||
136 | } | ||
137 | if (mRawImageGLp.isNull()) | ||
138 | { | ||
139 | mRawImageGLp = new LLImageRaw; // Note LLFontGL owns the image, not LLFont. | ||
140 | } | ||
141 | setRawImage( mRawImageGLp ); | ||
142 | } | ||
143 | |||
144 | void LLFontGL::reset() | ||
145 | { | ||
146 | init(); | ||
147 | resetBitmap(); | ||
148 | } | ||
149 | |||
150 | // static | ||
151 | LLString LLFontGL::getFontPathSystem() | ||
152 | { | ||
153 | LLString system_path; | ||
154 | |||
155 | // Try to figure out where the system's font files are stored. | ||
156 | char *system_root = NULL; | ||
157 | #if LL_WINDOWS | ||
158 | system_root = getenv("SystemRoot"); | ||
159 | if (!system_root) | ||
160 | { | ||
161 | llwarns << "SystemRoot not found, attempting to load fonts from default path." << llendl; | ||
162 | } | ||
163 | #endif | ||
164 | |||
165 | if (system_root) | ||
166 | { | ||
167 | system_path = llformat("%s/fonts/", system_root); | ||
168 | } | ||
169 | else | ||
170 | { | ||
171 | #if LL_WINDOWS | ||
172 | // HACK for windows 98/Me | ||
173 | system_path = "/WINDOWS/FONTS/"; | ||
174 | #elif LL_DARWIN | ||
175 | // HACK for Mac OS X | ||
176 | system_path = "/System/Library/Fonts/"; | ||
177 | #endif | ||
178 | } | ||
179 | return system_path; | ||
180 | } | ||
181 | |||
182 | |||
183 | // static | ||
184 | LLString LLFontGL::getFontPathLocal() | ||
185 | { | ||
186 | LLString local_path; | ||
187 | |||
188 | // Backup files if we can't load from system fonts directory. | ||
189 | // We could store this in an end-user writable directory to allow | ||
190 | // end users to switch fonts. | ||
191 | if (LLFontGL::sAppDir.length()) | ||
192 | { | ||
193 | // use specified application dir to look for fonts | ||
194 | local_path = LLFontGL::sAppDir + "/fonts/"; | ||
195 | } | ||
196 | else | ||
197 | { | ||
198 | // assume working directory is executable directory | ||
199 | local_path = "./fonts/"; | ||
200 | } | ||
201 | return local_path; | ||
202 | } | ||
203 | |||
204 | //static | ||
205 | bool LLFontGL::loadFaceFallback(LLFontList *fontlistp, const LLString& fontname, const F32 point_size) | ||
206 | { | ||
207 | LLString local_path = getFontPathLocal(); | ||
208 | LLString sys_path = getFontPathSystem(); | ||
209 | |||
210 | // The fontname string may contain multiple font file names separated by semicolons. | ||
211 | // Break it apart and try loading each one, in order. | ||
212 | typedef boost::tokenizer<boost::char_separator<char> > tokenizer; | ||
213 | boost::char_separator<char> sep(";"); | ||
214 | tokenizer tokens(fontname, sep); | ||
215 | tokenizer::iterator token_iter; | ||
216 | |||
217 | for(token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter) | ||
218 | { | ||
219 | LLFont *fontp = new LLFont(); | ||
220 | LLString font_path = local_path + *token_iter; | ||
221 | if (!fontp->loadFace(font_path.c_str(), point_size, sVertDPI, sHorizDPI, 2, TRUE)) | ||
222 | { | ||
223 | font_path = sys_path + *token_iter; | ||
224 | if (!fontp->loadFace(font_path.c_str(), point_size, sVertDPI, sHorizDPI, 2, TRUE)) | ||
225 | { | ||
226 | llwarns << "Couldn't load font " << *token_iter << llendl; | ||
227 | delete fontp; | ||
228 | fontp = NULL; | ||
229 | } | ||
230 | } | ||
231 | |||
232 | if(fontp) | ||
233 | { | ||
234 | fontlistp->addAtEnd(fontp); | ||
235 | } | ||
236 | } | ||
237 | |||
238 | // We want to return true if at least one fallback font loaded correctly. | ||
239 | return (fontlistp->size() > 0); | ||
240 | } | ||
241 | |||
242 | //static | ||
243 | bool LLFontGL::loadFace(LLFontGL *fontp, const LLString& fontname, const F32 point_size, LLFontList *fallback_fontp) | ||
244 | { | ||
245 | LLString local_path = getFontPathLocal(); | ||
246 | LLString font_path = local_path + fontname; | ||
247 | if (!fontp->loadFace(font_path.c_str(), point_size, sVertDPI, sHorizDPI)) | ||
248 | { | ||
249 | LLString sys_path = getFontPathSystem(); | ||
250 | font_path = sys_path + fontname; | ||
251 | if (!fontp->loadFace(font_path.c_str(), point_size, sVertDPI, sHorizDPI)) | ||
252 | { | ||
253 | llwarns << "Couldn't load font " << fontname << llendl; | ||
254 | return false; | ||
255 | } | ||
256 | } | ||
257 | |||
258 | fontp->setFallbackFont(fallback_fontp); | ||
259 | return true; | ||
260 | } | ||
261 | |||
262 | |||
263 | // static | ||
264 | BOOL LLFontGL::initDefaultFonts(F32 screen_dpi, F32 x_scale, F32 y_scale, | ||
265 | const LLString& monospace_file, F32 monospace_size, | ||
266 | const LLString& sansserif_file, | ||
267 | const LLString& sanserif_fallback_file, F32 ss_fallback_scale, | ||
268 | F32 small_size, F32 medium_size, F32 big_size, F32 huge_size, | ||
269 | const LLString& sansserif_bold_file, F32 bold_size, | ||
270 | const LLString& app_dir) | ||
271 | { | ||
272 | BOOL failed = FALSE; | ||
273 | sVertDPI = (F32)llfloor(screen_dpi * y_scale); | ||
274 | sHorizDPI = (F32)llfloor(screen_dpi * x_scale); | ||
275 | sScaleX = x_scale; | ||
276 | sScaleY = y_scale; | ||
277 | sAppDir = app_dir; | ||
278 | |||
279 | // | ||
280 | // Monospace font | ||
281 | // | ||
282 | |||
283 | if (!sMonospace) | ||
284 | { | ||
285 | sMonospace = new LLFontGL(); | ||
286 | } | ||
287 | else | ||
288 | { | ||
289 | sMonospace->reset(); | ||
290 | } | ||
291 | |||
292 | failed |= !loadFace(sMonospace, monospace_file, monospace_size, NULL); | ||
293 | |||
294 | // | ||
295 | // Sans-serif fonts | ||
296 | // | ||
297 | if(!sSansSerifHuge) | ||
298 | { | ||
299 | sSansSerifHuge = new LLFontGL(); | ||
300 | } | ||
301 | else | ||
302 | { | ||
303 | sSansSerifHuge->reset(); | ||
304 | } | ||
305 | |||
306 | if (!sSSHugeFallback) | ||
307 | { | ||
308 | sSSHugeFallback = new LLFontList(); | ||
309 | if (!loadFaceFallback(sSSHugeFallback, sanserif_fallback_file, huge_size*ss_fallback_scale)) | ||
310 | { | ||
311 | delete sSSHugeFallback; | ||
312 | sSSHugeFallback = NULL; | ||
313 | } | ||
314 | } | ||
315 | |||
316 | failed |= !loadFace(sSansSerifHuge, sansserif_file, huge_size, sSSHugeFallback); | ||
317 | |||
318 | |||
319 | if(!sSansSerifBig) | ||
320 | { | ||
321 | sSansSerifBig = new LLFontGL(); | ||
322 | } | ||
323 | else | ||
324 | { | ||
325 | sSansSerifBig->reset(); | ||
326 | } | ||
327 | |||
328 | if (!sSSBigFallback) | ||
329 | { | ||
330 | sSSBigFallback = new LLFontList(); | ||
331 | if (!loadFaceFallback(sSSBigFallback, sanserif_fallback_file, big_size*ss_fallback_scale)) | ||
332 | { | ||
333 | delete sSSBigFallback; | ||
334 | sSSBigFallback = NULL; | ||
335 | } | ||
336 | } | ||
337 | |||
338 | failed |= !loadFace(sSansSerifBig, sansserif_file, big_size, sSSBigFallback); | ||
339 | |||
340 | |||
341 | if(!sSansSerif) | ||
342 | { | ||
343 | sSansSerif = new LLFontGL(); | ||
344 | } | ||
345 | else | ||
346 | { | ||
347 | sSansSerif->reset(); | ||
348 | } | ||
349 | |||
350 | if (!sSSFallback) | ||
351 | { | ||
352 | sSSFallback = new LLFontList(); | ||
353 | if (!loadFaceFallback(sSSFallback, sanserif_fallback_file, medium_size*ss_fallback_scale)) | ||
354 | { | ||
355 | delete sSSFallback; | ||
356 | sSSFallback = NULL; | ||
357 | } | ||
358 | } | ||
359 | failed |= !loadFace(sSansSerif, sansserif_file, medium_size, sSSFallback); | ||
360 | |||
361 | |||
362 | if(!sSansSerifSmall) | ||
363 | { | ||
364 | sSansSerifSmall = new LLFontGL(); | ||
365 | } | ||
366 | else | ||
367 | { | ||
368 | sSansSerifSmall->reset(); | ||
369 | } | ||
370 | |||
371 | if (!sSSSmallFallback) | ||
372 | { | ||
373 | sSSSmallFallback = new LLFontList(); | ||
374 | if (!loadFaceFallback(sSSSmallFallback, sanserif_fallback_file, small_size*ss_fallback_scale)) | ||
375 | { | ||
376 | delete sSSSmallFallback; | ||
377 | sSSSmallFallback = NULL; | ||
378 | } | ||
379 | } | ||
380 | failed |= !loadFace(sSansSerifSmall, sansserif_file, small_size, sSSSmallFallback); | ||
381 | |||
382 | |||
383 | // | ||
384 | // Sans-serif bold | ||
385 | // | ||
386 | if(!sSansSerifBold) | ||
387 | { | ||
388 | sSansSerifBold = new LLFontGL(); | ||
389 | } | ||
390 | else | ||
391 | { | ||
392 | sSansSerifBold->reset(); | ||
393 | } | ||
394 | |||
395 | if (!sSSBoldFallback) | ||
396 | { | ||
397 | sSSBoldFallback = new LLFontList(); | ||
398 | if (!loadFaceFallback(sSSBoldFallback, sanserif_fallback_file, medium_size*ss_fallback_scale)) | ||
399 | { | ||
400 | delete sSSBoldFallback; | ||
401 | sSSBoldFallback = NULL; | ||
402 | } | ||
403 | } | ||
404 | failed |= !loadFace(sSansSerifBold, sansserif_bold_file, medium_size, sSSBoldFallback); | ||
405 | |||
406 | return !failed; | ||
407 | } | ||
408 | |||
409 | |||
410 | |||
411 | // static | ||
412 | void LLFontGL::destroyDefaultFonts() | ||
413 | { | ||
414 | delete sMonospace; | ||
415 | sMonospace = NULL; | ||
416 | |||
417 | delete sSansSerifHuge; | ||
418 | sSansSerifHuge = NULL; | ||
419 | |||
420 | delete sSansSerifBig; | ||
421 | sSansSerifBig = NULL; | ||
422 | |||
423 | delete sSansSerif; | ||
424 | sSansSerif = NULL; | ||
425 | |||
426 | delete sSansSerifSmall; | ||
427 | sSansSerifSmall = NULL; | ||
428 | |||
429 | delete sSansSerifBold; | ||
430 | sSansSerifBold = NULL; | ||
431 | |||
432 | delete sSSHugeFallback; | ||
433 | sSSHugeFallback = NULL; | ||
434 | |||
435 | delete sSSBigFallback; | ||
436 | sSSBigFallback = NULL; | ||
437 | |||
438 | delete sSSFallback; | ||
439 | sSSFallback = NULL; | ||
440 | |||
441 | delete sSSSmallFallback; | ||
442 | sSSSmallFallback = NULL; | ||
443 | |||
444 | delete sSSBoldFallback; | ||
445 | sSSBoldFallback = NULL; | ||
446 | } | ||
447 | |||
448 | //static | ||
449 | void LLFontGL::destroyGL() | ||
450 | { | ||
451 | if (!sMonospace) | ||
452 | { | ||
453 | // Already all destroyed. | ||
454 | return; | ||
455 | } | ||
456 | sMonospace->mImageGLp->destroyGLTexture(); | ||
457 | sSansSerifHuge->mImageGLp->destroyGLTexture(); | ||
458 | sSansSerifSmall->mImageGLp->destroyGLTexture(); | ||
459 | sSansSerif->mImageGLp->destroyGLTexture(); | ||
460 | sSansSerifBig->mImageGLp->destroyGLTexture(); | ||
461 | sSansSerifBold->mImageGLp->destroyGLTexture(); | ||
462 | } | ||
463 | |||
464 | |||
465 | |||
466 | LLFontGL &LLFontGL::operator=(const LLFontGL &source) | ||
467 | { | ||
468 | llerrs << "Not implemented" << llendl; | ||
469 | return *this; | ||
470 | } | ||
471 | |||
472 | BOOL LLFontGL::loadFace(const LLString& filename, const F32 point_size, const F32 vert_dpi, const F32 horz_dpi) | ||
473 | { | ||
474 | if (!LLFont::loadFace(filename, point_size, vert_dpi, horz_dpi, 2, FALSE)) | ||
475 | { | ||
476 | return FALSE; | ||
477 | } | ||
478 | mImageGLp->createGLTexture(0, mRawImageGLp); | ||
479 | mImageGLp->bind(); | ||
480 | mImageGLp->setMipFilterNearest(TRUE, TRUE); | ||
481 | return TRUE; | ||
482 | } | ||
483 | |||
484 | BOOL LLFontGL::addChar(const llwchar wch) | ||
485 | { | ||
486 | if (!LLFont::addChar(wch)) | ||
487 | { | ||
488 | return FALSE; | ||
489 | } | ||
490 | |||
491 | stop_glerror(); | ||
492 | mImageGLp->setSubImage(mRawImageGLp, 0, 0, mImageGLp->getWidth(), mImageGLp->getHeight()); | ||
493 | mImageGLp->bind(); | ||
494 | mImageGLp->setMipFilterNearest(TRUE, TRUE); | ||
495 | stop_glerror(); | ||
496 | return TRUE; | ||
497 | } | ||
498 | |||
499 | |||
500 | S32 LLFontGL::renderUTF8(const LLString &text, const S32 offset, | ||
501 | const F32 x, const F32 y, | ||
502 | const LLColor4 &color, | ||
503 | const HAlign halign, const VAlign valign, | ||
504 | U8 style, | ||
505 | const S32 max_chars, const S32 max_pixels, | ||
506 | F32* right_x, | ||
507 | BOOL use_ellipses) const | ||
508 | { | ||
509 | LLWString wstr = utf8str_to_wstring(text); | ||
510 | return render(wstr, offset, x, y, color, halign, valign, style, max_chars, max_pixels, right_x, use_ellipses); | ||
511 | } | ||
512 | |||
513 | S32 LLFontGL::render(const LLWString &wstr, | ||
514 | const S32 begin_offset, | ||
515 | const F32 x, const F32 y, | ||
516 | const LLColor4 &color, | ||
517 | const HAlign halign, const VAlign valign, | ||
518 | U8 style, | ||
519 | const S32 max_chars, S32 max_pixels, | ||
520 | F32* right_x, | ||
521 | BOOL use_embedded, | ||
522 | BOOL use_ellipses) const | ||
523 | { | ||
524 | LLGLEnable texture_2d(GL_TEXTURE_2D); | ||
525 | |||
526 | if (wstr.empty()) | ||
527 | { | ||
528 | return 0; | ||
529 | } | ||
530 | |||
531 | if (style & DROP_SHADOW) | ||
532 | { | ||
533 | LLColor4 shadow_color = sShadowColor; | ||
534 | shadow_color[3] = color[3]; | ||
535 | render(wstr, begin_offset, | ||
536 | x + 1.f / sScaleX, | ||
537 | y - 1.f / sScaleY, | ||
538 | shadow_color, | ||
539 | halign, | ||
540 | valign, | ||
541 | style & (~DROP_SHADOW), | ||
542 | max_chars, | ||
543 | max_pixels, | ||
544 | right_x, | ||
545 | use_embedded, | ||
546 | use_ellipses); | ||
547 | } | ||
548 | |||
549 | S32 scaled_max_pixels = max_pixels == S32_MAX ? S32_MAX : llceil((F32)max_pixels * sScaleX); | ||
550 | |||
551 | BOOL render_bold = FALSE; | ||
552 | |||
553 | // HACK for better bolding | ||
554 | if (style & BOLD) | ||
555 | { | ||
556 | if (this == LLFontGL::sSansSerif) | ||
557 | { | ||
558 | return LLFontGL::sSansSerifBold->render( | ||
559 | wstr, begin_offset, | ||
560 | x, y, | ||
561 | color, | ||
562 | halign, valign, | ||
563 | (style & ~BOLD), | ||
564 | max_chars, max_pixels, | ||
565 | right_x, use_embedded); | ||
566 | } | ||
567 | else | ||
568 | { | ||
569 | render_bold = TRUE; | ||
570 | } | ||
571 | } | ||
572 | |||
573 | glPushMatrix(); | ||
574 | glLoadIdentity(); | ||
575 | glTranslatef(floorf(sCurOrigin.mX*sScaleX), floorf(sCurOrigin.mY*sScaleY), sCurOrigin.mZ); | ||
576 | //glScalef(sScaleX, sScaleY, 1.0f); | ||
577 | |||
578 | // avoid half pixels | ||
579 | // RN: if we're going to this trouble, might as well snap to nearest pixel all the time | ||
580 | // but the plan is to get rid of this so that fonts "just work" | ||
581 | //F32 half_pixel_distance = llabs(fmodf(sCurOrigin.mX * sScaleX, 1.f) - 0.5f); | ||
582 | //if (half_pixel_distance < PIXEL_BORDER_THRESHOLD) | ||
583 | //{ | ||
584 | glTranslatef(PIXEL_CORRECTION_DISTANCE*sScaleX, 0.f, 0.f); | ||
585 | //} | ||
586 | |||
587 | // this code would just snap to pixel grid, although it seems to introduce more jitter | ||
588 | //F32 pixel_offset_x = llround(sCurOrigin.mX * sScaleX) - (sCurOrigin.mX * sScaleX); | ||
589 | //F32 pixel_offset_y = llround(sCurOrigin.mY * sScaleY) - (sCurOrigin.mY * sScaleY); | ||
590 | //glTranslatef(-pixel_offset_x, -pixel_offset_y, 0.f); | ||
591 | |||
592 | // scale back to native pixel size | ||
593 | //glScalef(1.f / sScaleX, 1.f / sScaleY, 1.f); | ||
594 | //glScaled(1.0 / (F64) sScaleX, 1.0 / (F64) sScaleY, 1.0f); | ||
595 | LLFastTimer t(LLFastTimer::FTM_RENDER_FONTS); | ||
596 | |||
597 | glColor4fv( color.mV ); | ||
598 | |||
599 | S32 chars_drawn = 0; | ||
600 | S32 i; | ||
601 | S32 length; | ||
602 | |||
603 | if (-1 == max_chars) | ||
604 | { | ||
605 | length = (S32)wstr.length() - begin_offset; | ||
606 | } | ||
607 | else | ||
608 | { | ||
609 | length = llmin((S32)wstr.length() - begin_offset, max_chars ); | ||
610 | } | ||
611 | |||
612 | F32 cur_x, cur_y, cur_render_x, cur_render_y; | ||
613 | F32 slant_offset; | ||
614 | |||
615 | slant_offset = ((style & ITALIC) ? ( -mAscender * 0.25f) : 0.f); | ||
616 | |||
617 | // Bind the font texture | ||
618 | |||
619 | mImageGLp->bind(0); | ||
620 | |||
621 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Not guaranteed to be set correctly | ||
622 | |||
623 | cur_x = ((F32)x * sScaleX); | ||
624 | cur_y = ((F32)y * sScaleY); | ||
625 | |||
626 | // Offset y by vertical alignment. | ||
627 | switch (valign) | ||
628 | { | ||
629 | case TOP: | ||
630 | cur_y -= mAscender; | ||
631 | break; | ||
632 | case BOTTOM: | ||
633 | cur_y += mDescender; | ||
634 | break; | ||
635 | case VCENTER: | ||
636 | cur_y -= ((mAscender - mDescender)/2.f); | ||
637 | break; | ||
638 | case BASELINE: | ||
639 | // Baseline, do nothing. | ||
640 | break; | ||
641 | default: | ||
642 | break; | ||
643 | } | ||
644 | |||
645 | switch (halign) | ||
646 | { | ||
647 | case LEFT: | ||
648 | break; | ||
649 | case RIGHT: | ||
650 | cur_x -= (F32)getWidth(wstr.c_str(), 0, length) * sScaleX; | ||
651 | break; | ||
652 | case HCENTER: | ||
653 | cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), 0, length) * sScaleX)) / 2; | ||
654 | break; | ||
655 | default: | ||
656 | break; | ||
657 | } | ||
658 | |||
659 | // Round properly. | ||
660 | //cur_render_y = (F32)llfloor(cur_y/sScaleY + 0.5f)*sScaleY; | ||
661 | //cur_render_x = (F32)llfloor(cur_x/sScaleX + 0.5f)*sScaleX; | ||
662 | |||
663 | cur_render_y = cur_y; | ||
664 | cur_render_x = cur_x; | ||
665 | |||
666 | F32 start_x = cur_x; | ||
667 | |||
668 | F32 inv_width = 1.f / mImageGLp->getWidth(); | ||
669 | F32 inv_height = 1.f / mImageGLp->getHeight(); | ||
670 | |||
671 | const S32 LAST_CHARACTER = LLFont::LAST_CHAR_FULL; | ||
672 | |||
673 | |||
674 | BOOL draw_ellipses = FALSE; | ||
675 | if (use_ellipses) | ||
676 | { | ||
677 | // check for too long of a string | ||
678 | if (getWidthF32(wstr.c_str(), 0, max_chars) > scaled_max_pixels) | ||
679 | { | ||
680 | const LLWString dots(utf8str_to_wstring(LLString("..."))); | ||
681 | scaled_max_pixels = llmax(0, scaled_max_pixels - llround(getWidthF32(dots.c_str()))); | ||
682 | draw_ellipses = TRUE; | ||
683 | } | ||
684 | } | ||
685 | |||
686 | |||
687 | glBegin(GL_QUADS); | ||
688 | for (i = begin_offset; i < begin_offset + length; i++) | ||
689 | { | ||
690 | llwchar wch = wstr[i]; | ||
691 | |||
692 | // Handle embedded characters first, if they're enabled. | ||
693 | // Embedded characters are a hack for notecards | ||
694 | const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL; | ||
695 | if (ext_data) | ||
696 | { | ||
697 | LLImageGL* ext_image = ext_data->mImage; | ||
698 | const LLWString& label = ext_data->mLabel; | ||
699 | |||
700 | F32 ext_height = (F32)ext_image->getHeight() * sScaleY; | ||
701 | |||
702 | F32 ext_width = (F32)ext_image->getWidth() * sScaleX; | ||
703 | F32 ext_advance = (EXT_X_BEARING * sScaleX) + ext_width; | ||
704 | |||
705 | if (!label.empty()) | ||
706 | { | ||
707 | ext_advance += (EXT_X_BEARING + gExtCharFont->getWidthF32( label.c_str() )) * sScaleX; | ||
708 | } | ||
709 | |||
710 | if (start_x + scaled_max_pixels < cur_x + ext_advance) | ||
711 | { | ||
712 | // Not enough room for this character. | ||
713 | break; | ||
714 | } | ||
715 | |||
716 | glEnd(); | ||
717 | |||
718 | glColor3f(1.f, 1.f, 1.f); | ||
719 | |||
720 | ext_image->bind(); | ||
721 | const F32 ext_x = cur_render_x + (EXT_X_BEARING * sScaleX); | ||
722 | const F32 ext_y = cur_render_y + (EXT_Y_BEARING * sScaleY + mAscender - mLineHeight); | ||
723 | |||
724 | glBegin(GL_QUADS); | ||
725 | { | ||
726 | S32 num_passes = render_bold ? 2 : 1; | ||
727 | for (S32 pass = 0; pass < num_passes; pass++) | ||
728 | { | ||
729 | glTexCoord2f(1.f, 1.f); | ||
730 | glVertex2f(llfont_round_x(ext_x + ext_width + (F32)(pass * BOLD_OFFSET)), | ||
731 | llfont_round_y(ext_y + ext_height)); | ||
732 | |||
733 | glTexCoord2f(0.f, 1.f); | ||
734 | glVertex2f(llfont_round_x(ext_x + (F32)(pass * BOLD_OFFSET)), | ||
735 | llfont_round_y(ext_y + ext_height)); | ||
736 | |||
737 | glTexCoord2f(0.f, 0.f); | ||
738 | glVertex2f(llfont_round_x(ext_x + (F32)(pass * BOLD_OFFSET)), llfont_round_y(ext_y)); | ||
739 | |||
740 | glTexCoord2f(1.f, 0.f); | ||
741 | glVertex2f(llfont_round_x(ext_x + ext_width + (F32)(pass * BOLD_OFFSET)), | ||
742 | llfont_round_y(ext_y)); | ||
743 | } | ||
744 | } | ||
745 | glEnd(); | ||
746 | |||
747 | if (!label.empty()) | ||
748 | { | ||
749 | glPushMatrix(); | ||
750 | //glLoadIdentity(); | ||
751 | //glTranslatef(sCurOrigin.mX, sCurOrigin.mY, 0.0f); | ||
752 | //glScalef(sScaleX, sScaleY, 1.f); | ||
753 | gExtCharFont->render(label, 0, | ||
754 | /*llfloor*/((ext_x + (F32)ext_image->getWidth() + EXT_X_BEARING) / sScaleX), | ||
755 | /*llfloor*/(cur_y / sScaleY), | ||
756 | color, | ||
757 | halign, BASELINE, NORMAL, S32_MAX, S32_MAX, NULL, | ||
758 | TRUE ); | ||
759 | glPopMatrix(); | ||
760 | } | ||
761 | |||
762 | glColor4fv(color.mV); | ||
763 | |||
764 | chars_drawn++; | ||
765 | cur_x += ext_advance; | ||
766 | if (((i + 1) < length) && wstr[i+1]) | ||
767 | { | ||
768 | cur_x += EXT_KERNING * sScaleX; | ||
769 | } | ||
770 | cur_render_x = cur_x; | ||
771 | |||
772 | // Bind the font texture | ||
773 | mImageGLp->bind(); | ||
774 | glBegin(GL_QUADS); | ||
775 | } | ||
776 | else | ||
777 | { | ||
778 | if (!hasGlyph(wch)) | ||
779 | { | ||
780 | glEnd(); | ||
781 | (const_cast<LLFontGL*>(this))->addChar(wch); | ||
782 | glBegin(GL_QUADS); | ||
783 | } | ||
784 | |||
785 | const LLFontGlyphInfo* fgi= getGlyphInfo(wch); | ||
786 | if (!fgi) | ||
787 | { | ||
788 | llerrs << "Missing Glyph Info" << llendl; | ||
789 | } | ||
790 | if ((start_x + scaled_max_pixels) < (cur_x + fgi->mXBearing + fgi->mWidth)) | ||
791 | { | ||
792 | // Not enough room for this character. | ||
793 | break; | ||
794 | } | ||
795 | |||
796 | // Draw the text at the appropriate location | ||
797 | //Specify vertices and texture coordinates | ||
798 | S32 num_passes = render_bold ? 2 : 1; | ||
799 | for (S32 pass = 0; pass < num_passes; pass++) | ||
800 | { | ||
801 | glTexCoord2f((fgi->mXBitmapOffset - PAD_AMT) * inv_width, | ||
802 | (fgi->mYBitmapOffset + fgi->mHeight + PAD_AMT) * inv_height); | ||
803 | glVertex2f(llfont_round_x(cur_render_x + (F32)fgi->mXBearing + (F32)(pass * BOLD_OFFSET) - PAD_AMT), | ||
804 | llfont_round_y(cur_render_y + (F32)fgi->mYBearing + PAD_AMT)); | ||
805 | glTexCoord2f((fgi->mXBitmapOffset - PAD_AMT) * inv_width, | ||
806 | (fgi->mYBitmapOffset - PAD_AMT) * inv_height); | ||
807 | glVertex2f(llfont_round_x(cur_render_x + (F32)fgi->mXBearing + slant_offset + (F32)(pass * BOLD_OFFSET) - PAD_AMT), | ||
808 | llfont_round_y(cur_render_y + (F32)fgi->mYBearing - (F32)fgi->mHeight - PAD_AMT)); | ||
809 | glTexCoord2f((fgi->mXBitmapOffset + fgi->mWidth + PAD_AMT) * inv_width, | ||
810 | (fgi->mYBitmapOffset - PAD_AMT) * inv_height); | ||
811 | glVertex2f(llfont_round_x(cur_render_x + (F32)fgi->mXBearing + slant_offset + (F32)fgi->mWidth + (F32)(pass * BOLD_OFFSET) + PAD_AMT), | ||
812 | llfont_round_y(cur_render_y + (F32)fgi->mYBearing - (F32)fgi->mHeight - PAD_AMT)); | ||
813 | glTexCoord2f((fgi->mXBitmapOffset + fgi->mWidth + PAD_AMT) * inv_width, | ||
814 | (fgi->mYBitmapOffset + fgi->mHeight + PAD_AMT) * inv_height); | ||
815 | glVertex2f(llfont_round_x(cur_render_x + (F32)fgi->mXBearing + (F32)fgi->mWidth + (F32)(pass * BOLD_OFFSET) + PAD_AMT), | ||
816 | llfont_round_y(cur_render_y + (F32)fgi->mYBearing + PAD_AMT)); | ||
817 | } | ||
818 | |||
819 | chars_drawn++; | ||
820 | |||
821 | cur_x += fgi->mXAdvance; | ||
822 | cur_y += fgi->mYAdvance; | ||
823 | llwchar next_char = wstr[i+1]; | ||
824 | if (next_char && (next_char < LAST_CHARACTER)) | ||
825 | { | ||
826 | // Kern this puppy. | ||
827 | if (!hasGlyph(next_char)) | ||
828 | { | ||
829 | glEnd(); | ||
830 | (const_cast<LLFontGL*>(this))->addChar(next_char); | ||
831 | glBegin(GL_QUADS); | ||
832 | } | ||
833 | cur_x += getXKerning(wch, next_char); | ||
834 | } | ||
835 | |||
836 | // Round after kerning. | ||
837 | // Must do this to cur_x, not just to cur_render_x, otherwise you | ||
838 | // will squish sub-pixel kerned characters too close together. | ||
839 | // For example, "CCCCC" looks bad. | ||
840 | cur_x = (F32)llfloor(cur_x + 0.5f); | ||
841 | //cur_y = (F32)llfloor(cur_y + 0.5f); | ||
842 | |||
843 | cur_render_x = cur_x; | ||
844 | cur_render_y = cur_y; | ||
845 | } | ||
846 | } | ||
847 | |||
848 | glEnd(); | ||
849 | |||
850 | if (right_x) | ||
851 | { | ||
852 | *right_x = cur_x / sScaleX; | ||
853 | } | ||
854 | |||
855 | if (style & UNDERLINE) | ||
856 | { | ||
857 | LLGLSNoTexture no_texture; | ||
858 | glBegin(GL_LINES); | ||
859 | glVertex2f(start_x, cur_y - (mDescender)); | ||
860 | glVertex2f(cur_x, cur_y - (mDescender)); | ||
861 | glEnd(); | ||
862 | } | ||
863 | |||
864 | // *FIX: get this working in all alignment cases, etc. | ||
865 | if (draw_ellipses) | ||
866 | { | ||
867 | // recursively render ellipses at end of string | ||
868 | // we've already reserved enough room | ||
869 | glPushMatrix(); | ||
870 | //glLoadIdentity(); | ||
871 | //glTranslatef(sCurOrigin.mX, sCurOrigin.mY, 0.0f); | ||
872 | //glScalef(sScaleX, sScaleY, 1.f); | ||
873 | renderUTF8("...", | ||
874 | 0, | ||
875 | cur_x / sScaleX, (F32)y, | ||
876 | color, | ||
877 | LEFT, valign, | ||
878 | style, | ||
879 | S32_MAX, max_pixels, | ||
880 | right_x, | ||
881 | FALSE); | ||
882 | glPopMatrix(); | ||
883 | } | ||
884 | |||
885 | glPopMatrix(); | ||
886 | |||
887 | return chars_drawn; | ||
888 | } | ||
889 | |||
890 | |||
891 | LLImageGL *LLFontGL::getImageGL() const | ||
892 | { | ||
893 | return mImageGLp; | ||
894 | } | ||
895 | |||
896 | S32 LLFontGL::getWidth(const LLString& utf8text) const | ||
897 | { | ||
898 | LLWString wtext = utf8str_to_wstring(utf8text); | ||
899 | return getWidth(wtext.c_str(), 0, S32_MAX); | ||
900 | } | ||
901 | |||
902 | S32 LLFontGL::getWidth(const llwchar* wchars) const | ||
903 | { | ||
904 | return getWidth(wchars, 0, S32_MAX); | ||
905 | } | ||
906 | |||
907 | S32 LLFontGL::getWidth(const LLString& utf8text, const S32 begin_offset, const S32 max_chars) const | ||
908 | { | ||
909 | LLWString wtext = utf8str_to_wstring(utf8text); | ||
910 | return getWidth(wtext.c_str(), begin_offset, max_chars); | ||
911 | } | ||
912 | |||
913 | S32 LLFontGL::getWidth(const llwchar* wchars, const S32 begin_offset, const S32 max_chars, BOOL use_embedded) const | ||
914 | { | ||
915 | F32 width = getWidthF32(wchars, begin_offset, max_chars, use_embedded); | ||
916 | return llround(width); | ||
917 | } | ||
918 | |||
919 | F32 LLFontGL::getWidthF32(const LLString& utf8text) const | ||
920 | { | ||
921 | LLWString wtext = utf8str_to_wstring(utf8text); | ||
922 | return getWidthF32(wtext.c_str(), 0, S32_MAX); | ||
923 | } | ||
924 | |||
925 | F32 LLFontGL::getWidthF32(const llwchar* wchars) const | ||
926 | { | ||
927 | return getWidthF32(wchars, 0, S32_MAX); | ||
928 | } | ||
929 | |||
930 | F32 LLFontGL::getWidthF32(const LLString& utf8text, const S32 begin_offset, const S32 max_chars ) const | ||
931 | { | ||
932 | LLWString wtext = utf8str_to_wstring(utf8text); | ||
933 | return getWidthF32(wtext.c_str(), begin_offset, max_chars); | ||
934 | } | ||
935 | |||
936 | F32 LLFontGL::getWidthF32(const llwchar* wchars, const S32 begin_offset, const S32 max_chars, BOOL use_embedded) const | ||
937 | { | ||
938 | const S32 LAST_CHARACTER = LLFont::LAST_CHAR_FULL; | ||
939 | |||
940 | F32 cur_x = 0; | ||
941 | const S32 max_index = begin_offset + max_chars; | ||
942 | for (S32 i = begin_offset; i < max_index; i++) | ||
943 | { | ||
944 | const llwchar wch = wchars[i]; | ||
945 | if (wch == 0) | ||
946 | { | ||
947 | break; // done | ||
948 | } | ||
949 | const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL; | ||
950 | if (ext_data) | ||
951 | { | ||
952 | // Handle crappy embedded hack | ||
953 | cur_x += getEmbeddedCharAdvance(ext_data); | ||
954 | |||
955 | if( ((i+1) < max_chars) && (i+1 < max_index)) | ||
956 | { | ||
957 | cur_x += EXT_KERNING * sScaleX; | ||
958 | } | ||
959 | } | ||
960 | else | ||
961 | { | ||
962 | cur_x += getXAdvance(wch); | ||
963 | llwchar next_char = wchars[i+1]; | ||
964 | |||
965 | if (((i + 1) < max_chars) | ||
966 | && next_char | ||
967 | && (next_char < LAST_CHARACTER)) | ||
968 | { | ||
969 | // Kern this puppy. | ||
970 | cur_x += getXKerning(wch, next_char); | ||
971 | } | ||
972 | } | ||
973 | // Round after kerning. | ||
974 | cur_x = (F32)llfloor(cur_x + 0.5f); | ||
975 | } | ||
976 | |||
977 | return cur_x / sScaleX; | ||
978 | } | ||
979 | |||
980 | |||
981 | |||
982 | // Returns the max number of complete characters from text (up to max_chars) that can be drawn in max_pixels | ||
983 | S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_chars, | ||
984 | BOOL end_on_word_boundary, const BOOL use_embedded, | ||
985 | F32* drawn_pixels) const | ||
986 | { | ||
987 | if (!wchars || !wchars[0] || max_chars == 0) | ||
988 | { | ||
989 | return 0; | ||
990 | } | ||
991 | |||
992 | llassert(max_pixels >= 0.f); | ||
993 | llassert(max_chars >= 0); | ||
994 | |||
995 | BOOL clip = FALSE; | ||
996 | F32 cur_x = 0; | ||
997 | F32 drawn_x = 0; | ||
998 | |||
999 | S32 start_of_last_word = 0; | ||
1000 | BOOL in_word = FALSE; | ||
1001 | |||
1002 | F32 scaled_max_pixels = (F32)llceil(max_pixels * sScaleX); | ||
1003 | |||
1004 | S32 i; | ||
1005 | for (i=0; (i < max_chars); i++) | ||
1006 | { | ||
1007 | llwchar wch = wchars[i]; | ||
1008 | |||
1009 | if(wch == 0) | ||
1010 | { | ||
1011 | // Null terminator. We're done. | ||
1012 | break; | ||
1013 | } | ||
1014 | |||
1015 | const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL; | ||
1016 | if (ext_data) | ||
1017 | { | ||
1018 | if (in_word) | ||
1019 | { | ||
1020 | in_word = FALSE; | ||
1021 | } | ||
1022 | else | ||
1023 | { | ||
1024 | start_of_last_word = i; | ||
1025 | } | ||
1026 | cur_x += getEmbeddedCharAdvance(ext_data); | ||
1027 | |||
1028 | if (scaled_max_pixels < cur_x) | ||
1029 | { | ||
1030 | clip = TRUE; | ||
1031 | break; | ||
1032 | } | ||
1033 | |||
1034 | if (((i+1) < max_chars) && wchars[i+1]) | ||
1035 | { | ||
1036 | cur_x += EXT_KERNING * sScaleX; | ||
1037 | } | ||
1038 | |||
1039 | if( scaled_max_pixels < cur_x ) | ||
1040 | { | ||
1041 | clip = TRUE; | ||
1042 | break; | ||
1043 | } | ||
1044 | } | ||
1045 | else | ||
1046 | { | ||
1047 | if (in_word) | ||
1048 | { | ||
1049 | if (iswspace(wch)) | ||
1050 | { | ||
1051 | in_word = FALSE; | ||
1052 | } | ||
1053 | } | ||
1054 | else | ||
1055 | { | ||
1056 | start_of_last_word = i; | ||
1057 | if (!iswspace(wch)) | ||
1058 | { | ||
1059 | in_word = TRUE; | ||
1060 | } | ||
1061 | } | ||
1062 | |||
1063 | cur_x += getXAdvance(wch); | ||
1064 | |||
1065 | if (scaled_max_pixels < cur_x) | ||
1066 | { | ||
1067 | clip = TRUE; | ||
1068 | break; | ||
1069 | } | ||
1070 | |||
1071 | if (((i+1) < max_chars) && wchars[i+1]) | ||
1072 | { | ||
1073 | // Kern this puppy. | ||
1074 | cur_x += getXKerning(wch, wchars[i+1]); | ||
1075 | } | ||
1076 | } | ||
1077 | // Round after kerning. | ||
1078 | cur_x = (F32)llfloor(cur_x + 0.5f); | ||
1079 | drawn_x = cur_x; | ||
1080 | } | ||
1081 | |||
1082 | if( clip && end_on_word_boundary && (start_of_last_word != 0) ) | ||
1083 | { | ||
1084 | i = start_of_last_word; | ||
1085 | } | ||
1086 | if (drawn_pixels) | ||
1087 | { | ||
1088 | *drawn_pixels = drawn_x; | ||
1089 | } | ||
1090 | return i; | ||
1091 | } | ||
1092 | |||
1093 | |||
1094 | S32 LLFontGL::firstDrawableChar(const llwchar* wchars, F32 max_pixels, S32 text_len, S32 start_pos, S32 max_chars) const | ||
1095 | { | ||
1096 | if (!wchars || !wchars[0] || max_chars == 0) | ||
1097 | { | ||
1098 | return 0; | ||
1099 | } | ||
1100 | |||
1101 | F32 total_width = 0.0; | ||
1102 | S32 drawable_chars = 0; | ||
1103 | |||
1104 | F32 scaled_max_pixels = max_pixels * sScaleX; | ||
1105 | |||
1106 | S32 start = llmin(start_pos, text_len - 1); | ||
1107 | for (S32 i = start; i >= 0; i--) | ||
1108 | { | ||
1109 | llwchar wch = wchars[i]; | ||
1110 | |||
1111 | const embedded_data_t* ext_data = getEmbeddedCharData(wch); | ||
1112 | if (ext_data) | ||
1113 | { | ||
1114 | F32 char_width = getEmbeddedCharAdvance(ext_data); | ||
1115 | |||
1116 | if( scaled_max_pixels < (total_width + char_width) ) | ||
1117 | { | ||
1118 | break; | ||
1119 | } | ||
1120 | |||
1121 | total_width += char_width; | ||
1122 | |||
1123 | drawable_chars++; | ||
1124 | if( max_chars >= 0 && drawable_chars >= max_chars ) | ||
1125 | { | ||
1126 | break; | ||
1127 | } | ||
1128 | |||
1129 | if ( i > 0 ) | ||
1130 | { | ||
1131 | total_width += EXT_KERNING * sScaleX; | ||
1132 | } | ||
1133 | |||
1134 | // Round after kerning. | ||
1135 | total_width = (F32)llfloor(total_width + 0.5f); | ||
1136 | } | ||
1137 | else | ||
1138 | { | ||
1139 | F32 char_width = getXAdvance(wch); | ||
1140 | if( scaled_max_pixels < (total_width + char_width) ) | ||
1141 | { | ||
1142 | break; | ||
1143 | } | ||
1144 | |||
1145 | total_width += char_width; | ||
1146 | |||
1147 | drawable_chars++; | ||
1148 | if( max_chars >= 0 && drawable_chars >= max_chars ) | ||
1149 | { | ||
1150 | break; | ||
1151 | } | ||
1152 | |||
1153 | if ( i > 0 ) | ||
1154 | { | ||
1155 | // Kerning | ||
1156 | total_width += getXKerning(wchars[i-1], wch); | ||
1157 | } | ||
1158 | |||
1159 | // Round after kerning. | ||
1160 | total_width = (F32)llfloor(total_width + 0.5f); | ||
1161 | } | ||
1162 | } | ||
1163 | |||
1164 | return text_len - drawable_chars; | ||
1165 | } | ||
1166 | |||
1167 | |||
1168 | S32 LLFontGL::charFromPixelOffset(const llwchar* wchars, const S32 begin_offset, F32 target_x, F32 max_pixels, S32 max_chars, BOOL round, BOOL use_embedded) const | ||
1169 | { | ||
1170 | if (!wchars || !wchars[0] || max_chars == 0) | ||
1171 | { | ||
1172 | return 0; | ||
1173 | } | ||
1174 | |||
1175 | F32 cur_x = 0; | ||
1176 | S32 pos = 0; | ||
1177 | |||
1178 | target_x *= sScaleX; | ||
1179 | |||
1180 | // max_chars is S32_MAX by default, so make sure we don't get overflow | ||
1181 | const S32 max_index = begin_offset + llmin(S32_MAX - begin_offset, max_chars); | ||
1182 | |||
1183 | F32 scaled_max_pixels = max_pixels * sScaleX; | ||
1184 | |||
1185 | for (S32 i = begin_offset; (i < max_index); i++) | ||
1186 | { | ||
1187 | llwchar wch = wchars[i]; | ||
1188 | if (!wch) | ||
1189 | { | ||
1190 | break; // done | ||
1191 | } | ||
1192 | const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL; | ||
1193 | if (ext_data) | ||
1194 | { | ||
1195 | F32 ext_advance = getEmbeddedCharAdvance(ext_data); | ||
1196 | |||
1197 | if (round) | ||
1198 | { | ||
1199 | // Note: if the mouse is on the left half of the character, the pick is to the character's left | ||
1200 | // If it's on the right half, the pick is to the right. | ||
1201 | if (target_x < cur_x + ext_advance/2) | ||
1202 | { | ||
1203 | break; | ||
1204 | } | ||
1205 | } | ||
1206 | else | ||
1207 | { | ||
1208 | if (target_x < cur_x + ext_advance) | ||
1209 | { | ||
1210 | break; | ||
1211 | } | ||
1212 | } | ||
1213 | |||
1214 | if (scaled_max_pixels < cur_x + ext_advance) | ||
1215 | { | ||
1216 | break; | ||
1217 | } | ||
1218 | |||
1219 | pos++; | ||
1220 | cur_x += ext_advance; | ||
1221 | |||
1222 | if (((i + 1) < max_index) | ||
1223 | && (wchars[(i + 1)])) | ||
1224 | { | ||
1225 | cur_x += EXT_KERNING * sScaleX; | ||
1226 | } | ||
1227 | // Round after kerning. | ||
1228 | cur_x = (F32)llfloor(cur_x + 0.5f); | ||
1229 | } | ||
1230 | else | ||
1231 | { | ||
1232 | F32 char_width = getXAdvance(wch); | ||
1233 | |||
1234 | if (round) | ||
1235 | { | ||
1236 | // Note: if the mouse is on the left half of the character, the pick is to the character's left | ||
1237 | // If it's on the right half, the pick is to the right. | ||
1238 | if (target_x < cur_x + char_width*0.5f) | ||
1239 | { | ||
1240 | break; | ||
1241 | } | ||
1242 | } | ||
1243 | else if (target_x < cur_x + char_width) | ||
1244 | { | ||
1245 | break; | ||
1246 | } | ||
1247 | |||
1248 | if (scaled_max_pixels < cur_x + char_width) | ||
1249 | { | ||
1250 | break; | ||
1251 | } | ||
1252 | |||
1253 | pos++; | ||
1254 | cur_x += char_width; | ||
1255 | |||
1256 | if (((i + 1) < max_index) | ||
1257 | && (wchars[(i + 1)])) | ||
1258 | { | ||
1259 | llwchar next_char = wchars[i + 1]; | ||
1260 | // Kern this puppy. | ||
1261 | cur_x += getXKerning(wch, next_char); | ||
1262 | } | ||
1263 | |||
1264 | // Round after kerning. | ||
1265 | cur_x = (F32)llfloor(cur_x + 0.5f); | ||
1266 | } | ||
1267 | } | ||
1268 | |||
1269 | return pos; | ||
1270 | } | ||
1271 | |||
1272 | |||
1273 | const LLFontGL::embedded_data_t* LLFontGL::getEmbeddedCharData(const llwchar wch) const | ||
1274 | { | ||
1275 | // Handle crappy embedded hack | ||
1276 | embedded_map_t::const_iterator iter = mEmbeddedChars.find(wch); | ||
1277 | if (iter != mEmbeddedChars.end()) | ||
1278 | { | ||
1279 | return iter->second; | ||
1280 | } | ||
1281 | return NULL; | ||
1282 | } | ||
1283 | |||
1284 | |||
1285 | F32 LLFontGL::getEmbeddedCharAdvance(const embedded_data_t* ext_data) const | ||
1286 | { | ||
1287 | const LLWString& label = ext_data->mLabel; | ||
1288 | LLImageGL* ext_image = ext_data->mImage; | ||
1289 | |||
1290 | F32 ext_width = (F32)ext_image->getWidth(); | ||
1291 | if( !label.empty() ) | ||
1292 | { | ||
1293 | ext_width += (EXT_X_BEARING + gExtCharFont->getWidthF32(label.c_str())) * sScaleX; | ||
1294 | } | ||
1295 | |||
1296 | return (EXT_X_BEARING * sScaleX) + ext_width; | ||
1297 | } | ||
1298 | |||
1299 | |||
1300 | void LLFontGL::clearEmbeddedChars() | ||
1301 | { | ||
1302 | for_each(mEmbeddedChars.begin(), mEmbeddedChars.end(), DeletePairedPointer()); | ||
1303 | mEmbeddedChars.clear(); | ||
1304 | } | ||
1305 | |||
1306 | void LLFontGL::addEmbeddedChar( llwchar wc, LLImageGL* image, const LLString& label ) | ||
1307 | { | ||
1308 | LLWString wlabel = utf8str_to_wstring(label); | ||
1309 | addEmbeddedChar(wc, image, wlabel); | ||
1310 | } | ||
1311 | |||
1312 | void LLFontGL::addEmbeddedChar( llwchar wc, LLImageGL* image, const LLWString& wlabel ) | ||
1313 | { | ||
1314 | embedded_data_t* ext_data = new embedded_data_t(image, wlabel); | ||
1315 | mEmbeddedChars[wc] = ext_data; | ||
1316 | } | ||
1317 | |||
1318 | void LLFontGL::removeEmbeddedChar( llwchar wc ) | ||
1319 | { | ||
1320 | embedded_map_t::iterator iter = mEmbeddedChars.find(wc); | ||
1321 | if (iter != mEmbeddedChars.end()) | ||
1322 | { | ||
1323 | delete iter->second; | ||
1324 | mEmbeddedChars.erase(wc); | ||
1325 | } | ||
1326 | } | ||
1327 | |||
1328 | // static | ||
1329 | LLString LLFontGL::nameFromFont(const LLFontGL* fontp) | ||
1330 | { | ||
1331 | if (fontp == sSansSerifHuge) | ||
1332 | { | ||
1333 | return LLString("SansSerifHude"); | ||
1334 | } | ||
1335 | else if (fontp == sSansSerifSmall) | ||
1336 | { | ||
1337 | return LLString("SansSerifSmall"); | ||
1338 | } | ||
1339 | else if (fontp == sSansSerif) | ||
1340 | { | ||
1341 | return LLString("SansSerif"); | ||
1342 | } | ||
1343 | else if (fontp == sSansSerifBig) | ||
1344 | { | ||
1345 | return LLString("SansSerifBig"); | ||
1346 | } | ||
1347 | else if (fontp == sSansSerifBold) | ||
1348 | { | ||
1349 | return LLString("SansSerifBold"); | ||
1350 | } | ||
1351 | else if (fontp == sMonospace) | ||
1352 | { | ||
1353 | return LLString("Monospace"); | ||
1354 | } | ||
1355 | else | ||
1356 | { | ||
1357 | return LLString(); | ||
1358 | } | ||
1359 | } | ||
1360 | |||
1361 | // static | ||
1362 | LLFontGL* LLFontGL::fontFromName(const LLString& font_name) | ||
1363 | { | ||
1364 | LLFontGL* gl_font = NULL; | ||
1365 | if (font_name == "SansSerifHuge") | ||
1366 | { | ||
1367 | gl_font = LLFontGL::sSansSerifHuge; | ||
1368 | } | ||
1369 | else if (font_name == "SansSerifSmall") | ||
1370 | { | ||
1371 | gl_font = LLFontGL::sSansSerifSmall; | ||
1372 | } | ||
1373 | else if (font_name == "SansSerif") | ||
1374 | { | ||
1375 | gl_font = LLFontGL::sSansSerif; | ||
1376 | } | ||
1377 | else if (font_name == "SansSerifBig") | ||
1378 | { | ||
1379 | gl_font = LLFontGL::sSansSerifBig; | ||
1380 | } | ||
1381 | else if (font_name == "SansSerifBold") | ||
1382 | { | ||
1383 | gl_font = LLFontGL::sSansSerifBold; | ||
1384 | } | ||
1385 | else if (font_name == "Monospace") | ||
1386 | { | ||
1387 | gl_font = LLFontGL::sMonospace; | ||
1388 | } | ||
1389 | return gl_font; | ||
1390 | } | ||
1391 | |||
1392 | // static | ||
1393 | LLString LLFontGL::nameFromHAlign(LLFontGL::HAlign align) | ||
1394 | { | ||
1395 | if (align == LEFT) return LLString("left"); | ||
1396 | else if (align == RIGHT) return LLString("right"); | ||
1397 | else if (align == HCENTER) return LLString("center"); | ||
1398 | else return LLString(); | ||
1399 | } | ||
1400 | |||
1401 | // static | ||
1402 | LLFontGL::HAlign LLFontGL::hAlignFromName(const LLString& name) | ||
1403 | { | ||
1404 | LLFontGL::HAlign gl_hfont_align = LLFontGL::LEFT; | ||
1405 | if (name == "left") | ||
1406 | { | ||
1407 | gl_hfont_align = LLFontGL::LEFT; | ||
1408 | } | ||
1409 | else if (name == "right") | ||
1410 | { | ||
1411 | gl_hfont_align = LLFontGL::RIGHT; | ||
1412 | } | ||
1413 | else if (name == "center") | ||
1414 | { | ||
1415 | gl_hfont_align = LLFontGL::HCENTER; | ||
1416 | } | ||
1417 | //else leave left | ||
1418 | return gl_hfont_align; | ||
1419 | } | ||
1420 | |||
1421 | // static | ||
1422 | LLString LLFontGL::nameFromVAlign(LLFontGL::VAlign align) | ||
1423 | { | ||
1424 | if (align == TOP) return LLString("top"); | ||
1425 | else if (align == VCENTER) return LLString("center"); | ||
1426 | else if (align == BASELINE) return LLString("baseline"); | ||
1427 | else if (align == BOTTOM) return LLString("bottom"); | ||
1428 | else return LLString(); | ||
1429 | } | ||
1430 | |||
1431 | // static | ||
1432 | LLFontGL::VAlign LLFontGL::vAlignFromName(const LLString& name) | ||
1433 | { | ||
1434 | LLFontGL::VAlign gl_vfont_align = LLFontGL::BASELINE; | ||
1435 | if (name == "top") | ||
1436 | { | ||
1437 | gl_vfont_align = LLFontGL::TOP; | ||
1438 | } | ||
1439 | else if (name == "center") | ||
1440 | { | ||
1441 | gl_vfont_align = LLFontGL::VCENTER; | ||
1442 | } | ||
1443 | else if (name == "baseline") | ||
1444 | { | ||
1445 | gl_vfont_align = LLFontGL::BASELINE; | ||
1446 | } | ||
1447 | else if (name == "bottom") | ||
1448 | { | ||
1449 | gl_vfont_align = LLFontGL::BOTTOM; | ||
1450 | } | ||
1451 | //else leave baseline | ||
1452 | return gl_vfont_align; | ||
1453 | } | ||