From f0040edff20df5faffe25ac17af4f1bc295e6a98 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 7 Oct 2025 13:30:28 +0530 Subject: [PATCH] More work on image previews --- kittens/choose_files/image_preview.go | 81 +++++++++++++++++++++++++-- kittens/choose_files/preview.go | 7 ++- 2 files changed, 79 insertions(+), 9 deletions(-) diff --git a/kittens/choose_files/image_preview.go b/kittens/choose_files/image_preview.go index 821461b02..32ce021bd 100644 --- a/kittens/choose_files/image_preview.go +++ b/kittens/choose_files/image_preview.go @@ -21,21 +21,89 @@ var preview_cache = sync.OnceValues(func() (*disk_cache.DiskCache, error) { return disk_cache.NewDiskCache(cdir, dc_size.Load()) }) +type PreviewRenderer interface { + Render(string) (map[string][]byte, error) + Show(h *Handler, abspath string, metadata fs.FileInfo, x, y, width, height int, cached_data map[string]string) +} + +type render_data struct { + cached_data map[string]string + err error +} + type ImagePreview struct { - abspath, cache_key string - disk_cache *disk_cache.DiskCache + abspath, cache_key string + metadata fs.FileInfo + disk_cache *disk_cache.DiskCache + cached_data map[string]string + render_err Preview + render_channel chan render_data + renderer PreviewRenderer + file_metadata_preview Preview + WakeupMainThread func() bool } func (p ImagePreview) IsValidForColorScheme(bool) bool { return true } func (p ImagePreview) Render(h *Handler, x, y, width, height int) { - offset := 0 - offset += h.render_wrapped_text_in_region("Rendering image, please wait...", x, y, width, height, true) + if p.render_channel == nil { + if p.render_err == nil { + p.renderer.Show(h, p.abspath, p.metadata, x, y, width, height, p.cached_data) + } else { + p.render_err.Render(h, x, y, width, height) + } + return + } + select { + case hd := <-p.render_channel: + p.render_channel = nil + p.cached_data = hd.cached_data + p.render_err = NewErrorPreview(fmt.Errorf("Failed to render the preview with error: %w", hd.err)) + p.Render(h, x, y, width, height) + return + default: + } + if p.file_metadata_preview == nil { + p.file_metadata_preview = NewFileMetadataPreview(p.abspath, p.metadata) + } + p.file_metadata_preview.Render(h, x, y, width, height) } -func NewImagePreview(abspath string, metadata fs.FileInfo, opts Settings) (Preview, error) { +func (p *ImagePreview) start_rendering() { + defer func() { + p.WakeupMainThread() + }() + ans := p.disk_cache.Get(p.cache_key) + if len(ans) > 0 { + p.render_channel <- render_data{ans, nil} + return + } + rdata, err := p.renderer.Render(p.abspath) + if err != nil { + p.render_channel <- render_data{nil, err} + } else { + ans, err = p.disk_cache.Add(p.cache_key, rdata) + p.render_channel <- render_data{utils.IfElse(err == nil, ans, nil), err} + } +} + +type ImagePreviewRenderer uint + +func (p ImagePreviewRenderer) Render(abspath string) (ans map[string][]byte, err error) { + return +} + +func (p ImagePreviewRenderer) Show(h *Handler, abspath string, metadata fs.FileInfo, x, y, width, height int, cached_data map[string]string) { +} + +func NewImagePreview( + abspath string, metadata fs.FileInfo, opts Settings, WakeupMainThread func() bool, r PreviewRenderer, +) (Preview, error) { dc_size.Store(opts.DiskCacheSize()) - ans := &ImagePreview{abspath: abspath} + ans := &ImagePreview{ + abspath: abspath, metadata: metadata, render_channel: make(chan render_data), + WakeupMainThread: WakeupMainThread, renderer: r, + } if dc, err := preview_cache(); err != nil { return nil, err } else { @@ -46,5 +114,6 @@ func NewImagePreview(abspath string, metadata fs.FileInfo, opts Settings) (Previ } else { ans.cache_key = key } + go ans.start_rendering() return ans, nil } diff --git a/kittens/choose_files/preview.go b/kittens/choose_files/preview.go index a42a43708..a569b342f 100644 --- a/kittens/choose_files/preview.go +++ b/kittens/choose_files/preview.go @@ -298,11 +298,12 @@ func (pm *PreviewManager) preview_for(abspath string, ftype fs.FileMode) (ans Pr return NewTextFilePreview(abspath, s, ch, pm.highlighter.Sanitize) } if strings.HasPrefix(mt, "image/") { - ans, err := NewImagePreview(abspath, s, pm.settings) - if err != nil { + var r ImagePreviewRenderer + if ans, err := NewImagePreview(abspath, s, pm.settings, pm.WakeupMainThread, r); err == nil { + return ans + } else { return NewErrorPreview(err) } - return ans } return NewFileMetadataPreview(abspath, s) }