# HG changeset patch # User Chris Cannam # Date 1454093465 0 # Node ID 55ac6ac1982e092bfb3a4ae0bc6f977b6cb514aa # Parent 0be17aafa9351dda8b30679fe1de7f23005c9391 Further fixes to the scrollable cache logic diff -r 0be17aafa935 -r 55ac6ac1982e layer/ScrollableImageCache.h --- 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; diff -r 0be17aafa935 -r 55ac6ac1982e layer/SpectrogramLayer.cpp --- 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; } diff -r 0be17aafa935 -r 55ac6ac1982e svgui.pro --- 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 \ diff -r 0be17aafa935 -r 55ac6ac1982e view/LayerGeometryProvider.h --- 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 #include +#include class ViewManager; class View;