2019-10-23 23:20:58 +00:00
|
|
|
/*
|
|
|
|
** bdffont.cpp
|
|
|
|
** Management for the VGA consolefont
|
|
|
|
**
|
|
|
|
**---------------------------------------------------------------------------
|
|
|
|
** Copyright 2019 Christoph Oelckers
|
|
|
|
** All rights reserved.
|
|
|
|
**
|
|
|
|
** Redistribution and use in source and binary forms, with or without
|
|
|
|
** modification, are permitted provided that the following conditions
|
|
|
|
** are met:
|
|
|
|
**
|
|
|
|
** 1. Redistributions of source code must retain the above copyright
|
|
|
|
** notice, this list of conditions and the following disclaimer.
|
|
|
|
** 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
** notice, this list of conditions and the following disclaimer in the
|
|
|
|
** documentation and/or other materials provided with the distribution.
|
|
|
|
** 3. The name of the author may not be used to endorse or promote products
|
|
|
|
** derived from this software without specific prior written permission.
|
|
|
|
**
|
|
|
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
|
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
**---------------------------------------------------------------------------
|
|
|
|
**
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "textures.h"
|
|
|
|
#include "image.h"
|
|
|
|
#include "v_font.h"
|
|
|
|
#include "utf8.h"
|
|
|
|
#include "sc_man.h"
|
|
|
|
#include "imagehelpers.h"
|
2019-10-25 00:07:46 +00:00
|
|
|
#include "v_draw.h"
|
2019-11-05 23:00:33 +00:00
|
|
|
#include "glbackend/glbackend.h"
|
2020-04-12 05:51:11 +00:00
|
|
|
#include "palettecontainer.h"
|
2020-05-24 14:32:52 +00:00
|
|
|
#include "texturemanager.h"
|
2019-10-23 23:20:58 +00:00
|
|
|
|
|
|
|
#include "fontinternals.h"
|
|
|
|
|
|
|
|
|
|
|
|
struct HexDataSource
|
|
|
|
{
|
|
|
|
int FirstChar = INT_MAX, LastChar = INT_MIN;
|
|
|
|
TArray<uint8_t> glyphdata;
|
|
|
|
unsigned glyphmap[65536] = {};
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// parse a HEX font
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void ParseDefinition(const char *fname)
|
|
|
|
{
|
|
|
|
FScanner sc;
|
|
|
|
sc.Open(fname);
|
|
|
|
sc.SetCMode(true);
|
|
|
|
glyphdata.Push(0); // ensure that index 0 can be used as 'not present'.
|
|
|
|
while (sc.GetString())
|
|
|
|
{
|
|
|
|
int codepoint = (int)strtoull(sc.String, nullptr, 16);
|
|
|
|
sc.MustGetStringName(":");
|
|
|
|
sc.MustGetString();
|
|
|
|
if (codepoint >= 0 && codepoint < 65536 && !sc.Compare("00000000000000000000000000000000")) // don't set up empty glyphs.
|
|
|
|
{
|
|
|
|
unsigned size = (unsigned)strlen(sc.String);
|
|
|
|
unsigned offset = glyphdata.Reserve(size / 2 + 1);
|
|
|
|
glyphmap[codepoint] = offset;
|
|
|
|
glyphdata[offset++] = size / 2;
|
|
|
|
for (unsigned i = 0; i < size; i += 2)
|
|
|
|
{
|
|
|
|
char hex[] = { sc.String[i], sc.String[i + 1], 0 };
|
|
|
|
glyphdata[offset++] = (uint8_t)strtoull(hex, nullptr, 16);
|
|
|
|
}
|
|
|
|
if (codepoint < FirstChar) FirstChar = codepoint;
|
|
|
|
if (codepoint > LastChar) LastChar = codepoint;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static HexDataSource hexdata;
|
|
|
|
|
|
|
|
// This is a font character that reads RLE compressed data.
|
2020-05-24 14:32:52 +00:00
|
|
|
class FHexFontChar : public FImageSource
|
2019-10-23 23:20:58 +00:00
|
|
|
{
|
|
|
|
public:
|
|
|
|
FHexFontChar(uint8_t *sourcedata, int swidth, int width, int height);
|
|
|
|
|
2020-05-24 14:32:52 +00:00
|
|
|
TArray<uint8_t> CreatePalettedPixels(int conversion) override;
|
2019-10-23 23:20:58 +00:00
|
|
|
|
|
|
|
protected:
|
|
|
|
int SourceWidth;
|
|
|
|
const uint8_t *SourceData;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FHexFontChar :: FHexFontChar
|
|
|
|
//
|
|
|
|
// Used by HEX fonts.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
FHexFontChar::FHexFontChar (uint8_t *sourcedata, int swidth, int width, int height)
|
|
|
|
: SourceData (sourcedata)
|
|
|
|
{
|
|
|
|
SourceWidth = swidth;
|
2020-05-24 14:32:52 +00:00
|
|
|
Width = width;
|
|
|
|
Height = height;
|
|
|
|
LeftOffset = 0;
|
|
|
|
TopOffset = 0;
|
2019-10-23 23:20:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FHexFontChar :: Get8BitPixels
|
|
|
|
//
|
|
|
|
// The render style has no relevance here.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
2020-05-24 14:32:52 +00:00
|
|
|
TArray<uint8_t> FHexFontChar::CreatePalettedPixels(int)
|
2019-10-23 23:20:58 +00:00
|
|
|
{
|
2020-05-24 14:32:52 +00:00
|
|
|
int destSize = Width * Height;
|
|
|
|
TArray<uint8_t> Pixels(destSize, true);
|
|
|
|
uint8_t *dest_p = Pixels.Data();
|
2019-10-23 23:20:58 +00:00
|
|
|
const uint8_t *src_p = SourceData;
|
|
|
|
|
|
|
|
memset(dest_p, 0, destSize);
|
2020-05-24 14:32:52 +00:00
|
|
|
for (int y = 0; y < Height; y++)
|
2019-10-23 23:20:58 +00:00
|
|
|
{
|
|
|
|
for (int x = 0; x < SourceWidth; x++)
|
|
|
|
{
|
|
|
|
int byte = *src_p++;
|
2020-05-24 14:32:52 +00:00
|
|
|
uint8_t *pixelstart = dest_p + 8 * x * Height + y;
|
2019-10-23 23:20:58 +00:00
|
|
|
for (int bit = 0; bit < 8; bit++)
|
|
|
|
{
|
|
|
|
if (byte & (128 >> bit))
|
|
|
|
{
|
2020-05-24 14:32:52 +00:00
|
|
|
pixelstart[bit*Height] = y+2;
|
2019-10-23 23:20:58 +00:00
|
|
|
// Add a shadow at the bottom right, similar to the old console font.
|
2020-05-24 14:32:52 +00:00
|
|
|
if (y != Height - 1)
|
2019-10-23 23:20:58 +00:00
|
|
|
{
|
2020-05-24 14:32:52 +00:00
|
|
|
pixelstart[bit*Height + Height + 1] = 1;
|
2019-10-23 23:20:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-05-24 14:32:52 +00:00
|
|
|
return Pixels;
|
2019-10-23 23:20:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
class FHexFontChar2 : public FHexFontChar
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
FHexFontChar2(uint8_t *sourcedata, int swidth, int width, int height);
|
|
|
|
|
2020-05-24 14:32:52 +00:00
|
|
|
TArray<uint8_t> CreatePalettedPixels(int conversion) override;
|
2019-10-23 23:20:58 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FHexFontChar :: FHexFontChar
|
|
|
|
//
|
|
|
|
// Used by HEX fonts.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
FHexFontChar2::FHexFontChar2(uint8_t *sourcedata, int swidth, int width, int height)
|
|
|
|
: FHexFontChar(sourcedata, swidth, width, height)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FHexFontChar :: Get8BitPixels
|
|
|
|
//
|
|
|
|
// The render style has no relevance here.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
2020-05-24 14:32:52 +00:00
|
|
|
TArray<uint8_t> FHexFontChar2::CreatePalettedPixels(int)
|
2019-10-23 23:20:58 +00:00
|
|
|
{
|
2020-05-24 14:32:52 +00:00
|
|
|
int destSize = Width * Height;
|
|
|
|
TArray<uint8_t> Pixels(destSize, true);
|
|
|
|
uint8_t *dest_p = Pixels.Data();
|
2019-10-23 23:20:58 +00:00
|
|
|
|
|
|
|
assert(SourceData);
|
|
|
|
if (SourceData)
|
|
|
|
{
|
|
|
|
auto drawLayer = [&](int ix, int iy, int color)
|
|
|
|
{
|
|
|
|
const uint8_t *src_p = SourceData;
|
2020-05-24 14:32:52 +00:00
|
|
|
for (int y = 0; y < Height - 2; y++)
|
2019-10-23 23:20:58 +00:00
|
|
|
{
|
|
|
|
for (int x = 0; x < SourceWidth; x++)
|
|
|
|
{
|
|
|
|
int byte = *src_p++;
|
2020-05-24 14:32:52 +00:00
|
|
|
uint8_t *pixelstart = dest_p + (ix + 8 * x) * Height + (iy + y);
|
2019-10-23 23:20:58 +00:00
|
|
|
for (int bit = 0; bit < 8; bit++)
|
|
|
|
{
|
|
|
|
if (byte & (128 >> bit))
|
|
|
|
{
|
2020-05-24 14:32:52 +00:00
|
|
|
pixelstart[bit*Height] = color;
|
2019-10-23 23:20:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
memset(dest_p, 0, destSize);
|
|
|
|
|
|
|
|
const int darkcolor = 1;
|
|
|
|
const int brightcolor = 14;
|
|
|
|
for (int xx = 0; xx < 3; xx++) for (int yy = 0; yy < 3; yy++) if (xx != 1 || yy != 1)
|
|
|
|
drawLayer(xx, yy, darkcolor);
|
|
|
|
drawLayer(1, 1, brightcolor);
|
|
|
|
}
|
2020-05-24 14:32:52 +00:00
|
|
|
return Pixels;
|
2019-10-23 23:20:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FHexFont : public FFont
|
|
|
|
{
|
|
|
|
|
|
|
|
public:
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FHexFont :: FHexFont
|
|
|
|
//
|
|
|
|
// Loads a HEX font
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
FHexFont (const char *fontname, const char *lump)
|
|
|
|
{
|
|
|
|
FontName = fontname;
|
|
|
|
|
|
|
|
FirstChar = hexdata.FirstChar;
|
|
|
|
LastChar = hexdata.LastChar;
|
|
|
|
|
|
|
|
Next = FirstFont;
|
|
|
|
FirstFont = this;
|
|
|
|
FontHeight = 16;
|
|
|
|
SpaceWidth = 9;
|
|
|
|
GlobalKerning = 0;
|
|
|
|
translateUntranslated = true;
|
|
|
|
|
|
|
|
LoadTranslations();
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FHexFont :: LoadTranslations
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void LoadTranslations()
|
|
|
|
{
|
|
|
|
const int spacing = 9;
|
|
|
|
double luminosity[256];
|
|
|
|
|
|
|
|
memset (PatchRemap, 0, 256);
|
|
|
|
for (int i = 0; i < 18; i++)
|
|
|
|
{
|
|
|
|
// Create a gradient similar to the old console font.
|
|
|
|
PatchRemap[i] = i;
|
|
|
|
luminosity[i] = i == 1? 0.01 : 0.5 + (i-2) * (0.5 / 17.);
|
|
|
|
}
|
|
|
|
ActiveColors = 18;
|
|
|
|
|
|
|
|
Chars.Resize(LastChar - FirstChar + 1);
|
|
|
|
for (int i = FirstChar; i <= LastChar; i++)
|
|
|
|
{
|
|
|
|
if (hexdata.glyphmap[i] > 0)
|
|
|
|
{
|
|
|
|
auto offset = hexdata.glyphmap[i];
|
|
|
|
int size = hexdata.glyphdata[offset] / 16;
|
2020-05-24 14:32:52 +00:00
|
|
|
Chars[i - FirstChar].TranslatedPic = new FImageTexture(new FHexFontChar (&hexdata.glyphdata[offset+1], size, size * 9, 16));
|
|
|
|
Chars[i - FirstChar].TranslatedPic->SetUseType(ETextureType::FontChar);
|
2019-10-23 23:20:58 +00:00
|
|
|
Chars[i - FirstChar].XMove = size * spacing;
|
2020-05-24 14:32:52 +00:00
|
|
|
TexMan.AddTexture(Chars[i - FirstChar].TranslatedPic);
|
2019-10-23 23:20:58 +00:00
|
|
|
}
|
|
|
|
else Chars[i - FirstChar].XMove = spacing;
|
|
|
|
|
|
|
|
}
|
|
|
|
BuildTranslations (luminosity, nullptr, &TranslationParms[1][0], ActiveColors, nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class FHexFont2 : public FFont
|
|
|
|
{
|
|
|
|
|
|
|
|
public:
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FHexFont :: FHexFont
|
|
|
|
//
|
|
|
|
// Loads a HEX font
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
FHexFont2(const char *fontname, const char *lump)
|
|
|
|
{
|
2020-01-07 00:11:19 +00:00
|
|
|
assert(lump != nullptr);
|
2019-10-23 23:20:58 +00:00
|
|
|
|
|
|
|
FontName = fontname;
|
|
|
|
|
|
|
|
FirstChar = hexdata.FirstChar;
|
|
|
|
LastChar = hexdata.LastChar;
|
|
|
|
|
|
|
|
Next = FirstFont;
|
|
|
|
FirstFont = this;
|
|
|
|
FontHeight = 18;
|
2020-02-02 15:45:32 +00:00
|
|
|
SpaceWidth = 9;
|
2019-10-23 23:20:58 +00:00
|
|
|
GlobalKerning = -1;
|
|
|
|
translateUntranslated = true;
|
|
|
|
|
|
|
|
LoadTranslations();
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// FHexFont :: LoadTranslations
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
2020-01-07 00:11:19 +00:00
|
|
|
void LoadTranslations() override
|
2019-10-23 23:20:58 +00:00
|
|
|
{
|
|
|
|
const int spacing = 9;
|
|
|
|
double luminosity[256];
|
|
|
|
|
|
|
|
memset(PatchRemap, 0, 256);
|
|
|
|
for (int i = 0; i < 18; i++)
|
|
|
|
{
|
|
|
|
// Create a gradient similar to the old console font.
|
|
|
|
PatchRemap[i] = i;
|
|
|
|
luminosity[i] = i / 17.;
|
|
|
|
}
|
|
|
|
ActiveColors = 18;
|
|
|
|
|
|
|
|
Chars.Resize(LastChar - FirstChar + 1);
|
|
|
|
for (int i = FirstChar; i <= LastChar; i++)
|
|
|
|
{
|
|
|
|
if (hexdata.glyphmap[i] > 0)
|
|
|
|
{
|
|
|
|
auto offset = hexdata.glyphmap[i];
|
|
|
|
int size = hexdata.glyphdata[offset] / 16;
|
2020-05-24 14:32:52 +00:00
|
|
|
Chars[i - FirstChar].TranslatedPic = new FImageTexture(new FHexFontChar2(&hexdata.glyphdata[offset + 1], size, 2 + size * 8, 18));
|
|
|
|
Chars[i - FirstChar].TranslatedPic->SetUseType(ETextureType::FontChar);
|
2019-10-23 23:20:58 +00:00
|
|
|
Chars[i - FirstChar].XMove = size * spacing;
|
2020-05-24 14:32:52 +00:00
|
|
|
TexMan.AddTexture(Chars[i - FirstChar].TranslatedPic);
|
2019-10-23 23:20:58 +00:00
|
|
|
}
|
|
|
|
else Chars[i - FirstChar].XMove = spacing;
|
|
|
|
|
|
|
|
}
|
|
|
|
BuildTranslations(luminosity, nullptr, &TranslationParms[0][0], ActiveColors, nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetDefaultTranslation(uint32_t *colors) override
|
|
|
|
{
|
|
|
|
double myluminosity[18];
|
|
|
|
|
|
|
|
myluminosity[0] = 0;
|
|
|
|
for (int i = 1; i < 18; i++)
|
|
|
|
{
|
|
|
|
myluminosity[i] = (i - 1) / 16.;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t othertranslation[256], otherreverse[256];
|
|
|
|
TArray<double> otherluminosity;
|
|
|
|
|
|
|
|
SimpleTranslation(colors, othertranslation, otherreverse, otherluminosity);
|
|
|
|
|
2020-05-24 14:32:52 +00:00
|
|
|
FRemapTable remap(ActiveColors);
|
|
|
|
remap.Remap[0] = 0;
|
2019-11-05 22:35:38 +00:00
|
|
|
remap.Palette[0] = 0;
|
2019-10-23 23:20:58 +00:00
|
|
|
|
|
|
|
for (unsigned l = 1; l < 18; l++)
|
|
|
|
{
|
|
|
|
for (unsigned o = 1; o < otherluminosity.Size() - 1; o++) // luminosity[0] is for the transparent color
|
|
|
|
{
|
|
|
|
if (myluminosity[l] >= otherluminosity[o] && myluminosity[l] <= otherluminosity[o + 1])
|
|
|
|
{
|
2020-04-12 05:51:11 +00:00
|
|
|
PalEntry color1 = GPalette.BaseColors[otherreverse[o]];
|
|
|
|
PalEntry color2 = GPalette.BaseColors[otherreverse[o + 1]];
|
2019-10-23 23:20:58 +00:00
|
|
|
double weight = 0;
|
|
|
|
if (otherluminosity[o] != otherluminosity[o + 1])
|
|
|
|
{
|
|
|
|
weight = (myluminosity[l] - otherluminosity[o]) / (otherluminosity[o + 1] - otherluminosity[o]);
|
|
|
|
}
|
|
|
|
int r = int(color1.r + weight * (color2.r - color1.r));
|
|
|
|
int g = int(color1.g + weight * (color2.g - color1.g));
|
|
|
|
int b = int(color1.b + weight * (color2.b - color1.b));
|
|
|
|
|
|
|
|
r = clamp(r, 0, 255);
|
|
|
|
g = clamp(g, 0, 255);
|
|
|
|
b = clamp(b, 0, 255);
|
2020-05-24 14:32:52 +00:00
|
|
|
remap.Remap[l] = ColorMatcher.Pick(r, g, b);
|
2019-10-23 23:20:58 +00:00
|
|
|
remap.Palette[l] = PalEntry(255, r, g, b);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
forceremap = true;
|
2019-11-05 23:00:33 +00:00
|
|
|
Ranges[CR_UNTRANSLATED] = GLInterface.GetPaletteIndex(remap.Palette);
|
2019-10-23 23:20:58 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
FFont *CreateHexLumpFont (const char *fontname, const char * lump)
|
|
|
|
{
|
|
|
|
if (hexdata.FirstChar == INT_MAX) hexdata.ParseDefinition(lump);
|
|
|
|
return new FHexFont(fontname, lump);
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
FFont *CreateHexLumpFont2(const char *fontname, const char* lump)
|
|
|
|
{
|
|
|
|
if (hexdata.FirstChar == INT_MAX) hexdata.ParseDefinition(lump);
|
|
|
|
return new FHexFont2(fontname, lump);
|
|
|
|
}
|