Mercurial > hg > svgui
changeset 1490:c83504eb2649
Attempt a mechanism for the View to time-align a layer on display using an aligning version of the ViewProxy
author | Chris Cannam |
---|---|
date | Fri, 02 Aug 2019 16:44:32 +0100 |
parents | b402121d8f5f |
children | 2f505d843d14 |
files | view/Pane.cpp view/View.cpp view/View.h view/ViewProxy.h |
diffstat | 4 files changed, 139 insertions(+), 30 deletions(-) [+] |
line wrap: on
line diff
--- a/view/Pane.cpp Fri Aug 02 16:42:32 2019 +0100 +++ b/view/Pane.cpp Fri Aug 02 16:44:32 2019 +0100 @@ -388,12 +388,9 @@ if (ModelById::isa<WaveFileModel>(modelId)) { workModelId = modelId; } else { - auto model = ModelById::get(modelId); - if (model) { - ModelId sourceId = model->getSourceModel(); - if (ModelById::isa<WaveFileModel>(sourceId)) { - workModelId = sourceId; - } + ModelId sourceId = (*vi)->getSourceModel(); + if (ModelById::isa<WaveFileModel>(sourceId)) { + workModelId = sourceId; } } }
--- a/view/View.cpp Fri Aug 02 16:42:32 2019 +0100 +++ b/view/View.cpp Fri Aug 02 16:44:32 2019 +0100 @@ -71,6 +71,7 @@ m_selectionCached(false), m_deleting(false), m_haveSelectedLayer(false), + m_useAligningProxy(false), m_manager(nullptr), m_propertyContainer(new ViewPropertyContainer(this)) { @@ -1349,15 +1350,22 @@ ModelId View::getAligningModel() const { + ModelId aligning, reference; + getAligningAndReferenceModels(aligning, reference); + return aligning; +} + +void +View::getAligningAndReferenceModels(ModelId &aligning, + ModelId &reference) const +{ if (!m_manager || !m_manager->getAlignMode() || m_manager->getPlaybackModel().isNone()) { - return {}; + return; } ModelId anyModel; - ModelId alignedModel; - ModelId goodModel; for (auto layer: m_layerStack) { @@ -1371,18 +1379,27 @@ anyModel = thisId; if (!model->getAlignmentReference().isNone()) { - alignedModel = thisId; + if (layer->isLayerOpaque() || std::dynamic_pointer_cast <RangeSummarisableTimeValueModel>(model)) { - goodModel = thisId; + + aligning = thisId; + reference = model->getAlignmentReference(); + return; + + } else if (aligning.isNone()) { + + aligning = thisId; + reference = model->getAlignmentReference(); } } } - if (!goodModel.isNone()) return goodModel; - else if (!alignedModel.isNone()) return alignedModel; - else return anyModel; + if (aligning.isNone()) { + aligning = anyModel; + reference = {}; + } } sv_frame_t @@ -2132,11 +2149,32 @@ throw std::logic_error("ERROR: shouldRepaintCache is true, but shouldUseCache is false: this can't lead to the correct result"); } + // Create the ViewProxy for geometry provision, using the + // device-pixel ratio for pixel-doubled hi-dpi rendering as + // appropriate. + + ViewProxy proxy(this, dpratio); + + // Some layers may need an aligning proxy. If a layer's model has + // a source model that is the reference model for the aligning + // model, and the layer is tagged as to be aligned, then we use an + // aligning proxy. + + ModelId alignmentModelId; + ModelId alignmentReferenceId; + auto aligningModel = ModelById::get(getAligningModel()); + if (aligningModel) { + alignmentModelId = aligningModel->getAlignment(); + alignmentReferenceId = aligningModel->getAlignmentReference(); + SVCERR << "alignmentModelId = " << alignmentModelId << " (reference = " << alignmentReferenceId << ")" << endl; + } else { + SVCERR << "no aligningModel" << endl; + } + ViewProxy aligningProxy(this, dpratio, alignmentModelId); + // 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; @@ -2164,11 +2202,22 @@ paint.setRenderHint(QPainter::Antialiasing, false); paint.save(); + Layer *layer = *i; + + bool useAligningProxy = false; + if (m_useAligningProxy) { + if (layer->getModel() == alignmentReferenceId || + layer->getSourceModel() == alignmentReferenceId) { + useAligningProxy = true; + } + } + #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; + cerr << "Painting scrollable layer " << layer << " (model " << layer->getModel() << ", source model " << layer->getSourceModel() << ") with shouldRepaintCache = " << shouldRepaintCache << ", useAligningProxy = " << useAligningProxy << ", dpratio = " << dpratio << ", areaToPaint = " << areaToPaint.x() << "," << areaToPaint.y() << " " << areaToPaint.width() << "x" << areaToPaint.height() << endl; #endif - - (*i)->paint(&proxy, paint, areaToPaint); + + layer->paint(useAligningProxy ? &aligningProxy : &proxy, + paint, areaToPaint); paint.restore(); } @@ -2204,12 +2253,23 @@ for (LayerList::iterator i = nonScrollables.begin(); i != nonScrollables.end(); ++i) { - -// Profiler profiler2("View::paintEvent non-cacheable"); + + Layer *layer = *i; + + bool useAligningProxy = false; + if (m_useAligningProxy) { + if (layer->getModel() == alignmentReferenceId || + layer->getSourceModel() == alignmentReferenceId) { + useAligningProxy = true; + } + } + #ifdef DEBUG_VIEW_WIDGET_PAINT - cerr << "Painting non-scrollable layer " << *i << " without proxy with shouldRepaintCache = " << shouldRepaintCache << ", dpratio = " << dpratio << ", requestedPaintArea = " << requestedPaintArea.x() << "," << requestedPaintArea.y() << " " << requestedPaintArea.width() << "x" << requestedPaintArea.height() << endl; + cerr << "Painting non-scrollable layer " << layer << " (model " << layer->getModel() << ", source model " << layer->getSourceModel() << ") with shouldRepaintCache = " << shouldRepaintCache << ", useAligningProxy = " << useAligningProxy << ", dpratio = " << dpratio << ", requestedPaintArea = " << requestedPaintArea.x() << "," << requestedPaintArea.y() << " " << requestedPaintArea.width() << "x" << requestedPaintArea.height() << endl; #endif - (*i)->paint(&proxy, paint, requestedPaintArea); + + layer->paint(useAligningProxy ? &aligningProxy : &proxy, + paint, requestedPaintArea); } paint.end();
--- a/view/View.h Fri Aug 02 16:42:32 2019 +0100 +++ b/view/View.h Fri Aug 02 16:44:32 2019 +0100 @@ -386,8 +386,14 @@ typedef std::set<ModelId> ModelSet; ModelSet getModels(); + //!!!??? poor name, probably poor api, consider this + void setUseAligningProxy(bool uap) { + m_useAligningProxy = uap; + } + //!!! ModelId getAligningModel() const; + void getAligningAndReferenceModels(ModelId &aligning, ModelId &reference) const; sv_frame_t alignFromReference(sv_frame_t) const; sv_frame_t alignToReference(sv_frame_t) const; sv_frame_t getAlignedPlaybackFrame() const; @@ -534,6 +540,8 @@ LayerList m_fixedOrderLayers; bool m_haveSelectedLayer; + bool m_useAligningProxy; + QString m_lastError; // caches for use in getScrollableBackLayers, getNonScrollableFrontLayers
--- a/view/ViewProxy.h Fri Aug 02 16:42:32 2019 +0100 +++ b/view/ViewProxy.h Fri Aug 02 16:44:32 2019 +0100 @@ -17,33 +17,60 @@ #include "layer/LayerGeometryProvider.h" +#include "data/model/AlignmentModel.h" + class ViewProxy : public LayerGeometryProvider { public: + /** + * Create a standard ViewProxy for the given view, mapping using + * the given scale factor. The scale factor is generally used with + * pixel-doubled "retina" Mac displays and is usually 1 elsewhere. + */ ViewProxy(View *view, int scaleFactor) : m_view(view), m_scaleFactor(scaleFactor) { } + /** + * Create a re-aligning ViewProxy for the given view, mapping + * using the given scale factor. The scale factor is generally + * used with pixel-doubled "retina" Mac displays and is usually 1 + * elsewhere. + * + * Coordinates are mapped through the given alignment model, such + * that frame values passed from the caller are mapped "from + * reference" by that alignment before being used by the view or + * converted to pixel coordinates, and returned values are mapped + * back "to reference" before being passed back to the caller. + * + * This form of proxy may be created specially for rendering a + * single layer which comes from a different alignment to that of + * the rest of the containing view. + */ + ViewProxy(View *view, int scaleFactor, ModelId alignment) : + m_view(view), m_scaleFactor(scaleFactor), m_alignment(alignment) { } + int getId() const override { return m_view->getId(); } sv_frame_t getStartFrame() const override { - return m_view->getStartFrame(); + return alignToReference(m_view->getStartFrame()); } sv_frame_t getCentreFrame() const override { - return m_view->getCentreFrame(); + return alignToReference(m_view->getCentreFrame()); } sv_frame_t getEndFrame() const override { - return m_view->getEndFrame(); + return alignToReference(m_view->getEndFrame()); } int getXForFrame(sv_frame_t frame) const override { //!!! not actually correct, if frame lies between view's pixels - return m_scaleFactor * m_view->getXForFrame(frame); + return m_scaleFactor * m_view->getXForFrame(alignFromReference(frame)); } sv_frame_t getFrameForX(int x) const override { sv_frame_t f0 = m_view->getFrameForX(x / m_scaleFactor); if (m_scaleFactor == 1) return f0; sv_frame_t f1 = m_view->getFrameForX((x / m_scaleFactor) + 1); - return f0 + ((f1 - f0) * (x % m_scaleFactor)) / m_scaleFactor; + sv_frame_t f = f0 + ((f1 - f0) * (x % m_scaleFactor)) / m_scaleFactor; + return alignToReference(f); } int getXForViewX(int viewx) const override { return viewx * m_scaleFactor; @@ -52,10 +79,10 @@ return x / m_scaleFactor; } sv_frame_t getModelsStartFrame() const override { - return m_view->getModelsStartFrame(); + return alignToReference(m_view->getModelsStartFrame()); } sv_frame_t getModelsEndFrame() const override { - return m_view->getModelsEndFrame(); + return alignToReference(m_view->getModelsEndFrame()); } double getYForFrequency(double frequency, double minFreq, double maxFreq, @@ -188,6 +215,23 @@ private: View *m_view; int m_scaleFactor; + ModelId m_alignment; + + sv_frame_t alignToReference(sv_frame_t frame) const { + if (auto am = ModelById::getAs<AlignmentModel>(m_alignment)) { + return am->toReference(frame); + } else { + return frame; + } + } + + sv_frame_t alignFromReference(sv_frame_t frame) const { + if (auto am = ModelById::getAs<AlignmentModel>(m_alignment)) { + return am->fromReference(frame); + } else { + return frame; + } + } }; #endif