Showing the changes to libavfilter (2 of 3 files). Show all
Unified · Split
| @@ -59,7 +59,7 @@ enum { · 58 unmodified lines | ||
| 59 | 59 | }; |
| 60 | 60 | |
| 61 | 61 | #define NBITS 5 |
| 62 | −#define HIST_SIZE (1<<(3*NBITS)) | |
| 62 | +#define HIST_SIZE (1<<(4*NBITS)) | |
| 63 | 63 | |
| 64 | 64 | typedef struct PaletteGenContext { |
| 65 | 65 | const AVClass *class; |
| @@ -67,6 +67,7 @@ typedef struct PaletteGenContext { · 1 unmodified line | ||
| 67 | 67 | int max_colors; |
| 68 | 68 | int reserve_transparent; |
| 69 | 69 | int stats_mode; |
| 70 | + int use_alpha; | |
| 70 | 71 | |
| 71 | 72 | AVFrame *prev_frame; // previous frame used for the diff stats_mode |
| 72 | 73 | struct hist_node histogram[HIST_SIZE]; // histogram/hashtable of the colors |
| @@ -88,6 +89,7 @@ static const AVOption palettegen_options[] = { · 15 unmodified lines | ||
| 88 | 89 | { "full", "compute full frame histograms", 0, AV_OPT_TYPE_CONST, {.i64=STATS_MODE_ALL_FRAMES}, INT_MIN, INT_MAX, FLAGS, "mode" }, |
| 89 | 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 | 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 | 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 | 116 | const struct color_ref * const *a = pa; \ |
| 115 | 117 | const struct color_ref * const *b = pb; \ |
| 116 | − return (int)((*a)->color >> (8 * (2 - (pos))) & 0xff) \ | |
| 117 | − - (int)((*b)->color >> (8 * (2 - (pos))) & 0xff); \ | |
| 118 | + return (int)((*a)->color >> (8 * (3 - (pos))) & 0xff) \ | |
| 119 | + - (int)((*b)->color >> (8 * (3 - (pos))) & 0xff); \ | |
| 118 | 120 | } |
| 119 | 121 | |
| 120 | −DECLARE_CMP_FUNC(r, 0) | |
| 121 | −DECLARE_CMP_FUNC(g, 1) | |
| 122 | −DECLARE_CMP_FUNC(b, 2) | |
| 122 | +DECLARE_CMP_FUNC(a, 0) | |
| 123 | +DECLARE_CMP_FUNC(r, 1) | |
| 124 | +DECLARE_CMP_FUNC(g, 2) | |
| 125 | +DECLARE_CMP_FUNC(b, 3) | |
| 123 | 126 | |
| 124 | −static const cmp_func cmp_funcs[] = {cmp_r, cmp_g, cmp_b}; | |
| 127 | +static const cmp_func cmp_funcs[] = {cmp_a, cmp_r, cmp_g, cmp_b}; | |
| 125 | 128 | |
| 126 | 129 | /** |
| 127 | 130 | * 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 | 146 | return dr*dr + dg*dg + db*db; |
| 144 | 147 | } |
| 145 | 148 | |
| 149 | +static 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 | 161 | * 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 | 179 | for (i = 0; i < box->len; i++) { |
| 166 | 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 | 186 | box->variance = variance; |
| 170 | 187 | } |
| @@ -184,24 +201,31 @@ static int get_next_box_id_to_split(PaletteGenContext *s) · 13 unmodified lines | ||
| 184 | 201 | * specified box. Takes into account the weight of each color. |
| 185 | 202 | */ |
| 186 | 203 | static 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 | 206 | int i; |
| 190 | 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 | 210 | for (i = 0; i < n; i++) { |
| 194 | 211 | const struct color_ref *ref = refs[box->start + i]; |
| 195 | − r += (ref->color >> 16 & 0xff) * ref->count; | |
| 196 | − g += (ref->color >> 8 & 0xff) * ref->count; | |
| 197 | − b += (ref->color & 0xff) * ref->count; | |
| 212 | + if (use_alpha) | |
| 213 | + a += (ref->color >> 24 & 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 | 217 | div += ref->count; |
| 199 | 218 | } |
| 200 | 219 | |
| 220 | + if (use_alpha) | |
| 221 | + a = a / div; | |
| 201 | 222 | r = r / div; |
| 202 | 223 | g = g / div; |
| 203 | 224 | b = b / div; |
| 204 | 225 | |
| 226 | + if (use_alpha) | |
| 227 | + return a<<24 | r<<16 | g<<8 | b; | |
| 228 | + | |
| 205 | 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 | 244 | av_assert0(box->len >= 1); |
| 221 | 245 | av_assert0(new_box->len >= 1); |
| 222 | 246 | |
| 223 | − box->color = get_avg_color(s->refs, box); | |
| 224 | − new_box->color = get_avg_color(s->refs, new_box); | |
| 247 | + box->color = get_avg_color(s->refs, box, s->use_alpha); | |
| 248 | + new_box->color = get_avg_color(s->refs, new_box, s->use_alpha); | |
| 225 | 249 | box->variance = -1; |
| 226 | 250 | new_box->variance = -1; |
| 227 | 251 | } |
| @@ -251,7 +275,7 @@ static void write_palette(AVFilterContext *ctx, AVFrame *out) · 23 unmodified lines | ||
| 251 | 275 | pal += pal_linesize; |
| 252 | 276 | } |
| 253 | 277 | |
| 254 | − if (s->reserve_transparent) { | |
| 278 | + if (s->reserve_transparent && !s->use_alpha) { | |
| 255 | 279 | av_assert0(s->nb_boxes < 256); |
| 256 | 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 | 343 | box = &s->boxes[box_id]; |
| 320 | 344 | box->len = s->nb_refs; |
| 321 | 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 | 347 | box->variance = -1; |
| 324 | 348 | s->nb_boxes = 1; |
| 325 | 349 | |
| 326 | 350 | while (box && box->len > 1) { |
| 327 | − int i, rr, gr, br, longest; | |
| 351 | + int i, ar, rr, gr, br, longest; | |
| 328 | 352 | uint64_t median, box_weight = 0; |
| 329 | 353 | |
| 330 | 354 | /* compute the box weight (sum all the weights of the colors in the |
| 331 | 355 | * range) and its boundings */ |
| 332 | − uint8_t min[3] = {0xff, 0xff, 0xff}; | |
| 333 | − uint8_t max[3] = {0x00, 0x00, 0x00}; | |
| 356 | + uint8_t min[4] = {0xff, 0xff, 0xff, 0xff}; | |
| 357 | + uint8_t max[4] = {0x00, 0x00, 0x00, 0x00}; | |
| 334 | 358 | for (i = box->start; i < box->start + box->len; i++) { |
| 335 | 359 | const struct color_ref *ref = s->refs[i]; |
| 336 | 360 | const uint32_t rgb = ref->color; |
| 337 | − const uint8_t r = rgb >> 16 & 0xff, g = rgb >> 8 & 0xff, b = rgb & 0xff; | |
| 338 | − min[0] = FFMIN(r, min[0]), max[0] = FFMAX(r, max[0]); | |
| 339 | − min[1] = FFMIN(g, min[1]), max[1] = FFMAX(g, max[1]); | |
| 340 | − min[2] = FFMIN(b, min[2]), max[2] = FFMAX(b, max[2]); | |
| 361 | + const uint8_t a = rgb >> 24 & 0xff, r = rgb >> 16 & 0xff, g = rgb >> 8 & 0xff, b = rgb & 0xff; | |
| 362 | + min[0] = FFMIN(a, min[0]); max[0] = FFMAX(a, max[0]); | |
| 363 | + min[1] = FFMIN(r, min[1]); max[1] = FFMAX(r, max[1]); | |
| 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 | 366 | box_weight += ref->count; |
| 342 | 367 | } |
| 343 | 368 | |
| 344 | 369 | /* define the axis to sort by according to the widest range of colors */ |
| 345 | − rr = max[0] - min[0]; | |
| 346 | − gr = max[1] - min[1]; | |
| 347 | − br = max[2] - min[2]; | |
| 348 | − longest = 1; // pick green by default (the color the eye is the most sensitive to) | |
| 349 | − if (br >= rr && br >= gr) longest = 2; | |
| 350 | − if (rr >= gr && rr >= br) longest = 0; | |
| 351 | − if (gr >= rr && gr >= br) longest = 1; // prefer green again | |
| 352 | − | |
| 353 | − ff_dlog(ctx, "box #%02X [%6d..%-6d] (%6d) w:%-6"PRIu64" ranges:[%2x %2x %2x] sort by %c (already sorted:%c) ", | |
| 370 | + ar = max[0] - min[0]; | |
| 371 | + rr = max[1] - min[1]; | |
| 372 | + gr = max[2] - min[2]; | |
| 373 | + br = max[3] - min[3]; | |
| 374 | + longest = 2; // pick green by default (the color the eye is the most sensitive to) | |
| 375 | + if (s->use_alpha) { | |
| 376 | + if (ar >= rr && ar >= br && ar >= gr) longest = 0; | |
| 377 | + if (br >= rr && br >= gr && br >= ar) longest = 3; | |
| 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 | 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 | 390 | /* sort the range by its longest axis if it's not already sorted */ |
| 358 | 391 | if (box->sorted_by != longest) { |
| @@ -394,21 +427,27 @@ static AVFrame *get_palette_frame(AVFilterContext *ctx) · 35 unmodified lines | ||
| 394 | 427 | * It keeps the NBITS least significant bit of each component to make it |
| 395 | 428 | * "random" even if the scene doesn't have much different colors. |
| 396 | 429 | */ |
| 397 | −static inline unsigned color_hash(uint32_t color) | |
| 430 | +static inline unsigned color_hash(uint32_t color, int use_alpha) | |
| 398 | 431 | { |
| 399 | 432 | const uint8_t r = color >> 16 & ((1<<NBITS)-1); |
| 400 | 433 | const uint8_t g = color >> 8 & ((1<<NBITS)-1); |
| 401 | 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 | 441 | return r<<(NBITS*2) | g<<NBITS | b; |
| 403 | 442 | } |
| 404 | 443 | |
| 405 | 444 | /** |
| 406 | 445 | * Locate the color in the hash table and increment its counter. |
| 407 | 446 | */ |
| 408 | −static int color_inc(struct hist_node *hist, uint32_t color) | |
| 447 | +static int color_inc(struct hist_node *hist, uint32_t color, int use_alpha) | |
| 409 | 448 | { |
| 410 | 449 | int i; |
| 411 | − const unsigned hash = color_hash(color); | |
| 450 | + const unsigned hash = color_hash(color, use_alpha); | |
| 412 | 451 | struct hist_node *node = &hist[hash]; |
| 413 | 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 | 472 | * Update histogram when pixels differ from previous frame. |
| 434 | 473 | */ |
| 435 | 474 | static 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 | 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 | 483 | for (x = 0; x < f1->width; x++) { |
| 445 | 484 | if (p[x] == q[x]) |
| 446 | 485 | continue; |
| 447 | − ret = color_inc(hist, p[x]); | |
| 486 | + ret = color_inc(hist, p[x], use_alpha); | |
| 448 | 487 | if (ret < 0) |
| 449 | 488 | return ret; |
| 450 | 489 | nb_diff_colors += ret; |
| @@ -456,7 +495,7 @@ static int update_histogram_diff(struct hist_node *hist, · 5 unmodified lines | ||
| 456 | 495 | /** |
| 457 | 496 | * Simple histogram of the frame. |
| 458 | 497 | */ |
| 459 | −static int update_histogram_frame(struct hist_node *hist, const AVFrame *f) | |
| 498 | +static int update_histogram_frame(struct hist_node *hist, const AVFrame *f, int use_alpha) | |
| 460 | 499 | { |
| 461 | 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 | 503 | const uint32_t *p = (const uint32_t *)(f->data[0] + y*f->linesize[0]); |
| 465 | 504 | |
| 466 | 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 | 507 | if (ret < 0) |
| 469 | 508 | return ret; |
| 470 | 509 | nb_diff_colors += ret; |
| @@ -480,8 +519,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) · 9 unmodified lines | ||
| 480 | 519 | { |
| 481 | 520 | AVFilterContext *ctx = inlink->dst; |
| 482 | 521 | PaletteGenContext *s = ctx->priv; |
| 483 | − int ret = s->prev_frame ? update_histogram_diff(s->histogram, s->prev_frame, in) | |
| 484 | − : update_histogram_frame(s->histogram, in); | |
| 522 | + int ret = s->prev_frame ? update_histogram_diff(s->histogram, s->prev_frame, in, s->use_alpha) | |
| 523 | + : update_histogram_frame(s->histogram, in, s->use_alpha); | |
| 485 | 524 | |
| 486 | 525 | if (ret > 0) |
| 487 | 526 | s->nb_refs += ret; |
| @@ -540,6 +579,16 @@ static int config_output(AVFilterLink *outlink) · 52 unmodified lines | ||
| 540 | 579 | return 0; |
| 541 | 580 | } |
| 542 | 581 | |
| 582 | +static 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 | + | |
| 543 | 592 | static av_cold void uninit(AVFilterContext *ctx) |
| 544 | 593 | { |
| 545 | 594 | int i; |
| @@ -572,6 +621,7 @@ const AVFilter ff_vf_palettegen = { · 26 unmodified lines | ||
| 572 | 621 | .name = "palettegen", |
| 573 | 622 | .description = NULL_IF_CONFIG_SMALL("Find the optimal palette for a given stream."), |
| 574 | 623 | .priv_size = sizeof(PaletteGenContext), |
| 624 | + .init = init, | |
| 575 | 625 | .uninit = uninit, |
| 576 | 626 | FILTER_INPUTS(palettegen_inputs), |
| 577 | 627 | FILTER_OUTPUTS(palettegen_outputs), |
| @@ -28,7 +28,6 @@ · 27 unmodified lines | ||
| 28 | 28 | #include "libavutil/opt.h" |
| 29 | 29 | #include "libavutil/qsort.h" |
| 30 | 30 | #include "avfilter.h" |
| 31 | −#include "filters.h" | |
| 32 | 31 | #include "framesync.h" |
| 33 | 32 | #include "internal.h" |
| 34 | 33 | |
| @@ -63,7 +62,7 @@ struct color_node { · 28 unmodified lines | ||
| 63 | 62 | }; |
| 64 | 63 | |
| 65 | 64 | #define NBITS 5 |
| 66 | −#define CACHE_SIZE (1<<(3*NBITS)) | |
| 65 | +#define CACHE_SIZE (1<<(4*NBITS)) | |
| 67 | 66 | |
| 68 | 67 | struct cached_color { |
| 69 | 68 | uint32_t color; |
| @@ -88,6 +87,7 @@ typedef struct PaletteUseContext { · 18 unmodified lines | ||
| 88 | 87 | uint32_t palette[AVPALETTE_COUNT]; |
| 89 | 88 | int transparency_index; /* index in the palette of transparency. -1 if there is no transparency in the palette. */ |
| 90 | 89 | int trans_thresh; |
| 90 | + int use_alpha; | |
| 91 | 91 | int palette_loaded; |
| 92 | 92 | int dither; |
| 93 | 93 | int new; |
| @@ -107,7 +107,7 @@ typedef struct PaletteUseContext { · 13 unmodified lines | ||
| 107 | 107 | } PaletteUseContext; |
| 108 | 108 | |
| 109 | 109 | #define OFFSET(x) offsetof(PaletteUseContext, x) |
| 110 | −#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM | |
| 110 | +#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) | |
| 111 | 111 | static const AVOption paletteuse_options[] = { |
| 112 | 112 | { "dither", "select dithering mode", OFFSET(dither), AV_OPT_TYPE_INT, {.i64=DITHERING_SIERRA2_4A}, 0, NB_DITHERING-1, FLAGS, "dithering_mode" }, |
| 113 | 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 | 120 | { "rectangle", "process smallest different rectangle", 0, AV_OPT_TYPE_CONST, {.i64=DIFF_MODE_RECTANGLE}, INT_MIN, INT_MAX, FLAGS, "diff_mode" }, |
| 121 | 121 | { "new", "take new palette for each output frame", OFFSET(new), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS }, |
| 122 | 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 | 125 | /* following are the debug options, not part of the official API */ |
| 125 | 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 | 162 | | av_clip_uint8((px & 0xff) + ((eb * scale) / (1<<shift))); |
| 162 | 163 | } |
| 163 | 164 | |
| 164 | −static av_always_inline int diff(const uint8_t *c1, const uint8_t *c2, const int trans_thresh) | |
| 165 | +static av_always_inline int diff(const uint8_t *c1, const uint8_t *c2, const PaletteUseContext *s) | |
| 165 | 166 | { |
| 166 | 167 | // XXX: try L*a*b with CIE76 (dL*dL + da*da + db*db) |
| 168 | + const int da = c1[0] - c2[0]; | |
| 167 | 169 | const int dr = c1[1] - c2[1]; |
| 168 | 170 | const int dg = c1[2] - c2[2]; |
| 169 | 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 | 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 | 179 | return dr*dr + dg*dg + db*db; |
| 175 | 180 | } else { |
| 176 | 181 | return 255*255 + 255*255 + 255*255; |
| 177 | 182 | } |
| 178 | 183 | } |
| 179 | 184 | |
| 180 | −static av_always_inline uint8_t colormap_nearest_bruteforce(const uint32_t *palette, const uint8_t *argb, const int trans_thresh) | |
| 185 | +static av_always_inline uint8_t colormap_nearest_bruteforce(const PaletteUseContext *s, const uint8_t *argb) | |
| 181 | 186 | { |
| 182 | 187 | int i, pal_id = -1, min_dist = INT_MAX; |
| 183 | 188 | |
| 184 | 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 entry | |
| 192 | + if (s->use_alpha || c >> 24 >= s->trans_thresh) { // ignore transparent entry | |
| 188 | 193 | const uint8_t palargb[] = { |
| 189 | − palette[i]>>24 & 0xff, | |
| 190 | − palette[i]>>16 & 0xff, | |
| 191 | − palette[i]>> 8 & 0xff, | |
| 192 | − palette[i] & 0xff, | |
| 194 | + s->palette[i]>>24 & 0xff, | |
| 195 | + s->palette[i]>>16 & 0xff, | |
| 196 | + s->palette[i]>> 8 & 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 | 200 | if (d < min_dist) { |
| 196 | 201 | pal_id = i; |
| 197 | 202 | min_dist = d; |
| @@ -207,17 +212,17 @@ struct nearest_color { · 9 unmodified lines | ||
| 207 | 212 | int dist_sqd; |
| 208 | 213 | }; |
| 209 | 214 | |
| 210 | −static void colormap_nearest_node(const struct color_node *map, | |
| 215 | +static void colormap_nearest_node(const PaletteUseContext *s, | |
| 216 | + const struct color_node *map, | |
| 211 | 217 | const int node_pos, |
| 212 | 218 | const uint8_t *target, |
| 213 | − const int trans_thresh, | |
| 214 | 219 | struct nearest_color *nearest) |
| 215 | 220 | { |
| 216 | 221 | const struct color_node *kd = map + node_pos; |
| 217 | − const int s = kd->split; | |
| 222 | + const int split = kd->split; | |
| 218 | 223 | int dx, nearer_kd_id, further_kd_id; |
| 219 | 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 | 227 | if (current_to_target < nearest->dist_sqd) { |
| 223 | 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 | 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 | 235 | if (dx <= 0) nearer_kd_id = kd->left_id, further_kd_id = kd->right_id; |
| 231 | 236 | else nearer_kd_id = kd->right_id, further_kd_id = kd->left_id; |
| 232 | 237 | |
| 233 | 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 | 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 | |
| 241 | −static av_always_inline uint8_t colormap_nearest_recursive(const struct color_node *node, const uint8_t *rgb, const int trans_thresh) | |
| 246 | +static av_always_inline uint8_t colormap_nearest_recursive(const PaletteUseContext *s, const struct color_node *node, const uint8_t *rgb) | |
| 242 | 247 | { |
| 243 | 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 | 250 | return node[res.node_pos].palette_id; |
| 246 | 251 | } |
| 247 | 252 | |
| @@ -250,7 +255,7 @@ struct stack_node { · 2 unmodified lines | ||
| 250 | 255 | int dx2; |
| 251 | 256 | }; |
| 252 | 257 | |
| 253 | −static av_always_inline uint8_t colormap_nearest_iterative(const struct color_node *root, const uint8_t *target, const int trans_thresh) | |
| 258 | +static av_always_inline uint8_t colormap_nearest_iterative(const PaletteUseContext *s, const struct color_node *root, const uint8_t *target) | |
| 254 | 259 | { |
| 255 | 260 | int pos = 0, best_node_id = -1, best_dist = INT_MAX, cur_color_id = 0; |
| 256 | 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 | 266 | const struct color_node *kd = &root[cur_color_id]; |
| 262 | 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 | 270 | /* Compare current color node to the target and update our best node if |
| 266 | 271 | * it's actually better. */ |
| @@ -322,10 +327,10 @@ end: · 55 unmodified lines | ||
| 322 | 327 | return root[best_node_id].palette_id; |
| 323 | 328 | } |
| 324 | 329 | |
| 325 | −#define COLORMAP_NEAREST(search, palette, root, target, trans_thresh) \ | |
| 326 | − search == COLOR_SEARCH_NNS_ITERATIVE ? colormap_nearest_iterative(root, target, trans_thresh) : \ | |
| 327 | − search == COLOR_SEARCH_NNS_RECURSIVE ? colormap_nearest_recursive(root, target, trans_thresh) : \ | |
| 328 | − colormap_nearest_bruteforce(palette, target, trans_thresh) | |
| 330 | +#define COLORMAP_NEAREST(s, search, root, target) \ | |
| 331 | + search == COLOR_SEARCH_NNS_ITERATIVE ? colormap_nearest_iterative(s, root, target) : \ | |
| 332 | + search == COLOR_SEARCH_NNS_RECURSIVE ? colormap_nearest_recursive(s, root, target) : \ | |
| 333 | + colormap_nearest_bruteforce(s, target) | |
| 329 | 334 | |
| 330 | 335 | /** |
| 331 | 336 | * 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 | 367 | if (!e) |
| 363 | 368 | return AVERROR(ENOMEM); |
| 364 | 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 | 372 | return e->pal_entry; |
| 368 | 373 | } |
| 369 | 374 | |
| 370 | 375 | static 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 | 377 | const enum color_search_method search_method) |
| 373 | 378 | { |
| 374 | 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 | 386 | return dstx; |
| 382 | 387 | dstc = s->palette[dstx]; |
| 383 | 388 | if (dstx == s->transparency_index) { |
| 384 | − *er = *eg = *eb = 0; | |
| 389 | + *ea =*er = *eg = *eb = 0; | |
| 385 | 390 | } else { |
| 391 | + *ea = (int)a - (int)(dstc >> 24 & 0xff); | |
| 386 | 392 | *er = (int)r - (int)(dstc >> 16 & 0xff); |
| 387 | 393 | *eg = (int)g - (int)(dstc >> 8 & 0xff); |
| 388 | 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 | 413 | for (y = y_start; y < h; y++) { |
| 408 | 414 | for (x = x_start; x < w; x++) { |
| 409 | − int er, eg, eb; | |
| 415 | + int ea, er, eg, eb; | |
| 410 | 416 | |
| 411 | 417 | if (dither == DITHERING_BAYER) { |
| 412 | 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 | 432 | } else if (dither == DITHERING_HECKBERT) { |
| 427 | 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 | 436 | if (color < 0) |
| 431 | 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 | 444 | } else if (dither == DITHERING_FLOYD_STEINBERG) { |
| 439 | 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 | 448 | if (color < 0) |
| 443 | 449 | return color; |
| @@ -451,7 +457,7 @@ static av_always_inline int set_frame(PaletteUseContext *s, AVFrame *out, AVFram · 7 unmodified lines | ||
| 451 | 457 | } else if (dither == DITHERING_SIERRA2) { |
| 452 | 458 | const int right = x < w - 1, down = y < h - 1, left = x > x_start; |
| 453 | 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 | 462 | if (color < 0) |
| 457 | 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 | 477 | } else if (dither == DITHERING_SIERRA2_4A) { |
| 472 | 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 | 481 | if (color < 0) |
| 476 | 482 | return color; |
| @@ -553,8 +559,7 @@ static int disp_tree(const struct color_node *node, const char *fname) · 76 unmodified lines | ||
| 553 | 559 | return 0; |
| 554 | 560 | } |
| 555 | 561 | |
| 556 | −static int debug_accuracy(const struct color_node *node, const uint32_t *palette, const int trans_thresh, | |
| 557 | − const enum color_search_method search_method) | |
| 562 | +static int debug_accuracy(const PaletteUseContext *s) | |
| 558 | 563 | { |
| 559 | 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 | 567 | for (g = 0; g < 256; g++) { |
| 563 | 568 | for (b = 0; b < 256; b++) { |
| 564 | 569 | const uint8_t argb[] = {0xff, r, g, b}; |
| 565 | − const int r1 = COLORMAP_NEAREST(search_method, palette, node, argb, trans_thresh); | |
| 566 | − const int r2 = colormap_nearest_bruteforce(palette, argb, trans_thresh); | |
| 570 | + const int r1 = COLORMAP_NEAREST(s, s->color_search_method, s->map, argb); | |
| 571 | + const int r2 = colormap_nearest_bruteforce(s, argb); | |
| 567 | 572 | if (r1 != r2) { |
| 568 | − const uint32_t c1 = palette[r1]; | |
| 569 | − const uint32_t c2 = palette[r2]; | |
| 570 | − const uint8_t palargb1[] = { 0xff, c1>>16 & 0xff, c1>> 8 & 0xff, c1 & 0xff }; | |
| 571 | − const uint8_t palargb2[] = { 0xff, c2>>16 & 0xff, c2>> 8 & 0xff, c2 & 0xff }; | |
| 572 | − const int d1 = diff(palargb1, argb, trans_thresh); | |
| 573 | − const int d2 = diff(palargb2, argb, trans_thresh); | |
| 573 | + const uint32_t c1 = s->palette[r1]; | |
| 574 | + const uint32_t c2 = s->palette[r2]; | |
| 575 | + const uint8_t a1 = s->use_alpha ? c1>>24 & 0xff : 0xff; | |
| 576 | + const uint8_t a2 = s->use_alpha ? c2>>24 & 0xff : 0xff; | |
| 577 | + const uint8_t palargb1[] = { a1, c1>>16 & 0xff, c1>> 8 & 0xff, c1 & 0xff }; | |
| 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 | 581 | if (d1 != d2) { |
| 575 | − av_log(NULL, AV_LOG_ERROR, | |
| 576 | − "/!\\ %02X%02X%02X: %d ! %d (%06"PRIX32" ! %06"PRIX32") / dist: %d ! %d\n", | |
| 577 | − r, g, b, r1, r2, c1 & 0xffffff, c2 & 0xffffff, d1, d2); | |
| 582 | + if (s->use_alpha) | |
| 583 | + av_log(NULL, AV_LOG_ERROR, | |
| 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 | 590 | ret = 1; |
| 579 | 591 | } |
| 580 | 592 | } |
| @@ -590,8 +602,8 @@ struct color { · 9 unmodified lines | ||
| 590 | 602 | }; |
| 591 | 603 | |
| 592 | 604 | struct color_rect { |
| 593 | − uint8_t min[3]; | |
| 594 | − uint8_t max[3]; | |
| 605 | + uint8_t min[4]; | |
| 606 | + uint8_t max[4]; | |
| 595 | 607 | }; |
| 596 | 608 | |
| 597 | 609 | typedef int (*cmp_func)(const void *, const void *); |
| @@ -612,43 +624,47 @@ DECLARE_CMP_FUNC(b, 3) · 14 unmodified lines | ||
| 612 | 624 | |
| 613 | 625 | static const cmp_func cmp_funcs[] = {cmp_a, cmp_r, cmp_g, cmp_b}; |
| 614 | 626 | |
| 615 | −static int get_next_color(const uint8_t *color_used, const uint32_t *palette, | |
| 616 | − const int trans_thresh, | |
| 627 | +static int get_next_color(const uint8_t *color_used, const PaletteUseContext *s, | |
| 617 | 628 | int *component, const struct color_rect *box) |
| 618 | 629 | { |
| 619 | − int wr, wg, wb; | |
| 630 | + int wa, wr, wg, wb; | |
| 620 | 631 | int i, longest = 0; |
| 621 | 632 | unsigned nb_color = 0; |
| 622 | 633 | struct color_rect ranges; |
| 623 | 634 | struct color tmp_pal[256]; |
| 624 | 635 | cmp_func cmpf; |
| 625 | 636 | |
| 626 | − ranges.min[0] = ranges.min[1] = ranges.min[2] = 0xff; | |
| 627 | − ranges.max[0] = ranges.max[1] = ranges.max[2] = 0x00; | |
| 637 | + ranges.min[0] = ranges.min[1] = ranges.min[2] = ranges.min[3]= 0xff; | |
| 638 | + ranges.max[0] = ranges.max[1] = ranges.max[2] = ranges.max[3]= 0x00; | |
| 628 | 639 | |
| 629 | 640 | for (i = 0; i < AVPALETTE_COUNT; i++) { |
| 630 | − const uint32_t c = palette[i]; | |
| 641 | + const uint32_t c = s->palette[i]; | |
| 631 | 642 | const uint8_t a = c >> 24 & 0xff; |
| 632 | 643 | const uint8_t r = c >> 16 & 0xff; |
| 633 | 644 | const uint8_t g = c >> 8 & 0xff; |
| 634 | 645 | const uint8_t b = c & 0xff; |
| 635 | 646 | |
| 636 | − if (a < trans_thresh) { | |
| 647 | + if (!s->use_alpha && a < s->trans_thresh) { | |
| 637 | 648 | continue; |
| 638 | 649 | } |
| 639 | 650 | |
| 640 | − if (color_used[i] || (a != 0xff) || | |
| 641 | − r < box->min[0] || g < box->min[1] || b < box->min[2] || | |
| 642 | − r > box->max[0] || g > box->max[1] || b > box->max[2]) | |
| 651 | + if (color_used[i] || (a != 0xff && !s->use_alpha) || | |
| 652 | + r < box->min[1] || g < box->min[2] || b < box->min[3] || | |
| 653 | + r > box->max[1] || g > box->max[2] || b > box->max[3]) | |
| 643 | 654 | continue; |
| 644 | 655 | |
| 645 | − if (r < ranges.min[0]) ranges.min[0] = r; | |
| 646 | − if (g < ranges.min[1]) ranges.min[1] = g; | |
| 647 | − if (b < ranges.min[2]) ranges.min[2] = b; | |
| 656 | + if (s->use_alpha && (a < box->min[0] || a > box->max[0])) | |
| 657 | + continue; | |
| 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; | |
| 650 | − if (g > ranges.max[1]) ranges.max[1] = g; | |
| 651 | − if (b > ranges.max[2]) ranges.max[2] = b; | |
| 664 | + if (a > ranges.max[0]) ranges.max[0] = a; | |
| 665 | + if (r > ranges.max[1]) ranges.max[1] = r; | |
| 666 | + if (g > ranges.max[2]) ranges.max[2] = g; | |
| 667 | + if (b > ranges.max[3]) ranges.max[3] = b; | |
| 652 | 668 | |
| 653 | 669 | tmp_pal[nb_color].value = c; |
| 654 | 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 | 676 | return -1; |
| 661 | 677 | |
| 662 | 678 | /* define longest axis that will be the split component */ |
| 663 | − wr = ranges.max[0] - ranges.min[0]; | |
| 664 | − wg = ranges.max[1] - ranges.min[1]; | |
| 665 | − wb = ranges.max[2] - ranges.min[2]; | |
| 666 | − if (wr >= wg && wr >= wb) longest = 1; | |
| 667 | − if (wg >= wr && wg >= wb) longest = 2; | |
| 668 | − if (wb >= wr && wb >= wg) longest = 3; | |
| 679 | + wa = ranges.max[0] - ranges.min[0]; | |
| 680 | + wr = ranges.max[1] - ranges.min[1]; | |
| 681 | + wg = ranges.max[2] - ranges.min[2]; | |
| 682 | + wb = ranges.max[3] - ranges.min[3]; | |
| 683 | + | |
| 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 | 695 | cmpf = cmp_funcs[longest]; |
| 670 | 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 | ||
| 678 | 704 | static int colormap_insert(struct color_node *map, |
| 679 | 705 | uint8_t *color_used, |
| 680 | 706 | int *nb_used, |
| 681 | − const uint32_t *palette, | |
| 682 | − const int trans_thresh, | |
| 707 | + const PaletteUseContext *s, | |
| 683 | 708 | const struct color_rect *box) |
| 684 | 709 | { |
| 685 | 710 | uint32_t c; |
| @@ -687,14 +712,14 @@ static int colormap_insert(struct color_node *map, · 1 unmodified line | ||
| 687 | 712 | int node_left_id = -1, node_right_id = -1; |
| 688 | 713 | struct color_node *node; |
| 689 | 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 | 717 | if (pal_id < 0) |
| 693 | 718 | return -1; |
| 694 | 719 | |
| 695 | 720 | /* create new node with that color */ |
| 696 | 721 | cur_id = (*nb_used)++; |
| 697 | − c = palette[pal_id]; | |
| 722 | + c = s->palette[pal_id]; | |
| 698 | 723 | node = &map[cur_id]; |
| 699 | 724 | node->split = component; |
| 700 | 725 | node->palette_id = pal_id; |
| @@ -707,13 +732,13 @@ static int colormap_insert(struct color_node *map, · 6 unmodified lines | ||
| 707 | 732 | |
| 708 | 733 | /* get the two boxes this node creates */ |
| 709 | 734 | box1 = box2 = *box; |
| 710 | − box1.max[component-1] = node->val[component]; | |
| 711 | − box2.min[component-1] = FFMIN(node->val[component] + 1, 255); | |
| 735 | + box1.max[component] = node->val[component]; | |
| 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]) | |
| 716 | − node_right_id = colormap_insert(map, color_used, nb_used, palette, trans_thresh, &box2); | |
| 740 | + if (box2.min[component] <= box2.max[component]) | |
| 741 | + node_right_id = colormap_insert(map, color_used, nb_used, s, &box2); | |
| 717 | 742 | |
| 718 | 743 | node->left_id = node_left_id; |
| 719 | 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 | 753 | return c1 - c2; |
| 729 | 754 | } |
| 730 | 755 | |
| 756 | +static 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 | + | |
| 731 | 763 | static void load_colormap(PaletteUseContext *s) |
| 732 | 764 | { |
| 733 | 765 | int i, nb_used = 0; |
| @@ -735,12 +767,13 @@ static void load_colormap(PaletteUseContext *s) · 1 unmodified line | ||
| 735 | 767 | uint32_t last_color = 0; |
| 736 | 768 | struct color_rect box; |
| 737 | 769 | |
| 738 | − if (s->transparency_index >= 0) { | |
| 770 | + if (!s->use_alpha && s->transparency_index >= 0) { | |
| 739 | 771 | FFSWAP(uint32_t, s->palette[s->transparency_index], s->palette[255]); |
| 740 | 772 | } |
| 741 | 773 | |
| 742 | 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 | 778 | for (i = 0; i < AVPALETTE_COUNT; i++) { |
| 746 | 779 | const uint32_t c = s->palette[i]; |
| @@ -749,22 +782,22 @@ static void load_colormap(PaletteUseContext *s) · 2 unmodified lines | ||
| 749 | 782 | continue; |
| 750 | 783 | } |
| 751 | 784 | last_color = c; |
| 752 | − if (c >> 24 < s->trans_thresh) { | |
| 785 | + if (!s->use_alpha && c >> 24 < s->trans_thresh) { | |
| 753 | 786 | color_used[i] = 1; // ignore transparent color(s) |
| 754 | 787 | continue; |
| 755 | 788 | } |
| 756 | 789 | } |
| 757 | 790 | |
| 758 | − box.min[0] = box.min[1] = box.min[2] = 0x00; | |
| 759 | − box.max[0] = box.max[1] = box.max[2] = 0xff; | |
| 791 | + box.min[0] = box.min[1] = box.min[2] = box.min[3] = 0x00; | |
| 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 | 796 | if (s->dot_filename) |
| 764 | 797 | disp_tree(s->map, s->dot_filename); |
| 765 | 798 | |
| 766 | 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 | 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 | 811 | uint8_t *src2 = in2->data[0]; |
| 779 | 812 | const int src1_linesize = in1->linesize[0] >> 2; |
| 780 | 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 | 815 | unsigned mean_err = 0; |
| 783 | 816 | |
| 784 | 817 | for (y = 0; y < in1->height; y++) { |
| 785 | 818 | for (x = 0; x < in1->width; x++) { |
| 786 | 819 | const uint32_t c1 = src1[x]; |
| 787 | 820 | const uint32_t c2 = palette[src2[x]]; |
| 788 | − const uint8_t argb1[] = {0xff, c1 >> 16 & 0xff, c1 >> 8 & 0xff, c1 & 0xff}; | |
| 789 | − const uint8_t argb2[] = {0xff, c2 >> 16 & 0xff, c2 >> 8 & 0xff, c2 & 0xff}; | |
| 790 | − mean_err += diff(argb1, argb2, s->trans_thresh); | |
| 821 | + const uint8_t a1 = s->use_alpha ? c1>>24 & 0xff : 0xff; | |
| 822 | + const uint8_t a2 = s->use_alpha ? c2>>24 & 0xff : 0xff; | |
| 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 | 827 | src1 += src1_linesize; |
| 793 | 828 | src2 += src2_linesize; |
| @@ -987,7 +1022,7 @@ static void load_palette(PaletteUseContext *s, const AVFrame *palette_frame) · 193 unmodified lines | ||
| 987 | 1022 | for (y = 0; y < palette_frame->height; y++) { |
| 988 | 1023 | for (x = 0; x < palette_frame->width; x++) { |
| 989 | 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 | 1026 | s->transparency_index = i; // we are assuming at most one transparent color in palette |
| 992 | 1027 | } |
| 993 | 1028 | i++; |