// basisu_comp.h
// Copyright (C) 2019-2026 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "basisu_frontend.h"
#include "basisu_backend.h"
#include "basisu_basis_file.h"
#include "../transcoder/basisu_transcoder.h"
#include "basisu_uastc_enc.h"
#include "basisu_uastc_hdr_4x4_enc.h"
#include "basisu_astc_hdr_6x6_enc.h"
#include "basisu_astc_ldr_encode.h"

#define BASISU_LIB_VERSION 210
#define BASISU_LIB_VERSION_STRING "2.10"

#ifndef BASISD_SUPPORT_KTX2
	#error BASISD_SUPPORT_KTX2 is undefined
#endif
#ifndef BASISD_SUPPORT_KTX2_ZSTD
	#error BASISD_SUPPORT_KTX2_ZSTD is undefined
#endif

#if !BASISD_SUPPORT_KTX2
	#error BASISD_SUPPORT_KTX2 must be enabled when building the encoder. To reduce code size if KTX2 support is not needed, set BASISD_SUPPORT_KTX2_ZSTD to 0
#endif

namespace basisu
{
	struct opencl_context;
	typedef opencl_context* opencl_context_ptr;

	const uint32_t BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION = 16384;

	// Allow block's color distance to increase by 1.5 while searching for an alternative nearby endpoint.
	const float BASISU_DEFAULT_ENDPOINT_RDO_THRESH = 1.5f; 
	
	// Allow block's color distance to increase by 1.25 while searching the selector history buffer for a close enough match.
	const float BASISU_DEFAULT_SELECTOR_RDO_THRESH = 1.25f; 

	const int BASISU_DEFAULT_QUALITY = 128;
	const float BASISU_DEFAULT_HYBRID_SEL_CB_QUALITY_THRESH = 2.0f;

	const uint32_t BASISU_MAX_IMAGE_DIMENSION = 16384;
	
	// The original ETC1S specific (non-unified) quality level
	const uint32_t BASISU_QUALITY_MIN = 1; // note 0 is also technically valid in the code/API for ETC1S; the difference in quality is tiny (both result in very small codebooks)
	const uint32_t BASISU_QUALITY_MAX = 255;
		
	const uint32_t BASISU_MAX_ENDPOINT_CLUSTERS = basisu_frontend::cMaxEndpointClusters;
	const uint32_t BASISU_MAX_SELECTOR_CLUSTERS = basisu_frontend::cMaxSelectorClusters;

	// [1,100] are also the valid unified quality levels
	const uint32_t BASISU_XUASTC_QUALITY_MIN = 1;
	const uint32_t BASISU_XUASTC_QUALITY_MAX = 100;

	const uint32_t BASISU_MAX_SLICES = 0xFFFFFF;

	const int BASISU_RDO_UASTC_DICT_SIZE_DEFAULT = 4096; // 32768;
	const int BASISU_RDO_UASTC_DICT_SIZE_MIN = 64;
	const int BASISU_RDO_UASTC_DICT_SIZE_MAX = 65536;

	struct image_stats
	{
		image_stats()
		{
			clear();
		}

		void clear()
		{
			m_filename.clear();
			m_width = 0;
			m_height = 0;
						
			m_basis_rgb_avg_psnr = 0.0f;
			m_basis_rgb_avg_log2_psnr = 0.0f;

			m_basis_rgba_avg_psnr = 0.0f;
			m_basis_a_avg_psnr = 0.0f;
			m_basis_luma_709_psnr = 0.0f;
			m_basis_luma_601_psnr = 0.0f;
			m_basis_luma_709_ssim = 0.0f;

			m_basis_rgb_avg_bc6h_psnr = 0.0f;
			m_basis_rgb_avg_bc6h_log2_psnr = 0.0f;

			m_bc7_rgb_avg_psnr = 0.0f;
			m_bc7_rgba_avg_psnr = 0.0f;
			m_bc7_a_avg_psnr = 0.0f;
			m_bc7_luma_709_psnr = 0.0f;
			m_bc7_luma_601_psnr = 0.0f;
			m_bc7_luma_709_ssim = 0.0f;
						
			m_best_etc1s_rgb_avg_psnr = 0.0f;
			m_best_etc1s_luma_709_psnr = 0.0f;
			m_best_etc1s_luma_601_psnr = 0.0f;
			m_best_etc1s_luma_709_ssim = 0.0f;

			m_opencl_failed = false;
		}

		std::string m_filename;
		uint32_t m_width;
		uint32_t m_height;

		// .basis/.ktx2 compressed (LDR: ETC1S or UASTC statistics, HDR: transcoded BC6H statistics)
		float m_basis_rgb_avg_psnr;
		float m_basis_rgb_avg_log2_psnr;

		float m_basis_rgba_avg_psnr;
		float m_basis_a_avg_psnr;
		float m_basis_luma_709_psnr;
		float m_basis_luma_601_psnr;
		float m_basis_luma_709_ssim;

		// UASTC HDR only.
		float m_basis_rgb_avg_bc6h_psnr;
		float m_basis_rgb_avg_bc6h_log2_psnr;

		// LDR: BC7 statistics
		float m_bc7_rgb_avg_psnr;
		float m_bc7_rgba_avg_psnr;
		float m_bc7_a_avg_psnr;
		float m_bc7_luma_709_psnr;
		float m_bc7_luma_601_psnr;
		float m_bc7_luma_709_ssim;
		
		// LDR: Highest achievable quality ETC1S statistics, for development/comparison
		float m_best_etc1s_rgb_avg_psnr;
		float m_best_etc1s_luma_709_psnr;
		float m_best_etc1s_luma_601_psnr;
		float m_best_etc1s_luma_709_ssim;

		bool m_opencl_failed;
	};

	enum class hdr_modes
	{
		// standard but constrained ASTC HDR 4x4 tex data that can be rapidly transcoded to BC6H
		cUASTC_HDR_4X4, 
		// standard RDO optimized or non-RDO (highest quality) ASTC HDR 6x6 tex data that can be rapidly re-encoded to BC6H
		cASTC_HDR_6X6,
		// a custom intermediate format based off ASTC HDR that can be rapidly decoded straight to ASTC HDR or re-encoded to BC6H
		cUASTC_HDR_6X6_INTERMEDIATE,
		cTotal
	};

	template<bool def>
	struct bool_param
	{
		bool_param() :
			m_value(def),
			m_changed(false)
		{
		}

		void clear()
		{
			m_value = def;
			m_changed = false;
		}

		operator bool() const
		{
			return m_value;
		}

		bool operator= (bool v)
		{
			m_value = v;
			m_changed = true;
			return m_value;
		}

		bool was_changed() const { return m_changed; }
		void set_changed(bool flag) { m_changed = flag; }

		bool m_value;
		bool m_changed;
	};

	template<typename T>
	struct param
	{
		param(T def, T min_v, T max_v) :
			m_value(def),
			m_def(def),
			m_min(min_v),
			m_max(max_v),
			m_changed(false)
		{
		}

		void clear()
		{
			m_value = m_def;
			m_changed = false;
		}

		operator T() const
		{
			return m_value;
		}

		T operator= (T v)
		{
			m_value = clamp<T>(v, m_min, m_max);
			m_changed = true;
			return m_value;
		}

		T operator *= (T v)
		{
			m_value *= v;
			m_changed = true;
			return m_value;
		}

		bool was_changed() const { return m_changed; }
		void set_changed(bool flag) { m_changed = flag; }

		T m_value;
		T m_def;
		T m_min;
		T m_max;
		bool m_changed;
	};

	// Low-level direct compressor parameters. 
	// Also see basis_compress() below for a simplified C-style interface.
	struct basis_compressor_params
	{
		basis_compressor_params() :
			m_xuastc_or_astc_ldr_basis_tex_format(-1, -1, INT_MAX),
			// Note the ETC1S default compression/effort level is 2, not the command line default of 1.
			m_etc1s_compression_level((int)BASISU_DEFAULT_ETC1S_COMPRESSION_LEVEL, 0, (int)BASISU_MAX_ETC1S_COMPRESSION_LEVEL),
			m_selector_rdo_thresh(BASISU_DEFAULT_SELECTOR_RDO_THRESH, 0.0f, 1e+10f),
			m_endpoint_rdo_thresh(BASISU_DEFAULT_ENDPOINT_RDO_THRESH, 0.0f, 1e+10f),
			m_mip_scale(1.0f, .000125f, 4.0f),
			m_mip_smallest_dimension(1, 1, 16384),
			m_etc1s_max_endpoint_clusters(0),
			m_etc1s_max_selector_clusters(0),
			m_quality_level(-1),
			m_pack_uastc_ldr_4x4_flags(cPackUASTCLevelDefault),
			m_rdo_uastc_ldr_4x4_quality_scalar(1.0f, 0.001f, 50.0f),
			m_rdo_uastc_ldr_4x4_dict_size(BASISU_RDO_UASTC_DICT_SIZE_DEFAULT, BASISU_RDO_UASTC_DICT_SIZE_MIN, BASISU_RDO_UASTC_DICT_SIZE_MAX),
			m_rdo_uastc_ldr_4x4_max_smooth_block_error_scale(UASTC_RDO_DEFAULT_SMOOTH_BLOCK_MAX_ERROR_SCALE, 1.0f, 300.0f),
			m_rdo_uastc_ldr_4x4_smooth_block_max_std_dev(UASTC_RDO_DEFAULT_MAX_SMOOTH_BLOCK_STD_DEV, .01f, 65536.0f),
			m_rdo_uastc_ldr_4x4_max_allowed_rms_increase_ratio(UASTC_RDO_DEFAULT_MAX_ALLOWED_RMS_INCREASE_RATIO, .01f, 100.0f),
			m_rdo_uastc_ldr_4x4_skip_block_rms_thresh(UASTC_RDO_DEFAULT_SKIP_BLOCK_RMS_THRESH, .01f, 100.0f),
			m_resample_width(0, 1, 16384),
			m_resample_height(0, 1, 16384),
			m_resample_factor(0.0f, .00125f, 100.0f),
			m_ktx2_uastc_supercompression(basist::KTX2_SS_NONE),
			m_ktx2_zstd_supercompression_level(6, INT_MIN, INT_MAX),
			m_transcode_flags(0, 0, UINT32_MAX),
			m_ldr_hdr_upconversion_nit_multiplier(0.0f, 0.0f, basist::MAX_HALF_FLOAT),
			m_ldr_hdr_upconversion_black_bias(0.0f, 0.0f, 1.0f),
			m_xuastc_ldr_effort_level(astc_ldr::EFFORT_LEVEL_DEF, astc_ldr::EFFORT_LEVEL_MIN, astc_ldr::EFFORT_LEVEL_MAX),
			m_xuastc_ldr_syntax((int)basist::astc_ldr_t::xuastc_ldr_syntax::cFullZStd, (int)basist::astc_ldr_t::xuastc_ldr_syntax::cFullArith, (int)basist::astc_ldr_t::xuastc_ldr_syntax::cFullZStd),
			m_ls_min_psnr(35.0f, 0.0f, 100.0f), m_ls_min_alpha_psnr(38.0f, 0.0f, 100.0f),
			m_ls_thresh_psnr(1.5f, 0.0f, 100.0f), m_ls_thresh_alpha_psnr(0.75f, 0.0f, 100.0f),
			m_ls_thresh_edge_psnr(1.0f, 0.0f, 100.00f), m_ls_thresh_edge_alpha_psnr(0.5f, 0.0f, 100.00f),
			m_pJob_pool(nullptr)
		{
			clear();
		}

		void clear()
		{
			m_format_mode = basist::basis_tex_format::cETC1S;

			m_uastc.clear();
			m_hdr.clear();
			m_hdr_mode = hdr_modes::cUASTC_HDR_4X4;
			m_xuastc_or_astc_ldr_basis_tex_format = -1;

			m_use_opencl.clear();
			m_status_output.clear();

			m_source_filenames.clear();
			m_source_alpha_filenames.clear();

			m_source_images.clear();
			m_source_mipmap_images.clear();

			m_out_filename.clear();

			m_y_flip.clear();
			m_debug.clear();
			m_validate_etc1s.clear();
			m_debug_images.clear();
			m_perceptual.clear();
			m_no_selector_rdo.clear();
			m_selector_rdo_thresh.clear();
			m_read_source_images.clear();
			m_write_output_basis_or_ktx2_files.clear();
			m_etc1s_compression_level.clear();
			m_compute_stats.clear();
			m_print_stats.clear();
			m_check_for_alpha.clear();
			m_force_alpha.clear();
			m_multithreading.clear();
			m_swizzle[0] = 0;
			m_swizzle[1] = 1;
			m_swizzle[2] = 2;
			m_swizzle[3] = 3;
			m_renormalize.clear();
			m_disable_hierarchical_endpoint_codebooks.clear();

			m_no_endpoint_rdo.clear();
			m_endpoint_rdo_thresh.clear();
						
			m_mip_gen.clear();
			m_mip_scale.clear();
			m_mip_filter = "kaiser";
			m_mip_scale = 1.0f;
			m_mip_srgb.clear();
			m_mip_premultiplied.clear();
			m_mip_renormalize.clear();
			m_mip_wrapping.clear();
			m_mip_fast.clear();
			m_mip_smallest_dimension.clear();

			m_etc1s_max_endpoint_clusters = 0;
			m_etc1s_max_selector_clusters = 0;
			m_quality_level = -1;

			m_tex_type = basist::cBASISTexType2D;
			m_userdata0 = 0;
			m_userdata1 = 0;
			m_us_per_frame = 0;

			m_pack_uastc_ldr_4x4_flags = cPackUASTCLevelDefault;
			m_rdo_uastc_ldr_4x4.clear();
			m_rdo_uastc_ldr_4x4_quality_scalar.clear();
			m_rdo_uastc_ldr_4x4_max_smooth_block_error_scale.clear();
			m_rdo_uastc_ldr_4x4_smooth_block_max_std_dev.clear();
			m_rdo_uastc_ldr_4x4_max_allowed_rms_increase_ratio.clear();
			m_rdo_uastc_ldr_4x4_skip_block_rms_thresh.clear();
			m_rdo_uastc_ldr_4x4_favor_simpler_modes_in_rdo_mode.clear();
			m_rdo_uastc_ldr_4x4_multithreading.clear();

			m_resample_width.clear();
			m_resample_height.clear();
			m_resample_factor.clear();

			m_pGlobal_codebooks = nullptr;

			m_create_ktx2_file.clear();
			m_ktx2_uastc_supercompression = basist::KTX2_SS_NONE;
			m_ktx2_key_values.clear();
			m_ktx2_zstd_supercompression_level.clear();
			m_ktx2_and_basis_srgb_transfer_function.clear();

			m_validate_output_data.clear();
			m_transcode_flags.clear();

			m_ldr_hdr_upconversion_srgb_to_linear.clear();

			m_hdr_favor_astc.clear();
			
			m_uastc_hdr_4x4_options.init();
			m_astc_hdr_6x6_options.clear();

			m_ldr_hdr_upconversion_nit_multiplier.clear();
			m_ldr_hdr_upconversion_black_bias.clear();

			m_xuastc_ldr_effort_level.clear();
			m_xuastc_ldr_use_dct.clear();
			m_xuastc_ldr_use_lossy_supercompression.clear();
			m_xuastc_ldr_force_disable_subsets.clear();
			m_xuastc_ldr_force_disable_rgb_dual_plane.clear();
			m_xuastc_ldr_syntax.clear();
			
			m_ls_min_psnr.clear();
			m_ls_min_alpha_psnr.clear();
			m_ls_thresh_psnr.clear();
			m_ls_thresh_alpha_psnr.clear();
			m_ls_thresh_edge_psnr.clear();
			m_ls_thresh_edge_alpha_psnr.clear();
			for (uint32_t i = 0; i < 4; i++)
				m_xuastc_ldr_channel_weights[i] = 1;
			m_xuastc_ldr_blurring.clear();
						
			m_pJob_pool = nullptr;
		}
				
		// Configures the compressor's mode by setting the proper parameters (which were preserved for backwards compatibility with old code).
		// This is by far the preferred way of controlling which codec mode the compressor will select.
		void set_format_mode(basist::basis_tex_format mode)
		{
			m_format_mode = mode;

			switch (mode)
			{
			case basist::basis_tex_format::cETC1S:
			{
				// ETC1S
				m_xuastc_or_astc_ldr_basis_tex_format = -1;
				m_hdr = false;
				m_uastc = false;
				m_hdr_mode = hdr_modes::cUASTC_HDR_4X4; // doesn't matter
				break;
			}
			case basist::basis_tex_format::cUASTC_LDR_4x4:
			{
				// UASTC LDR 4x4
				m_xuastc_or_astc_ldr_basis_tex_format = -1;
				m_hdr = false;
				m_uastc = true;
				m_hdr_mode = hdr_modes::cUASTC_HDR_4X4; // doesn't matter
				break;
			}
			case basist::basis_tex_format::cUASTC_HDR_4x4:
			{
				// UASTC HDR 4x4
				m_xuastc_or_astc_ldr_basis_tex_format = -1;
				m_hdr = true;
				m_uastc = true;
				m_hdr_mode = hdr_modes::cUASTC_HDR_4X4;
				break;
			}
			case basist::basis_tex_format::cASTC_HDR_6x6:
			{
				// ASTC HDR 6x6
				m_xuastc_or_astc_ldr_basis_tex_format = -1;
				m_hdr = true;
				m_uastc = true;
				m_hdr_mode = hdr_modes::cASTC_HDR_6X6;
				break;
			}
			case basist::basis_tex_format::cUASTC_HDR_6x6_INTERMEDIATE:
			{
				// UASTC HDR 6x6
				m_xuastc_or_astc_ldr_basis_tex_format = -1;
				m_hdr = true;
				m_uastc = true;
				m_hdr_mode = hdr_modes::cUASTC_HDR_6X6_INTERMEDIATE;
				break;
			}
			case basist::basis_tex_format::cXUASTC_LDR_4x4:
			case basist::basis_tex_format::cXUASTC_LDR_5x4:
			case basist::basis_tex_format::cXUASTC_LDR_5x5:
			case basist::basis_tex_format::cXUASTC_LDR_6x5:
			case basist::basis_tex_format::cXUASTC_LDR_6x6:
			case basist::basis_tex_format::cXUASTC_LDR_8x5:
			case basist::basis_tex_format::cXUASTC_LDR_8x6:
			case basist::basis_tex_format::cXUASTC_LDR_10x5:
			case basist::basis_tex_format::cXUASTC_LDR_10x6:
			case basist::basis_tex_format::cXUASTC_LDR_8x8:
			case basist::basis_tex_format::cXUASTC_LDR_10x8:
			case basist::basis_tex_format::cXUASTC_LDR_10x10:
			case basist::basis_tex_format::cXUASTC_LDR_12x10:
			case basist::basis_tex_format::cXUASTC_LDR_12x12:
			case basist::basis_tex_format::cASTC_LDR_4x4:
			case basist::basis_tex_format::cASTC_LDR_5x4:
			case basist::basis_tex_format::cASTC_LDR_5x5:
			case basist::basis_tex_format::cASTC_LDR_6x5:
			case basist::basis_tex_format::cASTC_LDR_6x6:
			case basist::basis_tex_format::cASTC_LDR_8x5:
			case basist::basis_tex_format::cASTC_LDR_8x6:
			case basist::basis_tex_format::cASTC_LDR_10x5:
			case basist::basis_tex_format::cASTC_LDR_10x6:
			case basist::basis_tex_format::cASTC_LDR_8x8:
			case basist::basis_tex_format::cASTC_LDR_10x8:
			case basist::basis_tex_format::cASTC_LDR_10x10:
			case basist::basis_tex_format::cASTC_LDR_12x10:
			case basist::basis_tex_format::cASTC_LDR_12x12:
			{
				// ASTC LDR 4x4-12x12 or XUASTC LDR 4x4-12x12
				m_xuastc_or_astc_ldr_basis_tex_format = (int)mode;
				m_hdr = false;
				m_uastc = true;
				m_hdr_mode = hdr_modes::cUASTC_HDR_4X4; // doesn't matter
				break;
			}
			default:
				assert(0);
				break;
			}
		}

		// Like set_format_mode() but also sets the effort and quality parameters appropriately for the selected mode.
		// "Effort" (perf. vs. highest achievable quality) and "quality" (quality vs. bitrate) parameters are now mode dependent.
		// Effort ranges from [0,10] and quality ranges from [1,100], unless they are -1 in which case you get the codec's default settings.
		bool set_format_mode_and_effort(basist::basis_tex_format mode, int effort = -1, bool set_defaults = true);
		bool set_format_mode_and_quality_effort(basist::basis_tex_format mode, int quality = -1, int effort = -1, bool set_defaults = true);

		// Sets all the sRGB-related options (m_perceptual, m_mip_srgb, m_ktx2_and_basis_srgb_transfer_function) to the specified value.
		void set_srgb_options(bool srgb_flag)
		{
			m_perceptual = srgb_flag;
			m_mip_srgb = srgb_flag;
			m_ktx2_and_basis_srgb_transfer_function = srgb_flag;
		}
		
		// Simpler helpers - I wish this was easier, but backwards API compat is also valuable.
		bool is_etc1s() const
		{
			return !m_uastc;
		}

		bool is_uastc_ldr_4x4() const
		{
			return m_uastc && !m_hdr && (m_xuastc_or_astc_ldr_basis_tex_format == -1);
		}

		bool is_uastc_hdr_4x4() const
		{
			return m_uastc && m_hdr && (m_hdr_mode == hdr_modes::cUASTC_HDR_4X4);
		}
								
		// By default we generate LDR ETC1S data. 
		// Ideally call set_format_mode() above instead of directly manipulating the below fields. These individual parameters are for backwards API compatibility. 
		//   - If m_uastc is false you get ETC1S (the default).
		//   - If m_uastc is true, and m_hdr is not true, and m_xuastc_or_astc_ldr_basis_tex_format==-1, we generate UASTC 4x4 LDR data (8bpp with or without RDO). 
		//   - If m_uastc is true, and m_hdr is not true, and m_xuastc_or_astc_ldr_basis_tex_format!=-1, we generate XUASTC 4x4-12x12 or ASTC 4x4-12x12 LDR data, controlled by m_xuastc_or_astc_ldr_basis_tex_format.
		//   - If m_uastc is true and m_hdr is true, we generate 4x4 or 6x6 HDR data, controlled by m_hdr_mode.
				
		// True to generate UASTC .basis/.KTX2 file data, otherwise ETC1S.
		// Should be true for any non-ETC1S format (UASTC 4x4 LDR, UASTC 4x4 HDR, RDO ASTC 6x6 HDR, UASTC 6x6 HDR, or ASTC/XUASTC LDR 4x4-12x12).
		// Note: Ideally call set_format_mode() or set_format_mode_and_quality_effort() above instead. 
		// Many of these individual parameters are for backwards API compatibility.
		bool_param<false> m_uastc;

		// Set m_hdr to true to switch to UASTC HDR mode. m_hdr_mode then controls which format is output.
		// m_hdr_mode then controls which format is output (4x4, 6x6, or 6x6 intermediate).
		// Note: Ideally call set_format_mode() instead. This is for backwards API compatibility.
		bool_param<false> m_hdr;
				
		// If m_hdr is true, this specifies which mode we operate in (currently UASTC 4x4 HDR or ASTC 6x6 HDR). Defaults to UASTC 4x4 HDR for backwards compatibility.
		// Note: Ideally call set_format_mode() instead. This is for backwards API compatibility.
		hdr_modes m_hdr_mode;

		// If not -1: Generate XUASTC or ASTC LDR 4x4-12x12 files in the specified basis_tex_format (which also sets the ASTC block size). If -1 (the default), don't generate XUASTC/ASTC LDR files.
		// m_uastc must also be set to true if this is not -1.
		// Note: Ideally call set_format_mode() instead.
		param<int> m_xuastc_or_astc_ldr_basis_tex_format; // enum basis_tex_format
		
		// True to enable OpenCL if it's available. The compressor will fall back to CPU encoding if something goes wrong.
		bool_param<false> m_use_opencl;

		// If m_read_source_images is true, m_source_filenames (and optionally m_source_alpha_filenames) contains the filenames of PNG etc. images to read. 
		// Otherwise, the compressor processes the images in m_source_images or m_source_images_hdr.
		basisu::vector<std::string> m_source_filenames;
		basisu::vector<std::string> m_source_alpha_filenames;
		
		// An array of 2D LDR/SDR source images.
		basisu::vector<image> m_source_images;
		
		// An array of 2D HDR source images.
		basisu::vector<imagef> m_source_images_hdr;
				
		// Stores mipmaps starting from level 1. Level 0 is still stored in m_source_images, as usual.
		// If m_source_mipmaps isn't empty, automatic mipmap generation isn't done. m_source_mipmaps.size() MUST equal m_source_images.size() or the compressor returns an error.
		// The compressor applies the user-provided swizzling (in m_swizzle) to these images.
		basisu::vector< basisu::vector<image> > m_source_mipmap_images;

		basisu::vector< basisu::vector<imagef> > m_source_mipmap_images_hdr;
						
		// Filename of the output basis/ktx2 file
		std::string m_out_filename;

		// The params are done this way so we can detect when the user has explictly changed them.

		// Flip images across Y axis
		bool_param<false> m_y_flip;

		// If true, the compressor will print basis status to stdout during compression.
		bool_param<true> m_status_output;
		
		// Output debug information during compression
		bool_param<false> m_debug;
		
		// Low-level ETC1S data validation during encoding (slower/development).
		bool_param<false> m_validate_etc1s;
		
		// m_debug_images is pretty slow
		bool_param<false> m_debug_images;

		// ETC1S compression effort level, from 0 to BASISU_MAX_ETC1S_COMPRESSION_LEVEL (higher is slower). 
		// This parameter controls numerous internal encoding speed vs. compression efficiency/performance tradeoffs.
		// Note this is NOT the same as the ETC1S quality level, and most users shouldn't change this.
		param<int> m_etc1s_compression_level;
						
		// Use perceptual sRGB colorspace metrics instead of linear.
		// Note: You probably also want to set m_ktx2_srgb_transfer_func to match.
		// Note: This member variable was previously called "m_perceptual".
		bool_param<true> m_perceptual;

		// Disable selector RDO, for faster compression but larger files
		bool_param<false> m_no_selector_rdo;
		param<float> m_selector_rdo_thresh;

		bool_param<false> m_no_endpoint_rdo;
		param<float> m_endpoint_rdo_thresh;

		// Read source images from m_source_filenames/m_source_alpha_filenames
		bool_param<false> m_read_source_images;

		// Write the output basis/ktx2 file to disk using m_out_filename
		bool_param<false> m_write_output_basis_or_ktx2_files;
								
		// Compute and display image metrics 
		bool_param<false> m_compute_stats;

		// Print stats to stdout, if m_compute_stats is true.
		bool_param<true> m_print_stats;
		
		// Check to see if any input image has an alpha channel, if so then the output basis/ktx2 file will have alpha channels
		bool_param<true> m_check_for_alpha;
		
		// Always put alpha slices in the output basis/ktx2 file, even when the input doesn't have alpha
		bool_param<false> m_force_alpha; 

		// True to enable multithreading in various compressors. 
		// Note currently, some compressors (like ASTC/XUASTC LDR) will utilize threading anyway if the job pool is more than one thread.
		bool_param<true> m_multithreading;
		
		// Split the R channel to RGB and the G channel to alpha, then write a basis/ktx2 file with alpha channels
		uint8_t m_swizzle[4];

		// Renormalize normal map normals after loading image
		bool_param<false> m_renormalize;

		// If true the front end will not use 2 level endpoint codebook searching, for slightly higher quality but much slower execution.
		// Note some m_etc1s_compression_level's disable this automatically.
		bool_param<false> m_disable_hierarchical_endpoint_codebooks;
						
		// mipmap generation parameters
		bool_param<false> m_mip_gen;
		param<float> m_mip_scale;
		std::string m_mip_filter;
		bool_param<false> m_mip_srgb;
		bool_param<true> m_mip_premultiplied; // not currently supported
		bool_param<false> m_mip_renormalize; 
		bool_param<true> m_mip_wrapping;
		bool_param<true> m_mip_fast;
		param<int> m_mip_smallest_dimension;
						
		// ETC1S codebook size (quality) control. 
		// If m_quality_level (previously named m_etc1s_quality_level) != -1, it controls the quality level. It ranges from [1,255] or [BASISU_QUALITY_MIN, BASISU_QUALITY_MAX].
		// Otherwise m_max_endpoint_clusters/m_max_selector_clusters controls the codebook sizes directly.
		uint32_t m_etc1s_max_endpoint_clusters;
		uint32_t m_etc1s_max_selector_clusters;

		// Quality level (bitrate vs. distortion tradeoff) control for ETC1S or XUASTC LDR 4x4-12x12. 
		// ETC1S: Must set to [1,255] or [BASISU_QUALITY_MIN, BASISU_QUALITY_MAX] to control quality vs. bitrate. If -1 (the default!), quality is controlled by m_etc1s_max_endpoint_clusters and m_etc1s_max_selector_clusters directly.
		// XUASTC LDR: Must not be -1 for DCT.
		int m_quality_level; 
		
		// m_tex_type, m_userdata0, m_userdata1, m_framerate - These fields go directly into the .basis file header.
		basist::basis_texture_type m_tex_type;
		uint32_t m_userdata0;
		uint32_t m_userdata1;
		uint32_t m_us_per_frame;

		// UASTC LDR 4x4 parameters
		// cPackUASTCLevelDefault, etc.
		uint32_t m_pack_uastc_ldr_4x4_flags;
		bool_param<false> m_rdo_uastc_ldr_4x4;
		param<float> m_rdo_uastc_ldr_4x4_quality_scalar; // RDO lambda for UASTC 4x4 LDR
		param<int> m_rdo_uastc_ldr_4x4_dict_size;
		param<float> m_rdo_uastc_ldr_4x4_max_smooth_block_error_scale;
		param<float> m_rdo_uastc_ldr_4x4_smooth_block_max_std_dev;
		param<float> m_rdo_uastc_ldr_4x4_max_allowed_rms_increase_ratio;
		param<float> m_rdo_uastc_ldr_4x4_skip_block_rms_thresh;
		bool_param<true> m_rdo_uastc_ldr_4x4_favor_simpler_modes_in_rdo_mode;
		bool_param<true> m_rdo_uastc_ldr_4x4_multithreading;

		// Resample input texture after loading
		param<int> m_resample_width;
		param<int> m_resample_height;
		param<float> m_resample_factor;

		// ETC1S global codebook control
		const basist::basisu_lowlevel_etc1s_transcoder *m_pGlobal_codebooks;

		// KTX2 specific parameters.
		// Internally, the compressor always creates a .basis file then it converts that losslessly to KTX2.
		bool_param<false> m_create_ktx2_file;
		basist::ktx2_supercompression m_ktx2_uastc_supercompression;
		basist::ktx2_transcoder::key_value_vec m_ktx2_key_values;
		param<int> m_ktx2_zstd_supercompression_level;
		
		// Note: The default for this parameter (which used to be "m_ktx2_srgb_transfer_func") used to be false, now setting this to true and renaming to m_ktx2_and_basis_srgb_transfer_function.
		// Also see m_perceptual and m_mip_srgb, which should in most uses be the same.
		// This also controls the XUASTC LDR ASTC decode profile (linear vs. sRGB) in the simulated decoder block. 
		// For XUASTC LDR, it's also still used when generating .basis files vs. .KTX2.
		bool_param<true> m_ktx2_and_basis_srgb_transfer_function; // false = linear transfer function, true = sRGB transfer function

		// HDR codec specific options
		uastc_hdr_4x4_codec_options m_uastc_hdr_4x4_options;
		astc_6x6_hdr::astc_hdr_6x6_global_config m_astc_hdr_6x6_options; // also UASTC HDR 6x6i

		// True to try transcoding the generated output after compression to a few formats.
		bool_param<false> m_validate_output_data;
		
		// The flags to use while transcoding if m_validate_output_data
		param<uint32_t> m_transcode_flags;

		// LDR->HDR upconversion parameters.
		// 
		// If true, LDR images (such as PNG) will be converted to normalized [0,1] linear light (via a sRGB->Linear conversion), or absolute luminance (nits or candelas per meter squared), and then processed as HDR. 
		// Otherwise, LDR images are assumed to already be in linear light (i.e. they don't use the sRGB transfer function).
		bool_param<true> m_ldr_hdr_upconversion_srgb_to_linear;
		
		// m_ldr_hdr_upconversion_nit_multiplier is only used when loading SDR/LDR images and compressing to an HDR output format.
		// By default m_ldr_hdr_upconversion_nit_multiplier is 0. It's an override for the default, which is now 100.0 nits (LDR_TO_HDR_NITS).
		// UASTC HDR 4x4: The default multiplier of 1.0 was previously used in this codec's original release. Note this encoder isn't dependent on absolute nits, unlike the ASTC 6x6 HDR encoder.
		// RDO ASTC HDR 6x6/UASTC HDR 6x6i: These encoders expect inputs in absolute nits, so the LDR upconversion luminance multiplier default will be 100 nits. (Most SDR monitors were/are 80-100 nits or so.)
		param<float> m_ldr_hdr_upconversion_nit_multiplier;

		// The optional sRGB space bias to use during LDR->HDR upconversion. Should be between [0,.49] or so. Only applied on black (0.0) color components.
		// Defaults to no bias (0.0f).
		param<float> m_ldr_hdr_upconversion_black_bias;

		// If true, ASTC HDR quality is favored more than BC6H quality by the dual target encoder. Otherwise it's a rough balance.
		// UASTC HDR 4x4
		bool_param<false> m_hdr_favor_astc;

		// XUASTC LDR 4x4-12x12 specific options
		param<int> m_xuastc_ldr_effort_level;
		bool_param<false> m_xuastc_ldr_use_dct; // set the DCT quality above using m_quality_level, [1,100]
		bool_param<false> m_xuastc_ldr_use_lossy_supercompression; // allows the compressor to introduce a bounded amount of distortion if doing so would make smaller files (actually ASTC or XUASTC)
		bool_param<false> m_xuastc_ldr_force_disable_subsets; // disable 2-3 subset usage in all effort levels, faster encoding, faster transcoding to BC7, but lower quality)
		bool_param<false> m_xuastc_ldr_force_disable_rgb_dual_plane; // disable RGB dual plane usage (still can use dual plane on alpha blocks), for faster transcoding to BC7 but lower quality
		param<int> m_xuastc_ldr_syntax; // favor faster decompression over ratio, default is basist::astc_ldr_t::xuastc_ldr_syntax::cFullZstd (fastest transcoding but lower ratio)
		uint32_t m_xuastc_ldr_channel_weights[4];
		bool_param<false> m_xuastc_ldr_blurring; // experimental, not recommended, very slow

		// XUASTC Lossy supercompression PSNR threshold parameters
		param<float> m_ls_min_psnr, m_ls_min_alpha_psnr;
		param<float> m_ls_thresh_psnr, m_ls_thresh_alpha_psnr;
		param<float> m_ls_thresh_edge_psnr, m_ls_thresh_edge_alpha_psnr;
												
		// Job pool, MUST not be nullptr;
		job_pool *m_pJob_pool;

		// Returns the current format mode as set by set_format_mode() above.
		// Because of backwards API compatibility we don't use this directly yet, it's just here to aid the transition to the new API.
		basist::basis_tex_format get_format_mode() const { return m_format_mode; }

	private:
		// This is set by set_format_mode() above. For backwards API compat we don't use it directly, it's just here to aid the transition to the new API.
		basist::basis_tex_format m_format_mode;
	};

	// Important: basisu_encoder_init() MUST be called first before using this class.
	class basis_compressor
	{
		BASISU_NO_EQUALS_OR_COPY_CONSTRUCT(basis_compressor);

	public:
		basis_compressor();
		~basis_compressor();

		// Note it *should* be possible to call init() multiple times with different inputs, but this scenario isn't well tested. Ideally, create 1 object, compress, then delete it.
		bool init(const basis_compressor_params &params);
		
		enum error_code
		{
			cECSuccess = 0,
			cECFailedInitializing,
			cECFailedReadingSourceImages,
			cECFailedValidating,
			cECFailedEncodeUASTC,
			cECFailedFrontEnd,
			cECFailedFrontendExtract,
			cECFailedBackend,
			cECFailedCreateBasisFile,
			cECFailedWritingOutput,
			cECFailedUASTCRDOPostProcess,
			cECFailedCreateKTX2File,
			cECFailedInvalidParameters
		};

		error_code process();

		// The output .basis file will always be valid of process() succeeded.
		const uint8_vec &get_output_basis_file() const { return m_output_basis_file; }
		
		// The output .ktx2 file will only be valid if m_create_ktx2_file was true and process() succeeded.
		const uint8_vec& get_output_ktx2_file() const { return m_output_ktx2_file; }

		const basisu::vector<image_stats> &get_stats() const { return m_stats; }

		// Sum of all slice orig pixels. Intended for statistics display.
		uint64_t get_total_slice_orig_texels() const { return m_total_slice_orig_texels; }

		uint64_t get_basis_file_size() const { return m_basis_file_size; }
		double get_basis_bits_per_texel() const { return m_basis_bits_per_texel; }

		uint64_t get_ktx2_file_size() const { return m_ktx2_file_size; }
		double get_ktx2_bits_per_texel() const { return m_ktx2_bits_per_texel; }
		
		bool get_any_source_image_has_alpha() const { return m_any_source_image_has_alpha; }

		bool get_opencl_failed() const { return m_opencl_failed; }
								
	private:
		basis_compressor_params m_params;
				
		opencl_context_ptr m_pOpenCL_context;

		// the output mode/codec
		basist::basis_tex_format m_fmt_mode; 
		
		// the output mode/codec's block width/height
		uint32_t m_fmt_mode_block_width; 
		uint32_t m_fmt_mode_block_height;
		
		// Note these images are expanded if necessary (duplicating cols/rows) to account for block dimensions.
		basisu::vector<image> m_slice_images;
		basisu::vector<imagef> m_slice_images_hdr;

		basisu::vector<image_stats> m_stats;

		uint64_t m_total_slice_orig_texels;

		uint64_t m_basis_file_size;
		double m_basis_bits_per_texel;

		uint64_t m_ktx2_file_size;
		double m_ktx2_bits_per_texel;
						
		basisu_backend_slice_desc_vec m_slice_descs;

		uint32_t m_total_blocks;
		
		basisu_frontend m_frontend;

		// These are 4x4 blocks.
		pixel_block_vec m_source_blocks;
		pixel_block_hdr_vec m_source_blocks_hdr;

		basisu::vector<gpu_image> m_frontend_output_textures;

		basisu::vector<gpu_image> m_best_etc1s_images;
		basisu::vector<image> m_best_etc1s_images_unpacked;

		basisu_backend m_backend;

		basisu_file m_basis_file;

		basisu::vector<gpu_image> m_decoded_output_textures;			// BC6H in HDR mode
		basisu::vector<image> m_decoded_output_textures_unpacked;
		
		basisu::vector<gpu_image> m_decoded_output_textures_bc7;
		basisu::vector<image> m_decoded_output_textures_unpacked_bc7;

		basisu::vector<imagef> m_decoded_output_textures_bc6h_hdr_unpacked;	// BC6H in HDR mode

		basisu::vector<gpu_image> m_decoded_output_textures_astc_hdr;
		basisu::vector<imagef> m_decoded_output_textures_astc_hdr_unpacked;

		uint8_vec m_output_basis_file;
		uint8_vec m_output_ktx2_file;
		
		basisu::vector<gpu_image> m_uastc_slice_textures;
		basisu_backend_output m_uastc_backend_output;

		// The amount the HDR input has to be scaled up in case it had to be rescaled to fit into half floats.
		float m_hdr_image_scale; 
		
		// The upconversion multiplier used to load LDR images in HDR mode.
		float m_ldr_to_hdr_upconversion_nit_multiplier;
				
		// True if any loaded source images were LDR and upconverted to HDR.
		bool m_upconverted_any_ldr_images;

		bool m_any_source_image_has_alpha;

		bool m_opencl_failed;

		void check_for_hdr_inputs();
		bool sanity_check_input_params();
		void clean_hdr_image(imagef& src_img);
		bool read_dds_source_images();
		bool read_source_images();
		bool extract_source_blocks();
		bool process_frontend();
		bool extract_frontend_texture_data();
		bool process_backend();
		bool create_basis_file_and_transcode();
		bool write_hdr_debug_images(const char* pBasename, const imagef& img, uint32_t width, uint32_t height);
		bool write_output_files_and_compute_stats();
		error_code encode_slices_to_astc_6x6_hdr();
		error_code encode_slices_to_uastc_4x4_hdr();
		error_code encode_slices_to_uastc_4x4_ldr();
		error_code encode_slices_to_xuastc_or_astc_ldr();
		bool generate_mipmaps(const imagef& img, basisu::vector<imagef>& mips, bool has_alpha);
		bool generate_mipmaps(const image &img, basisu::vector<image> &mips, bool has_alpha);
		bool validate_texture_type_constraints();
		bool validate_ktx2_constraints();
		bool get_dfd(uint8_vec& dfd, const basist::ktx2_header& hdr);
		bool create_ktx2_file();
		bool pick_format_mode();

		uint32_t get_block_width() const { return m_fmt_mode_block_width; }
		uint32_t get_block_height() const { return m_fmt_mode_block_height; }
	};
				
	// Alternative simple C-style wrapper API around the basis_compressor class. 
	// This doesn't expose every encoder feature, but it's enough to get going.
	// Important: basisu_encoder_init() MUST be called first before calling these functions.
	//
	// Input parameters:
	//   source_images: Array of "image" objects, one per mipmap level, largest mipmap level first.
	// OR
	//   pImageRGBA: pointer to a 32-bpp RGBx or RGBA raster image, R first in memory, A last. Top scanline first in memory.
	//   width/height/pitch_in_pixels: dimensions of pImageRGBA
	//   
	// flags_and_quality: Combination of the above flags logically OR'd with the ETC1S or UASTC quality or effort level.
	//    Note: basis_compress2() variants below accept the new-style "quality_level" (0-100) and "effort_level" (0-10) parameters instead of packing them into flags_and_quality.
	//	  In ETC1S mode, the lower 8-bits are the ETC1S quality level which ranges from [1,255] (higher=better quality/larger files)
	//	  In UASTC LDR 4x4 mode, the lower 8-bits are the UASTC LDR/HDR pack or effort level (see cPackUASTCLevelFastest to cPackUASTCLevelVerySlow). Fastest/lowest quality is 0, so be sure to set it correctly. Valid values are [0,4] for both LDR/HDR.
	//    In UASTC HDR 4x4 mode, the lower 8-bits are the codec's effort level. Valid range is [uastc_hdr_4x4_codec_options::cMinLevel, uastc_hdr_4x4_codec_options::cMaxLevel]. Higher=better quality, but slower.
	//    In RDO ASTC HDR 6x6/UASTC HDR 6x6 mode, the lower 8-bits are the codec's effort level. Valid range is [0,astc_6x6_hdr::ASTC_HDR_6X6_MAX_USER_COMP_LEVEL]. Higher levels=better quality, but slower.
	//    In XUASTC/ASTC LDR 4x4-12x12 mode, the lower 8-bits are the compressor's effort level from [0,10] (astc_ldr_t::EFFORT_LEVEL_MIN, astc_ldr_t::EFFORT_LEVEL_MAX).
	// 
	// float uastc_rdo_or_dct_quality:
	//    UASTC LDR 4x4 RDO quality level: RDO lambda setting - 0=no change/highest quality. Higher values lower quality but increase compressibility, initially try .5-1.5.
	//    RDO ASTC 6x6 HDR/UASTC 6x6 HDR: RDO lambda setting. 0=no change/highest quality. Higher values lower quality but increase compressibility, initially try 250-2000 (HDR) or 1000-10000 (LDR/SDR inputs upconverted to HDR). 
	//	  In XUASTC/ASTC LDR 4x4-12x12 mode, this is the [1,100] weight grid DCT quality level.
	// 
	// pSize: Returns the output data's compressed size in bytes
	// 
	// Return value is the compressed .basis or .ktx2 file data, or nullptr on failure. Must call basis_free() to free it.
	enum
	{
		cFlagUseOpenCL = 1 << 8,		// use OpenCL if available
		cFlagThreaded = 1 << 9,			// use multiple threads for compression
		cFlagDebug = 1 << 10,			// enable debug output

		cFlagKTX2 = 1 << 11,			// generate a KTX2 file
		cFlagKTX2UASTCSuperCompression = 1 << 12, // use KTX2 Zstd supercompression on non-supercompressed formats that support it.

		cFlagSRGB = 1 << 13,			// input texture is sRGB, use perceptual colorspace metrics, also use sRGB filtering during mipmap gen, and also sets KTX2/.basis output transfer func to sRGB
		cFlagGenMipsClamp = 1 << 14,	// generate mipmaps with clamp addressing
		cFlagGenMipsWrap = 1 << 15,		// generate mipmaps with wrap addressing
		
		cFlagYFlip = 1 << 16,			// flip source image on Y axis before compression
		
		// Note 11/18/2025: cFlagUASTCRDO flag is now ignored. Now if uastc_rdo_or_dct_quality>0 in UASTC LDR 4x4 mode, you automatically get RDO.
		//cFlagUASTCRDO = 1 << 17,		// use RDO postprocessing when generating UASTC LDR 4x4 files (must set uastc_rdo_or_dct_quality to the quality scalar) 
		
		cFlagPrintStats = 1 << 18,		// print image stats to stdout
		cFlagPrintStatus = 1 << 19,		// print status to stdout
		
		cFlagDebugImages = 1 << 20,		// enable debug image generation (for development, slower)

		cFlagREC2020 = 1 << 21,			// treat input as REC 2020 vs. the default 709 (for codecs that support this, currently UASTC HDR and ASTC 6x6), bit is always placed into KTX2 DFD
		
		cFlagValidateOutput = 1 << 22,	// transcode the output after encoding for testing
				
		// XUASTC LDR profile: full arith, hybrid or full zstd (see basist::astc_ldr_t::xuastc_ldr_syntax)
		cFlagXUASTCLDRSyntaxFullArith	= 0 << 23,
		cFlagXUASTCLDRSyntaxHybrid		= 1 << 23,
		cFlagXUASTCLDRSyntaxFullZStd	= 2 << 23,
		
		cFlagXUASTCLDRSyntaxShift		= 23,
		cFlagXUASTCLDRSyntaxMask		= 3,
		
		// Texture Type: 2D, 2D Array, Cubemap Array, or Texture Video (see enum basis_texture_type). Defaults to plain 2D.
		cFlagTextureType2D				= 0 << 25,
		cFlagTextureType2DArray			= 1 << 25,
		cFlagTextureTypeCubemapArray	= 2 << 25,
		cFlagTextureTypeVideoFrames		= 3 << 25,

		cFlagTextureTypeShift			= 25,
		cFlagTextureTypeMask			= 3,
	};

	void* basis_compress_internal(
		basist::basis_tex_format mode,
		const basisu::vector<image>* pSource_images,
		const basisu::vector<imagef>* pSource_images_hdr,
		uint32_t flags_and_quality, float uastc_rdo_or_dct_quality,
		size_t* pSize,
		image_stats* pStats,
		int quality_level = -1, int effort_level = -1);

	// This function accepts an array of source images. 
	// If more than one image is provided, it's assumed the images form a mipmap pyramid and automatic mipmap generation is disabled.
	// Returns a pointer to the compressed .basis or .ktx2 file data. *pSize is the size of the compressed data. 
	// Important: The returned block MUST be manually freed using basis_free_data().
	// basisu_encoder_init() MUST be called first!
	// LDR version.
	void* basis_compress(
		basist::basis_tex_format mode,
		const basisu::vector<image> &source_images,
		uint32_t flags_and_quality, float uastc_rdo_or_dct_quality,
		size_t* pSize,
		image_stats* pStats = nullptr);

	// HDR-only version.
	// Important: The returned block MUST be manually freed using basis_free_data().
	void* basis_compress(
		basist::basis_tex_format mode,
		const basisu::vector<imagef>& source_images_hdr,
		uint32_t flags_and_quality, float uastc_rdo_or_dct_quality,
		size_t* pSize,
		image_stats* pStats = nullptr);

	// This function only accepts a single LDR source image. It's just a wrapper for basis_compress() above.
	// Important: The returned block MUST be manually freed using basis_free_data().
	void* basis_compress(
		basist::basis_tex_format mode,
		const uint8_t* pImageRGBA, uint32_t width, uint32_t height, uint32_t pitch_in_pixels,
		uint32_t flags_and_quality, float uastc_rdo_or_dct_quality,
		size_t* pSize,
		image_stats* pStats = nullptr);

	// basis_compress2() variants accept the new unified quality_level and effort_level parameters instead of the old flags/float uastc_rdo_or_dct_quality parameter.
	// quality_level must be [0,100], effort_level [0,10].
	void* basis_compress2(
		basist::basis_tex_format mode,
		const basisu::vector<image>& source_images,
		uint32_t flags_and_quality, int quality_level, int effort_level,
		size_t* pSize,
		image_stats* pStats = nullptr);

	void* basis_compress2(
		basist::basis_tex_format mode,
		const basisu::vector<imagef>& source_images_hdr,
		uint32_t flags_and_quality, int quality_level, int effort_level,
		size_t* pSize,
		image_stats* pStats = nullptr);

	void* basis_compress2(
		basist::basis_tex_format mode,
		const uint8_t* pImageRGBA, uint32_t width, uint32_t height, uint32_t pitch_in_pixels,
		uint32_t flags_and_quality, int quality_level, int effort_level,
		size_t* pSize,
		image_stats* pStats = nullptr);

	// Frees the dynamically allocated file data returned by basis_compress().
	// This MUST be called on the pointer returned by basis_compress() when you're done with it.
	void basis_free_data(void* p);

	// Runs a short benchmark using synthetic image data to time OpenCL encoding vs. CPU encoding, with multithreading enabled.
	// Returns true if opencl is worth using on this system, otherwise false.
	// If pOpenCL_failed is not null, it will be set to true if OpenCL encoding failed *on this particular machine/driver/BasisU version* and the encoder falled back to CPU encoding.
	// basisu_encoder_init() MUST be called first. If OpenCL support wasn't enabled this always returns false.
	bool basis_benchmark_etc1s_opencl(bool *pOpenCL_failed = nullptr);

	// Parallel compression API
	struct parallel_results
	{
		double m_total_time;
		basis_compressor::error_code m_error_code;
		uint8_vec m_basis_file;
		uint8_vec m_ktx2_file;
		basisu::vector<image_stats> m_stats;
		double m_basis_bits_per_texel;
		bool m_any_source_image_has_alpha;

		parallel_results() 
		{
			clear();
		}

		void clear()
		{
			m_total_time = 0.0f;
			m_error_code = basis_compressor::cECFailedInitializing;
			m_basis_file.clear();
			m_ktx2_file.clear();
			m_stats.clear();
			m_basis_bits_per_texel = 0.0f;
			m_any_source_image_has_alpha = false;
		}
	};
		
	// Compresses an array of input textures across total_threads threads using the basis_compressor class.
	// Compressing multiple textures at a time is substantially more efficient than just compressing one at a time.
	// total_threads must be >= 1.
	bool basis_parallel_compress(
		uint32_t total_threads,
		const basisu::vector<basis_compressor_params> &params_vec,
		basisu::vector< parallel_results > &results_vec);
		
} // namespace basisu

