Coverage Report

Created: 2025-08-28 07:26

/src/mpv/video/out/bitmap_packer.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Calculate how to pack bitmap rectangles into a larger surface
3
 *
4
 * Copyright 2009, 2012 Uoti Urpala
5
 *
6
 * This file is part of mpv.
7
 *
8
 * mpv is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU Lesser General Public
10
 * License as published by the Free Software Foundation; either
11
 * version 2.1 of the License, or (at your option) any later version.
12
 *
13
 * mpv is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public
19
 * License along with mpv.  If not, see <http://www.gnu.org/licenses/>.
20
 */
21
22
#include <stdlib.h>
23
#include <assert.h>
24
#include <stdio.h>
25
#include <limits.h>
26
27
#include "mpv_talloc.h"
28
#include "bitmap_packer.h"
29
#include "common/common.h"
30
31
#define IS_POWER_OF_2(x) (((x) > 0) && !(((x) - 1) & (x)))
32
33
void packer_reset(struct bitmap_packer *packer)
34
0
{
35
0
    struct bitmap_packer old = *packer;
36
0
    *packer = (struct bitmap_packer) {
37
0
        .w_max = old.w_max,
38
0
        .h_max = old.h_max,
39
0
    };
40
0
    talloc_free_children(packer);
41
0
}
42
43
void packer_get_bb(struct bitmap_packer *packer, struct pos out_bb[2])
44
10
{
45
10
    out_bb[0] = (struct pos) {0};
46
10
    out_bb[1] = (struct pos) {packer->used_width, packer->used_height};
47
10
}
48
49
16.3k
#define HEIGHT_SORT_BITS 4
50
static int size_index(int s)
51
2.19k
{
52
2.19k
    int n = mp_log2(s);
53
2.19k
    return (n << HEIGHT_SORT_BITS)
54
2.19k
       + ((- 1 - (s << HEIGHT_SORT_BITS >> n)) & ((1 << HEIGHT_SORT_BITS) - 1));
55
2.19k
}
56
57
/* Pack the given rectangles into an area of size w * h.
58
 * The size of each rectangle is read from in[i].x / in[i].y.
59
 * The height of each rectangle must be less than 65536.
60
 * 'scratch' must point to work memory for num_rects+16 ints.
61
 * The packed position for rectangle number i is set in out[i].
62
 * Return 0 on success, -1 if the rectangles did not fit in w*h.
63
 *
64
 * The rectangles are placed in rows in order approximately sorted by
65
 * height (the approximate sorting is simpler than a full one would be,
66
 * and allows the algorithm to work in linear time). Additionally, to
67
 * reduce wasted space when there are a few tall rectangles, empty
68
 * lower-right parts of rows are filled recursively when the size of
69
 * rectangles in the row drops past a power-of-two threshold. So if a
70
 * row starts with rectangles of size 3x50, 10x40 and 5x20 then the
71
 * free rectangle with corners (13, 20)-(w, 50) is filled recursively.
72
 */
73
static int pack_rectangles(struct pos *in, struct pos *out, int num_rects,
74
                           int w, int h, int *scratch, int *used_width)
75
29
{
76
29
    int bins[16 << HEIGHT_SORT_BITS];
77
29
    int sizes[16 << HEIGHT_SORT_BITS] = { 0 };
78
1.12k
    for (int i = 0; i < num_rects; i++)
79
1.09k
        sizes[size_index(in[i].y)]++;
80
29
    int idx = 0;
81
493
    for (int i = 0; i < 16 << HEIGHT_SORT_BITS; i += 1 << HEIGHT_SORT_BITS) {
82
7.88k
        for (int j = 0; j < 1 << HEIGHT_SORT_BITS; j++) {
83
7.42k
            bins[i + j] = idx;
84
7.42k
            idx += sizes[i + j];
85
7.42k
        }
86
464
        scratch[idx++] = -1;
87
464
    }
88
1.12k
    for (int i = 0; i < num_rects; i++)
89
1.09k
        scratch[bins[size_index(in[i].y)]++] = i;
90
493
    for (int i = 0; i < 16; i++)
91
464
        bins[i] = bins[i << HEIGHT_SORT_BITS] - sizes[i << HEIGHT_SORT_BITS];
92
29
    struct {
93
29
        int size, x, bottom;
94
29
    } stack[16] = {{15, 0, h}}, s = {0};
95
29
    int stackpos = 1;
96
29
    int y;
97
199
    while (stackpos) {
98
170
        y = s.bottom;
99
170
        s = stack[--stackpos];
100
170
        s.size++;
101
1.25k
        while (s.size--) {
102
1.08k
            int maxy = -1;
103
1.08k
            int obj;
104
1.62k
            while ((obj = scratch[bins[s.size]]) >= 0) {
105
815
                int bottom = y + in[obj].y;
106
815
                if (bottom > s.bottom)
107
35
                    break;
108
780
                int right = s.x + in[obj].x;
109
780
                if (right > w)
110
240
                    break;
111
540
                bins[s.size]++;
112
540
                out[obj] = (struct pos){s.x, y};
113
540
                num_rects--;
114
540
                if (maxy < 0)
115
141
                    stack[stackpos++] = s;
116
540
                s.x = right;
117
540
                maxy = MPMAX(maxy, bottom);
118
540
            }
119
1.08k
            *used_width = MPMAX(*used_width, s.x);
120
1.08k
            if (maxy > 0)
121
141
                s.bottom = maxy;
122
1.08k
        }
123
170
    }
124
29
    return num_rects ? -1 : y;
125
29
}
126
127
int packer_pack(struct bitmap_packer *packer)
128
10
{
129
10
    if (packer->count == 0)
130
0
        return 0;
131
10
    int w_orig = packer->w, h_orig = packer->h;
132
10
    struct pos *in = packer->in;
133
10
    int xmax = 0, ymax = 0;
134
348
    for (int i = 0; i < packer->count; i++) {
135
338
        if (in[i].x <= 0 || in[i].y <= 0) {
136
0
            in[i] = (struct pos){0, 0};
137
338
        } else {
138
338
            in[i].x += packer->padding * 2;
139
338
            in[i].y += packer->padding * 2;
140
338
        }
141
338
        if (in[i].x < 0 || in [i].x > 65535 || in[i].y < 0 || in[i].y > 65535) {
142
0
            fprintf(stderr, "Invalid OSD / subtitle bitmap size\n");
143
0
            abort();
144
0
        }
145
338
        xmax = MPMAX(xmax, in[i].x);
146
338
        ymax = MPMAX(ymax, in[i].y);
147
338
    }
148
10
    if (xmax > packer->w)
149
7
        packer->w = 1 << (mp_log2(xmax - 1) + 1);
150
10
    if (ymax > packer->h)
151
7
        packer->h = 1 << (mp_log2(ymax - 1) + 1);
152
29
    while (1) {
153
29
        int used_width = 0;
154
29
        int y = pack_rectangles(in, packer->result, packer->count,
155
29
                                packer->w, packer->h,
156
29
                                packer->scratch, &used_width);
157
29
        if (y >= 0) {
158
10
            packer->used_width = MPMIN(used_width, packer->w);
159
10
            packer->used_height = MPMIN(y, packer->h);
160
10
            mp_assert(packer->w == 0 || IS_POWER_OF_2(packer->w));
161
10
            mp_assert(packer->h == 0 || IS_POWER_OF_2(packer->h));
162
10
            if (packer->padding) {
163
348
                for (int i = 0; i < packer->count; i++) {
164
338
                    packer->result[i].x += packer->padding;
165
338
                    packer->result[i].y += packer->padding;
166
338
                }
167
10
            }
168
10
            return packer->w != w_orig || packer->h != h_orig;
169
10
        }
170
19
        int w_max = packer->w_max > 0 ? packer->w_max : INT_MAX;
171
19
        int h_max = packer->h_max > 0 ? packer->h_max : INT_MAX;
172
19
        if (packer->w <= packer->h && packer->w != w_max)
173
8
            packer->w = MPMIN(packer->w * 2, w_max);
174
11
        else if (packer->h != h_max)
175
11
            packer->h = MPMIN(packer->h * 2, h_max);
176
0
        else {
177
0
            packer->w = w_orig;
178
0
            packer->h = h_orig;
179
0
            return -1;
180
0
        }
181
19
    }
182
10
}
183
184
void packer_set_size(struct bitmap_packer *packer, int size)
185
40
{
186
40
    packer->count = size;
187
40
    if (size <= packer->asize)
188
31
        return;
189
9
    packer->asize = MPMAX(packer->asize * 2, size);
190
9
    talloc_free(packer->result);
191
9
    talloc_free(packer->scratch);
192
9
    packer->in = talloc_realloc(packer, packer->in, struct pos, packer->asize);
193
9
    packer->result = talloc_array_ptrtype(packer, packer->result,
194
9
                                          packer->asize);
195
9
    packer->scratch = talloc_array_ptrtype(packer, packer->scratch,
196
9
                                           packer->asize + 16);
197
9
}