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