Coverage Report

Created: 2025-11-24 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/src/video_output/video_widgets.c
Line
Count
Source
1
/*****************************************************************************
2
 * video_widgets.c : OSD widgets manipulation functions
3
 *****************************************************************************
4
 * Copyright (C) 2004-2010 VLC authors and VideoLAN
5
 *
6
 * Author: Yoann Peronneau <yoann@videolan.org>
7
 *         Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
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
24
/*****************************************************************************
25
 * Preamble
26
 *****************************************************************************/
27
#ifdef HAVE_CONFIG_H
28
# include "config.h"
29
#endif
30
#include <assert.h>
31
32
#include <vlc_common.h>
33
#include <vlc_vout.h>
34
#include <vlc_vout_osd.h>
35
36
#include <vlc_filter.h>
37
38
#include "vout_spuregion_helper.h"
39
40
#define STYLE_EMPTY 0
41
0
#define STYLE_FILLED 1
42
43
#define RGB_BLUE        0x2badde
44
#define RGB_ORANGE      0xf48b00
45
#define RGB_FILL        RGB_ORANGE
46
47
0
#define COL_TRANSPARENT 0
48
0
#define COL_WHITE       1
49
0
#define COL_FILL        2
50
0
#define COL_FILL_SHADE  3
51
52
0
#define SET_PALETTE_COLOR(id, rgb, alpha) \
53
0
{\
54
0
    uint8_t color[4] = { HEX2YUV(rgb), alpha };\
55
0
    memcpy( &palette.palette[id], &color, 4 );\
56
0
}
57
58
/**
59
 * Draws a rectangle at the given position in the region.
60
 * It may be filled (fill == STYLE_FILLED) or empty (fill == STYLE_EMPTY).
61
 */
62
static void DrawRect(subpicture_region_t *r, int fill, uint8_t color,
63
                     int x1, int y1, int x2, int y2)
64
0
{
65
0
    uint8_t *p    = r->p_picture->p->p_pixels;
66
0
    int     pitch = r->p_picture->p->i_pitch;
67
0
    if( x1 > x2 || y1 > y2 )
68
0
        return;
69
70
0
    if (fill == STYLE_FILLED) {
71
0
        if(x1 == 0 && x2 + 1 == r->p_picture->p->i_visible_pitch) {
72
0
            memset(&p[pitch * y1], color, pitch * (y2 - y1 + 1));
73
0
        } else {
74
0
            for (int y = y1; y <= y2; y++)
75
0
                memset(&p[x1 + pitch * y], color, x2 - x1 + 1);
76
0
        }
77
0
    } else {
78
0
        DrawRect(r, STYLE_FILLED, color, x1, y1, x1, y2);
79
0
        DrawRect(r, STYLE_FILLED, color, x2, y1, x2, y2);
80
0
        DrawRect(r, STYLE_FILLED, color, x1, y1, x2, y1);
81
0
        DrawRect(r, STYLE_FILLED, color, x1, y2, x2, y2);
82
0
    }
83
0
}
84
85
/**
86
 * Draws a triangle at the given position in the region.
87
 * It may be filled (fill == STYLE_FILLED) or empty (fill == STYLE_EMPTY).
88
 */
89
static void DrawTriangle(subpicture_region_t *r, int fill, uint8_t color,
90
                         int x1, int y1, int x2, int y2)
91
0
{
92
0
    uint8_t *p    = r->p_picture->p->p_pixels;
93
0
    int     pitch = r->p_picture->p->i_pitch;
94
0
    const int mid = y1 + (y2 - y1) / 2;
95
0
    const bool b_swap = (x1 > x2);
96
97
0
    for (int y = y1; y <= mid; y++) {
98
0
        const int h = y - y1;
99
0
        if (fill == STYLE_FILLED) {
100
0
            const int w = b_swap ? __MAX(x1 - h, x2) : __MIN(x1 + h, x2);
101
0
            DrawRect(r, STYLE_FILLED, color,
102
0
                     (b_swap) ? w : x1, y, (b_swap) ? x1 : w, y);
103
0
            DrawRect(r, STYLE_FILLED, color,
104
0
                     (b_swap) ? w : x1, y2 - h, (b_swap) ? x1 : w, y2 - h);
105
0
        } else {
106
0
            p[x1 +                     pitch * y       ] = color;
107
0
            p[x1 + (b_swap ? -h : h) + pitch * y       ] = color;
108
0
            p[x1 +                     pitch * (y2 - h)] = color;
109
0
            p[x1 + (b_swap ? -h : h) + pitch * (y2 - h)] = color;
110
0
        }
111
0
    }
112
0
}
113
114
/**
115
 * Create a region with a white transparent picture.
116
 */
117
static subpicture_region_t *OSDRegion(int x, int y, int width, int height)
118
0
{
119
0
    if( unlikely( width == 0 || height == 0 ) )
120
0
        return NULL;
121
122
0
    video_palette_t palette;
123
0
    SET_PALETTE_COLOR(COL_WHITE,       0xffffff, STYLE_ALPHA_OPAQUE)
124
0
    SET_PALETTE_COLOR(COL_TRANSPARENT, 0xffffff, STYLE_ALPHA_TRANSPARENT)
125
0
    SET_PALETTE_COLOR(COL_FILL,        RGB_FILL, 0xA0)
126
0
    SET_PALETTE_COLOR(COL_FILL_SHADE,  RGB_FILL, 0x25)
127
0
    palette.i_entries = 4;
128
129
0
    video_format_t fmt;
130
0
    video_format_Init(&fmt, VLC_CODEC_YUVP);
131
0
    fmt.i_width          =
132
0
    fmt.i_visible_width  = width;
133
0
    fmt.i_height         =
134
0
    fmt.i_visible_height = height;
135
0
    fmt.i_sar_num        = 1;
136
0
    fmt.i_sar_den        = 1;
137
0
    fmt.p_palette        = &palette;
138
139
0
    subpicture_region_t *r = subpicture_region_New(&fmt);
140
0
    if (!r)
141
0
        return NULL;
142
0
    r->i_x = x;
143
0
    r->i_y = y;
144
145
0
    return r;
146
0
}
147
148
/**
149
 * Create the region for an OSD slider.
150
 * Types are: OSD_HOR_SLIDER and OSD_VERT_SLIDER.
151
 */
152
0
#define SLIDER_MARGIN_BASE 0.10
153
static subpicture_region_t *OSDSlider(vlc_osd_widget_type type, int position,
154
                                      const unsigned i_visible_width,
155
                                      const unsigned i_visible_height)
156
0
{
157
0
    const unsigned size = __MAX(i_visible_width, i_visible_height);
158
0
    const unsigned margin = size * SLIDER_MARGIN_BASE;
159
0
    const unsigned marginbottom = margin * 0.2;
160
0
    const unsigned marginright = margin * 0.5;
161
0
    unsigned i_padding = __MIN(1, size * 0.25); /* small sizes */
162
163
0
    unsigned x, y;
164
0
    unsigned width, height;
165
0
    if (type == OSD_HOR_SLIDER) {
166
0
        width  = __MAX(i_visible_width - 2 * margin, 1);
167
0
        height = __MAX(i_visible_height * 0.01,      1);
168
0
        x      = __MIN(margin, i_visible_width - width);
169
0
        y      = __MAX(i_visible_height - marginbottom, 0);
170
0
    } else {
171
0
        width  = __MAX(i_visible_width * 0.010,       1);
172
0
        height = __MAX(i_visible_height - 2 * margin, 1);
173
0
        x      = __MAX(i_visible_width - marginright, 0);
174
0
        y      = __MIN(margin, i_visible_height - height);
175
0
    }
176
177
0
    if( (width < 1 + 2 * i_padding) || (height < 1 + 2 * i_padding) )
178
0
        return NULL;
179
180
0
    subpicture_region_t *r = OSDRegion(x, y, width, height);
181
0
    if( !r)
182
0
        return NULL;
183
184
0
    int pos_x = i_padding;
185
0
    int pos_y, pos_xend;
186
0
    int pos_yend = height - 1 - i_padding;
187
188
0
    if (type == OSD_HOR_SLIDER ) {
189
0
        pos_y = i_padding;
190
0
        pos_xend = pos_x + (width - 2 * i_padding) * position / 100;
191
0
    } else {
192
0
        pos_y = height - (height - 2 * i_padding) * position / 100;
193
0
        pos_xend = width - 1 - i_padding;
194
0
    }
195
196
    /* one full fill is faster than drawing outline */
197
0
    DrawRect(r, STYLE_FILLED, COL_FILL_SHADE, 0, 0, width - 1, height - 1);
198
0
    DrawRect(r, STYLE_FILLED, COL_FILL, pos_x, pos_y, pos_xend, pos_yend);
199
200
0
    return r;
201
0
}
202
203
/**
204
 * Create the region for an OSD slider.
205
 * Types are: OSD_PLAY_ICON, OSD_PAUSE_ICON, OSD_SPEAKER_ICON, OSD_MUTE_ICON
206
 */
207
static subpicture_region_t *OSDIcon(vlc_osd_widget_type type,
208
                                    const unsigned i_visible_width,
209
                                    const unsigned i_visible_height)
210
0
{
211
0
    const unsigned int size_ratio = 20;
212
0
    const unsigned int margin_ratio = 14;
213
214
0
    const unsigned int size   = __MAX(i_visible_width, i_visible_height);
215
0
    if( size < size_ratio )
216
0
        return NULL;
217
218
0
    const unsigned int width  = size / size_ratio;
219
0
    const unsigned int height = width;
220
0
    const unsigned int margin = size / margin_ratio;
221
0
    const int x      = i_visible_width - margin - width;
222
0
    const int y      =                   margin;
223
224
0
    subpicture_region_t *r = OSDRegion(__MAX(x, 0),
225
0
                                       __MIN(y, (int)i_visible_height - (int)height),
226
0
                                       width, height);
227
0
    if (!r)
228
0
        return NULL;
229
230
0
    DrawRect(r, STYLE_FILLED, COL_TRANSPARENT, 0, 0, width - 1, height - 1);
231
232
0
    if (type == OSD_PAUSE_ICON) {
233
0
        int bar_width = width / 3;
234
0
        DrawRect(r, STYLE_FILLED, COL_WHITE, 0, 0, bar_width - 1, height -1);
235
0
        DrawRect(r, STYLE_FILLED, COL_WHITE, width - bar_width, 0, width - 1, height - 1);
236
0
    } else if (type == OSD_PLAY_ICON) {
237
0
        int mid   = height >> 1;
238
0
        int delta = (width - mid) >> 1;
239
0
        int y2    = ((height - 1) >> 1) * 2;
240
0
        DrawTriangle(r, STYLE_FILLED, COL_WHITE, delta, 0, width - delta, y2);
241
0
    } else {
242
0
        int mid   = height >> 1;
243
0
        int delta = (width - mid) >> 1;
244
0
        int y2    = ((height - 1) >> 1) * 2;
245
0
        DrawRect(r, STYLE_FILLED, COL_WHITE, delta, mid / 2, width - delta, height - 1 - mid / 2);
246
0
        DrawTriangle(r, STYLE_FILLED, COL_WHITE, width - delta, 0, delta, y2);
247
0
        if (type == OSD_MUTE_ICON) {
248
0
            for(unsigned y1 = 0; y1 < height; y1++)
249
0
                DrawRect(r, STYLE_FILLED, COL_FILL, y1, y1, __MIN(y1 + delta, width - 1), y1);
250
0
        }
251
0
    }
252
0
    return r;
253
0
}
254
255
typedef struct {
256
    vlc_osd_widget_type type;
257
    int value;
258
} osdwidget_spu_updater_sys_t;
259
260
static void OSDWidgetUpdate(subpicture_t *subpic,
261
                            const struct vlc_spu_updater_configuration *cfg)
262
0
{
263
0
    osdwidget_spu_updater_sys_t *sys = subpic->updater.sys;
264
0
    subpicture_region_t *p_region;
265
266
0
    vlc_spu_regions_Clear( &subpic->regions );
267
268
0
    subpic->i_original_picture_width  = cfg->display_width;
269
0
    subpic->i_original_picture_height = cfg->display_height;
270
0
    if (sys->type == OSD_HOR_SLIDER || sys->type == OSD_VERT_SLIDER)
271
0
        p_region = OSDSlider(sys->type, sys->value, cfg->display_width, cfg->display_height);
272
0
    else
273
0
        p_region = OSDIcon(sys->type, cfg->display_width, cfg->display_height);
274
0
    if (p_region)
275
0
    {
276
0
        p_region->b_absolute = true;
277
0
        p_region->b_in_window = true;
278
0
        vlc_spu_regions_push(&subpic->regions, p_region);
279
0
    }
280
0
}
281
282
static void OSDWidgetDestroy(subpicture_t *subpic)
283
0
{
284
0
    free(subpic->updater.sys);
285
0
}
286
287
static void OSDWidget(vout_thread_t *vout, int channel, vlc_osd_widget_type type, int position)
288
0
{
289
0
    if (!var_InheritBool(vout, "osd"))
290
0
        return;
291
0
    if (type == OSD_HOR_SLIDER || type == OSD_VERT_SLIDER)
292
0
        position = VLC_CLIP(position, 0, 100);
293
294
0
    osdwidget_spu_updater_sys_t *sys = malloc(sizeof(*sys));
295
0
    if (!sys)
296
0
        return;
297
0
    sys->type     = type;
298
0
    sys->value    = position;
299
300
0
    static const struct vlc_spu_updater_ops spu_ops =
301
0
    {
302
0
        .update   = OSDWidgetUpdate,
303
0
        .destroy  = OSDWidgetDestroy,
304
0
    };
305
306
0
    subpicture_updater_t updater = {
307
0
        .sys = sys,
308
0
        .ops = &spu_ops,
309
0
    };
310
0
    subpicture_t *subpic = subpicture_New(&updater);
311
0
    if (!subpic) {
312
0
        free(sys);
313
0
        return;
314
0
    }
315
316
0
    subpic->i_channel  = channel;
317
0
    subpic->i_start    = vlc_tick_now();
318
0
    subpic->i_stop     = subpic->i_start + VLC_TICK_FROM_MS(1200);
319
0
    subpic->b_ephemer  = true;
320
0
    subpic->b_fade     = true;
321
322
0
    vout_PutSubpicture(vout, subpic);
323
0
}
324
325
void vout_OSDSlider(vout_thread_t *vout, int channel, int position, vlc_osd_widget_type type)
326
0
{
327
0
    OSDWidget(vout, channel, type, position);
328
0
}
329
330
void vout_OSDIcon(vout_thread_t *vout, int channel, vlc_osd_widget_type type )
331
0
{
332
0
    OSDWidget(vout, channel, type, 0);
333
0
}