Mercurial > mplayer.hg
annotate libmpcodecs/vf_ass.c @ 19938:f862045246a9
Aggregate multiple small EOSD textures into one large since hundreds of
texture creates are very slow.
Might cause artefacts with scaled OSD since large textures are not cleared each
time.
author | reimar |
---|---|
date | Fri, 22 Sep 2006 20:23:17 +0000 |
parents | 661d6c8a4adb |
children | fa122b7c71c6 |
rev | line source |
---|---|
18937 | 1 #include "config.h" |
2 | |
3 #include <stdio.h> | |
4 #include <stdlib.h> | |
5 #include <string.h> | |
6 #include <stdint.h> | |
7 #include <assert.h> | |
8 | |
9 #include "config.h" | |
10 #include "mp_msg.h" | |
11 #include "help_mp.h" | |
12 | |
13 #include "img_format.h" | |
14 #include "mp_image.h" | |
15 #include "vf.h" | |
16 | |
17 #include "libvo/fastmemcpy.h" | |
18 | |
19 #include "m_option.h" | |
20 #include "m_struct.h" | |
21 | |
22 #include "libass/ass.h" | |
23 #include "libass/ass_mp.h" | |
24 | |
25 #define _r(c) ((c)>>24) | |
26 #define _g(c) (((c)>>16)&0xFF) | |
27 #define _b(c) (((c)>>8)&0xFF) | |
28 #define _a(c) ((c)&0xFF) | |
29 #define rgba2y(c) ( (( 263*_r(c) + 516*_g(c) + 100*_b(c)) >> 10) + 16 ) | |
30 #define rgba2u(c) ( ((-152*_r(c) - 298*_g(c) + 450*_b(c)) >> 10) + 128 ) | |
31 #define rgba2v(c) ( (( 450*_r(c) - 376*_g(c) - 73*_b(c)) >> 10) + 128 ) | |
32 | |
33 | |
34 static struct vf_priv_s { | |
35 int outh, outw; | |
36 | |
37 unsigned int outfmt; | |
38 | |
39 // 1 = auto-added filter: insert only if chain does not support EOSD already | |
40 // 0 = insert always | |
41 int auto_insert; | |
42 | |
43 ass_instance_t* ass_priv; | |
44 | |
45 unsigned char* planes[3]; | |
46 unsigned char* dirty_rows; | |
47 } vf_priv_dflt; | |
48 | |
49 extern int opt_screen_size_x; | |
50 extern int opt_screen_size_y; | |
51 | |
52 extern ass_track_t* ass_track; | |
53 extern float sub_delay; | |
54 extern int sub_visibility; | |
55 | |
56 static int config(struct vf_instance_s* vf, | |
57 int width, int height, int d_width, int d_height, | |
58 unsigned int flags, unsigned int outfmt) | |
59 { | |
60 ass_settings_t settings; | |
61 | |
62 if (outfmt == IMGFMT_IF09) return 0; | |
63 | |
64 vf->priv->outh = height + ass_top_margin + ass_bottom_margin; | |
65 vf->priv->outw = width; | |
66 | |
67 if(!opt_screen_size_x && !opt_screen_size_y){ | |
68 d_width = d_width * vf->priv->outw / width; | |
69 d_height = d_height * vf->priv->outh / height; | |
70 } | |
71 | |
19065
26ed3fcd5cd4
drops casts from void * on malloc/calloc, leftover on libmpcodecs
reynaldo
parents:
18937
diff
changeset
|
72 vf->priv->planes[1] = malloc(vf->priv->outw * vf->priv->outh); |
26ed3fcd5cd4
drops casts from void * on malloc/calloc, leftover on libmpcodecs
reynaldo
parents:
18937
diff
changeset
|
73 vf->priv->planes[2] = malloc(vf->priv->outw * vf->priv->outh); |
26ed3fcd5cd4
drops casts from void * on malloc/calloc, leftover on libmpcodecs
reynaldo
parents:
18937
diff
changeset
|
74 vf->priv->dirty_rows = malloc(vf->priv->outh); |
18937 | 75 |
76 if (vf->priv->ass_priv) { | |
19550 | 77 memset(&settings, 0, sizeof(ass_settings_t)); |
18937 | 78 settings.frame_width = vf->priv->outw; |
79 settings.frame_height = vf->priv->outh; | |
80 settings.font_size_coeff = ass_font_scale; | |
81 settings.line_spacing = ass_line_spacing; | |
82 settings.top_margin = ass_top_margin; | |
83 settings.bottom_margin = ass_bottom_margin; | |
19563 | 84 settings.use_margins = ass_use_margins; |
18937 | 85 settings.aspect = ((double)d_width) / d_height; |
86 | |
87 ass_configure(vf->priv->ass_priv, &settings); | |
88 } | |
89 | |
90 return vf_next_config(vf, vf->priv->outw, vf->priv->outh, d_width, d_height, flags, outfmt); | |
91 } | |
92 | |
93 static void get_image(struct vf_instance_s* vf, mp_image_t *mpi) | |
94 { | |
95 if(mpi->type == MP_IMGTYPE_IPB) return; | |
96 if(mpi->flags & MP_IMGFLAG_PRESERVE) return; | |
97 if(mpi->imgfmt != vf->priv->outfmt) return; // colorspace differ | |
98 | |
99 // width never changes, always try full DR | |
100 mpi->priv = vf->dmpi = vf_get_image(vf->next, mpi->imgfmt, | |
101 mpi->type, mpi->flags, | |
102 vf->priv->outw, | |
103 vf->priv->outh); | |
104 | |
105 if((vf->dmpi->flags & MP_IMGFLAG_DRAW_CALLBACK) && | |
106 !(vf->dmpi->flags & MP_IMGFLAG_DIRECT)){ | |
107 mp_msg(MSGT_VFILTER, MSGL_INFO, MSGTR_MPCODECS_FullDRNotPossible); | |
108 return; | |
109 } | |
110 | |
111 // set up mpi as a cropped-down image of dmpi: | |
112 if(mpi->flags&MP_IMGFLAG_PLANAR){ | |
113 mpi->planes[0]=vf->dmpi->planes[0] + ass_top_margin * vf->dmpi->stride[0]; | |
114 mpi->planes[1]=vf->dmpi->planes[1] + (ass_top_margin >> mpi->chroma_y_shift) * vf->dmpi->stride[1]; | |
115 mpi->planes[2]=vf->dmpi->planes[2] + (ass_top_margin >> mpi->chroma_y_shift) * vf->dmpi->stride[2]; | |
116 mpi->stride[1]=vf->dmpi->stride[1]; | |
117 mpi->stride[2]=vf->dmpi->stride[2]; | |
118 } else { | |
119 mpi->planes[0]=vf->dmpi->planes[0] + ass_top_margin * vf->dmpi->stride[0]; | |
120 } | |
121 mpi->stride[0]=vf->dmpi->stride[0]; | |
122 mpi->width=vf->dmpi->width; | |
123 mpi->flags|=MP_IMGFLAG_DIRECT; | |
124 mpi->flags&=~MP_IMGFLAG_DRAW_CALLBACK; | |
125 // vf->dmpi->flags&=~MP_IMGFLAG_DRAW_CALLBACK; | |
126 } | |
127 | |
128 static void blank(mp_image_t *mpi, int y1, int y2) | |
129 { | |
130 int color[3] = {16, 128, 128}; // black (YUV) | |
131 int y; | |
132 unsigned char* dst; | |
133 int chroma_rows = (y2 - y1) >> mpi->chroma_y_shift; | |
134 | |
135 dst = mpi->planes[0] + y1 * mpi->stride[0]; | |
136 for (y = 0; y < y2 - y1; ++y) { | |
137 memset(dst, color[0], mpi->w); | |
138 dst += mpi->stride[0]; | |
139 } | |
140 dst = mpi->planes[1] + (y1 >> mpi->chroma_y_shift) * mpi->stride[1]; | |
141 for (y = 0; y < chroma_rows ; ++y) { | |
142 memset(dst, color[1], mpi->chroma_width); | |
143 dst += mpi->stride[1]; | |
144 } | |
145 dst = mpi->planes[2] + (y1 >> mpi->chroma_y_shift) * mpi->stride[2]; | |
146 for (y = 0; y < chroma_rows ; ++y) { | |
147 memset(dst, color[2], mpi->chroma_width); | |
148 dst += mpi->stride[2]; | |
149 } | |
150 } | |
151 | |
152 static int prepare_image(struct vf_instance_s* vf, mp_image_t *mpi) | |
153 { | |
154 if(mpi->flags&MP_IMGFLAG_DIRECT || mpi->flags&MP_IMGFLAG_DRAW_CALLBACK){ | |
155 vf->dmpi = mpi->priv; | |
156 if (!vf->dmpi) { mp_msg(MSGT_VFILTER, MSGL_WARN, MSGTR_MPCODECS_FunWhydowegetNULL); return 0; } | |
157 mpi->priv = NULL; | |
158 // we've used DR, so we're ready... | |
159 if (ass_top_margin) | |
160 blank(vf->dmpi, 0, ass_top_margin); | |
161 if (ass_bottom_margin) | |
162 blank(vf->dmpi, vf->priv->outh - ass_bottom_margin, vf->priv->outh); | |
163 if(!(mpi->flags&MP_IMGFLAG_PLANAR)) | |
164 vf->dmpi->planes[1] = mpi->planes[1]; // passthrough rgb8 palette | |
165 return 0; | |
166 } | |
167 | |
168 // hope we'll get DR buffer: | |
169 vf->dmpi = vf_get_image(vf->next, vf->priv->outfmt, | |
170 MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE, | |
171 vf->priv->outw, vf->priv->outh); | |
172 | |
173 // copy mpi->dmpi... | |
174 if(mpi->flags&MP_IMGFLAG_PLANAR){ | |
175 memcpy_pic(vf->dmpi->planes[0] + ass_top_margin * vf->dmpi->stride[0], | |
176 mpi->planes[0], mpi->w, mpi->h, | |
177 vf->dmpi->stride[0], mpi->stride[0]); | |
178 memcpy_pic(vf->dmpi->planes[1] + (ass_top_margin >> mpi->chroma_y_shift) * vf->dmpi->stride[1], | |
179 mpi->planes[1], mpi->w >> mpi->chroma_x_shift, mpi->h >> mpi->chroma_y_shift, | |
180 vf->dmpi->stride[1], mpi->stride[1]); | |
181 memcpy_pic(vf->dmpi->planes[2] + (ass_top_margin >> mpi->chroma_y_shift) * vf->dmpi->stride[2], | |
182 mpi->planes[2], mpi->w >> mpi->chroma_x_shift, mpi->h >> mpi->chroma_y_shift, | |
183 vf->dmpi->stride[2], mpi->stride[2]); | |
184 } else { | |
185 memcpy_pic(vf->dmpi->planes[0] + ass_top_margin * vf->dmpi->stride[0], | |
186 mpi->planes[0], mpi->w*(vf->dmpi->bpp/8), mpi->h, | |
187 vf->dmpi->stride[0], mpi->stride[0]); | |
188 vf->dmpi->planes[1] = mpi->planes[1]; // passthrough rgb8 palette | |
189 } | |
190 if (ass_top_margin) | |
191 blank(vf->dmpi, 0, ass_top_margin); | |
192 if (ass_bottom_margin) | |
193 blank(vf->dmpi, vf->priv->outh - ass_bottom_margin, vf->priv->outh); | |
194 return 0; | |
195 } | |
196 | |
197 /** | |
198 * \brief Copy specified rows from render_context.dmpi to render_context.planes, upsampling to 4:4:4 | |
199 */ | |
200 static void copy_from_image(struct vf_instance_s* vf, int first_row, int last_row) | |
201 { | |
202 int pl; | |
203 int i, j, k; | |
204 unsigned char val; | |
205 int chroma_rows; | |
206 | |
207 first_row -= (first_row % 2); | |
208 last_row += (last_row % 2); | |
209 chroma_rows = (last_row - first_row) / 2; | |
210 | |
211 for (pl = 1; pl < 3; ++pl) { | |
212 int dst_stride = vf->dmpi->stride[pl] * 2; | |
213 int src_stride = vf->dmpi->stride[pl]; | |
214 | |
215 unsigned char* src = vf->dmpi->planes[pl] + (first_row/2) * src_stride; | |
216 unsigned char* dst = vf->priv->planes[pl] + first_row * dst_stride; | |
217 unsigned char* dst_next = dst + dst_stride; | |
218 for(i = 0; i < chroma_rows; ++i) | |
219 { | |
220 if ((vf->priv->dirty_rows[first_row + i*2] == 0) || | |
221 (vf->priv->dirty_rows[first_row + i*2 + 1] == 0)) { | |
222 for (j = 0, k = 0; j < vf->dmpi->chroma_width; ++j, k+=2) { | |
223 val = *(src + j); | |
224 *(dst + k) = val; | |
225 *(dst + k + 1) = val; | |
226 *(dst_next + k) = val; | |
227 *(dst_next + k + 1) = val; | |
228 } | |
229 } | |
230 src += src_stride; | |
231 dst = dst_next + dst_stride; | |
232 dst_next = dst + dst_stride; | |
233 } | |
234 } | |
235 for (i = first_row; i < last_row; ++i) | |
236 vf->priv->dirty_rows[i] = 1; | |
237 } | |
238 | |
239 /** | |
240 * \brief Copy all previously copied rows back to render_context.dmpi | |
241 */ | |
242 static void copy_to_image(struct vf_instance_s* vf) | |
243 { | |
244 int pl; | |
245 int i, j, k; | |
246 for (pl = 1; pl < 3; ++pl) { | |
247 int dst_stride = vf->dmpi->stride[pl]; | |
248 int src_stride = vf->dmpi->stride[pl] * 2; | |
249 | |
250 unsigned char* dst = vf->dmpi->planes[pl]; | |
251 unsigned char* src = vf->priv->planes[pl]; | |
252 unsigned char* src_next = vf->priv->planes[pl] + src_stride; | |
253 for(i = 0; i < vf->dmpi->chroma_height; ++i) | |
254 { | |
255 if ((vf->priv->dirty_rows[i*2] == 1)) { | |
256 assert(vf->priv->dirty_rows[i*2 + 1] == 1); | |
257 for (j = 0, k = 0; j < vf->dmpi->chroma_width; ++j, k+=2) { | |
258 unsigned val = 0; | |
259 val += *(src + k); | |
260 val += *(src + k + 1); | |
261 val += *(src_next + k); | |
262 val += *(src_next + k + 1); | |
263 *(dst + j) = val >> 2; | |
264 } | |
265 } | |
266 dst += dst_stride; | |
267 src = src_next + src_stride; | |
268 src_next = src + src_stride; | |
269 } | |
270 } | |
271 } | |
272 | |
273 static void my_draw_bitmap(struct vf_instance_s* vf, unsigned char* bitmap, int bitmap_w, int bitmap_h, int stride, int dst_x, int dst_y, unsigned color) | |
274 { | |
275 unsigned char y = rgba2y(color); | |
276 unsigned char u = rgba2u(color); | |
277 unsigned char v = rgba2v(color); | |
278 unsigned char opacity = 255 - _a(color); | |
279 unsigned char* src; | |
280 unsigned char* dst; | |
281 int i, j; | |
282 mp_image_t* dmpi = vf->dmpi; | |
283 | |
284 | |
285 src = bitmap; | |
286 dst = dmpi->planes[0] + dst_x + dst_y * dmpi->stride[0]; | |
287 for(i = 0; i < bitmap_h; ++i) | |
288 { | |
289 for (j = 0; j < bitmap_w; ++j) { | |
290 // unsigned k = *(src+j); | |
291 unsigned k = ((unsigned)*(bitmap + stride * i + j)) * opacity / 255; | |
292 unsigned char orig_color = *(dst+j); | |
293 *(dst+j) = (k*y + (255-k)*orig_color) / 255; | |
294 } | |
295 src += stride; | |
296 dst += dmpi->stride[0]; | |
297 } | |
298 | |
299 for (i = 0; i < bitmap_h; ++i) { | |
300 for (j = 0; j < bitmap_w; ++j) { | |
301 int x = dst_x + j; | |
302 int y = dst_y + i; | |
303 unsigned k; | |
304 unsigned char orig_u, orig_v; | |
305 unsigned char new_u, new_v; | |
306 | |
307 k = ((unsigned)*(bitmap + stride * i + j)) * opacity / 255; | |
308 | |
309 orig_u = *(vf->priv->planes[1] + x + y * 2 * dmpi->chroma_width); | |
310 new_u = (k*u + (255-k)*orig_u) / 255; | |
311 *(vf->priv->planes[1] + x + y * 2 * dmpi->chroma_width) = new_u; | |
312 | |
313 orig_v = *(vf->priv->planes[2] + x + y * 2 * dmpi->chroma_width); | |
314 new_v = (k*v + (255-k)*orig_v) / 255; | |
315 *(vf->priv->planes[2] + x + y * 2 * dmpi->chroma_width) = new_v; | |
316 } | |
317 } | |
318 } | |
319 | |
320 static int render_frame(struct vf_instance_s* vf, mp_image_t *mpi, const ass_image_t* img) | |
321 { | |
322 if (img) { | |
323 memset(vf->priv->dirty_rows, 0, vf->priv->outh); // reset dirty rows | |
324 while (img) { | |
325 copy_from_image(vf, img->dst_y, img->dst_y + img->h); | |
326 my_draw_bitmap(vf, img->bitmap, img->w, img->h, img->stride, | |
327 img->dst_x, img->dst_y, img->color); | |
328 img = img->next; | |
329 } | |
330 copy_to_image(vf); | |
331 } | |
332 return 0; | |
333 } | |
334 | |
335 static int put_image(struct vf_instance_s* vf, mp_image_t *mpi, double pts) | |
336 { | |
337 ass_image_t* images = 0; | |
338 if (sub_visibility && vf->priv->ass_priv && ass_track && (pts != MP_NOPTS_VALUE)) | |
339 images = ass_render_frame(vf->priv->ass_priv, ass_track, (pts+sub_delay) * 1000 + .5); | |
340 | |
341 prepare_image(vf, mpi); | |
342 if (images) render_frame(vf, mpi, images); | |
343 | |
344 return vf_next_put_image(vf, vf->dmpi, pts); | |
345 } | |
346 | |
347 static int query_format(struct vf_instance_s* vf, unsigned int fmt) | |
348 { | |
349 switch(fmt){ | |
350 case IMGFMT_YV12: | |
351 case IMGFMT_I420: | |
352 case IMGFMT_IYUV: | |
353 return vf_next_query_format(vf, vf->priv->outfmt); | |
354 } | |
355 return 0; | |
356 } | |
357 | |
358 static int control(vf_instance_t *vf, int request, void *data) | |
359 { | |
19521 | 360 switch (request) { |
361 case VFCTRL_INIT_EOSD: | |
18937 | 362 vf->priv->ass_priv = ass_init(); |
19517 | 363 return vf->priv->ass_priv ? CONTROL_TRUE : CONTROL_FALSE; |
19521 | 364 case VFCTRL_DRAW_EOSD: |
365 if (vf->priv->ass_priv) return CONTROL_TRUE; | |
366 break; | |
18937 | 367 } |
368 return vf_next_control(vf, request, data); | |
369 } | |
370 | |
371 static void uninit(struct vf_instance_s* vf) | |
372 { | |
373 if (vf->priv->ass_priv) | |
374 ass_done(vf->priv->ass_priv); | |
375 if (vf->priv->planes[1]) | |
376 free(vf->priv->planes[1]); | |
377 if (vf->priv->planes[2]) | |
378 free(vf->priv->planes[2]); | |
379 if (vf->priv->dirty_rows) | |
380 free(vf->priv->dirty_rows); | |
381 } | |
382 | |
383 static unsigned int fmt_list[]={ | |
384 IMGFMT_YV12, | |
385 IMGFMT_I420, | |
386 IMGFMT_IYUV, | |
387 0 | |
388 }; | |
389 | |
390 static int open(vf_instance_t *vf, char* args) | |
391 { | |
392 int flags; | |
393 vf->priv->outfmt = vf_match_csp(&vf->next,fmt_list,IMGFMT_YV12); | |
394 if (vf->priv->outfmt) | |
395 flags = vf_next_query_format(vf, vf->priv->outfmt); | |
396 if (!vf->priv->outfmt || (vf->priv->auto_insert && flags&VFCAP_EOSD)) | |
397 { | |
398 uninit(vf); | |
399 return 0; | |
400 } | |
401 | |
402 if (vf->priv->auto_insert) | |
403 mp_msg(MSGT_VFILTER, MSGL_INFO, "[ass] auto-open\n"); | |
404 | |
405 vf->config = config; | |
406 vf->query_format = query_format; | |
407 vf->uninit = uninit; | |
408 vf->control = control; | |
409 vf->get_image = get_image; | |
410 vf->put_image = put_image; | |
411 vf->default_caps=VFCAP_EOSD; | |
412 return 1; | |
413 } | |
414 | |
415 #define ST_OFF(f) M_ST_OFF(struct vf_priv_s,f) | |
416 static m_option_t vf_opts_fields[] = { | |
417 {"auto", ST_OFF(auto_insert), CONF_TYPE_FLAG, 0 , 0, 1, NULL}, | |
418 { NULL, NULL, 0, 0, 0, 0, NULL } | |
419 }; | |
420 | |
421 static m_struct_t vf_opts = { | |
422 "ass", | |
423 sizeof(struct vf_priv_s), | |
424 &vf_priv_dflt, | |
425 vf_opts_fields | |
426 }; | |
427 | |
428 vf_info_t vf_info_ass = { | |
429 "Render ASS/SSA subtitles", | |
430 "ass", | |
431 "Evgeniy Stepanov", | |
432 "", | |
433 open, | |
434 &vf_opts | |
435 }; | |
436 |