Skip to content

Commit

Permalink
Changed structure of .pxo files
Browse files Browse the repository at this point in the history
The structure of the .pxo files is now consisted of a JSON-structured metadata part, where all the data that can be stored as text are, and a binary part, that contain all the actual image data for each cel and project brush.

This makes it easier for users to understand the .pxo structure, easier to add more changes without having to check versions for backwards compatibility, easier to be opened by third-party apps and it allows us to make an "Export JSON metadata" option, that will export just the metadata in JSON format, without the binary image data.

It's backwards compatible and .pxo files from as far as v0.5 are still supported.
  • Loading branch information
OverloadedOrama committed Jun 10, 2020
1 parent 49b61db commit b0338ab
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 97 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
<br><br>

## [v0.8] - Unreleased
This update has been brought to you by the contributions of:
Darshan Phaldesai (luiq54)

### Added
- Project tabs! You can now have multiple projects open at the same time, and access each one with tabs.
- You can now draw on the tiling mode previews! ([#65](https://github.com/Orama-Interactive/Pixelorama/issues/65))

### Changed
- The .pxo file structure has been changed. It's now consisted of a JSON-structured metadata part, where all the data that can be stored as text are, and a binary part, that contain all the actual image data for each cel and project brush.
<br><br>

## [v0.7.1] - Unreleased
Expand Down
6 changes: 3 additions & 3 deletions src/Autoload/DrawingAlgos.gd
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ func draw_pixel_blended(sprite : Image, pos : Vector2, color : Color, pen_pressu
var x_max = Global.current_project.x_max
var y_min = Global.current_project.y_min
var y_max = Global.current_project.y_max

# #Check if Tiling is enabled and whether mouse is in TilingPreviews
if Global.tile_mode and point_in_rectangle(pos,Vector2( - Global.current_project.size.x - 1 , - Global.current_project.size.y -1 ), Vector2(2 * Global.current_project.size.x, 2 * Global.current_project.size.y)):
pos = pos.posmodv(Global.current_project.size)

if !point_in_rectangle(pos, Vector2(x_min - 1, y_min - 1), Vector2(x_max, y_max)):
return

Expand Down Expand Up @@ -107,7 +107,7 @@ func draw_brush(sprite : Image, pos : Vector2, color : Color, current_mouse_butt
# #Check if Tiling is enabled and whether mouse is in TilingPreviews
if Global.tile_mode and point_in_rectangle(pos,Vector2( - Global.current_project.size.x - 1 , - Global.current_project.size.y -1 ), Vector2(2 * Global.current_project.size.x, 2 * Global.current_project.size.y)):
pos = pos.posmodv(Global.current_project.size)

var dst := rectangle_center(pos, custom_brush_size)
var src_rect := Rect2(Vector2.ZERO, custom_brush_size + Vector2.ONE)
# Rectangle with the same size as the brush, but at cursor's position
Expand Down
149 changes: 62 additions & 87 deletions src/Autoload/OpenSave.gd
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,52 @@ func open_pxo_file(path : String, untitled_backup : bool = false) -> void:
else:
new_project = Project.new([], path.get_file())

var file_version := file.get_line() # Example, "v0.7.10-beta"
var first_line := file.get_line()
var dict := JSON.parse(first_line)
if dict.error != OK:
open_old_pxo_file(file, new_project, first_line)
else:
if typeof(dict.result) != TYPE_DICTIONARY:
print("Error, json parsed result is: %s" % typeof(dict.result))
file.close()
return

new_project.deserialize(dict.result)
for frame in new_project.frames:
for cel in frame.cels:
var buffer := file.get_buffer(new_project.size.x * new_project.size.y * 4)
cel.image.create_from_data(new_project.size.x, new_project.size.y, false, Image.FORMAT_RGBA8, buffer)
cel.image = cel.image # Just to call image_changed

if dict.result.has("brushes"):
for brush in dict.result.brushes:
var b_width = brush.size_x
var b_height = brush.size_y
var buffer := file.get_buffer(b_width * b_height * 4)
var image := Image.new()
image.create_from_data(b_width, b_height, false, Image.FORMAT_RGBA8, buffer)
new_project.brushes.append(image)
Global.create_brush_button(image)

file.close()
if !empty_project:
Global.projects.append(new_project)
Global.tabs.current_tab = Global.tabs.get_tab_count() - 1
else:
new_project.frames = new_project.frames # Just to call frames_changed
new_project.layers = new_project.layers # Just to call layers_changed
Global.canvas.camera_zoom()

if not untitled_backup:
# Untitled backup should not change window title and save path
current_save_paths[Global.current_project_index] = path
Global.window_title = path.get_file() + " - Pixelorama " + Global.current_version


# For pxo files older than v0.8
func open_old_pxo_file(file : File, new_project : Project, first_line : String) -> void:
# var file_version := file.get_line() # Example, "v0.7.10-beta"
var file_version := first_line
var file_ver_splitted := file_version.split("-")
var file_ver_splitted_numbers := file_ver_splitted[0].split(".")

Expand Down Expand Up @@ -104,6 +149,8 @@ func open_pxo_file(path : String, untitled_backup : bool = false) -> void:
if file_major_version >= 0 and file_minor_version >= 7:
if frame in linked_cels[layer_i]:
new_project.layers[layer_i].linked_cels.append(frame_class)
frame_class.cels[layer_i].image = new_project.layers[layer_i].linked_cels[0].cels[layer_i].image
frame_class.cels[layer_i].image_texture = new_project.layers[layer_i].linked_cels[0].cels[layer_i].image_texture

layer_i += 1
layer_line = file.get_line()
Expand All @@ -121,6 +168,7 @@ func open_pxo_file(path : String, untitled_backup : bool = false) -> void:
guide.add_point(Vector2(file.get_16(), 99999))
guide.has_focus = false
Global.canvas.add_child(guide)
new_project.guides.append(guide)
guide_line = file.get_line()

new_project.size = Vector2(width, height)
Expand All @@ -141,6 +189,7 @@ func open_pxo_file(path : String, untitled_backup : bool = false) -> void:
guide.add_point(Vector2(file.get_16(), 99999))
guide.has_focus = false
Global.canvas.add_child(guide)
new_project.guides.append(guide)
guide_line = file.get_line()

# Load tool options
Expand Down Expand Up @@ -181,113 +230,39 @@ func open_pxo_file(path : String, untitled_backup : bool = false) -> void:
new_project.animation_tags = new_project.animation_tags # To execute animation_tags_changed()
tag_line = file.get_line()

file.close()
if !empty_project:
Global.projects.append(new_project)
Global.tabs.current_tab = Global.tabs.get_tab_count() - 1
else:
new_project.frames = new_project.frames # Just to call frames_changed
new_project.layers = new_project.layers # Just to call layers_changed
Global.canvas.camera_zoom()

if not untitled_backup:
# Untitled backup should not change window title and save path
current_save_paths[Global.current_project_index] = path
Global.window_title = path.get_file() + " - Pixelorama " + Global.current_version


func save_pxo_file(path : String, autosave : bool, project : Project = Global.current_project) -> void:
var file := File.new()
var err := file.open_compressed(path, File.WRITE, File.COMPRESSION_ZSTD)
if err == OK:
# Store Pixelorama version
file.store_line(Global.current_version)

# Store Global layers
for layer in project.layers:
file.store_line(".")
file.store_line(layer.name)
file.store_8(layer.visible)
file.store_8(layer.locked)
file.store_8(layer.new_cels_linked)
var linked_cels := []
for frame in layer.linked_cels:
linked_cels.append(project.frames.find(frame))
file.store_var(linked_cels) # Linked cels as cel numbers

file.store_line("END_GLOBAL_LAYERS")

# Store frames
if !autosave:
project.name = path.get_file()
current_save_paths[Global.current_project_index] = path

var to_save = JSON.print(project.serialize())
file.store_line(to_save)
for frame in project.frames:
file.store_line("--")
file.store_16(project.size.x)
file.store_16(project.size.y)
for cel in frame.cels: # Store canvas layers
file.store_line("-")
for cel in frame.cels:
file.store_buffer(cel.image.get_data())
file.store_float(cel.opacity)
file.store_line("END_LAYERS")

file.store_line("END_FRAMES")

# Store guides
for child in Global.canvas.get_children():
if child is Guide:
file.store_line("|")
file.store_8(child.type)
if child.type == child.Types.HORIZONTAL:
file.store_16(child.points[0].y)
file.store_16(child.points[1].y)
else:
file.store_16(child.points[1].x)
file.store_16(child.points[0].x)
file.store_line("END_GUIDES")

# Save tool options
var left_color : Color = Global.color_pickers[0].color
var right_color : Color = Global.color_pickers[1].color
var left_brush_size : int = Global.brush_sizes[0]
var right_brush_size : int = Global.brush_sizes[1]
file.store_var(left_color)
file.store_var(right_color)
file.store_8(left_brush_size)
file.store_8(right_brush_size)

# Save custom brushes
for i in range(project.brushes.size()):
var brush = project.brushes[i]
file.store_line("/")
file.store_16(brush.get_size().x)
file.store_16(brush.get_size().y)
file.store_buffer(brush.get_data())
file.store_line("END_BRUSHES")

# Store animation tags
for tag in project.animation_tags:
file.store_line(".T/")
file.store_line(tag.name)
file.store_var(tag.color)
file.store_8(tag.from)
file.store_8(tag.to)
file.store_line("END_FRAME_TAGS")
for brush in project.brushes:
file.store_buffer(brush.get_data())

file.close()

if project.has_changed and not autosave:
project.has_changed = false

if autosave:
Global.notification_label("File autosaved")
else:
# First remove backup then set current save path
if project.has_changed:
project.has_changed = false
remove_backup(Global.current_project_index)
current_save_paths[Global.current_project_index] = path
Global.notification_label("File saved")
project.name = path.get_file()
Global.window_title = path.get_file() + " - Pixelorama " + Global.current_version

else:
Global.notification_label("File failed to save")
file.close()


func update_autosave() -> void:
Expand Down
2 changes: 1 addition & 1 deletion src/Canvas.gd
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func _draw() -> void:
draw_set_transform(mouse_pos, rotation, scale)
for rect in Global.left_circle_points:
draw_rect(Rect2(rect, Vector2.ONE), Color.blue, false)

#Check for tile mode
if Global.tile_mode and point_in_rectangle(mouse_pos,Vector2( - Global.current_project.size.x - 1 , - Global.current_project.size.y -1 ), Vector2(2 * Global.current_project.size.x, 2 * Global.current_project.size.y)):
if !point_in_rectangle(mouse_pos, Vector2(Global.current_project.x_min - 1,Global.current_project.y_min - 1), Vector2(Global.current_project.x_max,Global.current_project.y_max)):
Expand Down
5 changes: 3 additions & 2 deletions src/Classes/Cel.gd
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ var opacity : float


func _init(_image := Image.new(), _opacity := 1.0) -> void:
image_texture = ImageTexture.new()
self.image = _image
opacity = _opacity


func image_changed(value : Image) -> void:
image = value
image_texture = ImageTexture.new()
image_texture.create_from_image(image, 0)
if !image.is_empty():
image_texture.create_from_image(image, 0)
4 changes: 2 additions & 2 deletions src/Classes/Layer.gd
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
class_name Layer extends Reference
# A class for layer properties
# A class for layer properties.


var name := ""
var visible := true
var locked := false
var frame_container : HBoxContainer
var new_cels_linked := false
var linked_cels := [] # Array of Canvases
var linked_cels := [] # Array of Frames


func _init(_name := tr("Layer") + " 0", _visible := true, _locked := false, _frame_container := HBoxContainer.new(), _new_cels_linked := false, _linked_cels := []) -> void:
Expand Down
Loading

0 comments on commit b0338ab

Please sign in to comment.