From 8043420af5d6057040c4456de8601b7e058d3010 Mon Sep 17 00:00:00 2001 From: nlp201 <13403166+nlp201@users.noreply.github.com> Date: Tue, 11 Aug 2026 20:56:32 +0900 Subject: [PATCH 1/2] [FIX] Clear stale CEA-708 window rows in no-rollup mode --- src/lib_ccx/ccx_decoders_708.c | 6 +- src/rust/src/decoder/service_decoder.rs | 73 ++++++++++++++++++++++++- 2 files changed, 76 insertions(+), 3 deletions(-) diff --git a/src/lib_ccx/ccx_decoders_708.c b/src/lib_ccx/ccx_decoders_708.c index 7df84aa03..35467145d 100644 --- a/src/lib_ccx/ccx_decoders_708.c +++ b/src/lib_ccx/ccx_decoders_708.c @@ -771,7 +771,11 @@ void dtvcc_process_cr(dtvcc_ctx *dtvcc, dtvcc_service_decoder *decoder) dtvcc_window_copy_to_screen(decoder, window); dtvcc_screen_print(dtvcc, decoder); if (dtvcc->no_rollup) - dtvcc_window_clear_row(window, window->pen_row); + { + for (int row = 0; row < CCX_DTVCC_MAX_ROWS; row++) + dtvcc_window_clear_row(window, row); + window->is_empty = 1; + } else dtvcc_window_rollup(decoder, window); } diff --git a/src/rust/src/decoder/service_decoder.rs b/src/rust/src/decoder/service_decoder.rs index 6b9019b63..b2d022398 100644 --- a/src/rust/src/decoder/service_decoder.rs +++ b/src/rust/src/decoder/service_decoder.rs @@ -163,7 +163,6 @@ impl dtvcc_service_decoder { }; if is_true(window.is_defined) { - let pen_row = window.pen_row; window.update_time_hide(timing); if rollup_required { @@ -171,7 +170,11 @@ impl dtvcc_service_decoder { self.copy_to_screen(&self.windows[self.current_window as usize]); self.screen_print(encoder, timing); if no_rollup { - self.windows[self.current_window as usize].clear_row(pen_row as usize); + let window = &mut self.windows[self.current_window as usize]; + for row in 0..CCX_DTVCC_MAX_ROWS as usize { + window.clear_row(row); + } + window.is_empty = 1; } else { self.windows[self.current_window as usize].rollup(); } @@ -1920,4 +1923,70 @@ mod test { ); } } + + #[test] + fn test_process_cr_no_rollup_clears_emitted_rows_and_preserves_pen_state() { + use std::ffi::CString; + + let mut decoder = get_zero_allocated_obj::(); + decoder.current_window = 0; + decoder.tv = Box::into_raw(Box::new(dtvcc_tv_screen { + service_number: 1, + ..Default::default() + })); + + let window = &mut decoder.windows[0]; + window.is_defined = 1; + window.visible = 1; + window.row_count = 2; + window.col_count = 4; + window.pen_row = 1; + window.pen_column = 3; + window.memory_reserved = 1; + window.is_empty = 0; + window.attribs.print_direction = dtvcc_window_pd::DTVCC_WINDOW_PD_LEFT_RIGHT as i32; + window.pen_color_pattern = dtvcc_pen_color { + fg_color: 0x0f, + ..Default::default() + }; + window.pen_attribs_pattern = dtvcc_pen_attribs { + italic: 1, + ..Default::default() + }; + + let layout = Layout::array::(CCX_DTVCC_MAX_COLUMNS as usize).unwrap(); + for row in 0..CCX_DTVCC_MAX_ROWS as usize { + window.rows[row] = unsafe { alloc_zeroed(layout) } as *mut dtvcc_symbol; + } + unsafe { + *window.rows[0] = dtvcc_symbol::new(0x41); + *window.rows[1] = dtvcc_symbol::new(0x42); + } + + let output = tempfile::NamedTempFile::new().unwrap(); + let filename = CString::new(output.path().to_str().unwrap()).unwrap(); + let mut encoder = encoder_ctx::default(); + encoder.dtvcc_writers[0].fd = -1; + encoder.dtvcc_writers[0].filename = filename.as_ptr() as *mut _; + let mut timing = ccx_common_timing_ctx::default(); + + decoder.process_cr(&mut encoder, &mut timing, true); + + assert_eq!(decoder.windows[0].pen_row, 1); + assert_eq!(decoder.windows[0].pen_column, 0); + unsafe { + assert_eq!(*decoder.windows[0].rows[0], dtvcc_symbol::default()); + assert_eq!(*decoder.windows[0].rows[1], dtvcc_symbol::default()); + } + assert_eq!(decoder.windows[0].is_empty, 1); + assert_eq!(decoder.windows[0].pen_color_pattern.fg_color, 0x0f); + assert_eq!(decoder.windows[0].pen_attribs_pattern.italic, 1); + + for row_ptr in decoder.windows[0].rows.iter() { + unsafe { crate::decoder::window::dealloc_row(*row_ptr) }; + } + unsafe { + drop(Box::from_raw(decoder.tv)); + } + } } From 196881b132b8f973bdb4049d2f9af6ffac851d19 Mon Sep 17 00:00:00 2001 From: nlp201 <13403166+nlp201@users.noreply.github.com> Date: Sun, 20 Sep 2026 12:59:29 +0900 Subject: [PATCH 2/2] test: fix CEA-708 no-rollup fixture stack usage --- src/rust/src/decoder/service_decoder.rs | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/rust/src/decoder/service_decoder.rs b/src/rust/src/decoder/service_decoder.rs index b2d022398..b9371ef51 100644 --- a/src/rust/src/decoder/service_decoder.rs +++ b/src/rust/src/decoder/service_decoder.rs @@ -1930,10 +1930,9 @@ mod test { let mut decoder = get_zero_allocated_obj::(); decoder.current_window = 0; - decoder.tv = Box::into_raw(Box::new(dtvcc_tv_screen { - service_number: 1, - ..Default::default() - })); + let mut tv = get_zero_allocated_obj::(); + tv.service_number = 1; + decoder.tv = Box::into_raw(tv); let window = &mut decoder.windows[0]; window.is_defined = 1; @@ -1965,11 +1964,25 @@ mod test { let output = tempfile::NamedTempFile::new().unwrap(); let filename = CString::new(output.path().to_str().unwrap()).unwrap(); - let mut encoder = encoder_ctx::default(); + let mut encoder = get_zero_allocated_obj::(); encoder.dtvcc_writers[0].fd = -1; encoder.dtvcc_writers[0].filename = filename.as_ptr() as *mut _; let mut timing = ccx_common_timing_ctx::default(); + // No-rollup CR before the boundary must only advance the pen, leaving + // the partially populated window intact. + decoder.windows[0].row_count = 4; + decoder.process_cr(&mut encoder, &mut timing, true); + assert_eq!(decoder.windows[0].pen_row, 2); + unsafe { + assert_eq!(*decoder.windows[0].rows[0], dtvcc_symbol::new(0x41)); + assert_eq!(*decoder.windows[0].rows[1], dtvcc_symbol::new(0x42)); + } + + decoder.windows[0].row_count = 2; + decoder.windows[0].pen_row = 1; + decoder.windows[0].pen_column = 3; + decoder.process_cr(&mut encoder, &mut timing, true); assert_eq!(decoder.windows[0].pen_row, 1);