From efc735901523752a26440b2d3da390d1c787bc56 Mon Sep 17 00:00:00 2001 From: Jean-Marc Lasgouttes Date: Sun, 16 Jul 2017 01:25:03 +0200 Subject: [PATCH] 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. --- src/BufferView.cpp | 27 +++++-- src/BufferView.h | 5 +- src/RowPainter.cpp | 2 +- src/TextMetrics.cpp | 18 ++--- src/frontends/Makefile.am | 1 + src/frontends/NullPainter.h | 109 ++++++++++++++++++++++++++ src/frontends/Painter.h | 7 +- src/frontends/qt4/GuiPainter.cpp | 26 +----- src/frontends/qt4/GuiPainter.h | 3 + src/frontends/qt4/GuiWorkArea.cpp | 14 ++-- src/mathed/InsetMathMacroTemplate.cpp | 18 ++--- 11 files changed, 159 insertions(+), 71 deletions(-) create mode 100644 src/frontends/NullPainter.h diff --git a/src/BufferView.cpp b/src/BufferView.cpp index d2215627cf..00de06080a 100644 --- a/src/BufferView.cpp +++ b/src/BufferView.cpp @@ -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: diff --git a/src/BufferView.h b/src/BufferView.h index 92e8987350..83b4d39e37 100644 --- a/src/BufferView.h +++ b/src/BufferView.h @@ -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); diff --git a/src/RowPainter.cpp b/src/RowPainter.cpp index 2d7e12c445..2dc76664a9 100644 --- a/src/RowPainter.cpp +++ b/src/RowPainter.cpp @@ -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; diff --git a/src/TextMetrics.cpp b/src/TextMetrics.cpp index f94accae67..c5784070d2 100644 --- a/src/TextMetrics.cpp +++ b/src/TextMetrics.cpp @@ -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, "."); } diff --git a/src/frontends/Makefile.am b/src/frontends/Makefile.am index 862613a67f..22b7508a7a 100644 --- a/src/frontends/Makefile.am +++ b/src/frontends/Makefile.am @@ -17,6 +17,7 @@ liblyxfrontends_a_SOURCES = \ Delegates.h \ KeyModifier.h \ KeySymbol.h \ + NullPainter.h \ Painter.h \ Clipboard.h \ Selection.h \ diff --git a/src/frontends/NullPainter.h b/src/frontends/NullPainter.h new file mode 100644 index 0000000000..19ac8b66a2 --- /dev/null +++ b/src/frontends/NullPainter.h @@ -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 diff --git a/src/frontends/Painter.h b/src/frontends/Painter.h index d03ebf40e8..79c907d23f 100644 --- a/src/frontends/Painter.h +++ b/src/frontends/Painter.h @@ -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_; } diff --git a/src/frontends/qt4/GuiPainter.cpp b/src/frontends/qt4/GuiPainter.cpp index a3cb089e78..56e9cc4aec 100644 --- a/src/frontends/qt4/GuiPainter.cpp +++ b/src/frontends/qt4/GuiPainter.cpp @@ -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 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 diff --git a/src/frontends/qt4/GuiPainter.h b/src/frontends/qt4/GuiPainter.h index 9657ed9770..7513965ea6 100644 --- a/src/frontends/qt4/GuiPainter.h +++ b/src/frontends/qt4/GuiPainter.h @@ -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, diff --git a/src/frontends/qt4/GuiWorkArea.cpp b/src/frontends/qt4/GuiWorkArea.cpp index fe7082da64..1218061741 100644 --- a/src/frontends/qt4/GuiWorkArea.cpp +++ b/src/frontends/qt4/GuiWorkArea.cpp @@ -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); } diff --git a/src/mathed/InsetMathMacroTemplate.cpp b/src/mathed/InsetMathMacroTemplate.cpp index 17fc2d04a2..a2fc064c00 100644 --- a/src/mathed/InsetMathMacroTemplate.cpp +++ b/src/mathed/InsetMathMacroTemplate.cpp @@ -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); }