| @@ -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++; |