mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-09 16:12:17 +00:00
Co-authored-by: Casey <a.roaming.shadow@gmail.com> Co-authored-by: Raeschen <rycoop29@gmail.com>
666 lines
20 KiB
Plaintext
666 lines
20 KiB
Plaintext
/*
|
|
IconProcs README
|
|
|
|
A BYOND library for manipulating icons and colors
|
|
|
|
by Lummox JR
|
|
|
|
version 1.0
|
|
|
|
The IconProcs library was made to make a lot of common icon operations much easier. BYOND's icon manipulation
|
|
routines are very capable but some of the advanced capabilities like using alpha transparency can be unintuitive to beginners.
|
|
|
|
CHANGING ICONS
|
|
|
|
Several new procs have been added to the /icon datum to simplify working with icons. To use them,
|
|
remember you first need to setup an /icon var like so:
|
|
|
|
GLOBAL_DATUM_INIT(my_icon, /icon, new('iconfile.dmi'))
|
|
|
|
icon/ChangeOpacity(amount = 1)
|
|
A very common operation in DM is to try to make an icon more or less transparent. Making an icon more
|
|
transparent is usually much easier than making it less so, however. This proc basically is a frontend
|
|
for MapColors() which can change opacity any way you like, in much the same way that SetIntensity()
|
|
can make an icon lighter or darker. If amount is 0.5, the opacity of the icon will be cut in half.
|
|
If amount is 2, opacity is doubled and anything more than half-opaque will become fully opaque.
|
|
icon/GrayScale()
|
|
Converts the icon to grayscale instead of a fully colored icon. Alpha values are left intact.
|
|
icon/ColorTone(tone)
|
|
Similar to GrayScale(), this proc converts the icon to a range of black -> tone -> white, where tone is an
|
|
RGB color (its alpha is ignored). This can be used to create a sepia tone or similar effect.
|
|
See also the global ColorTone() proc.
|
|
icon/MinColors(icon)
|
|
The icon is blended with a second icon where the minimum of each RGB pixel is the result.
|
|
Transparency may increase, as if the icons were blended with ICON_ADD. You may supply a color in place of an icon.
|
|
icon/MaxColors(icon)
|
|
The icon is blended with a second icon where the maximum of each RGB pixel is the result.
|
|
Opacity may increase, as if the icons were blended with ICON_OR. You may supply a color in place of an icon.
|
|
icon/Opaque(background = "#000000")
|
|
All alpha values are set to 255 throughout the icon. Transparent pixels become black, or whatever background color you specify.
|
|
icon/BecomeAlphaMask()
|
|
You can convert a simple grayscale icon into an alpha mask to use with other icons very easily with this proc.
|
|
The black parts become transparent, the white parts stay white, and anything in between becomes a translucent shade of white.
|
|
icon/AddAlphaMask(mask)
|
|
The alpha values of the mask icon will be blended with the current icon. Anywhere the mask is opaque,
|
|
the current icon is untouched. Anywhere the mask is transparent, the current icon becomes transparent.
|
|
Where the mask is translucent, the current icon becomes more transparent.
|
|
icon/UseAlphaMask(mask, mode)
|
|
Sometimes you may want to take the alpha values from one icon and use them on a different icon.
|
|
This proc will do that. Just supply the icon whose alpha mask you want to use, and src will change
|
|
so it has the same colors as before but uses the mask for opacity.
|
|
|
|
COLOR MANAGEMENT AND HSV
|
|
|
|
RGB isn't the only way to represent color. Sometimes it's more useful to work with a model called HSV, which stands for hue, saturation, and value.
|
|
|
|
* The hue of a color describes where it is along the color wheel. It goes from red to yellow to green to
|
|
cyan to blue to magenta and back to red.
|
|
* The saturation of a color is how much color is in it. A color with low saturation will be more gray,
|
|
and with no saturation at all it is a shade of gray.
|
|
* 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.
|
|
|
|
* 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
|
|
|
|
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".
|
|
|
|
More than one HSV color can match the same RGB color.
|
|
|
|
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
|
|
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.
|
|
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.
|
|
GrayScale(rgb)
|
|
Takes an RGB or RGBA color and converts it to grayscale. Returns an RGB or RGBA string.
|
|
ColorTone(rgb, tone)
|
|
Similar to GrayScale(), this proc converts an RGB or RGBA color to a range of black -> tone -> white instead of
|
|
using strict shades of gray. The tone value is an RGB color; any alpha value is ignored.
|
|
*/
|
|
|
|
/*
|
|
Get Flat Icon DEMO by DarkCampainger
|
|
|
|
This is a test for the get flat icon proc, modified approprietly for icons and their states.
|
|
Probably not a good idea to run this unless you want to see how the proc works in detail.
|
|
mob
|
|
icon = 'old_or_unused.dmi'
|
|
icon_state = "green"
|
|
|
|
Login()
|
|
// Testing image underlays
|
|
underlays += image(icon='old_or_unused.dmi',icon_state="red")
|
|
underlays += image(icon='old_or_unused.dmi',icon_state="red", pixel_x = 32)
|
|
underlays += image(icon='old_or_unused.dmi',icon_state="red", pixel_x = -32)
|
|
|
|
// Testing image overlays
|
|
add_overlay(image(icon='old_or_unused.dmi',icon_state="green", pixel_x = 32, pixel_y = -32))
|
|
add_overlay(image(icon='old_or_unused.dmi',icon_state="green", pixel_x = 32, pixel_y = 32))
|
|
add_overlay(image(icon='old_or_unused.dmi',icon_state="green", pixel_x = -32, pixel_y = -32))
|
|
|
|
// Testing icon file overlays (defaults to mob's state)
|
|
add_overlay('_flat_demoIcons2.dmi')
|
|
|
|
// Testing icon_state overlays (defaults to mob's icon)
|
|
add_overlay("white")
|
|
|
|
// Testing dynamic icon overlays
|
|
var/icon/I = icon('old_or_unused.dmi', icon_state="aqua")
|
|
I.Shift(NORTH,16,1)
|
|
add_overlay(I)
|
|
|
|
// Testing dynamic image overlays
|
|
I=image(icon=I,pixel_x = -32, pixel_y = 32)
|
|
add_overlay(I)
|
|
|
|
// Testing object types (and layers)
|
|
add_overlay(/obj/effect/overlayTest)
|
|
|
|
loc = locate (10,10,1)
|
|
verb
|
|
Browse_Icon()
|
|
set name = "1. Browse Icon"
|
|
// Give it a name for the cache
|
|
var/iconName = "[ckey(src.name)]_flattened.dmi"
|
|
// Send the icon to src's local cache
|
|
src<<browse_rsc(get_flat_icon(src), iconName)
|
|
// Display the icon in their browser
|
|
src<<browse("<body bgcolor='#000000'><p><img src='[iconName]'></p></body>")
|
|
|
|
Output_Icon()
|
|
set name = "2. Output Icon"
|
|
to_chat(src, "Icon is: [icon2base64html(get_flat_icon(src))]")
|
|
|
|
Label_Icon()
|
|
set name = "3. Label Icon"
|
|
// Give it a name for the cache
|
|
var/iconName = "[ckey(src.name)]_flattened.dmi"
|
|
// Copy the file to the rsc manually
|
|
var/icon/I = fcopy_rsc(get_flat_icon(src))
|
|
// Send the icon to src's local cache
|
|
src<<browse_rsc(I, iconName)
|
|
// Update the label to show it
|
|
winset(src,"imageLabel","image='[REF(I)]'");
|
|
|
|
Add_Overlay()
|
|
set name = "4. Add Overlay"
|
|
add_overlay(image(icon='old_or_unused.dmi',icon_state="yellow",pixel_x = rand(-64,32), pixel_y = rand(-64,32))
|
|
|
|
Stress_Test()
|
|
set name = "5. Stress Test"
|
|
for(var/i = 0 to 1000)
|
|
// The third parameter forces it to generate a new one, even if it's already cached
|
|
get_flat_icon(src,0,2)
|
|
if(prob(5))
|
|
Add_Overlay()
|
|
Browse_Icon()
|
|
|
|
Cache_Test()
|
|
set name = "6. Cache Test"
|
|
for(var/i = 0 to 1000)
|
|
get_flat_icon(src)
|
|
Browse_Icon()
|
|
|
|
/obj/effect/overlayTest
|
|
icon = 'old_or_unused.dmi'
|
|
icon_state = "blue"
|
|
pixel_x = -24
|
|
pixel_y = 24
|
|
layer = TURF_LAYER // Should appear below the rest of the overlays
|
|
|
|
world
|
|
view = "7x7"
|
|
maxx = 20
|
|
maxy = 20
|
|
maxz = 1
|
|
*/
|
|
|
|
/*
|
|
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
|
|
*/
|
|
|
|
/**
|
|
* Porting the icons_ch.dm file straight from chompstation as I do not want to risk breaking stuff.
|
|
*/
|
|
|
|
/**
|
|
* reads RGB or RGBA values to list
|
|
* @return list(r, g, b) or list(r, g, b, a), values 0 to 255.
|
|
*/
|
|
/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)
|
|
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)
|
|
|
|
/*
|
|
Smooth blend between HSV colors
|
|
|
|
amount=0 is the first color
|
|
amount=1 is the second color
|
|
amount=0.5 is directly between the two colors
|
|
|
|
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)
|
|
|
|
/proc/BlendRGBasHSV(rgb1, rgb2, amount)
|
|
return HSVtoRGB(RGBtoHSV(rgb1), RGBtoHSV(rgb2), amount)
|
|
|
|
/proc/HueToAngle(hue)
|
|
// normalize hsv in case anything is screwy
|
|
if(hue < 0 || hue >= 1536)
|
|
hue %= 1536
|
|
if(hue < 0)
|
|
hue += 1536
|
|
// Compress hue into easier-to-manage range
|
|
hue -= hue >> 8
|
|
return hue / (1530/360)
|
|
|
|
/proc/AngleToHue(angle)
|
|
// normalize hsv in case anything is screwy
|
|
if(angle < 0 || angle >= 360)
|
|
angle -= 360 * round(angle / 360)
|
|
var/hue = angle * (1530/360)
|
|
// Decompress hue
|
|
hue += round(hue / 255)
|
|
return hue
|
|
|
|
|
|
// positive angle rotates forward through red->green->blue
|
|
/proc/RotateHue(hsv, angle)
|
|
var/list/HSV = ReadHSV(hsv)
|
|
|
|
// normalize hsv in case anything is screwy
|
|
if(HSV[1] >= 1536)
|
|
HSV[1] %= 1536
|
|
if(HSV[1] < 0)
|
|
HSV[1] += 1536
|
|
|
|
// Compress hue into easier-to-manage range
|
|
HSV[1] -= HSV[1] >> 8
|
|
|
|
if(angle < 0 || angle >= 360)
|
|
angle -= 360 * round(angle / 360)
|
|
HSV[1] = round(HSV[1] + angle * (1530/360), 1)
|
|
|
|
// normalize hue
|
|
if(HSV[1] < 0 || HSV[1] >= 1530)
|
|
HSV[1] %= 1530
|
|
if(HSV[1] < 0)
|
|
HSV[1] += 1530
|
|
// decompress hue
|
|
HSV[1] += round(HSV[1] / 255)
|
|
|
|
return hsv(HSV[1], HSV[2], HSV[3], (HSV.len > 3 ? HSV[4] : null))
|
|
|
|
// Convert an rgb color to grayscale
|
|
/proc/GrayScale(rgb)
|
|
var/list/RGB = ReadRGB(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/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
|
|
|
|
if(gray <= tone_gray)
|
|
return BlendRGB("#000000", tone, gray/(tone_gray || 1))
|
|
else
|
|
return BlendRGB(tone, "#ffffff", (gray-tone_gray)/((255-tone_gray) || 1))
|
|
|
|
|
|
//Used in the OLD chem colour mixing algorithm
|
|
/proc/GetColors(hex)
|
|
hex = uppertext(hex)
|
|
// No alpha set? Default to full alpha.
|
|
if(length(hex) == 7)
|
|
hex += "FF"
|
|
var/hi1 = text2ascii(hex, 2) // R
|
|
var/lo1 = text2ascii(hex, 3) // R
|
|
var/hi2 = text2ascii(hex, 4) // G
|
|
var/lo2 = text2ascii(hex, 5) // G
|
|
var/hi3 = text2ascii(hex, 6) // B
|
|
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),
|
|
((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))
|
|
|
|
//Interface for using DrawBox() to draw 1 pixel on a coordinate.
|
|
//Returns the same icon specifed in the argument, but with the pixel drawn
|
|
/proc/DrawPixel(icon/I,colour,drawX,drawY)
|
|
if(!I)
|
|
return 0
|
|
|
|
var/Iwidth = I.Width()
|
|
var/Iheight = I.Height()
|
|
|
|
if(drawX > Iwidth || drawX <= 0)
|
|
return 0
|
|
if(drawY > Iheight || drawY <= 0)
|
|
return 0
|
|
|
|
I.DrawBox(colour,drawX, drawY)
|
|
return I
|
|
|
|
|
|
//Interface for easy drawing of one pixel on an atom.
|
|
/atom/proc/DrawPixelOn(colour, drawX, drawY)
|
|
var/icon/I = new(icon)
|
|
var/icon/J = DrawPixel(I, colour, drawX, drawY)
|
|
if(J) //Only set the icon if it succeeded, the icon without the pixel is 1000x better than a black square.
|
|
icon = J
|
|
return J
|
|
return 0
|
|
|
|
//Hook, override to run code on- wait this is images
|
|
//Images have dir without being an atom, so they get their own definition.
|
|
//Lame.
|
|
/image/proc/setDir(newdir)
|
|
dir = newdir
|
|
|
|
/* Gives the result RGB of a RGB string after a matrix transformation. No alpha.
|
|
* Input: rr, rg, rb, gr, gg, gb, br, bg, bb, cr, cg, cb
|
|
* Output: RGB string
|
|
*/
|
|
/proc/RGBMatrixTransform(list/color, list/cm)
|
|
ASSERT(cm.len >= 9)
|
|
if(cm.len < 12) // fill in the rest
|
|
for(var/i in 1 to (12 - cm.len))
|
|
cm += 0
|
|
if(!islist(color))
|
|
color = ReadRGB(color)
|
|
color[1] = color[1] * cm[1] + color[2] * cm[2] + color[3] * cm[3] + cm[10] * 255
|
|
color[2] = color[1] * cm[4] + color[2] * cm[5] + color[3] * cm[6] + cm[11] * 255
|
|
color[3] = color[1] * cm[7] + color[2] * cm[8] + color[3] * cm[9] + cm[12] * 255
|
|
return rgb(color[1], color[2], color[3])
|