comparison view/View.cpp @ 1357:93eaff6f206d

Rework cacheing logic to reduce the number of reallocations and be more correct about the repaint areas. I don't expect the difference to be really noticeable but in theory performance should be a little better...
author Chris Cannam
date Thu, 11 Oct 2018 14:59:34 +0100
parents b9bfcb8cd5a1
children bbeffb29bf09
comparison
equal deleted inserted replaced
1356:dddfd28e4f02 1357:93eaff6f206d
18 #include "data/model/Model.h" 18 #include "data/model/Model.h"
19 #include "base/ZoomConstraint.h" 19 #include "base/ZoomConstraint.h"
20 #include "base/Profiler.h" 20 #include "base/Profiler.h"
21 #include "base/Pitch.h" 21 #include "base/Pitch.h"
22 #include "base/Preferences.h" 22 #include "base/Preferences.h"
23 #include "base/HitCount.h"
23 #include "ViewProxy.h" 24 #include "ViewProxy.h"
24 25
25 #include "layer/TimeRulerLayer.h" 26 #include "layer/TimeRulerLayer.h"
26 #include "layer/SingleColourLayer.h" 27 #include "layer/SingleColourLayer.h"
27 #include "layer/PaintAssistant.h" 28 #include "layer/PaintAssistant.h"
61 m_followPlayIsDetached(false), 62 m_followPlayIsDetached(false),
62 m_playPointerFrame(0), 63 m_playPointerFrame(0),
63 m_showProgress(showProgress), 64 m_showProgress(showProgress),
64 m_cache(0), 65 m_cache(0),
65 m_buffer(0), 66 m_buffer(0),
67 m_cacheValid(false),
66 m_cacheCentreFrame(0), 68 m_cacheCentreFrame(0),
67 m_cacheZoomLevel(ZoomLevel::FramesPerPixel, 1024), 69 m_cacheZoomLevel(ZoomLevel::FramesPerPixel, 1024),
68 m_selectionCached(false), 70 m_selectionCached(false),
69 m_deleting(false), 71 m_deleting(false),
70 m_haveSelectedLayer(false), 72 m_haveSelectedLayer(false),
258 update(); 260 update();
259 } 261 }
260 return; 262 return;
261 } 263 }
262 264
263 delete m_cache; 265 m_cacheValid = false;
264 m_cache = 0;
265 266
266 Layer *selectedLayer = 0; 267 Layer *selectedLayer = 0;
267 268
268 for (LayerList::iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) { 269 for (LayerList::iterator i = m_layerStack.begin(); i != m_layerStack.end(); ++i) {
269 if (*i == pc) { 270 if (*i == pc) {
291 } 292 }
292 293
293 void 294 void
294 View::overlayModeChanged() 295 View::overlayModeChanged()
295 { 296 {
296 delete m_cache; 297 m_cacheValid = false;
297 m_cache = 0;
298 update(); 298 update();
299 } 299 }
300 300
301 void 301 void
302 View::zoomWheelsEnabledChanged() 302 View::zoomWheelsEnabledChanged()
615 } 615 }
616 616
617 void 617 void
618 View::addLayer(Layer *layer) 618 View::addLayer(Layer *layer)
619 { 619 {
620 delete m_cache; 620 m_cacheValid = false;
621 m_cache = 0;
622 621
623 SingleColourLayer *scl = dynamic_cast<SingleColourLayer *>(layer); 622 SingleColourLayer *scl = dynamic_cast<SingleColourLayer *>(layer);
624 if (scl) scl->setDefaultColourFor(this); 623 if (scl) scl->setDefaultColourFor(this);
625 624
626 m_fixedOrderLayers.push_back(layer); 625 m_fixedOrderLayers.push_back(layer);
687 { 686 {
688 if (m_deleting) { 687 if (m_deleting) {
689 return; 688 return;
690 } 689 }
691 690
692 delete m_cache; 691 m_cacheValid = false;
693 m_cache = 0;
694 692
695 for (LayerList::iterator i = m_fixedOrderLayers.begin(); 693 for (LayerList::iterator i = m_fixedOrderLayers.begin();
696 i != m_fixedOrderLayers.end(); 694 i != m_fixedOrderLayers.end();
697 ++i) { 695 ++i) {
698 if (*i == layer) { 696 if (*i == layer) {
905 break; 903 break;
906 } 904 }
907 } 905 }
908 906
909 if (recreate) { 907 if (recreate) {
910 delete m_cache; 908 m_cacheValid = false;
911 m_cache = 0;
912 } 909 }
913 910
914 emit layerModelChanged(); 911 emit layerModelChanged();
915 912
916 checkProgress(obj); 913 checkProgress(obj);
953 break; 950 break;
954 } 951 }
955 } 952 }
956 953
957 if (recreate) { 954 if (recreate) {
958 delete m_cache; 955 m_cacheValid = false;
959 m_cache = 0;
960 } 956 }
961 957
962 if (startFrame < myStartFrame) startFrame = myStartFrame; 958 if (startFrame < myStartFrame) startFrame = myStartFrame;
963 if (endFrame > myEndFrame) endFrame = myEndFrame; 959 if (endFrame > myEndFrame) endFrame = myEndFrame;
964 960
989 View::modelReplaced() 985 View::modelReplaced()
990 { 986 {
991 #ifdef DEBUG_VIEW_WIDGET_PAINT 987 #ifdef DEBUG_VIEW_WIDGET_PAINT
992 cerr << "View(" << this << ")::modelReplaced()" << endl; 988 cerr << "View(" << this << ")::modelReplaced()" << endl;
993 #endif 989 #endif
994 delete m_cache; 990 m_cacheValid = false;
995 m_cache = 0;
996
997 update(); 991 update();
998 } 992 }
999 993
1000 void 994 void
1001 View::layerParametersChanged() 995 View::layerParametersChanged()
1004 998
1005 #ifdef DEBUG_VIEW_WIDGET_PAINT 999 #ifdef DEBUG_VIEW_WIDGET_PAINT
1006 SVDEBUG << "View::layerParametersChanged()" << endl; 1000 SVDEBUG << "View::layerParametersChanged()" << endl;
1007 #endif 1001 #endif
1008 1002
1009 delete m_cache; 1003 m_cacheValid = false;
1010 m_cache = 0;
1011 update(); 1004 update();
1012 1005
1013 if (layer) { 1006 if (layer) {
1014 emit propertyContainerPropertyChanged(layer); 1007 emit propertyContainerPropertyChanged(layer);
1015 } 1008 }
1201 1194
1202 void 1195 void
1203 View::selectionChanged() 1196 View::selectionChanged()
1204 { 1197 {
1205 if (m_selectionCached) { 1198 if (m_selectionCached) {
1206 delete m_cache; 1199 m_cacheValid = false;
1207 m_cache = 0;
1208 m_selectionCached = false; 1200 m_selectionCached = false;
1209 } 1201 }
1210 update(); 1202 update();
1211 } 1203 }
1212 1204
1801 1793
1802 // ensure our constraints are met 1794 // ensure our constraints are met
1803 m_zoomLevel = getZoomConstraintLevel 1795 m_zoomLevel = getZoomConstraintLevel
1804 (m_zoomLevel, ZoomConstraint::RoundNearest); 1796 (m_zoomLevel, ZoomConstraint::RoundNearest);
1805 1797
1806 QPainter paint; 1798 // We have a cache, which retains the state of scrollable (back)
1807 bool repaintCache = false; 1799 // layers from one paint to the next, and a buffer, which we paint
1808 bool paintedCacheRect = false; 1800 // onto before copying directly to the widget. Both are at scaled
1809 1801 // resolution (e.g. 2x on a pixel-doubled display), whereas the
1810 QRect cacheRect(rect()); 1802 // paint event always comes in at formal (1x) resolution.
1811 1803
1804 // If we touch the cache, we always leave it in a valid state
1805 // across its whole extent. When another method invalidates the
1806 // cache, it does so by setting m_cacheValid false, so if that
1807 // flag is true on entry, then the cache is valid across its whole
1808 // extent - although it may be valid for a different centre frame,
1809 // zoom level, or view size from those now in effect.
1810
1811 // Our process goes:
1812 //
1813 // 1. Check whether we have any scrollable (cacheable) layers. If
1814 // we don't, then invalidate and ignore the cache and go to
1815 // step 5. Otherwise:
1816 //
1817 // 2. Check the cache, scroll as necessary, identify any area that
1818 // needs to be refreshed (this might be the whole cache).
1819 //
1820 // 3. Paint to cache the area that needs to be refreshed, from the
1821 // stack of scrollable layers.
1822 //
1823 // 4. Paint to buffer from cache: if there are no non-cached areas
1824 // or selections and the cache has not scrolled, then paint the
1825 // union of the area of cache that has changed and the area
1826 // that the paint event reported as exposed; otherwise paint
1827 // the whole.
1828 //
1829 // 5. Paint the exposed area to the buffer from the cache plus all
1830 // the layers that haven't been cached, plus selections etc.
1831 //
1832 // 6. Paint the exposed rect from the buffer.
1833 //
1834 // Note that all rects except the target for the final step are at
1835 // cache (scaled, 2x as applicable) resolution.
1836
1837 int dpratio = effectiveDevicePixelRatio();
1838
1839 QRect requestedPaintArea(scaledRect(rect(), dpratio));
1812 if (e) { 1840 if (e) {
1813 cacheRect &= e->rect(); 1841 // cut down to only the area actually exposed
1814 #ifdef DEBUG_VIEW_WIDGET_PAINT 1842 requestedPaintArea &= scaledRect(e->rect(), dpratio);
1815 cerr << "paint rect " << cacheRect.width() << "x" << cacheRect.height() 1843 }
1816 << ", my rect " << width() << "x" << height() << endl;
1817 #endif
1818 }
1819
1820 QRect nonCacheRect(cacheRect);
1821
1822 int dpratio = effectiveDevicePixelRatio();
1823 1844
1824 // If not all layers are scrollable, but some of the back layers 1845 // If not all layers are scrollable, but some of the back layers
1825 // are, we should store only those in the cache. 1846 // are, we should store only those in the cache.
1826 1847
1827 bool layersChanged = false; 1848 bool layersChanged = false;
1828 LayerList scrollables = getScrollableBackLayers(true, layersChanged); 1849 LayerList scrollables = getScrollableBackLayers(true, layersChanged);
1829 LayerList nonScrollables = getNonScrollableFrontLayers(true, layersChanged); 1850 LayerList nonScrollables = getNonScrollableFrontLayers(true, layersChanged);
1830 bool selectionCacheable = nonScrollables.empty();
1831 bool haveSelections = m_manager && !m_manager->getSelections().empty();
1832
1833 // If all the non-scrollable layers are non-opaque, then we draw
1834 // the selection rectangle behind them and cache it. If any are
1835 // opaque, however, or if our device-pixel ratio is not 1 (so we
1836 // need to paint direct to the widget), then we can't cache.
1837 //
1838 if (dpratio == 1) {
1839
1840 if (!selectionCacheable) {
1841 selectionCacheable = true;
1842 for (LayerList::const_iterator i = nonScrollables.begin();
1843 i != nonScrollables.end(); ++i) {
1844 if ((*i)->isLayerOpaque()) {
1845 selectionCacheable = false;
1846 break;
1847 }
1848 }
1849 }
1850
1851 if (selectionCacheable) {
1852 QPoint localPos;
1853 bool closeToLeft, closeToRight;
1854 if (shouldIlluminateLocalSelection
1855 (localPos, closeToLeft, closeToRight)) {
1856 selectionCacheable = false;
1857 }
1858 }
1859
1860 } else {
1861
1862 selectionCacheable = false;
1863 }
1864 1851
1865 #ifdef DEBUG_VIEW_WIDGET_PAINT 1852 #ifdef DEBUG_VIEW_WIDGET_PAINT
1866 cerr << "View(" << this << ")::paintEvent: have " << scrollables.size() 1853 cerr << "View(" << this << ")::paintEvent: have " << scrollables.size()
1867 << " scrollable back layers and " << nonScrollables.size() 1854 << " scrollable back layers and " << nonScrollables.size()
1868 << " non-scrollable front layers" << endl; 1855 << " non-scrollable front layers" << endl;
1869 cerr << "haveSelections " << haveSelections << ", selectionCacheable " 1856 #endif
1870 << selectionCacheable << ", m_selectionCached " << m_selectionCached << endl; 1857
1871 #endif 1858 if (layersChanged || scrollables.empty()) {
1872 1859 m_cacheValid = false;
1873 if (layersChanged || scrollables.empty() || 1860 }
1874 (haveSelections && (selectionCacheable != m_selectionCached))) { 1861
1875 delete m_cache; 1862 QRect wholeArea(scaledRect(rect(), dpratio));
1876 m_cache = 0; 1863 QSize wholeSize(scaledSize(size(), dpratio));
1877 m_selectionCached = false; 1864
1878 } 1865 if (!m_buffer || wholeSize != m_buffer->size()) {
1879
1880 QSize scaledCacheSize(scaledSize(size(), dpratio));
1881 QRect scaledCacheRect(scaledRect(cacheRect, dpratio));
1882
1883 if (!m_buffer || scaledCacheSize != m_buffer->size()) {
1884 delete m_buffer; 1866 delete m_buffer;
1885 m_buffer = new QPixmap(scaledCacheSize); 1867 m_buffer = new QPixmap(wholeSize);
1886 } 1868 }
1869
1870 bool shouldUseCache = false;
1871 bool shouldRepaintCache = false;
1872 QRect cacheAreaToRepaint;
1887 1873
1874 static HitCount count("View cache");
1875
1888 if (!scrollables.empty()) { 1876 if (!scrollables.empty()) {
1877
1878 shouldUseCache = true;
1879 shouldRepaintCache = true;
1880 cacheAreaToRepaint = wholeArea;
1889 1881
1890 #ifdef DEBUG_VIEW_WIDGET_PAINT 1882 #ifdef DEBUG_VIEW_WIDGET_PAINT
1891 cerr << "View(" << this << "): cache " << m_cache << ", cache zoom " 1883 cerr << "View(" << this << "): cache " << m_cache << ", cache zoom "
1892 << m_cacheZoomLevel << ", zoom " << m_zoomLevel << endl; 1884 << m_cacheZoomLevel << ", zoom " << m_zoomLevel << endl;
1893 #endif 1885 #endif
1894 1886
1895 using namespace std::rel_ops; 1887 using namespace std::rel_ops;
1896 1888
1897 if (!m_cache || 1889 if (!m_cacheValid ||
1890 !m_cache ||
1898 m_cacheZoomLevel != m_zoomLevel || 1891 m_cacheZoomLevel != m_zoomLevel ||
1899 scaledCacheSize != m_cache->size()) { 1892 m_cache->size() != wholeSize) {
1900 1893
1901 // cache is not valid 1894 // cache is not valid at all
1902 1895
1903 if (cacheRect.width() < width()/10) { 1896 if (requestedPaintArea.width() < wholeSize.width() / 10) {
1904 delete m_cache; 1897
1905 m_cache = 0; 1898 m_cacheValid = false;
1899 shouldUseCache = false;
1900 shouldRepaintCache = false;
1901
1906 #ifdef DEBUG_VIEW_WIDGET_PAINT 1902 #ifdef DEBUG_VIEW_WIDGET_PAINT
1907 cerr << "View(" << this << ")::paintEvent: small repaint, not bothering to recreate cache" << endl; 1903 cerr << "View(" << this << ")::paintEvent: cache is invalid but only small area requested, will repaint directly instead" << endl;
1908 #endif 1904 #endif
1909 } else { 1905 } else {
1910 delete m_cache; 1906
1911 m_cache = new QPixmap(scaledCacheSize); 1907 if (!m_cache ||
1908 m_cache->size() != wholeSize) {
1909 delete m_cache;
1910 m_cache = new QPixmap(wholeSize);
1911 }
1912
1912 #ifdef DEBUG_VIEW_WIDGET_PAINT 1913 #ifdef DEBUG_VIEW_WIDGET_PAINT
1913 cerr << "View(" << this << ")::paintEvent: recreated cache" << endl; 1914 cerr << "View(" << this << ")::paintEvent: cache is invalid, will repaint whole" << endl;
1914 #endif 1915 #endif
1915 cacheRect = rect(); 1916 }
1916 repaintCache = true; 1917
1917 } 1918 count.miss();
1918 1919
1919 } else if (m_cacheCentreFrame != m_centreFrame) { 1920 } else if (m_cacheCentreFrame != m_centreFrame) {
1920 1921
1921 int dx = 1922 int dx = dpratio * (getXForFrame(m_cacheCentreFrame) -
1922 getXForFrame(m_cacheCentreFrame) - 1923 getXForFrame(m_centreFrame));
1923 getXForFrame(m_centreFrame); 1924
1924 1925 if (dx > -m_cache->width() && dx < m_cache->width()) {
1925 if (dx > -width() && dx < width()) { 1926
1926 static QPixmap *tmpPixmap = 0; 1927 m_cache->scroll(dx, 0, m_cache->rect(), 0);
1927 if (!tmpPixmap || tmpPixmap->size() != scaledCacheSize) { 1928
1928 delete tmpPixmap; 1929 if (dx < 0) {
1929 tmpPixmap = new QPixmap(scaledCacheSize); 1930 cacheAreaToRepaint =
1931 QRect(m_cache->width() + dx, 0, -dx, m_cache->height());
1932 } else {
1933 cacheAreaToRepaint =
1934 QRect(0, 0, dx, m_cache->height());
1930 } 1935 }
1931 paint.begin(tmpPixmap); 1936
1932 paint.drawPixmap(0, 0, *m_cache); 1937 count.partial();
1933 paint.end(); 1938
1934 paint.begin(m_cache);
1935 paint.drawPixmap(dx, 0, *tmpPixmap);
1936 paint.end();
1937 if (dx < 0) {
1938 cacheRect = QRect(width() + dx, 0, -dx, height());
1939 } else {
1940 cacheRect = QRect(0, 0, dx, height());
1941 }
1942 #ifdef DEBUG_VIEW_WIDGET_PAINT 1939 #ifdef DEBUG_VIEW_WIDGET_PAINT
1943 cerr << "View(" << this << ")::paintEvent: scrolled cache by " << dx << endl; 1940 cerr << "View(" << this << ")::paintEvent: scrolled cache by " << dx << endl;
1944 #endif 1941 #endif
1945 } else { 1942 } else {
1946 cacheRect = rect(); 1943 count.miss();
1947 #ifdef DEBUG_VIEW_WIDGET_PAINT 1944 #ifdef DEBUG_VIEW_WIDGET_PAINT
1948 cerr << "View(" << this << ")::paintEvent: scrolling too far" << endl; 1945 cerr << "View(" << this << ")::paintEvent: scrolling too far" << endl;
1949 #endif 1946 #endif
1950 } 1947 }
1951 repaintCache = true;
1952 1948
1953 } else { 1949 } else {
1954 #ifdef DEBUG_VIEW_WIDGET_PAINT 1950 #ifdef DEBUG_VIEW_WIDGET_PAINT
1955 cerr << "View(" << this << ")::paintEvent: cache is good" << endl; 1951 cerr << "View(" << this << ")::paintEvent: cache is good" << endl;
1956 #endif 1952 #endif
1957 paint.begin(m_buffer); 1953 count.hit();
1958 paint.drawPixmap(scaledCacheRect, *m_cache, scaledCacheRect); 1954 shouldRepaintCache = false;
1959 paint.end(); 1955 }
1960 QFrame::paintEvent(e); 1956 }
1961 paintedCacheRect = true; 1957
1962 } 1958 #ifdef DEBUG_VIEW_WIDGET_PAINT
1963 1959 cerr << "View(" << this << ")::paintEvent: m_cacheValid = " << m_cacheValid << ", shouldUseCache = " << shouldUseCache << ", shouldRepaintCache = " << shouldRepaintCache << ", cacheAreaToRepaint = " << cacheAreaToRepaint.x() << "," << cacheAreaToRepaint.y() << " " << cacheAreaToRepaint.width() << "x" << cacheAreaToRepaint.height() << endl;
1960 #endif
1961
1962 if (shouldRepaintCache && !shouldUseCache) {
1963 // If we are repainting the cache, then we paint the
1964 // scrollables only to the cache, not to the buffer. So if
1965 // shouldUseCache is also false, then the scrollables can't
1966 // appear because they will only be on the cache
1967 throw std::logic_error("ERROR: shouldRepaintCache is true, but shouldUseCache is false: this can't lead to the correct result");
1968 }
1969
1970 // Scrollable (cacheable) items first. If we are repainting the
1971 // cache, then we paint these to the cache; otherwise straight to
1972 // the buffer.
1973
1974 ViewProxy proxy(this, dpratio);
1975 QRect areaToPaint;
1976 QPainter paint;
1977
1978 if (shouldRepaintCache) {
1979 paint.begin(m_cache);
1980 areaToPaint = cacheAreaToRepaint;
1981 } else {
1982 paint.begin(m_buffer);
1983 areaToPaint = requestedPaintArea;
1984 }
1985
1986 setPaintFont(paint);
1987 paint.setClipRect(areaToPaint);
1988
1989 paint.setPen(getBackground());
1990 paint.setBrush(getBackground());
1991 paint.drawRect(areaToPaint);
1992
1993 paint.setPen(getForeground());
1994 paint.setBrush(Qt::NoBrush);
1995
1996 for (LayerList::iterator i = scrollables.begin();
1997 i != scrollables.end(); ++i) {
1998
1999 paint.setRenderHint(QPainter::Antialiasing, false);
2000 paint.save();
2001
2002 #ifdef DEBUG_VIEW_WIDGET_PAINT
2003 cerr << "Painting scrollable layer " << *i << " using proxy with shouldRepaintCache = " << shouldRepaintCache << ", dpratio = " << dpratio << ", areaToPaint = " << areaToPaint.x() << "," << areaToPaint.y() << " " << areaToPaint.width() << "x" << areaToPaint.height() << endl;
2004 #endif
2005
2006 (*i)->paint(&proxy, paint, areaToPaint);
2007
2008 paint.restore();
2009 }
2010
2011 paint.end();
2012
2013 if (shouldRepaintCache) {
2014 // and now we have
2015 m_cacheValid = true;
1964 m_cacheCentreFrame = m_centreFrame; 2016 m_cacheCentreFrame = m_centreFrame;
1965 m_cacheZoomLevel = m_zoomLevel; 2017 m_cacheZoomLevel = m_zoomLevel;
1966 } 2018 }
1967 2019
1968 #ifdef DEBUG_VIEW_WIDGET_PAINT 2020 if (shouldUseCache) {
1969 // cerr << "View(" << this << ")::paintEvent: cacheRect " << cacheRect << ", nonCacheRect " << (nonCacheRect | cacheRect) << ", repaintCache " << repaintCache << ", paintedCacheRect " << paintedCacheRect << endl; 2021 paint.begin(m_buffer);
1970 #endif 2022 paint.drawPixmap(requestedPaintArea, *m_cache, requestedPaintArea);
1971
1972 // Scrollable (cacheable) items first
1973
1974 ViewProxy proxy(this, dpratio);
1975
1976 if (!paintedCacheRect) {
1977
1978 QRect rectToPaint;
1979
1980 if (repaintCache) {
1981 paint.begin(m_cache);
1982 rectToPaint = scaledCacheRect;
1983 } else {
1984 paint.begin(m_buffer);
1985 rectToPaint = scaledCacheRect;
1986 }
1987
1988 setPaintFont(paint);
1989 paint.setClipRect(rectToPaint);
1990
1991 paint.setPen(getBackground());
1992 paint.setBrush(getBackground());
1993 paint.drawRect(rectToPaint);
1994
1995 paint.setPen(getForeground());
1996 paint.setBrush(Qt::NoBrush);
1997
1998 for (LayerList::iterator i = scrollables.begin(); i != scrollables.end(); ++i) {
1999 paint.setRenderHint(QPainter::Antialiasing, false);
2000 paint.save();
2001 #ifdef DEBUG_VIEW_WIDGET_PAINT
2002 cerr << "Painting scrollable layer " << *i << " using proxy with repaintCache = " << repaintCache << ", dpratio = " << dpratio << ", rectToPaint = " << rectToPaint.x() << "," << rectToPaint.y() << " " << rectToPaint.width() << "x" << rectToPaint.height() << endl;
2003 #endif
2004 (*i)->paint(&proxy, paint, rectToPaint);
2005 paint.restore();
2006 }
2007
2008 if (haveSelections && selectionCacheable) {
2009 drawSelections(paint);
2010 m_selectionCached = repaintCache;
2011 }
2012
2013 paint.end(); 2023 paint.end();
2014 2024 }
2015 if (repaintCache) { 2025
2016 cacheRect |= (e ? e->rect() : rect()); 2026 // Now non-cacheable items.
2017 scaledCacheRect = scaledRect(cacheRect, dpratio); 2027
2018 paint.begin(m_buffer);
2019 paint.drawPixmap(scaledCacheRect, *m_cache, scaledCacheRect);
2020 paint.end();
2021 }
2022 }
2023
2024 // Now non-cacheable items. We always need to redraw the
2025 // non-cacheable items across at least the area we drew of the
2026 // cacheable items.
2027
2028 nonCacheRect |= cacheRect;
2029
2030 QRect scaledNonCacheRect = scaledRect(nonCacheRect, dpratio);
2031
2032 paint.begin(m_buffer); 2028 paint.begin(m_buffer);
2033 paint.setClipRect(scaledNonCacheRect); 2029 paint.setClipRect(requestedPaintArea);
2034 setPaintFont(paint); 2030 setPaintFont(paint);
2035 if (scrollables.empty()) { 2031 if (scrollables.empty()) {
2036 paint.setPen(getBackground()); 2032 paint.setPen(getBackground());
2037 paint.setBrush(getBackground()); 2033 paint.setBrush(getBackground());
2038 paint.drawRect(scaledNonCacheRect); 2034 paint.drawRect(requestedPaintArea);
2039 } 2035 }
2040 2036
2041 paint.setPen(getForeground()); 2037 paint.setPen(getForeground());
2042 paint.setBrush(Qt::NoBrush); 2038 paint.setBrush(Qt::NoBrush);
2043 2039
2044 for (LayerList::iterator i = nonScrollables.begin(); i != nonScrollables.end(); ++i) { 2040 for (LayerList::iterator i = nonScrollables.begin();
2041 i != nonScrollables.end(); ++i) {
2042
2045 // Profiler profiler2("View::paintEvent non-cacheable"); 2043 // Profiler profiler2("View::paintEvent non-cacheable");
2046 #ifdef DEBUG_VIEW_WIDGET_PAINT 2044 #ifdef DEBUG_VIEW_WIDGET_PAINT
2047 cerr << "Painting non-scrollable layer " << *i << " without proxy with repaintCache = " << repaintCache << ", dpratio = " << dpratio << ", rectToPaint = " << nonCacheRect.x() << "," << nonCacheRect.y() << " " << nonCacheRect.width() << "x" << nonCacheRect.height() << endl; 2045 cerr << "Painting non-scrollable layer " << *i << " without proxy with shouldRepaintCache = " << shouldRepaintCache << ", dpratio = " << dpratio << ", requestedPaintArea = " << requestedPaintArea.x() << "," << requestedPaintArea.y() << " " << requestedPaintArea.width() << "x" << requestedPaintArea.height() << endl;
2048 #endif 2046 #endif
2049 (*i)->paint(&proxy, paint, scaledNonCacheRect); 2047 (*i)->paint(&proxy, paint, requestedPaintArea);
2050 } 2048 }
2051 2049
2052 paint.end(); 2050 paint.end();
2053 2051
2054 paint.begin(this); 2052 // Now paint to widget from buffer: target rects from here on,
2055 QRect finalPaintRect = e ? e->rect() : rect(); 2053 // unlike all the preceding, are at formal (1x) resolution
2056 paint.drawPixmap(finalPaintRect, *m_buffer, scaledRect(finalPaintRect, dpratio));
2057 paint.end();
2058 2054
2059 paint.begin(this); 2055 paint.begin(this);
2060 setPaintFont(paint); 2056 setPaintFont(paint);
2061 if (e) paint.setClipRect(e->rect()); 2057 if (e) paint.setClipRect(e->rect());
2062 if (!m_selectionCached) { 2058
2063 drawSelections(paint); 2059 QRect finalPaintRect = e ? e->rect() : rect();
2064 } 2060 paint.drawPixmap(finalPaintRect, *m_buffer,
2061 scaledRect(finalPaintRect, dpratio));
2062
2063 drawSelections(paint);
2064 drawPlayPointer(paint);
2065
2065 paint.end(); 2066 paint.end();
2066
2067 bool showPlayPointer = true;
2068 if (m_followPlay == PlaybackScrollContinuous) {
2069 showPlayPointer = false;
2070 } else if (m_playPointerFrame <= getStartFrame() ||
2071 m_playPointerFrame >= getEndFrame()) {
2072 showPlayPointer = false;
2073 } else if (m_manager && !m_manager->isPlaying()) {
2074 if (m_playPointerFrame == getCentreFrame() &&
2075 m_manager->shouldShowCentreLine() &&
2076 m_followPlay != PlaybackIgnore) {
2077 // Don't show the play pointer when it is redundant with
2078 // the centre line
2079 showPlayPointer = false;
2080 }
2081 }
2082
2083 if (showPlayPointer) {
2084
2085 paint.begin(this);
2086
2087 int playx = getXForFrame(m_playPointerFrame);
2088
2089 paint.setPen(getForeground());
2090 paint.drawLine(playx - 1, 0, playx - 1, height() - 1);
2091 paint.drawLine(playx + 1, 0, playx + 1, height() - 1);
2092 paint.drawPoint(playx, 0);
2093 paint.drawPoint(playx, height() - 1);
2094 paint.setPen(getBackground());
2095 paint.drawLine(playx, 1, playx, height() - 2);
2096
2097 paint.end();
2098 }
2099 2067
2100 QFrame::paintEvent(e); 2068 QFrame::paintEvent(e);
2101 } 2069 }
2102 2070
2103 void 2071 void
2256 2224
2257 paint.restore(); 2225 paint.restore();
2258 } 2226 }
2259 2227
2260 void 2228 void
2229 View::drawPlayPointer(QPainter &paint)
2230 {
2231 bool showPlayPointer = true;
2232
2233 if (m_followPlay == PlaybackScrollContinuous) {
2234 showPlayPointer = false;
2235 } else if (m_playPointerFrame <= getStartFrame() ||
2236 m_playPointerFrame >= getEndFrame()) {
2237 showPlayPointer = false;
2238 } else if (m_manager && !m_manager->isPlaying()) {
2239 if (m_playPointerFrame == getCentreFrame() &&
2240 m_manager->shouldShowCentreLine() &&
2241 m_followPlay != PlaybackIgnore) {
2242 // Don't show the play pointer when it is redundant with
2243 // the centre line
2244 showPlayPointer = false;
2245 }
2246 }
2247
2248 if (showPlayPointer) {
2249
2250 int playx = getXForFrame(m_playPointerFrame);
2251
2252 paint.setPen(getForeground());
2253 paint.drawLine(playx - 1, 0, playx - 1, height() - 1);
2254 paint.drawLine(playx + 1, 0, playx + 1, height() - 1);
2255 paint.drawPoint(playx, 0);
2256 paint.drawPoint(playx, height() - 1);
2257 paint.setPen(getBackground());
2258 paint.drawLine(playx, 1, playx, height() - 2);
2259 }
2260 }
2261
2262 void
2261 View::drawMeasurementRect(QPainter &paint, const Layer *topLayer, QRect r, 2263 View::drawMeasurementRect(QPainter &paint, const Layer *topLayer, QRect r,
2262 bool focus) const 2264 bool focus) const
2263 { 2265 {
2264 // SVDEBUG << "View::drawMeasurementRect(" << r.x() << "," << r.y() << " " 2266 // SVDEBUG << "View::drawMeasurementRect(" << r.x() << "," << r.y() << " "
2265 // << r.width() << "x" << r.height() << ")" << endl; 2267 // << r.width() << "x" << r.height() << ")" << endl;