mirror of
https://github.com/thunderbrewhq/thunderbrew
synced 2025-10-29 07:16:05 +03:00
929 lines
26 KiB
C++
929 lines
26 KiB
C++
#include "gx/font/CGxFont.hpp"
|
|
#include "gx/font/FontFace.hpp"
|
|
#include "gx/Texture.hpp"
|
|
#include <cmath>
|
|
#include <cstring>
|
|
#include <storm/Error.hpp>
|
|
#include <storm/String.hpp>
|
|
#include <storm/Unicode.hpp>
|
|
|
|
uint16_t TEXTURECACHE::s_textureData[256 * 256];
|
|
|
|
GLYPHBITMAPDATA::~GLYPHBITMAPDATA() {
|
|
if (this->m_data) {
|
|
SMemFree(this->m_data, __FILE__, __LINE__, 0x0);
|
|
}
|
|
|
|
this->m_data = nullptr;
|
|
}
|
|
|
|
void GLYPHBITMAPDATA::CopyFrom(GLYPHBITMAPDATA* data) {
|
|
if (this->m_data) {
|
|
SMemFree(this->m_data, __FILE__, __LINE__, 0);
|
|
}
|
|
|
|
this->m_data = nullptr;
|
|
*this = *data;
|
|
data->m_data = nullptr;
|
|
}
|
|
|
|
uint32_t CHARCODEDESC::GapToNextTexture() {
|
|
CHARCODEDESC* next = this->textureRowLink.Next();
|
|
|
|
return next
|
|
? next->glyphStartPixel - this->glyphEndPixel - 1
|
|
: 255 - this->glyphEndPixel;
|
|
}
|
|
|
|
uint32_t CHARCODEDESC::GapToPreviousTexture() {
|
|
CHARCODEDESC* previous = this->textureRowLink.Prev();
|
|
|
|
return previous
|
|
? this->glyphStartPixel - previous->glyphEndPixel - 1
|
|
: this->glyphStartPixel;
|
|
}
|
|
|
|
void CHARCODEDESC::GenerateTextureCoords(uint32_t rowNumber, uint32_t glyphSide) {
|
|
this->bitmapData.m_textureCoords.minY = (glyphSide * rowNumber) / 256.0f;
|
|
this->bitmapData.m_textureCoords.minX = this->glyphStartPixel / 256.0f;
|
|
this->bitmapData.m_textureCoords.maxY = ((glyphSide * rowNumber) + glyphSide) / 256.0f;
|
|
this->bitmapData.m_textureCoords.maxX = (this->glyphStartPixel + this->bitmapData.m_glyphCellWidth) / 256.0f;
|
|
}
|
|
|
|
KERNINGHASHKEY& KERNINGHASHKEY::operator=(const KERNINGHASHKEY& rhs) {
|
|
if (this->code != rhs.code) {
|
|
this->code = rhs.code;
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
bool KERNINGHASHKEY::operator==(const KERNINGHASHKEY& rhs) {
|
|
return this->code == rhs.code;
|
|
}
|
|
|
|
CHARCODEDESC* TEXTURECACHEROW::CreateNewDesc(GLYPHBITMAPDATA* data, uint32_t rowNumber, uint32_t glyphCellHeight) {
|
|
uint32_t glyphWidth = data->m_glyphCellWidth;
|
|
|
|
if (this->widestFreeSlot < glyphWidth) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (!this->glyphList.Head()) {
|
|
CHARCODEDESC* newCode = this->glyphList.NewNode(2, 0, 0);
|
|
|
|
newCode->glyphStartPixel = 0;
|
|
newCode->glyphEndPixel = glyphWidth - 1;
|
|
newCode->rowNumber = rowNumber;
|
|
newCode->bitmapData.CopyFrom(data);
|
|
newCode->GenerateTextureCoords(rowNumber, glyphCellHeight);
|
|
|
|
this->widestFreeSlot -= glyphWidth;
|
|
|
|
return newCode;
|
|
}
|
|
|
|
uint32_t gapToPrevious = this->glyphList.Head()->GapToPreviousTexture();
|
|
this->widestFreeSlot = gapToPrevious;
|
|
|
|
if (gapToPrevious >= glyphWidth) {
|
|
auto m = SMemAlloc(sizeof(CHARCODEDESC), __FILE__, __LINE__, 0x0);
|
|
auto newCode = new (m) CHARCODEDESC();
|
|
|
|
this->glyphList.LinkNode(newCode, 2, this->glyphList.Head());
|
|
|
|
newCode->glyphEndPixel = this->glyphList.Head()->glyphStartPixel - 1;
|
|
newCode->glyphStartPixel = newCode->glyphEndPixel - glyphWidth + 1;
|
|
newCode->rowNumber = rowNumber;
|
|
newCode->bitmapData.CopyFrom(data);
|
|
newCode->GenerateTextureCoords(rowNumber, glyphCellHeight);
|
|
|
|
this->widestFreeSlot = this->glyphList.Head()
|
|
? this->glyphList.Head()->GapToPreviousTexture()
|
|
: 0;
|
|
|
|
for (auto code = this->glyphList.Head(); code; code = this->glyphList.Link(code)->Next()) {
|
|
uint32_t gapToNext = code->GapToNextTexture();
|
|
if (gapToNext > this->widestFreeSlot) {
|
|
this->widestFreeSlot = gapToNext;
|
|
}
|
|
}
|
|
|
|
return newCode;
|
|
}
|
|
|
|
int32_t inserted = 0;
|
|
CHARCODEDESC* newCode = nullptr;
|
|
|
|
for (auto code = this->glyphList.Head(); code; code = this->glyphList.Link(code)->Next()) {
|
|
uint32_t gapToNext = code->GapToNextTexture();
|
|
|
|
if (inserted) {
|
|
if (gapToNext > this->widestFreeSlot) {
|
|
this->widestFreeSlot = gapToNext;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if (gapToNext >= glyphWidth) {
|
|
auto m = SMemAlloc(sizeof(CHARCODEDESC), __FILE__, __LINE__, 0x0);
|
|
newCode = new (m) CHARCODEDESC();
|
|
|
|
this->glyphList.LinkNode(newCode, 1, code);
|
|
|
|
newCode->glyphStartPixel = code->glyphEndPixel + 1;
|
|
newCode->glyphEndPixel = code->glyphEndPixel + glyphWidth;
|
|
newCode->rowNumber = rowNumber;
|
|
newCode->bitmapData.CopyFrom(data);
|
|
newCode->GenerateTextureCoords(rowNumber, glyphCellHeight);
|
|
|
|
inserted = 1;
|
|
}
|
|
}
|
|
|
|
return newCode;
|
|
}
|
|
|
|
void TEXTURECACHEROW::EvictGlyph(CHARCODEDESC* desc) {
|
|
// TODO
|
|
}
|
|
|
|
void TEXTURECACHE::TextureCallback(EGxTexCommand cmd, uint32_t w, uint32_t h, uint32_t d, uint32_t mipLevel, void* userArg, uint32_t& texelStrideInBytes, const void*& texels) {
|
|
TEXTURECACHE* cache = static_cast<TEXTURECACHE*>(userArg);
|
|
|
|
switch (cmd) {
|
|
case GxTex_Latch: {
|
|
for (int32_t i = cache->m_textureRows.Count() - 1; i >= 0; i--) {
|
|
auto& cacheRow = cache->m_textureRows[i];
|
|
|
|
for (auto glyph = cacheRow.glyphList.Head(); glyph; glyph = cacheRow.glyphList.Next(glyph)) {
|
|
cache->WriteGlyphToTexture(glyph);
|
|
}
|
|
}
|
|
|
|
texelStrideInBytes = 512;
|
|
texels = TEXTURECACHE::s_textureData;
|
|
|
|
break;
|
|
};
|
|
}
|
|
}
|
|
|
|
CHARCODEDESC* TEXTURECACHE::AllocateNewGlyph(GLYPHBITMAPDATA* data) {
|
|
for (int32_t i = 0; i < this->m_textureRows.Count(); i++) {
|
|
auto& cacheRow = this->m_textureRows[i];
|
|
CHARCODEDESC* glyph = cacheRow.CreateNewDesc(data, i, this->m_theFace->m_cellHeight);
|
|
|
|
if (glyph) {
|
|
return glyph;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void TEXTURECACHE::CreateTexture(int32_t filter) {
|
|
CGxTexFlags flags = CGxTexFlags(filter ? GxTex_Linear : GxTex_Nearest, 0, 0, 0, 0, 0, 1);
|
|
|
|
HTEXTURE texture = TextureCreate(
|
|
256,
|
|
256,
|
|
GxTex_Argb4444,
|
|
GxTex_Argb4444,
|
|
flags,
|
|
this,
|
|
TEXTURECACHE::TextureCallback,
|
|
"GxuFont",
|
|
0
|
|
);
|
|
|
|
this->m_texture = texture;
|
|
}
|
|
|
|
void TEXTURECACHE::Initialize(CGxFont* face, uint32_t pixelSize) {
|
|
this->m_theFace = face;
|
|
|
|
uint32_t rowCount = 256 / pixelSize;
|
|
this->m_textureRows.SetCount(rowCount);
|
|
|
|
for (int32_t i = 0; i < rowCount; i++) {
|
|
this->m_textureRows[i].widestFreeSlot = 256;
|
|
}
|
|
}
|
|
|
|
void TEXTURECACHE::PasteGlyph(const GLYPHBITMAPDATA& data, uint16_t* dst) {
|
|
if (this->m_theFace->m_flags & FONT_OUTLINE) {
|
|
if (this->m_theFace->m_flags & FONT_MONOCHROME) {
|
|
this->PasteGlyphOutlinedMonochrome(data, dst);
|
|
} else {
|
|
this->PasteGlyphOutlinedAA(data, dst);
|
|
}
|
|
} else if (this->m_theFace->m_flags & FONT_MONOCHROME) {
|
|
this->PasteGlyphNonOutlinedMonochrome(data, dst);
|
|
} else {
|
|
this->PasteGlyphNonOutlinedAA(data, dst);
|
|
}
|
|
}
|
|
|
|
void TEXTURECACHE::PasteGlyphNonOutlinedAA(const GLYPHBITMAPDATA& glyphData, uint16_t* dst) {
|
|
auto src = reinterpret_cast<uint8_t*>(glyphData.m_data);
|
|
auto pitch = glyphData.m_glyphPitch;
|
|
auto dstCellStride = glyphData.m_glyphCellWidth * 2;
|
|
|
|
for (int32_t y = 0; y < glyphData.m_yStart; y++) {
|
|
memset(dst, 0, dstCellStride);
|
|
dst += 256;
|
|
}
|
|
|
|
for (int32_t y = 0; y < glyphData.m_glyphHeight; y++) {
|
|
for (int32_t x = 0; x < glyphData.m_glyphWidth; x++) {
|
|
dst[x] = ((src[x] & 0xF0) << 8) | 0xFFF;
|
|
}
|
|
|
|
src += pitch;
|
|
dst += 256;
|
|
}
|
|
|
|
auto glyphHeight = glyphData.m_glyphHeight;
|
|
auto yStart = glyphData.m_yStart;
|
|
if (this->m_theFace->m_cellHeight - glyphHeight - yStart > 0 && this->m_theFace->m_cellHeight - glyphHeight != yStart) {
|
|
for (int32_t y = 0; y < this->m_theFace->m_cellHeight - glyphHeight - yStart; y++) {
|
|
memset(dst, 0, dstCellStride);
|
|
dst += 256;
|
|
}
|
|
}
|
|
}
|
|
|
|
void TEXTURECACHE::PasteGlyphNonOutlinedMonochrome(const GLYPHBITMAPDATA& data, uint16_t* dst) {
|
|
// TODO
|
|
}
|
|
|
|
void TEXTURECACHE::PasteGlyphOutlinedAA(const GLYPHBITMAPDATA& glyphData, uint16_t* dst) {
|
|
uint32_t v6;
|
|
uint32_t v7;
|
|
uint16_t* v8;
|
|
int32_t v9;
|
|
int32_t v15;
|
|
int32_t v16;
|
|
uint32_t v17;
|
|
uint32_t v18;
|
|
uint32_t v19;
|
|
uint32_t v20;
|
|
int32_t v21;
|
|
int32_t v22;
|
|
int32_t v23;
|
|
int32_t v24;
|
|
int32_t v25;
|
|
uint32_t v26;
|
|
uint32_t v27;
|
|
uint32_t v28;
|
|
bool v29;
|
|
bool v30;
|
|
int32_t v31;
|
|
int32_t v32;
|
|
int32_t v33;
|
|
int32_t v34;
|
|
int32_t v35;
|
|
int32_t v36;
|
|
bool v37;
|
|
int32_t v38;
|
|
int32_t v39;
|
|
int32_t v40;
|
|
uint32_t v41;
|
|
uint8_t v42;
|
|
bool v43;
|
|
uint16_t v44[9216];
|
|
uint16_t v45[9216];
|
|
uint16_t v46[9216];
|
|
uint32_t v49;
|
|
uint16_t* v52;
|
|
uint32_t v52_2;
|
|
uint32_t v52_3;
|
|
int32_t v53;
|
|
|
|
static uint8_t pixelsLitLevels[] = {
|
|
0, 1, 1, 3, 5, 7, 9, 0xB, 0xD, 0xF, 0, 0
|
|
};
|
|
|
|
const char* src = reinterpret_cast<char*>(glyphData.m_data);
|
|
uint32_t thick = this->m_theFace->m_flags & 0x8;
|
|
|
|
memset(v45, 0, sizeof(v45));
|
|
memset(v46, 0, sizeof(v46));
|
|
|
|
uint32_t ofs = thick
|
|
? 256 * glyphData.m_yStart + 258
|
|
: 256 * glyphData.m_yStart + 257;
|
|
|
|
for (int32_t y = 0; y < glyphData.m_glyphHeight; y++) {
|
|
for (int32_t x = 0; x < glyphData.m_glyphWidth; x++) {
|
|
uint8_t v10 = src[(y * glyphData.m_glyphPitch) + x];
|
|
|
|
if (v10) {
|
|
v45[ofs + (y * 256) + x] = v10;
|
|
v46[ofs + (y * 256) + x] = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
v52_2 = 0;
|
|
|
|
v15 = glyphData.m_yStart - 1;
|
|
|
|
for (int32_t i = 0; i < (thick != 0) + 1; i++) {
|
|
v49 = 2 * (i != 0) + 2;
|
|
v16 = 2 * (i != 0) + 1;
|
|
v17 = v15 < 0 ? 0 : v15;
|
|
v53 = v15 < 0 ? 0 : v15;
|
|
|
|
if (v17 >= this->m_theFace->m_cellHeight) {
|
|
continue;
|
|
}
|
|
|
|
do {
|
|
v18 = glyphData.m_glyphCellWidth;
|
|
v19 = v17 << 8;
|
|
v20 = 0;
|
|
|
|
if (!v18) {
|
|
goto LABEL_68;
|
|
}
|
|
|
|
do {
|
|
v21 = v19 + v20;
|
|
|
|
if (v46[v19 + v20] & v16) {
|
|
goto LABEL_66;
|
|
}
|
|
|
|
if (v53) {
|
|
if (v53 == this->m_theFace->m_cellHeight - 1) {
|
|
if (v20) {
|
|
if (v20 == v18 - 1) {
|
|
if (
|
|
v46[v21 - 1] & v16
|
|
|| v46[v19 + v20 - 257] & v16
|
|
) {
|
|
goto LABEL_65;
|
|
}
|
|
|
|
v22 = v46[v19 + v20 - 256];
|
|
} else {
|
|
if (
|
|
v46[v19 + v20 - 257] & v16
|
|
|| v46[v19 + v20 - 256] & v16
|
|
|| v46[v19 + v20 - 255] & v16
|
|
|| v46[v21 - 1] & v16
|
|
) {
|
|
goto LABEL_65;
|
|
}
|
|
|
|
v22 = v46[v21 + 1];
|
|
}
|
|
} else {
|
|
if (
|
|
v46[v19 - 256] & v16
|
|
|| v46[v19 - 255] & v16
|
|
) {
|
|
goto LABEL_65;
|
|
}
|
|
|
|
v22 = v46[v19 + 1];
|
|
}
|
|
} else if (v20) {
|
|
v24 = v19 + v20;
|
|
|
|
if (v20 == v18 - 1) {
|
|
if (
|
|
v46[v24 - 256] & v16
|
|
|| v46[v19 + v20 - 257] & v16
|
|
|| v46[v21 - 1] & v16
|
|
|| v46[v21 + 255] & v16
|
|
) {
|
|
goto LABEL_65;
|
|
}
|
|
|
|
v22 = v46[v21 + 256];
|
|
} else {
|
|
if (
|
|
v46[v24 - 257] & v16
|
|
|| v46[v19 + v20 - 256] & v16
|
|
|| v46[v19 + v20 - 255] & v16
|
|
|| v46[v21 - 1] & v16
|
|
|| v46[v21 + 1] & v16
|
|
|| v46[v21 + 255] & v16
|
|
|| v46[v21 + 256] & v16
|
|
) {
|
|
goto LABEL_65;
|
|
}
|
|
|
|
v22 = v46[v21 + 257];
|
|
}
|
|
} else {
|
|
if (
|
|
v46[v19 - 256] & v16
|
|
|| v46[v19 - 255] & v16
|
|
|| v46[v19 + 1] & v16
|
|
|| v46[v19 + 257] & v16
|
|
) {
|
|
goto LABEL_65;
|
|
}
|
|
|
|
v22 = v46[v19 + 256];
|
|
}
|
|
LABEL_64:
|
|
if (!(v22 & v16)) {
|
|
goto LABEL_66;
|
|
}
|
|
|
|
goto LABEL_65;
|
|
}
|
|
|
|
if (v20) {
|
|
v23 = v46[v20 - 1];
|
|
|
|
if (v20 == v18 - 1) {
|
|
if (!(v23 & v16) && !(v46[v20 + 255] & v16)) {
|
|
v22 = v46[v20 + 256];
|
|
goto LABEL_64;
|
|
}
|
|
} else if (!(v23 & v16) && !(v46[v20 + 1] & v16) && !(v46[v20 + 255] & v16) && !(v46[v20 + 256] & v16)) {
|
|
v22 = v46[v20 + 257];
|
|
goto LABEL_64;
|
|
}
|
|
} else if (!(v46[1] & v16) && !(v46[257] & v16)) {
|
|
v22 = v46[256];
|
|
goto LABEL_64;
|
|
}
|
|
LABEL_65:
|
|
v46[v21] = v49;
|
|
LABEL_66:
|
|
++v20;
|
|
} while (v20 < v18);
|
|
|
|
v17 = v53;
|
|
LABEL_68:
|
|
v53 = ++v17;
|
|
} while (v17 < this->m_theFace->m_cellHeight);
|
|
}
|
|
|
|
memset(v44, 0, sizeof(v44));
|
|
|
|
v26 = 0;
|
|
v53 = 0;
|
|
|
|
if (!this->m_theFace->m_cellHeight) {
|
|
goto LABEL_95;
|
|
}
|
|
|
|
while (2) {
|
|
v27 = glyphData.m_glyphCellWidth;
|
|
v28 = v26 << 8;
|
|
v25 = 0;
|
|
|
|
if (!v27) {
|
|
goto LABEL_94;
|
|
}
|
|
|
|
while (2) {
|
|
v37 = v46[v28 + v25] == 0;
|
|
v52 = &v46[v28 + v25];
|
|
v29 = !v37;
|
|
v30 = !v37;
|
|
|
|
if (v26) {
|
|
if (v53 == this->m_theFace->m_cellHeight - 1) {
|
|
if (!v25) {
|
|
v31 = v30 + (v46[v28 + 1] != 0) + (v46[v28 - 255] != 0) + (v46[v28 - 256] != 0);
|
|
goto LABEL_92;
|
|
}
|
|
|
|
v37 = v25 == v27 - 1;
|
|
v34 = v28 + v25;
|
|
|
|
if (v37) {
|
|
v31 = (v46[v28 + v25 - 256] != 0) + (v46[v34 - 257] != 0) + v30 + (*(v52 - 1) != 0);
|
|
goto LABEL_92;
|
|
}
|
|
|
|
v35 = (v46[v34 - 256] != 0) + (v46[v28 + v25 - 257] != 0);
|
|
v36 = 0;
|
|
v37 = v46[v28 + v25 - 255] == 0;
|
|
v38 = v28 + v25;
|
|
} else {
|
|
if (!v25) {
|
|
v31 = v30
|
|
+ (v46[v28 + 256] != 0)
|
|
+ (v46[v28 + 1] != 0)
|
|
+ (v46[v28 + 257] != 0)
|
|
+ (v46[v28 - 255] != 0)
|
|
+ (v46[v28 - 256] != 0);
|
|
|
|
goto LABEL_92;
|
|
}
|
|
|
|
v37 = v25 == v27 - 1;
|
|
v39 = v28 + v25;
|
|
|
|
if (v37) {
|
|
v31 = (v46[v28 + v25 + 256] != 0)
|
|
+ (v46[v28 + v25 + 255] != 0)
|
|
+ (v46[v28 + v25 - 256] != 0)
|
|
+ (v46[v39 - 257] != 0)
|
|
+ v30
|
|
+ (*(v52 - 1) != 0);
|
|
|
|
goto LABEL_92;
|
|
}
|
|
|
|
v37 = v46[v39 - 256] == 0;
|
|
v38 = v28 + v25;
|
|
v35 = (v46[v28 + v25 + 256] != 0)
|
|
+ (v46[v28 + v25 + 255] != 0)
|
|
+ (v46[v28 + v25 - 255] != 0)
|
|
+ !v37
|
|
+ (v46[v28 + v25 - 257] != 0);
|
|
v36 = 0;
|
|
v37 = v46[v28 + v25 + 257] == 0;
|
|
}
|
|
|
|
v36 = !v37;
|
|
v31 = (v46[v38 + 1] != 0) + v36 + v35 + v30 + (*(v52 - 1) != 0);
|
|
} else if (v25) {
|
|
if (v25 == v27 - 1) {
|
|
v32 = (v46[v25 + 255] != 0) + (v46[v25 - 1] != 0);
|
|
v33 = v29 + (v46[v25 + 256] != 0);
|
|
} else {
|
|
v32 = (v46[v25 + 257] != 0)
|
|
+ (v46[v25 + 256] != 0)
|
|
+ (v46[v25 + 255] != 0)
|
|
+ (v46[v25 - 1] != 0);
|
|
v33 = v29 + (v46[v25 + 1] != 0);
|
|
}
|
|
|
|
v31 = v33 + v32;
|
|
} else {
|
|
v31 = v29 + (v46[1] != 0) + (v46[257] != 0) + (v46[256] != 0);
|
|
}
|
|
LABEL_92:
|
|
v26 = v53;
|
|
|
|
v44[v28 + v25] = pixelsLitLevels[v31];
|
|
|
|
v27 = glyphData.m_glyphCellWidth;
|
|
|
|
if (++v25 < v27) {
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
LABEL_94:
|
|
v53 = ++v26;
|
|
|
|
if (v26 < this->m_theFace->m_cellHeight) {
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
LABEL_95:
|
|
v40 = 0;
|
|
|
|
for (int32_t y = 0; y < this->m_theFace->m_cellHeight; y++) {
|
|
for (int32_t x = 0; x < glyphData.m_glyphCellWidth; x++) {
|
|
if (v46[v40 + x]) {
|
|
if (v45[v40 + x]) {
|
|
v42 = (255 * v45[v40 + x]) >> 12;
|
|
dst[v40 + x] = v42 | (16 * (v42 | (16 * (v42 | (16 * v44[v40 + x])))));
|
|
} else {
|
|
dst[v40 + x] = v44[v40 + x] << 12;
|
|
}
|
|
} else {
|
|
dst[v40 + x] = 0;
|
|
}
|
|
}
|
|
|
|
v40 += 256;
|
|
}
|
|
}
|
|
|
|
void TEXTURECACHE::PasteGlyphOutlinedMonochrome(const GLYPHBITMAPDATA& data, uint16_t* dst) {
|
|
// TODO
|
|
}
|
|
|
|
void TEXTURECACHE::UpdateDirty() {
|
|
if (this->m_anyDirtyGlyphs && this->m_texture) {
|
|
CGxTex* gxTex = TextureGetGxTex(this->m_texture, 1, nullptr);
|
|
CiRect updateRect = { 0, 0, 256, 256 };
|
|
GxTexUpdate(gxTex, updateRect, 1);
|
|
|
|
this->m_anyDirtyGlyphs = 0;
|
|
}
|
|
}
|
|
|
|
void TEXTURECACHE::WriteGlyphToTexture(CHARCODEDESC* glyph) {
|
|
if (!this->m_texture || !this->m_theFace || !this->m_theFace->m_cellHeight) {
|
|
return;
|
|
}
|
|
|
|
uint32_t ofs = glyph->glyphStartPixel + (glyph->rowNumber * this->m_theFace->m_cellHeight << 8);
|
|
uint16_t* ptr = &TEXTURECACHE::s_textureData[ofs];
|
|
|
|
this->PasteGlyph(glyph->bitmapData, ptr);
|
|
}
|
|
|
|
CGxFont::~CGxFont() {
|
|
this->Clear();
|
|
}
|
|
|
|
int32_t CGxFont::CheckStringGlyphs(const char* string) {
|
|
if (!string || !*string) {
|
|
return 1;
|
|
}
|
|
|
|
while (*string) {
|
|
int32_t advance;
|
|
auto code = SUniSGetUTF8(reinterpret_cast<const uint8_t*>(string), &advance);
|
|
|
|
HASHKEY_NONE key = {};
|
|
|
|
if (code != '\r' && code != '\n' && !this->m_activeCharacters.Ptr(code, key)) {
|
|
return 0;
|
|
}
|
|
|
|
string += advance;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void CGxFont::Clear() {
|
|
if (this->m_faceHandle) {
|
|
FontFaceCloseHandle(this->m_faceHandle);
|
|
}
|
|
|
|
this->m_faceHandle = nullptr;
|
|
|
|
this->ClearGlyphs();
|
|
}
|
|
|
|
void CGxFont::ClearGlyphs() {
|
|
for (int32_t i = 0; i < 8; i++) {
|
|
auto& cache = this->m_textureCache[i];
|
|
|
|
if (cache.m_texture) {
|
|
HandleClose(this->m_textureCache[i].m_texture);
|
|
}
|
|
|
|
cache.m_texture = nullptr;
|
|
|
|
// TODO
|
|
}
|
|
|
|
this->m_activeCharacters.Clear();
|
|
|
|
this->m_activeCharacterCache.Clear();
|
|
|
|
this->m_kernInfo.Clear();
|
|
}
|
|
|
|
float CGxFont::ComputeStep(uint32_t currentCode, uint32_t nextCode) {
|
|
KERNINGHASHKEY kernKey = { nextCode | (currentCode << 16) };
|
|
KERNNODE* kern = this->m_kernInfo.Ptr(currentCode, kernKey);
|
|
|
|
if (kern && kern->flags & 0x02) {
|
|
return kern->proporportionalSpacing;
|
|
}
|
|
|
|
auto face = FontFaceGetFace(this->m_faceHandle);
|
|
auto currentIndex = FT_Get_Char_Index(face, currentCode);
|
|
auto nextIndex = FT_Get_Char_Index(face, nextCode);
|
|
|
|
FT_Vector vector;
|
|
vector.x = 0;
|
|
|
|
if (face->face_flags & FT_FACE_FLAG_KERNING) {
|
|
FT_Get_Kerning(face, currentIndex, nextIndex, ft_kerning_unscaled, &vector);
|
|
vector.x &= (vector.x >= 0) - 1;
|
|
}
|
|
|
|
HASHKEY_NONE charKey = {};
|
|
auto activeChar = this->m_activeCharacters.Ptr(currentCode, charKey);
|
|
|
|
float advance = 0.0f;
|
|
|
|
if (activeChar) {
|
|
advance = this->m_flags & 0x08
|
|
? activeChar->bitmapData.m_glyphAdvance + 1.0f
|
|
: activeChar->bitmapData.m_glyphAdvance;
|
|
}
|
|
|
|
float spacing = (this->m_pixelsPerUnit * vector.x) + advance;
|
|
|
|
if (!kern) {
|
|
kern = this->m_kernInfo.New(currentCode, kernKey, 0, 0);
|
|
}
|
|
|
|
kern->flags |= 0x02;
|
|
kern->proporportionalSpacing = ceil(spacing);
|
|
|
|
return kern->proporportionalSpacing;
|
|
}
|
|
|
|
float CGxFont::ComputeStepFixedWidth(uint32_t currentCode, uint32_t nextCode) {
|
|
// TODO
|
|
return 0.0f;
|
|
}
|
|
|
|
float CGxFont::GetGlyphBearing(const CHARCODEDESC* glyph, bool billboarded, float height) {
|
|
if (billboarded) {
|
|
float v8 = ScreenToPixelHeight(1, height) / this->GetPixelSize();
|
|
return glyph->bitmapData.m_glyphBearing * v8;
|
|
}
|
|
|
|
return ceil(glyph->bitmapData.m_glyphBearing);
|
|
}
|
|
|
|
int32_t CGxFont::GetGlyphData(GLYPHBITMAPDATA* glyphData, uint32_t code) {
|
|
FT_Face face = FontFaceGetFace(this->m_faceHandle);
|
|
FT_Set_Pixel_Sizes(face, this->m_pixelSize, 0);
|
|
|
|
uint32_t v6 = 0;
|
|
|
|
if (this->m_flags & 0x8) {
|
|
v6 = 4;
|
|
} else if (this->m_flags & FONT_OUTLINE) {
|
|
v6 = 2;
|
|
}
|
|
|
|
return IGxuFontGlyphRenderGlyph(
|
|
face,
|
|
this->m_pixelSize,
|
|
code,
|
|
this->m_baseline,
|
|
glyphData,
|
|
this->m_flags & FONT_MONOCHROME,
|
|
v6
|
|
);
|
|
}
|
|
|
|
const char* CGxFont::GetName(void) const {
|
|
STORM_ASSERT(this->m_faceHandle);
|
|
|
|
return FontFaceGetFontName(this->m_faceHandle);
|
|
}
|
|
|
|
uint32_t CGxFont::GetPixelSize() {
|
|
return this->m_pixelSize;
|
|
}
|
|
|
|
int32_t CGxFont::Initialize(const char* name, uint32_t newFlags, float fontHeight) {
|
|
SStrPrintf(this->m_fontName, 260, "%s", name);
|
|
|
|
this->m_requestedFontHeight = fontHeight;
|
|
|
|
this->Clear();
|
|
|
|
this->m_flags = newFlags;
|
|
|
|
this->m_faceHandle = FontFaceGetHandle(name, GetFreeTypeLibrary());
|
|
|
|
if (this->m_faceHandle) {
|
|
return this->UpdateDimensions();
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
const CHARCODEDESC* CGxFont::NewCodeDesc(uint32_t code) {
|
|
HASHKEY_NONE key = {};
|
|
CHARCODEDESC* charDesc = this->m_activeCharacters.Ptr(code, key);
|
|
|
|
if (charDesc) {
|
|
this->m_activeCharacterCache.LinkToHead(charDesc);
|
|
return charDesc;
|
|
}
|
|
|
|
GLYPHBITMAPDATA data;
|
|
|
|
if (!CGxFont::GetGlyphData(&data, code)) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Attempt to allocate the character off of texture caches
|
|
for (uint32_t textureNumber = 0; textureNumber < 8; textureNumber++) {
|
|
TEXTURECACHE* textureCache = &this->m_textureCache[textureNumber];
|
|
|
|
if (textureCache->m_texture && TextureGetGxTex(reinterpret_cast<CTexture*>(textureCache->m_texture), 1, nullptr)) {
|
|
charDesc = textureCache->AllocateNewGlyph(&data);
|
|
|
|
if (charDesc) {
|
|
charDesc->textureNumber = textureNumber;
|
|
break;
|
|
}
|
|
} else {
|
|
textureCache->CreateTexture(this->m_flags & 0x4);
|
|
textureCache->Initialize(this, this->m_cellHeight);
|
|
|
|
charDesc = textureCache->AllocateNewGlyph(&data);
|
|
|
|
if (charDesc) {
|
|
charDesc->textureNumber = textureNumber;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// No character was allocated from the texture caches, so evict the oldest character and
|
|
// attempt to allocate from that character's texture cache row
|
|
if (!charDesc) {
|
|
CHARCODEDESC* oldestDesc = this->m_activeCharacterCache.Tail();
|
|
|
|
if (oldestDesc) {
|
|
uint32_t textureNumber = oldestDesc->textureNumber;
|
|
uint32_t rowNumber = oldestDesc->rowNumber;
|
|
|
|
TEXTURECACHE* textureCache = &this->m_textureCache[textureNumber];
|
|
TEXTURECACHEROW* cacheRow = &textureCache->m_textureRows[rowNumber];
|
|
cacheRow->EvictGlyph(oldestDesc);
|
|
|
|
this->RegisterEvictNotice(textureNumber);
|
|
|
|
charDesc = cacheRow->CreateNewDesc(&data, rowNumber, this->m_cellHeight);
|
|
|
|
if (charDesc) {
|
|
charDesc->rowNumber = rowNumber;
|
|
charDesc->textureNumber = textureNumber;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (charDesc) {
|
|
this->m_activeCharacters.Insert(charDesc, code, key);
|
|
this->m_activeCharacterCache.LinkToHead(charDesc);
|
|
this->m_textureCache[charDesc->textureNumber].m_anyDirtyGlyphs = 1;
|
|
}
|
|
|
|
return charDesc;
|
|
}
|
|
|
|
void CGxFont::RegisterEvictNotice(uint32_t a2) {
|
|
// TODO
|
|
}
|
|
|
|
int32_t CGxFont::UpdateDimensions() {
|
|
FT_Face theFace = FontFaceGetFace(this->m_faceHandle);
|
|
|
|
float v11 = Sub6C2280(theFace, this->m_requestedFontHeight);
|
|
float v3 = 2.0 / GetScreenPixelHeight();
|
|
|
|
if (v11 > v3) {
|
|
v3 = v11;
|
|
}
|
|
|
|
float height = v3;
|
|
|
|
uint32_t pixelSize = ScreenToPixelHeight(0, height);
|
|
|
|
if (pixelSize <= 32) {
|
|
if (!pixelSize) {
|
|
// TODO
|
|
// nullsub_3();
|
|
}
|
|
} else {
|
|
pixelSize = 32;
|
|
}
|
|
|
|
this->m_pixelSize = pixelSize;
|
|
|
|
uint32_t v10 = theFace->ascender + abs(theFace->descender);
|
|
|
|
if (!v10) {
|
|
return 0;
|
|
}
|
|
|
|
this->m_cellHeight = pixelSize;
|
|
float baseline = (double)(pixelSize * theFace->ascender) / (double)v10;
|
|
|
|
this->m_baseline = (int64_t)(baseline + 0.5);
|
|
|
|
uint32_t flags = this->m_flags;
|
|
|
|
if (flags & 0x8) {
|
|
this->m_cellHeight = pixelSize + 4;
|
|
} else if (flags & 0x1) {
|
|
this->m_cellHeight = pixelSize + 2;
|
|
}
|
|
|
|
int32_t result = FT_Set_Pixel_Sizes(theFace, pixelSize, 0) == FT_Err_Ok;
|
|
|
|
this->m_pixelsPerUnit = (double)theFace->size->metrics.x_ppem / (double)theFace->units_per_EM;
|
|
|
|
return result;
|
|
}
|