# HG changeset patch # User rubidium # Date 2013-06-25 20:44:54 # Node ID 4996405dfe571ad8c9dc14aad665fd513b93fe78 # Parent 0170ff7ae55b6f0de1b274e935ba3cfe3f3f32d7 (svn r25472) -Cleanup: remove the old methods for drawing text diff --git a/src/gfx.cpp b/src/gfx.cpp --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -363,128 +363,6 @@ static void SetColourRemap(TextColour co _colour_remap_ptr = _string_colourremap; } -#if !defined(WITH_ICU) -static WChar *HandleBiDiAndArabicShapes(WChar *text) { return text; } -#else -#include -#include -#include - -/** - * 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(&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 ¶ms, bool parse_string_also_when_clipped = false); - /** * Drawing routine for drawing a laid out line of text. * @param line String to draw. @@ -633,109 +511,6 @@ static int DrawLayoutLine(ParagraphLayou } /** - * 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 ¶ms, 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 * * @param left The left most position to draw on. @@ -779,141 +554,6 @@ int DrawString(int left, int right, int } /** - * '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(&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(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 * @param maxw maximum string width @@ -985,103 +625,6 @@ Dimension GetStringMultiLineBoundingBox( * @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. - * - * @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 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 @@ -1202,86 +745,6 @@ void DrawCharCentered(WChar c, int x, in } /** - * 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 ¶ms, 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. * @param [out] offset Optionally returns the sprite position offset.