]> granicus.if.org Git - libass/blob - libass/ass_render.c
1369a828a28a07a2410e9784675685d9aabf8fd1
[libass] / libass / ass_render.c
1 /*
2  * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
3  *
4  * This file is part of libass.
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 #include "config.h"
20 #include "ass_compat.h"
21
22 #include <assert.h>
23 #include <math.h>
24 #include <string.h>
25 #include <stdbool.h>
26
27 #include "ass_outline.h"
28 #include "ass_render.h"
29 #include "ass_parse.h"
30 #include "ass_shaper.h"
31
32 #define MAX_GLYPHS_INITIAL 1024
33 #define MAX_LINES_INITIAL 64
34 #define MAX_BITMAPS_INITIAL 16
35 #define MAX_SUB_BITMAPS_INITIAL 64
36 #define SUBPIXEL_MASK 63
37 #define SUBPIXEL_ACCURACY 7
38
39
40 ASS_Renderer *ass_renderer_init(ASS_Library *library)
41 {
42     int error;
43     FT_Library ft;
44     ASS_Renderer *priv = 0;
45     int vmajor, vminor, vpatch;
46
47     error = FT_Init_FreeType(&ft);
48     if (error) {
49         ass_msg(library, MSGL_FATAL, "%s failed", "FT_Init_FreeType");
50         goto ass_init_exit;
51     }
52
53     FT_Library_Version(ft, &vmajor, &vminor, &vpatch);
54     ass_msg(library, MSGL_V, "Raster: FreeType %d.%d.%d",
55            vmajor, vminor, vpatch);
56
57     priv = calloc(1, sizeof(ASS_Renderer));
58     if (!priv) {
59         FT_Done_FreeType(ft);
60         goto ass_init_exit;
61     }
62
63     priv->library = library;
64     priv->ftlibrary = ft;
65     // images_root and related stuff is zero-filled in calloc
66
67 #if (defined(__i386__) || defined(__x86_64__)) && CONFIG_ASM
68     if (has_avx2())
69         priv->engine = &ass_bitmap_engine_avx2;
70     else if (has_sse2())
71         priv->engine = &ass_bitmap_engine_sse2;
72     else
73         priv->engine = &ass_bitmap_engine_c;
74 #else
75     priv->engine = &ass_bitmap_engine_c;
76 #endif
77
78     if (!rasterizer_init(&priv->rasterizer, priv->engine->tile_order, 16)) {
79         FT_Done_FreeType(ft);
80         goto ass_init_exit;
81     }
82
83     priv->cache.font_cache = ass_font_cache_create();
84     priv->cache.bitmap_cache = ass_bitmap_cache_create();
85     priv->cache.composite_cache = ass_composite_cache_create();
86     priv->cache.outline_cache = ass_outline_cache_create();
87     priv->cache.glyph_max = GLYPH_CACHE_MAX;
88     priv->cache.bitmap_max_size = BITMAP_CACHE_MAX_SIZE;
89     priv->cache.composite_max_size = COMPOSITE_CACHE_MAX_SIZE;
90
91     priv->text_info.max_bitmaps = MAX_BITMAPS_INITIAL;
92     priv->text_info.max_glyphs = MAX_GLYPHS_INITIAL;
93     priv->text_info.max_lines = MAX_LINES_INITIAL;
94     priv->text_info.n_bitmaps = 0;
95     priv->text_info.combined_bitmaps = calloc(MAX_BITMAPS_INITIAL, sizeof(CombinedBitmapInfo));
96     priv->text_info.glyphs = calloc(MAX_GLYPHS_INITIAL, sizeof(GlyphInfo));
97     priv->text_info.lines = calloc(MAX_LINES_INITIAL, sizeof(LineInfo));
98
99     priv->settings.font_size_coeff = 1.;
100     priv->settings.selective_style_overrides = ASS_OVERRIDE_BIT_SELECTIVE_FONT_SCALE;
101
102     priv->shaper = ass_shaper_new(0);
103     ass_shaper_info(library);
104 #ifdef CONFIG_HARFBUZZ
105     priv->settings.shaper = ASS_SHAPING_COMPLEX;
106 #else
107     priv->settings.shaper = ASS_SHAPING_SIMPLE;
108 #endif
109
110   ass_init_exit:
111     if (priv)
112         ass_msg(library, MSGL_V, "Initialized");
113     else
114         ass_msg(library, MSGL_ERR, "Initialization failed");
115
116     return priv;
117 }
118
119 void ass_renderer_done(ASS_Renderer *render_priv)
120 {
121     ass_frame_unref(render_priv->images_root);
122     ass_frame_unref(render_priv->prev_images_root);
123
124     ass_cache_done(render_priv->cache.composite_cache);
125     ass_cache_done(render_priv->cache.bitmap_cache);
126     ass_cache_done(render_priv->cache.outline_cache);
127     ass_shaper_free(render_priv->shaper);
128     ass_cache_done(render_priv->cache.font_cache);
129
130     rasterizer_done(&render_priv->rasterizer);
131
132     if (render_priv->fontselect)
133         ass_fontselect_free(render_priv->fontselect);
134     if (render_priv->ftlibrary)
135         FT_Done_FreeType(render_priv->ftlibrary);
136     free(render_priv->eimg);
137     free(render_priv->text_info.glyphs);
138     free(render_priv->text_info.lines);
139
140     free(render_priv->text_info.combined_bitmaps);
141
142     free(render_priv->settings.default_font);
143     free(render_priv->settings.default_family);
144
145     free(render_priv->user_override_style.FontName);
146
147     free(render_priv);
148 }
149
150 /**
151  * \brief Create a new ASS_Image
152  * Parameters are the same as ASS_Image fields.
153  */
154 static ASS_Image *my_draw_bitmap(unsigned char *bitmap, int bitmap_w,
155                                  int bitmap_h, int stride, int dst_x,
156                                  int dst_y, uint32_t color,
157                                  CompositeHashValue *source)
158 {
159     ASS_ImagePriv *img = malloc(sizeof(ASS_ImagePriv));
160     if (!img) {
161         if (!source)
162             ass_aligned_free(bitmap);
163         return NULL;
164     }
165
166     img->result.w = bitmap_w;
167     img->result.h = bitmap_h;
168     img->result.stride = stride;
169     img->result.bitmap = bitmap;
170     img->result.color = color;
171     img->result.dst_x = dst_x;
172     img->result.dst_y = dst_y;
173
174     img->source = source;
175     ass_cache_inc_ref(source);
176     img->ref_count = 0;
177
178     return &img->result;
179 }
180
181 /**
182  * \brief Mapping between script and screen coordinates
183  */
184 static double x2scr_pos(ASS_Renderer *render_priv, double x)
185 {
186     return x * render_priv->orig_width / render_priv->font_scale_x / render_priv->track->PlayResX +
187         render_priv->settings.left_margin;
188 }
189 static double x2scr(ASS_Renderer *render_priv, double x)
190 {
191     if (render_priv->state.explicit)
192         return x2scr_pos(render_priv, x);
193     return x * render_priv->orig_width_nocrop / render_priv->font_scale_x /
194         render_priv->track->PlayResX +
195         FFMAX(render_priv->settings.left_margin, 0);
196 }
197 static double x2scr_pos_scaled(ASS_Renderer *render_priv, double x)
198 {
199     return x * render_priv->orig_width / render_priv->track->PlayResX +
200         render_priv->settings.left_margin;
201 }
202 static double x2scr_scaled(ASS_Renderer *render_priv, double x)
203 {
204     if (render_priv->state.explicit)
205         return x2scr_pos_scaled(render_priv, x);
206     return x * render_priv->orig_width_nocrop /
207         render_priv->track->PlayResX +
208         FFMAX(render_priv->settings.left_margin, 0);
209 }
210 /**
211  * \brief Mapping between script and screen coordinates
212  */
213 static double y2scr_pos(ASS_Renderer *render_priv, double y)
214 {
215     return y * render_priv->orig_height / render_priv->track->PlayResY +
216         render_priv->settings.top_margin;
217 }
218 static double y2scr(ASS_Renderer *render_priv, double y)
219 {
220     if (render_priv->state.explicit)
221         return y2scr_pos(render_priv, y);
222     return y * render_priv->orig_height_nocrop /
223         render_priv->track->PlayResY +
224         FFMAX(render_priv->settings.top_margin, 0);
225 }
226
227 // the same for toptitles
228 static double y2scr_top(ASS_Renderer *render_priv, double y)
229 {
230     if (render_priv->state.explicit)
231         return y2scr_pos(render_priv, y);
232     if (render_priv->settings.use_margins)
233         return y * render_priv->orig_height_nocrop /
234             render_priv->track->PlayResY;
235     else
236         return y * render_priv->orig_height_nocrop /
237             render_priv->track->PlayResY +
238             FFMAX(render_priv->settings.top_margin, 0);
239 }
240 // the same for subtitles
241 static double y2scr_sub(ASS_Renderer *render_priv, double y)
242 {
243     if (render_priv->state.explicit)
244         return y2scr_pos(render_priv, y);
245     if (render_priv->settings.use_margins)
246         return y * render_priv->orig_height_nocrop /
247             render_priv->track->PlayResY +
248             FFMAX(render_priv->settings.top_margin, 0)
249             + FFMAX(render_priv->settings.bottom_margin, 0);
250     else
251         return y * render_priv->orig_height_nocrop /
252             render_priv->track->PlayResY +
253             FFMAX(render_priv->settings.top_margin, 0);
254 }
255
256 /*
257  * \brief Convert bitmap glyphs into ASS_Image list with inverse clipping
258  *
259  * Inverse clipping with the following strategy:
260  * - find rectangle from (x0, y0) to (cx0, y1)
261  * - find rectangle from (cx0, y0) to (cx1, cy0)
262  * - find rectangle from (cx0, cy1) to (cx1, y1)
263  * - find rectangle from (cx1, y0) to (x1, y1)
264  * These rectangles can be invalid and in this case are discarded.
265  * Afterwards, they are clipped against the screen coordinates.
266  * In an additional pass, the rectangles need to be split up left/right for
267  * karaoke effects.  This can result in a lot of bitmaps (6 to be exact).
268  */
269 static ASS_Image **render_glyph_i(ASS_Renderer *render_priv,
270                                   Bitmap *bm, int dst_x, int dst_y,
271                                   uint32_t color, uint32_t color2, int brk,
272                                   ASS_Image **tail, unsigned type,
273                                   CompositeHashValue *source)
274 {
275     int i, j, x0, y0, x1, y1, cx0, cy0, cx1, cy1, sx, sy, zx, zy;
276     Rect r[4];
277     ASS_Image *img;
278
279     dst_x += bm->left;
280     dst_y += bm->top;
281
282     // we still need to clip against screen boundaries
283     zx = x2scr_pos_scaled(render_priv, 0);
284     zy = y2scr_pos(render_priv, 0);
285     sx = x2scr_pos_scaled(render_priv, render_priv->track->PlayResX);
286     sy = y2scr_pos(render_priv, render_priv->track->PlayResY);
287
288     x0 = 0;
289     y0 = 0;
290     x1 = bm->w;
291     y1 = bm->h;
292     cx0 = render_priv->state.clip_x0 - dst_x;
293     cy0 = render_priv->state.clip_y0 - dst_y;
294     cx1 = render_priv->state.clip_x1 - dst_x;
295     cy1 = render_priv->state.clip_y1 - dst_y;
296
297     // calculate rectangles and discard invalid ones while we're at it.
298     i = 0;
299     r[i].x0 = x0;
300     r[i].y0 = y0;
301     r[i].x1 = (cx0 > x1) ? x1 : cx0;
302     r[i].y1 = y1;
303     if (r[i].x1 > r[i].x0 && r[i].y1 > r[i].y0) i++;
304     r[i].x0 = (cx0 < 0) ? x0 : cx0;
305     r[i].y0 = y0;
306     r[i].x1 = (cx1 > x1) ? x1 : cx1;
307     r[i].y1 = (cy0 > y1) ? y1 : cy0;
308     if (r[i].x1 > r[i].x0 && r[i].y1 > r[i].y0) i++;
309     r[i].x0 = (cx0 < 0) ? x0 : cx0;
310     r[i].y0 = (cy1 < 0) ? y0 : cy1;
311     r[i].x1 = (cx1 > x1) ? x1 : cx1;
312     r[i].y1 = y1;
313     if (r[i].x1 > r[i].x0 && r[i].y1 > r[i].y0) i++;
314     r[i].x0 = (cx1 < 0) ? x0 : cx1;
315     r[i].y0 = y0;
316     r[i].x1 = x1;
317     r[i].y1 = y1;
318     if (r[i].x1 > r[i].x0 && r[i].y1 > r[i].y0) i++;
319
320     // clip each rectangle to screen coordinates
321     for (j = 0; j < i; j++) {
322         r[j].x0 = (r[j].x0 + dst_x < zx) ? zx - dst_x : r[j].x0;
323         r[j].y0 = (r[j].y0 + dst_y < zy) ? zy - dst_y : r[j].y0;
324         r[j].x1 = (r[j].x1 + dst_x > sx) ? sx - dst_x : r[j].x1;
325         r[j].y1 = (r[j].y1 + dst_y > sy) ? sy - dst_y : r[j].y1;
326     }
327
328     // draw the rectangles
329     for (j = 0; j < i; j++) {
330         int lbrk = brk;
331         // kick out rectangles that are invalid now
332         if (r[j].x1 <= r[j].x0 || r[j].y1 <= r[j].y0)
333             continue;
334         // split up into left and right for karaoke, if needed
335         if (lbrk > r[j].x0) {
336             if (lbrk > r[j].x1) lbrk = r[j].x1;
337             img = my_draw_bitmap(bm->buffer + r[j].y0 * bm->stride + r[j].x0,
338                                  lbrk - r[j].x0, r[j].y1 - r[j].y0, bm->stride,
339                                  dst_x + r[j].x0, dst_y + r[j].y0, color, source);
340             if (!img) break;
341             img->type = type;
342             *tail = img;
343             tail = &img->next;
344         }
345         if (lbrk < r[j].x1) {
346             if (lbrk < r[j].x0) lbrk = r[j].x0;
347             img = my_draw_bitmap(bm->buffer + r[j].y0 * bm->stride + lbrk,
348                                  r[j].x1 - lbrk, r[j].y1 - r[j].y0, bm->stride,
349                                  dst_x + lbrk, dst_y + r[j].y0, color2, source);
350             if (!img) break;
351             img->type = type;
352             *tail = img;
353             tail = &img->next;
354         }
355     }
356
357     return tail;
358 }
359
360 /**
361  * \brief convert bitmap glyph into ASS_Image struct(s)
362  * \param bit freetype bitmap glyph, FT_PIXEL_MODE_GRAY
363  * \param dst_x bitmap x coordinate in video frame
364  * \param dst_y bitmap y coordinate in video frame
365  * \param color first color, RGBA
366  * \param color2 second color, RGBA
367  * \param brk x coordinate relative to glyph origin, color is used to the left of brk, color2 - to the right
368  * \param tail pointer to the last image's next field, head of the generated list should be stored here
369  * \return pointer to the new list tail
370  * Performs clipping. Uses my_draw_bitmap for actual bitmap convertion.
371  */
372 static ASS_Image **
373 render_glyph(ASS_Renderer *render_priv, Bitmap *bm, int dst_x, int dst_y,
374              uint32_t color, uint32_t color2, int brk, ASS_Image **tail,
375              unsigned type, CompositeHashValue *source)
376 {
377     // Inverse clipping in use?
378     if (render_priv->state.clip_mode)
379         return render_glyph_i(render_priv, bm, dst_x, dst_y, color, color2,
380                               brk, tail, type, source);
381
382     // brk is relative to dst_x
383     // color = color left of brk
384     // color2 = color right of brk
385     int b_x0, b_y0, b_x1, b_y1; // visible part of the bitmap
386     int clip_x0, clip_y0, clip_x1, clip_y1;
387     int tmp;
388     ASS_Image *img;
389
390     dst_x += bm->left;
391     dst_y += bm->top;
392     brk -= bm->left;
393
394     // clipping
395     clip_x0 = FFMINMAX(render_priv->state.clip_x0, 0, render_priv->width);
396     clip_y0 = FFMINMAX(render_priv->state.clip_y0, 0, render_priv->height);
397     clip_x1 = FFMINMAX(render_priv->state.clip_x1, 0, render_priv->width);
398     clip_y1 = FFMINMAX(render_priv->state.clip_y1, 0, render_priv->height);
399     b_x0 = 0;
400     b_y0 = 0;
401     b_x1 = bm->w;
402     b_y1 = bm->h;
403
404     tmp = dst_x - clip_x0;
405     if (tmp < 0)
406         b_x0 = -tmp;
407     tmp = dst_y - clip_y0;
408     if (tmp < 0)
409         b_y0 = -tmp;
410     tmp = clip_x1 - dst_x - bm->w;
411     if (tmp < 0)
412         b_x1 = bm->w + tmp;
413     tmp = clip_y1 - dst_y - bm->h;
414     if (tmp < 0)
415         b_y1 = bm->h + tmp;
416
417     if ((b_y0 >= b_y1) || (b_x0 >= b_x1))
418         return tail;
419
420     if (brk > b_x0) {           // draw left part
421         if (brk > b_x1)
422             brk = b_x1;
423         img = my_draw_bitmap(bm->buffer + bm->stride * b_y0 + b_x0,
424                              brk - b_x0, b_y1 - b_y0, bm->stride,
425                              dst_x + b_x0, dst_y + b_y0, color, source);
426         if (!img) return tail;
427         img->type = type;
428         *tail = img;
429         tail = &img->next;
430     }
431     if (brk < b_x1) {           // draw right part
432         if (brk < b_x0)
433             brk = b_x0;
434         img = my_draw_bitmap(bm->buffer + bm->stride * b_y0 + brk,
435                              b_x1 - brk, b_y1 - b_y0, bm->stride,
436                              dst_x + brk, dst_y + b_y0, color2, source);
437         if (!img) return tail;
438         img->type = type;
439         *tail = img;
440         tail = &img->next;
441     }
442     return tail;
443 }
444
445 // Calculate bitmap memory footprint
446 static inline size_t bitmap_size(Bitmap *bm)
447 {
448     return bm ? sizeof(Bitmap) + bm->stride * bm->h : 0;
449 }
450
451 /**
452  * Iterate through a list of bitmaps and blend with clip vector, if
453  * applicable. The blended bitmaps are added to a free list which is freed
454  * at the start of a new frame.
455  */
456 static void blend_vector_clip(ASS_Renderer *render_priv,
457                               ASS_Image *head)
458 {
459     ASS_Drawing *drawing = render_priv->state.clip_drawing;
460     if (!drawing)
461         return;
462
463     // Try to get mask from cache
464     BitmapHashKey key;
465     memset(&key, 0, sizeof(key));
466     key.type = BITMAP_CLIP;
467     key.u.clip.text = drawing->text;
468
469     BitmapHashValue *val;
470     if (!ass_cache_get(render_priv->cache.bitmap_cache, &key, &val)) {
471         if (!val)
472             return;
473         val->bm = val->bm_o = NULL;
474
475         // Not found in cache, parse and rasterize it
476         ASS_Outline *outline = ass_drawing_parse(drawing, 1);
477         if (!outline) {
478             ass_msg(render_priv->library, MSGL_WARN,
479                     "Clip vector parsing failed. Skipping.");
480             ass_cache_commit(val, sizeof(BitmapHashKey) + sizeof(BitmapHashValue));
481             ass_cache_dec_ref(val);
482             return;
483         }
484
485         // We need to translate the clip according to screen borders
486         if (render_priv->settings.left_margin != 0 ||
487             render_priv->settings.top_margin != 0) {
488             FT_Vector trans = {
489                 .x = int_to_d6(render_priv->settings.left_margin),
490                 .y = -int_to_d6(render_priv->settings.top_margin),
491             };
492             outline_translate(outline, trans.x, trans.y);
493         }
494
495         val->bm = outline_to_bitmap(render_priv, outline, NULL, 1);
496         ass_cache_commit(val, bitmap_size(val->bm) +
497                          sizeof(BitmapHashKey) + sizeof(BitmapHashValue));
498     }
499
500     Bitmap *clip_bm = val->bm;
501     if (!clip_bm) {
502         ass_cache_dec_ref(val);
503         return;
504     }
505
506     // Iterate through bitmaps and blend/clip them
507     for (ASS_Image *cur = head; cur; cur = cur->next) {
508         int left, top, right, bottom, w, h;
509         int ax, ay, aw, ah, as;
510         int bx, by, bw, bh, bs;
511         int aleft, atop, bleft, btop;
512         unsigned char *abuffer, *bbuffer, *nbuffer;
513
514         abuffer = cur->bitmap;
515         bbuffer = clip_bm->buffer;
516         ax = cur->dst_x;
517         ay = cur->dst_y;
518         aw = cur->w;
519         ah = cur->h;
520         as = cur->stride;
521         bx = clip_bm->left;
522         by = clip_bm->top;
523         bw = clip_bm->w;
524         bh = clip_bm->h;
525         bs = clip_bm->stride;
526
527         // Calculate overlap coordinates
528         left = (ax > bx) ? ax : bx;
529         top = (ay > by) ? ay : by;
530         right = ((ax + aw) < (bx + bw)) ? (ax + aw) : (bx + bw);
531         bottom = ((ay + ah) < (by + bh)) ? (ay + ah) : (by + bh);
532         aleft = left - ax;
533         atop = top - ay;
534         w = right - left;
535         h = bottom - top;
536         bleft = left - bx;
537         btop = top - by;
538
539         if (render_priv->state.clip_drawing_mode) {
540             // Inverse clip
541             if (ax + aw < bx || ay + ah < by || ax > bx + bw ||
542                 ay > by + bh || !h || !w) {
543                 continue;
544             }
545
546             // Allocate new buffer and add to free list
547             nbuffer = ass_aligned_alloc(32, as * ah, false);
548             if (!nbuffer)
549                 break;
550
551             // Blend together
552             memcpy(nbuffer, abuffer, ((ah - 1) * as) + aw);
553             render_priv->engine->sub_bitmaps(nbuffer + atop * as + aleft, as,
554                                              bbuffer + btop * bs + bleft, bs,
555                                              h, w);
556         } else {
557             // Regular clip
558             if (ax + aw < bx || ay + ah < by || ax > bx + bw ||
559                 ay > by + bh || !h || !w) {
560                 cur->w = cur->h = cur->stride = 0;
561                 continue;
562             }
563
564             // Allocate new buffer and add to free list
565             unsigned align = (w >= 16) ? 16 : ((w >= 8) ? 8 : 1);
566             unsigned ns = ass_align(align, w);
567             nbuffer = ass_aligned_alloc(align, ns * h, false);
568             if (!nbuffer)
569                 break;
570
571             // Blend together
572             render_priv->engine->mul_bitmaps(nbuffer, ns,
573                                              abuffer + atop * as + aleft, as,
574                                              bbuffer + btop * bs + bleft, bs,
575                                              w, h);
576             cur->dst_x += aleft;
577             cur->dst_y += atop;
578             cur->w = w;
579             cur->h = h;
580             cur->stride = ns;
581         }
582
583         cur->bitmap = nbuffer;
584         ASS_ImagePriv *priv = (ASS_ImagePriv *) cur;
585         ass_cache_dec_ref(priv->source);
586         priv->source = NULL;
587     }
588
589     ass_cache_dec_ref(val);
590 }
591
592 /**
593  * \brief Convert TextInfo struct to ASS_Image list
594  * Splits glyphs in halves when needed (for \kf karaoke).
595  */
596 static ASS_Image *render_text(ASS_Renderer *render_priv)
597 {
598     ASS_Image *head;
599     ASS_Image **tail = &head;
600     unsigned n_bitmaps = render_priv->text_info.n_bitmaps;
601     CombinedBitmapInfo *bitmaps = render_priv->text_info.combined_bitmaps;
602
603     for (unsigned i = 0; i < n_bitmaps; i++) {
604         CombinedBitmapInfo *info = &bitmaps[i];
605         if (!info->bm_s || render_priv->state.border_style == 4)
606             continue;
607
608         tail =
609             render_glyph(render_priv, info->bm_s, info->x, info->y, info->c[3], 0,
610                          1000000, tail, IMAGE_TYPE_SHADOW, info->image);
611     }
612
613     for (unsigned i = 0; i < n_bitmaps; i++) {
614         CombinedBitmapInfo *info = &bitmaps[i];
615         if (!info->bm_o)
616             continue;
617
618         if ((info->effect_type == EF_KARAOKE_KO)
619                 && (info->effect_timing <= info->first_pos_x)) {
620             // do nothing
621         } else {
622             tail =
623                 render_glyph(render_priv, info->bm_o, info->x, info->y, info->c[2],
624                              0, 1000000, tail, IMAGE_TYPE_OUTLINE, info->image);
625         }
626     }
627
628     for (unsigned i = 0; i < n_bitmaps; i++) {
629         CombinedBitmapInfo *info = &bitmaps[i];
630         if (!info->bm)
631             continue;
632
633         if ((info->effect_type == EF_KARAOKE)
634                 || (info->effect_type == EF_KARAOKE_KO)) {
635             if (info->effect_timing > info->first_pos_x)
636                 tail =
637                     render_glyph(render_priv, info->bm, info->x, info->y,
638                                  info->c[0], 0, 1000000, tail,
639                                  IMAGE_TYPE_CHARACTER, info->image);
640             else
641                 tail =
642                     render_glyph(render_priv, info->bm, info->x, info->y,
643                                  info->c[1], 0, 1000000, tail,
644                                  IMAGE_TYPE_CHARACTER, info->image);
645         } else if (info->effect_type == EF_KARAOKE_KF) {
646             tail =
647                 render_glyph(render_priv, info->bm, info->x, info->y, info->c[0],
648                              info->c[1], info->effect_timing, tail,
649                              IMAGE_TYPE_CHARACTER, info->image);
650         } else
651             tail =
652                 render_glyph(render_priv, info->bm, info->x, info->y, info->c[0],
653                              0, 1000000, tail, IMAGE_TYPE_CHARACTER, info->image);
654     }
655
656     for (unsigned i = 0; i < n_bitmaps; i++)
657         ass_cache_dec_ref(bitmaps[i].image);
658
659     *tail = 0;
660     blend_vector_clip(render_priv, head);
661
662     return head;
663 }
664
665 static void compute_string_bbox(TextInfo *text, DBBox *bbox)
666 {
667     int i;
668
669     if (text->length > 0) {
670         bbox->xMin = 32000;
671         bbox->xMax = -32000;
672         bbox->yMin = -1 * text->lines[0].asc + d6_to_double(text->glyphs[0].pos.y);
673         bbox->yMax = text->height - text->lines[0].asc +
674                      d6_to_double(text->glyphs[0].pos.y);
675
676         for (i = 0; i < text->length; ++i) {
677             GlyphInfo *info = text->glyphs + i;
678             if (info->skip) continue;
679             double s = d6_to_double(info->pos.x);
680             double e = s + d6_to_double(info->cluster_advance.x);
681             bbox->xMin = FFMIN(bbox->xMin, s);
682             bbox->xMax = FFMAX(bbox->xMax, e);
683         }
684     } else
685         bbox->xMin = bbox->xMax = bbox->yMin = bbox->yMax = 0.;
686 }
687
688 static ASS_Style *handle_selective_style_overrides(ASS_Renderer *render_priv,
689                                                    ASS_Style *rstyle)
690 {
691     // The script style is the one the event was declared with.
692     ASS_Style *script = render_priv->track->styles +
693                         render_priv->state.event->Style;
694     // The user style was set with ass_set_selective_style_override().
695     ASS_Style *user = &render_priv->user_override_style;
696     ASS_Style *new = &render_priv->state.override_style_temp_storage;
697     int explicit = event_has_hard_overrides(render_priv->state.event->Text) ||
698                    render_priv->state.evt_type != EVENT_NORMAL;
699     int requested = render_priv->settings.selective_style_overrides;
700     double scale;
701
702     user->Name = "OverrideStyle"; // name insignificant
703
704     // Either the event's style, or the style forced with a \r tag.
705     if (!rstyle)
706         rstyle = script;
707
708     // Create a new style that contains a mix of the original style and
709     // user_style (the user's override style). Copy only fields from the
710     // script's style that are deemed necessary.
711     *new = *rstyle;
712
713     render_priv->state.explicit = explicit;
714
715     render_priv->state.apply_font_scale =
716         !explicit || !(requested & ASS_OVERRIDE_BIT_SELECTIVE_FONT_SCALE);
717
718     // On positioned events, do not apply most overrides.
719     if (explicit)
720         requested = 0;
721
722     if (requested & ASS_OVERRIDE_BIT_STYLE)
723         requested |= ASS_OVERRIDE_BIT_FONT_NAME |
724                      ASS_OVERRIDE_BIT_FONT_SIZE_FIELDS |
725                      ASS_OVERRIDE_BIT_COLORS |
726                      ASS_OVERRIDE_BIT_BORDER |
727                      ASS_OVERRIDE_BIT_ATTRIBUTES;
728
729     // Copies fields even not covered by any of the other bits.
730     if (requested & ASS_OVERRIDE_FULL_STYLE)
731         *new = *user;
732
733     // The user style is supposed to be independent of the script resolution.
734     // Treat the user style's values as if they were specified for a script with
735     // PlayResY=288, and rescale the values to the current script.
736     scale = render_priv->track->PlayResY / 288.0;
737
738     if (requested & ASS_OVERRIDE_BIT_FONT_SIZE_FIELDS) {
739         new->FontSize = user->FontSize * scale;
740         new->Spacing = user->Spacing * scale;
741         new->ScaleX = user->ScaleX;
742         new->ScaleY = user->ScaleY;
743     }
744
745     if (requested & ASS_OVERRIDE_BIT_FONT_NAME) {
746         new->FontName = user->FontName;
747         new->treat_fontname_as_pattern = user->treat_fontname_as_pattern;
748     }
749
750     if (requested & ASS_OVERRIDE_BIT_COLORS) {
751         new->PrimaryColour = user->PrimaryColour;
752         new->SecondaryColour = user->SecondaryColour;
753         new->OutlineColour = user->OutlineColour;
754         new->BackColour = user->BackColour;
755     }
756
757     if (requested & ASS_OVERRIDE_BIT_ATTRIBUTES) {
758         new->Bold = user->Bold;
759         new->Italic = user->Italic;
760         new->Underline = user->Underline;
761         new->StrikeOut = user->StrikeOut;
762     }
763
764     if (requested & ASS_OVERRIDE_BIT_BORDER) {
765         new->BorderStyle = user->BorderStyle;
766         new->Outline = user->Outline * scale;
767         new->Shadow = user->Shadow * scale;
768     }
769
770     if (requested & ASS_OVERRIDE_BIT_ALIGNMENT)
771         new->Alignment = user->Alignment;
772
773     if (requested & ASS_OVERRIDE_BIT_JUSTIFY)
774         new->Justify = user->Justify;
775
776     if (requested & ASS_OVERRIDE_BIT_MARGINS) {
777         new->MarginL = user->MarginL;
778         new->MarginR = user->MarginR;
779         new->MarginV = user->MarginV;
780     }
781
782     if (!new->FontName)
783         new->FontName = rstyle->FontName;
784
785     render_priv->state.style = new;
786     render_priv->state.overrides = requested;
787
788     return new;
789 }
790
791 static void init_font_scale(ASS_Renderer *render_priv)
792 {
793     ASS_Settings *settings_priv = &render_priv->settings;
794
795     render_priv->font_scale = ((double) render_priv->orig_height) /
796                               render_priv->track->PlayResY;
797     if (settings_priv->storage_height)
798         render_priv->blur_scale = ((double) render_priv->orig_height) /
799             settings_priv->storage_height;
800     else
801         render_priv->blur_scale = 1.;
802     if (render_priv->track->ScaledBorderAndShadow)
803         render_priv->border_scale =
804             ((double) render_priv->orig_height) /
805             render_priv->track->PlayResY;
806     else
807         render_priv->border_scale = render_priv->blur_scale;
808     if (!settings_priv->storage_height)
809         render_priv->blur_scale = render_priv->border_scale;
810
811     if (render_priv->state.apply_font_scale) {
812         render_priv->font_scale *= settings_priv->font_size_coeff;
813         render_priv->border_scale *= settings_priv->font_size_coeff;
814         render_priv->blur_scale *= settings_priv->font_size_coeff;
815     }
816 }
817
818 /**
819  * \brief partially reset render_context to style values
820  * Works like {\r}: resets some style overrides
821  */
822 void reset_render_context(ASS_Renderer *render_priv, ASS_Style *style)
823 {
824     style = handle_selective_style_overrides(render_priv, style);
825
826     init_font_scale(render_priv);
827
828     render_priv->state.c[0] = style->PrimaryColour;
829     render_priv->state.c[1] = style->SecondaryColour;
830     render_priv->state.c[2] = style->OutlineColour;
831     render_priv->state.c[3] = style->BackColour;
832     render_priv->state.flags =
833         (style->Underline ? DECO_UNDERLINE : 0) |
834         (style->StrikeOut ? DECO_STRIKETHROUGH : 0);
835     render_priv->state.font_size = style->FontSize;
836
837     free(render_priv->state.family);
838     render_priv->state.family = NULL;
839     render_priv->state.family = strdup(style->FontName);
840     render_priv->state.treat_family_as_pattern =
841         style->treat_fontname_as_pattern;
842     render_priv->state.bold = style->Bold;
843     render_priv->state.italic = style->Italic;
844     update_font(render_priv);
845
846     render_priv->state.border_style = style->BorderStyle;
847     render_priv->state.border_x = style->Outline;
848     render_priv->state.border_y = style->Outline;
849     render_priv->state.scale_x = style->ScaleX;
850     render_priv->state.scale_y = style->ScaleY;
851     render_priv->state.hspacing = style->Spacing;
852     render_priv->state.be = 0;
853     render_priv->state.blur = style->Blur;
854     render_priv->state.shadow_x = style->Shadow;
855     render_priv->state.shadow_y = style->Shadow;
856     render_priv->state.frx = render_priv->state.fry = 0.;
857     render_priv->state.frz = M_PI * style->Angle / 180.;
858     render_priv->state.fax = render_priv->state.fay = 0.;
859     render_priv->state.font_encoding = style->Encoding;
860 }
861
862 /**
863  * \brief Start new event. Reset render_priv->state.
864  */
865 static void
866 init_render_context(ASS_Renderer *render_priv, ASS_Event *event)
867 {
868     render_priv->state.event = event;
869     render_priv->state.parsed_tags = 0;
870     render_priv->state.evt_type = EVENT_NORMAL;
871
872     reset_render_context(render_priv, NULL);
873     render_priv->state.wrap_style = render_priv->track->WrapStyle;
874
875     render_priv->state.alignment = render_priv->state.style->Alignment;
876     render_priv->state.justify = render_priv->state.style->Justify;
877     render_priv->state.pos_x = 0;
878     render_priv->state.pos_y = 0;
879     render_priv->state.org_x = 0;
880     render_priv->state.org_y = 0;
881     render_priv->state.have_origin = 0;
882     render_priv->state.clip_x0 = 0;
883     render_priv->state.clip_y0 = 0;
884     render_priv->state.clip_x1 = render_priv->track->PlayResX;
885     render_priv->state.clip_y1 = render_priv->track->PlayResY;
886     render_priv->state.clip_mode = 0;
887     render_priv->state.detect_collisions = 1;
888     render_priv->state.fade = 0;
889     render_priv->state.drawing_scale = 0;
890     render_priv->state.pbo = 0;
891     render_priv->state.effect_type = EF_NONE;
892     render_priv->state.effect_timing = 0;
893     render_priv->state.effect_skip_timing = 0;
894
895     apply_transition_effects(render_priv, event);
896 }
897
898 static void free_render_context(ASS_Renderer *render_priv)
899 {
900     ass_cache_dec_ref(render_priv->state.font);
901     free(render_priv->state.family);
902     ass_drawing_free(render_priv->state.clip_drawing);
903
904     render_priv->state.font = NULL;
905     render_priv->state.family = NULL;
906     render_priv->state.clip_drawing = NULL;
907
908     TextInfo *text_info = &render_priv->text_info;
909     for (int n = 0; n < text_info->length; n++)
910         ass_drawing_free(text_info->glyphs[n].drawing);
911     text_info->length = 0;
912 }
913
914 /*
915  * Replace the outline of a glyph by a contour which makes up a simple
916  * opaque rectangle.
917  */
918 static void draw_opaque_box(ASS_Renderer *render_priv, GlyphInfo *info,
919                             int asc, int desc, ASS_Outline *ol,
920                             FT_Vector advance, int sx, int sy)
921 {
922     int adv = advance.x;
923     double scale_y = info->orig_scale_y;
924     double scale_x = info->orig_scale_x;
925
926     // to avoid gaps
927     sx = FFMAX(64, sx);
928     sy = FFMAX(64, sy);
929
930     // Emulate the WTFish behavior of VSFilter, i.e. double-scale
931     // the sizes of the opaque box.
932     adv += double_to_d6(info->hspacing * render_priv->font_scale * scale_x);
933     adv *= scale_x;
934     sx *= scale_x;
935     sy *= scale_y;
936     desc *= scale_y;
937     desc += asc * (scale_y - 1.0);
938
939     FT_Vector points[4] = {
940         { .x = -sx,         .y = asc + sy },
941         { .x = adv + sx,    .y = asc + sy },
942         { .x = adv + sx,    .y = -desc - sy },
943         { .x = -sx,         .y = -desc - sy },
944     };
945
946     ol->n_points = ol->n_contours = 0;
947     if (!outline_alloc(ol, 4, 1))
948         return;
949     for (int i = 0; i < 4; ++i) {
950         ol->points[ol->n_points] = points[i];
951         ol->tags[ol->n_points++] = 1;
952     }
953     ol->contours[ol->n_contours++] = ol->n_points - 1;
954 }
955
956 /**
957  * \brief Prepare glyph hash
958  */
959 static void
960 fill_glyph_hash(ASS_Renderer *priv, OutlineHashKey *outline_key,
961                 GlyphInfo *info)
962 {
963     if (info->drawing) {
964         DrawingHashKey *key = &outline_key->u.drawing;
965         outline_key->type = OUTLINE_DRAWING;
966         key->scale_x = double_to_d16(info->scale_x);
967         key->scale_y = double_to_d16(info->scale_y);
968         key->outline.x = double_to_d6(info->border_x * priv->border_scale);
969         key->outline.y = double_to_d6(info->border_y * priv->border_scale);
970         key->border_style = info->border_style;
971         // hpacing only matters for opaque box borders (see draw_opaque_box),
972         // so for normal borders, maximize cache utility by ignoring it
973         key->hspacing =
974             info->border_style == 3 ? double_to_d16(info->hspacing) : 0;
975         key->hash = info->drawing->hash;
976         key->text = info->drawing->text;
977         key->pbo = info->drawing->pbo;
978         key->scale = info->drawing->scale;
979     } else {
980         GlyphHashKey *key = &outline_key->u.glyph;
981         outline_key->type = OUTLINE_GLYPH;
982         key->font = info->font;
983         key->size = info->font_size;
984         key->face_index = info->face_index;
985         key->glyph_index = info->glyph_index;
986         key->bold = info->bold;
987         key->italic = info->italic;
988         key->scale_x = double_to_d16(info->scale_x);
989         key->scale_y = double_to_d16(info->scale_y);
990         key->outline.x = double_to_d6(info->border_x * priv->border_scale);
991         key->outline.y = double_to_d6(info->border_y * priv->border_scale);
992         key->flags = info->flags;
993         key->border_style = info->border_style;
994         key->hspacing =
995             info->border_style == 3 ? double_to_d16(info->hspacing) : 0;
996     }
997 }
998
999 /**
1000  * \brief Prepare combined-bitmap hash
1001  */
1002 static void fill_composite_hash(CompositeHashKey *hk, CombinedBitmapInfo *info)
1003 {
1004     hk->filter = info->filter;
1005     hk->bitmap_count = info->bitmap_count;
1006     hk->bitmaps = info->bitmaps;
1007 }
1008
1009 /**
1010  * \brief Get normal and outline (border) glyphs
1011  * \param info out: struct filled with extracted data
1012  * Tries to get both glyphs from cache.
1013  * If they can't be found, gets a glyph from font face, generates outline,
1014  * and add them to cache.
1015  * The glyphs are returned in info->glyph and info->outline_glyph
1016  */
1017 static void
1018 get_outline_glyph(ASS_Renderer *priv, GlyphInfo *info)
1019 {
1020     memset(&info->hash_key, 0, sizeof(info->hash_key));
1021
1022     OutlineHashKey key;
1023     OutlineHashValue *val;
1024     fill_glyph_hash(priv, &key, info);
1025     if (!ass_cache_get(priv->cache.outline_cache, &key, &val)) {
1026         if (!val)
1027             return;
1028         memset(val, 0, sizeof(*val));
1029
1030         if (info->drawing) {
1031             ASS_Drawing *drawing = info->drawing;
1032             ass_drawing_hash(drawing);
1033             if(!ass_drawing_parse(drawing, 0) ||
1034                     !outline_copy(&val->outline, &drawing->outline)) {
1035                 ass_cache_commit(val, 1);
1036                 ass_cache_dec_ref(val);
1037                 return;
1038             }
1039             val->advance.x = drawing->advance.x;
1040             val->advance.y = drawing->advance.y;
1041             val->asc = drawing->asc;
1042             val->desc = drawing->desc;
1043         } else {
1044             ass_face_set_size(info->font->faces[info->face_index],
1045                               info->font_size);
1046             ass_font_set_transform(info->font, info->scale_x,
1047                                    info->scale_y, NULL);
1048             FT_Glyph glyph =
1049                 ass_font_get_glyph(info->font,
1050                         info->symbol, info->face_index, info->glyph_index,
1051                         priv->settings.hinting, info->flags);
1052             if (glyph != NULL) {
1053                 FT_Outline *src = &((FT_OutlineGlyph) glyph)->outline;
1054                 if (!outline_convert(&val->outline, src)) {
1055                     ass_cache_commit(val, 1);
1056                     ass_cache_dec_ref(val);
1057                     return;
1058                 }
1059                 if (priv->settings.shaper == ASS_SHAPING_SIMPLE) {
1060                     val->advance.x = d16_to_d6(glyph->advance.x);
1061                     val->advance.y = d16_to_d6(glyph->advance.y);
1062                 }
1063                 FT_Done_Glyph(glyph);
1064                 ass_font_get_asc_desc(info->font, info->symbol,
1065                                       &val->asc, &val->desc);
1066                 val->asc  *= info->scale_y;
1067                 val->desc *= info->scale_y;
1068             }
1069         }
1070         val->valid = true;
1071
1072         outline_get_cbox(&val->outline, &val->bbox_scaled);
1073
1074         if (info->border_style == 3) {
1075             FT_Vector advance;
1076             if (priv->settings.shaper == ASS_SHAPING_SIMPLE || info->drawing)
1077                 advance = val->advance;
1078             else
1079                 advance = info->advance;
1080
1081             draw_opaque_box(priv, info, val->asc, val->desc, &val->border[0], advance,
1082                             double_to_d6(info->border_x * priv->border_scale),
1083                             double_to_d6(info->border_y * priv->border_scale));
1084
1085         } else if (val->outline.n_points && (info->border_x > 0 || info->border_y > 0)
1086                 && double_to_d6(info->scale_x) && double_to_d6(info->scale_y)) {
1087             const int eps = 16;
1088             int xbord = double_to_d6(info->border_x * priv->border_scale);
1089             int ybord = double_to_d6(info->border_y * priv->border_scale);
1090             if(xbord >= eps || ybord >= eps) {
1091                 outline_alloc(&val->border[0], 2 * val->outline.n_points, val->outline.n_contours);
1092                 outline_alloc(&val->border[1], 2 * val->outline.n_points, val->outline.n_contours);
1093                 if (!val->border[0].max_points || !val->border[1].max_points ||
1094                         !outline_stroke(&val->border[0], &val->border[1],
1095                                         &val->outline, xbord, ybord, eps)) {
1096                     ass_msg(priv->library, MSGL_WARN, "Cannot stoke outline");
1097                     outline_free(&val->border[0]);
1098                     outline_free(&val->border[1]);
1099                 }
1100             }
1101         }
1102
1103         ass_cache_commit(val, 1);
1104     } else if (!val->valid) {
1105         ass_cache_dec_ref(val);
1106         return;
1107     }
1108
1109     info->hash_key.u.outline.outline = val;
1110     info->outline = &val->outline;
1111     info->border[0] = &val->border[0];
1112     info->border[1] = &val->border[1];
1113     info->bbox = val->bbox_scaled;
1114     if (info->drawing || priv->settings.shaper == ASS_SHAPING_SIMPLE) {
1115         info->cluster_advance.x = info->advance.x = val->advance.x;
1116         info->cluster_advance.y = info->advance.y = val->advance.y;
1117     }
1118     info->asc = val->asc;
1119     info->desc = val->desc;
1120 }
1121
1122 /**
1123  * \brief Apply transformation to outline points of a glyph
1124  * Applies rotations given by frx, fry and frz and projects the points back
1125  * onto the screen plane.
1126  */
1127 static void
1128 transform_3d_points(FT_Vector shift, ASS_Outline *outline, double frx, double fry,
1129                     double frz, double fax, double fay, double scale,
1130                     int yshift)
1131 {
1132     double sx = sin(frx);
1133     double sy = sin(fry);
1134     double sz = sin(frz);
1135     double cx = cos(frx);
1136     double cy = cos(fry);
1137     double cz = cos(frz);
1138     FT_Vector *p = outline->points;
1139     double x, y, z, xx, yy, zz;
1140     int dist;
1141
1142     dist = 20000 * scale;
1143     for (size_t i = 0; i < outline->n_points; ++i) {
1144         x = (double) p[i].x + shift.x + (fax * (yshift - p[i].y));
1145         y = (double) p[i].y + shift.y + (-fay * p[i].x);
1146         z = 0.;
1147
1148         xx = x * cz + y * sz;
1149         yy = -(x * sz - y * cz);
1150         zz = z;
1151
1152         x = xx;
1153         y = yy * cx + zz * sx;
1154         z = yy * sx - zz * cx;
1155
1156         xx = x * cy + z * sy;
1157         yy = y;
1158         zz = x * sy - z * cy;
1159
1160         zz = FFMAX(zz, 1000 - dist);
1161
1162         x = (xx * dist) / (zz + dist);
1163         y = (yy * dist) / (zz + dist);
1164         p[i].x = x - shift.x + 0.5;
1165         p[i].y = y - shift.y + 0.5;
1166     }
1167 }
1168
1169 /**
1170  * \brief Apply 3d transformation to several objects
1171  * \param shift FreeType vector
1172  * \param glyph FreeType glyph
1173  * \param glyph2 FreeType glyph
1174  * \param frx x-axis rotation angle
1175  * \param fry y-axis rotation angle
1176  * \param frz z-axis rotation angle
1177  * Rotates both glyphs by frx, fry and frz. Shift vector is added before rotation and subtracted after it.
1178  */
1179 static void
1180 transform_3d(FT_Vector shift, ASS_Outline *outline, int n_outlines,
1181              double frx, double fry, double frz, double fax, double fay,
1182              double scale, int yshift)
1183 {
1184     frx = -frx;
1185     frz = -frz;
1186     if (frx != 0. || fry != 0. || frz != 0. || fax != 0. || fay != 0.)
1187         for (int i = 0; i < n_outlines; i++)
1188             transform_3d_points(shift, &outline[i], frx, fry, frz,
1189                                 fax, fay, scale, yshift);
1190 }
1191
1192 /**
1193  * \brief Get bitmaps for a glyph
1194  * \param info glyph info
1195  * Tries to get glyph bitmaps from bitmap cache.
1196  * If they can't be found, they are generated by rotating and rendering the glyph.
1197  * After that, bitmaps are added to the cache.
1198  * They are returned in info->bm (glyph), info->bm_o (outline) and info->bm_s (shadow).
1199  */
1200 static void
1201 get_bitmap_glyph(ASS_Renderer *render_priv, GlyphInfo *info)
1202 {
1203     if (!info->outline || info->symbol == '\n' || info->symbol == 0 || info->skip)
1204         return;
1205
1206     BitmapHashValue *val;
1207     OutlineBitmapHashKey *key = &info->hash_key.u.outline;
1208     if (ass_cache_get(render_priv->cache.bitmap_cache, &info->hash_key, &val)) {
1209         info->image = val;
1210         if (!val->valid)
1211             info->symbol = 0;
1212         return;
1213     }
1214     if (!val) {
1215         info->symbol = 0;
1216         return;
1217     }
1218     if (!info->outline) {
1219         memset(val, 0, sizeof(*val));
1220         ass_cache_commit(val, sizeof(BitmapHashKey) + sizeof(BitmapHashValue));
1221         info->image = val;
1222         info->symbol = 0;
1223         return;
1224     }
1225
1226     const int n_outlines = 3;
1227     ASS_Outline outline[n_outlines];
1228     outline_copy(&outline[0], info->outline);
1229     outline_copy(&outline[1], info->border[0]);
1230     outline_copy(&outline[2], info->border[1]);
1231
1232     // calculating rotation shift vector (from rotation origin to the glyph basepoint)
1233     FT_Vector shift = { key->shift_x, key->shift_y };
1234     double scale_x = render_priv->font_scale_x;
1235     double fax_scaled = info->fax / info->scale_y * info->scale_x;
1236     double fay_scaled = info->fay / info->scale_x * info->scale_y;
1237
1238     // apply rotation
1239     // use blur_scale because, like blurs, VSFilter forgets to scale this
1240     transform_3d(shift, outline, n_outlines,
1241                  info->frx, info->fry, info->frz, fax_scaled,
1242                  fay_scaled, render_priv->blur_scale, info->asc);
1243
1244     // PAR correction scaling
1245     FT_Matrix m = { double_to_d16(scale_x), 0,
1246                     0, double_to_d16(1.0) };
1247
1248     // subpixel shift
1249     if (scale_x != 1.0)
1250         for (int i = 0; i < n_outlines; i++)
1251             outline_transform(&outline[i], &m);
1252     for (int i = 0; i < n_outlines; i++)
1253         outline_translate(&outline[i], key->advance.x, -key->advance.y);
1254
1255     // render glyph
1256     val->valid = outline_to_bitmap2(render_priv,
1257                                     &outline[0], &outline[1], &outline[2],
1258                                     &val->bm, &val->bm_o);
1259     if (!val->valid)
1260         info->symbol = 0;
1261
1262     ass_cache_commit(val, bitmap_size(val->bm) + bitmap_size(val->bm_o) +
1263                      sizeof(BitmapHashKey) + sizeof(BitmapHashValue));
1264     info->image = val;
1265
1266     for (int i = 0; i < n_outlines; i++)
1267         outline_free(&outline[i]);
1268 }
1269
1270 /**
1271  * This function goes through text_info and calculates text parameters.
1272  * The following text_info fields are filled:
1273  *   height
1274  *   lines[].height
1275  *   lines[].asc
1276  *   lines[].desc
1277  */
1278 static void measure_text(ASS_Renderer *render_priv)
1279 {
1280     TextInfo *text_info = &render_priv->text_info;
1281     int cur_line = 0;
1282     double max_asc = 0., max_desc = 0.;
1283     GlyphInfo *last = NULL;
1284     int i;
1285     int empty_line = 1;
1286     text_info->height = 0.;
1287     for (i = 0; i < text_info->length + 1; ++i) {
1288         if ((i == text_info->length) || text_info->glyphs[i].linebreak) {
1289             if (empty_line && cur_line > 0 && last) {
1290                 max_asc = d6_to_double(last->asc) / 2.0;
1291                 max_desc = d6_to_double(last->desc) / 2.0;
1292             }
1293             text_info->lines[cur_line].asc = max_asc;
1294             text_info->lines[cur_line].desc = max_desc;
1295             text_info->height += max_asc + max_desc;
1296             cur_line++;
1297             max_asc = max_desc = 0.;
1298             empty_line = 1;
1299         }
1300         if (i < text_info->length) {
1301             GlyphInfo *cur = text_info->glyphs + i;
1302             if (d6_to_double(cur->asc) > max_asc)
1303                 max_asc = d6_to_double(cur->asc);
1304             if (d6_to_double(cur->desc) > max_desc)
1305                 max_desc = d6_to_double(cur->desc);
1306             if (cur->symbol != '\n' && cur->symbol != 0) {
1307                 empty_line = 0;
1308                 last = cur;
1309             }
1310         }
1311     }
1312     text_info->height +=
1313         (text_info->n_lines -
1314          1) * render_priv->settings.line_spacing;
1315 }
1316
1317 /**
1318  * Mark extra whitespace for later removal.
1319  */
1320 #define IS_WHITESPACE(x) ((x->symbol == ' ' || x->symbol == '\n') \
1321                           && !x->linebreak)
1322 static void trim_whitespace(ASS_Renderer *render_priv)
1323 {
1324     int i, j;
1325     GlyphInfo *cur;
1326     TextInfo *ti = &render_priv->text_info;
1327
1328     // Mark trailing spaces
1329     i = ti->length - 1;
1330     cur = ti->glyphs + i;
1331     while (i && IS_WHITESPACE(cur)) {
1332         cur->skip++;
1333         cur = ti->glyphs + --i;
1334     }
1335
1336     // Mark leading whitespace
1337     i = 0;
1338     cur = ti->glyphs;
1339     while (i < ti->length && IS_WHITESPACE(cur)) {
1340         cur->skip++;
1341         cur = ti->glyphs + ++i;
1342     }
1343
1344     // Mark all extraneous whitespace inbetween
1345     for (i = 0; i < ti->length; ++i) {
1346         cur = ti->glyphs + i;
1347         if (cur->linebreak) {
1348             // Mark whitespace before
1349             j = i - 1;
1350             cur = ti->glyphs + j;
1351             while (j && IS_WHITESPACE(cur)) {
1352                 cur->skip++;
1353                 cur = ti->glyphs + --j;
1354             }
1355             // A break itself can contain a whitespace, too
1356             cur = ti->glyphs + i;
1357             if (cur->symbol == ' ' || cur->symbol == '\n') {
1358                 cur->skip++;
1359                 // Mark whitespace after
1360                 j = i + 1;
1361                 cur = ti->glyphs + j;
1362                 while (j < ti->length && IS_WHITESPACE(cur)) {
1363                     cur->skip++;
1364                     cur = ti->glyphs + ++j;
1365                 }
1366                 i = j - 1;
1367             }
1368         }
1369     }
1370 }
1371 #undef IS_WHITESPACE
1372
1373 /**
1374  * \brief rearrange text between lines
1375  * \param max_text_width maximal text line width in pixels
1376  * The algo is similar to the one in libvo/sub.c:
1377  * 1. Place text, wrapping it when current line is full
1378  * 2. Try moving words from the end of a line to the beginning of the next one while it reduces
1379  * the difference in lengths between this two lines.
1380  * The result may not be optimal, but usually is good enough.
1381  *
1382  * FIXME: implement style 0 and 3 correctly
1383  */
1384 static void
1385 wrap_lines_smart(ASS_Renderer *render_priv, double max_text_width)
1386 {
1387     int i;
1388     GlyphInfo *cur, *s1, *e1, *s2, *s3;
1389     int last_space;
1390     int break_type;
1391     int exit;
1392     double pen_shift_x;
1393     double pen_shift_y;
1394     int cur_line;
1395     TextInfo *text_info = &render_priv->text_info;
1396
1397     last_space = -1;
1398     text_info->n_lines = 1;
1399     break_type = 0;
1400     s1 = text_info->glyphs;     // current line start
1401     for (i = 0; i < text_info->length; ++i) {
1402         int break_at = -1;
1403         double s_offset, len;
1404         cur = text_info->glyphs + i;
1405         s_offset = d6_to_double(s1->bbox.xMin + s1->pos.x);
1406         len = d6_to_double(cur->bbox.xMax + cur->pos.x) - s_offset;
1407
1408         if (cur->symbol == '\n') {
1409             break_type = 2;
1410             break_at = i;
1411             ass_msg(render_priv->library, MSGL_DBG2,
1412                     "forced line break at %d", break_at);
1413         } else if (cur->symbol == ' ') {
1414             last_space = i;
1415         } else if (len >= max_text_width
1416                    && (render_priv->state.wrap_style != 2)) {
1417             break_type = 1;
1418             break_at = last_space;
1419             if (break_at >= 0)
1420                 ass_msg(render_priv->library, MSGL_DBG2, "line break at %d",
1421                         break_at);
1422         }
1423
1424         if (break_at != -1) {
1425             // need to use one more line
1426             // marking break_at+1 as start of a new line
1427             int lead = break_at + 1;    // the first symbol of the new line
1428             if (text_info->n_lines >= text_info->max_lines) {
1429                 // Raise maximum number of lines
1430                 text_info->max_lines *= 2;
1431                 text_info->lines = realloc(text_info->lines,
1432                                            sizeof(LineInfo) *
1433                                            text_info->max_lines);
1434             }
1435             if (lead < text_info->length) {
1436                 text_info->glyphs[lead].linebreak = break_type;
1437                 last_space = -1;
1438                 s1 = text_info->glyphs + lead;
1439                 text_info->n_lines++;
1440             }
1441         }
1442     }
1443 #define DIFF(x,y) (((x) < (y)) ? (y - x) : (x - y))
1444     exit = 0;
1445     while (!exit && render_priv->state.wrap_style != 1) {
1446         exit = 1;
1447         s3 = text_info->glyphs;
1448         s1 = s2 = 0;
1449         for (i = 0; i <= text_info->length; ++i) {
1450             cur = text_info->glyphs + i;
1451             if ((i == text_info->length) || cur->linebreak) {
1452                 s1 = s2;
1453                 s2 = s3;
1454                 s3 = cur;
1455                 if (s1 && (s2->linebreak == 1)) {       // have at least 2 lines, and linebreak is 'soft'
1456                     double l1, l2, l1_new, l2_new;
1457                     GlyphInfo *w = s2;
1458
1459                     do {
1460                         --w;
1461                     } while ((w > s1) && (w->symbol == ' '));
1462                     while ((w > s1) && (w->symbol != ' ')) {
1463                         --w;
1464                     }
1465                     e1 = w;
1466                     while ((e1 > s1) && (e1->symbol == ' ')) {
1467                         --e1;
1468                     }
1469                     if (w->symbol == ' ')
1470                         ++w;
1471
1472                     l1 = d6_to_double(((s2 - 1)->bbox.xMax + (s2 - 1)->pos.x) -
1473                         (s1->bbox.xMin + s1->pos.x));
1474                     l2 = d6_to_double(((s3 - 1)->bbox.xMax + (s3 - 1)->pos.x) -
1475                         (s2->bbox.xMin + s2->pos.x));
1476                     l1_new = d6_to_double(
1477                         (e1->bbox.xMax + e1->pos.x) -
1478                         (s1->bbox.xMin + s1->pos.x));
1479                     l2_new = d6_to_double(
1480                         ((s3 - 1)->bbox.xMax + (s3 - 1)->pos.x) -
1481                         (w->bbox.xMin + w->pos.x));
1482
1483                     if (DIFF(l1_new, l2_new) < DIFF(l1, l2)) {
1484                         if (w->linebreak || w == text_info->glyphs)
1485                             text_info->n_lines--;
1486                         if (w != text_info->glyphs)
1487                             w->linebreak = 1;
1488                         s2->linebreak = 0;
1489                         exit = 0;
1490                     }
1491                 }
1492             }
1493             if (i == text_info->length)
1494                 break;
1495         }
1496
1497     }
1498     assert(text_info->n_lines >= 1);
1499 #undef DIFF
1500
1501     measure_text(render_priv);
1502     trim_whitespace(render_priv);
1503
1504     cur_line = 1;
1505
1506     i = 0;
1507     cur = text_info->glyphs + i;
1508     while (i < text_info->length && cur->skip)
1509         cur = text_info->glyphs + ++i;
1510     pen_shift_x = d6_to_double(-cur->pos.x);
1511     pen_shift_y = 0.;
1512
1513     for (i = 0; i < text_info->length; ++i) {
1514         cur = text_info->glyphs + i;
1515         if (cur->linebreak) {
1516             while (i < text_info->length && cur->skip && cur->symbol != '\n')
1517                 cur = text_info->glyphs + ++i;
1518             double height =
1519                 text_info->lines[cur_line - 1].desc +
1520                 text_info->lines[cur_line].asc;
1521             text_info->lines[cur_line - 1].len = i -
1522                 text_info->lines[cur_line - 1].offset;
1523             text_info->lines[cur_line].offset = i;
1524             cur_line++;
1525             pen_shift_x = d6_to_double(-cur->pos.x);
1526             pen_shift_y += height + render_priv->settings.line_spacing;
1527         }
1528         cur->pos.x += double_to_d6(pen_shift_x);
1529         cur->pos.y += double_to_d6(pen_shift_y);
1530     }
1531     text_info->lines[cur_line - 1].len =
1532         text_info->length - text_info->lines[cur_line - 1].offset;
1533
1534 #if 0
1535     // print line info
1536     for (i = 0; i < text_info->n_lines; i++) {
1537         printf("line %d offset %d length %d\n", i, text_info->lines[i].offset,
1538                 text_info->lines[i].len);
1539     }
1540 #endif
1541 }
1542
1543 /**
1544  * \brief Calculate base point for positioning and rotation
1545  * \param bbox text bbox
1546  * \param alignment alignment
1547  * \param bx, by out: base point coordinates
1548  */
1549 static void get_base_point(DBBox *bbox, int alignment, double *bx, double *by)
1550 {
1551     const int halign = alignment & 3;
1552     const int valign = alignment & 12;
1553     if (bx)
1554         switch (halign) {
1555         case HALIGN_LEFT:
1556             *bx = bbox->xMin;
1557             break;
1558         case HALIGN_CENTER:
1559             *bx = (bbox->xMax + bbox->xMin) / 2.0;
1560             break;
1561         case HALIGN_RIGHT:
1562             *bx = bbox->xMax;
1563             break;
1564         }
1565     if (by)
1566         switch (valign) {
1567         case VALIGN_TOP:
1568             *by = bbox->yMin;
1569             break;
1570         case VALIGN_CENTER:
1571             *by = (bbox->yMax + bbox->yMin) / 2.0;
1572             break;
1573         case VALIGN_SUB:
1574             *by = bbox->yMax;
1575             break;
1576         }
1577 }
1578
1579 /**
1580  * Prepare bitmap hash key of a glyph
1581  */
1582 static void
1583 fill_bitmap_hash(ASS_Renderer *priv, GlyphInfo *info,
1584                  OutlineBitmapHashKey *hash_key)
1585 {
1586     hash_key->frx = rot_key(info->frx);
1587     hash_key->fry = rot_key(info->fry);
1588     hash_key->frz = rot_key(info->frz);
1589     hash_key->fax = double_to_d16(info->fax);
1590     hash_key->fay = double_to_d16(info->fay);
1591 }
1592
1593 /**
1594  * \brief Adjust the glyph's font size and scale factors to ensure smooth
1595  *  scaling and handle pathological font sizes. The main problem here is
1596  *  freetype's grid fitting, which destroys animations by font size, or will
1597  *  result in incorrect final text size if font sizes are very small and
1598  *  scale factors very large. See Google Code issue #46.
1599  * \param priv guess what
1600  * \param glyph the glyph to be modified
1601  */
1602 static void
1603 fix_glyph_scaling(ASS_Renderer *priv, GlyphInfo *glyph)
1604 {
1605     double ft_size;
1606     if (priv->settings.hinting == ASS_HINTING_NONE) {
1607         // arbitrary, not too small to prevent grid fitting rounding effects
1608         // XXX: this is a rather crude hack
1609         ft_size = 256.0;
1610     } else {
1611         // If hinting is enabled, we want to pass the real font size
1612         // to freetype. Normalize scale_y to 1.0.
1613         ft_size = glyph->scale_y * glyph->font_size;
1614     }
1615     glyph->scale_x = glyph->scale_x * glyph->font_size / ft_size;
1616     glyph->scale_y = glyph->scale_y * glyph->font_size / ft_size;
1617     glyph->font_size = ft_size;
1618 }
1619
1620  /**
1621   * \brief Checks whether a glyph should start a new bitmap run
1622   * \param info Pointer to new GlyphInfo to check
1623   * \param current_info Pointer to CombinedBitmapInfo for current run (may be NULL)
1624   * \return 1 if a new run should be started
1625   */
1626 static int is_new_bm_run(GlyphInfo *info, GlyphInfo *last)
1627 {
1628     // FIXME: Don't break on glyph substitutions
1629     return !last || info->effect || info->drawing || last->drawing ||
1630         strcmp(last->font->desc.family, info->font->desc.family) ||
1631         last->font->desc.vertical != info->font->desc.vertical ||
1632         last->face_index != info->face_index ||
1633         last->font_size != info->font_size ||
1634         last->c[0] != info->c[0] ||
1635         last->c[1] != info->c[1] ||
1636         last->c[2] != info->c[2] ||
1637         last->c[3] != info->c[3] ||
1638         last->be != info->be ||
1639         last->blur != info->blur ||
1640         last->shadow_x != info->shadow_x ||
1641         last->shadow_y != info->shadow_y ||
1642         last->frx != info->frx ||
1643         last->fry != info->fry ||
1644         last->frz != info->frz ||
1645         last->fax != info->fax ||
1646         last->fay != info->fay ||
1647         last->scale_x != info->scale_x ||
1648         last->scale_y != info->scale_y ||
1649         last->border_style != info->border_style ||
1650         last->border_x != info->border_x ||
1651         last->border_y != info->border_y ||
1652         last->hspacing != info->hspacing ||
1653         last->italic != info->italic ||
1654         last->bold != info->bold ||
1655         last->flags != info->flags;
1656 }
1657
1658 static void make_shadow_bitmap(CombinedBitmapInfo *info, ASS_Renderer *render_priv)
1659 {
1660     if (!(info->filter.flags & FILTER_NONZERO_SHADOW)) {
1661         if (info->bm && info->bm_o && !(info->filter.flags & FILTER_BORDER_STYLE_3)) {
1662             fix_outline(info->bm, info->bm_o);
1663         } else if (info->bm_o && !(info->filter.flags & FILTER_NONZERO_BORDER)) {
1664             ass_free_bitmap(info->bm_o);
1665             info->bm_o = 0;
1666         }
1667         return;
1668     }
1669
1670     // Create shadow and fix outline as needed
1671     if (info->bm && info->bm_o && !(info->filter.flags & FILTER_BORDER_STYLE_3)) {
1672         info->bm_s = copy_bitmap(render_priv->engine, info->bm_o);
1673         fix_outline(info->bm, info->bm_o);
1674     } else if (info->bm_o && (info->filter.flags & FILTER_NONZERO_BORDER)) {
1675         info->bm_s = copy_bitmap(render_priv->engine, info->bm_o);
1676     } else if (info->bm_o) {
1677         info->bm_s = info->bm_o;
1678         info->bm_o = 0;
1679     } else if (info->bm)
1680         info->bm_s = copy_bitmap(render_priv->engine, info->bm);
1681
1682     if (!info->bm_s)
1683         return;
1684
1685     // Works right even for negative offsets
1686     // '>>' rounds toward negative infinity, '&' returns correct remainder
1687     info->bm_s->left += info->filter.shadow.x >> 6;
1688     info->bm_s->top  += info->filter.shadow.y >> 6;
1689     shift_bitmap(info->bm_s, info->filter.shadow.x & SUBPIXEL_MASK, info->filter.shadow.y & SUBPIXEL_MASK);
1690 }
1691
1692 // Parse event text.
1693 // Fill render_priv->text_info.
1694 static int parse_events(ASS_Renderer *render_priv, ASS_Event *event)
1695 {
1696     TextInfo *text_info = &render_priv->text_info;
1697     ASS_Drawing *drawing = NULL;
1698     unsigned code;
1699     char *p, *q;
1700     int i;
1701
1702     p = event->Text;
1703
1704     // Event parsing.
1705     while (1) {
1706         // get next char, executing style override
1707         // this affects render_context
1708         code = 0;
1709         while (*p) {
1710             if ((*p == '{') && (q = strchr(p, '}'))) {
1711                 while (p < q)
1712                     p = parse_tag(render_priv, p, q, 1.);
1713                 assert(*p == '}');
1714                 p++;
1715             } else if (render_priv->state.drawing_scale) {
1716                 q = p;
1717                 if (*p == '{')
1718                     q++;
1719                 while ((*q != '{') && (*q != 0))
1720                     q++;
1721                 if (!drawing) {
1722                     drawing = ass_drawing_new(render_priv->library,
1723                                               render_priv->ftlibrary);
1724                     if (!drawing)
1725                         return 1;
1726                 }
1727                 ass_drawing_set_text(drawing, p, q - p);
1728                 code = 0xfffc; // object replacement character
1729                 p = q;
1730                 break;
1731             } else {
1732                 code = get_next_char(render_priv, &p);
1733                 break;
1734             }
1735         }
1736
1737         if (code == 0)
1738             break;
1739
1740         // face could have been changed in get_next_char
1741         if (!render_priv->state.font) {
1742             free_render_context(render_priv);
1743             ass_drawing_free(drawing);
1744             return 1;
1745         }
1746
1747         if (text_info->length >= text_info->max_glyphs) {
1748             // Raise maximum number of glyphs
1749             text_info->max_glyphs *= 2;
1750             text_info->glyphs =
1751                 realloc(text_info->glyphs,
1752                         sizeof(GlyphInfo) * text_info->max_glyphs);
1753         }
1754
1755         GlyphInfo *info = &text_info->glyphs[text_info->length];
1756
1757         // Clear current GlyphInfo
1758         memset(info, 0, sizeof(GlyphInfo));
1759
1760         // Parse drawing
1761         if (drawing && drawing->text) {
1762             drawing->scale_x = render_priv->state.scale_x *
1763                                      render_priv->font_scale;
1764             drawing->scale_y = render_priv->state.scale_y *
1765                                      render_priv->font_scale;
1766             drawing->scale = render_priv->state.drawing_scale;
1767             drawing->pbo = render_priv->state.pbo;
1768             info->drawing = drawing;
1769             drawing = NULL;
1770         }
1771
1772         // Fill glyph information
1773         info->symbol = code;
1774         info->font = render_priv->state.font;
1775         if (!info->drawing)
1776             ass_cache_inc_ref(info->font);
1777         for (i = 0; i < 4; ++i) {
1778             uint32_t clr = render_priv->state.c[i];
1779             // VSFilter compatibility: apply fade only when it's positive
1780             if (render_priv->state.fade > 0)
1781                 change_alpha(&clr,
1782                              mult_alpha(_a(clr), render_priv->state.fade), 1.);
1783             info->c[i] = clr;
1784         }
1785
1786         info->effect_type = render_priv->state.effect_type;
1787         info->effect_timing = render_priv->state.effect_timing;
1788         info->effect_skip_timing = render_priv->state.effect_skip_timing;
1789         info->font_size =
1790             render_priv->state.font_size * render_priv->font_scale;
1791         info->be = render_priv->state.be;
1792         info->blur = render_priv->state.blur;
1793         info->shadow_x = render_priv->state.shadow_x;
1794         info->shadow_y = render_priv->state.shadow_y;
1795         info->scale_x = info->orig_scale_x = render_priv->state.scale_x;
1796         info->scale_y = info->orig_scale_y = render_priv->state.scale_y;
1797         info->border_style = render_priv->state.border_style;
1798         info->border_x= render_priv->state.border_x;
1799         info->border_y = render_priv->state.border_y;
1800         info->hspacing = render_priv->state.hspacing;
1801         info->bold = render_priv->state.bold;
1802         info->italic = render_priv->state.italic;
1803         info->flags = render_priv->state.flags;
1804         info->frx = render_priv->state.frx;
1805         info->fry = render_priv->state.fry;
1806         info->frz = render_priv->state.frz;
1807         info->fax = render_priv->state.fax;
1808         info->fay = render_priv->state.fay;
1809
1810         if (!info->drawing)
1811             fix_glyph_scaling(render_priv, info);
1812
1813         text_info->length++;
1814
1815         render_priv->state.effect_type = EF_NONE;
1816         render_priv->state.effect_timing = 0;
1817         render_priv->state.effect_skip_timing = 0;
1818     }
1819
1820     ass_drawing_free(drawing);
1821
1822     return 0;
1823 }
1824
1825 // Process render_priv->text_info and load glyph outlines.
1826 static void retrieve_glyphs(ASS_Renderer *render_priv)
1827 {
1828     GlyphInfo *glyphs = render_priv->text_info.glyphs;
1829     int i;
1830
1831     for (i = 0; i < render_priv->text_info.length; i++) {
1832         GlyphInfo *info = glyphs + i;
1833         while (info) {
1834             get_outline_glyph(render_priv, info);
1835             info = info->next;
1836         }
1837         info = glyphs + i;
1838
1839         // Add additional space after italic to non-italic style changes
1840         if (i && glyphs[i - 1].italic && !info->italic) {
1841             int back = i - 1;
1842             GlyphInfo *og = &glyphs[back];
1843             while (back && og->bbox.xMax - og->bbox.xMin == 0
1844                     && og->italic)
1845                 og = &glyphs[--back];
1846             if (og->bbox.xMax > og->cluster_advance.x)
1847                 og->cluster_advance.x = og->bbox.xMax;
1848         }
1849
1850         // add horizontal letter spacing
1851         info->cluster_advance.x += double_to_d6(info->hspacing *
1852                 render_priv->font_scale * info->orig_scale_x);
1853
1854         // add displacement for vertical shearing
1855         info->cluster_advance.y += (info->fay / info->scale_x * info->scale_y) * info->cluster_advance.x;
1856     }
1857 }
1858
1859 // Preliminary layout (for line wrapping)
1860 static void preliminary_layout(ASS_Renderer *render_priv)
1861 {
1862     FT_Vector pen;
1863     int i;
1864
1865     pen.x = 0;
1866     pen.y = 0;
1867     for (i = 0; i < render_priv->text_info.length; i++) {
1868         GlyphInfo *info = render_priv->text_info.glyphs + i;
1869         FT_Vector cluster_pen = pen;
1870         while (info) {
1871             info->pos.x = cluster_pen.x;
1872             info->pos.y = cluster_pen.y;
1873
1874             cluster_pen.x += info->advance.x;
1875             cluster_pen.y += info->advance.y;
1876
1877             // fill bitmap hash
1878             info->hash_key.type = BITMAP_OUTLINE;
1879             fill_bitmap_hash(render_priv, info, &info->hash_key.u.outline);
1880
1881             info = info->next;
1882         }
1883         info = render_priv->text_info.glyphs + i;
1884         pen.x += info->cluster_advance.x;
1885         pen.y += info->cluster_advance.y;
1886     }
1887 }
1888
1889 // Reorder text into visual order
1890 static void reorder_text(ASS_Renderer *render_priv)
1891 {
1892     TextInfo *text_info = &render_priv->text_info;
1893     FT_Vector pen;
1894     int i;
1895
1896     FriBidiStrIndex *cmap = ass_shaper_reorder(render_priv->shaper, text_info);
1897     if (!cmap) {
1898         ass_msg(render_priv->library, MSGL_ERR, "Failed to reorder text");
1899         ass_shaper_cleanup(render_priv->shaper, text_info);
1900         free_render_context(render_priv);
1901         return;
1902     }
1903
1904     // Reposition according to the map
1905     pen.x = 0;
1906     pen.y = 0;
1907     int lineno = 1;
1908     double last_pen_x = 0;
1909     double last_fay = 0;
1910     for (i = 0; i < text_info->length; i++) {
1911         GlyphInfo *info = text_info->glyphs + cmap[i];
1912         if (text_info->glyphs[i].linebreak) {
1913             pen.y -= (last_fay / info->scale_x * info->scale_y) * (pen.x - last_pen_x);
1914             last_pen_x = pen.x = 0;
1915             pen.y += double_to_d6(text_info->lines[lineno-1].desc);
1916             pen.y += double_to_d6(text_info->lines[lineno].asc);
1917             pen.y += double_to_d6(render_priv->settings.line_spacing);
1918             lineno++;
1919         }
1920         else if (last_fay != info->fay) {
1921             pen.y -= (last_fay / info->scale_x * info->scale_y) * (pen.x - last_pen_x);
1922             last_pen_x = pen.x;
1923         }
1924         last_fay = info->fay;
1925         if (info->skip) continue;
1926         FT_Vector cluster_pen = pen;
1927         while (info) {
1928             info->pos.x = info->offset.x + cluster_pen.x;
1929             info->pos.y = info->offset.y + cluster_pen.y;
1930             cluster_pen.x += info->advance.x;
1931             cluster_pen.y += info->advance.y;
1932             info = info->next;
1933         }
1934         info = text_info->glyphs + cmap[i];
1935         pen.x += info->cluster_advance.x;
1936         pen.y += info->cluster_advance.y;
1937     }
1938 }
1939
1940 static void align_lines(ASS_Renderer *render_priv, double max_text_width)
1941 {
1942     TextInfo *text_info = &render_priv->text_info;
1943     GlyphInfo *glyphs = text_info->glyphs;
1944     int i, j;
1945     double width = 0;
1946     int last_break = -1;
1947     int halign = render_priv->state.alignment & 3;
1948     int justify = render_priv->state.justify;
1949     double max_width = 0;
1950
1951     if (render_priv->state.evt_type == EVENT_HSCROLL)
1952         return;
1953
1954     for (i = 0; i <= text_info->length; ++i) {   // (text_info->length + 1) is the end of the last line
1955         if ((i == text_info->length) || glyphs[i].linebreak) {
1956             max_width = FFMAX(max_width,width);
1957             width = 0;
1958         }
1959         if (i < text_info->length && !glyphs[i].skip &&
1960                 glyphs[i].symbol != '\n' && glyphs[i].symbol != 0) {
1961             width += d6_to_double(glyphs[i].cluster_advance.x);
1962         }
1963     }
1964     for (i = 0; i <= text_info->length; ++i) {   // (text_info->length + 1) is the end of the last line
1965         if ((i == text_info->length) || glyphs[i].linebreak) {
1966             double shift = 0;
1967             if (halign == HALIGN_LEFT) {    // left aligned, no action
1968                 if (justify == ASS_JUSTIFY_RIGHT) {
1969                     shift = max_width - width;
1970                 } else if (justify == ASS_JUSTIFY_CENTER) {
1971                     shift = (max_width - width) / 2.0;
1972                 } else {
1973                     shift = 0;
1974                 }
1975             } else if (halign == HALIGN_RIGHT) {    // right aligned
1976                 if (justify == ASS_JUSTIFY_LEFT) {
1977                     shift = max_text_width - max_width;
1978                 } else if (justify == ASS_JUSTIFY_CENTER) {
1979                     shift = max_text_width - max_width + (max_width - width) / 2.0;
1980                 } else {
1981                     shift = max_text_width - width;
1982                 }
1983             } else if (halign == HALIGN_CENTER) {   // centered
1984                 if (justify == ASS_JUSTIFY_LEFT) {
1985                     shift = (max_text_width - max_width) / 2.0;
1986                 } else if (justify == ASS_JUSTIFY_RIGHT) {
1987                     shift = (max_text_width - max_width) / 2.0 + max_width - width;
1988                 } else {
1989                     shift = (max_text_width - width) / 2.0;
1990                 }
1991             }
1992             for (j = last_break + 1; j < i; ++j) {
1993                 GlyphInfo *info = glyphs + j;
1994                 while (info) {
1995                     info->pos.x += double_to_d6(shift);
1996                     info = info->next;
1997                 }
1998             }
1999             last_break = i - 1;
2000             width = 0;
2001         }
2002         if (i < text_info->length && !glyphs[i].skip &&
2003                 glyphs[i].symbol != '\n' && glyphs[i].symbol != 0) {
2004             width += d6_to_double(glyphs[i].cluster_advance.x);
2005         }
2006     }
2007 }
2008
2009 static void calculate_rotation_params(ASS_Renderer *render_priv, DBBox *bbox,
2010                                       double device_x, double device_y)
2011 {
2012     TextInfo *text_info = &render_priv->text_info;
2013     DVector center;
2014     int i;
2015
2016     if (render_priv->state.have_origin) {
2017         center.x = x2scr(render_priv, render_priv->state.org_x);
2018         center.y = y2scr(render_priv, render_priv->state.org_y);
2019     } else {
2020         double bx = 0., by = 0.;
2021         get_base_point(bbox, render_priv->state.alignment, &bx, &by);
2022         center.x = device_x + bx;
2023         center.y = device_y + by;
2024     }
2025
2026     for (i = 0; i < text_info->length; ++i) {
2027         GlyphInfo *info = text_info->glyphs + i;
2028         while (info) {
2029             OutlineBitmapHashKey *key = &info->hash_key.u.outline;
2030
2031             if (key->frx || key->fry || key->frz || key->fax || key->fay) {
2032                 key->shift_x = info->pos.x + double_to_d6(device_x - center.x);
2033                 key->shift_y = -(info->pos.y + double_to_d6(device_y - center.y));
2034             } else {
2035                 key->shift_x = 0;
2036                 key->shift_y = 0;
2037             }
2038             info = info->next;
2039         }
2040     }
2041 }
2042
2043
2044 static inline void rectangle_reset(Rectangle *rect)
2045 {
2046     rect->x_min = rect->y_min = INT_MAX;
2047     rect->x_max = rect->y_max = INT_MIN;
2048 }
2049
2050 static inline void rectangle_combine(Rectangle *rect, const Bitmap *bm, int x, int y)
2051 {
2052     rect->x_min = FFMIN(rect->x_min, x + bm->left);
2053     rect->y_min = FFMIN(rect->y_min, y + bm->top);
2054     rect->x_max = FFMAX(rect->x_max, x + bm->left + bm->w);
2055     rect->y_max = FFMAX(rect->y_max, y + bm->top + bm->h);
2056 }
2057
2058 // Convert glyphs to bitmaps, combine them, apply blur, generate shadows.
2059 static void render_and_combine_glyphs(ASS_Renderer *render_priv,
2060                                       double device_x, double device_y)
2061 {
2062     TextInfo *text_info = &render_priv->text_info;
2063     int left = render_priv->settings.left_margin;
2064     device_x = (device_x - left) * render_priv->font_scale_x + left;
2065     unsigned nb_bitmaps = 0;
2066     char linebreak = 0;
2067     CombinedBitmapInfo *combined_info = text_info->combined_bitmaps;
2068     CombinedBitmapInfo *current_info = NULL;
2069     GlyphInfo *last_info = NULL;
2070     for (int i = 0; i < text_info->length; i++) {
2071         GlyphInfo *info = text_info->glyphs + i;
2072         if (info->linebreak) linebreak = 1;
2073         if (info->skip) {
2074             for (; info; info = info->next)
2075                 ass_cache_dec_ref(info->hash_key.u.outline.outline);
2076             continue;
2077         }
2078         for (; info; info = info->next) {
2079             OutlineBitmapHashKey *key = &info->hash_key.u.outline;
2080
2081             info->pos.x = double_to_d6(device_x + d6_to_double(info->pos.x) * render_priv->font_scale_x);
2082             info->pos.y = double_to_d6(device_y) + info->pos.y;
2083             key->advance.x = info->pos.x & (SUBPIXEL_MASK & ~SUBPIXEL_ACCURACY);
2084             key->advance.y = info->pos.y & (SUBPIXEL_MASK & ~SUBPIXEL_ACCURACY);
2085             int x = info->pos.x >> 6, y = info->pos.y >> 6;
2086             get_bitmap_glyph(render_priv, info);
2087
2088             if(linebreak || is_new_bm_run(info, last_info)) {
2089                 linebreak = 0;
2090                 last_info = NULL;
2091                 if (nb_bitmaps >= text_info->max_bitmaps) {
2092                     size_t new_size = 2 * text_info->max_bitmaps;
2093                     if (!ASS_REALLOC_ARRAY(text_info->combined_bitmaps, new_size)) {
2094                         ass_cache_dec_ref(info->image);
2095                         continue;
2096                     }
2097                     text_info->max_bitmaps = new_size;
2098                     combined_info = text_info->combined_bitmaps;
2099                 }
2100                 current_info = &combined_info[nb_bitmaps];
2101
2102                 memcpy(&current_info->c, &info->c, sizeof(info->c));
2103                 current_info->effect_type = info->effect_type;
2104                 current_info->effect_timing = info->effect_timing;
2105                 current_info->first_pos_x = info->bbox.xMax >> 6;
2106
2107                 current_info->filter.flags = 0;
2108                 if (info->border_style == 3)
2109                     current_info->filter.flags |= FILTER_BORDER_STYLE_3;
2110                 if (info->border_x || info->border_y)
2111                     current_info->filter.flags |= FILTER_NONZERO_BORDER;
2112                 if (info->shadow_x || info->shadow_y)
2113                     current_info->filter.flags |= FILTER_NONZERO_SHADOW;
2114                 // VSFilter compatibility: invisible fill and no border?
2115                 // In this case no shadow is supposed to be rendered.
2116                 if (info->border || (info->c[0] & 0xFF) != 0xFF)
2117                     current_info->filter.flags |= FILTER_DRAW_SHADOW;
2118
2119                 current_info->filter.be = info->be;
2120                 current_info->filter.blur = 2 * info->blur * render_priv->blur_scale;
2121                 current_info->filter.shadow.x = double_to_d6(info->shadow_x * render_priv->border_scale);
2122                 current_info->filter.shadow.y = double_to_d6(info->shadow_y * render_priv->border_scale);
2123
2124                 current_info->x = current_info->y = INT_MAX;
2125                 rectangle_reset(&current_info->rect);
2126                 rectangle_reset(&current_info->rect_o);
2127                 current_info->n_bm = current_info->n_bm_o = 0;
2128                 current_info->bm = current_info->bm_o = current_info->bm_s = NULL;
2129                 current_info->image = NULL;
2130
2131                 current_info->bitmap_count = current_info->max_bitmap_count = 0;
2132                 current_info->bitmaps = malloc(MAX_SUB_BITMAPS_INITIAL * sizeof(BitmapRef));
2133                 if (!current_info->bitmaps) {
2134                     ass_cache_dec_ref(info->image);
2135                     continue;
2136                 }
2137                 current_info->max_bitmap_count = MAX_SUB_BITMAPS_INITIAL;
2138
2139                 nb_bitmaps++;
2140             }
2141             last_info = info;
2142
2143             if (!info->image || !current_info) {
2144                 ass_cache_dec_ref(info->image);
2145                 continue;
2146             }
2147
2148             if (current_info->bitmap_count >= current_info->max_bitmap_count) {
2149                 size_t new_size = 2 * current_info->max_bitmap_count;
2150                 if (!ASS_REALLOC_ARRAY(current_info->bitmaps, new_size)) {
2151                     ass_cache_dec_ref(info->image);
2152                     continue;
2153                 }
2154                 current_info->max_bitmap_count = new_size;
2155             }
2156             current_info->bitmaps[current_info->bitmap_count].image = info->image;
2157             current_info->bitmaps[current_info->bitmap_count].x = x;
2158             current_info->bitmaps[current_info->bitmap_count].y = y;
2159             current_info->bitmap_count++;
2160
2161             current_info->x = FFMIN(current_info->x, x);
2162             current_info->y = FFMIN(current_info->y, y);
2163             if (info->image->bm) {
2164                 rectangle_combine(&current_info->rect, info->image->bm, x, y);
2165                 current_info->n_bm++;
2166             }
2167             if (info->image->bm_o) {
2168                 rectangle_combine(&current_info->rect_o, info->image->bm_o, x, y);
2169                 current_info->n_bm_o++;
2170             }
2171         }
2172     }
2173
2174     for (int i = 0; i < nb_bitmaps; i++) {
2175         CombinedBitmapInfo *info = &combined_info[i];
2176         for (int j = 0; j < info->bitmap_count; j++) {
2177             info->bitmaps[j].x -= info->x;
2178             info->bitmaps[j].y -= info->y;
2179         }
2180
2181         CompositeHashKey hk;
2182         CompositeHashValue *hv;
2183         fill_composite_hash(&hk, info);
2184         if (ass_cache_get(render_priv->cache.composite_cache, &hk, &hv)) {
2185             info->bm = hv->bm;
2186             info->bm_o = hv->bm_o;
2187             info->bm_s = hv->bm_s;
2188             info->image = hv;
2189             continue;
2190         }
2191         if (!hv)
2192             continue;
2193
2194         int bord = be_padding(info->filter.be);
2195         if (!bord && info->n_bm == 1) {
2196             for (int j = 0; j < info->bitmap_count; j++) {
2197                 if (!info->bitmaps[j].image->bm)
2198                     continue;
2199                 info->bm = copy_bitmap(render_priv->engine, info->bitmaps[j].image->bm);
2200                 if (info->bm) {
2201                     info->bm->left += info->bitmaps[j].x;
2202                     info->bm->top  += info->bitmaps[j].y;
2203                 }
2204                 break;
2205             }
2206         } else if (info->n_bm) {
2207             info->bm = alloc_bitmap(render_priv->engine,
2208                                     info->rect.x_max - info->rect.x_min + 2 * bord,
2209                                     info->rect.y_max - info->rect.y_min + 2 * bord, true);
2210             Bitmap *dst = info->bm;
2211             if (dst) {
2212                 dst->left = info->rect.x_min - info->x - bord;
2213                 dst->top  = info->rect.y_min - info->y - bord;
2214                 for (int j = 0; j < info->bitmap_count; j++) {
2215                     Bitmap *src = info->bitmaps[j].image->bm;
2216                     if (!src)
2217                         continue;
2218                     int x = info->bitmaps[j].x + src->left - dst->left;
2219                     int y = info->bitmaps[j].y + src->top  - dst->top;
2220                     assert(x >= 0 && x + src->w <= dst->w);
2221                     assert(y >= 0 && y + src->h <= dst->h);
2222                     unsigned char *buf = dst->buffer + y * dst->stride + x;
2223                     render_priv->engine->add_bitmaps(buf, dst->stride,
2224                                                      src->buffer, src->stride,
2225                                                      src->h, src->w);
2226                 }
2227             }
2228         }
2229         if (!bord && info->n_bm_o == 1) {
2230             for (int j = 0; j < info->bitmap_count; j++) {
2231                 if (!info->bitmaps[j].image->bm_o)
2232                     continue;
2233                 info->bm_o = copy_bitmap(render_priv->engine, info->bitmaps[j].image->bm_o);
2234                 if (info->bm_o) {
2235                     info->bm_o->left += info->bitmaps[j].x;
2236                     info->bm_o->top  += info->bitmaps[j].y;
2237                 }
2238                 break;
2239             }
2240         } else if (info->n_bm_o) {
2241             info->bm_o = alloc_bitmap(render_priv->engine,
2242                                       info->rect_o.x_max - info->rect_o.x_min + 2 * bord,
2243                                       info->rect_o.y_max - info->rect_o.y_min + 2 * bord,
2244                                       true);
2245             Bitmap *dst = info->bm_o;
2246             if (dst) {
2247                 dst->left = info->rect_o.x_min - info->x - bord;
2248                 dst->top  = info->rect_o.y_min - info->y - bord;
2249                 for (int j = 0; j < info->bitmap_count; j++) {
2250                     Bitmap *src = info->bitmaps[j].image->bm_o;
2251                     if (!src)
2252                         continue;
2253                     int x = info->bitmaps[j].x + src->left - dst->left;
2254                     int y = info->bitmaps[j].y + src->top  - dst->top;
2255                     assert(x >= 0 && x + src->w <= dst->w);
2256                     assert(y >= 0 && y + src->h <= dst->h);
2257                     unsigned char *buf = dst->buffer + y * dst->stride + x;
2258                     render_priv->engine->add_bitmaps(buf, dst->stride,
2259                                                      src->buffer, src->stride,
2260                                                      src->h, src->w);
2261                 }
2262             }
2263         }
2264
2265         if (info->bm || info->bm_o) {
2266             ass_synth_blur(render_priv->engine, info->filter.flags & FILTER_BORDER_STYLE_3,
2267                            info->filter.be, info->filter.blur, info->bm, info->bm_o);
2268             if (info->filter.flags & FILTER_DRAW_SHADOW)
2269                 make_shadow_bitmap(info, render_priv);
2270         }
2271
2272         hv->bm = info->bm;
2273         hv->bm_o = info->bm_o;
2274         hv->bm_s = info->bm_s;
2275         ass_cache_commit(hv, bitmap_size(hv->bm) +
2276                          bitmap_size(hv->bm_o) + bitmap_size(hv->bm_s) +
2277                          sizeof(CompositeHashKey) + sizeof(CompositeHashValue));
2278         info->image = hv;
2279     }
2280
2281     text_info->n_bitmaps = nb_bitmaps;
2282 }
2283
2284 static void add_background(ASS_Renderer *render_priv, EventImages *event_images)
2285 {
2286     double size_x = render_priv->state.shadow_x > 0 ?
2287                     render_priv->state.shadow_x * render_priv->border_scale : 0;
2288     double size_y = render_priv->state.shadow_y > 0 ?
2289                     render_priv->state.shadow_y * render_priv->border_scale : 0;
2290     int left    = event_images->left - size_x;
2291     int top     = event_images->top  - size_y;
2292     int right   = event_images->left + event_images->width  + size_x;
2293     int bottom  = event_images->top  + event_images->height + size_y;
2294     left        = FFMINMAX(left,   0, render_priv->width);
2295     top         = FFMINMAX(top,    0, render_priv->height);
2296     right       = FFMINMAX(right,  0, render_priv->width);
2297     bottom      = FFMINMAX(bottom, 0, render_priv->height);
2298     int w = right - left;
2299     int h = bottom - top;
2300     if (w < 1 || h < 1)
2301         return;
2302     void *nbuffer = ass_aligned_alloc(1, w * h, false);
2303     if (!nbuffer)
2304         return;
2305     memset(nbuffer, 0xFF, w * h);
2306     ASS_Image *img = my_draw_bitmap(nbuffer, w, h, w, left, top,
2307                                     render_priv->state.c[3], NULL);
2308     if (img) {
2309         img->next = event_images->imgs;
2310         event_images->imgs = img;
2311     }
2312 }
2313
2314 /**
2315  * \brief Main ass rendering function, glues everything together
2316  * \param event event to render
2317  * \param event_images struct containing resulting images, will also be initialized
2318  * Process event, appending resulting ASS_Image's to images_root.
2319  */
2320 static int
2321 ass_render_event(ASS_Renderer *render_priv, ASS_Event *event,
2322                  EventImages *event_images)
2323 {
2324     DBBox bbox;
2325     int MarginL, MarginR, MarginV;
2326     int valign;
2327     double device_x = 0;
2328     double device_y = 0;
2329     TextInfo *text_info = &render_priv->text_info;
2330
2331     if (event->Style >= render_priv->track->n_styles) {
2332         ass_msg(render_priv->library, MSGL_WARN, "No style found");
2333         return 1;
2334     }
2335     if (!event->Text) {
2336         ass_msg(render_priv->library, MSGL_WARN, "Empty event");
2337         return 1;
2338     }
2339
2340     free_render_context(render_priv);
2341     init_render_context(render_priv, event);
2342
2343     if (parse_events(render_priv, event))
2344         return 1;
2345
2346     if (text_info->length == 0) {
2347         // no valid symbols in the event; this can be smth like {comment}
2348         free_render_context(render_priv);
2349         return 1;
2350     }
2351
2352     // Find shape runs and shape text
2353     ass_shaper_set_base_direction(render_priv->shaper,
2354             resolve_base_direction(render_priv->state.font_encoding));
2355     ass_shaper_find_runs(render_priv->shaper, render_priv, text_info->glyphs,
2356             text_info->length);
2357     if (ass_shaper_shape(render_priv->shaper, text_info) < 0) {
2358         ass_msg(render_priv->library, MSGL_ERR, "Failed to shape text");
2359         free_render_context(render_priv);
2360         return 1;
2361     }
2362
2363     retrieve_glyphs(render_priv);
2364
2365     preliminary_layout(render_priv);
2366
2367     // depends on glyph x coordinates being monotonous, so it should be done before line wrap
2368     process_karaoke_effects(render_priv);
2369
2370     valign = render_priv->state.alignment & 12;
2371
2372     MarginL =
2373         (event->MarginL) ? event->MarginL : render_priv->state.style->MarginL;
2374     MarginR =
2375         (event->MarginR) ? event->MarginR : render_priv->state.style->MarginR;
2376     MarginV =
2377         (event->MarginV) ? event->MarginV : render_priv->state.style->MarginV;
2378
2379     // calculate max length of a line
2380     double max_text_width =
2381         x2scr(render_priv, render_priv->track->PlayResX - MarginR) -
2382         x2scr(render_priv, MarginL);
2383
2384     // wrap lines
2385     if (render_priv->state.evt_type != EVENT_HSCROLL) {
2386         // rearrange text in several lines
2387         wrap_lines_smart(render_priv, max_text_width);
2388     } else {
2389         // no breaking or wrapping, everything in a single line
2390         text_info->lines[0].offset = 0;
2391         text_info->lines[0].len = text_info->length;
2392         text_info->n_lines = 1;
2393         measure_text(render_priv);
2394     }
2395
2396     reorder_text(render_priv);
2397
2398     align_lines(render_priv, max_text_width);
2399
2400     // determing text bounding box
2401     compute_string_bbox(text_info, &bbox);
2402
2403     // determine device coordinates for text
2404
2405     // x coordinate for everything except positioned events
2406     if (render_priv->state.evt_type == EVENT_NORMAL ||
2407         render_priv->state.evt_type == EVENT_VSCROLL) {
2408         device_x = x2scr(render_priv, MarginL);
2409     } else if (render_priv->state.evt_type == EVENT_HSCROLL) {
2410         if (render_priv->state.scroll_direction == SCROLL_RL)
2411             device_x =
2412                 x2scr(render_priv,
2413                       render_priv->track->PlayResX -
2414                       render_priv->state.scroll_shift);
2415         else if (render_priv->state.scroll_direction == SCROLL_LR)
2416             device_x =
2417                 x2scr(render_priv,
2418                       render_priv->state.scroll_shift) - (bbox.xMax -
2419                                                           bbox.xMin);
2420     }
2421
2422     // y coordinate for everything except positioned events
2423     if (render_priv->state.evt_type == EVENT_NORMAL ||
2424         render_priv->state.evt_type == EVENT_HSCROLL) {
2425         if (valign == VALIGN_TOP) {     // toptitle
2426             device_y =
2427                 y2scr_top(render_priv,
2428                           MarginV) + text_info->lines[0].asc;
2429         } else if (valign == VALIGN_CENTER) {   // midtitle
2430             double scr_y =
2431                 y2scr(render_priv, render_priv->track->PlayResY / 2.0);
2432             device_y = scr_y - (bbox.yMax + bbox.yMin) / 2.0;
2433         } else {                // subtitle
2434             double line_pos = render_priv->state.explicit ?
2435                 0 : render_priv->settings.line_position;
2436             double scr_top, scr_bottom, scr_y0;
2437             if (valign != VALIGN_SUB)
2438                 ass_msg(render_priv->library, MSGL_V,
2439                        "Invalid valign, assuming 0 (subtitle)");
2440             scr_bottom =
2441                 y2scr_sub(render_priv,
2442                           render_priv->track->PlayResY - MarginV);
2443             scr_top = y2scr_top(render_priv, 0); //xxx not always 0?
2444             device_y = scr_bottom + (scr_top - scr_bottom) * line_pos / 100.0;
2445             device_y -= text_info->height;
2446             device_y += text_info->lines[0].asc;
2447             // clip to top to avoid confusion if line_position is very high,
2448             // turning the subtitle into a toptitle
2449             // also, don't change behavior if line_position is not used
2450             scr_y0 = scr_top + text_info->lines[0].asc;
2451             if (device_y < scr_y0 && line_pos > 0) {
2452                 device_y = scr_y0;
2453             }
2454         }
2455     } else if (render_priv->state.evt_type == EVENT_VSCROLL) {
2456         if (render_priv->state.scroll_direction == SCROLL_TB)
2457             device_y =
2458                 y2scr(render_priv,
2459                       render_priv->state.clip_y0 +
2460                       render_priv->state.scroll_shift) - (bbox.yMax -
2461                                                           bbox.yMin);
2462         else if (render_priv->state.scroll_direction == SCROLL_BT)
2463             device_y =
2464                 y2scr(render_priv,
2465                       render_priv->state.clip_y1 -
2466                       render_priv->state.scroll_shift);
2467     }
2468
2469     // positioned events are totally different
2470     if (render_priv->state.evt_type == EVENT_POSITIONED) {
2471         double base_x = 0;
2472         double base_y = 0;
2473         get_base_point(&bbox, render_priv->state.alignment, &base_x, &base_y);
2474         device_x =
2475             x2scr_pos(render_priv, render_priv->state.pos_x) - base_x;
2476         device_y =
2477             y2scr_pos(render_priv, render_priv->state.pos_y) - base_y;
2478     }
2479
2480     // fix clip coordinates (they depend on alignment)
2481     if (render_priv->state.evt_type == EVENT_NORMAL ||
2482         render_priv->state.evt_type == EVENT_HSCROLL ||
2483         render_priv->state.evt_type == EVENT_VSCROLL) {
2484         render_priv->state.clip_x0 =
2485             x2scr_scaled(render_priv, render_priv->state.clip_x0);
2486         render_priv->state.clip_x1 =
2487             x2scr_scaled(render_priv, render_priv->state.clip_x1);
2488         if (valign == VALIGN_TOP) {
2489             render_priv->state.clip_y0 =
2490                 y2scr_top(render_priv, render_priv->state.clip_y0);
2491             render_priv->state.clip_y1 =
2492                 y2scr_top(render_priv, render_priv->state.clip_y1);
2493         } else if (valign == VALIGN_CENTER) {
2494             render_priv->state.clip_y0 =
2495                 y2scr(render_priv, render_priv->state.clip_y0);
2496             render_priv->state.clip_y1 =
2497                 y2scr(render_priv, render_priv->state.clip_y1);
2498         } else if (valign == VALIGN_SUB) {
2499             render_priv->state.clip_y0 =
2500                 y2scr_sub(render_priv, render_priv->state.clip_y0);
2501             render_priv->state.clip_y1 =
2502                 y2scr_sub(render_priv, render_priv->state.clip_y1);
2503         }
2504     } else if (render_priv->state.evt_type == EVENT_POSITIONED) {
2505         render_priv->state.clip_x0 =
2506             x2scr_pos_scaled(render_priv, render_priv->state.clip_x0);
2507         render_priv->state.clip_x1 =
2508             x2scr_pos_scaled(render_priv, render_priv->state.clip_x1);
2509         render_priv->state.clip_y0 =
2510             y2scr_pos(render_priv, render_priv->state.clip_y0);
2511         render_priv->state.clip_y1 =
2512             y2scr_pos(render_priv, render_priv->state.clip_y1);
2513     }
2514
2515     if (render_priv->state.explicit) {
2516         // we still need to clip against screen boundaries
2517         double zx = x2scr_pos_scaled(render_priv, 0);
2518         double zy = y2scr_pos(render_priv, 0);
2519         double sx = x2scr_pos_scaled(render_priv, render_priv->track->PlayResX);
2520         double sy = y2scr_pos(render_priv, render_priv->track->PlayResY);
2521
2522         render_priv->state.clip_x0 = render_priv->state.clip_x0 < zx ? zx : render_priv->state.clip_x0;
2523         render_priv->state.clip_y0 = render_priv->state.clip_y0 < zy ? zy : render_priv->state.clip_y0;
2524         render_priv->state.clip_x1 = render_priv->state.clip_x1 > sx ? sx : render_priv->state.clip_x1;
2525         render_priv->state.clip_y1 = render_priv->state.clip_y1 > sy ? sy : render_priv->state.clip_y1;
2526     }
2527
2528     calculate_rotation_params(render_priv, &bbox, device_x, device_y);
2529
2530     render_and_combine_glyphs(render_priv, device_x, device_y);
2531
2532     memset(event_images, 0, sizeof(*event_images));
2533     event_images->top = device_y - text_info->lines[0].asc;
2534     event_images->height = text_info->height;
2535     event_images->left =
2536         (device_x + bbox.xMin * render_priv->font_scale_x) + 0.5;
2537     event_images->width =
2538         (bbox.xMax - bbox.xMin) * render_priv->font_scale_x + 0.5;
2539     event_images->detect_collisions = render_priv->state.detect_collisions;
2540     event_images->shift_direction = (valign == VALIGN_TOP) ? 1 : -1;
2541     event_images->event = event;
2542     event_images->imgs = render_text(render_priv);
2543
2544     if (render_priv->state.border_style == 4)
2545         add_background(render_priv, event_images);
2546
2547     ass_shaper_cleanup(render_priv->shaper, text_info);
2548     free_render_context(render_priv);
2549
2550     return 0;
2551 }
2552
2553 /**
2554  * \brief Check cache limits and reset cache if they are exceeded
2555  */
2556 static void check_cache_limits(ASS_Renderer *priv, CacheStore *cache)
2557 {
2558     ass_cache_cut(cache->composite_cache, cache->composite_max_size);
2559     ass_cache_cut(cache->bitmap_cache, cache->bitmap_max_size);
2560     ass_cache_cut(cache->outline_cache, cache->glyph_max);
2561 }
2562
2563 /**
2564  * \brief Start a new frame
2565  */
2566 static int
2567 ass_start_frame(ASS_Renderer *render_priv, ASS_Track *track,
2568                 long long now)
2569 {
2570     ASS_Settings *settings_priv = &render_priv->settings;
2571
2572     if (!render_priv->settings.frame_width
2573         && !render_priv->settings.frame_height)
2574         return 1;               // library not initialized
2575
2576     if (!render_priv->fontselect)
2577         return 1;
2578
2579     if (render_priv->library != track->library)
2580         return 1;
2581
2582     if (track->n_events == 0)
2583         return 1;               // nothing to do
2584
2585     render_priv->track = track;
2586     render_priv->time = now;
2587
2588     ass_lazy_track_init(render_priv->library, render_priv->track);
2589
2590     ass_shaper_set_kerning(render_priv->shaper, track->Kerning);
2591     ass_shaper_set_language(render_priv->shaper, track->Language);
2592     ass_shaper_set_level(render_priv->shaper, render_priv->settings.shaper);
2593
2594     // PAR correction
2595     double par = render_priv->settings.par;
2596     if (par == 0.) {
2597         if (settings_priv->frame_width && settings_priv->frame_height &&
2598             settings_priv->storage_width && settings_priv->storage_height) {
2599             double dar = ((double) settings_priv->frame_width) /
2600                          settings_priv->frame_height;
2601             double sar = ((double) settings_priv->storage_width) /
2602                          settings_priv->storage_height;
2603             par = sar / dar;
2604         } else
2605             par = 1.0;
2606     }
2607     render_priv->font_scale_x = par;
2608
2609     render_priv->prev_images_root = render_priv->images_root;
2610     render_priv->images_root = NULL;
2611
2612     check_cache_limits(render_priv, &render_priv->cache);
2613
2614     return 0;
2615 }
2616
2617 static int cmp_event_layer(const void *p1, const void *p2)
2618 {
2619     ASS_Event *e1 = ((EventImages *) p1)->event;
2620     ASS_Event *e2 = ((EventImages *) p2)->event;
2621     if (e1->Layer < e2->Layer)
2622         return -1;
2623     if (e1->Layer > e2->Layer)
2624         return 1;
2625     if (e1->ReadOrder < e2->ReadOrder)
2626         return -1;
2627     if (e1->ReadOrder > e2->ReadOrder)
2628         return 1;
2629     return 0;
2630 }
2631
2632 static ASS_RenderPriv *get_render_priv(ASS_Renderer *render_priv,
2633                                        ASS_Event *event)
2634 {
2635     if (!event->render_priv) {
2636         event->render_priv = calloc(1, sizeof(ASS_RenderPriv));
2637         if (!event->render_priv)
2638             return NULL;
2639     }
2640     if (render_priv->render_id != event->render_priv->render_id) {
2641         memset(event->render_priv, 0, sizeof(ASS_RenderPriv));
2642         event->render_priv->render_id = render_priv->render_id;
2643     }
2644
2645     return event->render_priv;
2646 }
2647
2648 static int overlap(Segment *s1, Segment *s2)
2649 {
2650     if (s1->a >= s2->b || s2->a >= s1->b ||
2651         s1->ha >= s2->hb || s2->ha >= s1->hb)
2652         return 0;
2653     return 1;
2654 }
2655
2656 static int cmp_segment(const void *p1, const void *p2)
2657 {
2658     return ((Segment *) p1)->a - ((Segment *) p2)->a;
2659 }
2660
2661 static void
2662 shift_event(ASS_Renderer *render_priv, EventImages *ei, int shift)
2663 {
2664     ASS_Image *cur = ei->imgs;
2665     while (cur) {
2666         cur->dst_y += shift;
2667         // clip top and bottom
2668         if (cur->dst_y < 0) {
2669             int clip = -cur->dst_y;
2670             cur->h -= clip;
2671             cur->bitmap += clip * cur->stride;
2672             cur->dst_y = 0;
2673         }
2674         if (cur->dst_y + cur->h >= render_priv->height) {
2675             int clip = cur->dst_y + cur->h - render_priv->height;
2676             cur->h -= clip;
2677         }
2678         if (cur->h <= 0) {
2679             cur->h = 0;
2680             cur->dst_y = 0;
2681         }
2682         cur = cur->next;
2683     }
2684     ei->top += shift;
2685 }
2686
2687 // dir: 1 - move down
2688 //      -1 - move up
2689 static int fit_segment(Segment *s, Segment *fixed, int *cnt, int dir)
2690 {
2691     int i;
2692     int shift = 0;
2693
2694     if (dir == 1)               // move down
2695         for (i = 0; i < *cnt; ++i) {
2696             if (s->b + shift <= fixed[i].a || s->a + shift >= fixed[i].b ||
2697                 s->hb <= fixed[i].ha || s->ha >= fixed[i].hb)
2698                 continue;
2699             shift = fixed[i].b - s->a;
2700     } else                      // dir == -1, move up
2701         for (i = *cnt - 1; i >= 0; --i) {
2702             if (s->b + shift <= fixed[i].a || s->a + shift >= fixed[i].b ||
2703                 s->hb <= fixed[i].ha || s->ha >= fixed[i].hb)
2704                 continue;
2705             shift = fixed[i].a - s->b;
2706         }
2707
2708     fixed[*cnt].a = s->a + shift;
2709     fixed[*cnt].b = s->b + shift;
2710     fixed[*cnt].ha = s->ha;
2711     fixed[*cnt].hb = s->hb;
2712     (*cnt)++;
2713     qsort(fixed, *cnt, sizeof(Segment), cmp_segment);
2714
2715     return shift;
2716 }
2717
2718 static void
2719 fix_collisions(ASS_Renderer *render_priv, EventImages *imgs, int cnt)
2720 {
2721     Segment *used = ass_realloc_array(NULL, cnt, sizeof(*used));
2722     int cnt_used = 0;
2723     int i, j;
2724
2725     if (!used)
2726         return;
2727
2728     // fill used[] with fixed events
2729     for (i = 0; i < cnt; ++i) {
2730         ASS_RenderPriv *priv;
2731         if (!imgs[i].detect_collisions)
2732             continue;
2733         priv = get_render_priv(render_priv, imgs[i].event);
2734         if (priv && priv->height > 0) { // it's a fixed event
2735             Segment s;
2736             s.a = priv->top;
2737             s.b = priv->top + priv->height;
2738             s.ha = priv->left;
2739             s.hb = priv->left + priv->width;
2740             if (priv->height != imgs[i].height) {       // no, it's not
2741                 ass_msg(render_priv->library, MSGL_WARN,
2742                         "Event height has changed");
2743                 priv->top = 0;
2744                 priv->height = 0;
2745                 priv->left = 0;
2746                 priv->width = 0;
2747             }
2748             for (j = 0; j < cnt_used; ++j)
2749                 if (overlap(&s, used + j)) {    // no, it's not
2750                     priv->top = 0;
2751                     priv->height = 0;
2752                     priv->left = 0;
2753                     priv->width = 0;
2754                 }
2755             if (priv->height > 0) {     // still a fixed event
2756                 used[cnt_used].a = priv->top;
2757                 used[cnt_used].b = priv->top + priv->height;
2758                 used[cnt_used].ha = priv->left;
2759                 used[cnt_used].hb = priv->left + priv->width;
2760                 cnt_used++;
2761                 shift_event(render_priv, imgs + i, priv->top - imgs[i].top);
2762             }
2763         }
2764     }
2765     qsort(used, cnt_used, sizeof(Segment), cmp_segment);
2766
2767     // try to fit other events in free spaces
2768     for (i = 0; i < cnt; ++i) {
2769         ASS_RenderPriv *priv;
2770         if (!imgs[i].detect_collisions)
2771             continue;
2772         priv = get_render_priv(render_priv, imgs[i].event);
2773         if (priv && priv->height == 0) {        // not a fixed event
2774             int shift;
2775             Segment s;
2776             s.a = imgs[i].top;
2777             s.b = imgs[i].top + imgs[i].height;
2778             s.ha = imgs[i].left;
2779             s.hb = imgs[i].left + imgs[i].width;
2780             shift = fit_segment(&s, used, &cnt_used, imgs[i].shift_direction);
2781             if (shift)
2782                 shift_event(render_priv, imgs + i, shift);
2783             // make it fixed
2784             priv->top = imgs[i].top;
2785             priv->height = imgs[i].height;
2786             priv->left = imgs[i].left;
2787             priv->width = imgs[i].width;
2788         }
2789
2790     }
2791
2792     free(used);
2793 }
2794
2795 /**
2796  * \brief compare two images
2797  * \param i1 first image
2798  * \param i2 second image
2799  * \return 0 if identical, 1 if different positions, 2 if different content
2800  */
2801 static int ass_image_compare(ASS_Image *i1, ASS_Image *i2)
2802 {
2803     if (i1->w != i2->w)
2804         return 2;
2805     if (i1->h != i2->h)
2806         return 2;
2807     if (i1->stride != i2->stride)
2808         return 2;
2809     if (i1->color != i2->color)
2810         return 2;
2811     if (i1->bitmap != i2->bitmap)
2812         return 2;
2813     if (i1->dst_x != i2->dst_x)
2814         return 1;
2815     if (i1->dst_y != i2->dst_y)
2816         return 1;
2817     return 0;
2818 }
2819
2820 /**
2821  * \brief compare current and previous image list
2822  * \param priv library handle
2823  * \return 0 if identical, 1 if different positions, 2 if different content
2824  */
2825 static int ass_detect_change(ASS_Renderer *priv)
2826 {
2827     ASS_Image *img, *img2;
2828     int diff;
2829
2830     img = priv->prev_images_root;
2831     img2 = priv->images_root;
2832     diff = 0;
2833     while (img && diff < 2) {
2834         ASS_Image *next, *next2;
2835         next = img->next;
2836         if (img2) {
2837             int d = ass_image_compare(img, img2);
2838             if (d > diff)
2839                 diff = d;
2840             next2 = img2->next;
2841         } else {
2842             // previous list is shorter
2843             diff = 2;
2844             break;
2845         }
2846         img = next;
2847         img2 = next2;
2848     }
2849
2850     // is the previous list longer?
2851     if (img2)
2852         diff = 2;
2853
2854     return diff;
2855 }
2856
2857 /**
2858  * \brief render a frame
2859  * \param priv library handle
2860  * \param track track
2861  * \param now current video timestamp (ms)
2862  * \param detect_change a value describing how the new images differ from the previous ones will be written here:
2863  *        0 if identical, 1 if different positions, 2 if different content.
2864  *        Can be NULL, in that case no detection is performed.
2865  */
2866 ASS_Image *ass_render_frame(ASS_Renderer *priv, ASS_Track *track,
2867                             long long now, int *detect_change)
2868 {
2869     int i, cnt, rc;
2870     EventImages *last;
2871     ASS_Image **tail;
2872
2873     // init frame
2874     rc = ass_start_frame(priv, track, now);
2875     if (rc != 0) {
2876         if (detect_change) {
2877             *detect_change = 2;
2878         }
2879         return NULL;
2880     }
2881
2882     // render events separately
2883     cnt = 0;
2884     for (i = 0; i < track->n_events; ++i) {
2885         ASS_Event *event = track->events + i;
2886         if ((event->Start <= now)
2887             && (now < (event->Start + event->Duration))) {
2888             if (cnt >= priv->eimg_size) {
2889                 priv->eimg_size += 100;
2890                 priv->eimg =
2891                     realloc(priv->eimg,
2892                             priv->eimg_size * sizeof(EventImages));
2893             }
2894             rc = ass_render_event(priv, event, priv->eimg + cnt);
2895             if (!rc)
2896                 ++cnt;
2897         }
2898     }
2899
2900     // sort by layer
2901     qsort(priv->eimg, cnt, sizeof(EventImages), cmp_event_layer);
2902
2903     // call fix_collisions for each group of events with the same layer
2904     last = priv->eimg;
2905     for (i = 1; i < cnt; ++i)
2906         if (last->event->Layer != priv->eimg[i].event->Layer) {
2907             fix_collisions(priv, last, priv->eimg + i - last);
2908             last = priv->eimg + i;
2909         }
2910     if (cnt > 0)
2911         fix_collisions(priv, last, priv->eimg + cnt - last);
2912
2913     // concat lists
2914     tail = &priv->images_root;
2915     for (i = 0; i < cnt; ++i) {
2916         ASS_Image *cur = priv->eimg[i].imgs;
2917         while (cur) {
2918             *tail = cur;
2919             tail = &cur->next;
2920             cur = cur->next;
2921         }
2922     }
2923     ass_frame_ref(priv->images_root);
2924
2925     if (detect_change)
2926         *detect_change = ass_detect_change(priv);
2927
2928     // free the previous image list
2929     ass_frame_unref(priv->prev_images_root);
2930     priv->prev_images_root = NULL;
2931
2932     return priv->images_root;
2933 }
2934
2935 /**
2936  * \brief Add reference to a frame image list.
2937  * \param image_list image list returned by ass_render_frame()
2938  */
2939 void ass_frame_ref(ASS_Image *img)
2940 {
2941     if (!img)
2942         return;
2943     ((ASS_ImagePriv *) img)->ref_count++;
2944 }
2945
2946 /**
2947  * \brief Release reference to a frame image list.
2948  * \param image_list image list returned by ass_render_frame()
2949  */
2950 void ass_frame_unref(ASS_Image *img)
2951 {
2952     if (!img || --((ASS_ImagePriv *) img)->ref_count)
2953         return;
2954     do {
2955         ASS_ImagePriv *priv = (ASS_ImagePriv *) img;
2956         img = img->next;
2957         if (priv->source)
2958             ass_cache_dec_ref(priv->source);
2959         else
2960             ass_aligned_free(priv->result.bitmap);
2961         free(priv);
2962     } while (img);
2963 }