Coverage Report

Created: 2025-09-04 07:15

/src/mpv/video/out/dither.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Generate a dithering matrix for downsampling images.
3
 *
4
 * Copyright © 2013  Wessel Dankers <wsl@fruit.je>
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 <stdio.h>
23
#include <stdint.h>
24
#include <stdbool.h>
25
#include <stdlib.h>
26
#include <inttypes.h>
27
#include <string.h>
28
#include <assert.h>
29
#include <math.h>
30
31
#include <libavutil/lfg.h>
32
33
#include "misc/mp_assert.h"
34
#include "mpv_talloc.h"
35
#include "dither.h"
36
37
#define MAX_SIZEB 8
38
#define MAX_SIZE (1 << MAX_SIZEB)
39
#define MAX_SIZE2 (MAX_SIZE * MAX_SIZE)
40
41
0
#define WRAP_SIZE2(k, x) ((unsigned int)((unsigned int)(x) & ((k)->size2 - 1)))
42
0
#define XY(k, x, y) ((unsigned int)(((x) | ((y) << (k)->sizeb))))
43
44
struct ctx {
45
    unsigned int sizeb, size, size2;
46
    unsigned int gauss_radius;
47
    unsigned int gauss_middle;
48
    uint64_t gauss[MAX_SIZE2];
49
    unsigned int randomat[MAX_SIZE2];
50
    bool calcmat[MAX_SIZE2];
51
    uint64_t gaussmat[MAX_SIZE2];
52
    unsigned int unimat[MAX_SIZE2];
53
    AVLFG avlfg;
54
};
55
56
static void makegauss(struct ctx *k, unsigned int sizeb)
57
0
{
58
0
    mp_assert(sizeb >= 1 && sizeb <= MAX_SIZEB);
59
60
0
    av_lfg_init(&k->avlfg, 123);
61
62
0
    k->sizeb = sizeb;
63
0
    k->size = 1 << k->sizeb;
64
0
    k->size2 = k->size * k->size;
65
66
0
    k->gauss_radius = k->size / 2 - 1;
67
0
    k->gauss_middle = XY(k, k->gauss_radius, k->gauss_radius);
68
69
0
    unsigned int gauss_size = k->gauss_radius * 2 + 1;
70
0
    unsigned int gauss_size2 = gauss_size * gauss_size;
71
72
0
    for (unsigned int c = 0; c < k->size2; c++)
73
0
        k->gauss[c] = 0;
74
75
0
    double sigma = -log(1.5 / (double) UINT64_MAX * gauss_size2) / k->gauss_radius;
76
77
0
    for (unsigned int gy = 0; gy <= k->gauss_radius; gy++) {
78
0
        for (unsigned int gx = 0; gx <= gy; gx++) {
79
0
            int cx = (int)gx - k->gauss_radius;
80
0
            int cy = (int)gy - k->gauss_radius;
81
0
            int sq = cx * cx + cy * cy;
82
0
            double e = exp(-sqrt(sq) * sigma);
83
0
            uint64_t v = e / gauss_size2 * (double) UINT64_MAX;
84
0
            k->gauss[XY(k, gx, gy)] =
85
0
                k->gauss[XY(k, gy, gx)] =
86
0
                k->gauss[XY(k, gx, gauss_size - 1 - gy)] =
87
0
                k->gauss[XY(k, gy, gauss_size - 1 - gx)] =
88
0
                k->gauss[XY(k, gauss_size - 1 - gx, gy)] =
89
0
                k->gauss[XY(k, gauss_size - 1 - gy, gx)] =
90
0
                k->gauss[XY(k, gauss_size - 1 - gx, gauss_size - 1 - gy)] =
91
0
                k->gauss[XY(k, gauss_size - 1 - gy, gauss_size - 1 - gx)] = v;
92
0
        }
93
0
    }
94
0
    uint64_t total = 0;
95
0
    for (unsigned int c = 0; c < k->size2; c++) {
96
0
        uint64_t oldtotal = total;
97
0
        total += k->gauss[c];
98
0
        mp_assert(total >= oldtotal);
99
0
    }
100
0
}
101
102
static void setbit(struct ctx *k, unsigned int c)
103
0
{
104
0
    if (k->calcmat[c])
105
0
        return;
106
0
    k->calcmat[c] = true;
107
0
    uint64_t *m = k->gaussmat;
108
0
    uint64_t *me = k->gaussmat + k->size2;
109
0
    uint64_t *g = k->gauss + WRAP_SIZE2(k, k->gauss_middle + k->size2 - c);
110
0
    uint64_t *ge = k->gauss + k->size2;
111
0
    while (g < ge)
112
0
        *m++ += *g++;
113
0
    g = k->gauss;
114
0
    while (m < me)
115
0
        *m++ += *g++;
116
0
}
117
118
static unsigned int getmin(struct ctx *k)
119
0
{
120
0
    uint64_t min = UINT64_MAX;
121
0
    unsigned int resnum = 0;
122
0
    unsigned int size2 = k->size2;
123
0
    for (unsigned int c = 0; c < size2; c++) {
124
0
        if (k->calcmat[c])
125
0
            continue;
126
0
        uint64_t total = k->gaussmat[c];
127
0
        if (total <= min) {
128
0
            if (total != min) {
129
0
                min = total;
130
0
                resnum = 0;
131
0
            }
132
0
            k->randomat[resnum++] = c;
133
0
        }
134
0
    }
135
0
    if (resnum == 1)
136
0
        return k->randomat[0];
137
0
    if (resnum == size2)
138
0
        return size2 / 2;
139
0
    return k->randomat[av_lfg_get(&k->avlfg) % resnum];
140
0
}
141
142
static void makeuniform(struct ctx *k)
143
0
{
144
0
    unsigned int size2 = k->size2;
145
0
    for (unsigned int c = 0; c < size2; c++) {
146
0
        unsigned int r = getmin(k);
147
0
        setbit(k, r);
148
0
        k->unimat[r] = c;
149
0
    }
150
0
}
151
152
// out_matrix is a reactangular tsize * tsize array, where tsize = (1 << size).
153
void mp_make_fruit_dither_matrix(float *out_matrix, int size)
154
0
{
155
0
    struct ctx *k = talloc_zero(NULL, struct ctx);
156
0
    makegauss(k, size);
157
0
    makeuniform(k);
158
0
    float invscale = k->size2;
159
0
    for(unsigned int y = 0; y < k->size; y++) {
160
0
        for(unsigned int x = 0; x < k->size; x++)
161
0
            out_matrix[x + y * k->size] = k->unimat[XY(k, x, y)] / invscale;
162
0
    }
163
0
    talloc_free(k);
164
0
}
165
166
void mp_make_ordered_dither_matrix(unsigned char *m, int size)
167
0
{
168
0
    m[0] = 0;
169
0
    for (int sz = 1; sz < size; sz *= 2) {
170
0
        int offset[] = {sz*size, sz, sz * (size+1), 0};
171
0
        for (int i = 0; i < 4; i++)
172
0
            for (int y = 0; y < sz * size; y += size)
173
0
                for (int x = 0; x < sz; x++)
174
0
                    m[x+y+offset[i]] = m[x+y] * 4 + (3-i) * 256/size/size;
175
0
    }
176
0
}