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