perf: cache resized ascii images (#547)
Some checks are pending
Deploy docs / build-and-deploy (push) Waiting to run
Merge checks / Checks (push) Waiting to run
Merge checks / Validate nix flake (push) Waiting to run
Merge checks / Validate bat assets (push) Waiting to run
Merge checks / Validate JSON schemas (push) Waiting to run

This caches:
* Ascii images so we don't reload them every time we're doing slide
transitions.
* Ascii image resizes, so if we go to a slide that contains an image and
we're using slide transitions, the next transition that includes that
image won't require resizing it to that same sizes.

The one performance "issue" still present when using slide transitions
is that we still need to pay for that first image -> ascii image
conversion and that first ascii image resize the first time we
transition to a slide that contains an image. This is currently
noticeable in debug mode and not in release mode (at least in my machine
™️), but it would be sweet if it wasn't there ever. For this we need to
run through all slides, find all images, ascii convert them and resize
them to the current terminal size. The same should be done when the
terminal is resized.
This commit is contained in:
Matias Fontanini 2025-04-13 14:29:53 -07:00 committed by GitHub
commit 9e1f2beca2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 185 additions and 141 deletions

View File

@ -230,7 +230,7 @@ impl CoreComponents {
}
let graphics_mode = Self::select_graphics_mode(cli, &config);
let printer = Arc::new(ImagePrinter::new(graphics_mode.clone())?);
let registry = ImageRegistry(printer.clone());
let registry = ImageRegistry::new(printer.clone());
let resources = Resources::new(
resources_path.clone(),
themes_path.unwrap_or_else(|| resources_path.clone()),

View File

@ -22,7 +22,7 @@ use crate::{
resource::Resources,
terminal::image::{
Image,
printer::{ImageRegistry, RegisterImageError},
printer::{ImageRegistry, ImageSpec, RegisterImageError},
},
theme::{
Alignment, AuthorPositioning, ElementType, PresentationTheme, ProcessingThemeError, ThemeOptions,
@ -246,7 +246,7 @@ impl<'a> PresentationBuilder<'a> {
};
let mut image = DynamicImage::new_rgba8(1, 1);
image.as_mut_rgba8().unwrap().get_pixel_mut(0, 0).0 = rgba;
let image = self.image_registry.register_image(image)?;
let image = self.image_registry.register(ImageSpec::Generated(image))?;
Ok(image)
}

View File

@ -382,7 +382,7 @@ impl<'a> Presenter<'a> {
&mut self.third_party,
self.code_executor.clone(),
&self.themes,
ImageRegistry(self.image_printer.clone()),
ImageRegistry::new(self.image_printer.clone()),
self.options.bindings.clone(),
self.options.builder_options.clone(),
)?
@ -425,13 +425,14 @@ impl<'a> Presenter<'a> {
let Some(config) = self.options.transition.clone() else {
return Ok(());
};
let registry = self.resources.image_registry();
let options = drawer.render_engine_options();
let presentation = self.state.presentation_mut();
let dimensions = WindowSize::current(self.options.font_size_fallback)?;
presentation.jump_previous();
let left = Self::virtual_render(presentation.current_slide(), dimensions.clone(), &options)?;
let left = Self::virtual_render(presentation.current_slide(), dimensions.clone(), &options, registry.clone())?;
presentation.jump_next();
let right = Self::virtual_render(presentation.current_slide(), dimensions.clone(), &options)?;
let right = Self::virtual_render(presentation.current_slide(), dimensions.clone(), &options, registry)?;
let direction = TransitionDirection::Next;
self.animate_transition(drawer, left, right, direction, dimensions, config)
}
@ -440,13 +441,14 @@ impl<'a> Presenter<'a> {
let Some(config) = self.options.transition.clone() else {
return Ok(());
};
let registry = self.resources.image_registry();
let options = drawer.render_engine_options();
let presentation = self.state.presentation_mut();
let dimensions = WindowSize::current(self.options.font_size_fallback)?;
presentation.jump_next();
let right = Self::virtual_render(presentation.current_slide(), dimensions.clone(), &options)?;
let right = Self::virtual_render(presentation.current_slide(), dimensions.clone(), &options, registry.clone())?;
presentation.jump_previous();
let left = Self::virtual_render(presentation.current_slide(), dimensions.clone(), &options)?;
let left = Self::virtual_render(presentation.current_slide(), dimensions.clone(), &options, registry)?;
let direction = TransitionDirection::Previous;
self.animate_transition(drawer, left, right, direction, dimensions, config)
}
@ -526,8 +528,9 @@ impl<'a> Presenter<'a> {
slide: &Slide,
dimensions: WindowSize,
options: &RenderEngineOptions,
registry: ImageRegistry,
) -> Result<TerminalGrid, RenderError> {
let mut term = VirtualTerminal::new(dimensions.clone(), ImageBehavior::PrintAscii);
let mut term = VirtualTerminal::new(dimensions.clone(), ImageBehavior::PrintAscii(registry));
let engine = RenderEngine::new(&mut term, dimensions.clone(), options.clone());
engine.render(slide.iter_visible_operations())?;
Ok(term.into_contents())

View File

@ -1,7 +1,7 @@
use crate::{
terminal::image::{
Image,
printer::{ImageRegistry, RegisterImageError},
printer::{ImageRegistry, ImageSpec, RegisterImageError},
},
theme::{raw::PresentationTheme, registry::LoadThemeError},
};
@ -24,8 +24,6 @@ const LOOP_INTERVAL: Duration = Duration::from_millis(250);
#[derive(Debug)]
struct ResourcesInner {
images: HashMap<PathBuf, Image>,
theme_images: HashMap<PathBuf, Image>,
themes: HashMap<PathBuf, PresentationTheme>,
external_snippets: HashMap<PathBuf, String>,
base_path: PathBuf,
@ -56,8 +54,6 @@ impl Resources {
let inner = ResourcesInner {
base_path: base_path.into(),
themes_path: themes_path.into(),
images: Default::default(),
theme_images: Default::default(),
themes: Default::default(),
external_snippets: Default::default(),
image_registry,
@ -73,14 +69,9 @@ impl Resources {
/// Get the image at the given path.
pub(crate) fn image<P: AsRef<Path>>(&self, path: P) -> Result<Image, RegisterImageError> {
let mut inner = self.inner.borrow_mut();
let inner = self.inner.borrow();
let path = inner.base_path.join(path);
if let Some(image) = inner.images.get(&path) {
return Ok(image.clone());
}
let image = inner.image_registry.register_resource(path.clone())?;
inner.images.insert(path, image.clone());
let image = inner.image_registry.register(ImageSpec::Filesystem(path.clone()))?;
Ok(image)
}
@ -91,14 +82,9 @@ impl Resources {
_ => (),
};
let mut inner = self.inner.borrow_mut();
let inner = self.inner.borrow();
let path = inner.themes_path.join(path);
if let Some(image) = inner.theme_images.get(&path) {
return Ok(image.clone());
}
let image = inner.image_registry.register_resource(path.clone())?;
inner.theme_images.insert(path, image.clone());
let image = inner.image_registry.register(ImageSpec::Filesystem(path.clone()))?;
Ok(image)
}
@ -144,9 +130,13 @@ impl Resources {
/// Clears all resources.
pub(crate) fn clear(&self) {
let mut inner = self.inner.borrow_mut();
inner.images.clear();
inner.image_registry.clear();
inner.themes.clear();
}
pub(crate) fn image_registry(&self) -> ImageRegistry {
self.inner.borrow().image_registry.clone()
}
}
/// Watches for file changes.

View File

@ -16,19 +16,18 @@ use crate::{
use image::{DynamicImage, ImageError};
use std::{
borrow::Cow,
collections::HashMap,
fmt, io,
path::{Path, PathBuf},
sync::Arc,
ops::Deref,
path::PathBuf,
sync::{Arc, Mutex},
};
pub(crate) trait PrintImage {
type Image: ImageProperties;
/// Register an image.
fn register(&self, image: DynamicImage) -> Result<Self::Image, RegisterImageError>;
/// Load and register an image from the given path.
fn register_from_path<P: AsRef<Path>>(&self, path: P) -> Result<Self::Image, RegisterImageError>;
fn register(&self, spec: ImageSpec) -> Result<Self::Image, RegisterImageError>;
fn print<T>(&self, image: &Self::Image, options: &PrintOptions, terminal: &mut T) -> Result<(), PrintImageError>
where
@ -120,26 +119,14 @@ impl ImagePrinter {
impl PrintImage for ImagePrinter {
type Image = TerminalImage;
fn register(&self, image: DynamicImage) -> Result<Self::Image, RegisterImageError> {
fn register(&self, spec: ImageSpec) -> Result<Self::Image, RegisterImageError> {
let image = match self {
Self::Kitty(printer) => TerminalImage::Kitty(printer.register(image)?),
Self::Iterm(printer) => TerminalImage::Iterm(printer.register(image)?),
Self::Ascii(printer) => TerminalImage::Ascii(printer.register(image)?),
Self::Kitty(printer) => TerminalImage::Kitty(printer.register(spec)?),
Self::Iterm(printer) => TerminalImage::Iterm(printer.register(spec)?),
Self::Ascii(printer) => TerminalImage::Ascii(printer.register(spec)?),
Self::Null => return Err(RegisterImageError::Unsupported),
#[cfg(feature = "sixel")]
Self::Sixel(printer) => TerminalImage::Sixel(printer.register(image)?),
};
Ok(image)
}
fn register_from_path<P: AsRef<Path>>(&self, path: P) -> Result<Self::Image, RegisterImageError> {
let image = match self {
Self::Kitty(printer) => TerminalImage::Kitty(printer.register_from_path(path)?),
Self::Iterm(printer) => TerminalImage::Iterm(printer.register_from_path(path)?),
Self::Ascii(printer) => TerminalImage::Ascii(printer.register_from_path(path)?),
Self::Null => return Err(RegisterImageError::Unsupported),
#[cfg(feature = "sixel")]
Self::Sixel(printer) => TerminalImage::Sixel(printer.register_from_path(path)?),
Self::Sixel(printer) => TerminalImage::Sixel(printer.register(spec)?),
};
Ok(image)
}
@ -161,11 +148,21 @@ impl PrintImage for ImagePrinter {
}
#[derive(Clone, Default)]
pub(crate) struct ImageRegistry(pub Arc<ImagePrinter>);
pub(crate) struct ImageRegistry {
printer: Arc<ImagePrinter>,
images: Arc<Mutex<HashMap<PathBuf, Image>>>,
ascii_images: Arc<Mutex<HashMap<PathBuf, AsciiImage>>>,
}
impl ImageRegistry {
pub fn new(printer: Arc<ImagePrinter>) -> Self {
Self { printer, images: Default::default(), ascii_images: Default::default() }
}
}
impl fmt::Debug for ImageRegistry {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let inner = match self.0.as_ref() {
let inner = match self.printer.as_ref() {
ImagePrinter::Kitty(_) => "Kitty",
ImagePrinter::Iterm(_) => "Iterm",
ImagePrinter::Ascii(_) => "Ascii",
@ -178,17 +175,61 @@ impl fmt::Debug for ImageRegistry {
}
impl ImageRegistry {
pub(crate) fn register_image(&self, image: DynamicImage) -> Result<Image, RegisterImageError> {
let resource = self.0.register(image)?;
let image = Image::new(resource, ImageSource::Generated);
pub(crate) fn register(&self, spec: ImageSpec) -> Result<Image, RegisterImageError> {
let mut images = self.images.lock().unwrap();
let (source, cache_key) = match &spec {
ImageSpec::Generated(_) => (ImageSource::Generated, None),
ImageSpec::Filesystem(path) => {
// Return if already cached
if let Some(image) = images.get(path) {
return Ok(image.clone());
}
(ImageSource::Filesystem(path.clone()), Some(path.clone()))
}
};
let resource = self.printer.register(spec)?;
let image = Image::new(resource, source);
if let Some(key) = cache_key {
images.insert(key.clone(), image.clone());
drop(images);
if let TerminalImage::Ascii(image) = image.image.as_ref() {
self.ascii_images.lock().unwrap().insert(key, image.clone());
}
}
Ok(image)
}
pub(crate) fn register_resource(&self, path: PathBuf) -> Result<Image, RegisterImageError> {
let resource = self.0.register_from_path(&path)?;
let image = Image::new(resource, ImageSource::Filesystem(path));
Ok(image)
pub(crate) fn clear(&self) {
self.images.lock().unwrap().clear();
self.ascii_images.lock().unwrap().clear();
}
pub(crate) fn as_ascii(&self, image: &Image) -> AsciiImage {
if let ImageSource::Filesystem(path) = &image.source {
if let Some(image) = self.ascii_images.lock().unwrap().get(path) {
return image.clone();
}
if let Some(TerminalImage::Ascii(image)) = self.images.lock().unwrap().get(path).map(|i| i.image.as_ref()) {
return image.clone();
}
}
let ascii_image = match image.image.deref() {
TerminalImage::Ascii(image) => image.clone(),
TerminalImage::Kitty(image) => DynamicImage::from(image.as_rgba8()).into(),
TerminalImage::Iterm(image) => DynamicImage::from(image.as_rgba8()).into(),
#[cfg(feature = "sixel")]
TerminalImage::Sixel(image) => DynamicImage::from(image.as_rgba8()).into(),
};
if let ImageSource::Filesystem(path) = &image.source {
self.ascii_images.lock().unwrap().insert(path.clone(), ascii_image.clone());
}
ascii_image
}
}
pub(crate) enum ImageSpec {
Generated(DynamicImage),
Filesystem(PathBuf),
}
#[derive(Debug, thiserror::Error)]

View File

@ -1,29 +1,38 @@
use crate::{
markdown::text_style::{Color, Colors, TextStyle},
terminal::{
image::printer::{ImageProperties, PrintImage, PrintImageError, PrintOptions, RegisterImageError},
image::printer::{ImageProperties, ImageSpec, PrintImage, PrintImageError, PrintOptions, RegisterImageError},
printer::{TerminalCommand, TerminalIo},
},
};
use image::{DynamicImage, GenericImageView, Pixel, Rgba, imageops::FilterType};
use image::{DynamicImage, GenericImageView, Pixel, Rgba, RgbaImage, imageops::FilterType};
use itertools::Itertools;
use std::{fs, ops::Deref};
use std::{
collections::HashMap,
fs,
ops::Deref,
sync::{Arc, Mutex},
};
const TOP_CHAR: &str = "";
const BOTTOM_CHAR: &str = "";
pub(crate) struct AsciiImage(DynamicImage);
#[derive(Clone)]
pub(crate) struct AsciiImage {
image: Arc<DynamicImage>,
cached_sizes: Arc<Mutex<HashMap<(u16, u16), RgbaImage>>>,
}
impl ImageProperties for AsciiImage {
fn dimensions(&self) -> (u32, u32) {
self.0.dimensions()
self.image.dimensions()
}
}
impl From<DynamicImage> for AsciiImage {
fn from(image: DynamicImage) -> Self {
let image = image.into_rgba8();
Self(image.into())
Self { image: Arc::new(image.into()), cached_sizes: Default::default() }
}
}
@ -31,7 +40,7 @@ impl Deref for AsciiImage {
type Target = DynamicImage;
fn deref(&self) -> &Self::Target {
&self.0
&self.image
}
}
@ -65,25 +74,37 @@ impl AsciiPrinter {
impl PrintImage for AsciiPrinter {
type Image = AsciiImage;
fn register(&self, image: image::DynamicImage) -> Result<Self::Image, RegisterImageError> {
Ok(AsciiImage(image))
}
fn register_from_path<P: AsRef<std::path::Path>>(&self, path: P) -> Result<Self::Image, RegisterImageError> {
let contents = fs::read(path)?;
let image = image::load_from_memory(&contents)?;
Ok(AsciiImage(image))
fn register(&self, spec: ImageSpec) -> Result<Self::Image, RegisterImageError> {
let image = match spec {
ImageSpec::Generated(image) => image,
ImageSpec::Filesystem(path) => {
let contents = fs::read(path)?;
image::load_from_memory(&contents)?
}
};
Ok(AsciiImage::from(image))
}
fn print<T>(&self, image: &Self::Image, options: &PrintOptions, terminal: &mut T) -> Result<(), PrintImageError>
where
T: TerminalIo,
{
let columns = options.columns;
let rows = options.rows * 2;
let mut cached_sizes = image.cached_sizes.lock().unwrap();
// lookup on cache/resize the image and store it in cache
let cache_key = (columns, rows);
let image = match cached_sizes.get(&cache_key) {
Some(image) => image,
None => {
let image = image.image.resize_exact(columns as u32, rows as u32, FilterType::Triangle);
cached_sizes.insert(cache_key, image.into_rgba8());
cached_sizes.get(&cache_key).unwrap()
}
};
// The strategy here is taken from viuer: use half vertical ascii blocks in combination
// with foreground/background colors to fit 2 vertical pixels per cell. That is, cell (x, y)
// will contain the pixels at (x, y) and (x, y + 1) combined.
let image = image.0.resize_exact(options.columns as u32, 2 * options.rows as u32, FilterType::Triangle);
let image = image.into_rgba8();
let default_background = options.background_color.map(Color::from);
// Iterate pixel rows in pairs to be able to merge both pixels in a single iteration.

View File

@ -1,10 +1,10 @@
use crate::terminal::{
image::printer::{ImageProperties, PrintImage, PrintImageError, PrintOptions, RegisterImageError},
image::printer::{ImageProperties, ImageSpec, PrintImage, PrintImageError, PrintOptions, RegisterImageError},
printer::{TerminalCommand, TerminalIo},
};
use base64::{Engine, engine::general_purpose::STANDARD};
use image::{GenericImageView, ImageEncoder, RgbaImage, codecs::png::PngEncoder};
use std::{fs, path::Path};
use std::fs;
pub(crate) struct ItermImage {
dimensions: (u32, u32),
@ -38,18 +38,21 @@ pub struct ItermPrinter;
impl PrintImage for ItermPrinter {
type Image = ItermImage;
fn register(&self, image: image::DynamicImage) -> Result<Self::Image, RegisterImageError> {
let dimensions = image.dimensions();
let mut contents = Vec::new();
let encoder = PngEncoder::new(&mut contents);
encoder.write_image(image.as_bytes(), dimensions.0, dimensions.1, image.color().into())?;
Ok(ItermImage::new(contents, dimensions))
}
fn register_from_path<P: AsRef<Path>>(&self, path: P) -> Result<Self::Image, RegisterImageError> {
let contents = fs::read(path)?;
let image = image::load_from_memory(&contents)?;
Ok(ItermImage::new(contents, image.dimensions()))
fn register(&self, spec: ImageSpec) -> Result<Self::Image, RegisterImageError> {
match spec {
ImageSpec::Generated(image) => {
let dimensions = image.dimensions();
let mut contents = Vec::new();
let encoder = PngEncoder::new(&mut contents);
encoder.write_image(image.as_bytes(), dimensions.0, dimensions.1, image.color().into())?;
Ok(ItermImage::new(contents, dimensions))
}
ImageSpec::Filesystem(path) => {
let contents = fs::read(path)?;
let image = image::load_from_memory(&contents)?;
Ok(ItermImage::new(contents, image.dimensions()))
}
}
}
fn print<T>(&self, image: &Self::Image, options: &PrintOptions, terminal: &mut T) -> Result<(), PrintImageError>

View File

@ -1,12 +1,12 @@
use crate::{
markdown::text_style::{Color, TextStyle},
terminal::{
image::printer::{ImageProperties, PrintImage, PrintImageError, PrintOptions, RegisterImageError},
image::printer::{ImageProperties, ImageSpec, PrintImage, PrintImageError, PrintOptions, RegisterImageError},
printer::{TerminalCommand, TerminalIo},
},
};
use base64::{Engine, engine::general_purpose::STANDARD};
use image::{AnimationDecoder, Delay, DynamicImage, EncodableLayout, ImageReader, RgbaImage, codecs::gif::GifDecoder};
use image::{AnimationDecoder, Delay, EncodableLayout, ImageReader, RgbaImage, codecs::gif::GifDecoder};
use std::{
fmt,
fs::{self, File},
@ -395,20 +395,14 @@ impl KittyPrinter {
impl PrintImage for KittyPrinter {
type Image = KittyImage;
fn register(&self, image: DynamicImage) -> Result<Self::Image, RegisterImageError> {
let resource = RawResource::Image(image.into_rgba8());
let resource = match &self.mode {
KittyMode::Local => self.persist_resource(resource)?,
KittyMode::Remote => resource.into_memory_resource(),
fn register(&self, spec: ImageSpec) -> Result<Self::Image, RegisterImageError> {
let image = match spec {
ImageSpec::Generated(image) => RawResource::Image(image.into_rgba8()),
ImageSpec::Filesystem(path) => Self::load_raw_resource(&path)?,
};
Ok(resource)
}
fn register_from_path<P: AsRef<Path>>(&self, path: P) -> Result<Self::Image, RegisterImageError> {
let resource = Self::load_raw_resource(path.as_ref())?;
let resource = match &self.mode {
KittyMode::Local => self.persist_resource(resource)?,
KittyMode::Remote => resource.into_memory_resource(),
KittyMode::Local => self.persist_resource(image)?,
KittyMode::Remote => image.into_memory_resource(),
};
Ok(resource)
}

View File

@ -1,6 +1,6 @@
use crate::terminal::{
image::printer::{
CreatePrinterError, ImageProperties, PrintImage, PrintImageError, PrintOptions, RegisterImageError,
CreatePrinterError, ImageProperties, ImageSpec, PrintImage, PrintImageError, PrintOptions, RegisterImageError,
},
printer::{TerminalCommand, TerminalIo},
};
@ -38,14 +38,15 @@ impl SixelPrinter {
impl PrintImage for SixelPrinter {
type Image = SixelImage;
fn register(&self, image: image::DynamicImage) -> Result<Self::Image, RegisterImageError> {
Ok(SixelImage(image))
}
fn register_from_path<P: AsRef<std::path::Path>>(&self, path: P) -> Result<Self::Image, RegisterImageError> {
let contents = fs::read(path)?;
let image = image::load_from_memory(&contents)?;
Ok(SixelImage(image))
fn register(&self, spec: ImageSpec) -> Result<Self::Image, RegisterImageError> {
match spec {
ImageSpec::Generated(image) => Ok(SixelImage(image)),
ImageSpec::Filesystem(path) => {
let contents = fs::read(path)?;
let image = image::load_from_memory(&contents)?;
Ok(SixelImage(image))
}
}
}
fn print<T>(&self, image: &Self::Image, options: &PrintOptions, terminal: &mut T) -> Result<(), PrintImageError>

View File

@ -1,21 +1,20 @@
use super::{
image::{
Image,
printer::{PrintImage, PrintImageError, PrintOptions, TerminalImage},
printer::{PrintImage, PrintImageError, PrintOptions},
protocols::ascii::AsciiPrinter,
},
printer::{TerminalError, TerminalIo},
};
use crate::{
WindowSize,
ImageRegistry, WindowSize,
markdown::{
elements::Text,
text_style::{Color, Colors, TextStyle},
},
terminal::printer::TerminalCommand,
};
use image::DynamicImage;
use std::{collections::HashMap, io, ops::Deref};
use std::{collections::HashMap, io};
#[derive(Clone, Debug, PartialEq)]
pub(crate) struct PrintedImage {
@ -188,24 +187,10 @@ impl VirtualTerminal {
let image = PrintedImage { image: image.clone(), width_columns: options.columns };
self.images.insert(key, image);
}
ImageBehavior::PrintAscii => {
ImageBehavior::PrintAscii(registry) => {
let image = registry.as_ascii(image);
let image_printer = AsciiPrinter;
match image.image.deref() {
TerminalImage::Kitty(image) => {
let image = DynamicImage::from(image.as_rgba8());
image_printer.print(&image.into(), options, self)
}
TerminalImage::Iterm(image) => {
let image = DynamicImage::from(image.as_rgba8());
image_printer.print(&image.into(), options, self)
}
TerminalImage::Ascii(image) => image_printer.print(image, options, self),
#[cfg(feature = "sixel")]
TerminalImage::Sixel(image) => {
let image = DynamicImage::from(image.as_rgba8());
image_printer.print(&image.into(), options, self)
}
}?;
image_printer.print(&image, options, self)?
}
};
Ok(())
@ -243,7 +228,7 @@ impl TerminalIo for VirtualTerminal {
pub(crate) enum ImageBehavior {
#[default]
Store,
PrintAscii,
PrintAscii(ImageRegistry),
}
#[derive(Clone, Copy, Debug, PartialEq)]

View File

@ -12,7 +12,10 @@ use crate::{
},
properties::WindowSize,
},
terminal::image::{Image, printer::RegisterImageError},
terminal::image::{
Image,
printer::{ImageSpec, RegisterImageError},
},
theme::{Alignment, MermaidStyle, PresentationTheme, TypstStyle, raw::RawColor},
tools::{ExecutionError, ThirdPartyTools},
};
@ -269,7 +272,7 @@ impl Worker {
fn load_image(&self, snippet: ImageSnippet, path: &Path) -> Result<Image, ThirdPartyRenderError> {
let contents = fs::read(path)?;
let image = image::load_from_memory(&contents)?;
let image = self.state.lock().unwrap().image_registry.register_image(image)?;
let image = self.state.lock().unwrap().image_registry.register(ImageSpec::Generated(image))?;
self.state.lock().unwrap().cache.insert(snippet, image.clone());
Ok(image)
}

View File

@ -17,7 +17,10 @@ use crate::{
},
terminal::{
ansi::AnsiSplitter,
image::{Image, printer::ImageRegistry},
image::{
Image,
printer::{ImageRegistry, ImageSpec},
},
should_hide_cursor,
},
theme::{Alignment, ExecutionOutputBlockStyle, ExecutionStatusBlockStyle, Margin},
@ -404,7 +407,7 @@ impl RunImageSnippet {
return Err(e.to_string());
}
};
self.image_registry.register_image(image).map_err(|e| e.to_string())
self.image_registry.register(ImageSpec::Generated(image)).map_err(|e| e.to_string())
}
}