Coverage Report

Created: 2026-03-27 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mpv/sub/packer.c
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