1cc1dc7a3Sopenharmony_ci// SPDX-License-Identifier: Apache-2.0
2cc1dc7a3Sopenharmony_ci// ----------------------------------------------------------------------------
3cc1dc7a3Sopenharmony_ci// Copyright 2011-2024 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/**
19cc1dc7a3Sopenharmony_ci * @brief Functions for codec library front-end.
20cc1dc7a3Sopenharmony_ci */
21cc1dc7a3Sopenharmony_ci
22cc1dc7a3Sopenharmony_ci#include "astcenc.h"
23cc1dc7a3Sopenharmony_ci#include "astcenccli_internal.h"
24cc1dc7a3Sopenharmony_ci
25cc1dc7a3Sopenharmony_ci#if defined(_WIN32)
26cc1dc7a3Sopenharmony_ci	#include <io.h>
27cc1dc7a3Sopenharmony_ci	#define isatty _isatty
28cc1dc7a3Sopenharmony_ci#else
29cc1dc7a3Sopenharmony_ci	#include <unistd.h>
30cc1dc7a3Sopenharmony_ci#endif
31cc1dc7a3Sopenharmony_ci#include <cassert>
32cc1dc7a3Sopenharmony_ci#include <cstring>
33cc1dc7a3Sopenharmony_ci#include <functional>
34cc1dc7a3Sopenharmony_ci#include <string>
35cc1dc7a3Sopenharmony_ci#include <sstream>
36cc1dc7a3Sopenharmony_ci#include <vector>
37cc1dc7a3Sopenharmony_ci#include <memory>
38cc1dc7a3Sopenharmony_ci
39cc1dc7a3Sopenharmony_ci/* ============================================================================
40cc1dc7a3Sopenharmony_ci	Data structure definitions
41cc1dc7a3Sopenharmony_ci============================================================================ */
42cc1dc7a3Sopenharmony_ci
43cc1dc7a3Sopenharmony_citypedef unsigned int astcenc_operation;
44cc1dc7a3Sopenharmony_ci
45cc1dc7a3Sopenharmony_cistruct mode_entry
46cc1dc7a3Sopenharmony_ci{
47cc1dc7a3Sopenharmony_ci	const char* opt;
48cc1dc7a3Sopenharmony_ci	astcenc_operation operation;
49cc1dc7a3Sopenharmony_ci	astcenc_profile decode_mode;
50cc1dc7a3Sopenharmony_ci};
51cc1dc7a3Sopenharmony_ci
52cc1dc7a3Sopenharmony_ci/* ============================================================================
53cc1dc7a3Sopenharmony_ci	Constants and literals
54cc1dc7a3Sopenharmony_ci============================================================================ */
55cc1dc7a3Sopenharmony_ci
56cc1dc7a3Sopenharmony_ci/** @brief Stage bit indicating we need to load a compressed image. */
57cc1dc7a3Sopenharmony_cistatic const unsigned int ASTCENC_STAGE_LD_COMP    = 1 << 0;
58cc1dc7a3Sopenharmony_ci
59cc1dc7a3Sopenharmony_ci/** @brief Stage bit indicating we need to store a compressed image. */
60cc1dc7a3Sopenharmony_cistatic const unsigned int ASTCENC_STAGE_ST_COMP    = 1 << 1;
61cc1dc7a3Sopenharmony_ci
62cc1dc7a3Sopenharmony_ci/** @brief Stage bit indicating we need to load an uncompressed image. */
63cc1dc7a3Sopenharmony_cistatic const unsigned int ASTCENC_STAGE_LD_NCOMP   = 1 << 2;
64cc1dc7a3Sopenharmony_ci
65cc1dc7a3Sopenharmony_ci/** @brief Stage bit indicating we need to store an uncompressed image. */
66cc1dc7a3Sopenharmony_cistatic const unsigned int ASTCENC_STAGE_ST_NCOMP   = 1 << 3;
67cc1dc7a3Sopenharmony_ci
68cc1dc7a3Sopenharmony_ci/** @brief Stage bit indicating we need compress an image. */
69cc1dc7a3Sopenharmony_cistatic const unsigned int ASTCENC_STAGE_COMPRESS   = 1 << 4;
70cc1dc7a3Sopenharmony_ci
71cc1dc7a3Sopenharmony_ci/** @brief Stage bit indicating we need to decompress an image. */
72cc1dc7a3Sopenharmony_cistatic const unsigned int ASTCENC_STAGE_DECOMPRESS = 1 << 5;
73cc1dc7a3Sopenharmony_ci
74cc1dc7a3Sopenharmony_ci/** @brief Stage bit indicating we need to compare an image with the original input. */
75cc1dc7a3Sopenharmony_cistatic const unsigned int ASTCENC_STAGE_COMPARE    = 1 << 6;
76cc1dc7a3Sopenharmony_ci
77cc1dc7a3Sopenharmony_ci/** @brief Operation indicating an unknown request (should never happen). */
78cc1dc7a3Sopenharmony_cistatic const astcenc_operation ASTCENC_OP_UNKNOWN  = 0;
79cc1dc7a3Sopenharmony_ci
80cc1dc7a3Sopenharmony_ci/** @brief Operation indicating the user wants to print long-form help text and version info. */
81cc1dc7a3Sopenharmony_cistatic const astcenc_operation ASTCENC_OP_HELP     = 1 << 7;
82cc1dc7a3Sopenharmony_ci
83cc1dc7a3Sopenharmony_ci/** @brief Operation indicating the user wants to print short-form help text and version info. */
84cc1dc7a3Sopenharmony_cistatic const astcenc_operation ASTCENC_OP_VERSION  = 1 << 8;
85cc1dc7a3Sopenharmony_ci
86cc1dc7a3Sopenharmony_ci/** @brief Operation indicating the user wants to compress and store an image. */
87cc1dc7a3Sopenharmony_cistatic const astcenc_operation ASTCENC_OP_COMPRESS =
88cc1dc7a3Sopenharmony_ci                               ASTCENC_STAGE_LD_NCOMP |
89cc1dc7a3Sopenharmony_ci                               ASTCENC_STAGE_COMPRESS |
90cc1dc7a3Sopenharmony_ci                               ASTCENC_STAGE_ST_COMP;
91cc1dc7a3Sopenharmony_ci
92cc1dc7a3Sopenharmony_ci/** @brief Operation indicating the user wants to decompress and store an image. */
93cc1dc7a3Sopenharmony_cistatic const astcenc_operation ASTCENC_OP_DECOMPRESS =
94cc1dc7a3Sopenharmony_ci                               ASTCENC_STAGE_LD_COMP |
95cc1dc7a3Sopenharmony_ci                               ASTCENC_STAGE_DECOMPRESS |
96cc1dc7a3Sopenharmony_ci                               ASTCENC_STAGE_ST_NCOMP;
97cc1dc7a3Sopenharmony_ci
98cc1dc7a3Sopenharmony_ci/** @brief Operation indicating the user wants to test a compression setting on an image. */
99cc1dc7a3Sopenharmony_cistatic const astcenc_operation ASTCENC_OP_TEST =
100cc1dc7a3Sopenharmony_ci                               ASTCENC_STAGE_LD_NCOMP |
101cc1dc7a3Sopenharmony_ci                               ASTCENC_STAGE_COMPRESS |
102cc1dc7a3Sopenharmony_ci                               ASTCENC_STAGE_DECOMPRESS |
103cc1dc7a3Sopenharmony_ci                               ASTCENC_STAGE_COMPARE |
104cc1dc7a3Sopenharmony_ci                               ASTCENC_STAGE_ST_NCOMP;
105cc1dc7a3Sopenharmony_ci
106cc1dc7a3Sopenharmony_ci/**
107cc1dc7a3Sopenharmony_ci * @brief Image preprocesing tasks prior to encoding.
108cc1dc7a3Sopenharmony_ci */
109cc1dc7a3Sopenharmony_cienum astcenc_preprocess
110cc1dc7a3Sopenharmony_ci{
111cc1dc7a3Sopenharmony_ci	/** @brief No image preprocessing. */
112cc1dc7a3Sopenharmony_ci	ASTCENC_PP_NONE = 0,
113cc1dc7a3Sopenharmony_ci	/** @brief Normal vector unit-length normalization. */
114cc1dc7a3Sopenharmony_ci	ASTCENC_PP_NORMALIZE,
115cc1dc7a3Sopenharmony_ci	/** @brief Color data alpha premultiplication. */
116cc1dc7a3Sopenharmony_ci	ASTCENC_PP_PREMULTIPLY
117cc1dc7a3Sopenharmony_ci};
118cc1dc7a3Sopenharmony_ci
119cc1dc7a3Sopenharmony_ci/** @brief Decode table for command line operation modes. */
120cc1dc7a3Sopenharmony_cistatic const mode_entry modes[] {
121cc1dc7a3Sopenharmony_ci	{"-cl",      ASTCENC_OP_COMPRESS,   ASTCENC_PRF_LDR},
122cc1dc7a3Sopenharmony_ci	{"-dl",      ASTCENC_OP_DECOMPRESS, ASTCENC_PRF_LDR},
123cc1dc7a3Sopenharmony_ci	{"-tl",      ASTCENC_OP_TEST,       ASTCENC_PRF_LDR},
124cc1dc7a3Sopenharmony_ci	{"-cs",      ASTCENC_OP_COMPRESS,   ASTCENC_PRF_LDR_SRGB},
125cc1dc7a3Sopenharmony_ci	{"-ds",      ASTCENC_OP_DECOMPRESS, ASTCENC_PRF_LDR_SRGB},
126cc1dc7a3Sopenharmony_ci	{"-ts",      ASTCENC_OP_TEST,       ASTCENC_PRF_LDR_SRGB},
127cc1dc7a3Sopenharmony_ci	{"-ch",      ASTCENC_OP_COMPRESS,   ASTCENC_PRF_HDR_RGB_LDR_A},
128cc1dc7a3Sopenharmony_ci	{"-dh",      ASTCENC_OP_DECOMPRESS, ASTCENC_PRF_HDR_RGB_LDR_A},
129cc1dc7a3Sopenharmony_ci	{"-th",      ASTCENC_OP_TEST,       ASTCENC_PRF_HDR_RGB_LDR_A},
130cc1dc7a3Sopenharmony_ci	{"-cH",      ASTCENC_OP_COMPRESS,   ASTCENC_PRF_HDR},
131cc1dc7a3Sopenharmony_ci	{"-dH",      ASTCENC_OP_DECOMPRESS, ASTCENC_PRF_HDR},
132cc1dc7a3Sopenharmony_ci	{"-tH",      ASTCENC_OP_TEST,       ASTCENC_PRF_HDR},
133cc1dc7a3Sopenharmony_ci	{"-h",       ASTCENC_OP_HELP,       ASTCENC_PRF_HDR},
134cc1dc7a3Sopenharmony_ci	{"-help",    ASTCENC_OP_HELP,       ASTCENC_PRF_HDR},
135cc1dc7a3Sopenharmony_ci	{"-v",       ASTCENC_OP_VERSION,    ASTCENC_PRF_HDR},
136cc1dc7a3Sopenharmony_ci	{"-version", ASTCENC_OP_VERSION,    ASTCENC_PRF_HDR}
137cc1dc7a3Sopenharmony_ci};
138cc1dc7a3Sopenharmony_ci
139cc1dc7a3Sopenharmony_ci/**
140cc1dc7a3Sopenharmony_ci * @brief Compression workload definition for worker threads.
141cc1dc7a3Sopenharmony_ci */
142cc1dc7a3Sopenharmony_cistruct compression_workload
143cc1dc7a3Sopenharmony_ci{
144cc1dc7a3Sopenharmony_ci	astcenc_context* context;
145cc1dc7a3Sopenharmony_ci	astcenc_image* image;
146cc1dc7a3Sopenharmony_ci	astcenc_swizzle swizzle;
147cc1dc7a3Sopenharmony_ci	uint8_t* data_out;
148cc1dc7a3Sopenharmony_ci	size_t data_len;
149cc1dc7a3Sopenharmony_ci	astcenc_error error;
150cc1dc7a3Sopenharmony_ci#if QUALITY_CONTROL
151cc1dc7a3Sopenharmony_ci	bool calQualityEnable;
152cc1dc7a3Sopenharmony_ci	int32_t *mse[RGBA_COM];
153cc1dc7a3Sopenharmony_ci#endif
154cc1dc7a3Sopenharmony_ci};
155cc1dc7a3Sopenharmony_ci
156cc1dc7a3Sopenharmony_ci/**
157cc1dc7a3Sopenharmony_ci * @brief Decompression workload definition for worker threads.
158cc1dc7a3Sopenharmony_ci */
159cc1dc7a3Sopenharmony_cistruct decompression_workload
160cc1dc7a3Sopenharmony_ci{
161cc1dc7a3Sopenharmony_ci	astcenc_context* context;
162cc1dc7a3Sopenharmony_ci	uint8_t* data;
163cc1dc7a3Sopenharmony_ci	size_t data_len;
164cc1dc7a3Sopenharmony_ci	astcenc_image* image_out;
165cc1dc7a3Sopenharmony_ci	astcenc_swizzle swizzle;
166cc1dc7a3Sopenharmony_ci	astcenc_error error;
167cc1dc7a3Sopenharmony_ci};
168cc1dc7a3Sopenharmony_ci
169cc1dc7a3Sopenharmony_ci/**
170cc1dc7a3Sopenharmony_ci * @brief Callback emitting a progress bar
171cc1dc7a3Sopenharmony_ci */
172cc1dc7a3Sopenharmony_ciextern "C" void progress_emitter(
173cc1dc7a3Sopenharmony_ci	float value
174cc1dc7a3Sopenharmony_ci) {
175cc1dc7a3Sopenharmony_ci	const unsigned int bar_size = 25;
176cc1dc7a3Sopenharmony_ci	unsigned int parts = static_cast<int>(value / 4.0f);
177cc1dc7a3Sopenharmony_ci
178cc1dc7a3Sopenharmony_ci	char buffer[bar_size + 3];
179cc1dc7a3Sopenharmony_ci	buffer[0] = '[';
180cc1dc7a3Sopenharmony_ci
181cc1dc7a3Sopenharmony_ci	for (unsigned int i = 0; i < parts; i++)
182cc1dc7a3Sopenharmony_ci	{
183cc1dc7a3Sopenharmony_ci		buffer[i + 1] = '=';
184cc1dc7a3Sopenharmony_ci	}
185cc1dc7a3Sopenharmony_ci
186cc1dc7a3Sopenharmony_ci	for (unsigned int i = parts; i < bar_size; i++)
187cc1dc7a3Sopenharmony_ci	{
188cc1dc7a3Sopenharmony_ci		buffer[i + 1] = ' ';
189cc1dc7a3Sopenharmony_ci	}
190cc1dc7a3Sopenharmony_ci
191cc1dc7a3Sopenharmony_ci	buffer[bar_size + 1] = ']';
192cc1dc7a3Sopenharmony_ci	buffer[bar_size + 2] = '\0';
193cc1dc7a3Sopenharmony_ci
194cc1dc7a3Sopenharmony_ci	printf("    Progress: %s %03.1f%%\r", buffer, static_cast<double>(value));
195cc1dc7a3Sopenharmony_ci	fflush(stdout);
196cc1dc7a3Sopenharmony_ci}
197cc1dc7a3Sopenharmony_ci
198cc1dc7a3Sopenharmony_ci/**
199cc1dc7a3Sopenharmony_ci * @brief Test if a string argument is a well formed float.
200cc1dc7a3Sopenharmony_ci */
201cc1dc7a3Sopenharmony_cistatic bool is_float(
202cc1dc7a3Sopenharmony_ci	std::string target
203cc1dc7a3Sopenharmony_ci) {
204cc1dc7a3Sopenharmony_ci	float test;
205cc1dc7a3Sopenharmony_ci	std::istringstream stream(target);
206cc1dc7a3Sopenharmony_ci
207cc1dc7a3Sopenharmony_ci	// Leading whitespace is an error
208cc1dc7a3Sopenharmony_ci	stream >> std::noskipws >> test;
209cc1dc7a3Sopenharmony_ci
210cc1dc7a3Sopenharmony_ci	// Ensure entire no remaining string in addition to parse failure
211cc1dc7a3Sopenharmony_ci	return stream.eof() && !stream.fail();
212cc1dc7a3Sopenharmony_ci}
213cc1dc7a3Sopenharmony_ci
214cc1dc7a3Sopenharmony_ci/**
215cc1dc7a3Sopenharmony_ci * @brief Test if a string ends with a given suffix.
216cc1dc7a3Sopenharmony_ci */
217cc1dc7a3Sopenharmony_cistatic bool ends_with(
218cc1dc7a3Sopenharmony_ci	const std::string& str,
219cc1dc7a3Sopenharmony_ci	const std::string& suffix
220cc1dc7a3Sopenharmony_ci) {
221cc1dc7a3Sopenharmony_ci	return (str.size() >= suffix.size()) &&
222cc1dc7a3Sopenharmony_ci	       (0 == str.compare(str.size() - suffix.size(), suffix.size(), suffix));
223cc1dc7a3Sopenharmony_ci}
224cc1dc7a3Sopenharmony_ci
225cc1dc7a3Sopenharmony_ci/**
226cc1dc7a3Sopenharmony_ci * @brief Runner callback function for a compression worker thread.
227cc1dc7a3Sopenharmony_ci *
228cc1dc7a3Sopenharmony_ci * @param thread_count   The number of threads in the worker pool.
229cc1dc7a3Sopenharmony_ci * @param thread_id      The index of this thread in the worker pool.
230cc1dc7a3Sopenharmony_ci * @param payload        The parameters for this thread.
231cc1dc7a3Sopenharmony_ci */
232cc1dc7a3Sopenharmony_cistatic void compression_workload_runner(
233cc1dc7a3Sopenharmony_ci	int thread_count,
234cc1dc7a3Sopenharmony_ci	int thread_id,
235cc1dc7a3Sopenharmony_ci	void* payload
236cc1dc7a3Sopenharmony_ci) {
237cc1dc7a3Sopenharmony_ci	(void)thread_count;
238cc1dc7a3Sopenharmony_ci
239cc1dc7a3Sopenharmony_ci	compression_workload* work = static_cast<compression_workload*>(payload);
240cc1dc7a3Sopenharmony_ci	astcenc_error error = astcenc_compress_image(
241cc1dc7a3Sopenharmony_ci	                       work->context, work->image, &work->swizzle,
242cc1dc7a3Sopenharmony_ci	                       work->data_out, work->data_len,
243cc1dc7a3Sopenharmony_ci#if QUALITY_CONTROL
244cc1dc7a3Sopenharmony_ci	                       work->calQualityEnable, work->mse,
245cc1dc7a3Sopenharmony_ci#endif
246cc1dc7a3Sopenharmony_ci	                       thread_id);
247cc1dc7a3Sopenharmony_ci
248cc1dc7a3Sopenharmony_ci	// This is a racy update, so which error gets returned is a random, but it
249cc1dc7a3Sopenharmony_ci	// will reliably report an error if an error occurs
250cc1dc7a3Sopenharmony_ci	if (error != ASTCENC_SUCCESS)
251cc1dc7a3Sopenharmony_ci	{
252cc1dc7a3Sopenharmony_ci		work->error = error;
253cc1dc7a3Sopenharmony_ci	}
254cc1dc7a3Sopenharmony_ci}
255cc1dc7a3Sopenharmony_ci
256cc1dc7a3Sopenharmony_ci/**
257cc1dc7a3Sopenharmony_ci * @brief Runner callback function for a decompression worker thread.
258cc1dc7a3Sopenharmony_ci *
259cc1dc7a3Sopenharmony_ci * @param thread_count   The number of threads in the worker pool.
260cc1dc7a3Sopenharmony_ci * @param thread_id      The index of this thread in the worker pool.
261cc1dc7a3Sopenharmony_ci * @param payload        The parameters for this thread.
262cc1dc7a3Sopenharmony_ci */
263cc1dc7a3Sopenharmony_cistatic void decompression_workload_runner(
264cc1dc7a3Sopenharmony_ci	int thread_count,
265cc1dc7a3Sopenharmony_ci	int thread_id,
266cc1dc7a3Sopenharmony_ci	void* payload
267cc1dc7a3Sopenharmony_ci) {
268cc1dc7a3Sopenharmony_ci	(void)thread_count;
269cc1dc7a3Sopenharmony_ci
270cc1dc7a3Sopenharmony_ci	decompression_workload* work = static_cast<decompression_workload*>(payload);
271cc1dc7a3Sopenharmony_ci	astcenc_error error = astcenc_decompress_image(
272cc1dc7a3Sopenharmony_ci	                       work->context, work->data, work->data_len,
273cc1dc7a3Sopenharmony_ci	                       work->image_out, &work->swizzle, thread_id);
274cc1dc7a3Sopenharmony_ci
275cc1dc7a3Sopenharmony_ci	// This is a racy update, so which error gets returned is a random, but it
276cc1dc7a3Sopenharmony_ci	// will reliably report an error if an error occurs
277cc1dc7a3Sopenharmony_ci	if (error != ASTCENC_SUCCESS)
278cc1dc7a3Sopenharmony_ci	{
279cc1dc7a3Sopenharmony_ci		work->error = error;
280cc1dc7a3Sopenharmony_ci	}
281cc1dc7a3Sopenharmony_ci}
282cc1dc7a3Sopenharmony_ci
283cc1dc7a3Sopenharmony_ci/**
284cc1dc7a3Sopenharmony_ci * @brief Utility to generate a slice file name from a pattern.
285cc1dc7a3Sopenharmony_ci *
286cc1dc7a3Sopenharmony_ci * Convert "foo/bar.png" in to "foo/bar_<slice>.png"
287cc1dc7a3Sopenharmony_ci *
288cc1dc7a3Sopenharmony_ci * @param basename The base pattern; must contain a file extension.
289cc1dc7a3Sopenharmony_ci * @param index    The slice index.
290cc1dc7a3Sopenharmony_ci * @param error    Set to true on success, false on error (no extension found).
291cc1dc7a3Sopenharmony_ci *
292cc1dc7a3Sopenharmony_ci * @return The slice file name.
293cc1dc7a3Sopenharmony_ci */
294cc1dc7a3Sopenharmony_cistatic std::string get_slice_filename(
295cc1dc7a3Sopenharmony_ci	const std::string& basename,
296cc1dc7a3Sopenharmony_ci	unsigned int index,
297cc1dc7a3Sopenharmony_ci	bool& error
298cc1dc7a3Sopenharmony_ci) {
299cc1dc7a3Sopenharmony_ci	size_t sep = basename.find_last_of('.');
300cc1dc7a3Sopenharmony_ci	if (sep == std::string::npos)
301cc1dc7a3Sopenharmony_ci	{
302cc1dc7a3Sopenharmony_ci		error = true;
303cc1dc7a3Sopenharmony_ci		return "";
304cc1dc7a3Sopenharmony_ci	}
305cc1dc7a3Sopenharmony_ci
306cc1dc7a3Sopenharmony_ci	std::string base = basename.substr(0, sep);
307cc1dc7a3Sopenharmony_ci	std::string ext = basename.substr(sep);
308cc1dc7a3Sopenharmony_ci	std::string name = base + "_" + std::to_string(index) + ext;
309cc1dc7a3Sopenharmony_ci	error = false;
310cc1dc7a3Sopenharmony_ci	return name;
311cc1dc7a3Sopenharmony_ci}
312cc1dc7a3Sopenharmony_ci
313cc1dc7a3Sopenharmony_ci/**
314cc1dc7a3Sopenharmony_ci * @brief Load a non-astc image file from memory.
315cc1dc7a3Sopenharmony_ci *
316cc1dc7a3Sopenharmony_ci * @param filename            The file to load, or a pattern for array loads.
317cc1dc7a3Sopenharmony_ci * @param dim_z               The number of slices to load.
318cc1dc7a3Sopenharmony_ci * @param y_flip              Should this image be Y flipped?
319cc1dc7a3Sopenharmony_ci * @param[out] is_hdr         Is the loaded image HDR?
320cc1dc7a3Sopenharmony_ci * @param[out] component_count The number of components in the loaded image.
321cc1dc7a3Sopenharmony_ci *
322cc1dc7a3Sopenharmony_ci * @return The astc image file, or nullptr on error.
323cc1dc7a3Sopenharmony_ci */
324cc1dc7a3Sopenharmony_cistatic astcenc_image* load_uncomp_file(
325cc1dc7a3Sopenharmony_ci	const char* filename,
326cc1dc7a3Sopenharmony_ci	unsigned int dim_z,
327cc1dc7a3Sopenharmony_ci	bool y_flip,
328cc1dc7a3Sopenharmony_ci	bool& is_hdr,
329cc1dc7a3Sopenharmony_ci	unsigned int& component_count
330cc1dc7a3Sopenharmony_ci) {
331cc1dc7a3Sopenharmony_ci	astcenc_image *image = nullptr;
332cc1dc7a3Sopenharmony_ci
333cc1dc7a3Sopenharmony_ci	// For a 2D image just load the image directly
334cc1dc7a3Sopenharmony_ci	if (dim_z == 1)
335cc1dc7a3Sopenharmony_ci	{
336cc1dc7a3Sopenharmony_ci		image = load_ncimage(filename, y_flip, is_hdr, component_count);
337cc1dc7a3Sopenharmony_ci	}
338cc1dc7a3Sopenharmony_ci	else
339cc1dc7a3Sopenharmony_ci	{
340cc1dc7a3Sopenharmony_ci		bool slice_is_hdr;
341cc1dc7a3Sopenharmony_ci		unsigned int slice_component_count;
342cc1dc7a3Sopenharmony_ci		astcenc_image* slice = nullptr;
343cc1dc7a3Sopenharmony_ci		std::vector<astcenc_image*> slices;
344cc1dc7a3Sopenharmony_ci
345cc1dc7a3Sopenharmony_ci		// For a 3D image load an array of slices
346cc1dc7a3Sopenharmony_ci		for (unsigned int image_index = 0; image_index < dim_z; image_index++)
347cc1dc7a3Sopenharmony_ci		{
348cc1dc7a3Sopenharmony_ci			bool error;
349cc1dc7a3Sopenharmony_ci			std::string slice_name = get_slice_filename(filename, image_index, error);
350cc1dc7a3Sopenharmony_ci			if (error)
351cc1dc7a3Sopenharmony_ci			{
352cc1dc7a3Sopenharmony_ci				print_error("ERROR: Image pattern does not contain file extension: %s\n", filename);
353cc1dc7a3Sopenharmony_ci				break;
354cc1dc7a3Sopenharmony_ci			}
355cc1dc7a3Sopenharmony_ci
356cc1dc7a3Sopenharmony_ci			slice = load_ncimage(slice_name.c_str(), y_flip,
357cc1dc7a3Sopenharmony_ci			                     slice_is_hdr, slice_component_count);
358cc1dc7a3Sopenharmony_ci			if (!slice)
359cc1dc7a3Sopenharmony_ci			{
360cc1dc7a3Sopenharmony_ci				break;
361cc1dc7a3Sopenharmony_ci			}
362cc1dc7a3Sopenharmony_ci
363cc1dc7a3Sopenharmony_ci			slices.push_back(slice);
364cc1dc7a3Sopenharmony_ci
365cc1dc7a3Sopenharmony_ci			// Check it is not a 3D image
366cc1dc7a3Sopenharmony_ci			if (slice->dim_z != 1)
367cc1dc7a3Sopenharmony_ci			{
368cc1dc7a3Sopenharmony_ci				print_error("ERROR: Image arrays do not support 3D sources: %s\n", slice_name.c_str());
369cc1dc7a3Sopenharmony_ci				break;
370cc1dc7a3Sopenharmony_ci			}
371cc1dc7a3Sopenharmony_ci
372cc1dc7a3Sopenharmony_ci			// Check slices are consistent with each other
373cc1dc7a3Sopenharmony_ci			if (image_index != 0)
374cc1dc7a3Sopenharmony_ci			{
375cc1dc7a3Sopenharmony_ci				if ((is_hdr != slice_is_hdr) || (component_count != slice_component_count))
376cc1dc7a3Sopenharmony_ci				{
377cc1dc7a3Sopenharmony_ci					print_error("ERROR: Image array[0] and [%d] are different formats\n", image_index);
378cc1dc7a3Sopenharmony_ci					break;
379cc1dc7a3Sopenharmony_ci				}
380cc1dc7a3Sopenharmony_ci
381cc1dc7a3Sopenharmony_ci				if ((slices[0]->dim_x != slice->dim_x) ||
382cc1dc7a3Sopenharmony_ci				    (slices[0]->dim_y != slice->dim_y) ||
383cc1dc7a3Sopenharmony_ci				    (slices[0]->dim_z != slice->dim_z))
384cc1dc7a3Sopenharmony_ci				{
385cc1dc7a3Sopenharmony_ci					print_error("ERROR: Image array[0] and [%d] are different dimensions\n", image_index);
386cc1dc7a3Sopenharmony_ci					break;
387cc1dc7a3Sopenharmony_ci				}
388cc1dc7a3Sopenharmony_ci			}
389cc1dc7a3Sopenharmony_ci			else
390cc1dc7a3Sopenharmony_ci			{
391cc1dc7a3Sopenharmony_ci				is_hdr = slice_is_hdr;
392cc1dc7a3Sopenharmony_ci				component_count = slice_component_count;
393cc1dc7a3Sopenharmony_ci			}
394cc1dc7a3Sopenharmony_ci		}
395cc1dc7a3Sopenharmony_ci
396cc1dc7a3Sopenharmony_ci		// If all slices loaded correctly then repack them into a single image
397cc1dc7a3Sopenharmony_ci		if (slices.size() == dim_z)
398cc1dc7a3Sopenharmony_ci		{
399cc1dc7a3Sopenharmony_ci			unsigned int dim_x = slices[0]->dim_x;
400cc1dc7a3Sopenharmony_ci			unsigned int dim_y = slices[0]->dim_y;
401cc1dc7a3Sopenharmony_ci			int bitness = is_hdr ? 16 : 8;
402cc1dc7a3Sopenharmony_ci			int slice_size = dim_x * dim_y;
403cc1dc7a3Sopenharmony_ci
404cc1dc7a3Sopenharmony_ci			image = alloc_image(bitness, dim_x, dim_y, dim_z);
405cc1dc7a3Sopenharmony_ci
406cc1dc7a3Sopenharmony_ci			// Combine 2D source images into one 3D image
407cc1dc7a3Sopenharmony_ci			for (unsigned int z = 0; z < dim_z; z++)
408cc1dc7a3Sopenharmony_ci			{
409cc1dc7a3Sopenharmony_ci				if (image->data_type == ASTCENC_TYPE_U8)
410cc1dc7a3Sopenharmony_ci				{
411cc1dc7a3Sopenharmony_ci					uint8_t* data8 = static_cast<uint8_t*>(image->data[z]);
412cc1dc7a3Sopenharmony_ci					uint8_t* data8src = static_cast<uint8_t*>(slices[z]->data[0]);
413cc1dc7a3Sopenharmony_ci					size_t copy_size = slice_size * 4 * sizeof(uint8_t);
414cc1dc7a3Sopenharmony_ci					memcpy(data8, data8src, copy_size);
415cc1dc7a3Sopenharmony_ci				}
416cc1dc7a3Sopenharmony_ci				else if (image->data_type == ASTCENC_TYPE_F16)
417cc1dc7a3Sopenharmony_ci				{
418cc1dc7a3Sopenharmony_ci					uint16_t* data16 = static_cast<uint16_t*>(image->data[z]);
419cc1dc7a3Sopenharmony_ci					uint16_t* data16src = static_cast<uint16_t*>(slices[z]->data[0]);
420cc1dc7a3Sopenharmony_ci					size_t copy_size = slice_size * 4 * sizeof(uint16_t);
421cc1dc7a3Sopenharmony_ci					memcpy(data16, data16src, copy_size);
422cc1dc7a3Sopenharmony_ci				}
423cc1dc7a3Sopenharmony_ci				else // if (image->data_type == ASTCENC_TYPE_F32)
424cc1dc7a3Sopenharmony_ci				{
425cc1dc7a3Sopenharmony_ci					assert(image->data_type == ASTCENC_TYPE_F32);
426cc1dc7a3Sopenharmony_ci					float* data32 = static_cast<float*>(image->data[z]);
427cc1dc7a3Sopenharmony_ci					float* data32src = static_cast<float*>(slices[z]->data[0]);
428cc1dc7a3Sopenharmony_ci					size_t copy_size = slice_size * 4 * sizeof(float);
429cc1dc7a3Sopenharmony_ci					memcpy(data32, data32src, copy_size);
430cc1dc7a3Sopenharmony_ci				}
431cc1dc7a3Sopenharmony_ci			}
432cc1dc7a3Sopenharmony_ci		}
433cc1dc7a3Sopenharmony_ci
434cc1dc7a3Sopenharmony_ci		for (auto &i : slices)
435cc1dc7a3Sopenharmony_ci		{
436cc1dc7a3Sopenharmony_ci			free_image(i);
437cc1dc7a3Sopenharmony_ci		}
438cc1dc7a3Sopenharmony_ci	}
439cc1dc7a3Sopenharmony_ci
440cc1dc7a3Sopenharmony_ci	return image;
441cc1dc7a3Sopenharmony_ci}
442cc1dc7a3Sopenharmony_ci
443cc1dc7a3Sopenharmony_ci/**
444cc1dc7a3Sopenharmony_ci * @brief Parse the command line.
445cc1dc7a3Sopenharmony_ci *
446cc1dc7a3Sopenharmony_ci * @param      argc        Command line argument count.
447cc1dc7a3Sopenharmony_ci * @param[in]  argv        Command line argument vector.
448cc1dc7a3Sopenharmony_ci * @param[out] operation   Codec operation mode.
449cc1dc7a3Sopenharmony_ci * @param[out] profile     Codec color profile.
450cc1dc7a3Sopenharmony_ci *
451cc1dc7a3Sopenharmony_ci * @return 0 if everything is okay, 1 if there is some error
452cc1dc7a3Sopenharmony_ci */
453cc1dc7a3Sopenharmony_cistatic int parse_commandline_options(
454cc1dc7a3Sopenharmony_ci	int argc,
455cc1dc7a3Sopenharmony_ci	char **argv,
456cc1dc7a3Sopenharmony_ci	astcenc_operation& operation,
457cc1dc7a3Sopenharmony_ci	astcenc_profile& profile
458cc1dc7a3Sopenharmony_ci) {
459cc1dc7a3Sopenharmony_ci	assert(argc >= 2); (void)argc;
460cc1dc7a3Sopenharmony_ci
461cc1dc7a3Sopenharmony_ci	profile = ASTCENC_PRF_LDR;
462cc1dc7a3Sopenharmony_ci	operation = ASTCENC_OP_UNKNOWN;
463cc1dc7a3Sopenharmony_ci
464cc1dc7a3Sopenharmony_ci	int modes_count = sizeof(modes) / sizeof(modes[0]);
465cc1dc7a3Sopenharmony_ci	for (int i = 0; i < modes_count; i++)
466cc1dc7a3Sopenharmony_ci	{
467cc1dc7a3Sopenharmony_ci		if (!strcmp(modes[i].opt, argv[1]))
468cc1dc7a3Sopenharmony_ci		{
469cc1dc7a3Sopenharmony_ci			operation = modes[i].operation;
470cc1dc7a3Sopenharmony_ci			profile = modes[i].decode_mode;
471cc1dc7a3Sopenharmony_ci			break;
472cc1dc7a3Sopenharmony_ci		}
473cc1dc7a3Sopenharmony_ci	}
474cc1dc7a3Sopenharmony_ci
475cc1dc7a3Sopenharmony_ci	if (operation == ASTCENC_OP_UNKNOWN)
476cc1dc7a3Sopenharmony_ci	{
477cc1dc7a3Sopenharmony_ci		print_error("ERROR: Unrecognized operation '%s'\n", argv[1]);
478cc1dc7a3Sopenharmony_ci		return 1;
479cc1dc7a3Sopenharmony_ci	}
480cc1dc7a3Sopenharmony_ci
481cc1dc7a3Sopenharmony_ci	return 0;
482cc1dc7a3Sopenharmony_ci}
483cc1dc7a3Sopenharmony_ci
484cc1dc7a3Sopenharmony_ci/**
485cc1dc7a3Sopenharmony_ci * @brief Initialize the astcenc_config
486cc1dc7a3Sopenharmony_ci *
487cc1dc7a3Sopenharmony_ci * @param      argc         Command line argument count.
488cc1dc7a3Sopenharmony_ci * @param[in]  argv         Command line argument vector.
489cc1dc7a3Sopenharmony_ci * @param      operation    Codec operation mode.
490cc1dc7a3Sopenharmony_ci * @param[out] profile      Codec color profile.
491cc1dc7a3Sopenharmony_ci * @param      comp_image   Compressed image if a decompress operation.
492cc1dc7a3Sopenharmony_ci * @param[out] preprocess   Image preprocess operation.
493cc1dc7a3Sopenharmony_ci * @param[out] config       Codec configuration.
494cc1dc7a3Sopenharmony_ci *
495cc1dc7a3Sopenharmony_ci * @return 0 if everything is okay, 1 if there is some error
496cc1dc7a3Sopenharmony_ci */
497cc1dc7a3Sopenharmony_cistatic int init_astcenc_config(
498cc1dc7a3Sopenharmony_ci	int argc,
499cc1dc7a3Sopenharmony_ci	char **argv,
500cc1dc7a3Sopenharmony_ci	astcenc_profile profile,
501cc1dc7a3Sopenharmony_ci	astcenc_operation operation,
502cc1dc7a3Sopenharmony_ci	astc_compressed_image& comp_image,
503cc1dc7a3Sopenharmony_ci	astcenc_preprocess& preprocess,
504cc1dc7a3Sopenharmony_ci	astcenc_config& config
505cc1dc7a3Sopenharmony_ci) {
506cc1dc7a3Sopenharmony_ci	unsigned int block_x = 0;
507cc1dc7a3Sopenharmony_ci	unsigned int block_y = 0;
508cc1dc7a3Sopenharmony_ci	unsigned int block_z = 1;
509cc1dc7a3Sopenharmony_ci
510cc1dc7a3Sopenharmony_ci	// For decode the block size is set by the incoming image.
511cc1dc7a3Sopenharmony_ci	if (operation == ASTCENC_OP_DECOMPRESS)
512cc1dc7a3Sopenharmony_ci	{
513cc1dc7a3Sopenharmony_ci		block_x = comp_image.block_x;
514cc1dc7a3Sopenharmony_ci		block_y = comp_image.block_y;
515cc1dc7a3Sopenharmony_ci		block_z = comp_image.block_z;
516cc1dc7a3Sopenharmony_ci	}
517cc1dc7a3Sopenharmony_ci
518cc1dc7a3Sopenharmony_ci	float quality = 0.0f;
519cc1dc7a3Sopenharmony_ci	preprocess = ASTCENC_PP_NONE;
520cc1dc7a3Sopenharmony_ci
521cc1dc7a3Sopenharmony_ci	// parse the command line's encoding options.
522cc1dc7a3Sopenharmony_ci	int argidx = 4;
523cc1dc7a3Sopenharmony_ci	if (operation & ASTCENC_STAGE_COMPRESS)
524cc1dc7a3Sopenharmony_ci	{
525cc1dc7a3Sopenharmony_ci		// Read and decode block size
526cc1dc7a3Sopenharmony_ci		if (argc < 5)
527cc1dc7a3Sopenharmony_ci		{
528cc1dc7a3Sopenharmony_ci			print_error("ERROR: Block size must be specified\n");
529cc1dc7a3Sopenharmony_ci			return 1;
530cc1dc7a3Sopenharmony_ci		}
531cc1dc7a3Sopenharmony_ci
532cc1dc7a3Sopenharmony_ci		int cnt2D, cnt3D;
533cc1dc7a3Sopenharmony_ci		int dimensions = sscanf(argv[4], "%ux%u%nx%u%n",
534cc1dc7a3Sopenharmony_ci		                        &block_x, &block_y, &cnt2D, &block_z, &cnt3D);
535cc1dc7a3Sopenharmony_ci		// Character after the last match should be a NUL
536cc1dc7a3Sopenharmony_ci		if (!(((dimensions == 2) && !argv[4][cnt2D]) || ((dimensions == 3) && !argv[4][cnt3D])))
537cc1dc7a3Sopenharmony_ci		{
538cc1dc7a3Sopenharmony_ci			print_error("ERROR: Block size '%s' is invalid\n", argv[4]);
539cc1dc7a3Sopenharmony_ci			return 1;
540cc1dc7a3Sopenharmony_ci		}
541cc1dc7a3Sopenharmony_ci
542cc1dc7a3Sopenharmony_ci		// Read and decode search quality
543cc1dc7a3Sopenharmony_ci		if (argc < 6)
544cc1dc7a3Sopenharmony_ci		{
545cc1dc7a3Sopenharmony_ci			print_error("ERROR: Search quality level must be specified\n");
546cc1dc7a3Sopenharmony_ci			return 1;
547cc1dc7a3Sopenharmony_ci		}
548cc1dc7a3Sopenharmony_ci
549cc1dc7a3Sopenharmony_ci		if (!strcmp(argv[5], "-fastest"))
550cc1dc7a3Sopenharmony_ci		{
551cc1dc7a3Sopenharmony_ci			quality = ASTCENC_PRE_FASTEST;
552cc1dc7a3Sopenharmony_ci		}
553cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[5], "-fast"))
554cc1dc7a3Sopenharmony_ci		{
555cc1dc7a3Sopenharmony_ci			quality = ASTCENC_PRE_FAST;
556cc1dc7a3Sopenharmony_ci		}
557cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[5], "-medium"))
558cc1dc7a3Sopenharmony_ci		{
559cc1dc7a3Sopenharmony_ci			quality = ASTCENC_PRE_MEDIUM;
560cc1dc7a3Sopenharmony_ci		}
561cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[5], "-thorough"))
562cc1dc7a3Sopenharmony_ci		{
563cc1dc7a3Sopenharmony_ci			quality = ASTCENC_PRE_THOROUGH;
564cc1dc7a3Sopenharmony_ci		}
565cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[5], "-verythorough"))
566cc1dc7a3Sopenharmony_ci		{
567cc1dc7a3Sopenharmony_ci			quality = ASTCENC_PRE_VERYTHOROUGH;
568cc1dc7a3Sopenharmony_ci		}
569cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[5], "-exhaustive"))
570cc1dc7a3Sopenharmony_ci		{
571cc1dc7a3Sopenharmony_ci			quality = ASTCENC_PRE_EXHAUSTIVE;
572cc1dc7a3Sopenharmony_ci		}
573cc1dc7a3Sopenharmony_ci		else if (is_float(argv[5]))
574cc1dc7a3Sopenharmony_ci		{
575cc1dc7a3Sopenharmony_ci			quality = static_cast<float>(atof(argv[5]));
576cc1dc7a3Sopenharmony_ci		}
577cc1dc7a3Sopenharmony_ci		else
578cc1dc7a3Sopenharmony_ci		{
579cc1dc7a3Sopenharmony_ci			print_error("ERROR: Search quality/preset '%s' is invalid\n", argv[5]);
580cc1dc7a3Sopenharmony_ci			return 1;
581cc1dc7a3Sopenharmony_ci		}
582cc1dc7a3Sopenharmony_ci
583cc1dc7a3Sopenharmony_ci		argidx = 6;
584cc1dc7a3Sopenharmony_ci	}
585cc1dc7a3Sopenharmony_ci
586cc1dc7a3Sopenharmony_ci	unsigned int flags = 0;
587cc1dc7a3Sopenharmony_ci
588cc1dc7a3Sopenharmony_ci	// Gather the flags that we need
589cc1dc7a3Sopenharmony_ci	while (argidx < argc)
590cc1dc7a3Sopenharmony_ci	{
591cc1dc7a3Sopenharmony_ci		if (!strcmp(argv[argidx], "-a"))
592cc1dc7a3Sopenharmony_ci		{
593cc1dc7a3Sopenharmony_ci			// Skip over the data value for now
594cc1dc7a3Sopenharmony_ci			argidx++;
595cc1dc7a3Sopenharmony_ci			flags |= ASTCENC_FLG_USE_ALPHA_WEIGHT;
596cc1dc7a3Sopenharmony_ci		}
597cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-normal"))
598cc1dc7a3Sopenharmony_ci		{
599cc1dc7a3Sopenharmony_ci			flags |= ASTCENC_FLG_MAP_NORMAL;
600cc1dc7a3Sopenharmony_ci		}
601cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-decode_unorm8"))
602cc1dc7a3Sopenharmony_ci		{
603cc1dc7a3Sopenharmony_ci			flags |= ASTCENC_FLG_USE_DECODE_UNORM8;
604cc1dc7a3Sopenharmony_ci		}
605cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-rgbm"))
606cc1dc7a3Sopenharmony_ci		{
607cc1dc7a3Sopenharmony_ci			// Skip over the data value for now
608cc1dc7a3Sopenharmony_ci			argidx++;
609cc1dc7a3Sopenharmony_ci			flags |= ASTCENC_FLG_MAP_RGBM;
610cc1dc7a3Sopenharmony_ci		}
611cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-perceptual"))
612cc1dc7a3Sopenharmony_ci		{
613cc1dc7a3Sopenharmony_ci			flags |= ASTCENC_FLG_USE_PERCEPTUAL;
614cc1dc7a3Sopenharmony_ci		}
615cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-pp-normalize"))
616cc1dc7a3Sopenharmony_ci		{
617cc1dc7a3Sopenharmony_ci			if (preprocess != ASTCENC_PP_NONE)
618cc1dc7a3Sopenharmony_ci			{
619cc1dc7a3Sopenharmony_ci				print_error("ERROR: Only a single image preprocess can be used\n");
620cc1dc7a3Sopenharmony_ci				return 1;
621cc1dc7a3Sopenharmony_ci			}
622cc1dc7a3Sopenharmony_ci			preprocess = ASTCENC_PP_NORMALIZE;
623cc1dc7a3Sopenharmony_ci		}
624cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-pp-premultiply"))
625cc1dc7a3Sopenharmony_ci		{
626cc1dc7a3Sopenharmony_ci			if (preprocess != ASTCENC_PP_NONE)
627cc1dc7a3Sopenharmony_ci			{
628cc1dc7a3Sopenharmony_ci				print_error("ERROR: Only a single image preprocess can be used\n");
629cc1dc7a3Sopenharmony_ci				return 1;
630cc1dc7a3Sopenharmony_ci			}
631cc1dc7a3Sopenharmony_ci			preprocess = ASTCENC_PP_PREMULTIPLY;
632cc1dc7a3Sopenharmony_ci		}
633cc1dc7a3Sopenharmony_ci		argidx ++;
634cc1dc7a3Sopenharmony_ci	}
635cc1dc7a3Sopenharmony_ci
636cc1dc7a3Sopenharmony_ci#if defined(ASTCENC_DECOMPRESS_ONLY)
637cc1dc7a3Sopenharmony_ci	flags |= ASTCENC_FLG_DECOMPRESS_ONLY;
638cc1dc7a3Sopenharmony_ci#else
639cc1dc7a3Sopenharmony_ci	// Decompression can skip some memory allocation, but need full tables
640cc1dc7a3Sopenharmony_ci	if (operation == ASTCENC_OP_DECOMPRESS)
641cc1dc7a3Sopenharmony_ci	{
642cc1dc7a3Sopenharmony_ci		flags |= ASTCENC_FLG_DECOMPRESS_ONLY;
643cc1dc7a3Sopenharmony_ci	}
644cc1dc7a3Sopenharmony_ci	// Compression and test passes can skip some decimation initialization
645cc1dc7a3Sopenharmony_ci	// as we know we are decompressing images that were compressed using the
646cc1dc7a3Sopenharmony_ci	// same settings and heuristics ...
647cc1dc7a3Sopenharmony_ci	else
648cc1dc7a3Sopenharmony_ci	{
649cc1dc7a3Sopenharmony_ci		flags |= ASTCENC_FLG_SELF_DECOMPRESS_ONLY;
650cc1dc7a3Sopenharmony_ci	}
651cc1dc7a3Sopenharmony_ci#endif
652cc1dc7a3Sopenharmony_ci
653cc1dc7a3Sopenharmony_ci	astcenc_error status = astcenc_config_init(profile, block_x, block_y, block_z,
654cc1dc7a3Sopenharmony_ci	                                           quality, flags, &config);
655cc1dc7a3Sopenharmony_ci	if (status == ASTCENC_ERR_BAD_BLOCK_SIZE)
656cc1dc7a3Sopenharmony_ci	{
657cc1dc7a3Sopenharmony_ci		print_error("ERROR: Block size '%s' is invalid\n", argv[4]);
658cc1dc7a3Sopenharmony_ci		return 1;
659cc1dc7a3Sopenharmony_ci	}
660cc1dc7a3Sopenharmony_ci	else if (status == ASTCENC_ERR_BAD_DECODE_MODE)
661cc1dc7a3Sopenharmony_ci	{
662cc1dc7a3Sopenharmony_ci		print_error("ERROR: Decode_unorm8 is not supported by HDR profiles\n", argv[4]);
663cc1dc7a3Sopenharmony_ci		return 1;
664cc1dc7a3Sopenharmony_ci	}
665cc1dc7a3Sopenharmony_ci	else if (status == ASTCENC_ERR_BAD_CPU_FLOAT)
666cc1dc7a3Sopenharmony_ci	{
667cc1dc7a3Sopenharmony_ci		print_error("ERROR: astcenc must not be compiled with -ffast-math\n");
668cc1dc7a3Sopenharmony_ci		return 1;
669cc1dc7a3Sopenharmony_ci	}
670cc1dc7a3Sopenharmony_ci	else if (status != ASTCENC_SUCCESS)
671cc1dc7a3Sopenharmony_ci	{
672cc1dc7a3Sopenharmony_ci		print_error("ERROR: Init config failed with %s\n", astcenc_get_error_string(status));
673cc1dc7a3Sopenharmony_ci		return 1;
674cc1dc7a3Sopenharmony_ci	}
675cc1dc7a3Sopenharmony_ci
676cc1dc7a3Sopenharmony_ci	return 0;
677cc1dc7a3Sopenharmony_ci}
678cc1dc7a3Sopenharmony_ci
679cc1dc7a3Sopenharmony_ci/**
680cc1dc7a3Sopenharmony_ci * @brief Edit the astcenc_config
681cc1dc7a3Sopenharmony_ci *
682cc1dc7a3Sopenharmony_ci * @param         argc         Command line argument count.
683cc1dc7a3Sopenharmony_ci * @param[in]     argv         Command line argument vector.
684cc1dc7a3Sopenharmony_ci * @param         operation    Codec operation.
685cc1dc7a3Sopenharmony_ci * @param[out]    cli_config   Command line config.
686cc1dc7a3Sopenharmony_ci * @param[in,out] config       Codec configuration.
687cc1dc7a3Sopenharmony_ci *
688cc1dc7a3Sopenharmony_ci * @return 0 if everything is OK, 1 if there is some error
689cc1dc7a3Sopenharmony_ci */
690cc1dc7a3Sopenharmony_cistatic int edit_astcenc_config(
691cc1dc7a3Sopenharmony_ci	int argc,
692cc1dc7a3Sopenharmony_ci	char **argv,
693cc1dc7a3Sopenharmony_ci	const astcenc_operation operation,
694cc1dc7a3Sopenharmony_ci	cli_config_options& cli_config,
695cc1dc7a3Sopenharmony_ci	astcenc_config& config
696cc1dc7a3Sopenharmony_ci) {
697cc1dc7a3Sopenharmony_ci
698cc1dc7a3Sopenharmony_ci	int argidx = (operation & ASTCENC_STAGE_COMPRESS) ? 6 : 4;
699cc1dc7a3Sopenharmony_ci	config.privateProfile = HIGH_QUALITY_PROFILE;
700cc1dc7a3Sopenharmony_ci	while (argidx < argc)
701cc1dc7a3Sopenharmony_ci	{
702cc1dc7a3Sopenharmony_ci		if (!strcmp(argv[argidx], "-silent"))
703cc1dc7a3Sopenharmony_ci		{
704cc1dc7a3Sopenharmony_ci			argidx++;
705cc1dc7a3Sopenharmony_ci			cli_config.silentmode = 1;
706cc1dc7a3Sopenharmony_ci		}
707cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-cw"))
708cc1dc7a3Sopenharmony_ci		{
709cc1dc7a3Sopenharmony_ci			argidx += 5;
710cc1dc7a3Sopenharmony_ci			if (argidx > argc)
711cc1dc7a3Sopenharmony_ci			{
712cc1dc7a3Sopenharmony_ci				print_error("ERROR: -cw switch with less than 4 arguments\n");
713cc1dc7a3Sopenharmony_ci				return 1;
714cc1dc7a3Sopenharmony_ci			}
715cc1dc7a3Sopenharmony_ci
716cc1dc7a3Sopenharmony_ci			config.cw_r_weight = static_cast<float>(atof(argv[argidx - 4]));
717cc1dc7a3Sopenharmony_ci			config.cw_g_weight = static_cast<float>(atof(argv[argidx - 3]));
718cc1dc7a3Sopenharmony_ci			config.cw_b_weight = static_cast<float>(atof(argv[argidx - 2]));
719cc1dc7a3Sopenharmony_ci			config.cw_a_weight = static_cast<float>(atof(argv[argidx - 1]));
720cc1dc7a3Sopenharmony_ci		}
721cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-a"))
722cc1dc7a3Sopenharmony_ci		{
723cc1dc7a3Sopenharmony_ci			argidx += 2;
724cc1dc7a3Sopenharmony_ci			if (argidx > argc)
725cc1dc7a3Sopenharmony_ci			{
726cc1dc7a3Sopenharmony_ci				print_error("ERROR: -a switch with no argument\n");
727cc1dc7a3Sopenharmony_ci				return 1;
728cc1dc7a3Sopenharmony_ci			}
729cc1dc7a3Sopenharmony_ci
730cc1dc7a3Sopenharmony_ci			config.a_scale_radius = atoi(argv[argidx - 1]);
731cc1dc7a3Sopenharmony_ci		}
732cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-esw"))
733cc1dc7a3Sopenharmony_ci		{
734cc1dc7a3Sopenharmony_ci			argidx += 2;
735cc1dc7a3Sopenharmony_ci			if (argidx > argc)
736cc1dc7a3Sopenharmony_ci			{
737cc1dc7a3Sopenharmony_ci				print_error("ERROR: -esw switch with no argument\n");
738cc1dc7a3Sopenharmony_ci				return 1;
739cc1dc7a3Sopenharmony_ci			}
740cc1dc7a3Sopenharmony_ci
741cc1dc7a3Sopenharmony_ci			if (strlen(argv[argidx - 1]) != 4)
742cc1dc7a3Sopenharmony_ci			{
743cc1dc7a3Sopenharmony_ci				print_error("ERROR: -esw pattern does not contain 4 characters\n");
744cc1dc7a3Sopenharmony_ci				return 1;
745cc1dc7a3Sopenharmony_ci			}
746cc1dc7a3Sopenharmony_ci
747cc1dc7a3Sopenharmony_ci			astcenc_swz swizzle_components[4];
748cc1dc7a3Sopenharmony_ci			for (int i = 0; i < 4; i++)
749cc1dc7a3Sopenharmony_ci			{
750cc1dc7a3Sopenharmony_ci				switch (argv[argidx - 1][i])
751cc1dc7a3Sopenharmony_ci				{
752cc1dc7a3Sopenharmony_ci				case 'r':
753cc1dc7a3Sopenharmony_ci					swizzle_components[i] = ASTCENC_SWZ_R;
754cc1dc7a3Sopenharmony_ci					break;
755cc1dc7a3Sopenharmony_ci				case 'g':
756cc1dc7a3Sopenharmony_ci					swizzle_components[i] = ASTCENC_SWZ_G;
757cc1dc7a3Sopenharmony_ci					break;
758cc1dc7a3Sopenharmony_ci				case 'b':
759cc1dc7a3Sopenharmony_ci					swizzle_components[i] = ASTCENC_SWZ_B;
760cc1dc7a3Sopenharmony_ci					break;
761cc1dc7a3Sopenharmony_ci				case 'a':
762cc1dc7a3Sopenharmony_ci					swizzle_components[i] = ASTCENC_SWZ_A;
763cc1dc7a3Sopenharmony_ci					break;
764cc1dc7a3Sopenharmony_ci				case '0':
765cc1dc7a3Sopenharmony_ci					swizzle_components[i] = ASTCENC_SWZ_0;
766cc1dc7a3Sopenharmony_ci					break;
767cc1dc7a3Sopenharmony_ci				case '1':
768cc1dc7a3Sopenharmony_ci					swizzle_components[i] = ASTCENC_SWZ_1;
769cc1dc7a3Sopenharmony_ci					break;
770cc1dc7a3Sopenharmony_ci				default:
771cc1dc7a3Sopenharmony_ci					print_error("ERROR: -esw component '%c' is not valid\n", argv[argidx - 1][i]);
772cc1dc7a3Sopenharmony_ci					return 1;
773cc1dc7a3Sopenharmony_ci				}
774cc1dc7a3Sopenharmony_ci			}
775cc1dc7a3Sopenharmony_ci
776cc1dc7a3Sopenharmony_ci			cli_config.swz_encode.r = swizzle_components[0];
777cc1dc7a3Sopenharmony_ci			cli_config.swz_encode.g = swizzle_components[1];
778cc1dc7a3Sopenharmony_ci			cli_config.swz_encode.b = swizzle_components[2];
779cc1dc7a3Sopenharmony_ci			cli_config.swz_encode.a = swizzle_components[3];
780cc1dc7a3Sopenharmony_ci		}
781cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-ssw"))
782cc1dc7a3Sopenharmony_ci		{
783cc1dc7a3Sopenharmony_ci			argidx += 2;
784cc1dc7a3Sopenharmony_ci			if (argidx > argc)
785cc1dc7a3Sopenharmony_ci			{
786cc1dc7a3Sopenharmony_ci				print_error("ERROR: -ssw switch with no argument\n");
787cc1dc7a3Sopenharmony_ci				return 1;
788cc1dc7a3Sopenharmony_ci			}
789cc1dc7a3Sopenharmony_ci
790cc1dc7a3Sopenharmony_ci			size_t char_count = strlen(argv[argidx - 1]);
791cc1dc7a3Sopenharmony_ci			if (char_count == 0)
792cc1dc7a3Sopenharmony_ci			{
793cc1dc7a3Sopenharmony_ci				print_error("ERROR: -ssw pattern contains no characters\n");
794cc1dc7a3Sopenharmony_ci				return 1;
795cc1dc7a3Sopenharmony_ci			}
796cc1dc7a3Sopenharmony_ci
797cc1dc7a3Sopenharmony_ci			if (char_count > 4)
798cc1dc7a3Sopenharmony_ci			{
799cc1dc7a3Sopenharmony_ci				print_error("ERROR: -ssw pattern contains more than 4 characters\n");
800cc1dc7a3Sopenharmony_ci				return 1;
801cc1dc7a3Sopenharmony_ci			}
802cc1dc7a3Sopenharmony_ci
803cc1dc7a3Sopenharmony_ci			bool found_r = false;
804cc1dc7a3Sopenharmony_ci			bool found_g = false;
805cc1dc7a3Sopenharmony_ci			bool found_b = false;
806cc1dc7a3Sopenharmony_ci			bool found_a = false;
807cc1dc7a3Sopenharmony_ci
808cc1dc7a3Sopenharmony_ci			for (size_t i = 0; i < char_count; i++)
809cc1dc7a3Sopenharmony_ci			{
810cc1dc7a3Sopenharmony_ci				switch (argv[argidx - 1][i])
811cc1dc7a3Sopenharmony_ci				{
812cc1dc7a3Sopenharmony_ci				case 'r':
813cc1dc7a3Sopenharmony_ci					found_r = true;
814cc1dc7a3Sopenharmony_ci					break;
815cc1dc7a3Sopenharmony_ci				case 'g':
816cc1dc7a3Sopenharmony_ci					found_g = true;
817cc1dc7a3Sopenharmony_ci					break;
818cc1dc7a3Sopenharmony_ci				case 'b':
819cc1dc7a3Sopenharmony_ci					found_b = true;
820cc1dc7a3Sopenharmony_ci					break;
821cc1dc7a3Sopenharmony_ci				case 'a':
822cc1dc7a3Sopenharmony_ci					found_a = true;
823cc1dc7a3Sopenharmony_ci					break;
824cc1dc7a3Sopenharmony_ci				default:
825cc1dc7a3Sopenharmony_ci					print_error("ERROR: -ssw component '%c' is not valid\n", argv[argidx - 1][i]);
826cc1dc7a3Sopenharmony_ci					return 1;
827cc1dc7a3Sopenharmony_ci				}
828cc1dc7a3Sopenharmony_ci			}
829cc1dc7a3Sopenharmony_ci
830cc1dc7a3Sopenharmony_ci			config.cw_r_weight = found_r ? 1.0f : 0.0f;
831cc1dc7a3Sopenharmony_ci			config.cw_g_weight = found_g ? 1.0f : 0.0f;
832cc1dc7a3Sopenharmony_ci			config.cw_b_weight = found_b ? 1.0f : 0.0f;
833cc1dc7a3Sopenharmony_ci			config.cw_a_weight = found_a ? 1.0f : 0.0f;
834cc1dc7a3Sopenharmony_ci		}
835cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-dsw"))
836cc1dc7a3Sopenharmony_ci		{
837cc1dc7a3Sopenharmony_ci			argidx += 2;
838cc1dc7a3Sopenharmony_ci			if (argidx > argc)
839cc1dc7a3Sopenharmony_ci			{
840cc1dc7a3Sopenharmony_ci				print_error("ERROR: -dsw switch with no argument\n");
841cc1dc7a3Sopenharmony_ci				return 1;
842cc1dc7a3Sopenharmony_ci			}
843cc1dc7a3Sopenharmony_ci
844cc1dc7a3Sopenharmony_ci			if (strlen(argv[argidx - 1]) != 4)
845cc1dc7a3Sopenharmony_ci			{
846cc1dc7a3Sopenharmony_ci				print_error("ERROR: -dsw switch does not contain 4 characters\n");
847cc1dc7a3Sopenharmony_ci				return 1;
848cc1dc7a3Sopenharmony_ci			}
849cc1dc7a3Sopenharmony_ci
850cc1dc7a3Sopenharmony_ci			astcenc_swz swizzle_components[4];
851cc1dc7a3Sopenharmony_ci			for (int i = 0; i < 4; i++)
852cc1dc7a3Sopenharmony_ci			{
853cc1dc7a3Sopenharmony_ci				switch (argv[argidx - 1][i])
854cc1dc7a3Sopenharmony_ci				{
855cc1dc7a3Sopenharmony_ci				case 'r':
856cc1dc7a3Sopenharmony_ci					swizzle_components[i] = ASTCENC_SWZ_R;
857cc1dc7a3Sopenharmony_ci					break;
858cc1dc7a3Sopenharmony_ci				case 'g':
859cc1dc7a3Sopenharmony_ci					swizzle_components[i] = ASTCENC_SWZ_G;
860cc1dc7a3Sopenharmony_ci					break;
861cc1dc7a3Sopenharmony_ci				case 'b':
862cc1dc7a3Sopenharmony_ci					swizzle_components[i] = ASTCENC_SWZ_B;
863cc1dc7a3Sopenharmony_ci					break;
864cc1dc7a3Sopenharmony_ci				case 'a':
865cc1dc7a3Sopenharmony_ci					swizzle_components[i] = ASTCENC_SWZ_A;
866cc1dc7a3Sopenharmony_ci					break;
867cc1dc7a3Sopenharmony_ci				case '0':
868cc1dc7a3Sopenharmony_ci					swizzle_components[i] = ASTCENC_SWZ_0;
869cc1dc7a3Sopenharmony_ci					break;
870cc1dc7a3Sopenharmony_ci				case '1':
871cc1dc7a3Sopenharmony_ci					swizzle_components[i] = ASTCENC_SWZ_1;
872cc1dc7a3Sopenharmony_ci					break;
873cc1dc7a3Sopenharmony_ci				case 'z':
874cc1dc7a3Sopenharmony_ci					swizzle_components[i] =  ASTCENC_SWZ_Z;
875cc1dc7a3Sopenharmony_ci					break;
876cc1dc7a3Sopenharmony_ci				default:
877cc1dc7a3Sopenharmony_ci					print_error("ERROR: ERROR: -dsw component '%c' is not valid\n", argv[argidx - 1][i]);
878cc1dc7a3Sopenharmony_ci					return 1;
879cc1dc7a3Sopenharmony_ci				}
880cc1dc7a3Sopenharmony_ci			}
881cc1dc7a3Sopenharmony_ci
882cc1dc7a3Sopenharmony_ci			cli_config.swz_decode.r = swizzle_components[0];
883cc1dc7a3Sopenharmony_ci			cli_config.swz_decode.g = swizzle_components[1];
884cc1dc7a3Sopenharmony_ci			cli_config.swz_decode.b = swizzle_components[2];
885cc1dc7a3Sopenharmony_ci			cli_config.swz_decode.a = swizzle_components[3];
886cc1dc7a3Sopenharmony_ci		}
887cc1dc7a3Sopenharmony_ci		// presets begin here
888cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-normal"))
889cc1dc7a3Sopenharmony_ci		{
890cc1dc7a3Sopenharmony_ci			argidx++;
891cc1dc7a3Sopenharmony_ci
892cc1dc7a3Sopenharmony_ci			cli_config.swz_encode.r = ASTCENC_SWZ_R;
893cc1dc7a3Sopenharmony_ci			cli_config.swz_encode.g = ASTCENC_SWZ_R;
894cc1dc7a3Sopenharmony_ci			cli_config.swz_encode.b = ASTCENC_SWZ_R;
895cc1dc7a3Sopenharmony_ci			cli_config.swz_encode.a = ASTCENC_SWZ_G;
896cc1dc7a3Sopenharmony_ci
897cc1dc7a3Sopenharmony_ci			cli_config.swz_decode.r = ASTCENC_SWZ_R;
898cc1dc7a3Sopenharmony_ci			cli_config.swz_decode.g = ASTCENC_SWZ_A;
899cc1dc7a3Sopenharmony_ci			cli_config.swz_decode.b = ASTCENC_SWZ_Z;
900cc1dc7a3Sopenharmony_ci			cli_config.swz_decode.a = ASTCENC_SWZ_1;
901cc1dc7a3Sopenharmony_ci		}
902cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-rgbm"))
903cc1dc7a3Sopenharmony_ci		{
904cc1dc7a3Sopenharmony_ci			argidx += 2;
905cc1dc7a3Sopenharmony_ci			if (argidx > argc)
906cc1dc7a3Sopenharmony_ci			{
907cc1dc7a3Sopenharmony_ci				print_error("ERROR: -rgbm switch with no argument\n");
908cc1dc7a3Sopenharmony_ci				return 1;
909cc1dc7a3Sopenharmony_ci			}
910cc1dc7a3Sopenharmony_ci
911cc1dc7a3Sopenharmony_ci			config.rgbm_m_scale = static_cast<float>(atof(argv[argidx - 1]));
912cc1dc7a3Sopenharmony_ci			config.cw_a_weight = 2.0f * config.rgbm_m_scale;
913cc1dc7a3Sopenharmony_ci		}
914cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-decode_unorm8"))
915cc1dc7a3Sopenharmony_ci		{
916cc1dc7a3Sopenharmony_ci			argidx++;
917cc1dc7a3Sopenharmony_ci		}
918cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-perceptual"))
919cc1dc7a3Sopenharmony_ci		{
920cc1dc7a3Sopenharmony_ci			argidx++;
921cc1dc7a3Sopenharmony_ci		}
922cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-pp-normalize"))
923cc1dc7a3Sopenharmony_ci		{
924cc1dc7a3Sopenharmony_ci			argidx++;
925cc1dc7a3Sopenharmony_ci		}
926cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-pp-premultiply"))
927cc1dc7a3Sopenharmony_ci		{
928cc1dc7a3Sopenharmony_ci			argidx++;
929cc1dc7a3Sopenharmony_ci		}
930cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-blockmodelimit"))
931cc1dc7a3Sopenharmony_ci		{
932cc1dc7a3Sopenharmony_ci			argidx += 2;
933cc1dc7a3Sopenharmony_ci			if (argidx > argc)
934cc1dc7a3Sopenharmony_ci			{
935cc1dc7a3Sopenharmony_ci				print_error("ERROR: -blockmodelimit switch with no argument\n");
936cc1dc7a3Sopenharmony_ci				return 1;
937cc1dc7a3Sopenharmony_ci			}
938cc1dc7a3Sopenharmony_ci
939cc1dc7a3Sopenharmony_ci			config.tune_block_mode_limit = atoi(argv[argidx - 1]);
940cc1dc7a3Sopenharmony_ci		}
941cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-partitioncountlimit"))
942cc1dc7a3Sopenharmony_ci		{
943cc1dc7a3Sopenharmony_ci			argidx += 2;
944cc1dc7a3Sopenharmony_ci			if (argidx > argc)
945cc1dc7a3Sopenharmony_ci			{
946cc1dc7a3Sopenharmony_ci				print_error("ERROR: -partitioncountlimit switch with no argument\n");
947cc1dc7a3Sopenharmony_ci				return 1;
948cc1dc7a3Sopenharmony_ci			}
949cc1dc7a3Sopenharmony_ci
950cc1dc7a3Sopenharmony_ci			config.tune_partition_count_limit = atoi(argv[argidx - 1]);
951cc1dc7a3Sopenharmony_ci		}
952cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-2partitionindexlimit"))
953cc1dc7a3Sopenharmony_ci		{
954cc1dc7a3Sopenharmony_ci			argidx += 2;
955cc1dc7a3Sopenharmony_ci			if (argidx > argc)
956cc1dc7a3Sopenharmony_ci			{
957cc1dc7a3Sopenharmony_ci				print_error("ERROR: -2partitionindexlimit switch with no argument\n");
958cc1dc7a3Sopenharmony_ci				return 1;
959cc1dc7a3Sopenharmony_ci			}
960cc1dc7a3Sopenharmony_ci
961cc1dc7a3Sopenharmony_ci			config.tune_2partition_index_limit = atoi(argv[argidx - 1]);
962cc1dc7a3Sopenharmony_ci		}
963cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-3partitionindexlimit"))
964cc1dc7a3Sopenharmony_ci		{
965cc1dc7a3Sopenharmony_ci			argidx += 2;
966cc1dc7a3Sopenharmony_ci			if (argidx > argc)
967cc1dc7a3Sopenharmony_ci			{
968cc1dc7a3Sopenharmony_ci				print_error("ERROR: -3partitionindexlimit switch with no argument\n");
969cc1dc7a3Sopenharmony_ci				return 1;
970cc1dc7a3Sopenharmony_ci			}
971cc1dc7a3Sopenharmony_ci
972cc1dc7a3Sopenharmony_ci			config.tune_3partition_index_limit = atoi(argv[argidx - 1]);
973cc1dc7a3Sopenharmony_ci		}
974cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-4partitionindexlimit"))
975cc1dc7a3Sopenharmony_ci		{
976cc1dc7a3Sopenharmony_ci			argidx += 2;
977cc1dc7a3Sopenharmony_ci			if (argidx > argc)
978cc1dc7a3Sopenharmony_ci			{
979cc1dc7a3Sopenharmony_ci				print_error("ERROR: -4partitionindexlimit switch with no argument\n");
980cc1dc7a3Sopenharmony_ci				return 1;
981cc1dc7a3Sopenharmony_ci			}
982cc1dc7a3Sopenharmony_ci
983cc1dc7a3Sopenharmony_ci			config.tune_4partition_index_limit = atoi(argv[argidx - 1]);
984cc1dc7a3Sopenharmony_ci		}
985cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-2partitioncandidatelimit"))
986cc1dc7a3Sopenharmony_ci		{
987cc1dc7a3Sopenharmony_ci			argidx += 2;
988cc1dc7a3Sopenharmony_ci			if (argidx > argc)
989cc1dc7a3Sopenharmony_ci			{
990cc1dc7a3Sopenharmony_ci				print_error("ERROR: -2partitioncandidatelimit switch with no argument\n");
991cc1dc7a3Sopenharmony_ci				return 1;
992cc1dc7a3Sopenharmony_ci			}
993cc1dc7a3Sopenharmony_ci
994cc1dc7a3Sopenharmony_ci			config.tune_2partitioning_candidate_limit = atoi(argv[argidx - 1]);
995cc1dc7a3Sopenharmony_ci		}
996cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-3partitioncandidatelimit"))
997cc1dc7a3Sopenharmony_ci		{
998cc1dc7a3Sopenharmony_ci			argidx += 2;
999cc1dc7a3Sopenharmony_ci			if (argidx > argc)
1000cc1dc7a3Sopenharmony_ci			{
1001cc1dc7a3Sopenharmony_ci				print_error("ERROR: -3partitioncandidatelimit switch with no argument\n");
1002cc1dc7a3Sopenharmony_ci				return 1;
1003cc1dc7a3Sopenharmony_ci			}
1004cc1dc7a3Sopenharmony_ci
1005cc1dc7a3Sopenharmony_ci			config.tune_3partitioning_candidate_limit = atoi(argv[argidx - 1]);
1006cc1dc7a3Sopenharmony_ci		}
1007cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-4partitioncandidatelimit"))
1008cc1dc7a3Sopenharmony_ci		{
1009cc1dc7a3Sopenharmony_ci			argidx += 2;
1010cc1dc7a3Sopenharmony_ci			if (argidx > argc)
1011cc1dc7a3Sopenharmony_ci			{
1012cc1dc7a3Sopenharmony_ci				print_error("ERROR: -4partitioncandidatelimit switch with no argument\n");
1013cc1dc7a3Sopenharmony_ci				return 1;
1014cc1dc7a3Sopenharmony_ci			}
1015cc1dc7a3Sopenharmony_ci
1016cc1dc7a3Sopenharmony_ci			config.tune_4partitioning_candidate_limit = atoi(argv[argidx - 1]);
1017cc1dc7a3Sopenharmony_ci		}
1018cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-dblimit"))
1019cc1dc7a3Sopenharmony_ci		{
1020cc1dc7a3Sopenharmony_ci			argidx += 2;
1021cc1dc7a3Sopenharmony_ci			if (argidx > argc)
1022cc1dc7a3Sopenharmony_ci			{
1023cc1dc7a3Sopenharmony_ci				print_error("ERROR: -dblimit switch with no argument\n");
1024cc1dc7a3Sopenharmony_ci				return 1;
1025cc1dc7a3Sopenharmony_ci			}
1026cc1dc7a3Sopenharmony_ci
1027cc1dc7a3Sopenharmony_ci			if ((config.profile == ASTCENC_PRF_LDR) || (config.profile == ASTCENC_PRF_LDR_SRGB))
1028cc1dc7a3Sopenharmony_ci			{
1029cc1dc7a3Sopenharmony_ci				config.tune_db_limit = static_cast<float>(atof(argv[argidx - 1]));
1030cc1dc7a3Sopenharmony_ci			}
1031cc1dc7a3Sopenharmony_ci		}
1032cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-2partitionlimitfactor"))
1033cc1dc7a3Sopenharmony_ci		{
1034cc1dc7a3Sopenharmony_ci			argidx += 2;
1035cc1dc7a3Sopenharmony_ci			if (argidx > argc)
1036cc1dc7a3Sopenharmony_ci			{
1037cc1dc7a3Sopenharmony_ci				print_error("ERROR: -2partitionlimitfactor switch with no argument\n");
1038cc1dc7a3Sopenharmony_ci				return 1;
1039cc1dc7a3Sopenharmony_ci			}
1040cc1dc7a3Sopenharmony_ci
1041cc1dc7a3Sopenharmony_ci			config.tune_2partition_early_out_limit_factor = static_cast<float>(atof(argv[argidx - 1]));
1042cc1dc7a3Sopenharmony_ci		}
1043cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-3partitionlimitfactor"))
1044cc1dc7a3Sopenharmony_ci		{
1045cc1dc7a3Sopenharmony_ci			argidx += 2;
1046cc1dc7a3Sopenharmony_ci			if (argidx > argc)
1047cc1dc7a3Sopenharmony_ci			{
1048cc1dc7a3Sopenharmony_ci				print_error("ERROR: -3partitionlimitfactor switch with no argument\n");
1049cc1dc7a3Sopenharmony_ci				return 1;
1050cc1dc7a3Sopenharmony_ci			}
1051cc1dc7a3Sopenharmony_ci
1052cc1dc7a3Sopenharmony_ci			config.tune_3partition_early_out_limit_factor = static_cast<float>(atof(argv[argidx - 1]));
1053cc1dc7a3Sopenharmony_ci		}
1054cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-2planelimitcorrelation"))
1055cc1dc7a3Sopenharmony_ci		{
1056cc1dc7a3Sopenharmony_ci			argidx += 2;
1057cc1dc7a3Sopenharmony_ci			if (argidx > argc)
1058cc1dc7a3Sopenharmony_ci			{
1059cc1dc7a3Sopenharmony_ci				print_error("ERROR: -2planelimitcorrelation switch with no argument\n");
1060cc1dc7a3Sopenharmony_ci				return 1;
1061cc1dc7a3Sopenharmony_ci			}
1062cc1dc7a3Sopenharmony_ci
1063cc1dc7a3Sopenharmony_ci			config.tune_2plane_early_out_limit_correlation = static_cast<float>(atof(argv[argidx - 1]));
1064cc1dc7a3Sopenharmony_ci		}
1065cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-refinementlimit"))
1066cc1dc7a3Sopenharmony_ci		{
1067cc1dc7a3Sopenharmony_ci			argidx += 2;
1068cc1dc7a3Sopenharmony_ci			if (argidx > argc)
1069cc1dc7a3Sopenharmony_ci			{
1070cc1dc7a3Sopenharmony_ci				print_error("ERROR: -refinementlimit switch with no argument\n");
1071cc1dc7a3Sopenharmony_ci				return 1;
1072cc1dc7a3Sopenharmony_ci			}
1073cc1dc7a3Sopenharmony_ci
1074cc1dc7a3Sopenharmony_ci			config.tune_refinement_limit = atoi(argv[argidx - 1]);
1075cc1dc7a3Sopenharmony_ci		}
1076cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-candidatelimit"))
1077cc1dc7a3Sopenharmony_ci		{
1078cc1dc7a3Sopenharmony_ci			argidx += 2;
1079cc1dc7a3Sopenharmony_ci			if (argidx > argc)
1080cc1dc7a3Sopenharmony_ci			{
1081cc1dc7a3Sopenharmony_ci				print_error("ERROR: -candidatelimit switch with no argument\n");
1082cc1dc7a3Sopenharmony_ci				return 1;
1083cc1dc7a3Sopenharmony_ci			}
1084cc1dc7a3Sopenharmony_ci
1085cc1dc7a3Sopenharmony_ci			config.tune_candidate_limit = atoi(argv[argidx - 1]);
1086cc1dc7a3Sopenharmony_ci		}
1087cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-j"))
1088cc1dc7a3Sopenharmony_ci		{
1089cc1dc7a3Sopenharmony_ci			argidx += 2;
1090cc1dc7a3Sopenharmony_ci			if (argidx > argc)
1091cc1dc7a3Sopenharmony_ci			{
1092cc1dc7a3Sopenharmony_ci				print_error("ERROR: -j switch with no argument\n");
1093cc1dc7a3Sopenharmony_ci				return 1;
1094cc1dc7a3Sopenharmony_ci			}
1095cc1dc7a3Sopenharmony_ci
1096cc1dc7a3Sopenharmony_ci			cli_config.thread_count = atoi(argv[argidx - 1]);
1097cc1dc7a3Sopenharmony_ci		}
1098cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-repeats"))
1099cc1dc7a3Sopenharmony_ci		{
1100cc1dc7a3Sopenharmony_ci			argidx += 2;
1101cc1dc7a3Sopenharmony_ci			if (argidx > argc)
1102cc1dc7a3Sopenharmony_ci			{
1103cc1dc7a3Sopenharmony_ci				print_error("ERROR: -repeats switch with no argument\n");
1104cc1dc7a3Sopenharmony_ci				return 1;
1105cc1dc7a3Sopenharmony_ci			}
1106cc1dc7a3Sopenharmony_ci
1107cc1dc7a3Sopenharmony_ci			cli_config.repeat_count = atoi(argv[argidx - 1]);
1108cc1dc7a3Sopenharmony_ci			if (cli_config.repeat_count <= 0)
1109cc1dc7a3Sopenharmony_ci			{
1110cc1dc7a3Sopenharmony_ci				print_error("ERROR: -repeats value must be at least one\n");
1111cc1dc7a3Sopenharmony_ci				return 1;
1112cc1dc7a3Sopenharmony_ci			}
1113cc1dc7a3Sopenharmony_ci		}
1114cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-yflip"))
1115cc1dc7a3Sopenharmony_ci		{
1116cc1dc7a3Sopenharmony_ci			argidx++;
1117cc1dc7a3Sopenharmony_ci			cli_config.y_flip = 1;
1118cc1dc7a3Sopenharmony_ci		}
1119cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-mpsnr"))
1120cc1dc7a3Sopenharmony_ci		{
1121cc1dc7a3Sopenharmony_ci			argidx += 3;
1122cc1dc7a3Sopenharmony_ci			if (argidx > argc)
1123cc1dc7a3Sopenharmony_ci			{
1124cc1dc7a3Sopenharmony_ci				print_error("ERROR: -mpsnr switch with less than 2 arguments\n");
1125cc1dc7a3Sopenharmony_ci				return 1;
1126cc1dc7a3Sopenharmony_ci			}
1127cc1dc7a3Sopenharmony_ci
1128cc1dc7a3Sopenharmony_ci			cli_config.low_fstop = atoi(argv[argidx - 2]);
1129cc1dc7a3Sopenharmony_ci			cli_config.high_fstop = atoi(argv[argidx - 1]);
1130cc1dc7a3Sopenharmony_ci			if (cli_config.high_fstop < cli_config.low_fstop)
1131cc1dc7a3Sopenharmony_ci			{
1132cc1dc7a3Sopenharmony_ci				print_error("ERROR: -mpsnr switch <low> is greater than the <high>\n");
1133cc1dc7a3Sopenharmony_ci				return 1;
1134cc1dc7a3Sopenharmony_ci			}
1135cc1dc7a3Sopenharmony_ci		}
1136cc1dc7a3Sopenharmony_ci		// Option: Encode a 3D image from a sequence of 2D images.
1137cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-zdim"))
1138cc1dc7a3Sopenharmony_ci		{
1139cc1dc7a3Sopenharmony_ci			// Only supports compressing
1140cc1dc7a3Sopenharmony_ci			if (!(operation & ASTCENC_STAGE_COMPRESS))
1141cc1dc7a3Sopenharmony_ci			{
1142cc1dc7a3Sopenharmony_ci				print_error("ERROR: -zdim switch is only valid for compression\n");
1143cc1dc7a3Sopenharmony_ci				return 1;
1144cc1dc7a3Sopenharmony_ci			}
1145cc1dc7a3Sopenharmony_ci
1146cc1dc7a3Sopenharmony_ci			// Image depth must be specified.
1147cc1dc7a3Sopenharmony_ci			if (argidx + 2 > argc)
1148cc1dc7a3Sopenharmony_ci			{
1149cc1dc7a3Sopenharmony_ci				print_error("ERROR: -zdim switch with no argument\n");
1150cc1dc7a3Sopenharmony_ci				return 1;
1151cc1dc7a3Sopenharmony_ci			}
1152cc1dc7a3Sopenharmony_ci			argidx++;
1153cc1dc7a3Sopenharmony_ci
1154cc1dc7a3Sopenharmony_ci			// Read array size (image depth).
1155cc1dc7a3Sopenharmony_ci			if (!sscanf(argv[argidx], "%u", &cli_config.array_size) || cli_config.array_size == 0)
1156cc1dc7a3Sopenharmony_ci			{
1157cc1dc7a3Sopenharmony_ci				print_error("ERROR: -zdim size '%s' is invalid\n", argv[argidx]);
1158cc1dc7a3Sopenharmony_ci				return 1;
1159cc1dc7a3Sopenharmony_ci			}
1160cc1dc7a3Sopenharmony_ci
1161cc1dc7a3Sopenharmony_ci			if ((cli_config.array_size > 1) && (config.block_z == 1))
1162cc1dc7a3Sopenharmony_ci			{
1163cc1dc7a3Sopenharmony_ci				print_error("ERROR: -zdim with 3D input data for a 2D output format\n");
1164cc1dc7a3Sopenharmony_ci				return 1;
1165cc1dc7a3Sopenharmony_ci			}
1166cc1dc7a3Sopenharmony_ci			argidx++;
1167cc1dc7a3Sopenharmony_ci		}
1168cc1dc7a3Sopenharmony_ci#if defined(ASTCENC_DIAGNOSTICS)
1169cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-dtrace"))
1170cc1dc7a3Sopenharmony_ci		{
1171cc1dc7a3Sopenharmony_ci			argidx += 2;
1172cc1dc7a3Sopenharmony_ci			if (argidx > argc)
1173cc1dc7a3Sopenharmony_ci			{
1174cc1dc7a3Sopenharmony_ci				print_error("ERROR: -dtrace switch with no argument\n");
1175cc1dc7a3Sopenharmony_ci				return 1;
1176cc1dc7a3Sopenharmony_ci			}
1177cc1dc7a3Sopenharmony_ci
1178cc1dc7a3Sopenharmony_ci			config.trace_file_path = argv[argidx - 1];
1179cc1dc7a3Sopenharmony_ci		}
1180cc1dc7a3Sopenharmony_ci#endif
1181cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-privateProfile"))
1182cc1dc7a3Sopenharmony_ci		{
1183cc1dc7a3Sopenharmony_ci			argidx += 2; // skip 2 chatacters to get next parameter
1184cc1dc7a3Sopenharmony_ci			config.privateProfile = static_cast<QualityProfile>(atoi(argv[argidx - 1]));
1185cc1dc7a3Sopenharmony_ci		}
1186cc1dc7a3Sopenharmony_ci		else if (!strcmp(argv[argidx], "-dimage"))
1187cc1dc7a3Sopenharmony_ci		{
1188cc1dc7a3Sopenharmony_ci			argidx += 1;
1189cc1dc7a3Sopenharmony_ci			cli_config.diagnostic_images = true;
1190cc1dc7a3Sopenharmony_ci		}
1191cc1dc7a3Sopenharmony_ci		else // check others as well
1192cc1dc7a3Sopenharmony_ci		{
1193cc1dc7a3Sopenharmony_ci			print_error("ERROR: Argument '%s' not recognized\n", argv[argidx]);
1194cc1dc7a3Sopenharmony_ci			return 1;
1195cc1dc7a3Sopenharmony_ci		}
1196cc1dc7a3Sopenharmony_ci	}
1197cc1dc7a3Sopenharmony_ci
1198cc1dc7a3Sopenharmony_ci	if (cli_config.thread_count <= 0)
1199cc1dc7a3Sopenharmony_ci	{
1200cc1dc7a3Sopenharmony_ci		cli_config.thread_count = get_cpu_count();
1201cc1dc7a3Sopenharmony_ci	}
1202cc1dc7a3Sopenharmony_ci
1203cc1dc7a3Sopenharmony_ci#if defined(ASTCENC_DIAGNOSTICS)
1204cc1dc7a3Sopenharmony_ci	// Force single threaded for diagnostic builds
1205cc1dc7a3Sopenharmony_ci	cli_config.thread_count = 1;
1206cc1dc7a3Sopenharmony_ci
1207cc1dc7a3Sopenharmony_ci	if (!config.trace_file_path)
1208cc1dc7a3Sopenharmony_ci	{
1209cc1dc7a3Sopenharmony_ci		print_error("ERROR: Diagnostics builds must set -dtrace\n");
1210cc1dc7a3Sopenharmony_ci		return 1;
1211cc1dc7a3Sopenharmony_ci	}
1212cc1dc7a3Sopenharmony_ci#endif
1213cc1dc7a3Sopenharmony_ci
1214cc1dc7a3Sopenharmony_ci	return 0;
1215cc1dc7a3Sopenharmony_ci}
1216cc1dc7a3Sopenharmony_ci
1217cc1dc7a3Sopenharmony_ci/**
1218cc1dc7a3Sopenharmony_ci * @brief Print the config settings in a human readable form.
1219cc1dc7a3Sopenharmony_ci *
1220cc1dc7a3Sopenharmony_ci * @param[in] cli_config   Command line config.
1221cc1dc7a3Sopenharmony_ci * @param[in] config       Codec configuration.
1222cc1dc7a3Sopenharmony_ci */
1223cc1dc7a3Sopenharmony_cistatic void print_astcenc_config(
1224cc1dc7a3Sopenharmony_ci	const cli_config_options& cli_config,
1225cc1dc7a3Sopenharmony_ci	const astcenc_config& config
1226cc1dc7a3Sopenharmony_ci) {
1227cc1dc7a3Sopenharmony_ci	// Print all encoding settings unless specifically told otherwise
1228cc1dc7a3Sopenharmony_ci	if (!cli_config.silentmode)
1229cc1dc7a3Sopenharmony_ci	{
1230cc1dc7a3Sopenharmony_ci		printf("Compressor settings\n");
1231cc1dc7a3Sopenharmony_ci		printf("===================\n\n");
1232cc1dc7a3Sopenharmony_ci
1233cc1dc7a3Sopenharmony_ci		switch (config.profile)
1234cc1dc7a3Sopenharmony_ci		{
1235cc1dc7a3Sopenharmony_ci		case ASTCENC_PRF_LDR:
1236cc1dc7a3Sopenharmony_ci			printf("    Color profile:              LDR linear\n");
1237cc1dc7a3Sopenharmony_ci			break;
1238cc1dc7a3Sopenharmony_ci		case ASTCENC_PRF_LDR_SRGB:
1239cc1dc7a3Sopenharmony_ci			printf("    Color profile:              LDR sRGB\n");
1240cc1dc7a3Sopenharmony_ci			break;
1241cc1dc7a3Sopenharmony_ci		case ASTCENC_PRF_HDR_RGB_LDR_A:
1242cc1dc7a3Sopenharmony_ci			printf("    Color profile:              HDR RGB + LDR A\n");
1243cc1dc7a3Sopenharmony_ci			break;
1244cc1dc7a3Sopenharmony_ci		case ASTCENC_PRF_HDR:
1245cc1dc7a3Sopenharmony_ci			printf("    Color profile:              HDR RGBA\n");
1246cc1dc7a3Sopenharmony_ci			break;
1247cc1dc7a3Sopenharmony_ci		}
1248cc1dc7a3Sopenharmony_ci
1249cc1dc7a3Sopenharmony_ci		if (config.block_z == 1)
1250cc1dc7a3Sopenharmony_ci		{
1251cc1dc7a3Sopenharmony_ci			printf("    Block size:                 %ux%u\n", config.block_x, config.block_y);
1252cc1dc7a3Sopenharmony_ci		}
1253cc1dc7a3Sopenharmony_ci		else
1254cc1dc7a3Sopenharmony_ci		{
1255cc1dc7a3Sopenharmony_ci			printf("    Block size:                 %ux%ux%u\n", config.block_x, config.block_y, config.block_z);
1256cc1dc7a3Sopenharmony_ci		}
1257cc1dc7a3Sopenharmony_ci
1258cc1dc7a3Sopenharmony_ci		printf("    Bitrate:                    %3.2f bpp\n", 128.0 / (config.block_x * config.block_y * config.block_z));
1259cc1dc7a3Sopenharmony_ci		printf("    RGB alpha scale weight:     %d\n", (config.flags & ASTCENC_FLG_USE_ALPHA_WEIGHT));
1260cc1dc7a3Sopenharmony_ci		if ((config.flags & ASTCENC_FLG_USE_ALPHA_WEIGHT))
1261cc1dc7a3Sopenharmony_ci		{
1262cc1dc7a3Sopenharmony_ci			printf("    Radius RGB alpha scale:     %u texels\n", config.a_scale_radius);
1263cc1dc7a3Sopenharmony_ci		}
1264cc1dc7a3Sopenharmony_ci
1265cc1dc7a3Sopenharmony_ci		printf("    R component weight:         %g\n", static_cast<double>(config.cw_r_weight));
1266cc1dc7a3Sopenharmony_ci		printf("    G component weight:         %g\n", static_cast<double>(config.cw_g_weight));
1267cc1dc7a3Sopenharmony_ci		printf("    B component weight:         %g\n", static_cast<double>(config.cw_b_weight));
1268cc1dc7a3Sopenharmony_ci		printf("    A component weight:         %g\n", static_cast<double>(config.cw_a_weight));
1269cc1dc7a3Sopenharmony_ci		printf("    Partition cutoff:           %u partitions\n", config.tune_partition_count_limit);
1270cc1dc7a3Sopenharmony_ci		printf("    2 partition index cutoff:   %u partition ids\n", config.tune_2partition_index_limit);
1271cc1dc7a3Sopenharmony_ci		printf("    3 partition index cutoff:   %u partition ids\n", config.tune_3partition_index_limit);
1272cc1dc7a3Sopenharmony_ci		printf("    4 partition index cutoff:   %u partition ids\n", config.tune_4partition_index_limit);
1273cc1dc7a3Sopenharmony_ci		printf("    PSNR cutoff:                %g dB\n", static_cast<double>(config.tune_db_limit));
1274cc1dc7a3Sopenharmony_ci		printf("    3 partition cutoff:         %g\n", static_cast<double>(config.tune_2partition_early_out_limit_factor));
1275cc1dc7a3Sopenharmony_ci		printf("    4 partition cutoff:         %g\n", static_cast<double>(config.tune_3partition_early_out_limit_factor));
1276cc1dc7a3Sopenharmony_ci		printf("    2 plane correlation cutoff: %g\n", static_cast<double>(config.tune_2plane_early_out_limit_correlation));
1277cc1dc7a3Sopenharmony_ci		printf("    Block mode centile cutoff:  %g%%\n", static_cast<double>(config.tune_block_mode_limit));
1278cc1dc7a3Sopenharmony_ci		printf("    Candidate cutoff:           %u candidates\n", config.tune_candidate_limit);
1279cc1dc7a3Sopenharmony_ci		printf("    Refinement cutoff:          %u iterations\n", config.tune_refinement_limit);
1280cc1dc7a3Sopenharmony_ci		printf("    Compressor thread count:    %d\n", cli_config.thread_count);
1281cc1dc7a3Sopenharmony_ci		printf("\n");
1282cc1dc7a3Sopenharmony_ci	}
1283cc1dc7a3Sopenharmony_ci}
1284cc1dc7a3Sopenharmony_ci
1285cc1dc7a3Sopenharmony_ci/**
1286cc1dc7a3Sopenharmony_ci * @brief Get the value of a single pixel in an image.
1287cc1dc7a3Sopenharmony_ci *
1288cc1dc7a3Sopenharmony_ci * Note, this implementation is not particularly optimal as it puts format
1289cc1dc7a3Sopenharmony_ci * checks in the inner-most loop. For the CLI preprocess passes this is deemed
1290cc1dc7a3Sopenharmony_ci * acceptable as these are not performance critical paths.
1291cc1dc7a3Sopenharmony_ci *
1292cc1dc7a3Sopenharmony_ci * @param[in] img   The output image.
1293cc1dc7a3Sopenharmony_ci * @param     x     The pixel x coordinate.
1294cc1dc7a3Sopenharmony_ci * @param     y     The pixel y coordinate.
1295cc1dc7a3Sopenharmony_ci * @param     z     The pixel z coordinate.
1296cc1dc7a3Sopenharmony_ci *
1297cc1dc7a3Sopenharmony_ci * @return      pixel   The pixel color value to write.
1298cc1dc7a3Sopenharmony_ci */
1299cc1dc7a3Sopenharmony_cistatic vfloat4 image_get_pixel(
1300cc1dc7a3Sopenharmony_ci	const astcenc_image& img,
1301cc1dc7a3Sopenharmony_ci	unsigned int x,
1302cc1dc7a3Sopenharmony_ci	unsigned int y,
1303cc1dc7a3Sopenharmony_ci	unsigned int z
1304cc1dc7a3Sopenharmony_ci) {
1305cc1dc7a3Sopenharmony_ci	// We should never escape bounds
1306cc1dc7a3Sopenharmony_ci	assert(x < img.dim_x);
1307cc1dc7a3Sopenharmony_ci	assert(y < img.dim_y);
1308cc1dc7a3Sopenharmony_ci	assert(z < img.dim_z);
1309cc1dc7a3Sopenharmony_ci
1310cc1dc7a3Sopenharmony_ci	if (img.data_type == ASTCENC_TYPE_U8)
1311cc1dc7a3Sopenharmony_ci	{
1312cc1dc7a3Sopenharmony_ci		uint8_t* data = static_cast<uint8_t*>(img.data[z]);
1313cc1dc7a3Sopenharmony_ci
1314cc1dc7a3Sopenharmony_ci		float r = data[(4 * img.dim_x * y) + (4 * x    )] / 255.0f;
1315cc1dc7a3Sopenharmony_ci		float g = data[(4 * img.dim_x * y) + (4 * x + 1)] / 255.0f;
1316cc1dc7a3Sopenharmony_ci		float b = data[(4 * img.dim_x * y) + (4 * x + 2)] / 255.0f;
1317cc1dc7a3Sopenharmony_ci		float a = data[(4 * img.dim_x * y) + (4 * x + 3)] / 255.0f;
1318cc1dc7a3Sopenharmony_ci
1319cc1dc7a3Sopenharmony_ci		return vfloat4(r, g, b, a);
1320cc1dc7a3Sopenharmony_ci	}
1321cc1dc7a3Sopenharmony_ci	else if (img.data_type == ASTCENC_TYPE_F16)
1322cc1dc7a3Sopenharmony_ci	{
1323cc1dc7a3Sopenharmony_ci		uint16_t* data = static_cast<uint16_t*>(img.data[z]);
1324cc1dc7a3Sopenharmony_ci
1325cc1dc7a3Sopenharmony_ci		vint4 colori(
1326cc1dc7a3Sopenharmony_ci			data[(4 * img.dim_x * y) + (4 * x    )],
1327cc1dc7a3Sopenharmony_ci			data[(4 * img.dim_x * y) + (4 * x + 1)],
1328cc1dc7a3Sopenharmony_ci			data[(4 * img.dim_x * y) + (4 * x + 2)],
1329cc1dc7a3Sopenharmony_ci			data[(4 * img.dim_x * y) + (4 * x + 3)]
1330cc1dc7a3Sopenharmony_ci		);
1331cc1dc7a3Sopenharmony_ci
1332cc1dc7a3Sopenharmony_ci		return float16_to_float(colori);
1333cc1dc7a3Sopenharmony_ci	}
1334cc1dc7a3Sopenharmony_ci	else // if (img.data_type == ASTCENC_TYPE_F32)
1335cc1dc7a3Sopenharmony_ci	{
1336cc1dc7a3Sopenharmony_ci		assert(img.data_type == ASTCENC_TYPE_F32);
1337cc1dc7a3Sopenharmony_ci		float* data = static_cast<float*>(img.data[z]);
1338cc1dc7a3Sopenharmony_ci
1339cc1dc7a3Sopenharmony_ci		return vfloat4(
1340cc1dc7a3Sopenharmony_ci			data[(4 * img.dim_x * y) + (4 * x    )],
1341cc1dc7a3Sopenharmony_ci			data[(4 * img.dim_x * y) + (4 * x + 1)],
1342cc1dc7a3Sopenharmony_ci			data[(4 * img.dim_x * y) + (4 * x + 2)],
1343cc1dc7a3Sopenharmony_ci			data[(4 * img.dim_x * y) + (4 * x + 3)]
1344cc1dc7a3Sopenharmony_ci		);
1345cc1dc7a3Sopenharmony_ci	}
1346cc1dc7a3Sopenharmony_ci}
1347cc1dc7a3Sopenharmony_ci
1348cc1dc7a3Sopenharmony_ci/**
1349cc1dc7a3Sopenharmony_ci * @brief Set the value of a single pixel in an image.
1350cc1dc7a3Sopenharmony_ci *
1351cc1dc7a3Sopenharmony_ci * @param[out] img     The output image; must use F32 texture components.
1352cc1dc7a3Sopenharmony_ci * @param      x       The pixel x coordinate.
1353cc1dc7a3Sopenharmony_ci * @param      y       The pixel y coordinate.
1354cc1dc7a3Sopenharmony_ci * @param      z       The pixel z coordinate.
1355cc1dc7a3Sopenharmony_ci * @param      pixel   The pixel color value to write.
1356cc1dc7a3Sopenharmony_ci */
1357cc1dc7a3Sopenharmony_cistatic void image_set_pixel(
1358cc1dc7a3Sopenharmony_ci	astcenc_image& img,
1359cc1dc7a3Sopenharmony_ci	unsigned int x,
1360cc1dc7a3Sopenharmony_ci	unsigned int y,
1361cc1dc7a3Sopenharmony_ci	unsigned int z,
1362cc1dc7a3Sopenharmony_ci	vfloat4 pixel
1363cc1dc7a3Sopenharmony_ci) {
1364cc1dc7a3Sopenharmony_ci	// We should never escape bounds
1365cc1dc7a3Sopenharmony_ci	assert(x < img.dim_x);
1366cc1dc7a3Sopenharmony_ci	assert(y < img.dim_y);
1367cc1dc7a3Sopenharmony_ci	assert(z < img.dim_z);
1368cc1dc7a3Sopenharmony_ci	assert(img.data_type == ASTCENC_TYPE_F32);
1369cc1dc7a3Sopenharmony_ci
1370cc1dc7a3Sopenharmony_ci	float* data = static_cast<float*>(img.data[z]);
1371cc1dc7a3Sopenharmony_ci
1372cc1dc7a3Sopenharmony_ci	data[(4 * img.dim_x * y) + (4 * x    )] = pixel.lane<0>();
1373cc1dc7a3Sopenharmony_ci	data[(4 * img.dim_x * y) + (4 * x + 1)] = pixel.lane<1>();
1374cc1dc7a3Sopenharmony_ci	data[(4 * img.dim_x * y) + (4 * x + 2)] = pixel.lane<2>();
1375cc1dc7a3Sopenharmony_ci	data[(4 * img.dim_x * y) + (4 * x + 3)] = pixel.lane<3>();
1376cc1dc7a3Sopenharmony_ci}
1377cc1dc7a3Sopenharmony_ci
1378cc1dc7a3Sopenharmony_ci/**
1379cc1dc7a3Sopenharmony_ci * @brief Set the value of a single pixel in an image.
1380cc1dc7a3Sopenharmony_ci *
1381cc1dc7a3Sopenharmony_ci * @param[out] img     The output image; must use F32 texture components.
1382cc1dc7a3Sopenharmony_ci * @param      x       The pixel x coordinate.
1383cc1dc7a3Sopenharmony_ci * @param      y       The pixel y coordinate.
1384cc1dc7a3Sopenharmony_ci * @param      pixel   The pixel color value to write.
1385cc1dc7a3Sopenharmony_ci */
1386cc1dc7a3Sopenharmony_cistatic void image_set_pixel_u8(
1387cc1dc7a3Sopenharmony_ci	astcenc_image& img,
1388cc1dc7a3Sopenharmony_ci	size_t x,
1389cc1dc7a3Sopenharmony_ci	size_t y,
1390cc1dc7a3Sopenharmony_ci	vint4 pixel
1391cc1dc7a3Sopenharmony_ci) {
1392cc1dc7a3Sopenharmony_ci	// We should never escape bounds
1393cc1dc7a3Sopenharmony_ci	assert(x < img.dim_x);
1394cc1dc7a3Sopenharmony_ci	assert(y < img.dim_y);
1395cc1dc7a3Sopenharmony_ci	assert(img.data_type == ASTCENC_TYPE_U8);
1396cc1dc7a3Sopenharmony_ci
1397cc1dc7a3Sopenharmony_ci	uint8_t* data = static_cast<uint8_t*>(img.data[0]);
1398cc1dc7a3Sopenharmony_ci	pixel = pack_low_bytes(pixel);
1399cc1dc7a3Sopenharmony_ci	store_nbytes(pixel, data + (4 * img.dim_x * y) + (4 * x    ));
1400cc1dc7a3Sopenharmony_ci}
1401cc1dc7a3Sopenharmony_ci
1402cc1dc7a3Sopenharmony_ci/**
1403cc1dc7a3Sopenharmony_ci * @brief Create a copy of @c input with forced unit-length normal vectors.
1404cc1dc7a3Sopenharmony_ci *
1405cc1dc7a3Sopenharmony_ci * It is assumed that all normal vectors are stored in the RGB components, and
1406cc1dc7a3Sopenharmony_ci * stored in a packed unsigned range of [0,1] which must be unpacked prior
1407cc1dc7a3Sopenharmony_ci * normalization. Data must then be repacked into this form for handing over to
1408cc1dc7a3Sopenharmony_ci * the core codec.
1409cc1dc7a3Sopenharmony_ci *
1410cc1dc7a3Sopenharmony_ci * @param[in]  input    The input image.
1411cc1dc7a3Sopenharmony_ci * @param[out] output   The output image, must use F32 components.
1412cc1dc7a3Sopenharmony_ci */
1413cc1dc7a3Sopenharmony_cistatic void image_preprocess_normalize(
1414cc1dc7a3Sopenharmony_ci	const astcenc_image& input,
1415cc1dc7a3Sopenharmony_ci	astcenc_image& output
1416cc1dc7a3Sopenharmony_ci) {
1417cc1dc7a3Sopenharmony_ci	for (unsigned int z = 0; z < input.dim_z; z++)
1418cc1dc7a3Sopenharmony_ci	{
1419cc1dc7a3Sopenharmony_ci		for (unsigned int y = 0; y < input.dim_y; y++)
1420cc1dc7a3Sopenharmony_ci		{
1421cc1dc7a3Sopenharmony_ci			for (unsigned int x = 0; x < input.dim_x; x++)
1422cc1dc7a3Sopenharmony_ci			{
1423cc1dc7a3Sopenharmony_ci				vfloat4 pixel = image_get_pixel(input, x, y, z);
1424cc1dc7a3Sopenharmony_ci
1425cc1dc7a3Sopenharmony_ci				// Stash alpha component and zero
1426cc1dc7a3Sopenharmony_ci				float a = pixel.lane<3>();
1427cc1dc7a3Sopenharmony_ci				pixel.set_lane<3>(0.0f);
1428cc1dc7a3Sopenharmony_ci
1429cc1dc7a3Sopenharmony_ci				// Decode [0,1] normals to [-1,1]
1430cc1dc7a3Sopenharmony_ci				pixel.set_lane<0>((pixel.lane<0>() * 2.0f) - 1.0f);
1431cc1dc7a3Sopenharmony_ci				pixel.set_lane<1>((pixel.lane<1>() * 2.0f) - 1.0f);
1432cc1dc7a3Sopenharmony_ci				pixel.set_lane<2>((pixel.lane<2>() * 2.0f) - 1.0f);
1433cc1dc7a3Sopenharmony_ci
1434cc1dc7a3Sopenharmony_ci				// Normalize pixel and restore alpha
1435cc1dc7a3Sopenharmony_ci				pixel = normalize(pixel);
1436cc1dc7a3Sopenharmony_ci				pixel.set_lane<3>(a);
1437cc1dc7a3Sopenharmony_ci
1438cc1dc7a3Sopenharmony_ci				// Encode [-1,1] normals to [0,1]
1439cc1dc7a3Sopenharmony_ci				pixel.set_lane<0>((pixel.lane<0>() + 1.0f) / 2.0f);
1440cc1dc7a3Sopenharmony_ci				pixel.set_lane<1>((pixel.lane<1>() + 1.0f) / 2.0f);
1441cc1dc7a3Sopenharmony_ci				pixel.set_lane<2>((pixel.lane<2>() + 1.0f) / 2.0f);
1442cc1dc7a3Sopenharmony_ci
1443cc1dc7a3Sopenharmony_ci				image_set_pixel(output, x, y, z, pixel);
1444cc1dc7a3Sopenharmony_ci			}
1445cc1dc7a3Sopenharmony_ci		}
1446cc1dc7a3Sopenharmony_ci	}
1447cc1dc7a3Sopenharmony_ci}
1448cc1dc7a3Sopenharmony_ci
1449cc1dc7a3Sopenharmony_ci/**
1450cc1dc7a3Sopenharmony_ci * @brief Linearize an sRGB value.
1451cc1dc7a3Sopenharmony_ci *
1452cc1dc7a3Sopenharmony_ci * @return The linearized value.
1453cc1dc7a3Sopenharmony_ci */
1454cc1dc7a3Sopenharmony_cistatic float srgb_to_linear(
1455cc1dc7a3Sopenharmony_ci	float a
1456cc1dc7a3Sopenharmony_ci) {
1457cc1dc7a3Sopenharmony_ci	if (a <= 0.04045f)
1458cc1dc7a3Sopenharmony_ci	{
1459cc1dc7a3Sopenharmony_ci		return a * (1.0f / 12.92f);
1460cc1dc7a3Sopenharmony_ci	}
1461cc1dc7a3Sopenharmony_ci
1462cc1dc7a3Sopenharmony_ci	return powf((a + 0.055f) * (1.0f / 1.055f), 2.4f);
1463cc1dc7a3Sopenharmony_ci}
1464cc1dc7a3Sopenharmony_ci
1465cc1dc7a3Sopenharmony_ci/**
1466cc1dc7a3Sopenharmony_ci * @brief sRGB gamma-encode a linear value.
1467cc1dc7a3Sopenharmony_ci *
1468cc1dc7a3Sopenharmony_ci * @return The gamma encoded value.
1469cc1dc7a3Sopenharmony_ci */
1470cc1dc7a3Sopenharmony_cistatic float linear_to_srgb(
1471cc1dc7a3Sopenharmony_ci	float a
1472cc1dc7a3Sopenharmony_ci) {
1473cc1dc7a3Sopenharmony_ci	if (a <= 0.0031308f)
1474cc1dc7a3Sopenharmony_ci	{
1475cc1dc7a3Sopenharmony_ci		return a * 12.92f;
1476cc1dc7a3Sopenharmony_ci	}
1477cc1dc7a3Sopenharmony_ci
1478cc1dc7a3Sopenharmony_ci	return 1.055f * powf(a, 1.0f / 2.4f) - 0.055f;
1479cc1dc7a3Sopenharmony_ci}
1480cc1dc7a3Sopenharmony_ci
1481cc1dc7a3Sopenharmony_ci/**
1482cc1dc7a3Sopenharmony_ci * @brief Create a copy of @c input with premultiplied color data.
1483cc1dc7a3Sopenharmony_ci *
1484cc1dc7a3Sopenharmony_ci * If we are compressing sRGB data we linearize the data prior to
1485cc1dc7a3Sopenharmony_ci * premultiplication and re-gamma-encode afterwards.
1486cc1dc7a3Sopenharmony_ci *
1487cc1dc7a3Sopenharmony_ci * @param[in]  input     The input image.
1488cc1dc7a3Sopenharmony_ci * @param[out] output    The output image, must use F32 components.
1489cc1dc7a3Sopenharmony_ci * @param      profile   The encoding profile.
1490cc1dc7a3Sopenharmony_ci */
1491cc1dc7a3Sopenharmony_cistatic void image_preprocess_premultiply(
1492cc1dc7a3Sopenharmony_ci	const astcenc_image& input,
1493cc1dc7a3Sopenharmony_ci	astcenc_image& output,
1494cc1dc7a3Sopenharmony_ci	astcenc_profile profile
1495cc1dc7a3Sopenharmony_ci) {
1496cc1dc7a3Sopenharmony_ci	for (unsigned int z = 0; z < input.dim_z; z++)
1497cc1dc7a3Sopenharmony_ci	{
1498cc1dc7a3Sopenharmony_ci		for (unsigned int y = 0; y < input.dim_y; y++)
1499cc1dc7a3Sopenharmony_ci		{
1500cc1dc7a3Sopenharmony_ci			for (unsigned int x = 0; x < input.dim_x; x++)
1501cc1dc7a3Sopenharmony_ci			{
1502cc1dc7a3Sopenharmony_ci				vfloat4 pixel = image_get_pixel(input, x, y, z);
1503cc1dc7a3Sopenharmony_ci
1504cc1dc7a3Sopenharmony_ci				// Linearize sRGB
1505cc1dc7a3Sopenharmony_ci				if (profile == ASTCENC_PRF_LDR_SRGB)
1506cc1dc7a3Sopenharmony_ci				{
1507cc1dc7a3Sopenharmony_ci					pixel.set_lane<0>(srgb_to_linear(pixel.lane<0>()));
1508cc1dc7a3Sopenharmony_ci					pixel.set_lane<1>(srgb_to_linear(pixel.lane<1>()));
1509cc1dc7a3Sopenharmony_ci					pixel.set_lane<2>(srgb_to_linear(pixel.lane<2>()));
1510cc1dc7a3Sopenharmony_ci				}
1511cc1dc7a3Sopenharmony_ci
1512cc1dc7a3Sopenharmony_ci				// Premultiply pixel in linear-space
1513cc1dc7a3Sopenharmony_ci				pixel.set_lane<0>(pixel.lane<0>() * pixel.lane<3>());
1514cc1dc7a3Sopenharmony_ci				pixel.set_lane<1>(pixel.lane<1>() * pixel.lane<3>());
1515cc1dc7a3Sopenharmony_ci				pixel.set_lane<2>(pixel.lane<2>() * pixel.lane<3>());
1516cc1dc7a3Sopenharmony_ci
1517cc1dc7a3Sopenharmony_ci				// Gamma-encode sRGB
1518cc1dc7a3Sopenharmony_ci				if (profile == ASTCENC_PRF_LDR_SRGB)
1519cc1dc7a3Sopenharmony_ci				{
1520cc1dc7a3Sopenharmony_ci					pixel.set_lane<0>(linear_to_srgb(pixel.lane<0>()));
1521cc1dc7a3Sopenharmony_ci					pixel.set_lane<1>(linear_to_srgb(pixel.lane<1>()));
1522cc1dc7a3Sopenharmony_ci					pixel.set_lane<2>(linear_to_srgb(pixel.lane<2>()));
1523cc1dc7a3Sopenharmony_ci				}
1524cc1dc7a3Sopenharmony_ci
1525cc1dc7a3Sopenharmony_ci				image_set_pixel(output, x, y, z, pixel);
1526cc1dc7a3Sopenharmony_ci			}
1527cc1dc7a3Sopenharmony_ci		}
1528cc1dc7a3Sopenharmony_ci	}
1529cc1dc7a3Sopenharmony_ci}
1530cc1dc7a3Sopenharmony_ci
1531cc1dc7a3Sopenharmony_ci/**
1532cc1dc7a3Sopenharmony_ci * @brief Populate a single diagnostic image showing aspects of the encoding.
1533cc1dc7a3Sopenharmony_ci *
1534cc1dc7a3Sopenharmony_ci * @param context      The context to use.
1535cc1dc7a3Sopenharmony_ci * @param image        The compressed image to analyze.
1536cc1dc7a3Sopenharmony_ci * @param diag_image   The output visualization image to populate.
1537cc1dc7a3Sopenharmony_ci * @param texel_func   The per-texel callback used to determine output color.
1538cc1dc7a3Sopenharmony_ci */
1539cc1dc7a3Sopenharmony_cistatic void print_diagnostic_image(
1540cc1dc7a3Sopenharmony_ci	astcenc_context* context,
1541cc1dc7a3Sopenharmony_ci	const astc_compressed_image& image,
1542cc1dc7a3Sopenharmony_ci	astcenc_image& diag_image,
1543cc1dc7a3Sopenharmony_ci	std::function<vint4(astcenc_block_info&, size_t, size_t)> texel_func
1544cc1dc7a3Sopenharmony_ci) {
1545cc1dc7a3Sopenharmony_ci	size_t block_cols = (image.dim_x + image.block_x - 1) / image.block_x;
1546cc1dc7a3Sopenharmony_ci	size_t block_rows = (image.dim_y + image.block_y - 1) / image.block_y;
1547cc1dc7a3Sopenharmony_ci
1548cc1dc7a3Sopenharmony_ci	uint8_t* data = image.data;
1549cc1dc7a3Sopenharmony_ci	for (size_t block_y = 0; block_y < block_rows; block_y++)
1550cc1dc7a3Sopenharmony_ci	{
1551cc1dc7a3Sopenharmony_ci		for (size_t block_x = 0; block_x < block_cols; block_x++)
1552cc1dc7a3Sopenharmony_ci		{
1553cc1dc7a3Sopenharmony_ci			astcenc_block_info block_info;
1554cc1dc7a3Sopenharmony_ci			astcenc_get_block_info(context, data, &block_info);
1555cc1dc7a3Sopenharmony_ci			data += 16;
1556cc1dc7a3Sopenharmony_ci
1557cc1dc7a3Sopenharmony_ci			size_t start_row = block_y * image.block_y;
1558cc1dc7a3Sopenharmony_ci			size_t start_col = block_x * image.block_x;
1559cc1dc7a3Sopenharmony_ci
1560cc1dc7a3Sopenharmony_ci			size_t end_row = astc::min(start_row + image.block_y, static_cast<size_t>(image.dim_y));
1561cc1dc7a3Sopenharmony_ci			size_t end_col = astc::min(start_col + image.block_x, static_cast<size_t>(image.dim_x));
1562cc1dc7a3Sopenharmony_ci
1563cc1dc7a3Sopenharmony_ci			for (size_t texel_y = start_row; texel_y < end_row; texel_y++)
1564cc1dc7a3Sopenharmony_ci			{
1565cc1dc7a3Sopenharmony_ci				for (size_t texel_x = start_col; texel_x < end_col; texel_x++)
1566cc1dc7a3Sopenharmony_ci				{
1567cc1dc7a3Sopenharmony_ci					vint4 color = texel_func(block_info, texel_x - start_col, texel_y - start_row);
1568cc1dc7a3Sopenharmony_ci					image_set_pixel_u8(diag_image, texel_x, texel_y, color);
1569cc1dc7a3Sopenharmony_ci				}
1570cc1dc7a3Sopenharmony_ci			}
1571cc1dc7a3Sopenharmony_ci		}
1572cc1dc7a3Sopenharmony_ci	}
1573cc1dc7a3Sopenharmony_ci}
1574cc1dc7a3Sopenharmony_ci
1575cc1dc7a3Sopenharmony_ci/**
1576cc1dc7a3Sopenharmony_ci * @brief Print a set of diagnostic images showing aspects of the encoding.
1577cc1dc7a3Sopenharmony_ci *
1578cc1dc7a3Sopenharmony_ci * @param context       The context to use.
1579cc1dc7a3Sopenharmony_ci * @param image         The compressed image to analyze.
1580cc1dc7a3Sopenharmony_ci * @param output_file   The output file name to use as a stem for new names.
1581cc1dc7a3Sopenharmony_ci */
1582cc1dc7a3Sopenharmony_cistatic void print_diagnostic_images(
1583cc1dc7a3Sopenharmony_ci	astcenc_context* context,
1584cc1dc7a3Sopenharmony_ci	const astc_compressed_image& image,
1585cc1dc7a3Sopenharmony_ci	const std::string& output_file
1586cc1dc7a3Sopenharmony_ci) {
1587cc1dc7a3Sopenharmony_ci	if (image.dim_z != 1)
1588cc1dc7a3Sopenharmony_ci	{
1589cc1dc7a3Sopenharmony_ci		return;
1590cc1dc7a3Sopenharmony_ci	}
1591cc1dc7a3Sopenharmony_ci
1592cc1dc7a3Sopenharmony_ci	// Try to find a file extension we know about
1593cc1dc7a3Sopenharmony_ci	size_t index = output_file.find_last_of(".");
1594cc1dc7a3Sopenharmony_ci	std::string stem = output_file;
1595cc1dc7a3Sopenharmony_ci	if (index != std::string::npos)
1596cc1dc7a3Sopenharmony_ci	{
1597cc1dc7a3Sopenharmony_ci		stem = stem.substr(0, index);
1598cc1dc7a3Sopenharmony_ci	}
1599cc1dc7a3Sopenharmony_ci
1600cc1dc7a3Sopenharmony_ci	auto diag_image = alloc_image(8, image.dim_x, image.dim_y, image.dim_z);
1601cc1dc7a3Sopenharmony_ci
1602cc1dc7a3Sopenharmony_ci	// ---- ---- ---- ---- Partitioning ---- ---- ---- ----
1603cc1dc7a3Sopenharmony_ci	auto partition_func = [](astcenc_block_info& info, size_t texel_x, size_t texel_y) {
1604cc1dc7a3Sopenharmony_ci		const vint4 colors[] {
1605cc1dc7a3Sopenharmony_ci			vint4(  0,   0,   0, 255),
1606cc1dc7a3Sopenharmony_ci			vint4(255,   0,   0, 255),
1607cc1dc7a3Sopenharmony_ci			vint4(  0, 255,   0, 255),
1608cc1dc7a3Sopenharmony_ci			vint4(  0,   0, 255, 255),
1609cc1dc7a3Sopenharmony_ci			vint4(255, 255, 255, 255)
1610cc1dc7a3Sopenharmony_ci		};
1611cc1dc7a3Sopenharmony_ci
1612cc1dc7a3Sopenharmony_ci		size_t texel_index = texel_y * info.block_x + texel_x;
1613cc1dc7a3Sopenharmony_ci
1614cc1dc7a3Sopenharmony_ci		int partition { 0 };
1615cc1dc7a3Sopenharmony_ci		if (!info.is_constant_block)
1616cc1dc7a3Sopenharmony_ci		{
1617cc1dc7a3Sopenharmony_ci			partition = info.partition_assignment[texel_index] + 1;
1618cc1dc7a3Sopenharmony_ci		}
1619cc1dc7a3Sopenharmony_ci
1620cc1dc7a3Sopenharmony_ci		return colors[partition];
1621cc1dc7a3Sopenharmony_ci	};
1622cc1dc7a3Sopenharmony_ci
1623cc1dc7a3Sopenharmony_ci	print_diagnostic_image(context, image, *diag_image, partition_func);
1624cc1dc7a3Sopenharmony_ci	std::string fname = stem + "_diag_partitioning.png";
1625cc1dc7a3Sopenharmony_ci	store_ncimage(diag_image, fname.c_str(), false);
1626cc1dc7a3Sopenharmony_ci
1627cc1dc7a3Sopenharmony_ci	// ---- ---- ---- ---- Weight planes  ---- ---- ---- ----
1628cc1dc7a3Sopenharmony_ci	auto texel_func1 = [](astcenc_block_info& info, size_t texel_x, size_t texel_y) {
1629cc1dc7a3Sopenharmony_ci		(void)texel_x;
1630cc1dc7a3Sopenharmony_ci		(void)texel_y;
1631cc1dc7a3Sopenharmony_ci
1632cc1dc7a3Sopenharmony_ci		const vint4 colors[] {
1633cc1dc7a3Sopenharmony_ci			vint4(  0,   0,   0, 255),
1634cc1dc7a3Sopenharmony_ci			vint4(255,   0,   0, 255),
1635cc1dc7a3Sopenharmony_ci			vint4(  0, 255,   0, 255),
1636cc1dc7a3Sopenharmony_ci			vint4(  0,   0, 255, 255),
1637cc1dc7a3Sopenharmony_ci			vint4(255, 255, 255, 255)
1638cc1dc7a3Sopenharmony_ci		};
1639cc1dc7a3Sopenharmony_ci
1640cc1dc7a3Sopenharmony_ci		int component { 0 };
1641cc1dc7a3Sopenharmony_ci		if (info.is_dual_plane_block)
1642cc1dc7a3Sopenharmony_ci		{
1643cc1dc7a3Sopenharmony_ci			component = info.dual_plane_component + 1;
1644cc1dc7a3Sopenharmony_ci		}
1645cc1dc7a3Sopenharmony_ci
1646cc1dc7a3Sopenharmony_ci		return colors[component];
1647cc1dc7a3Sopenharmony_ci	};
1648cc1dc7a3Sopenharmony_ci
1649cc1dc7a3Sopenharmony_ci	print_diagnostic_image(context, image, *diag_image, texel_func1);
1650cc1dc7a3Sopenharmony_ci	fname = stem + "_diag_weight_plane2.png";
1651cc1dc7a3Sopenharmony_ci	store_ncimage(diag_image, fname.c_str(), false);
1652cc1dc7a3Sopenharmony_ci
1653cc1dc7a3Sopenharmony_ci	// ---- ---- ---- ---- Weight density  ---- ---- ---- ----
1654cc1dc7a3Sopenharmony_ci	auto texel_func2 = [](astcenc_block_info& info, size_t texel_x, size_t texel_y) {
1655cc1dc7a3Sopenharmony_ci		(void)texel_x;
1656cc1dc7a3Sopenharmony_ci		(void)texel_y;
1657cc1dc7a3Sopenharmony_ci
1658cc1dc7a3Sopenharmony_ci		float density = 0.0f;
1659cc1dc7a3Sopenharmony_ci		if (!info.is_constant_block)
1660cc1dc7a3Sopenharmony_ci		{
1661cc1dc7a3Sopenharmony_ci			float texel_count = static_cast<float>(info.block_x * info.block_y);
1662cc1dc7a3Sopenharmony_ci			float weight_count = static_cast<float>(info.weight_x * info.weight_y);
1663cc1dc7a3Sopenharmony_ci			density = weight_count / texel_count;
1664cc1dc7a3Sopenharmony_ci		}
1665cc1dc7a3Sopenharmony_ci
1666cc1dc7a3Sopenharmony_ci		int densityi = static_cast<int>(255.0f * density);
1667cc1dc7a3Sopenharmony_ci		return vint4(densityi, densityi, densityi, 255);
1668cc1dc7a3Sopenharmony_ci	};
1669cc1dc7a3Sopenharmony_ci
1670cc1dc7a3Sopenharmony_ci	print_diagnostic_image(context, image, *diag_image, texel_func2);
1671cc1dc7a3Sopenharmony_ci	fname = stem + "_diag_weight_density.png";
1672cc1dc7a3Sopenharmony_ci	store_ncimage(diag_image, fname.c_str(), false);
1673cc1dc7a3Sopenharmony_ci
1674cc1dc7a3Sopenharmony_ci	// ---- ---- ---- ---- Weight quant  ---- ---- ---- ----
1675cc1dc7a3Sopenharmony_ci	auto texel_func3 = [](astcenc_block_info& info, size_t texel_x, size_t texel_y) {
1676cc1dc7a3Sopenharmony_ci		(void)texel_x;
1677cc1dc7a3Sopenharmony_ci		(void)texel_y;
1678cc1dc7a3Sopenharmony_ci
1679cc1dc7a3Sopenharmony_ci		int quant { 0 };
1680cc1dc7a3Sopenharmony_ci		if (!info.is_constant_block)
1681cc1dc7a3Sopenharmony_ci		{
1682cc1dc7a3Sopenharmony_ci			quant = info.weight_level_count - 1;
1683cc1dc7a3Sopenharmony_ci		}
1684cc1dc7a3Sopenharmony_ci
1685cc1dc7a3Sopenharmony_ci		return vint4(quant, quant, quant, 255);
1686cc1dc7a3Sopenharmony_ci	};
1687cc1dc7a3Sopenharmony_ci
1688cc1dc7a3Sopenharmony_ci	print_diagnostic_image(context, image, *diag_image, texel_func3);
1689cc1dc7a3Sopenharmony_ci	fname = stem + "_diag_weight_quant.png";
1690cc1dc7a3Sopenharmony_ci	store_ncimage(diag_image, fname.c_str(), false);
1691cc1dc7a3Sopenharmony_ci
1692cc1dc7a3Sopenharmony_ci	// ---- ---- ---- ---- Color quant  ---- ---- ---- ----
1693cc1dc7a3Sopenharmony_ci	auto texel_func4 = [](astcenc_block_info& info, size_t texel_x, size_t texel_y) {
1694cc1dc7a3Sopenharmony_ci		(void)texel_x;
1695cc1dc7a3Sopenharmony_ci		(void)texel_y;
1696cc1dc7a3Sopenharmony_ci
1697cc1dc7a3Sopenharmony_ci		int quant { 0 };
1698cc1dc7a3Sopenharmony_ci		if (!info.is_constant_block)
1699cc1dc7a3Sopenharmony_ci		{
1700cc1dc7a3Sopenharmony_ci			quant = info.color_level_count - 1;
1701cc1dc7a3Sopenharmony_ci		}
1702cc1dc7a3Sopenharmony_ci
1703cc1dc7a3Sopenharmony_ci		return vint4(quant, quant, quant, 255);
1704cc1dc7a3Sopenharmony_ci	};
1705cc1dc7a3Sopenharmony_ci
1706cc1dc7a3Sopenharmony_ci	print_diagnostic_image(context, image, *diag_image, texel_func4);
1707cc1dc7a3Sopenharmony_ci	fname = stem + "_diag_color_quant.png";
1708cc1dc7a3Sopenharmony_ci	store_ncimage(diag_image, fname.c_str(), false);
1709cc1dc7a3Sopenharmony_ci
1710cc1dc7a3Sopenharmony_ci	// ---- ---- ---- ---- Color endpoint mode: Index ---- ---- ---- ----
1711cc1dc7a3Sopenharmony_ci	auto texel_func5 = [](astcenc_block_info& info, size_t texel_x, size_t texel_y) {
1712cc1dc7a3Sopenharmony_ci		(void)texel_x;
1713cc1dc7a3Sopenharmony_ci		(void)texel_y;
1714cc1dc7a3Sopenharmony_ci
1715cc1dc7a3Sopenharmony_ci		size_t texel_index = texel_y * info.block_x + texel_x;
1716cc1dc7a3Sopenharmony_ci
1717cc1dc7a3Sopenharmony_ci		int cem { 255 };
1718cc1dc7a3Sopenharmony_ci		if (!info.is_constant_block)
1719cc1dc7a3Sopenharmony_ci		{
1720cc1dc7a3Sopenharmony_ci			uint8_t partition = info.partition_assignment[texel_index];
1721cc1dc7a3Sopenharmony_ci			cem = info.color_endpoint_modes[partition] * 16;
1722cc1dc7a3Sopenharmony_ci		}
1723cc1dc7a3Sopenharmony_ci
1724cc1dc7a3Sopenharmony_ci		return vint4(cem, cem, cem, 255);
1725cc1dc7a3Sopenharmony_ci	};
1726cc1dc7a3Sopenharmony_ci
1727cc1dc7a3Sopenharmony_ci	print_diagnostic_image(context, image, *diag_image, texel_func5);
1728cc1dc7a3Sopenharmony_ci	fname = stem + "_diag_cem_index.png";
1729cc1dc7a3Sopenharmony_ci	store_ncimage(diag_image, fname.c_str(), false);
1730cc1dc7a3Sopenharmony_ci
1731cc1dc7a3Sopenharmony_ci	// ---- ---- ---- ---- Color endpoint mode: Components ---- ---- ---- ----
1732cc1dc7a3Sopenharmony_ci	auto texel_func6 = [](astcenc_block_info& info, size_t texel_x, size_t texel_y) {
1733cc1dc7a3Sopenharmony_ci		(void)texel_x;
1734cc1dc7a3Sopenharmony_ci		(void)texel_y;
1735cc1dc7a3Sopenharmony_ci
1736cc1dc7a3Sopenharmony_ci		const vint4 colors[] {
1737cc1dc7a3Sopenharmony_ci			vint4(  0,   0,   0, 255),
1738cc1dc7a3Sopenharmony_ci			vint4(255,   0,   0, 255),
1739cc1dc7a3Sopenharmony_ci			vint4(  0, 255,   0, 255),
1740cc1dc7a3Sopenharmony_ci			vint4(  0,   0, 255, 255),
1741cc1dc7a3Sopenharmony_ci			vint4(255, 255, 255, 255)
1742cc1dc7a3Sopenharmony_ci		};
1743cc1dc7a3Sopenharmony_ci
1744cc1dc7a3Sopenharmony_ci		size_t texel_index = texel_y * info.block_x + texel_x;
1745cc1dc7a3Sopenharmony_ci
1746cc1dc7a3Sopenharmony_ci		int components { 0 };
1747cc1dc7a3Sopenharmony_ci		if (!info.is_constant_block)
1748cc1dc7a3Sopenharmony_ci		{
1749cc1dc7a3Sopenharmony_ci			uint8_t partition = info.partition_assignment[texel_index];
1750cc1dc7a3Sopenharmony_ci			uint8_t cem = info.color_endpoint_modes[partition];
1751cc1dc7a3Sopenharmony_ci
1752cc1dc7a3Sopenharmony_ci			switch (cem)
1753cc1dc7a3Sopenharmony_ci			{
1754cc1dc7a3Sopenharmony_ci				case 0:
1755cc1dc7a3Sopenharmony_ci				case 1:
1756cc1dc7a3Sopenharmony_ci				case 2:
1757cc1dc7a3Sopenharmony_ci				case 3:
1758cc1dc7a3Sopenharmony_ci					components = 1;
1759cc1dc7a3Sopenharmony_ci					break;
1760cc1dc7a3Sopenharmony_ci				case 4:
1761cc1dc7a3Sopenharmony_ci				case 5:
1762cc1dc7a3Sopenharmony_ci					components = 2;
1763cc1dc7a3Sopenharmony_ci					break;
1764cc1dc7a3Sopenharmony_ci				case 6:
1765cc1dc7a3Sopenharmony_ci				case 7:
1766cc1dc7a3Sopenharmony_ci				case 8:
1767cc1dc7a3Sopenharmony_ci				case 9:
1768cc1dc7a3Sopenharmony_ci				case 11:
1769cc1dc7a3Sopenharmony_ci					components = 3;
1770cc1dc7a3Sopenharmony_ci					break;
1771cc1dc7a3Sopenharmony_ci				default:
1772cc1dc7a3Sopenharmony_ci					components = 4;
1773cc1dc7a3Sopenharmony_ci					break;
1774cc1dc7a3Sopenharmony_ci			}
1775cc1dc7a3Sopenharmony_ci		}
1776cc1dc7a3Sopenharmony_ci
1777cc1dc7a3Sopenharmony_ci		return colors[components];
1778cc1dc7a3Sopenharmony_ci	};
1779cc1dc7a3Sopenharmony_ci
1780cc1dc7a3Sopenharmony_ci	print_diagnostic_image(context, image, *diag_image, texel_func6);
1781cc1dc7a3Sopenharmony_ci	fname = stem + "_diag_cem_components.png";
1782cc1dc7a3Sopenharmony_ci	store_ncimage(diag_image, fname.c_str(), false);
1783cc1dc7a3Sopenharmony_ci
1784cc1dc7a3Sopenharmony_ci	// ---- ---- ---- ---- Color endpoint mode: Style ---- ---- ---- ----
1785cc1dc7a3Sopenharmony_ci	auto texel_func7 = [](astcenc_block_info& info, size_t texel_x, size_t texel_y) {
1786cc1dc7a3Sopenharmony_ci		(void)texel_x;
1787cc1dc7a3Sopenharmony_ci		(void)texel_y;
1788cc1dc7a3Sopenharmony_ci
1789cc1dc7a3Sopenharmony_ci		const vint4 colors[] {
1790cc1dc7a3Sopenharmony_ci			vint4(  0,   0,   0, 255),
1791cc1dc7a3Sopenharmony_ci			vint4(255,   0,   0, 255),
1792cc1dc7a3Sopenharmony_ci			vint4(  0, 255,   0, 255),
1793cc1dc7a3Sopenharmony_ci			vint4(  0,   0, 255, 255),
1794cc1dc7a3Sopenharmony_ci		};
1795cc1dc7a3Sopenharmony_ci
1796cc1dc7a3Sopenharmony_ci		size_t texel_index = texel_y * info.block_x + texel_x;
1797cc1dc7a3Sopenharmony_ci
1798cc1dc7a3Sopenharmony_ci		int style { 0 };
1799cc1dc7a3Sopenharmony_ci		if (!info.is_constant_block)
1800cc1dc7a3Sopenharmony_ci		{
1801cc1dc7a3Sopenharmony_ci			uint8_t partition = info.partition_assignment[texel_index];
1802cc1dc7a3Sopenharmony_ci			uint8_t cem = info.color_endpoint_modes[partition];
1803cc1dc7a3Sopenharmony_ci
1804cc1dc7a3Sopenharmony_ci			switch (cem)
1805cc1dc7a3Sopenharmony_ci			{
1806cc1dc7a3Sopenharmony_ci				// Direct - two absolute endpoints
1807cc1dc7a3Sopenharmony_ci				case 0:
1808cc1dc7a3Sopenharmony_ci				case 1:
1809cc1dc7a3Sopenharmony_ci				case 2:
1810cc1dc7a3Sopenharmony_ci				case 3:
1811cc1dc7a3Sopenharmony_ci				case 4:
1812cc1dc7a3Sopenharmony_ci				case 8:
1813cc1dc7a3Sopenharmony_ci				case 11:
1814cc1dc7a3Sopenharmony_ci				case 12:
1815cc1dc7a3Sopenharmony_ci				case 14:
1816cc1dc7a3Sopenharmony_ci				case 15:
1817cc1dc7a3Sopenharmony_ci					style = 1;
1818cc1dc7a3Sopenharmony_ci					break;
1819cc1dc7a3Sopenharmony_ci				// Offset - one absolute plus delta
1820cc1dc7a3Sopenharmony_ci				case 5:
1821cc1dc7a3Sopenharmony_ci				case 9:
1822cc1dc7a3Sopenharmony_ci				case 13:
1823cc1dc7a3Sopenharmony_ci					style = 2;
1824cc1dc7a3Sopenharmony_ci					break;
1825cc1dc7a3Sopenharmony_ci				// Scale - one absolute plus scale
1826cc1dc7a3Sopenharmony_ci				case 6:
1827cc1dc7a3Sopenharmony_ci				case 7:
1828cc1dc7a3Sopenharmony_ci				case 10:
1829cc1dc7a3Sopenharmony_ci					style = 3;
1830cc1dc7a3Sopenharmony_ci					break;
1831cc1dc7a3Sopenharmony_ci				// Shouldn't happen ...
1832cc1dc7a3Sopenharmony_ci				default:
1833cc1dc7a3Sopenharmony_ci					style = 0;
1834cc1dc7a3Sopenharmony_ci					break;
1835cc1dc7a3Sopenharmony_ci			}
1836cc1dc7a3Sopenharmony_ci		}
1837cc1dc7a3Sopenharmony_ci
1838cc1dc7a3Sopenharmony_ci		return colors[style];
1839cc1dc7a3Sopenharmony_ci	};
1840cc1dc7a3Sopenharmony_ci
1841cc1dc7a3Sopenharmony_ci	print_diagnostic_image(context, image, *diag_image, texel_func7);
1842cc1dc7a3Sopenharmony_ci	fname = stem + "_diag_cem_style.png";
1843cc1dc7a3Sopenharmony_ci	store_ncimage(diag_image, fname.c_str(), false);
1844cc1dc7a3Sopenharmony_ci
1845cc1dc7a3Sopenharmony_ci	// ---- ---- ---- ---- Color endpoint mode: Style ---- ---- ---- ----
1846cc1dc7a3Sopenharmony_ci	auto texel_func8 = [](astcenc_block_info& info, size_t texel_x, size_t texel_y) {
1847cc1dc7a3Sopenharmony_ci		(void)texel_x;
1848cc1dc7a3Sopenharmony_ci		(void)texel_y;
1849cc1dc7a3Sopenharmony_ci
1850cc1dc7a3Sopenharmony_ci		size_t texel_index = texel_y * info.block_x + texel_x;
1851cc1dc7a3Sopenharmony_ci
1852cc1dc7a3Sopenharmony_ci		int style { 0 };
1853cc1dc7a3Sopenharmony_ci		if (!info.is_constant_block)
1854cc1dc7a3Sopenharmony_ci		{
1855cc1dc7a3Sopenharmony_ci			uint8_t partition = info.partition_assignment[texel_index];
1856cc1dc7a3Sopenharmony_ci			uint8_t cem = info.color_endpoint_modes[partition];
1857cc1dc7a3Sopenharmony_ci
1858cc1dc7a3Sopenharmony_ci			switch (cem)
1859cc1dc7a3Sopenharmony_ci			{
1860cc1dc7a3Sopenharmony_ci				// LDR blocks
1861cc1dc7a3Sopenharmony_ci				case 0:
1862cc1dc7a3Sopenharmony_ci				case 1:
1863cc1dc7a3Sopenharmony_ci				case 4:
1864cc1dc7a3Sopenharmony_ci				case 5:
1865cc1dc7a3Sopenharmony_ci				case 6:
1866cc1dc7a3Sopenharmony_ci				case 8:
1867cc1dc7a3Sopenharmony_ci				case 9:
1868cc1dc7a3Sopenharmony_ci				case 10:
1869cc1dc7a3Sopenharmony_ci				case 12:
1870cc1dc7a3Sopenharmony_ci				case 13:
1871cc1dc7a3Sopenharmony_ci					style = 128;
1872cc1dc7a3Sopenharmony_ci					break;
1873cc1dc7a3Sopenharmony_ci				// HDR blocks
1874cc1dc7a3Sopenharmony_ci				default:
1875cc1dc7a3Sopenharmony_ci					style = 155;
1876cc1dc7a3Sopenharmony_ci					break;
1877cc1dc7a3Sopenharmony_ci			}
1878cc1dc7a3Sopenharmony_ci		}
1879cc1dc7a3Sopenharmony_ci
1880cc1dc7a3Sopenharmony_ci		return vint4(style, style, style, 255);
1881cc1dc7a3Sopenharmony_ci	};
1882cc1dc7a3Sopenharmony_ci
1883cc1dc7a3Sopenharmony_ci	print_diagnostic_image(context, image, *diag_image, texel_func8);
1884cc1dc7a3Sopenharmony_ci	fname = stem + "_diag_cem_hdr.png";
1885cc1dc7a3Sopenharmony_ci	store_ncimage(diag_image, fname.c_str(), false);
1886cc1dc7a3Sopenharmony_ci
1887cc1dc7a3Sopenharmony_ci	free_image(diag_image);
1888cc1dc7a3Sopenharmony_ci}
1889cc1dc7a3Sopenharmony_ci
1890cc1dc7a3Sopenharmony_ci#if QUALITY_CONTROL
1891cc1dc7a3Sopenharmony_ciconstexpr double MAX_PSNR = 99.9;
1892cc1dc7a3Sopenharmony_ciconstexpr double MAX_VALUE = 255;
1893cc1dc7a3Sopenharmony_ciconstexpr double THRESHOLD_R = 30.0;
1894cc1dc7a3Sopenharmony_ciconstexpr double THRESHOLD_G = 30.0;
1895cc1dc7a3Sopenharmony_ciconstexpr double THRESHOLD_B = 30.0;
1896cc1dc7a3Sopenharmony_ciconstexpr double THRESHOLD_A = 30.0;
1897cc1dc7a3Sopenharmony_ciconstexpr double THRESHOLD_RGB = 30.0;
1898cc1dc7a3Sopenharmony_ciconstexpr double LOG_BASE = 10.0;
1899cc1dc7a3Sopenharmony_ci
1900cc1dc7a3Sopenharmony_cibool CheckQuality(int32_t* mseIn[RGBA_COM], int blockNum, int blockXYZ)
1901cc1dc7a3Sopenharmony_ci{
1902cc1dc7a3Sopenharmony_ci    double psnr[RGBA_COM + 1];
1903cc1dc7a3Sopenharmony_ci    double threshold[RGBA_COM + 1] = { THRESHOLD_R, THRESHOLD_G, THRESHOLD_B, THRESHOLD_A, THRESHOLD_RGB};
1904cc1dc7a3Sopenharmony_ci    uint64_t mseTotal[RGBA_COM + 1] = { 0, 0, 0, 0, 0};
1905cc1dc7a3Sopenharmony_ci    for (int i = R_COM; i < RGBA_COM; i++) {
1906cc1dc7a3Sopenharmony_ci        int32_t* mse = mseIn[i];
1907cc1dc7a3Sopenharmony_ci        for (int j = 0; j < blockNum; j++) {
1908cc1dc7a3Sopenharmony_ci            mseTotal[i] += *mse;
1909cc1dc7a3Sopenharmony_ci            if(i != A_COM) mseTotal[RGBA_COM] += *mse;
1910cc1dc7a3Sopenharmony_ci            mse++;
1911cc1dc7a3Sopenharmony_ci        }
1912cc1dc7a3Sopenharmony_ci    }
1913cc1dc7a3Sopenharmony_ci    for (int i = R_COM; i < RGBA_COM; i++) {
1914cc1dc7a3Sopenharmony_ci        if (mseTotal[i] == 0) {
1915cc1dc7a3Sopenharmony_ci        	psnr[i] = MAX_PSNR;
1916cc1dc7a3Sopenharmony_ci        	continue;
1917cc1dc7a3Sopenharmony_ci        }
1918cc1dc7a3Sopenharmony_ci        double mseRgb = (double)mseTotal[i] / (blockNum * blockXYZ);
1919cc1dc7a3Sopenharmony_ci        psnr[i] = LOG_BASE * log((double)(MAX_VALUE * MAX_VALUE) / mseRgb) / log(LOG_BASE);
1920cc1dc7a3Sopenharmony_ci    }
1921cc1dc7a3Sopenharmony_ci    if (mseTotal[RGBA_COM] == 0) {
1922cc1dc7a3Sopenharmony_ci        psnr[RGBA_COM] = MAX_PSNR;
1923cc1dc7a3Sopenharmony_ci    }
1924cc1dc7a3Sopenharmony_ci    else {
1925cc1dc7a3Sopenharmony_ci        double mseRgb = (double)mseTotal[RGBA_COM] / (blockNum * blockXYZ * (RGBA_COM - 1));
1926cc1dc7a3Sopenharmony_ci        psnr[RGBA_COM] = LOG_BASE * log((double)(MAX_VALUE * MAX_VALUE) / mseRgb) / log(LOG_BASE);
1927cc1dc7a3Sopenharmony_ci    }
1928cc1dc7a3Sopenharmony_ci    printf("astc psnr r%f g%f b%f a%f rgb%f\n",
1929cc1dc7a3Sopenharmony_ci        psnr[R_COM], psnr[G_COM], psnr[B_COM], psnr[A_COM],
1930cc1dc7a3Sopenharmony_ci        psnr[RGBA_COM]);
1931cc1dc7a3Sopenharmony_ci    return (psnr[R_COM] > threshold[R_COM]) && (psnr[G_COM] > threshold[G_COM])
1932cc1dc7a3Sopenharmony_ci        && (psnr[B_COM] > threshold[B_COM]) && (psnr[A_COM] > threshold[A_COM])
1933cc1dc7a3Sopenharmony_ci        && (psnr[RGBA_COM] > threshold[RGBA_COM]);
1934cc1dc7a3Sopenharmony_ci}
1935cc1dc7a3Sopenharmony_ci#endif
1936cc1dc7a3Sopenharmony_ci
1937cc1dc7a3Sopenharmony_ci/**
1938cc1dc7a3Sopenharmony_ci * @brief The main entry point.
1939cc1dc7a3Sopenharmony_ci *
1940cc1dc7a3Sopenharmony_ci * @param argc   The number of arguments.
1941cc1dc7a3Sopenharmony_ci * @param argv   The vector of arguments.
1942cc1dc7a3Sopenharmony_ci *
1943cc1dc7a3Sopenharmony_ci * @return 0 on success, non-zero otherwise.
1944cc1dc7a3Sopenharmony_ci */
1945cc1dc7a3Sopenharmony_ciint astcenc_main(
1946cc1dc7a3Sopenharmony_ci	int argc,
1947cc1dc7a3Sopenharmony_ci	char **argv
1948cc1dc7a3Sopenharmony_ci) {
1949cc1dc7a3Sopenharmony_ci	double start_time = get_time();
1950cc1dc7a3Sopenharmony_ci
1951cc1dc7a3Sopenharmony_ci	if (argc < 2)
1952cc1dc7a3Sopenharmony_ci	{
1953cc1dc7a3Sopenharmony_ci		astcenc_print_shorthelp();
1954cc1dc7a3Sopenharmony_ci		return 0;
1955cc1dc7a3Sopenharmony_ci	}
1956cc1dc7a3Sopenharmony_ci
1957cc1dc7a3Sopenharmony_ci	astcenc_operation operation;
1958cc1dc7a3Sopenharmony_ci	astcenc_profile profile;
1959cc1dc7a3Sopenharmony_ci	int error = parse_commandline_options(argc, argv, operation, profile);
1960cc1dc7a3Sopenharmony_ci	if (error)
1961cc1dc7a3Sopenharmony_ci	{
1962cc1dc7a3Sopenharmony_ci		return 1;
1963cc1dc7a3Sopenharmony_ci	}
1964cc1dc7a3Sopenharmony_ci
1965cc1dc7a3Sopenharmony_ci	switch (operation)
1966cc1dc7a3Sopenharmony_ci	{
1967cc1dc7a3Sopenharmony_ci	case ASTCENC_OP_HELP:
1968cc1dc7a3Sopenharmony_ci		astcenc_print_longhelp();
1969cc1dc7a3Sopenharmony_ci		return 0;
1970cc1dc7a3Sopenharmony_ci	case ASTCENC_OP_VERSION:
1971cc1dc7a3Sopenharmony_ci		astcenc_print_header();
1972cc1dc7a3Sopenharmony_ci		return 0;
1973cc1dc7a3Sopenharmony_ci	default:
1974cc1dc7a3Sopenharmony_ci		break;
1975cc1dc7a3Sopenharmony_ci	}
1976cc1dc7a3Sopenharmony_ci
1977cc1dc7a3Sopenharmony_ci	std::string input_filename = argc >= 3 ? argv[2] : "";
1978cc1dc7a3Sopenharmony_ci	std::string output_filename = argc >= 4 ? argv[3] : "";
1979cc1dc7a3Sopenharmony_ci
1980cc1dc7a3Sopenharmony_ci	if (input_filename.empty())
1981cc1dc7a3Sopenharmony_ci	{
1982cc1dc7a3Sopenharmony_ci		print_error("ERROR: Input file not specified\n");
1983cc1dc7a3Sopenharmony_ci		return 1;
1984cc1dc7a3Sopenharmony_ci	}
1985cc1dc7a3Sopenharmony_ci
1986cc1dc7a3Sopenharmony_ci	if (output_filename.empty())
1987cc1dc7a3Sopenharmony_ci	{
1988cc1dc7a3Sopenharmony_ci		print_error("ERROR: Output file not specified\n");
1989cc1dc7a3Sopenharmony_ci		return 1;
1990cc1dc7a3Sopenharmony_ci	}
1991cc1dc7a3Sopenharmony_ci
1992cc1dc7a3Sopenharmony_ci	// TODO: Handle RAII resources so they get freed when out of scope
1993cc1dc7a3Sopenharmony_ci	// Load the compressed input file if needed
1994cc1dc7a3Sopenharmony_ci
1995cc1dc7a3Sopenharmony_ci	// This has to come first, as the block size is in the file header
1996cc1dc7a3Sopenharmony_ci	astc_compressed_image image_comp {};
1997cc1dc7a3Sopenharmony_ci	if (operation & ASTCENC_STAGE_LD_COMP)
1998cc1dc7a3Sopenharmony_ci	{
1999cc1dc7a3Sopenharmony_ci		if (ends_with(input_filename, ".astc"))
2000cc1dc7a3Sopenharmony_ci		{
2001cc1dc7a3Sopenharmony_ci			error = load_cimage(input_filename.c_str(), image_comp);
2002cc1dc7a3Sopenharmony_ci			if (error)
2003cc1dc7a3Sopenharmony_ci			{
2004cc1dc7a3Sopenharmony_ci				return 1;
2005cc1dc7a3Sopenharmony_ci			}
2006cc1dc7a3Sopenharmony_ci		}
2007cc1dc7a3Sopenharmony_ci		else if (ends_with(input_filename, ".ktx"))
2008cc1dc7a3Sopenharmony_ci		{
2009cc1dc7a3Sopenharmony_ci			bool is_srgb;
2010cc1dc7a3Sopenharmony_ci			error = load_ktx_compressed_image(input_filename.c_str(), is_srgb, image_comp);
2011cc1dc7a3Sopenharmony_ci			if (error)
2012cc1dc7a3Sopenharmony_ci			{
2013cc1dc7a3Sopenharmony_ci				return 1;
2014cc1dc7a3Sopenharmony_ci			}
2015cc1dc7a3Sopenharmony_ci
2016cc1dc7a3Sopenharmony_ci			if (is_srgb && (profile != ASTCENC_PRF_LDR_SRGB))
2017cc1dc7a3Sopenharmony_ci			{
2018cc1dc7a3Sopenharmony_ci				printf("WARNING: Input file is sRGB, but decompressing as linear\n");
2019cc1dc7a3Sopenharmony_ci			}
2020cc1dc7a3Sopenharmony_ci
2021cc1dc7a3Sopenharmony_ci			if (!is_srgb && (profile == ASTCENC_PRF_LDR_SRGB))
2022cc1dc7a3Sopenharmony_ci			{
2023cc1dc7a3Sopenharmony_ci				printf("WARNING: Input file is linear, but decompressing as sRGB\n");
2024cc1dc7a3Sopenharmony_ci			}
2025cc1dc7a3Sopenharmony_ci		}
2026cc1dc7a3Sopenharmony_ci		else
2027cc1dc7a3Sopenharmony_ci		{
2028cc1dc7a3Sopenharmony_ci			print_error("ERROR: Unknown compressed input file type\n");
2029cc1dc7a3Sopenharmony_ci			return 1;
2030cc1dc7a3Sopenharmony_ci		}
2031cc1dc7a3Sopenharmony_ci	}
2032cc1dc7a3Sopenharmony_ci
2033cc1dc7a3Sopenharmony_ci	astcenc_config config {};
2034cc1dc7a3Sopenharmony_ci	astcenc_preprocess preprocess;
2035cc1dc7a3Sopenharmony_ci	error = init_astcenc_config(argc, argv, profile, operation, image_comp, preprocess, config);
2036cc1dc7a3Sopenharmony_ci	if (error)
2037cc1dc7a3Sopenharmony_ci	{
2038cc1dc7a3Sopenharmony_ci		return 1;
2039cc1dc7a3Sopenharmony_ci	}
2040cc1dc7a3Sopenharmony_ci
2041cc1dc7a3Sopenharmony_ci	// Initialize cli_config_options with default values
2042cc1dc7a3Sopenharmony_ci	cli_config_options cli_config { 0, 1, 1, false, false, false, -10, 10,
2043cc1dc7a3Sopenharmony_ci		{ ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A },
2044cc1dc7a3Sopenharmony_ci		{ ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A } };
2045cc1dc7a3Sopenharmony_ci
2046cc1dc7a3Sopenharmony_ci	error = edit_astcenc_config(argc, argv, operation, cli_config, config);
2047cc1dc7a3Sopenharmony_ci	if (error)
2048cc1dc7a3Sopenharmony_ci	{
2049cc1dc7a3Sopenharmony_ci		return 1;
2050cc1dc7a3Sopenharmony_ci	}
2051cc1dc7a3Sopenharmony_ci
2052cc1dc7a3Sopenharmony_ci	// Enable progress callback if not in silent mode and using a terminal
2053cc1dc7a3Sopenharmony_ci	#if defined(_WIN32)
2054cc1dc7a3Sopenharmony_ci		int stdoutfno = _fileno(stdout);
2055cc1dc7a3Sopenharmony_ci	#else
2056cc1dc7a3Sopenharmony_ci		int stdoutfno = STDOUT_FILENO;
2057cc1dc7a3Sopenharmony_ci	#endif
2058cc1dc7a3Sopenharmony_ci
2059cc1dc7a3Sopenharmony_ci	if ((!cli_config.silentmode) && isatty(stdoutfno))
2060cc1dc7a3Sopenharmony_ci	{
2061cc1dc7a3Sopenharmony_ci		config.progress_callback = progress_emitter;
2062cc1dc7a3Sopenharmony_ci	}
2063cc1dc7a3Sopenharmony_ci
2064cc1dc7a3Sopenharmony_ci	astcenc_image* image_uncomp_in = nullptr ;
2065cc1dc7a3Sopenharmony_ci	unsigned int image_uncomp_in_component_count = 0;
2066cc1dc7a3Sopenharmony_ci	bool image_uncomp_in_is_hdr = false;
2067cc1dc7a3Sopenharmony_ci	astcenc_image* image_decomp_out = nullptr;
2068cc1dc7a3Sopenharmony_ci
2069cc1dc7a3Sopenharmony_ci	// Determine decompression output bitness, if limited by file type
2070cc1dc7a3Sopenharmony_ci	int out_bitness = 0;
2071cc1dc7a3Sopenharmony_ci	if (operation & ASTCENC_STAGE_DECOMPRESS)
2072cc1dc7a3Sopenharmony_ci	{
2073cc1dc7a3Sopenharmony_ci		out_bitness = get_output_filename_enforced_bitness(output_filename.c_str());
2074cc1dc7a3Sopenharmony_ci		if (out_bitness == 0)
2075cc1dc7a3Sopenharmony_ci		{
2076cc1dc7a3Sopenharmony_ci			bool is_hdr = (config.profile == ASTCENC_PRF_HDR) ||
2077cc1dc7a3Sopenharmony_ci			              (config.profile == ASTCENC_PRF_HDR_RGB_LDR_A);
2078cc1dc7a3Sopenharmony_ci			out_bitness = is_hdr ? 16 : 8;
2079cc1dc7a3Sopenharmony_ci		}
2080cc1dc7a3Sopenharmony_ci
2081cc1dc7a3Sopenharmony_ci		// If decompressed output is unorm8 then force the decode_unorm8 heuristics for compression
2082cc1dc7a3Sopenharmony_ci		if (out_bitness == 8)
2083cc1dc7a3Sopenharmony_ci		{
2084cc1dc7a3Sopenharmony_ci			config.flags |= ASTCENC_FLG_USE_DECODE_UNORM8;
2085cc1dc7a3Sopenharmony_ci		}
2086cc1dc7a3Sopenharmony_ci	}
2087cc1dc7a3Sopenharmony_ci
2088cc1dc7a3Sopenharmony_ci	// TODO: Handle RAII resources so they get freed when out of scope
2089cc1dc7a3Sopenharmony_ci	astcenc_error    codec_status;
2090cc1dc7a3Sopenharmony_ci	astcenc_context* codec_context;
2091cc1dc7a3Sopenharmony_ci
2092cc1dc7a3Sopenharmony_ci	// Preflight - check we have valid extensions for storing a file
2093cc1dc7a3Sopenharmony_ci	if (operation & ASTCENC_STAGE_ST_NCOMP)
2094cc1dc7a3Sopenharmony_ci	{
2095cc1dc7a3Sopenharmony_ci		int bitness = get_output_filename_enforced_bitness(output_filename.c_str());
2096cc1dc7a3Sopenharmony_ci		if (bitness < 0)
2097cc1dc7a3Sopenharmony_ci		{
2098cc1dc7a3Sopenharmony_ci			const char *eptr = strrchr(output_filename.c_str(), '.');
2099cc1dc7a3Sopenharmony_ci			eptr = eptr ? eptr : "";
2100cc1dc7a3Sopenharmony_ci			print_error("ERROR: Unknown uncompressed output file type '%s'\n", eptr);
2101cc1dc7a3Sopenharmony_ci			return 1;
2102cc1dc7a3Sopenharmony_ci		}
2103cc1dc7a3Sopenharmony_ci	}
2104cc1dc7a3Sopenharmony_ci
2105cc1dc7a3Sopenharmony_ci	if (operation & ASTCENC_STAGE_ST_COMP)
2106cc1dc7a3Sopenharmony_ci	{
2107cc1dc7a3Sopenharmony_ci#if defined(_WIN32)
2108cc1dc7a3Sopenharmony_ci		bool is_null = output_filename == "NUL" || output_filename == "nul";
2109cc1dc7a3Sopenharmony_ci#else
2110cc1dc7a3Sopenharmony_ci		bool is_null = output_filename == "/dev/null";
2111cc1dc7a3Sopenharmony_ci#endif
2112cc1dc7a3Sopenharmony_ci
2113cc1dc7a3Sopenharmony_ci		if (!(is_null || ends_with(output_filename, ".astc") || ends_with(output_filename, ".ktx")))
2114cc1dc7a3Sopenharmony_ci		{
2115cc1dc7a3Sopenharmony_ci			const char *eptr = strrchr(output_filename.c_str(), '.');
2116cc1dc7a3Sopenharmony_ci			eptr = eptr ? eptr : "";
2117cc1dc7a3Sopenharmony_ci			print_error("ERROR: Unknown compressed output file type '%s'\n", eptr);
2118cc1dc7a3Sopenharmony_ci			return 1;
2119cc1dc7a3Sopenharmony_ci		}
2120cc1dc7a3Sopenharmony_ci	}
2121cc1dc7a3Sopenharmony_ci
2122cc1dc7a3Sopenharmony_ci	codec_status = astcenc_context_alloc(&config, cli_config.thread_count, &codec_context);
2123cc1dc7a3Sopenharmony_ci	if (codec_status != ASTCENC_SUCCESS)
2124cc1dc7a3Sopenharmony_ci	{
2125cc1dc7a3Sopenharmony_ci		print_error("ERROR: Codec context alloc failed: %s\n", astcenc_get_error_string(codec_status));
2126cc1dc7a3Sopenharmony_ci		return 1;
2127cc1dc7a3Sopenharmony_ci	}
2128cc1dc7a3Sopenharmony_ci
2129cc1dc7a3Sopenharmony_ci	// Load the uncompressed input file if needed
2130cc1dc7a3Sopenharmony_ci	if (operation & ASTCENC_STAGE_LD_NCOMP)
2131cc1dc7a3Sopenharmony_ci	{
2132cc1dc7a3Sopenharmony_ci		image_uncomp_in = load_uncomp_file(
2133cc1dc7a3Sopenharmony_ci		    input_filename.c_str(), cli_config.array_size, cli_config.y_flip,
2134cc1dc7a3Sopenharmony_ci		    image_uncomp_in_is_hdr, image_uncomp_in_component_count);
2135cc1dc7a3Sopenharmony_ci		if (!image_uncomp_in)
2136cc1dc7a3Sopenharmony_ci		{
2137cc1dc7a3Sopenharmony_ci			print_error("ERROR: Failed to load uncompressed image file\n");
2138cc1dc7a3Sopenharmony_ci			return 1;
2139cc1dc7a3Sopenharmony_ci		}
2140cc1dc7a3Sopenharmony_ci
2141cc1dc7a3Sopenharmony_ci
2142cc1dc7a3Sopenharmony_ci		if (preprocess != ASTCENC_PP_NONE)
2143cc1dc7a3Sopenharmony_ci		{
2144cc1dc7a3Sopenharmony_ci			// Allocate a float image so we can avoid additional quantization,
2145cc1dc7a3Sopenharmony_ci			// as e.g. premultiplication can result in fractional color values
2146cc1dc7a3Sopenharmony_ci			astcenc_image* image_pp = alloc_image(32,
2147cc1dc7a3Sopenharmony_ci			                                      image_uncomp_in->dim_x,
2148cc1dc7a3Sopenharmony_ci			                                      image_uncomp_in->dim_y,
2149cc1dc7a3Sopenharmony_ci			                                      image_uncomp_in->dim_z);
2150cc1dc7a3Sopenharmony_ci			if (!image_pp)
2151cc1dc7a3Sopenharmony_ci			{
2152cc1dc7a3Sopenharmony_ci				print_error("ERROR: Failed to allocate preprocessed image\n");
2153cc1dc7a3Sopenharmony_ci				return 1;
2154cc1dc7a3Sopenharmony_ci			}
2155cc1dc7a3Sopenharmony_ci
2156cc1dc7a3Sopenharmony_ci			if (preprocess == ASTCENC_PP_NORMALIZE)
2157cc1dc7a3Sopenharmony_ci			{
2158cc1dc7a3Sopenharmony_ci				image_preprocess_normalize(*image_uncomp_in, *image_pp);
2159cc1dc7a3Sopenharmony_ci			}
2160cc1dc7a3Sopenharmony_ci
2161cc1dc7a3Sopenharmony_ci			if (preprocess == ASTCENC_PP_PREMULTIPLY)
2162cc1dc7a3Sopenharmony_ci			{
2163cc1dc7a3Sopenharmony_ci				image_preprocess_premultiply(*image_uncomp_in, *image_pp,
2164cc1dc7a3Sopenharmony_ci				                             config.profile);
2165cc1dc7a3Sopenharmony_ci			}
2166cc1dc7a3Sopenharmony_ci
2167cc1dc7a3Sopenharmony_ci			// Delete the original as we no longer need it
2168cc1dc7a3Sopenharmony_ci			free_image(image_uncomp_in);
2169cc1dc7a3Sopenharmony_ci			image_uncomp_in = image_pp;
2170cc1dc7a3Sopenharmony_ci		}
2171cc1dc7a3Sopenharmony_ci
2172cc1dc7a3Sopenharmony_ci		if (!cli_config.silentmode)
2173cc1dc7a3Sopenharmony_ci		{
2174cc1dc7a3Sopenharmony_ci			printf("Source image\n");
2175cc1dc7a3Sopenharmony_ci			printf("============\n\n");
2176cc1dc7a3Sopenharmony_ci			printf("    Source:                     %s\n", input_filename.c_str());
2177cc1dc7a3Sopenharmony_ci			printf("    Color profile:              %s\n", image_uncomp_in_is_hdr ? "HDR" : "LDR");
2178cc1dc7a3Sopenharmony_ci			if (image_uncomp_in->dim_z > 1)
2179cc1dc7a3Sopenharmony_ci			{
2180cc1dc7a3Sopenharmony_ci				printf("    Dimensions:                 3D, %ux%ux%u\n",
2181cc1dc7a3Sopenharmony_ci				       image_uncomp_in->dim_x, image_uncomp_in->dim_y, image_uncomp_in->dim_z);
2182cc1dc7a3Sopenharmony_ci			}
2183cc1dc7a3Sopenharmony_ci			else
2184cc1dc7a3Sopenharmony_ci			{
2185cc1dc7a3Sopenharmony_ci				printf("    Dimensions:                 2D, %ux%u\n",
2186cc1dc7a3Sopenharmony_ci				       image_uncomp_in->dim_x, image_uncomp_in->dim_y);
2187cc1dc7a3Sopenharmony_ci			}
2188cc1dc7a3Sopenharmony_ci			printf("    Components:                 %d\n\n", image_uncomp_in_component_count);
2189cc1dc7a3Sopenharmony_ci		}
2190cc1dc7a3Sopenharmony_ci	}
2191cc1dc7a3Sopenharmony_ci
2192cc1dc7a3Sopenharmony_ci	double image_size = 0.0;
2193cc1dc7a3Sopenharmony_ci	if (image_uncomp_in)
2194cc1dc7a3Sopenharmony_ci	{
2195cc1dc7a3Sopenharmony_ci		image_size = static_cast<double>(image_uncomp_in->dim_x) *
2196cc1dc7a3Sopenharmony_ci		             static_cast<double>(image_uncomp_in->dim_y) *
2197cc1dc7a3Sopenharmony_ci		             static_cast<double>(image_uncomp_in->dim_z);
2198cc1dc7a3Sopenharmony_ci	}
2199cc1dc7a3Sopenharmony_ci	else
2200cc1dc7a3Sopenharmony_ci	{
2201cc1dc7a3Sopenharmony_ci		image_size = static_cast<double>(image_comp.dim_x) *
2202cc1dc7a3Sopenharmony_ci		             static_cast<double>(image_comp.dim_y) *
2203cc1dc7a3Sopenharmony_ci		             static_cast<double>(image_comp.dim_z);
2204cc1dc7a3Sopenharmony_ci	}
2205cc1dc7a3Sopenharmony_ci
2206cc1dc7a3Sopenharmony_ci	// Compress an image
2207cc1dc7a3Sopenharmony_ci	double best_compression_time = 100000.0;
2208cc1dc7a3Sopenharmony_ci	double total_compression_time = 0.0;
2209cc1dc7a3Sopenharmony_ci	if (operation & ASTCENC_STAGE_COMPRESS)
2210cc1dc7a3Sopenharmony_ci	{
2211cc1dc7a3Sopenharmony_ci		print_astcenc_config(cli_config, config);
2212cc1dc7a3Sopenharmony_ci
2213cc1dc7a3Sopenharmony_ci		unsigned int blocks_x = (image_uncomp_in->dim_x + config.block_x - 1) / config.block_x;
2214cc1dc7a3Sopenharmony_ci		unsigned int blocks_y = (image_uncomp_in->dim_y + config.block_y - 1) / config.block_y;
2215cc1dc7a3Sopenharmony_ci		unsigned int blocks_z = (image_uncomp_in->dim_z + config.block_z - 1) / config.block_z;
2216cc1dc7a3Sopenharmony_ci		size_t buffer_size = blocks_x * blocks_y * blocks_z * 16;
2217cc1dc7a3Sopenharmony_ci		uint8_t* buffer = new uint8_t[buffer_size];
2218cc1dc7a3Sopenharmony_ci
2219cc1dc7a3Sopenharmony_ci		compression_workload work;
2220cc1dc7a3Sopenharmony_ci		work.context = codec_context;
2221cc1dc7a3Sopenharmony_ci		image_uncomp_in->dim_stride = image_uncomp_in->dim_x;
2222cc1dc7a3Sopenharmony_ci		work.image = image_uncomp_in;
2223cc1dc7a3Sopenharmony_ci		work.swizzle = cli_config.swz_encode;
2224cc1dc7a3Sopenharmony_ci		work.data_out = buffer;
2225cc1dc7a3Sopenharmony_ci		work.data_len = buffer_size;
2226cc1dc7a3Sopenharmony_ci		work.error = ASTCENC_SUCCESS;
2227cc1dc7a3Sopenharmony_ci#if QUALITY_CONTROL
2228cc1dc7a3Sopenharmony_ci		work.calQualityEnable = true;
2229cc1dc7a3Sopenharmony_ci		work.mse[R_COM] = work.mse[G_COM] = work.mse[B_COM] = work.mse[A_COM] = nullptr;
2230cc1dc7a3Sopenharmony_ci		if (work.calQualityEnable) {
2231cc1dc7a3Sopenharmony_ci		for (int i = R_COM; i < RGBA_COM; i++) {
2232cc1dc7a3Sopenharmony_ci				work.mse[i] = (int32_t*)calloc(blocks_x * blocks_y, sizeof(int32_t));
2233cc1dc7a3Sopenharmony_ci				if (!work.mse[i]) {
2234cc1dc7a3Sopenharmony_ci					printf("quality control calloc failed");
2235cc1dc7a3Sopenharmony_ci					return -1;
2236cc1dc7a3Sopenharmony_ci				}
2237cc1dc7a3Sopenharmony_ci			}
2238cc1dc7a3Sopenharmony_ci		}
2239cc1dc7a3Sopenharmony_ci#endif
2240cc1dc7a3Sopenharmony_ci		// Only launch worker threads for multi-threaded use - it makes basic
2241cc1dc7a3Sopenharmony_ci		// single-threaded profiling and debugging a little less convoluted
2242cc1dc7a3Sopenharmony_ci		double start_compression_time = get_time();
2243cc1dc7a3Sopenharmony_ci		for (unsigned int i = 0; i < cli_config.repeat_count; i++)
2244cc1dc7a3Sopenharmony_ci		{
2245cc1dc7a3Sopenharmony_ci			if (config.progress_callback)
2246cc1dc7a3Sopenharmony_ci			{
2247cc1dc7a3Sopenharmony_ci				printf("Compression\n");
2248cc1dc7a3Sopenharmony_ci				printf("===========\n");
2249cc1dc7a3Sopenharmony_ci				printf("\n");
2250cc1dc7a3Sopenharmony_ci			}
2251cc1dc7a3Sopenharmony_ci
2252cc1dc7a3Sopenharmony_ci			double start_iter_time = get_time();
2253cc1dc7a3Sopenharmony_ci			if (cli_config.thread_count > 1)
2254cc1dc7a3Sopenharmony_ci			{
2255cc1dc7a3Sopenharmony_ci				launch_threads("Compression", cli_config.thread_count, compression_workload_runner, &work);
2256cc1dc7a3Sopenharmony_ci			}
2257cc1dc7a3Sopenharmony_ci			else
2258cc1dc7a3Sopenharmony_ci			{
2259cc1dc7a3Sopenharmony_ci				work.error = astcenc_compress_image(
2260cc1dc7a3Sopenharmony_ci					work.context, work.image, &work.swizzle,
2261cc1dc7a3Sopenharmony_ci					work.data_out, work.data_len,
2262cc1dc7a3Sopenharmony_ci#if QUALITY_CONTROL
2263cc1dc7a3Sopenharmony_ci			    	work.calQualityEnable, work.mse,
2264cc1dc7a3Sopenharmony_ci#endif
2265cc1dc7a3Sopenharmony_ci			    	0);
2266cc1dc7a3Sopenharmony_ci			}
2267cc1dc7a3Sopenharmony_ci
2268cc1dc7a3Sopenharmony_ci			astcenc_compress_reset(codec_context);
2269cc1dc7a3Sopenharmony_ci
2270cc1dc7a3Sopenharmony_ci			if (config.progress_callback)
2271cc1dc7a3Sopenharmony_ci			{
2272cc1dc7a3Sopenharmony_ci				printf("\n\n");
2273cc1dc7a3Sopenharmony_ci			}
2274cc1dc7a3Sopenharmony_ci
2275cc1dc7a3Sopenharmony_ci			double iter_time = get_time() - start_iter_time;
2276cc1dc7a3Sopenharmony_ci			best_compression_time = astc::min(iter_time, best_compression_time);
2277cc1dc7a3Sopenharmony_ci		}
2278cc1dc7a3Sopenharmony_ci		total_compression_time = get_time() - start_compression_time;
2279cc1dc7a3Sopenharmony_ci
2280cc1dc7a3Sopenharmony_ci		if (work.error != ASTCENC_SUCCESS)
2281cc1dc7a3Sopenharmony_ci		{
2282cc1dc7a3Sopenharmony_ci			print_error("ERROR: Codec compress failed: %s\n", astcenc_get_error_string(work.error));
2283cc1dc7a3Sopenharmony_ci			return 1;
2284cc1dc7a3Sopenharmony_ci		}
2285cc1dc7a3Sopenharmony_ci#if QUALITY_CONTROL
2286cc1dc7a3Sopenharmony_ci		if (work.calQualityEnable && !CheckQuality(work.mse, blocks_x * blocks_y, config.block_x * config.block_y)) {
2287cc1dc7a3Sopenharmony_ci		    work.error = ASTCENC_ERR_BAD_QUALITY_CHECK;
2288cc1dc7a3Sopenharmony_ci		}
2289cc1dc7a3Sopenharmony_ci		if (work.calQualityEnable) {
2290cc1dc7a3Sopenharmony_ci			for (int i = R_COM; i < RGBA_COM; i++) {
2291cc1dc7a3Sopenharmony_ci				if (work.mse[i]) {
2292cc1dc7a3Sopenharmony_ci					free(work.mse[i]);
2293cc1dc7a3Sopenharmony_ci				}
2294cc1dc7a3Sopenharmony_ci			}
2295cc1dc7a3Sopenharmony_ci		}
2296cc1dc7a3Sopenharmony_ci#endif
2297cc1dc7a3Sopenharmony_ci		image_comp.block_x = config.block_x;
2298cc1dc7a3Sopenharmony_ci		image_comp.block_y = config.block_y;
2299cc1dc7a3Sopenharmony_ci		image_comp.block_z = config.block_z;
2300cc1dc7a3Sopenharmony_ci		image_comp.dim_x = image_uncomp_in->dim_x;
2301cc1dc7a3Sopenharmony_ci		image_comp.dim_y = image_uncomp_in->dim_y;
2302cc1dc7a3Sopenharmony_ci		image_comp.dim_z = image_uncomp_in->dim_z;
2303cc1dc7a3Sopenharmony_ci		image_comp.data = buffer;
2304cc1dc7a3Sopenharmony_ci		image_comp.data_len = buffer_size;
2305cc1dc7a3Sopenharmony_ci	}
2306cc1dc7a3Sopenharmony_ci
2307cc1dc7a3Sopenharmony_ci	// Decompress an image
2308cc1dc7a3Sopenharmony_ci	double best_decompression_time = 100000.0;
2309cc1dc7a3Sopenharmony_ci	double total_decompression_time = 0.0;
2310cc1dc7a3Sopenharmony_ci	if (operation & ASTCENC_STAGE_DECOMPRESS)
2311cc1dc7a3Sopenharmony_ci	{
2312cc1dc7a3Sopenharmony_ci		image_decomp_out = alloc_image(
2313cc1dc7a3Sopenharmony_ci		    out_bitness, image_comp.dim_x, image_comp.dim_y, image_comp.dim_z);
2314cc1dc7a3Sopenharmony_ci
2315cc1dc7a3Sopenharmony_ci		decompression_workload work;
2316cc1dc7a3Sopenharmony_ci		work.context = codec_context;
2317cc1dc7a3Sopenharmony_ci		work.data = image_comp.data;
2318cc1dc7a3Sopenharmony_ci		work.data_len = image_comp.data_len;
2319cc1dc7a3Sopenharmony_ci		work.image_out = image_decomp_out;
2320cc1dc7a3Sopenharmony_ci		work.swizzle = cli_config.swz_decode;
2321cc1dc7a3Sopenharmony_ci		work.error = ASTCENC_SUCCESS;
2322cc1dc7a3Sopenharmony_ci
2323cc1dc7a3Sopenharmony_ci		// Only launch worker threads for multi-threaded use - it makes basic
2324cc1dc7a3Sopenharmony_ci		// single-threaded profiling and debugging a little less convoluted
2325cc1dc7a3Sopenharmony_ci		double start_decompression_time = get_time();
2326cc1dc7a3Sopenharmony_ci		for (unsigned int i = 0; i < cli_config.repeat_count; i++)
2327cc1dc7a3Sopenharmony_ci		{
2328cc1dc7a3Sopenharmony_ci			double start_iter_time = get_time();
2329cc1dc7a3Sopenharmony_ci			if (cli_config.thread_count > 1)
2330cc1dc7a3Sopenharmony_ci			{
2331cc1dc7a3Sopenharmony_ci				launch_threads("Decompression", cli_config.thread_count, decompression_workload_runner, &work);
2332cc1dc7a3Sopenharmony_ci			}
2333cc1dc7a3Sopenharmony_ci			else
2334cc1dc7a3Sopenharmony_ci			{
2335cc1dc7a3Sopenharmony_ci				work.error = astcenc_decompress_image(
2336cc1dc7a3Sopenharmony_ci				    work.context, work.data, work.data_len,
2337cc1dc7a3Sopenharmony_ci				    work.image_out, &work.swizzle, 0);
2338cc1dc7a3Sopenharmony_ci			}
2339cc1dc7a3Sopenharmony_ci
2340cc1dc7a3Sopenharmony_ci			astcenc_decompress_reset(codec_context);
2341cc1dc7a3Sopenharmony_ci
2342cc1dc7a3Sopenharmony_ci			double iter_time = get_time() - start_iter_time;
2343cc1dc7a3Sopenharmony_ci			best_decompression_time = astc::min(iter_time, best_decompression_time);
2344cc1dc7a3Sopenharmony_ci		}
2345cc1dc7a3Sopenharmony_ci		total_decompression_time = get_time() - start_decompression_time;
2346cc1dc7a3Sopenharmony_ci
2347cc1dc7a3Sopenharmony_ci		if (work.error != ASTCENC_SUCCESS)
2348cc1dc7a3Sopenharmony_ci		{
2349cc1dc7a3Sopenharmony_ci			print_error("ERROR: Codec decompress failed: %s\n", astcenc_get_error_string(codec_status));
2350cc1dc7a3Sopenharmony_ci			return 1;
2351cc1dc7a3Sopenharmony_ci		}
2352cc1dc7a3Sopenharmony_ci	}
2353cc1dc7a3Sopenharmony_ci
2354cc1dc7a3Sopenharmony_ci#if defined(_WIN32)
2355cc1dc7a3Sopenharmony_ci	bool is_null = output_filename == "NUL" || output_filename == "nul";
2356cc1dc7a3Sopenharmony_ci#else
2357cc1dc7a3Sopenharmony_ci	bool is_null = output_filename == "/dev/null";
2358cc1dc7a3Sopenharmony_ci#endif
2359cc1dc7a3Sopenharmony_ci
2360cc1dc7a3Sopenharmony_ci   // Print metrics in comparison mode
2361cc1dc7a3Sopenharmony_ci	if (operation & ASTCENC_STAGE_COMPARE)
2362cc1dc7a3Sopenharmony_ci	{
2363cc1dc7a3Sopenharmony_ci		bool is_normal_map = config.flags & ASTCENC_FLG_MAP_NORMAL;
2364cc1dc7a3Sopenharmony_ci
2365cc1dc7a3Sopenharmony_ci		compute_error_metrics(
2366cc1dc7a3Sopenharmony_ci		    image_uncomp_in_is_hdr, is_normal_map, image_uncomp_in_component_count,
2367cc1dc7a3Sopenharmony_ci		    image_uncomp_in, image_decomp_out, cli_config.low_fstop, cli_config.high_fstop);
2368cc1dc7a3Sopenharmony_ci	}
2369cc1dc7a3Sopenharmony_ci
2370cc1dc7a3Sopenharmony_ci	// Store compressed image
2371cc1dc7a3Sopenharmony_ci	if (operation & ASTCENC_STAGE_ST_COMP)
2372cc1dc7a3Sopenharmony_ci	{
2373cc1dc7a3Sopenharmony_ci		if (ends_with(output_filename, ".astc"))
2374cc1dc7a3Sopenharmony_ci		{
2375cc1dc7a3Sopenharmony_ci			error = store_cimage(image_comp, output_filename.c_str());
2376cc1dc7a3Sopenharmony_ci			if (error)
2377cc1dc7a3Sopenharmony_ci			{
2378cc1dc7a3Sopenharmony_ci				print_error("ERROR: Failed to store compressed image\n");
2379cc1dc7a3Sopenharmony_ci				return 1;
2380cc1dc7a3Sopenharmony_ci			}
2381cc1dc7a3Sopenharmony_ci		}
2382cc1dc7a3Sopenharmony_ci		else if (ends_with(output_filename, ".ktx"))
2383cc1dc7a3Sopenharmony_ci		{
2384cc1dc7a3Sopenharmony_ci			bool srgb = profile == ASTCENC_PRF_LDR_SRGB;
2385cc1dc7a3Sopenharmony_ci			error = store_ktx_compressed_image(image_comp, output_filename.c_str(), srgb);
2386cc1dc7a3Sopenharmony_ci			if (error)
2387cc1dc7a3Sopenharmony_ci			{
2388cc1dc7a3Sopenharmony_ci				print_error("ERROR: Failed to store compressed image\n");
2389cc1dc7a3Sopenharmony_ci				return 1;
2390cc1dc7a3Sopenharmony_ci			}
2391cc1dc7a3Sopenharmony_ci		}
2392cc1dc7a3Sopenharmony_ci		else
2393cc1dc7a3Sopenharmony_ci		{
2394cc1dc7a3Sopenharmony_ci			if (!is_null)
2395cc1dc7a3Sopenharmony_ci			{
2396cc1dc7a3Sopenharmony_ci				print_error("ERROR: Unknown compressed output file type\n");
2397cc1dc7a3Sopenharmony_ci				return 1;
2398cc1dc7a3Sopenharmony_ci			}
2399cc1dc7a3Sopenharmony_ci		}
2400cc1dc7a3Sopenharmony_ci	}
2401cc1dc7a3Sopenharmony_ci
2402cc1dc7a3Sopenharmony_ci	// Store decompressed image
2403cc1dc7a3Sopenharmony_ci	if (operation & ASTCENC_STAGE_ST_NCOMP)
2404cc1dc7a3Sopenharmony_ci	{
2405cc1dc7a3Sopenharmony_ci		if (!is_null)
2406cc1dc7a3Sopenharmony_ci		{
2407cc1dc7a3Sopenharmony_ci			bool store_result = store_ncimage(image_decomp_out, output_filename.c_str(),
2408cc1dc7a3Sopenharmony_ci			                                  cli_config.y_flip);
2409cc1dc7a3Sopenharmony_ci			if (!store_result)
2410cc1dc7a3Sopenharmony_ci			{
2411cc1dc7a3Sopenharmony_ci				print_error("ERROR: Failed to write output image %s\n", output_filename.c_str());
2412cc1dc7a3Sopenharmony_ci				return 1;
2413cc1dc7a3Sopenharmony_ci			}
2414cc1dc7a3Sopenharmony_ci		}
2415cc1dc7a3Sopenharmony_ci	}
2416cc1dc7a3Sopenharmony_ci
2417cc1dc7a3Sopenharmony_ci	// Store diagnostic images
2418cc1dc7a3Sopenharmony_ci	if (cli_config.diagnostic_images && !is_null)
2419cc1dc7a3Sopenharmony_ci	{
2420cc1dc7a3Sopenharmony_ci		print_diagnostic_images(codec_context, image_comp, output_filename);
2421cc1dc7a3Sopenharmony_ci	}
2422cc1dc7a3Sopenharmony_ci
2423cc1dc7a3Sopenharmony_ci	free_image(image_uncomp_in);
2424cc1dc7a3Sopenharmony_ci	free_image(image_decomp_out);
2425cc1dc7a3Sopenharmony_ci	astcenc_context_free(codec_context);
2426cc1dc7a3Sopenharmony_ci
2427cc1dc7a3Sopenharmony_ci	delete[] image_comp.data;
2428cc1dc7a3Sopenharmony_ci
2429cc1dc7a3Sopenharmony_ci	if ((operation & ASTCENC_STAGE_COMPARE) || (!cli_config.silentmode))
2430cc1dc7a3Sopenharmony_ci	{
2431cc1dc7a3Sopenharmony_ci		double end_time = get_time();
2432cc1dc7a3Sopenharmony_ci
2433cc1dc7a3Sopenharmony_ci		double repeats = static_cast<double>(cli_config.repeat_count);
2434cc1dc7a3Sopenharmony_ci		double avg_compression_time = total_compression_time / repeats;
2435cc1dc7a3Sopenharmony_ci		double avg_decompression_time = total_decompression_time / repeats;
2436cc1dc7a3Sopenharmony_ci		double total_time = (end_time - start_time) - ((repeats - 1.0) * avg_compression_time)  - ((repeats - 1.0) * avg_decompression_time);
2437cc1dc7a3Sopenharmony_ci
2438cc1dc7a3Sopenharmony_ci		printf("Performance metrics\n");
2439cc1dc7a3Sopenharmony_ci		printf("===================\n\n");
2440cc1dc7a3Sopenharmony_ci		printf("    Total time:                %8.4f s\n", total_time);
2441cc1dc7a3Sopenharmony_ci
2442cc1dc7a3Sopenharmony_ci		if (operation & ASTCENC_STAGE_COMPRESS)
2443cc1dc7a3Sopenharmony_ci		{
2444cc1dc7a3Sopenharmony_ci			double compression_rate = image_size / (best_compression_time * 1000000.0);
2445cc1dc7a3Sopenharmony_ci
2446cc1dc7a3Sopenharmony_ci			printf("    Coding time:               %8.4f s\n", best_compression_time);
2447cc1dc7a3Sopenharmony_ci			printf("    Coding rate:               %8.4f MT/s\n", compression_rate);
2448cc1dc7a3Sopenharmony_ci		}
2449cc1dc7a3Sopenharmony_ci
2450cc1dc7a3Sopenharmony_ci		if (operation & ASTCENC_STAGE_DECOMPRESS)
2451cc1dc7a3Sopenharmony_ci		{
2452cc1dc7a3Sopenharmony_ci			double decompression_rate = image_size / (best_decompression_time * 1000000.0);
2453cc1dc7a3Sopenharmony_ci			printf("    Decoding time:             %8.4f s\n", best_decompression_time);
2454cc1dc7a3Sopenharmony_ci			printf("    Decoding rate:             %8.4f MT/s\n", decompression_rate);
2455cc1dc7a3Sopenharmony_ci		}
2456cc1dc7a3Sopenharmony_ci	}
2457cc1dc7a3Sopenharmony_ci
2458cc1dc7a3Sopenharmony_ci	return 0;
2459cc1dc7a3Sopenharmony_ci}
2460