Coverage Report

Created: 2026-08-14 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mpv/video/img_format.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 <assert.h>
19
#include <string.h>
20
21
#include <libavcodec/avcodec.h>
22
#include <libavutil/imgutils.h>
23
#include <libavutil/pixfmt.h>
24
#include <libavutil/pixdesc.h>
25
26
#include "video/img_format.h"
27
#include "video/mp_image.h"
28
#include "video/fmt-conversion.h"
29
30
struct mp_imgfmt_entry {
31
    const char *name;
32
    // Valid if flags!=0.
33
    // This can be incomplete, and missing fields are filled in:
34
    //  - sets num_planes and bpp[], derived from comps[] (rounds to bytes)
35
    //  - sets MP_IMGFLAG_GRAY, derived from comps[]
36
    //  - sets MP_IMGFLAG_ALPHA, derived from comps[]
37
    //  - sets align_x/y if 0, derived from chroma shift
38
    //  - sets xs[]/ys[] always, derived from num_planes/chroma_shift
39
    //  - sets MP_IMGFLAG_HAS_COMPS|MP_IMGFLAG_NE if num_planes>0
40
    //  - sets MP_IMGFLAG_TYPE_UINT if no other type set
41
    //  - sets id to mp_imgfmt_list[] implied format
42
    struct mp_imgfmt_desc desc;
43
};
44
45
#define FRINGE_GBRP(def, dname, b)                                          \
46
    [def - IMGFMT_CUST_BASE] = {                                            \
47
        .name = dname,                                                      \
48
        .desc = { .flags = MP_IMGFLAG_COLOR_RGB,                            \
49
                  .comps = { {2, 0, 8, (b) - 8}, {0, 0, 8, (b) - 8},        \
50
                             {1, 0, 8, (b) - 8}, }, }}
51
52
#define FLOAT_YUV(def, dname, xs, ys, a)                                    \
53
    [def - IMGFMT_CUST_BASE] = {                                            \
54
        .name = dname,                                                      \
55
        .desc = { .flags = MP_IMGFLAG_COLOR_YUV | MP_IMGFLAG_TYPE_FLOAT,    \
56
                   .chroma_xs = xs, .chroma_ys = ys,                        \
57
                   .comps = { {0, 0, 32}, {1, 0, 32}, {2, 0, 32},           \
58
                              {3 * (a), 0, 32 * (a)} }, }}
59
60
static const struct mp_imgfmt_entry mp_imgfmt_list[] = {
61
    // not in ffmpeg
62
    [IMGFMT_VDPAU_OUTPUT - IMGFMT_CUST_BASE] = {
63
        .name = "vdpau_output",
64
        .desc = {
65
            .flags = MP_IMGFLAG_NE | MP_IMGFLAG_RGB | MP_IMGFLAG_HWACCEL,
66
        },
67
    },
68
    [IMGFMT_YAP8 - IMGFMT_CUST_BASE] = {
69
        .name = "yap8",
70
        .desc = {
71
            .flags = MP_IMGFLAG_COLOR_YUV,
72
            .comps = { {0, 0, 8}, {0}, {0}, {1, 0, 8} },
73
        },
74
    },
75
    [IMGFMT_YAP16 - IMGFMT_CUST_BASE] = {
76
        .name = "yap16",
77
        .desc = {
78
            .flags = MP_IMGFLAG_COLOR_YUV,
79
            .comps = { {0, 0, 16}, {0}, {0}, {1, 0, 16} },
80
        },
81
    },
82
    [IMGFMT_Y1 - IMGFMT_CUST_BASE] = {
83
        .name = "y1",
84
        .desc = {
85
            .flags = MP_IMGFLAG_COLOR_RGB,
86
            .comps = { {0, 0, 8, -7} },
87
        },
88
    },
89
    [IMGFMT_YAPF - IMGFMT_CUST_BASE] = {
90
        .name = "grayaf32", // try to mimic ffmpeg naming convention
91
        .desc = {
92
            .flags = MP_IMGFLAG_COLOR_YUV | MP_IMGFLAG_TYPE_FLOAT,
93
            .comps = { {0, 0, 32}, {0}, {0}, {1, 0, 32} },
94
        },
95
    },
96
    FLOAT_YUV(IMGFMT_444PF,  "yuv444pf",  0, 0, 0),
97
    FLOAT_YUV(IMGFMT_444APF, "yuva444pf", 0, 0, 1),
98
    FLOAT_YUV(IMGFMT_420PF,  "yuv420pf",  1, 1, 0),
99
    FLOAT_YUV(IMGFMT_420APF, "yuva420pf", 1, 1, 1),
100
    FLOAT_YUV(IMGFMT_422PF,  "yuv422pf",  1, 0, 0),
101
    FLOAT_YUV(IMGFMT_422APF, "yuva422pf", 1, 0, 1),
102
    FLOAT_YUV(IMGFMT_440PF,  "yuv440pf",  0, 1, 0),
103
    FLOAT_YUV(IMGFMT_440APF, "yuva440pf", 0, 1, 1),
104
    FLOAT_YUV(IMGFMT_410PF,  "yuv410pf",  2, 2, 0),
105
    FLOAT_YUV(IMGFMT_410APF, "yuva410pf", 2, 2, 1),
106
    FLOAT_YUV(IMGFMT_411PF,  "yuv411pf",  2, 0, 0),
107
    FLOAT_YUV(IMGFMT_411APF, "yuva411pf", 2, 0, 1),
108
    FRINGE_GBRP(IMGFMT_GBRP1, "gbrp1", 1),
109
    FRINGE_GBRP(IMGFMT_GBRP2, "gbrp2", 2),
110
    FRINGE_GBRP(IMGFMT_GBRP3, "gbrp3", 3),
111
    FRINGE_GBRP(IMGFMT_GBRP4, "gbrp4", 4),
112
    FRINGE_GBRP(IMGFMT_GBRP5, "gbrp5", 5),
113
    FRINGE_GBRP(IMGFMT_GBRP6, "gbrp6", 6),
114
    // in FFmpeg, but FFmpeg names have an annoying "_vld" suffix
115
    [IMGFMT_VIDEOTOOLBOX - IMGFMT_CUST_BASE] = {
116
        .name = "videotoolbox",
117
    },
118
    [IMGFMT_VAAPI - IMGFMT_CUST_BASE] = {
119
        .name = "vaapi",
120
    },
121
};
122
123
static const struct mp_imgfmt_entry *get_mp_desc(int imgfmt)
124
1.37M
{
125
1.37M
    if (imgfmt < IMGFMT_CUST_BASE)
126
895k
        return NULL;
127
480k
    int index = imgfmt - IMGFMT_CUST_BASE;
128
480k
    if (index >= MP_ARRAY_SIZE(mp_imgfmt_list))
129
474k
        return NULL;
130
6.46k
    const struct mp_imgfmt_entry *e = &mp_imgfmt_list[index];
131
6.46k
    return e->name ? e : NULL;
132
480k
}
133
134
char **mp_imgfmt_name_list(void)
135
1
{
136
1
    int count = IMGFMT_END - IMGFMT_START;
137
1
    char **list = talloc_zero_array(NULL, char *, count + 1);
138
1
    int num = 0;
139
560
    for (int n = IMGFMT_START; n < IMGFMT_END; n++) {
140
559
        const char *name = mp_imgfmt_to_name(n);
141
559
        if (strcmp(name, "unknown") != 0)
142
289
            list[num++] = talloc_strdup(list, name);
143
559
    }
144
1
    return list;
145
1
}
146
147
int mp_imgfmt_from_name(bstr name)
148
28.9k
{
149
28.9k
    if (bstr_equals0(name, "none"))
150
5
        return 0;
151
28.8k
    static const struct { bstr old, new; } deprecated_fmt_names[] = {
152
28.8k
        {bstr0_lit("rgb30"), bstr0_lit("x2rgb10")},
153
28.8k
    };
154
57.4k
    for (int n = 0; n < MP_ARRAY_SIZE(deprecated_fmt_names); n++) {
155
28.8k
        if (bstr_equals(name, deprecated_fmt_names[n].old)) {
156
301
            name = deprecated_fmt_names[n].new;
157
301
            break;
158
301
        }
159
28.8k
    }
160
768k
    for (int n = 0; n < MP_ARRAY_SIZE(mp_imgfmt_list); n++) {
161
741k
        const struct mp_imgfmt_entry *p = &mp_imgfmt_list[n];
162
741k
        if (p->name && bstr_equals0(name, p->name))
163
1.47k
            return IMGFMT_CUST_BASE + n;
164
741k
    }
165
27.4k
    return pixfmt2imgfmt(av_get_pix_fmt(mp_tprintf(80, "%.*s", BSTR_P(name))));
166
28.8k
}
167
168
char *mp_imgfmt_to_name_buf(char *buf, size_t buf_size, int fmt)
169
352k
{
170
352k
    const struct mp_imgfmt_entry *p = get_mp_desc(fmt);
171
352k
    const char *name = p ? p->name : NULL;
172
352k
    if (!name) {
173
352k
        const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(imgfmt2pixfmt(fmt));
174
352k
        if (pixdesc)
175
348k
            name = pixdesc->name;
176
352k
    }
177
352k
    if (!name)
178
4.16k
        name = "unknown";
179
352k
    snprintf(buf, buf_size, "%s", name);
180
352k
    int len = strlen(buf);
181
352k
    if (len > 2 && buf[len - 2] == MP_SELECT_LE_BE('l', 'b') && buf[len - 1] == 'e')
182
46.6k
        buf[len - 2] = '\0';
183
352k
    return buf;
184
352k
}
185
186
static void fill_pixdesc_layout(struct mp_imgfmt_desc *desc,
187
                                enum AVPixelFormat fmt,
188
                                const AVPixFmtDescriptor *pd)
189
1.01M
{
190
1.01M
    if (pd->flags & AV_PIX_FMT_FLAG_PAL ||
191
799k
        pd->flags & AV_PIX_FMT_FLAG_HWACCEL)
192
215k
        goto fail;
193
194
1.01M
    bool has_alpha = pd->flags & AV_PIX_FMT_FLAG_ALPHA;
195
796k
    if (pd->nb_components != 1 + has_alpha &&
196
693k
        pd->nb_components != 3 + has_alpha)
197
0
        goto fail;
198
199
    // Very convenient: we assume we're always on little endian, and FFmpeg
200
    // explicitly marks big endian formats => don't need to guess whether a
201
    // format is little endian, or not affected by byte order.
202
796k
    bool is_be = pd->flags & AV_PIX_FMT_FLAG_BE;
203
796k
    bool is_ne = MP_SELECT_LE_BE(false, true) == is_be;
204
205
    // Packed sub-sampled YUV is very... special.
206
796k
    bool is_packed_ss_yuv = pd->log2_chroma_w && !pd->log2_chroma_h &&
207
45.7k
        pd->comp[1].plane == 0 && pd->comp[2].plane == 0 &&
208
4.32k
        pd->nb_components == 3;
209
210
796k
    if (is_packed_ss_yuv)
211
4.32k
        desc->bpp[0] = pd->comp[1].step * 8;
212
213
    // Determine if there are any byte overlaps => relevant for determining
214
    // access unit for endian, since pixdesc does not expose this, and assumes
215
    // a weird model where you do separate memory fetches for each component.
216
796k
    bool any_shared_bytes = !!(pd->flags & AV_PIX_FMT_FLAG_BITSTREAM);
217
3.07M
    for (int c = 0; c < pd->nb_components; c++) {
218
4.61M
        for (int i = 0; i < c; i++) {
219
2.33M
            const AVComponentDescriptor *d1 = &pd->comp[c];
220
2.33M
            const AVComponentDescriptor *d2 = &pd->comp[i];
221
2.33M
            if (d1->plane == d2->plane) {
222
556k
                if (d1->offset + (d1->depth + 7) / 8u > d2->offset &&
223
409k
                    d2->offset + (d2->depth + 7) / 8u > d1->offset)
224
26.6k
                    any_shared_bytes = true;
225
556k
            }
226
2.33M
        }
227
2.27M
    }
228
229
796k
    int el_bits = (pd->flags & AV_PIX_FMT_FLAG_BITSTREAM) ? 1 : 8;
230
3.07M
    for (int c = 0; c < pd->nb_components; c++) {
231
2.27M
        const AVComponentDescriptor *d = &pd->comp[c];
232
2.27M
        if (d->plane >= MP_MAX_PLANES)
233
0
            goto fail;
234
235
2.27M
        desc->num_planes = MPMAX(desc->num_planes, d->plane + 1);
236
237
2.27M
        int plane_bits = desc->bpp[d->plane];
238
2.27M
        int c_bits = d->step * el_bits;
239
240
        // The first component wins, because either all components result in
241
        // the same value, or luma wins (luma always comes before chroma).
242
2.27M
        if (plane_bits) {
243
337k
            if (c_bits > plane_bits)
244
14
                goto fail; // inconsistent
245
1.93M
        } else {
246
1.93M
            desc->bpp[d->plane] = plane_bits = c_bits;
247
1.93M
        }
248
249
2.27M
        int shift = d->shift;
250
        // What the fuck: for some inexplicable reason, MONOB uses shift=7
251
        // in pixdesc, which is basically out of bounds. Pixdesc bug?
252
        // Make it behave like MONOW. (No, the bit-order is not different.)
253
2.27M
        if (fmt == AV_PIX_FMT_MONOBLACK)
254
925
            shift = 0;
255
256
2.27M
        int offset = d->offset * el_bits;
257
        // The pixdesc logic for reading and endian swapping is as follows
258
        // (reverse engineered from av_read_image_line2()):
259
        // - determine a word size that will include the component fully;
260
        //   this includes the "active" bits and the amount "shifted" away
261
        //   (for example shift=7/depth=18 => 32 bit word reading [31:0])
262
        // - the same format can use different word sizes (e.g. bgr565: the R
263
        //   component at offset 0 is read as 8 bit; BG is read as 16 bits)
264
        // - if BE flag is set, swap the word before proceeding
265
        // - extract via shift and mask derived by depth
266
2.27M
        int word = mp_round_next_power_of_2(MPMAX(d->depth + shift, 8));
267
        // The purpose of this is unknown. It's an absurdity fished out of
268
        // av_read_image_line2()'s implementation. It seems technically
269
        // unnecessary, and provides no information. On the other hand, it
270
        // compensates for seemingly bogus packed integer pixdescs; this
271
        // is "why" some formats use d->offset = -1.
272
2.27M
        if (is_be && el_bits == 8 && word == 8)
273
4.43k
            offset += 8;
274
        // Pixdesc's model sometimes requires accesses with varying word-sizes,
275
        // as seen in bgr565 and other formats. Also, it makes you read some
276
        // formats with multiple endian-dependent accesses, where accessing a
277
        // larger unit would make more sense. (Consider X2RGB10BE, for which
278
        // pixdesc wants you to perform 3 * 2 byte accesses, and swap each of
279
        // the read 16 bit words. What you really want is to swap the entire 4
280
        // byte thing, and then extract the components with bit shifts).
281
        // This is complete bullshit, so we transform it into word swaps before
282
        // further processing. Care needs to be taken to not change formats like
283
        // P010 or YA16 (prefer component accesses for them; P010 isn't even
284
        // representable, because endian_shift is for all planes).
285
        // As a heuristic, assume that if any components share a byte, the whole
286
        // pixel is read as a single memory access and endian swapped at once.
287
2.27M
        int access_size = 8;
288
2.27M
        if (plane_bits > 8) {
289
956k
            if (any_shared_bytes) {
290
34.8k
                access_size = plane_bits;
291
34.8k
                if (is_be && word != access_size) {
292
                    // Before: offset = 8*byte_offset (with word bits of data)
293
                    // After: offset = bit_offset into swapped endian_size word
294
4.47k
                    offset = access_size - word - offset;
295
4.47k
                }
296
921k
            } else {
297
921k
                access_size = word;
298
921k
            }
299
956k
        }
300
2.27M
        int endian_size = (access_size && !is_ne) ? access_size : 8;
301
2.27M
        int endian_shift = mp_log2(endian_size) - 3;
302
2.27M
        if (!MP_IS_POWER_OF_2(endian_size) || endian_shift < 0 || endian_shift > 3)
303
0
            goto fail;
304
2.27M
        if (desc->endian_shift && desc->endian_shift != endian_shift)
305
0
            goto fail;
306
2.27M
        desc->endian_shift = endian_shift;
307
308
        // We always use bit offsets; this doesn't lose any information,
309
        // and pixdesc is merely more redundant.
310
2.27M
        offset += shift;
311
2.27M
        if (offset < 0 || offset >= (1 << 6))
312
22
            goto fail;
313
2.27M
        if (offset + d->depth > plane_bits)
314
0
            goto fail;
315
2.27M
        if (d->depth < 0 || d->depth >= (1 << 6))
316
0
            goto fail;
317
2.27M
        desc->comps[c] = (struct mp_imgfmt_comp_desc){
318
2.27M
            .plane = d->plane,
319
2.27M
            .offset = offset,
320
2.27M
            .size = d->depth,
321
2.27M
        };
322
2.27M
    }
323
324
2.73M
    for (int p = 0; p < desc->num_planes; p++) {
325
1.94M
        if (!desc->bpp[p])
326
0
            goto fail; // plane doesn't exist
327
1.94M
    }
328
329
    // What the fuck: this is probably a pixdesc bug, so fix it.
330
796k
    if (fmt == AV_PIX_FMT_RGB8) {
331
2.69k
        desc->comps[2] = (struct mp_imgfmt_comp_desc){0, 0, 2};
332
2.69k
        desc->comps[1] = (struct mp_imgfmt_comp_desc){0, 2, 3};
333
2.69k
        desc->comps[0] = (struct mp_imgfmt_comp_desc){0, 5, 3};
334
2.69k
    }
335
336
    // Overlap test. If any shared bits are happening, this is not a format we
337
    // can represent (or it's something like Bayer: components in the same bits,
338
    // but different alternating lines).
339
796k
    bool any_shared_bits = false;
340
3.07M
    for (int c = 0; c < pd->nb_components; c++) {
341
4.61M
        for (int i = 0; i < c; i++) {
342
2.33M
            struct mp_imgfmt_comp_desc *c1 = &desc->comps[c];
343
2.33M
            struct mp_imgfmt_comp_desc *c2 = &desc->comps[i];
344
2.33M
            if (c1->plane == c2->plane) {
345
556k
                if (c1->offset + c1->size > c2->offset &&
346
390k
                    c2->offset + c2->size > c1->offset)
347
111
                    any_shared_bits = true;
348
556k
            }
349
2.33M
        }
350
2.27M
    }
351
352
796k
    if (any_shared_bits) {
353
148
        for (int c = 0; c < pd->nb_components; c++)
354
111
            desc->comps[c] = (struct mp_imgfmt_comp_desc){0};
355
37
    }
356
357
    // Many important formats have padding within an access word. For example
358
    // yuv420p10 has the upper 6 bit cleared to 0; P010 has the lower 6 bits
359
    // cleared to 0. Pixdesc cannot represent that these bits are 0. There are
360
    // other formats where padding is not guaranteed to be 0, but they are
361
    // described in the same way.
362
    // Apply a heuristic that is supposed to identify formats which use
363
    // guaranteed 0 padding. This could fail, but nobody said this pixdesc crap
364
    // is robust.
365
3.07M
    for (int c = 0; c < pd->nb_components; c++) {
366
2.27M
        struct mp_imgfmt_comp_desc *cd = &desc->comps[c];
367
        // Note: rgb444 would defeat our heuristic if we checked only per comp.
368
        //       also, exclude "bitstream" formats due to monow/monob
369
2.27M
        int fsize = MP_ALIGN_UP(cd->size, 8);
370
2.27M
        if (!any_shared_bytes && el_bits == 8 && fsize != cd->size &&
371
427k
            fsize - cd->size <= (1 << 3))
372
427k
        {
373
427k
            if (!(cd->offset % 8u)) {
374
202k
                cd->pad = -(fsize - cd->size);
375
202k
                cd->size = fsize;
376
224k
            } else if (!((cd->offset + cd->size) % 8u)) {
377
224k
                cd->pad = fsize - cd->size;
378
224k
                cd->size = fsize;
379
224k
                cd->offset = MP_ALIGN_DOWN(cd->offset, 8);
380
224k
            }
381
427k
        }
382
2.27M
    }
383
384
    // The alpha component always has ID 4 (index 3) in our representation, so
385
    // move the alpha component to there.
386
796k
    if (has_alpha && pd->nb_components < 4) {
387
9.80k
        desc->comps[3] = desc->comps[pd->nb_components - 1];
388
9.80k
        desc->comps[pd->nb_components - 1] = (struct mp_imgfmt_comp_desc){0};
389
9.80k
    }
390
391
796k
    if (is_packed_ss_yuv) {
392
4.32k
        desc->flags |= MP_IMGFLAG_PACKED_SS_YUV;
393
4.32k
        desc->bpp[0] /= 1 << pd->log2_chroma_w;
394
792k
    } else if (!any_shared_bits) {
395
792k
        desc->flags |= MP_IMGFLAG_HAS_COMPS;
396
792k
    }
397
398
796k
    return;
399
400
215k
fail:
401
1.07M
    for (int n = 0; n < 4; n++)
402
862k
        desc->comps[n] = (struct mp_imgfmt_comp_desc){0};
403
    // Average bit size fallback.
404
215k
    int num_planes = av_pix_fmt_count_planes(fmt);
405
215k
    desc->num_planes = MPCLAMP(num_planes, 0, MP_MAX_PLANES);
406
429k
    for (int p = 0; p < desc->num_planes; p++) {
407
213k
        int ls = av_image_get_linesize(fmt, 256, p);
408
213k
        desc->bpp[p] = ls > 0 ? ls * 8 / 256 : 0;
409
213k
    }
410
215k
}
411
412
static bool mp_imgfmt_get_desc_from_pixdesc(int mpfmt, struct mp_imgfmt_desc *out)
413
1.01M
{
414
1.01M
    enum AVPixelFormat fmt = imgfmt2pixfmt(mpfmt);
415
1.01M
    const AVPixFmtDescriptor *pd = av_pix_fmt_desc_get(fmt);
416
1.01M
    if (!pd || pd->nb_components > 4)
417
5.35k
        return false;
418
419
1.01M
    struct mp_imgfmt_desc desc = {
420
1.01M
        .id = mpfmt,
421
1.01M
        .chroma_xs = pd->log2_chroma_w,
422
1.01M
        .chroma_ys = pd->log2_chroma_h,
423
1.01M
    };
424
425
1.01M
    if (pd->flags & AV_PIX_FMT_FLAG_ALPHA)
426
305k
        desc.flags |= MP_IMGFLAG_ALPHA;
427
428
1.01M
    if (pd->flags & AV_PIX_FMT_FLAG_HWACCEL)
429
2.37k
        desc.flags |= MP_IMGFLAG_TYPE_HW;
430
431
    // Pixdesc does not provide a flag for XYZ, so this is the best we can do.
432
1.01M
    if (strncmp(pd->name, "xyz", 3) == 0) {
433
25.8k
        desc.flags |= MP_IMGFLAG_COLOR_XYZ;
434
986k
    } else if (pd->flags & AV_PIX_FMT_FLAG_RGB) {
435
162k
        desc.flags |= MP_IMGFLAG_COLOR_RGB;
436
823k
    } else if (fmt == AV_PIX_FMT_MONOBLACK || fmt == AV_PIX_FMT_MONOWHITE) {
437
15.9k
        desc.flags |= MP_IMGFLAG_COLOR_RGB;
438
807k
    } else if (fmt == AV_PIX_FMT_PAL8) {
439
213k
        desc.flags |= MP_IMGFLAG_COLOR_RGB | MP_IMGFLAG_TYPE_PAL8;
440
213k
    }
441
442
1.01M
    if (pd->flags & AV_PIX_FMT_FLAG_FLOAT)
443
32.8k
        desc.flags |= MP_IMGFLAG_TYPE_FLOAT;
444
445
    // Educated guess.
446
1.01M
    if (!(desc.flags & MP_IMGFLAG_COLOR_MASK) &&
447
594k
        !(desc.flags & MP_IMGFLAG_TYPE_HW))
448
592k
        desc.flags |= MP_IMGFLAG_COLOR_YUV;
449
450
1.01M
    desc.align_x = 1 << desc.chroma_xs;
451
1.01M
    desc.align_y = 1 << desc.chroma_ys;
452
453
1.01M
    fill_pixdesc_layout(&desc, fmt, pd);
454
455
1.01M
    if (desc.flags & (MP_IMGFLAG_HAS_COMPS | MP_IMGFLAG_PACKED_SS_YUV)) {
456
796k
        if (!(desc.flags & MP_IMGFLAG_TYPE_MASK))
457
763k
            desc.flags |= MP_IMGFLAG_TYPE_UINT;
458
796k
    }
459
460
1.01M
    if (desc.bpp[0] % 8u && (pd->flags & AV_PIX_FMT_FLAG_BITSTREAM))
461
17.2k
        desc.align_x = 8 / desc.bpp[0]; // expect power of 2
462
463
    // Very heuristical.
464
1.01M
    bool is_ne = !desc.endian_shift;
465
1.01M
    bool need_endian = (desc.comps[0].size % 8u && desc.bpp[0] > 8) ||
466
1.00M
                       desc.comps[0].size > 8;
467
468
1.01M
    if (need_endian) {
469
253k
        bool is_le = MP_SELECT_LE_BE(is_ne, !is_ne);
470
253k
        desc.flags |= is_le ? MP_IMGFLAG_LE : MP_IMGFLAG_BE;
471
758k
    } else {
472
758k
        desc.flags |= MP_IMGFLAG_LE | MP_IMGFLAG_BE;
473
758k
    }
474
475
1.01M
    *out = desc;
476
1.01M
    return true;
477
1.01M
}
478
479
bool mp_imgfmt_get_packed_yuv_locations(int imgfmt, uint8_t *luma_offsets)
480
312
{
481
312
    struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(imgfmt);
482
312
    if (!(desc.flags & MP_IMGFLAG_PACKED_SS_YUV))
483
0
        return false;
484
485
312
    mp_assert(desc.num_planes == 1);
486
487
    // Guess at which positions the additional luma samples are. We iterate
488
    // starting with the first byte, and then put a luma sample at places
489
    // not covered by other luma/chroma.
490
    // Pixdesc does not and can not provide this information. This heuristic
491
    // may fail in certain cases. What a load of bullshit, right?
492
312
    int lsize = desc.comps[0].size;
493
312
    int cur_offset = 0;
494
624
    for (int lsample = 1; lsample < (1 << desc.chroma_xs); lsample++) {
495
990
        while (1) {
496
990
            if (cur_offset + lsize > desc.bpp[0] * desc.align_x)
497
0
                return false;
498
990
            bool free = true;
499
2.34k
            for (int c = 0; c < 3; c++) {
500
2.03k
                struct mp_imgfmt_comp_desc *cd = &desc.comps[c];
501
2.03k
                if (!cd->size)
502
0
                    continue;
503
2.03k
                if (cd->offset + cd->size > cur_offset &&
504
990
                    cur_offset + lsize > cd->offset)
505
678
                {
506
678
                    free = false;
507
678
                    break;
508
678
                }
509
2.03k
            }
510
990
            if (free)
511
312
                break;
512
678
            cur_offset += lsize;
513
678
        }
514
312
        luma_offsets[lsample] = cur_offset;
515
312
        cur_offset += lsize;
516
312
    }
517
518
312
    luma_offsets[0] = desc.comps[0].offset;
519
312
    return true;
520
312
}
521
522
static bool get_native_desc(int mpfmt, struct mp_imgfmt_desc *desc)
523
1.02M
{
524
1.02M
    const struct mp_imgfmt_entry *p = get_mp_desc(mpfmt);
525
1.02M
    if (!p || !p->desc.flags)
526
1.01M
        return false;
527
528
5.73k
    *desc = p->desc;
529
530
    // Fill in some fields mp_imgfmt_entry.desc is not required to set.
531
532
5.73k
    desc->id = mpfmt;
533
534
28.6k
    for (int n = 0; n < MP_NUM_COMPONENTS; n++) {
535
22.9k
        struct mp_imgfmt_comp_desc *cd = &desc->comps[n];
536
22.9k
        if (cd->size)
537
16.2k
            desc->num_planes = MPMAX(desc->num_planes, cd->plane + 1);
538
22.9k
        desc->bpp[cd->plane] =
539
22.9k
            MPMAX(desc->bpp[cd->plane], MP_ALIGN_UP(cd->offset + cd->size, 8));
540
22.9k
    }
541
542
5.73k
    if (!desc->align_x && !desc->align_y) {
543
5.73k
        desc->align_x = 1 << desc->chroma_xs;
544
5.73k
        desc->align_y = 1 << desc->chroma_ys;
545
5.73k
    }
546
547
5.73k
    if (desc->num_planes)
548
5.50k
        desc->flags |= MP_IMGFLAG_HAS_COMPS | MP_IMGFLAG_NE;
549
550
5.73k
    if (!(desc->flags & MP_IMGFLAG_TYPE_MASK))
551
2.45k
        desc->flags |= MP_IMGFLAG_TYPE_UINT;
552
553
5.73k
    return true;
554
1.02M
}
555
556
int mp_imgfmt_desc_get_num_comps(const struct mp_imgfmt_desc *desc)
557
1.20M
{
558
1.20M
    int flags = desc->flags;
559
1.20M
    if (!(flags & MP_IMGFLAG_COLOR_MASK))
560
0
        return 0;
561
1.20M
    return 3 + (flags & MP_IMGFLAG_GRAY ? -2 : 0) + !!(flags & MP_IMGFLAG_ALPHA);
562
1.20M
}
563
564
struct mp_imgfmt_desc mp_imgfmt_get_desc(int mpfmt)
565
1.02M
{
566
1.02M
    struct mp_imgfmt_desc desc;
567
568
1.02M
    if (!get_native_desc(mpfmt, &desc) &&
569
1.01M
        !mp_imgfmt_get_desc_from_pixdesc(mpfmt, &desc))
570
5.35k
        return (struct mp_imgfmt_desc){0};
571
572
3.18M
    for (int p = 0; p < desc.num_planes; p++) {
573
2.17M
        desc.xs[p] = (p == 1 || p == 2) ? desc.chroma_xs : 0;
574
2.17M
        desc.ys[p] = (p == 1 || p == 2) ? desc.chroma_ys : 0;
575
2.17M
    }
576
577
1.01M
    bool is_ba = desc.num_planes > 0;
578
3.18M
    for (int p = 0; p < desc.num_planes; p++)
579
2.17M
        is_ba = !(desc.bpp[p] % 8u);
580
581
1.01M
    if (is_ba)
582
998k
        desc.flags |= MP_IMGFLAG_BYTE_ALIGNED;
583
584
1.01M
    if (desc.flags & MP_IMGFLAG_HAS_COMPS) {
585
797k
        if (desc.comps[3].size)
586
94.5k
            desc.flags |= MP_IMGFLAG_ALPHA;
587
588
        // Assuming all colors are (CCC+[A]) or (C+[A]), the latter being gray.
589
797k
        if (!desc.comps[1].size)
590
104k
            desc.flags |= MP_IMGFLAG_GRAY;
591
592
797k
        bool bb = true;
593
3.98M
        for (int n = 0; n < MP_NUM_COMPONENTS; n++) {
594
3.19M
            if (desc.comps[n].offset % 8u || desc.comps[n].size % 8u)
595
65.4k
                bb = false;
596
3.19M
        }
597
797k
        if (bb)
598
765k
            desc.flags |= MP_IMGFLAG_BYTES;
599
797k
    }
600
601
1.01M
    if ((desc.flags & (MP_IMGFLAG_YUV | MP_IMGFLAG_RGB))
602
989k
        && (desc.flags & MP_IMGFLAG_HAS_COMPS)
603
771k
        && (desc.flags & MP_IMGFLAG_BYTES)
604
739k
        && ((desc.flags & MP_IMGFLAG_TYPE_MASK) == MP_IMGFLAG_TYPE_UINT))
605
703k
    {
606
703k
        int cnt = mp_imgfmt_desc_get_num_comps(&desc);
607
703k
        bool same_depth = true;
608
2.50M
        for (int p = 0; p < desc.num_planes; p++)
609
1.79M
            same_depth &= desc.bpp[p] == desc.bpp[0];
610
703k
        if (same_depth && cnt == desc.num_planes) {
611
595k
            if (desc.flags & MP_IMGFLAG_YUV) {
612
523k
                desc.flags |= MP_IMGFLAG_YUV_P;
613
523k
            } else {
614
71.9k
                desc.flags |= MP_IMGFLAG_RGB_P;
615
71.9k
            }
616
595k
        }
617
703k
        if (cnt == 3 && desc.num_planes == 2 &&
618
10.7k
            desc.bpp[1] == desc.bpp[0] * 2 &&
619
10.7k
            (desc.flags & MP_IMGFLAG_YUV))
620
10.7k
        {
621
622
10.7k
            desc.flags |= MP_IMGFLAG_YUV_NV;
623
10.7k
        }
624
703k
    }
625
626
1.01M
    return desc;
627
1.02M
}
628
629
static bool validate_regular_imgfmt(const struct mp_regular_imgfmt *fmt)
630
16.2k
{
631
16.2k
    bool present[MP_NUM_COMPONENTS] = {0};
632
16.2k
    int n_comp = 0;
633
634
56.9k
    for (int n = 0; n < fmt->num_planes; n++) {
635
40.6k
        const struct mp_regular_imgfmt_plane *plane = &fmt->planes[n];
636
40.6k
        n_comp += plane->num_components;
637
40.6k
        if (n_comp > MP_NUM_COMPONENTS)
638
0
            return false;
639
40.6k
        if (!plane->num_components)
640
0
            return false; // no empty planes in between allowed
641
642
40.6k
        bool pad_only = true;
643
40.6k
        int chroma_luma = 0; // luma: 1, chroma: 2, both: 3
644
92.1k
        for (int i = 0; i < plane->num_components; i++) {
645
51.5k
            int comp = plane->components[i];
646
51.5k
            if (comp > MP_NUM_COMPONENTS)
647
0
                return false;
648
51.5k
            if (comp == 0)
649
924
                continue;
650
50.5k
            pad_only = false;
651
50.5k
            if (present[comp - 1])
652
0
                return false; // no duplicates
653
50.5k
            present[comp - 1] = true;
654
50.5k
            chroma_luma |= (comp == 2 || comp == 3) ? 2 : 1;
655
50.5k
        }
656
40.6k
        if (pad_only)
657
0
            return false; // no planes with only padding allowed
658
40.6k
        if ((fmt->chroma_xs > 0 || fmt->chroma_ys > 0) && chroma_luma == 3)
659
0
            return false; // separate chroma/luma planes required
660
40.6k
    }
661
662
16.2k
    if (!(present[0] || present[3]) ||  // at least component 1 or alpha needed
663
16.2k
        (present[1] && !present[0]) ||  // component 2 requires component 1
664
16.2k
        (present[2] && !present[1]))    // component 3 requires component 2
665
0
        return false;
666
667
16.2k
    return true;
668
16.2k
}
669
670
static enum pl_color_system get_forced_csp_from_flags(int flags)
671
263k
{
672
263k
    if (flags & MP_IMGFLAG_COLOR_XYZ)
673
11.1k
        return PL_COLOR_SYSTEM_XYZ;
674
675
252k
    if (flags & MP_IMGFLAG_COLOR_RGB)
676
62.3k
        return PL_COLOR_SYSTEM_RGB;
677
678
189k
    return PL_COLOR_SYSTEM_UNKNOWN;
679
252k
}
680
681
enum pl_color_system mp_imgfmt_get_forced_csp(int imgfmt)
682
246k
{
683
246k
    return get_forced_csp_from_flags(mp_imgfmt_get_desc(imgfmt).flags);
684
246k
}
685
686
static enum mp_component_type get_component_type_from_flags(int flags)
687
19.9k
{
688
19.9k
    if (flags & MP_IMGFLAG_TYPE_UINT)
689
16.7k
        return MP_COMPONENT_TYPE_UINT;
690
691
3.23k
    if (flags & MP_IMGFLAG_TYPE_FLOAT)
692
3.23k
        return MP_COMPONENT_TYPE_FLOAT;
693
694
0
    return MP_COMPONENT_TYPE_UNKNOWN;
695
3.23k
}
696
697
enum mp_component_type mp_imgfmt_get_component_type(int imgfmt)
698
0
{
699
0
    return get_component_type_from_flags(mp_imgfmt_get_desc(imgfmt).flags);
700
0
}
701
702
int mp_find_other_endian(int imgfmt)
703
0
{
704
0
    return pixfmt2imgfmt(av_pix_fmt_swap_endianness(imgfmt2pixfmt(imgfmt)));
705
0
}
706
707
bool mp_get_regular_imgfmt(struct mp_regular_imgfmt *dst, int imgfmt)
708
33.4k
{
709
33.4k
    struct mp_regular_imgfmt res = {0};
710
711
33.4k
    struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(imgfmt);
712
33.4k
    if (!desc.num_planes)
713
7.08k
        return false;
714
26.4k
    res.num_planes = desc.num_planes;
715
716
26.4k
    if (desc.endian_shift || !(desc.flags & MP_IMGFLAG_HAS_COMPS))
717
6.46k
        return false;
718
719
19.9k
    res.component_type = get_component_type_from_flags(desc.flags);
720
19.9k
    if (!res.component_type)
721
0
        return false;
722
723
19.9k
    struct mp_imgfmt_comp_desc *comp0 = &desc.comps[0];
724
19.9k
    if (comp0->size < 1 || comp0->size > 64 || (comp0->size % 8u))
725
3.69k
        return false;
726
727
16.2k
    res.component_size = comp0->size / 8u;
728
16.2k
    res.component_pad = comp0->pad;
729
730
56.9k
    for (int n = 0; n < res.num_planes; n++) {
731
40.6k
        if (desc.bpp[n] % comp0->size)
732
0
            return false;
733
40.6k
        res.planes[n].num_components = desc.bpp[n] / comp0->size;
734
40.6k
    }
735
736
81.2k
    for (int n = 0; n < MP_NUM_COMPONENTS; n++) {
737
64.9k
        struct mp_imgfmt_comp_desc *comp = &desc.comps[n];
738
64.9k
        if (!comp->size)
739
14.3k
            continue;
740
741
50.5k
        struct mp_regular_imgfmt_plane *plane = &res.planes[comp->plane];
742
743
50.5k
        res.num_planes = MPMAX(res.num_planes, comp->plane + 1);
744
745
        // We support uniform depth only.
746
50.5k
        if (comp->size != comp0->size || comp->pad != comp0->pad)
747
0
            return false;
748
749
        // Size-aligned only.
750
50.5k
        int pos = comp->offset / comp->size;
751
50.5k
        if (comp->offset != pos * comp->size || pos >= MP_NUM_COMPONENTS)
752
0
            return false;
753
754
50.5k
        if (plane->components[pos])
755
0
            return false;
756
50.5k
        plane->components[pos] = n + 1;
757
50.5k
    }
758
759
16.2k
    res.chroma_xs = desc.chroma_xs;
760
16.2k
    res.chroma_ys = desc.chroma_ys;
761
762
16.2k
    res.forced_csp = get_forced_csp_from_flags(desc.flags);
763
764
16.2k
    if (!validate_regular_imgfmt(&res))
765
0
        return false;
766
767
16.2k
    *dst = res;
768
16.2k
    return true;
769
16.2k
}
770
771
static bool regular_imgfmt_equals(struct mp_regular_imgfmt *a,
772
                                  struct mp_regular_imgfmt *b)
773
15.6k
{
774
15.6k
    if (a->component_type != b->component_type ||
775
12.3k
        a->component_size != b->component_size ||
776
7.70k
        a->num_planes     != b->num_planes ||
777
2.61k
        a->component_pad  != b->component_pad ||
778
1.69k
        a->forced_csp     != b->forced_csp ||
779
231
        a->chroma_xs      != b->chroma_xs ||
780
231
        a->chroma_ys      != b->chroma_ys)
781
15.4k
        return false;
782
783
1.00k
    for (int n = 0; n < a->num_planes; n++) {
784
770
        int num_comps = a->planes[n].num_components;
785
770
        if (num_comps != b->planes[n].num_components)
786
0
            return false;
787
1.54k
        for (int i = 0; i < num_comps; i++) {
788
770
            if (a->planes[n].components[i] != b->planes[n].components[i])
789
0
                return false;
790
770
        }
791
770
    }
792
793
231
    return true;
794
231
}
795
796
// Find a format that matches this one exactly.
797
int mp_find_regular_imgfmt(struct mp_regular_imgfmt *src)
798
231
{
799
32.8k
    for (int n = IMGFMT_START + 1; n < IMGFMT_END; n++) {
800
32.8k
        struct mp_regular_imgfmt f;
801
32.8k
        if (mp_get_regular_imgfmt(&f, n) && regular_imgfmt_equals(src, &f))
802
231
            return n;
803
32.8k
    }
804
0
    return 0;
805
231
}
806
807
// Compare the dst image formats, and return the one which can carry more data
808
// (e.g. higher depth, more color components, lower chroma subsampling, etc.),
809
// with respect to what is required to keep most of the src format.
810
// Returns the imgfmt, or 0 on error.
811
int mp_imgfmt_select_best(int dst1, int dst2, int src)
812
0
{
813
0
    enum AVPixelFormat dst1pxf = imgfmt2pixfmt(dst1);
814
0
    enum AVPixelFormat dst2pxf = imgfmt2pixfmt(dst2);
815
0
    enum AVPixelFormat srcpxf = imgfmt2pixfmt(src);
816
0
    enum AVPixelFormat dstlist[] = {dst1pxf, dst2pxf, AV_PIX_FMT_NONE};
817
0
    return pixfmt2imgfmt(avcodec_find_best_pix_fmt_of_list(dstlist, srcpxf, 1, 0));
818
0
}
819
820
// Same as mp_imgfmt_select_best(), but with a list of dst formats.
821
int mp_imgfmt_select_best_list(int *dst, int num_dst, int src)
822
0
{
823
0
    int best = 0;
824
0
    for (int n = 0; n < num_dst; n++)
825
0
        best = best ? mp_imgfmt_select_best(best, dst[n], src) : dst[n];
826
0
    return best;
827
0
}
828
829
bool mp_imgfmt_is_subsampled(enum mp_imgfmt fmt)
830
118k
{
831
118k
    struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(fmt);
832
118k
    return desc.chroma_xs || desc.chroma_ys;
833
118k
}
834
835
bool mp_imgfmt_is_420_subsampled(enum mp_imgfmt fmt)
836
0
{
837
0
    struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(fmt);
838
0
    return desc.chroma_xs == 1 && desc.chroma_ys == 1;
839
0
}