aboutsummaryrefslogtreecommitdiff
path: root/saga/font.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'saga/font.cpp')
-rw-r--r--saga/font.cpp721
1 files changed, 721 insertions, 0 deletions
diff --git a/saga/font.cpp b/saga/font.cpp
new file mode 100644
index 0000000000..18f2aaf276
--- /dev/null
+++ b/saga/font.cpp
@@ -0,0 +1,721 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004 The ScummVM project
+ *
+ * The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * $Header$
+ *
+ */
+/*
+
+ Description:
+
+ Font management and font drawing module
+
+ Notes:
+*/
+
+#include "reinherit.h"
+
+#include "yslib.h"
+
+/*
+ * Uses the following modules:
+\*--------------------------------------------------------------------------*/
+#include "rscfile_mod.h"
+#include "game_mod.h"
+
+/*
+ * Begin module
+\*--------------------------------------------------------------------------*/
+#include "font_mod.h"
+#include "font.h"
+
+namespace Saga {
+
+static R_FONT_MODULE FontModule;
+
+int FONT_Init(void)
+{
+
+ R_GAME_FONTDESC *gamefonts;
+ int i;
+
+ if (FontModule.init) {
+
+ FontModule.err_str = "Font module already initialized.";
+
+ return R_FAILURE;
+ }
+
+ /* Load font module resource context
+ * \*------------------------------------------------------------ */
+ if (GAME_GetFileContext(&FontModule.font_ctxt,
+ R_GAME_RESOURCEFILE, 0) != R_SUCCESS) {
+
+ FontModule.err_str = "Couldn't get resource context.";
+
+ return R_FAILURE;
+ }
+
+ /* Allocate font table
+ * \*------------------------------------------------------------ */
+ GAME_GetFontInfo(&gamefonts, &FontModule.n_fonts);
+
+ assert(FontModule.n_fonts > 0);
+
+ FontModule.fonts = (R_FONT **)malloc(FontModule.n_fonts *
+ sizeof *FontModule.fonts);
+ if (FontModule.fonts == NULL) {
+
+ FontModule.err_str = "Memory allocation failure.";
+
+ return R_MEM;
+ }
+
+ for (i = 0; i < FontModule.n_fonts; i++) {
+
+ FONT_Load(gamefonts[i].font_rn, gamefonts[i].font_id);
+ }
+
+ FontModule.init = 1;
+
+ return R_SUCCESS;
+}
+
+int FONT_Shutdown(void)
+{
+
+ int i;
+
+ R_printf(R_STDOUT, "FONT_Shutdown(): Freeing fonts.\n");
+/*
+ for ( i = 0 ; i < R_FONT_COUNT ; i ++ ) {
+
+ if ( FontModule.fonts[i] != NULL ) {
+
+ if ( FontModule.fonts[i]->normal_loaded ) {
+ free( FontModule.fonts[i]->normal->font_free_p );
+ free( FontModule.fonts[i]->normal );
+ }
+
+ if ( FontModule.fonts[i]->outline_loaded ) {
+ free( FontModule.fonts[i]->outline->font_free_p );
+ free( FontModule.fonts[i]->outline );
+ }
+ }
+
+ free( FontModule.fonts[i] );
+ }
+*/
+ return R_SUCCESS;
+}
+
+int FONT_Load(ulong font_rn, int font_id)
+{
+
+ R_FONT_HEADER fh;
+ R_FONT *font;
+ R_FONT_STYLE *normal_font;
+
+ uchar *fontres_p;
+ size_t fontres_len;
+ size_t remain;
+
+ const uchar *read_p;
+
+ int nbits;
+ int c;
+
+ if ((font_id < 0) || (font_id >= FontModule.n_fonts)) {
+ return R_FAILURE;
+ }
+
+ /* Load font resource
+ * \*------------------------------------------------------------- */
+ if (RSC_LoadResource(FontModule.font_ctxt,
+ font_rn, &fontres_p, &fontres_len) != R_SUCCESS) {
+
+ FontModule.err_str = "Couldn't load font resource.";
+
+ return R_FAILURE;
+ }
+
+ if (fontres_len < R_FONT_DESCSIZE) {
+
+ FontModule.err_str = "Invalid font length.";
+ }
+
+ read_p = fontres_p;
+ remain = fontres_len;
+
+ /* Create new font structure
+ * \*------------------------------------------------------------- */
+ font = (R_FONT *)malloc(sizeof *font);
+ if (font == NULL) {
+ FontModule.err_str = "Memory allocation error.";
+
+ return R_MEM;
+ }
+
+ /* Read font header
+ * \*------------------------------------------------------------- */
+ fh.c_height = ys_read_u16_le(read_p, &read_p);
+ fh.c_width = ys_read_u16_le(read_p, &read_p);
+ fh.row_length = ys_read_u16_le(read_p, &read_p);
+
+#if R_FONT_DBGLVL >= R_DEBUG_INFO
+ R_printf(R_STDOUT, "FONT_Load(): Reading font resource...\n");
+#endif
+
+#if R_FONT_DBGLVL >= R_DEBUG_VERBOSE
+ R_printf(R_STDOUT, "Character width:\t%d\n", fh.c_width);
+ R_printf(R_STDOUT, "Character height:\t%d\n", fh.c_height);
+ R_printf(R_STDOUT, "Row padding:\t%d\n", fh.row_length);
+#endif
+
+ /* Create normal font style
+ * \*------------------------------------------------------------- */
+ normal_font = (R_FONT_STYLE *)malloc(sizeof *normal_font);
+ if (normal_font == NULL) {
+
+ FontModule.err_str = "Memory allocation error.";
+ free(font);
+
+ return R_MEM;
+ }
+
+ normal_font->font_free_p = fontres_p;
+ normal_font->hdr.c_height = fh.c_height;
+ normal_font->hdr.c_width = fh.c_width;
+ normal_font->hdr.row_length = fh.row_length;
+
+ for (c = 0; c < R_FONT_CHARCOUNT; c++) {
+ normal_font->fce[c].index = ys_read_u16_le(read_p, &read_p);
+ }
+
+ for (c = 0; c < R_FONT_CHARCOUNT; c++) {
+ nbits = normal_font->fce[c].width =
+ ys_read_u8(read_p, &read_p);
+ normal_font->fce[c].byte_width = GetByteLen(nbits);
+ }
+
+ for (c = 0; c < R_FONT_CHARCOUNT; c++) {
+ normal_font->fce[c].flag = ys_read_u8(read_p, &read_p);
+ }
+
+ for (c = 0; c < R_FONT_CHARCOUNT; c++) {
+ normal_font->fce[c].tracking = ys_read_u8(read_p, &read_p);
+ }
+
+ if ((read_p - fontres_p) != R_FONT_DESCSIZE) {
+
+ R_printf(R_STDERR, "Invalid font resource size.\n");
+ return R_FAILURE;
+ }
+
+ normal_font->font_p = (uchar *) read_p;
+
+ font->normal = normal_font;
+ font->normal_loaded = 1;
+
+ /* Create outline font style
+ * \*------------------------------------------------------------- */
+ font->outline = FONT_CreateOutline(normal_font);
+ font->outline_loaded = 1;
+
+ /* Set font data
+ * \*------------------------------------------------------------- */
+
+ FontModule.fonts[font_id] = font;
+
+ return R_SUCCESS;
+}
+
+int FONT_GetHeight(int font_id)
+{
+
+ R_FONT *font;
+
+ if (!FontModule.init) {
+ return R_FAILURE;
+ }
+
+ if ((font_id < 0) ||
+ (font_id >= FontModule.n_fonts) ||
+ (FontModule.fonts[font_id] == NULL)) {
+
+ FontModule.err_str = "Invalid font id.";
+
+ return R_FAILURE;
+ }
+
+ font = FontModule.fonts[font_id];
+
+ return font->normal->hdr.c_height;
+}
+
+static R_FONT_STYLE *FONT_CreateOutline(R_FONT_STYLE * src_font)
+{
+
+ R_FONT_STYLE *new_font;
+ unsigned char *new_font_data;
+ size_t new_font_data_len;
+
+ int s_width = src_font->hdr.c_width;
+ int s_height = src_font->hdr.c_height;
+
+ int new_row_len = 0;
+ int row;
+ int i;
+
+ int index;
+ size_t index_offset = 0;
+
+ int new_byte_width;
+ int old_byte_width;
+
+ int current_byte;
+
+ unsigned char *base_ptr;
+ unsigned char *src_ptr;
+ unsigned char *dest_ptr1;
+ unsigned char *dest_ptr2;
+ unsigned char *dest_ptr3;
+
+ unsigned char c_rep;
+
+ /* Create new font style structure
+ * \*------------------------------------------------------------- */
+ new_font = (R_FONT_STYLE *)malloc(sizeof *new_font);
+
+ if (new_font == NULL) {
+ FontModule.err_str = "Memory allocation error.";
+
+ return NULL;
+ }
+
+ memset(new_font, 0, sizeof *new_font);
+
+ /* Populate new font style character data
+ * \*------------------------------------------------------------- */
+ for (i = 0; i < R_FONT_CHARCOUNT; i++) {
+
+ new_byte_width = 0;
+ old_byte_width = 0;
+
+ index = src_font->fce[i].index;
+ if ((index > 0) || (i == R_FONT_FIRSTCHAR)) {
+ index += index_offset;
+ }
+
+ new_font->fce[i].index = index;
+ new_font->fce[i].tracking = src_font->fce[i].tracking;
+ new_font->fce[i].flag = src_font->fce[i].flag;
+
+ if (src_font->fce[i].width != 0) {
+
+ new_byte_width =
+ GetByteLen(src_font->fce[i].width + 2);
+ old_byte_width = GetByteLen(src_font->fce[i].width);
+
+ if (new_byte_width > old_byte_width) {
+ index_offset++;
+ }
+ }
+
+ new_font->fce[i].width = src_font->fce[i].width + 2;
+ new_font->fce[i].byte_width = new_byte_width;
+ new_row_len += new_byte_width;
+ }
+
+#if R_FONT_DBGLVL >= R_DEBUG_VERBOSE
+ R_printf(R_STDOUT, "New row length: %d\n", new_row_len);
+#endif
+
+ new_font->hdr.c_width = s_width + 2;
+ new_font->hdr.c_height = s_height + 2;
+ new_font->hdr.row_length = new_row_len;
+
+ /* Allocate new font representation storage
+ * \*------------------------------------------------------------- */
+ new_font_data_len = new_row_len * (s_height + 2);
+ new_font_data = (unsigned char *)malloc(new_font_data_len);
+
+ if (new_font_data == NULL) {
+ FontModule.err_str = "Memory allocation error.";
+
+ return NULL;
+ }
+
+ memset(new_font_data, 0, new_font_data_len);
+
+ new_font->font_free_p = new_font_data;
+ new_font->font_p = new_font_data;
+
+ /* Generate outline font representation
+ * \*------------------------------------------------------------- */
+ for (i = 0; i < R_FONT_CHARCOUNT; i++) {
+
+ for (row = 0; row < s_height; row++) {
+
+ for (current_byte = 0;
+ current_byte < new_font->fce[i].byte_width;
+ current_byte++) {
+
+ base_ptr =
+ new_font->font_p + new_font->fce[i].index +
+ current_byte;
+
+ dest_ptr1 =
+ base_ptr + new_font->hdr.row_length * row;
+ dest_ptr2 =
+ base_ptr +
+ new_font->hdr.row_length * (row + 1);
+ dest_ptr3 =
+ base_ptr +
+ new_font->hdr.row_length * (row + 2);
+
+ if (current_byte > 0) {
+
+ /* Get last two columns from previous byte */
+ src_ptr = src_font->font_p +
+ src_font->hdr.row_length * row +
+ src_font->fce[i].index +
+ (current_byte - 1);
+
+ c_rep = *src_ptr;
+ *dest_ptr1 |=
+ ((c_rep << 6) | (c_rep << 7));
+ *dest_ptr2 |=
+ ((c_rep << 6) | (c_rep << 7));
+ *dest_ptr3 |=
+ ((c_rep << 6) | (c_rep << 7));
+ }
+
+ if (current_byte < src_font->fce[i].byte_width) {
+
+ src_ptr = src_font->font_p +
+ src_font->hdr.row_length * row +
+ src_font->fce[i].index +
+ current_byte;
+
+ c_rep = *src_ptr;
+ *dest_ptr1 |=
+ c_rep | (c_rep >> 1) | (c_rep >>
+ 2);
+ *dest_ptr2 |=
+ c_rep | (c_rep >> 1) | (c_rep >>
+ 2);
+ *dest_ptr3 |=
+ c_rep | (c_rep >> 1) | (c_rep >>
+ 2);
+ }
+ }
+ }
+
+ /* "Hollow out" character to prevent overdraw */
+ for (row = 0; row < s_height; row++) {
+
+ for (current_byte = 0;
+ current_byte < new_font->fce[i].byte_width;
+ current_byte++) {
+
+ dest_ptr2 = new_font->font_p +
+ new_font->hdr.row_length * (row + 1) +
+ new_font->fce[i].index + current_byte;
+
+ if (current_byte > 0) {
+
+ /* Get last two columns from previous byte */
+ src_ptr = src_font->font_p +
+ src_font->hdr.row_length * row +
+ src_font->fce[i].index +
+ (current_byte - 1);
+
+ *dest_ptr2 &=
+ ((*src_ptr << 7) ^ 0xFFU);
+ }
+
+ if (current_byte < src_font->fce[i].byte_width) {
+
+ src_ptr = src_font->font_p +
+ src_font->hdr.row_length * row +
+ src_font->fce[i].index +
+ current_byte;
+
+ *dest_ptr2 &=
+ ((*src_ptr >> 1) ^ 0xFFU);
+ }
+ }
+ }
+ }
+
+ return new_font;
+
+}
+
+static int GetByteLen(int num_bits)
+{
+
+ int byte_len;
+
+ byte_len = num_bits / 8;
+
+ if (num_bits % 8) {
+ byte_len++;
+ }
+
+ return byte_len;
+}
+
+int
+FONT_GetStringWidth(int font_id,
+ const char *test_str, size_t test_str_ct, int flags)
+/*--------------------------------------------------------------------------*\
+ * Returns the horizontal length in pixels of the graphical representation
+ * of at most 'test_str_ct' characters of the string 'test_str', taking
+ * into account any formatting options specified by 'flags'.
+ * If 'test_str_ct' is 0, all characters of 'test_str' are counted.
+\*--------------------------------------------------------------------------*/
+{
+
+ R_FONT *font;
+ size_t ct;
+ int width = 0;
+ int ch;
+ uchar *txt_p;
+
+ if (!FontModule.init) {
+ return R_FAILURE;
+ }
+
+ if ((font_id < 0) ||
+ (font_id >= FontModule.n_fonts) ||
+ (FontModule.fonts[font_id] == NULL)) {
+
+ FontModule.err_str = "Invalid font id.";
+
+ return R_FAILURE;
+ }
+
+ font = FontModule.fonts[font_id];
+ assert(font != NULL);
+
+ txt_p = (uchar *) test_str;
+
+ for (ct = test_str_ct;
+ *txt_p && (!test_str_ct || ct > 0); txt_p++, ct--) {
+
+ ch = *txt_p & 0xFFU;
+
+ /* Translate character */
+ ch = CharMap[ch];
+ assert(ch < R_FONT_CHARCOUNT);
+
+ width += font->normal->fce[ch].tracking;
+ }
+
+ if ((flags & FONT_BOLD) || (flags & FONT_OUTLINE)) {
+ width += 1;
+ }
+
+ return width;
+}
+
+int
+FONT_Draw(int font_id,
+ R_SURFACE * ds,
+ const char *draw_str,
+ size_t draw_str_ct,
+ int text_x, int text_y, int color, int effect_color, int flags)
+{
+
+ R_FONT *font;
+
+ if (!FontModule.init) {
+ FontModule.err_str = "Font Module not initialized.";
+
+ return R_FAILURE;
+ }
+
+ if ((font_id < 0) ||
+ (font_id >= FontModule.n_fonts) ||
+ (FontModule.fonts[font_id] == NULL)) {
+
+ FontModule.err_str = "Invalid font id.";
+
+ return R_FAILURE;
+ }
+
+ font = FontModule.fonts[font_id];
+
+ if (flags & FONT_OUTLINE) {
+
+ FONT_Out(font->outline,
+ ds,
+ draw_str,
+ draw_str_ct, text_x - 1, text_y - 1, effect_color);
+
+ FONT_Out(font->normal,
+ ds, draw_str, draw_str_ct, text_x, text_y, color);
+ } else if (flags & FONT_SHADOW) {
+
+ FONT_Out(font->normal,
+ ds,
+ draw_str,
+ draw_str_ct, text_x - 1, text_y + 1, effect_color);
+
+ FONT_Out(font->normal,
+ ds, draw_str, draw_str_ct, text_x, text_y, color);
+ } else { /* FONT_NORMAL */
+
+ FONT_Out(font->normal,
+ ds, draw_str, draw_str_ct, text_x, text_y, color);
+ }
+
+ return R_SUCCESS;
+}
+
+int
+FONT_Out(R_FONT_STYLE * draw_font,
+ R_SURFACE * ds,
+ const char *draw_str,
+ size_t draw_str_ct, int text_x, int text_y, int color)
+{
+
+ const uchar *draw_str_p;
+
+ uchar *c_data_ptr;
+ int c_code;
+
+ int char_row;
+
+ uchar *output_ptr;
+ uchar *output_ptr_min;
+ uchar *output_ptr_max;
+
+ int row;
+ int row_limit;
+
+ int c_byte_len;
+ int c_byte;
+ int c_bit;
+
+ int ct;
+
+ if ((text_x > ds->buf_w) || (text_y > ds->buf_h)) {
+ /* Output string can't be visible */
+ return R_SUCCESS;
+ }
+
+ draw_str_p = (uchar *) draw_str;
+ ct = draw_str_ct;
+
+ /* Draw string one character at a time, maximum of 'draw_str'_ct
+ * characters, or no limit if 'draw_str_ct' is 0 */
+ for (; *draw_str_p && (!draw_str_ct || ct); draw_str_p++, ct--) {
+
+ c_code = *draw_str_p & 0xFFU;
+
+ /* Translate character */
+ c_code = CharMap[c_code];
+ assert(c_code < R_FONT_CHARCOUNT);
+
+ /* Check if character is defined */
+ if ((draw_font->fce[c_code].index == 0) &&
+ (c_code != R_FONT_FIRSTCHAR)) {
+
+# if R_FONT_SHOWUNDEFINED
+
+ if (c_code == R_FONT_CH_SPACE) {
+ text_x += draw_font->fce[c_code].tracking;
+ continue;
+ }
+ c_code = R_FONT_CH_QMARK;
+
+# else
+
+ /* Character code is not defined, but advance tracking
+ * ( Not defined if offset is 0, except for 33 ('!') which
+ * is defined ) */
+ text_x += draw_font->fce[c_code].tracking;
+ continue;
+
+#endif
+ }
+
+ /* Get length of character in bytes */
+ c_byte_len = ((draw_font->fce[c_code].width - 1) / 8) + 1;
+
+ row_limit = (ds->buf_h < (text_y + draw_font->hdr.c_height))
+ ? ds->buf_h : text_y + draw_font->hdr.c_height;
+
+ char_row = 0;
+
+ for (row = text_y; row < row_limit; row++, char_row++) {
+
+ /* Clip negative rows */
+ if (row < 0) {
+ continue;
+ }
+
+ output_ptr = ds->buf + (ds->buf_pitch * row) + text_x;
+
+ output_ptr_min = ds->buf + (ds->buf_pitch * row) +
+ (text_x > 0 ? text_x : 0);
+
+ output_ptr_max = output_ptr + (ds->buf_pitch - text_x);
+
+ /* If character starts off the screen, jump to next character */
+ if (output_ptr < output_ptr_min) {
+ break;
+ }
+
+ c_data_ptr = draw_font->font_p +
+ char_row * draw_font->hdr.row_length +
+ draw_font->fce[c_code].index;
+
+ for (c_byte = 0; c_byte < c_byte_len;
+ c_byte++, c_data_ptr++) {
+
+ /* Check each bit, draw pixel if bit is set */
+ for (c_bit = 7;
+ c_bit >= 0
+ && (output_ptr < output_ptr_max);
+ c_bit--) {
+
+ if ((*c_data_ptr >> c_bit) & 0x01) {
+ *output_ptr = (uchar) color;
+ }
+ output_ptr++;
+
+ } /* end per-bit processing */
+
+ } /* end per-byte processing */
+
+ } /* end per-row processing */
+
+ /* Advance tracking position */
+ text_x += draw_font->fce[c_code].tracking;
+
+ } /* end per-character processing */
+
+ return R_SUCCESS;
+}
+
+} // End of namespace Saga