xref: /aosp_15_r20/external/libaom/av1/encoder/palette.c (revision 77c1e3ccc04c968bd2bc212e87364f250e820521)
1*77c1e3ccSAndroid Build Coastguard Worker /*
2*77c1e3ccSAndroid Build Coastguard Worker  * Copyright (c) 2016, Alliance for Open Media. All rights reserved.
3*77c1e3ccSAndroid Build Coastguard Worker  *
4*77c1e3ccSAndroid Build Coastguard Worker  * This source code is subject to the terms of the BSD 2 Clause License and
5*77c1e3ccSAndroid Build Coastguard Worker  * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6*77c1e3ccSAndroid Build Coastguard Worker  * was not distributed with this source code in the LICENSE file, you can
7*77c1e3ccSAndroid Build Coastguard Worker  * obtain it at www.aomedia.org/license/software. If the Alliance for Open
8*77c1e3ccSAndroid Build Coastguard Worker  * Media Patent License 1.0 was not distributed with this source code in the
9*77c1e3ccSAndroid Build Coastguard Worker  * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
10*77c1e3ccSAndroid Build Coastguard Worker  */
11*77c1e3ccSAndroid Build Coastguard Worker 
12*77c1e3ccSAndroid Build Coastguard Worker #include <math.h>
13*77c1e3ccSAndroid Build Coastguard Worker #include <stdlib.h>
14*77c1e3ccSAndroid Build Coastguard Worker 
15*77c1e3ccSAndroid Build Coastguard Worker #include "av1/common/pred_common.h"
16*77c1e3ccSAndroid Build Coastguard Worker 
17*77c1e3ccSAndroid Build Coastguard Worker #include "av1/encoder/block.h"
18*77c1e3ccSAndroid Build Coastguard Worker #include "av1/encoder/cost.h"
19*77c1e3ccSAndroid Build Coastguard Worker #include "av1/encoder/encoder.h"
20*77c1e3ccSAndroid Build Coastguard Worker #include "av1/encoder/intra_mode_search.h"
21*77c1e3ccSAndroid Build Coastguard Worker #include "av1/encoder/intra_mode_search_utils.h"
22*77c1e3ccSAndroid Build Coastguard Worker #include "av1/encoder/palette.h"
23*77c1e3ccSAndroid Build Coastguard Worker #include "av1/encoder/random.h"
24*77c1e3ccSAndroid Build Coastguard Worker #include "av1/encoder/rdopt_utils.h"
25*77c1e3ccSAndroid Build Coastguard Worker #include "av1/encoder/tx_search.h"
26*77c1e3ccSAndroid Build Coastguard Worker 
27*77c1e3ccSAndroid Build Coastguard Worker #define AV1_K_MEANS_DIM 1
28*77c1e3ccSAndroid Build Coastguard Worker #include "av1/encoder/k_means_template.h"
29*77c1e3ccSAndroid Build Coastguard Worker #undef AV1_K_MEANS_DIM
30*77c1e3ccSAndroid Build Coastguard Worker #define AV1_K_MEANS_DIM 2
31*77c1e3ccSAndroid Build Coastguard Worker #include "av1/encoder/k_means_template.h"
32*77c1e3ccSAndroid Build Coastguard Worker #undef AV1_K_MEANS_DIM
33*77c1e3ccSAndroid Build Coastguard Worker 
int16_comparer(const void * a,const void * b)34*77c1e3ccSAndroid Build Coastguard Worker static int int16_comparer(const void *a, const void *b) {
35*77c1e3ccSAndroid Build Coastguard Worker   return (*(int16_t *)a - *(int16_t *)b);
36*77c1e3ccSAndroid Build Coastguard Worker }
37*77c1e3ccSAndroid Build Coastguard Worker 
38*77c1e3ccSAndroid Build Coastguard Worker /*!\brief Removes duplicated centroid indices.
39*77c1e3ccSAndroid Build Coastguard Worker  *
40*77c1e3ccSAndroid Build Coastguard Worker  * \ingroup palette_mode_search
41*77c1e3ccSAndroid Build Coastguard Worker  * \param[in]    centroids          A list of centroids index.
42*77c1e3ccSAndroid Build Coastguard Worker  * \param[in]    num_centroids      Number of centroids.
43*77c1e3ccSAndroid Build Coastguard Worker  *
44*77c1e3ccSAndroid Build Coastguard Worker  * \return Returns the number of unique centroids and saves the unique centroids
45*77c1e3ccSAndroid Build Coastguard Worker  * in beginning of the centroids array.
46*77c1e3ccSAndroid Build Coastguard Worker  *
47*77c1e3ccSAndroid Build Coastguard Worker  * \attention The centroids should be rounded to integers before calling this
48*77c1e3ccSAndroid Build Coastguard Worker  * method.
49*77c1e3ccSAndroid Build Coastguard Worker  */
remove_duplicates(int16_t * centroids,int num_centroids)50*77c1e3ccSAndroid Build Coastguard Worker static int remove_duplicates(int16_t *centroids, int num_centroids) {
51*77c1e3ccSAndroid Build Coastguard Worker   int num_unique;  // number of unique centroids
52*77c1e3ccSAndroid Build Coastguard Worker   int i;
53*77c1e3ccSAndroid Build Coastguard Worker   qsort(centroids, num_centroids, sizeof(*centroids), int16_comparer);
54*77c1e3ccSAndroid Build Coastguard Worker   // Remove duplicates.
55*77c1e3ccSAndroid Build Coastguard Worker   num_unique = 1;
56*77c1e3ccSAndroid Build Coastguard Worker   for (i = 1; i < num_centroids; ++i) {
57*77c1e3ccSAndroid Build Coastguard Worker     if (centroids[i] != centroids[i - 1]) {  // found a new unique centroid
58*77c1e3ccSAndroid Build Coastguard Worker       centroids[num_unique++] = centroids[i];
59*77c1e3ccSAndroid Build Coastguard Worker     }
60*77c1e3ccSAndroid Build Coastguard Worker   }
61*77c1e3ccSAndroid Build Coastguard Worker   return num_unique;
62*77c1e3ccSAndroid Build Coastguard Worker }
63*77c1e3ccSAndroid Build Coastguard Worker 
delta_encode_cost(const int * colors,int num,int bit_depth,int min_val)64*77c1e3ccSAndroid Build Coastguard Worker static int delta_encode_cost(const int *colors, int num, int bit_depth,
65*77c1e3ccSAndroid Build Coastguard Worker                              int min_val) {
66*77c1e3ccSAndroid Build Coastguard Worker   if (num <= 0) return 0;
67*77c1e3ccSAndroid Build Coastguard Worker   int bits_cost = bit_depth;
68*77c1e3ccSAndroid Build Coastguard Worker   if (num == 1) return bits_cost;
69*77c1e3ccSAndroid Build Coastguard Worker   bits_cost += 2;
70*77c1e3ccSAndroid Build Coastguard Worker   int max_delta = 0;
71*77c1e3ccSAndroid Build Coastguard Worker   int deltas[PALETTE_MAX_SIZE];
72*77c1e3ccSAndroid Build Coastguard Worker   const int min_bits = bit_depth - 3;
73*77c1e3ccSAndroid Build Coastguard Worker   for (int i = 1; i < num; ++i) {
74*77c1e3ccSAndroid Build Coastguard Worker     const int delta = colors[i] - colors[i - 1];
75*77c1e3ccSAndroid Build Coastguard Worker     deltas[i - 1] = delta;
76*77c1e3ccSAndroid Build Coastguard Worker     assert(delta >= min_val);
77*77c1e3ccSAndroid Build Coastguard Worker     if (delta > max_delta) max_delta = delta;
78*77c1e3ccSAndroid Build Coastguard Worker   }
79*77c1e3ccSAndroid Build Coastguard Worker   int bits_per_delta = AOMMAX(av1_ceil_log2(max_delta + 1 - min_val), min_bits);
80*77c1e3ccSAndroid Build Coastguard Worker   assert(bits_per_delta <= bit_depth);
81*77c1e3ccSAndroid Build Coastguard Worker   int range = (1 << bit_depth) - colors[0] - min_val;
82*77c1e3ccSAndroid Build Coastguard Worker   for (int i = 0; i < num - 1; ++i) {
83*77c1e3ccSAndroid Build Coastguard Worker     bits_cost += bits_per_delta;
84*77c1e3ccSAndroid Build Coastguard Worker     range -= deltas[i];
85*77c1e3ccSAndroid Build Coastguard Worker     bits_per_delta = AOMMIN(bits_per_delta, av1_ceil_log2(range));
86*77c1e3ccSAndroid Build Coastguard Worker   }
87*77c1e3ccSAndroid Build Coastguard Worker   return bits_cost;
88*77c1e3ccSAndroid Build Coastguard Worker }
89*77c1e3ccSAndroid Build Coastguard Worker 
av1_index_color_cache(const uint16_t * color_cache,int n_cache,const uint16_t * colors,int n_colors,uint8_t * cache_color_found,int * out_cache_colors)90*77c1e3ccSAndroid Build Coastguard Worker int av1_index_color_cache(const uint16_t *color_cache, int n_cache,
91*77c1e3ccSAndroid Build Coastguard Worker                           const uint16_t *colors, int n_colors,
92*77c1e3ccSAndroid Build Coastguard Worker                           uint8_t *cache_color_found, int *out_cache_colors) {
93*77c1e3ccSAndroid Build Coastguard Worker   if (n_cache <= 0) {
94*77c1e3ccSAndroid Build Coastguard Worker     for (int i = 0; i < n_colors; ++i) out_cache_colors[i] = colors[i];
95*77c1e3ccSAndroid Build Coastguard Worker     return n_colors;
96*77c1e3ccSAndroid Build Coastguard Worker   }
97*77c1e3ccSAndroid Build Coastguard Worker   memset(cache_color_found, 0, n_cache * sizeof(*cache_color_found));
98*77c1e3ccSAndroid Build Coastguard Worker   int n_in_cache = 0;
99*77c1e3ccSAndroid Build Coastguard Worker   int in_cache_flags[PALETTE_MAX_SIZE];
100*77c1e3ccSAndroid Build Coastguard Worker   memset(in_cache_flags, 0, sizeof(in_cache_flags));
101*77c1e3ccSAndroid Build Coastguard Worker   for (int i = 0; i < n_cache && n_in_cache < n_colors; ++i) {
102*77c1e3ccSAndroid Build Coastguard Worker     for (int j = 0; j < n_colors; ++j) {
103*77c1e3ccSAndroid Build Coastguard Worker       if (colors[j] == color_cache[i]) {
104*77c1e3ccSAndroid Build Coastguard Worker         in_cache_flags[j] = 1;
105*77c1e3ccSAndroid Build Coastguard Worker         cache_color_found[i] = 1;
106*77c1e3ccSAndroid Build Coastguard Worker         ++n_in_cache;
107*77c1e3ccSAndroid Build Coastguard Worker         break;
108*77c1e3ccSAndroid Build Coastguard Worker       }
109*77c1e3ccSAndroid Build Coastguard Worker     }
110*77c1e3ccSAndroid Build Coastguard Worker   }
111*77c1e3ccSAndroid Build Coastguard Worker   int j = 0;
112*77c1e3ccSAndroid Build Coastguard Worker   for (int i = 0; i < n_colors; ++i)
113*77c1e3ccSAndroid Build Coastguard Worker     if (!in_cache_flags[i]) out_cache_colors[j++] = colors[i];
114*77c1e3ccSAndroid Build Coastguard Worker   assert(j == n_colors - n_in_cache);
115*77c1e3ccSAndroid Build Coastguard Worker   return j;
116*77c1e3ccSAndroid Build Coastguard Worker }
117*77c1e3ccSAndroid Build Coastguard Worker 
av1_get_palette_delta_bits_v(const PALETTE_MODE_INFO * const pmi,int bit_depth,int * zero_count,int * min_bits)118*77c1e3ccSAndroid Build Coastguard Worker int av1_get_palette_delta_bits_v(const PALETTE_MODE_INFO *const pmi,
119*77c1e3ccSAndroid Build Coastguard Worker                                  int bit_depth, int *zero_count,
120*77c1e3ccSAndroid Build Coastguard Worker                                  int *min_bits) {
121*77c1e3ccSAndroid Build Coastguard Worker   const int n = pmi->palette_size[1];
122*77c1e3ccSAndroid Build Coastguard Worker   const int max_val = 1 << bit_depth;
123*77c1e3ccSAndroid Build Coastguard Worker   int max_d = 0;
124*77c1e3ccSAndroid Build Coastguard Worker   *min_bits = bit_depth - 4;
125*77c1e3ccSAndroid Build Coastguard Worker   *zero_count = 0;
126*77c1e3ccSAndroid Build Coastguard Worker   for (int i = 1; i < n; ++i) {
127*77c1e3ccSAndroid Build Coastguard Worker     const int delta = pmi->palette_colors[2 * PALETTE_MAX_SIZE + i] -
128*77c1e3ccSAndroid Build Coastguard Worker                       pmi->palette_colors[2 * PALETTE_MAX_SIZE + i - 1];
129*77c1e3ccSAndroid Build Coastguard Worker     const int v = abs(delta);
130*77c1e3ccSAndroid Build Coastguard Worker     const int d = AOMMIN(v, max_val - v);
131*77c1e3ccSAndroid Build Coastguard Worker     if (d > max_d) max_d = d;
132*77c1e3ccSAndroid Build Coastguard Worker     if (d == 0) ++(*zero_count);
133*77c1e3ccSAndroid Build Coastguard Worker   }
134*77c1e3ccSAndroid Build Coastguard Worker   return AOMMAX(av1_ceil_log2(max_d + 1), *min_bits);
135*77c1e3ccSAndroid Build Coastguard Worker }
136*77c1e3ccSAndroid Build Coastguard Worker 
av1_palette_color_cost_y(const PALETTE_MODE_INFO * const pmi,const uint16_t * color_cache,int n_cache,int bit_depth)137*77c1e3ccSAndroid Build Coastguard Worker int av1_palette_color_cost_y(const PALETTE_MODE_INFO *const pmi,
138*77c1e3ccSAndroid Build Coastguard Worker                              const uint16_t *color_cache, int n_cache,
139*77c1e3ccSAndroid Build Coastguard Worker                              int bit_depth) {
140*77c1e3ccSAndroid Build Coastguard Worker   const int n = pmi->palette_size[0];
141*77c1e3ccSAndroid Build Coastguard Worker   int out_cache_colors[PALETTE_MAX_SIZE];
142*77c1e3ccSAndroid Build Coastguard Worker   uint8_t cache_color_found[2 * PALETTE_MAX_SIZE];
143*77c1e3ccSAndroid Build Coastguard Worker   const int n_out_cache =
144*77c1e3ccSAndroid Build Coastguard Worker       av1_index_color_cache(color_cache, n_cache, pmi->palette_colors, n,
145*77c1e3ccSAndroid Build Coastguard Worker                             cache_color_found, out_cache_colors);
146*77c1e3ccSAndroid Build Coastguard Worker   const int total_bits =
147*77c1e3ccSAndroid Build Coastguard Worker       n_cache + delta_encode_cost(out_cache_colors, n_out_cache, bit_depth, 1);
148*77c1e3ccSAndroid Build Coastguard Worker   return av1_cost_literal(total_bits);
149*77c1e3ccSAndroid Build Coastguard Worker }
150*77c1e3ccSAndroid Build Coastguard Worker 
av1_palette_color_cost_uv(const PALETTE_MODE_INFO * const pmi,const uint16_t * color_cache,int n_cache,int bit_depth)151*77c1e3ccSAndroid Build Coastguard Worker int av1_palette_color_cost_uv(const PALETTE_MODE_INFO *const pmi,
152*77c1e3ccSAndroid Build Coastguard Worker                               const uint16_t *color_cache, int n_cache,
153*77c1e3ccSAndroid Build Coastguard Worker                               int bit_depth) {
154*77c1e3ccSAndroid Build Coastguard Worker   const int n = pmi->palette_size[1];
155*77c1e3ccSAndroid Build Coastguard Worker   int total_bits = 0;
156*77c1e3ccSAndroid Build Coastguard Worker   // U channel palette color cost.
157*77c1e3ccSAndroid Build Coastguard Worker   int out_cache_colors[PALETTE_MAX_SIZE];
158*77c1e3ccSAndroid Build Coastguard Worker   uint8_t cache_color_found[2 * PALETTE_MAX_SIZE];
159*77c1e3ccSAndroid Build Coastguard Worker   const int n_out_cache = av1_index_color_cache(
160*77c1e3ccSAndroid Build Coastguard Worker       color_cache, n_cache, pmi->palette_colors + PALETTE_MAX_SIZE, n,
161*77c1e3ccSAndroid Build Coastguard Worker       cache_color_found, out_cache_colors);
162*77c1e3ccSAndroid Build Coastguard Worker   total_bits +=
163*77c1e3ccSAndroid Build Coastguard Worker       n_cache + delta_encode_cost(out_cache_colors, n_out_cache, bit_depth, 0);
164*77c1e3ccSAndroid Build Coastguard Worker 
165*77c1e3ccSAndroid Build Coastguard Worker   // V channel palette color cost.
166*77c1e3ccSAndroid Build Coastguard Worker   int zero_count = 0, min_bits_v = 0;
167*77c1e3ccSAndroid Build Coastguard Worker   const int bits_v =
168*77c1e3ccSAndroid Build Coastguard Worker       av1_get_palette_delta_bits_v(pmi, bit_depth, &zero_count, &min_bits_v);
169*77c1e3ccSAndroid Build Coastguard Worker   const int bits_using_delta =
170*77c1e3ccSAndroid Build Coastguard Worker       2 + bit_depth + (bits_v + 1) * (n - 1) - zero_count;
171*77c1e3ccSAndroid Build Coastguard Worker   const int bits_using_raw = bit_depth * n;
172*77c1e3ccSAndroid Build Coastguard Worker   total_bits += 1 + AOMMIN(bits_using_delta, bits_using_raw);
173*77c1e3ccSAndroid Build Coastguard Worker   return av1_cost_literal(total_bits);
174*77c1e3ccSAndroid Build Coastguard Worker }
175*77c1e3ccSAndroid Build Coastguard Worker 
176*77c1e3ccSAndroid Build Coastguard Worker // Extends 'color_map' array from 'orig_width x orig_height' to 'new_width x
177*77c1e3ccSAndroid Build Coastguard Worker // new_height'. Extra rows and columns are filled in by copying last valid
178*77c1e3ccSAndroid Build Coastguard Worker // row/column.
extend_palette_color_map(uint8_t * const color_map,int orig_width,int orig_height,int new_width,int new_height)179*77c1e3ccSAndroid Build Coastguard Worker static inline void extend_palette_color_map(uint8_t *const color_map,
180*77c1e3ccSAndroid Build Coastguard Worker                                             int orig_width, int orig_height,
181*77c1e3ccSAndroid Build Coastguard Worker                                             int new_width, int new_height) {
182*77c1e3ccSAndroid Build Coastguard Worker   int j;
183*77c1e3ccSAndroid Build Coastguard Worker   assert(new_width >= orig_width);
184*77c1e3ccSAndroid Build Coastguard Worker   assert(new_height >= orig_height);
185*77c1e3ccSAndroid Build Coastguard Worker   if (new_width == orig_width && new_height == orig_height) return;
186*77c1e3ccSAndroid Build Coastguard Worker 
187*77c1e3ccSAndroid Build Coastguard Worker   for (j = orig_height - 1; j >= 0; --j) {
188*77c1e3ccSAndroid Build Coastguard Worker     memmove(color_map + j * new_width, color_map + j * orig_width, orig_width);
189*77c1e3ccSAndroid Build Coastguard Worker     // Copy last column to extra columns.
190*77c1e3ccSAndroid Build Coastguard Worker     memset(color_map + j * new_width + orig_width,
191*77c1e3ccSAndroid Build Coastguard Worker            color_map[j * new_width + orig_width - 1], new_width - orig_width);
192*77c1e3ccSAndroid Build Coastguard Worker   }
193*77c1e3ccSAndroid Build Coastguard Worker   // Copy last row to extra rows.
194*77c1e3ccSAndroid Build Coastguard Worker   for (j = orig_height; j < new_height; ++j) {
195*77c1e3ccSAndroid Build Coastguard Worker     memcpy(color_map + j * new_width, color_map + (orig_height - 1) * new_width,
196*77c1e3ccSAndroid Build Coastguard Worker            new_width);
197*77c1e3ccSAndroid Build Coastguard Worker   }
198*77c1e3ccSAndroid Build Coastguard Worker }
199*77c1e3ccSAndroid Build Coastguard Worker 
200*77c1e3ccSAndroid Build Coastguard Worker // Bias toward using colors in the cache.
201*77c1e3ccSAndroid Build Coastguard Worker // TODO(huisu): Try other schemes to improve compression.
optimize_palette_colors(uint16_t * color_cache,int n_cache,int n_colors,int stride,int16_t * centroids,int bit_depth)202*77c1e3ccSAndroid Build Coastguard Worker static inline void optimize_palette_colors(uint16_t *color_cache, int n_cache,
203*77c1e3ccSAndroid Build Coastguard Worker                                            int n_colors, int stride,
204*77c1e3ccSAndroid Build Coastguard Worker                                            int16_t *centroids, int bit_depth) {
205*77c1e3ccSAndroid Build Coastguard Worker   if (n_cache <= 0) return;
206*77c1e3ccSAndroid Build Coastguard Worker   for (int i = 0; i < n_colors * stride; i += stride) {
207*77c1e3ccSAndroid Build Coastguard Worker     int min_diff = abs((int)centroids[i] - (int)color_cache[0]);
208*77c1e3ccSAndroid Build Coastguard Worker     int idx = 0;
209*77c1e3ccSAndroid Build Coastguard Worker     for (int j = 1; j < n_cache; ++j) {
210*77c1e3ccSAndroid Build Coastguard Worker       const int this_diff = abs((int)centroids[i] - (int)color_cache[j]);
211*77c1e3ccSAndroid Build Coastguard Worker       if (this_diff < min_diff) {
212*77c1e3ccSAndroid Build Coastguard Worker         min_diff = this_diff;
213*77c1e3ccSAndroid Build Coastguard Worker         idx = j;
214*77c1e3ccSAndroid Build Coastguard Worker       }
215*77c1e3ccSAndroid Build Coastguard Worker     }
216*77c1e3ccSAndroid Build Coastguard Worker     const int min_threshold = 4 << (bit_depth - 8);
217*77c1e3ccSAndroid Build Coastguard Worker     if (min_diff <= min_threshold) centroids[i] = color_cache[idx];
218*77c1e3ccSAndroid Build Coastguard Worker   }
219*77c1e3ccSAndroid Build Coastguard Worker }
220*77c1e3ccSAndroid Build Coastguard Worker 
221*77c1e3ccSAndroid Build Coastguard Worker /*!\brief Calculate the luma palette cost from a given color palette
222*77c1e3ccSAndroid Build Coastguard Worker  *
223*77c1e3ccSAndroid Build Coastguard Worker  * \ingroup palette_mode_search
224*77c1e3ccSAndroid Build Coastguard Worker  * \callergraph
225*77c1e3ccSAndroid Build Coastguard Worker  * Given the base colors as specified in centroids[], calculate the RD cost
226*77c1e3ccSAndroid Build Coastguard Worker  * of palette mode.
227*77c1e3ccSAndroid Build Coastguard Worker  */
palette_rd_y(const AV1_COMP * const cpi,MACROBLOCK * x,MB_MODE_INFO * mbmi,BLOCK_SIZE bsize,int dc_mode_cost,const int16_t * data,int16_t * centroids,int n,uint16_t * color_cache,int n_cache,bool do_header_rd_based_gating,MB_MODE_INFO * best_mbmi,uint8_t * best_palette_color_map,int64_t * best_rd,int * rate,int * rate_tokenonly,int64_t * distortion,uint8_t * skippable,int * beat_best_rd,PICK_MODE_CONTEXT * ctx,uint8_t * blk_skip,uint8_t * tx_type_map,int * beat_best_palette_rd,bool * do_header_rd_based_breakout,int discount_color_cost)228*77c1e3ccSAndroid Build Coastguard Worker static inline void palette_rd_y(
229*77c1e3ccSAndroid Build Coastguard Worker     const AV1_COMP *const cpi, MACROBLOCK *x, MB_MODE_INFO *mbmi,
230*77c1e3ccSAndroid Build Coastguard Worker     BLOCK_SIZE bsize, int dc_mode_cost, const int16_t *data, int16_t *centroids,
231*77c1e3ccSAndroid Build Coastguard Worker     int n, uint16_t *color_cache, int n_cache, bool do_header_rd_based_gating,
232*77c1e3ccSAndroid Build Coastguard Worker     MB_MODE_INFO *best_mbmi, uint8_t *best_palette_color_map, int64_t *best_rd,
233*77c1e3ccSAndroid Build Coastguard Worker     int *rate, int *rate_tokenonly, int64_t *distortion, uint8_t *skippable,
234*77c1e3ccSAndroid Build Coastguard Worker     int *beat_best_rd, PICK_MODE_CONTEXT *ctx, uint8_t *blk_skip,
235*77c1e3ccSAndroid Build Coastguard Worker     uint8_t *tx_type_map, int *beat_best_palette_rd,
236*77c1e3ccSAndroid Build Coastguard Worker     bool *do_header_rd_based_breakout, int discount_color_cost) {
237*77c1e3ccSAndroid Build Coastguard Worker   if (do_header_rd_based_breakout != NULL) *do_header_rd_based_breakout = false;
238*77c1e3ccSAndroid Build Coastguard Worker   optimize_palette_colors(color_cache, n_cache, n, 1, centroids,
239*77c1e3ccSAndroid Build Coastguard Worker                           cpi->common.seq_params->bit_depth);
240*77c1e3ccSAndroid Build Coastguard Worker   const int num_unique_colors = remove_duplicates(centroids, n);
241*77c1e3ccSAndroid Build Coastguard Worker   if (num_unique_colors < PALETTE_MIN_SIZE) {
242*77c1e3ccSAndroid Build Coastguard Worker     // Too few unique colors to create a palette. And DC_PRED will work
243*77c1e3ccSAndroid Build Coastguard Worker     // well for that case anyway. So skip.
244*77c1e3ccSAndroid Build Coastguard Worker     return;
245*77c1e3ccSAndroid Build Coastguard Worker   }
246*77c1e3ccSAndroid Build Coastguard Worker   PALETTE_MODE_INFO *const pmi = &mbmi->palette_mode_info;
247*77c1e3ccSAndroid Build Coastguard Worker   if (cpi->common.seq_params->use_highbitdepth) {
248*77c1e3ccSAndroid Build Coastguard Worker     for (int i = 0; i < num_unique_colors; ++i) {
249*77c1e3ccSAndroid Build Coastguard Worker       pmi->palette_colors[i] = clip_pixel_highbd(
250*77c1e3ccSAndroid Build Coastguard Worker           (int)centroids[i], cpi->common.seq_params->bit_depth);
251*77c1e3ccSAndroid Build Coastguard Worker     }
252*77c1e3ccSAndroid Build Coastguard Worker   } else {
253*77c1e3ccSAndroid Build Coastguard Worker     for (int i = 0; i < num_unique_colors; ++i) {
254*77c1e3ccSAndroid Build Coastguard Worker       pmi->palette_colors[i] = clip_pixel(centroids[i]);
255*77c1e3ccSAndroid Build Coastguard Worker     }
256*77c1e3ccSAndroid Build Coastguard Worker   }
257*77c1e3ccSAndroid Build Coastguard Worker   pmi->palette_size[0] = num_unique_colors;
258*77c1e3ccSAndroid Build Coastguard Worker   MACROBLOCKD *const xd = &x->e_mbd;
259*77c1e3ccSAndroid Build Coastguard Worker   uint8_t *const color_map = xd->plane[0].color_index_map;
260*77c1e3ccSAndroid Build Coastguard Worker   int block_width, block_height, rows, cols;
261*77c1e3ccSAndroid Build Coastguard Worker   av1_get_block_dimensions(bsize, 0, xd, &block_width, &block_height, &rows,
262*77c1e3ccSAndroid Build Coastguard Worker                            &cols);
263*77c1e3ccSAndroid Build Coastguard Worker   av1_calc_indices(data, centroids, color_map, rows * cols, num_unique_colors,
264*77c1e3ccSAndroid Build Coastguard Worker                    1);
265*77c1e3ccSAndroid Build Coastguard Worker   extend_palette_color_map(color_map, cols, rows, block_width, block_height);
266*77c1e3ccSAndroid Build Coastguard Worker 
267*77c1e3ccSAndroid Build Coastguard Worker   RD_STATS tokenonly_rd_stats;
268*77c1e3ccSAndroid Build Coastguard Worker   int this_rate;
269*77c1e3ccSAndroid Build Coastguard Worker 
270*77c1e3ccSAndroid Build Coastguard Worker   if (do_header_rd_based_gating) {
271*77c1e3ccSAndroid Build Coastguard Worker     assert(do_header_rd_based_breakout != NULL);
272*77c1e3ccSAndroid Build Coastguard Worker     const int palette_mode_rate = intra_mode_info_cost_y(
273*77c1e3ccSAndroid Build Coastguard Worker         cpi, x, mbmi, bsize, dc_mode_cost, discount_color_cost);
274*77c1e3ccSAndroid Build Coastguard Worker     const int64_t header_rd = RDCOST(x->rdmult, palette_mode_rate, 0);
275*77c1e3ccSAndroid Build Coastguard Worker     // Less aggressive pruning when prune_luma_palette_size_search_level == 1.
276*77c1e3ccSAndroid Build Coastguard Worker     const int header_rd_shift =
277*77c1e3ccSAndroid Build Coastguard Worker         (cpi->sf.intra_sf.prune_luma_palette_size_search_level == 1) ? 1 : 0;
278*77c1e3ccSAndroid Build Coastguard Worker     // Terminate further palette_size search, if the header cost corresponding
279*77c1e3ccSAndroid Build Coastguard Worker     // to lower palette_size is more than *best_rd << header_rd_shift. This
280*77c1e3ccSAndroid Build Coastguard Worker     // logic is implemented with a right shift in the LHS to prevent a possible
281*77c1e3ccSAndroid Build Coastguard Worker     // overflow with the left shift in RHS.
282*77c1e3ccSAndroid Build Coastguard Worker     if ((header_rd >> header_rd_shift) > *best_rd) {
283*77c1e3ccSAndroid Build Coastguard Worker       *do_header_rd_based_breakout = true;
284*77c1e3ccSAndroid Build Coastguard Worker       return;
285*77c1e3ccSAndroid Build Coastguard Worker     }
286*77c1e3ccSAndroid Build Coastguard Worker     av1_pick_uniform_tx_size_type_yrd(cpi, x, &tokenonly_rd_stats, bsize,
287*77c1e3ccSAndroid Build Coastguard Worker                                       *best_rd);
288*77c1e3ccSAndroid Build Coastguard Worker     if (tokenonly_rd_stats.rate == INT_MAX) return;
289*77c1e3ccSAndroid Build Coastguard Worker     this_rate = tokenonly_rd_stats.rate + palette_mode_rate;
290*77c1e3ccSAndroid Build Coastguard Worker   } else {
291*77c1e3ccSAndroid Build Coastguard Worker     av1_pick_uniform_tx_size_type_yrd(cpi, x, &tokenonly_rd_stats, bsize,
292*77c1e3ccSAndroid Build Coastguard Worker                                       *best_rd);
293*77c1e3ccSAndroid Build Coastguard Worker     if (tokenonly_rd_stats.rate == INT_MAX) return;
294*77c1e3ccSAndroid Build Coastguard Worker     this_rate = tokenonly_rd_stats.rate +
295*77c1e3ccSAndroid Build Coastguard Worker                 intra_mode_info_cost_y(cpi, x, mbmi, bsize, dc_mode_cost,
296*77c1e3ccSAndroid Build Coastguard Worker                                        discount_color_cost);
297*77c1e3ccSAndroid Build Coastguard Worker   }
298*77c1e3ccSAndroid Build Coastguard Worker 
299*77c1e3ccSAndroid Build Coastguard Worker   int64_t this_rd = RDCOST(x->rdmult, this_rate, tokenonly_rd_stats.dist);
300*77c1e3ccSAndroid Build Coastguard Worker   if (!xd->lossless[mbmi->segment_id] && block_signals_txsize(mbmi->bsize)) {
301*77c1e3ccSAndroid Build Coastguard Worker     tokenonly_rd_stats.rate -= tx_size_cost(x, bsize, mbmi->tx_size);
302*77c1e3ccSAndroid Build Coastguard Worker   }
303*77c1e3ccSAndroid Build Coastguard Worker   // Collect mode stats for multiwinner mode processing
304*77c1e3ccSAndroid Build Coastguard Worker   const int txfm_search_done = 1;
305*77c1e3ccSAndroid Build Coastguard Worker   store_winner_mode_stats(
306*77c1e3ccSAndroid Build Coastguard Worker       &cpi->common, x, mbmi, NULL, NULL, NULL, THR_DC, color_map, bsize,
307*77c1e3ccSAndroid Build Coastguard Worker       this_rd, cpi->sf.winner_mode_sf.multi_winner_mode_type, txfm_search_done);
308*77c1e3ccSAndroid Build Coastguard Worker   if (this_rd < *best_rd) {
309*77c1e3ccSAndroid Build Coastguard Worker     *best_rd = this_rd;
310*77c1e3ccSAndroid Build Coastguard Worker     // Setting beat_best_rd flag because current mode rd is better than best_rd.
311*77c1e3ccSAndroid Build Coastguard Worker     // This flag need to be updated only for palette evaluation in key frames
312*77c1e3ccSAndroid Build Coastguard Worker     if (beat_best_rd) *beat_best_rd = 1;
313*77c1e3ccSAndroid Build Coastguard Worker     memcpy(best_palette_color_map, color_map,
314*77c1e3ccSAndroid Build Coastguard Worker            block_width * block_height * sizeof(color_map[0]));
315*77c1e3ccSAndroid Build Coastguard Worker     *best_mbmi = *mbmi;
316*77c1e3ccSAndroid Build Coastguard Worker     memcpy(blk_skip, x->txfm_search_info.blk_skip,
317*77c1e3ccSAndroid Build Coastguard Worker            sizeof(x->txfm_search_info.blk_skip[0]) * ctx->num_4x4_blk);
318*77c1e3ccSAndroid Build Coastguard Worker     av1_copy_array(tx_type_map, xd->tx_type_map, ctx->num_4x4_blk);
319*77c1e3ccSAndroid Build Coastguard Worker     if (rate) *rate = this_rate;
320*77c1e3ccSAndroid Build Coastguard Worker     if (rate_tokenonly) *rate_tokenonly = tokenonly_rd_stats.rate;
321*77c1e3ccSAndroid Build Coastguard Worker     if (distortion) *distortion = tokenonly_rd_stats.dist;
322*77c1e3ccSAndroid Build Coastguard Worker     if (skippable) *skippable = tokenonly_rd_stats.skip_txfm;
323*77c1e3ccSAndroid Build Coastguard Worker     if (beat_best_palette_rd) *beat_best_palette_rd = 1;
324*77c1e3ccSAndroid Build Coastguard Worker   }
325*77c1e3ccSAndroid Build Coastguard Worker }
326*77c1e3ccSAndroid Build Coastguard Worker 
is_iter_over(int curr_idx,int end_idx,int step_size)327*77c1e3ccSAndroid Build Coastguard Worker static inline int is_iter_over(int curr_idx, int end_idx, int step_size) {
328*77c1e3ccSAndroid Build Coastguard Worker   assert(step_size != 0);
329*77c1e3ccSAndroid Build Coastguard Worker   return (step_size > 0) ? curr_idx >= end_idx : curr_idx <= end_idx;
330*77c1e3ccSAndroid Build Coastguard Worker }
331*77c1e3ccSAndroid Build Coastguard Worker 
332*77c1e3ccSAndroid Build Coastguard Worker // Performs count-based palette search with number of colors in interval
333*77c1e3ccSAndroid Build Coastguard Worker // [start_n, end_n) with step size step_size. If step_size < 0, then end_n can
334*77c1e3ccSAndroid Build Coastguard Worker // be less than start_n. Saves the last numbers searched in last_n_searched and
335*77c1e3ccSAndroid Build Coastguard Worker // returns the best number of colors found.
perform_top_color_palette_search(const AV1_COMP * const cpi,MACROBLOCK * x,MB_MODE_INFO * mbmi,BLOCK_SIZE bsize,int dc_mode_cost,const int16_t * data,int16_t * top_colors,int start_n,int end_n,int step_size,bool do_header_rd_based_gating,int * last_n_searched,uint16_t * color_cache,int n_cache,MB_MODE_INFO * best_mbmi,uint8_t * best_palette_color_map,int64_t * best_rd,int * rate,int * rate_tokenonly,int64_t * distortion,uint8_t * skippable,int * beat_best_rd,PICK_MODE_CONTEXT * ctx,uint8_t * best_blk_skip,uint8_t * tx_type_map,int discount_color_cost)336*77c1e3ccSAndroid Build Coastguard Worker static inline int perform_top_color_palette_search(
337*77c1e3ccSAndroid Build Coastguard Worker     const AV1_COMP *const cpi, MACROBLOCK *x, MB_MODE_INFO *mbmi,
338*77c1e3ccSAndroid Build Coastguard Worker     BLOCK_SIZE bsize, int dc_mode_cost, const int16_t *data,
339*77c1e3ccSAndroid Build Coastguard Worker     int16_t *top_colors, int start_n, int end_n, int step_size,
340*77c1e3ccSAndroid Build Coastguard Worker     bool do_header_rd_based_gating, int *last_n_searched, uint16_t *color_cache,
341*77c1e3ccSAndroid Build Coastguard Worker     int n_cache, MB_MODE_INFO *best_mbmi, uint8_t *best_palette_color_map,
342*77c1e3ccSAndroid Build Coastguard Worker     int64_t *best_rd, int *rate, int *rate_tokenonly, int64_t *distortion,
343*77c1e3ccSAndroid Build Coastguard Worker     uint8_t *skippable, int *beat_best_rd, PICK_MODE_CONTEXT *ctx,
344*77c1e3ccSAndroid Build Coastguard Worker     uint8_t *best_blk_skip, uint8_t *tx_type_map, int discount_color_cost) {
345*77c1e3ccSAndroid Build Coastguard Worker   int16_t centroids[PALETTE_MAX_SIZE];
346*77c1e3ccSAndroid Build Coastguard Worker   int n = start_n;
347*77c1e3ccSAndroid Build Coastguard Worker   int top_color_winner = end_n;
348*77c1e3ccSAndroid Build Coastguard Worker   /* clang-format off */
349*77c1e3ccSAndroid Build Coastguard Worker   assert(IMPLIES(step_size < 0, start_n > end_n));
350*77c1e3ccSAndroid Build Coastguard Worker   /* clang-format on */
351*77c1e3ccSAndroid Build Coastguard Worker   assert(IMPLIES(step_size > 0, start_n < end_n));
352*77c1e3ccSAndroid Build Coastguard Worker   while (!is_iter_over(n, end_n, step_size)) {
353*77c1e3ccSAndroid Build Coastguard Worker     int beat_best_palette_rd = 0;
354*77c1e3ccSAndroid Build Coastguard Worker     bool do_header_rd_based_breakout = false;
355*77c1e3ccSAndroid Build Coastguard Worker     memcpy(centroids, top_colors, n * sizeof(top_colors[0]));
356*77c1e3ccSAndroid Build Coastguard Worker     palette_rd_y(cpi, x, mbmi, bsize, dc_mode_cost, data, centroids, n,
357*77c1e3ccSAndroid Build Coastguard Worker                  color_cache, n_cache, do_header_rd_based_gating, best_mbmi,
358*77c1e3ccSAndroid Build Coastguard Worker                  best_palette_color_map, best_rd, rate, rate_tokenonly,
359*77c1e3ccSAndroid Build Coastguard Worker                  distortion, skippable, beat_best_rd, ctx, best_blk_skip,
360*77c1e3ccSAndroid Build Coastguard Worker                  tx_type_map, &beat_best_palette_rd,
361*77c1e3ccSAndroid Build Coastguard Worker                  &do_header_rd_based_breakout, discount_color_cost);
362*77c1e3ccSAndroid Build Coastguard Worker     *last_n_searched = n;
363*77c1e3ccSAndroid Build Coastguard Worker     if (do_header_rd_based_breakout) {
364*77c1e3ccSAndroid Build Coastguard Worker       // Terminate palette_size search by setting last_n_searched to end_n.
365*77c1e3ccSAndroid Build Coastguard Worker       *last_n_searched = end_n;
366*77c1e3ccSAndroid Build Coastguard Worker       break;
367*77c1e3ccSAndroid Build Coastguard Worker     }
368*77c1e3ccSAndroid Build Coastguard Worker     if (beat_best_palette_rd) {
369*77c1e3ccSAndroid Build Coastguard Worker       top_color_winner = n;
370*77c1e3ccSAndroid Build Coastguard Worker     } else if (cpi->sf.intra_sf.prune_palette_search_level == 2) {
371*77c1e3ccSAndroid Build Coastguard Worker       // At search level 2, we return immediately if we don't see an improvement
372*77c1e3ccSAndroid Build Coastguard Worker       return top_color_winner;
373*77c1e3ccSAndroid Build Coastguard Worker     }
374*77c1e3ccSAndroid Build Coastguard Worker     n += step_size;
375*77c1e3ccSAndroid Build Coastguard Worker   }
376*77c1e3ccSAndroid Build Coastguard Worker   return top_color_winner;
377*77c1e3ccSAndroid Build Coastguard Worker }
378*77c1e3ccSAndroid Build Coastguard Worker 
379*77c1e3ccSAndroid Build Coastguard Worker // Performs k-means based palette search with number of colors in interval
380*77c1e3ccSAndroid Build Coastguard Worker // [start_n, end_n) with step size step_size. If step_size < 0, then end_n can
381*77c1e3ccSAndroid Build Coastguard Worker // be less than start_n. Saves the last numbers searched in last_n_searched and
382*77c1e3ccSAndroid Build Coastguard Worker // returns the best number of colors found.
perform_k_means_palette_search(const AV1_COMP * const cpi,MACROBLOCK * x,MB_MODE_INFO * mbmi,BLOCK_SIZE bsize,int dc_mode_cost,const int16_t * data,int lower_bound,int upper_bound,int start_n,int end_n,int step_size,bool do_header_rd_based_gating,int * last_n_searched,uint16_t * color_cache,int n_cache,MB_MODE_INFO * best_mbmi,uint8_t * best_palette_color_map,int64_t * best_rd,int * rate,int * rate_tokenonly,int64_t * distortion,uint8_t * skippable,int * beat_best_rd,PICK_MODE_CONTEXT * ctx,uint8_t * best_blk_skip,uint8_t * tx_type_map,uint8_t * color_map,int data_points,int discount_color_cost)383*77c1e3ccSAndroid Build Coastguard Worker static inline int perform_k_means_palette_search(
384*77c1e3ccSAndroid Build Coastguard Worker     const AV1_COMP *const cpi, MACROBLOCK *x, MB_MODE_INFO *mbmi,
385*77c1e3ccSAndroid Build Coastguard Worker     BLOCK_SIZE bsize, int dc_mode_cost, const int16_t *data, int lower_bound,
386*77c1e3ccSAndroid Build Coastguard Worker     int upper_bound, int start_n, int end_n, int step_size,
387*77c1e3ccSAndroid Build Coastguard Worker     bool do_header_rd_based_gating, int *last_n_searched, uint16_t *color_cache,
388*77c1e3ccSAndroid Build Coastguard Worker     int n_cache, MB_MODE_INFO *best_mbmi, uint8_t *best_palette_color_map,
389*77c1e3ccSAndroid Build Coastguard Worker     int64_t *best_rd, int *rate, int *rate_tokenonly, int64_t *distortion,
390*77c1e3ccSAndroid Build Coastguard Worker     uint8_t *skippable, int *beat_best_rd, PICK_MODE_CONTEXT *ctx,
391*77c1e3ccSAndroid Build Coastguard Worker     uint8_t *best_blk_skip, uint8_t *tx_type_map, uint8_t *color_map,
392*77c1e3ccSAndroid Build Coastguard Worker     int data_points, int discount_color_cost) {
393*77c1e3ccSAndroid Build Coastguard Worker   int16_t centroids[PALETTE_MAX_SIZE];
394*77c1e3ccSAndroid Build Coastguard Worker   const int max_itr = 50;
395*77c1e3ccSAndroid Build Coastguard Worker   int n = start_n;
396*77c1e3ccSAndroid Build Coastguard Worker   int top_color_winner = end_n;
397*77c1e3ccSAndroid Build Coastguard Worker   /* clang-format off */
398*77c1e3ccSAndroid Build Coastguard Worker   assert(IMPLIES(step_size < 0, start_n > end_n));
399*77c1e3ccSAndroid Build Coastguard Worker   /* clang-format on */
400*77c1e3ccSAndroid Build Coastguard Worker   assert(IMPLIES(step_size > 0, start_n < end_n));
401*77c1e3ccSAndroid Build Coastguard Worker   while (!is_iter_over(n, end_n, step_size)) {
402*77c1e3ccSAndroid Build Coastguard Worker     int beat_best_palette_rd = 0;
403*77c1e3ccSAndroid Build Coastguard Worker     bool do_header_rd_based_breakout = false;
404*77c1e3ccSAndroid Build Coastguard Worker     for (int i = 0; i < n; ++i) {
405*77c1e3ccSAndroid Build Coastguard Worker       centroids[i] =
406*77c1e3ccSAndroid Build Coastguard Worker           lower_bound + (2 * i + 1) * (upper_bound - lower_bound) / n / 2;
407*77c1e3ccSAndroid Build Coastguard Worker     }
408*77c1e3ccSAndroid Build Coastguard Worker     av1_k_means(data, centroids, color_map, data_points, n, 1, max_itr);
409*77c1e3ccSAndroid Build Coastguard Worker     palette_rd_y(cpi, x, mbmi, bsize, dc_mode_cost, data, centroids, n,
410*77c1e3ccSAndroid Build Coastguard Worker                  color_cache, n_cache, do_header_rd_based_gating, best_mbmi,
411*77c1e3ccSAndroid Build Coastguard Worker                  best_palette_color_map, best_rd, rate, rate_tokenonly,
412*77c1e3ccSAndroid Build Coastguard Worker                  distortion, skippable, beat_best_rd, ctx, best_blk_skip,
413*77c1e3ccSAndroid Build Coastguard Worker                  tx_type_map, &beat_best_palette_rd,
414*77c1e3ccSAndroid Build Coastguard Worker                  &do_header_rd_based_breakout, discount_color_cost);
415*77c1e3ccSAndroid Build Coastguard Worker     *last_n_searched = n;
416*77c1e3ccSAndroid Build Coastguard Worker     if (do_header_rd_based_breakout) {
417*77c1e3ccSAndroid Build Coastguard Worker       // Terminate palette_size search by setting last_n_searched to end_n.
418*77c1e3ccSAndroid Build Coastguard Worker       *last_n_searched = end_n;
419*77c1e3ccSAndroid Build Coastguard Worker       break;
420*77c1e3ccSAndroid Build Coastguard Worker     }
421*77c1e3ccSAndroid Build Coastguard Worker     if (beat_best_palette_rd) {
422*77c1e3ccSAndroid Build Coastguard Worker       top_color_winner = n;
423*77c1e3ccSAndroid Build Coastguard Worker     } else if (cpi->sf.intra_sf.prune_palette_search_level == 2) {
424*77c1e3ccSAndroid Build Coastguard Worker       // At search level 2, we return immediately if we don't see an improvement
425*77c1e3ccSAndroid Build Coastguard Worker       return top_color_winner;
426*77c1e3ccSAndroid Build Coastguard Worker     }
427*77c1e3ccSAndroid Build Coastguard Worker     n += step_size;
428*77c1e3ccSAndroid Build Coastguard Worker   }
429*77c1e3ccSAndroid Build Coastguard Worker   return top_color_winner;
430*77c1e3ccSAndroid Build Coastguard Worker }
431*77c1e3ccSAndroid Build Coastguard Worker 
432*77c1e3ccSAndroid Build Coastguard Worker // Sets the parameters to search the current number of colors +- 1
set_stage2_params(int * min_n,int * max_n,int * step_size,int winner,int end_n)433*77c1e3ccSAndroid Build Coastguard Worker static inline void set_stage2_params(int *min_n, int *max_n, int *step_size,
434*77c1e3ccSAndroid Build Coastguard Worker                                      int winner, int end_n) {
435*77c1e3ccSAndroid Build Coastguard Worker   // Set min to winner - 1 unless we are already at the border, then we set it
436*77c1e3ccSAndroid Build Coastguard Worker   // to winner + 1
437*77c1e3ccSAndroid Build Coastguard Worker   *min_n = (winner == PALETTE_MIN_SIZE) ? (PALETTE_MIN_SIZE + 1)
438*77c1e3ccSAndroid Build Coastguard Worker                                         : AOMMAX(winner - 1, PALETTE_MIN_SIZE);
439*77c1e3ccSAndroid Build Coastguard Worker   // Set max to winner + 1 unless we are already at the border, then we set it
440*77c1e3ccSAndroid Build Coastguard Worker   // to winner - 1
441*77c1e3ccSAndroid Build Coastguard Worker   *max_n =
442*77c1e3ccSAndroid Build Coastguard Worker       (winner == end_n) ? (winner - 1) : AOMMIN(winner + 1, PALETTE_MAX_SIZE);
443*77c1e3ccSAndroid Build Coastguard Worker 
444*77c1e3ccSAndroid Build Coastguard Worker   // Set the step size to max_n - min_n so we only search those two values.
445*77c1e3ccSAndroid Build Coastguard Worker   // If max_n == min_n, then set step_size to 1 to avoid infinite loop later.
446*77c1e3ccSAndroid Build Coastguard Worker   *step_size = AOMMAX(1, *max_n - *min_n);
447*77c1e3ccSAndroid Build Coastguard Worker }
448*77c1e3ccSAndroid Build Coastguard Worker 
fill_data_and_get_bounds(const uint8_t * src,const int src_stride,const int rows,const int cols,const int is_high_bitdepth,int16_t * data,int * lower_bound,int * upper_bound)449*77c1e3ccSAndroid Build Coastguard Worker static inline void fill_data_and_get_bounds(const uint8_t *src,
450*77c1e3ccSAndroid Build Coastguard Worker                                             const int src_stride,
451*77c1e3ccSAndroid Build Coastguard Worker                                             const int rows, const int cols,
452*77c1e3ccSAndroid Build Coastguard Worker                                             const int is_high_bitdepth,
453*77c1e3ccSAndroid Build Coastguard Worker                                             int16_t *data, int *lower_bound,
454*77c1e3ccSAndroid Build Coastguard Worker                                             int *upper_bound) {
455*77c1e3ccSAndroid Build Coastguard Worker   if (is_high_bitdepth) {
456*77c1e3ccSAndroid Build Coastguard Worker     const uint16_t *src_ptr = CONVERT_TO_SHORTPTR(src);
457*77c1e3ccSAndroid Build Coastguard Worker     *lower_bound = *upper_bound = src_ptr[0];
458*77c1e3ccSAndroid Build Coastguard Worker     for (int r = 0; r < rows; ++r) {
459*77c1e3ccSAndroid Build Coastguard Worker       for (int c = 0; c < cols; ++c) {
460*77c1e3ccSAndroid Build Coastguard Worker         const int val = src_ptr[c];
461*77c1e3ccSAndroid Build Coastguard Worker         data[c] = (int16_t)val;
462*77c1e3ccSAndroid Build Coastguard Worker         *lower_bound = AOMMIN(*lower_bound, val);
463*77c1e3ccSAndroid Build Coastguard Worker         *upper_bound = AOMMAX(*upper_bound, val);
464*77c1e3ccSAndroid Build Coastguard Worker       }
465*77c1e3ccSAndroid Build Coastguard Worker       src_ptr += src_stride;
466*77c1e3ccSAndroid Build Coastguard Worker       data += cols;
467*77c1e3ccSAndroid Build Coastguard Worker     }
468*77c1e3ccSAndroid Build Coastguard Worker     return;
469*77c1e3ccSAndroid Build Coastguard Worker   }
470*77c1e3ccSAndroid Build Coastguard Worker 
471*77c1e3ccSAndroid Build Coastguard Worker   // low bit depth
472*77c1e3ccSAndroid Build Coastguard Worker   *lower_bound = *upper_bound = src[0];
473*77c1e3ccSAndroid Build Coastguard Worker   for (int r = 0; r < rows; ++r) {
474*77c1e3ccSAndroid Build Coastguard Worker     for (int c = 0; c < cols; ++c) {
475*77c1e3ccSAndroid Build Coastguard Worker       const int val = src[c];
476*77c1e3ccSAndroid Build Coastguard Worker       data[c] = (int16_t)val;
477*77c1e3ccSAndroid Build Coastguard Worker       *lower_bound = AOMMIN(*lower_bound, val);
478*77c1e3ccSAndroid Build Coastguard Worker       *upper_bound = AOMMAX(*upper_bound, val);
479*77c1e3ccSAndroid Build Coastguard Worker     }
480*77c1e3ccSAndroid Build Coastguard Worker     src += src_stride;
481*77c1e3ccSAndroid Build Coastguard Worker     data += cols;
482*77c1e3ccSAndroid Build Coastguard Worker   }
483*77c1e3ccSAndroid Build Coastguard Worker }
484*77c1e3ccSAndroid Build Coastguard Worker 
485*77c1e3ccSAndroid Build Coastguard Worker /*! \brief Colors are sorted by their count: the higher the better.
486*77c1e3ccSAndroid Build Coastguard Worker  */
487*77c1e3ccSAndroid Build Coastguard Worker struct ColorCount {
488*77c1e3ccSAndroid Build Coastguard Worker   //! Color index in the histogram.
489*77c1e3ccSAndroid Build Coastguard Worker   int index;
490*77c1e3ccSAndroid Build Coastguard Worker   //! Histogram count.
491*77c1e3ccSAndroid Build Coastguard Worker   int count;
492*77c1e3ccSAndroid Build Coastguard Worker };
493*77c1e3ccSAndroid Build Coastguard Worker 
color_count_comp(const void * c1,const void * c2)494*77c1e3ccSAndroid Build Coastguard Worker static int color_count_comp(const void *c1, const void *c2) {
495*77c1e3ccSAndroid Build Coastguard Worker   const struct ColorCount *color_count1 = (const struct ColorCount *)c1;
496*77c1e3ccSAndroid Build Coastguard Worker   const struct ColorCount *color_count2 = (const struct ColorCount *)c2;
497*77c1e3ccSAndroid Build Coastguard Worker   if (color_count1->count > color_count2->count) return -1;
498*77c1e3ccSAndroid Build Coastguard Worker   if (color_count1->count < color_count2->count) return 1;
499*77c1e3ccSAndroid Build Coastguard Worker   if (color_count1->index < color_count2->index) return -1;
500*77c1e3ccSAndroid Build Coastguard Worker   return 1;
501*77c1e3ccSAndroid Build Coastguard Worker }
502*77c1e3ccSAndroid Build Coastguard Worker 
find_top_colors(const int * const count_buf,int bit_depth,int n_colors,int16_t * top_colors)503*77c1e3ccSAndroid Build Coastguard Worker static void find_top_colors(const int *const count_buf, int bit_depth,
504*77c1e3ccSAndroid Build Coastguard Worker                             int n_colors, int16_t *top_colors) {
505*77c1e3ccSAndroid Build Coastguard Worker   // Top color array, serving as a priority queue if more than n_colors are
506*77c1e3ccSAndroid Build Coastguard Worker   // found.
507*77c1e3ccSAndroid Build Coastguard Worker   struct ColorCount top_color_counts[PALETTE_MAX_SIZE] = { { 0 } };
508*77c1e3ccSAndroid Build Coastguard Worker   int n_color_count = 0;
509*77c1e3ccSAndroid Build Coastguard Worker   for (int i = 0; i < (1 << bit_depth); ++i) {
510*77c1e3ccSAndroid Build Coastguard Worker     if (count_buf[i] > 0) {
511*77c1e3ccSAndroid Build Coastguard Worker       if (n_color_count < n_colors) {
512*77c1e3ccSAndroid Build Coastguard Worker         // Keep adding to the top colors.
513*77c1e3ccSAndroid Build Coastguard Worker         top_color_counts[n_color_count].index = i;
514*77c1e3ccSAndroid Build Coastguard Worker         top_color_counts[n_color_count].count = count_buf[i];
515*77c1e3ccSAndroid Build Coastguard Worker         ++n_color_count;
516*77c1e3ccSAndroid Build Coastguard Worker         if (n_color_count == n_colors) {
517*77c1e3ccSAndroid Build Coastguard Worker           qsort(top_color_counts, n_colors, sizeof(top_color_counts[0]),
518*77c1e3ccSAndroid Build Coastguard Worker                 color_count_comp);
519*77c1e3ccSAndroid Build Coastguard Worker         }
520*77c1e3ccSAndroid Build Coastguard Worker       } else {
521*77c1e3ccSAndroid Build Coastguard Worker         // Check the worst in the sorted top.
522*77c1e3ccSAndroid Build Coastguard Worker         if (count_buf[i] > top_color_counts[n_colors - 1].count) {
523*77c1e3ccSAndroid Build Coastguard Worker           int j = n_colors - 1;
524*77c1e3ccSAndroid Build Coastguard Worker           // Move up to the best one.
525*77c1e3ccSAndroid Build Coastguard Worker           while (j >= 1 && count_buf[i] > top_color_counts[j - 1].count) --j;
526*77c1e3ccSAndroid Build Coastguard Worker           memmove(top_color_counts + j + 1, top_color_counts + j,
527*77c1e3ccSAndroid Build Coastguard Worker                   (n_colors - j - 1) * sizeof(top_color_counts[0]));
528*77c1e3ccSAndroid Build Coastguard Worker           top_color_counts[j].index = i;
529*77c1e3ccSAndroid Build Coastguard Worker           top_color_counts[j].count = count_buf[i];
530*77c1e3ccSAndroid Build Coastguard Worker         }
531*77c1e3ccSAndroid Build Coastguard Worker       }
532*77c1e3ccSAndroid Build Coastguard Worker     }
533*77c1e3ccSAndroid Build Coastguard Worker   }
534*77c1e3ccSAndroid Build Coastguard Worker   assert(n_color_count == n_colors);
535*77c1e3ccSAndroid Build Coastguard Worker 
536*77c1e3ccSAndroid Build Coastguard Worker   for (int i = 0; i < n_colors; ++i) {
537*77c1e3ccSAndroid Build Coastguard Worker     top_colors[i] = top_color_counts[i].index;
538*77c1e3ccSAndroid Build Coastguard Worker   }
539*77c1e3ccSAndroid Build Coastguard Worker }
540*77c1e3ccSAndroid Build Coastguard Worker 
av1_rd_pick_palette_intra_sby(const AV1_COMP * cpi,MACROBLOCK * x,BLOCK_SIZE bsize,int dc_mode_cost,MB_MODE_INFO * best_mbmi,uint8_t * best_palette_color_map,int64_t * best_rd,int * rate,int * rate_tokenonly,int64_t * distortion,uint8_t * skippable,int * beat_best_rd,PICK_MODE_CONTEXT * ctx,uint8_t * best_blk_skip,uint8_t * tx_type_map)541*77c1e3ccSAndroid Build Coastguard Worker void av1_rd_pick_palette_intra_sby(
542*77c1e3ccSAndroid Build Coastguard Worker     const AV1_COMP *cpi, MACROBLOCK *x, BLOCK_SIZE bsize, int dc_mode_cost,
543*77c1e3ccSAndroid Build Coastguard Worker     MB_MODE_INFO *best_mbmi, uint8_t *best_palette_color_map, int64_t *best_rd,
544*77c1e3ccSAndroid Build Coastguard Worker     int *rate, int *rate_tokenonly, int64_t *distortion, uint8_t *skippable,
545*77c1e3ccSAndroid Build Coastguard Worker     int *beat_best_rd, PICK_MODE_CONTEXT *ctx, uint8_t *best_blk_skip,
546*77c1e3ccSAndroid Build Coastguard Worker     uint8_t *tx_type_map) {
547*77c1e3ccSAndroid Build Coastguard Worker   MACROBLOCKD *const xd = &x->e_mbd;
548*77c1e3ccSAndroid Build Coastguard Worker   MB_MODE_INFO *const mbmi = xd->mi[0];
549*77c1e3ccSAndroid Build Coastguard Worker   assert(!is_inter_block(mbmi));
550*77c1e3ccSAndroid Build Coastguard Worker   assert(av1_allow_palette(cpi->common.features.allow_screen_content_tools,
551*77c1e3ccSAndroid Build Coastguard Worker                            bsize));
552*77c1e3ccSAndroid Build Coastguard Worker   assert(PALETTE_MAX_SIZE == 8);
553*77c1e3ccSAndroid Build Coastguard Worker   assert(PALETTE_MIN_SIZE == 2);
554*77c1e3ccSAndroid Build Coastguard Worker 
555*77c1e3ccSAndroid Build Coastguard Worker   const int src_stride = x->plane[0].src.stride;
556*77c1e3ccSAndroid Build Coastguard Worker   const uint8_t *const src = x->plane[0].src.buf;
557*77c1e3ccSAndroid Build Coastguard Worker   int block_width, block_height, rows, cols;
558*77c1e3ccSAndroid Build Coastguard Worker   av1_get_block_dimensions(bsize, 0, xd, &block_width, &block_height, &rows,
559*77c1e3ccSAndroid Build Coastguard Worker                            &cols);
560*77c1e3ccSAndroid Build Coastguard Worker   const SequenceHeader *const seq_params = cpi->common.seq_params;
561*77c1e3ccSAndroid Build Coastguard Worker   const int is_hbd = seq_params->use_highbitdepth;
562*77c1e3ccSAndroid Build Coastguard Worker   const int bit_depth = seq_params->bit_depth;
563*77c1e3ccSAndroid Build Coastguard Worker   const int discount_color_cost = cpi->sf.rt_sf.use_nonrd_pick_mode;
564*77c1e3ccSAndroid Build Coastguard Worker   int unused;
565*77c1e3ccSAndroid Build Coastguard Worker 
566*77c1e3ccSAndroid Build Coastguard Worker   int count_buf[1 << 12];  // Maximum (1 << 12) color levels.
567*77c1e3ccSAndroid Build Coastguard Worker   int colors, colors_threshold = 0;
568*77c1e3ccSAndroid Build Coastguard Worker   if (is_hbd) {
569*77c1e3ccSAndroid Build Coastguard Worker     int count_buf_8bit[1 << 8];  // Maximum (1 << 8) bins for hbd path.
570*77c1e3ccSAndroid Build Coastguard Worker     av1_count_colors_highbd(src, src_stride, rows, cols, bit_depth, count_buf,
571*77c1e3ccSAndroid Build Coastguard Worker                             count_buf_8bit, &colors_threshold, &colors);
572*77c1e3ccSAndroid Build Coastguard Worker   } else {
573*77c1e3ccSAndroid Build Coastguard Worker     av1_count_colors(src, src_stride, rows, cols, count_buf, &colors);
574*77c1e3ccSAndroid Build Coastguard Worker     colors_threshold = colors;
575*77c1e3ccSAndroid Build Coastguard Worker   }
576*77c1e3ccSAndroid Build Coastguard Worker 
577*77c1e3ccSAndroid Build Coastguard Worker   uint8_t *const color_map = xd->plane[0].color_index_map;
578*77c1e3ccSAndroid Build Coastguard Worker   int color_thresh_palette = x->color_palette_thresh;
579*77c1e3ccSAndroid Build Coastguard Worker   // Allow for larger color_threshold for palette search, based on color,
580*77c1e3ccSAndroid Build Coastguard Worker   // scene_change, and block source variance.
581*77c1e3ccSAndroid Build Coastguard Worker   // Since palette is Y based, only allow larger threshold if block
582*77c1e3ccSAndroid Build Coastguard Worker   // color_dist is below threshold.
583*77c1e3ccSAndroid Build Coastguard Worker   if (cpi->sf.rt_sf.use_nonrd_pick_mode &&
584*77c1e3ccSAndroid Build Coastguard Worker       cpi->sf.rt_sf.increase_color_thresh_palette && cpi->rc.high_source_sad &&
585*77c1e3ccSAndroid Build Coastguard Worker       x->source_variance > 50) {
586*77c1e3ccSAndroid Build Coastguard Worker     int64_t norm_color_dist = 0;
587*77c1e3ccSAndroid Build Coastguard Worker     if (x->color_sensitivity[0] || x->color_sensitivity[1]) {
588*77c1e3ccSAndroid Build Coastguard Worker       norm_color_dist = x->min_dist_inter_uv >>
589*77c1e3ccSAndroid Build Coastguard Worker                         (mi_size_wide_log2[bsize] + mi_size_high_log2[bsize]);
590*77c1e3ccSAndroid Build Coastguard Worker       if (x->color_sensitivity[0] && x->color_sensitivity[1])
591*77c1e3ccSAndroid Build Coastguard Worker         norm_color_dist = norm_color_dist >> 1;
592*77c1e3ccSAndroid Build Coastguard Worker     }
593*77c1e3ccSAndroid Build Coastguard Worker     if (norm_color_dist < 8000) color_thresh_palette += 20;
594*77c1e3ccSAndroid Build Coastguard Worker   }
595*77c1e3ccSAndroid Build Coastguard Worker   if (colors_threshold > 1 && colors_threshold <= color_thresh_palette) {
596*77c1e3ccSAndroid Build Coastguard Worker     int16_t *const data = x->palette_buffer->kmeans_data_buf;
597*77c1e3ccSAndroid Build Coastguard Worker     int16_t centroids[PALETTE_MAX_SIZE];
598*77c1e3ccSAndroid Build Coastguard Worker     int lower_bound, upper_bound;
599*77c1e3ccSAndroid Build Coastguard Worker     fill_data_and_get_bounds(src, src_stride, rows, cols, is_hbd, data,
600*77c1e3ccSAndroid Build Coastguard Worker                              &lower_bound, &upper_bound);
601*77c1e3ccSAndroid Build Coastguard Worker 
602*77c1e3ccSAndroid Build Coastguard Worker     mbmi->mode = DC_PRED;
603*77c1e3ccSAndroid Build Coastguard Worker     mbmi->filter_intra_mode_info.use_filter_intra = 0;
604*77c1e3ccSAndroid Build Coastguard Worker 
605*77c1e3ccSAndroid Build Coastguard Worker     uint16_t color_cache[2 * PALETTE_MAX_SIZE];
606*77c1e3ccSAndroid Build Coastguard Worker     const int n_cache = av1_get_palette_cache(xd, 0, color_cache);
607*77c1e3ccSAndroid Build Coastguard Worker 
608*77c1e3ccSAndroid Build Coastguard Worker     // Find the dominant colors, stored in top_colors[].
609*77c1e3ccSAndroid Build Coastguard Worker     int16_t top_colors[PALETTE_MAX_SIZE] = { 0 };
610*77c1e3ccSAndroid Build Coastguard Worker     find_top_colors(count_buf, bit_depth, AOMMIN(colors, PALETTE_MAX_SIZE),
611*77c1e3ccSAndroid Build Coastguard Worker                     top_colors);
612*77c1e3ccSAndroid Build Coastguard Worker 
613*77c1e3ccSAndroid Build Coastguard Worker     // The following are the approaches used for header rdcost based gating
614*77c1e3ccSAndroid Build Coastguard Worker     // for early termination for different values of prune_palette_search_level.
615*77c1e3ccSAndroid Build Coastguard Worker     // 0: Pruning based on header rdcost for ascending order palette_size
616*77c1e3ccSAndroid Build Coastguard Worker     // search.
617*77c1e3ccSAndroid Build Coastguard Worker     // 1: When colors > PALETTE_MIN_SIZE, enabled only for coarse palette_size
618*77c1e3ccSAndroid Build Coastguard Worker     // search and for finer search do_header_rd_based_gating parameter is
619*77c1e3ccSAndroid Build Coastguard Worker     // explicitly passed as 'false'.
620*77c1e3ccSAndroid Build Coastguard Worker     // 2: Enabled only for ascending order palette_size search and for
621*77c1e3ccSAndroid Build Coastguard Worker     // descending order search do_header_rd_based_gating parameter is explicitly
622*77c1e3ccSAndroid Build Coastguard Worker     // passed as 'false'.
623*77c1e3ccSAndroid Build Coastguard Worker     const bool do_header_rd_based_gating =
624*77c1e3ccSAndroid Build Coastguard Worker         cpi->sf.intra_sf.prune_luma_palette_size_search_level != 0;
625*77c1e3ccSAndroid Build Coastguard Worker 
626*77c1e3ccSAndroid Build Coastguard Worker     // TODO([email protected]): Try to avoid duplicate computation in cases
627*77c1e3ccSAndroid Build Coastguard Worker     // where the dominant colors and the k-means results are similar.
628*77c1e3ccSAndroid Build Coastguard Worker     if ((cpi->sf.intra_sf.prune_palette_search_level == 1) &&
629*77c1e3ccSAndroid Build Coastguard Worker         (colors > PALETTE_MIN_SIZE)) {
630*77c1e3ccSAndroid Build Coastguard Worker       // Start index and step size below are chosen to evaluate unique
631*77c1e3ccSAndroid Build Coastguard Worker       // candidates in neighbor search, in case a winner candidate is found in
632*77c1e3ccSAndroid Build Coastguard Worker       // coarse search. Example,
633*77c1e3ccSAndroid Build Coastguard Worker       // 1) 8 colors (end_n = 8): 2,3,4,5,6,7,8. start_n is chosen as 2 and step
634*77c1e3ccSAndroid Build Coastguard Worker       // size is chosen as 3. Therefore, coarse search will evaluate 2, 5 and 8.
635*77c1e3ccSAndroid Build Coastguard Worker       // If winner is found at 5, then 4 and 6 are evaluated. Similarly, for 2
636*77c1e3ccSAndroid Build Coastguard Worker       // (3) and 8 (7).
637*77c1e3ccSAndroid Build Coastguard Worker       // 2) 7 colors (end_n = 7): 2,3,4,5,6,7. If start_n is chosen as 2 (same
638*77c1e3ccSAndroid Build Coastguard Worker       // as for 8 colors) then step size should also be 2, to cover all
639*77c1e3ccSAndroid Build Coastguard Worker       // candidates. Coarse search will evaluate 2, 4 and 6. If winner is either
640*77c1e3ccSAndroid Build Coastguard Worker       // 2 or 4, 3 will be evaluated. Instead, if start_n=3 and step_size=3,
641*77c1e3ccSAndroid Build Coastguard Worker       // coarse search will evaluate 3 and 6. For the winner, unique neighbors
642*77c1e3ccSAndroid Build Coastguard Worker       // (3: 2,4 or 6: 5,7) would be evaluated.
643*77c1e3ccSAndroid Build Coastguard Worker 
644*77c1e3ccSAndroid Build Coastguard Worker       // Start index for coarse palette search for dominant colors and k-means
645*77c1e3ccSAndroid Build Coastguard Worker       const uint8_t start_n_lookup_table[PALETTE_MAX_SIZE + 1] = { 0, 0, 0,
646*77c1e3ccSAndroid Build Coastguard Worker                                                                    3, 3, 2,
647*77c1e3ccSAndroid Build Coastguard Worker                                                                    3, 3, 2 };
648*77c1e3ccSAndroid Build Coastguard Worker       // Step size for coarse palette search for dominant colors and k-means
649*77c1e3ccSAndroid Build Coastguard Worker       const uint8_t step_size_lookup_table[PALETTE_MAX_SIZE + 1] = { 0, 0, 0,
650*77c1e3ccSAndroid Build Coastguard Worker                                                                      3, 3, 3,
651*77c1e3ccSAndroid Build Coastguard Worker                                                                      3, 3, 3 };
652*77c1e3ccSAndroid Build Coastguard Worker 
653*77c1e3ccSAndroid Build Coastguard Worker       // Choose the start index and step size for coarse search based on number
654*77c1e3ccSAndroid Build Coastguard Worker       // of colors
655*77c1e3ccSAndroid Build Coastguard Worker       const int max_n = AOMMIN(colors, PALETTE_MAX_SIZE);
656*77c1e3ccSAndroid Build Coastguard Worker       const int min_n = start_n_lookup_table[max_n];
657*77c1e3ccSAndroid Build Coastguard Worker       const int step_size = step_size_lookup_table[max_n];
658*77c1e3ccSAndroid Build Coastguard Worker       assert(min_n >= PALETTE_MIN_SIZE);
659*77c1e3ccSAndroid Build Coastguard Worker       // Perform top color coarse palette search to find the winner candidate
660*77c1e3ccSAndroid Build Coastguard Worker       const int top_color_winner = perform_top_color_palette_search(
661*77c1e3ccSAndroid Build Coastguard Worker           cpi, x, mbmi, bsize, dc_mode_cost, data, top_colors, min_n, max_n + 1,
662*77c1e3ccSAndroid Build Coastguard Worker           step_size, do_header_rd_based_gating, &unused, color_cache, n_cache,
663*77c1e3ccSAndroid Build Coastguard Worker           best_mbmi, best_palette_color_map, best_rd, rate, rate_tokenonly,
664*77c1e3ccSAndroid Build Coastguard Worker           distortion, skippable, beat_best_rd, ctx, best_blk_skip, tx_type_map,
665*77c1e3ccSAndroid Build Coastguard Worker           discount_color_cost);
666*77c1e3ccSAndroid Build Coastguard Worker       // Evaluate neighbors for the winner color (if winner is found) in the
667*77c1e3ccSAndroid Build Coastguard Worker       // above coarse search for dominant colors
668*77c1e3ccSAndroid Build Coastguard Worker       if (top_color_winner <= max_n) {
669*77c1e3ccSAndroid Build Coastguard Worker         int stage2_min_n, stage2_max_n, stage2_step_size;
670*77c1e3ccSAndroid Build Coastguard Worker         set_stage2_params(&stage2_min_n, &stage2_max_n, &stage2_step_size,
671*77c1e3ccSAndroid Build Coastguard Worker                           top_color_winner, max_n);
672*77c1e3ccSAndroid Build Coastguard Worker         // perform finer search for the winner candidate
673*77c1e3ccSAndroid Build Coastguard Worker         perform_top_color_palette_search(
674*77c1e3ccSAndroid Build Coastguard Worker             cpi, x, mbmi, bsize, dc_mode_cost, data, top_colors, stage2_min_n,
675*77c1e3ccSAndroid Build Coastguard Worker             stage2_max_n + 1, stage2_step_size,
676*77c1e3ccSAndroid Build Coastguard Worker             /*do_header_rd_based_gating=*/false, &unused, color_cache, n_cache,
677*77c1e3ccSAndroid Build Coastguard Worker             best_mbmi, best_palette_color_map, best_rd, rate, rate_tokenonly,
678*77c1e3ccSAndroid Build Coastguard Worker             distortion, skippable, beat_best_rd, ctx, best_blk_skip,
679*77c1e3ccSAndroid Build Coastguard Worker             tx_type_map, discount_color_cost);
680*77c1e3ccSAndroid Build Coastguard Worker       }
681*77c1e3ccSAndroid Build Coastguard Worker       // K-means clustering.
682*77c1e3ccSAndroid Build Coastguard Worker       // Perform k-means coarse palette search to find the winner candidate
683*77c1e3ccSAndroid Build Coastguard Worker       const int k_means_winner = perform_k_means_palette_search(
684*77c1e3ccSAndroid Build Coastguard Worker           cpi, x, mbmi, bsize, dc_mode_cost, data, lower_bound, upper_bound,
685*77c1e3ccSAndroid Build Coastguard Worker           min_n, max_n + 1, step_size, do_header_rd_based_gating, &unused,
686*77c1e3ccSAndroid Build Coastguard Worker           color_cache, n_cache, best_mbmi, best_palette_color_map, best_rd,
687*77c1e3ccSAndroid Build Coastguard Worker           rate, rate_tokenonly, distortion, skippable, beat_best_rd, ctx,
688*77c1e3ccSAndroid Build Coastguard Worker           best_blk_skip, tx_type_map, color_map, rows * cols,
689*77c1e3ccSAndroid Build Coastguard Worker           discount_color_cost);
690*77c1e3ccSAndroid Build Coastguard Worker       // Evaluate neighbors for the winner color (if winner is found) in the
691*77c1e3ccSAndroid Build Coastguard Worker       // above coarse search for k-means
692*77c1e3ccSAndroid Build Coastguard Worker       if (k_means_winner <= max_n) {
693*77c1e3ccSAndroid Build Coastguard Worker         int start_n_stage2, end_n_stage2, step_size_stage2;
694*77c1e3ccSAndroid Build Coastguard Worker         set_stage2_params(&start_n_stage2, &end_n_stage2, &step_size_stage2,
695*77c1e3ccSAndroid Build Coastguard Worker                           k_means_winner, max_n);
696*77c1e3ccSAndroid Build Coastguard Worker         // perform finer search for the winner candidate
697*77c1e3ccSAndroid Build Coastguard Worker         perform_k_means_palette_search(
698*77c1e3ccSAndroid Build Coastguard Worker             cpi, x, mbmi, bsize, dc_mode_cost, data, lower_bound, upper_bound,
699*77c1e3ccSAndroid Build Coastguard Worker             start_n_stage2, end_n_stage2 + 1, step_size_stage2,
700*77c1e3ccSAndroid Build Coastguard Worker             /*do_header_rd_based_gating=*/false, &unused, color_cache, n_cache,
701*77c1e3ccSAndroid Build Coastguard Worker             best_mbmi, best_palette_color_map, best_rd, rate, rate_tokenonly,
702*77c1e3ccSAndroid Build Coastguard Worker             distortion, skippable, beat_best_rd, ctx, best_blk_skip,
703*77c1e3ccSAndroid Build Coastguard Worker             tx_type_map, color_map, rows * cols, discount_color_cost);
704*77c1e3ccSAndroid Build Coastguard Worker       }
705*77c1e3ccSAndroid Build Coastguard Worker     } else {
706*77c1e3ccSAndroid Build Coastguard Worker       const int max_n = AOMMIN(colors, PALETTE_MAX_SIZE),
707*77c1e3ccSAndroid Build Coastguard Worker                 min_n = PALETTE_MIN_SIZE;
708*77c1e3ccSAndroid Build Coastguard Worker       // Perform top color palette search in ascending order
709*77c1e3ccSAndroid Build Coastguard Worker       int last_n_searched = min_n;
710*77c1e3ccSAndroid Build Coastguard Worker       perform_top_color_palette_search(
711*77c1e3ccSAndroid Build Coastguard Worker           cpi, x, mbmi, bsize, dc_mode_cost, data, top_colors, min_n, max_n + 1,
712*77c1e3ccSAndroid Build Coastguard Worker           1, do_header_rd_based_gating, &last_n_searched, color_cache, n_cache,
713*77c1e3ccSAndroid Build Coastguard Worker           best_mbmi, best_palette_color_map, best_rd, rate, rate_tokenonly,
714*77c1e3ccSAndroid Build Coastguard Worker           distortion, skippable, beat_best_rd, ctx, best_blk_skip, tx_type_map,
715*77c1e3ccSAndroid Build Coastguard Worker           discount_color_cost);
716*77c1e3ccSAndroid Build Coastguard Worker       if (last_n_searched < max_n) {
717*77c1e3ccSAndroid Build Coastguard Worker         // Search in descending order until we get to the previous best
718*77c1e3ccSAndroid Build Coastguard Worker         perform_top_color_palette_search(
719*77c1e3ccSAndroid Build Coastguard Worker             cpi, x, mbmi, bsize, dc_mode_cost, data, top_colors, max_n,
720*77c1e3ccSAndroid Build Coastguard Worker             last_n_searched, -1, /*do_header_rd_based_gating=*/false, &unused,
721*77c1e3ccSAndroid Build Coastguard Worker             color_cache, n_cache, best_mbmi, best_palette_color_map, best_rd,
722*77c1e3ccSAndroid Build Coastguard Worker             rate, rate_tokenonly, distortion, skippable, beat_best_rd, ctx,
723*77c1e3ccSAndroid Build Coastguard Worker             best_blk_skip, tx_type_map, discount_color_cost);
724*77c1e3ccSAndroid Build Coastguard Worker       }
725*77c1e3ccSAndroid Build Coastguard Worker       // K-means clustering.
726*77c1e3ccSAndroid Build Coastguard Worker       if (colors == PALETTE_MIN_SIZE) {
727*77c1e3ccSAndroid Build Coastguard Worker         // Special case: These colors automatically become the centroids.
728*77c1e3ccSAndroid Build Coastguard Worker         assert(colors == 2);
729*77c1e3ccSAndroid Build Coastguard Worker         centroids[0] = lower_bound;
730*77c1e3ccSAndroid Build Coastguard Worker         centroids[1] = upper_bound;
731*77c1e3ccSAndroid Build Coastguard Worker         palette_rd_y(cpi, x, mbmi, bsize, dc_mode_cost, data, centroids, colors,
732*77c1e3ccSAndroid Build Coastguard Worker                      color_cache, n_cache, /*do_header_rd_based_gating=*/false,
733*77c1e3ccSAndroid Build Coastguard Worker                      best_mbmi, best_palette_color_map, best_rd, rate,
734*77c1e3ccSAndroid Build Coastguard Worker                      rate_tokenonly, distortion, skippable, beat_best_rd, ctx,
735*77c1e3ccSAndroid Build Coastguard Worker                      best_blk_skip, tx_type_map, NULL, NULL,
736*77c1e3ccSAndroid Build Coastguard Worker                      discount_color_cost);
737*77c1e3ccSAndroid Build Coastguard Worker       } else {
738*77c1e3ccSAndroid Build Coastguard Worker         // Perform k-means palette search in ascending order
739*77c1e3ccSAndroid Build Coastguard Worker         last_n_searched = min_n;
740*77c1e3ccSAndroid Build Coastguard Worker         perform_k_means_palette_search(
741*77c1e3ccSAndroid Build Coastguard Worker             cpi, x, mbmi, bsize, dc_mode_cost, data, lower_bound, upper_bound,
742*77c1e3ccSAndroid Build Coastguard Worker             min_n, max_n + 1, 1, do_header_rd_based_gating, &last_n_searched,
743*77c1e3ccSAndroid Build Coastguard Worker             color_cache, n_cache, best_mbmi, best_palette_color_map, best_rd,
744*77c1e3ccSAndroid Build Coastguard Worker             rate, rate_tokenonly, distortion, skippable, beat_best_rd, ctx,
745*77c1e3ccSAndroid Build Coastguard Worker             best_blk_skip, tx_type_map, color_map, rows * cols,
746*77c1e3ccSAndroid Build Coastguard Worker             discount_color_cost);
747*77c1e3ccSAndroid Build Coastguard Worker         if (last_n_searched < max_n) {
748*77c1e3ccSAndroid Build Coastguard Worker           // Search in descending order until we get to the previous best
749*77c1e3ccSAndroid Build Coastguard Worker           perform_k_means_palette_search(
750*77c1e3ccSAndroid Build Coastguard Worker               cpi, x, mbmi, bsize, dc_mode_cost, data, lower_bound, upper_bound,
751*77c1e3ccSAndroid Build Coastguard Worker               max_n, last_n_searched, -1, /*do_header_rd_based_gating=*/false,
752*77c1e3ccSAndroid Build Coastguard Worker               &unused, color_cache, n_cache, best_mbmi, best_palette_color_map,
753*77c1e3ccSAndroid Build Coastguard Worker               best_rd, rate, rate_tokenonly, distortion, skippable,
754*77c1e3ccSAndroid Build Coastguard Worker               beat_best_rd, ctx, best_blk_skip, tx_type_map, color_map,
755*77c1e3ccSAndroid Build Coastguard Worker               rows * cols, discount_color_cost);
756*77c1e3ccSAndroid Build Coastguard Worker         }
757*77c1e3ccSAndroid Build Coastguard Worker       }
758*77c1e3ccSAndroid Build Coastguard Worker     }
759*77c1e3ccSAndroid Build Coastguard Worker   }
760*77c1e3ccSAndroid Build Coastguard Worker 
761*77c1e3ccSAndroid Build Coastguard Worker   if (best_mbmi->palette_mode_info.palette_size[0] > 0) {
762*77c1e3ccSAndroid Build Coastguard Worker     memcpy(color_map, best_palette_color_map,
763*77c1e3ccSAndroid Build Coastguard Worker            block_width * block_height * sizeof(best_palette_color_map[0]));
764*77c1e3ccSAndroid Build Coastguard Worker     // Gather the stats to determine whether to use screen content tools in
765*77c1e3ccSAndroid Build Coastguard Worker     // function av1_determine_sc_tools_with_encoding().
766*77c1e3ccSAndroid Build Coastguard Worker     x->palette_pixels += (block_width * block_height);
767*77c1e3ccSAndroid Build Coastguard Worker   }
768*77c1e3ccSAndroid Build Coastguard Worker   *mbmi = *best_mbmi;
769*77c1e3ccSAndroid Build Coastguard Worker }
770*77c1e3ccSAndroid Build Coastguard Worker 
av1_rd_pick_palette_intra_sbuv(const AV1_COMP * cpi,MACROBLOCK * x,int dc_mode_cost,uint8_t * best_palette_color_map,MB_MODE_INFO * const best_mbmi,int64_t * best_rd,int * rate,int * rate_tokenonly,int64_t * distortion,uint8_t * skippable)771*77c1e3ccSAndroid Build Coastguard Worker void av1_rd_pick_palette_intra_sbuv(const AV1_COMP *cpi, MACROBLOCK *x,
772*77c1e3ccSAndroid Build Coastguard Worker                                     int dc_mode_cost,
773*77c1e3ccSAndroid Build Coastguard Worker                                     uint8_t *best_palette_color_map,
774*77c1e3ccSAndroid Build Coastguard Worker                                     MB_MODE_INFO *const best_mbmi,
775*77c1e3ccSAndroid Build Coastguard Worker                                     int64_t *best_rd, int *rate,
776*77c1e3ccSAndroid Build Coastguard Worker                                     int *rate_tokenonly, int64_t *distortion,
777*77c1e3ccSAndroid Build Coastguard Worker                                     uint8_t *skippable) {
778*77c1e3ccSAndroid Build Coastguard Worker   MACROBLOCKD *const xd = &x->e_mbd;
779*77c1e3ccSAndroid Build Coastguard Worker   MB_MODE_INFO *const mbmi = xd->mi[0];
780*77c1e3ccSAndroid Build Coastguard Worker   assert(!is_inter_block(mbmi));
781*77c1e3ccSAndroid Build Coastguard Worker   assert(av1_allow_palette(cpi->common.features.allow_screen_content_tools,
782*77c1e3ccSAndroid Build Coastguard Worker                            mbmi->bsize));
783*77c1e3ccSAndroid Build Coastguard Worker   PALETTE_MODE_INFO *const pmi = &mbmi->palette_mode_info;
784*77c1e3ccSAndroid Build Coastguard Worker   const BLOCK_SIZE bsize = mbmi->bsize;
785*77c1e3ccSAndroid Build Coastguard Worker   const SequenceHeader *const seq_params = cpi->common.seq_params;
786*77c1e3ccSAndroid Build Coastguard Worker   int this_rate;
787*77c1e3ccSAndroid Build Coastguard Worker   int64_t this_rd;
788*77c1e3ccSAndroid Build Coastguard Worker   int colors_u, colors_v;
789*77c1e3ccSAndroid Build Coastguard Worker   int colors_threshold_u = 0, colors_threshold_v = 0, colors_threshold = 0;
790*77c1e3ccSAndroid Build Coastguard Worker   const int src_stride = x->plane[1].src.stride;
791*77c1e3ccSAndroid Build Coastguard Worker   const uint8_t *const src_u = x->plane[1].src.buf;
792*77c1e3ccSAndroid Build Coastguard Worker   const uint8_t *const src_v = x->plane[2].src.buf;
793*77c1e3ccSAndroid Build Coastguard Worker   uint8_t *const color_map = xd->plane[1].color_index_map;
794*77c1e3ccSAndroid Build Coastguard Worker   RD_STATS tokenonly_rd_stats;
795*77c1e3ccSAndroid Build Coastguard Worker   int plane_block_width, plane_block_height, rows, cols;
796*77c1e3ccSAndroid Build Coastguard Worker   av1_get_block_dimensions(bsize, 1, xd, &plane_block_width,
797*77c1e3ccSAndroid Build Coastguard Worker                            &plane_block_height, &rows, &cols);
798*77c1e3ccSAndroid Build Coastguard Worker 
799*77c1e3ccSAndroid Build Coastguard Worker   mbmi->uv_mode = UV_DC_PRED;
800*77c1e3ccSAndroid Build Coastguard Worker   if (seq_params->use_highbitdepth) {
801*77c1e3ccSAndroid Build Coastguard Worker     int count_buf[1 << 12];      // Maximum (1 << 12) color levels.
802*77c1e3ccSAndroid Build Coastguard Worker     int count_buf_8bit[1 << 8];  // Maximum (1 << 8) bins for hbd path.
803*77c1e3ccSAndroid Build Coastguard Worker     av1_count_colors_highbd(src_u, src_stride, rows, cols,
804*77c1e3ccSAndroid Build Coastguard Worker                             seq_params->bit_depth, count_buf, count_buf_8bit,
805*77c1e3ccSAndroid Build Coastguard Worker                             &colors_threshold_u, &colors_u);
806*77c1e3ccSAndroid Build Coastguard Worker     av1_count_colors_highbd(src_v, src_stride, rows, cols,
807*77c1e3ccSAndroid Build Coastguard Worker                             seq_params->bit_depth, count_buf, count_buf_8bit,
808*77c1e3ccSAndroid Build Coastguard Worker                             &colors_threshold_v, &colors_v);
809*77c1e3ccSAndroid Build Coastguard Worker   } else {
810*77c1e3ccSAndroid Build Coastguard Worker     int count_buf[1 << 8];
811*77c1e3ccSAndroid Build Coastguard Worker     av1_count_colors(src_u, src_stride, rows, cols, count_buf, &colors_u);
812*77c1e3ccSAndroid Build Coastguard Worker     av1_count_colors(src_v, src_stride, rows, cols, count_buf, &colors_v);
813*77c1e3ccSAndroid Build Coastguard Worker     colors_threshold_u = colors_u;
814*77c1e3ccSAndroid Build Coastguard Worker     colors_threshold_v = colors_v;
815*77c1e3ccSAndroid Build Coastguard Worker   }
816*77c1e3ccSAndroid Build Coastguard Worker 
817*77c1e3ccSAndroid Build Coastguard Worker   uint16_t color_cache[2 * PALETTE_MAX_SIZE];
818*77c1e3ccSAndroid Build Coastguard Worker   const int n_cache = av1_get_palette_cache(xd, 1, color_cache);
819*77c1e3ccSAndroid Build Coastguard Worker 
820*77c1e3ccSAndroid Build Coastguard Worker   colors_threshold = colors_threshold_u > colors_threshold_v
821*77c1e3ccSAndroid Build Coastguard Worker                          ? colors_threshold_u
822*77c1e3ccSAndroid Build Coastguard Worker                          : colors_threshold_v;
823*77c1e3ccSAndroid Build Coastguard Worker   if (colors_threshold > 1 && colors_threshold <= 64) {
824*77c1e3ccSAndroid Build Coastguard Worker     int r, c, n, i, j;
825*77c1e3ccSAndroid Build Coastguard Worker     const int max_itr = 50;
826*77c1e3ccSAndroid Build Coastguard Worker     int lb_u, ub_u, val_u;
827*77c1e3ccSAndroid Build Coastguard Worker     int lb_v, ub_v, val_v;
828*77c1e3ccSAndroid Build Coastguard Worker     int16_t *const data = x->palette_buffer->kmeans_data_buf;
829*77c1e3ccSAndroid Build Coastguard Worker     int16_t centroids[2 * PALETTE_MAX_SIZE];
830*77c1e3ccSAndroid Build Coastguard Worker 
831*77c1e3ccSAndroid Build Coastguard Worker     uint16_t *src_u16 = CONVERT_TO_SHORTPTR(src_u);
832*77c1e3ccSAndroid Build Coastguard Worker     uint16_t *src_v16 = CONVERT_TO_SHORTPTR(src_v);
833*77c1e3ccSAndroid Build Coastguard Worker     if (seq_params->use_highbitdepth) {
834*77c1e3ccSAndroid Build Coastguard Worker       lb_u = src_u16[0];
835*77c1e3ccSAndroid Build Coastguard Worker       ub_u = src_u16[0];
836*77c1e3ccSAndroid Build Coastguard Worker       lb_v = src_v16[0];
837*77c1e3ccSAndroid Build Coastguard Worker       ub_v = src_v16[0];
838*77c1e3ccSAndroid Build Coastguard Worker     } else {
839*77c1e3ccSAndroid Build Coastguard Worker       lb_u = src_u[0];
840*77c1e3ccSAndroid Build Coastguard Worker       ub_u = src_u[0];
841*77c1e3ccSAndroid Build Coastguard Worker       lb_v = src_v[0];
842*77c1e3ccSAndroid Build Coastguard Worker       ub_v = src_v[0];
843*77c1e3ccSAndroid Build Coastguard Worker     }
844*77c1e3ccSAndroid Build Coastguard Worker 
845*77c1e3ccSAndroid Build Coastguard Worker     for (r = 0; r < rows; ++r) {
846*77c1e3ccSAndroid Build Coastguard Worker       for (c = 0; c < cols; ++c) {
847*77c1e3ccSAndroid Build Coastguard Worker         if (seq_params->use_highbitdepth) {
848*77c1e3ccSAndroid Build Coastguard Worker           val_u = src_u16[r * src_stride + c];
849*77c1e3ccSAndroid Build Coastguard Worker           val_v = src_v16[r * src_stride + c];
850*77c1e3ccSAndroid Build Coastguard Worker           data[(r * cols + c) * 2] = val_u;
851*77c1e3ccSAndroid Build Coastguard Worker           data[(r * cols + c) * 2 + 1] = val_v;
852*77c1e3ccSAndroid Build Coastguard Worker         } else {
853*77c1e3ccSAndroid Build Coastguard Worker           val_u = src_u[r * src_stride + c];
854*77c1e3ccSAndroid Build Coastguard Worker           val_v = src_v[r * src_stride + c];
855*77c1e3ccSAndroid Build Coastguard Worker           data[(r * cols + c) * 2] = val_u;
856*77c1e3ccSAndroid Build Coastguard Worker           data[(r * cols + c) * 2 + 1] = val_v;
857*77c1e3ccSAndroid Build Coastguard Worker         }
858*77c1e3ccSAndroid Build Coastguard Worker         if (val_u < lb_u)
859*77c1e3ccSAndroid Build Coastguard Worker           lb_u = val_u;
860*77c1e3ccSAndroid Build Coastguard Worker         else if (val_u > ub_u)
861*77c1e3ccSAndroid Build Coastguard Worker           ub_u = val_u;
862*77c1e3ccSAndroid Build Coastguard Worker         if (val_v < lb_v)
863*77c1e3ccSAndroid Build Coastguard Worker           lb_v = val_v;
864*77c1e3ccSAndroid Build Coastguard Worker         else if (val_v > ub_v)
865*77c1e3ccSAndroid Build Coastguard Worker           ub_v = val_v;
866*77c1e3ccSAndroid Build Coastguard Worker       }
867*77c1e3ccSAndroid Build Coastguard Worker     }
868*77c1e3ccSAndroid Build Coastguard Worker 
869*77c1e3ccSAndroid Build Coastguard Worker     const int colors = colors_u > colors_v ? colors_u : colors_v;
870*77c1e3ccSAndroid Build Coastguard Worker     const int max_colors =
871*77c1e3ccSAndroid Build Coastguard Worker         colors > PALETTE_MAX_SIZE ? PALETTE_MAX_SIZE : colors;
872*77c1e3ccSAndroid Build Coastguard Worker     for (n = PALETTE_MIN_SIZE; n <= max_colors; ++n) {
873*77c1e3ccSAndroid Build Coastguard Worker       for (i = 0; i < n; ++i) {
874*77c1e3ccSAndroid Build Coastguard Worker         centroids[i * 2] = lb_u + (2 * i + 1) * (ub_u - lb_u) / n / 2;
875*77c1e3ccSAndroid Build Coastguard Worker         centroids[i * 2 + 1] = lb_v + (2 * i + 1) * (ub_v - lb_v) / n / 2;
876*77c1e3ccSAndroid Build Coastguard Worker       }
877*77c1e3ccSAndroid Build Coastguard Worker       av1_k_means(data, centroids, color_map, rows * cols, n, 2, max_itr);
878*77c1e3ccSAndroid Build Coastguard Worker       optimize_palette_colors(color_cache, n_cache, n, 2, centroids,
879*77c1e3ccSAndroid Build Coastguard Worker                               cpi->common.seq_params->bit_depth);
880*77c1e3ccSAndroid Build Coastguard Worker       // Sort the U channel colors in ascending order.
881*77c1e3ccSAndroid Build Coastguard Worker       for (i = 0; i < 2 * (n - 1); i += 2) {
882*77c1e3ccSAndroid Build Coastguard Worker         int min_idx = i;
883*77c1e3ccSAndroid Build Coastguard Worker         int min_val = centroids[i];
884*77c1e3ccSAndroid Build Coastguard Worker         for (j = i + 2; j < 2 * n; j += 2)
885*77c1e3ccSAndroid Build Coastguard Worker           if (centroids[j] < min_val) min_val = centroids[j], min_idx = j;
886*77c1e3ccSAndroid Build Coastguard Worker         if (min_idx != i) {
887*77c1e3ccSAndroid Build Coastguard Worker           int temp_u = centroids[i], temp_v = centroids[i + 1];
888*77c1e3ccSAndroid Build Coastguard Worker           centroids[i] = centroids[min_idx];
889*77c1e3ccSAndroid Build Coastguard Worker           centroids[i + 1] = centroids[min_idx + 1];
890*77c1e3ccSAndroid Build Coastguard Worker           centroids[min_idx] = temp_u, centroids[min_idx + 1] = temp_v;
891*77c1e3ccSAndroid Build Coastguard Worker         }
892*77c1e3ccSAndroid Build Coastguard Worker       }
893*77c1e3ccSAndroid Build Coastguard Worker       av1_calc_indices(data, centroids, color_map, rows * cols, n, 2);
894*77c1e3ccSAndroid Build Coastguard Worker       extend_palette_color_map(color_map, cols, rows, plane_block_width,
895*77c1e3ccSAndroid Build Coastguard Worker                                plane_block_height);
896*77c1e3ccSAndroid Build Coastguard Worker       pmi->palette_size[1] = n;
897*77c1e3ccSAndroid Build Coastguard Worker       for (i = 1; i < 3; ++i) {
898*77c1e3ccSAndroid Build Coastguard Worker         for (j = 0; j < n; ++j) {
899*77c1e3ccSAndroid Build Coastguard Worker           if (seq_params->use_highbitdepth)
900*77c1e3ccSAndroid Build Coastguard Worker             pmi->palette_colors[i * PALETTE_MAX_SIZE + j] = clip_pixel_highbd(
901*77c1e3ccSAndroid Build Coastguard Worker                 (int)centroids[j * 2 + i - 1], seq_params->bit_depth);
902*77c1e3ccSAndroid Build Coastguard Worker           else
903*77c1e3ccSAndroid Build Coastguard Worker             pmi->palette_colors[i * PALETTE_MAX_SIZE + j] =
904*77c1e3ccSAndroid Build Coastguard Worker                 clip_pixel((int)centroids[j * 2 + i - 1]);
905*77c1e3ccSAndroid Build Coastguard Worker         }
906*77c1e3ccSAndroid Build Coastguard Worker       }
907*77c1e3ccSAndroid Build Coastguard Worker 
908*77c1e3ccSAndroid Build Coastguard Worker       if (cpi->sf.intra_sf.early_term_chroma_palette_size_search) {
909*77c1e3ccSAndroid Build Coastguard Worker         const int palette_mode_rate =
910*77c1e3ccSAndroid Build Coastguard Worker             intra_mode_info_cost_uv(cpi, x, mbmi, bsize, dc_mode_cost);
911*77c1e3ccSAndroid Build Coastguard Worker         const int64_t header_rd = RDCOST(x->rdmult, palette_mode_rate, 0);
912*77c1e3ccSAndroid Build Coastguard Worker         // Terminate further palette_size search, if header cost corresponding
913*77c1e3ccSAndroid Build Coastguard Worker         // to lower palette_size is more than the best_rd.
914*77c1e3ccSAndroid Build Coastguard Worker         if (header_rd >= *best_rd) break;
915*77c1e3ccSAndroid Build Coastguard Worker         av1_txfm_uvrd(cpi, x, &tokenonly_rd_stats, bsize, *best_rd);
916*77c1e3ccSAndroid Build Coastguard Worker         if (tokenonly_rd_stats.rate == INT_MAX) continue;
917*77c1e3ccSAndroid Build Coastguard Worker         this_rate = tokenonly_rd_stats.rate + palette_mode_rate;
918*77c1e3ccSAndroid Build Coastguard Worker       } else {
919*77c1e3ccSAndroid Build Coastguard Worker         av1_txfm_uvrd(cpi, x, &tokenonly_rd_stats, bsize, *best_rd);
920*77c1e3ccSAndroid Build Coastguard Worker         if (tokenonly_rd_stats.rate == INT_MAX) continue;
921*77c1e3ccSAndroid Build Coastguard Worker         this_rate = tokenonly_rd_stats.rate +
922*77c1e3ccSAndroid Build Coastguard Worker                     intra_mode_info_cost_uv(cpi, x, mbmi, bsize, dc_mode_cost);
923*77c1e3ccSAndroid Build Coastguard Worker       }
924*77c1e3ccSAndroid Build Coastguard Worker 
925*77c1e3ccSAndroid Build Coastguard Worker       this_rd = RDCOST(x->rdmult, this_rate, tokenonly_rd_stats.dist);
926*77c1e3ccSAndroid Build Coastguard Worker       if (this_rd < *best_rd) {
927*77c1e3ccSAndroid Build Coastguard Worker         *best_rd = this_rd;
928*77c1e3ccSAndroid Build Coastguard Worker         *best_mbmi = *mbmi;
929*77c1e3ccSAndroid Build Coastguard Worker         memcpy(best_palette_color_map, color_map,
930*77c1e3ccSAndroid Build Coastguard Worker                plane_block_width * plane_block_height *
931*77c1e3ccSAndroid Build Coastguard Worker                    sizeof(best_palette_color_map[0]));
932*77c1e3ccSAndroid Build Coastguard Worker         *rate = this_rate;
933*77c1e3ccSAndroid Build Coastguard Worker         *distortion = tokenonly_rd_stats.dist;
934*77c1e3ccSAndroid Build Coastguard Worker         *rate_tokenonly = tokenonly_rd_stats.rate;
935*77c1e3ccSAndroid Build Coastguard Worker         *skippable = tokenonly_rd_stats.skip_txfm;
936*77c1e3ccSAndroid Build Coastguard Worker       }
937*77c1e3ccSAndroid Build Coastguard Worker     }
938*77c1e3ccSAndroid Build Coastguard Worker   }
939*77c1e3ccSAndroid Build Coastguard Worker   if (best_mbmi->palette_mode_info.palette_size[1] > 0) {
940*77c1e3ccSAndroid Build Coastguard Worker     memcpy(color_map, best_palette_color_map,
941*77c1e3ccSAndroid Build Coastguard Worker            plane_block_width * plane_block_height *
942*77c1e3ccSAndroid Build Coastguard Worker                sizeof(best_palette_color_map[0]));
943*77c1e3ccSAndroid Build Coastguard Worker   }
944*77c1e3ccSAndroid Build Coastguard Worker }
945*77c1e3ccSAndroid Build Coastguard Worker 
av1_restore_uv_color_map(const AV1_COMP * cpi,MACROBLOCK * x)946*77c1e3ccSAndroid Build Coastguard Worker void av1_restore_uv_color_map(const AV1_COMP *cpi, MACROBLOCK *x) {
947*77c1e3ccSAndroid Build Coastguard Worker   MACROBLOCKD *const xd = &x->e_mbd;
948*77c1e3ccSAndroid Build Coastguard Worker   MB_MODE_INFO *const mbmi = xd->mi[0];
949*77c1e3ccSAndroid Build Coastguard Worker   PALETTE_MODE_INFO *const pmi = &mbmi->palette_mode_info;
950*77c1e3ccSAndroid Build Coastguard Worker   const BLOCK_SIZE bsize = mbmi->bsize;
951*77c1e3ccSAndroid Build Coastguard Worker   int src_stride = x->plane[1].src.stride;
952*77c1e3ccSAndroid Build Coastguard Worker   const uint8_t *const src_u = x->plane[1].src.buf;
953*77c1e3ccSAndroid Build Coastguard Worker   const uint8_t *const src_v = x->plane[2].src.buf;
954*77c1e3ccSAndroid Build Coastguard Worker   int16_t *const data = x->palette_buffer->kmeans_data_buf;
955*77c1e3ccSAndroid Build Coastguard Worker   int16_t centroids[2 * PALETTE_MAX_SIZE];
956*77c1e3ccSAndroid Build Coastguard Worker   uint8_t *const color_map = xd->plane[1].color_index_map;
957*77c1e3ccSAndroid Build Coastguard Worker   int r, c;
958*77c1e3ccSAndroid Build Coastguard Worker   const uint16_t *const src_u16 = CONVERT_TO_SHORTPTR(src_u);
959*77c1e3ccSAndroid Build Coastguard Worker   const uint16_t *const src_v16 = CONVERT_TO_SHORTPTR(src_v);
960*77c1e3ccSAndroid Build Coastguard Worker   int plane_block_width, plane_block_height, rows, cols;
961*77c1e3ccSAndroid Build Coastguard Worker   av1_get_block_dimensions(bsize, 1, xd, &plane_block_width,
962*77c1e3ccSAndroid Build Coastguard Worker                            &plane_block_height, &rows, &cols);
963*77c1e3ccSAndroid Build Coastguard Worker 
964*77c1e3ccSAndroid Build Coastguard Worker   for (r = 0; r < rows; ++r) {
965*77c1e3ccSAndroid Build Coastguard Worker     for (c = 0; c < cols; ++c) {
966*77c1e3ccSAndroid Build Coastguard Worker       if (cpi->common.seq_params->use_highbitdepth) {
967*77c1e3ccSAndroid Build Coastguard Worker         data[(r * cols + c) * 2] = src_u16[r * src_stride + c];
968*77c1e3ccSAndroid Build Coastguard Worker         data[(r * cols + c) * 2 + 1] = src_v16[r * src_stride + c];
969*77c1e3ccSAndroid Build Coastguard Worker       } else {
970*77c1e3ccSAndroid Build Coastguard Worker         data[(r * cols + c) * 2] = src_u[r * src_stride + c];
971*77c1e3ccSAndroid Build Coastguard Worker         data[(r * cols + c) * 2 + 1] = src_v[r * src_stride + c];
972*77c1e3ccSAndroid Build Coastguard Worker       }
973*77c1e3ccSAndroid Build Coastguard Worker     }
974*77c1e3ccSAndroid Build Coastguard Worker   }
975*77c1e3ccSAndroid Build Coastguard Worker 
976*77c1e3ccSAndroid Build Coastguard Worker   for (r = 1; r < 3; ++r) {
977*77c1e3ccSAndroid Build Coastguard Worker     for (c = 0; c < pmi->palette_size[1]; ++c) {
978*77c1e3ccSAndroid Build Coastguard Worker       centroids[c * 2 + r - 1] = pmi->palette_colors[r * PALETTE_MAX_SIZE + c];
979*77c1e3ccSAndroid Build Coastguard Worker     }
980*77c1e3ccSAndroid Build Coastguard Worker   }
981*77c1e3ccSAndroid Build Coastguard Worker 
982*77c1e3ccSAndroid Build Coastguard Worker   av1_calc_indices(data, centroids, color_map, rows * cols,
983*77c1e3ccSAndroid Build Coastguard Worker                    pmi->palette_size[1], 2);
984*77c1e3ccSAndroid Build Coastguard Worker   extend_palette_color_map(color_map, cols, rows, plane_block_width,
985*77c1e3ccSAndroid Build Coastguard Worker                            plane_block_height);
986*77c1e3ccSAndroid Build Coastguard Worker }
987