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