annotate layer/Colour3DPlotRenderer.h @ 1137:4e7ed3252d80 spectrogram-minor-refactor

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