-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Fix issue #7253: port gl-js line breaking behavior #7446
Conversation
@ChrisLoer, thanks for your PR! By analyzing this pull request, we identified @ansis, @1ec5 and @jfirebaugh to be potential reviewers. |
f87626d
to
de0b349
Compare
// We're iterating from last potential break towards the first | ||
for (const PotentialBreak& potentialBreak : potentialBreaks) { | ||
const float lineWidth = breakX - potentialBreak.x; | ||
edges.push_front(LineBreakEdge{ .priorBreak = &potentialBreak, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Huh. Is this using C99 designated initializers? I was under the impression that these don't work in C++.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apparently not part of C++ but still supported with a warning by clang and gcc. I'll switch to C++-style struct initialization -- I only picked up this style from Xcode suggesting fixes to the gl-js code I had pasted into the editor.
de0b349
to
cc49840
Compare
@brunoabinader this touches the Qt-specific bidi.cpp, although it shouldn't change behavior at all. |
{ | ||
} | ||
|
||
BiDi::~BiDi() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BiDi::~BiDi() = default;
edges.push_front(LineBreakEdge{ &potentialBreak, | ||
calculateBadness(lineWidth, targetWidth, penalty, isLastBreak) + potentialBreak.badness }); | ||
} | ||
edges.push_front(LineBreakEdge{ .priorBreak = nullptr, .badness = calculateBadness(breakX, targetWidth, penalty, isLastBreak)}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace the designated initializers here.
// ...and when targetWidth and maxWidth are close, strictly enforcing maxWidth can give | ||
// more lopsided results. | ||
|
||
std::list<LineBreakEdge> edges; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this use std::vector
instead, with pushes to the back and a reversed sort order?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I switched to using a std::vector
, which for better or worse required wrapping the PotentialBreak
in a shared_ptr
so that I could maintain the singly-linked priorBreak
list.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah ok. I think it's better to avoid shared_ptr
, either by using a container that doesn't invalidate references on insert or by precalculating the number of potential breakpoints and preallocating the vector.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
std::list
is a container that doesn't invalidate references on insert, right?
} | ||
potentialBreaks.push_front(evaluateBreak(static_cast<int32_t>(logicalInput.size()), currentX, targetWidth, potentialBreaks, 0, true)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to reduce the number of static_cast
s in this code by using std::size_t
in more places?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pushed the static casts into bidi.cpp
implementations.
cc49840
to
3cf7eaa
Compare
// more lopsided results. | ||
|
||
std::vector<LineBreakEdge> edges; | ||
// We're iterating from last potential break towards the first |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this iteration order necessary or could it use for (const auto& potentialBreak : potentialBreaks)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, the iteration order was vestigial from when I originally implemented this with a sliding window... no need to maintain that now.
} | ||
edges.push_back(LineBreakEdge(nullptr, calculateBadness(breakX, targetWidth, penalty, isLastBreak))); | ||
|
||
std::sort(edges.begin(), edges.end(), [](const LineBreakEdge& a, const LineBreakEdge& b) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than keeping the entire edges
vector, wouldn't it suffice to keep a running minimum badness and prior break?
return a.badness < b.badness; | ||
}); | ||
|
||
return std::make_shared<PotentialBreak>(PotentialBreak(breakIndex, breakX, edges.front().priorBreak, edges.front().badness)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
std::make_shared<PotentialBreak>(breakIndex, breakX, ...)
} | ||
potentialBreaks.push_back(evaluateBreak(logicalInput.size(), currentX, targetWidth, potentialBreaks, 0, true)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Return the result directly rather than doing push_back
+ back
: return leastBadBreaks(*evaluateBreak(...));
6c5a0e4
to
a88357d
Compare
e98a7a7
to
be3d57f
Compare
@jfirebaugh @nickidlugash @1ec5 I've updated this PR with a commit that addresses some of the small issues that came up testing with Chinese map data (although the issues weren't specific to China). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
Don't forget to rebase test-suite and update the package.json SHA before merging.
* Don't include trailing spacing (as opposed to whitespace) in lineLength * Modify BiDi interface to require initial bidi layout and linebreaking to happen in one call. * Code style changes suggested by @kkaefer
- Put "breakable" punctuation (such as a hyphen) on the line that starts the break, not the line after the break. - Process all characters with the line breaking algorithm, even if we don't have glyphs for them. Some fonts have glyph-less breakable characters (we end up treating them similarly to a "zero-width space"). - Don't include trailing white space in raggedness calculations - Make the "favor short final lines" rule more aggressive (unlike the other changes, this one is purely an aesthetic choice)
be3d57f
to
1015209
Compare
Porting newline-handling changes from gl-js -- biggest change is the "raggedness minimizing" line breaking algorithm from mapbox/mapbox-gl-js#3743.