metaforce/specter/lib/TextView.cpp

474 lines
17 KiB
C++
Raw Normal View History

2015-11-21 23:45:02 +00:00
#include "Specter/TextView.hpp"
#include "Specter/ViewResources.hpp"
2015-11-25 01:46:30 +00:00
#include "utf8proc.h"
2015-11-21 23:45:02 +00:00
2015-11-26 07:35:43 +00:00
#include <freetype/internal/internal.h>
#include <freetype/internal/ftobjs.h>
2015-11-21 23:45:02 +00:00
namespace Specter
{
2015-11-25 01:46:30 +00:00
static LogVisor::LogModule Log("Specter::TextView");
void TextView::Resources::init(boo::GLDataFactory* factory, FontCache* fcache)
2015-11-25 01:46:30 +00:00
{
2015-11-26 00:24:01 +00:00
m_fcache = fcache;
2015-11-25 01:46:30 +00:00
static const char* VS =
"#version 330\n"
2015-11-26 00:24:01 +00:00
"layout(location=0) in vec3 posIn[4];\n"
"layout(location=4) in mat4 mvMtx;\n"
"layout(location=8) in vec3 uvIn[4];\n"
"layout(location=12) in vec4 colorIn;\n"
SPECTER_VIEW_VERT_BLOCK_GLSL
2015-11-25 01:46:30 +00:00
"struct VertToFrag\n"
"{\n"
" vec3 uv;\n"
" vec4 color;\n"
"};\n"
"out VertToFrag vtf;\n"
"void main()\n"
"{\n"
" vtf.uv = uvIn[gl_VertexID];\n"
" vtf.color = colorIn;\n"
2015-11-26 00:24:01 +00:00
" gl_Position = mv * mvMtx * vec4(posIn[gl_VertexID], 1.0);\n"
2015-11-25 01:46:30 +00:00
"}\n";
static const char* FSReg =
"#version 330\n"
"uniform sampler2DArray fontTex;\n"
"struct VertToFrag\n"
"{\n"
" vec3 uv;\n"
" vec4 color;\n"
"};\n"
"in VertToFrag vtf;\n"
"layout(location=0) out vec4 colorOut;\n"
"void main()\n"
"{\n"
" colorOut = vtf.color;\n"
2015-12-07 00:52:07 +00:00
" colorOut.a *= texture(fontTex, vtf.uv).r;\n"
2015-11-25 01:46:30 +00:00
"}\n";
static const char* FSSubpixel =
"#version 330\n"
"uniform sampler2DArray fontTex;\n"
"struct VertToFrag\n"
"{\n"
" vec3 uv;\n"
" vec4 color;\n"
"};\n"
"in VertToFrag vtf;\n"
"layout(location=0, index=0) out vec4 colorOut;\n"
"layout(location=0, index=1) out vec4 blendOut;\n"
"void main()\n"
"{\n"
" colorOut = vtf.color;\n"
2015-12-07 00:52:07 +00:00
" blendOut = colorOut.a * texture(fontTex, vtf.uv);\n"
2015-11-25 01:46:30 +00:00
"}\n";
2015-11-26 00:24:01 +00:00
static const char* BlockNames[] = {"SpecterViewBlock"};
2015-11-25 01:46:30 +00:00
2015-11-26 00:24:01 +00:00
m_regular =
factory->newShaderPipeline(VS, FSReg, 1, "fontTex", 1, BlockNames,
2015-11-25 01:46:30 +00:00
boo::BlendFactor::SrcAlpha, boo::BlendFactor::InvSrcAlpha,
2015-11-26 23:03:56 +00:00
false, false, false);
2015-11-25 01:46:30 +00:00
2015-11-26 00:24:01 +00:00
m_subpixel =
factory->newShaderPipeline(VS, FSSubpixel, 1, "fontTex", 1, BlockNames,
2015-11-25 01:46:30 +00:00
boo::BlendFactor::SrcColor1, boo::BlendFactor::InvSrcColor1,
2015-11-26 23:03:56 +00:00
false, false, false);
2015-11-25 01:46:30 +00:00
}
2015-11-28 01:40:59 +00:00
#if _WIN32
2015-11-28 04:07:09 +00:00
2015-12-02 03:05:22 +00:00
void TextView::Resources::init(boo::ID3DDataFactory* factory, FontCache* fcache)
2015-11-27 22:20:22 +00:00
{
m_fcache = fcache;
static const char* VS =
"struct VertData\n"
"{\n"
" float3 posIn[4] : POSITION;\n"
" float4x4 mvMtx : MODELVIEW;\n"
" float3 uvIn[4] : UV;\n"
" float4 colorIn : COLOR;\n"
"};\n"
SPECTER_VIEW_VERT_BLOCK_HLSL
"struct VertToFrag\n"
"{\n"
" float4 position : SV_Position;\n"
" float3 uv : UV;\n"
" float4 color : COLOR;\n"
"};\n"
"VertToFrag main(in VertData v, in uint vertId : SV_VertexID)\n"
"{\n"
" VertToFrag vtf;\n"
" vtf.uv = v.uvIn[vertId];\n"
" vtf.color = v.colorIn;\n"
" vtf.position = mul(mv, mul(v.mvMtx, float4(v.posIn[vertId], 1.0)));\n"
" return vtf;\n"
"}\n";
static const char* FSReg =
"Texture2DArray fontTex : register(t0);\n"
"SamplerState samp : register(s0);\n"
"struct VertToFrag\n"
"{\n"
" float4 position : SV_Position;\n"
" float3 uv : UV;\n"
" float4 color : COLOR;\n"
"};\n"
"float4 main(in VertToFrag vtf) : SV_Target0\n"
"{\n"
" float4 colorOut = vtf.color;\n"
2015-12-07 00:52:07 +00:00
" colorOut.a *= fontTex.Sample(samp, vtf.uv).r;\n"
2015-11-27 22:20:22 +00:00
" return colorOut;\n"
"}\n";
static const char* FSSubpixel =
"Texture2DArray fontTex : register(t0);\n"
"SamplerState samp : register(s0);\n"
"struct VertToFrag\n"
"{\n"
" float4 position : SV_Position;\n"
" float3 uv : UV;\n"
" float4 color : COLOR;\n"
"};\n"
"struct BlendOut\n"
"{\n"
" float4 colorOut : SV_Target0;\n"
" float4 blendOut : SV_Target1;\n"
"};\n"
"BlendOut main(in VertToFrag vtf)\n"
"{\n"
" BlendOut ret;\n"
" ret.colorOut = vtf.color;\n"
2015-12-07 00:52:07 +00:00
" ret.blendOut = ret.colorOut.a * fontTex.Sample(samp, vtf.uv);\n"
2015-11-27 22:20:22 +00:00
" return ret;\n"
"}\n";
boo::VertexElementDescriptor vdescs[] =
{
{nullptr, nullptr, boo::VertexSemantic::Position4 | boo::VertexSemantic::Instanced, 0},
{nullptr, nullptr, boo::VertexSemantic::Position4 | boo::VertexSemantic::Instanced, 1},
{nullptr, nullptr, boo::VertexSemantic::Position4 | boo::VertexSemantic::Instanced, 2},
{nullptr, nullptr, boo::VertexSemantic::Position4 | boo::VertexSemantic::Instanced, 3},
{nullptr, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 0},
{nullptr, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 1},
{nullptr, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 2},
{nullptr, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 3},
{nullptr, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 0},
{nullptr, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 1},
{nullptr, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 2},
{nullptr, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 3},
{nullptr, nullptr, boo::VertexSemantic::Color | boo::VertexSemantic::Instanced}
};
m_vtxFmt = factory->newVertexFormat(13, vdescs);
ComPtr<ID3DBlob> blobVert;
ComPtr<ID3DBlob> blobFrag;
ComPtr<ID3DBlob> blobPipe;
m_regular =
factory->newShaderPipeline(VS, FSReg, blobVert, blobFrag, blobPipe, m_vtxFmt,
boo::BlendFactor::SrcAlpha, boo::BlendFactor::InvSrcAlpha,
false, false, false);
blobFrag.Reset();
blobPipe.Reset();
m_subpixel =
2015-11-27 22:55:21 +00:00
factory->newShaderPipeline(nullptr, FSSubpixel, blobVert, blobFrag, blobPipe, m_vtxFmt,
2015-11-27 22:20:22 +00:00
boo::BlendFactor::SrcColor1, boo::BlendFactor::InvSrcColor1,
false, false, false);
}
2015-11-28 04:05:27 +00:00
#elif BOO_HAS_METAL
2015-12-02 03:05:22 +00:00
void TextView::Resources::init(boo::MetalDataFactory* factory, FontCache* fcache)
2015-11-28 04:05:27 +00:00
{
m_fcache = fcache;
static const char* VS =
"#include <metal_stdlib>\n"
"using namespace metal;\n"
"struct VertData\n"
"{\n"
" float3 posIn[4];\n"
" float4x4 mvMtx;\n"
" float3 uvIn[4];\n"
" float4 colorIn;\n"
"};\n"
SPECTER_VIEW_VERT_BLOCK_METAL
"struct VertToFrag\n"
"{\n"
" float4 position [[ position ]];\n"
" float3 uv;\n"
" float4 color;\n"
"};\n"
"vertex VertToFrag vmain(constant VertData* va [[ buffer(1) ]],\n"
" uint vertId [[ vertex_id ]], uint instId [[ instance_id ]],\n"
" constant SpecterViewBlock& view [[ buffer(2) ]])\n"
"{\n"
" VertToFrag vtf;\n"
" constant VertData& v = va[instId];\n"
" vtf.uv = v.uvIn[vertId];\n"
" vtf.color = v.colorIn;\n"
" vtf.position = view.mv * v.mvMtx * float4(v.posIn[vertId], 1.0);\n"
" return vtf;\n"
"}\n";
static const char* FSReg =
"#include <metal_stdlib>\n"
"using namespace metal;\n"
"constexpr sampler samp(address::repeat);\n"
"struct VertToFrag\n"
"{\n"
" float4 position [[ position ]];\n"
" float3 uv;\n"
" float4 color;\n"
"};\n"
"fragment float4 fmain(VertToFrag vtf [[ stage_in ]], texture2d_array<float> fontTex [[ texture(0) ]])\n"
"{\n"
" float4 colorOut = vtf.color;\n"
2015-12-07 00:52:07 +00:00
" colorOut.a *= fontTex.sample(samp, vtf.uv.xy, vtf.uv.z).r;\n"
2015-11-28 04:05:27 +00:00
" return colorOut;\n"
"}\n";
boo::VertexElementDescriptor vdescs[] =
{
{nullptr, nullptr, boo::VertexSemantic::Position4 | boo::VertexSemantic::Instanced, 0},
{nullptr, nullptr, boo::VertexSemantic::Position4 | boo::VertexSemantic::Instanced, 1},
{nullptr, nullptr, boo::VertexSemantic::Position4 | boo::VertexSemantic::Instanced, 2},
{nullptr, nullptr, boo::VertexSemantic::Position4 | boo::VertexSemantic::Instanced, 3},
{nullptr, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 0},
{nullptr, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 1},
{nullptr, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 2},
{nullptr, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 3},
{nullptr, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 0},
{nullptr, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 1},
{nullptr, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 2},
{nullptr, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 3},
{nullptr, nullptr, boo::VertexSemantic::Color | boo::VertexSemantic::Instanced}
};
m_vtxFmt = factory->newVertexFormat(13, vdescs);
m_regular =
factory->newShaderPipeline(VS, FSReg, m_vtxFmt, 1,
boo::BlendFactor::SrcAlpha, boo::BlendFactor::InvSrcAlpha,
false, false, false);
}
2015-11-28 01:40:59 +00:00
#endif
2015-11-27 22:20:22 +00:00
2015-12-05 00:42:46 +00:00
TextView::TextView(ViewResources& res, View& parentView, const FontAtlas& font, size_t capacity)
: View(res, parentView),
2015-11-26 07:35:43 +00:00
m_capacity(capacity),
2015-11-29 02:55:30 +00:00
m_fontAtlas(font)
2015-11-25 01:46:30 +00:00
{
m_glyphBuf =
2015-12-05 00:42:46 +00:00
res.m_factory->newDynamicBuffer(boo::BufferUse::Vertex,
sizeof(RenderGlyph), capacity);
2015-11-25 01:46:30 +00:00
2015-11-29 02:55:30 +00:00
boo::IShaderPipeline* shader;
if (font.subpixel())
2015-12-05 00:42:46 +00:00
shader = res.m_textRes.m_subpixel;
2015-11-29 02:55:30 +00:00
else
2015-12-05 00:42:46 +00:00
shader = res.m_textRes.m_regular;
2015-11-29 02:55:30 +00:00
2015-12-05 00:42:46 +00:00
if (!res.m_textRes.m_vtxFmt)
2015-11-25 01:46:30 +00:00
{
boo::VertexElementDescriptor vdescs[] =
{
2015-11-26 00:24:01 +00:00
{m_glyphBuf, nullptr, boo::VertexSemantic::Position4 | boo::VertexSemantic::Instanced, 0},
{m_glyphBuf, nullptr, boo::VertexSemantic::Position4 | boo::VertexSemantic::Instanced, 1},
{m_glyphBuf, nullptr, boo::VertexSemantic::Position4 | boo::VertexSemantic::Instanced, 2},
{m_glyphBuf, nullptr, boo::VertexSemantic::Position4 | boo::VertexSemantic::Instanced, 3},
2015-11-25 01:46:30 +00:00
{m_glyphBuf, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 0},
{m_glyphBuf, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 1},
{m_glyphBuf, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 2},
{m_glyphBuf, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 3},
{m_glyphBuf, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 0},
{m_glyphBuf, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 1},
{m_glyphBuf, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 2},
{m_glyphBuf, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 3},
{m_glyphBuf, nullptr, boo::VertexSemantic::Color | boo::VertexSemantic::Instanced}
};
2015-12-05 00:42:46 +00:00
m_vtxFmt = res.m_factory->newVertexFormat(13, vdescs);
2015-11-26 07:35:43 +00:00
boo::ITexture* texs[] = {m_fontAtlas.texture()};
2015-12-05 00:42:46 +00:00
m_shaderBinding = res.m_factory->newShaderDataBinding(shader, m_vtxFmt,
nullptr, m_glyphBuf, nullptr, 1,
(boo::IGraphicsBuffer**)&m_viewVertBlockBuf,
1, texs);
2015-11-27 22:20:22 +00:00
}
else
{
boo::ITexture* texs[] = {m_fontAtlas.texture()};
2015-12-05 00:42:46 +00:00
m_shaderBinding = res.m_factory->newShaderDataBinding(shader, res.m_textRes.m_vtxFmt,
nullptr, m_glyphBuf, nullptr, 1,
(boo::IGraphicsBuffer**)&m_viewVertBlockBuf,
1, texs);
2015-11-25 01:46:30 +00:00
}
2015-11-26 07:35:43 +00:00
m_glyphs.reserve(capacity);
2015-12-05 00:42:46 +00:00
commitResources(res);
2015-11-25 01:46:30 +00:00
}
2015-12-05 00:42:46 +00:00
TextView::TextView(ViewResources& res, View& parentView, FontTag font, size_t capacity)
: TextView(res, parentView, res.m_textRes.m_fcache->lookupAtlas(font), capacity) {}
2015-11-29 02:55:30 +00:00
2015-11-26 07:35:43 +00:00
TextView::RenderGlyph::RenderGlyph(int& adv, const FontAtlas::Glyph& glyph, const Zeus::CColor& defaultColor)
2015-11-26 00:24:01 +00:00
{
m_pos[0].assign(adv + glyph.m_leftPadding, glyph.m_verticalOffset + glyph.m_height, 0.f);
m_pos[1].assign(adv + glyph.m_leftPadding, glyph.m_verticalOffset, 0.f);
m_pos[2].assign(adv + glyph.m_leftPadding + glyph.m_width, glyph.m_verticalOffset + glyph.m_height, 0.f);
m_pos[3].assign(adv + glyph.m_leftPadding + glyph.m_width, glyph.m_verticalOffset, 0.f);
m_uv[0].assign(glyph.m_uv[0], glyph.m_uv[1], glyph.m_layerFloat);
m_uv[1].assign(glyph.m_uv[0], glyph.m_uv[3], glyph.m_layerFloat);
m_uv[2].assign(glyph.m_uv[2], glyph.m_uv[1], glyph.m_layerFloat);
m_uv[3].assign(glyph.m_uv[2], glyph.m_uv[3], glyph.m_layerFloat);
m_color = defaultColor;
adv += glyph.m_advance;
}
2015-12-07 00:52:07 +00:00
int TextView::DoKern(FT_Pos val, const FontAtlas& atlas)
2015-11-26 07:35:43 +00:00
{
2015-12-02 06:13:43 +00:00
if (!val) return 0;
2015-11-26 07:35:43 +00:00
val = FT_MulFix(val, atlas.FT_Xscale());
FT_Pos orig_x = val;
/* we scale down kerning values for small ppem values */
/* to avoid that rounding makes them too big. */
/* `25' has been determined heuristically. */
if (atlas.FT_XPPem() < 25)
val = FT_MulDiv(orig_x, atlas.FT_XPPem(), 25);
2015-11-26 23:03:56 +00:00
return FT_PIX_ROUND(val) >> 6;
2015-11-26 07:35:43 +00:00
}
void TextView::typesetGlyphs(const std::string& str, const Zeus::CColor& defaultColor)
2015-11-25 01:46:30 +00:00
{
size_t rem = str.size();
2015-11-26 00:24:01 +00:00
const utf8proc_uint8_t* it = reinterpret_cast<const utf8proc_uint8_t*>(str.data());
2015-12-02 06:13:43 +00:00
uint32_t lCh = -1;
2015-11-26 00:24:01 +00:00
m_glyphs.clear();
m_glyphs.reserve(str.size());
m_glyphDims.clear();
m_glyphDims.reserve(str.size());
2015-11-26 00:24:01 +00:00
int adv = 0;
2015-11-25 01:46:30 +00:00
while (rem)
{
utf8proc_int32_t ch;
utf8proc_ssize_t sz = utf8proc_iterate(it, -1, &ch);
if (sz < 0)
Log.report(LogVisor::FatalError, "invalid UTF-8 char");
2015-11-26 00:24:01 +00:00
if (ch == '\n')
break;
2015-11-25 01:46:30 +00:00
2015-11-26 00:24:01 +00:00
const FontAtlas::Glyph* glyph = m_fontAtlas.lookupGlyph(ch);
if (!glyph)
{
rem -= sz;
it += sz;
continue;
}
if (lCh != -1)
2015-12-02 06:13:43 +00:00
adv += DoKern(m_fontAtlas.lookupKern(lCh, glyph->m_glyphIdx), m_fontAtlas);
2015-11-28 21:45:38 +00:00
m_glyphs.emplace_back(adv, *glyph, defaultColor);
m_glyphDims.emplace_back(glyph->m_width, glyph->m_height);
2015-11-25 01:46:30 +00:00
2015-12-02 06:13:43 +00:00
lCh = glyph->m_glyphIdx;
2015-11-25 01:46:30 +00:00
rem -= sz;
it += sz;
2015-11-26 07:35:43 +00:00
if (m_glyphs.size() == m_capacity)
break;
2015-11-25 01:46:30 +00:00
}
2015-11-26 00:24:01 +00:00
2015-12-05 00:42:46 +00:00
m_width = adv;
2015-12-02 21:11:50 +00:00
m_valid = false;
2015-12-05 00:42:46 +00:00
updateSize();
2015-11-25 01:46:30 +00:00
}
2015-12-07 00:52:07 +00:00
2015-11-26 07:35:43 +00:00
void TextView::typesetGlyphs(const std::wstring& str, const Zeus::CColor& defaultColor)
2015-11-25 01:46:30 +00:00
{
2015-12-02 06:13:43 +00:00
uint32_t lCh = -1;
2015-11-26 00:24:01 +00:00
m_glyphs.clear();
m_glyphs.reserve(str.size());
m_glyphDims.clear();
m_glyphDims.reserve(str.size());
2015-11-26 00:24:01 +00:00
int adv = 0;
for (wchar_t ch : str)
{
if (ch == L'\n')
break;
const FontAtlas::Glyph* glyph = m_fontAtlas.lookupGlyph(ch);
if (!glyph)
continue;
if (lCh != -1)
2015-12-02 06:13:43 +00:00
adv += DoKern(m_fontAtlas.lookupKern(lCh, glyph->m_glyphIdx), m_fontAtlas);
2015-11-28 21:45:38 +00:00
m_glyphs.emplace_back(adv, *glyph, defaultColor);
m_glyphDims.emplace_back(glyph->m_width, glyph->m_height);
2015-11-26 00:24:01 +00:00
2015-12-02 06:13:43 +00:00
lCh = glyph->m_glyphIdx;
2015-11-26 07:35:43 +00:00
if (m_glyphs.size() == m_capacity)
break;
2015-11-26 00:24:01 +00:00
}
2015-12-05 00:42:46 +00:00
m_width = adv;
2015-12-02 21:11:50 +00:00
m_valid = false;
2015-12-05 00:42:46 +00:00
updateSize();
2015-11-25 01:46:30 +00:00
}
2015-11-26 07:35:43 +00:00
void TextView::colorGlyphs(const Zeus::CColor& newColor)
2015-11-25 01:46:30 +00:00
{
2015-11-26 07:35:43 +00:00
for (RenderGlyph& glyph : m_glyphs)
glyph.m_color = newColor;
2015-12-02 21:11:50 +00:00
m_valid = false;
2015-11-25 01:46:30 +00:00
}
2015-11-26 07:35:43 +00:00
void TextView::colorGlyphsTypeOn(const Zeus::CColor& newColor, float startInterval, float fadeTime)
2015-11-25 01:46:30 +00:00
{
}
void TextView::think()
{
}
2015-12-05 00:42:46 +00:00
void TextView::resized(const boo::SWindowRect &rootView, const boo::SWindowRect& sub)
{
View::resized(rootView, sub);
}
2015-11-25 01:46:30 +00:00
void TextView::draw(boo::IGraphicsCommandQueue* gfxQ)
{
2015-11-26 07:35:43 +00:00
View::draw(gfxQ);
2015-11-30 03:41:53 +00:00
if (m_glyphs.size())
2015-11-26 00:24:01 +00:00
{
2015-12-02 21:11:50 +00:00
if (!m_valid)
{
m_glyphBuf->load(m_glyphs.data(), m_glyphs.size() * sizeof(RenderGlyph));
2015-12-02 21:11:50 +00:00
m_valid = true;
}
gfxQ->setShaderDataBinding(m_shaderBinding);
gfxQ->setDrawPrimitive(boo::Primitive::TriStrips);
2015-11-30 03:41:53 +00:00
gfxQ->drawInstances(0, 4, m_glyphs.size());
2015-11-26 00:24:01 +00:00
}
2015-11-25 01:46:30 +00:00
}
std::pair<int,int> TextView::queryGlyphDimensions(size_t pos) const
{
if (pos >= m_glyphDims.size())
Log.report(LogVisor::FatalError,
"TextView::queryGlyphWidth(%" PRISize ") out of bounds: %" PRISize,
pos, m_glyphDims.size());
return m_glyphDims[pos];
}
2015-11-21 23:45:02 +00:00
}