From c9026cb3210205b07e2e068467a18ee40f9259a3 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Wed, 26 Jan 2011 19:40:52 +0200 Subject: sub/OSD: move some related files to sub/ --- sub/ass_mp.c | 355 ++++++++ sub/ass_mp.h | 82 ++ sub/av_sub.c | 119 +++ sub/av_sub.h | 30 + sub/find_sub.c | 175 ++++ sub/font_load.c | 356 ++++++++ sub/font_load.h | 115 +++ sub/font_load_ft.c | 1176 ++++++++++++++++++++++++ sub/osd_font.h | 545 ++++++++++++ sub/sd_ass.c | 2 +- sub/spudec.c | 1390 +++++++++++++++++++++++++++++ sub/spudec.h | 45 + sub/sub.c | 1348 ++++++++++++++++++++++++++++ sub/sub.h | 167 ++++ sub/sub_cc.c | 347 ++++++++ sub/sub_cc.h | 29 + sub/subreader.c | 2504 ++++++++++++++++++++++++++++++++++++++++++++++++++++ sub/subreader.h | 114 +++ sub/unrar_exec.c | 235 +++++ sub/unrar_exec.h | 54 ++ sub/vobsub.c | 1443 ++++++++++++++++++++++++++++++ sub/vobsub.h | 47 + 22 files changed, 10677 insertions(+), 1 deletion(-) create mode 100644 sub/ass_mp.c create mode 100644 sub/ass_mp.h create mode 100644 sub/av_sub.c create mode 100644 sub/av_sub.h create mode 100644 sub/find_sub.c create mode 100644 sub/font_load.c create mode 100644 sub/font_load.h create mode 100644 sub/font_load_ft.c create mode 100644 sub/osd_font.h create mode 100644 sub/spudec.c create mode 100644 sub/spudec.h create mode 100644 sub/sub.c create mode 100644 sub/sub.h create mode 100644 sub/sub_cc.c create mode 100644 sub/sub_cc.h create mode 100644 sub/subreader.c create mode 100644 sub/subreader.h create mode 100644 sub/unrar_exec.c create mode 100644 sub/unrar_exec.h create mode 100644 sub/vobsub.c create mode 100644 sub/vobsub.h (limited to 'sub') diff --git a/sub/ass_mp.c b/sub/ass_mp.c new file mode 100644 index 0000000000..98602ace03 --- /dev/null +++ b/sub/ass_mp.c @@ -0,0 +1,355 @@ +/* + * Copyright (C) 2006 Evgeniy Stepanov + * + * This file is part of MPlayer. + * + * MPlayer 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. + * + * MPlayer 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 libass; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "config.h" +#include "mp_msg.h" +#include "path.h" +#include "ass_mp.h" +#include "subreader.h" +#include "stream/stream.h" + +#ifdef CONFIG_FONTCONFIG +#include +#endif + +// libass-related command line options +ASS_Library *ass_library; +float ass_font_scale = 1.; +float ass_line_spacing = 0.; +int ass_top_margin = 0; +int ass_bottom_margin = 0; +int use_embedded_fonts = 1; +char **ass_force_style_list = NULL; +int ass_use_margins = 0; +char *ass_color = NULL; +char *ass_border_color = NULL; +char *ass_styles_file = NULL; +int ass_hinting = ASS_HINTING_LIGHT + 4; // light hinting for unscaled osd + +#ifdef CONFIG_FONTCONFIG +extern int font_fontconfig; +#else +static int font_fontconfig = -1; +#endif +extern char *font_name; +extern char *sub_font_name; +extern float text_font_scale_factor; +extern int subtitle_autoscale; + +#ifdef CONFIG_ICONV +extern char *sub_cp; +#else +static char *sub_cp = 0; +#endif + +ASS_Track *mp_ass_default_track(ASS_Library *library) +{ + ASS_Track *track = ass_new_track(library); + + track->track_type = TRACK_TYPE_ASS; + track->Timer = 100.; + track->PlayResY = 288; + track->WrapStyle = 0; + + if (ass_styles_file) + ass_read_styles(track, ass_styles_file, sub_cp); + + if (track->n_styles == 0) { + track->Kerning = true; + int sid = ass_alloc_style(track); + ASS_Style *style = track->styles + sid; + style->Name = strdup("Default"); + style->FontName = (font_fontconfig >= 0 + && sub_font_name) ? strdup(sub_font_name) + : (font_fontconfig >= 0 + && font_name) ? strdup(font_name) : strdup("Sans"); + style->treat_fontname_as_pattern = 1; + + double fs = track->PlayResY * text_font_scale_factor / 100.; + /* The font size is always proportional to video height only; + * real -subfont-autoscale behavior is not implemented. + * Apply a correction that corresponds to about 4:3 aspect ratio + * video to get a size somewhat closer to what non-libass rendering + * would produce with the same text_font_scale_factor + * and subtitle_autoscale. + */ + if (subtitle_autoscale == 2) + fs *= 1.3; + else if (subtitle_autoscale == 3) + fs *= 1.7; + + uint32_t c1 = 0xFFFFFF00; + uint32_t c2 = 0x00000000; + if (ass_color) + c1 = strtoll(ass_color, NULL, 16); + if (ass_border_color) + c2 = strtoll(ass_border_color, NULL, 16); + + style->FontSize = fs; + style->PrimaryColour = c1; + style->SecondaryColour = c1; + style->OutlineColour = c2; + style->BackColour = 0x00000000; + style->BorderStyle = 1; + style->Alignment = 2; + style->Outline = fs / 16; + style->MarginL = 10; + style->MarginR = 10; + style->MarginV = 5; + style->ScaleX = 1.; + style->ScaleY = 1.; + } + + ass_process_force_style(track); + return track; +} + +static int check_duplicate_plaintext_event(ASS_Track *track) +{ + int i; + ASS_Event *evt = track->events + track->n_events - 1; + + for (i = 0; i < track->n_events - 1; ++i) // ignoring last event, it is the one we are comparing with + if (track->events[i].Start == evt->Start && + track->events[i].Duration == evt->Duration && + strcmp(track->events[i].Text, evt->Text) == 0) + return 1; + return 0; +} + +/** + * \brief Convert subtitle to ASS_Events for the given track + * \param track track + * \param sub subtitle to convert + * \return event id + * note: assumes that subtitle is _not_ fps-based; caller must manually correct + * Start and Duration in other case. + **/ +static int ass_process_subtitle(ASS_Track *track, subtitle *sub) +{ + int eid; + ASS_Event *event; + int len = 0, j; + char *p; + char *end; + + eid = ass_alloc_event(track); + event = track->events + eid; + + event->Start = sub->start * 10; + event->Duration = (sub->end - sub->start) * 10; + event->Style = 0; + + for (j = 0; j < sub->lines; ++j) + len += sub->text[j] ? strlen(sub->text[j]) : 0; + + len += 2 * sub->lines; // '\N', including the one after the last line + len += 6; // {\anX} + len += 1; // '\0' + + event->Text = malloc(len); + end = event->Text + len; + p = event->Text; + + if (sub->alignment) + p += snprintf(p, end - p, "{\\an%d}", sub->alignment); + + for (j = 0; j < sub->lines; ++j) + p += snprintf(p, end - p, "%s\\N", sub->text[j]); + + if (sub->lines > 0) + p -= 2; // remove last "\N" + *p = 0; + + if (check_duplicate_plaintext_event(track)) { + ass_free_event(track, eid); + track->n_events--; + return -1; + } + + mp_msg(MSGT_ASS, MSGL_V, + "plaintext event at %" PRId64 ", +%" PRId64 ": %s \n", + (int64_t) event->Start, (int64_t) event->Duration, event->Text); + + return eid; +} + + +/** + * \brief Convert subdata to ASS_Track + * \param subdata subtitles struct from subreader + * \param fps video framerate + * \return newly allocated ASS_Track, filled with subtitles from subdata + */ +ASS_Track *mp_ass_read_subdata(ASS_Library *library, sub_data *subdata, + double fps) +{ + ASS_Track *track; + int i; + + track = mp_ass_default_track(library); + track->name = subdata->filename ? strdup(subdata->filename) : 0; + + for (i = 0; i < subdata->sub_num; ++i) { + int eid = ass_process_subtitle(track, subdata->subtitles + i); + if (eid < 0) + continue; + if (!subdata->sub_uses_time) { + track->events[eid].Start *= 100. / fps; + track->events[eid].Duration *= 100. / fps; + } + } + return track; +} + +ASS_Track *mp_ass_read_stream(ASS_Library *library, const char *fname, + char *charset) +{ + int i; + char *buf = NULL; + ASS_Track *track; + size_t sz = 0; + size_t buf_alloc = 0; + stream_t *fd; + + fd = open_stream(fname, NULL, NULL); + if (!fd) + // Stream code should have printed an error already + return NULL; + if (fd->end_pos > STREAM_BUFFER_SIZE) + /* read entire file if size is known */ + buf_alloc = fd->end_pos; + else + buf_alloc = 1000; + for (;;) { + if (sz > 100000000) { + mp_tmsg(MSGT_ASS, MSGL_ERR, "Refusing to load subtitle file " + "larger than 100 MB: %s\n", fname); + sz = 0; + break; + } + buf_alloc = FFMAX(buf_alloc, sz + (sz >> 1)); + buf_alloc = FFMIN(buf_alloc, 100000001); + buf = realloc(buf, buf_alloc + 1); + i = stream_read(fd, buf + sz, buf_alloc - sz); + if (i <= 0) + break; + sz += i; + } + free_stream(fd); + if (!sz) { + free(buf); + return NULL; + } + buf[sz] = 0; + buf = realloc(buf, sz + 1); + track = ass_read_memory(library, buf, sz, charset); + if (track) { + free(track->name); + track->name = strdup(fname); + } + free(buf); + return track; +} + +void mp_ass_configure(ASS_Renderer *priv, int w, int h, bool unscaled) +{ + int hinting; + ass_set_frame_size(priv, w, h); + ass_set_margins(priv, ass_top_margin, ass_bottom_margin, 0, 0); + ass_set_use_margins(priv, ass_use_margins); + ass_set_font_scale(priv, ass_font_scale); + if (!unscaled && (ass_hinting & 4)) + hinting = 0; + else + hinting = ass_hinting & 3; + ass_set_hinting(priv, hinting); + ass_set_line_spacing(priv, ass_line_spacing); +} + +void mp_ass_configure_fonts(ASS_Renderer *priv) +{ + char *dir, *path, *family; + dir = get_path("fonts"); + if (font_fontconfig < 0 && sub_font_name) + path = strdup(sub_font_name); + else if (font_fontconfig < 0 && font_name) + path = strdup(font_name); + else + path = get_path("subfont.ttf"); + if (font_fontconfig >= 0 && sub_font_name) + family = strdup(sub_font_name); + else if (font_fontconfig >= 0 && font_name) + family = strdup(font_name); + else + family = 0; + + ass_set_fonts(priv, path, family, font_fontconfig + 1, NULL, 1); + + free(dir); + free(path); + free(family); +} + +static void message_callback(int level, const char *format, va_list va, void *ctx) +{ + mp_msg(MSGT_ASS, level, "[ass] "); + mp_msg_va(MSGT_ASS, level, format, va); + // libass messages lack trailing \n + mp_msg(MSGT_ASS, level, "\n"); +} + +ASS_Library *mp_ass_init(void) +{ + ASS_Library *priv; + char *path = get_path("fonts"); + priv = ass_library_init(); + ass_set_message_cb(priv, message_callback, NULL); + ass_set_fonts_dir(priv, path); + ass_set_extract_fonts(priv, use_embedded_fonts); + ass_set_style_overrides(priv, ass_force_style_list); + free(path); + return priv; +} + +int ass_force_reload = 0; // flag set if global ass-related settings were changed + +ASS_Image *mp_ass_render_frame(ASS_Renderer *priv, ASS_Track *track, + long long now, int *detect_change) +{ + if (ass_force_reload) { + ass_set_margins(priv, ass_top_margin, ass_bottom_margin, 0, 0); + ass_set_use_margins(priv, ass_use_margins); + ass_set_font_scale(priv, ass_font_scale); + ass_force_reload = 0; + } + return ass_render_frame(priv, track, now, detect_change); +} diff --git a/sub/ass_mp.h b/sub/ass_mp.h new file mode 100644 index 0000000000..965b063403 --- /dev/null +++ b/sub/ass_mp.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2006 Evgeniy Stepanov + * + * This file is part of MPlayer. + * + * MPlayer 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. + * + * MPlayer 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 libass; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_ASS_MP_H +#define MPLAYER_ASS_MP_H + +#include +#include + +#include "config.h" +#include "subreader.h" + +#ifdef CONFIG_ASS +#include +#include + +extern ASS_Library *ass_library; +extern float ass_font_scale; +extern float ass_line_spacing; +extern int ass_top_margin; +extern int ass_bottom_margin; +extern int use_embedded_fonts; +extern char **ass_force_style_list; +extern int ass_use_margins; +extern char *ass_color; +extern char *ass_border_color; +extern char *ass_styles_file; +extern int ass_hinting; + +ASS_Track *mp_ass_default_track(ASS_Library *library); +ASS_Track *mp_ass_read_subdata(ASS_Library *library, sub_data *subdata, + double fps); +ASS_Track *mp_ass_read_stream(ASS_Library *library, const char *fname, + char *charset); + +void mp_ass_configure(ASS_Renderer *priv, int w, int h, bool unscaled); +void mp_ass_configure_fonts(ASS_Renderer *priv); +ASS_Library *mp_ass_init(void); + +extern int ass_force_reload; +ASS_Image *mp_ass_render_frame(ASS_Renderer *priv, ASS_Track *track, + long long now, int *detect_change); + +#else /* CONFIG_ASS */ + +/* Needed for EOSD code using this type to compile */ + +typedef struct ass_image { + int w, h; + int stride; + unsigned char *bitmap; + uint32_t color; + int dst_x, dst_y; + struct ass_image *next; +} ASS_Image; + +#endif + +typedef struct { + ASS_Image *imgs; + int changed; +} mp_eosd_images_t; + + +#endif /* MPLAYER_ASS_MP_H */ diff --git a/sub/av_sub.c b/sub/av_sub.c new file mode 100644 index 0000000000..a68fbce083 --- /dev/null +++ b/sub/av_sub.c @@ -0,0 +1,119 @@ +/* + * This file is part of MPlayer. + * + * MPlayer 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. + * + * MPlayer 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 MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include + +#include "libmpdemux/stheader.h" +#include "sub.h" +#include "spudec.h" +#include "av_sub.h" + +void reset_avsub(struct sh_sub *sh) +{ + if (sh->context) { + avcodec_close(sh->context); + av_freep(&sh->context); + } +} + +/** + * Decode a subtitle packet via libavcodec. + * \return < 0 on error, > 0 if further processing is needed + */ +int decode_avsub(struct sh_sub *sh, uint8_t *data, int size, + double pts, double duration) +{ + AVCodecContext *ctx = sh->context; + enum CodecID cid = CODEC_ID_NONE; + int res; + int got_sub; + AVSubtitle sub; + AVPacket pkt; + + switch (sh->type) { + case 'b': + cid = CODEC_ID_DVB_SUBTITLE; break; + case 'p': + cid = CODEC_ID_HDMV_PGS_SUBTITLE; break; + case 'x': + cid = CODEC_ID_XSUB; break; + } + + av_init_packet(&pkt); + pkt.data = data; + pkt.size = size; + pkt.pts = pts * 1000; + if (duration >= 0) + pkt.convergence_duration = duration * 1000; + if (!ctx) { + AVCodec *sub_codec; + avcodec_init(); + avcodec_register_all(); + ctx = avcodec_alloc_context(); + sub_codec = avcodec_find_decoder(cid); + if (!ctx || !sub_codec || avcodec_open(ctx, sub_codec) < 0) { + mp_msg(MSGT_SUBREADER, MSGL_FATAL, + "Could not open subtitle decoder\n"); + av_freep(&ctx); + return -1; + } + sh->context = ctx; + } + res = avcodec_decode_subtitle2(ctx, &sub, &got_sub, &pkt); + if (res < 0) + return res; + if (pts != MP_NOPTS_VALUE) { + if (sub.end_display_time > sub.start_display_time) + duration = (sub.end_display_time - sub.start_display_time) / 1000.0; + pts += sub.start_display_time / 1000.0; + } + double endpts = MP_NOPTS_VALUE; + if (pts != MP_NOPTS_VALUE && duration >= 0) + endpts = pts + duration; + if (got_sub && vo_spudec && sub.num_rects == 0) + spudec_set_paletted(vo_spudec, NULL, 0, NULL, 0, 0, 0, 0, pts, endpts); + if (got_sub && sub.num_rects > 0) { + switch (sub.rects[0]->type) { + case SUBTITLE_BITMAP: + if (!vo_spudec) + vo_spudec = spudec_new_scaled(NULL, ctx->width, ctx->height, NULL, 0); + spudec_set_paletted(vo_spudec, + sub.rects[0]->pict.data[0], + sub.rects[0]->pict.linesize[0], + sub.rects[0]->pict.data[1], + sub.rects[0]->x, + sub.rects[0]->y, + sub.rects[0]->w, + sub.rects[0]->h, + pts, + endpts); + vo_osd_changed(OSDTYPE_SPU); + break; + default: + mp_msg(MSGT_SUBREADER, MSGL_ERR, "sd_avsub: unsupported subtitle " + "type from libavcodec\n"); + res = -1; + break; + } + } +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 82, 0) + if (got_sub) + avsubtitle_free(&sub); +#endif + return res; +} diff --git a/sub/av_sub.h b/sub/av_sub.h new file mode 100644 index 0000000000..af3edc4d34 --- /dev/null +++ b/sub/av_sub.h @@ -0,0 +1,30 @@ +/* + * This file is part of MPlayer. + * + * MPlayer 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. + * + * MPlayer 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 MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_AV_SUB_H +#define MPLAYER_AV_SUB_H + +#include + +struct sh_sub; + +void reset_avsub(struct sh_sub *sh); +int decode_avsub(struct sh_sub *sh, uint8_t *data, int size, + double pts, double endpts); + +#endif /* MPLAYER_AV_SUB_H */ diff --git a/sub/find_sub.c b/sub/find_sub.c new file mode 100644 index 0000000000..97c232b1db --- /dev/null +++ b/sub/find_sub.c @@ -0,0 +1,175 @@ +/* + * .SUB + * + * This file is part of MPlayer. + * + * MPlayer 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. + * + * MPlayer 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 MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include + +#include "libvo/video_out.h" +#include "sub.h" +#include "subreader.h" + +#include "mp_msg.h" +#include "mpcommon.h" +#include "mplayer.h" + +static int current_sub=0; + +//static subtitle* subtitles=NULL; +static int nosub_range_start=-1; +static int nosub_range_end=-1; +static const sub_data *last_sub_data = NULL; + +void step_sub(sub_data *subd, float pts, int movement) { + subtitle *subs; + int key; + + if (subd == NULL) return; + subs = subd->subtitles; + key = (pts+sub_delay) * (subd->sub_uses_time ? 100 : sub_fps); + + /* Tell the OSD subsystem that the OSD contents will change soon */ + vo_osd_changed(OSDTYPE_SUBTITLE); + + /* If we are moving forward, don't count the next (current) subtitle + * if we haven't displayed it yet. Same when moving other direction. + */ + if (movement > 0 && key < subs[current_sub].start) + movement--; + if (movement < 0 && key >= subs[current_sub].end) + movement++; + + /* Never move beyond first or last subtitle. */ + if (current_sub+movement < 0) + movement = 0-current_sub; + if (current_sub+movement >= subd->sub_num) + movement = subd->sub_num - current_sub - 1; + + current_sub += movement; + sub_delay = subs[current_sub].start / (subd->sub_uses_time ? 100 : sub_fps) - pts; +} + +void find_sub(struct MPContext *mpctx, sub_data* subd,int key){ + subtitle *subs; + subtitle *new_sub = NULL; + int i,j; + + if ( !subd || subd->sub_num == 0) return; + subs = subd->subtitles; + + if (last_sub_data != subd) { + // Sub data changed, reset nosub range. + last_sub_data = subd; + nosub_range_start = -1; + nosub_range_end = -1; + } + + if(vo_sub){ + if(key>=vo_sub->start && key<=vo_sub->end) return; // OK! + } else { + if(key>nosub_range_start && key=0 && current_sub+1 < subd->sub_num){ + if(key>subs[current_sub].end && key=new_sub->start && key<=new_sub->end) goto update; // OK! + } + +// printf("\r---- sub log search... ----\n"); + + // use logarithmic search: + i=0; + j = subd->sub_num - 1; +// printf("Searching %d in %d..%d\n",key,subs[i].start,subs[j].end); + while(j>=i){ + current_sub=(i+j+1)/2; + new_sub=&subs[current_sub]; + if(keystart) j=current_sub-1; + else if(key>new_sub->end) i=current_sub+1; + else goto update; // found! + } +// if(key>=new_sub->start && key<=new_sub->end) return; // OK! + + // check where are we... + if(keystart){ + if(current_sub<=0){ + // before the first sub + nosub_range_start=key-1; // tricky + nosub_range_end=new_sub->start; +// printf("FIRST... key=%d end=%d \n",key,new_sub->start); + new_sub=NULL; + goto update; + } + --current_sub; + if(key>subs[current_sub].end && keyend) printf("JAJJ! "); else + if(current_sub+1 >= subd->sub_num){ + // at the end? + nosub_range_start=new_sub->end; + nosub_range_end=0x7FFFFFFF; // MAXINT +// printf("END!?\n"); + new_sub=NULL; + goto update; + } else + if(key>subs[current_sub].end && keystart,(int)new_sub->end,current_sub); + + new_sub=NULL; // no sub here +update: + set_osd_subtitle(mpctx, new_sub); +} diff --git a/sub/font_load.c b/sub/font_load.c new file mode 100644 index 0000000000..e9980b8e41 --- /dev/null +++ b/sub/font_load.c @@ -0,0 +1,356 @@ +/* + * This file is part of MPlayer. + * + * MPlayer 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. + * + * MPlayer 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 MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "font_load.h" +#include "mp_msg.h" + +raw_file* load_raw(char *name,int verbose){ + int bpp; + raw_file* raw=malloc(sizeof(raw_file)); + unsigned char head[32]; + FILE *f=fopen(name,"rb"); + if(!f) goto err_out; // can't open + if(fread(head,32,1,f)<1) goto err_out; // too small + if(memcmp(head,"mhwanh",6)) goto err_out; // not raw file + raw->w=head[8]*256+head[9]; + raw->h=head[10]*256+head[11]; + raw->c=head[12]*256+head[13]; + if(raw->w == 0) // 2 bytes were not enough for the width... read 4 bytes from the end of the header + raw->w = ((head[28]*0x100 + head[29])*0x100 + head[30])*0x100 + head[31]; + if(raw->c>256) goto err_out; // too many colors!? + mp_msg(MSGT_OSD, MSGL_DBG2, "RAW: %s %d x %d, %d colors\n",name,raw->w,raw->h,raw->c); + if(raw->c){ + raw->pal=malloc(raw->c*3); + fread(raw->pal,3,raw->c,f); + bpp=1; + } else { + raw->pal=NULL; + bpp=3; + } + raw->bmp=malloc(raw->h*raw->w*bpp); + fread(raw->bmp,raw->h*raw->w*bpp,1,f); + fclose(f); + return raw; + +err_out: + if (f) + fclose(f); + free(raw); + return NULL; +} + +extern int sub_unicode; + +font_desc_t* read_font_desc(const char* fname,float factor,int verbose){ +unsigned char sor[1024]; +unsigned char sor2[1024]; +font_desc_t *desc; +FILE *f = NULL; +char *dn; +//struct stat fstate; +char section[64]; +int i,j; +int chardb=0; +int fontdb=-1; +int version=0; +int first=1; + +desc=malloc(sizeof(font_desc_t));if(!desc) goto fail_out; +memset(desc,0,sizeof(font_desc_t)); + +f=fopen(fname,"rt");if(!f){ mp_msg(MSGT_OSD, MSGL_V, "font: can't open file: %s\n",fname); goto fail_out;} + +i = strlen (fname) - 9; +if ((dn = malloc(i+1))){ + strncpy (dn, fname, i); + dn[i]='\0'; +} + +desc->fpath = dn; // search in the same dir as fonts.desc + +// desc->fpath=get_path("font/"); +// if (stat(desc->fpath, &fstate)!=0) desc->fpath=DATADIR"/font"; + + + + +// set up some defaults, and erase table +desc->charspace=2; +desc->spacewidth=12; +desc->height=0; +for(i=0;i<65536;i++) desc->start[i]=desc->width[i]=desc->font[i]=-1; + +section[0]=0; + +while(fgets(sor,1020,f)){ + unsigned char* p[8]; + int pdb=0; + unsigned char *s=sor; + unsigned char *d=sor2; + int ec=' '; + int id=0; + sor[1020]=0; + + /* skip files that look like: TTF (0x00, 0x01), PFM (0x00, 0x01), PFB + * (0x80, 0x01), PCF (0x01, 0x66), fon ("MZ"), gzipped (0x1f, 0x8b) */ + + if (first) { + if (!sor[0] || sor[1] == 1 || (sor[0] == 'M' && sor[1] == 'Z') || (sor[0] == 0x1f && sor[1] == 0x8b) || (sor[0] == 1 && sor[1] == 0x66)) { + mp_msg(MSGT_OSD, MSGL_ERR, "%s doesn't look like a bitmap font description, ignoring.\n", fname); + goto fail_out; + } + first = 0; + } + + p[0]=d;++pdb; + while(1){ + int c=*s++; + if(c==0 || c==13 || c==10) break; + if(!id){ + if(c==39 || c==34){ id=c;continue;} // idezojel + if(c==';' || c=='#') break; + if(c==9) c=' '; + if(c==' '){ + if(ec==' ') continue; + *d=0; ++d; + p[pdb]=d;++pdb; + if(pdb>=8) break; + continue; + } + } else { + if(id==c){ id=0;continue;} // idezojel + + } + *d=c;d++; + ec=c; + } + if(d==sor2) continue; // skip empty lines + *d=0; + +// printf("params=%d sor=%s\n",pdb,sor); +// for(i=0;i=16){ mp_msg(MSGT_OSD, MSGL_ERR, "font: Too many bitmaps defined.\n");goto fail_out;} + } + continue; + } + } + + if(strcmp(section,"[fpath]")==0){ + if(pdb==1){ + free (desc->fpath); // release previously allocated memory + desc->fpath=strdup(p[0]); + continue; + } + } else + +#ifdef __AMIGAOS4__ +#define FONT_PATH_SEP "" +#else +//! path seperator for font paths, may not be more than one character +#define FONT_PATH_SEP "/" +#endif + + if(strcmp(section,"[files]")==0){ + char *default_dir=MPLAYER_DATADIR FONT_PATH_SEP "font"; + if(pdb==2 && strcmp(p[0],"alpha")==0){ + char *cp; + if (!(cp=malloc(strlen(desc->fpath)+strlen(p[1])+2))) goto fail_out; + + snprintf(cp,strlen(desc->fpath)+strlen(p[1])+2,"%s" FONT_PATH_SEP "%s", + desc->fpath,p[1]); + if(!((desc->pic_a[fontdb]=load_raw(cp,verbose)))){ + free(cp); + if (!(cp=malloc(strlen(default_dir)+strlen(p[1])+2))) + goto fail_out; + snprintf(cp,strlen(default_dir)+strlen(p[1])+2,"%s" FONT_PATH_SEP "%s", + default_dir,p[1]); + if (!((desc->pic_a[fontdb]=load_raw(cp,verbose)))){ + mp_msg(MSGT_OSD, MSGL_ERR, "Can't load font bitmap: %s\n",p[1]); + free(cp); + goto fail_out; + } + } + free(cp); + continue; + } + if(pdb==2 && strcmp(p[0],"bitmap")==0){ + char *cp; + if (!(cp=malloc(strlen(desc->fpath)+strlen(p[1])+2))) goto fail_out; + + snprintf(cp,strlen(desc->fpath)+strlen(p[1])+2,"%s" FONT_PATH_SEP "%s", + desc->fpath,p[1]); + if(!((desc->pic_b[fontdb]=load_raw(cp,verbose)))){ + free(cp); + if (!(cp=malloc(strlen(default_dir)+strlen(p[1])+2))) + goto fail_out; + snprintf(cp,strlen(default_dir)+strlen(p[1])+2,"%s" FONT_PATH_SEP "%s", + default_dir,p[1]); + if (!((desc->pic_b[fontdb]=load_raw(cp,verbose)))){ + mp_msg(MSGT_OSD, MSGL_ERR, "Can't load font bitmap: %s\n",p[1]); + free(cp); + goto fail_out; + } + } + free(cp); + continue; + } + } else + + if(strcmp(section,"[info]")==0){ + if(pdb==2 && strcmp(p[0],"name")==0){ + desc->name=strdup(p[1]); + continue; + } + if(pdb==2 && strcmp(p[0],"descversion")==0){ + version=atoi(p[1]); + continue; + } + if(pdb==2 && strcmp(p[0],"spacewidth")==0){ + desc->spacewidth=atoi(p[1]); + continue; + } + if(pdb==2 && strcmp(p[0],"charspace")==0){ + desc->charspace=atoi(p[1]); + continue; + } + if(pdb==2 && strcmp(p[0],"height")==0){ + desc->height=atoi(p[1]); + continue; + } + } else + + if(strcmp(section,"[characters]")==0){ + if(pdb==3){ + int chr=p[0][0]; + int start=atoi(p[1]); + int end=atoi(p[2]); + if(sub_unicode && (chr>=0x80)) chr=(chr<<8)+p[0][1]; + else if(strlen(p[0])!=1) chr=strtol(p[0],NULL,0); + if(endstart[chr]=start; + desc->width[chr]=end-start+1; + desc->font[chr]=fontdb; +// printf("char %d '%c' start=%d width=%d\n",chr,chr,desc->start[chr],desc->width[chr]); + ++chardb; + } + continue; + } + } + mp_msg(MSGT_OSD, MSGL_ERR, "Syntax error in font desc: %s",sor); + goto fail_out; + +} +fclose(f); +f = NULL; + + if (first == 1) { + mp_msg(MSGT_OSD, MSGL_ERR, "%s is empty or a directory, ignoring.\n", fname); + goto fail_out; + } + +//printf("font: pos of U = %d\n",desc->start[218]); + +for(i=0;i<=fontdb;i++){ + if(!desc->pic_a[i] || !desc->pic_b[i]){ + mp_msg(MSGT_OSD, MSGL_ERR, "font: Missing bitmap(s) for sub-font #%d\n",i); + goto fail_out; + } + //if(factor!=1.0f) + { + // re-sample alpha + int f=factor*256.0f; + int size=desc->pic_a[i]->w*desc->pic_a[i]->h; + int j; + mp_msg(MSGT_OSD, MSGL_DBG2, "font: resampling alpha by factor %5.3f (%d) ",factor,f);fflush(stdout); + for(j=0;jpic_a[i]->bmp[j]; // alpha + int y=desc->pic_b[i]->bmp[j]; // bitmap + +#ifdef FAST_OSD + x=(x<(255-f))?0:1; +#else + + x=255-((x*f)>>8); // scale + //if(x<0) x=0; else if(x>255) x=255; + //x^=255; // invert + + if(x+y>255) x=255-y; // to avoid overflows + + //x=0; + //x=((x*f*(255-y))>>16); + //x=((x*f*(255-y))>>16)+y; + //x=(x*f)>>8;if(x=252) x=0; +#endif + + desc->pic_a[i]->bmp[j]=x; +// desc->pic_b[i]->bmp[j]=0; // hack + } + mp_msg(MSGT_OSD, MSGL_DBG2, "DONE!\n"); + } + if(!desc->height) desc->height=desc->pic_a[i]->h; +} + +j='_';if(desc->font[j]<0) j='?'; +for(i=0;i<65536;i++) + if(desc->font[i]<0){ + desc->start[i]=desc->start[j]; + desc->width[i]=desc->width[j]; + desc->font[i]=desc->font[j]; + } +desc->font[' ']=-1; +desc->width[' ']=desc->spacewidth; + +mp_msg(MSGT_OSD, MSGL_V, "Bitmap font %s loaded successfully! (%d chars)\n",fname,chardb); + +return desc; + +fail_out: + if (f) + fclose(f); + free(desc->fpath); + free(desc->name); + free(desc); + return NULL; +} + +#ifndef CONFIG_FREETYPE +void render_one_glyph(font_desc_t *desc, int c) {} +int kerning(font_desc_t *desc, int prevc, int c) { return 0; } +#endif diff --git a/sub/font_load.h b/sub/font_load.h new file mode 100644 index 0000000000..7efe067aaf --- /dev/null +++ b/sub/font_load.h @@ -0,0 +1,115 @@ +/* + * This file is part of MPlayer. + * + * MPlayer 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. + * + * MPlayer 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 MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_FONT_LOAD_H +#define MPLAYER_FONT_LOAD_H + +#include "config.h" + +#ifdef CONFIG_FREETYPE +#include +#include FT_FREETYPE_H +#endif + +typedef struct { + unsigned char *bmp; + unsigned char *pal; + int w,h,c; +#ifdef CONFIG_FREETYPE + int charwidth,charheight,pen,baseline,padding; + int current_count, current_alloc; +#endif +} raw_file; + +typedef struct font_desc { +#ifdef CONFIG_FREETYPE + int dynamic; +#endif + char *name; + char *fpath; + int spacewidth; + int charspace; + int height; +// char *fname_a; +// char *fname_b; + raw_file* pic_a[16]; + raw_file* pic_b[16]; + short font[65536]; + int start[65536]; // short is not enough for unicode fonts + short width[65536]; + int freetype; + +#ifdef CONFIG_FREETYPE + int face_cnt; + + FT_Face faces[16]; + FT_UInt glyph_index[65536]; + + int max_width, max_height; + + struct + { + int g_r; + int o_r; + int g_w; + int o_w; + int o_size; + unsigned volume; + + unsigned *g; + unsigned *gt2; + unsigned *om; + unsigned char *omt; + unsigned short *tmp; + } tables; +#endif + +} font_desc_t; + +extern font_desc_t* vo_font; + +extern char *subtitle_font_encoding; +extern float text_font_scale_factor; +extern float osd_font_scale_factor; +extern float subtitle_font_radius; +extern float subtitle_font_thickness; +extern int subtitle_autoscale; + +extern int vo_image_width; +extern int vo_image_height; + +extern int force_load_font; + +int init_freetype(void); +int done_freetype(void); + +font_desc_t* read_font_desc_ft(const char* fname,int face_index,int movie_width, int movie_height, float font_scale_factor); +void free_font_desc(font_desc_t *desc); + +void render_one_glyph(font_desc_t *desc, int c); +int kerning(font_desc_t *desc, int prevc, int c); + +void load_font_ft(int width, int height, font_desc_t **desc, const char *name, float font_scale_factor); + +void blur(unsigned char *buffer, unsigned short *tmp2, int width, int height, + int stride, int *m2, int r, int mwidth); + +raw_file* load_raw(char *name,int verbose); +font_desc_t* read_font_desc(const char* fname,float factor,int verbose); + +#endif /* MPLAYER_FONT_LOAD_H */ diff --git a/sub/font_load_ft.c b/sub/font_load_ft.c new file mode 100644 index 0000000000..41a0f886cb --- /dev/null +++ b/sub/font_load_ft.c @@ -0,0 +1,1176 @@ +/* + * Renders antialiased fonts for mplayer using freetype library. + * Should work with TrueType, Type1 and any other font supported by libfreetype. + * + * Artur Zaprzala + * + * ported inside MPlayer by Jindrich Makovicka + * + * This file is part of MPlayer. + * + * MPlayer 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. + * + * MPlayer 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 MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include +#include +#include +#include + +#ifdef CONFIG_ICONV +#include +#endif + +#include +#include FT_FREETYPE_H +#include FT_GLYPH_H + +#ifdef CONFIG_FONTCONFIG +#include +#endif + +#include "libavutil/common.h" +#include "mpbswap.h" +#include "font_load.h" +#include "mp_msg.h" +#include "mplayer.h" +#include "path.h" +#include "osd_font.h" + +#if (FREETYPE_MAJOR > 2) || (FREETYPE_MAJOR == 2 && FREETYPE_MINOR >= 1) +#define HAVE_FREETYPE21 +#endif + +char *subtitle_font_encoding = NULL; +float text_font_scale_factor = 3.5; +float osd_font_scale_factor = 4.0; +float subtitle_font_radius = 2.0; +float subtitle_font_thickness = 2.0; +// 0 = no autoscale +// 1 = video height +// 2 = video width +// 3 = diagonal +int subtitle_autoscale = 3; + +int vo_image_width = 0; +int vo_image_height = 0; +int force_load_font; + +int using_freetype = 0; +#ifdef CONFIG_FONTCONFIG +int font_fontconfig = 1; +#else +int font_fontconfig = -1; +#endif + +//// constants +static unsigned int const colors = 256; +static unsigned int const maxcolor = 255; +static unsigned const base = 256; +static unsigned const first_char = 33; +#define MAX_CHARSET_SIZE 60000 + +static FT_Library library; + +#define OSD_CHARSET_SIZE 15 + +static const FT_ULong osd_charset[OSD_CHARSET_SIZE] = +{ + 0xe001, 0xe002, 0xe003, 0xe004, 0xe005, 0xe006, 0xe007, 0xe008, + 0xe009, 0xe00a, 0xe00b, 0xe010, 0xe011, 0xe012, 0xe013 +}; + +static const FT_ULong osd_charcodes[OSD_CHARSET_SIZE] = +{ + 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08, + 0x09,0x0a,0x0b,0x10,0x11,0x12,0x13 +}; + +#define f266ToInt(x) (((x)+32)>>6) // round fractional fixed point number to integer + // coordinates are in 26.6 pixels (i.e. 1/64th of pixels) +#define f266CeilToInt(x) (((x)+63)>>6) // ceiling +#define f266FloorToInt(x) ((x)>>6) // floor +#define f1616ToInt(x) (((x)+0x8000)>>16) // 16.16 +#define floatTof266(x) ((int)((x)*(1<<6)+0.5)) + +#define ALIGN(x) (((x)+7)&~7) // 8 byte align + +#define WARNING(msg, args...) mp_msg(MSGT_OSD, MSGL_WARN, msg "\n", ## args) + +#define DEBUG 0 + +//static double ttime; + + +static void paste_bitmap(unsigned char *bbuffer, FT_Bitmap *bitmap, int x, int y, int width, int height, int bwidth) { + int drow = x+y*width; + int srow = 0; + int sp, dp, w, h; + if (bitmap->pixel_mode==ft_pixel_mode_mono) + for (h = bitmap->rows; h>0 && height > 0; --h, height--, drow+=width, srow+=bitmap->pitch) + for (w = bwidth, sp=dp=0; w>0; --w, ++dp, ++sp) + bbuffer[drow+dp] = (bitmap->buffer[srow+sp/8] & (0x80>>(sp%8))) ? 255:0; + else + for (h = bitmap->rows; h>0 && height > 0; --h, height--, drow+=width, srow+=bitmap->pitch) + for (w = bwidth, sp=dp=0; w>0; --w, ++dp, ++sp) + bbuffer[drow+dp] = bitmap->buffer[srow+sp]; +} + + +static int check_font(font_desc_t *desc, float ppem, int padding, int pic_idx, + int charset_size, FT_ULong *charset, FT_ULong *charcodes, + int unicode) { + FT_Error error; + FT_Face face = desc->faces[pic_idx]; + int const load_flags = FT_LOAD_DEFAULT; + int ymin = INT_MAX, ymax = INT_MIN; + int space_advance = 20; + int width, height; + unsigned char *bbuffer; + int i, uni_charmap = 1; + + error = FT_Select_Charmap(face, ft_encoding_unicode); +// fprintf(stderr, "select unicode charmap: %d\n", error); + + if (face->charmap==NULL || face->charmap->encoding!=ft_encoding_unicode) { + WARNING("Unicode charmap not available for this font. Very bad!"); + uni_charmap = 0; + error = FT_Set_Charmap(face, face->charmaps[0]); + if (error) WARNING("No charmaps! Strange."); + } + + /* set size */ + if (FT_IS_SCALABLE(face)) { + error = FT_Set_Char_Size(face, 0, floatTof266(ppem), 0, 0); + if (error) WARNING("FT_Set_Char_Size failed."); + } else { + int j = 0; + int jppem = face->available_sizes[0].height; + /* find closest size */ + for (i = 0; inum_fixed_sizes; ++i) { + if (fabs(face->available_sizes[i].height - ppem) < abs(face->available_sizes[i].height - jppem)) { + j = i; + jppem = face->available_sizes[i].height; + } + } + WARNING("Selected font is not scalable. Using ppem=%i.", face->available_sizes[j].height); + error = FT_Set_Pixel_Sizes(face, face->available_sizes[j].width, face->available_sizes[j].height); + if (error) WARNING("FT_Set_Pixel_Sizes failed."); + } + + if (FT_IS_FIXED_WIDTH(face)) + WARNING("Selected font is fixed-width."); + + /* compute space advance */ + error = FT_Load_Char(face, ' ', load_flags); + if (error) WARNING("spacewidth set to default."); + else space_advance = f266ToInt(face->glyph->advance.x); + + if (!desc->spacewidth) desc->spacewidth = 2*padding + space_advance; + if (!desc->charspace) desc->charspace = -2*padding; + if (!desc->height) desc->height = f266ToInt(face->size->metrics.height); + + + for (i= 0; ifont[unicode?character:code] = pic_idx; + // get glyph index + if (character==0) + glyph_index = 0; + else { + glyph_index = FT_Get_Char_Index(face, uni_charmap ? character:code); + if (glyph_index==0) { + WARNING("Glyph for char 0x%02lx|U+%04lX|%c not found.", code, character, + code<' '||code>255 ? '.':(char)code); + desc->font[unicode?character:code] = -1; + continue; + } + } + desc->glyph_index[unicode?character:code] = glyph_index; + } +// fprintf(stderr, "font height: %f\n", (double)(face->bbox.yMax-face->bbox.yMin)/(double)face->units_per_EM*ppem); +// fprintf(stderr, "font width: %f\n", (double)(face->bbox.xMax-face->bbox.xMin)/(double)face->units_per_EM*ppem); + + ymax = (double)(face->bbox.yMax)/(double)face->units_per_EM*ppem+1; + ymin = (double)(face->bbox.yMin)/(double)face->units_per_EM*ppem-1; + + width = ppem*(face->bbox.xMax-face->bbox.xMin)/face->units_per_EM+3+2*padding; + if (desc->max_width < width) desc->max_width = width; + width = ALIGN(width); + desc->pic_b[pic_idx]->charwidth = width; + + if (width <= 0) { + mp_msg(MSGT_OSD, MSGL_ERR, "Wrong bounding box, width <= 0 !\n"); + return -1; + } + + if (ymax<=ymin) { + mp_msg(MSGT_OSD, MSGL_ERR, "Something went wrong. Use the source!\n"); + return -1; + } + + height = ymax - ymin + 2*padding; + if (height <= 0) { + mp_msg(MSGT_OSD, MSGL_ERR, "Wrong bounding box, height <= 0 !\n"); + return -1; + } + + if (desc->max_height < height) desc->max_height = height; + desc->pic_b[pic_idx]->charheight = height; + +// fprintf(stderr, "font height2: %d\n", height); + desc->pic_b[pic_idx]->baseline = ymax + padding; + desc->pic_b[pic_idx]->padding = padding; + desc->pic_b[pic_idx]->current_alloc = 0; + desc->pic_b[pic_idx]->current_count = 0; + + bbuffer = NULL; + + desc->pic_b[pic_idx]->w = width; + desc->pic_b[pic_idx]->h = height; + desc->pic_b[pic_idx]->c = colors; + desc->pic_b[pic_idx]->bmp = bbuffer; + desc->pic_b[pic_idx]->pen = 0; + return 0; +} + +// general outline +static void outline( + unsigned char *s, + unsigned char *t, + int width, + int height, + int stride, + unsigned char *m, + int r, + int mwidth, + int msize) { + + int x, y; + + for (y = 0; y=width ) ? r+width -x : 2*r+1; + const int y2=(y+r>=height) ? r+height-y : 2*r+1; + register unsigned char *dstp= t + (y1+y-r)* stride + x-r; + //register int *mp = m + y1 *mwidth; + register unsigned char *mp= m + msize*src + y1*mwidth; + int my; + + for(my= y1; mymaxcolor ? maxcolor : v; + } + *t++ = *s++; + s += skip; + t += skip; + } + for (x = 0; x>8; + unsigned *m3= m2 + src2*mwidth; + + int mx; + *srcp= 128; + for(mx=r-1; mx>8; + unsigned *m3= m2 + src2*mwidth; + + int mx; + *srcp= 128; + for(mx=0; mx>8; + unsigned *m3= m2 + src2*mwidth; + + int mx; + *srcp= 128; + for(mx=0; mx>8; + } + s+= stride; + t+= width + 1; + } +} + +static void resample_alpha(unsigned char *abuf, unsigned char *bbuf, int width, int height, int stride, float factor) +{ + int f=factor*256.0f; + int i,j; + for (i = 0; i < height; i++) { + unsigned char *a = abuf+i*stride; + unsigned char *b = bbuf+i*stride; + for(j=0;j>8); // scale + if (x+y>255) x=255-y; // to avoid overflows + if (x<1) x=1; else if (x>=252) x=0; + *a=x; + } + } +} + +#define ALLOC_INCR 32 +void render_one_glyph(font_desc_t *desc, int c) +{ + FT_GlyphSlot slot; + FT_UInt glyph_index; + FT_BitmapGlyph glyph; + int width, height, stride, maxw, off; + unsigned char *abuffer, *bbuffer; + + int const load_flags = FT_LOAD_DEFAULT; + int pen_xa; + int font = desc->font[c]; + int error; + +// fprintf(stderr, "render_one_glyph %d\n", c); + + if (!desc->dynamic) return; + if (desc->width[c] != -1) return; + if (desc->font[c] == -1) return; + + glyph_index = desc->glyph_index[c]; + + // load glyph + error = FT_Load_Glyph(desc->faces[font], glyph_index, load_flags); + if (error) { + WARNING("FT_Load_Glyph 0x%02x (char 0x%04x) failed.", glyph_index, c); + desc->font[c] = -1; + return; + } + slot = desc->faces[font]->glyph; + + // render glyph + if (slot->format != ft_glyph_format_bitmap) { + error = FT_Render_Glyph(slot, ft_render_mode_normal); + if (error) { + WARNING("FT_Render_Glyph 0x%04x (char 0x%04x) failed.", glyph_index, c); + desc->font[c] = -1; + return; + } + } + + // extract glyph image + error = FT_Get_Glyph(slot, (FT_Glyph*)&glyph); + if (error) { + WARNING("FT_Get_Glyph 0x%04x (char 0x%04x) failed.", glyph_index, c); + desc->font[c] = -1; + return; + } + +// fprintf(stderr, "glyph generated\n"); + + maxw = desc->pic_b[font]->charwidth; + + if (glyph->bitmap.width > maxw) { + fprintf(stderr, "glyph too wide!\n"); + } + + // allocate new memory, if needed +// fprintf(stderr, "\n%d %d %d\n", desc->pic_b[font]->charwidth, desc->pic_b[font]->charheight, desc->pic_b[font]->current_alloc); + if (desc->pic_b[font]->current_count >= desc->pic_b[font]->current_alloc) { + int newsize = desc->pic_b[font]->charwidth*desc->pic_b[font]->charheight*(desc->pic_b[font]->current_alloc+ALLOC_INCR); + int increment = desc->pic_b[font]->charwidth*desc->pic_b[font]->charheight*ALLOC_INCR; + desc->pic_b[font]->current_alloc += ALLOC_INCR; + +// fprintf(stderr, "\nns = %d inc = %d\n", newsize, increment); + + desc->pic_b[font]->bmp = realloc(desc->pic_b[font]->bmp, newsize); + desc->pic_a[font]->bmp = realloc(desc->pic_a[font]->bmp, newsize); + + off = desc->pic_b[font]->current_count*desc->pic_b[font]->charwidth*desc->pic_b[font]->charheight; + memset(desc->pic_b[font]->bmp+off, 0, increment); + memset(desc->pic_a[font]->bmp+off, 0, increment); + } + + abuffer = desc->pic_a[font]->bmp; + bbuffer = desc->pic_b[font]->bmp; + + off = desc->pic_b[font]->current_count*desc->pic_b[font]->charwidth*desc->pic_b[font]->charheight; + + paste_bitmap(bbuffer+off, + &glyph->bitmap, + desc->pic_b[font]->padding + glyph->left, + desc->pic_b[font]->baseline - glyph->top, + desc->pic_b[font]->charwidth, desc->pic_b[font]->charheight, + glyph->bitmap.width <= maxw ? glyph->bitmap.width : maxw); + +// fprintf(stderr, "glyph pasted\n"); + FT_Done_Glyph((FT_Glyph)glyph); + + /* advance pen */ + pen_xa = f266ToInt(slot->advance.x) + 2*desc->pic_b[font]->padding; + if (pen_xa > maxw) pen_xa = maxw; + + desc->start[c] = off; + width = desc->width[c] = pen_xa; + height = desc->pic_b[font]->charheight; + stride = desc->pic_b[font]->w; + + if (desc->tables.o_r == 0) { + outline0(bbuffer+off, abuffer+off, width, height, stride); + } else if (desc->tables.o_r == 1) { + outline1(bbuffer+off, abuffer+off, width, height, stride); + } else { + outline(bbuffer+off, abuffer+off, width, height, stride, + desc->tables.omt, desc->tables.o_r, desc->tables.o_w, + desc->tables.o_size); + } +// fprintf(stderr, "fg: outline t = %f\n", GetTimer()-t); + + if (desc->tables.g_r) { + blur(abuffer+off, desc->tables.tmp, width, height, stride, + desc->tables.gt2, desc->tables.g_r, + desc->tables.g_w); +// fprintf(stderr, "fg: blur t = %f\n", GetTimer()-t); + } + + resample_alpha(abuffer+off, bbuffer+off, width, height, stride, font_factor); + + desc->pic_b[font]->current_count++; +} + + +static int prepare_font(font_desc_t *desc, FT_Face face, float ppem, int pic_idx, + int charset_size, FT_ULong *charset, FT_ULong *charcodes, int unicode, + double thickness, double radius) +{ + int i, err; + int padding = ceil(radius) + ceil(thickness); + + desc->faces[pic_idx] = face; + + desc->pic_a[pic_idx] = malloc(sizeof(raw_file)); + if (!desc->pic_a[pic_idx]) return -1; + desc->pic_b[pic_idx] = malloc(sizeof(raw_file)); + if (!desc->pic_b[pic_idx]) return -1; + + desc->pic_a[pic_idx]->bmp = NULL; + desc->pic_a[pic_idx]->pal = NULL; + desc->pic_b[pic_idx]->bmp = NULL; + desc->pic_b[pic_idx]->pal = NULL; + + desc->pic_a[pic_idx]->pal = malloc(sizeof(unsigned char)*256*3); + if (!desc->pic_a[pic_idx]->pal) return -1; + for (i = 0; i<768; ++i) desc->pic_a[pic_idx]->pal[i] = i/3; + + desc->pic_b[pic_idx]->pal = malloc(sizeof(unsigned char)*256*3); + if (!desc->pic_b[pic_idx]->pal) return -1; + for (i = 0; i<768; ++i) desc->pic_b[pic_idx]->pal[i] = i/3; + +// ttime = GetTimer(); + err = check_font(desc, ppem, padding, pic_idx, charset_size, charset, charcodes, unicode); +// ttime=GetTimer()-ttime; +// printf("render: %7f us\n",ttime); + if (err) return -1; +// fprintf(stderr, "fg: render t = %f\n", GetTimer()-t); + + desc->pic_a[pic_idx]->w = desc->pic_b[pic_idx]->w; + desc->pic_a[pic_idx]->h = desc->pic_b[pic_idx]->h; + desc->pic_a[pic_idx]->c = colors; + + desc->pic_a[pic_idx]->bmp = NULL; + +// fprintf(stderr, "fg: w = %d, h = %d\n", desc->pic_a[pic_idx]->w, desc->pic_a[pic_idx]->h); + return 0; + +} + +static int generate_tables(font_desc_t *desc, double thickness, double radius) +{ + int width = desc->max_height; + int height = desc->max_width; + + double A = log(1.0/base)/(radius*radius*2); + int mx, my, i; + double volume_diff, volume_factor = 0; + unsigned char *omtp; + + desc->tables.g_r = ceil(radius); + desc->tables.o_r = ceil(thickness); + desc->tables.g_w = 2*desc->tables.g_r+1; + desc->tables.o_w = 2*desc->tables.o_r+1; + desc->tables.o_size = desc->tables.o_w * desc->tables.o_w; + +// fprintf(stderr, "o_r = %d\n", desc->tables.o_r); + + if (desc->tables.g_r) { + desc->tables.g = malloc(desc->tables.g_w * sizeof(unsigned)); + desc->tables.gt2 = malloc(256 * desc->tables.g_w * sizeof(unsigned)); + if (desc->tables.g==NULL || desc->tables.gt2==NULL) { + return -1; + } + } + desc->tables.om = malloc(desc->tables.o_w*desc->tables.o_w * sizeof(unsigned)); + desc->tables.omt = malloc(desc->tables.o_size*256); + + omtp = desc->tables.omt; + desc->tables.tmp = malloc((width+1)*height*sizeof(short)); + + if (desc->tables.om==NULL || desc->tables.omt==NULL || desc->tables.tmp==NULL) { + return -1; + }; + + if (desc->tables.g_r) { + // gaussian curve with volume = 256 + for (volume_diff=10000000; volume_diff>0.0000001; volume_diff*=0.5){ + volume_factor+= volume_diff; + desc->tables.volume=0; + for (i = 0; itables.g_w; ++i) { + desc->tables.g[i] = (unsigned)(exp(A * (i-desc->tables.g_r)*(i-desc->tables.g_r)) * volume_factor + .5); + desc->tables.volume+= desc->tables.g[i]; + } + if(desc->tables.volume>256) volume_factor-= volume_diff; + } + desc->tables.volume=0; + for (i = 0; itables.g_w; ++i) { + desc->tables.g[i] = (unsigned)(exp(A * (i-desc->tables.g_r)*(i-desc->tables.g_r)) * volume_factor + .5); + desc->tables.volume+= desc->tables.g[i]; + } + + // gauss table: + for(mx=0;mxtables.g_w;mx++){ + for(i=0;i<256;i++){ + desc->tables.gt2[mx+i*desc->tables.g_w] = i*desc->tables.g[mx]; + } + } + } + + /* outline matrix */ + for (my = 0; mytables.o_w; ++my) { + for (mx = 0; mxtables.o_w; ++mx) { + // antialiased circle would be perfect here, but this one is good enough + double d = thickness + 1 - sqrt((mx-desc->tables.o_r)*(mx-desc->tables.o_r)+(my-desc->tables.o_r)*(my-desc->tables.o_r)); + desc->tables.om[mx+my*desc->tables.o_w] = d>=1 ? base : d<=0 ? 0 : (d*base + .5); + } + } + + // outline table: + for(i=0;i<256;i++){ + for(mx=0;mxtables.o_size;mx++) *(omtp++) = (i*desc->tables.om[mx] + (base/2))/base; + } + + return 0; +} + +#ifdef CONFIG_ICONV +/* decode from 'encoding' to unicode */ +static FT_ULong decode_char(iconv_t *cd, char c) { + FT_ULong o; + char *inbuf = &c; + char *outbuf = (char*)&o; + size_t inbytesleft = 1; + size_t outbytesleft = sizeof(FT_ULong); + + iconv(*cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft); + + /* convert unicode BigEndian -> MachineEndian */ + o = be2me_32(o); + + // if (count==-1) o = 0; // not OK, at least my iconv() returns E2BIG for all + if (outbytesleft!=0) o = 0; + + /* we don't want control characters */ + if (o>=0x7f && o<0xa0) o = 0; + return o; +} + +static int prepare_charset(char *charmap, char *encoding, FT_ULong *charset, FT_ULong *charcodes) { + FT_ULong i; + int count = 0; + int charset_size; + iconv_t cd; + + // check if ucs-4 is available + cd = iconv_open(charmap, charmap); + if (cd==(iconv_t)-1) { + mp_msg(MSGT_OSD, MSGL_ERR, "iconv doesn't know %s encoding. Use the source!\n", charmap); + return -1; + } + + iconv_close(cd); + + cd = iconv_open(charmap, encoding); + if (cd==(iconv_t)-1) { + mp_msg(MSGT_OSD, MSGL_ERR, "Unsupported encoding `%s', use iconv --list to list character sets known on your system.\n", encoding); + return -1; + } + + charset_size = 256 - first_char; + for (i = 0; icharmap==NULL || face->charmap->encoding!=ft_encoding_unicode) { + WARNING("Unicode charmap not available for this font. Very bad!"); + return -1; + } +#ifdef HAVE_FREETYPE21 + i = 0; + charcode = FT_Get_First_Char( face, &gindex ); + while (gindex != 0) { + if (charcode < 65536 && charcode >= 33) { // sanity check + charset[i] = charcode; + charcodes[i] = 0; + i++; + } + charcode = FT_Get_Next_Char( face, charcode, &gindex ); + } +#else + // for FT < 2.1 we have to use brute force enumeration + i = 0; + for (j = 33; j < 65536; j++) { + gindex = FT_Get_Char_Index(face, j); + if (gindex > 0) { + charset[i] = j; + charcodes[i] = 0; + i++; + } + } +#endif + mp_msg(MSGT_OSD, MSGL_V, "Unicode font: %d glyphs.\n", i); + + return i; +} +#endif + +static font_desc_t* init_font_desc(void) +{ + font_desc_t *desc; + + desc = calloc(1, sizeof(*desc)); + if(!desc) return NULL; + + desc->dynamic = 1; + + /* setup sane defaults */ + desc->freetype = 1; + + memset(desc->start, 0xff, sizeof(desc->start)); + memset(desc->width, 0xff, sizeof(desc->width)); + memset(desc->font, 0xff, sizeof(desc->font)); + + return desc; +} + +void free_font_desc(font_desc_t *desc) +{ + int i; + + if (!desc) return; + +// if (!desc->dynamic) return; // some vo_aa crap, better leaking than crashing + + free(desc->name); + free(desc->fpath); + + for(i = 0; i < 16; i++) { + if (desc->pic_a[i]) { + free(desc->pic_a[i]->bmp); + free(desc->pic_a[i]->pal); + free(desc->pic_a[i]); + } + if (desc->pic_b[i]) { + free(desc->pic_b[i]->bmp); + free(desc->pic_b[i]->pal); + free(desc->pic_b[i]); + } + } + + free(desc->tables.g); + free(desc->tables.gt2); + free(desc->tables.om); + free(desc->tables.omt); + free(desc->tables.tmp); + + for(i = 0; i < desc->face_cnt; i++) { + FT_Done_Face(desc->faces[i]); + } + + free(desc); +} + +static int load_sub_face(const char *name, int face_index, FT_Face *face) +{ + int err = -1; + + if