Commit 3c8d8ce7 by NTAuthority

my weird font renderer from Cfx (which currently is very Windows-specific, sadly…

my weird font renderer from Cfx (which currently is very Windows-specific, sadly Pango is weird to build so I didn't opt to change it to that) and a graphical console
parent aec7ea3c
Pipeline #77 skipped
......@@ -52,6 +52,8 @@ class ConsoleCommandManager
void Invoke(const std::string& commandString);
void ForAllCommands(const std::function<void(const std::string&)>& callback);
private:
struct Entry
{
......
......@@ -106,6 +106,20 @@ void ConsoleCommandManager::Invoke(const std::string& commandName, const Program
}
}
void ConsoleCommandManager::ForAllCommands(const std::function<void(const std::string&)>& callback)
{
{
// lock the mutex
auto lock = shared_lock_acquire<std::shared_timed_mutex>(m_mutex);
// loop through the commands
for (auto& command : m_entries)
{
callback(command.first);
}
}
}
ConsoleCommandManager* ConsoleCommandManager::GetDefaultInstance()
{
return console::GetDefaultContext()->GetCommandManager();
......
......@@ -17,6 +17,8 @@ public:
virtual void ProcessEventsOnce() = 0;
virtual void GetMetrics(int& width, int& height) = 0;
public:
static std::unique_ptr<GameWindow> Create(const std::string& title, int defaultWidth, int defaultHeight, EventSystem* eventSystem);
};
......
#pragma once
#ifdef DrawText
#undef DrawText
#endif
#include <fonts/Rect.h>
#include <fonts/RGBA.h>
#include <fonts/FontRendererAbstract.h>
namespace krt
{
class FontRenderer
{
public:
virtual void Initialize(FontRendererGameInterface* gameInterface) = 0;
virtual void DrawText(const std::string& text, const Rect& rect, const RGBA& color, float fontSize, float fontScale, const std::string& fontRef) = 0;
virtual void DrawRectangle(const Rect& rect, const RGBA& color) = 0;
virtual void DrawPerFrame() = 0;
virtual bool GetStringMetrics(const std::string& characterString, float fontSize, float fontScale, const std::string& fontRef, Rect* outRect) = 0;
};
class GameWindow;
FontRendererGameInterface* CreateGameInterface(GameWindow* gameWindow);
extern FontRenderer* TheFonts;
}
\ No newline at end of file
#pragma once
#include "RGBA.h"
#include "FontRendererAllocator.h"
namespace krt
{
enum class FontRendererTextureFormat
{
DXT5,
ARGB
};
class FontRendererTexture
{
public:
virtual ~FontRendererTexture() = 0;
};
struct FontRendererVertex : public FrpUseSequentialAllocator
{
float x;
float y;
float u;
float v;
RGBA color;
FontRendererVertex()
{
}
FontRendererVertex(float x, float y, float u, float v, RGBA color)
: x(x), y(y), u(u), v(v), color(color)
{
}
};
struct ResultingRectangle : public FrpUseSequentialAllocator
{
Rect rectangle;
RGBA color;
};
struct ResultingRectangles : public FrpUseSequentialAllocator
{
int count;
ResultingRectangle* rectangles;
};
class FontRendererGameInterface
{
public:
virtual FontRendererTexture* CreateTexture(int width, int height, FontRendererTextureFormat format, void* pixelData) = 0;
virtual void SetTexture(FontRendererTexture* texture) = 0;
virtual void DrawIndexedVertices(int numVertices, int numIndices, FontRendererVertex* vertices, uint16_t* indices) = 0;
virtual void DrawRectangles(int numRectangles, const ResultingRectangle* rectangles) = 0;
virtual void UnsetTexture() = 0;
virtual void InvokeOnRender(void(*cb)(void*), void* arg) = 0;
};
}
\ No newline at end of file
#pragma once
class FrpUseSequentialAllocator
{
public:
void* operator new[](size_t size);
void operator delete[](void* ptr);
};
void FrpSeqAllocatorWaitForSwap();
void FrpSeqAllocatorUnlockSwap();
void FrpSeqAllocatorSwapPage();
\ No newline at end of file
#pragma once
#include <dwrite.h>
#include <dwrite_2.h>
#include <wrl.h>
#include "FontRenderer.h"
#include "FontRendererAbstract.h"
#include <mutex>
using Microsoft::WRL::ComPtr;
namespace std
{
template<typename T1, typename T2>
struct hash<std::pair<T1, T2>>
{
std::size_t operator()(const std::pair<T1, T2>& x) const
{
return (3 * std::hash<T1>()(x.first)) ^ std::hash<T2>()(x.second);
}
};
}
namespace krt
{
class CachedFont;
class CachedFontPage
{
private:
struct FontCharacter
{
Rect address;
Rect affect;
uint8_t* pixelData;
};
private:
CachedFont* m_owningFont;
uint32_t m_minCodePoint;
uint32_t m_maxCodePoint;
bool m_enqueued;
std::vector<FontCharacter> m_characterAddresses;
void* m_pixelData;
FontRendererTexture* m_targetTexture;
private:
void CreateFontPage();
void EnqueueCreateFontPage();
void CreateNow();
void CreateCharacter(uint32_t codePoint);
public:
CachedFontPage(CachedFont* parent, uint32_t minCodePoint, uint32_t maxCodePoint);
bool EnsureFontPageCreated();
inline FontRendererTexture* GetTexture() { return m_targetTexture; }
const Rect& GetCharacterAddress(uint32_t codePoint);
const Rect& GetCharacterSize(uint32_t codePoint);
};
typedef FontRendererVertex ResultingVertex;
typedef uint16_t ResultingIndex;
struct ResultingSubGlyphRun : public FrpUseSequentialAllocator
{
FontRendererTexture* texture;
uint32_t numVertices;
uint32_t numIndices;
ResultingVertex* vertices;
ResultingIndex* indices;
ResultingSubGlyphRun();
~ResultingSubGlyphRun();
};
struct ResultingGlyphRun : public FrpUseSequentialAllocator
{
uint32_t numSubRuns;
ResultingSubGlyphRun* subRuns;
~ResultingGlyphRun();
};
class CachedFont
{
friend class CachedFontPage;
private:
std::map<uint32_t, std::shared_ptr<CachedFontPage>> m_pages;
std::vector<bool> m_presentPages;
ComPtr<IDWriteFontFace> m_dwFace;
float m_emSize;
private:
void CreateFace();
void TargetGlyphRunInternal(float originX, float originY, const DWRITE_GLYPH_RUN* glyphRun, ResultingSubGlyphRun* initialRuns, ResultingVertex* initialVertices, ResultingIndex* initialIndices, RGBA color, int& runCount);
public:
CachedFont(ComPtr<IDWriteFontFace> fontFace, float emSize);
bool EnsureFaceCreated();
ResultingGlyphRun* TargetGlyphRun(float originX, float originY, const DWRITE_GLYPH_RUN* glyphRun, const DWRITE_GLYPH_RUN_DESCRIPTION* glyphRunDescription, RGBA color);
};
struct FrDrawingContext
{
std::vector<ResultingGlyphRun*> glyphRuns;
float fontScale;
~FrDrawingContext();
};
class FrRenderable
{
public:
virtual ~FrRenderable() = default;
virtual void Render() = 0;
};
class FontRendererImpl : public FontRenderer
{
private:
ComPtr<IDWriteFactory> m_dwFactory;
ComPtr<IDWriteFactory2> m_dwFactory2;
ComPtr<IDWriteTextRenderer> m_textRenderer;
FontRendererGameInterface* m_gameInterface;
std::vector<std::unique_ptr<FrRenderable>> m_queuedRenderables;
//std::vector<ResultingGlyphRun*> m_queuedGlyphRuns;
//std::vector<ResultingRectangle> m_queuedRectangles;
std::unordered_map<std::string, std::shared_ptr<CachedFont>> m_fontCache;
std::unordered_map<std::pair<std::string, float>, ComPtr<IDWriteTextFormat>> m_textFormatCache;
std::unordered_map<std::pair<IDWriteTextFormat*, std::pair<uint32_t, std::string>>, ComPtr<IDWriteTextLayout>> m_textLayoutCache;
uint32_t m_lastLayoutClean;
std::recursive_mutex m_mutex;
private:
void CreateTextRenderer();
std::string GetFontKey(ComPtr<IDWriteFontFace> fontFace, float emSize);
public:
virtual void Initialize(FontRendererGameInterface* gameInterface);
virtual void DrawPerFrame();
std::shared_ptr<CachedFont> GetFontInstance(ComPtr<IDWriteFontFace> fontFace, float emSize);
inline FontRendererGameInterface* GetGameInterface() { return m_gameInterface; }
inline ComPtr<IDWriteFactory> GetDWriteFactory() { return m_dwFactory; }
inline ComPtr<IDWriteFactory2> GetDWriteFactory2() { return m_dwFactory2; }
virtual void DrawText(const std::string& text, const Rect& rect, const RGBA& color, float fontSize, float fontScale, const std::string& fontRef) override;
virtual void DrawRectangle(const Rect& rect, const RGBA& color) override;
virtual bool GetStringMetrics(const std::string& characterString, float fontSize, float fontScale, const std::string& fontRef, Rect* outRect) override;
};
// not entirely COM calling convention, but we'll only use it internally
// {71246052-4EEA-4339-BBC0-D2246A3F5CE3}
DEFINE_GUID(IID_IFrDrawingEffect,
0x71246052, 0x4eea, 0x4339, 0xbb, 0xc0, 0xd2, 0x24, 0x6a, 0x3f, 0x5c, 0xe3);
interface DECLSPEC_UUID("71246052-4EEA-4339-BBC0-D2246A3F5CE3") IFrDrawingEffect;
struct IFrDrawingEffect : public IUnknown
{
virtual RGBA GetColor() = 0;
virtual void SetColor(RGBA color) = 0;
};
class FrDrawingEffect : public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, IFrDrawingEffect>
{
private:
RGBA m_color;
public:
virtual RGBA GetColor() { return m_color; }
virtual void SetColor(RGBA color) { m_color = color; }
};
FontRendererGameInterface* CreateGameInterface();
extern FontRendererImpl g_fontRenderer;
}
\ No newline at end of file
#pragma once
namespace krt
{
struct RGBA
{
uint8_t red;
uint8_t green;
uint8_t blue;
uint8_t alpha;
inline RGBA(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
: red(r), green(g), blue(b), alpha(a)
{
}
inline RGBA()
: RGBA(0, 0, 0, 255)
{
}
inline RGBA(uint8_t r, uint8_t g, uint8_t b)
: RGBA(r, g, b, 255)
{
}
inline static RGBA FromFloat(float r, float g, float b, float a)
{
return RGBA((uint8_t)(r * 255.0f), (uint8_t)(g * 255.0f), (uint8_t)(b * 255.0f), (uint8_t)(a * 255.0f));
}
inline static RGBA FromARGB(uint32_t argb)
{
return RGBA((argb & 0xFF0000) >> 16, ((argb & 0xFF00) >> 8), argb & 0xFF, (argb & 0xFF000000) >> 24);
}
inline uint32_t AsARGB() const
{
return (alpha << 24) | (red << 16) | (green << 8) | blue;
}
};
}
\ No newline at end of file
#pragma once
#undef min
#undef max
namespace krt
{
///
/// A class to represent a floating-point rectangle.
///
class Rect
{
public:
/// The values for the rectangle.
float fX1;
float fY1;
float fX2;
float fY2;
public:
///
/// Constructor, initializing the rectangle with the passed values.
///
inline Rect(float x1, float y1, float x2, float y2)
: fX1(x1), fY1(y1), fX2(x2), fY2(y2)
{
}
///
/// Constructor, initializing the rectangle to be empty.
///
inline Rect()
: Rect(0, 0, 0, 0)
{
}
///
/// Returns the left edge of the rectangle.
///
inline float Left() const { return std::min(fX1, fX2); }
///
/// Returns the right edge of the rectangle.
///
inline float Right() const { return std::max(fX1, fX2); }
///
/// Returns the top edge of the rectangle.
///
inline float Top() const { return std::min(fY1, fY2); }
///
/// Returns the bottom edge of the rectangle.
///
inline float Bottom() const { return std::max(fY1, fY2); }
///
/// Returns the width of the rectangle.
///
inline float Width() const { return Right() - Left(); }
///
/// Returns the height of the rectangle.
///
inline float Height() const { return Bottom() - Top(); }
///
/// Overwrites the rectangle with the passed values.
///
inline void SetRect(float x1, float y1, float x2, float y2)
{
fX1 = x1;
fY1 = y1;
fX2 = x2;
fY2 = y2;
}
};
}
\ No newline at end of file
......@@ -177,6 +177,12 @@ class Win32GameWindow : public GameWindow
}
}
virtual void GetMetrics(int& width, int& height) override
{
width = m_width;
height = m_height;
}
private:
void ProcessMouse()
{
......
/******************************************************************************
* Fast DXT - a realtime DXT compression tool
*
* Author : Luc Renambot
*
* Copyright (C) 2007 Electronic Visualization Laboratory,
* University of Illinois at Chicago
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either Version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser Public License along
* with this library; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*****************************************************************************/
// From:
// Real-Time DXT Compression
// May 20th 2006 J.M.P. van Waveren
// (c) 2006, Id Software, Inc.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string.h>
#include <math.h>
typedef unsigned char byte;
typedef unsigned short word;
typedef unsigned int dword;
#define C565_5_MASK 0xF8 // 0xFF minus last three bits
#define C565_6_MASK 0xFC // 0xFF minus last two bits
#define INSET_SHIFT 4 // inset the bounding box with ( range >> shift )
#if !defined(MAX_INT)
#define MAX_INT 2147483647 /* max value for an int 32 */
#define MIN_INT (-2147483647-1) /* min value for an int 32 */
#endif
#if defined(__GNUC__)
#define ALIGN16(_x) _x __attribute((aligned(16)))
#else
#define ALIGN16( x ) __declspec(align(16)) x
#endif
// Compress to DXT1 format
void CompressImageDXT1(const byte *inBuf, byte *outBuf, int width, int height, int &outputBytes);
// Compress to DXT5 format
void CompressImageDXT5(const byte *inBuf, byte *outBuf, int width, int height, int &outputBytes);
// Compress to DXT5 format, first convert to YCoCg color space
void CompressImageDXT5YCoCg(const byte *inBuf, byte *outBuf, int width, int height, int &outputBytes);
// Compute error between two images
double ComputeError(const byte *original, const byte *dxt, int width, int height);
#include "StdInc.h"
#include <fonts/FontRendererImpl.h>
namespace krt
{
CachedFont::CachedFont(ComPtr<IDWriteFontFace> face, float emSize)
: m_dwFace(face), m_emSize(emSize)
{
}
bool CachedFont::EnsureFaceCreated()
{
return true;
}
ResultingSubGlyphRun::ResultingSubGlyphRun()
: vertices(nullptr), indices(nullptr)
{
}
ResultingSubGlyphRun::~ResultingSubGlyphRun()
{
delete[] vertices;
delete[] indices;
}
void CachedFont::TargetGlyphRunInternal(float originX, float originY, const DWRITE_GLYPH_RUN* glyphRun, ResultingSubGlyphRun* initialRuns, ResultingVertex* initialVertices, ResultingIndex* initialIndices, RGBA color, int& runCount)
{
// continue on as usual
float x = originX;
for (uint32_t i = 0; i < glyphRun->glyphCount; i++)
{
uint32_t pageIndex = glyphRun->glyphIndices[i] / 256;
std::shared_ptr<CachedFontPage> page;
if (pageIndex >= m_presentPages.size())
{
m_presentPages.resize(pageIndex + 1);
}
if (!m_presentPages[pageIndex])
{
page = std::make_shared<CachedFontPage>(this, pageIndex * 256, (pageIndex + 1) * 256);
m_presentPages[pageIndex] = true;
m_pages[pageIndex] = page;
}
else
{
page = m_pages[pageIndex];
}
if (page->EnsureFontPageCreated())
{
ResultingSubGlyphRun* thisRun = &initialRuns[runCount];
thisRun->texture = page->GetTexture();
thisRun->numIndices = 6;
thisRun->numVertices = 4;
thisRun->vertices = &initialVertices[runCount * 4];
thisRun->indices = &initialIndices[runCount * 6];
auto& address = page->GetCharacterAddress(glyphRun->glyphIndices[i]);
auto& affect = page->GetCharacterSize(glyphRun->glyphIndices[i]);
float tX = x + affect.Left();// -0.5f;
float tY = originY + affect.Top();// -0.5f;
if (glyphRun->glyphOffsets)
{
auto offset = &glyphRun->glyphOffsets[i];
tX += offset->advanceOffset;
tY -= offset->ascenderOffset;
}
thisRun->vertices[0].x = tX;
thisRun->vertices[0].y = tY;
thisRun->vertices[0].u = address.fX1;
thisRun->vertices[0].v = address.fY1;
thisRun->vertices[0].color = color;
thisRun->vertices[1].x = tX + affect.Width();
thisRun->vertices[1].y = tY;
thisRun->vertices[1].u = address.fX2;
thisRun->vertices[1].v = address.fY1;
thisRun->vertices[1].color = color;
thisRun->vertices[2].x = tX + affect.Width();
thisRun->vertices[2].y = tY + affect.Height();
thisRun->vertices[2].u = address.fX2;
thisRun->vertices[2].v = address.fY2;
thisRun->vertices[2].color = color;
thisRun->vertices[3].x = tX;
thisRun->vertices[3].y = tY + affect.Height();
thisRun->vertices[3].u = address.fX1;
thisRun->vertices[3].v = address.fY2;
thisRun->vertices[3].color = color;
thisRun->indices[0] = 0;
thisRun->indices[1] = 1;
thisRun->indices[2] = 2;
thisRun->indices[3] = 2;
thisRun->indices[4] = 3;
thisRun->indices[5] = 0;
runCount++;
}
x += glyphRun->glyphAdvances[i];