/* 2004.02.01 first released source code for IOMP */ /* * Copyright (C) 2000-2002 the xine project * * This file is part of xine, a free video player. * * xine is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * xine is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * * OSD stuff (text and graphic primitives) */ #define __OSD_C__ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #ifndef _MSC_VER #include #endif /* _MSC_VER */ #ifdef HAVE_LANGINFO_CODESET #include #endif #include "xine_internal.h" #include "video_out/alphablend.h" #include "xine-engine/bswap.h" #include "xineutils.h" #include "video_out.h" #include "osd.h" #define HAVE_FT2 #ifdef HAVE_FT2 #include #include FT_FREETYPE_H #endif /* #define LOG_DEBUG 1 */ #define BINARY_SEARCH 1 /* unicode value of alias character, * used if conversion fails */ #define ALIAS_CHARACTER_CONV '#' /* unicode value of alias character, * used if character isn't in the font */ #define ALIAS_CHARACTER_FONT '_' #ifdef MAX #undef MAX #endif #define MAX(a,b) ( (a) > (b) ) ? (a) : (b) #ifdef MIN #undef MIN #endif #define MIN(a,b) ( (a) < (b) ) ? (a) : (b) int previous_timestamp = 0; /* referenced in video_out/alphablend.c and libspudec/spu.c */ int frame_rate_code = 4; /* reference in video_out/alphablend.c, default 29.97 fps */ int osd_frame_counter = -1; /* reference in video_out/alphablend.c */ int osd_is_showing = 1; /* reference in video_out/alphablend.c */ int forced_display = 0; /* reference in libspudec/spu.c */ int enable_alpha = 0; /* reference in libspudec/spu.c */ int enable_color = 0; /* reference in libspudec/spu.c */ int spu_current_enable = 1; /* reference in libspudec/spu.c */ typedef struct osd_fontchar_s { uint16_t code; uint16_t width; uint16_t height; uint8_t *bmp; } osd_fontchar_t; struct osd_font_s { char name[40]; uint16_t version; uint16_t size; uint16_t num_fontchars; osd_fontchar_t *fontchar; osd_font_t *next; }; #ifdef HAVE_FT2 struct osd_ft2context_s { int useme; FT_Library library; FT_Face face; int size; }; #endif /* * open a new osd object. this will allocated an empty (all zero) drawing * area where graphic primitives may be used. * It is ok to specify big width and height values. The render will keep * track of the smallest changed area to not generate too big overlays. * A default palette is initialized (i sugest keeping color 0 as transparent * for the sake of simplicity) */ static osd_object_t *osd_new_object (osd_renderer_t *this, int width, int height) { osd_object_t *osd; pthread_mutex_lock (&this->osd_mutex); osd = xine_xmalloc( sizeof(osd_object_t) ); osd->renderer = this; osd->next = this->osds; this->osds = osd; osd->width = width; osd->height = height; osd->area = xine_xmalloc( width * height ); osd->x1 = width; osd->y1 = height; osd->x2 = 0; osd->y2 = 0; memcpy(osd->color, textpalettes_color[0], sizeof(textpalettes_color[0])); memcpy(osd->trans, textpalettes_trans[0], sizeof(textpalettes_trans[0])); osd->handle = -1; #ifndef _MSC_VER osd->cd = (iconv_t)-1; osd->encoding = NULL; #endif pthread_mutex_unlock (&this->osd_mutex); #ifdef LOG_DEBUG printf("osd_open %p [%dx%d]\n", osd, width, height); #endif return osd; } /* * send the osd to be displayed at given pts (0=now) * the object is not changed. there may be subsequent drawing on it. */ static int osd_show (osd_object_t *osd, int64_t vpts ) { osd_renderer_t *this = osd->renderer; rle_elem_t rle, *rle_p=0; int x, y, spare; uint8_t *c; #ifdef LOG_DEBUG printf("osd_show %p vpts=%lld\n", osd, vpts); #endif if( osd->handle < 0 ) { if( (osd->handle = this->video_overlay->get_handle(this->video_overlay,0)) == -1 ) { return 0; } } pthread_mutex_lock (&this->osd_mutex); /* check if osd is valid (something drawn on it) */ if( osd->x2 >= osd->x1 ) { this->event.object.handle = osd->handle; if(osd->x1 > osd->width) osd->x1 = osd->width; if(osd->x2 > osd->width) osd->x2 = osd->width; if(osd->y1 > osd->height) osd->y1 = osd->height; if(osd->y2 > osd->height) osd->y2 = osd->height; memset( this->event.object.overlay, 0, sizeof(*this->event.object.overlay) ); this->event.object.overlay->x = osd->display_x + osd->x1; this->event.object.overlay->y = osd->display_y + osd->y1; this->event.object.overlay->width = osd->x2 - osd->x1 + 1; this->event.object.overlay->height = osd->y2 - osd->y1 + 1; this->event.object.overlay->clip_top = -1; this->event.object.overlay->clip_bottom = this->event.object.overlay->height + osd->display_y; this->event.object.overlay->clip_left = 0; this->event.object.overlay->clip_right = this->event.object.overlay->width + osd->display_x; spare = osd->y2 - osd->y1; this->event.object.overlay->num_rle = 0; this->event.object.overlay->data_size = 1024; rle_p = this->event.object.overlay->rle = malloc(this->event.object.overlay->data_size * sizeof(rle_elem_t) ); for( y = osd->y1; y <= osd->y2; y++ ) { rle.len = 0; rle.color = 0; c = osd->area + y * osd->width + osd->x1; for( x = osd->x1; x <= osd->x2; x++, c++ ) { if( rle.color != *c ) { if( rle.len ) { if( (this->event.object.overlay->num_rle + spare) > this->event.object.overlay->data_size ) { this->event.object.overlay->data_size += 1024; rle_p = this->event.object.overlay->rle = realloc( this->event.object.overlay->rle, this->event.object.overlay->data_size * sizeof(rle_elem_t) ); rle_p += this->event.object.overlay->num_rle; } *rle_p++ = rle; this->event.object.overlay->num_rle++; } rle.color = *c; rle.len = 1; } else { rle.len++; } } *rle_p++ = rle; this->event.object.overlay->num_rle++; } #ifdef LOG_DEBUG printf("osd_show num_rle = %d\n", this->event.object.overlay->num_rle); #endif memcpy(this->event.object.overlay->clip_color, osd->color, sizeof(osd->color)); memcpy(this->event.object.overlay->clip_trans, osd->trans, sizeof(osd->trans)); memcpy(this->event.object.overlay->color, osd->color, sizeof(osd->color)); memcpy(this->event.object.overlay->trans, osd->trans, sizeof(osd->trans)); this->event.event_type = OVERLAY_EVENT_SHOW; this->event.vpts = vpts; this->video_overlay->add_event(this->video_overlay,(void *)&this->event); } pthread_mutex_unlock (&this->osd_mutex); return 1; } /* * send event to hide osd at given pts (0=now) * the object is not changed. there may be subsequent drawing on it. */ static int osd_hide (osd_object_t *osd, int64_t vpts) { osd_renderer_t *this = osd->renderer; #ifdef LOG_DEBUG printf("osd_hide %p vpts=%lld\n",osd, vpts); #endif if( osd->handle < 0 ) return 0; pthread_mutex_lock (&this->osd_mutex); this->event.object.handle = osd->handle; /* not really needed this, but good pratice to clean it up */ memset( this->event.object.overlay, 0, sizeof(this->event.object.overlay) ); this->event.event_type = OVERLAY_EVENT_HIDE; this->event.vpts = vpts; this->video_overlay->add_event(this->video_overlay,(void *)&this->event); pthread_mutex_unlock (&this->osd_mutex); return 1; } /* * clear an osd object, so that it can be used for rendering a new image */ static void osd_clear (osd_object_t *osd) { #ifdef LOG_DEBUG printf("osd_clear\n"); #endif memset(osd->area, 0, osd->width * osd->height); osd->x1 = osd->width; osd->y1 = osd->height; osd->x2 = 0; osd->y2 = 0; } /* * Draw a point. */ static void osd_point (osd_object_t *osd, int x, int y, int color) { uint8_t *c; #ifdef LOG_DEBUG printf("osd_point %p (%d x %d)\n", osd, x, y); #endif /* update clipping area */ osd->x1 = MIN(osd->x1, x); osd->x2 = MAX(osd->x2, (x + 1)); osd->y1 = MIN(osd->y1, y); osd->y2 = MAX(osd->y2, (y + 1)); c = osd->area + y * osd->width + x; if(c <= (osd->area + (osd->width * osd->height))) *c = color; } /* * Bresenham line implementation on osd object */ static void osd_line (osd_object_t *osd, int x1, int y1, int x2, int y2, int color) { uint8_t *c; int dx, dy, t, inc, d, inc1, inc2; #ifdef LOG_DEBUG printf("osd_line %p (%d,%d)-(%d,%d)\n",osd, x1,y1, x2,y2 ); #endif /* update clipping area */ t = MIN( x1, x2 ); osd->x1 = MIN( osd->x1, t ); t = MAX( x1, x2 ); osd->x2 = MAX( osd->x2, t ); t = MIN( y1, y2 ); osd->y1 = MIN( osd->y1, t ); t = MAX( y1, y2 ); osd->y2 = MAX( osd->y2, t ); dx = abs(x1-x2); dy = abs(y1-y2); if( dx>=dy ) { if( x1>x2 ) { t = x2; x2 = x1; x1 = t; t = y2; y2 = y1; y1 = t; } if( y2 > y1 ) inc = 1; else inc = -1; inc1 = 2*dy; d = inc1 - dx; inc2 = 2*(dy-dx); c = osd->area + y1 * osd->width + x1; while(x1area + (osd->width * osd->height))) *c = color; c++; x1++; if( d<0 ) { d+=inc1; } else { y1+=inc; d+=inc2; c = osd->area + y1 * osd->width + x1; } } } else { if( y1>y2 ) { t = x2; x2 = x1; x1 = t; t = y2; y2 = y1; y1 = t; } if( x2 > x1 ) inc = 1; else inc = -1; inc1 = 2*dx; d = inc1-dy; inc2 = 2*(dx-dy); c = osd->area + y1 * osd->width + x1; while(y1area + (osd->width * osd->height))) *c = color; c += osd->width; y1++; if( d<0 ) { d+=inc1; } else { x1+=inc; d+=inc2; c = osd->area + y1 * osd->width + x1; } } } } /* * filled retangle */ static void osd_filled_rect (osd_object_t *osd, int x1, int y1, int x2, int y2, int color) { int x, y, dx, dy; #ifdef LOG_DEBUG printf("osd_filled_rect %p (%d,%d)-(%d,%d)\n",osd, x1,y1, x2,y2 ); #endif /* update clipping area */ x = MIN( x1, x2 ); osd->x1 = MIN( osd->x1, x ); dx = MAX( x1, x2 ); osd->x2 = MAX( osd->x2, dx ); y = MIN( y1, y2 ); osd->y1 = MIN( osd->y1, y ); dy = MAX( y1, y2 ); osd->y2 = MAX( osd->y2, dy ); dx -= x; dy -= y; for( ; dy--; y++ ) { memset(osd->area + y * osd->width + x,color,dx); } } /* * set palette (color and transparency) */ static void osd_set_palette(osd_object_t *osd, const uint32_t *color, const uint8_t *trans ) { memcpy(osd->color, color, sizeof(osd->color)); memcpy(osd->trans, trans, sizeof(osd->trans)); } /* * set on existing text palette * (-1 to set user specified palette) */ static void osd_set_text_palette(osd_object_t *osd, int palette_number, int color_base) { if( palette_number < 0 ) palette_number = osd->renderer->textpalette; /* some sanity checks for the color indices */ if( color_base < 0 ) color_base = 0; else if( color_base > OVL_PALETTE_SIZE - TEXT_PALETTE_SIZE ) color_base = OVL_PALETTE_SIZE - TEXT_PALETTE_SIZE; memcpy(&osd->color[color_base], textpalettes_color[palette_number], sizeof(textpalettes_color[palette_number])); memcpy(&osd->trans[color_base], textpalettes_trans[palette_number], sizeof(textpalettes_trans[palette_number])); } /* * get palette (color and transparency) */ static void osd_get_palette (osd_object_t *osd, uint32_t *color, uint8_t *trans) { memcpy(color, osd->color, sizeof(osd->color)); memcpy(trans, osd->trans, sizeof(osd->trans)); } /* * set position were overlay will be blended */ static void osd_set_position (osd_object_t *osd, int x, int y) { if( x < 0 || x > 0x10000 ) x = 0; if( y < 0 || y > 0x10000 ) y = 0; osd->display_x = x; osd->display_y = y; } static uint16_t gzread_i16(gzFile *fp) { uint16_t ret; ret = gzgetc(fp); ret |= (gzgetc(fp)<<8); return ret; } /* load bitmap font into osd engine */ static int osd_renderer_load_font(osd_renderer_t *this, char *filename) { gzFile *fp; osd_font_t *font = NULL; int i, ret = 0; #ifdef LOG_DEBUG printf("osd: renderer_load_font %p name=%s\n", this, filename ); #endif pthread_mutex_lock (&this->osd_mutex); /* load quick & dirt font format */ /* fixme: check read errors... */ if( (fp = gzopen(filename,"rb")) != NULL ) { font = xine_xmalloc( sizeof(osd_font_t) ); gzread(fp, font->name, sizeof(font->name) ); font->version = gzread_i16(fp); font->size = gzread_i16(fp); font->num_fontchars = gzread_i16(fp); font->fontchar = malloc( sizeof(osd_fontchar_t) * font->num_fontchars ); #ifdef LOG_DEBUG printf("osd: font %s %d\n", font->name, font->num_fontchars); #endif for( i = 0; i < font->num_fontchars; i++ ) { font->fontchar[i].code = gzread_i16(fp); font->fontchar[i].width = gzread_i16(fp); font->fontchar[i].height = gzread_i16(fp); font->fontchar[i].bmp = malloc(font->fontchar[i].width*font->fontchar[i].height); if( gzread(fp, font->fontchar[i].bmp, font->fontchar[i].width*font->fontchar[i].height) <= 0 ) break; } if( i == font->num_fontchars ) { ret = 1; #ifdef LOG_DEBUG printf("osd: font %s loading ok\n",font->name); #endif font->next = this->fonts; this->fonts = font; } else { #ifdef LOG_DEBUG printf("osd: font %s loading failed (%d < %d)\n",font->name, i, font->num_fontchars); #endif while( --i >= 0 ) { free(font->fontchar[i].bmp); } free(font->fontchar); free(font); } gzclose(fp); } pthread_mutex_unlock (&this->osd_mutex); return ret; } /* * unload font */ static int osd_renderer_unload_font(osd_renderer_t *this, char *fontname ) { osd_font_t *font, *last; osd_object_t *osd; int i, ret = 0; #ifdef LOG_DEBUG printf("osd_renderer_unload_font %p name=%s\n", this, fontname); #endif pthread_mutex_lock (&this->osd_mutex); osd = this->osds; while( osd ) { if( !strcmp(osd->font->name, fontname) ) osd->font = NULL; osd = osd->next; } last = NULL; font = this->fonts; while( font ) { if ( !strcmp(font->name,fontname) ) { for( i = 0; i < font->num_fontchars; i++ ) { free( font->fontchar[i].bmp ); } free( font->fontchar ); if( last ) last->next = font->next; else this->fonts = font->next; free( font ); ret = 1; break; } last = font; font = font->next; } pthread_mutex_unlock (&this->osd_mutex); return ret; } /* set the font of osd object */ static int osd_set_font( osd_object_t *osd, const char *fontname, int size) { osd_renderer_t *this = osd->renderer; osd_font_t *font; int best = 0; int ret = 0; #ifdef HAVE_FT2 char pathname[1024]; int error_flag = 0; #endif #ifdef LOG_DEBUG printf("osd_set_font %p name=%s\n", osd, fontname); #endif pthread_mutex_lock (&this->osd_mutex); osd->font = NULL; font = this->fonts; while( font ) { if( !strcmp(font->name, fontname) && (size>=font->size) && (bestsize)) { ret = 1; osd->font = font; best = font->size; #ifdef LOG_DEBUG printf ("osd_set_font: font->name=%s, size=%d\n", font->name, font->size); #endif } font = font->next; } #ifdef HAVE_FT2 if (osd->ft2) { osd->ft2->useme = 0; } if (!ret) { /* trying to load a font file with ft2 */ if (!osd->ft2) { osd->ft2 = xine_xmalloc(sizeof(osd_ft2context_t)); if(FT_Init_FreeType( &osd->ft2->library )) { printf("osd: cannot initialize ft2 library\n"); free(osd->ft2); osd->ft2 = NULL; } } if (osd->ft2) { /* try load font from current directory */ if (FT_New_Face(osd->ft2->library, fontname, 0, &osd->ft2->face)) { /* try load font from home directory */ sprintf(pathname, "%s/.xine/fonts/%s", xine_get_homedir(), fontname); if (FT_New_Face(osd->ft2->library, pathname, 0, &osd->ft2->face)) { /* try load font from xine font directory */ sprintf(pathname, "%s/%s", XINE_FONTDIR, fontname); if (FT_New_Face(osd->ft2->library, pathname, 0, &osd->ft2->face)) { error_flag = 1; printf("osd: error loading font %s with ft2\n", fontname); } } } if (!error_flag) { if (FT_Set_Pixel_Sizes(osd->ft2->face, 0, size)) { printf("osd: error setting font size (no scalable font?)\n"); } else { ret = 1; osd->ft2->useme = 1; osd->ft2->size = size; } } } } #endif pthread_mutex_unlock (&this->osd_mutex); return ret; } /* * search the character in the sorted array, * * returns ALIAS_CHARACTER_FONT if character 'code' isn't found, * returns 'n' on error */ static int osd_search(osd_fontchar_t *array, size_t n, uint16_t code) { #ifdef BINARY_SEARCH size_t i, left, right; if (!n) return 0; left = 0; right = n - 1; while (left < right) { i = (left + right) >> 1; if (code <= array[i].code) right = i; else left = i + 1; } if (array[right].code == code) return right; else return ALIAS_CHARACTER_FONT < n ? ALIAS_CHARACTER_FONT : n; #else size_t i; for( i = 0; i < n; i++ ) { if( font->fontchar[i].code == unicode ) break; } if (i < n) return i; else return ALIAS_CHARACTER_FONT < n ? ALIAS_CHARACTER_FONT : n; #endif } #ifndef _MSC_VER /* * get next unicode value */ static uint16_t osd_iconv_getunicode(iconv_t *cd, const char *encoding, char **inbuf, size_t *inbytesleft) { uint16_t unicode; char *outbuf = (char*)&unicode; size_t outbytesleft = 2; size_t count; if (cd != (iconv_t)-1) { /* get unicode value from iconv */ count = iconv(cd, inbuf, inbytesleft, &outbuf, &outbytesleft); if (count == (size_t)-1 && errno != E2BIG) { /* unknown character or character wider than 16 bits, try skip one byte */ printf(_("osd: unknown sequence starting with byte 0x%02X" " in encoding \"%s\", skipping\n"), (*inbuf)[0] & 0xFF, encoding); if (*inbytesleft) { (*inbytesleft)--; (*inbuf)++; } return ALIAS_CHARACTER_CONV; } } else { /* direct mapping without iconv */ unicode = (*inbuf)[0]; (*inbuf)++; (*inbytesleft)--; } return unicode; } #endif /* * free iconv encoding */ static void osd_free_encoding(osd_object_t *osd) { #ifndef _MSC_VER if (osd->cd != (iconv_t)-1) { iconv_close(osd->cd); osd->cd = (iconv_t)-1; } if (osd->encoding) { free(osd->encoding); osd->encoding = NULL; } #endif } /* * set encoding of text * * NULL ... no conversion (iso-8859-1) * "" ... locale encoding */ static int osd_set_encoding (osd_object_t *osd, const char *encoding) { #ifndef _MSC_VER osd_free_encoding(osd); if (!encoding) return 1; if (!encoding[0]) { #ifdef HAVE_LANGINFO_CODESET if ((encoding = nl_langinfo(CODESET)) == NULL) { printf(_("osd: can't find out current locale character set\n")); return 0; } #else return 0; #endif } /* prepare conversion to UCS-2 */ if ((osd->cd = iconv_open("UCS-2", encoding)) == (iconv_t)-1) { printf(_("osd: unsupported conversion %s -> UCS-2, " "no conversion performed\n"), encoding); return 0; } osd->encoding = strdup(encoding); return 1; #else return encoding == NULL; #endif /* _MSC_VER */ } /* * render text in current encoding on x,y position * no \n yet */ static int osd_render_text (osd_object_t *osd, int x1, int y1, const char *text, int color_base) { osd_renderer_t *this = osd->renderer; osd_font_t *font; int i, y; uint8_t *dst, *src; const char *inbuf; uint16_t unicode; size_t inbytesleft; #ifdef LOG_DEBUG printf("osd_render_text %p (%d,%d) \"%s\"\n", osd, x1, y1, text); #endif /* some sanity checks for the color indices */ if( color_base < 0 ) color_base = 0; else if( color_base > OVL_PALETTE_SIZE - TEXT_PALETTE_SIZE ) color_base = OVL_PALETTE_SIZE - TEXT_PALETTE_SIZE; pthread_mutex_lock (&this->osd_mutex); { int proceed = 0; if ((font = osd->font)) proceed = 1; #ifdef HAVE_FT2 if (osd->ft2 && osd->ft2->useme) proceed = 1; #endif if (proceed == 0) { printf(_("osd: font isn't defined\n")); pthread_mutex_unlock(&this->osd_mutex); return 0; } } if( x1 < osd->x1 ) osd->x1 = x1; if( y1 < osd->y1 ) osd->y1 = y1; inbuf = text; inbytesleft = strlen(text); while( inbytesleft ) { #ifndef _MSC_VER unicode = osd_iconv_getunicode(osd->cd, osd->encoding, (char **)&inbuf, &inbytesleft); #else unicode = inbuf[0]; inbuf++; inbytesleft--; #endif #ifdef HAVE_FT2 if (osd->ft2 && osd->ft2->useme) { i = FT_Get_Char_Index( osd->ft2->face, unicode ); } else { #endif i = osd_search(font->fontchar, font->num_fontchars, unicode); #ifdef LOG_DEBUG printf("font %s [%d, U+%04X == U+%04X] %dx%d -> %d,%d\n", font->name, i, unicode, font->fontchar[i].code, font->fontchar[i].width, font->fontchar[i].height, x1, y1); #endif #ifdef HAVE_FT2 } /* !(osd->ft2 && osd->ft2->useme) */ #endif #ifdef HAVE_FT2 if (osd->ft2 && osd->ft2->useme) { int gheight, gwidth; FT_GlyphSlot slot = osd->ft2->face->glyph; if (FT_Load_Glyph(osd->ft2->face, i, FT_LOAD_DEFAULT)) { printf("osd: error loading glyph\n"); continue; } if (slot->format != ft_glyph_format_bitmap) { if (FT_Render_Glyph(osd->ft2->face->glyph, ft_render_mode_normal)) printf("osd: error in rendering glyph\n"); } dst = osd->area + y1 * osd->width + x1; src = (uint8_t*) slot->bitmap.buffer; gheight = slot->bitmap.rows; gwidth = slot->bitmap.width; // printf("slot->bitmap.rows=%d, gheight=%d\n", slot->bitmap.rows, gheight); for( y = 0; y < gheight; y++ ) { uint8_t *s = src; uint8_t *d = dst - (slot->bitmap_top+10) * osd->width + slot->bitmap_left; #if 0 uint8_t *d = dst - slot->bitmap.rows * osd->width + slot->bitmap_left; #endif //printf("osd->height=%d\n",osd->height); while (s < src + gwidth) { if(d <= (osd->area + (osd->width * osd->height))) { *d = (uint8_t)(*s/26+1) + (uint8_t) color_base; } else { //printf("not output\n"); } d++; s++; } src += slot->bitmap.pitch; dst += osd->width; } x1 += slot->advance.x >> 6; if( x1 > osd->x2 ) osd->x2 = x1; if( y1 > osd->y2 ) osd->y2 = y1; } else { #endif if ( i != font->num_fontchars ) { dst = osd->area + y1 * osd->width + x1; src = font->fontchar[i].bmp; for( y = 0; y < font->fontchar[i].height; y++ ) { int width = font->fontchar[i].width; uint8_t *s = src, *d = dst; while (s < src + width) { if(d <= (osd->area + (osd->width * osd->height))) *d = *s + (uint8_t) color_base; d++; s++; } src += font->fontchar[i].width; dst += osd->width; } x1 += font->fontchar[i].width; if( x1 > osd->x2 ) osd->x2 = x1; if( y1 + font->fontchar[i].height > osd->y2 ) osd->y2 = y1 + font->fontchar[i].height; } #ifdef HAVE_FT2 } /* !(osd->ft2 && osd->ft2->useme) */ #endif } pthread_mutex_unlock (&this->osd_mutex); return 1; } /* get width and height of how text will be renderized */ static int osd_get_text_size(osd_object_t *osd, const char *text, int *width, int *height) { osd_renderer_t *this = osd->renderer; osd_font_t *font; int i; const char *inbuf; uint16_t unicode; size_t inbytesleft; #ifdef LOG_DEBUG printf("osd_get_text_size %p \"%s\"\n", osd, text); #endif pthread_mutex_lock (&this->osd_mutex); { int proceed = 0; if ((font = osd->font)) proceed = 1; #ifdef HAVE_FT2 if (osd->ft2 && osd->ft2->useme) proceed = 1; #endif if (proceed == 0) { printf(_("osd: font isn't defined\n")); pthread_mutex_unlock(&this->osd_mutex); return 0; } } *width = 0; *height = 0; inbuf = text; inbytesleft = strlen(text); while( inbytesleft ) { #ifndef _MSC_VER unicode = osd_iconv_getunicode(osd->cd, osd->encoding, (char **)&inbuf, &inbytesleft); #else unicode = inbuf[0]; inbuf++; inbytesleft--; #endif #ifdef HAVE_FT2 if (osd->ft2 && osd->ft2->useme) { int first = 1; FT_GlyphSlot slot = osd->ft2->face->glyph; i = FT_Get_Char_Index( osd->ft2->face, unicode); if (FT_Load_Glyph(osd->ft2->face, i, FT_LOAD_DEFAULT)) { printf("osd: error loading glyph %i\n", i); text++; continue; } if (slot->format != ft_glyph_format_bitmap) { if (FT_Render_Glyph(osd->ft2->face->glyph, ft_render_mode_normal)) printf("osd: error in rendering\n"); } if (first) *width += slot->bitmap_left; first = 0; *width += slot->advance.x >> 6; /* font height from baseline to top */ *height = MAX(*height, (slot->bitmap.rows)*3/2); //printf("slot->bitmap_top=%d *height=%d\n", slot->bitmap_top, *height); text++; } else { #endif i = osd_search(font->fontchar, font->num_fontchars, unicode); if ( i != font->num_fontchars ) { if( font->fontchar[i].height > *height ) *height = font->fontchar[i].height; *width += font->fontchar[i].width; } #ifdef HAVE_FT2 } /* !(osd->ft2 && osd->ft2->useme) */ #endif } pthread_mutex_unlock (&this->osd_mutex); return 1; } static void osd_load_fonts (osd_renderer_t *this, char *path) { DIR *dir; char pathname [1024]; #ifdef LOG_DEBUG printf ("osd: load_fonts, path=%s\n", path); #endif dir = opendir (path) ; if (dir) { struct dirent *entry; #ifdef LOG_DEBUG printf ("osd: load_fonts, %s opened\n", path); #endif while ((entry = readdir (dir)) != NULL) { int len; len = strlen (entry->d_name); if ( (len>12) && !strncmp (&entry->d_name[len-12], ".xinefont.gz", 12)) { #ifdef LOG_DEBUG printf ("osd: trying to load font >%s< (ending >%s<)\n", entry->d_name,&entry->d_name[len-12]); #endif sprintf (pathname, "%s/%s", path, entry->d_name); osd_renderer_load_font (this, pathname); } } closedir (dir); } } /* * free osd object */ static void osd_free_object (osd_object_t *osd_to_close) { osd_renderer_t *this = osd_to_close->renderer; osd_object_t *osd, *last; if( osd_to_close->handle >= 0 ) { osd_hide(osd_to_close,0); this->event.object.handle = osd_to_close->handle; /* not really needed this, but good pratice to clean it up */ memset( this->event.object.overlay, 0, sizeof(this->event.object.overlay) ); this->event.event_type = OVERLAY_EVENT_FREE_HANDLE; this->event.vpts = 0; this->video_overlay->add_event(this->video_overlay,(void *)&this->event); osd_to_close->handle = -1; /* handle will be freed */ } pthread_mutex_lock (&this->osd_mutex); last = NULL; osd = this->osds; while( osd ) { if ( osd == osd_to_close ) { free( osd->area ); if( osd->ft2 ) free( osd->ft2 ); osd_free_encoding(osd); if( last ) last->next = osd->next; else this->osds = osd->next; free( osd ); break; } last = osd; osd = osd->next; } pthread_mutex_unlock (&this->osd_mutex); } static void osd_renderer_close (osd_renderer_t *this) { while( this->osds ) osd_free_object ( this->osds ); while( this->fonts ) osd_renderer_unload_font( this, this->fonts->name ); pthread_mutex_destroy (&this->osd_mutex); free(this->event.object.overlay); free(this); } static void update_text_palette(void *this_gen, xine_cfg_entry_t *entry) { osd_renderer_t *this = (osd_renderer_t *)this_gen; this->textpalette = entry->num_value; printf("osd: text palette will be %s\n", textpalettes_str[this->textpalette] ); } static void osd_draw_bitmap(osd_object_t *osd, uint8_t *bitmap, int x1, int y1, int width, int height, uint8_t *palette_map) { int y, x; #ifdef LOG_DEBUG printf("osd_draw_bitmap %p at (%d,%d) %dx%d\n",osd, x1,y1, width,height ); #endif /* update clipping area */ osd->x1 = MIN( osd->x1, x1 ); osd->x2 = MAX( osd->x2, x1+width-1 ); osd->y1 = MIN( osd->y1, y1 ); osd->y2 = MAX( osd->y2, y1+height-1 ); for( y=0; ywidth + x1; /* Slow copy with palette translation, the map describes how to convert color indexes in the source bitmap to indexes in the osd palette */ for ( x=0; xarea[dst_offset+x] = palette_map[bitmap[src_offset+x]]; } } else { /* Fast copy with direct mapping */ memcpy(osd->area + (y1+y) * osd->width + x1, bitmap + y * width, width); } } } dvdspu_color_block_t *global_color_blocks = NULL; dvdspu_color_block_t *global_travel_cb = NULL; /* * initialize the osd rendering engine */ osd_renderer_t *osd_renderer_init( video_overlay_manager_t *video_overlay, config_values_t *config ) { osd_renderer_t *this; char str[1024]; this = xine_xmalloc(sizeof(osd_renderer_t)); this->video_overlay = video_overlay; this->config = config; this->event.object.overlay = xine_xmalloc( sizeof(vo_overlay_t) ); pthread_mutex_init (&this->osd_mutex, NULL); #ifdef LOG_DEBUG printf("osd: osd_renderer_init %p\n", this); #endif /* * load available fonts */ osd_load_fonts (this, XINE_FONTDIR); sprintf (str, "%s/.xine/fonts", xine_get_homedir ()); osd_load_fonts (this, str); this->textpalette = config->register_enum (config, "misc.osd_text_palette", 0, textpalettes_str, _("Palette (foreground-border-background) to use on subtitles"), NULL, 10, update_text_palette, this); /* * set up function pointer */ this->new_object = osd_new_object; this->free_object = osd_free_object; this->show = osd_show; this->hide = osd_hide; this->set_palette = osd_set_palette; this->set_text_palette = osd_set_text_palette; this->get_palette = osd_get_palette; this->set_position = osd_set_position; this->set_font = osd_set_font; this->clear = osd_clear; this->point = osd_point; this->line = osd_line; this->filled_rect = osd_filled_rect; this->set_encoding = osd_set_encoding; this->render_text = osd_render_text; this->get_text_size = osd_get_text_size; this->close = osd_renderer_close; this->draw_bitmap = osd_draw_bitmap; return this; }