|  | #include <cstdint> | 
|  | #include <cinttypes> | 
|  | #include <cstdlib> | 
|  | #include <cstdio> | 
|  | #include <cassert> | 
|  |  | 
|  | #include "config/aom_config.h" | 
|  |  | 
|  | #define PALETTE_MAX_SIZE 8 | 
|  | #define NUM_PALETTE_NEIGHBORS 3 | 
|  | #define MAX_COLOR_CONTEXT_HASH 8 | 
|  | #define PALETTE_COLOR_INDEX_CONTEXTS 6 | 
|  | #define AOMMIN(x, y) (((x) < (y)) ? (x) : (y)) | 
|  | #define AOMMAX(x, y) (((x) > (y)) ? (x) : (y)) | 
|  | #include "third_party/googletest/src/googletest/include/gtest/gtest.h" | 
|  |  | 
|  | static void swap_color_order(uint8_t *color_order, uint8_t *color_order_status, | 
|  | int switch_idx, int max_idx, | 
|  | int *color_order_cnt) { | 
|  | color_order[switch_idx] = max_idx; | 
|  | color_order_status[max_idx] = 1; | 
|  | (*color_order_cnt)++; | 
|  | } | 
|  |  | 
|  | // Negative values are invalid | 
|  | static const int palette_color_index_context_lookup[MAX_COLOR_CONTEXT_HASH + | 
|  | 1] = { -1, -1, 0, -1, -1, | 
|  | 4,  3,  2, 1 }; | 
|  |  | 
|  | int derive_color_index_ctx(uint8_t *color_order, int *color_idx, | 
|  | const uint8_t *color_map, int stride, int r, int c) { | 
|  | int color_index_ctx = 0; | 
|  | uint8_t color_status[PALETTE_MAX_SIZE] = { 0 }; | 
|  | int color_cnt = 0; | 
|  | for (int j = 0; j < PALETTE_MAX_SIZE; ++j) { | 
|  | color_order[j] = j; | 
|  | } | 
|  |  | 
|  | if (r > 0 && c > 0) { | 
|  | int color_neighbors[3] = { 0 }; | 
|  | color_neighbors[0] = color_map[0]; | 
|  | color_neighbors[1] = color_map[1]; | 
|  | color_neighbors[2] = color_map[2]; | 
|  | // color_neighbors[0] = color_map[r * stride + c - 1]; | 
|  | // color_neighbors[1] = color_map[(r - 1) * stride + c - 1]; | 
|  | // color_neighbors[2] = color_map[(r - 1) * stride + c]; | 
|  |  | 
|  | if (color_neighbors[0] == color_neighbors[1] && | 
|  | color_neighbors[0] == color_neighbors[2]) { | 
|  | color_index_ctx = 4; | 
|  | swap_color_order(color_order, color_status, 0, color_neighbors[0], | 
|  | &color_cnt); | 
|  | } else if (color_neighbors[0] == color_neighbors[2]) { | 
|  | color_index_ctx = 3; | 
|  | swap_color_order(color_order, color_status, 0, color_neighbors[0], | 
|  | &color_cnt); | 
|  | swap_color_order(color_order, color_status, 1, color_neighbors[1], | 
|  | &color_cnt); | 
|  | } else if (color_neighbors[0] == color_neighbors[1]) { | 
|  | color_index_ctx = 2; | 
|  | swap_color_order(color_order, color_status, 0, color_neighbors[0], | 
|  | &color_cnt); | 
|  | swap_color_order(color_order, color_status, 1, color_neighbors[2], | 
|  | &color_cnt); | 
|  | } else if (color_neighbors[1] == color_neighbors[2]) { | 
|  | color_index_ctx = 2; | 
|  | swap_color_order(color_order, color_status, 0, color_neighbors[2], | 
|  | &color_cnt); | 
|  | swap_color_order(color_order, color_status, 1, color_neighbors[0], | 
|  | &color_cnt); | 
|  | } else { | 
|  | color_index_ctx = 1; | 
|  | int min_color = AOMMIN(color_neighbors[0], color_neighbors[2]); | 
|  | int max_color = AOMMAX(color_neighbors[0], color_neighbors[2]); | 
|  | swap_color_order(color_order, color_status, 0, min_color, &color_cnt); | 
|  | swap_color_order(color_order, color_status, 1, max_color, &color_cnt); | 
|  | swap_color_order(color_order, color_status, 2, color_neighbors[1], | 
|  | &color_cnt); | 
|  | } | 
|  | } else if (c == 0 && r > 0) { | 
|  | color_index_ctx = 0; | 
|  | const int color_neighbor = color_map[2]; | 
|  | swap_color_order(color_order, color_status, 0, color_neighbor, &color_cnt); | 
|  | } else if (c > 0 && r == 0) { | 
|  | color_index_ctx = 0; | 
|  | const int color_neighbor = color_map[0]; | 
|  | swap_color_order(color_order, color_status, 0, color_neighbor, &color_cnt); | 
|  | } | 
|  |  | 
|  | int write_idx = color_cnt; | 
|  | for (int read_idx = 0; read_idx < PALETTE_MAX_SIZE; read_idx++) { | 
|  | if (color_status[read_idx] == 0) { | 
|  | color_order[write_idx] = read_idx; | 
|  | write_idx++; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (color_idx != NULL) { | 
|  | // If any of the neighbor color has higher index than current color index, | 
|  | // then we move up by 1 unless the current color is the same as one of the | 
|  | // neighbor | 
|  | const int current_color = *color_idx = color_map[r * stride + c]; | 
|  | for (int idx = 0; idx < PALETTE_MAX_SIZE; idx++) { | 
|  | if (color_order[idx] == current_color) { | 
|  | *color_idx = idx; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | return color_index_ctx; | 
|  | } | 
|  |  | 
|  | int new_av1_get_palette_color_index_context(const uint8_t *color_map, | 
|  | int stride, int r, int c, | 
|  | int palette_size, | 
|  | uint8_t *color_order, int *color_idx | 
|  | #if CONFIG_PALETTE_IMPROVEMENTS | 
|  | , | 
|  | int row_flag, int prev_row_flag | 
|  | #endif  // CONFIG_PALETTE_IMPROVEMENTS | 
|  | ) { | 
|  | assert(palette_size <= PALETTE_MAX_SIZE); | 
|  | assert(r > 0 || c > 0); | 
|  | (void)palette_size; | 
|  |  | 
|  | int color_index_ctx = | 
|  | derive_color_index_ctx(color_order, color_idx, color_map, stride, r, c); | 
|  | #if CONFIG_PALETTE_IMPROVEMENTS | 
|  | // Special context value for the first (and only) index of an identity row | 
|  | // and when the previous row is also an identity row. | 
|  | if (c == 0 && row_flag && prev_row_flag) | 
|  | color_index_ctx = PALETTE_COLOR_INDEX_CONTEXTS - 1; | 
|  | #endif  // CONFIG_PALETTE_IMPROVEMENTS | 
|  | return color_index_ctx; | 
|  | } | 
|  |  | 
|  | int old_av1_get_palette_color_index_context(const uint8_t *color_map, | 
|  | int stride, int r, int c, | 
|  | int palette_size, | 
|  | uint8_t *color_order, int *color_idx | 
|  | #if CONFIG_PALETTE_IMPROVEMENTS | 
|  | , | 
|  | int row_flag, int prev_row_flag | 
|  | #endif  // CONFIG_PALETTE_IMPROVEMENTS | 
|  | ) { | 
|  | assert(palette_size <= PALETTE_MAX_SIZE); | 
|  | assert(r > 0 || c > 0); | 
|  |  | 
|  | // Get color indices of neighbors. | 
|  | int color_neighbors[NUM_PALETTE_NEIGHBORS]; | 
|  | color_neighbors[0] = (c - 1 >= 0) ? color_map[0] : -1; | 
|  | color_neighbors[1] = (c - 1 >= 0 && r - 1 >= 0) ? color_map[1] : -1; | 
|  | color_neighbors[2] = (r - 1 >= 0) ? color_map[2] : -1; | 
|  | // color_neighbors[0] = (c - 1 >= 0) ? color_map[r * stride + c - 1] : -1; | 
|  | // color_neighbors[1] = | 
|  | //     (c - 1 >= 0 && r - 1 >= 0) ? color_map[(r - 1) * stride + c - 1] : -1; | 
|  | // color_neighbors[2] = (r - 1 >= 0) ? color_map[(r - 1) * stride + c] : -1; | 
|  |  | 
|  | // The +10 below should not be needed. But we get a warning "array subscript | 
|  | // is above array bounds [-Werror=array-bounds]" without it, possibly due to | 
|  | // this (or similar) bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59124 | 
|  | int scores[PALETTE_MAX_SIZE + 10] = { 0 }; | 
|  | int i; | 
|  | static const int weights[NUM_PALETTE_NEIGHBORS] = { 2, 1, 2 }; | 
|  | for (i = 0; i < NUM_PALETTE_NEIGHBORS; ++i) { | 
|  | if (color_neighbors[i] >= 0) { | 
|  | scores[color_neighbors[i]] += weights[i]; | 
|  | } | 
|  | } | 
|  |  | 
|  | int inverse_color_order[PALETTE_MAX_SIZE]; | 
|  | for (i = 0; i < PALETTE_MAX_SIZE; ++i) { | 
|  | color_order[i] = i; | 
|  | inverse_color_order[i] = i; | 
|  | } | 
|  |  | 
|  | // Get the top NUM_PALETTE_NEIGHBORS scores (sorted from large to small). | 
|  | for (i = 0; i < NUM_PALETTE_NEIGHBORS; ++i) { | 
|  | int max = scores[i]; | 
|  | int max_idx = i; | 
|  | for (int j = i + 1; j < palette_size; ++j) { | 
|  | if (scores[j] > max) { | 
|  | max = scores[j]; | 
|  | max_idx = j; | 
|  | } | 
|  | } | 
|  | if (max_idx != i) { | 
|  | // Move the score at index 'max_idx' to index 'i', and shift the scores | 
|  | // from 'i' to 'max_idx - 1' by 1. | 
|  | const int max_score = scores[max_idx]; | 
|  | const uint8_t max_color_order = color_order[max_idx]; | 
|  | for (int k = max_idx; k > i; --k) { | 
|  | scores[k] = scores[k - 1]; | 
|  | color_order[k] = color_order[k - 1]; | 
|  | inverse_color_order[color_order[k]] = k; | 
|  | } | 
|  | scores[i] = max_score; | 
|  | color_order[i] = max_color_order; | 
|  | inverse_color_order[color_order[i]] = i; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (color_idx != NULL) | 
|  | *color_idx = inverse_color_order[color_map[r * stride + c]]; | 
|  |  | 
|  | #if CONFIG_PALETTE_IMPROVEMENTS | 
|  | // Special context value for the first (and only) index of an identity row and | 
|  | // when the previous row is also an identity row. | 
|  | if (c == 0 && row_flag && prev_row_flag) | 
|  | return PALETTE_COLOR_INDEX_CONTEXTS - 1; | 
|  | #endif  // CONFIG_PALETTE_IMPROVEMENTS | 
|  |  | 
|  | // Get hash value of context. | 
|  | int color_index_ctx_hash = 0; | 
|  | static const int hash_multipliers[NUM_PALETTE_NEIGHBORS] = { 1, 2, 2 }; | 
|  | for (i = 0; i < NUM_PALETTE_NEIGHBORS; ++i) { | 
|  | color_index_ctx_hash += scores[i] * hash_multipliers[i]; | 
|  | } | 
|  | assert(color_index_ctx_hash > 0); | 
|  | assert(color_index_ctx_hash <= MAX_COLOR_CONTEXT_HASH); | 
|  |  | 
|  | // Lookup context from hash. | 
|  | const int color_index_ctx = | 
|  | palette_color_index_context_lookup[color_index_ctx_hash]; | 
|  | assert(color_index_ctx >= 0); | 
|  | assert(color_index_ctx < PALETTE_COLOR_INDEX_CONTEXTS); | 
|  | return color_index_ctx; | 
|  | } | 
|  |  | 
|  | TEST(PaletteContextTest, TestTopAndLeftNeighbor) { | 
|  | uint8_t color_map[3]; | 
|  | uint8_t color_order[8]; | 
|  | const int stride = 1; | 
|  | const int r = 1; | 
|  | const int c = 1; | 
|  | const int palette_size = 8; | 
|  | const int row_flag = 0; | 
|  | const int prev_row_flag = 0; | 
|  | for (int i = 0; i < PALETTE_MAX_SIZE; i++) | 
|  | for (int j = 0; j < PALETTE_MAX_SIZE; j++) | 
|  | for (int k = 0; k < PALETTE_MAX_SIZE; k++) { | 
|  | color_map[0] = i; | 
|  | color_map[1] = j; | 
|  | color_map[2] = k; | 
|  | const int old_ctx = old_av1_get_palette_color_index_context( | 
|  | color_map, stride, r, c, palette_size, color_order, NULL, row_flag, | 
|  | prev_row_flag); | 
|  | const int new_ctx = new_av1_get_palette_color_index_context( | 
|  | color_map, stride, r, c, palette_size, color_order, NULL, row_flag, | 
|  | prev_row_flag); | 
|  | GTEST_ASSERT_EQ(old_ctx, new_ctx); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(PaletteContextTest, TestLeftNeighbor) { | 
|  | uint8_t color_map[3]; | 
|  | uint8_t color_order[8]; | 
|  | const int stride = 1; | 
|  | const int r = 0; | 
|  | const int c = 1; | 
|  | const int palette_size = 8; | 
|  | const int row_flag = 0; | 
|  | const int prev_row_flag = 0; | 
|  | for (int i = 0; i < PALETTE_MAX_SIZE; i++) | 
|  | for (int j = 0; j < PALETTE_MAX_SIZE; j++) | 
|  | for (int k = 0; k < PALETTE_MAX_SIZE; k++) { | 
|  | color_map[0] = i; | 
|  | color_map[1] = j; | 
|  | color_map[2] = k; | 
|  | const int old_ctx = old_av1_get_palette_color_index_context( | 
|  | color_map, stride, r, c, palette_size, color_order, NULL, row_flag, | 
|  | prev_row_flag); | 
|  | const int new_ctx = new_av1_get_palette_color_index_context( | 
|  | color_map, stride, r, c, palette_size, color_order, NULL, row_flag, | 
|  | prev_row_flag); | 
|  | GTEST_ASSERT_EQ(old_ctx, new_ctx); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(PaletteContextTest, TestTopNeighbor) { | 
|  | uint8_t color_map[3]; | 
|  | uint8_t color_order[8]; | 
|  | const int stride = 1; | 
|  | const int r = 1; | 
|  | const int c = 0; | 
|  | const int palette_size = 8; | 
|  | const int row_flag = 0; | 
|  | const int prev_row_flag = 0; | 
|  | for (int i = 0; i < PALETTE_MAX_SIZE; i++) | 
|  | for (int j = 0; j < PALETTE_MAX_SIZE; j++) | 
|  | for (int k = 0; k < PALETTE_MAX_SIZE; k++) { | 
|  | color_map[0] = i; | 
|  | color_map[1] = j; | 
|  | color_map[2] = k; | 
|  | const int old_ctx = old_av1_get_palette_color_index_context( | 
|  | color_map, stride, r, c, palette_size, color_order, NULL, row_flag, | 
|  | prev_row_flag); | 
|  | const int new_ctx = new_av1_get_palette_color_index_context( | 
|  | color_map, stride, r, c, palette_size, color_order, NULL, row_flag, | 
|  | prev_row_flag); | 
|  | GTEST_ASSERT_EQ(old_ctx, new_ctx); | 
|  | } | 
|  | } |