feat(img): implement loading an image into memory

Implement `vim.img.load()` to load from a file or wrap base64 encoded bytes as a `vim.img.Image` instance.
This commit is contained in:
Chip Senkbeil 2024-11-29 20:56:54 -06:00
parent 2833925cfc
commit 25820b5e24
No known key found for this signature in database
GPG Key ID: 35EF1F8EC72A4131
3 changed files with 122 additions and 0 deletions

View File

@ -31,6 +31,7 @@ for k, v in pairs({
loader = true,
func = true,
F = true,
img = true,
lsp = true,
hl = true,
diagnostic = true,

14
runtime/lua/vim/img.lua Normal file
View File

@ -0,0 +1,14 @@
local img = vim._defer_require('vim.img', {
_image = ..., --- @module 'vim.img._image'
})
---Loads an image into memory, returning a wrapper around the image.
---
---Accepts `data` as base64-encoded bytes, or a `filename` that will be loaded.
---@param opts {data?:string, filename?:string}
---@return vim.img.Image
function img.load(opts)
return img._image:new(opts)
end
return img

View File

@ -0,0 +1,107 @@
---@class vim.img.Image
---@field name string|nil name of the image if loaded from disk
---@field data string|nil base64 encoded data
local M = {}
M.__index = M
---Creates a new image instance.
---@param opts? {data?:string, filename?:string}
---@return vim.img.Image
function M:new(opts)
opts = opts or {}
local instance = {}
setmetatable(instance, M)
instance.data = opts.data
if not instance.data and opts.filename then
instance:load_from_file(opts.filename)
end
return instance
end
---Returns true if the image is loaded into memory.
---@return boolean
function M:is_loaded()
return self.data ~= nil
end
---Returns the size of the base64 encoded image.
---@return integer
function M:size()
return string.len(self.data or '')
end
---Loads data for an image from a file, replacing any existing data.
---If a callback provided, will load asynchronously; otherwise, is blocking.
---@param filename string
---@param cb fun(err:string|nil, image:vim.img.Image|nil)
---@overload fun(filename:string):vim.img.Image
function M:load_from_file(filename, cb)
local name = vim.fn.fnamemodify(filename, ':t:r')
if not cb then
local stat = vim.uv.fs_stat(filename)
assert(stat, 'unable to stat ' .. filename)
local fd = vim.uv.fs_open(filename, 'r', 644) --[[ @type integer|nil ]]
assert(fd, 'unable to open ' .. filename)
local data = vim.uv.fs_read(fd, stat.size, -1) --[[ @type string|nil ]]
assert(data, 'unable to read ' .. filename)
self.name = name
self.data = vim.base64.encode(data)
return self
end
---@param err string|nil
---@return boolean
local function report_err(err)
if err then
vim.schedule(function()
cb(err)
end)
end
return err ~= nil
end
vim.uv.fs_stat(filename, function(stat_err, stat)
if report_err(stat_err) then
return
end
if not stat then
report_err('missing stat')
return
end
vim.uv.fs_open(filename, 'r', 644, function(open_err, fd)
if report_err(open_err) then
return
end
if not fd then
report_err('missing fd')
return
end
vim.uv.fs_read(fd, stat.size, -1, function(read_err, data)
if report_err(read_err) then
return
end
vim.uv.fs_close(fd, function() end)
self.name = name
self.data = vim.base64.encode(data or '')
vim.schedule(function()
cb(nil, self)
end)
end)
end)
end)
end
return M