/* xdrv_52_3_berry_leds.ino - Berry scripting language, native fucnctions Copyright (C) 2021 Stephan Hadinger, Berry language by Guan Wenliang https://github.com/Skiars/berry This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifdef USE_BERRY #include #ifdef USE_WS2812 extern uint16_t changeUIntScale(uint16_t inum, uint16_t ifrom_min, uint16_t ifrom_max,uint16_t ito_min, uint16_t ito_max); extern uint32_t ApplyBriGamma(uint32_t color_a /* 0xRRGGBB */, uint32_t bri /* 0..255 */, bool gamma); extern "C" { // Leds_frame.blend(color1:int, color2:int, alpha:int) -> int // int32_t be_leds_blend(bvm *vm); int32_t be_leds_blend(bvm *vm) { int32_t top = be_top(vm); // Get the number of arguments if (top >= 3 && be_isint(vm, 1) && be_isint(vm, 2) && be_isint(vm, 3)) { uint32_t color_a = be_toint(vm, 1); uint32_t color_b = be_toint(vm, 2); uint32_t alpha = be_toint(vm, 3); uint32_t r = (color_a >> 16) & 0xFF; uint32_t g = (color_a >> 8) & 0xFF; uint32_t b = (color_a ) & 0xFF; uint32_t a = (color_a >> 24) & 0xFF; uint32_t r2 = (color_b >> 16) & 0xFF; uint32_t g2 = (color_b >> 8) & 0xFF; uint32_t b2 = (color_b ) & 0xFF; uint32_t a2 = (color_b >> 24) & 0xFF; uint8_t r3 = changeUIntScale(alpha, 0, 255, r2, r); uint8_t g3 = changeUIntScale(alpha, 0, 255, g2, g); uint8_t b3 = changeUIntScale(alpha, 0, 255, b2, b); uint8_t a3 = changeUIntScale(alpha, 0, 255, a2, a); uint32_t rgb = (a3 << 24) | (r3 << 16) | (g3 << 8) | b3; be_pushint(vm, rgb); be_return(vm); } be_raise(vm, "type_error", nullptr); } // Leds_frame.blend_pixels(dest:bytes(), foreground:bytes) -> nil // Destination can be the same as foreground or background // // All calculation are done in `0xAARRGGBB` format, AA=0 if opaque (i.e. ignored) // Background has always alpha = 0 (any other value is ignored) - for simplification // Size is truncated to smallest of all 3 buffers int32_t be_leds_blend_pixels(bvm *vm); int32_t be_leds_blend_pixels(bvm *vm) { int32_t top = be_top(vm); // Get the number of arguments if (top >= 2 && be_isbytes(vm, 2)) { size_t dest_len = 0; uint32_t * dest_buf = (uint32_t*) be_tobytes(vm, 1, &dest_len); // back = dest for now, could be changed in the future size_t back_len = 0; const uint32_t * back_buf = (const uint32_t*) be_tobytes(vm, 1, &back_len); size_t fore_len = 0; const uint32_t * fore_buf = (const uint32_t*) be_tobytes(vm, 2, &fore_len); if (fore_len < dest_len) { dest_len = fore_len; } if (back_len < dest_len) { dest_len = back_len; } size_t pixels_count = dest_len / 4; if (pixels_count > 0) { uint32_t * dest = (uint32_t *)dest_buf; uint32_t * back = (uint32_t *)back_buf; uint32_t * fore = (uint32_t *)fore_buf; for (size_t i = 0; i < pixels_count; i++) { uint32_t back_argb = back[i]; uint32_t fore_argb = fore[i]; uint32_t fore_alpha = (fore_argb >> 24) & 0xFF; uint32_t dest_rgb_new = back_argb; if (fore_alpha == 0) { // opaque layer, copy value from fore dest_rgb_new = fore_argb; } else if (fore_alpha == 255) { // fore is transparent, use back // nothing to do, dest_rgb_new = back_argb above } else { uint32_t back_r = (back_argb >> 16) & 0xFF; uint32_t fore_r = (fore_argb >> 16) & 0xFF; uint32_t back_g = (back_argb >> 8) & 0xFF; uint32_t fore_g = (fore_argb >> 8) & 0xFF; uint32_t back_b = (back_argb ) & 0xFF; uint32_t fore_b = (fore_argb ) & 0xFF; uint8_t dest_r_new = changeUIntScale(fore_alpha, 0, 255, fore_r, back_r); uint8_t dest_g_new = changeUIntScale(fore_alpha, 0, 255, fore_g, back_g); uint8_t dest_b_new = changeUIntScale(fore_alpha, 0, 255, fore_b, back_b); dest_rgb_new = (dest_r_new << 16) | (dest_g_new << 8) | dest_b_new; } dest[i] = dest_rgb_new; } } be_return_nil(vm); } be_raise(vm, "type_error", nullptr); } // Leds_frame.fill_pixels(dest:bytes(), color:int) -> nil // // Fill buffer with same color int32_t be_leds_fill_pixels(bvm *vm); int32_t be_leds_fill_pixels(bvm *vm) { int32_t top = be_top(vm); // Get the number of arguments if (top >= 2 && be_isint(vm, 2)) { size_t dest_len = 0; uint32_t * dest_buf = (uint32_t*) be_tobytes(vm, 1, &dest_len); uint32_t color = be_toint(vm, 2); size_t pixels_count = dest_len / 4; if (pixels_count > 0) { uint32_t * dest = (uint32_t *)dest_buf; for (size_t i = 0; i < pixels_count; i++) { dest[i] = color; } } be_return_nil(vm); } be_raise(vm, "type_error", nullptr); } // Leds_frame.paste_pixels(neopixel:bytes(), led_buffer:bytes(), bri:int 0..100, gamma:bool) // // Copy from ARGB buffer to RGB int32_t be_leds_paste_pixels(bvm *vm); int32_t be_leds_paste_pixels(bvm *vm) { int32_t top = be_top(vm); // Get the number of arguments if (top >= 2 && be_isbytes(vm, 2)) { size_t src_len = 0; uint32_t * src_buf = (uint32_t*) be_tobytes(vm, 1, &src_len); size_t dest_len = 0; uint8_t * dest_buf = (uint8_t*) be_tobytes(vm, 2, &dest_len); uint32_t bri255 = 255; if (top >= 3 && be_isint(vm, 3)) { bri255 = be_toint(vm, 3); } bool gamma = false; if (top >= 4 && be_isbool(vm, 4)) { gamma = be_tobool(vm, 4); } size_t pixels_count = src_len / 4; if (pixels_count > dest_len / 3) { pixels_count = dest_len / 3; } if (pixels_count > 0) { for (size_t i = 0; i < pixels_count; i++) { uint32_t src_argb = ApplyBriGamma(src_buf[i], bri255, gamma); uint32_t src_r = (src_argb >> 16) & 0xFF; uint32_t src_g = (src_argb >> 8) & 0xFF; uint32_t src_b = (src_argb ) & 0xFF; dest_buf[i * 3 + 0] = src_r; dest_buf[i * 3 + 1] = src_g; dest_buf[i * 3 + 2] = src_b; } } be_return_nil(vm); } be_raise(vm, "type_error", nullptr); } } #endif // USE_WS2812 #endif // USE_BERRY