MythTV master
bumpscope.cpp
Go to the documentation of this file.
1// This was:
2// Bump Scope - Visualization Plugin for XMMS
3// Copyright (C) 1999-2001 Zinx Verituse
4
5// C++ headers
6#include <algorithm>
7#include <cmath>
8#include <cstdlib>
9#include <iostream>
10#include <numbers>
11
12// QT headers
13#include <QCoreApplication>
14#include <QPainter>
15
16// MythTV headers
17#include <libmythbase/compat.h>
20#ifndef __cpp_size_t_suffix
22#endif
23
24// Mythmusic Headers
25#include "bumpscope.h"
26#include "mainvisual.h"
27
29{
30 m_fps = 15;
31
32 for (unsigned int i = 255; i > 0; i--)
33 {
34 m_intense1[i] = cos(((double)(255 - i) * std::numbers::pi) / 512.0);
35 m_intense2[i] = pow(m_intense1[i], 250) * 150;
36 }
37 m_intense1[0] = m_intense1[1];
38 m_intense2[0] = m_intense2[1];
39}
40
42{
43 delete [] m_rgbBuf;
44 delete m_image;
45 for (auto & dat : m_phongDat)
46 dat.clear();
47 m_phongDat.clear();
48}
49
50void BumpScope::resize(const QSize &newsize)
51{
52 m_size = newsize;
53
54 m_size.setHeight((m_size.height() / 2) * 2);
55 m_size.setWidth((m_size.width() / 4) * 4);
56
57 delete [] m_rgbBuf;
58
59 int bufsize = (m_size.height() + 2) * (m_size.width() + 2);
60
61 m_rgbBuf = new unsigned char[bufsize];
62
63 m_bpl = m_size.width() + 2;
64
65 delete m_image;
66 m_image = new QImage(m_size.width(), m_size.height(), QImage::Format_Indexed8);
67
68 m_width = m_size.width();
69 m_height = m_size.height();
71
72 m_x = m_width / 2;
73 m_y = m_height;
74
75#ifdef __cpp_size_t_suffix
76 m_phongDat.resize(m_phongRad * 2UZ);
77 for (auto & dat : m_phongDat)
78 dat.resize(m_phongRad * 2UZ);
79#else
80 m_phongDat.resize(m_phongRad * 2_UZ);
81 for (auto & dat : m_phongDat)
82 dat.resize(m_phongRad * 2_UZ);
83#endif
84
87}
88
89void BumpScope::blur_8(unsigned char *ptr, [[maybe_unused]] int w, int h, ptrdiff_t bpl)
90{
91 uchar *iptr = ptr + bpl + 1;
92 uint i = bpl * h;
93
94 while (i--)
95 {
96 uint sum = (iptr[-bpl] + iptr[-1] + iptr[1] + iptr[bpl]) >> 2;
97 if (sum > 2)
98 sum -= 2;
99 *(iptr++) = sum;
100 }
101}
102
103void BumpScope::generate_cmap(unsigned int color)
104{
105 if (m_image)
106 {
107 uint red = color / 0x10000;
108 uint green = (color % 0x10000) / 0x100;
109 uint blue = color % 0x100;
110
111 for (uint i = 255; i > 0; i--)
112 {
113 uint r = (unsigned int)(((100 * static_cast<double>(red) / 255)
114 * m_intense1[i]) + m_intense2[i]);
115 r = std::min<uint>(r, 255);
116 uint g = (unsigned int)(((100 * static_cast<double>(green) / 255)
117 * m_intense1[i]) + m_intense2[i]);
118 g = std::min<uint>(g, 255);
119 uint b = (unsigned int)(((100 * static_cast<double>(blue) / 255)
120 * m_intense1[i]) + m_intense2[i]);
121 b = std::min<uint>(b, 255);
122
123 m_image->setColor(i, qRgba(r, g, b, 255));
124 }
125
126 m_image->setColor(0, m_image->color(1));
127 }
128}
129
131{
132 unsigned int PHONGRES = m_phongRad * 2;
133
134 for (uint y = 0; y < m_phongRad; y++)
135 {
136 for (uint x = 0; x < m_phongRad; x++)
137 {
138 double i = ((double)x / ((double)m_phongRad)) - 1;
139 double i2 = ((double)y / ((double)m_phongRad)) - 1;
140
141 //if (m_diamond)
142 i = 1 - pow(i*i2,.75) - (i*i) - (i2*i2);
143 //else
144 // i = 1 - i*i - i2*i2;
145
146 if (i >= 0)
147 {
148 //if (m_diamond)
149 i = i*i*i * 255.0;
150 //else
151 // i = i*i*i * 255.0;
152
153 i = std::min<double>(i, 255);
154 auto uci = (unsigned char)i;
155
156 m_phongDat[y][x] = uci;
157 m_phongDat[(PHONGRES-1)-y][x] = uci;
158 m_phongDat[y][(PHONGRES-1)-x] = uci;
159 m_phongDat[(PHONGRES-1)-y][(PHONGRES-1)-x] = uci;
160 }
161 else
162 {
163 m_phongDat[y][x] = 0;
164 m_phongDat[(PHONGRES-1)-y][x] = 0;
165 m_phongDat[y][(PHONGRES-1)-x] = 0;
166 m_phongDat[(PHONGRES-1)-y][(PHONGRES-1)-x] = 0;
167 }
168 }
169 }
170}
171
172static constexpr float M_PI_F { std::numbers::pi_v<float> };
173void BumpScope::translate(int x, int y, int *xo, int *yo, int *xd, int *yd,
174 int *angle) const
175{
176 unsigned int HEIGHT = m_height;
177 unsigned int WIDTH = m_width;
178
179 int wd2 = (int)(WIDTH / 2);
180 int hd2 = (int)(HEIGHT / 2);
181
182 /* try setting y to both maxes */
183 *yo = HEIGHT/2;
184 *angle = (int)(asinf((float)(y-(HEIGHT/2.0F))/(float)*yo)/(M_PI_F/180.0F));
185 *xo = (int)((x-(WIDTH/2.0F))/cosf(*angle*(M_PI_F/180.0F)));
186
187 if (*xo >= -wd2 && *xo <= wd2) {
188 *xd = (*xo>0)?-1:1;
189 *yd = 0;
190 return;
191 }
192
193 *yo = -*yo;
194 *angle = (int)(asinf((float)(y-(HEIGHT/2.0F))/(float)*yo)/(M_PI_F/180.0F));
195 *xo = (int)((x-(WIDTH/2.0F))/cosf(*angle*(M_PI_F/180.0F)));
196
197 if (*xo >= -wd2 && *xo <= wd2) {
198 *xd = (*xo>0)?-1:1;
199 *yd = 0;
200 return;
201 }
202
203 /* try setting x to both maxes */
204 *xo = WIDTH/2;
205 *angle = (int)(acosf((float)(x-(WIDTH/2.0F))/(float)*xo)/(M_PI_F/180.0F));
206 *yo = (int)((y-(HEIGHT/2.0F))/sinf(*angle*(M_PI_F/180.0F)));
207
208 if (*yo >= -hd2 && *yo <= hd2) {
209 *yd = (*yo>0)?-1:1;
210 *xd = 0;
211 return;
212 }
213
214 *xo = -*xo;
215 *angle = (int)(acosf((float)(x-(WIDTH/2.0F))/(float)*xo)/(M_PI_F/180.0F));
216 *yo = (int)((y-(HEIGHT/2.0F))/sinf(*angle*(M_PI_F/180.0F)));
217
218 /* if this isn't right, it's out of our range and we don't care */
219 *yd = (*yo>0)?-1:1;
220 *xd = 0;
221}
222
223inline void BumpScope::draw_vert_line(unsigned char *buffer, int x, int y1,
224 int y2) const
225{
226 if (y1 < y2)
227 {
228 uchar *p = buffer + ((y1 + 1) * m_bpl) + x + 1;
229 for (int y = y1; y <= y2; y++)
230 {
231 *p = 0xff;
232 p += m_bpl;
233 }
234 }
235 else if (y2 < y1)
236 {
237 uchar *p = buffer + ((y2 + 1) * m_bpl) + x + 1;
238 for (int y = y2; y <= y1; y++)
239 {
240 *p = 0xff;
241 p += m_bpl;
242 }
243 }
244 else
245 {
246 buffer[((y1 + 1) * m_bpl) + x + 1] = 0xff;
247 }
248}
249
250void BumpScope::render_light(int lx, int ly)
251{
252 int dy = 0;
253 unsigned int PHONGRES = m_phongRad * 2;
254 unsigned int j = 0;
255
256 int prev_y = m_bpl + 1;
257 int out_y = 0;
258 unsigned char *outputbuf = m_image->bits();
259
260 for (dy = (-ly) + (PHONGRES / 2), j = 0; j < m_height; j++, dy++,
261 prev_y += m_bpl - m_width)
262 {
263 int dx = 0;
264 unsigned int i = 0;
265 for (dx = (-lx) + (PHONGRES / 2), i = 0; i < m_width; i++, dx++,
266 prev_y++, out_y++)
267 {
268 int xp = (m_rgbBuf[prev_y - 1] - m_rgbBuf[prev_y + 1]) + dx;
269 int yp = (m_rgbBuf[prev_y - m_bpl] - m_rgbBuf[prev_y + m_bpl]) + dy;
270
271 if (yp < 0 || yp >= (int)PHONGRES ||
272 xp < 0 || xp >= (int)PHONGRES)
273 {
274 outputbuf[out_y] = 0;
275 continue;
276 }
277
278 outputbuf[out_y] = m_phongDat[yp][xp];
279 }
280 }
281}
282
283void BumpScope::rgb_to_hsv(unsigned int color, double *h, double *s, double *v)
284{
285 double r = (double)(color>>16) / 255.0;
286 double g = (double)((color>>8)&0xff) / 255.0;
287 double b = (double)(color&0xff) / 255.0;
288
289 double max = r;
290 max = std::max(g, max);
291 max = std::max(b, max);
292
293 double min = r;
294 min = std::min(g, min);
295 min = std::min(b, min);
296
297 *v = max;
298
299 if (max != 0.0) *s = (max - min) / max;
300 else *s = 0.0;
301
302 if (*s == 0.0)
303 {
304 *h = 0.0;
305 }
306 else
307 {
308 double delta = max - min;
309
310 if (r == max) *h = (g - b) / delta;
311 else if (g == max) *h = 2.0 + ((b - r) / delta);
312 else if (b == max) *h = 4.0 + ((r - g) / delta);
313
314 *h = *h * 60.0;
315
316 if (*h < 0.0) *h = *h + 360;
317 }
318}
319
320void BumpScope::hsv_to_rgb(double h, double s, double v, unsigned int *color)
321{
322 double r = __builtin_nan("");
323 double g = __builtin_nan("");
324 double b = __builtin_nan("");
325
326 if (s == 0.0)
327 s = 0.000001;
328
329 if (h == -1.0)
330 {
331 r = v; g = v; b = v;
332 }
333 else
334 {
335 if (h == 360.0) h = 0.0;
336 h = h / 60.0;
337 int i = (int) h;
338 double f = h - i;
339 double w = v * (1.0 - s);
340 double q = v * (1.0 - (s * f));
341 double t = v * (1.0 - (s * (1.0 - f)));
342
343 switch (i)
344 {
345 case 0: r = v; g = t; b = w; break;
346 case 1: r = q; g = v; b = w; break;
347 case 2: r = w; g = v; b = t; break;
348 case 3: r = w; g = q; b = v; break;
349 case 4: r = t; g = w; b = v; break;
350 /*case 5: use default to keep gcc from complaining */
351 default: r = v; g = w; b = q; break;
352 }
353 }
354
355 *color = ((unsigned int)(r*255)<<16) | ((unsigned int)(g*255)<<8) | ((unsigned int)(b*255));
356}
357
359{
360 if (!node || node->m_length == 0 || !m_image)
361 return false;
362
363 int numSamps = 512;
364 if (node->m_length < 512)
365 numSamps = node->m_length;
366
367 int prev_y = ((int)m_height / 2) +
368 (((int)node->m_left[0] * (int)m_height) / 0x10000);
369
370 prev_y = std::max(prev_y, 0);
371 if (prev_y >= (int)m_height) prev_y = m_height - 1;
372
373 for (uint i = 0; i < m_width; i++)
374 {
375 int y = (i * numSamps) / (m_width - 1);
376 y = ((int)m_height / 2) +
377 (((int)node->m_left[y] * (int)m_height) / 0x10000);
378
379 y = std::max(y, 0);
380 if (y >= (int)m_height)
381 y = m_height - 1;
382
383 draw_vert_line(m_rgbBuf, i, prev_y, y);
384 prev_y = y;
385 }
386
388
389 return false;
390}
391
392bool BumpScope::draw(QPainter *p, [[maybe_unused]] const QColor &back)
393{
394 if (!m_image || m_image->isNull())
395 {
396 LOG(VB_GENERAL, LOG_ERR, "BumpScope::draw: Bad image");
397 return false;
398 }
399
400 m_ilx = m_x;
401 m_ily = m_y;
402
403 if (m_movingLight)
404 {
405 if (!m_wasMoving)
406 {
408 m_wasMoving = 1;
409 }
410
411 m_ilx = (int)((m_width / 2.0F) + (cosf(m_iangle * (M_PI_F / 180.0F)) * m_ixo));
412 m_ily = (int)((m_height / 2.0F) + (sinf(m_iangle * (M_PI_F / 180.0F)) * m_iyo));
413
414 m_iangle += 2;
415 if (m_iangle >= 360)
416 m_iangle = 0;
417
418 m_ixo += m_ixd;
419 if (m_ixo > ((int)m_width / 2) || m_ixo < -((int)m_width / 2))
420 {
421 m_ixo = (m_ixo > 0) ? (m_width / 2) : -(m_width / 2);
422 if (rand_bool())
423 {
424 m_ixd = (m_ixd > 0) ? -1 : 1;
425 m_iyd = 0;
426 }
427 else
428 {
429 m_iyd = (m_iyd > 0) ? -1 : 1;
430 m_ixd = 0;
431 }
432 }
433
434 m_iyo += m_iyd;
435 if (m_iyo > ((int)m_height / 2) || m_iyo < -((int)m_height / 2))
436 {
437 m_iyo = (m_iyo > 0) ? (m_height / 2) : -(m_height / 2);
438 if (rand_bool())
439 {
440 m_ixd = (m_ixd > 0) ? -1 : 1;
441 m_iyd = 0;
442 }
443 else
444 {
445 m_iyd = (m_iyd > 0) ? -1 : 1;
446 m_ixd = 0;
447 }
448 }
449 }
450
451 if (m_colorCycle)
452 {
453 if (!m_wasColor)
454 {
456 m_wasColor = 1;
457
458 if (rand_bool())
459 {
460 m_ihd = rand_bool() ? -1 : 1;
461 m_isd = 0;
462 }
463 else
464 {
465 m_isd = rand_bool() ? -0.01 : 0.01;
466 m_ihd = 0;
467 }
468 }
469
471
473
474 if (m_ihd)
475 {
476 m_ih += m_ihd;
477 if (m_ih >= 360)
478 m_ih = 0;
479 if (m_ih < 0)
480 m_ih = 359;
481 if (rand_bool(150))
482 {
483 if (rand_bool())
484 {
485 m_ihd = rand_bool() ? -1 : 1;
486 m_isd = 0;
487 }
488 else
489 {
490 m_isd = rand_bool() ? -0.01 : 0.01;
491 m_ihd = 0;
492 }
493 }
494 }
495 else
496 {
497 m_is += m_isd;
498
499 if (m_is <= 0 || m_is >= 0.5)
500 {
501 m_is = std::max<double>(m_is, 0);
502 if (m_is > 0.52)
503 {
504 m_isd = -0.01;
505 }
506 else if (m_is == 0)
507 {
508 m_ihd = MythRandom(0, 360 - 1);
509 m_isd = 0.01;
510 }
511 else
512 {
513 if (rand_bool())
514 {
515 m_ihd = rand_bool() ? -1 : 1;
516 m_isd = 0;
517 }
518 else
519 {
520 m_isd = rand_bool() ? -0.01 : 0.01;
521 m_ihd = 0;
522 }
523 }
524 }
525 }
526 }
527
529
530 p->drawImage(0, 0, *m_image);
531
532 return true;
533}
534
535static class BumpScopeFactory : public VisFactory
536{
537 public:
538 const QString &name(void) const override // VisFactory
539 {
540 static QString s_name = QCoreApplication::translate("Visualizers",
541 "BumpScope");
542 return s_name;
543 }
544
545 uint plugins(QStringList *list) const override // VisFactory
546 {
547 *list << name();
548 return 1;
549 }
550
551 VisualBase *create([[maybe_unused]] MainVisual *parent,
552 [[maybe_unused]] const QString &pluginName) const override // VisFactory
553 {
554 return new BumpScope();
555 }
static constexpr float M_PI_F
Definition: bumpscope.cpp:172
BumpScopeFactory BumpScopeFactory
uint plugins(QStringList *list) const override
Definition: bumpscope.cpp:545
const QString & name(void) const override
Definition: bumpscope.cpp:538
VisualBase * create(MainVisual *parent, const QString &pluginName) const override
Definition: bumpscope.cpp:551
bool m_movingLight
Definition: bumpscope.h:46
int m_ixo
Definition: bumpscope.h:57
bool m_colorCycle
Definition: bumpscope.h:45
double m_isd
Definition: bumpscope.h:68
void translate(int x, int y, int *xo, int *yo, int *xd, int *yd, int *angle) const
Definition: bumpscope.cpp:173
std::array< double, 256 > m_intense2
Definition: bumpscope.h:54
static void blur_8(unsigned char *ptr, int w, int h, ptrdiff_t bpl)
Definition: bumpscope.cpp:89
ptrdiff_t m_bpl
Definition: bumpscope.h:49
int m_iyd
Definition: bumpscope.h:60
unsigned int m_width
Definition: bumpscope.h:41
double m_iv
Definition: bumpscope.h:67
unsigned char * m_rgbBuf
Definition: bumpscope.h:52
void generate_cmap(unsigned int color)
Definition: bumpscope.cpp:103
std::array< double, 256 > m_intense1
Definition: bumpscope.h:53
static void rgb_to_hsv(unsigned int color, double *h, double *s, double *v)
Definition: bumpscope.cpp:283
double m_is
Definition: bumpscope.h:66
void resize(const QSize &size) override
Definition: bumpscope.cpp:50
int m_wasColor
Definition: bumpscope.h:64
double m_ih
Definition: bumpscope.h:65
int m_ihd
Definition: bumpscope.h:69
int m_ily
Definition: bumpscope.h:62
~BumpScope() override
Definition: bumpscope.cpp:41
bool process(VisualNode *node) override
Definition: bumpscope.cpp:358
void draw_vert_line(unsigned char *buffer, int x, int y1, int y2) const
Definition: bumpscope.cpp:223
QImage * m_image
Definition: bumpscope.h:34
int m_ilx
Definition: bumpscope.h:61
void render_light(int lx, int ly)
Definition: bumpscope.cpp:250
std::vector< std::vector< unsigned char > > m_phongDat
Definition: bumpscope.h:51
unsigned int m_y
Definition: bumpscope.h:40
int m_ixd
Definition: bumpscope.h:59
unsigned int m_color
Definition: bumpscope.h:38
QSize m_size
Definition: bumpscope.h:36
int m_iangle
Definition: bumpscope.h:56
static void hsv_to_rgb(double h, double s, double v, unsigned int *color)
Definition: bumpscope.cpp:320
unsigned int m_height
Definition: bumpscope.h:42
int m_wasMoving
Definition: bumpscope.h:63
void generate_phongdat(void)
Definition: bumpscope.cpp:130
bool draw(QPainter *p, const QColor &back) override
Definition: bumpscope.cpp:392
int m_iyo
Definition: bumpscope.h:58
unsigned int m_phongRad
Definition: bumpscope.h:43
unsigned int m_x
Definition: bumpscope.h:39
unsigned int m_icolor
Definition: bumpscope.h:70
int m_fps
Definition: visualize.h:90
long m_length
Definition: videovisual.h:38
short * m_left
Definition: videovisual.h:36
unsigned int uint
Definition: compat.h:60
static std::vector< uint32_t > back
Definition: goom_core.cpp:27
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
Convenience inline random number generator functions.
uint32_t MythRandom()
generate 32 random bits
Definition: mythrandom.h:20
bool rand_bool(uint32_t chance=2)
return a random bool with P(true) = 1/chance
Definition: mythrandom.h:86