1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
/*
* Copyright (c) 2020-2023 Vasile Vilvoiu <vasi@vilvoiu.ro>
*
* specgram is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#ifndef _RENDERER_HPP_
#define _RENDERER_HPP_
#include "configuration.hpp"
#include <SFML/Graphics.hpp>
#include <vector>
#include <list>
/* Orientation */
enum class Orientation {
k90CCW,
kNormal,
k90CW,
k180
};
/**
* Spectrogram rendering class
*/
class Renderer {
protected: /* for all intents and purposes this should be private, but we want to unit test a few of the methods here */
const Configuration configuration_; /* configuration is cached as it contains multiple settings regarding spacing and sizing */
const std::size_t fft_count_; /* number of windows to render */
const std::unique_ptr<const ColorMap> color_map_; /* color map used for rendering */
sf::Font font_;
sf::RenderTexture canvas_;
sf::Texture spectrogram_texture_;
std::size_t width_;
std::size_t height_;
sf::Transform legend_transform_;
sf::Transform live_transform_;
sf::Transform spectrogram_transform_;
/**
* First value specifies the position of the tick in the domain [0..1].
* Second value is the text of the tick.
*/
using AxisTick = std::tuple<double, std::string>;
std::list<AxisTick> frequency_ticks_;
std::list<AxisTick> live_ticks_;
/**
* Return a short representation of the value (using unit prefixes like, m, k, M ...).
* @param value The value to encode.
* @param scale Scale of the value (in the numeric sense).
* @param unit Unit of the value.
* @return Short representation string.
*
* NOTE: The unit prefix is computed just from the scale, not the value itself.
* For example, ValueToShortString(0.00004, 5, "V") will yield "0.04mV",
* not "40uV" (which would otherwise be the more natural representation).
* This behaviour is purposeful, as it allows more flexibility (e.g. if
* we want the same prefix and number of decimal places for all values
* of an axis).
*/
static std::string ValueToShortString(double value, int scale, const std::string& unit);
/**
* Build an array of ticks with linear spacing.
* @param v_min Lowest value on the axis.
* @param v_max Highest value on the axis.
* @param v_unit Unit of the axis.
* @param num_ticks Number of ticks to generate.
* @return Array of ticks.
*/
[[maybe_unused]] /* need for this method disappeared when fixing #9, might be useful in the future */
static std::list<AxisTick> GetLinearTicks(double v_min, double v_max, const std::string& v_unit,
unsigned int num_ticks);
/**
* Build an array of nicely spaced ticks.
* @param v_min Lowest value on the axis.
* @param v_max Highest value on the axis.
* @param v_unit Unit of the axis.
* @param length_px Length of the scale, in pixels (used for spacing estimation).
* @param min_tick_length_px Minimum tick spacing.
* @param rotated If true then vertical. Otherwise horizontal.
* @return Array of ticks.
*
* NOTE: This method attempts to find some nice values for the ticks that
* also have sufficient spacing for the text to fit properly.
* NOTE: This method enforces an error of <1% between tick text and tick value.
*/
std::list<AxisTick> GetNiceTicks(double v_min, double v_max, const std::string& v_unit,
unsigned int length_px, unsigned int min_tick_length_px, bool rotated);
/**
* Render an axis upon a texture
* @param texture Texture to render to.
* @param t Transform to use.
* @param lhs True if has left-hand side text.
* @param orientation One of Orientation.
* @param length Length in pixels.
* @param ticks Ticks.
*/
void RenderAxis(sf::RenderTexture& texture,
const sf::Transform& t, bool lhs, Orientation orientation, double length,
const std::list<AxisTick>& ticks);
public:
Renderer() = delete;
Renderer(const Configuration& conf, std::size_t fft_count);
/**
* Render the spectrogram area.
* @param memory RGBA memory of the colorized spectrogram.
*/
void RenderFFTArea(const std::vector<uint8_t>& memory);
/**
* Render the spectrogram area.
* @param history List of RGBA colorized windows.
*/
void RenderFFTArea(const std::list<std::vector<uint8_t>>& history);
std::vector<uint8_t> RenderLiveFFT(const RealWindow& window);
/**
* @return The rendered canvas texture.
*/
sf::Texture GetCanvas();
/* size getters */
auto GetWidth() const { return width_; }
auto GetHeight() const { return height_; }
};
#endif
|