diff --git a/src/vobsub/img.rs b/src/vobsub/img.rs index 860d4ea..b3f7861 100644 --- a/src/vobsub/img.rs +++ b/src/vobsub/img.rs @@ -1,8 +1,7 @@ //! Run-length encoded image format for subtitles. -use core::fmt; - use cast; +use core::fmt::{self, Debug}; use image::{ImageBuffer, Rgba, RgbaImage}; use log::trace; use nom::{ @@ -15,15 +14,13 @@ use nom::{ use safemem::write_bytes; use thiserror::Error; -use super::{IResultExt, Palette}; +use super::{IResultExt, NomError, Palette, VobSubError}; use crate::{ content::{Area, Size}, image::ImageSize, util::BytesFormatter, }; -use super::NomError; - /// Errors of `vobsub` img management. #[derive(Error, Debug)] pub enum Error { @@ -43,6 +40,40 @@ pub enum Error { ScanLineParsing(#[source] NomError), } +/// Handle `VobSub` Rle image data info in one struct. +pub struct VobSubRleImageData<'a> { + data: [&'a [u8]; 2], +} +impl<'a> VobSubRleImageData<'a> { + pub fn new(raw_data: &'a [u8], rle_offsets: [u16; 2], end: usize) -> Result { + // We know the starting points of each set of scan lines, but we don't + // really know where they end, because encoders like to reuse bytes + // that they're already using for something else. For example, the + // last few bytes of the first set of scan lines may overlap with the + // first bytes of the second set of scanlines, and the last bytes of + // the second set of scan lines may overlap with the start of the + // control sequence. For now, we limit it to the first two bytes of + // the control packet, which are usually `[0x00, 0x00]`. (We might + // actually want to remove `end` entirely here and allow the scan lines + // to go to the end of the packet, but I've never seen that in + // practice.) + let start_0 = cast::usize(rle_offsets[0]); + let start_1 = cast::usize(rle_offsets[1]); + + if start_0 > start_1 || start_1 > end { + Err(VobSubError::InvalidScanLineOffsets { + start_0, + start_1, + end, + }) + } else { + Ok(Self { + data: [&raw_data[start_0..end], &raw_data[start_1..end]], + }) + } + } +} + /// A run-length encoded value. #[derive(Debug)] struct Rle { @@ -115,12 +146,12 @@ fn scan_line(input: &[u8], output: &mut [u8]) -> Result { /// order, starting at the upper-left and scanning right and down, with one /// byte for each 2-bit value. #[profiling::function] -pub fn decompress(size: Size, data: [&[u8]; 2]) -> Result, Error> { +pub fn decompress(size: Size, data: VobSubRleImageData) -> Result, Error> { trace!( "decompressing image {:?}, max: [0x{:x}, 0x{:x}]", &size, - data[0].len(), - data[1].len() + data.data[0].len(), + data.data[1].len() ); let mut img = vec![0; size.w * size.h]; let mut offsets = [0; 2]; @@ -128,7 +159,7 @@ pub fn decompress(size: Size, data: [&[u8]; 2]) -> Result, Error> { let odd = y % 2; trace!("line {:?}, offset 0x{:x}", y, offsets[odd]); let consumed = scan_line( - &data[odd][offsets[odd]..], + &data.data[odd][offsets[odd]..], &mut img[y * size.w..(y + 1) * size.w], )?; offsets[odd] += consumed; diff --git a/src/vobsub/sub.rs b/src/vobsub/sub.rs index 879c6bc..3cff3e4 100644 --- a/src/vobsub/sub.rs +++ b/src/vobsub/sub.rs @@ -4,6 +4,16 @@ //! //! [subs]: http://sam.zoy.org/writings/dvd/subtitles/ +use super::{ + img::{decompress, VobSubIndexedImage}, + mpeg2::ps, + VobSubError, +}; +use crate::{ + content::{Area, AreaValues}, + util::BytesFormatter, + vobsub::{img::VobSubRleImageData, IResultExt}, +}; use cast; use log::{trace, warn}; use nom::{ @@ -23,13 +33,6 @@ use std::{ }; use thiserror::Error; -use super::{img::decompress, mpeg2::ps, VobSubError}; -use crate::{ - content::{Area, AreaValues}, - util::BytesFormatter, - vobsub::{img::VobSubIndexedImage, IResultExt}, -}; - /// The default time between two adjacent subtitles if no end time is /// provided. This is chosen to be a value that's usually representable in /// `SRT` format, barring rounding errors. @@ -404,32 +407,10 @@ where // Decompress our image. // - // We know the starting points of each set of scan lines, but we don't - // really know where they end, because encoders like to reuse bytes - // that they're already using for something else. For example, the - // last few bytes of the first set of scan lines may overlap with the - // first bytes of the second set of scanlines, and the last bytes of - // the second set of scan lines may overlap with the start of the - // control sequence. For now, we limit it to the first two bytes of - // the control packet, which are usually `[0x00, 0x00]`. (We might - // actually want to remove `end` entirely here and allow the scan lines - // to go to the end of the packet, but I've never seen that in - // practice.) - let start_0 = cast::usize(rle_offsets[0]); - let start_1 = cast::usize(rle_offsets[1]); - let end = cast::usize(initial_control_offset + 2); - if start_0 > start_1 || start_1 > end { - return Err(VobSubError::InvalidScanLineOffsets { - start_0, - start_1, - end, - }); - } - let image = decompress( - area.size(), - [&raw_data[start_0..end], &raw_data[start_1..end]], - )?; + let end = cast::usize(initial_control_offset + 2); + let image_data = VobSubRleImageData::new(raw_data, rle_offsets, end)?; + let image = decompress(area.size(), image_data)?; let indexed_image = VobSubIndexedImage::new(area, palette, alpha, image); // Return our parsed subtitle.