Mercurial > hg > svgui
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; |