Adding initial support for user-provided mipmaps. This isn't hooked up to the command line tool or the JS wrappers yet.
diff --git a/encoder/basisu_comp.cpp b/encoder/basisu_comp.cpp
index 0e57a4c..e9c9d1a 100644
--- a/encoder/basisu_comp.cpp
+++ b/encoder/basisu_comp.cpp
@@ -71,8 +71,16 @@
 
 			debug_printf("Has global selector codebook: %i\n", m_params.m_pSel_codebook != nullptr);
 
-			debug_printf("Source images: %u, source filenames: %u, source alpha filenames: %i\n", 
-				(uint32_t)m_params.m_source_images.size(), (uint32_t)m_params.m_source_filenames.size(), (uint32_t)m_params.m_source_alpha_filenames.size());
+			debug_printf("Source images: %u, source filenames: %u, source alpha filenames: %i, Source mipmap images: %u\n",
+				m_params.m_source_images.size(), m_params.m_source_filenames.size(), m_params.m_source_alpha_filenames.size(), m_params.m_source_mipmap_images.size());
+
+			if (m_params.m_source_mipmap_images.size())
+			{
+				debug_printf("m_source_mipmap_images array sizes:\n");
+				for (uint32_t i = 0; i < m_params.m_source_mipmap_images.size(); i++)
+					debug_printf("%u ", m_params.m_source_mipmap_images[i].size());
+				debug_printf("\n");
+			}
 
 			PRINT_BOOL_VALUE(m_uastc);
 			PRINT_BOOL_VALUE(m_y_flip);
@@ -540,7 +548,7 @@
 
 				debug_printf("Resampling to %ix%i\n", new_width, new_height);
 
-				// TODO: A box filter - kaiser looks too sharp on video.
+				// TODO: A box filter - kaiser looks too sharp on video. Let the caller control this.
 				image temp_img(new_width, new_height);
 				image_resample(file_image, temp_img, m_params.m_perceptual, "box"); // "kaiser");
 				temp_img.swap(file_image);
@@ -562,6 +570,39 @@
 			source_filenames.push_back(pSource_filename);
 		}
 
+		// Check if the caller has generated their own mipmaps. 
+		if (m_params.m_source_mipmap_images.size())
+		{
+			// Make sure they've passed us enough mipmap chains.
+			if ((m_params.m_source_images.size() != m_params.m_source_mipmap_images.size()) || (total_source_files != m_params.m_source_images.size()))
+			{
+				error_printf("basis_compressor::read_source_images(): m_params.m_source_mipmap_images.size() must equal m_params.m_source_images.size()!\n");
+				return false;
+			}
+
+			// Check if any of the user-supplied mipmap levels has alpha.
+			// We're assuming the user has already preswizzled their mipmap source images.
+			if (!m_any_source_image_has_alpha)
+			{
+				for (uint32_t source_file_index = 0; source_file_index < total_source_files; source_file_index++)
+				{
+					for (uint32_t mip_index = 0; mip_index < m_params.m_source_mipmap_images[source_file_index].size(); mip_index++)
+					{
+						const image& mip_img = m_params.m_source_mipmap_images[source_file_index][mip_index];
+
+						if (mip_img.has_alpha())
+						{
+							m_any_source_image_has_alpha = true;
+							break;
+						}
+					}
+
+					if (m_any_source_image_has_alpha)
+						break;
+				}
+			}
+		}
+
 		debug_printf("Any source image has alpha: %u\n", m_any_source_image_has_alpha);
 
 		for (uint32_t source_file_index = 0; source_file_index < total_source_files; source_file_index++)
@@ -573,10 +614,22 @@
 			basisu::vector<image> slices;
 			
 			slices.reserve(32);
+			
+			// The first (largest) mipmap level.
 			slices.push_back(file_image);
-									
-			if (m_params.m_mip_gen)
+			
+			if (m_params.m_source_mipmap_images.size())
 			{
+				// User-provided mipmaps for each layer or image in the texture array.
+				for (uint32_t mip_index = 0; mip_index < m_params.m_source_mipmap_images[source_file_index].size(); mip_index++)
+				{
+					const image& mip_img = m_params.m_source_mipmap_images[source_file_index][mip_index];
+					slices.push_back(mip_img);
+				}
+			}
+			else if (m_params.m_mip_gen)
+			{
+				// Automatically generate mipmaps.
 				if (!generate_mipmaps(file_image, slices, m_any_source_image_has_alpha))
 					return false;
 			}
diff --git a/encoder/basisu_comp.h b/encoder/basisu_comp.h
index fd8a913..1964565 100644
--- a/encoder/basisu_comp.h
+++ b/encoder/basisu_comp.h
@@ -241,6 +241,7 @@
 			m_source_alpha_filenames.clear();
 
 			m_source_images.clear();
+			m_source_mipmap_images.clear();
 
 			m_out_filename.clear();
 
@@ -329,7 +330,11 @@
 		basisu::vector<std::string> m_source_alpha_filenames;
 		
 		basisu::vector<image> m_source_images;
-		// TODO: Allow caller to supply their own mipmaps
+		
+		// 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.
+		// Note the compressor doesn't apply the user-provided swizzling (in m_swizzle), or any other preprocessing, to these images.
+		basisu::vector< basisu::vector<image> > m_source_mipmap_images;
 						
 		// Filename of the output basis file
 		std::string m_out_filename;
diff --git a/webgl/encoder/build/basis_encoder.wasm b/webgl/encoder/build/basis_encoder.wasm
index 379d4a9..e64b734 100644
--- a/webgl/encoder/build/basis_encoder.wasm
+++ b/webgl/encoder/build/basis_encoder.wasm
Binary files differ