1 // Copyright (c) Charles J. Cliffe
2 // SPDX-License-Identifier: GPL-2.0+
3
4 #include "WaterfallPanel.h"
5
WaterfallPanel()6 WaterfallPanel::WaterfallPanel() : GLPanel(), fft_size(0), waterfall_lines(0), waterfall_slice(nullptr), activeTheme(nullptr) {
7 setFillColor(RGBA4f(0,0,0));
8 for (unsigned int & i : waterfall) {
9 i = 0;
10 }
11 }
12
setup(unsigned int fft_size_in,int num_waterfall_lines_in)13 void WaterfallPanel::setup(unsigned int fft_size_in, int num_waterfall_lines_in) {
14 waterfall_lines = num_waterfall_lines_in;
15 fft_size = fft_size_in;
16 lines_buffered.store(0);
17
18 if (points.size() != fft_size) {
19 points.resize(fft_size);
20 }
21
22 texInitialized.store(false);
23 bufferInitialized.store(false);
24 }
25
refreshTheme()26 void WaterfallPanel::refreshTheme() {
27 glEnable (GL_TEXTURE_2D);
28
29 for (unsigned int i : waterfall) {
30 glBindTexture(GL_TEXTURE_2D, i);
31
32 glPixelTransferi(GL_MAP_COLOR, GL_TRUE);
33 glPixelMapfv(GL_PIXEL_MAP_I_TO_R, 256, &(ThemeMgr::mgr.currentTheme->waterfallGradient.getRed())[0]);
34 glPixelMapfv(GL_PIXEL_MAP_I_TO_G, 256, &(ThemeMgr::mgr.currentTheme->waterfallGradient.getGreen())[0]);
35 glPixelMapfv(GL_PIXEL_MAP_I_TO_B, 256, &(ThemeMgr::mgr.currentTheme->waterfallGradient.getBlue())[0]);
36 }
37 }
38
setPoints(std::vector<float> & points_in)39 void WaterfallPanel::setPoints(std::vector<float> &points_in) {
40 size_t halfPts = points_in.size() / 2;
41 if (halfPts == fft_size) {
42
43 for (unsigned int i = 0; i < fft_size; i++) {
44 points[i] = points_in[i * 2 + 1];
45 }
46 } else {
47 points.assign(points_in.begin(), points_in.end());
48 }
49 }
50
step()51 void WaterfallPanel::step() {
52 unsigned int half_fft_size = fft_size / 2;
53
54 if (!bufferInitialized.load()) {
55 delete waterfall_slice;
56 waterfall_slice = new unsigned char[half_fft_size];
57 bufferInitialized.store(true);
58 }
59
60 if (!texInitialized.load()) {
61 return;
62 }
63
64 if (!points.empty() && points.size() == fft_size) {
65 for (int j = 0; j < 2; j++) {
66 for (unsigned int i = 0, iMax = half_fft_size; i < iMax; i++) {
67 float v = points[j * half_fft_size + i];
68
69 float wv = v < 0 ? 0 : (v > 0.99 ? 0.99 : v);
70
71 waterfall_slice[i] = (unsigned char) floor(wv * 255.0);
72 }
73
74 unsigned int newBufSize = (half_fft_size*lines_buffered.load()+half_fft_size);
75 if (lineBuffer[j].size() < newBufSize) {
76 lineBuffer[j].resize(newBufSize);
77 rLineBuffer[j].resize(newBufSize);
78 }
79 memcpy(&(lineBuffer[j][half_fft_size*lines_buffered.load()]), waterfall_slice, sizeof(unsigned char) * half_fft_size);
80 }
81 lines_buffered++;
82 }
83 }
84
update()85 void WaterfallPanel::update() {
86 unsigned int half_fft_size = fft_size / 2;
87
88 if (!bufferInitialized.load()) {
89 return;
90 }
91
92 if (!texInitialized.load()) {
93 for (int i = 0; i < 2; i++) {
94 if (waterfall[i]) {
95 glDeleteTextures(1, &waterfall[i]);
96 waterfall[i] = 0;
97 }
98
99 waterfall_ofs[i] = waterfall_lines - 1;
100 }
101
102 glGenTextures(2, waterfall);
103
104 unsigned char *waterfall_tex;
105
106 //Creates 2x 2D textures into card memory.
107 //of size half_fft_size * waterfall_lines, which can be BIG.
108 //The limit of the size of Waterfall is the size of the maximum supported 2D texture
109 //by the graphic card. (half_fft_size * waterfall_lines, i.e DEFAULT_DEMOD_WATERFALL_LINES_NB * DEFAULT_FFT_SIZE/2)
110 waterfall_tex = new unsigned char[half_fft_size * waterfall_lines];
111 memset(waterfall_tex, 0, half_fft_size * waterfall_lines);
112
113 for (unsigned int i : waterfall) {
114 glBindTexture(GL_TEXTURE_2D, i);
115 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
116
117 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
118 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
119 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
120 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
121
122 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, half_fft_size, waterfall_lines, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, (GLvoid *) waterfall_tex);
123 }
124
125 delete[] waterfall_tex;
126
127 refreshTheme();
128
129 texInitialized.store(true);
130 }
131
132 for (int i = 0, iMax = lines_buffered.load(); i < iMax; i++) {
133 for (int j = 0; j < 2; j++) {
134 memcpy(&(rLineBuffer[j][i*half_fft_size]),
135 &(lineBuffer[j][((iMax-1)*half_fft_size)-(i*half_fft_size)]), sizeof(unsigned char) * half_fft_size);
136 }
137 }
138
139 unsigned int run_ofs = 0;
140 while (lines_buffered.load()) {
141 int run_lines = lines_buffered.load();
142 if (run_lines > waterfall_ofs[0]) {
143 run_lines = waterfall_ofs[0];
144 }
145 for (int j = 0; j < 2; j++) {
146 glBindTexture(GL_TEXTURE_2D, waterfall[j]);
147 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, waterfall_ofs[j]-run_lines, half_fft_size, run_lines,
148 GL_COLOR_INDEX, GL_UNSIGNED_BYTE, (GLvoid *) &(rLineBuffer[j][run_ofs]));
149
150 waterfall_ofs[j]-=run_lines;
151
152 if (waterfall_ofs[j] == 0) {
153 waterfall_ofs[j] = waterfall_lines;
154 }
155 }
156 run_ofs += run_lines*half_fft_size;
157 lines_buffered.store(lines_buffered.load()-run_lines);
158 }
159 }
160
drawPanelContents()161 void WaterfallPanel::drawPanelContents() {
162 if (!texInitialized.load()) {
163 return;
164 }
165
166 unsigned int half_fft_size = fft_size / 2;
167
168 glLoadMatrixf(transform.to_ptr());
169
170 glEnable (GL_TEXTURE_2D);
171 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
172
173 if (activeTheme != ThemeMgr::mgr.currentTheme) {
174 refreshTheme();
175 activeTheme = ThemeMgr::mgr.currentTheme;
176 }
177 glColor3f(1.0, 1.0, 1.0);
178
179 GLint vp[4];
180 glGetIntegerv(GL_VIEWPORT, vp);
181
182 float viewWidth = (float) vp[2];
183
184 // some bias to prevent seams at odd scales
185 float half_pixel = 1.0 / viewWidth;
186 float half_texel = 1.0 / (float) half_fft_size;
187 float vtexel = 1.0 / (float) waterfall_lines;
188 float vofs = (float) (waterfall_ofs[0]) * vtexel;
189
190 glBindTexture(GL_TEXTURE_2D, waterfall[0]);
191 glBegin (GL_QUADS);
192 glTexCoord2f(0.0 + half_texel, 1.0 + vofs);
193 glVertex3f(-1.0, -1.0, 0.0);
194 glTexCoord2f(1.0 - half_texel, 1.0 + vofs);
195 glVertex3f(0.0 + half_pixel, -1.0, 0.0);
196 glTexCoord2f(1.0 - half_texel, 0.0 + vofs);
197 glVertex3f(0.0 + half_pixel, 1.0, 0.0);
198 glTexCoord2f(0.0 + half_texel, 0.0 + vofs);
199 glVertex3f(-1.0, 1.0, 0.0);
200 glEnd();
201
202 vofs = (float) (waterfall_ofs[1]) * vtexel;
203 glBindTexture(GL_TEXTURE_2D, waterfall[1]);
204 glBegin(GL_QUADS);
205 glTexCoord2f(0.0 + half_texel, 1.0 + vofs);
206 glVertex3f(0.0 - half_pixel, -1.0, 0.0);
207 glTexCoord2f(1.0 - half_texel, 1.0 + vofs);
208 glVertex3f(1.0, -1.0, 0.0);
209 glTexCoord2f(1.0 - half_texel, 0.0 + vofs);
210 glVertex3f(1.0, 1.0, 0.0);
211 glTexCoord2f(0.0 + half_texel, 0.0 + vofs);
212 glVertex3f(0.0 - half_pixel, 1.0, 0.0);
213 glEnd();
214
215 glBindTexture(GL_TEXTURE_2D, 0);
216
217 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
218 glDisable(GL_TEXTURE_2D);
219 }
220