1 /*
2  * Copyright (C) 2020 Linux Studio Plugins Project <https://lsp-plug.in/>
3  *           (C) 2020 Vladimir Sadovnikov <sadko4u@gmail.com>
4  *
5  * This file is part of lsp-plugins
6  * Created on: 12 нояб. 2018 г.
7  *
8  * lsp-plugins is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * any later version.
12  *
13  * lsp-plugins is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with lsp-plugins. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <ui/tk/tk.h>
23 #include <core/sugar.h>
24 #include <dsp/dsp.h>
25 
26 namespace lsp
27 {
28     namespace tk
29     {
30         const w_class_t LSPFrameBuffer::metadata = { "LSPFrameBuffer", &LSPGraphItem::metadata };
31 
LSPFrameBuffer(LSPDisplay * dpy)32         LSPFrameBuffer::LSPFrameBuffer(LSPDisplay *dpy): LSPGraphItem(dpy)
33         {
34             nChanges    = 0;
35             nRows       = 0;
36             nCols       = 0;
37             nCurrRow    = 0;
38             vData       = NULL;
39             vTempRGBA   = NULL;
40             pData       = NULL;
41 
42             fTransparency    = 1.0f;
43             nAngle      = 0;
44             fHPos       = -1.0f;
45             fVPos       = 1.0f;
46             fWidth      = 1.0f;
47             fHeight     = 1.0f;
48             bClear      = true;
49             nPalette    = 0;
50             pCalcColor  = &LSPFrameBuffer::calc_rainbow_color;
51 
52             pClass      = &metadata;
53 
54             sBgColor.set_rgba(0.0f, 0.0f, 0.0f, 1.0f); // Full transparency
55             sColor.set_rgba(1.0f, 0.0f, 0.0f, 0.0f);
56 
57             // Store actual color value
58             sColRGBA.r  = sColor.red();
59             sColRGBA.g  = sColor.green();
60             sColRGBA.b  = sColor.blue();
61             sColRGBA.a  = sColor.alpha();
62 
63             sBgRGBA.r   = sBgColor.red();
64             sBgRGBA.g   = sBgColor.green();
65             sBgRGBA.b   = sBgColor.blue();
66             sBgRGBA.a   = sBgColor.alpha();
67         }
68 
~LSPFrameBuffer()69         LSPFrameBuffer::~LSPFrameBuffer()
70         {
71             drop_data();
72         }
73 
drop_data()74         void LSPFrameBuffer::drop_data()
75         {
76             if (vData != NULL)
77             {
78                 free_aligned(pData);
79                 vData = NULL;
80                 pData = NULL;
81             }
82             vTempRGBA   = NULL;
83         }
84 
allocate_buffer()85         void LSPFrameBuffer::allocate_buffer()
86         {
87             size_t amount = nRows * nCols;
88             if (amount <= 0)
89                 return;
90 
91             amount     += nCols * 4; // RGBA x number of columns for temporary buffer
92             vData       = alloc_aligned<float>(pData, amount, ALIGN64);
93             vTempRGBA   = &vData[nRows * nCols];
94         }
95 
get_buffer()96         float *LSPFrameBuffer::get_buffer()
97         {
98             if (vData == NULL)
99                 allocate_buffer();
100             return vData;
101         }
102 
get_rgba_buffer()103         float *LSPFrameBuffer::get_rgba_buffer()
104         {
105             if (vTempRGBA == NULL)
106                 allocate_buffer();
107             return vTempRGBA;
108         }
109 
init()110         status_t LSPFrameBuffer::init()
111         {
112             status_t result = LSPGraphItem::init();
113             if (result != STATUS_OK)
114                 return result;
115 
116             return STATUS_OK;
117         }
118 
destroy()119         void LSPFrameBuffer::destroy()
120         {
121             drop_data();
122         }
123 
check_color_changed()124         void LSPFrameBuffer::check_color_changed()
125         {
126             // Trigger bClean flag if color changed
127             if (!bClear)
128             {
129                 bClear =
130                     (sColor.red() != sColRGBA.r) ||
131                     (sColor.green() != sColRGBA.g) ||
132                     (sColor.blue() != sColRGBA.b) ||
133                     (sColor.alpha() != sColRGBA.a);
134 
135                 if (!bClear)
136                     bClear =
137                         (sBgColor.red() != sBgRGBA.r) ||
138                         (sBgColor.green() != sBgRGBA.g) ||
139                         (sBgColor.blue() != sBgRGBA.b) ||
140                         (sBgColor.alpha() != sBgRGBA.a);
141             }
142 
143             // Store actual color value
144             sColRGBA.r  = sColor.red();
145             sColRGBA.g  = sColor.green();
146             sColRGBA.b  = sColor.blue();
147             sColRGBA.a  = sColor.alpha();
148 
149             sBgRGBA.r   = sBgColor.red();
150             sBgRGBA.g   = sBgColor.green();
151             sBgRGBA.b   = sBgColor.blue();
152             sBgRGBA.a   = sBgColor.alpha();
153         }
154 
append_data(uint32_t row_id,const float * data)155         status_t LSPFrameBuffer::append_data(uint32_t row_id, const float *data)
156         {
157             float *buf = get_buffer();
158             if (buf == NULL)
159                 return STATUS_NO_MEM;
160 
161             if (nCurrRow != row_id)
162             {
163                 lsp_trace("Row out of sync: curr=%d, requested=%d", int(nCurrRow), int(row_id));
164                 bClear      = true;
165             }
166 
167             nCurrRow        = row_id + 1; // Estimate next row number
168             size_t dst_row  = row_id % nRows;
169             dsp::limit2(&buf[dst_row * nCols], data, 0.0f, 1.0f, nCols);
170 //            dsp::limit_saturate2(&buf[dst_row * nCols], data, nCols);
171             query_draw();
172 
173             nChanges++;
174 
175             return STATUS_OK;
176         }
177 
set_rows(size_t rows)178         void LSPFrameBuffer::set_rows(size_t rows)
179         {
180             if (nRows == rows)
181                 return;
182             nRows = rows;
183             drop_data();
184             query_draw();
185         }
186 
set_cols(size_t cols)187         void LSPFrameBuffer::set_cols(size_t cols)
188         {
189             if (nCols == cols)
190                 return;
191             nCols = cols;
192             drop_data();
193             query_draw();
194         }
195 
set_size(size_t rows,size_t cols)196         void LSPFrameBuffer::set_size(size_t rows, size_t cols)
197         {
198             if ((nRows == rows) && (nCols == cols))
199                 return;
200             nRows = rows;
201             nCols = cols;
202             drop_data();
203             query_draw();
204         }
205 
set_angle(size_t angle)206         void LSPFrameBuffer::set_angle(size_t angle)
207         {
208             if (nAngle == angle)
209                 return;
210             nAngle = angle;
211             bClear = true;
212             query_draw();
213         }
214 
set_hpos(float value)215         void LSPFrameBuffer::set_hpos(float value)
216         {
217             if (fHPos == value)
218                 return;
219             fHPos = value;
220             query_draw();
221         }
222 
set_vpos(float value)223         void LSPFrameBuffer::set_vpos(float value)
224         {
225             if (fVPos == value)
226                 return;
227             fVPos = value;
228             query_draw();
229         }
230 
set_width(float value)231         void LSPFrameBuffer::set_width(float value)
232         {
233             if (fWidth == value)
234                 return;
235             fWidth = value;
236             query_draw();
237         }
238 
set_height(float value)239         void LSPFrameBuffer::set_height(float value)
240         {
241             if (fHeight == value)
242                 return;
243             fHeight = value;
244             query_draw();
245         }
246 
set_transparency(float value)247         void LSPFrameBuffer::set_transparency(float value)
248         {
249             if (fTransparency != value)
250                 fTransparency = value;
251             query_draw();
252         }
253 
set_palette(size_t value)254         void LSPFrameBuffer::set_palette(size_t value)
255         {
256             if (nPalette == value)
257                 return;
258 
259             switch (value % 5)
260             {
261                 case 0: pCalcColor  = &LSPFrameBuffer::calc_rainbow_color; break;
262                 case 1: pCalcColor  = &LSPFrameBuffer::calc_fog_color; break;
263                 case 2: pCalcColor  = &LSPFrameBuffer::calc_color; break;
264                 case 3: pCalcColor  = &LSPFrameBuffer::calc_lightness; break;
265                 case 4: pCalcColor  = &LSPFrameBuffer::calc_lightness2; break;
266                 default:
267                     pCalcColor  = &LSPFrameBuffer::calc_rainbow_color; break;
268                     break;
269             }
270 
271             nPalette = value;
272             bClear = true;
273             query_draw();
274         }
275 
calc_rainbow_color(float * rgba,const float * v,size_t n)276         void LSPFrameBuffer::calc_rainbow_color(float *rgba, const float *v, size_t n)
277         {
278             dsp::hsla_hue_eff_t eff;
279             eff.h       = sColor.hue();
280             eff.s       = sColor.saturation();
281             eff.l       = sColor.lightness();
282             eff.a       = sColor.alpha();
283             eff.thresh  = 1.0f / 3.0f;
284 
285             dsp::eff_hsla_hue(rgba, v, &eff, n);
286             dsp::hsla_to_rgba(rgba, rgba, n);
287         }
288 
calc_fog_color(float * rgba,const float * v,size_t n)289         void LSPFrameBuffer::calc_fog_color(float *rgba, const float *v, size_t n)
290         {
291             dsp::hsla_alpha_eff_t eff;
292             eff.h       = sColor.hue();
293             eff.s       = sColor.saturation();
294             eff.l       = sColor.lightness();
295             eff.a       = sColor.alpha();
296 
297             dsp::eff_hsla_alpha(rgba, v, &eff, n);
298             dsp::hsla_to_rgba(rgba, rgba, n);
299         }
300 
calc_color(float * rgba,const float * v,size_t n)301         void LSPFrameBuffer::calc_color(float *rgba, const float *v, size_t n)
302         {
303             dsp::hsla_sat_eff_t eff;
304             eff.h       = sColor.hue();
305             eff.s       = sColor.saturation();
306             eff.l       = sColor.lightness();
307             eff.a       = sColor.alpha();
308             eff.thresh  = 0.25f;
309 
310             dsp::eff_hsla_sat(rgba, v, &eff, n);
311             dsp::hsla_to_rgba(rgba, rgba, n);
312         }
313 
calc_lightness(float * rgba,const float * v,size_t n)314         void LSPFrameBuffer::calc_lightness(float *rgba, const float *v, size_t n)
315         {
316             dsp::hsla_light_eff_t eff;
317             eff.h       = sColor.hue();
318             eff.s       = sColor.saturation();
319             eff.l       = 1.0f;
320             eff.a       = sColor.alpha();
321             eff.thresh  = 0.25f;
322 
323             dsp::eff_hsla_light(rgba, v, &eff, n);
324             dsp::hsla_to_rgba(rgba, rgba, n);
325         }
326 
calc_lightness2(float * rgba,const float * v,size_t n)327         void LSPFrameBuffer::calc_lightness2(float *rgba, const float *v, size_t n)
328         {
329             dsp::hsla_light_eff_t eff;
330             eff.h       = sColor.hue();
331             eff.s       = sColor.saturation();
332             eff.l       = 0.5f;
333             eff.a       = sColor.alpha();
334             eff.thresh  = 0.25f;
335 
336             dsp::eff_hsla_light(rgba, v, &eff, n);
337             dsp::hsla_to_rgba(rgba, rgba, n);
338         }
339 
render(ISurface * s,bool force)340         void LSPFrameBuffer::render(ISurface *s, bool force)
341         {
342             // Check size
343             if ((nRows <= 0) || (nCols <= 0))
344                 return;
345 
346             // Get data buffer
347             float *buf = get_buffer();
348             float *rgba = get_rgba_buffer();
349             if ((buf == NULL) || (rgba == NULL))
350                 return;
351 
352             // Get drawing surface
353             ISurface *pp = get_surface(s, nCols, nRows);
354             if (pp == NULL)
355                 return;
356 
357             // Check whether the color has changed and we need to clear buffer
358             check_color_changed();
359 
360             // Deploy new changes
361             if ((nChanges > 0) || (bClear))
362             {
363                 // Get target buffer for rendering
364                 uint8_t *xp = reinterpret_cast<uint8_t *>(pp->start_direct());
365                 if (xp == NULL)
366                     return;
367 
368                 // Do not draw more than can
369                 if ((nChanges >= nRows) || (bClear))
370                     nChanges    = nRows;
371 
372                 // Shift buffer
373                 size_t stride = pp->stride();
374                 ::memmove(&xp[stride * nChanges], xp, (nRows - nChanges) * stride);
375 
376                 // Draw dots
377                 Color c(1.0f, 0.0f, 0.0f);
378                 size_t row = (nCurrRow + nRows - 1) % nRows;
379 
380                 for (size_t i=0; i<nChanges; ++i, xp += stride)
381                 {
382                     const float *p = &vData[row * nCols];
383                     (this->*pCalcColor)(rgba, p, nCols);
384                     dsp::rgba_to_bgra32(xp, rgba, nCols);
385                     row = (row + nRows - 1) % nRows;
386                 }
387 
388                 pp->end_direct();
389 
390                 nChanges    = 0;
391                 bClear      = false;
392             }
393 
394             // Draw surface on the target
395             float x, y, sx, sy;
396             float ra = -0.5f * nAngle * M_PI;
397 
398             x = 0.5f * (fHPos + 1.0f) * s->width();
399             y = 0.5f * (1.0f - fVPos) * s->height();
400 
401             switch (nAngle & 0x03)
402             {
403                 case 0:
404                     sx = fWidth * s->width() / nCols;
405                     sy = fHeight * s->height() / nRows;
406 
407                     if (sx < 0.0f)
408                         x       -= sx * nCols;
409                     if (sy < 0.0f)
410                         y       -= sy * nRows;
411                     break;
412 
413                 case 1:
414                     sx = fWidth * s->width() / nRows;
415                     sy = fHeight * s->height() / nCols;
416 
417                     if (sx < 0.0f)
418                         x       -= sx * nRows;
419                     if (sy > 0.0f)
420                         y       += sy * nCols;
421                     break;
422 
423                 case 2:
424                     sx = fWidth * s->width() / nCols;
425                     sy = fHeight * s->height() / nRows;
426 
427                     if (sx > 0.0f)
428                         x       += sx * nCols;
429                     if (sy > 0.0f)
430                         y       += sy * nRows;
431                     break;
432 
433                 case 3:
434                     sx = fWidth * s->width() / nRows;
435                     sy = fHeight * s->height() / nCols;
436 
437                     if (sx > 0.0f)
438                         x       += sx * nRows;
439                     if (sy < 0.0f)
440                         y       -= sy * nCols;
441                     break;
442 
443                 default:
444                     break;
445             }
446 
447             s->draw_rotate_alpha(pp, x, y, sx, sy, ra, fTransparency);
448         }
449     } /* namespace tk */
450 } /* namespace lsp */
451