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