diff --git a/internal/c/libqb/include/graphics.h b/internal/c/libqb/include/graphics.h index 099d9785f..b9431d3e7 100644 --- a/internal/c/libqb/include/graphics.h +++ b/internal/c/libqb/include/graphics.h @@ -55,6 +55,20 @@ struct img_struct { #define IMG_SCREEN 2 // img is linked to other screen pages #define IMG_FREEMEM 4 // if set, it means memory must be freed +// used by HSB/RGB color conversion routines +struct hsb_color +{ + double h; // [0,360] hue + double s; // [0,1] saturation + double b; // [0,1] brightness +}; +struct rgb_color +{ + double r; // [0,1] red + double g; // [0,1] green + double b; // [0,1] blue +}; + /********** Render State **********/ /* Apart from 'glTexParameter' based settings (with are texture specific) @@ -176,6 +190,12 @@ struct hardware_graphics_command_struct { #define HARDWARE_GRAPHICS_COMMAND__MAPTRIANGLE3D 5 #define HARDWARE_GRAPHICS_COMMAND__CLEAR_DEPTHBUFFER 6 +uint32_t func__hsb32(double hue, double sat, double bri); +uint32_t func__hsba32(double hue, double sat, double bri, double alf); +double func__hue32(uint32_t argb); +double func__sat32(uint32_t argb); +double func__bri32(uint32_t argb); + void sub__depthbuffer(int32_t options, int32_t dst, int32_t passed); void sub__maptriangle(int32_t cull_options, float sx1, float sy1, float sx2, float sy2, float sx3, float sy3, int32_t si, float dx1, float dy1, float dz1, float dx2, float dy2, float dz2, float dx3, float dy3, float dz3, int32_t di, int32_t smooth_options, int32_t passed); diff --git a/internal/c/libqb/src/graphics.cpp b/internal/c/libqb/src/graphics.cpp index 44702b1ff..1ce381116 100644 --- a/internal/c/libqb/src/graphics.cpp +++ b/internal/c/libqb/src/graphics.cpp @@ -35,6 +35,118 @@ extern uint8_t *ablend128; static int32_t depthbuffer_mode0 = DEPTHBUFFER_MODE__ON; static int32_t depthbuffer_mode1 = DEPTHBUFFER_MODE__ON; +void hsb2rgb(hsb_color *hsb, rgb_color *rgb) { + double hu, hi, hf, pv, qv, tv; + + if (hsb->s == 0.0) { + rgb->r = hsb->b; rgb->g = hsb->b; rgb->b = hsb->b; // no saturation = grayscale + } else { + hu = hsb->h / 60.0; // to sixtant [0,5] + if (hu >= 6.0) hu = hu - 6.0; + hf = modf(hu, &hi); // int/frac parts of hue + pv = hsb->b * (1.0 - hsb->s); + qv = hsb->b * (1.0 - (hsb->s * hf)); + tv = hsb->b * (1.0 - (hsb->s * (1.0 - hf))); + switch (lround(hi)) { + case 0: {rgb->r = hsb->b; rgb->g = tv; rgb->b = pv; break;} // 0- 60 = Red->Yellow + case 1: {rgb->r = qv; rgb->g = hsb->b; rgb->b = pv; break;} // 60-120 = Yellow->Green + case 2: {rgb->r = pv; rgb->g = hsb->b; rgb->b = tv; break;} // 120-180 = Green->Cyan + case 3: {rgb->r = pv; rgb->g = qv; rgb->b = hsb->b; break;} // 180-240 = Cyan->Blue + case 4: {rgb->r = tv; rgb->g = pv; rgb->b = hsb->b; break;} // 240-300 = Blue->Magenta + case 5: {rgb->r = hsb->b; rgb->g = pv; rgb->b = qv; break;} // 300-360 = Magenta->Red + } + } +} + +void rgb2hsb(rgb_color *rgb, hsb_color *hsb) { + double mini, maxi, diff, hu; + // --- find min/max and difference --- + mini = fmin(fmin(rgb->r, rgb->g), rgb->b); + maxi = fmax(fmax(rgb->r, rgb->g), rgb->b); + diff = maxi - mini; + // --- brightness --- + hsb->b = maxi; + // --- saturation (avoid division by zero) --- + maxi != 0.0 ? hsb->s = diff / maxi : hsb->s = 0.0; + // --- hue in degrees --- + if (hsb->s != 0.0) { + if (rgb->r == maxi) { + hu = ((rgb->g - rgb->b) / diff); // between Yellow & Magenta + if (hu < 0.0) hu = hu + 6.0; + } else if (rgb->g == maxi) { + hu = 2.0 + ((rgb->b - rgb->r) / diff); // between Cyan & Yellow + } else { + hu = 4.0 + ((rgb->r - rgb->g) / diff); // between Magenta & Cyan + } + hsb->h = hu * 60.0; // to degrees + } else { + hsb->h = 0.0; // technically there's no hue w/o saturation, commonly used is 0 (red) + } +} + +uint32_t func__hsb32(double hue, double sat, double bri) { + hsb_color hsb; rgb_color rgb; + // --- prepare values for conversion --- + (hue < 0.0) ? hsb.h = 0.0 : ((hue > 360.0) ? hsb.h = 360.0 : hsb.h = hue); + (sat < 0.0) ? hsb.s = 0.0 : ((sat > 100.0) ? hsb.s = 100.0 : hsb.s = sat); + (bri < 0.0) ? hsb.b = 0.0 : ((bri > 100.0) ? hsb.b = 100.0 : hsb.b = bri); + hsb.s /= 100.0; hsb.b /= 100.0; // range [0,1] + // --- convert colorspace --- + hsb2rgb(&hsb, &rgb); + // --- build result --- + return ((lround(rgb.r * 255.0) << 16) + (lround(rgb.g * 255.0) << 8) + lround(rgb.b * 255.0)) | 0xFF000000; +} + +uint32_t func__hsba32(double hue, double sat, double bri, double alf) { + hsb_color hsb; rgb_color rgb; double alpha; + // --- prepare values for conversion --- + (hue < 0.0) ? hsb.h = 0.0 : ((hue > 360.0) ? hsb.h = 360.0 : hsb.h = hue); + (sat < 0.0) ? hsb.s = 0.0 : ((sat > 100.0) ? hsb.s = 100.0 : hsb.s = sat); + (bri < 0.0) ? hsb.b = 0.0 : ((bri > 100.0) ? hsb.b = 100.0 : hsb.b = bri); + (alf < 0.0) ? alpha = 0.0 : ((alf > 100.0) ? alpha = 100.0 : alpha = alf); + hsb.s /= 100.0; hsb.b /= 100.0; alpha /= 100.0; // range [0,1] + // --- convert colorspace --- + hsb2rgb(&hsb, &rgb); + // --- build result --- + return (lround(alpha * 255.0) << 24) + (lround(rgb.r * 255.0) << 16) + (lround(rgb.g * 255.0) << 8) + lround(rgb.b * 255.0); +} + +double func__hue32(uint32_t argb) { + rgb_color rgb; hsb_color hsb; + // --- prepare values for conversion --- + rgb.r = ((argb >> 16) & 0xFF) / 255.0; + rgb.g = ((argb >> 8) & 0xFF) / 255.0; + rgb.b = (argb & 0xFF) / 255.0; + // --- convert colorspace --- + rgb2hsb(&rgb, &hsb); + // --- build result --- + return hsb.h; +} + +double func__sat32(uint32_t argb) { + rgb_color rgb; hsb_color hsb; + // --- prepare values for conversion --- + rgb.r = ((argb >> 16) & 0xFF) / 255.0; + rgb.g = ((argb >> 8) & 0xFF) / 255.0; + rgb.b = (argb & 0xFF) / 255.0; + // --- convert colorspace --- + rgb2hsb(&rgb, &hsb); + // --- build result --- + return hsb.s * 100.0; +} + +double func__bri32(uint32_t argb) { + rgb_color rgb; hsb_color hsb; + // --- prepare values for conversion --- + rgb.r = ((argb >> 16) & 0xFF) / 255.0; + rgb.g = ((argb >> 8) & 0xFF) / 255.0; + rgb.b = (argb & 0xFF) / 255.0; + // --- convert colorspace --- + rgb2hsb(&rgb, &hsb); + // --- build result --- + return hsb.b * 100.0; +} + void sub__depthbuffer(int32_t options, int32_t dst, int32_t passed) { // {ON|OFF|LOCK|_CLEAR} diff --git a/internal/support/include/afterlastline.bm b/internal/support/include/afterlastline.bm index 894dbfd15..a1cf53ee1 100644 --- a/internal/support/include/afterlastline.bm +++ b/internal/support/include/afterlastline.bm @@ -75,84 +75,6 @@ FUNCTION _IKW_Clamp## (_value##, _minVal##, _maxVal##) _CLAMP = _IIF(_value## < _realMin##, _realMin##, _IIF(_value## > _realMax##, _realMax##, _value##)) END FUNCTION -'_HSB32() returns the 32-bit ARGB color value build from the given HSB values, -' always with full alpha (opaque colors). -'---------- [0,360] [0,100] [0,100] -FUNCTION _IKW_HSB32~& (_hue#, _saturation#, _brightness#) - '--- option _explicit requirements --- - DIM _hu#, _sa#, _br#, _re#, _gr#, _bl# - '--- prepare values for conversion --- - _hu# = _CLAMP(_hue#, 0, 360) - _sa# = _CLAMP(_saturation#, 0, 100) / 100 - _br# = _CLAMP(_brightness#, 0, 100) / 100 - '--- convert colorspace --- - _HSBtoRGB _hu#, _sa#, _br#, _re#, _gr#, _bl# - '--- build result --- - _HSB32 = _RGB32(CINT(_re# * 255), CINT(_gr# * 255), CINT(_bl# * 255)) -END FUNCTION - -'_HSBA32() returns the 32-bit ARGB color value build from the given HSB values, -' using 0-100% alpha as given. -'---------- [0,360] [0,100] [0,100] [0,100] -FUNCTION _IKW_HSBA32~& (_hue#, _saturation#, _brightness#, _alfa#) - '--- option _explicit requirements --- - DIM _hu#, _sa#, _br#, _al#, _re#, _gr#, _bl# - '--- prepare values for conversion --- - _hu# = _CLAMP(_hue#, 0, 360) - _sa# = _CLAMP(_saturation#, 0, 100) / 100 - _br# = _CLAMP(_brightness#, 0, 100) / 100 - _al# = _CLAMP(_alfa#, 0, 100) / 100 - '--- convert colorspace --- - _HSBtoRGB _hu#, _sa#, _br#, _re#, _gr#, _bl# - '--- build result --- - _HSBA32 = _RGBA32(CINT(_re# * 255), CINT(_gr# * 255), CINT(_bl# * 255), CINT(_al# * 255)) -END FUNCTION - -'_HUE32() returns the hue [0,360] of the given 32-bit ARGB color value. -'---------- -FUNCTION _IKW_Hue32# (_argbColor~&) - '--- option _explicit requirements --- - DIM _re#, _gr#, _bl#, _hu#, _sa#, _br# - '--- prepare values for conversion --- - _re# = _RED32(_argbColor~&) / 255 - _gr# = _GREEN32(_argbColor~&) / 255 - _bl# = _BLUE32(_argbColor~&) / 255 - '--- convert colorspace --- - _RGBtoHSB _re#, _gr#, _bl#, _hu#, _sa#, _br# - '--- build result --- - _HUE32 = _hu# -END FUNCTION - -'_SATURATION32() returns the saturation [0,100] of the given 32-bit ARGB color value. -'---------- -FUNCTION _IKW_Saturation32# (_argbColor~&) - '--- option _explicit requirements --- - DIM _re#, _gr#, _bl#, _hu#, _sa#, _br# - '--- prepare values for conversion --- - _re# = _RED32(_argbColor~&) / 255 - _gr# = _GREEN32(_argbColor~&) / 255 - _bl# = _BLUE32(_argbColor~&) / 255 - '--- convert colorspace --- - _RGBtoHSB _re#, _gr#, _bl#, _hu#, _sa#, _br# - '--- build result --- - _SATURATION32 = _sa# * 100 -END FUNCTION - -'_BRIGHTNESS32() returns the brightness [0,100] of the given 32-bit ARGB color value. -'---------- -FUNCTION _IKW_Brightness32# (_argbColor~&) - '--- option _explicit requirements --- - DIM _re#, _gr#, _bl#, _hu#, _sa#, _br# - '--- prepare values for conversion --- - _re# = _RED32(_argbColor~&) / 255 - _gr# = _GREEN32(_argbColor~&) / 255 - _bl# = _BLUE32(_argbColor~&) / 255 - '--- convert colorspace --- - _RGBtoHSB _re#, _gr#, _bl#, _hu#, _sa#, _br# - '--- build result --- - _BRIGHTNESS32 = _br# * 100 -END FUNCTION - '_EncodeURL$() returns the percent encoded version of the given plain text ' URL for use with the _OPENCLIENT("HTTP:url") syntax. '---------- @@ -222,73 +144,6 @@ END FUNCTION ' rule #2 its names must begin with an underscore. '========================================================================== -'_HSBtoRGB() converts colors from the HSB into the RGB colorspace. Note that -' HSB (B=Brightness) is also known as HSV (V=Value) and sometimes called -' HSI (I=Intensity), but that's just different names for the same thing. -' However, this SUB is not suitable for the HSL (L=Lightness) colorspace. -'---------- -' IN: hu# = [0,360], sa# = [0,1], br# = [0,1] -'OUT: re#, gr#, bl# values in range [0,1] (through arguments) -'---------- -SUB _HSBtoRGB (_hu#, _sa#, _br#, _re#, _gr#, _bl#) - '--- option _explicit requirements --- - DIM _hi%, _hf#, _pv#, _qv#, _tv# - '--- compute RGB --- - IF _sa# = 0 THEN - _re# = _br#: _gr# = _br#: _bl# = _br# 'no saturation = grayscale - ELSE - _hu# = _hu# / 60 ' 'to sixtant [0,5] - IF _hu# >= 6 THEN _hu# = _hu# - 6 - _hi% = INT(_hu#): _hf# = _hu# - _hi% 'int/frac parts of hue - _pv# = _br# * (1 - _sa#) - _qv# = _br# * (1 - (_sa# * _hf#)) - _tv# = _br# * (1 - (_sa# * (1 - _hf#))) - SELECT CASE _hi% - CASE 0: _re# = _br#: _gr# = _tv#: _bl# = _pv# ' 0- 60 = Red->Yellow - CASE 1: _re# = _qv#: _gr# = _br#: _bl# = _pv# ' 60-120 = Yellow->Green - CASE 2: _re# = _pv#: _gr# = _br#: _bl# = _tv# '120-180 = Green->Cyan - CASE 3: _re# = _pv#: _gr# = _qv#: _bl# = _br# '180-240 = Cyan->Blue - CASE 4: _re# = _tv#: _gr# = _pv#: _bl# = _br# '240-300 = Blue->Magenta - CASE 5: _re# = _br#: _gr# = _pv#: _bl# = _qv# '300-360 = Magenta->Red - END SELECT - END IF -END SUB - -'_RGBtoHSB() converts colors from the RGB into the HSB colorspace. Note that -' HSB (B=Brightness) is also known as HSV (V=Value) and sometimes called -' HSI (I=Intensity), but that's just different names for the same thing. -' However, this SUB is not suitable for the HSL (L=Lightness) colorspace. -'---------- -' IN: re#, gr#, bl# values in range [0,1] -'OUT: hu# = [0,360], sa# = [0,1], br# = [0,1] (through arguments) -'---------- -SUB _RGBtoHSB (_re#, _gr#, _bl#, _hu#, _sa#, _br#) - '--- option _explicit requirements --- - DIM _mini#, _maxi#, _diff# - '--- find min/max and difference --- - _mini# = _MIN(_MIN(_re#, _gr#), _bl#) - _maxi# = _MAX(_MAX(_re#, _gr#), _bl#) - _diff# = _maxi# - _mini# - '--- brightness --- - _br# = _maxi# - '--- saturation (avoid division by zero) --- - IF _maxi# <> 0 THEN _sa# = _diff# / _maxi# ELSE _sa# = 0 - '--- hue in degrees --- - IF _sa# <> 0 THEN - IF _re# = _maxi# THEN - _hu# = ((_gr# - _bl#) / _diff#) ' 'between Yellow & Magenta - IF _hu# < 0 THEN _hu# = _hu# + 6 - ELSEIF _gr# = _maxi# THEN - _hu# = 2 + ((_bl# - _re#) / _diff#) 'between Cyan & Yellow - ELSE - _hu# = 4 + ((_re# - _gr#) / _diff#) 'between Magenta & Cyan - END IF - _hu# = _hu# * 60 'to degrees - ELSE - _hu# = 0 'technically there's no hue w/o saturation, commonly used is 0 (red) - END IF -END SUB - $IF _SOCKETS_ THEN '_WhatIsMyIP$() is a helper function required by _CONNECTIONADDRESS$, ' but only required when network stuff is compiled in (_SOCKETS_). diff --git a/source/subs_functions/subs_functions.bas b/source/subs_functions/subs_functions.bas index 5813f2c3a..3efd5ae00 100644 --- a/source/subs_functions/subs_functions.bas +++ b/source/subs_functions/subs_functions.bas @@ -1683,6 +1683,56 @@ SUB reginternal id.hr_syntax = "_ALPHA32(rgbaColor&)" regid + clearid + id.n = "_HSB32" + id.subfunc = 1 + id.callname = "func__hsb32" + id.args = 3 + id.arg = MKL$(DOUBLETYPE - ISPOINTER) + MKL$(DOUBLETYPE - ISPOINTER) + MKL$(DOUBLETYPE - ISPOINTER) + id.ret = ULONGTYPE - ISPOINTER + id.hr_syntax = "_HSB32(hue#, saturation#, brightness#)" + regid + + clearid + id.n = "_HSBA32" + id.subfunc = 1 + id.callname = "func__hsba32" + id.args = 4 + id.arg = MKL$(DOUBLETYPE - ISPOINTER) + MKL$(DOUBLETYPE - ISPOINTER) + MKL$(DOUBLETYPE - ISPOINTER) + MKL$(DOUBLETYPE - ISPOINTER) + id.ret = ULONGTYPE - ISPOINTER + id.hr_syntax = "_HSBA32(hue#, saturation#, brightness#, alpha#)" + regid + + clearid + id.n = "_Hue32" + id.subfunc = 1 + id.callname = "func__hue32" + id.args = 1 + id.arg = MKL$(ULONGTYPE - ISPOINTER) + id.ret = DOUBLETYPE - ISPOINTER + id.hr_syntax = "_HUE32(rgbaColor&)" + regid + + clearid + id.n = "_Saturation32" + id.subfunc = 1 + id.callname = "func__sat32" + id.args = 1 + id.arg = MKL$(ULONGTYPE - ISPOINTER) + id.ret = DOUBLETYPE - ISPOINTER + id.hr_syntax = "_SATURATION32(rgbaColor&)" + regid + + clearid + id.n = "_Brightness32" + id.subfunc = 1 + id.callname = "func__bri32" + id.args = 1 + id.arg = MKL$(ULONGTYPE - ISPOINTER) + id.ret = DOUBLETYPE - ISPOINTER + id.hr_syntax = "_BRIGHTNESS32(rgbaColor&)" + regid + clearid id.n = "Draw" id.subfunc = 2