Skip to content

Commit

Permalink
Adds a color matrix layer to GAGS (tgstation#63957)
Browse files Browse the repository at this point in the history
* Adds a color matrix layer to GAGS

* Fixes default row value

* Passes along the last viable icon for color matrix use

* Removes stray nocache reference
  • Loading branch information
ninjanomnom authored Jan 13, 2022
1 parent dd5622a commit fdeca47
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 16 deletions.
14 changes: 7 additions & 7 deletions code/datums/greyscale/_greyscale_config.dm
Original file line number Diff line number Diff line change
Expand Up @@ -231,20 +231,20 @@
fcopy(icon_output, "tmp/gags_debug_output.dmi")

/// Actually create the icon and color it in, handles caching
/datum/greyscale_config/proc/Generate(color_string)
/datum/greyscale_config/proc/Generate(color_string, icon/last_external_icon)
var/key = color_string
var/icon/new_icon = icon_cache[key]
if(new_icon)
return icon(new_icon)

var/icon/icon_bundle = GenerateBundle(color_string)
var/icon/icon_bundle = GenerateBundle(color_string, last_external_icon=last_external_icon)
icon_bundle = fcopy_rsc(icon_bundle)
icon_cache[key] = icon_bundle
var/icon/output = icon(icon_bundle)
return output

/// Handles the actual icon manipulation to create the spritesheet
/datum/greyscale_config/proc/GenerateBundle(list/colors, list/render_steps)
/datum/greyscale_config/proc/GenerateBundle(list/colors, list/render_steps, icon/last_external_icon)
if(!istype(colors))
colors = SSgreyscale.ParseColorString(colors)
if(length(colors) != expected_colors)
Expand All @@ -255,7 +255,7 @@
var/list/icon_state_steps
if(render_steps)
icon_state_steps = render_steps[icon_state] = list()
var/icon/generated_icon = GenerateLayerGroup(colors, icon_states[icon_state], icon_state_steps)
var/icon/generated_icon = GenerateLayerGroup(colors, icon_states[icon_state], icon_state_steps, last_external_icon)
// We read a pixel to force the icon to be fully generated before we let it loose into the world
// I hate this
generated_icon.GetPixel(1, 1)
Expand All @@ -271,15 +271,15 @@
return icon_bundle

/// Internal recursive proc to handle nested layer groups
/datum/greyscale_config/proc/GenerateLayerGroup(list/colors, list/group, list/render_steps)
/datum/greyscale_config/proc/GenerateLayerGroup(list/colors, list/group, list/render_steps, icon/last_external_icon)
var/icon/new_icon
for(var/datum/greyscale_layer/layer as anything in group)
var/icon/layer_icon
if(islist(layer))
layer_icon = GenerateLayerGroup(colors, layer, render_steps)
layer_icon = GenerateLayerGroup(colors, layer, render_steps, new_icon || last_external_icon)
layer = layer[1] // When there are multiple layers in a group like this we use the first one's blend mode
else
layer_icon = layer.Generate(colors, render_steps)
layer_icon = layer.Generate(colors, render_steps, new_icon || last_external_icon)

if(!new_icon)
new_icon = layer_icon
Expand Down
21 changes: 21 additions & 0 deletions code/datums/greyscale/json_reader.dm
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,27 @@
new_values += new_value
return new_values

/datum/json_reader/color_matrix/ReadJson(list/value)
if(!istype(value))
CRASH("Expected a list but got [value]")
if(length(value) > 5 || length(value) < 4)
CRASH("Color matrix must contain 4 or 5 rows")
var/list/new_values = list()
for(var/list/row in value)
var/list/interpreted_row = list()
if(!istype(row) || length(row) != 4)
stack_trace("Expected list to contain further row lists with exactly 4 entries")
interpreted_row = list(0, 0, 0, 0)
continue
for(var/number in row)
if(!isnum(number))
stack_trace("Each color matrix row must only contain numbers")
interpreted_row += 0
else
interpreted_row += number
new_values += interpreted_row
return new_values

/datum/json_reader/blend_mode
var/static/list/blend_modes = list(
"add" = ICON_ADD,
Expand Down
33 changes: 24 additions & 9 deletions code/datums/greyscale/layer.dm
Original file line number Diff line number Diff line change
Expand Up @@ -66,18 +66,19 @@

/// Used to actualy create the layer using the given colors
/// Do not override, use InternalGenerate instead
/datum/greyscale_layer/proc/Generate(list/colors, list/render_steps)
/datum/greyscale_layer/proc/Generate(list/colors, list/render_steps, icon/new_icon)
var/list/processed_colors = list()
for(var/i in color_ids)
if(isnum(i))
processed_colors += colors[i]
else
processed_colors += i
return InternalGenerate(processed_colors, render_steps)
var/icon/copy_of_new_icon = icon(new_icon) // Layers shouldn't be modifying it directly, this is just for them to reference
return InternalGenerate(processed_colors, render_steps, copy_of_new_icon)

/// Override this to implement layers.
/// The colors var will only contain colors that this layer is configured to use.
/datum/greyscale_layer/proc/InternalGenerate(list/colors, list/render_steps)
/datum/greyscale_layer/proc/InternalGenerate(list/colors, list/render_steps, icon/new_icon)

////////////////////////////////////////////////////////
// Subtypes
Expand All @@ -103,11 +104,25 @@
. = ..()
required_values[NAMEOF(src, icon_state)] = /datum/json_reader/text

/datum/greyscale_layer/icon_state/InternalGenerate(list/colors, list/render_steps)
/datum/greyscale_layer/icon_state/InternalGenerate(list/colors, list/render_steps, icon/new_icon)
. = ..()
var/icon/new_icon = icon(icon)
var/icon/generated_icon = icon(icon)
if(length(colors))
new_icon.Blend(colors[1], ICON_MULTIPLY)
generated_icon.Blend(colors[1], ICON_MULTIPLY)
return generated_icon

/// A layer to modify the previous layer's colors with a color matrix
/datum/greyscale_layer/color_matrix
layer_type = "color_matrix"
var/list/color_matrix

/datum/greyscale_layer/color_matrix/GetExpectedValues(list/required_values, list/optional_values)
. = ..()
required_values[NAMEOF(src, color_matrix)] = /datum/json_reader/color_matrix

/datum/greyscale_layer/color_matrix/InternalGenerate(list/colors, list/render_steps, icon/new_icon)
. = ..()
new_icon.MapColors(arglist(color_matrix))
return new_icon

/// A layer created by using another greyscale icon's configuration
Expand All @@ -130,12 +145,12 @@
if(!reference_type.icon_states[icon_state])
CRASH("[src] expects icon_state '[icon_state]' but referenced configuration '[reference_type]' does not have it.")

/datum/greyscale_layer/reference/InternalGenerate(list/colors, list/render_steps)
/datum/greyscale_layer/reference/InternalGenerate(list/colors, list/render_steps, icon/new_icon)
var/icon/generated_icon
if(render_steps)
var/list/reference_data = list()
generated_icon = reference_type.GenerateBundle(colors, reference_data)
generated_icon = reference_type.GenerateBundle(colors, reference_data, new_icon)
render_steps += reference_data[icon_state]
else
generated_icon = reference_type.Generate(colors.Join())
generated_icon = reference_type.Generate(colors.Join(), new_icon)
return icon(generated_icon, icon_state)

0 comments on commit fdeca47

Please sign in to comment.