Skip to content

Commit 71e89c4

Browse files
committed
♻️ refactor: rustify writer utility test_free_rows
1 parent 2f4928a commit 71e89c4

File tree

8 files changed

+145
-63
lines changed

8 files changed

+145
-63
lines changed

packages/biliass/rust/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,7 @@ fn biliass_pyo3(m: &Bound<'_, PyModule>) -> PyResult<()> {
3131
m.add_function(wrap_pyfunction!(python::py_convert_color, m)?)?;
3232
m.add_function(wrap_pyfunction!(python::py_get_zoom_factor, m)?)?;
3333
m.add_function(wrap_pyfunction!(python::py_convert_flash_rotation, m)?)?;
34+
m.add_function(wrap_pyfunction!(python::py_test_free_rows, m)?)?;
35+
m.add_class::<python::PyRows>()?;
3436
Ok(())
3537
}

packages/biliass/rust/src/python/comment.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ impl PyCommentPosition {
2727

2828
#[pyclass(name = "Comment", frozen)]
2929
pub struct PyComment {
30-
inner: comment::Comment,
30+
pub inner: comment::Comment,
3131
}
3232

3333
impl PyComment {
@@ -96,7 +96,7 @@ impl PyComment {
9696

9797
#[pyclass(name = "OptionComment", frozen)]
9898
pub struct PyOptionComment {
99-
inner: Option<comment::Comment>,
99+
pub inner: Option<comment::Comment>,
100100
}
101101

102102
impl PyOptionComment {
@@ -107,27 +107,27 @@ impl PyOptionComment {
107107

108108
#[pymethods]
109109
impl PyOptionComment {
110-
fn is_none(&self) -> bool {
110+
pub fn is_none(&self) -> bool {
111111
self.inner.is_none()
112112
}
113113

114-
fn is_some(&self) -> bool {
114+
pub fn is_some(&self) -> bool {
115115
self.inner.is_some()
116116
}
117117

118-
fn unwrap(&self) -> PyComment {
118+
pub fn unwrap(&self) -> PyComment {
119119
PyComment::new(self.inner.clone().unwrap())
120120
}
121121

122122
#[staticmethod]
123-
fn from_comment(comment: &PyComment) -> Self {
123+
pub fn from_comment(comment: &PyComment) -> Self {
124124
PyOptionComment {
125125
inner: Some(comment.inner.clone()),
126126
}
127127
}
128128

129129
#[staticmethod]
130-
fn none() -> Self {
130+
pub fn none() -> Self {
131131
PyOptionComment { inner: None }
132132
}
133133

packages/biliass/rust/src/python/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ pub use proto::{PyDanmakuElem, PyDmSegMobileReply};
88
pub use reader::{py_read_comments_from_protobuf, py_read_comments_from_xml};
99
pub use writer::{
1010
py_ass_escape, py_convert_color, py_convert_flash_rotation, py_convert_timestamp,
11-
py_get_zoom_factor,
11+
py_get_zoom_factor, py_test_free_rows, PyRows,
1212
};

packages/biliass/rust/src/python/writer.rs

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
use crate::writer;
1+
use crate::python;
2+
use crate::writer::{self, rows};
23

34
use pyo3::prelude::*;
45

6+
use super::PyOptionComment;
7+
58
#[pyfunction(name = "convert_timestamp")]
69
pub fn py_convert_timestamp(timestamp: f64) -> PyResult<String> {
710
Ok(writer::utils::convert_timestamp(timestamp))
@@ -40,7 +43,55 @@ pub fn py_convert_flash_rotation(
4043
))
4144
}
4245

43-
// #[pyfunction(name = "test_rows")]
44-
// pub fn py_test_rows(rows: Vec<Vec<PyRef<crate::python::PyOptionComment>>>) -> PyResult<()> {
45-
// Ok(())
46-
// }
46+
#[pyclass(name = "Rows")]
47+
pub struct PyRows {
48+
pub inner: rows::Rows,
49+
}
50+
51+
#[pymethods]
52+
impl PyRows {
53+
#[new]
54+
fn new(num_types: usize, capacity: usize) -> Self {
55+
let mut rows: rows::Rows = Vec::new();
56+
for _ in 0..num_types {
57+
let mut type_rows = Vec::with_capacity(capacity);
58+
for _ in 0..capacity {
59+
type_rows.push(None);
60+
}
61+
rows.push(type_rows);
62+
}
63+
PyRows { inner: rows }
64+
}
65+
66+
fn get(&self, row: usize, col: usize) -> PyOptionComment {
67+
PyOptionComment::new(self.inner[row][col].clone())
68+
}
69+
70+
fn set(&mut self, row: usize, col: usize, value: PyRef<PyOptionComment>) {
71+
self.inner[row][col] = value.inner.clone();
72+
}
73+
}
74+
75+
#[allow(clippy::too_many_arguments)]
76+
#[pyfunction(name = "test_free_rows")]
77+
pub fn py_test_free_rows(
78+
rows: &python::writer::PyRows,
79+
comment: &crate::python::PyComment,
80+
row: usize,
81+
width: u32,
82+
height: u32,
83+
bottom_reserved: u32,
84+
duration_marquee: f64,
85+
duration_still: f64,
86+
) -> PyResult<usize> {
87+
Ok(writer::rows::test_free_rows(
88+
&rows.inner,
89+
&comment.inner,
90+
row,
91+
width,
92+
height,
93+
bottom_reserved,
94+
duration_marquee,
95+
duration_still,
96+
))
97+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
pub mod rows;
12
pub mod utils;
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
use crate::comment::{Comment, CommentPosition};
2+
3+
pub type Rows = Vec<Vec<Option<Comment>>>;
4+
5+
#[allow(clippy::too_many_arguments)]
6+
pub fn test_free_rows(
7+
rows: &Rows,
8+
comment: &Comment,
9+
row: usize,
10+
width: u32,
11+
height: u32,
12+
bottom_reserved: u32,
13+
duration_marquee: f64,
14+
duration_still: f64,
15+
) -> usize {
16+
let mut res = 0;
17+
let rowmax = (height - bottom_reserved) as usize;
18+
let mut target_row = None;
19+
let comment_pos_id = comment.pos.clone() as usize;
20+
if comment.pos == CommentPosition::Bottom || comment.pos == CommentPosition::Top {
21+
let mut current_row = row;
22+
while current_row < rowmax && (res as f32) < comment.height {
23+
if target_row != rows[comment_pos_id][current_row] {
24+
target_row = rows[comment_pos_id][current_row].clone();
25+
if let Some(target_row) = target_row.clone() {
26+
if target_row.timeline + duration_still > comment.timeline {
27+
break;
28+
}
29+
}
30+
}
31+
current_row += 1;
32+
res += 1;
33+
}
34+
} else {
35+
let threshold_time: f64 = comment.timeline
36+
- duration_marquee * (1.0 - width as f64 / (comment.width as f64 + width as f64));
37+
let mut current_row = row;
38+
while current_row < rowmax && (res as f32) < comment.height {
39+
if target_row != rows[comment_pos_id][current_row] {
40+
target_row = rows[comment_pos_id][current_row].clone();
41+
if let Some(target_row) = target_row.clone() {
42+
if target_row.timeline > threshold_time
43+
|| target_row.timeline
44+
+ target_row.width as f64 * duration_marquee
45+
/ (target_row.width as f64 + width as f64)
46+
> comment.timeline
47+
{
48+
break;
49+
}
50+
}
51+
}
52+
current_row += 1;
53+
res += 1;
54+
}
55+
}
56+
res
57+
}

packages/biliass/src/biliass/_core.pyi

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,18 @@ def get_zoom_factor(source_size: tuple[int, int], target_size: tuple[int, int])
7373
def convert_flash_rotation(
7474
rot_y: float, rot_z: float, x: float, y: float, width: float, height: float
7575
) -> tuple[float, float, float, float, float, float, float]: ...
76+
def test_free_rows(
77+
rows: Rows,
78+
comment: Comment,
79+
row: int,
80+
width: int,
81+
height: int,
82+
bottom_reserved: int,
83+
duration_marquee: float,
84+
duration_still: float,
85+
) -> int: ...
86+
87+
class Rows:
88+
def __init__(self, num_types: int, capacity: int) -> None: ...
89+
def get(self, row: int, col: int) -> OptionComment: ...
90+
def set(self, row: int, col: int, OptionComment) -> None: ...

packages/biliass/src/biliass/biliass.py

Lines changed: 6 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,22 @@
1313
Comment,
1414
CommentPosition,
1515
OptionComment,
16+
Rows,
1617
ass_escape,
1718
convert_color,
1819
convert_flash_rotation,
1920
convert_timestamp,
2021
get_zoom_factor,
2122
read_comments_from_protobuf,
2223
read_comments_from_xml,
24+
test_free_rows,
2325
)
2426

2527
if TYPE_CHECKING:
2628
from collections.abc import Callable
2729

2830

2931
T = TypeVar("T")
30-
Rows = list[list[OptionComment]]
3132

3233

3334
def read_comments_bilibili_xml(text: str | bytes, fontsize: float) -> list[Comment]:
@@ -245,7 +246,7 @@ def process_comments(
245246
styleid = f"biliass_{random.randint(0, 0xFFFF):04x}"
246247
ass = AssText()
247248
ass.write_head(width, height, fontface, fontsize, alpha, styleid)
248-
rows = [[OptionComment.none()] * (height - bottom_reserved + 1) for i in range(4)]
249+
rows = Rows(4, height - bottom_reserved + 1)
249250
for idx, comment in enumerate(comments):
250251
if progress_callback and idx % 1000 == 0:
251252
progress_callback(idx, len(comments))
@@ -315,58 +316,13 @@ def process_comments(
315316
return ass.to_string()
316317

317318

318-
def test_free_rows(
319-
rows: Rows,
320-
comment: Comment,
321-
row: int,
322-
width: int,
323-
height: int,
324-
bottom_reserved: int,
325-
duration_marquee: float,
326-
duration_still: float,
327-
) -> int:
328-
res = 0
329-
rowmax = height - bottom_reserved
330-
target_row = OptionComment.none()
331-
comment_pos_id = comment.pos.id
332-
if comment.pos in (CommentPosition.Bottom, CommentPosition.Top):
333-
while row < rowmax and res < comment.height:
334-
if target_row != rows[comment_pos_id][row]:
335-
target_row = rows[comment_pos_id][row]
336-
if target_row.is_some() and target_row.unwrap().timeline + duration_still > comment.timeline:
337-
break
338-
row += 1
339-
res += 1
340-
else:
341-
try:
342-
threshold_time = comment.timeline - duration_marquee * (1 - width / (comment.width + width))
343-
except ZeroDivisionError:
344-
threshold_time = comment.timeline - duration_marquee
345-
while row < rowmax and res < comment.height:
346-
if target_row != rows[comment_pos_id][row]:
347-
target_row = rows[comment_pos_id][row]
348-
try:
349-
if target_row.is_some() and (
350-
target_row.unwrap().timeline > threshold_time
351-
or target_row.unwrap().timeline
352-
+ target_row.unwrap().width * duration_marquee / (target_row.unwrap().width + width)
353-
> comment.timeline
354-
):
355-
break
356-
except ZeroDivisionError:
357-
pass
358-
row += 1
359-
res += 1
360-
return res
361-
362-
363319
def find_alternative_row(rows: Rows, comment: Comment, height, bottom_reserved):
364320
res = 0
365321
comment_pos_id = comment.pos.id
366322
for row in range(height - bottom_reserved - math.ceil(comment.height)):
367-
if rows[comment_pos_id][row].is_none():
323+
if rows.get(comment_pos_id, row).is_none():
368324
return row
369-
elif rows[comment_pos_id][row].unwrap().timeline < rows[comment_pos_id][res].unwrap().timeline:
325+
elif rows.get(comment_pos_id, row).unwrap().timeline < rows.get(comment_pos_id, res).unwrap().timeline:
370326
res = row
371327
return res
372328

@@ -375,7 +331,7 @@ def mark_comment_row(rows: Rows, comment: Comment, row: int):
375331
comment_pos_id = comment.pos.id
376332
try:
377333
for i in range(row, row + math.ceil(comment.height)):
378-
rows[comment_pos_id][i] = OptionComment.from_comment(comment)
334+
rows.set(comment_pos_id, i, OptionComment.from_comment(comment))
379335
except IndexError:
380336
pass
381337

0 commit comments

Comments
 (0)