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