Skip to content

Commit

Permalink
wip: VobSubRleImageData
Browse files Browse the repository at this point in the history
Handle `VobSub` Rle image data info in one struct.
  • Loading branch information
gwen-lg committed May 29, 2024
1 parent e59aee5 commit 3048b11
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 41 deletions.
49 changes: 40 additions & 9 deletions src/vobsub/img.rs
Original file line number Diff line number Diff line change
@@ -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::{
Expand All @@ -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 {
Expand All @@ -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<Self, VobSubError> {
// 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 {
Expand Down Expand Up @@ -115,20 +146,20 @@ fn scan_line(input: &[u8], output: &mut [u8]) -> Result<usize, Error> {
/// 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<Vec<u8>, Error> {
pub fn decompress(size: Size, data: VobSubRleImageData) -> Result<Vec<u8>, 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];
for y in 0..size.h {
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;
Expand Down
45 changes: 13 additions & 32 deletions src/vobsub/sub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand All @@ -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.
Expand Down Expand Up @@ -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.
Expand Down

0 comments on commit 3048b11

Please sign in to comment.