From 3ea538cbde4141c5dbd6b539d59c515baf416a77 Mon Sep 17 00:00:00 2001 From: Kapu1178 <75460809+Kapu1178@users.noreply.github.com> Date: Mon, 4 Mar 2024 02:22:08 -0500 Subject: [PATCH] Impliments rgb2num() in all color procs (#819) * ports https://github.com/tgstation/tgstation/pull/81182 * oops * update * fix --- code/__DEFINES/gradient.dm | 4 + code/__DEFINES/lighting.dm | 9 - code/__DEFINES/matrices.dm | 26 + code/__HELPERS/colors.dm | 7 +- code/__HELPERS/icons.dm | 444 +++--------------- code/__HELPERS/matrices.dm | 42 +- code/__HELPERS/type2type.dm | 17 +- code/game/machinery/rotating_light.dm | 5 +- .../effects/decals/cleanable/humans.dm | 4 +- code/game/objects/items/plushes.dm | 4 +- code/game/objects/items/stacks/wrap.dm | 8 +- code/game/objects/structures/mirror.dm | 4 +- .../carbon/human/species_types/ethereal.dm | 19 +- .../simple_animal/hostile/space_dragon.dm | 5 +- code/modules/power/supermatter/nupermatter.dm | 9 +- daedalus.dme | 1 + 16 files changed, 163 insertions(+), 445 deletions(-) create mode 100644 code/__DEFINES/gradient.dm diff --git a/code/__DEFINES/gradient.dm b/code/__DEFINES/gradient.dm new file mode 100644 index 000000000000..573a339880b9 --- /dev/null +++ b/code/__DEFINES/gradient.dm @@ -0,0 +1,4 @@ +// spacemandmm doesn't really implement gradient() right, so let's just handle that here yeah? +#define rgb_gradient(index, args...) UNLINT(gradient(args, index)) +#define hsl_gradient(index, args...) UNLINT(gradient(args, space = COLORSPACE_HSL, index)) +#define hsv_gradient(index, args...) UNLINT(gradient(args, space = COLORSPACE_HSV, index)) diff --git a/code/__DEFINES/lighting.dm b/code/__DEFINES/lighting.dm index 836dc7323b18..530dbc8dbe6e 100644 --- a/code/__DEFINES/lighting.dm +++ b/code/__DEFINES/lighting.dm @@ -72,15 +72,6 @@ GLOBAL_LIST_INIT(em_block_color, EM_BLOCK_COLOR) /// A globaly cached version of [EM_MASK_MATRIX] for quick access. GLOBAL_LIST_INIT(em_mask_matrix, EM_MASK_MATRIX) -/// Returns the red part of a #RRGGBB hex sequence as number -#define GETREDPART(hexa) hex2num(copytext(hexa, 2, 4)) - -/// Returns the green part of a #RRGGBB hex sequence as number -#define GETGREENPART(hexa) hex2num(copytext(hexa, 4, 6)) - -/// Returns the blue part of a #RRGGBB hex sequence as number -#define GETBLUEPART(hexa) hex2num(copytext(hexa, 6, 8)) - /// Parse the hexadecimal color into lumcounts of each perspective. #define PARSE_LIGHT_COLOR(source) \ do { \ diff --git a/code/__DEFINES/matrices.dm b/code/__DEFINES/matrices.dm index ae16554b7064..26ff5a7232a2 100644 --- a/code/__DEFINES/matrices.dm +++ b/code/__DEFINES/matrices.dm @@ -1 +1,27 @@ +/// Helper macro for creating a matrix at the given offsets. +/// Works at compile time. #define TRANSLATE_MATRIX(offset_x, offset_y) matrix(1, 0, (offset_x), 0, 1, (offset_y)) +/// The color matrix of an image which colors haven't been altered. Does nothing. +#define COLOR_MATRIX_IDENTITY list(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1, 0,0,0,0) +/// Color inversion +#define COLOR_MATRIX_INVERT list(-1,0,0,0, 0,-1,0,0, 0,0,-1,0, 0,0,0,1, 1,1,1,0) +///Sepiatone +#define COLOR_MATRIX_SEPIATONE list(0.393,0.349,0.272,0, 0.769,0.686,0.534,0, 0.189,0.168,0.131,0, 0,0,0,1, 0,0,0,0) +///Grayscale +#define COLOR_MATRIX_GRAYSCALE list(0.33,0.33,0.33,0, 0.59,0.59,0.59,0, 0.11,0.11,0.11,0, 0,0,0,1, 0,0,0,0) +///Polaroid colors +#define COLOR_MATRIX_POLAROID list(1.438,-0.062,-0.062,0, -0.122,1.378,-0.122,0, -0.016,-0.016,1.483,0, 0,0,0,1, 0,0,0,0) +/// Converts reds to blue, green to red and blue to green. +#define COLOR_MATRIX_BRG list(0,0,1,0, 0,1,0,0, 1,0,0,0, 0,0,0,1, 0,0,0,0) +/// Black & White +#define COLOR_MATRIX_BLACK_WHITE list(1.5,1.5,1.5,0, 1.5,1.5,1.5,0, 1.5,1.5,1.5,0, 0,0,0,1, -1,-1,-1,0) +/** + * Adds/subtracts overall lightness + * 0 is identity, 1 makes everything white, -1 makes everything black + */ +#define COLOR_MATRIX_LIGHTNESS(power) list(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1, power,power,power,0) +/** + * Changes distance colors have from rgb(127,127,127) grey + * 1 is identity. 0 makes everything grey >1 blows out colors and greys + */ +#define COLOR_MATRIX_CONTRAST(val) list(val,0,0,0, 0,val,0,0, 0,0,val,0, 0,0,0,1, (1-val)*0.5,(1-val)*0.5,(1-val)*0.5,0) diff --git a/code/__HELPERS/colors.dm b/code/__HELPERS/colors.dm index 44c7058c9c31..d2ece3ab1a8d 100644 --- a/code/__HELPERS/colors.dm +++ b/code/__HELPERS/colors.dm @@ -37,10 +37,9 @@ CRASH("Given non-HTML argument!") else if(length_char(HTMLstring) != 7) CRASH("Given non-hex symbols in argument!") - var/textr = copytext(HTMLstring, 2, 4) - var/textg = copytext(HTMLstring, 4, 6) - var/textb = copytext(HTMLstring, 6, 8) - return rgb(255 - hex2num(textr), 255 - hex2num(textg), 255 - hex2num(textb)) + + var/list/color = rgb2num(HTMLstring) + return rgb(255 - color[1], 255 - color[2], 255 - color[3]) ///Flash a color on the client /proc/flash_color(mob_or_client, flash_color="#960000", flash_time=20) diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm index 5d8c1dc031ed..3339fba0f864 100644 --- a/code/__HELPERS/icons.dm +++ b/code/__HELPERS/icons.dm @@ -60,55 +60,41 @@ RGB isn't the only way to represent color. Sometimes it's more useful to work wi * The value of a color determines how bright it is. A high-value color is vivid, moderate value is dark, and no value at all is black. -Just as BYOND uses "#rrggbb" to represent RGB values, a similar format is used for HSV: "#hhhssvv". The hue is three -hex digits because it ranges from 0 to 0x5FF. +While rgb is typically stored in the #rrggbb" format (with optional "aa" on the end), HSV never needs to be displayed. +Most procs that work in HSV "space" will simply accept RGB inputs and convert them in place using rgb2num(color, space = COLORSPACE_HSV). - * 0 to 0xFF - red to yellow - * 0x100 to 0x1FF - yellow to green - * 0x200 to 0x2FF - green to cyan - * 0x300 to 0x3FF - cyan to blue - * 0x400 to 0x4FF - blue to magenta - * 0x500 to 0x5FF - magenta to red +That said, if you want to manually modify these values rgb2hsv() will hand you back a list in the format list(hue, saturation, value, alpha). +Converting back is simple, just a hsv2rgb(hsv) call -Knowing this, you can figure out that red is "#000ffff" in HSV format, which is hue 0 (red), saturation 255 (as colorful as possible), -value 255 (as bright as possible). Green is "#200ffff" and blue is "#400ffff". +Hue ranges from 0 to 360 (it's in degrees of a color wheel) +Saturation ranges from 0 to 100 +Value ranges from 0 to 100 -More than one HSV color can match the same RGB color. +Knowing this, you can figure out that red is list(0, 100, 100) in HSV format, which is hue 0 (red), saturation 100 (as colorful as possible), +value 255 (as bright as possible). Green is list(120, 100, 100) and blue is list(240, 100, 100). + +It is worth noting that while we do not have helpers for them currently, these same ideas apply to all of byond's color spaces +HSV (hue saturation value), HSL (hue satriation luminosity) and HCY (hue chroma luminosity) Here are some procs you can use for color management: -ReadRGB(rgb) - Takes an RGB string like "#ffaa55" and converts it to a list such as list(255,170,85). If an RGBA format is used - that includes alpha, the list will have a fourth item for the alpha value. -hsv(hue, sat, val, apha) - Counterpart to rgb(), this takes the values you input and converts them to a string in "#hhhssvv" or "#hhhssvvaa" - format. Alpha is not included in the result if null. -ReadHSV(rgb) - Takes an HSV string like "#100FF80" and converts it to a list such as list(256,255,128). If an HSVA format is used that - includes alpha, the list will have a fourth item for the alpha value. -RGBtoHSV(rgb) - Takes an RGB or RGBA string like "#ffaa55" and converts it into an HSV or HSVA color such as "#080aaff". -HSVtoRGB(hsv) - Takes an HSV or HSVA string like "#080aaff" and converts it into an RGB or RGBA color such as "#ff55aa". BlendRGB(rgb1, rgb2, amount) Blends between two RGB or RGBA colors using regular RGB blending. If amount is 0, the first color is the result; if 1, the second color is the result. 0.5 produces an average of the two. Values outside the 0 to 1 range are allowed as well. - The returned value is an RGB or RGBA color. -BlendHSV(hsv1, hsv2, amount) - Blends between two HSV or HSVA colors using HSV blending, which tends to produce nicer results than regular RGB + Returns an RGB or RGBA string +BlendHSV(rgb1, rgb2, amount) + Blends between two RGB or RGBA colors using HSV blending, which tends to produce nicer results than regular RGB blending because the brightness of the color is left intact. If amount is 0, the first color is the result; if 1, the second color is the result. 0.5 produces an average of the two. Values outside the 0 to 1 range are allowed as well. - The returned value is an HSV or HSVA color. -BlendRGBasHSV(rgb1, rgb2, amount) - Like BlendHSV(), but the colors used and the return value are RGB or RGBA colors. The blending is done in HSV form. + Returns an RGB or RGBA string HueToAngle(hue) Converts a hue to an angle range of 0 to 360. Angle 0 is red, 120 is green, and 240 is blue. AngleToHue(hue) Converts an angle to a hue in the valid range. -RotateHue(hsv, angle) - Takes an HSV or HSVA value and rotates the hue forward through red, green, and blue by an angle from 0 to 360. - (Rotating red by 60° produces yellow.) The result is another HSV or HSVA color with the same saturation and value - as the original, but a different hue. +RotateHue(rgb, angle) + Takes an RGB or RGBA value and rotates the hue forward through red, green, and blue by an angle from 0 to 360. + (Rotating red by 60° produces yellow.) + Returns an RGB or RGBA string GrayScale(rgb) Takes an RGB or RGBA color and converts it to grayscale. Returns an RGB or RGBA string. ColorTone(rgb, tone) @@ -215,8 +201,7 @@ world #define TO_HEX_DIGIT(n) ascii2text((n&15) + ((n&15)<10 ? 48 : 87)) - - // Multiply all alpha values by this float +// Multiply all alpha values by this float /icon/proc/ChangeOpacity(opacity = 1) MapColors(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,opacity, 0,0,0,0) @@ -227,7 +212,7 @@ world /icon/proc/ColorTone(tone) GrayScale() - var/list/TONE = ReadRGB(tone) + var/list/TONE = rgb2num(tone) var/gray = round(TONE[1]*0.3 + TONE[2]*0.59 + TONE[3]*0.11, 1) var/icon/upper = (255-gray) ? new(src) : null @@ -243,26 +228,26 @@ world // Take the minimum color of two icons; combine transparency as if blending with ICON_ADD /icon/proc/MinColors(icon) - var/icon/I = new(src) - I.Opaque() - I.Blend(icon, ICON_SUBTRACT) - Blend(I, ICON_SUBTRACT) + var/icon/new_icon = new(src) + new_icon.Opaque() + new_icon.Blend(icon, ICON_SUBTRACT) + Blend(new_icon, ICON_SUBTRACT) // Take the maximum color of two icons; combine opacity as if blending with ICON_OR /icon/proc/MaxColors(icon) - var/icon/I + var/icon/new_icon if(isicon(icon)) - I = new(icon) + new_icon = new(icon) else // solid color - I = new(src) - I.Blend("#000000", ICON_OVERLAY) - I.SwapColor("#000000", null) - I.Blend(icon, ICON_OVERLAY) - var/icon/J = new(src) - J.Opaque() - I.Blend(J, ICON_SUBTRACT) - Blend(I, ICON_OR) + new_icon = new(src) + new_icon.Blend("#000000", ICON_OVERLAY) + new_icon.SwapColor("#000000", null) + new_icon.Blend(icon, ICON_OVERLAY) + var/icon/blend_icon = new(src) + blend_icon.Opaque() + new_icon.Blend(blend_icon, ICON_SUBTRACT) + Blend(new_icon, ICON_OR) // make this icon fully opaque--transparent pixels become black /icon/proc/Opaque(background = "#000000") @@ -280,250 +265,30 @@ world AddAlphaMask(mask) /icon/proc/AddAlphaMask(mask) - var/icon/M = new(mask) - M.Blend("#ffffff", ICON_SUBTRACT) + var/icon/mask_icon = new(mask) + mask_icon.Blend("#ffffff", ICON_SUBTRACT) // apply mask - Blend(M, ICON_ADD) - -/* - HSV format is represented as "#hhhssvv" or "#hhhssvvaa" - - Hue ranges from 0 to 0x5ff (1535) - - 0x000 = red - 0x100 = yellow - 0x200 = green - 0x300 = cyan - 0x400 = blue - 0x500 = magenta - - Saturation is from 0 to 0xff (255) - - More saturation = more color - Less saturation = more gray - - Value ranges from 0 to 0xff (255) - - Higher value means brighter color - */ - -/proc/ReadRGB(rgb) - if(!rgb) - return - - // interpret the HSV or HSVA value - var/i=1,start=1 - if(text2ascii(rgb) == 35) ++start // skip opening # - var/ch,which=0,r=0,g=0,b=0,alpha=0,usealpha - var/digits=0 - for(i=start, i<=length(rgb), ++i) - ch = text2ascii(rgb, i) - if(ch < 48 || (ch > 57 && ch < 65) || (ch > 70 && ch < 97) || ch > 102) - break - ++digits - if(digits == 8) - break - - var/single = digits < 6 - if(digits != 3 && digits != 4 && digits != 6 && digits != 8) - return - if(digits == 4 || digits == 8) - usealpha = 1 - for(i=start, digits>0, ++i) - ch = text2ascii(rgb, i) - if(ch >= 48 && ch <= 57) - ch -= 48 - else if(ch >= 65 && ch <= 70) - ch -= 55 - else if(ch >= 97 && ch <= 102) - ch -= 87 - else - break - --digits - switch(which) - if(0) - r = (r << 4) | ch - if(single) - r |= r << 4 - ++which - else if(!(digits & 1)) - ++which - if(1) - g = (g << 4) | ch - if(single) - g |= g << 4 - ++which - else if(!(digits & 1)) - ++which - if(2) - b = (b << 4) | ch - if(single) - b |= b << 4 - ++which - else if(!(digits & 1)) - ++which - if(3) - alpha = (alpha << 4) | ch - if(single) - alpha |= alpha << 4 - - . = list(r, g, b) - if(usealpha) - . += alpha - -/proc/ReadHSV(hsv) - if(!hsv) - return - - // interpret the HSV or HSVA value - var/i=1,start=1 - if(text2ascii(hsv) == 35) - ++start // skip opening # - var/ch,which=0,hue=0,sat=0,val=0,alpha=0,usealpha - var/digits=0 - for(i=start, i<=length(hsv), ++i) - ch = text2ascii(hsv, i) - if(ch < 48 || (ch > 57 && ch < 65) || (ch > 70 && ch < 97) || ch > 102) - break - ++digits - if(digits == 9) - break - if(digits > 7) - usealpha = 1 - if(digits <= 4) - ++which - if(digits <= 2) - ++which - for(i=start, digits>0, ++i) - ch = text2ascii(hsv, i) - if(ch >= 48 && ch <= 57) - ch -= 48 - else if(ch >= 65 && ch <= 70) - ch -= 55 - else if(ch >= 97 && ch <= 102) - ch -= 87 - else - break - --digits - switch(which) - if(0) - hue = (hue << 4) | ch - if(digits == (usealpha ? 6 : 4)) - ++which - if(1) - sat = (sat << 4) | ch - if(digits == (usealpha ? 4 : 2)) - ++which - if(2) - val = (val << 4) | ch - if(digits == (usealpha ? 2 : 0)) - ++which - if(3) - alpha = (alpha << 4) | ch - - . = list(hue, sat, val) - if(usealpha) - . += alpha - -/proc/HSVtoRGB(hsv) - if(!hsv) - return "#000000" - var/list/HSV = ReadHSV(hsv) - if(!HSV) + Blend(mask_icon, ICON_ADD) + +/// Converts an rgb color into a list storing hsva +/// Exists because it's useful to have a guarenteed alpha value +/proc/rgb2hsv(rgb) + RETURN_TYPE(/list) + var/list/hsv = rgb2num(rgb, COLORSPACE_HSV) + if(length(hsv) < 4) + hsv += 255 // Max alpha, just to make life easy + return hsv + +/// Converts a list storing hsva into an rgb color string +/proc/hsv2rgb(hsv) + if(length(hsv) < 3) return "#000000" - - var/hue = HSV[1] - var/sat = HSV[2] - var/val = HSV[3] - - // Compress hue into easier-to-manage range - hue -= hue >> 8 - if(hue >= 0x5fa) - hue -= 0x5fa - - var/hi,mid,lo,r,g,b - hi = val - lo = round((255 - sat) * val / 255, 1) - mid = lo + round(abs(round(hue, 510) - hue) * (hi - lo) / 255, 1) - if(hue >= 765) - if(hue >= 1275) {r=hi; g=lo; b=mid} - else if(hue >= 1020) {r=mid; g=lo; b=hi } - else {r=lo; g=mid; b=hi } - else - if(hue >= 510) {r=lo; g=hi; b=mid} - else if(hue >= 255) {r=mid; g=hi; b=lo } - else {r=hi; g=mid; b=lo } - - return (HSV.len > 3) ? rgb(r,g,b,HSV[4]) : rgb(r,g,b) - -/proc/RGBtoHSV(rgb) - if(!rgb) - return "#0000000" - var/list/RGB = ReadRGB(rgb) - if(!RGB) - return "#0000000" - - var/r = RGB[1] - var/g = RGB[2] - var/b = RGB[3] - var/hi = max(r,g,b) - var/lo = min(r,g,b) - - var/val = hi - var/sat = hi ? round((hi-lo) * 255 / hi, 1) : 0 - var/hue = 0 - - if(sat) - var/dir - var/mid - if(hi == r) - if(lo == b) {hue=0; dir=1; mid=g} - else {hue=1535; dir=-1; mid=b} - else if(hi == g) - if(lo == r) {hue=512; dir=1; mid=b} - else {hue=511; dir=-1; mid=r} - else if(hi == b) - if(lo == g) {hue=1024; dir=1; mid=r} - else {hue=1023; dir=-1; mid=g} - hue += dir * round((mid-lo) * 255 / (hi-lo), 1) - - return hsv(hue, sat, val, (RGB.len>3 ? RGB[4] : null)) - -/proc/hsv(hue, sat, val, alpha) - if(hue < 0 || hue >= 1536) - hue %= 1536 - if(hue < 0) - hue += 1536 - if((hue & 0xFF) == 0xFF) - ++hue - if(hue >= 1536) - hue = 0 - if(sat < 0) - sat = 0 - if(sat > 255) - sat = 255 - if(val < 0) - val = 0 - if(val > 255) - val = 255 - . = "#" - . += TO_HEX_DIGIT(hue >> 8) - . += TO_HEX_DIGIT(hue >> 4) - . += TO_HEX_DIGIT(hue) - . += TO_HEX_DIGIT(sat >> 4) - . += TO_HEX_DIGIT(sat) - . += TO_HEX_DIGIT(val >> 4) - . += TO_HEX_DIGIT(val) - if(!isnull(alpha)) - if(alpha < 0) - alpha = 0 - if(alpha > 255) - alpha = 255 - . += TO_HEX_DIGIT(alpha >> 4) - . += TO_HEX_DIGIT(alpha) + if(length(hsv) == 3) + return rgb(hsv[1], hsv[2], hsv[3], space = COLORSPACE_HSV) + return rgb(hsv[1], hsv[2], hsv[3], hsv[4], space = COLORSPACE_HSV) /* - Smooth blend between HSV colors + Smooth blend between RGB colors interpreted as HSV amount=0 is the first color amount=1 is the second color @@ -532,63 +297,7 @@ world amount<0 or amount>1 are allowed */ /proc/BlendHSV(hsv1, hsv2, amount) - var/list/HSV1 = ReadHSV(hsv1) - var/list/HSV2 = ReadHSV(hsv2) - - // add missing alpha if needed - if(HSV1.len < HSV2.len) - HSV1 += 255 - else if(HSV2.len < HSV1.len) - HSV2 += 255 - var/usealpha = HSV1.len > 3 - - // normalize hsv values in case anything is screwy - if(HSV1[1] > 1536) - HSV1[1] %= 1536 - if(HSV2[1] > 1536) - HSV2[1] %= 1536 - if(HSV1[1] < 0) - HSV1[1] += 1536 - if(HSV2[1] < 0) - HSV2[1] += 1536 - if(!HSV1[3]) {HSV1[1] = 0; HSV1[2] = 0} - if(!HSV2[3]) {HSV2[1] = 0; HSV2[2] = 0} - - // no value for one color means don't change saturation - if(!HSV1[3]) - HSV1[2] = HSV2[2] - if(!HSV2[3]) - HSV2[2] = HSV1[2] - // no saturation for one color means don't change hues - if(!HSV1[2]) - HSV1[1] = HSV2[1] - if(!HSV2[2]) - HSV2[1] = HSV1[1] - - // Compress hues into easier-to-manage range - HSV1[1] -= HSV1[1] >> 8 - HSV2[1] -= HSV2[1] >> 8 - - var/hue_diff = HSV2[1] - HSV1[1] - if(hue_diff > 765) - hue_diff -= 1530 - else if(hue_diff <= -765) - hue_diff += 1530 - - var/hue = round(HSV1[1] + hue_diff * amount, 1) - var/sat = round(HSV1[2] + (HSV2[2] - HSV1[2]) * amount, 1) - var/val = round(HSV1[3] + (HSV2[3] - HSV1[3]) * amount, 1) - var/alpha = usealpha ? round(HSV1[4] + (HSV2[4] - HSV1[4]) * amount, 1) : null - - // normalize hue - if(hue < 0 || hue >= 1530) - hue %= 1530 - if(hue < 0) - hue += 1530 - // decompress hue - hue += round(hue / 255) - - return hsv(hue, sat, val, alpha) + return hsv_gradient(amount, 0, hsv1, 1, hsv2, "loop") /* Smooth blend between RGB colors @@ -600,25 +309,7 @@ world amount<0 or amount>1 are allowed */ /proc/BlendRGB(rgb1, rgb2, amount) - var/list/RGB1 = ReadRGB(rgb1) - var/list/RGB2 = ReadRGB(rgb2) - - // add missing alpha if needed - if(RGB1.len < RGB2.len) - RGB1 += 255 - else if(RGB2.len < RGB1.len) - RGB2 += 255 - var/usealpha = RGB1.len > 3 - - var/r = round(RGB1[1] + (RGB2[1] - RGB1[1]) * amount, 1) - var/g = round(RGB1[2] + (RGB2[2] - RGB1[2]) * amount, 1) - var/b = round(RGB1[3] + (RGB2[3] - RGB1[3]) * amount, 1) - var/alpha = usealpha ? round(RGB1[4] + (RGB2[4] - RGB1[4]) * amount, 1) : null - - return isnull(alpha) ? rgb(r, g, b) : rgb(r, g, b, alpha) - -/proc/BlendRGBasHSV(rgb1, rgb2, amount) - return HSVtoRGB(RGBtoHSV(rgb1), RGBtoHSV(rgb2), amount) + return rgb_gradient(amount, 0, rgb1, 1, rgb2, "loop") /proc/HueToAngle(hue) // normalize hsv in case anything is screwy @@ -639,10 +330,9 @@ world hue += round(hue / 255) return hue - // positive angle rotates forward through red->green->blue -/proc/RotateHue(hsv, angle) - var/list/HSV = ReadHSV(hsv) +/proc/RotateHue(rgb, angle) + var/list/HSV = rgb2hsv(rgb) // normalize hsv in case anything is screwy if(HSV[1] >= 1536) @@ -665,18 +355,18 @@ world // decompress hue HSV[1] += round(HSV[1] / 255) - return hsv(HSV[1], HSV[2], HSV[3], (HSV.len > 3 ? HSV[4] : null)) + return hsv2rgb(HSV) // Convert an rgb color to grayscale /proc/GrayScale(rgb) - var/list/RGB = ReadRGB(rgb) + var/list/RGB = rgb2num(rgb) var/gray = RGB[1]*0.3 + RGB[2]*0.59 + RGB[3]*0.11 return (RGB.len > 3) ? rgb(gray, gray, gray, RGB[4]) : rgb(gray, gray, gray) // Change grayscale color to black->tone->white range /proc/ColorTone(rgb, tone) - var/list/RGB = ReadRGB(rgb) - var/list/TONE = ReadRGB(tone) + var/list/RGB = rgb2num(rgb) + var/list/TONE = rgb2num(tone) var/gray = RGB[1]*0.3 + RGB[2]*0.59 + RGB[3]*0.11 var/tone_gray = TONE[1]*0.3 + TONE[2]*0.59 + TONE[3]*0.11 @@ -701,15 +391,11 @@ world var/lo3 = text2ascii(hex, 7) // B var/hi4 = text2ascii(hex, 8) // A var/lo4 = text2ascii(hex, 9) // A - return list(((hi1>= 65 ? hi1-55 : hi1-48)<<4) | (lo1 >= 65 ? lo1-55 : lo1-48), + return list(((hi1 >= 65 ? hi1-55 : hi1-48)<<4) | (lo1 >= 65 ? lo1-55 : lo1-48), ((hi2 >= 65 ? hi2-55 : hi2-48)<<4) | (lo2 >= 65 ? lo2-55 : lo2-48), ((hi3 >= 65 ? hi3-55 : hi3-48)<<4) | (lo3 >= 65 ? lo3-55 : lo3-48), ((hi4 >= 65 ? hi4-55 : hi4-48)<<4) | (lo4 >= 65 ? lo4-55 : lo4-48)) -/// Create a single [/icon] from a given [/atom] or [/image]. -/// -/// Very low-performance. Should usually only be used for HTML, where BYOND's -/// appearance system (overlays/underlays, etc.) is not available. /// /// Only the first argument is required. /proc/getFlatIcon(image/appearance, defdir, deficon, defstate, defblend, start = TRUE, no_anim = FALSE) diff --git a/code/__HELPERS/matrices.dm b/code/__HELPERS/matrices.dm index ee4c5a5b11db..42935b2d0a66 100644 --- a/code/__HELPERS/matrices.dm +++ b/code/__HELPERS/matrices.dm @@ -210,21 +210,32 @@ round(cos_inv_third+sqrt3_sin, 0.001), round(cos_inv_third-sqrt3_sin, 0.001), ro output[offset+x] = round(A[offset+1]*B[x] + A[offset+2]*B[x+4] + A[offset+3]*B[x+8] + A[offset+4]*B[x+12]+(y==5?B[x+16]:0), 0.001) return output -///Converts RGB shorthands into RGBA matrices complete of constants rows (ergo a 20 keys list in byond). -/proc/color_to_full_rgba_matrix(color) +/** + * Converts RGB shorthands into RGBA matrices complete of constants rows (ergo a 20 keys list in byond). + * if return_identity_on_fail is true, stack_trace is called instead of CRASH, and an identity is returned. + */ +/proc/color_to_full_rgba_matrix(color, return_identity_on_fail = TRUE) + if(!color) + return COLOR_MATRIX_IDENTITY if(istext(color)) - var/list/L = ReadRGB(color) + var/list/L = rgb2num(color) if(!L) - CRASH("Invalid/unsupported color format argument in color_to_full_rgba_matrix()") + var/message = "Invalid/unsupported color ([color]) argument in color_to_full_rgba_matrix()" + if(return_identity_on_fail) + stack_trace(message) + return COLOR_MATRIX_IDENTITY + CRASH(message) return list(L[1]/255,0,0,0, 0,L[2]/255,0,0, 0,0,L[3]/255,0, 0,0,0,L.len>3?L[4]/255:1, 0,0,0,0) - else if(!islist(color)) //invalid format - return color_matrix_identity() + + if(!islist(color)) //invalid format + CRASH("Invalid/unsupported color ([color]) argument in color_to_full_rgba_matrix()") + var/list/L = color switch(L.len) if(3 to 5) // row-by-row hexadecimals . = list() for(var/a in 1 to L.len) - var/list/rgb = ReadRGB(L[a]) + var/list/rgb = rgb2num(L[a]) for(var/b in rgb) . += b/255 if(length(rgb) % 4) // RGB has no alpha instruction @@ -244,17 +255,18 @@ round(cos_inv_third+sqrt3_sin, 0.001), round(cos_inv_third-sqrt3_sin, 0.001), ro for(var/b in 1 to 20-L.len) . += 0 else - CRASH("Invalid/unsupported color format argument in color_to_full_rgba_matrix()") - -//Returns an identity color matrix which does nothing -/proc/color_identity() - return list(1,0,0, 0,1,0, 0,0,1) + var/message = "Invalid/unsupported color (list of length [L.len]) argument in color_to_full_rgba_matrix()" + if(return_identity_on_fail) + stack_trace(message) + return COLOR_MATRIX_IDENTITY + CRASH(message) //Moves all colors angle degrees around the color wheel while maintaining intensity of the color and not affecting whites //TODO: Need a version that only affects one color (ie shift red to blue but leave greens and blues alone) /proc/color_rotation(angle) if(angle == 0) - return color_identity() + return COLOR_MATRIX_IDENTITY + angle = clamp(angle, -180, 180) var/cos = cos(angle) var/sin = sin(angle) @@ -285,7 +297,7 @@ GLOBAL_REAL_VAR(list/delta_index) = list( /proc/color_contrast(value) value = clamp(value, -100, 100) if(value == 0) - return color_identity() + return COLOR_MATRIX_IDENTITY var/x = 0 if (value < 0) @@ -305,7 +317,7 @@ GLOBAL_REAL_VAR(list/delta_index) = list( //Exxagerates or removes colors /proc/color_saturation(value as num) if(value == 0) - return color_identity() + return COLOR_MATRIX_IDENTITY value = clamp(value, -100, 100) if(value > 0) value *= 3 diff --git a/code/__HELPERS/type2type.dm b/code/__HELPERS/type2type.dm index f6089e6a767d..badf6f27e62d 100644 --- a/code/__HELPERS/type2type.dm +++ b/code/__HELPERS/type2type.dm @@ -318,15 +318,16 @@ GLOBAL_LIST_INIT(modulo_angle_to_dir, list(NORTH,NORTHEAST,EAST,SOUTHEAST,SOUTH, /proc/color_hex2color_matrix(string) var/length = length(string) if((length != 7 && length != 9) || length != length_char(string)) - return color_matrix_identity() - var/r = hex2num(copytext(string, 2, 4))/255 - var/g = hex2num(copytext(string, 4, 6))/255 - var/b = hex2num(copytext(string, 6, 8))/255 + return COLOR_MATRIX_IDENTITY + // For runtime safety + . = COLOR_MATRIX_IDENTITY + var/list/color = rgb2num(string) + var/r = color[1] / 255 + var/g = color[2] / 255 + var/b = color[3] / 255 var/a = 1 - if(length == 9) - a = hex2num(copytext(string, 8, 10))/255 - if(!isnum(r) || !isnum(g) || !isnum(b) || !isnum(a)) - return color_matrix_identity() + if(length(color) == 4) + a = color[4] / 255 return list(r,0,0,0, 0,g,0,0, 0,0,b,0, 0,0,0,a, 0,0,0,0) //will drop all values not on the diagonal diff --git a/code/game/machinery/rotating_light.dm b/code/game/machinery/rotating_light.dm index 5191efd3c912..194234113dab 100644 --- a/code/game/machinery/rotating_light.dm +++ b/code/game/machinery/rotating_light.dm @@ -84,8 +84,9 @@ spinning_lights_cache["[color]"] = new /obj/effect/spinning_light() spin_effect = spinning_lights_cache["[color]"] alarm_light_color = color - var/HSV = RGBtoHSV(alarm_light_color) - var/RGB = HSVtoRGB(RotateHue(HSV, angle)) + + var/RGB = RotateHue(alarm_light_color, angle) + alarm_light_color = RGB spin_effect.set_color(color) if (on) diff --git a/code/game/objects/effects/decals/cleanable/humans.dm b/code/game/objects/effects/decals/cleanable/humans.dm index f89042bd50a9..32fed44f4b69 100644 --- a/code/game/objects/effects/decals/cleanable/humans.dm +++ b/code/game/objects/effects/decals/cleanable/humans.dm @@ -102,8 +102,8 @@ name = dryname desc = drydesc reagents.remove_all_type(/datum/reagent/blood, INFINITY) - var/temp_color = ReadHSV(RGBtoHSV(color || COLOR_WHITE)) - color = HSVtoRGB(hsv(temp_color[1], temp_color[2], max(temp_color[3] - 100, 0))) + var/list/temp_color = rgb2hsv(color || COLOR_WHITE) + color = hsv2rgb(temp_color[1], temp_color[2], max(temp_color[3] - 100, 0)) qdel(GetComponent(/datum/component/smell)) if(spook_factor) AddComponent(/datum/component/spook_factor, spook_factor) diff --git a/code/game/objects/items/plushes.dm b/code/game/objects/items/plushes.dm index 8134ba705386..bdd09f19d5ec 100644 --- a/code/game/objects/items/plushes.dm +++ b/code/game/objects/items/plushes.dm @@ -509,10 +509,10 @@ if(!greyscale_colors) // Generate a random valid lizard color for our plushie friend var/generated_lizard_color = "#" + random_color() - var/temp_hsv = RGBtoHSV(generated_lizard_color) + var/list/temp_hsv = rgb2hsv(generated_lizard_color) // If our color is too dark, use the classic green lizard plush color - if(ReadHSV(temp_hsv)[3] < ReadHSV("#7F7F7F")[3]) + if(temp_hsv[3] < 50) generated_lizard_color = "#66ff33" // Set our greyscale colors to the lizard color we made + black eyes diff --git a/code/game/objects/items/stacks/wrap.dm b/code/game/objects/items/stacks/wrap.dm index f6c40cf0e346..dd3628887e43 100644 --- a/code/game/objects/items/stacks/wrap.dm +++ b/code/game/objects/items/stacks/wrap.dm @@ -23,13 +23,13 @@ //Generate random valid colors for paper and ribbon var/generated_base_color = "#" + random_color() var/generated_ribbon_color = "#" + random_color() - var/temp_base_hsv = RGBtoHSV(generated_base_color) - var/temp_ribbon_hsv = RGBtoHSV(generated_ribbon_color) + var/list/temp_base_hsv = rgb2hsv(generated_base_color) + var/list/temp_ribbon_hsv = rgb2hsv(generated_ribbon_color) //If colors are too dark, set to original colors - if(ReadHSV(temp_base_hsv)[3] < ReadHSV("7F7F7F")[3]) + if(temp_base_hsv[3] < 50) generated_base_color = "#00FF00" - if(ReadHSV(temp_ribbon_hsv)[3] < ReadHSV("7F7F7F")[3]) + if(temp_ribbon_hsv[3] < 50) generated_ribbon_color = "#FF0000" //Set layers to these colors, base then ribbon diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm index f96ce4eae1db..eedd8b78146f 100644 --- a/code/game/objects/structures/mirror.dm +++ b/code/game/objects/structures/mirror.dm @@ -198,9 +198,9 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/mirror, 28) return TRUE if(new_mutantcolor) - var/temp_hsv = RGBtoHSV(new_mutantcolor) + var/list/temp_hsv = rgb2hsv(new_mutantcolor) - if(ReadHSV(temp_hsv)[3] >= ReadHSV("#7F7F7F")[3]) // mutantcolors must be bright + if(temp_hsv[3] >= 50) // mutantcolors must be bright amazed_human.dna.features[mutcolor2change] = sanitize_hexcolor(new_mutantcolor) amazed_human.dna.update_uf_block(DNA_MUTANT_COLOR_BLOCK) diff --git a/code/modules/mob/living/carbon/human/species_types/ethereal.dm b/code/modules/mob/living/carbon/human/species_types/ethereal.dm index 1685a56f512f..629930704e1d 100644 --- a/code/modules/mob/living/carbon/human/species_types/ethereal.dm +++ b/code/modules/mob/living/carbon/human/species_types/ethereal.dm @@ -60,12 +60,6 @@ var/current_color var/EMPeffect = FALSE var/emageffect = FALSE - var/r1 - var/g1 - var/b1 - var/static/r2 = 237 - var/static/g2 = 164 - var/static/b2 = 149 var/obj/effect/dummy/lighting_obj/ethereal_light @@ -81,10 +75,9 @@ if(!ishuman(C)) return var/mob/living/carbon/human/ethereal = C + default_color = ethereal.dna.features["ethcolor"] - r1 = GETREDPART(default_color) - g1 = GETGREENPART(default_color) - b1 = GETBLUEPART(default_color) + RegisterSignal(ethereal, COMSIG_ATOM_EMAG_ACT, PROC_REF(on_emag_act)) RegisterSignal(ethereal, COMSIG_ATOM_EMP_ACT, PROC_REF(on_emp_act)) RegisterSignal(ethereal, COMSIG_LIGHT_EATER_ACT, PROC_REF(on_light_eater)) @@ -127,13 +120,19 @@ if(H.stat != DEAD && !EMPeffect) var/healthpercent = max(H.health, 0) / 100 if(!emageffect) - current_color = rgb(r2 + ((r1-r2)*healthpercent), g2 + ((g1-g2)*healthpercent), b2 + ((b1-b2)*healthpercent)) + var/static/list/skin_color = rgb2num("#eda495") + var/list/colors = rgb2num(H.dna.features["ethcolor"]) + var/list/built_color = list() + for(var/i in 1 to 3) + built_color += skin_color[i] + ((colors[i] - skin_color[i]) * healthpercent) + current_color = rgb(built_color[1], built_color[2], built_color[3]) ethereal_light.set_light_range_power_color(1 + (2 * healthpercent), 1 + (1 * healthpercent), current_color) ethereal_light.set_light_on(TRUE) fixed_mut_color = current_color else ethereal_light.set_light_on(FALSE) fixed_mut_color = rgb(128,128,128) + H.hair_color = current_color H.facial_hair_color = current_color H.update_body() diff --git a/code/modules/mob/living/simple_animal/hostile/space_dragon.dm b/code/modules/mob/living/simple_animal/hostile/space_dragon.dm index 00b900438922..04bb989f94a7 100644 --- a/code/modules/mob/living/simple_animal/hostile/space_dragon.dm +++ b/code/modules/mob/living/simple_animal/hostile/space_dragon.dm @@ -236,11 +236,12 @@ to_chat(src, span_warning("Not a valid color, please try again.")) color_selection() return - var/temp_hsv = RGBtoHSV(chosen_color) - if(ReadHSV(temp_hsv)[3] < DARKNESS_THRESHOLD) + var/list/temp_hsv = rgb2hsv(chosen_color) + if(temp_hsv[3] < DARKNESS_THRESHOLD) to_chat(src, span_danger("Invalid color. Your color is not bright enough.")) color_selection() return + add_atom_colour(chosen_color, FIXED_COLOUR_PRIORITY) add_dragon_overlay() diff --git a/code/modules/power/supermatter/nupermatter.dm b/code/modules/power/supermatter/nupermatter.dm index 28af71540a74..b9b78faf3b24 100644 --- a/code/modules/power/supermatter/nupermatter.dm +++ b/code/modules/power/supermatter/nupermatter.dm @@ -564,16 +564,13 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter) color = color_matrix - var/HSV = RGBtoHSV(base_color) - var/RGB = HSVtoRGB(RotateHue(HSV, angle)) + var/RGB = RotateHue(base_color, angle) base_color = RGB - HSV = RGBtoHSV(warning_color) - RGB = HSVtoRGB(RotateHue(HSV, angle)) + RGB = RotateHue(warning_color, angle) warning_color = RGB - HSV = RGBtoHSV(emergency_color) - RGB = HSVtoRGB(RotateHue(HSV, angle)) + RGB = RotateHue(emergency_color, angle) emergency_color = RGB /obj/machinery/power/supermatter/inert diff --git a/daedalus.dme b/daedalus.dme index f6b4138cb78f..20a1ed09c8ea 100644 --- a/daedalus.dme +++ b/daedalus.dme @@ -98,6 +98,7 @@ #include "code\__DEFINES\generators.dm" #include "code\__DEFINES\ghost.dm" #include "code\__DEFINES\grab_defines.dm" +#include "code\__DEFINES\gradient.dm" #include "code\__DEFINES\gravity.dm" #include "code\__DEFINES\greyscale.dm" #include "code\__DEFINES\hud.dm"