Qt: Change almost all icons
[vlc.git] / modules / codec / substext.h
1 /*****************************************************************************
2  * subsdec.c : text subtitle decoder
3  *****************************************************************************
4  * Copyright © 2011-2015 VLC authors and VideoLAN
5  *
6  * Authors: Laurent Aimer <fenrir@videolan.org>
7  *          Jean-Baptiste Kempf <jb@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #include <vlc_strings.h>
25 #include <vlc_text_style.h>
26
27 typedef struct subpicture_updater_sys_region_t subpicture_updater_sys_region_t;
28
29 enum subpicture_updater_sys_region_flags_e
30 {
31     UPDT_REGION_ORIGIN_X_IS_PERCENTILE = 1 << 0,
32     UPDT_REGION_ORIGIN_Y_IS_PERCENTILE = 1 << 1,
33     UPDT_REGION_EXTENT_X_IS_PERCENTILE = 1 << 2,
34     UPDT_REGION_EXTENT_Y_IS_PERCENTILE = 1 << 3,
35     UPDT_REGION_IGNORE_BACKGROUND      = 1 << 4,
36     UPDT_REGION_USES_GRID_COORDINATES  = 1 << 5,
37     UPDT_REGION_FIX_DONE               = 1 << 31,
38 };
39
40 #define EIA608_MARGIN  0.10
41 #define EIA608_VISIBLE 0.80
42 #define EIA608_ROWS 15
43
44 #define FONT_TO_LINE_HEIGHT_RATIO 1.06
45
46 struct subpicture_updater_sys_region_t
47 {
48     struct
49     {
50         int x;
51         int y;
52     } origin, extent;
53     /* store above percentile meanings as modifier flags */
54     int flags; /* subpicture_updater_sys_region_flags_e */
55     int align; /* alignment of the region itself */
56     int inner_align; /* alignment of content inside the region */
57     text_style_t *p_region_style;
58     text_segment_t *p_segments;
59     subpicture_updater_sys_region_t *p_next;
60 };
61
62 struct subpicture_updater_sys_t {
63
64     /* a min of one region */
65     subpicture_updater_sys_region_t region;
66
67     /* styling */
68     text_style_t *p_default_style; /* decoder (full or partial) defaults */
69 };
70
71 static inline void SubpictureUpdaterSysRegionClean(subpicture_updater_sys_region_t *p_updtregion)
72 {
73     text_segment_ChainDelete( p_updtregion->p_segments );
74     text_style_Delete( p_updtregion->p_region_style );
75 }
76
77 static inline void SubpictureUpdaterSysRegionInit(subpicture_updater_sys_region_t *p_updtregion)
78 {
79     memset(p_updtregion, 0, sizeof(*p_updtregion));
80 }
81
82 static inline subpicture_updater_sys_region_t *SubpictureUpdaterSysRegionNew( )
83 {
84     subpicture_updater_sys_region_t *p_region = malloc(sizeof(*p_region));
85     if(p_region)
86         SubpictureUpdaterSysRegionInit(p_region);
87     return p_region;
88 }
89
90 static inline void SubpictureUpdaterSysRegionAdd(subpicture_updater_sys_region_t *p_prev,
91                                                  subpicture_updater_sys_region_t *p_new)
92 {
93     subpicture_updater_sys_region_t **pp_next = &p_prev->p_next;
94     for(; *pp_next; pp_next = &(*pp_next)->p_next);
95     *pp_next = p_new;
96 }
97
98 static int SubpictureTextValidate(subpicture_t *subpic,
99                                   bool has_src_changed, const video_format_t *fmt_src,
100                                   bool has_dst_changed, const video_format_t *fmt_dst,
101                                   mtime_t ts)
102 {
103     subpicture_updater_sys_t *sys = subpic->updater.p_sys;
104     VLC_UNUSED(fmt_src); VLC_UNUSED(fmt_dst); VLC_UNUSED(ts);
105
106     if (!has_src_changed && !has_dst_changed)
107         return VLC_SUCCESS;
108
109     subpicture_updater_sys_region_t *p_updtregion = &sys->region;
110
111     if (!(p_updtregion->flags & UPDT_REGION_FIX_DONE) &&
112         subpic->b_absolute && subpic->p_region &&
113         subpic->i_original_picture_width > 0 &&
114         subpic->i_original_picture_height > 0)
115     {
116         p_updtregion->flags |= UPDT_REGION_FIX_DONE;
117         p_updtregion->origin.x = subpic->p_region->i_x;
118         p_updtregion->origin.y = subpic->p_region->i_y;
119         p_updtregion->extent.x = subpic->i_original_picture_width;
120         p_updtregion->extent.y = subpic->i_original_picture_height;
121     }
122
123     return VLC_EGENERIC;
124 }
125
126 static void SubpictureTextUpdate(subpicture_t *subpic,
127                                  const video_format_t *fmt_src,
128                                  const video_format_t *fmt_dst,
129                                  mtime_t ts)
130 {
131     subpicture_updater_sys_t *sys = subpic->updater.p_sys;
132     VLC_UNUSED(fmt_src); VLC_UNUSED(ts);
133
134     if (fmt_dst->i_sar_num <= 0 || fmt_dst->i_sar_den <= 0)
135         return;
136
137     video_format_t fmt;
138     video_format_Init(&fmt, VLC_CODEC_TEXT);
139
140     if( sys->region.flags & UPDT_REGION_USES_GRID_COORDINATES )
141     {
142         fmt.i_sar_num = 4;
143         fmt.i_sar_den = 3;
144         subpic->i_original_picture_width  = fmt_dst->i_visible_height * fmt.i_sar_num / fmt.i_sar_den;
145         subpic->i_original_picture_height = fmt_dst->i_visible_height;
146     }
147     else
148     {
149         subpic->i_original_picture_width  = fmt_dst->i_width * fmt_dst->i_sar_num / fmt_dst->i_sar_den;
150         subpic->i_original_picture_height = fmt_dst->i_height;
151         fmt.i_sar_num = 1;
152         fmt.i_sar_den = 1;
153     }
154
155     subpicture_region_t **pp_last_region = &subpic->p_region;
156
157     for( subpicture_updater_sys_region_t *p_updtregion = &sys->region;
158                                           p_updtregion; p_updtregion = p_updtregion->p_next )
159     {
160         subpicture_region_t *r = *pp_last_region = subpicture_region_New(&fmt);
161         if (!r)
162             return;
163         pp_last_region = &r->p_next;
164
165         r->p_text = text_segment_Copy( p_updtregion->p_segments );
166         r->i_align  = p_updtregion->inner_align | p_updtregion->align; /* we do not support text align by itself */
167         r->b_noregionbg = p_updtregion->flags & UPDT_REGION_IGNORE_BACKGROUND;
168         r->b_gridmode = p_updtregion->flags & UPDT_REGION_USES_GRID_COORDINATES;
169
170         if( r->b_gridmode )
171         {
172             /* Ensure correct flags are set */
173             r->i_align &= ~(SUBPICTURE_ALIGN_RIGHT | SUBPICTURE_ALIGN_BOTTOM);
174             r->i_align |= (SUBPICTURE_ALIGN_LEFT | SUBPICTURE_ALIGN_TOP);
175         }
176
177         if (!(p_updtregion->flags & UPDT_REGION_FIX_DONE))
178         {
179             const float margin_ratio = ( r->b_gridmode ) ? 0.10 : 0.04;
180             const int   margin_h     = margin_ratio * (( r->b_gridmode ) ? (unsigned) subpic->i_original_picture_width
181                                                                          : fmt_dst->i_visible_width );
182             const int   margin_v     = margin_ratio * fmt_dst->i_visible_height;
183
184             if (r->i_align & SUBPICTURE_ALIGN_LEFT)
185                 r->i_x = margin_h + fmt_dst->i_x_offset;
186             else if (r->i_align & SUBPICTURE_ALIGN_RIGHT)
187                 r->i_x = margin_h + fmt_dst->i_width - (fmt_dst->i_visible_width + fmt_dst->i_x_offset);
188
189             if (r->i_align & SUBPICTURE_ALIGN_TOP )
190                 r->i_y = margin_v + fmt_dst->i_y_offset;
191             else if (r->i_align & SUBPICTURE_ALIGN_BOTTOM )
192                 r->i_y = margin_v + fmt_dst->i_height - (fmt_dst->i_visible_height + fmt_dst->i_y_offset);
193
194             if( r->b_gridmode )
195             {
196                 r->i_y += p_updtregion->origin.y * /* row number */
197                          (EIA608_VISIBLE / EIA608_ROWS) *
198                          (fmt_dst->i_visible_height - r->i_y) * FONT_TO_LINE_HEIGHT_RATIO;
199             }
200
201         } else {
202             /* FIXME it doesn't adapt on crop settings changes */
203             r->i_x = p_updtregion->origin.x * fmt_dst->i_width  / p_updtregion->extent.x;
204             r->i_y = p_updtregion->origin.y * fmt_dst->i_height / p_updtregion->extent.y;
205         }
206
207         /* Add missing default style, if any, to all segments */
208         for ( text_segment_t* p_segment = r->p_text; p_segment; p_segment = p_segment->p_next )
209         {
210             /* Add decoder defaults */
211             if( p_segment->style )
212                 text_style_Merge( p_segment->style, sys->p_default_style, false );
213             else
214                 p_segment->style = text_style_Duplicate( sys->p_default_style );
215             /* Update all segments font sizes in pixels, *** metric used by renderers *** */
216             /* We only do this when a fixed font size isn't set */
217             if( r->b_gridmode )
218             {
219                 /* Ensure font size is correct for grid layout */
220                 p_segment->style->f_font_relsize = 0; /* Force to unset */
221                 p_segment->style->i_font_size = EIA608_VISIBLE * subpic->i_original_picture_height /
222                                                 EIA608_ROWS / FONT_TO_LINE_HEIGHT_RATIO;
223             }
224             else if( p_segment->style && p_segment->style->f_font_relsize && !p_segment->style->i_font_size )
225             {
226                 p_segment->style->i_font_size = p_segment->style->f_font_relsize *
227                         subpic->i_original_picture_height / 100;
228             }
229         }
230
231     }
232 }
233 static void SubpictureTextDestroy(subpicture_t *subpic)
234 {
235     subpicture_updater_sys_t *sys = subpic->updater.p_sys;
236
237     SubpictureUpdaterSysRegionClean( &sys->region );
238     subpicture_updater_sys_region_t *p_region = sys->region.p_next;
239     while( p_region )
240     {
241         subpicture_updater_sys_region_t *p_next = p_region->p_next;
242         SubpictureUpdaterSysRegionClean( p_region );
243         p_region = p_next;
244     }
245     text_style_Delete( sys->p_default_style );
246     free(sys);
247 }
248
249 static inline subpicture_t *decoder_NewSubpictureText(decoder_t *decoder)
250 {
251     subpicture_updater_sys_t *sys = calloc(1, sizeof(*sys));
252     subpicture_updater_t updater = {
253         .pf_validate = SubpictureTextValidate,
254         .pf_update   = SubpictureTextUpdate,
255         .pf_destroy  = SubpictureTextDestroy,
256         .p_sys       = sys,
257     };
258     SubpictureUpdaterSysRegionInit( &sys->region );
259     sys->p_default_style = text_style_Create( STYLE_NO_DEFAULTS );
260     if(unlikely(!sys->p_default_style))
261     {
262         free(sys);
263         return NULL;
264     }
265     subpicture_t *subpic = decoder_NewSubpicture(decoder, &updater);
266     if (!subpic)
267     {
268         text_style_Delete(sys->p_default_style);
269         free(sys);
270     }
271     return subpic;
272 }