/* * Copyright 2013 Jeremie Roy. All rights reserved. * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause */ #include "../common.h" #include #include #include // offsetof #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" static const bgfx::EmbeddedShader s_embeddedShaders[] = { BGFX_EMBEDDED_SHADER(vs_font_basic), BGFX_EMBEDDED_SHADER(fs_font_basic), BGFX_EMBEDDED_SHADER(vs_font_distance_field), BGFX_EMBEDDED_SHADER(fs_font_distance_field), BGFX_EMBEDDED_SHADER(vs_font_distance_field_subpixel), BGFX_EMBEDDED_SHADER(fs_font_distance_field_subpixel), BGFX_EMBEDDED_SHADER_END() }; #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; if (_end == NULL) { _end = _string + bx::strLen(_string); } BX_CHECK(_end >= _string); 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 + 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) { if (_codePoint == L'\t') { for (uint32_t ii = 0; ii < 4; ++ii) { appendGlyph(_handle, L' '); } return; } 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; } if( m_vertexCount/4 >= MAX_BUFFERED_CHARACTERS) { return; } const FontInfo& font = m_fontManager->getFontInfo(_handle); 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]; bgfx::RendererType::Enum type = bgfx::getRendererType(); m_basicProgram = bgfx::createProgram( bgfx::createEmbeddedShader(s_embeddedShaders, type, "vs_font_basic") , bgfx::createEmbeddedShader(s_embeddedShaders, type, "fs_font_basic") , true ); m_distanceProgram = bgfx::createProgram( bgfx::createEmbeddedShader(s_embeddedShaders, type, "vs_font_distance_field") , bgfx::createEmbeddedShader(s_embeddedShaders, type, "fs_font_distance_field") , true ); m_distanceSubpixelProgram = bgfx::createProgram( bgfx::createEmbeddedShader(s_embeddedShaders, type, "vs_font_distance_field_subpixel") , bgfx::createEmbeddedShader(s_embeddedShaders, type, "fs_font_distance_field_subpixel") , true ); m_vertexDecl .begin() .add(bgfx::Attrib::Position, 2, bgfx::AttribType::Float) .add(bgfx::Attrib::TexCoord0, 4, bgfx::AttribType::Int16, true) .add(bgfx::Attrib::Color0, 4, bgfx::AttribType::Uint8, true) .end(); s_texColor = bgfx::createUniform("s_texColor", bgfx::UniformType::Int1); } TextBufferManager::~TextBufferManager() { BX_CHECK(m_textBufferHandles.getNumHandles() == 0, "All the text buffers must be destroyed before destroying the manager"); delete [] m_textBuffers; bgfx::destroy(s_texColor); bgfx::destroy(m_basicProgram); bgfx::destroy(m_distanceProgram); bgfx::destroy(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::kInvalidHandle; bc.vertexBufferHandleIdx = bgfx::kInvalidHandle; 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::kInvalidHandle) { return; } switch (bc.bufferType) { case BufferType::Static: { bgfx::IndexBufferHandle ibh; bgfx::VertexBufferHandle vbh; ibh.idx = bc.indexBufferHandleIdx; vbh.idx = bc.vertexBufferHandleIdx; bgfx::destroy(ibh); bgfx::destroy(vbh); } break; case BufferType::Dynamic: bgfx::DynamicIndexBufferHandle ibh; bgfx::DynamicVertexBufferHandle vbh; ibh.idx = bc.indexBufferHandleIdx; vbh.idx = bc.vertexBufferHandleIdx; bgfx::destroy(ibh); bgfx::destroy(vbh); break; case BufferType::Transient: // destroyed every frame break; } } void TextBufferManager::submitTextBuffer(TextBufferHandle _handle, bgfx::ViewId _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; } bgfx::setTexture(0, s_texColor, m_fontManager->getAtlas()->getTextureHandle() ); bgfx::ProgramHandle program = BGFX_INVALID_HANDLE; switch (bc.fontType) { case FONT_TYPE_ALPHA: program = 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: program = 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: program = 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::kInvalidHandle == bc.vertexBufferHandleIdx) { ibh = bgfx::createIndexBuffer( bgfx::copy(bc.textBuffer->getIndexBuffer(), indexSize) ); vbh = bgfx::createVertexBuffer( bgfx::copy(bc.textBuffer->getVertexBuffer(), vertexSize) , m_vertexDecl ); bc.vertexBufferHandleIdx = vbh.idx; bc.indexBufferHandleIdx = ibh.idx; } else { vbh.idx = bc.vertexBufferHandleIdx; ibh.idx = bc.indexBufferHandleIdx; } bgfx::setVertexBuffer(0, vbh, 0, bc.textBuffer->getVertexCount() ); bgfx::setIndexBuffer(ibh, 0, bc.textBuffer->getIndexCount() ); } break; case BufferType::Dynamic: { bgfx::DynamicIndexBufferHandle ibh; bgfx::DynamicVertexBufferHandle vbh; if (bgfx::kInvalidHandle == bc.vertexBufferHandleIdx ) { ibh = bgfx::createDynamicIndexBuffer( bgfx::copy(bc.textBuffer->getIndexBuffer(), indexSize) ); vbh = bgfx::createDynamicVertexBuffer( bgfx::copy(bc.textBuffer->getVertexBuffer(), vertexSize) , m_vertexDecl ); bc.indexBufferHandleIdx = ibh.idx; bc.vertexBufferHandleIdx = vbh.idx; } else { ibh.idx = bc.indexBufferHandleIdx; vbh.idx = bc.vertexBufferHandleIdx; bgfx::updateDynamicIndexBuffer(ibh , 0 , bgfx::copy(bc.textBuffer->getIndexBuffer(), indexSize) ); bgfx::updateDynamicVertexBuffer(vbh , 0 , bgfx::copy(bc.textBuffer->getVertexBuffer(), vertexSize) ); } bgfx::setVertexBuffer(0, vbh, 0, bc.textBuffer->getVertexCount() ); bgfx::setIndexBuffer(ibh, 0, 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); bx::memCopy(tib.data, bc.textBuffer->getIndexBuffer(), indexSize); bx::memCopy(tvb.data, bc.textBuffer->getVertexBuffer(), vertexSize); bgfx::setVertexBuffer(0, &tvb, 0, bc.textBuffer->getVertexCount() ); bgfx::setIndexBuffer(&tib, 0, bc.textBuffer->getIndexCount() ); } break; } bgfx::submit(_id, program, _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(); }