Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor HSB color functions #606

Merged
merged 2 commits into from
Feb 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions internal/c/libqb/include/graphics.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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);
112 changes: 112 additions & 0 deletions internal/c/libqb/src/graphics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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}

Expand Down
145 changes: 0 additions & 145 deletions internal/support/include/afterlastline.bm
Original file line number Diff line number Diff line change
Expand Up @@ -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.
'----------
Expand Down Expand Up @@ -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_).
Expand Down
50 changes: 50 additions & 0 deletions source/subs_functions/subs_functions.bas
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down