Coverage Report

Created: 2026-06-13 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mpv/video/out/bitmap_packer.c
Line
Count
Source
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
260
{
45
260
    out_bb[0] = (struct pos) {0};
46
260
    out_bb[1] = (struct pos) {packer->used_width, packer->used_height};
47
260
}
48
49
251k
#define HEIGHT_SORT_BITS 4
50
static int size_index(int s)
51
17.8k
{
52
17.8k
    int n = mp_log2(s);
53
17.8k
    return (n << HEIGHT_SORT_BITS)
54
17.8k
       + ((- 1 - (s << HEIGHT_SORT_BITS >> n)) & ((1 << HEIGHT_SORT_BITS) - 1));
55
17.8k
}
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
587
{
76
587
    int bins[16 << HEIGHT_SORT_BITS];
77
587
    int sizes[16 << HEIGHT_SORT_BITS] = { 0 };
78
9.50k
    for (int i = 0; i < num_rects; i++)
79
8.92k
        sizes[size_index(in[i].y)]++;
80
587
    int idx = 0;
81
9.97k
    for (int i = 0; i < 16 << HEIGHT_SORT_BITS; i += 1 << HEIGHT_SORT_BITS) {
82
159k
        for (int j = 0; j < 1 << HEIGHT_SORT_BITS; j++) {
83
150k
            bins[i + j] = idx;
84
150k
            idx += sizes[i + j];
85
150k
        }
86
9.39k
        scratch[idx++] = -1;
87
9.39k
    }
88
9.50k
    for (int i = 0; i < num_rects; i++)
89
8.92k
        scratch[bins[size_index(in[i].y)]++] = i;
90
9.97k
    for (int i = 0; i < 16; i++)
91
9.39k
        bins[i] = bins[i << HEIGHT_SORT_BITS] - sizes[i << HEIGHT_SORT_BITS];
92
587
    struct {
93
587
        int size, x, bottom;
94
587
    } stack[16] = {{15, 0, h}}, s = {0};
95
587
    int stackpos = 1;
96
587
    int y;
97
2.84k
    while (stackpos) {
98
2.25k
        y = s.bottom;
99
2.25k
        s = stack[--stackpos];
100
2.25k
        s.size++;
101
20.1k
        while (s.size--) {
102
17.8k
            int maxy = -1;
103
17.8k
            int obj;
104
22.0k
            while ((obj = scratch[bins[s.size]]) >= 0) {
105
6.60k
                int bottom = y + in[obj].y;
106
6.60k
                if (bottom > s.bottom)
107
506
                    break;
108
6.09k
                int right = s.x + in[obj].x;
109
6.09k
                if (right > w)
110
1.89k
                    break;
111
4.20k
                bins[s.size]++;
112
4.20k
                out[obj] = (struct pos){s.x, y};
113
4.20k
                num_rects--;
114
4.20k
                if (maxy < 0)
115
1.66k
                    stack[stackpos++] = s;
116
4.20k
                s.x = right;
117
4.20k
                maxy = MPMAX(maxy, bottom);
118
4.20k
            }
119
17.8k
            *used_width = MPMAX(*used_width, s.x);
120
17.8k
            if (maxy > 0)
121
1.66k
                s.bottom = maxy;
122
17.8k
        }
123
2.25k
    }
124
587
    return num_rects ? -1 : y;
125
587
}
126
127
int packer_pack(struct bitmap_packer *packer)
128
260
{
129
260
    if (packer->count == 0)
130
0
        return 0;
131
260
    int w_orig = packer->w, h_orig = packer->h;
132
260
    struct pos *in = packer->in;
133
260
    int xmax = 0, ymax = 0;
134
2.68k
    for (int i = 0; i < packer->count; i++) {
135
2.42k
        if (in[i].x <= 0 || in[i].y <= 0) {
136
0
            in[i] = (struct pos){0, 0};
137
2.42k
        } else {
138
2.42k
            in[i].x += packer->padding * 2;
139
2.42k
            in[i].y += packer->padding * 2;
140
2.42k
        }
141
2.42k
        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
2.42k
        xmax = MPMAX(xmax, in[i].x);
146
2.42k
        ymax = MPMAX(ymax, in[i].y);
147
2.42k
    }
148
260
    if (xmax > packer->w)
149
252
        packer->w = 1 << (mp_log2(xmax - 1) + 1);
150
260
    if (ymax > packer->h)
151
252
        packer->h = 1 << (mp_log2(ymax - 1) + 1);
152
587
    while (1) {
153
587
        int used_width = 0;
154
587
        int y = pack_rectangles(in, packer->result, packer->count,
155
587
                                packer->w, packer->h,
156
587
                                packer->scratch, &used_width);
157
587
        if (y >= 0) {
158
260
            packer->used_width = MPMIN(used_width, packer->w);
159
260
            packer->used_height = MPMIN(y, packer->h);
160
260
            mp_assert(packer->w == 0 || IS_POWER_OF_2(packer->w));
161
260
            mp_assert(packer->h == 0 || IS_POWER_OF_2(packer->h));
162
260
            if (packer->padding) {
163
2.68k
                for (int i = 0; i < packer->count; i++) {
164
2.42k
                    packer->result[i].x += packer->padding;
165
2.42k
                    packer->result[i].y += packer->padding;
166
2.42k
                }
167
260
            }
168
260
            return packer->w != w_orig || packer->h != h_orig;
169
260
        }
170
327
        int w_max = packer->w_max > 0 ? packer->w_max : INT_MAX;
171
327
        int h_max = packer->h_max > 0 ? packer->h_max : INT_MAX;
172
327
        if (packer->w <= packer->h && packer->w != w_max)
173
109
            packer->w = MPMIN(packer->w * 2, w_max);
174
218
        else if (packer->h != h_max)
175
218
            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
327
    }
182
260
}
183
184
void packer_set_size(struct bitmap_packer *packer, int size)
185
1.11k
{
186
1.11k
    packer->count = size;
187
1.11k
    if (size <= packer->asize)
188
855
        return;
189
255
    packer->asize = MPMAX(packer->asize * 2, size);
190
255
    talloc_free(packer->result);
191
255
    talloc_free(packer->scratch);
192
255
    packer->in = talloc_realloc(packer, packer->in, struct pos, packer->asize);
193
255
    packer->result = talloc_array_ptrtype(packer, packer->result,
194
255
                                          packer->asize);
195
255
    packer->scratch = talloc_array_ptrtype(packer, packer->scratch,
196
255
                                           packer->asize + 16);
197
255
}