History

avfilter/vf_palette(gen|use): support palettes with alpha

Soft Works committed 5 years ago

Commit dea673d1 parent 3ee72503 files · +231 −138Browse files

Unified · Split

3 files
  1. doc/filters.texi +8 −0
  2. libavfilter/vf_palettegen.c +93 −43
  3. libavfilter/vf_paletteuse.c +130 −95
doc/filters.texi+8−0
@@ -16580,6 +16580,9 @@ Compute new histogram for each frame. · 16579 unmodified lines
16580@end table16580@end table
16581 16581
16582Default value is @var{full}.16582Default value is @var{full}.
16583@item use_alpha
16584Create a palette of colors with alpha components.
16585Setting this, will automatically disable 'reserve_transparent'.
16583@end table16586@end table
16584 16587
16585The filter also exports the frame metadata @code{lavfi.color_quant_ratio}16588The filter also exports the frame metadata @code{lavfi.color_quant_ratio}
@@ -16658,6 +16661,11 @@ will be treated as completely opaque, and values below this threshold will be · 72 unmodified lines
16658treated as completely transparent.16661treated as completely transparent.
16659 16662
16660The option must be an integer value in the range [0,255]. Default is @var{128}.16663The option must be an integer value in the range [0,255]. Default is @var{128}.
16664
16665@item use_alpha
16666Apply the palette by taking alpha values into account. Only useful with
16667palettes that are containing multiple colors with alpha components.
16668Setting this will automatically disable 'alpha_treshold'.
16661@end table16669@end table
16662 16670
16663@subsection Examples16671@subsection Examples
libavfilter/vf_palettegen.c+93−43
@@ -59,7 +59,7 @@ enum { · 58 unmodified lines
59};59};
60 60
61#define NBITS 561#define NBITS 5
62#define HIST_SIZE (1<<(3*NBITS))62#define HIST_SIZE (1<<(4*NBITS))
63 63
64typedef struct PaletteGenContext {64typedef struct PaletteGenContext {
65 const AVClass *class;65 const AVClass *class;
@@ -67,6 +67,7 @@ typedef struct PaletteGenContext { · 1 unmodified line
67 int max_colors;67 int max_colors;
68 int reserve_transparent;68 int reserve_transparent;
69 int stats_mode;69 int stats_mode;
70 int use_alpha;
70 71
71 AVFrame *prev_frame; // previous frame used for the diff stats_mode72 AVFrame *prev_frame; // previous frame used for the diff stats_mode
72 struct hist_node histogram[HIST_SIZE]; // histogram/hashtable of the colors73 struct hist_node histogram[HIST_SIZE]; // histogram/hashtable of the colors
@@ -88,6 +89,7 @@ static const AVOption palettegen_options[] = { · 15 unmodified lines
88 { "full", "compute full frame histograms", 0, AV_OPT_TYPE_CONST, {.i64=STATS_MODE_ALL_FRAMES}, INT_MIN, INT_MAX, FLAGS, "mode" },89 { "full", "compute full frame histograms", 0, AV_OPT_TYPE_CONST, {.i64=STATS_MODE_ALL_FRAMES}, INT_MIN, INT_MAX, FLAGS, "mode" },
89 { "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" },90 { "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" },
90 { "single", "compute new histogram for each frame", 0, AV_OPT_TYPE_CONST, {.i64=STATS_MODE_SINGLE_FRAMES}, INT_MIN, INT_MAX, FLAGS, "mode" },91 { "single", "compute new histogram for each frame", 0, AV_OPT_TYPE_CONST, {.i64=STATS_MODE_SINGLE_FRAMES}, INT_MIN, INT_MAX, FLAGS, "mode" },
92 { "use_alpha", "create a palette including alpha values", OFFSET(use_alpha), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS },
91 { NULL }93 { NULL }
92};94};
93 95
@@ -113,15 +115,16 @@ static int cmp_##name(const void *pa, const void *pb) \ · 19 unmodified lines
113{ \115{ \
114 const struct color_ref * const *a = pa; \116 const struct color_ref * const *a = pa; \
115 const struct color_ref * const *b = pb; \117 const struct color_ref * const *b = pb; \
116 return (int)((*a)->color >> (8 * (2 - (pos))) & 0xff) \118 return (int)((*a)->color >> (8 * (3 - (pos))) & 0xff) \
117 - (int)((*b)->color >> (8 * (2 - (pos))) & 0xff); \119 - (int)((*b)->color >> (8 * (3 - (pos))) & 0xff); \
118}120}
119 121
120DECLARE_CMP_FUNC(r, 0)122DECLARE_CMP_FUNC(a, 0)
121DECLARE_CMP_FUNC(g, 1)123DECLARE_CMP_FUNC(r, 1)
122DECLARE_CMP_FUNC(b, 2)124DECLARE_CMP_FUNC(g, 2)
125DECLARE_CMP_FUNC(b, 3)
123 126
124static const cmp_func cmp_funcs[] = {cmp_r, cmp_g, cmp_b};127static const cmp_func cmp_funcs[] = {cmp_a, cmp_r, cmp_g, cmp_b};
125 128
126/**129/**
127 * Simple color comparison for sorting the final palette130 * Simple color comparison for sorting the final palette
@@ -143,6 +146,17 @@ static av_always_inline int diff(const uint32_t a, const uint32_t b) · 15 unmodified lines
143 return dr*dr + dg*dg + db*db;146 return dr*dr + dg*dg + db*db;
144}147}
145 148
149static av_always_inline int diff_alpha(const uint32_t a, const uint32_t b)
150{
151 const uint8_t c1[] = {a >> 24 & 0xff, a >> 16 & 0xff, a >> 8 & 0xff, a & 0xff};
152 const uint8_t c2[] = {b >> 24 & 0xff, b >> 16 & 0xff, b >> 8 & 0xff, b & 0xff};
153 const int da = c1[0] - c2[0];
154 const int dr = c1[1] - c2[1];
155 const int dg = c1[2] - c2[2];
156 const int db = c1[3] - c2[3];
157 return da*da + dr*dr + dg*dg + db*db;
158}
159
146/**160/**
147 * Find the next box to split: pick the one with the highest variance161 * Find the next box to split: pick the one with the highest variance
148 */162 */
@@ -164,7 +178,10 @@ static int get_next_box_id_to_split(PaletteGenContext *s) · 15 unmodified lines
164 178
165 for (i = 0; i < box->len; i++) {179 for (i = 0; i < box->len; i++) {
166 const struct color_ref *ref = s->refs[box->start + i];180 const struct color_ref *ref = s->refs[box->start + i];
167 variance += diff(ref->color, box->color) * ref->count;181 if (s->use_alpha)
182 variance += (int64_t)diff_alpha(ref->color, box->color) * ref->count;
183 else
184 variance += (int64_t)diff(ref->color, box->color) * ref->count;
168 }185 }
169 box->variance = variance;186 box->variance = variance;
170 }187 }
@@ -184,24 +201,31 @@ static int get_next_box_id_to_split(PaletteGenContext *s) · 13 unmodified lines
184 * specified box. Takes into account the weight of each color.201 * specified box. Takes into account the weight of each color.
185 */202 */
186static uint32_t get_avg_color(struct color_ref * const *refs,203static uint32_t get_avg_color(struct color_ref * const *refs,
187 const struct range_box *box)204 const struct range_box *box, int use_alpha)
188{205{
189 int i;206 int i;
190 const int n = box->len;207 const int n = box->len;
191 uint64_t r = 0, g = 0, b = 0, div = 0;208 uint64_t a = 0, r = 0, g = 0, b = 0, div = 0;
192 209
193 for (i = 0; i < n; i++) {210 for (i = 0; i < n; i++) {
194 const struct color_ref *ref = refs[box->start + i];211 const struct color_ref *ref = refs[box->start + i];
195 r += (ref->color >> 16 & 0xff) * ref->count;212 if (use_alpha)
196 g += (ref->color >> 8 & 0xff) * ref->count;213 a += (ref->color >> 24 & 0xff) * ref->count;
197 b += (ref->color & 0xff) * ref->count;214 r += (ref->color >> 16 & 0xff) * ref->count;
215 g += (ref->color >> 8 & 0xff) * ref->count;
216 b += (ref->color & 0xff) * ref->count;
198 div += ref->count;217 div += ref->count;
199 }218 }
200 219
220 if (use_alpha)
221 a = a / div;
201 r = r / div;222 r = r / div;
202 g = g / div;223 g = g / div;
203 b = b / div;224 b = b / div;
204 225
226 if (use_alpha)
227 return a<<24 | r<<16 | g<<8 | b;
228
205 return 0xffU<<24 | r<<16 | g<<8 | b;229 return 0xffU<<24 | r<<16 | g<<8 | b;
206}230}
207 231
@@ -220,8 +244,8 @@ static void split_box(PaletteGenContext *s, struct range_box *box, int n) · 12 unmodified lines
220 av_assert0(box->len >= 1);244 av_assert0(box->len >= 1);
221 av_assert0(new_box->len >= 1);245 av_assert0(new_box->len >= 1);
222 246
223 box->color = get_avg_color(s->refs, box);247 box->color = get_avg_color(s->refs, box, s->use_alpha);
224 new_box->color = get_avg_color(s->refs, new_box);248 new_box->color = get_avg_color(s->refs, new_box, s->use_alpha);
225 box->variance = -1;249 box->variance = -1;
226 new_box->variance = -1;250 new_box->variance = -1;
227}251}
@@ -251,7 +275,7 @@ static void write_palette(AVFilterContext *ctx, AVFrame *out) · 23 unmodified lines
251 pal += pal_linesize;275 pal += pal_linesize;
252 }276 }
253 277
254 if (s->reserve_transparent) {278 if (s->reserve_transparent && !s->use_alpha) {
255 av_assert0(s->nb_boxes < 256);279 av_assert0(s->nb_boxes < 256);
256 pal[out->width - pal_linesize - 1] = AV_RB32(&s->transparency_color) >> 8;280 pal[out->width - pal_linesize - 1] = AV_RB32(&s->transparency_color) >> 8;
257 }281 }
@@ -319,40 +343,49 @@ static AVFrame *get_palette_frame(AVFilterContext *ctx) · 61 unmodified lines
319 box = &s->boxes[box_id];343 box = &s->boxes[box_id];
320 box->len = s->nb_refs;344 box->len = s->nb_refs;
321 box->sorted_by = -1;345 box->sorted_by = -1;
322 box->color = get_avg_color(s->refs, box);346 box->color = get_avg_color(s->refs, box, s->use_alpha);
323 box->variance = -1;347 box->variance = -1;
324 s->nb_boxes = 1;348 s->nb_boxes = 1;
325 349
326 while (box && box->len > 1) {350 while (box && box->len > 1) {
327 int i, rr, gr, br, longest;351 int i, ar, rr, gr, br, longest;
328 uint64_t median, box_weight = 0;352 uint64_t median, box_weight = 0;
329 353
330 /* compute the box weight (sum all the weights of the colors in the354 /* compute the box weight (sum all the weights of the colors in the
331 * range) and its boundings */355 * range) and its boundings */
332 uint8_t min[3] = {0xff, 0xff, 0xff};356 uint8_t min[4] = {0xff, 0xff, 0xff, 0xff};
333 uint8_t max[3] = {0x00, 0x00, 0x00};357 uint8_t max[4] = {0x00, 0x00, 0x00, 0x00};
334 for (i = box->start; i < box->start + box->len; i++) {358 for (i = box->start; i < box->start + box->len; i++) {
335 const struct color_ref *ref = s->refs[i];359 const struct color_ref *ref = s->refs[i];
336 const uint32_t rgb = ref->color;360 const uint32_t rgb = ref->color;
337 const uint8_t r = rgb >> 16 & 0xff, g = rgb >> 8 & 0xff, b = rgb & 0xff;361 const uint8_t a = rgb >> 24 & 0xff, r = rgb >> 16 & 0xff, g = rgb >> 8 & 0xff, b = rgb & 0xff;
338 min[0] = FFMIN(r, min[0]), max[0] = FFMAX(r, max[0]);362 min[0] = FFMIN(a, min[0]); max[0] = FFMAX(a, max[0]);
339 min[1] = FFMIN(g, min[1]), max[1] = FFMAX(g, max[1]);363 min[1] = FFMIN(r, min[1]); max[1] = FFMAX(r, max[1]);
340 min[2] = FFMIN(b, min[2]), max[2] = FFMAX(b, max[2]);364 min[2] = FFMIN(g, min[2]); max[2] = FFMAX(g, max[2]);
365 min[3] = FFMIN(b, min[3]); max[3] = FFMAX(b, max[3]);
341 box_weight += ref->count;366 box_weight += ref->count;
342 }367 }
343 368
344 /* define the axis to sort by according to the widest range of colors */369 /* define the axis to sort by according to the widest range of colors */
345 rr = max[0] - min[0];370 ar = max[0] - min[0];
346 gr = max[1] - min[1];371 rr = max[1] - min[1];
347 br = max[2] - min[2];372 gr = max[2] - min[2];
348 longest = 1; // pick green by default (the color the eye is the most sensitive to)373 br = max[3] - min[3];
349 if (br >= rr && br >= gr) longest = 2;374 longest = 2; // pick green by default (the color the eye is the most sensitive to)
350 if (rr >= gr && rr >= br) longest = 0;375 if (s->use_alpha) {
351 if (gr >= rr && gr >= br) longest = 1; // prefer green again376 if (ar >= rr && ar >= br && ar >= gr) longest = 0;
352 377 if (br >= rr && br >= gr && br >= ar) longest = 3;
353 ff_dlog(ctx, "box #%02X [%6d..%-6d] (%6d) w:%-6"PRIu64" ranges:[%2x %2x %2x] sort by %c (already sorted:%c) ",378 if (rr >= gr && rr >= br && rr >= ar) longest = 1;
379 if (gr >= rr && gr >= br && gr >= ar) longest = 2; // prefer green again
380 } else {
381 if (br >= rr && br >= gr) longest = 3;
382 if (rr >= gr && rr >= br) longest = 1;
383 if (gr >= rr && gr >= br) longest = 2; // prefer green again
384 }
385
386 ff_dlog(ctx, "box #%02X [%6d..%-6d] (%6d) w:%-6"PRIu64" ranges:[%2x %2x %2x %2x] sort by %c (already sorted:%c) ",
354 box_id, box->start, box->start + box->len - 1, box->len, box_weight,387 box_id, box->start, box->start + box->len - 1, box->len, box_weight,
355 rr, gr, br, "rgb"[longest], box->sorted_by == longest ? 'y':'n');388 ar, rr, gr, br, "argb"[longest], box->sorted_by == longest ? 'y' : 'n');
356 389
357 /* sort the range by its longest axis if it's not already sorted */390 /* sort the range by its longest axis if it's not already sorted */
358 if (box->sorted_by != longest) {391 if (box->sorted_by != longest) {
@@ -394,21 +427,27 @@ static AVFrame *get_palette_frame(AVFilterContext *ctx) · 35 unmodified lines
394 * It keeps the NBITS least significant bit of each component to make it427 * It keeps the NBITS least significant bit of each component to make it
395 * "random" even if the scene doesn't have much different colors.428 * "random" even if the scene doesn't have much different colors.
396 */429 */
397static inline unsigned color_hash(uint32_t color)430static inline unsigned color_hash(uint32_t color, int use_alpha)
398{431{
399 const uint8_t r = color >> 16 & ((1<<NBITS)-1);432 const uint8_t r = color >> 16 & ((1<<NBITS)-1);
400 const uint8_t g = color >> 8 & ((1<<NBITS)-1);433 const uint8_t g = color >> 8 & ((1<<NBITS)-1);
401 const uint8_t b = color & ((1<<NBITS)-1);434 const uint8_t b = color & ((1<<NBITS)-1);
435
436 if (use_alpha) {
437 const uint8_t a = color >> 24 & ((1 << NBITS) - 1);
438 return a << (NBITS * 3) | r << (NBITS * 2) | g << NBITS | b;
439 }
440
402 return r<<(NBITS*2) | g<<NBITS | b;441 return r<<(NBITS*2) | g<<NBITS | b;
403}442}
404 443
405/**444/**
406 * Locate the color in the hash table and increment its counter.445 * Locate the color in the hash table and increment its counter.
407 */446 */
408static int color_inc(struct hist_node *hist, uint32_t color)447static int color_inc(struct hist_node *hist, uint32_t color, int use_alpha)
409{448{
410 int i;449 int i;
411 const unsigned hash = color_hash(color);450 const unsigned hash = color_hash(color, use_alpha);
412 struct hist_node *node = &hist[hash];451 struct hist_node *node = &hist[hash];
413 struct color_ref *e;452 struct color_ref *e;
414 453
@@ -433,7 +472,7 @@ static int color_inc(struct hist_node *hist, uint32_t color) · 18 unmodified lines
433 * Update histogram when pixels differ from previous frame.472 * Update histogram when pixels differ from previous frame.
434 */473 */
435static int update_histogram_diff(struct hist_node *hist,474static int update_histogram_diff(struct hist_node *hist,
436 const AVFrame *f1, const AVFrame *f2)475 const AVFrame *f1, const AVFrame *f2, int use_alpha)
437{476{
438 int x, y, ret, nb_diff_colors = 0;477 int x, y, ret, nb_diff_colors = 0;
439 478
@@ -444,7 +483,7 @@ static int update_histogram_diff(struct hist_node *hist, · 4 unmodified lines
444 for (x = 0; x < f1->width; x++) {483 for (x = 0; x < f1->width; x++) {
445 if (p[x] == q[x])484 if (p[x] == q[x])
446 continue;485 continue;
447 ret = color_inc(hist, p[x]);486 ret = color_inc(hist, p[x], use_alpha);
448 if (ret < 0)487 if (ret < 0)
449 return ret;488 return ret;
450 nb_diff_colors += ret;489 nb_diff_colors += ret;
@@ -456,7 +495,7 @@ static int update_histogram_diff(struct hist_node *hist, · 5 unmodified lines
456/**495/**
457 * Simple histogram of the frame.496 * Simple histogram of the frame.
458 */497 */
459static int update_histogram_frame(struct hist_node *hist, const AVFrame *f)498static int update_histogram_frame(struct hist_node *hist, const AVFrame *f, int use_alpha)
460{499{
461 int x, y, ret, nb_diff_colors = 0;500 int x, y, ret, nb_diff_colors = 0;
462 501
@@ -464,7 +503,7 @@ static int update_histogram_frame(struct hist_node *hist, const AVFrame *f) · 1 unmodified line
464 const uint32_t *p = (const uint32_t *)(f->data[0] + y*f->linesize[0]);503 const uint32_t *p = (const uint32_t *)(f->data[0] + y*f->linesize[0]);
465 504
466 for (x = 0; x < f->width; x++) {505 for (x = 0; x < f->width; x++) {
467 ret = color_inc(hist, p[x]);506 ret = color_inc(hist, p[x], use_alpha);
468 if (ret < 0)507 if (ret < 0)
469 return ret;508 return ret;
470 nb_diff_colors += ret;509 nb_diff_colors += ret;
@@ -480,8 +519,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) · 9 unmodified lines
480{519{
481 AVFilterContext *ctx = inlink->dst;520 AVFilterContext *ctx = inlink->dst;
482 PaletteGenContext *s = ctx->priv;521 PaletteGenContext *s = ctx->priv;
483 int ret = s->prev_frame ? update_histogram_diff(s->histogram, s->prev_frame, in)522 int ret = s->prev_frame ? update_histogram_diff(s->histogram, s->prev_frame, in, s->use_alpha)
484 : update_histogram_frame(s->histogram, in);523 : update_histogram_frame(s->histogram, in, s->use_alpha);
485 524
486 if (ret > 0)525 if (ret > 0)
487 s->nb_refs += ret;526 s->nb_refs += ret;
@@ -540,6 +579,16 @@ static int config_output(AVFilterLink *outlink) · 52 unmodified lines
540 return 0;579 return 0;
541}580}
542 581
582static int init(AVFilterContext *ctx)
583{
584 PaletteGenContext* s = ctx->priv;
585
586 if (s->use_alpha && s->reserve_transparent)
587 s->reserve_transparent = 0;
588
589 return 0;
590}
591
543static av_cold void uninit(AVFilterContext *ctx)592static av_cold void uninit(AVFilterContext *ctx)
544{593{
545 int i;594 int i;
@@ -572,6 +621,7 @@ const AVFilter ff_vf_palettegen = { · 26 unmodified lines
572 .name = "palettegen",621 .name = "palettegen",
573 .description = NULL_IF_CONFIG_SMALL("Find the optimal palette for a given stream."),622 .description = NULL_IF_CONFIG_SMALL("Find the optimal palette for a given stream."),
574 .priv_size = sizeof(PaletteGenContext),623 .priv_size = sizeof(PaletteGenContext),
624 .init = init,
575 .uninit = uninit,625 .uninit = uninit,
576 FILTER_INPUTS(palettegen_inputs),626 FILTER_INPUTS(palettegen_inputs),
577 FILTER_OUTPUTS(palettegen_outputs),627 FILTER_OUTPUTS(palettegen_outputs),
libavfilter/vf_paletteuse.c+130−95
@@ -28,7 +28,6 @@ · 27 unmodified lines
28#include "libavutil/opt.h"28#include "libavutil/opt.h"
29#include "libavutil/qsort.h"29#include "libavutil/qsort.h"
30#include "avfilter.h"30#include "avfilter.h"
31#include "filters.h"
32#include "framesync.h"31#include "framesync.h"
33#include "internal.h"32#include "internal.h"
34 33
@@ -63,7 +62,7 @@ struct color_node { · 28 unmodified lines
63};62};
64 63
65#define NBITS 564#define NBITS 5
66#define CACHE_SIZE (1<<(3*NBITS))65#define CACHE_SIZE (1<<(4*NBITS))
67 66
68struct cached_color {67struct cached_color {
69 uint32_t color;68 uint32_t color;
@@ -88,6 +87,7 @@ typedef struct PaletteUseContext { · 18 unmodified lines
88 uint32_t palette[AVPALETTE_COUNT];87 uint32_t palette[AVPALETTE_COUNT];
89 int transparency_index; /* index in the palette of transparency. -1 if there is no transparency in the palette. */88 int transparency_index; /* index in the palette of transparency. -1 if there is no transparency in the palette. */
90 int trans_thresh;89 int trans_thresh;
90 int use_alpha;
91 int palette_loaded;91 int palette_loaded;
92 int dither;92 int dither;
93 int new;93 int new;
@@ -107,7 +107,7 @@ typedef struct PaletteUseContext { · 13 unmodified lines
107} PaletteUseContext;107} PaletteUseContext;
108 108
109#define OFFSET(x) offsetof(PaletteUseContext, x)109#define OFFSET(x) offsetof(PaletteUseContext, x)
110#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM110#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
111static const AVOption paletteuse_options[] = {111static const AVOption paletteuse_options[] = {
112 { "dither", "select dithering mode", OFFSET(dither), AV_OPT_TYPE_INT, {.i64=DITHERING_SIERRA2_4A}, 0, NB_DITHERING-1, FLAGS, "dithering_mode" },112 { "dither", "select dithering mode", OFFSET(dither), AV_OPT_TYPE_INT, {.i64=DITHERING_SIERRA2_4A}, 0, NB_DITHERING-1, FLAGS, "dithering_mode" },
113 { "bayer", "ordered 8x8 bayer dithering (deterministic)", 0, AV_OPT_TYPE_CONST, {.i64=DITHERING_BAYER}, INT_MIN, INT_MAX, FLAGS, "dithering_mode" },113 { "bayer", "ordered 8x8 bayer dithering (deterministic)", 0, AV_OPT_TYPE_CONST, {.i64=DITHERING_BAYER}, INT_MIN, INT_MAX, FLAGS, "dithering_mode" },
@@ -120,6 +120,7 @@ static const AVOption paletteuse_options[] = { · 6 unmodified lines
120 { "rectangle", "process smallest different rectangle", 0, AV_OPT_TYPE_CONST, {.i64=DIFF_MODE_RECTANGLE}, INT_MIN, INT_MAX, FLAGS, "diff_mode" },120 { "rectangle", "process smallest different rectangle", 0, AV_OPT_TYPE_CONST, {.i64=DIFF_MODE_RECTANGLE}, INT_MIN, INT_MAX, FLAGS, "diff_mode" },
121 { "new", "take new palette for each output frame", OFFSET(new), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },121 { "new", "take new palette for each output frame", OFFSET(new), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
122 { "alpha_threshold", "set the alpha threshold for transparency", OFFSET(trans_thresh), AV_OPT_TYPE_INT, {.i64=128}, 0, 255, FLAGS },122 { "alpha_threshold", "set the alpha threshold for transparency", OFFSET(trans_thresh), AV_OPT_TYPE_INT, {.i64=128}, 0, 255, FLAGS },
123 { "use_alpha", "use alpha channel for mapping", OFFSET(use_alpha), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
123 124
124 /* following are the debug options, not part of the official API */125 /* following are the debug options, not part of the official API */
125 { "debug_kdtree", "save Graphviz graph of the kdtree in specified file", OFFSET(dot_filename), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },126 { "debug_kdtree", "save Graphviz graph of the kdtree in specified file", OFFSET(dot_filename), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
@@ -161,37 +162,41 @@ static av_always_inline uint32_t dither_color(uint32_t px, int er, int eg, · 35 unmodified lines
161 | av_clip_uint8((px & 0xff) + ((eb * scale) / (1<<shift)));162 | av_clip_uint8((px & 0xff) + ((eb * scale) / (1<<shift)));
162}163}
163 164
164static av_always_inline int diff(const uint8_t *c1, const uint8_t *c2, const int trans_thresh)165static av_always_inline int diff(const uint8_t *c1, const uint8_t *c2, const PaletteUseContext *s)
165{166{
166 // XXX: try L*a*b with CIE76 (dL*dL + da*da + db*db)167 // XXX: try L*a*b with CIE76 (dL*dL + da*da + db*db)
168 const int da = c1[0] - c2[0];
167 const int dr = c1[1] - c2[1];169 const int dr = c1[1] - c2[1];
168 const int dg = c1[2] - c2[2];170 const int dg = c1[2] - c2[2];
169 const int db = c1[3] - c2[3];171 const int db = c1[3] - c2[3];
170 172
171 if (c1[0] < trans_thresh && c2[0] < trans_thresh) {173 if (s->use_alpha)
174 return da*da + dr*dr + dg*dg + db*db;
175
176 if (c1[0] < s->trans_thresh && c2[0] < s->trans_thresh) {
172 return 0;177 return 0;
173 } else if (c1[0] >= trans_thresh && c2[0] >= trans_thresh) {178 } else if (c1[0] >= s->trans_thresh && c2[0] >= s->trans_thresh) {
174 return dr*dr + dg*dg + db*db;179 return dr*dr + dg*dg + db*db;
175 } else {180 } else {
176 return 255*255 + 255*255 + 255*255;181 return 255*255 + 255*255 + 255*255;
177 }182 }
178}183}
179 184
180static av_always_inline uint8_t colormap_nearest_bruteforce(const uint32_t *palette, const uint8_t *argb, const int trans_thresh)185static av_always_inline uint8_t colormap_nearest_bruteforce(const PaletteUseContext *s, const uint8_t *argb)
181{186{
182 int i, pal_id = -1, min_dist = INT_MAX;187 int i, pal_id = -1, min_dist = INT_MAX;
183 188
184 for (i = 0; i < AVPALETTE_COUNT; i++) {189 for (i = 0; i < AVPALETTE_COUNT; i++) {
185 const uint32_t c = palette[i];190 const uint32_t c = s->palette[i];
186 191
187 if (c >> 24 >= trans_thresh) { // ignore transparent entry192 if (s->use_alpha || c >> 24 >= s->trans_thresh) { // ignore transparent entry
188 const uint8_t palargb[] = {193 const uint8_t palargb[] = {
189 palette[i]>>24 & 0xff,194 s->palette[i]>>24 & 0xff,
190 palette[i]>>16 & 0xff,195 s->palette[i]>>16 & 0xff,
191 palette[i]>> 8 & 0xff,196 s->palette[i]>> 8 & 0xff,
192 palette[i] & 0xff,197 s->palette[i] & 0xff,
193 };198 };
194 const int d = diff(palargb, argb, trans_thresh);199 const int d = diff(palargb, argb, s);
195 if (d < min_dist) {200 if (d < min_dist) {
196 pal_id = i;201 pal_id = i;
197 min_dist = d;202 min_dist = d;
@@ -207,17 +212,17 @@ struct nearest_color { · 9 unmodified lines
207 int dist_sqd;212 int dist_sqd;
208};213};
209 214
210static void colormap_nearest_node(const struct color_node *map,215static void colormap_nearest_node(const PaletteUseContext *s,
216 const struct color_node *map,
211 const int node_pos,217 const int node_pos,
212 const uint8_t *target,218 const uint8_t *target,
213 const int trans_thresh,
214 struct nearest_color *nearest)219 struct nearest_color *nearest)
215{220{
216 const struct color_node *kd = map + node_pos;221 const struct color_node *kd = map + node_pos;
217 const int s = kd->split;222 const int split = kd->split;
218 int dx, nearer_kd_id, further_kd_id;223 int dx, nearer_kd_id, further_kd_id;
219 const uint8_t *current = kd->val;224 const uint8_t *current = kd->val;
220 const int current_to_target = diff(target, current, trans_thresh);225 const int current_to_target = diff(target, current, s);
221 226
222 if (current_to_target < nearest->dist_sqd) {227 if (current_to_target < nearest->dist_sqd) {
223 nearest->node_pos = node_pos;228 nearest->node_pos = node_pos;
@@ -225,23 +230,23 @@ static void colormap_nearest_node(const struct color_node *map, · 1 unmodified line
225 }230 }
226 231
227 if (kd->left_id != -1 || kd->right_id != -1) {232 if (kd->left_id != -1 || kd->right_id != -1) {
228 dx = target[s] - current[s];233 dx = target[split] - current[split];
229 234
230 if (dx <= 0) nearer_kd_id = kd->left_id, further_kd_id = kd->right_id;235 if (dx <= 0) nearer_kd_id = kd->left_id, further_kd_id = kd->right_id;
231 else nearer_kd_id = kd->right_id, further_kd_id = kd->left_id;236 else nearer_kd_id = kd->right_id, further_kd_id = kd->left_id;
232 237
233 if (nearer_kd_id != -1)238 if (nearer_kd_id != -1)
234 colormap_nearest_node(map, nearer_kd_id, target, trans_thresh, nearest);239 colormap_nearest_node(s, map, nearer_kd_id, target, nearest);
235 240
236 if (further_kd_id != -1 && dx*dx < nearest->dist_sqd)241 if (further_kd_id != -1 && dx*dx < nearest->dist_sqd)
237 colormap_nearest_node(map, further_kd_id, target, trans_thresh, nearest);242 colormap_nearest_node(s, map, further_kd_id, target, nearest);
238 }243 }
239}244}
240 245
241static av_always_inline uint8_t colormap_nearest_recursive(const struct color_node *node, const uint8_t *rgb, const int trans_thresh)246static av_always_inline uint8_t colormap_nearest_recursive(const PaletteUseContext *s, const struct color_node *node, const uint8_t *rgb)
242{247{
243 struct nearest_color res = {.dist_sqd = INT_MAX, .node_pos = -1};248 struct nearest_color res = {.dist_sqd = INT_MAX, .node_pos = -1};
244 colormap_nearest_node(node, 0, rgb, trans_thresh, &res);249 colormap_nearest_node(s, node, 0, rgb, &res);
245 return node[res.node_pos].palette_id;250 return node[res.node_pos].palette_id;
246}251}
247 252
@@ -250,7 +255,7 @@ struct stack_node { · 2 unmodified lines
250 int dx2;255 int dx2;
251};256};
252 257
253static av_always_inline uint8_t colormap_nearest_iterative(const struct color_node *root, const uint8_t *target, const int trans_thresh)258static av_always_inline uint8_t colormap_nearest_iterative(const PaletteUseContext *s, const struct color_node *root, const uint8_t *target)
254{259{
255 int pos = 0, best_node_id = -1, best_dist = INT_MAX, cur_color_id = 0;260 int pos = 0, best_node_id = -1, best_dist = INT_MAX, cur_color_id = 0;
256 struct stack_node nodes[16];261 struct stack_node nodes[16];
@@ -260,7 +265,7 @@ static av_always_inline uint8_t colormap_nearest_iterative(const struct color_no · 3 unmodified lines
260 265
261 const struct color_node *kd = &root[cur_color_id];266 const struct color_node *kd = &root[cur_color_id];
262 const uint8_t *current = kd->val;267 const uint8_t *current = kd->val;
263 const int current_to_target = diff(target, current, trans_thresh);268 const int current_to_target = diff(target, current, s);
264 269
265 /* Compare current color node to the target and update our best node if270 /* Compare current color node to the target and update our best node if
266 * it's actually better. */271 * it's actually better. */
@@ -322,10 +327,10 @@ end: · 55 unmodified lines
322 return root[best_node_id].palette_id;327 return root[best_node_id].palette_id;
323}328}
324 329
325#define COLORMAP_NEAREST(search, palette, root, target, trans_thresh) \330#define COLORMAP_NEAREST(s, search, root, target) \
326 search == COLOR_SEARCH_NNS_ITERATIVE ? colormap_nearest_iterative(root, target, trans_thresh) : \331 search == COLOR_SEARCH_NNS_ITERATIVE ? colormap_nearest_iterative(s, root, target) : \
327 search == COLOR_SEARCH_NNS_RECURSIVE ? colormap_nearest_recursive(root, target, trans_thresh) : \332 search == COLOR_SEARCH_NNS_RECURSIVE ? colormap_nearest_recursive(s, root, target) : \
328 colormap_nearest_bruteforce(palette, target, trans_thresh)333 colormap_nearest_bruteforce(s, target)
329 334
330/**335/**
331 * Check if the requested color is in the cache already. If not, find it in the336 * Check if the requested color is in the cache already. If not, find it in the
@@ -362,13 +367,13 @@ static av_always_inline int color_get(PaletteUseContext *s, uint32_t color, · 30 unmodified lines
362 if (!e)367 if (!e)
363 return AVERROR(ENOMEM);368 return AVERROR(ENOMEM);
364 e->color = color;369 e->color = color;
365 e->pal_entry = COLORMAP_NEAREST(search_method, s->palette, s->map, argb_elts, s->trans_thresh);370 e->pal_entry = COLORMAP_NEAREST(s, search_method, s->map, argb_elts);
366 371
367 return e->pal_entry;372 return e->pal_entry;
368}373}
369 374
370static av_always_inline int get_dst_color_err(PaletteUseContext *s,375static av_always_inline int get_dst_color_err(PaletteUseContext *s,
371 uint32_t c, int *er, int *eg, int *eb,376 uint32_t c, int *ea, int *er, int *eg, int *eb,
372 const enum color_search_method search_method)377 const enum color_search_method search_method)
373{378{
374 const uint8_t a = c >> 24 & 0xff;379 const uint8_t a = c >> 24 & 0xff;
@@ -381,8 +386,9 @@ static av_always_inline int get_dst_color_err(PaletteUseContext *s, · 6 unmodified lines
381 return dstx;386 return dstx;
382 dstc = s->palette[dstx];387 dstc = s->palette[dstx];
383 if (dstx == s->transparency_index) {388 if (dstx == s->transparency_index) {
384 *er = *eg = *eb = 0;389 *ea =*er = *eg = *eb = 0;
385 } else {390 } else {
391 *ea = (int)a - (int)(dstc >> 24 & 0xff);
386 *er = (int)r - (int)(dstc >> 16 & 0xff);392 *er = (int)r - (int)(dstc >> 16 & 0xff);
387 *eg = (int)g - (int)(dstc >> 8 & 0xff);393 *eg = (int)g - (int)(dstc >> 8 & 0xff);
388 *eb = (int)b - (int)(dstc & 0xff);394 *eb = (int)b - (int)(dstc & 0xff);
@@ -406,7 +412,7 @@ static av_always_inline int set_frame(PaletteUseContext *s, AVFrame *out, AVFram · 17 unmodified lines
406 412
407 for (y = y_start; y < h; y++) {413 for (y = y_start; y < h; y++) {
408 for (x = x_start; x < w; x++) {414 for (x = x_start; x < w; x++) {
409 int er, eg, eb;415 int ea, er, eg, eb;
410 416
411 if (dither == DITHERING_BAYER) {417 if (dither == DITHERING_BAYER) {
412 const int d = s->ordered_dither[(y & 7)<<3 | (x & 7)];418 const int d = s->ordered_dither[(y & 7)<<3 | (x & 7)];
@@ -425,7 +431,7 @@ static av_always_inline int set_frame(PaletteUseContext *s, AVFrame *out, AVFram · 12 unmodified lines
425 431
426 } else if (dither == DITHERING_HECKBERT) {432 } else if (dither == DITHERING_HECKBERT) {
427 const int right = x < w - 1, down = y < h - 1;433 const int right = x < w - 1, down = y < h - 1;
428 const int color = get_dst_color_err(s, src[x], &er, &eg, &eb, search_method);434 const int color = get_dst_color_err(s, src[x], &ea, &er, &eg, &eb, search_method);
429 435
430 if (color < 0)436 if (color < 0)
431 return color;437 return color;
@@ -437,7 +443,7 @@ static av_always_inline int set_frame(PaletteUseContext *s, AVFrame *out, AVFram · 5 unmodified lines
437 443
438 } else if (dither == DITHERING_FLOYD_STEINBERG) {444 } else if (dither == DITHERING_FLOYD_STEINBERG) {
439 const int right = x < w - 1, down = y < h - 1, left = x > x_start;445 const int right = x < w - 1, down = y < h - 1, left = x > x_start;
440 const int color = get_dst_color_err(s, src[x], &er, &eg, &eb, search_method);446 const int color = get_dst_color_err(s, src[x], &ea, &er, &eg, &eb, search_method);
441 447
442 if (color < 0)448 if (color < 0)
443 return color;449 return color;
@@ -451,7 +457,7 @@ static av_always_inline int set_frame(PaletteUseContext *s, AVFrame *out, AVFram · 7 unmodified lines
451 } else if (dither == DITHERING_SIERRA2) {457 } else if (dither == DITHERING_SIERRA2) {
452 const int right = x < w - 1, down = y < h - 1, left = x > x_start;458 const int right = x < w - 1, down = y < h - 1, left = x > x_start;
453 const int right2 = x < w - 2, left2 = x > x_start + 1;459 const int right2 = x < w - 2, left2 = x > x_start + 1;
454 const int color = get_dst_color_err(s, src[x], &er, &eg, &eb, search_method);460 const int color = get_dst_color_err(s, src[x], &ea, &er, &eg, &eb, search_method);
455 461
456 if (color < 0)462 if (color < 0)
457 return color;463 return color;
@@ -470,7 +476,7 @@ static av_always_inline int set_frame(PaletteUseContext *s, AVFrame *out, AVFram · 12 unmodified lines
470 476
471 } else if (dither == DITHERING_SIERRA2_4A) {477 } else if (dither == DITHERING_SIERRA2_4A) {
472 const int right = x < w - 1, down = y < h - 1, left = x > x_start;478 const int right = x < w - 1, down = y < h - 1, left = x > x_start;
473 const int color = get_dst_color_err(s, src[x], &er, &eg, &eb, search_method);479 const int color = get_dst_color_err(s, src[x], &ea, &er, &eg, &eb, search_method);
474 480
475 if (color < 0)481 if (color < 0)
476 return color;482 return color;
@@ -553,8 +559,7 @@ static int disp_tree(const struct color_node *node, const char *fname) · 76 unmodified lines
553 return 0;559 return 0;
554}560}
555 561
556static int debug_accuracy(const struct color_node *node, const uint32_t *palette, const int trans_thresh,562static int debug_accuracy(const PaletteUseContext *s)
557 const enum color_search_method search_method)
558{563{
559 int r, g, b, ret = 0;564 int r, g, b, ret = 0;
560 565
@@ -562,19 +567,26 @@ static int debug_accuracy(const struct color_node *node, const uint32_t *palette · 1 unmodified line
562 for (g = 0; g < 256; g++) {567 for (g = 0; g < 256; g++) {
563 for (b = 0; b < 256; b++) {568 for (b = 0; b < 256; b++) {
564 const uint8_t argb[] = {0xff, r, g, b};569 const uint8_t argb[] = {0xff, r, g, b};
565 const int r1 = COLORMAP_NEAREST(search_method, palette, node, argb, trans_thresh);570 const int r1 = COLORMAP_NEAREST(s, s->color_search_method, s->map, argb);
566 const int r2 = colormap_nearest_bruteforce(palette, argb, trans_thresh);571 const int r2 = colormap_nearest_bruteforce(s, argb);
567 if (r1 != r2) {572 if (r1 != r2) {
568 const uint32_t c1 = palette[r1];573 const uint32_t c1 = s->palette[r1];
569 const uint32_t c2 = palette[r2];574 const uint32_t c2 = s->palette[r2];
570 const uint8_t palargb1[] = { 0xff, c1>>16 & 0xff, c1>> 8 & 0xff, c1 & 0xff };575 const uint8_t a1 = s->use_alpha ? c1>>24 & 0xff : 0xff;
571 const uint8_t palargb2[] = { 0xff, c2>>16 & 0xff, c2>> 8 & 0xff, c2 & 0xff };576 const uint8_t a2 = s->use_alpha ? c2>>24 & 0xff : 0xff;
572 const int d1 = diff(palargb1, argb, trans_thresh);577 const uint8_t palargb1[] = { a1, c1>>16 & 0xff, c1>> 8 & 0xff, c1 & 0xff };
573 const int d2 = diff(palargb2, argb, trans_thresh);578 const uint8_t palargb2[] = { a2, c2>>16 & 0xff, c2>> 8 & 0xff, c2 & 0xff };
579 const int d1 = diff(palargb1, argb, s);
580 const int d2 = diff(palargb2, argb, s);
574 if (d1 != d2) {581 if (d1 != d2) {
575 av_log(NULL, AV_LOG_ERROR,582 if (s->use_alpha)
576 "/!\\ %02X%02X%02X: %d ! %d (%06"PRIX32" ! %06"PRIX32") / dist: %d ! %d\n",583 av_log(NULL, AV_LOG_ERROR,
577 r, g, b, r1, r2, c1 & 0xffffff, c2 & 0xffffff, d1, d2);584 "/!\\ %02X%02X%02X: %d ! %d (%08"PRIX32" ! %08"PRIX32") / dist: %d ! %d\n",
585 r, g, b, r1, r2, c1, c2, d1, d2);
586 else
587 av_log(NULL, AV_LOG_ERROR,
588 "/!\\ %02X%02X%02X: %d ! %d (%06"PRIX32" ! %06"PRIX32") / dist: %d ! %d\n",
589 r, g, b, r1, r2, c1 & 0xffffff, c2 & 0xffffff, d1, d2);
578 ret = 1;590 ret = 1;
579 }591 }
580 }592 }
@@ -590,8 +602,8 @@ struct color { · 9 unmodified lines
590};602};
591 603
592struct color_rect {604struct color_rect {
593 uint8_t min[3];605 uint8_t min[4];
594 uint8_t max[3];606 uint8_t max[4];
595};607};
596 608
597typedef int (*cmp_func)(const void *, const void *);609typedef int (*cmp_func)(const void *, const void *);
@@ -612,43 +624,47 @@ DECLARE_CMP_FUNC(b, 3) · 14 unmodified lines
612 624
613static const cmp_func cmp_funcs[] = {cmp_a, cmp_r, cmp_g, cmp_b};625static const cmp_func cmp_funcs[] = {cmp_a, cmp_r, cmp_g, cmp_b};
614 626
615static int get_next_color(const uint8_t *color_used, const uint32_t *palette,627static int get_next_color(const uint8_t *color_used, const PaletteUseContext *s,
616 const int trans_thresh,
617 int *component, const struct color_rect *box)628 int *component, const struct color_rect *box)
618{629{
619 int wr, wg, wb;630 int wa, wr, wg, wb;
620 int i, longest = 0;631 int i, longest = 0;
621 unsigned nb_color = 0;632 unsigned nb_color = 0;
622 struct color_rect ranges;633 struct color_rect ranges;
623 struct color tmp_pal[256];634 struct color tmp_pal[256];
624 cmp_func cmpf;635 cmp_func cmpf;
625 636
626 ranges.min[0] = ranges.min[1] = ranges.min[2] = 0xff;637 ranges.min[0] = ranges.min[1] = ranges.min[2] = ranges.min[3]= 0xff;
627 ranges.max[0] = ranges.max[1] = ranges.max[2] = 0x00;638 ranges.max[0] = ranges.max[1] = ranges.max[2] = ranges.max[3]= 0x00;
628 639
629 for (i = 0; i < AVPALETTE_COUNT; i++) {640 for (i = 0; i < AVPALETTE_COUNT; i++) {
630 const uint32_t c = palette[i];641 const uint32_t c = s->palette[i];
631 const uint8_t a = c >> 24 & 0xff;642 const uint8_t a = c >> 24 & 0xff;
632 const uint8_t r = c >> 16 & 0xff;643 const uint8_t r = c >> 16 & 0xff;
633 const uint8_t g = c >> 8 & 0xff;644 const uint8_t g = c >> 8 & 0xff;
634 const uint8_t b = c & 0xff;645 const uint8_t b = c & 0xff;
635 646
636 if (a < trans_thresh) {647 if (!s->use_alpha && a < s->trans_thresh) {
637 continue;648 continue;
638 }649 }
639 650
640 if (color_used[i] || (a != 0xff) ||651 if (color_used[i] || (a != 0xff && !s->use_alpha) ||
641 r < box->min[0] || g < box->min[1] || b < box->min[2] ||652 r < box->min[1] || g < box->min[2] || b < box->min[3] ||
642 r > box->max[0] || g > box->max[1] || b > box->max[2])653 r > box->max[1] || g > box->max[2] || b > box->max[3])
643 continue;654 continue;
644 655
645 if (r < ranges.min[0]) ranges.min[0] = r;656 if (s->use_alpha && (a < box->min[0] || a > box->max[0]))
646 if (g < ranges.min[1]) ranges.min[1] = g;657 continue;
647 if (b < ranges.min[2]) ranges.min[2] = b;658
659 if (a < ranges.min[0]) ranges.min[0] = a;
660 if (r < ranges.min[1]) ranges.min[1] = r;
661 if (g < ranges.min[2]) ranges.min[2] = g;
662 if (b < ranges.min[3]) ranges.min[3] = b;
648 663
649 if (r > ranges.max[0]) ranges.max[0] = r;664 if (a > ranges.max[0]) ranges.max[0] = a;
650 if (g > ranges.max[1]) ranges.max[1] = g;665 if (r > ranges.max[1]) ranges.max[1] = r;
651 if (b > ranges.max[2]) ranges.max[2] = b;666 if (g > ranges.max[2]) ranges.max[2] = g;
667 if (b > ranges.max[3]) ranges.max[3] = b;
652 668
653 tmp_pal[nb_color].value = c;669 tmp_pal[nb_color].value = c;
654 tmp_pal[nb_color].pal_id = i;670 tmp_pal[nb_color].pal_id = i;
@@ -660,12 +676,22 @@ static int get_next_color(const uint8_t *color_used, const uint32_t *palette, · 5 unmodified lines
660 return -1;676 return -1;
661 677
662 /* define longest axis that will be the split component */678 /* define longest axis that will be the split component */
663 wr = ranges.max[0] - ranges.min[0];679 wa = ranges.max[0] - ranges.min[0];
664 wg = ranges.max[1] - ranges.min[1];680 wr = ranges.max[1] - ranges.min[1];
665 wb = ranges.max[2] - ranges.min[2];681 wg = ranges.max[2] - ranges.min[2];
666 if (wr >= wg && wr >= wb) longest = 1;682 wb = ranges.max[3] - ranges.min[3];
667 if (wg >= wr && wg >= wb) longest = 2;683
668 if (wb >= wr && wb >= wg) longest = 3;684 if (s->use_alpha) {
685 if (wa >= wr && wa >= wb && wa >= wg) longest = 0;
686 if (wr >= wg && wr >= wb && wr >= wa) longest = 1;
687 if (wg >= wr && wg >= wb && wg >= wa) longest = 2;
688 if (wb >= wr && wb >= wg && wb >= wa) longest = 3;
689 } else {
690 if (wr >= wg && wr >= wb) longest = 1;
691 if (wg >= wr && wg >= wb) longest = 2;
692 if (wb >= wr && wb >= wg) longest = 3;
693 }
694
669 cmpf = cmp_funcs[longest];695 cmpf = cmp_funcs[longest];
670 *component = longest;696 *component = longest;
671 697
@@ -678,8 +704,7 @@ static int get_next_color(const uint8_t *color_used, const uint32_t *palette, · 6 unmodified lines
678static int colormap_insert(struct color_node *map,704static int colormap_insert(struct color_node *map,
679 uint8_t *color_used,705 uint8_t *color_used,
680 int *nb_used,706 int *nb_used,
681 const uint32_t *palette,707 const PaletteUseContext *s,
682 const int trans_thresh,
683 const struct color_rect *box)708 const struct color_rect *box)
684{709{
685 uint32_t c;710 uint32_t c;
@@ -687,14 +712,14 @@ static int colormap_insert(struct color_node *map, · 1 unmodified line
687 int node_left_id = -1, node_right_id = -1;712 int node_left_id = -1, node_right_id = -1;
688 struct color_node *node;713 struct color_node *node;
689 struct color_rect box1, box2;714 struct color_rect box1, box2;
690 const int pal_id = get_next_color(color_used, palette, trans_thresh, &component, box);715 const int pal_id = get_next_color(color_used, s, &component, box);
691 716
692 if (pal_id < 0)717 if (pal_id < 0)
693 return -1;718 return -1;
694 719
695 /* create new node with that color */720 /* create new node with that color */
696 cur_id = (*nb_used)++;721 cur_id = (*nb_used)++;
697 c = palette[pal_id];722 c = s->palette[pal_id];
698 node = &map[cur_id];723 node = &map[cur_id];
699 node->split = component;724 node->split = component;
700 node->palette_id = pal_id;725 node->palette_id = pal_id;
@@ -707,13 +732,13 @@ static int colormap_insert(struct color_node *map, · 6 unmodified lines
707 732
708 /* get the two boxes this node creates */733 /* get the two boxes this node creates */
709 box1 = box2 = *box;734 box1 = box2 = *box;
710 box1.max[component-1] = node->val[component];735 box1.max[component] = node->val[component];
711 box2.min[component-1] = FFMIN(node->val[component] + 1, 255);736 box2.min[component] = FFMIN(node->val[component] + 1, 255);
712 737
713 node_left_id = colormap_insert(map, color_used, nb_used, palette, trans_thresh, &box1);738 node_left_id = colormap_insert(map, color_used, nb_used, s, &box1);
714 739
715 if (box2.min[component-1] <= box2.max[component-1])740 if (box2.min[component] <= box2.max[component])
716 node_right_id = colormap_insert(map, color_used, nb_used, palette, trans_thresh, &box2);741 node_right_id = colormap_insert(map, color_used, nb_used, s, &box2);
717 742
718 node->left_id = node_left_id;743 node->left_id = node_left_id;
719 node->right_id = node_right_id;744 node->right_id = node_right_id;
@@ -728,6 +753,13 @@ static int cmp_pal_entry(const void *a, const void *b) · 8 unmodified lines
728 return c1 - c2;753 return c1 - c2;
729}754}
730 755
756static int cmp_pal_entry_alpha(const void *a, const void *b)
757{
758 const int c1 = *(const uint32_t *)a;
759 const int c2 = *(const uint32_t *)b;
760 return c1 - c2;
761}
762
731static void load_colormap(PaletteUseContext *s)763static void load_colormap(PaletteUseContext *s)
732{764{
733 int i, nb_used = 0;765 int i, nb_used = 0;
@@ -735,12 +767,13 @@ static void load_colormap(PaletteUseContext *s) · 1 unmodified line
735 uint32_t last_color = 0;767 uint32_t last_color = 0;
736 struct color_rect box;768 struct color_rect box;
737 769
738 if (s->transparency_index >= 0) {770 if (!s->use_alpha && s->transparency_index >= 0) {
739 FFSWAP(uint32_t, s->palette[s->transparency_index], s->palette[255]);771 FFSWAP(uint32_t, s->palette[s->transparency_index], s->palette[255]);
740 }772 }
741 773
742 /* disable transparent colors and dups */774 /* disable transparent colors and dups */
743 qsort(s->palette, AVPALETTE_COUNT-(s->transparency_index >= 0), sizeof(*s->palette), cmp_pal_entry);775 qsort(s->palette, AVPALETTE_COUNT-(s->transparency_index >= 0), sizeof(*s->palette),
776 s->use_alpha ? cmp_pal_entry_alpha : cmp_pal_entry);
744 777
745 for (i = 0; i < AVPALETTE_COUNT; i++) {778 for (i = 0; i < AVPALETTE_COUNT; i++) {
746 const uint32_t c = s->palette[i];779 const uint32_t c = s->palette[i];
@@ -749,22 +782,22 @@ static void load_colormap(PaletteUseContext *s) · 2 unmodified lines
749 continue;782 continue;
750 }783 }
751 last_color = c;784 last_color = c;
752 if (c >> 24 < s->trans_thresh) {785 if (!s->use_alpha && c >> 24 < s->trans_thresh) {
753 color_used[i] = 1; // ignore transparent color(s)786 color_used[i] = 1; // ignore transparent color(s)
754 continue;787 continue;
755 }788 }
756 }789 }
757 790
758 box.min[0] = box.min[1] = box.min[2] = 0x00;791 box.min[0] = box.min[1] = box.min[2] = box.min[3] = 0x00;
759 box.max[0] = box.max[1] = box.max[2] = 0xff;792 box.max[0] = box.max[1] = box.max[2] = box.max[3] = 0xff;
760 793
761 colormap_insert(s->map, color_used, &nb_used, s->palette, s->trans_thresh, &box);794 colormap_insert(s->map, color_used, &nb_used, s, &box);
762 795
763 if (s->dot_filename)796 if (s->dot_filename)
764 disp_tree(s->map, s->dot_filename);797 disp_tree(s->map, s->dot_filename);
765 798
766 if (s->debug_accuracy) {799 if (s->debug_accuracy) {
767 if (!debug_accuracy(s->map, s->palette, s->trans_thresh, s->color_search_method))800 if (!debug_accuracy(s))
768 av_log(NULL, AV_LOG_INFO, "Accuracy check passed\n");801 av_log(NULL, AV_LOG_INFO, "Accuracy check passed\n");
769 }802 }
770}803}
@@ -778,16 +811,18 @@ static void debug_mean_error(PaletteUseContext *s, const AVFrame *in1, · 7 unmodified lines
778 uint8_t *src2 = in2->data[0];811 uint8_t *src2 = in2->data[0];
779 const int src1_linesize = in1->linesize[0] >> 2;812 const int src1_linesize = in1->linesize[0] >> 2;
780 const int src2_linesize = in2->linesize[0];813 const int src2_linesize = in2->linesize[0];
781 const float div = in1->width * in1->height * 3;814 const float div = in1->width * in1->height * s->use_alpha ? 4 : 3;
782 unsigned mean_err = 0;815 unsigned mean_err = 0;
783 816
784 for (y = 0; y < in1->height; y++) {817 for (y = 0; y < in1->height; y++) {
785 for (x = 0; x < in1->width; x++) {818 for (x = 0; x < in1->width; x++) {
786 const uint32_t c1 = src1[x];819 const uint32_t c1 = src1[x];
787 const uint32_t c2 = palette[src2[x]];820 const uint32_t c2 = palette[src2[x]];
788 const uint8_t argb1[] = {0xff, c1 >> 16 & 0xff, c1 >> 8 & 0xff, c1 & 0xff};821 const uint8_t a1 = s->use_alpha ? c1>>24 & 0xff : 0xff;
789 const uint8_t argb2[] = {0xff, c2 >> 16 & 0xff, c2 >> 8 & 0xff, c2 & 0xff};822 const uint8_t a2 = s->use_alpha ? c2>>24 & 0xff : 0xff;
790 mean_err += diff(argb1, argb2, s->trans_thresh);823 const uint8_t argb1[] = {a1, c1 >> 16 & 0xff, c1 >> 8 & 0xff, c1 & 0xff};
824 const uint8_t argb2[] = {a2, c2 >> 16 & 0xff, c2 >> 8 & 0xff, c2 & 0xff};
825 mean_err += diff(argb1, argb2, s);
791 }826 }
792 src1 += src1_linesize;827 src1 += src1_linesize;
793 src2 += src2_linesize;828 src2 += src2_linesize;
@@ -987,7 +1022,7 @@ static void load_palette(PaletteUseContext *s, const AVFrame *palette_frame) · 193 unmodified lines
987 for (y = 0; y < palette_frame->height; y++) {1022 for (y = 0; y < palette_frame->height; y++) {
988 for (x = 0; x < palette_frame->width; x++) {1023 for (x = 0; x < palette_frame->width; x++) {
989 s->palette[i] = p[x];1024 s->palette[i] = p[x];
990 if (p[x]>>24 < s->trans_thresh) {1025 if (!s->use_alpha && p[x]>>24 < s->trans_thresh) {
991 s->transparency_index = i; // we are assuming at most one transparent color in palette1026 s->transparency_index = i; // we are assuming at most one transparent color in palette
992 }1027 }
993 i++;1028 i++;

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t