• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
srgb_to_linear(float a)37 static 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  */
linear_to_srgb(float a)53 static 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 
main(int argc,char ** argv)64 int 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