20008
|
1 /*
|
26723
|
2 * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
|
|
3 *
|
26738
|
4 * This file is part of libass.
|
26723
|
5 *
|
26738
|
6 * libass is free software; you can redistribute it and/or modify
|
26723
|
7 * it under the terms of the GNU General Public License as published by
|
|
8 * the Free Software Foundation; either version 2 of the License, or
|
|
9 * (at your option) any later version.
|
|
10 *
|
26738
|
11 * libass is distributed in the hope that it will be useful,
|
26723
|
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14 * GNU General Public License for more details.
|
|
15 *
|
|
16 * You should have received a copy of the GNU General Public License along
|
26738
|
17 * with libass; if not, write to the Free Software Foundation, Inc.,
|
26723
|
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
19 */
|
20008
|
20
|
18937
|
21 #include "config.h"
|
|
22
|
|
23 #include <assert.h>
|
|
24 #include <math.h>
|
|
25
|
30200
|
26 #include "ass_render.h"
|
|
27 #include "ass_parse.h"
|
22258
|
28
|
30200
|
29 #define MAX_GLYPHS_INITIAL 1024
|
|
30 #define MAX_LINES_INITIAL 64
|
|
31 #define SUBPIXEL_MASK 63
|
31853
|
32 #define SUBPIXEL_ACCURACY 7
|
18937
|
33
|
30200
|
34 static void ass_lazy_track_init(ASS_Renderer *render_priv)
|
|
35 {
|
|
36 ASS_Track *track = render_priv->track;
|
18937
|
37
|
30200
|
38 if (track->PlayResX && track->PlayResY)
|
|
39 return;
|
|
40 if (!track->PlayResX && !track->PlayResY) {
|
|
41 ass_msg(render_priv->library, MSGL_WARN,
|
|
42 "Neither PlayResX nor PlayResY defined. Assuming 384x288");
|
|
43 track->PlayResX = 384;
|
|
44 track->PlayResY = 288;
|
|
45 } else {
|
|
46 if (!track->PlayResY && track->PlayResX == 1280) {
|
|
47 track->PlayResY = 1024;
|
|
48 ass_msg(render_priv->library, MSGL_WARN,
|
|
49 "PlayResY undefined, setting to %d", track->PlayResY);
|
|
50 } else if (!track->PlayResY) {
|
|
51 track->PlayResY = track->PlayResX * 3 / 4;
|
|
52 ass_msg(render_priv->library, MSGL_WARN,
|
|
53 "PlayResY undefined, setting to %d", track->PlayResY);
|
|
54 } else if (!track->PlayResX && track->PlayResY == 1024) {
|
|
55 track->PlayResX = 1280;
|
|
56 ass_msg(render_priv->library, MSGL_WARN,
|
|
57 "PlayResX undefined, setting to %d", track->PlayResX);
|
|
58 } else if (!track->PlayResX) {
|
|
59 track->PlayResX = track->PlayResY * 4 / 3;
|
|
60 ass_msg(render_priv->library, MSGL_WARN,
|
|
61 "PlayResX undefined, setting to %d", track->PlayResX);
|
|
62 }
|
|
63 }
|
18937
|
64 }
|
|
65
|
30200
|
66 ASS_Renderer *ass_renderer_init(ASS_Library *library)
|
18937
|
67 {
|
30200
|
68 int error;
|
|
69 FT_Library ft;
|
|
70 ASS_Renderer *priv = 0;
|
|
71 int vmajor, vminor, vpatch;
|
20202
|
72
|
30200
|
73 error = FT_Init_FreeType(&ft);
|
|
74 if (error) {
|
|
75 ass_msg(library, MSGL_FATAL, "%s failed", "FT_Init_FreeType");
|
|
76 goto ass_init_exit;
|
|
77 }
|
18937
|
78
|
30200
|
79 FT_Library_Version(ft, &vmajor, &vminor, &vpatch);
|
|
80 ass_msg(library, MSGL_V, "FreeType library version: %d.%d.%d",
|
|
81 vmajor, vminor, vpatch);
|
|
82 ass_msg(library, MSGL_V, "FreeType headers version: %d.%d.%d",
|
|
83 FREETYPE_MAJOR, FREETYPE_MINOR, FREETYPE_PATCH);
|
|
84
|
|
85 priv = calloc(1, sizeof(ASS_Renderer));
|
|
86 if (!priv) {
|
|
87 FT_Done_FreeType(ft);
|
|
88 goto ass_init_exit;
|
|
89 }
|
26032
|
90
|
30200
|
91 priv->synth_priv = ass_synth_init(BLUR_MAX_RADIUS);
|
|
92
|
|
93 priv->library = library;
|
|
94 priv->ftlibrary = ft;
|
|
95 // images_root and related stuff is zero-filled in calloc
|
19846
|
96
|
30200
|
97 priv->cache.font_cache = ass_font_cache_init(library);
|
|
98 priv->cache.bitmap_cache = ass_bitmap_cache_init(library);
|
|
99 priv->cache.composite_cache = ass_composite_cache_init(library);
|
|
100 priv->cache.glyph_cache = ass_glyph_cache_init(library);
|
|
101 priv->cache.glyph_max = GLYPH_CACHE_MAX;
|
|
102 priv->cache.bitmap_max_size = BITMAP_CACHE_MAX_SIZE;
|
29263
|
103
|
30200
|
104 priv->text_info.max_glyphs = MAX_GLYPHS_INITIAL;
|
|
105 priv->text_info.max_lines = MAX_LINES_INITIAL;
|
31853
|
106 priv->text_info.glyphs = calloc(MAX_GLYPHS_INITIAL, sizeof(GlyphInfo));
|
30200
|
107 priv->text_info.lines = calloc(MAX_LINES_INITIAL, sizeof(LineInfo));
|
18937
|
108
|
31853
|
109 priv->settings.font_size_coeff = 1.;
|
|
110
|
30200
|
111 ass_init_exit:
|
|
112 if (priv)
|
31853
|
113 ass_msg(library, MSGL_V, "Init");
|
30200
|
114 else
|
|
115 ass_msg(library, MSGL_ERR, "Init failed");
|
29263
|
116
|
30200
|
117 return priv;
|
18937
|
118 }
|
|
119
|
30200
|
120 static void free_list_clear(ASS_Renderer *render_priv)
|
|
121 {
|
|
122 if (render_priv->free_head) {
|
|
123 FreeList *item = render_priv->free_head;
|
|
124 while(item) {
|
|
125 FreeList *oi = item;
|
|
126 free(item->object);
|
|
127 item = item->next;
|
|
128 free(oi);
|
|
129 }
|
|
130 render_priv->free_head = NULL;
|
|
131 }
|
|
132 }
|
|
133
|
|
134 void ass_renderer_done(ASS_Renderer *render_priv)
|
18937
|
135 {
|
30200
|
136 ass_font_cache_done(render_priv->cache.font_cache);
|
|
137 ass_bitmap_cache_done(render_priv->cache.bitmap_cache);
|
|
138 ass_composite_cache_done(render_priv->cache.composite_cache);
|
|
139 ass_glyph_cache_done(render_priv->cache.glyph_cache);
|
|
140
|
|
141 ass_free_images(render_priv->images_root);
|
|
142 ass_free_images(render_priv->prev_images_root);
|
|
143
|
|
144 if (render_priv->state.stroker) {
|
|
145 FT_Stroker_Done(render_priv->state.stroker);
|
|
146 render_priv->state.stroker = 0;
|
|
147 }
|
31875
|
148 if (render_priv->ftlibrary)
|
30200
|
149 FT_Done_FreeType(render_priv->ftlibrary);
|
31875
|
150 if (render_priv->fontconfig_priv)
|
30200
|
151 fontconfig_done(render_priv->fontconfig_priv);
|
31875
|
152 if (render_priv->synth_priv)
|
30200
|
153 ass_synth_done(render_priv->synth_priv);
|
31875
|
154 free(render_priv->eimg);
|
30200
|
155 free(render_priv->text_info.glyphs);
|
|
156 free(render_priv->text_info.lines);
|
|
157
|
|
158 free(render_priv->settings.default_font);
|
|
159 free(render_priv->settings.default_family);
|
|
160
|
|
161 free_list_clear(render_priv);
|
|
162 free(render_priv);
|
18937
|
163 }
|
|
164
|
|
165 /**
|
30200
|
166 * \brief Create a new ASS_Image
|
|
167 * Parameters are the same as ASS_Image fields.
|
18937
|
168 */
|
30200
|
169 static ASS_Image *my_draw_bitmap(unsigned char *bitmap, int bitmap_w,
|
|
170 int bitmap_h, int stride, int dst_x,
|
|
171 int dst_y, uint32_t color)
|
18937
|
172 {
|
31853
|
173 ASS_Image *img = malloc(sizeof(ASS_Image));
|
30200
|
174
|
31853
|
175 if (img) {
|
|
176 img->w = bitmap_w;
|
|
177 img->h = bitmap_h;
|
|
178 img->stride = stride;
|
|
179 img->bitmap = bitmap;
|
|
180 img->color = color;
|
|
181 img->dst_x = dst_x;
|
|
182 img->dst_y = dst_y;
|
|
183 }
|
30200
|
184
|
|
185 return img;
|
|
186 }
|
|
187
|
31853
|
188 /**
|
|
189 * \brief Mapping between script and screen coordinates
|
|
190 */
|
|
191 static double x2scr(ASS_Renderer *render_priv, double x)
|
|
192 {
|
|
193 return x * render_priv->orig_width_nocrop / render_priv->font_scale_x /
|
|
194 render_priv->track->PlayResX +
|
|
195 FFMAX(render_priv->settings.left_margin, 0);
|
|
196 }
|
|
197 static double x2scr_pos(ASS_Renderer *render_priv, double x)
|
|
198 {
|
|
199 return x * render_priv->orig_width / render_priv->font_scale_x / render_priv->track->PlayResX +
|
|
200 render_priv->settings.left_margin;
|
|
201 }
|
|
202 static double x2scr_scaled(ASS_Renderer *render_priv, double x)
|
|
203 {
|
|
204 return x * render_priv->orig_width_nocrop /
|
|
205 render_priv->track->PlayResX +
|
|
206 FFMAX(render_priv->settings.left_margin, 0);
|
|
207 }
|
|
208 static double x2scr_pos_scaled(ASS_Renderer *render_priv, double x)
|
|
209 {
|
|
210 return x * render_priv->orig_width / render_priv->track->PlayResX +
|
|
211 render_priv->settings.left_margin;
|
|
212 }
|
|
213 /**
|
|
214 * \brief Mapping between script and screen coordinates
|
|
215 */
|
|
216 static double y2scr(ASS_Renderer *render_priv, double y)
|
|
217 {
|
|
218 return y * render_priv->orig_height_nocrop /
|
|
219 render_priv->track->PlayResY +
|
|
220 FFMAX(render_priv->settings.top_margin, 0);
|
|
221 }
|
|
222 static double y2scr_pos(ASS_Renderer *render_priv, double y)
|
|
223 {
|
|
224 return y * render_priv->orig_height / render_priv->track->PlayResY +
|
|
225 render_priv->settings.top_margin;
|
|
226 }
|
|
227
|
|
228 // the same for toptitles
|
|
229 static double y2scr_top(ASS_Renderer *render_priv, double y)
|
|
230 {
|
|
231 if (render_priv->settings.use_margins)
|
|
232 return y * render_priv->orig_height_nocrop /
|
|
233 render_priv->track->PlayResY;
|
|
234 else
|
|
235 return y * render_priv->orig_height_nocrop /
|
|
236 render_priv->track->PlayResY +
|
|
237 FFMAX(render_priv->settings.top_margin, 0);
|
|
238 }
|
|
239 // the same for subtitles
|
|
240 static double y2scr_sub(ASS_Renderer *render_priv, double y)
|
|
241 {
|
|
242 if (render_priv->settings.use_margins)
|
|
243 return y * render_priv->orig_height_nocrop /
|
|
244 render_priv->track->PlayResY +
|
|
245 FFMAX(render_priv->settings.top_margin, 0)
|
|
246 + FFMAX(render_priv->settings.bottom_margin, 0);
|
|
247 else
|
|
248 return y * render_priv->orig_height_nocrop /
|
|
249 render_priv->track->PlayResY +
|
|
250 FFMAX(render_priv->settings.top_margin, 0);
|
|
251 }
|
30200
|
252
|
|
253 /*
|
|
254 * \brief Convert bitmap glyphs into ASS_Image list with inverse clipping
|
|
255 *
|
|
256 * Inverse clipping with the following strategy:
|
|
257 * - find rectangle from (x0, y0) to (cx0, y1)
|
|
258 * - find rectangle from (cx0, y0) to (cx1, cy0)
|
|
259 * - find rectangle from (cx0, cy1) to (cx1, y1)
|
|
260 * - find rectangle from (cx1, y0) to (x1, y1)
|
|
261 * These rectangles can be invalid and in this case are discarded.
|
|
262 * Afterwards, they are clipped against the screen coordinates.
|
|
263 * In an additional pass, the rectangles need to be split up left/right for
|
|
264 * karaoke effects. This can result in a lot of bitmaps (6 to be exact).
|
|
265 */
|
|
266 static ASS_Image **render_glyph_i(ASS_Renderer *render_priv,
|
|
267 Bitmap *bm, int dst_x, int dst_y,
|
|
268 uint32_t color, uint32_t color2, int brk,
|
|
269 ASS_Image **tail)
|
|
270 {
|
|
271 int i, j, x0, y0, x1, y1, cx0, cy0, cx1, cy1, sx, sy, zx, zy;
|
|
272 Rect r[4];
|
|
273 ASS_Image *img;
|
|
274
|
|
275 dst_x += bm->left;
|
|
276 dst_y += bm->top;
|
|
277
|
|
278 // we still need to clip against screen boundaries
|
31853
|
279 zx = x2scr_pos_scaled(render_priv, 0);
|
30200
|
280 zy = y2scr_pos(render_priv, 0);
|
31853
|
281 sx = x2scr_pos_scaled(render_priv, render_priv->track->PlayResX);
|
30200
|
282 sy = y2scr_pos(render_priv, render_priv->track->PlayResY);
|
29263
|
283
|
30200
|
284 x0 = 0;
|
|
285 y0 = 0;
|
|
286 x1 = bm->w;
|
|
287 y1 = bm->h;
|
|
288 cx0 = render_priv->state.clip_x0 - dst_x;
|
|
289 cy0 = render_priv->state.clip_y0 - dst_y;
|
|
290 cx1 = render_priv->state.clip_x1 - dst_x;
|
|
291 cy1 = render_priv->state.clip_y1 - dst_y;
|
|
292
|
|
293 // calculate rectangles and discard invalid ones while we're at it.
|
|
294 i = 0;
|
|
295 r[i].x0 = x0;
|
|
296 r[i].y0 = y0;
|
|
297 r[i].x1 = (cx0 > x1) ? x1 : cx0;
|
|
298 r[i].y1 = y1;
|
|
299 if (r[i].x1 > r[i].x0 && r[i].y1 > r[i].y0) i++;
|
|
300 r[i].x0 = (cx0 < 0) ? x0 : cx0;
|
|
301 r[i].y0 = y0;
|
|
302 r[i].x1 = (cx1 > x1) ? x1 : cx1;
|
|
303 r[i].y1 = (cy0 > y1) ? y1 : cy0;
|
|
304 if (r[i].x1 > r[i].x0 && r[i].y1 > r[i].y0) i++;
|
|
305 r[i].x0 = (cx0 < 0) ? x0 : cx0;
|
|
306 r[i].y0 = (cy1 < 0) ? y0 : cy1;
|
|
307 r[i].x1 = (cx1 > x1) ? x1 : cx1;
|
|
308 r[i].y1 = y1;
|
|
309 if (r[i].x1 > r[i].x0 && r[i].y1 > r[i].y0) i++;
|
|
310 r[i].x0 = (cx1 < 0) ? x0 : cx1;
|
|
311 r[i].y0 = y0;
|
|
312 r[i].x1 = x1;
|
|
313 r[i].y1 = y1;
|
|
314 if (r[i].x1 > r[i].x0 && r[i].y1 > r[i].y0) i++;
|
29383
|
315
|
30200
|
316 // clip each rectangle to screen coordinates
|
|
317 for (j = 0; j < i; j++) {
|
|
318 r[j].x0 = (r[j].x0 + dst_x < zx) ? zx - dst_x : r[j].x0;
|
|
319 r[j].y0 = (r[j].y0 + dst_y < zy) ? zy - dst_y : r[j].y0;
|
|
320 r[j].x1 = (r[j].x1 + dst_x > sx) ? sx - dst_x : r[j].x1;
|
|
321 r[j].y1 = (r[j].y1 + dst_y > sy) ? sy - dst_y : r[j].y1;
|
|
322 }
|
19638
|
323
|
30200
|
324 // draw the rectangles
|
|
325 for (j = 0; j < i; j++) {
|
|
326 int lbrk = brk;
|
|
327 // kick out rectangles that are invalid now
|
|
328 if (r[j].x1 <= r[j].x0 || r[j].y1 <= r[j].y0)
|
|
329 continue;
|
|
330 // split up into left and right for karaoke, if needed
|
|
331 if (lbrk > r[j].x0) {
|
|
332 if (lbrk > r[j].x1) lbrk = r[j].x1;
|
|
333 img = my_draw_bitmap(bm->buffer + r[j].y0 * bm->w + r[j].x0,
|
|
334 lbrk - r[j].x0, r[j].y1 - r[j].y0,
|
|
335 bm->w, dst_x + r[j].x0, dst_y + r[j].y0, color);
|
31853
|
336 if (!img) break;
|
30200
|
337 *tail = img;
|
|
338 tail = &img->next;
|
|
339 }
|
|
340 if (lbrk < r[j].x1) {
|
|
341 if (lbrk < r[j].x0) lbrk = r[j].x0;
|
|
342 img = my_draw_bitmap(bm->buffer + r[j].y0 * bm->w + lbrk,
|
|
343 r[j].x1 - lbrk, r[j].y1 - r[j].y0,
|
|
344 bm->w, dst_x + lbrk, dst_y + r[j].y0, color2);
|
31853
|
345 if (!img) break;
|
30200
|
346 *tail = img;
|
|
347 tail = &img->next;
|
|
348 }
|
|
349 }
|
|
350
|
|
351 return tail;
|
18937
|
352 }
|
|
353
|
|
354 /**
|
30200
|
355 * \brief convert bitmap glyph into ASS_Image struct(s)
|
18937
|
356 * \param bit freetype bitmap glyph, FT_PIXEL_MODE_GRAY
|
|
357 * \param dst_x bitmap x coordinate in video frame
|
|
358 * \param dst_y bitmap y coordinate in video frame
|
|
359 * \param color first color, RGBA
|
|
360 * \param color2 second color, RGBA
|
|
361 * \param brk x coordinate relative to glyph origin, color is used to the left of brk, color2 - to the right
|
19638
|
362 * \param tail pointer to the last image's next field, head of the generated list should be stored here
|
|
363 * \return pointer to the new list tail
|
18937
|
364 * Performs clipping. Uses my_draw_bitmap for actual bitmap convertion.
|
|
365 */
|
30200
|
366 static ASS_Image **
|
|
367 render_glyph(ASS_Renderer *render_priv, Bitmap *bm, int dst_x, int dst_y,
|
|
368 uint32_t color, uint32_t color2, int brk, ASS_Image **tail)
|
18937
|
369 {
|
30200
|
370 // Inverse clipping in use?
|
|
371 if (render_priv->state.clip_mode)
|
|
372 return render_glyph_i(render_priv, bm, dst_x, dst_y, color, color2,
|
|
373 brk, tail);
|
18937
|
374
|
30200
|
375 // brk is relative to dst_x
|
|
376 // color = color left of brk
|
|
377 // color2 = color right of brk
|
|
378 int b_x0, b_y0, b_x1, b_y1; // visible part of the bitmap
|
|
379 int clip_x0, clip_y0, clip_x1, clip_y1;
|
|
380 int tmp;
|
|
381 ASS_Image *img;
|
29382
|
382
|
30200
|
383 dst_x += bm->left;
|
|
384 dst_y += bm->top;
|
|
385 brk -= bm->left;
|
29263
|
386
|
30200
|
387 // clipping
|
|
388 clip_x0 = FFMINMAX(render_priv->state.clip_x0, 0, render_priv->width);
|
|
389 clip_y0 = FFMINMAX(render_priv->state.clip_y0, 0, render_priv->height);
|
|
390 clip_x1 = FFMINMAX(render_priv->state.clip_x1, 0, render_priv->width);
|
|
391 clip_y1 = FFMINMAX(render_priv->state.clip_y1, 0, render_priv->height);
|
|
392 b_x0 = 0;
|
|
393 b_y0 = 0;
|
|
394 b_x1 = bm->w;
|
|
395 b_y1 = bm->h;
|
29263
|
396
|
30200
|
397 tmp = dst_x - clip_x0;
|
|
398 if (tmp < 0) {
|
|
399 ass_msg(render_priv->library, MSGL_DBG2, "clip left");
|
|
400 b_x0 = -tmp;
|
|
401 }
|
|
402 tmp = dst_y - clip_y0;
|
|
403 if (tmp < 0) {
|
|
404 ass_msg(render_priv->library, MSGL_DBG2, "clip top");
|
|
405 b_y0 = -tmp;
|
|
406 }
|
|
407 tmp = clip_x1 - dst_x - bm->w;
|
|
408 if (tmp < 0) {
|
|
409 ass_msg(render_priv->library, MSGL_DBG2, "clip right");
|
|
410 b_x1 = bm->w + tmp;
|
|
411 }
|
|
412 tmp = clip_y1 - dst_y - bm->h;
|
|
413 if (tmp < 0) {
|
|
414 ass_msg(render_priv->library, MSGL_DBG2, "clip bottom");
|
|
415 b_y1 = bm->h + tmp;
|
|
416 }
|
29263
|
417
|
30200
|
418 if ((b_y0 >= b_y1) || (b_x0 >= b_x1))
|
|
419 return tail;
|
18937
|
420
|
30200
|
421 if (brk > b_x0) { // draw left part
|
|
422 if (brk > b_x1)
|
|
423 brk = b_x1;
|
|
424 img = my_draw_bitmap(bm->buffer + bm->w * b_y0 + b_x0,
|
|
425 brk - b_x0, b_y1 - b_y0, bm->w,
|
|
426 dst_x + b_x0, dst_y + b_y0, color);
|
31853
|
427 if (!img) return tail;
|
30200
|
428 *tail = img;
|
|
429 tail = &img->next;
|
|
430 }
|
|
431 if (brk < b_x1) { // draw right part
|
|
432 if (brk < b_x0)
|
|
433 brk = b_x0;
|
|
434 img = my_draw_bitmap(bm->buffer + bm->w * b_y0 + brk,
|
|
435 b_x1 - brk, b_y1 - b_y0, bm->w,
|
|
436 dst_x + brk, dst_y + b_y0, color2);
|
31853
|
437 if (!img) return tail;
|
30200
|
438 *tail = img;
|
|
439 tail = &img->next;
|
|
440 }
|
|
441 return tail;
|
18937
|
442 }
|
|
443
|
|
444 /**
|
30200
|
445 * \brief Replace the bitmap buffer in ASS_Image with a copy
|
|
446 * \param img ASS_Image to operate on
|
|
447 * \return pointer to old bitmap buffer
|
29381
|
448 */
|
30200
|
449 static unsigned char *clone_bitmap_buffer(ASS_Image *img)
|
29381
|
450 {
|
30200
|
451 unsigned char *old_bitmap = img->bitmap;
|
|
452 int size = img->stride * (img->h - 1) + img->w;
|
|
453 img->bitmap = malloc(size);
|
|
454 memcpy(img->bitmap, old_bitmap, size);
|
|
455 return old_bitmap;
|
29381
|
456 }
|
|
457
|
|
458 /**
|
28789
|
459 * \brief Calculate overlapping area of two consecutive bitmaps and in case they
|
30200
|
460 * overlap, blend them together
|
28789
|
461 * Mainly useful for translucent glyphs and especially borders, to avoid the
|
|
462 * luminance adding up where they overlap (which looks ugly)
|
|
463 */
|
30200
|
464 static void
|
|
465 render_overlap(ASS_Renderer *render_priv, ASS_Image **last_tail,
|
|
466 ASS_Image **tail)
|
|
467 {
|
|
468 int left, top, bottom, right;
|
|
469 int old_left, old_top, w, h, cur_left, cur_top;
|
|
470 int x, y, opos, cpos;
|
|
471 char m;
|
|
472 CompositeHashKey hk;
|
|
473 CompositeHashValue *hv;
|
|
474 CompositeHashValue chv;
|
|
475 int ax = (*last_tail)->dst_x;
|
|
476 int ay = (*last_tail)->dst_y;
|
|
477 int aw = (*last_tail)->w;
|
|
478 int as = (*last_tail)->stride;
|
|
479 int ah = (*last_tail)->h;
|
|
480 int bx = (*tail)->dst_x;
|
|
481 int by = (*tail)->dst_y;
|
|
482 int bw = (*tail)->w;
|
|
483 int bs = (*tail)->stride;
|
|
484 int bh = (*tail)->h;
|
|
485 unsigned char *a;
|
|
486 unsigned char *b;
|
28789
|
487
|
30200
|
488 if ((*last_tail)->bitmap == (*tail)->bitmap)
|
|
489 return;
|
28789
|
490
|
30200
|
491 if ((*last_tail)->color != (*tail)->color)
|
|
492 return;
|
28840
|
493
|
30200
|
494 // Calculate overlap coordinates
|
|
495 left = (ax > bx) ? ax : bx;
|
|
496 top = (ay > by) ? ay : by;
|
|
497 right = ((ax + aw) < (bx + bw)) ? (ax + aw) : (bx + bw);
|
|
498 bottom = ((ay + ah) < (by + bh)) ? (ay + ah) : (by + bh);
|
|
499 if ((right <= left) || (bottom <= top))
|
|
500 return;
|
|
501 old_left = left - ax;
|
|
502 old_top = top - ay;
|
|
503 w = right - left;
|
|
504 h = bottom - top;
|
|
505 cur_left = left - bx;
|
|
506 cur_top = top - by;
|
28789
|
507
|
30200
|
508 // Query cache
|
|
509 hk.a = (*last_tail)->bitmap;
|
|
510 hk.b = (*tail)->bitmap;
|
|
511 hk.aw = aw;
|
|
512 hk.ah = ah;
|
|
513 hk.bw = bw;
|
|
514 hk.bh = bh;
|
|
515 hk.ax = ax;
|
|
516 hk.ay = ay;
|
|
517 hk.bx = bx;
|
|
518 hk.by = by;
|
|
519 hk.as = as;
|
|
520 hk.bs = bs;
|
|
521 hv = cache_find_composite(render_priv->cache.composite_cache, &hk);
|
|
522 if (hv) {
|
|
523 (*last_tail)->bitmap = hv->a;
|
|
524 (*tail)->bitmap = hv->b;
|
|
525 return;
|
|
526 }
|
|
527 // Allocate new bitmaps and copy over data
|
|
528 a = clone_bitmap_buffer(*last_tail);
|
|
529 b = clone_bitmap_buffer(*tail);
|
28789
|
530
|
30200
|
531 // Blend overlapping area
|
|
532 for (y = 0; y < h; y++)
|
|
533 for (x = 0; x < w; x++) {
|
|
534 opos = (old_top + y) * (as) + (old_left + x);
|
|
535 cpos = (cur_top + y) * (bs) + (cur_left + x);
|
|
536 m = FFMIN(a[opos] + b[cpos], 0xff);
|
|
537 (*last_tail)->bitmap[opos] = 0;
|
|
538 (*tail)->bitmap[cpos] = m;
|
|
539 }
|
28789
|
540
|
30200
|
541 // Insert bitmaps into the cache
|
|
542 chv.a = (*last_tail)->bitmap;
|
|
543 chv.b = (*tail)->bitmap;
|
|
544 cache_add_composite(render_priv->cache.composite_cache, &hk, &chv);
|
|
545 }
|
28789
|
546
|
30200
|
547 static void free_list_add(ASS_Renderer *render_priv, void *object)
|
|
548 {
|
|
549 if (!render_priv->free_head) {
|
|
550 render_priv->free_head = calloc(1, sizeof(FreeList));
|
|
551 render_priv->free_head->object = object;
|
|
552 render_priv->free_tail = render_priv->free_head;
|
|
553 } else {
|
|
554 FreeList *l = calloc(1, sizeof(FreeList));
|
|
555 l->object = object;
|
|
556 render_priv->free_tail->next = l;
|
|
557 render_priv->free_tail = render_priv->free_tail->next;
|
|
558 }
|
28789
|
559 }
|
|
560
|
|
561 /**
|
30200
|
562 * Iterate through a list of bitmaps and blend with clip vector, if
|
|
563 * applicable. The blended bitmaps are added to a free list which is freed
|
|
564 * at the start of a new frame.
|
18937
|
565 */
|
30200
|
566 static void blend_vector_clip(ASS_Renderer *render_priv,
|
|
567 ASS_Image *head)
|
18937
|
568 {
|
30200
|
569 FT_Glyph glyph;
|
|
570 FT_BitmapGlyph clip_bm;
|
|
571 ASS_Image *cur;
|
|
572 ASS_Drawing *drawing = render_priv->state.clip_drawing;
|
31853
|
573 GlyphHashKey key;
|
|
574 GlyphHashValue *val;
|
30200
|
575 int error;
|
|
576
|
|
577 if (!drawing)
|
|
578 return;
|
18937
|
579
|
31853
|
580 // Try to get mask from cache
|
|
581 ass_drawing_hash(drawing);
|
|
582 memset(&key, 0, sizeof(key));
|
|
583 key.ch = -2;
|
|
584 key.drawing_hash = drawing->hash;
|
|
585 val = cache_find_glyph(render_priv->cache.glyph_cache, &key);
|
|
586
|
|
587 if (val) {
|
|
588 clip_bm = (FT_BitmapGlyph) val->glyph;
|
|
589 } else {
|
|
590 GlyphHashValue v;
|
|
591
|
|
592 // Not found in cache, parse and rasterize it
|
|
593 glyph = (FT_Glyph) *ass_drawing_parse(drawing, 1);
|
|
594 if (!glyph) {
|
|
595 ass_msg(render_priv->library, MSGL_WARN,
|
|
596 "Clip vector parsing failed. Skipping.");
|
|
597 goto blend_vector_error;
|
|
598 }
|
|
599
|
|
600 // We need to translate the clip according to screen borders
|
|
601 if (render_priv->settings.left_margin != 0 ||
|
|
602 render_priv->settings.top_margin != 0) {
|
|
603 FT_Vector trans = {
|
|
604 .x = int_to_d6(render_priv->settings.left_margin),
|
|
605 .y = -int_to_d6(render_priv->settings.top_margin),
|
|
606 };
|
|
607 FT_Outline_Translate(&drawing->glyph->outline,
|
|
608 trans.x, trans.y);
|
|
609 }
|
|
610
|
|
611 // Check glyph bounding box size
|
|
612 if (check_glyph_area(render_priv->library, glyph)) {
|
|
613 FT_Done_Glyph(glyph);
|
|
614 glyph = 0;
|
|
615 goto blend_vector_error;
|
|
616 }
|
|
617
|
|
618 ass_msg(render_priv->library, MSGL_DBG2,
|
|
619 "Parsed vector clip: scales (%f, %f) string [%s]\n",
|
|
620 drawing->scale_x, drawing->scale_y, drawing->text);
|
|
621
|
|
622 error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1);
|
|
623 if (error) {
|
|
624 ass_msg(render_priv->library, MSGL_WARN,
|
|
625 "Clip vector rasterization failed: %d. Skipping.", error);
|
|
626 FT_Done_Glyph(glyph);
|
|
627 glyph = 0;
|
|
628 }
|
|
629
|
|
630 blend_vector_error:
|
|
631 clip_bm = (FT_BitmapGlyph) glyph;
|
|
632
|
|
633 // Add to cache
|
|
634 memset(&v, 0, sizeof(v));
|
|
635 v.glyph = glyph;
|
|
636 cache_add_glyph(render_priv->cache.glyph_cache, &key, &v);
|
30200
|
637 }
|
19965
|
638
|
31853
|
639 if (!clip_bm) goto blend_vector_exit;
|
19965
|
640
|
30200
|
641 // Iterate through bitmaps and blend/clip them
|
|
642 for (cur = head; cur; cur = cur->next) {
|
|
643 int left, top, right, bottom, apos, bpos, y, x, w, h;
|
|
644 int ax, ay, aw, ah, as;
|
|
645 int bx, by, bw, bh, bs;
|
|
646 int aleft, atop, bleft, btop;
|
|
647 unsigned char *abuffer, *bbuffer, *nbuffer;
|
19965
|
648
|
30200
|
649 abuffer = cur->bitmap;
|
|
650 bbuffer = clip_bm->bitmap.buffer;
|
|
651 ax = cur->dst_x;
|
|
652 ay = cur->dst_y;
|
|
653 aw = cur->w;
|
|
654 ah = cur->h;
|
|
655 as = cur->stride;
|
|
656 bx = clip_bm->left;
|
31853
|
657 by = -clip_bm->top;
|
30200
|
658 bw = clip_bm->bitmap.width;
|
|
659 bh = clip_bm->bitmap.rows;
|
|
660 bs = clip_bm->bitmap.pitch;
|
18937
|
661
|
30200
|
662 // Calculate overlap coordinates
|
|
663 left = (ax > bx) ? ax : bx;
|
|
664 top = (ay > by) ? ay : by;
|
|
665 right = ((ax + aw) < (bx + bw)) ? (ax + aw) : (bx + bw);
|
|
666 bottom = ((ay + ah) < (by + bh)) ? (ay + ah) : (by + bh);
|
|
667 aleft = left - ax;
|
|
668 atop = top - ay;
|
|
669 w = right - left;
|
|
670 h = bottom - top;
|
|
671 bleft = left - bx;
|
|
672 btop = top - by;
|
|
673
|
|
674 if (render_priv->state.clip_drawing_mode) {
|
|
675 // Inverse clip
|
|
676 if (ax + aw < bx || ay + ah < by || ax > bx + bw ||
|
|
677 ay > by + bh) {
|
|
678 continue;
|
|
679 }
|
|
680
|
|
681 // Allocate new buffer and add to free list
|
|
682 nbuffer = malloc(as * ah);
|
31853
|
683 if (!nbuffer) goto blend_vector_exit;
|
30200
|
684 free_list_add(render_priv, nbuffer);
|
29263
|
685
|
30200
|
686 // Blend together
|
|
687 memcpy(nbuffer, abuffer, as * (ah - 1) + aw);
|
|
688 for (y = 0; y < h; y++)
|
|
689 for (x = 0; x < w; x++) {
|
|
690 apos = (atop + y) * as + aleft + x;
|
|
691 bpos = (btop + y) * bs + bleft + x;
|
|
692 nbuffer[apos] = FFMAX(0, abuffer[apos] - bbuffer[bpos]);
|
|
693 }
|
|
694 } else {
|
|
695 // Regular clip
|
|
696 if (ax + aw < bx || ay + ah < by || ax > bx + bw ||
|
|
697 ay > by + bh) {
|
|
698 cur->w = cur->h = 0;
|
|
699 continue;
|
|
700 }
|
18937
|
701
|
30200
|
702 // Allocate new buffer and add to free list
|
|
703 nbuffer = calloc(as, ah);
|
31853
|
704 if (!nbuffer) goto blend_vector_exit;
|
30200
|
705 free_list_add(render_priv, nbuffer);
|
18937
|
706
|
30200
|
707 // Blend together
|
|
708 for (y = 0; y < h; y++)
|
|
709 for (x = 0; x < w; x++) {
|
|
710 apos = (atop + y) * as + aleft + x;
|
|
711 bpos = (btop + y) * bs + bleft + x;
|
|
712 nbuffer[apos] = (abuffer[apos] * bbuffer[bpos] + 255) >> 8;
|
|
713 }
|
|
714 }
|
|
715 cur->bitmap = nbuffer;
|
|
716 }
|
18937
|
717
|
30200
|
718 blend_vector_exit:
|
|
719 ass_drawing_free(render_priv->state.clip_drawing);
|
|
720 render_priv->state.clip_drawing = 0;
|
18937
|
721 }
|
|
722
|
|
723 /**
|
30200
|
724 * \brief Convert TextInfo struct to ASS_Image list
|
|
725 * Splits glyphs in halves when needed (for \kf karaoke).
|
18937
|
726 */
|
31853
|
727 static ASS_Image *render_text(ASS_Renderer *render_priv, int dst_x, int dst_y)
|
30200
|
728 {
|
|
729 int pen_x, pen_y;
|
|
730 int i;
|
|
731 Bitmap *bm;
|
|
732 ASS_Image *head;
|
|
733 ASS_Image **tail = &head;
|
|
734 ASS_Image **last_tail = 0;
|
|
735 ASS_Image **here_tail = 0;
|
|
736 TextInfo *text_info = &render_priv->text_info;
|
|
737
|
|
738 for (i = 0; i < text_info->length; ++i) {
|
|
739 GlyphInfo *info = text_info->glyphs + i;
|
|
740 if ((info->symbol == 0) || (info->symbol == '\n') || !info->bm_s
|
|
741 || (info->shadow_x == 0 && info->shadow_y == 0) || info->skip)
|
|
742 continue;
|
|
743
|
|
744 pen_x =
|
|
745 dst_x + (info->pos.x >> 6) +
|
|
746 (int) (info->shadow_x * render_priv->border_scale);
|
|
747 pen_y =
|
|
748 dst_y + (info->pos.y >> 6) +
|
|
749 (int) (info->shadow_y * render_priv->border_scale);
|
|
750 bm = info->bm_s;
|
|
751
|
|
752 here_tail = tail;
|
|
753 tail =
|
|
754 render_glyph(render_priv, bm, pen_x, pen_y, info->c[3], 0,
|
|
755 1000000, tail);
|
|
756 if (last_tail && tail != here_tail && ((info->c[3] & 0xff) > 0))
|
|
757 render_overlap(render_priv, last_tail, here_tail);
|
|
758
|
|
759 last_tail = here_tail;
|
|
760 }
|
|
761
|
|
762 last_tail = 0;
|
|
763 for (i = 0; i < text_info->length; ++i) {
|
|
764 GlyphInfo *info = text_info->glyphs + i;
|
|
765 if ((info->symbol == 0) || (info->symbol == '\n') || !info->bm_o
|
|
766 || info->skip)
|
|
767 continue;
|
|
768
|
|
769 pen_x = dst_x + (info->pos.x >> 6);
|
|
770 pen_y = dst_y + (info->pos.y >> 6);
|
|
771 bm = info->bm_o;
|
|
772
|
|
773 if ((info->effect_type == EF_KARAOKE_KO)
|
|
774 && (info->effect_timing <= (info->bbox.xMax >> 6))) {
|
|
775 // do nothing
|
|
776 } else {
|
|
777 here_tail = tail;
|
|
778 tail =
|
|
779 render_glyph(render_priv, bm, pen_x, pen_y, info->c[2],
|
|
780 0, 1000000, tail);
|
|
781 if (last_tail && tail != here_tail && ((info->c[2] & 0xff) > 0))
|
|
782 render_overlap(render_priv, last_tail, here_tail);
|
|
783
|
|
784 last_tail = here_tail;
|
|
785 }
|
|
786 }
|
|
787
|
|
788 for (i = 0; i < text_info->length; ++i) {
|
|
789 GlyphInfo *info = text_info->glyphs + i;
|
|
790 if ((info->symbol == 0) || (info->symbol == '\n') || !info->bm
|
|
791 || info->skip)
|
|
792 continue;
|
|
793
|
|
794 pen_x = dst_x + (info->pos.x >> 6);
|
|
795 pen_y = dst_y + (info->pos.y >> 6);
|
|
796 bm = info->bm;
|
|
797
|
|
798 if ((info->effect_type == EF_KARAOKE)
|
|
799 || (info->effect_type == EF_KARAOKE_KO)) {
|
|
800 if (info->effect_timing > (info->bbox.xMax >> 6))
|
|
801 tail =
|
|
802 render_glyph(render_priv, bm, pen_x, pen_y,
|
|
803 info->c[0], 0, 1000000, tail);
|
|
804 else
|
|
805 tail =
|
|
806 render_glyph(render_priv, bm, pen_x, pen_y,
|
|
807 info->c[1], 0, 1000000, tail);
|
|
808 } else if (info->effect_type == EF_KARAOKE_KF) {
|
|
809 tail =
|
|
810 render_glyph(render_priv, bm, pen_x, pen_y, info->c[0],
|
|
811 info->c[1], info->effect_timing, tail);
|
|
812 } else
|
|
813 tail =
|
|
814 render_glyph(render_priv, bm, pen_x, pen_y, info->c[0],
|
|
815 0, 1000000, tail);
|
|
816 }
|
|
817
|
|
818 *tail = 0;
|
|
819 blend_vector_clip(render_priv, head);
|
|
820
|
|
821 return head;
|
28748
|
822 }
|
29383
|
823
|
30200
|
824 static void compute_string_bbox(TextInfo *info, DBBox *bbox)
|
18937
|
825 {
|
30200
|
826 int i;
|
19556
|
827
|
30200
|
828 if (info->length > 0) {
|
|
829 bbox->xMin = 32000;
|
|
830 bbox->xMax = -32000;
|
|
831 bbox->yMin = -1 * info->lines[0].asc + d6_to_double(info->glyphs[0].pos.y);
|
|
832 bbox->yMax = info->height - info->lines[0].asc +
|
|
833 d6_to_double(info->glyphs[0].pos.y);
|
19556
|
834
|
30200
|
835 for (i = 0; i < info->length; ++i) {
|
|
836 if (info->glyphs[i].skip) continue;
|
|
837 double s = d6_to_double(info->glyphs[i].pos.x);
|
|
838 double e = s + d6_to_double(info->glyphs[i].advance.x);
|
|
839 bbox->xMin = FFMIN(bbox->xMin, s);
|
|
840 bbox->xMax = FFMAX(bbox->xMax, e);
|
|
841 }
|
|
842 } else
|
|
843 bbox->xMin = bbox->xMax = bbox->yMin = bbox->yMax = 0.;
|
19556
|
844 }
|
|
845
|
18937
|
846 /**
|
19873
|
847 * \brief partially reset render_context to style values
|
|
848 * Works like {\r}: resets some style overrides
|
|
849 */
|
30200
|
850 void reset_render_context(ASS_Renderer *render_priv)
|
19873
|
851 {
|
30200
|
852 render_priv->state.c[0] = render_priv->state.style->PrimaryColour;
|
|
853 render_priv->state.c[1] = render_priv->state.style->SecondaryColour;
|
|
854 render_priv->state.c[2] = render_priv->state.style->OutlineColour;
|
|
855 render_priv->state.c[3] = render_priv->state.style->BackColour;
|
|
856 render_priv->state.flags =
|
|
857 (render_priv->state.style->Underline ? DECO_UNDERLINE : 0) |
|
|
858 (render_priv->state.style->StrikeOut ? DECO_STRIKETHROUGH : 0);
|
|
859 render_priv->state.font_size = render_priv->state.style->FontSize;
|
|
860
|
|
861 free(render_priv->state.family);
|
|
862 render_priv->state.family = NULL;
|
|
863 render_priv->state.family = strdup(render_priv->state.style->FontName);
|
|
864 render_priv->state.treat_family_as_pattern =
|
|
865 render_priv->state.style->treat_fontname_as_pattern;
|
|
866 render_priv->state.bold = render_priv->state.style->Bold;
|
|
867 render_priv->state.italic = render_priv->state.style->Italic;
|
|
868 update_font(render_priv);
|
|
869
|
|
870 change_border(render_priv, -1., -1.);
|
|
871 render_priv->state.scale_x = render_priv->state.style->ScaleX;
|
|
872 render_priv->state.scale_y = render_priv->state.style->ScaleY;
|
|
873 render_priv->state.hspacing = render_priv->state.style->Spacing;
|
|
874 render_priv->state.be = 0;
|
|
875 render_priv->state.blur = 0.0;
|
|
876 render_priv->state.shadow_x = render_priv->state.style->Shadow;
|
|
877 render_priv->state.shadow_y = render_priv->state.style->Shadow;
|
|
878 render_priv->state.frx = render_priv->state.fry = 0.;
|
|
879 render_priv->state.frz = M_PI * render_priv->state.style->Angle / 180.;
|
|
880 render_priv->state.fax = render_priv->state.fay = 0.;
|
|
881 render_priv->state.wrap_style = render_priv->track->WrapStyle;
|
|
882 }
|
|
883
|
|
884 /**
|
|
885 * \brief Start new event. Reset render_priv->state.
|
|
886 */
|
|
887 static void
|
|
888 init_render_context(ASS_Renderer *render_priv, ASS_Event *event)
|
|
889 {
|
|
890 render_priv->state.event = event;
|
|
891 render_priv->state.style = render_priv->track->styles + event->Style;
|
|
892
|
|
893 reset_render_context(render_priv);
|
19873
|
894
|
30200
|
895 render_priv->state.evt_type = EVENT_NORMAL;
|
|
896 render_priv->state.alignment = render_priv->state.style->Alignment;
|
|
897 render_priv->state.pos_x = 0;
|
|
898 render_priv->state.pos_y = 0;
|
|
899 render_priv->state.org_x = 0;
|
|
900 render_priv->state.org_y = 0;
|
|
901 render_priv->state.have_origin = 0;
|
|
902 render_priv->state.clip_x0 = 0;
|
|
903 render_priv->state.clip_y0 = 0;
|
|
904 render_priv->state.clip_x1 = render_priv->track->PlayResX;
|
|
905 render_priv->state.clip_y1 = render_priv->track->PlayResY;
|
|
906 render_priv->state.clip_mode = 0;
|
|
907 render_priv->state.detect_collisions = 1;
|
|
908 render_priv->state.fade = 0;
|
|
909 render_priv->state.drawing_mode = 0;
|
|
910 render_priv->state.effect_type = EF_NONE;
|
|
911 render_priv->state.effect_timing = 0;
|
|
912 render_priv->state.effect_skip_timing = 0;
|
31875
|
913 ass_drawing_free(render_priv->state.drawing);
|
31853
|
914 render_priv->state.drawing = ass_drawing_new(render_priv->fontconfig_priv,
|
|
915 render_priv->state.font,
|
|
916 render_priv->ftlibrary);
|
19873
|
917
|
30200
|
918 apply_transition_effects(render_priv, event);
|
|
919 }
|
|
920
|
|
921 static void free_render_context(ASS_Renderer *render_priv)
|
|
922 {
|
|
923 free(render_priv->state.family);
|
|
924 ass_drawing_free(render_priv->state.drawing);
|
|
925
|
|
926 render_priv->state.family = NULL;
|
|
927 render_priv->state.drawing = NULL;
|
|
928 }
|
19873
|
929
|
30200
|
930 /*
|
|
931 * Replace the outline of a glyph by a contour which makes up a simple
|
|
932 * opaque rectangle.
|
|
933 */
|
|
934 static void draw_opaque_box(ASS_Renderer *render_priv, uint32_t ch,
|
|
935 FT_Glyph glyph, int sx, int sy)
|
18937
|
936 {
|
30200
|
937 int asc = 0, desc = 0;
|
|
938 int i;
|
|
939 int adv = d16_to_d6(glyph->advance.x);
|
|
940 double scale_y = render_priv->state.scale_y;
|
31853
|
941 double scale_x = render_priv->state.scale_x;
|
30200
|
942 FT_OutlineGlyph og = (FT_OutlineGlyph) glyph;
|
|
943 FT_Outline *ol;
|
|
944
|
|
945 // to avoid gaps
|
|
946 sx = FFMAX(64, sx);
|
|
947 sy = FFMAX(64, sy);
|
|
948
|
|
949 if (ch == -1) {
|
|
950 asc = render_priv->state.drawing->asc;
|
|
951 desc = render_priv->state.drawing->desc;
|
|
952 } else {
|
|
953 ass_font_get_asc_desc(render_priv->state.font, ch, &asc, &desc);
|
|
954 asc *= scale_y;
|
|
955 desc *= scale_y;
|
|
956 }
|
|
957
|
|
958 // Emulate the WTFish behavior of VSFilter, i.e. double-scale
|
|
959 // the sizes of the opaque box.
|
|
960 adv += double_to_d6(render_priv->state.hspacing * render_priv->font_scale
|
|
961 * scale_x);
|
|
962 adv *= scale_x;
|
|
963 sx *= scale_x;
|
|
964 sy *= scale_y;
|
|
965 desc *= scale_y;
|
|
966 desc += asc * (scale_y - 1.0);
|
|
967
|
|
968 FT_Vector points[4] = {
|
|
969 { .x = -sx, .y = asc + sy },
|
|
970 { .x = adv + sx, .y = asc + sy },
|
|
971 { .x = adv + sx, .y = -desc - sy },
|
|
972 { .x = -sx, .y = -desc - sy },
|
|
973 };
|
|
974
|
|
975 FT_Outline_Done(render_priv->ftlibrary, &og->outline);
|
|
976 FT_Outline_New(render_priv->ftlibrary, 4, 1, &og->outline);
|
|
977
|
|
978 ol = &og->outline;
|
|
979 ol->n_points = ol->n_contours = 0;
|
|
980 for (i = 0; i < 4; i++) {
|
|
981 ol->points[ol->n_points] = points[i];
|
|
982 ol->tags[ol->n_points++] = 1;
|
|
983 }
|
|
984 ol->contours[ol->n_contours++] = ol->n_points - 1;
|
|
985 }
|
|
986
|
|
987 /*
|
|
988 * Stroke an outline glyph in x/y direction. Applies various fixups to get
|
|
989 * around limitations of the FreeType stroker.
|
|
990 */
|
|
991 static void stroke_outline_glyph(ASS_Renderer *render_priv,
|
|
992 FT_OutlineGlyph *glyph, int sx, int sy)
|
|
993 {
|
|
994 if (sx <= 0 && sy <= 0)
|
|
995 return;
|
|
996
|
|
997 fix_freetype_stroker(*glyph, sx, sy);
|
|
998
|
|
999 // Borders are equal; use the regular stroker
|
|
1000 if (sx == sy && render_priv->state.stroker) {
|
|
1001 int error;
|
|
1002 error = FT_Glyph_StrokeBorder((FT_Glyph *) glyph,
|
|
1003 render_priv->state.stroker, 0, 1);
|
|
1004 if (error)
|
|
1005 ass_msg(render_priv->library, MSGL_WARN,
|
|
1006 "FT_Glyph_Stroke error: %d", error);
|
|
1007
|
|
1008 // "Stroke" with the outline emboldener in two passes.
|
|
1009 // The outlines look uglier, but the emboldening never adds any points
|
|
1010 } else {
|
|
1011 int i;
|
|
1012 FT_Outline *ol = &(*glyph)->outline;
|
|
1013 FT_Outline nol;
|
|
1014 FT_Outline_New(render_priv->ftlibrary, ol->n_points,
|
|
1015 ol->n_contours, &nol);
|
|
1016 FT_Outline_Copy(ol, &nol);
|
|
1017
|
|
1018 FT_Outline_Embolden(ol, sx * 2);
|
|
1019 FT_Outline_Translate(ol, -sx, -sx);
|
|
1020 FT_Outline_Embolden(&nol, sy * 2);
|
|
1021 FT_Outline_Translate(&nol, -sy, -sy);
|
|
1022
|
|
1023 for (i = 0; i < ol->n_points; i++)
|
|
1024 ol->points[i].y = nol.points[i].y;
|
|
1025
|
|
1026 FT_Outline_Done(render_priv->ftlibrary, &nol);
|
|
1027 }
|
18937
|
1028 }
|
|
1029
|
23179
|
1030 /**
|
31853
|
1031 * \brief Prepare glyph hash
|
|
1032 */
|
|
1033 static void
|
|
1034 fill_glyph_hash(ASS_Renderer *priv, GlyphHashKey *key,
|
|
1035 ASS_Drawing *drawing, uint32_t ch)
|
|
1036 {
|
|
1037 if (drawing->hash) {
|
|
1038 key->scale_x = double_to_d16(priv->state.scale_x);
|
|
1039 key->scale_y = double_to_d16(priv->state.scale_y);
|
|
1040 key->outline.x = priv->state.border_x * 0xFFFF;
|
|
1041 key->outline.y = priv->state.border_y * 0xFFFF;
|
|
1042 key->border_style = priv->state.style->BorderStyle;
|
|
1043 key->drawing_hash = drawing->hash;
|
|
1044 // not very clean, but works
|
|
1045 key->size = drawing->scale;
|
|
1046 key->ch = -1;
|
|
1047 } else {
|
|
1048 key->font = priv->state.font;
|
|
1049 key->size = priv->state.font_size;
|
|
1050 key->ch = ch;
|
|
1051 key->bold = priv->state.bold;
|
|
1052 key->italic = priv->state.italic;
|
|
1053 key->scale_x = double_to_d16(priv->state.scale_x);
|
|
1054 key->scale_y = double_to_d16(priv->state.scale_y);
|
|
1055 key->outline.x = priv->state.border_x * 0xFFFF;
|
|
1056 key->outline.y = priv->state.border_y * 0xFFFF;
|
|
1057 key->flags = priv->state.flags;
|
|
1058 key->border_style = priv->state.style->BorderStyle;
|
|
1059 }
|
|
1060 }
|
|
1061
|
|
1062 /**
|
23179
|
1063 * \brief Get normal and outline (border) glyphs
|
|
1064 * \param symbol ucs4 char
|
|
1065 * \param info out: struct filled with extracted data
|
|
1066 * Tries to get both glyphs from cache.
|
|
1067 * If they can't be found, gets a glyph from font face, generates outline with FT_Stroker,
|
|
1068 * and add them to cache.
|
|
1069 * The glyphs are returned in info->glyph and info->outline_glyph
|
|
1070 */
|
30200
|
1071 static void
|
|
1072 get_outline_glyph(ASS_Renderer *render_priv, int symbol, GlyphInfo *info,
|
|
1073 ASS_Drawing *drawing)
|
23021
|
1074 {
|
30200
|
1075 GlyphHashValue *val;
|
|
1076 GlyphHashKey key;
|
31853
|
1077
|
30200
|
1078 memset(&key, 0, sizeof(key));
|
|
1079 memset(info, 0, sizeof(GlyphInfo));
|
23021
|
1080
|
31853
|
1081 fill_glyph_hash(render_priv, &key, drawing, symbol);
|
30200
|
1082 val = cache_find_glyph(render_priv->cache.glyph_cache, &key);
|
|
1083 if (val) {
|
31853
|
1084 info->glyph = val->glyph;
|
|
1085 info->outline_glyph = val->outline_glyph;
|
30200
|
1086 info->bbox = val->bbox_scaled;
|
|
1087 info->advance.x = val->advance.x;
|
|
1088 info->advance.y = val->advance.y;
|
|
1089 if (drawing->hash) {
|
|
1090 drawing->asc = val->asc;
|
|
1091 drawing->desc = val->desc;
|
|
1092 }
|
|
1093 } else {
|
|
1094 GlyphHashValue v;
|
|
1095 if (drawing->hash) {
|
|
1096 if(!ass_drawing_parse(drawing, 0))
|
|
1097 return;
|
31853
|
1098 info->glyph = (FT_Glyph) drawing->glyph;
|
30200
|
1099 } else {
|
|
1100 info->glyph =
|
|
1101 ass_font_get_glyph(render_priv->fontconfig_priv,
|
|
1102 render_priv->state.font, symbol,
|
|
1103 render_priv->settings.hinting,
|
|
1104 render_priv->state.flags);
|
|
1105 }
|
|
1106 if (!info->glyph)
|
|
1107 return;
|
31853
|
1108
|
30200
|
1109 info->advance.x = d16_to_d6(info->glyph->advance.x);
|
|
1110 info->advance.y = d16_to_d6(info->glyph->advance.y);
|
|
1111 FT_Glyph_Get_CBox(info->glyph, FT_GLYPH_BBOX_SUBPIXELS, &info->bbox);
|
23021
|
1112
|
30200
|
1113 if (render_priv->state.style->BorderStyle == 3 &&
|
|
1114 (render_priv->state.border_x > 0||
|
|
1115 render_priv->state.border_y > 0)) {
|
|
1116 FT_Glyph_Copy(info->glyph, &info->outline_glyph);
|
|
1117 draw_opaque_box(render_priv, symbol, info->outline_glyph,
|
|
1118 double_to_d6(render_priv->state.border_x *
|
|
1119 render_priv->border_scale),
|
|
1120 double_to_d6(render_priv->state.border_y *
|
|
1121 render_priv->border_scale));
|
31853
|
1122 } else if ((render_priv->state.border_x > 0
|
|
1123 || render_priv->state.border_y > 0)
|
|
1124 && key.scale_x && key.scale_y) {
|
23025
|
1125
|
30200
|
1126 FT_Glyph_Copy(info->glyph, &info->outline_glyph);
|
|
1127 stroke_outline_glyph(render_priv,
|
|
1128 (FT_OutlineGlyph *) &info->outline_glyph,
|
|
1129 double_to_d6(render_priv->state.border_x *
|
|
1130 render_priv->border_scale),
|
|
1131 double_to_d6(render_priv->state.border_y *
|
|
1132 render_priv->border_scale));
|
|
1133 }
|
|
1134
|
|
1135 memset(&v, 0, sizeof(v));
|
31853
|
1136 v.glyph = info->glyph;
|
|
1137 v.outline_glyph = info->outline_glyph;
|
30200
|
1138 v.advance = info->advance;
|
|
1139 v.bbox_scaled = info->bbox;
|
|
1140 if (drawing->hash) {
|
|
1141 v.asc = drawing->asc;
|
|
1142 v.desc = drawing->desc;
|
|
1143 }
|
|
1144 cache_add_glyph(render_priv->cache.glyph_cache, &key, &v);
|
|
1145 }
|
23021
|
1146 }
|
|
1147
|
31853
|
1148 /**
|
|
1149 * \brief Apply transformation to outline points of a glyph
|
|
1150 * Applies rotations given by frx, fry and frz and projects the points back
|
|
1151 * onto the screen plane.
|
|
1152 */
|
|
1153 static void
|
|
1154 transform_3d_points(FT_Vector shift, FT_Glyph glyph, double frx, double fry,
|
|
1155 double frz, double fax, double fay, double scale,
|
|
1156 int yshift)
|
|
1157 {
|
|
1158 double sx = sin(frx);
|
|
1159 double sy = sin(fry);
|
|
1160 double sz = sin(frz);
|
|
1161 double cx = cos(frx);
|
|
1162 double cy = cos(fry);
|
|
1163 double cz = cos(frz);
|
|
1164 FT_Outline *outline = &((FT_OutlineGlyph) glyph)->outline;
|
|
1165 FT_Vector *p = outline->points;
|
|
1166 double x, y, z, xx, yy, zz;
|
|
1167 int i, dist;
|
|
1168
|
|
1169 dist = 20000 * scale;
|
|
1170 for (i = 0; i < outline->n_points; i++) {
|
|
1171 x = (double) p[i].x + shift.x + (fax * (yshift - p[i].y));
|
|
1172 y = (double) p[i].y + shift.y + (-fay * p[i].x);
|
|
1173 z = 0.;
|
|
1174
|
|
1175 xx = x * cz + y * sz;
|
|
1176 yy = -(x * sz - y * cz);
|
|
1177 zz = z;
|
|
1178
|
|
1179 x = xx;
|
|
1180 y = yy * cx + zz * sx;
|
|
1181 z = yy * sx - zz * cx;
|
|
1182
|
|
1183 xx = x * cy + z * sy;
|
|
1184 yy = y;
|
|
1185 zz = x * sy - z * cy;
|
|
1186
|
|
1187 zz = FFMAX(zz, 1000 - dist);
|
|
1188
|
|
1189 x = (xx * dist) / (zz + dist);
|
|
1190 y = (yy * dist) / (zz + dist);
|
|
1191 p[i].x = x - shift.x + 0.5;
|
|
1192 p[i].y = y - shift.y + 0.5;
|
|
1193 }
|
|
1194 }
|
|
1195
|
|
1196 /**
|
|
1197 * \brief Apply 3d transformation to several objects
|
|
1198 * \param shift FreeType vector
|
|
1199 * \param glyph FreeType glyph
|
|
1200 * \param glyph2 FreeType glyph
|
|
1201 * \param frx x-axis rotation angle
|
|
1202 * \param fry y-axis rotation angle
|
|
1203 * \param frz z-axis rotation angle
|
|
1204 * Rotates both glyphs by frx, fry and frz. Shift vector is added before rotation and subtracted after it.
|
|
1205 */
|
|
1206 static void
|
|
1207 transform_3d(FT_Vector shift, FT_Glyph *glyph, FT_Glyph *glyph2,
|
|
1208 double frx, double fry, double frz, double fax, double fay,
|
|
1209 double scale, int yshift)
|
|
1210 {
|
|
1211 frx = -frx;
|
|
1212 frz = -frz;
|
|
1213 if (frx != 0. || fry != 0. || frz != 0. || fax != 0. || fay != 0.) {
|
|
1214 if (glyph && *glyph)
|
|
1215 transform_3d_points(shift, *glyph, frx, fry, frz,
|
|
1216 fax, fay, scale, yshift);
|
|
1217
|
|
1218 if (glyph2 && *glyph2)
|
|
1219 transform_3d_points(shift, *glyph2, frx, fry, frz,
|
|
1220 fax, fay, scale, yshift);
|
|
1221 }
|
|
1222 }
|
23173
|
1223
|
18937
|
1224 /**
|
23179
|
1225 * \brief Get bitmaps for a glyph
|
|
1226 * \param info glyph info
|
|
1227 * Tries to get glyph bitmaps from bitmap cache.
|
|
1228 * If they can't be found, they are generated by rotating and rendering the glyph.
|
|
1229 * After that, bitmaps are added to the cache.
|
|
1230 * They are returned in info->bm (glyph), info->bm_o (outline) and info->bm_s (shadow).
|
18937
|
1231 */
|
30200
|
1232 static void
|
|
1233 get_bitmap_glyph(ASS_Renderer *render_priv, GlyphInfo *info)
|
18937
|
1234 {
|
30200
|
1235 BitmapHashValue *val;
|
|
1236 BitmapHashKey *key = &info->hash_key;
|
|
1237
|
|
1238 val = cache_find_bitmap(render_priv->cache.bitmap_cache, key);
|
29263
|
1239
|
30200
|
1240 if (val) {
|
|
1241 info->bm = val->bm;
|
|
1242 info->bm_o = val->bm_o;
|
|
1243 info->bm_s = val->bm_s;
|
|
1244 } else {
|
|
1245 FT_Vector shift;
|
|
1246 BitmapHashValue hash_val;
|
|
1247 int error;
|
|
1248 double fax_scaled, fay_scaled;
|
|
1249 info->bm = info->bm_o = info->bm_s = 0;
|
|
1250 if (info->glyph && info->symbol != '\n' && info->symbol != 0
|
|
1251 && !info->skip) {
|
31853
|
1252 FT_Glyph glyph;
|
|
1253 FT_Glyph outline;
|
|
1254 double scale_x = render_priv->font_scale_x;
|
|
1255
|
|
1256 FT_Glyph_Copy(info->glyph, &glyph);
|
|
1257 FT_Glyph_Copy(info->outline_glyph, &outline);
|
30200
|
1258 // calculating rotation shift vector (from rotation origin to the glyph basepoint)
|
31853
|
1259 shift.x = key->shift_x;
|
|
1260 shift.y = key->shift_y;
|
|
1261 fax_scaled = info->fax *
|
30200
|
1262 render_priv->state.scale_x;
|
|
1263 fay_scaled = info->fay * render_priv->state.scale_y;
|
|
1264 // apply rotation
|
31853
|
1265 transform_3d(shift, &glyph, &outline,
|
30200
|
1266 info->frx, info->fry, info->frz, fax_scaled,
|
|
1267 fay_scaled, render_priv->font_scale, info->asc);
|
29263
|
1268
|
31853
|
1269 // PAR correction scaling
|
|
1270 FT_Matrix m = { double_to_d16(scale_x), 0,
|
|
1271 0, double_to_d16(1.0) };
|
|
1272
|
30200
|
1273 // subpixel shift
|
31853
|
1274 if (glyph) {
|
|
1275 FT_Outline *outl = &((FT_OutlineGlyph) glyph)->outline;
|
|
1276 if (scale_x != 1.0)
|
|
1277 FT_Outline_Transform(outl, &m);
|
|
1278 FT_Outline_Translate(outl, key->advance.x, -key->advance.y);
|
|
1279 }
|
|
1280 if (outline) {
|
|
1281 FT_Outline *outl = &((FT_OutlineGlyph) outline)->outline;
|
|
1282 if (scale_x != 1.0)
|
|
1283 FT_Outline_Transform(outl, &m);
|
|
1284 FT_Outline_Translate(outl, key->advance.x, -key->advance.y);
|
|
1285 }
|
30200
|
1286 // render glyph
|
|
1287 error = glyph_to_bitmap(render_priv->library,
|
|
1288 render_priv->synth_priv,
|
31853
|
1289 glyph, outline,
|
30200
|
1290 &info->bm, &info->bm_o,
|
|
1291 &info->bm_s, info->be,
|
|
1292 info->blur * render_priv->border_scale,
|
31853
|
1293 key->shadow_offset, key->border_style);
|
30200
|
1294 if (error)
|
|
1295 info->symbol = 0;
|
23177
|
1296
|
30200
|
1297 // add bitmaps to cache
|
|
1298 hash_val.bm_o = info->bm_o;
|
|
1299 hash_val.bm = info->bm;
|
|
1300 hash_val.bm_s = info->bm_s;
|
31853
|
1301 cache_add_bitmap(render_priv->cache.bitmap_cache, key, &hash_val);
|
|
1302
|
|
1303 FT_Done_Glyph(glyph);
|
|
1304 FT_Done_Glyph(outline);
|
30200
|
1305 }
|
|
1306 }
|
18937
|
1307 }
|
|
1308
|
|
1309 /**
|
19932
|
1310 * This function goes through text_info and calculates text parameters.
|
|
1311 * The following text_info fields are filled:
|
|
1312 * height
|
|
1313 * lines[].height
|
|
1314 * lines[].asc
|
|
1315 * lines[].desc
|
|
1316 */
|
30200
|
1317 static void measure_text(ASS_Renderer *render_priv)
|
|
1318 {
|
|
1319 TextInfo *text_info = &render_priv->text_info;
|
|
1320 int cur_line = 0;
|
|
1321 double max_asc = 0., max_desc = 0.;
|
|
1322 GlyphInfo *last = NULL;
|
|
1323 int i;
|
|
1324 int empty_line = 1;
|
|
1325 text_info->height = 0.;
|
|
1326 for (i = 0; i < text_info->length + 1; ++i) {
|
|
1327 if ((i == text_info->length) || text_info->glyphs[i].linebreak) {
|
|
1328 if (empty_line && cur_line > 0 && last && i < text_info->length) {
|
|
1329 max_asc = d6_to_double(last->asc) / 2.0;
|
|
1330 max_desc = d6_to_double(last->desc) / 2.0;
|
|
1331 }
|
|
1332 text_info->lines[cur_line].asc = max_asc;
|
|
1333 text_info->lines[cur_line].desc = max_desc;
|
|
1334 text_info->height += max_asc + max_desc;
|
|
1335 cur_line++;
|
|
1336 max_asc = max_desc = 0.;
|
|
1337 empty_line = 1;
|
|
1338 } else
|
|
1339 empty_line = 0;
|
|
1340 if (i < text_info->length) {
|
|
1341 GlyphInfo *cur = text_info->glyphs + i;
|
|
1342 if (d6_to_double(cur->asc) > max_asc)
|
|
1343 max_asc = d6_to_double(cur->asc);
|
|
1344 if (d6_to_double(cur->desc) > max_desc)
|
|
1345 max_desc = d6_to_double(cur->desc);
|
|
1346 if (cur->symbol != '\n' && cur->symbol != 0)
|
|
1347 last = cur;
|
|
1348 }
|
|
1349 }
|
|
1350 text_info->height +=
|
|
1351 (text_info->n_lines -
|
|
1352 1) * render_priv->settings.line_spacing;
|
|
1353 }
|
|
1354
|
|
1355 /**
|
|
1356 * Mark extra whitespace for later removal.
|
|
1357 */
|
|
1358 #define IS_WHITESPACE(x) ((x->symbol == ' ' || x->symbol == '\n') \
|
|
1359 && !x->linebreak)
|
|
1360 static void trim_whitespace(ASS_Renderer *render_priv)
|
19932
|
1361 {
|
30200
|
1362 int i, j;
|
|
1363 GlyphInfo *cur;
|
|
1364 TextInfo *ti = &render_priv->text_info;
|
|
1365
|
|
1366 // Mark trailing spaces
|
|
1367 i = ti->length - 1;
|
|
1368 cur = ti->glyphs + i;
|
|
1369 while (i && IS_WHITESPACE(cur)) {
|
|
1370 cur->skip++;
|
|
1371 cur = ti->glyphs + --i;
|
|
1372 }
|
|
1373
|
|
1374 // Mark leading whitespace
|
|
1375 i = 0;
|
|
1376 cur = ti->glyphs;
|
|
1377 while (i < ti->length && IS_WHITESPACE(cur)) {
|
|
1378 cur->skip++;
|
|
1379 cur = ti->glyphs + ++i;
|
|
1380 }
|
|
1381
|
|
1382 // Mark all extraneous whitespace inbetween
|
|
1383 for (i = 0; i < ti->length; ++i) {
|
|
1384 cur = ti->glyphs + i;
|
|
1385 if (cur->linebreak) {
|
|
1386 // Mark whitespace before
|
|
1387 j = i - 1;
|
|
1388 cur = ti->glyphs + j;
|
|
1389 while (j && IS_WHITESPACE(cur)) {
|
|
1390 cur->skip++;
|
|
1391 cur = ti->glyphs + --j;
|
|
1392 }
|
|
1393 // A break itself can contain a whitespace, too
|
|
1394 cur = ti->glyphs + i;
|
|
1395 if (cur->symbol == ' ')
|
|
1396 cur->skip++;
|
|
1397 // Mark whitespace after
|
|
1398 j = i + 1;
|
|
1399 cur = ti->glyphs + j;
|
|
1400 while (j < ti->length && IS_WHITESPACE(cur)) {
|
|
1401 cur->skip++;
|
|
1402 cur = ti->glyphs + ++j;
|
|
1403 }
|
|
1404 i = j - 1;
|
|
1405 }
|
|
1406 }
|
19932
|
1407 }
|
30200
|
1408 #undef IS_WHITESPACE
|
19932
|
1409
|
|
1410 /**
|
18937
|
1411 * \brief rearrange text between lines
|
|
1412 * \param max_text_width maximal text line width in pixels
|
|
1413 * The algo is similar to the one in libvo/sub.c:
|
|
1414 * 1. Place text, wrapping it when current line is full
|
|
1415 * 2. Try moving words from the end of a line to the beginning of the next one while it reduces
|
|
1416 * the difference in lengths between this two lines.
|
|
1417 * The result may not be optimal, but usually is good enough.
|
30200
|
1418 *
|
31853
|
1419 * FIXME: implement style 0 and 3 correctly
|
18937
|
1420 */
|
30200
|
1421 static void
|
|
1422 wrap_lines_smart(ASS_Renderer *render_priv, double max_text_width)
|
18937
|
1423 {
|
30200
|
1424 int i;
|
|
1425 GlyphInfo *cur, *s1, *e1, *s2, *s3, *w;
|
|
1426 int last_space;
|
|
1427 int break_type;
|
|
1428 int exit;
|
|
1429 double pen_shift_x;
|
|
1430 double pen_shift_y;
|
|
1431 int cur_line;
|
|
1432 TextInfo *text_info = &render_priv->text_info;
|
18937
|
1433
|
30200
|
1434 last_space = -1;
|
|
1435 text_info->n_lines = 1;
|
|
1436 break_type = 0;
|
|
1437 s1 = text_info->glyphs; // current line start
|
|
1438 for (i = 0; i < text_info->length; ++i) {
|
|
1439 int break_at;
|
|
1440 double s_offset, len;
|
|
1441 cur = text_info->glyphs + i;
|
|
1442 break_at = -1;
|
|
1443 s_offset = d6_to_double(s1->bbox.xMin + s1->pos.x);
|
|
1444 len = d6_to_double(cur->bbox.xMax + cur->pos.x) - s_offset;
|
18937
|
1445
|
30200
|
1446 if (cur->symbol == '\n') {
|
|
1447 break_type = 2;
|
|
1448 break_at = i;
|
|
1449 ass_msg(render_priv->library, MSGL_DBG2,
|
|
1450 "forced line break at %d", break_at);
|
|
1451 }
|
29263
|
1452
|
30200
|
1453 if ((len >= max_text_width)
|
|
1454 && (render_priv->state.wrap_style != 2)) {
|
|
1455 break_type = 1;
|
|
1456 break_at = last_space;
|
|
1457 if (break_at == -1)
|
|
1458 break_at = i - 1;
|
|
1459 if (break_at == -1)
|
|
1460 break_at = 0;
|
|
1461 ass_msg(render_priv->library, MSGL_DBG2, "overfill at %d", i);
|
|
1462 ass_msg(render_priv->library, MSGL_DBG2, "line break at %d",
|
|
1463 break_at);
|
|
1464 }
|
18937
|
1465
|
30200
|
1466 if (break_at != -1) {
|
|
1467 // need to use one more line
|
|
1468 // marking break_at+1 as start of a new line
|
|
1469 int lead = break_at + 1; // the first symbol of the new line
|
|
1470 if (text_info->n_lines >= text_info->max_lines) {
|
|
1471 // Raise maximum number of lines
|
|
1472 text_info->max_lines *= 2;
|
|
1473 text_info->lines = realloc(text_info->lines,
|
|
1474 sizeof(LineInfo) *
|
|
1475 text_info->max_lines);
|
|
1476 }
|
|
1477 if (lead < text_info->length)
|
|
1478 text_info->glyphs[lead].linebreak = break_type;
|
|
1479 last_space = -1;
|
|
1480 s1 = text_info->glyphs + lead;
|
|
1481 s_offset = d6_to_double(s1->bbox.xMin + s1->pos.x);
|
|
1482 text_info->n_lines++;
|
|
1483 }
|
29263
|
1484
|
30200
|
1485 if (cur->symbol == ' ')
|
|
1486 last_space = i;
|
22913
|
1487
|
30200
|
1488 // make sure the hard linebreak is not forgotten when
|
|
1489 // there was a new soft linebreak just inserted
|
|
1490 if (cur->symbol == '\n' && break_type == 1)
|
|
1491 i--;
|
|
1492 }
|
18937
|
1493 #define DIFF(x,y) (((x) < (y)) ? (y - x) : (x - y))
|
30200
|
1494 exit = 0;
|
|
1495 while (!exit && render_priv->state.wrap_style != 1) {
|
|
1496 exit = 1;
|
|
1497 w = s3 = text_info->glyphs;
|
|
1498 s1 = s2 = 0;
|
|
1499 for (i = 0; i <= text_info->length; ++i) {
|
|
1500 cur = text_info->glyphs + i;
|
|
1501 if ((i == text_info->length) || cur->linebreak) {
|
|
1502 s1 = s2;
|
|
1503 s2 = s3;
|
|
1504 s3 = cur;
|
|
1505 if (s1 && (s2->linebreak == 1)) { // have at least 2 lines, and linebreak is 'soft'
|
|
1506 double l1, l2, l1_new, l2_new;
|
18937
|
1507
|
30200
|
1508 w = s2;
|
|
1509 do {
|
|
1510 --w;
|
|
1511 } while ((w > s1) && (w->symbol == ' '));
|
|
1512 while ((w > s1) && (w->symbol != ' ')) {
|
|
1513 --w;
|
|
1514 }
|
|
1515 e1 = w;
|
|
1516 while ((e1 > s1) && (e1->symbol == ' ')) {
|
|
1517 --e1;
|
|
1518 }
|
|
1519 if (w->symbol == ' ')
|
|
1520 ++w;
|
18937
|
1521
|
30200
|
1522 l1 = d6_to_double(((s2 - 1)->bbox.xMax + (s2 - 1)->pos.x) -
|
|
1523 (s1->bbox.xMin + s1->pos.x));
|
|
1524 l2 = d6_to_double(((s3 - 1)->bbox.xMax + (s3 - 1)->pos.x) -
|
|
1525 (s2->bbox.xMin + s2->pos.x));
|
|
1526 l1_new = d6_to_double(
|
|
1527 (e1->bbox.xMax + e1->pos.x) -
|
|
1528 (s1->bbox.xMin + s1->pos.x));
|
|
1529 l2_new = d6_to_double(
|
|
1530 ((s3 - 1)->bbox.xMax + (s3 - 1)->pos.x) -
|
|
1531 (w->bbox.xMin + w->pos.x));
|
29263
|
1532
|
30200
|
1533 if (DIFF(l1_new, l2_new) < DIFF(l1, l2)) {
|
|
1534 w->linebreak = 1;
|
|
1535 s2->linebreak = 0;
|
|
1536 exit = 0;
|
|
1537 }
|
|
1538 }
|
|
1539 }
|
|
1540 if (i == text_info->length)
|
|
1541 break;
|
|
1542 }
|
|
1543
|
|
1544 }
|
|
1545 assert(text_info->n_lines >= 1);
|
18937
|
1546 #undef DIFF
|
29263
|
1547
|
30200
|
1548 measure_text(render_priv);
|
|
1549 trim_whitespace(render_priv);
|
|
1550
|
|
1551 pen_shift_x = 0.;
|
|
1552 pen_shift_y = 0.;
|
|
1553 cur_line = 1;
|
|
1554
|
|
1555 i = 0;
|
|
1556 cur = text_info->glyphs + i;
|
|
1557 while (i < text_info->length && cur->skip)
|
|
1558 cur = text_info->glyphs + ++i;
|
|
1559 pen_shift_x = d6_to_double(-cur->pos.x);
|
19932
|
1560
|
30200
|
1561 for (i = 0; i < text_info->length; ++i) {
|
|
1562 cur = text_info->glyphs + i;
|
|
1563 if (cur->linebreak) {
|
|
1564 while (i < text_info->length && cur->skip && cur->symbol != '\n')
|
|
1565 cur = text_info->glyphs + ++i;
|
|
1566 double height =
|
|
1567 text_info->lines[cur_line - 1].desc +
|
|
1568 text_info->lines[cur_line].asc;
|
|
1569 cur_line++;
|
|
1570 pen_shift_x = d6_to_double(-cur->pos.x);
|
|
1571 pen_shift_y += height + render_priv->settings.line_spacing;
|
|
1572 ass_msg(render_priv->library, MSGL_DBG2,
|
|
1573 "shifting from %d to %d by (%f, %f)", i,
|
|
1574 text_info->length - 1, pen_shift_x, pen_shift_y);
|
|
1575 }
|
|
1576 cur->pos.x += double_to_d6(pen_shift_x);
|
|
1577 cur->pos.y += double_to_d6(pen_shift_y);
|
|
1578 }
|
18937
|
1579 }
|
|
1580
|
|
1581 /**
|
|
1582 * \brief determine karaoke effects
|
|
1583 * Karaoke effects cannot be calculated during parse stage (get_next_char()),
|
|
1584 * so they are done in a separate step.
|
29263
|
1585 * Parse stage: when karaoke style override is found, its parameters are stored in the next glyph's
|
18937
|
1586 * (the first glyph of the karaoke word)'s effect_type and effect_timing.
|
|
1587 * This function:
|
|
1588 * 1. sets effect_type for all glyphs in the word (_karaoke_ word)
|
|
1589 * 2. sets effect_timing for all glyphs to x coordinate of the border line between the left and right karaoke parts
|
|
1590 * (left part is filled with PrimaryColour, right one - with SecondaryColour).
|
|
1591 */
|
30200
|
1592 static void process_karaoke_effects(ASS_Renderer *render_priv)
|
18937
|
1593 {
|
30200
|
1594 GlyphInfo *cur, *cur2;
|
|
1595 GlyphInfo *s1, *e1; // start and end of the current word
|
|
1596 GlyphInfo *s2; // start of the next word
|
|
1597 int i;
|
|
1598 int timing; // current timing
|
|
1599 int tm_start, tm_end; // timings at start and end of the current word
|
|
1600 int tm_current;
|
|
1601 double dt;
|
|
1602 int x;
|
|
1603 int x_start, x_end;
|
18937
|
1604
|
30200
|
1605 tm_current = render_priv->time - render_priv->state.event->Start;
|
|
1606 timing = 0;
|
|
1607 s1 = s2 = 0;
|
|
1608 for (i = 0; i <= render_priv->text_info.length; ++i) {
|
|
1609 cur = render_priv->text_info.glyphs + i;
|
|
1610 if ((i == render_priv->text_info.length)
|
|
1611 || (cur->effect_type != EF_NONE)) {
|
|
1612 s1 = s2;
|
|
1613 s2 = cur;
|
|
1614 if (s1) {
|
|
1615 e1 = s2 - 1;
|
|
1616 tm_start = timing + s1->effect_skip_timing;
|
|
1617 tm_end = tm_start + s1->effect_timing;
|
|
1618 timing = tm_end;
|
|
1619 x_start = 1000000;
|
|
1620 x_end = -1000000;
|
|
1621 for (cur2 = s1; cur2 <= e1; ++cur2) {
|
|
1622 x_start = FFMIN(x_start, d6_to_int(cur2->bbox.xMin + cur2->pos.x));
|
|
1623 x_end = FFMAX(x_end, d6_to_int(cur2->bbox.xMax + cur2->pos.x));
|
|
1624 }
|
18937
|
1625
|
30200
|
1626 dt = (tm_current - tm_start);
|
|
1627 if ((s1->effect_type == EF_KARAOKE)
|
|
1628 || (s1->effect_type == EF_KARAOKE_KO)) {
|
|
1629 if (dt > 0)
|
|
1630 x = x_end + 1;
|
|
1631 else
|
|
1632 x = x_start;
|
|
1633 } else if (s1->effect_type == EF_KARAOKE_KF) {
|
|
1634 dt /= (tm_end - tm_start);
|
|
1635 x = x_start + (x_end - x_start) * dt;
|
|
1636 } else {
|
|
1637 ass_msg(render_priv->library, MSGL_ERR,
|
|
1638 "Unknown effect type");
|
|
1639 continue;
|
|
1640 }
|
18937
|
1641
|
30200
|
1642 for (cur2 = s1; cur2 <= e1; ++cur2) {
|
|
1643 cur2->effect_type = s1->effect_type;
|
|
1644 cur2->effect_timing = x - d6_to_int(cur2->pos.x);
|
|
1645 }
|
|
1646 }
|
|
1647 }
|
|
1648 }
|
18937
|
1649 }
|
|
1650
|
|
1651 /**
|
20294
|
1652 * \brief Calculate base point for positioning and rotation
|
|
1653 * \param bbox text bbox
|
|
1654 * \param alignment alignment
|
|
1655 * \param bx, by out: base point coordinates
|
|
1656 */
|
30200
|
1657 static void get_base_point(DBBox *bbox, int alignment, double *bx, double *by)
|
20294
|
1658 {
|
30200
|
1659 const int halign = alignment & 3;
|
|
1660 const int valign = alignment & 12;
|
|
1661 if (bx)
|
|
1662 switch (halign) {
|
|
1663 case HALIGN_LEFT:
|
|
1664 *bx = bbox->xMin;
|
|
1665 break;
|
|
1666 case HALIGN_CENTER:
|
|
1667 *bx = (bbox->xMax + bbox->xMin) / 2.0;
|
|
1668 break;
|
|
1669 case HALIGN_RIGHT:
|
|
1670 *bx = bbox->xMax;
|
|
1671 break;
|
|
1672 }
|
|
1673 if (by)
|
|
1674 switch (valign) {
|
|
1675 case VALIGN_TOP:
|
|
1676 *by = bbox->yMin;
|
|
1677 break;
|
|
1678 case VALIGN_CENTER:
|
|
1679 *by = (bbox->yMax + bbox->yMin) / 2.0;
|
|
1680 break;
|
|
1681 case VALIGN_SUB:
|
|
1682 *by = bbox->yMax;
|
|
1683 break;
|
|
1684 }
|
20294
|
1685 }
|
|
1686
|
|
1687 /**
|
31853
|
1688 * Prepare bitmap hash key of a glyph
|
22215
|
1689 */
|
30200
|
1690 static void
|
31853
|
1691 fill_bitmap_hash(ASS_Renderer *priv, BitmapHashKey *hash_key,
|
|
1692 ASS_Drawing *drawing, FT_Vector pen, uint32_t code)
|
30200
|
1693 {
|
31853
|
1694 if (!drawing->hash) {
|
|
1695 hash_key->font = priv->state.font;
|
|
1696 hash_key->size = priv->state.font_size;
|
|
1697 hash_key->bold = priv->state.bold;
|
|
1698 hash_key->italic = priv->state.italic;
|
|
1699 } else {
|
|
1700 hash_key->drawing_hash = drawing->hash;
|
|
1701 hash_key->size = drawing->scale;
|
30200
|
1702 }
|
31853
|
1703 hash_key->ch = code;
|
|
1704 hash_key->outline.x = double_to_d16(priv->state.border_x);
|
|
1705 hash_key->outline.y = double_to_d16(priv->state.border_y);
|
|
1706 hash_key->scale_x = double_to_d16(priv->state.scale_x);
|
|
1707 hash_key->scale_y = double_to_d16(priv->state.scale_y);
|
|
1708 hash_key->frx = rot_key(priv->state.frx);
|
|
1709 hash_key->fry = rot_key(priv->state.fry);
|
|
1710 hash_key->frz = rot_key(priv->state.frz);
|
|
1711 hash_key->fax = double_to_d16(priv->state.fax);
|
|
1712 hash_key->fay = double_to_d16(priv->state.fay);
|
|
1713 hash_key->be = priv->state.be;
|
|
1714 hash_key->blur = priv->state.blur;
|
|
1715 hash_key->border_style = priv->state.style->BorderStyle;
|
|
1716 hash_key->shadow_offset.x = double_to_d6(
|
|
1717 priv->state.shadow_x * priv->border_scale -
|
|
1718 (int) (priv->state.shadow_x * priv->border_scale));
|
|
1719 hash_key->shadow_offset.y = double_to_d6(
|
|
1720 priv->state.shadow_y * priv->border_scale -
|
|
1721 (int) (priv->state.shadow_y * priv->border_scale));
|
|
1722 hash_key->flags = priv->state.flags;
|
22215
|
1723 }
|
|
1724
|
|
1725 /**
|
18937
|
1726 * \brief Main ass rendering function, glues everything together
|
|
1727 * \param event event to render
|
29048
|
1728 * \param event_images struct containing resulting images, will also be initialized
|
30200
|
1729 * Process event, appending resulting ASS_Image's to images_root.
|
18937
|
1730 */
|
30200
|
1731 static int
|
|
1732 ass_render_event(ASS_Renderer *render_priv, ASS_Event *event,
|
|
1733 EventImages *event_images)
|
18937
|
1734 {
|
30200
|
1735 char *p;
|
|
1736 FT_UInt previous;
|
|
1737 FT_UInt num_glyphs;
|
|
1738 FT_Vector pen;
|
|
1739 unsigned code;
|
|
1740 DBBox bbox;
|
|
1741 int i, j;
|
|
1742 int MarginL, MarginR, MarginV;
|
|
1743 int last_break;
|
|
1744 int alignment, halign, valign;
|
|
1745 int kern = render_priv->track->Kerning;
|
|
1746 double device_x = 0;
|
|
1747 double device_y = 0;
|
|
1748 TextInfo *text_info = &render_priv->text_info;
|
31853
|
1749 GlyphInfo *glyphs = render_priv->text_info.glyphs;
|
30200
|
1750 ASS_Drawing *drawing;
|
18937
|
1751
|
30200
|
1752 if (event->Style >= render_priv->track->n_styles) {
|
|
1753 ass_msg(render_priv->library, MSGL_WARN, "No style found");
|
|
1754 return 1;
|
|
1755 }
|
|
1756 if (!event->Text) {
|
|
1757 ass_msg(render_priv->library, MSGL_WARN, "Empty event");
|
|
1758 return 1;
|
|
1759 }
|
19650
|
1760
|
30200
|
1761 init_render_context(render_priv, event);
|
18937
|
1762
|
30200
|
1763 drawing = render_priv->state.drawing;
|
|
1764 text_info->length = 0;
|
|
1765 pen.x = 0;
|
|
1766 pen.y = 0;
|
|
1767 previous = 0;
|
|
1768 num_glyphs = 0;
|
|
1769 p = event->Text;
|
|
1770 // Event parsing.
|
|
1771 while (1) {
|
|
1772 // get next char, executing style override
|
|
1773 // this affects render_context
|
|
1774 do {
|
|
1775 code = get_next_char(render_priv, &p);
|
|
1776 if (render_priv->state.drawing_mode && code)
|
|
1777 ass_drawing_add_char(drawing, (char) code);
|
|
1778 } while (code && render_priv->state.drawing_mode); // skip everything in drawing mode
|
29263
|
1779
|
30200
|
1780 // Parse drawing
|
|
1781 if (drawing->i) {
|
|
1782 drawing->scale_x = render_priv->state.scale_x *
|
|
1783 render_priv->font_scale;
|
|
1784 drawing->scale_y = render_priv->state.scale_y *
|
|
1785 render_priv->font_scale;
|
|
1786 ass_drawing_hash(drawing);
|
|
1787 p--;
|
|
1788 code = -1;
|
|
1789 }
|
18937
|
1790
|
30200
|
1791 // face could have been changed in get_next_char
|
|
1792 if (!render_priv->state.font) {
|
|
1793 free_render_context(render_priv);
|
|
1794 return 1;
|
|
1795 }
|
18937
|
1796
|
30200
|
1797 if (code == 0)
|
|
1798 break;
|
|
1799
|
|
1800 if (text_info->length >= text_info->max_glyphs) {
|
|
1801 // Raise maximum number of glyphs
|
|
1802 text_info->max_glyphs *= 2;
|
31853
|
1803 text_info->glyphs = glyphs =
|
30200
|
1804 realloc(text_info->glyphs,
|
|
1805 sizeof(GlyphInfo) * text_info->max_glyphs);
|
|
1806 }
|
18937
|
1807
|
30200
|
1808 // Add kerning to pen
|
|
1809 if (kern && previous && code && !drawing->hash) {
|
|
1810 FT_Vector delta;
|
|
1811 delta =
|
|
1812 ass_font_get_kerning(render_priv->state.font, previous,
|
|
1813 code);
|
31853
|
1814 pen.x += delta.x * render_priv->state.scale_x;
|
|
1815 pen.y += delta.y * render_priv->state.scale_y;
|
30200
|
1816 }
|
28787
|
1817
|
30200
|
1818 ass_font_set_transform(render_priv->state.font,
|
31853
|
1819 render_priv->state.scale_x,
|
30200
|
1820 render_priv->state.scale_y, NULL);
|
18937
|
1821
|
30200
|
1822 get_outline_glyph(render_priv, code,
|
31853
|
1823 glyphs + text_info->length, drawing);
|
23021
|
1824
|
30200
|
1825 // Add additional space after italic to non-italic style changes
|
|
1826 if (text_info->length &&
|
31853
|
1827 glyphs[text_info->length - 1].hash_key.italic &&
|
30200
|
1828 !render_priv->state.italic) {
|
|
1829 int back = text_info->length - 1;
|
31853
|
1830 GlyphInfo *og = &glyphs[back];
|
30200
|
1831 while (back && og->bbox.xMax - og->bbox.xMin == 0
|
|
1832 && og->hash_key.italic)
|
31853
|
1833 og = &glyphs[--back];
|
30200
|
1834 if (og->bbox.xMax > og->advance.x) {
|
|
1835 // The FreeType oblique slants by 6/16
|
|
1836 pen.x += og->bbox.yMax * 0.375;
|
|
1837 }
|
|
1838 }
|
29263
|
1839
|
31853
|
1840 glyphs[text_info->length].pos.x = pen.x;
|
|
1841 glyphs[text_info->length].pos.y = pen.y;
|
29263
|
1842
|
31853
|
1843 pen.x += glyphs[text_info->length].advance.x;
|
30200
|
1844 pen.x += double_to_d6(render_priv->state.hspacing *
|
|
1845 render_priv->font_scale
|
|
1846 * render_priv->state.scale_x);
|
31853
|
1847 pen.y += glyphs[text_info->length].advance.y;
|
30200
|
1848 pen.y += (render_priv->state.fay * render_priv->state.scale_y) *
|
31853
|
1849 glyphs[text_info->length].advance.x;
|
30200
|
1850
|
|
1851 previous = code;
|
21614
|
1852
|
31853
|
1853 glyphs[text_info->length].symbol = code;
|
|
1854 glyphs[text_info->length].linebreak = 0;
|
30200
|
1855 for (i = 0; i < 4; ++i) {
|
|
1856 uint32_t clr = render_priv->state.c[i];
|
|
1857 change_alpha(&clr,
|
|
1858 mult_alpha(_a(clr), render_priv->state.fade), 1.);
|
31853
|
1859 glyphs[text_info->length].c[i] = clr;
|
30200
|
1860 }
|
31853
|
1861 glyphs[text_info->length].effect_type = render_priv->state.effect_type;
|
|
1862 glyphs[text_info->length].effect_timing =
|
30200
|
1863 render_priv->state.effect_timing;
|
31853
|
1864 glyphs[text_info->length].effect_skip_timing =
|
30200
|
1865 render_priv->state.effect_skip_timing;
|
31853
|
1866 glyphs[text_info->length].be = render_priv->state.be;
|
|
1867 glyphs[text_info->length].blur = render_priv->state.blur;
|
|
1868 glyphs[text_info->length].shadow_x = render_priv->state.shadow_x;
|
|
1869 glyphs[text_info->length].shadow_y = render_priv->state.shadow_y;
|
|
1870 glyphs[text_info->length].frx = render_priv->state.frx;
|
|
1871 glyphs[text_info->length].fry = render_priv->state.fry;
|
|
1872 glyphs[text_info->length].frz = render_priv->state.frz;
|
|
1873 glyphs[text_info->length].fax = render_priv->state.fax;
|
|
1874 glyphs[text_info->length].fay = render_priv->state.fay;
|
30200
|
1875 if (drawing->hash) {
|
31853
|
1876 glyphs[text_info->length].asc = drawing->asc;
|
|
1877 glyphs[text_info->length].desc = drawing->desc;
|
30200
|
1878 } else {
|
|
1879 ass_font_get_asc_desc(render_priv->state.font, code,
|
31853
|
1880 &glyphs[text_info->length].asc,
|
|
1881 &glyphs[text_info->length].desc);
|
21614
|
1882
|
31853
|
1883 glyphs[text_info->length].asc *= render_priv->state.scale_y;
|
|
1884 glyphs[text_info->length].desc *= render_priv->state.scale_y;
|
30200
|
1885 }
|
19716
|
1886
|
31853
|
1887 // fill bitmap hash
|
|
1888 fill_bitmap_hash(render_priv, &glyphs[text_info->length].hash_key,
|
|
1889 drawing, pen, code);
|
29263
|
1890
|
30200
|
1891 text_info->length++;
|
29263
|
1892
|
30200
|
1893 render_priv->state.effect_type = EF_NONE;
|
|
1894 render_priv->state.effect_timing = 0;
|
|
1895 render_priv->state.effect_skip_timing = 0;
|
19931
|
1896
|
30200
|
1897 if (drawing->hash) {
|
|
1898 ass_drawing_free(drawing);
|
|
1899 drawing = render_priv->state.drawing =
|
|
1900 ass_drawing_new(render_priv->fontconfig_priv,
|
|
1901 render_priv->state.font,
|
|
1902 render_priv->ftlibrary);
|
|
1903 }
|
|
1904 }
|
19556
|
1905
|
18937
|
1906
|
30200
|
1907 if (text_info->length == 0) {
|
|
1908 // no valid symbols in the event; this can be smth like {comment}
|
|
1909 free_render_context(render_priv);
|
|
1910 return 1;
|
|
1911 }
|
31853
|
1912
|
30200
|
1913 // depends on glyph x coordinates being monotonous, so it should be done before line wrap
|
|
1914 process_karaoke_effects(render_priv);
|
18937
|
1915
|
30200
|
1916 // alignments
|
|
1917 alignment = render_priv->state.alignment;
|
|
1918 halign = alignment & 3;
|
|
1919 valign = alignment & 12;
|
|
1920
|
|
1921 MarginL =
|
31853
|
1922 (event->MarginL) ? event->MarginL : render_priv->state.style->MarginL;
|
30200
|
1923 MarginR =
|
31853
|
1924 (event->MarginR) ? event->MarginR : render_priv->state.style->MarginR;
|
30200
|
1925 MarginV =
|
31853
|
1926 (event->MarginV) ? event->MarginV : render_priv->state.style->MarginV;
|
30200
|
1927
|
|
1928 if (render_priv->state.evt_type != EVENT_HSCROLL) {
|
|
1929 double max_text_width;
|
|
1930
|
|
1931 // calculate max length of a line
|
|
1932 max_text_width =
|
|
1933 x2scr(render_priv,
|
|
1934 render_priv->track->PlayResX - MarginR) -
|
|
1935 x2scr(render_priv, MarginL);
|
29263
|
1936
|
30200
|
1937 // rearrange text in several lines
|
|
1938 wrap_lines_smart(render_priv, max_text_width);
|
29263
|
1939
|
30200
|
1940 // align text
|
|
1941 last_break = -1;
|
|
1942 for (i = 1; i < text_info->length + 1; ++i) { // (text_info->length + 1) is the end of the last line
|
|
1943 if ((i == text_info->length)
|
31853
|
1944 || glyphs[i].linebreak) {
|
30200
|
1945 double width, shift = 0;
|
|
1946 GlyphInfo *first_glyph =
|
31853
|
1947 glyphs + last_break + 1;
|
|
1948 GlyphInfo *last_glyph = glyphs + i - 1;
|
30200
|
1949
|
|
1950 while (first_glyph < last_glyph && first_glyph->skip)
|
|
1951 first_glyph++;
|
|
1952
|
|
1953 while ((last_glyph > first_glyph)
|
|
1954 && ((last_glyph->symbol == '\n')
|
|
1955 || (last_glyph->symbol == 0)
|
|
1956 || (last_glyph->skip)))
|
|
1957 last_glyph--;
|
18937
|
1958
|
30200
|
1959 width = d6_to_double(
|
|
1960 last_glyph->pos.x + last_glyph->advance.x -
|
|
1961 first_glyph->pos.x);
|
|
1962 if (halign == HALIGN_LEFT) { // left aligned, no action
|
|
1963 shift = 0;
|
|
1964 } else if (halign == HALIGN_RIGHT) { // right aligned
|
|
1965 shift = max_text_width - width;
|
|
1966 } else if (halign == HALIGN_CENTER) { // centered
|
|
1967 shift = (max_text_width - width) / 2.0;
|
|
1968 }
|
|
1969 for (j = last_break + 1; j < i; ++j) {
|
31853
|
1970 glyphs[j].pos.x += double_to_d6(shift);
|
30200
|
1971 }
|
|
1972 last_break = i - 1;
|
|
1973 }
|
|
1974 }
|
|
1975 } else { // render_priv->state.evt_type == EVENT_HSCROLL
|
|
1976 measure_text(render_priv);
|
|
1977 }
|
|
1978
|
|
1979 // determing text bounding box
|
|
1980 compute_string_bbox(text_info, &bbox);
|
|
1981
|
|
1982 // determine device coordinates for text
|
19556
|
1983
|
30200
|
1984 // x coordinate for everything except positioned events
|
|
1985 if (render_priv->state.evt_type == EVENT_NORMAL ||
|
|
1986 render_priv->state.evt_type == EVENT_VSCROLL) {
|
|
1987 device_x = x2scr(render_priv, MarginL);
|
|
1988 } else if (render_priv->state.evt_type == EVENT_HSCROLL) {
|
|
1989 if (render_priv->state.scroll_direction == SCROLL_RL)
|
|
1990 device_x =
|
|
1991 x2scr(render_priv,
|
|
1992 render_priv->track->PlayResX -
|
|
1993 render_priv->state.scroll_shift);
|
|
1994 else if (render_priv->state.scroll_direction == SCROLL_LR)
|
|
1995 device_x =
|
|
1996 x2scr(render_priv,
|
|
1997 render_priv->state.scroll_shift) - (bbox.xMax -
|
|
1998 bbox.xMin);
|
|
1999 }
|
31853
|
2000
|
30200
|
2001 // y coordinate for everything except positioned events
|
|
2002 if (render_priv->state.evt_type == EVENT_NORMAL ||
|
|
2003 render_priv->state.evt_type == EVENT_HSCROLL) {
|
|
2004 if (valign == VALIGN_TOP) { // toptitle
|
|
2005 device_y =
|
|
2006 y2scr_top(render_priv,
|
|
2007 MarginV) + text_info->lines[0].asc;
|
|
2008 } else if (valign == VALIGN_CENTER) { // midtitle
|
|
2009 double scr_y =
|
|
2010 y2scr(render_priv, render_priv->track->PlayResY / 2.0);
|
|
2011 device_y = scr_y - (bbox.yMax + bbox.yMin) / 2.0;
|
|
2012 } else { // subtitle
|
|
2013 double scr_y;
|
|
2014 if (valign != VALIGN_SUB)
|
|
2015 ass_msg(render_priv->library, MSGL_V,
|
31853
|
2016 "Invalid valign, assuming 0 (subtitle)");
|
30200
|
2017 scr_y =
|
|
2018 y2scr_sub(render_priv,
|
|
2019 render_priv->track->PlayResY - MarginV);
|
|
2020 device_y = scr_y;
|
|
2021 device_y -= text_info->height;
|
|
2022 device_y += text_info->lines[0].asc;
|
|
2023 }
|
|
2024 } else if (render_priv->state.evt_type == EVENT_VSCROLL) {
|
|
2025 if (render_priv->state.scroll_direction == SCROLL_TB)
|
|
2026 device_y =
|
|
2027 y2scr(render_priv,
|
|
2028 render_priv->state.clip_y0 +
|
|
2029 render_priv->state.scroll_shift) - (bbox.yMax -
|
|
2030 bbox.yMin);
|
|
2031 else if (render_priv->state.scroll_direction == SCROLL_BT)
|
|
2032 device_y =
|
|
2033 y2scr(render_priv,
|
|
2034 render_priv->state.clip_y1 -
|
|
2035 render_priv->state.scroll_shift);
|
|
2036 }
|
31853
|
2037
|
30200
|
2038 // positioned events are totally different
|
|
2039 if (render_priv->state.evt_type == EVENT_POSITIONED) {
|
|
2040 double base_x = 0;
|
|
2041 double base_y = 0;
|
|
2042 ass_msg(render_priv->library, MSGL_DBG2, "positioned event at %f, %f",
|
|
2043 render_priv->state.pos_x, render_priv->state.pos_y);
|
|
2044 get_base_point(&bbox, alignment, &base_x, &base_y);
|
|
2045 device_x =
|
|
2046 x2scr_pos(render_priv, render_priv->state.pos_x) - base_x;
|
|
2047 device_y =
|
|
2048 y2scr_pos(render_priv, render_priv->state.pos_y) - base_y;
|
|
2049 }
|
31853
|
2050
|
30200
|
2051 // fix clip coordinates (they depend on alignment)
|
|
2052 if (render_priv->state.evt_type == EVENT_NORMAL ||
|
|
2053 render_priv->state.evt_type == EVENT_HSCROLL ||
|
|
2054 render_priv->state.evt_type == EVENT_VSCROLL) {
|
|
2055 render_priv->state.clip_x0 =
|
31853
|
2056 x2scr_scaled(render_priv, render_priv->state.clip_x0);
|
30200
|
2057 render_priv->state.clip_x1 =
|
31853
|
2058 x2scr_scaled(render_priv, render_priv->state.clip_x1);
|
30200
|
2059 if (valign == VALIGN_TOP) {
|
|
2060 render_priv->state.clip_y0 =
|
|
2061 y2scr_top(render_priv, render_priv->state.clip_y0);
|
|
2062 render_priv->state.clip_y1 =
|
|
2063 y2scr_top(render_priv, render_priv->state.clip_y1);
|
|
2064 } else if (valign == VALIGN_CENTER) {
|
|
2065 render_priv->state.clip_y0 =
|
|
2066 y2scr(render_priv, render_priv->state.clip_y0);
|
|
2067 render_priv->state.clip_y1 =
|
|
2068 y2scr(render_priv, render_priv->state.clip_y1);
|
|
2069 } else if (valign == VALIGN_SUB) {
|
|
2070 render_priv->state.clip_y0 =
|
|
2071 y2scr_sub(render_priv, render_priv->state.clip_y0);
|
|
2072 render_priv->state.clip_y1 =
|
|
2073 y2scr_sub(render_priv, render_priv->state.clip_y1);
|
|
2074 }
|
|
2075 } else if (render_priv->state.evt_type == EVENT_POSITIONED) {
|
|
2076 render_priv->state.clip_x0 =
|
31853
|
2077 x2scr_pos_scaled(render_priv, render_priv->state.clip_x0);
|
30200
|
2078 render_priv->state.clip_x1 =
|
31853
|
2079 x2scr_pos_scaled(render_priv, render_priv->state.clip_x1);
|
30200
|
2080 render_priv->state.clip_y0 =
|
|
2081 y2scr_pos(render_priv, render_priv->state.clip_y0);
|
|
2082 render_priv->state.clip_y1 =
|
|
2083 y2scr_pos(render_priv, render_priv->state.clip_y1);
|
|
2084 }
|
31853
|
2085
|
30200
|
2086 // calculate rotation parameters
|
|
2087 {
|
|
2088 DVector center;
|
29383
|
2089
|
30200
|
2090 if (render_priv->state.have_origin) {
|
|
2091 center.x = x2scr(render_priv, render_priv->state.org_x);
|
|
2092 center.y = y2scr(render_priv, render_priv->state.org_y);
|
|
2093 } else {
|
|
2094 double bx = 0., by = 0.;
|
|
2095 get_base_point(&bbox, alignment, &bx, &by);
|
|
2096 center.x = device_x + bx;
|
|
2097 center.y = device_y + by;
|
|
2098 }
|
|
2099
|
|
2100 for (i = 0; i < text_info->length; ++i) {
|
31853
|
2101 GlyphInfo *info = glyphs + i;
|
29263
|
2102
|
30200
|
2103 if (info->hash_key.frx || info->hash_key.fry
|
|
2104 || info->hash_key.frz || info->hash_key.fax
|
|
2105 || info->hash_key.fay) {
|
|
2106 info->hash_key.shift_x = info->pos.x + double_to_d6(device_x - center.x);
|
|
2107 info->hash_key.shift_y =
|
|
2108 -(info->pos.y + double_to_d6(device_y - center.y));
|
|
2109 } else {
|
|
2110 info->hash_key.shift_x = 0;
|
|
2111 info->hash_key.shift_y = 0;
|
|
2112 }
|
|
2113 }
|
|
2114 }
|
18937
|
2115
|
30200
|
2116 // convert glyphs to bitmaps
|
31853
|
2117 device_x *= render_priv->font_scale_x;
|
30200
|
2118 for (i = 0; i < text_info->length; ++i) {
|
31853
|
2119 GlyphInfo *g = glyphs + i;
|
|
2120 g->pos.x *= render_priv->font_scale_x;
|
30200
|
2121 g->hash_key.advance.x =
|
|
2122 double_to_d6(device_x - (int) device_x +
|
|
2123 d6_to_double(g->pos.x & SUBPIXEL_MASK)) & ~SUBPIXEL_ACCURACY;
|
|
2124 g->hash_key.advance.y =
|
|
2125 double_to_d6(device_y - (int) device_y +
|
|
2126 d6_to_double(g->pos.y & SUBPIXEL_MASK)) & ~SUBPIXEL_ACCURACY;
|
31853
|
2127 get_bitmap_glyph(render_priv, glyphs + i);
|
30200
|
2128 }
|
18937
|
2129
|
30200
|
2130 memset(event_images, 0, sizeof(*event_images));
|
|
2131 event_images->top = device_y - text_info->lines[0].asc;
|
|
2132 event_images->height = text_info->height;
|
31853
|
2133 event_images->left =
|
|
2134 (device_x + bbox.xMin * render_priv->font_scale_x) + 0.5;
|
|
2135 event_images->width =
|
|
2136 (bbox.xMax - bbox.xMin) * render_priv->font_scale_x + 0.5;
|
30200
|
2137 event_images->detect_collisions = render_priv->state.detect_collisions;
|
|
2138 event_images->shift_direction = (valign == VALIGN_TOP) ? 1 : -1;
|
|
2139 event_images->event = event;
|
|
2140 event_images->imgs = render_text(render_priv, (int) device_x, (int) device_y);
|
23174
|
2141
|
30200
|
2142 free_render_context(render_priv);
|
18937
|
2143
|
30200
|
2144 return 0;
|
18937
|
2145 }
|
|
2146
|
21506
|
2147 /**
|
|
2148 * \brief deallocate image list
|
|
2149 * \param img list pointer
|
|
2150 */
|
31853
|
2151 void ass_free_images(ASS_Image *img)
|
21506
|
2152 {
|
30200
|
2153 while (img) {
|
|
2154 ASS_Image *next = img->next;
|
|
2155 free(img);
|
|
2156 img = next;
|
|
2157 }
|
21506
|
2158 }
|
|
2159
|
31853
|
2160 /**
|
|
2161 * \brief Check cache limits and reset cache if they are exceeded
|
|
2162 */
|
|
2163 static void check_cache_limits(ASS_Renderer *priv, CacheStore *cache)
|
20446
|
2164 {
|
31853
|
2165 if (cache->bitmap_cache->cache_size > cache->bitmap_max_size) {
|
|
2166 ass_msg(priv->library, MSGL_V,
|
|
2167 "Hitting hard bitmap cache limit (was: %ld bytes), "
|
|
2168 "resetting.", (long) cache->bitmap_cache->cache_size);
|
|
2169 cache->bitmap_cache = ass_bitmap_cache_reset(cache->bitmap_cache);
|
|
2170 cache->composite_cache = ass_composite_cache_reset(
|
|
2171 cache->composite_cache);
|
|
2172 ass_free_images(priv->prev_images_root);
|
|
2173 priv->prev_images_root = 0;
|
30200
|
2174 }
|
|
2175
|
31853
|
2176 if (cache->glyph_cache->count > cache->glyph_max
|
|
2177 || cache->glyph_cache->cache_size > cache->bitmap_max_size) {
|
|
2178 ass_msg(priv->library, MSGL_V,
|
|
2179 "Hitting hard glyph cache limit (was: %d glyphs, %ld bytes), "
|
|
2180 "resetting.",
|
|
2181 cache->glyph_cache->count, (long) cache->glyph_cache->cache_size);
|
|
2182 cache->glyph_cache = ass_glyph_cache_reset(cache->glyph_cache);
|
|
2183 }
|
26582
|
2184 }
|
|
2185
|
18937
|
2186 /**
|
|
2187 * \brief Start a new frame
|
|
2188 */
|
30200
|
2189 static int
|
|
2190 ass_start_frame(ASS_Renderer *render_priv, ASS_Track *track,
|
|
2191 long long now)
|
18937
|
2192 {
|
30200
|
2193 ASS_Settings *settings_priv = &render_priv->settings;
|
|
2194
|
|
2195 if (!render_priv->settings.frame_width
|
|
2196 && !render_priv->settings.frame_height)
|
|
2197 return 1; // library not initialized
|
|
2198
|
|
2199 if (render_priv->library != track->library)
|
|
2200 return 1;
|
|
2201
|
31853
|
2202 if (!render_priv->fontconfig_priv)
|
|
2203 return 1;
|
|
2204
|
30200
|
2205 free_list_clear(render_priv);
|
|
2206
|
|
2207 if (track->n_events == 0)
|
|
2208 return 1; // nothing to do
|
18937
|
2209
|
30200
|
2210 render_priv->track = track;
|
|
2211 render_priv->time = now;
|
29263
|
2212
|
30200
|
2213 ass_lazy_track_init(render_priv);
|
|
2214
|
|
2215 render_priv->font_scale = settings_priv->font_size_coeff *
|
|
2216 render_priv->orig_height / render_priv->track->PlayResY;
|
|
2217 if (render_priv->track->ScaledBorderAndShadow)
|
|
2218 render_priv->border_scale =
|
|
2219 ((double) render_priv->orig_height) /
|
|
2220 render_priv->track->PlayResY;
|
|
2221 else
|
|
2222 render_priv->border_scale = 1.;
|
|
2223
|
|
2224 // PAR correction
|
|
2225 render_priv->font_scale_x = render_priv->settings.aspect /
|
|
2226 render_priv->settings.storage_aspect;
|
|
2227
|
|
2228 render_priv->prev_images_root = render_priv->images_root;
|
|
2229 render_priv->images_root = 0;
|
18937
|
2230
|
31853
|
2231 check_cache_limits(render_priv, &render_priv->cache);
|
19825
|
2232
|
30200
|
2233 return 0;
|
18937
|
2234 }
|
|
2235
|
30200
|
2236 static int cmp_event_layer(const void *p1, const void *p2)
|
19638
|
2237 {
|
30200
|
2238 ASS_Event *e1 = ((EventImages *) p1)->event;
|
|
2239 ASS_Event *e2 = ((EventImages *) p2)->event;
|
|
2240 if (e1->Layer < e2->Layer)
|
|
2241 return -1;
|
|
2242 if (e1->Layer > e2->Layer)
|
|
2243 return 1;
|
|
2244 if (e1->ReadOrder < e2->ReadOrder)
|
|
2245 return -1;
|
|
2246 if (e1->ReadOrder > e2->ReadOrder)
|
|
2247 return 1;
|
|
2248 return 0;
|
19638
|
2249 }
|
|
2250
|
30200
|
2251 static ASS_RenderPriv *get_render_priv(ASS_Renderer *render_priv,
|
|
2252 ASS_Event *event)
|
18937
|
2253 {
|
30200
|
2254 if (!event->render_priv)
|
|
2255 event->render_priv = calloc(1, sizeof(ASS_RenderPriv));
|
|
2256 if (render_priv->render_id != event->render_priv->render_id) {
|
|
2257 memset(event->render_priv, 0, sizeof(ASS_RenderPriv));
|
|
2258 event->render_priv->render_id = render_priv->render_id;
|
|
2259 }
|
|
2260
|
|
2261 return event->render_priv;
|
19638
|
2262 }
|
|
2263
|
30200
|
2264 static int overlap(Segment *s1, Segment *s2)
|
19638
|
2265 {
|
30200
|
2266 if (s1->a >= s2->b || s2->a >= s1->b ||
|
|
2267 s1->ha >= s2->hb || s2->ha >= s1->hb)
|
|
2268 return 0;
|
|
2269 return 1;
|
19638
|
2270 }
|
|
2271
|
30200
|
2272 static int cmp_segment(const void *p1, const void *p2)
|
19638
|
2273 {
|
30200
|
2274 return ((Segment *) p1)->a - ((Segment *) p2)->a;
|
19638
|
2275 }
|
|
2276
|
30200
|
2277 static void
|
|
2278 shift_event(ASS_Renderer *render_priv, EventImages *ei, int shift)
|
19638
|
2279 {
|
30200
|
2280 ASS_Image *cur = ei->imgs;
|
|
2281 while (cur) {
|
|
2282 cur->dst_y += shift;
|
|
2283 // clip top and bottom
|
|
2284 if (cur->dst_y < 0) {
|
|
2285 int clip = -cur->dst_y;
|
|
2286 cur->h -= clip;
|
|
2287 cur->bitmap += clip * cur->stride;
|
|
2288 cur->dst_y = 0;
|
|
2289 }
|
|
2290 if (cur->dst_y + cur->h >= render_priv->height) {
|
|
2291 int clip = cur->dst_y + cur->h - render_priv->height;
|
|
2292 cur->h -= clip;
|
|
2293 }
|
|
2294 if (cur->h <= 0) {
|
|
2295 cur->h = 0;
|
|
2296 cur->dst_y = 0;
|
|
2297 }
|
|
2298 cur = cur->next;
|
|
2299 }
|
|
2300 ei->top += shift;
|
19638
|
2301 }
|
|
2302
|
|
2303 // dir: 1 - move down
|
|
2304 // -1 - move up
|
30200
|
2305 static int fit_segment(Segment *s, Segment *fixed, int *cnt, int dir)
|
19638
|
2306 {
|
30200
|
2307 int i;
|
|
2308 int shift = 0;
|
19638
|
2309
|
30200
|
2310 if (dir == 1) // move down
|
|
2311 for (i = 0; i < *cnt; ++i) {
|
|
2312 if (s->b + shift <= fixed[i].a || s->a + shift >= fixed[i].b ||
|
|
2313 s->hb <= fixed[i].ha || s->ha >= fixed[i].hb)
|
|
2314 continue;
|
|
2315 shift = fixed[i].b - s->a;
|
|
2316 } else // dir == -1, move up
|
|
2317 for (i = *cnt - 1; i >= 0; --i) {
|
|
2318 if (s->b + shift <= fixed[i].a || s->a + shift >= fixed[i].b ||
|
|
2319 s->hb <= fixed[i].ha || s->ha >= fixed[i].hb)
|
|
2320 continue;
|
|
2321 shift = fixed[i].a - s->b;
|
|
2322 }
|
21097
|
2323
|
30200
|
2324 fixed[*cnt].a = s->a + shift;
|
|
2325 fixed[*cnt].b = s->b + shift;
|
|
2326 fixed[*cnt].ha = s->ha;
|
|
2327 fixed[*cnt].hb = s->hb;
|
|
2328 (*cnt)++;
|
|
2329 qsort(fixed, *cnt, sizeof(Segment), cmp_segment);
|
29263
|
2330
|
30200
|
2331 return shift;
|
18937
|
2332 }
|
19638
|
2333
|
30200
|
2334 static void
|
|
2335 fix_collisions(ASS_Renderer *render_priv, EventImages *imgs, int cnt)
|
19638
|
2336 {
|
30200
|
2337 Segment *used = malloc(cnt * sizeof(*used));
|
|
2338 int cnt_used = 0;
|
|
2339 int i, j;
|
19638
|
2340
|
30200
|
2341 // fill used[] with fixed events
|
|
2342 for (i = 0; i < cnt; ++i) {
|
|
2343 ASS_RenderPriv *priv;
|
|
2344 if (!imgs[i].detect_collisions)
|
|
2345 continue;
|
|
2346 priv = get_render_priv(render_priv, imgs[i].event);
|
|
2347 if (priv->height > 0) { // it's a fixed event
|
|
2348 Segment s;
|
|
2349 s.a = priv->top;
|
|
2350 s.b = priv->top + priv->height;
|
|
2351 s.ha = priv->left;
|
|
2352 s.hb = priv->left + priv->width;
|
|
2353 if (priv->height != imgs[i].height) { // no, it's not
|
|
2354 ass_msg(render_priv->library, MSGL_WARN,
|
31853
|
2355 "Event height has changed");
|
30200
|
2356 priv->top = 0;
|
|
2357 priv->height = 0;
|
|
2358 priv->left = 0;
|
|
2359 priv->width = 0;
|
|
2360 }
|
|
2361 for (j = 0; j < cnt_used; ++j)
|
|
2362 if (overlap(&s, used + j)) { // no, it's not
|
|
2363 priv->top = 0;
|
|
2364 priv->height = 0;
|
|
2365 priv->left = 0;
|
|
2366 priv->width = 0;
|
|
2367 }
|
|
2368 if (priv->height > 0) { // still a fixed event
|
|
2369 used[cnt_used].a = priv->top;
|
|
2370 used[cnt_used].b = priv->top + priv->height;
|
|
2371 used[cnt_used].ha = priv->left;
|
|
2372 used[cnt_used].hb = priv->left + priv->width;
|
|
2373 cnt_used++;
|
|
2374 shift_event(render_priv, imgs + i, priv->top - imgs[i].top);
|
|
2375 }
|
|
2376 }
|
|
2377 }
|
|
2378 qsort(used, cnt_used, sizeof(Segment), cmp_segment);
|
19638
|
2379
|
30200
|
2380 // try to fit other events in free spaces
|
|
2381 for (i = 0; i < cnt; ++i) {
|
|
2382 ASS_RenderPriv *priv;
|
|
2383 if (!imgs[i].detect_collisions)
|
|
2384 continue;
|
|
2385 priv = get_render_priv(render_priv, imgs[i].event);
|
|
2386 if (priv->height == 0) { // not a fixed event
|
|
2387 int shift;
|
|
2388 Segment s;
|
|
2389 s.a = imgs[i].top;
|
|
2390 s.b = imgs[i].top + imgs[i].height;
|
|
2391 s.ha = imgs[i].left;
|
|
2392 s.hb = imgs[i].left + imgs[i].width;
|
|
2393 shift = fit_segment(&s, used, &cnt_used, imgs[i].shift_direction);
|
|
2394 if (shift)
|
|
2395 shift_event(render_priv, imgs + i, shift);
|
|
2396 // make it fixed
|
|
2397 priv->top = imgs[i].top;
|
|
2398 priv->height = imgs[i].height;
|
|
2399 priv->left = imgs[i].left;
|
|
2400 priv->width = imgs[i].width;
|
|
2401 }
|
29263
|
2402
|
30200
|
2403 }
|
|
2404
|
|
2405 free(used);
|
19638
|
2406 }
|
|
2407
|
18937
|
2408 /**
|
21506
|
2409 * \brief compare two images
|
|
2410 * \param i1 first image
|
|
2411 * \param i2 second image
|
|
2412 * \return 0 if identical, 1 if different positions, 2 if different content
|
|
2413 */
|
30200
|
2414 static int ass_image_compare(ASS_Image *i1, ASS_Image *i2)
|
21506
|
2415 {
|
30200
|
2416 if (i1->w != i2->w)
|
|
2417 return 2;
|
|
2418 if (i1->h != i2->h)
|
|
2419 return 2;
|
|
2420 if (i1->stride != i2->stride)
|
|
2421 return 2;
|
|
2422 if (i1->color != i2->color)
|
|
2423 return 2;
|
|
2424 if (i1->bitmap != i2->bitmap)
|
|
2425 return 2;
|
|
2426 if (i1->dst_x != i2->dst_x)
|
|
2427 return 1;
|
|
2428 if (i1->dst_y != i2->dst_y)
|
|
2429 return 1;
|
|
2430 return 0;
|
21506
|
2431 }
|
|
2432
|
|
2433 /**
|
|
2434 * \brief compare current and previous image list
|
|
2435 * \param priv library handle
|
|
2436 * \return 0 if identical, 1 if different positions, 2 if different content
|
|
2437 */
|
30200
|
2438 static int ass_detect_change(ASS_Renderer *priv)
|
21506
|
2439 {
|
30200
|
2440 ASS_Image *img, *img2;
|
|
2441 int diff;
|
21506
|
2442
|
30200
|
2443 img = priv->prev_images_root;
|
|
2444 img2 = priv->images_root;
|
|
2445 diff = 0;
|
|
2446 while (img && diff < 2) {
|
|
2447 ASS_Image *next, *next2;
|
|
2448 next = img->next;
|
|
2449 if (img2) {
|
|
2450 int d = ass_image_compare(img, img2);
|
|
2451 if (d > diff)
|
|
2452 diff = d;
|
|
2453 next2 = img2->next;
|
|
2454 } else {
|
|
2455 // previous list is shorter
|
|
2456 diff = 2;
|
|
2457 break;
|
|
2458 }
|
|
2459 img = next;
|
|
2460 img2 = next2;
|
|
2461 }
|
21506
|
2462
|
30200
|
2463 // is the previous list longer?
|
|
2464 if (img2)
|
|
2465 diff = 2;
|
21506
|
2466
|
30200
|
2467 return diff;
|
21506
|
2468 }
|
|
2469
|
|
2470 /**
|
18937
|
2471 * \brief render a frame
|
|
2472 * \param priv library handle
|
|
2473 * \param track track
|
|
2474 * \param now current video timestamp (ms)
|
21506
|
2475 * \param detect_change a value describing how the new images differ from the previous ones will be written here:
|
|
2476 * 0 if identical, 1 if different positions, 2 if different content.
|
|
2477 * Can be NULL, in that case no detection is performed.
|
18937
|
2478 */
|
30200
|
2479 ASS_Image *ass_render_frame(ASS_Renderer *priv, ASS_Track *track,
|
|
2480 long long now, int *detect_change)
|
18937
|
2481 {
|
30200
|
2482 int i, cnt, rc;
|
|
2483 EventImages *last;
|
|
2484 ASS_Image **tail;
|
29263
|
2485
|
30200
|
2486 // init frame
|
|
2487 rc = ass_start_frame(priv, track, now);
|
|
2488 if (rc != 0)
|
|
2489 return 0;
|
19638
|
2490
|
30200
|
2491 // render events separately
|
|
2492 cnt = 0;
|
|
2493 for (i = 0; i < track->n_events; ++i) {
|
|
2494 ASS_Event *event = track->events + i;
|
|
2495 if ((event->Start <= now)
|
|
2496 && (now < (event->Start + event->Duration))) {
|
|
2497 if (cnt >= priv->eimg_size) {
|
|
2498 priv->eimg_size += 100;
|
|
2499 priv->eimg =
|
|
2500 realloc(priv->eimg,
|
|
2501 priv->eimg_size * sizeof(EventImages));
|
|
2502 }
|
|
2503 rc = ass_render_event(priv, event, priv->eimg + cnt);
|
|
2504 if (!rc)
|
|
2505 ++cnt;
|
|
2506 }
|
|
2507 }
|
19638
|
2508
|
30200
|
2509 // sort by layer
|
|
2510 qsort(priv->eimg, cnt, sizeof(EventImages), cmp_event_layer);
|
|
2511
|
|
2512 // call fix_collisions for each group of events with the same layer
|
|
2513 last = priv->eimg;
|
|
2514 for (i = 1; i < cnt; ++i)
|
|
2515 if (last->event->Layer != priv->eimg[i].event->Layer) {
|
|
2516 fix_collisions(priv, last, priv->eimg + i - last);
|
|
2517 last = priv->eimg + i;
|
|
2518 }
|
|
2519 if (cnt > 0)
|
|
2520 fix_collisions(priv, last, priv->eimg + cnt - last);
|
19638
|
2521
|
30200
|
2522 // concat lists
|
|
2523 tail = &priv->images_root;
|
|
2524 for (i = 0; i < cnt; ++i) {
|
|
2525 ASS_Image *cur = priv->eimg[i].imgs;
|
|
2526 while (cur) {
|
|
2527 *tail = cur;
|
|
2528 tail = &cur->next;
|
|
2529 cur = cur->next;
|
|
2530 }
|
|
2531 }
|
21506
|
2532
|
30200
|
2533 if (detect_change)
|
|
2534 *detect_change = ass_detect_change(priv);
|
29263
|
2535
|
30200
|
2536 // free the previous image list
|
|
2537 ass_free_images(priv->prev_images_root);
|
|
2538 priv->prev_images_root = 0;
|
21506
|
2539
|
30200
|
2540 return priv->images_root;
|
18937
|
2541 }
|