1818#endif
1919
2020int log_level = 3 ;
21+ int *is_event_animated;
2122
2223typedef struct {
2324 void *buffer;
@@ -62,6 +63,7 @@ void buffer_init(buffer_t *buf) {
6263
6364void buffer_free (buffer_t *buf) {
6465 free (buf->buffer );
66+ buffer_init (buf);
6567}
6668
6769void msg_callback (int level, const char *fmt, va_list va, void *data) {
@@ -102,31 +104,115 @@ double libassjs_find_next_event_start(double tm) {
102104 return closest / 1000.0 ;
103105}
104106
105- int _is_event_complex (ASS_Event *event) {
107+ static int _is_move_tag_animated (char *begin, char *end) {
108+ int params[6 ];
109+ int count = 0 , value = 0 , num_digits = 0 ;
110+ for (; begin < end; begin++) {
111+ switch (*begin) {
112+ case ' ' : // fallthrough
113+ case ' \t ' :
114+ break ;
115+ case ' ,' :
116+ params[count] = value;
117+ count++;
118+ value = 0 ;
119+ num_digits = 0 ;
120+ break ;
121+ default : {
122+ int digit = *begin - ' 0' ;
123+ if (digit < 0 || digit > 9 ) return 0 ; // invalid move
124+ value = value * 10 + digit;
125+ num_digits++;
126+ break ;
127+ }
128+ }
129+ }
130+ if (num_digits > 0 ) {
131+ params[count] = value;
132+ count++;
133+ }
134+ if (count < 4 ) return 0 ; // invalid move
135+
136+ // move is animated if (x1,y1) != (x2,y2)
137+ return params[0 ] != params[2 ] || params[1 ] != params[3 ];
138+ }
139+
140+ static int _is_animated_tag (char *begin, char *end) {
141+ // strip whitespaces around the tag
142+ while (begin < end && (*begin == ' ' || *begin == ' \t ' )) begin++;
143+ while (end > begin && (end[-1 ] == ' ' || end[-1 ] == ' \t ' )) end--;
144+
145+ int length = end - begin;
146+ if (length < 3 || *begin != ' \\ ' ) return 0 ; // too short to be animated or not a command
147+
148+ switch (begin[1 ]) {
149+ case ' k' : // fallthrough
150+ case ' K' :
151+ // \kXX is karaoke
152+ return 1 ;
153+ case ' t' :
154+ // \t(...) is transition
155+ return length >= 4 && begin[2 ] == ' (' && end[-1 ] == ' )' ;
156+ case ' m' :
157+ if (length >=7 && end[-1 ] == ' )' && strcmp (begin, " \\ move(" ) == 0 ) {
158+ return _is_move_tag_animated (begin + 6 , end - 1 );
159+ }
160+ break ;
161+ case ' f' :
162+ // \fad() or \fade() are fades
163+ return (length >= 7 && end[-1 ] == ' )' &&
164+ (strcmp (begin, " \\ fad(" ) == 0 || strcmp (begin, " \\ fade(" ) == 0 ));
165+ }
166+
167+ return 0 ;
168+ }
169+
170+ static int _is_event_animated (ASS_Event *event) {
106171 // event is complex if it's animated in any way,
107172 // either by having non-empty Effect or
108173 // by having tags (enclosed in '{}' in Text)
109174 if (event->Effect && event->Effect [0 ] != ' \0 ' ) return 1 ;
110175
111176 int escaped = 0 ;
177+ char *tagStart = NULL ;
112178 for (char *p = event->Text ; *p != ' \0 ' ; p++) {
113179 switch (*p) {
114180 case ' \\ ' :
115181 escaped = !escaped;
116182 break ;
117183 case ' {' :
118- if (escaped) return 1 ;
184+ if (!escaped && tagStart == NULL ) tagStart = p + 1 ;
185+ break ;
186+ case ' }' :
187+ if (!escaped && tagStart != NULL ) {
188+ if (_is_animated_tag (tagStart, p)) return 1 ;
189+ tagStart = NULL ;
190+ }
191+ break ;
192+ case ' ;' :
193+ if (tagStart != NULL ) {
194+ if (_is_animated_tag (tagStart, p)) return 1 ;
195+ }
196+ tagStart = p + 1 ;
119197 break ;
120198 }
121199 }
122200
123201 return 0 ;
124202}
125203
126- int libassjs_find_event_stop_times (double tm, double *eventFinish, double *emptyFinish) {
204+ static void detect_animated_events () {
205+ ASS_Event *cur = track->events ;
206+ int *animated = is_animated_events;
207+ for (int i = 0 ; i < track->n_events ; i++, cur++, animated++) {
208+ *animated = _is_event_animated (cur);
209+ }
210+ }
211+
212+ void libassjs_find_event_stop_times (double tm, double *eventFinish, double *emptyFinish, int *is_animated) {
127213 if (!track || track->n_events == 0 ) {
128214 *eventFinish = *emptyFinish = -1 ;
129- return 0 ;
215+ return ;
130216 }
131217
132218 ASS_Event *cur = track->events ;
@@ -146,12 +232,13 @@ int libassjs_find_event_stop_times(double tm, double *eventFinish, double *empty
146232 if (finish > maxFinish) {
147233 maxFinish = finish;
148234 }
149- if (!current_animated && _is_event_complex (cur)) current_animated = 1 ;
235+ if (!current_animated) current_animated = is_event_animated[i] ;
150236 }
151237 } else if (start < minStart || minStart == -1 ) {
152238 minStart = start;
153239 }
154240 }
241+ *is_animated = current_animated;
155242
156243 if (minFinish != -1 ) {
157244 // some event is going on, so we need to re-draw either when it stops
@@ -169,8 +256,6 @@ int libassjs_find_event_stop_times(double tm, double *eventFinish, double *empty
169256 // there's no empty space after eventFinish happens
170257 *emptyFinish = *eventFinish;
171258 }
172-
173- return current_animated;
174259}
175260
176261class SubtitleOctopus {
@@ -216,6 +301,7 @@ class SubtitleOctopus {
216301
217302 reloadFonts ();
218303 buffer_init (&m_blend);
304+ is_event_animated = NULL ;
219305 }
220306
221307 /* TRACK */
@@ -226,6 +312,14 @@ class SubtitleOctopus {
226312 printf (" Failed to start a track\n " );
227313 exit (4 );
228314 }
315+
316+ free (is_event_animated);
317+ is_event_animated = (int *)malloc (sizeof (int ) * track->n_events );
318+ if (is_event_animated == NULL ) {
319+ printf (" cannot parse animated events\n " );
320+ exit (5 );
321+ }
322+ detect_animated_events ();
229323 }
230324
231325 void createTrackMem (char *buf, unsigned long bufsize) {
@@ -242,6 +336,8 @@ class SubtitleOctopus {
242336 ass_free_track (track);
243337 track = NULL ;
244338 }
339+ free (is_event_animated);
340+ is_event_animated = NULL ;
245341 }
246342 /* TRACK */
247343
@@ -262,6 +358,8 @@ class SubtitleOctopus {
262358 ass_renderer_done (ass_renderer);
263359 ass_library_done (ass_library);
264360 buffer_free (&m_blend);
361+ free (is_event_animated);
362+ is_event_animated = NULL ;
265363 }
266364 void reloadLibrary () {
267365 quitLibrary ();
0 commit comments