Skip to content

Commit

Permalink
Performance|libappfw: Rasterize long text content in the background
Browse files Browse the repository at this point in the history
When preparing text for drawing, rasterize longer text content in
the TextDrawable background thread. This should yield improved
performance when the text rasterization is slow.

Previously rasterization was always done in the UI thread.
  • Loading branch information
skyjake committed Aug 29, 2016
1 parent 6b50b13 commit dd73202
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 8 deletions.
14 changes: 14 additions & 0 deletions doomsday/sdk/libappfw/include/de/framework/fontlinewrapping.h
Expand Up @@ -23,6 +23,7 @@

#include <de/String>
#include <de/Font>
#include <de/Image>
#include <de/Lockable>
#include <de/shell/ILineWrapping>

Expand Down Expand Up @@ -65,6 +66,9 @@ class LIBAPPFW_PUBLIC FontLineWrapping : public Lockable, public shell::ILineWra
void wrapTextToWidth(String const &text, int maxWidth);
void wrapTextToWidth(String const &text, Font::RichFormat const &format, int maxWidth);

void rasterizeLines(Rangei const &lineRange);
void clearRasterizedLines() const;

/**
* Cancels the ongoing wrapping operation. This is useful when doing long wrapping
* operations in the background. An exception is thrown from the ongoing
Expand Down Expand Up @@ -129,6 +133,16 @@ class LIBAPPFW_PUBLIC FontLineWrapping : public Lockable, public shell::ILineWra
*/
LineInfo const &lineInfo(int index) const;

/**
* Returns a rasterized version of a segment.
* Before calling this, segments must first be rasterized by calling
* rasterizeAllSegments().
*
* @param line Line index.
* @param segment Segment on the line.
*/
Image rasterizedSegment(int line, int segment) const;

private:
DENG2_PRIVATE(d)
};
Expand Down
58 changes: 57 additions & 1 deletion doomsday/sdk/libappfw/src/fontlinewrapping.cpp
Expand Up @@ -22,6 +22,7 @@

#include "de/FontLineWrapping"
#include "de/BaseGuiApp"
#include <de/Image>

#include <QMap>

Expand Down Expand Up @@ -64,6 +65,11 @@ DENG2_PIMPL_NOREF(FontLineWrapping)
typedef QList<Line *> Lines;
Lines lines;

struct RasterizedLine {
QList<Image> segmentImages;
};
QList<RasterizedLine> rasterized;

int maxWidth;
String text; ///< Plain text.
Font::RichFormat format;
Expand All @@ -90,6 +96,7 @@ DENG2_PIMPL_NOREF(FontLineWrapping)
{
qDeleteAll(lines);
lines.clear();
rasterized.clear();
}

String rangeText(Rangei const &range) const
Expand Down Expand Up @@ -504,6 +511,12 @@ DENG2_PIMPL_NOREF(FontLineWrapping)

return lineRange.end + extraLinesProduced;
}

Image rasterizeSegment(LineInfo::Segment const &segment)
{
return font->rasterize(text .substr (segment.range),
format.subRange(segment.range));
}
};

FontLineWrapping::FontLineWrapping() : d(new Impl)
Expand Down Expand Up @@ -578,7 +591,7 @@ void FontLineWrapping::wrapTextToWidth(String const &text, Font::RichFormat cons
// When tabs are used, we must first determine the maximum width of each tab stop.
if (d->containsTabs(Rangei(0, text.size())))
{
d->indent = 0;
d->indent = 0;
d->tabStop = 0;

// Divide the content into lines by newlines.
Expand Down Expand Up @@ -750,9 +763,52 @@ Vector2i FontLineWrapping::charTopLeftInPixels(int line, int charIndex)

FontLineWrapping::LineInfo const &FontLineWrapping::lineInfo(int index) const
{
DENG2_ASSERT(line >= 0 && line < d->lines.size());
return d->lines[index]->info;
}

void FontLineWrapping::rasterizeLines(Rangei const &lineRange)
{
d->rasterized.clear();

for (int i = 0; i < height(); ++i)
{
Impl::RasterizedLine rasterLine;
if (lineRange.contains(i))
{
LineInfo const &line = lineInfo(i);
for (int k = 0; k < line.segs.size(); ++k)
{
rasterLine.segmentImages << d->rasterizeSegment(line.segs.at(k));
}
}
d->rasterized << rasterLine;
}
}

void FontLineWrapping::clearRasterizedLines() const
{
d->rasterized.clear();
}

Image FontLineWrapping::rasterizedSegment(int line, int segment) const
{
DENG2_ASSERT(line >= 0);
if (line >= 0 && line < d->rasterized.size())
{
auto const &rasterLine = d->rasterized.at(line);
if (!rasterLine.segmentImages.isEmpty())
{
DENG2_ASSERT(segment >= 0 && segment < rasterLine.segmentImages.size());
return rasterLine.segmentImages.at(segment);
}
}
// Rasterize now, since it wasn't previously rasterized.
return d->rasterizeSegment(lineInfo(line).segs.at(segment));
}

//---------------------------------------------------------------------------------------

int FontLineWrapping::LineInfo::highestTabStop() const
{
int stop = -1;
Expand Down
11 changes: 4 additions & 7 deletions doomsday/sdk/libappfw/src/gltextcomposer.cpp
Expand Up @@ -195,13 +195,8 @@ DENG2_PIMPL(GLTextComposer)
fgColor = format.style().richStyleColor(Font::RichFormat::NormalColor);
}

// Set up the background color to be transparent with no
// change of color in the alphablended smooth edges.
Vector4ub bgColor = fgColor;
bgColor.w = 0;

seg.id = atlas->alloc(font->rasterize(seg.text, format.subRange(seg.range),
fgColor, bgColor));
Image const segmentImage = wraps->rasterizedSegment(i, k);
seg.id = atlas->alloc(segmentImage.multiplied(fgColor));
}
line.segs << seg;
}
Expand All @@ -217,6 +212,8 @@ DENG2_PIMPL(GLTextComposer)
changed = true;
}

wraps->clearRasterizedLines();

DENG2_ASSERT(wraps->height() == lines.size());

return changed;
Expand Down
5 changes: 5 additions & 0 deletions doomsday/sdk/libappfw/src/textdrawable.cpp
Expand Up @@ -175,6 +175,11 @@ DENG2_PIMPL(TextDrawable)
// This is where most of the time will be spent:
_wrapper->wrapTextToWidth(_wrapper->plainText, _wrapper->format, _width);

// Pre-rasterize the first lines of the text. The assumption is that
// longer text will only be visible after scrolling, so it will be
// rasterized as needed.
_wrapper->rasterizeLines(Rangei(0, 10)); // May also take a while...

// Pass the finished wrapping to the owner.
{
DENG2_GUARD(d);
Expand Down

0 comments on commit dd73202

Please sign in to comment.