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