Skip to content

Commit

Permalink
perf: optimize rendering and always use shallow delete for kitty images
Browse files Browse the repository at this point in the history
  • Loading branch information
3rd committed Jul 10, 2023
1 parent ae4db25 commit 05679fa
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 34 deletions.
13 changes: 10 additions & 3 deletions lua/image/backends/kitty/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,13 @@ backend.render = function(image, x, y, width, height)
display_virtual_placeholder = with_virtual_placeholders and 1 or 0,
quiet = 2,
}, image.cropped_path)
-- utils.debug("[kitty] transmitted image " .. image.id .. " (" .. image.internal_id .. ")")
end
if backend.features.crop then
if not transmitted_images[image.id] then
local preprocessing_hash = ("%s-%s"):format(image.id, image.resize_hash)
if transmitted_images[image.id] ~= preprocessing_hash then
transmit()
transmitted_images[image.id] = true
transmitted_images[image.id] = preprocessing_hash
end
else
local preprocessing_hash = ("%s-%s-%s"):format(image.id, image.resize_hash, image.crop_hash)
Expand Down Expand Up @@ -126,6 +128,8 @@ backend.render = function(image, x, y, width, height)
image.is_rendered = true
backend.state.images[image.id] = image
helpers.restore_cursor()

-- utils.debug("[kitty] rendered image", image.id, "(" .. image.internal_id .. ")")
end

backend.clear = function(image_id, shallow)
Expand All @@ -135,7 +139,7 @@ backend.clear = function(image_id, shallow)
if not image then return end
helpers.write_graphics({
action = codes.control.action.delete,
display_delete = shallow and "i" or "I",
display_delete = "i",
image_id = image.internal_id,
quiet = 2,
})
Expand All @@ -144,6 +148,7 @@ backend.clear = function(image_id, shallow)
backend.state.images[image_id] = nil
transmitted_images[image.id] = nil
end
-- utils.debug("[kitty] cleared image", image.id, "(" .. image.internal_id .. ")", "shallow:", shallow)
return
end

Expand All @@ -153,12 +158,14 @@ backend.clear = function(image_id, shallow)
display_delete = "a",
quiet = 2,
})
-- utils.debug("[kitty] cleared all")
for id, image in pairs(backend.state.images) do
image.is_rendered = false
if not shallow then
backend.state.images[id] = nil
transmitted_images[image.id] = nil
end
-- utils.debug("[kitty] cleared image (all)", image.id, "shallow:", shallow)
end
end

Expand Down
68 changes: 44 additions & 24 deletions lua/image/image.lua
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ function Image:render(geometry)
local was_rendered = renderer.render(self)
-- utils.log("render result", self.id, was_rendered)

if self.is_rendered and not was_rendered then
-- shallow clear if render was prevented
self.global_state.backend.clear(self.id, true)
end

-- virtual padding
if self.buffer and self.with_virtual_padding then
local row = self.geometry.y
Expand All @@ -42,48 +47,62 @@ function Image:render(geometry)

local previous_extmark = buf_extmark_map[self.buffer .. ":" .. row]

-- clear previous extmark if rendering was prevented
if not was_rendered and previous_extmark then
self.global_state.backend.clear(self.id, true)
vim.api.nvim_buf_del_extmark(self.buffer, self.global_state.extmarks_namespace, previous_extmark.id)
if vim.api.nvim_buf_is_valid(self.buffer) then
vim.api.nvim_buf_del_extmark(self.buffer, self.global_state.extmarks_namespace, previous_extmark.id)
end
buf_extmark_map[self.buffer .. ":" .. row] = nil
return
end

if previous_extmark then
if previous_extmark.height == height then return end
vim.api.nvim_buf_del_extmark(self.buffer, self.global_state.extmarks_namespace, previous_extmark.id)
end

-- create extmark if outdated or it doesn't exist
if was_rendered then
local text = string.rep(" ", width)
local filler = {}
for _ = 0, height - 1 do
filler[#filler + 1] = { { text, "" } }
if previous_extmark then
if previous_extmark.height == height then return end
vim.api.nvim_buf_del_extmark(self.buffer, self.global_state.extmarks_namespace, previous_extmark.id)
end
vim.api.nvim_buf_set_extmark(self.buffer, self.global_state.extmarks_namespace, row - 1, 0, {
id = self.internal_id,
virt_lines = filler,
})
buf_extmark_map[self.buffer .. ":" .. row] = { id = self.internal_id, height = height }

-- rerender any images that are below this one
for _, image in pairs(self.global_state.images) do
if image.buffer == self.buffer and image.geometry.y > self.geometry.y then image:render() end
if was_rendered then
local text = string.rep(" ", width)
local filler = {}
for _ = 0, height - 1 do
filler[#filler + 1] = { { text, "" } }
end
vim.api.nvim_buf_set_extmark(self.buffer, self.global_state.extmarks_namespace, row - 1, 0, {
id = self.internal_id,
virt_lines = filler,
})
buf_extmark_map[self.buffer .. ":" .. row] = { id = self.internal_id, height = height }

-- rerender any images that are below this one
-- TODO: only the one after
-- TODO: should this be here?
for _, image in pairs(self.global_state.images) do
if image.buffer == self.buffer and image.geometry.y > self.geometry.y then image:render() end
end
end
end
end
end

function Image:clear()
self.global_state.backend.clear(self.id)
---@param shallow? boolean
function Image:clear(shallow)
if not self.is_rendered then return end

-- utils.debug(("[image] clear %s, shallow: %s"):format(self.id, shallow))
self.global_state.backend.clear(self.id, shallow or false)
self.rendered_geometry = {
x = nil,
y = nil,
width = nil,
height = nil,
}
if self.buffer then
vim.api.nvim_buf_del_extmark(self.buffer, self.global_state.extmarks_namespace, self.internal_id)

if self.with_virtual_padding and self.buffer then
if vim.api.nvim_buf_is_valid(self.buffer) then
vim.api.nvim_buf_del_extmark(self.buffer, self.global_state.extmarks_namespace, self.internal_id)
end
buf_extmark_map[self.buffer .. ":" .. self.geometry.y] = nil
end
end
Expand Down Expand Up @@ -207,7 +226,8 @@ local from_file = function(path, options, state)
},
with_virtual_padding = opts.with_virtual_padding or false,
is_rendered = false,
crop_resize_hash = nil,
crop_hash = nil,
resize_hash = nil,
}, state)

return instance
Expand Down
6 changes: 4 additions & 2 deletions lua/image/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ api.setup = function(options)
vim.defer_fn(function()
if needs_clear then
for _, curr in ipairs(api.get_images({ buffer = prev.bufnr })) do
curr:clear()
curr:clear(true)
end
elseif needs_rerender then
for _, current_image in ipairs(api.get_images({ window = winid })) do
Expand Down Expand Up @@ -147,12 +147,14 @@ api.setup = function(options)
local is_buffer_binding_valid = win_buf_map[current_image.window] == current_image.buffer

local should_clear = false
local shallow = false
if is_window_bound and not is_window_binding_valid then
should_clear = true
elseif is_buffer_bound and not is_buffer_binding_valid then
should_clear = true
shallow = true
end
if should_clear then current_image:clear() end
if should_clear then current_image:clear(shallow) end
end
end,
})
Expand Down
6 changes: 5 additions & 1 deletion lua/image/integrations/markdown.lua
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,12 @@ local render = function(ctx)
end
end

-- clear previous images
for _, image in ipairs(previous_images) do
if not vim.tbl_contains(new_image_ids, image.id) then image:clear() end
if not vim.tbl_contains(new_image_ids, image.id) then
-- utils.debug("[markdown] clearing removed image", image.id)
image:clear()
end
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lua/image/integrations/neorg.lua
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ local setup_autocommands = function(ctx)
local current_window = vim.api.nvim_get_current_win()
local images = ctx.api.get_images({ window = current_window })
for _, image in ipairs(images) do
image:clear()
image:clear(true)
end
end,
})
Expand Down
2 changes: 1 addition & 1 deletion lua/image/renderer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -324,8 +324,8 @@ local render = function(image)
end

-- crop
local crop_hash = ("%d-%d-%d-%d"):format(0, crop_offset_top, pixel_width, cropped_pixel_height)
if needs_crop then
local crop_hash = ("%d-%d-%d-%d"):format(0, crop_offset_top, pixel_width, cropped_pixel_height)
if (needs_resize and image.resize_hash ~= resize_hash) or image.crop_hash ~= crop_hash then
local cropped_image = magick.load_image(image.resized_path or image.path)
cropped_image:set_format("png")
Expand Down
4 changes: 2 additions & 2 deletions lua/types.lua
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@
---@field crop_hash? string
---@field global_state State
---@field render fun(self: Image, geometry?: ImageGeometry)
---@field clear fun(self: Image)
---@field clear fun(self: Image, shallow?: boolean)
---@field move fun(self: Image, x: number, y: number)
---@field brightness fun(self: Image, brightness: number)
---@field saturation fun(self: Image, saturation: number)
Expand Down Expand Up @@ -151,4 +151,4 @@
---@field display_cursor_policy 0|1
---@field display_virtual_placeholder 0|1
---@field display_zindex number
---@field display_delete "a"|"i"|"I"|"p"
---@field display_delete "a"|"A"|"i"|"I"|"p"

0 comments on commit 05679fa

Please sign in to comment.