/*
* Descent 3
* Copyright (C) 2024 Parallax Software
*
* This program 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.
*
* This program 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 this program. If not, see .
--- HISTORICAL COMMENTS FOLLOW ---
* $Source: /home/kevin/cvsstuff/descent3/descent3/Main/FontEditor/FontKern.cpp,v $
* $Revision: 1.1.1.1 $
* $Author: kevinb $
* $Date: 2003-08-26 03:57:45 $
*
* Kerning
*
* $Log: not supported by cvs2svn $
*
* 4 11/16/99 3:18p Samir
* updated fonteditor to include tracking.
*
* 3 5/19/99 8:48p Samir
* added kerning dump utilitiy.
*
* 2 4/17/99 4:05p Samir
* complete font editor.
*
*/
#include "FontEditor.h"
#include "pserror.h"
#include "renderer.h"
#include "ddio.h"
#include "grtext.h"
#include "bitmap.h"
#include "mem.h"
#include
#include
chunked_bitmap Editor_bkg;
tFontTemplate Font_template;
char *SampleText = "This is some sample text that is here to\n"
"Show you how the antialiasing will\n"
"look over different color backgrounds\n"
"KERN PAIRS: VaWaVeWeVAV-LyT.T,TyTvTcYe\n";
// this function determines if a kerning pair exists in the current font template
// if there is no kerning structure, add it. if no kerning in font, add it to the template
#define KERNINFO_PAIR_SIZE 3
int fonttool_get_kerning(int c1, int c2, int *pairnum = NULL);
int fonttool_get_kerning(int c1, int c2, int *pairnum) {
int i;
uint8_t *kern_data;
kern_data = Font_template.kern_data;
if (kern_data) {
i = 0;
while (kern_data[i] != 255) {
if (kern_data[i] == (uint8_t)c1 && kern_data[i + 1] == (uint8_t)c2) {
if (pairnum)
*pairnum = i / 3;
return (int)((int8_t)kern_data[i + 2]);
}
i += KERNINFO_PAIR_SIZE;
}
}
return 0;
}
void fonttool_set_kerning(int c1, int c2, int dist) {
int i, j;
uint8_t *kern_data;
if (c1 == 255 || c2 == 255) {
mprintf(0, "seting illegal kerning of 255!\n");
return;
}
kern_data = Font_template.kern_data;
if (!kern_data) {
// create a one entry kern table.
kern_data = (uint8_t *)mem_malloc(KERNINFO_PAIR_SIZE * 2);
kern_data[0] = (uint8_t)c1;
kern_data[1] = (uint8_t)c2;
kern_data[2] = (int8_t)dist;
kern_data[3] = 255;
kern_data[4] = 255;
kern_data[5] = 0;
Font_template.kern_data = kern_data;
mprintf(0, "adding first kerning pair %d,%d\n", c1, c2);
return;
}
// check for match in current list.
i = 0;
while (kern_data[i] != 255) {
if (kern_data[i] == (uint8_t)c1 && kern_data[i + 1] == (uint8_t)c2) {
kern_data[i + 2] = (int8_t)dist;
if (dist == 0) {
// remove this kerning pair.
j = i;
do {
kern_data[j] = kern_data[j + 3];
kern_data[j + 1] = kern_data[j + 4];
kern_data[j + 2] = kern_data[j + 5];
j += KERNINFO_PAIR_SIZE;
} while (kern_data[j] != 255);
if (i == 0) {
// last pair, deallocate kern_data and end.
mprintf(0, "removing last kerning pair\n");
mem_free(kern_data);
kern_data = NULL;
} else {
mprintf(0, "removing kerning pair %d,%d\n", c1, c2);
}
}
Font_template.kern_data = kern_data;
return;
}
i += KERNINFO_PAIR_SIZE;
}
// new entry.
uint8_t *new_kern_data;
int n_pairs = i / KERNINFO_PAIR_SIZE;
new_kern_data = (uint8_t *)mem_malloc((n_pairs + 2) * sizeof(uint8_t) * KERNINFO_PAIR_SIZE);
for (i = 0; i < n_pairs; i++) {
new_kern_data[i * KERNINFO_PAIR_SIZE] = kern_data[i * 3];
new_kern_data[i * KERNINFO_PAIR_SIZE + 1] = kern_data[i * 3 + 1];
new_kern_data[i * KERNINFO_PAIR_SIZE + 2] = kern_data[i * 3 + 2];
}
new_kern_data[i * KERNINFO_PAIR_SIZE] = (uint8_t)c1;
new_kern_data[i * KERNINFO_PAIR_SIZE + 1] = (uint8_t)c2;
new_kern_data[i * KERNINFO_PAIR_SIZE + 2] = (int8_t)dist;
i++;
new_kern_data[i * KERNINFO_PAIR_SIZE] = 255;
new_kern_data[i * KERNINFO_PAIR_SIZE + 1] = 255;
new_kern_data[i * KERNINFO_PAIR_SIZE + 2] = 0;
mem_free(kern_data);
Font_template.kern_data = new_kern_data;
mprintf(0, "adding first kerning pair %d,%d (total=%d)\n", c1, c2, n_pairs + 1);
}
void fonttool_remove_kerning() {
if (Font_template.kern_data) {
mem_free(Font_template.kern_data);
Font_template.kern_data = NULL;
}
}
/////////////////////////////////////////////////////////////////////////////
void FontKern(const char *fnt_file_name) {
int bm_handle;
int font_handle;
int c1 = 'b', c2 = 'u', d;
int last_good_pair, current_pair;
int first_item = 0;
int current_item = 0;
int num_items_displayed = 1;
bool done = false;
int16_t text_alpha = 255;
int red_comp, green_comp, blue_comp;
ddgr_color text_color;
red_comp = green_comp = blue_comp = 255;
bm_handle = bm_AllocLoadFileBitmap("FontTool.ogf", 0);
if (bm_handle <= BAD_BITMAP_HANDLE) {
Error("Error loading FontEditor art.");
}
if (!bm_CreateChunkedBitmap(bm_handle, &Editor_bkg)) {
Error("Error chunking FontEditor art.");
}
bm_FreeBitmap(bm_handle);
// load font into memory.
font_handle = grfont_Load((char *)fnt_file_name);
if (font_handle < 0) {
Error("Error loading font %s", fnt_file_name);
}
if (!grfont_LoadTemplate((char *)fnt_file_name, &Font_template)) {
Error("Error loading font template %s", fnt_file_name);
}
if (Font_template.ffi2 == false) {
Font_template.ffi2 = true;
Font_template.ch_tracking = 0;
}
if (Font_template.kern_data) {
current_pair = 0;
} else {
current_pair = -1;
}
last_good_pair = current_pair;
while (!done) {
int key;
FontDoIO();
key = ddio_KeyInKey();
switch (key) {
case KEY_ESC:
done = true;
break;
case KEY_F5:
grfont_Free(font_handle);
grfont_FreeTemplate(&Font_template);
font_handle = grfont_Load((char *)fnt_file_name);
if (font_handle == -1) {
Error("Font failed reload test!");
}
grfont_LoadTemplate((char *)fnt_file_name, &Font_template);
break;
case KEY_MINUS:
if (Font_template.ch_tracking > -80) {
Font_template.ch_tracking--;
grfont_SetTracking(font_handle, Font_template.ch_tracking);
}
break;
case KEY_EQUAL:
if (Font_template.ch_tracking < 80) {
Font_template.ch_tracking++;
grfont_SetTracking(font_handle, Font_template.ch_tracking);
}
break;
case KEY_F10:
grfont_Free(font_handle);
if (!grfont_SetTemplate(fnt_file_name, &Font_template)) {
Error("Failed to save font correctly!");
}
grfont_FreeTemplate(&Font_template);
font_handle = grfont_Load((char *)fnt_file_name);
if (font_handle == -1) {
Error("Font failed reload test on save!");
}
grfont_LoadTemplate((char *)fnt_file_name, &Font_template);
mprintf(0, "font saved and reloaded correctly?\n");
done = true;
break;
case KEY_F6:
fonttool_remove_kerning();
break;
case KEY_COMMA:
text_alpha -= 16;
if (text_alpha < 0)
text_alpha = 0;
break;
case KEY_PERIOD:
text_alpha += 16;
if (text_alpha > 255)
text_alpha = 255;
break;
case KEY_PAD7:
c1++;
if (c1 > Font_template.max_ascii)
c1 = Font_template.min_ascii;
break;
case KEY_PAD9:
c2++;
if (c2 > Font_template.max_ascii)
c2 = Font_template.min_ascii;
break;
case KEY_PAD1:
c1--;
if (c1 < Font_template.min_ascii)
c1 = Font_template.max_ascii;
break;
case KEY_PAD3:
c2--;
if (c2 < Font_template.min_ascii)
c2 = Font_template.max_ascii;
break;
case KEY_PAD4:
d = fonttool_get_kerning(c1, c2);
fonttool_set_kerning(c1, c2, d - 1);
grfont_SetKerning(font_handle, Font_template.kern_data);
break;
case KEY_PAD5:
fonttool_set_kerning(c1, c2, 0);
break;
case KEY_PAD6:
d = fonttool_get_kerning(c1, c2);
fonttool_set_kerning(c1, c2, d + 1);
grfont_SetKerning(font_handle, Font_template.kern_data);
break;
case KEY_PAD2:
if (current_pair < 0) {
current_pair = last_good_pair;
}
if (current_pair >= 0) {
uint8_t *kern_data = &Font_template.kern_data[current_pair * 3];
if (kern_data[3] != 255) {
current_pair++;
kern_data = &Font_template.kern_data[current_pair * 3];
}
c1 = kern_data[0];
c2 = kern_data[1];
}
break;
case KEY_PAD8:
if (current_pair < 0) {
current_pair = last_good_pair;
}
if (current_pair >= 0) {
uint8_t *kern_data = &Font_template.kern_data[current_pair * 3];
if (kern_data != Font_template.kern_data) {
// gosh, comparing pointers like this may not be the best method...this is shitty code...
current_pair--;
kern_data = &Font_template.kern_data[current_pair * 3];
}
c1 = kern_data[0];
c2 = kern_data[1];
}
break;
case KEY_R:
red_comp = red_comp ? 0 : 255;
break;
case KEY_G:
green_comp = green_comp ? 0 : 255;
break;
case KEY_B:
blue_comp = blue_comp ? 0 : 255;
break;
}
// draw.
text_color = GR_RGB(red_comp, green_comp, blue_comp);
rend_StartFrame(0, 0, FIXED_SCREEN_WIDTH, FIXED_SCREEN_HEIGHT, 0);
grtext_SetParameters(0, 0, FIXED_SCREEN_WIDTH, FIXED_SCREEN_HEIGHT);
grtext_SetFont(font_handle);
grtext_SetColor(text_color);
grtext_SetAlpha((uint8_t)text_alpha);
rend_ClearScreen(GR_BLACK);
rend_DrawChunkedBitmap(&Editor_bkg, 0, 0, 255);
grtext_Printf(240, 210, "%c (%d)", c1, c1);
grtext_Printf(340, 210, "%c (%d)", c2, c2);
grtext_CenteredPrintf(0, 240, "Ham%c%crger", c1, c2);
grtext_CenteredPrintf(0, 270, "HAM%c%cRGER", c1, c2);
grtext_CenteredPrintf(0, 300, "Offset: %d pixels", fonttool_get_kerning(c1, c2));
grtext_CenteredPrintf(0, 320, "Tracking: %d pixels", (int)Font_template.ch_tracking);
{
int th;
th = grtext_GetTextHeight(SampleText);
grtext_Printf(10, 350, SampleText);
grtext_Printf(10, 350 + th + 10, SampleText);
}
// display kerned pairs.
int x = 5, y = 200, i, n, widest = 0;
int n_pairs = 0, stop;
int tmpp = -1;
char kerntext[16];
if (Font_template.kern_data) {
i = 0;
while (Font_template.kern_data[i] != 255) {
n_pairs++;
i += KERNINFO_PAIR_SIZE;
}
}
// update current pair values
if (current_pair >= n_pairs) {
current_pair = -1;
}
fonttool_get_kerning(c1, c2, &tmpp);
if (tmpp != -1) {
current_pair = tmpp;
} else {
if (current_pair > -1)
last_good_pair = current_pair;
current_pair = -1;
}
if (current_pair > -1) {
current_item = current_pair;
}
if (current_item < 0) {
current_item = 0;
}
if (current_item >= n_pairs) {
current_item = n_pairs - 1;
}
if (current_item < first_item) {
first_item = current_item;
}
if (current_item >= (first_item + num_items_displayed)) {
first_item = current_item - num_items_displayed + 1;
}
if (n_pairs <= num_items_displayed) {
first_item = 0;
}
stop = first_item + num_items_displayed;
if (stop > n_pairs)
stop = n_pairs;
for (i = first_item, n = 0; i < stop; i++) {
int tw, th;
ASSERT(Font_template.kern_data);
uint8_t *kern_data = &Font_template.kern_data[i * 3];
if (i == current_pair) {
rend_SetFlatColor(GR_RGB(255, 0, 0));
rend_DrawLine(x, y, x + 6, y + 5);
rend_DrawLine(x + 6, y + 5, x, y + 8);
rend_DrawLine(x, y + 8, x, y);
}
sprintf(kerntext, "%c%c", kern_data[0], kern_data[1]);
grtext_Puts(x + 8, y, kerntext);
n++;
tw = grtext_GetTextLineWidth(kerntext) + 8;
th = grtext_GetTextHeight(kerntext);
if (tw > widest) {
widest = tw;
}
y += th;
if (y > 330) {
y = 200;
x += widest + 5;
if (x > 150) {
num_items_displayed = n;
break;
}
widest = 0;
}
}
if (i == stop) {
num_items_displayed++;
}
if (num_items_displayed < 1) {
num_items_displayed = 1;
}
if (num_items_displayed > n_pairs) {
num_items_displayed = n_pairs;
}
grtext_Flush();
rend_EndFrame();
rend_Flip();
}
grfont_FreeTemplate(&Font_template);
grfont_Free(font_handle);
bm_DestroyChunkedBitmap(&Editor_bkg);
}
void FontKernDump(const char *fnt_file_name) {
if (!grfont_LoadTemplate((char *)fnt_file_name, &Font_template)) {
Error("Error loading font template %s", fnt_file_name);
}
if (Font_template.kern_data) {
FILE *fp;
char filename[_MAX_PATH];
sprintf(filename, "%s.kern", fnt_file_name);
fp = fopen(filename, "wt");
if (fp) {
uint8_t *kern_data = Font_template.kern_data;
while (kern_data[0] != 255) {
uint8_t c1 = kern_data[0];
uint8_t c2 = kern_data[1];
int8_t delta = (int8_t)kern_data[2];
fprintf(fp, "%c %c => %d spacing\n", c1, c2, (int)delta);
kern_data += 3;
}
fclose(fp);
}
}
grfont_FreeTemplate(&Font_template);
}