static text_segment_t* NewTextSegmentPopStyle( text_segment_t* p_segment, style_stack_t** pp_stack ) { text_segment_t* p_new = text_segment_New( NULL ); if ( unlikely( p_new == NULL ) ) return NULL; // We shouldn't have an empty stack since this happens when closing a tag, // but better be safe than sorry if (/when) we encounter a broken subtitle file. PopStyle( pp_stack ); text_style_t* p_dup = ( *pp_stack ) ? text_style_Duplicate( (*pp_stack)->p_style ) : text_style_Create( STYLE_NO_DEFAULTS ); p_new->style = p_dup; p_segment->p_next = p_new; return p_new; }
int draw(Display *display,int num, int mouseX, int mouseY) { double angle1, angle2; double dx,dy; double tx,ty; double cosa, sina; double x,y,x2,y2; x = pupils[num]; y = pupils[num+1]; x2 = eyes[num]; y2 = eyes[num+1]; DrawEllipse(display, (int)eyes[num], (int)eyes[num+1], EYE_W, EYE_H); PushStyle(display); Fill1i(display, 0); dx = (double)mouseX - x; dy = (double)mouseY - y; angle1 = atan2(dy,dx); cosa = cos(angle1); sina = sin(angle1); tx = mouseX - cosa * EYE_RAD; ty = mouseY - sina * EYE_RAD; dx = tx - x2; dy = ty - y2; angle2 = atan2(dy,dx); cosa = cos(angle2); sina = sin(angle2); x = x2 + cosa * EYE_RAD; y = y2 + sina * EYE_RAD; PushMatrix(display); Translate2f(display,x, y); Rotate(display, angle1); DrawEllipse(display, EYE_RAD, 0, PUPIL_W, PUPIL_W); PopMatrix(display); pupils[num] = x; pupils[num+1] = y; PopStyle(display); return 0; }
static text_segment_t* ParseSubtitles( int *pi_align, const char *psz_subtitle ) { text_segment_t* p_segment; text_segment_t* p_first_segment; style_stack_t* p_stack = NULL; tag_stack_t* p_tag_stack = NULL; //FIXME: Remove initial allocation? Might make the below code more complicated p_first_segment = p_segment = text_segment_New( "" ); bool b_has_align = false; /* */ while( *psz_subtitle ) { /* HTML extensions */ if( *psz_subtitle == '<' ) { char *psz_tagname = GetTag( &psz_subtitle, false ); if ( psz_tagname != NULL ) { if( !strcasecmp( psz_tagname, "br" ) ) { if ( !AppendCharacter( p_segment, '\n' ) ) { free( psz_tagname ); goto fail; } } else if( !strcasecmp( psz_tagname, "b" ) ) { p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); p_segment->style->i_style_flags |= STYLE_BOLD; p_segment->style->i_features |= STYLE_HAS_FLAGS; } else if( !strcasecmp( psz_tagname, "i" ) ) { p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); p_segment->style->i_style_flags |= STYLE_ITALIC; p_segment->style->i_features |= STYLE_HAS_FLAGS; } else if( !strcasecmp( psz_tagname, "u" ) ) { p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); p_segment->style->i_style_flags |= STYLE_UNDERLINE; p_segment->style->i_features |= STYLE_HAS_FLAGS; } else if( !strcasecmp( psz_tagname, "s" ) ) { p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); p_segment->style->i_style_flags |= STYLE_STRIKEOUT; p_segment->style->i_features |= STYLE_HAS_FLAGS; } else if( !strcasecmp( psz_tagname, "font" ) ) { p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); char* psz_attribute_name; char* psz_attribute_value; while( ( psz_attribute_name = ConsumeAttribute( &psz_subtitle, &psz_attribute_value ) ) ) { if ( !strcasecmp( psz_attribute_name, "face" ) ) { p_segment->style->psz_fontname = psz_attribute_value; // We don't want to free the attribute value since it has become our fontname psz_attribute_value = NULL; } else if ( !strcasecmp( psz_attribute_name, "family" ) ) { p_segment->style->psz_monofontname = psz_attribute_value; psz_attribute_value = NULL; } else if ( !strcasecmp( psz_attribute_name, "size" ) ) { int size = atoi( psz_attribute_value ); if( size ) { p_segment->style->i_font_size = size; p_segment->style->f_font_relsize = STYLE_DEFAULT_REL_FONT_SIZE * STYLE_DEFAULT_FONT_SIZE / p_segment->style->i_font_size; } } else if ( !strcasecmp( psz_attribute_name, "color" ) ) { p_segment->style->i_font_color = vlc_html_color( psz_attribute_value, NULL ); p_segment->style->i_features |= STYLE_HAS_FONT_COLOR; } else if ( !strcasecmp( psz_attribute_name, "outline-color" ) ) { p_segment->style->i_outline_color = vlc_html_color( psz_attribute_value, NULL ); p_segment->style->i_features |= STYLE_HAS_OUTLINE_COLOR; } else if ( !strcasecmp( psz_attribute_name, "shadow-color" ) ) { p_segment->style->i_shadow_color = vlc_html_color( psz_attribute_value, NULL ); p_segment->style->i_features |= STYLE_HAS_SHADOW_COLOR; } else if ( !strcasecmp( psz_attribute_name, "outline-level" ) ) { p_segment->style->i_outline_width = atoi( psz_attribute_value ); } else if ( !strcasecmp( psz_attribute_name, "shadow-level" ) ) { p_segment->style->i_shadow_width = atoi( psz_attribute_value ); } else if ( !strcasecmp( psz_attribute_name, "back-color" ) ) { p_segment->style->i_background_color = vlc_html_color( psz_attribute_value, NULL ); p_segment->style->i_features |= STYLE_HAS_BACKGROUND_COLOR; } else if ( !strcasecmp( psz_attribute_name, "alpha" ) ) { p_segment->style->i_font_alpha = atoi( psz_attribute_value ); p_segment->style->i_features |= STYLE_HAS_FONT_ALPHA; } free( psz_attribute_name ); free( psz_attribute_value ); } } else { // This is an unknown tag. We need to hide it if it's properly closed, and display it otherwise if ( !IsClosed( psz_subtitle, psz_tagname ) ) { AppendCharacter( p_segment, '<' ); AppendString( p_segment, psz_tagname ); AppendCharacter( p_segment, '>' ); } else { AppendTag( &p_tag_stack, psz_tagname ); // We don't want to free the tagname now, it will be freed when the tag // gets poped from the stack. psz_tagname = NULL; } // In any case, fall through and skip to the closing tag. } // Skip potential spaces & end tag while ( *psz_subtitle && *psz_subtitle != '>' ) psz_subtitle++; if ( *psz_subtitle == '>' ) psz_subtitle++; free( psz_tagname ); } else if( !strncmp( psz_subtitle, "</", 2 )) { char* psz_tagname = GetTag( &psz_subtitle, true ); if ( psz_tagname != NULL ) { if ( !strcasecmp( psz_tagname, "b" ) || !strcasecmp( psz_tagname, "i" ) || !strcasecmp( psz_tagname, "u" ) || !strcasecmp( psz_tagname, "s" ) || !strcasecmp( psz_tagname, "font" ) ) { // A closing tag for one of the tags we handle, meaning // we pushed a style onto the stack earlier p_segment = NewTextSegmentPopStyle( p_segment, &p_stack ); } else { // Unknown closing tag. If it is closing an unknown tag, ignore it. Otherwise, display it if ( !HasTag( &p_tag_stack, psz_tagname ) ) { AppendString( p_segment, "</" ); AppendString( p_segment, psz_tagname ); AppendCharacter( p_segment, '>' ); } } while ( *psz_subtitle == ' ' ) psz_subtitle++; if ( *psz_subtitle == '>' ) psz_subtitle++; free( psz_tagname ); } } else { /* We have an unknown tag, just append it, and move on. * The rest of the string won't be recognized as a tag, and * we will ignore unknown closing tag */ AppendCharacter( p_segment, '<' ); psz_subtitle++; } } /* SSA extensions */ else if( psz_subtitle[0] == '{' && psz_subtitle[1] == '\\' && strchr( psz_subtitle, '}' ) ) { /* Check for forced alignment */ if( !b_has_align && !strncmp( psz_subtitle, "{\\an", 4 ) && psz_subtitle[4] >= '1' && psz_subtitle[4] <= '9' && psz_subtitle[5] == '}' ) { static const int pi_vertical[3] = { SUBPICTURE_ALIGN_BOTTOM, 0, SUBPICTURE_ALIGN_TOP }; static const int pi_horizontal[3] = { SUBPICTURE_ALIGN_LEFT, 0, SUBPICTURE_ALIGN_RIGHT }; const int i_id = psz_subtitle[4] - '1'; b_has_align = true; *pi_align = pi_vertical[i_id/3] | pi_horizontal[i_id%3]; } /* TODO fr -> rotation */ /* Hide {\stupidity} */ psz_subtitle = strchr( psz_subtitle, '}' ) + 1; } /* MicroDVD extensions */ /* FIXME: * - Currently, we don't do difference between X and x, and we should: * Capital Letters applies to the whole text and not one line * - We don't support Position and Coordinates * - We don't support the DEFAULT flag (HEADER) */ else if( psz_subtitle[0] == '{' && psz_subtitle[2] == ':' && strchr( &psz_subtitle[2], '}' ) ) { const char *psz_tag_end = strchr( &psz_subtitle[2], '}' ); size_t i_len = psz_tag_end - &psz_subtitle[3]; if( psz_subtitle[1] == 'Y' || psz_subtitle[1] == 'y' ) { if( psz_subtitle[3] == 'i' ) { p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); p_segment->style->i_style_flags |= STYLE_ITALIC; p_segment->style->i_features |= STYLE_HAS_FLAGS; psz_subtitle++; } if( psz_subtitle[3] == 'b' ) { p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); p_segment->style->i_style_flags |= STYLE_BOLD; p_segment->style->i_features |= STYLE_HAS_FLAGS; psz_subtitle++; } if( psz_subtitle[3] == 'u' ) { p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); p_segment->style->i_style_flags |= STYLE_UNDERLINE; p_segment->style->i_features |= STYLE_HAS_FLAGS; psz_subtitle++; } } else if( (psz_subtitle[1] == 'C' || psz_subtitle[1] == 'c' ) && psz_subtitle[3] == '$' && i_len >= 7 ) { /* Yes, they use BBGGRR, instead of RRGGBB */ char psz_color[7]; psz_color[0] = psz_subtitle[8]; psz_color[1] = psz_subtitle[9]; psz_color[2] = psz_subtitle[6]; psz_color[3] = psz_subtitle[7]; psz_color[4] = psz_subtitle[4]; psz_color[5] = psz_subtitle[5]; psz_color[6] = '\0'; p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); p_segment->style->i_font_color = vlc_html_color( psz_color, NULL ); p_segment->style->i_features |= STYLE_HAS_FONT_COLOR; } else if( psz_subtitle[1] == 'F' || psz_subtitle[1] == 'f' ) { p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); p_segment->style->psz_fontname = strndup( &psz_subtitle[3], i_len ); } else if( psz_subtitle[1] == 'S' || psz_subtitle[1] == 's' ) { int size = atoi( &psz_subtitle[3] ); if( size ) { p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); p_segment->style->i_font_size = size; p_segment->style->f_font_relsize = STYLE_DEFAULT_REL_FONT_SIZE * STYLE_DEFAULT_FONT_SIZE / p_segment->style->i_font_size; } } /* Currently unsupported since we don't have access to the i_align flag here else if( psz_subtitle[1] == 'P' ) { if( psz_subtitle[3] == "1" ) i_align = SUBPICTURE_ALIGN_TOP; else if( psz_subtitle[3] == "0" ) i_align = SUBPICTURE_ALIGN_BOTTOM; } */ // Hide other {x:y} atrocities, notably {o:x} psz_subtitle = psz_tag_end + 1; } else { if( *psz_subtitle == '\n' || !strncasecmp( psz_subtitle, "\\n", 2 ) ) { if ( !AppendCharacter( p_segment, '\n' ) ) goto fail; if ( *psz_subtitle == '\n' ) psz_subtitle++; else psz_subtitle += 2; } else if( !strncasecmp( psz_subtitle, "\\h", 2 ) ) { if ( !AppendString( p_segment, "\xC2\xA0" ) ) goto fail; psz_subtitle += 2; } else { //FIXME: Highly inneficient AppendCharacter( p_segment, *psz_subtitle ); psz_subtitle++; } } } while ( p_stack ) PopStyle( &p_stack ); while ( p_tag_stack ) { tag_stack_t *p_tag = p_tag_stack; p_tag_stack = p_tag_stack->p_next; free( p_tag->psz_tagname ); free( p_tag ); } return p_first_segment; fail: text_segment_ChainDelete( p_first_segment ); return NULL; }