Coverage Report

Created: 2025-12-10 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mpv/sub/draw_bmp.c
Line
Count
Source
1
/*
2
 * This file is part of mpv.
3
 *
4
 * mpv is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
6
 * License as published by the Free Software Foundation; either
7
 * version 2.1 of the License, or (at your option) any later version.
8
 *
9
 * mpv is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15
 * License along with mpv.  If not, see <http://www.gnu.org/licenses/>.
16
 */
17
18
#include <stddef.h>
19
#include <stdbool.h>
20
#include <assert.h>
21
#include <math.h>
22
#include <inttypes.h>
23
24
#include "common/common.h"
25
#include "draw_bmp.h"
26
#include "img_convert.h"
27
#include "video/mp_image.h"
28
#include "video/repack.h"
29
#include "video/sws_utils.h"
30
#include "video/img_format.h"
31
#include "video/csputils.h"
32
33
const bool mp_draw_sub_formats[SUBBITMAP_COUNT] = {
34
    [SUBBITMAP_LIBASS] = true,
35
    [SUBBITMAP_BGRA] = true,
36
};
37
38
struct part {
39
    int change_id;
40
    // Sub-bitmaps scaled to final sizes.
41
    int num_imgs;
42
    struct mp_image **imgs;
43
};
44
45
// Must be a power of 2. Height is 1, but mark_rect() effectively operates on
46
// multiples of chroma sized macro-pixels. (E.g. 4:2:0 -> every second line is
47
// the same as the previous one, and x0%2==x1%2==0.)
48
225k
#define SLICE_W 256u
49
50
// Whether to scale in tiles. Faster, but can't use correct chroma position.
51
// Should be a runtime option. SLICE_W is used as tile width. The tile size
52
// should probably be small; too small or too big will cause overhead when
53
// scaling.
54
84
#define SCALE_IN_TILES 1
55
84
#define TILE_H 4u
56
57
struct slice {
58
    uint16_t x0, x1;
59
};
60
61
struct mp_draw_sub_cache
62
{
63
    struct mpv_global *global;
64
65
    // Possibly cached parts. Also implies what's in the video_overlay.
66
    struct part parts[MAX_OSD_PARTS];
67
    int64_t change_id;
68
69
    struct mp_image_params params;  // target image params
70
71
    int w, h;                       // like params.w/h, but rounded up to chroma
72
    unsigned align_x, align_y;      // alignment for all video pixels
73
74
    struct mp_image *rgba_overlay;  // all OSD in RGBA
75
    struct mp_image *video_overlay; // rgba_overlay converted to video colorspace
76
    struct mp_image *alpha_overlay; // alpha plane ref. to video_overlay
77
    struct mp_image *calpha_overlay; // alpha_overlay scaled to chroma plane size
78
79
    unsigned s_w;                   // number of slices per line
80
    struct slice *slices;           // slices[y * s_w + x / SLICE_W]
81
    bool any_osd;
82
83
    struct mp_sws_context *rgba_to_overlay; // scaler for rgba -> video csp.
84
    struct mp_sws_context *alpha_to_calpha; // scaler for overlay -> calpha
85
    bool scale_in_tiles;
86
87
    struct mp_sws_context *sub_scale; // scaler for SUBBITMAP_BGRA
88
89
    struct mp_repack *overlay_to_f32; // convert video_overlay to float
90
    struct mp_image *overlay_tmp;   // slice in float32
91
92
    struct mp_repack *calpha_to_f32; // convert video_overlay to float
93
    struct mp_image *calpha_tmp;    // slice in float32
94
95
    struct mp_repack *video_to_f32; // convert video to float
96
    struct mp_repack *video_from_f32; // convert float back to video
97
    struct mp_image *video_tmp;     // slice in float32
98
99
    struct mp_sws_context *premul;  // video -> premultiplied video
100
    struct mp_sws_context *unpremul; // reverse
101
    struct mp_image *premul_tmp;
102
103
    // Function that works on the _f32 data.
104
    void (*blend_line)(void *dst, void *src, void *src_a, int w);
105
106
    struct mp_image res_overlay;    // returned by mp_draw_sub_overlay()
107
};
108
109
static void blend_line_f32(void *dst, void *src, void *src_a, int w)
110
0
{
111
0
    float *dst_f = dst;
112
0
    float *src_f = src;
113
0
    float *src_a_f = src_a;
114
115
0
    for (int x = 0; x < w; x++)
116
0
        dst_f[x] = src_f[x] + dst_f[x] * (1.0f - src_a_f[x]);
117
0
}
118
119
static void blend_line_u8(void *dst, void *src, void *src_a, int w)
120
32.5k
{
121
32.5k
    uint8_t *dst_i = dst;
122
32.5k
    uint8_t *src_i = src;
123
32.5k
    uint8_t *src_a_i = src_a;
124
125
8.37M
    for (int x = 0; x < w; x++)
126
8.34M
        dst_i[x] = src_i[x] + dst_i[x] * (255u - src_a_i[x]) / 255u;
127
32.5k
}
128
129
static void blend_slice(struct mp_draw_sub_cache *p)
130
10.8k
{
131
10.8k
    struct mp_image *ov = p->overlay_tmp;
132
10.8k
    struct mp_image *ca = p->calpha_tmp;
133
10.8k
    struct mp_image *vid = p->video_tmp;
134
135
43.4k
    for (int plane = 0; plane < vid->num_planes; plane++) {
136
32.5k
        int xs = vid->fmt.xs[plane];
137
32.5k
        int ys = vid->fmt.ys[plane];
138
32.5k
        int h = (1 << vid->fmt.chroma_ys) - (1 << ys) + 1;
139
32.5k
        int cw = mp_chroma_div_up(vid->w, xs);
140
65.1k
        for (int y = 0; y < h; y++) {
141
32.5k
            p->blend_line(mp_image_pixel_ptr_ny(vid, plane, 0, y),
142
32.5k
                          mp_image_pixel_ptr_ny(ov, plane, 0, y),
143
32.5k
                          xs || ys ? mp_image_pixel_ptr_ny(ca, 0, 0, y)
144
32.5k
                            : mp_image_pixel_ptr_ny(ov, ov->num_planes - 1, 0, y),
145
32.5k
                          cw);
146
32.5k
        }
147
32.5k
    }
148
10.8k
}
149
150
static bool blend_overlay_with_video(struct mp_draw_sub_cache *p,
151
                                     struct mp_image *dst)
152
84
{
153
84
    if (!repack_config_buffers(p->video_to_f32, 0, p->video_tmp, 0, dst, NULL))
154
0
        return false;
155
84
    if (!repack_config_buffers(p->video_from_f32, 0, dst, 0, p->video_tmp, NULL))
156
0
        return false;
157
158
84
    int xs = dst->fmt.chroma_xs;
159
84
    int ys = dst->fmt.chroma_ys;
160
161
15.2k
    for (int y = 0; y < dst->h; y += p->align_y) {
162
15.1k
        struct slice *line = &p->slices[y * p->s_w];
163
164
45.3k
        for (int sx = 0; sx < p->s_w; sx++) {
165
30.2k
            struct slice *s = &line[sx];
166
167
30.2k
            int w = s->x1 - s->x0;
168
30.2k
            if (w <= 0)
169
19.3k
                continue;
170
10.8k
            int x = sx * SLICE_W + s->x0;
171
172
10.8k
            mp_assert(MP_IS_ALIGNED(x, p->align_x));
173
10.8k
            mp_assert(MP_IS_ALIGNED(w, p->align_x));
174
10.8k
            mp_assert(x + w <= p->w);
175
176
10.8k
            repack_line(p->overlay_to_f32, 0, 0, x, y, w);
177
10.8k
            repack_line(p->video_to_f32, 0, 0, x, y, w);
178
10.8k
            if (p->calpha_to_f32)
179
0
                repack_line(p->calpha_to_f32, 0, 0, x >> xs, y >> ys, w >> xs);
180
181
10.8k
            blend_slice(p);
182
183
10.8k
            repack_line(p->video_from_f32, x, y, 0, 0, w);
184
10.8k
        }
185
15.1k
    }
186
187
84
    return true;
188
84
}
189
190
static bool convert_overlay_part(struct mp_draw_sub_cache *p,
191
                                 int x0, int y0, int w, int h)
192
0
{
193
0
    struct mp_image src = *p->rgba_overlay;
194
0
    struct mp_image dst = *p->video_overlay;
195
196
0
    mp_image_crop(&src, x0, y0, x0 + w, y0 + h);
197
0
    mp_image_crop(&dst, x0, y0, x0 + w, y0 + h);
198
199
0
    if (mp_sws_scale(p->rgba_to_overlay, &dst, &src) < 0)
200
0
        return false;
201
202
0
    if (p->calpha_overlay) {
203
0
        src = *p->alpha_overlay;
204
0
        dst = *p->calpha_overlay;
205
206
0
        int xs = p->video_overlay->fmt.chroma_xs;
207
0
        int ys = p->video_overlay->fmt.chroma_ys;
208
0
        mp_image_crop(&src, x0, y0, x0 + w, y0 + h);
209
0
        mp_image_crop(&dst, x0 >> xs, y0 >> ys, (x0 + w) >> xs, (y0 + h) >> ys);
210
211
0
        if (mp_sws_scale(p->alpha_to_calpha, &dst, &src) < 0)
212
0
            return false;
213
0
    }
214
215
0
    return true;
216
0
}
217
218
static bool convert_to_video_overlay(struct mp_draw_sub_cache *p)
219
84
{
220
84
    if (!p->video_overlay)
221
84
        return true;
222
223
0
    if (p->scale_in_tiles) {
224
0
        int t_h = p->rgba_overlay->h / TILE_H;
225
0
        for (int ty = 0; ty < t_h; ty++) {
226
0
            for (int sx = 0; sx < p->s_w; sx++) {
227
0
                struct slice *s = &p->slices[ty * TILE_H * p->s_w + sx];
228
0
                bool pixels_set = false;
229
0
                for (int y = 0; y < TILE_H; y++) {
230
0
                    if (s[0].x0 < s[0].x1) {
231
0
                        pixels_set = true;
232
0
                        break;
233
0
                    }
234
0
                    s += p->s_w;
235
0
                }
236
0
                if (!pixels_set)
237
0
                    continue;
238
0
                if (!convert_overlay_part(p, sx * SLICE_W, ty * TILE_H,
239
0
                                          SLICE_W, TILE_H))
240
0
                    return false;
241
0
            }
242
0
        }
243
0
    } else {
244
0
        if (!convert_overlay_part(p, 0, 0, p->rgba_overlay->w, p->rgba_overlay->h))
245
0
            return false;
246
0
    }
247
248
0
    return true;
249
0
}
250
251
// Mark the given rectangle of pixels as possibly non-transparent.
252
// The rectangle must have been pre-clipped.
253
static void mark_rect(struct mp_draw_sub_cache *p, int x0, int y0, int x1, int y1)
254
2.89k
{
255
2.89k
    x0 = MP_ALIGN_DOWN(x0, p->align_x);
256
2.89k
    y0 = MP_ALIGN_DOWN(y0, p->align_y);
257
2.89k
    x1 = MP_ALIGN_UP(x1, p->align_x);
258
2.89k
    y1 = MP_ALIGN_UP(y1, p->align_y);
259
260
2.89k
    mp_assert(x0 >= 0 && x0 <= x1 && x1 <= p->w);
261
2.89k
    mp_assert(y0 >= 0 && y0 <= y1 && y1 <= p->h);
262
263
2.89k
    const int sx0 = x0 / SLICE_W;
264
2.89k
    const int sx1 = MPMIN(x1 / SLICE_W, p->s_w - 1);
265
266
48.8k
    for (int y = y0; y < y1; y++) {
267
45.9k
        struct slice *line = &p->slices[y * p->s_w];
268
269
45.9k
        struct slice *s0 = &line[sx0];
270
45.9k
        struct slice *s1 = &line[sx1];
271
272
45.9k
        s0->x0 = MPMIN(s0->x0, x0 % SLICE_W);
273
45.9k
        s1->x1 = MPMAX(s1->x1, ((x1 - 1) % SLICE_W) + 1);
274
275
45.9k
        if (s0 != s1) {
276
0
            s0->x1 = SLICE_W;
277
0
            s1->x0 = 0;
278
279
0
            for (int x = sx0 + 1; x < sx1; x++) {
280
0
                struct slice *s = &line[x];
281
0
                s->x0 = 0;
282
0
                s->x1 = SLICE_W;
283
0
            }
284
0
        }
285
286
        // Ensure the very last slice does not extend
287
        // beyond the total width.
288
45.9k
        struct slice *last_s = &line[p->s_w - 1];
289
45.9k
        last_s->x1 = MPMIN(p->w - ((p->s_w - 1) * SLICE_W), last_s->x1);
290
291
45.9k
        p->any_osd = true;
292
45.9k
    }
293
2.89k
}
294
295
static void draw_ass_rgba(uint8_t *dst, ptrdiff_t dst_stride,
296
                          uint8_t *src, ptrdiff_t src_stride,
297
                          int w, int h, uint32_t color)
298
2.89k
{
299
2.89k
    const unsigned int r = (color >> 24) & 0xff;
300
2.89k
    const unsigned int g = (color >> 16) & 0xff;
301
2.89k
    const unsigned int b = (color >>  8) & 0xff;
302
2.89k
    const unsigned int a = 0xff - (color & 0xff);
303
304
48.8k
    for (int y = 0; y < h; y++) {
305
45.9k
        uint32_t *dstrow = (uint32_t *) dst;
306
1.11M
        for (int x = 0; x < w; x++) {
307
1.06M
            const unsigned int v = src[x];
308
1.06M
            unsigned int aa = a * v;
309
1.06M
            uint32_t dstpix = dstrow[x];
310
1.06M
            unsigned int dstb =  dstpix        & 0xFF;
311
1.06M
            unsigned int dstg = (dstpix >>  8) & 0xFF;
312
1.06M
            unsigned int dstr = (dstpix >> 16) & 0xFF;
313
1.06M
            unsigned int dsta = (dstpix >> 24) & 0xFF;
314
1.06M
            dstb = (v * b * a   + dstb * (255 * 255 - aa)) / (255 * 255);
315
1.06M
            dstg = (v * g * a   + dstg * (255 * 255 - aa)) / (255 * 255);
316
1.06M
            dstr = (v * r * a   + dstr * (255 * 255 - aa)) / (255 * 255);
317
1.06M
            dsta = (aa * 255    + dsta * (255 * 255 - aa)) / (255 * 255);
318
1.06M
            dstrow[x] = dstb | (dstg << 8) | (dstr << 16) | (dsta << 24);
319
1.06M
        }
320
45.9k
        dst += dst_stride;
321
45.9k
        src += src_stride;
322
45.9k
    }
323
2.89k
}
324
325
static void render_ass(struct mp_draw_sub_cache *p, struct sub_bitmaps *sb)
326
84
{
327
84
    mp_assert(sb->format == SUBBITMAP_LIBASS);
328
329
2.98k
    for (int i = 0; i < sb->num_parts; i++) {
330
2.89k
        struct sub_bitmap *s = &sb->parts[i];
331
332
2.89k
        draw_ass_rgba(mp_image_pixel_ptr(p->rgba_overlay, 0, s->x, s->y),
333
2.89k
                      p->rgba_overlay->stride[0], s->bitmap, s->stride,
334
2.89k
                      s->w, s->h, s->libass.color);
335
336
2.89k
        mark_rect(p, s->x, s->y, s->x + s->w, s->y + s->h);
337
2.89k
    }
338
84
}
339
340
static void draw_rgba(uint8_t *dst, ptrdiff_t dst_stride,
341
                      uint8_t *src, ptrdiff_t src_stride, int w, int h)
342
0
{
343
0
    for (int y = 0; y < h; y++) {
344
0
        uint32_t *srcrow = (uint32_t *)src;
345
0
        uint32_t *dstrow = (uint32_t *)dst;
346
0
        for (int x = 0; x < w; x++) {
347
0
            uint32_t srcpix = srcrow[x];
348
0
            uint32_t dstpix = dstrow[x];
349
0
            unsigned int srcb =  srcpix        & 0xFF;
350
0
            unsigned int srcg = (srcpix >>  8) & 0xFF;
351
0
            unsigned int srcr = (srcpix >> 16) & 0xFF;
352
0
            unsigned int srca = (srcpix >> 24) & 0xFF;
353
0
            unsigned int dstb =  dstpix        & 0xFF;
354
0
            unsigned int dstg = (dstpix >>  8) & 0xFF;
355
0
            unsigned int dstr = (dstpix >> 16) & 0xFF;
356
0
            unsigned int dsta = (dstpix >> 24) & 0xFF;
357
0
            dstb = srcb + dstb * (255 * 255 - srca) / (255 * 255);
358
0
            dstg = srcg + dstg * (255 * 255 - srca) / (255 * 255);
359
0
            dstr = srcr + dstr * (255 * 255 - srca) / (255 * 255);
360
0
            dsta = srca + dsta * (255 * 255 - srca) / (255 * 255);
361
0
            dstrow[x] = dstb | (dstg << 8) | (dstr << 16) | (dsta << 24);
362
0
        }
363
0
        dst += dst_stride;
364
0
        src += src_stride;
365
0
    }
366
0
}
367
368
static bool render_rgba(struct mp_draw_sub_cache *p, struct part *part,
369
                        struct sub_bitmaps *sb)
370
0
{
371
0
    mp_assert(sb->format == SUBBITMAP_BGRA);
372
373
0
    if (part->change_id != sb->change_id) {
374
0
        for (int n = 0; n < part->num_imgs; n++)
375
0
            talloc_free(part->imgs[n]);
376
0
        part->num_imgs = sb->num_parts;
377
0
        MP_TARRAY_GROW(p, part->imgs, part->num_imgs);
378
0
        for (int n = 0; n < part->num_imgs; n++)
379
0
            part->imgs[n] = NULL;
380
381
0
        part->change_id = sb->change_id;
382
0
    }
383
384
0
    for (int i = 0; i < sb->num_parts; i++) {
385
0
        struct sub_bitmap *s = &sb->parts[i];
386
387
        // Clipping is rare but necessary.
388
0
        int sx0 = s->x;
389
0
        int sy0 = s->y;
390
0
        int sx1 = s->x + s->dw;
391
0
        int sy1 = s->y + s->dh;
392
393
0
        int x0 = MPCLAMP(sx0, 0, p->w);
394
0
        int y0 = MPCLAMP(sy0, 0, p->h);
395
0
        int x1 = MPCLAMP(sx1, 0, p->w);
396
0
        int y1 = MPCLAMP(sy1, 0, p->h);
397
398
0
        int dw = x1 - x0;
399
0
        int dh = y1 - y0;
400
0
        if (dw <= 0 || dh <= 0)
401
0
            continue;
402
403
        // We clip the source instead of the scaled image, because that might
404
        // avoid excessive memory usage when applying a ridiculous scale factor,
405
        // even if that stretches it to up to 1 pixel due to integer rounding.
406
0
        int sx = 0;
407
0
        int sy = 0;
408
0
        int sw = s->w;
409
0
        int sh = s->h;
410
0
        if (x0 != sx0 || y0 != sy0 || x1 != sx1 || y1 != sy1) {
411
0
            double fx = s->dw / (double)s->w;
412
0
            double fy = s->dh / (double)s->h;
413
0
            sx = MPCLAMP((x0 - sx0) / fx, 0, s->w);
414
0
            sy = MPCLAMP((y0 - sy0) / fy, 0, s->h);
415
0
            sw = MPCLAMP(dw / fx, 1, s->w);
416
0
            sh = MPCLAMP(dh / fy, 1, s->h);
417
0
        }
418
419
0
        mp_assert(sx >= 0 && sw > 0 && sx + sw <= s->w);
420
0
        mp_assert(sy >= 0 && sh > 0 && sy + sh <= s->h);
421
422
0
        ptrdiff_t s_stride = s->stride;
423
0
        void *s_ptr = (char *)s->bitmap + s_stride * sy + sx * 4;
424
425
0
        if (dw != sw || dh != sh) {
426
0
            struct mp_image *scaled = part->imgs[i];
427
428
0
            if (!scaled) {
429
0
                struct mp_image src_img = {0};
430
0
                mp_image_setfmt(&src_img, IMGFMT_BGRA);
431
0
                mp_image_set_size(&src_img, sw, sh);
432
0
                src_img.planes[0] = s_ptr;
433
0
                src_img.stride[0] = s_stride;
434
0
                src_img.params.repr.alpha = PL_ALPHA_PREMULTIPLIED;
435
436
0
                scaled = mp_image_alloc(IMGFMT_BGRA, dw, dh);
437
0
                if (!scaled)
438
0
                    return false;
439
0
                part->imgs[i] = talloc_steal(p, scaled);
440
0
                mp_image_copy_attributes(scaled, &src_img);
441
442
0
                if (mp_sws_scale(p->sub_scale, scaled, &src_img) < 0)
443
0
                    return false;
444
0
            }
445
446
0
            mp_assert(scaled->w == dw);
447
0
            mp_assert(scaled->h == dh);
448
449
0
            s_stride = scaled->stride[0];
450
0
            s_ptr = scaled->planes[0];
451
0
        }
452
453
0
        draw_rgba(mp_image_pixel_ptr(p->rgba_overlay, 0, x0, y0),
454
0
                  p->rgba_overlay->stride[0], s_ptr, s_stride, dw, dh);
455
456
0
        mark_rect(p, x0, y0, x1, y1);
457
0
    }
458
459
0
    return true;
460
0
}
461
462
static bool render_sb(struct mp_draw_sub_cache *p, struct sub_bitmaps *sb)
463
84
{
464
84
    struct part *part = &p->parts[sb->render_index];
465
466
84
    switch (sb->format) {
467
84
    case SUBBITMAP_LIBASS:
468
84
        render_ass(p, sb);
469
84
        return true;
470
0
    case SUBBITMAP_BGRA:
471
0
        return render_rgba(p, part, sb);
472
84
    }
473
474
0
    return false;
475
84
}
476
477
static void clear_rgba_overlay(struct mp_draw_sub_cache *p)
478
168
{
479
168
    mp_assert(p->rgba_overlay->imgfmt == IMGFMT_BGRA);
480
481
30.4k
    for (int y = 0; y < p->rgba_overlay->h; y++) {
482
30.2k
        uint32_t *px = mp_image_pixel_ptr(p->rgba_overlay, 0, 0, y);
483
30.2k
        struct slice *line = &p->slices[y * p->s_w];
484
485
90.7k
        for (int sx = 0; sx < p->s_w; sx++) {
486
60.4k
            struct slice *s = &line[sx];
487
488
            // Ensure this final slice doesn't extend beyond the width of p->s_w
489
60.4k
            if (s->x1 == SLICE_W && sx == p->s_w - 1 && y == p->rgba_overlay->h - 1)
490
0
                s->x1 = MPMIN(p->w - ((p->s_w - 1) * SLICE_W), s->x1);
491
492
60.4k
            if (s->x0 <= s->x1) {
493
30.2k
                memset(px + s->x0, 0, (s->x1 - s->x0) * 4);
494
30.2k
                *s = (struct slice){SLICE_W, 0};
495
30.2k
            }
496
497
60.4k
            px += SLICE_W;
498
60.4k
        }
499
30.2k
    }
500
501
168
    p->any_osd = false;
502
168
}
503
504
static struct mp_sws_context *alloc_scaler(struct mp_draw_sub_cache *p)
505
84
{
506
84
    struct mp_sws_context *s = mp_sws_alloc(p);
507
84
    mp_sws_enable_cmdline_opts(s, p->global);
508
84
    return s;
509
84
}
510
511
static void init_general(struct mp_draw_sub_cache *p)
512
84
{
513
84
    p->sub_scale = alloc_scaler(p);
514
515
84
    p->s_w = MP_ALIGN_UP(p->rgba_overlay->w, SLICE_W) / SLICE_W;
516
517
84
    p->slices = talloc_zero_array(p, struct slice, p->s_w * p->rgba_overlay->h);
518
519
84
    mp_image_clear(p->rgba_overlay, 0, 0, p->w, p->h);
520
84
    clear_rgba_overlay(p);
521
84
}
522
523
static bool reinit_to_video(struct mp_draw_sub_cache *p)
524
84
{
525
84
    struct mp_image_params *params = &p->params;
526
84
    mp_image_params_guess_csp(params);
527
528
84
    bool need_premul = params->repr.alpha != PL_ALPHA_PREMULTIPLIED &&
529
84
        (mp_imgfmt_get_desc(params->imgfmt).flags & MP_IMGFLAG_ALPHA);
530
531
    // Intermediate format for video_overlay. Requirements:
532
    //  - same subsampling as video
533
    //  - uses video colorspace
534
    //  - has alpha
535
    //  - repacker support (to the format used in p->blend_line)
536
    //  - probably 8 bit per component rather than something wasteful or strange
537
84
    struct mp_regular_imgfmt vfdesc = {0};
538
539
84
    int rflags = REPACK_CREATE_EXPAND_8BIT;
540
84
    bool use_shortcut = false;
541
542
84
    p->video_to_f32 = mp_repack_create_planar(params->imgfmt, false, rflags);
543
84
    talloc_steal(p, p->video_to_f32);
544
84
    if (!p->video_to_f32)
545
0
        return false;
546
84
    mp_get_regular_imgfmt(&vfdesc, mp_repack_get_format_dst(p->video_to_f32));
547
84
    mp_assert(vfdesc.num_planes); // must have succeeded
548
549
84
    if (params->repr.sys == PL_COLOR_SYSTEM_RGB && vfdesc.num_planes >= 3) {
550
84
        use_shortcut = true;
551
552
84
        if (vfdesc.component_type == MP_COMPONENT_TYPE_UINT &&
553
84
            vfdesc.component_size == 1 && vfdesc.component_pad == 0)
554
84
            p->blend_line = blend_line_u8;
555
84
    }
556
557
    // If no special blender is available, blend in float.
558
84
    if (!p->blend_line) {
559
0
        TA_FREEP(&p->video_to_f32);
560
561
0
        rflags |= REPACK_CREATE_PLANAR_F32;
562
563
0
        p->video_to_f32 = mp_repack_create_planar(params->imgfmt, false, rflags);
564
0
        talloc_steal(p, p->video_to_f32);
565
0
        if (!p->video_to_f32)
566
0
            return false;
567
568
0
        mp_get_regular_imgfmt(&vfdesc, mp_repack_get_format_dst(p->video_to_f32));
569
0
        mp_assert(vfdesc.component_type == MP_COMPONENT_TYPE_FLOAT);
570
571
0
        p->blend_line = blend_line_f32;
572
0
    }
573
574
84
    p->scale_in_tiles = SCALE_IN_TILES;
575
576
84
    int vid_f32_fmt = mp_repack_get_format_dst(p->video_to_f32);
577
578
84
    p->video_from_f32 = mp_repack_create_planar(params->imgfmt, true, rflags);
579
84
    talloc_steal(p, p->video_from_f32);
580
84
    if (!p->video_from_f32)
581
0
        return false;
582
583
84
    mp_assert(mp_repack_get_format_dst(p->video_to_f32) ==
584
84
           mp_repack_get_format_src(p->video_from_f32));
585
586
84
    int overlay_fmt = 0;
587
84
    if (use_shortcut) {
588
        // No point in doing anything fancy.
589
84
        overlay_fmt = IMGFMT_BGRA;
590
84
        p->scale_in_tiles = false;
591
84
    } else {
592
0
        struct mp_regular_imgfmt odesc = vfdesc;
593
        // Just use 8 bit as well (should be fine, may use less memory).
594
0
        odesc.component_type = MP_COMPONENT_TYPE_UINT;
595
0
        odesc.component_size = 1;
596
0
        odesc.component_pad = 0;
597
598
        // Ensure there's alpha.
599
0
        if (odesc.planes[odesc.num_planes - 1].components[0] != 4) {
600
0
            if (odesc.num_planes >= 4)
601
0
                return false; // wat
602
0
            odesc.planes[odesc.num_planes++] =
603
0
                (struct mp_regular_imgfmt_plane){1, {4}};
604
0
        }
605
606
0
        overlay_fmt = mp_find_regular_imgfmt(&odesc);
607
0
        p->scale_in_tiles = odesc.chroma_xs || odesc.chroma_ys;
608
0
    }
609
84
    if (!overlay_fmt)
610
0
        return false;
611
612
84
    p->overlay_to_f32 = mp_repack_create_planar(overlay_fmt, false, rflags);
613
84
    talloc_steal(p, p->overlay_to_f32);
614
84
    if (!p->overlay_to_f32)
615
0
        return false;
616
617
84
    int render_fmt = mp_repack_get_format_dst(p->overlay_to_f32);
618
619
84
    struct mp_regular_imgfmt ofdesc = {0};
620
84
    mp_get_regular_imgfmt(&ofdesc, render_fmt);
621
622
84
    if (ofdesc.planes[ofdesc.num_planes - 1].components[0] != 4)
623
0
        return false;
624
625
    // The formats must be the same, minus possible lack of alpha in vfdesc.
626
84
    if (ofdesc.num_planes != vfdesc.num_planes &&
627
84
        ofdesc.num_planes - 1 != vfdesc.num_planes)
628
0
        return false;
629
336
    for (int n = 0; n < vfdesc.num_planes; n++) {
630
252
        if (vfdesc.planes[n].components[0] != ofdesc.planes[n].components[0])
631
0
            return false;
632
252
    }
633
634
84
    p->align_x = mp_repack_get_align_x(p->video_to_f32);
635
84
    p->align_y = mp_repack_get_align_y(p->video_to_f32);
636
637
84
    mp_assert(p->align_x >= mp_repack_get_align_x(p->overlay_to_f32));
638
84
    mp_assert(p->align_y >= mp_repack_get_align_y(p->overlay_to_f32));
639
640
84
    if (p->align_x > SLICE_W || p->align_y > TILE_H)
641
0
        return false;
642
643
84
    p->w = MP_ALIGN_UP(params->w, p->align_x);
644
84
    int slice_h = p->align_y;
645
84
    p->h = MP_ALIGN_UP(params->h, slice_h);
646
647
    // Size of the overlay. If scaling in tiles, round up to tiles, so we don't
648
    // need to reinit the scale for right/bottom tiles.
649
84
    int w = p->w;
650
84
    int h = p->h;
651
84
    if (p->scale_in_tiles) {
652
0
        w = MP_ALIGN_UP(w, SLICE_W);
653
0
        h = MP_ALIGN_UP(h, TILE_H);
654
0
    }
655
656
84
    p->rgba_overlay = talloc_steal(p, mp_image_alloc(IMGFMT_BGRA, w, h));
657
84
    p->overlay_tmp = talloc_steal(p, mp_image_alloc(render_fmt, SLICE_W, slice_h));
658
84
    p->video_tmp = talloc_steal(p, mp_image_alloc(vid_f32_fmt, SLICE_W, slice_h));
659
84
    if (!p->rgba_overlay || !p->overlay_tmp || !p->video_tmp)
660
0
        return false;
661
662
84
    mp_image_params_guess_csp(&p->rgba_overlay->params);
663
84
    p->rgba_overlay->params.repr.alpha = PL_ALPHA_PREMULTIPLIED;
664
665
84
    p->overlay_tmp->params.repr = params->repr;
666
84
    p->overlay_tmp->params.color = params->color;
667
84
    p->video_tmp->params.repr = params->repr;
668
84
    p->video_tmp->params.color = params->color;
669
670
84
    if (p->rgba_overlay->imgfmt == overlay_fmt) {
671
84
        if (!repack_config_buffers(p->overlay_to_f32, 0, p->overlay_tmp,
672
84
                                   0, p->rgba_overlay, NULL))
673
0
            return false;
674
84
    } else {
675
        // Generally non-RGB.
676
0
        p->video_overlay = talloc_steal(p, mp_image_alloc(overlay_fmt, w, h));
677
0
        if (!p->video_overlay)
678
0
            return false;
679
680
0
        p->video_overlay->params.repr = params->repr;
681
0
        p->video_overlay->params.color = params->color;
682
0
        p->video_overlay->params.chroma_location = params->chroma_location;
683
0
        p->video_overlay->params.repr.alpha = PL_ALPHA_PREMULTIPLIED;
684
685
0
        if (p->scale_in_tiles)
686
0
            p->video_overlay->params.chroma_location = PL_CHROMA_CENTER;
687
688
0
        p->rgba_to_overlay = alloc_scaler(p);
689
0
        p->rgba_to_overlay->allow_zimg = true;
690
0
        if (!mp_sws_supports_formats(p->rgba_to_overlay,
691
0
                            p->video_overlay->imgfmt, p->rgba_overlay->imgfmt))
692
0
            return false;
693
694
0
        if (!repack_config_buffers(p->overlay_to_f32, 0, p->overlay_tmp,
695
0
                                   0, p->video_overlay, NULL))
696
0
            return false;
697
698
        // Setup a scaled alpha plane if chroma-subsampling is present.
699
0
        int xs = p->video_overlay->fmt.chroma_xs;
700
0
        int ys = p->video_overlay->fmt.chroma_ys;
701
0
        if (xs || ys) {
702
            // Require float so format selection becomes simpler (maybe).
703
0
            mp_assert(rflags & REPACK_CREATE_PLANAR_F32);
704
705
            // For extracting the alpha plane, construct a gray format that is
706
            // compatible with the alpha one.
707
0
            struct mp_regular_imgfmt odesc = {0};
708
0
            mp_get_regular_imgfmt(&odesc, overlay_fmt);
709
0
            mp_assert(odesc.component_size);
710
0
            int aplane = odesc.num_planes - 1;
711
0
            mp_assert(odesc.planes[aplane].num_components == 1);
712
0
            mp_assert(odesc.planes[aplane].components[0] == 4);
713
0
            struct mp_regular_imgfmt cadesc = odesc;
714
0
            cadesc.num_planes = 1;
715
0
            cadesc.planes[0] = (struct mp_regular_imgfmt_plane){1, {1}};
716
0
            cadesc.chroma_xs = cadesc.chroma_ys = 0;
717
718
0
            int calpha_fmt = mp_find_regular_imgfmt(&cadesc);
719
0
            if (!calpha_fmt)
720
0
                return false;
721
722
            // Unscaled alpha plane from p->video_overlay.
723
0
            p->alpha_overlay = talloc_zero(p, struct mp_image);
724
0
            mp_image_setfmt(p->alpha_overlay, calpha_fmt);
725
0
            mp_image_set_size(p->alpha_overlay, w, h);
726
0
            p->alpha_overlay->planes[0] = p->video_overlay->planes[aplane];
727
0
            p->alpha_overlay->stride[0] = p->video_overlay->stride[aplane];
728
729
            // Full range gray always has the same range as alpha.
730
0
            p->alpha_overlay->params.repr.levels = PL_COLOR_LEVELS_FULL;
731
0
            mp_image_params_guess_csp(&p->alpha_overlay->params);
732
733
0
            p->calpha_overlay =
734
0
                talloc_steal(p, mp_image_alloc(calpha_fmt, w >> xs, h >> ys));
735
0
            if (!p->calpha_overlay)
736
0
                return false;
737
0
            p->calpha_overlay->params.repr = p->alpha_overlay->params.repr;
738
0
            p->calpha_overlay->params.color = p->alpha_overlay->params.color;
739
740
0
            p->calpha_to_f32 = mp_repack_create_planar(calpha_fmt, false, rflags);
741
0
            talloc_steal(p, p->calpha_to_f32);
742
0
            if (!p->calpha_to_f32)
743
0
                return false;
744
745
0
            int af32_fmt = mp_repack_get_format_dst(p->calpha_to_f32);
746
0
            p->calpha_tmp = talloc_steal(p, mp_image_alloc(af32_fmt, SLICE_W, 1));
747
0
            if (!p->calpha_tmp)
748
0
                return false;
749
750
0
            if (!repack_config_buffers(p->calpha_to_f32, 0, p->calpha_tmp,
751
0
                                       0, p->calpha_overlay, NULL))
752
0
                return false;
753
754
0
            p->alpha_to_calpha = alloc_scaler(p);
755
0
            if (!mp_sws_supports_formats(p->alpha_to_calpha,
756
0
                                         calpha_fmt, calpha_fmt))
757
0
                return false;
758
0
        }
759
0
    }
760
761
84
    if (need_premul) {
762
0
        p->premul = alloc_scaler(p);
763
0
        p->unpremul = alloc_scaler(p);
764
0
        p->premul_tmp = mp_image_alloc(params->imgfmt, params->w, params->h);
765
0
        talloc_steal(p, p->premul_tmp);
766
0
        if (!p->premul_tmp)
767
0
            return false;
768
0
        mp_image_set_params(p->premul_tmp, params);
769
0
        p->premul_tmp->params.repr.alpha = PL_ALPHA_PREMULTIPLIED;
770
771
        // Only zimg supports this.
772
0
        p->premul->force_scaler = MP_SWS_ZIMG;
773
0
        p->unpremul->force_scaler = MP_SWS_ZIMG;
774
0
    }
775
776
84
    init_general(p);
777
778
84
    return true;
779
84
}
780
781
static bool reinit_to_overlay(struct mp_draw_sub_cache *p)
782
0
{
783
0
    p->align_x = 1;
784
0
    p->align_y = 1;
785
786
0
    p->w = p->params.w;
787
0
    p->h = p->params.h;
788
789
0
    p->rgba_overlay = talloc_steal(p, mp_image_alloc(IMGFMT_BGRA, p->w, p->h));
790
0
    if (!p->rgba_overlay)
791
0
        return false;
792
793
0
    mp_image_params_guess_csp(&p->rgba_overlay->params);
794
0
    p->rgba_overlay->params.repr.alpha = PL_ALPHA_PREMULTIPLIED;
795
796
    // Some non-sense with the intention to somewhat isolate the returned image.
797
0
    mp_image_setfmt(&p->res_overlay, p->rgba_overlay->imgfmt);
798
0
    mp_image_set_size(&p->res_overlay, p->rgba_overlay->w, p->rgba_overlay->h);
799
0
    mp_image_copy_attributes(&p->res_overlay, p->rgba_overlay);
800
0
    p->res_overlay.planes[0] = p->rgba_overlay->planes[0];
801
0
    p->res_overlay.stride[0] = p->rgba_overlay->stride[0];
802
803
0
    init_general(p);
804
805
    // Mark all dirty (for full reinit of user state).
806
0
    for (int y = 0; y < p->rgba_overlay->h; y++) {
807
0
        for (int sx = 0; sx < p->s_w; sx++)
808
0
            p->slices[y * p->s_w + sx] = (struct slice){0, SLICE_W};
809
0
    }
810
811
0
    return true;
812
0
}
813
814
static bool check_reinit(struct mp_draw_sub_cache *p,
815
                         struct mp_image_params *params, bool to_video)
816
84
{
817
84
    if (!mp_image_params_equal(&p->params, params) || !p->rgba_overlay) {
818
84
        talloc_free_children(p);
819
84
        *p = (struct mp_draw_sub_cache){.global = p->global, .params = *params};
820
84
        if (!(to_video ? reinit_to_video(p) : reinit_to_overlay(p))) {
821
0
            talloc_free_children(p);
822
0
            *p = (struct mp_draw_sub_cache){.global = p->global};
823
0
            return false;
824
0
        }
825
84
    }
826
84
    return true;
827
84
}
828
829
char *mp_draw_sub_get_dbg_info(struct mp_draw_sub_cache *p)
830
0
{
831
0
    mp_assert(p);
832
833
0
    return talloc_asprintf(NULL,
834
0
        "align=%d:%d ov=%-7s, ov_f=%s, v_f=%s, a=%s, ca=%s, ca_f=%s",
835
0
        p->align_x, p->align_y,
836
0
        mp_imgfmt_to_name(p->video_overlay ? p->video_overlay->imgfmt : 0),
837
0
        mp_imgfmt_to_name(p->overlay_tmp->imgfmt),
838
0
        mp_imgfmt_to_name(p->video_tmp->imgfmt),
839
0
        mp_imgfmt_to_name(p->alpha_overlay ? p->alpha_overlay->imgfmt : 0),
840
0
        mp_imgfmt_to_name(p->calpha_overlay ? p->calpha_overlay->imgfmt : 0),
841
0
        mp_imgfmt_to_name(p->calpha_tmp ? p->calpha_tmp->imgfmt : 0));
842
0
}
843
844
struct mp_draw_sub_cache *mp_draw_sub_alloc(void *ta_parent, struct mpv_global *g)
845
42
{
846
42
    struct mp_draw_sub_cache *c = talloc_zero(ta_parent, struct mp_draw_sub_cache);
847
42
    c->global = g;
848
42
    return c;
849
42
}
850
851
// For tests.
852
struct mp_draw_sub_cache *mp_draw_sub_alloc_test(struct mp_image *dst)
853
0
{
854
0
    struct mp_draw_sub_cache *c = talloc_zero(NULL, struct mp_draw_sub_cache);
855
0
    reinit_to_video(c);
856
0
    return c;
857
0
}
858
859
bool mp_draw_sub_bitmaps(struct mp_draw_sub_cache *p, struct mp_image *dst,
860
                         struct sub_bitmap_list *sbs_list)
861
84
{
862
84
    bool ok = false;
863
864
    // dst must at least be as large as the bounding box, or you may get memory
865
    // corruption.
866
84
    mp_assert(dst->w >= sbs_list->w);
867
84
    mp_assert(dst->h >= sbs_list->h);
868
869
84
    if (!check_reinit(p, &dst->params, true))
870
0
        return false;
871
872
84
    if (p->change_id != sbs_list->change_id) {
873
84
        p->change_id = sbs_list->change_id;
874
875
84
        clear_rgba_overlay(p);
876
877
168
        for (int n = 0; n < sbs_list->num_items; n++) {
878
84
            if (!render_sb(p, sbs_list->items[n]))
879
0
                goto done;
880
84
        }
881
882
84
        if (!convert_to_video_overlay(p))
883
0
            goto done;
884
84
    }
885
886
84
    if (p->any_osd) {
887
84
        struct mp_image *target = dst;
888
84
        if (p->premul_tmp) {
889
0
            if (mp_sws_scale(p->premul, p->premul_tmp, dst) < 0)
890
0
                goto done;
891
0
            target = p->premul_tmp;
892
0
        }
893
894
84
        if (!blend_overlay_with_video(p, target))
895
0
            goto done;
896
897
84
        if (target != dst) {
898
0
            if (mp_sws_scale(p->unpremul, dst, p->premul_tmp) < 0)
899
0
                goto done;
900
0
        }
901
84
    }
902
903
84
    ok = true;
904
905
84
done:
906
84
    return ok;
907
84
}
908
909
// Bounding boxes for mp_draw_sub_overlay() API. For simplicity, each rectangle
910
// covers a fixed tile on the screen, starts out empty, but is not extended
911
// beyond the tile. In the simplest case, there's only 1 rect/tile for everything.
912
struct rc_grid {
913
    unsigned w, h;                  // size in grid tiles
914
    unsigned r_w, r_h;              // size of a grid tile in pixels
915
    struct mp_rect *rcs;            // rcs[x * w + y]
916
};
917
918
static void init_rc_grid(struct rc_grid *gr, struct mp_draw_sub_cache *p,
919
                         struct mp_rect *rcs, int max_rcs)
920
0
{
921
0
    *gr = (struct rc_grid){ .w = max_rcs ? 1 : 0, .h = max_rcs ? 1 : 0,
922
0
                            .rcs = rcs, .r_w = p->s_w * SLICE_W, .r_h = p->h, };
923
924
    // Dumb iteration to figure out max. size because I'm stupid.
925
0
    bool more = true;
926
0
    while (more) {
927
0
        more = false;
928
0
        if (gr->r_h >= 128) {
929
0
            if (gr->w * gr->h * 2 > max_rcs)
930
0
                break;
931
0
            gr->h *= 2;
932
0
            gr->r_h = (p->h + gr->h - 1) / gr->h;
933
0
            more = true;
934
0
        }
935
0
        if (gr->r_w >= SLICE_W * 2) {
936
0
            if (gr->w * gr->h * 2 > max_rcs)
937
0
                break;
938
0
            gr->w *= 2;
939
0
            gr->r_w = (p->s_w + gr->w - 1) / gr->w * SLICE_W;
940
0
            more = true;
941
0
        }
942
0
    }
943
944
0
    mp_assert(gr->r_h * gr->h >= p->h);
945
0
    mp_assert(!(gr->r_w & (SLICE_W - 1)));
946
0
    mp_assert(gr->r_w * gr->w >= p->w);
947
948
    // Init with empty (degenerate) rectangles.
949
0
    for (int y = 0; y < gr->h; y++) {
950
0
        for (int x = 0; x < gr->w; x++) {
951
0
            struct mp_rect *rc = &gr->rcs[y * gr->w + x];
952
0
            rc->x1 = x * gr->r_w;
953
0
            rc->y1 = y * gr->r_h;
954
0
            rc->x0 = rc->x1 + gr->r_w;
955
0
            rc->y0 = rc->y1 + gr->r_h;
956
0
        }
957
0
    }
958
0
}
959
960
// Extend given grid with contents of p->slices.
961
static void mark_rcs(struct mp_draw_sub_cache *p, struct rc_grid *gr)
962
0
{
963
0
    for (int y = 0; y < p->h; y++) {
964
0
        struct slice *line = &p->slices[y * p->s_w];
965
0
        struct mp_rect *rcs = &gr->rcs[y / gr->r_h * gr->w];
966
967
0
        for (int sx = 0; sx < p->s_w; sx++) {
968
0
            struct slice *s = &line[sx];
969
0
            if (s->x0 < s->x1) {
970
0
                unsigned xpos = sx * SLICE_W;
971
0
                struct mp_rect *rc = &rcs[xpos / gr->r_w];
972
0
                rc->y0 = MPMIN(rc->y0, y);
973
0
                rc->y1 = MPMAX(rc->y1, y + 1);
974
0
                rc->x0 = MPMIN(rc->x0, xpos + s->x0);
975
                // Ensure this does not extend beyond the total width
976
0
                rc->x1 = MPCLAMP(xpos + s->x1, rc->x1, p->w);
977
0
            }
978
0
        }
979
0
    }
980
0
}
981
982
// Remove empty RCs, and return rc count.
983
static int return_rcs(struct rc_grid *gr)
984
0
{
985
0
    int num = 0, cnt = gr->w * gr->h;
986
0
    for (int n = 0; n < cnt; n++) {
987
0
        struct mp_rect *rc = &gr->rcs[n];
988
0
        if (rc->x0 < rc->x1 && rc->y0 < rc->y1)
989
0
            gr->rcs[num++] = *rc;
990
0
    }
991
0
    return num;
992
0
}
993
994
struct mp_image *mp_draw_sub_overlay(struct mp_draw_sub_cache *p,
995
                                     struct sub_bitmap_list *sbs_list,
996
                                     struct mp_rect *act_rcs,
997
                                     int max_act_rcs,
998
                                     int *num_act_rcs,
999
                                     struct mp_rect *mod_rcs,
1000
                                     int max_mod_rcs,
1001
                                     int *num_mod_rcs)
1002
0
{
1003
0
    *num_act_rcs = 0;
1004
0
    *num_mod_rcs = 0;
1005
1006
0
    struct mp_image_params params = {.w = sbs_list->w, .h = sbs_list->h};
1007
0
    if (!check_reinit(p, &params, false))
1008
0
        return NULL;
1009
1010
0
    struct rc_grid gr_act, gr_mod;
1011
0
    init_rc_grid(&gr_act, p, act_rcs, max_act_rcs);
1012
0
    init_rc_grid(&gr_mod, p, mod_rcs, max_mod_rcs);
1013
1014
0
    if (p->change_id != sbs_list->change_id) {
1015
0
        p->change_id = sbs_list->change_id;
1016
1017
0
        mark_rcs(p, &gr_mod);
1018
1019
0
        clear_rgba_overlay(p);
1020
1021
0
        for (int n = 0; n < sbs_list->num_items; n++) {
1022
0
            if (!render_sb(p, sbs_list->items[n])) {
1023
0
                p->change_id = 0;
1024
0
                return NULL;
1025
0
            }
1026
0
        }
1027
1028
0
        mark_rcs(p, &gr_mod);
1029
0
    }
1030
1031
0
    mark_rcs(p, &gr_act);
1032
1033
0
    *num_act_rcs = return_rcs(&gr_act);
1034
0
    *num_mod_rcs = return_rcs(&gr_mod);
1035
1036
0
    return &p->res_overlay;
1037
0
}
1038
1039
// vim: ts=4 sw=4 et tw=80