1
0
Fork 0
mirror of https://github.com/Orama-Interactive/Pixelorama.git synced 2025-02-13 17:23:08 +00:00
Pixelorama/src/Classes/Layers/GroupLayer.gd
Emmanouil Papadeas 2d28136449
Implement indexed mode (#1136)
* Create a custom PixeloramaImage class, initial support for indexed mode

* Convert opened projects and images to indexed mode

* Use shaders for RGB to Indexed conversion and vice versa

* Add `is_indexed` variable in PixeloramaImage

* Basic undo/redo support for indexed mode when drawing

* Make image effects respect indexed mode

* Move code from image effects to ShaderImageEffect instead

* Bucket tool works with indexed mode

* Move and selection tools works with indexed mode

* Brushes respect indexed mode

* Add color_mode variable and some helper methods in Project

Replace hard-coded cases of Image.FORMAT_RGBA8 with `Project.get_image_format()` just in case we want to add more formats in the future

* Add a helper new_empty_image() method to Project

* Set new images to indexed if the project is indexed

* Change color modes from the Image menu

* Fix open image to replace cel

* Load/save indices in pxo files

* Merging layers works with indexed mode

* Layer effects respect indexed mode

* Add an `other_image` parameter to `PixeloramaImage.add_data_to_dictionary()`

* Scale image works with indexed mode

* Resizing works with indexed mode

* Fix non-shader rotation not working with indexed mode

* Minor refactor of PixeloramaImage's set_pixelv_custom()

* Make the text tool work with indexed mode

* Remove print from PixeloramaImage

* Rename "PixeloramaImage" to "ImageExtended"

* Add docstrings in ImageExtended

* Set color mode from the create new image dialog

* Update Translations.pot

* Show the color mode in the project properties dialog
2024-11-20 14:41:37 +02:00

170 lines
4.7 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
blend_mode = BlendModes.PASS_THROUGH
## 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 := ImageExtended.create_custom(
project.size.x, project.size.y, false, project.get_image_format(), project.is_indexed()
)
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_RGF)
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: ImageExtended,
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: ImageExtended,
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