diff --git a/config.lib b/config.lib index 93c03a568c..6551e25884 100644 --- a/config.lib +++ b/config.lib @@ -3631,12 +3631,13 @@ showhelp() { echo " --with-cocoa enables COCOA video driver (OSX ONLY)" echo " --with-sdl[=sdl-config] enables SDL video driver support" echo " --with-zlib[=zlib.a] enables zlib support" - echo " --with-liblzma[=liblzma.a] enables liblzma support" + echo " --with-liblzma[=\"pkg-config liblzma\"]" + echo " enables liblzma support" echo " --with-liblzo2[=liblzo2.a] enables liblzo2 support" echo " --with-png[=libpng-config] enables libpng support" echo " --with-freetype[=freetype-config]" echo " enables libfreetype support" - echo " --with-fontconfig[=pkg-config fontconfig]" + echo " --with-fontconfig[=\"pkg-config fontconfig\"]" echo " enables fontconfig support" echo " --with-icu[=icu-config] enables icu (used for right-to-left support)" echo " --static-icu try to link statically (libsicu instead of" diff --git a/known-bugs.txt b/known-bugs.txt index 0a0f1d4c45..626289a386 100644 --- a/known-bugs.txt +++ b/known-bugs.txt @@ -403,3 +403,44 @@ Mouse cursor going missing with SDL [FS#4997]: We cannot fix this problem as SDL simply does not provide the required information in these corner cases. This is a bug in SDL and as such there is little that we can do about it. + +Inconsistent catchment areas [FS#5661]: + Due to performance decisions the catchment area for cargo accepted + by a station for delivery to houses or industries differs from the + catchment area for cargo that is delivered to stations from houses + or industries. + + Conceptually they work the same, but the effect in game differs. + They work by finding the closest destination "around" the source + which is within a certain distance. This distance depends on the + type of station, e.g. road stops have a smaller catchment area than + large airports. In both cases the bounding box, the smallest + rectangle that contains all tiles of something, is searched for the + target of the cargo, and then spiraling outwards finding the closest + tile of the target. + + In the case of a station with two tiles spread far apart with a house + that is within the station's bounding box, it would be possible that + the spiraling search from the house does not reach one of the station + tiles before the search ends, i.e. all tiles within that distance + are searched. So the house does not deliver cargo to the station. On + the other hand, the station will deliver cargo because the house + falls within the bounding box, and thus search area. + + It is possible to make these consistent, but then cargo from a house + to a station needs to search up to 32 tiles around itself, i.e. 64 + by 64 tiles, to find all possible stations it could deliver to + instead of 10 by 10 tiles (40 times more tiles). Alternatively the + search from a station could be changed to use the actual tiles, but + that would require considering checking 10 by 10 tiles for each of + the tiles of a station, instead of just once. + +Trains might not stop at platforms that are currently being changed [FS#5553]: + If you add tiles to or remove tiles from a platform while a train is + approaching to stop at the same platform, that train can miss the place + where it's supposed to stop and pass the station without stopping. This + is caused by the fact that the train is considered to already have stopped + if it's beyond its assigned stopping location. We can't let the train stop + just anywhere in the station because then it would never leave the station + if you have the same station in the order list multiple times in a row or + if there is only one station in the order list (see FS#5684). diff --git a/src/gfx.cpp b/src/gfx.cpp index b1851f0ebe..114f852ef3 100644 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -270,12 +270,12 @@ static void SetColourRemap(TextColour colour) * @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 DrawLayoutLine(ParagraphLayout::Line *line, int y, int left, int right, StringAlignment align, bool underline, bool truncation) +static int DrawLayoutLine(const ParagraphLayouter::Line *line, int y, int left, int right, StringAlignment align, bool underline, bool truncation) { - if (line->countRuns() == 0) return 0; + if (line->CountRuns() == 0) return 0; - int w = line->getWidth(); - int h = line->getLeading(); + int w = line->GetWidth(); + int h = line->GetLeading(); /* * The following is needed for truncation. @@ -306,7 +306,7 @@ static int DrawLayoutLine(ParagraphLayout::Line *line, int y, int left, int righ * another size would be chosen it won't have truncated too little for * the truncation dots. */ - FontCache *fc = ((const Font*)line->getVisualRun(0)->getFont())->fc; + FontCache *fc = ((const Font*)line->GetVisualRun(0)->GetFont())->fc; GlyphID dot_glyph = fc->MapCharToGlyph('.'); dot_width = fc->GetGlyphWidth(dot_glyph); dot_sprite = fc->GetGlyph(dot_glyph); @@ -349,9 +349,9 @@ static int DrawLayoutLine(ParagraphLayout::Line *line, int y, int left, int righ NOT_REACHED(); } - for (int run_index = 0; run_index < line->countRuns(); run_index++) { - const ParagraphLayout::VisualRun *run = line->getVisualRun(run_index); - const Font *f = (const Font*)run->getFont(); + for (int run_index = 0; run_index < line->CountRuns(); run_index++) { + const ParagraphLayouter::VisualRun *run = line->GetVisualRun(run_index); + const Font *f = (const Font*)run->GetFont(); FontCache *fc = f->fc; TextColour colour = f->colour; @@ -363,15 +363,15 @@ static int DrawLayoutLine(ParagraphLayout::Line *line, int y, int left, int righ bool draw_shadow = fc->GetDrawGlyphShadow() && colour != TC_BLACK; - for (int i = 0; i < run->getGlyphCount(); i++) { - GlyphID glyph = run->getGlyphs()[i]; + for (int i = 0; i < run->GetGlyphCount(); i++) { + GlyphID glyph = run->GetGlyphs()[i]; /* Not a valid glyph (empty) */ if (glyph == 0xFFFF) continue; - int begin_x = run->getPositions()[i * 2] + left - offset_x; - int end_x = run->getPositions()[i * 2 + 2] + left - offset_x - 1; - int top = run->getPositions()[i * 2 + 1] + y; + int begin_x = (int)run->GetPositions()[i * 2] + left - offset_x; + int end_x = (int)run->GetPositions()[i * 2 + 2] + left - offset_x - 1; + int top = (int)run->GetPositions()[i * 2 + 1] + y; /* Truncated away. */ if (truncation && (begin_x < min_x || end_x > max_x)) continue; @@ -571,10 +571,10 @@ int DrawStringMultiLine(int left, int right, int top, int bottom, const char *st int last_line = top; int first_line = bottom; - for (ParagraphLayout::Line **iter = layout.Begin(); iter != layout.End(); iter++) { - ParagraphLayout::Line *line = *iter; + for (const ParagraphLayouter::Line **iter = layout.Begin(); iter != layout.End(); iter++) { + const ParagraphLayouter::Line *line = *iter; - int line_height = line->getLeading(); + int line_height = line->GetLeading(); if (y >= top && y < bottom) { last_line = y + line_height; if (first_line > y) first_line = y; diff --git a/src/gfx_layout.cpp b/src/gfx_layout.cpp index fa543cd921..a4fc6969f8 100644 --- a/src/gfx_layout.cpp +++ b/src/gfx_layout.cpp @@ -13,6 +13,7 @@ #include "gfx_layout.h" #include "string_func.h" #include "strings_func.h" +#include "debug.h" #include "table/control_codes.h" @@ -110,7 +111,7 @@ le_bool Font::getGlyphPoint(LEGlyphID glyph, le_int32 pointNumber, LEPoint &poin return FALSE; } -size_t Layouter::AppendToBuffer(UChar *buff, const UChar *buffer_last, WChar c) +static size_t AppendToBuffer(UChar *buff, const UChar *buffer_last, WChar c) { /* Transform from UTF-32 to internal ICU format of UTF-16. */ int32 length = 0; @@ -119,7 +120,63 @@ size_t Layouter::AppendToBuffer(UChar *buff, const UChar *buffer_last, WChar c) return length; } -ParagraphLayout *Layouter::GetParagraphLayout(UChar *buff, UChar *buff_end, FontMap &fontMapping) +/** + * Wrapper for doing layouts with ICU. + */ +class ICUParagraphLayout : public AutoDeleteSmallVector, public ParagraphLayouter { + ParagraphLayout *p; ///< The actual ICU paragraph layout. +public: + /** Helper for GetLayouter, to get the right type. */ + typedef UChar CharType; + /** Helper for GetLayouter, to get whether the layouter supports RTL. */ + static const bool SUPPORTS_RTL = true; + + /** Visual run contains data about the bit of text with the same font. */ + class ICUVisualRun : public ParagraphLayouter::VisualRun { + const ParagraphLayout::VisualRun *vr; ///< The actual ICU vr. + + public: + ICUVisualRun(const ParagraphLayout::VisualRun *vr) : vr(vr) { } + + const Font *GetFont() const { return (const Font*)vr->getFont(); } + int GetGlyphCount() const { return vr->getGlyphCount(); } + const GlyphID *GetGlyphs() const { return vr->getGlyphs(); } + const float *GetPositions() const { return vr->getPositions(); } + int GetLeading() const { return vr->getLeading(); } + const int *GetGlyphToCharMap() const { return vr->getGlyphToCharMap(); } + }; + + /** A single line worth of VisualRuns. */ + class ICULine : public AutoDeleteSmallVector, public ParagraphLayouter::Line { + ParagraphLayout::Line *l; ///< The actual ICU line. + + public: + ICULine(ParagraphLayout::Line *l) : l(l) + { + for (int i = 0; i < l->countRuns(); i++) { + *this->Append() = new ICUVisualRun(l->getVisualRun(i)); + } + } + ~ICULine() { delete l; } + + int GetLeading() const { return l->getLeading(); } + int GetWidth() const { return l->getWidth(); } + int CountRuns() const { return l->countRuns(); } + const ParagraphLayouter::VisualRun *GetVisualRun(int run) const { return *this->Get(run); } + }; + + ICUParagraphLayout(ParagraphLayout *p) : p(p) { } + ~ICUParagraphLayout() { delete p; } + void Reflow() { p->reflow(); } + + ParagraphLayouter::Line *NextLine(int max_width) + { + ParagraphLayout::Line *l = p->nextLine(max_width); + return l == NULL ? NULL : new ICULine(l); + } +}; + +static ParagraphLayouter *GetParagraphLayout(UChar *buff, UChar *buff_end, FontMap &fontMapping) { int32 length = buff_end - buff; @@ -139,12 +196,79 @@ ParagraphLayout *Layouter::GetParagraphLayout(UChar *buff, UChar *buff_end, Font LEErrorCode status = LE_NO_ERROR; /* ParagraphLayout does not copy "buff", so it must stay valid. * "runs" is copied according to the ICU source, but the documentation does not specify anything, so this might break somewhen. */ - return new ParagraphLayout(buff, length, &runs, NULL, NULL, NULL, _current_text_dir == TD_RTL ? UBIDI_DEFAULT_RTL : UBIDI_DEFAULT_LTR, false, status); + ParagraphLayout *p = new ParagraphLayout(buff, length, &runs, NULL, NULL, NULL, _current_text_dir == TD_RTL ? UBIDI_DEFAULT_RTL : UBIDI_DEFAULT_LTR, false, status); + if (status != LE_NO_ERROR) { + delete p; + return NULL; + } + + return new ICUParagraphLayout(p); } -#else /* WITH_ICU */ +#endif /* WITH_ICU */ /*** Paragraph layout ***/ +/** + * Class handling the splitting of a paragraph of text into lines and + * visual runs. + * + * One constructs this class with the text that needs to be split into + * lines. Then nextLine is called with the maximum width until NULL is + * returned. Each nextLine call creates VisualRuns which contain the + * length of text that are to be drawn with the same font. In other + * words, the result of this class is a list of sub strings with their + * font. The sub strings are then already fully laid out, and only + * need actual drawing. + * + * The positions in a visual run are sequential pairs of X,Y of the + * begin of each of the glyphs plus an extra pair to mark the end. + * + * @note This variant does not handle left-to-right properly. This + * is supported in the one ParagraphLayout coming from ICU. + */ +class FallbackParagraphLayout : public ParagraphLayouter { +public: + /** Helper for GetLayouter, to get the right type. */ + typedef WChar CharType; + /** Helper for GetLayouter, to get whether the layouter supports RTL. */ + static const bool SUPPORTS_RTL = false; + + /** Visual run contains data about the bit of text with the same font. */ + class FallbackVisualRun : public ParagraphLayouter::VisualRun { + Font *font; ///< The font used to layout these. + GlyphID *glyphs; ///< The glyphs we're drawing. + float *positions; ///< The positions of the glyphs. + int *glyph_to_char; ///< The char index of the glyphs. + int glyph_count; ///< The number of glyphs. + + public: + FallbackVisualRun(Font *font, const WChar *chars, int glyph_count, int x); + ~FallbackVisualRun(); + const Font *GetFont() const; + int GetGlyphCount() const; + const GlyphID *GetGlyphs() const; + const float *GetPositions() const; + int GetLeading() const; + const int *GetGlyphToCharMap() const; + }; + + /** A single line worth of VisualRuns. */ + class FallbackLine : public AutoDeleteSmallVector, public ParagraphLayouter::Line { + public: + int GetLeading() const; + int GetWidth() const; + int CountRuns() const; + const ParagraphLayouter::VisualRun *GetVisualRun(int run) const; + }; + + const WChar *buffer_begin; ///< Begin of the buffer. + const WChar *buffer; ///< The current location in the buffer. + FontMap &runs; ///< The fonts we have to use for this paragraph. + + FallbackParagraphLayout(WChar *buffer, int length, FontMap &runs); + void Reflow(); + const ParagraphLayouter::Line *NextLine(int max_width); +}; /** * Create the visual run. @@ -153,7 +277,7 @@ ParagraphLayout *Layouter::GetParagraphLayout(UChar *buff, UChar *buff_end, Font * @param char_count The number of characters in this run. * @param x The initial x position for this run. */ -ParagraphLayout::VisualRun::VisualRun(Font *font, const WChar *chars, int char_count, int x) : +FallbackParagraphLayout::FallbackVisualRun::FallbackVisualRun(Font *font, const WChar *chars, int char_count, int x) : font(font), glyph_count(char_count) { this->glyphs = MallocT(this->glyph_count); @@ -173,7 +297,7 @@ ParagraphLayout::VisualRun::VisualRun(Font *font, const WChar *chars, int char_c } /** Free all data. */ -ParagraphLayout::VisualRun::~VisualRun() +FallbackParagraphLayout::FallbackVisualRun::~FallbackVisualRun() { free(this->positions); free(this->glyph_to_char); @@ -184,7 +308,7 @@ ParagraphLayout::VisualRun::~VisualRun() * Get the font associated with this run. * @return The font. */ -Font *ParagraphLayout::VisualRun::getFont() const +const Font *FallbackParagraphLayout::FallbackVisualRun::GetFont() const { return this->font; } @@ -193,7 +317,7 @@ Font *ParagraphLayout::VisualRun::getFont() const * Get the number of glyhps in this run. * @return The number of glyphs. */ -int ParagraphLayout::VisualRun::getGlyphCount() const +int FallbackParagraphLayout::FallbackVisualRun::GetGlyphCount() const { return this->glyph_count; } @@ -202,7 +326,7 @@ int ParagraphLayout::VisualRun::getGlyphCount() const * Get the glyhps of this run. * @return The glyphs. */ -const GlyphID *ParagraphLayout::VisualRun::getGlyphs() const +const GlyphID *FallbackParagraphLayout::FallbackVisualRun::GetGlyphs() const { return this->glyphs; } @@ -211,7 +335,7 @@ const GlyphID *ParagraphLayout::VisualRun::getGlyphs() const * Get the positions of this run. * @return The positions. */ -float *ParagraphLayout::VisualRun::getPositions() const +const float *FallbackParagraphLayout::FallbackVisualRun::GetPositions() const { return this->positions; } @@ -220,7 +344,7 @@ float *ParagraphLayout::VisualRun::getPositions() const * Get the glyph-to-character map for this visual run. * @return The glyph-to-character map. */ -const int *ParagraphLayout::VisualRun::getGlyphToCharMap() const +const int *FallbackParagraphLayout::FallbackVisualRun::GetGlyphToCharMap() const { return this->glyph_to_char; } @@ -229,20 +353,20 @@ const int *ParagraphLayout::VisualRun::getGlyphToCharMap() const * Get the height of this font. * @return The height of the font. */ -int ParagraphLayout::VisualRun::getLeading() const +int FallbackParagraphLayout::FallbackVisualRun::GetLeading() const { - return this->getFont()->fc->GetHeight(); + return this->GetFont()->fc->GetHeight(); } /** * Get the height of the line. * @return The maximum height of the line. */ -int ParagraphLayout::Line::getLeading() const +int FallbackParagraphLayout::FallbackLine::GetLeading() const { int leading = 0; - for (const VisualRun * const *run = this->Begin(); run != this->End(); run++) { - leading = max(leading, (*run)->getLeading()); + for (const FallbackVisualRun * const *run = this->Begin(); run != this->End(); run++) { + leading = max(leading, (*run)->GetLeading()); } return leading; @@ -252,7 +376,7 @@ int ParagraphLayout::Line::getLeading() const * Get the width of this line. * @return The width of the line. */ -int ParagraphLayout::Line::getWidth() const +int FallbackParagraphLayout::FallbackLine::GetWidth() const { if (this->Length() == 0) return 0; @@ -261,15 +385,15 @@ int ParagraphLayout::Line::getWidth() const * Since there is no left-to-right support, taking this value of * the last run gives us the end of the line and thus the width. */ - const VisualRun *run = this->getVisualRun(this->countRuns() - 1); - return run->getPositions()[run->getGlyphCount() * 2]; + const ParagraphLayouter::VisualRun *run = this->GetVisualRun(this->CountRuns() - 1); + return (int)run->GetPositions()[run->GetGlyphCount() * 2]; } /** * Get the number of runs in this line. * @return The number of runs. */ -int ParagraphLayout::Line::countRuns() const +int FallbackParagraphLayout::FallbackLine::CountRuns() const { return this->Length(); } @@ -278,7 +402,7 @@ int ParagraphLayout::Line::countRuns() const * Get a specific visual run. * @return The visual run. */ -ParagraphLayout::VisualRun *ParagraphLayout::Line::getVisualRun(int run) const +const ParagraphLayouter::VisualRun *FallbackParagraphLayout::FallbackLine::GetVisualRun(int run) const { return *this->Get(run); } @@ -289,7 +413,7 @@ ParagraphLayout::VisualRun *ParagraphLayout::Line::getVisualRun(int run) const * @param length The length of the paragraph. * @param runs The font mapping of this paragraph. */ -ParagraphLayout::ParagraphLayout(WChar *buffer, int length, FontMap &runs) : buffer_begin(buffer), buffer(buffer), runs(runs) +FallbackParagraphLayout::FallbackParagraphLayout(WChar *buffer, int length, FontMap &runs) : buffer_begin(buffer), buffer(buffer), runs(runs) { assert(runs.End()[-1].first == length); } @@ -297,7 +421,7 @@ ParagraphLayout::ParagraphLayout(WChar *buffer, int length, FontMap &runs) : buf /** * Reset the position to the start of the paragraph. */ -void ParagraphLayout::reflow() +void FallbackParagraphLayout::Reflow() { this->buffer = this->buffer_begin; } @@ -307,7 +431,7 @@ void ParagraphLayout::reflow() * @param max_width The maximum width of the string. * @return A Line, or NULL when at the end of the paragraph. */ -ParagraphLayout::Line *ParagraphLayout::nextLine(int max_width) +const ParagraphLayouter::Line *FallbackParagraphLayout::NextLine(int max_width) { /* Simple idea: * - split a line at a newline character, or at a space where we can break a line. @@ -315,12 +439,12 @@ ParagraphLayout::Line *ParagraphLayout::nextLine(int max_width) */ if (this->buffer == NULL) return NULL; - Line *l = new Line(); + FallbackLine *l = new FallbackLine(); if (*this->buffer == '\0') { /* Only a newline. */ this->buffer = NULL; - *l->Append() = new VisualRun(this->runs.Begin()->second, this->buffer, 0, 0); + *l->Append() = new FallbackVisualRun(this->runs.Begin()->second, this->buffer, 0, 0); return l; } @@ -349,8 +473,8 @@ ParagraphLayout::Line *ParagraphLayout::nextLine(int max_width) } if (this->buffer == next_run) { - int w = l->getWidth(); - *l->Append() = new VisualRun(iter->second, begin, this->buffer - begin, w); + int w = l->GetWidth(); + *l->Append() = new FallbackVisualRun(iter->second, begin, this->buffer - begin, w); iter++; assert(iter != this->runs.End()); @@ -396,8 +520,8 @@ ParagraphLayout::Line *ParagraphLayout::nextLine(int max_width) } if (l->Length() == 0 || last_char - begin != 0) { - int w = l->getWidth(); - *l->Append() = new VisualRun(iter->second, begin, last_char - begin, w); + int w = l->GetWidth(); + *l->Append() = new FallbackVisualRun(iter->second, begin, last_char - begin, w); } return l; } @@ -409,7 +533,7 @@ ParagraphLayout::Line *ParagraphLayout::nextLine(int max_width) * @param c The character to add. * @return The number of buffer spaces that were used. */ -size_t Layouter::AppendToBuffer(WChar *buff, const WChar *buffer_last, WChar c) +static size_t AppendToBuffer(WChar *buff, const WChar *buffer_last, WChar c) { *buff = c; return 1; @@ -422,11 +546,74 @@ size_t Layouter::AppendToBuffer(WChar *buff, const WChar *buffer_last, WChar c) * @param fontMapping THe mapping of the fonts. * @return The ParagraphLayout instance. */ -ParagraphLayout *Layouter::GetParagraphLayout(WChar *buff, WChar *buff_end, FontMap &fontMapping) +static FallbackParagraphLayout *GetParagraphLayout(WChar *buff, WChar *buff_end, FontMap &fontMapping) { - return new ParagraphLayout(buff, buff_end - buff, fontMapping); + return new FallbackParagraphLayout(buff, buff_end - buff, fontMapping); +} + +/** + * Helper for getting a ParagraphLayouter of the given type. + * + * @note In case no ParagraphLayouter could be constructed, line.layout will be NULL. + * @param line The cache item to store our layouter in. + * @param str The string to create a layouter for. + * @param state The state of the font and color. + * @tparam T The type of layouter we want. + */ +template +static inline void GetLayouter(Layouter::LineCacheItem &line, const char *str, FontState state) +{ + if (line.buffer != NULL) free(line.buffer); + + typename T::CharType *buff_begin = MallocT(DRAW_STRING_BUFFER); + const typename T::CharType *buffer_last = buff_begin + DRAW_STRING_BUFFER; + typename T::CharType *buff = buff_begin; + FontMap &fontMapping = line.runs; + Font *f = Layouter::GetFont(state.fontsize, state.cur_colour); + + line.buffer = buff_begin; + + /* + * Go through the whole string while adding Font instances to the font map + * whenever the font changes, and convert the wide characters into a format + * usable by ParagraphLayout. + */ + for (; buff < buffer_last;) { + WChar c = Utf8Consume(const_cast(&str)); + if (c == '\0' || c == '\n') { + break; + } else if (c >= SCC_BLUE && c <= SCC_BLACK) { + state.SetColour((TextColour)(c - SCC_BLUE)); + } else if (c == SCC_PREVIOUS_COLOUR) { // Revert to the previous colour. + state.SetPreviousColour(); + } else if (c == SCC_TINYFONT) { + state.SetFontSize(FS_SMALL); + } else if (c == SCC_BIGFONT) { + state.SetFontSize(FS_LARGE); + } else { + /* Filter out text direction characters that shouldn't be drawn, and + * will not be handled in the fallback non ICU case because they are + * mostly needed for RTL languages which need more ICU support. */ + if (!T::SUPPORTS_RTL && IsTextDirectionChar(c)) continue; + buff += AppendToBuffer(buff, buffer_last, c); + continue; + } + + if (!fontMapping.Contains(buff - buff_begin)) { + fontMapping.Insert(buff - buff_begin, f); + } + f = Layouter::GetFont(state.fontsize, state.cur_colour); + } + + /* Better safe than sorry. */ + *buff = '\0'; + + if (!fontMapping.Contains(buff - buff_begin)) { + fontMapping.Insert(buff - buff_begin, f); + } + line.layout = GetParagraphLayout(buff_begin, buff, fontMapping); + line.state_after = state; } -#endif /* !WITH_ICU */ /** * Create a new layouter. @@ -454,62 +641,27 @@ Layouter::Layouter(const char *str, int maxw, TextColour colour, FontSize fontsi /* Line is in cache */ str = lineend + 1; state = line.state_after; - line.layout->reflow(); + line.layout->Reflow(); } else { /* Line is new, layout it */ - const CharType *buffer_last = lastof(line.buffer); - CharType *buff_begin = line.buffer; - CharType *buff = buff_begin; - FontMap &fontMapping = line.runs; - Font *f = GetFont(state.fontsize, state.cur_colour); - - /* - * Go through the whole string while adding Font instances to the font map - * whenever the font changes, and convert the wide characters into a format - * usable by ParagraphLayout. - */ - for (; buff < buffer_last;) { - c = Utf8Consume(const_cast(&str)); - if (c == '\0' || c == '\n') { - break; - } else if (c >= SCC_BLUE && c <= SCC_BLACK) { - state.SetColour((TextColour)(c - SCC_BLUE)); - } else if (c == SCC_PREVIOUS_COLOUR) { // Revert to the previous colour. - state.SetPreviousColour(); - } else if (c == SCC_TINYFONT) { - state.SetFontSize(FS_SMALL); - } else if (c == SCC_BIGFONT) { - state.SetFontSize(FS_LARGE); - } else { -#ifndef WITH_ICU - /* Filter out text direction characters that shouldn't be drawn, and - * will not be handled in the fallback non ICU case because they are - * mostly needed for RTL languages which need more ICU support. */ - if (IsTextDirectionChar(c)) continue; +#ifdef WITH_ICU + GetLayouter(line, str, state); + if (line.layout == NULL) { + static bool warned = false; + if (!warned) { + DEBUG(misc, 0, "ICU layouter bailed on the font. Falling back to the fallback layouter"); + warned = true; + } + GetLayouter(line, str, state); + } +#else + GetLayouter(line, str, state); #endif - buff += AppendToBuffer(buff, buffer_last, c); - continue; - } - - if (!fontMapping.Contains(buff - buff_begin)) { - fontMapping.Insert(buff - buff_begin, f); - } - f = GetFont(state.fontsize, state.cur_colour); - } - - /* Better safe than sorry. */ - *buff = '\0'; - - if (!fontMapping.Contains(buff - buff_begin)) { - fontMapping.Insert(buff - buff_begin, f); - } - line.layout = GetParagraphLayout(buff_begin, buff, fontMapping); - line.state_after = state; } /* Copy all lines into a local cache so we can reuse them later on more easily. */ - ParagraphLayout::Line *l; - while ((l = line.layout->nextLine(maxw)) != NULL) { + const ParagraphLayouter::Line *l; + while ((l = line.layout->NextLine(maxw)) != NULL) { *this->Append() = l; } @@ -523,9 +675,9 @@ Layouter::Layouter(const char *str, int maxw, TextColour colour, FontSize fontsi Dimension Layouter::GetBounds() { Dimension d = { 0, 0 }; - for (ParagraphLayout::Line **l = this->Begin(); l != this->End(); l++) { - d.width = max(d.width, (*l)->getWidth()); - d.height += (*l)->getLeading(); + for (const ParagraphLayouter::Line **l = this->Begin(); l != this->End(); l++) { + d.width = max(d.width, (*l)->GetWidth()); + d.height += (*l)->GetLeading(); } return d; } @@ -557,22 +709,22 @@ Point Layouter::GetCharPosition(const char *ch) const if (str == ch) { /* Valid character. */ - const ParagraphLayout::Line *line = *this->Begin(); + const ParagraphLayouter::Line *line = *this->Begin(); /* Pointer to the end-of-string/line marker? Return total line width. */ if (*ch == '\0' || *ch == '\n') { - Point p = { line->getWidth(), 0 }; + Point p = { line->GetWidth(), 0 }; return p; } /* Scan all runs until we've found our code point index. */ - for (int run_index = 0; run_index < line->countRuns(); run_index++) { - const ParagraphLayout::VisualRun *run = line->getVisualRun(run_index); + for (int run_index = 0; run_index < line->CountRuns(); run_index++) { + const ParagraphLayouter::VisualRun *run = line->GetVisualRun(run_index); - for (int i = 0; i < run->getGlyphCount(); i++) { + for (int i = 0; i < run->GetGlyphCount(); i++) { /* Matching glyph? Return position. */ - if ((size_t)run->getGlyphToCharMap()[i] == index) { - Point p = { (int)run->getPositions()[i * 2], (int)run->getPositions()[i * 2 + 1] }; + if ((size_t)run->GetGlyphToCharMap()[i] == index) { + Point p = { (int)run->GetPositions()[i * 2], (int)run->GetPositions()[i * 2 + 1] }; return p; } } diff --git a/src/gfx_layout.h b/src/gfx_layout.h index 8b5d0e8faf..4a30860cc7 100644 --- a/src/gfx_layout.h +++ b/src/gfx_layout.h @@ -97,84 +97,47 @@ public: /** Mapping from index to font. */ typedef SmallMap FontMap; -#ifndef WITH_ICU /** - * Class handling the splitting of a paragraph of text into lines and - * visual runs. - * - * One constructs this class with the text that needs to be split into - * lines. Then nextLine is called with the maximum width until NULL is - * returned. Each nextLine call creates VisualRuns which contain the - * length of text that are to be drawn with the same font. In other - * words, the result of this class is a list of sub strings with their - * font. The sub strings are then already fully laid out, and only - * need actual drawing. - * - * The positions in a visual run are sequential pairs of X,Y of the - * begin of each of the glyphs plus an extra pair to mark the end. - * - * @note This variant does not handle left-to-right properly. This - * is supported in the one ParagraphLayout coming from ICU. - * @note Does not conform to function naming style as it provides a - * fallback for the ICU class. + * Interface to glue fallback and normal layouter into one. */ -class ParagraphLayout { +class ParagraphLayouter { public: + virtual ~ParagraphLayouter() {} + /** Visual run contains data about the bit of text with the same font. */ class VisualRun { - Font *font; ///< The font used to layout these. - GlyphID *glyphs; ///< The glyphs we're drawing. - float *positions; ///< The positions of the glyphs. - int *glyph_to_char; ///< The char index of the glyphs. - int glyph_count; ///< The number of glyphs. - public: - VisualRun(Font *font, const WChar *chars, int glyph_count, int x); - ~VisualRun(); - Font *getFont() const; - int getGlyphCount() const; - const GlyphID *getGlyphs() const; - float *getPositions() const; - int getLeading() const; - const int *getGlyphToCharMap() const; + virtual ~VisualRun() {} + virtual const Font *GetFont() const = 0; + virtual int GetGlyphCount() const = 0; + virtual const GlyphID *GetGlyphs() const = 0; + virtual const float *GetPositions() const = 0; + virtual int GetLeading() const = 0; + virtual const int *GetGlyphToCharMap() const = 0; }; /** A single line worth of VisualRuns. */ - class Line : public AutoDeleteSmallVector { + class Line { public: - int getLeading() const; - int getWidth() const; - int countRuns() const; - VisualRun *getVisualRun(int run) const; + virtual ~Line() {} + virtual int GetLeading() const = 0; + virtual int GetWidth() const = 0; + virtual int CountRuns() const = 0; + virtual const VisualRun *GetVisualRun(int run) const = 0; }; - const WChar *buffer_begin; ///< Begin of the buffer. - const WChar *buffer; ///< The current location in the buffer. - FontMap &runs; ///< The fonts we have to use for this paragraph. - - ParagraphLayout(WChar *buffer, int length, FontMap &runs); - void reflow(); - Line *nextLine(int max_width); + virtual void Reflow() = 0; + virtual const Line *NextLine(int max_width) = 0; }; -#endif /* !WITH_ICU */ /** * The layouter performs all the layout work. * * It also accounts for the memory allocations and frees. */ -class Layouter : public AutoDeleteSmallVector { -#ifdef WITH_ICU - typedef UChar CharType; ///< The type of character used within the layouter. -#else /* WITH_ICU */ - typedef WChar CharType; ///< The type of character used within the layouter. -#endif /* WITH_ICU */ - +class Layouter : public AutoDeleteSmallVector { const char *string; ///< Pointer to the original string. - size_t AppendToBuffer(CharType *buff, const CharType *buffer_last, WChar c); - ParagraphLayout *GetParagraphLayout(CharType *buff, CharType *buff_end, FontMap &fontMapping); - /** Key into the linecache */ struct LineCacheKey { FontState state_before; ///< Font state at the beginning of the line. @@ -189,18 +152,20 @@ class Layouter : public AutoDeleteSmallVector { return this->str < other.str; } }; +public: /** Item in the linecache */ struct LineCacheItem { /* Stuff that cannot be freed until the ParagraphLayout is freed */ - CharType buffer[DRAW_STRING_BUFFER]; ///< Accessed by both ICU's and our ParagraphLayout::nextLine. - FontMap runs; ///< Accessed by our ParagraphLayout::nextLine. + void *buffer; ///< Accessed by both ICU's and our ParagraphLayout::nextLine. + FontMap runs; ///< Accessed by our ParagraphLayout::nextLine. - FontState state_after; ///< Font state after the line. - ParagraphLayout *layout; ///< Layout of the line. + FontState state_after; ///< Font state after the line. + ParagraphLayouter *layout; ///< Layout of the line. - LineCacheItem() : layout(NULL) {} - ~LineCacheItem() { delete layout; } + LineCacheItem() : buffer(NULL), layout(NULL) {} + ~LineCacheItem() { delete layout; free(buffer); } }; +private: typedef std::map LineCache; static LineCache *linecache; @@ -208,9 +173,9 @@ class Layouter : public AutoDeleteSmallVector { typedef SmallMap FontColourMap; static FontColourMap fonts[FS_END]; +public: static Font *GetFont(FontSize size, TextColour colour); -public: Layouter(const char *str, int maxw = INT32_MAX, TextColour colour = TC_FROMSTRING, FontSize fontsize = FS_NORMAL); Dimension GetBounds(); Point GetCharPosition(const char *ch) const; diff --git a/src/script/api/script_controller.hpp b/src/script/api/script_controller.hpp index 20a849fc84..10fd48dcdc 100644 --- a/src/script/api/script_controller.hpp +++ b/src/script/api/script_controller.hpp @@ -127,7 +127,8 @@ public: /** * Import a library. - * @param library The name of the library to import. + * @param library The name of the library to import. The name should be composed as ScriptInfo::GetCategory() + "." + + * ScriptInfo::CreateInstance(). * @param class_name Under which name you want it to be available (or "" if you just want the returning object). * @param version Which version you want specifically. * @return The loaded library object. If class_name is set, it is also available (under the scope of the import) under that name. diff --git a/src/script/api/script_info_docs.hpp b/src/script/api/script_info_docs.hpp index 1ae38750c3..e9e83881a0 100644 --- a/src/script/api/script_info_docs.hpp +++ b/src/script/api/script_info_docs.hpp @@ -18,9 +18,10 @@ * Scripts must or can implemented to provide information to OpenTTD to * base configuring/starting/loading the Script on. * - * @note The required functions are also needed for Script Libraries. As such - * the information here can be used for libraries, but the information - * will not be shown in the GUI except for error/debug messages. + * @note The required functions are also needed for Script Libraries, but in + * that case you extend ScriptLibrary. As such the information here can + * be used for libraries, but the information will not be shown in the + * GUI except for error/debug messages. * * @api ai game */ @@ -43,6 +44,8 @@ public: * * @return The name of the Script. * @note This function is required. + * @note This name is not used as library name by ScriptController::Import, + * instead the name returned by #CreateInstance is used. */ string GetName(); @@ -144,7 +147,8 @@ public: /** * Gets the name of main class of the Script so OpenTTD knows - * what class to instantiate. + * what class to instantiate. For libraries, this name is also + * used when other scripts import it using @ScriptController::Import. * * @return The class name of the Script. * @note This function is required. diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 4ddf150ea5..0b86810f00 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -3131,11 +3131,13 @@ static VehicleEnterTileStatus VehicleEnter_Station(Vehicle *v, TileIndex tile, i if (dir != DIAGDIR_SE && dir != DIAGDIR_SW) x = TILE_SIZE - 1 - x; stop &= TILE_SIZE - 1; - if (x >= stop) return VETSB_ENTERED_STATION | (VehicleEnterTileStatus)(station_id << VETS_STATION_ID_OFFSET); // enter station - - v->vehstatus |= VS_TRAIN_SLOWING; - uint16 spd = max(0, (stop - x) * 20 - 15); - if (spd < v->cur_speed) v->cur_speed = spd; + if (x == stop) { + return VETSB_ENTERED_STATION | (VehicleEnterTileStatus)(station_id << VETS_STATION_ID_OFFSET); // enter station + } else if (x < stop) { + v->vehstatus |= VS_TRAIN_SLOWING; + uint16 spd = max(0, (stop - x) * 20 - 15); + if (spd < v->cur_speed) v->cur_speed = spd; + } } } else if (v->type == VEH_ROAD) { RoadVehicle *rv = RoadVehicle::From(v);