├── examples ├── ESPxRGB │ ├── main.ino │ ├── ESPxRGB.ino │ ├── XHsvRgbw.ino │ ├── XRgbw.ino │ ├── XGammaRgbw.ino │ ├── XGammaHsvRgbw.ino │ ├── XGamma.ino │ ├── XHsv.ino │ └── XGenerator.ino └── Benchmarking │ ├── kkhsv.ino │ ├── fast_hsv2rgb.ino │ └── Benchmarking.ino ├── library.properties ├── .gitignore ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── keywords.txt ├── src ├── options.h ├── xrgb2rgbw.S ├── xtables.S ├── ESPxRGB.h ├── xrgbgamma.S └── xhsv2rgb.S ├── README.md └── LICENSE /examples/ESPxRGB/main.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Main Example Routine 3 | * 4 | Perform function checks 5 | */ 6 | void setup() 7 | { 8 | Serial.begin(115200); 9 | 10 | checkGAMMA(); 11 | checkHSV(); 12 | checkRGBW(); 13 | checkGAMMARGBW(); 14 | checkHSVRGBW(); 15 | checkGAMMAHSVRGBW(); 16 | //genWave(); 17 | } 18 | 19 | 20 | /* 21 | Not used 22 | */ 23 | void loop() 24 | { 25 | } 26 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=ESPxRGB 2 | version=1.0.0 3 | author=technosf 4 | maintainer=technosf 5 | sentence=RGB manipulation functions in Xtensa assembler for ESP SoCs 6 | paragraph=Fast RGB, RGBW, HSV conversion functions and gamma and chromiance correction in raw Xtensa assembler. 7 | category=Other 8 | url=https://github.com/technosf/ESPxRGB 9 | architectures=esp32 10 | includes=ESPxRGB.h 11 | -------------------------------------------------------------------------------- /examples/ESPxRGB/ESPxRGB.ino: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | ESPxRGB Library 4 | 5 | This Sketch set organizes checks of the library 6 | and generation of reference tables. 7 | 8 | This is the super sketch containing globals etc. 9 | Set up and loop are in main. 10 | 11 | */ 12 | 13 | #include "options.h" // Library configuration options 14 | #include "ESPxRGB.h" // Library definition header 15 | 16 | // Test variables 17 | uint16_t h; 18 | uint8_t s, v, r , g , b , w; 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # settings 35 | *.vscode 36 | *.code-workspace 37 | -------------------------------------------------------------------------------- /examples/ESPxRGB/XHsvRgbw.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Checks for HSV RGBW functions 3 | */ 4 | 5 | void checkHSVRGBW() 6 | 7 | { 8 | 9 | #ifdef __HSV_RGBW__ 10 | 11 | /* 12 | HSV to RGBW checks for default HSV variant 13 | */ 14 | h = 0x0280; 15 | s = 0x80; 16 | v = 0x80; 17 | 18 | Serial.printf( "\n\nHSV to RGB {Spectrum}\n\tfor: 0x%04x, 0x%02x, 0x%02x", h, s, v ); 19 | xhsv2rgb8( h, s, v, &r, &g , &b ); 20 | Serial.printf( "\n\t is: 0x%02x, 0x%02x, 0x%02x", r, g, b ); 21 | 22 | #endif // __HSV_RGBW__ 23 | 24 | } // checkHSVRGBW() 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Additional context** 24 | Add any other context about the problem here. 25 | -------------------------------------------------------------------------------- /examples/ESPxRGB/XRgbw.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Checks for RGBW functions 3 | */ 4 | 5 | void checkRGBW() 6 | { 7 | 8 | #ifdef __RGBW__ 9 | 10 | /* 11 | RGB to RGBW conversion on 8-bit range 12 | 13 | xrgb2rgbw8( uint8_t *r, uint8_t *g , uint8_t *b, uint8_t *w ); // Done 14 | */ 15 | 16 | r = 0x99; 17 | g = 0x05; 18 | b = 0xf0; 19 | w = 0xAA; 20 | 21 | Serial.printf( "\n\nRGB to RGBW\n\tfor: 0x%02x, 0x%02x, 0x%02x, 0x%02x", r, g, b, w ); 22 | xrgb2rgbw8( &r, &g , &b, &w ); 23 | Serial.printf( "\n\t is: 0x%02x, 0x%02x, 0x%02x, 0x%02x", r, g, b, w ); 24 | 25 | #endif // __RGBW__ 26 | 27 | } // checkRGBW() 28 | -------------------------------------------------------------------------------- /examples/ESPxRGB/XGammaRgbw.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Checks for Gamma RGBW functions 3 | */ 4 | 5 | void checkGAMMARGBW() 6 | 7 | { 8 | 9 | #ifdef __GAMMA_RGBW__ 10 | 11 | /* 12 | RGB to RGBW with Gamma correction for 8-bit range 13 | 14 | oid xrgb2rgbwgamma8( uint8_t *r, uint8_t *g , uint8_t *b, uint8_t *w ) 15 | */ 16 | 17 | r = 0x99; 18 | g = 0x05; 19 | b = 0xf0; 20 | w = 0xAA; 21 | 22 | Serial.printf( "\n\nRGB to RGBW\nwith Gamma Correction\n\tfor: 0x%02x, 0x%02x, 0x%02x", r, g, b, w ); 23 | xrgb2rgbwgamma8( &r, &g , &b, &w ); 24 | Serial.printf( "\n\t is: 0x%02x, 0x%02x, 0x%02x, 0x%02x", r, g, b, w ); 25 | 26 | #endif // __GAMMA_RGBW__ 27 | 28 | } // checkGAMMARGBW() 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /examples/ESPxRGB/XGammaHsvRgbw.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Checks for Gamma HSV RGBW functions 3 | */ 4 | 5 | void checkGAMMAHSVRGBW() 6 | { 7 | 8 | #ifdef __GAMMA_HSV_RGBW__ 9 | 10 | /* 11 | HSV to RGB/RGBW conversion with Gamma correction for 8-bit range 12 | */ 13 | 14 | h = 0x0280; 15 | s = 0x80; 16 | v = 0x80; 17 | 18 | Serial.printf( "\n\nHSV to RGB\nwith Gamma Correction\n\tfor: 0x%04x, 0x%02x, 0x%02x", h, s, v ); 19 | xhsv2rgbgamma8( h, s, v, &r, &g, &b ); 20 | Serial.printf( "\n\t is: 0x%02x, 0x%02x, 0x%02x", r, g, b ); 21 | 22 | h = 0x0280; 23 | s = 0x80; 24 | v = 0x80; 25 | 26 | Serial.printf( "\n\nHSV to RGBW\nwith Gamma Correction\n\tfor: 0x%04x, 0x%02x, 0x%02x", h, s, v ); 27 | xhsv2rgbwgamma8( h, s, v, &r, &g, &b, &w ); 28 | Serial.printf( "\n\t is: 0x%02x, 0x%02x, 0x%02x, 0x%02x", r, g, b, w ); 29 | 30 | #endif // __GAMMA_HSV_RGBW__ 31 | 32 | } // checkGAMMAHSVRGBW() 33 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | ####################################### 10 | # Methods and Functions (KEYWORD2) 11 | ####################################### 12 | 13 | xgamma8 KEYWORD2 14 | xgammaarray8 KEYWORD2 15 | 16 | xrgbgamma8 KEYWORD2 17 | xrgbwgamma8 KEYWORD2 18 | 19 | xhsv2rgb8 KEYWORD2 20 | xhsv2rgbgamma8 KEYWORD2 21 | 22 | xhsv2rgbw8s KEYWORD2 23 | xhsv2rgbw8e KEYWORD2 24 | xhsv2rgbw8w KEYWORD2 25 | xhsv2rgbw8t KEYWORD2 26 | xhsv2rgbw8 KEYWORD2 27 | 28 | xrgb2rgbw8 KEYWORD2 29 | 30 | xrgb2rgbwgamma8 KEYWORD2 31 | xhsv2rgbw8 KEYWORD2 32 | xhsv2rgbgamma8 KEYWORD2 33 | xhsv2rgbwgamma8 KEYWORD2 34 | 35 | ###################################### 36 | # Constants (LITERAL1) 37 | ####################################### 38 | 39 | -------------------------------------------------------------------------------- /src/options.h: -------------------------------------------------------------------------------- 1 | /* 2 | ESPxRGB Library Options 3 | 4 | Compile options for ESPxRGB 5 | 6 | */ 7 | 8 | #if !defined (ARDUINO_ARCH_ESP32) && !defined (ARDUINO_ARCH_ESP8266) 9 | #error "ESP target required for compilation" 10 | #endif 11 | 12 | #ifndef _ASMLANGUAGE /* conditionalize to avoid cpp warnings (3rd parties might use same macro) */ 13 | #define _ASMLANGUAGE 14 | #endif 15 | 16 | 17 | /* 18 | Features to compile in 19 | */ 20 | #define __GAMMA__ // Gamma correction functions 21 | #define __HSV__ // HSV to RGB conversion functions 22 | #define __RGBW__ // RGB to RGBW conversion functions 23 | 24 | 25 | /* 26 | * Compile logic 27 | */ 28 | #if defined (__GAMMA__ ) && defined ( __RGBW__ ) 29 | #define __GAMMA_RGBW__ 30 | #endif 31 | 32 | #if defined ( __HSV__ ) && defined ( __RGBW__ ) 33 | #define __HSV_RGBW__ 34 | #endif 35 | 36 | #if defined (__GAMMA__ ) && defined ( __HSV__ )&& defined ( __RGBW__ ) 37 | #define __GAMMA_HSV_RGBW__ 38 | #endif 39 | 40 | 41 | /* 42 | Function Configurations 43 | */ 44 | 45 | #ifdef __GAMMA__ 46 | 47 | #define GAMMA_CORRECTION 2.8 // Gamma correction used to generate table in sketch 48 | 49 | // 50 | //#define __GAMMA_CHECK_SKETCH__ // Sketch checks its generated gamma table against the compiled function 51 | // 52 | 53 | #endif // __GAMMA__ 54 | -------------------------------------------------------------------------------- /examples/Benchmarking/kkhsv.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Modified from code from that 3 | 4 | created 05-01-2010 by kasperkamperman.com 5 | */ 6 | 7 | void getKKRGB(uint16_t hue, uint8_t sat, uint8_t val, uint8_t *R, uint8_t *G , uint8_t *B) 8 | { 9 | 10 | uint8_t r, g, b, base, sextant, step; 11 | 12 | if (sat == 0 || val == 0) { // Acromatic color (gray). Hue doesn't mind. 13 | *R = val; 14 | *G = val; 15 | *B = val; 16 | } else { 17 | 18 | sextant = hue >> 8; 19 | step = hue % 256; 20 | if ( sextant >= 6 ) 21 | { 22 | sextant = 5; 23 | step = 255; 24 | } 25 | base = ((255 - sat) * val) >> 8; 26 | 27 | 28 | switch (sextant) { 29 | case 0: 30 | r = val; 31 | g = (((val - base) * step) / 255) + base; 32 | b = base; 33 | break; 34 | 35 | case 1: 36 | r = (((val - base) * (255 - step)) / 255) + base; 37 | g = val; 38 | b = base; 39 | break; 40 | 41 | case 2: 42 | r = base; 43 | g = val; 44 | b = (((val - base) * step) / 255) + base; 45 | break; 46 | 47 | case 3: 48 | r = base; 49 | g = (((val - base) * (255 - step)) / 255) + base; 50 | b = val; 51 | break; 52 | 53 | case 4: 54 | r = (((val - base) * step) / 255) + base; 55 | g = base; 56 | b = val; 57 | break; 58 | 59 | case 5: 60 | r = val; 61 | g = base; 62 | b = (((val - base) * (255 - step)) / 255) + base; 63 | break; 64 | } 65 | 66 | *R = r; 67 | *G = g; 68 | *B = b; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /examples/ESPxRGB/XGamma.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Checks for Gamma functions 3 | */ 4 | 5 | void checkGAMMA() 6 | { 7 | 8 | #ifdef __GAMMA__ 9 | 10 | /* 11 | Gamma correction on an 8-bit range 12 | 13 | uint8_t xgamma8( uint8_t val ); 14 | */ 15 | 16 | Serial.printf( "\n\nGamma\n\tfor: %02x\n\t is: %02x", 0x99, xgamma8(0x99) ); 17 | 18 | /* 19 | Gamma correction over an array of 8-bit values 20 | 21 | void xgammaarray8( uint8_t *pntr, uint8_t count ); 22 | */ 23 | 24 | uint8_t ga [] = { 0x99, 0x05, 0xf0, 0x25, 0x10, 0xff }; 25 | Serial.printf( "\n\nGamma Array\n\tfor: 0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x", *ga, *(ga + 1), *(ga + 2), *(ga + 3), *(ga + 4), *(ga + 5) ); 26 | xgammaarray8( ga, 0x5 ); 27 | Serial.printf( "\n\t is: 0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x", *ga, *(ga + 1), *(ga + 2), *(ga + 3), *(ga + 4), *(ga + 5) ); 28 | 29 | 30 | /* 31 | Gamma correct on RGB/W in 8-bit range 32 | 33 | void xrgbgamma8( uint8_t *r, uint8_t *g , uint8_t *b ); 34 | void xrgbwgamma8( uint8_t *r, uint8_t *g , uint8_t *b, uint8_t *w ); 35 | */ 36 | 37 | r = 0x99; 38 | g = 0x05; 39 | b = 0xf0; 40 | 41 | Serial.printf( "\n\nGamma RGB\n\tfor: %02x, %02x, %02x", r, g, b ); 42 | xrgbgamma8( &r, &g , &b ); 43 | Serial.printf( "\n\t is: %02x, %02x, %02x", r, g, b); 44 | 45 | r = 0x99; 46 | g = 0x05; 47 | b = 0xf0; 48 | w = 0xAA; 49 | 50 | Serial.printf( "\n\nGamma RGBW\n\tfor: 0x%02x, 0x%02x, 0x%02x", r, g, b ); 51 | xrgbwgamma8( &r, &g , &b, &w ); 52 | Serial.printf( "\n\t is: 0x%02x, 0x%02x, 0x%02x, 0x%02x", r, g, b, w ); 53 | 54 | 55 | #endif // __GAMMA__ 56 | 57 | } //checkGAMMA() 58 | -------------------------------------------------------------------------------- /examples/ESPxRGB/XHsv.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Checks for HSV Tests and HSV Luma-Reference functions 3 | */ 4 | 5 | #include "options.h" 6 | 7 | uint8_t luma(byte R, byte G, byte B); 8 | 9 | 10 | void checkHSV() 11 | { 12 | 13 | // HSV Checks 14 | 15 | #ifdef __HSV__ 16 | 17 | /* 18 | HSV to RGB checks for S, E, W, T variants 19 | */ 20 | h = 0x0280; 21 | s = 0x80; 22 | v = 0x80; 23 | 24 | Serial.printf( "\n\nHSV to RGB {Spectrum}\n\tfor: 0x%04x, 0x%02x, 0x%02x", h, s, v ); 25 | xhsv2rgb8s( h, s, v, &r, &g , &b ); 26 | Serial.printf( "\n\t is: 0x%02x, 0x%02x, 0x%02x", r, g, b ); 27 | 28 | Serial.printf( "\n\nHSV to RGB {Efficient}\n\tfor: 0x%04x, 0x%02x, 0x%02x", h, s, v ); 29 | xhsv2rgb8e( h, s, v, &r, &g , &b ); 30 | Serial.printf( "\n\t is: 0x%02x, 0x%02x, 0x%02x", r, g, b ); 31 | 32 | Serial.printf( "\n\nHSV to RGB {Wave}\n\tfor: 0x%04x, 0x%02x, 0x%02x", h, s, v ); 33 | xhsv2rgb8w( h, s, v, &r, &g , &b ); 34 | Serial.printf( "\n\t is: 0x%02x, 0x%02x, 0x%02x", r, g, b ); 35 | 36 | Serial.printf( "\n\nHSV to RGB {Tweaked}\n\tfor: 0x%04x, 0x%02x, 0x%02x", h, s, v ); 37 | xhsv2rgb8t( h, s, v, &r, &g , &b ); 38 | Serial.printf( "\n\t is: 0x%02x, 0x%02x, 0x%02x", r, g, b ); 39 | 40 | 41 | 42 | /* 43 | HSV Relative Lumanance correction table info 44 | 45 | Uncomment the lumanance function of your choice below 46 | 47 | */ 48 | 49 | uint8_t sr, sg, sb, er, eg, eb, wr, wg, wb, tr, tg, tb; 50 | Serial.println("\n\nIndex\tHue == Spectrum - Eficient - Sine Wave - Tweaked"); 51 | Serial.println("Index\t R\t\t\t G\t\t\t B\t\t\t L"); 52 | for ( uint16_t i = 0; i < 1535 ; i += 6 ) 53 | { 54 | xhsv2rgb8s( i, 255, 255, &sr, &sg , &sb ); 55 | xhsv2rgb8e( i, 255, 255, &er, &eg , &eb ); 56 | xhsv2rgb8w( i, 255, 255, &wr, &wg , &wb ); 57 | xhsv2rgb8t( i, 255, 255, &tr, &tg , &tb ); 58 | Serial.printf("%03u\t%02x-%02x-%02x-%02x", i / 6, sr, er, wr, tr ); 59 | Serial.printf("\t\t%02x-%02x-%02x-%02x", sg, eg, wg, tg ); 60 | Serial.printf("\t\t%02x-%02x-%02x-%02x", sb, eb, wb, tb ); 61 | Serial.printf("\t\t%02x-%02x-%02x-%02x\n", luma(sr, sg, sb), luma(er, eg, eb), luma(wr, wg, wb), luma(tr, tg, tb) ); 62 | } 63 | 64 | #endif // __HSV__ 65 | 66 | } // checkHSV() 67 | 68 | 69 | // ============= Value Functions ============== 70 | 71 | /* 72 | Luminance of given RGB 73 | Choice of three standards, uncomment your choice 74 | */ 75 | uint8_t luma(byte R, byte G, byte B) 76 | { 77 | // return byte( ( 0.299 * R ) + ( 0.587 * G ) + ( 0.114 * B ) ); // ITU-R BT.601 78 | // return byte( ( 0.2126 * R ) + ( 0.7152 * G ) + ( 0.0722 * B ) ); // ITU-R BT.709 79 | return byte( ( 0.212 * R ) + ( 0.701 * G ) + ( 0.087 * B ) ); // SMPTE 240M 80 | return 0; 81 | } 82 | -------------------------------------------------------------------------------- /examples/ESPxRGB/XGenerator.ino: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Generators for Lookup tables used in ESPxRGB 4 | 5 | */ 6 | 7 | byte _slice(double angle); 8 | 9 | // =========== Lookup Table Generators ============= 10 | 11 | /* 12 | * Gamma correction generator 13 | */ 14 | void genGamma( float gammacorrection, unsigned int inMax, unsigned int outMax, unsigned int * garray ) 15 | { 16 | *garray++ = 0; 17 | unsigned int x; 18 | for ( float i = 1.0; i < inMax; i++ ) 19 | { 20 | x = ( pow( i / inMax, gammacorrection ) * outMax ) + 0.5 ; 21 | //x = pow( 2.0, ( i + 64 ) / 41 ) - 1; 22 | *garray++ = x; 23 | } 24 | } 25 | 26 | /* 27 | * Quarter Sine Wave generator 28 | */ 29 | void genWave() 30 | { 31 | Serial.println("\nESPxRGB\nQuarter Wave Lookup ASM data\n"); 32 | 33 | for (int i = 0; i < 256; i += 16) 34 | { 35 | Serial.printf("\n.word"); 36 | boolean comma = false; 37 | for (int j = i; j < i + 16; j += 4 ) 38 | { 39 | if ( comma ) Serial.printf(","); 40 | comma = true; 41 | Serial.printf(" 0x%02x%02x%02x%02x", _slice(j+3), _slice(j+2), _slice(j+1), _slice(j)); 42 | } 43 | } 44 | Serial.println(); 45 | } 46 | 47 | byte _slice(double angle) 48 | { 49 | const double XPI = 3.141592653589793238463; 50 | const double incr = XPI / 512; // (PI/2)/256 51 | const double offset = 3 * XPI / 2; 52 | 53 | 54 | return 128*(sin(offset + (angle*incr))+1); 55 | } 56 | 57 | // ============= Table Checkers ============= 58 | 59 | #ifdef __GAMMA_CHECK_SKETCH__ 60 | 61 | /* 62 | Check the Gamma function against the values from the sketch generated table 63 | */ 64 | void checkGamma( int entries, unsigned int gammaArray [] ) 65 | { 66 | int errors = 0; 67 | 68 | Serial.println("\n\nGamma Check ASM data"); 69 | 70 | for ( unsigned int i = 0; i < entries; i++ ) 71 | { 72 | if ( gammaArray[i] != xgamma8(i) ) 73 | { 74 | errors++; 75 | Serial.printf( "Gamma error index:%u Expected: %02x Got: %02x\n", i, gammaArray[i], xgamma8(i) ); 76 | } 77 | } 78 | 79 | if ( errors = 0 ) 80 | { 81 | Serial.println("Gamma Lookup ASM data OK!"); 82 | } 83 | else 84 | { 85 | Serial.printf("Gamma Lookup ASM data for %u errors out of %u entries.\n", errors, entries ); 86 | } 87 | } // checkGamma 88 | 89 | 90 | /* 91 | Calculate the gamma tabe and copmare to the one deployed in the library 92 | */ 93 | void genGammaTable() 94 | { 95 | Serial.println("\nESPxRGB\n\nGamma Lookup ASM data\n"); 96 | 97 | unsigned int ledRange = 256; 98 | unsigned int horizwords = 16; 99 | unsigned int gammaArray [ledRange]; 100 | calGamma( GAMMA_CORRECTION, ledRange, ledRange, gammaArray ); 101 | 102 | for (int i = 0; i < ledRange; i += horizwords) 103 | { 104 | Serial.printf("\n.word"); 105 | boolean comma = false; 106 | for (int j = i; j < i + horizwords; j += 4 ) 107 | { 108 | if ( comma ) Serial.printf(","); 109 | comma = true; 110 | Serial.printf(" 0x%02x%02x%02x%02x", gammaArray[j + 3], gammaArray[j + 2], gammaArray[j + 1], gammaArray[j]); 111 | } 112 | } 113 | 114 | checkGamma( ledRange, gammaArray ); 115 | 116 | } // genGammaTable 117 | 118 | 119 | #endif // __GAMMA_CHECK_SKETCH__ 120 | -------------------------------------------------------------------------------- /src/xrgb2rgbw.S: -------------------------------------------------------------------------------- 1 | /* 2 | ESPxRGB Library RGB/W Functions 3 | 4 | v0.1.0 5 | 6 | Copyright 2019 technosf [https://github.com/technosf] 7 | 8 | Licensed under the GNU LESSER GENERAL PUBLIC LICENSE, Version 3.0 or greater (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | 12 | https://www.gnu.org/licenses/lgpl-3.0.en.html 13 | Unless required by applicable law or agreed to in writing, 14 | software distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and limitations under the License. 17 | */ 18 | /* 19 | ESPxRGB Library RGB to RGBW conversion functions 20 | 21 | Param: Reg in: Size: 22 | r* a2 1 bytes 23 | g* a3 1 bytes 24 | b* a4 1 bytes 25 | w* a5 1 bytes 26 | 27 | M = max(Ri,Gi,Bi) 28 | m = min(Ri,Gi,Bi) 29 | 30 | Wo = if (m/M < 0.5) use ( (m*M) / (M-m) ) else M 31 | Q = 255 32 | K = (Wo + M) / m 33 | Ro = floor( [ ( K * Ri ) - Wo ] / Q ) 34 | Go = floor( [ ( K * Gi ) - Wo ] / Q ) 35 | Bo = floor( [ ( K * Bi ) - Wo ] / Q ) 36 | */ 37 | 38 | #include "options.h" 39 | 40 | 41 | #ifndef __RGBW__ 42 | #warning "RGBW functions are not included." 43 | #else 44 | 45 | // Inputs 46 | #define p_R a2 47 | #define p_G a3 48 | #define p_B a4 49 | #define p_W a5 50 | 51 | // Working storage 52 | #define R a6 53 | #define G a7 54 | #define B a8 55 | #define m a9 56 | #define M a10 57 | #define W a11 58 | #define K a12 59 | #define Q a13 60 | #define temp a14 61 | 62 | 63 | // -------------- Macros -------------- 64 | 65 | /* 66 | floor( [ ( K * Color ) - White ] / Q ) 67 | Store result 68 | */ 69 | .macro m_calcoffset color: req, pntr: req 70 | mul16u \color, \color, K 71 | sub \color, \color, W 72 | quou \color, \color, Q 73 | s8i \color, \pntr, 0 74 | .endm 75 | 76 | 77 | .text 78 | 79 | // --------------- Externals --------------- 80 | 81 | .global xrgb2rgbw8 82 | 83 | // --------------- Code --------------- 84 | 85 | /* 86 | RGB to RGBW 8 bit conversion 87 | 88 | */ 89 | .type xrgb2rgbw8, @function 90 | .align 4 91 | xrgb2rgbw8: 92 | .frame a1, 32 93 | entry a1, 32 94 | 95 | 96 | _rgb2rgbw8: 97 | // Get the color values from the pointers 98 | l8ui R, p_R, 0 99 | l8ui G, p_G, 0 100 | l8ui B, p_B, 0 101 | movi W, 0 102 | 103 | // Max - M = max(Ri,Gi,Bi) 104 | maxu M, R, G 105 | maxu M, M, B 106 | beqz M, _exit // Max is zero, w = zero 107 | 108 | // Min - m = min(Ri,Gi,Bi) 109 | minu m, R, G 110 | minu m, m, B 111 | beqz m, _exit // Min is zero, w = Max 112 | 113 | mov W, M // W = M, default 114 | slli temp, m, 1 // temp = 2m 115 | bge M, temp, _k // M > 2 * m, bypass calc, use W = M 116 | 117 | mul16u W, m, M // W = M*m 118 | sub temp, M, m // temp = M -m 119 | quou W, W, temp // W = (M * m) / (M - m) 120 | 121 | 122 | _k: 123 | add K, W, M // K = (Wo + M) 124 | quou K, K, m // K = (Wo + M) / m 125 | 126 | movi Q, 0xFF 127 | m_calcoffset R, p_R 128 | m_calcoffset G, p_G 129 | m_calcoffset B, p_B 130 | 131 | _exit: 132 | 133 | s8i W, p_W, 0 134 | retw.n 135 | 136 | #endif // __RGBW__ 137 | -------------------------------------------------------------------------------- /src/xtables.S: -------------------------------------------------------------------------------- 1 | /* 2 | ESPxRGB Look-Up Tables 3 | 4 | v0.1.0 5 | 6 | Copyright 2019 technosf [https://github.com/technosf] 7 | 8 | Licensed under the GNU LESSER GENERAL PUBLIC LICENSE, Version 3.0 or greater (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | 12 | https://www.gnu.org/licenses/lgpl-3.0.en.html 13 | Unless required by applicable law or agreed to in writing, 14 | software distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and limitations under the License. 17 | */ 18 | 19 | 20 | // --------------------- Externals --------------------- 21 | 22 | .global _gamma8_table 23 | .global _dim_curve 24 | .global _wave_quarter 25 | 26 | // ============ Data ============ 27 | 28 | .data 29 | /* 30 | * Gamma correction table generated by the sketch 31 | * 32 | * To change the correction factor, regen the table 33 | * replace the data below 34 | */ 35 | 36 | _gamma8_table: 37 | .word 0x00000000, 0x00000000, 0x00000000, 0x00000000 38 | .word 0x00000000, 0x00000000, 0x00000000, 0x01010101 39 | .word 0x01010101, 0x01010101, 0x02020201, 0x02020202 40 | .word 0x03030202, 0x03030303, 0x04040404, 0x05050504 41 | .word 0x06060605, 0x07070706, 0x08080807, 0x0a090909 42 | .word 0x0b0b0a0a, 0x0c0c0c0b, 0x0e0e0d0d, 0x100f0f0f 43 | .word 0x12111110, 0x14131312, 0x16161515, 0x19181717 44 | .word 0x1b1b1a19, 0x1e1d1d1c, 0x21201f1f, 0x24232222 45 | .word 0x27262625, 0x2b2a2928, 0x2e2d2c2c, 0x3231302f 46 | .word 0x36353433, 0x3a393837, 0x3f3e3d3b, 0x43424140 47 | .word 0x48474645, 0x4d4c4b4a, 0x5351504f, 0x58575554 48 | .word 0x5e5d5b5a, 0x6462615f, 0x6a696766, 0x716f6d6c 49 | .word 0x77767472, 0x7e7d7b79, 0x86848280, 0x8d8b8988 50 | .word 0x9593918f, 0x9d9b9997, 0xa5a3a19f, 0xaeacaaa7 51 | .word 0xb7b5b2b0, 0xc0bebbb9, 0xc9c7c5c2, 0xd3d1cecc 52 | .word 0xdddbd8d6, 0xe8e5e2e0, 0xf2f0edea, 0xfdfaf8f5 53 | 54 | _dim_curve: 55 | .word 0x00000000, 0x00000000, 0x00000000, 0x00000000 56 | .word 0x00000000, 0x00000000, 0x00000000, 0x01010101 57 | .word 0x01010101, 0x01010101, 0x02020201, 0x02020202 58 | .word 0x03030202, 0x03030303, 0x04040404, 0x05050504 59 | .word 0x06060605, 0x07070706, 0x08080807, 0x0a090909 60 | .word 0x0b0b0a0a, 0x0c0c0c0b, 0x0e0e0d0d, 0x100f0f0f 61 | .word 0x12111110, 0x14131312, 0x16161515, 0x19181717 62 | .word 0x1b1b1a19, 0x1e1d1d1c, 0x21201f1f, 0x24232222 63 | .word 0x27262625, 0x2b2a2928, 0x2e2d2c2c, 0x3231302f 64 | .word 0x36353433, 0x3a393837, 0x3f3e3d3b, 0x43424140 65 | .word 0x48474645, 0x4d4c4b4a, 0x5351504f, 0x58575554 66 | .word 0x5e5d5b5a, 0x6462615f, 0x6a696766, 0x716f6d6c 67 | .word 0x77767472, 0x7e7d7b79, 0x86848280, 0x8d8b8988 68 | .word 0x9593918f, 0x9d9b9997, 0xa5a3a19f, 0xaeacaaa7 69 | .word 0xb7b5b2b0, 0xc0bebbb9, 0xc9c7c5c2, 0xd3d1cecc 70 | .word 0xdddbd8d6, 0xe8e5e2e0, 0xf2f0edea, 0xfdfaf8f5 71 | 72 | _wave_quarter: 73 | /* 74 | * Quarter wave, 256 steps, 0x7F max 75 | */ 76 | .word 0x00000000, 0x00000000, 0x00000000, 0x00000000 77 | .word 0x00000000, 0x01010100, 0x01010101, 0x02020201 78 | .word 0x02020202, 0x03030303, 0x04040403, 0x05050404 79 | .word 0x06050505, 0x07060606, 0x08080707, 0x09090808 80 | .word 0x0a0a0a09, 0x0b0b0b0a, 0x0d0c0c0c, 0x0e0e0e0d 81 | .word 0x100f0f0f, 0x11111110, 0x13131212, 0x15141413 82 | .word 0x16161615, 0x18181717, 0x1a1a1919, 0x1c1c1b1b 83 | .word 0x1e1e1d1d, 0x20201f1f, 0x22222121, 0x24242323 84 | .word 0x27262625, 0x29282827, 0x2b2b2a2a, 0x2e2d2c2c 85 | .word 0x30302f2e, 0x33323131, 0x35353433, 0x38373636 86 | .word 0x3a3a3938, 0x3d3c3c3b, 0x403f3e3e, 0x42424140 87 | .word 0x45454443, 0x48474746, 0x4b4a4949, 0x4e4d4c4c 88 | .word 0x51504f4f, 0x54535251, 0x57565554, 0x5a595857 89 | .word 0x5d5c5b5a, 0x605f5e5d, 0x63626160, 0x66656463 90 | .word 0x69686767, 0x6c6b6a6a, 0x6f6e6d6d, 0x72717170 91 | .word 0x75757473, 0x78787776, 0x7c7b7a79, 0x7f7e7d7c 92 | -------------------------------------------------------------------------------- /src/ESPxRGB.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | ESPxRGB Library Header 4 | 5 | v0.1.0 6 | 7 | Copyright 2019 technosf [https://github.com/technosf] 8 | 9 | Licensed under the GNU LESSER GENERAL PUBLIC LICENSE, Version 3.0 or greater (the "License"); 10 | you may not use this file except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | https://www.gnu.org/licenses/lgpl-3.0.en.html 14 | Unless required by applicable law or agreed to in writing, 15 | software distributed under the License is distributed on an "AS IS" BASIS, 16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | See the License for the specific language governing permissions and limitations under the License. 18 | */ 19 | 20 | #ifndef ESPXRGB_H 21 | #define ESPXRGB_H 22 | 23 | #include "options.h" 24 | #include "Arduino.h" 25 | 26 | extern "C" 27 | { 28 | 29 | #ifdef __GAMMA__ 30 | 31 | /* 32 | Gamma correction on an 8-bit range 33 | */ 34 | uint8_t xgamma8(uint8_t val); 35 | 36 | /* 37 | Gamma correction over an array of 8-bit values 38 | */ 39 | void xgammaarray8(uint8_t *pntr, uint8_t count); 40 | 41 | /* 42 | Gamma correction on RGB/W in 8-bit range 43 | */ 44 | void xrgbgamma8(uint8_t *r, uint8_t *g, uint8_t *b); 45 | void xrgbwgamma8(uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *w); 46 | 47 | /* 48 | 49 | Gamma correction on RGBW in 8-bit range 50 | */ 51 | void xrgbmgamma8(uint8_t *r, uint8_t *g, uint8_t *b, float multiplier ); 52 | void xrgbwmgamma8(uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *w, float multiplier ); 53 | 54 | #endif // __GAMMA__ 55 | 56 | #ifdef __HSV__ 57 | 58 | #define HSV_HUE_MIN 0 59 | #define HSV_HUE_MAX 1535 60 | #define HSV_SAT_MIN 0 61 | #define HSV_SAT_MAX 255 62 | #define HSV_VAL_MIN 0 63 | #define HSV_VAL_MAX 255 64 | 65 | /* 66 | HSV to RGB for 8-bit range 67 | */ 68 | void xhsv2rgb8s(uint16_t h, uint8_t s, uint8_t v, uint8_t *r, uint8_t *g, uint8_t *b); 69 | void xhsv2rgb8e(uint16_t h, uint8_t s, uint8_t v, uint8_t *r, uint8_t *g, uint8_t *b); 70 | void xhsv2rgb8w(uint16_t h, uint8_t s, uint8_t v, uint8_t *r, uint8_t *g, uint8_t *b); 71 | void xhsv2rgb8t(uint16_t h, uint8_t s, uint8_t v, uint8_t *r, uint8_t *g, uint8_t *b); 72 | 73 | /* 74 | * Provide a default xhsv2rgb8 function 75 | */ 76 | void xhsv2rgb8(uint16_t h, uint8_t s, uint8_t v, uint8_t *r, uint8_t *g, uint8_t *b) 77 | { 78 | return xhsv2rgb8s(h, s, v, r, g, b); 79 | } 80 | 81 | #endif // __HSV__ 82 | 83 | #ifdef __RGBW__ 84 | 85 | /* 86 | RGB to RGBW conversion on 8-bit range 87 | */ 88 | void xrgb2rgbw8(uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *w); // Done 89 | 90 | #endif // __RGBW__ 91 | 92 | // ------------------- Convienence functions ------------------------------ 93 | 94 | #ifdef __GAMMA_RGBW__ 95 | 96 | /* 97 | RGB to RGBW with Gamma Correction for 8-bit range 98 | */ 99 | void xrgb2rgbwgamma8(uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *w) 100 | { 101 | xrgb2rgbw8(r, g, b, w); 102 | xrgbwgamma8(r, g, b, w); 103 | } 104 | 105 | #endif // __GAMMA__RGBW__ 106 | 107 | #ifdef __HSV_RGBW__ 108 | 109 | /* 110 | HSV to RGBW for 8-bit range 111 | */ 112 | void xhsv2rgbw8(uint16_t h, uint8_t s, uint8_t v, uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *w) 113 | { 114 | xhsv2rgb8(h, s, v, r, g, b); 115 | xrgb2rgbw8(r, g, b, w); 116 | } 117 | 118 | #endif // __HSV_RGBW__ 119 | 120 | #ifdef __GAMMA_HSV_RGBW__ 121 | 122 | /* 123 | HSV to RGB conversion with Gamma correction for 8-bit range 124 | */ 125 | void xhsv2rgbgamma8(uint16_t h, uint8_t s, uint8_t v, uint8_t *r, uint8_t *g, uint8_t *b) 126 | { 127 | xhsv2rgb8(h, s, v, r, g, b); 128 | xrgbgamma8(r, g, b); 129 | } 130 | 131 | /* 132 | HSV to RGBW with Gamma Correction for 8-bit range 133 | */ 134 | void xhsv2rgbwgamma8(uint16_t h, uint8_t s, uint8_t v, uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *w) 135 | { 136 | xhsv2rgb8(h, s, v, r, g, b); 137 | xrgb2rgbw8(r, g, b, w); 138 | xrgbwgamma8(r, g, b, w); 139 | } 140 | 141 | #endif // __GAMMA_HSV_RGBW__ 142 | } 143 | 144 | #endif // ESPXRGB_H 145 | -------------------------------------------------------------------------------- /examples/Benchmarking/fast_hsv2rgb.ino: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2016 B. Stultiens 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to 8 | deal in the Software without restriction, including without limitation the 9 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | sell copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | IN THE SOFTWARE. 23 | */ 24 | 25 | #include 26 | 27 | #define HSV_HUE_SEXTANT 256 28 | #define HSV_HUE_STEPS (6 * HSV_HUE_SEXTANT) 29 | 30 | #define HSV_HUE_MIN 0 31 | #define HSV_HUE_MAX (HSV_HUE_STEPS - 1) 32 | #define HSV_SAT_MIN 0 33 | #define HSV_SAT_MAX 255 34 | #define HSV_VAL_MIN 0 35 | #define HSV_VAL_MAX 255 36 | 37 | /* Options: */ 38 | #define HSV_USE_SEXTANT_TEST /* Limit the hue to 0...360 degrees */ 39 | #define HSV_USE_ASSEMBLY /* Optimize code using assembly */ 40 | 41 | 42 | /* 43 | Macros that are common to all implementations 44 | */ 45 | #define HSV_MONOCHROMATIC_TEST(s,v,r,g,b) \ 46 | do { \ 47 | if(!(s)) { \ 48 | *(r) = *(g) = *(b) = (v); \ 49 | return; \ 50 | } \ 51 | } while(0) 52 | 53 | #ifdef HSV_USE_SEXTANT_TEST 54 | #define HSV_SEXTANT_TEST(sextant) \ 55 | do { \ 56 | if((sextant) > 5) { \ 57 | (sextant) = 5; \ 58 | } \ 59 | } while(0) 60 | #else 61 | #define HSV_SEXTANT_TEST(sextant) do { ; } while(0) 62 | #endif 63 | 64 | /* 65 | Pointer swapping: 66 | sext. r g b r<>b g<>b r <> g result 67 | 0 0 0 v u c !u v c u v c 68 | 0 0 1 d v c d v c 69 | 0 1 0 c v u u v c u v c 70 | 0 1 1 c d v v d c d v c d v c 71 | 1 0 0 u c v u v c u v c 72 | 1 0 1 v c d v d c d v c d v c 73 | 74 | if(sextant & 2) 75 | r <-> b 76 | 77 | if(sextant & 4) 78 | g <-> b 79 | 80 | if(!(sextant & 6) { 81 | if(!(sextant & 1)) 82 | r <-> g 83 | } else { 84 | if(sextant & 1) 85 | r <-> g 86 | } 87 | */ 88 | #define HSV_SWAPPTR(a,b) do { uint8_t *tmp = (a); (a) = (b); (b) = tmp; } while(0) 89 | #define HSV_POINTER_SWAP(sextant,r,g,b) \ 90 | do { \ 91 | if((sextant) & 2) { \ 92 | HSV_SWAPPTR((r), (b)); \ 93 | } \ 94 | if((sextant) & 4) { \ 95 | HSV_SWAPPTR((g), (b)); \ 96 | } \ 97 | if(!((sextant) & 6)) { \ 98 | if(!((sextant) & 1)) { \ 99 | HSV_SWAPPTR((r), (g)); \ 100 | } \ 101 | } else { \ 102 | if((sextant) & 1) { \ 103 | HSV_SWAPPTR((r), (g)); \ 104 | } \ 105 | } \ 106 | } while(0) 107 | 108 | 109 | void fast_hsv2rgb(uint16_t h, uint8_t s, uint8_t v, uint8_t *r, uint8_t *g , uint8_t *b) 110 | { 111 | HSV_MONOCHROMATIC_TEST(s, v, r, g, b); // Exit with grayscale if s == 0 112 | 113 | uint8_t sextant = h >> 8; 114 | 115 | HSV_SEXTANT_TEST(sextant); // Optional: Limit hue sextants to defined space 116 | 117 | HSV_POINTER_SWAP(sextant, r, g, b); // Swap pointers depending which sextant we are in 118 | 119 | *g = v; // Top level 120 | 121 | // Perform actual calculations 122 | 123 | /* 124 | Bottom level: v * (1.0 - s) 125 | --> (v * (255 - s) + error_corr + 1) / 256 126 | */ 127 | uint16_t ww; // Intermediate result 128 | ww = v * (255 - s); // We don't use ~s to prevent size-promotion side effects 129 | ww += 1; // Error correction 130 | ww += ww >> 8; // Error correction 131 | *b = ww >> 8; 132 | 133 | uint8_t h_fraction = h & 0xff; // 0...255 134 | uint32_t d; // Intermediate result 135 | 136 | if (!(sextant & 1)) { 137 | // *r = ...slope_up...; 138 | d = v * (uint32_t)((255 << 8) - (uint16_t)(s * (256 - h_fraction))); 139 | d += d >> 8; // Error correction 140 | d += v; // Error correction 141 | *r = d >> 16; 142 | } else { 143 | // *r = ...slope_down...; 144 | d = v * (uint32_t)((255 << 8) - (uint16_t)(s * h_fraction)); 145 | d += d >> 8; // Error correction 146 | d += v; // Error correction 147 | *r = d >> 16; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESPxRGB 2 | 3 | **ESPxRGB** is an Arduino library of RGB, RGBW, HSV conversion functions and Gamma Correction written in **Xtensa** assembler for ESP SoCs that use Xtensa cores (_ESP32_). 4 | 5 | ## Table of Contents 6 | 7 | - [Why an assembler RGB manipulation library](#why-an-assembler-rgb-manipulation-library) 8 | - [Features](#features) 9 | - [Performance](#performance) 10 | - [Options and Use](#options-and-use) 11 | - [Versions](#versions) 12 | - [History and References](#history_and_references) 13 | - [License](#license) 14 | 15 | ## Why an assembler RGB manipulation library 16 | 17 | Looking to use small, low-powered SoCs to drive long strings of LEDs with the optimum speed and power efficiency places a premium on efficient algorythms and code, and Assembler is as efficient as you can get. The ESP SoCs are great given their feature set, conectivity, power and price. Plus, they are easy to program with a nice instruction set. 18 | 19 | The assembler itself is contained in GNU-format assembler **.S** files, and is not _inline assembler_. The **.S** files are compiled along with the **ESPxRGB.h** header when pulled into a project. 20 | 21 | ## Features 22 | 23 | ESPxRGB currently works in the 8-bit RGB/W space only. It covers functions supporting: 24 | * RGB to RGBW 25 | * HSV to RGB and RGBW, with a choice of four HSV algorythms 26 | * RGB gamma correction (for normalizing the perception of pulsing luminance) 27 | * RGB chroma correction (for normalizing the perception of luminance across the spectrum) 28 | 29 | plus intersections of some of the above. 30 | 31 | External **C** headers provide regular Arduino and ESP-IDF code access to the functions. 32 | 33 | ## Performance 34 | 35 | From the HSV Benchmark example 36 | ``` 37 | Benching HSV Conversions for 6291456 iterations 38 | 39 | Impl Time ms Calls per Second 40 | FastHSV 2232.91 894121 41 | KKs Spectrum 2123.05 940389 42 | ESPxRGB Spectrum 1972.31 1012261 43 | ESPxRGB Efficient 1987.78 1004383 44 | ESPxRGB Wave 2188.28 912356 45 | ESPxRGB Tweak 2040.31 978521 46 | ``` 47 | 48 | Taking KasperKampemans C impementation of HSV as the baseline: 49 | 50 | HSV Implementation | CpS | Additional CpS | Gain % | Gain @ 50 FPS and 60 LEDs per Meter 51 | -------------------|-----|----------------|--------|------------------------------------ 52 | FastHSV | 894,121 | -46,268 | -4.9% | -15 Meters 53 | KKs Spectrum | 940,389 | 0 |0% | 0 Meters 54 | ESPxRGB Spectrum| 1,012,261 | 71,872 | 7.6% | +23 Meters 55 | ESPxRGB Efficient| 1,004,383 | 63,994 | 6.8% | +21 Meters 56 | ESPxRGB Wave | 912,356 | -28,033 | -3% | -9 Meters 57 | ESPxRGB Tweak | 978,521 | 38,132 | 4.0% | +12 Meters 58 | 59 | ## Options and Use 60 | 61 | Drop the library into your *~Arduino/libraries* folder and include the *ESPxRGB.h* header in your project. 62 | The library is compiled based on flags in the *src/options.h* file: 63 | The functions are grouped and can be included/excluded in the compiled code as dictated by pre-processor definitions. 64 | 65 | The *examples* folder contains *sketches* than can test the functions and generate look-up tables used by the code. 66 | 67 | ### Functions 68 | 69 | #### Conversion 70 | 71 | * *xgamma8* - Normalizes the value of a single 8-bit color value 72 | * *xgammaarray8* - Normalizes a range of 8-bit color values 73 | * *xrgbgamma8* - Normalizes a set of 8-bit RGB values 74 | * *xrgbwgamma8* - Normalizes a set of 8-bit RGBW values 75 | 76 | #### Conversion 77 | 78 | * *xrgb2rgbw8* - RGB to RGBW 79 | * *xhsv2rgb8s* - HSV to RGB using the Spectrum algorythm 80 | * *xhsv2rgb8e* - HSV to RGB using a power efficient function 81 | * *xhsv2rgb8t* - HSV to RGB using a tweaked function aproximating FastLED Rainbow HSV 82 | * *xhsv2rgb8w* - HSV to RGB using a (sine) wave function 83 | * *xhsv2rgb8* - HSV to RGB default function that points to an above function of your choice. 84 | 85 | #### Combined 86 | 87 | * *xhsv2rgbgamma8* - HSV to RGB with gamma correction 88 | * *xhsv2rgbwgamma8* - HSV to RGB with gamma correction 89 | * *xrgb2rgbwgamma8* - RGB to RGBW with gamma correction 90 | 91 | 92 | ## Versions 93 | 94 | * _1.0.0_ Initial release 95 | * _0.1.0_ Initial commit and pre-release 96 | 97 | 98 | ## History and References 99 | 100 | I came to write this library after looking into options to do HSV-to-RGB conversion. Initially I looked at [FastLED](http://github.com/FastLED/FastLED) but determined it too broad a library for my use, and it dictated that FastLED be at the center of whatever you were doing which was not what I wanted. 101 | 102 | Next I came to use [fast_hsv2rgb from Vagrearg](http://www.vagrearg.org/content/hsvrgb): Lots of interesting math and theory on _spectrum_ HSV, with **C** implementations, and also **AVR**, which got me thinking of why not an **Xtensa** version. I tried copying the logic of the _AVR_ in _fast_hsv2rgb_, and then the _C_ logic, both of which use opaque iterative pointer-swapping, but I decided to use a _jump table_, and then to move to simplify the whole thing. 103 | 104 | And the simplest HSV to RGB code out there, I found, is [Kasper Kamperman's](http://www.kasperkamperman.com/blog/arduino/arduino-programming-hsb-to-rgb/), with a simple flow through and _case_ at the end. Looking at it, I found I could simplify it a little more by pulling common calculation up out of the _case_; I then implemented that in _Xtensa_. Kasper also addressed _dimming_ for luminance normalization. 105 | 106 | Reading up on HSV again on [Instructables](http://www.instructables.com/id/How-to-Make-Proper-Rainbow-and-Random-Colors-With-/), [Ontaelio](http://www.instructables.com/member/Ontaelio/) also spoke to power efficiency through using wave forms and in to how they effected percieved color. I added both power-efficient (saw-tooth) and smoother looking (sine) wave implementation leveraging the _spectrum_ HSV code. 107 | 108 | Yet the discussion of more natural looking HSV color-wheels led me to look back at [FastLED HSV to RGB](http://github.com/FastLED/FastLED/wiki/FastLED-HSV-Colors) and at their _Rainbow_ HSV. Not being able to quickly grok the code, [looking at the rainbow waveform image](http://raw.github.com/FastLED/FastLED/gh-pages/images/HSV-rainbow-with-desc.jpg) led me to implement an approximation of that as simply as I could. 109 | 110 | 111 | ## License 112 | 113 | ESPxRGB - Copyright 2019 technosf [http://github.com/technosf] 114 | 115 | Licensed under the GNU LESSER GENERAL PUBLIC LICENSE, Version 3.0 or greater (the "License"); 116 | you may not use this file except in compliance with the License. 117 | You may obtain a copy of the License at 118 | 119 |          120 | http://www.gnu.org/licenses/lgpl-3.0.en.html 121 |          122 | ![lglp3 logo](http://www.gnu.org/graphics/lgplv3-88x31.png) 123 | 124 | Unless required by applicable law or agreed to in writing, software 125 | distributed under the License is distributed on an "AS IS" BASIS, 126 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 127 | See the License for the specific language governing permissions and 128 | limitations under the License. 129 | -------------------------------------------------------------------------------- /examples/Benchmarking/Benchmarking.ino: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | */ 4 | 5 | #include "ESPxRGB.h" 6 | 7 | const unsigned long MAX_LONG = 4294967295; 8 | const int STEPS = 1536; 9 | const unsigned long ITERS = 6291456; 10 | const float I = ITERS * 1000000; 11 | 12 | uint8_t r, g, b; 13 | 14 | unsigned long startts; 15 | unsigned long endts; 16 | 17 | void setup() { 18 | 19 | Serial.begin(115200); 20 | 21 | Serial.println("Benching HSV Conversions for 6291456 iterations"); 22 | Serial.println("Impl\t\t\tTime ms\t\tCalls per Second"); 23 | 24 | 25 | unsigned long elapsed; 26 | 27 | elapsed = benchHSV(1); 28 | Serial.printf("%-15s\t\t%8.2f\t%8.2f\n", "FastHSV", elapsed / 1000.0, I / elapsed); 29 | 30 | elapsed = benchHSV(2); 31 | Serial.printf("%-15s\t\t%8.2f\t%8.2f\n", "KKs Spectrum", elapsed / 1000.0, I / elapsed); 32 | 33 | elapsed = benchHSV(3); 34 | Serial.printf("%-15s\t%8.2f\t%8.2f\n", "ESPxRGB Spectrum", elapsed / 1000.0, I / elapsed); 35 | 36 | elapsed = benchHSV(4); 37 | Serial.printf("%-15s\t%8.2f\t%8.2f\n", "ESPxRGB Efficient", elapsed / 1000.0, I / elapsed); 38 | 39 | elapsed = benchHSV(5); 40 | Serial.printf("%-15s\t\t%8.2f\t%8.2f\n", "ESPxRGB Wave", elapsed / 1000.0, I / elapsed); 41 | 42 | elapsed = benchHSV(6); 43 | Serial.printf("%-15s\t\t%8.2f\t%8.2f\n", "ESPxRGB Tweak", elapsed / 1000.0, I / elapsed); 44 | 45 | Serial.println("\nDone"); 46 | 47 | } 48 | 49 | void loop() { 50 | // Not used 51 | } 52 | 53 | unsigned long benchHSV(int clc) 54 | { 55 | unsigned long acc = 0; 56 | 57 | for ( uint16_t h = 0; h < STEPS; h++ ) 58 | { 59 | for ( int s = 0; s < 256; s++ ) 60 | { 61 | switch (clc) 62 | { 63 | case 1: 64 | acc += fasthsv(h, s); 65 | break; 66 | case 2: 67 | acc += kkhsv(h, s); 68 | break; 69 | case 3: 70 | acc += hsvs(h, s); 71 | break; 72 | case 4: 73 | acc += hsve(h, s); 74 | break; 75 | case 5: 76 | acc += hsvw(h, s); 77 | break; 78 | case 6: 79 | acc += hsvt(h, s); 80 | } 81 | } 82 | } 83 | return acc; 84 | } 85 | 86 | unsigned long hsvs( uint16_t h, uint8_t s) 87 | { 88 | startts = micros(); 89 | xhsv2rgb8s(h, s, 0, &r, &g, &b); 90 | xhsv2rgb8s(h, s, 31, &r, &g, &b); 91 | xhsv2rgb8s(h, s, 32, &r, &g, &b); 92 | xhsv2rgb8s(h, s, 63, &r, &g, &b); 93 | xhsv2rgb8s(h, s, 64, &r, &g, &b); 94 | xhsv2rgb8s(h, s, 95, &r, &g, &b); 95 | xhsv2rgb8s(h, s, 96, &r, &g, &b); 96 | xhsv2rgb8s(h, s, 127, &r, &g, &b); 97 | xhsv2rgb8s(h, s, 128, &r, &g, &b); 98 | xhsv2rgb8s(h, s, 159, &r, &g, &b); 99 | xhsv2rgb8s(h, s, 160, &r, &g, &b); 100 | xhsv2rgb8s(h, s, 191, &r, &g, &b); 101 | xhsv2rgb8s(h, s, 192, &r, &g, &b); 102 | xhsv2rgb8s(h, s, 223, &r, &g, &b); 103 | xhsv2rgb8s(h, s, 224, &r, &g, &b); 104 | xhsv2rgb8s(h, s, 255, &r, &g, &b); 105 | endts = micros(); 106 | if ( endts < startts ) return endts + ( MAX_LONG - startts ); 107 | return endts - startts; 108 | } 109 | 110 | unsigned long hsve( uint16_t h, uint8_t s) 111 | { 112 | startts = micros(); 113 | xhsv2rgb8e(h, s, 0, &r, &g, &b); 114 | xhsv2rgb8e(h, s, 31, &r, &g, &b); 115 | xhsv2rgb8e(h, s, 32, &r, &g, &b); 116 | xhsv2rgb8e(h, s, 63, &r, &g, &b); 117 | xhsv2rgb8e(h, s, 64, &r, &g, &b); 118 | xhsv2rgb8e(h, s, 95, &r, &g, &b); 119 | xhsv2rgb8e(h, s, 96, &r, &g, &b); 120 | xhsv2rgb8e(h, s, 127, &r, &g, &b); 121 | xhsv2rgb8e(h, s, 128, &r, &g, &b); 122 | xhsv2rgb8e(h, s, 159, &r, &g, &b); 123 | xhsv2rgb8e(h, s, 160, &r, &g, &b); 124 | xhsv2rgb8e(h, s, 191, &r, &g, &b); 125 | xhsv2rgb8e(h, s, 192, &r, &g, &b); 126 | xhsv2rgb8e(h, s, 223, &r, &g, &b); 127 | xhsv2rgb8e(h, s, 224, &r, &g, &b); 128 | xhsv2rgb8e(h, s, 255, &r, &g, &b); 129 | endts = micros(); 130 | if ( endts < startts ) return endts + ( MAX_LONG - startts ); 131 | return endts - startts; 132 | } 133 | 134 | unsigned long hsvw( uint16_t h, uint8_t s) 135 | { 136 | startts = micros(); 137 | xhsv2rgb8w(h, s, 0, &r, &g, &b); 138 | xhsv2rgb8w(h, s, 31, &r, &g, &b); 139 | xhsv2rgb8w(h, s, 32, &r, &g, &b); 140 | xhsv2rgb8w(h, s, 63, &r, &g, &b); 141 | xhsv2rgb8w(h, s, 64, &r, &g, &b); 142 | xhsv2rgb8w(h, s, 95, &r, &g, &b); 143 | xhsv2rgb8w(h, s, 96, &r, &g, &b); 144 | xhsv2rgb8w(h, s, 127, &r, &g, &b); 145 | xhsv2rgb8w(h, s, 128, &r, &g, &b); 146 | xhsv2rgb8w(h, s, 159, &r, &g, &b); 147 | xhsv2rgb8w(h, s, 160, &r, &g, &b); 148 | xhsv2rgb8w(h, s, 191, &r, &g, &b); 149 | xhsv2rgb8w(h, s, 192, &r, &g, &b); 150 | xhsv2rgb8w(h, s, 223, &r, &g, &b); 151 | xhsv2rgb8w(h, s, 224, &r, &g, &b); 152 | xhsv2rgb8w(h, s, 255, &r, &g, &b); 153 | endts = micros(); 154 | if ( endts < startts ) return endts + ( MAX_LONG - startts ); 155 | return endts - startts; 156 | } 157 | 158 | unsigned long hsvt( uint16_t h, unsigned int s) 159 | { 160 | startts = micros(); 161 | xhsv2rgb8t(h, s, 0, &r, &g, &b); 162 | xhsv2rgb8t(h, s, 31, &r, &g, &b); 163 | xhsv2rgb8t(h, s, 32, &r, &g, &b); 164 | xhsv2rgb8t(h, s, 63, &r, &g, &b); 165 | xhsv2rgb8t(h, s, 64, &r, &g, &b); 166 | xhsv2rgb8t(h, s, 95, &r, &g, &b); 167 | xhsv2rgb8t(h, s, 96, &r, &g, &b); 168 | xhsv2rgb8t(h, s, 127, &r, &g, &b); 169 | xhsv2rgb8t(h, s, 128, &r, &g, &b); 170 | xhsv2rgb8t(h, s, 159, &r, &g, &b); 171 | xhsv2rgb8t(h, s, 160, &r, &g, &b); 172 | xhsv2rgb8t(h, s, 191, &r, &g, &b); 173 | xhsv2rgb8t(h, s, 192, &r, &g, &b); 174 | xhsv2rgb8t(h, s, 223, &r, &g, &b); 175 | xhsv2rgb8t(h, s, 224, &r, &g, &b); 176 | xhsv2rgb8t(h, s, 255, &r, &g, &b); 177 | endts = micros(); 178 | if ( endts < startts ) return endts + ( MAX_LONG - startts ); 179 | return endts - startts; 180 | } 181 | 182 | unsigned long fasthsv( uint16_t h, uint8_t s) 183 | { 184 | startts = micros(); 185 | fast_hsv2rgb(h, s, 0, &r, &g, &b); 186 | fast_hsv2rgb(h, s, 31, &r, &g, &b); 187 | fast_hsv2rgb(h, s, 32, &r, &g, &b); 188 | fast_hsv2rgb(h, s, 63, &r, &g, &b); 189 | fast_hsv2rgb(h, s, 64, &r, &g, &b); 190 | fast_hsv2rgb(h, s, 95, &r, &g, &b); 191 | fast_hsv2rgb(h, s, 96, &r, &g, &b); 192 | fast_hsv2rgb(h, s, 127, &r, &g, &b); 193 | fast_hsv2rgb(h, s, 128, &r, &g, &b); 194 | fast_hsv2rgb(h, s, 159, &r, &g, &b); 195 | fast_hsv2rgb(h, s, 160, &r, &g, &b); 196 | fast_hsv2rgb(h, s, 191, &r, &g, &b); 197 | fast_hsv2rgb(h, s, 192, &r, &g, &b); 198 | fast_hsv2rgb(h, s, 223, &r, &g, &b); 199 | fast_hsv2rgb(h, s, 224, &r, &g, &b); 200 | fast_hsv2rgb(h, s, 255, &r, &g, &b); 201 | endts = micros(); 202 | if ( endts < startts ) return endts + ( MAX_LONG - startts ); 203 | return endts - startts; 204 | } 205 | 206 | unsigned long kkhsv( uint16_t h, uint8_t s) 207 | { 208 | startts = micros(); 209 | getKKRGB(h, s, 0, &r, &g, &b); 210 | getKKRGB(h, s, 31, &r, &g, &b); 211 | getKKRGB(h, s, 32, &r, &g, &b); 212 | getKKRGB(h, s, 63, &r, &g, &b); 213 | getKKRGB(h, s, 64, &r, &g, &b); 214 | getKKRGB(h, s, 95, &r, &g, &b); 215 | getKKRGB(h, s, 96, &r, &g, &b); 216 | getKKRGB(h, s, 127, &r, &g, &b); 217 | getKKRGB(h, s, 128, &r, &g, &b); 218 | getKKRGB(h, s, 159, &r, &g, &b); 219 | getKKRGB(h, s, 160, &r, &g, &b); 220 | getKKRGB(h, s, 191, &r, &g, &b); 221 | getKKRGB(h, s, 192, &r, &g, &b); 222 | getKKRGB(h, s, 223, &r, &g, &b); 223 | getKKRGB(h, s, 224, &r, &g, &b); 224 | getKKRGB(h, s, 255, &r, &g, &b); 225 | endts = micros(); 226 | if ( endts < startts ) return endts + ( MAX_LONG - startts ); 227 | return endts - startts; 228 | } 229 | -------------------------------------------------------------------------------- /src/xrgbgamma.S: -------------------------------------------------------------------------------- 1 | /* 2 | ESPxRGB Library Gamma Functions 3 | 4 | v0.1.0 5 | 6 | Copyright 2019 technosf [https://github.com/technosf] 7 | 8 | Licensed under the GNU LESSER GENERAL PUBLIC LICENSE, Version 3.0 or greater (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | 12 | https://www.gnu.org/licenses/lgpl-3.0.en.html 13 | Unless required by applicable law or agreed to in writing, 14 | software distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and limitations under the License. 17 | */ 18 | 19 | /* 20 | * ESPxRGB Library Gamma functions 21 | * 22 | * For driving RGB LEDs with RGB data. Reduces RGB values down to a range perceivable by 23 | */ 24 | 25 | #include "options.h" 26 | 27 | 28 | #ifndef __GAMMA__ 29 | #warning "Gamma functions are not included." 30 | #else 31 | 32 | // Inputsfor rgb 33 | #define p_R a2 34 | #define p_G a3 35 | #define p_B a4 36 | #define p_W a5 37 | #define mtpl a6 38 | 39 | 40 | // Inputsfor rgb 41 | #define pntr a2 42 | #define count a3 43 | 44 | // Working storage 45 | #define gammatbl a7 46 | #define val a8 47 | #define temp a9 48 | 49 | 50 | // -------------- Macros -------------- 51 | 52 | 53 | /* 54 | * Update the pointer with the gamma lookup from the table 55 | */ 56 | .macro m_gamma8 pntr:req, gamma:req 57 | l8ui temp, \pntr, 0 // load the raw color value 58 | add.n temp, temp, \gamma // Calculate the corrected gamma value adress 59 | l8ui temp, temp, 0 // Load the gamma value into temp from address in temp 60 | s8i temp, \pntr, 0 // Store the corrected color value 61 | .endm 62 | 63 | /* 64 | * Multiply the pointer value, then 65 | * Update the pointer with the gamma lookup from the table 66 | */ 67 | .macro m_m_gamma8 pntr:req, gamma:req 68 | l8ui temp, \pntr, 0 // load the raw color value 69 | movi val, 0x3f80 // MSBs Float 1.0 70 | quou val, mtpl, val // fp 1 = 0x100 71 | mul16u temp, temp, val // temp = temp * val 72 | srli temp, temp, 8 // rs temp 8 73 | add.n temp, temp, \gamma // Calculate the corrected gamma value adress 74 | l8ui temp, temp, 0 // Load the gamma value into temp from address in temp 75 | s8i temp, \pntr, 0 // Store the corrected color value 76 | .endm 77 | 78 | 79 | .text 80 | 81 | // --------------- Externals --------------- 82 | 83 | .global xgamma8 84 | .global xgammaarray8 85 | .global xrgbgamma8 86 | .global xrgbwgamma8 87 | .global xrgbmgamma8 88 | .global xrgbwmgamma8 89 | 90 | // --------------- Code --------------- 91 | 92 | /* 93 | * Gamma Correction 94 | * 95 | * xgamma8 96 | * 97 | */ 98 | .type xgamma8,@function 99 | .align 4 100 | 101 | xgamma8: 102 | 103 | .frame a1, 32 104 | entry a1, 32 105 | 106 | 107 | _gamma8: 108 | 109 | movi a3, _gamma8_table // Gamma table goes in a3 110 | add.n a2, a2, a3 // Calculate the gamma address 111 | l8ui a2, a2, 0 // Load into a2 the value at a2 112 | retw.n // Return from the windowed call 113 | 114 | 115 | /* 116 | * RGB Gamma Correction 117 | * 118 | * xrgbmgamma8 119 | * 120 | */ 121 | .type xrgbmgamma8,@function 122 | .align 4 123 | 124 | xrgbmgamma8: 125 | 126 | .frame a1, 32 127 | entry a1, 32 128 | 129 | _rgbmgamma8: 130 | 131 | movi gammatbl, _gamma8_table // Lookup table address 132 | mov mtpl, a5 // Move mltiplier from a5 to a6 133 | 134 | _calc_rgb_mgamma8: 135 | 136 | m_m_gamma8 p_R, gammatbl // Red 137 | m_m_gamma8 p_G, gammatbl // Green 138 | m_m_gamma8 p_B, gammatbl // Blue 139 | retw.n 140 | 141 | 142 | /* 143 | * RGB Gamma Correction 144 | * 145 | * xrgbwmgamma8 146 | * 147 | */ 148 | .type xrgbwmgamma8,@function 149 | .align 4 150 | 151 | xrgbwmgamma8: 152 | 153 | .frame a1, 32 154 | entry a1, 32 155 | 156 | _rgbwmgamma8: 157 | 158 | movi gammatbl, _gamma8_table 159 | 160 | _calc_w_mgamma8: 161 | 162 | m_m_gamma8 p_W, gammatbl // White 163 | j _calc_rgb_mgamma8 // Calculate the RGB 164 | 165 | 166 | /* 167 | * RGB Gamma Correction 168 | * 169 | * xrgbgamma8 170 | * 171 | */ 172 | .type xrgbgamma8,@function 173 | .align 4 174 | 175 | xrgbgamma8: 176 | 177 | .frame a1, 32 178 | entry a1, 32 179 | 180 | 181 | _rgbgamma8: 182 | 183 | movi gammatbl, _gamma8_table // Lookup table address 184 | 185 | 186 | _calc_rgb_gamma8: 187 | 188 | m_gamma8 p_R, gammatbl // Red 189 | m_gamma8 p_G, gammatbl // Green 190 | m_gamma8 p_B, gammatbl // Blue 191 | retw.n 192 | 193 | 194 | /* 195 | * RGBW Gamma Correction (White, then RGB) 196 | * 197 | * xrgbwgamma8 198 | * 199 | */ 200 | .type xrgbwgamma8,@function 201 | .align 4 202 | 203 | xrgbwgamma8: 204 | 205 | .frame a1, 32 206 | entry a1, 32 207 | 208 | 209 | _rgbwgamma8: 210 | 211 | movi gammatbl, _gamma8_table 212 | 213 | _calc_w_gamma8: 214 | 215 | m_gamma8 p_W, gammatbl // White 216 | j _calc_rgb_gamma8 // Calculate the RGB 217 | 218 | 219 | 220 | /* 221 | * RGB Gamma Correction 222 | * 223 | * xgammaarray8 224 | * 225 | * Gamma correct values in an 8bit value array 226 | */ 227 | .type xgammaarray8,@function 228 | .align 4 229 | 230 | xgammaarray8: 231 | 232 | .frame a1, 32 233 | entry a1, 32 234 | 235 | _gammaarray8: 236 | 237 | beqz count, _loopexit 238 | movi gammatbl, _gamma8_table 239 | 240 | _calc_array_gamma8: 241 | 242 | loop count, _loopexit 243 | m_gamma8 pntr, gammatbl // Update pntr addr with gamma tble lookup 244 | addi.n pntr, pntr, 0x01 // Increment pntr 245 | 246 | _loopexit: 247 | 248 | retw.n 249 | 250 | /* 251 | * RGB Luma Correction 252 | * 253 | * xrgbluma8 254 | * 255 | * Gamma correct values in an 8bit value array 256 | */ 257 | .type xrgbluma8,@function 258 | .align 4 259 | 260 | xrgbluma8: 261 | 262 | .frame a1, 32 263 | entry a1, 32 264 | 265 | _rgbluma8: 266 | 267 | l8ui val, p_R, 0 // Halve Red intensity 268 | srli val, val, 1 269 | s8i val, p_R, 0 270 | 271 | l8ui val, p_G, 0 // Quarter Green intensity 272 | srli val, val, 2 273 | s8i val, p_G, 0 274 | 275 | l8ui val, p_B, 0 // Boost Blue intensity by an eigth 276 | movi temp, 0xEE 277 | bge val, temp, _lumaexit 278 | srli temp, val, 5 279 | add.n val, val, temp 280 | s8i val, p_B, 0 281 | 282 | _lumaexit: 283 | 284 | retw.n 285 | 286 | #endif 287 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /src/xhsv2rgb.S: -------------------------------------------------------------------------------- 1 | /* 2 | ESPxRGB Library HSV Functions 3 | 4 | v0.1.0 5 | 6 | Copyright 2019 technosf [https://github.com/technosf] 7 | 8 | Licensed under the GNU LESSER GENERAL PUBLIC LICENSE, Version 3.0 or greater (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | 12 | https://www.gnu.org/licenses/lgpl-3.0.en.html 13 | Unless required by applicable law or agreed to in writing, 14 | software distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and limitations under the License. 17 | */ 18 | 19 | /* 20 | * Param: Reg in: Size: 21 | * h a2 2 bytes 22 | * s a3 1 byte 23 | * v a4 1 byte 24 | * r* a5 1 bytes 25 | * g* a6 1 bytes 26 | * b* a7 1 bytes 27 | * 28 | * ASM is optimized for fall-through minimal branching 29 | */ 30 | 31 | #include "options.h" 32 | 33 | #ifndef __HSV__ 34 | #warning "HSV functions are not included." 35 | #else 36 | 37 | // Inputs 38 | #define hue a2 39 | #define sat a3 40 | #define val a4 41 | #define p_R a5 42 | #define p_G a6 43 | #define p_B a7 44 | 45 | // Working storage 46 | #define sector a8 47 | #define step a9 48 | #define base a3 49 | #define slope a10 50 | 51 | #define chroma a11 52 | #define jumper a12 53 | #define temp a13 54 | 55 | #define S_SECTORS 6 // Spectrum Sectors 56 | #define S_STEPS 256 // SpectrumSector Steps 57 | 58 | #define T_SECTORS 8 // Tweaked Sectors 59 | #define T_STEPS 192 // Tweaked Sector Steps 60 | 61 | .text 62 | 63 | // --------------------- Externals --------------------- 64 | 65 | .global xhsv2rgb8s 66 | .global xhsv2rgb8e 67 | .global xhsv2rgb8w 68 | .global xhsv2rgb8t 69 | 70 | // ====================== Spectrum ====================== 71 | 72 | 73 | /* 74 | * Spectrum 75 | * HSV -> RGB function 76 | * 77 | * xhsv2rgb8 78 | * Sectors 8, Step 192 79 | */ 80 | .type xhsv2rgb8s,@function 81 | .align 4 82 | xhsv2rgb8s: 83 | .frame a1, 32 84 | entry a1, 32 85 | 86 | movi jumper, _hsv2rgb8s 87 | j _range_check_and_chroma 88 | 89 | 90 | _hsv2rgb8s: 91 | 92 | bbci sector, 0x0, _s_slope_calc // Do not invert step/slope if even sector 93 | 94 | xor step, step, temp // step = 256 - step 95 | 96 | 97 | _s_slope_calc: 98 | 99 | mul16u slope, chroma, step // slope = chroma * step 100 | srli slope, slope, 8 // slope = (chroma * step) / 256 101 | add.n slope, slope, base // slope = ((chroma * step)/256) + base 102 | 103 | movi jumper, _vector6 // vector addr 104 | j _sectorvector // jump to sector-specific code 105 | 106 | 107 | 108 | // ====================== Efficient ====================== 109 | 110 | 111 | /* 112 | * Efficient 113 | * HSV -> RGB function 114 | * 115 | * xhsv2rgb8e 116 | * Sectors 8, Step 192 117 | */ 118 | .type xhsv2rgb8e,@function 119 | .align 4 120 | 121 | xhsv2rgb8e: 122 | 123 | .frame a1, 32 124 | entry a1, 32 125 | 126 | movi jumper, _hsv2rgb8e 127 | j _range_check_and_chroma 128 | 129 | 130 | _hsv2rgb8e: 131 | 132 | bbci sector, 0x0, _e_slope_calc // Do not invert step/slope if even sector 133 | xor step, step, temp // step = 256 - step 134 | 135 | 136 | _e_slope_calc: 137 | 138 | mul16u slope, chroma, step // slope = chroma * step 139 | srli slope, slope, 9 // slope = (chroma * step) / 512 140 | sub val, val, slope // val = val - slope 141 | add.n slope, slope, base // slope = slope + base 142 | 143 | movi jumper, _vector6 // vector addr 144 | j _sectorvector // jump to sector-specific code 145 | 146 | // ====================== Wave ====================== 147 | 148 | 149 | /* 150 | * Wave (Sine) 151 | * HSV -> RGB function 152 | * 153 | * xhsv2rgb8w 154 | * Sectors 8, Step 192 155 | */ 156 | .type xhsv2rgb8w,@function 157 | .align 4 158 | 159 | xhsv2rgb8w: 160 | 161 | .frame a1, 32 162 | entry a1, 32 163 | 164 | movi jumper, _hsv2rgb8w 165 | j _range_check_and_chroma 166 | 167 | _hsv2rgb8w: 168 | 169 | bbci sector, 0x0, _w_slope_calc // Do not invert step/slope if even sector 170 | xor step, step, temp // step = 256 - step 171 | 172 | 173 | _w_slope_calc: 174 | 175 | movi jumper, _wave_quarter // sine table goes in jumper 176 | add.n jumper, jumper, step // Calculate the sine address 177 | l8ui slope, jumper, 0 // Load into a2 the value at a2 178 | mul16u slope, slope, chroma // chroma * step 179 | srli slope, slope, 8 // slope = ((chroma * step) + base ) / 256 180 | 181 | sub val, val, slope // val = val - slope 182 | add.n slope, slope, base // slope = slope + base 183 | 184 | movi jumper, _vector6 // vector addr 185 | j _sectorvector // jump to sector-specific code 186 | 187 | 188 | // ====================== Tweaked ====================== 189 | 190 | 191 | /* 192 | * Tweaked (Pyschologically) 193 | * HSV -> RGB function 194 | * 195 | * xhsv2rgb8t 196 | * 197 | * Based on Wave function with 198 | */ 199 | .type xhsv2rgb8t,@function 200 | .align 4 201 | 202 | xhsv2rgb8t: 203 | 204 | .frame a1, 32 205 | entry a1, 32 206 | 207 | beqz val, _monochromatic_exit // if v = 0, jump set values to off, exit 208 | beqz sat, _monochromatic_exit // if s = 0, jump set values to monochromatic, exit 209 | 210 | /// Base 211 | movi temp, 0xFF // Load sector step lsb mask 212 | xor base, sat, temp // base = 255 - sat 213 | mul16u base, base, val // base = ((255 - sat) * val) 214 | srli base, base, 8 // base = ((255 - sat) * val) >> 8; 215 | /// chroma 216 | sub chroma, val, base // chroma = val - base 217 | mov slope, chroma 218 | movi temp, 0x03 // chroma / 3 219 | quou chroma, chroma, temp // chroma / 3 220 | /// Steps 221 | movi temp, T_STEPS // 192 222 | remu step, hue, temp // Step = mod hue, 192 223 | quou sector, hue, temp // Sector = hue / 192 224 | /// Slope 225 | mul16u slope, chroma, step // slope = chroma * step 226 | quou slope, slope, temp // slope = chroma * step /192 227 | 228 | 229 | movi jumper, _vector8 // vector addr 230 | movi temp, T_SECTORS // 8 sectors max 231 | bgeu sector, temp, _tweak_max // Branch if sector is out of range 232 | j _sectorvector // jump to sector-specific code 233 | 234 | /* 235 | * Set max sectors, steps for tweak model 236 | */ 237 | _tweak_max: 238 | 239 | movi sector, T_SECTORS - 1 240 | movi step, T_STEPS - 1 241 | j _sectorvector 242 | 243 | /* 244 | * Tweaked 8-sector, 192 step code 245 | */ 246 | _T0: 247 | sub val, val, slope // val = val - slope 248 | add slope, slope, base // slope = base + slope 249 | s8i val, p_R, 0 250 | s8i slope, p_G, 0 251 | s8i base, p_B, 0 252 | retw.n 253 | 254 | _T1: 255 | sub val, val, chroma // val = val - chroma 256 | add slope, slope, chroma // slope = chroma + slope 257 | add slope, slope, base // slope = base + slope 258 | s8i val, p_R, 0 259 | s8i slope, p_G, 0 260 | s8i base, p_B, 0 261 | retw.n 262 | 263 | _T2: 264 | addx2 temp, slope, chroma // s* slope + chroma 265 | sub val, val, temp // val - chroma - 2*slope 266 | add slope, slope, base // slope = 2 * inv slope + base 267 | addx2 slope, chroma, slope 268 | s8i val, p_R, 0 269 | s8i slope, p_G, 0 270 | s8i base, p_B, 0 271 | retw.n 272 | 273 | _T3: 274 | sub val, val, slope // val = val -slope 275 | add slope, slope, base // slope = slope + base 276 | s8i base, p_R, 0 277 | s8i val, p_G, 0 278 | s8i slope, p_B, 0 279 | retw.n 280 | 281 | _T4: 282 | addx2 slope, slope, chroma 283 | sub val, val, slope 284 | add slope, slope, base 285 | s8i base, p_R, 0 286 | s8i val, p_G, 0 287 | s8i slope, p_B, 0 288 | retw.n 289 | 290 | _T5: 291 | sub val, val, slope // val = val - slope 292 | add slope, slope, base // slope = base + slope 293 | s8i slope, p_R, 0 294 | s8i base, p_G, 0 295 | s8i val, p_B, 0 296 | retw.n 297 | 298 | _T6: 299 | add temp, chroma, slope // val = val - slope 300 | sub val, val, temp // val = val - chroma 301 | add slope, temp, base // slope = base + slope 302 | s8i slope, p_R, 0 303 | s8i base, p_G, 0 304 | s8i val, p_B, 0 305 | retw.n 306 | 307 | _T7: 308 | sub slope, chroma, slope // Invert slope 309 | sub val, val, slope // val = val - inv slope 310 | add slope, slope, base // slope = base + inv slope 311 | s8i val, p_R, 0 312 | s8i base, p_G, 0 313 | s8i slope, p_B, 0 314 | retw.n 315 | 316 | // ============ Common ============ 317 | 318 | _range_check_and_chroma: 319 | 320 | beqz val, _monochromatic_exit // if v = 0, jump set values to off, exit 321 | beqz sat, _monochromatic_exit // if s = 0, jump set values to monochromatic, exit 322 | 323 | /// chroma 324 | movi temp, 0xFF // Load sector step lsb mask 325 | xor base, sat, temp // base = 255 - sat 326 | mul16u base, base, val // base = ((255 - sat) * val) 327 | srli base, base, 8 // base = ((255 - sat) * val) >> 8; 328 | sub chroma, val, base // chroma = val - base; 329 | 330 | /// Steps 331 | and step, hue, temp // Get sector step 332 | srli sector, hue, 8 // Shift hue right 8 places (MSB) to get sector 333 | bgeui sector, S_SECTORS, _sector_max // If >= max sectors, max sector and steps 334 | jx jumper 335 | 336 | /* 337 | * Set HSV to spectrum max range - HSV_HUE_STEP 338 | * Set sector to max 339 | */ 340 | _sector_max: 341 | 342 | movi sector, S_SECTORS - 1 343 | movi step, S_STEPS - 1 344 | jx jumper 345 | 346 | // ------------------------------ 347 | 348 | _sectorvector: 349 | 350 | movi temp, 0x4 // One word 351 | mul16u temp, temp, sector // sector X words 352 | add.n jumper, jumper, temp // Vector plus words 353 | l32i.n jumper, jumper, 0 // load jumper address from vector 354 | jx jumper // jump 355 | 356 | /* 357 | * Sector-specific code 358 | * 359 | * Regular six-sector RGB-CMY 360 | */ 361 | _S0: 362 | s8i val, p_R, 0 363 | s8i slope, p_G, 0 364 | s8i base, p_B, 0 365 | retw.n 366 | _S1: 367 | s8i slope, p_R, 0 368 | s8i val, p_G, 0 369 | s8i base, p_B, 0 370 | retw.n 371 | _S2: 372 | s8i base, p_R, 0 373 | s8i val, p_G, 0 374 | s8i slope, p_B, 0 375 | retw.n 376 | _S3: 377 | s8i base, p_R, 0 378 | s8i slope, p_G, 0 379 | s8i val, p_B, 0 380 | retw.n 381 | _S4: 382 | s8i slope, p_R, 0 383 | s8i base, p_G, 0 384 | s8i val, p_B, 0 385 | retw.n 386 | _S5: 387 | s8i val, p_R, 0 388 | s8i base, p_G, 0 389 | s8i slope, p_B, 0 390 | retw.n 391 | 392 | 393 | /* 394 | * Set RGB to "v" 395 | */ 396 | _monochromatic_exit: 397 | 398 | s8i val, p_R, 0 399 | s8i val, p_G, 0 400 | s8i val, p_B, 0 401 | retw.n 402 | 403 | // ============ Data ============ 404 | 405 | .data 406 | 407 | _vector6: 408 | .word _S0 409 | .word _S1 410 | .word _S2 411 | .word _S3 412 | .word _S4 413 | .word _S5 414 | 415 | _vector8: 416 | .word _T0 417 | .word _T1 418 | .word _T2 419 | .word _T3 420 | .word _T4 421 | .word _T5 422 | .word _T6 423 | .word _T7 424 | 425 | #endif // __HSV__ 426 | --------------------------------------------------------------------------------