| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * Copyright (c) 2026 Michael Niedermayer <michael-ffmpeg@niedermayer.cc> | ||
| 3 | * | ||
| 4 | * This file is part of FFmpeg. | ||
| 5 | * | ||
| 6 | * FFmpeg is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU Lesser General Public | ||
| 8 | * License as published by the Free Software Foundation; either | ||
| 9 | * version 2.1 of the License, or (at your option) any later version. | ||
| 10 | * | ||
| 11 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 14 | * Lesser General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU Lesser General Public | ||
| 17 | * License along with FFmpeg; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 19 | */ | ||
| 20 | |||
| 21 | /** | ||
| 22 | * @file | ||
| 23 | * Convert packed RGB/BGR to PAL8 with a per-frame palette whose colors sit | ||
| 24 | * on a face-centered cubic (FCC) lattice. | ||
| 25 | * | ||
| 26 | * The FCC lattice is realized as the D3 checkerboard lattice: the points | ||
| 27 | * (i,j,k) with an even coordinate sum, scaled so that the density option is | ||
| 28 | * the number of lattice steps spanning one color axis (0..255). Only the | ||
| 29 | * lattice points actually used by a frame enter its palette; if a frame | ||
| 30 | * uses more than 256 of them, the used colors are reduced by iteratively | ||
| 31 | * dropping the color whose removal has the least impact and mapping its | ||
| 32 | * pixels to the nearest remaining color. | ||
| 33 | */ | ||
| 34 | |||
| 35 | #include <math.h> | ||
| 36 | #include "libavutil/lfg.h" | ||
| 37 | #include "libavutil/mem.h" | ||
| 38 | #include "libavutil/opt.h" | ||
| 39 | #include "libavutil/pixdesc.h" | ||
| 40 | #include "avfilter.h" | ||
| 41 | #include "filters.h" | ||
| 42 | #include "formats.h" | ||
| 43 | #include "video.h" | ||
| 44 | |||
| 45 | #define MAX_DENSITY 255 | ||
| 46 | #define MAX_DENSITY_ALPHA 64 | ||
| 47 | |||
| 48 | enum dithering_mode { | ||
| 49 | DITHERING_NONE, | ||
| 50 | DITHERING_BAYER, | ||
| 51 | DITHERING_BLUE_NOISE, | ||
| 52 | DITHERING_FLOYD_STEINBERG, | ||
| 53 | NB_DITHERING | ||
| 54 | }; | ||
| 55 | |||
| 56 | enum refine_mode { | ||
| 57 | REFINE_NONE, | ||
| 58 | REFINE_FULL, | ||
| 59 | REFINE_RESIDUAL, | ||
| 60 | REFINE_BATCHED, | ||
| 61 | NB_REFINE | ||
| 62 | }; | ||
| 63 | |||
| 64 | /* void-and-cluster blue noise mask parameters */ | ||
| 65 | #define VC_SHIFT 6 | ||
| 66 | #define VC_SIZE (1 << VC_SHIFT) | ||
| 67 | #define VC_AREA (VC_SIZE * VC_SIZE) | ||
| 68 | #define VC_MASK (VC_SIZE - 1) | ||
| 69 | #define VC_SIGMA 1.5f | ||
| 70 | #define VC_RADIUS 7 | ||
| 71 | #define VC_KSIZE (2 * VC_RADIUS + 1) | ||
| 72 | |||
| 73 | typedef struct PalEntry { | ||
| 74 | int pos; ///< lattice cell position, ((i * dim + j) * dim + k) [* dim + l] | ||
| 75 | uint8_t ci[4]; ///< lattice indices of the color | ||
| 76 | uint8_t alive; ///< still part of the palette while reducing | ||
| 77 | int nn; ///< nearest live color while reducing, then palette slot | ||
| 78 | int d2; ///< squared RGB(A) distance to nn | ||
| 79 | int64_t count; ///< pixels quantizing to this color, incl. absorbed ones | ||
| 80 | } PalEntry; | ||
| 81 | |||
| 82 | typedef struct LatticePalContext { | ||
| 83 | const AVClass *class; | ||
| 84 | int density; ///< lattice steps per color axis | ||
| 85 | int dither; | ||
| 86 | int max_colors; ///< palette entries to use at most | ||
| 87 | int alpha; ///< quantize the alpha channel too (D4 lattice) | ||
| 88 | int refine; ///< rediffuse the error of dropped colors | ||
| 89 | |||
| 90 | int ro, go, bo, ao; ///< byte offsets of R, G, B, A in an input pixel, ao < 0: opaque | ||
| 91 | int nc; ///< quantized components, 3 or 4 | ||
| 92 | int pixstep; ///< bytes per input pixel | ||
| 93 | float scale; ///< density / 255 | ||
| 94 | uint8_t idx2val[MAX_DENSITY + 1]; ///< lattice index -> 8-bit component value | ||
| 95 | int ordered_dither[4][8 * 8]; ///< per-channel bayer offsets spanning one lattice period | ||
| 96 | int *blue_dither[4]; ///< per-channel blue noise offsets spanning one lattice period | ||
| 97 | int32_t *err[2]; ///< Floyd-Steinberg error rows, nc ints per pixel | ||
| 98 | |||
| 99 | int dim; ///< density + 1, lattice cells per axis | ||
| 100 | int min_gap; ///< smallest idx2val increment | ||
| 101 | int32_t *cell; ///< dim^nc table: lattice cell -> list index + 1, 0 if unused | ||
| 102 | uint32_t *pixpos; ///< per-pixel cell position of the quantized color | ||
| 103 | PalEntry *list; ///< colors used by the current frame | ||
| 104 | int nb_used; | ||
| 105 | int nb_alloc; | ||
| 106 | int *alive_arr; ///< compact list of live color indices while reducing | ||
| 107 | int *alive_pos; ///< position of each live color in alive_arr | ||
| 108 | int nb_alive; ///< entries in alive_arr, 0 outside reduction/remap | ||
| 109 | int alive_alloc; | ||
| 110 | uint32_t prev_pal[AVPALETTE_COUNT]; ///< previous frame's palette, for stable slot assignment | ||
| 111 | int *touched; ///< cells filled on demand by the refinement pass | ||
| 112 | int nb_touched; | ||
| 113 | int touched_alloc; | ||
| 114 | } LatticePalContext; | ||
| 115 | |||
| 116 | #define OFFSET(x) offsetof(LatticePalContext, x) | ||
| 117 | #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM | ||
| 118 | static const AVOption latticepal_options[] = { | ||
| 119 | { "density", "set the number of lattice steps spanning one color axis", OFFSET(density), AV_OPT_TYPE_INT, {.i64=20}, 1, MAX_DENSITY, FLAGS }, | ||
| 120 | { "max_colors", "set the maximum number of palette entries to use", OFFSET(max_colors), AV_OPT_TYPE_INT, {.i64=256}, 2, 256, FLAGS }, | ||
| 121 | { "alpha", "quantize the alpha channel too, on a 4 dimensional lattice", OFFSET(alpha), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS }, | ||
| 122 | { "refine", "rediffuse the error of dropped colors against the final palette", OFFSET(refine), AV_OPT_TYPE_INT, {.i64=REFINE_NONE}, 0, NB_REFINE-1, FLAGS, .unit = "refine_mode" }, | ||
| 123 | { "none", "no refinement", 0, AV_OPT_TYPE_CONST, {.i64=REFINE_NONE}, INT_MIN, INT_MAX, FLAGS, .unit = "refine_mode" }, | ||
| 124 | { "full", "rediffuse the whole frame against the final palette", 0, AV_OPT_TYPE_CONST, {.i64=REFINE_FULL}, INT_MIN, INT_MAX, FLAGS, .unit = "refine_mode" }, | ||
| 125 | { "residual", "diffuse only the residual of the dropped colors", 0, AV_OPT_TYPE_CONST, {.i64=REFINE_RESIDUAL}, INT_MIN, INT_MAX, FLAGS, .unit = "refine_mode" }, | ||
| 126 | { "batched", "interleave removal and rediffusion in geometric batches", 0, AV_OPT_TYPE_CONST, {.i64=REFINE_BATCHED}, INT_MIN, INT_MAX, FLAGS, .unit = "refine_mode" }, | ||
| 127 | { "dither", "select dithering mode", OFFSET(dither), AV_OPT_TYPE_INT, {.i64=DITHERING_FLOYD_STEINBERG}, 0, NB_DITHERING-1, FLAGS, .unit = "dithering_mode" }, | ||
| 128 | { "none", "no dithering", 0, AV_OPT_TYPE_CONST, {.i64=DITHERING_NONE}, INT_MIN, INT_MAX, FLAGS, .unit = "dithering_mode" }, | ||
| 129 | { "bayer", "ordered 8x8 bayer dithering", 0, AV_OPT_TYPE_CONST, {.i64=DITHERING_BAYER}, INT_MIN, INT_MAX, FLAGS, .unit = "dithering_mode" }, | ||
| 130 | { "blue_noise", "void-and-cluster blue noise dithering", 0, AV_OPT_TYPE_CONST, {.i64=DITHERING_BLUE_NOISE}, INT_MIN, INT_MAX, FLAGS, .unit = "dithering_mode" }, | ||
| 131 | { "floyd_steinberg", "Floyd-Steinberg error diffusion", 0, AV_OPT_TYPE_CONST, {.i64=DITHERING_FLOYD_STEINBERG}, INT_MIN, INT_MAX, FLAGS, .unit = "dithering_mode" }, | ||
| 132 | { NULL } | ||
| 133 | }; | ||
| 134 | |||
| 135 | AVFILTER_DEFINE_CLASS(latticepal); | ||
| 136 | |||
| 137 | 6 | static int query_formats(const AVFilterContext *ctx, | |
| 138 | AVFilterFormatsConfig **cfg_in, | ||
| 139 | AVFilterFormatsConfig **cfg_out) | ||
| 140 | { | ||
| 141 | static const enum AVPixelFormat in_fmts[] = { | ||
| 142 | AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24, | ||
| 143 | AV_PIX_FMT_RGBA, AV_PIX_FMT_BGRA, | ||
| 144 | AV_PIX_FMT_ARGB, AV_PIX_FMT_ABGR, | ||
| 145 | AV_PIX_FMT_RGB0, AV_PIX_FMT_BGR0, | ||
| 146 | AV_PIX_FMT_0RGB, AV_PIX_FMT_0BGR, | ||
| 147 | AV_PIX_FMT_NONE | ||
| 148 | }; | ||
| 149 | static const enum AVPixelFormat out_fmts[] = { AV_PIX_FMT_PAL8, AV_PIX_FMT_NONE }; | ||
| 150 | int ret; | ||
| 151 | |||
| 152 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
|
6 | if ((ret = ff_formats_ref(ff_make_pixel_format_list(in_fmts), &cfg_in[0]->formats)) < 0) |
| 153 | ✗ | return ret; | |
| 154 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
|
6 | if ((ret = ff_formats_ref(ff_make_pixel_format_list(out_fmts), &cfg_out[0]->formats)) < 0) |
| 155 | ✗ | return ret; | |
| 156 | 6 | return 0; | |
| 157 | } | ||
| 158 | |||
| 159 | /* Classic swscale ordered dither matrix (libswscale/output.c), 73 levels. */ | ||
| 160 | static const uint8_t dither_8x8_73[8][8] = { | ||
| 161 | { 0, 55, 14, 68, 3, 58, 17, 72, }, | ||
| 162 | { 37, 18, 50, 32, 40, 22, 54, 35, }, | ||
| 163 | { 9, 64, 5, 59, 13, 67, 8, 63, }, | ||
| 164 | { 46, 27, 41, 23, 49, 31, 44, 26, }, | ||
| 165 | { 2, 57, 16, 71, 1, 56, 15, 70, }, | ||
| 166 | { 39, 21, 52, 34, 38, 19, 51, 33, }, | ||
| 167 | { 11, 66, 7, 62, 10, 65, 6, 60, }, | ||
| 168 | { 48, 30, 43, 25, 47, 29, 42, 24, }, | ||
| 169 | }; | ||
| 170 | |||
| 171 | /** | ||
| 172 | * Add (sign > 0) or remove (sign < 0) the Gaussian energy contribution of a | ||
| 173 | * minority pixel at position pos on the VC_SIZE x VC_SIZE torus. | ||
| 174 | */ | ||
| 175 | ✗ | static void vc_splat(float *energy, const float *kern, int pos, float sign) | |
| 176 | { | ||
| 177 | ✗ | const int px = pos & VC_MASK, py = pos >> VC_SHIFT; | |
| 178 | |||
| 179 | ✗ | for (int dy = -VC_RADIUS; dy <= VC_RADIUS; dy++) { | |
| 180 | ✗ | const int y = (py + dy) & VC_MASK; | |
| 181 | ✗ | const float *krow = &kern[(dy + VC_RADIUS) * VC_KSIZE + VC_RADIUS]; | |
| 182 | ✗ | float *erow = &energy[y << VC_SHIFT]; | |
| 183 | |||
| 184 | ✗ | for (int dx = -VC_RADIUS; dx <= VC_RADIUS; dx++) | |
| 185 | ✗ | erow[(px + dx) & VC_MASK] += sign * krow[dx]; | |
| 186 | } | ||
| 187 | ✗ | } | |
| 188 | |||
| 189 | /** | ||
| 190 | * Position of the extreme energy value among the cells whose bit equals | ||
| 191 | * want_set: the tightest cluster (find_max, among minority pixels) or the | ||
| 192 | * largest void (!find_max, among majority pixels). | ||
| 193 | */ | ||
| 194 | ✗ | static int vc_extreme(const float *energy, const uint8_t *bits, int want_set, int find_max) | |
| 195 | { | ||
| 196 | ✗ | int best = -1; | |
| 197 | ✗ | float beste = 0.f; | |
| 198 | |||
| 199 | ✗ | for (int i = 0; i < VC_AREA; i++) { | |
| 200 | ✗ | if (bits[i] != want_set) | |
| 201 | ✗ | continue; | |
| 202 | ✗ | if (best < 0 || (find_max ? energy[i] > beste : energy[i] < beste)) { | |
| 203 | ✗ | best = i; | |
| 204 | ✗ | beste = energy[i]; | |
| 205 | } | ||
| 206 | } | ||
| 207 | ✗ | return best; | |
| 208 | } | ||
| 209 | |||
| 210 | /** | ||
| 211 | * Generate a void-and-cluster blue noise rank matrix (Ulichney 1993): | ||
| 212 | * every cell gets a unique rank in [0, VC_AREA). | ||
| 213 | * | ||
| 214 | * On a torus the filtered field of the zero pattern is the constant kernel | ||
| 215 | * sum minus the field of the one pattern, so the tightest cluster of zeros | ||
| 216 | * coincides with the largest void of ones and the ranking above 50% fill | ||
| 217 | * needs no separate phase. | ||
| 218 | */ | ||
| 219 | ✗ | static void vc_generate(uint16_t *rank, uint8_t *bits, float *energy, | |
| 220 | float *e1, const float *kern, AVLFG *lfg) | ||
| 221 | { | ||
| 222 | ✗ | const int n1 = VC_AREA / 10; | |
| 223 | uint8_t b1[VC_AREA]; | ||
| 224 | int placed, pos; | ||
| 225 | |||
| 226 | ✗ | memset(bits, 0, VC_AREA * sizeof(*bits)); | |
| 227 | ✗ | memset(energy, 0, VC_AREA * sizeof(*energy)); | |
| 228 | |||
| 229 | /* initial random minority pattern */ | ||
| 230 | ✗ | for (placed = 0; placed < n1;) { | |
| 231 | ✗ | pos = av_lfg_get(lfg) & (VC_AREA - 1); | |
| 232 | ✗ | if (!bits[pos]) { | |
| 233 | ✗ | bits[pos] = 1; | |
| 234 | ✗ | vc_splat(energy, kern, pos, 1.f); | |
| 235 | ✗ | placed++; | |
| 236 | } | ||
| 237 | } | ||
| 238 | |||
| 239 | /* relax: move the tightest cluster into the largest void until stable */ | ||
| 240 | ✗ | for (int i = 0; i < VC_AREA * 8; i++) { | |
| 241 | ✗ | pos = vc_extreme(energy, bits, 1, 1); | |
| 242 | ✗ | bits[pos] = 0; | |
| 243 | ✗ | vc_splat(energy, kern, pos, -1.f); | |
| 244 | ✗ | const int vp = vc_extreme(energy, bits, 0, 0); | |
| 245 | ✗ | bits[vp] = 1; | |
| 246 | ✗ | vc_splat(energy, kern, vp, 1.f); | |
| 247 | ✗ | if (vp == pos) | |
| 248 | ✗ | break; | |
| 249 | } | ||
| 250 | |||
| 251 | /* phase 1: rank the initial pattern by removing tightest clusters */ | ||
| 252 | ✗ | memcpy(b1, bits, sizeof(b1)); | |
| 253 | ✗ | memcpy(e1, energy, VC_AREA * sizeof(*e1)); | |
| 254 | ✗ | for (int r = n1 - 1; r >= 0; r--) { | |
| 255 | ✗ | pos = vc_extreme(e1, b1, 1, 1); | |
| 256 | ✗ | b1[pos] = 0; | |
| 257 | ✗ | vc_splat(e1, kern, pos, -1.f); | |
| 258 | ✗ | rank[pos] = r; | |
| 259 | } | ||
| 260 | |||
| 261 | /* phase 2+3: fill the largest void until all cells are ranked */ | ||
| 262 | ✗ | for (int r = n1; r < VC_AREA; r++) { | |
| 263 | ✗ | pos = vc_extreme(energy, bits, 0, 0); | |
| 264 | ✗ | bits[pos] = 1; | |
| 265 | ✗ | vc_splat(energy, kern, pos, 1.f); | |
| 266 | ✗ | rank[pos] = r; | |
| 267 | } | ||
| 268 | ✗ | } | |
| 269 | |||
| 270 | /** | ||
| 271 | * Quantize a color to the nearest point of the scaled D3 (FCC) or D4 | ||
| 272 | * lattice. | ||
| 273 | * | ||
| 274 | * Conway & Sloane: round every coordinate to the nearest integer; if the | ||
| 275 | * coordinate sum is odd, re-round the coordinate with the largest rounding | ||
| 276 | * error in the other direction. This works for any checkerboard lattice Dn. | ||
| 277 | * | ||
| 278 | * @param in the nc input components | ||
| 279 | * @param f receives the nc lattice indices | ||
| 280 | */ | ||
| 281 | 13989888 | static av_always_inline void quant_dn(const LatticePalContext *s, const int *in, int f[4]) | |
| 282 | { | ||
| 283 | 13989888 | const int N = s->density; | |
| 284 | 13989888 | const int nc = s->nc; | |
| 285 | 13989888 | int sum = 0; | |
| 286 | float d[4]; | ||
| 287 | |||
| 288 |
2/2✓ Branch 0 taken 41969664 times.
✓ Branch 1 taken 13989888 times.
|
55959552 | for (int i = 0; i < nc; i++) { |
| 289 | 41969664 | const float u = av_clipf(in[i] * s->scale, 0.f, N); | |
| 290 | 41969664 | f[i] = lrintf(u); | |
| 291 | 41969664 | d[i] = u - f[i]; | |
| 292 | 41969664 | sum += f[i]; | |
| 293 | } | ||
| 294 | |||
| 295 |
2/2✓ Branch 0 taken 6985274 times.
✓ Branch 1 taken 7004614 times.
|
13989888 | if (sum & 1) { |
| 296 | 6985274 | int k = 0, dir; | |
| 297 |
2/2✓ Branch 0 taken 13970548 times.
✓ Branch 1 taken 6985274 times.
|
20955822 | for (int i = 1; i < nc; i++) |
| 298 |
2/2✓ Branch 0 taken 5751663 times.
✓ Branch 1 taken 8218885 times.
|
13970548 | if (fabsf(d[i]) > fabsf(d[k])) |
| 299 | 5751663 | k = i; | |
| 300 |
2/2✓ Branch 0 taken 3495776 times.
✓ Branch 1 taken 3489498 times.
|
6985274 | dir = d[k] >= 0.f ? 1 : -1; |
| 301 |
3/4✓ Branch 0 taken 6985274 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 820 times.
✓ Branch 3 taken 6984454 times.
|
6985274 | if (f[k] + dir < 0 || f[k] + dir > N) |
| 302 | 820 | dir = -dir; | |
| 303 | 6985274 | f[k] += dir; | |
| 304 | } | ||
| 305 | 13989888 | } | |
| 306 | |||
| 307 | /** | ||
| 308 | * Account one pixel using the lattice color with indices f, registering the | ||
| 309 | * color in the cell table and the used color list on first use. | ||
| 310 | * | ||
| 311 | * @return the lattice cell position, or AVERROR(ENOMEM) | ||
| 312 | */ | ||
| 313 | 3649536 | static av_always_inline int color_inc(LatticePalContext *s, const int f[4]) | |
| 314 | { | ||
| 315 | 3649536 | int pos = f[0]; | |
| 316 | int idx; | ||
| 317 | |||
| 318 |
2/2✓ Branch 0 taken 7299072 times.
✓ Branch 1 taken 3649536 times.
|
10948608 | for (int i = 1; i < s->nc; i++) |
| 319 | 7299072 | pos = pos * s->dim + f[i]; | |
| 320 | 3649536 | idx = s->cell[pos]; | |
| 321 | |||
| 322 |
2/2✓ Branch 0 taken 519270 times.
✓ Branch 1 taken 3130266 times.
|
3649536 | if (!idx) { |
| 323 | PalEntry *e; | ||
| 324 | |||
| 325 |
2/2✓ Branch 0 taken 36 times.
✓ Branch 1 taken 519234 times.
|
519270 | if (s->nb_used == s->nb_alloc) { |
| 326 | 36 | const int na = FFMAX(s->nb_alloc * 2, 512); | |
| 327 | 36 | PalEntry *nl = av_realloc_array(s->list, na, sizeof(*nl)); | |
| 328 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
|
36 | if (!nl) |
| 329 | ✗ | return AVERROR(ENOMEM); | |
| 330 | 36 | s->list = nl; | |
| 331 | 36 | s->nb_alloc = na; | |
| 332 | } | ||
| 333 | 519270 | e = &s->list[s->nb_used]; | |
| 334 | 519270 | e->pos = pos; | |
| 335 | 519270 | e->ci[0] = f[0]; | |
| 336 | 519270 | e->ci[1] = f[1]; | |
| 337 | 519270 | e->ci[2] = f[2]; | |
| 338 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 519270 times.
|
519270 | e->ci[3] = s->nc == 4 ? f[3] : 0; |
| 339 | 519270 | e->alive = 1; | |
| 340 | 519270 | e->count = 0; | |
| 341 | 519270 | s->cell[pos] = idx = ++s->nb_used; | |
| 342 | } | ||
| 343 | 3649536 | s->list[idx - 1].count++; | |
| 344 | 3649536 | return pos; | |
| 345 | } | ||
| 346 | |||
| 347 | /** | ||
| 348 | * Find the nearest live color of the used color list, excluding self | ||
| 349 | * (pass -1 to match any live color). | ||
| 350 | * | ||
| 351 | * While many colors are alive, search outward in Chebyshev shells of the | ||
| 352 | * lattice index space; the component value difference of a cell r shells | ||
| 353 | * away is at least r * min_gap, which bounds the search once a candidate | ||
| 354 | * is known. When only few colors are left the lattice around them is | ||
| 355 | * sparse and shells get expensive, so scan the compact live list instead. | ||
| 356 | * (For 3 components ci[3] is 0 everywhere, contributing nothing.) | ||
| 357 | * | ||
| 358 | * @return list index of the nearest live color, its distance in *out_d2 | ||
| 359 | */ | ||
| 360 | 1756701 | static int nearest_alive(LatticePalContext *s, const uint8_t ci[4], int self, int *out_d2) | |
| 361 | { | ||
| 362 | 1756701 | PalEntry *list = s->list; | |
| 363 | 1756701 | const uint8_t *val = s->idx2val; | |
| 364 | 1756701 | const int dim = s->dim; | |
| 365 | 1756701 | const int i0 = ci[0], i1 = ci[1], i2 = ci[2], i3 = ci[3]; | |
| 366 | 1756701 | const int v0 = val[i0], v1 = val[i1], v2 = val[i2], v3 = val[i3]; | |
| 367 | 1756701 | int best = -1, bestd = INT_MAX; | |
| 368 | |||
| 369 |
3/4✓ Branch 0 taken 1756701 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 583825 times.
✓ Branch 3 taken 1172876 times.
|
1756701 | if (s->nb_alive > 0 && s->nb_alive <= 1024) { |
| 370 |
2/2✓ Branch 0 taken 181035657 times.
✓ Branch 1 taken 583825 times.
|
181619482 | for (int k = 0; k < s->nb_alive; k++) { |
| 371 | 181035657 | const int j = s->alive_arr[k]; | |
| 372 | 181035657 | const PalEntry *o = &list[j]; | |
| 373 | int d2, dv; | ||
| 374 | |||
| 375 |
2/2✓ Branch 0 taken 51033 times.
✓ Branch 1 taken 180984624 times.
|
181035657 | if (j == self) |
| 376 | 51033 | continue; | |
| 377 | 180984624 | dv = val[o->ci[0]] - v0; d2 = dv * dv; | |
| 378 | 180984624 | dv = val[o->ci[1]] - v1; d2 += dv * dv; | |
| 379 | 180984624 | dv = val[o->ci[2]] - v2; d2 += dv * dv; | |
| 380 | 180984624 | dv = val[o->ci[3]] - v3; d2 += dv * dv; | |
| 381 |
2/2✓ Branch 0 taken 3626548 times.
✓ Branch 1 taken 177358076 times.
|
180984624 | if (d2 < bestd) { |
| 382 | 3626548 | bestd = d2; | |
| 383 | 3626548 | best = j; | |
| 384 | } | ||
| 385 | } | ||
| 386 |
1/2✓ Branch 0 taken 583825 times.
✗ Branch 1 not taken.
|
583825 | *out_d2 = best >= 0 ? bestd : 0; |
| 387 | 583825 | return best; | |
| 388 | } | ||
| 389 | |||
| 390 | #define CHECK_CELL3(a, b, c) do { \ | ||
| 391 | const int e = s->cell[((a) * dim + (b)) * dim + (c)]; \ | ||
| 392 | if (e > 0 && e - 1 != self && list[e - 1].alive) { \ | ||
| 393 | const int dr = val[a] - v0; \ | ||
| 394 | const int dg = val[b] - v1; \ | ||
| 395 | const int db = val[c] - v2; \ | ||
| 396 | const int d2 = dr * dr + dg * dg + db * db; \ | ||
| 397 | if (d2 < bestd) { \ | ||
| 398 | bestd = d2; \ | ||
| 399 | best = e - 1; \ | ||
| 400 | } \ | ||
| 401 | } \ | ||
| 402 | } while (0) | ||
| 403 | |||
| 404 | #define CHECK_CELL4(a, b, c, l) do { \ | ||
| 405 | const int e = s->cell[(((a) * dim + (b)) * dim + (c)) * dim + (l)]; \ | ||
| 406 | if (e > 0 && e - 1 != self && list[e - 1].alive) { \ | ||
| 407 | const int dr = val[a] - v0; \ | ||
| 408 | const int dg = val[b] - v1; \ | ||
| 409 | const int db = val[c] - v2; \ | ||
| 410 | const int da_ = val[l] - v3; \ | ||
| 411 | const int d2 = dr * dr + dg * dg + db * db + da_ * da_; \ | ||
| 412 | if (d2 < bestd) { \ | ||
| 413 | bestd = d2; \ | ||
| 414 | best = e - 1; \ | ||
| 415 | } \ | ||
| 416 | } \ | ||
| 417 | } while (0) | ||
| 418 | |||
| 419 |
1/2✓ Branch 0 taken 2479265 times.
✗ Branch 1 not taken.
|
2479265 | for (int r = 1; r < dim; r++) { |
| 420 |
4/4✓ Branch 0 taken 1180449 times.
✓ Branch 1 taken 1298816 times.
✓ Branch 2 taken 1172876 times.
✓ Branch 3 taken 7573 times.
|
2479265 | if (best >= 0 && (int64_t)r * s->min_gap * r * s->min_gap > bestd) |
| 421 | 1172876 | break; | |
| 422 |
4/4✓ Branch 0 taken 163520 times.
✓ Branch 1 taken 5233044 times.
✓ Branch 2 taken 4090175 times.
✓ Branch 3 taken 1306389 times.
|
5396564 | for (int a = FFMAX(i0 - r, 0); a <= FFMIN(i0 + r, dim - 1); a++) { |
| 423 | 4090175 | const int da = FFABS(a - i0); | |
| 424 |
4/4✓ Branch 0 taken 510311 times.
✓ Branch 1 taken 16925648 times.
✓ Branch 2 taken 13345784 times.
✓ Branch 3 taken 4090175 times.
|
17435959 | for (int b = FFMAX(i1 - r, 0); b <= FFMIN(i1 + r, dim - 1); b++) { |
| 425 | 13345784 | const int dab = FFMAX(da, FFABS(b - i1)); | |
| 426 | |||
| 427 |
1/2✓ Branch 0 taken 13345784 times.
✗ Branch 1 not taken.
|
13345784 | if (s->nc == 3) { |
| 428 |
2/2✓ Branch 0 taken 10910630 times.
✓ Branch 1 taken 2435154 times.
|
13345784 | if (dab == r) { |
| 429 |
4/4✓ Branch 0 taken 1542386 times.
✓ Branch 1 taken 45265842 times.
✓ Branch 2 taken 35897598 times.
✓ Branch 3 taken 10910630 times.
|
46808228 | for (int c = FFMAX(i2 - r, 0); c <= FFMIN(i2 + r, dim - 1); c++) |
| 430 |
2/2✓ Branch 0 taken 18018521 times.
✓ Branch 1 taken 17879077 times.
|
35897598 | if (!((a + b + c) & 1)) |
| 431 |
7/8✓ Branch 0 taken 13261835 times.
✓ Branch 1 taken 4756686 times.
✓ Branch 2 taken 13261835 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8379222 times.
✓ Branch 5 taken 4882613 times.
✓ Branch 6 taken 1336984 times.
✓ Branch 7 taken 7042238 times.
|
18018521 | CHECK_CELL3(a, b, c); |
| 432 | } else { | ||
| 433 |
4/4✓ Branch 0 taken 2300647 times.
✓ Branch 1 taken 134507 times.
✓ Branch 2 taken 628516 times.
✓ Branch 3 taken 1672131 times.
|
2435154 | if (i2 - r >= 0 && !((a + b + i2 - r) & 1)) |
| 434 |
7/8✓ Branch 0 taken 349720 times.
✓ Branch 1 taken 278796 times.
✓ Branch 2 taken 349720 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 71870 times.
✓ Branch 5 taken 277850 times.
✓ Branch 6 taken 22972 times.
✓ Branch 7 taken 48898 times.
|
628516 | CHECK_CELL3(a, b, i2 - r); |
| 435 |
4/4✓ Branch 0 taken 2295662 times.
✓ Branch 1 taken 139492 times.
✓ Branch 2 taken 626390 times.
✓ Branch 3 taken 1669272 times.
|
2435154 | if (i2 + r < dim && !((a + b + i2 + r) & 1)) |
| 436 |
7/8✓ Branch 0 taken 349433 times.
✓ Branch 1 taken 276957 times.
✓ Branch 2 taken 349433 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 70224 times.
✓ Branch 5 taken 279209 times.
✓ Branch 6 taken 20879 times.
✓ Branch 7 taken 49345 times.
|
626390 | CHECK_CELL3(a, b, i2 + r); |
| 437 | } | ||
| 438 | } else { | ||
| 439 | ✗ | for (int c = FFMAX(i2 - r, 0); c <= FFMIN(i2 + r, dim - 1); c++) { | |
| 440 | ✗ | if (FFMAX(dab, FFABS(c - i2)) == r) { | |
| 441 | ✗ | for (int l = FFMAX(i3 - r, 0); l <= FFMIN(i3 + r, dim - 1); l++) | |
| 442 | ✗ | if (!((a + b + c + l) & 1)) | |
| 443 | ✗ | CHECK_CELL4(a, b, c, l); | |
| 444 | } else { | ||
| 445 | ✗ | if (i3 - r >= 0 && !((a + b + c + i3 - r) & 1)) | |
| 446 | ✗ | CHECK_CELL4(a, b, c, i3 - r); | |
| 447 | ✗ | if (i3 + r < dim && !((a + b + c + i3 + r) & 1)) | |
| 448 | ✗ | CHECK_CELL4(a, b, c, i3 + r); | |
| 449 | } | ||
| 450 | } | ||
| 451 | } | ||
| 452 | } | ||
| 453 | } | ||
| 454 | } | ||
| 455 | #undef CHECK_CELL3 | ||
| 456 | #undef CHECK_CELL4 | ||
| 457 | |||
| 458 |
1/2✓ Branch 0 taken 1172876 times.
✗ Branch 1 not taken.
|
1172876 | *out_d2 = best >= 0 ? bestd : 0; |
| 459 | 1172876 | return best; | |
| 460 | } | ||
| 461 | |||
| 462 | 1451528 | static void nn_search(LatticePalContext *s, int self) | |
| 463 | { | ||
| 464 | int d2; | ||
| 465 | |||
| 466 | 1451528 | s->list[self].nn = nearest_alive(s, s->list[self].ci, self, &d2); | |
| 467 | 1451528 | s->list[self].d2 = d2; | |
| 468 | 1451528 | } | |
| 469 | |||
| 470 | typedef struct HeapEnt { | ||
| 471 | int64_t imp; ///< impact of the removal when the entry was pushed | ||
| 472 | int idx; ///< used color list index | ||
| 473 | } HeapEnt; | ||
| 474 | |||
| 475 | 1036755 | static void heap_push(HeapEnt *heap, int *nb, int64_t imp, int idx) | |
| 476 | { | ||
| 477 | 1036755 | int i = (*nb)++; | |
| 478 | |||
| 479 |
4/4✓ Branch 0 taken 2039308 times.
✓ Branch 1 taken 773 times.
✓ Branch 2 taken 1003326 times.
✓ Branch 3 taken 1035982 times.
|
2040081 | while (i > 0 && heap[(i - 1) / 2].imp > imp) { |
| 480 | 1003326 | heap[i] = heap[(i - 1) / 2]; | |
| 481 | 1003326 | i = (i - 1) / 2; | |
| 482 | } | ||
| 483 | 1036755 | heap[i].imp = imp; | |
| 484 | 1036755 | heap[i].idx = idx; | |
| 485 | 1036755 | } | |
| 486 | |||
| 487 | 905311 | static HeapEnt heap_pop(HeapEnt *heap, int *nb) | |
| 488 | { | ||
| 489 | 905311 | const HeapEnt top = heap[0]; | |
| 490 | 905311 | const HeapEnt last = heap[--*nb]; | |
| 491 | 905311 | int i = 0, c; | |
| 492 | |||
| 493 |
2/2✓ Branch 0 taken 10296844 times.
✓ Branch 1 taken 691184 times.
|
10988028 | while ((c = 2 * i + 1) < *nb) { |
| 494 |
4/4✓ Branch 0 taken 10296367 times.
✓ Branch 1 taken 477 times.
✓ Branch 2 taken 4988223 times.
✓ Branch 3 taken 5308144 times.
|
10296844 | if (c + 1 < *nb && heap[c + 1].imp < heap[c].imp) |
| 495 | 4988223 | c++; | |
| 496 |
2/2✓ Branch 0 taken 214127 times.
✓ Branch 1 taken 10082717 times.
|
10296844 | if (heap[c].imp >= last.imp) |
| 497 | 214127 | break; | |
| 498 | 10082717 | heap[i] = heap[c]; | |
| 499 | 10082717 | i = c; | |
| 500 | } | ||
| 501 | 905311 | heap[i] = last; | |
| 502 | 905311 | return top; | |
| 503 | } | ||
| 504 | |||
| 505 | /** | ||
| 506 | * Reduce the live colors down to target by repeatedly dropping the color | ||
| 507 | * whose removal has the least impact: its pixel count times the squared | ||
| 508 | * distance to the nearest remaining color, which then absorbs the | ||
| 509 | * dropped pixels. | ||
| 510 | * | ||
| 511 | * An impact can only grow: counts grow by absorption and the nearest | ||
| 512 | * neighbor distance grows when the neighbor is dropped. Stale heap entries | ||
| 513 | * therefore underestimate their color's impact, and validating at pop time | ||
| 514 | * and re-pushing the corrected entry yields the exact minimum. | ||
| 515 | */ | ||
| 516 | 114 | static int reduce_colors(LatticePalContext *s, int target) | |
| 517 | { | ||
| 518 | 114 | PalEntry *list = s->list; | |
| 519 | int alive; | ||
| 520 | 114 | int nb_heap = 0, cap = s->nb_used + 64; | |
| 521 | 114 | HeapEnt *heap = av_malloc_array(cap, sizeof(*heap)); | |
| 522 | |||
| 523 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 114 times.
|
114 | if (!heap) |
| 524 | ✗ | return AVERROR(ENOMEM); | |
| 525 | |||
| 526 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 103 times.
|
114 | if (s->alive_alloc < s->nb_used) { |
| 527 | 11 | av_freep(&s->alive_arr); | |
| 528 | 11 | av_freep(&s->alive_pos); | |
| 529 | 11 | s->alive_arr = av_malloc_array(s->nb_used, sizeof(*s->alive_arr)); | |
| 530 | 11 | s->alive_pos = av_malloc_array(s->nb_used, sizeof(*s->alive_pos)); | |
| 531 |
2/4✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
|
11 | if (!s->alive_arr || !s->alive_pos) { |
| 532 | ✗ | av_free(heap); | |
| 533 | ✗ | return AVERROR(ENOMEM); | |
| 534 | } | ||
| 535 | 11 | s->alive_alloc = s->nb_used; | |
| 536 | } | ||
| 537 | 114 | s->nb_alive = 0; | |
| 538 |
2/2✓ Branch 0 taken 1984836 times.
✓ Branch 1 taken 114 times.
|
1984950 | for (int i = 0; i < s->nb_used; i++) { |
| 539 |
2/2✓ Branch 0 taken 1342422 times.
✓ Branch 1 taken 642414 times.
|
1984836 | if (!list[i].alive) |
| 540 | 1342422 | continue; | |
| 541 | 642414 | s->alive_pos[i] = s->nb_alive; | |
| 542 | 642414 | s->alive_arr[s->nb_alive++] = i; | |
| 543 | } | ||
| 544 | 114 | alive = s->nb_alive; | |
| 545 | |||
| 546 |
2/2✓ Branch 0 taken 642414 times.
✓ Branch 1 taken 114 times.
|
642528 | for (int k = 0; k < s->nb_alive; k++) { |
| 547 | 642414 | const int i = s->alive_arr[k]; | |
| 548 | |||
| 549 | 642414 | nn_search(s, i); | |
| 550 | 642414 | heap_push(heap, &nb_heap, list[i].count * list[i].d2, i); | |
| 551 | } | ||
| 552 | |||
| 553 |
2/2✓ Branch 0 taken 905311 times.
✓ Branch 1 taken 114 times.
|
905425 | while (alive > target) { |
| 554 | 905311 | const HeapEnt top = heap_pop(heap, &nb_heap); | |
| 555 | 905311 | const int i = top.idx; | |
| 556 | int64_t imp; | ||
| 557 | |||
| 558 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 905311 times.
|
905311 | if (!list[i].alive) |
| 559 | 394341 | continue; | |
| 560 |
2/2✓ Branch 0 taken 401381 times.
✓ Branch 1 taken 503930 times.
|
905311 | if (!list[list[i].nn].alive) |
| 561 | 401381 | nn_search(s, i); | |
| 562 | 905311 | imp = list[i].count * list[i].d2; | |
| 563 |
2/2✓ Branch 0 taken 394341 times.
✓ Branch 1 taken 510970 times.
|
905311 | if (imp != top.imp) { |
| 564 | /* stale entry, reinsert with the corrected impact */ | ||
| 565 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 394341 times.
|
394341 | if (nb_heap == cap) { |
| 566 | ✗ | HeapEnt *nh = av_realloc_array(heap, cap * 2, sizeof(*heap)); | |
| 567 | ✗ | if (!nh) { | |
| 568 | ✗ | av_free(heap); | |
| 569 | ✗ | return AVERROR(ENOMEM); | |
| 570 | } | ||
| 571 | ✗ | heap = nh; | |
| 572 | ✗ | cap *= 2; | |
| 573 | } | ||
| 574 | 394341 | heap_push(heap, &nb_heap, imp, i); | |
| 575 | 394341 | continue; | |
| 576 | } | ||
| 577 | 510970 | list[i].alive = 0; | |
| 578 | 510970 | list[list[i].nn].count += list[i].count; | |
| 579 | 510970 | alive--; | |
| 580 | /* swap-remove from the compact live list */ | ||
| 581 | 510970 | s->alive_arr[s->alive_pos[i]] = s->alive_arr[--s->nb_alive]; | |
| 582 | 510970 | s->alive_pos[s->alive_arr[s->nb_alive]] = s->alive_pos[i]; | |
| 583 | } | ||
| 584 | |||
| 585 | 114 | av_free(heap); | |
| 586 | 114 | return 0; | |
| 587 | } | ||
| 588 | |||
| 589 | /** Palette color (AARRGGBB) of a used color list entry. */ | ||
| 590 | 23223 | static av_always_inline uint32_t entry_color(const LatticePalContext *s, const PalEntry *e) | |
| 591 | { | ||
| 592 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 23223 times.
|
23223 | return (s->nc == 4 ? (uint32_t)s->idx2val[e->ci[3]] << 24 : 0xFF000000) | |
| 593 | 23223 | s->idx2val[e->ci[0]] << 16 | | |
| 594 | 46446 | s->idx2val[e->ci[1]] << 8 | | |
| 595 | 23223 | s->idx2val[e->ci[2]]; | |
| 596 | } | ||
| 597 | |||
| 598 | /** | ||
| 599 | * Full refinement pass: rediffuse the whole frame against the final | ||
| 600 | * palette. | ||
| 601 | * | ||
| 602 | * The first pass diffused its error against the full lattice, so the shift | ||
| 603 | * from dropped colors to their nearest survivor is uncompensated and shows | ||
| 604 | * up as flat discolored patches exactly where the reduction hit. Re-run | ||
| 605 | * Floyd-Steinberg error diffusion on the original pixels, quantizing to | ||
| 606 | * the nearest final palette color, which redistributes that error by | ||
| 607 | * construction (error diffusion is average correct against any codebook). | ||
| 608 | * | ||
| 609 | * The nearest palette color is resolved at lattice cell granularity: used | ||
| 610 | * cells already hold their slot from the remap, cells first touched by the | ||
| 611 | * diffusion get a scan over the at most 256 survivors and are cached in | ||
| 612 | * the cell table and recorded for the per frame reset. | ||
| 613 | */ | ||
| 614 | 6 | static int refine_full(LatticePalContext *s, AVFrame *out, const AVFrame *in) | |
| 615 | { | ||
| 616 | 6 | const int w = in->width, h = in->height; | |
| 617 | 6 | const int ro = s->ro, go = s->go, bo = s->bo, ao = s->ao, pixstep = s->pixstep; | |
| 618 | 6 | const int nc = s->nc; | |
| 619 | 6 | const uint32_t *pal = (const uint32_t *)out->data[1]; | |
| 620 | |||
| 621 | 6 | memset(s->err[0], 0, (w + 2) * nc * sizeof(*s->err[0])); | |
| 622 | 6 | memset(s->err[1], 0, (w + 2) * nc * sizeof(*s->err[1])); | |
| 623 | |||
| 624 |
2/2✓ Branch 0 taken 1728 times.
✓ Branch 1 taken 6 times.
|
1734 | for (int y = 0; y < h; y++) { |
| 625 | 1728 | const uint8_t *src = in->data[0] + y * in->linesize[0]; | |
| 626 | 1728 | uint8_t *dst = out->data[0] + y * out->linesize[0]; | |
| 627 |
2/2✓ Branch 0 taken 864 times.
✓ Branch 1 taken 864 times.
|
1728 | const int dir = (y & 1) ? -1 : 1; |
| 628 |
2/2✓ Branch 0 taken 864 times.
✓ Branch 1 taken 864 times.
|
1728 | const int x0 = dir > 0 ? 0 : w - 1; |
| 629 | 1728 | int32_t *err_cur = s->err[ y & 1] + nc; | |
| 630 | 1728 | int32_t *err_next = s->err[(y + 1) & 1] + nc; | |
| 631 | int f[4], px[4]; | ||
| 632 | |||
| 633 | 1728 | px[3] = 255; | |
| 634 | 1728 | memset(err_next - nc, 0, (w + 2) * nc * sizeof(*err_next)); | |
| 635 | |||
| 636 |
2/2✓ Branch 0 taken 608256 times.
✓ Branch 1 taken 1728 times.
|
609984 | for (int k = 0; k < w; k++) { |
| 637 | 608256 | const int x = x0 + k * dir; | |
| 638 | 608256 | const int32_t *e = &err_cur[x * nc]; | |
| 639 | uint32_t c; | ||
| 640 | int pos, slot1; | ||
| 641 | |||
| 642 | 608256 | px[0] = av_clip_uint8(src[x * pixstep + ro] + ((e[0] + 8) >> 4)); | |
| 643 | 608256 | px[1] = av_clip_uint8(src[x * pixstep + go] + ((e[1] + 8) >> 4)); | |
| 644 | 608256 | px[2] = av_clip_uint8(src[x * pixstep + bo] + ((e[2] + 8) >> 4)); | |
| 645 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 608256 times.
|
608256 | if (nc == 4) |
| 646 | ✗ | px[3] = av_clip_uint8((ao >= 0 ? src[x * pixstep + ao] : 255) + ((e[3] + 8) >> 4)); | |
| 647 | |||
| 648 | 608256 | quant_dn(s, px, f); | |
| 649 | 608256 | pos = f[0]; | |
| 650 |
2/2✓ Branch 0 taken 1216512 times.
✓ Branch 1 taken 608256 times.
|
1824768 | for (int i = 1; i < nc; i++) |
| 651 | 1216512 | pos = pos * s->dim + f[i]; | |
| 652 | |||
| 653 | 608256 | slot1 = s->cell[pos]; | |
| 654 |
2/2✓ Branch 0 taken 4772 times.
✓ Branch 1 taken 603484 times.
|
608256 | if (!slot1) { |
| 655 | 4772 | const uint8_t *val = s->idx2val; | |
| 656 | 4772 | int bd = INT_MAX; | |
| 657 | |||
| 658 |
2/2✓ Branch 0 taken 1221632 times.
✓ Branch 1 taken 4772 times.
|
1226404 | for (int k = 0; k < s->nb_alive; k++) { |
| 659 | 1221632 | const PalEntry *o = &s->list[s->alive_arr[k]]; | |
| 660 | int d2, dv; | ||
| 661 | |||
| 662 | 1221632 | dv = val[o->ci[0]] - val[f[0]]; d2 = dv * dv; | |
| 663 | 1221632 | dv = val[o->ci[1]] - val[f[1]]; d2 += dv * dv; | |
| 664 | 1221632 | dv = val[o->ci[2]] - val[f[2]]; d2 += dv * dv; | |
| 665 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1221632 times.
|
1221632 | if (nc == 4) { |
| 666 | ✗ | dv = val[o->ci[3]] - val[f[3]]; d2 += dv * dv; | |
| 667 | } | ||
| 668 |
2/2✓ Branch 0 taken 28893 times.
✓ Branch 1 taken 1192739 times.
|
1221632 | if (d2 < bd) { |
| 669 | 28893 | bd = d2; | |
| 670 | 28893 | slot1 = o->nn + 1; | |
| 671 | } | ||
| 672 | } | ||
| 673 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4771 times.
|
4772 | if (s->nb_touched == s->touched_alloc) { |
| 674 | 1 | const int na = FFMAX(s->touched_alloc * 2, 1024); | |
| 675 | 1 | int *nt = av_realloc_array(s->touched, na, sizeof(*nt)); | |
| 676 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!nt) |
| 677 | ✗ | return AVERROR(ENOMEM); | |
| 678 | 1 | s->touched = nt; | |
| 679 | 1 | s->touched_alloc = na; | |
| 680 | } | ||
| 681 | 4772 | s->touched[s->nb_touched++] = pos; | |
| 682 | 4772 | s->cell[pos] = slot1; | |
| 683 | } | ||
| 684 | 608256 | dst[x] = slot1 - 1; | |
| 685 | |||
| 686 | 608256 | c = pal[slot1 - 1]; | |
| 687 |
2/2✓ Branch 0 taken 1824768 times.
✓ Branch 1 taken 608256 times.
|
2433024 | for (int i = 0; i < nc; i++) { |
| 688 | static const int sh[4] = { 16, 8, 0, 24 }; | ||
| 689 | 1824768 | const int qerr = px[i] - (int)(c >> sh[i] & 0xff); | |
| 690 | |||
| 691 | 1824768 | err_cur [(x + dir) * nc + i] += qerr * 7; | |
| 692 | 1824768 | err_next[(x - dir) * nc + i] += qerr * 3; | |
| 693 | 1824768 | err_next[ x * nc + i] += qerr * 5; | |
| 694 | 1824768 | err_next[(x + dir) * nc + i] += qerr; | |
| 695 | } | ||
| 696 | } | ||
| 697 | } | ||
| 698 | 6 | return 0; | |
| 699 | } | ||
| 700 | |||
| 701 | /** | ||
| 702 | * Residual refinement pass: rediffuse only the error of the dropped | ||
| 703 | * colors, using the first pass quantized color as the diffusion target. | ||
| 704 | * The error stream then carries only the removal residuals: a pixel whose | ||
| 705 | * color survived and that receives no incoming residual picks its own | ||
| 706 | * color with zero error and is unchanged, keeping the character of the | ||
| 707 | * selected dither mode outside the reduced regions. | ||
| 708 | * | ||
| 709 | * Runs before the cell table is rewritten: cells hold list indices, and | ||
| 710 | * cells resolved on demand are cached as -(slot + 1). | ||
| 711 | */ | ||
| 712 | 6 | static int refine_residual(LatticePalContext *s, AVFrame *out, const AVFrame *in) | |
| 713 | { | ||
| 714 | 6 | const int w = in->width, h = in->height; | |
| 715 | 6 | const int nc = s->nc; | |
| 716 | 6 | const uint32_t *pal = (const uint32_t *)out->data[1]; | |
| 717 | 6 | const uint8_t *val = s->idx2val; | |
| 718 | 6 | PalEntry *list = s->list; | |
| 719 | |||
| 720 | 6 | memset(s->err[0], 0, (w + 2) * nc * sizeof(*s->err[0])); | |
| 721 | 6 | memset(s->err[1], 0, (w + 2) * nc * sizeof(*s->err[1])); | |
| 722 | |||
| 723 |
2/2✓ Branch 0 taken 1728 times.
✓ Branch 1 taken 6 times.
|
1734 | for (int y = 0; y < h; y++) { |
| 724 | 1728 | const uint32_t *ppos = s->pixpos + (size_t)y * w; | |
| 725 | 1728 | uint8_t *dst = out->data[0] + y * out->linesize[0]; | |
| 726 |
2/2✓ Branch 0 taken 864 times.
✓ Branch 1 taken 864 times.
|
1728 | const int dir = (y & 1) ? -1 : 1; |
| 727 |
2/2✓ Branch 0 taken 864 times.
✓ Branch 1 taken 864 times.
|
1728 | const int x0 = dir > 0 ? 0 : w - 1; |
| 728 | 1728 | int32_t *err_cur = s->err[ y & 1] + nc; | |
| 729 | 1728 | int32_t *err_next = s->err[(y + 1) & 1] + nc; | |
| 730 | int f[4], px[4]; | ||
| 731 | |||
| 732 | 1728 | px[3] = 255; | |
| 733 | 1728 | memset(err_next - nc, 0, (w + 2) * nc * sizeof(*err_next)); | |
| 734 | |||
| 735 |
2/2✓ Branch 0 taken 608256 times.
✓ Branch 1 taken 1728 times.
|
609984 | for (int k = 0; k < w; k++) { |
| 736 | 608256 | const int x = x0 + k * dir; | |
| 737 | 608256 | const PalEntry *e1 = &list[s->cell[ppos[x]] - 1]; | |
| 738 | 608256 | const int32_t *e = &err_cur[x * nc]; | |
| 739 | uint32_t c; | ||
| 740 | int pos, v, slot; | ||
| 741 | |||
| 742 |
4/6✓ Branch 0 taken 20885 times.
✓ Branch 1 taken 587371 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 20885 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 20885 times.
|
608256 | if (e1->alive && !(e[0] | e[1] | e[2] | (nc == 4 ? e[3] : 0))) { |
| 743 | /* surviving color, no incoming residual: unchanged */ | ||
| 744 | ✗ | dst[x] = e1->nn; | |
| 745 | ✗ | continue; | |
| 746 | } | ||
| 747 | |||
| 748 | /* target = first pass color + carried residual */ | ||
| 749 |
2/2✓ Branch 0 taken 1824768 times.
✓ Branch 1 taken 608256 times.
|
2433024 | for (int i = 0; i < nc; i++) |
| 750 | 1824768 | px[i] = av_clip_uint8(val[e1->ci[i]] + ((e[i] + 8) >> 4)); | |
| 751 | |||
| 752 | 608256 | quant_dn(s, px, f); | |
| 753 | 608256 | pos = f[0]; | |
| 754 |
2/2✓ Branch 0 taken 1216512 times.
✓ Branch 1 taken 608256 times.
|
1824768 | for (int i = 1; i < nc; i++) |
| 755 | 1216512 | pos = pos * s->dim + f[i]; | |
| 756 | |||
| 757 | 608256 | v = s->cell[pos]; | |
| 758 |
2/2✓ Branch 0 taken 589263 times.
✓ Branch 1 taken 18993 times.
|
608256 | if (v > 0) { |
| 759 | 589263 | slot = list[v - 1].nn; | |
| 760 |
2/2✓ Branch 0 taken 13999 times.
✓ Branch 1 taken 4994 times.
|
18993 | } else if (v < 0) { |
| 761 | 13999 | slot = -v - 1; | |
| 762 | } else { | ||
| 763 | 4994 | int bd = INT_MAX; | |
| 764 | |||
| 765 | 4994 | slot = 0; | |
| 766 |
2/2✓ Branch 0 taken 1278464 times.
✓ Branch 1 taken 4994 times.
|
1283458 | for (int j = 0; j < s->nb_alive; j++) { |
| 767 | 1278464 | const PalEntry *o = &list[s->alive_arr[j]]; | |
| 768 | int d2, dv; | ||
| 769 | |||
| 770 | 1278464 | dv = val[o->ci[0]] - val[f[0]]; d2 = dv * dv; | |
| 771 | 1278464 | dv = val[o->ci[1]] - val[f[1]]; d2 += dv * dv; | |
| 772 | 1278464 | dv = val[o->ci[2]] - val[f[2]]; d2 += dv * dv; | |
| 773 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1278464 times.
|
1278464 | if (nc == 4) { |
| 774 | ✗ | dv = val[o->ci[3]] - val[f[3]]; d2 += dv * dv; | |
| 775 | } | ||
| 776 |
2/2✓ Branch 0 taken 30212 times.
✓ Branch 1 taken 1248252 times.
|
1278464 | if (d2 < bd) { |
| 777 | 30212 | bd = d2; | |
| 778 | 30212 | slot = o->nn; | |
| 779 | } | ||
| 780 | } | ||
| 781 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4993 times.
|
4994 | if (s->nb_touched == s->touched_alloc) { |
| 782 | 1 | const int na = FFMAX(s->touched_alloc * 2, 1024); | |
| 783 | 1 | int *nt = av_realloc_array(s->touched, na, sizeof(*nt)); | |
| 784 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!nt) |
| 785 | ✗ | return AVERROR(ENOMEM); | |
| 786 | 1 | s->touched = nt; | |
| 787 | 1 | s->touched_alloc = na; | |
| 788 | } | ||
| 789 | 4994 | s->touched[s->nb_touched++] = pos; | |
| 790 | 4994 | s->cell[pos] = -(slot + 1); | |
| 791 | } | ||
| 792 | 608256 | dst[x] = slot; | |
| 793 | |||
| 794 | 608256 | c = pal[slot]; | |
| 795 |
2/2✓ Branch 0 taken 1824768 times.
✓ Branch 1 taken 608256 times.
|
2433024 | for (int i = 0; i < nc; i++) { |
| 796 | static const int sh[4] = { 16, 8, 0, 24 }; | ||
| 797 | 1824768 | const int qerr = px[i] - (int)(c >> sh[i] & 0xff); | |
| 798 | |||
| 799 | 1824768 | err_cur [(x + dir) * nc + i] += qerr * 7; | |
| 800 | 1824768 | err_next[(x - dir) * nc + i] += qerr * 3; | |
| 801 | 1824768 | err_next[ x * nc + i] += qerr * 5; | |
| 802 | 1824768 | err_next[(x + dir) * nc + i] += qerr; | |
| 803 | } | ||
| 804 | } | ||
| 805 | } | ||
| 806 | 6 | return 0; | |
| 807 | } | ||
| 808 | |||
| 809 | /** | ||
| 810 | * Rediffuse the whole frame against the current live colors and reassign | ||
| 811 | * every pixel, recounting how often each live color is actually used. | ||
| 812 | * | ||
| 813 | * Cells resolving to a dead or unused state are cached as -(list index+1); | ||
| 814 | * stale caches from earlier rounds heal themselves through the alive check. | ||
| 815 | */ | ||
| 816 | 90 | static int batch_assign(LatticePalContext *s, const AVFrame *in) | |
| 817 | { | ||
| 818 | 90 | const int w = in->width, h = in->height; | |
| 819 | 90 | const int ro = s->ro, go = s->go, bo = s->bo, ao = s->ao, pixstep = s->pixstep; | |
| 820 | 90 | const int nc = s->nc; | |
| 821 | 90 | const uint8_t *val = s->idx2val; | |
| 822 | 90 | PalEntry *list = s->list; | |
| 823 | |||
| 824 |
2/2✓ Branch 0 taken 126236 times.
✓ Branch 1 taken 90 times.
|
126326 | for (int k = 0; k < s->nb_alive; k++) |
| 825 | 126236 | list[s->alive_arr[k]].count = 0; | |
| 826 | |||
| 827 | 90 | memset(s->err[0], 0, (w + 2) * nc * sizeof(*s->err[0])); | |
| 828 | 90 | memset(s->err[1], 0, (w + 2) * nc * sizeof(*s->err[1])); | |
| 829 | |||
| 830 |
2/2✓ Branch 0 taken 25920 times.
✓ Branch 1 taken 90 times.
|
26010 | for (int y = 0; y < h; y++) { |
| 831 | 25920 | const uint8_t *src = in->data[0] + y * in->linesize[0]; | |
| 832 | 25920 | uint32_t *ppos = s->pixpos + (size_t)y * w; | |
| 833 |
2/2✓ Branch 0 taken 12960 times.
✓ Branch 1 taken 12960 times.
|
25920 | const int dir = (y & 1) ? -1 : 1; |
| 834 |
2/2✓ Branch 0 taken 12960 times.
✓ Branch 1 taken 12960 times.
|
25920 | const int x0 = dir > 0 ? 0 : w - 1; |
| 835 | 25920 | int32_t *err_cur = s->err[ y & 1] + nc; | |
| 836 | 25920 | int32_t *err_next = s->err[(y + 1) & 1] + nc; | |
| 837 | int f[4], px[4]; | ||
| 838 | |||
| 839 | 25920 | px[3] = 255; | |
| 840 | 25920 | memset(err_next - nc, 0, (w + 2) * nc * sizeof(*err_next)); | |
| 841 | |||
| 842 |
2/2✓ Branch 0 taken 9123840 times.
✓ Branch 1 taken 25920 times.
|
9149760 | for (int k = 0; k < w; k++) { |
| 843 | 9123840 | const int x = x0 + k * dir; | |
| 844 | 9123840 | const int32_t *e = &err_cur[x * nc]; | |
| 845 | const PalEntry *o; | ||
| 846 | int pos, v, j; | ||
| 847 | |||
| 848 | 9123840 | px[0] = av_clip_uint8(src[x * pixstep + ro] + ((e[0] + 8) >> 4)); | |
| 849 | 9123840 | px[1] = av_clip_uint8(src[x * pixstep + go] + ((e[1] + 8) >> 4)); | |
| 850 | 9123840 | px[2] = av_clip_uint8(src[x * pixstep + bo] + ((e[2] + 8) >> 4)); | |
| 851 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9123840 times.
|
9123840 | if (nc == 4) |
| 852 | ✗ | px[3] = av_clip_uint8((ao >= 0 ? src[x * pixstep + ao] : 255) + ((e[3] + 8) >> 4)); | |
| 853 | |||
| 854 | 9123840 | quant_dn(s, px, f); | |
| 855 | 9123840 | pos = f[0]; | |
| 856 |
2/2✓ Branch 0 taken 18247680 times.
✓ Branch 1 taken 9123840 times.
|
27371520 | for (int i = 1; i < nc; i++) |
| 857 | 18247680 | pos = pos * s->dim + f[i]; | |
| 858 | |||
| 859 | 9123840 | v = s->cell[pos]; | |
| 860 |
4/4✓ Branch 0 taken 1206125 times.
✓ Branch 1 taken 7917715 times.
✓ Branch 2 taken 1102868 times.
✓ Branch 3 taken 103257 times.
|
9123840 | if (v > 0 && list[v - 1].alive) { |
| 861 | 1102868 | j = v - 1; | |
| 862 |
4/4✓ Branch 0 taken 7914694 times.
✓ Branch 1 taken 106278 times.
✓ Branch 2 taken 7715799 times.
✓ Branch 3 taken 198895 times.
|
8020972 | } else if (v < 0 && list[-v - 1].alive) { |
| 863 | 7715799 | j = -v - 1; | |
| 864 | } else { | ||
| 865 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 305173 times.
|
305173 | const uint8_t fci[4] = { f[0], f[1], f[2], nc == 4 ? f[3] : 0 }; |
| 866 | int d2; | ||
| 867 | |||
| 868 | 305173 | j = nearest_alive(s, fci, -1, &d2); | |
| 869 |
2/2✓ Branch 0 taken 3021 times.
✓ Branch 1 taken 302152 times.
|
305173 | if (!v) { |
| 870 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3020 times.
|
3021 | if (s->nb_touched == s->touched_alloc) { |
| 871 | 1 | const int na = FFMAX(s->touched_alloc * 2, 1024); | |
| 872 | 1 | int *nt = av_realloc_array(s->touched, na, sizeof(*nt)); | |
| 873 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!nt) |
| 874 | ✗ | return AVERROR(ENOMEM); | |
| 875 | 1 | s->touched = nt; | |
| 876 | 1 | s->touched_alloc = na; | |
| 877 | } | ||
| 878 | 3021 | s->touched[s->nb_touched++] = pos; | |
| 879 | } | ||
| 880 | 305173 | s->cell[pos] = -(j + 1); | |
| 881 | } | ||
| 882 | 9123840 | o = &list[j]; | |
| 883 | 9123840 | list[j].count++; | |
| 884 | 9123840 | ppos[x] = j; | |
| 885 | |||
| 886 |
2/2✓ Branch 0 taken 27371520 times.
✓ Branch 1 taken 9123840 times.
|
36495360 | for (int i = 0; i < nc; i++) { |
| 887 | 27371520 | const int qerr = px[i] - val[o->ci[i]]; | |
| 888 | |||
| 889 | 27371520 | err_cur [(x + dir) * nc + i] += qerr * 7; | |
| 890 | 27371520 | err_next[(x - dir) * nc + i] += qerr * 3; | |
| 891 | 27371520 | err_next[ x * nc + i] += qerr * 5; | |
| 892 | 27371520 | err_next[(x + dir) * nc + i] += qerr; | |
| 893 | } | ||
| 894 | } | ||
| 895 | } | ||
| 896 | 90 | return 0; | |
| 897 | } | ||
| 898 | |||
| 899 | /** | ||
| 900 | * Batched reduction: instead of dropping all excess colors against the | ||
| 901 | * first pass statistics, drop half of the excess, rediffuse the frame | ||
| 902 | * against the survivors and recount from the actual assignments, then | ||
| 903 | * repeat. Colors whose pixels the rediffusion absorbed elsewhere become | ||
| 904 | * free to drop, while colors that dithering cannot reproduce (extremes of | ||
| 905 | * the used gamut) keep their pixels and with them a high removal impact, | ||
| 906 | * so they are protected in later rounds. | ||
| 907 | */ | ||
| 908 | 6 | static int reduce_batched(LatticePalContext *s, const AVFrame *in) | |
| 909 | { | ||
| 910 | 6 | int alive = s->nb_used; | |
| 911 | |||
| 912 |
2/2✓ Branch 0 taken 90 times.
✓ Branch 1 taken 6 times.
|
96 | while (alive > s->max_colors) { |
| 913 | 90 | const int excess = alive - s->max_colors; | |
| 914 | 90 | const int target = s->max_colors + excess / 2; | |
| 915 | 90 | int ret = reduce_colors(s, target); | |
| 916 | |||
| 917 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 90 times.
|
90 | if (ret < 0) |
| 918 | ✗ | return ret; | |
| 919 | 90 | alive = target; | |
| 920 | |||
| 921 | 90 | ret = batch_assign(s, in); | |
| 922 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 90 times.
|
90 | if (ret < 0) |
| 923 | ✗ | return ret; | |
| 924 | |||
| 925 | /* drop the colors the rediffusion no longer uses */ | ||
| 926 |
2/2✓ Branch 0 taken 126236 times.
✓ Branch 1 taken 90 times.
|
126326 | for (int k = 0; k < s->nb_alive;) { |
| 927 | 126236 | const int i = s->alive_arr[k]; | |
| 928 | |||
| 929 |
2/2✓ Branch 0 taken 20 times.
✓ Branch 1 taken 126216 times.
|
126236 | if (!s->list[i].count) { |
| 930 | 20 | s->list[i].alive = 0; | |
| 931 | 20 | s->alive_arr[k] = s->alive_arr[--s->nb_alive]; | |
| 932 | 20 | s->alive_pos[s->alive_arr[k]] = k; | |
| 933 | 20 | alive--; | |
| 934 | } else | ||
| 935 | 126216 | k++; | |
| 936 | } | ||
| 937 | } | ||
| 938 | 6 | return 0; | |
| 939 | } | ||
| 940 | |||
| 941 | 36 | static int filter_frame(AVFilterLink *inlink, AVFrame *in) | |
| 942 | { | ||
| 943 | 36 | AVFilterContext *ctx = inlink->dst; | |
| 944 | 36 | LatticePalContext *s = ctx->priv; | |
| 945 | 36 | AVFilterLink *outlink = ctx->outputs[0]; | |
| 946 | 36 | const int w = in->width, h = in->height; | |
| 947 | 36 | const int ro = s->ro, go = s->go, bo = s->bo, ao = s->ao, pixstep = s->pixstep; | |
| 948 | 36 | const int nc = s->nc; | |
| 949 | uint32_t *pal; | ||
| 950 | AVFrame *out; | ||
| 951 | 36 | int ret, reduced = 0; | |
| 952 | |||
| 953 | 36 | out = ff_get_video_buffer(outlink, outlink->w, outlink->h); | |
| 954 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
|
36 | if (!out) { |
| 955 | ✗ | av_frame_free(&in); | |
| 956 | ✗ | return AVERROR(ENOMEM); | |
| 957 | } | ||
| 958 | 36 | ret = av_frame_copy_props(out, in); | |
| 959 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
|
36 | if (ret < 0) |
| 960 | ✗ | goto fail; | |
| 961 | |||
| 962 | 36 | s->nb_used = 0; | |
| 963 | 36 | s->nb_alive = 0; | |
| 964 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 24 times.
|
36 | if (s->dither == DITHERING_FLOYD_STEINBERG) { |
| 965 | 12 | memset(s->err[0], 0, (w + 2) * nc * sizeof(*s->err[0])); | |
| 966 | 12 | memset(s->err[1], 0, (w + 2) * nc * sizeof(*s->err[1])); | |
| 967 | } | ||
| 968 | |||
| 969 | /* first pass: quantize, collect the used colors and their pixel counts */ | ||
| 970 |
2/2✓ Branch 0 taken 10368 times.
✓ Branch 1 taken 36 times.
|
10404 | for (int y = 0; y < h; y++) { |
| 971 | 10368 | const uint8_t *src = in->data[0] + y * in->linesize[0]; | |
| 972 | 10368 | uint32_t *ppos = s->pixpos + (size_t)y * w; | |
| 973 | int f[4], px[4], pos; | ||
| 974 | |||
| 975 | 10368 | px[3] = 255; | |
| 976 | |||
| 977 |
2/2✓ Branch 0 taken 3456 times.
✓ Branch 1 taken 6912 times.
|
10368 | if (s->dither == DITHERING_FLOYD_STEINBERG) { |
| 978 | /* serpentine scan: odd lines run right to left with mirrored | ||
| 979 | * diffusion weights, avoiding directional drift artifacts */ | ||
| 980 |
2/2✓ Branch 0 taken 1728 times.
✓ Branch 1 taken 1728 times.
|
3456 | const int dir = (y & 1) ? -1 : 1; |
| 981 |
2/2✓ Branch 0 taken 1728 times.
✓ Branch 1 taken 1728 times.
|
3456 | const int x0 = dir > 0 ? 0 : w - 1; |
| 982 | 3456 | int32_t *err_cur = s->err[ y & 1] + nc; | |
| 983 | 3456 | int32_t *err_next = s->err[(y + 1) & 1] + nc; | |
| 984 | |||
| 985 | 3456 | memset(err_next - nc, 0, (w + 2) * nc * sizeof(*err_next)); | |
| 986 | |||
| 987 |
2/2✓ Branch 0 taken 1216512 times.
✓ Branch 1 taken 3456 times.
|
1219968 | for (int k = 0; k < w; k++) { |
| 988 | 1216512 | const int x = x0 + k * dir; | |
| 989 | 1216512 | const int32_t *e = &err_cur[x * nc]; | |
| 990 | |||
| 991 | 1216512 | px[0] = av_clip_uint8(src[x * pixstep + ro] + ((e[0] + 8) >> 4)); | |
| 992 | 1216512 | px[1] = av_clip_uint8(src[x * pixstep + go] + ((e[1] + 8) >> 4)); | |
| 993 | 1216512 | px[2] = av_clip_uint8(src[x * pixstep + bo] + ((e[2] + 8) >> 4)); | |
| 994 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1216512 times.
|
1216512 | if (nc == 4) |
| 995 | ✗ | px[3] = av_clip_uint8((ao >= 0 ? src[x * pixstep + ao] : 255) + ((e[3] + 8) >> 4)); | |
| 996 | |||
| 997 | 1216512 | quant_dn(s, px, f); | |
| 998 | 1216512 | pos = color_inc(s, f); | |
| 999 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1216512 times.
|
1216512 | if (pos < 0) { |
| 1000 | ✗ | ret = pos; | |
| 1001 | ✗ | goto fail; | |
| 1002 | } | ||
| 1003 | 1216512 | ppos[x] = pos; | |
| 1004 | |||
| 1005 |
2/2✓ Branch 0 taken 3649536 times.
✓ Branch 1 taken 1216512 times.
|
4866048 | for (int i = 0; i < nc; i++) { |
| 1006 | 3649536 | const int qerr = px[i] - s->idx2val[f[i]]; | |
| 1007 | |||
| 1008 | 3649536 | err_cur [(x + dir) * nc + i] += qerr * 7; | |
| 1009 | 3649536 | err_next[(x - dir) * nc + i] += qerr * 3; | |
| 1010 | 3649536 | err_next[ x * nc + i] += qerr * 5; | |
| 1011 | 3649536 | err_next[(x + dir) * nc + i] += qerr; | |
| 1012 | } | ||
| 1013 | } | ||
| 1014 |
3/4✓ Branch 0 taken 3456 times.
✓ Branch 1 taken 3456 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3456 times.
|
10368 | } else if (s->dither == DITHERING_BAYER || s->dither == DITHERING_BLUE_NOISE) { |
| 1015 | 3456 | const int blue = s->dither == DITHERING_BLUE_NOISE; | |
| 1016 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3456 times.
|
3456 | const int mask = blue ? VC_MASK : 7; |
| 1017 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3456 times.
|
3456 | const int rowoff = blue ? (y & VC_MASK) << VC_SHIFT : (y & 7) << 3; |
| 1018 | const int *dt[4]; | ||
| 1019 | |||
| 1020 |
2/2✓ Branch 0 taken 10368 times.
✓ Branch 1 taken 3456 times.
|
13824 | for (int i = 0; i < nc; i++) |
| 1021 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10368 times.
|
10368 | dt[i] = (blue ? s->blue_dither[i] : s->ordered_dither[i]) + rowoff; |
| 1022 | |||
| 1023 |
2/2✓ Branch 0 taken 1216512 times.
✓ Branch 1 taken 3456 times.
|
1219968 | for (int x = 0; x < w; x++) { |
| 1024 | 1216512 | px[0] = src[x * pixstep + ro] + dt[0][x & mask]; | |
| 1025 | 1216512 | px[1] = src[x * pixstep + go] + dt[1][x & mask]; | |
| 1026 | 1216512 | px[2] = src[x * pixstep + bo] + dt[2][x & mask]; | |
| 1027 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1216512 times.
|
1216512 | if (nc == 4) |
| 1028 | ✗ | px[3] = (ao >= 0 ? src[x * pixstep + ao] : 255) + dt[3][x & mask]; | |
| 1029 | |||
| 1030 | 1216512 | quant_dn(s, px, f); | |
| 1031 | 1216512 | pos = color_inc(s, f); | |
| 1032 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1216512 times.
|
1216512 | if (pos < 0) { |
| 1033 | ✗ | ret = pos; | |
| 1034 | ✗ | goto fail; | |
| 1035 | } | ||
| 1036 | 1216512 | ppos[x] = pos; | |
| 1037 | } | ||
| 1038 | } else { | ||
| 1039 |
2/2✓ Branch 0 taken 1216512 times.
✓ Branch 1 taken 3456 times.
|
1219968 | for (int x = 0; x < w; x++) { |
| 1040 | 1216512 | px[0] = src[x * pixstep + ro]; | |
| 1041 | 1216512 | px[1] = src[x * pixstep + go]; | |
| 1042 | 1216512 | px[2] = src[x * pixstep + bo]; | |
| 1043 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1216512 times.
|
1216512 | if (nc == 4) |
| 1044 | ✗ | px[3] = ao >= 0 ? src[x * pixstep + ao] : 255; | |
| 1045 | |||
| 1046 | 1216512 | quant_dn(s, px, f); | |
| 1047 | 1216512 | pos = color_inc(s, f); | |
| 1048 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1216512 times.
|
1216512 | if (pos < 0) { |
| 1049 | ✗ | ret = pos; | |
| 1050 | ✗ | goto fail; | |
| 1051 | } | ||
| 1052 | 1216512 | ppos[x] = pos; | |
| 1053 | } | ||
| 1054 | } | ||
| 1055 | } | ||
| 1056 | |||
| 1057 | 36 | reduced = s->nb_used > s->max_colors; | |
| 1058 |
2/2✓ Branch 0 taken 30 times.
✓ Branch 1 taken 6 times.
|
36 | if (reduced) { |
| 1059 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 24 times.
|
30 | if (s->refine == REFINE_BATCHED) |
| 1060 | 6 | ret = reduce_batched(s, in); | |
| 1061 | else | ||
| 1062 | 24 | ret = reduce_colors(s, s->max_colors); | |
| 1063 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
|
30 | if (ret < 0) |
| 1064 | ✗ | goto fail; | |
| 1065 | 30 | av_log(ctx, AV_LOG_DEBUG, | |
| 1066 | "pts %"PRId64": %d lattice colors used, %d dropped\n", | ||
| 1067 | 30 | in->pts, s->nb_used, s->nb_used - s->nb_alive); | |
| 1068 | } | ||
| 1069 | |||
| 1070 | /* Assign palette slots so that each entry is identical or similar to | ||
| 1071 | * the same entry of the previous frame: static regions keep their | ||
| 1072 | * indices and both the index plane and the palette stay small under | ||
| 1073 | * inter prediction. Unclaimed slots keep the previous frame's color. | ||
| 1074 | * On the first frame the all black previous palette degenerates this | ||
| 1075 | * to insertion order. */ | ||
| 1076 | { | ||
| 1077 | 36 | uint8_t claimed[AVPALETTE_COUNT] = { 0 }; | |
| 1078 | |||
| 1079 |
2/2✓ Branch 0 taken 519270 times.
✓ Branch 1 taken 36 times.
|
519306 | for (int i = 0; i < s->nb_used; i++) { |
| 1080 | 519270 | PalEntry *e = &s->list[i]; | |
| 1081 | uint32_t c; | ||
| 1082 | |||
| 1083 |
2/2✓ Branch 0 taken 510990 times.
✓ Branch 1 taken 8280 times.
|
519270 | if (!e->alive) |
| 1084 | 510990 | continue; | |
| 1085 | 8280 | e->nn = -1; | |
| 1086 | 8280 | c = entry_color(s, e); | |
| 1087 |
2/2✓ Branch 0 taken 1910797 times.
✓ Branch 1 taken 6663 times.
|
1917460 | for (int slot = 0; slot < AVPALETTE_COUNT; slot++) { |
| 1088 |
4/4✓ Branch 0 taken 1771921 times.
✓ Branch 1 taken 138876 times.
✓ Branch 2 taken 1617 times.
✓ Branch 3 taken 1770304 times.
|
1910797 | if (!claimed[slot] && s->prev_pal[slot] == c) { |
| 1089 | 1617 | claimed[slot] = 1; | |
| 1090 | 1617 | e->nn = slot; | |
| 1091 | 1617 | break; | |
| 1092 | } | ||
| 1093 | } | ||
| 1094 | } | ||
| 1095 |
2/2✓ Branch 0 taken 519270 times.
✓ Branch 1 taken 36 times.
|
519306 | for (int i = 0; i < s->nb_used; i++) { |
| 1096 | 519270 | PalEntry *e = &s->list[i]; | |
| 1097 | uint32_t c; | ||
| 1098 | 519270 | int bslot = -1; | |
| 1099 | 519270 | int64_t bd = INT64_MAX; | |
| 1100 | |||
| 1101 |
4/4✓ Branch 0 taken 8280 times.
✓ Branch 1 taken 510990 times.
✓ Branch 2 taken 1617 times.
✓ Branch 3 taken 6663 times.
|
519270 | if (!e->alive || e->nn >= 0) |
| 1102 | 512607 | continue; | |
| 1103 | 6663 | c = entry_color(s, e); | |
| 1104 |
2/2✓ Branch 0 taken 1705728 times.
✓ Branch 1 taken 6663 times.
|
1712391 | for (int slot = 0; slot < AVPALETTE_COUNT; slot++) { |
| 1105 | 1705728 | const uint32_t p = s->prev_pal[slot]; | |
| 1106 | 1705728 | const int da = (int)(p >> 24 ) - (int)(c >> 24 ); | |
| 1107 | 1705728 | const int dr = (int)(p >> 16 & 0xff) - (int)(c >> 16 & 0xff); | |
| 1108 | 1705728 | const int dg = (int)(p >> 8 & 0xff) - (int)(c >> 8 & 0xff); | |
| 1109 | 1705728 | const int db = (int)(p & 0xff) - (int)(c & 0xff); | |
| 1110 | 1705728 | const int64_t d2 = (int64_t)da * da + dr * dr + dg * dg + db * db; | |
| 1111 | |||
| 1112 |
4/4✓ Branch 0 taken 862494 times.
✓ Branch 1 taken 843234 times.
✓ Branch 2 taken 27228 times.
✓ Branch 3 taken 835266 times.
|
1705728 | if (!claimed[slot] && d2 < bd) { |
| 1113 | 27228 | bd = d2; | |
| 1114 | 27228 | bslot = slot; | |
| 1115 | } | ||
| 1116 | } | ||
| 1117 | 6663 | claimed[bslot] = 1; | |
| 1118 | 6663 | e->nn = bslot; | |
| 1119 | } | ||
| 1120 | |||
| 1121 | 36 | pal = (uint32_t *)out->data[1]; | |
| 1122 | 36 | memcpy(pal, s->prev_pal, AVPALETTE_SIZE); | |
| 1123 |
2/2✓ Branch 0 taken 519270 times.
✓ Branch 1 taken 36 times.
|
519306 | for (int i = 0; i < s->nb_used; i++) { |
| 1124 | 519270 | const PalEntry *e = &s->list[i]; | |
| 1125 | |||
| 1126 |
2/2✓ Branch 0 taken 8280 times.
✓ Branch 1 taken 510990 times.
|
519270 | if (e->alive) |
| 1127 | 8280 | pal[e->nn] = entry_color(s, e); | |
| 1128 | } | ||
| 1129 | 36 | memcpy(s->prev_pal, pal, AVPALETTE_SIZE); | |
| 1130 | } | ||
| 1131 |
3/4✓ Branch 0 taken 6 times.
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
|
36 | if (!(s->refine == REFINE_BATCHED && reduced)) { |
| 1132 |
2/2✓ Branch 0 taken 414477 times.
✓ Branch 1 taken 30 times.
|
414507 | for (int i = 0; i < s->nb_used; i++) { |
| 1133 | 414477 | PalEntry *e = &s->list[i]; | |
| 1134 | |||
| 1135 |
2/2✓ Branch 0 taken 407733 times.
✓ Branch 1 taken 6744 times.
|
414477 | if (!e->alive) { |
| 1136 | 407733 | nn_search(s, i); | |
| 1137 | 407733 | e->nn = s->list[e->nn].nn; | |
| 1138 | } | ||
| 1139 | } | ||
| 1140 | } | ||
| 1141 | |||
| 1142 |
3/4✓ Branch 0 taken 6 times.
✓ Branch 1 taken 30 times.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
|
36 | if (s->refine == REFINE_BATCHED && reduced) { |
| 1143 | /* pixpos holds the list index assigned by the last rediffusion */ | ||
| 1144 |
2/2✓ Branch 0 taken 1728 times.
✓ Branch 1 taken 6 times.
|
1734 | for (int y = 0; y < h; y++) { |
| 1145 | 1728 | const uint32_t *ppos = s->pixpos + (size_t)y * w; | |
| 1146 | 1728 | uint8_t *dst = out->data[0] + y * out->linesize[0]; | |
| 1147 | |||
| 1148 |
2/2✓ Branch 0 taken 608256 times.
✓ Branch 1 taken 1728 times.
|
609984 | for (int x = 0; x < w; x++) |
| 1149 | 608256 | dst[x] = s->list[ppos[x]].nn; | |
| 1150 | } | ||
| 1151 |
3/4✓ Branch 0 taken 6 times.
✓ Branch 1 taken 24 times.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
|
30 | } else if (s->refine == REFINE_RESIDUAL && reduced) { |
| 1152 | /* needs the cell table still holding list indices */ | ||
| 1153 | 6 | ret = refine_residual(s, out, in); | |
| 1154 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (ret < 0) |
| 1155 | ✗ | goto fail; | |
| 1156 |
3/4✓ Branch 0 taken 6 times.
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
|
24 | } else if (s->refine == REFINE_FULL && reduced) { |
| 1157 | /* rewrite the cell table to slot + 1, 0 keeps meaning unused */ | ||
| 1158 |
2/2✓ Branch 0 taken 102799 times.
✓ Branch 1 taken 6 times.
|
102805 | for (int i = 0; i < s->nb_used; i++) |
| 1159 | 102799 | s->cell[s->list[i].pos] = s->list[i].nn + 1; | |
| 1160 | 6 | ret = refine_full(s, out, in); | |
| 1161 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (ret < 0) |
| 1162 | ✗ | goto fail; | |
| 1163 | } else { | ||
| 1164 | /* second pass: write the palette indices */ | ||
| 1165 |
2/2✓ Branch 0 taken 209128 times.
✓ Branch 1 taken 18 times.
|
209146 | for (int i = 0; i < s->nb_used; i++) |
| 1166 | 209128 | s->cell[s->list[i].pos] = s->list[i].nn; | |
| 1167 |
2/2✓ Branch 0 taken 5184 times.
✓ Branch 1 taken 18 times.
|
5202 | for (int y = 0; y < h; y++) { |
| 1168 | 5184 | const uint32_t *ppos = s->pixpos + (size_t)y * w; | |
| 1169 | 5184 | uint8_t *dst = out->data[0] + y * out->linesize[0]; | |
| 1170 | |||
| 1171 |
2/2✓ Branch 0 taken 1824768 times.
✓ Branch 1 taken 5184 times.
|
1829952 | for (int x = 0; x < w; x++) |
| 1172 | 1824768 | dst[x] = s->cell[ppos[x]]; | |
| 1173 | } | ||
| 1174 | } | ||
| 1175 | |||
| 1176 | /* reset the cell table for the next frame */ | ||
| 1177 |
2/2✓ Branch 0 taken 519270 times.
✓ Branch 1 taken 36 times.
|
519306 | for (int i = 0; i < s->nb_used; i++) |
| 1178 | 519270 | s->cell[s->list[i].pos] = 0; | |
| 1179 |
2/2✓ Branch 0 taken 12787 times.
✓ Branch 1 taken 36 times.
|
12823 | for (int i = 0; i < s->nb_touched; i++) |
| 1180 | 12787 | s->cell[s->touched[i]] = 0; | |
| 1181 | 36 | s->nb_touched = 0; | |
| 1182 | |||
| 1183 | 36 | av_frame_free(&in); | |
| 1184 | 36 | return ff_filter_frame(outlink, out); | |
| 1185 | |||
| 1186 | ✗ | fail: | |
| 1187 | ✗ | for (int i = 0; i < s->nb_used; i++) | |
| 1188 | ✗ | s->cell[s->list[i].pos] = 0; | |
| 1189 | ✗ | for (int i = 0; i < s->nb_touched; i++) | |
| 1190 | ✗ | s->cell[s->touched[i]] = 0; | |
| 1191 | ✗ | s->nb_touched = 0; | |
| 1192 | ✗ | av_frame_free(&in); | |
| 1193 | ✗ | av_frame_free(&out); | |
| 1194 | ✗ | return ret; | |
| 1195 | } | ||
| 1196 | |||
| 1197 | 6 | static int config_input(AVFilterLink *inlink) | |
| 1198 | { | ||
| 1199 | 6 | AVFilterContext *ctx = inlink->dst; | |
| 1200 | 6 | LatticePalContext *s = ctx->priv; | |
| 1201 | 6 | const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); | |
| 1202 | 6 | const int N = s->density; | |
| 1203 | |||
| 1204 | 6 | s->pixstep = desc->comp[0].step; | |
| 1205 | 6 | s->ro = desc->comp[0].offset; | |
| 1206 | 6 | s->go = desc->comp[1].offset; | |
| 1207 | 6 | s->bo = desc->comp[2].offset; | |
| 1208 | ✗ | s->ao = desc->nb_components == 4 && (desc->flags & AV_PIX_FMT_FLAG_ALPHA) | |
| 1209 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | ? desc->comp[3].offset : -1; |
| 1210 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | s->nc = s->alpha ? 4 : 3; |
| 1211 | |||
| 1212 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
6 | if (s->alpha && N > MAX_DENSITY_ALPHA) { |
| 1213 | ✗ | av_log(ctx, AV_LOG_ERROR, | |
| 1214 | "density is limited to %d when the alpha channel is quantized\n", | ||
| 1215 | MAX_DENSITY_ALPHA); | ||
| 1216 | ✗ | return AVERROR(EINVAL); | |
| 1217 | } | ||
| 1218 | |||
| 1219 | 6 | s->scale = N / 255.f; | |
| 1220 |
2/2✓ Branch 0 taken 173 times.
✓ Branch 1 taken 6 times.
|
179 | for (int i = 0; i <= N; i++) |
| 1221 | 173 | s->idx2val[i] = lrintf(i * 255.f / N); | |
| 1222 | |||
| 1223 |
2/2✓ Branch 0 taken 1536 times.
✓ Branch 1 taken 6 times.
|
1542 | for (int i = 0; i < AVPALETTE_COUNT; i++) |
| 1224 | 1536 | s->prev_pal[i] = 0xFF000000; | |
| 1225 | |||
| 1226 | 6 | s->dim = N + 1; | |
| 1227 | 6 | s->min_gap = 255; | |
| 1228 |
2/2✓ Branch 0 taken 167 times.
✓ Branch 1 taken 6 times.
|
173 | for (int i = 1; i <= N; i++) |
| 1229 | 167 | s->min_gap = FFMIN(s->min_gap, s->idx2val[i] - s->idx2val[i - 1]); | |
| 1230 | |||
| 1231 | 6 | av_freep(&s->cell); | |
| 1232 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
12 | s->cell = av_calloc(s->nc == 4 ? (size_t)s->dim * s->dim * s->dim * s->dim |
| 1233 | 6 | : (size_t)s->dim * s->dim * s->dim, | |
| 1234 | sizeof(*s->cell)); | ||
| 1235 | 6 | av_freep(&s->pixpos); | |
| 1236 | 6 | s->pixpos = av_malloc_array((size_t)inlink->w * inlink->h, sizeof(*s->pixpos)); | |
| 1237 |
2/4✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
|
6 | if (!s->cell || !s->pixpos) |
| 1238 | ✗ | return AVERROR(ENOMEM); | |
| 1239 | |||
| 1240 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
|
6 | if (s->dither == DITHERING_BAYER) { |
| 1241 | /* The per-channel period of the scaled D3 lattice is 2*step, not | ||
| 1242 | * step: translating the lattice by step*e_i flips the parity | ||
| 1243 | * constraint, only 2*step*e_i maps it onto itself. The dither | ||
| 1244 | * offsets must span one period for intermediate colors to be | ||
| 1245 | * reproduced on average, hence the amplitude of 2*step. | ||
| 1246 | * | ||
| 1247 | * The matrix and the per-channel decorrelation follow the classic | ||
| 1248 | * swscale ordered dither (libswscale/yuv2rgb.c): all channels use | ||
| 1249 | * ff_dither_8x8_73, green shifted by one column and blue with | ||
| 1250 | * flipped rows. The channel patterns are strongly anti-correlated | ||
| 1251 | * (-0.94 R/G, -0.60 R/B), which keeps the offset sum and with it | ||
| 1252 | * the luminance noise small, and it interacts well with the D3 | ||
| 1253 | * quantizer: over a 40..215 color cube this scheme measures the | ||
| 1254 | * smallest flat field bias (max 8.3, mean 3.5) and the smallest | ||
| 1255 | * luminance RMS noise (15.3) of all evaluated 8x8 schemes. With | ||
| 1256 | * alpha, the fourth channel combines both transforms. */ | ||
| 1257 | 2 | const float amp = 2.f * 255.f / N; | |
| 1258 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 2 times.
|
18 | for (int y = 0; y < 8; y++) { |
| 1259 |
2/2✓ Branch 0 taken 128 times.
✓ Branch 1 taken 16 times.
|
144 | for (int x = 0; x < 8; x++) { |
| 1260 | 128 | const int i = y << 3 | x; | |
| 1261 | 128 | s->ordered_dither[0][i] = lrintf(((dither_8x8_73[y][x] + 0.5f) / 64.f - 0.5f) * amp); | |
| 1262 | 128 | s->ordered_dither[1][i] = lrintf(((dither_8x8_73[y][(x + 1) & 7] + 0.5f) / 64.f - 0.5f) * amp); | |
| 1263 | 128 | s->ordered_dither[2][i] = lrintf(((dither_8x8_73[y ^ 7][x] + 0.5f) / 64.f - 0.5f) * amp); | |
| 1264 | 128 | s->ordered_dither[3][i] = lrintf(((dither_8x8_73[y ^ 7][(x + 1) & 7] + 0.5f) / 64.f - 0.5f) * amp); | |
| 1265 | } | ||
| 1266 | } | ||
| 1267 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | } else if (s->dither == DITHERING_BLUE_NOISE) { |
| 1268 | /* Dither in a lattice adapted frame: step*(1,1,0), step*(1,-1,0) | ||
| 1269 | * and 2*step*(0,0,1) span an orthogonal sublattice of the color | ||
| 1270 | * lattice, so offsets drawn uniformly from its fundamental box tile | ||
| 1271 | * the color space under lattice translations, and since Dn Voronoi | ||
| 1272 | * cells are centrally symmetric the dithered average is exactly the | ||
| 1273 | * input color. Independently seeded void-and-cluster masks | ||
| 1274 | * a, b, c (and d with alpha) drive the axes: | ||
| 1275 | * tR = step * (a + b - 1) | ||
| 1276 | * tG = step * (a - b) | ||
| 1277 | * tB = step * (2c - 1) | ||
| 1278 | * Compared with independent per-channel offsets over the axis | ||
| 1279 | * aligned (-step,step)^3 box (index 4 instead of 2) this cuts the | ||
| 1280 | * red/green noise variance in half; the remaining full range axis | ||
| 1281 | * is assigned to blue, the perceptually least weighted channel. | ||
| 1282 | * For D4 the sublattice step*{(1,1,0,0), (1,-1,0,0), (0,0,1,1), | ||
| 1283 | * (0,0,1,-1)} is orthogonal with equally long axes, so all four | ||
| 1284 | * channels get the halved variance: | ||
| 1285 | * tB = step * (c + d - 1) | ||
| 1286 | * tA = step * (c - d) */ | ||
| 1287 | ✗ | const float step = 255.f / N; | |
| 1288 | float kern[VC_KSIZE * VC_KSIZE]; | ||
| 1289 | ✗ | uint16_t *ranks[4] = { NULL }; | |
| 1290 | ✗ | uint8_t *bits = NULL; | |
| 1291 | ✗ | float *energy = NULL, *e1 = NULL; | |
| 1292 | ✗ | int ret = 0; | |
| 1293 | |||
| 1294 | ✗ | for (int dy = -VC_RADIUS; dy <= VC_RADIUS; dy++) | |
| 1295 | ✗ | for (int dx = -VC_RADIUS; dx <= VC_RADIUS; dx++) | |
| 1296 | ✗ | kern[(dy + VC_RADIUS) * VC_KSIZE + dx + VC_RADIUS] = | |
| 1297 | ✗ | expf(-(dx * dx + dy * dy) / (2 * VC_SIGMA * VC_SIGMA)); | |
| 1298 | |||
| 1299 | ✗ | bits = av_malloc_array(VC_AREA, sizeof(*bits)); | |
| 1300 | ✗ | energy = av_malloc_array(VC_AREA, sizeof(*energy)); | |
| 1301 | ✗ | e1 = av_malloc_array(VC_AREA, sizeof(*e1)); | |
| 1302 | ✗ | ret = !bits || !energy || !e1 ? AVERROR(ENOMEM) : 0; | |
| 1303 | |||
| 1304 | ✗ | for (int p = 0; p < s->nc && ret >= 0; p++) { | |
| 1305 | AVLFG lfg; | ||
| 1306 | |||
| 1307 | ✗ | ranks[p] = av_malloc_array(VC_AREA, sizeof(*ranks[p])); | |
| 1308 | ✗ | if (!s->blue_dither[p]) | |
| 1309 | ✗ | s->blue_dither[p] = av_malloc_array(VC_AREA, sizeof(*s->blue_dither[p])); | |
| 1310 | ✗ | if (!ranks[p] || !s->blue_dither[p]) { | |
| 1311 | ✗ | ret = AVERROR(ENOMEM); | |
| 1312 | ✗ | break; | |
| 1313 | } | ||
| 1314 | ✗ | av_lfg_init(&lfg, 0xB1DE + p); | |
| 1315 | ✗ | vc_generate(ranks[p], bits, energy, e1, kern, &lfg); | |
| 1316 | } | ||
| 1317 | |||
| 1318 | ✗ | if (ret >= 0) { | |
| 1319 | ✗ | for (int i = 0; i < VC_AREA; i++) { | |
| 1320 | ✗ | const float a = (ranks[0][i] + 0.5f) / VC_AREA; | |
| 1321 | ✗ | const float b = (ranks[1][i] + 0.5f) / VC_AREA; | |
| 1322 | ✗ | const float c = (ranks[2][i] + 0.5f) / VC_AREA; | |
| 1323 | |||
| 1324 | ✗ | s->blue_dither[0][i] = lrintf(step * (a + b - 1.f)); | |
| 1325 | ✗ | s->blue_dither[1][i] = lrintf(step * (a - b)); | |
| 1326 | ✗ | if (s->nc == 4) { | |
| 1327 | ✗ | const float dd = (ranks[3][i] + 0.5f) / VC_AREA; | |
| 1328 | |||
| 1329 | ✗ | s->blue_dither[2][i] = lrintf(step * (c + dd - 1.f)); | |
| 1330 | ✗ | s->blue_dither[3][i] = lrintf(step * (c - dd)); | |
| 1331 | } else { | ||
| 1332 | ✗ | s->blue_dither[2][i] = lrintf(step * (2.f * c - 1.f)); | |
| 1333 | } | ||
| 1334 | } | ||
| 1335 | } | ||
| 1336 | |||
| 1337 | ✗ | av_freep(&ranks[0]); | |
| 1338 | ✗ | av_freep(&ranks[1]); | |
| 1339 | ✗ | av_freep(&ranks[2]); | |
| 1340 | ✗ | av_freep(&ranks[3]); | |
| 1341 | ✗ | av_freep(&bits); | |
| 1342 | ✗ | av_freep(&energy); | |
| 1343 | ✗ | av_freep(&e1); | |
| 1344 | ✗ | if (ret < 0) | |
| 1345 | ✗ | return ret; | |
| 1346 | } | ||
| 1347 |
4/4✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
|
6 | if (s->dither == DITHERING_FLOYD_STEINBERG || s->refine) { |
| 1348 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 4 times.
|
12 | for (int i = 0; i < 2; i++) { |
| 1349 | 8 | av_freep(&s->err[i]); | |
| 1350 | 8 | s->err[i] = av_calloc(inlink->w + 2, s->nc * sizeof(*s->err[i])); | |
| 1351 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 | if (!s->err[i]) |
| 1352 | ✗ | return AVERROR(ENOMEM); | |
| 1353 | } | ||
| 1354 | } | ||
| 1355 | |||
| 1356 | 6 | return 0; | |
| 1357 | } | ||
| 1358 | |||
| 1359 | 12 | static av_cold void uninit(AVFilterContext *ctx) | |
| 1360 | { | ||
| 1361 | 12 | LatticePalContext *s = ctx->priv; | |
| 1362 | |||
| 1363 | 12 | av_freep(&s->err[0]); | |
| 1364 | 12 | av_freep(&s->err[1]); | |
| 1365 |
2/2✓ Branch 0 taken 48 times.
✓ Branch 1 taken 12 times.
|
60 | for (int i = 0; i < 4; i++) |
| 1366 | 48 | av_freep(&s->blue_dither[i]); | |
| 1367 | 12 | av_freep(&s->cell); | |
| 1368 | 12 | av_freep(&s->pixpos); | |
| 1369 | 12 | av_freep(&s->list); | |
| 1370 | 12 | av_freep(&s->alive_arr); | |
| 1371 | 12 | av_freep(&s->alive_pos); | |
| 1372 | 12 | av_freep(&s->touched); | |
| 1373 | 12 | } | |
| 1374 | |||
| 1375 | static const AVFilterPad latticepal_inputs[] = { | ||
| 1376 | { | ||
| 1377 | .name = "default", | ||
| 1378 | .type = AVMEDIA_TYPE_VIDEO, | ||
| 1379 | .filter_frame = filter_frame, | ||
| 1380 | .config_props = config_input, | ||
| 1381 | }, | ||
| 1382 | }; | ||
| 1383 | |||
| 1384 | const FFFilter ff_vf_latticepal = { | ||
| 1385 | .p.name = "latticepal", | ||
| 1386 | .p.description = NULL_IF_CONFIG_SMALL("Convert RGB to PAL8 using a per-frame FCC lattice palette."), | ||
| 1387 | .p.priv_class = &latticepal_class, | ||
| 1388 | .priv_size = sizeof(LatticePalContext), | ||
| 1389 | .uninit = uninit, | ||
| 1390 | FILTER_INPUTS(latticepal_inputs), | ||
| 1391 | FILTER_OUTPUTS(ff_video_default_filterpad), | ||
| 1392 | FILTER_QUERY_FUNC2(query_formats), | ||
| 1393 | }; | ||
| 1394 |