comparison layer/SpectrogramLayer.cpp @ 1030:0be17aafa935 spectrogram-minor-refactor

Start refactoring out the spectrogram image cache
author Chris Cannam
date Fri, 29 Jan 2016 15:08:01 +0000
parents fdfd84b022df
children 55ac6ac1982e
comparison
equal deleted inserted replaced
1029:fdfd84b022df 1030:0be17aafa935
582 void 582 void
583 SpectrogramLayer::invalidateImageCaches() 583 SpectrogramLayer::invalidateImageCaches()
584 { 584 {
585 for (ViewImageCache::iterator i = m_imageCaches.begin(); 585 for (ViewImageCache::iterator i = m_imageCaches.begin();
586 i != m_imageCaches.end(); ++i) { 586 i != m_imageCaches.end(); ++i) {
587 i->second.validArea = QRect(); 587 i->second.invalidate();
588 }
589 }
590
591 void
592 SpectrogramLayer::invalidateImageCaches(sv_frame_t startFrame, sv_frame_t endFrame)
593 {
594 for (ViewImageCache::iterator i = m_imageCaches.begin();
595 i != m_imageCaches.end(); ++i) {
596
597 //!!! when are views removed from the map? on setLayerDormant?
598 const LayerGeometryProvider *v = i->first;
599
600 #ifdef DEBUG_SPECTROGRAM_REPAINT
601 cerr << "SpectrogramLayer::invalidateImageCaches("
602 << startFrame << ", " << endFrame << "): view range is "
603 << v->getStartFrame() << ", " << v->getEndFrame()
604 << endl;
605
606 cerr << "Valid area was: " << i->second.validArea.x() << ", "
607 << i->second.validArea.y() << " "
608 << i->second.validArea.width() << "x"
609 << i->second.validArea.height() << endl;
610 #endif
611
612 if (int(startFrame) > v->getStartFrame()) {
613 if (startFrame >= v->getEndFrame()) {
614 #ifdef DEBUG_SPECTROGRAM_REPAINT
615 cerr << "Modified start frame is off right of view" << endl;
616 #endif
617 return;
618 }
619 int x = v->getXForFrame(startFrame);
620 #ifdef DEBUG_SPECTROGRAM_REPAINT
621 cerr << "clipping from 0 to " << x-1 << endl;
622 #endif
623 if (x > 1) {
624 i->second.validArea &=
625 QRect(0, 0, x-1, v->getPaintHeight());
626 } else {
627 i->second.validArea = QRect();
628 }
629 } else {
630 if (int(endFrame) < v->getStartFrame()) {
631 #ifdef DEBUG_SPECTROGRAM_REPAINT
632 cerr << "Modified end frame is off left of view" << endl;
633 #endif
634 return;
635 }
636 int x = v->getXForFrame(endFrame);
637 #ifdef DEBUG_SPECTROGRAM_REPAINT
638 cerr << "clipping from " << x+1 << " to " << v->getPaintWidth()
639 << endl;
640 #endif
641 if (x < v->getPaintWidth()) {
642 i->second.validArea &=
643 QRect(x+1, 0, v->getPaintWidth()-(x+1), v->getPaintHeight());
644 } else {
645 i->second.validArea = QRect();
646 }
647 }
648
649 #ifdef DEBUG_SPECTROGRAM_REPAINT
650 cerr << "Valid area is now: " << i->second.validArea.x() << ", "
651 << i->second.validArea.y() << " "
652 << i->second.validArea.width() << "x"
653 << i->second.validArea.height() << endl;
654 #endif
655 } 588 }
656 } 589 }
657 590
658 void 591 void
659 SpectrogramLayer::preferenceChanged(PropertyContainer::PropertyName name) 592 SpectrogramLayer::preferenceChanged(PropertyContainer::PropertyName name)
986 919
987 const View *view = v->getView(); 920 const View *view = v->getView();
988 921
989 invalidateImageCaches(); 922 invalidateImageCaches();
990 923
991 m_imageCaches.erase(view); 924 m_imageCaches.erase(view->getId());
992 925
993 if (m_fftModels.find(view) != m_fftModels.end()) { 926 if (m_fftModels.find(view->getId()) != m_fftModels.end()) {
994 927
995 if (m_sliceableModel == m_fftModels[view]) { 928 if (m_sliceableModel == m_fftModels[view->getId()]) {
996 bool replaced = false; 929 bool replaced = false;
997 for (ViewFFTMap::iterator i = m_fftModels.begin(); 930 for (ViewFFTMap::iterator i = m_fftModels.begin();
998 i != m_fftModels.end(); ++i) { 931 i != m_fftModels.end(); ++i) {
999 if (i->second != m_sliceableModel) { 932 if (i->second != m_sliceableModel) {
1000 emit sliceableModelReplaced(m_sliceableModel, i->second); 933 emit sliceableModelReplaced(m_sliceableModel, i->second);
1003 } 936 }
1004 } 937 }
1005 if (!replaced) emit sliceableModelReplaced(m_sliceableModel, 0); 938 if (!replaced) emit sliceableModelReplaced(m_sliceableModel, 0);
1006 } 939 }
1007 940
1008 delete m_fftModels[view]; 941 delete m_fftModels[view->getId()];
1009 m_fftModels.erase(view); 942 m_fftModels.erase(view->getId());
1010 943
1011 delete m_peakCaches[view]; 944 delete m_peakCaches[view->getId()];
1012 m_peakCaches.erase(view); 945 m_peakCaches.erase(view->getId());
1013 } 946 }
1014 947
1015 } else { 948 } else {
1016 949
1017 Layer::setLayerDormant(v, false); 950 Layer::setLayerDormant(v, false);
1034 { 967 {
1035 #ifdef DEBUG_SPECTROGRAM_REPAINT 968 #ifdef DEBUG_SPECTROGRAM_REPAINT
1036 cerr << "SpectrogramLayer::cacheInvalid(" << from << ", " << to << ")" << endl; 969 cerr << "SpectrogramLayer::cacheInvalid(" << from << ", " << to << ")" << endl;
1037 #endif 970 #endif
1038 971
1039 invalidateImageCaches(from, to); 972 // We used to call invalidateMagnitudes(from, to) to invalidate
973 // only those caches whose views contained some of the (from, to)
974 // range. That's the right thing to do; it has been lost in
975 // pulling out the image cache code, but it might not matter very
976 // much, since the underlying models for spectrogram layers don't
977 // change very often. Let's see.
978 invalidateImageCaches();
1040 invalidateMagnitudes(); 979 invalidateMagnitudes();
1041 } 980 }
1042 981
1043 bool 982 bool
1044 SpectrogramLayer::hasLightBackground() const 983 SpectrogramLayer::hasLightBackground() const
1100 1039
1101 double min = 0.0; 1040 double min = 0.0;
1102 double max = 1.0; 1041 double max = 1.0;
1103 1042
1104 if (m_normalization == NormalizeVisibleArea) { 1043 if (m_normalization == NormalizeVisibleArea) {
1105 min = m_viewMags[v].getMin(); 1044 min = m_viewMags[v->getId()].getMin();
1106 max = m_viewMags[v].getMax(); 1045 max = m_viewMags[v->getId()].getMax();
1107 } else if (m_normalization != NormalizeColumns) { 1046 } else if (m_normalization != NormalizeColumns) {
1108 if (m_colourScale == LinearColourScale //|| 1047 if (m_colourScale == LinearColourScale //||
1109 // m_colourScale == MeterColourScale) { 1048 // m_colourScale == MeterColourScale) {
1110 ) { 1049 ) {
1111 max = 0.1; 1050 max = 0.1;
1508 1447
1509 int fftSize = getFFTSize(v); 1448 int fftSize = getFFTSize(v);
1510 1449
1511 const View *view = v->getView(); 1450 const View *view = v->getView();
1512 1451
1513 if (m_fftModels.find(view) != m_fftModels.end()) { 1452 if (m_fftModels.find(view->getId()) != m_fftModels.end()) {
1514 if (m_fftModels[view] == 0) { 1453 if (m_fftModels[view->getId()] == 0) {
1515 #ifdef DEBUG_SPECTROGRAM_REPAINT 1454 #ifdef DEBUG_SPECTROGRAM_REPAINT
1516 cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found null model" << endl; 1455 cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found null model" << endl;
1517 #endif 1456 #endif
1518 return 0; 1457 return 0;
1519 } 1458 }
1520 if (m_fftModels[view]->getHeight() != fftSize / 2 + 1) { 1459 if (m_fftModels[view->getId()]->getHeight() != fftSize / 2 + 1) {
1521 #ifdef DEBUG_SPECTROGRAM_REPAINT 1460 #ifdef DEBUG_SPECTROGRAM_REPAINT
1522 cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found a model with the wrong height (" << m_fftModels[view]->getHeight() << ", wanted " << (fftSize / 2 + 1) << ")" << endl; 1461 cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found a model with the wrong height (" << m_fftModels[view->getId()]->getHeight() << ", wanted " << (fftSize / 2 + 1) << ")" << endl;
1523 #endif 1462 #endif
1524 delete m_fftModels[view]; 1463 delete m_fftModels[view->getId()];
1525 m_fftModels.erase(view); 1464 m_fftModels.erase(view->getId());
1526 delete m_peakCaches[view]; 1465 delete m_peakCaches[view->getId()];
1527 m_peakCaches.erase(view); 1466 m_peakCaches.erase(view->getId());
1528 } else { 1467 } else {
1529 #ifdef DEBUG_SPECTROGRAM_REPAINT 1468 #ifdef DEBUG_SPECTROGRAM_REPAINT
1530 cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found a good model of height " << m_fftModels[view]->getHeight() << endl; 1469 cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found a good model of height " << m_fftModels[view->getId()]->getHeight() << endl;
1531 #endif 1470 #endif
1532 return m_fftModels[view]; 1471 return m_fftModels[view->getId()];
1533 } 1472 }
1534 } 1473 }
1535 1474
1536 if (m_fftModels.find(view) == m_fftModels.end()) { 1475 if (m_fftModels.find(view->getId()) == m_fftModels.end()) {
1537 1476
1538 FFTModel *model = new FFTModel(m_model, 1477 FFTModel *model = new FFTModel(m_model,
1539 m_channel, 1478 m_channel,
1540 m_windowType, 1479 m_windowType,
1541 m_windowSize, 1480 m_windowSize,
1546 QMessageBox::critical 1485 QMessageBox::critical
1547 (0, tr("FFT cache failed"), 1486 (0, tr("FFT cache failed"),
1548 tr("Failed to create the FFT model for this spectrogram.\n" 1487 tr("Failed to create the FFT model for this spectrogram.\n"
1549 "There may be insufficient memory or disc space to continue.")); 1488 "There may be insufficient memory or disc space to continue."));
1550 delete model; 1489 delete model;
1551 m_fftModels[view] = 0; 1490 m_fftModels[view->getId()] = 0;
1552 return 0; 1491 return 0;
1553 } 1492 }
1554 1493
1555 if (!m_sliceableModel) { 1494 if (!m_sliceableModel) {
1556 #ifdef DEBUG_SPECTROGRAM 1495 #ifdef DEBUG_SPECTROGRAM
1558 #endif 1497 #endif
1559 ((SpectrogramLayer *)this)->sliceableModelReplaced(0, model); 1498 ((SpectrogramLayer *)this)->sliceableModelReplaced(0, model);
1560 m_sliceableModel = model; 1499 m_sliceableModel = model;
1561 } 1500 }
1562 1501
1563 m_fftModels[view] = model; 1502 m_fftModels[view->getId()] = model;
1564 } 1503 }
1565 1504
1566 return m_fftModels[view]; 1505 return m_fftModels[view->getId()];
1567 } 1506 }
1568 1507
1569 Dense3DModelPeakCache * 1508 Dense3DModelPeakCache *
1570 SpectrogramLayer::getPeakCache(const LayerGeometryProvider *v) const 1509 SpectrogramLayer::getPeakCache(const LayerGeometryProvider *v) const
1571 { 1510 {
1572 const View *view = v->getView(); 1511 const View *view = v->getView();
1573 if (!m_peakCaches[view]) { 1512 if (!m_peakCaches[view->getId()]) {
1574 FFTModel *f = getFFTModel(v); 1513 FFTModel *f = getFFTModel(v);
1575 if (!f) return 0; 1514 if (!f) return 0;
1576 m_peakCaches[view] = new Dense3DModelPeakCache(f, 8); 1515 m_peakCaches[view->getId()] = new Dense3DModelPeakCache(f, 8);
1577 } 1516 }
1578 return m_peakCaches[view]; 1517 return m_peakCaches[view->getId()];
1579 } 1518 }
1580 1519
1581 const Model * 1520 const Model *
1582 SpectrogramLayer::getSliceableModel() const 1521 SpectrogramLayer::getSliceableModel() const
1583 { 1522 {
1654 cerr << "SpectrogramLayer::updateViewMagnitudes returning from cols " 1593 cerr << "SpectrogramLayer::updateViewMagnitudes returning from cols "
1655 << s0 << " -> " << s1 << " inclusive" << endl; 1594 << s0 << " -> " << s1 << " inclusive" << endl;
1656 #endif 1595 #endif
1657 1596
1658 if (!mag.isSet()) return false; 1597 if (!mag.isSet()) return false;
1659 if (mag == m_viewMags[v]) return false; 1598 if (mag == m_viewMags[v->getId()]) return false;
1660 m_viewMags[v] = mag; 1599 m_viewMags[v->getId()] = mag;
1661 return true; 1600 return true;
1662 } 1601 }
1663 1602
1664 void 1603 void
1665 SpectrogramLayer::setSynchronousPainting(bool synchronous) 1604 SpectrogramLayer::setSynchronousPainting(bool synchronous)
1666 { 1605 {
1667 m_synchronous = synchronous; 1606 m_synchronous = synchronous;
1668 } 1607 }
1669 1608
1609 ScrollableImageCache &
1610 SpectrogramLayer::getImageCacheReference(const LayerGeometryProvider *view) const
1611 {
1612 if (m_imageCaches.find(view->getId()) == m_imageCaches.end()) {
1613 m_imageCaches[view->getId()] = ScrollableImageCache(view);
1614 }
1615 return m_imageCaches.at(view->getId());
1616 }
1617
1670 void 1618 void
1671 SpectrogramLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const 1619 SpectrogramLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
1672 { 1620 {
1673 // What a lovely, old-fashioned function this is.
1674 // It's practically FORTRAN 77 in its clarity and linearity.
1675
1676 Profiler profiler("SpectrogramLayer::paint", false); 1621 Profiler profiler("SpectrogramLayer::paint", false);
1677 1622
1678 #ifdef DEBUG_SPECTROGRAM_REPAINT 1623 #ifdef DEBUG_SPECTROGRAM_REPAINT
1679 cerr << "SpectrogramLayer::paint() entering: m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << endl; 1624 cerr << "SpectrogramLayer::paint() entering: m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << endl;
1680 1625
1699 const_cast<SpectrogramLayer *>(this)->Layer::setLayerDormant(v, false); 1644 const_cast<SpectrogramLayer *>(this)->Layer::setLayerDormant(v, false);
1700 1645
1701 int fftSize = getFFTSize(v); 1646 int fftSize = getFFTSize(v);
1702 1647
1703 const View *view = v->getView(); 1648 const View *view = v->getView();
1704 1649 ScrollableImageCache &cache = getImageCacheReference(view);
1705 ImageCache &cache = m_imageCaches[view];
1706 1650
1707 #ifdef DEBUG_SPECTROGRAM_REPAINT 1651 #ifdef DEBUG_SPECTROGRAM_REPAINT
1708 cerr << "SpectrogramLayer::paint(): image cache valid area from " << cache.validArea.x() << "," << cache.validArea.y() << ", " << cache.validArea.width() << "x" << cache.validArea.height() << endl; 1652 cerr << "SpectrogramLayer::paint(): image cache valid area from " << cache.getValidLeft() << " width " << cache.getValidWidth() << ", height " << cache.getSize().height() << endl;
1653 if (rect.x() + rect.width() + 1 < cache.getValidLeft() ||
1654 rect.x() > cache.getValidRight()) {
1655 cerr << "SpectrogramLayer: NOTE: requested rect is not contiguous with cache valid area" << endl;
1656 }
1709 #endif 1657 #endif
1710 1658
1711 int zoomLevel = v->getZoomLevel(); 1659 int zoomLevel = v->getZoomLevel();
1712 1660
1713 int x0 = 0; 1661 int x0 = v->getXForViewX(rect.x());
1714 int x1 = v->getPaintWidth(); 1662 int x1 = v->getXForViewX(rect.x() + rect.width());
1715 1663 if (x0 < 0) x0 = 0;
1716 bool recreateWholeImageCache = true; 1664 if (x1 > v->getPaintWidth()) x1 = v->getPaintWidth();
1717
1718 x0 = rect.left();
1719 x1 = rect.right() + 1;
1720 1665
1721 if (updateViewMagnitudes(v)) { 1666 if (updateViewMagnitudes(v)) {
1722 #ifdef DEBUG_SPECTROGRAM_REPAINT 1667 #ifdef DEBUG_SPECTROGRAM_REPAINT
1723 cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << endl; 1668 cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v->getId()].getMin() << "->" << m_viewMags[v->getId()].getMax() << "]" << endl;
1724 #endif 1669 #endif
1725 if (m_normalization == NormalizeVisibleArea) { 1670 if (m_normalization == NormalizeVisibleArea) {
1726 cache.validArea = QRect(); 1671 cache.invalidate();
1727 } 1672 }
1728 } 1673 }
1729 1674
1730 if (cache.validArea.width() > 0) { 1675 if (cache.getZoomLevel() != zoomLevel ||
1731 1676 cache.getSize() != v->getPaintSize()) {
1732 int cw = cache.image.width(); 1677 cache.resize(v->getPaintSize());
1733 int ch = cache.image.height(); 1678 }
1679
1680 if (cache.isValid()) {
1734 1681
1735 if (int(cache.zoomLevel) == zoomLevel && 1682 if (v->getXForFrame(cache.getStartFrame()) ==
1736 cw == v->getPaintWidth() && 1683 v->getXForFrame(startFrame) &&
1737 ch == v->getPaintHeight()) { 1684 cache.getValidLeft() <= x0 &&
1738 1685 cache.getValidRight() >= x1) {
1739 // cache size and zoom level exactly match the view
1740
1741 if (v->getXForFrame(cache.startFrame) ==
1742 v->getXForFrame(startFrame) &&
1743 cache.validArea.x() <= x0 &&
1744 cache.validArea.x() + cache.validArea.width() >= x1) {
1745
1746 // and cache begins at the right frame, so use it whole
1747 1686
1748 #ifdef DEBUG_SPECTROGRAM_REPAINT 1687 #ifdef DEBUG_SPECTROGRAM_REPAINT
1749 cerr << "SpectrogramLayer: image cache good" << endl; 1688 cerr << "SpectrogramLayer: image cache hit!" << endl;
1750 #endif 1689 #endif
1751 1690
1752 paint.drawImage(rect, cache.image, rect); 1691 paint.drawImage(rect, cache.getImage(), rect);
1753 1692
1754 illuminateLocalFeatures(v, paint); 1693 illuminateLocalFeatures(v, paint);
1755 return; 1694 return;
1756 1695
1757 } else { 1696 } else {
1758 1697
1759 // cache doesn't begin at the right frame or doesn't 1698 // cache doesn't begin at the right frame or doesn't
1760 // contain the complete view, but might be scrollable 1699 // contain the complete view, but might be scrollable or
1761 // or partially usable 1700 // partially usable
1762 1701
1763 #ifdef DEBUG_SPECTROGRAM_REPAINT 1702 #ifdef DEBUG_SPECTROGRAM_REPAINT
1764 cerr << "SpectrogramLayer: image cache partially OK" << endl; 1703 cerr << "SpectrogramLayer: scrolling the image cache if applicable" << endl;
1765 #endif 1704 #endif
1766 1705
1767 recreateWholeImageCache = false; 1706 cache.scrollTo(startFrame);
1768 1707
1769 int dx = v->getXForFrame(cache.startFrame) -
1770 v->getXForFrame(startFrame);
1771
1772 #ifdef DEBUG_SPECTROGRAM_REPAINT 1708 #ifdef DEBUG_SPECTROGRAM_REPAINT
1773 cerr << "SpectrogramLayer: dx = " << dx << " (image cache " << cw << "x" << ch << ")" << endl; 1709 cerr << "SpectrogramLayer: cache valid now from "
1774 #endif 1710 << cache.getValidLeft() << " width " << cache.getValidWidth()
1775 1711 << endl;
1776 if (dx != 0 && 1712 #endif
1777 dx > -cw && 1713 }
1778 dx < cw) { 1714 }
1779 1715
1780 // cache is scrollable, scroll it 1716 bool rightToLeft = false;
1781 1717
1782 int dxp = dx; 1718 if (!cache.isValid()) {
1783 if (dxp < 0) dxp = -dxp;
1784 size_t copy = (cw - dxp) * sizeof(QRgb);
1785 for (int y = 0; y < ch; ++y) {
1786 QRgb *line = (QRgb *)cache.image.scanLine(y);
1787 if (dx < 0) {
1788 memmove(line, line + dxp, copy);
1789 } else {
1790 memmove(line + dxp, line, copy);
1791 }
1792 }
1793
1794 // and calculate its new valid area
1795
1796 int px = cache.validArea.x();
1797 int pw = cache.validArea.width();
1798
1799 // so px and pw will be updated to the new x and
1800 // width of the valid area of cache;
1801
1802 // x0 and x1 will be the left and right extents of
1803 // the area needing repainted
1804
1805 px += dx;
1806
1807 if (dx < 0) {
1808 // we scrolled left
1809 if (px < 0) {
1810 pw += px;
1811 px = 0;
1812 if (pw < 0) {
1813 pw = 0;
1814 }
1815 }
1816 x0 = px + pw;
1817 x1 = cw;
1818 } else {
1819 // we scrolled right
1820 if (px + pw > cw) {
1821 pw = cw - px;
1822 if (pw < 0) {
1823 pw = 0;
1824 }
1825 }
1826 x0 = 0;
1827 x1 = px;
1828 }
1829
1830 cache.validArea =
1831 QRect(px, cache.validArea.y(),
1832 pw, cache.validArea.height());
1833
1834 if (cache.validArea.width() == 0) {
1835 recreateWholeImageCache = true;
1836 }
1837
1838 #ifdef DEBUG_SPECTROGRAM_REPAINT
1839 cerr << "SpectrogramLayer: valid area now "
1840 << px << "," << cache.validArea.y()
1841 << " " << pw << "x" << cache.validArea.height()
1842 << endl;
1843 #endif
1844
1845 } else if (dx != 0) {
1846
1847 // we've moved too far from the cached area for it
1848 // to be of use
1849
1850 #ifdef DEBUG_SPECTROGRAM_REPAINT
1851 cerr << "SpectrogramLayer: dx == " << dx << ": scrolled too far for cache to be useful" << endl;
1852 #endif
1853
1854 cache.validArea = QRect();
1855 recreateWholeImageCache = true;
1856
1857 } else {
1858
1859 // dx == 0, we haven't scrolled but the cache is
1860 // only partly valid
1861
1862 #ifdef DEBUG_SPECTROGRAM_REPAINT
1863 cerr << "SpectrogramLayer: haven't scrolled, but cache is not complete" << endl;
1864 #endif
1865 if (cache.validArea.x() == 0) {
1866 x0 = cache.validArea.width();
1867 } else {
1868 x1 = cache.validArea.x();
1869 }
1870 }
1871 }
1872 } else {
1873 #ifdef DEBUG_SPECTROGRAM_REPAINT
1874 cerr << "SpectrogramLayer: image cache useless" << endl;
1875 if (int(cache.zoomLevel) != zoomLevel) {
1876 cerr << "(cache zoomLevel " << cache.zoomLevel
1877 << " != " << zoomLevel << ")" << endl;
1878 }
1879 if (cw != v->getPaintWidth()) {
1880 cerr << "(cache width " << cw
1881 << " != " << v->getPaintWidth();
1882 }
1883 if (ch != v->getPaintHeight()) {
1884 cerr << "(cache height " << ch
1885 << " != " << v->getPaintHeight();
1886 }
1887 #endif
1888 cache.validArea = QRect();
1889 }
1890 }
1891
1892 if (recreateWholeImageCache) {
1893 if (!m_synchronous) { 1719 if (!m_synchronous) {
1894 // When rendering the whole thing, start from somewhere near 1720 // When rendering the whole thing, start from somewhere near
1895 // the middle so that the region of interest appears first 1721 // the middle so that the region of interest appears first
1896 x0 = int(v->getPaintWidth() * 0.4); 1722 if (x0 == 0 && x1 == v->getPaintWidth()) {
1897 } else { 1723 x0 = int(x1 * 0.4);
1898 x0 = 0; 1724 }
1899 } 1725 }
1900 x1 = v->getPaintWidth(); 1726 } else {
1901 } 1727 // When rendering only a part of the cache, we need to make
1902 1728 // sure that the part we're rendering is adjacent to (or
1729 // overlapping) a valid area of cache, if we have one. The
1730 // alternative is to ditch the valid area of cache and render
1731 // only the requested area, but that's risky because this can
1732 // happen when just waving the pointer over a small part of
1733 // the view -- if we lose the partly-built cache every time
1734 // the user does that, we'll never finish building it.
1735 int left = x0;
1736 int width = x1 - x0;
1737 bool isLeftOfValidArea = false;
1738 cache.resizeToTouchValidArea(left, width, isLeftOfValidArea);
1739 x0 = left;
1740 x1 = x0 + width;
1741
1742 // That call also told us whether we should be painting
1743 // sub-regions of our target region in right-to-left order in
1744 // order to ensure contiguity
1745 rightToLeft = isLeftOfValidArea;
1746 }
1747
1903 // We always paint the full height when refreshing the cache. 1748 // We always paint the full height when refreshing the cache.
1904 // Smaller heights can be used when painting direct from cache 1749 // Smaller heights can be used when painting direct from cache
1905 // (further up in this function), but we want to ensure the cache 1750 // (further up in this function), but we want to ensure the cache
1906 // is coherent without having to worry about vertical matching of 1751 // is coherent without having to worry about vertical matching of
1907 // required and valid areas as well as horizontal. 1752 // required and valid areas as well as horizontal.
1908
1909 int h = v->getPaintHeight(); 1753 int h = v->getPaintHeight();
1910 1754
1911 int repaintWidth = x1 - x0; 1755 int repaintWidth = x1 - x0;
1912 1756
1913 // If we are painting a section to the left of a valid area of
1914 // cache, then we must paint it "backwards", right-to-left. This
1915 // is because painting may be interrupted by a timeout, leaving us
1916 // with a partially painted area, and we have no way to record
1917 // that the union of the existing cached area and this new
1918 // partially painted bit is a valid cache area unless they are
1919 // adjacent (because our valid extent is a single x,width range).
1920
1921 bool rightToLeft = (x0 == 0 && x1 < v->getPaintWidth());
1922
1923 #ifdef DEBUG_SPECTROGRAM_REPAINT 1757 #ifdef DEBUG_SPECTROGRAM_REPAINT
1924 cerr << "SpectrogramLayer: x0 " << x0 << ", x1 " << x1 1758 cerr << "SpectrogramLayer: x0 " << x0 << ", x1 " << x1
1925 << ", repaintWidth " << repaintWidth << ", h " << h 1759 << ", repaintWidth " << repaintWidth << ", h " << h
1926 << ", rightToLeft " << rightToLeft 1760 << ", rightToLeft " << rightToLeft << endl;
1927 << ", recreateWholeImageCache " << recreateWholeImageCache << endl;
1928 #endif 1761 #endif
1929 1762
1930 sv_samplerate_t sr = m_model->getSampleRate(); 1763 sv_samplerate_t sr = m_model->getSampleRate();
1931 1764
1932 // Set minFreq and maxFreq to the frequency extents of the possibly 1765 // Set minFreq and maxFreq to the frequency extents of the possibly
1973 1806
1974 int increment = getWindowIncrement(); 1807 int increment = getWindowIncrement();
1975 1808
1976 bool logarithmic = (m_frequencyScale == LogFrequencyScale); 1809 bool logarithmic = (m_frequencyScale == LogFrequencyScale);
1977 1810
1978 MagnitudeRange overallMag = m_viewMags[v]; 1811 MagnitudeRange overallMag = m_viewMags[v->getId()];
1979 bool overallMagChanged = false; 1812 bool overallMagChanged = false;
1980 1813
1981 #ifdef DEBUG_SPECTROGRAM_REPAINT 1814 #ifdef DEBUG_SPECTROGRAM_REPAINT
1982 cerr << "SpectrogramLayer: " << ((double(v->getFrameForX(1) - v->getFrameForX(0))) / increment) << " bin(s) per pixel" << endl; 1815 cerr << "SpectrogramLayer: " << ((double(v->getFrameForX(1) - v->getFrameForX(0))) / increment) << " bin(s) per pixel" << endl;
1983 #endif 1816 #endif
2116 << endl; 1949 << endl;
2117 failedToRepaint = 0; 1950 failedToRepaint = 0;
2118 } 1951 }
2119 1952
2120 if (overallMagChanged) { 1953 if (overallMagChanged) {
2121 m_viewMags[v] = overallMag; 1954 m_viewMags[v->getId()] = overallMag;
2122 #ifdef DEBUG_SPECTROGRAM_REPAINT 1955 #ifdef DEBUG_SPECTROGRAM_REPAINT
2123 cerr << "SpectrogramLayer: Overall mag is now [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "] - will be updating" << endl; 1956 cerr << "SpectrogramLayer: Overall mag is now [" << m_viewMags[v->getId()].getMin() << "->" << m_viewMags[v->getId()].getMax() << "] - will be updating" << endl;
2124 #endif 1957 #endif
2125 } 1958 }
2126 1959
2127 outerprof.end(); 1960 outerprof.end();
2128 1961
2129 Profiler profiler2("SpectrogramLayer::paint: draw image"); 1962 Profiler profiler2("SpectrogramLayer::paint: draw image");
2130
2131 if (recreateWholeImageCache) {
2132 #ifdef DEBUG_SPECTROGRAM_REPAINT
2133 cerr << "SpectrogramLayer: Recreating image cache: width = " << v->getPaintWidth()
2134 << ", height = " << h << endl;
2135 #endif
2136 cache.image = QImage(v->getPaintWidth(), h, QImage::Format_ARGB32_Premultiplied);
2137 }
2138 1963
2139 if (repaintWidth > 0) { 1964 if (repaintWidth > 0) {
2140 1965
2141 #ifdef DEBUG_SPECTROGRAM_REPAINT 1966 #ifdef DEBUG_SPECTROGRAM_REPAINT
2142 cerr << "SpectrogramLayer: Copying " << repaintWidth << "x" << h 1967 cerr << "SpectrogramLayer: Copying " << repaintWidth << "x" << h
2143 << " from draw buffer at " << 0 << "," << 0 1968 << " from draw buffer at " << 0 << "," << 0
2144 << " to " << repaintWidth << "x" << h << " on cache at " 1969 << " to " << repaintWidth << "x" << h << " on cache at "
2145 << x0 << "," << 0 << endl; 1970 << x0 << "," << 0 << endl;
2146 #endif 1971 #endif
2147
2148 QPainter cachePainter(&cache.image);
2149 1972
2150 if (bufferBinResolution) { 1973 if (bufferBinResolution) {
2151 int scaledLeft = v->getXForFrame(leftBoundaryFrame); 1974 int scaledLeft = v->getXForFrame(leftBoundaryFrame);
2152 int scaledRight = v->getXForFrame(rightBoundaryFrame); 1975 int scaledRight = v->getXForFrame(rightBoundaryFrame);
2153 #ifdef DEBUG_SPECTROGRAM_REPAINT 1976 #ifdef DEBUG_SPECTROGRAM_REPAINT
2168 int scaledRightCrop = v->getXForFrame(rightCropFrame); 1991 int scaledRightCrop = v->getXForFrame(rightCropFrame);
2169 #ifdef DEBUG_SPECTROGRAM_REPAINT 1992 #ifdef DEBUG_SPECTROGRAM_REPAINT
2170 cerr << "SpectrogramLayer: Drawing image region of width " << scaledRightCrop - scaledLeftCrop << " to " 1993 cerr << "SpectrogramLayer: Drawing image region of width " << scaledRightCrop - scaledLeftCrop << " to "
2171 << scaledLeftCrop << " from " << scaledLeftCrop - scaledLeft << endl; 1994 << scaledLeftCrop << " from " << scaledLeftCrop - scaledLeft << endl;
2172 #endif 1995 #endif
2173 cachePainter.drawImage 1996
2174 (QRect(scaledLeftCrop, 0, 1997 cache.drawImage
2175 scaledRightCrop - scaledLeftCrop, h), 1998 (scaledLeftCrop,
1999 scaledRightCrop - scaledLeftCrop,
2176 scaled, 2000 scaled,
2177 QRect(scaledLeftCrop - scaledLeft, 0, 2001 scaledLeftCrop - scaledLeft,
2178 scaledRightCrop - scaledLeftCrop, h)); 2002 scaledRightCrop - scaledLeftCrop);
2179 2003
2180 } else { 2004 } else {
2181 2005
2182 cachePainter.drawImage(QRect(x0, 0, repaintWidth, h), 2006 cache.drawImage(x0, repaintWidth,
2183 m_drawBuffer, 2007 m_drawBuffer,
2184 QRect(0, 0, repaintWidth, h)); 2008 0, repaintWidth);
2185 } 2009 }
2186 2010 }
2187 cachePainter.end(); 2011
2188 }
2189
2190 // update cache valid area based on painted area
2191
2192 int left = x0;
2193 int wid = x1 - x0;
2194
2195 if (failedToRepaint > 0) {
2196 #ifdef DEBUG_SPECTROGRAM_REPAINT 2012 #ifdef DEBUG_SPECTROGRAM_REPAINT
2197 cerr << "SpectrogramLayer: Reduced painted extent from " 2013 cerr << "SpectrogramLayer: Cache valid area now from " << cache.getValidLeft()
2198 << left << "," << wid; 2014 << " width " << cache.getValidWidth() << ", height "
2199 #endif 2015 << cache.getSize().height() << endl;
2200 if (rightToLeft) { 2016 #endif
2201 left += failedToRepaint; 2017
2202 } 2018 QRect pr = rect & cache.getValidArea();
2203 wid -= failedToRepaint;
2204
2205 if (wid < 0) wid = 0;
2206
2207 #ifdef DEBUG_SPECTROGRAM_REPAINT
2208 cerr << " to " << left << "," << wid << endl;
2209 #endif
2210 }
2211
2212 if (cache.validArea.width() > 0) {
2213 left = min(left, cache.validArea.x());
2214 wid = cache.validArea.width() + wid;
2215 }
2216
2217 cache.validArea = QRect(left, 0, wid, h);
2218
2219 #ifdef DEBUG_SPECTROGRAM_REPAINT
2220 cerr << "SpectrogramLayer: Cache valid area becomes " << cache.validArea.x()
2221 << ", " << cache.validArea.y() << ", "
2222 << cache.validArea.width() << "x"
2223 << cache.validArea.height() << " (size = "
2224 << cache.image.width() << "x" << cache.image.height() << ")"
2225 << endl;
2226 #endif
2227
2228 QRect pr = rect & cache.validArea;
2229 2019
2230 #ifdef DEBUG_SPECTROGRAM_REPAINT 2020 #ifdef DEBUG_SPECTROGRAM_REPAINT
2231 cerr << "SpectrogramLayer: Copying " << pr.width() << "x" << pr.height() 2021 cerr << "SpectrogramLayer: Copying " << pr.width() << "x" << pr.height()
2232 << " from cache at " << pr.x() << "," << pr.y() 2022 << " from cache at " << pr.x() << "," << pr.y()
2233 << " to window" << endl; 2023 << " to window" << endl;
2234 #endif 2024 #endif
2235 2025
2236 paint.drawImage(pr.x(), pr.y(), cache.image, 2026 paint.drawImage(pr.x(), pr.y(), cache.getImage(),
2237 pr.x(), pr.y(), pr.width(), pr.height()); 2027 pr.x(), pr.y(), pr.width(), pr.height());
2238 2028
2239 cache.startFrame = startFrame;
2240 cache.zoomLevel = zoomLevel;
2241
2242 if (!m_synchronous) { 2029 if (!m_synchronous) {
2243 2030
2244 if ((m_normalization != NormalizeVisibleArea) || !overallMagChanged) { 2031 if ((m_normalization != NormalizeVisibleArea) || !overallMagChanged) {
2245 2032
2246 if (cache.validArea.x() > 0) { 2033 if (cache.getValidLeft() > 0) {
2247 #ifdef DEBUG_SPECTROGRAM_REPAINT 2034 #ifdef DEBUG_SPECTROGRAM_REPAINT
2248 cerr << "SpectrogramLayer::paint() updating left (0, " 2035 cerr << "SpectrogramLayer::paint() updating left (0, "
2249 << cache.validArea.x() << ")" << endl; 2036 << cache.getValidLeft() << ")" << endl;
2250 #endif 2037 #endif
2251 v->getView()->update(0, 0, cache.validArea.x(), h); 2038 v->updatePaintRect(QRect(0, 0, cache.getValidLeft(), h));
2252 } 2039 }
2253 2040
2254 if (cache.validArea.x() + cache.validArea.width() < 2041 if (cache.getValidRight() <
2255 cache.image.width()) { 2042 cache.getSize().width()) {
2256 #ifdef DEBUG_SPECTROGRAM_REPAINT 2043 #ifdef DEBUG_SPECTROGRAM_REPAINT
2257 cerr << "SpectrogramLayer::paint() updating right (" 2044 cerr << "SpectrogramLayer::paint() updating right ("
2258 << cache.validArea.x() + cache.validArea.width() 2045 << cache.getValidRight()
2259 << ", " 2046 << ", "
2260 << cache.image.width() - (cache.validArea.x() + 2047 << cache.getSize().width() - cache.getValidRight()
2261 cache.validArea.width()) 2048 << ")" << endl;
2262 << ")" << endl; 2049 #endif
2263 #endif 2050 v->updatePaintRect
2264 v->getView()->update 2051 (QRect(cache.getValidRight(),
2265 (cache.validArea.x() + cache.validArea.width(), 2052 0,
2266 0, 2053 cache.getSize().width() - cache.getValidRight(),
2267 cache.image.width() - (cache.validArea.x() + 2054 h));
2268 cache.validArea.width()),
2269 h);
2270 } 2055 }
2271 } else { 2056 } else {
2272 // overallMagChanged 2057 // overallMagChanged
2273 cerr << "\noverallMagChanged - updating all\n" << endl; 2058 cerr << "\noverallMagChanged - updating all\n" << endl;
2274 cache.validArea = QRect(); 2059 cache.invalidate();
2275 v->getView()->update(); 2060 v->updatePaintRect(v->getPaintRect());
2276 } 2061 }
2277 } 2062 }
2278 2063
2279 illuminateLocalFeatures(v, paint); 2064 illuminateLocalFeatures(v, paint);
2280 2065
2764 int 2549 int
2765 SpectrogramLayer::getCompletion(LayerGeometryProvider *v) const 2550 SpectrogramLayer::getCompletion(LayerGeometryProvider *v) const
2766 { 2551 {
2767 const View *view = v->getView(); 2552 const View *view = v->getView();
2768 2553
2769 if (m_fftModels.find(view) == m_fftModels.end()) return 100; 2554 if (m_fftModels.find(view->getId()) == m_fftModels.end()) return 100;
2770 2555
2771 int completion = m_fftModels[view]->getCompletion(); 2556 int completion = m_fftModels[view->getId()]->getCompletion();
2772 #ifdef DEBUG_SPECTROGRAM_REPAINT 2557 #ifdef DEBUG_SPECTROGRAM_REPAINT
2773 cerr << "SpectrogramLayer::getCompletion: completion = " << completion << endl; 2558 cerr << "SpectrogramLayer::getCompletion: completion = " << completion << endl;
2774 #endif 2559 #endif
2775 return completion; 2560 return completion;
2776 } 2561 }
2777 2562
2778 QString 2563 QString
2779 SpectrogramLayer::getError(LayerGeometryProvider *v) const 2564 SpectrogramLayer::getError(LayerGeometryProvider *v) const
2780 { 2565 {
2781 const View *view = v->getView(); 2566 const View *view = v->getView();
2782 if (m_fftModels.find(view) == m_fftModels.end()) return ""; 2567 if (m_fftModels.find(view->getId()) == m_fftModels.end()) return "";
2783 return m_fftModels[view]->getError(); 2568 return m_fftModels[view->getId()]->getError();
2784 } 2569 }
2785 2570
2786 bool 2571 bool
2787 SpectrogramLayer::getValueExtents(double &min, double &max, 2572 SpectrogramLayer::getValueExtents(double &min, double &max,
2788 bool &logarithmic, QString &unit) const 2573 bool &logarithmic, QString &unit) const
2874 2659
2875 void 2660 void
2876 SpectrogramLayer::measureDoubleClick(LayerGeometryProvider *v, QMouseEvent *e) 2661 SpectrogramLayer::measureDoubleClick(LayerGeometryProvider *v, QMouseEvent *e)
2877 { 2662 {
2878 const View *view = v->getView(); 2663 const View *view = v->getView();
2879 ImageCache &cache = m_imageCaches[view]; 2664 ScrollableImageCache &cache = getImageCacheReference(view);
2880 2665
2881 cerr << "cache width: " << cache.image.width() << ", height: " 2666 cerr << "cache width: " << cache.getSize().width() << ", height: "
2882 << cache.image.height() << endl; 2667 << cache.getSize().height() << endl;
2883 2668
2884 QImage image = cache.image; 2669 QImage image = cache.getImage();
2885 2670
2886 ImageRegionFinder finder; 2671 ImageRegionFinder finder;
2887 QRect rect = finder.findRegionExtents(&image, e->pos()); 2672 QRect rect = finder.findRegionExtents(&image, e->pos());
2888 if (rect.isValid()) { 2673 if (rect.isValid()) {
2889 MeasureRect mr; 2674 MeasureRect mr;
3194 int ch = h - textHeight * (topLines + 1) - 8; 2979 int ch = h - textHeight * (topLines + 1) - 8;
3195 // paint.drawRect(4, textHeight + 4, cw - 1, ch + 1); 2980 // paint.drawRect(4, textHeight + 4, cw - 1, ch + 1);
3196 paint.drawRect(4 + cw - cbw, textHeight * topLines + 4, cbw - 1, ch + 1); 2981 paint.drawRect(4 + cw - cbw, textHeight * topLines + 4, cbw - 1, ch + 1);
3197 2982
3198 QString top, bottom; 2983 QString top, bottom;
3199 double min = m_viewMags[v].getMin(); 2984 double min = m_viewMags[v->getId()].getMin();
3200 double max = m_viewMags[v].getMax(); 2985 double max = m_viewMags[v->getId()].getMax();
3201 2986
3202 double dBmin = AudioLevel::multiplier_to_dB(min); 2987 double dBmin = AudioLevel::multiplier_to_dB(min);
3203 double dBmax = AudioLevel::multiplier_to_dB(max); 2988 double dBmax = AudioLevel::multiplier_to_dB(max);
3204 2989
3205 if (dBmax < -60.f) dBmax = -60.f; 2990 if (dBmax < -60.f) dBmax = -60.f;