ass_render.c

来自「君正早期ucos系统(只有早期的才不没有打包成库),MPLAYER,文件系统,图」· C语言 代码 · 共 2,195 行 · 第 1/5 页

C
2,195
字号
 * This function goes through text_info and calculates text parameters. * The following text_info fields are filled: *   height *   lines[].height *   lines[].asc *   lines[].desc */static void measure_text(void){	int cur_line = 0, max_asc = 0, max_desc = 0;	int i;	text_info.height = 0;	for (i = 0; i < text_info.length + 1; ++i) {		if ((i == text_info.length) || text_info.glyphs[i].linebreak) {			text_info.lines[cur_line].asc = max_asc;			text_info.lines[cur_line].desc = max_desc;			text_info.height += max_asc + max_desc;			cur_line ++;			max_asc = max_desc = 0;		}		if (i < text_info.length) {			glyph_info_t* cur = text_info.glyphs + i;			if (cur->asc > max_asc)				max_asc = cur->asc;			if (cur->desc > max_desc)				max_desc = cur->desc;		}	}	text_info.height += (text_info.n_lines - 1) * double_to_d6(global_settings->line_spacing);}/** * \brief rearrange text between lines * \param max_text_width maximal text line width in pixels * The algo is similar to the one in libvo/sub.c: * 1. Place text, wrapping it when current line is full * 2. Try moving words from the end of a line to the beginning of the next one while it reduces * the difference in lengths between this two lines. * The result may not be optimal, but usually is good enough. */static void wrap_lines_smart(int max_text_width){	int i, j;	glyph_info_t *cur, *s1, *e1, *s2, *s3, *w;	int last_space;	int break_type;	int exit;	int pen_shift_x;	int pen_shift_y;	int cur_line;	last_space = -1;	text_info.n_lines = 1;	break_type = 0;	s1 = text_info.glyphs; // current line start	for (i = 0; i < text_info.length; ++i) {		int break_at, s_offset, len;		cur = text_info.glyphs + i;		break_at = -1;		s_offset = s1->bbox.xMin + s1->pos.x;		len = (cur->bbox.xMax + cur->pos.x) - s_offset;		if (cur->symbol == '\n') {			break_type = 2;			break_at = i;			mp_msg(MSGT_ASS, MSGL_DBG2, "forced line break at %d\n", break_at);		}				if (len >= max_text_width) {			break_type = 1;			break_at = last_space;			if (break_at == -1)				break_at = i - 1;			if (break_at == -1)				break_at = 0;			mp_msg(MSGT_ASS, MSGL_DBG2, "overfill at %d\n", i);			mp_msg(MSGT_ASS, MSGL_DBG2, "line break at %d\n", break_at);		}		if (break_at != -1) {			// need to use one more line			// marking break_at+1 as start of a new line			int lead = break_at + 1; // the first symbol of the new line			if (text_info.n_lines >= MAX_LINES) {				// to many lines ! 				// no more linebreaks				for (j = lead; j < text_info.length; ++j)					text_info.glyphs[j].linebreak = 0;				break;			}			if (lead < text_info.length)				text_info.glyphs[lead].linebreak = break_type;			last_space = -1;			s1 = text_info.glyphs + lead;			s_offset = s1->bbox.xMin + s1->pos.x;			text_info.n_lines ++;		}				if (cur->symbol == ' ')			last_space = i;		// make sure the hard linebreak is not forgotten when		// there was a new soft linebreak just inserted		if (cur->symbol == '\n' && break_type == 1)			i--;	}#define DIFF(x,y) (((x) < (y)) ? (y - x) : (x - y))	exit = 0;	while (!exit) {		exit = 1;		w = s3 = text_info.glyphs;		s1 = s2 = 0;		for (i = 0; i <= text_info.length; ++i) {			cur = text_info.glyphs + i;			if ((i == text_info.length) || cur->linebreak) {				s1 = s2;				s2 = s3;				s3 = cur;				if (s1 && (s2->linebreak == 1)) { // have at least 2 lines, and linebreak is 'soft'					int l1, l2, l1_new, l2_new;					w = s2;					do { --w; } while ((w > s1) && (w->symbol == ' '));					while ((w > s1) && (w->symbol != ' ')) { --w; }					e1 = w;					while ((e1 > s1) && (e1->symbol == ' ')) { --e1; }					if (w->symbol == ' ') ++w;					l1 = ((s2-1)->bbox.xMax + (s2-1)->pos.x) - (s1->bbox.xMin + s1->pos.x);					l2 = ((s3-1)->bbox.xMax + (s3-1)->pos.x) - (s2->bbox.xMin + s2->pos.x);					l1_new = (e1->bbox.xMax + e1->pos.x) - (s1->bbox.xMin + s1->pos.x);					l2_new = ((s3-1)->bbox.xMax + (s3-1)->pos.x) - (w->bbox.xMin + w->pos.x);					if (DIFF(l1_new, l2_new) < DIFF(l1, l2)) {						w->linebreak = 1;						s2->linebreak = 0;						exit = 0;					}				}			}			if (i == text_info.length)				break;		}			}	assert(text_info.n_lines >= 1);#undef DIFF		measure_text();	pen_shift_x = 0;	pen_shift_y = 0;	cur_line = 1;	for (i = 0; i < text_info.length; ++i) {		cur = text_info.glyphs + i;		if (cur->linebreak) {			int height = text_info.lines[cur_line - 1].desc + text_info.lines[cur_line].asc;			cur_line ++;			pen_shift_x = - cur->pos.x;			pen_shift_y += d6_to_int(height + double_to_d6(global_settings->line_spacing));			mp_msg(MSGT_ASS, MSGL_DBG2, "shifting from %d to %d by (%d, %d)\n", i, text_info.length - 1, pen_shift_x, pen_shift_y);		}		cur->pos.x += pen_shift_x;		cur->pos.y += pen_shift_y;	}}/** * \brief determine karaoke effects * Karaoke effects cannot be calculated during parse stage (get_next_char()), * so they are done in a separate step. * Parse stage: when karaoke style override is found, its parameters are stored in the next glyph's  * (the first glyph of the karaoke word)'s effect_type and effect_timing. * This function: * 1. sets effect_type for all glyphs in the word (_karaoke_ word) * 2. sets effect_timing for all glyphs to x coordinate of the border line between the left and right karaoke parts * (left part is filled with PrimaryColour, right one - with SecondaryColour). */static void process_karaoke_effects(void){	glyph_info_t *cur, *cur2;	glyph_info_t *s1, *e1; // start and end of the current word	glyph_info_t *s2; // start of the next word	int i;	int timing; // current timing	int tm_start, tm_end; // timings at start and end of the current word	int tm_current;	double dt;	int x;	int x_start, x_end;	tm_current = frame_context.time - render_context.event->Start;	timing = 0;	s1 = s2 = 0;	for (i = 0; i <= text_info.length; ++i) {		cur = text_info.glyphs + i;		if ((i == text_info.length) || (cur->effect_type != EF_NONE)) {			s1 = s2;			s2 = cur;			if (s1) {				e1 = s2 - 1;				tm_start = timing + s1->effect_skip_timing;				tm_end = tm_start + s1->effect_timing;				timing = tm_end;				x_start = 1000000;				x_end = -1000000;				for (cur2 = s1; cur2 <= e1; ++cur2) {					x_start = FFMIN(x_start, cur2->bbox.xMin + cur2->pos.x);					x_end = FFMAX(x_end, cur2->bbox.xMax + cur2->pos.x);				}				dt = (tm_current - tm_start);				if ((s1->effect_type == EF_KARAOKE) || (s1->effect_type == EF_KARAOKE_KO)) {					if (dt > 0)						x = x_end + 1;					else						x = x_start;				} else if (s1->effect_type == EF_KARAOKE_KF) {					dt /= (tm_end - tm_start);					x = x_start + (x_end - x_start) * dt;				} else {					mp_msg(MSGT_ASS, MSGL_ERR, MSGTR_LIBASS_UnknownEffectType_InternalError);					continue;				}				for (cur2 = s1; cur2 <= e1; ++cur2) {					cur2->effect_type = s1->effect_type;					cur2->effect_timing = x - cur2->pos.x;				}			}		}	}}/** * \brief Calculate base point for positioning and rotation * \param bbox text bbox * \param alignment alignment * \param bx, by out: base point coordinates */static void get_base_point(FT_BBox bbox, int alignment, int* bx, int* by){	const int halign = alignment & 3;	const int valign = alignment & 12;	if (bx)		switch(halign) {		case HALIGN_LEFT:			*bx = bbox.xMin;			break;		case HALIGN_CENTER:			*bx = (bbox.xMax + bbox.xMin) / 2;			break;		case HALIGN_RIGHT:			*bx = bbox.xMax;			break;		}	if (by)		switch(valign) {		case VALIGN_TOP:			*by = bbox.yMin;			break;		case VALIGN_CENTER:			*by = (bbox.yMax + bbox.yMin) / 2;			break;		case VALIGN_SUB:			*by = bbox.yMax;			break;		}}/** * \brief Multiply 4-vector by 4-matrix * \param a 4-vector * \param m 4-matrix] * \param b out: 4-vector * Calculates a * m and stores result in b */static inline void transform_point_3d(double *a, double *m, double *b){	b[0] = a[0] * m[0] + a[1] * m[4] + a[2] * m[8] +  a[3] * m[12];	b[1] = a[0] * m[1] + a[1] * m[5] + a[2] * m[9] +  a[3] * m[13];	b[2] = a[0] * m[2] + a[1] * m[6] + a[2] * m[10] + a[3] * m[14];	b[3] = a[0] * m[3] + a[1] * m[7] + a[2] * m[11] + a[3] * m[15];}/** * \brief Apply 3d transformation to a vector * \param v FreeType vector (2d) * \param m 4-matrix * Transforms v by m, projects the result back to the screen plane * Result is returned in v. */static inline void transform_vector_3d(FT_Vector* v, double *m) {	const double camera = 2500 * frame_context.border_scale; // camera distance	double a[4], b[4];	a[0] = d6_to_double(v->x);	a[1] = d6_to_double(v->y);	a[2] = 0.;	a[3] = 1.;	transform_point_3d(a, m, b);	/* Apply perspective projection with the following matrix:	   2500     0     0     0	      0  2500     0     0	      0     0     0     0	      0     0     8     2500	   where 2500 is camera distance, 8 - z-axis scale.	   Camera is always located in (org_x, org_y, -2500). This means	   that different subtitle events can be displayed at the same time	   using different cameras. */	b[0] *= camera;	b[1] *= camera;	b[3] = 8 * b[2] + camera;	if (b[3] < 0.001 && b[3] > -0.001)		b[3] = b[3] < 0. ? -0.001 : 0.001;	v->x = double_to_d6(b[0] / b[3]);	v->y = double_to_d6(b[1] / b[3]);}/** * \brief Apply 3d transformation to a glyph * \param glyph FreeType glyph * \param m 4-matrix * Transforms glyph by m, projects the result back to the screen plane * Result is returned in glyph. */static inline void transform_glyph_3d(FT_Glyph glyph, double *m, FT_Vector shift) {	int i;	FT_Outline* outline = &((FT_OutlineGlyph)glyph)->outline;	FT_Vector* p = outline->points;	for (i=0; i<outline->n_points; i++) {		p[i].x += shift.x;		p[i].y += shift.y;		transform_vector_3d(p + i, m);		p[i].x -= shift.x;		p[i].y -= shift.y;	}	//transform_vector_3d(&glyph->advance, m);}/** * \brief Apply 3d transformation to several objects * \param shift FreeType vector * \param glyph FreeType glyph * \param glyph2 FreeType glyph * \param frx x-axis rotation angle * \param fry y-axis rotation angle * \param frz z-axis rotation angle * Rotates both glyphs by frx, fry and frz. Shift vector is added before rotation and subtracted after it. */static void transform_3d(FT_Vector shift, FT_Glyph* glyph, FT_Glyph* glyph2, double frx, double fry, double frz){	fry = - fry; // FreeType's y axis goes in the opposite direction	if (frx != 0. || fry != 0. || frz != 0.) {		double m[16];		double sx = sin(frx);		double sy = sin(fry); 		double sz = sin(frz);		double cx = cos(frx);		double cy = cos(fry);		double cz = cos(frz);		m[0] = cy * cz;            m[1] = cy*sz;              m[2]  = -sy;    m[3] = 0.0;		m[4] = -cx*sz + sx*sy*cz;  m[5] = cx*cz + sx*sy*sz;   m[6]  = sx*cy;  m[7] = 0.0;		m[8] = sx*sz + cx*sy*cz;   m[9] = -sx*cz + cx*sy*sz;  m[10] = cx*cy;  m[11] = 0.0;		m[12] = 0.0;               m[13] = 0.0;               m[14] = 0.0;    m[15] = 1.0;		if (glyph && *glyph)			transform_glyph_3d(*glyph, m, shift);		if (glyph2 && *glyph2)			transform_glyph_3d(*glyph2, m, shift);	}}/** * \brief Main ass rendering function, glues everything together * \param event event to render * Process event, appending resulting ass_image_t's to images_root. */static int ass_render_event(ass_event_t* event, event_images_t* event_images){	char* p;	FT_UInt previous; 	FT_UInt num_glyphs;	FT_Vector pen;	unsigned code;	FT_BBox bbox;	int i, j;	FT_Vector shift;	int MarginL, MarginR, MarginV;	int last_break;	int alignment, halign, valign;	int device_x = 0, device_y = 0;	if (event->Style >= frame_context.track->n_styles) {		mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_NoStyleFound);		return 1;	}	if (!event->Text) {		mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_EmptyEvent);		return 1;	}	init_render_context(event);	text_info.length = 0;	pen.x = 0;	pen.y = 0;	previous = 0;	num_glyphs = 0;	p = event->Text;	// Event parsing.	while (1) {		// get next char, executing style override		// this affects render_context		code = get_next_char(&p);				// face could have been changed in get_next_char		if (!render_context.font) {			free_render_context();			return 1;		}		if (code == 0)			break;		if (text_info.length >= MAX_GLYPHS) {			mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_MAX_GLYPHS_Reached, 					(int)(event - frame_context.track->events), event->Start, event->Duration, event->Text);			break;		}		if ( previous && code ) {			FT_Vector delta;			delta = ass_font_get_kerning(render_context.font, previous, code);			pen.x += delta.x * render_context.scale_x;			pen.y += delta.y * render_context.scale_y;		}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?