three-stage drawing: add a nodraw stage

Normally the two stages of drawing are
1/ compute metrics of insets/rows/paragraphs/mathrow...
2/ draw the elements and cache their positions

Now the three stages are
1/ metrics
2/ nodraw: do not draw the elements, but cache their position
3/ draw the elements (and store again their position; it does not
   seems to hurt performance).

Revive the NullPainter: this replaces the setDrawingEnabled mechanism
with a painter that does nothing. The advantage is that updatePosCache
(renamed from setPosCache) does not need anymore to be invoked from
the frontend.

updatePosCache (the nodraw stage) is called at the end of
BufferView::updateMetrics.
This commit is contained in:
Jean-Marc Lasgouttes 2017-07-16 01:25:03 +02:00
parent 48f099d93a
commit efc7359015
11 changed files with 159 additions and 71 deletions

View File

@ -70,6 +70,7 @@
#include "frontends/Application.h"
#include "frontends/Delegates.h"
#include "frontends/FontMetrics.h"
#include "frontends/NullPainter.h"
#include "frontends/Painter.h"
#include "frontends/Selection.h"
@ -2745,6 +2746,9 @@ void BufferView::updateMetrics()
d->update_strategy_ = FullScreenUpdate;
// Now update the positions of insets in the cache.
updatePosCache();
if (lyxerr.debugging(Debug::WORKAREA)) {
LYXERR(Debug::WORKAREA, "BufferView::updateMetrics");
d->coord_cache_.dump();
@ -2752,6 +2756,15 @@ void BufferView::updateMetrics()
}
void BufferView::updatePosCache()
{
// this is the "nodraw" drawing stage: only set the positions of the
// insets in metrics cache.
frontend::NullPainter np;
draw(np);
}
void BufferView::insertLyXFile(FileName const & fname)
{
LASSERT(d->cursor_.inTexted(), return);
@ -2997,12 +3010,11 @@ void BufferView::checkCursorScrollOffset(PainterInfo & pi)
* at this point.
*/
// Force the recomputation of inset positions
bool const drawing = pi.pain.isDrawingEnabled();
pi.pain.setDrawingEnabled(false);
frontend::NullPainter np;
PainterInfo(this, np);
// No need to care about vertical position.
RowPainter rp(pi, buffer().text(), row, -d->horiz_scroll_offset_, 0);
rp.paintText();
pi.pain.setDrawingEnabled(drawing);
}
// Current x position of the cursor in pixels
@ -3066,12 +3078,13 @@ void BufferView::draw(frontend::Painter & pain)
switch (d->update_strategy_) {
case NoScreenUpdate:
// If no screen painting is actually needed, only some the different
// coordinates of insets and paragraphs needs to be updated.
// no screen painting is actually needed. In nodraw stage
// however, the different coordinates of insets and paragraphs
// needs to be updated.
LYXERR(Debug::PAINTING, "Strategy: NoScreenUpdate");
pi.full_repaint = true;
pi.pain.setDrawingEnabled(false);
tm.draw(pi, 0, y);
if (pain.isNull())
tm.draw(pi, 0, y);
break;
case SingleParUpdate:

View File

@ -283,6 +283,10 @@ public:
/// update the internal \c ViewMetricsInfo.
void updateMetrics();
// this is the "nodraw" drawing stage: only set the positions of the
// insets in metrics cache.
void updatePosCache();
///
TextMetrics const & textMetrics(Text const * t) const;
TextMetrics & textMetrics(Text const * t);
@ -303,7 +307,6 @@ public:
/// get the position and height of the cursor
void cursorPosAndHeight(Point & p, int & h) const;
///
void draw(frontend::Painter & pain);

View File

@ -576,7 +576,7 @@ void RowPainter::paintText()
paintStringAndSel(e);
// Paint the spelling marks if enabled.
if (lyxrc.spellcheck_continuously && pi_.do_spellcheck && pi_.pain.isDrawingEnabled())
if (lyxrc.spellcheck_continuously && pi_.do_spellcheck && !pi_.pain.isNull())
paintMisspelledMark(e);
break;

View File

@ -1802,8 +1802,8 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
return;
size_t const nrows = pm.rows().size();
// Use fast lane when drawing is disabled.
if (!pi.pain.isDrawingEnabled()) {
// Use fast lane in nodraw stage.
if (pi.pain.isNull()) {
for (size_t i = 0; i != nrows; ++i) {
Row const & row = pm.rows()[i];
@ -1855,17 +1855,11 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
if (i)
y += row.ascent();
RowPainter rp(pi, *text_, row, row_x, y);
// It is not needed to draw on screen if we are not inside.
bool const inside = (y + row.descent() >= 0
&& y - row.ascent() < ww);
pi.pain.setDrawingEnabled(inside);
if (!inside) {
// Paint only the insets to set inset cache correctly
// FIXME: remove paintOnlyInsets when we know that positions
// have already been set.
rp.paintOnlyInsets();
// Inset positions have already been set in nodraw stage.
y += row.descent();
continue;
}
@ -1894,6 +1888,8 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
text_->getPar(pit).spellCheck();
}
RowPainter rp(pi, *text_, row, row_x, y);
// Don't paint the row if a full repaint has not been requested
// and if it has not changed.
if (!pi.full_repaint && !row_has_changed) {
@ -1924,7 +1920,7 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
<< " row_selection=" << row.selection()
<< " full_repaint=" << pi.full_repaint
<< " row_has_changed=" << row_has_changed
<< " drawingEnabled=" << pi.pain.isDrawingEnabled());
<< " null painter=" << pi.pain.isNull());
}
// Backup full_repaint status and force full repaint
@ -1952,8 +1948,6 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
// Restore full_repaint status.
pi.full_repaint = tmp;
}
// Re-enable screen drawing for future use of the painter.
pi.pain.setDrawingEnabled(true);
//LYXERR(Debug::PAINTING, ".");
}

View File

@ -17,6 +17,7 @@ liblyxfrontends_a_SOURCES = \
Delegates.h \
KeyModifier.h \
KeySymbol.h \
NullPainter.h \
Painter.h \
Clipboard.h \
Selection.h \

109
src/frontends/NullPainter.h Normal file
View File

@ -0,0 +1,109 @@
// -*- C++ -*-
/**
* \file NullPainter.h
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author unknown
* \author John Levon
* \author Jean-Marc Lasgouttes
*
* Full author contact details are available in file CREDITS.
*/
#ifndef NULLPAINTER_H
#define NULLPAINTER_H
#include "Painter.h"
namespace lyx {
namespace frontend {
/**
* NullPainter - A painter instance that does nothing
*/
class NullPainter : public Painter {
public:
NullPainter() : Painter(1) {}
~NullPainter() {}
/// draw a line from point to point
void line(int, int, int, int, Color,
line_style = line_solid, int = thin_line) {}
///
void lines(int const *, int const *, int, Color,
fill_style = fill_none, line_style = line_solid,
int = thin_line) {}
///
void path(int const *, int const *, int const *, int const *,
int const *, int const *, int, Color,
fill_style = fill_none, line_style = line_solid, int = thin_line) {}
/// draw a rectangle
void rectangle(int, int, int, int, Color,
line_style = line_solid, int = thin_line) {}
/// draw a filled rectangle
void fillRectangle(int, int, int, int, Color) {}
/// draw an arc
void arc(int, int, unsigned int, unsigned int, int, int, Color) {}
/// draw a pixel
void point(int, int, Color) {}
/// draw an image from the image cache
void image(int, int, int, int, graphics::Image const &) {}
/// draw a string
void text(int, int, docstring const &, FontInfo const &) {}
/// draw a char
void text(int, int, char_type, FontInfo const &) {}
/// draw a string
void text(int, int, docstring const &, Font const &, double, double) {}
///
void text(int, int, docstring const &, Font const &,
Color, size_type, size_type, double, double) {}
/// This painter does not paint
bool isNull() const { return true; }
/// draw the underbar, strikeout, xout, uuline and uwave font attributes
void textDecoration(FontInfo const &, int, int, int) {}
/**
* Draw a string and enclose it inside a rectangle. If
* back color is specified, the background is cleared with
* the given color. If frame is specified, a thin frame is drawn
* around the text with the given color.
*/
void rectText(int, int, docstring const &,
FontInfo const &, Color, Color) {}
/// draw a string and enclose it inside a button frame
void buttonText(int, int, docstring const &,
FontInfo const &, Color, Color, int) {}
/// draw a character of a preedit string for cjk support.
int preeditText(int, int, char_type, FontInfo const &,
preedit_style) { return 0; }
/// start monochrome painting mode, i.e. map every color into [min,max]
void enterMonochromeMode(Color const &, Color const &) {}
/// leave monochrome painting mode
void leaveMonochromeMode() {}
/// draws a wavy line that can be used for underlining.
void wavyHorizontalLine(int, int, int, ColorCode) {}
};
} // namespace frontend
} // namespace lyx
#endif // NULLPAINTER_H

View File

@ -147,11 +147,8 @@ public:
Color other, size_type from, size_type to,
double wordspacing, double textwidth) = 0;
void setDrawingEnabled(bool drawing_enabled)
{ drawing_enabled_ = drawing_enabled; }
/// Indicate wether real screen drawing shall be done or not.
bool isDrawingEnabled() const { return drawing_enabled_; }
// Returns true if the painter does not actually paint.
virtual bool isNull() const = 0;
double pixelRatio() const { return pixel_ratio_; }

View File

@ -171,9 +171,6 @@ void GuiPainter::leaveMonochromeMode()
void GuiPainter::point(int x, int y, Color col)
{
if (!isDrawingEnabled())
return;
setQPainterPen(computeColor(col));
drawPoint(x, y);
}
@ -184,9 +181,6 @@ void GuiPainter::line(int x1, int y1, int x2, int y2,
line_style ls,
int lw)
{
if (!isDrawingEnabled())
return;
setQPainterPen(computeColor(col), ls, lw);
bool const do_antialiasing = renderHints() & TextAntialiasing
&& x1 != x2 && y1 != y2 && ls != line_solid_aliased;
@ -202,9 +196,6 @@ void GuiPainter::lines(int const * xp, int const * yp, int np,
line_style ls,
int lw)
{
if (!isDrawingEnabled())
return;
// double the size if needed
// FIXME THREAD
static QVector<QPoint> points(32);
@ -247,9 +238,6 @@ void GuiPainter::path(int const * xp, int const * yp,
line_style ls,
int lw)
{
if (!isDrawingEnabled())
return;
QPainterPath bpath;
// This is the starting point, so its control points are meaningless
bpath.moveTo(xp[0], yp[0]);
@ -278,9 +266,6 @@ void GuiPainter::rectangle(int x, int y, int w, int h,
line_style ls,
int lw)
{
if (!isDrawingEnabled())
return;
setQPainterPen(computeColor(col), ls, lw);
drawRect(x, y, w, h);
}
@ -288,9 +273,6 @@ void GuiPainter::rectangle(int x, int y, int w, int h,
void GuiPainter::fillRectangle(int x, int y, int w, int h, Color col)
{
if (!isDrawingEnabled())
return;
fillRect(x, y, w, h, guiApp->colorCache().get(col));
}
@ -298,9 +280,6 @@ void GuiPainter::fillRectangle(int x, int y, int w, int h, Color col)
void GuiPainter::arc(int x, int y, unsigned int w, unsigned int h,
int a1, int a2, Color col)
{
if (!isDrawingEnabled())
return;
// LyX usings 1/64ths degree, Qt usings 1/16th
setQPainterPen(computeColor(col));
bool const do_antialiasing = renderHints() & TextAntialiasing;
@ -317,9 +296,6 @@ void GuiPainter::image(int x, int y, int w, int h, graphics::Image const & i)
fillRectangle(x, y, w, h, Color_graphicsbg);
if (!isDrawingEnabled())
return;
QImage const image = qlimage.image();
QRectF const drect = QRectF(x, y, w, h);
QRectF const srect = QRectF(0, 0, image.width(), image.height());
@ -391,7 +367,7 @@ void GuiPainter::text(int x, int y, docstring const & s,
double const wordspacing, double const tw)
{
//LYXERR0("text: x=" << x << ", s=" << s);
if (s.empty() || !isDrawingEnabled())
if (s.empty())
return;
/* Caution: The following ucs4 to QString conversions work for symbol fonts

View File

@ -37,6 +37,9 @@ public:
GuiPainter(QPaintDevice *, double pixel_ratio);
virtual ~GuiPainter();
/// This painter paints
virtual bool isNull() const { return false; }
/// draw a line from point to point
virtual void line(
int x1, int y1,

View File

@ -16,6 +16,11 @@
#include "ColorCache.h"
#include "FontLoader.h"
#include "GuiApplication.h"
#include "GuiCompleter.h"
#include "GuiKeySymbol.h"
#include "GuiPainter.h"
#include "GuiView.h"
#include "Menus.h"
#include "Buffer.h"
@ -26,11 +31,6 @@
#include "Cursor.h"
#include "Font.h"
#include "FuncRequest.h"
#include "GuiApplication.h"
#include "GuiCompleter.h"
#include "GuiKeySymbol.h"
#include "GuiPainter.h"
#include "GuiView.h"
#include "KeySymbol.h"
#include "Language.h"
#include "LyX.h"
@ -1271,9 +1271,8 @@ void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e)
return;
}
GuiPainter pain(d->screen_, pixelRatio());
d->buffer_view_->updateMetrics();
d->buffer_view_->draw(pain);
d->updateScreen();
// FIXME: shall we use real_current_font here? (see #10478)
FontInfo font = d->buffer_view_->cursor().getFont().fontInfo();
FontMetrics const & fm = theFontMetrics(font);
@ -1365,6 +1364,7 @@ void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e)
ps = Painter::preedit_cursor;
// draw one character and update cur_x.
GuiPainter pain(d->screen_, pixelRatio());
cur_x += pain.preeditText(cur_x, cur_y, typed_char, font, ps);
}

View File

@ -231,25 +231,17 @@ void InsetDisplayLabelBox::metrics(MetricsInfo & mi, Dimension & dim) const
{
InsetLabelBox::metrics(mi, dim);
if (!parent_.editing(mi.base.bv)
&& parent_.cell(parent_.displayIdx()).empty()) {
dim.wid = 0;
dim.asc = 0;
dim.des = 0;
}
&& parent_.cell(parent_.displayIdx()).empty())
dim.clear();
}
void InsetDisplayLabelBox::draw(PainterInfo & pi, int x, int y) const
{
if (parent_.editing(pi.base.bv)
|| !parent_.cell(parent_.displayIdx()).empty()) {
InsetLabelBox::draw(pi, x, y);
} else {
bool enabled = pi.pain.isDrawingEnabled();
pi.pain.setDrawingEnabled(false);
InsetLabelBox::draw(pi, x, y);
pi.pain.setDrawingEnabled(enabled);
}
|| !parent_.cell(parent_.displayIdx()).empty()
|| pi.pain.isNull())
InsetLabelBox::draw(pi, x, y);
}