Descent3/legacy/FontEditor/FontKern.cpp

522 lines
12 KiB
C++
Raw Normal View History

/*
* 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 <http://www.gnu.org/licenses/>.
--- 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 <stdio.h>
#include <stdlib.h>
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;
ubyte *kern_data;
kern_data= Font_template.kern_data;
if (kern_data) {
i=0;
while (kern_data[i] != 255)
{
if (kern_data[i] == (ubyte)c1 && kern_data[i+1] == (ubyte)c2) {
if (pairnum) *pairnum = i/3;
return (int)((sbyte)kern_data[i+2]);
}
i+=KERNINFO_PAIR_SIZE;
}
}
return 0;
}
void fonttool_set_kerning( int c1, int c2, int dist )
{
int i,j;
ubyte *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 = (ubyte *)mem_malloc(KERNINFO_PAIR_SIZE*2);
kern_data[0] = (ubyte)c1;
kern_data[1] = (ubyte)c2;
kern_data[2] = (sbyte)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] == (ubyte)c1 && kern_data[i+1] == (ubyte)c2) {
kern_data[i+2] = (sbyte)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.
ubyte *new_kern_data;
int n_pairs = i/KERNINFO_PAIR_SIZE;
new_kern_data = (ubyte *)mem_malloc((n_pairs+2)*sizeof(ubyte)*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] = (ubyte)c1;
new_kern_data[i*KERNINFO_PAIR_SIZE+1] = (ubyte)c2;
new_kern_data[i*KERNINFO_PAIR_SIZE+2] = (sbyte)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;
short 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) {
ubyte *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) {
ubyte *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((ubyte)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);
ubyte *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) {
ubyte *kern_data = Font_template.kern_data;
while (kern_data[0] != 255)
{
ubyte c1 = kern_data[0];
ubyte c2 = kern_data[1];
sbyte delta = (sbyte)kern_data[2];
fprintf(fp, "%c %c => %d spacing\n", c1,c2,(int)delta);
kern_data += 3;
}
fclose(fp);
}
}
grfont_FreeTemplate(&Font_template);
}