1use super::Renderer;
2use crate::graphics_renderer::GraphicRenderInfo;
3use anyhow::Result;
4
5pub type PrettifierGraphicRef<'a> = (u64, &'a [u8], u32, u32, isize, usize);
8
9impl Renderer {
10 pub fn update_graphics(
18 &mut self,
19 graphics: &[par_term_emu_core_rust::graphics::TerminalGraphic],
20 view_scroll_offset: usize,
21 scrollback_len: usize,
22 visible_rows: usize,
23 ) -> Result<()> {
24 let had_graphics = !self.sixel_graphics.is_empty();
26
27 self.sixel_graphics.clear();
29
30 let total_lines = scrollback_len + visible_rows;
35 let view_end = total_lines.saturating_sub(view_scroll_offset);
36 let view_start = view_end.saturating_sub(visible_rows);
37
38 for graphic in graphics {
40 let id = graphic.id;
42 let (col, row) = graphic.position;
43
44 let core_cell_height = graphic
48 .cell_dimensions
49 .map(|(_, h)| h as f32)
50 .unwrap_or(2.0)
51 .max(1.0);
52 let display_cell_height = self.cell_renderer.cell_height().max(1.0);
53 let scroll_offset_in_display_rows = (graphic.scroll_offset_rows as f32
54 * core_cell_height
55 / display_cell_height)
56 .round() as usize;
57
58 let screen_row: isize = if let Some(sb_row) = graphic.scrollback_row {
60 sb_row as isize - view_start as isize
63 } else {
64 let absolute_row =
68 scrollback_len.saturating_sub(scroll_offset_in_display_rows) + row;
69
70 log::trace!(
71 "[RENDERER] CALC: scrollback_len={}, row={}, scroll_offset_rows={}, scroll_in_display_rows={}, absolute_row={}, view_start={}, screen_row={}",
72 scrollback_len,
73 row,
74 graphic.scroll_offset_rows,
75 scroll_offset_in_display_rows,
76 absolute_row,
77 view_start,
78 absolute_row as isize - view_start as isize
79 );
80
81 absolute_row as isize - view_start as isize
82 };
83
84 log::debug!(
85 "[RENDERER] Graphics update: id={}, protocol={:?}, pos=({},{}), screen_row={}, scrollback_row={:?}, scroll_offset_rows={}, size={}x{}, view=[{},{})",
86 id,
87 graphic.protocol,
88 col,
89 row,
90 screen_row,
91 graphic.scrollback_row,
92 graphic.scroll_offset_rows,
93 graphic.width,
94 graphic.height,
95 view_start,
96 view_end
97 );
98
99 self.graphics_renderer.get_or_create_texture(
101 self.cell_renderer.device(),
102 self.cell_renderer.queue(),
103 id,
104 &graphic.pixels, graphic.width as u32,
106 graphic.height as u32,
107 )?;
108
109 let width_cells =
112 ((graphic.width as f32 / self.cell_renderer.cell_width()).ceil() as usize).max(1);
113 let height_cells =
114 ((graphic.height as f32 / self.cell_renderer.cell_height()).ceil() as usize).max(1);
115
116 let effective_clip_rows = if screen_row < 0 {
120 (-screen_row) as usize
121 } else {
122 0
123 };
124
125 self.sixel_graphics.push(GraphicRenderInfo {
126 id,
127 screen_row,
128 col,
129 width_cells,
130 height_cells,
131 alpha: 1.0,
132 scroll_offset_rows: effective_clip_rows,
133 });
134 }
135
136 if !graphics.is_empty() || had_graphics {
138 self.dirty = true;
139 }
140
141 Ok(())
142 }
143
144 pub fn update_pane_graphics(
151 &mut self,
152 graphics: &[par_term_emu_core_rust::graphics::TerminalGraphic],
153 view_scroll_offset: usize,
154 scrollback_len: usize,
155 visible_rows: usize,
156 ) -> Result<Vec<GraphicRenderInfo>> {
157 let total_lines = scrollback_len + visible_rows;
158 let view_end = total_lines.saturating_sub(view_scroll_offset);
159 let view_start = view_end.saturating_sub(visible_rows);
160
161 log::debug!(
162 "[PANE_GRAPHICS] update_pane_graphics: scrollback_len={}, visible_rows={}, view_scroll_offset={}, total_lines={}, view_start={}, view_end={}, graphics_count={}",
163 scrollback_len,
164 visible_rows,
165 view_scroll_offset,
166 total_lines,
167 view_start,
168 view_end,
169 graphics.len()
170 );
171
172 let mut positioned = Vec::new();
173
174 for graphic in graphics {
175 let id = graphic.id;
176 let (col, row) = graphic.position;
177
178 let core_cell_height = graphic
183 .cell_dimensions
184 .map(|(_, h)| h as f32)
185 .unwrap_or(2.0)
186 .max(1.0);
187 let display_cell_height = self.cell_renderer.cell_height().max(1.0);
188 let scroll_offset_in_display_rows = (graphic.scroll_offset_rows as f32
189 * core_cell_height
190 / display_cell_height)
191 .round() as usize;
192
193 let screen_row: isize = if let Some(sb_row) = graphic.scrollback_row {
194 let sr = sb_row as isize - view_start as isize;
195 log::debug!(
196 "[PANE_GRAPHICS] scrollback graphic id={}: sb_row={}, view_start={}, screen_row={}",
197 id,
198 sb_row,
199 view_start,
200 sr
201 );
202 sr
203 } else {
204 let absolute_row =
205 scrollback_len.saturating_sub(scroll_offset_in_display_rows) + row;
206 let sr = absolute_row as isize - view_start as isize;
207 log::debug!(
208 "[PANE_GRAPHICS] current graphic id={}: scrollback_len={}, scroll_offset_rows={}, core_cell_h={}, disp_cell_h={}, scroll_in_display_rows={}, row={}, absolute_row={}, view_start={}, screen_row={}",
209 id,
210 scrollback_len,
211 graphic.scroll_offset_rows,
212 core_cell_height,
213 display_cell_height,
214 scroll_offset_in_display_rows,
215 row,
216 absolute_row,
217 view_start,
218 sr
219 );
220 sr
221 };
222
223 self.graphics_renderer.get_or_create_texture(
225 self.cell_renderer.device(),
226 self.cell_renderer.queue(),
227 id,
228 &graphic.pixels,
229 graphic.width as u32,
230 graphic.height as u32,
231 )?;
232
233 let width_cells =
234 ((graphic.width as f32 / self.cell_renderer.cell_width()).ceil() as usize).max(1);
235 let height_cells =
236 ((graphic.height as f32 / self.cell_renderer.cell_height()).ceil() as usize).max(1);
237
238 let effective_clip_rows = if screen_row < 0 {
239 (-screen_row) as usize
240 } else {
241 0
242 };
243
244 positioned.push(GraphicRenderInfo {
245 id,
246 screen_row,
247 col,
248 width_cells,
249 height_cells,
250 alpha: 1.0,
251 scroll_offset_rows: effective_clip_rows,
252 });
253 }
254
255 Ok(positioned)
256 }
257
258 pub(crate) fn render_pane_sixel_graphics(
264 &mut self,
265 surface_view: &wgpu::TextureView,
266 viewport: &crate::cell_renderer::PaneViewport,
267 graphics: &[par_term_emu_core_rust::graphics::TerminalGraphic],
268 scroll_offset: usize,
269 scrollback_len: usize,
270 visible_rows: usize,
271 ) -> Result<()> {
272 let positioned =
273 self.update_pane_graphics(graphics, scroll_offset, scrollback_len, visible_rows)?;
274
275 if positioned.is_empty() {
276 return Ok(());
277 }
278
279 let mut encoder =
280 self.cell_renderer
281 .device()
282 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
283 label: Some("pane sixel encoder"),
284 });
285
286 {
287 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
288 label: Some("pane sixel render pass"),
289 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
290 view: surface_view,
291 resolve_target: None,
292 ops: wgpu::Operations {
293 load: wgpu::LoadOp::Load,
294 store: wgpu::StoreOp::Store,
295 },
296 depth_slice: None,
297 })],
298 depth_stencil_attachment: None,
299 timestamp_writes: None,
300 occlusion_query_set: None,
301 });
302
303 let (sx, sy, sw, sh) = viewport.to_scissor_rect();
305 render_pass.set_scissor_rect(sx, sy, sw, sh);
306
307 let (ox, oy) = viewport.content_origin();
308
309 log::debug!(
310 "[PANE_GRAPHICS] render_pane_sixel_graphics: scissor=({},{},{},{}), origin=({},{}), window={}x{}, positioned_count={}",
311 sx,
312 sy,
313 sw,
314 sh,
315 ox,
316 oy,
317 self.size.width,
318 self.size.height,
319 positioned.len()
320 );
321 for g in &positioned {
322 log::debug!(
323 "[PANE_GRAPHICS] positioned: id={}, screen_row={}, col={}, width_cells={}, height_cells={}, clip_rows={}",
324 g.id,
325 g.screen_row,
326 g.col,
327 g.width_cells,
328 g.height_cells,
329 g.scroll_offset_rows
330 );
331 }
332
333 self.graphics_renderer.render_for_pane(
334 self.cell_renderer.device(),
335 self.cell_renderer.queue(),
336 &mut render_pass,
337 &positioned,
338 crate::graphics_renderer::PaneRenderGeometry {
339 window_width: self.size.width as f32,
340 window_height: self.size.height as f32,
341 pane_origin_x: ox,
342 pane_origin_y: oy,
343 },
344 )?;
345 }
346
347 self.cell_renderer
348 .queue()
349 .submit(std::iter::once(encoder.finish()));
350
351 Ok(())
352 }
353
354 pub fn update_prettifier_graphics(
366 &mut self,
367 graphics: &[PrettifierGraphicRef<'_>],
368 ) -> Result<()> {
369 for &(id, rgba_data, pixel_width, pixel_height, screen_row, col) in graphics {
370 if rgba_data.is_empty() || pixel_width == 0 || pixel_height == 0 {
371 continue;
372 }
373
374 self.graphics_renderer.get_or_create_texture(
376 self.cell_renderer.device(),
377 self.cell_renderer.queue(),
378 id,
379 rgba_data,
380 pixel_width,
381 pixel_height,
382 )?;
383
384 let width_cells =
386 ((pixel_width as f32 / self.cell_renderer.cell_width()).ceil() as usize).max(1);
387 let height_cells =
388 ((pixel_height as f32 / self.cell_renderer.cell_height()).ceil() as usize).max(1);
389
390 let effective_clip_rows = if screen_row < 0 {
392 (-screen_row) as usize
393 } else {
394 0
395 };
396
397 self.sixel_graphics.push(GraphicRenderInfo {
398 id,
399 screen_row,
400 col,
401 width_cells,
402 height_cells,
403 alpha: 1.0,
404 scroll_offset_rows: effective_clip_rows,
405 });
406 }
407
408 if !graphics.is_empty() {
409 self.dirty = true;
410 }
411
412 Ok(())
413 }
414
415 pub fn clear_sixel_cache(&mut self) {
417 self.graphics_renderer.clear_cache();
418 self.sixel_graphics.clear();
419 self.dirty = true;
420 }
421
422 pub fn sixel_cache_size(&self) -> usize {
424 self.graphics_renderer.cache_size()
425 }
426
427 pub fn remove_sixel_texture(&mut self, id: u64) {
429 self.graphics_renderer.remove_texture(id);
430 self.sixel_graphics.retain(|g| g.id != id);
431 self.dirty = true;
432 }
433}