1cc1dc7a3Sopenharmony_ci// SPDX-License-Identifier: Apache-2.0
2cc1dc7a3Sopenharmony_ci// ----------------------------------------------------------------------------
3cc1dc7a3Sopenharmony_ci// Copyright 2011-2023 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 loading/storing uncompressed and compressed images.
20cc1dc7a3Sopenharmony_ci */
21cc1dc7a3Sopenharmony_ci
22cc1dc7a3Sopenharmony_ci#include <array>
23cc1dc7a3Sopenharmony_ci#include <cassert>
24cc1dc7a3Sopenharmony_ci#include <cstdio>
25cc1dc7a3Sopenharmony_ci#include <cstdlib>
26cc1dc7a3Sopenharmony_ci#include <cstring>
27cc1dc7a3Sopenharmony_ci#include <fstream>
28cc1dc7a3Sopenharmony_ci#include <iomanip>
29cc1dc7a3Sopenharmony_ci#include <sstream>
30cc1dc7a3Sopenharmony_ci
31cc1dc7a3Sopenharmony_ci#include "astcenccli_internal.h"
32cc1dc7a3Sopenharmony_ci
33cc1dc7a3Sopenharmony_ci#include "stb_image.h"
34cc1dc7a3Sopenharmony_ci#include "stb_image_write.h"
35cc1dc7a3Sopenharmony_ci#include "tinyexr.h"
36cc1dc7a3Sopenharmony_ci
37cc1dc7a3Sopenharmony_ci/**
38cc1dc7a3Sopenharmony_ci * @brief Determine the output file name to use for a sliced image write.
39cc1dc7a3Sopenharmony_ci *
40cc1dc7a3Sopenharmony_ci * @param img        The source data for the image.
41cc1dc7a3Sopenharmony_ci * @param filename   The base name of the file to save.
42cc1dc7a3Sopenharmony_ci * @param index      The slice index to write.
43cc1dc7a3Sopenharmony_ci *
44cc1dc7a3Sopenharmony_ci * @return The file name to use when saving the file.
45cc1dc7a3Sopenharmony_ci */
46cc1dc7a3Sopenharmony_cistatic std::string get_output_filename(
47cc1dc7a3Sopenharmony_ci	const astcenc_image* img,
48cc1dc7a3Sopenharmony_ci	const char* filename,
49cc1dc7a3Sopenharmony_ci	unsigned int index
50cc1dc7a3Sopenharmony_ci) {
51cc1dc7a3Sopenharmony_ci	if (img->dim_z <= 1)
52cc1dc7a3Sopenharmony_ci	{
53cc1dc7a3Sopenharmony_ci		return filename;
54cc1dc7a3Sopenharmony_ci	}
55cc1dc7a3Sopenharmony_ci
56cc1dc7a3Sopenharmony_ci	std::string fnmod(filename);
57cc1dc7a3Sopenharmony_ci	std::string fnext = fnmod.substr(fnmod.find_last_of("."));
58cc1dc7a3Sopenharmony_ci
59cc1dc7a3Sopenharmony_ci	// Remove the extension
60cc1dc7a3Sopenharmony_ci	fnmod = fnmod.erase(fnmod.length() - fnext.size());
61cc1dc7a3Sopenharmony_ci
62cc1dc7a3Sopenharmony_ci	// Insert the file index into the base name, then append the extension
63cc1dc7a3Sopenharmony_ci	std::stringstream ss;
64cc1dc7a3Sopenharmony_ci	ss << fnmod << "_" << std::setw(3) << std::setfill('0') << index << fnext;
65cc1dc7a3Sopenharmony_ci	return ss.str();
66cc1dc7a3Sopenharmony_ci}
67cc1dc7a3Sopenharmony_ci
68cc1dc7a3Sopenharmony_ci/* ============================================================================
69cc1dc7a3Sopenharmony_ci  Image load and store through the stb_image and tinyexr libraries
70cc1dc7a3Sopenharmony_ci============================================================================ */
71cc1dc7a3Sopenharmony_ci
72cc1dc7a3Sopenharmony_ci/**
73cc1dc7a3Sopenharmony_ci * @brief Load a .exr image using TinyExr to provide the loader.
74cc1dc7a3Sopenharmony_ci *
75cc1dc7a3Sopenharmony_ci * @param      filename          The name of the file to load.
76cc1dc7a3Sopenharmony_ci * @param      y_flip            Should the image be vertically flipped?
77cc1dc7a3Sopenharmony_ci * @param[out] is_hdr            Is this an HDR image load? Always @c true for this function.
78cc1dc7a3Sopenharmony_ci * @param[out] component_count   The number of components in the data.
79cc1dc7a3Sopenharmony_ci *
80cc1dc7a3Sopenharmony_ci * @return The loaded image data in a canonical 4 channel format.
81cc1dc7a3Sopenharmony_ci */
82cc1dc7a3Sopenharmony_cistatic astcenc_image* load_image_with_tinyexr(
83cc1dc7a3Sopenharmony_ci	const char* filename,
84cc1dc7a3Sopenharmony_ci	bool y_flip,
85cc1dc7a3Sopenharmony_ci	bool& is_hdr,
86cc1dc7a3Sopenharmony_ci	unsigned int& component_count
87cc1dc7a3Sopenharmony_ci) {
88cc1dc7a3Sopenharmony_ci	int dim_x, dim_y;
89cc1dc7a3Sopenharmony_ci	float* image;
90cc1dc7a3Sopenharmony_ci	const char* err;
91cc1dc7a3Sopenharmony_ci
92cc1dc7a3Sopenharmony_ci	int load_res = LoadEXR(&image, &dim_x, &dim_y, filename, &err);
93cc1dc7a3Sopenharmony_ci	if (load_res != TINYEXR_SUCCESS)
94cc1dc7a3Sopenharmony_ci	{
95cc1dc7a3Sopenharmony_ci		print_error("ERROR: Failed to load image %s (%s)\n", filename, err);
96cc1dc7a3Sopenharmony_ci		free(reinterpret_cast<void*>(const_cast<char*>(err)));
97cc1dc7a3Sopenharmony_ci		return nullptr;
98cc1dc7a3Sopenharmony_ci	}
99cc1dc7a3Sopenharmony_ci
100cc1dc7a3Sopenharmony_ci	astcenc_image* res_img = astc_img_from_floatx4_array(image, dim_x, dim_y, y_flip);
101cc1dc7a3Sopenharmony_ci	free(image);
102cc1dc7a3Sopenharmony_ci
103cc1dc7a3Sopenharmony_ci	is_hdr = true;
104cc1dc7a3Sopenharmony_ci	component_count = 4;
105cc1dc7a3Sopenharmony_ci	return res_img;
106cc1dc7a3Sopenharmony_ci}
107cc1dc7a3Sopenharmony_ci
108cc1dc7a3Sopenharmony_ci/**
109cc1dc7a3Sopenharmony_ci * @brief Load an image using STBImage to provide the loader.
110cc1dc7a3Sopenharmony_ci *
111cc1dc7a3Sopenharmony_ci * @param      filename          The name of the file to load.
112cc1dc7a3Sopenharmony_ci * @param      y_flip            Should the image be vertically flipped?
113cc1dc7a3Sopenharmony_ci * @param[out] is_hdr            Is this an HDR image load?
114cc1dc7a3Sopenharmony_ci * @param[out] component_count   The number of components in the data.
115cc1dc7a3Sopenharmony_ci *
116cc1dc7a3Sopenharmony_ci * @return The loaded image data in a canonical 4 channel format, or @c nullptr on error.
117cc1dc7a3Sopenharmony_ci */
118cc1dc7a3Sopenharmony_cistatic astcenc_image* load_image_with_stb(
119cc1dc7a3Sopenharmony_ci	const char* filename,
120cc1dc7a3Sopenharmony_ci	bool y_flip,
121cc1dc7a3Sopenharmony_ci	bool& is_hdr,
122cc1dc7a3Sopenharmony_ci	unsigned int& component_count
123cc1dc7a3Sopenharmony_ci) {
124cc1dc7a3Sopenharmony_ci	int dim_x, dim_y;
125cc1dc7a3Sopenharmony_ci
126cc1dc7a3Sopenharmony_ci	if (stbi_is_hdr(filename))
127cc1dc7a3Sopenharmony_ci	{
128cc1dc7a3Sopenharmony_ci		float* data = stbi_loadf(filename, &dim_x, &dim_y, nullptr, STBI_rgb_alpha);
129cc1dc7a3Sopenharmony_ci		if (data)
130cc1dc7a3Sopenharmony_ci		{
131cc1dc7a3Sopenharmony_ci			astcenc_image* img = astc_img_from_floatx4_array(data, dim_x, dim_y, y_flip);
132cc1dc7a3Sopenharmony_ci			stbi_image_free(data);
133cc1dc7a3Sopenharmony_ci			is_hdr = true;
134cc1dc7a3Sopenharmony_ci			component_count = 4;
135cc1dc7a3Sopenharmony_ci			return img;
136cc1dc7a3Sopenharmony_ci		}
137cc1dc7a3Sopenharmony_ci	}
138cc1dc7a3Sopenharmony_ci	else
139cc1dc7a3Sopenharmony_ci	{
140cc1dc7a3Sopenharmony_ci		uint8_t* data = stbi_load(filename, &dim_x, &dim_y, nullptr, STBI_rgb_alpha);
141cc1dc7a3Sopenharmony_ci		if (data)
142cc1dc7a3Sopenharmony_ci		{
143cc1dc7a3Sopenharmony_ci			astcenc_image* img = astc_img_from_unorm8x4_array(data, dim_x, dim_y, y_flip);
144cc1dc7a3Sopenharmony_ci			stbi_image_free(data);
145cc1dc7a3Sopenharmony_ci			is_hdr = false;
146cc1dc7a3Sopenharmony_ci			component_count = 4;
147cc1dc7a3Sopenharmony_ci			return img;
148cc1dc7a3Sopenharmony_ci		}
149cc1dc7a3Sopenharmony_ci	}
150cc1dc7a3Sopenharmony_ci
151cc1dc7a3Sopenharmony_ci	print_error("ERROR: Failed to load image %s (%s)\n", filename, stbi_failure_reason());
152cc1dc7a3Sopenharmony_ci	return nullptr;
153cc1dc7a3Sopenharmony_ci}
154cc1dc7a3Sopenharmony_ci
155cc1dc7a3Sopenharmony_ci/**
156cc1dc7a3Sopenharmony_ci * @brief Save an EXR image using TinyExr to provide the store routine.
157cc1dc7a3Sopenharmony_ci *
158cc1dc7a3Sopenharmony_ci * @param img        The source data for the image.
159cc1dc7a3Sopenharmony_ci * @param filename   The name of the file to save.
160cc1dc7a3Sopenharmony_ci * @param y_flip     Should the image be vertically flipped?
161cc1dc7a3Sopenharmony_ci *
162cc1dc7a3Sopenharmony_ci * @return @c true if the image saved OK, @c false on error.
163cc1dc7a3Sopenharmony_ci */
164cc1dc7a3Sopenharmony_cistatic bool store_exr_image_with_tinyexr(
165cc1dc7a3Sopenharmony_ci	const astcenc_image* img,
166cc1dc7a3Sopenharmony_ci	const char* filename,
167cc1dc7a3Sopenharmony_ci	int y_flip
168cc1dc7a3Sopenharmony_ci) {
169cc1dc7a3Sopenharmony_ci	int res { 0 };
170cc1dc7a3Sopenharmony_ci
171cc1dc7a3Sopenharmony_ci	for (unsigned int i = 0; i < img->dim_z; i++)
172cc1dc7a3Sopenharmony_ci	{
173cc1dc7a3Sopenharmony_ci		std::string fnmod = get_output_filename(img, filename, i);
174cc1dc7a3Sopenharmony_ci		float* buf = floatx4_array_from_astc_img(img, y_flip, i);
175cc1dc7a3Sopenharmony_ci
176cc1dc7a3Sopenharmony_ci		res = SaveEXR(buf, img->dim_x, img->dim_y, 4, 1, fnmod.c_str(), nullptr);
177cc1dc7a3Sopenharmony_ci		delete[] buf;
178cc1dc7a3Sopenharmony_ci		if (res < 0)
179cc1dc7a3Sopenharmony_ci		{
180cc1dc7a3Sopenharmony_ci			break;
181cc1dc7a3Sopenharmony_ci		}
182cc1dc7a3Sopenharmony_ci	}
183cc1dc7a3Sopenharmony_ci
184cc1dc7a3Sopenharmony_ci	return res >= 0;
185cc1dc7a3Sopenharmony_ci}
186cc1dc7a3Sopenharmony_ci
187cc1dc7a3Sopenharmony_ci/**
188cc1dc7a3Sopenharmony_ci * @brief Save a PNG image using STBImageWrite to provide the store routine.
189cc1dc7a3Sopenharmony_ci *
190cc1dc7a3Sopenharmony_ci * @param img        The source data for the image.
191cc1dc7a3Sopenharmony_ci * @param filename   The name of the file to save.
192cc1dc7a3Sopenharmony_ci * @param y_flip     Should the image be vertically flipped?
193cc1dc7a3Sopenharmony_ci *
194cc1dc7a3Sopenharmony_ci * @return @c true if the image saved OK, @c false on error.
195cc1dc7a3Sopenharmony_ci */
196cc1dc7a3Sopenharmony_cistatic bool store_png_image_with_stb(
197cc1dc7a3Sopenharmony_ci	const astcenc_image* img,
198cc1dc7a3Sopenharmony_ci	const char* filename,
199cc1dc7a3Sopenharmony_ci	int y_flip
200cc1dc7a3Sopenharmony_ci) {
201cc1dc7a3Sopenharmony_ci	int res { 0 };
202cc1dc7a3Sopenharmony_ci
203cc1dc7a3Sopenharmony_ci	assert(img->data_type == ASTCENC_TYPE_U8);
204cc1dc7a3Sopenharmony_ci
205cc1dc7a3Sopenharmony_ci	for (unsigned int i = 0; i < img->dim_z; i++)
206cc1dc7a3Sopenharmony_ci	{
207cc1dc7a3Sopenharmony_ci		std::string fnmod = get_output_filename(img, filename, i);
208cc1dc7a3Sopenharmony_ci		uint8_t* buf = reinterpret_cast<uint8_t*>(img->data[i]);
209cc1dc7a3Sopenharmony_ci
210cc1dc7a3Sopenharmony_ci		stbi_flip_vertically_on_write(y_flip);
211cc1dc7a3Sopenharmony_ci		res = stbi_write_png(fnmod.c_str(), img->dim_x, img->dim_y, 4, buf, img->dim_x * 4);
212cc1dc7a3Sopenharmony_ci		if (res == 0)
213cc1dc7a3Sopenharmony_ci		{
214cc1dc7a3Sopenharmony_ci			break;
215cc1dc7a3Sopenharmony_ci		}
216cc1dc7a3Sopenharmony_ci	}
217cc1dc7a3Sopenharmony_ci
218cc1dc7a3Sopenharmony_ci	return res != 0;
219cc1dc7a3Sopenharmony_ci}
220cc1dc7a3Sopenharmony_ci
221cc1dc7a3Sopenharmony_ci/**
222cc1dc7a3Sopenharmony_ci * @brief Save a TGA image using STBImageWrite to provide the store routine.
223cc1dc7a3Sopenharmony_ci *
224cc1dc7a3Sopenharmony_ci * @param img        The source data for the image.
225cc1dc7a3Sopenharmony_ci * @param filename   The name of the file to save.
226cc1dc7a3Sopenharmony_ci * @param y_flip     Should the image be vertically flipped?
227cc1dc7a3Sopenharmony_ci *
228cc1dc7a3Sopenharmony_ci * @return @c true if the image saved OK, @c false on error.
229cc1dc7a3Sopenharmony_ci */
230cc1dc7a3Sopenharmony_cistatic bool store_tga_image_with_stb(
231cc1dc7a3Sopenharmony_ci	const astcenc_image* img,
232cc1dc7a3Sopenharmony_ci	const char* filename,
233cc1dc7a3Sopenharmony_ci	int y_flip
234cc1dc7a3Sopenharmony_ci) {
235cc1dc7a3Sopenharmony_ci	int res { 0 };
236cc1dc7a3Sopenharmony_ci
237cc1dc7a3Sopenharmony_ci	assert(img->data_type == ASTCENC_TYPE_U8);
238cc1dc7a3Sopenharmony_ci
239cc1dc7a3Sopenharmony_ci	for (unsigned int i = 0; i < img->dim_z; i++)
240cc1dc7a3Sopenharmony_ci	{
241cc1dc7a3Sopenharmony_ci		std::string fnmod = get_output_filename(img, filename, i);
242cc1dc7a3Sopenharmony_ci		uint8_t* buf = reinterpret_cast<uint8_t*>(img->data[i]);
243cc1dc7a3Sopenharmony_ci
244cc1dc7a3Sopenharmony_ci		stbi_flip_vertically_on_write(y_flip);
245cc1dc7a3Sopenharmony_ci		res = stbi_write_tga(fnmod.c_str(), img->dim_x, img->dim_y, 4, buf);
246cc1dc7a3Sopenharmony_ci		if (res == 0)
247cc1dc7a3Sopenharmony_ci		{
248cc1dc7a3Sopenharmony_ci			break;
249cc1dc7a3Sopenharmony_ci		}
250cc1dc7a3Sopenharmony_ci	}
251cc1dc7a3Sopenharmony_ci
252cc1dc7a3Sopenharmony_ci	return res != 0;
253cc1dc7a3Sopenharmony_ci}
254cc1dc7a3Sopenharmony_ci
255cc1dc7a3Sopenharmony_ci/**
256cc1dc7a3Sopenharmony_ci * @brief Save a BMP image using STBImageWrite to provide the store routine.
257cc1dc7a3Sopenharmony_ci *
258cc1dc7a3Sopenharmony_ci * @param img        The source data for the image.
259cc1dc7a3Sopenharmony_ci * @param filename   The name of the file to save.
260cc1dc7a3Sopenharmony_ci * @param y_flip     Should the image be vertically flipped?
261cc1dc7a3Sopenharmony_ci *
262cc1dc7a3Sopenharmony_ci * @return @c true if the image saved OK, @c false on error.
263cc1dc7a3Sopenharmony_ci */
264cc1dc7a3Sopenharmony_cistatic bool store_bmp_image_with_stb(
265cc1dc7a3Sopenharmony_ci	const astcenc_image* img,
266cc1dc7a3Sopenharmony_ci	const char* filename,
267cc1dc7a3Sopenharmony_ci	int y_flip
268cc1dc7a3Sopenharmony_ci) {
269cc1dc7a3Sopenharmony_ci	int res { 0 };
270cc1dc7a3Sopenharmony_ci
271cc1dc7a3Sopenharmony_ci	assert(img->data_type == ASTCENC_TYPE_U8);
272cc1dc7a3Sopenharmony_ci
273cc1dc7a3Sopenharmony_ci	for (unsigned int i = 0; i < img->dim_z; i++)
274cc1dc7a3Sopenharmony_ci	{
275cc1dc7a3Sopenharmony_ci		std::string fnmod = get_output_filename(img, filename, i);
276cc1dc7a3Sopenharmony_ci		uint8_t* buf = reinterpret_cast<uint8_t*>(img->data[i]);
277cc1dc7a3Sopenharmony_ci
278cc1dc7a3Sopenharmony_ci		stbi_flip_vertically_on_write(y_flip);
279cc1dc7a3Sopenharmony_ci		res = stbi_write_bmp(fnmod.c_str(), img->dim_x, img->dim_y, 4, buf);
280cc1dc7a3Sopenharmony_ci		if (res == 0)
281cc1dc7a3Sopenharmony_ci		{
282cc1dc7a3Sopenharmony_ci			break;
283cc1dc7a3Sopenharmony_ci		}
284cc1dc7a3Sopenharmony_ci	}
285cc1dc7a3Sopenharmony_ci
286cc1dc7a3Sopenharmony_ci	return res != 0;
287cc1dc7a3Sopenharmony_ci}
288cc1dc7a3Sopenharmony_ci
289cc1dc7a3Sopenharmony_ci/**
290cc1dc7a3Sopenharmony_ci * @brief Save a HDR image using STBImageWrite to provide the store routine.
291cc1dc7a3Sopenharmony_ci *
292cc1dc7a3Sopenharmony_ci * @param img        The source data for the image.
293cc1dc7a3Sopenharmony_ci * @param filename   The name of the file to save.
294cc1dc7a3Sopenharmony_ci * @param y_flip     Should the image be vertically flipped?
295cc1dc7a3Sopenharmony_ci *
296cc1dc7a3Sopenharmony_ci * @return @c true if the image saved OK, @c false on error.
297cc1dc7a3Sopenharmony_ci */
298cc1dc7a3Sopenharmony_cistatic bool store_hdr_image_with_stb(
299cc1dc7a3Sopenharmony_ci	const astcenc_image* img,
300cc1dc7a3Sopenharmony_ci	const char* filename,
301cc1dc7a3Sopenharmony_ci	int y_flip
302cc1dc7a3Sopenharmony_ci) {
303cc1dc7a3Sopenharmony_ci	int res { 0 };
304cc1dc7a3Sopenharmony_ci
305cc1dc7a3Sopenharmony_ci	for (unsigned int i = 0; i < img->dim_z; i++)
306cc1dc7a3Sopenharmony_ci	{
307cc1dc7a3Sopenharmony_ci		std::string fnmod = get_output_filename(img, filename, i);
308cc1dc7a3Sopenharmony_ci		float* buf = floatx4_array_from_astc_img(img, y_flip, i);
309cc1dc7a3Sopenharmony_ci
310cc1dc7a3Sopenharmony_ci		res = stbi_write_hdr(fnmod.c_str(), img->dim_x, img->dim_y, 4, buf);
311cc1dc7a3Sopenharmony_ci		delete[] buf;
312cc1dc7a3Sopenharmony_ci		if (res == 0)
313cc1dc7a3Sopenharmony_ci		{
314cc1dc7a3Sopenharmony_ci			break;
315cc1dc7a3Sopenharmony_ci		}
316cc1dc7a3Sopenharmony_ci	}
317cc1dc7a3Sopenharmony_ci
318cc1dc7a3Sopenharmony_ci	return res != 0;
319cc1dc7a3Sopenharmony_ci}
320cc1dc7a3Sopenharmony_ci
321cc1dc7a3Sopenharmony_ci/* ============================================================================
322cc1dc7a3Sopenharmony_ciNative Load and store of KTX and DDS file formats.
323cc1dc7a3Sopenharmony_ci
324cc1dc7a3Sopenharmony_ciUnlike "regular" 2D image formats, which are mostly supported through stb_image
325cc1dc7a3Sopenharmony_ciand tinyexr, these formats are supported directly; this involves a relatively
326cc1dc7a3Sopenharmony_cilarge number of pixel formats.
327cc1dc7a3Sopenharmony_ci
328cc1dc7a3Sopenharmony_ciThe following restrictions apply to loading of these file formats:
329cc1dc7a3Sopenharmony_ci
330cc1dc7a3Sopenharmony_ci    * Only uncompressed data supported
331cc1dc7a3Sopenharmony_ci    * Only first mipmap in mipmap pyramid supported
332cc1dc7a3Sopenharmony_ci    * KTX: Cube-map arrays are not supported
333cc1dc7a3Sopenharmony_ci============================================================================ */
334cc1dc7a3Sopenharmony_cienum scanline_transfer
335cc1dc7a3Sopenharmony_ci{
336cc1dc7a3Sopenharmony_ci	R8_TO_RGBA8,
337cc1dc7a3Sopenharmony_ci	RG8_TO_RGBA8,
338cc1dc7a3Sopenharmony_ci	RGB8_TO_RGBA8,
339cc1dc7a3Sopenharmony_ci	RGBA8_TO_RGBA8,
340cc1dc7a3Sopenharmony_ci	BGR8_TO_RGBA8,
341cc1dc7a3Sopenharmony_ci	BGRA8_TO_RGBA8,
342cc1dc7a3Sopenharmony_ci	L8_TO_RGBA8,
343cc1dc7a3Sopenharmony_ci	LA8_TO_RGBA8,
344cc1dc7a3Sopenharmony_ci
345cc1dc7a3Sopenharmony_ci	RGBX8_TO_RGBA8,
346cc1dc7a3Sopenharmony_ci	BGRX8_TO_RGBA8,
347cc1dc7a3Sopenharmony_ci
348cc1dc7a3Sopenharmony_ci	R16_TO_RGBA16F,
349cc1dc7a3Sopenharmony_ci	RG16_TO_RGBA16F,
350cc1dc7a3Sopenharmony_ci	RGB16_TO_RGBA16F,
351cc1dc7a3Sopenharmony_ci	RGBA16_TO_RGBA16F,
352cc1dc7a3Sopenharmony_ci	BGR16_TO_RGBA16F,
353cc1dc7a3Sopenharmony_ci	BGRA16_TO_RGBA16F,
354cc1dc7a3Sopenharmony_ci	L16_TO_RGBA16F,
355cc1dc7a3Sopenharmony_ci	LA16_TO_RGBA16F,
356cc1dc7a3Sopenharmony_ci
357cc1dc7a3Sopenharmony_ci	R16F_TO_RGBA16F,
358cc1dc7a3Sopenharmony_ci	RG16F_TO_RGBA16F,
359cc1dc7a3Sopenharmony_ci	RGB16F_TO_RGBA16F,
360cc1dc7a3Sopenharmony_ci	RGBA16F_TO_RGBA16F,
361cc1dc7a3Sopenharmony_ci	BGR16F_TO_RGBA16F,
362cc1dc7a3Sopenharmony_ci	BGRA16F_TO_RGBA16F,
363cc1dc7a3Sopenharmony_ci	L16F_TO_RGBA16F,
364cc1dc7a3Sopenharmony_ci	LA16F_TO_RGBA16F,
365cc1dc7a3Sopenharmony_ci
366cc1dc7a3Sopenharmony_ci	R32F_TO_RGBA16F,
367cc1dc7a3Sopenharmony_ci	RG32F_TO_RGBA16F,
368cc1dc7a3Sopenharmony_ci	RGB32F_TO_RGBA16F,
369cc1dc7a3Sopenharmony_ci	RGBA32F_TO_RGBA16F,
370cc1dc7a3Sopenharmony_ci	BGR32F_TO_RGBA16F,
371cc1dc7a3Sopenharmony_ci	BGRA32F_TO_RGBA16F,
372cc1dc7a3Sopenharmony_ci	L32F_TO_RGBA16F,
373cc1dc7a3Sopenharmony_ci	LA32F_TO_RGBA16F
374cc1dc7a3Sopenharmony_ci};
375cc1dc7a3Sopenharmony_ci
376cc1dc7a3Sopenharmony_ci/**
377cc1dc7a3Sopenharmony_ci * @brief Copy a scanline from a source file and expand to a canonical format.
378cc1dc7a3Sopenharmony_ci *
379cc1dc7a3Sopenharmony_ci * Outputs are always 4 component RGBA, stored as U8 (LDR) or FP16 (HDR).
380cc1dc7a3Sopenharmony_ci *
381cc1dc7a3Sopenharmony_ci * @param[out] dst           The start of the line to store to.
382cc1dc7a3Sopenharmony_ci * @param      src           The start of the line to load.
383cc1dc7a3Sopenharmony_ci * @param      pixel_count   The number of pixels in the scanline.
384cc1dc7a3Sopenharmony_ci * @param      method        The conversion function.
385cc1dc7a3Sopenharmony_ci */
386cc1dc7a3Sopenharmony_cistatic void copy_scanline(
387cc1dc7a3Sopenharmony_ci	void* dst,
388cc1dc7a3Sopenharmony_ci	const void* src,
389cc1dc7a3Sopenharmony_ci	int pixel_count,
390cc1dc7a3Sopenharmony_ci	scanline_transfer method
391cc1dc7a3Sopenharmony_ci) {
392cc1dc7a3Sopenharmony_ci
393cc1dc7a3Sopenharmony_ci#define id(x) (x)
394cc1dc7a3Sopenharmony_ci#define u16_sf16(x) float_to_float16(x * (1.0f/65535.0f))
395cc1dc7a3Sopenharmony_ci#define f32_sf16(x) float_to_float16(x)
396cc1dc7a3Sopenharmony_ci
397cc1dc7a3Sopenharmony_ci#define COPY_R(dsttype, srctype, convfunc, oneval) \
398cc1dc7a3Sopenharmony_ci	do { \
399cc1dc7a3Sopenharmony_ci		const srctype* s = reinterpret_cast<const srctype*>(src); \
400cc1dc7a3Sopenharmony_ci		dsttype* d = reinterpret_cast<dsttype*>(dst); \
401cc1dc7a3Sopenharmony_ci		for (int i = 0; i < pixel_count; i++) \
402cc1dc7a3Sopenharmony_ci		{ \
403cc1dc7a3Sopenharmony_ci			d[4 * i    ] = convfunc(s[i]); \
404cc1dc7a3Sopenharmony_ci			d[4 * i + 1] = 0;              \
405cc1dc7a3Sopenharmony_ci			d[4 * i + 2] = 0;              \
406cc1dc7a3Sopenharmony_ci			d[4 * i + 3] = oneval;         \
407cc1dc7a3Sopenharmony_ci		} \
408cc1dc7a3Sopenharmony_ci	} while (0); \
409cc1dc7a3Sopenharmony_ci	break
410cc1dc7a3Sopenharmony_ci
411cc1dc7a3Sopenharmony_ci#define COPY_RG(dsttype, srctype, convfunc, oneval) \
412cc1dc7a3Sopenharmony_ci	do { \
413cc1dc7a3Sopenharmony_ci		const srctype* s = reinterpret_cast<const srctype*>(src); \
414cc1dc7a3Sopenharmony_ci		dsttype* d = reinterpret_cast<dsttype*>(dst); \
415cc1dc7a3Sopenharmony_ci		for (int i = 0; i < pixel_count; i++) \
416cc1dc7a3Sopenharmony_ci		{ \
417cc1dc7a3Sopenharmony_ci			d[4 * i    ] = convfunc(s[2 * i    ]); \
418cc1dc7a3Sopenharmony_ci			d[4 * i + 1] = convfunc(s[2 * i + 1]); \
419cc1dc7a3Sopenharmony_ci			d[4 * i + 2] = 0;                      \
420cc1dc7a3Sopenharmony_ci			d[4 * i + 3] = oneval;                 \
421cc1dc7a3Sopenharmony_ci		} \
422cc1dc7a3Sopenharmony_ci	} while (0); \
423cc1dc7a3Sopenharmony_ci	break
424cc1dc7a3Sopenharmony_ci
425cc1dc7a3Sopenharmony_ci#define COPY_RGB(dsttype, srctype, convfunc, oneval) \
426cc1dc7a3Sopenharmony_ci	do { \
427cc1dc7a3Sopenharmony_ci		const srctype* s = reinterpret_cast<const srctype*>(src); \
428cc1dc7a3Sopenharmony_ci		dsttype* d = reinterpret_cast<dsttype*>(dst); \
429cc1dc7a3Sopenharmony_ci		for (int i = 0; i < pixel_count; i++) \
430cc1dc7a3Sopenharmony_ci		{ \
431cc1dc7a3Sopenharmony_ci			d[4 * i    ] = convfunc(s[3 * i    ]); \
432cc1dc7a3Sopenharmony_ci			d[4 * i + 1] = convfunc(s[3 * i + 1]); \
433cc1dc7a3Sopenharmony_ci			d[4 * i + 2] = convfunc(s[3 * i + 2]); \
434cc1dc7a3Sopenharmony_ci			d[4 * i + 3] = oneval;                 \
435cc1dc7a3Sopenharmony_ci		} \
436cc1dc7a3Sopenharmony_ci	} while (0); \
437cc1dc7a3Sopenharmony_ci	break
438cc1dc7a3Sopenharmony_ci
439cc1dc7a3Sopenharmony_ci#define COPY_BGR(dsttype, srctype, convfunc, oneval) \
440cc1dc7a3Sopenharmony_ci	do { \
441cc1dc7a3Sopenharmony_ci		const srctype* s = reinterpret_cast<const srctype*>(src); \
442cc1dc7a3Sopenharmony_ci		dsttype* d = reinterpret_cast<dsttype*>(dst); \
443cc1dc7a3Sopenharmony_ci		for (int i = 0; i < pixel_count; i++)\
444cc1dc7a3Sopenharmony_ci		{ \
445cc1dc7a3Sopenharmony_ci			d[4 * i    ] = convfunc(s[3 * i + 2]); \
446cc1dc7a3Sopenharmony_ci			d[4 * i + 1] = convfunc(s[3 * i + 1]); \
447cc1dc7a3Sopenharmony_ci			d[4 * i + 2] = convfunc(s[3 * i    ]); \
448cc1dc7a3Sopenharmony_ci			d[4 * i + 3] = oneval;                 \
449cc1dc7a3Sopenharmony_ci		} \
450cc1dc7a3Sopenharmony_ci	} while (0); \
451cc1dc7a3Sopenharmony_ci	break
452cc1dc7a3Sopenharmony_ci
453cc1dc7a3Sopenharmony_ci#define COPY_RGBX(dsttype, srctype, convfunc, oneval) \
454cc1dc7a3Sopenharmony_ci	do { \
455cc1dc7a3Sopenharmony_ci		const srctype* s = reinterpret_cast<const srctype*>(src); \
456cc1dc7a3Sopenharmony_ci		dsttype* d = reinterpret_cast<dsttype*>(dst); \
457cc1dc7a3Sopenharmony_ci		for (int i = 0; i < pixel_count; i++)\
458cc1dc7a3Sopenharmony_ci		{ \
459cc1dc7a3Sopenharmony_ci			d[4 * i    ] = convfunc(s[4 * i    ]); \
460cc1dc7a3Sopenharmony_ci			d[4 * i + 1] = convfunc(s[4 * i + 1]); \
461cc1dc7a3Sopenharmony_ci			d[4 * i + 2] = convfunc(s[4 * i + 2]); \
462cc1dc7a3Sopenharmony_ci			d[4 * i + 3] = oneval;                 \
463cc1dc7a3Sopenharmony_ci		} \
464cc1dc7a3Sopenharmony_ci	} while (0); \
465cc1dc7a3Sopenharmony_ci	break
466cc1dc7a3Sopenharmony_ci
467cc1dc7a3Sopenharmony_ci#define COPY_BGRX(dsttype, srctype, convfunc, oneval) \
468cc1dc7a3Sopenharmony_ci	do { \
469cc1dc7a3Sopenharmony_ci		const srctype* s = reinterpret_cast<const srctype*>(src); \
470cc1dc7a3Sopenharmony_ci		dsttype* d = reinterpret_cast<dsttype*>(dst); \
471cc1dc7a3Sopenharmony_ci		for (int i = 0; i < pixel_count; i++)\
472cc1dc7a3Sopenharmony_ci		{ \
473cc1dc7a3Sopenharmony_ci			d[4 * i    ] = convfunc(s[4 * i + 2]); \
474cc1dc7a3Sopenharmony_ci			d[4 * i + 1] = convfunc(s[4 * i + 1]); \
475cc1dc7a3Sopenharmony_ci			d[4 * i + 2] = convfunc(s[4 * i    ]); \
476cc1dc7a3Sopenharmony_ci			d[4 * i + 3] = oneval;                 \
477cc1dc7a3Sopenharmony_ci		} \
478cc1dc7a3Sopenharmony_ci	} while (0); \
479cc1dc7a3Sopenharmony_ci	break
480cc1dc7a3Sopenharmony_ci
481cc1dc7a3Sopenharmony_ci#define COPY_RGBA(dsttype, srctype, convfunc, oneval) \
482cc1dc7a3Sopenharmony_ci	do { \
483cc1dc7a3Sopenharmony_ci		const srctype* s = reinterpret_cast<const srctype*>(src); \
484cc1dc7a3Sopenharmony_ci		dsttype* d = reinterpret_cast<dsttype*>(dst); \
485cc1dc7a3Sopenharmony_ci		for (int i = 0; i < pixel_count; i++) \
486cc1dc7a3Sopenharmony_ci		{ \
487cc1dc7a3Sopenharmony_ci			d[4 * i    ] = convfunc(s[4 * i    ]); \
488cc1dc7a3Sopenharmony_ci			d[4 * i + 1] = convfunc(s[4 * i + 1]); \
489cc1dc7a3Sopenharmony_ci			d[4 * i + 2] = convfunc(s[4 * i + 2]); \
490cc1dc7a3Sopenharmony_ci			d[4 * i + 3] = convfunc(s[4 * i + 3]); \
491cc1dc7a3Sopenharmony_ci		} \
492cc1dc7a3Sopenharmony_ci	} while (0); \
493cc1dc7a3Sopenharmony_ci	break
494cc1dc7a3Sopenharmony_ci
495cc1dc7a3Sopenharmony_ci#define COPY_BGRA(dsttype, srctype, convfunc, oneval) \
496cc1dc7a3Sopenharmony_ci	do { \
497cc1dc7a3Sopenharmony_ci		const srctype* s = reinterpret_cast<const srctype*>(src); \
498cc1dc7a3Sopenharmony_ci		dsttype* d = reinterpret_cast<dsttype*>(dst); \
499cc1dc7a3Sopenharmony_ci		for (int i = 0; i < pixel_count; i++) \
500cc1dc7a3Sopenharmony_ci		{ \
501cc1dc7a3Sopenharmony_ci			d[4 * i    ] = convfunc(s[4 * i + 2]); \
502cc1dc7a3Sopenharmony_ci			d[4 * i + 1] = convfunc(s[4 * i + 1]); \
503cc1dc7a3Sopenharmony_ci			d[4 * i + 2] = convfunc(s[4 * i    ]); \
504cc1dc7a3Sopenharmony_ci			d[4 * i + 3] = convfunc(s[4 * i + 3]); \
505cc1dc7a3Sopenharmony_ci		} \
506cc1dc7a3Sopenharmony_ci	} while (0); \
507cc1dc7a3Sopenharmony_ci	break
508cc1dc7a3Sopenharmony_ci
509cc1dc7a3Sopenharmony_ci#define COPY_L(dsttype, srctype, convfunc, oneval) \
510cc1dc7a3Sopenharmony_ci	do { \
511cc1dc7a3Sopenharmony_ci		const srctype* s = reinterpret_cast<const srctype*>(src); \
512cc1dc7a3Sopenharmony_ci		dsttype* d = reinterpret_cast<dsttype*>(dst); \
513cc1dc7a3Sopenharmony_ci		for (int i = 0; i < pixel_count; i++) \
514cc1dc7a3Sopenharmony_ci		{ \
515cc1dc7a3Sopenharmony_ci			d[4 * i    ] = convfunc(s[i]); \
516cc1dc7a3Sopenharmony_ci			d[4 * i + 1] = convfunc(s[i]); \
517cc1dc7a3Sopenharmony_ci			d[4 * i + 2] = convfunc(s[i]); \
518cc1dc7a3Sopenharmony_ci			d[4 * i + 3] = oneval;         \
519cc1dc7a3Sopenharmony_ci		} \
520cc1dc7a3Sopenharmony_ci	} while (0); \
521cc1dc7a3Sopenharmony_ci	break
522cc1dc7a3Sopenharmony_ci
523cc1dc7a3Sopenharmony_ci#define COPY_LA(dsttype, srctype, convfunc, oneval) \
524cc1dc7a3Sopenharmony_ci	do { \
525cc1dc7a3Sopenharmony_ci		const srctype* s = reinterpret_cast<const srctype*>(src); \
526cc1dc7a3Sopenharmony_ci		dsttype* d = reinterpret_cast<dsttype*>(dst); \
527cc1dc7a3Sopenharmony_ci		for (int i = 0; i < pixel_count; i++) \
528cc1dc7a3Sopenharmony_ci		{ \
529cc1dc7a3Sopenharmony_ci			d[4 * i    ] = convfunc(s[2 * i    ]); \
530cc1dc7a3Sopenharmony_ci			d[4 * i + 1] = convfunc(s[2 * i    ]); \
531cc1dc7a3Sopenharmony_ci			d[4 * i + 2] = convfunc(s[2 * i    ]); \
532cc1dc7a3Sopenharmony_ci			d[4 * i + 3] = convfunc(s[2 * i + 1]); \
533cc1dc7a3Sopenharmony_ci		} \
534cc1dc7a3Sopenharmony_ci	} while (0); \
535cc1dc7a3Sopenharmony_ci	break
536cc1dc7a3Sopenharmony_ci
537cc1dc7a3Sopenharmony_ci	switch (method)
538cc1dc7a3Sopenharmony_ci	{
539cc1dc7a3Sopenharmony_ci	case R8_TO_RGBA8:
540cc1dc7a3Sopenharmony_ci		COPY_R(uint8_t, uint8_t, id, 0xFF);
541cc1dc7a3Sopenharmony_ci	case RG8_TO_RGBA8:
542cc1dc7a3Sopenharmony_ci		COPY_RG(uint8_t, uint8_t, id, 0xFF);
543cc1dc7a3Sopenharmony_ci	case RGB8_TO_RGBA8:
544cc1dc7a3Sopenharmony_ci		COPY_RGB(uint8_t, uint8_t, id, 0xFF);
545cc1dc7a3Sopenharmony_ci	case RGBA8_TO_RGBA8:
546cc1dc7a3Sopenharmony_ci		COPY_RGBA(uint8_t, uint8_t, id, 0xFF);
547cc1dc7a3Sopenharmony_ci	case BGR8_TO_RGBA8:
548cc1dc7a3Sopenharmony_ci		COPY_BGR(uint8_t, uint8_t, id, 0xFF);
549cc1dc7a3Sopenharmony_ci	case BGRA8_TO_RGBA8:
550cc1dc7a3Sopenharmony_ci		COPY_BGRA(uint8_t, uint8_t, id, 0xFF);
551cc1dc7a3Sopenharmony_ci	case RGBX8_TO_RGBA8:
552cc1dc7a3Sopenharmony_ci		COPY_RGBX(uint8_t, uint8_t, id, 0xFF);
553cc1dc7a3Sopenharmony_ci	case BGRX8_TO_RGBA8:
554cc1dc7a3Sopenharmony_ci		COPY_BGRX(uint8_t, uint8_t, id, 0xFF);
555cc1dc7a3Sopenharmony_ci	case L8_TO_RGBA8:
556cc1dc7a3Sopenharmony_ci		COPY_L(uint8_t, uint8_t, id, 0xFF);
557cc1dc7a3Sopenharmony_ci	case LA8_TO_RGBA8:
558cc1dc7a3Sopenharmony_ci		COPY_LA(uint8_t, uint8_t, id, 0xFF);
559cc1dc7a3Sopenharmony_ci
560cc1dc7a3Sopenharmony_ci	case R16F_TO_RGBA16F:
561cc1dc7a3Sopenharmony_ci		COPY_R(uint16_t, uint16_t, id, 0x3C00);
562cc1dc7a3Sopenharmony_ci	case RG16F_TO_RGBA16F:
563cc1dc7a3Sopenharmony_ci		COPY_RG(uint16_t, uint16_t, id, 0x3C00);
564cc1dc7a3Sopenharmony_ci	case RGB16F_TO_RGBA16F:
565cc1dc7a3Sopenharmony_ci		COPY_RGB(uint16_t, uint16_t, id, 0x3C00);
566cc1dc7a3Sopenharmony_ci	case RGBA16F_TO_RGBA16F:
567cc1dc7a3Sopenharmony_ci		COPY_RGBA(uint16_t, uint16_t, id, 0x3C00);
568cc1dc7a3Sopenharmony_ci	case BGR16F_TO_RGBA16F:
569cc1dc7a3Sopenharmony_ci		COPY_BGR(uint16_t, uint16_t, id, 0x3C00);
570cc1dc7a3Sopenharmony_ci	case BGRA16F_TO_RGBA16F:
571cc1dc7a3Sopenharmony_ci		COPY_BGRA(uint16_t, uint16_t, id, 0x3C00);
572cc1dc7a3Sopenharmony_ci	case L16F_TO_RGBA16F:
573cc1dc7a3Sopenharmony_ci		COPY_L(uint16_t, uint16_t, id, 0x3C00);
574cc1dc7a3Sopenharmony_ci	case LA16F_TO_RGBA16F:
575cc1dc7a3Sopenharmony_ci		COPY_LA(uint16_t, uint16_t, id, 0x3C00);
576cc1dc7a3Sopenharmony_ci
577cc1dc7a3Sopenharmony_ci	case R16_TO_RGBA16F:
578cc1dc7a3Sopenharmony_ci		COPY_R(uint16_t, uint16_t, u16_sf16, 0x3C00);
579cc1dc7a3Sopenharmony_ci	case RG16_TO_RGBA16F:
580cc1dc7a3Sopenharmony_ci		COPY_RG(uint16_t, uint16_t, u16_sf16, 0x3C00);
581cc1dc7a3Sopenharmony_ci	case RGB16_TO_RGBA16F:
582cc1dc7a3Sopenharmony_ci		COPY_RGB(uint16_t, uint16_t, u16_sf16, 0x3C00);
583cc1dc7a3Sopenharmony_ci	case RGBA16_TO_RGBA16F:
584cc1dc7a3Sopenharmony_ci		COPY_RGBA(uint16_t, uint16_t, u16_sf16, 0x3C00);
585cc1dc7a3Sopenharmony_ci	case BGR16_TO_RGBA16F:
586cc1dc7a3Sopenharmony_ci		COPY_BGR(uint16_t, uint16_t, u16_sf16, 0x3C00);
587cc1dc7a3Sopenharmony_ci	case BGRA16_TO_RGBA16F:
588cc1dc7a3Sopenharmony_ci		COPY_BGRA(uint16_t, uint16_t, u16_sf16, 0x3C00);
589cc1dc7a3Sopenharmony_ci	case L16_TO_RGBA16F:
590cc1dc7a3Sopenharmony_ci		COPY_L(uint16_t, uint16_t, u16_sf16, 0x3C00);
591cc1dc7a3Sopenharmony_ci	case LA16_TO_RGBA16F:
592cc1dc7a3Sopenharmony_ci		COPY_LA(uint16_t, uint16_t, u16_sf16, 0x3C00);
593cc1dc7a3Sopenharmony_ci
594cc1dc7a3Sopenharmony_ci	case R32F_TO_RGBA16F:
595cc1dc7a3Sopenharmony_ci		COPY_R(uint16_t, float, f32_sf16, 0x3C00);
596cc1dc7a3Sopenharmony_ci	case RG32F_TO_RGBA16F:
597cc1dc7a3Sopenharmony_ci		COPY_RG(uint16_t, float, f32_sf16, 0x3C00);
598cc1dc7a3Sopenharmony_ci	case RGB32F_TO_RGBA16F:
599cc1dc7a3Sopenharmony_ci		COPY_RGB(uint16_t, float, f32_sf16, 0x3C00);
600cc1dc7a3Sopenharmony_ci	case RGBA32F_TO_RGBA16F:
601cc1dc7a3Sopenharmony_ci		COPY_RGBA(uint16_t, float, f32_sf16, 0x3C00);
602cc1dc7a3Sopenharmony_ci	case BGR32F_TO_RGBA16F:
603cc1dc7a3Sopenharmony_ci		COPY_BGR(uint16_t, float, f32_sf16, 0x3C00);
604cc1dc7a3Sopenharmony_ci	case BGRA32F_TO_RGBA16F:
605cc1dc7a3Sopenharmony_ci		COPY_BGRA(uint16_t, float, f32_sf16, 0x3C00);
606cc1dc7a3Sopenharmony_ci	case L32F_TO_RGBA16F:
607cc1dc7a3Sopenharmony_ci		COPY_L(uint16_t, float, f32_sf16, 0x3C00);
608cc1dc7a3Sopenharmony_ci	case LA32F_TO_RGBA16F:
609cc1dc7a3Sopenharmony_ci		COPY_LA(uint16_t, float, f32_sf16, 0x3C00);
610cc1dc7a3Sopenharmony_ci	}
611cc1dc7a3Sopenharmony_ci}
612cc1dc7a3Sopenharmony_ci
613cc1dc7a3Sopenharmony_ci/**
614cc1dc7a3Sopenharmony_ci * @brief Swap endianness of N two byte values.
615cc1dc7a3Sopenharmony_ci *
616cc1dc7a3Sopenharmony_ci * @param[in,out] dataptr      The data to convert.
617cc1dc7a3Sopenharmony_ci * @param         byte_count   The number of bytes to convert.
618cc1dc7a3Sopenharmony_ci */
619cc1dc7a3Sopenharmony_cistatic void switch_endianness2(
620cc1dc7a3Sopenharmony_ci	void* dataptr,
621cc1dc7a3Sopenharmony_ci	int byte_count
622cc1dc7a3Sopenharmony_ci) {
623cc1dc7a3Sopenharmony_ci	uint8_t* data = reinterpret_cast<uint8_t*>(dataptr);
624cc1dc7a3Sopenharmony_ci	for (int i = 0; i < byte_count / 2; i++)
625cc1dc7a3Sopenharmony_ci	{
626cc1dc7a3Sopenharmony_ci		uint8_t d0 = data[0];
627cc1dc7a3Sopenharmony_ci		uint8_t d1 = data[1];
628cc1dc7a3Sopenharmony_ci		data[0] = d1;
629cc1dc7a3Sopenharmony_ci		data[1] = d0;
630cc1dc7a3Sopenharmony_ci		data += 2;
631cc1dc7a3Sopenharmony_ci	}
632cc1dc7a3Sopenharmony_ci}
633cc1dc7a3Sopenharmony_ci
634cc1dc7a3Sopenharmony_ci/**
635cc1dc7a3Sopenharmony_ci * @brief Swap endianness of N four byte values.
636cc1dc7a3Sopenharmony_ci *
637cc1dc7a3Sopenharmony_ci * @param[in,out] dataptr      The data to convert.
638cc1dc7a3Sopenharmony_ci * @param         byte_count   The number of bytes to convert.
639cc1dc7a3Sopenharmony_ci */
640cc1dc7a3Sopenharmony_cistatic void switch_endianness4(
641cc1dc7a3Sopenharmony_ci	void* dataptr,
642cc1dc7a3Sopenharmony_ci	int byte_count
643cc1dc7a3Sopenharmony_ci) {
644cc1dc7a3Sopenharmony_ci	uint8_t* data = reinterpret_cast<uint8_t*>(dataptr);
645cc1dc7a3Sopenharmony_ci	for (int i = 0; i < byte_count / 4; i++)
646cc1dc7a3Sopenharmony_ci	{
647cc1dc7a3Sopenharmony_ci		uint8_t d0 = data[0];
648cc1dc7a3Sopenharmony_ci		uint8_t d1 = data[1];
649cc1dc7a3Sopenharmony_ci		uint8_t d2 = data[2];
650cc1dc7a3Sopenharmony_ci		uint8_t d3 = data[3];
651cc1dc7a3Sopenharmony_ci		data[0] = d3;
652cc1dc7a3Sopenharmony_ci		data[1] = d2;
653cc1dc7a3Sopenharmony_ci		data[2] = d1;
654cc1dc7a3Sopenharmony_ci		data[3] = d0;
655cc1dc7a3Sopenharmony_ci		data += 4;
656cc1dc7a3Sopenharmony_ci	}
657cc1dc7a3Sopenharmony_ci}
658cc1dc7a3Sopenharmony_ci
659cc1dc7a3Sopenharmony_ci/**
660cc1dc7a3Sopenharmony_ci * @brief Swap endianness of a u32 value.
661cc1dc7a3Sopenharmony_ci *
662cc1dc7a3Sopenharmony_ci * @param v   The data to convert.
663cc1dc7a3Sopenharmony_ci *
664cc1dc7a3Sopenharmony_ci * @return The converted value.
665cc1dc7a3Sopenharmony_ci */
666cc1dc7a3Sopenharmony_cistatic uint32_t u32_byterev(uint32_t v)
667cc1dc7a3Sopenharmony_ci{
668cc1dc7a3Sopenharmony_ci	return (v >> 24) | ((v >> 8) & 0xFF00) | ((v << 8) & 0xFF0000) | (v << 24);
669cc1dc7a3Sopenharmony_ci}
670cc1dc7a3Sopenharmony_ci
671cc1dc7a3Sopenharmony_ci/*
672cc1dc7a3Sopenharmony_ci Notes about KTX:
673cc1dc7a3Sopenharmony_ci
674cc1dc7a3Sopenharmony_ci After the header and the key/value data area, the actual image data follows.
675cc1dc7a3Sopenharmony_ci Each image starts with a 4-byte "imageSize" value indicating the number of bytes of image data follow.
676cc1dc7a3Sopenharmony_ci (For cube-maps, this value appears only after first image; the remaining 5 images are all of equal size.)
677cc1dc7a3Sopenharmony_ci If the size of an image is not a multiple of 4, then it is padded to the next multiple of 4.
678cc1dc7a3Sopenharmony_ci Note that this padding is NOT included in the "imageSize" field.
679cc1dc7a3Sopenharmony_ci In a cubemap, the padding appears after each face note that in a 2D/3D texture, padding does
680cc1dc7a3Sopenharmony_ci NOT appear between the lines/planes of the texture!
681cc1dc7a3Sopenharmony_ci
682cc1dc7a3Sopenharmony_ci In a KTX file, there may be multiple images; they are organized as follows:
683cc1dc7a3Sopenharmony_ci
684cc1dc7a3Sopenharmony_ci For each mipmap_level in numberOfMipmapLevels
685cc1dc7a3Sopenharmony_ci 	UInt32 imageSize;
686cc1dc7a3Sopenharmony_ci 	For each array_element in numberOfArrayElements
687cc1dc7a3Sopenharmony_ci 	* for each face in numberOfFaces
688cc1dc7a3Sopenharmony_ci 		* for each z_slice in pixelDepth
689cc1dc7a3Sopenharmony_ci 			* for each row or row_of_blocks in pixelHeight
690cc1dc7a3Sopenharmony_ci 				* for each pixel or block_of_pixels in pixelWidth
691cc1dc7a3Sopenharmony_ci 					Byte data[format-specific-number-of-bytes]
692cc1dc7a3Sopenharmony_ci 				* end
693cc1dc7a3Sopenharmony_ci 			* end
694cc1dc7a3Sopenharmony_ci 		*end
695cc1dc7a3Sopenharmony_ci 		Byte cubePadding[0-3]
696cc1dc7a3Sopenharmony_ci 	*end
697cc1dc7a3Sopenharmony_ci 	Byte mipPadding[3 - ((imageSize+ 3) % 4)]
698cc1dc7a3Sopenharmony_ci *end
699cc1dc7a3Sopenharmony_ci
700cc1dc7a3Sopenharmony_ci In the ASTC codec, we will, for the time being only harvest the first image,
701cc1dc7a3Sopenharmony_ci and we will support only a limited set of formats:
702cc1dc7a3Sopenharmony_ci
703cc1dc7a3Sopenharmony_ci gl_type: UNSIGNED_BYTE UNSIGNED_SHORT HALF_FLOAT FLOAT UNSIGNED_INT_8_8_8_8 UNSIGNED_INT_8_8_8_8_REV
704cc1dc7a3Sopenharmony_ci gl_format: RED, RG. RGB, RGBA BGR, BGRA
705cc1dc7a3Sopenharmony_ci gl_internal_format: used for upload to OpenGL; we can ignore it on uncompressed-load, but
706cc1dc7a3Sopenharmony_ci 	need to provide a reasonable value on store: RGB8 RGBA8 RGB16F RGBA16F
707cc1dc7a3Sopenharmony_ci gl_base_internal_format: same as gl_format unless texture is compressed (well, BGR is turned into RGB)
708cc1dc7a3Sopenharmony_ci 	RED, RG, RGB, RGBA
709cc1dc7a3Sopenharmony_ci*/
710cc1dc7a3Sopenharmony_ci
711cc1dc7a3Sopenharmony_ci// Khronos enums
712cc1dc7a3Sopenharmony_ci#define GL_RED                                      0x1903
713cc1dc7a3Sopenharmony_ci#define GL_RG                                       0x8227
714cc1dc7a3Sopenharmony_ci#define GL_RGB                                      0x1907
715cc1dc7a3Sopenharmony_ci#define GL_RGBA                                     0x1908
716cc1dc7a3Sopenharmony_ci#define GL_BGR                                      0x80E0
717cc1dc7a3Sopenharmony_ci#define GL_BGRA                                     0x80E1
718cc1dc7a3Sopenharmony_ci#define GL_LUMINANCE                                0x1909
719cc1dc7a3Sopenharmony_ci#define GL_LUMINANCE_ALPHA                          0x190A
720cc1dc7a3Sopenharmony_ci
721cc1dc7a3Sopenharmony_ci#define GL_R8                                       0x8229
722cc1dc7a3Sopenharmony_ci#define GL_RG8                                      0x822B
723cc1dc7a3Sopenharmony_ci#define GL_RGB8                                     0x8051
724cc1dc7a3Sopenharmony_ci#define GL_RGBA8                                    0x8058
725cc1dc7a3Sopenharmony_ci
726cc1dc7a3Sopenharmony_ci#define GL_R16F                                     0x822D
727cc1dc7a3Sopenharmony_ci#define GL_RG16F                                    0x822F
728cc1dc7a3Sopenharmony_ci#define GL_RGB16F                                   0x881B
729cc1dc7a3Sopenharmony_ci#define GL_RGBA16F                                  0x881A
730cc1dc7a3Sopenharmony_ci
731cc1dc7a3Sopenharmony_ci#define GL_UNSIGNED_BYTE                            0x1401
732cc1dc7a3Sopenharmony_ci#define GL_UNSIGNED_SHORT                           0x1403
733cc1dc7a3Sopenharmony_ci#define GL_HALF_FLOAT                               0x140B
734cc1dc7a3Sopenharmony_ci#define GL_FLOAT                                    0x1406
735cc1dc7a3Sopenharmony_ci
736cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_RGBA_ASTC_4x4                 0x93B0
737cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_RGBA_ASTC_5x4                 0x93B1
738cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_RGBA_ASTC_5x5                 0x93B2
739cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_RGBA_ASTC_6x5                 0x93B3
740cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_RGBA_ASTC_6x6                 0x93B4
741cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_RGBA_ASTC_8x5                 0x93B5
742cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_RGBA_ASTC_8x6                 0x93B6
743cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_RGBA_ASTC_8x8                 0x93B7
744cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_RGBA_ASTC_10x5                0x93B8
745cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_RGBA_ASTC_10x6                0x93B9
746cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_RGBA_ASTC_10x8                0x93BA
747cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_RGBA_ASTC_10x10               0x93BB
748cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_RGBA_ASTC_12x10               0x93BC
749cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_RGBA_ASTC_12x12               0x93BD
750cc1dc7a3Sopenharmony_ci
751cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4         0x93D0
752cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4         0x93D1
753cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5         0x93D2
754cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5         0x93D3
755cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6         0x93D4
756cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5         0x93D5
757cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6         0x93D6
758cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8         0x93D7
759cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5        0x93D8
760cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6        0x93D9
761cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8        0x93DA
762cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10       0x93DB
763cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10       0x93DC
764cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12       0x93DD
765cc1dc7a3Sopenharmony_ci
766cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_RGBA_ASTC_3x3x3_OES           0x93C0
767cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_RGBA_ASTC_4x3x3_OES           0x93C1
768cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_RGBA_ASTC_4x4x3_OES           0x93C2
769cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_RGBA_ASTC_4x4x4_OES           0x93C3
770cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_RGBA_ASTC_5x4x4_OES           0x93C4
771cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_RGBA_ASTC_5x5x4_OES           0x93C5
772cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_RGBA_ASTC_5x5x5_OES           0x93C6
773cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_RGBA_ASTC_6x5x5_OES           0x93C7
774cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_RGBA_ASTC_6x6x5_OES           0x93C8
775cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_RGBA_ASTC_6x6x6_OES           0x93C9
776cc1dc7a3Sopenharmony_ci
777cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES   0x93E0
778cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES   0x93E1
779cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES   0x93E2
780cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES   0x93E3
781cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES   0x93E4
782cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES   0x93E5
783cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES   0x93E6
784cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES   0x93E7
785cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES   0x93E8
786cc1dc7a3Sopenharmony_ci#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES   0x93E9
787cc1dc7a3Sopenharmony_ci
788cc1dc7a3Sopenharmony_cistruct format_entry
789cc1dc7a3Sopenharmony_ci{
790cc1dc7a3Sopenharmony_ci	unsigned int x;
791cc1dc7a3Sopenharmony_ci	unsigned int y;
792cc1dc7a3Sopenharmony_ci	unsigned int z;
793cc1dc7a3Sopenharmony_ci	bool is_srgb;
794cc1dc7a3Sopenharmony_ci	unsigned int format;
795cc1dc7a3Sopenharmony_ci};
796cc1dc7a3Sopenharmony_ci
797cc1dc7a3Sopenharmony_cistatic const std::array<format_entry, 48> ASTC_FORMATS =
798cc1dc7a3Sopenharmony_ci{{
799cc1dc7a3Sopenharmony_ci	// 2D Linear RGB
800cc1dc7a3Sopenharmony_ci	{ 4,  4,  1, false, GL_COMPRESSED_RGBA_ASTC_4x4},
801cc1dc7a3Sopenharmony_ci	{ 5,  4,  1, false, GL_COMPRESSED_RGBA_ASTC_5x4},
802cc1dc7a3Sopenharmony_ci	{ 5,  5,  1, false, GL_COMPRESSED_RGBA_ASTC_5x5},
803cc1dc7a3Sopenharmony_ci	{ 6,  5,  1, false, GL_COMPRESSED_RGBA_ASTC_6x5},
804cc1dc7a3Sopenharmony_ci	{ 6,  6,  1, false, GL_COMPRESSED_RGBA_ASTC_6x6},
805cc1dc7a3Sopenharmony_ci	{ 8,  5,  1, false, GL_COMPRESSED_RGBA_ASTC_8x5},
806cc1dc7a3Sopenharmony_ci	{ 8,  6,  1, false, GL_COMPRESSED_RGBA_ASTC_8x6},
807cc1dc7a3Sopenharmony_ci	{ 8,  8,  1, false, GL_COMPRESSED_RGBA_ASTC_8x8},
808cc1dc7a3Sopenharmony_ci	{10,  5,  1, false, GL_COMPRESSED_RGBA_ASTC_10x5},
809cc1dc7a3Sopenharmony_ci	{10,  6,  1, false, GL_COMPRESSED_RGBA_ASTC_10x6},
810cc1dc7a3Sopenharmony_ci	{10,  8,  1, false, GL_COMPRESSED_RGBA_ASTC_10x8},
811cc1dc7a3Sopenharmony_ci	{10, 10,  1, false, GL_COMPRESSED_RGBA_ASTC_10x10},
812cc1dc7a3Sopenharmony_ci	{12, 10,  1, false, GL_COMPRESSED_RGBA_ASTC_12x10},
813cc1dc7a3Sopenharmony_ci	{12, 12,  1, false, GL_COMPRESSED_RGBA_ASTC_12x12},
814cc1dc7a3Sopenharmony_ci	// 2D SRGB
815cc1dc7a3Sopenharmony_ci	{ 4,  4,  1,  true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4},
816cc1dc7a3Sopenharmony_ci	{ 5,  4,  1,  true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4},
817cc1dc7a3Sopenharmony_ci	{ 5,  5,  1,  true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5},
818cc1dc7a3Sopenharmony_ci	{ 6,  5,  1,  true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5},
819cc1dc7a3Sopenharmony_ci	{ 6,  6,  1,  true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6},
820cc1dc7a3Sopenharmony_ci	{ 8,  5,  1,  true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5},
821cc1dc7a3Sopenharmony_ci	{ 8,  6,  1,  true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6},
822cc1dc7a3Sopenharmony_ci	{ 8,  8,  1,  true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8},
823cc1dc7a3Sopenharmony_ci	{10,  5,  1,  true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5},
824cc1dc7a3Sopenharmony_ci	{10,  6,  1,  true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6},
825cc1dc7a3Sopenharmony_ci	{10,  8,  1,  true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8},
826cc1dc7a3Sopenharmony_ci	{10, 10,  1,  true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10},
827cc1dc7a3Sopenharmony_ci	{12, 10,  1,  true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10},
828cc1dc7a3Sopenharmony_ci	{12, 12,  1,  true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12},
829cc1dc7a3Sopenharmony_ci	// 3D Linear RGB
830cc1dc7a3Sopenharmony_ci	{ 3,  3,  3, false, GL_COMPRESSED_RGBA_ASTC_3x3x3_OES},
831cc1dc7a3Sopenharmony_ci	{ 4,  3,  3, false, GL_COMPRESSED_RGBA_ASTC_4x3x3_OES},
832cc1dc7a3Sopenharmony_ci	{ 4,  4,  3, false, GL_COMPRESSED_RGBA_ASTC_4x4x3_OES},
833cc1dc7a3Sopenharmony_ci	{ 4,  4,  4, false, GL_COMPRESSED_RGBA_ASTC_4x4x4_OES},
834cc1dc7a3Sopenharmony_ci	{ 5,  4,  4, false, GL_COMPRESSED_RGBA_ASTC_5x4x4_OES},
835cc1dc7a3Sopenharmony_ci	{ 5,  5,  4, false, GL_COMPRESSED_RGBA_ASTC_5x5x4_OES},
836cc1dc7a3Sopenharmony_ci	{ 5,  5,  5, false, GL_COMPRESSED_RGBA_ASTC_5x5x5_OES},
837cc1dc7a3Sopenharmony_ci	{ 6,  5,  5, false, GL_COMPRESSED_RGBA_ASTC_6x5x5_OES},
838cc1dc7a3Sopenharmony_ci	{ 6,  6,  5, false, GL_COMPRESSED_RGBA_ASTC_6x6x5_OES},
839cc1dc7a3Sopenharmony_ci	{ 6,  6,  6, false, GL_COMPRESSED_RGBA_ASTC_6x6x6_OES},
840cc1dc7a3Sopenharmony_ci	// 3D SRGB
841cc1dc7a3Sopenharmony_ci	{ 3,  3,  3,  true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES},
842cc1dc7a3Sopenharmony_ci	{ 4,  3,  3,  true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES},
843cc1dc7a3Sopenharmony_ci	{ 4,  4,  3,  true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES},
844cc1dc7a3Sopenharmony_ci	{ 4,  4,  4,  true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES},
845cc1dc7a3Sopenharmony_ci	{ 5,  4,  4,  true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES},
846cc1dc7a3Sopenharmony_ci	{ 5,  5,  4,  true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES},
847cc1dc7a3Sopenharmony_ci	{ 5,  5,  5,  true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES},
848cc1dc7a3Sopenharmony_ci	{ 6,  5,  5,  true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES},
849cc1dc7a3Sopenharmony_ci	{ 6,  6,  5,  true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES},
850cc1dc7a3Sopenharmony_ci	{ 6,  6,  6,  true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES}
851cc1dc7a3Sopenharmony_ci}};
852cc1dc7a3Sopenharmony_ci
853cc1dc7a3Sopenharmony_cistatic const format_entry* get_format(
854cc1dc7a3Sopenharmony_ci	unsigned int format
855cc1dc7a3Sopenharmony_ci) {
856cc1dc7a3Sopenharmony_ci	for (auto& it : ASTC_FORMATS)
857cc1dc7a3Sopenharmony_ci	{
858cc1dc7a3Sopenharmony_ci		if (it.format == format)
859cc1dc7a3Sopenharmony_ci		{
860cc1dc7a3Sopenharmony_ci			return &it;
861cc1dc7a3Sopenharmony_ci		}
862cc1dc7a3Sopenharmony_ci	}
863cc1dc7a3Sopenharmony_ci	return nullptr;
864cc1dc7a3Sopenharmony_ci}
865cc1dc7a3Sopenharmony_ci
866cc1dc7a3Sopenharmony_cistatic unsigned int get_format(
867cc1dc7a3Sopenharmony_ci	unsigned int x,
868cc1dc7a3Sopenharmony_ci	unsigned int y,
869cc1dc7a3Sopenharmony_ci	unsigned int z,
870cc1dc7a3Sopenharmony_ci	bool is_srgb
871cc1dc7a3Sopenharmony_ci) {
872cc1dc7a3Sopenharmony_ci	for (auto& it : ASTC_FORMATS)
873cc1dc7a3Sopenharmony_ci	{
874cc1dc7a3Sopenharmony_ci		if ((it.x == x) && (it.y == y) && (it.z == z) && (it.is_srgb == is_srgb))
875cc1dc7a3Sopenharmony_ci		{
876cc1dc7a3Sopenharmony_ci			return it.format;
877cc1dc7a3Sopenharmony_ci		}
878cc1dc7a3Sopenharmony_ci	}
879cc1dc7a3Sopenharmony_ci	return 0;
880cc1dc7a3Sopenharmony_ci}
881cc1dc7a3Sopenharmony_ci
882cc1dc7a3Sopenharmony_cistruct ktx_header
883cc1dc7a3Sopenharmony_ci{
884cc1dc7a3Sopenharmony_ci	uint8_t magic[12];
885cc1dc7a3Sopenharmony_ci	uint32_t endianness;				// should be 0x04030201; if it is instead 0x01020304, then the endianness of everything must be switched.
886cc1dc7a3Sopenharmony_ci	uint32_t gl_type;					// 0 for compressed textures, otherwise value from table 3.2 (page 162) of OpenGL 4.0 spec
887cc1dc7a3Sopenharmony_ci	uint32_t gl_type_size;				// size of data elements to do endianness swap on (1=endian-neutral data)
888cc1dc7a3Sopenharmony_ci	uint32_t gl_format;					// 0 for compressed textures, otherwise value from table 3.3 (page 163) of OpenGL spec
889cc1dc7a3Sopenharmony_ci	uint32_t gl_internal_format;		// sized-internal-format, corresponding to table 3.12 to 3.14 (pages 182-185) of OpenGL spec
890cc1dc7a3Sopenharmony_ci	uint32_t gl_base_internal_format;	// unsized-internal-format: corresponding to table 3.11 (page 179) of OpenGL spec
891cc1dc7a3Sopenharmony_ci	uint32_t pixel_width;				// texture dimensions; not rounded up to block size for compressed.
892cc1dc7a3Sopenharmony_ci	uint32_t pixel_height;				// must be 0 for 1D textures.
893cc1dc7a3Sopenharmony_ci	uint32_t pixel_depth;				// must be 0 for 1D, 2D and cubemap textures.
894cc1dc7a3Sopenharmony_ci	uint32_t number_of_array_elements;	// 0 if not a texture array
895cc1dc7a3Sopenharmony_ci	uint32_t number_of_faces;			// 6 for cubemaps, 1 for non-cubemaps
896cc1dc7a3Sopenharmony_ci	uint32_t number_of_mipmap_levels;	// 0 or 1 for non-mipmapped textures; 0 indicates that auto-mipmap-gen should be done at load time.
897cc1dc7a3Sopenharmony_ci	uint32_t bytes_of_key_value_data;	// size in bytes of the key-and-value area immediately following the header.
898cc1dc7a3Sopenharmony_ci};
899cc1dc7a3Sopenharmony_ci
900cc1dc7a3Sopenharmony_ci// Magic 12-byte sequence that must appear at the beginning of every KTX file.
901cc1dc7a3Sopenharmony_cistatic uint8_t ktx_magic[12] {
902cc1dc7a3Sopenharmony_ci	0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A
903cc1dc7a3Sopenharmony_ci};
904cc1dc7a3Sopenharmony_ci
905cc1dc7a3Sopenharmony_cistatic void ktx_header_switch_endianness(ktx_header * kt)
906cc1dc7a3Sopenharmony_ci{
907cc1dc7a3Sopenharmony_ci	#define REV(x) kt->x = u32_byterev(kt->x)
908cc1dc7a3Sopenharmony_ci	REV(endianness);
909cc1dc7a3Sopenharmony_ci	REV(gl_type);
910cc1dc7a3Sopenharmony_ci	REV(gl_type_size);
911cc1dc7a3Sopenharmony_ci	REV(gl_format);
912cc1dc7a3Sopenharmony_ci	REV(gl_internal_format);
913cc1dc7a3Sopenharmony_ci	REV(gl_base_internal_format);
914cc1dc7a3Sopenharmony_ci	REV(pixel_width);
915cc1dc7a3Sopenharmony_ci	REV(pixel_height);
916cc1dc7a3Sopenharmony_ci	REV(pixel_depth);
917cc1dc7a3Sopenharmony_ci	REV(number_of_array_elements);
918cc1dc7a3Sopenharmony_ci	REV(number_of_faces);
919cc1dc7a3Sopenharmony_ci	REV(number_of_mipmap_levels);
920cc1dc7a3Sopenharmony_ci	REV(bytes_of_key_value_data);
921cc1dc7a3Sopenharmony_ci	#undef REV
922cc1dc7a3Sopenharmony_ci}
923cc1dc7a3Sopenharmony_ci
924cc1dc7a3Sopenharmony_ci/**
925cc1dc7a3Sopenharmony_ci * @brief Load an uncompressed KTX image using the local custom loader.
926cc1dc7a3Sopenharmony_ci *
927cc1dc7a3Sopenharmony_ci * @param      filename          The name of the file to load.
928cc1dc7a3Sopenharmony_ci * @param      y_flip            Should the image be vertically flipped?
929cc1dc7a3Sopenharmony_ci * @param[out] is_hdr            Is this an HDR image load?
930cc1dc7a3Sopenharmony_ci * @param[out] component_count   The number of components in the data.
931cc1dc7a3Sopenharmony_ci *
932cc1dc7a3Sopenharmony_ci * @return The loaded image data in a canonical 4 channel format, or @c nullptr on error.
933cc1dc7a3Sopenharmony_ci */
934cc1dc7a3Sopenharmony_cistatic astcenc_image* load_ktx_uncompressed_image(
935cc1dc7a3Sopenharmony_ci	const char* filename,
936cc1dc7a3Sopenharmony_ci	bool y_flip,
937cc1dc7a3Sopenharmony_ci	bool& is_hdr,
938cc1dc7a3Sopenharmony_ci	unsigned int& component_count
939cc1dc7a3Sopenharmony_ci) {
940cc1dc7a3Sopenharmony_ci	FILE *f = fopen(filename, "rb");
941cc1dc7a3Sopenharmony_ci	if (!f)
942cc1dc7a3Sopenharmony_ci	{
943cc1dc7a3Sopenharmony_ci		printf("Failed to open file %s\n", filename);
944cc1dc7a3Sopenharmony_ci		return nullptr;
945cc1dc7a3Sopenharmony_ci	}
946cc1dc7a3Sopenharmony_ci
947cc1dc7a3Sopenharmony_ci	ktx_header hdr;
948cc1dc7a3Sopenharmony_ci	size_t header_bytes_read = fread(&hdr, 1, sizeof(hdr), f);
949cc1dc7a3Sopenharmony_ci
950cc1dc7a3Sopenharmony_ci	if (header_bytes_read != sizeof(hdr))
951cc1dc7a3Sopenharmony_ci	{
952cc1dc7a3Sopenharmony_ci		printf("Failed to read header of KTX file %s\n", filename);
953cc1dc7a3Sopenharmony_ci		fclose(f);
954cc1dc7a3Sopenharmony_ci		return nullptr;
955cc1dc7a3Sopenharmony_ci	}
956cc1dc7a3Sopenharmony_ci
957cc1dc7a3Sopenharmony_ci	if (memcmp(hdr.magic, ktx_magic, 12) != 0 || (hdr.endianness != 0x04030201 && hdr.endianness != 0x01020304))
958cc1dc7a3Sopenharmony_ci	{
959cc1dc7a3Sopenharmony_ci		printf("File %s does not have a valid KTX header\n", filename);
960cc1dc7a3Sopenharmony_ci		fclose(f);
961cc1dc7a3Sopenharmony_ci		return nullptr;
962cc1dc7a3Sopenharmony_ci	}
963cc1dc7a3Sopenharmony_ci
964cc1dc7a3Sopenharmony_ci	int switch_endianness = 0;
965cc1dc7a3Sopenharmony_ci	if (hdr.endianness == 0x01020304)
966cc1dc7a3Sopenharmony_ci	{
967cc1dc7a3Sopenharmony_ci		ktx_header_switch_endianness(&hdr);
968cc1dc7a3Sopenharmony_ci		switch_endianness = 1;
969cc1dc7a3Sopenharmony_ci	}
970cc1dc7a3Sopenharmony_ci
971cc1dc7a3Sopenharmony_ci	if (hdr.gl_type == 0 || hdr.gl_format == 0)
972cc1dc7a3Sopenharmony_ci	{
973cc1dc7a3Sopenharmony_ci		printf("File %s appears to be compressed, not supported as input\n", filename);
974cc1dc7a3Sopenharmony_ci		fclose(f);
975cc1dc7a3Sopenharmony_ci		return nullptr;
976cc1dc7a3Sopenharmony_ci	}
977cc1dc7a3Sopenharmony_ci
978cc1dc7a3Sopenharmony_ci	// the formats we support are:
979cc1dc7a3Sopenharmony_ci
980cc1dc7a3Sopenharmony_ci	// Cartesian product of gl_type=(UNSIGNED_BYTE, UNSIGNED_SHORT, HALF_FLOAT, FLOAT) x gl_format=(RED, RG, RGB, RGBA, BGR, BGRA)
981cc1dc7a3Sopenharmony_ci
982cc1dc7a3Sopenharmony_ci	int components;
983cc1dc7a3Sopenharmony_ci	switch (hdr.gl_format)
984cc1dc7a3Sopenharmony_ci	{
985cc1dc7a3Sopenharmony_ci	case GL_RED:
986cc1dc7a3Sopenharmony_ci		components = 1;
987cc1dc7a3Sopenharmony_ci		break;
988cc1dc7a3Sopenharmony_ci	case GL_RG:
989cc1dc7a3Sopenharmony_ci		components = 2;
990cc1dc7a3Sopenharmony_ci		break;
991cc1dc7a3Sopenharmony_ci	case GL_RGB:
992cc1dc7a3Sopenharmony_ci		components = 3;
993cc1dc7a3Sopenharmony_ci		break;
994cc1dc7a3Sopenharmony_ci	case GL_RGBA:
995cc1dc7a3Sopenharmony_ci		components = 4;
996cc1dc7a3Sopenharmony_ci		break;
997cc1dc7a3Sopenharmony_ci	case GL_BGR:
998cc1dc7a3Sopenharmony_ci		components = 3;
999cc1dc7a3Sopenharmony_ci		break;
1000cc1dc7a3Sopenharmony_ci	case GL_BGRA:
1001cc1dc7a3Sopenharmony_ci		components = 4;
1002cc1dc7a3Sopenharmony_ci		break;
1003cc1dc7a3Sopenharmony_ci	case GL_LUMINANCE:
1004cc1dc7a3Sopenharmony_ci		components = 1;
1005cc1dc7a3Sopenharmony_ci		break;
1006cc1dc7a3Sopenharmony_ci	case GL_LUMINANCE_ALPHA:
1007cc1dc7a3Sopenharmony_ci		components = 2;
1008cc1dc7a3Sopenharmony_ci		break;
1009cc1dc7a3Sopenharmony_ci	default:
1010cc1dc7a3Sopenharmony_ci		printf("KTX file %s has unsupported GL type\n", filename);
1011cc1dc7a3Sopenharmony_ci		fclose(f);
1012cc1dc7a3Sopenharmony_ci		return nullptr;
1013cc1dc7a3Sopenharmony_ci	}
1014cc1dc7a3Sopenharmony_ci
1015cc1dc7a3Sopenharmony_ci	// Although these are set up later, use default initializer to remove warnings
1016cc1dc7a3Sopenharmony_ci	int bitness = 8;              // Internal precision after conversion
1017cc1dc7a3Sopenharmony_ci	int bytes_per_component = 1;  // Bytes per component in the KTX file
1018cc1dc7a3Sopenharmony_ci	scanline_transfer copy_method = R8_TO_RGBA8;
1019cc1dc7a3Sopenharmony_ci
1020cc1dc7a3Sopenharmony_ci	switch (hdr.gl_type)
1021cc1dc7a3Sopenharmony_ci	{
1022cc1dc7a3Sopenharmony_ci	case GL_UNSIGNED_BYTE:
1023cc1dc7a3Sopenharmony_ci		{
1024cc1dc7a3Sopenharmony_ci			bitness = 8;
1025cc1dc7a3Sopenharmony_ci			bytes_per_component = 1;
1026cc1dc7a3Sopenharmony_ci			switch (hdr.gl_format)
1027cc1dc7a3Sopenharmony_ci			{
1028cc1dc7a3Sopenharmony_ci			case GL_RED:
1029cc1dc7a3Sopenharmony_ci				copy_method = R8_TO_RGBA8;
1030cc1dc7a3Sopenharmony_ci				break;
1031cc1dc7a3Sopenharmony_ci			case GL_RG:
1032cc1dc7a3Sopenharmony_ci				copy_method = RG8_TO_RGBA8;
1033cc1dc7a3Sopenharmony_ci				break;
1034cc1dc7a3Sopenharmony_ci			case GL_RGB:
1035cc1dc7a3Sopenharmony_ci				copy_method = RGB8_TO_RGBA8;
1036cc1dc7a3Sopenharmony_ci				break;
1037cc1dc7a3Sopenharmony_ci			case GL_RGBA:
1038cc1dc7a3Sopenharmony_ci				copy_method = RGBA8_TO_RGBA8;
1039cc1dc7a3Sopenharmony_ci				break;
1040cc1dc7a3Sopenharmony_ci			case GL_BGR:
1041cc1dc7a3Sopenharmony_ci				copy_method = BGR8_TO_RGBA8;
1042cc1dc7a3Sopenharmony_ci				break;
1043cc1dc7a3Sopenharmony_ci			case GL_BGRA:
1044cc1dc7a3Sopenharmony_ci				copy_method = BGRA8_TO_RGBA8;
1045cc1dc7a3Sopenharmony_ci				break;
1046cc1dc7a3Sopenharmony_ci			case GL_LUMINANCE:
1047cc1dc7a3Sopenharmony_ci				copy_method = L8_TO_RGBA8;
1048cc1dc7a3Sopenharmony_ci				break;
1049cc1dc7a3Sopenharmony_ci			case GL_LUMINANCE_ALPHA:
1050cc1dc7a3Sopenharmony_ci				copy_method = LA8_TO_RGBA8;
1051cc1dc7a3Sopenharmony_ci				break;
1052cc1dc7a3Sopenharmony_ci			}
1053cc1dc7a3Sopenharmony_ci			break;
1054cc1dc7a3Sopenharmony_ci		}
1055cc1dc7a3Sopenharmony_ci	case GL_UNSIGNED_SHORT:
1056cc1dc7a3Sopenharmony_ci		{
1057cc1dc7a3Sopenharmony_ci			bitness = 16;
1058cc1dc7a3Sopenharmony_ci			bytes_per_component = 2;
1059cc1dc7a3Sopenharmony_ci			switch (hdr.gl_format)
1060cc1dc7a3Sopenharmony_ci			{
1061cc1dc7a3Sopenharmony_ci			case GL_RED:
1062cc1dc7a3Sopenharmony_ci				copy_method = R16_TO_RGBA16F;
1063cc1dc7a3Sopenharmony_ci				break;
1064cc1dc7a3Sopenharmony_ci			case GL_RG:
1065cc1dc7a3Sopenharmony_ci				copy_method = RG16_TO_RGBA16F;
1066cc1dc7a3Sopenharmony_ci				break;
1067cc1dc7a3Sopenharmony_ci			case GL_RGB:
1068cc1dc7a3Sopenharmony_ci				copy_method = RGB16_TO_RGBA16F;
1069cc1dc7a3Sopenharmony_ci				break;
1070cc1dc7a3Sopenharmony_ci			case GL_RGBA:
1071cc1dc7a3Sopenharmony_ci				copy_method = RGBA16_TO_RGBA16F;
1072cc1dc7a3Sopenharmony_ci				break;
1073cc1dc7a3Sopenharmony_ci			case GL_BGR:
1074cc1dc7a3Sopenharmony_ci				copy_method = BGR16_TO_RGBA16F;
1075cc1dc7a3Sopenharmony_ci				break;
1076cc1dc7a3Sopenharmony_ci			case GL_BGRA:
1077cc1dc7a3Sopenharmony_ci				copy_method = BGRA16_TO_RGBA16F;
1078cc1dc7a3Sopenharmony_ci				break;
1079cc1dc7a3Sopenharmony_ci			case GL_LUMINANCE:
1080cc1dc7a3Sopenharmony_ci				copy_method = L16_TO_RGBA16F;
1081cc1dc7a3Sopenharmony_ci				break;
1082cc1dc7a3Sopenharmony_ci			case GL_LUMINANCE_ALPHA:
1083cc1dc7a3Sopenharmony_ci				copy_method = LA16_TO_RGBA16F;
1084cc1dc7a3Sopenharmony_ci				break;
1085cc1dc7a3Sopenharmony_ci			}
1086cc1dc7a3Sopenharmony_ci			break;
1087cc1dc7a3Sopenharmony_ci		}
1088cc1dc7a3Sopenharmony_ci	case GL_HALF_FLOAT:
1089cc1dc7a3Sopenharmony_ci		{
1090cc1dc7a3Sopenharmony_ci			bitness = 16;
1091cc1dc7a3Sopenharmony_ci			bytes_per_component = 2;
1092cc1dc7a3Sopenharmony_ci			switch (hdr.gl_format)
1093cc1dc7a3Sopenharmony_ci			{
1094cc1dc7a3Sopenharmony_ci			case GL_RED:
1095cc1dc7a3Sopenharmony_ci				copy_method = R16F_TO_RGBA16F;
1096cc1dc7a3Sopenharmony_ci				break;
1097cc1dc7a3Sopenharmony_ci			case GL_RG:
1098cc1dc7a3Sopenharmony_ci				copy_method = RG16F_TO_RGBA16F;
1099cc1dc7a3Sopenharmony_ci				break;
1100cc1dc7a3Sopenharmony_ci			case GL_RGB:
1101cc1dc7a3Sopenharmony_ci				copy_method = RGB16F_TO_RGBA16F;
1102cc1dc7a3Sopenharmony_ci				break;
1103cc1dc7a3Sopenharmony_ci			case GL_RGBA:
1104cc1dc7a3Sopenharmony_ci				copy_method = RGBA16F_TO_RGBA16F;
1105cc1dc7a3Sopenharmony_ci				break;
1106cc1dc7a3Sopenharmony_ci			case GL_BGR:
1107cc1dc7a3Sopenharmony_ci				copy_method = BGR16F_TO_RGBA16F;
1108cc1dc7a3Sopenharmony_ci				break;
1109cc1dc7a3Sopenharmony_ci			case GL_BGRA:
1110cc1dc7a3Sopenharmony_ci				copy_method = BGRA16F_TO_RGBA16F;
1111cc1dc7a3Sopenharmony_ci				break;
1112cc1dc7a3Sopenharmony_ci			case GL_LUMINANCE:
1113cc1dc7a3Sopenharmony_ci				copy_method = L16F_TO_RGBA16F;
1114cc1dc7a3Sopenharmony_ci				break;
1115cc1dc7a3Sopenharmony_ci			case GL_LUMINANCE_ALPHA:
1116cc1dc7a3Sopenharmony_ci				copy_method = LA16F_TO_RGBA16F;
1117cc1dc7a3Sopenharmony_ci				break;
1118cc1dc7a3Sopenharmony_ci			}
1119cc1dc7a3Sopenharmony_ci			break;
1120cc1dc7a3Sopenharmony_ci		}
1121cc1dc7a3Sopenharmony_ci	case GL_FLOAT:
1122cc1dc7a3Sopenharmony_ci		{
1123cc1dc7a3Sopenharmony_ci			bitness = 16;
1124cc1dc7a3Sopenharmony_ci			bytes_per_component = 4;
1125cc1dc7a3Sopenharmony_ci			switch (hdr.gl_format)
1126cc1dc7a3Sopenharmony_ci			{
1127cc1dc7a3Sopenharmony_ci			case GL_RED:
1128cc1dc7a3Sopenharmony_ci				copy_method = R32F_TO_RGBA16F;
1129cc1dc7a3Sopenharmony_ci				break;
1130cc1dc7a3Sopenharmony_ci			case GL_RG:
1131cc1dc7a3Sopenharmony_ci				copy_method = RG32F_TO_RGBA16F;
1132cc1dc7a3Sopenharmony_ci				break;
1133cc1dc7a3Sopenharmony_ci			case GL_RGB:
1134cc1dc7a3Sopenharmony_ci				copy_method = RGB32F_TO_RGBA16F;
1135cc1dc7a3Sopenharmony_ci				break;
1136cc1dc7a3Sopenharmony_ci			case GL_RGBA:
1137cc1dc7a3Sopenharmony_ci				copy_method = RGBA32F_TO_RGBA16F;
1138cc1dc7a3Sopenharmony_ci				break;
1139cc1dc7a3Sopenharmony_ci			case GL_BGR:
1140cc1dc7a3Sopenharmony_ci				copy_method = BGR32F_TO_RGBA16F;
1141cc1dc7a3Sopenharmony_ci				break;
1142cc1dc7a3Sopenharmony_ci			case GL_BGRA:
1143cc1dc7a3Sopenharmony_ci				copy_method = BGRA32F_TO_RGBA16F;
1144cc1dc7a3Sopenharmony_ci				break;
1145cc1dc7a3Sopenharmony_ci			case GL_LUMINANCE:
1146cc1dc7a3Sopenharmony_ci				copy_method = L32F_TO_RGBA16F;
1147cc1dc7a3Sopenharmony_ci				break;
1148cc1dc7a3Sopenharmony_ci			case GL_LUMINANCE_ALPHA:
1149cc1dc7a3Sopenharmony_ci				copy_method = LA32F_TO_RGBA16F;
1150cc1dc7a3Sopenharmony_ci				break;
1151cc1dc7a3Sopenharmony_ci			}
1152cc1dc7a3Sopenharmony_ci			break;
1153cc1dc7a3Sopenharmony_ci		}
1154cc1dc7a3Sopenharmony_ci	default:
1155cc1dc7a3Sopenharmony_ci		printf("KTX file %s has unsupported GL format\n", filename);
1156cc1dc7a3Sopenharmony_ci		fclose(f);
1157cc1dc7a3Sopenharmony_ci		return nullptr;
1158cc1dc7a3Sopenharmony_ci	}
1159cc1dc7a3Sopenharmony_ci
1160cc1dc7a3Sopenharmony_ci	if (hdr.number_of_mipmap_levels > 1)
1161cc1dc7a3Sopenharmony_ci	{
1162cc1dc7a3Sopenharmony_ci		printf("WARNING: KTX file %s has %d mipmap levels; only the first one will be encoded.\n", filename, hdr.number_of_mipmap_levels);
1163cc1dc7a3Sopenharmony_ci	}
1164cc1dc7a3Sopenharmony_ci
1165cc1dc7a3Sopenharmony_ci	if (hdr.number_of_array_elements > 1)
1166cc1dc7a3Sopenharmony_ci	{
1167cc1dc7a3Sopenharmony_ci		printf("WARNING: KTX file %s contains a texture array with %d layers; only the first one will be encoded.\n", filename, hdr.number_of_array_elements);
1168cc1dc7a3Sopenharmony_ci	}
1169cc1dc7a3Sopenharmony_ci
1170cc1dc7a3Sopenharmony_ci	if (hdr.number_of_faces > 1)
1171cc1dc7a3Sopenharmony_ci	{
1172cc1dc7a3Sopenharmony_ci		printf("WARNING: KTX file %s contains a cubemap with 6 faces; only the first one will be encoded.\n", filename);
1173cc1dc7a3Sopenharmony_ci	}
1174cc1dc7a3Sopenharmony_ci
1175cc1dc7a3Sopenharmony_ci
1176cc1dc7a3Sopenharmony_ci	unsigned int dim_x = hdr.pixel_width;
1177cc1dc7a3Sopenharmony_ci	unsigned int dim_y = astc::max(hdr.pixel_height, 1u);
1178cc1dc7a3Sopenharmony_ci	unsigned int dim_z = astc::max(hdr.pixel_depth, 1u);
1179cc1dc7a3Sopenharmony_ci
1180cc1dc7a3Sopenharmony_ci	// ignore the key/value data
1181cc1dc7a3Sopenharmony_ci	fseek(f, hdr.bytes_of_key_value_data, SEEK_CUR);
1182cc1dc7a3Sopenharmony_ci
1183cc1dc7a3Sopenharmony_ci	uint32_t specified_bytes_of_surface = 0;
1184cc1dc7a3Sopenharmony_ci	size_t sb_read = fread(&specified_bytes_of_surface, 1, 4, f);
1185cc1dc7a3Sopenharmony_ci	if (sb_read != 4)
1186cc1dc7a3Sopenharmony_ci	{
1187cc1dc7a3Sopenharmony_ci		printf("Failed to read header of KTX file %s\n", filename);
1188cc1dc7a3Sopenharmony_ci		fclose(f);
1189cc1dc7a3Sopenharmony_ci		return nullptr;
1190cc1dc7a3Sopenharmony_ci	}
1191cc1dc7a3Sopenharmony_ci
1192cc1dc7a3Sopenharmony_ci	if (switch_endianness)
1193cc1dc7a3Sopenharmony_ci	{
1194cc1dc7a3Sopenharmony_ci		specified_bytes_of_surface = u32_byterev(specified_bytes_of_surface);
1195cc1dc7a3Sopenharmony_ci	}
1196cc1dc7a3Sopenharmony_ci
1197cc1dc7a3Sopenharmony_ci	// read the surface
1198cc1dc7a3Sopenharmony_ci	uint32_t xstride = bytes_per_component * components * dim_x;
1199cc1dc7a3Sopenharmony_ci	uint32_t ystride = xstride * dim_y;
1200cc1dc7a3Sopenharmony_ci	uint32_t computed_bytes_of_surface = dim_z * ystride;
1201cc1dc7a3Sopenharmony_ci	if (computed_bytes_of_surface != specified_bytes_of_surface)
1202cc1dc7a3Sopenharmony_ci	{
1203cc1dc7a3Sopenharmony_ci		fclose(f);
1204cc1dc7a3Sopenharmony_ci		printf("%s: KTX file inconsistency: computed surface size is %d bytes, but specified size is %d bytes\n", filename, computed_bytes_of_surface, specified_bytes_of_surface);
1205cc1dc7a3Sopenharmony_ci		return nullptr;
1206cc1dc7a3Sopenharmony_ci	}
1207cc1dc7a3Sopenharmony_ci
1208cc1dc7a3Sopenharmony_ci	uint8_t *buf = new uint8_t[specified_bytes_of_surface];
1209cc1dc7a3Sopenharmony_ci	size_t bytes_read = fread(buf, 1, specified_bytes_of_surface, f);
1210cc1dc7a3Sopenharmony_ci	fclose(f);
1211cc1dc7a3Sopenharmony_ci	if (bytes_read != specified_bytes_of_surface)
1212cc1dc7a3Sopenharmony_ci	{
1213cc1dc7a3Sopenharmony_ci		delete[] buf;
1214cc1dc7a3Sopenharmony_ci		printf("Failed to read file %s\n", filename);
1215cc1dc7a3Sopenharmony_ci		return nullptr;
1216cc1dc7a3Sopenharmony_ci	}
1217cc1dc7a3Sopenharmony_ci
1218cc1dc7a3Sopenharmony_ci	// perform an endianness swap on the surface if needed.
1219cc1dc7a3Sopenharmony_ci	if (switch_endianness)
1220cc1dc7a3Sopenharmony_ci	{
1221cc1dc7a3Sopenharmony_ci		if (hdr.gl_type_size == 2)
1222cc1dc7a3Sopenharmony_ci		{
1223cc1dc7a3Sopenharmony_ci			switch_endianness2(buf, specified_bytes_of_surface);
1224cc1dc7a3Sopenharmony_ci		}
1225cc1dc7a3Sopenharmony_ci
1226cc1dc7a3Sopenharmony_ci		if (hdr.gl_type_size == 4)
1227cc1dc7a3Sopenharmony_ci		{
1228cc1dc7a3Sopenharmony_ci			switch_endianness4(buf, specified_bytes_of_surface);
1229cc1dc7a3Sopenharmony_ci		}
1230cc1dc7a3Sopenharmony_ci	}
1231cc1dc7a3Sopenharmony_ci
1232cc1dc7a3Sopenharmony_ci	// Transfer data from the surface to our own image data structure
1233cc1dc7a3Sopenharmony_ci	astcenc_image *astc_img = alloc_image(bitness, dim_x, dim_y, dim_z);
1234cc1dc7a3Sopenharmony_ci
1235cc1dc7a3Sopenharmony_ci	for (unsigned int z = 0; z < dim_z; z++)
1236cc1dc7a3Sopenharmony_ci	{
1237cc1dc7a3Sopenharmony_ci		for (unsigned int y = 0; y < dim_y; y++)
1238cc1dc7a3Sopenharmony_ci		{
1239cc1dc7a3Sopenharmony_ci			unsigned int ymod = y_flip ? dim_y - y - 1 : y;
1240cc1dc7a3Sopenharmony_ci			unsigned int ydst = ymod;
1241cc1dc7a3Sopenharmony_ci			void *dst;
1242cc1dc7a3Sopenharmony_ci
1243cc1dc7a3Sopenharmony_ci			if (astc_img->data_type == ASTCENC_TYPE_U8)
1244cc1dc7a3Sopenharmony_ci			{
1245cc1dc7a3Sopenharmony_ci				uint8_t* data8 = static_cast<uint8_t*>(astc_img->data[z]);
1246cc1dc7a3Sopenharmony_ci				dst = static_cast<void*>(&data8[4 * dim_x * ydst]);
1247cc1dc7a3Sopenharmony_ci			}
1248cc1dc7a3Sopenharmony_ci			else // if (astc_img->data_type == ASTCENC_TYPE_F16)
1249cc1dc7a3Sopenharmony_ci			{
1250cc1dc7a3Sopenharmony_ci				assert(astc_img->data_type == ASTCENC_TYPE_F16);
1251cc1dc7a3Sopenharmony_ci				uint16_t* data16 = static_cast<uint16_t*>(astc_img->data[z]);
1252cc1dc7a3Sopenharmony_ci				dst = static_cast<void*>(&data16[4 * dim_x * ydst]);
1253cc1dc7a3Sopenharmony_ci			}
1254cc1dc7a3Sopenharmony_ci
1255cc1dc7a3Sopenharmony_ci			uint8_t *src = buf + (z * ystride) + (y * xstride);
1256cc1dc7a3Sopenharmony_ci			copy_scanline(dst, src, dim_x, copy_method);
1257cc1dc7a3Sopenharmony_ci		}
1258cc1dc7a3Sopenharmony_ci	}
1259cc1dc7a3Sopenharmony_ci
1260cc1dc7a3Sopenharmony_ci	delete[] buf;
1261cc1dc7a3Sopenharmony_ci	is_hdr = bitness >= 16;
1262cc1dc7a3Sopenharmony_ci	component_count = components;
1263cc1dc7a3Sopenharmony_ci	return astc_img;
1264cc1dc7a3Sopenharmony_ci}
1265cc1dc7a3Sopenharmony_ci
1266cc1dc7a3Sopenharmony_ci/**
1267cc1dc7a3Sopenharmony_ci * @brief Load a KTX compressed image using the local custom loader.
1268cc1dc7a3Sopenharmony_ci *
1269cc1dc7a3Sopenharmony_ci * @param      filename          The name of the file to load.
1270cc1dc7a3Sopenharmony_ci * @param[out] is_srgb           @c true if this is an sRGB image, @c false otherwise.
1271cc1dc7a3Sopenharmony_ci * @param[out] img               The output image to populate.
1272cc1dc7a3Sopenharmony_ci *
1273cc1dc7a3Sopenharmony_ci * @return @c true on error, @c false otherwise.
1274cc1dc7a3Sopenharmony_ci */
1275cc1dc7a3Sopenharmony_cibool load_ktx_compressed_image(
1276cc1dc7a3Sopenharmony_ci	const char* filename,
1277cc1dc7a3Sopenharmony_ci	bool& is_srgb,
1278cc1dc7a3Sopenharmony_ci	astc_compressed_image& img
1279cc1dc7a3Sopenharmony_ci) {
1280cc1dc7a3Sopenharmony_ci	FILE *f = fopen(filename, "rb");
1281cc1dc7a3Sopenharmony_ci	if (!f)
1282cc1dc7a3Sopenharmony_ci	{
1283cc1dc7a3Sopenharmony_ci		printf("Failed to open file %s\n", filename);
1284cc1dc7a3Sopenharmony_ci		return true;
1285cc1dc7a3Sopenharmony_ci	}
1286cc1dc7a3Sopenharmony_ci
1287cc1dc7a3Sopenharmony_ci	ktx_header hdr;
1288cc1dc7a3Sopenharmony_ci	size_t actual = fread(&hdr, 1, sizeof(hdr), f);
1289cc1dc7a3Sopenharmony_ci	if (actual != sizeof(hdr))
1290cc1dc7a3Sopenharmony_ci	{
1291cc1dc7a3Sopenharmony_ci		printf("Failed to read header from %s\n", filename);
1292cc1dc7a3Sopenharmony_ci		fclose(f);
1293cc1dc7a3Sopenharmony_ci		return true;
1294cc1dc7a3Sopenharmony_ci	}
1295cc1dc7a3Sopenharmony_ci
1296cc1dc7a3Sopenharmony_ci	if (memcmp(hdr.magic, ktx_magic, 12) != 0 ||
1297cc1dc7a3Sopenharmony_ci	    (hdr.endianness != 0x04030201 && hdr.endianness != 0x01020304))
1298cc1dc7a3Sopenharmony_ci	{
1299cc1dc7a3Sopenharmony_ci		printf("File %s does not have a valid KTX header\n", filename);
1300cc1dc7a3Sopenharmony_ci		fclose(f);
1301cc1dc7a3Sopenharmony_ci		return true;
1302cc1dc7a3Sopenharmony_ci	}
1303cc1dc7a3Sopenharmony_ci
1304cc1dc7a3Sopenharmony_ci	bool switch_endianness = false;
1305cc1dc7a3Sopenharmony_ci	if (hdr.endianness == 0x01020304)
1306cc1dc7a3Sopenharmony_ci	{
1307cc1dc7a3Sopenharmony_ci		switch_endianness = true;
1308cc1dc7a3Sopenharmony_ci		ktx_header_switch_endianness(&hdr);
1309cc1dc7a3Sopenharmony_ci	}
1310cc1dc7a3Sopenharmony_ci
1311cc1dc7a3Sopenharmony_ci	if (hdr.gl_type != 0 || hdr.gl_format != 0 || hdr.gl_type_size != 1 ||
1312cc1dc7a3Sopenharmony_ci	    hdr.gl_base_internal_format != GL_RGBA)
1313cc1dc7a3Sopenharmony_ci	{
1314cc1dc7a3Sopenharmony_ci		printf("File %s is not a compressed ASTC file\n", filename);
1315cc1dc7a3Sopenharmony_ci		fclose(f);
1316cc1dc7a3Sopenharmony_ci		return true;
1317cc1dc7a3Sopenharmony_ci	}
1318cc1dc7a3Sopenharmony_ci
1319cc1dc7a3Sopenharmony_ci	const format_entry* fmt = get_format(hdr.gl_internal_format);
1320cc1dc7a3Sopenharmony_ci	if (!fmt)
1321cc1dc7a3Sopenharmony_ci	{
1322cc1dc7a3Sopenharmony_ci		printf("File %s is not a compressed ASTC file\n", filename);
1323cc1dc7a3Sopenharmony_ci		fclose(f);
1324cc1dc7a3Sopenharmony_ci		return true;
1325cc1dc7a3Sopenharmony_ci	}
1326cc1dc7a3Sopenharmony_ci
1327cc1dc7a3Sopenharmony_ci	// Skip over any key-value pairs
1328cc1dc7a3Sopenharmony_ci	int seekerr;
1329cc1dc7a3Sopenharmony_ci	seekerr = fseek(f, hdr.bytes_of_key_value_data, SEEK_CUR);
1330cc1dc7a3Sopenharmony_ci	if (seekerr)
1331cc1dc7a3Sopenharmony_ci	{
1332cc1dc7a3Sopenharmony_ci		printf("Failed to skip key-value pairs in %s\n", filename);
1333cc1dc7a3Sopenharmony_ci		fclose(f);
1334cc1dc7a3Sopenharmony_ci		return true;
1335cc1dc7a3Sopenharmony_ci	}
1336cc1dc7a3Sopenharmony_ci
1337cc1dc7a3Sopenharmony_ci	// Read the length of the data and endianess convert
1338cc1dc7a3Sopenharmony_ci	unsigned int data_len;
1339cc1dc7a3Sopenharmony_ci	actual = fread(&data_len, 1, sizeof(data_len), f);
1340cc1dc7a3Sopenharmony_ci	if (actual != sizeof(data_len))
1341cc1dc7a3Sopenharmony_ci	{
1342cc1dc7a3Sopenharmony_ci		printf("Failed to read mip 0 size from %s\n", filename);
1343cc1dc7a3Sopenharmony_ci		fclose(f);
1344cc1dc7a3Sopenharmony_ci		return true;
1345cc1dc7a3Sopenharmony_ci	}
1346cc1dc7a3Sopenharmony_ci
1347cc1dc7a3Sopenharmony_ci	if (switch_endianness)
1348cc1dc7a3Sopenharmony_ci	{
1349cc1dc7a3Sopenharmony_ci		data_len = u32_byterev(data_len);
1350cc1dc7a3Sopenharmony_ci	}
1351cc1dc7a3Sopenharmony_ci
1352cc1dc7a3Sopenharmony_ci	// Read the data
1353cc1dc7a3Sopenharmony_ci	unsigned char* data = new unsigned char[data_len];
1354cc1dc7a3Sopenharmony_ci	actual = fread(data, 1, data_len, f);
1355cc1dc7a3Sopenharmony_ci	if (actual != data_len)
1356cc1dc7a3Sopenharmony_ci	{
1357cc1dc7a3Sopenharmony_ci		printf("Failed to read mip 0 data from %s\n", filename);
1358cc1dc7a3Sopenharmony_ci		fclose(f);
1359cc1dc7a3Sopenharmony_ci		delete[] data;
1360cc1dc7a3Sopenharmony_ci		return true;
1361cc1dc7a3Sopenharmony_ci	}
1362cc1dc7a3Sopenharmony_ci
1363cc1dc7a3Sopenharmony_ci	img.block_x = fmt->x;
1364cc1dc7a3Sopenharmony_ci	img.block_y = fmt->y;
1365cc1dc7a3Sopenharmony_ci	img.block_z = fmt->z == 0 ? 1 : fmt->z;
1366cc1dc7a3Sopenharmony_ci
1367cc1dc7a3Sopenharmony_ci	img.dim_x = hdr.pixel_width;
1368cc1dc7a3Sopenharmony_ci	img.dim_y = hdr.pixel_height;
1369cc1dc7a3Sopenharmony_ci	img.dim_z = hdr.pixel_depth == 0 ? 1 : hdr.pixel_depth;
1370cc1dc7a3Sopenharmony_ci
1371cc1dc7a3Sopenharmony_ci	img.data_len = data_len;
1372cc1dc7a3Sopenharmony_ci	img.data = data;
1373cc1dc7a3Sopenharmony_ci
1374cc1dc7a3Sopenharmony_ci	is_srgb = fmt->is_srgb;
1375cc1dc7a3Sopenharmony_ci
1376cc1dc7a3Sopenharmony_ci	fclose(f);
1377cc1dc7a3Sopenharmony_ci	return false;
1378cc1dc7a3Sopenharmony_ci}
1379cc1dc7a3Sopenharmony_ci
1380cc1dc7a3Sopenharmony_ci/**
1381cc1dc7a3Sopenharmony_ci * @brief Store a KTX compressed image using a local store routine.
1382cc1dc7a3Sopenharmony_ci *
1383cc1dc7a3Sopenharmony_ci * @param img        The image data to store.
1384cc1dc7a3Sopenharmony_ci * @param filename   The name of the file to save.
1385cc1dc7a3Sopenharmony_ci * @param is_srgb    @c true if this is an sRGB image, @c false if linear.
1386cc1dc7a3Sopenharmony_ci *
1387cc1dc7a3Sopenharmony_ci * @return @c true on error, @c false otherwise.
1388cc1dc7a3Sopenharmony_ci */
1389cc1dc7a3Sopenharmony_cibool store_ktx_compressed_image(
1390cc1dc7a3Sopenharmony_ci	const astc_compressed_image& img,
1391cc1dc7a3Sopenharmony_ci	const char* filename,
1392cc1dc7a3Sopenharmony_ci	bool is_srgb
1393cc1dc7a3Sopenharmony_ci) {
1394cc1dc7a3Sopenharmony_ci	unsigned int fmt = get_format(img.block_x, img.block_y, img.block_z, is_srgb);
1395cc1dc7a3Sopenharmony_ci
1396cc1dc7a3Sopenharmony_ci	ktx_header hdr;
1397cc1dc7a3Sopenharmony_ci	memcpy(hdr.magic, ktx_magic, 12);
1398cc1dc7a3Sopenharmony_ci	hdr.endianness = 0x04030201;
1399cc1dc7a3Sopenharmony_ci	hdr.gl_type = 0;
1400cc1dc7a3Sopenharmony_ci	hdr.gl_type_size = 1;
1401cc1dc7a3Sopenharmony_ci	hdr.gl_format = 0;
1402cc1dc7a3Sopenharmony_ci	hdr.gl_internal_format = fmt;
1403cc1dc7a3Sopenharmony_ci	hdr.gl_base_internal_format = GL_RGBA;
1404cc1dc7a3Sopenharmony_ci	hdr.pixel_width = img.dim_x;
1405cc1dc7a3Sopenharmony_ci	hdr.pixel_height = img.dim_y;
1406cc1dc7a3Sopenharmony_ci	hdr.pixel_depth = (img.dim_z == 1) ? 0 : img.dim_z;
1407cc1dc7a3Sopenharmony_ci	hdr.number_of_array_elements = 0;
1408cc1dc7a3Sopenharmony_ci	hdr.number_of_faces = 1;
1409cc1dc7a3Sopenharmony_ci	hdr.number_of_mipmap_levels = 1;
1410cc1dc7a3Sopenharmony_ci	hdr.bytes_of_key_value_data = 0;
1411cc1dc7a3Sopenharmony_ci
1412cc1dc7a3Sopenharmony_ci	size_t expected = sizeof(ktx_header) + 4 + img.data_len;
1413cc1dc7a3Sopenharmony_ci	size_t actual = 0;
1414cc1dc7a3Sopenharmony_ci
1415cc1dc7a3Sopenharmony_ci	FILE *wf = fopen(filename, "wb");
1416cc1dc7a3Sopenharmony_ci	if (!wf)
1417cc1dc7a3Sopenharmony_ci	{
1418cc1dc7a3Sopenharmony_ci		return true;
1419cc1dc7a3Sopenharmony_ci	}
1420cc1dc7a3Sopenharmony_ci
1421cc1dc7a3Sopenharmony_ci	actual += fwrite(&hdr, 1, sizeof(ktx_header), wf);
1422cc1dc7a3Sopenharmony_ci	actual += fwrite(&img.data_len, 1, 4, wf);
1423cc1dc7a3Sopenharmony_ci	actual += fwrite(img.data, 1, img.data_len, wf);
1424cc1dc7a3Sopenharmony_ci	fclose(wf);
1425cc1dc7a3Sopenharmony_ci
1426cc1dc7a3Sopenharmony_ci	if (actual != expected)
1427cc1dc7a3Sopenharmony_ci	{
1428cc1dc7a3Sopenharmony_ci		return true;
1429cc1dc7a3Sopenharmony_ci	}
1430cc1dc7a3Sopenharmony_ci
1431cc1dc7a3Sopenharmony_ci	return false;
1432cc1dc7a3Sopenharmony_ci}
1433cc1dc7a3Sopenharmony_ci
1434cc1dc7a3Sopenharmony_ci/**
1435cc1dc7a3Sopenharmony_ci * @brief Save a KTX uncompressed image using a local store routine.
1436cc1dc7a3Sopenharmony_ci *
1437cc1dc7a3Sopenharmony_ci * @param img        The source data for the image.
1438cc1dc7a3Sopenharmony_ci * @param filename   The name of the file to save.
1439cc1dc7a3Sopenharmony_ci * @param y_flip     Should the image be vertically flipped?
1440cc1dc7a3Sopenharmony_ci *
1441cc1dc7a3Sopenharmony_ci * @return @c true if the image saved OK, @c false on error.
1442cc1dc7a3Sopenharmony_ci */
1443cc1dc7a3Sopenharmony_cistatic bool store_ktx_uncompressed_image(
1444cc1dc7a3Sopenharmony_ci	const astcenc_image* img,
1445cc1dc7a3Sopenharmony_ci	const char* filename,
1446cc1dc7a3Sopenharmony_ci	int y_flip
1447cc1dc7a3Sopenharmony_ci) {
1448cc1dc7a3Sopenharmony_ci	unsigned int dim_x = img->dim_x;
1449cc1dc7a3Sopenharmony_ci	unsigned int dim_y = img->dim_y;
1450cc1dc7a3Sopenharmony_ci	unsigned int dim_z = img->dim_z;
1451cc1dc7a3Sopenharmony_ci
1452cc1dc7a3Sopenharmony_ci	int bitness = img->data_type == ASTCENC_TYPE_U8 ? 8 : 16;
1453cc1dc7a3Sopenharmony_ci	int image_components = determine_image_components(img);
1454cc1dc7a3Sopenharmony_ci
1455cc1dc7a3Sopenharmony_ci	ktx_header hdr;
1456cc1dc7a3Sopenharmony_ci
1457cc1dc7a3Sopenharmony_ci	static const int gl_format_of_components[4] {
1458cc1dc7a3Sopenharmony_ci		GL_RED, GL_RG, GL_RGB, GL_RGBA
1459cc1dc7a3Sopenharmony_ci	};
1460cc1dc7a3Sopenharmony_ci
1461cc1dc7a3Sopenharmony_ci	static const int gl_sized_format_of_components_ldr[4] {
1462cc1dc7a3Sopenharmony_ci		GL_R8, GL_RG8, GL_RGB8, GL_RGBA8
1463cc1dc7a3Sopenharmony_ci	};
1464cc1dc7a3Sopenharmony_ci
1465cc1dc7a3Sopenharmony_ci	static const int gl_sized_format_of_components_hdr[4] {
1466cc1dc7a3Sopenharmony_ci		GL_R16F, GL_RG16F, GL_RGB16F, GL_RGBA16F
1467cc1dc7a3Sopenharmony_ci	};
1468cc1dc7a3Sopenharmony_ci
1469cc1dc7a3Sopenharmony_ci	memcpy(hdr.magic, ktx_magic, 12);
1470cc1dc7a3Sopenharmony_ci	hdr.endianness = 0x04030201;
1471cc1dc7a3Sopenharmony_ci	hdr.gl_type = (bitness == 16) ? GL_HALF_FLOAT : GL_UNSIGNED_BYTE;
1472cc1dc7a3Sopenharmony_ci	hdr.gl_type_size = bitness / 8;
1473cc1dc7a3Sopenharmony_ci	hdr.gl_format = gl_format_of_components[image_components - 1];
1474cc1dc7a3Sopenharmony_ci	if (bitness == 16)
1475cc1dc7a3Sopenharmony_ci	{
1476cc1dc7a3Sopenharmony_ci		hdr.gl_internal_format = gl_sized_format_of_components_hdr[image_components - 1];
1477cc1dc7a3Sopenharmony_ci	}
1478cc1dc7a3Sopenharmony_ci	else
1479cc1dc7a3Sopenharmony_ci	{
1480cc1dc7a3Sopenharmony_ci		hdr.gl_internal_format = gl_sized_format_of_components_ldr[image_components - 1];
1481cc1dc7a3Sopenharmony_ci	}
1482cc1dc7a3Sopenharmony_ci	hdr.gl_base_internal_format = hdr.gl_format;
1483cc1dc7a3Sopenharmony_ci	hdr.pixel_width = dim_x;
1484cc1dc7a3Sopenharmony_ci	hdr.pixel_height = dim_y;
1485cc1dc7a3Sopenharmony_ci	hdr.pixel_depth = (dim_z == 1) ? 0 : dim_z;
1486cc1dc7a3Sopenharmony_ci	hdr.number_of_array_elements = 0;
1487cc1dc7a3Sopenharmony_ci	hdr.number_of_faces = 1;
1488cc1dc7a3Sopenharmony_ci	hdr.number_of_mipmap_levels = 1;
1489cc1dc7a3Sopenharmony_ci	hdr.bytes_of_key_value_data = 0;
1490cc1dc7a3Sopenharmony_ci
1491cc1dc7a3Sopenharmony_ci	// Collect image data to write
1492cc1dc7a3Sopenharmony_ci	uint8_t ***row_pointers8 = nullptr;
1493cc1dc7a3Sopenharmony_ci	uint16_t ***row_pointers16 = nullptr;
1494cc1dc7a3Sopenharmony_ci	if (bitness == 8)
1495cc1dc7a3Sopenharmony_ci	{
1496cc1dc7a3Sopenharmony_ci		row_pointers8 = new uint8_t **[dim_z];
1497cc1dc7a3Sopenharmony_ci		row_pointers8[0] = new uint8_t *[dim_y * dim_z];
1498cc1dc7a3Sopenharmony_ci		row_pointers8[0][0] = new uint8_t[dim_x * dim_y * dim_z * image_components + 3];
1499cc1dc7a3Sopenharmony_ci
1500cc1dc7a3Sopenharmony_ci		for (unsigned int z = 1; z < dim_z; z++)
1501cc1dc7a3Sopenharmony_ci		{
1502cc1dc7a3Sopenharmony_ci			row_pointers8[z] = row_pointers8[0] + dim_y * z;
1503cc1dc7a3Sopenharmony_ci			row_pointers8[z][0] = row_pointers8[0][0] + dim_y * dim_x * image_components * z;
1504cc1dc7a3Sopenharmony_ci		}
1505cc1dc7a3Sopenharmony_ci
1506cc1dc7a3Sopenharmony_ci		for (unsigned int z = 0; z < dim_z; z++)
1507cc1dc7a3Sopenharmony_ci		{
1508cc1dc7a3Sopenharmony_ci			for (unsigned int y = 1; y < dim_y; y++)
1509cc1dc7a3Sopenharmony_ci			{
1510cc1dc7a3Sopenharmony_ci				row_pointers8[z][y] = row_pointers8[z][0] + dim_x * image_components * y;
1511cc1dc7a3Sopenharmony_ci			}
1512cc1dc7a3Sopenharmony_ci		}
1513cc1dc7a3Sopenharmony_ci
1514cc1dc7a3Sopenharmony_ci		for (unsigned int z = 0; z < dim_z; z++)
1515cc1dc7a3Sopenharmony_ci		{
1516cc1dc7a3Sopenharmony_ci			uint8_t* data8 = static_cast<uint8_t*>(img->data[z]);
1517cc1dc7a3Sopenharmony_ci			for (unsigned int y = 0; y < dim_y; y++)
1518cc1dc7a3Sopenharmony_ci			{
1519cc1dc7a3Sopenharmony_ci				int ym = y_flip ? dim_y - y - 1 : y;
1520cc1dc7a3Sopenharmony_ci				switch (image_components)
1521cc1dc7a3Sopenharmony_ci				{
1522cc1dc7a3Sopenharmony_ci				case 1:		// single-component, treated as Luminance
1523cc1dc7a3Sopenharmony_ci					for (unsigned int x = 0; x < dim_x; x++)
1524cc1dc7a3Sopenharmony_ci					{
1525cc1dc7a3Sopenharmony_ci						row_pointers8[z][y][x] = data8[(4 * dim_x * ym) + (4 * x    )];
1526cc1dc7a3Sopenharmony_ci					}
1527cc1dc7a3Sopenharmony_ci					break;
1528cc1dc7a3Sopenharmony_ci				case 2:		// two-component, treated as Luminance-Alpha
1529cc1dc7a3Sopenharmony_ci					for (unsigned int x = 0; x < dim_x; x++)
1530cc1dc7a3Sopenharmony_ci					{
1531cc1dc7a3Sopenharmony_ci						row_pointers8[z][y][2 * x    ] = data8[(4 * dim_x * ym) + (4 * x    )];
1532cc1dc7a3Sopenharmony_ci						row_pointers8[z][y][2 * x + 1] = data8[(4 * dim_x * ym) + (4 * x + 3)];
1533cc1dc7a3Sopenharmony_ci					}
1534cc1dc7a3Sopenharmony_ci					break;
1535cc1dc7a3Sopenharmony_ci				case 3:		// three-component, treated a
1536cc1dc7a3Sopenharmony_ci					for (unsigned int x = 0; x < dim_x; x++)
1537cc1dc7a3Sopenharmony_ci					{
1538cc1dc7a3Sopenharmony_ci						row_pointers8[z][y][3 * x    ] = data8[(4 * dim_x * ym) + (4 * x    )];
1539cc1dc7a3Sopenharmony_ci						row_pointers8[z][y][3 * x + 1] = data8[(4 * dim_x * ym) + (4 * x + 1)];
1540cc1dc7a3Sopenharmony_ci						row_pointers8[z][y][3 * x + 2] = data8[(4 * dim_x * ym) + (4 * x + 2)];
1541cc1dc7a3Sopenharmony_ci					}
1542cc1dc7a3Sopenharmony_ci					break;
1543cc1dc7a3Sopenharmony_ci				case 4:		// four-component, treated as RGBA
1544cc1dc7a3Sopenharmony_ci					for (unsigned int x = 0; x < dim_x; x++)
1545cc1dc7a3Sopenharmony_ci					{
1546cc1dc7a3Sopenharmony_ci						row_pointers8[z][y][4 * x    ] = data8[(4 * dim_x * ym) + (4 * x    )];
1547cc1dc7a3Sopenharmony_ci						row_pointers8[z][y][4 * x + 1] = data8[(4 * dim_x * ym) + (4 * x + 1)];
1548cc1dc7a3Sopenharmony_ci						row_pointers8[z][y][4 * x + 2] = data8[(4 * dim_x * ym) + (4 * x + 2)];
1549cc1dc7a3Sopenharmony_ci						row_pointers8[z][y][4 * x + 3] = data8[(4 * dim_x * ym) + (4 * x + 3)];
1550cc1dc7a3Sopenharmony_ci					}
1551cc1dc7a3Sopenharmony_ci					break;
1552cc1dc7a3Sopenharmony_ci				}
1553cc1dc7a3Sopenharmony_ci			}
1554cc1dc7a3Sopenharmony_ci		}
1555cc1dc7a3Sopenharmony_ci	}
1556cc1dc7a3Sopenharmony_ci	else						// if bitness == 16
1557cc1dc7a3Sopenharmony_ci	{
1558cc1dc7a3Sopenharmony_ci		row_pointers16 = new uint16_t **[dim_z];
1559cc1dc7a3Sopenharmony_ci		row_pointers16[0] = new uint16_t *[dim_y * dim_z];
1560cc1dc7a3Sopenharmony_ci		row_pointers16[0][0] = new uint16_t[dim_x * dim_y * dim_z * image_components + 1];
1561cc1dc7a3Sopenharmony_ci
1562cc1dc7a3Sopenharmony_ci		for (unsigned int z = 1; z < dim_z; z++)
1563cc1dc7a3Sopenharmony_ci		{
1564cc1dc7a3Sopenharmony_ci			row_pointers16[z] = row_pointers16[0] + dim_y * z;
1565cc1dc7a3Sopenharmony_ci			row_pointers16[z][0] = row_pointers16[0][0] + dim_y * dim_x * image_components * z;
1566cc1dc7a3Sopenharmony_ci		}
1567cc1dc7a3Sopenharmony_ci
1568cc1dc7a3Sopenharmony_ci		for (unsigned int z = 0; z < dim_z; z++)
1569cc1dc7a3Sopenharmony_ci		{
1570cc1dc7a3Sopenharmony_ci			for (unsigned int y = 1; y < dim_y; y++)
1571cc1dc7a3Sopenharmony_ci			{
1572cc1dc7a3Sopenharmony_ci				row_pointers16[z][y] = row_pointers16[z][0] + dim_x * image_components * y;
1573cc1dc7a3Sopenharmony_ci			}
1574cc1dc7a3Sopenharmony_ci		}
1575cc1dc7a3Sopenharmony_ci
1576cc1dc7a3Sopenharmony_ci		for (unsigned int z = 0; z < dim_z; z++)
1577cc1dc7a3Sopenharmony_ci		{
1578cc1dc7a3Sopenharmony_ci			uint16_t* data16 = static_cast<uint16_t*>(img->data[z]);
1579cc1dc7a3Sopenharmony_ci			for (unsigned int y = 0; y < dim_y; y++)
1580cc1dc7a3Sopenharmony_ci			{
1581cc1dc7a3Sopenharmony_ci				int ym = y_flip ? dim_y - y - 1 : y;
1582cc1dc7a3Sopenharmony_ci				switch (image_components)
1583cc1dc7a3Sopenharmony_ci				{
1584cc1dc7a3Sopenharmony_ci				case 1:		// single-component, treated as Luminance
1585cc1dc7a3Sopenharmony_ci					for (unsigned int x = 0; x < dim_x; x++)
1586cc1dc7a3Sopenharmony_ci					{
1587cc1dc7a3Sopenharmony_ci						row_pointers16[z][y][x] = data16[(4 * dim_x * ym) + (4 * x    )];
1588cc1dc7a3Sopenharmony_ci					}
1589cc1dc7a3Sopenharmony_ci					break;
1590cc1dc7a3Sopenharmony_ci				case 2:		// two-component, treated as Luminance-Alpha
1591cc1dc7a3Sopenharmony_ci					for (unsigned int x = 0; x < dim_x; x++)
1592cc1dc7a3Sopenharmony_ci					{
1593cc1dc7a3Sopenharmony_ci						row_pointers16[z][y][2 * x    ] = data16[(4 * dim_x * ym) + (4 * x    )];
1594cc1dc7a3Sopenharmony_ci						row_pointers16[z][y][2 * x + 1] = data16[(4 * dim_x * ym) + (4 * x + 3)];
1595cc1dc7a3Sopenharmony_ci					}
1596cc1dc7a3Sopenharmony_ci					break;
1597cc1dc7a3Sopenharmony_ci				case 3:		// three-component, treated as RGB
1598cc1dc7a3Sopenharmony_ci					for (unsigned int x = 0; x < dim_x; x++)
1599cc1dc7a3Sopenharmony_ci					{
1600cc1dc7a3Sopenharmony_ci						row_pointers16[z][y][3 * x    ] = data16[(4 * dim_x * ym) + (4 * x    )];
1601cc1dc7a3Sopenharmony_ci						row_pointers16[z][y][3 * x + 1] = data16[(4 * dim_x * ym) + (4 * x + 1)];
1602cc1dc7a3Sopenharmony_ci						row_pointers16[z][y][3 * x + 2] = data16[(4 * dim_x * ym) + (4 * x + 2)];
1603cc1dc7a3Sopenharmony_ci					}
1604cc1dc7a3Sopenharmony_ci					break;
1605cc1dc7a3Sopenharmony_ci				case 4:		// four-component, treated as RGBA
1606cc1dc7a3Sopenharmony_ci					for (unsigned int x = 0; x < dim_x; x++)
1607cc1dc7a3Sopenharmony_ci					{
1608cc1dc7a3Sopenharmony_ci						row_pointers16[z][y][4 * x    ] = data16[(4 * dim_x * ym) + (4 * x    )];
1609cc1dc7a3Sopenharmony_ci						row_pointers16[z][y][4 * x + 1] = data16[(4 * dim_x * ym) + (4 * x + 1)];
1610cc1dc7a3Sopenharmony_ci						row_pointers16[z][y][4 * x + 2] = data16[(4 * dim_x * ym) + (4 * x + 2)];
1611cc1dc7a3Sopenharmony_ci						row_pointers16[z][y][4 * x + 3] = data16[(4 * dim_x * ym) + (4 * x + 3)];
1612cc1dc7a3Sopenharmony_ci					}
1613cc1dc7a3Sopenharmony_ci					break;
1614cc1dc7a3Sopenharmony_ci				}
1615cc1dc7a3Sopenharmony_ci			}
1616cc1dc7a3Sopenharmony_ci		}
1617cc1dc7a3Sopenharmony_ci	}
1618cc1dc7a3Sopenharmony_ci
1619cc1dc7a3Sopenharmony_ci	bool retval { true };
1620cc1dc7a3Sopenharmony_ci	uint32_t image_bytes = dim_x * dim_y * dim_z * image_components * (bitness / 8);
1621cc1dc7a3Sopenharmony_ci	uint32_t image_write_bytes = (image_bytes + 3) & ~3;
1622cc1dc7a3Sopenharmony_ci
1623cc1dc7a3Sopenharmony_ci	FILE *wf = fopen(filename, "wb");
1624cc1dc7a3Sopenharmony_ci	if (wf)
1625cc1dc7a3Sopenharmony_ci	{
1626cc1dc7a3Sopenharmony_ci		void* dataptr = (bitness == 16) ?
1627cc1dc7a3Sopenharmony_ci			reinterpret_cast<void*>(row_pointers16[0][0]) :
1628cc1dc7a3Sopenharmony_ci			reinterpret_cast<void*>(row_pointers8[0][0]);
1629cc1dc7a3Sopenharmony_ci
1630cc1dc7a3Sopenharmony_ci		size_t expected_bytes_written = sizeof(ktx_header) + image_write_bytes + 4;
1631cc1dc7a3Sopenharmony_ci		size_t hdr_bytes_written = fwrite(&hdr, 1, sizeof(ktx_header), wf);
1632cc1dc7a3Sopenharmony_ci		size_t bytecount_bytes_written = fwrite(&image_bytes, 1, 4, wf);
1633cc1dc7a3Sopenharmony_ci		size_t data_bytes_written = fwrite(dataptr, 1, image_write_bytes, wf);
1634cc1dc7a3Sopenharmony_ci		fclose(wf);
1635cc1dc7a3Sopenharmony_ci		if (hdr_bytes_written + bytecount_bytes_written + data_bytes_written != expected_bytes_written)
1636cc1dc7a3Sopenharmony_ci		{
1637cc1dc7a3Sopenharmony_ci			retval = false;
1638cc1dc7a3Sopenharmony_ci		}
1639cc1dc7a3Sopenharmony_ci	}
1640cc1dc7a3Sopenharmony_ci	else
1641cc1dc7a3Sopenharmony_ci	{
1642cc1dc7a3Sopenharmony_ci		retval = false;
1643cc1dc7a3Sopenharmony_ci	}
1644cc1dc7a3Sopenharmony_ci
1645cc1dc7a3Sopenharmony_ci	if (row_pointers8)
1646cc1dc7a3Sopenharmony_ci	{
1647cc1dc7a3Sopenharmony_ci		delete[] row_pointers8[0][0];
1648cc1dc7a3Sopenharmony_ci		delete[] row_pointers8[0];
1649cc1dc7a3Sopenharmony_ci		delete[] row_pointers8;
1650cc1dc7a3Sopenharmony_ci	}
1651cc1dc7a3Sopenharmony_ci
1652cc1dc7a3Sopenharmony_ci	if (row_pointers16)
1653cc1dc7a3Sopenharmony_ci	{
1654cc1dc7a3Sopenharmony_ci		delete[] row_pointers16[0][0];
1655cc1dc7a3Sopenharmony_ci		delete[] row_pointers16[0];
1656cc1dc7a3Sopenharmony_ci		delete[] row_pointers16;
1657cc1dc7a3Sopenharmony_ci	}
1658cc1dc7a3Sopenharmony_ci
1659cc1dc7a3Sopenharmony_ci	return retval;
1660cc1dc7a3Sopenharmony_ci}
1661cc1dc7a3Sopenharmony_ci
1662cc1dc7a3Sopenharmony_ci/*
1663cc1dc7a3Sopenharmony_ci	Loader for DDS files.
1664cc1dc7a3Sopenharmony_ci
1665cc1dc7a3Sopenharmony_ci	Note that after the header, data are densely packed with no padding;
1666cc1dc7a3Sopenharmony_ci	in the case of multiple surfaces, they appear one after another in
1667cc1dc7a3Sopenharmony_ci	the file, again with no padding.
1668cc1dc7a3Sopenharmony_ci
1669cc1dc7a3Sopenharmony_ci	This code is NOT endian-neutral.
1670cc1dc7a3Sopenharmony_ci*/
1671cc1dc7a3Sopenharmony_cistruct dds_pixelformat
1672cc1dc7a3Sopenharmony_ci{
1673cc1dc7a3Sopenharmony_ci	uint32_t size;				// structure size, set to 32.
1674cc1dc7a3Sopenharmony_ci	/*
1675cc1dc7a3Sopenharmony_ci	   flags bits are a combination of the following: 0x1 : Texture contains alpha data 0x2 : ---- (older files: texture contains alpha data, for Alpha-only texture) 0x4 : The fourcc field is valid,
1676cc1dc7a3Sopenharmony_ci	   indicating a compressed or DX10 texture format 0x40 : texture contains uncompressed RGB data 0x200 : ---- (YUV in older files) 0x20000 : Texture contains Luminance data (can be combined with
1677cc1dc7a3Sopenharmony_ci	   0x1 for Lum-Alpha) */
1678cc1dc7a3Sopenharmony_ci	uint32_t flags;
1679cc1dc7a3Sopenharmony_ci	uint32_t fourcc;			// "DX10" to indicate a DX10 format, "DXTn" for the DXT formats
1680cc1dc7a3Sopenharmony_ci	uint32_t rgbbitcount;		// number of bits per texel; up to 32 for non-DX10 formats.
1681cc1dc7a3Sopenharmony_ci	uint32_t rbitmask;			// bitmap indicating position of red/luminance color component
1682cc1dc7a3Sopenharmony_ci	uint32_t gbitmask;			// bitmap indicating position of green color component
1683cc1dc7a3Sopenharmony_ci	uint32_t bbitmask;			// bitmap indicating position of blue color component
1684cc1dc7a3Sopenharmony_ci	uint32_t abitmask;			// bitmap indicating position of alpha color component
1685cc1dc7a3Sopenharmony_ci};
1686cc1dc7a3Sopenharmony_ci
1687cc1dc7a3Sopenharmony_cistruct dds_header
1688cc1dc7a3Sopenharmony_ci{
1689cc1dc7a3Sopenharmony_ci	uint32_t size;				// header size; must be exactly 124.
1690cc1dc7a3Sopenharmony_ci	/*
1691cc1dc7a3Sopenharmony_ci	   flag field is an OR or the following bits, that indicate fields containing valid data:
1692cc1dc7a3Sopenharmony_ci		1: caps/caps2/caps3/caps4 (set in all DDS files, ignore on read)
1693cc1dc7a3Sopenharmony_ci		2: height (set in all DDS files, ignore on read)
1694cc1dc7a3Sopenharmony_ci		4: width (set in all DDS files, ignore on read)
1695cc1dc7a3Sopenharmony_ci		8: pitch (for uncompressed texture)
1696cc1dc7a3Sopenharmony_ci		0x1000: the pixel format field (set in all DDS files, ignore on read)
1697cc1dc7a3Sopenharmony_ci		0x20000: mipmap count (for mipmapped textures with >1 level)
1698cc1dc7a3Sopenharmony_ci		0x80000: pitch (for compressed texture)
1699cc1dc7a3Sopenharmony_ci		0x800000: depth (for 3d textures)
1700cc1dc7a3Sopenharmony_ci	*/
1701cc1dc7a3Sopenharmony_ci	uint32_t flags;
1702cc1dc7a3Sopenharmony_ci	uint32_t height;
1703cc1dc7a3Sopenharmony_ci	uint32_t width;
1704cc1dc7a3Sopenharmony_ci	uint32_t pitch_or_linear_size;	// scanline pitch for uncompressed; total size in bytes for compressed
1705cc1dc7a3Sopenharmony_ci	uint32_t depth;
1706cc1dc7a3Sopenharmony_ci	uint32_t mipmapcount;
1707cc1dc7a3Sopenharmony_ci	// unused, set to 0
1708cc1dc7a3Sopenharmony_ci	uint32_t reserved1[11];
1709cc1dc7a3Sopenharmony_ci	dds_pixelformat ddspf;
1710cc1dc7a3Sopenharmony_ci	/*
1711cc1dc7a3Sopenharmony_ci	   caps field is an OR of the following values:
1712cc1dc7a3Sopenharmony_ci		8 : should be set for a file that contains more than 1 surface (ignore on read)
1713cc1dc7a3Sopenharmony_ci		0x400000 : should be set for a mipmapped texture
1714cc1dc7a3Sopenharmony_ci		0x1000 : should be set if the surface is a texture at all (all DDS files, ignore on read)
1715cc1dc7a3Sopenharmony_ci	*/
1716cc1dc7a3Sopenharmony_ci	uint32_t caps;
1717cc1dc7a3Sopenharmony_ci	/*
1718cc1dc7a3Sopenharmony_ci	   caps2 field is an OR of the following values:
1719cc1dc7a3Sopenharmony_ci		0x200 : texture is cubemap
1720cc1dc7a3Sopenharmony_ci		0x400 : +X face of cubemap is present
1721cc1dc7a3Sopenharmony_ci		0x800 : -X face of cubemap is present
1722cc1dc7a3Sopenharmony_ci		0x1000 : +Y face of cubemap is present
1723cc1dc7a3Sopenharmony_ci		0x2000 : -Y face of cubemap is present
1724cc1dc7a3Sopenharmony_ci		0x4000 : +Z face of cubemap is present
1725cc1dc7a3Sopenharmony_ci		0x8000 : -Z face of cubemap is present
1726cc1dc7a3Sopenharmony_ci		0x200000 : texture is a 3d texture.
1727cc1dc7a3Sopenharmony_ci	*/
1728cc1dc7a3Sopenharmony_ci	uint32_t caps2;
1729cc1dc7a3Sopenharmony_ci	// unused, set to 0
1730cc1dc7a3Sopenharmony_ci	uint32_t caps3;
1731cc1dc7a3Sopenharmony_ci	// unused, set to 0
1732cc1dc7a3Sopenharmony_ci	uint32_t caps4;
1733cc1dc7a3Sopenharmony_ci	// unused, set to 0
1734cc1dc7a3Sopenharmony_ci	uint32_t reserved2;
1735cc1dc7a3Sopenharmony_ci};
1736cc1dc7a3Sopenharmony_ci
1737cc1dc7a3Sopenharmony_cistruct dds_header_dx10
1738cc1dc7a3Sopenharmony_ci{
1739cc1dc7a3Sopenharmony_ci	uint32_t dxgi_format;
1740cc1dc7a3Sopenharmony_ci	uint32_t resource_dimension;	// 2=1d-texture, 3=2d-texture or cubemap, 4=3d-texture
1741cc1dc7a3Sopenharmony_ci	uint32_t misc_flag;			// 4 if cubemap, else 0
1742cc1dc7a3Sopenharmony_ci	uint32_t array_size;		// size of array in case of a texture array; set to 1 for a non-array
1743cc1dc7a3Sopenharmony_ci	uint32_t reserved;			// set to 0.
1744cc1dc7a3Sopenharmony_ci};
1745cc1dc7a3Sopenharmony_ci
1746cc1dc7a3Sopenharmony_ci#define DDS_MAGIC 0x20534444
1747cc1dc7a3Sopenharmony_ci#define DX10_MAGIC 0x30315844
1748cc1dc7a3Sopenharmony_ci
1749cc1dc7a3Sopenharmony_ci/**
1750cc1dc7a3Sopenharmony_ci * @brief Load an uncompressed DDS image using the local custom loader.
1751cc1dc7a3Sopenharmony_ci *
1752cc1dc7a3Sopenharmony_ci * @param      filename          The name of the file to load.
1753cc1dc7a3Sopenharmony_ci * @param      y_flip            Should the image be vertically flipped?
1754cc1dc7a3Sopenharmony_ci * @param[out] is_hdr            Is this an HDR image load?
1755cc1dc7a3Sopenharmony_ci * @param[out] component_count   The number of components in the data.
1756cc1dc7a3Sopenharmony_ci *
1757cc1dc7a3Sopenharmony_ci * @return The loaded image data in a canonical 4 channel format, or @c nullptr on error.
1758cc1dc7a3Sopenharmony_ci */
1759cc1dc7a3Sopenharmony_cistatic astcenc_image* load_dds_uncompressed_image(
1760cc1dc7a3Sopenharmony_ci	const char* filename,
1761cc1dc7a3Sopenharmony_ci	bool y_flip,
1762cc1dc7a3Sopenharmony_ci	bool& is_hdr,
1763cc1dc7a3Sopenharmony_ci	unsigned int& component_count
1764cc1dc7a3Sopenharmony_ci) {
1765cc1dc7a3Sopenharmony_ci	FILE *f = fopen(filename, "rb");
1766cc1dc7a3Sopenharmony_ci	if (!f)
1767cc1dc7a3Sopenharmony_ci	{
1768cc1dc7a3Sopenharmony_ci		printf("Failed to open file %s\n", filename);
1769cc1dc7a3Sopenharmony_ci		return nullptr;
1770cc1dc7a3Sopenharmony_ci	}
1771cc1dc7a3Sopenharmony_ci
1772cc1dc7a3Sopenharmony_ci	uint8_t magic[4];
1773cc1dc7a3Sopenharmony_ci
1774cc1dc7a3Sopenharmony_ci	dds_header hdr;
1775cc1dc7a3Sopenharmony_ci	size_t magic_bytes_read = fread(magic, 1, 4, f);
1776cc1dc7a3Sopenharmony_ci	size_t header_bytes_read = fread(&hdr, 1, sizeof(hdr), f);
1777cc1dc7a3Sopenharmony_ci	if (magic_bytes_read != 4 || header_bytes_read != sizeof(hdr))
1778cc1dc7a3Sopenharmony_ci	{
1779cc1dc7a3Sopenharmony_ci		printf("Failed to read header of DDS file %s\n", filename);
1780cc1dc7a3Sopenharmony_ci		fclose(f);
1781cc1dc7a3Sopenharmony_ci		return nullptr;
1782cc1dc7a3Sopenharmony_ci	}
1783cc1dc7a3Sopenharmony_ci
1784cc1dc7a3Sopenharmony_ci	uint32_t magicx = magic[0] | (magic[1] << 8) | (magic[2] << 16) | (magic[3] << 24);
1785cc1dc7a3Sopenharmony_ci
1786cc1dc7a3Sopenharmony_ci	if (magicx != DDS_MAGIC || hdr.size != 124)
1787cc1dc7a3Sopenharmony_ci	{
1788cc1dc7a3Sopenharmony_ci		printf("File %s does not have a valid DDS header\n", filename);
1789cc1dc7a3Sopenharmony_ci		fclose(f);
1790cc1dc7a3Sopenharmony_ci		return nullptr;
1791cc1dc7a3Sopenharmony_ci	}
1792cc1dc7a3Sopenharmony_ci
1793cc1dc7a3Sopenharmony_ci	int use_dx10_header = 0;
1794cc1dc7a3Sopenharmony_ci	if (hdr.ddspf.flags & 4)
1795cc1dc7a3Sopenharmony_ci	{
1796cc1dc7a3Sopenharmony_ci		if (hdr.ddspf.fourcc == DX10_MAGIC)
1797cc1dc7a3Sopenharmony_ci		{
1798cc1dc7a3Sopenharmony_ci			use_dx10_header = 1;
1799cc1dc7a3Sopenharmony_ci		}
1800cc1dc7a3Sopenharmony_ci		else
1801cc1dc7a3Sopenharmony_ci		{
1802cc1dc7a3Sopenharmony_ci			printf("DDS file %s is compressed, not supported\n", filename);
1803cc1dc7a3Sopenharmony_ci			fclose(f);
1804cc1dc7a3Sopenharmony_ci			return nullptr;
1805cc1dc7a3Sopenharmony_ci		}
1806cc1dc7a3Sopenharmony_ci	}
1807cc1dc7a3Sopenharmony_ci
1808cc1dc7a3Sopenharmony_ci	dds_header_dx10 dx10_header;
1809cc1dc7a3Sopenharmony_ci	if (use_dx10_header)
1810cc1dc7a3Sopenharmony_ci	{
1811cc1dc7a3Sopenharmony_ci		size_t dx10_header_bytes_read = fread(&dx10_header, 1, sizeof(dx10_header), f);
1812cc1dc7a3Sopenharmony_ci		if (dx10_header_bytes_read != sizeof(dx10_header))
1813cc1dc7a3Sopenharmony_ci		{
1814cc1dc7a3Sopenharmony_ci			printf("Failed to read header of DDS file %s\n", filename);
1815cc1dc7a3Sopenharmony_ci			fclose(f);
1816cc1dc7a3Sopenharmony_ci			return nullptr;
1817cc1dc7a3Sopenharmony_ci		}
1818cc1dc7a3Sopenharmony_ci	}
1819cc1dc7a3Sopenharmony_ci
1820cc1dc7a3Sopenharmony_ci	unsigned int dim_x = hdr.width;
1821cc1dc7a3Sopenharmony_ci	unsigned int dim_y = hdr.height;
1822cc1dc7a3Sopenharmony_ci	unsigned int dim_z = (hdr.flags & 0x800000) ? hdr.depth : 1;
1823cc1dc7a3Sopenharmony_ci
1824cc1dc7a3Sopenharmony_ci	// The bitcount that we will use internally in the codec
1825cc1dc7a3Sopenharmony_ci	int bitness = 0;
1826cc1dc7a3Sopenharmony_ci
1827cc1dc7a3Sopenharmony_ci	// The bytes per component in the DDS file itself
1828cc1dc7a3Sopenharmony_ci	int bytes_per_component = 0;
1829cc1dc7a3Sopenharmony_ci	int components = 0;
1830cc1dc7a3Sopenharmony_ci	scanline_transfer copy_method = R8_TO_RGBA8;
1831cc1dc7a3Sopenharmony_ci
1832cc1dc7a3Sopenharmony_ci	// figure out the format actually used in the DDS file.
1833cc1dc7a3Sopenharmony_ci	if (use_dx10_header)
1834cc1dc7a3Sopenharmony_ci	{
1835cc1dc7a3Sopenharmony_ci		// DX10 header present; use the DXGI format.
1836cc1dc7a3Sopenharmony_ci		#define DXGI_FORMAT_R32G32B32A32_FLOAT   2
1837cc1dc7a3Sopenharmony_ci		#define DXGI_FORMAT_R32G32B32_FLOAT      6
1838cc1dc7a3Sopenharmony_ci		#define DXGI_FORMAT_R16G16B16A16_FLOAT  10
1839cc1dc7a3Sopenharmony_ci		#define DXGI_FORMAT_R16G16B16A16_UNORM  11
1840cc1dc7a3Sopenharmony_ci		#define DXGI_FORMAT_R32G32_FLOAT        16
1841cc1dc7a3Sopenharmony_ci		#define DXGI_FORMAT_R8G8B8A8_UNORM      28
1842cc1dc7a3Sopenharmony_ci		#define DXGI_FORMAT_R16G16_FLOAT    34
1843cc1dc7a3Sopenharmony_ci		#define DXGI_FORMAT_R16G16_UNORM    35
1844cc1dc7a3Sopenharmony_ci		#define DXGI_FORMAT_R32_FLOAT       41
1845cc1dc7a3Sopenharmony_ci		#define DXGI_FORMAT_R8G8_UNORM      49
1846cc1dc7a3Sopenharmony_ci		#define DXGI_FORMAT_R16_FLOAT       54
1847cc1dc7a3Sopenharmony_ci		#define DXGI_FORMAT_R16_UNORM       56
1848cc1dc7a3Sopenharmony_ci		#define DXGI_FORMAT_R8_UNORM        61
1849cc1dc7a3Sopenharmony_ci		#define DXGI_FORMAT_B8G8R8A8_UNORM  86
1850cc1dc7a3Sopenharmony_ci		#define DXGI_FORMAT_B8G8R8X8_UNORM  87
1851cc1dc7a3Sopenharmony_ci
1852cc1dc7a3Sopenharmony_ci		struct dxgi_params
1853cc1dc7a3Sopenharmony_ci		{
1854cc1dc7a3Sopenharmony_ci			int bitness;
1855cc1dc7a3Sopenharmony_ci			int bytes_per_component;
1856cc1dc7a3Sopenharmony_ci			int components;
1857cc1dc7a3Sopenharmony_ci			scanline_transfer copy_method;
1858cc1dc7a3Sopenharmony_ci			uint32_t dxgi_format_number;
1859cc1dc7a3Sopenharmony_ci		};
1860cc1dc7a3Sopenharmony_ci
1861cc1dc7a3Sopenharmony_ci		static const dxgi_params format_params[] {
1862cc1dc7a3Sopenharmony_ci			{16, 4, 4, RGBA32F_TO_RGBA16F, DXGI_FORMAT_R32G32B32A32_FLOAT},
1863cc1dc7a3Sopenharmony_ci			{16, 4, 3, RGB32F_TO_RGBA16F, DXGI_FORMAT_R32G32B32_FLOAT},
1864cc1dc7a3Sopenharmony_ci			{16, 2, 4, RGBA16F_TO_RGBA16F, DXGI_FORMAT_R16G16B16A16_FLOAT},
1865cc1dc7a3Sopenharmony_ci			{16, 2, 4, RGBA16_TO_RGBA16F, DXGI_FORMAT_R16G16B16A16_UNORM},
1866cc1dc7a3Sopenharmony_ci			{16, 4, 2, RG32F_TO_RGBA16F, DXGI_FORMAT_R32G32_FLOAT},
1867cc1dc7a3Sopenharmony_ci			{8, 1, 4, RGBA8_TO_RGBA8, DXGI_FORMAT_R8G8B8A8_UNORM},
1868cc1dc7a3Sopenharmony_ci			{16, 2, 2, RG16F_TO_RGBA16F, DXGI_FORMAT_R16G16_FLOAT},
1869cc1dc7a3Sopenharmony_ci			{16, 2, 2, RG16_TO_RGBA16F, DXGI_FORMAT_R16G16_UNORM},
1870cc1dc7a3Sopenharmony_ci			{16, 4, 1, R32F_TO_RGBA16F, DXGI_FORMAT_R32_FLOAT},
1871cc1dc7a3Sopenharmony_ci			{8, 1, 2, RG8_TO_RGBA8, DXGI_FORMAT_R8G8_UNORM},
1872cc1dc7a3Sopenharmony_ci			{16, 2, 1, R16F_TO_RGBA16F, DXGI_FORMAT_R16_FLOAT},
1873cc1dc7a3Sopenharmony_ci			{16, 2, 1, R16_TO_RGBA16F, DXGI_FORMAT_R16_UNORM},
1874cc1dc7a3Sopenharmony_ci			{8, 1, 1, R8_TO_RGBA8, DXGI_FORMAT_R8_UNORM},
1875cc1dc7a3Sopenharmony_ci			{8, 1, 4, BGRA8_TO_RGBA8, DXGI_FORMAT_B8G8R8A8_UNORM},
1876cc1dc7a3Sopenharmony_ci			{8, 1, 4, BGRX8_TO_RGBA8, DXGI_FORMAT_B8G8R8X8_UNORM},
1877cc1dc7a3Sopenharmony_ci		};
1878cc1dc7a3Sopenharmony_ci
1879cc1dc7a3Sopenharmony_ci		int dxgi_modes_supported = sizeof(format_params) / sizeof(format_params[0]);
1880cc1dc7a3Sopenharmony_ci		int did_select_format = 0;
1881cc1dc7a3Sopenharmony_ci		for (int i = 0; i < dxgi_modes_supported; i++)
1882cc1dc7a3Sopenharmony_ci		{
1883cc1dc7a3Sopenharmony_ci			if (dx10_header.dxgi_format == format_params[i].dxgi_format_number)
1884cc1dc7a3Sopenharmony_ci			{
1885cc1dc7a3Sopenharmony_ci				bitness = format_params[i].bitness;
1886cc1dc7a3Sopenharmony_ci				bytes_per_component = format_params[i].bytes_per_component;
1887cc1dc7a3Sopenharmony_ci				components = format_params[i].components;
1888cc1dc7a3Sopenharmony_ci				copy_method = format_params[i].copy_method;
1889cc1dc7a3Sopenharmony_ci				did_select_format = 1;
1890cc1dc7a3Sopenharmony_ci				break;
1891cc1dc7a3Sopenharmony_ci			}
1892cc1dc7a3Sopenharmony_ci		}
1893cc1dc7a3Sopenharmony_ci
1894cc1dc7a3Sopenharmony_ci		if (!did_select_format)
1895cc1dc7a3Sopenharmony_ci		{
1896cc1dc7a3Sopenharmony_ci			printf("DDS file %s: DXGI format not supported by codec\n", filename);
1897cc1dc7a3Sopenharmony_ci			fclose(f);
1898cc1dc7a3Sopenharmony_ci			return nullptr;
1899cc1dc7a3Sopenharmony_ci		}
1900cc1dc7a3Sopenharmony_ci	}
1901cc1dc7a3Sopenharmony_ci	else
1902cc1dc7a3Sopenharmony_ci	{
1903cc1dc7a3Sopenharmony_ci		// No DX10 header present. Then try to match the bitcount and bitmask against
1904cc1dc7a3Sopenharmony_ci		// a set of prepared patterns.
1905cc1dc7a3Sopenharmony_ci		uint32_t flags = hdr.ddspf.flags;
1906cc1dc7a3Sopenharmony_ci		uint32_t bitcount = hdr.ddspf.rgbbitcount;
1907cc1dc7a3Sopenharmony_ci		uint32_t rmask = hdr.ddspf.rbitmask;
1908cc1dc7a3Sopenharmony_ci		uint32_t gmask = hdr.ddspf.gbitmask;
1909cc1dc7a3Sopenharmony_ci		uint32_t bmask = hdr.ddspf.bbitmask;
1910cc1dc7a3Sopenharmony_ci		uint32_t amask = hdr.ddspf.abitmask;
1911cc1dc7a3Sopenharmony_ci
1912cc1dc7a3Sopenharmony_ci		// RGBA-unorm8
1913cc1dc7a3Sopenharmony_ci		if ((flags & 0x41) == 0x41 && bitcount == 32 && rmask == 0xFF && gmask == 0xFF00 && bmask == 0xFF0000 && amask == 0xFF000000)
1914cc1dc7a3Sopenharmony_ci		{
1915cc1dc7a3Sopenharmony_ci			bytes_per_component = 1;
1916cc1dc7a3Sopenharmony_ci			components = 4;
1917cc1dc7a3Sopenharmony_ci			copy_method = RGBA8_TO_RGBA8;
1918cc1dc7a3Sopenharmony_ci		}
1919cc1dc7a3Sopenharmony_ci		// BGRA-unorm8
1920cc1dc7a3Sopenharmony_ci		else if ((flags & 0x41) == 0x41 && bitcount == 32 && rmask == 0xFF0000 && gmask == 0xFF00 && bmask == 0xFF && amask == 0xFF000000)
1921cc1dc7a3Sopenharmony_ci		{
1922cc1dc7a3Sopenharmony_ci			bytes_per_component = 1;
1923cc1dc7a3Sopenharmony_ci			components = 4;
1924cc1dc7a3Sopenharmony_ci			copy_method = BGRA8_TO_RGBA8;
1925cc1dc7a3Sopenharmony_ci		}
1926cc1dc7a3Sopenharmony_ci		// RGBX-unorm8
1927cc1dc7a3Sopenharmony_ci		else if ((flags & 0x40) && bitcount == 32 && rmask == 0xFF && gmask == 0xFF00 && bmask == 0xFF0000)
1928cc1dc7a3Sopenharmony_ci		{
1929cc1dc7a3Sopenharmony_ci			bytes_per_component = 1;
1930cc1dc7a3Sopenharmony_ci			components = 4;
1931cc1dc7a3Sopenharmony_ci			copy_method = RGBX8_TO_RGBA8;
1932cc1dc7a3Sopenharmony_ci		}
1933cc1dc7a3Sopenharmony_ci		// BGRX-unorm8
1934cc1dc7a3Sopenharmony_ci		else if ((flags & 0x40) && bitcount == 32 && rmask == 0xFF0000 && gmask == 0xFF00 && bmask == 0xFF)
1935cc1dc7a3Sopenharmony_ci		{
1936cc1dc7a3Sopenharmony_ci			bytes_per_component = 1;
1937cc1dc7a3Sopenharmony_ci			components = 4;
1938cc1dc7a3Sopenharmony_ci			copy_method = BGRX8_TO_RGBA8;
1939cc1dc7a3Sopenharmony_ci		}
1940cc1dc7a3Sopenharmony_ci		// RGB-unorm8
1941cc1dc7a3Sopenharmony_ci		else if ((flags & 0x40) && bitcount == 24 && rmask == 0xFF && gmask == 0xFF00 && bmask == 0xFF0000)
1942cc1dc7a3Sopenharmony_ci		{
1943cc1dc7a3Sopenharmony_ci			bytes_per_component = 1;
1944cc1dc7a3Sopenharmony_ci			components = 3;
1945cc1dc7a3Sopenharmony_ci			copy_method = RGB8_TO_RGBA8;
1946cc1dc7a3Sopenharmony_ci		}
1947cc1dc7a3Sopenharmony_ci		// BGR-unorm8
1948cc1dc7a3Sopenharmony_ci		else if ((flags & 0x40) && bitcount == 24 && rmask == 0xFF0000 && gmask == 0xFF00 && bmask == 0xFF)
1949cc1dc7a3Sopenharmony_ci		{
1950cc1dc7a3Sopenharmony_ci			bytes_per_component = 1;
1951cc1dc7a3Sopenharmony_ci			components = 3;
1952cc1dc7a3Sopenharmony_ci			copy_method = BGR8_TO_RGBA8;
1953cc1dc7a3Sopenharmony_ci		}
1954cc1dc7a3Sopenharmony_ci		// RG-unorm16
1955cc1dc7a3Sopenharmony_ci		else if ((flags & 0x40) && bitcount == 16 && rmask == 0xFFFF && gmask == 0xFFFF0000)
1956cc1dc7a3Sopenharmony_ci		{
1957cc1dc7a3Sopenharmony_ci			bytes_per_component = 2;
1958cc1dc7a3Sopenharmony_ci			components = 2;
1959cc1dc7a3Sopenharmony_ci			copy_method = RG16_TO_RGBA16F;
1960cc1dc7a3Sopenharmony_ci		}
1961cc1dc7a3Sopenharmony_ci		// A8L8
1962cc1dc7a3Sopenharmony_ci		else if ((flags & 0x20001) == 0x20001 && bitcount == 16 && rmask == 0xFF && amask == 0xFF00)
1963cc1dc7a3Sopenharmony_ci		{
1964cc1dc7a3Sopenharmony_ci			bytes_per_component = 1;
1965cc1dc7a3Sopenharmony_ci			components = 2;
1966cc1dc7a3Sopenharmony_ci			copy_method = LA8_TO_RGBA8;
1967cc1dc7a3Sopenharmony_ci		}
1968cc1dc7a3Sopenharmony_ci		// L8
1969cc1dc7a3Sopenharmony_ci		else if ((flags & 0x20000) && bitcount == 8 && rmask == 0xFF)
1970cc1dc7a3Sopenharmony_ci		{
1971cc1dc7a3Sopenharmony_ci			bytes_per_component = 1;
1972cc1dc7a3Sopenharmony_ci			components = 1;
1973cc1dc7a3Sopenharmony_ci			copy_method = L8_TO_RGBA8;
1974cc1dc7a3Sopenharmony_ci		}
1975cc1dc7a3Sopenharmony_ci		// L16
1976cc1dc7a3Sopenharmony_ci		else if ((flags & 0x20000) && bitcount == 16 && rmask == 0xFFFF)
1977cc1dc7a3Sopenharmony_ci		{
1978cc1dc7a3Sopenharmony_ci			bytes_per_component = 2;
1979cc1dc7a3Sopenharmony_ci			components = 1;
1980cc1dc7a3Sopenharmony_ci			copy_method = L16_TO_RGBA16F;
1981cc1dc7a3Sopenharmony_ci		}
1982cc1dc7a3Sopenharmony_ci		else
1983cc1dc7a3Sopenharmony_ci		{
1984cc1dc7a3Sopenharmony_ci			printf("DDS file %s: Non-DXGI format not supported by codec\n", filename);
1985cc1dc7a3Sopenharmony_ci			fclose(f);
1986cc1dc7a3Sopenharmony_ci			return nullptr;
1987cc1dc7a3Sopenharmony_ci		}
1988cc1dc7a3Sopenharmony_ci
1989cc1dc7a3Sopenharmony_ci		bitness = bytes_per_component * 8;
1990cc1dc7a3Sopenharmony_ci	}
1991cc1dc7a3Sopenharmony_ci
1992cc1dc7a3Sopenharmony_ci	// then, load the actual file.
1993cc1dc7a3Sopenharmony_ci	uint32_t xstride = bytes_per_component * components * dim_x;
1994cc1dc7a3Sopenharmony_ci	uint32_t ystride = xstride * dim_y;
1995cc1dc7a3Sopenharmony_ci	uint32_t bytes_of_surface = ystride * dim_z;
1996cc1dc7a3Sopenharmony_ci
1997cc1dc7a3Sopenharmony_ci	uint8_t *buf = new uint8_t[bytes_of_surface];
1998cc1dc7a3Sopenharmony_ci	size_t bytes_read = fread(buf, 1, bytes_of_surface, f);
1999cc1dc7a3Sopenharmony_ci	fclose(f);
2000cc1dc7a3Sopenharmony_ci	if (bytes_read != bytes_of_surface)
2001cc1dc7a3Sopenharmony_ci	{
2002cc1dc7a3Sopenharmony_ci		delete[] buf;
2003cc1dc7a3Sopenharmony_ci		printf("Failed to read file %s\n", filename);
2004cc1dc7a3Sopenharmony_ci		return nullptr;
2005cc1dc7a3Sopenharmony_ci	}
2006cc1dc7a3Sopenharmony_ci
2007cc1dc7a3Sopenharmony_ci	// then transfer data from the surface to our own image-data-structure.
2008cc1dc7a3Sopenharmony_ci	astcenc_image *astc_img = alloc_image(bitness, dim_x, dim_y, dim_z);
2009cc1dc7a3Sopenharmony_ci
2010cc1dc7a3Sopenharmony_ci	for (unsigned int z = 0; z < dim_z; z++)
2011cc1dc7a3Sopenharmony_ci	{
2012cc1dc7a3Sopenharmony_ci		for (unsigned int y = 0; y < dim_y; y++)
2013cc1dc7a3Sopenharmony_ci		{
2014cc1dc7a3Sopenharmony_ci			unsigned int ymod = y_flip ? dim_y - y - 1 : y;
2015cc1dc7a3Sopenharmony_ci			unsigned int ydst = ymod;
2016cc1dc7a3Sopenharmony_ci			void* dst;
2017cc1dc7a3Sopenharmony_ci
2018cc1dc7a3Sopenharmony_ci			if (astc_img->data_type == ASTCENC_TYPE_U8)
2019cc1dc7a3Sopenharmony_ci			{
2020cc1dc7a3Sopenharmony_ci				uint8_t* data8 = static_cast<uint8_t*>(astc_img->data[z]);
2021cc1dc7a3Sopenharmony_ci				dst = static_cast<void*>(&data8[4 * dim_x * ydst]);
2022cc1dc7a3Sopenharmony_ci			}
2023cc1dc7a3Sopenharmony_ci			else // if (astc_img->data_type == ASTCENC_TYPE_F16)
2024cc1dc7a3Sopenharmony_ci			{
2025cc1dc7a3Sopenharmony_ci				assert(astc_img->data_type == ASTCENC_TYPE_F16);
2026cc1dc7a3Sopenharmony_ci				uint16_t* data16 = static_cast<uint16_t*>(astc_img->data[z]);
2027cc1dc7a3Sopenharmony_ci				dst = static_cast<void*>(&data16[4 * dim_x * ydst]);
2028cc1dc7a3Sopenharmony_ci			}
2029cc1dc7a3Sopenharmony_ci
2030cc1dc7a3Sopenharmony_ci			uint8_t *src = buf + (z * ystride) + (y * xstride);
2031cc1dc7a3Sopenharmony_ci			copy_scanline(dst, src, dim_x, copy_method);
2032cc1dc7a3Sopenharmony_ci		}
2033cc1dc7a3Sopenharmony_ci	}
2034cc1dc7a3Sopenharmony_ci
2035cc1dc7a3Sopenharmony_ci	delete[] buf;
2036cc1dc7a3Sopenharmony_ci	is_hdr = bitness >= 16;
2037cc1dc7a3Sopenharmony_ci	component_count = components;
2038cc1dc7a3Sopenharmony_ci	return astc_img;
2039cc1dc7a3Sopenharmony_ci}
2040cc1dc7a3Sopenharmony_ci
2041cc1dc7a3Sopenharmony_ci/**
2042cc1dc7a3Sopenharmony_ci * @brief Save a DDS uncompressed image using a local store routine.
2043cc1dc7a3Sopenharmony_ci *
2044cc1dc7a3Sopenharmony_ci * @param img        The source data for the image.
2045cc1dc7a3Sopenharmony_ci * @param filename   The name of the file to save.
2046cc1dc7a3Sopenharmony_ci * @param y_flip     Should the image be vertically flipped?
2047cc1dc7a3Sopenharmony_ci *
2048cc1dc7a3Sopenharmony_ci * @return @c true if the image saved OK, @c false on error.
2049cc1dc7a3Sopenharmony_ci */
2050cc1dc7a3Sopenharmony_cistatic bool store_dds_uncompressed_image(
2051cc1dc7a3Sopenharmony_ci	const astcenc_image* img,
2052cc1dc7a3Sopenharmony_ci	const char* filename,
2053cc1dc7a3Sopenharmony_ci	int y_flip
2054cc1dc7a3Sopenharmony_ci) {
2055cc1dc7a3Sopenharmony_ci	unsigned int dim_x = img->dim_x;
2056cc1dc7a3Sopenharmony_ci	unsigned int dim_y = img->dim_y;
2057cc1dc7a3Sopenharmony_ci	unsigned int dim_z = img->dim_z;
2058cc1dc7a3Sopenharmony_ci
2059cc1dc7a3Sopenharmony_ci	int bitness = img->data_type == ASTCENC_TYPE_U8 ? 8 : 16;
2060cc1dc7a3Sopenharmony_ci	int image_components = (bitness == 16) ? 4 : determine_image_components(img);
2061cc1dc7a3Sopenharmony_ci
2062cc1dc7a3Sopenharmony_ci	// DDS-pixel-format structures to use when storing LDR image with 1,2,3 or 4 components.
2063cc1dc7a3Sopenharmony_ci	static const dds_pixelformat format_of_image_components[4] =
2064cc1dc7a3Sopenharmony_ci	{
2065cc1dc7a3Sopenharmony_ci		{32, 0x20000, 0, 8, 0xFF, 0, 0, 0},	// luminance
2066cc1dc7a3Sopenharmony_ci		{32, 0x20001, 0, 16, 0xFF, 0, 0, 0xFF00},	// L8A8
2067cc1dc7a3Sopenharmony_ci		{32, 0x40, 0, 24, 0xFF, 0xFF00, 0xFF0000, 0},	// RGB8
2068cc1dc7a3Sopenharmony_ci		{32, 0x41, 0, 32, 0xFF, 0xFF00, 0xFF0000, 0xFF000000}	// RGBA8
2069cc1dc7a3Sopenharmony_ci	};
2070cc1dc7a3Sopenharmony_ci
2071cc1dc7a3Sopenharmony_ci	// DDS-pixel-format structures to use when storing HDR image.
2072cc1dc7a3Sopenharmony_ci	static const dds_pixelformat dxt10_diverter =
2073cc1dc7a3Sopenharmony_ci	{
2074cc1dc7a3Sopenharmony_ci		32, 4, DX10_MAGIC, 0, 0, 0, 0, 0
2075cc1dc7a3Sopenharmony_ci	};
2076cc1dc7a3Sopenharmony_ci
2077cc1dc7a3Sopenharmony_ci	// Header handling; will write:
2078cc1dc7a3Sopenharmony_ci	// * DDS magic value
2079cc1dc7a3Sopenharmony_ci	// * DDS header
2080cc1dc7a3Sopenharmony_ci	// * DDS DX10 header, if the file is floating-point
2081cc1dc7a3Sopenharmony_ci	// * pixel data
2082cc1dc7a3Sopenharmony_ci
2083cc1dc7a3Sopenharmony_ci	// Main header data
2084cc1dc7a3Sopenharmony_ci	dds_header hdr;
2085cc1dc7a3Sopenharmony_ci	hdr.size = 124;
2086cc1dc7a3Sopenharmony_ci	hdr.flags = 0x100F | (dim_z > 1 ? 0x800000 : 0);
2087cc1dc7a3Sopenharmony_ci	hdr.height = dim_y;
2088cc1dc7a3Sopenharmony_ci	hdr.width = dim_x;
2089cc1dc7a3Sopenharmony_ci	hdr.pitch_or_linear_size = image_components * (bitness / 8) * dim_x;
2090cc1dc7a3Sopenharmony_ci	hdr.depth = dim_z;
2091cc1dc7a3Sopenharmony_ci	hdr.mipmapcount = 1;
2092cc1dc7a3Sopenharmony_ci	for (unsigned int i = 0; i < 11; i++)
2093cc1dc7a3Sopenharmony_ci	{
2094cc1dc7a3Sopenharmony_ci		hdr.reserved1[i] = 0;
2095cc1dc7a3Sopenharmony_ci	}
2096cc1dc7a3Sopenharmony_ci	hdr.caps = 0x1000;
2097cc1dc7a3Sopenharmony_ci	hdr.caps2 = (dim_z > 1) ? 0x200000 : 0;
2098cc1dc7a3Sopenharmony_ci	hdr.caps3 = 0;
2099cc1dc7a3Sopenharmony_ci	hdr.caps4 = 0;
2100cc1dc7a3Sopenharmony_ci
2101cc1dc7a3Sopenharmony_ci	// Pixel-format data
2102cc1dc7a3Sopenharmony_ci	if (bitness == 8)
2103cc1dc7a3Sopenharmony_ci	{
2104cc1dc7a3Sopenharmony_ci		hdr.ddspf = format_of_image_components[image_components - 1];
2105cc1dc7a3Sopenharmony_ci	}
2106cc1dc7a3Sopenharmony_ci	else
2107cc1dc7a3Sopenharmony_ci	{
2108cc1dc7a3Sopenharmony_ci		hdr.ddspf = dxt10_diverter;
2109cc1dc7a3Sopenharmony_ci	}
2110cc1dc7a3Sopenharmony_ci
2111cc1dc7a3Sopenharmony_ci	// DX10 data
2112cc1dc7a3Sopenharmony_ci	dds_header_dx10 dx10;
2113cc1dc7a3Sopenharmony_ci	dx10.dxgi_format = DXGI_FORMAT_R16G16B16A16_FLOAT;
2114cc1dc7a3Sopenharmony_ci	dx10.resource_dimension = (dim_z > 1) ? 4 : 3;
2115cc1dc7a3Sopenharmony_ci	dx10.misc_flag = 0;
2116cc1dc7a3Sopenharmony_ci	dx10.array_size = 1;
2117cc1dc7a3Sopenharmony_ci	dx10.reserved = 0;
2118cc1dc7a3Sopenharmony_ci
2119cc1dc7a3Sopenharmony_ci	// Collect image data to write
2120cc1dc7a3Sopenharmony_ci	uint8_t ***row_pointers8 = nullptr;
2121cc1dc7a3Sopenharmony_ci	uint16_t ***row_pointers16 = nullptr;
2122cc1dc7a3Sopenharmony_ci
2123cc1dc7a3Sopenharmony_ci	if (bitness == 8)
2124cc1dc7a3Sopenharmony_ci	{
2125cc1dc7a3Sopenharmony_ci		row_pointers8 = new uint8_t **[dim_z];
2126cc1dc7a3Sopenharmony_ci		row_pointers8[0] = new uint8_t *[dim_y * dim_z];
2127cc1dc7a3Sopenharmony_ci		row_pointers8[0][0] = new uint8_t[dim_x * dim_y * dim_z * image_components];
2128cc1dc7a3Sopenharmony_ci
2129cc1dc7a3Sopenharmony_ci		for (unsigned int z = 1; z < dim_z; z++)
2130cc1dc7a3Sopenharmony_ci		{
2131cc1dc7a3Sopenharmony_ci			row_pointers8[z] = row_pointers8[0] + dim_y * z;
2132cc1dc7a3Sopenharmony_ci			row_pointers8[z][0] = row_pointers8[0][0] + dim_y * dim_z * image_components * z;
2133cc1dc7a3Sopenharmony_ci		}
2134cc1dc7a3Sopenharmony_ci
2135cc1dc7a3Sopenharmony_ci		for (unsigned int z = 0; z < dim_z; z++)
2136cc1dc7a3Sopenharmony_ci		{
2137cc1dc7a3Sopenharmony_ci			for (unsigned int y = 1; y < dim_y; y++)
2138cc1dc7a3Sopenharmony_ci			{
2139cc1dc7a3Sopenharmony_ci				row_pointers8[z][y] = row_pointers8[z][0] + dim_x * image_components * y;
2140cc1dc7a3Sopenharmony_ci			}
2141cc1dc7a3Sopenharmony_ci		}
2142cc1dc7a3Sopenharmony_ci
2143cc1dc7a3Sopenharmony_ci		for (unsigned int z = 0; z < dim_z; z++)
2144cc1dc7a3Sopenharmony_ci		{
2145cc1dc7a3Sopenharmony_ci			uint8_t* data8 = static_cast<uint8_t*>(img->data[z]);
2146cc1dc7a3Sopenharmony_ci
2147cc1dc7a3Sopenharmony_ci			for (unsigned int y = 0; y < dim_y; y++)
2148cc1dc7a3Sopenharmony_ci			{
2149cc1dc7a3Sopenharmony_ci				int ym = y_flip ? dim_y - y - 1 : y;
2150cc1dc7a3Sopenharmony_ci				switch (image_components)
2151cc1dc7a3Sopenharmony_ci				{
2152cc1dc7a3Sopenharmony_ci				case 1:		// single-component, treated as Luminance
2153cc1dc7a3Sopenharmony_ci					for (unsigned int x = 0; x < dim_x; x++)
2154cc1dc7a3Sopenharmony_ci					{
2155cc1dc7a3Sopenharmony_ci						row_pointers8[z][y][x] = data8[(4 * dim_x * ym) + (4 * x    )];
2156cc1dc7a3Sopenharmony_ci					}
2157cc1dc7a3Sopenharmony_ci					break;
2158cc1dc7a3Sopenharmony_ci				case 2:		// two-component, treated as Luminance-Alpha
2159cc1dc7a3Sopenharmony_ci					for (unsigned int x = 0; x < dim_x; x++)
2160cc1dc7a3Sopenharmony_ci					{
2161cc1dc7a3Sopenharmony_ci						row_pointers8[z][y][2 * x    ] = data8[(4 * dim_x * ym) + (4 * x    )];
2162cc1dc7a3Sopenharmony_ci						row_pointers8[z][y][2 * x + 1] = data8[(4 * dim_x * ym) + (4 * x + 3)];
2163cc1dc7a3Sopenharmony_ci					}
2164cc1dc7a3Sopenharmony_ci					break;
2165cc1dc7a3Sopenharmony_ci				case 3:		// three-component, treated as RGB
2166cc1dc7a3Sopenharmony_ci					for (unsigned int x = 0; x < dim_x; x++)
2167cc1dc7a3Sopenharmony_ci					{
2168cc1dc7a3Sopenharmony_ci						row_pointers8[z][y][3 * x    ] = data8[(4 * dim_x * ym) + (4 * x    )];
2169cc1dc7a3Sopenharmony_ci						row_pointers8[z][y][3 * x + 1] = data8[(4 * dim_x * ym) + (4 * x + 1)];
2170cc1dc7a3Sopenharmony_ci						row_pointers8[z][y][3 * x + 2] = data8[(4 * dim_x * ym) + (4 * x + 2)];
2171cc1dc7a3Sopenharmony_ci					}
2172cc1dc7a3Sopenharmony_ci					break;
2173cc1dc7a3Sopenharmony_ci				case 4:		// four-component, treated as RGBA
2174cc1dc7a3Sopenharmony_ci					for (unsigned int x = 0; x < dim_x; x++)
2175cc1dc7a3Sopenharmony_ci					{
2176cc1dc7a3Sopenharmony_ci						row_pointers8[z][y][4 * x    ] = data8[(4 * dim_x * ym) + (4 * x    )];
2177cc1dc7a3Sopenharmony_ci						row_pointers8[z][y][4 * x + 1] = data8[(4 * dim_x * ym) + (4 * x + 1)];
2178cc1dc7a3Sopenharmony_ci						row_pointers8[z][y][4 * x + 2] = data8[(4 * dim_x * ym) + (4 * x + 2)];
2179cc1dc7a3Sopenharmony_ci						row_pointers8[z][y][4 * x + 3] = data8[(4 * dim_x * ym) + (4 * x + 3)];
2180cc1dc7a3Sopenharmony_ci					}
2181cc1dc7a3Sopenharmony_ci					break;
2182cc1dc7a3Sopenharmony_ci				}
2183cc1dc7a3Sopenharmony_ci			}
2184cc1dc7a3Sopenharmony_ci		}
2185cc1dc7a3Sopenharmony_ci	}
2186cc1dc7a3Sopenharmony_ci	else						// if bitness == 16
2187cc1dc7a3Sopenharmony_ci	{
2188cc1dc7a3Sopenharmony_ci		row_pointers16 = new uint16_t **[dim_z];
2189cc1dc7a3Sopenharmony_ci		row_pointers16[0] = new uint16_t *[dim_y * dim_z];
2190cc1dc7a3Sopenharmony_ci		row_pointers16[0][0] = new uint16_t[dim_x * dim_y * dim_z * image_components];
2191cc1dc7a3Sopenharmony_ci
2192cc1dc7a3Sopenharmony_ci		for (unsigned int z = 1; z < dim_z; z++)
2193cc1dc7a3Sopenharmony_ci		{
2194cc1dc7a3Sopenharmony_ci			row_pointers16[z] = row_pointers16[0] + dim_y * z;
2195cc1dc7a3Sopenharmony_ci			row_pointers16[z][0] = row_pointers16[0][0] + dim_y * dim_x * image_components * z;
2196cc1dc7a3Sopenharmony_ci		}
2197cc1dc7a3Sopenharmony_ci
2198cc1dc7a3Sopenharmony_ci		for (unsigned int z = 0; z < dim_z; z++)
2199cc1dc7a3Sopenharmony_ci		{
2200cc1dc7a3Sopenharmony_ci			for (unsigned int y = 1; y < dim_y; y++)
2201cc1dc7a3Sopenharmony_ci			{
2202cc1dc7a3Sopenharmony_ci				row_pointers16[z][y] = row_pointers16[z][0] + dim_x * image_components * y;
2203cc1dc7a3Sopenharmony_ci			}
2204cc1dc7a3Sopenharmony_ci		}
2205cc1dc7a3Sopenharmony_ci
2206cc1dc7a3Sopenharmony_ci		for (unsigned int z = 0; z < dim_z; z++)
2207cc1dc7a3Sopenharmony_ci		{
2208cc1dc7a3Sopenharmony_ci			uint16_t* data16 = static_cast<uint16_t*>(img->data[z]);
2209cc1dc7a3Sopenharmony_ci
2210cc1dc7a3Sopenharmony_ci			for (unsigned int y = 0; y < dim_y; y++)
2211cc1dc7a3Sopenharmony_ci			{
2212cc1dc7a3Sopenharmony_ci				int ym = y_flip ? dim_y - y - 1: y;
2213cc1dc7a3Sopenharmony_ci				switch (image_components)
2214cc1dc7a3Sopenharmony_ci				{
2215cc1dc7a3Sopenharmony_ci				case 1:		// single-component, treated as Luminance
2216cc1dc7a3Sopenharmony_ci					for (unsigned int x = 0; x < dim_x; x++)
2217cc1dc7a3Sopenharmony_ci					{
2218cc1dc7a3Sopenharmony_ci						row_pointers16[z][y][x] = data16[(4 * dim_x * ym) + (4 * x    )];
2219cc1dc7a3Sopenharmony_ci					}
2220cc1dc7a3Sopenharmony_ci					break;
2221cc1dc7a3Sopenharmony_ci				case 2:		// two-component, treated as Luminance-Alpha
2222cc1dc7a3Sopenharmony_ci					for (unsigned int x = 0; x < dim_x; x++)
2223cc1dc7a3Sopenharmony_ci					{
2224cc1dc7a3Sopenharmony_ci						row_pointers16[z][y][2 * x    ] = data16[(4 * dim_x * ym) + (4 * x    )];
2225cc1dc7a3Sopenharmony_ci						row_pointers16[z][y][2 * x + 1] = data16[(4 * dim_x * ym) + (4 * x + 3)];
2226cc1dc7a3Sopenharmony_ci					}
2227cc1dc7a3Sopenharmony_ci					break;
2228cc1dc7a3Sopenharmony_ci				case 3:		// three-component, treated as RGB
2229cc1dc7a3Sopenharmony_ci					for (unsigned int x = 0; x < dim_x; x++)
2230cc1dc7a3Sopenharmony_ci					{
2231cc1dc7a3Sopenharmony_ci						row_pointers16[z][y][3 * x    ] = data16[(4 * dim_x * ym) + (4 * x    )];
2232cc1dc7a3Sopenharmony_ci						row_pointers16[z][y][3 * x + 1] = data16[(4 * dim_x * ym) + (4 * x + 1)];
2233cc1dc7a3Sopenharmony_ci						row_pointers16[z][y][3 * x + 2] = data16[(4 * dim_x * ym) + (4 * x + 2)];
2234cc1dc7a3Sopenharmony_ci					}
2235cc1dc7a3Sopenharmony_ci					break;
2236cc1dc7a3Sopenharmony_ci				case 4:		// four-component, treated as RGBA
2237cc1dc7a3Sopenharmony_ci					for (unsigned int x = 0; x < dim_x; x++)
2238cc1dc7a3Sopenharmony_ci					{
2239cc1dc7a3Sopenharmony_ci						row_pointers16[z][y][4 * x    ] = data16[(4 * dim_x * ym) + (4 * x    )];
2240cc1dc7a3Sopenharmony_ci						row_pointers16[z][y][4 * x + 1] = data16[(4 * dim_x * ym) + (4 * x + 1)];
2241cc1dc7a3Sopenharmony_ci						row_pointers16[z][y][4 * x + 2] = data16[(4 * dim_x * ym) + (4 * x + 2)];
2242cc1dc7a3Sopenharmony_ci						row_pointers16[z][y][4 * x + 3] = data16[(4 * dim_x * ym) + (4 * x + 3)];
2243cc1dc7a3Sopenharmony_ci					}
2244cc1dc7a3Sopenharmony_ci					break;
2245cc1dc7a3Sopenharmony_ci				}
2246cc1dc7a3Sopenharmony_ci			}
2247cc1dc7a3Sopenharmony_ci		}
2248cc1dc7a3Sopenharmony_ci	}
2249cc1dc7a3Sopenharmony_ci
2250cc1dc7a3Sopenharmony_ci	bool retval { true };
2251cc1dc7a3Sopenharmony_ci	uint32_t image_bytes = dim_x * dim_y * dim_z * image_components * (bitness / 8);
2252cc1dc7a3Sopenharmony_ci
2253cc1dc7a3Sopenharmony_ci	uint32_t dds_magic = DDS_MAGIC;
2254cc1dc7a3Sopenharmony_ci
2255cc1dc7a3Sopenharmony_ci	FILE *wf = fopen(filename, "wb");
2256cc1dc7a3Sopenharmony_ci	if (wf)
2257cc1dc7a3Sopenharmony_ci	{
2258cc1dc7a3Sopenharmony_ci		void *dataptr = (bitness == 16) ?
2259cc1dc7a3Sopenharmony_ci			reinterpret_cast<void*>(row_pointers16[0][0]) :
2260cc1dc7a3Sopenharmony_ci			reinterpret_cast<void*>(row_pointers8[0][0]);
2261cc1dc7a3Sopenharmony_ci
2262cc1dc7a3Sopenharmony_ci		size_t expected_bytes_written = 4 + sizeof(dds_header) + (bitness > 8 ? sizeof(dds_header_dx10) : 0) + image_bytes;
2263cc1dc7a3Sopenharmony_ci
2264cc1dc7a3Sopenharmony_ci		size_t magic_bytes_written = fwrite(&dds_magic, 1, 4, wf);
2265cc1dc7a3Sopenharmony_ci		size_t hdr_bytes_written = fwrite(&hdr, 1, sizeof(dds_header), wf);
2266cc1dc7a3Sopenharmony_ci
2267cc1dc7a3Sopenharmony_ci		size_t dx10_bytes_written;
2268cc1dc7a3Sopenharmony_ci		if (bitness > 8)
2269cc1dc7a3Sopenharmony_ci		{
2270cc1dc7a3Sopenharmony_ci			dx10_bytes_written = fwrite(&dx10, 1, sizeof(dx10), wf);
2271cc1dc7a3Sopenharmony_ci		}
2272cc1dc7a3Sopenharmony_ci		else
2273cc1dc7a3Sopenharmony_ci		{
2274cc1dc7a3Sopenharmony_ci			dx10_bytes_written = 0;
2275cc1dc7a3Sopenharmony_ci		}
2276cc1dc7a3Sopenharmony_ci
2277cc1dc7a3Sopenharmony_ci		size_t data_bytes_written = fwrite(dataptr, 1, image_bytes, wf);
2278cc1dc7a3Sopenharmony_ci
2279cc1dc7a3Sopenharmony_ci		fclose(wf);
2280cc1dc7a3Sopenharmony_ci		if (magic_bytes_written + hdr_bytes_written + dx10_bytes_written + data_bytes_written != expected_bytes_written)
2281cc1dc7a3Sopenharmony_ci		{
2282cc1dc7a3Sopenharmony_ci			retval = false;
2283cc1dc7a3Sopenharmony_ci		}
2284cc1dc7a3Sopenharmony_ci	}
2285cc1dc7a3Sopenharmony_ci	else
2286cc1dc7a3Sopenharmony_ci	{
2287cc1dc7a3Sopenharmony_ci		retval = false;
2288cc1dc7a3Sopenharmony_ci	}
2289cc1dc7a3Sopenharmony_ci
2290cc1dc7a3Sopenharmony_ci	if (row_pointers8)
2291cc1dc7a3Sopenharmony_ci	{
2292cc1dc7a3Sopenharmony_ci		delete[] row_pointers8[0][0];
2293cc1dc7a3Sopenharmony_ci		delete[] row_pointers8[0];
2294cc1dc7a3Sopenharmony_ci		delete[] row_pointers8;
2295cc1dc7a3Sopenharmony_ci	}
2296cc1dc7a3Sopenharmony_ci
2297cc1dc7a3Sopenharmony_ci	if (row_pointers16)
2298cc1dc7a3Sopenharmony_ci	{
2299cc1dc7a3Sopenharmony_ci		delete[] row_pointers16[0][0];
2300cc1dc7a3Sopenharmony_ci		delete[] row_pointers16[0];
2301cc1dc7a3Sopenharmony_ci		delete[] row_pointers16;
2302cc1dc7a3Sopenharmony_ci	}
2303cc1dc7a3Sopenharmony_ci
2304cc1dc7a3Sopenharmony_ci	return retval;
2305cc1dc7a3Sopenharmony_ci}
2306cc1dc7a3Sopenharmony_ci
2307cc1dc7a3Sopenharmony_ci/**
2308cc1dc7a3Sopenharmony_ci * @brief Supported uncompressed image load functions, and their associated file extensions.
2309cc1dc7a3Sopenharmony_ci */
2310cc1dc7a3Sopenharmony_cistatic const struct
2311cc1dc7a3Sopenharmony_ci{
2312cc1dc7a3Sopenharmony_ci	const char* ending1;
2313cc1dc7a3Sopenharmony_ci	const char* ending2;
2314cc1dc7a3Sopenharmony_ci	astcenc_image* (*loader_func)(const char*, bool, bool&, unsigned int&);
2315cc1dc7a3Sopenharmony_ci} loader_descs[] {
2316cc1dc7a3Sopenharmony_ci	// LDR formats
2317cc1dc7a3Sopenharmony_ci	{".png",   ".PNG",  load_png_with_wuffs},
2318cc1dc7a3Sopenharmony_ci	// HDR formats
2319cc1dc7a3Sopenharmony_ci	{".exr",   ".EXR",  load_image_with_tinyexr },
2320cc1dc7a3Sopenharmony_ci	// Container formats
2321cc1dc7a3Sopenharmony_ci	{".ktx",   ".KTX",  load_ktx_uncompressed_image },
2322cc1dc7a3Sopenharmony_ci	{".dds",   ".DDS",  load_dds_uncompressed_image },
2323cc1dc7a3Sopenharmony_ci	// Generic catch all; this one must be last in the list
2324cc1dc7a3Sopenharmony_ci	{ nullptr, nullptr, load_image_with_stb }
2325cc1dc7a3Sopenharmony_ci};
2326cc1dc7a3Sopenharmony_ci
2327cc1dc7a3Sopenharmony_cistatic const int loader_descr_count = sizeof(loader_descs) / sizeof(loader_descs[0]);
2328cc1dc7a3Sopenharmony_ci
2329cc1dc7a3Sopenharmony_ci/**
2330cc1dc7a3Sopenharmony_ci * @brief Supported uncompressed image store functions, and their associated file extensions.
2331cc1dc7a3Sopenharmony_ci */
2332cc1dc7a3Sopenharmony_cistatic const struct
2333cc1dc7a3Sopenharmony_ci{
2334cc1dc7a3Sopenharmony_ci	const char *ending1;
2335cc1dc7a3Sopenharmony_ci	const char *ending2;
2336cc1dc7a3Sopenharmony_ci	int enforced_bitness;
2337cc1dc7a3Sopenharmony_ci	bool (*storer_func)(const astcenc_image *output_image, const char *filename, int y_flip);
2338cc1dc7a3Sopenharmony_ci} storer_descs[] {
2339cc1dc7a3Sopenharmony_ci	// LDR formats
2340cc1dc7a3Sopenharmony_ci	{".bmp", ".BMP",  8, store_bmp_image_with_stb},
2341cc1dc7a3Sopenharmony_ci	{".png", ".PNG",  8, store_png_image_with_stb},
2342cc1dc7a3Sopenharmony_ci	{".tga", ".TGA",  8, store_tga_image_with_stb},
2343cc1dc7a3Sopenharmony_ci	// HDR formats
2344cc1dc7a3Sopenharmony_ci	{".exr", ".EXR", 16, store_exr_image_with_tinyexr},
2345cc1dc7a3Sopenharmony_ci	{".hdr", ".HDR", 16, store_hdr_image_with_stb},
2346cc1dc7a3Sopenharmony_ci	// Container formats
2347cc1dc7a3Sopenharmony_ci	{".dds", ".DDS",  0, store_dds_uncompressed_image},
2348cc1dc7a3Sopenharmony_ci	{".ktx", ".KTX",  0, store_ktx_uncompressed_image}
2349cc1dc7a3Sopenharmony_ci};
2350cc1dc7a3Sopenharmony_ci
2351cc1dc7a3Sopenharmony_cistatic const int storer_descr_count = sizeof(storer_descs) / sizeof(storer_descs[0]);
2352cc1dc7a3Sopenharmony_ci
2353cc1dc7a3Sopenharmony_ci/* See header for documentation. */
2354cc1dc7a3Sopenharmony_ciint get_output_filename_enforced_bitness(
2355cc1dc7a3Sopenharmony_ci	const char* filename
2356cc1dc7a3Sopenharmony_ci) {
2357cc1dc7a3Sopenharmony_ci	const char *eptr = strrchr(filename, '.');
2358cc1dc7a3Sopenharmony_ci	if (!eptr)
2359cc1dc7a3Sopenharmony_ci	{
2360cc1dc7a3Sopenharmony_ci		return 0;
2361cc1dc7a3Sopenharmony_ci	}
2362cc1dc7a3Sopenharmony_ci
2363cc1dc7a3Sopenharmony_ci	for (int i = 0; i < storer_descr_count; i++)
2364cc1dc7a3Sopenharmony_ci	{
2365cc1dc7a3Sopenharmony_ci		if (strcmp(eptr, storer_descs[i].ending1) == 0
2366cc1dc7a3Sopenharmony_ci		 || strcmp(eptr, storer_descs[i].ending2) == 0)
2367cc1dc7a3Sopenharmony_ci		{
2368cc1dc7a3Sopenharmony_ci			return storer_descs[i].enforced_bitness;
2369cc1dc7a3Sopenharmony_ci		}
2370cc1dc7a3Sopenharmony_ci	}
2371cc1dc7a3Sopenharmony_ci
2372cc1dc7a3Sopenharmony_ci	return -1;
2373cc1dc7a3Sopenharmony_ci}
2374cc1dc7a3Sopenharmony_ci
2375cc1dc7a3Sopenharmony_ci/* See header for documentation. */
2376cc1dc7a3Sopenharmony_ciastcenc_image* load_ncimage(
2377cc1dc7a3Sopenharmony_ci	const char* filename,
2378cc1dc7a3Sopenharmony_ci	bool y_flip,
2379cc1dc7a3Sopenharmony_ci	bool& is_hdr,
2380cc1dc7a3Sopenharmony_ci	unsigned int& component_count
2381cc1dc7a3Sopenharmony_ci) {
2382cc1dc7a3Sopenharmony_ci	// Get the file extension
2383cc1dc7a3Sopenharmony_ci	const char* eptr = strrchr(filename, '.');
2384cc1dc7a3Sopenharmony_ci	if (!eptr)
2385cc1dc7a3Sopenharmony_ci	{
2386cc1dc7a3Sopenharmony_ci		eptr = filename;
2387cc1dc7a3Sopenharmony_ci	}
2388cc1dc7a3Sopenharmony_ci
2389cc1dc7a3Sopenharmony_ci	// Scan through descriptors until a matching loader is found
2390cc1dc7a3Sopenharmony_ci	for (unsigned int i = 0; i < loader_descr_count; i++)
2391cc1dc7a3Sopenharmony_ci	{
2392cc1dc7a3Sopenharmony_ci		if (loader_descs[i].ending1 == nullptr
2393cc1dc7a3Sopenharmony_ci			|| strcmp(eptr, loader_descs[i].ending1) == 0
2394cc1dc7a3Sopenharmony_ci			|| strcmp(eptr, loader_descs[i].ending2) == 0)
2395cc1dc7a3Sopenharmony_ci		{
2396cc1dc7a3Sopenharmony_ci			return loader_descs[i].loader_func(filename, y_flip, is_hdr, component_count);
2397cc1dc7a3Sopenharmony_ci		}
2398cc1dc7a3Sopenharmony_ci	}
2399cc1dc7a3Sopenharmony_ci
2400cc1dc7a3Sopenharmony_ci	// Should never reach here - stb_image provides a generic handler
2401cc1dc7a3Sopenharmony_ci	return nullptr;
2402cc1dc7a3Sopenharmony_ci}
2403cc1dc7a3Sopenharmony_ci
2404cc1dc7a3Sopenharmony_ci/* See header for documentation. */
2405cc1dc7a3Sopenharmony_cibool store_ncimage(
2406cc1dc7a3Sopenharmony_ci	const astcenc_image* output_image,
2407cc1dc7a3Sopenharmony_ci	const char* filename,
2408cc1dc7a3Sopenharmony_ci	int y_flip
2409cc1dc7a3Sopenharmony_ci) {
2410cc1dc7a3Sopenharmony_ci	const char* eptr = strrchr(filename, '.');
2411cc1dc7a3Sopenharmony_ci	if (!eptr)
2412cc1dc7a3Sopenharmony_ci	{
2413cc1dc7a3Sopenharmony_ci		eptr = ".ktx"; // use KTX file format if we don't have an ending.
2414cc1dc7a3Sopenharmony_ci	}
2415cc1dc7a3Sopenharmony_ci
2416cc1dc7a3Sopenharmony_ci	for (int i = 0; i < storer_descr_count; i++)
2417cc1dc7a3Sopenharmony_ci	{
2418cc1dc7a3Sopenharmony_ci		if (strcmp(eptr, storer_descs[i].ending1) == 0
2419cc1dc7a3Sopenharmony_ci		 || strcmp(eptr, storer_descs[i].ending2) == 0)
2420cc1dc7a3Sopenharmony_ci		{
2421cc1dc7a3Sopenharmony_ci			return storer_descs[i].storer_func(output_image, filename, y_flip);
2422cc1dc7a3Sopenharmony_ci		}
2423cc1dc7a3Sopenharmony_ci	}
2424cc1dc7a3Sopenharmony_ci
2425cc1dc7a3Sopenharmony_ci	// Should never reach here - get_output_filename_enforced_bitness should
2426cc1dc7a3Sopenharmony_ci	// have acted as a preflight check
2427cc1dc7a3Sopenharmony_ci	return false;
2428cc1dc7a3Sopenharmony_ci}
2429cc1dc7a3Sopenharmony_ci
2430cc1dc7a3Sopenharmony_ci/* ============================================================================
2431cc1dc7a3Sopenharmony_ci	ASTC compressed file loading
2432cc1dc7a3Sopenharmony_ci============================================================================ */
2433cc1dc7a3Sopenharmony_cistruct astc_header
2434cc1dc7a3Sopenharmony_ci{
2435cc1dc7a3Sopenharmony_ci	uint8_t magic[4];
2436cc1dc7a3Sopenharmony_ci	uint8_t block_x;
2437cc1dc7a3Sopenharmony_ci	uint8_t block_y;
2438cc1dc7a3Sopenharmony_ci	uint8_t block_z;
2439cc1dc7a3Sopenharmony_ci	uint8_t dim_x[3];			// dims = dim[0] + (dim[1] << 8) + (dim[2] << 16)
2440cc1dc7a3Sopenharmony_ci	uint8_t dim_y[3];			// Sizes are given in texels;
2441cc1dc7a3Sopenharmony_ci	uint8_t dim_z[3];			// block count is inferred
2442cc1dc7a3Sopenharmony_ci};
2443cc1dc7a3Sopenharmony_ci
2444cc1dc7a3Sopenharmony_cistatic const uint32_t ASTC_MAGIC_ID = 0x5CA1AB13;
2445cc1dc7a3Sopenharmony_ci
2446cc1dc7a3Sopenharmony_cistatic unsigned int unpack_bytes(
2447cc1dc7a3Sopenharmony_ci	uint8_t a,
2448cc1dc7a3Sopenharmony_ci	uint8_t b,
2449cc1dc7a3Sopenharmony_ci	uint8_t c,
2450cc1dc7a3Sopenharmony_ci	uint8_t d
2451cc1dc7a3Sopenharmony_ci) {
2452cc1dc7a3Sopenharmony_ci	return (static_cast<unsigned int>(a)      ) +
2453cc1dc7a3Sopenharmony_ci	       (static_cast<unsigned int>(b) <<  8) +
2454cc1dc7a3Sopenharmony_ci	       (static_cast<unsigned int>(c) << 16) +
2455cc1dc7a3Sopenharmony_ci	       (static_cast<unsigned int>(d) << 24);
2456cc1dc7a3Sopenharmony_ci}
2457cc1dc7a3Sopenharmony_ci
2458cc1dc7a3Sopenharmony_ci/* See header for documentation. */
2459cc1dc7a3Sopenharmony_ciint load_cimage(
2460cc1dc7a3Sopenharmony_ci	const char* filename,
2461cc1dc7a3Sopenharmony_ci	astc_compressed_image& img
2462cc1dc7a3Sopenharmony_ci) {
2463cc1dc7a3Sopenharmony_ci	std::ifstream file(filename, std::ios::in | std::ios::binary);
2464cc1dc7a3Sopenharmony_ci	if (!file)
2465cc1dc7a3Sopenharmony_ci	{
2466cc1dc7a3Sopenharmony_ci		print_error("ERROR: File open failed '%s'\n", filename);
2467cc1dc7a3Sopenharmony_ci		return 1;
2468cc1dc7a3Sopenharmony_ci	}
2469cc1dc7a3Sopenharmony_ci
2470cc1dc7a3Sopenharmony_ci	astc_header hdr;
2471cc1dc7a3Sopenharmony_ci	file.read(reinterpret_cast<char*>(&hdr), sizeof(astc_header));
2472cc1dc7a3Sopenharmony_ci	if (file.fail())
2473cc1dc7a3Sopenharmony_ci	{
2474cc1dc7a3Sopenharmony_ci		print_error("ERROR: File read failed '%s'\n", filename);
2475cc1dc7a3Sopenharmony_ci		return 1;
2476cc1dc7a3Sopenharmony_ci	}
2477cc1dc7a3Sopenharmony_ci
2478cc1dc7a3Sopenharmony_ci	unsigned int magicval = unpack_bytes(hdr.magic[0], hdr.magic[1], hdr.magic[2], hdr.magic[3]);
2479cc1dc7a3Sopenharmony_ci	if (magicval != ASTC_MAGIC_ID)
2480cc1dc7a3Sopenharmony_ci	{
2481cc1dc7a3Sopenharmony_ci		print_error("ERROR: File not recognized '%s'\n", filename);
2482cc1dc7a3Sopenharmony_ci		return 1;
2483cc1dc7a3Sopenharmony_ci	}
2484cc1dc7a3Sopenharmony_ci
2485cc1dc7a3Sopenharmony_ci	// Ensure these are not zero to avoid div by zero
2486cc1dc7a3Sopenharmony_ci	unsigned int block_x = astc::max(static_cast<unsigned int>(hdr.block_x), 1u);
2487cc1dc7a3Sopenharmony_ci	unsigned int block_y = astc::max(static_cast<unsigned int>(hdr.block_y), 1u);
2488cc1dc7a3Sopenharmony_ci	unsigned int block_z = astc::max(static_cast<unsigned int>(hdr.block_z), 1u);
2489cc1dc7a3Sopenharmony_ci
2490cc1dc7a3Sopenharmony_ci	unsigned int dim_x = unpack_bytes(hdr.dim_x[0], hdr.dim_x[1], hdr.dim_x[2], 0);
2491cc1dc7a3Sopenharmony_ci	unsigned int dim_y = unpack_bytes(hdr.dim_y[0], hdr.dim_y[1], hdr.dim_y[2], 0);
2492cc1dc7a3Sopenharmony_ci	unsigned int dim_z = unpack_bytes(hdr.dim_z[0], hdr.dim_z[1], hdr.dim_z[2], 0);
2493cc1dc7a3Sopenharmony_ci
2494cc1dc7a3Sopenharmony_ci	if (dim_x == 0 || dim_y == 0 || dim_z == 0)
2495cc1dc7a3Sopenharmony_ci	{
2496cc1dc7a3Sopenharmony_ci		print_error("ERROR: Image header corrupt '%s'\n", filename);
2497cc1dc7a3Sopenharmony_ci		return 1;
2498cc1dc7a3Sopenharmony_ci	}
2499cc1dc7a3Sopenharmony_ci
2500cc1dc7a3Sopenharmony_ci	unsigned int xblocks = (dim_x + block_x - 1) / block_x;
2501cc1dc7a3Sopenharmony_ci	unsigned int yblocks = (dim_y + block_y - 1) / block_y;
2502cc1dc7a3Sopenharmony_ci	unsigned int zblocks = (dim_z + block_z - 1) / block_z;
2503cc1dc7a3Sopenharmony_ci
2504cc1dc7a3Sopenharmony_ci	size_t data_size = xblocks * yblocks * zblocks * 16;
2505cc1dc7a3Sopenharmony_ci	uint8_t *buffer = new uint8_t[data_size];
2506cc1dc7a3Sopenharmony_ci
2507cc1dc7a3Sopenharmony_ci	file.read(reinterpret_cast<char*>(buffer), data_size);
2508cc1dc7a3Sopenharmony_ci	if (file.fail())
2509cc1dc7a3Sopenharmony_ci	{
2510cc1dc7a3Sopenharmony_ci		print_error("ERROR: Image data size exceeded file size '%s'\n", filename);
2511cc1dc7a3Sopenharmony_ci		delete[] buffer;
2512cc1dc7a3Sopenharmony_ci		return 1;
2513cc1dc7a3Sopenharmony_ci	}
2514cc1dc7a3Sopenharmony_ci
2515cc1dc7a3Sopenharmony_ci	img.data = buffer;
2516cc1dc7a3Sopenharmony_ci	img.data_len = data_size;
2517cc1dc7a3Sopenharmony_ci	img.block_x = block_x;
2518cc1dc7a3Sopenharmony_ci	img.block_y = block_y;
2519cc1dc7a3Sopenharmony_ci	img.block_z = block_z;
2520cc1dc7a3Sopenharmony_ci	img.dim_x = dim_x;
2521cc1dc7a3Sopenharmony_ci	img.dim_y = dim_y;
2522cc1dc7a3Sopenharmony_ci	img.dim_z = dim_z;
2523cc1dc7a3Sopenharmony_ci	return 0;
2524cc1dc7a3Sopenharmony_ci}
2525cc1dc7a3Sopenharmony_ci
2526cc1dc7a3Sopenharmony_ci/* See header for documentation. */
2527cc1dc7a3Sopenharmony_ciint store_cimage(
2528cc1dc7a3Sopenharmony_ci	const astc_compressed_image& img,
2529cc1dc7a3Sopenharmony_ci	const char* filename
2530cc1dc7a3Sopenharmony_ci) {
2531cc1dc7a3Sopenharmony_ci	astc_header hdr;
2532cc1dc7a3Sopenharmony_ci	hdr.magic[0] =  ASTC_MAGIC_ID        & 0xFF;
2533cc1dc7a3Sopenharmony_ci	hdr.magic[1] = (ASTC_MAGIC_ID >>  8) & 0xFF;
2534cc1dc7a3Sopenharmony_ci	hdr.magic[2] = (ASTC_MAGIC_ID >> 16) & 0xFF;
2535cc1dc7a3Sopenharmony_ci	hdr.magic[3] = (ASTC_MAGIC_ID >> 24) & 0xFF;
2536cc1dc7a3Sopenharmony_ci
2537cc1dc7a3Sopenharmony_ci	hdr.block_x = static_cast<uint8_t>(img.block_x);
2538cc1dc7a3Sopenharmony_ci	hdr.block_y = static_cast<uint8_t>(img.block_y);
2539cc1dc7a3Sopenharmony_ci	hdr.block_z = static_cast<uint8_t>(img.block_z);
2540cc1dc7a3Sopenharmony_ci
2541cc1dc7a3Sopenharmony_ci	hdr.dim_x[0] =  img.dim_x        & 0xFF;
2542cc1dc7a3Sopenharmony_ci	hdr.dim_x[1] = (img.dim_x >>  8) & 0xFF;
2543cc1dc7a3Sopenharmony_ci	hdr.dim_x[2] = (img.dim_x >> 16) & 0xFF;
2544cc1dc7a3Sopenharmony_ci
2545cc1dc7a3Sopenharmony_ci	hdr.dim_y[0] =  img.dim_y       & 0xFF;
2546cc1dc7a3Sopenharmony_ci	hdr.dim_y[1] = (img.dim_y >>  8) & 0xFF;
2547cc1dc7a3Sopenharmony_ci	hdr.dim_y[2] = (img.dim_y >> 16) & 0xFF;
2548cc1dc7a3Sopenharmony_ci
2549cc1dc7a3Sopenharmony_ci	hdr.dim_z[0] =  img.dim_z        & 0xFF;
2550cc1dc7a3Sopenharmony_ci	hdr.dim_z[1] = (img.dim_z >>  8) & 0xFF;
2551cc1dc7a3Sopenharmony_ci	hdr.dim_z[2] = (img.dim_z >> 16) & 0xFF;
2552cc1dc7a3Sopenharmony_ci
2553cc1dc7a3Sopenharmony_ci	std::ofstream file(filename, std::ios::out | std::ios::binary);
2554cc1dc7a3Sopenharmony_ci	if (!file)
2555cc1dc7a3Sopenharmony_ci	{
2556cc1dc7a3Sopenharmony_ci		print_error("ERROR: File open failed '%s'\n", filename);
2557cc1dc7a3Sopenharmony_ci		return 1;
2558cc1dc7a3Sopenharmony_ci	}
2559cc1dc7a3Sopenharmony_ci
2560cc1dc7a3Sopenharmony_ci	file.write(reinterpret_cast<char*>(&hdr), sizeof(astc_header));
2561cc1dc7a3Sopenharmony_ci	file.write(reinterpret_cast<char*>(img.data), img.data_len);
2562cc1dc7a3Sopenharmony_ci	return 0;
2563cc1dc7a3Sopenharmony_ci}
2564