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;
}
Beispiel #9
0
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);
}