FFmpeg
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 
54 };
55 
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
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
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 
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. */
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 static 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  */
313 static av_always_inline int color_inc(LatticePalContext *s, const int f[4])
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  */
360 static 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 
462 static 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 
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 static 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 
487 static 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  */
516 static 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. */
590 static 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  */
816 static int batch_assign(LatticePalContext *s, const AVFrame *in)
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  */
908 static int reduce_batched(LatticePalContext *s, const AVFrame *in)
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 
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 
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 
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 
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 
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 };
formats
formats
Definition: signature.h:47
ff_get_video_buffer
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
config_input
static int config_input(AVFilterLink *inlink)
Definition: vf_latticepal.c:1197
AVPixelFormat
AVPixelFormat
Pixel format.
Definition: pixfmt.h:71
LatticePalContext::nb_touched
int nb_touched
Definition: vf_latticepal.c:112
r
const char * r
Definition: vf_curves.c:127
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
opt.h
vc_generate
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 ...
Definition: vf_latticepal.c:219
out
static FILE * out
Definition: movenc.c:55
VC_SHIFT
#define VC_SHIFT
Definition: vf_latticepal.c:65
av_lfg_init
av_cold void av_lfg_init(AVLFG *c, unsigned int seed)
Definition: lfg.c:32
ff_filter_frame
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition: avfilter.c:1068
LatticePalContext::alpha
int alpha
quantize the alpha channel too (D4 lattice)
Definition: vf_latticepal.c:87
av_pix_fmt_desc_get
const AVPixFmtDescriptor * av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt)
Definition: pixdesc.c:3460
av_cold
#define av_cold
Definition: attributes.h:119
int64_t
long long int64_t
Definition: coverity.c:34
inlink
The exact code depends on how similar the blocks are and how related they are to the and needs to apply these operations to the correct inlink or outlink if there are several Macros are available to factor that when no extra processing is inlink
Definition: filter_design.txt:212
refine_mode
refine_mode
Definition: vf_latticepal.c:56
REFINE_RESIDUAL
@ REFINE_RESIDUAL
Definition: vf_latticepal.c:59
mask
int mask
Definition: mediacodecdec_common.c:154
av_frame_free
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition: frame.c:64
AVFrame
This structure describes decoded (raw) audio or video data.
Definition: frame.h:472
pixdesc.h
LatticePalContext::scale
float scale
density / 255
Definition: vf_latticepal.c:93
AVFrame::pts
int64_t pts
Presentation timestamp in time_base units (time when frame should be shown to user).
Definition: frame.h:574
step
trying all byte sequences megabyte in length and selecting the best looking sequence will yield cases to try But a word about which is also called distortion Distortion can be quantified by almost any quality measurement one chooses the sum of squared differences is used but more complex methods that consider psychovisual effects can be used as well It makes no difference in this discussion First step
Definition: rate_distortion.txt:58
AVFrame::width
int width
Definition: frame.h:544
u
#define u(width, name, range_min, range_max)
Definition: cbs_apv.c:68
NB_DITHERING
@ NB_DITHERING
Definition: vf_latticepal.c:53
LatticePalContext::cell
int32_t * cell
dim^nc table: lattice cell -> list index + 1, 0 if unused
Definition: vf_latticepal.c:101
AVOption
AVOption.
Definition: opt.h:428
b
#define b
Definition: input.c:43
ff_make_pixel_format_list
av_warn_unused_result AVFilterFormats * ff_make_pixel_format_list(const enum AVPixelFormat *fmts)
Create a list of supported pixel formats.
filters.h
expf
#define expf(x)
Definition: libm.h:285
HeapEnt::imp
int64_t imp
impact of the removal when the entry was pushed
Definition: vf_latticepal.c:471
AVFILTER_DEFINE_CLASS
AVFILTER_DEFINE_CLASS(latticepal)
LatticePalContext::bo
int bo
Definition: vf_latticepal.c:90
AV_PIX_FMT_BGR24
@ AV_PIX_FMT_BGR24
packed RGB 8:8:8, 24bpp, BGRBGR...
Definition: pixfmt.h:76
AV_PIX_FMT_BGRA
@ AV_PIX_FMT_BGRA
packed BGRA 8:8:8:8, 32bpp, BGRABGRA...
Definition: pixfmt.h:102
FFMAX
#define FFMAX(a, b)
Definition: macros.h:47
AVFilter::name
const char * name
Filter name.
Definition: avfilter.h:219
LatticePalContext::blue_dither
int * blue_dither[4]
per-channel blue noise offsets spanning one lattice period
Definition: vf_latticepal.c:96
PalEntry::pos
int pos
lattice cell position, ((i * dim + j) * dim + k) [* dim + l]
Definition: vf_latticepal.c:74
video.h
batch_assign
static int batch_assign(LatticePalContext *s, const AVFrame *in)
Rediffuse the whole frame against the current live colors and reassign every pixel,...
Definition: vf_latticepal.c:816
latticepal_options
static const AVOption latticepal_options[]
Definition: vf_latticepal.c:118
uninit
static av_cold void uninit(AVFilterContext *ctx)
Definition: vf_latticepal.c:1359
AVFrame::data
uint8_t * data[AV_NUM_DATA_POINTERS]
pointer to the picture/channel planes.
Definition: frame.h:493
VC_KSIZE
#define VC_KSIZE
Definition: vf_latticepal.c:71
formats.h
av_always_inline
#define av_always_inline
Definition: attributes.h:76
LatticePalContext::alive_alloc
int alive_alloc
Definition: vf_latticepal.c:109
nearest_alive
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).
Definition: vf_latticepal.c:360
OFFSET
#define OFFSET(x)
Definition: vf_latticepal.c:116
b1
static double b1(void *priv, double x, double y)
Definition: vf_xfade.c:2034
LatticePalContext::density
int density
lattice steps per color axis
Definition: vf_latticepal.c:84
LatticePalContext::dither
int dither
Definition: vf_latticepal.c:85
heap_push
static void heap_push(HeapEnt *heap, int *nb, int64_t imp, int idx)
Definition: vf_latticepal.c:475
LatticePalContext::pixstep
int pixstep
bytes per input pixel
Definition: vf_latticepal.c:92
LatticePalContext::list
PalEntry * list
colors used by the current frame
Definition: vf_latticepal.c:103
val
static double val(void *priv, double ch)
Definition: aeval.c:77
latticepal_inputs
static const AVFilterPad latticepal_inputs[]
Definition: vf_latticepal.c:1375
LatticePalContext::max_colors
int max_colors
palette entries to use at most
Definition: vf_latticepal.c:86
PalEntry::alive
uint8_t alive
still part of the palette while reducing
Definition: vf_latticepal.c:76
fabsf
static __device__ float fabsf(float a)
Definition: cuda_runtime.h:181
LatticePalContext::refine
int refine
rediffuse the error of dropped colors
Definition: vf_latticepal.c:88
FILTER_QUERY_FUNC2
#define FILTER_QUERY_FUNC2(func)
Definition: filters.h:241
AVFilterPad
A filter pad used for either input or output.
Definition: filters.h:40
vc_extreme
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 ...
Definition: vf_latticepal.c:194
HeapEnt::idx
int idx
used color list index
Definition: vf_latticepal.c:472
PalEntry
Definition: vf_latticepal.c:73
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:210
ff_video_default_filterpad
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
FFFilter
Definition: filters.h:267
ff_vf_latticepal
const FFFilter ff_vf_latticepal
Definition: vf_latticepal.c:1384
heap_pop
static HeapEnt heap_pop(HeapEnt *heap, int *nb)
Definition: vf_latticepal.c:487
LatticePalContext::alive_pos
int * alive_pos
position of each live color in alive_arr
Definition: vf_latticepal.c:107
dither
static const uint16_t dither[8][8]
Definition: vf_gradfun.c:46
FILTER_OUTPUTS
#define FILTER_OUTPUTS(array)
Definition: filters.h:265
av_lfg_get
static unsigned int av_lfg_get(AVLFG *c)
Get the next random unsigned 32-bit number using an ALFG.
Definition: lfg.h:53
av_realloc_array
void * av_realloc_array(void *ptr, size_t nmemb, size_t size)
Definition: mem.c:217
DITHERING_FLOYD_STEINBERG
@ DITHERING_FLOYD_STEINBERG
Definition: vf_latticepal.c:52
ff_formats_ref
int ff_formats_ref(AVFilterFormats *f, AVFilterFormats **ref)
Add *ref as a new reference to formats.
Definition: formats.c:756
HeapEnt
Definition: vf_latticepal.c:470
lfg.h
bits
uint8_t bits
Definition: vp3data.h:128
CHECK_CELL3
#define CHECK_CELL3(a, b, c)
AV_LOG_DEBUG
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition: log.h:231
AV_PIX_FMT_FLAG_ALPHA
#define AV_PIX_FMT_FLAG_ALPHA
The pixel format has an alpha channel.
Definition: pixdesc.h:147
ctx
static AVFormatContext * ctx
Definition: movenc.c:49
VC_SIGMA
#define VC_SIGMA
Definition: vf_latticepal.c:69
VC_RADIUS
#define VC_RADIUS
Definition: vf_latticepal.c:70
VC_AREA
#define VC_AREA
Definition: vf_latticepal.c:67
AV_PIX_FMT_RGBA
@ AV_PIX_FMT_RGBA
packed RGBA 8:8:8:8, 32bpp, RGBARGBA...
Definition: pixfmt.h:100
VC_MASK
#define VC_MASK
Definition: vf_latticepal.c:68
REFINE_BATCHED
@ REFINE_BATCHED
Definition: vf_latticepal.c:60
FFABS
#define FFABS(a)
Absolute value, Note, INT_MIN / INT64_MIN result in undefined behavior as they are not representable ...
Definition: common.h:74
if
if(ret)
Definition: filter_design.txt:179
LatticePalContext::nb_alloc
int nb_alloc
Definition: vf_latticepal.c:105
LatticePalContext::min_gap
int min_gap
smallest idx2val increment
Definition: vf_latticepal.c:100
vc_splat
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 ...
Definition: vf_latticepal.c:175
fail
#define fail
Definition: test.h:478
AVClass
Describe the class of an AVClass context structure.
Definition: log.h:76
NULL
#define NULL
Definition: coverity.c:32
av_frame_copy_props
int av_frame_copy_props(AVFrame *dst, const AVFrame *src)
Copy only "metadata" fields from src to dst.
Definition: frame.c:599
color_inc
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...
Definition: vf_latticepal.c:313
AVPALETTE_SIZE
#define AVPALETTE_SIZE
Definition: pixfmt.h:32
list
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining list
Definition: filter_design.txt:25
AV_PIX_FMT_BGR0
@ AV_PIX_FMT_BGR0
packed BGR 8:8:8, 32bpp, BGRXBGRX... X=unused/undefined
Definition: pixfmt.h:265
av_clipf
av_clipf
Definition: af_crystalizer.c:122
LatticePalContext::ao
int ao
byte offsets of R, G, B, A in an input pixel, ao < 0: opaque
Definition: vf_latticepal.c:90
LatticePalContext::prev_pal
uint32_t prev_pal[AVPALETTE_COUNT]
previous frame's palette, for stable slot assignment
Definition: vf_latticepal.c:110
LatticePalContext
Definition: vf_latticepal.c:82
REFINE_FULL
@ REFINE_FULL
Definition: vf_latticepal.c:58
AV_PIX_FMT_ABGR
@ AV_PIX_FMT_ABGR
packed ABGR 8:8:8:8, 32bpp, ABGRABGR...
Definition: pixfmt.h:101
c
Undefined Behavior In the C some operations are like signed integer dereferencing freed accessing outside allocated Undefined Behavior must not occur in a C it is not safe even if the output of undefined operations is unused The unsafety may seem nit picking but Optimizing compilers have in fact optimized code on the assumption that no undefined Behavior occurs Optimizing code based on wrong assumptions can and has in some cases lead to effects beyond the output of computations The signed integer overflow problem in speed critical code Code which is highly optimized and works with signed integers sometimes has the problem that often the output of the computation does not c
Definition: undefined.txt:32
AVPALETTE_COUNT
#define AVPALETTE_COUNT
Definition: pixfmt.h:33
AVFilterFormatsConfig
Lists of formats / etc.
Definition: avfilter.h:120
entry_color
static av_always_inline uint32_t entry_color(const LatticePalContext *s, const PalEntry *e)
Palette color (AARRGGBB) of a used color list entry.
Definition: vf_latticepal.c:590
LatticePalContext::go
int go
Definition: vf_latticepal.c:90
AVLFG
Context structure for the Lagged Fibonacci PRNG.
Definition: lfg.h:33
refine_residual
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...
Definition: vf_latticepal.c:712
PalEntry::d2
int d2
squared RGB(A) distance to nn
Definition: vf_latticepal.c:78
f
f
Definition: af_crystalizer.c:122
AV_PIX_FMT_RGB24
@ AV_PIX_FMT_RGB24
packed RGB 8:8:8, 24bpp, RGBRGB...
Definition: pixfmt.h:75
NULL_IF_CONFIG_SMALL
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition: internal.h:88
dst
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
Definition: dsp.h:87
i
#define i(width, name, range_min, range_max)
Definition: cbs_h264.c:63
for
for(k=2;k<=8;++k)
Definition: h264pred_template.c:424
LatticePalContext::touched_alloc
int touched_alloc
Definition: vf_latticepal.c:113
LatticePalContext::touched
int * touched
cells filled on demand by the refinement pass
Definition: vf_latticepal.c:111
a
The reader does not expect b to be semantically here and if the code is changed by maybe adding a a division or other the signedness will almost certainly be mistaken To avoid this confusion a new type was SUINT is the C unsigned type but it holds a signed int to use the same example SUINT a
Definition: undefined.txt:41
LatticePalContext::dim
int dim
density + 1, lattice cells per axis
Definition: vf_latticepal.c:99
AV_PIX_FMT_RGB0
@ AV_PIX_FMT_RGB0
packed RGB 8:8:8, 32bpp, RGBXRGBX... X=unused/undefined
Definition: pixfmt.h:263
N
#define N
Definition: af_mcompand.c:54
LatticePalContext::err
int32_t * err[2]
Floyd-Steinberg error rows, nc ints per pixel.
Definition: vf_latticepal.c:97
PalEntry::ci
uint8_t ci[4]
lattice indices of the color
Definition: vf_latticepal.c:75
AV_PIX_FMT_ARGB
@ AV_PIX_FMT_ARGB
packed ARGB 8:8:8:8, 32bpp, ARGBARGB...
Definition: pixfmt.h:99
MAX_DENSITY_ALPHA
#define MAX_DENSITY_ALPHA
Definition: vf_latticepal.c:46
LatticePalContext::idx2val
uint8_t idx2val[MAX_DENSITY+1]
lattice index -> 8-bit component value
Definition: vf_latticepal.c:94
filter_frame
static int filter_frame(AVFilterLink *inlink, AVFrame *in)
Definition: vf_latticepal.c:941
lrintf
#define lrintf(x)
Definition: libm_mips.h:72
LatticePalContext::nc
int nc
quantized components, 3 or 4
Definition: vf_latticepal.c:91
LatticePalContext::ordered_dither
int ordered_dither[4][8 *8]
per-channel bayer offsets spanning one lattice period
Definition: vf_latticepal.c:95
REFINE_NONE
@ REFINE_NONE
Definition: vf_latticepal.c:57
av_malloc_array
#define av_malloc_array(a, b)
Definition: tableprint_vlc.h:32
s
uint8_t s
Definition: llvidencdsp.c:39
FFMIN
#define FFMIN(a, b)
Definition: macros.h:49
AVFilterPad::name
const char * name
Pad name.
Definition: filters.h:46
LatticePalContext::nb_used
int nb_used
Definition: vf_latticepal.c:104
av_calloc
void * av_calloc(size_t nmemb, size_t size)
Definition: mem.c:264
LatticePalContext::nb_alive
int nb_alive
entries in alive_arr, 0 outside reduction/remap
Definition: vf_latticepal.c:108
dim
int dim
Definition: vorbis_enc_data.h:425
AV_PIX_FMT_PAL8
@ AV_PIX_FMT_PAL8
8 bits with AV_PIX_FMT_RGB32 palette
Definition: pixfmt.h:84
ret
ret
Definition: filter_design.txt:187
PalEntry::count
int64_t count
pixels quantizing to this color, incl. absorbed ones
Definition: vf_latticepal.c:79
LatticePalContext::ro
int ro
Definition: vf_latticepal.c:90
AV_PIX_FMT_0BGR
@ AV_PIX_FMT_0BGR
packed BGR 8:8:8, 32bpp, XBGRXBGR... X=unused/undefined
Definition: pixfmt.h:264
pos
unsigned int pos
Definition: spdifenc.c:414
reduce_colors
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...
Definition: vf_latticepal.c:516
FILTER_INPUTS
#define FILTER_INPUTS(array)
Definition: filters.h:264
nn_search
static void nn_search(LatticePalContext *s, int self)
Definition: vf_latticepal.c:462
AVFrame::height
int height
Definition: frame.h:544
CHECK_CELL4
#define CHECK_CELL4(a, b, c, l)
reduce_batched
static int reduce_batched(LatticePalContext *s, const AVFrame *in)
Batched reduction: instead of dropping all excess colors against the first pass statistics,...
Definition: vf_latticepal.c:908
dither_8x8_73
static const uint8_t dither_8x8_73[8][8]
Definition: vf_latticepal.c:160
AV_PIX_FMT_NONE
@ AV_PIX_FMT_NONE
Definition: pixfmt.h:72
AV_OPT_TYPE_INT
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition: opt.h:258
avfilter.h
query_formats
static int query_formats(const AVFilterContext *ctx, AVFilterFormatsConfig **cfg_in, AVFilterFormatsConfig **cfg_out)
Definition: vf_latticepal.c:137
dithering_mode
dithering_mode
Definition: vf_latticepal.c:48
PalEntry::nn
int nn
nearest live color while reducing, then palette slot
Definition: vf_latticepal.c:77
px
#define px
Definition: uops_tmpl.c:54
Windows::Graphics::DirectX::Direct3D11::p
IDirect3DDxgiInterfaceAccess _COM_Outptr_ void ** p
Definition: vsrc_gfxcapture_winrt.hpp:53
DITHERING_BAYER
@ DITHERING_BAYER
Definition: vf_latticepal.c:50
quant_dn
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.
Definition: vf_latticepal.c:281
av_clip_uint8
#define av_clip_uint8
Definition: common.h:106
AVFilterContext
An instance of a filter.
Definition: avfilter.h:273
desc
const char * desc
Definition: libsvtav1.c:83
AVMEDIA_TYPE_VIDEO
@ AVMEDIA_TYPE_VIDEO
Definition: avutil.h:200
FFFilter::p
AVFilter p
The public AVFilter.
Definition: filters.h:271
mem.h
LatticePalContext::pixpos
uint32_t * pixpos
per-pixel cell position of the quantized color
Definition: vf_latticepal.c:102
AVPixFmtDescriptor
Descriptor that unambiguously describes how the bits of a pixel are stored in the up to 4 data planes...
Definition: pixdesc.h:69
w
uint8_t w
Definition: llvidencdsp.c:39
LatticePalContext::alive_arr
int * alive_arr
compact list of live color indices while reducing
Definition: vf_latticepal.c:106
av_free
#define av_free(p)
Definition: tableprint_vlc.h:34
refine_full
static int refine_full(LatticePalContext *s, AVFrame *out, const AVFrame *in)
Full refinement pass: rediffuse the whole frame against the final palette.
Definition: vf_latticepal.c:614
alpha
static const int16_t alpha[]
Definition: ilbcdata.h:55
AV_OPT_TYPE_BOOL
@ AV_OPT_TYPE_BOOL
Underlying C type is int.
Definition: opt.h:326
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:35
DITHERING_BLUE_NOISE
@ DITHERING_BLUE_NOISE
Definition: vf_latticepal.c:51
int32_t
int32_t
Definition: audioconvert.c:56
AVFrame::linesize
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
AV_PIX_FMT_0RGB
@ AV_PIX_FMT_0RGB
packed RGB 8:8:8, 32bpp, XRGBXRGB... X=unused/undefined
Definition: pixfmt.h:262
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
h
h
Definition: vp9dsp_template.c:2070
NB_REFINE
@ NB_REFINE
Definition: vf_latticepal.c:61
AV_OPT_TYPE_CONST
@ AV_OPT_TYPE_CONST
Special option type for declaring named constants.
Definition: opt.h:298
DITHERING_NONE
@ DITHERING_NONE
Definition: vf_latticepal.c:49
FLAGS
#define FLAGS
Definition: vf_latticepal.c:117
MAX_DENSITY
#define MAX_DENSITY
Definition: vf_latticepal.c:45
src
#define src
Definition: vp8dsp.c:248