[FFmpeg-devel] [PATCH v2 02/32] avfilter/palette{gen, use}: revert support palettes with alpha
Paul B Mahol
onemda at gmail.com
Tue Jan 3 21:11:28 EET 2023
Please add alpha support back.
On Wed, Dec 28, 2022 at 12:18 AM Clément Bœsch <u at pkh.me> wrote:
> This reverts commit dea673d0d548c864ec85f9260d8900d944ef7a2a.
>
> This change cannot work for several reasons, the most obvious ones are:
>
> - the alpha is being part of the scoring of the color difference, even
> though we can not interpret the alpha as part of the perception of the
> color (we don't even know if it's premultiplied or postmultiplied)
> - the colors are averaged with their alpha value which simply cannot
> work
>
> The command proposed in the original thread of the patch actually
> produces a completely broken file:
>
> ffmpeg -y -loglevel verbose -i fate-suite/apng/o_sample.png
> -filter_complex
> "split[split1][split2];[split1]palettegen=max_colors=254:use_alpha=1[pal1];[split2][pal1]paletteuse=use_alpha=1"
> -frames:v 1 out.png
>
> We can see that many color pixels are off, but more importantly some
> colors have a random alpha value: https://imgur.com/eFQ2UK7
>
> I don't see any easy fix for this unfortunately, the approach appears to
> be flawed by design.
> ---
> doc/filters.texi | 8 --
> libavfilter/vf_palettegen.c | 127 +++++++-------------
> libavfilter/vf_paletteuse.c | 225 +++++++++++++++---------------------
> 3 files changed, 138 insertions(+), 222 deletions(-)
>
> diff --git a/doc/filters.texi b/doc/filters.texi
> index 9b866de5ae..f51623d16a 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -18474,9 +18474,6 @@ Compute new histogram for each frame.
> @end table
>
> Default value is @var{full}.
> - at item use_alpha
> -Create a palette of colors with alpha components.
> -Setting this, will automatically disable 'reserve_transparent'.
> @end table
>
> The filter also exports the frame metadata @code{lavfi.color_quant_ratio}
> @@ -18555,11 +18552,6 @@ will be treated as completely opaque, and values
> below this threshold will be
> treated as completely transparent.
>
> The option must be an integer value in the range [0,255]. Default is
> @var{128}.
> -
> - at item use_alpha
> -Apply the palette by taking alpha values into account. Only useful with
> -palettes that are containing multiple colors with alpha components.
> -Setting this will automatically disable 'alpha_treshold'.
> @end table
>
> @subsection Examples
> diff --git a/libavfilter/vf_palettegen.c b/libavfilter/vf_palettegen.c
> index c03f62b942..bea3292796 100644
> --- a/libavfilter/vf_palettegen.c
> +++ b/libavfilter/vf_palettegen.c
> @@ -59,7 +59,7 @@ enum {
> };
>
> #define NBITS 5
> -#define HIST_SIZE (1<<(4*NBITS))
> +#define HIST_SIZE (1<<(3*NBITS))
>
> typedef struct PaletteGenContext {
> const AVClass *class;
> @@ -67,7 +67,6 @@ typedef struct PaletteGenContext {
> int max_colors;
> int reserve_transparent;
> int stats_mode;
> - int use_alpha;
>
> AVFrame *prev_frame; // previous frame used for
> the diff stats_mode
> struct hist_node histogram[HIST_SIZE]; // histogram/hashtable of the
> colors
> @@ -89,7 +88,6 @@ static const AVOption palettegen_options[] = {
> { "full", "compute full frame histograms", 0, AV_OPT_TYPE_CONST,
> {.i64=STATS_MODE_ALL_FRAMES}, INT_MIN, INT_MAX, FLAGS, "mode" },
> { "diff", "compute histograms only for the part that differs from
> previous frame", 0, AV_OPT_TYPE_CONST, {.i64=STATS_MODE_DIFF_FRAMES},
> INT_MIN, INT_MAX, FLAGS, "mode" },
> { "single", "compute new histogram for each frame", 0,
> AV_OPT_TYPE_CONST, {.i64=STATS_MODE_SINGLE_FRAMES}, INT_MIN, INT_MAX,
> FLAGS, "mode" },
> - { "use_alpha", "create a palette including alpha values",
> OFFSET(use_alpha), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS },
> { NULL }
> };
>
> @@ -115,16 +113,15 @@ static int cmp_##name(const void *pa, const void
> *pb) \
> { \
> const struct color_ref * const *a = pa; \
> const struct color_ref * const *b = pb; \
> - return (int)((*a)->color >> (8 * (3 - (pos))) & 0xff) \
> - - (int)((*b)->color >> (8 * (3 - (pos))) & 0xff); \
> + return (int)((*a)->color >> (8 * (2 - (pos))) & 0xff) \
> + - (int)((*b)->color >> (8 * (2 - (pos))) & 0xff); \
> }
>
> -DECLARE_CMP_FUNC(a, 0)
> -DECLARE_CMP_FUNC(r, 1)
> -DECLARE_CMP_FUNC(g, 2)
> -DECLARE_CMP_FUNC(b, 3)
> +DECLARE_CMP_FUNC(r, 0)
> +DECLARE_CMP_FUNC(g, 1)
> +DECLARE_CMP_FUNC(b, 2)
>
> -static const cmp_func cmp_funcs[] = {cmp_a, cmp_r, cmp_g, cmp_b};
> +static const cmp_func cmp_funcs[] = {cmp_r, cmp_g, cmp_b};
>
> /**
> * Simple color comparison for sorting the final palette
> @@ -146,17 +143,6 @@ static av_always_inline int diff(const uint32_t a,
> const uint32_t b)
> return dr*dr + dg*dg + db*db;
> }
>
> -static av_always_inline int diff_alpha(const uint32_t a, const uint32_t b)
> -{
> - const uint8_t c1[] = {a >> 24 & 0xff, a >> 16 & 0xff, a >> 8 & 0xff,
> a & 0xff};
> - const uint8_t c2[] = {b >> 24 & 0xff, b >> 16 & 0xff, b >> 8 & 0xff,
> b & 0xff};
> - const int da = c1[0] - c2[0];
> - const int dr = c1[1] - c2[1];
> - const int dg = c1[2] - c2[2];
> - const int db = c1[3] - c2[3];
> - return da*da + dr*dr + dg*dg + db*db;
> -}
> -
> /**
> * Find the next box to split: pick the one with the highest variance
> */
> @@ -178,10 +164,7 @@ static int get_next_box_id_to_split(PaletteGenContext
> *s)
>
> for (i = 0; i < box->len; i++) {
> const struct color_ref *ref = s->refs[box->start + i];
> - if (s->use_alpha)
> - variance += (int64_t)diff_alpha(ref->color,
> box->color) * ref->count;
> - else
> - variance += (int64_t)diff(ref->color, box->color)
> * ref->count;
> + variance += diff(ref->color, box->color) * ref->count;
> }
> box->variance = variance;
> }
> @@ -201,31 +184,24 @@ static int
> get_next_box_id_to_split(PaletteGenContext *s)
> * specified box. Takes into account the weight of each color.
> */
> static uint32_t get_avg_color(struct color_ref * const *refs,
> - const struct range_box *box, int use_alpha)
> + const struct range_box *box)
> {
> int i;
> const int n = box->len;
> - uint64_t a = 0, r = 0, g = 0, b = 0, div = 0;
> + uint64_t r = 0, g = 0, b = 0, div = 0;
>
> for (i = 0; i < n; i++) {
> const struct color_ref *ref = refs[box->start + i];
> - if (use_alpha)
> - a += (ref->color >> 24 & 0xff) * ref->count;
> - r += (ref->color >> 16 & 0xff) * ref->count;
> - g += (ref->color >> 8 & 0xff) * ref->count;
> - b += (ref->color & 0xff) * ref->count;
> + r += (ref->color >> 16 & 0xff) * ref->count;
> + g += (ref->color >> 8 & 0xff) * ref->count;
> + b += (ref->color & 0xff) * ref->count;
> div += ref->count;
> }
>
> - if (use_alpha)
> - a = a / div;
> r = r / div;
> g = g / div;
> b = b / div;
>
> - if (use_alpha)
> - return a<<24 | r<<16 | g<<8 | b;
> -
> return 0xffU<<24 | r<<16 | g<<8 | b;
> }
>
> @@ -244,8 +220,8 @@ static void split_box(PaletteGenContext *s, struct
> range_box *box, int n)
> av_assert0(box->len >= 1);
> av_assert0(new_box->len >= 1);
>
> - box->color = get_avg_color(s->refs, box, s->use_alpha);
> - new_box->color = get_avg_color(s->refs, new_box, s->use_alpha);
> + box->color = get_avg_color(s->refs, box);
> + new_box->color = get_avg_color(s->refs, new_box);
> box->variance = -1;
> new_box->variance = -1;
> }
> @@ -275,7 +251,7 @@ static void write_palette(AVFilterContext *ctx,
> AVFrame *out)
> pal += pal_linesize;
> }
>
> - if (s->reserve_transparent && !s->use_alpha) {
> + if (s->reserve_transparent) {
> av_assert0(s->nb_boxes < 256);
> pal[out->width - pal_linesize - 1] =
> AV_RB32(&s->transparency_color) >> 8;
> }
> @@ -343,49 +319,40 @@ static AVFrame *get_palette_frame(AVFilterContext
> *ctx)
> box = &s->boxes[box_id];
> box->len = s->nb_refs;
> box->sorted_by = -1;
> - box->color = get_avg_color(s->refs, box, s->use_alpha);
> + box->color = get_avg_color(s->refs, box);
> box->variance = -1;
> s->nb_boxes = 1;
>
> while (box && box->len > 1) {
> - int i, ar, rr, gr, br, longest;
> + int i, rr, gr, br, longest;
> uint64_t median, box_weight = 0;
>
> /* compute the box weight (sum all the weights of the colors in
> the
> * range) and its boundings */
> - uint8_t min[4] = {0xff, 0xff, 0xff, 0xff};
> - uint8_t max[4] = {0x00, 0x00, 0x00, 0x00};
> + uint8_t min[3] = {0xff, 0xff, 0xff};
> + uint8_t max[3] = {0x00, 0x00, 0x00};
> for (i = box->start; i < box->start + box->len; i++) {
> const struct color_ref *ref = s->refs[i];
> const uint32_t rgb = ref->color;
> - const uint8_t a = rgb >> 24 & 0xff, r = rgb >> 16 & 0xff, g =
> rgb >> 8 & 0xff, b = rgb & 0xff;
> - min[0] = FFMIN(a, min[0]); max[0] = FFMAX(a, max[0]);
> - min[1] = FFMIN(r, min[1]); max[1] = FFMAX(r, max[1]);
> - min[2] = FFMIN(g, min[2]); max[2] = FFMAX(g, max[2]);
> - min[3] = FFMIN(b, min[3]); max[3] = FFMAX(b, max[3]);
> + const uint8_t r = rgb >> 16 & 0xff, g = rgb >> 8 & 0xff, b =
> rgb & 0xff;
> + min[0] = FFMIN(r, min[0]), max[0] = FFMAX(r, max[0]);
> + min[1] = FFMIN(g, min[1]), max[1] = FFMAX(g, max[1]);
> + min[2] = FFMIN(b, min[2]), max[2] = FFMAX(b, max[2]);
> box_weight += ref->count;
> }
>
> /* define the axis to sort by according to the widest range of
> colors */
> - ar = max[0] - min[0];
> - rr = max[1] - min[1];
> - gr = max[2] - min[2];
> - br = max[3] - min[3];
> - longest = 2; // pick green by default (the color the eye is the
> most sensitive to)
> - if (s->use_alpha) {
> - if (ar >= rr && ar >= br && ar >= gr) longest = 0;
> - if (br >= rr && br >= gr && br >= ar) longest = 3;
> - if (rr >= gr && rr >= br && rr >= ar) longest = 1;
> - if (gr >= rr && gr >= br && gr >= ar) longest = 2; // prefer
> green again
> - } else {
> - if (br >= rr && br >= gr) longest = 3;
> - if (rr >= gr && rr >= br) longest = 1;
> - if (gr >= rr && gr >= br) longest = 2; // prefer green again
> - }
> -
> - ff_dlog(ctx, "box #%02X [%6d..%-6d] (%6d) w:%-6"PRIu64"
> ranges:[%2x %2x %2x %2x] sort by %c (already sorted:%c) ",
> + rr = max[0] - min[0];
> + gr = max[1] - min[1];
> + br = max[2] - min[2];
> + longest = 1; // pick green by default (the color the eye is the
> most sensitive to)
> + if (br >= rr && br >= gr) longest = 2;
> + if (rr >= gr && rr >= br) longest = 0;
> + if (gr >= rr && gr >= br) longest = 1; // prefer green again
> +
> + ff_dlog(ctx, "box #%02X [%6d..%-6d] (%6d) w:%-6"PRIu64"
> ranges:[%2x %2x %2x] sort by %c (already sorted:%c) ",
> box_id, box->start, box->start + box->len - 1, box->len,
> box_weight,
> - ar, rr, gr, br, "argb"[longest], box->sorted_by ==
> longest ? 'y' : 'n');
> + rr, gr, br, "rgb"[longest], box->sorted_by == longest ?
> 'y':'n');
>
> /* sort the range by its longest axis if it's not already sorted
> */
> if (box->sorted_by != longest) {
> @@ -427,27 +394,22 @@ static AVFrame *get_palette_frame(AVFilterContext
> *ctx)
> * It keeps the NBITS least significant bit of each component to make it
> * "random" even if the scene doesn't have much different colors.
> */
> -static inline unsigned color_hash(uint32_t color, int use_alpha)
> +static inline unsigned color_hash(uint32_t color)
> {
> const uint8_t r = color >> 16 & ((1<<NBITS)-1);
> const uint8_t g = color >> 8 & ((1<<NBITS)-1);
> const uint8_t b = color & ((1<<NBITS)-1);
>
> - if (use_alpha) {
> - const uint8_t a = color >> 24 & ((1 << NBITS) - 1);
> - return a << (NBITS * 3) | r << (NBITS * 2) | g << NBITS | b;
> - }
> -
> return r << (NBITS * 2) | g << NBITS | b;
> }
>
> /**
> * Locate the color in the hash table and increment its counter.
> */
> -static int color_inc(struct hist_node *hist, uint32_t color, int
> use_alpha)
> +static int color_inc(struct hist_node *hist, uint32_t color)
> {
> int i;
> - const unsigned hash = color_hash(color, use_alpha);
> + const unsigned hash = color_hash(color);
> struct hist_node *node = &hist[hash];
> struct color_ref *e;
>
> @@ -472,7 +434,7 @@ static int color_inc(struct hist_node *hist, uint32_t
> color, int use_alpha)
> * Update histogram when pixels differ from previous frame.
> */
> static int update_histogram_diff(struct hist_node *hist,
> - const AVFrame *f1, const AVFrame *f2,
> int use_alpha)
> + const AVFrame *f1, const AVFrame *f2)
> {
> int x, y, ret, nb_diff_colors = 0;
>
> @@ -483,7 +445,7 @@ static int update_histogram_diff(struct hist_node
> *hist,
> for (x = 0; x < f1->width; x++) {
> if (p[x] == q[x])
> continue;
> - ret = color_inc(hist, p[x], use_alpha);
> + ret = color_inc(hist, p[x]);
> if (ret < 0)
> return ret;
> nb_diff_colors += ret;
> @@ -495,7 +457,7 @@ static int update_histogram_diff(struct hist_node
> *hist,
> /**
> * Simple histogram of the frame.
> */
> -static int update_histogram_frame(struct hist_node *hist, const AVFrame
> *f, int use_alpha)
> +static int update_histogram_frame(struct hist_node *hist, const AVFrame
> *f)
> {
> int x, y, ret, nb_diff_colors = 0;
>
> @@ -503,7 +465,7 @@ static int update_histogram_frame(struct hist_node
> *hist, const AVFrame *f, int
> const uint32_t *p = (const uint32_t *)(f->data[0] +
> y*f->linesize[0]);
>
> for (x = 0; x < f->width; x++) {
> - ret = color_inc(hist, p[x], use_alpha);
> + ret = color_inc(hist, p[x]);
> if (ret < 0)
> return ret;
> nb_diff_colors += ret;
> @@ -519,8 +481,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame
> *in)
> {
> AVFilterContext *ctx = inlink->dst;
> PaletteGenContext *s = ctx->priv;
> - int ret = s->prev_frame ? update_histogram_diff(s->histogram,
> s->prev_frame, in, s->use_alpha)
> - : update_histogram_frame(s->histogram, in,
> s->use_alpha);
> + int ret = s->prev_frame ? update_histogram_diff(s->histogram,
> s->prev_frame, in)
> + : update_histogram_frame(s->histogram, in);
>
> if (ret > 0)
> s->nb_refs += ret;
> @@ -583,9 +545,6 @@ static int init(AVFilterContext *ctx)
> {
> PaletteGenContext* s = ctx->priv;
>
> - if (s->use_alpha && s->reserve_transparent)
> - s->reserve_transparent = 0;
> -
> if (s->max_colors - s->reserve_transparent < 2) {
> av_log(ctx, AV_LOG_ERROR, "max_colors=2 is only allowed without
> reserving a transparent color slot\n");
> return AVERROR(EINVAL);
> diff --git a/libavfilter/vf_paletteuse.c b/libavfilter/vf_paletteuse.c
> index a6b5d5a5fa..cb18329bb7 100644
> --- a/libavfilter/vf_paletteuse.c
> +++ b/libavfilter/vf_paletteuse.c
> @@ -29,6 +29,7 @@
> #include "libavutil/opt.h"
> #include "libavutil/qsort.h"
> #include "avfilter.h"
> +#include "filters.h"
> #include "framesync.h"
> #include "internal.h"
>
> @@ -63,7 +64,7 @@ struct color_node {
> };
>
> #define NBITS 5
> -#define CACHE_SIZE (1<<(4*NBITS))
> +#define CACHE_SIZE (1<<(3*NBITS))
>
> struct cached_color {
> uint32_t color;
> @@ -88,7 +89,6 @@ typedef struct PaletteUseContext {
> uint32_t palette[AVPALETTE_COUNT];
> int transparency_index; /* index in the palette of transparency. -1
> if there is no transparency in the palette. */
> int trans_thresh;
> - int use_alpha;
> int palette_loaded;
> int dither;
> int new;
> @@ -108,7 +108,7 @@ typedef struct PaletteUseContext {
> } PaletteUseContext;
>
> #define OFFSET(x) offsetof(PaletteUseContext, x)
> -#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
> +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
> static const AVOption paletteuse_options[] = {
> { "dither", "select dithering mode", OFFSET(dither), AV_OPT_TYPE_INT,
> {.i64=DITHERING_SIERRA2_4A}, 0, NB_DITHERING-1, FLAGS, "dithering_mode" },
> { "bayer", "ordered 8x8 bayer dithering
> (deterministic)", 0, AV_OPT_TYPE_CONST,
> {.i64=DITHERING_BAYER}, INT_MIN, INT_MAX, FLAGS, "dithering_mode"
> },
> @@ -121,7 +121,6 @@ static const AVOption paletteuse_options[] = {
> { "rectangle", "process smallest different rectangle", 0,
> AV_OPT_TYPE_CONST, {.i64=DIFF_MODE_RECTANGLE}, INT_MIN, INT_MAX, FLAGS,
> "diff_mode" },
> { "new", "take new palette for each output frame", OFFSET(new),
> AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
> { "alpha_threshold", "set the alpha threshold for transparency",
> OFFSET(trans_thresh), AV_OPT_TYPE_INT, {.i64=128}, 0, 255, FLAGS },
> - { "use_alpha", "use alpha channel for mapping", OFFSET(use_alpha),
> AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
>
> /* following are the debug options, not part of the official API */
> { "debug_kdtree", "save Graphviz graph of the kdtree in specified
> file", OFFSET(dot_filename), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
> @@ -163,41 +162,37 @@ static av_always_inline uint32_t
> dither_color(uint32_t px, int er, int eg,
> | av_clip_uint8((px & 0xff) + ((eb * scale) / (1<<shift)));
> }
>
> -static av_always_inline int diff(const uint8_t *c1, const uint8_t *c2,
> const PaletteUseContext *s)
> +static av_always_inline int diff(const uint8_t *c1, const uint8_t *c2,
> const int trans_thresh)
> {
> // XXX: try L*a*b with CIE76 (dL*dL + da*da + db*db)
> - const int da = c1[0] - c2[0];
> const int dr = c1[1] - c2[1];
> const int dg = c1[2] - c2[2];
> const int db = c1[3] - c2[3];
>
> - if (s->use_alpha)
> - return da*da + dr*dr + dg*dg + db*db;
> -
> - if (c1[0] < s->trans_thresh && c2[0] < s->trans_thresh) {
> + if (c1[0] < trans_thresh && c2[0] < trans_thresh) {
> return 0;
> - } else if (c1[0] >= s->trans_thresh && c2[0] >= s->trans_thresh) {
> + } else if (c1[0] >= trans_thresh && c2[0] >= trans_thresh) {
> return dr*dr + dg*dg + db*db;
> } else {
> return 255*255 + 255*255 + 255*255;
> }
> }
>
> -static av_always_inline uint8_t colormap_nearest_bruteforce(const
> PaletteUseContext *s, const uint8_t *argb)
> +static av_always_inline uint8_t colormap_nearest_bruteforce(const
> uint32_t *palette, const uint8_t *argb, const int trans_thresh)
> {
> int i, pal_id = -1, min_dist = INT_MAX;
>
> for (i = 0; i < AVPALETTE_COUNT; i++) {
> - const uint32_t c = s->palette[i];
> + const uint32_t c = palette[i];
>
> - if (s->use_alpha || c >> 24 >= s->trans_thresh) { // ignore
> transparent entry
> + if (c >> 24 >= trans_thresh) { // ignore transparent entry
> const uint8_t palargb[] = {
> - s->palette[i]>>24 & 0xff,
> - s->palette[i]>>16 & 0xff,
> - s->palette[i]>> 8 & 0xff,
> - s->palette[i] & 0xff,
> + palette[i]>>24 & 0xff,
> + palette[i]>>16 & 0xff,
> + palette[i]>> 8 & 0xff,
> + palette[i] & 0xff,
> };
> - const int d = diff(palargb, argb, s);
> + const int d = diff(palargb, argb, trans_thresh);
> if (d < min_dist) {
> pal_id = i;
> min_dist = d;
> @@ -213,17 +208,17 @@ struct nearest_color {
> int dist_sqd;
> };
>
> -static void colormap_nearest_node(const PaletteUseContext *s,
> - const struct color_node *map,
> +static void colormap_nearest_node(const struct color_node *map,
> const int node_pos,
> const uint8_t *target,
> + const int trans_thresh,
> struct nearest_color *nearest)
> {
> const struct color_node *kd = map + node_pos;
> - const int split = kd->split;
> + const int s = kd->split;
> int dx, nearer_kd_id, further_kd_id;
> const uint8_t *current = kd->val;
> - const int current_to_target = diff(target, current, s);
> + const int current_to_target = diff(target, current, trans_thresh);
>
> if (current_to_target < nearest->dist_sqd) {
> nearest->node_pos = node_pos;
> @@ -231,23 +226,23 @@ static void colormap_nearest_node(const
> PaletteUseContext *s,
> }
>
> if (kd->left_id != -1 || kd->right_id != -1) {
> - dx = target[split] - current[split];
> + dx = target[s] - current[s];
>
> if (dx <= 0) nearer_kd_id = kd->left_id, further_kd_id =
> kd->right_id;
> else nearer_kd_id = kd->right_id, further_kd_id =
> kd->left_id;
>
> if (nearer_kd_id != -1)
> - colormap_nearest_node(s, map, nearer_kd_id, target, nearest);
> + colormap_nearest_node(map, nearer_kd_id, target,
> trans_thresh, nearest);
>
> if (further_kd_id != -1 && dx*dx < nearest->dist_sqd)
> - colormap_nearest_node(s, map, further_kd_id, target, nearest);
> + colormap_nearest_node(map, further_kd_id, target,
> trans_thresh, nearest);
> }
> }
>
> -static av_always_inline uint8_t colormap_nearest_recursive(const
> PaletteUseContext *s, const struct color_node *node, const uint8_t *rgb)
> +static av_always_inline uint8_t colormap_nearest_recursive(const struct
> color_node *node, const uint8_t *rgb, const int trans_thresh)
> {
> struct nearest_color res = {.dist_sqd = INT_MAX, .node_pos = -1};
> - colormap_nearest_node(s, node, 0, rgb, &res);
> + colormap_nearest_node(node, 0, rgb, trans_thresh, &res);
> return node[res.node_pos].palette_id;
> }
>
> @@ -256,7 +251,7 @@ struct stack_node {
> int dx2;
> };
>
> -static av_always_inline uint8_t colormap_nearest_iterative(const
> PaletteUseContext *s, const struct color_node *root, const uint8_t *target)
> +static av_always_inline uint8_t colormap_nearest_iterative(const struct
> color_node *root, const uint8_t *target, const int trans_thresh)
> {
> int pos = 0, best_node_id = -1, best_dist = INT_MAX, cur_color_id = 0;
> struct stack_node nodes[16];
> @@ -266,7 +261,7 @@ static av_always_inline uint8_t
> colormap_nearest_iterative(const PaletteUseConte
>
> const struct color_node *kd = &root[cur_color_id];
> const uint8_t *current = kd->val;
> - const int current_to_target = diff(target, current, s);
> + const int current_to_target = diff(target, current, trans_thresh);
>
> /* Compare current color node to the target and update our best
> node if
> * it's actually better. */
> @@ -328,10 +323,10 @@ end:
> return root[best_node_id].palette_id;
> }
>
> -#define COLORMAP_NEAREST(s, search, root, target)
> \
> - search == COLOR_SEARCH_NNS_ITERATIVE ? colormap_nearest_iterative(s,
> root, target) : \
> - search == COLOR_SEARCH_NNS_RECURSIVE ? colormap_nearest_recursive(s,
> root, target) : \
> - colormap_nearest_bruteforce(s,
> target)
> +#define COLORMAP_NEAREST(search, palette, root, target, trans_thresh)
> \
> + search == COLOR_SEARCH_NNS_ITERATIVE ?
> colormap_nearest_iterative(root, target, trans_thresh) : \
> + search == COLOR_SEARCH_NNS_RECURSIVE ?
> colormap_nearest_recursive(root, target, trans_thresh) : \
> +
> colormap_nearest_bruteforce(palette, target, trans_thresh)
>
> /**
> * Check if the requested color is in the cache already. If not, find it
> in the
> @@ -368,13 +363,13 @@ static av_always_inline int
> color_get(PaletteUseContext *s, uint32_t color,
> if (!e)
> return AVERROR(ENOMEM);
> e->color = color;
> - e->pal_entry = COLORMAP_NEAREST(s, search_method, s->map, argb_elts);
> + e->pal_entry = COLORMAP_NEAREST(search_method, s->palette, s->map,
> argb_elts, s->trans_thresh);
>
> return e->pal_entry;
> }
>
> static av_always_inline int get_dst_color_err(PaletteUseContext *s,
> - uint32_t c, int *ea, int
> *er, int *eg, int *eb,
> + uint32_t c, int *er, int
> *eg, int *eb,
> const enum
> color_search_method search_method)
> {
> const uint8_t a = c >> 24 & 0xff;
> @@ -387,9 +382,8 @@ static av_always_inline int
> get_dst_color_err(PaletteUseContext *s,
> return dstx;
> dstc = s->palette[dstx];
> if (dstx == s->transparency_index) {
> - *ea =*er = *eg = *eb = 0;
> + *er = *eg = *eb = 0;
> } else {
> - *ea = (int)a - (int)(dstc >> 24 & 0xff);
> *er = (int)r - (int)(dstc >> 16 & 0xff);
> *eg = (int)g - (int)(dstc >> 8 & 0xff);
> *eb = (int)b - (int)(dstc & 0xff);
> @@ -413,7 +407,7 @@ static av_always_inline int
> set_frame(PaletteUseContext *s, AVFrame *out, AVFram
>
> for (y = y_start; y < h; y++) {
> for (x = x_start; x < w; x++) {
> - int ea, er, eg, eb;
> + int er, eg, eb;
>
> if (dither == DITHERING_BAYER) {
> const int d = s->ordered_dither[(y & 7)<<3 | (x & 7)];
> @@ -433,7 +427,7 @@ static av_always_inline int
> set_frame(PaletteUseContext *s, AVFrame *out, AVFram
>
> } else if (dither == DITHERING_HECKBERT) {
> const int right = x < w - 1, down = y < h - 1;
> - const int color = get_dst_color_err(s, src[x], &ea, &er,
> &eg, &eb, search_method);
> + const int color = get_dst_color_err(s, src[x], &er, &eg,
> &eb, search_method);
>
> if (color < 0)
> return color;
> @@ -445,7 +439,7 @@ static av_always_inline int
> set_frame(PaletteUseContext *s, AVFrame *out, AVFram
>
> } else if (dither == DITHERING_FLOYD_STEINBERG) {
> const int right = x < w - 1, down = y < h - 1, left = x >
> x_start;
> - const int color = get_dst_color_err(s, src[x], &ea, &er,
> &eg, &eb, search_method);
> + const int color = get_dst_color_err(s, src[x], &er, &eg,
> &eb, search_method);
>
> if (color < 0)
> return color;
> @@ -459,7 +453,7 @@ static av_always_inline int
> set_frame(PaletteUseContext *s, AVFrame *out, AVFram
> } else if (dither == DITHERING_SIERRA2) {
> const int right = x < w - 1, down = y < h - 1, left =
> x > x_start;
> const int right2 = x < w - 2, left2 =
> x > x_start + 1;
> - const int color = get_dst_color_err(s, src[x], &ea, &er,
> &eg, &eb, search_method);
> + const int color = get_dst_color_err(s, src[x], &er, &eg,
> &eb, search_method);
>
> if (color < 0)
> return color;
> @@ -478,7 +472,7 @@ static av_always_inline int
> set_frame(PaletteUseContext *s, AVFrame *out, AVFram
>
> } else if (dither == DITHERING_SIERRA2_4A) {
> const int right = x < w - 1, down = y < h - 1, left = x >
> x_start;
> - const int color = get_dst_color_err(s, src[x], &ea, &er,
> &eg, &eb, search_method);
> + const int color = get_dst_color_err(s, src[x], &er, &eg,
> &eb, search_method);
>
> if (color < 0)
> return color;
> @@ -561,7 +555,8 @@ static int disp_tree(const struct color_node *node,
> const char *fname)
> return 0;
> }
>
> -static int debug_accuracy(const PaletteUseContext *s)
> +static int debug_accuracy(const struct color_node *node, const uint32_t
> *palette, const int trans_thresh,
> + const enum color_search_method search_method)
> {
> int r, g, b, ret = 0;
>
> @@ -569,26 +564,19 @@ static int debug_accuracy(const PaletteUseContext *s)
> for (g = 0; g < 256; g++) {
> for (b = 0; b < 256; b++) {
> const uint8_t argb[] = {0xff, r, g, b};
> - const int r1 = COLORMAP_NEAREST(s,
> s->color_search_method, s->map, argb);
> - const int r2 = colormap_nearest_bruteforce(s, argb);
> + const int r1 = COLORMAP_NEAREST(search_method, palette,
> node, argb, trans_thresh);
> + const int r2 = colormap_nearest_bruteforce(palette, argb,
> trans_thresh);
> if (r1 != r2) {
> - const uint32_t c1 = s->palette[r1];
> - const uint32_t c2 = s->palette[r2];
> - const uint8_t a1 = s->use_alpha ? c1>>24 & 0xff :
> 0xff;
> - const uint8_t a2 = s->use_alpha ? c2>>24 & 0xff :
> 0xff;
> - const uint8_t palargb1[] = { a1, c1>>16 & 0xff, c1>>
> 8 & 0xff, c1 & 0xff };
> - const uint8_t palargb2[] = { a2, c2>>16 & 0xff, c2>>
> 8 & 0xff, c2 & 0xff };
> - const int d1 = diff(palargb1, argb, s);
> - const int d2 = diff(palargb2, argb, s);
> + const uint32_t c1 = palette[r1];
> + const uint32_t c2 = palette[r2];
> + const uint8_t palargb1[] = { 0xff, c1>>16 & 0xff,
> c1>> 8 & 0xff, c1 & 0xff };
> + const uint8_t palargb2[] = { 0xff, c2>>16 & 0xff,
> c2>> 8 & 0xff, c2 & 0xff };
> + const int d1 = diff(palargb1, argb, trans_thresh);
> + const int d2 = diff(palargb2, argb, trans_thresh);
> if (d1 != d2) {
> - if (s->use_alpha)
> - av_log(NULL, AV_LOG_ERROR,
> - "/!\\ %02X%02X%02X: %d ! %d
> (%08"PRIX32" ! %08"PRIX32") / dist: %d ! %d\n",
> - r, g, b, r1, r2, c1, c2, d1, d2);
> - else
> - av_log(NULL, AV_LOG_ERROR,
> - "/!\\ %02X%02X%02X: %d ! %d
> (%06"PRIX32" ! %06"PRIX32") / dist: %d ! %d\n",
> - r, g, b, r1, r2, c1 & 0xffffff, c2 &
> 0xffffff, d1, d2);
> + av_log(NULL, AV_LOG_ERROR,
> + "/!\\ %02X%02X%02X: %d ! %d (%06"PRIX32" !
> %06"PRIX32") / dist: %d ! %d\n",
> + r, g, b, r1, r2, c1 & 0xffffff, c2 &
> 0xffffff, d1, d2);
> ret = 1;
> }
> }
> @@ -604,8 +592,8 @@ struct color {
> };
>
> struct color_rect {
> - uint8_t min[4];
> - uint8_t max[4];
> + uint8_t min[3];
> + uint8_t max[3];
> };
>
> typedef int (*cmp_func)(const void *, const void *);
> @@ -626,47 +614,43 @@ DECLARE_CMP_FUNC(b, 3)
>
> static const cmp_func cmp_funcs[] = {cmp_a, cmp_r, cmp_g, cmp_b};
>
> -static int get_next_color(const uint8_t *color_used, const
> PaletteUseContext *s,
> +static int get_next_color(const uint8_t *color_used, const uint32_t
> *palette,
> + const int trans_thresh,
> int *component, const struct color_rect *box)
> {
> - int wa, wr, wg, wb;
> + int wr, wg, wb;
> int i, longest = 0;
> unsigned nb_color = 0;
> struct color_rect ranges;
> struct color tmp_pal[256];
> cmp_func cmpf;
>
> - ranges.min[0] = ranges.min[1] = ranges.min[2] = ranges.min[3]= 0xff;
> - ranges.max[0] = ranges.max[1] = ranges.max[2] = ranges.max[3]= 0x00;
> + ranges.min[0] = ranges.min[1] = ranges.min[2] = 0xff;
> + ranges.max[0] = ranges.max[1] = ranges.max[2] = 0x00;
>
> for (i = 0; i < AVPALETTE_COUNT; i++) {
> - const uint32_t c = s->palette[i];
> + const uint32_t c = palette[i];
> const uint8_t a = c >> 24 & 0xff;
> const uint8_t r = c >> 16 & 0xff;
> const uint8_t g = c >> 8 & 0xff;
> const uint8_t b = c & 0xff;
>
> - if (!s->use_alpha && a < s->trans_thresh) {
> + if (a < trans_thresh) {
> continue;
> }
>
> - if (color_used[i] || (a != 0xff && !s->use_alpha) ||
> - r < box->min[1] || g < box->min[2] || b < box->min[3] ||
> - r > box->max[1] || g > box->max[2] || b > box->max[3])
> + if (color_used[i] || (a != 0xff) ||
> + r < box->min[0] || g < box->min[1] || b < box->min[2] ||
> + r > box->max[0] || g > box->max[1] || b > box->max[2])
> continue;
>
> - if (s->use_alpha && (a < box->min[0] || a > box->max[0]))
> - continue;
> -
> - if (a < ranges.min[0]) ranges.min[0] = a;
> - if (r < ranges.min[1]) ranges.min[1] = r;
> - if (g < ranges.min[2]) ranges.min[2] = g;
> - if (b < ranges.min[3]) ranges.min[3] = b;
> + if (r < ranges.min[0]) ranges.min[0] = r;
> + if (g < ranges.min[1]) ranges.min[1] = g;
> + if (b < ranges.min[2]) ranges.min[2] = b;
>
> - if (a > ranges.max[0]) ranges.max[0] = a;
> - if (r > ranges.max[1]) ranges.max[1] = r;
> - if (g > ranges.max[2]) ranges.max[2] = g;
> - if (b > ranges.max[3]) ranges.max[3] = b;
> + if (r > ranges.max[0]) ranges.max[0] = r;
> + if (g > ranges.max[1]) ranges.max[1] = g;
> + if (b > ranges.max[2]) ranges.max[2] = b;
>
> tmp_pal[nb_color].value = c;
> tmp_pal[nb_color].pal_id = i;
> @@ -678,22 +662,12 @@ static int get_next_color(const uint8_t *color_used,
> const PaletteUseContext *s,
> return -1;
>
> /* define longest axis that will be the split component */
> - wa = ranges.max[0] - ranges.min[0];
> - wr = ranges.max[1] - ranges.min[1];
> - wg = ranges.max[2] - ranges.min[2];
> - wb = ranges.max[3] - ranges.min[3];
> -
> - if (s->use_alpha) {
> - if (wa >= wr && wa >= wb && wa >= wg) longest = 0;
> - if (wr >= wg && wr >= wb && wr >= wa) longest = 1;
> - if (wg >= wr && wg >= wb && wg >= wa) longest = 2;
> - if (wb >= wr && wb >= wg && wb >= wa) longest = 3;
> - } else {
> - if (wr >= wg && wr >= wb) longest = 1;
> - if (wg >= wr && wg >= wb) longest = 2;
> - if (wb >= wr && wb >= wg) longest = 3;
> - }
> -
> + wr = ranges.max[0] - ranges.min[0];
> + wg = ranges.max[1] - ranges.min[1];
> + wb = ranges.max[2] - ranges.min[2];
> + if (wr >= wg && wr >= wb) longest = 1;
> + if (wg >= wr && wg >= wb) longest = 2;
> + if (wb >= wr && wb >= wg) longest = 3;
> cmpf = cmp_funcs[longest];
> *component = longest;
>
> @@ -706,7 +680,8 @@ static int get_next_color(const uint8_t *color_used,
> const PaletteUseContext *s,
> static int colormap_insert(struct color_node *map,
> uint8_t *color_used,
> int *nb_used,
> - const PaletteUseContext *s,
> + const uint32_t *palette,
> + const int trans_thresh,
> const struct color_rect *box)
> {
> uint32_t c;
> @@ -714,14 +689,14 @@ static int colormap_insert(struct color_node *map,
> int node_left_id = -1, node_right_id = -1;
> struct color_node *node;
> struct color_rect box1, box2;
> - const int pal_id = get_next_color(color_used, s, &component, box);
> + const int pal_id = get_next_color(color_used, palette, trans_thresh,
> &component, box);
>
> if (pal_id < 0)
> return -1;
>
> /* create new node with that color */
> cur_id = (*nb_used)++;
> - c = s->palette[pal_id];
> + c = palette[pal_id];
> node = &map[cur_id];
> node->split = component;
> node->palette_id = pal_id;
> @@ -734,13 +709,13 @@ static int colormap_insert(struct color_node *map,
>
> /* get the two boxes this node creates */
> box1 = box2 = *box;
> - box1.max[component] = node->val[component];
> - box2.min[component] = FFMIN(node->val[component] + 1, 255);
> + box1.max[component-1] = node->val[component];
> + box2.min[component-1] = FFMIN(node->val[component] + 1, 255);
>
> - node_left_id = colormap_insert(map, color_used, nb_used, s, &box1);
> + node_left_id = colormap_insert(map, color_used, nb_used, palette,
> trans_thresh, &box1);
>
> - if (box2.min[component] <= box2.max[component])
> - node_right_id = colormap_insert(map, color_used, nb_used, s,
> &box2);
> + if (box2.min[component-1] <= box2.max[component-1])
> + node_right_id = colormap_insert(map, color_used, nb_used,
> palette, trans_thresh, &box2);
>
> node->left_id = node_left_id;
> node->right_id = node_right_id;
> @@ -755,13 +730,6 @@ static int cmp_pal_entry(const void *a, const void *b)
> return c1 - c2;
> }
>
> -static int cmp_pal_entry_alpha(const void *a, const void *b)
> -{
> - const int c1 = *(const uint32_t *)a;
> - const int c2 = *(const uint32_t *)b;
> - return c1 - c2;
> -}
> -
> static void load_colormap(PaletteUseContext *s)
> {
> int i, nb_used = 0;
> @@ -769,13 +737,12 @@ static void load_colormap(PaletteUseContext *s)
> uint32_t last_color = 0;
> struct color_rect box;
>
> - if (!s->use_alpha && s->transparency_index >= 0) {
> + if (s->transparency_index >= 0) {
> FFSWAP(uint32_t, s->palette[s->transparency_index],
> s->palette[255]);
> }
>
> /* disable transparent colors and dups */
> - qsort(s->palette, AVPALETTE_COUNT-(s->transparency_index >= 0),
> sizeof(*s->palette),
> - s->use_alpha ? cmp_pal_entry_alpha : cmp_pal_entry);
> + qsort(s->palette, AVPALETTE_COUNT-(s->transparency_index >= 0),
> sizeof(*s->palette), cmp_pal_entry);
>
> for (i = 0; i < AVPALETTE_COUNT; i++) {
> const uint32_t c = s->palette[i];
> @@ -784,22 +751,22 @@ static void load_colormap(PaletteUseContext *s)
> continue;
> }
> last_color = c;
> - if (!s->use_alpha && c >> 24 < s->trans_thresh) {
> + if (c >> 24 < s->trans_thresh) {
> color_used[i] = 1; // ignore transparent color(s)
> continue;
> }
> }
>
> - box.min[0] = box.min[1] = box.min[2] = box.min[3] = 0x00;
> - box.max[0] = box.max[1] = box.max[2] = box.max[3] = 0xff;
> + box.min[0] = box.min[1] = box.min[2] = 0x00;
> + box.max[0] = box.max[1] = box.max[2] = 0xff;
>
> - colormap_insert(s->map, color_used, &nb_used, s, &box);
> + colormap_insert(s->map, color_used, &nb_used, s->palette,
> s->trans_thresh, &box);
>
> if (s->dot_filename)
> disp_tree(s->map, s->dot_filename);
>
> if (s->debug_accuracy) {
> - if (!debug_accuracy(s))
> + if (!debug_accuracy(s->map, s->palette, s->trans_thresh,
> s->color_search_method))
> av_log(NULL, AV_LOG_INFO, "Accuracy check passed\n");
> }
> }
> @@ -813,18 +780,16 @@ static void debug_mean_error(PaletteUseContext *s,
> const AVFrame *in1,
> uint8_t *src2 = in2->data[0];
> const int src1_linesize = in1->linesize[0] >> 2;
> const int src2_linesize = in2->linesize[0];
> - const float div = in1->width * in1->height * (s->use_alpha ? 4 : 3);
> + const float div = in1->width * in1->height * 3;
> unsigned mean_err = 0;
>
> for (y = 0; y < in1->height; y++) {
> for (x = 0; x < in1->width; x++) {
> const uint32_t c1 = src1[x];
> const uint32_t c2 = palette[src2[x]];
> - const uint8_t a1 = s->use_alpha ? c1>>24 & 0xff : 0xff;
> - const uint8_t a2 = s->use_alpha ? c2>>24 & 0xff : 0xff;
> - const uint8_t argb1[] = {a1, c1 >> 16 & 0xff, c1 >> 8 & 0xff,
> c1 & 0xff};
> - const uint8_t argb2[] = {a2, c2 >> 16 & 0xff, c2 >> 8 & 0xff,
> c2 & 0xff};
> - mean_err += diff(argb1, argb2, s);
> + const uint8_t argb1[] = {0xff, c1 >> 16 & 0xff, c1 >> 8 &
> 0xff, c1 & 0xff};
> + const uint8_t argb2[] = {0xff, c2 >> 16 & 0xff, c2 >> 8 &
> 0xff, c2 & 0xff};
> + mean_err += diff(argb1, argb2, s->trans_thresh);
> }
> src1 += src1_linesize;
> src2 += src2_linesize;
> @@ -1024,7 +989,7 @@ static void load_palette(PaletteUseContext *s, const
> AVFrame *palette_frame)
> for (y = 0; y < palette_frame->height; y++) {
> for (x = 0; x < palette_frame->width; x++) {
> s->palette[i] = p[x];
> - if (!s->use_alpha && p[x]>>24 < s->trans_thresh) {
> + if (p[x]>>24 < s->trans_thresh) {
> s->transparency_index = i; // we are assuming at most one
> transparent color in palette
> }
> i++;
> --
> 2.39.0
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request at ffmpeg.org with subject "unsubscribe".
>
More information about the ffmpeg-devel
mailing list