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