mirror of
https://github.com/mfontanini/presenterm.git
synced 2025-05-05 15:32:58 +00:00
perf: cache generated images as ascii
This commit is contained in:
parent
a97e66fedf
commit
5eee8a9fae
@ -97,13 +97,14 @@ impl ContentManager {
|
|||||||
ImageSource::Filesystem(path) => Ok(path),
|
ImageSource::Filesystem(path) => Ok(path),
|
||||||
ImageSource::Generated => {
|
ImageSource::Generated => {
|
||||||
let mut buffer = Vec::new();
|
let mut buffer = Vec::new();
|
||||||
let dimensions = image.dimensions();
|
let dimensions = image.image().dimensions();
|
||||||
let TerminalImage::Ascii(resource) = image.image.as_ref() else { panic!("not in ascii mode") };
|
let TerminalImage::Ascii(image) = image.image() else { panic!("not in ascii mode") };
|
||||||
|
let image = image.image();
|
||||||
PngEncoder::new(&mut buffer).write_image(
|
PngEncoder::new(&mut buffer).write_image(
|
||||||
resource.as_bytes(),
|
image.as_bytes(),
|
||||||
dimensions.0,
|
dimensions.0,
|
||||||
dimensions.1,
|
dimensions.1,
|
||||||
resource.color().into(),
|
image.color().into(),
|
||||||
)?;
|
)?;
|
||||||
let name = format!("img-{}.png", self.image_count);
|
let name = format!("img-{}.png", self.image_count);
|
||||||
let path = self.output_directory.path().join(name);
|
let path = self.output_directory.path().join(name);
|
||||||
|
@ -361,7 +361,7 @@ impl<'a> Presenter<'a> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let options = RenderEngineOptions { max_size: self.options.max_size.clone(), ..Default::default() };
|
let options = RenderEngineOptions { max_size: self.options.max_size.clone(), ..Default::default() };
|
||||||
let scaler = AsciiScaler::new(options, self.resources.image_registry());
|
let scaler = AsciiScaler::new(options);
|
||||||
let dimensions = WindowSize::current(self.options.font_size_fallback)?;
|
let dimensions = WindowSize::current(self.options.font_size_fallback)?;
|
||||||
scaler.process(self.state.presentation(), &dimensions)?;
|
scaler.process(self.state.presentation(), &dimensions)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -440,14 +440,13 @@ impl<'a> Presenter<'a> {
|
|||||||
let Some(config) = self.options.transition.clone() else {
|
let Some(config) = self.options.transition.clone() else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
let registry = self.resources.image_registry();
|
|
||||||
let options = drawer.render_engine_options();
|
let options = drawer.render_engine_options();
|
||||||
let presentation = self.state.presentation_mut();
|
let presentation = self.state.presentation_mut();
|
||||||
let dimensions = WindowSize::current(self.options.font_size_fallback)?;
|
let dimensions = WindowSize::current(self.options.font_size_fallback)?;
|
||||||
presentation.jump_previous();
|
presentation.jump_previous();
|
||||||
let left = Self::virtual_render(presentation.current_slide(), dimensions.clone(), &options, registry.clone())?;
|
let left = Self::virtual_render(presentation.current_slide(), dimensions.clone(), &options)?;
|
||||||
presentation.jump_next();
|
presentation.jump_next();
|
||||||
let right = Self::virtual_render(presentation.current_slide(), dimensions.clone(), &options, registry)?;
|
let right = Self::virtual_render(presentation.current_slide(), dimensions.clone(), &options)?;
|
||||||
let direction = TransitionDirection::Next;
|
let direction = TransitionDirection::Next;
|
||||||
self.animate_transition(drawer, left, right, direction, dimensions, config)
|
self.animate_transition(drawer, left, right, direction, dimensions, config)
|
||||||
}
|
}
|
||||||
@ -456,14 +455,13 @@ impl<'a> Presenter<'a> {
|
|||||||
let Some(config) = self.options.transition.clone() else {
|
let Some(config) = self.options.transition.clone() else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
let registry = self.resources.image_registry();
|
|
||||||
let options = drawer.render_engine_options();
|
let options = drawer.render_engine_options();
|
||||||
let presentation = self.state.presentation_mut();
|
let presentation = self.state.presentation_mut();
|
||||||
let dimensions = WindowSize::current(self.options.font_size_fallback)?;
|
let dimensions = WindowSize::current(self.options.font_size_fallback)?;
|
||||||
presentation.jump_next();
|
presentation.jump_next();
|
||||||
let right = Self::virtual_render(presentation.current_slide(), dimensions.clone(), &options, registry.clone())?;
|
let right = Self::virtual_render(presentation.current_slide(), dimensions.clone(), &options)?;
|
||||||
presentation.jump_previous();
|
presentation.jump_previous();
|
||||||
let left = Self::virtual_render(presentation.current_slide(), dimensions.clone(), &options, registry)?;
|
let left = Self::virtual_render(presentation.current_slide(), dimensions.clone(), &options)?;
|
||||||
let direction = TransitionDirection::Previous;
|
let direction = TransitionDirection::Previous;
|
||||||
self.animate_transition(drawer, left, right, direction, dimensions, config)
|
self.animate_transition(drawer, left, right, direction, dimensions, config)
|
||||||
}
|
}
|
||||||
@ -543,9 +541,8 @@ impl<'a> Presenter<'a> {
|
|||||||
slide: &Slide,
|
slide: &Slide,
|
||||||
dimensions: WindowSize,
|
dimensions: WindowSize,
|
||||||
options: &RenderEngineOptions,
|
options: &RenderEngineOptions,
|
||||||
registry: ImageRegistry,
|
|
||||||
) -> Result<TerminalGrid, RenderError> {
|
) -> Result<TerminalGrid, RenderError> {
|
||||||
let mut term = VirtualTerminal::new(dimensions.clone(), ImageBehavior::PrintAscii(registry));
|
let mut term = VirtualTerminal::new(dimensions.clone(), ImageBehavior::PrintAscii);
|
||||||
let engine = RenderEngine::new(&mut term, dimensions.clone(), options.clone());
|
let engine = RenderEngine::new(&mut term, dimensions.clone(), options.clone());
|
||||||
engine.render(slide.iter_visible_operations())?;
|
engine.render(slide.iter_visible_operations())?;
|
||||||
Ok(term.into_contents())
|
Ok(term.into_contents())
|
||||||
|
@ -3,10 +3,10 @@ use super::{
|
|||||||
engine::{RenderEngine, RenderEngineOptions},
|
engine::{RenderEngine, RenderEngineOptions},
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
ImageRegistry, WindowSize,
|
WindowSize,
|
||||||
presentation::Presentation,
|
presentation::Presentation,
|
||||||
terminal::{
|
terminal::{
|
||||||
image::{Image, ImageSource},
|
image::Image,
|
||||||
printer::{TerminalCommand, TerminalError, TerminalIo},
|
printer::{TerminalCommand, TerminalError, TerminalIo},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -15,12 +15,11 @@ use unicode_width::UnicodeWidthStr;
|
|||||||
|
|
||||||
pub(crate) struct AsciiScaler {
|
pub(crate) struct AsciiScaler {
|
||||||
options: RenderEngineOptions,
|
options: RenderEngineOptions,
|
||||||
registry: ImageRegistry,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsciiScaler {
|
impl AsciiScaler {
|
||||||
pub(crate) fn new(options: RenderEngineOptions, registry: ImageRegistry) -> Self {
|
pub(crate) fn new(options: RenderEngineOptions) -> Self {
|
||||||
Self { options, registry }
|
Self { options }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn process(self, presentation: &Presentation, dimensions: &WindowSize) -> Result<(), RenderError> {
|
pub(crate) fn process(self, presentation: &Presentation, dimensions: &WindowSize) -> Result<(), RenderError> {
|
||||||
@ -29,13 +28,13 @@ impl AsciiScaler {
|
|||||||
let engine = RenderEngine::new(&mut collector, dimensions.clone(), self.options.clone());
|
let engine = RenderEngine::new(&mut collector, dimensions.clone(), self.options.clone());
|
||||||
engine.render(slide.iter_operations())?;
|
engine.render(slide.iter_operations())?;
|
||||||
}
|
}
|
||||||
thread::spawn(move || Self::scale(collector.images, self.registry));
|
thread::spawn(move || Self::scale(collector.images));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scale(images: Vec<ScalableImage>, registry: ImageRegistry) {
|
fn scale(images: Vec<ScalableImage>) {
|
||||||
for image in images {
|
for image in images {
|
||||||
let ascii_image = registry.as_ascii(&image.image);
|
let ascii_image = image.image.to_ascii();
|
||||||
ascii_image.cache_scaling(image.columns, image.rows);
|
ascii_image.cache_scaling(image.columns, image.rows);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,11 +83,8 @@ impl TerminalIo for ImageCollector {
|
|||||||
}
|
}
|
||||||
PrintImage { image, options } => {
|
PrintImage { image, options } => {
|
||||||
// we can only really cache filesystem images for now
|
// we can only really cache filesystem images for now
|
||||||
if matches!(image.source, ImageSource::Filesystem(_)) {
|
let image = ScalableImage { image: image.clone(), rows: options.rows * 2, columns: options.columns };
|
||||||
let image =
|
self.images.push(image);
|
||||||
ScalableImage { image: image.clone(), rows: options.rows * 2, columns: options.columns };
|
|
||||||
self.images.push(image);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ClearScreen => {
|
ClearScreen => {
|
||||||
self.current_column = 0;
|
self.current_column = 0;
|
||||||
|
@ -258,7 +258,7 @@ where
|
|||||||
let starting_cursor =
|
let starting_cursor =
|
||||||
CursorPosition { row: starting_row.saturating_sub(rect.start_row), column: rect.start_column };
|
CursorPosition { row: starting_row.saturating_sub(rect.start_row), column: rect.start_column };
|
||||||
|
|
||||||
let (width, height) = image.dimensions();
|
let (width, height) = image.image().dimensions();
|
||||||
let (columns, rows) = match properties.size {
|
let (columns, rows) = match properties.size {
|
||||||
ImageSize::ShrinkIfNeeded => {
|
ImageSize::ShrinkIfNeeded => {
|
||||||
let image_scale =
|
let image_scale =
|
||||||
|
@ -133,10 +133,6 @@ impl Resources {
|
|||||||
inner.image_registry.clear();
|
inner.image_registry.clear();
|
||||||
inner.themes.clear();
|
inner.themes.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn image_registry(&self) -> ImageRegistry {
|
|
||||||
self.inner.borrow().image_registry.clone()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Watches for file changes.
|
/// Watches for file changes.
|
||||||
|
@ -1,23 +1,59 @@
|
|||||||
|
use image::DynamicImage;
|
||||||
|
use protocols::ascii::AsciiImage;
|
||||||
|
|
||||||
use self::printer::{ImageProperties, TerminalImage};
|
use self::printer::{ImageProperties, TerminalImage};
|
||||||
use std::{fmt::Debug, ops::Deref, path::PathBuf, sync::Arc};
|
use std::{
|
||||||
|
fmt::Debug,
|
||||||
|
ops::Deref,
|
||||||
|
path::PathBuf,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
};
|
||||||
|
|
||||||
pub(crate) mod printer;
|
pub(crate) mod printer;
|
||||||
pub(crate) mod protocols;
|
pub(crate) mod protocols;
|
||||||
pub(crate) mod scale;
|
pub(crate) mod scale;
|
||||||
|
|
||||||
|
struct Inner {
|
||||||
|
image: TerminalImage,
|
||||||
|
ascii_image: Mutex<Option<AsciiImage>>,
|
||||||
|
}
|
||||||
|
|
||||||
/// An image.
|
/// An image.
|
||||||
///
|
///
|
||||||
/// This stores the image in an [std::sync::Arc] so it's cheap to clone.
|
/// This stores the image in an [std::sync::Arc] so it's cheap to clone.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) struct Image {
|
pub(crate) struct Image {
|
||||||
pub(crate) image: Arc<TerminalImage>,
|
inner: Arc<Inner>,
|
||||||
pub(crate) source: ImageSource,
|
pub(crate) source: ImageSource,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Image {
|
impl Image {
|
||||||
/// Constructs a new image.
|
/// Constructs a new image.
|
||||||
pub(crate) fn new(image: TerminalImage, source: ImageSource) -> Self {
|
pub(crate) fn new(image: TerminalImage, source: ImageSource) -> Self {
|
||||||
Self { image: Arc::new(image), source }
|
let inner = Inner { image, ascii_image: Default::default() };
|
||||||
|
Self { inner: Arc::new(inner), source }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn to_ascii(&self) -> AsciiImage {
|
||||||
|
let mut ascii_image = self.inner.ascii_image.lock().unwrap();
|
||||||
|
match ascii_image.deref() {
|
||||||
|
Some(image) => image.clone(),
|
||||||
|
None => {
|
||||||
|
let image = match &self.inner.image {
|
||||||
|
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(),
|
||||||
|
};
|
||||||
|
*ascii_image = Some(image.clone());
|
||||||
|
image
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn image(&self) -> &TerminalImage {
|
||||||
|
&self.inner.image
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,19 +65,11 @@ impl PartialEq for Image {
|
|||||||
|
|
||||||
impl Debug for Image {
|
impl Debug for Image {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let (width, height) = self.image.dimensions();
|
let (width, height) = self.inner.image.dimensions();
|
||||||
write!(f, "Image<{width}x{height}>")
|
write!(f, "Image<{width}x{height}>")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for Image {
|
|
||||||
type Target = TerminalImage;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.image
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub(crate) enum ImageSource {
|
pub(crate) enum ImageSource {
|
||||||
Filesystem(PathBuf),
|
Filesystem(PathBuf),
|
||||||
|
@ -18,7 +18,6 @@ use std::{
|
|||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fmt, io,
|
fmt, io,
|
||||||
ops::Deref,
|
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
@ -151,12 +150,11 @@ impl PrintImage for ImagePrinter {
|
|||||||
pub(crate) struct ImageRegistry {
|
pub(crate) struct ImageRegistry {
|
||||||
printer: Arc<ImagePrinter>,
|
printer: Arc<ImagePrinter>,
|
||||||
images: Arc<Mutex<HashMap<PathBuf, Image>>>,
|
images: Arc<Mutex<HashMap<PathBuf, Image>>>,
|
||||||
ascii_images: Arc<Mutex<HashMap<PathBuf, AsciiImage>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ImageRegistry {
|
impl ImageRegistry {
|
||||||
pub fn new(printer: Arc<ImagePrinter>) -> Self {
|
pub fn new(printer: Arc<ImagePrinter>) -> Self {
|
||||||
Self { printer, images: Default::default(), ascii_images: Default::default() }
|
Self { printer, images: Default::default() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,39 +189,12 @@ impl ImageRegistry {
|
|||||||
let image = Image::new(resource, source);
|
let image = Image::new(resource, source);
|
||||||
if let Some(key) = cache_key {
|
if let Some(key) = cache_key {
|
||||||
images.insert(key.clone(), image.clone());
|
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)
|
Ok(image)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn clear(&self) {
|
pub(crate) fn clear(&self) {
|
||||||
self.images.lock().unwrap().clear();
|
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,49 +10,49 @@ use itertools::Itertools;
|
|||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fs,
|
fs,
|
||||||
ops::Deref,
|
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
const TOP_CHAR: &str = "▀";
|
const TOP_CHAR: &str = "▀";
|
||||||
const BOTTOM_CHAR: &str = "▄";
|
const BOTTOM_CHAR: &str = "▄";
|
||||||
|
|
||||||
|
struct Inner {
|
||||||
|
image: DynamicImage,
|
||||||
|
cached_sizes: Mutex<HashMap<(u16, u16), RgbaImage>>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) struct AsciiImage {
|
pub(crate) struct AsciiImage {
|
||||||
image: Arc<DynamicImage>,
|
inner: Arc<Inner>,
|
||||||
cached_sizes: Arc<Mutex<HashMap<(u16, u16), RgbaImage>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsciiImage {
|
impl AsciiImage {
|
||||||
pub(crate) fn cache_scaling(&self, columns: u16, rows: u16) {
|
pub(crate) fn cache_scaling(&self, columns: u16, rows: u16) {
|
||||||
let mut cached_sizes = self.cached_sizes.lock().unwrap();
|
let mut cached_sizes = self.inner.cached_sizes.lock().unwrap();
|
||||||
// lookup on cache/resize the image and store it in cache
|
// lookup on cache/resize the image and store it in cache
|
||||||
let cache_key = (columns, rows);
|
let cache_key = (columns, rows);
|
||||||
if cached_sizes.get(&cache_key).is_none() {
|
if cached_sizes.get(&cache_key).is_none() {
|
||||||
let image = self.image.resize_exact(columns as u32, rows as u32, FilterType::Triangle);
|
let image = self.inner.image.resize_exact(columns as u32, rows as u32, FilterType::Triangle);
|
||||||
cached_sizes.insert(cache_key, image.into_rgba8());
|
cached_sizes.insert(cache_key, image.into_rgba8());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn image(&self) -> &DynamicImage {
|
||||||
|
&self.inner.image
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ImageProperties for AsciiImage {
|
impl ImageProperties for AsciiImage {
|
||||||
fn dimensions(&self) -> (u32, u32) {
|
fn dimensions(&self) -> (u32, u32) {
|
||||||
self.image.dimensions()
|
self.inner.image.dimensions()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<DynamicImage> for AsciiImage {
|
impl From<DynamicImage> for AsciiImage {
|
||||||
fn from(image: DynamicImage) -> Self {
|
fn from(image: DynamicImage) -> Self {
|
||||||
let image = image.into_rgba8();
|
let image = image.into_rgba8();
|
||||||
Self { image: Arc::new(image.into()), cached_sizes: Default::default() }
|
let inner = Inner { image: image.into(), cached_sizes: Default::default() };
|
||||||
}
|
Self { inner: Arc::new(inner) }
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for AsciiImage {
|
|
||||||
type Target = DynamicImage;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.image
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,7 +108,7 @@ impl PrintImage for AsciiPrinter {
|
|||||||
|
|
||||||
// lookup on cache/resize the image and store it in cache
|
// lookup on cache/resize the image and store it in cache
|
||||||
let cache_key = (columns, rows);
|
let cache_key = (columns, rows);
|
||||||
let cached_sizes = image.cached_sizes.lock().unwrap();
|
let cached_sizes = image.inner.cached_sizes.lock().unwrap();
|
||||||
let image = cached_sizes.get(&cache_key).expect("scaled image no longer there");
|
let image = cached_sizes.get(&cache_key).expect("scaled image no longer there");
|
||||||
|
|
||||||
let default_background = options.background_color.map(Color::from);
|
let default_background = options.background_color.map(Color::from);
|
||||||
|
@ -146,7 +146,7 @@ impl<I: TerminalWrite> Terminal<I> {
|
|||||||
|
|
||||||
fn print_image(&mut self, image: &Image, options: &PrintOptions) -> Result<(), PrintImageError> {
|
fn print_image(&mut self, image: &Image, options: &PrintOptions) -> Result<(), PrintImageError> {
|
||||||
let image_printer = self.image_printer.clone();
|
let image_printer = self.image_printer.clone();
|
||||||
image_printer.print(&image.image, options, self)?;
|
image_printer.print(image.image(), options, self)?;
|
||||||
self.cursor_row += options.rows;
|
self.cursor_row += options.rows;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ use super::{
|
|||||||
printer::{TerminalError, TerminalIo},
|
printer::{TerminalError, TerminalIo},
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
ImageRegistry, WindowSize,
|
WindowSize,
|
||||||
markdown::{
|
markdown::{
|
||||||
elements::Text,
|
elements::Text,
|
||||||
text_style::{Color, Colors, TextStyle},
|
text_style::{Color, Colors, TextStyle},
|
||||||
@ -187,8 +187,8 @@ impl VirtualTerminal {
|
|||||||
let image = PrintedImage { image: image.clone(), width_columns: options.columns };
|
let image = PrintedImage { image: image.clone(), width_columns: options.columns };
|
||||||
self.images.insert(key, image);
|
self.images.insert(key, image);
|
||||||
}
|
}
|
||||||
ImageBehavior::PrintAscii(registry) => {
|
ImageBehavior::PrintAscii => {
|
||||||
let image = registry.as_ascii(image);
|
let image = image.to_ascii();
|
||||||
let image_printer = AsciiPrinter;
|
let image_printer = AsciiPrinter;
|
||||||
image_printer.print(&image, options, self)?
|
image_printer.print(&image, options, self)?
|
||||||
}
|
}
|
||||||
@ -228,7 +228,7 @@ impl TerminalIo for VirtualTerminal {
|
|||||||
pub(crate) enum ImageBehavior {
|
pub(crate) enum ImageBehavior {
|
||||||
#[default]
|
#[default]
|
||||||
Store,
|
Store,
|
||||||
PrintAscii(ImageRegistry),
|
PrintAscii,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user