291 lines
7.7 KiB
C++
291 lines
7.7 KiB
C++
/*
|
|
Copyright (c) 2013 yvt
|
|
|
|
This file is part of OpenSpades.
|
|
|
|
OpenSpades is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
OpenSpades 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 General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with OpenSpades. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
#include "IFont.h"
|
|
#include "IRenderer.h"
|
|
#include <unordered_map>
|
|
#include <Core/FileManager.h>
|
|
#include <Core/IStream.h>
|
|
#include <memory>
|
|
#include <Core/Exception.h>
|
|
|
|
namespace spades {
|
|
namespace client{
|
|
|
|
class IImage;
|
|
|
|
class FallbackFontManager;
|
|
struct FallbackFont;
|
|
|
|
// FallbackFontManager cannot save IImage because multiple images
|
|
// come from different renderers.
|
|
class FallbackFontRenderer: public RefCountedObject {
|
|
std::vector<IImage *> images;
|
|
IRenderer *renderer;
|
|
FallbackFontManager *manager;
|
|
Handle<IImage> whiteImage; // used for last resort
|
|
|
|
struct FindResult {
|
|
IImage *img;
|
|
float sizeInverse;
|
|
float advance;
|
|
int x, y, w, h;
|
|
int offX, offY;
|
|
FindResult():
|
|
img(nullptr), sizeInverse(0.f), advance(0.f),
|
|
x(0),y(0),w(0),h(0),offX(0),offY(0){}
|
|
};
|
|
FindResult FindGlyph(uint32_t);
|
|
protected:
|
|
virtual ~FallbackFontRenderer();
|
|
public:
|
|
|
|
FallbackFontRenderer(IRenderer *renderer,
|
|
FallbackFontManager *manager);
|
|
|
|
void Draw(uint32_t unicode,
|
|
Vector2 offset, float size,
|
|
Vector4 color);
|
|
float Measure(uint32_t unicode, float size);
|
|
|
|
};
|
|
|
|
struct FallbackFont {
|
|
private:
|
|
|
|
public:
|
|
struct GlyphInfo {
|
|
uint32_t unicode;
|
|
uint16_t x, y;
|
|
uint8_t w, h;
|
|
uint16_t advance;
|
|
uint16_t offX, offY;
|
|
};
|
|
std::string imagePath;
|
|
std::unordered_map<uint32_t, GlyphInfo> glyphs;
|
|
float fontSize;
|
|
float fontSizeInverse;
|
|
|
|
FallbackFont(const std::string& path) {
|
|
std::unique_ptr<IStream> s(FileManager::OpenForReading(path.c_str()));
|
|
char buf[17];
|
|
buf[16] = 0;
|
|
if(s->Read(buf, 16) < 16) {
|
|
SPRaise("Reading %s: file truncated", path.c_str());
|
|
}
|
|
if(strcmp(buf, "OpenSpadesFontFl")) {
|
|
SPRaise("Reading %s: font file corrupted", path.c_str());
|
|
}
|
|
|
|
uint32_t numGlyphs;
|
|
if(s->Read(&numGlyphs, 4) < 4) {
|
|
SPRaise("Reading %s: file truncated", path.c_str());
|
|
}
|
|
|
|
uint32_t fontSize;
|
|
if(s->Read(&fontSize, 4) < 4) {
|
|
SPRaise("Reading %s: file truncated", path.c_str());
|
|
}
|
|
fontSize = (float)fontSize;
|
|
fontSizeInverse = 1.f / fontSize;
|
|
|
|
std::vector<GlyphInfo> infos;
|
|
infos.resize(static_cast<std::size_t>(numGlyphs));
|
|
size_t siz = infos.size() * sizeof(GlyphInfo);
|
|
if(s->Read(infos.data(), siz) < siz) {
|
|
SPRaise("Reading %s: file truncated: trying to read %d byte(s)", path.c_str(), (int)siz);
|
|
}
|
|
|
|
for(const auto& i: infos) {
|
|
glyphs[i.unicode] = i;
|
|
}
|
|
|
|
// remove .ospfont and add .tga
|
|
std::string p = path.substr(0, path.size() - 8);
|
|
imagePath = p + ".tga";
|
|
if(FileManager::FileExists(imagePath.c_str())) {
|
|
return;
|
|
}
|
|
imagePath = p + ".png";
|
|
if(FileManager::FileExists(imagePath.c_str())) {
|
|
return;
|
|
}
|
|
SPRaise("Reading %s: failed to find %s.(png|tga)", path.c_str(), p.c_str());
|
|
}
|
|
|
|
};
|
|
|
|
class FallbackFontManager {
|
|
|
|
FallbackFontManager() {
|
|
auto files = FileManager::EnumFiles("Gfx/Fonts");
|
|
for(const auto& file: files) {
|
|
if(file.rfind(".ospfont") != file.size() - 8) {
|
|
continue;
|
|
}
|
|
|
|
std::string path = "Gfx/Fonts/" + file;
|
|
auto fnt = new FallbackFont(path);
|
|
fonts.push_back(fnt);
|
|
}
|
|
}
|
|
public:
|
|
std::vector<FallbackFont *> fonts;
|
|
|
|
static FallbackFontManager *GetInstance() {
|
|
static FallbackFontManager *inst = nullptr;
|
|
if(inst == nullptr) {
|
|
inst = new FallbackFontManager();
|
|
}
|
|
return inst;
|
|
}
|
|
FallbackFontRenderer *CreateRenderer(IRenderer*r) {
|
|
return new FallbackFontRenderer(r,this);
|
|
}
|
|
};
|
|
|
|
FallbackFontRenderer::FallbackFontRenderer(IRenderer *renderer,
|
|
FallbackFontManager *manager):
|
|
renderer(renderer), manager(manager){
|
|
for(auto* font: manager->fonts) {
|
|
auto imgPath = font->imagePath;
|
|
auto *img = renderer->RegisterImage(imgPath.c_str()); // addref'd
|
|
images.push_back(img);
|
|
}
|
|
whiteImage.Set(renderer->RegisterImage("Gfx/White.tga"), false);
|
|
}
|
|
|
|
FallbackFontRenderer::~FallbackFontRenderer() {
|
|
for(auto* img: images) {
|
|
img->Release();
|
|
}
|
|
images.clear();
|
|
}
|
|
|
|
FallbackFontRenderer::FindResult FallbackFontRenderer::FindGlyph(uint32_t unicode) {
|
|
FindResult result;
|
|
for(size_t i = 0; i < manager->fonts.size(); i++) {
|
|
auto *font = manager->fonts[i];
|
|
const auto& it = font->glyphs.find(unicode);
|
|
if(it == font->glyphs.end()) {
|
|
continue;
|
|
}
|
|
result.img = images[i];
|
|
result.sizeInverse = font->fontSizeInverse;
|
|
result.advance = it->second.advance;
|
|
result.x = it->second.x;
|
|
result.y = it->second.y;
|
|
result.w = it->second.w;
|
|
result.h = it->second.h;
|
|
result.offX = it->second.offX;
|
|
result.offY = it->second.offY;
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void FallbackFontRenderer::Draw(uint32_t unicode, spades::Vector2 offset,
|
|
float size, spades::Vector4 color) {
|
|
renderer->SetColorAlphaPremultiplied(color);
|
|
float x = offset.x;
|
|
float y = offset.y;
|
|
|
|
auto glyph = FindGlyph(unicode);
|
|
if(glyph.img == nullptr) {
|
|
// no glyph found! draw box in the last resort
|
|
IImage *img = whiteImage;
|
|
renderer->DrawImage(img, AABB2(x, y, size, 1.f));
|
|
renderer->DrawImage(img, AABB2(x, y + size - 1.f, size, 1.f));
|
|
renderer->DrawImage(img, AABB2(x, y + 1.f, 1.f, size - 2.f));
|
|
renderer->DrawImage(img, AABB2(x + size - 1.f, y + 1.f, 1.f, size - 2.f));
|
|
return;
|
|
}
|
|
|
|
if(glyph.w == 0) {
|
|
// null glyph.
|
|
return;
|
|
}
|
|
|
|
float scale = size * glyph.sizeInverse;
|
|
|
|
AABB2 inRect(glyph.x, glyph.y, glyph.w, glyph.h);
|
|
AABB2 outRect(glyph.offX, glyph.offY, glyph.w, glyph.h);
|
|
|
|
// margin to make the border completely transparent
|
|
// (considering the OpenGL's linear interpolation)
|
|
inRect.min.x -= 0.5f;
|
|
inRect.min.y -= 0.5f;
|
|
outRect.min.x -= 0.5f;
|
|
outRect.min.y -= 0.5f;
|
|
inRect.max.x += 0.5f;
|
|
inRect.max.y += 0.5f;
|
|
outRect.max.x += 0.5f;
|
|
outRect.max.y += 0.5f;
|
|
|
|
outRect.min *= scale;
|
|
outRect.max *= scale;
|
|
|
|
outRect.min += offset;
|
|
outRect.max += offset;
|
|
|
|
renderer->DrawImage(glyph.img, outRect, inRect);
|
|
}
|
|
|
|
float FallbackFontRenderer::Measure(uint32_t unicode, float size) {
|
|
auto glyph = FindGlyph(unicode);
|
|
if(glyph.img == nullptr) {
|
|
// no glyph found! draw box in the last resort
|
|
return size + 1.f;
|
|
}
|
|
|
|
float scale = size * glyph.sizeInverse;
|
|
return glyph.advance * scale;
|
|
}
|
|
|
|
IFont::IFont(IRenderer *r) {
|
|
fallback.Set(FallbackFontManager::GetInstance()->CreateRenderer(r), false);
|
|
}
|
|
|
|
IFont::~IFont()
|
|
{
|
|
//---
|
|
}
|
|
|
|
float IFont::MeasureFallback(uint32_t unicodeCodePoint, float size) {
|
|
return fallback->Measure(unicodeCodePoint, size);
|
|
}
|
|
|
|
void IFont::DrawFallback(uint32_t unicodeCodePoint,
|
|
spades::Vector2 offset,
|
|
float size,
|
|
spades::Vector4 color) {
|
|
fallback->Draw(unicodeCodePoint, offset, size, color);
|
|
}
|
|
|
|
void IFont::DrawShadow( const std::string& message, const Vector2& offset, float scale, const Vector4& color, const Vector4& shadowColor )
|
|
{
|
|
Draw( message, offset + MakeVector2(1,1), scale, shadowColor );
|
|
Draw( message, offset, scale, color );
|
|
}
|
|
|
|
}
|
|
}
|
|
|