Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
203 changes: 133 additions & 70 deletions .lib/record-core.lua
Original file line number Diff line number Diff line change
Expand Up @@ -22,129 +22,148 @@ function check_api_version()
end
end

function show_error(errorMsg)
function show_error(error_msg)
local error_dialog = Dialog("Error")
error_dialog
:label { text = errorMsg }
:label { text = error_msg }
:newrow()
:button { text = "Close", onclick = function() error_dialog:close() end }
error_dialog:show()
end

-- Core functions
local function initialize_snapshot(self, sprite)
self.auto_snap_enabled = false
self.auto_snap_delay = 1
self.auto_snap_increment = 0
self.context = nil

-- Instance of Aseprite Sprite object
-- https://github.com/aseprite/api/blob/master/api/sprite.md#sprite
self.sprite = nil
if sprite then
self.sprite = sprite
self.context = get_recording_context(sprite)
local function get_active_frame_number()
local frame = app.activeFrame
if frame == nil then
return 1
else
return frame
end
end

local function get_snapshot_recording_index(self)
local path = self.context.index_file
RecordingContext = {}

function RecordingContext:get_recording_index()
local path = self.index_file
if not app.fs.isFile(path) then
return 0
end
local file = io.open(path, "r")
assert(file)
local contents = file:read()
io.close(file)
return tonumber(contents)
end

local function increment_snapshot_recording_index(self)
local index = get_snapshot_recording_index(self) + 1
local path = self.context.index_file
function RecordingContext:set_recording_index(index)
local path = self.index_file
local file = io.open(path, "w")
assert(file)
file:write("" .. index)
io.close(file)
end

local function get_active_frame_number()
local frame = app.activeFrame
if frame == nil then
return 1
else
return frame
end
end

function get_recording_context(sprite)
function RecordingContext.new(sprite)
local self = {}
setmetatable(self, { __index = RecordingContext })
self.sprite_file_name = app.fs.fileTitle(sprite.filename)
self.sprite_file_path = app.fs.filePath(sprite.filename)
self:_init_directory()
self.index_file = app.fs.joinPath(self.record_directory_path, "_index.txt")
self:_promote_v2_to_v3()
return self
end

-- 2.x Target Directory Backwards Compatibility
self.record_directory_name_legacy = self.sprite_file_name .. "_record"
local is_legacy_recording = false
if app.fs.isDirectory(app.fs.joinPath(self.sprite_file_path, self.record_directory_name_legacy)) then
is_legacy_recording = true
self.record_directory_name = self.record_directory_name_legacy
function RecordingContext:_init_directory()
local dir_name_legacy = self.sprite_file_name .. "_record"
local dir_path_legacy = app.fs.joinPath(self.sprite_file_path, dir_name_legacy)
if app.fs.isDirectory(dir_path_legacy) then
-- For 2.x Target Directory Backwards Compatibility
self._is_legacy_recording = true
self.record_directory_name = dir_name_legacy
else
self._is_legacy_recording = false
self.record_directory_name = self.sprite_file_name .. "__record"
end

self.record_directory_path = app.fs.joinPath(self.sprite_file_path, self.record_directory_name)
self.index_file = app.fs.joinPath(self.record_directory_path, "_index.txt")
end

function RecordingContext:_promote_v2_to_v3()
if not self._is_legacy_recording then
return -- Is not v2.x
end
if app.fs.isFile(self.index_file) then
return -- Is v2.x, but already has promoted structure
end
-- 2.x Add Missing Index File for Forward Compatibility
if is_legacy_recording and not app.fs.isFile(self.index_file) then
local is_index_set = false
local current_index = 0
while not is_index_set do
if not app.fs.isFile(get_contextual_recording_image_path(self, current_index)) then
is_index_set = true
local file = io.open(self.index_file, "w")
file:write("" .. current_index)
io.close(file)
else
current_index = current_index + 1
end
local is_index_set = false
local current_index = 0
while not is_index_set do
if not app.fs.isFile(self:get_recording_image_path(current_index)) then
is_index_set = true
self.context:set_recording_index(current_index)
else
current_index = current_index + 1
end
end

return self
end

function get_contextual_recording_image_path(self, index)
function RecordingContext:get_recording_image_path(index)
return app.fs.joinPath(self.record_directory_path, self.sprite_file_name .. "_" .. index .. ".png")
end

function get_recording_image_path_at_index(self, index)
return get_contextual_recording_image_path(self.context, index)
Snapshot = {}

function Snapshot:_initialize(sprite)
self.auto_snap_enabled = false
self.auto_snap_delay = 1
self.auto_snap_increment = 0
self.context = nil

-- Instance of Aseprite Sprite object
-- https://github.com/aseprite/api/blob/master/api/sprite.md#sprite
self.sprite = nil
if sprite then
self.sprite = sprite
self.context = RecordingContext.new(sprite)
end
end

function Snapshot:_increment_recording_index()
local index = self.context:get_recording_index(self) + 1
self.context:set_recording_index(index)
end

function get_snapshot()
function Snapshot:get_recording_image_path(index)
return self.context:get_recording_image_path(index)
end

function Snapshot.new()
local self = {}
initialize_snapshot(self, nil)
setmetatable(self, { __index = Snapshot })
self:_initialize(nil)
return self
end

function is_snapshot_active(self)
if not is_snapshot_valid(self) then
function Snapshot:is_active()
if not self:is_valid() then
return false
end
return self.auto_snap_enabled
end

function is_snapshot_valid(self)
function Snapshot:is_valid()
if self.sprite then
return true
end
return false
end

function reset_snapshot(self)
initialize_snapshot(self, nil)
function Snapshot:reset()
self:_initialize(nil)
end

function auto_save_snapshot(self)
function Snapshot:auto_save()
if not self.auto_snap_enabled then
return
end
Expand All @@ -157,31 +176,75 @@ function auto_save_snapshot(self)
return
end
self.auto_snap_increment = 0
save_snapshot(self)
self:save()
end

function save_snapshot(self)
local path = get_recording_image_path_at_index(self, get_snapshot_recording_index(self))
function Snapshot:_get_current_image()
local image = Image(self.sprite)
image:drawSprite(self.sprite, get_active_frame_number())
return image
end

function Snapshot:_get_saved_image_content(index)
if index < 0 then
return nil
end
-- This intentionally does not do anything "smart" such as scanning the
-- directory if gaps exist because that is O(N) at IO speeds and on every
-- snapshot (where N is the current snapshot index).
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
-- This intentionally does not do anything "smart" such as scanning the
-- directory if gaps exist because that is O(N) at IO speeds and on every
-- snapshot (where N is the current snapshot index).

Can prune these comments from the code, is preferable to maintain these types of artifacts in PR comments for history tracking.

local path = self:get_recording_image_path(index)
if not app.fs.isFile(path) then
return nil
end

-- NOTE(teding): Unfortunately `Image { fromFile = path }`:
-- * causes load popups
-- * pollutes "recent files"
-- Anyway, this seems more than fast enough for human inputs. I tried lots
-- of very very fast changes on large detailed canvases, had no issues, and
-- have a computer that is average by 2022 standards.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ill be sure to beat this up as I have a computer considered average by 2012 standard.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Ran through a gambit of:

  • Image creation
  • cropping
  • painting
  • fills
  • moving
  • resizing

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
-- NOTE(teding): Unfortunately `Image { fromFile = path }`:
-- * causes load popups
-- * pollutes "recent files"
-- Anyway, this seems more than fast enough for human inputs. I tried lots
-- of very very fast changes on large detailed canvases, had no issues, and
-- have a computer that is average by 2022 standards.

Can prune these comments from the code, is preferable to maintain these types of artifacts in PR comments for history tracking.

local file = io.open(path, "rb")
assert(file)
local content = file:read("a")
io.close(file)
return content
end

function Snapshot:save()
local image = self:_get_current_image()
local index = self.context:get_recording_index()
local path = self:get_recording_image_path(index)
image:saveAs(path)
increment_snapshot_recording_index(self)

local image_changed = true
local prev_content = self:_get_saved_image_content(index - 1)
if prev_content ~= nil then
local curr_content = self:_get_saved_image_content(index)
assert(curr_content ~= nil)
if prev_content == curr_content then
image_changed = false
end
end

if image_changed then
self:_increment_recording_index()
end
end

function set_snapshot_sprite(self, sprite)
function Snapshot:set_sprite(sprite)
if not app.fs.isFile(sprite.filename) then
return show_error(error_messages.save_required)
end

if (not self.sprite or self.sprite ~= sprite) then
initialize_snapshot(self, sprite)
self:_initialize(sprite)
end
end

function update_snapshot_sprite(self)
function Snapshot:update_sprite()
local sprite = app.activeSprite
if not sprite then
return show_error(error_messages.no_active_sprite)
end
set_snapshot_sprite(self, sprite)
self:set_sprite(sprite)
end
14 changes: 7 additions & 7 deletions Automatic Snapshot.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@

dofile(".lib/record-core.lua")

local snapshot = get_snapshot()
local snapshot = Snapshot.new()

local function take_auto_snapshot()
auto_save_snapshot(snapshot)
snapshot:auto_save()
end

local function disable_auto_snapshot(dialog)
snapshot.auto_snap_enabled = false
if not is_snapshot_valid(snapshot) then
if not snapshot:is_valid() then
return
end

Expand All @@ -32,9 +32,9 @@ local function disable_auto_snapshot(dialog)
end

local function enable_auto_snapshot(dialog)
update_snapshot_sprite(snapshot)
snapshot:update_sprite()

snapshot.auto_snap_enabled = is_snapshot_valid(snapshot)
snapshot.auto_snap_enabled = snapshot:is_valid()
if not snapshot.auto_snap_enabled then
return
end
Expand All @@ -60,7 +60,7 @@ if check_api_version() then
local main_dialog = Dialog {
title = "Record - Auto Snapshot",
onclose = function()
reset_snapshot(snapshot)
snapshot:reset()
end
}

Expand Down Expand Up @@ -89,7 +89,7 @@ if check_api_version() then
id = "toggle",
text = "Start",
onclick = function()
if is_snapshot_active(snapshot) then
if snapshot:is_active() then
disable_auto_snapshot(main_dialog)
else
enable_auto_snapshot(main_dialog)
Expand Down
15 changes: 7 additions & 8 deletions Command Palette.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,23 @@

dofile(".lib/record-core.lua")

local snapshot = get_snapshot()
local snapshot = Snapshot.new()

local function take_snapshot()
update_snapshot_sprite(snapshot)
if not is_snapshot_valid(snapshot) then
snapshot:update_sprite()
if not snapshot:is_valid() then
return
end

save_snapshot(snapshot)
snapshot:save()
end

local function open_time_lapse()
update_snapshot_sprite(snapshot)
if not is_snapshot_valid(snapshot) then
snapshot:update_sprite()
if not snapshot:is_valid() then
return
end

local path = get_recording_image_path_at_index(snapshot, 0)
local path = snapshot:get_recording_image_path_at_index(0)
Copy link
Owner

@sprngr sprngr May 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
local path = snapshot:get_recording_image_path_at_index(0)
local path = snapshot:get_recording_image_path(0)

Method used is the old name, needs to be replaced with new one.

if app.fs.isFile(path) then
app.command.OpenFile { filename = path }
else
Expand Down
4 changes: 2 additions & 2 deletions Open Time Lapse.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ dofile(".lib/record-core.lua")
if check_api_version() then
local sprite = app.activeSprite
if sprite and app.fs.isFile(sprite.filename) then
local context = get_recording_context(sprite)
local path = get_contextual_recording_image_path(context, 0)
local context = RecordingContext.new(sprite)
local path = context:get_recording_image_path(0)
if app.fs.isFile(path) then
app.command.OpenFile { filename = path }
else
Expand Down
6 changes: 3 additions & 3 deletions Take Snapshot.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ dofile(".lib/record-core.lua")
if check_api_version() then
local sprite = app.activeSprite
if sprite and app.fs.isFile(sprite.filename) then
local snapshot = get_snapshot()
set_snapshot_sprite(snapshot, sprite)
save_snapshot(snapshot)
local snapshot = Snapshot.new()
snapshot:set_sprite(sprite)
snapshot:save()
else
return show_error(error_messages.save_required)
end
Expand Down