FFmpeg
Loading...
Searching...
No Matches
vf_latticepal.c
Go to the documentation of this file.
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
55
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
73typedef 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
82typedef 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
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
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
115
116#define OFFSET(x) offsetof(LatticePalContext, x)
117#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
118static 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
136
138 AVFilterFormatsConfig **cfg_in,
139 AVFilterFormatsConfig **cfg_out)
140{
141 static const enum AVPixelFormat in_fmts[] = {
148 };
149 static const enum AVPixelFormat out_fmts[] = { AV_PIX_FMT_PAL8, AV_PIX_FMT_NONE };
150 int ret;
151
152 if ((ret = ff_formats_ref(ff_make_pixel_format_list(in_fmts), &cfg_in[0]->formats)) < 0)
153 return ret;
154 if ((ret = ff_formats_ref(ff_make_pixel_format_list(out_fmts), &cfg_out[0]->formats)) < 0)
155 return ret;
156 return 0;
157}
158
159/* Classic swscale ordered dither matrix (libswscale/output.c), 73 levels. */
160static 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 */
175static 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 */
194static 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 */
219static 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 */
281static av_always_inline void quant_dn(const LatticePalContext *s, const int *in, int f[4])
282{
283 const int N = s->density;
284 const int nc = s->nc;
285 int sum = 0;
286 float d[4];
287
288 for (int i = 0; i < nc; i++) {
289 const float u = av_clipf(in[i] * s->scale, 0.f, N);
290 f[i] = lrintf(u);
291 d[i] = u - f[i];
292 sum += f[i];
293 }
294
295 if (sum & 1) {
296 int k = 0, dir;
297 for (int i = 1; i < nc; i++)
298 if (fabsf(d[i]) > fabsf(d[k]))
299 k = i;
300 dir = d[k] >= 0.f ? 1 : -1;
301 if (f[k] + dir < 0 || f[k] + dir > N)
302 dir = -dir;
303 f[k] += dir;
304 }
305}
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 */
314{
315 int pos = f[0];
316 int idx;
317
318 for (int i = 1; i < s->nc; i++)
319 pos = pos * s->dim + f[i];
320 idx = s->cell[pos];
321
322 if (!idx) {
323 PalEntry *e;
324
325 if (s->nb_used == s->nb_alloc) {
326 const int na = FFMAX(s->nb_alloc * 2, 512);
327 PalEntry *nl = av_realloc_array(s->list, na, sizeof(*nl));
328 if (!nl)
329 return AVERROR(ENOMEM);
330 s->list = nl;
331 s->nb_alloc = na;
332 }
333 e = &s->list[s->nb_used];
334 e->pos = pos;
335 e->ci[0] = f[0];
336 e->ci[1] = f[1];
337 e->ci[2] = f[2];
338 e->ci[3] = s->nc == 4 ? f[3] : 0;
339 e->alive = 1;
340 e->count = 0;
341 s->cell[pos] = idx = ++s->nb_used;
342 }
343 s->list[idx - 1].count++;
344 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 */
360static int nearest_alive(LatticePalContext *s, const uint8_t ci[4], int self, int *out_d2)
361{
362 PalEntry *list = s->list;
363 const uint8_t *val = s->idx2val;
364 const int dim = s->dim;
365 const int i0 = ci[0], i1 = ci[1], i2 = ci[2], i3 = ci[3];
366 const int v0 = val[i0], v1 = val[i1], v2 = val[i2], v3 = val[i3];
367 int best = -1, bestd = INT_MAX;
368
369 if (s->nb_alive > 0 && s->nb_alive <= 1024) {
370 for (int k = 0; k < s->nb_alive; k++) {
371 const int j = s->alive_arr[k];
372 const PalEntry *o = &list[j];
373 int d2, dv;
374
375 if (j == self)
376 continue;
377 dv = val[o->ci[0]] - v0; d2 = dv * dv;
378 dv = val[o->ci[1]] - v1; d2 += dv * dv;
379 dv = val[o->ci[2]] - v2; d2 += dv * dv;
380 dv = val[o->ci[3]] - v3; d2 += dv * dv;
381 if (d2 < bestd) {
382 bestd = d2;
383 best = j;
384 }
385 }
386 *out_d2 = best >= 0 ? bestd : 0;
387 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 for (int r = 1; r < dim; r++) {
420 if (best >= 0 && (int64_t)r * s->min_gap * r * s->min_gap > bestd)
421 break;
422 for (int a = FFMAX(i0 - r, 0); a <= FFMIN(i0 + r, dim - 1); a++) {
423 const int da = FFABS(a - i0);
424 for (int b = FFMAX(i1 - r, 0); b <= FFMIN(i1 + r, dim - 1); b++) {
425 const int dab = FFMAX(da, FFABS(b - i1));
426
427 if (s->nc == 3) {
428 if (dab == r) {
429 for (int c = FFMAX(i2 - r, 0); c <= FFMIN(i2 + r, dim - 1); c++)
430 if (!((a + b + c) & 1))
431 CHECK_CELL3(a, b, c);
432 } else {
433 if (i2 - r >= 0 && !((a + b + i2 - r) & 1))
434 CHECK_CELL3(a, b, i2 - r);
435 if (i2 + r < dim && !((a + b + i2 + r) & 1))
436 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 *out_d2 = best >= 0 ? bestd : 0;
459 return best;
460}
461
462static void nn_search(LatticePalContext *s, int self)
463{
464 int d2;
465
466 s->list[self].nn = nearest_alive(s, s->list[self].ci, self, &d2);
467 s->list[self].d2 = d2;
468}
469
470typedef 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
475static void heap_push(HeapEnt *heap, int *nb, int64_t imp, int idx)
476{
477 int i = (*nb)++;
478
479 while (i > 0 && heap[(i - 1) / 2].imp > imp) {
480 heap[i] = heap[(i - 1) / 2];
481 i = (i - 1) / 2;
482 }
483 heap[i].imp = imp;
484 heap[i].idx = idx;
485}
486
487static HeapEnt heap_pop(HeapEnt *heap, int *nb)
488{
489 const HeapEnt top = heap[0];
490 const HeapEnt last = heap[--*nb];
491 int i = 0, c;
492
493 while ((c = 2 * i + 1) < *nb) {
494 if (c + 1 < *nb && heap[c + 1].imp < heap[c].imp)
495 c++;
496 if (heap[c].imp >= last.imp)
497 break;
498 heap[i] = heap[c];
499 i = c;
500 }
501 heap[i] = last;
502 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 */
516static int reduce_colors(LatticePalContext *s, int target)
517{
518 PalEntry *list = s->list;
519 int alive;
520 int nb_heap = 0, cap = s->nb_used + 64;
521 HeapEnt *heap = av_malloc_array(cap, sizeof(*heap));
522
523 if (!heap)
524 return AVERROR(ENOMEM);
525
526 if (s->alive_alloc < s->nb_used) {
527 av_freep(&s->alive_arr);
528 av_freep(&s->alive_pos);
529 s->alive_arr = av_malloc_array(s->nb_used, sizeof(*s->alive_arr));
530 s->alive_pos = av_malloc_array(s->nb_used, sizeof(*s->alive_pos));
531 if (!s->alive_arr || !s->alive_pos) {
532 av_free(heap);
533 return AVERROR(ENOMEM);
534 }
535 s->alive_alloc = s->nb_used;
536 }
537 s->nb_alive = 0;
538 for (int i = 0; i < s->nb_used; i++) {
539 if (!list[i].alive)
540 continue;
541 s->alive_pos[i] = s->nb_alive;
542 s->alive_arr[s->nb_alive++] = i;
543 }
544 alive = s->nb_alive;
545
546 for (int k = 0; k < s->nb_alive; k++) {
547 const int i = s->alive_arr[k];
548
549 nn_search(s, i);
550 heap_push(heap, &nb_heap, list[i].count * list[i].d2, i);
551 }
552
553 while (alive > target) {
554 const HeapEnt top = heap_pop(heap, &nb_heap);
555 const int i = top.idx;
556 int64_t imp;
557
558 if (!list[i].alive)
559 continue;
560 if (!list[list[i].nn].alive)
561 nn_search(s, i);
562 imp = list[i].count * list[i].d2;
563 if (imp != top.imp) {
564 /* stale entry, reinsert with the corrected impact */
565 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 heap_push(heap, &nb_heap, imp, i);
575 continue;
576 }
577 list[i].alive = 0;
578 list[list[i].nn].count += list[i].count;
579 alive--;
580 /* swap-remove from the compact live list */
581 s->alive_arr[s->alive_pos[i]] = s->alive_arr[--s->nb_alive];
582 s->alive_pos[s->alive_arr[s->nb_alive]] = s->alive_pos[i];
583 }
584
585 av_free(heap);
586 return 0;
587}
588
589/** Palette color (AARRGGBB) of a used color list entry. */
590static av_always_inline uint32_t entry_color(const LatticePalContext *s, const PalEntry *e)
591{
592 return (s->nc == 4 ? (uint32_t)s->idx2val[e->ci[3]] << 24 : 0xFF000000) |
593 s->idx2val[e->ci[0]] << 16 |
594 s->idx2val[e->ci[1]] << 8 |
595 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 */
615{
616 const int w = in->width, h = in->height;
617 const int ro = s->ro, go = s->go, bo = s->bo, ao = s->ao, pixstep = s->pixstep;
618 const int nc = s->nc;
619 const uint32_t *pal = (const uint32_t *)out->data[1];
620
621 memset(s->err[0], 0, (w + 2) * nc * sizeof(*s->err[0]));
622 memset(s->err[1], 0, (w + 2) * nc * sizeof(*s->err[1]));
623
624 for (int y = 0; y < h; y++) {
625 const uint8_t *src = in->data[0] + y * in->linesize[0];
626 uint8_t *dst = out->data[0] + y * out->linesize[0];
627 const int dir = (y & 1) ? -1 : 1;
628 const int x0 = dir > 0 ? 0 : w - 1;
629 int32_t *err_cur = s->err[ y & 1] + nc;
630 int32_t *err_next = s->err[(y + 1) & 1] + nc;
631 int f[4], px[4];
632
633 px[3] = 255;
634 memset(err_next - nc, 0, (w + 2) * nc * sizeof(*err_next));
635
636 for (int k = 0; k < w; k++) {
637 const int x = x0 + k * dir;
638 const int32_t *e = &err_cur[x * nc];
639 uint32_t c;
640 int pos, slot1;
641
642 px[0] = av_clip_uint8(src[x * pixstep + ro] + ((e[0] + 8) >> 4));
643 px[1] = av_clip_uint8(src[x * pixstep + go] + ((e[1] + 8) >> 4));
644 px[2] = av_clip_uint8(src[x * pixstep + bo] + ((e[2] + 8) >> 4));
645 if (nc == 4)
646 px[3] = av_clip_uint8((ao >= 0 ? src[x * pixstep + ao] : 255) + ((e[3] + 8) >> 4));
647
648 quant_dn(s, px, f);
649 pos = f[0];
650 for (int i = 1; i < nc; i++)
651 pos = pos * s->dim + f[i];
652
653 slot1 = s->cell[pos];
654 if (!slot1) {
655 const uint8_t *val = s->idx2val;
656 int bd = INT_MAX;
657
658 for (int k = 0; k < s->nb_alive; k++) {
659 const PalEntry *o = &s->list[s->alive_arr[k]];
660 int d2, dv;
661
662 dv = val[o->ci[0]] - val[f[0]]; d2 = dv * dv;
663 dv = val[o->ci[1]] - val[f[1]]; d2 += dv * dv;
664 dv = val[o->ci[2]] - val[f[2]]; d2 += dv * dv;
665 if (nc == 4) {
666 dv = val[o->ci[3]] - val[f[3]]; d2 += dv * dv;
667 }
668 if (d2 < bd) {
669 bd = d2;
670 slot1 = o->nn + 1;
671 }
672 }
673 if (s->nb_touched == s->touched_alloc) {
674 const int na = FFMAX(s->touched_alloc * 2, 1024);
675 int *nt = av_realloc_array(s->touched, na, sizeof(*nt));
676 if (!nt)
677 return AVERROR(ENOMEM);
678 s->touched = nt;
679 s->touched_alloc = na;
680 }
681 s->touched[s->nb_touched++] = pos;
682 s->cell[pos] = slot1;
683 }
684 dst[x] = slot1 - 1;
685
686 c = pal[slot1 - 1];
687 for (int i = 0; i < nc; i++) {
688 static const int sh[4] = { 16, 8, 0, 24 };
689 const int qerr = px[i] - (int)(c >> sh[i] & 0xff);
690
691 err_cur [(x + dir) * nc + i] += qerr * 7;
692 err_next[(x - dir) * nc + i] += qerr * 3;
693 err_next[ x * nc + i] += qerr * 5;
694 err_next[(x + dir) * nc + i] += qerr;
695 }
696 }
697 }
698 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 */
713{
714 const int w = in->width, h = in->height;
715 const int nc = s->nc;
716 const uint32_t *pal = (const uint32_t *)out->data[1];
717 const uint8_t *val = s->idx2val;
718 PalEntry *list = s->list;
719
720 memset(s->err[0], 0, (w + 2) * nc * sizeof(*s->err[0]));
721 memset(s->err[1], 0, (w + 2) * nc * sizeof(*s->err[1]));
722
723 for (int y = 0; y < h; y++) {
724 const uint32_t *ppos = s->pixpos + (size_t)y * w;
725 uint8_t *dst = out->data[0] + y * out->linesize[0];
726 const int dir = (y & 1) ? -1 : 1;
727 const int x0 = dir > 0 ? 0 : w - 1;
728 int32_t *err_cur = s->err[ y & 1] + nc;
729 int32_t *err_next = s->err[(y + 1) & 1] + nc;
730 int f[4], px[4];
731
732 px[3] = 255;
733 memset(err_next - nc, 0, (w + 2) * nc * sizeof(*err_next));
734
735 for (int k = 0; k < w; k++) {
736 const int x = x0 + k * dir;
737 const PalEntry *e1 = &list[s->cell[ppos[x]] - 1];
738 const int32_t *e = &err_cur[x * nc];
739 uint32_t c;
740 int pos, v, slot;
741
742 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 for (int i = 0; i < nc; i++)
750 px[i] = av_clip_uint8(val[e1->ci[i]] + ((e[i] + 8) >> 4));
751
752 quant_dn(s, px, f);
753 pos = f[0];
754 for (int i = 1; i < nc; i++)
755 pos = pos * s->dim + f[i];
756
757 v = s->cell[pos];
758 if (v > 0) {
759 slot = list[v - 1].nn;
760 } else if (v < 0) {
761 slot = -v - 1;
762 } else {
763 int bd = INT_MAX;
764
765 slot = 0;
766 for (int j = 0; j < s->nb_alive; j++) {
767 const PalEntry *o = &list[s->alive_arr[j]];
768 int d2, dv;
769
770 dv = val[o->ci[0]] - val[f[0]]; d2 = dv * dv;
771 dv = val[o->ci[1]] - val[f[1]]; d2 += dv * dv;
772 dv = val[o->ci[2]] - val[f[2]]; d2 += dv * dv;
773 if (nc == 4) {
774 dv = val[o->ci[3]] - val[f[3]]; d2 += dv * dv;
775 }
776 if (d2 < bd) {
777 bd = d2;
778 slot = o->nn;
779 }
780 }
781 if (s->nb_touched == s->touched_alloc) {
782 const int na = FFMAX(s->touched_alloc * 2, 1024);
783 int *nt = av_realloc_array(s->touched, na, sizeof(*nt));
784 if (!nt)
785 return AVERROR(ENOMEM);
786 s->touched = nt;
787 s->touched_alloc = na;
788 }
789 s->touched[s->nb_touched++] = pos;
790 s->cell[pos] = -(slot + 1);
791 }
792 dst[x] = slot;
793
794 c = pal[slot];
795 for (int i = 0; i < nc; i++) {
796 static const int sh[4] = { 16, 8, 0, 24 };
797 const int qerr = px[i] - (int)(c >> sh[i] & 0xff);
798
799 err_cur [(x + dir) * nc + i] += qerr * 7;
800 err_next[(x - dir) * nc + i] += qerr * 3;
801 err_next[ x * nc + i] += qerr * 5;
802 err_next[(x + dir) * nc + i] += qerr;
803 }
804 }
805 }
806 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 */
817{
818 const int w = in->width, h = in->height;
819 const int ro = s->ro, go = s->go, bo = s->bo, ao = s->ao, pixstep = s->pixstep;
820 const int nc = s->nc;
821 const uint8_t *val = s->idx2val;
822 PalEntry *list = s->list;
823
824 for (int k = 0; k < s->nb_alive; k++)
825 list[s->alive_arr[k]].count = 0;
826
827 memset(s->err[0], 0, (w + 2) * nc * sizeof(*s->err[0]));
828 memset(s->err[1], 0, (w + 2) * nc * sizeof(*s->err[1]));
829
830 for (int y = 0; y < h; y++) {
831 const uint8_t *src = in->data[0] + y * in->linesize[0];
832 uint32_t *ppos = s->pixpos + (size_t)y * w;
833 const int dir = (y & 1) ? -1 : 1;
834 const int x0 = dir > 0 ? 0 : w - 1;
835 int32_t *err_cur = s->err[ y & 1] + nc;
836 int32_t *err_next = s->err[(y + 1) & 1] + nc;
837 int f[4], px[4];
838
839 px[3] = 255;
840 memset(err_next - nc, 0, (w + 2) * nc * sizeof(*err_next));
841
842 for (int k = 0; k < w; k++) {
843 const int x = x0 + k * dir;
844 const int32_t *e = &err_cur[x * nc];
845 const PalEntry *o;
846 int pos, v, j;
847
848 px[0] = av_clip_uint8(src[x * pixstep + ro] + ((e[0] + 8) >> 4));
849 px[1] = av_clip_uint8(src[x * pixstep + go] + ((e[1] + 8) >> 4));
850 px[2] = av_clip_uint8(src[x * pixstep + bo] + ((e[2] + 8) >> 4));
851 if (nc == 4)
852 px[3] = av_clip_uint8((ao >= 0 ? src[x * pixstep + ao] : 255) + ((e[3] + 8) >> 4));
853
854 quant_dn(s, px, f);
855 pos = f[0];
856 for (int i = 1; i < nc; i++)
857 pos = pos * s->dim + f[i];
858
859 v = s->cell[pos];
860 if (v > 0 && list[v - 1].alive) {
861 j = v - 1;
862 } else if (v < 0 && list[-v - 1].alive) {
863 j = -v - 1;
864 } else {
865 const uint8_t fci[4] = { f[0], f[1], f[2], nc == 4 ? f[3] : 0 };
866 int d2;
867
868 j = nearest_alive(s, fci, -1, &d2);
869 if (!v) {
870 if (s->nb_touched == s->touched_alloc) {
871 const int na = FFMAX(s->touched_alloc * 2, 1024);
872 int *nt = av_realloc_array(s->touched, na, sizeof(*nt));
873 if (!nt)
874 return AVERROR(ENOMEM);
875 s->touched = nt;
876 s->touched_alloc = na;
877 }
878 s->touched[s->nb_touched++] = pos;
879 }
880 s->cell[pos] = -(j + 1);
881 }
882 o = &list[j];
883 list[j].count++;
884 ppos[x] = j;
885
886 for (int i = 0; i < nc; i++) {
887 const int qerr = px[i] - val[o->ci[i]];
888
889 err_cur [(x + dir) * nc + i] += qerr * 7;
890 err_next[(x - dir) * nc + i] += qerr * 3;
891 err_next[ x * nc + i] += qerr * 5;
892 err_next[(x + dir) * nc + i] += qerr;
893 }
894 }
895 }
896 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 */
909{
910 int alive = s->nb_used;
911
912 while (alive > s->max_colors) {
913 const int excess = alive - s->max_colors;
914 const int target = s->max_colors + excess / 2;
915 int ret = reduce_colors(s, target);
916
917 if (ret < 0)
918 return ret;
919 alive = target;
920
921 ret = batch_assign(s, in);
922 if (ret < 0)
923 return ret;
924
925 /* drop the colors the rediffusion no longer uses */
926 for (int k = 0; k < s->nb_alive;) {
927 const int i = s->alive_arr[k];
928
929 if (!s->list[i].count) {
930 s->list[i].alive = 0;
931 s->alive_arr[k] = s->alive_arr[--s->nb_alive];
932 s->alive_pos[s->alive_arr[k]] = k;
933 alive--;
934 } else
935 k++;
936 }
937 }
938 return 0;
939}
940
941static int filter_frame(AVFilterLink *inlink, AVFrame *in)
942{
943 AVFilterContext *ctx = inlink->dst;
944 LatticePalContext *s = ctx->priv;
945 AVFilterLink *outlink = ctx->outputs[0];
946 const int w = in->width, h = in->height;
947 const int ro = s->ro, go = s->go, bo = s->bo, ao = s->ao, pixstep = s->pixstep;
948 const int nc = s->nc;
949 uint32_t *pal;
950 AVFrame *out;
951 int ret, reduced = 0;
952
953 out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
954 if (!out) {
955 av_frame_free(&in);
956 return AVERROR(ENOMEM);
957 }
958 ret = av_frame_copy_props(out, in);
959 if (ret < 0)
960 goto fail;
961
962 s->nb_used = 0;
963 s->nb_alive = 0;
964 if (s->dither == DITHERING_FLOYD_STEINBERG) {
965 memset(s->err[0], 0, (w + 2) * nc * sizeof(*s->err[0]));
966 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 for (int y = 0; y < h; y++) {
971 const uint8_t *src = in->data[0] + y * in->linesize[0];
972 uint32_t *ppos = s->pixpos + (size_t)y * w;
973 int f[4], px[4], pos;
974
975 px[3] = 255;
976
977 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 const int dir = (y & 1) ? -1 : 1;
981 const int x0 = dir > 0 ? 0 : w - 1;
982 int32_t *err_cur = s->err[ y & 1] + nc;
983 int32_t *err_next = s->err[(y + 1) & 1] + nc;
984
985 memset(err_next - nc, 0, (w + 2) * nc * sizeof(*err_next));
986
987 for (int k = 0; k < w; k++) {
988 const int x = x0 + k * dir;
989 const int32_t *e = &err_cur[x * nc];
990
991 px[0] = av_clip_uint8(src[x * pixstep + ro] + ((e[0] + 8) >> 4));
992 px[1] = av_clip_uint8(src[x * pixstep + go] + ((e[1] + 8) >> 4));
993 px[2] = av_clip_uint8(src[x * pixstep + bo] + ((e[2] + 8) >> 4));
994 if (nc == 4)
995 px[3] = av_clip_uint8((ao >= 0 ? src[x * pixstep + ao] : 255) + ((e[3] + 8) >> 4));
996
997 quant_dn(s, px, f);
998 pos = color_inc(s, f);
999 if (pos < 0) {
1000 ret = pos;
1001 goto fail;
1002 }
1003 ppos[x] = pos;
1004
1005 for (int i = 0; i < nc; i++) {
1006 const int qerr = px[i] - s->idx2val[f[i]];
1007
1008 err_cur [(x + dir) * nc + i] += qerr * 7;
1009 err_next[(x - dir) * nc + i] += qerr * 3;
1010 err_next[ x * nc + i] += qerr * 5;
1011 err_next[(x + dir) * nc + i] += qerr;
1012 }
1013 }
1014 } else if (s->dither == DITHERING_BAYER || s->dither == DITHERING_BLUE_NOISE) {
1015 const int blue = s->dither == DITHERING_BLUE_NOISE;
1016 const int mask = blue ? VC_MASK : 7;
1017 const int rowoff = blue ? (y & VC_MASK) << VC_SHIFT : (y & 7) << 3;
1018 const int *dt[4];
1019
1020 for (int i = 0; i < nc; i++)
1021 dt[i] = (blue ? s->blue_dither[i] : s->ordered_dither[i]) + rowoff;
1022
1023 for (int x = 0; x < w; x++) {
1024 px[0] = src[x * pixstep + ro] + dt[0][x & mask];
1025 px[1] = src[x * pixstep + go] + dt[1][x & mask];
1026 px[2] = src[x * pixstep + bo] + dt[2][x & mask];
1027 if (nc == 4)
1028 px[3] = (ao >= 0 ? src[x * pixstep + ao] : 255) + dt[3][x & mask];
1029
1030 quant_dn(s, px, f);
1031 pos = color_inc(s, f);
1032 if (pos < 0) {
1033 ret = pos;
1034 goto fail;
1035 }
1036 ppos[x] = pos;
1037 }
1038 } else {
1039 for (int x = 0; x < w; x++) {
1040 px[0] = src[x * pixstep + ro];
1041 px[1] = src[x * pixstep + go];
1042 px[2] = src[x * pixstep + bo];
1043 if (nc == 4)
1044 px[3] = ao >= 0 ? src[x * pixstep + ao] : 255;
1045
1046 quant_dn(s, px, f);
1047 pos = color_inc(s, f);
1048 if (pos < 0) {
1049 ret = pos;
1050 goto fail;
1051 }
1052 ppos[x] = pos;
1053 }
1054 }
1055 }
1056
1057 reduced = s->nb_used > s->max_colors;
1058 if (reduced) {
1059 if (s->refine == REFINE_BATCHED)
1060 ret = reduce_batched(s, in);
1061 else
1062 ret = reduce_colors(s, s->max_colors);
1063 if (ret < 0)
1064 goto fail;
1066 "pts %"PRId64": %d lattice colors used, %d dropped\n",
1067 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 uint8_t claimed[AVPALETTE_COUNT] = { 0 };
1078
1079 for (int i = 0; i < s->nb_used; i++) {
1080 PalEntry *e = &s->list[i];
1081 uint32_t c;
1082
1083 if (!e->alive)
1084 continue;
1085 e->nn = -1;
1086 c = entry_color(s, e);
1087 for (int slot = 0; slot < AVPALETTE_COUNT; slot++) {
1088 if (!claimed[slot] && s->prev_pal[slot] == c) {
1089 claimed[slot] = 1;
1090 e->nn = slot;
1091 break;
1092 }
1093 }
1094 }
1095 for (int i = 0; i < s->nb_used; i++) {
1096 PalEntry *e = &s->list[i];
1097 uint32_t c;
1098 int bslot = -1;
1099 int64_t bd = INT64_MAX;
1100
1101 if (!e->alive || e->nn >= 0)
1102 continue;
1103 c = entry_color(s, e);
1104 for (int slot = 0; slot < AVPALETTE_COUNT; slot++) {
1105 const uint32_t p = s->prev_pal[slot];
1106 const int da = (int)(p >> 24 ) - (int)(c >> 24 );
1107 const int dr = (int)(p >> 16 & 0xff) - (int)(c >> 16 & 0xff);
1108 const int dg = (int)(p >> 8 & 0xff) - (int)(c >> 8 & 0xff);
1109 const int db = (int)(p & 0xff) - (int)(c & 0xff);
1110 const int64_t d2 = (int64_t)da * da + dr * dr + dg * dg + db * db;
1111
1112 if (!claimed[slot] && d2 < bd) {
1113 bd = d2;
1114 bslot = slot;
1115 }
1116 }
1117 claimed[bslot] = 1;
1118 e->nn = bslot;
1119 }
1120
1121 pal = (uint32_t *)out->data[1];
1122 memcpy(pal, s->prev_pal, AVPALETTE_SIZE);
1123 for (int i = 0; i < s->nb_used; i++) {
1124 const PalEntry *e = &s->list[i];
1125
1126 if (e->alive)
1127 pal[e->nn] = entry_color(s, e);
1128 }
1129 memcpy(s->prev_pal, pal, AVPALETTE_SIZE);
1130 }
1131 if (!(s->refine == REFINE_BATCHED && reduced)) {
1132 for (int i = 0; i < s->nb_used; i++) {
1133 PalEntry *e = &s->list[i];
1134
1135 if (!e->alive) {
1136 nn_search(s, i);
1137 e->nn = s->list[e->nn].nn;
1138 }
1139 }
1140 }
1141
1142 if (s->refine == REFINE_BATCHED && reduced) {
1143 /* pixpos holds the list index assigned by the last rediffusion */
1144 for (int y = 0; y < h; y++) {
1145 const uint32_t *ppos = s->pixpos + (size_t)y * w;
1146 uint8_t *dst = out->data[0] + y * out->linesize[0];
1147
1148 for (int x = 0; x < w; x++)
1149 dst[x] = s->list[ppos[x]].nn;
1150 }
1151 } else if (s->refine == REFINE_RESIDUAL && reduced) {
1152 /* needs the cell table still holding list indices */
1153 ret = refine_residual(s, out, in);
1154 if (ret < 0)
1155 goto fail;
1156 } else if (s->refine == REFINE_FULL && reduced) {
1157 /* rewrite the cell table to slot + 1, 0 keeps meaning unused */
1158 for (int i = 0; i < s->nb_used; i++)
1159 s->cell[s->list[i].pos] = s->list[i].nn + 1;
1160 ret = refine_full(s, out, in);
1161 if (ret < 0)
1162 goto fail;
1163 } else {
1164 /* second pass: write the palette indices */
1165 for (int i = 0; i < s->nb_used; i++)
1166 s->cell[s->list[i].pos] = s->list[i].nn;
1167 for (int y = 0; y < h; y++) {
1168 const uint32_t *ppos = s->pixpos + (size_t)y * w;
1169 uint8_t *dst = out->data[0] + y * out->linesize[0];
1170
1171 for (int x = 0; x < w; x++)
1172 dst[x] = s->cell[ppos[x]];
1173 }
1174 }
1175
1176 /* reset the cell table for the next frame */
1177 for (int i = 0; i < s->nb_used; i++)
1178 s->cell[s->list[i].pos] = 0;
1179 for (int i = 0; i < s->nb_touched; i++)
1180 s->cell[s->touched[i]] = 0;
1181 s->nb_touched = 0;
1182
1183 av_frame_free(&in);
1184 return ff_filter_frame(outlink, out);
1185
1186fail:
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);
1194 return ret;
1195}
1196
1197static int config_input(AVFilterLink *inlink)
1198{
1199 AVFilterContext *ctx = inlink->dst;
1200 LatticePalContext *s = ctx->priv;
1202 const int N = s->density;
1203
1204 s->pixstep = desc->comp[0].step;
1205 s->ro = desc->comp[0].offset;
1206 s->go = desc->comp[1].offset;
1207 s->bo = desc->comp[2].offset;
1208 s->ao = desc->nb_components == 4 && (desc->flags & AV_PIX_FMT_FLAG_ALPHA)
1209 ? desc->comp[3].offset : -1;
1210 s->nc = s->alpha ? 4 : 3;
1211
1212 if (s->alpha && N > MAX_DENSITY_ALPHA) {
1214 "density is limited to %d when the alpha channel is quantized\n",
1216 return AVERROR(EINVAL);
1217 }
1218
1219 s->scale = N / 255.f;
1220 for (int i = 0; i <= N; i++)
1221 s->idx2val[i] = lrintf(i * 255.f / N);
1222
1223 for (int i = 0; i < AVPALETTE_COUNT; i++)
1224 s->prev_pal[i] = 0xFF000000;
1225
1226 s->dim = N + 1;
1227 s->min_gap = 255;
1228 for (int i = 1; i <= N; i++)
1229 s->min_gap = FFMIN(s->min_gap, s->idx2val[i] - s->idx2val[i - 1]);
1230
1231 av_freep(&s->cell);
1232 s->cell = av_calloc(s->nc == 4 ? (size_t)s->dim * s->dim * s->dim * s->dim
1233 : (size_t)s->dim * s->dim * s->dim,
1234 sizeof(*s->cell));
1235 av_freep(&s->pixpos);
1236 s->pixpos = av_malloc_array((size_t)inlink->w * inlink->h, sizeof(*s->pixpos));
1237 if (!s->cell || !s->pixpos)
1238 return AVERROR(ENOMEM);
1239
1240 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 const float amp = 2.f * 255.f / N;
1258 for (int y = 0; y < 8; y++) {
1259 for (int x = 0; x < 8; x++) {
1260 const int i = y << 3 | x;
1261 s->ordered_dither[0][i] = lrintf(((dither_8x8_73[y][x] + 0.5f) / 64.f - 0.5f) * amp);
1262 s->ordered_dither[1][i] = lrintf(((dither_8x8_73[y][(x + 1) & 7] + 0.5f) / 64.f - 0.5f) * amp);
1263 s->ordered_dither[2][i] = lrintf(((dither_8x8_73[y ^ 7][x] + 0.5f) / 64.f - 0.5f) * amp);
1264 s->ordered_dither[3][i] = lrintf(((dither_8x8_73[y ^ 7][(x + 1) & 7] + 0.5f) / 64.f - 0.5f) * amp);
1265 }
1266 }
1267 } 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 if (s->dither == DITHERING_FLOYD_STEINBERG || s->refine) {
1348 for (int i = 0; i < 2; i++) {
1349 av_freep(&s->err[i]);
1350 s->err[i] = av_calloc(inlink->w + 2, s->nc * sizeof(*s->err[i]));
1351 if (!s->err[i])
1352 return AVERROR(ENOMEM);
1353 }
1354 }
1355
1356 return 0;
1357}
1358
1360{
1361 LatticePalContext *s = ctx->priv;
1362
1363 av_freep(&s->err[0]);
1364 av_freep(&s->err[1]);
1365 for (int i = 0; i < 4; i++)
1366 av_freep(&s->blue_dither[i]);
1367 av_freep(&s->cell);
1368 av_freep(&s->pixpos);
1369 av_freep(&s->list);
1370 av_freep(&s->alive_arr);
1371 av_freep(&s->alive_pos);
1372 av_freep(&s->touched);
1373}
1374
1376 {
1377 .name = "default",
1378 .type = AVMEDIA_TYPE_VIDEO,
1379 .filter_frame = filter_frame,
1380 .config_props = config_input,
1381 },
1382};
1383
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,
1393};
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
Definition dsp.h:87
static double val(void *priv, double ch)
Definition aeval.c:77
static int query_formats(const AVFilterContext *ctx, AVFilterFormatsConfig **cfg_in, AVFilterFormatsConfig **cfg_out)
Definition aeval.c:246
static int config_input(AVFilterLink *inlink)
#define N
Definition af_mcompand.c:54
const FFFilter ff_vf_latticepal
static FILE * out
static AVFormatContext * ctx
int32_t
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition avfilter.c:1068
Main libavfilter public API header.
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define f(width, name)
Definition cbs_vp8.c:236
#define s(width, name)
Definition cbs_vp9.c:198
#define FLAGS
Definition cmdutils.c:598
#define av_clip_uint8
Definition common.h:106
#define av_clipf
Definition common.h:145
#define FFABS(a)
Absolute value, Note, INT_MIN / INT64_MIN result in undefined behavior as they are not representable ...
Definition common.h:74
#define NULL
Definition coverity.c:32
long long int64_t
Definition coverity.c:34
static __device__ float fabsf(float a)
static int filter_frame(DBEDecodeContext *s, AVFrame *frame)
Definition dolby_e.c:1067
static const uint8_t bits[8]
Definition fastaudio.c:100
int ff_formats_ref(AVFilterFormats *f, AVFilterFormats **ref)
Add ref as a new reference to formats.
Definition formats.c:756
av_warn_unused_result AVFilterFormats * ff_make_pixel_format_list(const enum AVPixelFormat *fmts)
Create a list of supported pixel formats.
#define fail
Definition test.h:479
@ AV_OPT_TYPE_CONST
Special option type for declaring named constants.
Definition opt.h:298
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition opt.h:258
@ AV_OPT_TYPE_BOOL
Underlying C type is int.
Definition opt.h:326
#define AVERROR(e)
Definition error.h:45
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition frame.c:64
int av_frame_copy_props(AVFrame *dst, const AVFrame *src)
Copy only "metadata" fields from src to dst.
Definition frame.c:599
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition log.h:231
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
void * av_realloc_array(void *ptr, size_t nmemb, size_t size)
Definition mem.c:217
@ AVMEDIA_TYPE_VIDEO
Definition avutil.h:200
int a
static const int16_t alpha[]
Definition ilbcdata.h:55
#define r
Definition input.c:42
#define b
Definition input.c:43
static av_cold void uninit(AVBitStreamFilterContext *ctx)
av_cold void av_lfg_init(AVLFG *c, unsigned int seed)
Definition lfg.c:32
static unsigned int av_lfg_get(AVLFG *c)
Get the next random unsigned 32-bit number using an ALFG.
Definition lfg.h:53
#define u(width, name, range_min, range_max)
Definition cbs_apv.c:68
#define FILTER_INPUTS(array)
Definition filters.h:264
#define FILTER_OUTPUTS(array)
Definition filters.h:265
#define AVFILTER_DEFINE_CLASS(fname)
Definition filters.h:478
#define FILTER_QUERY_FUNC2(func)
Definition filters.h:241
#define av_always_inline
Definition attributes.h:72
#define av_cold
Definition attributes.h:117
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition internal.h:88
#define expf(x)
Definition libm.h:285
#define lrintf(x)
Definition libm_mips.h:72
const char * desc
Definition libsvtav1.c:83
uint8_t w
Definition llvidencdsp.c:39
static const uint16_t mask[17]
Definition lzw.c:38
#define FFMIN(a, b)
Definition macros.h:49
#define FFMAX(a, b)
Definition macros.h:47
void * av_calloc(size_t nmemb, size_t size)
Definition mem.c:264
Memory handling functions.
AVOptions.
const AVPixFmtDescriptor * av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt)
Definition pixdesc.c:3460
#define AV_PIX_FMT_FLAG_ALPHA
The pixel format has an alpha channel.
Definition pixdesc.h:147
AVPixelFormat
Pixel format.
Definition pixfmt.h:71
@ AV_PIX_FMT_NONE
Definition pixfmt.h:72
@ AV_PIX_FMT_RGB24
packed RGB 8:8:8, 24bpp, RGBRGB...
Definition pixfmt.h:75
@ AV_PIX_FMT_BGR0
packed BGR 8:8:8, 32bpp, BGRXBGRX... X=unused/undefined
Definition pixfmt.h:265
@ AV_PIX_FMT_ARGB
packed ARGB 8:8:8:8, 32bpp, ARGBARGB...
Definition pixfmt.h:99
@ AV_PIX_FMT_BGRA
packed BGRA 8:8:8:8, 32bpp, BGRABGRA...
Definition pixfmt.h:102
@ AV_PIX_FMT_ABGR
packed ABGR 8:8:8:8, 32bpp, ABGRABGR...
Definition pixfmt.h:101
@ AV_PIX_FMT_0BGR
packed BGR 8:8:8, 32bpp, XBGRXBGR... X=unused/undefined
Definition pixfmt.h:264
@ AV_PIX_FMT_RGBA
packed RGBA 8:8:8:8, 32bpp, RGBARGBA...
Definition pixfmt.h:100
@ AV_PIX_FMT_RGB0
packed RGB 8:8:8, 32bpp, RGBXRGBX... X=unused/undefined
Definition pixfmt.h:263
@ AV_PIX_FMT_PAL8
8 bits with AV_PIX_FMT_RGB32 palette
Definition pixfmt.h:84
@ AV_PIX_FMT_BGR24
packed RGB 8:8:8, 24bpp, BGRBGR...
Definition pixfmt.h:76
@ AV_PIX_FMT_0RGB
packed RGB 8:8:8, 32bpp, XRGBXRGB... X=unused/undefined
Definition pixfmt.h:262
#define AVPALETTE_COUNT
Definition pixfmt.h:33
#define AVPALETTE_SIZE
Definition pixfmt.h:32
formats
Definition signature.h:47
unsigned int pos
Definition spdifenc.c:431
Describe the class of an AVClass context structure.
Definition log.h:76
An instance of a filter.
Definition avfilter.h:273
Lists of formats / etc.
Definition avfilter.h:120
A filter pad used for either input or output.
Definition filters.h:40
This structure describes decoded (raw) audio or video data.
Definition frame.h:472
int64_t pts
Presentation timestamp in time_base units (time when frame should be shown to user).
Definition frame.h:574
uint8_t * data[AV_NUM_DATA_POINTERS]
pointer to the picture/channel planes.
Definition frame.h:493
int width
Definition frame.h:544
int height
Definition frame.h:544
int linesize[AV_NUM_DATA_POINTERS]
For video, a positive or negative value, which is typically indicating the size in bytes of each pict...
Definition frame.h:517
Context structure for the Lagged Fibonacci PRNG.
Definition lfg.h:33
AVOption.
Definition opt.h:428
Descriptor that unambiguously describes how the bits of a pixel are stored in the up to 4 data planes...
Definition pixdesc.h:69
int64_t imp
impact of the removal when the entry was pushed
int idx
used color list index
int * alive_pos
position of each live color in alive_arr
int density
lattice steps per color axis
int32_t * err[2]
Floyd-Steinberg error rows, nc ints per pixel.
int ao
byte offsets of R, G, B, A in an input pixel, ao < 0: opaque
int * blue_dither[4]
per-channel blue noise offsets spanning one lattice period
int * touched
cells filled on demand by the refinement pass
uint8_t idx2val[MAX_DENSITY+1]
lattice index -> 8-bit component value
int nb_alive
entries in alive_arr, 0 outside reduction/remap
int alpha
quantize the alpha channel too (D4 lattice)
int nc
quantized components, 3 or 4
int ordered_dither[4][8 *8]
per-channel bayer offsets spanning one lattice period
int pixstep
bytes per input pixel
float scale
density / 255
int refine
rediffuse the error of dropped colors
int max_colors
palette entries to use at most
int min_gap
smallest idx2val increment
uint32_t prev_pal[AVPALETTE_COUNT]
previous frame's palette, for stable slot assignment
int * alive_arr
compact list of live color indices while reducing
int32_t * cell
dim^nc table: lattice cell -> list index + 1, 0 if unused
uint32_t * pixpos
per-pixel cell position of the quantized color
PalEntry * list
colors used by the current frame
int dim
density + 1, lattice cells per axis
int nn
nearest live color while reducing, then palette slot
int pos
lattice cell position, ((i * dim + j) * dim + k) [* dim + l]
int d2
squared RGB(A) distance to nn
int64_t count
pixels quantizing to this color, incl. absorbed ones
uint8_t ci[4]
lattice indices of the color
uint8_t alive
still part of the palette while reducing
#define av_free(p)
#define av_malloc_array(a, b)
#define av_freep(p)
#define av_log(a,...)
#define src
Definition vp8dsp.c:248
static const uint16_t dither[8][8]
Definition vf_gradfun.c:46
#define VC_SIGMA
static void heap_push(HeapEnt *heap, int *nb, int64_t imp, int idx)
static const AVFilterPad latticepal_inputs[]
#define VC_AREA
#define VC_KSIZE
#define VC_MASK
static int nearest_alive(LatticePalContext *s, const uint8_t ci[4], int self, int *out_d2)
Find the nearest live color of the used color list, excluding self (pass -1 to match any live color).
static void vc_generate(uint16_t *rank, uint8_t *bits, float *energy, float *e1, const float *kern, AVLFG *lfg)
Generate a void-and-cluster blue noise rank matrix (Ulichney 1993): every cell gets a unique rank in ...
#define CHECK_CELL3(a, b, c)
static int batch_assign(LatticePalContext *s, const AVFrame *in)
Rediffuse the whole frame against the current live colors and reassign every pixel,...
dithering_mode
@ DITHERING_FLOYD_STEINBERG
@ DITHERING_NONE
@ DITHERING_BLUE_NOISE
@ NB_DITHERING
@ DITHERING_BAYER
#define VC_RADIUS
static int refine_residual(LatticePalContext *s, AVFrame *out, const AVFrame *in)
Residual refinement pass: rediffuse only the error of the dropped colors, using the first pass quanti...
static void vc_splat(float *energy, const float *kern, int pos, float sign)
Add (sign > 0) or remove (sign < 0) the Gaussian energy contribution of a minority pixel at position ...
static int config_input(AVFilterLink *inlink)
static const AVOption latticepal_options[]
static const uint8_t dither_8x8_73[8][8]
static int refine_full(LatticePalContext *s, AVFrame *out, const AVFrame *in)
Full refinement pass: rediffuse the whole frame against the final palette.
static av_always_inline uint32_t entry_color(const LatticePalContext *s, const PalEntry *e)
Palette color (AARRGGBB) of a used color list entry.
#define MAX_DENSITY
static int filter_frame(AVFilterLink *inlink, AVFrame *in)
static void nn_search(LatticePalContext *s, int self)
static int vc_extreme(const float *energy, const uint8_t *bits, int want_set, int find_max)
Position of the extreme energy value among the cells whose bit equals want_set: the tightest cluster ...
static av_always_inline int color_inc(LatticePalContext *s, const int f[4])
Account one pixel using the lattice color with indices f, registering the color in the cell table and...
#define CHECK_CELL4(a, b, c, l)
static int query_formats(const AVFilterContext *ctx, AVFilterFormatsConfig **cfg_in, AVFilterFormatsConfig **cfg_out)
#define VC_SHIFT
static av_cold void uninit(AVFilterContext *ctx)
#define MAX_DENSITY_ALPHA
#define OFFSET(x)
static int reduce_colors(LatticePalContext *s, int target)
Reduce the live colors down to target by repeatedly dropping the color whose removal has the least im...
static int reduce_batched(LatticePalContext *s, const AVFrame *in)
Batched reduction: instead of dropping all excess colors against the first pass statistics,...
static HeapEnt heap_pop(HeapEnt *heap, int *nb)
refine_mode
@ REFINE_FULL
@ REFINE_NONE
@ NB_REFINE
@ REFINE_RESIDUAL
@ REFINE_BATCHED
static av_always_inline void quant_dn(const LatticePalContext *s, const int *in, int f[4])
Quantize a color to the nearest point of the scaled D3 (FCC) or D4 lattice.
static double b1(void *priv, double x, double y)
Definition vf_xfade.c:2034
const AVFilterPad ff_video_default_filterpad[1]
An AVFilterPad array whose only entry has name "default" and is of type AVMEDIA_TYPE_VIDEO.
Definition video.c:37
AVFrame * ff_get_video_buffer(AVFilterLink *link, int w, int h)
Request a picture buffer with a specific set of permissions.
Definition video.c:89
int dim
static double c[64]