mirror of
https://github.com/wez/wezterm.git
synced 2024-12-23 05:12:40 +03:00
try scaling down images that don't fit in the texture atlas
When trying to display a 4k image there is a high chance that we'll run out of texture space and then render with no images displayed. This commit changes the binary yes/no allow-images into a a series of attempts: display at natural size, scale down by 2, 4, then 8, then give up on images. While looking at this, I noticed that we had a TOCTOU in the blob lease stuff in the case where we might very quickly try the handle the same image in succession, and end up deleting a file out from under a live lease. I've put in place a simple bandaid for that, but it's probably worth revisiting the concurrency model for that.
This commit is contained in:
parent
134bf0a74d
commit
4ae176dd8a
@ -9,7 +9,7 @@ pub enum Error {
|
||||
#[error("Content with id {0} not found")]
|
||||
ContentNotFound(ContentId),
|
||||
|
||||
#[error(transparent)]
|
||||
#[error("Io error in BlobLease: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
#[error("Storage has already been initialized")]
|
||||
|
@ -44,6 +44,7 @@ impl SimpleTempDir {
|
||||
eprintln!("Failed to remove {}: {err:#}", path.display());
|
||||
}
|
||||
}
|
||||
*count = 0;
|
||||
}
|
||||
Some(count) => {
|
||||
*count -= 1;
|
||||
@ -57,6 +58,8 @@ impl SimpleTempDir {
|
||||
|
||||
impl BlobStorage for SimpleTempDir {
|
||||
fn store(&self, content_id: ContentId, data: &[u8], _lease_id: LeaseId) -> Result<(), Error> {
|
||||
let mut refs = self.refs.lock().unwrap();
|
||||
|
||||
let path = self.path_for_content(content_id)?;
|
||||
let mut file = tempfile::Builder::new()
|
||||
.prefix("new-")
|
||||
@ -67,12 +70,14 @@ impl BlobStorage for SimpleTempDir {
|
||||
file.persist(&path)
|
||||
.map_err(|persist_err| persist_err.error)?;
|
||||
|
||||
self.add_ref(content_id);
|
||||
*refs.entry(content_id).or_insert(0) += 1;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn lease_by_content(&self, content_id: ContentId, _lease_id: LeaseId) -> Result<(), Error> {
|
||||
let _refs = self.refs.lock().unwrap();
|
||||
|
||||
let path = self.path_for_content(content_id)?;
|
||||
if path.exists() {
|
||||
self.add_ref(content_id);
|
||||
@ -83,6 +88,8 @@ impl BlobStorage for SimpleTempDir {
|
||||
}
|
||||
|
||||
fn get_data(&self, content_id: ContentId, _lease_id: LeaseId) -> Result<Vec<u8>, Error> {
|
||||
let _refs = self.refs.lock().unwrap();
|
||||
|
||||
let path = self.path_for_content(content_id)?;
|
||||
Ok(std::fs::read(&path)?)
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use super::utilsprites::RenderMetrics;
|
||||
use crate::customglyph::*;
|
||||
use crate::renderstate::RenderContext;
|
||||
use crate::termwindow::render::paint::AllowImage;
|
||||
use ::window::bitmaps::atlas::{Atlas, OutOfTextureSpace, Sprite};
|
||||
use ::window::bitmaps::{BitmapImage, Image, ImageTexture, Texture2d};
|
||||
use ::window::color::SrgbaPixel;
|
||||
@ -885,17 +886,26 @@ impl GlyphCache {
|
||||
decoded: &DecodedImage,
|
||||
padding: Option<usize>,
|
||||
min_frame_duration: Duration,
|
||||
allow_image: AllowImage,
|
||||
) -> anyhow::Result<(Sprite, Option<Instant>, LoadState)> {
|
||||
let mut handle = DecodedImageHandle {
|
||||
h: decoded.image.data(),
|
||||
current_frame: *decoded.current_frame.borrow(),
|
||||
};
|
||||
|
||||
let scale_down = match allow_image {
|
||||
AllowImage::Scale(n) => Some(n),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
match &*handle.h {
|
||||
ImageDataType::Rgba8 { hash, .. } => {
|
||||
if let Some(sprite) = frame_cache.get(hash) {
|
||||
return Ok((sprite.clone(), None, LoadState::Loaded));
|
||||
}
|
||||
let sprite = atlas.allocate_with_padding(&handle, padding)?;
|
||||
let sprite = atlas
|
||||
.allocate_with_padding(&handle, padding, scale_down)
|
||||
.context("atlas.allocate_with_padding")?;
|
||||
frame_cache.insert(*hash, sprite.clone());
|
||||
|
||||
return Ok((sprite, None, LoadState::Loaded));
|
||||
@ -949,7 +959,9 @@ impl GlyphCache {
|
||||
return Ok((sprite.clone(), next, LoadState::Loaded));
|
||||
}
|
||||
|
||||
let sprite = atlas.allocate_with_padding(&handle, padding)?;
|
||||
let sprite = atlas
|
||||
.allocate_with_padding(&handle, padding, scale_down)
|
||||
.context("atlas.allocate_with_padding")?;
|
||||
|
||||
frame_cache.insert(hash, sprite.clone());
|
||||
|
||||
@ -1005,9 +1017,13 @@ impl GlyphCache {
|
||||
let frame = Image::from_raw(
|
||||
frames.current_frame.width,
|
||||
frames.current_frame.height,
|
||||
frames.current_frame.lease.get_data()?,
|
||||
frames
|
||||
.current_frame
|
||||
.lease
|
||||
.get_data()
|
||||
.context("frames.current_frame.lease.get_data")?,
|
||||
);
|
||||
let sprite = atlas.allocate_with_padding(&frame, padding)?;
|
||||
let sprite = atlas.allocate_with_padding(&frame, padding, scale_down)?;
|
||||
|
||||
frame_cache.insert(hash, sprite.clone());
|
||||
|
||||
@ -1024,6 +1040,7 @@ impl GlyphCache {
|
||||
&mut self,
|
||||
image_data: &Arc<ImageData>,
|
||||
padding: Option<usize>,
|
||||
allow_image: AllowImage,
|
||||
) -> anyhow::Result<(Sprite, Option<Instant>, LoadState)> {
|
||||
let hash = image_data.hash();
|
||||
|
||||
@ -1034,6 +1051,7 @@ impl GlyphCache {
|
||||
decoded,
|
||||
padding,
|
||||
self.min_frame_duration,
|
||||
allow_image,
|
||||
)
|
||||
} else {
|
||||
let decoded = DecodedImage::load(image_data);
|
||||
@ -1043,6 +1061,7 @@ impl GlyphCache {
|
||||
&decoded,
|
||||
padding,
|
||||
self.min_frame_duration,
|
||||
allow_image,
|
||||
)?;
|
||||
self.image_cache.put(hash, decoded);
|
||||
Ok(res)
|
||||
|
@ -425,10 +425,11 @@ impl crate::TermWindow {
|
||||
|
||||
let color = bg_color.mul_alpha(layer.def.opacity);
|
||||
|
||||
let (sprite, next_due, load_state) = gl_state
|
||||
.glyph_cache
|
||||
.borrow_mut()
|
||||
.cached_image(&layer.source, None)?;
|
||||
let (sprite, next_due, load_state) = gl_state.glyph_cache.borrow_mut().cached_image(
|
||||
&layer.source,
|
||||
None,
|
||||
self.allow_images,
|
||||
)?;
|
||||
self.update_next_frame_time(next_due);
|
||||
|
||||
if load_state == LoadState::Loading {
|
||||
|
@ -19,6 +19,7 @@ use crate::termwindow::background::{
|
||||
};
|
||||
use crate::termwindow::keyevent::{KeyTableArgs, KeyTableState};
|
||||
use crate::termwindow::modal::Modal;
|
||||
use crate::termwindow::render::paint::AllowImage;
|
||||
use crate::termwindow::render::{
|
||||
CachedLineState, LineQuadCacheKey, LineQuadCacheValue, LineToEleShapeCacheKey,
|
||||
LineToElementShapeItem,
|
||||
@ -73,7 +74,7 @@ mod mouseevent;
|
||||
pub mod palette;
|
||||
pub mod paneselect;
|
||||
mod prevcursor;
|
||||
mod render;
|
||||
pub mod render;
|
||||
pub mod resize;
|
||||
mod selection;
|
||||
pub mod spawn;
|
||||
@ -434,7 +435,7 @@ pub struct TermWindow {
|
||||
has_animation: RefCell<Option<Instant>>,
|
||||
/// We use this to attempt to do something reasonable
|
||||
/// if we run out of texture space
|
||||
allow_images: bool,
|
||||
allow_images: AllowImage,
|
||||
scheduled_animation: RefCell<Option<Instant>>,
|
||||
|
||||
created: Instant,
|
||||
@ -762,7 +763,7 @@ impl TermWindow {
|
||||
current_event: None,
|
||||
has_animation: RefCell::new(None),
|
||||
scheduled_animation: RefCell::new(None),
|
||||
allow_images: true,
|
||||
allow_images: AllowImage::Yes,
|
||||
semantic_zones: HashMap::new(),
|
||||
ui_items: vec![],
|
||||
dragging: None,
|
||||
|
@ -6,11 +6,12 @@ use crate::quad::{
|
||||
TripleLayerQuadAllocatorTrait,
|
||||
};
|
||||
use crate::shapecache::*;
|
||||
use crate::termwindow::render::paint::AllowImage;
|
||||
use crate::termwindow::{BorrowedShapeCacheKey, RenderState, ShapedInfo, TermWindowNotif};
|
||||
use crate::utilsprites::RenderMetrics;
|
||||
use ::window::bitmaps::{TextureCoord, TextureRect, TextureSize};
|
||||
use ::window::{DeadKeyStatus, PointF, RectF, SizeF, WindowOps};
|
||||
use anyhow::anyhow;
|
||||
use anyhow::{anyhow, Context};
|
||||
use config::{BoldBrightening, ConfigHandle, DimensionContext, TextStyle, VisualBellTarget};
|
||||
use euclid::num::Zero;
|
||||
use mux::pane::{Pane, PaneId};
|
||||
@ -417,7 +418,7 @@ impl crate::TermWindow {
|
||||
hsv: Option<config::HsbTransform>,
|
||||
glyph_color: LinearRgba,
|
||||
) -> anyhow::Result<()> {
|
||||
if !self.allow_images {
|
||||
if self.allow_images == AllowImage::No {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@ -435,7 +436,8 @@ impl crate::TermWindow {
|
||||
let (sprite, next_due, _load_state) = gl_state
|
||||
.glyph_cache
|
||||
.borrow_mut()
|
||||
.cached_image(image.image_data(), Some(padding))?;
|
||||
.cached_image(image.image_data(), Some(padding), self.allow_images)
|
||||
.context("cached_image")?;
|
||||
self.update_next_frame_time(next_due);
|
||||
let width = sprite.coords.size.width;
|
||||
let height = sprite.coords.size.height;
|
||||
|
@ -6,6 +6,13 @@ use smol::Timer;
|
||||
use std::time::{Duration, Instant};
|
||||
use wezterm_font::ClearShapeCache;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum AllowImage {
|
||||
Yes,
|
||||
Scale(usize),
|
||||
No,
|
||||
}
|
||||
|
||||
impl crate::TermWindow {
|
||||
pub fn paint_impl(&mut self, frame: &mut RenderFrame) {
|
||||
self.num_frames += 1;
|
||||
@ -13,7 +20,7 @@ impl crate::TermWindow {
|
||||
// invalidating as frequently
|
||||
*self.has_animation.borrow_mut() = None;
|
||||
// Start with the assumption that we should allow images to render
|
||||
self.allow_images = true;
|
||||
self.allow_images = AllowImage::Yes;
|
||||
|
||||
let start = Instant::now();
|
||||
|
||||
@ -61,21 +68,27 @@ impl crate::TermWindow {
|
||||
self.invalidate_modal();
|
||||
|
||||
if let Err(err) = result {
|
||||
if self.allow_images {
|
||||
self.allow_images = false;
|
||||
log::info!(
|
||||
"Not enough texture space ({:#}); \
|
||||
will retry render with images disabled",
|
||||
err
|
||||
);
|
||||
} else {
|
||||
log::error!(
|
||||
"Failed to {} texture: {}",
|
||||
if pass == 0 { "clear" } else { "resize" },
|
||||
err
|
||||
);
|
||||
break 'pass;
|
||||
}
|
||||
self.allow_images = match self.allow_images {
|
||||
AllowImage::Yes => AllowImage::Scale(2),
|
||||
AllowImage::Scale(2) => AllowImage::Scale(4),
|
||||
AllowImage::Scale(4) => AllowImage::Scale(8),
|
||||
AllowImage::Scale(8) => AllowImage::No,
|
||||
AllowImage::No | _ => {
|
||||
log::error!(
|
||||
"Failed to {} texture: {}",
|
||||
if pass == 0 { "clear" } else { "resize" },
|
||||
err
|
||||
);
|
||||
break 'pass;
|
||||
}
|
||||
};
|
||||
|
||||
log::info!(
|
||||
"Not enough texture space ({:#}); \
|
||||
will retry render with {:?}",
|
||||
err,
|
||||
self.allow_images,
|
||||
);
|
||||
}
|
||||
} else if err.root_cause().downcast_ref::<ClearShapeCache>().is_some() {
|
||||
self.invalidate_fancy_tab_bar();
|
||||
@ -175,7 +188,7 @@ impl crate::TermWindow {
|
||||
|
||||
// Render the full window background
|
||||
match (self.window_background.is_empty(), self.allow_images) {
|
||||
(false, true) => {
|
||||
(false, AllowImage::Yes) => {
|
||||
let bg_color = self.palette().background.to_linear();
|
||||
|
||||
let top = panes
|
||||
|
@ -8,6 +8,7 @@ use crate::termwindow::render::{
|
||||
use crate::termwindow::{ScrollHit, UIItem, UIItemType};
|
||||
use ::window::bitmaps::TextureRect;
|
||||
use ::window::DeadKeyStatus;
|
||||
use anyhow::Context;
|
||||
use config::VisualBellTarget;
|
||||
use mux::pane::{PaneId, WithPaneLines};
|
||||
use mux::renderable::{RenderableDimensions, StableCursorPosition};
|
||||
@ -62,7 +63,8 @@ impl crate::TermWindow {
|
||||
let (padding_left, padding_top) = self.padding_left_top();
|
||||
|
||||
let tab_bar_height = if self.show_tab_bar {
|
||||
self.tab_bar_pixel_height()?
|
||||
self.tab_bar_pixel_height()
|
||||
.context("tab_bar_pixel_height")?
|
||||
} else {
|
||||
0.
|
||||
};
|
||||
@ -152,15 +154,17 @@ impl crate::TermWindow {
|
||||
if self.window_background.is_empty() {
|
||||
// Per-pane, palette-specified background
|
||||
|
||||
let mut quad = self.filled_rectangle(
|
||||
layers,
|
||||
0,
|
||||
background_rect,
|
||||
palette
|
||||
.background
|
||||
.to_linear()
|
||||
.mul_alpha(config.window_background_opacity),
|
||||
)?;
|
||||
let mut quad = self
|
||||
.filled_rectangle(
|
||||
layers,
|
||||
0,
|
||||
background_rect,
|
||||
palette
|
||||
.background
|
||||
.to_linear()
|
||||
.mul_alpha(config.window_background_opacity),
|
||||
)
|
||||
.context("filled_rectangle")?;
|
||||
quad.set_hsv(if pos.is_active {
|
||||
None
|
||||
} else {
|
||||
@ -205,7 +209,9 @@ impl crate::TermWindow {
|
||||
};
|
||||
log::trace!("bell color is {:?}", background);
|
||||
|
||||
let mut quad = self.filled_rectangle(layers, 0, background_rect, background)?;
|
||||
let mut quad = self
|
||||
.filled_rectangle(layers, 0, background_rect, background)
|
||||
.context("filled_rectangle")?;
|
||||
|
||||
quad.set_hsv(if pos.is_active {
|
||||
None
|
||||
@ -279,7 +285,8 @@ impl crate::TermWindow {
|
||||
thumb_size as f32,
|
||||
),
|
||||
color,
|
||||
)?;
|
||||
)
|
||||
.context("filled_rectangle")?;
|
||||
}
|
||||
|
||||
let (selrange, rectangular) = {
|
||||
@ -451,7 +458,10 @@ impl crate::TermWindow {
|
||||
false
|
||||
};
|
||||
if !expired && !hover_changed {
|
||||
cached_quad.layers.apply_to(self.layers)?;
|
||||
cached_quad
|
||||
.layers
|
||||
.apply_to(self.layers)
|
||||
.context("cached_quad.layers.apply_to")?;
|
||||
self.term_window.update_next_frame_time(cached_quad.expires);
|
||||
return Ok(());
|
||||
}
|
||||
@ -476,49 +486,53 @@ impl crate::TermWindow {
|
||||
},
|
||||
};
|
||||
|
||||
let render_result = self.term_window.render_screen_line(
|
||||
RenderScreenLineParams {
|
||||
top_pixel_y: *quad_key.top_pixel_y,
|
||||
left_pixel_x: self.left_pixel_x,
|
||||
pixel_width: self.dims.cols as f32
|
||||
* self.term_window.render_metrics.cell_size.width as f32,
|
||||
stable_line_idx: Some(stable_row),
|
||||
line: &line,
|
||||
selection: selrange.clone(),
|
||||
cursor: &self.cursor,
|
||||
palette: &self.palette,
|
||||
dims: &self.dims,
|
||||
config: &self.term_window.config,
|
||||
cursor_border_color: self.cursor_border_color,
|
||||
foreground: self.foreground,
|
||||
is_active: self.pos.is_active,
|
||||
pane: Some(&self.pos.pane),
|
||||
selection_fg: self.selection_fg,
|
||||
selection_bg: self.selection_bg,
|
||||
cursor_fg: self.cursor_fg,
|
||||
cursor_bg: self.cursor_bg,
|
||||
cursor_is_default_color: self.cursor_is_default_color,
|
||||
white_space: self.white_space,
|
||||
filled_box: self.filled_box,
|
||||
window_is_transparent: self.window_is_transparent,
|
||||
default_bg: self.default_bg,
|
||||
font: None,
|
||||
style: None,
|
||||
use_pixel_positioning: self
|
||||
.term_window
|
||||
.config
|
||||
.experimental_pixel_positioning,
|
||||
render_metrics: self.term_window.render_metrics,
|
||||
shape_key: Some(shape_key),
|
||||
password_input,
|
||||
},
|
||||
&mut TripleLayerQuadAllocator::Heap(&mut buf),
|
||||
)?;
|
||||
let render_result = self
|
||||
.term_window
|
||||
.render_screen_line(
|
||||
RenderScreenLineParams {
|
||||
top_pixel_y: *quad_key.top_pixel_y,
|
||||
left_pixel_x: self.left_pixel_x,
|
||||
pixel_width: self.dims.cols as f32
|
||||
* self.term_window.render_metrics.cell_size.width as f32,
|
||||
stable_line_idx: Some(stable_row),
|
||||
line: &line,
|
||||
selection: selrange.clone(),
|
||||
cursor: &self.cursor,
|
||||
palette: &self.palette,
|
||||
dims: &self.dims,
|
||||
config: &self.term_window.config,
|
||||
cursor_border_color: self.cursor_border_color,
|
||||
foreground: self.foreground,
|
||||
is_active: self.pos.is_active,
|
||||
pane: Some(&self.pos.pane),
|
||||
selection_fg: self.selection_fg,
|
||||
selection_bg: self.selection_bg,
|
||||
cursor_fg: self.cursor_fg,
|
||||
cursor_bg: self.cursor_bg,
|
||||
cursor_is_default_color: self.cursor_is_default_color,
|
||||
white_space: self.white_space,
|
||||
filled_box: self.filled_box,
|
||||
window_is_transparent: self.window_is_transparent,
|
||||
default_bg: self.default_bg,
|
||||
font: None,
|
||||
style: None,
|
||||
use_pixel_positioning: self
|
||||
.term_window
|
||||
.config
|
||||
.experimental_pixel_positioning,
|
||||
render_metrics: self.term_window.render_metrics,
|
||||
shape_key: Some(shape_key),
|
||||
password_input,
|
||||
},
|
||||
&mut TripleLayerQuadAllocator::Heap(&mut buf),
|
||||
)
|
||||
.context("render_screen_line")?;
|
||||
|
||||
let expires = self.term_window.has_animation.borrow().as_ref().cloned();
|
||||
self.term_window.update_next_frame_time(next_due);
|
||||
|
||||
buf.apply_to(self.layers)?;
|
||||
buf.apply_to(self.layers)
|
||||
.context("HeapQuadAllocator::apply_to")?;
|
||||
|
||||
let quad_value = LineQuadCacheValue {
|
||||
layers: buf,
|
||||
@ -554,7 +568,7 @@ impl crate::TermWindow {
|
||||
|
||||
pos.pane.with_lines_mut(stable_range.clone(), &mut render);
|
||||
if let Some(error) = render.error.take() {
|
||||
return Err(error);
|
||||
return Err(error).context("error while calling with_lines_mut");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ use crate::termwindow::render::{
|
||||
};
|
||||
use crate::termwindow::LineToElementShapeItem;
|
||||
use ::window::DeadKeyStatus;
|
||||
use anyhow::Context;
|
||||
use config::{HsbTransform, TextStyle};
|
||||
use std::ops::Range;
|
||||
use std::rc::Rc;
|
||||
@ -170,17 +171,19 @@ impl crate::TermWindow {
|
||||
}
|
||||
|
||||
if params.dims.reverse_video {
|
||||
let mut quad = self.filled_rectangle(
|
||||
layers,
|
||||
0,
|
||||
euclid::rect(
|
||||
params.left_pixel_x,
|
||||
params.top_pixel_y,
|
||||
params.pixel_width,
|
||||
cell_height,
|
||||
),
|
||||
params.foreground,
|
||||
)?;
|
||||
let mut quad = self
|
||||
.filled_rectangle(
|
||||
layers,
|
||||
0,
|
||||
euclid::rect(
|
||||
params.left_pixel_x,
|
||||
params.top_pixel_y,
|
||||
params.pixel_width,
|
||||
cell_height,
|
||||
),
|
||||
params.foreground,
|
||||
)
|
||||
.context("filled_rectangle")?;
|
||||
quad.set_hsv(hsv);
|
||||
}
|
||||
|
||||
@ -248,7 +251,9 @@ impl crate::TermWindow {
|
||||
|
||||
let rect = euclid::rect(x, params.top_pixel_y, width, cell_height);
|
||||
if let Some(rect) = rect.intersection(&bounding_rect) {
|
||||
let mut quad = self.filled_rectangle(layers, 0, rect, bg_color)?;
|
||||
let mut quad = self
|
||||
.filled_rectangle(layers, 0, rect, bg_color)
|
||||
.context("filled_rectangle")?;
|
||||
quad.set_hsv(hsv);
|
||||
}
|
||||
}
|
||||
@ -258,7 +263,7 @@ impl crate::TermWindow {
|
||||
// Draw one per cell, otherwise curly underlines
|
||||
// stretch across the whole span
|
||||
for i in 0..cluster_width {
|
||||
let mut quad = layers.allocate(0)?;
|
||||
let mut quad = layers.allocate(0).context("layers.allocate(0)")?;
|
||||
let x = gl_x
|
||||
+ params.left_pixel_x
|
||||
+ if params.use_pixel_positioning {
|
||||
@ -283,12 +288,14 @@ impl crate::TermWindow {
|
||||
let selection_pixel_range = if !params.selection.is_empty() {
|
||||
let start = params.left_pixel_x + (params.selection.start as f32 * cell_width);
|
||||
let width = (params.selection.end - params.selection.start) as f32 * cell_width;
|
||||
let mut quad = self.filled_rectangle(
|
||||
layers,
|
||||
0,
|
||||
euclid::rect(start, params.top_pixel_y, width, cell_height),
|
||||
params.selection_bg,
|
||||
)?;
|
||||
let mut quad = self
|
||||
.filled_rectangle(
|
||||
layers,
|
||||
0,
|
||||
euclid::rect(start, params.top_pixel_y, width, cell_height),
|
||||
params.selection_bg,
|
||||
)
|
||||
.context("filled_rectangle")?;
|
||||
|
||||
quad.set_hsv(hsv);
|
||||
|
||||
@ -343,7 +350,7 @@ impl crate::TermWindow {
|
||||
+ (phys(params.cursor.x, num_cols, direction) as f32 * cell_width);
|
||||
|
||||
if cursor_shape.is_some() {
|
||||
let mut quad = layers.allocate(0)?;
|
||||
let mut quad = layers.allocate(0).context("layers.allocate(0)")?;
|
||||
quad.set_hsv(hsv);
|
||||
quad.set_has_color(false);
|
||||
|
||||
@ -355,13 +362,15 @@ impl crate::TermWindow {
|
||||
.map(|cell| cell.attrs().clone())
|
||||
.unwrap_or_else(|| CellAttributes::blank());
|
||||
|
||||
let glyph = self.resolve_lock_glyph(
|
||||
&TextStyle::default(),
|
||||
&attrs,
|
||||
params.font.as_ref(),
|
||||
gl_state,
|
||||
¶ms.render_metrics,
|
||||
)?;
|
||||
let glyph = self
|
||||
.resolve_lock_glyph(
|
||||
&TextStyle::default(),
|
||||
&attrs,
|
||||
params.font.as_ref(),
|
||||
gl_state,
|
||||
¶ms.render_metrics,
|
||||
)
|
||||
.context("resolve_lock_glyph")?;
|
||||
|
||||
if let Some(sprite) = &glyph.texture {
|
||||
let width = sprite.coords.size.width as f32 * glyph.scale as f32;
|
||||
@ -486,7 +495,8 @@ impl crate::TermWindow {
|
||||
gl_state
|
||||
.glyph_cache
|
||||
.borrow_mut()
|
||||
.cached_block(*block, ¶ms.render_metrics)?,
|
||||
.cached_block(*block, ¶ms.render_metrics)
|
||||
.context("cached_block")?,
|
||||
);
|
||||
// Custom glyphs don't have the same offsets as computed
|
||||
// by the shaper, and are rendered relative to the cell
|
||||
@ -624,7 +634,7 @@ impl crate::TermWindow {
|
||||
|
||||
let texture_rect = texture.texture.to_texture_coords(pixel_rect);
|
||||
|
||||
let mut quad = layers.allocate(1)?;
|
||||
let mut quad = layers.allocate(1).context("layers.allocate(1)")?;
|
||||
quad.set_position(
|
||||
gl_x + range.start,
|
||||
pos_y + top,
|
||||
@ -691,7 +701,8 @@ impl crate::TermWindow {
|
||||
¶ms,
|
||||
hsv,
|
||||
glyph_color,
|
||||
)?;
|
||||
)
|
||||
.context("populate_image_quad")?;
|
||||
}
|
||||
|
||||
metrics::histogram!("render_screen_line", start.elapsed());
|
||||
|
@ -56,16 +56,26 @@ impl Atlas {
|
||||
|
||||
/// Reserve space for a sprite of the given size
|
||||
pub fn allocate(&mut self, im: &dyn BitmapImage) -> Result<Sprite, OutOfTextureSpace> {
|
||||
self.allocate_with_padding(im, None)
|
||||
self.allocate_with_padding(im, None, None)
|
||||
}
|
||||
|
||||
pub fn allocate_with_padding(
|
||||
&mut self,
|
||||
im: &dyn BitmapImage,
|
||||
padding: Option<usize>,
|
||||
scale_down: Option<usize>,
|
||||
) -> Result<Sprite, OutOfTextureSpace> {
|
||||
let (width, height) = im.image_dimensions();
|
||||
|
||||
if let Some(scale_down) = scale_down {
|
||||
let mut copied = crate::Image::new(width, height);
|
||||
copied.draw_image(Point::new(0, 0), None, im);
|
||||
|
||||
let scaled = copied.resize(width / scale_down, height / scale_down);
|
||||
|
||||
return self.allocate_with_padding(&scaled, padding, None);
|
||||
}
|
||||
|
||||
// If we can't convert the sizes to i32, then we'll never
|
||||
// be able to store this image
|
||||
let reserve_width: i32 = width.try_into().map_err(|_| OutOfTextureSpace {
|
||||
|
Loading…
Reference in New Issue
Block a user