1use super::Renderer;
2use anyhow::Result;
3
4impl Renderer {
5 #[allow(dead_code)]
13 pub fn update_graphics(
14 &mut self,
15 graphics: &[par_term_emu_core_rust::graphics::TerminalGraphic],
16 view_scroll_offset: usize,
17 scrollback_len: usize,
18 visible_rows: usize,
19 ) -> Result<()> {
20 let had_graphics = !self.sixel_graphics.is_empty();
22
23 self.sixel_graphics.clear();
25
26 let total_lines = scrollback_len + visible_rows;
31 let view_end = total_lines.saturating_sub(view_scroll_offset);
32 let view_start = view_end.saturating_sub(visible_rows);
33
34 for graphic in graphics {
36 let id = graphic.id;
38 let (col, row) = graphic.position;
39
40 let core_cell_height = graphic
44 .cell_dimensions
45 .map(|(_, h)| h as f32)
46 .unwrap_or(2.0)
47 .max(1.0);
48 let display_cell_height = self.cell_renderer.cell_height().max(1.0);
49 let scroll_offset_in_display_rows = (graphic.scroll_offset_rows as f32
50 * core_cell_height
51 / display_cell_height)
52 .round() as usize;
53
54 let screen_row: isize = if let Some(sb_row) = graphic.scrollback_row {
56 sb_row as isize - view_start as isize
59 } else {
60 let absolute_row =
64 scrollback_len.saturating_sub(scroll_offset_in_display_rows) + row;
65
66 log::trace!(
67 "[RENDERER] CALC: scrollback_len={}, row={}, scroll_offset_rows={}, scroll_in_display_rows={}, absolute_row={}, view_start={}, screen_row={}",
68 scrollback_len,
69 row,
70 graphic.scroll_offset_rows,
71 scroll_offset_in_display_rows,
72 absolute_row,
73 view_start,
74 absolute_row as isize - view_start as isize
75 );
76
77 absolute_row as isize - view_start as isize
78 };
79
80 log::debug!(
81 "[RENDERER] Graphics update: id={}, protocol={:?}, pos=({},{}), screen_row={}, scrollback_row={:?}, scroll_offset_rows={}, size={}x{}, view=[{},{})",
82 id,
83 graphic.protocol,
84 col,
85 row,
86 screen_row,
87 graphic.scrollback_row,
88 graphic.scroll_offset_rows,
89 graphic.width,
90 graphic.height,
91 view_start,
92 view_end
93 );
94
95 self.graphics_renderer.get_or_create_texture(
97 self.cell_renderer.device(),
98 self.cell_renderer.queue(),
99 id,
100 &graphic.pixels, graphic.width as u32,
102 graphic.height as u32,
103 )?;
104
105 let width_cells =
108 ((graphic.width as f32 / self.cell_renderer.cell_width()).ceil() as usize).max(1);
109 let height_cells =
110 ((graphic.height as f32 / self.cell_renderer.cell_height()).ceil() as usize).max(1);
111
112 let effective_clip_rows = if screen_row < 0 {
116 (-screen_row) as usize
117 } else {
118 0
119 };
120
121 self.sixel_graphics.push((
122 id,
123 screen_row, col, width_cells,
126 height_cells,
127 1.0, effective_clip_rows, ));
130 }
131
132 if !graphics.is_empty() || had_graphics {
134 self.dirty = true;
135 }
136
137 Ok(())
138 }
139
140 pub(crate) fn render_sixel_graphics(
142 &mut self,
143 surface_texture: &wgpu::SurfaceTexture,
144 ) -> Result<()> {
145 use wgpu::TextureViewDescriptor;
146
147 let view = surface_texture
149 .texture
150 .create_view(&TextureViewDescriptor::default());
151
152 let mut encoder =
154 self.cell_renderer
155 .device()
156 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
157 label: Some("sixel encoder"),
158 });
159
160 {
162 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
163 label: Some("sixel render pass"),
164 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
165 view: &view,
166 resolve_target: None,
167 ops: wgpu::Operations {
168 load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store,
170 },
171 depth_slice: None,
172 })],
173 depth_stencil_attachment: None,
174 timestamp_writes: None,
175 occlusion_query_set: None,
176 });
177
178 self.graphics_renderer.render(
180 self.cell_renderer.device(),
181 self.cell_renderer.queue(),
182 &mut render_pass,
183 &self.sixel_graphics,
184 self.size.width as f32,
185 self.size.height as f32,
186 )?;
187 } self.cell_renderer
191 .queue()
192 .submit(std::iter::once(encoder.finish()));
193
194 Ok(())
195 }
196
197 #[allow(clippy::type_complexity)]
205 pub fn update_pane_graphics(
206 &mut self,
207 graphics: &[par_term_emu_core_rust::graphics::TerminalGraphic],
208 view_scroll_offset: usize,
209 scrollback_len: usize,
210 visible_rows: usize,
211 ) -> Result<Vec<(u64, isize, usize, usize, usize, f32, usize)>> {
212 let total_lines = scrollback_len + visible_rows;
213 let view_end = total_lines.saturating_sub(view_scroll_offset);
214 let view_start = view_end.saturating_sub(visible_rows);
215
216 log::debug!(
217 "[PANE_GRAPHICS] update_pane_graphics: scrollback_len={}, visible_rows={}, view_scroll_offset={}, total_lines={}, view_start={}, view_end={}, graphics_count={}",
218 scrollback_len,
219 visible_rows,
220 view_scroll_offset,
221 total_lines,
222 view_start,
223 view_end,
224 graphics.len()
225 );
226
227 let mut positioned = Vec::new();
228
229 for graphic in graphics {
230 let id = graphic.id;
231 let (col, row) = graphic.position;
232
233 let core_cell_height = graphic
238 .cell_dimensions
239 .map(|(_, h)| h as f32)
240 .unwrap_or(2.0)
241 .max(1.0);
242 let display_cell_height = self.cell_renderer.cell_height().max(1.0);
243 let scroll_offset_in_display_rows = (graphic.scroll_offset_rows as f32
244 * core_cell_height
245 / display_cell_height)
246 .round() as usize;
247
248 let screen_row: isize = if let Some(sb_row) = graphic.scrollback_row {
249 let sr = sb_row as isize - view_start as isize;
250 log::debug!(
251 "[PANE_GRAPHICS] scrollback graphic id={}: sb_row={}, view_start={}, screen_row={}",
252 id,
253 sb_row,
254 view_start,
255 sr
256 );
257 sr
258 } else {
259 let absolute_row =
260 scrollback_len.saturating_sub(scroll_offset_in_display_rows) + row;
261 let sr = absolute_row as isize - view_start as isize;
262 log::debug!(
263 "[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={}",
264 id,
265 scrollback_len,
266 graphic.scroll_offset_rows,
267 core_cell_height,
268 display_cell_height,
269 scroll_offset_in_display_rows,
270 row,
271 absolute_row,
272 view_start,
273 sr
274 );
275 sr
276 };
277
278 self.graphics_renderer.get_or_create_texture(
280 self.cell_renderer.device(),
281 self.cell_renderer.queue(),
282 id,
283 &graphic.pixels,
284 graphic.width as u32,
285 graphic.height as u32,
286 )?;
287
288 let width_cells =
289 ((graphic.width as f32 / self.cell_renderer.cell_width()).ceil() as usize).max(1);
290 let height_cells =
291 ((graphic.height as f32 / self.cell_renderer.cell_height()).ceil() as usize).max(1);
292
293 let effective_clip_rows = if screen_row < 0 {
294 (-screen_row) as usize
295 } else {
296 0
297 };
298
299 positioned.push((
300 id,
301 screen_row,
302 col,
303 width_cells,
304 height_cells,
305 1.0,
306 effective_clip_rows,
307 ));
308 }
309
310 Ok(positioned)
311 }
312
313 pub(crate) fn render_pane_sixel_graphics(
319 &mut self,
320 surface_view: &wgpu::TextureView,
321 viewport: &crate::cell_renderer::PaneViewport,
322 graphics: &[par_term_emu_core_rust::graphics::TerminalGraphic],
323 scroll_offset: usize,
324 scrollback_len: usize,
325 visible_rows: usize,
326 ) -> Result<()> {
327 let positioned =
328 self.update_pane_graphics(graphics, scroll_offset, scrollback_len, visible_rows)?;
329
330 if positioned.is_empty() {
331 return Ok(());
332 }
333
334 let mut encoder =
335 self.cell_renderer
336 .device()
337 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
338 label: Some("pane sixel encoder"),
339 });
340
341 {
342 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
343 label: Some("pane sixel render pass"),
344 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
345 view: surface_view,
346 resolve_target: None,
347 ops: wgpu::Operations {
348 load: wgpu::LoadOp::Load,
349 store: wgpu::StoreOp::Store,
350 },
351 depth_slice: None,
352 })],
353 depth_stencil_attachment: None,
354 timestamp_writes: None,
355 occlusion_query_set: None,
356 });
357
358 let (sx, sy, sw, sh) = viewport.to_scissor_rect();
360 render_pass.set_scissor_rect(sx, sy, sw, sh);
361
362 let (ox, oy) = viewport.content_origin();
363
364 log::debug!(
365 "[PANE_GRAPHICS] render_pane_sixel_graphics: scissor=({},{},{},{}), origin=({},{}), window={}x{}, positioned_count={}",
366 sx,
367 sy,
368 sw,
369 sh,
370 ox,
371 oy,
372 self.size.width,
373 self.size.height,
374 positioned.len()
375 );
376 for (id, screen_row, col, wc, hc, _, clip) in &positioned {
377 log::debug!(
378 "[PANE_GRAPHICS] positioned: id={}, screen_row={}, col={}, width_cells={}, height_cells={}, clip_rows={}",
379 id,
380 screen_row,
381 col,
382 wc,
383 hc,
384 clip
385 );
386 }
387
388 self.graphics_renderer.render_for_pane(
389 self.cell_renderer.device(),
390 self.cell_renderer.queue(),
391 &mut render_pass,
392 &positioned,
393 self.size.width as f32,
394 self.size.height as f32,
395 ox,
396 oy,
397 )?;
398 }
399
400 self.cell_renderer
401 .queue()
402 .submit(std::iter::once(encoder.finish()));
403
404 Ok(())
405 }
406
407 #[allow(clippy::type_complexity)]
419 pub fn update_prettifier_graphics(
420 &mut self,
421 graphics: &[(u64, &[u8], u32, u32, isize, usize)],
422 ) -> Result<()> {
423 for &(id, rgba_data, pixel_width, pixel_height, screen_row, col) in graphics {
424 if rgba_data.is_empty() || pixel_width == 0 || pixel_height == 0 {
425 continue;
426 }
427
428 self.graphics_renderer.get_or_create_texture(
430 self.cell_renderer.device(),
431 self.cell_renderer.queue(),
432 id,
433 rgba_data,
434 pixel_width,
435 pixel_height,
436 )?;
437
438 let width_cells =
440 ((pixel_width as f32 / self.cell_renderer.cell_width()).ceil() as usize).max(1);
441 let height_cells =
442 ((pixel_height as f32 / self.cell_renderer.cell_height()).ceil() as usize).max(1);
443
444 let effective_clip_rows = if screen_row < 0 {
446 (-screen_row) as usize
447 } else {
448 0
449 };
450
451 self.sixel_graphics.push((
452 id,
453 screen_row,
454 col,
455 width_cells,
456 height_cells,
457 1.0, effective_clip_rows,
459 ));
460 }
461
462 if !graphics.is_empty() {
463 self.dirty = true;
464 }
465
466 Ok(())
467 }
468
469 #[allow(dead_code)]
471 pub fn clear_sixel_cache(&mut self) {
472 self.graphics_renderer.clear_cache();
473 self.sixel_graphics.clear();
474 self.dirty = true;
475 }
476
477 #[allow(dead_code)]
479 pub fn sixel_cache_size(&self) -> usize {
480 self.graphics_renderer.cache_size()
481 }
482
483 #[allow(dead_code)]
485 pub fn remove_sixel_texture(&mut self, id: u64) {
486 self.graphics_renderer.remove_texture(id);
487 self.sixel_graphics
488 .retain(|(gid, _, _, _, _, _, _)| *gid != id);
489 self.dirty = true;
490 }
491}