annotate layer/Colour3DPlotRenderer.h @ 1330:c1f719094c25 zoom

Ensure getFrameForX returns value on zoom blocksize boundary; take advantage of that (this is essentially reverting to the same behaviour as in the default branch, which we should probably have done all along)
author Chris Cannam
date Fri, 21 Sep 2018 11:50:05 +0100
parents a34a2a25907c
children 7d28e7522dbd
rev   line source
Chris@1071 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@1071 2
Chris@1071 3 /*
Chris@1071 4 Sonic Visualiser
Chris@1071 5 An audio file viewer and annotation editor.
Chris@1071 6 Centre for Digital Music, Queen Mary, University of London.
Chris@1071 7 This file copyright 2006-2016 Chris Cannam and QMUL.
Chris@1071 8
Chris@1071 9 This program is free software; you can redistribute it and/or
Chris@1071 10 modify it under the terms of the GNU General Public License as
Chris@1071 11 published by the Free Software Foundation; either version 2 of the
Chris@1071 12 License, or (at your option) any later version. See the file
Chris@1071 13 COPYING included with this distribution for more information.
Chris@1071 14 */
Chris@1071 15
Chris@1071 16 #ifndef COLOUR_3D_PLOT_RENDERER_H
Chris@1071 17 #define COLOUR_3D_PLOT_RENDERER_H
Chris@1071 18
Chris@1071 19 #include "ColourScale.h"
Chris@1073 20 #include "ScrollableImageCache.h"
Chris@1119 21 #include "ScrollableMagRangeCache.h"
Chris@1071 22
Chris@1071 23 #include "base/ColumnOp.h"
Chris@1073 24 #include "base/MagnitudeRange.h"
Chris@1071 25
Chris@1073 26 #include <QRect>
Chris@1073 27 #include <QPainter>
Chris@1073 28 #include <QImage>
Chris@1073 29
Chris@1073 30 class LayerGeometryProvider;
Chris@1082 31 class VerticalBinLayer;
Chris@1071 32 class DenseThreeDimensionalModel;
Chris@1071 33 class Dense3DModelPeakCache;
Chris@1071 34 class FFTModel;
Chris@1221 35 class RenderTimer;
Chris@1071 36
Chris@1103 37 enum class BinDisplay {
Chris@1103 38 AllBins,
Chris@1103 39 PeakBins,
Chris@1103 40 PeakFrequencies
Chris@1103 41 };
Chris@1103 42
Chris@1103 43 enum class BinScale {
Chris@1103 44 Linear,
Chris@1103 45 Log
Chris@1103 46 };
Chris@1103 47
Chris@1071 48 class Colour3DPlotRenderer
Chris@1071 49 {
Chris@1071 50 public:
Chris@1073 51 struct Sources {
Chris@1212 52 Sources() : verticalBinLayer(0), source(0), fft(0) { }
Chris@1073 53
Chris@1073 54 // These must all outlive this class
Chris@1089 55 const VerticalBinLayer *verticalBinLayer; // always
Chris@1266 56 const DenseThreeDimensionalModel *source; // always
Chris@1266 57 const FFTModel *fft; // optionally
Chris@1266 58 std::vector<Dense3DModelPeakCache *> peakCaches; // zero or more
Chris@1073 59 };
Chris@1073 60
Chris@1071 61 struct Parameters {
Chris@1266 62 Parameters() :
Chris@1266 63 colourScale(ColourScale::Parameters()),
Chris@1266 64 normalization(ColumnNormalization::None),
Chris@1266 65 binDisplay(BinDisplay::AllBins),
Chris@1103 66 binScale(BinScale::Linear),
Chris@1266 67 alwaysOpaque(false),
Chris@1125 68 interpolate(false),
Chris@1112 69 invertVertical(false),
Chris@1125 70 scaleFactor(1.0),
Chris@1112 71 colourRotation(0) { }
Chris@1071 72
Chris@1125 73 /** A complete ColourScale object by value, used for colour
Chris@1125 74 * map conversion. Note that the final display gain setting is
Chris@1125 75 * also encapsulated here. */
Chris@1266 76 ColourScale colourScale;
Chris@1125 77
Chris@1125 78 /** Type of column normalization. */
Chris@1266 79 ColumnNormalization normalization;
Chris@1125 80
Chris@1125 81 /** Selection of bins to display. */
Chris@1266 82 BinDisplay binDisplay;
Chris@1125 83
Chris@1125 84 /** Scale for vertical bin spacing (linear or logarithmic). */
Chris@1266 85 BinScale binScale;
Chris@1125 86
Chris@1125 87 /** Whether cells should always be opaque. If false, then
Chris@1125 88 * large cells (when zoomed in a long way) will be rendered
Chris@1125 89 * translucent in order not to obscure anything in a layer
Chris@1125 90 * beneath. */
Chris@1266 91 bool alwaysOpaque;
Chris@1125 92
Chris@1125 93 /** Whether to apply smoothing when rendering cells at more
Chris@1125 94 * than one pixel per cell. !!! todo: decide about separating
Chris@1125 95 * out x-interpolate and y-interpolate as the spectrogram
Chris@1125 96 * actually does (or used to)
Chris@1125 97 */
Chris@1266 98 bool interpolate;
Chris@1125 99
Chris@1125 100 /** Whether to render the whole caboodle upside-down. */
Chris@1266 101 bool invertVertical;
Chris@1125 102
Chris@1125 103 /** Initial scale factor (e.g. for FFT scaling). This factor
Chris@1125 104 * is applied to all values read from the underlying model
Chris@1125 105 * *before* magnitude ranges are calculated, in contrast to
Chris@1125 106 * the display gain found in the ColourScale parameter. */
Chris@1125 107 double scaleFactor;
Chris@1125 108
Chris@1125 109 /** Colourmap rotation, in the range 0-255. */
Chris@1112 110 int colourRotation;
Chris@1071 111 };
Chris@1073 112
Chris@1073 113 Colour3DPlotRenderer(Sources sources, Parameters parameters) :
Chris@1073 114 m_sources(sources),
Chris@1266 115 m_params(parameters),
Chris@1221 116 m_secondsPerXPixel(0.0),
Chris@1221 117 m_secondsPerXPixelValid(false)
Chris@1071 118 { }
Chris@1071 119
Chris@1073 120 struct RenderResult {
Chris@1073 121 /**
Chris@1073 122 * The rect that was actually rendered. May be equal to the
Chris@1073 123 * rect that was requested to render, or may be smaller if
Chris@1073 124 * time ran out and the complete flag was not set.
Chris@1073 125 */
Chris@1073 126 QRect rendered;
Chris@1073 127
Chris@1073 128 /**
Chris@1248 129 * The magnitude range of the data in the rendered area, after
Chris@1248 130 * initial scaling (parameters.scaleFactor) and normalisation,
Chris@1248 131 * for use in displaying colour scale etc. (Note that the
Chris@1248 132 * magnitude range *before* normalisation would not be very
Chris@1248 133 * meaningful for this purpose, as the scale would need to be
Chris@1248 134 * different for every column if column or hybrid
Chris@1248 135 * normalisation was in use.)
Chris@1073 136 */
Chris@1073 137 MagnitudeRange range;
Chris@1073 138 };
Chris@1073 139
Chris@1073 140 /**
Chris@1073 141 * Render the requested area using the given painter, obtaining
Chris@1090 142 * geometry (e.g. start frame) from the given
Chris@1073 143 * LayerGeometryProvider.
Chris@1073 144 *
Chris@1090 145 * The whole of the supplied rect will be rendered and the
Chris@1090 146 * returned QRect will be equal to the supplied QRect. (See
Chris@1090 147 * renderTimeConstrained for an alternative that may render only
Chris@1090 148 * part of the rect in cases where obtaining source data is slow
Chris@1090 149 * and retaining responsiveness is important.)
Chris@1090 150 *
Chris@1090 151 * Note that Colour3DPlotRenderer retains internal cache state
Chris@1090 152 * related to the size and position of the supplied
Chris@1090 153 * LayerGeometryProvider. Although it is valid to call render()
Chris@1090 154 * successively on the same Colour3DPlotRenderer with different
Chris@1090 155 * LayerGeometryProviders, it will be much faster to use a
Chris@1090 156 * dedicated Colour3DPlotRenderer for each LayerGeometryProvider.
Chris@1075 157 *
Chris@1075 158 * If the model to render from is not ready, this will throw a
Chris@1075 159 * std::logic_error exception. The model must be ready and the
Chris@1075 160 * layer requesting the render must not be dormant in its view, so
Chris@1075 161 * that the LayerGeometryProvider returns valid results; it is the
Chris@1075 162 * caller's responsibility to ensure these.
Chris@1073 163 */
Chris@1125 164 RenderResult render(const LayerGeometryProvider *v,
Chris@1125 165 QPainter &paint, QRect rect);
Chris@1076 166
Chris@1076 167 /**
Chris@1076 168 * Render the requested area using the given painter, obtaining
Chris@1076 169 * geometry (e.g. start frame) from the stored
Chris@1076 170 * LayerGeometryProvider.
Chris@1076 171 *
Chris@1076 172 * As much of the rect will be rendered as can be managed given
Chris@1076 173 * internal time constraints (using a RenderTimer object
Chris@1076 174 * internally). The returned QRect (the rendered field in the
Chris@1076 175 * RenderResult struct) will contain the area that was
Chris@1076 176 * rendered. Note that we always render the full requested height,
Chris@1076 177 * it's only width that is time-constrained.
Chris@1076 178 *
Chris@1090 179 * Note that Colour3DPlotRenderer retains internal cache state
Chris@1090 180 * related to the size and position of the supplied
Chris@1090 181 * LayerGeometryProvider. Although it is valid to call render()
Chris@1090 182 * successively on the same Colour3DPlotRenderer with different
Chris@1090 183 * LayerGeometryProviders, it will be much faster to use a
Chris@1090 184 * dedicated Colour3DPlotRenderer for each LayerGeometryProvider.
Chris@1090 185 *
Chris@1076 186 * If the model to render from is not ready, this will throw a
Chris@1076 187 * std::logic_error exception. The model must be ready and the
Chris@1076 188 * layer requesting the render must not be dormant in its view, so
Chris@1076 189 * that the LayerGeometryProvider returns valid results; it is the
Chris@1076 190 * caller's responsibility to ensure these.
Chris@1076 191 */
Chris@1113 192 RenderResult renderTimeConstrained(const LayerGeometryProvider *v,
Chris@1090 193 QPainter &paint, QRect rect);
Chris@1096 194
Chris@1096 195 /**
Chris@1096 196 * Return the area of the largest rectangle within the entire area
Chris@1096 197 * of the cache that is unavailable in the cache. This is only
Chris@1096 198 * valid in relation to a preceding render() call which is
Chris@1096 199 * presumed to have set the area, start frame, and zoom level for
Chris@1096 200 * the cache. It could be used to establish a suitable region for
Chris@1096 201 * a subsequent paint request (because if an area is not in the
Chris@1096 202 * cache, it cannot have been rendered since the cache was
Chris@1096 203 * cleared).
Chris@1096 204 *
Chris@1096 205 * Returns an empty QRect if the cache is entirely valid.
Chris@1096 206 */
Chris@1121 207 QRect getLargestUncachedRect(const LayerGeometryProvider *v);
Chris@1113 208
Chris@1113 209 /**
Chris@1122 210 * Return true if the provider's geometry differs from the cache,
Chris@1122 211 * or if we are not using a cache. i.e. if the cache will be
Chris@1122 212 * regenerated for the next render, or the next render performed
Chris@1122 213 * from scratch.
Chris@1122 214 */
Chris@1122 215 bool geometryChanged(const LayerGeometryProvider *v);
Chris@1122 216
Chris@1122 217 /**
Chris@1113 218 * Return true if the rendering will be opaque. This may be used
Chris@1113 219 * by the calling layer to determine whether it can scroll
Chris@1113 220 * directly without regard to any other layers beneath.
Chris@1113 221 */
Chris@1113 222 bool willRenderOpaque(const LayerGeometryProvider *v) {
Chris@1113 223 return decideRenderType(v) != DirectTranslucent;
Chris@1113 224 }
Chris@1073 225
Chris@1125 226 /**
Chris@1125 227 * Return the colour corresponding to the given value.
Chris@1125 228 * \see ColourScale::getPixel
Chris@1125 229 * \see ColourScale::getColour
Chris@1125 230 */
Chris@1125 231 QColor getColour(double value) const {
Chris@1125 232 return m_params.colourScale.getColour(value, m_params.colourRotation);
Chris@1125 233 }
Chris@1139 234
Chris@1139 235 /**
Chris@1139 236 * Return the enclosing rectangle for the region of similar colour
Chris@1139 237 * to the given point within the cache. Return an empty QRect if
Chris@1139 238 * this is not possible. \see ImageRegionFinder
Chris@1139 239 */
Chris@1139 240 QRect findSimilarRegionExtents(QPoint point) const;
Chris@1125 241
Chris@1071 242 private:
Chris@1073 243 Sources m_sources;
Chris@1071 244 Parameters m_params;
Chris@1072 245
Chris@1073 246 // Draw buffer is the target of each partial repaint. It is always
Chris@1073 247 // at view height (not model height) and is cleared and repainted
Chris@1073 248 // on each fragment render. The only reason it's stored as a data
Chris@1073 249 // member is to avoid reallocation.
Chris@1073 250 QImage m_drawBuffer;
Chris@1072 251
Chris@1121 252 // A temporary store of magnitude ranges per-column, used when
Chris@1121 253 // rendering to the draw buffer. This always has the same length
Chris@1121 254 // as the width of the draw buffer, and the x coordinates of the
Chris@1121 255 // two containers are equivalent.
Chris@1121 256 std::vector<MagnitudeRange> m_magRanges;
Chris@1121 257
Chris@1119 258 // The image cache is our persistent record of the visible
Chris@1119 259 // area. It is always the same size as the view (i.e. the paint
Chris@1119 260 // size reported by the LayerGeometryProvider) and is scrolled and
Chris@1119 261 // partially repainted internally as appropriate. A render request
Chris@1119 262 // is carried out by repainting to cache (via the draw buffer) any
Chris@1073 263 // area that is being requested but is not valid in the cache, and
Chris@1073 264 // then repainting from cache to the requested painter.
Chris@1073 265 ScrollableImageCache m_cache;
Chris@1073 266
Chris@1119 267 // The mag range cache is our record of the column magnitude
Chris@1119 268 // ranges for each of the columns in the cache. It always has the
Chris@1119 269 // same start frame and width as the image cache, and the column
Chris@1119 270 // indices match up across both. Our cache update mechanism
Chris@1119 271 // guarantees that every valid column in the image cache has a
Chris@1119 272 // valid range in the magnitude cache, but not necessarily vice
Chris@1119 273 // versa (as the image cache is limited to contiguous ranges).
Chris@1119 274 ScrollableMagRangeCache m_magCache;
Chris@1221 275
Chris@1221 276 double m_secondsPerXPixel;
Chris@1221 277 bool m_secondsPerXPixelValid;
Chris@1119 278
Chris@1113 279 RenderResult render(const LayerGeometryProvider *v,
Chris@1090 280 QPainter &paint, QRect rect, bool timeConstrained);
Chris@1109 281
Chris@1121 282 MagnitudeRange renderDirectTranslucent(const LayerGeometryProvider *v,
Chris@1121 283 QPainter &paint, QRect rect);
Chris@1109 284
Chris@1113 285 void renderToCachePixelResolution(const LayerGeometryProvider *v, int x0,
Chris@1094 286 int repaintWidth, bool rightToLeft,
Chris@1094 287 bool timeConstrained);
Chris@1109 288
Chris@1113 289 void renderToCacheBinResolution(const LayerGeometryProvider *v, int x0,
Chris@1094 290 int repaintWidth);
Chris@1097 291
Chris@1083 292 int renderDrawBuffer(int w, int h,
Chris@1083 293 const std::vector<int> &binforx,
Chris@1083 294 const std::vector<double> &binfory,
Chris@1212 295 int peakCacheIndex, // -1 => don't use a peak cache
Chris@1083 296 bool rightToLeft,
Chris@1083 297 bool timeConstrained);
Chris@1097 298
Chris@1113 299 int renderDrawBufferPeakFrequencies(const LayerGeometryProvider *v,
Chris@1097 300 int w, int h,
Chris@1097 301 const std::vector<int> &binforx,
Chris@1097 302 const std::vector<double> &binfory,
Chris@1097 303 bool rightToLeft,
Chris@1097 304 bool timeConstrained);
Chris@1097 305
Chris@1095 306 void recreateDrawBuffer(int w, int h);
Chris@1079 307 void clearDrawBuffer(int w, int h);
Chris@1109 308
Chris@1109 309 enum RenderType {
Chris@1109 310 DrawBufferPixelResolution,
Chris@1109 311 DrawBufferBinResolution,
Chris@1109 312 DirectTranslucent
Chris@1109 313 };
Chris@1109 314
Chris@1113 315 RenderType decideRenderType(const LayerGeometryProvider *) const;
Chris@1138 316
Chris@1167 317 QImage scaleDrawBufferImage(QImage source, int targetWidth, int targetHeight)
Chris@1167 318 const;
Chris@1167 319
Chris@1161 320 ColumnOp::Column getColumn(int sx, int minbin, int nbins,
Chris@1212 321 int peakCacheIndex) const; // -1 => don't use cache
Chris@1213 322
Chris@1213 323 void getPreferredPeakCache(const LayerGeometryProvider *,
Chris@1213 324 int &peakCacheIndex, int &binsPerPeak) const;
Chris@1221 325
Chris@1221 326 void updateTimings(const RenderTimer &timer, int xPixelCount);
Chris@1071 327 };
Chris@1071 328
Chris@1071 329 #endif
Chris@1071 330