font renderer: exposed cursor offset; backlog: colored loglevel;
This commit is contained in:
+60
-21
@@ -22,15 +22,20 @@ using namespace wi::graphics;
|
||||
namespace wi::backlog
|
||||
{
|
||||
bool enabled = false;
|
||||
std::deque<std::string> stream;
|
||||
std::deque<std::string> history;
|
||||
struct LogEntry
|
||||
{
|
||||
std::string text;
|
||||
LogLevel level = LogLevel::Default;
|
||||
};
|
||||
std::deque<LogEntry> entries;
|
||||
std::deque<LogEntry> history;
|
||||
const float speed = 4000.0f;
|
||||
const size_t deletefromline = 500;
|
||||
float pos = std::numeric_limits<float>::lowest();
|
||||
float scroll = 0;
|
||||
std::string inputArea;
|
||||
int historyPos = 0;
|
||||
wi::SpriteFont font;
|
||||
wi::font::Params font_params;
|
||||
wi::SpinLock logLock;
|
||||
Texture backgroundTex;
|
||||
bool refitscroll = false;
|
||||
@@ -123,32 +128,60 @@ namespace wi::backlog
|
||||
fx.opacity = wi::math::Lerp(1, 0, -pos / canvas.GetLogicalHeight());
|
||||
wi::image::Draw(&backgroundTex, fx, cmd);
|
||||
|
||||
fx.pos = XMFLOAT3(5, canvas.GetLogicalHeight() - 30, 0);
|
||||
fx.siz = XMFLOAT2(canvas.GetLogicalWidth() - 10, 25);
|
||||
fx.color = XMFLOAT4(1, 1, 1, 0.2f);
|
||||
wi::image::Draw(wi::texturehelper::getWhite(), fx, cmd);
|
||||
|
||||
wi::font::Params params = wi::font::Params(10, canvas.GetLogicalHeight() - 10, wi::font::WIFONTSIZE_DEFAULT, wi::font::WIFALIGN_LEFT, wi::font::WIFALIGN_BOTTOM);
|
||||
params.h_wrap = canvas.GetLogicalWidth() - params.posX;
|
||||
params.v_align = wi::font::WIFALIGN_BOTTOM;
|
||||
params.shadowColor = 0x10000000u;
|
||||
params.shadow_offset_x = 2;
|
||||
params.shadow_offset_y = 2;
|
||||
params.shadow_softness = 1;
|
||||
wi::font::Draw(inputArea, params, cmd);
|
||||
|
||||
font.SetText(getText());
|
||||
font_params.cursor = XMFLOAT2(0, 0);
|
||||
params.shadowColor = 0x10000000u;
|
||||
params.shadow_offset_x = 2;
|
||||
params.shadow_offset_y = 2;
|
||||
params.shadow_softness = 1;
|
||||
if (refitscroll)
|
||||
{
|
||||
refitscroll = false;
|
||||
float textheight = font.TextHeight();
|
||||
float textheight = wi::font::TextHeight(getText(), font_params);
|
||||
float limit = canvas.GetLogicalHeight() * 0.9f;
|
||||
if (scroll + textheight > limit)
|
||||
{
|
||||
scroll = limit - textheight;
|
||||
}
|
||||
}
|
||||
font.params.posX = 50;
|
||||
font.params.posY = pos + scroll;
|
||||
font.params.h_wrap = canvas.GetLogicalWidth() - font.params.posX;
|
||||
font_params.posX = 50;
|
||||
font_params.posY = pos + scroll;
|
||||
font_params.h_wrap = canvas.GetLogicalWidth() - font_params.posX;
|
||||
Rect rect;
|
||||
rect.left = 0;
|
||||
rect.right = (int32_t)canvas.GetPhysicalWidth();
|
||||
rect.top = 0;
|
||||
rect.bottom = int32_t(canvas.GetPhysicalHeight() * 0.9f);
|
||||
rect.bottom = int32_t(canvas.LogicalToPhysical(canvas.GetLogicalHeight() - 35));
|
||||
wi::graphics::GetDevice()->BindScissorRects(1, &rect, cmd);
|
||||
font.Draw(cmd);
|
||||
for (auto& x : entries)
|
||||
{
|
||||
switch (x.level)
|
||||
{
|
||||
case LogLevel::Warning:
|
||||
font_params.color = 0xFF66FFFF; // light yellow
|
||||
break;
|
||||
case LogLevel::Error:
|
||||
font_params.color = 0xFF6666FF; // light red
|
||||
break;
|
||||
default:
|
||||
font_params.color = wi::Color::White();
|
||||
break;
|
||||
}
|
||||
font_params.cursor = wi::font::Draw(x.text, font_params, cmd);
|
||||
}
|
||||
rect.left = -std::numeric_limits<int>::max();
|
||||
rect.right = std::numeric_limits<int>::max();
|
||||
rect.top = -std::numeric_limits<int>::max();
|
||||
@@ -162,16 +195,16 @@ namespace wi::backlog
|
||||
{
|
||||
std::scoped_lock lock(logLock);
|
||||
std::string retval;
|
||||
for (auto& x : stream)
|
||||
for (auto& x : entries)
|
||||
{
|
||||
retval += x;
|
||||
retval += x.text;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
void clear()
|
||||
{
|
||||
std::scoped_lock lock(logLock);
|
||||
stream.clear();
|
||||
entries.clear();
|
||||
scroll = 0;
|
||||
}
|
||||
void post(const std::string& input, LogLevel level)
|
||||
@@ -201,10 +234,13 @@ namespace wi::backlog
|
||||
}
|
||||
str += input;
|
||||
str += '\n';
|
||||
stream.push_back(str);
|
||||
if (stream.size() > deletefromline)
|
||||
LogEntry entry;
|
||||
entry.text = str;
|
||||
entry.level = level;
|
||||
entries.push_back(entry);
|
||||
if (entries.size() > deletefromline)
|
||||
{
|
||||
stream.pop_front();
|
||||
entries.pop_front();
|
||||
}
|
||||
refitscroll = true;
|
||||
|
||||
@@ -243,7 +279,10 @@ namespace wi::backlog
|
||||
{
|
||||
historyPos = 0;
|
||||
post(inputArea.c_str());
|
||||
history.push_back(inputArea);
|
||||
LogEntry entry;
|
||||
entry.text = inputArea;
|
||||
entry.level = LogLevel::Default;
|
||||
history.push_back(entry);
|
||||
if (history.size() > deletefromline)
|
||||
{
|
||||
history.pop_front();
|
||||
@@ -272,7 +311,7 @@ namespace wi::backlog
|
||||
std::scoped_lock lock(logLock);
|
||||
if (!history.empty())
|
||||
{
|
||||
inputArea = history[history.size() - 1 - historyPos];
|
||||
inputArea = history[history.size() - 1 - historyPos].text;
|
||||
if ((size_t)historyPos < history.size() - 1)
|
||||
{
|
||||
historyPos++;
|
||||
@@ -288,7 +327,7 @@ namespace wi::backlog
|
||||
{
|
||||
historyPos--;
|
||||
}
|
||||
inputArea = history[history.size() - 1 - historyPos];
|
||||
inputArea = history[history.size() - 1 - historyPos].text;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,11 +337,11 @@ namespace wi::backlog
|
||||
}
|
||||
void setFontSize(int value)
|
||||
{
|
||||
font.params.size = value;
|
||||
font_params.size = value;
|
||||
}
|
||||
void setFontRowspacing(float value)
|
||||
{
|
||||
font.params.spacingY = value;
|
||||
font_params.spacingY = value;
|
||||
}
|
||||
|
||||
bool isActive() { return enabled; }
|
||||
|
||||
@@ -37,6 +37,10 @@ namespace wi
|
||||
inline float GetDPI() const { return dpi * scaling; }
|
||||
// The scaling factor between logical and physical coordinates
|
||||
inline float GetDPIScaling() const { return GetDPI() / 96.f; }
|
||||
// Convert from logical to physical coordinates
|
||||
inline uint32_t LogicalToPhysical(float logical) const { return uint32_t(logical * GetDPIScaling()); }
|
||||
// Convert from physical to logical coordinates
|
||||
inline float PhysicalToLogical(uint32_t physical) const { return float(physical) / GetDPIScaling(); }
|
||||
// Returns native resolution width in pixels:
|
||||
// Use this for texture allocations
|
||||
// Use this for scissor, viewport
|
||||
@@ -47,10 +51,10 @@ namespace wi
|
||||
inline uint32_t GetPhysicalHeight() const { return height; }
|
||||
// Returns the width with DPI scaling applied (subpixel size):
|
||||
// Use this for logic and positioning drawable elements
|
||||
inline float GetLogicalWidth() const { return GetPhysicalWidth() / GetDPIScaling(); }
|
||||
inline float GetLogicalWidth() const { return PhysicalToLogical(GetPhysicalWidth()); }
|
||||
// Returns the height with DPI scaling applied (subpixel size):
|
||||
// Use this for logic and positioning drawable elements
|
||||
inline float GetLogicalHeight() const { return GetPhysicalHeight() / GetDPIScaling(); }
|
||||
inline float GetLogicalHeight() const { return PhysicalToLogical(GetPhysicalHeight()); }
|
||||
// Returns projection matrix that maps logical to physical space
|
||||
// Use this to render to a graphics viewport
|
||||
inline XMMATRIX GetProjection() const
|
||||
|
||||
+34
-30
@@ -126,6 +126,8 @@ namespace wi::font
|
||||
Cursor ParseText(const T* text, size_t text_length, Params params)
|
||||
{
|
||||
Cursor cursor;
|
||||
cursor.pos = params.cursor;
|
||||
|
||||
const FontStyle& fontStyle = fontStyles[params.style];
|
||||
const float fontScale = stbtt_ScaleForPixelHeight(&fontStyle.fontInfo, (float)params.size);
|
||||
vertexList.clear();
|
||||
@@ -135,7 +137,7 @@ namespace wi::font
|
||||
if (cursor.last_word_begin > 0 && params.h_wrap >= 0 && cursor.pos.x >= params.h_wrap - 1)
|
||||
{
|
||||
// Word ended and wrap detected, push down last word by one line:
|
||||
float word_offset = vertexList[cursor.last_word_begin].pos.x;
|
||||
float word_offset = vertexList[cursor.last_word_begin].pos.x + WHITESPACE_SIZE;
|
||||
for (size_t i = cursor.last_word_begin; i < cursor.quadCount * 4; ++i)
|
||||
{
|
||||
vertexList[i].pos.x -= word_offset;
|
||||
@@ -146,7 +148,7 @@ namespace wi::font
|
||||
}
|
||||
};
|
||||
|
||||
cursor.size.y = LINEBREAK_SIZE;
|
||||
cursor.size.y = cursor.pos.y + LINEBREAK_SIZE;
|
||||
for (size_t i = 0; i < text_length; ++i)
|
||||
{
|
||||
T character = text[i];
|
||||
@@ -431,23 +433,23 @@ namespace wi::font
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Draw_internal(const T* text, size_t text_length, const Params& params, CommandList cmd)
|
||||
XMFLOAT2 Draw_internal(const T* text, size_t text_length, const Params& params_in, CommandList cmd)
|
||||
{
|
||||
if (text_length <= 0)
|
||||
{
|
||||
return;
|
||||
return XMFLOAT2(0, 0);
|
||||
}
|
||||
Cursor cursor = ParseText(text, text_length, params);
|
||||
Cursor cursor = ParseText(text, text_length, params_in);
|
||||
|
||||
Params newProps = params;
|
||||
Params params = params_in;
|
||||
if (params.h_align == WIFALIGN_CENTER)
|
||||
newProps.posX -= cursor.size.x / 2;
|
||||
params.posX -= cursor.size.x / 2;
|
||||
else if (params.h_align == WIFALIGN_RIGHT)
|
||||
newProps.posX -= cursor.size.x;
|
||||
params.posX -= cursor.size.x;
|
||||
if (params.v_align == WIFALIGN_CENTER)
|
||||
newProps.posY -= cursor.size.y / 2;
|
||||
params.posY -= cursor.size.y / 2;
|
||||
else if (params.v_align == WIFALIGN_BOTTOM)
|
||||
newProps.posY -= cursor.size.y;
|
||||
params.posY -= cursor.size.y;
|
||||
|
||||
|
||||
if (cursor.quadCount > 0)
|
||||
@@ -456,7 +458,7 @@ namespace wi::font
|
||||
GraphicsDevice::GPUAllocation mem = device->AllocateGPU(sizeof(FontVertex) * cursor.quadCount * 4, cmd);
|
||||
if (!mem.IsValid())
|
||||
{
|
||||
return;
|
||||
return cursor.pos;
|
||||
}
|
||||
CommitText(mem.data);
|
||||
|
||||
@@ -476,16 +478,16 @@ namespace wi::font
|
||||
assert(canvas.dpi > 0);
|
||||
const XMMATRIX Projection = canvas.GetProjection();
|
||||
|
||||
if (newProps.shadowColor.getA() > 0)
|
||||
if (params.shadowColor.getA() > 0)
|
||||
{
|
||||
// font shadow render:
|
||||
XMStoreFloat4x4(&font.transform,
|
||||
XMMatrixTranslation((float)newProps.posX + newProps.shadow_offset_x, (float)newProps.posY + newProps.shadow_offset_y, 0)
|
||||
XMMatrixTranslation((float)params.posX + params.shadow_offset_x, (float)params.posY + params.shadow_offset_y, 0)
|
||||
* Projection
|
||||
);
|
||||
font.color = newProps.shadowColor.rgba;
|
||||
font.sdf_threshold_top = wi::math::Lerp(float(SDF::onedge_value) / 255.0f, 0, std::max(0.0f, newProps.shadow_bolden));
|
||||
font.sdf_threshold_bottom = wi::math::Lerp(font.sdf_threshold_top, 0, std::max(0.0f, newProps.shadow_softness));
|
||||
font.color = params.shadowColor.rgba;
|
||||
font.sdf_threshold_top = wi::math::Lerp(float(SDF::onedge_value) / 255.0f, 0, std::max(0.0f, params.shadow_bolden));
|
||||
font.sdf_threshold_bottom = wi::math::Lerp(font.sdf_threshold_top, 0, std::max(0.0f, params.shadow_softness));
|
||||
device->BindDynamicConstantBuffer(font, CBSLOT_FONT, cmd);
|
||||
|
||||
device->DrawInstanced(4, cursor.quadCount, 0, 0, cmd);
|
||||
@@ -493,18 +495,20 @@ namespace wi::font
|
||||
|
||||
// font base render:
|
||||
XMStoreFloat4x4(&font.transform,
|
||||
XMMatrixTranslation((float)newProps.posX, (float)newProps.posY, 0)
|
||||
XMMatrixTranslation((float)params.posX, (float)params.posY, 0)
|
||||
* Projection
|
||||
);
|
||||
font.color = newProps.color.rgba;
|
||||
font.sdf_threshold_top = wi::math::Lerp(float(SDF::onedge_value) / 255.0f, 0, std::max(0.0f, newProps.bolden));
|
||||
font.sdf_threshold_bottom = wi::math::Lerp(font.sdf_threshold_top, 0, std::max(0.0f, newProps.softness));
|
||||
font.color = params.color.rgba;
|
||||
font.sdf_threshold_top = wi::math::Lerp(float(SDF::onedge_value) / 255.0f, 0, std::max(0.0f, params.bolden));
|
||||
font.sdf_threshold_bottom = wi::math::Lerp(font.sdf_threshold_top, 0, std::max(0.0f, params.softness));
|
||||
device->BindDynamicConstantBuffer(font, CBSLOT_FONT, cmd);
|
||||
|
||||
device->DrawInstanced(4, cursor.quadCount, 0, 0, cmd);
|
||||
|
||||
device->EventEnd(cmd);
|
||||
}
|
||||
|
||||
return cursor.pos;
|
||||
}
|
||||
|
||||
void SetCanvas(const wi::Canvas& current_canvas)
|
||||
@@ -512,31 +516,31 @@ namespace wi::font
|
||||
canvas = current_canvas;
|
||||
}
|
||||
|
||||
void Draw(const char* text, const Params& params, CommandList cmd)
|
||||
XMFLOAT2 Draw(const char* text, const Params& params, CommandList cmd)
|
||||
{
|
||||
size_t text_length = strlen(text);
|
||||
if (text_length == 0)
|
||||
{
|
||||
return;
|
||||
return XMFLOAT2(0, 0);
|
||||
}
|
||||
Draw_internal(text, text_length, params, cmd);
|
||||
return Draw_internal(text, text_length, params, cmd);
|
||||
}
|
||||
void Draw(const wchar_t* text, const Params& params, CommandList cmd)
|
||||
XMFLOAT2 Draw(const wchar_t* text, const Params& params, CommandList cmd)
|
||||
{
|
||||
size_t text_length = wcslen(text);
|
||||
if (text_length == 0)
|
||||
{
|
||||
return;
|
||||
return XMFLOAT2(0, 0);
|
||||
}
|
||||
Draw_internal(text, text_length, params, cmd);
|
||||
return Draw_internal(text, text_length, params, cmd);
|
||||
}
|
||||
void Draw(const std::string& text, const Params& params, CommandList cmd)
|
||||
XMFLOAT2 Draw(const std::string& text, const Params& params, CommandList cmd)
|
||||
{
|
||||
Draw_internal(text.c_str(), text.length(), params, cmd);
|
||||
return Draw_internal(text.c_str(), text.length(), params, cmd);
|
||||
}
|
||||
void Draw(const std::wstring& text, const Params& params, CommandList cmd)
|
||||
XMFLOAT2 Draw(const std::wstring& text, const Params& params, CommandList cmd)
|
||||
{
|
||||
Draw_internal(text.c_str(), text.length(), params, cmd);
|
||||
return Draw_internal(text.c_str(), text.length(), params, cmd);
|
||||
}
|
||||
|
||||
XMFLOAT2 TextSize(const char* text, const Params& params)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "wiGraphicsDevice.h"
|
||||
#include "wiColor.h"
|
||||
#include "wiCanvas.h"
|
||||
#include "wiMath.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -38,6 +39,7 @@ namespace wi::font
|
||||
float shadow_bolden = 0.1f; // value in [0,1] range
|
||||
float shadow_offset_x = 0; // offset for shadow under the text in logical canvas coordinates
|
||||
float shadow_offset_y = 0; // offset for shadow under the text in logical canvas coordinates
|
||||
XMFLOAT2 cursor = XMFLOAT2(0, 0); // cursor offset, it can be used to continue text drawing by taking the Draw's return value (optional)
|
||||
|
||||
Params(
|
||||
float posX = 0,
|
||||
@@ -83,10 +85,11 @@ namespace wi::font
|
||||
// Call once per frame to update font atlas texture
|
||||
void UpdateAtlas();
|
||||
|
||||
void Draw(const char* text, const Params& params, wi::graphics::CommandList cmd);
|
||||
void Draw(const wchar_t* text, const Params& params, wi::graphics::CommandList cmd);
|
||||
void Draw(const std::string& text, const Params& params, wi::graphics::CommandList cmd);
|
||||
void Draw(const std::wstring& text, const Params& params, wi::graphics::CommandList cmd);
|
||||
// Draw text with specified parameters and return cursor for last word
|
||||
XMFLOAT2 Draw(const char* text, const Params& params, wi::graphics::CommandList cmd);
|
||||
XMFLOAT2 Draw(const wchar_t* text, const Params& params, wi::graphics::CommandList cmd);
|
||||
XMFLOAT2 Draw(const std::string& text, const Params& params, wi::graphics::CommandList cmd);
|
||||
XMFLOAT2 Draw(const std::wstring& text, const Params& params, wi::graphics::CommandList cmd);
|
||||
|
||||
XMFLOAT2 TextSize(const char* text, const Params& params);
|
||||
XMFLOAT2 TextSize(const wchar_t* text, const Params& params);
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace wi::version
|
||||
// minor features, major updates, breaking compatibility changes
|
||||
const int minor = 60;
|
||||
// minor bug fixes, alterations, refactors, updates
|
||||
const int revision = 77;
|
||||
const int revision = 78;
|
||||
|
||||
const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user