/* * 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/FontCreate.cpp,v $ * $Revision: 1.1.1.1 $ * $Author: kevinb $ * $Date: 2003-08-26 03:57:45 $ * * Font Creation * * $Log: not supported by cvs2svn $ * * 3 11/16/99 3:18p Samir * updated fonteditor to include tracking. * * 2 4/24/99 5:43p Samir * incremental checkin * * 1 4/17/99 4:04p Samir * new font creator. * */ #include #include #include "FontEditor.h" #include "pstypes.h" #include "pserror.h" #include "bitmap.h" #include "renderer.h" #include "grtext.h" #include "ddio.h" #include "mem.h" #include // Font File stuff typedef FILE *FONTFILE; #define BITS_TO_BYTES(x) (((x) + 7) >> 3) inline int WRITE_FONT_INT(FONTFILE ffile, int i) { return fwrite(&i, sizeof(i), 1, (FILE *)ffile); } inline int WRITE_FONT_SHORT(FONTFILE ffile, int16_t s) { return fwrite(&s, sizeof(s), 1, (FILE *)ffile); } inline int WRITE_FONT_BYTE(FONTFILE ffile, uint8_t c) { return fwrite(&c, sizeof(c), 1, (FILE *)ffile); } inline int WRITE_FONT_DATA(FONTFILE ffile, void *buf, int size, int nelem) { int i; i = (int)fwrite(buf, size, nelem, (FILE *)ffile); ASSERT(i == nelem); return i; } inline FONTFILE OPEN_FONT(char *filename) { FONTFILE fp; fp = (FONTFILE)fopen(filename, "wb"); if (!fp) return NULL; return fp; } inline void CLOSE_FONT(FONTFILE ffile) { fclose((FILE *)ffile); } void message_box(const char *fmt, ...) { #define FIXED_MSGBOX_WIDTH (512) #define FIXED_MSGBOX_HEIGHT (128) std::va_list arglist; char buf[512]; int len; va_start(arglist, fmt); len = std::vsnprintf(buf, 512, fmt, arglist); va_end(arglist); if (len < 0) return; while (1) { int key; int l = (FIXED_SCREEN_WIDTH - FIXED_MSGBOX_WIDTH) / 2; int t = (FIXED_SCREEN_HEIGHT - FIXED_MSGBOX_HEIGHT) / 2; int r = l + FIXED_MSGBOX_WIDTH; int b = t + FIXED_MSGBOX_HEIGHT; FontDoIO(); key = ddio_KeyInKey(); if (key == KEY_ENTER || key == KEY_ESC) { break; } rend_StartFrame(0, 0, FIXED_SCREEN_WIDTH, FIXED_SCREEN_HEIGHT, 0); grtext_SetParameters(0, 0, FIXED_SCREEN_WIDTH, FIXED_SCREEN_HEIGHT); grtext_SetFont(System_font_handle); grtext_SetColor(GR_WHITE); rend_ClearScreen(GR_BLACK); rend_SetFlatColor(GR_WHITE); rend_DrawLine(l, t, r, t); rend_DrawLine(r, t, r, b); rend_DrawLine(l, b, r, b); rend_DrawLine(l, t, l, b); grtext_CenteredPrintf(0, t + 30, buf); grtext_CenteredPrintf(0, b - 30, "Press ENTER or ESC"); grtext_Flush(); rend_EndFrame(); rend_Flip(); } } ////////////////////////////////////////////////////////////////////////////////////// #define COLOR_FONT 1 #define MONOCHROME_FONT 2 #define MAX_FONT_CHARS 256 static bool m_FontProp; static uint8_t m_CharWidths[MAX_FONT_CHARS]; static int16_t m_CharHeight; static int16_t m_CharMaxWidth; static uint16_t *m_FontBmData; static uint16_t *m_DataBuffer, *m_DataPtr; static int m_FontType; static int m_FontBmW, m_FontBmH; static uint16_t m_FgColor, m_BgColor, m_BoxColor; #define PIX(_x, _y) m_FontBmData[(_x) + (_y) * m_FontBmW] int read_font_char(int cur_char, int &bmx, int &bmy) { int w, h, x, y; bool box_edge = false; mprintf(0, " %d-(%d,%d)", cur_char, bmx, bmy); while (PIX(bmx + 1, bmy + 1) == m_BoxColor) bmx++; // deal with double-thick vertical lines for (w = 0; PIX(bmx + 1 + w, bmy + 1) != m_BoxColor; w++) { if (PIX(bmx + 1 + w, bmy) != m_BoxColor) { if (box_edge) { message_box("Read probable premature eol at (%d,%d).\n", bmx + 1 + w, bmy); w--; return 2; } else { message_box("Suspected error at pixel coordindates %d,%d (char %d)", bmx + 1 + w, bmy, cur_char); return 0; } } else { box_edge = true; } } box_edge = false; for (h = 0; PIX(bmx + 1, bmy + 1 + h) != m_BoxColor; h++) { if (PIX(bmx, bmy + 1 + h) != m_BoxColor) { if (box_edge) { message_box("Read probable premature end of row at (%d,%d) (pix=%04x,box=%04x).\n", bmx, bmy + 1 + h, PIX(bmx, bmy + 1 + h), m_BoxColor); h--; return 2; } else { message_box("Suspected error at pixel coordindates %d,%d (char %d)", bmx, bmy + 1 + h, cur_char); return 0; } } else { box_edge = true; } } if (w == 0 || h == 0) { message_box("Character %d has width of %d and height of %d", cur_char, w, h); return 0; } if (w > 255 || w < 0) { message_box("Character %d has bad width %d (maximum=255)", cur_char, w); return 0; } m_CharWidths[cur_char] = w; if (m_CharMaxWidth != 0 && w != m_CharMaxWidth) m_FontProp = true; if (w > m_CharMaxWidth) m_CharMaxWidth = w; if (m_CharHeight == 0) m_CharHeight = h; else if (m_CharHeight != h) { message_box("Char %d is wrong height (height = %d, correct height = %d)", cur_char, h, m_CharHeight); return 0; } for (y = 0; y < h; y++) for (x = 0; x < w; x++) { uint16_t c; if ((c = PIX(bmx + 1 + x, bmy + 1 + y)) == m_BgColor) c = 0; // OLD COMMENT-must go back 565 pure green (old transparent) else if (!(m_FgColor & 0xf000)) // 4444 check for alpha m_FgColor = c; else if (m_FgColor != c) { // OLD_COMMENT-c here is 1555 !! remember this. m_FontType = COLOR_FONT; // mprintf(0, "%d,%d\n", bmx+1+x, bmy+1+y); } // if (c != 0) // *m_DataPtr++ = c; // OLD //COMMENT-here 555 pure green will turn into 565 pure green transparent. else *m_DataPtr++ = c; ASSERT(m_DataPtr < m_DataBuffer + (1024 * 256)); } return 1; } int read_font_line(int cur_char, int &bmx, int &bmy) { while (bmx < m_FontBmW && PIX(bmx + 1, bmy) == m_BoxColor) { int res; res = read_font_char(cur_char, bmx, bmy); if (res == 0) return -1; else if (res == 1) { bmx += m_CharWidths[cur_char] + 1; cur_char++; } else if (res == 2) { cur_char = -1; break; } else break; } return cur_char; } bool extract_font(int bm_handle, tFontFileInfo *ft) { int bmx, bmy; int cur_char; // grab font template bitmap data from the bitmap library and retrieve characters // store relevent information in file record. m_FontBmData = bm_data(bm_handle, 0); m_FontBmW = bm_w(bm_handle, 0); m_FontBmH = bm_h(bm_handle, 0); m_CharMaxWidth = m_CharHeight = 0; m_FontProp = false; m_FontType = MONOCHROME_FONT; m_FgColor = NEW_TRANSPARENT_COLOR; m_DataBuffer = new uint16_t[1024 * MAX_FONT_CHARS]; m_DataPtr = m_DataBuffer; // assume upper left pixel is background color, and first-found other // color is box color m_BgColor = PIX(0, 0); mprintf(0, "m_BgColor=%04x\n", m_BgColor); for (bmy = 0; bmy < m_FontBmH; bmy++) { for (bmx = 0; bmx < m_FontBmW && PIX(bmx, bmy) == m_BgColor; bmx++) ; if (PIX(bmx, bmy) != m_BgColor) break; } m_BoxColor = PIX(bmx, bmy); mprintf(0, "m_BoxColor=%04x\n", m_BoxColor); // Get all data from current line. mprintf(0, "Parsing font..."); mprintf(0, " read_characters: "); for (cur_char = 0; bmy < m_FontBmH && cur_char < MAX_FONT_CHARS;) { cur_char = read_font_line(cur_char, bmx, bmy); if (cur_char == -1) { delete[] m_DataBuffer; m_DataBuffer = NULL; return false; } bmy += m_CharHeight + 1; // search for start of next box for (; bmy < m_FontBmH; bmy++) { for (bmx = 0; bmx < m_FontBmW && PIX(bmx, bmy) != m_BoxColor; bmx++) ; if ((PIX(bmx, bmy) == m_BoxColor) && (PIX(bmx, bmy + 1) == m_BoxColor)) break; } if (bmy == m_FontBmH) break; // if (PIX(bmx,bmy+1) != m_BoxColor) // break; } mprintf(0, "\nDone!\n"); // now fill in font information & store it into our file record. this information // can be used to save the font ft->width = m_CharMaxWidth; ft->height = m_CharHeight; ft->flags = (m_FontProp ? FT_PROPORTIONAL : 0) + ((m_FontType == COLOR_FONT) ? FT_COLOR : 0); ft->baseline = m_CharHeight; ft->min_ascii = 0; ft->max_ascii = cur_char - 1; ft->raw_data = (uint8_t *)m_DataBuffer; ft->char_data = NULL; ft->char_widths = m_CharWidths; ft->kern_data = NULL; ft->flags |= FT_FMT4444; return true; } ////////////////////////////////////////////////////////////////////////////////////////// // Font file format. // // id=0xfeedbaba // maximum width // maximum height // font flags // baseline value (bashed brightness value) // min and max ascii // name // for proportional fonts, store all widths // no kerning. // store font data. void FontCreate(const char *fnt_file_source, const char *fnt_file_dest, int min_ascii) { // import font FONTFILE ffile; tFontFileInfo ft; int bm_handle; char fontname[32]; strcpy(fontname, "d3font"); bm_handle = bm_AllocLoadFileBitmap(fnt_file_source, 0, BITMAP_FORMAT_4444); if (bm_handle < 0) { Error("Couldn't open %s for extraction.", fnt_file_source); } // Get MinAscii and other needed info before extracting font. if (!extract_font(bm_handle, &ft)) { Error("Unable to generate font from %s.", fnt_file_source); return; } /////////////////////////////////////////////////////////////////////////////// // Adjust font record for file write. int num_char; ft.kern_data = NULL; ft.min_ascii = min_ascii; ft.max_ascii = ft.min_ascii + ft.max_ascii; // add newstyle variables here. ft.flags |= FT_FFI2; ft.ffi2.tracking = 0; // write to file mprintf(0, "Saving font data..."); ffile = OPEN_FONT((char *)fnt_file_dest); if (!ffile) { Error("Unable to save font %s.", fnt_file_dest); } // Write file id. WRITE_FONT_INT(ffile, 0xfeedbaba); // Write Header ft.baseline = 0; WRITE_FONT_SHORT(ffile, ft.width); WRITE_FONT_SHORT(ffile, ft.height); WRITE_FONT_SHORT(ffile, ft.flags); WRITE_FONT_SHORT(ffile, ft.baseline); WRITE_FONT_BYTE(ffile, ft.min_ascii); WRITE_FONT_BYTE(ffile, ft.max_ascii); WRITE_FONT_DATA(ffile, fontname, 32, 1); WRITE_FONT_SHORT(ffile, (int16_t)ft.ffi2.tracking); WRITE_FONT_DATA(ffile, ft.ffi2.reserved, sizeof(ft.ffi2.reserved), 1); num_char = (int)(ft.max_ascii - ft.min_ascii + 1); // Write widths now if necessary.(FT_PROPORTIONAL) if (ft.flags & FT_PROPORTIONAL) { for (int i = 0; i < num_char; i++) WRITE_FONT_SHORT(ffile, ft.char_widths[i]); } if (ft.flags & FT_COLOR) { WRITE_FONT_INT(ffile, (int)(m_DataPtr - m_DataBuffer) * sizeof(uint16_t)); WRITE_FONT_DATA(ffile, (uint8_t *)m_DataBuffer, (m_DataPtr - m_DataBuffer) * sizeof(uint16_t), 1); } else { // bitpack for mono font storage:: 16bpp -> 8 bits/1 byte int i, x, y, w, cnt = 0; uint16_t *p = m_DataBuffer; uint8_t *bits; bits = (uint8_t *)mem_malloc(256 * MAX_FONT_CHARS); for (i = 0; i < num_char; i++) { for (y = 0; y < ft.height; y++) { uint8_t mask, datum; w = (ft.flags & FT_PROPORTIONAL) ? ft.char_widths[i] : ft.width; ASSERT(w <= 48); // Max width size shall be 32 pixels mask = 0x80; datum = 0; for (x = 0; x < w; x++) { if (mask == 0) { bits[cnt++] = datum; datum = 0; mask = 0x80; } if (*p++ != 0x07e0) datum |= mask; mask >>= 1; } bits[cnt++] = datum; } } WRITE_FONT_INT(ffile, cnt); WRITE_FONT_DATA(ffile, bits, cnt, 1); mem_free(bits); } // We should add kerning support here. // Close font. CLOSE_FONT(ffile); delete[] m_DataBuffer; // allocated in extract_font! bm_FreeBitmap(bm_handle); int new_font_handle = grfont_Load((char *)fnt_file_dest); if (new_font_handle < 0) { message_box("New font failed to load!"); } // print out stats. while (1) { int x, y, i; FontDoIO(); if (ddio_KeyInKey() == KEY_ENTER) { break; } rend_StartFrame(0, 0, FIXED_SCREEN_WIDTH, FIXED_SCREEN_HEIGHT); grtext_SetParameters(0, 0, FIXED_SCREEN_WIDTH, FIXED_SCREEN_HEIGHT); grtext_SetFont(System_font_handle); grtext_SetColor(GR_WHITE); rend_ClearScreen(GR_BLACK); grtext_Printf(0, 12, "Created font:\2\65 %s", fnt_file_dest); grtext_Printf(0, 28, "Type:\2\65 %s, %s", (ft.flags & FT_COLOR) ? "COLOR" : "MONO", (ft.flags & FT_PROPORTIONAL) ? "PROPORTIONAL" : "FIXED WIDTH"); grtext_Printf(0, 40, "Ascii:\2\65 %d to %d", ft.min_ascii, ft.max_ascii); grtext_Printf(0, 52, "Maxwidth:\2\65 %d", ft.width); grtext_Printf(0, 64, "Maxheight:\2\65 %d", ft.height); grtext_Printf(0, FIXED_SCREEN_HEIGHT - 20, "Press ENTER to quit."); // spew new font. x = 10; y = 120; grtext_SetFont(new_font_handle); for (i = ft.min_ascii; i <= ft.max_ascii; i++) { char str[2]; str[0] = i; str[1] = 0; if ((x + grfont_GetCharWidth(new_font_handle, i)) > FIXED_SCREEN_WIDTH) { x = 10; y += grfont_GetHeight(new_font_handle) + 1; } grtext_Puts(x, y, str); x += grfont_GetCharWidth(new_font_handle, i) + 1; } grtext_Flush(); rend_EndFrame(); rend_Flip(); } grfont_Free(new_font_handle); } void FontView(const char *fnt_file_name) { tFontTemplate ft; int new_font_handle; new_font_handle = grfont_Load((char *)fnt_file_name); if (new_font_handle < 0) { Error("Couldn't load font %s.", fnt_file_name); } grfont_LoadTemplate((char *)fnt_file_name, &ft); ddio_KeyFlush(); // print out stats. while (1) { int x, y, i; FontDoIO(); if (ddio_KeyInKey() == KEY_ENTER) { break; } rend_StartFrame(0, 0, FIXED_SCREEN_WIDTH, FIXED_SCREEN_HEIGHT); grtext_SetParameters(0, 0, FIXED_SCREEN_WIDTH, FIXED_SCREEN_HEIGHT); grtext_SetFont(System_font_handle); grtext_SetColor(GR_WHITE); rend_ClearScreen(GR_BLACK); grtext_Printf(0, 12, "Font:\2\65 %s", fnt_file_name); grtext_Printf(0, 28, "Type:\2\65 %s, %s, %s", (ft.monochromatic ? "MONO" : "COLOR"), (ft.proportional ? "PROPORTIONAL" : "FIXED WIDTH"), (ft.newstyle ? "4444" : "1555")); grtext_Printf(0, 40, "Ascii:\2\65 %d to %d", ft.min_ascii, ft.max_ascii); grtext_Printf(0, 52, "Maxwidth:\2\65 %d", ft.ch_maxwidth); grtext_Printf(0, 64, "Maxheight:\2\65 %d", ft.ch_height); if (ft.ffi2) { grtext_Printf(0, 76, "Tracking:\2\65 %d", (int)ft.ch_tracking); } grtext_Printf(0, FIXED_SCREEN_HEIGHT - 20, "Press ENTER to quit."); // spew new font. x = 10; y = 120; grtext_SetFont(new_font_handle); for (i = ft.min_ascii; i <= ft.max_ascii; i++) { char str[2]; str[0] = i; str[1] = 0; if ((x + grfont_GetCharWidth(new_font_handle, i)) > FIXED_SCREEN_WIDTH) { x = 10; y += grfont_GetHeight(new_font_handle) + 1; } grtext_Puts(x, y, str); x += grfont_GetCharWidth(new_font_handle, i) + 1; } grtext_Flush(); rend_EndFrame(); rend_Flip(); } grfont_Free(new_font_handle); }