changeset 1031:55ac6ac1982e spectrogram-minor-refactor

Further fixes to the scrollable cache logic
author Chris Cannam
date Fri, 29 Jan 2016 18:51:05 +0000
parents 0be17aafa935
children 6d084d6d5015
files layer/ScrollableImageCache.h layer/SpectrogramLayer.cpp svgui.pro view/LayerGeometryProvider.h
diffstat 4 files changed, 115 insertions(+), 182 deletions(-) [+]
line wrap: on
line diff
--- a/layer/ScrollableImageCache.h	Fri Jan 29 15:08:01 2016 +0000
+++ b/layer/ScrollableImageCache.h	Fri Jan 29 18:51:05 2016 +0000
@@ -53,11 +53,6 @@
 	return m_width > 0;
     }
 
-    bool spans(int left, int right) const {
-	return (getValidLeft() <= left &&
-		getValidRight() >= right);
-    }
-    
     QSize getSize() const {
 	return m_image.size();
     }
@@ -86,169 +81,57 @@
     int getZoomLevel() const {
 	return m_zoomLevel;
     }
+    
+    void setZoomLevel(int zoom) {
+	m_zoomLevel = zoom;
+	invalidate();
+    }
 
     sv_frame_t getStartFrame() const {
 	return m_startFrame;
     }
-    
-    void setZoomLevel(int zoom) {
-	m_zoomLevel = zoom;
+
+    /**
+     * Set the start frame and invalidate the cache. To scroll,
+     * i.e. to set the start frame while retaining cache validity
+     * where possible, use scrollTo() instead.
+     */
+    void setStartFrame(sv_frame_t frame) {
+	m_startFrame = frame;
 	invalidate();
     }
     
     const QImage &getImage() const {
 	return m_image;
     }
-    
-    void scrollTo(sv_frame_t newStartFrame) {
 
-	if (!m_v) throw std::logic_error("ScrollableImageCache: not associated with a LayerGeometryProvider");
-	
-	int dx = (m_v->getXForFrame(m_startFrame) -
-		  m_v->getXForFrame(newStartFrame));
+    /**
+     * Set the new start frame for the cache, if possible also moving
+     * along any existing valid data within the cache so that it
+     * continues to be valid for the new start frame.
+     */
+    void scrollTo(sv_frame_t newStartFrame);
 
-	m_startFrame = newStartFrame;
-	
-	if (!isValid()) {
-	    return;
-	}
-
-	int w = m_image.width();
-
-	if (dx == 0) {
-	    // haven't moved
-	    return;
-	}
-
-	if (dx <= -w || dx >= w) {
-	    // scrolled entirely off
-	    invalidate();
-	    return;
-	}
-	
-	// dx is in range, cache is scrollable
-
-	int dxp = dx;
-	if (dxp < 0) dxp = -dxp;
-
-	int copylen = (w - dxp) * int(sizeof(QRgb));
-	for (int y = 0; y < m_image.height(); ++y) {
-	    QRgb *line = (QRgb *)m_image.scanLine(y);
-	    if (dx < 0) {
-		memmove(line, line + dxp, copylen);
-	    } else {
-		memmove(line + dxp, line, copylen);
-	    }
-	}
-	
-	// update valid area
-        
-	int px = m_left;
-	int pw = m_width;
-	
-	px += dx;
-	
-	if (dx < 0) {
-	    // we scrolled left
-	    if (px < 0) {
-		pw += px;
-		px = 0;
-		if (pw < 0) {
-		    pw = 0;
-		}
-	    }
-	} else {
-	    // we scrolled right
-	    if (px + pw > w) {
-		pw = w - px;
-		if (pw < 0) {
-		    pw = 0;
-		}
-	    }
-	}
-
-	m_left = px;
-	m_width = pw;
-    }
-
-    void resizeToTouchValidArea(int &left, int &width,
-				bool &isLeftOfValidArea) const {
-	if (left < m_left) {
-	    isLeftOfValidArea = true;
-	    if (left + width < m_left + m_width) {
-		width = m_left - left;
-	    }
-	} else {
-	    isLeftOfValidArea = false;
-	    width = left + width - (m_left + m_width);
-	    left = m_left + m_width;
-	    if (width < 0) width = 0;
-	}
-    }
-    
+    /**
+     * Take a left coordinate and width describing a region, and
+     * adjust them so that they are contiguous with the cache valid
+     * region and so that the union of the adjusted region with the
+     * cache valid region contains the supplied region.
+     */
+    void adjustToTouchValidArea(int &left, int &width,
+				bool &isLeftOfValidArea) const;
+    /**
+     * Draw from an image onto the cache. The supplied image must have
+     * the same height as the cache and the full height is always
+     * drawn. The left and width parameters determine the target
+     * region of the cache, the imageLeft and imageWidth parameters
+     * the source region of the image.
+     */
     void drawImage(int left,
 		   int width,
 		   QImage image,
 		   int imageLeft,
-		   int imageWidth) {
-
-	if (image.height() != m_image.height()) {
-	    throw std::logic_error("Image height must match cache height in ScrollableImageCache::drawImage");
-	}
-	if (left < 0 || left + width > m_image.width()) {
-	    throw std::logic_error("Drawing area out of bounds in ScrollableImageCache::drawImage");
-	}
-	
-	QPainter painter(&m_image);
-	painter.drawImage(QRect(left, 0, width, m_image.height()),
-			  image,
-			  QRect(imageLeft, 0, imageWidth, image.height()));
-	painter.end();
-
-	if (!isValid()) {
-	    m_left = left;
-	    m_width = width;
-	    return;
-	}
-	
-	if (left < m_left) {
-	    if (left + width > m_left + m_width) {
-		// new image completely contains the old valid area --
-		// use the new area as is
-		m_left = left;
-		m_width = width;
-	    } else if (left + width < m_left) {
-		// new image completely off left of old valid area --
-		// we can't extend the valid area because the bit in
-		// between is not valid, so must use the new area only
-		m_left = left;
-		m_width = width;
-	    } else {
-		// new image overlaps old valid area on left side --
-		// use new left edge, and extend width to existing
-		// right edge
-		m_width = (m_left + m_width) - left;
-		m_left = left;
-	    }
-	} else {
-	    if (left > m_left + m_width) {
-		// new image completely off right of old valid area --
-		// we can't extend the valid area because the bit in
-		// between is not valid, so must use the new area only
-		m_left = left;
-		m_width = width;
-	    } else if (left + width > m_left + m_width) {
-		// new image overlaps old valid area on right side --
-		// use existing left edge, and extend width to new
-		// right edge
-		m_width = (left + width) - m_left;
-		// (m_left unchanged)
-	    } else {
-		// new image completely contained within old valid
-		// area -- leave the old area unchanged
-	    }
-	}
-    }
+		   int imageWidth);
     
 private:
     const LayerGeometryProvider *m_v;
--- a/layer/SpectrogramLayer.cpp	Fri Jan 29 15:08:01 2016 +0000
+++ b/layer/SpectrogramLayer.cpp	Fri Jan 29 18:51:05 2016 +0000
@@ -1674,7 +1674,18 @@
 
     if (cache.getZoomLevel() != zoomLevel ||
         cache.getSize() != v->getPaintSize()) {
+#ifdef DEBUG_SPECTROGRAM_REPAINT
+        cerr << "SpectrogramLayer: resizing image cache from "
+             << cache.getSize().width() << "x" << cache.getSize().height()
+             << " to "
+             << v->getPaintSize().width() << "x" << v->getPaintSize().height()
+             << " and updating zoom level from " << cache.getZoomLevel()
+             << " to " << zoomLevel
+             << endl;
+#endif
         cache.resize(v->getPaintSize());
+        cache.setZoomLevel(zoomLevel);
+        cache.setStartFrame(startFrame);
     }
     
     if (cache.isValid()) {
@@ -1706,7 +1717,7 @@
             cache.scrollTo(startFrame);
             
 #ifdef DEBUG_SPECTROGRAM_REPAINT
-            cerr << "SpectrogramLayer: cache valid now from "
+            cerr << "SpectrogramLayer: after scrolling, cache valid from "
                  << cache.getValidLeft() << " width " << cache.getValidWidth()
                  << endl;
 #endif
@@ -1735,7 +1746,7 @@
         int left = x0;
         int width = x1 - x0;
         bool isLeftOfValidArea = false;
-        cache.resizeToTouchValidArea(left, width, isLeftOfValidArea);
+        cache.adjustToTouchValidArea(left, width, isLeftOfValidArea);
         x0 = left;
         x1 = x0 + width;
 
@@ -1895,7 +1906,7 @@
                 binforx[x] = -1; //???
             }
         }
-        if (m_drawBuffer.width() < bufwid || m_drawBuffer.height() < h) {
+        if (m_drawBuffer.width() < bufwid || m_drawBuffer.height() != h) {
             m_drawBuffer = QImage(bufwid, h, QImage::Format_Indexed8);
         }
         usePeaksCache = (increment * 8) < zoomLevel;
@@ -1939,17 +1950,33 @@
     }
 
     int failedToRepaint = bufwid - attainedBufwid;
+
+    int paintedLeft = x0;
+    int paintedWidth = x1 - x0;
+    
     if (failedToRepaint > 0) {
+
 #ifdef DEBUG_SPECTROGRAM_REPAINT
         cerr << "SpectrogramLayer::paint(): Failed to repaint " << failedToRepaint << " of " << bufwid
              << " columns in time (so managed to repaint " << bufwid - failedToRepaint << ")" << endl;
 #endif
+
+        if (rightToLeft) {
+            paintedLeft += failedToRepaint;
+        }
+
+        paintedWidth -= failedToRepaint;
+
+        if (paintedWidth < 0) {
+            paintedWidth = 0;
+        }
+        
     } else if (failedToRepaint < 0) {
         cerr << "WARNING: failedToRepaint < 0 (= " << failedToRepaint << ")"
              << endl;
         failedToRepaint = 0;
     }
-    
+
     if (overallMagChanged) {
         m_viewMags[v->getId()] = overallMag;
 #ifdef DEBUG_SPECTROGRAM_REPAINT
@@ -1961,23 +1988,26 @@
 
     Profiler profiler2("SpectrogramLayer::paint: draw image");
 
-    if (repaintWidth > 0) {
+    if (paintedWidth > 0) {
 
 #ifdef DEBUG_SPECTROGRAM_REPAINT
-        cerr << "SpectrogramLayer: Copying " << repaintWidth << "x" << h
-                  << " from draw buffer at " << 0 << "," << 0
-                  << " to " << repaintWidth << "x" << h << " on cache at "
+        cerr << "SpectrogramLayer: Copying " << paintedWidth << "x" << h
+                  << " from draw buffer at " << paintedLeft - x0 << "," << 0
+                  << " to " << paintedWidth << "x" << h << " on cache at "
                   << x0 << "," << 0 << endl;
 #endif
 
         if (bufferBinResolution) {
+
             int scaledLeft = v->getXForFrame(leftBoundaryFrame);
             int scaledRight = v->getXForFrame(rightBoundaryFrame);
+
 #ifdef DEBUG_SPECTROGRAM_REPAINT
             cerr << "SpectrogramLayer: Rescaling image from " << bufwid
                  << "x" << h << " to "
                  << scaledRight-scaledLeft << "x" << h << endl;
 #endif
+
             Preferences::SpectrogramXSmoothing xsmoothing = 
                 Preferences::getInstance()->getSpectrogramXSmoothing();
 
@@ -1989,11 +2019,14 @@
             
             int scaledLeftCrop = v->getXForFrame(leftCropFrame);
             int scaledRightCrop = v->getXForFrame(rightCropFrame);
+
 #ifdef DEBUG_SPECTROGRAM_REPAINT
             cerr << "SpectrogramLayer: Drawing image region of width " << scaledRightCrop - scaledLeftCrop << " to "
                  << scaledLeftCrop << " from " << scaledLeftCrop - scaledLeft << endl;
 #endif
 
+            //!!! Update this for failedToRepaint logic
+            
             cache.drawImage
                 (scaledLeftCrop,
                  scaledRightCrop - scaledLeftCrop,
@@ -2003,9 +2036,9 @@
 
         } else {
 
-            cache.drawImage(x0, repaintWidth,
+            cache.drawImage(paintedLeft, paintedWidth,
                             m_drawBuffer,
-                            0, repaintWidth);
+                            paintedLeft - x0, paintedWidth);
         }
     }
 
@@ -2029,30 +2062,45 @@
     if (!m_synchronous) {
 
         if ((m_normalization != NormalizeVisibleArea) || !overallMagChanged) {
-    
-            if (cache.getValidLeft() > 0) {
-#ifdef DEBUG_SPECTROGRAM_REPAINT
-                cerr << "SpectrogramLayer::paint() updating left (0, "
-                          << cache.getValidLeft() << ")" << endl;
-#endif
-                v->updatePaintRect(QRect(0, 0, cache.getValidLeft(), h));
+
+            QRect areaLeft(0, 0, cache.getValidLeft(), h);
+            QRect areaRight(cache.getValidRight(), 0,
+                            cache.getSize().width() - cache.getValidRight(), h);
+
+            bool haveSpaceLeft = (areaLeft.width() > 0);
+            bool haveSpaceRight = (areaRight.width() > 0);
+
+            bool updateLeft = haveSpaceLeft;
+            bool updateRight = haveSpaceRight;
+            
+            if (updateLeft && updateRight) {
+                if (rightToLeft) {
+                    // we just did something adjoining the cache on
+                    // its left side, so now do something on its right
+                    updateLeft = false;
+                } else {
+                    updateRight = false;
+                }
             }
             
-            if (cache.getValidRight() <
-                cache.getSize().width()) {
+            if (updateLeft) {
+#ifdef DEBUG_SPECTROGRAM_REPAINT
+                cerr << "SpectrogramLayer::paint() updating left ("
+                     << areaLeft.x() << ", "
+                     << areaLeft.width() << ")" << endl;
+#endif
+                v->updatePaintRect(areaLeft);
+            }
+            
+            if (updateRight) {
 #ifdef DEBUG_SPECTROGRAM_REPAINT
                 cerr << "SpectrogramLayer::paint() updating right ("
-                     << cache.getValidRight()
-                     << ", "
-                     << cache.getSize().width() - cache.getValidRight()
-                     << ")" << endl;
+                     << areaRight.x() << ", "
+                     << areaRight.width() << ")" << endl;
 #endif
-                v->updatePaintRect
-                    (QRect(cache.getValidRight(),
-                           0,
-                           cache.getSize().width() - cache.getValidRight(),
-                           h));
+                v->updatePaintRect(areaRight);
             }
+            
         } else {
             // overallMagChanged
             cerr << "\noverallMagChanged - updating all\n" << endl;
@@ -2209,7 +2257,7 @@
                 if (diff > maxTime) {
 #ifdef DEBUG_SPECTROGRAM_REPAINT
                     cerr << "SpectrogramLayer::paintDrawBufferPeakFrequencies: Max time " << maxTime << " sec exceeded after "
-                         << x << " columns with time " << diff << endl;
+                         << columnCount << " columns with time " << diff << endl;
 #endif
                     return columnCount;
                 }
@@ -2478,7 +2526,7 @@
                 if (diff > maxTime) {
 #ifdef DEBUG_SPECTROGRAM_REPAINT
                     cerr << "SpectrogramLayer::paintDrawBuffer: Max time " << maxTime << " sec exceeded after "
-                         << x << " columns with time " << diff << endl;
+                         << columnCount << " columns with time " << diff << endl;
 #endif
                     return columnCount;
                 }
--- a/svgui.pro	Fri Jan 29 15:08:01 2016 +0000
+++ b/svgui.pro	Fri Jan 29 18:51:05 2016 +0000
@@ -80,6 +80,7 @@
            layer/PaintAssistant.cpp \
            layer/PianoScale.cpp \
            layer/RegionLayer.cpp \
+           layer/ScrollableImageCache.cpp \
            layer/SingleColourLayer.cpp \
            layer/SliceLayer.cpp \
            layer/SpectrogramLayer.cpp \
--- a/view/LayerGeometryProvider.h	Fri Jan 29 15:08:01 2016 +0000
+++ b/view/LayerGeometryProvider.h	Fri Jan 29 18:51:05 2016 +0000
@@ -19,6 +19,7 @@
 
 #include <QMutex>
 #include <QMutexLocker>
+#include <QPainter>
 
 class ViewManager;
 class View;