Mercurial > hg > svgui
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/layer/Colour3DPlotRenderer.h Fri Jan 13 10:29:50 2017 +0000 @@ -0,0 +1,316 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006-2016 Chris Cannam and QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef COLOUR_3D_PLOT_RENDERER_H +#define COLOUR_3D_PLOT_RENDERER_H + +#include "ColourScale.h" +#include "ScrollableImageCache.h" +#include "ScrollableMagRangeCache.h" + +#include "base/ColumnOp.h" +#include "base/MagnitudeRange.h" + +#include <QRect> +#include <QPainter> +#include <QImage> + +class LayerGeometryProvider; +class VerticalBinLayer; +class DenseThreeDimensionalModel; +class Dense3DModelPeakCache; +class FFTModel; + +enum class BinDisplay { + AllBins, + PeakBins, + PeakFrequencies +}; + +enum class BinScale { + Linear, + Log +}; + +class Colour3DPlotRenderer +{ +public: + struct Sources { + Sources() : verticalBinLayer(0), source(0), fft(0) { } + + // These must all outlive this class + const VerticalBinLayer *verticalBinLayer; // always + const DenseThreeDimensionalModel *source; // always + const FFTModel *fft; // optionally + std::vector<Dense3DModelPeakCache *> peakCaches; // zero or more + }; + + struct Parameters { + Parameters() : + colourScale(ColourScale::Parameters()), + normalization(ColumnNormalization::None), + binDisplay(BinDisplay::AllBins), + binScale(BinScale::Linear), + alwaysOpaque(false), + interpolate(false), + invertVertical(false), + scaleFactor(1.0), + colourRotation(0) { } + + /** A complete ColourScale object by value, used for colour + * map conversion. Note that the final display gain setting is + * also encapsulated here. */ + ColourScale colourScale; + + /** Type of column normalization. */ + ColumnNormalization normalization; + + /** Selection of bins to display. */ + BinDisplay binDisplay; + + /** Scale for vertical bin spacing (linear or logarithmic). */ + BinScale binScale; + + /** Whether cells should always be opaque. If false, then + * large cells (when zoomed in a long way) will be rendered + * translucent in order not to obscure anything in a layer + * beneath. */ + bool alwaysOpaque; + + /** Whether to apply smoothing when rendering cells at more + * than one pixel per cell. !!! todo: decide about separating + * out x-interpolate and y-interpolate as the spectrogram + * actually does (or used to) + */ + bool interpolate; + + /** Whether to render the whole caboodle upside-down. */ + bool invertVertical; + + /** Initial scale factor (e.g. for FFT scaling). This factor + * is applied to all values read from the underlying model + * *before* magnitude ranges are calculated, in contrast to + * the display gain found in the ColourScale parameter. */ + double scaleFactor; + + /** Colourmap rotation, in the range 0-255. */ + int colourRotation; + }; + + Colour3DPlotRenderer(Sources sources, Parameters parameters) : + m_sources(sources), + m_params(parameters) + { } + + struct RenderResult { + /** + * The rect that was actually rendered. May be equal to the + * rect that was requested to render, or may be smaller if + * time ran out and the complete flag was not set. + */ + QRect rendered; + + /** + * The magnitude range of the data in the rendered area. + */ + MagnitudeRange range; + }; + + /** + * Render the requested area using the given painter, obtaining + * geometry (e.g. start frame) from the given + * LayerGeometryProvider. + * + * The whole of the supplied rect will be rendered and the + * returned QRect will be equal to the supplied QRect. (See + * renderTimeConstrained for an alternative that may render only + * part of the rect in cases where obtaining source data is slow + * and retaining responsiveness is important.) + * + * Note that Colour3DPlotRenderer retains internal cache state + * related to the size and position of the supplied + * LayerGeometryProvider. Although it is valid to call render() + * successively on the same Colour3DPlotRenderer with different + * LayerGeometryProviders, it will be much faster to use a + * dedicated Colour3DPlotRenderer for each LayerGeometryProvider. + * + * If the model to render from is not ready, this will throw a + * std::logic_error exception. The model must be ready and the + * layer requesting the render must not be dormant in its view, so + * that the LayerGeometryProvider returns valid results; it is the + * caller's responsibility to ensure these. + */ + RenderResult render(const LayerGeometryProvider *v, + QPainter &paint, QRect rect); + + /** + * Render the requested area using the given painter, obtaining + * geometry (e.g. start frame) from the stored + * LayerGeometryProvider. + * + * As much of the rect will be rendered as can be managed given + * internal time constraints (using a RenderTimer object + * internally). The returned QRect (the rendered field in the + * RenderResult struct) will contain the area that was + * rendered. Note that we always render the full requested height, + * it's only width that is time-constrained. + * + * Note that Colour3DPlotRenderer retains internal cache state + * related to the size and position of the supplied + * LayerGeometryProvider. Although it is valid to call render() + * successively on the same Colour3DPlotRenderer with different + * LayerGeometryProviders, it will be much faster to use a + * dedicated Colour3DPlotRenderer for each LayerGeometryProvider. + * + * If the model to render from is not ready, this will throw a + * std::logic_error exception. The model must be ready and the + * layer requesting the render must not be dormant in its view, so + * that the LayerGeometryProvider returns valid results; it is the + * caller's responsibility to ensure these. + */ + RenderResult renderTimeConstrained(const LayerGeometryProvider *v, + QPainter &paint, QRect rect); + + /** + * Return the area of the largest rectangle within the entire area + * of the cache that is unavailable in the cache. This is only + * valid in relation to a preceding render() call which is + * presumed to have set the area, start frame, and zoom level for + * the cache. It could be used to establish a suitable region for + * a subsequent paint request (because if an area is not in the + * cache, it cannot have been rendered since the cache was + * cleared). + * + * Returns an empty QRect if the cache is entirely valid. + */ + QRect getLargestUncachedRect(const LayerGeometryProvider *v); + + /** + * Return true if the provider's geometry differs from the cache, + * or if we are not using a cache. i.e. if the cache will be + * regenerated for the next render, or the next render performed + * from scratch. + */ + bool geometryChanged(const LayerGeometryProvider *v); + + /** + * Return true if the rendering will be opaque. This may be used + * by the calling layer to determine whether it can scroll + * directly without regard to any other layers beneath. + */ + bool willRenderOpaque(const LayerGeometryProvider *v) { + return decideRenderType(v) != DirectTranslucent; + } + + /** + * Return the colour corresponding to the given value. + * \see ColourScale::getPixel + * \see ColourScale::getColour + */ + QColor getColour(double value) const { + return m_params.colourScale.getColour(value, m_params.colourRotation); + } + + /** + * Return the enclosing rectangle for the region of similar colour + * to the given point within the cache. Return an empty QRect if + * this is not possible. \see ImageRegionFinder + */ + QRect findSimilarRegionExtents(QPoint point) const; + +private: + Sources m_sources; + Parameters m_params; + + // Draw buffer is the target of each partial repaint. It is always + // at view height (not model height) and is cleared and repainted + // on each fragment render. The only reason it's stored as a data + // member is to avoid reallocation. + QImage m_drawBuffer; + + // A temporary store of magnitude ranges per-column, used when + // rendering to the draw buffer. This always has the same length + // as the width of the draw buffer, and the x coordinates of the + // two containers are equivalent. + std::vector<MagnitudeRange> m_magRanges; + + // The image cache is our persistent record of the visible + // area. It is always the same size as the view (i.e. the paint + // size reported by the LayerGeometryProvider) and is scrolled and + // partially repainted internally as appropriate. A render request + // is carried out by repainting to cache (via the draw buffer) any + // area that is being requested but is not valid in the cache, and + // then repainting from cache to the requested painter. + ScrollableImageCache m_cache; + + // The mag range cache is our record of the column magnitude + // ranges for each of the columns in the cache. It always has the + // same start frame and width as the image cache, and the column + // indices match up across both. Our cache update mechanism + // guarantees that every valid column in the image cache has a + // valid range in the magnitude cache, but not necessarily vice + // versa (as the image cache is limited to contiguous ranges). + ScrollableMagRangeCache m_magCache; + + RenderResult render(const LayerGeometryProvider *v, + QPainter &paint, QRect rect, bool timeConstrained); + + MagnitudeRange renderDirectTranslucent(const LayerGeometryProvider *v, + QPainter &paint, QRect rect); + + void renderToCachePixelResolution(const LayerGeometryProvider *v, int x0, + int repaintWidth, bool rightToLeft, + bool timeConstrained); + + void renderToCacheBinResolution(const LayerGeometryProvider *v, int x0, + int repaintWidth); + + int renderDrawBuffer(int w, int h, + const std::vector<int> &binforx, + const std::vector<double> &binfory, + int peakCacheIndex, // -1 => don't use a peak cache + bool rightToLeft, + bool timeConstrained); + + int renderDrawBufferPeakFrequencies(const LayerGeometryProvider *v, + int w, int h, + const std::vector<int> &binforx, + const std::vector<double> &binfory, + bool rightToLeft, + bool timeConstrained); + + void recreateDrawBuffer(int w, int h); + void clearDrawBuffer(int w, int h); + + enum RenderType { + DrawBufferPixelResolution, + DrawBufferBinResolution, + DirectTranslucent + }; + + RenderType decideRenderType(const LayerGeometryProvider *) const; + + QImage scaleDrawBufferImage(QImage source, int targetWidth, int targetHeight) + const; + + ColumnOp::Column getColumn(int sx, int minbin, int nbins, + int peakCacheIndex) const; // -1 => don't use cache + + void getPreferredPeakCache(const LayerGeometryProvider *, + int &peakCacheIndex, int &binsPerPeak) const; +}; + +#endif +