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