* @brief Assign texels to clusters, based on a set of chosen center points. * * @param blk The image block color data to compress. * @param texel_count The number of texels in the block. * @param partition_count The number of partitions in the block. * @param cluster_centers The partition cluster center colors. * @param[out] partition_o
| 144 | * @param[out] partition_of_texel The partition assigned for each texel. |
| 145 | */ |
| 146 | static void kmeans_assign( |
| 147 | const image_block& blk, |
| 148 | unsigned int texel_count, |
| 149 | unsigned int partition_count, |
| 150 | const vfloat4 cluster_centers[BLOCK_MAX_PARTITIONS], |
| 151 | uint8_t partition_of_texel[BLOCK_MAX_TEXELS] |
| 152 | ) { |
| 153 | promise(texel_count > 0); |
| 154 | promise(partition_count > 0); |
| 155 | |
| 156 | uint8_t partition_texel_count[BLOCK_MAX_PARTITIONS] { 0 }; |
| 157 | |
| 158 | // Find the best partition for every texel |
| 159 | for (unsigned int i = 0; i < texel_count; i++) |
| 160 | { |
| 161 | float best_distance = std::numeric_limits<float>::max(); |
| 162 | unsigned int best_partition = 0; |
| 163 | |
| 164 | vfloat4 color = blk.texel(i); |
| 165 | for (unsigned int j = 0; j < partition_count; j++) |
| 166 | { |
| 167 | vfloat4 diff = color - cluster_centers[j]; |
| 168 | float distance = dot_s(diff * diff, blk.channel_weight); |
| 169 | if (distance < best_distance) |
| 170 | { |
| 171 | best_distance = distance; |
| 172 | best_partition = j; |
| 173 | } |
| 174 | } |
| 175 | |
| 176 | partition_of_texel[i] = static_cast<uint8_t>(best_partition); |
| 177 | partition_texel_count[best_partition]++; |
| 178 | } |
| 179 | |
| 180 | // It is possible to get a situation where a partition ends up without any texels. In this case, |
| 181 | // assign texel N to partition N. This is silly, but ensures that every partition retains at |
| 182 | // least one texel. Reassigning a texel in this manner may cause another partition to go empty, |
| 183 | // so if we actually did a reassignment, run the whole loop over again. |
| 184 | bool problem_case; |
| 185 | do |
| 186 | { |
| 187 | problem_case = false; |
| 188 | for (unsigned int i = 0; i < partition_count; i++) |
| 189 | { |
| 190 | if (partition_texel_count[i] == 0) |
| 191 | { |
| 192 | partition_texel_count[partition_of_texel[i]]--; |
| 193 | partition_texel_count[i]++; |
| 194 | partition_of_texel[i] = static_cast<uint8_t>(i); |
| 195 | problem_case = true; |
| 196 | } |
| 197 | } |
| 198 | } while (problem_case); |
| 199 | } |
| 200 | |
| 201 | /** |
| 202 | * @brief Compute new cluster centers based on their center of gravity. |
no test coverage detected