From f1042b3f6f419703358b4e48a12c2ec24094abf5 Mon Sep 17 00:00:00 2001 From: Jeremie Roy Date: Wed, 22 May 2013 17:16:11 +0200 Subject: [PATCH] add Text metrics helper --- examples/common/font/text_metrics.cpp | 303 ++++++++++++++++++++++++++ examples/common/font/text_metrics.h | 67 ++++++ 2 files changed, 370 insertions(+) create mode 100644 examples/common/font/text_metrics.cpp create mode 100644 examples/common/font/text_metrics.h diff --git a/examples/common/font/text_metrics.cpp b/examples/common/font/text_metrics.cpp new file mode 100644 index 000000000..537978f9d --- /dev/null +++ b/examples/common/font/text_metrics.cpp @@ -0,0 +1,303 @@ +/* + * Copyright 2013 Jeremie Roy. All rights reserved. + * License: http://www.opensource.org/licenses/BSD-2-Clause + */ +#include "text_metrics.h" +#include // wcslen +#include "utf8.h" + +TextMetrics::TextMetrics(FontManager* _fontManager): m_fontManager(_fontManager), m_width(0), m_height(0), m_x(0), m_lineHeight(0), m_lineGap(0) +{ +} + +void TextMetrics::appendText(FontHandle _fontHandle, const char* _string) +{ + GlyphInfo glyph; + const FontInfo& font = m_fontManager->getFontInfo(_fontHandle); + + if(font.lineGap > m_lineGap) + { + m_lineGap = font.lineGap; + } + + if( (font.ascender - font.descender) > m_lineHeight) + { + m_height -= m_lineHeight; + m_lineHeight = font.ascender - font.descender; + m_height += m_lineHeight; + } + + CodePoint codepoint = 0; + uint32_t state = 0; + + for (; *_string; ++_string) + { + if (!utf8_decode(&state, (uint32_t*)&codepoint, *_string) ) + { + if (m_fontManager->getGlyphInfo(_fontHandle, codepoint, glyph) ) + { + if (codepoint == L'\n') + { + m_height += m_lineGap + font.ascender - font.descender; + m_lineGap = font.lineGap; + m_lineHeight = font.ascender - font.descender; + m_x = 0; + break; + } + //TODO handle kerning + m_x += glyph.advance_x; + if(m_x > m_width) + { + m_width = m_x; + } + } + else + { + BX_CHECK(false, "Glyph not found"); + } + } + } + + BX_CHECK(state == UTF8_ACCEPT, "The string is not well-formed"); +} + +void TextMetrics::appendText(FontHandle _fontHandle, const wchar_t* _string) +{ + GlyphInfo glyph; + const FontInfo& font = m_fontManager->getFontInfo(_fontHandle); + + if(font.lineGap > m_lineGap) + { + m_lineGap = font.lineGap; + } + + if( (font.ascender - font.descender) > m_lineHeight) + { + m_height -= m_lineHeight; + m_lineHeight = font.ascender - font.descender; + m_height += m_lineHeight; + } + + for (uint32_t ii = 0, end = wcslen(_string); ii < end; ++ii) + { + uint32_t codepoint = _string[ii]; + if (m_fontManager->getGlyphInfo(_fontHandle, codepoint, glyph) ) + { + if (codepoint == L'\n') + { + m_height += m_lineGap + font.ascender - font.descender; + m_lineGap = font.lineGap; + m_lineHeight = font.ascender - font.descender; + m_x = 0; + break; + } + //TODO handle kerning + m_x += glyph.advance_x; + if(m_x > m_width) + { + m_width = m_x; + } + } + else + { + BX_CHECK(false, "Glyph not found"); + } + } +} + +TextLineMetrics::TextLineMetrics(FontManager* _fontManager, FontHandle _fontHandle ) +{ + const FontInfo& font = _fontManager->getFontInfo(_fontHandle); + m_lineHeight = font.ascender - font.descender + font.lineGap; +} + +uint32_t TextLineMetrics::getLineCount(const char* _string) const +{ + CodePoint codepoint = 0; + uint32_t state = 0; + uint32_t lineCount = 1; + for (; *_string; ++_string) + { + if(utf8_decode(&state, (uint32_t*)&codepoint, *_string) == UTF8_ACCEPT) + { + if(codepoint == L'\n') + { + ++lineCount; + } + } + } + BX_CHECK(state == UTF8_ACCEPT, "The string is not well-formed"); + return lineCount; +} + +uint32_t TextLineMetrics::getLineCount(const wchar_t* _string) const +{ + uint32_t lineCount = 1; + for ( ;*_string != L'\0'; ++_string) + { + if(*_string == L'\n') + { + ++lineCount; + } + } + return lineCount; +} + + +void TextLineMetrics::getSubText(const char* _string, uint32_t _firstLine, uint32_t _lastLine, const char*& _begin, const char*& _end) +{ + CodePoint codepoint = 0; + uint32_t state = 0; + // y is bottom of a text line + uint32_t currentLine = 0; + while(*_string && (currentLine < _firstLine)) + { + for (; *_string; ++_string) + { + if(utf8_decode(&state, (uint32_t*)&codepoint, *_string) == UTF8_ACCEPT) + { + if(codepoint == L'\n') + { + ++currentLine; + ++_string; + break; + } + } + } + } + BX_CHECK(state == UTF8_ACCEPT, "The string is not well-formed"); + _begin = _string; + + while((*_string) && (currentLine < _lastLine) ) + { + for (; *_string; ++_string) + { + if(utf8_decode(&state, (uint32_t*)&codepoint, *_string) == UTF8_ACCEPT) + { + if(codepoint == L'\n') + { + ++currentLine; + ++_string; + break; + } + } + } + } + BX_CHECK(state == UTF8_ACCEPT, "The string is not well-formed"); + _end = _string; +} + +void TextLineMetrics::getSubText(const wchar_t* _string, uint32_t _firstLine, uint32_t _lastLine, const wchar_t*& _begin, const wchar_t*& _end) +{ + uint32_t currentLine = 0; + while((*_string != L'\0') && (currentLine < _firstLine)) + { + for ( ;*_string != L'\0'; ++_string) + { + if(*_string == L'\n') + { + ++currentLine; + ++_string; + break; + } + } + } + _begin = _string; + + while((*_string != L'\0') && (currentLine < _lastLine) ) + { + for ( ;*_string != L'\0'; ++_string) + { + if(*_string == L'\n') + { + ++currentLine; + ++_string; + break; + } + } + } + _end = _string; +} + +void TextLineMetrics::getVisibleText(const char* _string, float _top, float _bottom, const char*& _begin, const char*& _end) +{ + CodePoint codepoint = 0; + uint32_t state = 0; + // y is bottom of a text line + float y = m_lineHeight; + while(*_string && (y < _top)) + { + for (; *_string; ++_string) + { + if(utf8_decode(&state, (uint32_t*)&codepoint, *_string) == UTF8_ACCEPT) + { + if(codepoint == L'\n') + { + y += m_lineHeight; + ++_string; + break; + } + } + } + } + BX_CHECK(state == UTF8_ACCEPT, "The string is not well-formed"); + _begin = _string; + + // y is now top of a text line + y -= m_lineHeight; + while((*_string) && (y < _bottom) ) + { + for (; *_string; ++_string) + { + if(utf8_decode(&state, (uint32_t*)&codepoint, *_string) == UTF8_ACCEPT) + { + if(codepoint == L'\n') + { + y += m_lineHeight; + ++_string; + break; + } + } + } + } + BX_CHECK(state == UTF8_ACCEPT, "The string is not well-formed"); + _end = _string; +} + +void TextLineMetrics::getVisibleText(const wchar_t* _string, float _top, float _bottom, const wchar_t*& _begin, const wchar_t*& _end) +{ + // y is bottom of a text line + float y = m_lineHeight; + + const wchar_t* _textEnd = _string + wcslen(_string); + + while(y < _top) + { + for (const wchar_t* _current = _string; _current < _textEnd; ++_current) + { + if(*_current == L'\n') + { + y += m_lineHeight; + ++_string; + break; + } + } + } + _begin = _string; + + // y is now top of a text line + y -= m_lineHeight; + while(y < _bottom ) + { + for (const wchar_t* _current = _string; _current < _textEnd; ++_current) + { + if(*_current == L'\n') + { + y += m_lineHeight; + ++_string; + break; + } + } + } + _end = _string; +} diff --git a/examples/common/font/text_metrics.h b/examples/common/font/text_metrics.h new file mode 100644 index 000000000..b912c4194 --- /dev/null +++ b/examples/common/font/text_metrics.h @@ -0,0 +1,67 @@ +/* + * Copyright 2013 Jeremie Roy. All rights reserved. + * License: http://www.opensource.org/licenses/BSD-2-Clause + */ + +#ifndef __TEXT_METRICS_H__ +#define __TEXT_METRICS_H__ + +#include "font_manager.h" + +class TextMetrics +{ +public: + TextMetrics(FontManager* _fontManager); + + /// Append an ASCII/utf-8 string to the metrics helper + void appendText(FontHandle _fontHandle, const char* _string); + + /// Append a wide char string to the metrics helper + void appendText(FontHandle _fontHandle, const wchar_t* _string); + + /// return the width of the measured text + float getWidth() const { return m_width; } + + /// return the height of the measured text + float getHeight() const { return m_height; } + +private: + FontManager* m_fontManager; + float m_width; + float m_height; + float m_x; + float m_lineHeight; + float m_lineGap; +}; + +/// Compute text crop area for text using a single font +class TextLineMetrics +{ +public: + TextLineMetrics(FontManager* _fontManager, FontHandle _fontHandle); + + /// Return the height of a line of text using the given font + float getLineHeight() const { return m_lineHeight; } + + /// Return the number of text line in the given text + uint32_t getLineCount(const char* _string) const; + /// Return the number of text line in the given text + uint32_t getLineCount(const wchar_t* _string) const; + + /// Return the first and last character visible in the [_firstLine, _lastLine[ range + void getSubText(const char* _string, uint32_t _firstLine, uint32_t _lastLine, const char*& _begin, const char*& _end); + /// Return the first and last character visible in the [_firstLine, _lastLine[ range + void getSubText(const wchar_t* _string, uint32_t _firstLine, uint32_t _lastLine, const wchar_t*& _begin, const wchar_t*& _end); + + /// Return the first and last character visible in the [_top, _bottom] range, + void getVisibleText(const char* _string, float _top, float _bottom, const char*& _begin, const char*& _end); + /// Return the first and last character visible in the [_top, _bottom] range, + void getVisibleText(const wchar_t* _string, float _top, float _bottom, const wchar_t*& _begin, const wchar_t*& _end); + +private: + FontManager* m_fontManager; + float m_lineHeight; +}; + + +#endif // __TEXT_METRICS_H__