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