Coverage Report

Created: 2025-08-25 07:17

/src/vlc/modules/demux/avi/bitmapinfoheader.h
Line
Count
Source (jump to first uncovered line)
1
/*****************************************************************************
2
 * bitmapinfoheader.h : BITMAPINFOHEADER handling
3
 *****************************************************************************
4
 * Copyright (C) 2001-2009 VLC authors and VideoLAN
5
 *               2018      VideoLabs, VLC authors and VideoLAN
6
 *
7
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8
 *
9
 * This program is free software; you can redistribute it and/or modify it
10
 * under the terms of the GNU Lesser General Public License as published by
11
 * the Free Software Foundation; either version 2.1 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 * GNU Lesser General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Lesser General Public License
20
 * along with this program; if not, write to the Free Software Foundation,
21
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22
 *****************************************************************************/
23
#include <vlc_common.h>
24
#include <vlc_es.h>
25
#include <vlc_codecs.h>
26
27
#include <assert.h>
28
#include <limits.h>
29
30
/* biCompression / Others are FourCC */
31
#ifndef BI_RGB
32
13.9k
 #define BI_RGB             0x0000
33
 #define BI_RLE8            0x0001
34
 #define BI_RLE4            0x0002
35
6.72k
 #define BI_BITFIELDS       0x0003
36
 #define BI_JPEG            0x0004
37
 #define BI_PNG             0x0005
38
#endif
39
#define BI_ALPHAFIELDS      0x0006
40
#define BI_CMYK             0x000B
41
#define BI_CMYKRLE8         0x000C
42
#define BI_CMYKRLE4         0x000D
43
44
static const struct
45
{
46
    vlc_fourcc_t codec;
47
    uint32_t i_rmask, i_gmask, i_bmask, i_amask;
48
    uint8_t depth;
49
} bitmap_rgb_masks[] = {
50
    { VLC_CODEC_XRGB,       0x00ff0000,
51
                            0x0000ff00,
52
                            0x000000ff,
53
                            0x00000000, 32 },
54
    { VLC_CODEC_XBGR,       0x000000ff,
55
                            0x0000ff00,
56
                            0x00ff0000,
57
                            0x00000000, 32 },
58
    { VLC_CODEC_RGBX,       0xff000000,
59
                            0x00ff0000,
60
                            0x0000ff00,
61
                            0x00000000, 32 },
62
    { VLC_CODEC_BGRX,       0x0000ff00,
63
                            0x00ff0000,
64
                            0xff000000,
65
                            0x00000000, 32 },
66
    { VLC_CODEC_ARGB,       0x00ff0000,
67
                            0x0000ff00,
68
                            0x000000ff,
69
                            0xff000000, 32 },
70
    { VLC_CODEC_ABGR,       0x000000ff,
71
                            0x0000ff00,
72
                            0x00ff0000,
73
                            0xff000000, 32 },
74
    { VLC_CODEC_RGBA,       0xff000000,
75
                            0x00ff0000,
76
                            0x0000ff00,
77
                            0x000000ff, 32 },
78
    { VLC_CODEC_BGRA,       0x0000ff00,
79
                            0x00ff0000,
80
                            0xff000000,
81
                            0x000000ff, 32 },
82
83
    { VLC_CODEC_RGB24,      0xff0000,
84
                            0x00ff00,
85
                            0x0000ff,
86
                            0x000000, 24 },
87
    { VLC_CODEC_BGR24,      0x0000ff,
88
                            0x00ff00,
89
                            0xff0000,
90
                            0x000000, 24 },
91
92
    { VLC_CODEC_RGB565LE,   0xf800,
93
                            0x07e0,
94
                            0x001f,
95
                            0x0000, 16 },
96
    { VLC_CODEC_BGR565LE,   0x001f,
97
                            0x07e0,
98
                            0xf800,
99
                            0x0000, 16 },
100
101
    { VLC_CODEC_RGB555LE,   0x7c00,
102
                            0x03e0,
103
                            0x001f,
104
                            0x0000, 15 },
105
    { VLC_CODEC_BGR555LE,   0x001f,
106
                            0x03e0,
107
                            0x7c00,
108
                            0x0000, 15 },
109
};
110
111
struct bitmapinfoheader_properties
112
{
113
    bool b_flipped;
114
    unsigned i_stride;
115
};
116
117
static inline int ParseBitmapInfoHeader( const VLC_BITMAPINFOHEADER *p_bih, size_t i_bih,
118
                                         es_format_t *fmt,
119
                                         struct bitmapinfoheader_properties *p_props )
120
6.69k
{
121
    /* Extradata is the remainder of the chunk less the BIH */
122
6.69k
    const uint8_t *p_bihextra = (const uint8_t *) &p_bih[1];
123
6.69k
    size_t i_bihextra;
124
6.69k
    if( i_bih <= INT_MAX - sizeof(VLC_BITMAPINFOHEADER) &&
125
6.69k
            i_bih >= sizeof(VLC_BITMAPINFOHEADER) )
126
6.69k
        i_bihextra = i_bih - sizeof(VLC_BITMAPINFOHEADER);
127
8
    else
128
8
        i_bihextra = 0;
129
130
6.69k
    if( p_bih->biCompression == BI_RGB ||
131
6.69k
        p_bih->biCompression == BI_BITFIELDS )
132
565
    {
133
565
        uint32_t biCompression = p_bih->biCompression;
134
565
        switch( p_bih->biBitCount )
135
565
        {
136
24
            case 32:
137
51
            case 24:
138
470
            case 16:
139
545
            case 15:
140
545
                break;
141
1
            case 9: /* <- TODO check that */
142
1
                fmt->video.i_chroma = fmt->i_codec = VLC_CODEC_I410;
143
1
                break;
144
3
            case 8:
145
3
                if ( p_bih->biClrUsed )
146
2
                    fmt->video.i_chroma = fmt->i_codec = VLC_CODEC_RGBP;
147
1
                else
148
1
                    fmt->video.i_chroma = fmt->i_codec = VLC_CODEC_GREY;
149
3
                break;
150
16
            default:
151
16
                if( p_bih->biClrUsed < 8 )
152
12
                    fmt->video.i_chroma = fmt->i_codec = VLC_CODEC_RGBP;
153
16
                break;
154
565
        }
155
156
565
        if( p_bih->biCompression == BI_BITFIELDS ) /* Only 16 & 32 */
157
23
        {
158
23
            if( i_bihextra >= 3 * sizeof(uint32_t) )
159
9
            {
160
9
                uint32_t rmask = GetDWLE( &p_bihextra[0] );
161
9
                uint32_t gmask = GetDWLE( &p_bihextra[4] );
162
9
                uint32_t bmask = GetDWLE( &p_bihextra[8] );
163
9
                uint32_t amask = i_bihextra >= 4 * sizeof(uint32_t) ? GetDWLE( &p_bihextra[12] ) : 0;
164
165
9
                vlc_fourcc_t known_chroma = 0;
166
135
                for( size_t i=0; i<ARRAY_SIZE(bitmap_rgb_masks); i++ )
167
126
                {
168
126
                    if (bitmap_rgb_masks[i].depth == p_bih->biBitCount &&
169
126
                        bitmap_rgb_masks[i].i_rmask == rmask &&
170
126
                        bitmap_rgb_masks[i].i_gmask == gmask &&
171
126
                        bitmap_rgb_masks[i].i_bmask == bmask &&
172
126
                        bitmap_rgb_masks[i].i_amask == amask )
173
0
                    {
174
0
                        known_chroma = bitmap_rgb_masks[i].codec;
175
0
                        break;
176
0
                    }
177
126
                }
178
179
9
                if (known_chroma != 0)
180
0
                {
181
0
                    fmt->video.i_chroma = fmt->i_codec = known_chroma;
182
0
                }
183
9
                else
184
9
                {
185
                    // unsupported alpha mask
186
9
                    return VLC_ENOTSUP;
187
9
                }
188
9
            }
189
14
            else
190
14
            {
191
                // bogus mask size, assume BI_RGB positions
192
14
                biCompression = BI_RGB;
193
14
            }
194
23
        }
195
542
        else if( fmt->i_codec == VLC_CODEC_RGBP )
196
12
        {
197
            /* The palette should not be included in biSize, but come
198
             * directly after BITMAPINFORHEADER in the BITMAPINFO structure */
199
12
            fmt->video.p_palette = malloc( sizeof(video_palette_t) );
200
12
            if ( fmt->video.p_palette )
201
12
            {
202
12
                fmt->video.p_palette->i_entries = __MIN(i_bihextra/4, 256);
203
1.00k
                for( int k = 0; k < fmt->video.p_palette->i_entries; k++ )
204
994
                {
205
3.97k
                    for( int j = 0; j < 3; j++ )
206
2.98k
                        fmt->video.p_palette->palette[k][j] = p_bihextra[4*k+j];
207
994
                    fmt->video.p_palette->palette[k][3] = 0xFF;
208
994
                }
209
12
            }
210
12
        }
211
556
        if (biCompression == BI_RGB)
212
556
        {
213
556
            vlc_fourcc_t bi_rgb_chroma;
214
556
            switch (p_bih->biBitCount)
215
556
            {
216
21
                case 32: bi_rgb_chroma = VLC_CODEC_XBGR; break;
217
26
                case 24: bi_rgb_chroma = VLC_CODEC_BGR24; break;
218
417
                case 16: bi_rgb_chroma = VLC_CODEC_BGR565LE; break;
219
75
                case 15: bi_rgb_chroma = VLC_CODEC_BGR555LE; break;
220
17
                default: return VLC_EINVAL;
221
556
            }
222
539
            fmt->video.i_chroma = fmt->i_codec = bi_rgb_chroma;
223
539
        }
224
225
539
        p_props->i_stride = p_bih->biWidth * (p_bih->biBitCount >> 3);
226
        /* Unintuitively RGB DIB are always coded from bottom to top,
227
         * except when height is negative */
228
539
        if ( p_bih->biHeight <= INT32_MAX )
229
479
            p_props->b_flipped = true;
230
        /* else
231
         *     set below to positive value */
232
539
    }
233
6.13k
    else /* Compressed codecs */
234
6.13k
    {
235
6.13k
        fmt->i_codec = vlc_fourcc_GetCodec( VIDEO_ES, p_bih->biCompression );
236
237
        /* Copy extradata if any */
238
6.13k
        if( i_bihextra > 0 )
239
5.84k
        {
240
5.84k
            fmt->p_extra = malloc( i_bihextra );
241
5.84k
            if( unlikely(fmt->p_extra == NULL) )
242
0
                return VLC_ENOMEM;
243
5.84k
            fmt->i_extra = i_bihextra;
244
5.84k
            memcpy( fmt->p_extra, p_bihextra, i_bihextra );
245
5.84k
        }
246
6.13k
    }
247
248
6.67k
    video_format_Setup( &fmt->video, fmt->i_codec,
249
6.67k
                        p_bih->biWidth, p_bih->biHeight,
250
6.67k
                        p_bih->biWidth, p_bih->biHeight,
251
6.67k
                        fmt->video.i_sar_num, fmt->video.i_sar_den );
252
253
    /* Uncompressed Bitmap or YUV, YUV being always top to bottom whatever
254
     * height sign is, and compressed must also not use flip, so positive
255
     * values only here */
256
6.67k
    if ( fmt->video.i_height > INT32_MAX )
257
1.89k
    {
258
1.89k
        fmt->video.i_visible_height =
259
1.89k
        fmt->video.i_height = -1 * p_bih->biHeight;
260
1.89k
    }
261
262
6.67k
    return VLC_SUCCESS;
263
6.69k
}
264
265
static inline int CreateBitmapInfoHeader( const es_format_t *fmt,
266
                                          VLC_BITMAPINFOHEADER *p_bih,
267
                                          uint8_t **p_bih_extra,
268
                                          size_t *pi_total )
269
0
{
270
0
    const vlc_chroma_description_t *desc =
271
0
        vlc_fourcc_GetChromaDescription(fmt->i_codec);
272
0
    uint16_t biBitCount = desc != NULL ? desc->pixel_size * 8 : 0;
273
0
    uint32_t biCompression;
274
0
    bool b_has_alpha = false;
275
0
    switch( fmt->i_codec )
276
0
    {
277
0
        case VLC_CODEC_XRGB:
278
0
        case VLC_CODEC_BGR24:
279
0
        case VLC_CODEC_BGR565LE:
280
0
        case VLC_CODEC_BGR555LE:
281
0
        case VLC_CODEC_RGBP:
282
0
        case VLC_CODEC_GREY:
283
0
            biCompression = BI_RGB;
284
0
            break;
285
0
        case VLC_CODEC_BGRX:
286
0
        case VLC_CODEC_XBGR:
287
0
        case VLC_CODEC_RGBX:
288
0
            biCompression = BI_BITFIELDS;
289
0
            break;
290
0
        case VLC_CODEC_BGRA:
291
0
        case VLC_CODEC_RGBA:
292
0
        case VLC_CODEC_ARGB:
293
0
        case VLC_CODEC_ABGR:
294
0
            biCompression = BI_BITFIELDS;
295
0
            b_has_alpha = true;
296
0
            break;
297
0
        case VLC_CODEC_RGB24:
298
0
            return VLC_EINVAL;
299
0
        case VLC_CODEC_MP4V:
300
0
            biCompression = VLC_FOURCC( 'X', 'V', 'I', 'D' );
301
0
            break;
302
0
        default:
303
0
            biCompression = fmt->i_original_fourcc
304
0
                ? fmt->i_original_fourcc : fmt->i_codec;
305
0
            break;
306
0
    }
307
0
308
0
    size_t i_bih_extra = 0;
309
0
    size_t i_bmiColors = 0;
310
0
    if( biCompression == BI_BITFIELDS )
311
0
        i_bmiColors = (b_has_alpha) ? 16 : 12;
312
0
    else if ( fmt->i_codec == VLC_CODEC_RGBP )
313
0
        i_bmiColors = fmt->video.p_palette ? (fmt->video.p_palette->i_entries * 4) : 0;
314
0
    else
315
0
        i_bih_extra = fmt->i_extra;
316
0
317
0
    *p_bih_extra = malloc( i_bih_extra + i_bmiColors );
318
0
    if( *p_bih_extra == NULL && (i_bih_extra + i_bmiColors) != 0 )
319
0
        return VLC_ENOMEM;
320
0
321
0
    uint8_t *p_bmiColors = *p_bih_extra;
322
0
    p_bih->biClrUsed = 0;
323
0
    if( biCompression == BI_BITFIELDS )
324
0
    {
325
0
        uint32_t i_rmask, i_gmask, i_bmask, i_amask;
326
0
        size_t i=0;
327
0
        for( ; i<ARRAY_SIZE(bitmap_rgb_masks); i++ )
328
0
        {
329
0
            if ( bitmap_rgb_masks[i].codec == fmt->i_codec )
330
0
            {
331
0
                i_rmask = bitmap_rgb_masks[i].i_rmask;
332
0
                i_gmask = bitmap_rgb_masks[i].i_gmask;
333
0
                i_bmask = bitmap_rgb_masks[i].i_bmask;
334
0
                i_amask = bitmap_rgb_masks[i].i_amask;
335
0
                break;
336
0
            }
337
0
        }
338
0
        if (i == ARRAY_SIZE(bitmap_rgb_masks))
339
0
            vlc_assert_unreachable();
340
0
341
0
        SetDWLE( &p_bmiColors[0], i_rmask );
342
0
        SetDWLE( &p_bmiColors[4], i_gmask );
343
0
        SetDWLE( &p_bmiColors[8], i_bmask );
344
0
        if( b_has_alpha )
345
0
        {
346
0
            SetDWLE( &p_bmiColors[12], i_amask );
347
0
        }
348
0
    }
349
0
    else if( fmt->i_codec == VLC_CODEC_RGBP )
350
0
    {
351
0
        for( int i = 0; i < fmt->video.p_palette->i_entries; i++ )
352
0
        {
353
0
            for( int j = 0; i < 3; i++ )
354
0
                p_bmiColors[i * 4 + j] = fmt->video.p_palette->palette[i][j];
355
0
            p_bmiColors[i * 4 + 3] = 0;
356
0
        }
357
0
        p_bih->biClrUsed = fmt->video.p_palette->i_entries;
358
0
    }
359
0
    else if( fmt->i_extra )
360
0
    {
361
0
        memcpy( *p_bih_extra, fmt->p_extra, fmt->i_extra );
362
0
    }
363
0
364
0
    p_bih->biSize = sizeof(VLC_BITMAPINFOHEADER) + i_bih_extra;
365
0
    p_bih->biCompression = biCompression;
366
0
    p_bih->biBitCount = biBitCount;
367
0
    p_bih->biWidth = fmt->video.i_visible_width;
368
0
    p_bih->biHeight = fmt->video.i_visible_height;
369
0
    p_bih->biPlanes = 1;
370
0
    p_bih->biSizeImage = 0;
371
0
    p_bih->biXPelsPerMeter = 0;
372
0
    p_bih->biYPelsPerMeter = 0;
373
0
    p_bih->biClrImportant = 0;
374
0
375
0
    *pi_total = i_bih_extra + i_bmiColors;
376
0
    return VLC_SUCCESS;
377
0
}