Initial commit
This commit is contained in:
945
engines/saga2/gtext.cpp
Normal file
945
engines/saga2/gtext.cpp
Normal file
@@ -0,0 +1,945 @@
|
||||
/* 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 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/>.
|
||||
*
|
||||
*
|
||||
* Based on the original sources
|
||||
* Faery Tale II -- The Halls of the Dead
|
||||
* (c) 1993-1996 The Wyrmkeep Entertainment Co.
|
||||
*/
|
||||
|
||||
#include "saga2/saga2.h"
|
||||
#include "saga2/gdraw.h"
|
||||
|
||||
namespace Saga2 {
|
||||
|
||||
#define textStyleBar (kTextStyleUnderBar|kTextStyleHiLiteBar)
|
||||
|
||||
/* ============================================================================ *
|
||||
Text Blitting Routines
|
||||
* ============================================================================ */
|
||||
|
||||
/* Notes:
|
||||
|
||||
These functions attempt to render planar fonts onto a chunky bitmap.
|
||||
|
||||
All non-pointer variables are 16 bits or less. (I don't know how
|
||||
big pointers need to be).
|
||||
*/
|
||||
|
||||
/* DrawChar: This function renders a single bitmapped character
|
||||
into an offscreen buffer.
|
||||
|
||||
'drawchar' is the ascii code of the character to be drawn.
|
||||
|
||||
'xpos' is the position in the buffer to draw the outline.
|
||||
|
||||
'baseline' is the address of the first scanline in the buffer.
|
||||
(it can actually be any scanline in the buffer if we want to
|
||||
draw the character at a different y position).
|
||||
Note that this has nothing to do with the Amiga "BASELINE" field,
|
||||
this routine always renders relative to the top of the font.
|
||||
|
||||
'color' is the pixel value to render into the buffer.
|
||||
|
||||
Note that this code does unaligned int16 writes to byte addresses,
|
||||
and will not run on an 68000 or 68010 processor.
|
||||
*/
|
||||
|
||||
void DrawChar(gFont *font, int drawchar, int xpos, uint8 *baseline, uint8 color,
|
||||
uint16 destwidth) {
|
||||
short w,
|
||||
font_mod; // width of font in bytes
|
||||
|
||||
uint8 *src, // start of char data
|
||||
*dst; // start of dest row
|
||||
|
||||
/*
|
||||
This code assumes that the font characters are all byte-aligned,
|
||||
and that there is no extra junk after the character in the bits
|
||||
that pad to the next byte. Also, the code currently makes no
|
||||
provision for "LoChar" or "HiChar" (i.e. there are blank table
|
||||
entries for characters 0-1f). This can be changed if desired.
|
||||
*/
|
||||
|
||||
font_mod = font->rowMod;
|
||||
uint16 offset = font->charXOffset[drawchar];
|
||||
src = &font->fontdata[offset];
|
||||
dst = baseline + xpos;
|
||||
|
||||
for (w = font->charWidth[drawchar]; w > 0; w -= 8) {
|
||||
uint8 *src_row,
|
||||
*dst_row;
|
||||
short h;
|
||||
|
||||
src_row = src;
|
||||
dst_row = dst;
|
||||
|
||||
for (h = font->height; h > 0; h--) {
|
||||
uint8 *b;
|
||||
uint8 s;
|
||||
|
||||
b = dst_row;
|
||||
|
||||
for (s = *src_row; s != 0; s <<= 1) {
|
||||
if ((s & 0x80) != 0)
|
||||
*b = color;
|
||||
++b;
|
||||
}
|
||||
src_row += font_mod;
|
||||
dst_row += destwidth;
|
||||
}
|
||||
|
||||
++src;
|
||||
dst += 8;
|
||||
}
|
||||
}
|
||||
/* DrawChar3x3Outline:
|
||||
|
||||
This function renders a single bitmapped character into an offscreen
|
||||
buffer. The character will be "ballooned", i.e. expanded, by 1 pixel
|
||||
in each direction. It does not render the center part of the outlined
|
||||
character in a different color -- that must be done as a separate
|
||||
step.
|
||||
|
||||
'drawchar' is the ascii code of the character to be drawn.
|
||||
|
||||
'xpos' is the position in the buffer to draw the outline. Note that
|
||||
the first pixel of the outline may be drawn at xpos-1, since the
|
||||
outline expands in both directions.
|
||||
|
||||
'baseline' is the address of the first scanline in the buffer.
|
||||
(it can actually be any scanline in the buffer if we want to
|
||||
draw the character at a different y position).
|
||||
Note that this has nothing to do with the Amiga "BASELINE" field,
|
||||
this routine always renders relative to the top of the font.
|
||||
|
||||
'color' is the pixel value to render into the buffer.
|
||||
|
||||
This code assumes that the font characters are all byte-aligned,
|
||||
and that there is no extra junk after the character in the bits
|
||||
that pad to the next byte. Also, the code currently makes no
|
||||
provision for "LoChar" or "HiChar" (i.e. there are blank table
|
||||
entries for characters 0-1f). This can be changed if desired.
|
||||
|
||||
Note that this code does unaligned int16 writes to byte addresses,
|
||||
and will not run on an 68000 or 68010 processor. (Even with the
|
||||
speed penalty, this is still pretty fast).
|
||||
*/
|
||||
|
||||
#define SQUARE_OUTLINES 1
|
||||
|
||||
void DrawChar3x3Outline(gFont *font, int drawchar, int xpos, uint8 *baseline,
|
||||
uint8 color, uint16 destwidth) {
|
||||
uint8 *d;
|
||||
short h, // font height counter
|
||||
rowmod;
|
||||
|
||||
short charwidth, w; // width of character in pixels
|
||||
|
||||
uint8 *chardata; // pointer to start of char data
|
||||
|
||||
uint8 *src, *dst;
|
||||
unsigned short s;
|
||||
unsigned short txt1, txt2, txt3;
|
||||
|
||||
// point to the first byte of the first scanline of the source char
|
||||
uint16 offset = font->charXOffset[drawchar];
|
||||
chardata = &font->fontdata[offset];
|
||||
|
||||
// get the width of the character in pixels
|
||||
charwidth = font->charWidth[drawchar];
|
||||
|
||||
// point to the first byte of where we want to place the character
|
||||
baseline += xpos - 1;
|
||||
|
||||
// this loop goes once for each 8 pixels wide that the character is
|
||||
rowmod = font->rowMod;
|
||||
|
||||
for (w = (charwidth + 7) >> 3; w > 0; w--) {
|
||||
src = chardata;
|
||||
dst = baseline;
|
||||
|
||||
txt1 = txt2 = 0;
|
||||
|
||||
for (h = font->height + 2; h; h--) {
|
||||
d = dst;
|
||||
|
||||
txt3 = txt2;
|
||||
txt2 = txt1;
|
||||
txt1 = h > 2 ? *src : 0;
|
||||
|
||||
s = txt1 | txt2 | txt3;
|
||||
|
||||
s = s | (s << 1) | (s << 2);
|
||||
|
||||
while (s) {
|
||||
if (s & 0x200)
|
||||
*d = color;
|
||||
++d;
|
||||
s <<= 1;
|
||||
}
|
||||
|
||||
src += rowmod;
|
||||
dst += destwidth;
|
||||
}
|
||||
|
||||
chardata++;
|
||||
baseline += 8;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void DrawChar5x5Outline(gFont *font, int drawchar, int xpos, uint8 *baseline,
|
||||
uint8 color, uint16 destwidth) {
|
||||
uint8 *d;
|
||||
short h, /* font height counter */
|
||||
rowmod;
|
||||
|
||||
short charwidth, w; /* width of character in pixels */
|
||||
|
||||
uint8 *chardata; /* pointer to start of char data */
|
||||
|
||||
uint8 *src, *dst;
|
||||
unsigned short s0, s1;
|
||||
unsigned short txt[5];
|
||||
|
||||
// point to the first byte of the first scanline of the source char
|
||||
uint16 offset = font->charXOffset[drawchar];
|
||||
chardata = &font->fontdata[offset];
|
||||
|
||||
// get the width of the character in pixels
|
||||
charwidth = font->charWidth[drawchar];
|
||||
|
||||
// point to the first byte of where we want to place the character
|
||||
baseline += xpos - 2;
|
||||
|
||||
// this loop goes once for each 8 pixels wide that the character is
|
||||
rowmod = font->rowMod;
|
||||
|
||||
for (w = (charwidth + 7) >> 3; w > 0; w--) {
|
||||
src = chardata;
|
||||
dst = baseline;
|
||||
|
||||
txt[0] = txt[1] = txt[2] = txt[3 ] = txt[ 4 ] = 0;
|
||||
|
||||
for (h = font->height + 4; h; h--) {
|
||||
d = dst;
|
||||
|
||||
txt[4] = txt[3];
|
||||
txt[3] = txt[2];
|
||||
txt[2] = txt[1];
|
||||
txt[1] = txt[0];
|
||||
txt[0] = h > 4 ? *src : 0;
|
||||
|
||||
s0 = txt[1] | txt[2] | txt[3];
|
||||
s1 = s0 | txt[0] | txt[4];
|
||||
|
||||
s0 = s0 | (s0 << 1) | (s0 << 2) | (s0 << 3) | (s0 << 4);
|
||||
s0 |= (s1 << 1) | (s1 << 2) | (s1 << 3);
|
||||
|
||||
while (s0) {
|
||||
if (s0 & 0x800)
|
||||
*d = color;
|
||||
++d;
|
||||
s0 <<= 1;
|
||||
}
|
||||
|
||||
src += rowmod;
|
||||
dst += destwidth;
|
||||
}
|
||||
|
||||
chardata++;
|
||||
baseline += 8;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* A private routine to render a string of characters into a temp
|
||||
buffer.
|
||||
*/
|
||||
|
||||
void gPort::drawStringChars(
|
||||
const char *str, // string to draw
|
||||
int16 len, // length of string
|
||||
gPixelMap &dest,
|
||||
int xpos, // x position to draw it
|
||||
int ypos) { // y position to draw it
|
||||
const char *s; // pointer to string
|
||||
uint8 drawchar; // char to draw
|
||||
int16 x; // current x position
|
||||
uint8 *buffer, // buffer to render to
|
||||
*uBuffer; // underline buffer
|
||||
uint16 drowMod = dest._size.x; // row modulus of dest
|
||||
int16 i; // loop index
|
||||
uint8 underbar = (_textStyles & textStyleBar) != 0;
|
||||
bool underscore;
|
||||
int16 underPos;
|
||||
|
||||
// the address to start rendering pixels to.
|
||||
|
||||
underPos = _font->baseLine + 2;
|
||||
if (underPos > _font->height) underPos = _font->height;
|
||||
buffer = dest._data + (ypos * drowMod);
|
||||
uBuffer = buffer + (underPos * drowMod);
|
||||
|
||||
// draw drop-shadow, if any
|
||||
|
||||
if (_textStyles & kTextStyleShadow) {
|
||||
x = xpos - 1;
|
||||
s = str;
|
||||
|
||||
if (_textStyles & kTextStyleOutline) { // if outlining
|
||||
for (i = 0; i < len; i++) {
|
||||
drawchar = *s++; // draw thick drop shadow
|
||||
x += _font->charKern[drawchar];
|
||||
DrawChar3x3Outline(_font, drawchar, x, buffer, _shPen, drowMod);
|
||||
x += _font->charSpace[drawchar] + _textSpacing;
|
||||
}
|
||||
} else if (_textStyles & kTextStyleThickOutline) { // if outlining
|
||||
for (i = 0; i < len; i++) {
|
||||
drawchar = *s++; // draw thick drop shadow
|
||||
x += _font->charKern[drawchar];
|
||||
DrawChar5x5Outline(_font, drawchar, x, buffer, _shPen, drowMod);
|
||||
x += _font->charSpace[drawchar] + _textSpacing;
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < len; i++) {
|
||||
drawchar = *s++; // draw thick drop shadow
|
||||
x += _font->charKern[drawchar];
|
||||
DrawChar(_font, drawchar, x, buffer + drowMod,
|
||||
_shPen, drowMod);
|
||||
x += _font->charSpace[drawchar] + _textSpacing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// draw outline, if any
|
||||
|
||||
if (_textStyles & kTextStyleOutline) { // if outlining
|
||||
x = xpos;
|
||||
s = str;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
drawchar = *s++; // draw thick text
|
||||
x += _font->charKern[drawchar];
|
||||
DrawChar3x3Outline(_font, drawchar, x, buffer - drowMod,
|
||||
_olPen, drowMod);
|
||||
x += _font->charSpace[drawchar] + _textSpacing;
|
||||
}
|
||||
} else if (_textStyles & kTextStyleThickOutline) { // if thick outlining
|
||||
x = xpos;
|
||||
s = str;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
drawchar = *s++; // draw extra thick text
|
||||
x += _font->charKern[drawchar];
|
||||
DrawChar5x5Outline(_font, drawchar, x, buffer - drowMod * 2,
|
||||
_olPen, drowMod);
|
||||
x += _font->charSpace[drawchar] + _textSpacing;
|
||||
}
|
||||
}
|
||||
|
||||
// draw inner part
|
||||
|
||||
x = xpos;
|
||||
s = str;
|
||||
underscore = _textStyles & kTextStyleUnderScore ? true : false;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
int16 last_x = x;
|
||||
uint8 color = _fgPen;
|
||||
|
||||
drawchar = *s++; // draw thick drop shadow
|
||||
if (drawchar == '_' && underbar) {
|
||||
len--;
|
||||
drawchar = *s++;
|
||||
if (_textStyles & kTextStyleUnderBar)
|
||||
underscore = true;
|
||||
if (_textStyles & kTextStyleHiLiteBar)
|
||||
color = _bgPen;
|
||||
}
|
||||
x += _font->charKern[drawchar];
|
||||
DrawChar(_font, drawchar, x, buffer, color, drowMod);
|
||||
x += _font->charSpace[drawchar] + _textSpacing;
|
||||
|
||||
if (underscore) { // draw underscore
|
||||
uint8 *put = uBuffer + last_x;
|
||||
int16 width = x - last_x;
|
||||
|
||||
while (width-- > 0) {
|
||||
*put++ = color;
|
||||
}
|
||||
|
||||
if (!(_textStyles & kTextStyleUnderScore))
|
||||
underscore = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draws a string clipped to the current clipping rectangle.
|
||||
// Note that if several clipping rectangles were to be used,
|
||||
// we would probably do this differently...
|
||||
|
||||
int16 gPort::drawClippedString(
|
||||
const char *s, // string to draw
|
||||
int16 len, // length of string
|
||||
int xpos, // x position to draw it
|
||||
int ypos) { // y position to draw it
|
||||
int16 clipWidth = 0, // width of clipped string
|
||||
clipLen; // chars to draw
|
||||
gPixelMap tempMap; // temp buffer for text
|
||||
uint8 underbar = (_textStyles & textStyleBar) != 0;
|
||||
int16 xoff = 0, // offset for outlines
|
||||
yoff = 0; // offset for outlines
|
||||
int16 penMove = 0; // keep track of pen movement
|
||||
|
||||
// First, we want to avoid rendering any characters to the
|
||||
// left of the clipping rectangle. Scan the string until
|
||||
// we find a character that is not completely to the left
|
||||
// of the clip.
|
||||
|
||||
while (len > 0) {
|
||||
int16 drawchar = *s,
|
||||
charwidth;
|
||||
|
||||
if (drawchar == '_' && underbar) {
|
||||
drawchar = s[1];
|
||||
charwidth = _font->charKern[drawchar]
|
||||
+ _font->charSpace[drawchar] + _textSpacing;
|
||||
|
||||
if (xpos + charwidth >= _clip.x)
|
||||
break;
|
||||
s++;
|
||||
} else {
|
||||
charwidth = _font->charKern[drawchar]
|
||||
+ _font->charSpace[drawchar] + _textSpacing;
|
||||
if (xpos + charwidth >= _clip.x)
|
||||
break;
|
||||
}
|
||||
|
||||
s++;
|
||||
len--;
|
||||
xpos += charwidth;
|
||||
penMove += charwidth;
|
||||
}
|
||||
|
||||
// Now, we also want to only draw that portion of the string
|
||||
// that actually appears in the clip, so scan the rest of the
|
||||
// string until we find a character who's left edge is past
|
||||
// the right edge of the clip.
|
||||
|
||||
for (clipLen = 0; clipLen < len; clipLen++) {
|
||||
int16 drawchar = s[clipLen];
|
||||
|
||||
if (drawchar == '_' && underbar)
|
||||
continue;
|
||||
|
||||
clipWidth += _font->charKern[drawchar]
|
||||
+ _font->charSpace[drawchar] + _textSpacing;
|
||||
|
||||
if (xpos > _clip.x + _clip.width)
|
||||
break;
|
||||
}
|
||||
|
||||
// Handle special case of negative kern value of 1st character
|
||||
|
||||
if (_font->charKern[(byte)s[0]] < 0) {
|
||||
int16 kern = - _font->charKern[(byte)s[0]];
|
||||
|
||||
clipWidth += kern; // increase size of map to render
|
||||
xoff += kern; // offset text into map right
|
||||
xpos -= kern; // offset map into port left
|
||||
}
|
||||
|
||||
// Set up a temporary bitmap to hold the string.
|
||||
|
||||
tempMap._size.x = clipWidth;
|
||||
tempMap._size.y = _font->height;
|
||||
|
||||
// Adjust the size and positioning of the temp map due
|
||||
// to text style effects.
|
||||
|
||||
if (_textStyles & kTextStyleOutline) {
|
||||
xoff = yoff = 1;
|
||||
xpos--;
|
||||
ypos--;
|
||||
tempMap._size.x += 2;
|
||||
tempMap._size.y += 2;
|
||||
} else if (_textStyles & kTextStyleThickOutline) {
|
||||
xoff = yoff = 2;
|
||||
xpos -= 2;
|
||||
ypos -= 2;
|
||||
tempMap._size.x += 4;
|
||||
tempMap._size.y += 4;
|
||||
}
|
||||
|
||||
if (_textStyles & (kTextStyleShadow | kTextStyleUnderScore | kTextStyleUnderBar)) {
|
||||
tempMap._size.x += 1;
|
||||
tempMap._size.y += 1;
|
||||
}
|
||||
|
||||
if (_textStyles & kTextStyleItalics) {
|
||||
int n = (_font->height - _font->baseLine - 1) / 2;
|
||||
|
||||
if (n > 0) xpos += n;
|
||||
tempMap._size.x += tempMap._size.y / 2;
|
||||
}
|
||||
|
||||
// Allocate a temporary bitmap
|
||||
|
||||
if (tempMap.bytes() == 0)
|
||||
return 0;
|
||||
tempMap._data = (uint8 *)malloc(tempMap.bytes());
|
||||
if (tempMap._data != nullptr) {
|
||||
// Fill the buffer with background pen if we're
|
||||
// not doing a transparent blit.
|
||||
|
||||
memset(tempMap._data,
|
||||
(_drawMode == kDrawModeReplace) ? _bgPen : 0,
|
||||
tempMap.bytes());
|
||||
|
||||
// Draw the characters into the buffer
|
||||
|
||||
drawStringChars(s, clipLen, tempMap, xoff, yoff);
|
||||
|
||||
// apply slant if italics
|
||||
|
||||
if (_textStyles & kTextStyleItalics) {
|
||||
int n = (_font->height - _font->baseLine - 1) / 2;
|
||||
int shift = (n > 0 ? n : 0);
|
||||
int flag = (_font->height - _font->baseLine - 1) & 1;
|
||||
|
||||
shift = -shift;
|
||||
|
||||
for (int k = _font->height - 1; k >= 0; k--) {
|
||||
if (shift < 0) {
|
||||
uint8 *dest = tempMap._data + k * tempMap._size.x,
|
||||
*src = dest - shift;
|
||||
int j;
|
||||
|
||||
for (j = 0; j < tempMap._size.x + shift; j++) {
|
||||
*dest++ = *src++;
|
||||
}
|
||||
for (; j < tempMap._size.x; j++) {
|
||||
*dest++ = 0;
|
||||
}
|
||||
} else if (shift > 0) {
|
||||
uint8 *dest = tempMap._data + (k + 1) * tempMap._size.x,
|
||||
*src = dest - shift;
|
||||
int j;
|
||||
|
||||
for (j = 0; j < tempMap._size.x - shift; j++) {
|
||||
*--dest = *--src;
|
||||
}
|
||||
for (; j < tempMap._size.x; j++) {
|
||||
*--dest = 0;
|
||||
}
|
||||
}
|
||||
|
||||
flag ^= 1;
|
||||
if (flag)
|
||||
shift++;
|
||||
}
|
||||
}
|
||||
|
||||
// blit the temp buffer onto the screen.
|
||||
|
||||
bltPixels(tempMap, 0, 0,
|
||||
xpos, ypos,
|
||||
tempMap._size.x, tempMap._size.y);
|
||||
|
||||
free(tempMap._data);
|
||||
}
|
||||
|
||||
// Now, we still need to scan the rest of the string
|
||||
// so that we can move the pen position by the right amount.
|
||||
|
||||
// penMove += clipWidth;
|
||||
for (clipLen = 0; clipLen < len; clipLen++) {
|
||||
int16 drawchar = s[clipLen];
|
||||
|
||||
if (drawchar == '_' && underbar)
|
||||
continue;
|
||||
|
||||
penMove += _font->charKern[drawchar]
|
||||
+ _font->charSpace[drawchar] + _textSpacing;
|
||||
}
|
||||
|
||||
return penMove;
|
||||
}
|
||||
|
||||
/********* gtext.cpp/gPort::drawText *********************************
|
||||
*
|
||||
* NAME
|
||||
* gPort::drawText -- draw a text string into a gPort
|
||||
*
|
||||
* SYNOPSIS
|
||||
* port.drawText( str, length );
|
||||
*
|
||||
* void gPort::drawText( char *, int16 = -1 );
|
||||
*
|
||||
* FUNCTION
|
||||
* This function draws a string of text at the current pen
|
||||
* position in the current pen color. The pen position is
|
||||
* updated to the end of the text.
|
||||
*
|
||||
* The text will be positioned such that the top-left corner
|
||||
* of the first character will be at the pen position.
|
||||
*
|
||||
* INPUTS
|
||||
* str A string of characters.
|
||||
*
|
||||
* length [Optional parameter] If supplied, it indicates
|
||||
* the length of the string; Otherwise, strlen()
|
||||
* is used.
|
||||
*
|
||||
* RESULT
|
||||
* none
|
||||
*
|
||||
* SEE ALSO
|
||||
* gPort class
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
void gPort::drawText(
|
||||
const char *str, /* string to draw */
|
||||
int16 length) {
|
||||
if (length < 0)
|
||||
length = strlen(str);
|
||||
|
||||
if (length > 0)
|
||||
_penPos.x += drawClippedString(str, length, _penPos.x, _penPos.y);
|
||||
}
|
||||
|
||||
/********* gtext.cpp/gPort::drawTextInBox *********************************
|
||||
*
|
||||
* NAME
|
||||
* gPort::drawTextInBox -- draw text within a box
|
||||
*
|
||||
* SYNOPSIS
|
||||
* port.drawTextInBox( str, len, rect, pos, borderSpace );
|
||||
*
|
||||
* void gPort::drawTextInBox( char *, int16, const Rect16 &,
|
||||
* /c/ int16, Point16 );
|
||||
*
|
||||
* FUNCTION
|
||||
* This function can draw a text string centered or justified
|
||||
* within a rectangular area, and clipped to that area as well.
|
||||
* The text string is drawn in the current pen color, and with
|
||||
* the current draw mode.
|
||||
*
|
||||
* INPUTS
|
||||
* str The text to draw.
|
||||
*
|
||||
* len The length of the string or -1 to indicate a
|
||||
* null-terminated string.
|
||||
*
|
||||
* rect The rectangle to draw the text within.
|
||||
*
|
||||
* pos How to position the text string within the
|
||||
* rectangle. The default (0) is to center the
|
||||
* string both horizontally and vertically; However,
|
||||
* the following flags will modify this:
|
||||
*
|
||||
* /i/ kTextPosLeft -- draw text left-justified.
|
||||
*
|
||||
* /i/ kTextPosRight -- draw text right-justified.
|
||||
*
|
||||
* /i/ kTextPosHigh -- draw text flush with top edge.
|
||||
*
|
||||
* /i/ kTextPosLow -- draw text flush with bottom edge.
|
||||
*
|
||||
* borderSpace A Point16 object, which indicates how much space
|
||||
* (in both x and y) to place between the text and
|
||||
* the border when the text is justified to a
|
||||
* particular edge. Does not apply when text is centered.
|
||||
*
|
||||
* RESULT
|
||||
* none
|
||||
*
|
||||
* SEE ALSO
|
||||
* gPort class
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
void gPort::drawTextInBox(
|
||||
const char *str,
|
||||
int16 length,
|
||||
const Rect16 &r,
|
||||
int16 pos,
|
||||
Point16 borders) {
|
||||
int16 height, width;
|
||||
int16 x, y;
|
||||
Rect16 newClip,
|
||||
saveClip = _clip;
|
||||
|
||||
if (!_font)
|
||||
return;
|
||||
|
||||
height = _font->height;
|
||||
width = TextWidth(_font, str, length, _textStyles);
|
||||
|
||||
if (_textStyles & (kTextStyleUnderScore | kTextStyleUnderBar)) {
|
||||
if (_font->baseLine + 2 >= _font->height)
|
||||
height++;
|
||||
}
|
||||
|
||||
// Calculate x position of text string
|
||||
|
||||
if (pos & kTextPosLeft)
|
||||
x = r.x + borders.x;
|
||||
else if (pos & kTextPosRight)
|
||||
x = r.x + r.width - width - borders.x;
|
||||
else
|
||||
x = r.x + (r.width - width) / 2;
|
||||
|
||||
// Calculate y position of text string
|
||||
|
||||
if (pos & kTextPosHigh)
|
||||
y = r.y + borders.y;
|
||||
else if (pos & kTextPosLow)
|
||||
y = r.y + r.height - height - borders.y;
|
||||
else
|
||||
y = r.y + (r.height - height) / 2;
|
||||
|
||||
// Calculate clipping region
|
||||
|
||||
_clip = intersect(_clip, r);
|
||||
|
||||
// Draw the text
|
||||
|
||||
moveTo(x, y);
|
||||
drawText(str, length);
|
||||
|
||||
// Restore the clipping region
|
||||
|
||||
_clip = saveClip;
|
||||
}
|
||||
|
||||
// Attach to gFont?
|
||||
|
||||
/********* gtext.cpp/TextWidth ***************************************
|
||||
*
|
||||
* NAME
|
||||
* TextWidth -- returns length of text string in pixels
|
||||
*
|
||||
* SYNOPSIS
|
||||
* width = TextWidth( font, str, len, styles );
|
||||
*
|
||||
* int16 TextWidth( gFont *, char *, int16, int16 );
|
||||
*
|
||||
* FUNCTION
|
||||
* This function computes the length of a text string in pixels
|
||||
* for a given font and text style.
|
||||
*
|
||||
* INPUTS
|
||||
* font The text font of the text.
|
||||
*
|
||||
* str The text string to measure.
|
||||
*
|
||||
* len The length of the string, or -1 to use strlen()
|
||||
*
|
||||
* styles The text rendering style (see gPort::setStyle).
|
||||
* 0 = normal.
|
||||
*
|
||||
* RESULT
|
||||
* The width of the text.
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
int16 TextWidth(gFont *font, const char *s, int16 length, int16 styles) {
|
||||
int16 count = 0;
|
||||
|
||||
if (length < 0)
|
||||
length = strlen(s);
|
||||
|
||||
while (length--) {
|
||||
uint8 chr = *s++;
|
||||
|
||||
if (chr == '_' && (styles & textStyleBar))
|
||||
continue;
|
||||
|
||||
count += font->charKern[chr] + font->charSpace[chr];
|
||||
}
|
||||
|
||||
if (styles & kTextStyleItalics) {
|
||||
count += (font->baseLine + 1) / 2 +
|
||||
(font->height - font->baseLine - 1) / 2;
|
||||
}
|
||||
if (styles & kTextStyleOutline)
|
||||
count += 2;
|
||||
else if (styles & kTextStyleThickOutline)
|
||||
count += 4;
|
||||
if (styles & kTextStyleShadow)
|
||||
count += 1;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
// Searches for the insertion point between chars under the cursor
|
||||
|
||||
/********* gtext.cpp/WhichIChar **************************************
|
||||
*
|
||||
* NAME
|
||||
* WhichIChar -- search for insertion point between characters
|
||||
*
|
||||
* SYNOPSIS
|
||||
* charNum = WhichIChar( font, str, pick, maxLen );
|
||||
*
|
||||
* int16 WhichIChar( gFont *, uint8 *, int16, int16 );
|
||||
*
|
||||
* FUNCTION
|
||||
* This function is used by the gTextBox class to select
|
||||
* the position of the insertion point when the text box
|
||||
* is clicked on. It computes the width of each character
|
||||
* in the string (as it would be rendered
|
||||
* on the screen) and determines which two characters the
|
||||
* pick position would fall between, in other words it finds
|
||||
* the nearest insertion point between characters.
|
||||
*
|
||||
* INPUTS
|
||||
* font The font to use in the character-width calculation.
|
||||
*
|
||||
* str The string to search.
|
||||
*
|
||||
* pick The pick position, relative to the start of the string.
|
||||
*
|
||||
* maxLen The length of the string.
|
||||
*
|
||||
* RESULT
|
||||
* The index of the selected character.
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
int16 WhichIChar(gFont *font, uint8 *s, int16 length, int16 maxLen) {
|
||||
int16 count = 0;
|
||||
|
||||
if (maxLen == -1)
|
||||
maxLen = strlen((char *)s);
|
||||
|
||||
for (count = 0; count < maxLen; count++) {
|
||||
uint8 chr = *s++;
|
||||
int16 width;
|
||||
|
||||
width = font->charKern[chr] + font->charSpace[chr];
|
||||
|
||||
if (length < width / 2)
|
||||
break;
|
||||
length -= width;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/****** gtext.cpp/GTextWrap *****************************************
|
||||
*
|
||||
* NAME
|
||||
* GTextWrap -- given text buffer and pixel width, find wrap point
|
||||
*
|
||||
* SYNOPSIS
|
||||
* offset = GTextWrap( font, buffer, count, width, styles );
|
||||
*
|
||||
* int32 GTextWrap( gFont *, char *, uint16 &, uint16, int16 );
|
||||
*
|
||||
* FUNCTION
|
||||
* This function finds the point in a text buffer that text
|
||||
* wrapping would have to occur given the indicated pixel width.
|
||||
* Linefeeds are considered implied wrap points. Tabs are not
|
||||
* expanded.
|
||||
*
|
||||
* INPUTS
|
||||
* font a gFont that the text will be rendered in.
|
||||
* buffer a NULL-terminated text buffer.
|
||||
* count reference to variable to store count of characters
|
||||
* to render before wrap point.
|
||||
* width width in pixels to wrap point.
|
||||
* styles possible text styles.
|
||||
*
|
||||
* RESULT
|
||||
* offset amount to add to buffer for next call to GTextWrap,
|
||||
* or -1 if the NULL-terminator was reached.
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
int32 GTextWrap(gFont *font, char *mark, uint16 &count, uint16 width, int16 styles) {
|
||||
char *text = mark;
|
||||
char *atext;
|
||||
uint16 pixlen;
|
||||
int aTextIndex = 0;
|
||||
|
||||
if (!strchr(text, '\n')) {
|
||||
count = strlen(text);
|
||||
pixlen = TextWidth(font, text, count, styles);
|
||||
if (pixlen <= width)
|
||||
return -1;
|
||||
}
|
||||
|
||||
atext = text;
|
||||
while (1) {
|
||||
Common::String s = atext;
|
||||
|
||||
int nTextIndex = aTextIndex + s.findFirstOf(' ');
|
||||
int lTextIndex = aTextIndex + s.findFirstOf('\n');
|
||||
|
||||
if (!s.contains(' ') || (s.contains('\n') && lTextIndex < nTextIndex)) {
|
||||
pixlen = TextWidth(font, text, lTextIndex, styles);
|
||||
if (pixlen <= width) {
|
||||
count = lTextIndex;
|
||||
return count + 1;
|
||||
}
|
||||
|
||||
if (!s.contains(' ')) {
|
||||
if (atext == text)
|
||||
break;
|
||||
|
||||
count = aTextIndex - 1;
|
||||
return count + 1;
|
||||
}
|
||||
}
|
||||
|
||||
pixlen = TextWidth(font, text, nTextIndex, styles);
|
||||
if (pixlen > width) {
|
||||
if (atext == text)
|
||||
break;
|
||||
|
||||
count = aTextIndex - 1;
|
||||
return count + 1;
|
||||
}
|
||||
|
||||
atext = text + nTextIndex + 1;
|
||||
aTextIndex = nTextIndex + 1;
|
||||
}
|
||||
|
||||
if (atext == text) {
|
||||
// current text has no spaces AND doesn't fit
|
||||
|
||||
count = strlen(text);
|
||||
while (--count) {
|
||||
pixlen = TextWidth(font, text, count, styles);
|
||||
if (pixlen <= width)
|
||||
return count;
|
||||
}
|
||||
} else {
|
||||
count = aTextIndex - 1;
|
||||
return count + 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
} // end of namespace Saga2
|
||||
Reference in New Issue
Block a user