scummvm/engines/sci/gfx/font.cpp
2009-03-16 00:07:12 +00:00

266 lines
6.8 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "sci/gfx/gfx_system.h"
#include "sci/gfx/gfx_resource.h"
#include "sci/gfx/gfx_tools.h"
#include "sci/gfx/font.h"
namespace Sci {
int font_counter = 0;
void gfxr_free_font(gfx_bitmap_font_t *font) {
free(font->widths);
free(font->data);
--font_counter;
free(font);
}
text_fragment_t *gfxr_font_calculate_size(gfx_bitmap_font_t *font, int max_width,
const char *text, int *width, int *height,
int *lines, int *line_height_p, int *last_offset_p, int flags) {
int est_char_width = font->widths[(font->chars_nr > 'M')? 'M' : font->chars_nr - 1];
// 'M' is typically among the widest chars
int fragments_nr;
text_fragment_t *fragments;
int lineheight = font->line_height;
int maxheight = lineheight;
int last_breakpoint = 0;
int last_break_width = 0;
int max_allowed_width = max_width;
int maxwidth = 0, localmaxwidth = 0;
int current_fragment = 1;
const char *breakpoint_ptr = NULL;
unsigned char foo;
if (line_height_p)
*line_height_p = lineheight;
if (max_width > 1) fragments_nr = 3 + (strlen(text) * est_char_width) * 3 / (max_width << 1);
else fragments_nr = 1;
fragments = (text_fragment_t *)sci_calloc(sizeof(text_fragment_t), fragments_nr);
fragments[0].offset = text;
while ((foo = *text++)) {
if (foo >= font->chars_nr) {
GFXWARN("Invalid char 0x%02x (max. 0x%02x) encountered in text string '%s', font %04x\n",
foo, font->chars_nr, text, font->ID);
if (font->chars_nr > ' ')
foo = ' ';
else {
free(fragments);
return NULL;
}
}
if (((foo == '\n') || (foo == 0x0d)) && !(flags & GFXR_FONT_FLAG_NO_NEWLINES)) {
fragments[current_fragment-1].length = text - 1 - fragments[current_fragment-1].offset;
if (*text)
maxheight += lineheight;
if (foo == 0x0d && *text == '\n')
text++; // Interpret DOS-style CR LF as single NL
fragments[current_fragment++].offset = text;
if (localmaxwidth > maxwidth)
maxwidth = localmaxwidth;
if (current_fragment == fragments_nr)
fragments = (text_fragment_t*)sci_realloc(fragments, sizeof(text_fragment_t) * (fragments_nr <<= 1));
localmaxwidth = 0;
} else { // foo != '\n'
localmaxwidth += font->widths[foo];
if (localmaxwidth > max_allowed_width) {
int blank_break = 1; // break is at a blank char, i.e. not within a word
maxheight += lineheight;
if (last_breakpoint == 0) { // Text block too long and without whitespace?
last_breakpoint = localmaxwidth - font->widths[foo];
last_break_width = 0;
--text;
blank_break = 0; // non-blank break
} else {
text = breakpoint_ptr + 1;
assert(breakpoint_ptr);
}
if (last_breakpoint == 0) {
GFXWARN("Warning: maxsize %d too small for '%s'\n", max_allowed_width, text);
}
if (last_breakpoint > maxwidth)
maxwidth = last_breakpoint;
fragments[current_fragment-1].length = text - blank_break - fragments[current_fragment-1].offset;
fragments[current_fragment++].offset = text;
if (current_fragment == fragments_nr)
fragments = (text_fragment_t*)sci_realloc(fragments, sizeof(text_fragment_t *) * (fragments_nr <<= 1));
localmaxwidth = localmaxwidth - last_breakpoint;
if (!(flags & GFXR_FONT_FLAG_COUNT_WHITESPACE))
localmaxwidth -= last_break_width;
last_breakpoint = localmaxwidth = 0;
} else if (*text == ' ') {
last_breakpoint = localmaxwidth;
last_break_width = font->widths[foo];
breakpoint_ptr = text;
}
}
}
if (localmaxwidth > maxwidth)
*width = localmaxwidth;
else
*width = maxwidth;
if (last_offset_p)
*last_offset_p = localmaxwidth;
if (height)
*height = maxheight;
if (lines)
*lines = current_fragment;
fragments[current_fragment-1].length = text - fragments[current_fragment-1].offset - 1;
return fragments;
}
static void render_char(byte *dest, byte *src, int width, int line_width, int lines, int bytes_per_src_line, int fg0, int fg1, int bg) {
int x, y;
for (y = 0; y < lines; y++) {
int dat = 0;
byte *vdest = dest;
byte *vsrc = src;
int xc = 0;
for (x = 0; x < width; x++) {
if (!xc) {
dat = *vsrc++;
xc = 8;
}
xc--;
if (dat & 0x80)
*vdest++ = ((xc ^ y) & 1) ? fg0 : fg1; /* dither */
else
*vdest++ = bg;
dat <<= 1;
}
src += bytes_per_src_line;
dest += line_width;
}
}
gfx_pixmap_t *gfxr_draw_font(gfx_bitmap_font_t *font, const char *stext, int characters, PaletteEntry *fg0, PaletteEntry *fg1, PaletteEntry *bg) {
unsigned char *text = (unsigned char *)stext;
int height = font->height;
int width = 0;
gfx_pixmap_t *pxm;
int fore_0, fore_1, back;
int i;
int hack = 0;
PaletteEntry dummy(0,0,0); // black
byte *offset;
for (i = 0; i < characters; i++) {
int ch = (int) text[i];
if (ch >= font->chars_nr) {
GFXERROR("Invalid character 0x%02x encountered!\n", text[i]);
return NULL;
}
width += font->widths[ch];
}
pxm = gfx_pixmap_alloc_index_data(gfx_new_pixmap(width, height, GFX_RESID_NONE, 0, 0));
int colors_nr = !!fg0 + !!fg1 + !!bg;
if (colors_nr == 0) {
GFXWARN("Pixmap would have zero colors, resetting!\n");
colors_nr = 3;
hack = 1;
fg0 = fg1 = bg = &dummy;
}
pxm->palette = new Palette(colors_nr);
pxm->palette->name = "font";
i = 0;
if (fg0 || hack) {
pxm->palette->setColor(i, fg0->r, fg0->g, fg0->b);
fore_0 = i++;
} else
fore_0 = pxm->color_key;
if (fg1 || hack) {
pxm->palette->setColor(i, fg1->r, fg1->g, fg1->b);
fore_1 = i++;
} else
fore_1 = pxm->color_key;
if (bg || hack) {
pxm->palette->setColor(i, bg->r, bg->g, bg->b);
back = i++;
} else
back = pxm->color_key;
offset = pxm->index_data;
memset(pxm->index_data, back, pxm->index_width * pxm->index_height);
for (i = 0; i < characters; i++) {
unsigned char ch = text[i];
width = font->widths[ch];
render_char(offset, font->data + (ch * font->char_size), width,
pxm->index_width, pxm->index_height, font->row_size, fore_0, fore_1, back);
offset += width;
}
return pxm;
}
} // End of namespace Sci