F32 LLFontGL::getWidthF32(const llwchar* wchars, const S32 begin_offset, const S32 max_chars, BOOL use_embedded) const { if (!wchars || !wchars[0] || max_chars == 0) { return 0; } const S32 LAST_CHARACTER = LLFont::LAST_CHAR_FULL; F32 cur_x = 0; const S32 max_index = begin_offset + max_chars; for (S32 i = begin_offset; i < max_index && wchars[i] != 0; i++) { llwchar wch = wchars[i]; const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL; if (ext_data) { // Handle crappy embedded hack cur_x += getEmbeddedCharAdvance(ext_data); if( ((i+1) < max_chars) && (i+1 < max_index)) { cur_x += EXT_KERNING * sScaleX; } } else { cur_x += getXAdvance(wch); llwchar next_char = wchars[i+1]; if (((i + 1) < max_chars) && next_char && (next_char < LAST_CHARACTER)) { // Kern this puppy. cur_x += getXKerning(wch, next_char); } } // Round after kerning. cur_x = (F32)llfloor(cur_x + 0.5f); } if (cur_x == 0) { return cur_x; } return cur_x / sScaleX; }
S32 LLFontGL::firstDrawableChar(const llwchar* wchars, F32 max_pixels, S32 text_len, S32 start_pos, S32 max_chars) const { if (!wchars || !wchars[0] || max_chars == 0) { return 0; } F32 total_width = 0.0; S32 drawable_chars = 0; F32 scaled_max_pixels = max_pixels * sScaleX; S32 start = llmin(start_pos, text_len - 1); for (S32 i = start; i >= 0; i--) { llwchar wch = wchars[i]; const embedded_data_t* ext_data = getEmbeddedCharData(wch); F32 char_width = ext_data ? getEmbeddedCharAdvance(ext_data) : getXAdvance(wch); if( scaled_max_pixels < (total_width + char_width) ) { break; } total_width += char_width; drawable_chars++; if( max_chars >= 0 && drawable_chars >= max_chars ) { break; } if ( i > 0 ) { // kerning total_width += ext_data ? (EXT_KERNING * sScaleX) : getXKerning(wchars[i-1], wch); } // Round after kerning. total_width = llround(total_width); } return start_pos - drawable_chars; }
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 { if (!wchars || !wchars[0] || max_chars == 0) { return 0; } F32 cur_x = 0; S32 pos = 0; target_x *= sScaleX; // max_chars is S32_MAX by default, so make sure we don't get overflow const S32 max_index = begin_offset + llmin(S32_MAX - begin_offset, max_chars); F32 scaled_max_pixels = max_pixels * sScaleX; for (S32 i = begin_offset; (i < max_index); i++) { llwchar wch = wchars[i]; if (!wch) { break; // done } const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL; if (ext_data) { F32 ext_advance = getEmbeddedCharAdvance(ext_data); if (round) { // Note: if the mouse is on the left half of the character, the pick is to the character's left // If it's on the right half, the pick is to the right. if (target_x < cur_x + ext_advance/2) { break; } } else { if (target_x < cur_x + ext_advance) { break; } } if (scaled_max_pixels < cur_x + ext_advance) { break; } pos++; cur_x += ext_advance; if (((i + 1) < max_index) && (wchars[(i + 1)])) { cur_x += EXT_KERNING * sScaleX; } // Round after kerning. cur_x = (F32)llfloor(cur_x + 0.5f); } else { F32 char_width = getXAdvance(wch); if (round) { // Note: if the mouse is on the left half of the character, the pick is to the character's left // If it's on the right half, the pick is to the right. if (target_x < cur_x + char_width*0.5f) { break; } } else if (target_x < cur_x + char_width) { break; } if (scaled_max_pixels < cur_x + char_width) { break; } pos++; cur_x += char_width; if (((i + 1) < max_index) && (wchars[(i + 1)])) { llwchar next_char = wchars[i + 1]; // Kern this puppy. cur_x += getXKerning(wch, next_char); } // Round after kerning. cur_x = (F32)llfloor(cur_x + 0.5f); } } return pos; }
// Returns the max number of complete characters from text (up to max_chars) that can be drawn in max_pixels S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_chars, BOOL end_on_word_boundary, const BOOL use_embedded, F32* drawn_pixels) const { if (!wchars || !wchars[0] || max_chars == 0) { return 0; } llassert(max_pixels >= 0.f); llassert(max_chars >= 0); BOOL clip = FALSE; F32 cur_x = 0; F32 drawn_x = 0; S32 start_of_last_word = 0; BOOL in_word = FALSE; F32 scaled_max_pixels = (F32)llceil(max_pixels * sScaleX); S32 i; for (i=0; (i < max_chars); i++) { llwchar wch = wchars[i]; if(wch == 0) { // Null terminator. We're done. break; } const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL; if (ext_data) { if (in_word) { in_word = FALSE; } else { start_of_last_word = i; } cur_x += getEmbeddedCharAdvance(ext_data); if (scaled_max_pixels < cur_x) { clip = TRUE; break; } if (((i+1) < max_chars) && wchars[i+1]) { cur_x += EXT_KERNING * sScaleX; } if( scaled_max_pixels < cur_x ) { clip = TRUE; break; } } else { if (in_word) { if (iswspace(wch)) { in_word = FALSE; } } else { start_of_last_word = i; if (!iswspace(wch)) { in_word = TRUE; } } cur_x += getXAdvance(wch); if (scaled_max_pixels < cur_x) { clip = TRUE; break; } if (((i+1) < max_chars) && wchars[i+1]) { // Kern this puppy. cur_x += getXKerning(wch, wchars[i+1]); } } // Round after kerning. cur_x = (F32)llfloor(cur_x + 0.5f); drawn_x = cur_x; } if( clip && end_on_word_boundary && (start_of_last_word != 0) ) { i = start_of_last_word; } if (drawn_pixels) { *drawn_pixels = drawn_x; } return i; }
S32 LLFontGL::charFromPixelOffset(const LLWString& utf32text, const S32 begin_offset, F32 target_x, F32 max_pixels, S32 max_chars, BOOL round, BOOL use_embedded) const { const S32 max_index = llmin(llmax(max_chars,begin_offset + max_chars), S32(utf32text.length())); if (max_index <= 0 || begin_offset >= max_index || max_pixels <= 0.f) return 0; F32 cur_x = 0; target_x *= sScaleX; F32 scaled_max_pixels = max_pixels * sScaleX; const LLFontGlyphInfo* next_glyph = NULL; S32 pos; for (pos = begin_offset; pos < max_index; pos++) { llwchar wch = utf32text[pos]; if (!wch) { break; // done } const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL; const LLFontGlyphInfo* glyph = next_glyph; next_glyph = NULL; if(!glyph && !ext_data) { glyph = mFontFreetype->getGlyphInfo(wch); } F32 char_width = ext_data ? getEmbeddedCharAdvance(ext_data) : mFontFreetype->getXAdvance(glyph); if (round) { // Note: if the mouse is on the left half of the character, the pick is to the character's left // If it's on the right half, the pick is to the right. if (target_x < cur_x + char_width*0.5f) { break; } } else if (target_x < cur_x + char_width) { break; } if (scaled_max_pixels < cur_x + char_width) { break; } cur_x += char_width; if ((pos + 1) < max_index) { if(ext_data) { cur_x += EXT_KERNING * sScaleX; } else { next_glyph = mFontFreetype->getGlyphInfo(utf32text[pos + 1]); cur_x += mFontFreetype->getXKerning(glyph, next_glyph); } } // Round after kerning. cur_x = (F32)ll_round(cur_x); } return pos - begin_offset; }
S32 LLFontGL::firstDrawableChar(const LLWString& utf32text, F32 max_pixels, S32 start_pos, S32 max_chars) const { const S32 max_index = llmin(llmax(max_chars, start_pos + max_chars), S32(utf32text.length())); if (max_index <= 0 || start_pos >= max_index || max_pixels <= 0.f || start_pos < 0) return 0; F32 total_width = 0.0; S32 drawable_chars = 0; F32 scaled_max_pixels = max_pixels * sScaleX; S32 start = llmin(start_pos, max_index - 1); for (S32 i = start; i >= 0; i--) { llwchar wch = utf32text[i]; const embedded_data_t* ext_data = getEmbeddedCharData(wch); F32 width = 0; if(ext_data) { width = getEmbeddedCharAdvance(ext_data); } else { const LLFontGlyphInfo* fgi= mFontFreetype->getGlyphInfo(wch); // last character uses character width, since the whole character needs to be visible // other characters just use advance width = (i == start) ? (F32)(fgi->mWidth + fgi->mXBearing) // use actual width for last character : fgi->mXAdvance; // use advance for all other characters } if( scaled_max_pixels < (total_width + width) ) { break; } total_width += width; drawable_chars++; if( max_index >= 0 && drawable_chars >= max_index ) { break; } if ( i > 0 ) { // kerning total_width += ext_data ? (EXT_KERNING * sScaleX) : mFontFreetype->getXKerning(utf32text[i - 1], wch); } // Round after kerning. total_width = (F32)ll_round(total_width); } if (drawable_chars == 0) { return start_pos; // just draw last character } else { // if only 1 character is drawable, we want to return start_pos as the first character to draw // if 2 are drawable, return start_pos and character before start_pos, etc. return start_pos + 1 - drawable_chars; } }
// Returns the max number of complete characters from text (up to max_chars) that can be drawn in max_pixels S32 LLFontGL::maxDrawableChars(const LLWString& utf32text, F32 max_pixels, S32 max_chars, EWordWrapStyle end_on_word_boundary, const BOOL use_embedded, F32* drawn_pixels) const { const S32 max_index = llmin(max_chars, S32(utf32text.length())); if (max_index <= 0 || max_pixels <= 0.f) return 0; BOOL clip = FALSE; F32 cur_x = 0; F32 drawn_x = 0; S32 start_of_last_word = 0; BOOL in_word = FALSE; // avoid S32 overflow when max_pixels == S32_MAX by staying in floating point F32 scaled_max_pixels = max_pixels * sScaleX; F32 width_padding = 0.f; LLFontGlyphInfo* next_glyph = NULL; S32 i; for (i=0; (i < max_index); i++) { llwchar wch = utf32text[i]; const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL; if (ext_data) { if (in_word) { in_word = FALSE; } else { start_of_last_word = i; } cur_x += getEmbeddedCharAdvance(ext_data); if (scaled_max_pixels < cur_x) { clip = TRUE; break; } if ((i+1) < max_index) { cur_x += EXT_KERNING * sScaleX; } if( scaled_max_pixels < cur_x ) { clip = TRUE; break; } } else { if (in_word) { if (iswspace(wch)) { in_word = FALSE; } } else { start_of_last_word = i; if (!iswspace(wch)) { in_word = TRUE; } } LLFontGlyphInfo* fgi = next_glyph; next_glyph = NULL; if(!fgi) { fgi = mFontFreetype->getGlyphInfo(wch); } // account for glyphs that run beyond the starting point for the next glyphs width_padding = llmax( 0.f, // always use positive padding amount width_padding - fgi->mXAdvance, // previous padding left over after advance of current character (F32)(fgi->mWidth + fgi->mXBearing) - fgi->mXAdvance); // difference between width of this character and advance to next character cur_x += fgi->mXAdvance; // clip if current character runs past scaled_max_pixels (using width_padding) if (scaled_max_pixels < cur_x + width_padding) { clip = TRUE; break; } if ((i+1) < max_index) { // Kern this puppy. next_glyph = mFontFreetype->getGlyphInfo(utf32text[i + 1]); cur_x += mFontFreetype->getXKerning(fgi, next_glyph); } } // Round after kerning. cur_x = (F32)ll_round(cur_x); drawn_x = cur_x; } if( clip ) { switch (end_on_word_boundary) { case ONLY_WORD_BOUNDARIES: i = start_of_last_word; break; case WORD_BOUNDARY_IF_POSSIBLE: if (start_of_last_word != 0) { i = start_of_last_word; } break; default: case ANYWHERE: // do nothing break; } } if (drawn_pixels) { *drawn_pixels = drawn_x; } return i; }
F32 LLFontGL::getWidthF32(const LLWString& utf32text, const S32 begin_offset, const S32 max_chars, BOOL use_embedded) const { const S32 LAST_CHARACTER = LLFontFreetype::LAST_CHAR_FULL; const S32 max_index = llmin(llmax(max_chars, begin_offset + max_chars), S32(utf32text.length())); if (max_index <= 0 || begin_offset >= max_index) return 0; F32 cur_x = 0; const LLFontGlyphInfo* next_glyph = NULL; F32 width_padding = 0.f; for (S32 i = begin_offset; i < max_index; i++) { const llwchar wch = utf32text[i]; const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL; if (ext_data) { // Handle crappy embedded hack cur_x += getEmbeddedCharAdvance(ext_data); if(i+1 < max_index) { cur_x += EXT_KERNING * sScaleX; } } else { const LLFontGlyphInfo* fgi = next_glyph; next_glyph = NULL; if(!fgi) { fgi = mFontFreetype->getGlyphInfo(wch); } F32 advance = mFontFreetype->getXAdvance(fgi); // for the last character we want to measure the greater of its width and xadvance values // so keep track of the difference between these values for the each character we measure // so we can fix things up at the end width_padding = llmax( 0.f, // always use positive padding amount width_padding - advance, // previous padding left over after advance of current character (F32)(fgi->mWidth + fgi->mXBearing) - advance); // difference between width of this character and advance to next character cur_x += advance; if ((i + 1) < max_index) { llwchar next_char = utf32text[i+1]; if (next_char < LAST_CHARACTER) { // Kern this puppy. next_glyph = mFontFreetype->getGlyphInfo(next_char); cur_x += mFontFreetype->getXKerning(fgi, next_glyph); } } // Round after kerning. cur_x = (F32)ll_round(cur_x); } } // add in extra pixels for last character's width past its xadvance cur_x += width_padding; return cur_x / sScaleX; }
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 { if (!wchars || !wchars[0] || max_chars == 0) { return 0; } F32 cur_x = 0; target_x *= sScaleX; // max_chars is S32_MAX by default, so make sure we don't get overflow const S32 max_index = begin_offset + llmin(S32_MAX - begin_offset, max_chars); F32 scaled_max_pixels = max_pixels * sScaleX; const LLFontGlyphInfo* next_glyph = NULL; S32 pos; for (pos = begin_offset; pos < max_index; pos++) { llwchar wch = wchars[pos]; if (!wch) { break; // done } const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL; const LLFontGlyphInfo* glyph = next_glyph; next_glyph = NULL; if(!glyph && !ext_data) { glyph = mFontFreetype->getGlyphInfo(wch); } F32 char_width = ext_data ? getEmbeddedCharAdvance(ext_data) : mFontFreetype->getXAdvance(glyph); if (round) { // Note: if the mouse is on the left half of the character, the pick is to the character's left // If it's on the right half, the pick is to the right. if (target_x < cur_x + char_width*0.5f) { break; } } else if (target_x < cur_x + char_width) { break; } if (scaled_max_pixels < cur_x + char_width) { break; } cur_x += char_width; if (((pos + 1) < max_index) && (wchars[(pos + 1)])) { if(ext_data) { cur_x += EXT_KERNING * sScaleX; } else { next_glyph = mFontFreetype->getGlyphInfo(wchars[pos + 1]); cur_x += mFontFreetype->getXKerning(glyph, next_glyph); } } // Round after kerning. cur_x = (F32)llround(cur_x); } return llmin(max_chars, pos - begin_offset); }