aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llrender/llfontgl.cpp
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:44:46 -0500
committerJacek Antonelli2008-08-15 23:44:46 -0500
commit38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch)
treeadca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/llrender/llfontgl.cpp
parentREADME.txt (diff)
downloadmeta-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.cpp1453
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
37const S32 BOLD_OFFSET = 1;
38
39// static class members
40F32 LLFontGL::sVertDPI = 96.f;
41F32 LLFontGL::sHorizDPI = 96.f;
42F32 LLFontGL::sScaleX = 1.f;
43F32 LLFontGL::sScaleY = 1.f;
44LLString LLFontGL::sAppDir;
45
46LLFontGL* LLFontGL::sMonospace = NULL;
47LLFontGL* LLFontGL::sSansSerifSmall = NULL;
48LLFontGL* LLFontGL::sSansSerif = NULL;
49LLFontGL* LLFontGL::sSansSerifBig = NULL;
50LLFontGL* LLFontGL::sSansSerifHuge = NULL;
51LLFontGL* LLFontGL::sSansSerifBold = NULL;
52LLFontList* LLFontGL::sSSFallback = NULL;
53LLFontList* LLFontGL::sSSSmallFallback = NULL;
54LLFontList* LLFontGL::sSSBigFallback = NULL;
55LLFontList* LLFontGL::sSSHugeFallback = NULL;
56LLFontList* LLFontGL::sSSBoldFallback = NULL;
57LLColor4 LLFontGL::sShadowColor(0.f, 0.f, 0.f, 1.f);
58
59LLCoordFont LLFontGL::sCurOrigin;
60std::vector<LLCoordFont> LLFontGL::sOriginStack;
61
62LLFontGL*& gExtCharFont = LLFontGL::sSansSerif;
63
64const F32 EXT_X_BEARING = 1.f;
65const F32 EXT_Y_BEARING = 0.f;
66const F32 EXT_KERNING = 1.f;
67const F32 PIXEL_BORDER_THRESHOLD = 0.0001f;
68const F32 PIXEL_CORRECTION_DISTANCE = 0.01f;
69
70const F32 PAD_AMT = 0.5f;
71
72F32 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
79F32 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
87U8 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
109LLFontGL::LLFontGL()
110 : LLFont()
111{
112 init();
113 clearEmbeddedChars();
114}
115
116LLFontGL::LLFontGL(const LLFontGL &source)
117{
118 llerrs << "Not implemented!" << llendl;
119}
120
121LLFontGL::~LLFontGL()
122{
123 mImageGLp = NULL;
124 mRawImageGLp = NULL;
125 clearEmbeddedChars();
126}
127
128void 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
144void LLFontGL::reset()
145{
146 init();
147 resetBitmap();
148}
149
150// static
151LLString 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
184LLString 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
205bool 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
243bool 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
264BOOL 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
412void 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
449void 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
466LLFontGL &LLFontGL::operator=(const LLFontGL &source)
467{
468 llerrs << "Not implemented" << llendl;
469 return *this;
470}
471
472BOOL 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
484BOOL 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
500S32 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
513S32 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
891LLImageGL *LLFontGL::getImageGL() const
892{
893 return mImageGLp;
894}
895
896S32 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
902S32 LLFontGL::getWidth(const llwchar* wchars) const
903{
904 return getWidth(wchars, 0, S32_MAX);
905}
906
907S32 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
913S32 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
919F32 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
925F32 LLFontGL::getWidthF32(const llwchar* wchars) const
926{
927 return getWidthF32(wchars, 0, S32_MAX);
928}
929
930F32 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
936F32 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
983S32 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
1094S32 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
1168S32 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
1273const 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
1285F32 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
1300void LLFontGL::clearEmbeddedChars()
1301{
1302 for_each(mEmbeddedChars.begin(), mEmbeddedChars.end(), DeletePairedPointer());
1303 mEmbeddedChars.clear();
1304}
1305
1306void LLFontGL::addEmbeddedChar( llwchar wc, LLImageGL* image, const LLString& label )
1307{
1308 LLWString wlabel = utf8str_to_wstring(label);
1309 addEmbeddedChar(wc, image, wlabel);
1310}
1311
1312void 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
1318void 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
1329LLString 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
1362LLFontGL* 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
1393LLString 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
1402LLFontGL::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
1422LLString 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
1432LLFontGL::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}