1 /*
2     Layout and rendering of subtitles for spumux
3 */
4 /* Copyright (C) 2000 - 2003 various authors of the MPLAYER project
5  * This module uses various parts of the MPLAYER project (http://www.mplayerhq.hu)
6  * With many changes by Sjef van Gool (svangool@hotmail.com) November 2003
7  * Major rework by Lawrence D'Oliveiro <ldo@geek-central.gen.nz>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or (at
12  * your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22  * MA 02110-1301 USA.
23  */
24 
25 /* Generic alpha renderers for all YUV modes and RGB depths.
26  * Optimized by Nick and Michael
27  * Code from Michael Niedermayer (michaelni@gmx.at) is under GPL
28  */
29 
30 #include "config.h"
31 
32 #include "compat.h"
33 
34 #include "subglobals.h"
35 #include "subrender.h"
36 #include "subfont.h"
37 
38 #define NEW_SPLITTING
39 
40 typedef struct mp_osd_bbox_s
41   {
42     int x1, y1; /* top left */
43     int x2, y2; /* bottom right */
44   } mp_osd_bbox_t;
45 
46 #define MAX_UCS 1600
47 #define MAX_UCSLINES 16
48 
49 typedef struct /* for holding and maintaining a rendered subtitle image */
50   {
51     int topy;
52     mp_osd_bbox_t bbox; // bounding box
53     union
54       {
55         struct /* only one used by spumux */
56           { /* subtitle text lines already laid out ready for rendering */
57             int utbl[MAX_UCS + 1]; /* all display lines concatenated, each terminated by a null */
58             int xtbl[MAX_UCSLINES]; /* x-positions of lines for centre alignment */
59             int lines;          // no. of lines
60           } subtitle;
61       } params;
62     int stride; /* bytes per row of both alpha and bitmap buffers */
63     int allocated; /* size in bytes of each buffer */
64     unsigned char *bitmap_buffer; /* four bytes per pixel */
65   } mp_osd_obj_t;
66 
67 static int sub_pos=100;
68 /* static int sub_width_p=100; */
69 
70 int sub_justify=1; /* fixme: not user-settable */
71 int sub_left_margin=60;   /* Size of left horizontal non-display area in pixel units */
72 int sub_right_margin=60;  /* Size of right horizontal non-display area in pixel units */
73 int sub_bottom_margin=30; /* Size of bottom horizontal non-display area in pixel units */
74 int sub_top_margin=20;    /* Size of top horizontal non-display area in pixel units */
75 int h_sub_alignment = H_SUB_ALIGNMENT_LEFT;  /* Horizontal alignment 0=center, 1=left, 2=right, 4=subtitle default */
76 int v_sub_alignment = V_SUB_ALIGNMENT_BOTTOM;      /* Vertical alignment 0=top, 1=center, 2=bottom */
77 /* following default according to default_video_format if not explicitly set by user: */
78 float movie_fps = 0.0;
79 int movie_width = 0;
80 int movie_height = 0;
81 
82 unsigned char *textsub_image_buffer;
83 size_t textsub_image_buffer_size;
84 
85 /* statistics */
86 int sub_max_chars;
87 int sub_max_lines;
88 int sub_max_font_height;
89 int sub_max_bottom_font_height;
90 
91 static mp_osd_obj_t* vo_osd = NULL;
92 
vo_draw_subtitle_line(int w,int h,const unsigned char * srcbase,int srcstride,unsigned char * dstbase,int dststride)93 static inline void vo_draw_subtitle_line
94   (
95     int w, /* dimensions of area to copy */
96     int h,
97     const unsigned char * srcbase, /* source pixels */
98     int srcstride, /* for srcbase */
99     unsigned char * dstbase, /* where to copy to */
100     int dststride /* for dstbase */
101   )
102   /* copies pixels from srcbase onto dstbase. Used to transfer a complete
103     rendered line to textsub_image_buffer. */
104   {
105     int y;
106     for (y = 0; y < h; y++)
107       {
108         const register unsigned char * src = srcbase;
109         register unsigned char * dst = dstbase;
110         register int x;
111         for (x = 0; x < w; x++)
112           {
113             *dst++ = *src++;
114             *dst++ = *src++;
115             *dst++ = *src++;
116             *dst++ = *src++;
117           } /*for*/
118         srcbase += srcstride;
119         dstbase += dststride;
120       } /*for*/
121   } /*vo_draw_subtitle_line*/
122 
draw_glyph(mp_osd_obj_t * obj,int x0,int y0,int w,int h,const unsigned char * src,const colorspec * srccolors,int stride)123 static void draw_glyph
124   (
125     mp_osd_obj_t * obj,
126     int x0, /* origin in destination buffer to copy to */
127     int y0,
128     int w, /* dimensions of area to copy */
129     int h,
130     const unsigned char * src, /* source pixels */
131     const colorspec * srccolors, /* source palette */
132     int stride /* of source */
133   )
134   /* used to assemble complete rendered screen lines in obj by copying individual
135     glyph images. */
136   {
137     int dststride = obj->stride;
138     int dstskip = obj->stride - w * 4;
139     int srcskip = stride - w;
140     int i, j;
141     unsigned char * bdst =
142             obj->bitmap_buffer
143         +
144             (y0 - obj->bbox.y1) * dststride
145         +
146             (x0 - obj->bbox.x1) * 4;
147     const unsigned char * bsrc = src;
148   /* fprintf(stderr, "***w:%d x0:%d bbx1:%d bbx2:%d dstsstride:%d y0:%d h:%d bby1:%d bby2:%d ofs:%d ***\n",w,x0,obj->bbox.x1,obj->bbox.x2,dststride,y0,h,obj->bbox.y1,obj->bbox.y2,(y0-obj->bbox.y1)*dststride + (x0-obj->bbox.x1));*/
149     if (x0 < obj->bbox.x1 || x0 + w > obj->bbox.x2 || y0 < obj->bbox.y1 || y0 + h > obj->bbox.y2)
150       {
151         fprintf
152           (
153             stderr,
154             "WARN: Text out of range: bbox [%d %d %d %d], txt [%d %d %d %d]\n",
155             obj->bbox.x1, obj->bbox.x2, obj->bbox.y1, obj->bbox.y2,
156             x0, x0 + w, y0, y0 + h
157           );
158         return;
159       } /*if*/
160     for (i = 0; i < h; i++)
161       {
162         for (j = 0; j < w; j++)
163           {
164             const colorspec srccolor = srccolors[*bsrc++];
165             if (srccolor.a != 0)
166               {
167                 *bdst++ = srccolor.r;
168                 *bdst++ = srccolor.g;
169                 *bdst++ = srccolor.b;
170                 *bdst++ = srccolor.a;
171               }
172             else
173               {
174                 bdst += 4;
175               } /*if*/
176           } /*for*/
177         bdst += dstskip;
178         bsrc += srcskip;
179       } /*for*/
180   } /*draw_glyph*/
181 
alloc_buf(mp_osd_obj_t * obj)182 static void alloc_buf(mp_osd_obj_t * obj)
183   /* (re)allocates pixel buffers to be large enough for bbox. */
184   {
185     int len;
186   /* fprintf(stderr,"x1:%d x2:%d y1:%d y2:%d\n",obj->bbox.x1,obj->bbox.x2,obj->bbox.y1,obj->bbox.y2); */
187     if (obj->bbox.x2 < obj->bbox.x1)
188         obj->bbox.x2 = obj->bbox.x1;
189     if (obj->bbox.y2 < obj->bbox.y1)
190         obj->bbox.y2 = obj->bbox.y1;
191     obj->stride = (obj->bbox.x2 - obj->bbox.x1) * 4 + 7 & ~7; /* round up to multiple of 8 bytes--why bother? */
192     len = obj->stride * (obj->bbox.y2 - obj->bbox.y1);
193     if (obj->allocated < len)
194       {
195       /* allocate new, bigger buffers, don't bother preserving contents of old ones */
196         obj->allocated = len;
197         free(obj->bitmap_buffer);
198         obj->bitmap_buffer = (unsigned char *)malloc(len);
199       } /*if*/
200     memset(obj->bitmap_buffer, 0, len);
201   } /*alloc_buf*/
202 
vo_update_text_sub(mp_osd_obj_t * obj,const subtitle_elt * the_sub)203 inline static void vo_update_text_sub
204   (
205     mp_osd_obj_t * obj,
206     const subtitle_elt * the_sub
207   )
208   /* lays out and renders the subtitle text from the_sub using the font settings from vo_font,
209     putting the results into obj. */
210   {
211     // Structures needed for the new splitting algorithm.
212     // osd_text_word contains the single subtitle word.
213     // osd_text_line is used to mark the lines of subtitles
214     struct osd_text_word
215       {
216         int osd_kerning; //kerning with the previous word
217         int osd_length;  //horizontal length inside the bbox
218         int text_length; //number of characters
219         int *text;       //characters
220         struct osd_text_word *prev, *next; /* doubly-linked list of all words on all lines */
221       };
222     struct osd_text_line
223       {
224         int linewidth;
225         struct osd_text_word *words; /* where in word list this line starts */
226         struct osd_text_line *prev, *next; /* doubly-linked list */
227       };
228     int linedone, linesleft;
229     bool warn_overlong_word;
230     int textlen, sub_totallen;
231   /* const int widthlimit = movie_width * sub_width_p / 100; */
232     const int widthlimit = movie_width - sub_right_margin - sub_left_margin;
233       /* maximum width of display lines after deducting space for margins
234         and starting point */
235     int xmin = widthlimit, xmax = 0;
236     int max_line_height;
237     int xtblc, utblc;
238 
239     if (!the_sub || !vo_font)
240       {
241         return;
242       } /*if*/
243     obj->bbox.y2 = obj->topy = movie_height - sub_bottom_margin;
244     obj->params.subtitle.lines = 0;
245 
246     // too long lines divide into a smaller ones
247     linedone = sub_totallen = 0;
248     max_line_height = vo_font->height;
249       /* actually a waste of time computing this when I don't allow mixing fonts */
250     linesleft = the_sub->lines;
251       {
252         struct osd_text_line
253           // these are used to store the whole sub text osd
254             *otp_sub = NULL, /* head of list */
255             *otp_sub_last = NULL; /* last element of list */
256         int *wordbuf = NULL;
257         while (linesleft)
258           { /* split next subtitle line into words */
259             struct osd_text_word
260                 *osl, /* head of list */
261                 *osl_tail; /* last element of list */
262             int chindex, prevch, wordlen;
263             const unsigned char *text;
264             int xsize = -vo_font->charspace;
265               /* cancels out extra space left before first word of line */
266             linesleft--;
267             text = (const unsigned char *)the_sub->text[linedone++];
268             textlen = strlen((const char *)text);
269             wordlen = 0;
270             wordbuf = (int *)realloc(wordbuf, textlen * sizeof(int));
271             prevch = -1;
272             osl = NULL;
273             osl_tail = NULL;
274             warn_overlong_word = true;
275             // reading the subtitle words from the_sub->text[]
276             chindex = 0;
277             for (;;) /* split line into words */
278               {
279                 int curch;
280                 if (chindex < textlen)
281                   {
282                     curch = text[chindex];
283                     if (curch >= 0x80)
284                       {
285                       /* fixme: no checking for chindex going out of range */
286                         if ((curch & 0xe0) == 0xc0)    /* 2 bytes U+00080..U+0007FF*/
287                             curch = (curch & 0x1f) << 6 | (text[++chindex] & 0x3f);
288                         else if ((curch & 0xf0) == 0xe0) /* 3 bytes U+00800..U+00FFFF*/
289                           {
290                             curch = (((curch & 0x0f) << 6) | (text[++chindex] & 0x3f)) << 6;
291                             curch |= text[++chindex] & 0x3f;
292                           } /*if*/
293                       } /*if*/
294                     if (sub_totallen == MAX_UCS)
295                       {
296                         textlen = chindex; // end here
297                         fprintf(stderr, "WARN: MAX_UCS exceeded!\n");
298                       } /*if*/
299                     if (!curch)
300                         curch++; // avoid UCS 0
301                     render_one_glyph(vo_font, curch); /* ensure I have an image for it */
302                   } /*if*/
303                 if (chindex >= textlen || curch == ' ')
304                   {
305                   /* word break */
306                     struct osd_text_word * const newelt =
307                         (struct osd_text_word *)calloc(1, sizeof(struct osd_text_word));
308                     int counter;
309                     if (osl == NULL)
310                       {
311                       /* first element on list */
312                         osl = newelt;
313                       }
314                     else
315                       {
316                       /* link to previous elements */
317                         newelt->prev = osl_tail;
318                         osl_tail->next = newelt;
319                         newelt->osd_kerning = vo_font->charspace + vo_font->width[' '];
320                       } /*if*/
321                     osl_tail = newelt;
322                     newelt->osd_length = xsize;
323                     newelt->text_length = wordlen;
324                     newelt->text = (int *)malloc(wordlen * sizeof(int));
325                     for (counter = 0; counter < wordlen; ++counter)
326                         newelt->text[counter] = wordbuf[counter];
327                     wordlen = 0;
328                     if (chindex == textlen)
329                         break;
330                     xsize = 0;
331                     prevch = curch;
332                   }
333                 else
334                   {
335                   /* continue accumulating word */
336                     const int delta_xsize =
337                             vo_font->width[curch]
338                         +
339                             vo_font->charspace
340                         +
341                             kerning(vo_font, prevch, curch);
342                       /* width which will be added to word by this character */
343                     if (xsize + delta_xsize <= widthlimit)
344                       {
345                       /* word still fits in available width */
346                         if (!warn_overlong_word)
347                             warn_overlong_word = true;
348                         prevch = curch;
349                         wordbuf[wordlen++] = curch;
350                         xsize += delta_xsize;
351                         if (!suboverlap_enabled)
352                           {
353                           /* keep track of line heights to ensure no overlap */
354                             const int font = vo_font->font[curch];
355                             if (font >= 0 && vo_font->pic_b[font]->h > max_line_height)
356                               {
357                                 max_line_height = vo_font->pic_b[font]->h;
358                               } /*if*/
359                           } /*if*/
360                       }
361                     else
362                       {
363                       /* truncate word to fit */
364                         if (warn_overlong_word)
365                           {
366                             fprintf(stderr, "WARN: Subtitle word '%s' too long!\n", text);
367                             warn_overlong_word = false; /* only warn once per line */
368                           } /*if*/
369                       } /*if*/
370                   } /*if*/
371                 ++chindex;
372               } /*for*/
373         // osl holds an ordered (as they appear in the lines) chain of the subtitle words
374             if (osl != NULL) /* will always be true! */
375               {
376               /* collect words of this line into one or more on-screen lines,
377                 wrapping too-long lines */
378                 int linewidth = 0, linewidth_variation = 0;
379                 struct osd_text_line *lastnewelt;
380                 struct osd_text_word *curword;
381                 struct osd_text_line *otp_new;
382                 // otp_new will contain the chain of the osd subtitle lines coming from the single the_sub line.
383                 otp_new = lastnewelt = (struct osd_text_line *)calloc(1, sizeof(struct osd_text_line));
384                 lastnewelt->words = osl;
385                 curword = lastnewelt->words;
386                 for (;;)
387                   {
388                     while
389                       (
390                             curword != NULL
391                         &&
392                             linewidth + curword->osd_kerning + curword->osd_length <= widthlimit
393                       )
394                       {
395                       /* include another word on this line */
396                         linewidth += curword->osd_kerning + curword->osd_length;
397                         curword = curword->next;
398                       } /*while*/
399                     if
400                       (
401                             curword != NULL
402                         &&
403                             curword != lastnewelt->words
404                               /* ensure new line contains at least one word (fix Ubuntu bug 385187) */
405                       )
406                       {
407                       /* append yet another new display line onto otp_new chain */
408                         struct osd_text_line * const nextnewelt =
409                             (struct osd_text_line *)calloc(1, sizeof(struct osd_text_line));
410                         lastnewelt->linewidth = linewidth;
411                         lastnewelt->next = nextnewelt;
412                         nextnewelt->prev = lastnewelt;
413                         lastnewelt = nextnewelt;
414                         lastnewelt->words = curword;
415                         linewidth = -2 * vo_font->charspace - vo_font->width[' '];
416                       }
417                     else
418                       {
419                         lastnewelt->linewidth = linewidth;
420                         break;
421                       } /*if*/
422                   } /*for*/
423 #ifdef NEW_SPLITTING
424               /* rebalance split among multiple onscreen lines corresponding to a single
425                 subtitle line */
426                 // linewidth_variation holds the 'sum of the differences in length among the lines',
427                 // a measure of the eveness of the lengths of the lines
428                   {
429                     struct osd_text_line *tmp_otp;
430                     for (tmp_otp = otp_new; tmp_otp->next != NULL; tmp_otp = tmp_otp->next)
431                       {
432                         const struct osd_text_line * pmt = tmp_otp->next;
433                         while (pmt != NULL)
434                           {
435                             linewidth_variation += abs(tmp_otp->linewidth - pmt->linewidth);
436                             pmt = pmt->next;
437                           } /*while*/
438                       } /*for*/
439                   }
440                 if (otp_new->next != NULL) /* line split into more than one display line */
441                   {
442                     // until the last word of a line can be moved to the beginning of following line
443                     // reducing the 'sum of the differences in length among the lines', it is done
444                     for (;;)
445                       /* even out variations in width of screen lines corresponding to
446                         a single subtitle line */
447                       {
448                         struct osd_text_line *this_display_line;
449                         bool exit1 = true; /* initial assumption */
450                         struct osd_text_line *rebalance_line = NULL;
451                           /* if non-null, then word at end of this line should be moved to
452                             following line */
453                         for
454                           (
455                             this_display_line = otp_new;
456                             this_display_line->next != NULL;
457                             this_display_line = this_display_line->next
458                           )
459                           {
460                             struct osd_text_line *next_display_line = this_display_line->next;
461                             struct osd_text_word *prev_word;
462                             for
463                               (
464                                 prev_word = this_display_line->words;
465                                 prev_word->next != next_display_line->words;
466                                 prev_word = prev_word->next
467                               )
468                               /* find predecessor word to next_display_line */;
469                               /* seems a shame I can't make use of the doubly-linked lists
470                                 somehow to speed this up */
471                             if
472                               (
473                                         next_display_line->linewidth
474                                     +
475                                         prev_word->osd_length
476                                     +
477                                         next_display_line->words->osd_kerning
478                                 <=
479                                     widthlimit
480                               )
481                               {
482                               /* prev_word can be moved from this_display_line line onto
483                                 next_display_line line; see if doing this improves the layout */
484                                 struct osd_text_line *that_display_line;
485                                 int new_variation;
486                                 int prev_line_width, cur_line_width;
487                                 prev_line_width = this_display_line->linewidth;
488                                 cur_line_width = next_display_line->linewidth;
489                               /* temporary change to line widths to see effect of new layout */
490                                 this_display_line->linewidth =
491                                         prev_line_width
492                                     -
493                                         prev_word->osd_length
494                                     -
495                                         prev_word->osd_kerning;
496                                 next_display_line->linewidth =
497                                         cur_line_width
498                                     +
499                                         prev_word->osd_length
500                                     +
501                                         next_display_line->words->osd_kerning;
502                                 new_variation = 0;
503                                 for
504                                   (
505                                     that_display_line = otp_new;
506                                     that_display_line->next != NULL;
507                                     that_display_line = that_display_line->next
508                                   )
509                                   {
510                                     next_display_line = that_display_line->next;
511                                     while (next_display_line != NULL)
512                                       {
513                                         new_variation +=
514                                             abs
515                                               (
516                                                     that_display_line->linewidth
517                                                 -
518                                                     next_display_line->linewidth
519                                               );
520                                         next_display_line = next_display_line->next;
521                                       } /*while*/
522                                   } /*for*/
523                                 if (new_variation < linewidth_variation)
524                                   {
525                                   /* implement this new layout unless I find something better */
526                                     linewidth_variation = new_variation;
527                                     rebalance_line = this_display_line;
528                                     exit1 = false;
529                                   } /*if*/
530                               /* undo the temporary line width changes */
531                                 this_display_line->linewidth = prev_line_width;
532                                 this_display_line->next->linewidth = cur_line_width;
533                               } /*if*/
534                           } /*for*/
535                         // merging
536                         if (exit1) /* no improvement found */
537                             break;
538                           {
539                           /* word at end of rebalance_line line should be moved to following line */
540                             struct osd_text_word *word_to_move;
541                             struct osd_text_line *next_display_line;
542                             this_display_line = rebalance_line;
543                             next_display_line = this_display_line->next;
544                             for
545                               (
546                                 word_to_move = this_display_line->words;
547                                 word_to_move->next != next_display_line->words;
548                                 word_to_move = word_to_move->next
549                               )
550                               /* find previous word to be moved to this line */;
551                               /* seems a shame I can't make use of the doubly-linked lists
552                                 somehow to speed this up, not to mention having to do
553                                 it twice */
554                             this_display_line->linewidth -=
555                                 word_to_move->osd_length + word_to_move->osd_kerning;
556                             next_display_line->linewidth +=
557                                 word_to_move->osd_length + next_display_line->words->osd_kerning;
558                             next_display_line->words = word_to_move;
559                           } //~merging
560                       } /*for*/
561                   } //~if (otp->next != NULL)
562 #endif
563                 // adding otp (containing splitted lines) to otp chain
564                 if (otp_sub == NULL)
565                   {
566                     otp_sub = otp_new;
567                     for
568                       (
569                         otp_sub_last = otp_sub;
570                         otp_sub_last->next != NULL;
571                         otp_sub_last = otp_sub_last->next
572                       )
573                       /* find last element in chain */;
574                   }
575                 else
576                   {
577                   /* append otp_new to otp_sub chain */
578                     struct osd_text_word * ott_last = otp_sub->words;
579                     while (ott_last->next != NULL)
580                         ott_last = ott_last->next;
581                     ott_last->next = otp_new->words;
582                     otp_new->words->prev = ott_last;
583                     //attaching new subtitle line at the end
584                     otp_sub_last->next = otp_new;
585                     otp_new->prev = otp_sub_last;
586                     do
587                         otp_sub_last = otp_sub_last->next;
588                     while (otp_sub_last->next != NULL);
589                   } /*if*/
590               } //~ if (osl != NULL)
591           } // while (linesleft)
592         free(wordbuf);
593         // write lines into utbl
594         xtblc = 0; /* count of display lines */
595         utblc = 0; /* total count of characters in all display lines */
596         obj->topy = movie_height - sub_bottom_margin;
597         obj->params.subtitle.lines = 0;
598           {
599           /* collect display line text into obj->params.subtitle.utbl and x-positions
600             into obj->params.subtitle.xtbl */
601             struct osd_text_line *this_display_line;
602             for
603               (
604                 this_display_line = otp_sub;
605                 this_display_line != NULL;
606                 this_display_line = this_display_line->next
607               )
608               {
609                 struct osd_text_word *this_word, *next_line_words;
610                 int xsize;
611                 if (obj->params.subtitle.lines++ >= MAX_UCSLINES)
612                   {
613                     fprintf(stderr, "WARN: max_ucs_lines\n");
614                     break;
615                   } /*if*/
616                 if (max_line_height + sub_top_margin > obj->topy) // out of the screen so end parsing
617                   {
618                     obj->topy += vo_font->height; /* undo inclusion of last line */
619                     fprintf(stderr, "WARN: Out of screen at Y: %d\n", obj->topy);
620                     obj->params.subtitle.lines -= 1;
621                       /* discard overlong line */
622                     break;
623                   } /*if*/
624                 xsize = this_display_line->linewidth;
625                 obj->params.subtitle.xtbl[xtblc++] = (widthlimit - xsize) / 2 + sub_left_margin;
626                 if (xmin > (widthlimit - xsize) / 2 + sub_left_margin)
627                     xmin = (widthlimit - xsize) / 2 + sub_left_margin;
628                 if (xmax < (widthlimit + xsize) / 2 + sub_left_margin)
629                     xmax = (widthlimit + xsize) / 2 + sub_left_margin;
630              /* fprintf(stderr, "lm %d rm: %d xm:%d xs:%d\n", sub_left_margin, sub_right_margin, xmax, xsize); */
631                 next_line_words =
632                     this_display_line->next == NULL ?
633                         NULL
634                     :
635                         this_display_line->next->words;
636                 for
637                   (
638                     this_word = this_display_line->words;
639                     this_word != next_line_words;
640                     this_word = this_word->next
641                   )
642                   {
643                   /* assemble display lines into obj->params.subtitle */
644                     int chindex = 0;
645                     for (;;)
646                       {
647                         int curch;
648                         if (chindex == this_word->text_length)
649                             break;
650                         if (utblc > MAX_UCS)
651                             break;
652                         curch = this_word->text[chindex];
653                         render_one_glyph(vo_font, curch); /* fixme: didn't we already do this? */
654                         obj->params.subtitle.utbl[utblc++] = curch;
655                         sub_totallen++;
656                         ++chindex;
657                       } /*for*/
658                     obj->params.subtitle.utbl[utblc++] = ' '; /* separate from next word */
659                   } /*for*/
660                 obj->params.subtitle.utbl[utblc - 1] = 0;
661                   /* overwrite last space with string terminator */
662                 obj->topy -= vo_font->height; /* adjust top to leave room for another line */
663                   /* fixme: shouldn't that be max_line_height? same is true some other places
664                     vo_font->height is mentioned */
665               } /*for*/
666           }
667         if (sub_max_lines < obj->params.subtitle.lines)
668             sub_max_lines = obj->params.subtitle.lines;
669         if (sub_max_font_height < vo_font->height)
670             sub_max_font_height = vo_font->height;
671         if (sub_max_bottom_font_height < vo_font->pic_b[vo_font->font[40]]->h)
672             sub_max_bottom_font_height = vo_font->pic_b[vo_font->font[40]]->h;
673         if (obj->params.subtitle.lines)
674             obj->topy = movie_height - sub_bottom_margin - (obj->params.subtitle.lines * vo_font->height); /* + vo_font->pic_b[vo_font->font[40]]->h; */
675 
676         // free memory
677         if (otp_sub != NULL)
678           {
679             struct osd_text_word *tmp;
680             struct osd_text_line *pmt;
681             for (tmp = otp_sub->words; tmp->next != NULL; free(tmp->prev))
682               {
683                 free(tmp->text);
684                 tmp = tmp->next;
685               } /*for*/
686             free(tmp->text);
687             free(tmp);
688             for (pmt = otp_sub; pmt->next != NULL; free(pmt->prev))
689               {
690                 pmt = pmt->next;
691               } /*for*/
692             free(pmt);
693           }
694         else
695           {
696             fprintf(stderr, "WARN: Subtitles requested but not found.\n");
697           } /*if*/
698       }
699       {
700       /* work out vertical alignment and final positioning of subtitle */
701         const int subs_height =
702                 (obj->params.subtitle.lines - 1) * vo_font->height
703             +
704                 vo_font->pic_b[vo_font->font[40]]->h;
705       /* fprintf(stderr,"^1 bby1:%d bby2:%d h:%d movie_height:%d oy:%d sa:%d sh:%d f:%d\n",obj->bbox.y1,obj->bbox.y2,h,movie_height,obj->topy,v_sub_alignment,subs_height,font); */
706         if (v_sub_alignment == V_SUB_ALIGNMENT_BOTTOM)
707             obj->topy = movie_height * sub_pos / 100 - sub_bottom_margin - subs_height;
708         else if (v_sub_alignment == V_SUB_ALIGNMENT_CENTER)
709             obj->topy =
710                     (
711                         movie_height * sub_pos / 100
712                     -
713                         sub_bottom_margin
714                     -
715                         sub_top_margin
716                     -
717                         subs_height
718                     +
719                         vo_font->height
720                     )
721                 /
722                     2;
723         else /* v_sub_alignment = V_SUB_ALIGNMENT_TOP */
724             obj->topy = sub_top_margin;
725         if (obj->topy < sub_top_margin)
726             obj->topy = sub_top_margin;
727         if (obj->topy > movie_height - sub_bottom_margin - vo_font->height)
728             obj->topy = movie_height - sub_bottom_margin - vo_font->height;
729         obj->bbox.y2 = obj->topy + subs_height + 3;
730         // calculate bbox:
731         if (sub_justify)
732             xmin = sub_left_margin;
733         obj->bbox.x1 = xmin - 3;
734         obj->bbox.x2 = xmax + 3 + vo_font->spacewidth;
735       /* if (obj->bbox.x2 >= movie_width - sub_right_margin - 20)
736            {
737              obj->bbox.x2 = movie_width;
738            } */
739         obj->bbox.y1 = obj->topy - 3;
740     //  obj->bbox.y2 = obj->topy + obj->params.subtitle.lines * vo_font->height;
741         alloc_buf(obj);
742       /* fprintf(stderr,"^2 bby1:%d bby2:%d h:%d movie_height:%d oy:%d sa:%d sh:%d\n",obj->bbox.y1,obj->bbox.y2,h,movie_height,obj->topy,v_sub_alignment,subs_height); */
743       }
744       {
745       /* now to actually render the subtitle */
746         int i, chindex, prev_line_end;
747         chindex = prev_line_end = 0;
748         linesleft = obj->params.subtitle.lines;
749         if (linesleft != 0)
750           {
751             int xtbl_min, x;
752             int y = obj->topy;
753             for (xtbl_min = widthlimit; linedone < linesleft; ++linedone)
754                 if (obj->params.subtitle.xtbl[linedone] < xtbl_min)
755                     xtbl_min = obj->params.subtitle.xtbl[linedone];
756             for (i = 0; i < linesleft; ++i)
757               {
758                 int prevch, curch;
759                 switch (the_sub->alignment) /* determine start position for rendering line */
760                   {
761                 case H_SUB_ALIGNMENT_LEFT:
762                     if (sub_justify)
763                         x = xmin;
764                     else
765                         x = xtbl_min;
766                 break;
767                 case H_SUB_ALIGNMENT_RIGHT:
768                     x =
769                             2 * obj->params.subtitle.xtbl[i]
770                         -
771                             xtbl_min
772                         -
773                             (obj->params.subtitle.xtbl[i] == xtbl_min ? 0 : 1);
774                 break;
775                 case H_SUB_ALIGNMENT_CENTER:
776                 default:
777                     x = obj->params.subtitle.xtbl[i];
778                 break;
779                   } /*switch*/
780                 prevch = -1;
781                 while ((curch = obj->params.subtitle.utbl[chindex++]) != 0)
782                   {
783                   /* collect the rendered characters of this subtitle display line */
784                     const int font = vo_font->font[curch];
785                     x += kerning(vo_font, prevch, curch);
786                     if (font >= 0)
787                       {
788                       /* fprintf(stderr, "^3 vfh:%d vfh+y:%d odys:%d\n", vo_font->pic_b[font]->h, vo_font->pic_b[font]->h + y, movie_height); */
789                         draw_glyph
790                           (
791                             /*obj =*/ obj,
792                             /*x0 =*/ x,
793                             /*y0 =*/ y,
794                             /*w =*/ vo_font->width[curch],
795                             /*h =*/
796                                 vo_font->pic_b[font]->h + y < movie_height - sub_bottom_margin ?
797                                     vo_font->pic_b[font]->h
798                                 :
799                                     movie_height - sub_bottom_margin - y,
800                             /*src =*/ vo_font->pic_b[font]->bmp + vo_font->start[curch],
801                             /*srccolors =*/ vo_font->pic_b[font]->pal,
802                             /*stride =*/ vo_font->pic_b[font]->w
803                           );
804                       } /*if*/
805                     x += vo_font->width[curch] + vo_font->charspace;
806                     prevch = curch;
807                   } /*while*/
808                 if (sub_max_chars < chindex - prev_line_end)
809                     sub_max_chars = chindex - prev_line_end;
810                 prev_line_end = chindex;
811                 y += vo_font->height;
812               } /*for*/
813             /* Here you could retrieve the buffers*/
814           } /*if*/
815       }
816   } /*vo_update_text_sub*/
817 
vo_update_osd(const subtitle_elt * vo_sub)818 void vo_update_osd(const subtitle_elt * vo_sub)
819   {
820     memset(textsub_image_buffer, 0, textsub_image_buffer_size);
821       /* fill with transparent colour */
822     vo_update_text_sub(vo_osd, vo_sub);
823     vo_draw_subtitle_line
824       (
825         /*w =*/ vo_osd->bbox.x2 - vo_osd->bbox.x1,
826         /*h =*/ vo_osd->bbox.y2 - vo_osd->bbox.y1,
827         /*srcbase =*/ vo_osd->bitmap_buffer,
828         /*srcstride =*/ vo_osd->stride,
829         /*dstbase =*/
830                 textsub_image_buffer
831             +
832                 4 * vo_osd->bbox.x1
833             +
834                 4 * vo_osd->bbox.y1 * movie_width,
835         /*dststride =*/ movie_width * 4
836       );
837   } /*vo_update_osd*/
838 
vo_init_osd()839 void vo_init_osd()
840   {
841     vo_finish_osd(); /* if previously allocated */
842     switch (default_video_format)
843       {
844     case VF_NTSC:
845         if (movie_fps == 0.0)
846           {
847             movie_fps = 29.97;
848           } /*if*/
849         if (movie_width == 0)
850           {
851             movie_width = 720;
852           }
853         if (movie_height == 0)
854           {
855             movie_height = 478;
856           } /*if*/
857     break;
858     case VF_PAL:
859         if (movie_fps == 0.0)
860           {
861             movie_fps = 25.0;
862           } /*if*/
863         if (movie_width == 0)
864           {
865             movie_width = 720;
866           }
867         if (movie_height == 0)
868           {
869             movie_height = 574;
870           } /*if*/
871     break;
872     default:
873         fprintf(stderr, "ERR:  cannot determine default video size and frame rate--no video format specified\n");
874         exit(1);
875       } /*switch*/
876     textsub_image_buffer_size = sizeof(uint8_t) * 4 * movie_height * movie_width;
877     textsub_image_buffer = malloc(textsub_image_buffer_size);
878       /* fixme: not freed from previous call! */
879     if (textsub_image_buffer == NULL)
880      {
881         fprintf(stderr, "ERR:  Failed to allocate memory\n");
882         exit(1);
883       } /*if*/
884 #ifdef HAVE_FREETYPE
885     init_freetype();
886     load_font_ft();
887 #endif
888     sub_max_chars = 0;
889     sub_max_lines = 0;
890     sub_max_font_height = 0;
891     sub_max_bottom_font_height = 0;
892     vo_osd = malloc(sizeof(mp_osd_obj_t));
893     memset(vo_osd, 0, sizeof(mp_osd_obj_t));
894     vo_osd->bitmap_buffer = NULL;
895     vo_osd->allocated = -1;
896   } /*vo_init_osd*/
897 
vo_finish_osd()898 void vo_finish_osd()
899   /* frees up memory allocated for vo_osd. */
900   {
901     if (vo_osd)
902       {
903         free(vo_osd->bitmap_buffer);
904       } /*if*/
905     free(vo_osd);
906     vo_osd = NULL;
907 #ifdef HAVE_FREETYPE
908     done_freetype();
909 #endif
910     free(textsub_image_buffer);
911     textsub_image_buffer = NULL;
912   } /*vo_finish_osd*/
913