1 /*
2  * Copyright (C) 2000-2020 the xine project
3  *
4  * This file is part of xine, a free video player.
5  *
6  * xine is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * xine is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
19  *
20  * OSD stuff (text and graphic primitives)
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdio.h>
30 #include <pthread.h>
31 #include <zlib.h>
32 #include <sys/types.h>
33 #ifdef HAVE_DIRENT_H
34 #include <dirent.h>
35 #endif
36 #include <errno.h>
37 
38 #ifdef HAVE_ICONV
39 #  include <iconv.h>
40 #endif
41 
42 #include <basedir.h>
43 
44 #define LOG_MODULE "osd"
45 #define LOG_VERBOSE
46 /*
47 #define LOG
48 */
49 
50 #include <xine/xine_internal.h>
51 #include "xine-engine/bswap.h"
52 #include <xine/xineutils.h>
53 #include <xine/video_out.h>
54 #include <xine/osd.h>
55 
56 #include "xine_private.h"
57 
58 #ifdef HAVE_FT2
59 #include <ft2build.h>
60 #include FT_FREETYPE_H
61 # ifdef HAVE_FONTCONFIG
62 #  include <fontconfig/fontconfig.h>
63 # endif
64 #endif
65 
66 #define FONT_VERSION  2
67 
68 /* unicode value of alias character,
69  * used if conversion fails
70  */
71 #define ALIAS_CHARACTER_CONV '#'
72 
73 /* unicode value of alias character,
74  * used if character isn't in the font
75  */
76 #define ALIAS_CHARACTER_FONT '_'
77 
78 /* we want UCS-2 encoding in the machine endian */
79 #ifdef WORDS_BIGENDIAN
80 #  define UCS2_ENCODING "UCS-2BE"
81 #else
82 #  define UCS2_ENCODING "UCS-2LE"
83 #endif
84 
85 #if (FREETYPE_MAJOR > 2) || \
86     (FREETYPE_MAJOR == 2 && FREETYPE_MINOR > 1) || \
87     (FREETYPE_MAJOR == 2 && FREETYPE_MINOR == 1 && FREETYPE_PATCH >= 3)
88 #  define KERNING_DEFAULT FT_KERNING_DEFAULT
89 #else
90 #  define KERNING_DEFAULT ft_kerning_default
91 #endif
92 
93 #ifdef ENABLE_ANTIALIASING
94 #  define FT_LOAD_FLAGS   FT_LOAD_DEFAULT
95 #else
96 #  define FT_LOAD_FLAGS  (FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING)
97 #endif
98 
99 #define CLIP0MAX(val,max) { int32_t _v = val; if (_v > (int32_t)(max)) _v = max; _v &= ~(_v >> 31); val = _v; }
100 
101 typedef struct {
102   osd_renderer_t r;
103   vo_overlay_t   ovl;
104   xine_t        *xine;
105 } osd_renderer_private_t;
106 
107 /* This text descriptions are used for config screen */
108 static const char *const textpalettes_str[NUMBER_OF_TEXT_PALETTES+1] = {
109   "white-black-transparent",
110   "white-none-transparent",
111   "white-none-translucid",
112   "yellow-black-transparent",
113   NULL};
114 
115 /*
116    Palette entries as used by osd fonts:
117 
118    0: not used by font, always transparent
119    1: font background, usually transparent, may be used to implement
120       translucid boxes where the font will be printed.
121    2-5: transition between background and border (usually only alpha
122         value changes).
123    6: font border. if the font is to be displayed without border this
124       will probably be adjusted to font background or near.
125    7-9: transition between border and foreground
126    10: font color (foreground)
127 */
128 
129 /*
130     The palettes below were made by hand, ie, i just throw
131     values that seemed to do the transitions i wanted.
132     This can surelly be improved a lot. [Miguel]
133 */
134 
135 static const clut_t textpalettes_color[NUMBER_OF_TEXT_PALETTES][TEXT_PALETTE_SIZE] = {
136 /* white, black border, transparent */
137   {
138     CLUT_Y_CR_CB_INIT(0x00, 0x00, 0x00), /*0*/
139     CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), /*1*/
140     CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), /*2*/
141     CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), /*3*/
142     CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), /*4*/
143     CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), /*5*/
144     CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), /*6*/
145     CLUT_Y_CR_CB_INIT(0x40, 0x80, 0x80), /*7*/
146     CLUT_Y_CR_CB_INIT(0x80, 0x80, 0x80), /*8*/
147     CLUT_Y_CR_CB_INIT(0xc0, 0x80, 0x80), /*9*/
148     CLUT_Y_CR_CB_INIT(0xff, 0x80, 0x80), /*10*/
149   },
150   /* white, no border, transparent */
151   {
152     CLUT_Y_CR_CB_INIT(0x00, 0x00, 0x00), /*0*/
153     CLUT_Y_CR_CB_INIT(0xff, 0x80, 0x80), /*1*/
154     CLUT_Y_CR_CB_INIT(0xff, 0x80, 0x80), /*2*/
155     CLUT_Y_CR_CB_INIT(0xff, 0x80, 0x80), /*3*/
156     CLUT_Y_CR_CB_INIT(0xff, 0x80, 0x80), /*4*/
157     CLUT_Y_CR_CB_INIT(0xff, 0x80, 0x80), /*5*/
158     CLUT_Y_CR_CB_INIT(0xff, 0x80, 0x80), /*6*/
159     CLUT_Y_CR_CB_INIT(0xff, 0x80, 0x80), /*7*/
160     CLUT_Y_CR_CB_INIT(0xff, 0x80, 0x80), /*8*/
161     CLUT_Y_CR_CB_INIT(0xff, 0x80, 0x80), /*9*/
162     CLUT_Y_CR_CB_INIT(0xff, 0x80, 0x80), /*10*/
163   },
164   /* white, no border, translucid */
165   {
166     CLUT_Y_CR_CB_INIT(0x00, 0x00, 0x00), /*0*/
167     CLUT_Y_CR_CB_INIT(0x80, 0x80, 0x80), /*1*/
168     CLUT_Y_CR_CB_INIT(0x80, 0x80, 0x80), /*2*/
169     CLUT_Y_CR_CB_INIT(0x80, 0x80, 0x80), /*3*/
170     CLUT_Y_CR_CB_INIT(0x80, 0x80, 0x80), /*4*/
171     CLUT_Y_CR_CB_INIT(0x80, 0x80, 0x80), /*5*/
172     CLUT_Y_CR_CB_INIT(0x80, 0x80, 0x80), /*6*/
173     CLUT_Y_CR_CB_INIT(0xa0, 0x80, 0x80), /*7*/
174     CLUT_Y_CR_CB_INIT(0xc0, 0x80, 0x80), /*8*/
175     CLUT_Y_CR_CB_INIT(0xe0, 0x80, 0x80), /*9*/
176     CLUT_Y_CR_CB_INIT(0xff, 0x80, 0x80), /*10*/
177   },
178   /* yellow, black border, transparent */
179   {
180     CLUT_Y_CR_CB_INIT(0x00, 0x00, 0x00), /*0*/
181     CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), /*1*/
182     CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), /*2*/
183     CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), /*3*/
184     CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), /*4*/
185     CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), /*5*/
186     CLUT_Y_CR_CB_INIT(0x00, 0x80, 0x80), /*6*/
187     CLUT_Y_CR_CB_INIT(0x40, 0x84, 0x60), /*7*/
188     CLUT_Y_CR_CB_INIT(0x70, 0x88, 0x40), /*8*/
189     CLUT_Y_CR_CB_INIT(0xb0, 0x8a, 0x20), /*9*/
190     CLUT_Y_CR_CB_INIT(0xff, 0x90, 0x00), /*10*/
191   },
192 };
193 
194 static const uint8_t textpalettes_trans[NUMBER_OF_TEXT_PALETTES][TEXT_PALETTE_SIZE] = {
195   {0, 0, 3, 6, 8, 10, 12, 14, 15, 15, 15 },
196   {0, 0, 0, 0, 0, 0, 2, 6, 9, 12, 15 },
197   {0, 8, 9, 10, 11, 12, 13, 14, 15, 15, 15 },
198   {0, 0, 3, 6, 8, 10, 12, 14, 15, 15, 15 },
199 };
200 
201 typedef struct osd_fontchar_s {
202   uint8_t *bmp;
203   uint16_t code;
204   uint16_t width;
205   uint16_t height;
206 } osd_fontchar_t;
207 
208 struct osd_font_s {
209   char             name[40];
210   char            *filename;
211   osd_fontchar_t  *fontchar;
212   uint8_t         *data;
213   osd_font_t      *next;
214   uint16_t         version;
215   uint16_t         size;
216   uint16_t         num_fontchars;
217   uint16_t         loaded;
218 };
219 
220 #ifdef HAVE_FT2
221 struct osd_ft2context_s {
222   FT_Library library;
223   FT_Face    face;
224   int        size;
225 };
226 
osd_free_ft2(osd_object_t * osd)227 static void osd_free_ft2 (osd_object_t *osd)
228 {
229   if( osd->ft2 ) {
230     if ( osd->ft2->face )
231       FT_Done_Face (osd->ft2->face);
232     if ( osd->ft2->library )
233       FT_Done_FreeType(osd->ft2->library);
234     _x_freep( &osd->ft2 );
235   }
236 }
237 #else
osd_free_ft2(osd_object_t * osd __attr_unused)238 static inline void osd_free_ft2 (osd_object_t *osd __attr_unused) {}
239 #endif
240 
241 /*
242  * open a new osd object. this will allocated an empty (all zero) drawing
243  * area where graphic primitives may be used.
244  * It is ok to specify big width and height values. The render will keep
245  * track of the smallest changed area to not generate too big overlays.
246  * A default palette is initialized (i sugest keeping color 0 as transparent
247  * for the sake of simplicity)
248  */
249 
osd_new_object(osd_renderer_t * this,int width,int height)250 static osd_object_t *osd_new_object (osd_renderer_t *this, int width, int height) {
251 
252   osd_object_t *osd;
253 
254   osd = calloc(1, sizeof(osd_object_t));
255   if (!osd)
256     return NULL;
257 #ifndef HAVE_ZERO_SAFE_MEM
258   osd->video_window_x      = 0;
259   osd->video_window_y      = 0;
260   osd->video_window_width  = 0;
261   osd->video_window_height = 0;
262   osd->extent_width        = 0;
263   osd->extent_height       = 0;
264   osd->area_touched        = 0;
265   osd->x2                  = 0;
266   osd->y2                  = 0;
267 #endif
268 
269   osd->area = calloc(width, height);
270   if (!osd->area) {
271     free (osd);
272     return NULL;
273   }
274 
275   osd->renderer = this;
276 
277   osd->width = width;
278   osd->height = height;
279 
280   osd->x1 = width;
281   osd->y1 = height;
282 
283   memcpy(osd->color, textpalettes_color[0], sizeof(textpalettes_color[0]));
284   memcpy(osd->trans, textpalettes_trans[0], sizeof(textpalettes_trans[0]));
285 
286   osd->handle = -1;
287 
288 #ifdef HAVE_ICONV
289   osd->cd       = (iconv_t)-1;
290   osd->encoding = NULL;
291 #endif
292 
293   /* append to renderer list */
294   pthread_mutex_lock (&this->osd_mutex);
295   osd->next = this->osds;
296   this->osds = osd;
297   pthread_mutex_unlock (&this->osd_mutex);
298 
299   lprintf("osd=%p size: %dx%d\n", (void*)osd, width, height);
300 
301   return osd;
302 }
303 
304 /*
305  * osd extent must be set to achive video resolution independent osds
306  * both sizes must be > 0 to take effect. otherwise, video resolution
307  * will still be used. the extent defines the reference coordinate
308  * system which is matched to the video output area.
309  */
osd_set_extent(osd_object_t * osd,int extent_width,int extent_height)310 static void osd_set_extent (osd_object_t *osd, int extent_width, int extent_height) {
311 
312   osd->extent_width  = extent_width;
313   osd->extent_height = extent_height;
314 }
315 
316 /*
317  * osd video window defines an area withing osd extent where the
318  * video shall be scaled to while an osd is displayed on screen.
319  * both width and height must be > 0 to take effect.
320  */
osd_set_video_window(osd_object_t * osd,int window_x,int window_y,int window_width,int window_height)321 static void osd_set_video_window (osd_object_t *osd, int window_x, int window_y, int window_width, int window_height) {
322 
323   osd->video_window_x      = window_x;
324   osd->video_window_y      = window_y;
325   osd->video_window_width  = window_width;
326   osd->video_window_height = window_height;
327 }
328 
argb_layer_create()329 static argb_layer_t *argb_layer_create() {
330 
331   argb_layer_t *argb_layer;
332 
333   argb_layer = calloc(1, sizeof (argb_layer_t));
334   if (!argb_layer)
335     return NULL;
336 
337   pthread_mutex_init(&argb_layer->mutex, NULL);
338   return argb_layer;
339 }
340 
argb_layer_destroy(argb_layer_t * argb_layer)341 static void argb_layer_destroy(argb_layer_t *argb_layer) {
342 
343   pthread_mutex_destroy(&argb_layer->mutex);
344   free(argb_layer);
345 }
346 
set_argb_layer_ptr(argb_layer_t ** dst,argb_layer_t * src)347 void set_argb_layer_ptr(argb_layer_t **dst, argb_layer_t *src) {
348 
349   if (src) {
350     pthread_mutex_lock(&src->mutex);
351     ++src->ref_count;
352     pthread_mutex_unlock(&src->mutex);
353   }
354 
355   if (*dst) {
356     int free_argb_layer;
357 
358     pthread_mutex_lock(&(*dst)->mutex);
359     free_argb_layer = (0 == --(*dst)->ref_count);
360     pthread_mutex_unlock(&(*dst)->mutex);
361 
362     if (free_argb_layer)
363       argb_layer_destroy(*dst);
364   }
365 
366   *dst = src;
367 }
368 
369 
370 /*
371 #define DEBUG_RLE
372 */
373 
374 static int _osd_hide (osd_object_t *osd, int64_t vpts);
375 
376 /*
377  * send the osd to be displayed at given pts (0=now)
378  * the object is not changed. there may be subsequent drawing  on it.
379  */
_osd_show(osd_object_t * osd,int64_t vpts,int unscaled)380 static int _osd_show (osd_object_t *osd, int64_t vpts, int unscaled ) {
381 
382   osd_renderer_t *this = osd->renderer;
383   xine_private_t *xine = (xine_private_t *)this->stream->xine;
384   video_overlay_manager_t *ovl_manager;
385   rle_elem_t rle, *rle_p=0;
386   int x, y;
387   uint8_t *c;
388 
389   lprintf("osd=%p vpts=%"PRId64"\n", (void*)osd, vpts);
390 
391   xine->port_ticket->acquire (xine->port_ticket, 1);
392 
393   ovl_manager = this->stream->video_out->get_overlay_manager(this->stream->video_out);
394 
395   if( osd->handle < 0 ) {
396     if( (osd->handle = ovl_manager->get_handle(ovl_manager, 0)) == -1 ) {
397       xine->port_ticket->release (xine->port_ticket, 1);
398       return 0;
399     }
400   }
401 
402   pthread_mutex_lock (&this->osd_mutex);
403 
404   /* clip update area to allowed range */
405   CLIP0MAX (osd->x1, osd->width);
406   CLIP0MAX (osd->x2, osd->width);
407   CLIP0MAX (osd->y1, osd->height);
408   CLIP0MAX (osd->y2, osd->height);
409 
410 #ifdef DEBUG_RLE
411   lprintf("osd_show %p rle starts\n", (void*)osd);
412 #endif
413 
414   /* check if osd is valid (something drawn on it) */
415   if( osd->x2 > osd->x1 && osd->y2 > osd->y1 ) {
416 
417     this->event.object.handle = osd->handle;
418 
419     memset( this->event.object.overlay, 0, sizeof(*this->event.object.overlay) );
420 
421     set_argb_layer_ptr(&this->event.object.overlay->argb_layer, osd->argb_layer);
422 
423     this->event.object.overlay->unscaled = unscaled;
424     this->event.object.overlay->x = osd->display_x + osd->x1;
425     this->event.object.overlay->y = osd->display_y + osd->y1;
426     this->event.object.overlay->width = osd->x2 - osd->x1;
427     this->event.object.overlay->height = osd->y2 - osd->y1;
428 
429     this->event.object.overlay->video_window_x      = osd->video_window_x;
430     this->event.object.overlay->video_window_y      = osd->video_window_y;
431     this->event.object.overlay->video_window_width  = osd->video_window_width;
432     this->event.object.overlay->video_window_height = osd->video_window_height;
433 
434     this->event.object.overlay->extent_width  = osd->extent_width;
435     this->event.object.overlay->extent_height = osd->extent_height;
436 
437     this->event.object.overlay->hili_top    = 0;
438     this->event.object.overlay->hili_bottom = this->event.object.overlay->height;
439     this->event.object.overlay->hili_left   = 0;
440     this->event.object.overlay->hili_right  = this->event.object.overlay->width;
441 
442     /* there will be at least that many rle objects (one for each row) */
443     this->event.object.overlay->num_rle = 0;
444     if (!osd->area_touched) {
445       /* avoid rle encoding when only argb_layer is modified */
446       this->event.object.overlay->data_size = 0;
447       rle_p = this->event.object.overlay->rle = NULL;
448     } else {
449       /* We will never need more rle objects than columns in any row
450          Rely on lazy page allocation to avoid us actually taking up
451          this much RAM */
452       this->event.object.overlay->data_size = osd->width * osd->height;
453       rle_p = this->event.object.overlay->rle =
454          malloc(this->event.object.overlay->data_size * sizeof(rle_elem_t) );
455 
456       for( y = osd->y1; y < osd->y2; y++ ) {
457 #ifdef DEBUG_RLE
458         lprintf("osd_show %p y = %d: ", (void*)osd, y);
459 #endif
460         c = osd->area + y * osd->width + osd->x1;
461 
462         /* initialize a rle object with the first pixel's color */
463         rle.len = 1;
464         rle.color = *c++;
465 
466         /* loop over the remaining pixels in the row */
467         for( x = osd->x1 + rle.len; x < osd->x2; x++, c++ ) {
468           if( rle.color != *c ) {
469 #ifdef DEBUG_RLE
470             lprintf("(%d, %d), ", rle.len, rle.color);
471 #endif
472             *rle_p++ = rle;
473             this->event.object.overlay->num_rle++;
474 
475             rle.color = *c;
476             rle.len = 1;
477           } else {
478             rle.len++;
479           }
480         }
481 #ifdef DEBUG_RLE
482         lprintf("(%d, %d)\n", rle.len, rle.color);
483 #endif
484         *rle_p++ = rle;
485         this->event.object.overlay->num_rle++;
486       }
487 #ifdef DEBUG_RLE
488       lprintf("osd_show %p rle ends\n", (void*)osd);
489 #endif
490       lprintf("num_rle = %d\n", this->event.object.overlay->num_rle);
491 
492       memcpy(this->event.object.overlay->hili_color, osd->color, sizeof(osd->color));
493       memcpy(this->event.object.overlay->hili_trans, osd->trans, sizeof(osd->trans));
494       memcpy(this->event.object.overlay->color, osd->color, sizeof(osd->color));
495       memcpy(this->event.object.overlay->trans, osd->trans, sizeof(osd->trans));
496     }
497 
498     this->event.event_type = OVERLAY_EVENT_SHOW;
499     this->event.vpts = vpts;
500     ovl_manager->add_event(ovl_manager, (void *)&this->event);
501 
502     set_argb_layer_ptr(&this->event.object.overlay->argb_layer, NULL);
503   } else {
504     /* osd empty - hide it */
505     _osd_hide(osd, vpts);
506   }
507   pthread_mutex_unlock (&this->osd_mutex);
508 
509   xine->port_ticket->release (xine->port_ticket, 1);
510 
511   return 1;
512 }
513 
514 /* normal OSD show
515  * overlay is blended and scaled together with the stream.
516  */
osd_show_scaled(osd_object_t * osd,int64_t vpts)517 static int osd_show_scaled (osd_object_t *osd, int64_t vpts) {
518   return _osd_show(osd, vpts, 0);
519 }
520 
521 /* unscaled OSD show
522  * overlay is blended at output (screen) resolution.
523  */
osd_show_unscaled(osd_object_t * osd,int64_t vpts)524 static int osd_show_unscaled (osd_object_t *osd, int64_t vpts) {
525   return _osd_show(osd, vpts, 1);
526 }
527 
528 /*
529  * send event to hide osd at given pts (0=now)
530  * the object is not changed. there may be subsequent drawing  on it.
531  */
_osd_hide(osd_object_t * osd,int64_t vpts)532 static int _osd_hide (osd_object_t *osd, int64_t vpts) {
533 
534   osd_renderer_t *this = osd->renderer;
535   video_overlay_manager_t *ovl_manager;
536 
537   lprintf("osd=%p vpts=%"PRId64"\n", (void*)osd, vpts);
538 
539   if( osd->handle < 0 )
540     return 0;
541 
542   this->event.object.handle = osd->handle;
543 
544   /* not really needed this, but good pratice to clean it up */
545   memset( this->event.object.overlay, 0, sizeof(*this->event.object.overlay) );
546 
547   this->event.event_type = OVERLAY_EVENT_HIDE;
548   this->event.vpts = vpts;
549 
550   ovl_manager = this->stream->video_out->get_overlay_manager(this->stream->video_out);
551   ovl_manager->add_event(ovl_manager, (void *)&this->event);
552 
553   return 1;
554 }
555 
osd_hide(osd_object_t * osd,int64_t vpts)556 static int osd_hide (osd_object_t *osd, int64_t vpts) {
557 
558   osd_renderer_t *this = osd->renderer;
559   xine_private_t *xine = (xine_private_t *)this->stream->xine;
560   int ret;
561 
562   xine->port_ticket->acquire (xine->port_ticket, 1);
563 
564   pthread_mutex_lock (&this->osd_mutex);
565 
566   ret = _osd_hide(osd, vpts);
567 
568   pthread_mutex_unlock (&this->osd_mutex);
569 
570   xine->port_ticket->release (xine->port_ticket, 1);
571 
572   return ret;
573 }
574 
575 
576 /*
577  * clear an osd object, so that it can be used for rendering a new image
578  */
579 
osd_clear(osd_object_t * osd)580 static void osd_clear (osd_object_t *osd) {
581   lprintf("osd=%p\n", (void*)osd);
582 
583   if (osd->area_touched) {
584     osd->area_touched = 0;
585     memset(osd->area, 0, osd->width * osd->height);
586   }
587 
588   osd->x1 = osd->width;
589   osd->y1 = osd->height;
590   osd->x2 = 0;
591   osd->y2 = 0;
592 
593   if (osd->argb_layer) {
594     pthread_mutex_lock(&osd->argb_layer->mutex);
595     osd->argb_layer->x1 = osd->x1;
596     osd->argb_layer->y1 = osd->y1;
597     osd->argb_layer->x2 = osd->x2;
598     osd->argb_layer->y2 = osd->y2;
599     pthread_mutex_unlock(&osd->argb_layer->mutex);
600   }
601 }
602 
603 /*
604  *
605  */
606 
_update_clipping(osd_object_t * osd,int x1,int y1,int x2,int y2)607 static void _update_clipping(osd_object_t *osd, int x1, int y1, int x2, int y2)
608 {
609   osd->x1 = MIN( osd->x1, x1 );
610   osd->y1 = MIN( osd->y1, y1 );
611   osd->x2 = MAX( osd->x2, x2 );
612   osd->y2 = MAX( osd->y2, y2 );
613   osd->area_touched = 1;
614 }
615 
616 /*
617  * Draw a point.
618  */
619 
osd_point(osd_object_t * osd,int x,int y,int color)620 static void osd_point (osd_object_t *osd, int x, int y, int color) {
621   uint8_t *c;
622 
623   lprintf("osd=%p (%d x %d)\n", (void*)osd, x, y);
624 
625   if (x < 0 || x >= osd->width)
626     return;
627   if (y < 0 || y >= osd->height)
628     return;
629 
630   _update_clipping(osd, x, y, x + 1, y + 1);
631 
632   c = osd->area + y * osd->width + x;
633   *c = color;
634 }
635 
636 /*
637  * Bresenham line implementation on osd object
638  */
639 
osd_line(osd_object_t * osd,int x1,int y1,int x2,int y2,int color)640 static void osd_line (osd_object_t *osd,
641 		      int x1, int y1, int x2, int y2, int color) {
642 
643   uint8_t *c;
644   int dx, dy, t, inc, d, inc1, inc2;
645   int swap_x = 0;
646   int swap_y = 0;
647 
648   lprintf("osd=%p (%d,%d)-(%d,%d)\n", (void*)osd, x1,y1, x2,y2 );
649 
650   /* sort line */
651   if (x2 < x1) {
652     t  = x1;
653     x1 = x2;
654     x2 = t;
655     swap_x = 1;
656   }
657   if (y2 < y1) {
658     t  = y1;
659     y1 = y2;
660     y2 = t;
661     swap_y = 1;
662   }
663 
664   /* clip line */
665   if (x1 < 0) {
666     if (x2 < 0)
667       return;
668     y1 = y1 + (y2-y1) * -x1 / (x2-x1);
669     x1 = 0;
670   }
671   if (y1 < 0) {
672     if (y2 < 0)
673       return;
674     x1 = x1 + (x2-x1) * -y1 / (y2-y1);
675     y1 = 0;
676   }
677   if (x2 > osd->width) {
678     if (x1 > osd->width)
679       return;
680     y2 = y1 + (y2-y1) * (osd->width-x1) / (x2-x1);
681     x2 = osd->width;
682   }
683   if (y2 > osd->height) {
684     if (y1 > osd->height)
685       return;
686     x2 = x1 + (x2-x1) * (osd->height-y1) / (y2-y1);
687     y2 = osd->height;
688   }
689 
690   if (x1 >= osd->width || y1 >= osd->height)
691     return;
692 
693   _update_clipping(osd, x1, y1, x2, y2);
694 
695   dx = x2 - x1;
696   dy = y2 - y1;
697 
698   /* unsort line */
699   if (swap_x) {
700     t  = x1;
701     x1 = x2;
702     x2 = t;
703   }
704   if (swap_y) {
705     t  = y1;
706     y1 = y2;
707     y2 = t;
708   }
709 
710   if( dx>=dy ) {
711     if( x1>x2 )
712     {
713       t = x2; x2 = x1; x1 = t;
714       t = y2; y2 = y1; y1 = t;
715     }
716 
717     if( y2 > y1 ) inc = 1; else inc = -1;
718 
719     inc1 = 2*dy;
720     d = inc1 - dx;
721     inc2 = 2*(dy-dx);
722 
723     c = osd->area + y1 * osd->width + x1;
724 
725     while(x1<x2)
726     {
727       if (y1 >= 0 && y1 < osd->height)
728         *c++ = color;
729 
730       x1++;
731       if( d<0 ) {
732         d+=inc1;
733       } else {
734         y1+=inc;
735         d+=inc2;
736         c = osd->area + y1 * osd->width + x1;
737       }
738     }
739   } else {
740     if( y1>y2 ) {
741       t = x2; x2 = x1; x1 = t;
742       t = y2; y2 = y1; y1 = t;
743     }
744 
745     if( x2 > x1 ) inc = 1; else inc = -1;
746 
747     inc1 = 2*dx;
748     d = inc1-dy;
749     inc2 = 2*(dx-dy);
750 
751     c = osd->area + y1 * osd->width + x1;
752 
753     while(y1<y2) {
754       if (x1 >= 0 && x1 < osd->width)
755         *c = color;
756 
757       c += osd->width;
758       y1++;
759       if( d<0 ) {
760 	d+=inc1;
761       } else {
762 	x1+=inc;
763 	d+=inc2;
764 	c = osd->area + y1 * osd->width + x1;
765       }
766     }
767   }
768 }
769 
770 
771 /*
772  * filled retangle
773  */
774 
osd_filled_rect(osd_object_t * osd,int x1,int y1,int x2,int y2,int color)775 static void osd_filled_rect (osd_object_t *osd,
776 			     int x1, int y1, int x2, int y2, int color) {
777 
778   int x, y, dx, dy;
779 
780   lprintf("osd=%p (%d,%d)-(%d,%d)\n", (void*)osd, x1,y1, x2,y2 );
781 
782   /* sort rectangle */
783   x  = MIN( x1, x2 );
784   dx = MAX( x1, x2 );
785   y  = MIN( y1, y2 );
786   dy = MAX( y1, y2 );
787 
788   /* clip rectangle */
789   if (x >= osd->width || y >= osd->height)
790     return;
791 
792   if (x < 0) {
793     dx += x;
794     x = 0;
795   }
796   if (y < 0) {
797     dy += y;
798     y = 0;
799   }
800 
801   dx = MIN( dx, osd->width );
802   dy = MIN( dy, osd->height );
803 
804   _update_clipping(osd, x, y, dx, dy);
805 
806   dx -= x;
807   dy -= y;
808 
809   for( ; dy--; y++ ) {
810     memset(osd->area + y * osd->width + x,color,dx);
811   }
812 }
813 
814 /*
815  * set palette (color and transparency)
816  */
817 
osd_set_palette(osd_object_t * osd,const uint32_t * color,const uint8_t * trans)818 static void osd_set_palette(osd_object_t *osd, const uint32_t *color, const uint8_t *trans ) {
819 
820   memcpy(osd->color, color, sizeof(osd->color));
821   memcpy(osd->trans, trans, sizeof(osd->trans));
822 }
823 
824 /*
825  * set on existing text palette
826  * (-1 to set user specified palette)
827  */
828 
osd_set_text_palette(osd_object_t * osd,int palette_number,int color_base)829 static void osd_set_text_palette(osd_object_t *osd, int palette_number,
830 				 int color_base) {
831 
832   if( palette_number < 0 )
833     palette_number = osd->renderer->textpalette;
834 
835   /* some sanity checks for the color indices */
836   if( color_base < 0 )
837     color_base = 0;
838   else if( color_base > OVL_PALETTE_SIZE - TEXT_PALETTE_SIZE )
839     color_base = OVL_PALETTE_SIZE - TEXT_PALETTE_SIZE;
840 
841   memcpy(&osd->color[color_base], textpalettes_color[palette_number],
842 	 sizeof(textpalettes_color[palette_number]));
843   memcpy(&osd->trans[color_base], textpalettes_trans[palette_number],
844 	 sizeof(textpalettes_trans[palette_number]));
845 }
846 
847 
848 /*
849  * get palette (color and transparency)
850  */
851 
osd_get_palette(osd_object_t * osd,uint32_t * color,uint8_t * trans)852 static void osd_get_palette (osd_object_t *osd, uint32_t *color, uint8_t *trans) {
853 
854   memcpy(color, osd->color, sizeof(osd->color));
855   memcpy(trans, osd->trans, sizeof(osd->trans));
856 }
857 
858 /*
859  * set position were overlay will be blended
860  */
861 
osd_set_position(osd_object_t * osd,int x,int y)862 static void osd_set_position (osd_object_t *osd, int x, int y) {
863 
864   if( x < 0 || x > 0x10000 )
865     x = 0;
866   if( y < 0 || y > 0x10000 )
867     y = 0;
868   osd->display_x = x;
869   osd->display_y = y;
870 }
871 
872 /*
873    load bitmap font into osd engine
874 */
875 
osd_renderer_load_font(osd_renderer_t * this,const char * filename)876 static int osd_renderer_load_font (osd_renderer_t *this, const char *filename) {
877 
878   gzFile       fp;
879   size_t       fnlen = strlen (filename) + 1;
880   int          i;
881   osd_font_t  *font = NULL;
882 
883   lprintf("name=%s\n", filename );
884 
885   /* load quick & dirt font format */
886   do {
887     uint8_t b[sizeof (font->name) + 3 * 2];
888     size_t dsize, lsize;
889 
890     fp = gzopen (filename, "rb");
891     if (!fp)
892       break;
893 
894     if (gzread (fp, b, sizeof (font->name) + 3 * 2) != (int)sizeof (font->name) + 3 * 2)
895       break;
896     i = _X_LE_16 (b + sizeof (font->name));
897     if (i != FONT_VERSION) {
898       xprintf (this->stream->xine, XINE_VERBOSITY_LOG,
899         _("wrong version for font '%s'. expected %d found %d.\n"), (char *)b, i, FONT_VERSION);
900       break;
901     }
902     font = calloc (1, sizeof (*font) + fnlen);
903     if (!font)
904       break;
905     memcpy (font->name, b, sizeof (font->name));
906     font->version = i;
907     font->size = _X_LE_16 (b + sizeof (font->name) + 1 * 2);
908     font->num_fontchars = _X_LE_16 (b + sizeof (font->name) + 2 * 2);
909     font->loaded = 1;
910     font->data = NULL;
911 
912     font->fontchar = malloc (sizeof (osd_fontchar_t) * font->num_fontchars);
913     if (!font->fontchar)
914       break;
915     lprintf ("font '%s' chars=%d\n", font->name, font->num_fontchars);
916 
917     /* estimate total uncompressed size, and load entire rest. */
918     dsize = font->num_fontchars * font->size * font->size;
919     font->data = malloc (dsize);
920     if (!font->data)
921       break;
922     lsize = 0;
923     while (1) {
924       uint8_t *n;
925       int r = gzread (fp, font->data + lsize, dsize - lsize);
926       if (r <= 0)
927         break;
928       lsize += r;
929       if (lsize < dsize)
930         break;
931       if (dsize > (20 << 20))
932         break;
933       n = realloc (font->data, 2 * dsize);
934       if (!n)
935         break;
936       font->data = n;
937       dsize *= 2;
938     }
939     gzclose (fp);
940     fp = NULL;
941     if (lsize < dsize) {
942       uint8_t *n = realloc (font->data, lsize);
943       if (n)
944         font->data = n;
945     }
946     dsize = sizeof (font->name) + 3 * 2 + lsize;
947 
948     /* load all characters */
949     {
950       uint8_t *p = font->data;
951       for (i = 0; i < font->num_fontchars; i++) {
952         size_t bsize;
953         if (lsize < 3 * 2)
954           break;
955         font->fontchar[i].code = _X_LE_16 (p);
956         font->fontchar[i].width = _X_LE_16 (p + 1 * 2);
957         font->fontchar[i].height = _X_LE_16 (p + 2 * 2);
958         p += 3 * 2;
959         lsize -= 3 * 2;
960         font->fontchar[i].bmp = p;
961         bsize = (size_t)font->fontchar[i].width * (size_t)font->fontchar[i].height;
962         if (lsize < bsize)
963           break;
964         p += bsize;
965         lsize -= bsize;
966       }
967     }
968     /* check if all expected characters were loaded */
969     if (i < font->num_fontchars)
970       break;
971 
972     {
973       osd_font_t *known_font;
974 
975       lprintf ("font '%s' loaded\n", font->name);
976       /* check if font is already known to us */
977       known_font = this->fonts;
978       while (known_font) {
979         if (!strcasecmp (known_font->name, font->name) && known_font->size == font->size)
980           break;
981         known_font = known_font->next;
982       }
983       if (!known_font) {
984         /* new font, add it to list */
985         font->filename = (char *)font + sizeof (*font);
986         memcpy (font->filename, filename, fnlen);
987         font->next = this->fonts;
988         this->fonts = font;
989         xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG,
990           "osd: loaded font %s (%u bytes).\n", filename, (unsigned int)dsize);
991         return 1;
992       }
993       if (!known_font->loaded) {
994         /* the font was preloaded before.
995          * add loaded characters to the existing entry.
996          */
997         known_font->version = font->version;
998         known_font->size = font->size;
999         known_font->num_fontchars = font->num_fontchars;
1000         known_font->loaded = 1;
1001         known_font->fontchar = font->fontchar;
1002         known_font->data = font->data;
1003         free (font);
1004         xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG,
1005           "osd: loaded font %s (%u bytes).\n", filename, (unsigned int)dsize);
1006         return 1;
1007       }
1008       xprintf (this->stream->xine, XINE_VERBOSITY_LOG,
1009         _("font '%s-%d' already loaded, weird.\n"), font->name, font->size);
1010       free (font->data);
1011       free (font->fontchar);
1012       free (font);
1013       return 1;
1014     }
1015   } while (0);
1016 
1017   if (font) {
1018     xprintf (this->stream->xine, XINE_VERBOSITY_LOG,
1019       _("font '%s' loading failed (%d < %d)\n"), font->name, i, font->num_fontchars);
1020     free (font->data);
1021     free (font->fontchar);
1022     free (font);
1023   }
1024   if (fp)
1025     gzclose (fp);
1026   return 0;
1027 }
1028 
1029 /*
1030  * unload font
1031  */
osd_renderer_unload_font(osd_renderer_t * this,const char * fontname)1032 static int osd_renderer_unload_font(osd_renderer_t *this, const char *fontname ) {
1033 
1034   osd_font_t *font, *last;
1035   osd_object_t *osd;
1036   int i, ret = 0;
1037 
1038   lprintf("font '%s'\n", fontname);
1039 
1040   pthread_mutex_lock (&this->osd_mutex);
1041 
1042   osd = this->osds;
1043   while( osd ) {
1044     if( !strcasecmp(osd->font->name, fontname) )
1045       osd->font = NULL;
1046     osd = osd->next;
1047   }
1048 
1049   last = NULL;
1050   font = this->fonts;
1051   while (font) {
1052     if (!strcasecmp (font->name, fontname)) {
1053       if (font->loaded) {
1054         if (font->data) {
1055           _x_freep (&font->data);
1056         } else {
1057           for (i = 0; i < font->num_fontchars; i++) {
1058             _x_freep (&font->fontchar[i].bmp);
1059           }
1060         }
1061         _x_freep (&font->fontchar);
1062       }
1063 
1064       if( last )
1065         last->next = font->next;
1066       else
1067         this->fonts = font->next;
1068 
1069       free( font );
1070       ret = 1;
1071       break;
1072     }
1073 
1074     last = font;
1075     font = font->next;
1076   }
1077 
1078   pthread_mutex_unlock (&this->osd_mutex);
1079   return ret;
1080 }
1081 
1082 /*
1083  look for a native xine font matching the given name and size and load it
1084  into osd->font.
1085  return nonzero if a native font is found, zero if not
1086 */
osd_lookup_native(osd_object_t * osd,const char * fontname,int size)1087 static int osd_lookup_native( osd_object_t *osd, const char *fontname, int size ) {
1088     osd_font_t *font;
1089     int best = 0;
1090     int ret = 0;
1091 
1092     font = osd->renderer->fonts;
1093     while( font ) {
1094       if( !strcasecmp(font->name, fontname) && (size>=font->size)
1095           && (best<font->size)) {
1096         ret = 1;
1097         osd->font = font;
1098         best = font->size;
1099         //lprintf ("best: font->name=%s, size=%d\n", font->name, font->size);
1100       }
1101       font = font->next;
1102     }
1103     if (ret)
1104       lprintf("native match for %s %1d: %s %1d\n",fontname,size,osd->font->name,osd->font->size);
1105     else
1106       lprintf("no native font matching %s %1d",fontname,size);
1107 
1108     return ret;
1109 }
1110 
1111 
1112 #ifdef HAVE_FT2
1113 
1114 # ifdef HAVE_FONTCONFIG
1115 /**
1116  * @brief Look up a font name using FontConfig library
1117  * @param osd The OSD object to load the font for.
1118  * @param fontname Name of the font to look up.
1119  * @param size Size of the font to look for.
1120  *
1121  * @return If the lookup was done correctly, a non-zero value is returned.
1122  */
osd_lookup_fontconfig(osd_object_t * osd,const char * const fontname,const int size)1123 static int osd_lookup_fontconfig( osd_object_t *osd, const char *const fontname, const int size ) {
1124   FcPattern *pat = NULL, *match = NULL;
1125   FcFontSet *fs = FcFontSetCreate();
1126   FcResult result;
1127 
1128   pat = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, fontname, FC_SIZE, FcTypeDouble, (double)size, NULL);
1129   FcConfigSubstitute(NULL, pat, FcMatchPattern);
1130   FcDefaultSubstitute(pat);
1131 
1132   match = FcFontMatch(NULL, pat, &result);
1133   FcPatternDestroy(pat);
1134 
1135   if ( ! match ) {
1136     FcFontSetDestroy(fs);
1137     xprintf(osd->renderer->stream->xine, XINE_VERBOSITY_LOG,
1138 	    _("osd: error matching font %s with FontConfig"), fontname);
1139     return 0;
1140   }
1141   FcFontSetAdd(fs, match);
1142 
1143   if ( fs->nfont != 0 ) {
1144     FcChar8 *filename = NULL;
1145     FcPatternGetString(fs->fonts[0], FC_FILE, 0, &filename);
1146     if ( ! FT_New_Face(osd->ft2->library, (const char*)filename, 0, &osd->ft2->face) ) {
1147       FcFontSetDestroy(fs);
1148       return 1;
1149     }
1150 
1151     xprintf(osd->renderer->stream->xine, XINE_VERBOSITY_LOG,
1152 	    _("osd: error loading font %s with FontConfig"), fontname);
1153   } else {
1154     xprintf(osd->renderer->stream->xine, XINE_VERBOSITY_LOG,
1155 	    _("osd: error looking up font %s with FontConfig"), fontname);
1156   }
1157 
1158   FcFontSetDestroy(fs);
1159   return 0;
1160 }
1161 # endif /* HAVE_FONTCONFIG */
1162 
1163 /**
1164  * @brief Look up a font file using XDG data directories.
1165  * @param osd The OSD object to load the font for.
1166  * @param fontname Name (absolute or relative) of the font to look up.
1167  *
1168  * @return If the lookup was done correctly, a non-zero value is returned.
1169  *
1170  * @see XDG Base Directory specification:
1171  *      http://standards.freedesktop.org/basedir-spec/latest/index.html
1172  */
osd_lookup_xdg(osd_object_t * osd,const char * const fontname)1173 static int osd_lookup_xdg (osd_object_t *osd, const char *const fontname) {
1174   const char * const *data_dirs = xdgSearchableDataDirectories (&osd->renderer->stream->xine->basedir_handle);
1175 
1176   if (data_dirs) {
1177     while ((*data_dirs) && (*data_dirs)[0]) {
1178       char fontpath[2048], *e = fontpath + sizeof (fontpath), *q = fontpath;
1179       FT_Error fte = FT_Err_Ok;
1180       q += strlcpy (q, *data_dirs, q - e);
1181       if (q > e)
1182         q = e;
1183       q += strlcpy (q, "/"PACKAGE"/fonts/", q - e);
1184       if (q > e)
1185         q = e;
1186       strlcpy (q, fontname, q - e);
1187       fte = FT_New_Face (osd->ft2->library, fontpath, 0, &osd->ft2->face);
1188       if (fte == FT_Err_Ok) {
1189         xprintf (osd->renderer->stream->xine, XINE_VERBOSITY_DEBUG,
1190           "osd: loaded font %s.\n", fontpath);
1191         return 1;
1192       }
1193       data_dirs++;
1194     }
1195   }
1196   xprintf (osd->renderer->stream->xine, XINE_VERBOSITY_LOG,
1197     _("osd: error loading font %s with in XDG data directories.\n"), fontname);
1198   return 0;
1199 }
1200 
osd_set_font_freetype2(osd_object_t * osd,const char * fontname,int size)1201 static int osd_set_font_freetype2( osd_object_t *osd, const char *fontname, int size ) {
1202   if (!osd->ft2) {
1203     osd->ft2 = calloc(1, sizeof(osd_ft2context_t));
1204     if(FT_Init_FreeType( &osd->ft2->library )) {
1205       xprintf(osd->renderer->stream->xine, XINE_VERBOSITY_LOG,
1206 	      _("osd: cannot initialize ft2 library\n"));
1207       _x_freep(&osd->ft2);
1208       return 0;
1209     }
1210   }
1211 
1212   if (osd->ft2->face) {
1213       FT_Done_Face (osd->ft2->face);
1214       osd->ft2->face = NULL;
1215   }
1216 
1217   do { /* while 0 */
1218 
1219     /*
1220     try to load font from current directory or from an absolute path
1221       we want to do this before trying osd_lookup_fontconfig
1222       (which doesn't handle filenames)
1223     */
1224     if ( FT_New_Face(osd->ft2->library, fontname, 0, &osd->ft2->face) == FT_Err_Ok )
1225       break;
1226     /*
1227 	try to find a native xine font and return 0 if it succeeds,
1228 	  allowing that to load.
1229 	  this has to happen before calling osd_lookup_fontconfig
1230 	  so that you don't get the system default font when trying
1231 	  to load e.g Cetus.
1232 	*/
1233     if (osd_lookup_native(osd,fontname,size))
1234 	  return 0;
1235 
1236 
1237 #ifdef HAVE_FONTCONFIG
1238     if ( osd_lookup_fontconfig(osd, fontname, size) )
1239       break;
1240 #endif
1241     if ( osd_lookup_xdg(osd, fontname) )
1242       break;
1243 
1244     osd_free_ft2 (osd);
1245     return 0;
1246   } while(0);
1247 
1248   if (FT_Set_Pixel_Sizes(osd->ft2->face, 0, size)) {
1249     xprintf(osd->renderer->stream->xine, XINE_VERBOSITY_LOG,
1250 	    _("osd: error setting font size (no scalable font?)\n"));
1251     osd_free_ft2 (osd);
1252     return 0;
1253   }
1254 
1255   osd->ft2->size = size;
1256   return 1;
1257 }
1258 #endif
1259 
1260 /*
1261   set the font of osd object
1262 */
1263 
osd_set_font(osd_object_t * osd,const char * fontname,int size)1264 static int osd_set_font( osd_object_t *osd, const char *fontname, int size) {
1265   int ret = 1;
1266 
1267   lprintf("osd=%p font '%s'\n", (void*)osd, fontname);
1268 
1269   pthread_mutex_lock (&osd->renderer->osd_mutex);
1270 
1271 #ifdef HAVE_FT2
1272   if ( ! osd_set_font_freetype2(osd, fontname, size) )
1273 #endif
1274     { /* If the FreeType2 loading failed
1275          (which can happen if it finds a native xine font)
1276        */
1277       osd->font = NULL;
1278       ret = osd_lookup_native(osd,fontname,size);
1279       if( ret ) {
1280         /* load font if needed */
1281         if( !osd->font->loaded )
1282           ret = osd_renderer_load_font(osd->renderer, osd->font->filename);
1283         if(!ret)
1284           osd->font = NULL;
1285       }
1286     }
1287 
1288   pthread_mutex_unlock (&osd->renderer->osd_mutex);
1289   return ret;
1290 }
1291 
1292 
1293 /*
1294  * search the character in the sorted array,
1295  *
1296  * returns ALIAS_CHARACTER_FONT if character 'code' isn't found,
1297  * returns 'n' on error
1298  */
osd_search(osd_fontchar_t * array,size_t n,uint16_t code)1299 static int osd_search(osd_fontchar_t *array, size_t n, uint16_t code) {
1300 
1301   size_t i, left, right;
1302 
1303   if (!n) return 0;
1304 
1305   left = 0;
1306   right = n - 1;
1307   while (left < right) {
1308     i = (left + right) >> 1;
1309     if (code <= array[i].code) right = i;
1310     else left = i + 1;
1311   }
1312 
1313   if (array[right].code == code)
1314     return right;
1315   else
1316     return ALIAS_CHARACTER_FONT < n ? ALIAS_CHARACTER_FONT : n;
1317 }
1318 
1319 
1320 #ifdef HAVE_ICONV
1321 /*
1322  * get next unicode value
1323  */
osd_iconv_getunicode(xine_t * xine,iconv_t cd,const char * encoding,ICONV_CONST char ** inbuf,size_t * inbytesleft)1324 static uint16_t osd_iconv_getunicode(xine_t *xine,
1325 				     iconv_t cd, const char *encoding, ICONV_CONST char **inbuf,
1326 				     size_t *inbytesleft) {
1327   uint16_t unicode;
1328   char *outbuf = (char*)&unicode;
1329   size_t outbytesleft = 2;
1330   size_t count;
1331 
1332   if (cd != (iconv_t)-1) {
1333     /* get unicode value from iconv */
1334     count = iconv(cd, inbuf, inbytesleft, &outbuf, &outbytesleft);
1335     if (count == (size_t)-1 && errno != E2BIG) {
1336       /* unknown character or character wider than 16 bits, try skip one byte */
1337       xprintf(xine, XINE_VERBOSITY_LOG,
1338 	      _("osd: unknown sequence starting with byte 0x%02X in encoding \"%s\", skipping\n"),
1339 	      (*inbuf)[0] & 0xFF, encoding);
1340       if (*inbytesleft) {
1341         (*inbytesleft)--;
1342         (*inbuf)++;
1343       }
1344       return ALIAS_CHARACTER_CONV;
1345     }
1346   } else {
1347     /* direct mapping without iconv */
1348     unicode = (unsigned char)(*inbuf)[0];
1349     (*inbuf)++;
1350     (*inbytesleft)--;
1351   }
1352 
1353   return unicode;
1354 }
1355 #endif
1356 
1357 
1358 /*
1359  * free iconv encoding
1360  */
osd_free_encoding(osd_object_t * osd)1361 static void osd_free_encoding(osd_object_t *osd) {
1362 #ifdef HAVE_ICONV
1363   if (osd->cd != (iconv_t)-1) {
1364     iconv_close(osd->cd);
1365     osd->cd = (iconv_t)-1;
1366   }
1367   _x_freep(&osd->encoding);
1368 #endif
1369 }
1370 
1371 
1372 /*
1373  * set encoding of text
1374  *
1375  * NULL ... no conversion (iso-8859-1)
1376  * ""   ... locale encoding
1377  */
osd_set_encoding(osd_object_t * osd,const char * encoding)1378 static int osd_set_encoding (osd_object_t *osd, const char *encoding) {
1379 #ifdef HAVE_ICONV
1380   char *enc;
1381 
1382   osd_free_encoding(osd);
1383 
1384   lprintf("osd=%p, encoding=%s\n", (void*)osd, encoding ? (encoding[0] ? encoding : "locale") : "no conversion");
1385   /* no conversion, use latin1 */
1386   if (!encoding) return 1;
1387   /* get encoding from system */
1388   if (!encoding[0]) {
1389     if ((enc = xine_get_system_encoding()) == NULL) {
1390       xprintf(osd->renderer->stream->xine, XINE_VERBOSITY_LOG,
1391 	      _("osd: can't find out current locale character set\n"));
1392       return 0;
1393     }
1394     lprintf("locale encoding='%s'\n", enc);
1395   } else
1396     enc = strdup(encoding);
1397 
1398   /* prepare conversion to UCS-2 */
1399   if ((osd->cd = iconv_open(UCS2_ENCODING, enc)) == (iconv_t)-1) {
1400     xprintf(osd->renderer->stream->xine, XINE_VERBOSITY_LOG,
1401 	    _("osd: unsupported conversion %s -> %s, no conversion performed\n"), enc, UCS2_ENCODING);
1402     free(enc);
1403     return 0;
1404   }
1405 
1406   osd->encoding = enc;
1407   return 1;
1408 #else
1409   return encoding == NULL;
1410 #endif /* HAVE_ICONV */
1411 }
1412 
1413 
1414 #define FONT_OVERLAP 1/10  /* overlap between consecutive characters */
1415 
1416 /* render text in current encoding on x,y position */
osd_render_text(osd_object_t * osd,int x1,int y1,const char * text,int color_base)1417 static int osd_render_text (osd_object_t *osd, int x1, int y1,
1418                             const char *text, int color_base) {
1419 
1420   osd_renderer_t *this = osd->renderer;
1421   int xleft = x1, i;
1422   const char *inbuf;
1423   uint16_t unicode;
1424   size_t inbytesleft;
1425 
1426   lprintf("osd=%p (%d,%d) \"%s\"\n", (void*)osd, x1, y1, text);
1427 
1428   /* some sanity checks for the color indices */
1429   if( color_base < 0 )
1430     color_base = 0;
1431   else if( color_base > OVL_PALETTE_SIZE - TEXT_PALETTE_SIZE )
1432     color_base = OVL_PALETTE_SIZE - TEXT_PALETTE_SIZE;
1433 
1434   pthread_mutex_lock (&this->osd_mutex);
1435   if ((x1 >= osd->width) || (y1 >= osd->height)) {
1436     pthread_mutex_unlock (&this->osd_mutex);
1437     return 0;
1438   }
1439 
1440   if (x1 < osd->x1)
1441     osd->x1 = x1 < 0 ? 0 : x1;
1442   if (y1 < osd->y1)
1443     osd->y1 = y1 < 0 ? 0 : y1;
1444   osd->area_touched = 1;
1445 
1446   inbuf = text;
1447   inbytesleft = strlen(text);
1448 
1449 #ifdef HAVE_FT2
1450   if (osd->ft2 && osd->ft2->face) {
1451     FT_UInt previous = 0;
1452     FT_Bool use_kerning = FT_HAS_KERNING (osd->ft2->face);
1453     int first = 1, yb = y1;
1454     uint8_t ctab[256];
1455 
1456     /* we will likely render more than 256 pixels, so preset a color table. */
1457     for (i = 0; i < 256; i++)
1458       ctab[i] = i / 25 + color_base;
1459 
1460     while (inbytesleft) {
1461       FT_GlyphSlot slot;
1462 #ifdef HAVE_ICONV
1463       unicode = osd_iconv_getunicode (this->stream->xine, osd->cd, osd->encoding,
1464         (ICONV_CONST char **)&inbuf, &inbytesleft);
1465 #else
1466       unicode = inbuf[0];
1467       inbuf++;
1468       inbytesleft--;
1469 #endif
1470       if (unicode == '\n') {
1471         y1 += osd->ft2->face->size->metrics.height / 64;
1472         if (!first)
1473           yb = y1;
1474         previous = 0;
1475         first = 1;
1476         if (x1 > osd->x2)
1477           osd->x2 = x1 > osd->width ? osd->width : x1;
1478         x1 = xleft;
1479         if (y1 >= osd->height)
1480           break;
1481         continue;
1482       }
1483       if (x1 >= osd->width)
1484         continue;
1485 
1486       slot = osd->ft2->face->glyph;
1487       i = FT_Get_Char_Index (osd->ft2->face, unicode);
1488 
1489       /* add kerning relative to the previous letter */
1490       if (use_kerning && previous && i) {
1491         FT_Vector delta;
1492         FT_Get_Kerning(osd->ft2->face, previous, i, KERNING_DEFAULT, &delta);
1493         x1 += delta.x / 64;
1494       }
1495       previous = i;
1496 
1497       if (FT_Load_Glyph(osd->ft2->face, i, FT_LOAD_FLAGS)) {
1498         xprintf(this->stream->xine, XINE_VERBOSITY_LOG, _("osd: error loading glyph\n"));
1499         continue;
1500       }
1501 
1502       if (slot->format != ft_glyph_format_bitmap) {
1503         if (FT_Render_Glyph(slot, ft_render_mode_normal))
1504           xprintf(this->stream->xine, XINE_VERBOSITY_LOG, _("osd: error in rendering glyph\n"));
1505       }
1506 
1507       /* if the first letter has a bearing not on the basepoint, shift the
1508        * whole output to be sure that we are inside the bounding box
1509        */
1510       if (first) x1 -= slot->bitmap_left;
1511       first = 0;
1512 
1513       {
1514         const uint8_t *s = (const uint8_t *)slot->bitmap.buffer;
1515         uint8_t *d = osd->area + x1 + slot->bitmap_left;
1516         int y, yt, lines = slot->bitmap.rows, cols = slot->bitmap.width;
1517         size_t pads = slot->bitmap.pitch - cols;
1518         size_t padd = osd->width - cols;
1519         /* we shift the whole glyph down by it's ascender so that the specified
1520          * coordinate is the top left corner which is much more practical than
1521          * the baseline as the user normally has no idea where the baseline is */
1522         yt = osd->ft2->face->size->metrics.ascender / 64 - slot->bitmap_top;
1523         if (yt < 0) { /* paranoia? */
1524           s -= yt * slot->bitmap.pitch;
1525           lines += yt;
1526           yt = 0;
1527         }
1528         yt += y1;
1529         d += yt * osd->width;
1530         /* clip top (XXX: is this at all possible?) */
1531         if (yt < 0) {
1532           s -= yt * slot->bitmap.pitch;
1533           d -= yt * osd->width;
1534           lines += yt;
1535         }
1536         /* clip bottom */
1537         y = osd->height - yt - lines;
1538         if (y < 0) {
1539           lines += y;
1540         }
1541         /* clip left (XXX: is this at all possible?) */
1542         if (x1 < 0) {
1543           s -= x1;
1544           d -= x1;
1545           pads -= x1;
1546           padd -= x1;
1547           cols += x1;
1548         }
1549         /* clip right */
1550         y = osd->width - x1 - cols;
1551         if (y < 0) {
1552           pads -= y;
1553           padd -= y;
1554           cols += y;
1555         }
1556         /* render this char (or not if there is too much clipping) */
1557         for (y = lines; y > 0; y--) {
1558           int x;
1559           for (x = cols; x > 0; x--) {
1560             if (*s) /* skip drawing transparency */
1561               *d = ctab[*s];
1562             s++;
1563             d++;
1564           }
1565           s += pads;
1566           d += padd;
1567         }
1568       }
1569       x1 += slot->advance.x / 64;
1570       if (x1 >= osd->width)
1571         break;
1572     }
1573     y1 += (osd->ft2->face->size->metrics.height / 64);
1574     /* mark the space down to the last nonempty line as dirty. */
1575     if (!first)
1576       yb = y1;
1577     if (yb > osd->y2)
1578       osd->y2 = yb > osd->width ? osd->width : yb;
1579     if (x1 > osd->x2)
1580       osd->x2 = x1 > osd->width ? osd->width : x1;
1581 
1582   } else
1583 #endif
1584   {
1585     osd_font_t *font = osd->font;
1586     int lineheight = 0, yb = y1;
1587     if (!font) {
1588       xprintf (this->stream->xine, XINE_VERBOSITY_LOG, _("osd: font isn't defined\n"));
1589       pthread_mutex_unlock (&this->osd_mutex);
1590       return 0;
1591     }
1592     while (inbytesleft) {
1593 #ifdef HAVE_ICONV
1594       unicode = osd_iconv_getunicode (this->stream->xine, osd->cd, osd->encoding,
1595         (ICONV_CONST char **)&inbuf, &inbytesleft);
1596 #else
1597       unicode = inbuf[0];
1598       inbuf++;
1599       inbytesleft--;
1600 #endif
1601       if (unicode == '\n') {
1602         y1 += font->size;
1603         if (lineheight)
1604           yb = y1;
1605         lineheight = 0;
1606         if (x1 > osd->x2)
1607           osd->x2 = x1 > osd->width ? osd->width : x1;
1608         x1 = xleft;
1609         if (y1 >= osd->height)
1610           break;
1611         continue;
1612       }
1613       if (x1 >= osd->width)
1614         continue;
1615 
1616       i = osd_search (font->fontchar, font->num_fontchars, unicode);
1617       lprintf ("font '%s' [%d, U+%04X == U+%04X] %dx%d -> %d,%d\n", font->name, i,
1618              unicode, font->fontchar[i].code, font->fontchar[i].width,
1619              font->fontchar[i].height, x1, y1);
1620       if (i != font->num_fontchars) {
1621         const uint8_t *s = font->fontchar[i].bmp;
1622         uint8_t *d = osd->area + y1 * osd->width + x1;
1623         int y, lines = font->fontchar[i].height, cols = font->fontchar[i].width;
1624         size_t pads = 0;
1625         size_t padd = osd->width - cols;
1626         /* clip top (XXX: is this at all possible?) */
1627         if (y1 < 0) {
1628           s -= y1 * cols;
1629           d -= y1 * osd->width;
1630           lines += y1;
1631         }
1632         /* clip bottom */
1633         y = osd->height - y1 - lines;
1634         if (y < 0) {
1635           lines += y;
1636         }
1637         /* clip left (XXX: is this at all possible?) */
1638         if (x1 < 0) {
1639           s -= x1;
1640           d -= x1;
1641           pads -= x1;
1642           padd -= x1;
1643           cols += x1;
1644         }
1645         /* clip right */
1646         y = osd->width - x1 - cols;
1647         if (y < 0) {
1648           pads -= y;
1649           padd -= y;
1650           cols += y;
1651         }
1652         /* render this char (or not if there is too much clipping) */
1653         for (y = lines; y > 0; y--) {
1654           int x;
1655           for (x = cols; x > 0; x--) {
1656             if (*s > 1) /* skip drawing transparency */
1657               *d = *s + (uint8_t)color_base;
1658             s++;
1659             d++;
1660           }
1661           s += pads;
1662           d += padd;
1663         }
1664         x1 += font->fontchar[i].width - (font->fontchar[i].width * FONT_OVERLAP);
1665         if (lineheight < font->fontchar[i].height)
1666           lineheight = font->fontchar[i].height;
1667       }
1668     }
1669     if (lineheight)
1670       yb = y1 + lineheight;
1671     if (osd->y2 < yb)
1672       osd->y2 = yb > osd->height ? osd->height : yb;
1673     if (x1 > osd->x2)
1674       osd->x2 = x1 > osd->width ? osd->width : x1;
1675 
1676   } /* !(osd->ft2 && osd->ft2->face) */
1677 
1678   pthread_mutex_unlock (&this->osd_mutex);
1679 
1680   return 1;
1681 }
1682 
1683 
1684 /*
1685   get width and height of how text will be renderized
1686 */
osd_get_text_size(osd_object_t * osd,const char * text,int * width,int * height)1687 static int osd_get_text_size(osd_object_t *osd, const char *text, int *width, int *height) {
1688 
1689   osd_renderer_t *this = osd->renderer;
1690   int i;
1691   const char *inbuf;
1692   uint16_t unicode;
1693   size_t inbytesleft;
1694 
1695   lprintf("osd=%p \"%s\"\n", (void*)osd, text);
1696   inbuf = text;
1697   inbytesleft = strlen (text);
1698   *width = 0;
1699   *height = 0;
1700 
1701   pthread_mutex_lock (&this->osd_mutex);
1702 
1703 #ifdef HAVE_FT2
1704   if (osd->ft2 && osd->ft2->face) {
1705     int y1 = 0, linewidth = 0;
1706     /* not all free type fonts provide kerning */
1707     FT_Bool use_kerning = FT_HAS_KERNING (osd->ft2->face);
1708     FT_UInt previous = 0;
1709     int first_glyph = 1;
1710     while (inbytesleft) {
1711       FT_GlyphSlot slot;
1712 #ifdef HAVE_ICONV
1713       unicode = osd_iconv_getunicode (this->stream->xine, osd->cd, osd->encoding,
1714         (ICONV_CONST char **)&inbuf, &inbytesleft);
1715 #else
1716       unicode = inbuf[0];
1717       inbuf++;
1718       inbytesleft--;
1719 #endif
1720       if (unicode == '\n') {
1721         y1 += osd->ft2->face->size->metrics.height / 64;
1722         /* see last char comment below */
1723         if (!first_glyph) {
1724           *height = y1;
1725           if (osd->ft2->face->glyph->bitmap.width)
1726             linewidth -= osd->ft2->face->glyph->advance.x / 64;
1727           linewidth += osd->ft2->face->glyph->bitmap.width;
1728           linewidth += osd->ft2->face->glyph->bitmap_left;
1729         }
1730         if (*width < linewidth)
1731           *width = linewidth;
1732         linewidth = 0;
1733         previous = 0;
1734         first_glyph = 1;
1735         continue;
1736       }
1737       slot = osd->ft2->face->glyph;
1738       i = FT_Get_Char_Index (osd->ft2->face, unicode);
1739       /* kerning add the relative to the previous letter */
1740       if (use_kerning && previous && i) {
1741         FT_Vector delta;
1742         FT_Get_Kerning (osd->ft2->face, previous, i, KERNING_DEFAULT, &delta);
1743         linewidth += delta.x / 64;
1744       }
1745       previous = i;
1746       if (FT_Load_Glyph (osd->ft2->face, i, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING)) {
1747         xprintf (this->stream->xine, XINE_VERBOSITY_LOG, _("osd: error loading glyph %i\n"), i);
1748         text++;
1749         continue;
1750       }
1751       if (slot->format != ft_glyph_format_bitmap) {
1752         if (FT_Render_Glyph(osd->ft2->face->glyph, ft_render_mode_normal))
1753           xprintf (this->stream->xine, XINE_VERBOSITY_LOG, _("osd: error in rendering\n"));
1754       }
1755       /* left shows the left edge relative to the base point. A positive value means the
1756        * letter is shifted right, so we need to subtract the value from the width
1757        */
1758       if (first_glyph) linewidth -= slot->bitmap_left;
1759       first_glyph = 0;
1760       linewidth += slot->advance.x / 64;
1761       text++;
1762     }
1763     y1 += osd->ft2->face->size->metrics.height / 64;
1764     /* if we have a true type font we need to do some corrections for the last
1765      * letter. As this one is still in the gylph slot we can still work with
1766      * it. For the last letter be must not use advance and width but the real
1767      * width of the bitmap. We're right from the base point so we subtract the
1768      * advance value that was added in the for-loop and add the width. We have
1769      * to also add the left bearing because the letter might be shifted left or
1770      * right and then the right edge is also shifted
1771      */
1772     if (!first_glyph) {
1773       *height = y1;
1774       if (osd->ft2->face->glyph->bitmap.width)
1775         linewidth -= osd->ft2->face->glyph->advance.x / 64;
1776       linewidth += osd->ft2->face->glyph->bitmap.width;
1777       linewidth += osd->ft2->face->glyph->bitmap_left;
1778     }
1779     if (*width < linewidth)
1780       *width = linewidth;
1781 
1782   } else
1783 #endif
1784   {
1785     int y1 = 0, linewidth = 0, lineheight = 0;
1786     osd_font_t *font = osd->font;
1787     if (!font) {
1788       xprintf (this->stream->xine, XINE_VERBOSITY_LOG, _("osd: font isn't defined\n"));
1789       pthread_mutex_unlock (&this->osd_mutex);
1790       return 0;
1791     }
1792     while (inbytesleft) {
1793 #ifdef HAVE_ICONV
1794       unicode = osd_iconv_getunicode (this->stream->xine, osd->cd, osd->encoding,
1795         (ICONV_CONST char **)&inbuf, &inbytesleft);
1796 #else
1797       unicode = inbuf[0];
1798       inbuf++;
1799       inbytesleft--;
1800 #endif
1801       if (unicode == '\n') {
1802         y1 += font->size;
1803         if (lineheight)
1804           *height = y1;
1805         if (*width < linewidth)
1806           *width = linewidth;
1807         linewidth = 0;
1808         lineheight = 0;
1809         continue;
1810       }
1811       i = osd_search (font->fontchar, font->num_fontchars, unicode);
1812       if (i != font->num_fontchars) {
1813         if (font->fontchar[i].height > lineheight)
1814           lineheight = font->fontchar[i].height;
1815         linewidth += font->fontchar[i].width - (font->fontchar[i].width * FONT_OVERLAP);
1816       }
1817     }
1818     if (lineheight)
1819       *height = y1 + lineheight;
1820     if (*width < linewidth)
1821       *width = linewidth;
1822 
1823   } /* !(osd->ft2 && osd->ft2->face) */
1824 
1825   pthread_mutex_unlock (&this->osd_mutex);
1826 
1827   return 1;
1828 }
1829 
1830 /*
1831  * free osd object
1832  */
1833 
osd_free_object(osd_object_t * osd_to_close)1834 static void osd_free_object (osd_object_t *osd_to_close) {
1835 
1836   osd_renderer_t *this = osd_to_close->renderer;
1837   xine_private_t *xine = (xine_private_t *)this->stream->xine;
1838   video_overlay_manager_t *ovl_manager;
1839   osd_object_t *osd, *last;
1840 
1841   if( osd_to_close->handle >= 0 ) {
1842     osd_hide(osd_to_close,0);
1843 
1844     this->event.object.handle = osd_to_close->handle;
1845 
1846     /* not really needed this, but good pratice to clean it up */
1847     memset( this->event.object.overlay, 0, sizeof(*this->event.object.overlay) );
1848     this->event.event_type = OVERLAY_EVENT_FREE_HANDLE;
1849     this->event.vpts = 0;
1850 
1851     xine->port_ticket->acquire (xine->port_ticket, 1);
1852     ovl_manager = this->stream->video_out->get_overlay_manager(this->stream->video_out);
1853     ovl_manager->add_event(ovl_manager, (void *)&this->event);
1854     xine->port_ticket->release (xine->port_ticket, 1);
1855 
1856     osd_to_close->handle = -1; /* handle will be freed */
1857   }
1858 
1859   if (osd_to_close->argb_layer) {
1860     /* clear argb buffer pointer so that buffer may be freed safely after returning */
1861     this->set_argb_buffer(osd_to_close, NULL, 0, 0, 0, 0);
1862     set_argb_layer_ptr(&osd_to_close->argb_layer, NULL);
1863   }
1864 
1865   pthread_mutex_lock (&this->osd_mutex);
1866 
1867   last = NULL;
1868   osd = this->osds;
1869   while( osd ) {
1870     if ( osd == osd_to_close ) {
1871       _x_freep( &osd->area );
1872 
1873       osd_free_ft2 (osd);
1874       osd_free_encoding(osd);
1875 
1876       if( last )
1877         last->next = osd->next;
1878       else
1879         this->osds = osd->next;
1880 
1881       free( osd );
1882       break;
1883     }
1884     last = osd;
1885     osd = osd->next;
1886   }
1887   pthread_mutex_unlock (&this->osd_mutex);
1888 }
1889 
osd_renderer_close(osd_renderer_t * this_gen)1890 static void osd_renderer_close (osd_renderer_t *this_gen) {
1891   osd_renderer_private_t *this = (osd_renderer_private_t *)this_gen;
1892 
1893   if (this->r.event.object.overlay == &this->ovl)
1894     this->xine->config->unregister_callbacks (this->xine->config, NULL, NULL, &this->r, sizeof (*this));
1895 
1896   while (this->r.osds)
1897     osd_free_object (this->r.osds);
1898 
1899   while (this->r.fonts)
1900     osd_renderer_unload_font (&this->r, this->r.fonts->name);
1901 
1902   pthread_mutex_destroy (&this->r.osd_mutex);
1903 
1904   if (this->r.event.object.overlay != &this->ovl)
1905     _x_freep (&this->r.event.object.overlay);
1906 
1907   free (this);
1908 }
1909 
1910 
update_text_palette(void * this_gen,xine_cfg_entry_t * entry)1911 static void update_text_palette(void *this_gen, xine_cfg_entry_t *entry)
1912 {
1913   osd_renderer_t *this = (osd_renderer_t *)this_gen;
1914 
1915   this->textpalette = entry->num_value;
1916   lprintf("palette will be '%s'\n", textpalettes_str[this->textpalette] );
1917 }
1918 
osd_draw_bitmap(osd_object_t * osd,const uint8_t * bitmap,int x1,int y1,int width,int height,const uint8_t * palette_map)1919 static void osd_draw_bitmap(osd_object_t *osd, const uint8_t *bitmap,
1920 			    int x1, int y1, int width, int height,
1921                             const uint8_t *palette_map)
1922 {
1923   int y, x;
1924 
1925   lprintf("osd=%p at (%d,%d) %dx%d\n", (void*)osd, x1,y1, width,height );
1926 
1927   _update_clipping(osd, x1, y1, x1 + width, y1 + height);
1928 
1929   for( y=0; y<height; y++ ) {
1930     if ( palette_map ) {
1931       int src_offset = y * width;
1932       int dst_offset = (y1+y) * osd->width + x1;
1933       /* Slow copy with palette translation, the map describes how to
1934          convert color indexes in the source bitmap to indexes in the
1935          osd palette */
1936       for ( x=0; x<width; x++ ) {
1937 	osd->area[dst_offset+x] = palette_map[bitmap[src_offset+x]];
1938       }
1939     } else {
1940       /* Fast copy with direct mapping */
1941       memcpy(osd->area + (y1+y) * osd->width + x1, bitmap + y * width, width);
1942     }
1943   }
1944 }
1945 
osd_set_argb_buffer(osd_object_t * osd,uint32_t * argb_buffer,int dirty_x,int dirty_y,int dirty_width,int dirty_height)1946 static void osd_set_argb_buffer(osd_object_t *osd, uint32_t *argb_buffer,
1947     int dirty_x, int dirty_y, int dirty_width, int dirty_height)
1948 {
1949   if (!osd->argb_layer)
1950     set_argb_layer_ptr(&osd->argb_layer, argb_layer_create());
1951 
1952   if (osd->argb_layer->buffer != argb_buffer) {
1953     dirty_x = 0;
1954     dirty_y = 0;
1955     dirty_width = osd->width;
1956     dirty_height = osd->height;
1957   }
1958 
1959   /* keep osd_object clipping behavior */
1960   osd->x1 = MIN( osd->x1, dirty_x );
1961   osd->x2 = MAX( osd->x2, dirty_x + dirty_width );
1962   osd->y1 = MIN( osd->y1, dirty_y );
1963   osd->y2 = MAX( osd->y2, dirty_y + dirty_height );
1964 
1965   pthread_mutex_lock(&osd->argb_layer->mutex);
1966 
1967   /* argb layer update area accumulation */
1968   osd->argb_layer->x1 = MIN( osd->argb_layer->x1, dirty_x );
1969   osd->argb_layer->x2 = MAX( osd->argb_layer->x2, dirty_x + dirty_width );
1970   osd->argb_layer->y1 = MIN( osd->argb_layer->y1, dirty_y );
1971   osd->argb_layer->y2 = MAX( osd->argb_layer->y2, dirty_y + dirty_height );
1972 
1973   osd->argb_layer->buffer = argb_buffer;
1974 
1975   pthread_mutex_unlock(&osd->argb_layer->mutex);
1976 }
1977 
osd_get_capabilities(osd_object_t * osd)1978 static uint32_t osd_get_capabilities (osd_object_t *osd) {
1979 
1980   osd_renderer_t *this = osd->renderer;
1981   xine_private_t *xine = (xine_private_t *)this->stream->xine;
1982   uint32_t capabilities = 0;
1983   uint32_t vo_capabilities;
1984 
1985 #ifdef HAVE_FT2
1986   capabilities |= XINE_OSD_CAP_FREETYPE2;
1987 #endif
1988 
1989   xine->port_ticket->acquire (xine->port_ticket, 1);
1990   vo_capabilities = this->stream->video_out->get_capabilities(this->stream->video_out);
1991   xine->port_ticket->release (xine->port_ticket, 1);
1992 
1993   if (vo_capabilities & VO_CAP_UNSCALED_OVERLAY)
1994     capabilities |= XINE_OSD_CAP_UNSCALED;
1995 
1996   if (vo_capabilities & VO_CAP_CUSTOM_EXTENT_OVERLAY)
1997     capabilities |= XINE_OSD_CAP_CUSTOM_EXTENT;
1998 
1999   if (vo_capabilities & VO_CAP_ARGB_LAYER_OVERLAY)
2000     capabilities |= XINE_OSD_CAP_ARGB_LAYER;
2001 
2002   if (vo_capabilities & VO_CAP_VIDEO_WINDOW_OVERLAY)
2003     capabilities |= XINE_OSD_CAP_VIDEO_WINDOW;
2004 
2005   return capabilities;
2006 }
2007 
2008 
2009 /*
2010  * initialize the osd rendering engine
2011  */
2012 
_x_osd_renderer_init(xine_stream_t * stream)2013 osd_renderer_t *_x_osd_renderer_init( xine_stream_t *stream ) {
2014 
2015   osd_renderer_private_t *this;
2016 
2017   this = calloc (1, sizeof (*this));
2018   if (!this)
2019     return NULL;
2020 
2021   this->r.stream = stream;
2022   this->r.event.object.overlay = &this->ovl;
2023   this->xine = stream->xine;
2024 
2025   pthread_mutex_init (&this->r.osd_mutex, NULL);
2026 
2027   /*
2028    * load available fonts
2029    */
2030   {
2031     const char * const *data_dirs = xdgSearchableDataDirectories (&stream->xine->basedir_handle);
2032     if (data_dirs) {
2033       char fontpath[2048], *e = fontpath + sizeof (fontpath);
2034       while ((*data_dirs) && (*data_dirs)[0]) {
2035         DIR *dir;
2036         char *q = fontpath;
2037         q += strlcpy (q, *data_dirs, e - q);
2038         if (q > e)
2039           q = e;
2040         q += strlcpy (q, "/"PACKAGE"/fonts/", e - q);
2041         if (q > e)
2042           q = e;
2043         lprintf ("path='%s'\n", fontpath);
2044         dir = opendir (fontpath);
2045         if (dir) {
2046           int n = 0;
2047           struct dirent *entry;
2048           while ((entry = readdir (dir)) != NULL) {
2049             char *s, *d;
2050             osd_font_t *font;
2051             size_t len = strlen (entry->d_name);
2052             if (len <= 12)
2053               continue;
2054             if (strncmp (entry->d_name + len - 12, ".xinefont.gz", 12))
2055               continue;
2056             s = q + strlcpy (q, entry->d_name, e - q) + 1;
2057             if (s > e)
2058               s = e;
2059             d = strchr (q, '-');
2060             if (!d)
2061               continue;
2062             font = calloc (1, sizeof (*font) + s - fontpath);
2063             if (!font)
2064               continue;
2065             font->filename = (char *)font + sizeof (*font);
2066             memcpy (font->filename, fontpath, s - fontpath);
2067             *d++ = 0;
2068             strlcpy (font->name, q, sizeof (font->name));
2069             {
2070               const char *d1 = d;
2071               font->size = xine_str2uint32 (&d1);
2072             }
2073             lprintf ("font '%s' size %d is preloaded\n", font->name, font->size);
2074             font->next = this->r.fonts;
2075             this->r.fonts = font;
2076             n++;
2077           }
2078           closedir (dir);
2079           if (n) {
2080             *q = 0;
2081             xprintf (this->xine, XINE_VERBOSITY_DEBUG,
2082               "osd: found %d xine fonts in %s.\n", n, fontpath);
2083           }
2084         }
2085         data_dirs++;
2086       }
2087     }
2088   }
2089 
2090   this->r.textpalette = this->xine->config->register_enum (this->xine->config,
2091     "ui.osd.text_palette", 0, (char **)textpalettes_str,
2092     _("palette (foreground-border-background) to use for subtitles and OSD"),
2093     _("The palette for on-screen-display and some subtitle formats that do "
2094       "not specify any colouring themselves. The palettes are listed in the "
2095       "form: foreground-border-background."),
2096     10, update_text_palette, &this->r);
2097 
2098   /*
2099    * set up function pointer
2100    */
2101 
2102   this->r.new_object         = osd_new_object;
2103   this->r.free_object        = osd_free_object;
2104   this->r.show               = osd_show_scaled;
2105   this->r.hide               = osd_hide;
2106   this->r.set_palette        = osd_set_palette;
2107   this->r.set_text_palette   = osd_set_text_palette;
2108   this->r.get_palette        = osd_get_palette;
2109   this->r.set_position       = osd_set_position;
2110   this->r.set_font           = osd_set_font;
2111   this->r.clear              = osd_clear;
2112   this->r.point              = osd_point;
2113   this->r.line               = osd_line;
2114   this->r.filled_rect        = osd_filled_rect;
2115   this->r.set_encoding       = osd_set_encoding;
2116   this->r.render_text        = osd_render_text;
2117   this->r.get_text_size      = osd_get_text_size;
2118   this->r.close              = osd_renderer_close;
2119   this->r.draw_bitmap        = osd_draw_bitmap;
2120   this->r.set_argb_buffer    = osd_set_argb_buffer;
2121   this->r.show_unscaled      = osd_show_unscaled;
2122   this->r.get_capabilities   = osd_get_capabilities;
2123   this->r.set_extent         = osd_set_extent;
2124   this->r.set_video_window   = osd_set_video_window;
2125 
2126   return &this->r;
2127 }
2128