]> git.sesse.net Git - vlc/blob - modules/codec/substext.h
upnp: change item b_net and i_type
[vlc] / modules / codec / substext.h
1 #include <vlc_strings.h>
2
3 typedef struct
4 {
5     bool b_set;
6     unsigned int i_value;
7 } subpicture_updater_sys_option_t;
8
9 typedef struct segment_t segment_t;
10
11 typedef struct
12 {
13     uint8_t i_fontsize;
14     uint32_t i_color;   //ARGB
15     uint8_t i_flags;
16 } segment_style_t;
17
18 struct segment_t
19 {
20     char *psz_string;
21     unsigned int i_size;
22     segment_t *p_next;
23     /* styles applied to that segment */
24     segment_style_t styles;
25 };
26
27 struct subpicture_updater_sys_t {
28     char *text;
29     char *html;
30     segment_t *p_htmlsegments;
31
32     int  align;
33     int  x;
34     int  y;
35     int  i_font_height_percent;
36     int  i_font_height_abs_to_src;
37
38     bool is_fixed;
39     int  fixed_width;
40     int  fixed_height;
41     bool renderbg;
42
43     /* styling */
44     subpicture_updater_sys_option_t style_flags;
45     subpicture_updater_sys_option_t font_color;
46     subpicture_updater_sys_option_t background_color;
47     int16_t i_alpha;
48     int16_t i_drop_shadow;
49     int16_t i_drop_shadow_alpha;
50 };
51
52 static void SegmentFree( segment_t *p_segment )
53 {
54     if ( p_segment )
55     {
56         free( p_segment->psz_string );
57         free( p_segment );
58     }
59 }
60
61 static void MakeHtmlNewLines( char **ppsz_src )
62 {
63     unsigned int i_nlcount = 0;
64     unsigned i_len = strlen( *ppsz_src );
65     if ( i_len == 0 ) return;
66     for ( unsigned i=0; i<i_len; i++ )
67         if ( (*ppsz_src)[i] == '\n' )
68             i_nlcount++;
69     if ( !i_nlcount ) return;
70
71     char *psz_dst = malloc( i_len + 1 + (i_nlcount * 4) );
72     char *ptr = psz_dst;
73     for ( unsigned i=0; i<i_len; i++ )
74     {
75         if ( (*ppsz_src)[i] == '\n' )
76         {
77             strcpy( ptr, "<br/>" );
78             ptr += 5;
79         } else {
80             *ptr++ = (*ppsz_src)[i];
81         }
82     }
83     *ptr = 0;
84     free( *ppsz_src );
85     *ppsz_src = psz_dst;
86 }
87
88 static void HtmlAppend( char **ppsz_dst, const char *psz_src,
89                         const segment_style_t *p_styles, const float f_scale )
90 {
91     if ( !ppsz_dst ) return;
92     int i_return;
93     char *psz_subtext = NULL;
94     char *psz_text = NULL;
95     char *psz_fontsize = NULL;
96     char *psz_color = NULL;
97     char *psz_encoded = convert_xml_special_chars( psz_src );
98     if ( !psz_encoded ) return;
99
100     MakeHtmlNewLines( &psz_encoded );
101
102     if ( p_styles->i_color & 0xFF000000 ) //ARGB
103     {
104         i_return = asprintf( &psz_color, " color=\"#%6x\"",
105                              p_styles->i_color & 0x00FFFFFF );
106         if ( i_return < 0 ) psz_color = NULL;
107     }
108
109     if ( p_styles->i_fontsize > 0 && f_scale > 0 )
110     {
111         i_return = asprintf( &psz_fontsize, " size=\"%u\"",
112                              (unsigned) (f_scale * p_styles->i_fontsize) );
113         if ( i_return < 0 ) psz_fontsize = NULL;
114     }
115
116     i_return = asprintf( &psz_subtext, "%s%s%s%s%s%s%s",
117                         ( p_styles->i_flags & STYLE_UNDERLINE ) ? "<u>" : "",
118                         ( p_styles->i_flags & STYLE_BOLD ) ? "<b>" : "",
119                         ( p_styles->i_flags & STYLE_ITALIC ) ? "<i>" : "",
120                           psz_encoded,
121                         ( p_styles->i_flags & STYLE_ITALIC ) ? "</i>" : "",
122                         ( p_styles->i_flags & STYLE_BOLD ) ? "</b>" : "",
123                         ( p_styles->i_flags & STYLE_UNDERLINE ) ? "</u>" : ""
124                         );
125     if ( i_return < 0 ) psz_subtext = NULL;
126
127     if ( psz_color || psz_fontsize )
128     {
129         i_return = asprintf( &psz_text, "<font%s%s>%s</font>",
130                             psz_color ? psz_color : "",
131                             psz_fontsize ? psz_fontsize : "",
132                             psz_subtext );
133         if ( i_return < 0 ) psz_text = NULL;
134         free( psz_subtext );
135     }
136     else
137     {
138         psz_text = psz_subtext;
139     }
140
141     free( psz_fontsize );
142     free( psz_color );
143
144     if ( *ppsz_dst )
145     {
146         char *psz_dst = *ppsz_dst;
147         i_return = asprintf( ppsz_dst, "%s%s", psz_dst, psz_text );
148         if ( i_return < 0 ) ppsz_dst = NULL;
149         free( psz_dst );
150         free( psz_text );
151     }
152     else
153         *ppsz_dst = psz_text;
154 }
155
156 static char *SegmentsToHtml( segment_t *p_head, const float f_scale )
157 {
158     char *psz_dst = NULL;
159     char *psz_ret = NULL;
160     while( p_head )
161     {
162         HtmlAppend( &psz_dst, p_head->psz_string, &p_head->styles, f_scale );
163         p_head = p_head->p_next;
164     }
165     int i_ret = asprintf( &psz_ret, "<text>%s</text>", psz_dst );
166     if ( i_ret < 0 ) psz_ret = NULL;
167     free( psz_dst );
168     return psz_ret;
169 }
170
171 static int SubpictureTextValidate(subpicture_t *subpic,
172                                   bool has_src_changed, const video_format_t *fmt_src,
173                                   bool has_dst_changed, const video_format_t *fmt_dst,
174                                   mtime_t ts)
175 {
176     subpicture_updater_sys_t *sys = subpic->updater.p_sys;
177     VLC_UNUSED(fmt_src); VLC_UNUSED(fmt_dst); VLC_UNUSED(ts);
178
179     if (!has_src_changed && !has_dst_changed)
180         return VLC_SUCCESS;
181     if (!sys->is_fixed && subpic->b_absolute && subpic->p_region &&
182         subpic->i_original_picture_width > 0 &&
183         subpic->i_original_picture_height > 0) {
184
185         sys->is_fixed     = true;
186         sys->x            = subpic->p_region->i_x;
187         sys->y            = subpic->p_region->i_y;
188         sys->fixed_width  = subpic->i_original_picture_width;
189         sys->fixed_height = subpic->i_original_picture_height;
190     }
191     return VLC_EGENERIC;
192 }
193 static void SubpictureTextUpdate(subpicture_t *subpic,
194                                  const video_format_t *fmt_src,
195                                  const video_format_t *fmt_dst,
196                                  mtime_t ts)
197 {
198     subpicture_updater_sys_t *sys = subpic->updater.p_sys;
199     VLC_UNUSED(fmt_src); VLC_UNUSED(ts);
200
201     if (fmt_dst->i_sar_num <= 0 || fmt_dst->i_sar_den <= 0)
202         return;
203
204     subpic->i_original_picture_width  = fmt_dst->i_width * fmt_dst->i_sar_num / fmt_dst->i_sar_den;
205     subpic->i_original_picture_height = fmt_dst->i_height;
206
207     video_format_t fmt;
208     video_format_Init(&fmt, VLC_CODEC_TEXT);
209     fmt.i_sar_num = 1;
210     fmt.i_sar_den = 1;
211
212     subpicture_region_t *r = subpic->p_region = subpicture_region_New(&fmt);
213     if (!r)
214         return;
215
216     r->psz_text = sys->text ? strdup(sys->text) : NULL;
217     if ( sys->p_htmlsegments )
218         r->psz_html = SegmentsToHtml( sys->p_htmlsegments,
219                                       (float) fmt_dst->i_height / fmt_src->i_height );
220     else if ( sys->html )
221         r->psz_html = strdup(sys->html);
222     else
223         r->psz_html = NULL;
224     r->i_align  = sys->align;
225     r->b_renderbg = sys->renderbg;
226     if (!sys->is_fixed) {
227         const float margin_ratio = 0.04;
228         const int   margin_h     = margin_ratio * fmt_dst->i_visible_width;
229         const int   margin_v     = margin_ratio * fmt_dst->i_visible_height;
230
231         r->i_x = 0;
232         if (r->i_align & SUBPICTURE_ALIGN_LEFT)
233             r->i_x += margin_h + fmt_dst->i_x_offset;
234         else if (r->i_align & SUBPICTURE_ALIGN_RIGHT)
235             r->i_x += margin_h + fmt_dst->i_width - (fmt_dst->i_visible_width + fmt_dst->i_x_offset);
236
237         r->i_y = 0;
238         if (r->i_align & SUBPICTURE_ALIGN_TOP )
239             r->i_y += margin_v + fmt_dst->i_y_offset;
240         else if (r->i_align & SUBPICTURE_ALIGN_BOTTOM )
241             r->i_y += margin_v + fmt_dst->i_height - (fmt_dst->i_visible_height + fmt_dst->i_y_offset);
242     } else {
243         /* FIXME it doesn't adapt on crop settings changes */
244         r->i_x = sys->x * fmt_dst->i_width  / sys->fixed_width;
245         r->i_y = sys->y * fmt_dst->i_height / sys->fixed_height;
246     }
247
248     if (sys->i_font_height_percent || sys->i_alpha ||
249         sys->style_flags.b_set ||
250         sys->font_color.b_set ||
251         sys->background_color.b_set )
252     {
253         r->p_style = text_style_New();
254         if (!r->p_style) return;
255
256         if (sys->i_font_height_abs_to_src)
257             sys->i_font_height_percent = sys->i_font_height_abs_to_src * 100 /
258                                          fmt_src->i_visible_height;
259
260         if (sys->i_font_height_percent)
261         {
262             r->p_style->i_font_size = sys->i_font_height_percent *
263                                       subpic->i_original_picture_height / 100;
264             r->p_style->i_font_color = 0xffffff;
265             r->p_style->i_font_alpha = 0xff;
266         }
267
268         if (sys->style_flags.b_set)
269             r->p_style->i_style_flags = sys->style_flags.i_value;
270         if (sys->font_color.b_set)
271             r->p_style->i_font_color = sys->font_color.i_value;
272         if (sys->background_color.b_set)
273             r->p_style->i_background_color = sys->background_color.i_value;
274         if (sys->i_alpha)
275             r->p_style->i_font_alpha = sys->i_alpha;
276         if (sys->i_drop_shadow)
277             r->p_style->i_shadow_width = sys->i_drop_shadow;
278         if (sys->i_drop_shadow_alpha)
279             r->p_style->i_shadow_alpha = sys->i_drop_shadow_alpha;
280     }
281 }
282 static void SubpictureTextDestroy(subpicture_t *subpic)
283 {
284     subpicture_updater_sys_t *sys = subpic->updater.p_sys;
285
286     free(sys->text);
287     free(sys->html);
288     while( sys->p_htmlsegments )
289     {
290         segment_t *p_segment = sys->p_htmlsegments;
291         sys->p_htmlsegments = sys->p_htmlsegments->p_next;
292         SegmentFree( p_segment );
293     }
294     free(sys);
295 }
296
297 static inline subpicture_t *decoder_NewSubpictureText(decoder_t *decoder)
298 {
299     subpicture_updater_sys_t *sys = calloc(1, sizeof(*sys));
300     subpicture_updater_t updater = {
301         .pf_validate = SubpictureTextValidate,
302         .pf_update   = SubpictureTextUpdate,
303         .pf_destroy  = SubpictureTextDestroy,
304         .p_sys       = sys,
305     };
306     subpicture_t *subpic = decoder_NewSubpicture(decoder, &updater);
307     if (!subpic)
308         free(sys);
309     return subpic;
310 }