changeset 332:6440e280122e spectrogram-cache-rejig

* Some bits and bobs of cache mangling -- closer to working, but still not
author Chris Cannam
date Mon, 19 Nov 2007 15:50:37 +0000
parents 846746e4e865
children 64e84e5efb76
files layer/SpectrogramLayer.cpp layer/SpectrogramLayer.h
diffstat 2 files changed, 262 insertions(+), 187 deletions(-) [+]
line wrap: on
line diff
--- a/layer/SpectrogramLayer.cpp	Fri Nov 16 17:15:30 2007 +0000
+++ b/layer/SpectrogramLayer.cpp	Mon Nov 19 15:50:37 2007 +0000
@@ -1756,13 +1756,16 @@
 
     rect = v->rect(); //!!!
 
+    float ratio = float(v->getZoomLevel()) / float(getWindowIncrement());
+    int imageWidth = lrintf(v->width() * ratio);
+
     PixmapCache *cache = m_pixmapCaches[v];
     if (!cache) {
         cache = new PixmapCache;
         cache->pixmap = 0;
         m_pixmapCaches[v] = cache;
         cache->mutex.lock();
-        cache->pixmap = new QImage(v->width(), v->height(),
+        cache->pixmap = new QImage(imageWidth, v->height(),
                                    QImage::Format_RGB32);
         cache->mutex.unlock();
 #ifdef DEBUG_SPECTROGRAM_REPAINT
@@ -1773,23 +1776,41 @@
         return; //!!! prod paint thread
     }
 
+    long myStartFrame = v->getStartFrame();
+    long cacheStartFrame = cache->startFrame;
+    long xoff = (myStartFrame - cacheStartFrame) / getWindowIncrement();
+
+    QRect sourceRect = QRect(lrintf(rect.x() * ratio) + xoff,
+                             rect.y(),
+                             lrintf(rect.width() * ratio),
+                             rect.height());
+
     if (!cache->mutex.tryLock()) {
         std::cerr << "lock unavailable" << std::endl;
-        paint.drawImage(rect,// & cache->validArea,
-                        *cache->pixmap,
-                        rect);// & cache->validArea);
+
+        paint.drawImage(rect,
+                        cache->pixmap->copy(sourceRect)
+                        .scaled(rect.size(),
+                                Qt::IgnoreAspectRatio,
+                                Qt::SmoothTransformation));
+
+//        paint.drawImage(rect,// & cache->validArea,
+//                        *cache->pixmap,
+//                        rect);// & cache->validArea);
 //        v->update(rect);
         return;
     }
 
-    if (cache->pixmap->width() != v->width() ||
+    if (cache->pixmap->width() != imageWidth ||
         cache->pixmap->height() != v->height()) {
         QImage *newImage = new QImage
-            (cache->pixmap->scaled(v->width(), v->height()));
+            (cache->pixmap->scaled(imageWidth, v->height(),
+                                   Qt::IgnoreAspectRatio,
+                                   Qt::SmoothTransformation));
 #ifdef DEBUG_SPECTROGRAM_REPAINT
         std::cerr << "SpectrogramLayer::paint: Rescaling cache to size "
-                  << v->width() << "x" << v->height() << " ("
-                  << v->width() * v->height() * 4 << " bytes)" << std::endl;
+                  << imageWidth << "x" << v->height() << " ("
+                  << imageWidth * v->height() * 4 << " bytes)" << std::endl;
 #endif
         delete cache->pixmap;
         cache->pixmap = newImage;
@@ -1805,19 +1826,8 @@
     //source or target rect will have edges at non-integral
     //coordinates
 
-    float ratio = float(v->getZoomLevel()) / float(getWindowIncrement());
-
-    long myStartFrame = v->getStartFrame();
-    long cacheStartFrame = cache->startFrame;
-    long xoff = (myStartFrame - cacheStartFrame) / getWindowIncrement();
-
     std::cerr << "my start frame = " << myStartFrame << ", cache start frame = " << cacheStartFrame << std::endl;
 
-    QRect sourceRect = QRect(lrintf(rect.x() * ratio) + xoff,
-                             rect.y(),
-                             lrintf(rect.width() * ratio),
-                             rect.height());
-
     std::cerr << "ratio = " << ratio << " (zoom " << v->getZoomLevel()
               << ", incr " << getWindowIncrement() << "); source rect "
               << sourceRect.x() << "," << sourceRect.y()
@@ -1830,8 +1840,12 @@
               << std::endl;
 
     paint.drawImage(rect,// & cache->validArea,
-                    *cache->pixmap,
-                    sourceRect);// & cache->validArea);
+                    cache->pixmap->copy(sourceRect)
+                    .scaled(rect.size(),
+                            Qt::IgnoreAspectRatio,
+                            Qt::SmoothTransformation));
+//                    *cache->pixmap,
+//                    sourceRect);// & cache->validArea);
 
     if (cache->startFrame != v->getStartFrame()) {
 #ifdef DEBUG_SPECTROGRAM_REPAINT
@@ -1893,7 +1907,7 @@
 
         if (!m_exiting) {
             //!!! wait on condition
-            if (workToDo) usleep(100);
+            if (workToDo) ; // usleep(100);
             else sleep(1);
         }
     }
@@ -2007,134 +2021,6 @@
     int x0 = rect.left();
     int x1 = rect.right() + 1;
 
-    if (cache.validArea.width() > 0) {
-
-        //!!! no, the gui thread will handle resizes -- we just draw
-        //the area that's available in the cache.  If the image is
-        //enlarged, our valid area should remain the same and we'll
-        //pick up on the discrepancy naturally (we hope)
-//	if (cache.pixmap->width() == v->width() &&
-//	    cache.pixmap->height() == v->height()) {
-
-	    if (cache.startFrame / increment == startFrame / increment &&
-                cache.validArea.x() <= x0 &&
-                cache.validArea.x() + cache.validArea.width() >= x1) {
-	    
-#ifdef DEBUG_SPECTROGRAM_REPAINT
-		std::cerr << "SpectrogramLayer: pixmap cache good" << std::endl;
-#endif
-
-                //!!! set a flag to that effect?
-
-		return true;
-
-	    } else {
-
-#ifdef DEBUG_SPECTROGRAM_REPAINT
-		std::cerr << "SpectrogramLayer: pixmap cache partially OK" << std::endl;
-#endif
-
-		recreateWholeCache = false;
-
-		int dx = cache.startFrame / increment - startFrame / increment;
-
-#ifdef DEBUG_SPECTROGRAM_REPAINT
-		std::cerr << "SpectrogramLayer: dx = " << dx << " (pixmap cache " << cache.pixmap->width() << "x" << cache.pixmap->height() << ")" << std::endl;
-#endif
-
-		if (dx != 0 && dx > -w && dx < w) {
-
-#if defined(Q_WS_WIN32) || defined(Q_WS_MAC)
-		    // Copying a pixmap to itself doesn't work
-		    // properly on Windows or Mac (it only works when
-		    // moving in one direction).
-
-		    //!!! Need a utility function for this
-
-                    //!!! test again on Win and OS/X for images (above
-                    //comment applies to pixmaps)
-
-                    //!!! indeed, test again on linux for images too
-                    //-- I bet it doesn't work
-
-		    static QImage *tmpPixmap = 0;
-		    if (!tmpPixmap ||
-			tmpPixmap->width() != cache.pixmap->width() ||
-			tmpPixmap->height() != cache.pixmap->height()) {
-			delete tmpPixmap;
-			tmpPixmap = new QImage(cache.pixmap->width(),
-                                               cache.pixmap->height(),
-                                               QImage::Format_RGB32);
-		    }
-		    QPainter tmpPainter;
-		    tmpPainter.begin(tmpPixmap);
-		    tmpPainter.drawImage(0, 0, image);
-		    tmpPainter.end();
-                    paint.drawImage(dx, 0, *tmpPixmap);
-#else
-                    paint.drawImage(dx, 0, image); //!!! see above
-#endif
-
-                    int px = cache.validArea.x();
-                    int pw = cache.validArea.width();
-
-		    if (dx < 0) {
-			x0 = w + dx;
-			x1 = w;
-                        px += dx;
-                        if (px < 0) {
-                            pw += px;
-                            px = 0;
-                            if (pw < 0) pw = 0;
-                        }
-		    } else {
-			x0 = 0;
-			x1 = dx;
-                        px += dx;
-                        if (px + pw > w) {
-                            pw = int(w) - px;
-                            if (pw < 0) pw = 0;
-                        }
-		    }
-                    
-                    cache.validArea =
-                        QRect(px, cache.validArea.y(),
-                              pw, cache.validArea.height());
-		}
-	    }
-//	} else {
-//#ifdef DEBUG_SPECTROGRAM_REPAINT
-//	    std::cerr << "SpectrogramLayer: pixmap cache useless" << std::endl;
-//            if (cache.pixmap->width() != v->width()) {
-//                std::cerr << "(cache width " << cache.pixmap->width()
-//                          << " != " << v->width();
-//            }
-//            if (cache.pixmap->height() != v->height()) {
-//                std::cerr << "(cache height " << cache.pixmap->height()
-//                          << " != " << v->height();
-//            }
-//#endif
-//            cache.validArea = QRect();
-//	}
-}
-
-/*!!!
-    if (updateViewMagnitudes(v)) {
-#ifdef DEBUG_SPECTROGRAM_REPAINT
-        std::cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl;
-#endif
-        recreateWholeCache = true;
-    } else {
-#ifdef DEBUG_SPECTROGRAM_REPAINT
-        std::cerr << "No change in magnitude range [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl;
-#endif
-    }
-*/
-    if (recreateWholeCache) {
-        x0 = 0;
-        x1 = w;
-    }
-
     struct timeval tv;
     (void)gettimeofday(&tv, 0);
     RealTime mainPaintStart = RealTime::fromTimeval(tv);
@@ -2163,6 +2049,12 @@
     std::cerr << "[" << this << "]: last paint width: " << m_lastPaintBlockWidth << ", last paint time: " << m_lastPaintTime << ", new paint width: " << paintBlockWidth << std::endl;
 #endif
 
+    recreateWholeCache = checkAndScrollCache(cache, paint,
+                                             startFrame, increment,
+                                             x0, x1);
+
+    selectPaintStrip(cache, x0, x1, paintBlockWidth);
+
     // We always paint the full height when refreshing the cache.
     // Smaller heights can be used when painting from cache, but we
     // want to ensure the cache is coherent without having to worry
@@ -2245,6 +2137,8 @@
 
     bool runOutOfData = false;
 
+    std::cerr << "painting from " << x0 << " to " << x1 << std::endl;
+
     for (int x = 0; x < (x1 - x0); ++x) {
 
         if (runOutOfData) break;
@@ -2456,55 +2350,22 @@
     }
 */
     if (cache.validArea.width() > 0) {
-
+        
         int vx0 = 0, vx1 = 0;
         vx0 = cache.validArea.x();
         vx1 = cache.validArea.x() + cache.validArea.width();
         
-#ifdef DEBUG_SPECTROGRAM_REPAINT
-        std::cerr << "x0 " << x0 << ", x1 " << x1 << ", vx0 " << vx0 << ", vx1 " << vx1 << ", paintBlockWidth " << paintBlockWidth << std::endl;
-#endif            
-        if (x0 < vx0) {
-            if (x0 + paintBlockWidth < vx0) {
-                x0 = vx0 - paintBlockWidth;
-            } else {
-                x0 = 0;
-            }
-        } else if (x0 > vx1) {
-            x0 = vx1;
-        }
-            
-        if (x1 < vx0) {
-            x1 = vx0;
-        } else if (x1 > vx1) {
-            if (vx1 + paintBlockWidth < x1) {
-                x1 = vx1 + paintBlockWidth;
-            } else {
-                x1 = v->width();
-            }
-        }
-            
         cache.validArea = QRect
             (std::min(vx0, x0), cache.validArea.y(),
              std::max(vx1 - std::min(vx0, x0),
                        x1 - std::min(vx0, x0)),
              cache.validArea.height());
-            
+
     } else {
-        if (x1 > x0 + paintBlockWidth) {
-            int sfx = x1;
-            if (startFrame < 0) sfx = v->getXForFrame(0);            //!!!
-            if (sfx >= x0 && sfx + paintBlockWidth <= x1) {
-                x0 = sfx;
-                x1 = x0 + paintBlockWidth;
-            } else {
-                int mid = (x1 + x0) / 2;
-                x0 = mid - paintBlockWidth/2;
-                x1 = x0 + paintBlockWidth;
-            }
-        }
+        
         cache.validArea = QRect(x0, 0, x1 - x0, h);
     }
+        
 /*
     Profiler profiler2("SpectrogramLayer::paintCache: draw image", true);
 
@@ -2589,6 +2450,214 @@
 //    if (fftSuspended) fft->resume();
 }
 
+bool
+SpectrogramLayer::checkAndScrollCache(PixmapCache &cache, QPainter &paint,
+                                      long startFrame, int increment,
+                                      int &x0, int &x1) const
+{
+    int w = cache.pixmap->width();
+
+    if (cache.validArea.width() == 0) {
+        x0 = 0;
+        x1 = w;
+        return true; // redraw everything
+    } 
+
+    //!!! no, the gui thread will handle resizes -- we just draw
+    //the area that's available in the cache.  If the image is
+    //enlarged, our valid area should remain the same and we'll
+    //pick up on the discrepancy naturally (we hope)
+//	if (cache.pixmap->width() == v->width() &&
+//	    cache.pixmap->height() == v->height()) {
+
+    if (cache.startFrame / increment == startFrame / increment &&
+        cache.validArea.x() <= x0 &&
+        cache.validArea.x() + cache.validArea.width() >= x1) {
+	    
+#ifdef DEBUG_SPECTROGRAM_REPAINT
+        std::cerr << "SpectrogramLayer: pixmap cache good" << std::endl;
+#endif
+
+        //!!! set a flag to that effect?
+
+        x1 = x0;
+        return false;
+
+    } else {
+
+#ifdef DEBUG_SPECTROGRAM_REPAINT
+        std::cerr << "SpectrogramLayer: pixmap cache partially OK" << std::endl;
+#endif
+
+        int dx = cache.startFrame / increment - startFrame / increment;
+
+#ifdef DEBUG_SPECTROGRAM_REPAINT
+        std::cerr << "SpectrogramLayer: dx = " << dx << " (pixmap cache " << cache.pixmap->width() << "x" << cache.pixmap->height() << ")" << std::endl;
+#endif
+
+        if (dx != 0 && dx > -w && dx < w) {
+
+//#if defined(Q_WS_WIN32) || defined(Q_WS_MAC)
+            // Copying a pixmap to itself doesn't work
+            // properly on Windows or Mac (it only works when
+            // moving in one direction).
+
+            //!!! Need a utility function for this
+
+            //!!! test again on Win and OS/X for images (above
+            //comment applies to pixmaps)
+
+            //!!! indeed, test again on linux for images too
+            //-- I bet it doesn't work
+
+            std::cerr << "SpectrogramLayer: copying from " << 0
+                      << " to " << dx << std::endl;
+
+            static QImage *tmp = 0; //!!! would need to be thread-local
+            if (!tmp ||
+                tmp->width() != w ||
+                tmp->height() != cache.pixmap->height()) {
+                delete tmp;
+                tmp = new QImage(w,
+                                 cache.pixmap->height(),
+                                 QImage::Format_RGB32);
+            }
+            QPainter tmpPainter;
+            tmpPainter.begin(tmp);
+            tmpPainter.drawImage(0, 0, *cache.pixmap);
+            tmpPainter.end();
+            paint.drawImage(dx, 0, *tmp);
+//#else
+//                    paint.drawImage(dx, 0, image); //!!! see above
+//#endif
+
+            int px = cache.validArea.x();
+            int pw = cache.validArea.width();
+
+            if (dx < 0) {
+                x0 = w + dx;
+                x1 = w;
+                px += dx;
+                if (px < 0) {
+                    pw += px;
+                    px = 0;
+                    if (pw < 0) pw = 0;
+                }
+            } else {
+                x0 = 0;
+                x1 = dx;
+                px += dx;
+                if (px + pw > w) {
+                    pw = int(w) - px;
+                    if (pw < 0) pw = 0;
+                }
+            }
+                    
+            cache.validArea =
+                QRect(px, cache.validArea.y(),
+                      pw, cache.validArea.height());
+        }
+    }
+//	} else {
+//#ifdef DEBUG_SPECTROGRAM_REPAINT
+//	    std::cerr << "SpectrogramLayer: pixmap cache useless" << std::endl;
+//            if (cache.pixmap->width() != v->width()) {
+//                std::cerr << "(cache width " << cache.pixmap->width()
+//                          << " != " << v->width();
+//            }
+//            if (cache.pixmap->height() != v->height()) {
+//                std::cerr << "(cache height " << cache.pixmap->height()
+//                          << " != " << v->height();
+//            }
+//#endif
+//            cache.validArea = QRect();
+//	}
+
+/*!!!
+  if (updateViewMagnitudes(v)) {
+  #ifdef DEBUG_SPECTROGRAM_REPAINT
+  std::cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl;
+  #endif
+  recreateWholeCache = true;
+  } else {
+  #ifdef DEBUG_SPECTROGRAM_REPAINT
+  std::cerr << "No change in magnitude range [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl;
+  #endif
+  }
+*/
+    return false;
+}
+
+void
+SpectrogramLayer::selectPaintStrip(PixmapCache &cache, int &x0, int &x1,
+                                   int paintBlockWidth) const
+{
+    // This function narrows down the region [x0, x1) where x1 >= x0
+    // to a strip of approximately paintBlockWidth width.
+
+    if (x1 - x0 < paintBlockWidth * 3 / 2) return; // it'll do
+
+    if (cache.validArea.width() > 0) {
+
+        // If part of the cache is known to be valid, select a strip
+        // immediately to left or right of the valid part
+
+        int vx0 = 0, vx1 = 0; // valid extents
+        vx0 = cache.validArea.x();
+        vx1 = cache.validArea.x() + cache.validArea.width();
+        
+#ifdef DEBUG_SPECTROGRAM_REPAINT
+        std::cerr << "x0 " << x0 << ", x1 " << x1 << ", vx0 " << vx0 << ", vx1 " << vx1 << ", paintBlockWidth " << paintBlockWidth << std::endl;
+#endif            
+        if (x0 < vx0) {
+            if (x0 + paintBlockWidth < vx0) {
+                x0 = vx0 - paintBlockWidth;
+//            } else {
+//                x0 = 0;
+            }
+            x1 = vx0;
+        } else if (x0 >= vx1) {
+            x0 = vx1;
+            if (x1 > x0 + paintBlockWidth) {
+                x1 = x0 + paintBlockWidth;
+            }
+        } else {
+            // x0 is within the valid area
+            if (x1 > vx1) {
+                x0 = vx1;
+                if (x0 + paintBlockWidth < x1) {
+                    x1 = x0 + paintBlockWidth;
+                }
+            } else {
+                x1 = x0; // it's all valid, paint nothing
+            }
+        }
+
+    } else {
+
+        // No existing valid area; start at the middle, unless x0 is
+        // already to the left of frame 0
+
+        //!!! no, for now just start at the middle always
+
+        if (x1 > x0 + paintBlockWidth) {
+
+//            int sfx = x1; // x for frame zero
+//            if (startFrame < 0) sfx = v->getXForFrame(0);            //!!!
+
+//            if (startFrame < 0) sfx = 
+//            if (sfx >= x0 && sfx + paintBlockWidth <= x1) {
+//                x0 = sfx;
+//                x1 = x0 + paintBlockWidth;
+//            } else {
+                int mid = (x1 + x0) / 2;
+                x0 = mid - paintBlockWidth/2;
+                x1 = x0 + paintBlockWidth;
+//            }
+        }
+    }
+}
+
 void
 SpectrogramLayer::illuminateLocalFeatures(View *v, QPainter &paint) const
 {
--- a/layer/SpectrogramLayer.h	Fri Nov 16 17:15:30 2007 +0000
+++ b/layer/SpectrogramLayer.h	Mon Nov 19 15:50:37 2007 +0000
@@ -354,6 +354,12 @@
 
     PaintThread *m_paintThread;
     bool paintCache(View *v) const;
+    bool checkAndScrollCache(PixmapCache &cache, QPainter &paint,
+                             long startFrame, int increment,
+                             int &x0, int &x1) const;
+    void selectPaintStrip(PixmapCache &cache,
+                          int &x0, int &x1,
+                          int paintBlockWidth) const;
 
     typedef std::pair<FFTModel *, int> FFTFillPair; // model, last fill
     typedef std::map<const View *, FFTFillPair> ViewFFTMap;