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 + -
显示快捷键?