Mercurial > mplayer.hg
annotate libass/ass_render.c @ 31441:6f152da62242
Reorder #includes and drop an unnecessary set of #ifdefs.
author | diego |
---|---|
date | Mon, 21 Jun 2010 10:20:17 +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 } |