1// SPDX-License-Identifier: Apache-2.0 2// ---------------------------------------------------------------------------- 3// Copyright 2021 Arm Limited 4// 5// Licensed under the Apache License, Version 2.0 (the "License"); you may not 6// use this file except in compliance with the License. You may obtain a copy 7// of the License at: 8// 9// http://www.apache.org/licenses/LICENSE-2.0 10// 11// Unless required by applicable law or agreed to in writing, software 12// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14// License for the specific language governing permissions and limitations 15// under the License. 16// ---------------------------------------------------------------------------- 17 18// This is a utility tool to test blend modes. 19 20#include <stdint.h> 21#include <stdio.h> 22#include <stdlib.h> 23 24#include "astcenc_mathlib.h" 25 26#define STB_IMAGE_IMPLEMENTATION 27#include "stb_image.h" 28 29#define STB_IMAGE_WRITE_IMPLEMENTATION 30#include "stb_image_write.h" 31 32/** 33 * @brief Linearize an sRGB value. 34 * 35 * @return The linearized value. 36 */ 37static float srgb_to_linear( 38 float a 39) { 40 if (a <= 0.04045f) 41 { 42 return a * (1.0f / 12.92f); 43 } 44 45 return powf((a + 0.055f) * (1.0f / 1.055f), 2.4f); 46} 47 48/** 49 * @brief sRGB gamma-encode a linear value. 50 * 51 * @return The gamma encoded value. 52 */ 53static float linear_to_srgb( 54 float a 55) { 56 if (a <= 0.0031308f) 57 { 58 return a * 12.92f; 59 } 60 61 return 1.055f * powf(a, 1.0f / 2.4f) - 0.055f; 62} 63 64int main(int argc, char **argv) 65{ 66 // Parse command line 67 if (argc != 6) 68 { 69 printf("Usage: astc_blend_test <source> <dest> <format> <blend_mode> <filter>\n"); 70 exit(1); 71 } 72 73 const char* src_file = argv[1]; 74 const char* dst_file = argv[2]; 75 76 bool use_linear = false; 77 if (!strcmp(argv[3], "linear")) 78 { 79 use_linear = true; 80 } 81 else if (!strcmp(argv[3], "srgb")) 82 { 83 use_linear = false; 84 } 85 else 86 { 87 printf("<format> must be either 'linear' or 'srgb'\n"); 88 exit(1); 89 } 90 91 bool use_post_blend = false; 92 if (!strcmp(argv[4], "post")) 93 { 94 use_post_blend = true; 95 } 96 else if (!strcmp(argv[4], "pre")) 97 { 98 use_post_blend = false; 99 } 100 else 101 { 102 printf("<blend_mode> must be either 'post' or 'pre'\n"); 103 exit(1); 104 } 105 106 bool use_filter = false; 107 if (!strcmp(argv[5], "on")) 108 { 109 use_filter = true; 110 } 111 else if (!strcmp(argv[5], "off")) 112 { 113 use_filter == false; 114 } 115 else 116 { 117 printf("<filter> must be either 'on' or 'off'\n"); 118 exit(1); 119 } 120 121 // Load the input image 122 int dim_x; 123 int dim_y; 124 const uint8_t* data_in = stbi_load(src_file, &dim_x, &dim_y, nullptr, 4); 125 if (!data_in) 126 { 127 printf("ERROR: Failed to load input image.\n"); 128 exit(1); 129 } 130 131 // Allocate the output image 132 uint8_t* data_out = (uint8_t*)malloc(4 * dim_y * dim_x); 133 if (!data_out) 134 { 135 printf("ERROR: Failed to allocate output image.\n"); 136 exit(1); 137 } 138 139 // For each pixel apply RGBM encoding 140 if (!use_filter) 141 { 142 for (int y = 0; y < dim_y; y++) 143 { 144 const uint8_t* row_in = data_in + (4 * dim_x * y); 145 uint8_t* row_out = data_out + (4 * dim_x * y); 146 147 for (int x = 0; x < dim_x; x++) 148 { 149 const uint8_t* pixel_in = row_in + 4 * x; 150 uint8_t* pixel_out = row_out + 4 * x; 151 152 float r_src = static_cast<float>(pixel_in[0]) / 255.0f; 153 float g_src = static_cast<float>(pixel_in[1]) / 255.0f; 154 float b_src = static_cast<float>(pixel_in[2]) / 255.0f; 155 float a_src = static_cast<float>(pixel_in[3]) / 255.0f; 156 157 if (use_linear == false) 158 { 159 r_src = srgb_to_linear(r_src); 160 g_src = srgb_to_linear(g_src); 161 b_src = srgb_to_linear(b_src); 162 } 163 164 float r_dst = 0.8f; 165 float g_dst = 1.0f; 166 float b_dst = 0.8f; 167 168 float r_out; 169 float g_out; 170 float b_out; 171 float a_out; 172 173 // Post-multiply blending 174 if (use_post_blend) 175 { 176 r_out = (r_dst * (1.0f - a_src)) + (r_src * a_src); 177 g_out = (g_dst * (1.0f - a_src)) + (g_src * a_src); 178 b_out = (b_dst * (1.0f - a_src)) + (b_src * a_src); 179 a_out = 1.0f; 180 } 181 // Pre-multiply blending 182 else 183 { 184 r_out = (r_dst * (1.0f - a_src)) + (r_src * 1.0f); 185 g_out = (g_dst * (1.0f - a_src)) + (g_src * 1.0f); 186 b_out = (b_dst * (1.0f - a_src)) + (b_src * 1.0f); 187 a_out = 1.0f; 188 } 189 190 // Clamp color between 0 and 1.0f 191 r_out = astc::min(r_out, 1.0f); 192 g_out = astc::min(g_out, 1.0f); 193 b_out = astc::min(b_out, 1.0f); 194 195 if (use_linear == false) 196 { 197 r_out = linear_to_srgb(r_out); 198 g_out = linear_to_srgb(g_out); 199 b_out = linear_to_srgb(b_out); 200 } 201 202 pixel_out[0] = (uint8_t)(r_out * 255.0f); 203 pixel_out[1] = (uint8_t)(g_out * 255.0f); 204 pixel_out[2] = (uint8_t)(b_out * 255.0f); 205 pixel_out[3] = (uint8_t)(a_out * 255.0f); 206 } 207 } 208 } 209 else 210 { 211 for (int y = 0; y < dim_y - 1; y++) 212 { 213 const uint8_t* row_in_0 = data_in + (4 * dim_x * y); 214 const uint8_t* row_in_1 = data_in + (4 * dim_x * (y + 1)); 215 216 uint8_t* row_out = data_out + (4 * (dim_x - 1) * y); 217 218 for (int x = 0; x < dim_x - 1; x++) 219 { 220 const uint8_t* pixel_in_00 = row_in_0 + 4 * x; 221 const uint8_t* pixel_in_01 = row_in_0 + 4 * (x + 1); 222 const uint8_t* pixel_in_10 = row_in_1 + 4 * x; 223 const uint8_t* pixel_in_11 = row_in_1 + 4 * (x + 1); 224 225 uint8_t* pixel_out = row_out + 4 * x; 226 227 // Bilinear filter with a half-pixel offset 228 float r_src = static_cast<float>(pixel_in_00[0] + pixel_in_01[0] + pixel_in_10[0] + pixel_in_11[0]) / (255.0f * 4.0f); 229 float g_src = static_cast<float>(pixel_in_00[1] + pixel_in_01[1] + pixel_in_10[1] + pixel_in_11[1]) / (255.0f * 4.0f); 230 float b_src = static_cast<float>(pixel_in_00[2] + pixel_in_01[2] + pixel_in_10[2] + pixel_in_11[2]) / (255.0f * 4.0f); 231 float a_src = static_cast<float>(pixel_in_00[3] + pixel_in_01[3] + pixel_in_10[3] + pixel_in_11[3]) / (255.0f * 4.0f); 232 233 if (use_linear == false) 234 { 235 r_src = srgb_to_linear(r_src); 236 g_src = srgb_to_linear(g_src); 237 b_src = srgb_to_linear(b_src); 238 } 239 240 float r_dst = 0.8f; 241 float g_dst = 1.0f; 242 float b_dst = 0.8f; 243 244 float r_out; 245 float g_out; 246 float b_out; 247 float a_out; 248 249 // Post-multiply blending 250 if (use_post_blend) 251 { 252 r_out = (r_dst * (1.0f - a_src)) + (r_src * a_src); 253 g_out = (g_dst * (1.0f - a_src)) + (g_src * a_src); 254 b_out = (b_dst * (1.0f - a_src)) + (b_src * a_src); 255 a_out = 1.0f; 256 } 257 // Pre-multiply blending 258 else 259 { 260 r_out = (r_dst * (1.0f - a_src)) + (r_src * 1.0f); 261 g_out = (g_dst * (1.0f - a_src)) + (g_src * 1.0f); 262 b_out = (b_dst * (1.0f - a_src)) + (b_src * 1.0f); 263 a_out = 1.0f; 264 } 265 266 // Clamp color between 0 and 1.0f 267 r_out = astc::min(r_out, 1.0f); 268 g_out = astc::min(g_out, 1.0f); 269 b_out = astc::min(b_out, 1.0f); 270 271 if (use_linear == false) 272 { 273 r_out = linear_to_srgb(r_out); 274 g_out = linear_to_srgb(g_out); 275 b_out = linear_to_srgb(b_out); 276 } 277 278 pixel_out[0] = (uint8_t)(r_out * 255.0f); 279 pixel_out[1] = (uint8_t)(g_out * 255.0f); 280 pixel_out[2] = (uint8_t)(b_out * 255.0f); 281 pixel_out[3] = (uint8_t)(a_out * 255.0f); 282 } 283 } 284 } 285 286 // Write out the result 287 if (!use_filter) 288 { 289 stbi_write_png(dst_file, dim_x, dim_y, 4, data_out, 4 * dim_x); 290 } 291 else 292 { 293 stbi_write_png(dst_file, dim_x - 1, dim_y - 1, 4, data_out, 4 * (dim_x - 1)); 294 } 295 296 297 return 0; 298} 299