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