diff view/View.cpp @ 1353:86429ff00f05

Merge from branch zoom
author Chris Cannam
date Wed, 10 Oct 2018 08:44:27 +0100
parents 9fb7133dd818 4949061fcb8c
children 40b9a495a0e0
line wrap: on
line diff
--- a/view/View.cpp	Wed Oct 03 12:59:55 2018 +0100
+++ b/view/View.cpp	Wed Oct 10 08:44:27 2018 +0100
@@ -54,7 +54,7 @@
     QFrame(w),
     m_id(getNextId()),
     m_centreFrame(0),
-    m_zoomLevel(1024),
+    m_zoomLevel(ZoomLevel::FramesPerPixel, 1024),
     m_followPan(true),
     m_followZoom(true),
     m_followPlay(PlaybackScrollPageWithCentre),
@@ -64,7 +64,7 @@
     m_cache(0),
     m_buffer(0),
     m_cacheCentreFrame(0),
-    m_cacheZoomLevel(1024),
+    m_cacheZoomLevel(ZoomLevel::FramesPerPixel, 1024),
     m_selectionCached(false),
     m_deleting(false),
     m_haveSelectedLayer(false),
@@ -319,7 +319,8 @@
 void
 View::setStartFrame(sv_frame_t f)
 {
-    setCentreFrame(f + m_zoomLevel * (width() / 2));
+    setCentreFrame(f + sv_frame_t(round
+                                  (m_zoomLevel.pixelsToFrames(width() / 2))));
 }
 
 bool
@@ -327,22 +328,40 @@
 {
     bool changeVisible = false;
 
+#ifdef DEBUG_VIEW
+    SVCERR << "View::setCentreFrame: from " << m_centreFrame
+           << " to " << f << endl;
+#endif
+
     if (m_centreFrame != f) {
 
-        int formerPixel = int(m_centreFrame / m_zoomLevel);
-
+        sv_frame_t formerCentre = m_centreFrame;
         m_centreFrame = f;
-
-        int newPixel = int(m_centreFrame / m_zoomLevel);
         
-        if (newPixel != formerPixel) {
+        if (m_zoomLevel.zone == ZoomLevel::PixelsPerFrame) {
 
 #ifdef DEBUG_VIEW_WIDGET_PAINT
-            cout << "View(" << this << ")::setCentreFrame: newPixel " << newPixel << ", formerPixel " << formerPixel << endl;
+            SVCERR << "View(" << this << ")::setCentreFrame: in PixelsPerFrame zone, so change must be visible" << endl;
 #endif
             update();
-
             changeVisible = true;
+
+        } else {
+        
+            int formerPixel = int(formerCentre / m_zoomLevel.level);
+            int newPixel = int(m_centreFrame / m_zoomLevel.level);
+        
+            if (newPixel != formerPixel) {
+
+#ifdef DEBUG_VIEW_WIDGET_PAINT
+                SVCERR << "View(" << this << ")::setCentreFrame: newPixel " << newPixel << ", formerPixel " << formerPixel << endl;
+#endif
+                // ensure the centre frame is a multiple of the zoom level
+                m_centreFrame = sv_frame_t(newPixel) * m_zoomLevel.level;
+                
+                update();
+                changeVisible = true;
+            }
         }
 
         if (e) {
@@ -362,23 +381,65 @@
 int
 View::getXForFrame(sv_frame_t frame) const
 {
-    return int((frame - getStartFrame()) / m_zoomLevel);
+    // In FramesPerPixel mode, the pixel should be the one "covering"
+    // the given frame, i.e. to the "left" of it - not necessarily the
+    // nearest boundary.
+    
+    sv_frame_t level = m_zoomLevel.level;
+    sv_frame_t fdiff = frame - getCentreFrame();
+    int diff, result;
+
+    if (m_zoomLevel.zone == ZoomLevel::FramesPerPixel) {
+        diff = int(fdiff / level);
+        if ((fdiff < 0) && ((fdiff % level) != 0)) {
+            --diff; // round to the left
+        }
+    } else {
+        diff = int(fdiff * level);
+    }
+
+    result = int(diff + (width()/2));
+    return result;
 }
 
 sv_frame_t
 View::getFrameForX(int x) const
 {
-    sv_frame_t z = m_zoomLevel; // nb not just int, or multiplication may overflow
-    sv_frame_t frame = m_centreFrame - (width()/2) * z;
-
-    frame = (frame / z) * z; // this is start frame
-    frame = frame + x * z;
-
-#ifdef DEBUG_VIEW_WIDGET_PAINT
-    cerr << "View::getFrameForX(" << x << "): z = " << z << ", m_centreFrame = " << m_centreFrame << ", width() = " << width() << ", frame = " << frame << endl;
+    // Note, this must always return a value that is on a zoom-level
+    // boundary - regardless of whether the nominal centre frame is on
+    // such a boundary or not.
+    
+    // In PixelsPerFrame mode, the frame should be the one immediately
+    // left of the given pixel, not necessarily the nearest.
+
+    int diff = x - (width()/2);
+    sv_frame_t level = m_zoomLevel.level;
+    sv_frame_t fdiff, result;
+    
+    if (m_zoomLevel.zone == ZoomLevel::FramesPerPixel) {
+        fdiff = diff * level;
+        result = ((fdiff + m_centreFrame) / level) * level;
+    } else {
+        fdiff = diff / level;
+        if ((diff < 0) && ((diff % level) != 0)) {
+            --fdiff; // round to the left
+        }
+        result = fdiff + m_centreFrame;
+    }
+
+#ifdef DEBUG_VIEW
+    if (x == 0) {
+        SVCERR << "getFrameForX(" << x << "): diff = " << diff << ", fdiff = "
+               << fdiff << ", m_centreFrame = " << m_centreFrame
+               << ", level = " << m_zoomLevel.level
+               << ", diff % level = " << (diff % m_zoomLevel.level)
+               << ", nominal " << fdiff + m_centreFrame
+               << ", will return " << result
+               << endl;
+    }
 #endif
-
-    return frame;
+    
+    return result;
 }
 
 double
@@ -447,7 +508,7 @@
     }
 }
 
-int
+ZoomLevel
 View::getZoomLevel() const
 {
 #ifdef DEBUG_VIEW_WIDGET_PAINT
@@ -476,16 +537,17 @@
 }
 
 void
-View::setZoomLevel(int z)
+View::setZoomLevel(ZoomLevel z)
 {
-    int dpratio = effectiveDevicePixelRatio();
-    if (z < dpratio) return;
-    if (z < 1) z = 1;
-    if (m_zoomLevel != int(z)) {
-        m_zoomLevel = z;
-        emit zoomLevelChanged(z, m_followZoom);
-        update();
+//!!!    int dpratio = effectiveDevicePixelRatio();
+//    if (z < dpratio) return;
+//    if (z < 1) z = 1;
+    if (m_zoomLevel == z) {
+        return;
     }
+    m_zoomLevel = z;
+    emit zoomLevelChanged(z, m_followZoom);
+    update();
 }
 
 bool
@@ -725,13 +787,13 @@
         m_manager->disconnect(this, SLOT(globalCentreFrameChanged(sv_frame_t)));
         m_manager->disconnect(this, SLOT(viewCentreFrameChanged(View *, sv_frame_t)));
         m_manager->disconnect(this, SLOT(viewManagerPlaybackFrameChanged(sv_frame_t)));
-        m_manager->disconnect(this, SLOT(viewZoomLevelChanged(View *, int, bool)));
+        m_manager->disconnect(this, SLOT(viewZoomLevelChanged(View *, ZoomLevel, bool)));
         m_manager->disconnect(this, SLOT(toolModeChanged()));
         m_manager->disconnect(this, SLOT(selectionChanged()));
         m_manager->disconnect(this, SLOT(overlayModeChanged()));
         m_manager->disconnect(this, SLOT(zoomWheelsEnabledChanged()));
         disconnect(m_manager, SLOT(viewCentreFrameChanged(sv_frame_t, bool, PlaybackFollowMode)));
-        disconnect(m_manager, SLOT(zoomLevelChanged(int, bool)));
+        disconnect(m_manager, SLOT(zoomLevelChanged(ZoomLevel, bool)));
     }
 
     m_manager = manager;
@@ -743,8 +805,8 @@
     connect(m_manager, SIGNAL(playbackFrameChanged(sv_frame_t)),
             this, SLOT(viewManagerPlaybackFrameChanged(sv_frame_t)));
 
-    connect(m_manager, SIGNAL(viewZoomLevelChanged(View *, int, bool)),
-            this, SLOT(viewZoomLevelChanged(View *, int, bool)));
+    connect(m_manager, SIGNAL(viewZoomLevelChanged(View *, ZoomLevel, bool)),
+            this, SLOT(viewZoomLevelChanged(View *, ZoomLevel, bool)));
 
     connect(m_manager, SIGNAL(toolModeChanged()),
             this, SLOT(toolModeChanged()));
@@ -764,8 +826,8 @@
             m_manager, SLOT(viewCentreFrameChanged(sv_frame_t, bool,
                                                    PlaybackFollowMode)));
 
-    connect(this, SIGNAL(zoomLevelChanged(int, bool)),
-            m_manager, SLOT(viewZoomLevelChanged(int, bool)));
+    connect(this, SIGNAL(zoomLevelChanged(ZoomLevel, bool)),
+            m_manager, SLOT(viewZoomLevelChanged(ZoomLevel, bool)));
 
     switch (m_followPlay) {
         
@@ -1127,7 +1189,7 @@
 }
 
 void
-View::viewZoomLevelChanged(View *p, int z, bool locked)
+View::viewZoomLevelChanged(View *p, ZoomLevel z, bool locked)
 {
 #ifdef DEBUG_VIEW_WIDGET_PAINT
     cerr  << "View[" << this << "]: viewZoomLevelChanged(" << p << ", " << z << ", " << locked << ")" << endl;
@@ -1400,30 +1462,32 @@
     return nonScrollables;
 }
 
-int
-View::getZoomConstraintBlockSize(int blockSize,
-                                 ZoomConstraint::RoundingDirection dir)
+ZoomLevel
+View::getZoomConstraintLevel(ZoomLevel zoomLevel,
+                             ZoomConstraint::RoundingDirection dir)
     const
 {
-    int candidate = blockSize;
+    using namespace std::rel_ops;
+    
+    ZoomLevel candidate = zoomLevel;
     bool haveCandidate = false;
 
     PowerOfSqrtTwoZoomConstraint defaultZoomConstraint;
 
-    for (LayerList::const_iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) {
+    for (auto i = m_layerStack.begin(); i != m_layerStack.end(); ++i) {
 
         const ZoomConstraint *zoomConstraint = (*i)->getZoomConstraint();
         if (!zoomConstraint) zoomConstraint = &defaultZoomConstraint;
 
-        int thisBlockSize =
-            zoomConstraint->getNearestBlockSize(blockSize, dir);
+        ZoomLevel thisLevel =
+            zoomConstraint->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 ||
-            (thisBlockSize > blockSize && thisBlockSize > candidate) ||
-            (thisBlockSize < blockSize && thisBlockSize < candidate)) {
-            candidate = thisBlockSize;
+            (thisLevel > zoomLevel && thisLevel > candidate) ||
+            (thisLevel < zoomLevel && thisLevel < candidate)) {
+            candidate = thisLevel;
             haveCandidate = true;
         }
     }
@@ -1454,16 +1518,18 @@
 void
 View::zoom(bool in)
 {
-    int newZoomLevel = m_zoomLevel;
+    ZoomLevel newZoomLevel = m_zoomLevel;
 
     if (in) {
-        newZoomLevel = getZoomConstraintBlockSize(newZoomLevel - 1, 
-                                                  ZoomConstraint::RoundDown);
+        newZoomLevel = getZoomConstraintLevel(m_zoomLevel.decremented(),
+                                              ZoomConstraint::RoundDown);
     } else {
-        newZoomLevel = getZoomConstraintBlockSize(newZoomLevel + 1,
-                                                  ZoomConstraint::RoundUp);
+        newZoomLevel = getZoomConstraintLevel(m_zoomLevel.incremented(),
+                                              ZoomConstraint::RoundUp);
     }
 
+    using namespace std::rel_ops;
+    
     if (newZoomLevel != m_zoomLevel) {
         setZoomLevel(newZoomLevel);
     }
@@ -1765,6 +1831,8 @@
                   << m_cacheZoomLevel << ", zoom " << m_zoomLevel << endl;
 #endif
 
+        using namespace std::rel_ops;
+    
         if (!m_cache ||
             m_cacheZoomLevel != m_zoomLevel ||
             scaledCacheSize != m_cache->size()) {
@@ -2370,8 +2438,8 @@
 bool
 View::render(QPainter &paint, int xorigin, sv_frame_t f0, sv_frame_t f1)
 {
-    int x0 = int(f0 / m_zoomLevel);
-    int x1 = int(f1 / m_zoomLevel);
+    int x0 = int(round(m_zoomLevel.framesToPixels(double(f0))));
+    int x1 = int(round(m_zoomLevel.framesToPixels(double(f1))));
 
     int w = x1 - x0;
 
@@ -2433,7 +2501,8 @@
             return false;
         }
 
-        m_centreFrame = f0 + (x + width()/2) * m_zoomLevel;
+        m_centreFrame = f0 + sv_frame_t(round(m_zoomLevel.pixelsToFrames
+                                              (x + width()/2)));
         
         QRect chunk(0, 0, width(), height());
 
@@ -2484,8 +2553,8 @@
 QImage *
 View::renderPartToNewImage(sv_frame_t f0, sv_frame_t f1)
 {
-    int x0 = int(f0 / getZoomLevel());
-    int x1 = int(f1 / getZoomLevel());
+    int x0 = int(round(getZoomLevel().framesToPixels(double(f0))));
+    int x1 = int(round(getZoomLevel().framesToPixels(double(f1))));
     
     QImage *image = new QImage(x1 - x0, height(), QImage::Format_RGB32);
 
@@ -2512,8 +2581,8 @@
 QSize
 View::getRenderedPartImageSize(sv_frame_t f0, sv_frame_t f1)
 {
-    int x0 = int(f0 / getZoomLevel());
-    int x1 = int(f1 / getZoomLevel());
+    int x0 = int(round(getZoomLevel().framesToPixels(double(f0))));
+    int x1 = int(round(getZoomLevel().framesToPixels(double(f1))));
 
     return QSize(x1 - x0, height());
 }
@@ -2530,8 +2599,8 @@
 bool
 View::renderPartToSvgFile(QString filename, sv_frame_t f0, sv_frame_t f1)
 {
-    int x0 = int(f0 / getZoomLevel());
-    int x1 = int(f1 / getZoomLevel());
+    int x0 = int(round(getZoomLevel().framesToPixels(double(f0))));
+    int x1 = int(round(getZoomLevel().framesToPixels(double(f1))));
 
     QSvgGenerator generator;
     generator.setFileName(filename);
@@ -2553,15 +2622,27 @@
 {
     stream << indent;
 
+    int classicZoomValue, deepZoomValue;
+
+    if (m_zoomLevel.zone == ZoomLevel::FramesPerPixel) {
+        classicZoomValue = m_zoomLevel.level;
+        deepZoomValue = 1;
+    } else {
+        classicZoomValue = 1;
+        deepZoomValue = m_zoomLevel.level;
+    }
+
     stream << QString("<view "
                       "centre=\"%1\" "
                       "zoom=\"%2\" "
-                      "followPan=\"%3\" "
-                      "followZoom=\"%4\" "
-                      "tracking=\"%5\" "
-                      " %6>\n")
+                      "deepZoom=\"%3\" "
+                      "followPan=\"%4\" "
+                      "followZoom=\"%5\" "
+                      "tracking=\"%6\" "
+                      " %7>\n")
         .arg(m_centreFrame)
-        .arg(m_zoomLevel)
+        .arg(classicZoomValue)
+        .arg(deepZoomValue)
         .arg(m_followPan)
         .arg(m_followZoom)
         .arg(m_followPlay == PlaybackScrollContinuous ? "scroll" :