Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com> |
3 | | * |
4 | | * This file is part of mpv. |
5 | | * |
6 | | * mpv is free software; you can redistribute it and/or |
7 | | * modify it under the terms of the GNU Lesser General Public |
8 | | * License as published by the Free Software Foundation; either |
9 | | * version 2.1 of the License, or (at your option) any later version. |
10 | | * |
11 | | * mpv is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | * GNU Lesser General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU Lesser General Public |
17 | | * License along with mpv. If not, see <http://www.gnu.org/licenses/>. |
18 | | */ |
19 | | |
20 | | #include <string.h> |
21 | | #include <stdarg.h> |
22 | | #include <stdbool.h> |
23 | | #include <assert.h> |
24 | | |
25 | | #include "config.h" |
26 | | |
27 | | #include <ass/ass.h> |
28 | | #include <ass/ass_types.h> |
29 | | #if HAVE_SUBRANDR |
30 | | #include <subrandr/subrandr.h> |
31 | | #endif |
32 | | |
33 | | #include "common/common.h" |
34 | | #include "packer.h" |
35 | | #include "img_convert.h" |
36 | | #include "osd.h" |
37 | | #include "video/out/bitmap_packer.h" |
38 | | #include "video/mp_image.h" |
39 | | |
40 | | struct mp_sub_packer { |
41 | | struct sub_bitmap *cached_parts; // only for the array memory |
42 | | struct sub_bitmap *cached_subrandr_images; |
43 | | struct mp_image *cached_img; |
44 | | struct sub_bitmaps cached_subs; |
45 | | bool cached_subs_valid; |
46 | | struct sub_bitmap rgba_imgs[MP_SUB_BB_LIST_MAX]; |
47 | | struct bitmap_packer *packer; |
48 | | }; |
49 | | |
50 | | // Free with talloc_free(). |
51 | | struct mp_sub_packer *mp_sub_packer_alloc(void *ta_parent) |
52 | 1.60k | { |
53 | 1.60k | struct mp_sub_packer *p = talloc_zero(ta_parent, struct mp_sub_packer); |
54 | 1.60k | p->packer = talloc_zero(p, struct bitmap_packer); |
55 | 1.60k | p->packer->padding = 1; // assume bilinear sampling |
56 | 1.60k | return p; |
57 | 1.60k | } |
58 | | |
59 | | static bool pack(struct mp_sub_packer *p, struct sub_bitmaps *res, int imgfmt) |
60 | 399 | { |
61 | 399 | packer_set_size(p->packer, res->num_parts); |
62 | | |
63 | 874 | for (int n = 0; n < res->num_parts; n++) |
64 | 475 | p->packer->in[n] = (struct pos){res->parts[n].w, res->parts[n].h}; |
65 | | |
66 | 399 | if (p->packer->count == 0 || packer_pack(p->packer) < 0) |
67 | 327 | return false; |
68 | | |
69 | 72 | struct pos bb[2]; |
70 | 72 | packer_get_bb(p->packer, bb); |
71 | | |
72 | 72 | res->packed_w = bb[1].x; |
73 | 72 | res->packed_h = bb[1].y; |
74 | | |
75 | 72 | if (!p->cached_img || p->cached_img->w < res->packed_w || |
76 | 0 | p->cached_img->h < res->packed_h || |
77 | 0 | p->cached_img->imgfmt != imgfmt) |
78 | 72 | { |
79 | 72 | talloc_free(p->cached_img); |
80 | 72 | p->cached_img = mp_image_alloc(imgfmt, p->packer->w, p->packer->h); |
81 | 72 | if (!p->cached_img) { |
82 | 0 | packer_reset(p->packer); |
83 | 0 | return false; |
84 | 0 | } |
85 | 72 | talloc_steal(p, p->cached_img); |
86 | 72 | } |
87 | | |
88 | 72 | if (!mp_image_make_writeable(p->cached_img)) { |
89 | 0 | packer_reset(p->packer); |
90 | 0 | return false; |
91 | 0 | } |
92 | | |
93 | 72 | res->packed = p->cached_img; |
94 | | |
95 | 547 | for (int n = 0; n < res->num_parts; n++) { |
96 | 475 | struct sub_bitmap *b = &res->parts[n]; |
97 | 475 | struct pos pos = p->packer->result[n]; |
98 | | |
99 | 475 | b->src_x = pos.x; |
100 | 475 | b->src_y = pos.y; |
101 | 475 | } |
102 | | |
103 | 72 | return true; |
104 | 72 | } |
105 | | |
106 | | static void fill_padding_1(uint8_t *base, int w, int h, int stride, int padding) |
107 | 475 | { |
108 | 16.6k | for (int row = 0; row < h; ++row) { |
109 | 16.1k | uint8_t *row_ptr = base + row * stride; |
110 | 16.1k | uint8_t left_pixel = row_ptr[0]; |
111 | 16.1k | uint8_t right_pixel = row_ptr[w - 1]; |
112 | | |
113 | 32.2k | for (int i = 1; i <= padding; ++i) |
114 | 16.1k | row_ptr[-i] = left_pixel; |
115 | | |
116 | 32.2k | for (int i = 0; i < padding; ++i) |
117 | 16.1k | row_ptr[w + i] = right_pixel; |
118 | 16.1k | } |
119 | | |
120 | 475 | int row_bytes = (w + 2 * padding); |
121 | 475 | uint8_t *top_row = base - padding; |
122 | 950 | for (int i = 1; i <= padding; ++i) |
123 | 475 | memcpy(base - i * stride - padding, top_row, row_bytes); |
124 | | |
125 | 475 | uint8_t *last_row = base + (h - 1) * stride - padding; |
126 | 950 | for (int i = 0; i < padding; ++i) |
127 | 475 | memcpy(base + (h + i) * stride - padding, last_row, row_bytes); |
128 | 475 | } |
129 | | |
130 | | static void fill_padding_4(uint8_t *base, int w, int h, int stride, int padding) |
131 | 0 | { |
132 | 0 | for (int row = 0; row < h; ++row) { |
133 | 0 | uint32_t *row_ptr = (uint32_t *)(base + row * stride); |
134 | 0 | uint32_t left_pixel = row_ptr[0]; |
135 | 0 | uint32_t right_pixel = row_ptr[w - 1]; |
136 | |
|
137 | 0 | for (int i = 1; i <= padding; ++i) |
138 | 0 | row_ptr[-i] = left_pixel; |
139 | |
|
140 | 0 | for (int i = 0; i < padding; ++i) |
141 | 0 | row_ptr[w + i] = right_pixel; |
142 | 0 | } |
143 | |
|
144 | 0 | int row_bytes = (w + 2 * padding) * 4; |
145 | 0 | uint8_t *top_row = base - padding * 4; |
146 | 0 | for (int i = 1; i <= padding; ++i) |
147 | 0 | memcpy(base - i * stride - padding * 4, top_row, row_bytes); |
148 | |
|
149 | 0 | uint8_t *last_row = base + (h - 1) * stride - padding * 4; |
150 | 0 | for (int i = 0; i < padding; ++i) |
151 | 0 | memcpy(base + (h + i) * stride - padding * 4, last_row, row_bytes); |
152 | 0 | } |
153 | | |
154 | | static void draw_ass_rgba(unsigned char *src, int src_w, int src_h, |
155 | | int src_stride, unsigned char *dst, size_t dst_stride, |
156 | | int dst_x, int dst_y, uint32_t color) |
157 | 0 | { |
158 | 0 | const unsigned int r = (color >> 24) & 0xff; |
159 | 0 | const unsigned int g = (color >> 16) & 0xff; |
160 | 0 | const unsigned int b = (color >> 8) & 0xff; |
161 | 0 | const unsigned int a = 0xff - (color & 0xff); |
162 | |
|
163 | 0 | dst += dst_y * dst_stride + dst_x * 4; |
164 | |
|
165 | 0 | for (int y = 0; y < src_h; y++, dst += dst_stride, src += src_stride) { |
166 | 0 | uint32_t *dstrow = (uint32_t *) dst; |
167 | 0 | for (int x = 0; x < src_w; x++) { |
168 | 0 | const unsigned int v = src[x]; |
169 | 0 | int rr = (r * a * v); |
170 | 0 | int gg = (g * a * v); |
171 | 0 | int bb = (b * a * v); |
172 | 0 | int aa = a * v; |
173 | 0 | uint32_t dstpix = dstrow[x]; |
174 | 0 | unsigned int dstb = dstpix & 0xFF; |
175 | 0 | unsigned int dstg = (dstpix >> 8) & 0xFF; |
176 | 0 | unsigned int dstr = (dstpix >> 16) & 0xFF; |
177 | 0 | unsigned int dsta = (dstpix >> 24) & 0xFF; |
178 | 0 | dstb = (bb + dstb * (255 * 255 - aa)) / (255 * 255); |
179 | 0 | dstg = (gg + dstg * (255 * 255 - aa)) / (255 * 255); |
180 | 0 | dstr = (rr + dstr * (255 * 255 - aa)) / (255 * 255); |
181 | 0 | dsta = (aa * 255 + dsta * (255 * 255 - aa)) / (255 * 255); |
182 | 0 | dstrow[x] = dstb | (dstg << 8) | (dstr << 16) | (dsta << 24); |
183 | 0 | } |
184 | 0 | } |
185 | 0 | } |
186 | | |
187 | | static bool pack_libass(struct mp_sub_packer *p, struct sub_bitmaps *res) |
188 | 399 | { |
189 | 399 | if (!pack(p, res, IMGFMT_Y8)) |
190 | 327 | return false; |
191 | | |
192 | 72 | int padding = p->packer->padding; |
193 | 72 | uint8_t *base = res->packed->planes[0]; |
194 | 72 | int stride = res->packed->stride[0]; |
195 | | |
196 | 547 | for (int n = 0; n < res->num_parts; n++) { |
197 | 475 | struct sub_bitmap *b = &res->parts[n]; |
198 | 475 | void *pdata = base + b->src_y * stride + b->src_x; |
199 | 475 | memcpy_pic(pdata, b->bitmap, b->w, b->h, stride, b->stride); |
200 | 475 | fill_padding_1(pdata, b->w, b->h, stride, padding); |
201 | | |
202 | 475 | b->bitmap = pdata; |
203 | 475 | b->stride = stride; |
204 | 475 | } |
205 | | |
206 | 72 | return true; |
207 | 399 | } |
208 | | |
209 | | static bool pack_rgba(struct mp_sub_packer *p, struct sub_bitmaps *res) |
210 | 0 | { |
211 | 0 | struct mp_rect bb_list[MP_SUB_BB_LIST_MAX]; |
212 | 0 | int num_bb = mp_get_sub_bb_list(res, bb_list, MP_SUB_BB_LIST_MAX); |
213 | |
|
214 | 0 | struct sub_bitmaps imgs = { |
215 | 0 | .change_id = res->change_id, |
216 | 0 | .format = SUBBITMAP_BGRA, |
217 | 0 | .parts = p->rgba_imgs, |
218 | 0 | .num_parts = num_bb, |
219 | 0 | }; |
220 | |
|
221 | 0 | for (int n = 0; n < imgs.num_parts; n++) { |
222 | 0 | imgs.parts[n].w = bb_list[n].x1 - bb_list[n].x0; |
223 | 0 | imgs.parts[n].h = bb_list[n].y1 - bb_list[n].y0; |
224 | 0 | } |
225 | |
|
226 | 0 | if (!pack(p, &imgs, IMGFMT_BGRA)) |
227 | 0 | return false; |
228 | | |
229 | 0 | int padding = p->packer->padding; |
230 | 0 | uint8_t *base = imgs.packed->planes[0]; |
231 | 0 | int stride = imgs.packed->stride[0]; |
232 | |
|
233 | 0 | for (int n = 0; n < num_bb; n++) { |
234 | 0 | struct mp_rect bb = bb_list[n]; |
235 | 0 | struct sub_bitmap *b = &imgs.parts[n]; |
236 | |
|
237 | 0 | b->x = bb.x0; |
238 | 0 | b->y = bb.y0; |
239 | 0 | b->w = b->dw = mp_rect_w(bb); |
240 | 0 | b->h = b->dh = mp_rect_h(bb); |
241 | 0 | b->stride = stride; |
242 | 0 | b->bitmap = base + b->stride * b->src_y + b->src_x * 4; |
243 | 0 | memset_pic(b->bitmap, 0, b->w * 4, b->h, b->stride); |
244 | |
|
245 | 0 | for (int i = 0; i < res->num_parts; i++) { |
246 | 0 | struct sub_bitmap *s = &res->parts[i]; |
247 | | |
248 | | // Assume mp_get_sub_bb_list() never splits sub bitmaps |
249 | | // So we don't clip/adjust the size of the sub bitmap |
250 | 0 | if (s->x >= b->x + b->w || s->x + s->w <= b->x || |
251 | 0 | s->y >= b->y + b->h || s->y + s->h <= b->y) |
252 | 0 | continue; |
253 | | |
254 | 0 | draw_ass_rgba(s->bitmap, s->w, s->h, s->stride, |
255 | 0 | b->bitmap, b->stride, |
256 | 0 | s->x - b->x, s->y - b->y, |
257 | 0 | s->libass.color); |
258 | 0 | } |
259 | 0 | fill_padding_4(b->bitmap, b->w, b->h, b->stride, padding); |
260 | 0 | } |
261 | |
|
262 | 0 | *res = imgs; |
263 | 0 | return true; |
264 | 0 | } |
265 | | |
266 | | // Pack the contents of image_lists[0] to image_lists[num_image_lists-1] into |
267 | | // a single image, and make *out point to it. *out is completely overwritten. |
268 | | // If libass reported any change, image_lists_changed must be set (it then |
269 | | // repacks all images). preferred_osd_format can be set to a desired |
270 | | // sub_bitmap_format. Currently, only SUBBITMAP_LIBASS is supported. |
271 | | void mp_sub_packer_pack_ass(struct mp_sub_packer *p, ASS_Image **image_lists, |
272 | | int num_image_lists, bool image_lists_changed, bool video_color_space, |
273 | | int preferred_osd_format, struct sub_bitmaps *out) |
274 | 470 | { |
275 | 470 | int format = preferred_osd_format == SUBBITMAP_BGRA ? SUBBITMAP_BGRA |
276 | 470 | : SUBBITMAP_LIBASS; |
277 | | |
278 | 470 | if (p->cached_subs_valid && !image_lists_changed && |
279 | 71 | p->cached_subs.format == format) |
280 | 71 | { |
281 | 71 | *out = p->cached_subs; |
282 | 71 | return; |
283 | 71 | } |
284 | | |
285 | 399 | *out = (struct sub_bitmaps){.change_id = 1}; |
286 | 399 | p->cached_subs_valid = false; |
287 | | |
288 | 399 | struct sub_bitmaps res = { |
289 | 399 | .change_id = image_lists_changed, |
290 | 399 | .format = SUBBITMAP_LIBASS, |
291 | 399 | .parts = p->cached_parts, |
292 | 399 | .video_color_space = video_color_space, |
293 | 399 | }; |
294 | | |
295 | 798 | for (int n = 0; n < num_image_lists; n++) { |
296 | 874 | for (struct ass_image *img = image_lists[n]; img; img = img->next) { |
297 | 475 | if (img->w == 0 || img->h == 0) |
298 | 0 | continue; |
299 | 475 | MP_TARRAY_GROW(p, p->cached_parts, res.num_parts); |
300 | 475 | res.parts = p->cached_parts; |
301 | 475 | struct sub_bitmap *b = &res.parts[res.num_parts]; |
302 | 475 | b->bitmap = img->bitmap; |
303 | 475 | b->stride = img->stride; |
304 | 475 | b->libass.color = img->color; |
305 | 475 | b->dw = b->w = img->w; |
306 | 475 | b->dh = b->h = img->h; |
307 | 475 | b->x = img->dst_x; |
308 | 475 | b->y = img->dst_y; |
309 | 475 | res.num_parts++; |
310 | 475 | } |
311 | 399 | } |
312 | | |
313 | 399 | bool r = false; |
314 | 399 | if (format == SUBBITMAP_BGRA) { |
315 | 0 | r = pack_rgba(p, &res); |
316 | 399 | } else { |
317 | 399 | r = pack_libass(p, &res); |
318 | 399 | } |
319 | | |
320 | 399 | if (!r) |
321 | 327 | return; |
322 | | |
323 | 72 | *out = res; |
324 | 72 | p->cached_subs = res; |
325 | 72 | p->cached_subs.change_id = 0; |
326 | | p->cached_subs_valid = true; |
327 | 72 | } |
328 | | |
329 | | #if HAVE_SUBRANDR |
330 | | // Pack the images in `res` into a BGRA8 atlas and populate `res->parts` |
331 | | // with instances of the images as described by `pass`. |
332 | | static bool pack_subrandr(struct mp_sub_packer *p, struct sub_bitmaps *res, |
333 | | struct sbr_instanced_raster_pass *pass) |
334 | | { |
335 | | if (!pack(p, res, IMGFMT_BGRA)) |
336 | | return false; |
337 | | |
338 | | int padding = p->packer->padding; |
339 | | sbr_bgra8 *base = (sbr_bgra8 *)res->packed->planes[0]; |
340 | | int byte_stride = res->packed->stride[0]; |
341 | | int pixel_stride = byte_stride / 4; |
342 | | struct sbr_output_instance *instances = sbr_instanced_raster_pass_get_instances(pass); |
343 | | |
344 | | for (int n = 0; n < res->num_parts; n++) { |
345 | | struct sub_bitmap *b = &res->parts[n]; |
346 | | sbr_output_image_rasterize_into(b->subrandr.image, pass, b->src_x, b->src_y, |
347 | | base, res->packed_w, res->packed_h, pixel_stride); |
348 | | |
349 | | void *pdata = base + b->src_y * pixel_stride + b->src_x; |
350 | | fill_padding_4(pdata, b->w, b->h, byte_stride, padding); |
351 | | b->bitmap = pdata; |
352 | | } |
353 | | |
354 | | res->parts = NULL; |
355 | | res->num_parts = 0; |
356 | | res->format = SUBBITMAP_BGRA; |
357 | | for (struct sbr_output_instance *instance = instances; instance; instance = instance->next) { |
358 | | if (!instance->base->user_data) |
359 | | continue; |
360 | | |
361 | | MP_TARRAY_GROW(p, p->cached_parts, res->num_parts); |
362 | | res->parts = p->cached_parts; |
363 | | struct sub_bitmap *inst_b = &res->parts[res->num_parts]; |
364 | | struct sub_bitmap *image_b = &p->cached_subrandr_images[(size_t)instance->base->user_data - 1]; |
365 | | |
366 | | *inst_b = (struct sub_bitmap){ |
367 | | .x = instance->dst_x, .y = instance->dst_y, |
368 | | .dw = instance->dst_width, .dh = instance->dst_height, |
369 | | .w = instance->src_width, .h = instance->src_height, |
370 | | .src_x = image_b->src_x + instance->src_off_x, |
371 | | .src_y = image_b->src_y + instance->src_off_y, |
372 | | .bitmap = image_b->bitmap, .stride = byte_stride, |
373 | | }; |
374 | | res->num_parts++; |
375 | | } |
376 | | |
377 | | return true; |
378 | | } |
379 | | |
380 | | // Pack the images in `pass` into a single image, make `out` point to it, |
381 | | // and populate `out->parts` to correctly describe all instances in `pass`. |
382 | | void mp_sub_packer_pack_sbr(struct mp_sub_packer *p, sbr_instanced_raster_pass *pass, |
383 | | struct sub_bitmaps *out) |
384 | | { |
385 | | *out = (struct sub_bitmaps){.change_id = 1}; |
386 | | p->cached_subs_valid = false; |
387 | | |
388 | | struct sub_bitmaps res = { |
389 | | .change_id = (unsigned)p->cached_subs.change_id + 1, |
390 | | .format = SUBBITMAP_SUBRANDR, |
391 | | .parts = p->cached_parts, |
392 | | .video_color_space = false, |
393 | | }; |
394 | | |
395 | | struct sbr_output_instance *instances = sbr_instanced_raster_pass_get_instances(pass); |
396 | | for (struct sbr_output_instance *instance = instances; instance; instance = instance->next) { |
397 | | // If `user_data` is non-null then this base image was already appended. |
398 | | if (instance->base->user_data) |
399 | | continue; |
400 | | |
401 | | MP_TARRAY_GROW(p, p->cached_subrandr_images, res.num_parts); |
402 | | res.parts = p->cached_subrandr_images; |
403 | | struct sub_bitmap *b = &res.parts[res.num_parts]; |
404 | | b->subrandr.image = instance->base; |
405 | | b->w = instance->base->width, b->h = instance->base->height; |
406 | | // Store the index of the `sub_bitmap` for this output image into `user_data`. |
407 | | // This index is incremented by one to differentiate index `0` from an absent index. |
408 | | // Will be read in `pack_subrandr` after packing to determine where each image |
409 | | // was actually packed to in the atlas. |
410 | | instance->base->user_data = (void *)(uintptr_t)(res.num_parts + 1); |
411 | | res.num_parts++; |
412 | | } |
413 | | |
414 | | if (!pack_subrandr(p, &res, pass)) |
415 | | return; |
416 | | |
417 | | *out = res; |
418 | | p->cached_subs = res; |
419 | | p->cached_subs_valid = true; |
420 | | } |
421 | | |
422 | | const struct sub_bitmaps *mp_sub_packer_get_cached(struct mp_sub_packer *p) { |
423 | | if (p->cached_subs_valid) |
424 | | return &p->cached_subs; |
425 | | return NULL; |
426 | | } |
427 | | #endif |