changeset 1332:6dac67c3ed82 zoom

Refactor out paintChannel
author Chris Cannam
date Fri, 21 Sep 2018 14:01:09 +0100
parents 9758dfc12555
children 59f6830be8d8
files layer/WaveformLayer.cpp layer/WaveformLayer.h
diffstat 2 files changed, 350 insertions(+), 324 deletions(-) [+]
line wrap: on
line diff
--- a/layer/WaveformLayer.cpp	Fri Sep 21 11:50:15 2018 +0100
+++ b/layer/WaveformLayer.cpp	Fri Sep 21 14:01:09 2018 +0100
@@ -499,7 +499,6 @@
     int w = v->getPaintWidth();
     int h = v->getPaintHeight();
 
-    bool ready = m_model->isReady();
     QPainter *paint;
 
     if (m_aggressive) {
@@ -595,103 +594,13 @@
         new RangeSummarisableTimeValueModel::RangeBlock;
 
     RangeSummarisableTimeValueModel::RangeBlock *otherChannelRanges = 0;
-    RangeSummarisableTimeValueModel::Range range;
-
-    QColor baseColour = getBaseQColor();
-    std::vector<QColor> greys = getPartialShades(v);
-        
-    QColor midColour = baseColour;
-    if (midColour == Qt::black) {
-        midColour = Qt::gray;
-    } else if (v->hasLightBackground()) {
-        midColour = midColour.light(150);
-    } else {
-        midColour = midColour.light(50);
-    }
 
     while ((int)m_effectiveGains.size() <= maxChannel) {
         m_effectiveGains.push_back(m_gain);
     }
 
     for (int ch = minChannel; ch <= maxChannel; ++ch) {
-
-        int prevRangeBottom = -1, prevRangeTop = -1;
-        QColor prevRangeBottomColour = baseColour, prevRangeTopColour = baseColour;
-
-        m_effectiveGains[ch] = m_gain;
-
-        if (m_autoNormalize) {
-            m_effectiveGains[ch] = getNormalizeGain(v, ch);
-        }
-
-        double gain = m_effectiveGains[ch];
-
-        int m = (h / channels) / 2;
-        int my = m + (((ch - minChannel) * h) / channels);
-
-#ifdef DEBUG_WAVEFORM_PAINT        
-        cerr << "ch = " << ch << ", channels = " << channels << ", m = " << m << ", my = " << my << ", h = " << h << endl;
-#endif
-
-        if (my - m > y1 || my + m < y0) continue;
-
-        if ((m_scale == dBScale || m_scale == MeterScale) &&
-            m_channelMode != MergeChannels) {
-            m = (h / channels);
-            my = m + (((ch - minChannel) * h) / channels);
-        }
-
-        paint->setPen(greys[1]);
-        paint->drawLine(x0, my, x1, my);
-
-        int n = 10;
-        int py = -1;
         
-        if (v->hasLightBackground() &&
-            v->getViewManager() &&
-            v->getViewManager()->shouldShowScaleGuides()) {
-
-            paint->setPen(QColor(240, 240, 240));
-
-            for (int i = 1; i < n; ++i) {
-                
-                double val = 0.0, nval = 0.0;
-
-                switch (m_scale) {
-
-                case LinearScale:
-                    val = (i * gain) / n;
-                    if (i > 0) nval = -val;
-                    break;
-
-                case MeterScale:
-                    val = AudioLevel::dB_to_multiplier(meterdbs[i]) * gain;
-                    break;
-
-                case dBScale:
-                    val = AudioLevel::dB_to_multiplier(-(10*n) + i * 10) * gain;
-                    break;
-                }
-
-                if (val < -1.0 || val > 1.0) continue;
-
-                int y = getYForValue(v, val, ch);
-
-                if (py >= 0 && abs(y - py) < 10) continue;
-                else py = y;
-
-                int ny = y;
-                if (nval != 0.0) {
-                    ny = getYForValue(v, nval, ch);
-                }
-
-                paint->drawLine(x0, y, x1, y);
-                if (ny != y) {
-                    paint->drawLine(x0, ny, x1, ny);
-                }
-            }
-        }
-  
         m_model->getSummaries(ch, frame0, frame1 - frame0,
                               *ranges, blockSize);
 
@@ -714,237 +623,8 @@
             }
         }
 
-        for (int x = x0; x <= x1; ++x) {
-
-            range = RangeSummarisableTimeValueModel::Range();
-
-            sv_frame_t f0, f1;
-            if (!getSourceFramesForX(v, x, blockSize, f0, f1)) continue;
-            f1 = f1 - 1;
-
-            if (f0 < frame0) {
-                cerr << "ERROR: WaveformLayer::paint: pixel " << x << " has f0 = " << f0 << " which is less than range frame0 " << frame0 << " for x0 = " << x0 << endl;
-                continue;
-            }
-
-            sv_frame_t i0 = (f0 - frame0) / blockSize;
-            sv_frame_t i1 = (f1 - frame0) / blockSize;
-
-#ifdef DEBUG_WAVEFORM_PAINT
-            cerr << "WaveformLayer::paint: pixel " << x << ": i0 " << i0 << " (f " << f0 << "), i1 " << i1 << " (f " << f1 << ")" << endl;
-#endif
-
-            if (i1 > i0 + 1) {
-                cerr << "WaveformLayer::paint: ERROR: i1 " << i1 << " > i0 " << i0 << " plus one (zoom = " << zoomLevel << ", model zoom = " << blockSize << ")" << endl;
-            }
-
-            if (ranges && i0 < (sv_frame_t)ranges->size()) {
-
-                range = (*ranges)[size_t(i0)];
-
-                if (i1 > i0 && i1 < (int)ranges->size()) {
-                    range.setMax(std::max(range.max(),
-                                          (*ranges)[size_t(i1)].max()));
-                    range.setMin(std::min(range.min(),
-                                          (*ranges)[size_t(i1)].min()));
-                    range.setAbsmean((range.absmean()
-                                      + (*ranges)[size_t(i1)].absmean()) / 2);
-                }
-
-            } else {
-#ifdef DEBUG_WAVEFORM_PAINT
-                cerr << "No (or not enough) ranges for i0 = " << i0 << endl;
-#endif
-                continue;
-            }
-
-            int rangeBottom = 0, rangeTop = 0, meanBottom = 0, meanTop = 0;
-
-            if (mergingChannels) {
-
-                if (otherChannelRanges && i0 < (sv_frame_t)otherChannelRanges->size()) {
-
-                    range.setMax(fabsf(range.max()));
-                    range.setMin(-fabsf((*otherChannelRanges)[size_t(i0)].max()));
-                    range.setAbsmean
-                        ((range.absmean() +
-                          (*otherChannelRanges)[size_t(i0)].absmean()) / 2);
-
-                    if (i1 > i0 && i1 < (sv_frame_t)otherChannelRanges->size()) {
-                        // let's not concern ourselves about the mean
-                        range.setMin
-                            (std::min
-                             (range.min(),
-                              -fabsf((*otherChannelRanges)[size_t(i1)].max())));
-                    }
-                }
-
-            } else if (mixingChannels) {
-
-                if (otherChannelRanges && i0 < (sv_frame_t)otherChannelRanges->size()) {
-
-                    range.setMax((range.max()
-                                  + (*otherChannelRanges)[size_t(i0)].max()) / 2);
-                    range.setMin((range.min()
-                                  + (*otherChannelRanges)[size_t(i0)].min()) / 2);
-                    range.setAbsmean((range.absmean()
-                                      + (*otherChannelRanges)[size_t(i0)].absmean()) / 2);
-                }
-            }
-
-            int greyLevels = 1;
-            if (m_greyscale && (m_scale == LinearScale)) greyLevels = 4;
-
-            switch (m_scale) {
-
-            case LinearScale:
-                rangeBottom = int(double(m * greyLevels) * range.min() * gain);
-                rangeTop    = int(double(m * greyLevels) * range.max() * gain);
-                meanBottom  = int(double(-m) * range.absmean() * gain);
-                meanTop     = int(double(m) * range.absmean() * gain);
-                break;
-
-            case dBScale:
-                if (!mergingChannels) {
-                    int db0 = dBscale(range.min() * gain, m);
-                    int db1 = dBscale(range.max() * gain, m);
-                    rangeTop    = std::max(db0, db1);
-                    meanTop     = std::min(db0, db1);
-                    if (mixingChannels) rangeBottom = meanTop;
-                    else rangeBottom = dBscale(range.absmean() * gain, m);
-                    meanBottom  = rangeBottom;
-                } else {
-                    rangeBottom = -dBscale(range.min() * gain, m * greyLevels);
-                    rangeTop    =  dBscale(range.max() * gain, m * greyLevels);
-                    meanBottom  = -dBscale(range.absmean() * gain, m);
-                    meanTop     =  dBscale(range.absmean() * gain, m);
-                }
-                break;
-
-            case MeterScale:
-                if (!mergingChannels) {
-                    int r0 = abs(AudioLevel::multiplier_to_preview(range.min() * gain, m));
-                    int r1 = abs(AudioLevel::multiplier_to_preview(range.max() * gain, m));
-                    rangeTop    = std::max(r0, r1);
-                    meanTop     = std::min(r0, r1);
-                    if (mixingChannels) rangeBottom = meanTop;
-                    else rangeBottom = AudioLevel::multiplier_to_preview(range.absmean() * gain, m);
-                    meanBottom  = rangeBottom;
-                } else {
-                    rangeBottom = -AudioLevel::multiplier_to_preview(range.min() * gain, m * greyLevels);
-                    rangeTop    =  AudioLevel::multiplier_to_preview(range.max() * gain, m * greyLevels);
-                    meanBottom  = -AudioLevel::multiplier_to_preview(range.absmean() * gain, m);
-                    meanTop     =  AudioLevel::multiplier_to_preview(range.absmean() * gain, m);
-                }
-                break;
-            }
-
-            rangeBottom = my * greyLevels - rangeBottom;
-            rangeTop    = my * greyLevels - rangeTop;
-            meanBottom  = my - meanBottom;
-            meanTop     = my - meanTop;
-
-            int topFill = (rangeTop % greyLevels);
-            if (topFill > 0) topFill = greyLevels - topFill;
-
-            int bottomFill = (rangeBottom % greyLevels);
-
-            rangeTop = rangeTop / greyLevels;
-            rangeBottom = rangeBottom / greyLevels;
-
-            bool clipped = false;
-
-            if (rangeTop < my - m) { rangeTop = my - m; }
-            if (rangeTop > my + m) { rangeTop = my + m; }
-            if (rangeBottom < my - m) { rangeBottom = my - m; }
-            if (rangeBottom > my + m) { rangeBottom = my + m; }
-
-            if (range.max() <= -1.0 ||
-                range.max() >= 1.0) clipped = true;
-            
-            if (meanBottom > rangeBottom) meanBottom = rangeBottom;
-            if (meanTop < rangeTop) meanTop = rangeTop;
-
-            bool drawMean = m_showMeans;
-            if (meanTop == rangeTop) {
-                if (meanTop < meanBottom) ++meanTop;
-                else drawMean = false;
-            }
-            if (meanBottom == rangeBottom && m_scale == LinearScale) {
-                if (meanBottom > meanTop) --meanBottom;
-                else drawMean = false;
-            }
-
-            if (x != x0 && prevRangeBottom != -1) {
-                if (prevRangeBottom > rangeBottom + 1 &&
-                    prevRangeTop    > rangeBottom + 1) {
-//                    paint->setPen(midColour);
-                    paint->setPen(baseColour);
-                    paint->drawLine(x-1, prevRangeTop, x, rangeBottom + 1);
-                    paint->setPen(prevRangeTopColour);
-                    paint->drawPoint(x-1, prevRangeTop);
-                } else if (prevRangeBottom < rangeTop - 1 &&
-                           prevRangeTop    < rangeTop - 1) {
-//                    paint->setPen(midColour);
-                    paint->setPen(baseColour);
-                    paint->drawLine(x-1, prevRangeBottom, x, rangeTop - 1);
-                    paint->setPen(prevRangeBottomColour);
-                    paint->drawPoint(x-1, prevRangeBottom);
-                }
-            }
-
-            if (ready) {
-                if (clipped /*!!! ||
-                    range.min() * gain <= -1.0 ||
-                    range.max() * gain >=  1.0 */) {
-                    paint->setPen(Qt::red); //!!! getContrastingColour
-                } else {
-                    paint->setPen(baseColour);
-                }
-            } else {
-                paint->setPen(midColour);
-            }
-
-#ifdef DEBUG_WAVEFORM_PAINT
-            cerr << "range " << rangeBottom << " -> " << rangeTop << ", means " << meanBottom << " -> " << meanTop << ", raw range " << range.min() << " -> " << range.max() << endl;
-#endif
-
-            if (rangeTop == rangeBottom) {
-                paint->drawPoint(x, rangeTop);
-            } else {
-                paint->drawLine(x, rangeBottom, x, rangeTop);
-            }
-
-            prevRangeTopColour = baseColour;
-            prevRangeBottomColour = baseColour;
-
-            if (m_greyscale && (m_scale == LinearScale) && ready) {
-                if (!clipped) {
-                    if (rangeTop < rangeBottom) {
-                        if (topFill > 0 &&
-                            (!drawMean || (rangeTop < meanTop - 1))) {
-                            paint->setPen(greys[topFill - 1]);
-                            paint->drawPoint(x, rangeTop);
-                            prevRangeTopColour = greys[topFill - 1];
-                        }
-                        if (bottomFill > 0 && 
-                            (!drawMean || (rangeBottom > meanBottom + 1))) {
-                            paint->setPen(greys[bottomFill - 1]);
-                            paint->drawPoint(x, rangeBottom);
-                            prevRangeBottomColour = greys[bottomFill - 1];
-                        }
-                    }
-                }
-            }
-        
-            if (drawMean) {
-                paint->setPen(midColour);
-                paint->drawLine(x, meanBottom, x, meanTop);
-            }
-        
-            prevRangeBottom = rangeBottom;
-            prevRangeTop = rangeTop;
-        }
+        paintChannel(v, paint, ch, ranges, otherChannelRanges,
+                     blockSize, x0, y0, x1, y1, frame0, frame1);
     }
 
     if (m_middleLineHeight != 0.5) {
@@ -953,7 +633,7 @@
 
     if (m_aggressive) {
 
-        if (ready && rect == v->getPaintRect()) {
+        if (m_model->isReady() && rect == v->getPaintRect()) {
             m_cacheValid = true;
             m_cacheZoomLevel = zoomLevel;
         }
@@ -966,6 +646,346 @@
     delete ranges;
 }
 
+void
+WaveformLayer::paintChannel(LayerGeometryProvider *v, QPainter *paint, int ch,
+                            const RangeSummarisableTimeValueModel::RangeBlock *ranges,
+                            const RangeSummarisableTimeValueModel::RangeBlock *otherChannelRanges,
+                            int blockSize, int x0, int y0, int x1, int y1,
+                            sv_frame_t frame0, sv_frame_t frame1)
+    const
+{
+    int w = v->getPaintWidth();
+    int h = v->getPaintHeight();
+
+    int channels = 0, minChannel = 0, maxChannel = 0;
+    bool mergingChannels = false, mixingChannels = false;
+
+    channels = getChannelArrangement(minChannel, maxChannel,
+                                     mergingChannels, mixingChannels);
+    if (channels == 0) return;
+
+    QColor baseColour = getBaseQColor();
+    std::vector<QColor> greys = getPartialShades(v);
+        
+    QColor midColour = baseColour;
+    if (midColour == Qt::black) {
+        midColour = Qt::gray;
+    } else if (v->hasLightBackground()) {
+        midColour = midColour.light(150);
+    } else {
+        midColour = midColour.light(50);
+    }
+
+    int prevRangeBottom = -1, prevRangeTop = -1;
+    QColor prevRangeBottomColour = baseColour, prevRangeTopColour = baseColour;
+
+    m_effectiveGains[ch] = m_gain;
+
+    if (m_autoNormalize) {
+        m_effectiveGains[ch] = getNormalizeGain(v, ch);
+    }
+
+    double gain = m_effectiveGains[ch];
+
+    int m = (h / channels) / 2;
+    int my = m + (((ch - minChannel) * h) / channels);
+
+#ifdef DEBUG_WAVEFORM_PAINT        
+    cerr << "ch = " << ch << ", channels = " << channels << ", m = " << m << ", my = " << my << ", h = " << h << endl;
+#endif
+
+    if (my - m > y1 || my + m < y0) return;
+
+    if ((m_scale == dBScale || m_scale == MeterScale) &&
+        m_channelMode != MergeChannels) {
+        m = (h / channels);
+        my = m + (((ch - minChannel) * h) / channels);
+    }
+
+    paint->setPen(greys[1]);
+    paint->drawLine(x0, my, x1, my);
+
+    int n = 10;
+    int py = -1;
+        
+    if (v->hasLightBackground() &&
+        v->getViewManager() &&
+        v->getViewManager()->shouldShowScaleGuides()) {
+
+        paint->setPen(QColor(240, 240, 240));
+
+        for (int i = 1; i < n; ++i) {
+                
+            double val = 0.0, nval = 0.0;
+
+            switch (m_scale) {
+
+            case LinearScale:
+                val = (i * gain) / n;
+                if (i > 0) nval = -val;
+                break;
+
+            case MeterScale:
+                val = AudioLevel::dB_to_multiplier(meterdbs[i]) * gain;
+                break;
+
+            case dBScale:
+                val = AudioLevel::dB_to_multiplier(-(10*n) + i * 10) * gain;
+                break;
+            }
+
+            if (val < -1.0 || val > 1.0) continue;
+
+            int y = getYForValue(v, val, ch);
+
+            if (py >= 0 && abs(y - py) < 10) continue;
+            else py = y;
+
+            int ny = y;
+            if (nval != 0.0) {
+                ny = getYForValue(v, nval, ch);
+            }
+
+            paint->drawLine(x0, y, x1, y);
+            if (ny != y) {
+                paint->drawLine(x0, ny, x1, ny);
+            }
+        }
+    }
+  
+    for (int x = x0; x <= x1; ++x) {
+
+        RangeSummarisableTimeValueModel::Range range;
+
+        sv_frame_t f0, f1;
+        if (!getSourceFramesForX(v, x, blockSize, f0, f1)) continue;
+        f1 = f1 - 1;
+
+        if (f0 < frame0) {
+            cerr << "ERROR: WaveformLayer::paint: pixel " << x << " has f0 = " << f0 << " which is less than range frame0 " << frame0 << " for x0 = " << x0 << endl;
+            continue;
+        }
+
+        sv_frame_t i0 = (f0 - frame0) / blockSize;
+        sv_frame_t i1 = (f1 - frame0) / blockSize;
+
+#ifdef DEBUG_WAVEFORM_PAINT
+        cerr << "WaveformLayer::paint: pixel " << x << ": i0 " << i0 << " (f " << f0 << "), i1 " << i1 << " (f " << f1 << ")" << endl;
+#endif
+
+        if (i1 > i0 + 1) {
+            cerr << "WaveformLayer::paint: ERROR: i1 " << i1 << " > i0 " << i0 << " plus one (zoom = " << v->getZoomLevel() << ", model zoom = " << blockSize << ")" << endl;
+        }
+
+        if (ranges && i0 < (sv_frame_t)ranges->size()) {
+
+            range = (*ranges)[size_t(i0)];
+
+            if (i1 > i0 && i1 < (int)ranges->size()) {
+                range.setMax(std::max(range.max(),
+                                      (*ranges)[size_t(i1)].max()));
+                range.setMin(std::min(range.min(),
+                                      (*ranges)[size_t(i1)].min()));
+                range.setAbsmean((range.absmean()
+                                  + (*ranges)[size_t(i1)].absmean()) / 2);
+            }
+
+        } else {
+#ifdef DEBUG_WAVEFORM_PAINT
+            cerr << "No (or not enough) ranges for i0 = " << i0 << endl;
+#endif
+            continue;
+        }
+
+        int rangeBottom = 0, rangeTop = 0, meanBottom = 0, meanTop = 0;
+
+        if (mergingChannels) {
+
+            if (otherChannelRanges && i0 < (sv_frame_t)otherChannelRanges->size()) {
+
+                range.setMax(fabsf(range.max()));
+                range.setMin(-fabsf((*otherChannelRanges)[size_t(i0)].max()));
+                range.setAbsmean
+                    ((range.absmean() +
+                      (*otherChannelRanges)[size_t(i0)].absmean()) / 2);
+
+                if (i1 > i0 && i1 < (sv_frame_t)otherChannelRanges->size()) {
+                    // let's not concern ourselves about the mean
+                    range.setMin
+                        (std::min
+                         (range.min(),
+                          -fabsf((*otherChannelRanges)[size_t(i1)].max())));
+                }
+            }
+
+        } else if (mixingChannels) {
+
+            if (otherChannelRanges && i0 < (sv_frame_t)otherChannelRanges->size()) {
+
+                range.setMax((range.max()
+                              + (*otherChannelRanges)[size_t(i0)].max()) / 2);
+                range.setMin((range.min()
+                              + (*otherChannelRanges)[size_t(i0)].min()) / 2);
+                range.setAbsmean((range.absmean()
+                                  + (*otherChannelRanges)[size_t(i0)].absmean()) / 2);
+            }
+        }
+
+        int greyLevels = 1;
+        if (m_greyscale && (m_scale == LinearScale)) greyLevels = 4;
+
+        switch (m_scale) {
+
+        case LinearScale:
+            rangeBottom = int(double(m * greyLevels) * range.min() * gain);
+            rangeTop    = int(double(m * greyLevels) * range.max() * gain);
+            meanBottom  = int(double(-m) * range.absmean() * gain);
+            meanTop     = int(double(m) * range.absmean() * gain);
+            break;
+
+        case dBScale:
+            if (!mergingChannels) {
+                int db0 = dBscale(range.min() * gain, m);
+                int db1 = dBscale(range.max() * gain, m);
+                rangeTop    = std::max(db0, db1);
+                meanTop     = std::min(db0, db1);
+                if (mixingChannels) rangeBottom = meanTop;
+                else rangeBottom = dBscale(range.absmean() * gain, m);
+                meanBottom  = rangeBottom;
+            } else {
+                rangeBottom = -dBscale(range.min() * gain, m * greyLevels);
+                rangeTop    =  dBscale(range.max() * gain, m * greyLevels);
+                meanBottom  = -dBscale(range.absmean() * gain, m);
+                meanTop     =  dBscale(range.absmean() * gain, m);
+            }
+            break;
+
+        case MeterScale:
+            if (!mergingChannels) {
+                int r0 = abs(AudioLevel::multiplier_to_preview(range.min() * gain, m));
+                int r1 = abs(AudioLevel::multiplier_to_preview(range.max() * gain, m));
+                rangeTop    = std::max(r0, r1);
+                meanTop     = std::min(r0, r1);
+                if (mixingChannels) rangeBottom = meanTop;
+                else rangeBottom = AudioLevel::multiplier_to_preview(range.absmean() * gain, m);
+                meanBottom  = rangeBottom;
+            } else {
+                rangeBottom = -AudioLevel::multiplier_to_preview(range.min() * gain, m * greyLevels);
+                rangeTop    =  AudioLevel::multiplier_to_preview(range.max() * gain, m * greyLevels);
+                meanBottom  = -AudioLevel::multiplier_to_preview(range.absmean() * gain, m);
+                meanTop     =  AudioLevel::multiplier_to_preview(range.absmean() * gain, m);
+            }
+            break;
+        }
+
+        rangeBottom = my * greyLevels - rangeBottom;
+        rangeTop    = my * greyLevels - rangeTop;
+        meanBottom  = my - meanBottom;
+        meanTop     = my - meanTop;
+
+        int topFill = (rangeTop % greyLevels);
+        if (topFill > 0) topFill = greyLevels - topFill;
+
+        int bottomFill = (rangeBottom % greyLevels);
+
+        rangeTop = rangeTop / greyLevels;
+        rangeBottom = rangeBottom / greyLevels;
+
+        bool clipped = false;
+
+        if (rangeTop < my - m) { rangeTop = my - m; }
+        if (rangeTop > my + m) { rangeTop = my + m; }
+        if (rangeBottom < my - m) { rangeBottom = my - m; }
+        if (rangeBottom > my + m) { rangeBottom = my + m; }
+
+        if (range.max() <= -1.0 ||
+            range.max() >= 1.0) clipped = true;
+            
+        if (meanBottom > rangeBottom) meanBottom = rangeBottom;
+        if (meanTop < rangeTop) meanTop = rangeTop;
+
+        bool drawMean = m_showMeans;
+        if (meanTop == rangeTop) {
+            if (meanTop < meanBottom) ++meanTop;
+            else drawMean = false;
+        }
+        if (meanBottom == rangeBottom && m_scale == LinearScale) {
+            if (meanBottom > meanTop) --meanBottom;
+            else drawMean = false;
+        }
+
+        if (x != x0 && prevRangeBottom != -1) {
+            if (prevRangeBottom > rangeBottom + 1 &&
+                prevRangeTop    > rangeBottom + 1) {
+//                    paint->setPen(midColour);
+                paint->setPen(baseColour);
+                paint->drawLine(x-1, prevRangeTop, x, rangeBottom + 1);
+                paint->setPen(prevRangeTopColour);
+                paint->drawPoint(x-1, prevRangeTop);
+            } else if (prevRangeBottom < rangeTop - 1 &&
+                       prevRangeTop    < rangeTop - 1) {
+//                    paint->setPen(midColour);
+                paint->setPen(baseColour);
+                paint->drawLine(x-1, prevRangeBottom, x, rangeTop - 1);
+                paint->setPen(prevRangeBottomColour);
+                paint->drawPoint(x-1, prevRangeBottom);
+            }
+        }
+
+        if (m_model->isReady()) {
+            if (clipped /*!!! ||
+                          range.min() * gain <= -1.0 ||
+                          range.max() * gain >=  1.0 */) {
+                paint->setPen(Qt::red); //!!! getContrastingColour
+            } else {
+                paint->setPen(baseColour);
+            }
+        } else {
+            paint->setPen(midColour);
+        }
+
+#ifdef DEBUG_WAVEFORM_PAINT
+        cerr << "range " << rangeBottom << " -> " << rangeTop << ", means " << meanBottom << " -> " << meanTop << ", raw range " << range.min() << " -> " << range.max() << endl;
+#endif
+
+        if (rangeTop == rangeBottom) {
+            paint->drawPoint(x, rangeTop);
+        } else {
+            paint->drawLine(x, rangeBottom, x, rangeTop);
+        }
+
+        prevRangeTopColour = baseColour;
+        prevRangeBottomColour = baseColour;
+
+        if (m_greyscale && (m_scale == LinearScale) && m_model->isReady()) {
+            if (!clipped) {
+                if (rangeTop < rangeBottom) {
+                    if (topFill > 0 &&
+                        (!drawMean || (rangeTop < meanTop - 1))) {
+                        paint->setPen(greys[topFill - 1]);
+                        paint->drawPoint(x, rangeTop);
+                        prevRangeTopColour = greys[topFill - 1];
+                    }
+                    if (bottomFill > 0 && 
+                        (!drawMean || (rangeBottom > meanBottom + 1))) {
+                        paint->setPen(greys[bottomFill - 1]);
+                        paint->drawPoint(x, rangeBottom);
+                        prevRangeBottomColour = greys[bottomFill - 1];
+                    }
+                }
+            }
+        }
+        
+        if (drawMean) {
+            paint->setPen(midColour);
+            paint->drawLine(x, meanBottom, x, meanTop);
+        }
+        
+        prevRangeBottom = rangeBottom;
+        prevRangeTop = rangeTop;
+    }
+}
+
 QString
 WaveformLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const
 {
--- a/layer/WaveformLayer.h	Fri Sep 21 11:50:15 2018 +0100
+++ b/layer/WaveformLayer.h	Fri Sep 21 14:01:09 2018 +0100
@@ -211,8 +211,14 @@
 
     /// Return value is number of channels displayed
     int getChannelArrangement(int &min, int &max,
-                                 bool &merging, bool &mixing) const;
+                              bool &merging, bool &mixing) const;
 
+    void paintChannel(LayerGeometryProvider *, QPainter *paint, int channel,
+                      const RangeSummarisableTimeValueModel::RangeBlock *ranges,
+                      const RangeSummarisableTimeValueModel::RangeBlock *other,
+                      int blockSize, int x0, int y0, int x1, int y1,
+                      sv_frame_t frame0, sv_frame_t frame1) const;
+    
     int getYForValue(const LayerGeometryProvider *v, double value, int channel) const;
 
     double getValueForY(const LayerGeometryProvider *v, int y, int &channel) const;