Commit fb42f335 authored by twanvl's avatar twanvl

Attemp at scaling the spacing between lines to better fill the text box;

Did some profiling, conclusions:
 - we want to buffer our input streams, apperantly wx doesn't do this automatically
 - compiling regexes is SLOW.
   This is not just in the numbers, but it is actually noticable! The textbox used to be quite unresponsive.
   I wrapped the call to filter_text in the game file with a quick contains() call, so usually, the regex doesn't fire.
   It would be nicer if this was somehow automatic, but that will not be easy.
parent 80b83a8c
......@@ -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
......
......@@ -103,13 +103,15 @@ init script:
# Look for a CDA that defines colors
text_to_color := {
text := filter_text(match: card_name+"(</[-a-z]+>)? 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+"(</[-a-z]+>)? 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)
......
......@@ -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);
......
......@@ -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 <line> 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?
......
......@@ -19,10 +19,11 @@ struct TextViewer::Line {
vector<double> 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<TextViewer::Line>& 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<TextViewer::Line>& 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<CharInfo>& 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<CharInfo>& 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<CharInfo>& 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<CharInfo>& 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<CharInfo>& 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<CharInfo>& 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) {
......
......@@ -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<wxFileInputStream>(it->second.tempName);
stream = new_shared1<BufferedFileInputStream>(it->second.tempName);
} else if (wxFileExists(filename+_("/")+file)) {
// a file in directory package
stream = new_shared1<wxFileInputStream>(filename+_("/")+file);
stream = new_shared1<BufferedFileInputStream>(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'
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment