ass_render.c
来自「君正早期ucos系统(只有早期的才不没有打包成库),MPLAYER,文件系统,图」· C语言 代码 · 共 2,195 行 · 第 1/5 页
C
2,195 行
// -*- c-basic-offset: 8; indent-tabs-mode: t -*-// vim:ts=8:sw=8:noet:ai:/* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com> This program 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. This program 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA*/#include "config.h"#include <assert.h>#include <math.h>#include <inttypes.h>#include <ft2build.h>#include FT_FREETYPE_H#include FT_STROKER_H#include FT_GLYPH_H#include FT_SYNTHESIS_H#include "mputils.h"#include "ass.h"#include "ass_font.h"#include "ass_bitmap.h"#include "ass_cache.h"#include "ass_utils.h"#include "ass_fontconfig.h"#include "ass_library.h"#define MAX_GLYPHS 1000#define MAX_LINES 100static int last_render_id = 0;typedef struct ass_settings_s { int frame_width; int frame_height; double font_size_coeff; // font size multiplier double line_spacing; // additional line spacing (in frame pixels) int top_margin; // height of top margin. Everything except toptitles is shifted down by top_margin. int bottom_margin; // height of bottom margin. (frame_height - top_margin - bottom_margin) is original video height. int left_margin; int right_margin; int use_margins; // 0 - place all subtitles inside original frame // 1 - use margins for placing toptitles and subtitles double aspect; // frame aspect ratio, d_width / d_height. ass_hinting_t hinting; char* default_font; char* default_family;} ass_settings_t;// a rendered eventtypedef struct event_images_s { ass_image_t* imgs; int top, height; int detect_collisions; int shift_direction; ass_event_t* event;} event_images_t;struct ass_renderer_s { ass_library_t* library; FT_Library ftlibrary; fc_instance_t* fontconfig_priv; ass_settings_t settings; int render_id; ass_synth_priv_t* synth_priv; ass_image_t* images_root; // rendering result is stored here ass_image_t* prev_images_root; event_images_t* eimg; // temporary buffer for sorting rendered events int eimg_size; // allocated buffer size};typedef enum {EF_NONE = 0, EF_KARAOKE, EF_KARAOKE_KF, EF_KARAOKE_KO} effect_t;// describes a glyph// glyph_info_t and text_info_t are used for text centering and word-wrapping operationstypedef struct glyph_info_s { unsigned symbol; FT_Glyph glyph; FT_Glyph outline_glyph; bitmap_t* bm; // glyph bitmap bitmap_t* bm_o; // outline bitmap bitmap_t* bm_s; // shadow bitmap FT_BBox bbox; FT_Vector pos; char linebreak; // the first (leading) glyph of some line ? uint32_t c[4]; // colors FT_Vector advance; // 26.6 effect_t effect_type; int effect_timing; // time duration of current karaoke word // after process_karaoke_effects: distance in pixels from the glyph origin. // part of the glyph to the left of it is displayed in a different color. int effect_skip_timing; // delay after the end of last karaoke word int asc, desc; // font max ascender and descender// int height; int be; // blur edges int shadow; double frx, fry, frz; // rotation bitmap_hash_key_t hash_key;} glyph_info_t;typedef struct line_info_s { int asc, desc;} line_info_t;typedef struct text_info_s { glyph_info_t* glyphs; int length; line_info_t lines[MAX_LINES]; int n_lines; int height;} text_info_t;// Renderer state.// Values like current font face, color, screen position, clipping and so on are stored here.typedef struct render_context_s { ass_event_t* event; ass_style_t* style; ass_font_t* font; char* font_path; double font_size; FT_Stroker stroker; int alignment; // alignment overrides go here; if zero, style value will be used double frx, fry, frz; enum { EVENT_NORMAL, // "normal" top-, sub- or mid- title EVENT_POSITIONED, // happens after pos(,), margins are ignored EVENT_HSCROLL, // "Banner" transition effect, text_width is unlimited EVENT_VSCROLL // "Scroll up", "Scroll down" transition effects } evt_type; int pos_x, pos_y; // position int org_x, org_y; // origin char have_origin; // origin is explicitly defined; if 0, get_base_point() is used double scale_x, scale_y; double hspacing; // distance between letters, in pixels double border; // outline width uint32_t c[4]; // colors(Primary, Secondary, so on) in RGBA int clip_x0, clip_y0, clip_x1, clip_y1; char detect_collisions; uint32_t fade; // alpha from \fad char be; // blur edges int shadow; effect_t effect_type; int effect_timing; int effect_skip_timing; enum { SCROLL_LR, // left-to-right SCROLL_RL, SCROLL_TB, // top-to-bottom SCROLL_BT } scroll_direction; // for EVENT_HSCROLL, EVENT_VSCROLL int scroll_shift; // face properties char* family; unsigned bold; unsigned italic; } render_context_t;// frame-global datatypedef struct frame_context_s { ass_renderer_t* ass_priv; int width, height; // screen dimensions int orig_height; // frame height ( = screen height - margins ) int orig_width; // frame width ( = screen width - margins ) ass_track_t* track; long long time; // frame's timestamp, ms double font_scale; double font_scale_x; // x scale applied to all glyphs to preserve text aspect ratio double border_scale;} frame_context_t;static ass_renderer_t* ass_renderer;static ass_settings_t* global_settings;static text_info_t text_info;static render_context_t render_context;static frame_context_t frame_context;struct render_priv_s { int top, height; int render_id;};static void ass_lazy_track_init(void){ ass_track_t* track = frame_context.track; if (track->PlayResX && track->PlayResY) return; if (!track->PlayResX && !track->PlayResY) { mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_NeitherPlayResXNorPlayResYDefined); track->PlayResX = 384; track->PlayResY = 288; } else { double orig_aspect = (global_settings->aspect * frame_context.height * frame_context.orig_width) / frame_context.orig_height / frame_context.width; if (!track->PlayResY) { track->PlayResY = track->PlayResX / orig_aspect + .5; mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_PlayResYUndefinedSettingY, track->PlayResY); } else if (!track->PlayResX) { track->PlayResX = track->PlayResY * orig_aspect + .5; mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_PlayResXUndefinedSettingX, track->PlayResX); } }}ass_renderer_t* ass_renderer_init(ass_library_t* library){ int error; FT_Library ft; ass_renderer_t* priv = 0; memset(&render_context, 0, sizeof(render_context)); memset(&frame_context, 0, sizeof(frame_context)); memset(&text_info, 0, sizeof(text_info)); error = FT_Init_FreeType( &ft ); if ( error ) { mp_msg(MSGT_ASS, MSGL_FATAL, MSGTR_LIBASS_FT_Init_FreeTypeFailed); goto ass_init_exit; } priv = calloc(1, sizeof(ass_renderer_t)); if (!priv) { FT_Done_FreeType(ft); goto ass_init_exit; } priv->synth_priv = ass_synth_init(); priv->library = library; priv->ftlibrary = ft; // images_root and related stuff is zero-filled in calloc ass_font_cache_init(); ass_bitmap_cache_init(); ass_glyph_cache_init(); text_info.glyphs = calloc(MAX_GLYPHS, sizeof(glyph_info_t)); ass_init_exit: if (priv) mp_msg(MSGT_ASS, MSGL_INFO, MSGTR_LIBASS_Init); else mp_msg(MSGT_ASS, MSGL_ERR, MSGTR_LIBASS_InitFailed); return priv;}void ass_renderer_done(ass_renderer_t* priv){ ass_font_cache_done(); ass_bitmap_cache_done(); ass_glyph_cache_done(); if (render_context.stroker) { FT_Stroker_Done(render_context.stroker); render_context.stroker = 0; } if (priv && priv->ftlibrary) FT_Done_FreeType(priv->ftlibrary); if (priv && priv->fontconfig_priv) fontconfig_done(priv->fontconfig_priv); if (priv && priv->synth_priv) ass_synth_done(priv->synth_priv); if (priv && priv->eimg) free(priv->eimg); if (priv) free(priv); if (text_info.glyphs) free(text_info.glyphs);}/** * \brief Create a new ass_image_t * Parameters are the same as ass_image_t fields. */static ass_image_t* my_draw_bitmap(unsigned char* bitmap, int bitmap_w, int bitmap_h, int stride, int dst_x, int dst_y, uint32_t color){ ass_image_t* img = calloc(1, sizeof(ass_image_t)); img->w = bitmap_w; img->h = bitmap_h; img->stride = stride; img->bitmap = bitmap; img->color = color; img->dst_x = dst_x; img->dst_y = dst_y; return img;}/** * \brief convert bitmap glyph into ass_image_t struct(s) * \param bit freetype bitmap glyph, FT_PIXEL_MODE_GRAY * \param dst_x bitmap x coordinate in video frame * \param dst_y bitmap y coordinate in video frame * \param color first color, RGBA * \param color2 second color, RGBA * \param brk x coordinate relative to glyph origin, color is used to the left of brk, color2 - to the right * \param tail pointer to the last image's next field, head of the generated list should be stored here * \return pointer to the new list tail * Performs clipping. Uses my_draw_bitmap for actual bitmap convertion. */static ass_image_t** render_glyph(bitmap_t* bm, int dst_x, int dst_y, uint32_t color, uint32_t color2, int brk, ass_image_t** tail){ // brk is relative to dst_x // color = color left of brk // color2 = color right of brk int b_x0, b_y0, b_x1, b_y1; // visible part of the bitmap int clip_x0, clip_y0, clip_x1, clip_y1; int tmp; ass_image_t* img; dst_x += bm->left; dst_y += bm->top; brk -= bm->left; // clipping clip_x0 = render_context.clip_x0; clip_y0 = render_context.clip_y0; clip_x1 = render_context.clip_x1; clip_y1 = render_context.clip_y1; b_x0 = 0; b_y0 = 0; b_x1 = bm->w; b_y1 = bm->h; tmp = dst_x - clip_x0; if (tmp < 0) { mp_msg(MSGT_ASS, MSGL_DBG2, "clip left\n"); b_x0 = - tmp; } tmp = dst_y - clip_y0; if (tmp < 0) { mp_msg(MSGT_ASS, MSGL_DBG2, "clip top\n"); b_y0 = - tmp; } tmp = clip_x1 - dst_x - bm->w; if (tmp < 0) { mp_msg(MSGT_ASS, MSGL_DBG2, "clip right\n"); b_x1 = bm->w + tmp; } tmp = clip_y1 - dst_y - bm->h; if (tmp < 0) { mp_msg(MSGT_ASS, MSGL_DBG2, "clip bottom\n"); b_y1 = bm->h + tmp; } if ((b_y0 >= b_y1) || (b_x0 >= b_x1)) return tail; if (brk > b_x0) { // draw left part if (brk > b_x1) brk = b_x1; img = my_draw_bitmap(bm->buffer + bm->w * b_y0 + b_x0, brk - b_x0, b_y1 - b_y0, bm->w, dst_x + b_x0, dst_y + b_y0, color); *tail = img; tail = &img->next; } if (brk < b_x1) { // draw right part if (brk < b_x0) brk = b_x0; img = my_draw_bitmap(bm->buffer + bm->w * b_y0 + brk, b_x1 - brk, b_y1 - b_y0, bm->w, dst_x + brk, dst_y + b_y0, color2); *tail = img; tail = &img->next; } return tail;}/** * \brief Convert text_info_t struct to ass_image_t list * Splits glyphs in halves when needed (for \kf karaoke). */static ass_image_t* render_text(text_info_t* text_info, int dst_x, int dst_y){ int pen_x, pen_y; int i; bitmap_t* bm; ass_image_t* head; ass_image_t** tail = &head; for (i = 0; i < text_info->length; ++i) { glyph_info_t* info = text_info->glyphs + i; if ((info->symbol == 0) || (info->symbol == '\n') || !info->bm_s || (info->shadow == 0)) continue; pen_x = dst_x + info->pos.x + info->shadow; pen_y = dst_y + info->pos.y + info->shadow; bm = info->bm_s; tail = render_glyph(bm, pen_x, pen_y, info->c[3], 0, 1000000, tail); } for (i = 0; i < text_info->length; ++i) { glyph_info_t* info = text_info->glyphs + i; if ((info->symbol == 0) || (info->symbol == '\n') || !info->bm_o) continue; pen_x = dst_x + info->pos.x; pen_y = dst_y + info->pos.y; bm = info->bm_o; if ((info->effect_type == EF_KARAOKE_KO) && (info->effect_timing <= info->bbox.xMax)) { // do nothing } else tail = render_glyph(bm, pen_x, pen_y, info->c[2], 0, 1000000, tail); } for (i = 0; i < text_info->length; ++i) { glyph_info_t* info = text_info->glyphs + i; if ((info->symbol == 0) || (info->symbol == '\n') || !info->bm) continue; pen_x = dst_x + info->pos.x; pen_y = dst_y + info->pos.y; bm = info->bm; if ((info->effect_type == EF_KARAOKE) || (info->effect_type == EF_KARAOKE_KO)) { if (info->effect_timing > info->bbox.xMax) tail = render_glyph(bm, pen_x, pen_y, info->c[0], 0, 1000000, tail); else tail = render_glyph(bm, pen_x, pen_y, info->c[1], 0, 1000000, tail); } else if (info->effect_type == EF_KARAOKE_KF) { tail = render_glyph(bm, pen_x, pen_y, info->c[0], info->c[1], info->effect_timing, tail); } else tail = render_glyph(bm, pen_x, pen_y, info->c[0], 0, 1000000, tail); }
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?