00001
00032 #include "linden_common.h"
00033
00034 #include <boost/tokenizer.hpp>
00035
00036 #include "llfont.h"
00037 #include "llfontgl.h"
00038 #include "llgl.h"
00039 #include "llglimmediate.h"
00040 #include "v4color.h"
00041 #include "llstl.h"
00042
00043 const S32 BOLD_OFFSET = 1;
00044
00045
00046 F32 LLFontGL::sVertDPI = 96.f;
00047 F32 LLFontGL::sHorizDPI = 96.f;
00048 F32 LLFontGL::sScaleX = 1.f;
00049 F32 LLFontGL::sScaleY = 1.f;
00050 BOOL LLFontGL::sDisplayFont = TRUE ;
00051 LLString LLFontGL::sAppDir;
00052
00053 LLFontGL* LLFontGL::sMonospace = NULL;
00054 LLFontGL* LLFontGL::sSansSerifSmall = NULL;
00055 LLFontGL* LLFontGL::sSansSerif = NULL;
00056 LLFontGL* LLFontGL::sSansSerifBig = NULL;
00057 LLFontGL* LLFontGL::sSansSerifHuge = NULL;
00058 LLFontGL* LLFontGL::sSansSerifBold = NULL;
00059 LLFontList* LLFontGL::sMonospaceFallback = NULL;
00060 LLFontList* LLFontGL::sSSFallback = NULL;
00061 LLFontList* LLFontGL::sSSSmallFallback = NULL;
00062 LLFontList* LLFontGL::sSSBigFallback = NULL;
00063 LLFontList* LLFontGL::sSSHugeFallback = NULL;
00064 LLFontList* LLFontGL::sSSBoldFallback = NULL;
00065 LLColor4 LLFontGL::sShadowColor(0.f, 0.f, 0.f, 1.f);
00066
00067 LLCoordFont LLFontGL::sCurOrigin;
00068 std::vector<LLCoordFont> LLFontGL::sOriginStack;
00069
00070 LLFontGL*& gExtCharFont = LLFontGL::sSansSerif;
00071
00072 const F32 EXT_X_BEARING = 1.f;
00073 const F32 EXT_Y_BEARING = 0.f;
00074 const F32 EXT_KERNING = 1.f;
00075 const F32 PIXEL_BORDER_THRESHOLD = 0.0001f;
00076 const F32 PIXEL_CORRECTION_DISTANCE = 0.01f;
00077
00078 const F32 PAD_AMT = 0.5f;
00079 const F32 DROP_SHADOW_SOFT_STRENGTH = 0.3f;
00080
00081 F32 llfont_round_x(F32 x)
00082 {
00083
00084
00085 return x;
00086 }
00087
00088 F32 llfont_round_y(F32 y)
00089 {
00090
00091
00092 return y;
00093 }
00094
00095
00096 U8 LLFontGL::getStyleFromString(const LLString &style)
00097 {
00098 S32 ret = 0;
00099 if (style.find("NORMAL") != style.npos)
00100 {
00101 ret |= NORMAL;
00102 }
00103 if (style.find("BOLD") != style.npos)
00104 {
00105 ret |= BOLD;
00106 }
00107 if (style.find("ITALIC") != style.npos)
00108 {
00109 ret |= ITALIC;
00110 }
00111 if (style.find("UNDERLINE") != style.npos)
00112 {
00113 ret |= UNDERLINE;
00114 }
00115 if (style.find("SHADOW") != style.npos)
00116 {
00117 ret |= DROP_SHADOW;
00118 }
00119 if (style.find("SOFT_SHADOW") != style.npos)
00120 {
00121 ret |= DROP_SHADOW_SOFT;
00122 }
00123 return ret;
00124 }
00125
00126 LLFontGL::LLFontGL()
00127 : LLFont()
00128 {
00129 init();
00130 clearEmbeddedChars();
00131 }
00132
00133 LLFontGL::LLFontGL(const LLFontGL &source)
00134 {
00135 llerrs << "Not implemented!" << llendl;
00136 }
00137
00138 LLFontGL::~LLFontGL()
00139 {
00140 mImageGLp = NULL;
00141 mRawImageGLp = NULL;
00142 clearEmbeddedChars();
00143 }
00144
00145 void LLFontGL::init()
00146 {
00147 if (mImageGLp.isNull())
00148 {
00149 mImageGLp = new LLImageGL(FALSE);
00150
00151 mImageGLp->bind();
00152
00153
00154 }
00155 if (mRawImageGLp.isNull())
00156 {
00157 mRawImageGLp = new LLImageRaw;
00158 }
00159 setRawImage( mRawImageGLp );
00160 }
00161
00162 void LLFontGL::reset()
00163 {
00164 init();
00165 resetBitmap();
00166 }
00167
00168
00169 LLString LLFontGL::getFontPathSystem()
00170 {
00171 LLString system_path;
00172
00173
00174 char *system_root = NULL;
00175 #if LL_WINDOWS
00176 system_root = getenv("SystemRoot");
00177 if (!system_root)
00178 {
00179 llwarns << "SystemRoot not found, attempting to load fonts from default path." << llendl;
00180 }
00181 #endif
00182
00183 if (system_root)
00184 {
00185 system_path = llformat("%s/fonts/", system_root);
00186 }
00187 else
00188 {
00189 #if LL_WINDOWS
00190
00191 system_path = "/WINDOWS/FONTS/";
00192 #elif LL_DARWIN
00193
00194 system_path = "/System/Library/Fonts/";
00195 #endif
00196 }
00197 return system_path;
00198 }
00199
00200
00201
00202 LLString LLFontGL::getFontPathLocal()
00203 {
00204 LLString local_path;
00205
00206
00207
00208
00209 if (LLFontGL::sAppDir.length())
00210 {
00211
00212 local_path = LLFontGL::sAppDir + "/fonts/";
00213 }
00214 else
00215 {
00216
00217 local_path = "./fonts/";
00218 }
00219 return local_path;
00220 }
00221
00222
00223 bool LLFontGL::loadFaceFallback(LLFontList *fontlistp, const LLString& fontname, const F32 point_size)
00224 {
00225 LLString local_path = getFontPathLocal();
00226 LLString sys_path = getFontPathSystem();
00227
00228
00229
00230 typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
00231 boost::char_separator<char> sep(";");
00232 tokenizer tokens(fontname, sep);
00233 tokenizer::iterator token_iter;
00234
00235 for(token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
00236 {
00237 LLFont *fontp = new LLFont();
00238 LLString font_path = local_path + *token_iter;
00239 if (!fontp->loadFace(font_path, point_size, sVertDPI, sHorizDPI, 2, TRUE))
00240 {
00241 font_path = sys_path + *token_iter;
00242 if (!fontp->loadFace(font_path, point_size, sVertDPI, sHorizDPI, 2, TRUE))
00243 {
00244 LL_INFOS_ONCE("ViewerImages") << "Couldn't load font " << *token_iter << LL_ENDL;
00245 delete fontp;
00246 fontp = NULL;
00247 }
00248 }
00249
00250 if(fontp)
00251 {
00252 fontlistp->addAtEnd(fontp);
00253 }
00254 }
00255
00256
00257 return (fontlistp->size() > 0);
00258 }
00259
00260
00261 bool LLFontGL::loadFace(LLFontGL *fontp, const LLString& fontname, const F32 point_size, LLFontList *fallback_fontp)
00262 {
00263 LLString local_path = getFontPathLocal();
00264 LLString font_path = local_path + fontname;
00265 if (!fontp->loadFace(font_path, point_size, sVertDPI, sHorizDPI, 2, FALSE))
00266 {
00267 LLString sys_path = getFontPathSystem();
00268 font_path = sys_path + fontname;
00269 if (!fontp->loadFace(font_path, point_size, sVertDPI, sHorizDPI, 2, FALSE))
00270 {
00271 LL_WARNS("ViewerImages") << "Couldn't load font " << fontname << LL_ENDL;
00272 return false;
00273 }
00274 }
00275
00276 fontp->setFallbackFont(fallback_fontp);
00277 return true;
00278 }
00279
00280
00281
00282 BOOL LLFontGL::initDefaultFonts(F32 screen_dpi, F32 x_scale, F32 y_scale,
00283 const LLString& monospace_file, F32 monospace_size,
00284 const LLString& sansserif_file,
00285 const LLString& sanserif_fallback_file, F32 ss_fallback_scale,
00286 F32 small_size, F32 medium_size, F32 big_size, F32 huge_size,
00287 const LLString& sansserif_bold_file, F32 bold_size,
00288 const LLString& app_dir)
00289 {
00290 BOOL failed = FALSE;
00291 sVertDPI = (F32)llfloor(screen_dpi * y_scale);
00292 sHorizDPI = (F32)llfloor(screen_dpi * x_scale);
00293 sScaleX = x_scale;
00294 sScaleY = y_scale;
00295 sAppDir = app_dir;
00296
00297
00298
00299
00300
00301 if (!sMonospace)
00302 {
00303 sMonospace = new LLFontGL();
00304 }
00305 else
00306 {
00307 sMonospace->reset();
00308 }
00309
00310 if (sMonospaceFallback)
00311 {
00312 delete sMonospaceFallback;
00313 }
00314 sMonospaceFallback = new LLFontList();
00315 if (!loadFaceFallback(
00316 sMonospaceFallback,
00317 sanserif_fallback_file,
00318 monospace_size * ss_fallback_scale))
00319 {
00320 delete sMonospaceFallback;
00321 sMonospaceFallback = NULL;
00322 }
00323
00324 failed |= !loadFace(sMonospace, monospace_file, monospace_size, sMonospaceFallback);
00325
00326
00327
00328
00329 if(!sSansSerifHuge)
00330 {
00331 sSansSerifHuge = new LLFontGL();
00332 }
00333 else
00334 {
00335 sSansSerifHuge->reset();
00336 }
00337
00338 if (sSSHugeFallback)
00339 {
00340 delete sSSHugeFallback;
00341 }
00342 sSSHugeFallback = new LLFontList();
00343 if (!loadFaceFallback(
00344 sSSHugeFallback,
00345 sanserif_fallback_file,
00346 huge_size*ss_fallback_scale))
00347 {
00348 delete sSSHugeFallback;
00349 sSSHugeFallback = NULL;
00350 }
00351
00352 failed |= !loadFace(sSansSerifHuge, sansserif_file, huge_size, sSSHugeFallback);
00353
00354
00355 if(!sSansSerifBig)
00356 {
00357 sSansSerifBig = new LLFontGL();
00358 }
00359 else
00360 {
00361 sSansSerifBig->reset();
00362 }
00363
00364 if (sSSBigFallback)
00365 {
00366 delete sSSBigFallback;
00367 }
00368 sSSBigFallback = new LLFontList();
00369 if (!loadFaceFallback(
00370 sSSBigFallback,
00371 sanserif_fallback_file,
00372 big_size*ss_fallback_scale))
00373 {
00374 delete sSSBigFallback;
00375 sSSBigFallback = NULL;
00376 }
00377
00378 failed |= !loadFace(sSansSerifBig, sansserif_file, big_size, sSSBigFallback);
00379
00380
00381 if(!sSansSerif)
00382 {
00383 sSansSerif = new LLFontGL();
00384 }
00385 else
00386 {
00387 sSansSerif->reset();
00388 }
00389
00390 if (sSSFallback)
00391 {
00392 delete sSSFallback;
00393 }
00394 sSSFallback = new LLFontList();
00395 if (!loadFaceFallback(
00396 sSSFallback,
00397 sanserif_fallback_file,
00398 medium_size*ss_fallback_scale))
00399 {
00400 delete sSSFallback;
00401 sSSFallback = NULL;
00402 }
00403 failed |= !loadFace(sSansSerif, sansserif_file, medium_size, sSSFallback);
00404
00405
00406 if(!sSansSerifSmall)
00407 {
00408 sSansSerifSmall = new LLFontGL();
00409 }
00410 else
00411 {
00412 sSansSerifSmall->reset();
00413 }
00414
00415 if(sSSSmallFallback)
00416 {
00417 delete sSSSmallFallback;
00418 }
00419 sSSSmallFallback = new LLFontList();
00420 if (!loadFaceFallback(
00421 sSSSmallFallback,
00422 sanserif_fallback_file,
00423 small_size*ss_fallback_scale))
00424 {
00425 delete sSSSmallFallback;
00426 sSSSmallFallback = NULL;
00427 }
00428 failed |= !loadFace(sSansSerifSmall, sansserif_file, small_size, sSSSmallFallback);
00429
00430
00431
00432
00433
00434 if(!sSansSerifBold)
00435 {
00436 sSansSerifBold = new LLFontGL();
00437 }
00438 else
00439 {
00440 sSansSerifBold->reset();
00441 }
00442
00443 if (sSSBoldFallback)
00444 {
00445 delete sSSBoldFallback;
00446 }
00447 sSSBoldFallback = new LLFontList();
00448 if (!loadFaceFallback(
00449 sSSBoldFallback,
00450 sanserif_fallback_file,
00451 medium_size*ss_fallback_scale))
00452 {
00453 delete sSSBoldFallback;
00454 sSSBoldFallback = NULL;
00455 }
00456 failed |= !loadFace(sSansSerifBold, sansserif_bold_file, medium_size, sSSBoldFallback);
00457
00458 return !failed;
00459 }
00460
00461
00462
00463
00464 void LLFontGL::destroyDefaultFonts()
00465 {
00466 delete sMonospace;
00467 sMonospace = NULL;
00468
00469 delete sSansSerifHuge;
00470 sSansSerifHuge = NULL;
00471
00472 delete sSansSerifBig;
00473 sSansSerifBig = NULL;
00474
00475 delete sSansSerif;
00476 sSansSerif = NULL;
00477
00478 delete sSansSerifSmall;
00479 sSansSerifSmall = NULL;
00480
00481 delete sSansSerifBold;
00482 sSansSerifBold = NULL;
00483
00484 delete sMonospaceFallback;
00485 sMonospaceFallback = NULL;
00486
00487 delete sSSHugeFallback;
00488 sSSHugeFallback = NULL;
00489
00490 delete sSSBigFallback;
00491 sSSBigFallback = NULL;
00492
00493 delete sSSFallback;
00494 sSSFallback = NULL;
00495
00496 delete sSSSmallFallback;
00497 sSSSmallFallback = NULL;
00498
00499 delete sSSBoldFallback;
00500 sSSBoldFallback = NULL;
00501 }
00502
00503
00504 void LLFontGL::destroyGL()
00505 {
00506 if (!sMonospace)
00507 {
00508
00509 return;
00510 }
00511 sMonospace->mImageGLp->destroyGLTexture();
00512 sSansSerifHuge->mImageGLp->destroyGLTexture();
00513 sSansSerifSmall->mImageGLp->destroyGLTexture();
00514 sSansSerif->mImageGLp->destroyGLTexture();
00515 sSansSerifBig->mImageGLp->destroyGLTexture();
00516 sSansSerifBold->mImageGLp->destroyGLTexture();
00517 }
00518
00519
00520
00521 LLFontGL &LLFontGL::operator=(const LLFontGL &source)
00522 {
00523 llerrs << "Not implemented" << llendl;
00524 return *this;
00525 }
00526
00527 BOOL LLFontGL::loadFace(const std::string& filename,
00528 const F32 point_size, const F32 vert_dpi, const F32 horz_dpi,
00529 const S32 components, BOOL is_fallback)
00530 {
00531 if (!LLFont::loadFace(filename, point_size, vert_dpi, horz_dpi, components, is_fallback))
00532 {
00533 return FALSE;
00534 }
00535 mImageGLp->createGLTexture(0, mRawImageGLp);
00536 mImageGLp->bind();
00537 mImageGLp->setMipFilterNearest(TRUE, TRUE);
00538 return TRUE;
00539 }
00540
00541 BOOL LLFontGL::addChar(const llwchar wch)
00542 {
00543 if (!LLFont::addChar(wch))
00544 {
00545 return FALSE;
00546 }
00547
00548 stop_glerror();
00549 mImageGLp->setSubImage(mRawImageGLp, 0, 0, mImageGLp->getWidth(), mImageGLp->getHeight());
00550 mImageGLp->bind();
00551 mImageGLp->setMipFilterNearest(TRUE, TRUE);
00552 stop_glerror();
00553 return TRUE;
00554 }
00555
00556
00557 S32 LLFontGL::renderUTF8(const LLString &text, const S32 offset,
00558 const F32 x, const F32 y,
00559 const LLColor4 &color,
00560 const HAlign halign, const VAlign valign,
00561 U8 style,
00562 const S32 max_chars, const S32 max_pixels,
00563 F32* right_x,
00564 BOOL use_ellipses) const
00565 {
00566 LLWString wstr = utf8str_to_wstring(text);
00567 return render(wstr, offset, x, y, color, halign, valign, style, max_chars, max_pixels, right_x, use_ellipses);
00568 }
00569
00570 S32 LLFontGL::render(const LLWString &wstr,
00571 const S32 begin_offset,
00572 const F32 x, const F32 y,
00573 const LLColor4 &color,
00574 const HAlign halign, const VAlign valign,
00575 U8 style,
00576 const S32 max_chars, S32 max_pixels,
00577 F32* right_x,
00578 BOOL use_embedded,
00579 BOOL use_ellipses) const
00580 {
00581 if(!sDisplayFont)
00582 {
00583 return wstr.length() ;
00584 }
00585
00586 LLGLEnable tex(GL_TEXTURE_2D);
00587
00588 if (wstr.empty())
00589 {
00590 return 0;
00591 }
00592
00593 S32 scaled_max_pixels = max_pixels == S32_MAX ? S32_MAX : llceil((F32)max_pixels * sScaleX);
00594
00595
00596 if (style & BOLD)
00597 {
00598 if (this == LLFontGL::sSansSerif)
00599 {
00600 return LLFontGL::sSansSerifBold->render(
00601 wstr, begin_offset,
00602 x, y,
00603 color,
00604 halign, valign,
00605 (style & ~BOLD),
00606 max_chars, max_pixels,
00607 right_x, use_embedded);
00608 }
00609 }
00610
00611 F32 drop_shadow_strength = 0.f;
00612 if (style & (DROP_SHADOW | DROP_SHADOW_SOFT))
00613 {
00614 F32 luminance;
00615 color.calcHSL(NULL, NULL, &luminance);
00616 drop_shadow_strength = clamp_rescale(luminance, 0.35f, 0.6f, 0.f, 1.f);
00617 if (luminance < 0.35f)
00618 {
00619 style = style & ~(DROP_SHADOW | DROP_SHADOW_SOFT);
00620 }
00621 }
00622
00623 gGL.pushMatrix();
00624 glLoadIdentity();
00625 gGL.translatef(floorf(sCurOrigin.mX*sScaleX), floorf(sCurOrigin.mY*sScaleY), sCurOrigin.mZ);
00626
00627
00628
00629
00630
00631
00632
00633
00634 gGL.translatef(PIXEL_CORRECTION_DISTANCE*sScaleX, 0.f, 0.f);
00635
00636
00637
00638
00639
00640
00641
00642
00643
00644
00645 LLFastTimer t(LLFastTimer::FTM_RENDER_FONTS);
00646
00647 gGL.color4fv( color.mV );
00648
00649 S32 chars_drawn = 0;
00650 S32 i;
00651 S32 length;
00652
00653 if (-1 == max_chars)
00654 {
00655 length = (S32)wstr.length() - begin_offset;
00656 }
00657 else
00658 {
00659 length = llmin((S32)wstr.length() - begin_offset, max_chars );
00660 }
00661
00662 F32 cur_x, cur_y, cur_render_x, cur_render_y;
00663
00664
00665
00666 mImageGLp->bind(0);
00667
00668 gGL.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00669
00670 cur_x = ((F32)x * sScaleX);
00671 cur_y = ((F32)y * sScaleY);
00672
00673
00674 switch (valign)
00675 {
00676 case TOP:
00677 cur_y -= mAscender;
00678 break;
00679 case BOTTOM:
00680 cur_y += mDescender;
00681 break;
00682 case VCENTER:
00683 cur_y -= ((mAscender - mDescender)/2.f);
00684 break;
00685 case BASELINE:
00686
00687 break;
00688 default:
00689 break;
00690 }
00691
00692 switch (halign)
00693 {
00694 case LEFT:
00695 break;
00696 case RIGHT:
00697 cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), 0, length) * sScaleX));
00698 break;
00699 case HCENTER:
00700 cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), 0, length) * sScaleX)) / 2;
00701 break;
00702 default:
00703 break;
00704 }
00705
00706
00707
00708
00709
00710 cur_render_y = cur_y;
00711 cur_render_x = cur_x;
00712
00713 F32 start_x = cur_x;
00714
00715 F32 inv_width = 1.f / mImageGLp->getWidth();
00716 F32 inv_height = 1.f / mImageGLp->getHeight();
00717
00718 const S32 LAST_CHARACTER = LLFont::LAST_CHAR_FULL;
00719
00720
00721 BOOL draw_ellipses = FALSE;
00722 if (use_ellipses && halign == LEFT)
00723 {
00724
00725 if (getWidthF32(wstr.c_str(), 0, max_chars) * sScaleX > scaled_max_pixels)
00726 {
00727
00728 const LLWString dots(utf8str_to_wstring(LLString("....")));
00729 scaled_max_pixels = llmax(0, scaled_max_pixels - llround(getWidthF32(dots.c_str())));
00730 draw_ellipses = TRUE;
00731 }
00732 }
00733
00734
00735 for (i = begin_offset; i < begin_offset + length; i++)
00736 {
00737 llwchar wch = wstr[i];
00738
00739
00740
00741 const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL;
00742 if (ext_data)
00743 {
00744 LLImageGL* ext_image = ext_data->mImage;
00745 const LLWString& label = ext_data->mLabel;
00746
00747 F32 ext_height = (F32)ext_image->getHeight() * sScaleY;
00748
00749 F32 ext_width = (F32)ext_image->getWidth() * sScaleX;
00750 F32 ext_advance = (EXT_X_BEARING * sScaleX) + ext_width;
00751
00752 if (!label.empty())
00753 {
00754 ext_advance += (EXT_X_BEARING + gExtCharFont->getWidthF32( label.c_str() )) * sScaleX;
00755 }
00756
00757 if (start_x + scaled_max_pixels < cur_x + ext_advance)
00758 {
00759
00760 break;
00761 }
00762
00763 ext_image->bind();
00764 const F32 ext_x = cur_render_x + (EXT_X_BEARING * sScaleX);
00765 const F32 ext_y = cur_render_y + (EXT_Y_BEARING * sScaleY + mAscender - mLineHeight);
00766
00767 LLRectf uv_rect(0.f, 1.f, 1.f, 0.f);
00768 LLRectf screen_rect(ext_x, ext_y + ext_height, ext_x + ext_width, ext_y);
00769 drawGlyph(screen_rect, uv_rect, LLColor4::white, style, drop_shadow_strength);
00770
00771 if (!label.empty())
00772 {
00773 gGL.pushMatrix();
00774
00775
00776
00777 gExtCharFont->render(label, 0,
00778 ((ext_x + (F32)ext_image->getWidth() + EXT_X_BEARING) / sScaleX),
00779 (cur_y / sScaleY),
00780 color,
00781 halign, BASELINE, NORMAL, S32_MAX, S32_MAX, NULL,
00782 TRUE );
00783 gGL.popMatrix();
00784 }
00785
00786 gGL.color4fv(color.mV);
00787
00788 chars_drawn++;
00789 cur_x += ext_advance;
00790 if (((i + 1) < length) && wstr[i+1])
00791 {
00792 cur_x += EXT_KERNING * sScaleX;
00793 }
00794 cur_render_x = cur_x;
00795
00796
00797 mImageGLp->bind();
00798 }
00799 else
00800 {
00801 if (!hasGlyph(wch))
00802 {
00803 (const_cast<LLFontGL*>(this))->addChar(wch);
00804 }
00805
00806 const LLFontGlyphInfo* fgi= getGlyphInfo(wch);
00807 if (!fgi)
00808 {
00809 llerrs << "Missing Glyph Info" << llendl;
00810 break;
00811 }
00812 if ((start_x + scaled_max_pixels) < (cur_x + fgi->mXBearing + fgi->mWidth))
00813 {
00814
00815 break;
00816 }
00817
00818
00819
00820 LLRectf uv_rect((fgi->mXBitmapOffset - PAD_AMT) * inv_width,
00821 (fgi->mYBitmapOffset + fgi->mHeight + PAD_AMT) * inv_height,
00822 (fgi->mXBitmapOffset + fgi->mWidth + PAD_AMT) * inv_width,
00823 (fgi->mYBitmapOffset - PAD_AMT) * inv_height);
00824 LLRectf screen_rect(cur_render_x + (F32)fgi->mXBearing - PAD_AMT,
00825 cur_render_y + (F32)fgi->mYBearing + PAD_AMT,
00826 cur_render_x + (F32)fgi->mXBearing + (F32)fgi->mWidth + PAD_AMT,
00827 cur_render_y + (F32)fgi->mYBearing - (F32)fgi->mHeight - PAD_AMT);
00828
00829 drawGlyph(screen_rect, uv_rect, color, style, drop_shadow_strength);
00830
00831 chars_drawn++;
00832 cur_x += fgi->mXAdvance;
00833 cur_y += fgi->mYAdvance;
00834
00835 llwchar next_char = wstr[i+1];
00836 if (next_char && (next_char < LAST_CHARACTER))
00837 {
00838
00839 if (!hasGlyph(next_char))
00840 {
00841 (const_cast<LLFontGL*>(this))->addChar(next_char);
00842 }
00843 cur_x += getXKerning(wch, next_char);
00844 }
00845
00846
00847
00848
00849
00850 cur_x = (F32)llfloor(cur_x + 0.5f);
00851
00852
00853 cur_render_x = cur_x;
00854 cur_render_y = cur_y;
00855 }
00856 }
00857
00858 if (right_x)
00859 {
00860 *right_x = cur_x / sScaleX;
00861 }
00862
00863 if (style & UNDERLINE)
00864 {
00865 LLGLSNoTexture no_texture;
00866 gGL.begin(LLVertexBuffer::LINES);
00867 gGL.vertex2f(start_x, cur_y - (mDescender));
00868 gGL.vertex2f(cur_x, cur_y - (mDescender));
00869 gGL.end();
00870 }
00871
00872
00873 if (draw_ellipses)
00874 {
00875
00876
00877 gGL.pushMatrix();
00878
00879
00880
00881 renderUTF8("...",
00882 0,
00883 cur_x / sScaleX, (F32)y,
00884 color,
00885 LEFT, valign,
00886 style,
00887 S32_MAX, max_pixels,
00888 right_x,
00889 FALSE);
00890 gGL.popMatrix();
00891 }
00892
00893 gGL.popMatrix();
00894
00895 return chars_drawn;
00896 }
00897
00898
00899 LLImageGL *LLFontGL::getImageGL() const
00900 {
00901 return mImageGLp;
00902 }
00903
00904 S32 LLFontGL::getWidth(const LLString& utf8text) const
00905 {
00906 LLWString wtext = utf8str_to_wstring(utf8text);
00907 return getWidth(wtext.c_str(), 0, S32_MAX);
00908 }
00909
00910 S32 LLFontGL::getWidth(const llwchar* wchars) const
00911 {
00912 return getWidth(wchars, 0, S32_MAX);
00913 }
00914
00915 S32 LLFontGL::getWidth(const LLString& utf8text, const S32 begin_offset, const S32 max_chars) const
00916 {
00917 LLWString wtext = utf8str_to_wstring(utf8text);
00918 return getWidth(wtext.c_str(), begin_offset, max_chars);
00919 }
00920
00921 S32 LLFontGL::getWidth(const llwchar* wchars, const S32 begin_offset, const S32 max_chars, BOOL use_embedded) const
00922 {
00923 F32 width = getWidthF32(wchars, begin_offset, max_chars, use_embedded);
00924 return llround(width);
00925 }
00926
00927 F32 LLFontGL::getWidthF32(const LLString& utf8text) const
00928 {
00929 LLWString wtext = utf8str_to_wstring(utf8text);
00930 return getWidthF32(wtext.c_str(), 0, S32_MAX);
00931 }
00932
00933 F32 LLFontGL::getWidthF32(const llwchar* wchars) const
00934 {
00935 return getWidthF32(wchars, 0, S32_MAX);
00936 }
00937
00938 F32 LLFontGL::getWidthF32(const LLString& utf8text, const S32 begin_offset, const S32 max_chars ) const
00939 {
00940 LLWString wtext = utf8str_to_wstring(utf8text);
00941 return getWidthF32(wtext.c_str(), begin_offset, max_chars);
00942 }
00943
00944 F32 LLFontGL::getWidthF32(const llwchar* wchars, const S32 begin_offset, const S32 max_chars, BOOL use_embedded) const
00945 {
00946 const S32 LAST_CHARACTER = LLFont::LAST_CHAR_FULL;
00947
00948 F32 cur_x = 0;
00949 const S32 max_index = begin_offset + max_chars;
00950 for (S32 i = begin_offset; i < max_index; i++)
00951 {
00952 const llwchar wch = wchars[i];
00953 if (wch == 0)
00954 {
00955 break;
00956 }
00957 const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL;
00958 if (ext_data)
00959 {
00960
00961 cur_x += getEmbeddedCharAdvance(ext_data);
00962
00963 if( ((i+1) < max_chars) && (i+1 < max_index))
00964 {
00965 cur_x += EXT_KERNING * sScaleX;
00966 }
00967 }
00968 else
00969 {
00970 cur_x += getXAdvance(wch);
00971 llwchar next_char = wchars[i+1];
00972
00973 if (((i + 1) < max_chars)
00974 && next_char
00975 && (next_char < LAST_CHARACTER))
00976 {
00977
00978 cur_x += getXKerning(wch, next_char);
00979 }
00980 }
00981
00982 cur_x = (F32)llfloor(cur_x + 0.5f);
00983 }
00984
00985 return cur_x / sScaleX;
00986 }
00987
00988
00989
00990
00991 S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_chars,
00992 BOOL end_on_word_boundary, const BOOL use_embedded,
00993 F32* drawn_pixels) const
00994 {
00995 if (!wchars || !wchars[0] || max_chars == 0)
00996 {
00997 return 0;
00998 }
00999
01000 llassert(max_pixels >= 0.f);
01001 llassert(max_chars >= 0);
01002
01003 BOOL clip = FALSE;
01004 F32 cur_x = 0;
01005 F32 drawn_x = 0;
01006
01007 S32 start_of_last_word = 0;
01008 BOOL in_word = FALSE;
01009
01010 F32 scaled_max_pixels = (F32)llceil(max_pixels * sScaleX);
01011
01012 S32 i;
01013 for (i=0; (i < max_chars); i++)
01014 {
01015 llwchar wch = wchars[i];
01016
01017 if(wch == 0)
01018 {
01019
01020 break;
01021 }
01022
01023 const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL;
01024 if (ext_data)
01025 {
01026 if (in_word)
01027 {
01028 in_word = FALSE;
01029 }
01030 else
01031 {
01032 start_of_last_word = i;
01033 }
01034 cur_x += getEmbeddedCharAdvance(ext_data);
01035
01036 if (scaled_max_pixels < cur_x)
01037 {
01038 clip = TRUE;
01039 break;
01040 }
01041
01042 if (((i+1) < max_chars) && wchars[i+1])
01043 {
01044 cur_x += EXT_KERNING * sScaleX;
01045 }
01046
01047 if( scaled_max_pixels < cur_x )
01048 {
01049 clip = TRUE;
01050 break;
01051 }
01052 }
01053 else
01054 {
01055 if (in_word)
01056 {
01057 if (iswspace(wch))
01058 {
01059 in_word = FALSE;
01060 }
01061 }
01062 else
01063 {
01064 start_of_last_word = i;
01065 if (!iswspace(wch))
01066 {
01067 in_word = TRUE;
01068 }
01069 }
01070
01071 cur_x += getXAdvance(wch);
01072
01073 if (scaled_max_pixels < cur_x)
01074 {
01075 clip = TRUE;
01076 break;
01077 }
01078
01079 if (((i+1) < max_chars) && wchars[i+1])
01080 {
01081
01082 cur_x += getXKerning(wch, wchars[i+1]);
01083 }
01084 }
01085
01086 cur_x = (F32)llfloor(cur_x + 0.5f);
01087 drawn_x = cur_x;
01088 }
01089
01090 if( clip && end_on_word_boundary && (start_of_last_word != 0) )
01091 {
01092 i = start_of_last_word;
01093 }
01094 if (drawn_pixels)
01095 {
01096 *drawn_pixels = drawn_x;
01097 }
01098 return i;
01099 }
01100
01101
01102 S32 LLFontGL::firstDrawableChar(const llwchar* wchars, F32 max_pixels, S32 text_len, S32 start_pos, S32 max_chars) const
01103 {
01104 if (!wchars || !wchars[0] || max_chars == 0)
01105 {
01106 return 0;
01107 }
01108
01109 F32 total_width = 0.0;
01110 S32 drawable_chars = 0;
01111
01112 F32 scaled_max_pixels = max_pixels * sScaleX;
01113
01114 S32 start = llmin(start_pos, text_len - 1);
01115 for (S32 i = start; i >= 0; i--)
01116 {
01117 llwchar wch = wchars[i];
01118
01119 const embedded_data_t* ext_data = getEmbeddedCharData(wch);
01120 if (ext_data)
01121 {
01122 F32 char_width = getEmbeddedCharAdvance(ext_data);
01123
01124 if( scaled_max_pixels < (total_width + char_width) )
01125 {
01126 break;
01127 }
01128
01129 total_width += char_width;
01130
01131 drawable_chars++;
01132 if( max_chars >= 0 && drawable_chars >= max_chars )
01133 {
01134 break;
01135 }
01136
01137 if ( i > 0 )
01138 {
01139 total_width += EXT_KERNING * sScaleX;
01140 }
01141
01142
01143 total_width = (F32)llfloor(total_width + 0.5f);
01144 }
01145 else
01146 {
01147 F32 char_width = getXAdvance(wch);
01148 if( scaled_max_pixels < (total_width + char_width) )
01149 {
01150 break;
01151 }
01152
01153 total_width += char_width;
01154
01155 drawable_chars++;
01156 if( max_chars >= 0 && drawable_chars >= max_chars )
01157 {
01158 break;
01159 }
01160
01161 if ( i > 0 )
01162 {
01163
01164 total_width += getXKerning(wchars[i-1], wch);
01165 }
01166
01167
01168 total_width = (F32)llfloor(total_width + 0.5f);
01169 }
01170 }
01171
01172 return text_len - drawable_chars;
01173 }
01174
01175
01176 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
01177 {
01178 if (!wchars || !wchars[0] || max_chars == 0)
01179 {
01180 return 0;
01181 }
01182
01183 F32 cur_x = 0;
01184 S32 pos = 0;
01185
01186 target_x *= sScaleX;
01187
01188
01189 const S32 max_index = begin_offset + llmin(S32_MAX - begin_offset, max_chars);
01190
01191 F32 scaled_max_pixels = max_pixels * sScaleX;
01192
01193 for (S32 i = begin_offset; (i < max_index); i++)
01194 {
01195 llwchar wch = wchars[i];
01196 if (!wch)
01197 {
01198 break;
01199 }
01200 const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL;
01201 if (ext_data)
01202 {
01203 F32 ext_advance = getEmbeddedCharAdvance(ext_data);
01204
01205 if (round)
01206 {
01207
01208
01209 if (target_x < cur_x + ext_advance/2)
01210 {
01211 break;
01212 }
01213 }
01214 else
01215 {
01216 if (target_x < cur_x + ext_advance)
01217 {
01218 break;
01219 }
01220 }
01221
01222 if (scaled_max_pixels < cur_x + ext_advance)
01223 {
01224 break;
01225 }
01226
01227 pos++;
01228 cur_x += ext_advance;
01229
01230 if (((i + 1) < max_index)
01231 && (wchars[(i + 1)]))
01232 {
01233 cur_x += EXT_KERNING * sScaleX;
01234 }
01235
01236 cur_x = (F32)llfloor(cur_x + 0.5f);
01237 }
01238 else
01239 {
01240 F32 char_width = getXAdvance(wch);
01241
01242 if (round)
01243 {
01244
01245
01246 if (target_x < cur_x + char_width*0.5f)
01247 {
01248 break;
01249 }
01250 }
01251 else if (target_x < cur_x + char_width)
01252 {
01253 break;
01254 }
01255
01256 if (scaled_max_pixels < cur_x + char_width)
01257 {
01258 break;
01259 }
01260
01261 pos++;
01262 cur_x += char_width;
01263
01264 if (((i + 1) < max_index)
01265 && (wchars[(i + 1)]))
01266 {
01267 llwchar next_char = wchars[i + 1];
01268
01269 cur_x += getXKerning(wch, next_char);
01270 }
01271
01272
01273 cur_x = (F32)llfloor(cur_x + 0.5f);
01274 }
01275 }
01276
01277 return pos;
01278 }
01279
01280
01281 const LLFontGL::embedded_data_t* LLFontGL::getEmbeddedCharData(const llwchar wch) const
01282 {
01283
01284 embedded_map_t::const_iterator iter = mEmbeddedChars.find(wch);
01285 if (iter != mEmbeddedChars.end())
01286 {
01287 return iter->second;
01288 }
01289 return NULL;
01290 }
01291
01292
01293 F32 LLFontGL::getEmbeddedCharAdvance(const embedded_data_t* ext_data) const
01294 {
01295 const LLWString& label = ext_data->mLabel;
01296 LLImageGL* ext_image = ext_data->mImage;
01297
01298 F32 ext_width = (F32)ext_image->getWidth();
01299 if( !label.empty() )
01300 {
01301 ext_width += (EXT_X_BEARING + gExtCharFont->getWidthF32(label.c_str())) * sScaleX;
01302 }
01303
01304 return (EXT_X_BEARING * sScaleX) + ext_width;
01305 }
01306
01307
01308 void LLFontGL::clearEmbeddedChars()
01309 {
01310 for_each(mEmbeddedChars.begin(), mEmbeddedChars.end(), DeletePairedPointer());
01311 mEmbeddedChars.clear();
01312 }
01313
01314 void LLFontGL::addEmbeddedChar( llwchar wc, LLImageGL* image, const LLString& label )
01315 {
01316 LLWString wlabel = utf8str_to_wstring(label);
01317 addEmbeddedChar(wc, image, wlabel);
01318 }
01319
01320 void LLFontGL::addEmbeddedChar( llwchar wc, LLImageGL* image, const LLWString& wlabel )
01321 {
01322 embedded_data_t* ext_data = new embedded_data_t(image, wlabel);
01323 mEmbeddedChars[wc] = ext_data;
01324 }
01325
01326 void LLFontGL::removeEmbeddedChar( llwchar wc )
01327 {
01328 embedded_map_t::iterator iter = mEmbeddedChars.find(wc);
01329 if (iter != mEmbeddedChars.end())
01330 {
01331 delete iter->second;
01332 mEmbeddedChars.erase(wc);
01333 }
01334 }
01335
01336
01337 void LLFontGL::renderQuad(const LLRectf& screen_rect, const LLRectf& uv_rect, F32 slant_amt) const
01338 {
01339 gGL.texCoord2f(uv_rect.mRight, uv_rect.mTop);
01340 gGL.vertex2f(llfont_round_x(screen_rect.mRight),
01341 llfont_round_y(screen_rect.mTop));
01342
01343 gGL.texCoord2f(uv_rect.mLeft, uv_rect.mTop);
01344 gGL.vertex2f(llfont_round_x(screen_rect.mLeft),
01345 llfont_round_y(screen_rect.mTop));
01346
01347 gGL.texCoord2f(uv_rect.mLeft, uv_rect.mBottom);
01348 gGL.vertex2f(llfont_round_x(screen_rect.mLeft + slant_amt),
01349 llfont_round_y(screen_rect.mBottom));
01350
01351 gGL.texCoord2f(uv_rect.mRight, uv_rect.mBottom);
01352 gGL.vertex2f(llfont_round_x(screen_rect.mRight + slant_amt),
01353 llfont_round_y(screen_rect.mBottom));
01354 }
01355
01356 void LLFontGL::drawGlyph(const LLRectf& screen_rect, const LLRectf& uv_rect, const LLColor4& color, U8 style, F32 drop_shadow_strength) const
01357 {
01358 F32 slant_offset;
01359 slant_offset = ((style & ITALIC) ? ( -mAscender * 0.2f) : 0.f);
01360
01361 gGL.begin(LLVertexBuffer::QUADS);
01362 {
01363
01364
01365 if (style & BOLD)
01366 {
01367 gGL.color4fv(color.mV);
01368 for (S32 pass = 0; pass < 2; pass++)
01369 {
01370 LLRectf screen_rect_offset = screen_rect;
01371
01372 screen_rect_offset.translate((F32)(pass * BOLD_OFFSET), 0.f);
01373 renderQuad(screen_rect_offset, uv_rect, slant_offset);
01374 }
01375 }
01376 else if (style & DROP_SHADOW_SOFT)
01377 {
01378 LLColor4 shadow_color = LLFontGL::sShadowColor;
01379 shadow_color.mV[VALPHA] = color.mV[VALPHA] * drop_shadow_strength * DROP_SHADOW_SOFT_STRENGTH;
01380 gGL.color4fv(shadow_color.mV);
01381 for (S32 pass = 0; pass < 5; pass++)
01382 {
01383 LLRectf screen_rect_offset = screen_rect;
01384
01385 switch(pass)
01386 {
01387 case 0:
01388 screen_rect_offset.translate(-1.f, -1.f);
01389 break;
01390 case 1:
01391 screen_rect_offset.translate(1.f, -1.f);
01392 break;
01393 case 2:
01394 screen_rect_offset.translate(1.f, 1.f);
01395 break;
01396 case 3:
01397 screen_rect_offset.translate(-1.f, 1.f);
01398 break;
01399 case 4:
01400 screen_rect_offset.translate(0, -2.f);
01401 break;
01402 }
01403
01404 renderQuad(screen_rect_offset, uv_rect, slant_offset);
01405 }
01406 gGL.color4fv(color.mV);
01407 renderQuad(screen_rect, uv_rect, slant_offset);
01408 }
01409 else if (style & DROP_SHADOW)
01410 {
01411 LLColor4 shadow_color = LLFontGL::sShadowColor;
01412 shadow_color.mV[VALPHA] = color.mV[VALPHA] * drop_shadow_strength;
01413 gGL.color4fv(shadow_color.mV);
01414 LLRectf screen_rect_shadow = screen_rect;
01415 screen_rect_shadow.translate(1.f, -1.f);
01416 renderQuad(screen_rect_shadow, uv_rect, slant_offset);
01417 gGL.color4fv(color.mV);
01418 renderQuad(screen_rect, uv_rect, slant_offset);
01419 }
01420 else
01421 {
01422 gGL.color4fv(color.mV);
01423 renderQuad(screen_rect, uv_rect, slant_offset);
01424 }
01425
01426 }
01427 gGL.end();
01428 }
01429
01430
01431 LLString LLFontGL::nameFromFont(const LLFontGL* fontp)
01432 {
01433 if (fontp == sSansSerifHuge)
01434 {
01435 return LLString("SansSerifHuge");
01436 }
01437 else if (fontp == sSansSerifSmall)
01438 {
01439 return LLString("SansSerifSmall");
01440 }
01441 else if (fontp == sSansSerif)
01442 {
01443 return LLString("SansSerif");
01444 }
01445 else if (fontp == sSansSerifBig)
01446 {
01447 return LLString("SansSerifBig");
01448 }
01449 else if (fontp == sSansSerifBold)
01450 {
01451 return LLString("SansSerifBold");
01452 }
01453 else if (fontp == sMonospace)
01454 {
01455 return LLString("Monospace");
01456 }
01457 else
01458 {
01459 return LLString();
01460 }
01461 }
01462
01463
01464 LLFontGL* LLFontGL::fontFromName(const LLString& font_name)
01465 {
01466 LLFontGL* gl_font = NULL;
01467 if (font_name == "SansSerifHuge")
01468 {
01469 gl_font = LLFontGL::sSansSerifHuge;
01470 }
01471 else if (font_name == "SansSerifSmall")
01472 {
01473 gl_font = LLFontGL::sSansSerifSmall;
01474 }
01475 else if (font_name == "SansSerif")
01476 {
01477 gl_font = LLFontGL::sSansSerif;
01478 }
01479 else if (font_name == "SansSerifBig")
01480 {
01481 gl_font = LLFontGL::sSansSerifBig;
01482 }
01483 else if (font_name == "SansSerifBold")
01484 {
01485 gl_font = LLFontGL::sSansSerifBold;
01486 }
01487 else if (font_name == "Monospace")
01488 {
01489 gl_font = LLFontGL::sMonospace;
01490 }
01491 return gl_font;
01492 }
01493
01494
01495 LLString LLFontGL::nameFromHAlign(LLFontGL::HAlign align)
01496 {
01497 if (align == LEFT) return LLString("left");
01498 else if (align == RIGHT) return LLString("right");
01499 else if (align == HCENTER) return LLString("center");
01500 else return LLString();
01501 }
01502
01503
01504 LLFontGL::HAlign LLFontGL::hAlignFromName(const LLString& name)
01505 {
01506 LLFontGL::HAlign gl_hfont_align = LLFontGL::LEFT;
01507 if (name == "left")
01508 {
01509 gl_hfont_align = LLFontGL::LEFT;
01510 }
01511 else if (name == "right")
01512 {
01513 gl_hfont_align = LLFontGL::RIGHT;
01514 }
01515 else if (name == "center")
01516 {
01517 gl_hfont_align = LLFontGL::HCENTER;
01518 }
01519
01520 return gl_hfont_align;
01521 }
01522
01523
01524 LLString LLFontGL::nameFromVAlign(LLFontGL::VAlign align)
01525 {
01526 if (align == TOP) return LLString("top");
01527 else if (align == VCENTER) return LLString("center");
01528 else if (align == BASELINE) return LLString("baseline");
01529 else if (align == BOTTOM) return LLString("bottom");
01530 else return LLString();
01531 }
01532
01533
01534 LLFontGL::VAlign LLFontGL::vAlignFromName(const LLString& name)
01535 {
01536 LLFontGL::VAlign gl_vfont_align = LLFontGL::BASELINE;
01537 if (name == "top")
01538 {
01539 gl_vfont_align = LLFontGL::TOP;
01540 }
01541 else if (name == "center")
01542 {
01543 gl_vfont_align = LLFontGL::VCENTER;
01544 }
01545 else if (name == "baseline")
01546 {
01547 gl_vfont_align = LLFontGL::BASELINE;
01548 }
01549 else if (name == "bottom")
01550 {
01551 gl_vfont_align = LLFontGL::BOTTOM;
01552 }
01553
01554 return gl_vfont_align;
01555 }