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 | #define NBITS 5 | 61 | #define NBITS 5 |
| 62 | #define HIST_SIZE (1<<(3*NBITS)) | 62 | #define HIST_SIZE (1<<(4*NBITS)) |
| 63 | 63 | ||
| 64 | typedef struct PaletteGenContext { | 64 | typedef 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_mode | 72 | AVFrame *prev_frame; // previous frame used for the diff stats_mode |
| 72 | struct hist_node histogram[HIST_SIZE]; // histogram/hashtable of the colors | 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 | { "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 | ||
| 120 | DECLARE_CMP_FUNC(r, 0) | 122 | DECLARE_CMP_FUNC(a, 0) |
| 121 | DECLARE_CMP_FUNC(g, 1) | 123 | DECLARE_CMP_FUNC(r, 1) |
| 122 | DECLARE_CMP_FUNC(b, 2) | 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 | * Simple color comparison for sorting the final palette | 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 | return dr*dr + dg*dg + db*db; | 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 | * Find the next box to split: pick the one with the highest variance | 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 | 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 | */ |
| 186 | static uint32_t get_avg_color(struct color_ref * const *refs, | 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 | 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 the | 354 | /* 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 again | 376 | 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 it | 427 | * 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 | */ |
| 397 | static inline unsigned color_hash(uint32_t color) | 430 | static 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 | */ |
| 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 | 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 | */ |
| 435 | static int update_histogram_diff(struct hist_node *hist, | 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 | 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 | */ |
| 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 | 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 | ||
| 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 | static av_cold void uninit(AVFilterContext *ctx) | 592 | static 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), |
| @@ -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 5 | 64 | #define NBITS 5 |
| 66 | #define CACHE_SIZE (1<<(3*NBITS)) | 65 | #define CACHE_SIZE (1<<(4*NBITS)) |
| 67 | 66 | ||
| 68 | struct cached_color { | 67 | struct 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_PARAM | 110 | #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) |
| 111 | static const AVOption paletteuse_options[] = { | 111 | static 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 | ||
| 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 | // 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 | ||
| 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 | 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 entry | 192 | 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 | ||
| 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 | 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 | ||
| 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 | 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 | ||
| 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 | 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 if | 270 | /* 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 the | 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 | 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 | ||
| 370 | static av_always_inline int get_dst_color_err(PaletteUseContext *s, | 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 | 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 | ||
| 556 | static int debug_accuracy(const struct color_node *node, const uint32_t *palette, const int trans_thresh, | 562 | static 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 | ||
| 592 | struct color_rect { | 604 | struct 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 | ||
| 597 | typedef int (*cmp_func)(const void *, const void *); | 609 | typedef int (*cmp_func)(const void *, const void *); |
| @@ -612,43 +624,47 @@ DECLARE_CMP_FUNC(b, 3) · 14 unmodified lines | |||
| 612 | 624 | ||
| 613 | static const cmp_func cmp_funcs[] = {cmp_a, cmp_r, cmp_g, cmp_b}; | 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, | 627 | static 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 | |||
| 678 | static int colormap_insert(struct color_node *map, | 704 | static 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 | ||
| 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 | static void load_colormap(PaletteUseContext *s) | 763 | static 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 palette | 1026 | s->transparency_index = i; // we are assuming at most one transparent color in palette |
| 992 | } | 1027 | } |
| 993 | i++; | 1028 | i++; |