Mercurial > hg > svgui
changeset 1361:2e3b3fadba27
Merge
author | Chris Cannam |
---|---|
date | Fri, 12 Oct 2018 11:17:29 +0100 |
parents | e7da9c9635ba (diff) e848ea0850fe (current diff) |
children | d79e21855aef |
files | |
diffstat | 6 files changed, 354 insertions(+), 380 deletions(-) [+] |
line wrap: on
line diff
--- a/layer/TimeRulerLayer.cpp Fri Oct 05 10:25:52 2018 +0100 +++ b/layer/TimeRulerLayer.cpp Fri Oct 12 11:17:29 2018 +0100 @@ -154,8 +154,12 @@ sv_frame_t startFrame = v->getStartFrame(); sv_frame_t endFrame = v->getEndFrame(); + if (endFrame == startFrame) { + endFrame = startFrame + 1; + } - int minPixelSpacing = ViewManager::scalePixelSize(50); + int exampleWidth = QFontMetrics(QFont()).width("10:42.987654"); + int minPixelSpacing = v->getXForViewX(exampleWidth); RealTime rtStart = RealTime::frame2RealTime(startFrame, sampleRate); RealTime rtEnd = RealTime::frame2RealTime(endFrame, sampleRate); @@ -164,6 +168,15 @@ if (count < 1) count = 1; RealTime rtGap = (rtEnd - rtStart) / count; +#ifdef DEBUG_TIME_RULER_LAYER + SVCERR << "zoomLevel = " << v->getZoomLevel() + << ", startFrame = " << startFrame << ", endFrame = " << endFrame + << ", rtStart = " << rtStart << ", rtEnd = " << rtEnd + << ", paint width = " << v->getPaintWidth() + << ", minPixelSpacing = " << minPixelSpacing + << ", count = " << count << ", rtGap = " << rtGap << endl; +#endif + int64_t incus; quarterTicks = false; @@ -197,6 +210,10 @@ if (us > 0) { incus *= 2; us /= 2; } } +#ifdef DEBUG_TIME_RULER_LAYER + SVCERR << "getMajorTickUSec: returning incus = " << incus << endl; +#endif + return incus; } @@ -277,7 +294,7 @@ // We always use the exact incus in our calculations for where to // draw the actual ticks or lines. - int minPixelSpacing = 50; + int minPixelSpacing = v->getXForViewX(50); sv_frame_t incFrame = lrint((double(incus) * sampleRate) / 1000000); int incX = int(round(v->getZoomLevel().framesToPixels(double(incFrame)))); int ticks = 10;
--- a/view/Pane.cpp Fri Oct 05 10:25:52 2018 +0100 +++ b/view/Pane.cpp Fri Oct 12 11:17:29 2018 +0100 @@ -110,23 +110,6 @@ if (!isVisible()) return; -/* - int count = 0; - int currentLevel = 1; - int level = 1; - while (true) { - if (getZoomLevel() == level) currentLevel = count; - int newLevel = getZoomConstraintBlockSize(level + 1, - ZoomConstraint::RoundUp); - if (newLevel == level) break; - if (newLevel == 131072) break; //!!! just because - level = newLevel; - ++count; - } - - cerr << "Have " << count+1 << " zoom levels" << endl; -*/ - Layer *layer = 0; if (getLayerCount() > 0) layer = getLayer(getLayerCount() - 1); @@ -199,68 +182,17 @@ connect(m_reset, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget())); } - int count = 0; - int current = 0; - ZoomLevel level; - - //!!! pull out into function (presumably in View) - bool haveConstraint = false; - for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); - ++i) { - if ((*i)->getZoomConstraint() && !(*i)->supportsOtherZoomLevels()) { - haveConstraint = true; - break; - } - } - - SVCERR << "haveConstraint = " << haveConstraint << endl; - - if (haveConstraint) { - while (true) { - //!!! this won't terminate if level is in the PixelsPerFrame zone - if (getZoomLevel() == level) current = count; - ZoomLevel newLevel = getZoomConstraintLevel(level.incremented(), - ZoomConstraint::RoundUp); - SVCERR << "newLevel = " << newLevel << endl; - if (newLevel == level) break; - level = newLevel; - if (++count == 50) break; - } - } else { - // if we have no particular constraints, we can really spread out - //!!! this is nonsense in PixelsPerFrame zone - while (true) { - using namespace std::rel_ops; - if (getZoomLevel() >= level) current = count; - int step = level.level / 10; - int pwr = 0; - while (step > 0) { - ++pwr; - step /= 2; - } - step = 1; - while (pwr > 0) { - step *= 2; - --pwr; - } - cerr << level.level << ", step " << step << endl; - level.level += step; - if (++count == 100 || level.level > 262144) break; - } - } - - //!!! - SVCERR << "Have " << count << " zoom levels" << endl; - - m_hthumb->setMinimumValue(0); + int count = countZoomLevels(); + int current = getZoomLevelIndex(getZoomLevel()); + + m_hthumb->setMinimumValue(1); m_hthumb->setMaximumValue(count); m_hthumb->setValue(count - current); -// cerr << "set value to " << count-current << endl; - +// cerr << "set value to " << count - 1 - current << endl; // cerr << "default value is " << m_hthumb->getDefaultValue() << endl; - if (count != 50 && m_hthumb->getDefaultValue() == 0) { + if (m_hthumb->getDefaultValue() == 0) { m_hthumb->setDefaultValue(count - current); // cerr << "set default value to " << m_hthumb->getDefaultValue() << endl; } @@ -2441,53 +2373,7 @@ void Pane::horizontalThumbwheelMoved(int value) { - //!!! dupe with updateHeadsUpDisplay - - int count = 0; - ZoomLevel level; - - //!!! pull out into function (presumably in View) - bool haveConstraint = false; - for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); - ++i) { - if ((*i)->getZoomConstraint() && !(*i)->supportsOtherZoomLevels()) { - haveConstraint = true; - break; - } - } - - if (haveConstraint) { - while (true) { - //!!! this won't terminate if level is in the PixelsPerFrame zone - if (m_hthumb->getMaximumValue() - value == count) break; - ZoomLevel newLevel = getZoomConstraintLevel(level.incremented(), - ZoomConstraint::RoundUp); - if (newLevel == level) break; - level = newLevel; - if (++count == 50) break; - } - } else { - //!!! this is nonsense in PixelsPerFrame zone - while (true) { - if (m_hthumb->getMaximumValue() - value == count) break; - int step = level.level / 10; - int pwr = 0; - while (step > 0) { - ++pwr; - step /= 2; - } - step = 1; - while (pwr > 0) { - step *= 2; - --pwr; - } -// cerr << level << endl; - level.level += step; - if (++count == 100 || level.level > 262144) break; - } - } - -// cerr << "new level is " << level << endl; + ZoomLevel level = getZoomLevelByIndex(m_hthumb->getMaximumValue() - value); setZoomLevel(level); }
--- a/view/Pane.h Fri Oct 05 10:25:52 2018 +0100 +++ b/view/Pane.h Fri Oct 12 11:17:29 2018 +0100 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _PANE_H_ -#define _PANE_H_ +#ifndef SV_PANE_H +#define SV_PANE_H #include <QFrame> #include <QPoint> @@ -212,7 +212,7 @@ bool m_playbackFrameMoveScheduled; sv_frame_t m_playbackFrameMoveTo; - + static QCursor *m_measureCursor1; static QCursor *m_measureCursor2; };
--- a/view/View.cpp Fri Oct 05 10:25:52 2018 +0100 +++ b/view/View.cpp Fri Oct 12 11:17:29 2018 +0100 @@ -20,13 +20,14 @@ #include "base/Profiler.h" #include "base/Pitch.h" #include "base/Preferences.h" +#include "base/HitCount.h" #include "ViewProxy.h" #include "layer/TimeRulerLayer.h" #include "layer/SingleColourLayer.h" #include "layer/PaintAssistant.h" -#include "data/model/PowerOfSqrtTwoZoomConstraint.h" +#include "data/model/RelativelyFineZoomConstraint.h" #include "data/model/RangeSummarisableTimeValueModel.h" #include "widgets/IconLoader.h" @@ -63,6 +64,7 @@ m_showProgress(showProgress), m_cache(0), m_buffer(0), + m_cacheValid(false), m_cacheCentreFrame(0), m_cacheZoomLevel(ZoomLevel::FramesPerPixel, 1024), m_selectionCached(false), @@ -260,8 +262,7 @@ return; } - delete m_cache; - m_cache = 0; + m_cacheValid = false; Layer *selectedLayer = 0; @@ -293,8 +294,7 @@ void View::overlayModeChanged() { - delete m_cache; - m_cache = 0; + m_cacheValid = false; update(); } @@ -617,8 +617,7 @@ void View::addLayer(Layer *layer) { - delete m_cache; - m_cache = 0; + m_cacheValid = false; SingleColourLayer *scl = dynamic_cast<SingleColourLayer *>(layer); if (scl) scl->setDefaultColourFor(this); @@ -689,8 +688,7 @@ return; } - delete m_cache; - m_cache = 0; + m_cacheValid = false; for (LayerList::iterator i = m_fixedOrderLayers.begin(); i != m_fixedOrderLayers.end(); @@ -907,8 +905,7 @@ } if (recreate) { - delete m_cache; - m_cache = 0; + m_cacheValid = false; } emit layerModelChanged(); @@ -955,8 +952,7 @@ } if (recreate) { - delete m_cache; - m_cache = 0; + m_cacheValid = false; } if (startFrame < myStartFrame) startFrame = myStartFrame; @@ -991,9 +987,7 @@ #ifdef DEBUG_VIEW_WIDGET_PAINT cerr << "View(" << this << ")::modelReplaced()" << endl; #endif - delete m_cache; - m_cache = 0; - + m_cacheValid = false; update(); } @@ -1006,8 +1000,7 @@ SVDEBUG << "View::layerParametersChanged()" << endl; #endif - delete m_cache; - m_cache = 0; + m_cacheValid = false; update(); if (layer) { @@ -1203,8 +1196,7 @@ View::selectionChanged() { if (m_selectionCached) { - delete m_cache; - m_cache = 0; + m_cacheValid = false; m_selectionCached = false; } update(); @@ -1469,32 +1461,98 @@ { using namespace std::rel_ops; - ZoomLevel candidate = zoomLevel; - bool haveCandidate = false; - - PowerOfSqrtTwoZoomConstraint defaultZoomConstraint; - - for (auto i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { - - const ZoomConstraint *zoomConstraint = (*i)->getZoomConstraint(); - if (!zoomConstraint) zoomConstraint = &defaultZoomConstraint; - + ZoomLevel candidate = + RelativelyFineZoomConstraint().getNearestZoomLevel(zoomLevel, dir); + + for (auto i : m_layerStack) { + + if (i->supportsOtherZoomLevels() || !(i->getZoomConstraint())) { + continue; + } + ZoomLevel thisLevel = - zoomConstraint->getNearestZoomLevel(zoomLevel, dir); + i->getZoomConstraint()->getNearestZoomLevel(zoomLevel, dir); // Go for the block size that's furthest from the one // passed in. Most of the time, that's what we want. - if (!haveCandidate || - (thisLevel > zoomLevel && thisLevel > candidate) || + if ((thisLevel > zoomLevel && thisLevel > candidate) || (thisLevel < zoomLevel && thisLevel < candidate)) { candidate = thisLevel; - haveCandidate = true; } } return candidate; } +int +View::countZoomLevels() const +{ + int n = 0; + ZoomLevel min = ZoomConstraint().getMinZoomLevel(); + ZoomLevel max = ZoomConstraint().getMaxZoomLevel(); + ZoomLevel level = min; + while (true) { + ++n; + if (level == max) { + break; + } + level = getZoomConstraintLevel + (level.incremented(), ZoomConstraint::RoundUp); + } +// cerr << "View::countZoomLevels: " << n << endl; + return n; +} + +ZoomLevel +View::getZoomLevelByIndex(int ix) const +{ + int n = 0; + ZoomLevel min = ZoomConstraint().getMinZoomLevel(); + ZoomLevel max = ZoomConstraint().getMaxZoomLevel(); + ZoomLevel level = min; + while (true) { + if (n == ix) { +// cerr << "View::getZoomLevelByIndex: " << ix << " -> " << level +// << endl; + return level; + } + ++n; + if (level == max) { + break; + } + level = getZoomConstraintLevel + (level.incremented(), ZoomConstraint::RoundUp); + } +// cerr << "View::getZoomLevelByIndex: " << ix << " -> " << max << " (max)" +// << endl; + return max; +} + +int +View::getZoomLevelIndex(ZoomLevel z) const +{ + int n = 0; + ZoomLevel min = ZoomConstraint().getMinZoomLevel(); + ZoomLevel max = ZoomConstraint().getMaxZoomLevel(); + ZoomLevel level = min; + while (true) { + if (z == level) { +// cerr << "View::getZoomLevelIndex: " << z << " -> " << n +// << endl; + return n; + } + ++n; + if (level == max) { + break; + } + level = getZoomConstraintLevel + (level.incremented(), ZoomConstraint::RoundUp); + } +// cerr << "View::getZoomLevelIndex: " << z << " -> " << n << " (max)" +// << endl; + return n; +} + bool View::areLayerColoursSignificant() const { @@ -1734,98 +1792,93 @@ } // ensure our constraints are met - -/*!!! Should we do this only if we have layers that can't support other - zoom levels? - - m_zoomLevel = getZoomConstraintBlockSize(m_zoomLevel, - ZoomConstraint::RoundUp); -*/ - - QPainter paint; - bool repaintCache = false; - bool paintedCacheRect = false; - - QRect cacheRect(rect()); - + m_zoomLevel = getZoomConstraintLevel + (m_zoomLevel, ZoomConstraint::RoundNearest); + + // We have a cache, which retains the state of scrollable (back) + // layers from one paint to the next, and a buffer, which we paint + // onto before copying directly to the widget. Both are at scaled + // resolution (e.g. 2x on a pixel-doubled display), whereas the + // paint event always comes in at formal (1x) resolution. + + // If we touch the cache, we always leave it in a valid state + // across its whole extent. When another method invalidates the + // cache, it does so by setting m_cacheValid false, so if that + // flag is true on entry, then the cache is valid across its whole + // extent - although it may be valid for a different centre frame, + // zoom level, or view size from those now in effect. + + // Our process goes: + // + // 1. Check whether we have any scrollable (cacheable) layers. If + // we don't, then invalidate and ignore the cache and go to + // step 5. Otherwise: + // + // 2. Check the cache, scroll as necessary, identify any area that + // needs to be refreshed (this might be the whole cache). + // + // 3. Paint to cache the area that needs to be refreshed, from the + // stack of scrollable layers. + // + // 4. Paint to buffer from cache: if there are no non-cached areas + // or selections and the cache has not scrolled, then paint the + // union of the area of cache that has changed and the area + // that the paint event reported as exposed; otherwise paint + // the whole. + // + // 5. Paint the exposed area to the buffer from the cache plus all + // the layers that haven't been cached, plus selections etc. + // + // 6. Paint the exposed rect from the buffer. + // + // Note that all rects except the target for the final step are at + // cache (scaled, 2x as applicable) resolution. + + int dpratio = effectiveDevicePixelRatio(); + + QRect requestedPaintArea(scaledRect(rect(), dpratio)); if (e) { - cacheRect &= e->rect(); -#ifdef DEBUG_VIEW_WIDGET_PAINT - cerr << "paint rect " << cacheRect.width() << "x" << cacheRect.height() - << ", my rect " << width() << "x" << height() << endl; -#endif + // cut down to only the area actually exposed + requestedPaintArea &= scaledRect(e->rect(), dpratio); } - QRect nonCacheRect(cacheRect); - - int dpratio = effectiveDevicePixelRatio(); - // If not all layers are scrollable, but some of the back layers // are, we should store only those in the cache. bool layersChanged = false; LayerList scrollables = getScrollableBackLayers(true, layersChanged); LayerList nonScrollables = getNonScrollableFrontLayers(true, layersChanged); - bool selectionCacheable = nonScrollables.empty(); - bool haveSelections = m_manager && !m_manager->getSelections().empty(); - - // If all the non-scrollable layers are non-opaque, then we draw - // the selection rectangle behind them and cache it. If any are - // opaque, however, or if our device-pixel ratio is not 1 (so we - // need to paint direct to the widget), then we can't cache. - // - if (dpratio == 1) { - - if (!selectionCacheable) { - selectionCacheable = true; - for (LayerList::const_iterator i = nonScrollables.begin(); - i != nonScrollables.end(); ++i) { - if ((*i)->isLayerOpaque()) { - selectionCacheable = false; - break; - } - } - } - - if (selectionCacheable) { - QPoint localPos; - bool closeToLeft, closeToRight; - if (shouldIlluminateLocalSelection - (localPos, closeToLeft, closeToRight)) { - selectionCacheable = false; - } - } - - } else { - - selectionCacheable = false; - } #ifdef DEBUG_VIEW_WIDGET_PAINT cerr << "View(" << this << ")::paintEvent: have " << scrollables.size() << " scrollable back layers and " << nonScrollables.size() << " non-scrollable front layers" << endl; - cerr << "haveSelections " << haveSelections << ", selectionCacheable " - << selectionCacheable << ", m_selectionCached " << m_selectionCached << endl; #endif - if (layersChanged || scrollables.empty() || - (haveSelections && (selectionCacheable != m_selectionCached))) { - delete m_cache; - m_cache = 0; - m_selectionCached = false; + if (layersChanged || scrollables.empty()) { + m_cacheValid = false; } - QSize scaledCacheSize(scaledSize(size(), dpratio)); - QRect scaledCacheRect(scaledRect(cacheRect, dpratio)); - - if (!m_buffer || scaledCacheSize != m_buffer->size()) { + QRect wholeArea(scaledRect(rect(), dpratio)); + QSize wholeSize(scaledSize(size(), dpratio)); + + if (!m_buffer || wholeSize != m_buffer->size()) { delete m_buffer; - m_buffer = new QPixmap(scaledCacheSize); + m_buffer = new QPixmap(wholeSize); } + + bool shouldUseCache = false; + bool shouldRepaintCache = false; + QRect cacheAreaToRepaint; + static HitCount count("View cache"); + if (!scrollables.empty()) { + shouldUseCache = true; + shouldRepaintCache = true; + cacheAreaToRepaint = wholeArea; + #ifdef DEBUG_VIEW_WIDGET_PAINT cerr << "View(" << this << "): cache " << m_cache << ", cache zoom " << m_cacheZoomLevel << ", zoom " << m_zoomLevel << endl; @@ -1833,209 +1886,185 @@ using namespace std::rel_ops; - if (!m_cache || + if (!m_cacheValid || + !m_cache || m_cacheZoomLevel != m_zoomLevel || - scaledCacheSize != m_cache->size()) { - - // cache is not valid - - if (cacheRect.width() < width()/10) { - delete m_cache; - m_cache = 0; + m_cache->size() != wholeSize) { + + // cache is not valid at all + + if (requestedPaintArea.width() < wholeSize.width() / 10) { + + m_cacheValid = false; + shouldUseCache = false; + shouldRepaintCache = false; + #ifdef DEBUG_VIEW_WIDGET_PAINT - cerr << "View(" << this << ")::paintEvent: small repaint, not bothering to recreate cache" << endl; + cerr << "View(" << this << ")::paintEvent: cache is invalid but only small area requested, will repaint directly instead" << endl; #endif } else { - delete m_cache; - m_cache = new QPixmap(scaledCacheSize); + + if (!m_cache || + m_cache->size() != wholeSize) { + delete m_cache; + m_cache = new QPixmap(wholeSize); + } + #ifdef DEBUG_VIEW_WIDGET_PAINT - cerr << "View(" << this << ")::paintEvent: recreated cache" << endl; + cerr << "View(" << this << ")::paintEvent: cache is invalid, will repaint whole" << endl; #endif - cacheRect = rect(); - repaintCache = true; } + count.miss(); + } else if (m_cacheCentreFrame != m_centreFrame) { - int dx = - getXForFrame(m_cacheCentreFrame) - - getXForFrame(m_centreFrame); - - if (dx > -width() && dx < width()) { - static QPixmap *tmpPixmap = 0; - if (!tmpPixmap || tmpPixmap->size() != scaledCacheSize) { - delete tmpPixmap; - tmpPixmap = new QPixmap(scaledCacheSize); + int dx = dpratio * (getXForFrame(m_cacheCentreFrame) - + getXForFrame(m_centreFrame)); + + if (dx > -m_cache->width() && dx < m_cache->width()) { + + m_cache->scroll(dx, 0, m_cache->rect(), 0); + + if (dx < 0) { + cacheAreaToRepaint = + QRect(m_cache->width() + dx, 0, -dx, m_cache->height()); + } else { + cacheAreaToRepaint = + QRect(0, 0, dx, m_cache->height()); } - paint.begin(tmpPixmap); - paint.drawPixmap(0, 0, *m_cache); - paint.end(); - paint.begin(m_cache); - paint.drawPixmap(dx, 0, *tmpPixmap); - paint.end(); - if (dx < 0) { - cacheRect = QRect(width() + dx, 0, -dx, height()); - } else { - cacheRect = QRect(0, 0, dx, height()); - } + + count.partial(); + #ifdef DEBUG_VIEW_WIDGET_PAINT cerr << "View(" << this << ")::paintEvent: scrolled cache by " << dx << endl; #endif } else { - cacheRect = rect(); + count.miss(); #ifdef DEBUG_VIEW_WIDGET_PAINT cerr << "View(" << this << ")::paintEvent: scrolling too far" << endl; #endif } - repaintCache = true; } else { #ifdef DEBUG_VIEW_WIDGET_PAINT cerr << "View(" << this << ")::paintEvent: cache is good" << endl; #endif - paint.begin(m_buffer); - paint.drawPixmap(scaledCacheRect, *m_cache, scaledCacheRect); - paint.end(); - QFrame::paintEvent(e); - paintedCacheRect = true; + count.hit(); + shouldRepaintCache = false; } - + } + +#ifdef DEBUG_VIEW_WIDGET_PAINT + cerr << "View(" << this << ")::paintEvent: m_cacheValid = " << m_cacheValid << ", shouldUseCache = " << shouldUseCache << ", shouldRepaintCache = " << shouldRepaintCache << ", cacheAreaToRepaint = " << cacheAreaToRepaint.x() << "," << cacheAreaToRepaint.y() << " " << cacheAreaToRepaint.width() << "x" << cacheAreaToRepaint.height() << endl; +#endif + + if (shouldRepaintCache && !shouldUseCache) { + // If we are repainting the cache, then we paint the + // scrollables only to the cache, not to the buffer. So if + // shouldUseCache is also false, then the scrollables can't + // appear because they will only be on the cache + throw std::logic_error("ERROR: shouldRepaintCache is true, but shouldUseCache is false: this can't lead to the correct result"); + } + + // Scrollable (cacheable) items first. If we are repainting the + // cache, then we paint these to the cache; otherwise straight to + // the buffer. + + ViewProxy proxy(this, dpratio); + QRect areaToPaint; + QPainter paint; + + if (shouldRepaintCache) { + paint.begin(m_cache); + areaToPaint = cacheAreaToRepaint; + } else { + paint.begin(m_buffer); + areaToPaint = requestedPaintArea; + } + + setPaintFont(paint); + paint.setClipRect(areaToPaint); + + paint.setPen(getBackground()); + paint.setBrush(getBackground()); + paint.drawRect(areaToPaint); + + paint.setPen(getForeground()); + paint.setBrush(Qt::NoBrush); + + for (LayerList::iterator i = scrollables.begin(); + i != scrollables.end(); ++i) { + + paint.setRenderHint(QPainter::Antialiasing, false); + paint.save(); + +#ifdef DEBUG_VIEW_WIDGET_PAINT + cerr << "Painting scrollable layer " << *i << " using proxy with shouldRepaintCache = " << shouldRepaintCache << ", dpratio = " << dpratio << ", areaToPaint = " << areaToPaint.x() << "," << areaToPaint.y() << " " << areaToPaint.width() << "x" << areaToPaint.height() << endl; +#endif + + (*i)->paint(&proxy, paint, areaToPaint); + + paint.restore(); + } + + paint.end(); + + if (shouldRepaintCache) { + // and now we have + m_cacheValid = true; m_cacheCentreFrame = m_centreFrame; m_cacheZoomLevel = m_zoomLevel; } -#ifdef DEBUG_VIEW_WIDGET_PAINT -// cerr << "View(" << this << ")::paintEvent: cacheRect " << cacheRect << ", nonCacheRect " << (nonCacheRect | cacheRect) << ", repaintCache " << repaintCache << ", paintedCacheRect " << paintedCacheRect << endl; -#endif - - // Scrollable (cacheable) items first - - ViewProxy proxy(this, dpratio); - - if (!paintedCacheRect) { - - QRect rectToPaint; - - if (repaintCache) { - paint.begin(m_cache); - rectToPaint = scaledCacheRect; - } else { - paint.begin(m_buffer); - rectToPaint = scaledCacheRect; - } - - setPaintFont(paint); - paint.setClipRect(rectToPaint); - - paint.setPen(getBackground()); - paint.setBrush(getBackground()); - paint.drawRect(rectToPaint); - - paint.setPen(getForeground()); - paint.setBrush(Qt::NoBrush); - - for (LayerList::iterator i = scrollables.begin(); i != scrollables.end(); ++i) { - paint.setRenderHint(QPainter::Antialiasing, false); - paint.save(); -#ifdef DEBUG_VIEW_WIDGET_PAINT - cerr << "Painting scrollable layer " << *i << " using proxy with repaintCache = " << repaintCache << ", dpratio = " << dpratio << ", rectToPaint = " << rectToPaint.x() << "," << rectToPaint.y() << " " << rectToPaint.width() << "x" << rectToPaint.height() << endl; -#endif - (*i)->paint(&proxy, paint, rectToPaint); - paint.restore(); - } - - if (haveSelections && selectionCacheable) { - drawSelections(paint); - m_selectionCached = repaintCache; - } - + if (shouldUseCache) { + paint.begin(m_buffer); + paint.drawPixmap(requestedPaintArea, *m_cache, requestedPaintArea); paint.end(); - - if (repaintCache) { - cacheRect |= (e ? e->rect() : rect()); - scaledCacheRect = scaledRect(cacheRect, dpratio); - paint.begin(m_buffer); - paint.drawPixmap(scaledCacheRect, *m_cache, scaledCacheRect); - paint.end(); - } } - // Now non-cacheable items. We always need to redraw the - // non-cacheable items across at least the area we drew of the - // cacheable items. - - nonCacheRect |= cacheRect; - - QRect scaledNonCacheRect = scaledRect(nonCacheRect, dpratio); - + // Now non-cacheable items. + paint.begin(m_buffer); - paint.setClipRect(scaledNonCacheRect); + paint.setClipRect(requestedPaintArea); setPaintFont(paint); if (scrollables.empty()) { paint.setPen(getBackground()); paint.setBrush(getBackground()); - paint.drawRect(scaledNonCacheRect); + paint.drawRect(requestedPaintArea); } paint.setPen(getForeground()); paint.setBrush(Qt::NoBrush); - for (LayerList::iterator i = nonScrollables.begin(); i != nonScrollables.end(); ++i) { + for (LayerList::iterator i = nonScrollables.begin(); + i != nonScrollables.end(); ++i) { + // Profiler profiler2("View::paintEvent non-cacheable"); #ifdef DEBUG_VIEW_WIDGET_PAINT - cerr << "Painting non-scrollable layer " << *i << " without proxy with repaintCache = " << repaintCache << ", dpratio = " << dpratio << ", rectToPaint = " << nonCacheRect.x() << "," << nonCacheRect.y() << " " << nonCacheRect.width() << "x" << nonCacheRect.height() << endl; + cerr << "Painting non-scrollable layer " << *i << " without proxy with shouldRepaintCache = " << shouldRepaintCache << ", dpratio = " << dpratio << ", requestedPaintArea = " << requestedPaintArea.x() << "," << requestedPaintArea.y() << " " << requestedPaintArea.width() << "x" << requestedPaintArea.height() << endl; #endif - (*i)->paint(&proxy, paint, scaledNonCacheRect); + (*i)->paint(&proxy, paint, requestedPaintArea); } paint.end(); - - paint.begin(this); - QRect finalPaintRect = e ? e->rect() : rect(); - paint.drawPixmap(finalPaintRect, *m_buffer, scaledRect(finalPaintRect, dpratio)); - paint.end(); + + // Now paint to widget from buffer: target rects from here on, + // unlike all the preceding, are at formal (1x) resolution paint.begin(this); setPaintFont(paint); if (e) paint.setClipRect(e->rect()); - if (!m_selectionCached) { - drawSelections(paint); - } + + QRect finalPaintRect = e ? e->rect() : rect(); + paint.drawPixmap(finalPaintRect, *m_buffer, + scaledRect(finalPaintRect, dpratio)); + + drawSelections(paint); + drawPlayPointer(paint); + paint.end(); - bool showPlayPointer = true; - if (m_followPlay == PlaybackScrollContinuous) { - showPlayPointer = false; - } else if (m_playPointerFrame <= getStartFrame() || - m_playPointerFrame >= getEndFrame()) { - showPlayPointer = false; - } else if (m_manager && !m_manager->isPlaying()) { - if (m_playPointerFrame == getCentreFrame() && - m_manager->shouldShowCentreLine() && - m_followPlay != PlaybackIgnore) { - // Don't show the play pointer when it is redundant with - // the centre line - showPlayPointer = false; - } - } - - if (showPlayPointer) { - - paint.begin(this); - - int playx = getXForFrame(m_playPointerFrame); - - paint.setPen(getForeground()); - paint.drawLine(playx - 1, 0, playx - 1, height() - 1); - paint.drawLine(playx + 1, 0, playx + 1, height() - 1); - paint.drawPoint(playx, 0); - paint.drawPoint(playx, height() - 1); - paint.setPen(getBackground()); - paint.drawLine(playx, 1, playx, height() - 2); - - paint.end(); - } - QFrame::paintEvent(e); } @@ -2197,6 +2226,40 @@ } void +View::drawPlayPointer(QPainter &paint) +{ + bool showPlayPointer = true; + + if (m_followPlay == PlaybackScrollContinuous) { + showPlayPointer = false; + } else if (m_playPointerFrame <= getStartFrame() || + m_playPointerFrame >= getEndFrame()) { + showPlayPointer = false; + } else if (m_manager && !m_manager->isPlaying()) { + if (m_playPointerFrame == getCentreFrame() && + m_manager->shouldShowCentreLine() && + m_followPlay != PlaybackIgnore) { + // Don't show the play pointer when it is redundant with + // the centre line + showPlayPointer = false; + } + } + + if (showPlayPointer) { + + int playx = getXForFrame(m_playPointerFrame); + + paint.setPen(getForeground()); + paint.drawLine(playx - 1, 0, playx - 1, height() - 1); + paint.drawLine(playx + 1, 0, playx + 1, height() - 1); + paint.drawPoint(playx, 0); + paint.drawPoint(playx, height() - 1); + paint.setPen(getBackground()); + paint.drawLine(playx, 1, playx, height() - 2); + } +} + +void View::drawMeasurementRect(QPainter &paint, const Layer *topLayer, QRect r, bool focus) const {
--- a/view/View.h Fri Oct 05 10:25:52 2018 +0100 +++ b/view/View.h Fri Oct 12 11:17:29 2018 +0100 @@ -440,6 +440,7 @@ virtual void paintEvent(QPaintEvent *e); virtual void drawSelections(QPainter &); virtual bool shouldLabelSelections() const { return true; } + virtual void drawPlayPointer(QPainter &); virtual bool render(QPainter &paint, int x0, sv_frame_t f0, sv_frame_t f1); virtual void setPaintFont(QPainter &paint); @@ -457,10 +458,16 @@ bool areLayersScrollable() const; LayerList getScrollableBackLayers(bool testChanged, bool &changed) const; LayerList getNonScrollableFrontLayers(bool testChanged, bool &changed) const; + ZoomLevel getZoomConstraintLevel(ZoomLevel level, ZoomConstraint::RoundingDirection dir = ZoomConstraint::RoundNearest) const; + // These three are slow, intended for indexing GUI thumbwheel stuff + int countZoomLevels() const; + int getZoomLevelIndex(ZoomLevel level) const; + ZoomLevel getZoomLevelByIndex(int ix) const; + // True if the top layer(s) use colours for meaningful things. If // this is the case, selections will be shown using unfilled boxes // rather than with a translucent fill. @@ -493,6 +500,7 @@ QPixmap *m_cache; // I own this QPixmap *m_buffer; // I own this + bool m_cacheValid; sv_frame_t m_cacheCentreFrame; ZoomLevel m_cacheZoomLevel; bool m_selectionCached;
--- a/widgets/SubdividingMenu.cpp Fri Oct 05 10:25:52 2018 +0100 +++ b/widgets/SubdividingMenu.cpp Fri Oct 12 11:17:29 2018 +0100 @@ -85,7 +85,7 @@ return QString::localeAwareCompare(s1, s2) < 0; }; - set<QString, typeof(comparator)> sortedEntries(comparator); + set<QString, decltype(comparator)> sortedEntries(comparator); sortedEntries.insert(entries.begin(), entries.end()); for (auto j = sortedEntries.begin(); j != sortedEntries.end(); ++j) { @@ -180,7 +180,7 @@ auto comparator = [](QString s1, QString s2) -> bool { return QString::localeAwareCompare(s1, s2) < 0; }; - set<QString, typeof(comparator)> sortedEntries(comparator); + set<QString, decltype(comparator)> sortedEntries(comparator); for (auto i: m_pendingEntries) { sortedEntries.insert(i.first); }