1
0
Fork 0
mirror of https://github.com/Orama-Interactive/Pixelorama.git synced 2025-02-18 19:43:14 +00:00
Pixelorama/src/Classes/Layers/GroupLayer.gd
Emmanouil Papadeas 077c57c53a
Implement support for group layer blending (#1077)
* Blend group layers on `DrawingAlgos.blend_layers()`

* Support group layer blending on the canvas

* Allow editing of group layer properties

* Fix issues with group layer blending in canvas, and unite common code

* Group layers can now be used as clipping masks

* Make move tool preview work on child layers

* Change OffsetImage's `blend_layers()` to support group layer blending

* Support group layer blending in the canvas preview

* Fix layer blending mode, clipping mask opacity and cel opacity not being updated automatically if the layer/cel changed is not selected

* Add a pass through blending mode to layer groups

Fingers crossed that no bugs were introduced

* Fix issue with layers that belong to pass through groups not updating their textures on the canvas automatically on undo
2024-08-15 15:52:55 +03:00

166 lines
4.6 KiB
GDScript

class_name GroupLayer
extends BaseLayer
## A class for group layer properties
var expanded := true
func _init(_project: Project, _name := "") -> void:
project = _project
name = _name
## Blends all of the images of children layer of the group layer into a single image.
func blend_children(frame: Frame, origin := Vector2i.ZERO, apply_effects := true) -> Image:
var image := Image.create(project.size.x, project.size.y, false, Image.FORMAT_RGBA8)
var children := get_children(false)
if children.size() <= 0:
return image
var textures: Array[Image] = []
var metadata_image := Image.create(children.size(), 4, false, Image.FORMAT_RG8)
var current_child_index := 0
for i in children.size():
var layer := children[i]
if not layer.is_visible_in_hierarchy():
current_child_index += 1
continue
if layer is GroupLayer:
current_child_index = _blend_child_group(
image,
layer,
frame,
textures,
metadata_image,
current_child_index,
origin,
apply_effects
)
else:
_include_child_in_blending(
image,
layer,
frame,
textures,
metadata_image,
current_child_index,
origin,
apply_effects
)
current_child_index += 1
if DisplayServer.get_name() != "headless" and textures.size() > 0:
var texture_array := Texture2DArray.new()
texture_array.create_from_images(textures)
var params := {
"layers": texture_array,
"metadata": ImageTexture.create_from_image(metadata_image),
"origin_x_positive": origin.x > 0,
"origin_y_positive": origin.y > 0,
}
var gen := ShaderImageEffect.new()
gen.generate_image(image, DrawingAlgos.blend_layers_shader, params, project.size)
if apply_effects:
image = display_effects(frame.cels[index], image)
return image
func _include_child_in_blending(
image: Image,
layer: BaseLayer,
frame: Frame,
textures: Array[Image],
metadata_image: Image,
i: int,
origin: Vector2i,
apply_effects: bool
) -> void:
var cel := frame.cels[layer.index]
if DisplayServer.get_name() == "headless":
DrawingAlgos.blend_layers_headless(image, project, layer, cel, origin)
else:
var cel_image: Image
if apply_effects:
cel_image = layer.display_effects(cel)
else:
cel_image = cel.get_image()
textures.append(cel_image)
DrawingAlgos.set_layer_metadata_image(layer, cel, metadata_image, i)
if origin != Vector2i.ZERO:
# Only used as a preview for the move tool, when used on a group's children
var test_array := [project.frames.find(frame), project.layers.find(layer)]
if test_array in project.selected_cels:
var origin_fixed := Vector2(origin).abs() / Vector2(cel_image.get_size())
metadata_image.set_pixel(i, 2, Color(origin_fixed.x, origin_fixed.y, 0.0, 0.0))
## Include a child group in the blending process.
## If the child group is set to pass through mode, loop through its children
## and include them as separate images, instead of blending them all together.
## Gets called recursively if the child group has children groups of its own,
## and they are also set to pass through mode.
func _blend_child_group(
image: Image,
layer: BaseLayer,
frame: Frame,
textures: Array[Image],
metadata_image: Image,
i: int,
origin: Vector2i,
apply_effects: bool
) -> int:
var new_i := i
var blend_rect := Rect2i(Vector2i.ZERO, project.size)
var cel := frame.cels[layer.index]
if layer.blend_mode == BlendModes.PASS_THROUGH:
var children := layer.get_children(false)
for j in children.size():
var child := children[j]
if child is GroupLayer:
new_i = _blend_child_group(
image, child, frame, textures, metadata_image, i + j, origin, apply_effects
)
else:
new_i += j
metadata_image.crop(metadata_image.get_width() + 1, metadata_image.get_height())
_include_child_in_blending(
image, child, frame, textures, metadata_image, new_i, origin, apply_effects
)
else:
var blended_children := (layer as GroupLayer).blend_children(frame, origin)
if DisplayServer.get_name() == "headless":
image.blend_rect(blended_children, blend_rect, origin)
else:
textures.append(blended_children)
DrawingAlgos.set_layer_metadata_image(layer, cel, metadata_image, i)
return new_i
# Overridden Methods:
func serialize() -> Dictionary:
var data := super.serialize()
data["type"] = get_layer_type()
data["expanded"] = expanded
return data
func deserialize(dict: Dictionary) -> void:
super.deserialize(dict)
expanded = dict.expanded
func get_layer_type() -> int:
return Global.LayerTypes.GROUP
func new_empty_cel() -> BaseCel:
return GroupCel.new()
func set_name_to_default(number: int) -> void:
name = tr("Group") + " %s" % number
func accepts_child(_layer: BaseLayer) -> bool:
return true