Mercurial > mplayer.hg
comparison libass/ass_parse.c @ 30200:48d020c5ceca
Update internal libass copy to commit 8db4a5
author | greg |
---|---|
date | Fri, 08 Jan 2010 18:35:44 +0000 |
parents | |
children | e64df5862cea |
comparison
equal
deleted
inserted
replaced
30199:f9984b2fc1b2 | 30200:48d020c5ceca |
---|---|
1 /* | |
2 * Copyright (C) 2009 Grigori Goronzy <greg@geekmind.org> | |
3 * | |
4 * This file is part of libass. | |
5 * | |
6 * Permission to use, copy, modify, and distribute this software for any | |
7 * purpose with or without fee is hereby granted, provided that the above | |
8 * copyright notice and this permission notice appear in all copies. | |
9 * | |
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
17 */ | |
18 | |
19 #include "config.h" | |
20 | |
21 #include <stdio.h> | |
22 #include <stdlib.h> | |
23 #include <string.h> | |
24 #include <math.h> | |
25 | |
26 #include "ass_render.h" | |
27 #include "ass_parse.h" | |
28 | |
29 #define MAX_BE 127 | |
30 #define NBSP 0xa0 // unicode non-breaking space character | |
31 | |
32 #define skip_to(x) while ((*p != (x)) && (*p != '}') && (*p != 0)) { ++p;} | |
33 #define skip(x) if (*p == (x)) ++p; else { return p; } | |
34 #define skipopt(x) if (*p == (x)) { ++p; } | |
35 | |
36 /** | |
37 * \brief Check if starting part of (*p) matches sample. | |
38 * If true, shift p to the first symbol after the matching part. | |
39 */ | |
40 static inline int mystrcmp(char **p, const char *sample) | |
41 { | |
42 int len = strlen(sample); | |
43 if (strncmp(*p, sample, len) == 0) { | |
44 (*p) += len; | |
45 return 1; | |
46 } else | |
47 return 0; | |
48 } | |
49 | |
50 static void change_font_size(ASS_Renderer *render_priv, double sz) | |
51 { | |
52 double size = sz * render_priv->font_scale; | |
53 | |
54 if (size < 1) | |
55 size = 1; | |
56 else if (size > render_priv->height * 2) | |
57 size = render_priv->height * 2; | |
58 | |
59 ass_font_set_size(render_priv->state.font, size); | |
60 | |
61 render_priv->state.font_size = sz; | |
62 } | |
63 | |
64 /** | |
65 * \brief Change current font, using setting from render_priv->state. | |
66 */ | |
67 void update_font(ASS_Renderer *render_priv) | |
68 { | |
69 unsigned val; | |
70 ASS_FontDesc desc; | |
71 desc.family = strdup(render_priv->state.family); | |
72 desc.treat_family_as_pattern = | |
73 render_priv->state.treat_family_as_pattern; | |
74 | |
75 val = render_priv->state.bold; | |
76 // 0 = normal, 1 = bold, >1 = exact weight | |
77 if (val == 1 || val == -1) | |
78 val = 200; // bold | |
79 else if (val <= 0) | |
80 val = 80; // normal | |
81 desc.bold = val; | |
82 | |
83 val = render_priv->state.italic; | |
84 if (val == 1 || val == -1) | |
85 val = 110; // italic | |
86 else if (val <= 0) | |
87 val = 0; // normal | |
88 desc.italic = val; | |
89 | |
90 render_priv->state.font = | |
91 ass_font_new(render_priv->cache.font_cache, render_priv->library, | |
92 render_priv->ftlibrary, render_priv->fontconfig_priv, | |
93 &desc); | |
94 free(desc.family); | |
95 | |
96 if (render_priv->state.font) | |
97 change_font_size(render_priv, render_priv->state.font_size); | |
98 } | |
99 | |
100 /** | |
101 * \brief Change border width | |
102 * negative value resets border to style value | |
103 */ | |
104 void change_border(ASS_Renderer *render_priv, double border_x, | |
105 double border_y) | |
106 { | |
107 int bord; | |
108 if (!render_priv->state.font) | |
109 return; | |
110 | |
111 if (border_x < 0 && border_y < 0) { | |
112 if (render_priv->state.style->BorderStyle == 1 || | |
113 render_priv->state.style->BorderStyle == 3) | |
114 border_x = border_y = render_priv->state.style->Outline; | |
115 else | |
116 border_x = border_y = 1.; | |
117 } | |
118 | |
119 render_priv->state.border_x = border_x; | |
120 render_priv->state.border_y = border_y; | |
121 | |
122 bord = 64 * border_x * render_priv->border_scale; | |
123 if (bord > 0 && border_x == border_y) { | |
124 if (!render_priv->state.stroker) { | |
125 int error; | |
126 error = | |
127 FT_Stroker_New(render_priv->ftlibrary, | |
128 &render_priv->state.stroker); | |
129 if (error) { | |
130 ass_msg(render_priv->library, MSGL_V, | |
131 "failed to get stroker"); | |
132 render_priv->state.stroker = 0; | |
133 } | |
134 } | |
135 if (render_priv->state.stroker) | |
136 FT_Stroker_Set(render_priv->state.stroker, bord, | |
137 FT_STROKER_LINECAP_ROUND, | |
138 FT_STROKER_LINEJOIN_ROUND, 0); | |
139 } else { | |
140 FT_Stroker_Done(render_priv->state.stroker); | |
141 render_priv->state.stroker = 0; | |
142 } | |
143 } | |
144 | |
145 /** | |
146 * \brief Calculate a weighted average of two colors | |
147 * calculates c1*(1-a) + c2*a, but separately for each component except alpha | |
148 */ | |
149 static void change_color(uint32_t *var, uint32_t new, double pwr) | |
150 { | |
151 (*var) = ((uint32_t) (_r(*var) * (1 - pwr) + _r(new) * pwr) << 24) + | |
152 ((uint32_t) (_g(*var) * (1 - pwr) + _g(new) * pwr) << 16) + | |
153 ((uint32_t) (_b(*var) * (1 - pwr) + _b(new) * pwr) << 8) + _a(*var); | |
154 } | |
155 | |
156 // like change_color, but for alpha component only | |
157 inline void change_alpha(uint32_t *var, uint32_t new, double pwr) | |
158 { | |
159 *var = | |
160 (_r(*var) << 24) + (_g(*var) << 16) + (_b(*var) << 8) + | |
161 (uint32_t) (_a(*var) * (1 - pwr) + _a(new) * pwr); | |
162 } | |
163 | |
164 /** | |
165 * \brief Multiply two alpha values | |
166 * \param a first value | |
167 * \param b second value | |
168 * \return result of multiplication | |
169 * Parameters and result are limited by 0xFF. | |
170 */ | |
171 inline uint32_t mult_alpha(uint32_t a, uint32_t b) | |
172 { | |
173 return 0xFF - (0xFF - a) * (0xFF - b) / 0xFF; | |
174 } | |
175 | |
176 /** | |
177 * \brief Calculate alpha value by piecewise linear function | |
178 * Used for \fad, \fade implementation. | |
179 */ | |
180 static unsigned | |
181 interpolate_alpha(long long now, long long t1, long long t2, long long t3, | |
182 long long t4, unsigned a1, unsigned a2, unsigned a3) | |
183 { | |
184 unsigned a; | |
185 double cf; | |
186 if (now <= t1) { | |
187 a = a1; | |
188 } else if (now >= t4) { | |
189 a = a3; | |
190 } else if (now < t2) { // and > t1 | |
191 cf = ((double) (now - t1)) / (t2 - t1); | |
192 a = a1 * (1 - cf) + a2 * cf; | |
193 } else if (now > t3) { | |
194 cf = ((double) (now - t3)) / (t4 - t3); | |
195 a = a2 * (1 - cf) + a3 * cf; | |
196 } else { // t2 <= now <= t3 | |
197 a = a2; | |
198 } | |
199 | |
200 return a; | |
201 } | |
202 | |
203 /** | |
204 * Parse a vector clip into an outline, using the proper scaling | |
205 * parameters. Translate it to correct for screen borders, if needed. | |
206 */ | |
207 static char *parse_vector_clip(ASS_Renderer *render_priv, char *p) | |
208 { | |
209 int scale = 1; | |
210 int res = 0; | |
211 ASS_Drawing *drawing; | |
212 | |
213 render_priv->state.clip_drawing = ass_drawing_new( | |
214 render_priv->fontconfig_priv, | |
215 render_priv->state.font, | |
216 render_priv->settings.hinting, | |
217 render_priv->ftlibrary); | |
218 drawing = render_priv->state.clip_drawing; | |
219 skipopt('('); | |
220 res = mystrtoi(&p, &scale); | |
221 skipopt(',') | |
222 if (!res) | |
223 scale = 1; | |
224 drawing->scale = scale; | |
225 drawing->scale_x = render_priv->font_scale_x * render_priv->font_scale; | |
226 drawing->scale_y = render_priv->font_scale; | |
227 while (*p != ')' && *p != '}' && p != 0) | |
228 ass_drawing_add_char(drawing, *p++); | |
229 skipopt(')'); | |
230 if (ass_drawing_parse(drawing, 1)) { | |
231 // We need to translate the clip according to screen borders | |
232 if (render_priv->settings.left_margin != 0 || | |
233 render_priv->settings.top_margin != 0) { | |
234 FT_Vector trans = { | |
235 .x = int_to_d6(render_priv->settings.left_margin), | |
236 .y = -int_to_d6(render_priv->settings.top_margin), | |
237 }; | |
238 FT_Outline_Translate(&drawing->glyph->outline, trans.x, trans.y); | |
239 } | |
240 ass_msg(render_priv->library, MSGL_DBG2, | |
241 "Parsed vector clip: scale %d, scales (%f, %f) string [%s]\n", | |
242 scale, drawing->scale_x, drawing->scale_y, drawing->text); | |
243 } | |
244 | |
245 return p; | |
246 } | |
247 | |
248 /** | |
249 * \brief Parse style override tag. | |
250 * \param p string to parse | |
251 * \param pwr multiplier for some tag effects (comes from \t tags) | |
252 */ | |
253 static char *parse_tag(ASS_Renderer *render_priv, char *p, double pwr) | |
254 { | |
255 skip_to('\\'); | |
256 skip('\\'); | |
257 if ((*p == '}') || (*p == 0)) | |
258 return p; | |
259 | |
260 // New tags introduced in vsfilter 2.39 | |
261 if (mystrcmp(&p, "xbord")) { | |
262 double val; | |
263 if (mystrtod(&p, &val)) | |
264 val = render_priv->state.border_x * (1 - pwr) + val * pwr; | |
265 else | |
266 val = -1.; | |
267 change_border(render_priv, val, render_priv->state.border_y); | |
268 } else if (mystrcmp(&p, "ybord")) { | |
269 double val; | |
270 if (mystrtod(&p, &val)) | |
271 val = render_priv->state.border_y * (1 - pwr) + val * pwr; | |
272 else | |
273 val = -1.; | |
274 change_border(render_priv, render_priv->state.border_x, val); | |
275 } else if (mystrcmp(&p, "xshad")) { | |
276 double val; | |
277 if (mystrtod(&p, &val)) | |
278 val = render_priv->state.shadow_x * (1 - pwr) + val * pwr; | |
279 else | |
280 val = 0.; | |
281 render_priv->state.shadow_x = val; | |
282 } else if (mystrcmp(&p, "yshad")) { | |
283 double val; | |
284 if (mystrtod(&p, &val)) | |
285 val = render_priv->state.shadow_y * (1 - pwr) + val * pwr; | |
286 else | |
287 val = 0.; | |
288 render_priv->state.shadow_y = val; | |
289 } else if (mystrcmp(&p, "fax")) { | |
290 double val; | |
291 if (mystrtod(&p, &val)) | |
292 render_priv->state.fax = | |
293 val * pwr + render_priv->state.fax * (1 - pwr); | |
294 else | |
295 render_priv->state.fax = 0.; | |
296 } else if (mystrcmp(&p, "fay")) { | |
297 double val; | |
298 if (mystrtod(&p, &val)) | |
299 render_priv->state.fay = | |
300 val * pwr + render_priv->state.fay * (1 - pwr); | |
301 else | |
302 render_priv->state.fay = 0.; | |
303 } else if (mystrcmp(&p, "iclip")) { | |
304 int x0, y0, x1, y1; | |
305 int res = 1; | |
306 char *start = p; | |
307 skipopt('('); | |
308 res &= mystrtoi(&p, &x0); | |
309 skipopt(','); | |
310 res &= mystrtoi(&p, &y0); | |
311 skipopt(','); | |
312 res &= mystrtoi(&p, &x1); | |
313 skipopt(','); | |
314 res &= mystrtoi(&p, &y1); | |
315 skipopt(')'); | |
316 if (res) { | |
317 render_priv->state.clip_x0 = | |
318 render_priv->state.clip_x0 * (1 - pwr) + x0 * pwr; | |
319 render_priv->state.clip_x1 = | |
320 render_priv->state.clip_x1 * (1 - pwr) + x1 * pwr; | |
321 render_priv->state.clip_y0 = | |
322 render_priv->state.clip_y0 * (1 - pwr) + y0 * pwr; | |
323 render_priv->state.clip_y1 = | |
324 render_priv->state.clip_y1 * (1 - pwr) + y1 * pwr; | |
325 render_priv->state.clip_mode = 1; | |
326 } else if (!render_priv->state.clip_drawing) { | |
327 p = parse_vector_clip(render_priv, start); | |
328 render_priv->state.clip_drawing_mode = 1; | |
329 } else | |
330 render_priv->state.clip_mode = 0; | |
331 } else if (mystrcmp(&p, "blur")) { | |
332 double val; | |
333 if (mystrtod(&p, &val)) { | |
334 val = render_priv->state.blur * (1 - pwr) + val * pwr; | |
335 val = (val < 0) ? 0 : val; | |
336 val = (val > BLUR_MAX_RADIUS) ? BLUR_MAX_RADIUS : val; | |
337 render_priv->state.blur = val; | |
338 } else | |
339 render_priv->state.blur = 0.0; | |
340 // ASS standard tags | |
341 } else if (mystrcmp(&p, "fsc")) { | |
342 char tp = *p++; | |
343 double val; | |
344 if (tp == 'x') { | |
345 if (mystrtod(&p, &val)) { | |
346 val /= 100; | |
347 render_priv->state.scale_x = | |
348 render_priv->state.scale_x * (1 - pwr) + val * pwr; | |
349 } else | |
350 render_priv->state.scale_x = | |
351 render_priv->state.style->ScaleX; | |
352 } else if (tp == 'y') { | |
353 if (mystrtod(&p, &val)) { | |
354 val /= 100; | |
355 render_priv->state.scale_y = | |
356 render_priv->state.scale_y * (1 - pwr) + val * pwr; | |
357 } else | |
358 render_priv->state.scale_y = | |
359 render_priv->state.style->ScaleY; | |
360 } | |
361 } else if (mystrcmp(&p, "fsp")) { | |
362 double val; | |
363 if (mystrtod(&p, &val)) | |
364 render_priv->state.hspacing = | |
365 render_priv->state.hspacing * (1 - pwr) + val * pwr; | |
366 else | |
367 render_priv->state.hspacing = render_priv->state.style->Spacing; | |
368 } else if (mystrcmp(&p, "fs")) { | |
369 double val; | |
370 if (mystrtod(&p, &val)) | |
371 val = render_priv->state.font_size * (1 - pwr) + val * pwr; | |
372 else | |
373 val = render_priv->state.style->FontSize; | |
374 if (render_priv->state.font) | |
375 change_font_size(render_priv, val); | |
376 } else if (mystrcmp(&p, "bord")) { | |
377 double val; | |
378 if (mystrtod(&p, &val)) { | |
379 if (render_priv->state.border_x == render_priv->state.border_y) | |
380 val = render_priv->state.border_x * (1 - pwr) + val * pwr; | |
381 } else | |
382 val = -1.; // reset to default | |
383 change_border(render_priv, val, val); | |
384 } else if (mystrcmp(&p, "move")) { | |
385 double x1, x2, y1, y2; | |
386 long long t1, t2, delta_t, t; | |
387 double x, y; | |
388 double k; | |
389 skip('('); | |
390 mystrtod(&p, &x1); | |
391 skip(','); | |
392 mystrtod(&p, &y1); | |
393 skip(','); | |
394 mystrtod(&p, &x2); | |
395 skip(','); | |
396 mystrtod(&p, &y2); | |
397 if (*p == ',') { | |
398 skip(','); | |
399 mystrtoll(&p, &t1); | |
400 skip(','); | |
401 mystrtoll(&p, &t2); | |
402 ass_msg(render_priv->library, MSGL_DBG2, | |
403 "movement6: (%f, %f) -> (%f, %f), (%" PRId64 " .. %" | |
404 PRId64 ")\n", x1, y1, x2, y2, (int64_t) t1, | |
405 (int64_t) t2); | |
406 } else { | |
407 t1 = 0; | |
408 t2 = render_priv->state.event->Duration; | |
409 ass_msg(render_priv->library, MSGL_DBG2, | |
410 "movement: (%f, %f) -> (%f, %f)", x1, y1, x2, y2); | |
411 } | |
412 skip(')'); | |
413 delta_t = t2 - t1; | |
414 t = render_priv->time - render_priv->state.event->Start; | |
415 if (t < t1) | |
416 k = 0.; | |
417 else if (t > t2) | |
418 k = 1.; | |
419 else | |
420 k = ((double) (t - t1)) / delta_t; | |
421 x = k * (x2 - x1) + x1; | |
422 y = k * (y2 - y1) + y1; | |
423 if (render_priv->state.evt_type != EVENT_POSITIONED) { | |
424 render_priv->state.pos_x = x; | |
425 render_priv->state.pos_y = y; | |
426 render_priv->state.detect_collisions = 0; | |
427 render_priv->state.evt_type = EVENT_POSITIONED; | |
428 } | |
429 } else if (mystrcmp(&p, "frx")) { | |
430 double val; | |
431 if (mystrtod(&p, &val)) { | |
432 val *= M_PI / 180; | |
433 render_priv->state.frx = | |
434 val * pwr + render_priv->state.frx * (1 - pwr); | |
435 } else | |
436 render_priv->state.frx = 0.; | |
437 } else if (mystrcmp(&p, "fry")) { | |
438 double val; | |
439 if (mystrtod(&p, &val)) { | |
440 val *= M_PI / 180; | |
441 render_priv->state.fry = | |
442 val * pwr + render_priv->state.fry * (1 - pwr); | |
443 } else | |
444 render_priv->state.fry = 0.; | |
445 } else if (mystrcmp(&p, "frz") || mystrcmp(&p, "fr")) { | |
446 double val; | |
447 if (mystrtod(&p, &val)) { | |
448 val *= M_PI / 180; | |
449 render_priv->state.frz = | |
450 val * pwr + render_priv->state.frz * (1 - pwr); | |
451 } else | |
452 render_priv->state.frz = | |
453 M_PI * render_priv->state.style->Angle / 180.; | |
454 } else if (mystrcmp(&p, "fn")) { | |
455 char *start = p; | |
456 char *family; | |
457 skip_to('\\'); | |
458 if (p > start) { | |
459 family = malloc(p - start + 1); | |
460 strncpy(family, start, p - start); | |
461 family[p - start] = '\0'; | |
462 } else | |
463 family = strdup(render_priv->state.style->FontName); | |
464 if (render_priv->state.family) | |
465 free(render_priv->state.family); | |
466 render_priv->state.family = family; | |
467 update_font(render_priv); | |
468 } else if (mystrcmp(&p, "alpha")) { | |
469 uint32_t val; | |
470 int i; | |
471 int hex = render_priv->track->track_type == TRACK_TYPE_ASS; | |
472 if (strtocolor(render_priv->library, &p, &val, hex)) { | |
473 unsigned char a = val >> 24; | |
474 for (i = 0; i < 4; ++i) | |
475 change_alpha(&render_priv->state.c[i], a, pwr); | |
476 } else { | |
477 change_alpha(&render_priv->state.c[0], | |
478 render_priv->state.style->PrimaryColour, pwr); | |
479 change_alpha(&render_priv->state.c[1], | |
480 render_priv->state.style->SecondaryColour, pwr); | |
481 change_alpha(&render_priv->state.c[2], | |
482 render_priv->state.style->OutlineColour, pwr); | |
483 change_alpha(&render_priv->state.c[3], | |
484 render_priv->state.style->BackColour, pwr); | |
485 } | |
486 // FIXME: simplify | |
487 } else if (mystrcmp(&p, "an")) { | |
488 int val; | |
489 if (mystrtoi(&p, &val) && val) { | |
490 int v = (val - 1) / 3; // 0, 1 or 2 for vertical alignment | |
491 ass_msg(render_priv->library, MSGL_DBG2, "an %d", val); | |
492 if (v != 0) | |
493 v = 3 - v; | |
494 val = ((val - 1) % 3) + 1; // horizontal alignment | |
495 val += v * 4; | |
496 ass_msg(render_priv->library, MSGL_DBG2, "align %d", val); | |
497 render_priv->state.alignment = val; | |
498 } else | |
499 render_priv->state.alignment = | |
500 render_priv->state.style->Alignment; | |
501 } else if (mystrcmp(&p, "a")) { | |
502 int val; | |
503 if (mystrtoi(&p, &val) && val) | |
504 // take care of a vsfilter quirk: handle illegal \a8 like \a5 | |
505 render_priv->state.alignment = (val == 8) ? 5 : val; | |
506 else | |
507 render_priv->state.alignment = | |
508 render_priv->state.style->Alignment; | |
509 } else if (mystrcmp(&p, "pos")) { | |
510 double v1, v2; | |
511 skip('('); | |
512 mystrtod(&p, &v1); | |
513 skip(','); | |
514 mystrtod(&p, &v2); | |
515 skip(')'); | |
516 ass_msg(render_priv->library, MSGL_DBG2, "pos(%f, %f)", v1, v2); | |
517 if (render_priv->state.evt_type == EVENT_POSITIONED) { | |
518 ass_msg(render_priv->library, MSGL_V, "Subtitle has a new \\pos " | |
519 "after \\move or \\pos, ignoring"); | |
520 } else { | |
521 render_priv->state.evt_type = EVENT_POSITIONED; | |
522 render_priv->state.detect_collisions = 0; | |
523 render_priv->state.pos_x = v1; | |
524 render_priv->state.pos_y = v2; | |
525 } | |
526 } else if (mystrcmp(&p, "fad")) { | |
527 int a1, a2, a3; | |
528 long long t1, t2, t3, t4; | |
529 if (*p == 'e') | |
530 ++p; // either \fad or \fade | |
531 skip('('); | |
532 mystrtoi(&p, &a1); | |
533 skip(','); | |
534 mystrtoi(&p, &a2); | |
535 if (*p == ')') { | |
536 // 2-argument version (\fad, according to specs) | |
537 // a1 and a2 are fade-in and fade-out durations | |
538 t1 = 0; | |
539 t4 = render_priv->state.event->Duration; | |
540 t2 = a1; | |
541 t3 = t4 - a2; | |
542 a1 = 0xFF; | |
543 a2 = 0; | |
544 a3 = 0xFF; | |
545 } else { | |
546 // 6-argument version (\fade) | |
547 // a1 and a2 (and a3) are opacity values | |
548 skip(','); | |
549 mystrtoi(&p, &a3); | |
550 skip(','); | |
551 mystrtoll(&p, &t1); | |
552 skip(','); | |
553 mystrtoll(&p, &t2); | |
554 skip(','); | |
555 mystrtoll(&p, &t3); | |
556 skip(','); | |
557 mystrtoll(&p, &t4); | |
558 } | |
559 skip(')'); | |
560 render_priv->state.fade = | |
561 interpolate_alpha(render_priv->time - | |
562 render_priv->state.event->Start, t1, t2, | |
563 t3, t4, a1, a2, a3); | |
564 } else if (mystrcmp(&p, "org")) { | |
565 int v1, v2; | |
566 skip('('); | |
567 mystrtoi(&p, &v1); | |
568 skip(','); | |
569 mystrtoi(&p, &v2); | |
570 skip(')'); | |
571 ass_msg(render_priv->library, MSGL_DBG2, "org(%d, %d)", v1, v2); | |
572 if (!render_priv->state.have_origin) { | |
573 render_priv->state.org_x = v1; | |
574 render_priv->state.org_y = v2; | |
575 render_priv->state.have_origin = 1; | |
576 render_priv->state.detect_collisions = 0; | |
577 } | |
578 } else if (mystrcmp(&p, "t")) { | |
579 double v[3]; | |
580 int v1, v2; | |
581 double v3; | |
582 int cnt; | |
583 long long t1, t2, t, delta_t; | |
584 double k; | |
585 skip('('); | |
586 for (cnt = 0; cnt < 3; ++cnt) { | |
587 if (*p == '\\') | |
588 break; | |
589 v[cnt] = strtod(p, &p); | |
590 skip(','); | |
591 } | |
592 if (cnt == 3) { | |
593 v1 = v[0]; | |
594 v2 = (v[1] < v1) ? render_priv->state.event->Duration : v[1]; | |
595 v3 = v[2]; | |
596 } else if (cnt == 2) { | |
597 v1 = v[0]; | |
598 v2 = (v[1] < v1) ? render_priv->state.event->Duration : v[1]; | |
599 v3 = 1.; | |
600 } else if (cnt == 1) { | |
601 v1 = 0; | |
602 v2 = render_priv->state.event->Duration; | |
603 v3 = v[0]; | |
604 } else { // cnt == 0 | |
605 v1 = 0; | |
606 v2 = render_priv->state.event->Duration; | |
607 v3 = 1.; | |
608 } | |
609 render_priv->state.detect_collisions = 0; | |
610 t1 = v1; | |
611 t2 = v2; | |
612 delta_t = v2 - v1; | |
613 if (v3 < 0.) | |
614 v3 = 0.; | |
615 t = render_priv->time - render_priv->state.event->Start; // FIXME: move to render_context | |
616 if (t <= t1) | |
617 k = 0.; | |
618 else if (t >= t2) | |
619 k = 1.; | |
620 else { | |
621 assert(delta_t != 0.); | |
622 k = pow(((double) (t - t1)) / delta_t, v3); | |
623 } | |
624 while (*p == '\\') | |
625 p = parse_tag(render_priv, p, k); // maybe k*pwr ? no, specs forbid nested \t's | |
626 skip_to(')'); // in case there is some unknown tag or a comment | |
627 skip(')'); | |
628 } else if (mystrcmp(&p, "clip")) { | |
629 char *start = p; | |
630 int x0, y0, x1, y1; | |
631 int res = 1; | |
632 skipopt('('); | |
633 res &= mystrtoi(&p, &x0); | |
634 skipopt(','); | |
635 res &= mystrtoi(&p, &y0); | |
636 skipopt(','); | |
637 res &= mystrtoi(&p, &x1); | |
638 skipopt(','); | |
639 res &= mystrtoi(&p, &y1); | |
640 skipopt(')'); | |
641 if (res) { | |
642 render_priv->state.clip_x0 = | |
643 render_priv->state.clip_x0 * (1 - pwr) + x0 * pwr; | |
644 render_priv->state.clip_x1 = | |
645 render_priv->state.clip_x1 * (1 - pwr) + x1 * pwr; | |
646 render_priv->state.clip_y0 = | |
647 render_priv->state.clip_y0 * (1 - pwr) + y0 * pwr; | |
648 render_priv->state.clip_y1 = | |
649 render_priv->state.clip_y1 * (1 - pwr) + y1 * pwr; | |
650 // Might be a vector clip | |
651 } else if (!render_priv->state.clip_drawing) { | |
652 p = parse_vector_clip(render_priv, start); | |
653 render_priv->state.clip_drawing_mode = 0; | |
654 } else { | |
655 render_priv->state.clip_x0 = 0; | |
656 render_priv->state.clip_y0 = 0; | |
657 render_priv->state.clip_x1 = render_priv->track->PlayResX; | |
658 render_priv->state.clip_y1 = render_priv->track->PlayResY; | |
659 } | |
660 } else if (mystrcmp(&p, "c")) { | |
661 uint32_t val; | |
662 int hex = render_priv->track->track_type == TRACK_TYPE_ASS; | |
663 if (!strtocolor(render_priv->library, &p, &val, hex)) | |
664 val = render_priv->state.style->PrimaryColour; | |
665 ass_msg(render_priv->library, MSGL_DBG2, "color: %X", val); | |
666 change_color(&render_priv->state.c[0], val, pwr); | |
667 } else if ((*p >= '1') && (*p <= '4') && (++p) | |
668 && (mystrcmp(&p, "c") || mystrcmp(&p, "a"))) { | |
669 char n = *(p - 2); | |
670 int cidx = n - '1'; | |
671 char cmd = *(p - 1); | |
672 uint32_t val; | |
673 int hex = render_priv->track->track_type == TRACK_TYPE_ASS; | |
674 assert((n >= '1') && (n <= '4')); | |
675 if (!strtocolor(render_priv->library, &p, &val, hex)) | |
676 switch (n) { | |
677 case '1': | |
678 val = render_priv->state.style->PrimaryColour; | |
679 break; | |
680 case '2': | |
681 val = render_priv->state.style->SecondaryColour; | |
682 break; | |
683 case '3': | |
684 val = render_priv->state.style->OutlineColour; | |
685 break; | |
686 case '4': | |
687 val = render_priv->state.style->BackColour; | |
688 break; | |
689 default: | |
690 val = 0; | |
691 break; // impossible due to assert; avoid compilation warning | |
692 } | |
693 switch (cmd) { | |
694 case 'c': | |
695 change_color(render_priv->state.c + cidx, val, pwr); | |
696 break; | |
697 case 'a': | |
698 change_alpha(render_priv->state.c + cidx, val >> 24, pwr); | |
699 break; | |
700 default: | |
701 ass_msg(render_priv->library, MSGL_WARN, "Bad command: %c%c", | |
702 n, cmd); | |
703 break; | |
704 } | |
705 ass_msg(render_priv->library, MSGL_DBG2, "single c/a at %f: %c%c = %X", | |
706 pwr, n, cmd, render_priv->state.c[cidx]); | |
707 } else if (mystrcmp(&p, "r")) { | |
708 reset_render_context(render_priv); | |
709 } else if (mystrcmp(&p, "be")) { | |
710 int val; | |
711 if (mystrtoi(&p, &val)) { | |
712 // Clamp to a safe upper limit, since high values need excessive CPU | |
713 val = (val < 0) ? 0 : val; | |
714 val = (val > MAX_BE) ? MAX_BE : val; | |
715 render_priv->state.be = val; | |
716 } else | |
717 render_priv->state.be = 0; | |
718 } else if (mystrcmp(&p, "b")) { | |
719 int b; | |
720 if (mystrtoi(&p, &b)) { | |
721 if (pwr >= .5) | |
722 render_priv->state.bold = b; | |
723 } else | |
724 render_priv->state.bold = render_priv->state.style->Bold; | |
725 update_font(render_priv); | |
726 } else if (mystrcmp(&p, "i")) { | |
727 int i; | |
728 if (mystrtoi(&p, &i)) { | |
729 if (pwr >= .5) | |
730 render_priv->state.italic = i; | |
731 } else | |
732 render_priv->state.italic = render_priv->state.style->Italic; | |
733 update_font(render_priv); | |
734 } else if (mystrcmp(&p, "kf") || mystrcmp(&p, "K")) { | |
735 int val = 0; | |
736 mystrtoi(&p, &val); | |
737 render_priv->state.effect_type = EF_KARAOKE_KF; | |
738 if (render_priv->state.effect_timing) | |
739 render_priv->state.effect_skip_timing += | |
740 render_priv->state.effect_timing; | |
741 render_priv->state.effect_timing = val * 10; | |
742 } else if (mystrcmp(&p, "ko")) { | |
743 int val = 0; | |
744 mystrtoi(&p, &val); | |
745 render_priv->state.effect_type = EF_KARAOKE_KO; | |
746 if (render_priv->state.effect_timing) | |
747 render_priv->state.effect_skip_timing += | |
748 render_priv->state.effect_timing; | |
749 render_priv->state.effect_timing = val * 10; | |
750 } else if (mystrcmp(&p, "k")) { | |
751 int val = 0; | |
752 mystrtoi(&p, &val); | |
753 render_priv->state.effect_type = EF_KARAOKE; | |
754 if (render_priv->state.effect_timing) | |
755 render_priv->state.effect_skip_timing += | |
756 render_priv->state.effect_timing; | |
757 render_priv->state.effect_timing = val * 10; | |
758 } else if (mystrcmp(&p, "shad")) { | |
759 double val; | |
760 if (mystrtod(&p, &val)) { | |
761 if (render_priv->state.shadow_x == render_priv->state.shadow_y) | |
762 val = render_priv->state.shadow_x * (1 - pwr) + val * pwr; | |
763 } else | |
764 val = 0.; | |
765 render_priv->state.shadow_x = render_priv->state.shadow_y = val; | |
766 } else if (mystrcmp(&p, "s")) { | |
767 int val; | |
768 if (mystrtoi(&p, &val) && val) | |
769 render_priv->state.flags |= DECO_STRIKETHROUGH; | |
770 else | |
771 render_priv->state.flags &= ~DECO_STRIKETHROUGH; | |
772 } else if (mystrcmp(&p, "u")) { | |
773 int val; | |
774 if (mystrtoi(&p, &val) && val) | |
775 render_priv->state.flags |= DECO_UNDERLINE; | |
776 else | |
777 render_priv->state.flags &= ~DECO_UNDERLINE; | |
778 } else if (mystrcmp(&p, "pbo")) { | |
779 double val = 0; | |
780 if (mystrtod(&p, &val)) | |
781 render_priv->state.drawing->pbo = val; | |
782 } else if (mystrcmp(&p, "p")) { | |
783 int val; | |
784 if (!mystrtoi(&p, &val)) | |
785 val = 0; | |
786 if (val) | |
787 render_priv->state.drawing->scale = val; | |
788 render_priv->state.drawing_mode = !!val; | |
789 } else if (mystrcmp(&p, "q")) { | |
790 int val; | |
791 if (!mystrtoi(&p, &val)) | |
792 val = render_priv->track->WrapStyle; | |
793 render_priv->state.wrap_style = val; | |
794 } | |
795 | |
796 return p; | |
797 } | |
798 | |
799 void apply_transition_effects(ASS_Renderer *render_priv, ASS_Event *event) | |
800 { | |
801 int v[4]; | |
802 int cnt; | |
803 char *p = event->Effect; | |
804 | |
805 if (!p || !*p) | |
806 return; | |
807 | |
808 cnt = 0; | |
809 while (cnt < 4 && (p = strchr(p, ';'))) { | |
810 v[cnt++] = atoi(++p); | |
811 } | |
812 | |
813 if (strncmp(event->Effect, "Banner;", 7) == 0) { | |
814 int delay; | |
815 if (cnt < 1) { | |
816 ass_msg(render_priv->library, MSGL_V, | |
817 "Error parsing effect: '%s'", event->Effect); | |
818 return; | |
819 } | |
820 if (cnt >= 2 && v[1] == 0) // right-to-left | |
821 render_priv->state.scroll_direction = SCROLL_RL; | |
822 else // left-to-right | |
823 render_priv->state.scroll_direction = SCROLL_LR; | |
824 | |
825 delay = v[0]; | |
826 if (delay == 0) | |
827 delay = 1; // ? | |
828 render_priv->state.scroll_shift = | |
829 (render_priv->time - render_priv->state.event->Start) / delay; | |
830 render_priv->state.evt_type = EVENT_HSCROLL; | |
831 return; | |
832 } | |
833 | |
834 if (strncmp(event->Effect, "Scroll up;", 10) == 0) { | |
835 render_priv->state.scroll_direction = SCROLL_BT; | |
836 } else if (strncmp(event->Effect, "Scroll down;", 12) == 0) { | |
837 render_priv->state.scroll_direction = SCROLL_TB; | |
838 } else { | |
839 ass_msg(render_priv->library, MSGL_V, | |
840 "Unknown transition effect: '%s'", event->Effect); | |
841 return; | |
842 } | |
843 // parse scroll up/down parameters | |
844 { | |
845 int delay; | |
846 int y0, y1; | |
847 if (cnt < 3) { | |
848 ass_msg(render_priv->library, MSGL_V, | |
849 "Error parsing effect: '%s'", event->Effect); | |
850 return; | |
851 } | |
852 delay = v[2]; | |
853 if (delay == 0) | |
854 delay = 1; // ? | |
855 render_priv->state.scroll_shift = | |
856 (render_priv->time - render_priv->state.event->Start) / delay; | |
857 if (v[0] < v[1]) { | |
858 y0 = v[0]; | |
859 y1 = v[1]; | |
860 } else { | |
861 y0 = v[1]; | |
862 y1 = v[0]; | |
863 } | |
864 if (y1 == 0) | |
865 y1 = render_priv->track->PlayResY; // y0=y1=0 means fullscreen scrolling | |
866 render_priv->state.clip_y0 = y0; | |
867 render_priv->state.clip_y1 = y1; | |
868 render_priv->state.evt_type = EVENT_VSCROLL; | |
869 render_priv->state.detect_collisions = 0; | |
870 } | |
871 | |
872 } | |
873 | |
874 /** | |
875 * \brief Get next ucs4 char from string, parsing and executing style overrides | |
876 * \param str string pointer | |
877 * \return ucs4 code of the next char | |
878 * On return str points to the unparsed part of the string | |
879 */ | |
880 unsigned get_next_char(ASS_Renderer *render_priv, char **str) | |
881 { | |
882 char *p = *str; | |
883 unsigned chr; | |
884 if (*p == '{') { // '\0' goes here | |
885 p++; | |
886 while (1) { | |
887 p = parse_tag(render_priv, p, 1.); | |
888 if (*p == '}') { // end of tag | |
889 p++; | |
890 if (*p == '{') { | |
891 p++; | |
892 continue; | |
893 } else | |
894 break; | |
895 } else if (*p != '\\') | |
896 ass_msg(render_priv->library, MSGL_V, | |
897 "Unable to parse: '%s'", p); | |
898 if (*p == 0) | |
899 break; | |
900 } | |
901 } | |
902 if (*p == '\t') { | |
903 ++p; | |
904 *str = p; | |
905 return ' '; | |
906 } | |
907 if (*p == '\\') { | |
908 if ((p[1] == 'N') || ((p[1] == 'n') && | |
909 (render_priv->state.wrap_style == 2))) { | |
910 p += 2; | |
911 *str = p; | |
912 return '\n'; | |
913 } else if (p[1] == 'n') { | |
914 p += 2; | |
915 *str = p; | |
916 return ' '; | |
917 } else if (p[1] == 'h') { | |
918 p += 2; | |
919 *str = p; | |
920 return NBSP; | |
921 } | |
922 } | |
923 chr = ass_utf8_get_char((char **) &p); | |
924 *str = p; | |
925 return chr; | |
926 } |