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