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