diff --git a/third_party/crunch/crn.2008.sln b/third_party/crunch/crn.2008.sln
new file mode 100644
index 000000000..077ae9424
--- /dev/null
+++ b/third_party/crunch/crn.2008.sln
@@ -0,0 +1,55 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "crunch", "crunch\crunch.2008.vcproj", "{8F645BA1-B996-49EB-859B-970A671DE05D}"
+ ProjectSection(ProjectDependencies) = postProject
+ {CF2E70E8-7133-4D96-92C7-68BB406C0664} = {CF2E70E8-7133-4D96-92C7-68BB406C0664}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "crnlib", "crnlib\crnlib.2008.vcproj", "{CF2E70E8-7133-4D96-92C7-68BB406C0664}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug_DLL|Win32 = Debug_DLL|Win32
+ Debug_DLL|x64 = Debug_DLL|x64
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release_DLL|Win32 = Release_DLL|Win32
+ Release_DLL|x64 = Release_DLL|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {8F645BA1-B996-49EB-859B-970A671DE05D}.Debug_DLL|Win32.ActiveCfg = Debug|Win32
+ {8F645BA1-B996-49EB-859B-970A671DE05D}.Debug_DLL|x64.ActiveCfg = Debug|x64
+ {8F645BA1-B996-49EB-859B-970A671DE05D}.Debug|Win32.ActiveCfg = Debug|Win32
+ {8F645BA1-B996-49EB-859B-970A671DE05D}.Debug|Win32.Build.0 = Debug|Win32
+ {8F645BA1-B996-49EB-859B-970A671DE05D}.Debug|x64.ActiveCfg = Debug|x64
+ {8F645BA1-B996-49EB-859B-970A671DE05D}.Debug|x64.Build.0 = Debug|x64
+ {8F645BA1-B996-49EB-859B-970A671DE05D}.Release_DLL|Win32.ActiveCfg = Release|Win32
+ {8F645BA1-B996-49EB-859B-970A671DE05D}.Release_DLL|x64.ActiveCfg = Release|x64
+ {8F645BA1-B996-49EB-859B-970A671DE05D}.Release|Win32.ActiveCfg = Release|Win32
+ {8F645BA1-B996-49EB-859B-970A671DE05D}.Release|Win32.Build.0 = Release|Win32
+ {8F645BA1-B996-49EB-859B-970A671DE05D}.Release|x64.ActiveCfg = Release|x64
+ {8F645BA1-B996-49EB-859B-970A671DE05D}.Release|x64.Build.0 = Release|x64
+ {CF2E70E8-7133-4D96-92C7-68BB406C0664}.Debug_DLL|Win32.ActiveCfg = Debug_DLL|Win32
+ {CF2E70E8-7133-4D96-92C7-68BB406C0664}.Debug_DLL|Win32.Build.0 = Debug_DLL|Win32
+ {CF2E70E8-7133-4D96-92C7-68BB406C0664}.Debug_DLL|x64.ActiveCfg = Debug_DLL|x64
+ {CF2E70E8-7133-4D96-92C7-68BB406C0664}.Debug_DLL|x64.Build.0 = Debug_DLL|x64
+ {CF2E70E8-7133-4D96-92C7-68BB406C0664}.Debug|Win32.ActiveCfg = Debug|Win32
+ {CF2E70E8-7133-4D96-92C7-68BB406C0664}.Debug|Win32.Build.0 = Debug|Win32
+ {CF2E70E8-7133-4D96-92C7-68BB406C0664}.Debug|x64.ActiveCfg = Debug|x64
+ {CF2E70E8-7133-4D96-92C7-68BB406C0664}.Debug|x64.Build.0 = Debug|x64
+ {CF2E70E8-7133-4D96-92C7-68BB406C0664}.Release_DLL|Win32.ActiveCfg = Release_DLL|Win32
+ {CF2E70E8-7133-4D96-92C7-68BB406C0664}.Release_DLL|Win32.Build.0 = Release_DLL|Win32
+ {CF2E70E8-7133-4D96-92C7-68BB406C0664}.Release_DLL|x64.ActiveCfg = Release_DLL|x64
+ {CF2E70E8-7133-4D96-92C7-68BB406C0664}.Release_DLL|x64.Build.0 = Release_DLL|x64
+ {CF2E70E8-7133-4D96-92C7-68BB406C0664}.Release|Win32.ActiveCfg = Release|Win32
+ {CF2E70E8-7133-4D96-92C7-68BB406C0664}.Release|Win32.Build.0 = Release|Win32
+ {CF2E70E8-7133-4D96-92C7-68BB406C0664}.Release|x64.ActiveCfg = Release|x64
+ {CF2E70E8-7133-4D96-92C7-68BB406C0664}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/third_party/crunch/crn.workspace b/third_party/crunch/crn.workspace
new file mode 100644
index 000000000..dcc08dfac
--- /dev/null
+++ b/third_party/crunch/crn.workspace
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/third_party/crunch/crn_examples.2008.sln b/third_party/crunch/crn_examples.2008.sln
new file mode 100644
index 000000000..92d04e53b
--- /dev/null
+++ b/third_party/crunch/crn_examples.2008.sln
@@ -0,0 +1,74 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example1", "example1\example1.2008.vcproj", "{8F745B42-F996-49EB-859B-970A671DE05D}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example2", "example2\example2.2008.vcproj", "{AF745B42-F996-49EB-859B-970A671DEF5E}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example3", "example3\example3.2008.vcproj", "{AF745B42-E296-46EB-859B-970A671DEF5E}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug_DLL|Win32 = Debug_DLL|Win32
+ Debug_DLL|x64 = Debug_DLL|x64
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release_DLL|Win32 = Release_DLL|Win32
+ Release_DLL|x64 = Release_DLL|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {8F745B42-F996-49EB-859B-970A671DE05D}.Debug_DLL|Win32.ActiveCfg = Debug_DLL|Win32
+ {8F745B42-F996-49EB-859B-970A671DE05D}.Debug_DLL|Win32.Build.0 = Debug_DLL|Win32
+ {8F745B42-F996-49EB-859B-970A671DE05D}.Debug_DLL|x64.ActiveCfg = Debug_DLL|x64
+ {8F745B42-F996-49EB-859B-970A671DE05D}.Debug_DLL|x64.Build.0 = Debug_DLL|x64
+ {8F745B42-F996-49EB-859B-970A671DE05D}.Debug|Win32.ActiveCfg = Debug|Win32
+ {8F745B42-F996-49EB-859B-970A671DE05D}.Debug|Win32.Build.0 = Debug|Win32
+ {8F745B42-F996-49EB-859B-970A671DE05D}.Debug|x64.ActiveCfg = Debug|x64
+ {8F745B42-F996-49EB-859B-970A671DE05D}.Debug|x64.Build.0 = Debug|x64
+ {8F745B42-F996-49EB-859B-970A671DE05D}.Release_DLL|Win32.ActiveCfg = Release_DLL|Win32
+ {8F745B42-F996-49EB-859B-970A671DE05D}.Release_DLL|Win32.Build.0 = Release_DLL|Win32
+ {8F745B42-F996-49EB-859B-970A671DE05D}.Release_DLL|x64.ActiveCfg = Release_DLL|x64
+ {8F745B42-F996-49EB-859B-970A671DE05D}.Release_DLL|x64.Build.0 = Release_DLL|x64
+ {8F745B42-F996-49EB-859B-970A671DE05D}.Release|Win32.ActiveCfg = Release|Win32
+ {8F745B42-F996-49EB-859B-970A671DE05D}.Release|Win32.Build.0 = Release|Win32
+ {8F745B42-F996-49EB-859B-970A671DE05D}.Release|x64.ActiveCfg = Release|x64
+ {8F745B42-F996-49EB-859B-970A671DE05D}.Release|x64.Build.0 = Release|x64
+ {AF745B42-F996-49EB-859B-970A671DEF5E}.Debug_DLL|Win32.ActiveCfg = Debug_DLL|Win32
+ {AF745B42-F996-49EB-859B-970A671DEF5E}.Debug_DLL|Win32.Build.0 = Debug_DLL|Win32
+ {AF745B42-F996-49EB-859B-970A671DEF5E}.Debug_DLL|x64.ActiveCfg = Debug_DLL|x64
+ {AF745B42-F996-49EB-859B-970A671DEF5E}.Debug_DLL|x64.Build.0 = Debug_DLL|x64
+ {AF745B42-F996-49EB-859B-970A671DEF5E}.Debug|Win32.ActiveCfg = Debug|Win32
+ {AF745B42-F996-49EB-859B-970A671DEF5E}.Debug|Win32.Build.0 = Debug|Win32
+ {AF745B42-F996-49EB-859B-970A671DEF5E}.Debug|x64.ActiveCfg = Debug|x64
+ {AF745B42-F996-49EB-859B-970A671DEF5E}.Debug|x64.Build.0 = Debug|x64
+ {AF745B42-F996-49EB-859B-970A671DEF5E}.Release_DLL|Win32.ActiveCfg = Release_DLL|Win32
+ {AF745B42-F996-49EB-859B-970A671DEF5E}.Release_DLL|Win32.Build.0 = Release_DLL|Win32
+ {AF745B42-F996-49EB-859B-970A671DEF5E}.Release_DLL|x64.ActiveCfg = Release_DLL|x64
+ {AF745B42-F996-49EB-859B-970A671DEF5E}.Release_DLL|x64.Build.0 = Release_DLL|x64
+ {AF745B42-F996-49EB-859B-970A671DEF5E}.Release|Win32.ActiveCfg = Release|Win32
+ {AF745B42-F996-49EB-859B-970A671DEF5E}.Release|Win32.Build.0 = Release|Win32
+ {AF745B42-F996-49EB-859B-970A671DEF5E}.Release|x64.ActiveCfg = Release|x64
+ {AF745B42-F996-49EB-859B-970A671DEF5E}.Release|x64.Build.0 = Release|x64
+ {AF745B42-E296-46EB-859B-970A671DEF5E}.Debug_DLL|Win32.ActiveCfg = Debug_DLL|Win32
+ {AF745B42-E296-46EB-859B-970A671DEF5E}.Debug_DLL|Win32.Build.0 = Debug_DLL|Win32
+ {AF745B42-E296-46EB-859B-970A671DEF5E}.Debug_DLL|x64.ActiveCfg = Debug_DLL|x64
+ {AF745B42-E296-46EB-859B-970A671DEF5E}.Debug_DLL|x64.Build.0 = Debug_DLL|x64
+ {AF745B42-E296-46EB-859B-970A671DEF5E}.Debug|Win32.ActiveCfg = Debug|Win32
+ {AF745B42-E296-46EB-859B-970A671DEF5E}.Debug|Win32.Build.0 = Debug|Win32
+ {AF745B42-E296-46EB-859B-970A671DEF5E}.Debug|x64.ActiveCfg = Debug|x64
+ {AF745B42-E296-46EB-859B-970A671DEF5E}.Debug|x64.Build.0 = Debug|x64
+ {AF745B42-E296-46EB-859B-970A671DEF5E}.Release_DLL|Win32.ActiveCfg = Release_DLL|Win32
+ {AF745B42-E296-46EB-859B-970A671DEF5E}.Release_DLL|Win32.Build.0 = Release_DLL|Win32
+ {AF745B42-E296-46EB-859B-970A671DEF5E}.Release_DLL|x64.ActiveCfg = Release_DLL|x64
+ {AF745B42-E296-46EB-859B-970A671DEF5E}.Release_DLL|x64.Build.0 = Release_DLL|x64
+ {AF745B42-E296-46EB-859B-970A671DEF5E}.Release|Win32.ActiveCfg = Release|Win32
+ {AF745B42-E296-46EB-859B-970A671DEF5E}.Release|Win32.Build.0 = Release|Win32
+ {AF745B42-E296-46EB-859B-970A671DEF5E}.Release|x64.ActiveCfg = Release|x64
+ {AF745B42-E296-46EB-859B-970A671DEF5E}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/third_party/crunch/crn_linux.workspace b/third_party/crunch/crn_linux.workspace
new file mode 100644
index 000000000..af9107b16
--- /dev/null
+++ b/third_party/crunch/crn_linux.workspace
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/third_party/crunch/crnlib/Makefile b/third_party/crunch/crnlib/Makefile
new file mode 100644
index 000000000..9a9de1870
--- /dev/null
+++ b/third_party/crunch/crnlib/Makefile
@@ -0,0 +1,96 @@
+COMPILE_OPTIONS = -O3 -fomit-frame-pointer -ffast-math -fno-math-errno -g -fno-strict-aliasing -Wall -Wno-unused-value -Wno-unused -march=core2
+LINKER_OPTIONS = -lpthread -g
+
+OBJECTS = \
+ crn_arealist.o \
+ crn_assert.o \
+ crn_checksum.o \
+ crn_colorized_console.o \
+ crn_command_line_params.o \
+ crn_comp.o \
+ crn_console.o \
+ crn_core.o \
+ crn_data_stream.o \
+ crn_mipmapped_texture.o \
+ crn_decomp.o \
+ crn_dxt1.o \
+ crn_dxt5a.o \
+ crn_dxt.o \
+ crn_dxt_endpoint_refiner.o \
+ crn_dxt_fast.o \
+ crn_dxt_hc_common.o \
+ crn_dxt_hc.o \
+ crn_dxt_image.o \
+ crn_dynamic_string.o \
+ crn_file_utils.o \
+ crn_find_files.o \
+ crn_hash.o \
+ crn_hash_map.o \
+ crn_huffman_codes.o \
+ crn_image_utils.o \
+ crnlib.o \
+ crn_math.o \
+ crn_mem.o \
+ crn_pixel_format.o \
+ crn_platform.o \
+ crn_prefix_coding.o \
+ crn_qdxt1.o \
+ crn_qdxt5.o \
+ crn_rand.o \
+ crn_resample_filters.o \
+ crn_resampler.o \
+ crn_ryg_dxt.o \
+ crn_sparse_bit_array.o \
+ crn_stb_image.o \
+ crn_strutils.o \
+ crn_symbol_codec.o \
+ crn_texture_file_types.o \
+ crn_threaded_resampler.o \
+ crn_threading_pthreads.o \
+ crn_timer.o \
+ crn_utils.o \
+ crn_value.o \
+ crn_vector.o \
+ crn_zeng.o \
+ crn_texture_comp.o \
+ crn_texture_conversion.o \
+ crn_dds_comp.o \
+ crn_lzma_codec.o \
+ crn_ktx_texture.o \
+ crn_etc.o \
+ crn_rg_etc1.o \
+ crn_miniz.o \
+ crn_jpge.o \
+ crn_jpgd.o \
+ lzma_7zBuf2.o \
+ lzma_7zBuf.o \
+ lzma_7zCrc.o \
+ lzma_7zFile.o \
+ lzma_7zStream.o \
+ lzma_Alloc.o \
+ lzma_Bcj2.o \
+ lzma_Bra86.o \
+ lzma_Bra.o \
+ lzma_BraIA64.o \
+ lzma_LzFind.o \
+ lzma_LzmaDec.o \
+ lzma_LzmaEnc.o \
+ lzma_LzmaLib.o
+
+all: crunch
+
+%.o: %.cpp
+ g++ $< -o $@ -c $(COMPILE_OPTIONS)
+
+crunch.o: ../crunch/crunch.cpp
+ g++ $< -o $@ -c -I../inc -I../crnlib $(COMPILE_OPTIONS)
+
+corpus_gen.o: ../crunch/corpus_gen.cpp
+ g++ $< -o $@ -c -I../inc -I../crnlib $(COMPILE_OPTIONS)
+
+corpus_test.o: ../crunch/corpus_test.cpp
+ g++ $< -o $@ -c -I../inc -I../crnlib $(COMPILE_OPTIONS)
+
+crunch: $(OBJECTS) crunch.o corpus_gen.o corpus_test.o
+ g++ $(OBJECTS) crunch.o corpus_gen.o corpus_test.o -o crunch $(LINKER_OPTIONS)
+
diff --git a/third_party/crunch/crnlib/crn_arealist.cpp b/third_party/crunch/crnlib/crn_arealist.cpp
new file mode 100644
index 000000000..8f9f937e2
--- /dev/null
+++ b/third_party/crunch/crnlib/crn_arealist.cpp
@@ -0,0 +1,698 @@
+// File: crn_arealist.cpp - 2D shape algebra (currently unused)
+// See Copyright Notice and license at the end of inc/crnlib.h
+// Ported from the PowerView DOS image viewer, a product I wrote back in 1993. Not currently used in the open source release of crnlib.
+#include "crn_core.h"
+#include "crn_arealist.h"
+
+#define RECT_DEBUG
+
+namespace crnlib
+{
+
+ static void area_fatal_error(const char* pFunc, const char* pMsg, ...)
+ {
+ pFunc;
+ va_list args;
+ va_start(args, pMsg);
+
+ char buf[512];
+#ifdef _MSC_VER
+ _vsnprintf_s(buf, sizeof(buf), pMsg, args);
+#else
+ vsnprintf(buf, sizeof(buf), pMsg, args);
+#endif
+
+ va_end(args);
+
+ CRNLIB_FAIL(buf);
+ }
+
+ static Area * delete_area(Area_List *Plist, Area *Parea)
+ {
+ Area *p, *q;
+
+ #ifdef RECT_DEBUG
+ if ((Parea == Plist->Phead) || (Parea == Plist->Ptail))
+ area_fatal_error("delete_area", "tried to remove head or tail");
+ #endif
+
+ p = Parea->Pprev;
+ q = Parea->Pnext;
+ p->Pnext = q;
+ q->Pprev = p;
+
+ Parea->Pnext = Plist->Pfree;
+ Parea->Pprev = NULL;
+ Plist->Pfree = Parea;
+
+ return (q);
+ }
+
+ static Area * alloc_area(Area_List *Plist)
+ {
+ Area *p = Plist->Pfree;
+
+ if (p == NULL)
+ {
+ if (Plist->next_free == Plist->total_areas)
+ area_fatal_error("alloc_area", "Out of areas!");
+
+ p = Plist->Phead + Plist->next_free;
+ Plist->next_free++;
+ }
+ else
+ Plist->Pfree = p->Pnext;
+
+ return (p);
+ }
+
+ static Area * insert_area_before(Area_List *Plist, Area *Parea,
+ int x1, int y1, int x2, int y2)
+ {
+ Area *p, *Pnew_area = alloc_area(Plist);
+
+ p = Parea->Pprev;
+
+ p->Pnext = Pnew_area;
+
+ Pnew_area->Pprev = p;
+ Pnew_area->Pnext = Parea;
+
+ Parea->Pprev = Pnew_area;
+
+ Pnew_area->x1 = x1;
+ Pnew_area->y1 = y1;
+ Pnew_area->x2 = x2;
+ Pnew_area->y2 = y2;
+
+ return (Pnew_area);
+ }
+
+ static Area * insert_area_after(Area_List *Plist, Area *Parea,
+ int x1, int y1, int x2, int y2)
+ {
+ Area *p, *Pnew_area = alloc_area(Plist);
+
+ p = Parea->Pnext;
+
+ p->Pprev = Pnew_area;
+
+ Pnew_area->Pnext = p;
+ Pnew_area->Pprev = Parea;
+
+ Parea->Pnext = Pnew_area;
+
+ Pnew_area->x1 = x1;
+ Pnew_area->y1 = y1;
+ Pnew_area->x2 = x2;
+ Pnew_area->y2 = y2;
+
+ return (Pnew_area);
+ }
+
+ void Area_List_deinit(Area_List* Pobj_base)
+ {
+ Area_List *Plist = (Area_List *)Pobj_base;
+
+ if (!Plist)
+ return;
+
+ if (Plist->Phead)
+ {
+ crnlib_free(Plist->Phead);
+ Plist->Phead = NULL;
+ }
+
+ crnlib_free(Plist);
+ }
+
+ Area_List * Area_List_init(int max_areas)
+ {
+ Area_List *Plist = (Area_List*)crnlib_calloc(1, sizeof(Area_List));
+
+ Plist->total_areas = max_areas + 2;
+
+ Plist->Phead = (Area *)crnlib_calloc(max_areas + 2, sizeof(Area));
+ Plist->Ptail = Plist->Phead + 1;
+
+ Plist->Phead->Pprev = NULL;
+ Plist->Phead->Pnext = Plist->Ptail;
+
+ Plist->Ptail->Pprev = Plist->Phead;
+ Plist->Ptail->Pnext = NULL;
+
+ Plist->Pfree = NULL;
+ Plist->next_free = 2;
+
+ return (Plist);
+ }
+
+ void Area_List_print(Area_List *Plist)
+ {
+ Area *Parea = Plist->Phead->Pnext;
+
+ while (Parea != Plist->Ptail)
+ {
+ printf("%04i %04i : %04i %04i\n", Parea->x1, Parea->y1, Parea->x2, Parea->y2);
+
+ Parea = Parea->Pnext;
+ }
+ }
+
+ Area_List * Area_List_dup_new(Area_List *Plist,
+ int x_ofs, int y_ofs)
+ {
+ int i;
+ Area_List *Pnew_list = (Area_List*)crnlib_calloc(1, sizeof(Area_List));
+
+ Pnew_list->total_areas = Plist->total_areas;
+
+ Pnew_list->Phead = (Area *)crnlib_malloc(sizeof(Area) * Plist->total_areas);
+ Pnew_list->Ptail = Pnew_list->Phead + 1;
+
+ Pnew_list->Pfree = (Plist->Pfree) ? ((Plist->Pfree - Plist->Phead) + Pnew_list->Phead) : NULL;
+
+ Pnew_list->next_free = Plist->next_free;
+
+ memcpy(Pnew_list->Phead, Plist->Phead, sizeof(Area) * Plist->total_areas);
+
+ for (i = 0; i < Plist->total_areas; i++)
+ {
+ Pnew_list->Phead[i].Pnext = (Plist->Phead[i].Pnext == NULL) ? NULL : (Plist->Phead[i].Pnext - Plist->Phead) + Pnew_list->Phead;
+ Pnew_list->Phead[i].Pprev = (Plist->Phead[i].Pprev == NULL) ? NULL : (Plist->Phead[i].Pprev - Plist->Phead) + Pnew_list->Phead;
+
+ Pnew_list->Phead[i].x1 += x_ofs;
+ Pnew_list->Phead[i].y1 += y_ofs;
+ Pnew_list->Phead[i].x2 += x_ofs;
+ Pnew_list->Phead[i].y2 += y_ofs;
+ }
+
+ return (Pnew_list);
+ }
+
+ uint Area_List_get_num(Area_List* Plist)
+ {
+ uint num = 0;
+
+ Area *Parea = Plist->Phead->Pnext;
+
+ while (Parea != Plist->Ptail)
+ {
+ num++;
+
+ Parea = Parea->Pnext;
+ }
+
+ return num;
+ }
+
+ void Area_List_dup(Area_List *Psrc_list, Area_List *Pdst_list,
+ int x_ofs, int y_ofs)
+ {
+ int i;
+
+ if (Psrc_list->total_areas != Pdst_list->total_areas)
+ area_fatal_error("Area_List_dup", "Src and Dst total_areas must be equal!");
+
+ Pdst_list->Pfree = (Psrc_list->Pfree) ? ((Psrc_list->Pfree - Psrc_list->Phead) + Pdst_list->Phead) : NULL;
+
+ Pdst_list->next_free = Psrc_list->next_free;
+
+ memcpy(Pdst_list->Phead, Psrc_list->Phead, sizeof(Area) * Psrc_list->total_areas);
+
+ if ((x_ofs) || (y_ofs))
+ {
+ for (i = 0; i < Psrc_list->total_areas; i++)
+ {
+ Pdst_list->Phead[i].Pnext = (Psrc_list->Phead[i].Pnext == NULL) ? NULL : (Psrc_list->Phead[i].Pnext - Psrc_list->Phead) + Pdst_list->Phead;
+ Pdst_list->Phead[i].Pprev = (Psrc_list->Phead[i].Pprev == NULL) ? NULL : (Psrc_list->Phead[i].Pprev - Psrc_list->Phead) + Pdst_list->Phead;
+
+ Pdst_list->Phead[i].x1 += x_ofs;
+ Pdst_list->Phead[i].y1 += y_ofs;
+ Pdst_list->Phead[i].x2 += x_ofs;
+ Pdst_list->Phead[i].y2 += y_ofs;
+ }
+ }
+ else
+ {
+ for (i = 0; i < Psrc_list->total_areas; i++)
+ {
+ Pdst_list->Phead[i].Pnext = (Psrc_list->Phead[i].Pnext == NULL) ? NULL : (Psrc_list->Phead[i].Pnext - Psrc_list->Phead) + Pdst_list->Phead;
+ Pdst_list->Phead[i].Pprev = (Psrc_list->Phead[i].Pprev == NULL) ? NULL : (Psrc_list->Phead[i].Pprev - Psrc_list->Phead) + Pdst_list->Phead;
+ }
+ }
+ }
+
+ void Area_List_copy(
+ Area_List *Psrc_list, Area_List *Pdst_list,
+ int x_ofs, int y_ofs)
+ {
+ Area *Parea = Psrc_list->Phead->Pnext;
+
+ Area_List_clear(Pdst_list);
+
+ if ((x_ofs) || (y_ofs))
+ {
+ Area *Pprev_area = Pdst_list->Phead;
+
+ while (Parea != Psrc_list->Ptail)
+ {
+ // Area *p, *Pnew_area;
+ Area *Pnew_area;
+
+ if (Pdst_list->next_free == Pdst_list->total_areas)
+ area_fatal_error("Area_List_copy", "Out of areas!");
+
+ Pnew_area = Pdst_list->Phead + Pdst_list->next_free;
+ Pdst_list->next_free++;
+
+ Pnew_area->Pprev = Pprev_area;
+ Pprev_area->Pnext = Pnew_area;
+
+ Pnew_area->x1 = Parea->x1 + x_ofs;
+ Pnew_area->y1 = Parea->y1 + y_ofs;
+ Pnew_area->x2 = Parea->x2 + x_ofs;
+ Pnew_area->y2 = Parea->y2 + y_ofs;
+
+ Pprev_area = Pnew_area;
+
+ Parea = Parea->Pnext;
+ }
+
+ Pprev_area->Pnext = Pdst_list->Ptail;
+ }
+ else
+ {
+ #if 0
+ while (Parea != Psrc_list->Ptail)
+ {
+ insert_area_after(Pdst_list, Pdst_list->Phead,
+ Parea->x1,
+ Parea->y1,
+ Parea->x2,
+ Parea->y2);
+
+ Parea = Parea->Pnext;
+ }
+ #endif
+
+ Area *Pprev_area = Pdst_list->Phead;
+
+ while (Parea != Psrc_list->Ptail)
+ {
+ // Area *p, *Pnew_area;
+ Area *Pnew_area;
+
+ if (Pdst_list->next_free == Pdst_list->total_areas)
+ area_fatal_error("Area_List_copy", "Out of areas!");
+
+ Pnew_area = Pdst_list->Phead + Pdst_list->next_free;
+ Pdst_list->next_free++;
+
+ Pnew_area->Pprev = Pprev_area;
+ Pprev_area->Pnext = Pnew_area;
+
+ Pnew_area->x1 = Parea->x1;
+ Pnew_area->y1 = Parea->y1;
+ Pnew_area->x2 = Parea->x2;
+ Pnew_area->y2 = Parea->y2;
+
+ Pprev_area = Pnew_area;
+
+ Parea = Parea->Pnext;
+ }
+
+ Pprev_area->Pnext = Pdst_list->Ptail;
+ }
+ }
+
+ void Area_List_clear(Area_List *Plist)
+ {
+ Plist->Phead->Pnext = Plist->Ptail;
+ Plist->Ptail->Pprev = Plist->Phead;
+ Plist->Pfree = NULL;
+ Plist->next_free = 2;
+ }
+
+ void Area_List_set(Area_List *Plist, int x1, int y1, int x2, int y2)
+ {
+ Plist->Pfree = NULL;
+
+ Plist->Phead[2].x1 = x1;
+ Plist->Phead[2].y1 = y1;
+ Plist->Phead[2].x2 = x2;
+ Plist->Phead[2].y2 = y2;
+
+ Plist->Phead[2].Pprev = Plist->Phead;
+ Plist->Phead->Pnext = Plist->Phead + 2;
+
+ Plist->Phead[2].Pnext = Plist->Ptail;
+ Plist->Ptail->Pprev = Plist->Phead + 2;
+
+ Plist->next_free = 3;
+ }
+
+ void Area_List_remove(Area_List *Plist,
+ int x1, int y1, int x2, int y2)
+ {
+ int l, h;
+ Area *Parea = Plist->Phead->Pnext;
+
+ #ifdef RECT_DEBUG
+ if ((x1 > x2) || (y1 > y2))
+ area_fatal_error("area_list_remove", "invalid coords: %i %i %i %i", x1, y1, x2, y2);
+ #endif
+
+ while (Parea != Plist->Ptail)
+ {
+ // Not touching
+ if ((x2 < Parea->x1) || (x1 > Parea->x2) ||
+ (y2 < Parea->y1) || (y1 > Parea->y2))
+ {
+ Parea = Parea->Pnext;
+ continue;
+ }
+
+ // Completely covers
+ if ((x1 <= Parea->x1) && (x2 >= Parea->x2) &&
+ (y1 <= Parea->y1) && (y2 >= Parea->y2))
+ {
+ if ((x1 == Parea->x1) && (x2 == Parea->x2) &&
+ (y1 == Parea->y1) && (y2 == Parea->y2))
+ {
+ delete_area(Plist, Parea);
+ return;
+ }
+
+ Parea = delete_area(Plist, Parea);
+
+ continue;
+ }
+
+ // top
+ if (y1 > Parea->y1)
+ {
+ insert_area_before(Plist, Parea,
+ Parea->x1, Parea->y1,
+ Parea->x2, y1 - 1);
+ }
+
+ // bottom
+ if (y2 < Parea->y2)
+ {
+ insert_area_before(Plist, Parea,
+ Parea->x1, y2 + 1,
+ Parea->x2, Parea->y2);
+ }
+
+ l = math::maximum(y1, Parea->y1);
+ h = math::minimum(y2, Parea->y2);
+
+ // left middle
+ if (x1 > Parea->x1)
+ {
+ insert_area_before(Plist, Parea,
+ Parea->x1, l,
+ x1 - 1, h);
+ }
+
+ // right middle
+ if (x2 < Parea->x2)
+ {
+ insert_area_before(Plist, Parea,
+ x2 + 1, l,
+ Parea->x2, h);
+ }
+
+ // early out - we know there's nothing else to remove, as areas can
+ // never overlap
+ if ((x1 >= Parea->x1) && (x2 <= Parea->x2) &&
+ (y1 >= Parea->y1) && (y2 <= Parea->y2))
+ {
+ delete_area(Plist, Parea);
+ return;
+ }
+
+ Parea = delete_area(Plist, Parea);
+ }
+ }
+
+ void Area_List_insert(Area_List *Plist,
+ int x1, int y1, int x2, int y2,
+ bool combine)
+ {
+ Area *Parea = Plist->Phead->Pnext;
+
+ #ifdef RECT_DEBUG
+ if ((x1 > x2) || (y1 > y2))
+ area_fatal_error("Area_List_insert", "invalid coords: %i %i %i %i", x1, y1, x2, y2);
+ #endif
+
+ while (Parea != Plist->Ptail)
+ {
+ // totally covers
+ if ((x1 <= Parea->x1) && (x2 >= Parea->x2) &&
+ (y1 <= Parea->y1) && (y2 >= Parea->y2))
+ {
+ Parea = delete_area(Plist, Parea);
+ continue;
+ }
+
+ // intersects
+ if ((x2 >= Parea->x1) && (x1 <= Parea->x2) &&
+ (y2 >= Parea->y1) && (y1 <= Parea->y2))
+ {
+ int ax1, ay1, ax2, ay2;
+
+ ax1 = Parea->x1;
+ ay1 = Parea->y1;
+ ax2 = Parea->x2;
+ ay2 = Parea->y2;
+
+ if (x1 < ax1)
+ Area_List_insert(Plist, x1, math::maximum(y1, ay1), ax1 - 1, math::minimum(y2, ay2), combine);
+
+ if (x2 > ax2)
+ Area_List_insert(Plist, ax2 + 1, math::maximum(y1, ay1), x2, math::minimum(y2, ay2), combine);
+
+ if (y1 < ay1)
+ Area_List_insert(Plist, x1, y1, x2, ay1 - 1, combine);
+
+ if (y2 > ay2)
+ Area_List_insert(Plist, x1, ay2 + 1, x2, y2, combine);
+
+ return;
+ }
+
+ if (combine)
+ {
+ if ((x1 == Parea->x1) && (x2 == Parea->x2))
+ {
+ if ((y2 == Parea->y1 - 1) || (y1 == Parea->y2 + 1))
+ {
+ delete_area(Plist, Parea);
+ Area_List_insert(Plist, x1, math::minimum(y1, Parea->y1), x2, math::maximum(y2, Parea->y2), CRNLIB_TRUE);
+ return;
+ }
+ }
+ else if ((y1 == Parea->y1) && (y2 == Parea->y2))
+ {
+ if ((x2 == Parea->x1 - 1) || (x1 == Parea->x2 + 1))
+ {
+ delete_area(Plist, Parea);
+ Area_List_insert(Plist, math::minimum(x1, Parea->x1), y1, math::maximum(x2, Parea->x2), y2, CRNLIB_TRUE);
+ return;
+ }
+ }
+ }
+
+ Parea = Parea->Pnext;
+ }
+
+ insert_area_before(Plist, Parea, x1, y1, x2, y2);
+ }
+
+ void Area_List_intersect_area(Area_List *Plist,
+ int x1, int y1, int x2, int y2)
+ {
+ Area *Parea = Plist->Phead->Pnext;
+
+ while (Parea != Plist->Ptail)
+ {
+ // doesn't cover
+ if ((x2 < Parea->x1) || (x1 > Parea->x2) ||
+ (y2 < Parea->y1) || (y1 > Parea->y2))
+ {
+ Parea = delete_area(Plist, Parea);
+ continue;
+ }
+
+ // totally covers
+ if ((x1 <= Parea->x1) && (x2 >= Parea->x2) &&
+ (y1 <= Parea->y1) && (y2 >= Parea->y2))
+ {
+ Parea = Parea->Pnext;
+ continue;
+ }
+
+ // Oct 21- should insert after, because deleted area will access the NEXT area!
+ // insert_area_after(Plist, Parea,
+ // math::maximum(x1, Parea->x1),
+ // math::maximum(y1, Parea->y1),
+ // math::minimum(x2, Parea->x2),
+ // math::minimum(y2, Parea->y2));
+
+ insert_area_before(Plist, Parea,
+ math::maximum(x1, Parea->x1),
+ math::maximum(y1, Parea->y1),
+ math::minimum(x2, Parea->x2),
+ math::minimum(y2, Parea->y2));
+
+ Parea = delete_area(Plist, Parea);
+ }
+ }
+
+ #if 0
+ void Area_List_intersect_Area_List(
+ Area_List *Pouter_list,
+ Area_List *Pinner_list,
+ Area_List *Pdst_list)
+ {
+ Area *Parea1 = Pouter_list->Phead->Pnext;
+
+ while (Parea1 != Pouter_list->Ptail)
+ {
+ Area *Parea2 = Pinner_list->Phead->Pnext;
+ int x1, y1, x2, y2;
+
+ x1 = Parea1->x1; x2 = Parea1->x2;
+ y1 = Parea1->y1; y2 = Parea1->y2;
+
+ while (Parea2 != Pinner_list->Ptail)
+ {
+ if ((x1 <= Parea2->x2) && (x2 >= Parea2->x1) &&
+ (y1 <= Parea2->y2) && (y2 >= Parea2->y1))
+ {
+ insert_area_after(Pdst_list, Pdst_list->Phead,
+ math::maximum(x1, Parea2->x1),
+ math::maximum(y1, Parea2->y1),
+ math::minimum(x2, Parea2->x2),
+ math::minimum(y2, Parea2->y2));
+ }
+
+ Parea2 = Parea2->Pnext;
+ }
+
+ Parea1 = Parea1->Pnext;
+ }
+ }
+ #endif
+
+ #if 1
+ void Area_List_intersect_Area_List(Area_List *Pouter_list,
+ Area_List *Pinner_list,
+ Area_List *Pdst_list)
+ {
+ Area *Parea1 = Pouter_list->Phead->Pnext;
+
+ while (Parea1 != Pouter_list->Ptail)
+ {
+ Area *Parea2 = Pinner_list->Phead->Pnext;
+ int x1, y1, x2, y2;
+
+ x1 = Parea1->x1; x2 = Parea1->x2;
+ y1 = Parea1->y1; y2 = Parea1->y2;
+
+ while (Parea2 != Pinner_list->Ptail)
+ {
+ if ((x1 <= Parea2->x2) && (x2 >= Parea2->x1) &&
+ (y1 <= Parea2->y2) && (y2 >= Parea2->y1))
+ {
+ int nx1, ny1, nx2, ny2;
+
+ nx1 = math::maximum(x1, Parea2->x1);
+ ny1 = math::maximum(y1, Parea2->y1);
+ nx2 = math::minimum(x2, Parea2->x2);
+ ny2 = math::minimum(y2, Parea2->y2);
+
+ if (Pdst_list->Phead->Pnext == Pdst_list->Ptail)
+ {
+ insert_area_after(Pdst_list, Pdst_list->Phead,
+ nx1, ny1, nx2, ny2);
+ }
+ else
+ {
+ Area_Ptr Ptemp = Pdst_list->Phead->Pnext;
+ if ((Ptemp->x1 == nx1) && (Ptemp->x2 == nx2))
+ {
+ if (Ptemp->y1 == (ny2+1))
+ {
+ Ptemp->y1 = ny1;
+ goto next;
+ }
+ else if (Ptemp->y2 == (ny1-1))
+ {
+ Ptemp->y2 = ny2;
+ goto next;
+ }
+ }
+ else if ((Ptemp->y1 == ny1) && (Ptemp->y2 == ny2))
+ {
+ if (Ptemp->x1 == (nx2+1))
+ {
+ Ptemp->x1 = nx1;
+ goto next;
+ }
+ else if (Ptemp->x2 == (nx1-1))
+ {
+ Ptemp->x2 = nx2;
+ goto next;
+ }
+ }
+
+ insert_area_after(Pdst_list, Pdst_list->Phead,
+ nx1, ny1, nx2, ny2);
+ }
+ }
+
+ next:
+
+ Parea2 = Parea2->Pnext;
+ }
+
+ Parea1 = Parea1->Pnext;
+ }
+ }
+ #endif
+
+ Area_List_Ptr Area_List_create_optimal(Area_List_Ptr Plist)
+ {
+ Area_Ptr Parea = Plist->Phead->Pnext, Parea_after;
+ int num = 2;
+ Area_List_Ptr Pnew_list;
+
+ while (Parea != Plist->Ptail)
+ {
+ num++;
+ Parea = Parea->Pnext;
+ }
+
+ Pnew_list = Area_List_init(num);
+
+ Parea = Plist->Phead->Pnext;
+
+ Parea_after = Pnew_list->Phead;
+
+ while (Parea != Plist->Ptail)
+ {
+ Parea_after = insert_area_after(Pnew_list, Parea_after,
+ Parea->x1, Parea->y1,
+ Parea->x2, Parea->y2);
+
+ Parea = Parea->Pnext;
+ }
+
+ return (Pnew_list);
+ }
+
+} // namespace crnlib
diff --git a/third_party/crunch/crnlib/crn_arealist.h b/third_party/crunch/crnlib/crn_arealist.h
new file mode 100644
index 000000000..a7e00f906
--- /dev/null
+++ b/third_party/crunch/crnlib/crn_arealist.h
@@ -0,0 +1,74 @@
+// File: crn_arealist.h - 2D shape algebra
+// See Copyright Notice and license at the end of inc/crnlib.h
+#pragma once
+
+namespace crnlib
+{
+ struct Area
+ {
+ struct Area *Pprev, *Pnext;
+
+ int x1, y1, x2, y2;
+
+ uint get_width() const { return x2 - x1 + 1; }
+ uint get_height() const { return y2 - y1 + 1; }
+ uint get_area() const { return get_width() * get_height(); }
+ };
+
+ typedef Area * Area_Ptr;
+
+ struct Area_List
+ {
+ int total_areas;
+ int next_free;
+
+ Area *Phead, *Ptail, *Pfree;
+ };
+
+ typedef Area_List * Area_List_Ptr;
+
+ Area_List * Area_List_init(int max_areas);
+ void Area_List_deinit(Area_List* Pobj_base);
+
+ void Area_List_print(Area_List *Plist);
+
+ Area_List * Area_List_dup_new(Area_List *Plist,
+ int x_ofs, int y_ofs);
+
+ uint Area_List_get_num(Area_List* Plist);
+
+ // src and dst area lists must have the same number of total areas.
+ void Area_List_dup(Area_List *Psrc_list,
+ Area_List *Pdst_list,
+ int x_ofs, int y_ofs);
+
+ void Area_List_copy(Area_List *Psrc_list,
+ Area_List *Pdst_list,
+ int x_ofs, int y_ofs);
+
+ void Area_List_clear(Area_List *Plist);
+
+ void Area_List_set(Area_List *Plist,
+ int x1, int y1, int x2, int y2);
+
+ // logical: x and (not y)
+ void Area_List_remove(Area_List *Plist,
+ int x1, int y1, int x2, int y2);
+
+ // logical: x or y
+ void Area_List_insert(Area_List *Plist,
+ int x1, int y1, int x2, int y2,
+ bool combine);
+
+ // logical: x and y
+ void Area_List_intersect_area(Area_List *Plist,
+ int x1, int y1, int x2, int y2);
+
+ // logical: x and y
+ void Area_List_intersect_Area_List(Area_List *Pouter_list,
+ Area_List *Pinner_list,
+ Area_List *Pdst_list);
+
+ Area_List_Ptr Area_List_create_optimal(Area_List_Ptr Plist);
+
+} // namespace crnlib
diff --git a/third_party/crunch/crnlib/crn_assert.cpp b/third_party/crunch/crnlib/crn_assert.cpp
new file mode 100644
index 000000000..aead7e98f
--- /dev/null
+++ b/third_party/crunch/crnlib/crn_assert.cpp
@@ -0,0 +1,69 @@
+// File: crn_assert.cpp
+// See Copyright Notice and license at the end of inc/crnlib.h
+#include "crn_core.h"
+#if CRNLIB_USE_WIN32_API
+#include "crn_winhdr.h"
+#endif
+
+static bool g_fail_exceptions;
+static bool g_exit_on_failure = true;
+
+void crnlib_enable_fail_exceptions(bool enabled)
+{
+ g_fail_exceptions = enabled;
+}
+
+void crnlib_assert(const char* pExp, const char* pFile, unsigned line)
+{
+ char buf[512];
+
+ sprintf_s(buf, sizeof(buf), "%s(%u): Assertion failed: \"%s\"\n", pFile, line, pExp);
+
+ crnlib_output_debug_string(buf);
+
+ fputs(buf, stderr);
+
+ if (crnlib_is_debugger_present())
+ crnlib_debug_break();
+}
+
+void crnlib_fail(const char* pExp, const char* pFile, unsigned line)
+{
+ char buf[512];
+
+ sprintf_s(buf, sizeof(buf), "%s(%u): Failure: \"%s\"\n", pFile, line, pExp);
+
+ crnlib_output_debug_string(buf);
+
+ fputs(buf, stderr);
+
+ if (crnlib_is_debugger_present())
+ crnlib_debug_break();
+
+#if CRNLIB_USE_WIN32_API
+ if (g_fail_exceptions)
+ RaiseException(CRNLIB_FAIL_EXCEPTION_CODE, 0, 0, NULL);
+ else
+#endif
+ if (g_exit_on_failure)
+ exit(EXIT_FAILURE);
+}
+
+void trace(const char* pFmt, va_list args)
+{
+ if (crnlib_is_debugger_present())
+ {
+ char buf[512];
+ vsprintf_s(buf, sizeof(buf), pFmt, args);
+
+ crnlib_output_debug_string(buf);
+ }
+};
+
+void trace(const char* pFmt, ...)
+{
+ va_list args;
+ va_start(args, pFmt);
+ trace(pFmt, args);
+ va_end(args);
+};
diff --git a/third_party/crunch/crnlib/crn_assert.h b/third_party/crunch/crnlib/crn_assert.h
new file mode 100644
index 000000000..a48dfa4a0
--- /dev/null
+++ b/third_party/crunch/crnlib/crn_assert.h
@@ -0,0 +1,61 @@
+// File: crn_assert.h
+// See Copyright Notice and license at the end of inc/crnlib.h
+#pragma once
+
+const unsigned int CRNLIB_FAIL_EXCEPTION_CODE = 256U;
+void crnlib_enable_fail_exceptions(bool enabled);
+
+void crnlib_assert(const char* pExp, const char* pFile, unsigned line);
+void crnlib_fail(const char* pExp, const char* pFile, unsigned line);
+
+#ifdef NDEBUG
+ #define CRNLIB_ASSERT(x) ((void)0)
+ #undef CRNLIB_ASSERTS_ENABLED
+#else
+ #define CRNLIB_ASSERT(_exp) (void)( (!!(_exp)) || (crnlib_assert(#_exp, __FILE__, __LINE__), 0) )
+ #define CRNLIB_ASSERTS_ENABLED
+#endif
+
+#define CRNLIB_VERIFY(_exp) (void)( (!!(_exp)) || (crnlib_assert(#_exp, __FILE__, __LINE__), 0) )
+
+#define CRNLIB_FAIL(msg) do { crnlib_fail(#msg, __FILE__, __LINE__); } while(0)
+
+#define CRNLIB_ASSERT_OPEN_RANGE(x, l, h) CRNLIB_ASSERT((x >= l) && (x < h))
+#define CRNLIB_ASSERT_CLOSED_RANGE(x, l, h) CRNLIB_ASSERT((x >= l) && (x <= h))
+
+void trace(const char* pFmt, va_list args);
+void trace(const char* pFmt, ...);
+
+// Borrowed from boost libraries.
+template struct crnlib_assume_failure;
+template <> struct crnlib_assume_failure { enum { blah = 1 }; };
+template struct crnlib_assume_try { };
+
+#define CRNLIB_JOINER_FINAL(a, b) a##b
+#define CRNLIB_JOINER(a, b) CRNLIB_JOINER_FINAL(a, b)
+#define CRNLIB_JOIN(a, b) CRNLIB_JOINER(a, b)
+#define CRNLIB_ASSUME(p) typedef crnlib_assume_try < sizeof(crnlib_assume_failure< (bool)(p) > ) > CRNLIB_JOIN(crnlib_assume_typedef, __COUNTER__)
+
+#ifdef NDEBUG
+template inline T crnlib_assert_range(T i, T m)
+{
+ m;
+ return i;
+}
+template inline T crnlib_assert_range_incl(T i, T m)
+{
+ m;
+ return i;
+}
+#else
+template inline T crnlib_assert_range(T i, T m)
+{
+ CRNLIB_ASSERT((i >= 0) && (i < m));
+ return i;
+}
+template inline T crnlib_assert_range_incl(T i, T m)
+{
+ CRNLIB_ASSERT((i >= 0) && (i <= m));
+ return i;
+}
+#endif
diff --git a/third_party/crunch/crnlib/crn_atomics.h b/third_party/crunch/crnlib/crn_atomics.h
new file mode 100644
index 000000000..b9e38dbc1
--- /dev/null
+++ b/third_party/crunch/crnlib/crn_atomics.h
@@ -0,0 +1,208 @@
+// File: crn_atomics.h
+#ifndef CRN_ATOMICS_H
+#define CRN_ATOMICS_H
+
+#ifdef WIN32
+#pragma once
+#endif
+
+#ifdef WIN32
+#include "crn_winhdr.h"
+#endif
+
+#if defined(__GNUC__) && CRNLIB_PLATFORM_PC
+extern __inline__ __attribute__((__always_inline__,__gnu_inline__)) void crnlib_yield_processor()
+{
+ __asm__ __volatile__("pause");
+}
+#else
+CRNLIB_FORCE_INLINE void crnlib_yield_processor()
+{
+#if CRNLIB_USE_MSVC_INTRINSICS
+ #if CRNLIB_PLATFORM_PC_X64
+ _mm_pause();
+ #else
+ YieldProcessor();
+ #endif
+#else
+ // No implementation
+#endif
+}
+#endif
+
+#if CRNLIB_USE_WIN32_ATOMIC_FUNCTIONS
+ extern "C" __int64 _InterlockedCompareExchange64(__int64 volatile * Destination, __int64 Exchange, __int64 Comperand);
+ #if defined(_MSC_VER)
+ #pragma intrinsic(_InterlockedCompareExchange64)
+ #endif
+#endif // CRNLIB_USE_WIN32_ATOMIC_FUNCTIONS
+
+namespace crnlib
+{
+#if CRNLIB_USE_WIN32_ATOMIC_FUNCTIONS
+ typedef LONG atomic32_t;
+ typedef LONGLONG atomic64_t;
+
+ // Returns the original value.
+ inline atomic32_t atomic_compare_exchange32(atomic32_t volatile *pDest, atomic32_t exchange, atomic32_t comparand)
+ {
+ CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ return InterlockedCompareExchange(pDest, exchange, comparand);
+ }
+
+ // Returns the original value.
+ inline atomic64_t atomic_compare_exchange64(atomic64_t volatile *pDest, atomic64_t exchange, atomic64_t comparand)
+ {
+ CRNLIB_ASSERT((reinterpret_cast(pDest) & 7) == 0);
+ return _InterlockedCompareExchange64(pDest, exchange, comparand);
+ }
+
+ // Returns the resulting incremented value.
+ inline atomic32_t atomic_increment32(atomic32_t volatile *pDest)
+ {
+ CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ return InterlockedIncrement(pDest);
+ }
+
+ // Returns the resulting decremented value.
+ inline atomic32_t atomic_decrement32(atomic32_t volatile *pDest)
+ {
+ CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ return InterlockedDecrement(pDest);
+ }
+
+ // Returns the original value.
+ inline atomic32_t atomic_exchange32(atomic32_t volatile *pDest, atomic32_t val)
+ {
+ CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ return InterlockedExchange(pDest, val);
+ }
+
+ // Returns the resulting value.
+ inline atomic32_t atomic_add32(atomic32_t volatile *pDest, atomic32_t val)
+ {
+ CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ return InterlockedExchangeAdd(pDest, val) + val;
+ }
+
+ // Returns the original value.
+ inline atomic32_t atomic_exchange_add32(atomic32_t volatile *pDest, atomic32_t val)
+ {
+ CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ return InterlockedExchangeAdd(pDest, val);
+ }
+#elif CRNLIB_USE_GCC_ATOMIC_BUILTINS
+ typedef long atomic32_t;
+ typedef long long atomic64_t;
+
+ // Returns the original value.
+ inline atomic32_t atomic_compare_exchange32(atomic32_t volatile *pDest, atomic32_t exchange, atomic32_t comparand)
+ {
+ CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ return __sync_val_compare_and_swap(pDest, comparand, exchange);
+ }
+
+ // Returns the original value.
+ inline atomic64_t atomic_compare_exchange64(atomic64_t volatile *pDest, atomic64_t exchange, atomic64_t comparand)
+ {
+ CRNLIB_ASSERT((reinterpret_cast(pDest) & 7) == 0);
+ return __sync_val_compare_and_swap(pDest, comparand, exchange);
+ }
+
+ // Returns the resulting incremented value.
+ inline atomic32_t atomic_increment32(atomic32_t volatile *pDest)
+ {
+ CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ return __sync_add_and_fetch(pDest, 1);
+ }
+
+ // Returns the resulting decremented value.
+ inline atomic32_t atomic_decrement32(atomic32_t volatile *pDest)
+ {
+ CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ return __sync_sub_and_fetch(pDest, 1);
+ }
+
+ // Returns the original value.
+ inline atomic32_t atomic_exchange32(atomic32_t volatile *pDest, atomic32_t val)
+ {
+ CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ return __sync_lock_test_and_set(pDest, val);
+ }
+
+ // Returns the resulting value.
+ inline atomic32_t atomic_add32(atomic32_t volatile *pDest, atomic32_t val)
+ {
+ CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ return __sync_add_and_fetch(pDest, val);
+ }
+
+ // Returns the original value.
+ inline atomic32_t atomic_exchange_add32(atomic32_t volatile *pDest, atomic32_t val)
+ {
+ CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ return __sync_fetch_and_add(pDest, val);
+ }
+#else
+ #define CRNLIB_NO_ATOMICS 1
+
+ // Atomic ops not supported - but try to do something reasonable. Assumes no threading at all.
+ typedef long atomic32_t;
+ typedef long long atomic64_t;
+
+ inline atomic32_t atomic_compare_exchange32(atomic32_t volatile *pDest, atomic32_t exchange, atomic32_t comparand)
+ {
+ CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ atomic32_t cur = *pDest;
+ if (cur == comparand)
+ *pDest = exchange;
+ return cur;
+ }
+
+ inline atomic64_t atomic_compare_exchange64(atomic64_t volatile *pDest, atomic64_t exchange, atomic64_t comparand)
+ {
+ CRNLIB_ASSERT((reinterpret_cast(pDest) & 7) == 0);
+ atomic64_t cur = *pDest;
+ if (cur == comparand)
+ *pDest = exchange;
+ return cur;
+ }
+
+ inline atomic32_t atomic_increment32(atomic32_t volatile *pDest)
+ {
+ CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ return (*pDest += 1);
+ }
+
+ inline atomic32_t atomic_decrement32(atomic32_t volatile *pDest)
+ {
+ CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ return (*pDest -= 1);
+ }
+
+ inline atomic32_t atomic_exchange32(atomic32_t volatile *pDest, atomic32_t val)
+ {
+ CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ atomic32_t cur = *pDest;
+ *pDest = val;
+ return cur;
+ }
+
+ inline atomic32_t atomic_add32(atomic32_t volatile *pDest, atomic32_t val)
+ {
+ CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ return (*pDest += val);
+ }
+
+ inline atomic32_t atomic_exchange_add32(atomic32_t volatile *pDest, atomic32_t val)
+ {
+ CRNLIB_ASSERT((reinterpret_cast(pDest) & 3) == 0);
+ atomic32_t cur = *pDest;
+ *pDest += val;
+ return cur;
+ }
+#endif
+
+} // namespace crnlib
+
+#endif // CRN_ATOMICS_H
diff --git a/third_party/crunch/crnlib/crn_buffer_stream.h b/third_party/crunch/crnlib/crn_buffer_stream.h
new file mode 100644
index 000000000..32662d916
--- /dev/null
+++ b/third_party/crunch/crnlib/crn_buffer_stream.h
@@ -0,0 +1,196 @@
+// File: crn_buffer_stream.h
+// See Copyright Notice and license at the end of inc/crnlib.h
+#pragma once
+#include "crn_data_stream.h"
+
+namespace crnlib
+{
+ class buffer_stream : public data_stream
+ {
+ public:
+ buffer_stream() :
+ data_stream(),
+ m_pBuf(NULL),
+ m_size(0),
+ m_ofs(0)
+ {
+ }
+
+ buffer_stream(void* p, uint size) :
+ data_stream(),
+ m_pBuf(NULL),
+ m_size(0),
+ m_ofs(0)
+ {
+ open(p, size);
+ }
+
+ buffer_stream(const void* p, uint size) :
+ data_stream(),
+ m_pBuf(NULL),
+ m_size(0),
+ m_ofs(0)
+ {
+ open(p, size);
+ }
+
+ virtual ~buffer_stream()
+ {
+ }
+
+ bool open(const void* p, uint size)
+ {
+ CRNLIB_ASSERT(p);
+
+ close();
+
+ if ((!p) || (!size))
+ return false;
+
+ m_opened = true;
+ m_pBuf = (uint8*)(p);
+ m_size = size;
+ m_ofs = 0;
+ m_attribs = cDataStreamSeekable | cDataStreamReadable;
+ return true;
+ }
+
+ bool open(void* p, uint size)
+ {
+ CRNLIB_ASSERT(p);
+
+ close();
+
+ if ((!p) || (!size))
+ return false;
+
+ m_opened = true;
+ m_pBuf = static_cast(p);
+ m_size = size;
+ m_ofs = 0;
+ m_attribs = cDataStreamSeekable | cDataStreamWritable | cDataStreamReadable;
+ return true;
+ }
+
+ virtual bool close()
+ {
+ if (m_opened)
+ {
+ m_opened = false;
+ m_pBuf = NULL;
+ m_size = 0;
+ m_ofs = 0;
+ return true;
+ }
+
+ return false;
+ }
+
+ const void* get_buf() const { return m_pBuf; }
+ void* get_buf() { return m_pBuf; }
+
+ virtual const void* get_ptr() const { return m_pBuf; }
+
+ virtual uint read(void* pBuf, uint len)
+ {
+ CRNLIB_ASSERT(pBuf && (len <= 0x7FFFFFFF));
+
+ if ((!m_opened) || (!is_readable()) || (!len))
+ return 0;
+
+ CRNLIB_ASSERT(m_ofs <= m_size);
+
+ uint bytes_left = m_size - m_ofs;
+
+ len = math::minimum(len, bytes_left);
+
+ if (len)
+ memcpy(pBuf, &m_pBuf[m_ofs], len);
+
+ m_ofs += len;
+
+ return len;
+ }
+
+ virtual uint write(const void* pBuf, uint len)
+ {
+ CRNLIB_ASSERT(pBuf && (len <= 0x7FFFFFFF));
+
+ if ((!m_opened) || (!is_writable()) || (!len))
+ return 0;
+
+ CRNLIB_ASSERT(m_ofs <= m_size);
+
+ uint bytes_left = m_size - m_ofs;
+
+ len = math::minimum(len, bytes_left);
+
+ if (len)
+ memcpy(&m_pBuf[m_ofs], pBuf, len);
+
+ m_ofs += len;
+
+ return len;
+ }
+
+ virtual bool flush()
+ {
+ if (!m_opened)
+ return false;
+
+ return true;
+ }
+
+ virtual uint64 get_size()
+ {
+ if (!m_opened)
+ return 0;
+
+ return m_size;
+ }
+
+ virtual uint64 get_remaining()
+ {
+ if (!m_opened)
+ return 0;
+
+ CRNLIB_ASSERT(m_ofs <= m_size);
+
+ return m_size - m_ofs;
+ }
+
+ virtual uint64 get_ofs()
+ {
+ if (!m_opened)
+ return 0;
+
+ return m_ofs;
+ }
+
+ virtual bool seek(int64 ofs, bool relative)
+ {
+ if ((!m_opened) || (!is_seekable()))
+ return false;
+
+ int64 new_ofs = relative ? (m_ofs + ofs) : ofs;
+
+ if (new_ofs < 0)
+ return false;
+ else if (new_ofs > m_size)
+ return false;
+
+ m_ofs = static_cast(new_ofs);
+
+ post_seek();
+
+ return true;
+ }
+
+ private:
+ uint8* m_pBuf;
+ uint m_size;
+ uint m_ofs;
+ };
+
+} // namespace crnlib
+
diff --git a/third_party/crunch/crnlib/crn_cfile_stream.h b/third_party/crunch/crnlib/crn_cfile_stream.h
new file mode 100644
index 000000000..12160dd43
--- /dev/null
+++ b/third_party/crunch/crnlib/crn_cfile_stream.h
@@ -0,0 +1,241 @@
+// File: crn_cfile_stream.h
+// See Copyright Notice and license at the end of inc/crnlib.h
+#pragma once
+#include "crn_data_stream.h"
+
+namespace crnlib
+{
+ class cfile_stream : public data_stream
+ {
+ public:
+ cfile_stream() : data_stream(), m_pFile(NULL), m_size(0), m_ofs(0), m_has_ownership(false)
+ {
+ }
+
+ cfile_stream(FILE* pFile, const char* pFilename, uint attribs, bool has_ownership) :
+ data_stream(), m_pFile(NULL), m_size(0), m_ofs(0), m_has_ownership(false)
+ {
+ open(pFile, pFilename, attribs, has_ownership);
+ }
+
+ cfile_stream(const char* pFilename, uint attribs = cDataStreamReadable | cDataStreamSeekable, bool open_existing = false) :
+ data_stream(), m_pFile(NULL), m_size(0), m_ofs(0), m_has_ownership(false)
+ {
+ open(pFilename, attribs, open_existing);
+ }
+
+ virtual ~cfile_stream()
+ {
+ close();
+ }
+
+ virtual bool close()
+ {
+ clear_error();
+
+ if (m_opened)
+ {
+ bool status = true;
+ if (m_has_ownership)
+ {
+ if (EOF == fclose(m_pFile))
+ status = false;
+ }
+
+ m_pFile = NULL;
+ m_opened = false;
+ m_size = 0;
+ m_ofs = 0;
+ m_has_ownership = false;
+
+ return status;
+ }
+
+ return false;
+ }
+
+ bool open(FILE* pFile, const char* pFilename, uint attribs, bool has_ownership)
+ {
+ CRNLIB_ASSERT(pFile);
+ CRNLIB_ASSERT(pFilename);
+
+ close();
+
+ set_name(pFilename);
+ m_pFile = pFile;
+ m_has_ownership = has_ownership;
+ m_attribs = static_cast(attribs);
+
+ m_ofs = crn_ftell(m_pFile);
+ crn_fseek(m_pFile, 0, SEEK_END);
+ m_size = crn_ftell(m_pFile);
+ crn_fseek(m_pFile, m_ofs, SEEK_SET);
+
+ m_opened = true;
+
+ return true;
+ }
+
+ bool open(const char* pFilename, uint attribs = cDataStreamReadable | cDataStreamSeekable, bool open_existing = false)
+ {
+ CRNLIB_ASSERT(pFilename);
+
+ close();
+
+ m_attribs = static_cast(attribs);
+
+ const char* pMode;
+ if ((is_readable()) && (is_writable()))
+ pMode = open_existing ? "r+b" : "w+b";
+ else if (is_writable())
+ pMode = open_existing ? "ab" : "wb";
+ else if (is_readable())
+ pMode = "rb";
+ else
+ {
+ set_error();
+ return false;
+ }
+
+ FILE* pFile = NULL;
+ crn_fopen(&pFile, pFilename, pMode);
+ m_has_ownership = true;
+
+ if (!pFile)
+ {
+ set_error();
+ return false;
+ }
+
+ // TODO: Change stream class to support UCS2 filenames.
+
+ return open(pFile, pFilename, attribs, true);
+ }
+
+ FILE* get_file() const { return m_pFile; }
+
+ virtual uint read(void* pBuf, uint len)
+ {
+ CRNLIB_ASSERT(pBuf && (len <= 0x7FFFFFFF));
+
+ if (!m_opened || (!is_readable()) || (!len))
+ return 0;
+
+ len = static_cast(math::minimum(len, get_remaining()));
+
+ if (fread(pBuf, 1, len, m_pFile) != len)
+ {
+ set_error();
+ return 0;
+ }
+
+ m_ofs += len;
+ return len;
+ }
+
+ virtual uint write(const void* pBuf, uint len)
+ {
+ CRNLIB_ASSERT(pBuf && (len <= 0x7FFFFFFF));
+
+ if (!m_opened || (!is_writable()) || (!len))
+ return 0;
+
+ if (fwrite(pBuf, 1, len, m_pFile) != len)
+ {
+ set_error();
+ return 0;
+ }
+
+ m_ofs += len;
+ m_size = math::maximum(m_size, m_ofs);
+
+ return len;
+ }
+
+ virtual bool flush()
+ {
+ if ((!m_opened) || (!is_writable()))
+ return false;
+
+ if (EOF == fflush(m_pFile))
+ {
+ set_error();
+ return false;
+ }
+
+ return true;
+ }
+
+ virtual uint64 get_size()
+ {
+ if (!m_opened)
+ return 0;
+
+ return m_size;
+ }
+
+ virtual uint64 get_remaining()
+ {
+ if (!m_opened)
+ return 0;
+
+ CRNLIB_ASSERT(m_ofs <= m_size);
+ return m_size - m_ofs;
+ }
+
+ virtual uint64 get_ofs()
+ {
+ if (!m_opened)
+ return 0;
+
+ return m_ofs;
+ }
+
+ virtual bool seek(int64 ofs, bool relative)
+ {
+ if ((!m_opened) || (!is_seekable()))
+ return false;
+
+ int64 new_ofs = relative ? (m_ofs + ofs) : ofs;
+ if (new_ofs < 0)
+ return false;
+ else if (static_cast(new_ofs) > m_size)
+ return false;
+
+ if (static_cast(new_ofs) != m_ofs)
+ {
+ if (crn_fseek(m_pFile, new_ofs, SEEK_SET) != 0)
+ {
+ set_error();
+ return false;
+ }
+
+ m_ofs = new_ofs;
+ }
+
+ return true;
+ }
+
+ static bool read_file_into_array(const char* pFilename, vector& buf)
+ {
+ cfile_stream in_stream(pFilename);
+ if (!in_stream.is_opened())
+ return false;
+ return in_stream.read_array(buf);
+ }
+
+ static bool write_array_to_file(const char* pFilename, const vector& buf)
+ {
+ cfile_stream out_stream(pFilename, cDataStreamWritable|cDataStreamSeekable);
+ if (!out_stream.is_opened())
+ return false;
+ return out_stream.write_array(buf);
+ }
+
+ private:
+ FILE* m_pFile;
+ uint64 m_size, m_ofs;
+ bool m_has_ownership;
+ };
+
+} // namespace crnlib
diff --git a/third_party/crunch/crnlib/crn_checksum.cpp b/third_party/crunch/crnlib/crn_checksum.cpp
new file mode 100644
index 000000000..5982a83e0
--- /dev/null
+++ b/third_party/crunch/crnlib/crn_checksum.cpp
@@ -0,0 +1,63 @@
+// File: crn_checksum.cpp
+#include "crn_core.h"
+
+namespace crnlib
+{
+ // From the public domain stb.h header.
+ uint adler32(const void* pBuf, size_t buflen, uint adler32)
+ {
+ const uint8* buffer = static_cast(pBuf);
+
+ const unsigned long ADLER_MOD = 65521;
+ unsigned long s1 = adler32 & 0xffff, s2 = adler32 >> 16;
+ size_t blocklen;
+ unsigned long i;
+
+ blocklen = buflen % 5552;
+ while (buflen) {
+ for (i=0; i + 7 < blocklen; i += 8) {
+ s1 += buffer[0], s2 += s1;
+ s1 += buffer[1], s2 += s1;
+ s1 += buffer[2], s2 += s1;
+ s1 += buffer[3], s2 += s1;
+ s1 += buffer[4], s2 += s1;
+ s1 += buffer[5], s2 += s1;
+ s1 += buffer[6], s2 += s1;
+ s1 += buffer[7], s2 += s1;
+
+ buffer += 8;
+ }
+
+ for (; i < blocklen; ++i)
+ s1 += *buffer++, s2 += s1;
+
+ s1 %= ADLER_MOD, s2 %= ADLER_MOD;
+ buflen -= blocklen;
+ blocklen = 5552;
+ }
+ return (s2 << 16) + s1;
+ }
+
+ uint16 crc16(const void* pBuf, size_t len, uint16 crc)
+ {
+ crc = ~crc;
+
+ const uint8* p = reinterpret_cast(pBuf);
+ while (len)
+ {
+ const uint16 q = *p++ ^ (crc >> 8);
+ crc <<= 8U;
+ uint16 r = (q >> 4) ^ q;
+ crc ^= r;
+ r <<= 5U;
+ crc ^= r;
+ r <<= 7U;
+ crc ^= r;
+ len--;
+ }
+
+ return static_cast(~crc);
+ }
+
+} // namespace crnlib
+
diff --git a/third_party/crunch/crnlib/crn_checksum.h b/third_party/crunch/crnlib/crn_checksum.h
new file mode 100644
index 000000000..6b9840c7b
--- /dev/null
+++ b/third_party/crunch/crnlib/crn_checksum.h
@@ -0,0 +1,13 @@
+// File: crn_checksum.h
+#pragma once
+
+namespace crnlib
+{
+ const uint cInitAdler32 = 1U;
+ uint adler32(const void* pBuf, size_t buflen, uint adler32 = cInitAdler32);
+
+ // crc16() intended for small buffers - doesn't use an acceleration table.
+ const uint cInitCRC16 = 0;
+ uint16 crc16(const void* pBuf, size_t len, uint16 crc = cInitCRC16);
+
+} // namespace crnlib
diff --git a/third_party/crunch/crnlib/crn_clusterizer.h b/third_party/crunch/crnlib/crn_clusterizer.h
new file mode 100644
index 000000000..c35745a07
--- /dev/null
+++ b/third_party/crunch/crnlib/crn_clusterizer.h
@@ -0,0 +1,764 @@
+// File: crn_clusterizer.h
+// See Copyright Notice and license at the end of inc/crnlib.h
+#pragma once
+#include "crn_matrix.h"
+
+namespace crnlib
+{
+ template
+ class clusterizer
+ {
+ public:
+ clusterizer() :
+ m_overall_variance(0.0f),
+ m_split_index(0),
+ m_heap_size(0),
+ m_quick(false)
+ {
+ }
+
+ void clear()
+ {
+ m_training_vecs.clear();
+ m_codebook.clear();
+ m_nodes.clear();
+ m_overall_variance = 0.0f;
+ m_split_index = 0;
+ m_heap_size = 0;
+ m_quick = false;
+ }
+
+ void reserve_training_vecs(uint num_expected)
+ {
+ m_training_vecs.reserve(num_expected);
+ }
+
+ void add_training_vec(const VectorType& v, uint weight)
+ {
+ m_training_vecs.push_back( std::make_pair(v, weight) );
+ }
+
+ typedef bool (*progress_callback_func_ptr)(uint percentage_completed, void* pData);
+
+ bool generate_codebook(uint max_size, progress_callback_func_ptr pProgress_callback = NULL, void* pProgress_data = NULL, bool quick = false)
+ {
+ if (m_training_vecs.empty())
+ return false;
+
+ m_quick = quick;
+
+ double ttsum = 0.0f;
+
+ vq_node root;
+ root.m_vectors.reserve(m_training_vecs.size());
+
+ for (uint i = 0; i < m_training_vecs.size(); i++)
+ {
+ const VectorType& v = m_training_vecs[i].first;
+ const uint weight = m_training_vecs[i].second;
+
+ root.m_centroid += (v * (float)weight);
+ root.m_total_weight += weight;
+ root.m_vectors.push_back(i);
+
+ ttsum += v.dot(v) * weight;
+ }
+
+ root.m_variance = (float)(ttsum - (root.m_centroid.dot(root.m_centroid) / root.m_total_weight));
+
+ root.m_centroid *= (1.0f / root.m_total_weight);
+
+ m_nodes.clear();
+ m_nodes.reserve(max_size * 2 + 1);
+
+ m_nodes.push_back(root);
+
+ m_heap.resize(max_size + 1);
+ m_heap[1] = 0;
+ m_heap_size = 1;
+
+ m_split_index = 0;
+
+ uint total_leaves = 1;
+
+ m_left_children.reserve(m_training_vecs.size() + 1);
+ m_right_children.reserve(m_training_vecs.size() + 1);
+
+ int prev_percentage = -1;
+ while ((total_leaves < max_size) && (m_heap_size))
+ {
+ int worst_node_index = m_heap[1];
+
+ m_heap[1] = m_heap[m_heap_size];
+ m_heap_size--;
+ if (m_heap_size)
+ down_heap(1);
+
+ split_node(worst_node_index);
+ total_leaves++;
+
+ if ((pProgress_callback) && ((total_leaves & 63) == 0) && (max_size))
+ {
+ int cur_percentage = (total_leaves * 100U + (max_size / 2U)) / max_size;
+ if (cur_percentage != prev_percentage)
+ {
+ if (!(*pProgress_callback)(cur_percentage, pProgress_data))
+ return false;
+
+ prev_percentage = cur_percentage;
+ }
+ }
+ }
+
+ m_codebook.clear();
+
+ m_overall_variance = 0.0f;
+
+ for (uint i = 0; i < m_nodes.size(); i++)
+ {
+ vq_node& node = m_nodes[i];
+ if (node.m_left != -1)
+ {
+ CRNLIB_ASSERT(node.m_right != -1);
+ continue;
+ }
+
+ CRNLIB_ASSERT((node.m_left == -1) && (node.m_right == -1));
+
+ node.m_codebook_index = m_codebook.size();
+ m_codebook.push_back(node.m_centroid);
+
+ m_overall_variance += node.m_variance;
+ }
+
+ m_heap.clear();
+ m_left_children.clear();
+ m_right_children.clear();
+
+ return true;
+ }
+
+ inline uint get_num_training_vecs() const { return m_training_vecs.size(); }
+ const VectorType& get_training_vec(uint index) const { return m_training_vecs[index].first; }
+ uint get_training_vec_weight(uint index) const { return m_training_vecs[index].second; }
+
+ typedef crnlib::vector< std::pair > training_vec_array;
+
+ const training_vec_array& get_training_vecs() const { return m_training_vecs; }
+ training_vec_array& get_training_vecs() { return m_training_vecs; }
+
+ inline float get_overall_variance() const { return m_overall_variance; }
+
+ inline uint get_codebook_size() const
+ {
+ return m_codebook.size();
+ }
+
+ inline const VectorType& get_codebook_entry(uint index) const
+ {
+ return m_codebook[index];
+ }
+
+ VectorType& get_codebook_entry(uint index)
+ {
+ return m_codebook[index];
+ }
+
+ typedef crnlib::vector vector_vec_type;
+ inline const vector_vec_type& get_codebook() const
+ {
+ return m_codebook;
+ }
+
+ uint find_best_codebook_entry(const VectorType& v) const
+ {
+ uint cur_node_index = 0;
+
+ for ( ; ; )
+ {
+ const vq_node& cur_node = m_nodes[cur_node_index];
+
+ if (cur_node.m_left == -1)
+ return cur_node.m_codebook_index;
+
+ const vq_node& left_node = m_nodes[cur_node.m_left];
+ const vq_node& right_node = m_nodes[cur_node.m_right];
+
+ float left_dist = left_node.m_centroid.squared_distance(v);
+ float right_dist = right_node.m_centroid.squared_distance(v);
+
+ if (left_dist < right_dist)
+ cur_node_index = cur_node.m_left;
+ else
+ cur_node_index = cur_node.m_right;
+ }
+ }
+
+ const VectorType& find_best_codebook_entry(const VectorType& v, uint max_codebook_size) const
+ {
+ uint cur_node_index = 0;
+
+ for ( ; ; )
+ {
+ const vq_node& cur_node = m_nodes[cur_node_index];
+
+ if ((cur_node.m_left == -1) || ((cur_node.m_codebook_index + 1) >= (int)max_codebook_size))
+ return cur_node.m_centroid;
+
+ const vq_node& left_node = m_nodes[cur_node.m_left];
+ const vq_node& right_node = m_nodes[cur_node.m_right];
+
+ float left_dist = left_node.m_centroid.squared_distance(v);
+ float right_dist = right_node.m_centroid.squared_distance(v);
+
+ if (left_dist < right_dist)
+ cur_node_index = cur_node.m_left;
+ else
+ cur_node_index = cur_node.m_right;
+ }
+ }
+
+ uint find_best_codebook_entry_fs(const VectorType& v) const
+ {
+ float best_dist = math::cNearlyInfinite;
+ uint best_index = 0;
+
+ for (uint i = 0; i < m_codebook.size(); i++)
+ {
+ float dist = m_codebook[i].squared_distance(v);
+ if (dist < best_dist)
+ {
+ best_dist = dist;
+ best_index = i;
+ if (best_dist == 0.0f)
+ break;
+ }
+ }
+
+ return best_index;
+ }
+
+ void retrieve_clusters(uint max_clusters, crnlib::vector< crnlib::vector >& clusters) const
+ {
+ clusters.resize(0);
+ clusters.reserve(max_clusters);
+
+ crnlib::vector stack;
+ stack.reserve(512);
+
+ uint cur_node_index = 0;
+
+ for ( ; ; )
+ {
+ const vq_node& cur_node = m_nodes[cur_node_index];
+
+ if ( (cur_node.is_leaf()) || ((cur_node.m_codebook_index + 2) > (int)max_clusters) )
+ {
+ clusters.resize(clusters.size() + 1);
+ clusters.back() = cur_node.m_vectors;
+
+ if (stack.empty())
+ break;
+ cur_node_index = stack.back();
+ stack.pop_back();
+ continue;
+ }
+
+ cur_node_index = cur_node.m_left;
+ stack.push_back(cur_node.m_right);
+ }
+ }
+
+ private:
+ training_vec_array m_training_vecs;
+
+ struct vq_node
+ {
+ vq_node() : m_centroid(cClear), m_total_weight(0), m_left(-1), m_right(-1), m_codebook_index(-1), m_unsplittable(false) { }
+
+ VectorType m_centroid;
+ uint64 m_total_weight;
+
+ float m_variance;
+
+ crnlib::vector m_vectors;
+
+ int m_left;
+ int m_right;
+
+ int m_codebook_index;
+
+ bool m_unsplittable;
+
+ bool is_leaf() const { return m_left < 0; }
+ };
+
+ typedef crnlib::vector node_vec_type;
+
+ node_vec_type m_nodes;
+
+ vector_vec_type m_codebook;
+
+ float m_overall_variance;
+
+ uint m_split_index;
+
+ crnlib::vector m_heap;
+ uint m_heap_size;
+
+ bool m_quick;
+
+ void insert_heap(uint node_index)
+ {
+ const float variance = m_nodes[node_index].m_variance;
+ uint pos = ++m_heap_size;
+
+ if (m_heap_size >= m_heap.size())
+ m_heap.resize(m_heap_size + 1);
+
+ for ( ; ; )
+ {
+ uint parent = pos >> 1;
+ if (!parent)
+ break;
+
+ float parent_variance = m_nodes[m_heap[parent]].m_variance;
+ if (parent_variance > variance)
+ break;
+
+ m_heap[pos] = m_heap[parent];
+
+ pos = parent;
+ }
+
+ m_heap[pos] = node_index;
+ }
+
+ void down_heap(uint pos)
+ {
+ uint child;
+ uint orig = m_heap[pos];
+
+ const float orig_variance = m_nodes[orig].m_variance;
+
+ while ((child = (pos << 1)) <= m_heap_size)
+ {
+ if (child < m_heap_size)
+ {
+ if (m_nodes[m_heap[child]].m_variance < m_nodes[m_heap[child + 1]].m_variance)
+ child++;
+ }
+
+ if (orig_variance > m_nodes[m_heap[child]].m_variance)
+ break;
+
+ m_heap[pos] = m_heap[child];
+
+ pos = child;
+ }
+
+ m_heap[pos] = orig;
+ }
+
+ void compute_split_estimate(VectorType& left_child_res, VectorType& right_child_res, const vq_node& parent_node)
+ {
+ VectorType furthest(0);
+ double furthest_dist = -1.0f;
+
+ for (uint i = 0; i < parent_node.m_vectors.size(); i++)
+ {
+ const VectorType& v = m_training_vecs[parent_node.m_vectors[i]].first;
+
+ double dist = v.squared_distance(parent_node.m_centroid);
+ if (dist > furthest_dist)
+ {
+ furthest_dist = dist;
+ furthest = v;
+ }
+ }
+
+ VectorType opposite(0);
+ double opposite_dist = -1.0f;
+
+ for (uint i = 0; i < parent_node.m_vectors.size(); i++)
+ {
+ const VectorType& v = m_training_vecs[parent_node.m_vectors[i]].first;
+
+ double dist = v.squared_distance(furthest);
+ if (dist > opposite_dist)
+ {
+ opposite_dist = dist;
+ opposite = v;
+ }
+ }
+
+ left_child_res = (furthest + parent_node.m_centroid) * .5f;
+ right_child_res = (opposite + parent_node.m_centroid) * .5f;
+ }
+
+ void compute_split_pca(VectorType& left_child_res, VectorType& right_child_res, const vq_node& parent_node)
+ {
+ if (parent_node.m_vectors.size() == 2)
+ {
+ left_child_res = m_training_vecs[parent_node.m_vectors[0]].first;
+ right_child_res = m_training_vecs[parent_node.m_vectors[1]].first;
+ return;
+ }
+
+ const uint N = VectorType::num_elements;
+
+ matrix covar;
+ covar.clear();
+
+ for (uint i = 0; i < parent_node.m_vectors.size(); i++)
+ {
+ const VectorType v(m_training_vecs[parent_node.m_vectors[i]].first - parent_node.m_centroid);
+ const VectorType w(v * (float)m_training_vecs[parent_node.m_vectors[i]].second);
+
+ for (uint x = 0; x < N; x++)
+ for (uint y = x; y < N; y++)
+ covar[x][y] = covar[x][y] + v[x] * w[y];
+ }
+
+ float one_over_total_weight = 1.0f / parent_node.m_total_weight;
+
+ for (uint x = 0; x < N; x++)
+ for (uint y = x; y < N; y++)
+ covar[x][y] *= one_over_total_weight;
+
+ for (uint x = 0; x < (N - 1); x++)
+ for (uint y = x + 1; y < N; y++)
+ covar[y][x] = covar[x][y];
+
+ VectorType axis;//(1.0f);
+ if (N == 1)
+ axis.set(1.0f);
+ else
+ {
+ for (uint i = 0; i < N; i++)
+ axis[i] = math::lerp(.75f, 1.25f, i * (1.0f / math::maximum(N - 1, 1)));
+ }
+
+ VectorType prev_axis(axis);
+
+ for (uint iter = 0; iter < 10; iter++)
+ {
+ VectorType x;
+
+ double max_sum = 0;
+
+ for (uint i = 0; i < N; i++)
+ {
+ double sum = 0;
+
+ for (uint j = 0; j < N; j++)
+ sum += axis[j] * covar[i][j];
+
+ x[i] = static_cast(sum);
+
+ max_sum = math::maximum(max_sum, fabs(sum));
+ }
+
+ if (max_sum != 0.0f)
+ x *= static_cast(1.0f / max_sum);
+
+ VectorType delta_axis(prev_axis - x);
+
+ prev_axis = axis;
+ axis = x;
+
+ if (delta_axis.norm() < .0025f)
+ break;
+ }
+
+ axis.normalize();
+
+ VectorType left_child(0.0f);
+ VectorType right_child(0.0f);
+
+ double left_weight = 0.0f;
+ double right_weight = 0.0f;
+
+ for (uint i = 0; i < parent_node.m_vectors.size(); i++)
+ {
+ const float weight = (float)m_training_vecs[parent_node.m_vectors[i]].second;
+
+ const VectorType& v = m_training_vecs[parent_node.m_vectors[i]].first;
+
+ double t = (v - parent_node.m_centroid) * axis;
+ if (t < 0.0f)
+ {
+ left_child += v * weight;
+ left_weight += weight;
+ }
+ else
+ {
+ right_child += v * weight;
+ right_weight += weight;
+ }
+ }
+
+ if ((left_weight > 0.0f) && (right_weight > 0.0f))
+ {
+ left_child_res = left_child * (float)(1.0f / left_weight);
+ right_child_res = right_child * (float)(1.0f / right_weight);
+ }
+ else
+ {
+ compute_split_estimate(left_child_res, right_child_res, parent_node);
+ }
+ }
+
+#if 0
+ void compute_split_pca2(VectorType& left_child_res, VectorType& right_child_res, const vq_node& parent_node)
+ {
+ if (parent_node.m_vectors.size() == 2)
+ {
+ left_child_res = m_training_vecs[parent_node.m_vectors[0]].first;
+ right_child_res = m_training_vecs[parent_node.m_vectors[1]].first;
+ return;
+ }
+
+ const uint N = VectorType::num_elements;
+
+ VectorType furthest;
+ double furthest_dist = -1.0f;
+
+ for (uint i = 0; i < parent_node.m_vectors.size(); i++)
+ {
+ const VectorType& v = m_training_vecs[parent_node.m_vectors[i]].first;
+
+ double dist = v.squared_distance(parent_node.m_centroid);
+ if (dist > furthest_dist)
+ {
+ furthest_dist = dist;
+ furthest = v;
+ }
+ }
+
+ VectorType opposite;
+ double opposite_dist = -1.0f;
+
+ for (uint i = 0; i < parent_node.m_vectors.size(); i++)
+ {
+ const VectorType& v = m_training_vecs[parent_node.m_vectors[i]].first;
+
+ double dist = v.squared_distance(furthest);
+ if (dist > opposite_dist)
+ {
+ opposite_dist = dist;
+ opposite = v;
+ }
+ }
+
+ VectorType axis(opposite - furthest);
+ if (axis.normalize() < .000125f)
+ {
+ left_child_res = (furthest + parent_node.m_centroid) * .5f;
+ right_child_res = (opposite + parent_node.m_centroid) * .5f;
+ return;
+ }
+
+ for (uint iter = 0; iter < 2; iter++)
+ {
+ double next_axis[N];
+ utils::zero_object(next_axis);
+
+ for (uint i = 0; i < parent_node.m_vectors.size(); i++)
+ {
+ const double weight = m_training_vecs[parent_node.m_vectors[i]].second;
+
+ VectorType v(m_training_vecs[parent_node.m_vectors[i]].first - parent_node.m_centroid);
+
+ double dot = (v * axis) * weight;
+
+ for (uint j = 0; j < N; j++)
+ next_axis[j] += dot * v[j];
+ }
+
+ double w = 0.0f;
+ for (uint j = 0; j < N; j++)
+ w += next_axis[j] * next_axis[j];
+
+ if (w > 0.0f)
+ {
+ w = 1.0f / sqrt(w);
+ for (uint j = 0; j < N; j++)
+ axis[j] = static_cast(next_axis[j] * w);
+ }
+ else
+ break;
+ }
+
+ VectorType left_child(0.0f);
+ VectorType right_child(0.0f);
+
+ double left_weight = 0.0f;
+ double right_weight = 0.0f;
+
+ for (uint i = 0; i < parent_node.m_vectors.size(); i++)
+ {
+ const float weight = (float)m_training_vecs[parent_node.m_vectors[i]].second;
+
+ const VectorType& v = m_training_vecs[parent_node.m_vectors[i]].first;
+
+ double t = (v - parent_node.m_centroid) * axis;
+ if (t < 0.0f)
+ {
+ left_child += v * weight;
+ left_weight += weight;
+ }
+ else
+ {
+ right_child += v * weight;
+ right_weight += weight;
+ }
+ }
+
+ if ((left_weight > 0.0f) && (right_weight > 0.0f))
+ {
+ left_child_res = left_child * (float)(1.0f / left_weight);
+ right_child_res = right_child * (float)(1.0f / right_weight);
+ }
+ else
+ {
+ left_child_res = (furthest + parent_node.m_centroid) * .5f;
+ right_child_res = (opposite + parent_node.m_centroid) * .5f;
+ }
+ }
+#endif
+
+ // thread safety warning: shared state!
+ crnlib::vector m_left_children;
+ crnlib::vector m_right_children;
+
+ void split_node(uint index)
+ {
+ vq_node& parent_node = m_nodes[index];
+
+ if (parent_node.m_vectors.size() == 1)
+ return;
+
+ VectorType left_child, right_child;
+ if (m_quick)
+ compute_split_estimate(left_child, right_child, parent_node);
+ else
+ compute_split_pca(left_child, right_child, parent_node);
+
+ uint64 left_weight = 0;
+ uint64 right_weight = 0;
+
+ float prev_total_variance = 1e+10f;
+
+ float left_variance = 0.0f;
+ float right_variance = 0.0f;
+
+ const uint cMaxLoops = m_quick ? 2 : 8;
+ for (uint total_loops = 0; total_loops < cMaxLoops; total_loops++)
+ {
+ m_left_children.resize(0);
+ m_right_children.resize(0);
+
+ VectorType new_left_child(cClear);
+ VectorType new_right_child(cClear);
+
+ double left_ttsum = 0.0f;
+ double right_ttsum = 0.0f;
+
+ left_weight = 0;
+ right_weight = 0;
+
+ for (uint i = 0; i < parent_node.m_vectors.size(); i++)
+ {
+ const VectorType& v = m_training_vecs[parent_node.m_vectors[i]].first;
+ const uint weight = m_training_vecs[parent_node.m_vectors[i]].second;
+
+ double left_dist2 = left_child.squared_distance(v);
+ double right_dist2 = right_child.squared_distance(v);
+
+ if (left_dist2 < right_dist2)
+ {
+ m_left_children.push_back(parent_node.m_vectors[i]);
+
+ new_left_child += (v * (float)weight);
+ left_weight += weight;
+
+ left_ttsum += v.dot(v) * weight;
+ }
+ else
+ {
+ m_right_children.push_back(parent_node.m_vectors[i]);
+
+ new_right_child += (v * (float)weight);
+ right_weight += weight;
+
+ right_ttsum += v.dot(v) * weight;
+ }
+ }
+
+ if ((!left_weight) || (!right_weight))
+ {
+ parent_node.m_unsplittable = true;
+ return;
+ }
+
+ left_variance = (float)(left_ttsum - (new_left_child.dot(new_left_child) / left_weight));
+ right_variance = (float)(right_ttsum - (new_right_child.dot(new_right_child) / right_weight));
+
+ new_left_child *= (1.0f / left_weight);
+ new_right_child *= (1.0f / right_weight);
+
+ left_child = new_left_child;
+ left_weight = left_weight;
+
+ right_child = new_right_child;
+ right_weight = right_weight;
+
+ float total_variance = left_variance + right_variance;
+ if (total_variance < .00001f)
+ break;
+
+ //const float variance_delta_thresh = .00001f;
+ const float variance_delta_thresh = .00125f;
+ if (((prev_total_variance - total_variance) / total_variance) < variance_delta_thresh)
+ break;
+
+ prev_total_variance = total_variance;
+ }
+
+ const uint left_child_index = m_nodes.size();
+ const uint right_child_index = m_nodes.size() + 1;
+
+ parent_node.m_left = m_nodes.size();
+ parent_node.m_right = m_nodes.size() + 1;
+ parent_node.m_codebook_index = m_split_index;
+ m_split_index++;
+
+ m_nodes.resize(m_nodes.size() + 2);
+
+ // parent_node is invalid now, because m_nodes has been changed
+
+ vq_node& left_child_node = m_nodes[left_child_index];
+ vq_node& right_child_node = m_nodes[right_child_index];
+
+ left_child_node.m_centroid = left_child;
+ left_child_node.m_total_weight = left_weight;
+ left_child_node.m_vectors.swap(m_left_children);
+ left_child_node.m_variance = left_variance;
+ if ((left_child_node.m_vectors.size() > 1) && (left_child_node.m_variance > 0.0f))
+ insert_heap(left_child_index);
+
+ right_child_node.m_centroid = right_child;
+ right_child_node.m_total_weight = right_weight;
+ right_child_node.m_vectors.swap(m_right_children);
+ right_child_node.m_variance = right_variance;
+ if ((right_child_node.m_vectors.size() > 1) && (right_child_node.m_variance > 0.0f))
+ insert_heap(right_child_index);
+ }
+
+ };
+
+} // namespace crnlib
+
+
+
diff --git a/third_party/crunch/crnlib/crn_color.h b/third_party/crunch/crnlib/crn_color.h
new file mode 100644
index 000000000..06f1983a4
--- /dev/null
+++ b/third_party/crunch/crnlib/crn_color.h
@@ -0,0 +1,994 @@
+// File: crn_color.h
+// See Copyright Notice and license at the end of inc/crnlib.h
+#pragma once
+#include "crn_core.h"
+
+namespace crnlib
+{
+ template struct color_quad_component_traits
+ {
+ enum
+ {
+ cSigned = false,
+ cFloat = false,
+ cMin = cUINT8_MIN,
+ cMax = cUINT8_MAX
+ };
+ };
+
+ template<> struct color_quad_component_traits
+ {
+ enum
+ {
+ cSigned = true,
+ cFloat = false,
+ cMin = cINT8_MIN,
+ cMax = cINT8_MAX
+ };
+ };
+
+ template<> struct color_quad_component_traits
+ {
+ enum
+ {
+ cSigned = true,
+ cFloat = false,
+ cMin = cINT16_MIN,
+ cMax = cINT16_MAX
+ };
+ };
+
+ template<> struct color_quad_component_traits
+ {
+ enum
+ {
+ cSigned = false,
+ cFloat = false,
+ cMin = cUINT16_MIN,
+ cMax = cUINT16_MAX
+ };
+ };
+
+ template<> struct color_quad_component_traits
+ {
+ enum
+ {
+ cSigned = true,
+ cFloat = false,
+ cMin = cINT32_MIN,
+ cMax = cINT32_MAX
+ };
+ };
+
+ template<> struct color_quad_component_traits
+ {
+ enum
+ {
+ cSigned = false,
+ cFloat = false,
+ cMin = cUINT32_MIN,
+ cMax = cUINT32_MAX
+ };
+ };
+
+ template<> struct color_quad_component_traits
+ {
+ enum
+ {
+ cSigned = false,
+ cFloat = true,
+ cMin = cINT32_MIN,
+ cMax = cINT32_MAX
+ };
+ };
+
+ template<> struct color_quad_component_traits
+ {
+ enum
+ {
+ cSigned = false,
+ cFloat = true,
+ cMin = cINT32_MIN,
+ cMax = cINT32_MAX
+ };
+ };
+
+ template
+ class color_quad : public helpers::rel_ops >
+ {
+ template
+ static inline parameter_type clamp(T v)
+ {
+ parameter_type result = static_cast(v);
+ if (!component_traits::cFloat)
+ {
+ if (v < component_traits::cMin)
+ result = static_cast(component_traits::cMin);
+ else if (v > component_traits::cMax)
+ result = static_cast(component_traits::cMax);
+ }
+ return result;
+ }
+
+#ifdef _MSC_VER
+ template<>
+ static inline parameter_type clamp(int v)
+ {
+ if (!component_traits::cFloat)
+ {
+ if ((!component_traits::cSigned) && (component_traits::cMin == 0) && (component_traits::cMax == 0xFF))
+ {
+ if (v & 0xFFFFFF00U)
+ v = (~(static_cast(v) >> 31)) & 0xFF;
+ }
+ else
+ {
+ if (v < component_traits::cMin)
+ v = component_traits::cMin;
+ else if (v > component_traits::cMax)
+ v = component_traits::cMax;
+ }
+ }
+ return static_cast(v);
+ }
+#endif
+
+ public:
+ typedef component_type component_t;
+ typedef parameter_type parameter_t;
+ typedef color_quad_component_traits component_traits;
+
+ enum { cNumComps = 4 };
+
+ union
+ {
+ struct
+ {
+ component_type r;
+ component_type g;
+ component_type b;
+ component_type a;
+ };
+
+ component_type c[cNumComps];
+
+ uint32 m_u32;
+ };
+
+ inline color_quad()
+ {
+ }
+
+ inline color_quad(eClear) :
+ r(0), g(0), b(0), a(0)
+ {
+ }
+
+ inline color_quad(const color_quad& other) :
+ r(other.r), g(other.g), b(other.b), a(other.a)
+ {
+ }
+
+ explicit inline color_quad(parameter_type y, parameter_type alpha = component_traits::cMax)
+ {
+ set(y, alpha);
+ }
+
+ inline color_quad(parameter_type red, parameter_type green, parameter_type blue, parameter_type alpha = component_traits::cMax)
+ {
+ set(red, green, blue, alpha);
+ }
+
+ explicit inline color_quad(eNoClamp, parameter_type y, parameter_type alpha = component_traits::cMax)
+ {
+ set_noclamp_y_alpha(y, alpha);
+ }
+
+ inline color_quad(eNoClamp, parameter_type red, parameter_type green, parameter_type blue, parameter_type alpha = component_traits::cMax)
+ {
+ set_noclamp_rgba(red, green, blue, alpha);
+ }
+
+ template
+ inline color_quad(const color_quad& other) :
+ r(static_cast(clamp(other.r))), g(static_cast(clamp(other.g))), b(static_cast(clamp(other.b))), a(static_cast(clamp(other.a)))
+ {
+ }
+
+ inline void clear()
+ {
+ r = 0;
+ g = 0;
+ b = 0;
+ a = 0;
+ }
+
+ inline color_quad& operator= (const color_quad& other)
+ {
+ r = other.r;
+ g = other.g;
+ b = other.b;
+ a = other.a;
+ return *this;
+ }
+
+ inline color_quad& set_rgb(const color_quad& other)
+ {
+ r = other.r;
+ g = other.g;
+ b = other.b;
+ return *this;
+ }
+
+ template
+ inline color_quad& operator=(const color_quad& other)
+ {
+ r = static_cast(clamp(other.r));
+ g = static_cast(clamp(other.g));
+ b = static_cast(clamp(other.b));
+ a = static_cast(clamp(other.a));
+ return *this;
+ }
+
+ inline color_quad& operator= (parameter_type y)
+ {
+ set(y, component_traits::cMax);
+ return *this;
+ }
+
+ inline color_quad& set(parameter_type y, parameter_type alpha = component_traits::cMax)
+ {
+ y = clamp(y);
+ alpha = clamp(alpha);
+ r = static_cast(y);
+ g = static_cast(y);
+ b = static_cast(y);
+ a = static_cast(alpha);
+ return *this;
+ }
+
+ inline color_quad& set_noclamp_y_alpha(parameter_type y, parameter_type alpha = component_traits::cMax)
+ {
+ CRNLIB_ASSERT( (y >= component_traits::cMin) && (y <= component_traits::cMax) );
+ CRNLIB_ASSERT( (alpha >= component_traits::cMin) && (alpha <= component_traits::cMax) );
+
+ r = static_cast(y);
+ g = static_cast(y);
+ b = static_cast(y);
+ a = static_cast(alpha);
+ return *this;
+ }
+
+ inline color_quad& set(parameter_type red, parameter_type green, parameter_type blue, parameter_type alpha = component_traits::cMax)
+ {
+ r = static_cast(clamp(red));
+ g = static_cast(clamp(green));
+ b = static_cast(clamp(blue));
+ a = static_cast(clamp(alpha));
+ return *this;
+ }
+
+ inline color_quad& set_noclamp_rgba(parameter_type red, parameter_type green, parameter_type blue, parameter_type alpha)
+ {
+ CRNLIB_ASSERT( (red >= component_traits::cMin) && (red <= component_traits::cMax) );
+ CRNLIB_ASSERT( (green >= component_traits::cMin) && (green <= component_traits::cMax) );
+ CRNLIB_ASSERT( (blue >= component_traits::cMin) && (blue <= component_traits::cMax) );
+ CRNLIB_ASSERT( (alpha >= component_traits::cMin) && (alpha <= component_traits::cMax) );
+
+ r = static_cast(red);
+ g = static_cast(green);
+ b = static_cast(blue);
+ a = static_cast(alpha);
+ return *this;
+ }
+
+ inline color_quad& set_noclamp_rgb(parameter_type red, parameter_type green, parameter_type blue)
+ {
+ CRNLIB_ASSERT( (red >= component_traits::cMin) && (red <= component_traits::cMax) );
+ CRNLIB_ASSERT( (green >= component_traits::cMin) && (green <= component_traits::cMax) );
+ CRNLIB_ASSERT( (blue >= component_traits::cMin) && (blue <= component_traits::cMax) );
+
+ r = static_cast(red);
+ g = static_cast(green);
+ b = static_cast(blue);
+ return *this;
+ }
+
+ static inline parameter_type get_min_comp() { return component_traits::cMin; }
+ static inline parameter_type get_max_comp() { return component_traits::cMax; }
+ static inline bool get_comps_are_signed() { return component_traits::cSigned; }
+
+ inline component_type operator[] (uint i) const { CRNLIB_ASSERT(i < cNumComps); return c[i]; }
+ inline component_type& operator[] (uint i) { CRNLIB_ASSERT(i < cNumComps); return c[i]; }
+
+ inline color_quad& set_component(uint i, parameter_type f)
+ {
+ CRNLIB_ASSERT(i < cNumComps);
+
+ c[i] = static_cast(clamp(f));
+
+ return *this;
+ }
+
+ inline color_quad& set_grayscale(parameter_t l)
+ {
+ component_t x = static_cast(clamp(l));
+ c[0] = x;
+ c[1] = x;
+ c[2] = x;
+ return *this;
+ }
+
+ inline color_quad& clamp(const color_quad& l, const color_quad& h)
+ {
+ for (uint i = 0; i < cNumComps; i++)
+ c[i] = static_cast(math::clamp(c[i], l[i], h[i]));
+ return *this;
+ }
+
+ inline color_quad& clamp(parameter_type l, parameter_type h)
+ {
+ for (uint i = 0; i < cNumComps; i++)
+ c[i] = static_cast(math::clamp(c[i], l, h));
+ return *this;
+ }
+
+ // Returns CCIR 601 luma (consistent with color_utils::RGB_To_Y).
+ inline parameter_type get_luma() const
+ {
+ return static_cast((19595U * r + 38470U * g + 7471U * b + 32768U) >> 16U);
+ }
+
+ // Returns REC 709 luma.
+ inline parameter_type get_luma_rec709() const
+ {
+ return static_cast((13938U * r + 46869U * g + 4729U * b + 32768U) >> 16U);
+ }
+
+ // Beware of endianness!
+ inline uint32 get_uint32() const
+ {
+ CRNLIB_ASSERT(sizeof(*this) == sizeof(uint32));
+ return *reinterpret_cast(this);
+ }
+
+ // Beware of endianness!
+ inline uint64 get_uint64() const
+ {
+ CRNLIB_ASSERT(sizeof(*this) == sizeof(uint64));
+ return *reinterpret_cast(this);
+ }
+
+ inline uint squared_distance(const color_quad& c, bool alpha = true) const
+ {
+ return math::square(r - c.r) + math::square(g - c.g) + math::square(b - c.b) + (alpha ? math::square(a - c.a) : 0);
+ }
+
+ inline bool rgb_equals(const color_quad& rhs) const
+ {
+ return (r == rhs.r) && (g == rhs.g) && (b == rhs.b);
+ }
+
+ inline bool operator== (const color_quad& rhs) const
+ {
+ if (sizeof(color_quad) == sizeof(uint32))
+ return m_u32 == rhs.m_u32;
+ else
+ return (r == rhs.r) && (g == rhs.g) && (b == rhs.b) && (a == rhs.a);
+ }
+
+ inline bool operator< (const color_quad& rhs) const
+ {
+ for (uint i = 0; i < cNumComps; i++)
+ {
+ if (c[i] < rhs.c[i])
+ return true;
+ else if (!(c[i] == rhs.c[i]))
+ return false;
+ }
+ return false;
+ }
+
+ color_quad& operator+= (const color_quad& other)
+ {
+ for (uint i = 0; i < 4; i++)
+ c[i] = static_cast(clamp(c[i] + other.c[i]));
+ return *this;
+ }
+
+ color_quad& operator-= (const color_quad& other)
+ {
+ for (uint i = 0; i < 4; i++)
+ c[i] = static_cast(clamp(c[i] - other.c[i]));
+ return *this;
+ }
+
+ color_quad& operator*= (parameter_type v)
+ {
+ for (uint i = 0; i < 4; i++)
+ c[i] = static_cast(clamp(c[i] * v));
+ return *this;
+ }
+
+ color_quad& operator/= (parameter_type v)
+ {
+ for (uint i = 0; i < 4; i++)
+ c[i] = static_cast(c[i] / v);
+ return *this;
+ }
+
+ color_quad get_swizzled(uint x, uint y, uint z, uint w) const
+ {
+ CRNLIB_ASSERT((x | y | z | w) < 4);
+ return color_quad(c[x], c[y], c[z], c[w]);
+ }
+
+ friend color_quad operator+ (const color_quad& lhs, const color_quad& rhs)
+ {
+ color_quad result(lhs);
+ result += rhs;
+ return result;
+ }
+
+ friend color_quad operator- (const color_quad& lhs, const color_quad& rhs)
+ {
+ color_quad result(lhs);
+ result -= rhs;
+ return result;
+ }
+
+ friend color_quad operator* (const color_quad& lhs, parameter_type v)
+ {
+ color_quad result(lhs);
+ result *= v;
+ return result;
+ }
+
+ friend color_quad operator/ (const color_quad& lhs, parameter_type v)
+ {
+ color_quad result(lhs);
+ result /= v;
+ return result;
+ }
+
+ friend color_quad operator* (parameter_type v, const color_quad& rhs)
+ {
+ color_quad result(rhs);
+ result *= v;
+ return result;
+ }
+
+ inline bool is_grayscale() const
+ {
+ return (c[0] == c[1]) && (c[1] == c[2]);
+ }
+
+ uint get_min_component_index(bool alpha = true) const
+ {
+ uint index = 0;
+ uint limit = alpha ? cNumComps : (cNumComps - 1);
+ for (uint i = 1; i < limit; i++)
+ if (c[i] < c[index])
+ index = i;
+ return index;
+ }
+
+ uint get_max_component_index(bool alpha = true) const
+ {
+ uint index = 0;
+ uint limit = alpha ? cNumComps : (cNumComps - 1);
+ for (uint i = 1; i < limit; i++)
+ if (c[i] > c[index])
+ index = i;
+ return index;
+ }
+
+ operator size_t() const
+ {
+ return (size_t)fast_hash(this, sizeof(*this));
+ }
+
+ void get_float4(float* pDst)
+ {
+ for (uint i = 0; i < 4; i++)
+ pDst[i] = ((*this)[i] - component_traits::cMin) / float(component_traits::cMax - component_traits::cMin);
+ }
+
+ void get_float3(float* pDst)
+ {
+ for (uint i = 0; i < 3; i++)
+ pDst[i] = ((*this)[i] - component_traits::cMin) / float(component_traits::cMax - component_traits::cMin);
+ }
+
+ static color_quad component_min(const color_quad& a, const color_quad& b)
+ {
+ color_quad result;
+ for (uint i = 0; i < 4; i++)
+ result[i] = static_cast(math::minimum(a[i], b[i]));
+ return result;
+ }
+
+ static color_quad component_max(const color_quad& a, const color_quad& b)
+ {
+ color_quad result;
+ for (uint i = 0; i < 4; i++)
+ result[i] = static_cast(math::maximum(a[i], b[i]));
+ return result;
+ }
+
+ static color_quad make_black()
+ {
+ return color_quad(0, 0, 0, component_traits::cMax);
+ }
+
+ static color_quad make_white()
+ {
+ return color_quad(component_traits::cMax, component_traits::cMax, component_traits::cMax, component_traits::cMax);
+ }
+ }; // class color_quad
+
+ template
+ struct scalar_type< color_quad >
+ {
+ enum { cFlag = true };
+ static inline void construct(color_quad* p) { }
+ static inline void construct(color_quad* p, const color_quad& init) { memcpy(p, &init, sizeof(color_quad)); }
+ static inline void construct_array(color_quad* p, uint n) { p, n; }
+ static inline void destruct(color_quad* p) { p; }
+ static inline void destruct_array(color_quad* p, uint n) { p, n; }
+ };
+
+ typedef color_quad color_quad_u8;
+ typedef color_quad color_quad_i8;
+ typedef color_quad color_quad_i16;
+ typedef color_quad color_quad_u16;
+ typedef color_quad color_quad_i32;
+ typedef color_quad color_quad_u32;
+ typedef color_quad color_quad_f;
+ typedef color_quad color_quad_d;
+
+ namespace color
+ {
+ inline uint elucidian_distance(uint r0, uint g0, uint b0, uint r1, uint g1, uint b1)
+ {
+ int dr = (int)r0 - (int)r1;
+ int dg = (int)g0 - (int)g1;
+ int db = (int)b0 - (int)b1;
+
+ return static_cast(dr * dr + dg * dg + db * db);
+ }
+
+ inline uint elucidian_distance(uint r0, uint g0, uint b0, uint a0, uint r1, uint g1, uint b1, uint a1)
+ {
+ int dr = (int)r0 - (int)r1;
+ int dg = (int)g0 - (int)g1;
+ int db = (int)b0 - (int)b1;
+ int da = (int)a0 - (int)a1;
+
+ return static_cast(dr * dr + dg * dg + db * db + da * da);
+ }
+
+ inline uint elucidian_distance(const color_quad_u8& c0, const color_quad_u8& c1, bool alpha)
+ {
+ if (alpha)
+ return elucidian_distance(c0.r, c0.g, c0.b, c0.a, c1.r, c1.g, c1.b, c1.a);
+ else
+ return elucidian_distance(c0.r, c0.g, c0.b, c1.r, c1.g, c1.b);
+ }
+
+ inline uint weighted_elucidian_distance(uint r0, uint g0, uint b0, uint r1, uint g1, uint b1, uint wr, uint wg, uint wb)
+ {
+ int dr = (int)r0 - (int)r1;
+ int dg = (int)g0 - (int)g1;
+ int db = (int)b0 - (int)b1;
+
+ return static_cast((wr * dr * dr) + (wg * dg * dg) + (wb * db * db));
+ }
+
+ inline uint weighted_elucidian_distance(
+ uint r0, uint g0, uint b0, uint a0,
+ uint r1, uint g1, uint b1, uint a1,
+ uint wr, uint wg, uint wb, uint wa)
+ {
+ int dr = (int)r0 - (int)r1;
+ int dg = (int)g0 - (int)g1;
+ int db = (int)b0 - (int)b1;
+ int da = (int)a0 - (int)a1;
+
+ return static_cast((wr * dr * dr) + (wg * dg * dg) + (wb * db * db) + (wa * da * da));
+ }
+
+ inline uint weighted_elucidian_distance(const color_quad_u8& c0, const color_quad_u8& c1, uint wr, uint wg, uint wb, uint wa)
+ {
+ return weighted_elucidian_distance(c0.r, c0.g, c0.b, c0.a, c1.r, c1.g, c1.b, c1.a, wr, wg, wb, wa);
+ }
+
+ //const uint cRWeight = 8;//24;
+ //const uint cGWeight = 24;//73;
+ //const uint cBWeight = 1;//3;
+
+ const uint cRWeight = 8;//24;
+ const uint cGWeight = 25;//73;
+ const uint cBWeight = 1;//3;
+
+ inline uint color_distance(bool perceptual, const color_quad_u8& e1, const color_quad_u8& e2, bool alpha)
+ {
+ if (perceptual)
+ {
+ if (alpha)
+ return weighted_elucidian_distance(e1, e2, cRWeight, cGWeight, cBWeight, cRWeight+cGWeight+cBWeight);
+ else
+ return weighted_elucidian_distance(e1, e2, cRWeight, cGWeight, cBWeight, 0);
+ }
+ else
+ return elucidian_distance(e1, e2, alpha);
+ }
+
+ inline uint peak_color_error(const color_quad_u8& e1, const color_quad_u8& e2)
+ {
+ return math::maximum(labs(e1[0] - e2[0]), labs(e1[1] - e2[1]), labs(e1[2] - e2[2]));
+ //return math::square(e1[0] - e2[0]) + math::square(e1[1] - e2[1]) + math::square(e1[2] - e2[2]);
+ }
+
+ // y - [0,255]
+ // co - [-127,127]
+ // cg - [-126,127]
+ inline void RGB_to_YCoCg(int r, int g, int b, int& y, int& co, int& cg)
+ {
+ y = (r >> 2) + (g >> 1) + (b >> 2);
+ co = (r >> 1) - (b >> 1);
+ cg = -(r >> 2) + (g >> 1) - (b >> 2);
+ }
+
+ inline void YCoCg_to_RGB(int y, int co, int cg, int& r, int& g, int& b)
+ {
+ int tmp = y - cg;
+ g = y + cg;
+ r = tmp + co;
+ b = tmp - co;
+ }
+
+ static inline uint8 clamp_component(int i) { if (static_cast(i) > 255U) { if (i < 0) i = 0; else if (i > 255) i = 255; } return static_cast(i); }
+
+ // RGB->YCbCr constants, scaled by 2^16
+ const int YR = 19595, YG = 38470, YB = 7471, CB_R = -11059, CB_G = -21709, CB_B = 32768, CR_R = 32768, CR_G = -27439, CR_B = -5329;
+ // YCbCr->RGB constants, scaled by 2^16
+ const int R_CR = 91881, B_CB = 116130, G_CR = -46802, G_CB = -22554;
+
+ inline int RGB_to_Y(const color_quad_u8& rgb)
+ {
+ const int r = rgb[0], g = rgb[1], b = rgb[2];
+ return (r * YR + g * YG + b * YB + 32768) >> 16;
+ }
+
+ // RGB to YCbCr (same as JFIF JPEG).
+ // Odd default biases account for 565 endpoint packing.
+ inline void RGB_to_YCC(color_quad_u8& ycc, const color_quad_u8& rgb, int cb_bias = 123, int cr_bias = 125)
+ {
+ const int r = rgb[0], g = rgb[1], b = rgb[2];
+ ycc.a = static_cast((r * YR + g * YG + b * YB + 32768) >> 16);
+ ycc.r = clamp_component(cb_bias + ((r * CB_R + g * CB_G + b * CB_B + 32768) >> 16));
+ ycc.g = clamp_component(cr_bias + ((r * CR_R + g * CR_G + b * CR_B + 32768) >> 16));
+ ycc.b = 0;
+ }
+
+ // YCbCr to RGB.
+ // Odd biases account for 565 endpoint packing.
+ inline void YCC_to_RGB(color_quad_u8& rgb, const color_quad_u8& ycc, int cb_bias = 123, int cr_bias = 125)
+ {
+ const int y = ycc.a;
+ const int cb = ycc.r - cb_bias;
+ const int cr = ycc.g - cr_bias;
+ rgb.r = clamp_component(y + ((R_CR * cr + 32768) >> 16));
+ rgb.g = clamp_component(y + ((G_CR * cr + G_CB * cb + 32768) >> 16));
+ rgb.b = clamp_component(y + ((B_CB * cb + 32768) >> 16));
+ rgb.a = 255;
+ }
+
+ // Float RGB->YCbCr constants
+ const float S = 1.0f/65536.0f;
+ const float F_YR = S*YR, F_YG = S*YG, F_YB = S*YB, F_CB_R = S*CB_R, F_CB_G = S*CB_G, F_CB_B = S*CB_B, F_CR_R = S*CR_R, F_CR_G = S*CR_G, F_CR_B = S*CR_B;
+ // Float YCbCr->RGB constants
+ const float F_R_CR = S*R_CR, F_B_CB = S*B_CB, F_G_CR = S*G_CR, F_G_CB = S*G_CB;
+
+ inline void RGB_to_YCC_float(color_quad_f& ycc, const color_quad_u8& rgb)
+ {
+ const int r = rgb[0], g = rgb[1], b = rgb[2];
+ ycc.a = r * F_YR + g * F_YG + b * F_YB;
+ ycc.r = r * F_CB_R + g * F_CB_G + b * F_CB_B;
+ ycc.g = r * F_CR_R + g * F_CR_G + b * F_CR_B;
+ ycc.b = 0;
+ }
+
+ inline void YCC_float_to_RGB(color_quad_u8& rgb, const color_quad_f& ycc)
+ {
+ float y = ycc.a, cb = ycc.r, cr = ycc.g;
+ rgb.r = color::clamp_component(static_cast(.5f + y + F_R_CR * cr));
+ rgb.g = color::clamp_component(static_cast(.5f + y + F_G_CR * cr + F_G_CB * cb));
+ rgb.b = color::clamp_component(static_cast(.5f + y + F_B_CB * cb));
+ rgb.a = 255;
+ }
+
+ } // namespace color
+
+ // This class purposely trades off speed for extremely flexibility. It can handle any component swizzle, any pixel type from 1-4 components and 1-32 bits/component,
+ // any pixel size between 1-16 bytes/pixel, any pixel stride, any color_quad data type (signed/unsigned/float 8/16/32 bits/component), and scaled/non-scaled components.
+ // On the downside, it's freaking slow.
+ class pixel_packer
+ {
+ public:
+ pixel_packer()
+ {
+ clear();
+ }
+
+ pixel_packer(uint num_comps, uint bits_per_comp, int pixel_stride = -1, bool reversed = false)
+ {
+ init(num_comps, bits_per_comp, pixel_stride, reversed);
+ }
+
+ pixel_packer(const char* pComp_map, int pixel_stride = -1, int force_comp_size = -1)
+ {
+ init(pComp_map, pixel_stride, force_comp_size);
+ }
+
+ void clear()
+ {
+ utils::zero_this(this);
+ }
+
+ inline bool is_valid() const { return m_pixel_stride > 0; }
+
+ inline uint get_pixel_stride() const { return m_pixel_stride; }
+ void set_pixel_stride(uint n) { m_pixel_stride = n; }
+
+ uint get_num_comps() const { return m_num_comps; }
+ uint get_comp_size(uint index) const { CRNLIB_ASSERT(index < 4); return m_comp_size[index]; }
+ uint get_comp_ofs(uint index) const { CRNLIB_ASSERT(index < 4); return m_comp_ofs[index]; }
+ uint get_comp_max(uint index) const { CRNLIB_ASSERT(index < 4); return m_comp_max[index]; }
+ bool get_rgb_is_luma() const { return m_rgb_is_luma; }
+
+ template
+ const void* unpack(const void* p, color_quad_type& color, bool rescale = true) const
+ {
+ const uint8* pSrc = static_cast(p);
+
+ for (uint i = 0; i < 4; i++)
+ {
+ const uint comp_size = m_comp_size[i];
+ if (!comp_size)
+ {
+ if (color_quad_type::component_traits::cFloat)
+ color[i] = static_cast< typename color_quad_type::parameter_t >((i == 3) ? 1 : 0);
+ else
+ color[i] = static_cast< typename color_quad_type::parameter_t >((i == 3) ? color_quad_type::component_traits::cMax : 0);
+ continue;
+ }
+
+ uint n = 0, dst_bit_ofs = 0;
+ uint src_bit_ofs = m_comp_ofs[i];
+ while (dst_bit_ofs < comp_size)
+ {
+ const uint byte_bit_ofs = src_bit_ofs & 7;
+ n |= ((pSrc[src_bit_ofs >> 3] >> byte_bit_ofs) << dst_bit_ofs);
+
+ const uint bits_read = 8 - byte_bit_ofs;
+ src_bit_ofs += bits_read;
+ dst_bit_ofs += bits_read;
+ }
+
+ const uint32 mx = m_comp_max[i];
+ n &= mx;
+
+ const uint32 h = static_cast(color_quad_type::component_traits::cMax);
+
+ if (color_quad_type::component_traits::cFloat)
+ color.set_component(i, static_cast(n));
+ else if (rescale)
+ color.set_component(i, static_cast( (static_cast(n) * h + (mx >> 1U)) / mx ) );
+ else if (color_quad_type::component_traits::cSigned)
+ color.set_component(i, static_cast(math::minimum(n, h)));
+ else
+ color.set_component(i, static_cast(n));
+ }
+
+ if (m_rgb_is_luma)
+ {
+ color[0] = color[1];
+ color[2] = color[1];
+ }
+
+ return pSrc + m_pixel_stride;
+ }
+
+ template
+ void* pack(const color_quad_type& color, void* p, bool rescale = true) const
+ {
+ uint8* pDst = static_cast(p);
+
+ for (uint i = 0; i < 4; i++)
+ {
+ const uint comp_size = m_comp_size[i];
+ if (!comp_size)
+ continue;
+
+ uint32 mx = m_comp_max[i];
+
+ uint32 n;
+ if (color_quad_type::component_traits::cFloat)
+ {
+ typename color_quad_type::parameter_t t = color[i];
+ if (t < 0.0f)
+ n = 0;
+ else if (t > static_cast(mx))
+ n = mx;
+ else
+ n = math::minimum(static_cast(floor(t + .5f)), mx);
+ }
+ else if (rescale)
+ {
+ if (color_quad_type::component_traits::cSigned)
+ n = math::maximum(static_cast(color[i]), 0);
+ else
+ n = static_cast(color[i]);
+
+ const uint32 h = static_cast(color_quad_type::component_traits::cMax);
+ n = static_cast((static_cast(n) * mx + (h >> 1)) / h);
+ }
+ else
+ {
+ if (color_quad_type::component_traits::cSigned)
+ n = math::minimum(static_cast(math::maximum(static_cast(color[i]), 0)), mx);
+ else
+ n = math::minimum(static_cast(color[i]), mx);
+ }
+
+ uint src_bit_ofs = 0;
+ uint dst_bit_ofs = m_comp_ofs[i];
+ while (src_bit_ofs < comp_size)
+ {
+ const uint cur_byte_bit_ofs = (dst_bit_ofs & 7);
+ const uint cur_byte_bits = 8 - cur_byte_bit_ofs;
+
+ uint byte_val = pDst[dst_bit_ofs >> 3];
+ uint bit_mask = (mx << cur_byte_bit_ofs) & 0xFF;
+ byte_val &= ~bit_mask;
+ byte_val |= (n << cur_byte_bit_ofs);
+ pDst[dst_bit_ofs >> 3] = static_cast(byte_val);
+
+ mx >>= cur_byte_bits;
+ n >>= cur_byte_bits;
+
+ dst_bit_ofs += cur_byte_bits;
+ src_bit_ofs += cur_byte_bits;
+ }
+ }
+
+ return pDst + m_pixel_stride;
+ }
+
+ bool init(uint num_comps, uint bits_per_comp, int pixel_stride = -1, bool reversed = false)
+ {
+ clear();
+
+ if ((num_comps < 1) || (num_comps > 4) || (bits_per_comp < 1) || (bits_per_comp > 32))
+ {
+ CRNLIB_ASSERT(0);
+ return false;
+ }
+
+ for (uint i = 0; i < num_comps; i++)
+ {
+ m_comp_size[i] = bits_per_comp;
+ m_comp_ofs[i] = i * bits_per_comp;
+ if (reversed)
+ m_comp_ofs[i] = ((num_comps - 1) * bits_per_comp) - m_comp_ofs[i];
+ }
+
+ for (uint i = 0; i < 4; i++)
+ m_comp_max[i] = static_cast((1ULL << m_comp_size[i]) - 1ULL);
+
+ m_pixel_stride = (pixel_stride >= 0) ? pixel_stride : (num_comps * bits_per_comp + 7) / 8;
+
+ return true;
+ }
+
+ // Format examples:
+ // R16G16B16
+ // B5G6R5
+ // B5G5R5x1
+ // Y8A8
+ // A8R8G8B8
+ // First component is at LSB in memory. Assumes unsigned integer components, 1-32bits each.
+ bool init(const char* pComp_map, int pixel_stride = -1, int force_comp_size = -1)
+ {
+ clear();
+
+ uint cur_bit_ofs = 0;
+
+ while (*pComp_map)
+ {
+ char c = *pComp_map++;
+
+ int comp_index = -1;
+ if (c == 'R')
+ comp_index = 0;
+ else if (c == 'G')
+ comp_index = 1;
+ else if (c == 'B')
+ comp_index = 2;
+ else if (c == 'A')
+ comp_index = 3;
+ else if (c == 'Y')
+ comp_index = 4;
+ else if (c != 'x')
+ return false;
+
+ uint comp_size = 0;
+
+ uint n = *pComp_map;
+ if ((n >= '0') && (n <= '9'))
+ {
+ comp_size = n - '0';
+ pComp_map++;
+
+ n = *pComp_map;
+ if ((n >= '0') && (n <= '9'))
+ {
+ comp_size = (comp_size * 10) + (n - '0');
+ pComp_map++;
+ }
+ }
+
+ if (force_comp_size != -1)
+ comp_size = force_comp_size;
+
+ if ((!comp_size) || (comp_size > 32))
+ return false;
+
+ if (comp_index == 4)
+ {
+ if (m_comp_size[0] || m_comp_size[1] || m_comp_size[2])
+ return false;
+
+ //m_comp_ofs[0] = m_comp_ofs[1] = m_comp_ofs[2] = cur_bit_ofs;
+ //m_comp_size[0] = m_comp_size[1] = m_comp_size[2] = comp_size;
+ m_comp_ofs[1] = cur_bit_ofs;
+ m_comp_size[1] = comp_size;
+ m_rgb_is_luma = true;
+ m_num_comps++;
+ }
+ else if (comp_index >= 0)
+ {
+ if (m_comp_size[comp_index])
+ return false;
+
+ m_comp_ofs[comp_index] = cur_bit_ofs;
+ m_comp_size[comp_index] = comp_size;
+ m_num_comps++;
+ }
+
+ cur_bit_ofs += comp_size;
+ }
+
+ for (uint i = 0; i < 4; i++)
+ m_comp_max[i] = static_cast((1ULL << m_comp_size[i]) - 1ULL);
+
+ if (pixel_stride >= 0)
+ m_pixel_stride = pixel_stride;
+ else
+ m_pixel_stride = (cur_bit_ofs + 7) / 8;
+ return true;
+ }
+
+ private:
+ uint m_pixel_stride;
+ uint m_num_comps;
+ uint m_comp_size[4];
+ uint m_comp_ofs[4];
+ uint m_comp_max[4];
+ bool m_rgb_is_luma;
+ };
+
+} // namespace crnlib
+
diff --git a/third_party/crunch/crnlib/crn_colorized_console.cpp b/third_party/crunch/crnlib/crn_colorized_console.cpp
new file mode 100644
index 000000000..a871f846f
--- /dev/null
+++ b/third_party/crunch/crnlib/crn_colorized_console.cpp
@@ -0,0 +1,119 @@
+// File: crn_colorized_console.cpp
+// See Copyright Notice and license at the end of inc/crnlib.h
+#include "crn_core.h"
+#include "crn_colorized_console.h"
+#ifdef CRNLIB_USE_WIN32_API
+#include "crn_winhdr.h"
+#endif
+
+namespace crnlib
+{
+ void colorized_console::init()
+ {
+ console::init();
+ console::add_console_output_func(console_output_func, NULL);
+ }
+
+ void colorized_console::deinit()
+ {
+ console::remove_console_output_func(console_output_func);
+ console::deinit();
+ }
+
+ void colorized_console::tick()
+ {
+ }
+
+#ifdef CRNLIB_USE_WIN32_API
+ bool colorized_console::console_output_func(eConsoleMessageType type, const char* pMsg, void* pData)
+ {
+ pData;
+
+ if (console::get_output_disabled())
+ return true;
+
+ HANDLE cons = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ DWORD attr = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
+ switch (type)
+ {
+ case cDebugConsoleMessage: attr = FOREGROUND_BLUE | FOREGROUND_INTENSITY; break;
+ case cMessageConsoleMessage: attr = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY; break;
+ case cWarningConsoleMessage: attr = FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY; break;
+ case cErrorConsoleMessage: attr = FOREGROUND_RED | FOREGROUND_INTENSITY; break;
+ default: break;
+ }
+
+ if (INVALID_HANDLE_VALUE != cons)
+ SetConsoleTextAttribute(cons, (WORD)attr);
+
+ if ((console::get_prefixes()) && (console::get_at_beginning_of_line()))
+ {
+ switch (type)
+ {
+ case cDebugConsoleMessage:
+ printf("Debug: %s", pMsg);
+ break;
+ case cWarningConsoleMessage:
+ printf("Warning: %s", pMsg);
+ break;
+ case cErrorConsoleMessage:
+ printf("Error: %s", pMsg);
+ break;
+ default:
+ printf("%s", pMsg);
+ break;
+ }
+ }
+ else
+ {
+ printf("%s", pMsg);
+ }
+
+ if (console::get_crlf())
+ printf("\n");
+
+ if (INVALID_HANDLE_VALUE != cons)
+ SetConsoleTextAttribute(cons, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
+
+ return true;
+ }
+#else
+ bool colorized_console::console_output_func(eConsoleMessageType type, const char* pMsg, void* pData)
+ {
+ pData;
+ if (console::get_output_disabled())
+ return true;
+
+ if ((console::get_prefixes()) && (console::get_at_beginning_of_line()))
+ {
+ switch (type)
+ {
+ case cDebugConsoleMessage:
+ printf("Debug: %s", pMsg);
+ break;
+ case cWarningConsoleMessage:
+ printf("Warning: %s", pMsg);
+ break;
+ case cErrorConsoleMessage:
+ printf("Error: %s", pMsg);
+ break;
+ default:
+ printf("%s", pMsg);
+ break;
+ }
+ }
+ else
+ {
+ printf("%s", pMsg);
+ }
+
+ if (console::get_crlf())
+ printf("\n");
+
+ return true;
+ }
+#endif
+
+} // namespace crnlib
+
diff --git a/third_party/crunch/crnlib/crn_colorized_console.h b/third_party/crunch/crnlib/crn_colorized_console.h
new file mode 100644
index 000000000..47e2573ff
--- /dev/null
+++ b/third_party/crunch/crnlib/crn_colorized_console.h
@@ -0,0 +1,19 @@
+// File: crn_colorized_console.h
+// See Copyright Notice and license at the end of inc/crnlib.h
+#pragma once
+#include "crn_console.h"
+
+namespace crnlib
+{
+ class colorized_console
+ {
+ public:
+ static void init();
+ static void deinit();
+ static void tick();
+
+ private:
+ static bool console_output_func(eConsoleMessageType type, const char* pMsg, void* pData);
+ };
+
+} // namespace crnlib
diff --git a/third_party/crunch/crnlib/crn_command_line_params.cpp b/third_party/crunch/crnlib/crn_command_line_params.cpp
new file mode 100644
index 000000000..a6094c2fa
--- /dev/null
+++ b/third_party/crunch/crnlib/crn_command_line_params.cpp
@@ -0,0 +1,472 @@
+// File: crn_command_line_params.cpp
+// See Copyright Notice and license at the end of inc/crnlib.h
+#include "crn_core.h"
+#include "crn_command_line_params.h"
+#include "crn_console.h"
+#include "crn_cfile_stream.h"
+
+#ifdef WIN32
+ #define CRNLIB_CMD_LINE_ALLOW_SLASH_PARAMS 1
+#endif
+
+#if CRNLIB_USE_WIN32_API
+#include "crn_winhdr.h"
+#endif
+namespace crnlib
+{
+ void get_command_line_as_single_string(dynamic_string& cmd_line, int argc, char *argv[])
+ {
+ argc, argv;
+#if CRNLIB_USE_WIN32_API
+ cmd_line.set(GetCommandLineA());
+#else
+ cmd_line.clear();
+ for (int i = 0; i < argc; i++)
+ {
+ dynamic_string tmp(argv[i]);
+ if ((tmp.front() != '"') && (tmp.front() != '-') && (tmp.front() != '@'))
+ tmp = "\"" + tmp + "\"";
+ if (cmd_line.get_len())
+ cmd_line += " ";
+ cmd_line += tmp;
+ }
+#endif
+ }
+
+ command_line_params::command_line_params()
+ {
+ }
+
+ void command_line_params::clear()
+ {
+ m_params.clear();
+
+ m_param_map.clear();
+ }
+
+ bool command_line_params::split_params(const char* p, dynamic_string_array& params)
+ {
+ bool within_param = false;
+ bool within_quote = false;
+
+ uint ofs = 0;
+ dynamic_string str;
+
+ while (p[ofs])
+ {
+ const char c = p[ofs];
+
+ if (within_param)
+ {
+ if (within_quote)
+ {
+ if (c == '"')
+ within_quote = false;
+
+ str.append_char(c);
+ }
+ else if ((c == ' ') || (c == '\t'))
+ {
+ if (!str.is_empty())
+ {
+ params.push_back(str);
+ str.clear();
+ }
+ within_param = false;
+ }
+ else
+ {
+ if (c == '"')
+ within_quote = true;
+
+ str.append_char(c);
+ }
+ }
+ else if ((c != ' ') && (c != '\t'))
+ {
+ within_param = true;
+
+ if (c == '"')
+ within_quote = true;
+
+ str.append_char(c);
+ }
+
+ ofs++;
+ }
+
+ if (within_quote)
+ {
+ console::error("Unmatched quote in command line \"%s\"", p);
+ return false;
+ }
+
+ if (!str.is_empty())
+ params.push_back(str);
+
+ return true;
+ }
+
+ bool command_line_params::load_string_file(const char* pFilename, dynamic_string_array& strings)
+ {
+ cfile_stream in_stream;
+ if (!in_stream.open(pFilename, cDataStreamReadable | cDataStreamSeekable))
+ {
+ console::error("Unable to open file \"%s\" for reading!", pFilename);
+ return false;
+ }
+
+ dynamic_string ansi_str;
+
+ for ( ; ; )
+ {
+ if (!in_stream.read_line(ansi_str))
+ break;
+
+ ansi_str.trim();
+ if (ansi_str.is_empty())
+ continue;
+
+ strings.push_back(dynamic_string(ansi_str.get_ptr()));
+ }
+
+ return true;
+ }
+
+ bool command_line_params::parse(const dynamic_string_array& params, uint n, const param_desc* pParam_desc)
+ {
+ CRNLIB_ASSERT(n && pParam_desc);
+
+ m_params = params;
+
+ uint arg_index = 0;
+ while (arg_index < params.size())
+ {
+ const uint cur_arg_index = arg_index;
+ const dynamic_string& src_param = params[arg_index++];
+
+ if (src_param.is_empty())
+ continue;
+#if CRNLIB_CMD_LINE_ALLOW_SLASH_PARAMS
+ if ((src_param[0] == '/') || (src_param[0] == '-'))
+#else
+ if (src_param[0] == '-')
+#endif
+ {
+ if (src_param.get_len() < 2)
+ {
+ console::error("Invalid command line parameter: \"%s\"", src_param.get_ptr());
+ return false;
+ }
+
+ dynamic_string key_str(src_param);
+
+ key_str.right(1);
+
+ int modifier = 0;
+ char c = key_str[key_str.get_len() - 1];
+ if (c == '+')
+ modifier = 1;
+ else if (c == '-')
+ modifier = -1;
+
+ if (modifier)
+ key_str.left(key_str.get_len() - 1);
+
+ uint param_index;
+ for (param_index = 0; param_index < n; param_index++)
+ if (key_str == pParam_desc[param_index].m_pName)
+ break;
+
+ if (param_index == n)
+ {
+ console::error("Unrecognized command line parameter: \"%s\"", src_param.get_ptr());
+ return false;
+ }
+
+ const param_desc& desc = pParam_desc[param_index];
+
+ const uint cMaxValues = 16;
+ dynamic_string val_str[cMaxValues];
+ uint num_val_strs = 0;
+ if (desc.m_num_values)
+ {
+ CRNLIB_ASSERT(desc.m_num_values <= cMaxValues);
+
+ if ((arg_index + desc.m_num_values) > params.size())
+ {
+ console::error("Expected %u value(s) after command line parameter: \"%s\"", desc.m_num_values, src_param.get_ptr());
+ return false;
+ }
+
+ for (uint v = 0; v < desc.m_num_values; v++)
+ val_str[num_val_strs++] = params[arg_index++];
+ }
+
+ dynamic_string_array strings;
+
+ if ((desc.m_support_listing_file) && (val_str[0].get_len() >= 2) && (val_str[0][0] == '@'))
+ {
+ dynamic_string filename(val_str[0]);
+ filename.right(1);
+ filename.unquote();
+
+ if (!load_string_file(filename.get_ptr(), strings))
+ {
+ console::error("Failed loading listing file \"%s\"!", filename.get_ptr());
+ return false;
+ }
+ }
+ else
+ {
+ for (uint v = 0; v < num_val_strs; v++)
+ {
+ val_str[v].unquote();
+ strings.push_back(val_str[v]);
+ }
+ }
+
+ param_value pv;
+ pv.m_values.swap(strings);
+ pv.m_index = cur_arg_index;
+ pv.m_modifier = (int8)modifier;
+ m_param_map.insert(std::make_pair(key_str, pv));
+ }
+ else
+ {
+ param_value pv;
+ pv.m_values.push_back(src_param);
+ pv.m_values.back().unquote();
+ pv.m_index = cur_arg_index;
+ m_param_map.insert(std::make_pair(g_empty_dynamic_string, pv));
+ }
+ }
+
+ return true;
+ }
+
+ bool command_line_params::parse(const char* pCmd_line, uint n, const param_desc* pParam_desc, bool skip_first_param)
+ {
+ CRNLIB_ASSERT(n && pParam_desc);
+
+ dynamic_string_array p;
+ if (!split_params(pCmd_line, p))
+ return 0;
+
+ if (p.empty())
+ return 0;
+
+ if (skip_first_param)
+ p.erase(0U);
+
+ return parse(p, n, pParam_desc);
+ }
+
+ bool command_line_params::is_param(uint index) const
+ {
+ CRNLIB_ASSERT(index < m_params.size());
+ if (index >= m_params.size())
+ return false;
+
+ const dynamic_string& w = m_params[index];
+ if (w.is_empty())
+ return false;
+
+#if CRNLIB_CMD_LINE_ALLOW_SLASH_PARAMS
+ return (w.get_len() >= 2) && ((w[0] == '-') || (w[0] == '/'));
+#else
+ return (w.get_len() >= 2) && (w[0] == '-');
+#endif
+ }
+
+ uint command_line_params::find(uint num_keys, const char** ppKeys, crnlib::vector* pIterators, crnlib::vector* pUnmatched_indices) const
+ {
+ CRNLIB_ASSERT(ppKeys);
+
+ if (pUnmatched_indices)
+ {
+ pUnmatched_indices->resize(m_params.size());
+ for (uint i = 0; i < m_params.size(); i++)
+ (*pUnmatched_indices)[i] = i;
+ }
+
+ uint n = 0;
+ for (uint i = 0; i < num_keys; i++)
+ {
+ const char* pKey = ppKeys[i];
+
+ param_map_const_iterator begin, end;
+ find(pKey, begin, end);
+
+ while (begin != end)
+ {
+ if (pIterators)
+ pIterators->push_back(begin);
+
+ if (pUnmatched_indices)
+ {
+ int k = pUnmatched_indices->find(begin->second.m_index);
+ if (k >= 0)
+ pUnmatched_indices->erase_unordered(k);
+ }
+
+ n++;
+ begin++;
+ }
+ }
+
+ return n;
+ }
+
+ void command_line_params::find(const char* pKey, param_map_const_iterator& begin, param_map_const_iterator& end) const
+ {
+ dynamic_string key(pKey);
+ begin = m_param_map.lower_bound(key);
+ end = m_param_map.upper_bound(key);
+ }
+
+ uint command_line_params::get_count(const char* pKey) const
+ {
+ param_map_const_iterator begin, end;
+ find(pKey, begin, end);
+
+ uint n = 0;
+
+ while (begin != end)
+ {
+ n++;
+ begin++;
+ }
+
+ return n;
+ }
+
+ command_line_params::param_map_const_iterator command_line_params::get_param(const char* pKey, uint index) const
+ {
+ param_map_const_iterator begin, end;
+ find(pKey, begin, end);
+
+ if (begin == end)
+ return m_param_map.end();
+
+ uint n = 0;
+
+ while ((begin != end) && (n != index))
+ {
+ n++;
+ begin++;
+ }
+
+ if (begin == end)
+ return m_param_map.end();
+
+ return begin;
+ }
+
+ bool command_line_params::has_value(const char* pKey, uint index) const
+ {
+ return get_num_values(pKey, index) != 0;
+ }
+
+ uint command_line_params::get_num_values(const char* pKey, uint index) const
+ {
+ param_map_const_iterator it = get_param(pKey, index);
+
+ if (it == end())
+ return 0;
+
+ return it->second.m_values.size();
+ }
+
+ bool command_line_params::get_value_as_bool(const char* pKey, uint index, bool def) const
+ {
+ param_map_const_iterator it = get_param(pKey, index);
+ if (it == end())
+ return def;
+
+ if (it->second.m_modifier)
+ return it->second.m_modifier > 0;
+ else
+ return true;
+ }
+
+ int command_line_params::get_value_as_int(const char* pKey, uint index, int def, int l, int h, uint value_index) const
+ {
+ param_map_const_iterator it = get_param(pKey, index);
+ if ((it == end()) || (value_index >= it->second.m_values.size()))
+ return def;
+
+ int val;
+ const char* p = it->second.m_values[value_index].get_ptr();
+ if (!string_to_int(p, val))
+ {
+ crnlib::console::warning("Invalid value specified for parameter \"%s\", using default value of %i", pKey, def);
+ return def;
+ }
+
+ if (val < l)
+ {
+ crnlib::console::warning("Value %i for parameter \"%s\" is out of range, clamping to %i", val, pKey, l);
+ val = l;
+ }
+ else if (val > h)
+ {
+ crnlib::console::warning("Value %i for parameter \"%s\" is out of range, clamping to %i", val, pKey, h);
+ val = h;
+ }
+
+ return val;
+ }
+
+ float command_line_params::get_value_as_float(const char* pKey, uint index, float def, float l, float h, uint value_index) const
+ {
+ param_map_const_iterator it = get_param(pKey, index);
+ if ((it == end()) || (value_index >= it->second.m_values.size()))
+ return def;
+
+ float val;
+ const char* p = it->second.m_values[value_index].get_ptr();
+ if (!string_to_float(p, val))
+ {
+ crnlib::console::warning("Invalid value specified for float parameter \"%s\", using default value of %f", pKey, def);
+ return def;
+ }
+
+ if (val < l)
+ {
+ crnlib::console::warning("Value %f for parameter \"%s\" is out of range, clamping to %f", val, pKey, l);
+ val = l;
+ }
+ else if (val > h)
+ {
+ crnlib::console::warning("Value %f for parameter \"%s\" is out of range, clamping to %f", val, pKey, h);
+ val = h;
+ }
+
+ return val;
+ }
+
+ bool command_line_params::get_value_as_string(const char* pKey, uint index, dynamic_string& value, uint value_index) const
+ {
+ param_map_const_iterator it = get_param(pKey, index);
+ if ((it == end()) || (value_index >= it->second.m_values.size()))
+ {
+ value.empty();
+ return false;
+ }
+
+ value = it->second.m_values[value_index];
+ return true;
+ }
+
+ const dynamic_string& command_line_params::get_value_as_string_or_empty(const char* pKey, uint index, uint value_index) const
+ {
+ param_map_const_iterator it = get_param(pKey, index);
+ if ((it == end()) || (value_index >= it->second.m_values.size()))
+ return g_empty_dynamic_string;
+
+ return it->second.m_values[value_index];
+ }
+
+} // namespace crnlib
+
diff --git a/third_party/crunch/crnlib/crn_command_line_params.h b/third_party/crunch/crnlib/crn_command_line_params.h
new file mode 100644
index 000000000..35b426931
--- /dev/null
+++ b/third_party/crunch/crnlib/crn_command_line_params.h
@@ -0,0 +1,86 @@
+// File: crn_command_line_params.h
+// See Copyright Notice and license at the end of inc/crnlib.h
+#pragma once
+#include "crn_value.h"
+#include