// Creates a single icon from a given /atom or /image. Only the first argument is required. /proc/getFlatIcon(image/A, defdir, deficon, defstate, defblend, start = TRUE, no_anim = FALSE) //Define... defines. var/icon/flat_template = icon('icons/misc/flatBlank.dmi') #define BLANK icon(flat_template) #define SET_SELF(SETVAR) var/icon/SELF_ICON=icon(icon(curicon, curstate, base_icon_dir),"",SOUTH,no_anim?1:null);if(A.alpha<255)SELF_ICON.Blend(rgb(255,255,255,A.alpha),ICON_MULTIPLY);if(A.color)SELF_ICON.Blend(A.color,ICON_MULTIPLY);;##SETVAR=SELF_ICON; #define INDEX_X_LOW 1 #define INDEX_X_HIGH 2 #define INDEX_Y_LOW 3 #define INDEX_Y_HIGH 4 #define flatX1 flat_size[INDEX_X_LOW] #define flatX2 flat_size[INDEX_X_HIGH] #define flatY1 flat_size[INDEX_Y_LOW] #define flatY2 flat_size[INDEX_Y_HIGH] #define addX1 add_size[INDEX_X_LOW] #define addX2 add_size[INDEX_X_HIGH] #define addY1 add_size[INDEX_Y_LOW] #define addY2 add_size[INDEX_Y_HIGH] if(!A || A.alpha <= 0) return BLANK var/noIcon = FALSE if(start) if(!defdir) defdir = A.dir if(!deficon) deficon = A.icon if(!defstate) defstate = A.icon_state if(!defblend) defblend = A.blend_mode var/curicon = A.icon || deficon var/curstate = A.icon_state || defstate if(!((noIcon = (!curicon)))) var/curstates = icon_states(curicon) if(!(curstate in curstates)) if("" in curstates) curstate = "" else noIcon = TRUE // Do not render this object. var/curdir var/base_icon_dir //We'll use this to get the icon state to display if not null BUT NOT pass it to overlays as the dir we have //These should use the parent's direction (most likely) if(!A.dir || A.dir == SOUTH) curdir = defdir else curdir = A.dir //Try to remove/optimize this section ASAP, CPU hog. //Determines if there's directionals. if(!noIcon && curdir != SOUTH) var/exist = FALSE var/static/list/checkdirs = list(NORTH, EAST, WEST) for(var/i in checkdirs) //Not using GLOB for a reason. if(length(icon_states(icon(curicon, curstate, i)))) exist = TRUE break if(!exist) base_icon_dir = SOUTH // if(!base_icon_dir) base_icon_dir = curdir ASSERT(!BLEND_DEFAULT) //I might just be stupid but lets make sure this define is 0. var/curblend = A.blend_mode || defblend if(A.overlays.len || A.underlays.len) var/icon/flat = BLANK // 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=base_icon_dir) copy.color = A.color copy.alpha = A.alpha copy.blend_mode = curblend layers[copy] = A.layer // Loop through the underlays, then overlays, sorting them into the layers list for(var/process_set in 0 to 1) var/list/process = process_set? A.overlays : A.underlays for(var/i in 1 to process.len) var/image/current = process[i] if(!current) continue if(current.plane != FLOAT_PLANE && current.plane != A.plane) continue var/current_layer = current.layer if(current_layer < 0) if(current_layer <= -1000) return flat current_layer = process_set + A.layer + current_layer / 1000 for(var/p in 1 to layers.len) var/image/cmp = layers[p] if(current_layer < layers[cmp]) layers.Insert(p, current) break layers[current] = current_layer //sortTim(layers, /proc/cmp_image_layer_asc) var/icon/add // Icon of overlay being added // Current dimensions of flattened icon var/list/flat_size = list(1, flat.Width(), 1, flat.Height()) // Dimensions of overlay being added var/list/add_size[4] for(var/V in layers) var/image/I = V 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, base_icon_dir) else // 'I' is an appearance object. add = getFlatIcon(image(I), curdir, curicon, curstate, curblend, FALSE, no_anim) if(!add) continue // Find the new dimensions of the flat icon to fit the added overlay add_size = list( min(flatX1, I.pixel_x+1), max(flatX2, I.pixel_x+add.Width()), min(flatY1, I.pixel_y+1), max(flatY2, I.pixel_y+add.Height()) ) if(flat_size ~! add_size) // Resize the flattened icon so the new icon fits flat.Crop( addX1 - flatX1 + 1, addY1 - flatY1 + 1, addX2 - flatX1 + 1, addY2 - flatY1 + 1 ) flat_size = add_size.Copy() // Blend the overlay into the flattened icon flat.Blend(add, blendMode2iconMode(curblend), I.pixel_x + 2 - flatX1, I.pixel_y + 2 - flatY1) if(A.color) flat.Blend(A.color, ICON_MULTIPLY) if(A.alpha < 255) flat.Blend(rgb(255, 255, 255, A.alpha), ICON_MULTIPLY) if(no_anim) //Clean up repeated frames var/icon/cleaned = new /icon() cleaned.Insert(flat, "", SOUTH, 1, 0) . = cleaned else . = icon(flat, "", SOUTH) else //There's no overlays. if(!noIcon) SET_SELF(.) //Clear defines #undef flatX1 #undef flatX2 #undef flatY1 #undef flatY2 #undef addX1 #undef addX2 #undef addY1 #undef addY2 #undef INDEX_X_LOW #undef INDEX_X_HIGH #undef INDEX_Y_LOW #undef INDEX_Y_HIGH #undef BLANK #undef SET_SELF //Converts a blend_mode constant to one acceptable to icon.Blend() /proc/blendMode2iconMode(blend_mode) switch(blend_mode) if(BLEND_MULTIPLY) return ICON_MULTIPLY if(BLEND_ADD) return ICON_ADD if(BLEND_SUBTRACT) return ICON_SUBTRACT else return ICON_OVERLAY