More tweaks (#2889)

changes:

Clipboard overlays are now a little gentler on SSoverlays.
Ported potential performance improvements for getFlatIcon and listclearnulls from /tg/.
Icon smoothing now clears the base icon by default; SMOOTH_NO_CLEAR_ICON disables this behavior.
Potential SSoverlays performance improvement from /tg/.
Speculative fix for #2826.
This commit is contained in:
Lohikar
2017-07-01 15:09:10 -05:00
committed by skull132
parent 1365befb04
commit f885bffdae
8 changed files with 139 additions and 160 deletions

View File

@@ -37,7 +37,7 @@
#define SMOOTH_DIAGONAL 4 //if atom should smooth diagonally, this should be present in 'smooth' var
#define SMOOTH_BORDER 8 //atom will smooth with the borders of the map
#define SMOOTH_QUEUED 16 //atom is currently queued to smooth.
#define SMOOTH_CLEAR_ICON 32 // clear the atom's icon_state on smooth. SMOOTH_DIAGONAL implies this.
#define SMOOTH_NO_CLEAR_ICON 32 // don't clear the atom's icon_state on smooth.
#define NULLTURF_BORDER 123456789
@@ -281,7 +281,7 @@
if(New)
A.add_overlay(New)
if (A.icon_state && (A.smooth & SMOOTH_CLEAR_ICON))
if (A.icon_state && !(A.smooth & SMOOTH_NO_CLEAR_ICON))
A.icon_state = null
/proc/find_type_in_direction(atom/source, direction)

View File

@@ -633,174 +633,149 @@ Get flat icon by DarkCampainger. As it says on the tin, will return an icon with
as a single icon. Useful for when you want to manipulate an icon via the above as overlays are not normally included.
*/
proc // Creates a single icon from a given /atom or /image. Only the first argument is required.
getFlatIcon(image/A, defdir=2, deficon=null, defstate="", defblend=BLEND_DEFAULT, always_use_defdir = 0)
// We start with a blank canvas, otherwise some icon procs crash silently
var/icon/flat = icon('icons/effects/effects.dmi', "icon_state"="nothing") // Final flattened icon
if(!A)
return flat
if(A.alpha <= 0)
return flat
var/noIcon = FALSE
/proc/getFlatIcon(image/A, defdir=2, deficon=null, defstate="", defblend=BLEND_DEFAULT, always_use_defdir = 0)
// We start with a blank canvas, otherwise some icon procs crash silently
var/icon/flat = icon('icons/effects/effects.dmi', "nothing") // Final flattened icon
if(!A)
return flat
if(A.alpha <= 0)
return flat
var/noIcon = FALSE
var/curicon
if(A.icon)
curicon = A.icon
var/curicon
if(A.icon)
curicon = A.icon
else
curicon = deficon
if(!curicon)
noIcon = TRUE // Do not render this object.
var/curstate
if(A.icon_state)
curstate = A.icon_state
else
curstate = defstate
if(!noIcon && !(curstate in icon_states(curicon)))
if("" in icon_states(curicon))
curstate = ""
else
curicon = deficon
if(!curicon)
noIcon = TRUE // Do not render this object.
var/curstate
if(A.icon_state)
curstate = A.icon_state
else
curstate = defstate
var/curdir
if(A.dir != 2 && !always_use_defdir)
curdir = A.dir
else
curdir = defdir
if(!noIcon && !(curstate in icon_states(curicon)))
if("" in icon_states(curicon))
curstate = ""
else
noIcon = TRUE // Do not render this object.
var/curblend
if(A.blend_mode == BLEND_DEFAULT)
curblend = defblend
else
curblend = A.blend_mode
var/curdir
if(A.dir != 2 && !always_use_defdir)
curdir = A.dir
else
curdir = defdir
// Layers will be a sorted list of icons/overlays, based on the order in which they are displayed
var/list/layers = list()
var/image/copy
// Add the atom's icon itself, without pixel_x/y offsets.
if(!noIcon)
copy = image(icon=curicon, icon_state=curstate, layer=A.layer, dir=curdir)
copy.color = A.color
copy.alpha = A.alpha
copy.blend_mode = curblend
layers[copy] = A.layer
var/curblend
if(A.blend_mode == BLEND_DEFAULT)
curblend = defblend
else
curblend = A.blend_mode
// Loop through the underlays, then overlays, sorting them into the layers list
var/list/process = A.underlays // Current list being processed
var/pSet=0 // Which list is being processed: 0 = underlays, 1 = overlays
var/curIndex=1 // index of 'current' in list being processed
var/current // Current overlay being sorted
var/currentLayer // Calculated layer that overlay appears on (special case for FLOAT_LAYER)
var/compare // The overlay 'add' is being compared against
var/cmpIndex // The index in the layers list of 'compare'
while(TRUE)
if(curIndex<=process.len)
current = process[curIndex]
if(!current)
curIndex++ //Try the next layer
continue
currentLayer = current:layer
if(currentLayer<0) // Special case for FLY_LAYER
if(currentLayer <= -1000) return flat
if(pSet == 0) // Underlay
currentLayer = A.layer+currentLayer/1000
else // Overlay
currentLayer = A.layer+(1000+currentLayer)/1000
// Layers will be a sorted list of icons/overlays, based on the order in which they are displayed
var/list/layers = list()
var/image/copy
// Add the atom's icon itself, without pixel_x/y offsets.
if(!noIcon)
copy = image(icon=curicon, icon_state=curstate, layer=A.layer, dir=curdir)
copy.color = A.color
copy.alpha = A.alpha
copy.blend_mode = curblend
layers[copy] = A.layer
// Sort add into layers list
for(cmpIndex=1,cmpIndex<=layers.len,cmpIndex++)
compare = layers[cmpIndex]
if(currentLayer < layers[compare]) // Associated value is the calculated layer
layers.Insert(cmpIndex,current)
layers[current] = currentLayer
break
if(cmpIndex>layers.len) // Reached end of list without inserting
layers[current]=currentLayer // Place at end
// Loop through the underlays, then overlays, sorting them into the layers list
var/list/process = A.underlays // Current list being processed
var/pSet=0 // Which list is being processed: 0 = underlays, 1 = overlays
var/curIndex=1 // index of 'current' in list being processed
var/current // Current overlay being sorted
var/currentLayer // Calculated layer that overlay appears on (special case for FLOAT_LAYER)
var/compare // The overlay 'add' is being compared against
var/cmpIndex // The index in the layers list of 'compare'
while(TRUE)
if(curIndex<=process.len)
current = process[curIndex]
if(current)
currentLayer = current:layer
if(currentLayer<0) // Special case for FLY_LAYER
if(currentLayer <= -1000) return flat
if(pSet == 0) // Underlay
currentLayer = A.layer+currentLayer/1000
else // Overlay
currentLayer = A.layer+(1000+currentLayer)/1000
curIndex++
// Sort add into layers list
for(cmpIndex=1,cmpIndex<=layers.len,cmpIndex++)
compare = layers[cmpIndex]
if(currentLayer < layers[compare]) // Associated value is the calculated layer
layers.Insert(cmpIndex,current)
layers[current] = currentLayer
break
if(cmpIndex>layers.len) // Reached end of list without inserting
layers[current]=currentLayer // Place at end
curIndex++
else if(pSet == 0) // Switch to overlays
if(curIndex>process.len)
if(pSet == 0) // Switch to overlays
curIndex = 1
pSet = 1
process = A.overlays
else // All done
break
var/icon/add // Icon of overlay being added
var/icon/add // Icon of overlay being added
// Current dimensions of flattened icon
var/{flatX1=1;flatX2=flat.Width();flatY1=1;flatY2=flat.Height()}
// Dimensions of overlay being added
var/{addX1;addX2;addY1;addY2}
// Current dimensions of flattened icon
var/{flatX1=1;flatX2=flat.Width();flatY1=1;flatY2=flat.Height()}
// Dimensions of overlay being added
var/{addX1;addX2;addY1;addY2}
for(var/I in layers)
for(var/I in layers)
if(I:alpha == 0)
continue
if(I:alpha == 0)
continue
if(I == copy) // 'I' is an /image based on the object being flattened.
curblend = BLEND_OVERLAY
add = icon(I:icon, I:icon_state, I:dir)
// This checks for a silent failure mode of the icon routine. If the requested dir
// doesn't exist in this icon state it returns a 32x32 icon with 0 alpha.
if (I:dir != SOUTH && add.Width() == 32 && add.Height() == 32)
// Check every pixel for blank (computationally expensive, but the process is limited
// by the amount of film on the station, only happens when we hit something that's
// turned, and bails at the very first pixel it sees.
var/blankpixel;
for(var/y;y<=32;y++)
for(var/x;x<32;x++)
blankpixel = isnull(add.GetPixel(x,y))
if(!blankpixel)
break
if(!blankpixel)
break
// If we ALWAYS returned a null (which happens when GetPixel encounters something with alpha 0)
if (blankpixel)
// Pull the default direction.
add = icon(I:icon, I:icon_state)
else // 'I' is an appearance object.
if(istype(A,/obj/machinery/atmospherics) && I in A.underlays)
var/image/Im = I
add = getFlatIcon(new/image(I), Im.dir, curicon, curstate, curblend, 1)
else
add = getFlatIcon(new/image(I), curdir, curicon, curstate, curblend, always_use_defdir)
if(I == copy) // 'I' is an /image based on the object being flattened.
curblend = BLEND_OVERLAY
add = icon(I:icon, I:icon_state, I:dir)
else // 'I' is an appearance object.
add = getFlatIcon(new/image(I), curdir, curicon, curstate, curblend)
// 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())
// 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
var/iconmode
if(I in A.overlays)
iconmode = ICON_OVERLAY
else if(I in A.underlays)
iconmode = ICON_UNDERLAY
else
iconmode = blendMode2iconMode(curblend)
// Blend the overlay into the flattened icon
flat.Blend(add, iconmode, I:pixel_x + 2 - flatX1, I:pixel_y + 2 - flatY1)
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
if(A.color)
flat.Blend(A.color, ICON_MULTIPLY)
if(A.alpha < 255)
flat.Blend(rgb(255, 255, 255, A.alpha), ICON_MULTIPLY)
// Blend the overlay into the flattened icon
flat.Blend(add, blendMode2iconMode(curblend), I:pixel_x + 2 - flatX1, I:pixel_y + 2 - flatY1)
return icon(flat, "", SOUTH)
if(A.color)
flat.Blend(A.color, ICON_MULTIPLY)
if(A.alpha < 255)
flat.Blend(rgb(255, 255, 255, A.alpha), ICON_MULTIPLY)
getIconMask(atom/A)//By yours truly. Creates a dynamic mask for a mob/whatever. /N
var/icon/alpha_mask = new(A.icon,A.icon_state)//So we want the default icon and icon state of A.
for(var/I in A.overlays)//For every image in overlays. var/image/I will not work, don't try it.
if(I:layer>A.layer) continue//If layer is greater than what we need, skip it.
var/icon/image_overlay = new(I:icon,I:icon_state)//Blend only works with icon objects.
//Also, icons cannot directly set icon_state. Slower than changing variables but whatever.
alpha_mask.Blend(image_overlay,ICON_OR)//OR so they are lumped together in a nice overlay.
return alpha_mask//And now return the mask.
return icon(flat, "", SOUTH)
/proc/getIconMask(atom/A)//By yours truly. Creates a dynamic mask for a mob/whatever. /N
var/icon/alpha_mask = new(A.icon,A.icon_state)//So we want the default icon and icon state of A.
for(var/I in A.overlays)//For every image in overlays. var/image/I will not work, don't try it.
if(I:layer>A.layer) continue//If layer is greater than what we need, skip it.
var/icon/image_overlay = new(I:icon,I:icon_state)//Blend only works with icon objects.
//Also, icons cannot directly set icon_state. Slower than changing variables but whatever.
alpha_mask.Blend(image_overlay,ICON_OR)//OR so they are lumped together in a nice overlay.
return alpha_mask//And now return the mask.
/mob/proc/AddCamoOverlay(atom/A)//A is the atom which we are using as the overlay.
var/icon/opacity_icon = new(A.icon, A.icon_state)//Don't really care for overlays/underlays.

View File

@@ -110,11 +110,12 @@ proc/clearlist(var/list/L)
L.Cut()
//Removes any null entries from the list
proc/listclearnulls(list/list)
if(istype(list))
while(null in list)
list -= null
return
//Returns TRUE if the list had nulls, FALSE otherwise
/proc/listclearnulls(list/L)
var/start_len = L.len
var/list/N = new(start_len)
L -= N
return L.len < start_len
/*
* Returns list containing all the entries from first list that are not present in second.