1
0
Fork 0

(svn r25472) -Cleanup: remove the old methods for drawing text

release/1.4
rubidium 2013-06-25 20:44:54 +00:00
parent 8bbbb1b37a
commit f980d1a43c
1 changed files with 0 additions and 537 deletions

View File

@ -363,128 +363,6 @@ static void SetColourRemap(TextColour colour)
_colour_remap_ptr = _string_colourremap;
}
#if !defined(WITH_ICU)
static WChar *HandleBiDiAndArabicShapes(WChar *text) { return text; }
#else
#include <unicode/ubidi.h>
#include <unicode/ushape.h>
#include <unicode/ustring.h>
/**
* Function to be able to handle right-to-left text and Arabic chars properly.
*
* First: right-to-left (RTL) is stored 'logically' in almost all applications
* and so do we. This means that their text is stored from right to the
* left in memory and any non-RTL text (like numbers or English) are
* then stored from left-to-right. When we want to actually draw the
* text we need to reverse the RTL text in memory, which is what
* happens in ubidi_writeReordered.
* Second: Arabic characters "differ" based on their context. To draw the
* correct variant we pass it through u_shapeArabic. This function can
* add or remove some characters. This is the reason for the lastof
* so we know till where we can fill the output.
*
* Sadly enough these functions work with a custom character format, UChar,
* which isn't the same size as WChar. Because of that we need to transform
* our text first to UChars and then back to something we can use.
*
* To be able to truncate strings properly you must truncate before passing to
* this function. This way the logical begin of the string remains and the end
* gets chopped of instead of the other way around.
*
* The reshaping of Arabic characters might increase or decrease the width of
* the characters/string. So it might still overflow after truncation, though
* the chance is fairly slim as most characters get shorter instead of longer.
* @param buffer the buffer to read from/to
* @param lastof the end of the buffer
* @return the buffer to draw from
*/
static WChar *HandleBiDiAndArabicShapes(WChar *buffer)
{
UChar input[DRAW_STRING_BUFFER];
UChar intermediate[DRAW_STRING_BUFFER];
static WChar output[DRAW_STRING_BUFFER];
/* Transform from UTF-32 to internal ICU format of UTF-16. */
UErrorCode err = U_ZERO_ERROR;
int32_t length = 0;
u_strFromUTF32(input, lengthof(input), &length, (UChar32 *)buffer, -1, &err);
if (U_FAILURE(err)) return buffer;
UBiDi *para = ubidi_openSized(length, 0, &err);
if (para == NULL) return buffer;
ubidi_setPara(para, input, length, _current_text_dir == TD_RTL ? UBIDI_DEFAULT_RTL : UBIDI_DEFAULT_LTR, NULL, &err);
length = ubidi_writeReordered(para, intermediate, lengthof(intermediate), UBIDI_REMOVE_BIDI_CONTROLS, &err);
length = u_shapeArabic(intermediate, length, input, lengthof(input), U_SHAPE_TEXT_DIRECTION_VISUAL_LTR | U_SHAPE_LETTERS_SHAPE, &err);
ubidi_close(para);
if (U_FAILURE(err)) return buffer;
/* Transform back to UTF-32. */
u_strToUTF32((UChar32 *)output, lengthof(output), NULL, input, length, &err);
if (U_FAILURE(err)) return buffer;
/* u_strToUTF32 doesn't add a NUL charcter if the buffer is too small, be safe. */
output[lengthof(output) - 1] = '\0';
return output;
}
#endif /* WITH_ICU */
/**
* Truncate a given string to a maximum width if necessary.
* If the string is truncated, add three dots ('...') to show this.
* @param *str string that is checked and possibly truncated
* @param maxw maximum width in pixels of the string
* @param start_fontsize Fontsize to start the text with
* @return new width of (truncated) string
*/
static int TruncateString(char *str, int maxw, FontSize start_fontsize)
{
int w = 0;
FontSize size = start_fontsize;
int ddd, ddd_w;
WChar c;
char *ddd_pos;
ddd_w = ddd = GetCharacterWidth(size, '.') * 3;
for (ddd_pos = str; (c = Utf8Consume(const_cast<const char **>(&str))) != '\0'; ) {
if (IsPrintable(c) && !IsTextDirectionChar(c)) {
w += GetCharacterWidth(size, c);
if (w > maxw) {
/* string got too big... insert dotdotdot, but make sure we do not
* print anything beyond the string termination character. */
for (int i = 0; *ddd_pos != '\0' && i < 3; i++, ddd_pos++) *ddd_pos = '.';
*ddd_pos = '\0';
return ddd_w;
}
} else {
if (c == SCC_TINYFONT) {
size = FS_SMALL;
ddd = GetCharacterWidth(size, '.') * 3;
} else if (c == SCC_BIGFONT) {
size = FS_LARGE;
ddd = GetCharacterWidth(size, '.') * 3;
} else if (c == '\n') {
DEBUG(misc, 0, "Drawing string using newlines with DrawString instead of DrawStringMultiLine. Please notify the developers of this: [%s]", str);
}
}
/* Remember the last position where three dots fit. */
if (w + ddd < maxw) {
ddd_w = w + ddd;
ddd_pos = str;
}
}
return w;
}
static int ReallyDoDrawString(const WChar *string, int x, int y, DrawStringParams &params, bool parse_string_also_when_clipped = false);
/**
* Drawing routine for drawing a laid out line of text.
* @param line String to draw.
@ -632,109 +510,6 @@ static int DrawLayoutLine(ParagraphLayout::Line *line, int y, int left, int righ
return (align & SA_HOR_MASK) == SA_RIGHT ? left : right;
}
/**
* Get the real width of the string.
* @param str the string to draw
* @param start_fontsize Fontsize to start the text with
* @return the width.
*/
static int GetStringWidth(const WChar *str, FontSize start_fontsize)
{
FontSize size = start_fontsize;
int max_width;
int width;
WChar c;
width = max_width = 0;
for (;;) {
c = *str++;
if (c == 0) break;
if (IsPrintable(c) && !IsTextDirectionChar(c)) {
width += GetCharacterWidth(size, c);
} else {
switch (c) {
case SCC_TINYFONT: size = FS_SMALL; break;
case SCC_BIGFONT: size = FS_LARGE; break;
case '\n':
max_width = max(max_width, width);
break;
}
}
}
return max(max_width, width);
}
/**
* Draw string, possibly truncated to make it fit in its allocated space
*
* @param left The left most position to draw on.
* @param right The right most position to draw on.
* @param top The top most position to draw on.
* @param str String to draw.
* @param last The end of the string buffer to draw.
* @param params Text drawing parameters.
* @param align The alignment of the string when drawing left-to-right. In the
* case a right-to-left language is chosen this is inverted so it
* will be drawn in the right direction.
* @param underline Whether to underline what has been drawn or not.
* @param truncate Whether to truncate the string or not.
*
* @return In case of left or center alignment the right most pixel we have drawn to.
* In case of right alignment the left most pixel we have drawn to.
*/
static int DrawString(int left, int right, int top, char *str, const char *last, DrawStringParams &params, StringAlignment align, bool underline = false, bool truncate = true)
{
if (truncate) TruncateString(str, right - left + 1, params.fontsize);
WChar draw_buffer[DRAW_STRING_BUFFER];
WChar *p = draw_buffer;
const char *text = str;
for (WChar c = Utf8Consume(&text); c != '\0'; c = Utf8Consume(&text)) {
*p++ = c;
}
*p++ = '\0';
/* In case we have a RTL language we swap the alignment. */
if (!(align & SA_FORCE) && _current_text_dir == TD_RTL && (align & SA_HOR_MASK) != SA_HOR_CENTER) align ^= SA_RIGHT;
WChar *to_draw = HandleBiDiAndArabicShapes(draw_buffer);
int w = GetStringWidth(to_draw, params.fontsize);
/* right is the right most position to draw on. In this case we want to do
* calculations with the width of the string. In comparison right can be
* seen as lastof(todraw) and width as lengthof(todraw). They differ by 1.
* So most +1/-1 additions are to move from lengthof to 'indices'.
*/
switch (align & SA_HOR_MASK) {
case SA_LEFT:
/* right + 1 = left + w */
right = left + w - 1;
break;
case SA_HOR_CENTER:
left = RoundDivSU(right + 1 + left - w, 2);
/* right + 1 = left + w */
right = left + w - 1;
break;
case SA_RIGHT:
left = right + 1 - w;
break;
default:
NOT_REACHED();
}
ReallyDoDrawString(to_draw, left, top, params, !truncate);
if (underline) {
GfxFillRect(left, top + FONT_HEIGHT_NORMAL, right, top + FONT_HEIGHT_NORMAL, _string_colourremap[1]);
}
return (align & SA_HOR_MASK) == SA_RIGHT ? left : right;
}
/**
* Draw string, possibly truncated to make it fit in its allocated space
*
@ -778,141 +553,6 @@ int DrawString(int left, int right, int top, StringID str, TextColour colour, St
return DrawString(left, right, top, buffer, colour, align, underline, fontsize);
}
/**
* 'Correct' a string to a maximum length. Longer strings will be cut into
* additional lines at whitespace characters if possible. The string parameter
* is modified with terminating characters mid-string which are the
* placeholders for the newlines.
* The string WILL be truncated if there was no whitespace for the current
* line's maximum width.
*
* @note To know if the terminating '\0' is the string end or just a
* newline, the returned 'num' value should be consulted. The num'th '\0',
* starting with index 0 is the real string end.
*
* @param str string to check and correct for length restrictions
* @param last the last valid location (for '\0') in the buffer of str
* @param maxw the maximum width the string can have on one line
* @param size Fontsize to start the text with
* @return return a 32bit wide number consisting of 2 packed values:
* 0 - 15 the number of lines ADDED to the string
* 16 - 31 the fontsize in which the length calculation was done at
*/
static uint32 FormatStringLinebreaks(char *str, const char *last, int maxw, FontSize size = FS_NORMAL)
{
int num = 0;
assert(maxw > 0);
for (;;) {
/* The character *after* the last space. */
char *last_space = NULL;
int w = 0;
for (;;) {
WChar c = Utf8Consume(const_cast<const char **>(&str));
/* whitespace is where we will insert the line-break */
if (IsWhitespace(c)) last_space = str;
if (IsPrintable(c) && !IsTextDirectionChar(c)) {
int char_w = GetCharacterWidth(size, c);
w += char_w;
if (w > maxw) {
/* The string is longer than maximum width so we need to decide
* what to do with it. */
if (w == char_w) {
/* The character is wider than allowed width; don't know
* what to do with this case... bail out! */
return num + (size << 16);
}
if (last_space == NULL) {
/* No space has been found. Just terminate at our current
* location. This usually happens for languages that do not
* require spaces in strings, like Chinese, Japanese and
* Korean. For other languages terminating mid-word might
* not be the best, but terminating the whole string instead
* of continuing the word at the next line is worse. */
str = Utf8PrevChar(str);
size_t len = strlen(str);
char *terminator = str + len;
/* The string location + length of the string + 1 for '\0'
* always fits; otherwise there's no trailing '\0' and it
* it not a valid string. */
assert(terminator <= last);
assert(*terminator == '\0');
/* If the string is too long we have to terminate it earlier. */
if (terminator == last) {
/* Get the 'begin' of the previous character and make that
* the terminator of the string; we truncate it 'early'. */
*Utf8PrevChar(terminator) = '\0';
len = strlen(str);
}
/* Also move the terminator! */
memmove(str + 1, str, len + 1);
*str = '\0';
/* str needs to point to the character *after* the last space */
str++;
} else {
/* A space is found; perfect place to terminate */
str = last_space;
}
break;
}
} else {
switch (c) {
case '\0': return num + (size << 16);
case SCC_TINYFONT: size = FS_SMALL; break;
case SCC_BIGFONT: size = FS_LARGE; break;
case '\n': goto end_of_inner_loop;
}
}
}
end_of_inner_loop:
/* String didn't fit on line (or a '\n' was encountered), so 'dummy' terminate
* and increase linecount. We use Utf8PrevChar() as also non 1 char long
* whitespace separators are supported */
num++;
char *s = Utf8PrevChar(str);
*s++ = '\0';
/* In which case (see above) we will shift remainder to left and close the gap */
if (str - s >= 1) {
for (; str[-1] != '\0';) *s++ = *str++;
}
}
}
/**
* Calculates height of string (in pixels). Accepts multiline string with '\0' as separators.
* @param src string to check
* @param num number of extra lines (output of FormatStringLinebreaks())
* @param start_fontsize Fontsize to start the text with
* @note assumes text won't be truncated. FormatStringLinebreaks() is a good way to ensure that.
* @return height of pixels of string when it is drawn
*/
static int GetMultilineStringHeight(const char *src, int num, FontSize start_fontsize)
{
int maxy = 0;
int y = 0;
int fh = GetCharacterHeight(start_fontsize);
for (;;) {
WChar c = Utf8Consume(&src);
switch (c) {
case 0: y += fh; if (--num < 0) return maxy; break;
case '\n': y += fh; break;
case SCC_TINYFONT: fh = GetCharacterHeight(FS_SMALL); break;
case SCC_BIGFONT: fh = GetCharacterHeight(FS_LARGE); break;
default: maxy = max<int>(maxy, y + fh); break;
}
}
}
/**
* Calculates height of string (in pixels). The string is changed to a multiline string if needed.
* @param str string to check
@ -977,103 +617,6 @@ Dimension GetStringMultiLineBoundingBox(const char *str, const Dimension &sugges
return box;
}
/**
* Draw string, possibly over multiple lines.
*
* @param left The left most position to draw on.
* @param right The right most position to draw on.
* @param top The top most position to draw on.
* @param bottom The bottom most position to draw on.
* @param str String to draw.
* @param last The end of the string buffer to draw.
* @param colour Colour used for drawing the string, see DoDrawString() for details
* @param align The horizontal and vertical alignment of the string.
* @param underline Whether to underline all strings
* @param fontsize The size of the initial characters.
*
* @return If \a align is #SA_BOTTOM, the top to where we have written, else the bottom to where we have written.
*/
static int DrawStringMultiLine(int left, int right, int top, int bottom, char *str, const char *last, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
{
int maxw = right - left + 1;
int maxh = bottom - top + 1;
/* It makes no sense to even try if it can't be drawn anyway, or
* do we really want to support fonts of 0 or less pixels high? */
if (maxh <= 0) return top;
uint32 tmp = FormatStringLinebreaks(str, last, maxw);
int num = GB(tmp, 0, 16) + 1;
int mt = GetCharacterHeight((FontSize)GB(tmp, 16, 16));
int total_height = num * mt;
int skip_lines = 0;
if (total_height > maxh) {
if (maxh < mt) return top; // Not enough room for a single line.
if ((align & SA_VERT_MASK) == SA_BOTTOM) {
skip_lines = num;
num = maxh / mt;
skip_lines -= num;
} else {
num = maxh / mt;
}
total_height = num * mt;
}
int y;
switch (align & SA_VERT_MASK) {
case SA_TOP:
y = top;
break;
case SA_VERT_CENTER:
y = RoundDivSU(bottom + top - total_height, 2);
break;
case SA_BOTTOM:
y = bottom - total_height;
break;
default: NOT_REACHED();
}
const char *src = str;
DrawStringParams params(colour, fontsize);
int written_top = bottom; // Uppermost position of rendering a line of text
for (;;) {
if (skip_lines == 0) {
char buf2[DRAW_STRING_BUFFER];
strecpy(buf2, src, lastof(buf2));
DrawString(left, right, y, buf2, lastof(buf2), params, align, underline, false);
if (written_top > y) written_top = y;
y += mt;
num--;
}
for (;;) {
WChar c = Utf8Consume(&src);
if (c == 0) {
break;
} else if (skip_lines > 0) {
/* Skipped drawing, so do additional processing to update params. */
if (c >= SCC_BLUE && c <= SCC_BLACK) {
params.SetColour((TextColour)(c - SCC_BLUE));
} else if (c == SCC_PREVIOUS_COLOUR) { // Revert to the previous colour.
params.SetPreviousColour();
} else if (c == SCC_TINYFONT) {
params.SetFontSize(FS_SMALL);
} else if (c == SCC_BIGFONT) {
params.SetFontSize(FS_LARGE);
}
}
}
if (skip_lines > 0) skip_lines--;
if (num == 0) return ((align & SA_VERT_MASK) == SA_BOTTOM) ? written_top : y;
}
}
/**
* Draw string, possibly over multiple lines.
*
@ -1201,86 +744,6 @@ void DrawCharCentered(WChar c, int x, int y, TextColour colour)
GfxMainBlitter(GetGlyph(FS_NORMAL, c), x - GetCharacterWidth(FS_NORMAL, c) / 2, y, BM_COLOUR_REMAP);
}
/**
* Draw a string at the given coordinates with the given colour.
* While drawing the string, parse it in case some formatting is specified,
* like new colour, new size or even positioning.
* @param string The string to draw. This is already bidi reordered.
* @param x Offset from left side of the screen
* @param y Offset from top side of the screen
* @param params Text drawing parameters
* @param parse_string_also_when_clipped
* By default, always test the available space where to draw the string.
* When in multiline drawing, it would already be done,
* so no need to re-perform the same kind (more or less) of verifications.
* It's not only an optimisation, it's also a way to ensures the string will be parsed
* (as there are certain side effects on global variables, which are important for the next line)
* @return the x-coordinates where the drawing has finished.
* If nothing is drawn, the originally passed x-coordinate is returned
*/
static int ReallyDoDrawString(const WChar *string, int x, int y, DrawStringParams &params, bool parse_string_also_when_clipped)
{
DrawPixelInfo *dpi = _cur_dpi;
bool draw_shadow = GetDrawGlyphShadow(FS_NORMAL);
WChar c;
int xo = x;
if (!parse_string_also_when_clipped) {
/* in "mode multiline", the available space have been verified. Not in regular one.
* So if the string cannot be drawn, return the original start to say so.*/
if (x >= dpi->left + dpi->width || y >= dpi->top + dpi->height) return x;
}
switch_colour:;
SetColourRemap(params.cur_colour);
check_bounds:
if (y + _max_char_height <= dpi->top || dpi->top + dpi->height <= y) {
skip_char:;
for (;;) {
c = *string++;
if (!IsPrintable(c)) goto skip_cont;
}
}
for (;;) {
c = *string++;
skip_cont:;
if (c == 0) {
return x; // Nothing more to draw, get out. And here is the new x position
}
if (IsPrintable(c) && !IsTextDirectionChar(c)) {
if (x >= dpi->left + dpi->width) goto skip_char;
if (x + _max_char_width >= dpi->left) {
const Sprite *glyph = GetGlyph(params.fontsize, c);
if (draw_shadow && params.fontsize == FS_NORMAL && params.cur_colour != TC_BLACK && !(c >= SCC_SPRITE_START && c <= SCC_SPRITE_END)) {
SetColourRemap(TC_BLACK);
GfxMainBlitter(glyph, x + 1, y + 1, BM_COLOUR_REMAP);
SetColourRemap(params.cur_colour);
}
GfxMainBlitter(glyph, x, y, BM_COLOUR_REMAP);
}
x += GetCharacterWidth(params.fontsize, c);
} else if (c == '\n') { // newline = {}
x = xo; // We require a new line, so the x coordinate is reset
y += GetCharacterHeight(params.fontsize);
goto check_bounds;
} else if (c >= SCC_BLUE && c <= SCC_BLACK) { // change colour?
params.SetColour((TextColour)(c - SCC_BLUE));
goto switch_colour;
} else if (c == SCC_PREVIOUS_COLOUR) { // revert to the previous colour
params.SetPreviousColour();
goto switch_colour;
} else if (c == SCC_TINYFONT) { // {TINYFONT}
params.SetFontSize(FS_SMALL);
} else if (c == SCC_BIGFONT) { // {BIGFONT}
params.SetFontSize(FS_LARGE);
} else if (!IsTextDirectionChar(c)) {
DEBUG(misc, 0, "[utf8] unknown string command character %d", c);
}
}
}
/**
* Get the size of a sprite.
* @param sprid Sprite to examine.