1
0
Fork 0

(svn r26021) [1.3] -Backport from trunk:

- Fix: Crash when the ICU layouter thinks a font is corrupted [FS#5711] (r26018, r26017, r26016, r26015)
- Fix: Having trains miss a platform that is just being modified is less of a problem than having trains stop twice without moving [FS#5684] (r26013)
- Fix: --help text of ./configure for packages that require pkg-config (r26011)
- Fix: The AI/GS library name to use in Import, is not the name given by GetName but GetInstanceName [FS#5662] (r26010)
release/1.3
rubidium 2013-11-17 11:29:44 +00:00
parent 5a82846fb0
commit 9fdc7a4ec9
8 changed files with 352 additions and 186 deletions

View File

@ -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"

View File

@ -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).

View File

@ -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;

View File

@ -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<ParagraphLayouter::Line *, 4>, 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<ICUVisualRun *, 4>, 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<FallbackVisualRun *, 4>, 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<GlyphID>(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 <typename T>
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<typename T::CharType>(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<const char **>(&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<const char **>(&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<ICUParagraphLayout>(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<FallbackParagraphLayout>(line, str, state);
}
#else
GetLayouter<FallbackParagraphLayout>(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<uint>(d.width, (*l)->getWidth());
d.height += (*l)->getLeading();
for (const ParagraphLayouter::Line **l = this->Begin(); l != this->End(); l++) {
d.width = max<uint>(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;
}
}

View File

@ -97,84 +97,47 @@ public:
/** Mapping from index to font. */
typedef SmallMap<int, Font *> 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<VisualRun *, 4> {
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<ParagraphLayout::Line *, 4> {
#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 ParagraphLayouter::Line *, 4> {
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<ParagraphLayout::Line *, 4> {
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<LineCacheKey, LineCacheItem> LineCache;
static LineCache *linecache;
@ -208,9 +173,9 @@ class Layouter : public AutoDeleteSmallVector<ParagraphLayout::Line *, 4> {
typedef SmallMap<TextColour, Font *> 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;

View File

@ -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.

View File

@ -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.

View File

@ -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);