aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llrender/llfont.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/llfont.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 '')
-rw-r--r--linden/indra/llrender/llfont.cpp627
1 files changed, 627 insertions, 0 deletions
diff --git a/linden/indra/llrender/llfont.cpp b/linden/indra/llrender/llfont.cpp
new file mode 100644
index 0000000..c64744e
--- /dev/null
+++ b/linden/indra/llrender/llfont.cpp
@@ -0,0 +1,627 @@
1/**
2 * @file llfont.cpp
3 * @brief Font library wrapper
4 *
5 * Copyright (c) 2002-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 "llfont.h"
31
32// Freetype stuff
33#if LL_LINUX // I had to do some work to avoid the system-installed FreeType headers... --ryan.
34#include "llfreetype2/freetype/ft2build.h"
35#else
36#include <ft2build.h>
37#endif
38
39// For some reason, this won't work if it's not wrapped in the ifdef
40#ifdef FT_FREETYPE_H
41#include FT_FREETYPE_H
42#endif
43
44#include "llerror.h"
45#include "llimage.h"
46//#include "llimagej2c.h"
47#include "llmath.h" // Linden math
48#include "llstring.h"
49//#include "imdebug.h"
50
51FT_Render_Mode gFontRenderMode = FT_RENDER_MODE_NORMAL;
52
53LLFontManager *gFontManagerp = NULL;
54
55FT_Library gFTLibrary = NULL;
56
57//static
58void LLFontManager::initClass()
59{
60 gFontManagerp = new LLFontManager;
61}
62
63//static
64void LLFontManager::cleanupClass()
65{
66 delete gFontManagerp;
67 gFontManagerp = NULL;
68}
69
70LLFontManager::LLFontManager()
71{
72 int error;
73 error = FT_Init_FreeType(&gFTLibrary);
74 if (error)
75 {
76 // Clean up freetype libs.
77 llerrs << "Freetype initialization failure!" << llendl;
78 FT_Done_FreeType(gFTLibrary);
79 }
80}
81
82
83LLFontManager::~LLFontManager()
84{
85 FT_Done_FreeType(gFTLibrary);
86}
87
88
89LLFontGlyphInfo::LLFontGlyphInfo(U32 index)
90{
91 mGlyphIndex = index;
92 mXBitmapOffset = 0; // Offset to the origin in the bitmap
93 mYBitmapOffset = 0; // Offset to the origin in the bitmap
94 mXBearing = 0; // Distance from baseline to left in pixels
95 mYBearing = 0; // Distance from baseline to top in pixels
96 mWidth = 0; // In pixels
97 mHeight = 0; // In pixels
98 mXAdvance = 0.f; // In pixels
99 mYAdvance = 0.f; // In pixels
100 mIsRendered = FALSE;
101}
102
103LLFontList::LLFontList()
104{
105}
106
107LLFontList::~LLFontList()
108{
109 LLFontList::iterator iter;
110 for(iter = this->begin(); iter != this->end(); iter++)
111 {
112 delete *iter;
113 // The (now dangling) pointers in the vector will be cleaned up when the vector is deleted by the superclass destructor.
114 }
115}
116void LLFontList::addAtEnd(LLFont *font)
117{
118 // Purely a convenience function
119 this->push_back(font);
120}
121
122LLFont::LLFont(LLImageRaw *imagep)
123 : mRawImagep(imagep)
124{
125 mValid = FALSE;
126 mAscender = 0.f;
127 mDescender = 0.f;
128 mLineHeight = 0.f;
129 mBitmapWidth = 0;
130 mBitmapHeight = 0;
131 mCurrentOffsetX = 1;
132 mCurrentOffsetY = 1;
133 mMaxCharWidth = 0;
134 mMaxCharHeight = 0;
135 mNumComponents = 0;
136 mFallbackFontp = NULL;
137 mIsFallback = FALSE;
138}
139
140
141LLFont::~LLFont()
142{
143 mRawImagep = NULL; // dereferences or deletes image
144
145 // Clean up freetype libs.
146 FT_Done_Face(mFTFace);
147 mFTFace = NULL;
148
149 // Delete glyph info
150 std::for_each(mCharGlyphInfoMap.begin(), mCharGlyphInfoMap.end(), DeletePairedPointer());
151}
152
153void LLFont::setRawImage(LLImageRaw *imagep)
154{
155 mRawImagep = imagep; // will delete old raw image if we have one and created it
156}
157
158// virtual
159F32 LLFont::getLineHeight() const
160{
161 return mLineHeight;
162}
163
164// virtual
165F32 LLFont::getAscenderHeight() const
166{
167 return mAscender;
168}
169
170// virtual
171F32 LLFont::getDescenderHeight() const
172{
173 return mDescender;
174}
175
176BOOL LLFont::loadFace(const std::string& filename, const F32 point_size, const F32 vert_dpi, const F32 horz_dpi, const S32 components, BOOL is_fallback)
177{
178 int error;
179 error = FT_New_Face( gFTLibrary,
180 filename.c_str(),
181 0,
182 &mFTFace );
183
184 if (error)
185 {
186 return FALSE;
187 }
188
189 mIsFallback = is_fallback;
190 mNumComponents = components;
191 F32 pixels_per_em = (point_size / 72.f)*vert_dpi; // Size in inches * dpi
192
193 error = FT_Set_Char_Size(mFTFace, /* handle to face object */
194 0, /* char_width in 1/64th of points */
195 (S32)(point_size*64), /* char_height in 1/64th of points */
196 (U32)horz_dpi, /* horizontal device resolution */
197 (U32)vert_dpi); /* vertical device resolution */
198
199 if (error)
200 {
201 // Clean up freetype libs.
202 FT_Done_Face(mFTFace);
203 return FALSE;
204 }
205
206 F32 y_max, y_min, x_max, x_min;
207 F32 ems_per_unit = 1.f/ mFTFace->units_per_EM;
208 F32 pixels_per_unit = pixels_per_em * ems_per_unit;
209
210 // Get size of bbox in pixels
211 y_max = mFTFace->bbox.yMax * pixels_per_unit;
212 y_min = mFTFace->bbox.yMin * pixels_per_unit;
213 x_max = mFTFace->bbox.xMax * pixels_per_unit;
214 x_min = mFTFace->bbox.xMin * pixels_per_unit;
215 mAscender = mFTFace->ascender * pixels_per_unit;
216 mDescender = -mFTFace->descender * pixels_per_unit;
217 mLineHeight = mFTFace->height * pixels_per_unit;
218
219 mMaxCharWidth = llround(0.5f + (x_max - x_min));
220 mMaxCharHeight = llround(0.5f + (y_max - y_min));
221
222 if (!mFTFace->charmap)
223 {
224 //llinfos << " no unicode encoding, set whatever encoding there is..." << llendl;
225 FT_Set_Charmap(mFTFace, mFTFace->charmaps[0]);
226 }
227
228 if (mRawImagep.isNull() && !mIsFallback)
229 {
230 mRawImagep = new LLImageRaw();
231 }
232
233 if (!mIsFallback)
234 {
235 // Place text into bitmap, and generate all necessary positions/
236 // offsets for the individual characters.
237
238 // calc width and height for mRawImagep (holds all characters)
239 // Guess for approximately 20*20 characters
240 S32 image_width = mMaxCharWidth * 20;
241 S32 pow_iw = 2;
242 while (pow_iw < image_width)
243 {
244 pow_iw *= 2;
245 }
246 image_width = pow_iw;
247 image_width = llmin(512, image_width); // Don't make bigger than 512x512, ever.
248 S32 image_height = image_width;
249
250 //llinfos << "Guessing texture size of " << image_width << " pixels square" << llendl;
251
252 mRawImagep->resize(image_width, image_height, components);
253
254 mBitmapWidth = image_width;
255 mBitmapHeight = image_height;
256
257 switch (components)
258 {
259 case 1:
260 mRawImagep->clear();
261 break;
262 case 2:
263 mRawImagep->clear(255, 0);
264 break;
265 }
266
267 mCurrentOffsetX = 1;
268 mCurrentOffsetY = 1;
269
270 // Add the default glyph
271 addGlyph(0, 0);
272 }
273
274 mName = filename;
275
276 return TRUE;
277}
278
279
280void LLFont::resetBitmap()
281{
282 llinfos << "Rebuilding bitmap for glyph" << llendl;
283
284 // Iterate through glyphs and clear the mIsRendered flag
285 for (char_glyph_info_map_t::iterator iter = mCharGlyphInfoMap.begin();
286 iter != mCharGlyphInfoMap.end(); ++iter)
287 {
288 iter->second->mIsRendered = FALSE;
289 }
290
291 mCurrentOffsetX = 1;
292 mCurrentOffsetY = 1;
293
294 // Add the empty glyph
295 addGlyph(0, 0);
296}
297
298LLFontGlyphInfo* LLFont::getGlyphInfo(const llwchar wch) const
299{
300 char_glyph_info_map_t::iterator iter = mCharGlyphInfoMap.find(wch);
301 if (iter != mCharGlyphInfoMap.end())
302 {
303 return iter->second;
304 }
305 return NULL;
306}
307
308
309BOOL LLFont::hasGlyph(const llwchar wch) const
310{
311 llassert(!mIsFallback);
312 const LLFontGlyphInfo* gi = getGlyphInfo(wch);
313 if (gi && gi->mIsRendered)
314 {
315 return TRUE;
316 }
317 else
318 {
319 return FALSE;
320 }
321}
322
323BOOL LLFont::addChar(const llwchar wch)
324{
325 llassert(!mIsFallback);
326 //lldebugs << "Adding new glyph for " << wch << " to font" << llendl;
327
328 FT_UInt glyph_index;
329
330 // Initialize char to glyph map
331 glyph_index = FT_Get_Char_Index(mFTFace, wch);
332 if (glyph_index == 0)
333 {
334 // Try looking it up in the backup Unicode font
335 if (mFallbackFontp)
336 {
337 //llinfos << "Trying to add glyph from fallback font!" << llendl
338 LLFontList::iterator iter;
339 for(iter = mFallbackFontp->begin(); iter != mFallbackFontp->end(); iter++)
340 {
341 glyph_index = FT_Get_Char_Index((*iter)->mFTFace, wch);
342 if (glyph_index)
343 {
344 addGlyphFromFont(*iter, wch, glyph_index);
345 return TRUE;
346 }
347 }
348 }
349 }
350
351 char_glyph_info_map_t::iterator iter = mCharGlyphInfoMap.find(wch);
352 if (iter == mCharGlyphInfoMap.end() || !(iter->second->mIsRendered))
353 {
354 BOOL result = addGlyph(wch, glyph_index);
355 //imdebug("luma b=8 w=%d h=%d t=%s %p", mRawImagep->getWidth(), mRawImagep->getHeight(), mName.c_str(), mRawImagep->getData());
356 return result;
357 }
358 return FALSE;
359}
360
361void LLFont::insertGlyphInfo(llwchar wch, LLFontGlyphInfo* gi) const
362{
363 char_glyph_info_map_t::iterator iter = mCharGlyphInfoMap.find(wch);
364 if (iter != mCharGlyphInfoMap.end())
365 {
366 delete iter->second;
367 iter->second = gi;
368 }
369 else
370 {
371 mCharGlyphInfoMap[wch] = gi;
372 }
373}
374
375BOOL LLFont::addGlyphFromFont(LLFont *fontp, const llwchar wch, const U32 glyph_index)
376{
377 llassert(!mIsFallback);
378 fontp->renderGlyph(glyph_index);
379 S32 width = fontp->mFTFace->glyph->bitmap.width;
380 S32 height = fontp->mFTFace->glyph->bitmap.rows;
381
382 if ((mCurrentOffsetX + width + 1) > mRawImagep->getWidth())
383 {
384 if ((mCurrentOffsetY + 2*mMaxCharHeight + 2) > mBitmapHeight)
385 {
386 // We're out of space in this texture - clear it an all of the glyphs
387 // and start over again. Easier than LRU and should work just as well
388 // (just slightly slower on the rebuild). As long as the texture has
389 // enough room to hold all glyphs needed for a particular frame this
390 // shouldn't be too slow.
391
392 resetBitmap();
393
394 // Need to rerender the glyph, as it's been overwritten by the default glyph.
395 fontp->renderGlyph(glyph_index);
396 width = fontp->mFTFace->glyph->bitmap.width;
397 height = fontp->mFTFace->glyph->bitmap.rows;
398
399 // We should have a reasonable offset for x and y, no need to check that it's in range
400 }
401 else
402 {
403 mCurrentOffsetX = 1;
404 mCurrentOffsetY += mMaxCharHeight + 1;
405 }
406 }
407
408 LLFontGlyphInfo* gi = new LLFontGlyphInfo(glyph_index);
409 gi->mXBitmapOffset = mCurrentOffsetX;
410 gi->mYBitmapOffset = mCurrentOffsetY;
411 gi->mWidth = width;
412 gi->mHeight = height;
413 gi->mXBearing = fontp->mFTFace->glyph->bitmap_left;
414 gi->mYBearing = fontp->mFTFace->glyph->bitmap_top;
415 // Convert these from 26.6 units to float pixels.
416 gi->mXAdvance = fontp->mFTFace->glyph->advance.x / 64.f;
417 gi->mYAdvance = fontp->mFTFace->glyph->advance.y / 64.f;
418 gi->mIsRendered = TRUE;
419
420 insertGlyphInfo(wch, gi);
421
422 llassert(fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO
423 || fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY);
424
425 if (fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO
426 || fontp->mFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY)
427 {
428 U8 *buffer_data = fontp->mFTFace->glyph->bitmap.buffer;
429 S32 buffer_row_stride = fontp->mFTFace->glyph->bitmap.pitch;
430 U8 *tmp_graydata = NULL;
431
432 if (fontp->mFTFace->glyph->bitmap.pixel_mode
433 == FT_PIXEL_MODE_MONO)
434 {
435 // need to expand 1-bit bitmap to 8-bit graymap.
436 tmp_graydata = new U8[width * height];
437 S32 xpos, ypos;
438 for (ypos = 0; ypos < height; ++ypos)
439 {
440 S32 bm_row_offset = buffer_row_stride * ypos;
441 for (xpos = 0; xpos < width; ++xpos)
442 {
443 U32 bm_col_offsetbyte = xpos / 8;
444 U32 bm_col_offsetbit = 7 - (xpos % 8);
445 U32 bit =
446 !!(buffer_data[bm_row_offset
447 + bm_col_offsetbyte
448 ] & (1 << bm_col_offsetbit) );
449 tmp_graydata[width*ypos + xpos] =
450 255 * bit;
451 }
452 }
453 // use newly-built graymap.
454 buffer_data = tmp_graydata;
455 buffer_row_stride = width;
456 }
457
458 switch (mNumComponents)
459 {
460 case 1:
461 mRawImagep->setSubImage(mCurrentOffsetX,
462 mCurrentOffsetY,
463 width,
464 height,
465 buffer_data,
466 buffer_row_stride,
467 TRUE);
468 break;
469 case 2:
470 setSubImageLuminanceAlpha(mCurrentOffsetX,
471 mCurrentOffsetY,
472 width,
473 height,
474 buffer_data,
475 buffer_row_stride);
476 break;
477 default:
478 break;
479 }
480
481 if (tmp_graydata)
482 delete[] tmp_graydata;
483 } else {
484 // we don't know how to handle this pixel format from FreeType;
485 // omit it from the font-image.
486 }
487
488 mCurrentOffsetX += width + 1;
489 return TRUE;
490}
491
492BOOL LLFont::addGlyph(const llwchar wch, const U32 glyph_index)
493{
494 return addGlyphFromFont(this, wch, glyph_index);
495}
496
497
498F32 LLFont::getXAdvance(const llwchar wch) const
499{
500 llassert(!mIsFallback);
501 U32 glyph_index;
502
503 // Return existing info only if it is current
504 LLFontGlyphInfo* gi = getGlyphInfo(wch);
505 if (gi && gi->mIsRendered)
506 {
507 return gi->mXAdvance;
508 }
509
510 const LLFont* fontp = this;
511
512 // Initialize char to glyph map
513 glyph_index = FT_Get_Char_Index(mFTFace, wch);
514 if (glyph_index == 0 && mFallbackFontp)
515 {
516 LLFontList::iterator iter;
517 for(iter = mFallbackFontp->begin(); (iter != mFallbackFontp->end()) && (glyph_index == 0); iter++)
518 {
519 glyph_index = FT_Get_Char_Index((*iter)->mFTFace, wch);
520 if(glyph_index)
521 {
522 fontp = *iter;
523 }
524 }
525 }
526
527 if (glyph_index)
528 {
529 // This font has this glyph
530 (const_cast<LLFont *>(fontp))->renderGlyph(glyph_index);
531
532 // Create the entry if it's not there
533 char_glyph_info_map_t::iterator iter2 = mCharGlyphInfoMap.find(wch);
534 if (iter2 == mCharGlyphInfoMap.end())
535 {
536 gi = new LLFontGlyphInfo(glyph_index);
537 insertGlyphInfo(wch, gi);
538 }
539 else
540 {
541 gi = iter2->second;
542 }
543
544 gi->mWidth = fontp->mFTFace->glyph->bitmap.width;
545 gi->mHeight = fontp->mFTFace->glyph->bitmap.rows;
546
547 // Convert these from 26.6 units to float pixels.
548 gi->mXAdvance = fontp->mFTFace->glyph->advance.x / 64.f;
549 gi->mYAdvance = fontp->mFTFace->glyph->advance.y / 64.f;
550 return gi->mXAdvance;
551 }
552 else
553 {
554 gi = get_if_there(mCharGlyphInfoMap, (llwchar)0, (LLFontGlyphInfo*)NULL);
555 if (gi)
556 {
557 return gi->mXAdvance;
558 }
559 }
560
561 // Last ditch fallback - no glyphs defined at all.
562 return (F32)mMaxCharWidth;
563}
564
565
566void LLFont::renderGlyph(const U32 glyph_index)
567{
568 int error = FT_Load_Glyph(mFTFace, glyph_index, FT_LOAD_DEFAULT );
569 llassert(!error);
570
571 error = FT_Render_Glyph(mFTFace->glyph, gFontRenderMode);
572 llassert(!error);
573}
574
575
576F32 LLFont::getXKerning(const llwchar char_left, const llwchar char_right) const
577{
578 llassert(!mIsFallback);
579 LLFontGlyphInfo* left_glyph_info = get_if_there(mCharGlyphInfoMap, char_left, (LLFontGlyphInfo*)NULL);
580 U32 left_glyph = left_glyph_info ? left_glyph_info->mGlyphIndex : 0;
581 // Kern this puppy.
582 LLFontGlyphInfo* right_glyph_info = get_if_there(mCharGlyphInfoMap, char_right, (LLFontGlyphInfo*)NULL);
583 U32 right_glyph = right_glyph_info ? right_glyph_info->mGlyphIndex : 0;
584
585 FT_Vector delta;
586
587 llverify(!FT_Get_Kerning(mFTFace, left_glyph, right_glyph, ft_kerning_unfitted, &delta));
588
589 return delta.x*(1.f/64.f);
590}
591
592void LLFont::setSubImageLuminanceAlpha(const U32 x,
593 const U32 y,
594 const U32 width,
595 const U32 height,
596 const U8 *data,
597 S32 stride)
598{
599 llassert(!mIsFallback);
600 llassert(mRawImagep->getComponents() == 2);
601
602 U8 *target = mRawImagep->getData();
603
604 if (!data)
605 {
606 return;
607 }
608
609 if (0 == stride)
610 stride = width;
611
612 U32 i, j;
613 U32 to_offset;
614 U32 from_offset;
615 U32 target_width = mRawImagep->getWidth();
616 for (i = 0; i < height; i++)
617 {
618 to_offset = (y + i)*target_width + x;
619 from_offset = (height - 1 - i)*stride;
620 for (j = 0; j < width; j++)
621 {
622 *(target + to_offset*2 + 1) = *(data + from_offset);
623 to_offset++;
624 from_offset++;
625 }
626 }
627}