//
// Font.h
// DescriptionHere
//
// Created by John Ryland (jryland@xiaofrog.com) on 02/12/2017.
// Copyright 2017 InvertedLogic. All rights reserved.
//
#pragma once
#include "Common.h"
#include "Graphics.h"
BEGIN_NAMESPACE
// Public API
struct PointF
{
float m_x, m_y;
};
struct Curve
{
PointF m_controlPoints[3];
};
struct Glyph
{
Vector<Curve> m_lines;
};
struct OutlineText
{
Vector<Glyph> m_text;
};
//
// This is to be able to call setFont on the painter or widget
//
// internally need to add the used fonts to a font manager inside Application
// so that re-used fonts don't maintain two copies in memory. Each font that
// is in use needs to maintain a mmap open handle. It then also needs to have
// a cache of the raw un-scaled glyph outlines. And then a cache of rasterized
// bitmaps of these for various sizes.
//
// The un-scaled glyph outlines can on demand be scaled and positioned for each
// time an outline is requested, or for each time rasterization needs to take place.
//
// Where to do the colorizing? During rasterization or after?
// How about ClearType / Sub-Pixel effects? This requires the color or perhaps
// strategy is to rasterize without color at a higher x-resolution. I'm already
// thinking that to do anti-aliasing, to rasterize at a higher resolution and
// smoothly down-scale it. Possibly with GPU acceleration, this can be offloaded to
// the GPU, so it might be an idea to rasterize to a given resolution and scale and
// colorize there.
//
class Font
{
public:
Font();
Font(const char* a_fontFamily, float a_pointSize);
~Font();
void setFontFamily();
void setPointSize(float a_pointSize);
// setColor ?
OutlineText asOutline(const char* a_text);
private:
struct FontData;
//FontData* m_data; // TODO: if make a copy of a Font, this raw pointer is copied
// then in the destructor it deletes it which the other has a hold of
// therefore need to solve - copyConstructor + assignment impl, or shared_ptr
// Need to check other classes and use a consistent safe solution
SharedPtr<FontData> m_data;
};
//
// This is internal implementation detail
//
class FontFixedPoint
{
public:
FontFixedPoint() {}
FontFixedPoint(int16_t x, int16_t y) // values should be already in fixed-point
: m_x(x), m_y(y)
{
}
// Raw fixed-point value
int16_t x() const { return m_x; }
int16_t y() const { return m_y; }
// As a float
float xf() const { return float(m_x) * scale; }
float yf() const { return float(m_y) * scale; }
private:
const unsigned int shiftBits = 10;
const float scale = 1.0f / float(1 << shiftBits);
int16_t m_x = 0, m_y = 0;
};
struct FontCurveFixed
{
FontFixedPoint m_controlPoints[3];
};
struct FontGlyphFixed
{
//Vector<FontCurve> m_lines;
Vector<Curve> m_lines;
};
class RasterizedFont
{
public:
RasterizedFont(const char* a_family, float a_pointSize, uint32_t a_color);
// class _Hash = hash<_Key>
// class _Pred = std::equal_to<_Key>
static uint32_t hash(const char* a_family, float a_pointSize, uint32_t a_color)
{
std::string family = a_family;
// uint32_t hash = family.hash();
uint32_t hash1 = std::hash<std::string>{}(family);
hash1 += uint32_t(a_pointSize * 100.0f);
hash1 ^= a_color;
return hash1;
}
int compare(const char* a_family, float a_pointSize, uint32_t a_color)
{
int ret = strcmp(a_family, m_family.c_str());
if (ret != 0)
return ret;
ret = (a_pointSize < m_pointSize) ? -1 : ((a_pointSize > m_pointSize) ? 1 : 0);
if (ret != 0)
return ret;
return (a_color < m_color) ? -1 : ((a_color > m_color) ? 1 : 0);
}
int getIndexForValues(float a_pointSize, uint32_t a_color);
int index();
private:
String m_family;
float m_pointSize;
uint32_t m_color;
Cache<int, PixelBuffer> m_glyphCache;
};
class ManagedFont
{
public:
void getRasterizedGlyph(const char* a_family, float a_size, uint32_t a_color);
private:
// For bitmap glyph caching, need to look up based on both the pointsize and the index
// also how about color too? Perhaps another layer needed here.
//Cache<int, FontGlyphFixed> m_glyphCache;
// class _Hash = hash<_Key>
// class _Pred = std::equal_to<_Key>
Cache<int, RasterizedFont> m_rasterCache;
// If idea is to use the GPU, one raster size might be better. Turn the font in to
// like a sprite-sheet and render the glyphs out of the font texture - can scale and
// colorize in a shader.
// Using shaders, can also do signed-distance fields.
Cache<int, PixelBuffer> m_glyphCache;
};
class FontManager
{
public:
FontManager();
~FontManager();
Vector<String> getAvailableFontNames();
ManagedFont& getFont(String fontName);
Cache<String, ManagedFont> m_loadedFonts;
};
END_NAMESPACE