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