/* * Copyright 2013 Jeremie Roy. All rights reserved. * License: http://www.opensource.org/licenses/BSD-2-Clause */ #include "../common.h" #include #include // offsetof #include // memcpy #include // wcslen #include "text_buffer_manager.h" #include "utf8.h" #include "../cube_atlas.h" #include "vs_font_basic.bin.h" #include "fs_font_basic.bin.h" #include "vs_font_distance_field.bin.h" #include "fs_font_distance_field.bin.h" #include "vs_font_distance_field_subpixel.bin.h" #include "fs_font_distance_field_subpixel.bin.h" #define MAX_BUFFERED_CHARACTERS (8192 - 5) class TextBuffer { public: /// TextBuffer is bound to a fontManager for glyph retrieval /// @remark the ownership of the manager is not taken TextBuffer(FontManager* _fontManager); ~TextBuffer(); void setStyle(uint32_t _flags = STYLE_NORMAL) { m_styleFlags = _flags; } void setTextColor(uint32_t _rgba = 0x000000FF) { m_textColor = toABGR(_rgba); } void setBackgroundColor(uint32_t _rgba = 0x000000FF) { m_backgroundColor = toABGR(_rgba); } void setOverlineColor(uint32_t _rgba = 0x000000FF) { m_overlineColor = toABGR(_rgba); } void setUnderlineColor(uint32_t _rgba = 0x000000FF) { m_underlineColor = toABGR(_rgba); } void setStrikeThroughColor(uint32_t _rgba = 0x000000FF) { m_strikeThroughColor = toABGR(_rgba); } void setPenPosition(float _x, float _y) { m_penX = _x; m_penY = _y; } /// Append an ASCII/utf-8 string to the buffer using current pen /// position and color. void appendText(FontHandle _fontHandle, const char* _string, const char* _end = NULL); /// Append a wide char unicode string to the buffer using current pen /// position and color. void appendText(FontHandle _fontHandle, const wchar_t* _string, const wchar_t* _end = NULL); /// Append a whole face of the atlas cube, mostly used for debugging /// and visualizing atlas. void appendAtlasFace(uint16_t _faceIndex); /// Clear the text buffer and reset its state (pen/color) void clearTextBuffer(); /// Get pointer to the vertex buffer to submit it to the graphic card. const uint8_t* getVertexBuffer() { return (uint8_t*) m_vertexBuffer; } /// Number of vertex in the vertex buffer. uint32_t getVertexCount() const { return m_vertexCount; } /// Size in bytes of a vertex. uint32_t getVertexSize() const { return sizeof(TextVertex); } /// get a pointer to the index buffer to submit it to the graphic const uint16_t* getIndexBuffer() const { return m_indexBuffer; } /// number of index in the index buffer uint32_t getIndexCount() const { return m_indexCount; } /// Size in bytes of an index. uint32_t getIndexSize() const { return sizeof(uint16_t); } uint32_t getTextColor() const { return toABGR(m_textColor); } TextRectangle getRectangle() const { return m_rectangle; } private: void appendGlyph(FontHandle _handle, CodePoint _codePoint); void verticalCenterLastLine(float _txtDecalY, float _top, float _bottom); static uint32_t toABGR(uint32_t _rgba) { return ( ( (_rgba >> 0) & 0xff) << 24) | ( ( (_rgba >> 8) & 0xff) << 16) | ( ( (_rgba >> 16) & 0xff) << 8) | ( ( (_rgba >> 24) & 0xff) << 0) ; } uint32_t m_styleFlags; // color states uint32_t m_textColor; uint32_t m_backgroundColor; uint32_t m_overlineColor; uint32_t m_underlineColor; uint32_t m_strikeThroughColor; //position states float m_penX; float m_penY; float m_originX; float m_originY; float m_lineAscender; float m_lineDescender; float m_lineGap; TextRectangle m_rectangle; FontManager* m_fontManager; void setVertex(uint32_t _i, float _x, float _y, uint32_t _rgba, uint8_t _style = STYLE_NORMAL) { m_vertexBuffer[_i].x = _x; m_vertexBuffer[_i].y = _y; m_vertexBuffer[_i].rgba = _rgba; m_styleBuffer[_i] = _style; } struct TextVertex { float x, y; int16_t u, v, w, t; uint32_t rgba; }; TextVertex* m_vertexBuffer; uint16_t* m_indexBuffer; uint8_t* m_styleBuffer; uint32_t m_indexCount; uint32_t m_lineStartIndex; uint16_t m_vertexCount; }; TextBuffer::TextBuffer(FontManager* _fontManager) : m_styleFlags(STYLE_NORMAL) , m_textColor(0xffffffff) , m_backgroundColor(0xffffffff) , m_overlineColor(0xffffffff) , m_underlineColor(0xffffffff) , m_strikeThroughColor(0xffffffff) , m_penX(0) , m_penY(0) , m_originX(0) , m_originY(0) , m_lineAscender(0) , m_lineDescender(0) , m_lineGap(0) , m_fontManager(_fontManager) , m_vertexBuffer(new TextVertex[MAX_BUFFERED_CHARACTERS * 4]) , m_indexBuffer(new uint16_t[MAX_BUFFERED_CHARACTERS * 6]) , m_styleBuffer(new uint8_t[MAX_BUFFERED_CHARACTERS * 4]) , m_indexCount(0) , m_lineStartIndex(0) , m_vertexCount(0) { m_rectangle.width = 0; m_rectangle.height = 0; } TextBuffer::~TextBuffer() { delete [] m_vertexBuffer; delete [] m_indexBuffer; delete [] m_styleBuffer; } void TextBuffer::appendText(FontHandle _fontHandle, const char* _string, const char* _end) { if (m_vertexCount == 0) { m_originX = m_penX; m_originY = m_penY; m_lineDescender = 0; m_lineAscender = 0; m_lineGap = 0; } CodePoint codepoint = 0; uint32_t state = 0; for (; *_string && _string<_end ; ++_string) { if (utf8_decode(&state, (uint32_t*)&codepoint, *_string) == UTF8_ACCEPT ) { appendGlyph(_fontHandle, codepoint); } } BX_CHECK(state == UTF8_ACCEPT, "The string is not well-formed"); } void TextBuffer::appendText(FontHandle _fontHandle, const wchar_t* _string, const wchar_t* _end) { if (m_vertexCount == 0) { m_originX = m_penX; m_originY = m_penY; m_lineDescender = 0; m_lineAscender = 0; m_lineGap = 0; } if (_end == NULL) { _end = _string + (uint32_t) wcslen(_string); } BX_CHECK(_end >= _string); for (const wchar_t* _current = _string; _current < _end; ++_current) { uint32_t _codePoint = *_current; appendGlyph(_fontHandle, _codePoint); } } void TextBuffer::appendAtlasFace(uint16_t _faceIndex) { if( m_vertexCount/4 >= MAX_BUFFERED_CHARACTERS) { return; } float x0 = m_penX; float y0 = m_penY; float x1 = x0 + (float)m_fontManager->getAtlas()->getTextureSize(); float y1 = y0 + (float)m_fontManager->getAtlas()->getTextureSize(); m_fontManager->getAtlas()->packFaceLayerUV(_faceIndex , (uint8_t*)m_vertexBuffer , sizeof(TextVertex) * m_vertexCount + offsetof(TextVertex, u) , sizeof(TextVertex) ); setVertex(m_vertexCount + 0, x0, y0, m_backgroundColor); setVertex(m_vertexCount + 1, x0, y1, m_backgroundColor); setVertex(m_vertexCount + 2, x1, y1, m_backgroundColor); setVertex(m_vertexCount + 3, x1, y0, m_backgroundColor); m_indexBuffer[m_indexCount + 0] = m_vertexCount + 0; m_indexBuffer[m_indexCount + 1] = m_vertexCount + 1; m_indexBuffer[m_indexCount + 2] = m_vertexCount + 2; m_indexBuffer[m_indexCount + 3] = m_vertexCount + 0; m_indexBuffer[m_indexCount + 4] = m_vertexCount + 2; m_indexBuffer[m_indexCount + 5] = m_vertexCount + 3; m_vertexCount += 4; m_indexCount += 6; } void TextBuffer::clearTextBuffer() { m_penX = 0; m_penY = 0; m_originX = 0; m_originY = 0; m_vertexCount = 0; m_indexCount = 0; m_lineStartIndex = 0; m_lineAscender = 0; m_lineDescender = 0; m_lineGap = 0; m_rectangle.width = 0; m_rectangle.height = 0; } void TextBuffer::appendGlyph(FontHandle _handle, CodePoint _codePoint) { const GlyphInfo* glyph = m_fontManager->getGlyphInfo(_handle, _codePoint); BX_WARN(NULL != glyph, "Glyph not found (font handle %d, code point %d)", _handle.idx, _codePoint); if (NULL == glyph) { return; } const FontInfo& font = m_fontManager->getFontInfo(_handle); if( m_vertexCount/4 >= MAX_BUFFERED_CHARACTERS) { return; } if (_codePoint == L'\n') { m_penX = m_originX; m_penY += m_lineGap + m_lineAscender -m_lineDescender; m_lineGap = font.lineGap; m_lineDescender = font.descender; m_lineAscender = font.ascender; m_lineStartIndex = m_vertexCount; return; } //is there a change of font size that require the text on the left to be centered again ? if (font.ascender > m_lineAscender || (font.descender < m_lineDescender) ) { if (font.descender < m_lineDescender) { m_lineDescender = font.descender; m_lineGap = font.lineGap; } float txtDecals = (font.ascender - m_lineAscender); m_lineAscender = font.ascender; m_lineGap = font.lineGap; verticalCenterLastLine( (txtDecals), (m_penY - m_lineAscender), (m_penY + m_lineAscender - m_lineDescender + m_lineGap) ); } float kerning = 0 * font.scale; m_penX += kerning; const GlyphInfo& blackGlyph = m_fontManager->getBlackGlyph(); const Atlas* atlas = m_fontManager->getAtlas(); if (m_styleFlags & STYLE_BACKGROUND && m_backgroundColor & 0xFF000000) { float x0 = (m_penX - kerning); float y0 = (m_penY); float x1 = ( (float)x0 + (glyph->advance_x) ); float y1 = (m_penY + m_lineAscender - m_lineDescender + m_lineGap); atlas->packUV(blackGlyph.regionIndex , (uint8_t*)m_vertexBuffer , sizeof(TextVertex) * m_vertexCount + offsetof(TextVertex, u) , sizeof(TextVertex) ); const uint16_t vertexCount = m_vertexCount; setVertex(vertexCount + 0, x0, y0, m_backgroundColor, STYLE_BACKGROUND); setVertex(vertexCount + 1, x0, y1, m_backgroundColor, STYLE_BACKGROUND); setVertex(vertexCount + 2, x1, y1, m_backgroundColor, STYLE_BACKGROUND); setVertex(vertexCount + 3, x1, y0, m_backgroundColor, STYLE_BACKGROUND); m_indexBuffer[m_indexCount + 0] = vertexCount + 0; m_indexBuffer[m_indexCount + 1] = vertexCount + 1; m_indexBuffer[m_indexCount + 2] = vertexCount + 2; m_indexBuffer[m_indexCount + 3] = vertexCount + 0; m_indexBuffer[m_indexCount + 4] = vertexCount + 2; m_indexBuffer[m_indexCount + 5] = vertexCount + 3; m_vertexCount += 4; m_indexCount += 6; } if (m_styleFlags & STYLE_UNDERLINE && m_underlineColor & 0xFF000000) { float x0 = (m_penX - kerning); float y0 = (m_penY + m_lineAscender - m_lineDescender * 0.5f); float x1 = ( (float)x0 + (glyph->advance_x) ); float y1 = y0 + font.underlineThickness; atlas->packUV(blackGlyph.regionIndex , (uint8_t*)m_vertexBuffer , sizeof(TextVertex) * m_vertexCount + offsetof(TextVertex, u) , sizeof(TextVertex) ); setVertex(m_vertexCount + 0, x0, y0, m_underlineColor, STYLE_UNDERLINE); setVertex(m_vertexCount + 1, x0, y1, m_underlineColor, STYLE_UNDERLINE); setVertex(m_vertexCount + 2, x1, y1, m_underlineColor, STYLE_UNDERLINE); setVertex(m_vertexCount + 3, x1, y0, m_underlineColor, STYLE_UNDERLINE); m_indexBuffer[m_indexCount + 0] = m_vertexCount + 0; m_indexBuffer[m_indexCount + 1] = m_vertexCount + 1; m_indexBuffer[m_indexCount + 2] = m_vertexCount + 2; m_indexBuffer[m_indexCount + 3] = m_vertexCount + 0; m_indexBuffer[m_indexCount + 4] = m_vertexCount + 2; m_indexBuffer[m_indexCount + 5] = m_vertexCount + 3; m_vertexCount += 4; m_indexCount += 6; } if (m_styleFlags & STYLE_OVERLINE && m_overlineColor & 0xFF000000) { float x0 = (m_penX - kerning); float y0 = (m_penY); float x1 = ( (float)x0 + (glyph->advance_x) ); float y1 = y0 + font.underlineThickness; m_fontManager->getAtlas()->packUV(blackGlyph.regionIndex , (uint8_t*)m_vertexBuffer , sizeof(TextVertex) * m_vertexCount + offsetof(TextVertex, u) , sizeof(TextVertex) ); setVertex(m_vertexCount + 0, x0, y0, m_overlineColor, STYLE_OVERLINE); setVertex(m_vertexCount + 1, x0, y1, m_overlineColor, STYLE_OVERLINE); setVertex(m_vertexCount + 2, x1, y1, m_overlineColor, STYLE_OVERLINE); setVertex(m_vertexCount + 3, x1, y0, m_overlineColor, STYLE_OVERLINE); m_indexBuffer[m_indexCount + 0] = m_vertexCount + 0; m_indexBuffer[m_indexCount + 1] = m_vertexCount + 1; m_indexBuffer[m_indexCount + 2] = m_vertexCount + 2; m_indexBuffer[m_indexCount + 3] = m_vertexCount + 0; m_indexBuffer[m_indexCount + 4] = m_vertexCount + 2; m_indexBuffer[m_indexCount + 5] = m_vertexCount + 3; m_vertexCount += 4; m_indexCount += 6; } if (m_styleFlags & STYLE_STRIKE_THROUGH && m_strikeThroughColor & 0xFF000000) { float x0 = (m_penX - kerning); float y0 = (m_penY + 0.666667f * font.ascender); float x1 = ( (float)x0 + (glyph->advance_x) ); float y1 = y0 + font.underlineThickness; atlas->packUV(blackGlyph.regionIndex , (uint8_t*)m_vertexBuffer , sizeof(TextVertex) * m_vertexCount + offsetof(TextVertex, u) , sizeof(TextVertex) ); setVertex(m_vertexCount + 0, x0, y0, m_strikeThroughColor, STYLE_STRIKE_THROUGH); setVertex(m_vertexCount + 1, x0, y1, m_strikeThroughColor, STYLE_STRIKE_THROUGH); setVertex(m_vertexCount + 2, x1, y1, m_strikeThroughColor, STYLE_STRIKE_THROUGH); setVertex(m_vertexCount + 3, x1, y0, m_strikeThroughColor, STYLE_STRIKE_THROUGH); m_indexBuffer[m_indexCount + 0] = m_vertexCount + 0; m_indexBuffer[m_indexCount + 1] = m_vertexCount + 1; m_indexBuffer[m_indexCount + 2] = m_vertexCount + 2; m_indexBuffer[m_indexCount + 3] = m_vertexCount + 0; m_indexBuffer[m_indexCount + 4] = m_vertexCount + 2; m_indexBuffer[m_indexCount + 5] = m_vertexCount + 3; m_vertexCount += 4; m_indexCount += 6; } float x0 = m_penX + (glyph->offset_x); float y0 = (m_penY + m_lineAscender + (glyph->offset_y) ); float x1 = (x0 + glyph->width); float y1 = (y0 + glyph->height); atlas->packUV(glyph->regionIndex , (uint8_t*)m_vertexBuffer , sizeof(TextVertex) * m_vertexCount + offsetof(TextVertex, u) , sizeof(TextVertex) ); setVertex(m_vertexCount + 0, x0, y0, m_textColor); setVertex(m_vertexCount + 1, x0, y1, m_textColor); setVertex(m_vertexCount + 2, x1, y1, m_textColor); setVertex(m_vertexCount + 3, x1, y0, m_textColor); m_indexBuffer[m_indexCount + 0] = m_vertexCount + 0; m_indexBuffer[m_indexCount + 1] = m_vertexCount + 1; m_indexBuffer[m_indexCount + 2] = m_vertexCount + 2; m_indexBuffer[m_indexCount + 3] = m_vertexCount + 0; m_indexBuffer[m_indexCount + 4] = m_vertexCount + 2; m_indexBuffer[m_indexCount + 5] = m_vertexCount + 3; m_vertexCount += 4; m_indexCount += 6; m_penX += glyph->advance_x; if (m_penX > m_rectangle.width) { m_rectangle.width = m_penX; } if ( (m_penY +m_lineAscender - m_lineDescender+m_lineGap) > m_rectangle.height) { m_rectangle.height = (m_penY +m_lineAscender - m_lineDescender+m_lineGap); } } void TextBuffer::verticalCenterLastLine(float _dy, float _top, float _bottom) { for (uint32_t ii = m_lineStartIndex; ii < m_vertexCount; ii += 4) { if (m_styleBuffer[ii] == STYLE_BACKGROUND) { m_vertexBuffer[ii + 0].y = _top; m_vertexBuffer[ii + 1].y = _bottom; m_vertexBuffer[ii + 2].y = _bottom; m_vertexBuffer[ii + 3].y = _top; } else { m_vertexBuffer[ii + 0].y += _dy; m_vertexBuffer[ii + 1].y += _dy; m_vertexBuffer[ii + 2].y += _dy; m_vertexBuffer[ii + 3].y += _dy; } } } TextBufferManager::TextBufferManager(FontManager* _fontManager) : m_fontManager(_fontManager) { m_textBuffers = new BufferCache[MAX_TEXT_BUFFER_COUNT]; const bgfx::Memory* vs_font_basic; const bgfx::Memory* fs_font_basic; const bgfx::Memory* vs_font_distance_field; const bgfx::Memory* fs_font_distance_field; const bgfx::Memory* vs_font_distance_field_subpixel; const bgfx::Memory* fs_font_distance_field_subpixel; switch (bgfx::getRendererType() ) { case bgfx::RendererType::Direct3D9: vs_font_basic = bgfx::makeRef(vs_font_basic_dx9, sizeof(vs_font_basic_dx9) ); fs_font_basic = bgfx::makeRef(fs_font_basic_dx9, sizeof(fs_font_basic_dx9) ); vs_font_distance_field = bgfx::makeRef(vs_font_distance_field_dx9, sizeof(vs_font_distance_field_dx9) ); fs_font_distance_field = bgfx::makeRef(fs_font_distance_field_dx9, sizeof(fs_font_distance_field_dx9) ); vs_font_distance_field_subpixel = bgfx::makeRef(vs_font_distance_field_subpixel_dx9, sizeof(vs_font_distance_field_subpixel_dx9) ); fs_font_distance_field_subpixel = bgfx::makeRef(fs_font_distance_field_subpixel_dx9, sizeof(fs_font_distance_field_subpixel_dx9) ); break; case bgfx::RendererType::Direct3D11: vs_font_basic = bgfx::makeRef(vs_font_basic_dx11, sizeof(vs_font_basic_dx11) ); fs_font_basic = bgfx::makeRef(fs_font_basic_dx11, sizeof(fs_font_basic_dx11) ); vs_font_distance_field = bgfx::makeRef(vs_font_distance_field_dx11, sizeof(vs_font_distance_field_dx11) ); fs_font_distance_field = bgfx::makeRef(fs_font_distance_field_dx11, sizeof(fs_font_distance_field_dx11) ); vs_font_distance_field_subpixel = bgfx::makeRef(vs_font_distance_field_subpixel_dx11, sizeof(vs_font_distance_field_subpixel_dx11) ); fs_font_distance_field_subpixel = bgfx::makeRef(fs_font_distance_field_subpixel_dx11, sizeof(fs_font_distance_field_subpixel_dx11) ); break; default: vs_font_basic = bgfx::makeRef(vs_font_basic_glsl, sizeof(vs_font_basic_glsl) ); fs_font_basic = bgfx::makeRef(fs_font_basic_glsl, sizeof(fs_font_basic_glsl) ); vs_font_distance_field = bgfx::makeRef(vs_font_distance_field_glsl, sizeof(vs_font_distance_field_glsl) ); fs_font_distance_field = bgfx::makeRef(fs_font_distance_field_glsl, sizeof(fs_font_distance_field_glsl) ); vs_font_distance_field_subpixel = bgfx::makeRef(vs_font_distance_field_subpixel_glsl, sizeof(vs_font_distance_field_subpixel_glsl) ); fs_font_distance_field_subpixel = bgfx::makeRef(fs_font_distance_field_subpixel_glsl, sizeof(fs_font_distance_field_subpixel_glsl) ); break; } bgfx::ShaderHandle vsh; bgfx::ShaderHandle fsh; vsh = bgfx::createShader(vs_font_basic); fsh = bgfx::createShader(fs_font_basic); m_basicProgram = bgfx::createProgram(vsh, fsh); bgfx::destroyShader(vsh); bgfx::destroyShader(fsh); vsh = bgfx::createShader(vs_font_distance_field); fsh = bgfx::createShader(fs_font_distance_field); m_distanceProgram = bgfx::createProgram(vsh, fsh); bgfx::destroyShader(vsh); bgfx::destroyShader(fsh); vsh = bgfx::createShader(vs_font_distance_field_subpixel); fsh = bgfx::createShader(fs_font_distance_field_subpixel); m_distanceSubpixelProgram = bgfx::createProgram(vsh, fsh); bgfx::destroyShader(vsh); bgfx::destroyShader(fsh); m_vertexDecl.begin(); m_vertexDecl.add(bgfx::Attrib::Position, 2, bgfx::AttribType::Float); m_vertexDecl.add(bgfx::Attrib::TexCoord0, 4, bgfx::AttribType::Int16, true); m_vertexDecl.add(bgfx::Attrib::Color0, 4, bgfx::AttribType::Uint8, true); m_vertexDecl.end(); u_texColor = bgfx::createUniform("u_texColor", bgfx::UniformType::Uniform1iv); } TextBufferManager::~TextBufferManager() { BX_CHECK(m_textBufferHandles.getNumHandles() == 0, "All the text buffers must be destroyed before destroying the manager"); delete [] m_textBuffers; bgfx::destroyUniform(u_texColor); bgfx::destroyProgram(m_basicProgram); bgfx::destroyProgram(m_distanceProgram); bgfx::destroyProgram(m_distanceSubpixelProgram); } TextBufferHandle TextBufferManager::createTextBuffer(uint32_t _type, BufferType::Enum _bufferType) { uint16_t textIdx = m_textBufferHandles.alloc(); BufferCache& bc = m_textBuffers[textIdx]; bc.textBuffer = new TextBuffer(m_fontManager); bc.fontType = _type; bc.bufferType = _bufferType; bc.indexBufferHandleIdx = bgfx::invalidHandle; bc.vertexBufferHandleIdx = bgfx::invalidHandle; TextBufferHandle ret = {textIdx}; return ret; } void TextBufferManager::destroyTextBuffer(TextBufferHandle _handle) { BX_CHECK(bgfx::isValid(_handle), "Invalid handle used"); BufferCache& bc = m_textBuffers[_handle.idx]; m_textBufferHandles.free(_handle.idx); delete bc.textBuffer; bc.textBuffer = NULL; if (bc.vertexBufferHandleIdx == bgfx::invalidHandle) { return; } switch (bc.bufferType) { case BufferType::Static: { bgfx::IndexBufferHandle ibh; bgfx::VertexBufferHandle vbh; ibh.idx = bc.indexBufferHandleIdx; vbh.idx = bc.vertexBufferHandleIdx; bgfx::destroyIndexBuffer(ibh); bgfx::destroyVertexBuffer(vbh); } break; case BufferType::Dynamic: bgfx::DynamicIndexBufferHandle ibh; bgfx::DynamicVertexBufferHandle vbh; ibh.idx = bc.indexBufferHandleIdx; vbh.idx = bc.vertexBufferHandleIdx; bgfx::destroyDynamicIndexBuffer(ibh); bgfx::destroyDynamicVertexBuffer(vbh); break; case BufferType::Transient: // destroyed every frame break; } } void TextBufferManager::submitTextBuffer(TextBufferHandle _handle, uint8_t _id, int32_t _depth) { BX_CHECK(bgfx::isValid(_handle), "Invalid handle used"); BufferCache& bc = m_textBuffers[_handle.idx]; uint32_t indexSize = bc.textBuffer->getIndexCount() * bc.textBuffer->getIndexSize(); uint32_t vertexSize = bc.textBuffer->getVertexCount() * bc.textBuffer->getVertexSize(); if (0 == indexSize || 0 == vertexSize) { return; } const bgfx::Memory* mem; bgfx::setTexture(0, u_texColor, m_fontManager->getAtlas()->getTextureHandle() ); switch (bc.fontType) { case FONT_TYPE_ALPHA: bgfx::setProgram(m_basicProgram); bgfx::setState(0 | BGFX_STATE_RGB_WRITE | BGFX_STATE_BLEND_FUNC(BGFX_STATE_BLEND_SRC_ALPHA, BGFX_STATE_BLEND_INV_SRC_ALPHA) ); break; case FONT_TYPE_DISTANCE: bgfx::setProgram(m_distanceProgram); bgfx::setState(0 | BGFX_STATE_RGB_WRITE | BGFX_STATE_BLEND_FUNC(BGFX_STATE_BLEND_SRC_ALPHA, BGFX_STATE_BLEND_INV_SRC_ALPHA) ); break; case FONT_TYPE_DISTANCE_SUBPIXEL: bgfx::setProgram(m_distanceSubpixelProgram); bgfx::setState(0 | BGFX_STATE_RGB_WRITE | BGFX_STATE_BLEND_FUNC(BGFX_STATE_BLEND_FACTOR, BGFX_STATE_BLEND_INV_SRC_COLOR) , bc.textBuffer->getTextColor() ); break; } switch (bc.bufferType) { case BufferType::Static: { bgfx::IndexBufferHandle ibh; bgfx::VertexBufferHandle vbh; if (bgfx::invalidHandle == bc.vertexBufferHandleIdx) { mem = bgfx::alloc(indexSize); memcpy(mem->data, bc.textBuffer->getIndexBuffer(), indexSize); ibh = bgfx::createIndexBuffer(mem); mem = bgfx::alloc(vertexSize); memcpy(mem->data, bc.textBuffer->getVertexBuffer(), vertexSize); vbh = bgfx::createVertexBuffer(mem, m_vertexDecl); bc.indexBufferHandleIdx = ibh.idx; bc.vertexBufferHandleIdx = vbh.idx; } else { ibh.idx = bc.indexBufferHandleIdx; vbh.idx = bc.vertexBufferHandleIdx; } bgfx::setVertexBuffer(vbh, bc.textBuffer->getVertexCount() ); bgfx::setIndexBuffer(ibh, bc.textBuffer->getIndexCount() ); } break; case BufferType::Dynamic: { bgfx::DynamicIndexBufferHandle ibh; bgfx::DynamicVertexBufferHandle vbh; if (bgfx::invalidHandle == bc.vertexBufferHandleIdx ) { mem = bgfx::alloc(indexSize); memcpy(mem->data, bc.textBuffer->getIndexBuffer(), indexSize); ibh = bgfx::createDynamicIndexBuffer(mem); mem = bgfx::alloc(vertexSize); memcpy(mem->data, bc.textBuffer->getVertexBuffer(), vertexSize); vbh = bgfx::createDynamicVertexBuffer(mem, m_vertexDecl); bc.indexBufferHandleIdx = ibh.idx; bc.vertexBufferHandleIdx = vbh.idx; } else { ibh.idx = bc.indexBufferHandleIdx; vbh.idx = bc.vertexBufferHandleIdx; mem = bgfx::alloc(indexSize); memcpy(mem->data, bc.textBuffer->getIndexBuffer(), indexSize); bgfx::updateDynamicIndexBuffer(ibh, mem); mem = bgfx::alloc(vertexSize); memcpy(mem->data, bc.textBuffer->getVertexBuffer(), vertexSize); bgfx::updateDynamicVertexBuffer(vbh, mem); } bgfx::setVertexBuffer(vbh, bc.textBuffer->getVertexCount() ); bgfx::setIndexBuffer(ibh, bc.textBuffer->getIndexCount() ); } break; case BufferType::Transient: { bgfx::TransientIndexBuffer tib; bgfx::TransientVertexBuffer tvb; bgfx::allocTransientIndexBuffer(&tib, bc.textBuffer->getIndexCount() ); bgfx::allocTransientVertexBuffer(&tvb, bc.textBuffer->getVertexCount(), m_vertexDecl); memcpy(tib.data, bc.textBuffer->getIndexBuffer(), indexSize); memcpy(tvb.data, bc.textBuffer->getVertexBuffer(), vertexSize); bgfx::setVertexBuffer(&tvb, bc.textBuffer->getVertexCount() ); bgfx::setIndexBuffer(&tib, bc.textBuffer->getIndexCount() ); } break; } bgfx::submit(_id, _depth); } void TextBufferManager::setStyle(TextBufferHandle _handle, uint32_t _flags) { BX_CHECK(bgfx::isValid(_handle), "Invalid handle used"); BufferCache& bc = m_textBuffers[_handle.idx]; bc.textBuffer->setStyle(_flags); } void TextBufferManager::setTextColor(TextBufferHandle _handle, uint32_t _rgba) { BX_CHECK(bgfx::isValid(_handle), "Invalid handle used"); BufferCache& bc = m_textBuffers[_handle.idx]; bc.textBuffer->setTextColor(_rgba); } void TextBufferManager::setBackgroundColor(TextBufferHandle _handle, uint32_t _rgba) { BX_CHECK(bgfx::isValid(_handle), "Invalid handle used"); BufferCache& bc = m_textBuffers[_handle.idx]; bc.textBuffer->setBackgroundColor(_rgba); } void TextBufferManager::setOverlineColor(TextBufferHandle _handle, uint32_t _rgba) { BX_CHECK(bgfx::isValid(_handle), "Invalid handle used"); BufferCache& bc = m_textBuffers[_handle.idx]; bc.textBuffer->setOverlineColor(_rgba); } void TextBufferManager::setUnderlineColor(TextBufferHandle _handle, uint32_t _rgba) { BX_CHECK(bgfx::isValid(_handle), "Invalid handle used"); BufferCache& bc = m_textBuffers[_handle.idx]; bc.textBuffer->setUnderlineColor(_rgba); } void TextBufferManager::setStrikeThroughColor(TextBufferHandle _handle, uint32_t _rgba) { BX_CHECK(bgfx::isValid(_handle), "Invalid handle used"); BufferCache& bc = m_textBuffers[_handle.idx]; bc.textBuffer->setStrikeThroughColor(_rgba); } void TextBufferManager::setPenPosition(TextBufferHandle _handle, float _x, float _y) { BX_CHECK(bgfx::isValid(_handle), "Invalid handle used"); BufferCache& bc = m_textBuffers[_handle.idx]; bc.textBuffer->setPenPosition(_x, _y); } void TextBufferManager::appendText(TextBufferHandle _handle, FontHandle _fontHandle, const char* _string, const char* _end) { BX_CHECK(bgfx::isValid(_handle), "Invalid handle used"); BufferCache& bc = m_textBuffers[_handle.idx]; bc.textBuffer->appendText(_fontHandle, _string, _end); } void TextBufferManager::appendText(TextBufferHandle _handle, FontHandle _fontHandle, const wchar_t* _string, const wchar_t* _end) { BX_CHECK(bgfx::isValid(_handle), "Invalid handle used"); BufferCache& bc = m_textBuffers[_handle.idx]; bc.textBuffer->appendText(_fontHandle, _string, _end); } void TextBufferManager::appendAtlasFace(TextBufferHandle _handle, uint16_t _faceIndex) { BX_CHECK(bgfx::isValid(_handle), "Invalid handle used"); BufferCache& bc = m_textBuffers[_handle.idx]; bc.textBuffer->appendAtlasFace(_faceIndex); } void TextBufferManager::clearTextBuffer(TextBufferHandle _handle) { BX_CHECK(bgfx::isValid(_handle), "Invalid handle used"); BufferCache& bc = m_textBuffers[_handle.idx]; bc.textBuffer->clearTextBuffer(); } TextRectangle TextBufferManager::getRectangle(TextBufferHandle _handle) const { BX_CHECK(bgfx::isValid(_handle), "Invalid handle used"); BufferCache& bc = m_textBuffers[_handle.idx]; return bc.textBuffer->getRectangle(); }