mirror of
https://github.com/vgstation-coders/vgstation13.git
synced 2025-12-10 10:21:11 +00:00
Move libraries into code/libs/
This commit is contained in:
94
code/libs/Get Flat Icon/Documentation.html
Normal file
94
code/libs/Get Flat Icon/Documentation.html
Normal file
@@ -0,0 +1,94 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Get Flat Icon - Documentation</title>
|
||||
<style type="text/css">
|
||||
#page {
|
||||
width: 600px;
|
||||
margin: 0px auto;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 44px;
|
||||
}
|
||||
.subtitle {
|
||||
font-size: 30px;
|
||||
}
|
||||
.title, .subtitle {
|
||||
font-weight: bold;
|
||||
font-family: tahoma;
|
||||
text-align: center;
|
||||
}
|
||||
.subsubtitle {
|
||||
font-size: 12px;
|
||||
font-family: tahoma;
|
||||
text-align: center;
|
||||
margin: 6px;
|
||||
}
|
||||
.section {
|
||||
font-weight: bold;
|
||||
font-family: tahoma;
|
||||
text-decoration: underline;
|
||||
margin: 20px 0px 6px 0px;
|
||||
}
|
||||
.code {
|
||||
white-space: pre;
|
||||
overflow: auto;
|
||||
font-family: courier;
|
||||
padding: 6px 6px 6px 12px;
|
||||
background: #DDDDDD;
|
||||
border: 1px solid #999999;
|
||||
margin: 6px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page">
|
||||
<div class="title">Get Flat Icon</div>
|
||||
<div class="subsubtitle">Created by David "DarkCampainger" Braun</div>
|
||||
<div class="subsubtitle">Released under the Unlicense (see Unlicense.txt)</div>
|
||||
<div class="subsubtitle">Version 2.0 - August 27, 2013</div>
|
||||
<div class="subsubtitle">Please see 'demo/demo.dm' for the example usage</div>
|
||||
|
||||
<div class="section">Summary</div>
|
||||
<p>The Get Flat Icon library flattens an object's appearance into a single dynamic icon at runtime. The icon can then easily be shown in a browser, output, label, ect.</p>
|
||||
<p>The library fully supports all types of overlays: icon files, icon states, objects, object types, images, dynamic icons, and images with dynamic icons. It correctly handles inheriting the icon, icon_state, and dir* from the base mob; as well as FLOAT_LAYER. It can even handle pixel_x/y values that move overlays outside of the base icon's bounds.</p>
|
||||
<p>The library also has a fully functional cache, to avoid flattening the same icon twice.</p>
|
||||
<p>(*note: there is a limitation with the <tt>dir</tt> variable where the library cannot distinguish between an overlay that inherits its direction from its parent and one that is set to face SOUTH. If an overlay's direction is set to SOUTH, the library will always assume it inherits from the parent)</p>
|
||||
|
||||
<div class="section">Reference</div>
|
||||
<dl>
|
||||
<dt><b>getFlatIcon(atom/A, dir, cache=1)</b></dt>
|
||||
<dd>Return value: /icon object</dd>
|
||||
<dd>Parameters:<br>
|
||||
<ul>
|
||||
<li>atom/A - the atom you want a flattened icon of</li>
|
||||
<li>dir - the direction you want the atom rendered in (defaults to current direction)</li>
|
||||
<li>cache - determines how the cache is used (default 1)
|
||||
<ul>
|
||||
<li>0 - Cache is ignored and not written to</li>
|
||||
<li>1 - Cache is checked</li>
|
||||
<li>2 - Cache is not checked, but generated icon is written to it</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<div class="section">Version History</div>
|
||||
<dl>
|
||||
<dt>Version 2 (August 27, 2013)</dt>
|
||||
<dd>Improved handling of overlays that inherit directions, as much as can be done</dd>
|
||||
<dt>Version 1 (December 27th, 2011)</dt>
|
||||
<dd>Improved the handling of directions</dd>
|
||||
<dd>Changed internal variable names to make them more readable</dd>
|
||||
<dt>Version 0 (May 31th, 2011)</dt>
|
||||
<dd>Initial release</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
135
code/libs/Get Flat Icon/Get Flat Icon.dm
Normal file
135
code/libs/Get Flat Icon/Get Flat Icon.dm
Normal file
@@ -0,0 +1,135 @@
|
||||
|
||||
/* *********************************************************************
|
||||
_____ _ ______ _ _ _____
|
||||
/ ____| | | | ____| | | | |_ _|
|
||||
| | __ ___| |_ | |__ | | __ _| |_ | | ___ ___ _ __
|
||||
| | |_ |/ _ \ __| | __| | |/ _` | __| | | / __/ _ \| '_ \
|
||||
| |__| | __/ |_ | | | | (_| | |_ _| || (_| (_) | | | |
|
||||
\_____|\___|\__| |_| |_|\__,_|\__| |_____\___\___/|_| |_|
|
||||
|
||||
Created by David "DarkCampainger" Braun
|
||||
|
||||
Released under the Unlicense (see Unlicense.txt)
|
||||
|
||||
Version 1.2 - August 27, 2013
|
||||
|
||||
Please see 'demo/demo.dm' for the example usage
|
||||
Please see 'Documentation.html' for reference
|
||||
|
||||
*///////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// Associative list of [md5 values = Icon] for determining if the icon already exists
|
||||
var/list/_flatIcons = list()
|
||||
|
||||
proc
|
||||
getFlatIcon(atom/A, dir, cache=1) // 1 = use cache, 2 = override cache, 0 = ignore cache
|
||||
|
||||
var/list/layers = list() // Associative list of [overlay = layer]
|
||||
var/hash = "" // Hash of overlay combination
|
||||
|
||||
if(!dir) dir = A.dir // dir defaults to A's dir
|
||||
|
||||
// Add the atom's icon itself
|
||||
if(A.icon)
|
||||
// Make a copy without pixel_x/y settings
|
||||
var/image/copy = image(icon=A.icon,icon_state=A.icon_state,layer=A.layer,dir=dir)
|
||||
layers[copy] = A.layer
|
||||
|
||||
|
||||
// Loop through the underlays, then overlays, sorting them into the layers list
|
||||
var
|
||||
list/process = A.underlays // Current list being processed
|
||||
processSubset=0 // Which list is being processed: 0 = underlays, 1 = overlays
|
||||
|
||||
currentIndex=1 // index of 'current' in list being processed
|
||||
currentOverlay // Current overlay being sorted
|
||||
currentLayer // Calculated layer that overlay appears on (special case for FLOAT_LAYER)
|
||||
|
||||
compareOverlay // The overlay that the current overlay is being compared against
|
||||
compareIndex // The index in the layers list of 'compare'
|
||||
while(TRUE)
|
||||
if(currentIndex<=process.len)
|
||||
currentOverlay = process[currentIndex]
|
||||
currentLayer = currentOverlay:layer
|
||||
if(currentLayer<0) // Special case for FLY_LAYER
|
||||
ASSERT(currentLayer > -1000)
|
||||
if(processSubset == 0) // Underlay
|
||||
currentLayer = A.layer+currentLayer/1000
|
||||
else // Overlay
|
||||
currentLayer = A.layer+(1000+currentLayer)/1000
|
||||
|
||||
// Sort add into layers list
|
||||
for(compareIndex=1,compareIndex<=layers.len,compareIndex++)
|
||||
compareOverlay = layers[compareIndex]
|
||||
if(currentLayer < layers[compareOverlay]) // Associated value is the calculated layer
|
||||
layers.Insert(compareIndex,currentOverlay)
|
||||
layers[currentOverlay] = currentLayer
|
||||
break
|
||||
if(compareIndex>layers.len) // Reached end of list without inserting
|
||||
layers[currentOverlay]=currentLayer // Place at end
|
||||
|
||||
currentIndex++
|
||||
|
||||
if(currentIndex>process.len)
|
||||
if(processSubset == 0) // Switch to overlays
|
||||
currentIndex = 1
|
||||
processSubset = 1
|
||||
process = A.overlays
|
||||
else // All done
|
||||
break
|
||||
|
||||
if(cache!=0) // If cache is NOT disabled
|
||||
// Create a hash value to represent this specific flattened icon
|
||||
for(var/I in layers)
|
||||
hash += "\ref[I:icon],[I:icon_state],[I:dir != SOUTH ? I:dir : dir],[I:pixel_x],[I:pixel_y];_;"
|
||||
hash=md5(hash)
|
||||
|
||||
if(cache!=2) // If NOT overriding cache
|
||||
// Check if the icon has already been generated
|
||||
if((hash in _flatIcons) && _flatIcons[hash])
|
||||
// Icon already exists, just return that one
|
||||
return _flatIcons[hash]
|
||||
|
||||
var
|
||||
// We start with a blank canvas, otherwise some icon procs crash silently
|
||||
icon/flat = icon('_flat_Blank.dmi') // Final flattened icon
|
||||
icon/add // Icon of overlay being added
|
||||
|
||||
// Set current dimensions of flattened icon
|
||||
flatX1=1
|
||||
flatX2=flat.Width()
|
||||
flatY1=1
|
||||
flatY2=flat.Height()
|
||||
|
||||
// Dimensions of overlay being added
|
||||
addX1;addX2;addY1;addY2
|
||||
|
||||
for(var/I in layers)
|
||||
|
||||
add = icon(I:icon || A.icon
|
||||
, I:icon_state || (I:icon && (A.icon_state in icon_states(I:icon)) && A.icon_state)
|
||||
, (I:dir != SOUTH ? I:dir : dir)
|
||||
, 1
|
||||
, 0)
|
||||
|
||||
// Find the new dimensions of the flat icon to fit the added overlay
|
||||
addX1 = min(flatX1, I:pixel_x+1)
|
||||
addX2 = max(flatX2, I:pixel_x+add.Width())
|
||||
addY1 = min(flatY1, I:pixel_y+1)
|
||||
addY2 = max(flatY2, I:pixel_y+add.Height())
|
||||
|
||||
if(addX1!=flatX1 || addX2!=flatX2 || addY1!=flatY1 || addY2!=flatY2)
|
||||
// Resize the flattened icon so the new icon fits
|
||||
flat.Crop(addX1-flatX1+1, addY1-flatY1+1, addX2-flatX1+1, addY2-flatY1+1)
|
||||
flatX1=addX1;flatX2=addX2
|
||||
flatY1=addY1;flatY2=addY2
|
||||
|
||||
// Blend the overlay into the flattened icon
|
||||
flat.Blend(add,ICON_OVERLAY,I:pixel_x+2-flatX1,I:pixel_y+2-flatY1)
|
||||
|
||||
if(cache!=0) // If cache is NOT disabled
|
||||
// Cache the generated icon in our list so we don't have to regenerate it
|
||||
_flatIcons[hash] = flat
|
||||
|
||||
return flat
|
||||
17
code/libs/Get Flat Icon/Get Flat Icon.dme
Normal file
17
code/libs/Get Flat Icon/Get Flat Icon.dme
Normal file
@@ -0,0 +1,17 @@
|
||||
// BEGIN_INTERNALS
|
||||
/*
|
||||
FILE: Documentation.html
|
||||
*/
|
||||
// END_INTERNALS
|
||||
// BEGIN_FILE_DIR
|
||||
#define FILE_DIR .
|
||||
// END_FILE_DIR
|
||||
// BEGIN_PREFERENCES
|
||||
// END_PREFERENCES
|
||||
// BEGIN_INCLUDE
|
||||
#include "Get Flat Icon.dm"
|
||||
#ifdef __MAIN__ // BEGIN_DEMO
|
||||
#include "demo\demo.dm"
|
||||
#include "demo\demoInterface.dmf"
|
||||
#endif // END_DEMO
|
||||
// END_INCLUDE
|
||||
24
code/libs/Get Flat Icon/Unlicense.txt
Normal file
24
code/libs/Get Flat Icon/Unlicense.txt
Normal file
@@ -0,0 +1,24 @@
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
BIN
code/libs/Get Flat Icon/_flat_Blank.dmi
Normal file
BIN
code/libs/Get Flat Icon/_flat_Blank.dmi
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 209 B |
BIN
code/libs/Get Flat Icon/demo/_flat_demoIcons.dmi
Normal file
BIN
code/libs/Get Flat Icon/demo/_flat_demoIcons.dmi
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
BIN
code/libs/Get Flat Icon/demo/_flat_demoIcons2.dmi
Normal file
BIN
code/libs/Get Flat Icon/demo/_flat_demoIcons2.dmi
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 368 B |
144
code/libs/Get Flat Icon/demo/demo.dm
Normal file
144
code/libs/Get Flat Icon/demo/demo.dm
Normal file
@@ -0,0 +1,144 @@
|
||||
|
||||
/* *********************************************************************
|
||||
_____ _ ______ _ _ _____
|
||||
/ ____| | | | ____| | | | |_ _|
|
||||
| | __ ___| |_ | |__ | | __ _| |_ | | ___ ___ _ __
|
||||
| | |_ |/ _ \ __| | __| | |/ _` | __| | | / __/ _ \| '_ \
|
||||
| |__| | __/ |_ | | | | (_| | |_ _| || (_| (_) | | | |
|
||||
\_____|\___|\__| |_| |_|\__,_|\__| |_____\___\___/|_| |_|
|
||||
|
||||
Created by David "DarkCampainger" Braun
|
||||
|
||||
Released under the Unlicense (see Unlicense.txt)
|
||||
|
||||
Version 1.2 - August 27, 2013
|
||||
|
||||
Please see 'Documentation.html' for reference
|
||||
|
||||
*///////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define FILE_DIR demo
|
||||
|
||||
mob
|
||||
icon = '_flat_demoIcons.dmi'
|
||||
icon_state = "green"
|
||||
|
||||
Login()
|
||||
// Testing image underlays
|
||||
underlays += image(icon='_flat_demoIcons.dmi',icon_state="red", dir = EAST)
|
||||
underlays += image(icon='_flat_demoIcons.dmi',icon_state="red", pixel_x = 32)
|
||||
underlays += image(icon='_flat_demoIcons.dmi',icon_state="red", pixel_x = -32)
|
||||
|
||||
// Testing image overlays
|
||||
overlays += image(icon='_flat_demoIcons.dmi',icon_state="green", pixel_x = 32, pixel_y = -32)
|
||||
overlays += image(icon='_flat_demoIcons.dmi',icon_state="green", pixel_x = 32, pixel_y = 32)
|
||||
overlays += image(icon='_flat_demoIcons.dmi',icon_state="green", pixel_x = -32, pixel_y = -32)
|
||||
|
||||
// Testing icon file overlays (defaults to mob's state)
|
||||
overlays += '_flat_demoIcons2.dmi'
|
||||
|
||||
// Testing icon_state overlays (defaults to mob's icon)
|
||||
overlays += "white"
|
||||
|
||||
// Testing dynamic icon overlays
|
||||
var/icon/I = icon('_flat_demoIcons.dmi', icon_state="aqua")
|
||||
I.Shift(NORTH,16,1)
|
||||
overlays+=I
|
||||
|
||||
// Testing dynamic image overlays
|
||||
I=image(icon=I,pixel_x = -32, pixel_y = 32)
|
||||
overlays+=I
|
||||
|
||||
// Testing object types (and layers)
|
||||
overlays+=/obj/overlayTest
|
||||
|
||||
loc = locate (10,10,1)
|
||||
verb
|
||||
Browse_Icon()
|
||||
set name = "1. Browse Icon"
|
||||
|
||||
// Generate the flattened icon
|
||||
var/icon/I = getFlatIcon(src)
|
||||
|
||||
// Give it a name for the browser cache
|
||||
var/iconName = "[ckey(src.name)]_flattened.dmi"
|
||||
// Send the icon to src's browser cache
|
||||
src<<browse_rsc(I, iconName)
|
||||
// Display the icon in their browser
|
||||
src<<browse("<body bgcolor='#000000'><p><img src='[iconName]'></p></body>")
|
||||
|
||||
Browse_Portrait()
|
||||
set name = "2. Browse Portrait"
|
||||
|
||||
// Generate the flattened icon, facing south
|
||||
var/icon/I = getFlatIcon(src, SOUTH)
|
||||
|
||||
// Give it a name for the browser cache
|
||||
var/iconName = "[ckey(src.name)]_flattened.dmi"
|
||||
// Send the icon to src's browser cache
|
||||
src<<browse_rsc(I, iconName)
|
||||
// Display the icon in their browser
|
||||
src<<browse("<body bgcolor='#000000'><p><img src='[iconName]'></p></body>")
|
||||
|
||||
Output_Icon()
|
||||
set name = "3. Output Icon"
|
||||
src<<"\icon[getFlatIcon(src)]"
|
||||
src<<"-----------------------------------------"
|
||||
|
||||
Output_Fullsize_Icon()
|
||||
set name = "4. Output Fullsize Icon"
|
||||
var/icon/I = getFlatIcon(src)
|
||||
src<<{"<img class="icon" src="\ref[fcopy_rsc(I)]" style="width: [I.Width()]px; height: [I.Height()]px;">"}
|
||||
src<<"-----------------------------------------"
|
||||
|
||||
Label_Icon()
|
||||
set name = "5. Label Icon"
|
||||
|
||||
// Generate the flattened icon
|
||||
var/icon/I = getFlatIcon(src)
|
||||
|
||||
// Copy the file to the rsc manually
|
||||
I = fcopy_rsc(I)
|
||||
// Update the label to show it
|
||||
winset(src,"imageLabel","image='\ref[I]'");
|
||||
|
||||
Add_Overlay()
|
||||
set name = "6. Add Overlay"
|
||||
var/image/I
|
||||
|
||||
if(prob(50))
|
||||
// Create an overlay with a defined direction
|
||||
I = image(icon='_flat_demoIcons.dmi',icon_state="yellow",pixel_x = rand(-64,32), pixel_y = rand(-64,32), dir = pick(NORTH, SOUTH, EAST, WEST))
|
||||
else
|
||||
// Create an overlay that will inherit the direction
|
||||
I = image(icon='_flat_demoIcons.dmi',icon_state="yellow",pixel_x = rand(-64,32), pixel_y = rand(-64,32))
|
||||
|
||||
overlays += I
|
||||
|
||||
Stress_Test()
|
||||
set name = "7. Stress Test"
|
||||
for(var/i = 0 to 1000)
|
||||
// Passing '2' to the third parameter forces it to generate a new icon, even if it's already cached
|
||||
getFlatIcon(src,0,2)
|
||||
if(prob(5))
|
||||
Add_Overlay()
|
||||
Browse_Icon()
|
||||
|
||||
Cache_Test()
|
||||
set name = "8. Cache Test"
|
||||
for(var/i = 0 to 1000)
|
||||
getFlatIcon(src)
|
||||
Browse_Icon()
|
||||
|
||||
obj/overlayTest
|
||||
icon = '_flat_demoIcons.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
|
||||
330
code/libs/Get Flat Icon/demo/demoInterface.dmf
Normal file
330
code/libs/Get Flat Icon/demo/demoInterface.dmf
Normal file
@@ -0,0 +1,330 @@
|
||||
macro "macro"
|
||||
elem
|
||||
name = "North+REP"
|
||||
command = ".north"
|
||||
is-disabled = false
|
||||
elem
|
||||
name = "South+REP"
|
||||
command = ".south"
|
||||
is-disabled = false
|
||||
elem
|
||||
name = "East+REP"
|
||||
command = ".east"
|
||||
is-disabled = false
|
||||
elem
|
||||
name = "West+REP"
|
||||
command = ".west"
|
||||
is-disabled = false
|
||||
elem
|
||||
name = "Northeast+REP"
|
||||
command = ".northeast"
|
||||
is-disabled = false
|
||||
elem
|
||||
name = "Northwest+REP"
|
||||
command = ".northwest"
|
||||
is-disabled = false
|
||||
elem
|
||||
name = "Southeast+REP"
|
||||
command = ".southeast"
|
||||
is-disabled = false
|
||||
elem
|
||||
name = "Southwest+REP"
|
||||
command = ".southwest"
|
||||
is-disabled = false
|
||||
elem
|
||||
name = "Center+REP"
|
||||
command = ".center"
|
||||
is-disabled = false
|
||||
|
||||
|
||||
menu "menu"
|
||||
elem
|
||||
name = "&Quit"
|
||||
command = ".quit"
|
||||
category = "&File"
|
||||
is-checked = false
|
||||
can-check = false
|
||||
group = ""
|
||||
is-disabled = false
|
||||
saved-params = "is-checked"
|
||||
|
||||
|
||||
window "default"
|
||||
elem "default"
|
||||
type = MAIN
|
||||
pos = 326,59
|
||||
size = 712x552
|
||||
anchor1 = none
|
||||
anchor2 = none
|
||||
font-family = ""
|
||||
font-size = 0
|
||||
font-style = ""
|
||||
text-color = #000000
|
||||
background-color = none
|
||||
is-visible = true
|
||||
is-disabled = false
|
||||
is-transparent = false
|
||||
is-default = true
|
||||
border = none
|
||||
drop-zone = false
|
||||
right-click = false
|
||||
saved-params = "pos;size;is-minimized;is-maximized"
|
||||
on-size = ""
|
||||
title = ""
|
||||
titlebar = true
|
||||
statusbar = true
|
||||
can-close = true
|
||||
can-minimize = true
|
||||
can-resize = false
|
||||
is-pane = false
|
||||
is-minimized = false
|
||||
is-maximized = false
|
||||
can-scroll = none
|
||||
icon = ""
|
||||
image = ""
|
||||
image-mode = stretch
|
||||
keep-aspect = false
|
||||
transparent-color = none
|
||||
alpha = 255
|
||||
macro = "macro"
|
||||
menu = "menu"
|
||||
on-close = ""
|
||||
elem "label4"
|
||||
type = LABEL
|
||||
pos = 200,276
|
||||
size = 60x20
|
||||
anchor1 = none
|
||||
anchor2 = none
|
||||
font-family = ""
|
||||
font-size = 0
|
||||
font-style = ""
|
||||
text-color = #000000
|
||||
background-color = none
|
||||
is-visible = true
|
||||
is-disabled = false
|
||||
is-transparent = false
|
||||
is-default = false
|
||||
border = none
|
||||
drop-zone = false
|
||||
right-click = false
|
||||
saved-params = ""
|
||||
on-size = ""
|
||||
text = "Output:"
|
||||
image = ""
|
||||
image-mode = center
|
||||
keep-aspect = false
|
||||
align = left
|
||||
text-wrap = false
|
||||
elem "label3"
|
||||
type = LABEL
|
||||
pos = 456,4
|
||||
size = 60x20
|
||||
anchor1 = none
|
||||
anchor2 = none
|
||||
font-family = ""
|
||||
font-size = 0
|
||||
font-style = ""
|
||||
text-color = #000000
|
||||
background-color = none
|
||||
is-visible = true
|
||||
is-disabled = false
|
||||
is-transparent = false
|
||||
is-default = false
|
||||
border = none
|
||||
drop-zone = false
|
||||
right-click = false
|
||||
saved-params = ""
|
||||
on-size = ""
|
||||
text = "Browser:"
|
||||
image = ""
|
||||
image-mode = center
|
||||
keep-aspect = false
|
||||
align = left
|
||||
text-wrap = false
|
||||
elem "label2"
|
||||
type = LABEL
|
||||
pos = 456,276
|
||||
size = 60x20
|
||||
anchor1 = none
|
||||
anchor2 = none
|
||||
font-family = ""
|
||||
font-size = 0
|
||||
font-style = ""
|
||||
text-color = #000000
|
||||
background-color = none
|
||||
is-visible = true
|
||||
is-disabled = false
|
||||
is-transparent = false
|
||||
is-default = false
|
||||
border = none
|
||||
drop-zone = false
|
||||
right-click = false
|
||||
saved-params = ""
|
||||
on-size = ""
|
||||
text = "Label:"
|
||||
image = ""
|
||||
image-mode = center
|
||||
keep-aspect = false
|
||||
align = left
|
||||
text-wrap = false
|
||||
elem "label1"
|
||||
type = LABEL
|
||||
pos = 200,4
|
||||
size = 60x20
|
||||
anchor1 = none
|
||||
anchor2 = none
|
||||
font-family = ""
|
||||
font-size = 0
|
||||
font-style = ""
|
||||
text-color = #000000
|
||||
background-color = none
|
||||
is-visible = true
|
||||
is-disabled = false
|
||||
is-transparent = false
|
||||
is-default = false
|
||||
border = none
|
||||
drop-zone = false
|
||||
right-click = false
|
||||
saved-params = ""
|
||||
on-size = ""
|
||||
text = "Map:"
|
||||
image = ""
|
||||
image-mode = center
|
||||
keep-aspect = false
|
||||
align = left
|
||||
text-wrap = false
|
||||
elem "output1"
|
||||
type = OUTPUT
|
||||
pos = 200,296
|
||||
size = 248x248
|
||||
anchor1 = none
|
||||
anchor2 = none
|
||||
font-family = ""
|
||||
font-size = 0
|
||||
font-style = ""
|
||||
text-color = #ffffff
|
||||
background-color = #000000
|
||||
is-visible = true
|
||||
is-disabled = false
|
||||
is-transparent = false
|
||||
is-default = true
|
||||
border = none
|
||||
drop-zone = false
|
||||
right-click = false
|
||||
saved-params = "max-lines"
|
||||
on-size = ""
|
||||
link-color = #0000ff
|
||||
visited-color = #ff00ff
|
||||
style = ""
|
||||
enable-http-images = false
|
||||
max-lines = 1000
|
||||
image = ""
|
||||
elem "browser1"
|
||||
type = BROWSER
|
||||
pos = 456,24
|
||||
size = 248x248
|
||||
anchor1 = none
|
||||
anchor2 = none
|
||||
font-family = ""
|
||||
font-size = 0
|
||||
font-style = ""
|
||||
text-color = #000000
|
||||
background-color = #000000
|
||||
is-visible = true
|
||||
is-disabled = false
|
||||
is-transparent = false
|
||||
is-default = true
|
||||
border = none
|
||||
drop-zone = false
|
||||
right-click = false
|
||||
saved-params = ""
|
||||
on-size = ""
|
||||
show-history = false
|
||||
show-url = false
|
||||
auto-format = true
|
||||
use-title = false
|
||||
on-show = ""
|
||||
on-hide = ""
|
||||
elem "info1"
|
||||
type = INFO
|
||||
pos = 8,8
|
||||
size = 180x536
|
||||
anchor1 = none
|
||||
anchor2 = none
|
||||
font-family = ""
|
||||
font-size = 0
|
||||
font-style = ""
|
||||
text-color = #000000
|
||||
background-color = #ffffff
|
||||
is-visible = true
|
||||
is-disabled = false
|
||||
is-transparent = false
|
||||
is-default = false
|
||||
border = none
|
||||
drop-zone = true
|
||||
right-click = false
|
||||
saved-params = ""
|
||||
on-size = ""
|
||||
highlight-color = #00ff00
|
||||
tab-text-color = #000000
|
||||
tab-background-color = none
|
||||
tab-font-family = ""
|
||||
tab-font-size = 0
|
||||
tab-font-style = ""
|
||||
allow-html = true
|
||||
multi-line = true
|
||||
on-show = ""
|
||||
on-hide = ""
|
||||
on-tab = ""
|
||||
elem "imageLabel"
|
||||
type = LABEL
|
||||
pos = 456,296
|
||||
size = 248x248
|
||||
anchor1 = none
|
||||
anchor2 = none
|
||||
font-family = ""
|
||||
font-size = 0
|
||||
font-style = ""
|
||||
text-color = #000000
|
||||
background-color = #000000
|
||||
is-visible = true
|
||||
is-disabled = false
|
||||
is-transparent = false
|
||||
is-default = false
|
||||
border = none
|
||||
drop-zone = false
|
||||
right-click = false
|
||||
saved-params = ""
|
||||
on-size = ""
|
||||
text = ""
|
||||
image = ""
|
||||
image-mode = center
|
||||
keep-aspect = false
|
||||
align = center
|
||||
text-wrap = false
|
||||
elem "map1"
|
||||
type = MAP
|
||||
pos = 200,24
|
||||
size = 248x248
|
||||
anchor1 = none
|
||||
anchor2 = none
|
||||
font-family = ""
|
||||
font-size = 0
|
||||
font-style = ""
|
||||
text-color = #000000
|
||||
background-color = none
|
||||
is-visible = true
|
||||
is-disabled = false
|
||||
is-transparent = false
|
||||
is-default = false
|
||||
border = none
|
||||
drop-zone = true
|
||||
right-click = false
|
||||
saved-params = "icon-size"
|
||||
on-size = ""
|
||||
icon-size = 32
|
||||
text-mode = false
|
||||
on-show = ""
|
||||
on-hide = ""
|
||||
style = ""
|
||||
|
||||
413
code/libs/IconProcs/IconProcs.dm
Normal file
413
code/libs/IconProcs/IconProcs.dm
Normal file
@@ -0,0 +1,413 @@
|
||||
/*
|
||||
IconProcs
|
||||
by Lummox JR
|
||||
version 1.0
|
||||
|
||||
A library for manipulating icons and colors in BYOND
|
||||
|
||||
See documentation in IconProcs.html
|
||||
*/
|
||||
|
||||
#define TO_HEX_DIGIT(n) ascii2text((n&15) + ((n&15)<10 ? 48 : 87))
|
||||
|
||||
icon
|
||||
// Multiply all alpha values by this float
|
||||
proc/ChangeOpacity(opacity = 1.0)
|
||||
MapColors(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,opacity, 0,0,0,0)
|
||||
|
||||
// Convert to grayscale
|
||||
proc/GrayScale()
|
||||
MapColors(0.3,0.3,0.3, 0.59,0.59,0.59, 0.11,0.11,0.11, 0,0,0)
|
||||
|
||||
proc/ColorTone(tone)
|
||||
GrayScale()
|
||||
|
||||
var/list/TONE = ReadRGB(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
|
||||
|
||||
if(gray)
|
||||
MapColors(255/gray,0,0, 0,255/gray,0, 0,0,255/gray, 0,0,0)
|
||||
Blend(tone, ICON_MULTIPLY)
|
||||
else SetIntensity(0)
|
||||
if(255-gray)
|
||||
upper.Blend(rgb(gray,gray,gray), ICON_SUBTRACT)
|
||||
upper.MapColors((255-TONE[1])/(255-gray),0,0,0, 0,(255-TONE[2])/(255-gray),0,0, 0,0,(255-TONE[3])/(255-gray),0, 0,0,0,0, 0,0,0,1)
|
||||
Blend(upper, ICON_ADD)
|
||||
|
||||
// Take the minimum color of two icons; combine transparency as if blending with ICON_ADD
|
||||
proc/MinColors(icon)
|
||||
var/icon/I = new(src)
|
||||
I.Opaque()
|
||||
I.Blend(icon, ICON_SUBTRACT)
|
||||
Blend(I, ICON_SUBTRACT)
|
||||
|
||||
// Take the maximum color of two icons; combine opacity as if blending with ICON_OR
|
||||
proc/MaxColors(icon)
|
||||
var/icon/I
|
||||
if(isicon(icon))
|
||||
I = 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)
|
||||
|
||||
// make this icon fully opaque--transparent pixels become black
|
||||
proc/Opaque(background = "#000000")
|
||||
SwapColor(null, background)
|
||||
MapColors(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,0, 0,0,0,1)
|
||||
|
||||
// Change a grayscale icon into a white icon where the original color becomes the alpha
|
||||
// I.e., black -> transparent, gray -> translucent white, white -> solid white
|
||||
proc/BecomeAlphaMask()
|
||||
SwapColor(null, "#000000ff") // don't let transparent become gray
|
||||
MapColors(0,0,0,0.3, 0,0,0,0.59, 0,0,0,0.11, 0,0,0,0, 1,1,1,0)
|
||||
|
||||
proc/UseAlphaMask(mask)
|
||||
Opaque()
|
||||
AddAlphaMask(mask)
|
||||
|
||||
proc/AddAlphaMask(mask)
|
||||
var/icon/M = new(mask)
|
||||
M.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) 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)
|
||||
|
||||
/*
|
||||
Smooth blend between RGB 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/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)
|
||||
|
||||
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))
|
||||
20
code/libs/IconProcs/IconProcs.dme
Normal file
20
code/libs/IconProcs/IconProcs.dme
Normal file
@@ -0,0 +1,20 @@
|
||||
// DM Environment file for IconProcs.dme.
|
||||
// All manual changes should be made outside the BEGIN_ and END_ blocks.
|
||||
// New source code should be placed in .dm files: choose File/New --> Code File.
|
||||
|
||||
// BEGIN_INTERNALS
|
||||
/*
|
||||
FILE: IconProcs.html
|
||||
*/
|
||||
// END_INTERNALS
|
||||
// BEGIN_FILE_DIR
|
||||
#define FILE_DIR .
|
||||
// END_FILE_DIR
|
||||
|
||||
// BEGIN_PREFERENCES
|
||||
// END_PREFERENCES
|
||||
|
||||
// BEGIN_INCLUDE
|
||||
#include "IconProcs.dm"
|
||||
// END_INCLUDE
|
||||
|
||||
196
code/libs/IconProcs/IconProcs.html
Normal file
196
code/libs/IconProcs/IconProcs.html
Normal file
@@ -0,0 +1,196 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>IconProcs -- a BYOND library</title>
|
||||
<style>
|
||||
body {font: 11pt 'Trebuchet MS',Tahoma,Verdana,Arial,Helvetica,sans-serif;}
|
||||
h1 {margin-bottom: 0; text-align: center;}
|
||||
p.subtitle {margin: 0; text-align: center;}
|
||||
p.author {margin-top: 0; text-align: center; font-style: italic;}
|
||||
p.version {text-align: center; font-weight: bold;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>IconProcs</h1>
|
||||
<p class="subtitle">A BYOND library for manipulating icons and colors</p>
|
||||
<p class="author">by Lummox JR</p>
|
||||
<p class="version">version 1.0</p>
|
||||
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
|
||||
|
||||
<h2>Changing Icons</h2>
|
||||
|
||||
<p>Several new procs have been added to the <tt>/icon</tt> datum to simplify
|
||||
working with icons. To use them, remember you first need to setup an
|
||||
<tt>/icon</tt> var like so:</p>
|
||||
|
||||
<pre>var/icon/my_icon = new('iconfile.dmi')</pre>
|
||||
|
||||
<dl>
|
||||
|
||||
<dt><tt>icon/ChangeOpacity(amount = 1)</tt></dt>
|
||||
<dd>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 <tt>MapColors()</tt>
|
||||
which can change opacity any way you like, in much the same way that
|
||||
<tt>SetIntensity()</tt> can make an icon lighter or darker. If <tt>amount</tt>
|
||||
is 0.5, the opacity of the icon will be cut in half. If <tt>amount</tt> is 2,
|
||||
opacity is doubled and anything more than half-opaque will become fully
|
||||
opaque.</dd>
|
||||
|
||||
<dt><tt>icon/GrayScale()</tt></dt>
|
||||
<dd>Converts the icon to grayscale instead of a fully colored icon. Alpha
|
||||
values are left intact.</dd>
|
||||
|
||||
<dt><tt>icon/ColorTone(tone)</tt></dt>
|
||||
<dd>Similar to <tt>GrayScale()</tt>, this proc converts the icon to a range of
|
||||
black -> <tt>tone</tt> -> white, where <tt>tone</tt> is an RGB color (its
|
||||
alpha is ignored). This can be used to create a sepia tone or similar effects.
|
||||
See also the global <tt>ColorTone()</tt> proc.</dd>
|
||||
|
||||
<dt><tt>icon/MinColors(icon)</tt></dt>
|
||||
<dd>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
|
||||
<tt>ICON_ADD</tt>. You may supply a color in place of an icon.</dd>
|
||||
|
||||
<dt><tt>icon/MaxColors(icon)</tt></dt>
|
||||
<dd>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
|
||||
<tt>ICON_OR</tt>. You may supply a color in place of an icon.</dd>
|
||||
|
||||
<dt><tt>icon/Opaque(background = "#000000")</tt></dt>
|
||||
<dd>All alpha values are set to 255 throughout the icon. Transparent pixels
|
||||
become black, or whatever background color you specify.</dd>
|
||||
|
||||
<dt><tt>icon/BecomeAlphaMask()</tt></dt>
|
||||
<dd>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.</dd>
|
||||
|
||||
<dt><tt>icon/AddAlphaMask(mask)</tt></dt>
|
||||
<dd>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.</dd>
|
||||
|
||||
<dt><tt>icon/UseAlphaMask(mask, mode)</tt></dt>
|
||||
<dd>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 <tt>src</tt> will change so it has the same colors
|
||||
as before but uses the mask for opacity.</dd>
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
<h2>Color management and HSV</h2>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<ul>
|
||||
<li>The <b>hue</b> 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.</li>
|
||||
<li>The <b>saturation</b> 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 <i>is</i> a shade of gray.</li>
|
||||
<li>The <b>value</b> 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.</li>
|
||||
</ul>
|
||||
|
||||
<p>Just as BYOND uses <tt>"#rrggbb"</tt> to represent RGB values, a similar
|
||||
format is used for HSV: <tt>"#hhhssvv"</tt>. The hue is three hex digits
|
||||
because it ranges from 0 to 0x5FF.</p>
|
||||
|
||||
<ul>
|
||||
<li>0 to 0xFF - red to yellow</li>
|
||||
<li>0x100 to 0x1FF - yellow to green</li>
|
||||
<li>0x200 to 0x2FF - green to cyan</li>
|
||||
<li>0x300 to 0x3FF - cyan to blue</li>
|
||||
<li>0x400 to 0x4FF - blue to magenta</li>
|
||||
<li>0x500 to 0x5FF - magenta to red</li>
|
||||
</ul>
|
||||
|
||||
<p>Knowing this, you can figure out that red is <tt>"#000ffff"</tt> in HSV
|
||||
format, which is hue 0 (red), saturation 255 (as colorful as possible), value
|
||||
255 (as bright as possible). Green is <tt>"#200ffff"</tt> and blue is
|
||||
<tt>"#400ffff"</tt>.</p>
|
||||
|
||||
<p>More than one HSV color can match the same RGB color.</p>
|
||||
|
||||
<p>Here are some procs you can use for color management:</p>
|
||||
|
||||
|
||||
<dl>
|
||||
|
||||
<dt><tt>ReadRGB(rgb)</tt></dt>
|
||||
<dd>Takes an RGB string like <tt>"#ffaa55"</tt> and converts it to a list such
|
||||
as <tt>list(255,170,85)</tt>. If an RGBA format is used that includes alpha,
|
||||
the list will have a fourth item for the alpha value.</dd>
|
||||
|
||||
<dt><tt>hsv(hue, sat, val, apha)</tt></dt>
|
||||
<dd>Counterpart to <tt>rgb()</tt>, this takes the values you input and converts
|
||||
them to a string in <tt>"#hhhssvv"</tt> or <tt>"#hhhssvvaa"</tt> format. Alpha
|
||||
is not included in the result if null.</dd>
|
||||
|
||||
<dt><tt>ReadHSV(rgb)</tt></dt>
|
||||
<dd>Takes an HSV string like <tt>"#100FF80"</tt> and converts it to a list such
|
||||
as <tt>list(256,255,128)</tt>. If an HSVA format is used that includes alpha,
|
||||
the list will have a fourth item for the alpha value.</dd>
|
||||
|
||||
<dt><tt>RGBtoHSV(rgb)</tt></dt>
|
||||
<dd>Takes an RGB or RGBA string like <tt>"#ffaa55"</tt> and converts it into an
|
||||
HSV or HSVA color such as <tt>"#080aaff"</tt>.</dd>
|
||||
|
||||
<dt><tt>HSVtoRGB(hsv)</tt></dt>
|
||||
<dd>Takes an HSV or HSVA string like <tt>"#080aaff"</tt> and converts it into
|
||||
an RGB or RGBA color such as <tt>"#ff55aa"</tt>.</dd>
|
||||
|
||||
<dt><tt>BlendRGB(rgb1, rgb2, amount)</tt></dt>
|
||||
<dd>Blends between two RGB or RGBA colors using regular RGB blending. If
|
||||
<tt>amount</tt> 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.</dd>
|
||||
|
||||
<dt><tt>BlendHSV(hsv1, hsv2, amount)</tt></dt>
|
||||
<dd>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 <tt>amount</tt> 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.</dd>
|
||||
|
||||
<dt><tt>BlendRGBasHSV(rgb1, rgb2, amount)</tt></dt>
|
||||
<dd>Like <tt>BlendHSV()</tt>, but the colors used and the return value are RGB
|
||||
or RGBA colors. The blending is done in HSV form.</dd>
|
||||
|
||||
<dt><tt>HueToAngle(hue)</tt></dt>
|
||||
<dd>Converts a hue to an angle range of 0 to 360. Angle 0 is red, 120 is green,
|
||||
and 240 is blue.</dd>
|
||||
|
||||
<dt><tt>AngleToHue(hue)</tt></dt>
|
||||
<dd>Converts an angle to a hue in the valid range.</dd>
|
||||
|
||||
<dt><tt>RotateHue(hsv, angle)</tt></dt>
|
||||
<dd>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.</dd>
|
||||
|
||||
<dt><tt>GrayScale(rgb)</tt></dt>
|
||||
<dd>Takes an RGB or RGBA color and converts it to grayscale. Returns an RGB or
|
||||
RGBA string.</dd>
|
||||
|
||||
<dt><tt>ColorTone(rgb, tone)</tt></dt>
|
||||
<dd>Similar to <tt>GrayScale()</tt>, this proc converts an RGB or RGBA color to
|
||||
a range of black -> <tt>tone</tt> -> white instead of using strict shades
|
||||
of gray. The <tt>tone</tt> value is an RGB color; any alpha value is
|
||||
ignored.</dd>
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user