diff --git a/data/magic-new.mse-style/style b/data/magic-new.mse-style/style index bed33e5e..c5a94e9a 100644 --- a/data/magic-new.mse-style/style +++ b/data/magic-new.mse-style/style @@ -262,6 +262,8 @@ card style: line height hard: 1.2 line height line: 1.5 line height soft: 0.9 + line height hard max: 1.3 + line height line max: 1.6 watermark: left: 117 diff --git a/data/magic.mse-game/game b/data/magic.mse-game/game index 95e1c04d..b38fd305 100644 --- a/data/magic.mse-game/game +++ b/data/magic.mse-game/game @@ -103,13 +103,15 @@ init script: # Look for a CDA that defines colors text_to_color := { - text := filter_text(match: card_name+"()? is (colorless|all colors|((blue|white|green|red|black)((,|,? and) (blue|white|green|red|black))*))\\.") - if text == "" then "" - else if contains(text, match: "all colors") then ( + # Note: running filter_text is quite slow, do a quick 'contains' check first + if contains(match: card_name) then ( + text := filter_text(match: card_name+"()? is (colorless|all colors|((blue|white|green|red|black)((,|,? and) (blue|white|green|red|black))*))\\.") + if text != "" then ( + if contains(text, match: "all colors") then ( colors := "WUBRG" if land = "land" then land_multicolor() else mana_to_color(hybrid: "") - ) else ( + ) else ( colors := "" if contains(text, match: "white") then colors := colors + "W" if contains(text, match: "blue") then colors := colors + "U" @@ -118,6 +120,8 @@ init script: if contains(text, match: "green") then colors := colors + "G" if land = "land" then land_multicolor() else mana_to_color(hybrid: "") + ) + ) ) } @@ -129,7 +133,7 @@ init script: is_spell := match_rule(match: "(?i)Instant|Sorcery") card_color := { # usually the color of mana - text_color := text_to_color(rules_text, land: is_land(type), card_name: card_name); + text_color := text_to_color(rules_text, land: is_land(type)); if text_color == "" then ( mana_color := mana_to_color(colors: color_filter(casting_cost), hybrid: color_filterH(casting_cost)) if mana_color == "colorless" and is_land (type) then land_to_color(watermark) diff --git a/src/data/field/text.cpp b/src/data/field/text.cpp index df70d3f4..52c089b6 100644 --- a/src/data/field/text.cpp +++ b/src/data/field/text.cpp @@ -52,6 +52,9 @@ TextStyle::TextStyle(const TextFieldP& field) , line_height_soft(1.0) , line_height_hard(1.0) , line_height_line(1.0) + , line_height_soft_max(0.0) + , line_height_hard_max(0.0) + , line_height_line_max(0.0) , direction(LEFT_TO_RIGHT) {} @@ -108,6 +111,9 @@ IMPLEMENT_REFLECTION(TextStyle) { REFLECT(line_height_soft); REFLECT(line_height_hard); REFLECT(line_height_line); + REFLECT(line_height_soft_max); + REFLECT(line_height_hard_max); + REFLECT(line_height_line_max); REFLECT_N("mask", mask_filename); REFLECT(direction); reflect_content(tag, *this); diff --git a/src/data/field/text.hpp b/src/data/field/text.hpp index c2a41a33..0eb5cb6a 100644 --- a/src/data/field/text.hpp +++ b/src/data/field/text.hpp @@ -65,6 +65,9 @@ class TextStyle : public Style { double line_height_soft; ///< Line height for soft linebreaks double line_height_hard; ///< Line height for hard linebreaks double line_height_line; ///< Line height for tags + double line_height_soft_max; ///< Maximum line height + double line_height_hard_max; ///< Maximum line height + double line_height_line_max; ///< Maximum line height String mask_filename; ///< Filename of the mask ContourMask mask; ///< Mask to fit the text to (may be null) Direction direction; ///< In what direction is text layed out? diff --git a/src/render/text/viewer.cpp b/src/render/text/viewer.cpp index 180bf993..4b4d1994 100644 --- a/src/render/text/viewer.cpp +++ b/src/render/text/viewer.cpp @@ -19,10 +19,11 @@ struct TextViewer::Line { vector positions; ///< x position of each character in this line, gives the number of characters + 1, never empty double top; ///< y position of (the top of) this line double line_height; ///< The height of this line in pixels - bool separator_after; ///< Is there a saparator after this line? + LineBreak break_after; ///< Is there a saparator after this line? +//% Alignment alignment; ///< Alignment of this line Line() - : start(0), top(0), line_height(0), separator_after(false) + : start(0), top(0), line_height(0), break_after(BREAK_NO) {} /// The position (just beyond) the bottom of this line @@ -136,7 +137,7 @@ void TextViewer::drawSeparators(RotatedDC& dc) { y = (y + l.top) / 2; dc.DrawLine(RealPoint(0, y), RealPoint(dc.getInternalRect().width, y)); } - separator = l.separator_after; + separator = l.break_after == BREAK_LINE; y = y2; } // separator at the end? @@ -376,14 +377,18 @@ void TextViewer::prepareLines(RotatedDC& dc, const String& text, TextStyle& styl // bound on max_scale, given that scale fits and produces the given lines inline double bound_on_max_scale(RotatedDC& dc, const TextStyle& style, const vector& lines, double scale) { + if (lines.empty()) return 1.0; double tot_height = dc.getInternalSize().height + 1; double height = min(tot_height, lines.back().bottom() + style.padding_bottom); + if (height < 1) return 1.0; return scale * tot_height / height; } // bound on min_scale, given that scale doesn't fit and produces the given lines inline double bound_on_min_scale(RotatedDC& dc, const TextStyle& style, const vector& lines, double scale) { + if (lines.empty()) return 0.0; double tot_height = dc.getInternalSize().height; double height = lines.back().bottom() + style.padding_bottom; + if (height < 1) return 0.0; return scale * tot_height / height; } @@ -511,23 +516,13 @@ bool TextViewer::prepareLinesScale(RotatedDC& dc, const vector& chars, for(size_t i = 0 ; i < chars.size() ; ++i) { const CharInfo& c = chars[i]; // Should we break? - bool break_now = false; - bool accept_word = false; // the current word should be added to the line - bool hide_breaker = true; // hide the \n or _(' ') that caused a line break - double line_height_multiplier = 1; // multiplier for line height for next line top - if (c.break_after == BREAK_SOFT) { + bool break_now = false; + bool accept_word = false; // the current word should be added to the line + bool hide_breaker = true; // hide the \n or _(' ') that caused a line break + if (c.break_after == BREAK_SOFT || c.break_after == BREAK_HARD || c.break_after == BREAK_LINE) { break_now = true; accept_word = true; - line_height_multiplier = style.line_height_soft; - } else if (c.break_after == BREAK_HARD) { - break_now = true; - accept_word = true; - line_height_multiplier = style.line_height_hard; - } else if (c.break_after == BREAK_LINE) { - line.separator_after = true; - break_now = true; - accept_word = true; - line_height_multiplier = style.line_height_line; + line.break_after = c.break_after; } else if (c.break_after == BREAK_SPACE && style.field().multi_line) { // Soft break == end of word accept_word = true; @@ -535,7 +530,7 @@ bool TextViewer::prepareLinesScale(RotatedDC& dc, const vector& chars, break_now = true; accept_word = true; hide_breaker = false; - line_height_multiplier = style.line_height_soft; + line.break_after = BREAK_SOFT; } // Add size of the character if (c.break_after != BREAK_LINE) { @@ -556,12 +551,12 @@ bool TextViewer::prepareLinesScale(RotatedDC& dc, const vector& chars, break_now = true; accept_word = true; hide_breaker = false; - line_height_multiplier = style.line_height_soft; + line.break_after = BREAK_SOFT; } } else if (line_size.width + word_size.width > max_width) { // line would become too long, break before the current word break_now = true; - line_height_multiplier = style.line_height_soft; + line.break_after = BREAK_SOFT; } } // Ending the current word @@ -595,11 +590,14 @@ bool TextViewer::prepareLinesScale(RotatedDC& dc, const vector& chars, // push lines.push_back(line); // reset line object for next line + double line_height_multiplier = line.break_after == BREAK_HARD ? style.line_height_hard + : line.break_after == BREAK_LINE ? style.line_height_line + : style.line_height_soft; line.top += line.line_height * line_height_multiplier; line.start = word_start; line.positions.clear(); - if (line.separator_after) line.line_height = 0; - line.separator_after = false; + if (line.break_after == BREAK_LINE) line.line_height = 0; + line.break_after = BREAK_NO; // reset line_size line_size = RealSize(lineLeft(dc, style, line.top), 0); while (line.top < style.height && line_size.width + 1 >= style.width - style.padding_right) { @@ -636,7 +634,6 @@ double TextViewer::lineRight(RotatedDC& dc, const TextStyle& style, double y) co void TextViewer::alignLines(RotatedDC& dc, const vector& chars, const TextStyle& style) { - if (style.alignment == ALIGN_TOP_LEFT) return; // Find height of the text, don't count the last lines if they are empty double height = 0; FOR_EACH_REVERSE(l, lines) { @@ -647,6 +644,45 @@ void TextViewer::alignLines(RotatedDC& dc, const vector& chars, const RealSize s = add_diagonal( dc.getInternalSize(), -RealSize(style.padding_left+style.padding_right, style.padding_top + style.padding_bottom)); + + // stretch lines by increasing the space between them + if (height < s.height) { + double d_soft = max(0.0, style.line_height_soft_max - style.line_height_soft); + double d_hard = max(0.0, style.line_height_hard_max - style.line_height_hard); + double d_line = max(0.0, style.line_height_line_max - style.line_height_line); + double stops[] = {0.0, d_soft, d_hard, d_line}; + sort(stops + 1, stops + 4); + for (int i = 1 ; i < 4 && height < s.height ; ++i) { + double stop = stops[i] - stops[i-1]; + if (stop <= 0) continue; + // which types can use this stop? + bool soft = d_soft >= stop; + bool hard = d_hard >= stop; + bool line = d_line >= stop; + // sum of the line height we can apply this to? + double sum = 0; + FOR_EACH(l, lines) { + if ((soft && l.break_after == BREAK_SOFT) + || (hard && l.break_after == BREAK_HARD) + || (line && l.break_after == BREAK_LINE)) sum += l.line_height; + } + if (sum == 0) break; + // how much do we need to add? + double to_add = min(stop, (s.height - height) / sum); + // apply + double add = 0; + FOR_EACH(l, lines) { + l.top += add; + // adjust next line by.. + if ((soft && l.break_after == BREAK_SOFT) + || (hard && l.break_after == BREAK_HARD) + || (line && l.break_after == BREAK_LINE)) add += to_add * l.line_height; + } + height += add; + } + } + if (style.alignment == ALIGN_TOP_LEFT) return; + // align double vdelta = align_delta_y(style.alignment, s.height, height); // align all lines FOR_EACH(l, lines) { diff --git a/src/util/io/package.cpp b/src/util/io/package.cpp index 3c971f08..1b85fe4b 100644 --- a/src/util/io/package.cpp +++ b/src/util/io/package.cpp @@ -155,6 +155,26 @@ class ZipFileInputStream : private wxFileInputStream, public wxZipInputStream { }; #endif +class BufferedFileInputStream_aux { + protected: + wxFileInputStream file_stream; + inline BufferedFileInputStream_aux(const String& filename) + : file_stream(filename) + {} +}; +/// A buffered version of wxFileInputStream +/** 2007-08-24: + * According to profiling this gives a significant speedup + * Bringing the avarage run time of read_utf8_line from 186k to 54k (in cpu time units) + */ +class BufferedFileInputStream : private BufferedFileInputStream_aux, public wxBufferedInputStream { + public: + inline BufferedFileInputStream(const String& filename) + : BufferedFileInputStream_aux(filename) + , wxBufferedInputStream(file_stream) + {} +}; + InputStreamP Package::openIn(const String& file) { if (!file.empty() && file.GetChar(0) == _('/')) { // absolute path, open file from another package @@ -167,10 +187,10 @@ InputStreamP Package::openIn(const String& file) { InputStreamP stream; if (it->second.wasWritten()) { // written to this file, open the temp file - stream = new_shared1(it->second.tempName); + stream = new_shared1(it->second.tempName); } else if (wxFileExists(filename+_("/")+file)) { // a file in directory package - stream = new_shared1(filename+_("/")+file); + stream = new_shared1(filename+_("/")+file); } else if (wxFileExists(filename) && it->second.zipEntry) { // a file in a zip archive // somebody in wx thought seeking was no longer needed, it now only works with the 'compatability constructor'