This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
xcopy /y /s E:\crunch17\*.vcproj .
|
||||
xcopy /y /s E:\crunch17\*.sln .
|
||||
xcopy /y /s E:\crunch17\*.workspace .
|
||||
xcopy /y /s E:\crunch17\*.cbp .
|
||||
xcopy /y /s E:\crunch17\*.cpp .
|
||||
xcopy /y /s E:\crunch17\*.c .
|
||||
xcopy /y /s E:\crunch17\*.inc .
|
||||
xcopy /y /s E:\crunch17\*.h .
|
||||
xcopy /y /s E:\crunch17\*.txt .
|
||||
@@ -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
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
<CodeBlocks_workspace_file>
|
||||
<Workspace title="Workspace">
|
||||
<Project filename="crunch/crunch.cbp" active="1">
|
||||
<Depends filename="crnlib/crnlib.cbp" />
|
||||
</Project>
|
||||
<Project filename="crnlib/crnlib.cbp" />
|
||||
</Workspace>
|
||||
</CodeBlocks_workspace_file>
|
||||
@@ -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
|
||||
@@ -0,0 +1,699 @@
|
||||
// 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"
|
||||
#include <stdio.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
|
||||
@@ -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
|
||||
@@ -0,0 +1,77 @@
|
||||
// File: crn_assert.cpp
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#include "crn_core.h"
|
||||
#include "crn_winhdr.h"
|
||||
#include <stdio.h>
|
||||
|
||||
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];
|
||||
|
||||
#if defined(WIN32) && defined(_MSC_VER)
|
||||
sprintf_s(buf, sizeof(buf), "%s(%u): Assertion failed: \"%s\"\n", pFile, line, pExp);
|
||||
#else
|
||||
sprintf(buf, "%s(%u): Assertion failed: \"%s\"\n", pFile, line, pExp);
|
||||
#endif
|
||||
|
||||
crnlib_output_debug_string(buf);
|
||||
|
||||
printf(buf);
|
||||
|
||||
if (crnlib_is_debugger_present())
|
||||
crnlib_debug_break();
|
||||
}
|
||||
|
||||
void crnlib_fail(const char* pExp, const char* pFile, unsigned line)
|
||||
{
|
||||
char buf[512];
|
||||
|
||||
#if defined(WIN32) && defined(_MSC_VER)
|
||||
sprintf_s(buf, sizeof(buf), "%s(%u): Failure: \"%s\"\n", pFile, line, pExp);
|
||||
#else
|
||||
sprintf(buf, "%s(%u): Failure: \"%s\"\n", pFile, line, pExp);
|
||||
#endif
|
||||
|
||||
crnlib_output_debug_string(buf);
|
||||
|
||||
printf(buf);
|
||||
|
||||
if (crnlib_is_debugger_present())
|
||||
crnlib_debug_break();
|
||||
|
||||
if (g_fail_exceptions)
|
||||
RaiseException(CRNLIB_FAIL_EXCEPTION_CODE, 0, 0, NULL);
|
||||
else if (g_exit_on_failure)
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
void trace(const char* pFmt, va_list args)
|
||||
{
|
||||
if (crnlib_is_debugger_present())
|
||||
{
|
||||
char buf[512];
|
||||
#if defined(WIN32) && defined(_MSC_VER)
|
||||
vsprintf_s(buf, sizeof(buf), pFmt, args);
|
||||
#else
|
||||
vsprintf(buf, pFmt, args);
|
||||
#endif
|
||||
|
||||
crnlib_output_debug_string(buf);
|
||||
}
|
||||
};
|
||||
|
||||
void trace(const char* pFmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, pFmt);
|
||||
trace(pFmt, args);
|
||||
va_end(args);
|
||||
};
|
||||
@@ -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 <bool x> struct crnlib_assume_failure;
|
||||
template <> struct crnlib_assume_failure<true> { enum { blah = 1 }; };
|
||||
template<int x> 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<typename T> inline T crnlib_assert_range(T i, T m)
|
||||
{
|
||||
m;
|
||||
return i;
|
||||
}
|
||||
template<typename T> inline T crnlib_assert_range_incl(T i, T m)
|
||||
{
|
||||
m;
|
||||
return i;
|
||||
}
|
||||
#else
|
||||
template<typename T> inline T crnlib_assert_range(T i, T m)
|
||||
{
|
||||
CRNLIB_ASSERT((i >= 0) && (i < m));
|
||||
return i;
|
||||
}
|
||||
template<typename T> inline T crnlib_assert_range_incl(T i, T m)
|
||||
{
|
||||
CRNLIB_ASSERT((i >= 0) && (i <= m));
|
||||
return i;
|
||||
}
|
||||
#endif
|
||||
@@ -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<uint8*>(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<uint>(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<uint>(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<uint>(new_ofs);
|
||||
|
||||
post_seek();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
uint8* m_pBuf;
|
||||
uint m_size;
|
||||
uint m_ofs;
|
||||
};
|
||||
|
||||
} // namespace crnlib
|
||||
|
||||
@@ -0,0 +1,246 @@
|
||||
// File: crn_cfile_stream.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
#include "crn_data_stream.h"
|
||||
#include <stdio.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 wchar_t* 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 wchar_t* 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 wchar_t* 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<uint16>(attribs);
|
||||
|
||||
m_ofs = _ftelli64(m_pFile);
|
||||
_fseeki64(m_pFile, 0, SEEK_END);
|
||||
m_size = _ftelli64(m_pFile);
|
||||
_fseeki64(m_pFile, m_ofs, SEEK_SET);
|
||||
|
||||
m_opened = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool open(const wchar_t* pFilename, uint attribs = cDataStreamReadable | cDataStreamSeekable, bool open_existing = false)
|
||||
{
|
||||
CRNLIB_ASSERT(pFilename);
|
||||
|
||||
close();
|
||||
|
||||
m_attribs = static_cast<uint16>(attribs);
|
||||
|
||||
const wchar_t* pMode;
|
||||
if ((is_readable()) && (is_writable()))
|
||||
pMode = open_existing ? L"r+b" : L"w+b";
|
||||
else if (is_writable())
|
||||
pMode = open_existing ? L"ab" : L"wb";
|
||||
else if (is_readable())
|
||||
pMode = L"rb";
|
||||
else
|
||||
{
|
||||
set_error();
|
||||
return false;
|
||||
}
|
||||
|
||||
FILE* pFile = NULL;
|
||||
#ifdef _MSC_VER
|
||||
_wfopen_s(&pFile, pFilename, pMode);
|
||||
#else
|
||||
pFile = _wfopen(pFilename, pMode);
|
||||
#endif
|
||||
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<uint>(math::minimum<uint64>(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<uint64>(new_ofs) > m_size)
|
||||
return false;
|
||||
|
||||
if (static_cast<uint64>(new_ofs) != m_ofs)
|
||||
{
|
||||
if (_fseeki64(m_pFile, new_ofs, SEEK_SET) != 0)
|
||||
{
|
||||
set_error();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_ofs = new_ofs;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool read_file_into_array(const wchar_t* pFilename, vector<uint8>& 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 wchar_t* pFilename, const vector<uint8>& 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
|
||||
@@ -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<const uint8*>(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<const uint8*>(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<uint16>(~crc);
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
|
||||
@@ -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
|
||||
@@ -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<typename VectorType>
|
||||
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; }
|
||||
const uint get_training_vec_weight(uint index) const { return m_training_vecs[index].second; }
|
||||
|
||||
typedef crnlib::vector< std::pair<VectorType, uint> > 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<VectorType> vector_vec_type;
|
||||
inline const vector_vec_type& get_codebook() const
|
||||
{
|
||||
return m_codebook;
|
||||
}
|
||||
|
||||
const 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;
|
||||
}
|
||||
}
|
||||
|
||||
const 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<uint> >& clusters) const
|
||||
{
|
||||
clusters.resize(0);
|
||||
clusters.reserve(max_clusters);
|
||||
|
||||
crnlib::vector<uint> 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<uint> 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<vq_node> node_vec_type;
|
||||
|
||||
node_vec_type m_nodes;
|
||||
|
||||
vector_vec_type m_codebook;
|
||||
|
||||
float m_overall_variance;
|
||||
|
||||
uint m_split_index;
|
||||
|
||||
crnlib::vector<uint> 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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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<N, N, float> 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<int>(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<float>(sum);
|
||||
|
||||
max_sum = math::maximum(max_sum, fabs(sum));
|
||||
}
|
||||
|
||||
if (max_sum != 0.0f)
|
||||
x *= static_cast<float>(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<float>(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<uint> m_left_children;
|
||||
crnlib::vector<uint> 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
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,694 @@
|
||||
// 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<typename component_type> struct color_quad_component_traits
|
||||
{
|
||||
enum
|
||||
{
|
||||
cSigned = false,
|
||||
cFloat = false,
|
||||
cMin = UINT8_MIN,
|
||||
cMax = UINT8_MAX
|
||||
};
|
||||
};
|
||||
|
||||
template<> struct color_quad_component_traits<int16>
|
||||
{
|
||||
enum
|
||||
{
|
||||
cSigned = true,
|
||||
cFloat = false,
|
||||
cMin = INT16_MIN,
|
||||
cMax = INT16_MAX
|
||||
};
|
||||
};
|
||||
|
||||
template<> struct color_quad_component_traits<uint16>
|
||||
{
|
||||
enum
|
||||
{
|
||||
cSigned = false,
|
||||
cFloat = false,
|
||||
cMin = UINT16_MIN,
|
||||
cMax = UINT16_MAX
|
||||
};
|
||||
};
|
||||
|
||||
template<> struct color_quad_component_traits<int32>
|
||||
{
|
||||
enum
|
||||
{
|
||||
cSigned = true,
|
||||
cFloat = false,
|
||||
cMin = INT32_MIN,
|
||||
cMax = INT32_MAX
|
||||
};
|
||||
};
|
||||
|
||||
template<> struct color_quad_component_traits<uint32>
|
||||
{
|
||||
enum
|
||||
{
|
||||
cSigned = false,
|
||||
cFloat = false,
|
||||
cMin = UINT32_MIN,
|
||||
cMax = UINT32_MAX
|
||||
};
|
||||
};
|
||||
|
||||
template<> struct color_quad_component_traits<float>
|
||||
{
|
||||
enum
|
||||
{
|
||||
cSigned = false,
|
||||
cFloat = true,
|
||||
cMin = INT32_MIN,
|
||||
cMax = INT32_MAX
|
||||
};
|
||||
};
|
||||
|
||||
template<> struct color_quad_component_traits<double>
|
||||
{
|
||||
enum
|
||||
{
|
||||
cSigned = false,
|
||||
cFloat = true,
|
||||
cMin = INT32_MIN,
|
||||
cMax = INT32_MAX
|
||||
};
|
||||
};
|
||||
|
||||
template<typename component_type, typename parameter_type>
|
||||
class color_quad : public helpers::rel_ops<color_quad<component_type, parameter_type> >
|
||||
{
|
||||
template<typename T>
|
||||
static inline T clamp(T v)
|
||||
{
|
||||
if (!component_traits::cFloat)
|
||||
{
|
||||
if (v < component_traits::cMin)
|
||||
v = component_traits::cMin;
|
||||
else if (v > component_traits::cMax)
|
||||
v = component_traits::cMax;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
template<>
|
||||
static inline int 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<int>(v) >> 31)) & 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (v < component_traits::cMin)
|
||||
v = component_traits::cMin;
|
||||
else if (v > component_traits::cMax)
|
||||
v = component_traits::cMax;
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
#endif
|
||||
|
||||
public:
|
||||
typedef component_type component_t;
|
||||
typedef parameter_type parameter_t;
|
||||
typedef color_quad_component_traits<component_type> 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<typename other_component_type, typename other_parameter_type>
|
||||
inline color_quad(const color_quad<other_component_type, other_parameter_type>& other) :
|
||||
r(clamp(other.r)), g(clamp(other.g)), b(clamp(other.b)), a(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;
|
||||
}
|
||||
|
||||
template<typename other_component_type, typename other_parameter_type>
|
||||
inline color_quad& operator=(const color_quad<other_component_type, other_parameter_type>& other)
|
||||
{
|
||||
r = clamp(other.r);
|
||||
g = clamp(other.g);
|
||||
b = clamp(other.b);
|
||||
a = 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<component_type>(y);
|
||||
g = static_cast<component_type>(y);
|
||||
b = static_cast<component_type>(y);
|
||||
a = static_cast<component_type>(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<component_type>(y);
|
||||
g = static_cast<component_type>(y);
|
||||
b = static_cast<component_type>(y);
|
||||
a = static_cast<component_type>(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<component_type>(clamp(red));
|
||||
g = static_cast<component_type>(clamp(green));
|
||||
b = static_cast<component_type>(clamp(blue));
|
||||
a = static_cast<component_type>(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<component_type>(red);
|
||||
g = static_cast<component_type>(green);
|
||||
b = static_cast<component_type>(blue);
|
||||
a = static_cast<component_type>(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<component_type>(red);
|
||||
g = static_cast<component_type>(green);
|
||||
b = static_cast<component_type>(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<component_type>(clamp(f));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline color_quad& set_grayscale(parameter_t l)
|
||||
{
|
||||
component_t x = static_cast<component_t>(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<component_type>(math::clamp<parameter_type>(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<component_type>(math::clamp<parameter_type>(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<parameter_type>((19595U * r + 38470U * g + 7471U * b + 32768U) >> 16U);
|
||||
}
|
||||
|
||||
// Returns REC 709 luma.
|
||||
inline parameter_type get_luma_rec709() const
|
||||
{
|
||||
return static_cast<parameter_type>((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<const uint32*>(this);
|
||||
}
|
||||
|
||||
// Beware of endianness!
|
||||
inline uint64 get_uint64() const
|
||||
{
|
||||
CRNLIB_ASSERT(sizeof(*this) == sizeof(uint64));
|
||||
return *reinterpret_cast<const uint64*>(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<component_type>(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<component_type>(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<component_type>(clamp(c[i] * v));
|
||||
return *this;
|
||||
}
|
||||
|
||||
color_quad& operator/= (parameter_type v)
|
||||
{
|
||||
for (uint i = 0; i < 4; i++)
|
||||
c[i] = static_cast<component_type>(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<component_type>(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<component_type>(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<typename c, typename q>
|
||||
struct scalar_type< color_quad<c, q> >
|
||||
{
|
||||
enum { cFlag = true };
|
||||
static inline void construct(color_quad<c, q>* p) { }
|
||||
static inline void construct(color_quad<c, q>* p, const color_quad<c, q>& init) { memcpy(p, &init, sizeof(color_quad<c, q>)); }
|
||||
static inline void construct_array(color_quad<c, q>* p, uint n) { p, n; }
|
||||
static inline void destruct(color_quad<c, q>* p) { p; }
|
||||
static inline void destruct_array(color_quad<c, q>* p, uint n) { p, n; }
|
||||
};
|
||||
|
||||
typedef color_quad<uint8, int> color_quad_u8;
|
||||
typedef color_quad<int16, int> color_quad_i16;
|
||||
typedef color_quad<uint16, int> color_quad_u16;
|
||||
typedef color_quad<int32, int> color_quad_i32;
|
||||
typedef color_quad<uint32, uint> color_quad_u32;
|
||||
typedef color_quad<float, float> color_quad_f;
|
||||
typedef color_quad<double, double> 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<uint>(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<uint>(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<uint>((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<uint>((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<uint>(labs(e1[0] - e2[0]), labs(e1[1] - e2[1]), labs(e1[2] - e2[2]));
|
||||
//return math::square<int>(e1[0] - e2[0]) + math::square<int>(e1[1] - e2[1]) + math::square<int>(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<uint>(i) > 255U) { if (i < 0) i = 0; else if (i > 255) i = 255; } return static_cast<uint8>(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<uint8>((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<int>(.5f + y + F_R_CR * cr));
|
||||
rgb.g = color::clamp_component(static_cast<int>(.5f + y + F_G_CR * cr + F_G_CB * cb));
|
||||
rgb.b = color::clamp_component(static_cast<int>(.5f + y + F_B_CB * cb));
|
||||
rgb.a = 255;
|
||||
}
|
||||
|
||||
} // namespace color
|
||||
|
||||
} // namespace crnlib
|
||||
|
||||
@@ -0,0 +1,439 @@
|
||||
// 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"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
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 wchar_t* p, dynamic_wstring_array& params)
|
||||
{
|
||||
bool within_param = false;
|
||||
bool within_quote = false;
|
||||
|
||||
uint ofs = 0;
|
||||
dynamic_wstring str;
|
||||
|
||||
while (p[ofs])
|
||||
{
|
||||
const wchar_t c = p[ofs];
|
||||
|
||||
if (within_param)
|
||||
{
|
||||
if (within_quote)
|
||||
{
|
||||
if (c == L'"')
|
||||
within_quote = false;
|
||||
|
||||
str.append_char(c);
|
||||
}
|
||||
else if ((c == L' ') || (c == L'\t'))
|
||||
{
|
||||
if (!str.is_empty())
|
||||
{
|
||||
params.push_back(str);
|
||||
str.clear();
|
||||
}
|
||||
within_param = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c == L'"')
|
||||
within_quote = true;
|
||||
|
||||
str.append_char(c);
|
||||
}
|
||||
}
|
||||
else if ((c != L' ') && (c != L'\t'))
|
||||
{
|
||||
within_param = true;
|
||||
|
||||
if (c == L'"')
|
||||
within_quote = true;
|
||||
|
||||
str.append_char(c);
|
||||
}
|
||||
|
||||
ofs++;
|
||||
}
|
||||
|
||||
if (within_quote)
|
||||
{
|
||||
console::error(L"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 wchar_t* pFilename, dynamic_wstring_array& strings)
|
||||
{
|
||||
cfile_stream in_stream;
|
||||
if (!in_stream.open(pFilename, cDataStreamReadable | cDataStreamSeekable))
|
||||
{
|
||||
console::error(L"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_wstring(ansi_str.get_ptr()));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool command_line_params::parse(const dynamic_wstring_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_wstring& src_param = params[arg_index++];
|
||||
|
||||
if (src_param.is_empty())
|
||||
continue;
|
||||
|
||||
if ((src_param[0] == L'/') || (src_param[0] == L'-'))
|
||||
{
|
||||
if (src_param.get_len() < 2)
|
||||
{
|
||||
console::error(L"Invalid command line parameter: \"%s\"", src_param.get_ptr());
|
||||
return false;
|
||||
}
|
||||
|
||||
dynamic_wstring key_str(src_param);
|
||||
|
||||
key_str.right(1);
|
||||
|
||||
int modifier = 0;
|
||||
wchar_t c = key_str[key_str.get_len() - 1];
|
||||
if (c == L'+')
|
||||
modifier = 1;
|
||||
else if (c == L'-')
|
||||
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(L"Unrecognized command line parameter: \"%s\"", src_param.get_ptr());
|
||||
return false;
|
||||
}
|
||||
|
||||
const param_desc& desc = pParam_desc[param_index];
|
||||
|
||||
const uint cMaxValues = 16;
|
||||
dynamic_wstring 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(L"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_wstring_array strings;
|
||||
|
||||
if ((desc.m_support_listing_file) && (val_str[0].get_len() >= 2) && (val_str[0][0] == L'@'))
|
||||
{
|
||||
dynamic_wstring filename(val_str[0]);
|
||||
filename.right(1);
|
||||
filename.unquote();
|
||||
|
||||
if (!load_string_file(filename.get_ptr(), strings))
|
||||
{
|
||||
console::error(L"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_wstring, pv));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool command_line_params::parse(const wchar_t* pCmd_line, uint n, const param_desc* pParam_desc, bool skip_first_param)
|
||||
{
|
||||
CRNLIB_ASSERT(n && pParam_desc);
|
||||
|
||||
dynamic_wstring_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_wstring& w = m_params[index];
|
||||
if (w.is_empty())
|
||||
return false;
|
||||
|
||||
return (w.get_len() >= 2) && ((w[0] == L'-') || (w[0] == L'/'));
|
||||
}
|
||||
|
||||
uint command_line_params::find(uint num_keys, const wchar_t** ppKeys, crnlib::vector<param_map_const_iterator>* pIterators, crnlib::vector<uint>* 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 wchar_t* 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 wchar_t* pKey, param_map_const_iterator& begin, param_map_const_iterator& end) const
|
||||
{
|
||||
dynamic_wstring key(pKey);
|
||||
begin = m_param_map.lower_bound(key);
|
||||
end = m_param_map.upper_bound(key);
|
||||
}
|
||||
|
||||
uint command_line_params::get_count(const wchar_t* 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 wchar_t* 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 wchar_t* pKey, uint index) const
|
||||
{
|
||||
return get_num_values(pKey, index) != 0;
|
||||
}
|
||||
|
||||
uint command_line_params::get_num_values(const wchar_t* 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 wchar_t* 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 wchar_t* 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 wchar_t* p = it->second.m_values[value_index].get_ptr();
|
||||
if (!string_to_int(p, val))
|
||||
{
|
||||
crnlib::console::warning(L"Invalid value specified for parameter \"%s\", using default value of %i", pKey, def);
|
||||
return def;
|
||||
}
|
||||
|
||||
if (val < l)
|
||||
{
|
||||
crnlib::console::warning(L"Value %i for parameter \"%s\" is out of range, clamping to %i", val, pKey, l);
|
||||
val = l;
|
||||
}
|
||||
else if (val > h)
|
||||
{
|
||||
crnlib::console::warning(L"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 wchar_t* 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 wchar_t* p = it->second.m_values[value_index].get_ptr();
|
||||
if (!string_to_float(p, val))
|
||||
{
|
||||
crnlib::console::warning(L"Invalid value specified for float parameter \"%s\", using default value of %f", pKey, def);
|
||||
return def;
|
||||
}
|
||||
|
||||
if (val < l)
|
||||
{
|
||||
crnlib::console::warning(L"Value %f for parameter \"%s\" is out of range, clamping to %f", val, pKey, l);
|
||||
val = l;
|
||||
}
|
||||
else if (val > h)
|
||||
{
|
||||
crnlib::console::warning(L"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 wchar_t* pKey, uint index, dynamic_wstring& 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_wstring& command_line_params::get_value_as_string_or_empty(const wchar_t* 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_wstring;
|
||||
|
||||
return it->second.m_values[value_index];
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
// 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 <map>
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
class command_line_params
|
||||
{
|
||||
public:
|
||||
struct param_value
|
||||
{
|
||||
param_value() : m_index(0), m_modifier(0) { }
|
||||
|
||||
dynamic_wstring_array m_values;
|
||||
uint m_index;
|
||||
int8 m_modifier;
|
||||
};
|
||||
|
||||
typedef std::multimap<dynamic_wstring, param_value> param_map;
|
||||
typedef param_map::const_iterator param_map_const_iterator;
|
||||
typedef param_map::iterator param_map_iterator;
|
||||
|
||||
command_line_params();
|
||||
|
||||
void clear();
|
||||
|
||||
static bool split_params(const wchar_t* p, dynamic_wstring_array& params);
|
||||
|
||||
struct param_desc
|
||||
{
|
||||
const wchar_t* m_pName;
|
||||
uint m_num_values;
|
||||
bool m_support_listing_file;
|
||||
};
|
||||
|
||||
bool parse(const dynamic_wstring_array& params, uint n, const param_desc* pParam_desc);
|
||||
bool parse(const wchar_t* pCmd_line, uint n, const param_desc* pParam_desc, bool skip_first_param = true);
|
||||
|
||||
const dynamic_wstring_array& get_array() const { return m_params; }
|
||||
|
||||
bool is_param(uint index) const;
|
||||
|
||||
const param_map& get_map() const { return m_param_map; }
|
||||
|
||||
uint get_num_params() const { return static_cast<uint>(m_param_map.size()); }
|
||||
|
||||
param_map_const_iterator begin() const { return m_param_map.begin(); }
|
||||
param_map_const_iterator end() const { return m_param_map.end(); }
|
||||
|
||||
uint find(uint num_keys, const wchar_t** ppKeys, crnlib::vector<param_map_const_iterator>* pIterators, crnlib::vector<uint>* pUnmatched_indices) const;
|
||||
|
||||
void find(const wchar_t* pKey, param_map_const_iterator& begin, param_map_const_iterator& end) const;
|
||||
|
||||
uint get_count(const wchar_t* pKey) const;
|
||||
|
||||
// Returns end() if param cannot be found, or index is out of range.
|
||||
param_map_const_iterator get_param(const wchar_t* pKey, uint index) const;
|
||||
|
||||
bool has_key(const wchar_t* pKey) const { return get_param(pKey, 0) != end(); }
|
||||
|
||||
bool has_value(const wchar_t* pKey, uint index) const;
|
||||
uint get_num_values(const wchar_t* pKey, uint index) const;
|
||||
|
||||
bool get_value_as_bool(const wchar_t* pKey, uint index = 0, bool def = false) const;
|
||||
|
||||
int get_value_as_int(const wchar_t* pKey, uint index, int def, int l = INT_MIN, int h = INT_MAX, uint value_index = 0) const;
|
||||
float get_value_as_float(const wchar_t* pKey, uint index, float def = 0.0f, float l = -math::cNearlyInfinite, float h = math::cNearlyInfinite, uint value_index = 0) const;
|
||||
|
||||
bool get_value_as_string(const wchar_t* pKey, uint index, dynamic_wstring& value, uint value_index = 0) const;
|
||||
const dynamic_wstring& get_value_as_string_or_empty(const wchar_t* pKey, uint index = 0, uint value_index = 0) const;
|
||||
|
||||
private:
|
||||
dynamic_wstring_array m_params;
|
||||
|
||||
param_map m_param_map;
|
||||
|
||||
static bool load_string_file(const wchar_t* pFilename, dynamic_wstring_array& strings);
|
||||
};
|
||||
|
||||
} // namespace crnlib
|
||||
+2173
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,181 @@
|
||||
// File: crn_comp.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
|
||||
#define CRND_HEADER_FILE_ONLY
|
||||
#include "../inc/crn_decomp.h"
|
||||
#undef CRND_HEADER_FILE_ONLY
|
||||
|
||||
#include "../inc/crnlib.h"
|
||||
#include "crn_symbol_codec.h"
|
||||
#include "crn_dxt_hc.h"
|
||||
#include "crn_image.h"
|
||||
#include "crn_image_utils.h"
|
||||
#include "crn_texture_comp.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
class crn_comp : public itexture_comp
|
||||
{
|
||||
CRNLIB_NO_COPY_OR_ASSIGNMENT_OP(crn_comp);
|
||||
|
||||
public:
|
||||
crn_comp();
|
||||
virtual ~crn_comp();
|
||||
|
||||
virtual const wchar_t *get_ext() const { return L"CRN"; }
|
||||
|
||||
virtual bool compress_init(const crn_comp_params& params);
|
||||
virtual bool compress_pass(const crn_comp_params& params, float *pEffective_bitrate);
|
||||
virtual void compress_deinit();
|
||||
|
||||
virtual const crnlib::vector<uint8>& get_comp_data() const { return m_comp_data; }
|
||||
virtual crnlib::vector<uint8>& get_comp_data() { return m_comp_data; }
|
||||
|
||||
uint get_comp_data_size() const { return m_comp_data.size(); }
|
||||
const uint8* get_comp_data_ptr() const { return m_comp_data.size() ? &m_comp_data[0] : NULL; }
|
||||
|
||||
private:
|
||||
task_pool m_task_pool;
|
||||
const crn_comp_params* m_pParams;
|
||||
|
||||
image_u8 m_images[cCRNMaxFaces][cCRNMaxLevels];
|
||||
|
||||
struct
|
||||
{
|
||||
uint m_width, m_height;
|
||||
uint m_chunk_width, m_chunk_height;
|
||||
uint m_group_index;
|
||||
uint m_num_chunks;
|
||||
uint m_first_chunk;
|
||||
uint m_group_first_chunk;
|
||||
} m_levels[cCRNMaxLevels];
|
||||
|
||||
struct mip_group
|
||||
{
|
||||
mip_group() : m_first_chunk(0), m_num_chunks(0) { }
|
||||
|
||||
uint m_first_chunk;
|
||||
uint m_num_chunks;
|
||||
};
|
||||
crnlib::vector<mip_group> m_mip_groups;
|
||||
|
||||
enum comp
|
||||
{
|
||||
cColor,
|
||||
cAlpha0,
|
||||
cAlpha1,
|
||||
cNumComps
|
||||
};
|
||||
|
||||
bool m_has_comp[cNumComps];
|
||||
|
||||
struct chunk_detail
|
||||
{
|
||||
chunk_detail() { utils::zero_object(*this); }
|
||||
|
||||
uint m_first_endpoint_index;
|
||||
uint m_first_selector_index;
|
||||
};
|
||||
typedef crnlib::vector<chunk_detail> chunk_detail_vec;
|
||||
chunk_detail_vec m_chunk_details;
|
||||
|
||||
crnlib::vector<uint> m_endpoint_indices[cNumComps];
|
||||
crnlib::vector<uint> m_selector_indices[cNumComps];
|
||||
|
||||
uint m_total_chunks;
|
||||
dxt_hc::pixel_chunk_vec m_chunks;
|
||||
|
||||
crnd::crn_header m_crn_header;
|
||||
crnlib::vector<uint8> m_comp_data;
|
||||
|
||||
dxt_hc m_hvq;
|
||||
|
||||
symbol_histogram m_chunk_encoding_hist;
|
||||
static_huffman_data_model m_chunk_encoding_dm;
|
||||
|
||||
symbol_histogram m_endpoint_index_hist[2];
|
||||
static_huffman_data_model m_endpoint_index_dm[2]; // color, alpha
|
||||
|
||||
symbol_histogram m_selector_index_hist[2];
|
||||
static_huffman_data_model m_selector_index_dm[2]; // color, alpha
|
||||
|
||||
crnlib::vector<uint8> m_packed_chunks[cCRNMaxLevels];
|
||||
crnlib::vector<uint8> m_packed_data_models;
|
||||
crnlib::vector<uint8> m_packed_color_endpoints;
|
||||
crnlib::vector<uint8> m_packed_color_selectors;
|
||||
crnlib::vector<uint8> m_packed_alpha_endpoints;
|
||||
crnlib::vector<uint8> m_packed_alpha_selectors;
|
||||
|
||||
void clear();
|
||||
|
||||
void append_chunks(const image_u8& img, uint num_chunks_x, uint num_chunks_y, dxt_hc::pixel_chunk_vec& chunks, float weight);
|
||||
|
||||
static float color_endpoint_similarity_func(uint index_a, uint index_b, void* pContext);
|
||||
static float alpha_endpoint_similarity_func(uint index_a, uint index_b, void* pContext);
|
||||
void sort_color_endpoint_codebook(crnlib::vector<uint>& remapping, const crnlib::vector<uint>& endpoints);
|
||||
void sort_alpha_endpoint_codebook(crnlib::vector<uint>& remapping, const crnlib::vector<uint>& endpoints);
|
||||
|
||||
bool pack_color_endpoints(crnlib::vector<uint8>& data, const crnlib::vector<uint>& remapping, const crnlib::vector<uint>& endpoint_indices, uint trial_index);
|
||||
bool pack_alpha_endpoints(crnlib::vector<uint8>& data, const crnlib::vector<uint>& remapping, const crnlib::vector<uint>& endpoint_indices, uint trial_index);
|
||||
|
||||
static float color_selector_similarity_func(uint index_a, uint index_b, void* pContext);
|
||||
static float alpha_selector_similarity_func(uint index_a, uint index_b, void* pContext);
|
||||
void sort_selector_codebook(crnlib::vector<uint>& remapping, const crnlib::vector<dxt_hc::selectors>& selectors, const uint8* pTo_linear);
|
||||
|
||||
bool pack_selectors(
|
||||
crnlib::vector<uint8>& packed_data,
|
||||
const crnlib::vector<uint>& selector_indices,
|
||||
const crnlib::vector<dxt_hc::selectors>& selectors,
|
||||
const crnlib::vector<uint>& remapping,
|
||||
uint max_selector_value,
|
||||
const uint8* pTo_linear,
|
||||
uint trial_index);
|
||||
|
||||
bool alias_images();
|
||||
void create_chunks();
|
||||
bool quantize_chunks();
|
||||
void create_chunk_indices();
|
||||
|
||||
bool pack_chunks(
|
||||
uint first_chunk, uint num_chunks,
|
||||
bool clear_histograms,
|
||||
symbol_codec* pCodec,
|
||||
const crnlib::vector<uint>* pColor_endpoint_remap,
|
||||
const crnlib::vector<uint>* pColor_selector_remap,
|
||||
const crnlib::vector<uint>* pAlpha_endpoint_remap,
|
||||
const crnlib::vector<uint>* pAlpha_selector_remap);
|
||||
|
||||
bool pack_chunks_simulation(
|
||||
uint first_chunk, uint num_chunks,
|
||||
uint& total_bits,
|
||||
const crnlib::vector<uint>* pColor_endpoint_remap,
|
||||
const crnlib::vector<uint>* pColor_selector_remap,
|
||||
const crnlib::vector<uint>* pAlpha_endpoint_remap,
|
||||
const crnlib::vector<uint>* pAlpha_selector_remap);
|
||||
|
||||
void optimize_color_endpoint_codebook_task(uint64 data, void* pData_ptr);
|
||||
bool optimize_color_endpoint_codebook(crnlib::vector<uint>& remapping);
|
||||
|
||||
void optimize_color_selector_codebook_task(uint64 data, void* pData_ptr);
|
||||
bool optimize_color_selector_codebook(crnlib::vector<uint>& remapping);
|
||||
|
||||
void optimize_alpha_endpoint_codebook_task(uint64 data, void* pData_ptr);
|
||||
bool optimize_alpha_endpoint_codebook(crnlib::vector<uint>& remapping);
|
||||
|
||||
void optimize_alpha_selector_codebook_task(uint64 data, void* pData_ptr);
|
||||
bool optimize_alpha_selector_codebook(crnlib::vector<uint>& remapping);
|
||||
|
||||
bool create_comp_data();
|
||||
|
||||
bool pack_data_models();
|
||||
|
||||
bool update_progress(uint phase_index, uint subphase_index, uint subphase_total);
|
||||
|
||||
bool compress_internal();
|
||||
|
||||
static void append_vec(crnlib::vector<uint8>& a, const void* p, uint size);
|
||||
static void append_vec(crnlib::vector<uint8>& a, const crnlib::vector<uint8>& b);
|
||||
};
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,431 @@
|
||||
// File: crn_condition_var.cpp
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#include "crn_core.h"
|
||||
#include "crn_condition_var.h"
|
||||
#include "crn_spinlock.h"
|
||||
#include "crn_winhdr.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
void spinlock::lock(uint32 max_spins, bool yielding, bool memoryBarrier)
|
||||
{
|
||||
if (g_number_of_processors <= 1)
|
||||
max_spins = 1;
|
||||
|
||||
uint32 spinCount = 0;
|
||||
uint32 yieldCount = 0;
|
||||
|
||||
for ( ; ; )
|
||||
{
|
||||
CRNLIB_ASSUME(sizeof(long) == sizeof(int32));
|
||||
if (!InterlockedExchange((volatile long*)&m_flag, TRUE))
|
||||
break;
|
||||
|
||||
YieldProcessor();
|
||||
YieldProcessor();
|
||||
YieldProcessor();
|
||||
YieldProcessor();
|
||||
YieldProcessor();
|
||||
YieldProcessor();
|
||||
YieldProcessor();
|
||||
YieldProcessor();
|
||||
|
||||
spinCount++;
|
||||
if ((yielding) && (spinCount >= max_spins))
|
||||
{
|
||||
switch (yieldCount)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
spinCount = 0;
|
||||
|
||||
Sleep(0);
|
||||
|
||||
yieldCount++;
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
if (g_number_of_processors <= 1)
|
||||
spinCount = 0;
|
||||
else
|
||||
spinCount = max_spins / 2;
|
||||
|
||||
Sleep(1);
|
||||
|
||||
yieldCount++;
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
if (g_number_of_processors <= 1)
|
||||
spinCount = 0;
|
||||
else
|
||||
spinCount = max_spins;
|
||||
|
||||
Sleep(2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (memoryBarrier)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
MemoryBarrier();
|
||||
#elif defined(__MINGW32__) && defined(__MINGW64__)
|
||||
__sync_synchronize();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void spinlock::unlock()
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
MemoryBarrier();
|
||||
#elif defined(__MINGW32__) && defined(__MINGW64__)
|
||||
__sync_synchronize();
|
||||
#endif
|
||||
|
||||
m_flag = FALSE;
|
||||
}
|
||||
|
||||
mutex::mutex(unsigned int spin_count)
|
||||
{
|
||||
CRNLIB_ASSUME(sizeof(mutex) >= sizeof(CRITICAL_SECTION));
|
||||
|
||||
void *p = m_buf;
|
||||
CRITICAL_SECTION &m_cs = *static_cast<CRITICAL_SECTION *>(p);
|
||||
|
||||
BOOL status = true;
|
||||
#ifdef _XBOX
|
||||
InitializeCriticalSectionAndSpinCount(&m_cs, spin_count);
|
||||
#else
|
||||
status = InitializeCriticalSectionAndSpinCount(&m_cs, spin_count);
|
||||
#endif
|
||||
if (!status)
|
||||
crnlib_fail("mutex::mutex: InitializeCriticalSectionAndSpinCount failed", __FILE__, __LINE__);
|
||||
|
||||
#ifdef CRNLIB_BUILD_DEBUG
|
||||
m_lock_count = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
mutex::~mutex()
|
||||
{
|
||||
void *p = m_buf;
|
||||
CRITICAL_SECTION &m_cs = *static_cast<CRITICAL_SECTION *>(p);
|
||||
|
||||
#ifdef CRNLIB_BUILD_DEBUG
|
||||
if (m_lock_count)
|
||||
crnlib_assert("mutex::~mutex: mutex is still locked", __FILE__, __LINE__);
|
||||
#endif
|
||||
DeleteCriticalSection(&m_cs);
|
||||
}
|
||||
|
||||
void mutex::lock()
|
||||
{
|
||||
void *p = m_buf;
|
||||
CRITICAL_SECTION &m_cs = *static_cast<CRITICAL_SECTION *>(p);
|
||||
|
||||
EnterCriticalSection(&m_cs);
|
||||
#ifdef CRNLIB_BUILD_DEBUG
|
||||
m_lock_count++;
|
||||
#endif
|
||||
}
|
||||
|
||||
void mutex::unlock()
|
||||
{
|
||||
void *p = m_buf;
|
||||
CRITICAL_SECTION &m_cs = *static_cast<CRITICAL_SECTION *>(p);
|
||||
|
||||
#ifdef CRNLIB_BUILD_DEBUG
|
||||
if (!m_lock_count)
|
||||
crnlib_assert("mutex::unlock: mutex is not locked", __FILE__, __LINE__);
|
||||
m_lock_count--;
|
||||
#endif
|
||||
LeaveCriticalSection(&m_cs);
|
||||
}
|
||||
|
||||
void mutex::set_spin_count(unsigned int count)
|
||||
{
|
||||
void *p = m_buf;
|
||||
CRITICAL_SECTION &m_cs = *static_cast<CRITICAL_SECTION *>(p);
|
||||
|
||||
SetCriticalSectionSpinCount(&m_cs, count);
|
||||
}
|
||||
|
||||
semaphore::semaphore(int32 initialCount, int32 maximumCount, const char* pName)
|
||||
{
|
||||
m_handle = CreateSemaphoreA(NULL, initialCount, maximumCount, pName);
|
||||
if (NULL == m_handle)
|
||||
{
|
||||
CRNLIB_FAIL("semaphore: CreateSemaphore() failed");
|
||||
}
|
||||
}
|
||||
|
||||
semaphore::~semaphore()
|
||||
{
|
||||
if (m_handle)
|
||||
{
|
||||
CloseHandle(m_handle);
|
||||
m_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void semaphore::release(int32 releaseCount, int32 *pPreviousCount)
|
||||
{
|
||||
CRNLIB_ASSUME(sizeof(LONG) == sizeof(int32));
|
||||
if (0 == ReleaseSemaphore(m_handle, releaseCount, (LPLONG)pPreviousCount))
|
||||
{
|
||||
CRNLIB_FAIL("semaphore: ReleaseSemaphore() failed");
|
||||
}
|
||||
}
|
||||
|
||||
bool semaphore::wait(uint32 milliseconds)
|
||||
{
|
||||
uint32 result = WaitForSingleObject(m_handle, milliseconds);
|
||||
|
||||
if (WAIT_FAILED == result)
|
||||
{
|
||||
CRNLIB_FAIL("semaphore: WaitForSingleObject() failed");
|
||||
}
|
||||
|
||||
return WAIT_OBJECT_0 == result;
|
||||
}
|
||||
|
||||
event::event(bool manual_reset, bool initial_state, const char* pName)
|
||||
{
|
||||
m_handle = CreateEventA(NULL, manual_reset, initial_state, pName);
|
||||
|
||||
if (NULL == m_handle)
|
||||
CRNLIB_FAIL("event: CreateEvent() failed");
|
||||
}
|
||||
|
||||
event::~event()
|
||||
{
|
||||
if (m_handle)
|
||||
{
|
||||
CloseHandle(m_handle);
|
||||
m_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void event::set(void)
|
||||
{
|
||||
SetEvent(m_handle);
|
||||
}
|
||||
|
||||
void event::reset(void)
|
||||
{
|
||||
ResetEvent(m_handle);
|
||||
}
|
||||
|
||||
void event::pulse(void)
|
||||
{
|
||||
PulseEvent(m_handle);
|
||||
}
|
||||
|
||||
bool event::wait(uint32 milliseconds)
|
||||
{
|
||||
uint32 result = WaitForSingleObject(m_handle, milliseconds);
|
||||
|
||||
if (result == WAIT_FAILED)
|
||||
{
|
||||
CRNLIB_FAIL("event: WaitForSingleObject() failed");
|
||||
}
|
||||
|
||||
return (result == WAIT_OBJECT_0);
|
||||
}
|
||||
|
||||
condition_var::condition_var(uint spin_count) :
|
||||
m_condition_var_lock(1, 1),
|
||||
m_tls(TlsAlloc()),
|
||||
m_cur_age(0),
|
||||
m_max_waiter_array_index(-1)
|
||||
{
|
||||
CRNLIB_ASSERT(TLS_OUT_OF_INDEXES != m_tls);
|
||||
|
||||
m_waiters_array_lock.set_spin_count(spin_count);
|
||||
|
||||
m_waiters_array_lock.lock();
|
||||
|
||||
for (uint i = 0; i < cMaxWaitingThreads; i++)
|
||||
m_waiters[i].clear();
|
||||
|
||||
m_waiters_array_lock.unlock();
|
||||
}
|
||||
|
||||
condition_var::~condition_var()
|
||||
{
|
||||
TlsFree(m_tls);
|
||||
}
|
||||
|
||||
void condition_var::lock()
|
||||
{
|
||||
uint32 cur_count = get_cur_lock_count();
|
||||
CRNLIB_ASSERT(cur_count != 0xFFFFFFFF);
|
||||
cur_count++;
|
||||
set_cur_lock_count(cur_count);
|
||||
|
||||
if (1 == cur_count)
|
||||
m_condition_var_lock.wait();
|
||||
}
|
||||
|
||||
void condition_var::unlock()
|
||||
{
|
||||
uint32 cur_count = get_cur_lock_count();
|
||||
CRNLIB_ASSERT(cur_count);
|
||||
cur_count--;
|
||||
set_cur_lock_count(cur_count);
|
||||
|
||||
if (!cur_count)
|
||||
leave_and_scan();
|
||||
}
|
||||
|
||||
void condition_var::leave_and_scan(int index_to_ignore)
|
||||
{
|
||||
m_waiters_array_lock.lock();
|
||||
|
||||
uint best_age = 0;
|
||||
int best_index = -1;
|
||||
for (int i = 0; i <= m_max_waiter_array_index; i++)
|
||||
{
|
||||
waiting_thread& waiter = m_waiters[i];
|
||||
|
||||
if ((i != index_to_ignore) && (waiter.m_occupied) && (!waiter.m_satisfied))
|
||||
{
|
||||
uint age = m_cur_age - waiter.m_age;
|
||||
|
||||
if ((age > best_age) || (best_index < 0))
|
||||
{
|
||||
if ((!waiter.m_callback_func) || (waiter.m_callback_func(waiter.m_pCallback_ptr, waiter.m_callback_data)))
|
||||
{
|
||||
best_age = age;
|
||||
best_index = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (best_index >= 0)
|
||||
{
|
||||
waiting_thread& waiter = m_waiters[best_index];
|
||||
waiter.m_satisfied = true;
|
||||
waiter.m_event.set();
|
||||
m_waiters_array_lock.unlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_waiters_array_lock.unlock();
|
||||
m_condition_var_lock.release();
|
||||
}
|
||||
}
|
||||
|
||||
uint32 condition_var::get_cur_lock_count() const
|
||||
{
|
||||
return (uint32)((intptr_t)TlsGetValue(m_tls));
|
||||
}
|
||||
|
||||
int condition_var::wait(
|
||||
pCondition_func pCallback, void* pCallback_data_ptr, uint64 callback_data,
|
||||
uint num_wait_handles, const void** pWait_handles, uint32 max_time_to_wait)
|
||||
{
|
||||
CRNLIB_ASSERT(get_cur_lock_count());
|
||||
|
||||
// First, see if the calling thread's condition function is satisfied. If so, there's no need to wait.
|
||||
if ((pCallback) && (pCallback(pCallback_data_ptr, callback_data)))
|
||||
return 0;
|
||||
|
||||
// Add this thread to the list of waiters.
|
||||
m_waiters_array_lock.lock();
|
||||
|
||||
uint i;
|
||||
for (i = 0; i < cMaxWaitingThreads; i++)
|
||||
if (!m_waiters[i].m_occupied)
|
||||
break;
|
||||
|
||||
CRNLIB_VERIFY(i != cMaxWaitingThreads);
|
||||
|
||||
m_max_waiter_array_index = math::maximum<int>(m_max_waiter_array_index, i);
|
||||
|
||||
waiting_thread& waiter = m_waiters[i];
|
||||
|
||||
waiter.m_callback_func = pCallback;
|
||||
waiter.m_pCallback_ptr = pCallback_data_ptr;
|
||||
waiter.m_callback_data = callback_data;
|
||||
waiter.m_satisfied = false;
|
||||
waiter.m_occupied = true;
|
||||
waiter.m_age = m_cur_age++;
|
||||
waiter.m_event.reset();
|
||||
|
||||
m_waiters_array_lock.unlock();
|
||||
|
||||
// Now leave the condition_var and scan to see if there are any satisfied waiters.
|
||||
leave_and_scan(i);
|
||||
|
||||
// Let's wait for this thread's condition to be satisfied, or until timeout, or until one of the user supplied handles is signaled.
|
||||
int return_index = 0;
|
||||
|
||||
const uint cMaxWaitHandles = 64;
|
||||
CRNLIB_ASSERT(num_wait_handles < cMaxWaitHandles);
|
||||
|
||||
HANDLE handles[cMaxWaitHandles];
|
||||
|
||||
handles[0] = waiter.m_event.get_handle();
|
||||
uint total_handles = 1;
|
||||
|
||||
if (num_wait_handles)
|
||||
{
|
||||
CRNLIB_ASSERT(pWait_handles);
|
||||
memcpy(handles + total_handles, pWait_handles, sizeof(HANDLE) * num_wait_handles);
|
||||
total_handles += num_wait_handles;
|
||||
}
|
||||
|
||||
uint32 result;
|
||||
if (max_time_to_wait == UINT32_MAX)
|
||||
{
|
||||
do
|
||||
{
|
||||
result = WaitForMultipleObjects(total_handles, handles, FALSE, 2000);
|
||||
} while (result == WAIT_TIMEOUT);
|
||||
}
|
||||
else
|
||||
result = WaitForMultipleObjects(total_handles, handles, FALSE, max_time_to_wait);
|
||||
|
||||
if ((result == WAIT_ABANDONED) || (result == WAIT_TIMEOUT))
|
||||
return_index = -1;
|
||||
else
|
||||
return_index = result - WAIT_OBJECT_0;
|
||||
|
||||
// See if our condition was satisfied, and remove this thread from the waiter list.
|
||||
m_waiters_array_lock.lock();
|
||||
|
||||
const bool was_satisfied = waiter.m_satisfied;
|
||||
|
||||
waiter.m_occupied = false;
|
||||
|
||||
m_waiters_array_lock.unlock();
|
||||
|
||||
if (0 == return_index)
|
||||
{
|
||||
CRNLIB_ASSERT(was_satisfied);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Enter the condition_var if a user supplied handle was signaled. This guarantees that on exit of this function we're still inside the condition_var, no matter
|
||||
// what happened during the WaitForMultipleObjects() call.
|
||||
if (!was_satisfied)
|
||||
m_condition_var_lock.wait();
|
||||
}
|
||||
|
||||
return return_index;
|
||||
}
|
||||
|
||||
void condition_var::set_cur_lock_count(uint32 newCount)
|
||||
{
|
||||
TlsSetValue(m_tls, (void*)newCount);
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,91 @@
|
||||
// File: crn_condition_var.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
// Inspired by the "monitor" class in "Win32 Multithreaded Programming" by Cohen and Woodring.
|
||||
// Also see http://en.wikipedia.org/wiki/Monitor_(synchronization)
|
||||
#pragma once
|
||||
|
||||
#include "crn_mutex.h"
|
||||
#include "crn_event.h"
|
||||
#include "crn_semaphore.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
class condition_var
|
||||
{
|
||||
CRNLIB_NO_COPY_OR_ASSIGNMENT_OP(condition_var);
|
||||
|
||||
public:
|
||||
condition_var(uint spin_count = 4096U);
|
||||
~condition_var();
|
||||
|
||||
// Locks the condition_var.
|
||||
// Recursive locks are supported.
|
||||
void lock();
|
||||
|
||||
// Returns TRUE if the thread owning this condition function should stop waiting.
|
||||
// This function will always be called from within the condition_var, but it may be called from several different threads!
|
||||
typedef bool (*pCondition_func)(void* pCallback_data_ptr, uint64 callback_data);
|
||||
|
||||
// Temporarily leaves the lock and waits for a condition to be satisfied.
|
||||
// If pCallback is NULL, this method will return after another thread enters and exits the lock (like a Vista-style condition variable).
|
||||
// Otherwise, this method will only return when the specified condition function returns TRUE when another thread exits the lock.
|
||||
// When this method returns, the calling thread will be inside the lock.
|
||||
// Returns -1 on timeout or error, 0 if the wait was satisfied, or 1 or higher if one of the extra wait handles became signaled.
|
||||
// It is highly recommended you use a non-null condition callback. If you don't be sure to check for race conditions!
|
||||
int wait(pCondition_func pCallback = NULL, void* pCallback_data_ptr = NULL, uint64 callback_data = 0,
|
||||
uint num_wait_handles = 0, const void** pWait_handles = NULL, uint32 max_time_to_wait = UINT32_MAX);
|
||||
|
||||
// Unlocks the condition_var. Another thread may be woken up if its condition function has become satisfied.
|
||||
void unlock();
|
||||
|
||||
uint32 get_cur_lock_count() const;
|
||||
|
||||
private:
|
||||
enum { cMaxWaitingThreads = 16, cMaxWaitingThreadsMask = cMaxWaitingThreads - 1 };
|
||||
|
||||
semaphore m_condition_var_lock;
|
||||
mutex m_waiters_array_lock;
|
||||
uint32 m_tls;
|
||||
uint m_cur_age;
|
||||
|
||||
struct waiting_thread
|
||||
{
|
||||
uint64 m_callback_data;
|
||||
void* m_pCallback_ptr;
|
||||
pCondition_func m_callback_func;
|
||||
uint m_age;
|
||||
bool m_satisfied;
|
||||
bool m_occupied;
|
||||
|
||||
event m_event;
|
||||
|
||||
void clear()
|
||||
{
|
||||
m_callback_data = 0;
|
||||
m_pCallback_ptr = NULL;
|
||||
m_callback_func = NULL;
|
||||
m_age = 0;
|
||||
m_satisfied = false;
|
||||
m_occupied = false;
|
||||
}
|
||||
};
|
||||
waiting_thread m_waiters[cMaxWaitingThreads];
|
||||
|
||||
int m_max_waiter_array_index;
|
||||
|
||||
void set_cur_lock_count(uint32 newCount);
|
||||
|
||||
void leave_and_scan(int index_to_ignore = -1);
|
||||
};
|
||||
|
||||
class scoped_condition_var
|
||||
{
|
||||
CRNLIB_NO_COPY_OR_ASSIGNMENT_OP(scoped_condition_var);
|
||||
public:
|
||||
inline scoped_condition_var(condition_var& m) : m_condition_var(m) { m_condition_var.lock(); }
|
||||
inline ~scoped_condition_var() { m_condition_var.unlock(); }
|
||||
private:
|
||||
condition_var& m_condition_var;
|
||||
};
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,231 @@
|
||||
// File: crn_console.cpp
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#include "crn_core.h"
|
||||
#include "crn_console.h"
|
||||
#include "crn_data_stream.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
eConsoleMessageType console::m_default_category = cInfoConsoleMessage;
|
||||
crnlib::vector<console::console_func> console::m_output_funcs;
|
||||
bool console::m_crlf = true;
|
||||
bool console::m_prefixes = true;
|
||||
bool console::m_output_disabled;
|
||||
data_stream* console::m_pLog_stream;
|
||||
mutex* console::m_pMutex;
|
||||
uint console::m_num_messages[cCMTTotal];
|
||||
|
||||
const uint cConsoleBufSize = 4096;
|
||||
|
||||
void console::init()
|
||||
{
|
||||
if (!m_pMutex)
|
||||
{
|
||||
m_pMutex = crnlib_new<mutex>();
|
||||
}
|
||||
}
|
||||
|
||||
void console::deinit()
|
||||
{
|
||||
if (m_pMutex)
|
||||
{
|
||||
crnlib_delete(m_pMutex);
|
||||
m_pMutex = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void console::disable_crlf()
|
||||
{
|
||||
init();
|
||||
|
||||
m_crlf = false;
|
||||
}
|
||||
|
||||
void console::enable_crlf()
|
||||
{
|
||||
init();
|
||||
|
||||
m_crlf = true;
|
||||
}
|
||||
|
||||
void console::vprintf(eConsoleMessageType type, const wchar_t* p, va_list args)
|
||||
{
|
||||
init();
|
||||
|
||||
scoped_mutex lock(*m_pMutex);
|
||||
|
||||
m_num_messages[type]++;
|
||||
|
||||
wchar_t buf[cConsoleBufSize];
|
||||
#ifdef _MSC_VER
|
||||
vswprintf_s(buf, cConsoleBufSize, p, args);
|
||||
#else
|
||||
vswprintf(buf, p, args);
|
||||
#endif
|
||||
|
||||
bool handled = false;
|
||||
|
||||
if (m_output_funcs.size())
|
||||
{
|
||||
for (uint i = 0; i < m_output_funcs.size(); i++)
|
||||
if (m_output_funcs[i].m_func(type, buf, m_output_funcs[i].m_pData))
|
||||
handled = true;
|
||||
}
|
||||
|
||||
const wchar_t* pPrefix = NULL;
|
||||
if (m_prefixes)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case cDebugConsoleMessage: pPrefix = L"Debug: "; break;
|
||||
case cWarningConsoleMessage: pPrefix = L"Warning: "; break;
|
||||
case cErrorConsoleMessage: pPrefix = L"Error: "; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((!m_output_disabled) && (!handled))
|
||||
{
|
||||
#ifdef _XBOX
|
||||
if (pPrefix)
|
||||
OutputDebugStringW(pPrefix);
|
||||
OutputDebugStringW(buf);
|
||||
if (m_crlf)
|
||||
OutputDebugStringW(L"\n");
|
||||
#else
|
||||
if (pPrefix)
|
||||
::wprintf(pPrefix);
|
||||
::wprintf(m_crlf ? L"%s\n" : L"%s", buf);
|
||||
#endif
|
||||
}
|
||||
|
||||
if ((type != cProgressConsoleMessage) && (m_pLog_stream))
|
||||
{
|
||||
// Yes this is bad.
|
||||
dynamic_wstring utf16_buf(buf);
|
||||
|
||||
dynamic_string ansi_buf;
|
||||
utf16_buf.as_ansi(ansi_buf);
|
||||
ansi_buf.translate_lf_to_crlf();
|
||||
|
||||
m_pLog_stream->printf(m_crlf ? "%s\r\n" : "%s", ansi_buf.get_ptr());
|
||||
m_pLog_stream->flush();
|
||||
}
|
||||
}
|
||||
|
||||
void console::printf(eConsoleMessageType type, const wchar_t* p, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, p);
|
||||
vprintf(type, p, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void console::printf(const wchar_t* p, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, p);
|
||||
vprintf(m_default_category, p, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void console::set_default_category(eConsoleMessageType category)
|
||||
{
|
||||
init();
|
||||
|
||||
m_default_category = category;
|
||||
}
|
||||
|
||||
eConsoleMessageType console::get_default_category()
|
||||
{
|
||||
init();
|
||||
|
||||
return m_default_category;
|
||||
}
|
||||
|
||||
void console::add_console_output_func(console_output_func pFunc, void* pData)
|
||||
{
|
||||
init();
|
||||
|
||||
scoped_mutex lock(*m_pMutex);
|
||||
|
||||
m_output_funcs.push_back(console_func(pFunc, pData));
|
||||
}
|
||||
|
||||
void console::remove_console_output_func(console_output_func pFunc)
|
||||
{
|
||||
init();
|
||||
|
||||
scoped_mutex lock(*m_pMutex);
|
||||
|
||||
for (int i = m_output_funcs.size() - 1; i >= 0; i--)
|
||||
{
|
||||
if (m_output_funcs[i].m_func == pFunc)
|
||||
{
|
||||
m_output_funcs.erase(m_output_funcs.begin() + i);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_output_funcs.size())
|
||||
{
|
||||
m_output_funcs.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void console::progress(const wchar_t* p, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, p);
|
||||
vprintf(cProgressConsoleMessage, p, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void console::info(const wchar_t* p, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, p);
|
||||
vprintf(cInfoConsoleMessage, p, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void console::message(const wchar_t* p, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, p);
|
||||
vprintf(cMessageConsoleMessage, p, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void console::cons(const wchar_t* p, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, p);
|
||||
vprintf(cConsoleConsoleMessage, p, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void console::debug(const wchar_t* p, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, p);
|
||||
vprintf(cDebugConsoleMessage, p, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void console::warning(const wchar_t* p, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, p);
|
||||
vprintf(cWarningConsoleMessage, p, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void console::error(const wchar_t* p, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, p);
|
||||
vprintf(cErrorConsoleMessage, p, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,94 @@
|
||||
// File: crn_console.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
#include "crn_dynamic_string.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
class dynamic_string;
|
||||
class data_stream;
|
||||
class mutex;
|
||||
|
||||
enum eConsoleMessageType
|
||||
{
|
||||
cDebugConsoleMessage, // debugging messages
|
||||
cProgressConsoleMessage, // progress messages
|
||||
cInfoConsoleMessage, // ordinary messages
|
||||
cConsoleConsoleMessage, // user console output
|
||||
cMessageConsoleMessage, // high importance messages
|
||||
cWarningConsoleMessage, // warnings
|
||||
cErrorConsoleMessage, // errors
|
||||
|
||||
cCMTTotal
|
||||
};
|
||||
|
||||
typedef bool (*console_output_func)(eConsoleMessageType type, const wchar_t* pMsg, void* pData);
|
||||
|
||||
class console
|
||||
{
|
||||
public:
|
||||
static void init();
|
||||
static void deinit();
|
||||
|
||||
static bool is_initialized() { return m_pMutex != NULL; }
|
||||
|
||||
static void set_default_category(eConsoleMessageType category);
|
||||
static eConsoleMessageType get_default_category();
|
||||
|
||||
static void add_console_output_func(console_output_func pFunc, void* pData);
|
||||
static void remove_console_output_func(console_output_func pFunc);
|
||||
|
||||
static void printf(const wchar_t* p, ...);
|
||||
|
||||
static void vprintf(eConsoleMessageType type, const wchar_t* p, va_list args);
|
||||
static void printf(eConsoleMessageType type, const wchar_t* p, ...);
|
||||
|
||||
static void cons(const wchar_t* p, ...);
|
||||
static void debug(const wchar_t* p, ...);
|
||||
static void progress(const wchar_t* p, ...);
|
||||
static void info(const wchar_t* p, ...);
|
||||
static void message(const wchar_t* p, ...);
|
||||
static void warning(const wchar_t* p, ...);
|
||||
static void error(const wchar_t* p, ...);
|
||||
|
||||
// FIXME: All console state is currently global!
|
||||
static void disable_prefixes();
|
||||
static void enable_prefixes();
|
||||
static bool get_prefixes() { return m_prefixes; }
|
||||
|
||||
static void disable_crlf();
|
||||
static void enable_crlf();
|
||||
static bool get_crlf() { return m_crlf; }
|
||||
|
||||
static void disable_output() { m_output_disabled = true; }
|
||||
static void enable_output() { m_output_disabled = false; }
|
||||
static bool get_output_disabled() { return m_output_disabled; }
|
||||
|
||||
static void set_log_stream(data_stream* pStream) { m_pLog_stream = pStream; }
|
||||
static data_stream* get_log_stream() { return m_pLog_stream; }
|
||||
|
||||
static uint get_num_messages(eConsoleMessageType type) { return m_num_messages[type]; }
|
||||
|
||||
private:
|
||||
static eConsoleMessageType m_default_category;
|
||||
|
||||
struct console_func
|
||||
{
|
||||
console_func(console_output_func func = NULL, void* pData = NULL) : m_func(func), m_pData(pData) { }
|
||||
|
||||
console_output_func m_func;
|
||||
void* m_pData;
|
||||
};
|
||||
static crnlib::vector<console_func> m_output_funcs;
|
||||
|
||||
static bool m_crlf, m_prefixes, m_output_disabled;
|
||||
|
||||
static data_stream* m_pLog_stream;
|
||||
|
||||
static mutex* m_pMutex;
|
||||
|
||||
static uint m_num_messages[cCMTTotal];
|
||||
};
|
||||
|
||||
} // namespace crnlib
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
// File: crn_core.cpp
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#include "crn_core.h"
|
||||
#include "crn_winhdr.h"
|
||||
|
||||
char *g_copyright_str = "Copyright (c) 2010-2011 Tenacious Software LLC";
|
||||
char *g_sig_str = "C8cfRlaorj0wLtnMSxrBJxTC85rho2L9hUZKHcBL";
|
||||
@@ -0,0 +1,103 @@
|
||||
// File: crn_core.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
|
||||
#if defined(WIN32) && defined(_MSC_VER)
|
||||
#pragma warning (disable: 4201) // nonstandard extension used : nameless struct/union
|
||||
#pragma warning (disable: 4127) // conditional expression is constant
|
||||
#pragma warning (disable: 4793) // function compiled as native
|
||||
#endif
|
||||
|
||||
#if defined(WIN32)
|
||||
|
||||
#if 0
|
||||
#ifdef NDEBUG
|
||||
// Ensure checked iterators are disabled.
|
||||
#define _SECURE_SCL 0
|
||||
#define _HAS_ITERATOR_DEBUGGING 0
|
||||
#endif
|
||||
|
||||
#ifndef _DLL
|
||||
// If we're using the DLL form of the run-time libs, we're also going to be enabling exceptions because we'll be building CLR apps.
|
||||
// Otherwise, we disable exceptions for a small (up to 5%) speed boost.
|
||||
#define _HAS_EXCEPTIONS 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//#define _CRT_SECURE_NO_WARNINGS
|
||||
#define NOMINMAX
|
||||
|
||||
#define CRNLIB_PLATFORM_PC 1
|
||||
|
||||
#ifdef _WIN64
|
||||
#define CRNLIB_PLATFORM_PC_X64 1
|
||||
#else
|
||||
#define CRNLIB_PLATFORM_PC_X86 1
|
||||
#endif
|
||||
|
||||
#define CRNLIB_USE_WIN32_API 1
|
||||
|
||||
#ifdef _WIN64
|
||||
#define CRNLIB_PLATFORM_PC_X64 1
|
||||
#define CRNLIB_64BIT_POINTERS 1
|
||||
#define CRNLIB_CPU_HAS_64BIT_REGISTERS 1
|
||||
#define CRNLIB_LITTLE_ENDIAN_CPU 1
|
||||
#else
|
||||
#define CRNLIB_PLATFORM_PC_X86 1
|
||||
#define CRNLIB_64BIT_POINTERS 0
|
||||
#define CRNLIB_CPU_HAS_64BIT_REGISTERS 0
|
||||
#define CRNLIB_LITTLE_ENDIAN_CPU 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
#include <locale>
|
||||
|
||||
#ifdef min
|
||||
#undef min
|
||||
#endif
|
||||
|
||||
#ifdef max
|
||||
#undef max
|
||||
#endif
|
||||
|
||||
#define CRNLIB_FALSE (0)
|
||||
#define CRNLIB_TRUE (1)
|
||||
#define CRNLIB_MAX_PATH (260)
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define CRNLIB_BUILD_DEBUG
|
||||
#else
|
||||
#define CRNLIB_BUILD_RELEASE
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define NDEBUG
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "crn_platform.h"
|
||||
|
||||
#if defined(WIN32)
|
||||
#include "crn_mutex.h"
|
||||
#endif
|
||||
|
||||
#include "crn_assert.h"
|
||||
#include "crn_types.h"
|
||||
#include "crn_helpers.h"
|
||||
#include "crn_traits.h"
|
||||
#include "crn_mem.h"
|
||||
#include "crn_math.h"
|
||||
#include "crn_utils.h"
|
||||
#include "crn_hash.h"
|
||||
#include "crn_vector.h"
|
||||
#include "crn_win32_timer.h"
|
||||
#include "crn_win32_threading.h"
|
||||
#include "crn_dynamic_string.h"
|
||||
#include "crn_dynamic_wstring.h"
|
||||
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
// File: crn_data_stream.cpp
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#include "crn_core.h"
|
||||
#include "crn_data_stream.h"
|
||||
#include <stdio.h>
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
data_stream::data_stream() :
|
||||
m_attribs(0),
|
||||
m_opened(false), m_error(false), m_got_cr(false)
|
||||
{
|
||||
}
|
||||
|
||||
data_stream::data_stream(const wchar_t* pName, uint attribs) :
|
||||
m_name(pName),
|
||||
m_attribs(static_cast<uint16>(attribs)),
|
||||
m_opened(false), m_error(false), m_got_cr(false)
|
||||
{
|
||||
}
|
||||
|
||||
uint64 data_stream::skip(uint64 len)
|
||||
{
|
||||
uint64 total_bytes_read = 0;
|
||||
|
||||
const uint cBufSize = 1024;
|
||||
uint8 buf[cBufSize];
|
||||
|
||||
while (len)
|
||||
{
|
||||
const uint64 bytes_to_read = math::minimum<uint64>(sizeof(buf), len);
|
||||
const uint64 bytes_read = read(buf, static_cast<uint>(bytes_to_read));
|
||||
total_bytes_read += bytes_read;
|
||||
|
||||
if (bytes_read != bytes_to_read)
|
||||
break;
|
||||
|
||||
len -= bytes_read;
|
||||
}
|
||||
|
||||
return total_bytes_read;
|
||||
}
|
||||
|
||||
bool data_stream::read_line(dynamic_string& str)
|
||||
{
|
||||
str.empty();
|
||||
|
||||
for ( ; ; )
|
||||
{
|
||||
const int c = read_byte();
|
||||
|
||||
const bool prev_got_cr = m_got_cr;
|
||||
m_got_cr = false;
|
||||
|
||||
if (c < 0)
|
||||
{
|
||||
if (!str.is_empty())
|
||||
break;
|
||||
|
||||
return false;
|
||||
}
|
||||
else if ((26 == c) || (!c))
|
||||
continue;
|
||||
else if (13 == c)
|
||||
{
|
||||
m_got_cr = true;
|
||||
break;
|
||||
}
|
||||
else if (10 == c)
|
||||
{
|
||||
if (prev_got_cr)
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
str.append_char(static_cast<char>(c));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool data_stream::printf(const char* p, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, p);
|
||||
char buf[4096];
|
||||
#ifdef _MSC_VER
|
||||
int l = vsprintf_s(buf, sizeof(buf), p, args);
|
||||
#else
|
||||
int l = vsprintf(buf, p, args);
|
||||
#endif
|
||||
va_end(args);
|
||||
if (l < 0)
|
||||
return false;
|
||||
return write(buf, l) == static_cast<uint>(l);
|
||||
}
|
||||
|
||||
bool data_stream::printf(const wchar_t* p, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, p);
|
||||
dynamic_wstring buf;
|
||||
buf.format_args(p, args);
|
||||
va_end(args);
|
||||
|
||||
return write(buf.get_ptr(), buf.get_len() * sizeof(wchar_t)) == buf.get_len() * sizeof(wchar_t);
|
||||
}
|
||||
|
||||
bool data_stream::write_line(const dynamic_string& str)
|
||||
{
|
||||
if (!str.is_empty())
|
||||
return write(str.get_ptr(), str.get_len()) == str.get_len();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool data_stream::write_line(const dynamic_wstring& str)
|
||||
{
|
||||
if (!str.is_empty())
|
||||
return write(str.get_ptr(), str.get_len() * sizeof(wchar_t)) == str.get_len() * sizeof(wchar_t);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool data_stream::read_array(vector<uint8>& buf)
|
||||
{
|
||||
if (buf.size() < get_remaining())
|
||||
{
|
||||
if (get_remaining() > 1024U*1024U*1024U)
|
||||
return false;
|
||||
|
||||
buf.resize((uint)get_remaining());
|
||||
}
|
||||
|
||||
if (!get_remaining())
|
||||
{
|
||||
buf.resize(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
return read(&buf[0], buf.size()) == buf.size();
|
||||
}
|
||||
|
||||
bool data_stream::write_array(const vector<uint8>& buf)
|
||||
{
|
||||
if (!buf.empty())
|
||||
return write(&buf[0], buf.size()) == buf.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,91 @@
|
||||
// File: crn_data_stream.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
enum data_stream_attribs
|
||||
{
|
||||
cDataStreamReadable = 1,
|
||||
cDataStreamWritable = 2,
|
||||
cDataStreamSeekable = 4
|
||||
};
|
||||
|
||||
const int64 DATA_STREAM_SIZE_UNKNOWN = INT64_MAX;
|
||||
const int64 DATA_STREAM_SIZE_INFINITE = UINT64_MAX;
|
||||
|
||||
class data_stream
|
||||
{
|
||||
data_stream(const data_stream&);
|
||||
data_stream& operator= (const data_stream&);
|
||||
|
||||
public:
|
||||
data_stream();
|
||||
data_stream(const wchar_t* pName, uint attribs);
|
||||
|
||||
virtual ~data_stream() { }
|
||||
|
||||
virtual data_stream *get_parent() { return NULL; }
|
||||
|
||||
virtual bool close() { m_opened = false; m_error = false; m_got_cr = false; return true; }
|
||||
|
||||
typedef uint16 attribs_t;
|
||||
inline attribs_t get_attribs() const { return m_attribs; }
|
||||
|
||||
inline bool is_opened() const { return m_opened; }
|
||||
|
||||
inline bool is_readable() const { return utils::is_bit_set(m_attribs, cDataStreamReadable); }
|
||||
inline bool is_writable() const { return utils::is_bit_set(m_attribs, cDataStreamWritable); }
|
||||
inline bool is_seekable() const { return utils::is_bit_set(m_attribs, cDataStreamSeekable); }
|
||||
|
||||
inline bool get_error() const { return m_error; }
|
||||
|
||||
inline const dynamic_wstring& get_name() const { return m_name; }
|
||||
inline void set_name(const wchar_t* pName) { m_name.set(pName); }
|
||||
|
||||
virtual uint read(void* pBuf, uint len) = 0;
|
||||
virtual uint64 skip(uint64 len);
|
||||
|
||||
virtual uint write(const void* pBuf, uint len) = 0;
|
||||
virtual bool flush() = 0;
|
||||
|
||||
virtual bool is_size_known() const { return true; }
|
||||
|
||||
// Returns DATA_STREAM_SIZE_UNKNOWN if size hasn't been determined yet, or DATA_STREAM_SIZE_INFINITE for infinite streams.
|
||||
virtual uint64 get_size() = 0;
|
||||
virtual uint64 get_remaining() = 0;
|
||||
|
||||
virtual uint64 get_ofs() = 0;
|
||||
virtual bool seek(int64 ofs, bool relative) = 0;
|
||||
|
||||
virtual const void* get_ptr() const { return NULL; }
|
||||
|
||||
inline int read_byte() { uint8 c; if (read(&c, 1) != 1) return -1; return c; }
|
||||
inline bool write_byte(uint8 c) { return write(&c, 1) == 1; }
|
||||
|
||||
bool read_line(dynamic_string& str);
|
||||
bool printf(const char* p, ...);
|
||||
bool printf(const wchar_t* p, ...);
|
||||
bool write_line(const dynamic_string& str);
|
||||
bool write_line(const dynamic_wstring& str);
|
||||
bool write_bom() { uint16 bom = 0xFEFF; return write(&bom, sizeof(bom)) == sizeof(bom); }
|
||||
|
||||
bool read_array(vector<uint8>& buf);
|
||||
bool write_array(const vector<uint8>& buf);
|
||||
|
||||
protected:
|
||||
dynamic_wstring m_name;
|
||||
|
||||
attribs_t m_attribs;
|
||||
bool m_opened : 1;
|
||||
bool m_error : 1;
|
||||
bool m_got_cr : 1;
|
||||
|
||||
inline void set_error() { m_error = true; }
|
||||
inline void clear_error() { m_error = false; }
|
||||
|
||||
inline void post_seek() { m_got_cr = false; }
|
||||
};
|
||||
|
||||
} // namespace crnlib
|
||||
|
||||
@@ -0,0 +1,432 @@
|
||||
// File: data_stream_serializer.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
#include "crn_data_stream.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
// Defaults to little endian mode.
|
||||
class data_stream_serializer
|
||||
{
|
||||
public:
|
||||
data_stream_serializer() : m_pStream(NULL), m_little_endian(true) { }
|
||||
data_stream_serializer(data_stream* pStream) : m_pStream(pStream), m_little_endian(true) { }
|
||||
data_stream_serializer(data_stream& stream) : m_pStream(&stream), m_little_endian(true) { }
|
||||
data_stream_serializer(const data_stream_serializer& other) : m_pStream(other.m_pStream), m_little_endian(other.m_little_endian) { }
|
||||
|
||||
data_stream_serializer& operator= (const data_stream_serializer& rhs) { m_pStream = rhs.m_pStream; m_little_endian = rhs.m_little_endian; return *this; }
|
||||
|
||||
data_stream* get_stream() const { return m_pStream; }
|
||||
void set_stream(data_stream* pStream) { m_pStream = pStream; }
|
||||
|
||||
bool get_error() { return m_pStream ? m_pStream->get_error() : false; }
|
||||
|
||||
bool get_little_endian() const { return m_little_endian; }
|
||||
void set_little_endian(bool little_endian) { m_little_endian = little_endian; }
|
||||
|
||||
bool write(const void* pBuf, uint len)
|
||||
{
|
||||
return m_pStream->write(pBuf, len) == len;
|
||||
}
|
||||
|
||||
bool read(void* pBuf, uint len)
|
||||
{
|
||||
return m_pStream->read(pBuf, len) == len;
|
||||
}
|
||||
|
||||
bool write_chars(const char* pBuf, uint len)
|
||||
{
|
||||
return write(pBuf, len);
|
||||
}
|
||||
|
||||
bool read_chars(char* pBuf, uint len)
|
||||
{
|
||||
return read(pBuf, len);
|
||||
}
|
||||
|
||||
bool skip(uint len)
|
||||
{
|
||||
return m_pStream->skip(len) == len;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool write_object(const T& obj)
|
||||
{
|
||||
if (m_little_endian == c_crnlib_little_endian_platform)
|
||||
return write(&obj, sizeof(obj));
|
||||
else
|
||||
{
|
||||
uint8 buf[sizeof(T)];
|
||||
uint buf_size = sizeof(T);
|
||||
void* pBuf = buf;
|
||||
utils::write_obj(obj, pBuf, buf_size, m_little_endian);
|
||||
|
||||
return write(buf, sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool read_object(T& obj)
|
||||
{
|
||||
if (m_little_endian == c_crnlib_little_endian_platform)
|
||||
return read(&obj, sizeof(obj));
|
||||
else
|
||||
{
|
||||
uint8 buf[sizeof(T)];
|
||||
if (!read(buf, sizeof(T)))
|
||||
return false;
|
||||
|
||||
uint buf_size = sizeof(T);
|
||||
const void* pBuf = buf;
|
||||
utils::read_obj(obj, pBuf, buf_size, m_little_endian);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool write_value(T value)
|
||||
{
|
||||
return write_object(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T read_value(const T& on_error_value = T())
|
||||
{
|
||||
T result;
|
||||
if (!read_object(result))
|
||||
result = on_error_value;
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool write_enum(T e)
|
||||
{
|
||||
int val = static_cast<int>(e);
|
||||
return write_object(val);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T read_enum()
|
||||
{
|
||||
return static_cast<T>(read_value<int>());
|
||||
}
|
||||
|
||||
// Writes uint using a simple variable length code (VLC).
|
||||
bool write_uint_vlc(uint val)
|
||||
{
|
||||
do
|
||||
{
|
||||
uint8 c = static_cast<uint8>(val) & 0x7F;
|
||||
if (val <= 0x7F)
|
||||
c |= 0x80;
|
||||
|
||||
if (!write_value(c))
|
||||
return false;
|
||||
|
||||
val >>= 7;
|
||||
} while (val);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Reads uint using a simple variable length code (VLC).
|
||||
bool read_uint_vlc(uint& val)
|
||||
{
|
||||
val = 0;
|
||||
uint shift = 0;
|
||||
|
||||
for ( ; ; )
|
||||
{
|
||||
if (shift >= 32)
|
||||
return false;
|
||||
|
||||
uint8 c;
|
||||
if (!read_object(c))
|
||||
return false;
|
||||
|
||||
val |= ((c & 0x7F) << shift);
|
||||
shift += 7;
|
||||
|
||||
if (c & 0x80)
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool write_c_str(const char* p)
|
||||
{
|
||||
uint len = static_cast<uint>(strlen(p));
|
||||
if (!write_uint_vlc(len))
|
||||
return false;
|
||||
|
||||
return write_chars(p, len);
|
||||
}
|
||||
|
||||
bool read_c_str(char* pBuf, uint buf_size)
|
||||
{
|
||||
uint len;
|
||||
if (!read_uint_vlc(len))
|
||||
return false;
|
||||
if ((len + 1) > buf_size)
|
||||
return false;
|
||||
|
||||
pBuf[len] = '\0';
|
||||
|
||||
return read_chars(pBuf, len);
|
||||
}
|
||||
|
||||
bool write_string(const dynamic_string& str)
|
||||
{
|
||||
if (!write_uint_vlc(str.get_len()))
|
||||
return false;
|
||||
|
||||
return write_chars(str.get_ptr(), str.get_len());
|
||||
}
|
||||
|
||||
bool read_string(dynamic_string& str)
|
||||
{
|
||||
uint len;
|
||||
if (!read_uint_vlc(len))
|
||||
return false;
|
||||
|
||||
if (!str.set_len(len))
|
||||
return false;
|
||||
|
||||
if (len)
|
||||
{
|
||||
if (!read_chars(str.get_ptr_raw(), len))
|
||||
return false;
|
||||
|
||||
if (memchr(str.get_ptr(), 0, len) != NULL)
|
||||
{
|
||||
str.truncate(0);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool write_vector(const T& vec)
|
||||
{
|
||||
if (!write_uint_vlc(vec.size()))
|
||||
return false;
|
||||
|
||||
for (uint i = 0; i < vec.size(); i++)
|
||||
{
|
||||
*this << vec[i];
|
||||
if (get_error())
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
bool read_vector(T& vec, uint num_expected = UINT_MAX)
|
||||
{
|
||||
uint size;
|
||||
if (!read_uint_vlc(size))
|
||||
return false;
|
||||
|
||||
if ((size * sizeof(T::value_type)) >= 2U*1024U*1024U*1024U)
|
||||
return false;
|
||||
|
||||
if ((num_expected != UINT_MAX) && (size != num_expected))
|
||||
return false;
|
||||
|
||||
vec.resize(size);
|
||||
for (uint i = 0; i < vec.size(); i++)
|
||||
{
|
||||
*this >> vec[i];
|
||||
|
||||
if (get_error())
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Got this idea from the Molly Rocket forums.
|
||||
// fmt may contain the characters "1", "2", or "4".
|
||||
bool writef(char *fmt, ...)
|
||||
{
|
||||
va_list v;
|
||||
va_start(v, fmt);
|
||||
|
||||
while (*fmt)
|
||||
{
|
||||
switch (*fmt++)
|
||||
{
|
||||
case '1':
|
||||
{
|
||||
const uint8 x = static_cast<uint8>(va_arg(v, uint));
|
||||
if (!write_value(x))
|
||||
return false;
|
||||
}
|
||||
case '2':
|
||||
{
|
||||
const uint16 x = static_cast<uint16>(va_arg(v, uint));
|
||||
if (!write_value(x))
|
||||
return false;
|
||||
}
|
||||
case '4':
|
||||
{
|
||||
const uint32 x = static_cast<uint32>(va_arg(v, uint));
|
||||
if (!write_value(x))
|
||||
return false;
|
||||
}
|
||||
case ' ':
|
||||
case ',':
|
||||
{
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
CRNLIB_ASSERT(0);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
va_end(v);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Got this idea from the Molly Rocket forums.
|
||||
// fmt may contain the characters "1", "2", or "4".
|
||||
bool readf(char *fmt, ...)
|
||||
{
|
||||
va_list v;
|
||||
va_start(v, fmt);
|
||||
|
||||
while (*fmt)
|
||||
{
|
||||
switch (*fmt++)
|
||||
{
|
||||
case '1':
|
||||
{
|
||||
uint8* x = va_arg(v, uint8*);
|
||||
CRNLIB_ASSERT(x);
|
||||
if (!read_object(*x))
|
||||
return false;
|
||||
}
|
||||
case '2':
|
||||
{
|
||||
uint16* x = va_arg(v, uint16*);
|
||||
CRNLIB_ASSERT(x);
|
||||
if (!read_object(*x))
|
||||
return false;
|
||||
}
|
||||
case '4':
|
||||
{
|
||||
uint32* x = va_arg(v, uint32*);
|
||||
CRNLIB_ASSERT(x);
|
||||
if (!read_object(*x))
|
||||
return false;
|
||||
}
|
||||
case ' ':
|
||||
case ',':
|
||||
{
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
CRNLIB_ASSERT(0);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
va_end(v);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
data_stream* m_pStream;
|
||||
|
||||
bool m_little_endian;
|
||||
};
|
||||
|
||||
// Write operators
|
||||
inline data_stream_serializer& operator<< (data_stream_serializer& serializer, bool val) { serializer.write_value(val); return serializer; }
|
||||
inline data_stream_serializer& operator<< (data_stream_serializer& serializer, int8 val) { serializer.write_value(val); return serializer; }
|
||||
inline data_stream_serializer& operator<< (data_stream_serializer& serializer, uint8 val) { serializer.write_value(val); return serializer; }
|
||||
inline data_stream_serializer& operator<< (data_stream_serializer& serializer, int16 val) { serializer.write_value(val); return serializer; }
|
||||
inline data_stream_serializer& operator<< (data_stream_serializer& serializer, uint16 val) { serializer.write_value(val); return serializer; }
|
||||
inline data_stream_serializer& operator<< (data_stream_serializer& serializer, int32 val) { serializer.write_value(val); return serializer; }
|
||||
inline data_stream_serializer& operator<< (data_stream_serializer& serializer, uint32 val) { serializer.write_uint_vlc(val); return serializer; }
|
||||
inline data_stream_serializer& operator<< (data_stream_serializer& serializer, int64 val) { serializer.write_value(val); return serializer; }
|
||||
inline data_stream_serializer& operator<< (data_stream_serializer& serializer, uint64 val) { serializer.write_value(val); return serializer; }
|
||||
inline data_stream_serializer& operator<< (data_stream_serializer& serializer, long val) { serializer.write_value(val); return serializer; }
|
||||
inline data_stream_serializer& operator<< (data_stream_serializer& serializer, unsigned long val) { serializer.write_value(val); return serializer; }
|
||||
inline data_stream_serializer& operator<< (data_stream_serializer& serializer, float val) { serializer.write_value(val); return serializer; }
|
||||
inline data_stream_serializer& operator<< (data_stream_serializer& serializer, double val) { serializer.write_value(val); return serializer; }
|
||||
inline data_stream_serializer& operator<< (data_stream_serializer& serializer, const char* p) { serializer.write_c_str(p); return serializer; }
|
||||
|
||||
inline data_stream_serializer& operator<< (data_stream_serializer& serializer, const dynamic_string& str)
|
||||
{
|
||||
serializer.write_string(str);
|
||||
return serializer;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline data_stream_serializer& operator<< (data_stream_serializer& serializer, const crnlib::vector<T>& vec)
|
||||
{
|
||||
serializer.write_vector(vec);
|
||||
return serializer;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline data_stream_serializer& operator<< (data_stream_serializer& serializer, const T* p)
|
||||
{
|
||||
serializer.write_object(*p);
|
||||
return serializer;
|
||||
}
|
||||
|
||||
// Read operators
|
||||
inline data_stream_serializer& operator>> (data_stream_serializer& serializer, bool& val) { serializer.read_object(val); return serializer; }
|
||||
inline data_stream_serializer& operator>> (data_stream_serializer& serializer, int8& val) { serializer.read_object(val); return serializer; }
|
||||
inline data_stream_serializer& operator>> (data_stream_serializer& serializer, uint8& val) { serializer.read_object(val); return serializer; }
|
||||
inline data_stream_serializer& operator>> (data_stream_serializer& serializer, int16& val) { serializer.read_object(val); return serializer; }
|
||||
inline data_stream_serializer& operator>> (data_stream_serializer& serializer, uint16& val) { serializer.read_object(val); return serializer; }
|
||||
inline data_stream_serializer& operator>> (data_stream_serializer& serializer, int32& val) { serializer.read_object(val); return serializer; }
|
||||
inline data_stream_serializer& operator>> (data_stream_serializer& serializer, uint32& val) { serializer.read_uint_vlc(val); return serializer; }
|
||||
inline data_stream_serializer& operator>> (data_stream_serializer& serializer, int64& val) { serializer.read_object(val); return serializer; }
|
||||
inline data_stream_serializer& operator>> (data_stream_serializer& serializer, uint64& val) { serializer.read_object(val); return serializer; }
|
||||
inline data_stream_serializer& operator>> (data_stream_serializer& serializer, long& val) { serializer.read_object(val); return serializer; }
|
||||
inline data_stream_serializer& operator>> (data_stream_serializer& serializer, unsigned long& val) { serializer.read_object(val); return serializer; }
|
||||
inline data_stream_serializer& operator>> (data_stream_serializer& serializer, float& val) { serializer.read_object(val); return serializer; }
|
||||
inline data_stream_serializer& operator>> (data_stream_serializer& serializer, double& val) { serializer.read_object(val); return serializer; }
|
||||
|
||||
inline data_stream_serializer& operator>> (data_stream_serializer& serializer, dynamic_string& str)
|
||||
{
|
||||
serializer.read_string(str);
|
||||
return serializer;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline data_stream_serializer& operator>> (data_stream_serializer& serializer, crnlib::vector<T>& vec)
|
||||
{
|
||||
serializer.read_vector(vec);
|
||||
return serializer;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline data_stream_serializer& operator>> (data_stream_serializer& serializer, T* p)
|
||||
{
|
||||
serializer.read_object(*p);
|
||||
return serializer;
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
// File: crn_dds_comp.cpp
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#include "crn_core.h"
|
||||
#include "crn_dds_comp.h"
|
||||
#include "crn_dynamic_stream.h"
|
||||
#include "crn_lzma_codec.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
dds_comp::dds_comp() :
|
||||
m_pParams(NULL),
|
||||
m_pixel_fmt(PIXEL_FMT_INVALID),
|
||||
m_pQDXT_state(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
dds_comp::~dds_comp()
|
||||
{
|
||||
crnlib_delete(m_pQDXT_state);
|
||||
}
|
||||
|
||||
void dds_comp::clear()
|
||||
{
|
||||
m_src_tex.clear();
|
||||
m_packed_tex.clear();
|
||||
m_comp_data.clear();
|
||||
m_pParams = NULL;
|
||||
m_pixel_fmt = PIXEL_FMT_INVALID;
|
||||
m_task_pool.deinit();
|
||||
if (m_pQDXT_state)
|
||||
{
|
||||
crnlib_delete(m_pQDXT_state);
|
||||
m_pQDXT_state = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool dds_comp::create_dds_tex(dds_texture &dds_tex)
|
||||
{
|
||||
image_u8 images[cCRNMaxFaces][cCRNMaxLevels];
|
||||
|
||||
bool has_alpha = false;
|
||||
for (uint face_index = 0; face_index < m_pParams->m_faces; face_index++)
|
||||
{
|
||||
for (uint level_index = 0; level_index < m_pParams->m_levels; level_index++)
|
||||
{
|
||||
const uint width = math::maximum(1U, m_pParams->m_width >> level_index);
|
||||
const uint height = math::maximum(1U, m_pParams->m_height >> level_index);
|
||||
|
||||
if (!m_pParams->m_pImages[face_index][level_index])
|
||||
return false;
|
||||
|
||||
images[face_index][level_index].alias((color_quad_u8*)m_pParams->m_pImages[face_index][level_index], width, height);
|
||||
if (!has_alpha)
|
||||
has_alpha = image_utils::has_alpha(images[face_index][level_index]);
|
||||
}
|
||||
}
|
||||
|
||||
for (uint face_index = 0; face_index < m_pParams->m_faces; face_index++)
|
||||
for (uint level_index = 0; level_index < m_pParams->m_levels; level_index++)
|
||||
images[face_index][level_index].set_component_valid(3, has_alpha);
|
||||
|
||||
image_utils::conversion_type conv_type = image_utils::get_image_conversion_type_from_crn_format((crn_format)m_pParams->m_format);
|
||||
if (conv_type != image_utils::cConversion_Invalid)
|
||||
{
|
||||
for (uint face_index = 0; face_index < m_pParams->m_faces; face_index++)
|
||||
{
|
||||
for (uint level_index = 0; level_index < m_pParams->m_levels; level_index++)
|
||||
{
|
||||
image_u8 cooked_image(images[face_index][level_index]);
|
||||
|
||||
image_utils::convert_image(cooked_image, conv_type);
|
||||
|
||||
images[face_index][level_index].swap(cooked_image);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
face_vec faces(m_pParams->m_faces);
|
||||
|
||||
for (uint face_index = 0; face_index < m_pParams->m_faces; face_index++)
|
||||
{
|
||||
for (uint level_index = 0; level_index < m_pParams->m_levels; level_index++)
|
||||
{
|
||||
mip_level *pMip = crnlib_new<mip_level>();
|
||||
|
||||
image_u8 *pImage = crnlib_new<image_u8>();
|
||||
pImage->swap(images[face_index][level_index]);
|
||||
pMip->assign(pImage);
|
||||
|
||||
faces[face_index].push_back(pMip);
|
||||
}
|
||||
}
|
||||
|
||||
dds_tex.assign(faces);
|
||||
#ifdef CRNLIB_BUILD_DEBUG
|
||||
CRNLIB_ASSERT(dds_tex.check());
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool progress_callback_func(uint percentage_complete, void* pUser_data_ptr)
|
||||
{
|
||||
const crn_comp_params& params = *(const crn_comp_params*)pUser_data_ptr;
|
||||
return params.m_pProgress_func(0, 1, percentage_complete, 100, params.m_pProgress_func_data) != 0;
|
||||
}
|
||||
|
||||
static bool progress_callback_func_phase_0(uint percentage_complete, void* pUser_data_ptr)
|
||||
{
|
||||
const crn_comp_params& params = *(const crn_comp_params*)pUser_data_ptr;
|
||||
return params.m_pProgress_func(0, 2, percentage_complete, 100, params.m_pProgress_func_data) != 0;
|
||||
}
|
||||
|
||||
static bool progress_callback_func_phase_1(uint percentage_complete, void* pUser_data_ptr)
|
||||
{
|
||||
const crn_comp_params& params = *(const crn_comp_params*)pUser_data_ptr;
|
||||
return params.m_pProgress_func(1, 2, percentage_complete, 100, params.m_pProgress_func_data) != 0;
|
||||
}
|
||||
|
||||
bool dds_comp::convert_to_dxt(const crn_comp_params& params)
|
||||
{
|
||||
if ((params.m_quality_level == cCRNMaxQualityLevel) || (params.m_format == cCRNFmtDXT3))
|
||||
{
|
||||
m_packed_tex = m_src_tex;
|
||||
if (!m_packed_tex.convert(m_pixel_fmt, false, m_pack_params))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
const bool hierarchical = (params.m_flags & cCRNCompFlagHierarchical) != 0;
|
||||
|
||||
m_q1_params.m_quality_level = params.m_quality_level;
|
||||
m_q1_params.m_hierarchical = hierarchical;
|
||||
|
||||
m_q5_params.m_quality_level = params.m_quality_level;
|
||||
m_q5_params.m_hierarchical = hierarchical;
|
||||
|
||||
if (!m_pQDXT_state)
|
||||
{
|
||||
m_pQDXT_state = crnlib_new<dds_texture::qdxt_state>(m_task_pool);
|
||||
|
||||
if (params.m_pProgress_func)
|
||||
{
|
||||
m_q1_params.m_pProgress_func = progress_callback_func_phase_0;
|
||||
m_q1_params.m_pProgress_data = (void*)¶ms;
|
||||
m_q5_params.m_pProgress_func = progress_callback_func_phase_0;
|
||||
m_q5_params.m_pProgress_data = (void*)¶ms;
|
||||
}
|
||||
|
||||
if (!m_src_tex.qdxt_pack_init(*m_pQDXT_state, m_packed_tex, m_q1_params, m_q5_params, m_pixel_fmt, false))
|
||||
return false;
|
||||
|
||||
if (params.m_pProgress_func)
|
||||
{
|
||||
m_q1_params.m_pProgress_func = progress_callback_func_phase_1;
|
||||
m_q5_params.m_pProgress_func = progress_callback_func_phase_1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (params.m_pProgress_func)
|
||||
{
|
||||
m_q1_params.m_pProgress_func = progress_callback_func;
|
||||
m_q1_params.m_pProgress_data = (void*)¶ms;
|
||||
m_q5_params.m_pProgress_func = progress_callback_func;
|
||||
m_q5_params.m_pProgress_data = (void*)¶ms;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_src_tex.qdxt_pack(*m_pQDXT_state, m_packed_tex, m_q1_params, m_q5_params))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dds_comp::compress_init(const crn_comp_params& params)
|
||||
{
|
||||
clear();
|
||||
|
||||
m_pParams = ¶ms;
|
||||
|
||||
if ((math::minimum(m_pParams->m_width, m_pParams->m_height) < 1) || (math::maximum(m_pParams->m_width, m_pParams->m_height) > cCRNMaxLevelResolution))
|
||||
return false;
|
||||
|
||||
if (math::minimum(m_pParams->m_faces, m_pParams->m_levels) < 1)
|
||||
return false;
|
||||
|
||||
if (!create_dds_tex(m_src_tex))
|
||||
return false;
|
||||
|
||||
m_pack_params.init(*m_pParams);
|
||||
if (params.m_pProgress_func)
|
||||
{
|
||||
m_pack_params.m_pProgress_callback = progress_callback_func;
|
||||
m_pack_params.m_pProgress_callback_user_data_ptr = (void*)¶ms;
|
||||
}
|
||||
|
||||
m_pixel_fmt = pixel_format_helpers::convert_crn_format_to_pixel_format(static_cast<crn_format>(m_pParams->m_format));
|
||||
if (m_pixel_fmt == PIXEL_FMT_INVALID)
|
||||
return false;
|
||||
if ((m_pixel_fmt == PIXEL_FMT_DXT1) && (m_src_tex.has_alpha()) && (m_pack_params.m_use_both_block_types) && (m_pParams->m_flags & cCRNCompFlagDXT1AForTransparency))
|
||||
m_pixel_fmt = PIXEL_FMT_DXT1A;
|
||||
|
||||
if (!m_task_pool.init(m_pParams->m_num_helper_threads))
|
||||
return false;
|
||||
m_pack_params.m_pTask_pool = &m_task_pool;
|
||||
|
||||
const bool hierarchical = (params.m_flags & cCRNCompFlagHierarchical) != 0;
|
||||
m_q1_params.init(m_pack_params, params.m_quality_level, hierarchical);
|
||||
m_q5_params.init(m_pack_params, params.m_quality_level, hierarchical);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dds_comp::compress_pass(const crn_comp_params& params, float *pEffective_bitrate)
|
||||
{
|
||||
if (pEffective_bitrate) *pEffective_bitrate = 0.0f;
|
||||
|
||||
if (!m_pParams)
|
||||
return false;
|
||||
|
||||
if (!convert_to_dxt(params))
|
||||
return false;
|
||||
|
||||
dynamic_stream out_stream;
|
||||
out_stream.reserve(512*1024);
|
||||
data_stream_serializer serializer(out_stream);
|
||||
|
||||
if (!m_packed_tex.write_dds(serializer))
|
||||
return false;
|
||||
out_stream.reserve(0);
|
||||
|
||||
m_comp_data.swap(out_stream.get_buf());
|
||||
|
||||
if (pEffective_bitrate)
|
||||
{
|
||||
lzma_codec lossless_codec;
|
||||
|
||||
crnlib::vector<uint8> cmp_tex_bytes;
|
||||
if (lossless_codec.pack(m_comp_data.get_ptr(), m_comp_data.size(), cmp_tex_bytes))
|
||||
{
|
||||
uint comp_size = cmp_tex_bytes.size();
|
||||
if (comp_size)
|
||||
{
|
||||
*pEffective_bitrate = (comp_size * 8.0f) / m_src_tex.get_total_pixels_in_all_faces_and_mips();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void dds_comp::compress_deinit()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,48 @@
|
||||
// File: crn_comp.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
#include "crn_comp.h"
|
||||
#include "crn_dds_texture.h"
|
||||
#include "crn_texture_comp.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
class dds_comp : public itexture_comp
|
||||
{
|
||||
CRNLIB_NO_COPY_OR_ASSIGNMENT_OP(dds_comp);
|
||||
|
||||
public:
|
||||
dds_comp();
|
||||
virtual ~dds_comp();
|
||||
|
||||
virtual const wchar_t *get_ext() const { return L"DDS"; }
|
||||
|
||||
virtual bool compress_init(const crn_comp_params& params);
|
||||
virtual bool compress_pass(const crn_comp_params& params, float *pEffective_bitrate);
|
||||
virtual void compress_deinit();
|
||||
|
||||
virtual const crnlib::vector<uint8>& get_comp_data() const { return m_comp_data; }
|
||||
virtual crnlib::vector<uint8>& get_comp_data() { return m_comp_data; }
|
||||
|
||||
private:
|
||||
dds_texture m_src_tex;
|
||||
dds_texture m_packed_tex;
|
||||
|
||||
crnlib::vector<uint8> m_comp_data;
|
||||
|
||||
const crn_comp_params* m_pParams;
|
||||
|
||||
pixel_format m_pixel_fmt;
|
||||
dxt_image::pack_params m_pack_params;
|
||||
|
||||
task_pool m_task_pool;
|
||||
qdxt1_params m_q1_params;
|
||||
qdxt5_params m_q5_params;
|
||||
dds_texture::qdxt_state *m_pQDXT_state;
|
||||
|
||||
void clear();
|
||||
bool create_dds_tex(dds_texture &dds_tex);
|
||||
bool convert_to_dxt(const crn_comp_params& params);
|
||||
};
|
||||
|
||||
} // namespace crnlib
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,292 @@
|
||||
// File: crn_dds_texture.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
#include "crn_dxt_image.h"
|
||||
#include "../inc/dds_defs.h"
|
||||
#include "crn_pixel_format.h"
|
||||
#include "crn_image.h"
|
||||
#include "crn_resampler.h"
|
||||
#include "crn_data_stream_serializer.h"
|
||||
#include "crn_qdxt1.h"
|
||||
#include "crn_qdxt5.h"
|
||||
#include "crn_texture_file_types.h"
|
||||
#include "crn_image_utils.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
extern const vec2I g_vertical_cross_image_offsets[6];
|
||||
|
||||
class mip_level
|
||||
{
|
||||
friend class dds_texture;
|
||||
|
||||
public:
|
||||
mip_level();
|
||||
~mip_level();
|
||||
|
||||
mip_level(const mip_level& other);
|
||||
mip_level& operator= (const mip_level& rhs);
|
||||
|
||||
// Assumes ownership.
|
||||
void assign(image_u8* p, pixel_format fmt = PIXEL_FMT_INVALID);
|
||||
void assign(dxt_image* p, pixel_format fmt = PIXEL_FMT_INVALID);
|
||||
|
||||
void clear();
|
||||
|
||||
inline uint get_width() const { return m_width; }
|
||||
inline uint get_height() const { return m_height; }
|
||||
inline uint get_total_pixels() const { return m_width * m_height; }
|
||||
|
||||
inline image_u8* get_image() const { return m_pImage; }
|
||||
inline dxt_image* get_dxt_image() const { return m_pDXTImage; }
|
||||
|
||||
image_u8* get_unpacked_image(image_u8& tmp, bool uncook) const;
|
||||
|
||||
inline bool is_packed() const { return m_pDXTImage != NULL; }
|
||||
|
||||
inline bool is_valid() const { return (m_pImage != NULL) || (m_pDXTImage != NULL); }
|
||||
|
||||
inline pixel_format_helpers::component_flags get_comp_flags() const { return m_comp_flags; }
|
||||
inline void set_comp_flags(pixel_format_helpers::component_flags comp_flags) { m_comp_flags = comp_flags; }
|
||||
|
||||
inline pixel_format get_format() const { return m_format; }
|
||||
inline void set_format(pixel_format fmt) { m_format = fmt; }
|
||||
|
||||
bool convert(pixel_format fmt, bool cook, const dxt_image::pack_params& p);
|
||||
|
||||
bool pack_to_dxt(const image_u8& img, pixel_format fmt, bool cook, const dxt_image::pack_params& p);
|
||||
bool pack_to_dxt(pixel_format fmt, bool cook, const dxt_image::pack_params& p);
|
||||
|
||||
bool unpack_from_dxt(bool uncook = true);
|
||||
|
||||
bool set_alpha_to_luma();
|
||||
bool convert(image_utils::conversion_type conv_type);
|
||||
|
||||
private:
|
||||
uint m_width;
|
||||
uint m_height;
|
||||
|
||||
pixel_format_helpers::component_flags m_comp_flags;
|
||||
pixel_format m_format;
|
||||
|
||||
image_u8* m_pImage;
|
||||
dxt_image* m_pDXTImage;
|
||||
|
||||
void cook_image(image_u8& img) const;
|
||||
void uncook_image(image_u8& img) const;
|
||||
};
|
||||
|
||||
// A face is an array of mip_level ptr's.
|
||||
typedef crnlib::vector<mip_level*> mip_ptr_vec;
|
||||
|
||||
// And an array of one, six, or N faces make up a texture.
|
||||
typedef crnlib::vector<mip_ptr_vec> face_vec;
|
||||
|
||||
class dds_texture
|
||||
{
|
||||
public:
|
||||
// Construction/destruction
|
||||
dds_texture();
|
||||
~dds_texture();
|
||||
|
||||
dds_texture(const dds_texture& other);
|
||||
dds_texture& operator= (const dds_texture& rhs);
|
||||
|
||||
void clear();
|
||||
|
||||
void init(uint width, uint height, uint levels, uint faces, pixel_format fmt, const wchar_t* pName);
|
||||
|
||||
// Assumes ownership.
|
||||
void assign(face_vec& faces);
|
||||
void assign(mip_level* pLevel);
|
||||
void assign(image_u8* p, pixel_format fmt = PIXEL_FMT_INVALID);
|
||||
void assign(dxt_image* p, pixel_format fmt = PIXEL_FMT_INVALID);
|
||||
|
||||
void set(texture_file_types::format source_file_type, const dds_texture& dds_texture);
|
||||
|
||||
// Accessors
|
||||
image_u8* get_level_image(uint face, uint level, image_u8& img, bool uncook = true) const;
|
||||
|
||||
inline bool is_valid() const { return m_faces.size() > 0; }
|
||||
|
||||
const dynamic_wstring& get_name() const { return m_name; }
|
||||
void set_name(const dynamic_wstring& name) { m_name = name; }
|
||||
|
||||
const dynamic_wstring& get_source_filename() const { return get_name(); }
|
||||
texture_file_types::format get_source_file_type() const { return m_source_file_type; }
|
||||
|
||||
inline uint get_width() const { return m_width; }
|
||||
inline uint get_height() const { return m_height; }
|
||||
inline uint get_total_pixels() const { return m_width * m_height; }
|
||||
uint get_total_pixels_in_all_faces_and_mips() const;
|
||||
|
||||
inline uint get_num_faces() const { return m_faces.size(); }
|
||||
inline uint get_num_levels() const { if (m_faces.empty()) return 0; else return m_faces[0].size(); }
|
||||
|
||||
inline pixel_format_helpers::component_flags get_comp_flags() const { return m_comp_flags; }
|
||||
inline pixel_format get_format() const { return m_format; }
|
||||
|
||||
inline bool is_unpacked() const { if (get_num_faces()) { return get_level(0, 0)->get_image() != NULL; } return false; }
|
||||
|
||||
inline const mip_ptr_vec& get_face(uint face) const { return m_faces[face]; }
|
||||
inline mip_ptr_vec& get_face(uint face) { return m_faces[face]; }
|
||||
|
||||
inline const mip_level* get_level(uint face, uint mip) const { return m_faces[face][mip]; }
|
||||
inline mip_level* get_level(uint face, uint mip) { return m_faces[face][mip]; }
|
||||
|
||||
bool has_alpha() const;
|
||||
bool is_normal_map() const;
|
||||
bool is_vertical_cross() const;
|
||||
bool is_packed() const;
|
||||
texture_type determine_texture_type() const;
|
||||
|
||||
const dynamic_wstring& get_last_error() const { return m_last_error; }
|
||||
void clear_last_error() { m_last_error.clear(); }
|
||||
|
||||
// Loading/saving
|
||||
bool read_dds(const wchar_t* pFilename);
|
||||
bool read_dds(data_stream_serializer& serializer);
|
||||
|
||||
bool write_dds(const wchar_t* pFilename) const;
|
||||
bool write_dds(data_stream_serializer& serializer) const;
|
||||
|
||||
bool load_crn_from_memory(const wchar_t* pFilename, const void *pData, uint data_size);
|
||||
|
||||
// If file_format is texture_file_types::cFormatInvalid, the format will be determined from the filename's extension.
|
||||
bool load_from_file(const wchar_t* pFilename, texture_file_types::format file_format);
|
||||
|
||||
bool write_to_file(
|
||||
const wchar_t* pFilename,
|
||||
texture_file_types::format file_format,
|
||||
crn_comp_params* pCRN_comp_params,
|
||||
uint32 *pActual_quality_level, float *pActual_bitrate);
|
||||
|
||||
// Conversion
|
||||
bool convert(pixel_format fmt, bool cook, const dxt_image::pack_params& p);
|
||||
bool convert(pixel_format fmt, const dxt_image::pack_params& p);
|
||||
bool convert(pixel_format fmt, bool cook, const dxt_image::pack_params& p, int qdxt_quality, bool hierarchical = true);
|
||||
bool convert(image_utils::conversion_type conv_type);
|
||||
|
||||
bool unpack_from_dxt(bool uncook = true);
|
||||
|
||||
bool set_alpha_to_luma();
|
||||
|
||||
void discard_mipmaps();
|
||||
|
||||
void discard_mips();
|
||||
|
||||
struct resample_params
|
||||
{
|
||||
resample_params() :
|
||||
m_pFilter("kaiser"),
|
||||
m_wrapping(false),
|
||||
m_srgb(false),
|
||||
m_renormalize(false),
|
||||
m_filter_scale(.9f),
|
||||
m_gamma(1.75f), // or 2.2f
|
||||
m_multithreaded(true)
|
||||
{
|
||||
}
|
||||
|
||||
const char* m_pFilter;
|
||||
bool m_wrapping;
|
||||
bool m_srgb;
|
||||
bool m_renormalize;
|
||||
float m_filter_scale;
|
||||
float m_gamma;
|
||||
bool m_multithreaded;
|
||||
};
|
||||
|
||||
bool resize(uint new_width, uint new_height, const resample_params& params);
|
||||
|
||||
struct generate_mipmap_params : public resample_params
|
||||
{
|
||||
generate_mipmap_params() :
|
||||
resample_params(),
|
||||
m_min_mip_size(1),
|
||||
m_max_mips(0)
|
||||
{
|
||||
}
|
||||
|
||||
uint m_min_mip_size;
|
||||
uint m_max_mips; // actually the max # of total levels
|
||||
};
|
||||
|
||||
bool generate_mipmaps(const generate_mipmap_params& params, bool force);
|
||||
|
||||
bool crop(uint x, uint y, uint width, uint height);
|
||||
|
||||
bool vertical_cross_to_cubemap();
|
||||
|
||||
// Low-level clustered DXT (QDXT) compression
|
||||
struct qdxt_state
|
||||
{
|
||||
qdxt_state(task_pool& tp) : m_fmt(PIXEL_FMT_INVALID), m_qdxt1(tp), m_qdxt5a(tp), m_qdxt5b(tp)
|
||||
{
|
||||
}
|
||||
|
||||
pixel_format m_fmt;
|
||||
qdxt1 m_qdxt1;
|
||||
qdxt5 m_qdxt5a;
|
||||
qdxt5 m_qdxt5b;
|
||||
crnlib::vector<dxt_pixel_block> m_pixel_blocks;
|
||||
|
||||
qdxt1_params m_qdxt1_params;
|
||||
qdxt5_params m_qdxt5_params[2];
|
||||
bool m_has_blocks[3];
|
||||
|
||||
void clear()
|
||||
{
|
||||
m_fmt = PIXEL_FMT_INVALID;
|
||||
m_qdxt1.clear();
|
||||
m_qdxt5a.clear();
|
||||
m_qdxt5b.clear();
|
||||
m_pixel_blocks.clear();
|
||||
m_qdxt1_params.clear();
|
||||
m_qdxt5_params[0].clear();
|
||||
m_qdxt5_params[1].clear();
|
||||
utils::zero_object(m_has_blocks);
|
||||
}
|
||||
};
|
||||
bool qdxt_pack_init(qdxt_state& state, dds_texture& dst_tex, const qdxt1_params& dxt1_params, const qdxt5_params& dxt5_params, pixel_format fmt, bool cook);
|
||||
bool qdxt_pack(qdxt_state& state, dds_texture& dst_tex, const qdxt1_params& dxt1_params, const qdxt5_params& dxt5_params);
|
||||
|
||||
void swap(dds_texture& img);
|
||||
|
||||
bool check() const;
|
||||
|
||||
private:
|
||||
dynamic_wstring m_name;
|
||||
|
||||
uint m_width;
|
||||
uint m_height;
|
||||
|
||||
pixel_format_helpers::component_flags m_comp_flags;
|
||||
pixel_format m_format;
|
||||
|
||||
face_vec m_faces;
|
||||
|
||||
texture_file_types::format m_source_file_type;
|
||||
|
||||
mutable dynamic_wstring m_last_error;
|
||||
|
||||
inline void clear_last_error() const { m_last_error.clear(); }
|
||||
inline void set_last_error(const wchar_t* p) const { m_last_error = p; }
|
||||
|
||||
void free_all_mips();
|
||||
bool read_dds_internal(data_stream_serializer& serializer);
|
||||
bool load_regular(const wchar_t* pFilename, texture_file_types::format file_format);
|
||||
bool load_dds(const wchar_t* pFilename);
|
||||
bool load_crn(const wchar_t* pFilename);
|
||||
void print_crn_comp_params(const crn_comp_params& p);
|
||||
bool save_regular(const wchar_t* pFilename);
|
||||
bool save_dds(const wchar_t* pFilename);
|
||||
bool save_comp_texture(const wchar_t* pFilename, const crn_comp_params &comp_params, uint32 *pActual_quality_level, float *pActual_bitrate);
|
||||
};
|
||||
|
||||
inline void swap(dds_texture& a, dds_texture& b)
|
||||
{
|
||||
a.swap(b);
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,6 @@
|
||||
// File: crn_decomp.cpp
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#include "crn_core.h"
|
||||
|
||||
// Include the single-file header library with no defines, which brings in the full CRN decompressor.
|
||||
#include "../inc/crn_decomp.h"
|
||||
@@ -0,0 +1,381 @@
|
||||
// File: crn_dxt.cpp
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#include "crn_core.h"
|
||||
#include "crn_dxt.h"
|
||||
#include "crn_dxt1.h"
|
||||
#include "crn_ryg_dxt.hpp"
|
||||
#include "crn_dxt_fast.h"
|
||||
#include "crn_intersect.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
const uint8 g_dxt5_from_linear[cDXT5SelectorValues] = { 0U, 2U, 3U, 4U, 5U, 6U, 7U, 1U };
|
||||
const uint8 g_dxt5_to_linear[cDXT5SelectorValues] = { 0U, 7U, 1U, 2U, 3U, 4U, 5U, 6U };
|
||||
|
||||
const uint8 g_dxt5_alpha6_to_linear[cDXT5SelectorValues] = { 0U, 5U, 1U, 2U, 3U, 4U, 0U, 0U };
|
||||
|
||||
const uint8 g_dxt1_from_linear[cDXT1SelectorValues] = { 0U, 2U, 3U, 1U };
|
||||
const uint8 g_dxt1_to_linear[cDXT1SelectorValues] = { 0U, 3U, 1U, 2U };
|
||||
|
||||
const uint8 g_six_alpha_invert_table[cDXT5SelectorValues] = { 1, 0, 5, 4, 3, 2, 6, 7 };
|
||||
const uint8 g_eight_alpha_invert_table[cDXT5SelectorValues] = { 1, 0, 7, 6, 5, 4, 3, 2 };
|
||||
|
||||
const wchar_t* get_dxt_format_string(dxt_format fmt)
|
||||
{
|
||||
switch (fmt)
|
||||
{
|
||||
case cDXT1: return L"DXT1";
|
||||
case cDXT1A: return L"DXT1A";
|
||||
case cDXT3: return L"DXT3";
|
||||
case cDXT5: return L"DXT5";
|
||||
case cDXT5A: return L"DXT5A";
|
||||
case cDXN_XY: return L"DXN_XY";
|
||||
case cDXN_YX: return L"DXN_YX";
|
||||
default: break;
|
||||
}
|
||||
CRNLIB_ASSERT(false);
|
||||
return L"?";
|
||||
}
|
||||
|
||||
const wchar_t* get_dxt_compressor_name(crn_dxt_compressor_type c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case cCRNDXTCompressorCRN: return L"CRN";
|
||||
case cCRNDXTCompressorCRNF: return L"CRNF";
|
||||
case cCRNDXTCompressorRYG: return L"RYG";
|
||||
default: break;
|
||||
}
|
||||
CRNLIB_ASSERT(false);
|
||||
return L"?";
|
||||
}
|
||||
|
||||
uint get_dxt_format_bits_per_pixel(dxt_format fmt)
|
||||
{
|
||||
switch (fmt)
|
||||
{
|
||||
case cDXT1:
|
||||
case cDXT1A:
|
||||
case cDXT5A:
|
||||
return 4;
|
||||
case cDXT3:
|
||||
case cDXT5:
|
||||
case cDXN_XY:
|
||||
case cDXN_YX:
|
||||
return 8;
|
||||
default: break;
|
||||
}
|
||||
CRNLIB_ASSERT(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool get_dxt_format_has_alpha(dxt_format fmt)
|
||||
{
|
||||
switch (fmt)
|
||||
{
|
||||
case cDXT1A:
|
||||
case cDXT3:
|
||||
case cDXT5:
|
||||
case cDXT5A:
|
||||
return true;
|
||||
default: break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16 dxt1_block::pack_color(const color_quad_u8& color, bool scaled, uint bias)
|
||||
{
|
||||
uint r = color.r;
|
||||
uint g = color.g;
|
||||
uint b = color.b;
|
||||
|
||||
if (scaled)
|
||||
{
|
||||
r = (r * 31U + bias) / 255U;
|
||||
g = (g * 63U + bias) / 255U;
|
||||
b = (b * 31U + bias) / 255U;
|
||||
}
|
||||
|
||||
r = math::minimum(r, 31U);
|
||||
g = math::minimum(g, 63U);
|
||||
b = math::minimum(b, 31U);
|
||||
|
||||
return static_cast<uint16>(b | (g << 5U) | (r << 11U));
|
||||
}
|
||||
|
||||
uint16 dxt1_block::pack_color(uint r, uint g, uint b, bool scaled, uint bias)
|
||||
{
|
||||
return pack_color(color_quad_u8(r, g, b, 0), scaled, bias);
|
||||
}
|
||||
|
||||
color_quad_u8 dxt1_block::unpack_color(uint16 packed_color, bool scaled, uint alpha)
|
||||
{
|
||||
uint b = packed_color & 31U;
|
||||
uint g = (packed_color >> 5U) & 63U;
|
||||
uint r = (packed_color >> 11U) & 31U;
|
||||
|
||||
if (scaled)
|
||||
{
|
||||
b = (b << 3U) | (b >> 2U);
|
||||
g = (g << 2U) | (g >> 4U);
|
||||
r = (r << 3U) | (r >> 2U);
|
||||
}
|
||||
|
||||
return color_quad_u8(cNoClamp, r, g, b, math::minimum(alpha, 255U));
|
||||
}
|
||||
|
||||
void dxt1_block::unpack_color(uint& r, uint& g, uint& b, uint16 packed_color, bool scaled)
|
||||
{
|
||||
color_quad_u8 c(unpack_color(packed_color, scaled, 0));
|
||||
r = c.r;
|
||||
g = c.g;
|
||||
b = c.b;
|
||||
}
|
||||
|
||||
void dxt1_block::get_block_colors_NV5x(color_quad_u8* pDst, uint16 packed_col0, uint16 packed_col1, bool color4)
|
||||
{
|
||||
color_quad_u8 col0(unpack_color(packed_col0, false));
|
||||
color_quad_u8 col1(unpack_color(packed_col1, false));
|
||||
|
||||
pDst[0].r = (3 * col0.r * 22) / 8;
|
||||
pDst[0].b = (3 * col0.b * 22) / 8;
|
||||
pDst[0].g = (col0.g << 2) | (col0.g >> 4);
|
||||
pDst[0].a = 0xFF;
|
||||
|
||||
pDst[1].r = (3 * col1.r * 22) / 8;
|
||||
pDst[1].g = (col1.g << 2) | (col1.g >> 4);
|
||||
pDst[1].b = (3 * col1.b * 22) / 8;
|
||||
pDst[1].a = 0xFF;
|
||||
|
||||
int gdiff = pDst[1].g - pDst[0].g;
|
||||
|
||||
if (color4) //(packed_col0 > packed_col1)
|
||||
{
|
||||
pDst[2].r = static_cast<uint8>(((2 * col0.r + col1.r) * 22) / 8);
|
||||
pDst[2].g = static_cast<uint8>((256 * pDst[0].g + gdiff/4 + 128 + gdiff * 80) / 256);
|
||||
pDst[2].b = static_cast<uint8>(((2 * col0.b + col1.b) * 22) / 8);
|
||||
pDst[2].a = 0xFF;
|
||||
|
||||
pDst[3].r = static_cast<uint8>(((2 * col1.r + col0.r) * 22) / 8);
|
||||
pDst[3].g = static_cast<uint8>((256 * pDst[1].g - gdiff/4 + 128 - gdiff * 80) / 256);
|
||||
pDst[3].b = static_cast<uint8>(((2 * col1.b + col0.b) * 22) / 8);
|
||||
pDst[3].a = 0xFF;
|
||||
}
|
||||
else {
|
||||
pDst[2].r = static_cast<uint8>(((col0.r + col1.r) * 33) / 8);
|
||||
pDst[2].g = static_cast<uint8>((256 * pDst[0].g + gdiff/4 + 128 + gdiff * 128) / 256);
|
||||
pDst[2].b = static_cast<uint8>(((col0.b + col1.b) * 33) / 8);
|
||||
pDst[2].a = 0xFF;
|
||||
|
||||
pDst[3].r = 0x00;
|
||||
pDst[3].g = 0x00;
|
||||
pDst[3].b = 0x00;
|
||||
pDst[3].a = 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
uint dxt1_block::get_block_colors3(color_quad_u8* pDst, uint16 color0, uint16 color1)
|
||||
{
|
||||
color_quad_u8 c0(unpack_color(color0, true));
|
||||
color_quad_u8 c1(unpack_color(color1, true));
|
||||
|
||||
pDst[0] = c0;
|
||||
pDst[1] = c1;
|
||||
pDst[2].set_noclamp_rgba( (c0.r + c1.r) >> 1U, (c0.g + c1.g) >> 1U, (c0.b + c1.b) >> 1U, 255U);
|
||||
pDst[3].set_noclamp_rgba(0, 0, 0, 0);
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
uint dxt1_block::get_block_colors4(color_quad_u8* pDst, uint16 color0, uint16 color1)
|
||||
{
|
||||
color_quad_u8 c0(unpack_color(color0, true));
|
||||
color_quad_u8 c1(unpack_color(color1, true));
|
||||
|
||||
pDst[0] = c0;
|
||||
pDst[1] = c1;
|
||||
|
||||
// The compiler changes the div3 into a mul by recip+shift.
|
||||
pDst[2].set_noclamp_rgba( (c0.r * 2 + c1.r) / 3, (c0.g * 2 + c1.g) / 3, (c0.b * 2 + c1.b) / 3, 255U);
|
||||
pDst[3].set_noclamp_rgba( (c1.r * 2 + c0.r) / 3, (c1.g * 2 + c0.g) / 3, (c1.b * 2 + c0.b) / 3, 255U);
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
uint dxt1_block::get_block_colors3_round(color_quad_u8* pDst, uint16 color0, uint16 color1)
|
||||
{
|
||||
color_quad_u8 c0(unpack_color(color0, true));
|
||||
color_quad_u8 c1(unpack_color(color1, true));
|
||||
|
||||
pDst[0] = c0;
|
||||
pDst[1] = c1;
|
||||
pDst[2].set_noclamp_rgba( (c0.r + c1.r + 1) >> 1U, (c0.g + c1.g + 1) >> 1U, (c0.b + c1.b + 1) >> 1U, 255U);
|
||||
pDst[3].set_noclamp_rgba(0, 0, 0, 0);
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
uint dxt1_block::get_block_colors4_round(color_quad_u8* pDst, uint16 color0, uint16 color1)
|
||||
{
|
||||
color_quad_u8 c0(unpack_color(color0, true));
|
||||
color_quad_u8 c1(unpack_color(color1, true));
|
||||
|
||||
pDst[0] = c0;
|
||||
pDst[1] = c1;
|
||||
|
||||
// 12/14/08 - Supposed to round according to DX docs, but this conflicts with the OpenGL S3TC spec. ?
|
||||
// The compiler changes the div3 into a mul by recip+shift.
|
||||
pDst[2].set_noclamp_rgba( (c0.r * 2 + c1.r + 1) / 3, (c0.g * 2 + c1.g + 1) / 3, (c0.b * 2 + c1.b + 1) / 3, 255U);
|
||||
pDst[3].set_noclamp_rgba( (c1.r * 2 + c0.r + 1) / 3, (c1.g * 2 + c0.g + 1) / 3, (c1.b * 2 + c0.b + 1) / 3, 255U);
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
uint dxt1_block::get_block_colors(color_quad_u8* pDst, uint16 color0, uint16 color1)
|
||||
{
|
||||
if (color0 > color1)
|
||||
return get_block_colors4(pDst, color0, color1);
|
||||
else
|
||||
return get_block_colors3(pDst, color0, color1);
|
||||
}
|
||||
|
||||
uint dxt1_block::get_block_colors_round(color_quad_u8* pDst, uint16 color0, uint16 color1)
|
||||
{
|
||||
if (color0 > color1)
|
||||
return get_block_colors4_round(pDst, color0, color1);
|
||||
else
|
||||
return get_block_colors3_round(pDst, color0, color1);
|
||||
}
|
||||
|
||||
color_quad_u8 dxt1_block::unpack_endpoint(uint32 endpoints, uint index, bool scaled, uint alpha)
|
||||
{
|
||||
CRNLIB_ASSERT(index < 2);
|
||||
return unpack_color( static_cast<uint16>((endpoints >> (index * 16U)) & 0xFFFFU), scaled, alpha );
|
||||
}
|
||||
|
||||
uint dxt1_block::pack_endpoints(uint lo, uint hi)
|
||||
{
|
||||
CRNLIB_ASSERT((lo <= 0xFFFFU) && (hi <= 0xFFFFU));
|
||||
return lo | (hi << 16U);
|
||||
}
|
||||
|
||||
void dxt3_block::set_alpha(uint x, uint y, uint value, bool scaled)
|
||||
{
|
||||
CRNLIB_ASSERT((x < cDXTBlockSize) && (y < cDXTBlockSize));
|
||||
|
||||
if (scaled)
|
||||
{
|
||||
CRNLIB_ASSERT(value <= 0xFF);
|
||||
value = (value * 15U + 128U) / 255U;
|
||||
}
|
||||
else
|
||||
{
|
||||
CRNLIB_ASSERT(value <= 0xF);
|
||||
}
|
||||
|
||||
uint ofs = (y << 1U) + (x >> 1U);
|
||||
uint c = m_alpha[ofs];
|
||||
|
||||
c &= ~(0xF << ((x & 1U) << 2U));
|
||||
c |= (value << ((x & 1U) << 2U));
|
||||
|
||||
m_alpha[ofs] = static_cast<uint8>(c);
|
||||
}
|
||||
|
||||
uint dxt3_block::get_alpha(uint x, uint y, bool scaled) const
|
||||
{
|
||||
CRNLIB_ASSERT((x < cDXTBlockSize) && (y < cDXTBlockSize));
|
||||
|
||||
uint value = m_alpha[(y << 1U) + (x >> 1U)];
|
||||
if (x & 1)
|
||||
value >>= 4;
|
||||
value &= 0xF;
|
||||
|
||||
if (scaled)
|
||||
value = (value << 4U) | value;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
uint dxt5_block::get_block_values6(color_quad_u8* pDst, uint l, uint h)
|
||||
{
|
||||
pDst[0].a = static_cast<uint8>(l);
|
||||
pDst[1].a = static_cast<uint8>(h);
|
||||
pDst[2].a = static_cast<uint8>((l * 4 + h ) / 5);
|
||||
pDst[3].a = static_cast<uint8>((l * 3 + h * 2) / 5);
|
||||
pDst[4].a = static_cast<uint8>((l * 2 + h * 3) / 5);
|
||||
pDst[5].a = static_cast<uint8>((l + h * 4) / 5);
|
||||
pDst[6].a = 0;
|
||||
pDst[7].a = 255;
|
||||
return 6;
|
||||
}
|
||||
|
||||
uint dxt5_block::get_block_values8(color_quad_u8* pDst, uint l, uint h)
|
||||
{
|
||||
pDst[0].a = static_cast<uint8>(l);
|
||||
pDst[1].a = static_cast<uint8>(h);
|
||||
pDst[2].a = static_cast<uint8>((l * 6 + h ) / 7);
|
||||
pDst[3].a = static_cast<uint8>((l * 5 + h * 2) / 7);
|
||||
pDst[4].a = static_cast<uint8>((l * 4 + h * 3) / 7);
|
||||
pDst[5].a = static_cast<uint8>((l * 3 + h * 4) / 7);
|
||||
pDst[6].a = static_cast<uint8>((l * 2 + h * 5) / 7);
|
||||
pDst[7].a = static_cast<uint8>((l + h * 6) / 7);
|
||||
return 8;
|
||||
}
|
||||
|
||||
uint dxt5_block::get_block_values(color_quad_u8* pDst, uint l, uint h)
|
||||
{
|
||||
if (l > h)
|
||||
return get_block_values8(pDst, l, h);
|
||||
else
|
||||
return get_block_values6(pDst, l, h);
|
||||
}
|
||||
|
||||
uint dxt5_block::get_block_values6(uint* pDst, uint l, uint h)
|
||||
{
|
||||
pDst[0] = l;
|
||||
pDst[1] = h;
|
||||
pDst[2] = (l * 4 + h ) / 5;
|
||||
pDst[3] = (l * 3 + h * 2) / 5;
|
||||
pDst[4] = (l * 2 + h * 3) / 5;
|
||||
pDst[5] = (l + h * 4) / 5;
|
||||
pDst[6] = 0;
|
||||
pDst[7] = 255;
|
||||
return 6;
|
||||
}
|
||||
|
||||
uint dxt5_block::get_block_values8(uint* pDst, uint l, uint h)
|
||||
{
|
||||
pDst[0] = l;
|
||||
pDst[1] = h;
|
||||
pDst[2] = (l * 6 + h ) / 7;
|
||||
pDst[3] = (l * 5 + h * 2) / 7;
|
||||
pDst[4] = (l * 4 + h * 3) / 7;
|
||||
pDst[5] = (l * 3 + h * 4) / 7;
|
||||
pDst[6] = (l * 2 + h * 5) / 7;
|
||||
pDst[7] = (l + h * 6) / 7;
|
||||
return 8;
|
||||
}
|
||||
|
||||
uint dxt5_block::unpack_endpoint(uint packed, uint index)
|
||||
{
|
||||
CRNLIB_ASSERT(index < 2);
|
||||
return (packed >> (8 * index)) & 0xFF;
|
||||
}
|
||||
|
||||
uint dxt5_block::pack_endpoints(uint lo, uint hi)
|
||||
{
|
||||
CRNLIB_ASSERT((lo <= 0xFF) && (hi <= 0xFF));
|
||||
return lo | (hi << 8U);
|
||||
}
|
||||
|
||||
uint dxt5_block::get_block_values(uint* pDst, uint l, uint h)
|
||||
{
|
||||
if (l > h)
|
||||
return get_block_values8(pDst, l, h);
|
||||
else
|
||||
return get_block_values6(pDst, l, h);
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
|
||||
@@ -0,0 +1,281 @@
|
||||
// File: crn_dxt.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
#include "../inc/crnlib.h"
|
||||
#include "crn_color.h"
|
||||
#include "crn_vec.h"
|
||||
#include "crn_rand.h"
|
||||
#include "crn_sparse_bit_array.h"
|
||||
#include "crn_hash_map.h"
|
||||
#include <map>
|
||||
|
||||
#define CRNLIB_DXT_ALT_ROUNDING 1
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
enum dxt_constants
|
||||
{
|
||||
cDXT1BytesPerBlock = 8U,
|
||||
cDXT5NBytesPerBlock = 16U,
|
||||
|
||||
cDXT5SelectorBits = 3U,
|
||||
cDXT5SelectorValues = 1U << cDXT5SelectorBits,
|
||||
cDXT5SelectorMask = cDXT5SelectorValues - 1U,
|
||||
|
||||
cDXT1SelectorBits = 2U,
|
||||
cDXT1SelectorValues = 1U << cDXT1SelectorBits,
|
||||
cDXT1SelectorMask = cDXT1SelectorValues - 1U,
|
||||
|
||||
cDXTBlockShift = 2U,
|
||||
cDXTBlockSize = 1U << cDXTBlockShift
|
||||
};
|
||||
|
||||
enum dxt_format
|
||||
{
|
||||
cDXTInvalid = -1,
|
||||
|
||||
// cDXT1/1A must appear first!
|
||||
cDXT1,
|
||||
cDXT1A,
|
||||
|
||||
cDXT3,
|
||||
cDXT5,
|
||||
cDXT5A,
|
||||
|
||||
cDXN_XY, // inverted relative to standard ATI2, 360's DXN
|
||||
cDXN_YX // standard ATI2
|
||||
};
|
||||
|
||||
const float cDXT1MaxLinearValue = 3.0f;
|
||||
const float cDXT1InvMaxLinearValue = 1.0f/3.0f;
|
||||
|
||||
const float cDXT5MaxLinearValue = 7.0f;
|
||||
const float cDXT5InvMaxLinearValue = 1.0f/7.0f;
|
||||
|
||||
// Converts DXT1 raw color selector index to a linear value.
|
||||
extern const uint8 g_dxt1_to_linear[cDXT1SelectorValues];
|
||||
|
||||
// Converts DXT5 raw alpha selector index to a linear value.
|
||||
extern const uint8 g_dxt5_to_linear[cDXT5SelectorValues];
|
||||
|
||||
// Converts DXT1 linear color selector index to a raw value (inverse of g_dxt1_to_linear).
|
||||
extern const uint8 g_dxt1_from_linear[cDXT1SelectorValues];
|
||||
|
||||
// Converts DXT5 linear alpha selector index to a raw value (inverse of g_dxt5_to_linear).
|
||||
extern const uint8 g_dxt5_from_linear[cDXT5SelectorValues];
|
||||
|
||||
extern const uint8 g_dxt5_alpha6_to_linear[cDXT5SelectorValues];
|
||||
|
||||
extern const uint8 g_six_alpha_invert_table[cDXT5SelectorValues];
|
||||
extern const uint8 g_eight_alpha_invert_table[cDXT5SelectorValues];
|
||||
|
||||
const wchar_t* get_dxt_format_string(dxt_format fmt);
|
||||
uint get_dxt_format_bits_per_pixel(dxt_format fmt);
|
||||
bool get_dxt_format_has_alpha(dxt_format fmt);
|
||||
|
||||
const wchar_t* get_dxt_quality_string(crn_dxt_quality q);
|
||||
|
||||
const wchar_t* get_dxt_compressor_name(crn_dxt_compressor_type c);
|
||||
|
||||
struct dxt1_block
|
||||
{
|
||||
uint8 m_low_color[2];
|
||||
uint8 m_high_color[2];
|
||||
|
||||
enum { cNumSelectorBytes = 4 };
|
||||
uint8 m_selectors[cNumSelectorBytes];
|
||||
|
||||
inline void clear()
|
||||
{
|
||||
utils::zero_this(this);
|
||||
}
|
||||
|
||||
// These methods assume the in-memory rep is in LE byte order.
|
||||
inline uint get_low_color() const
|
||||
{
|
||||
return m_low_color[0] | (m_low_color[1] << 8U);
|
||||
}
|
||||
|
||||
inline uint get_high_color() const
|
||||
{
|
||||
return m_high_color[0] | (m_high_color[1] << 8U);
|
||||
}
|
||||
|
||||
inline void set_low_color(uint16 c)
|
||||
{
|
||||
m_low_color[0] = static_cast<uint8>(c & 0xFF);
|
||||
m_low_color[1] = static_cast<uint8>((c >> 8) & 0xFF);
|
||||
}
|
||||
|
||||
inline void set_high_color(uint16 c)
|
||||
{
|
||||
m_high_color[0] = static_cast<uint8>(c & 0xFF);
|
||||
m_high_color[1] = static_cast<uint8>((c >> 8) & 0xFF);
|
||||
}
|
||||
|
||||
inline bool is_constant_color_block() const { return get_low_color() == get_high_color(); }
|
||||
inline bool is_alpha_block() const { return get_low_color() <= get_high_color(); }
|
||||
inline bool is_non_alpha_block() const { return !is_alpha_block(); }
|
||||
|
||||
inline uint get_selector(uint x, uint y) const
|
||||
{
|
||||
CRNLIB_ASSERT((x < 4U) && (y < 4U));
|
||||
return (m_selectors[y] >> (x * cDXT1SelectorBits)) & cDXT1SelectorMask;
|
||||
}
|
||||
|
||||
inline void set_selector(uint x, uint y, uint val)
|
||||
{
|
||||
CRNLIB_ASSERT((x < 4U) && (y < 4U) && (val < 4U));
|
||||
|
||||
m_selectors[y] &= (~(cDXT1SelectorMask << (x * cDXT1SelectorBits)));
|
||||
m_selectors[y] |= (val << (x * cDXT1SelectorBits));
|
||||
}
|
||||
|
||||
static uint16 pack_color(const color_quad_u8& color, bool scaled, uint bias = 127U);
|
||||
static uint16 pack_color(uint r, uint g, uint b, bool scaled, uint bias = 127U);
|
||||
|
||||
static color_quad_u8 unpack_color(uint16 packed_color, bool scaled, uint alpha = 255U);
|
||||
static void unpack_color(uint& r, uint& g, uint& b, uint16 packed_color, bool scaled);
|
||||
|
||||
static uint get_block_colors3(color_quad_u8* pDst, uint16 color0, uint16 color1);
|
||||
static uint get_block_colors3_round(color_quad_u8* pDst, uint16 color0, uint16 color1);
|
||||
|
||||
static uint get_block_colors4(color_quad_u8* pDst, uint16 color0, uint16 color1);
|
||||
static uint get_block_colors4_round(color_quad_u8* pDst, uint16 color0, uint16 color1);
|
||||
|
||||
// pDst must point to an array at least cDXT1SelectorValues long.
|
||||
static uint get_block_colors(color_quad_u8* pDst, uint16 color0, uint16 color1);
|
||||
|
||||
static uint get_block_colors_round(color_quad_u8* pDst, uint16 color0, uint16 color1);
|
||||
|
||||
static color_quad_u8 unpack_endpoint(uint32 endpoints, uint index, bool scaled, uint alpha = 255U);
|
||||
static uint pack_endpoints(uint lo, uint hi);
|
||||
|
||||
static void get_block_colors_NV5x(color_quad_u8* pDst, uint16 packed_col0, uint16 packed_col1, bool color4);
|
||||
};
|
||||
|
||||
CRNLIB_DEFINE_BITWISE_COPYABLE(dxt1_block);
|
||||
|
||||
struct dxt3_block
|
||||
{
|
||||
enum { cNumAlphaBytes = 8 };
|
||||
uint8 m_alpha[cNumAlphaBytes];
|
||||
|
||||
void set_alpha(uint x, uint y, uint value, bool scaled);
|
||||
uint get_alpha(uint x, uint y, bool scaled) const;
|
||||
};
|
||||
|
||||
CRNLIB_DEFINE_BITWISE_COPYABLE(dxt3_block);
|
||||
|
||||
struct dxt5_block
|
||||
{
|
||||
uint8 m_endpoints[2];
|
||||
|
||||
enum { cNumSelectorBytes = 6 };
|
||||
uint8 m_selectors[cNumSelectorBytes];
|
||||
|
||||
inline void clear()
|
||||
{
|
||||
utils::zero_this(this);
|
||||
}
|
||||
|
||||
inline uint get_low_alpha() const
|
||||
{
|
||||
return m_endpoints[0];
|
||||
}
|
||||
|
||||
inline uint get_high_alpha() const
|
||||
{
|
||||
return m_endpoints[1];
|
||||
}
|
||||
|
||||
inline void set_low_alpha(uint i)
|
||||
{
|
||||
CRNLIB_ASSERT(i <= UINT8_MAX);
|
||||
m_endpoints[0] = static_cast<uint8>(i);
|
||||
}
|
||||
|
||||
inline void set_high_alpha(uint i)
|
||||
{
|
||||
CRNLIB_ASSERT(i <= UINT8_MAX);
|
||||
m_endpoints[1] = static_cast<uint8>(i);
|
||||
}
|
||||
|
||||
inline bool is_alpha6_block() const { return get_low_alpha() <= get_high_alpha(); }
|
||||
|
||||
uint get_endpoints_as_word() const { return m_endpoints[0] | (m_endpoints[1] << 8); }
|
||||
uint get_selectors_as_word(uint index) { CRNLIB_ASSERT(index < 3); return m_selectors[index * 2] | (m_selectors[index * 2 + 1] << 8); }
|
||||
|
||||
inline uint get_selector(uint x, uint y) const
|
||||
{
|
||||
CRNLIB_ASSERT((x < 4U) && (y < 4U));
|
||||
|
||||
uint selector_index = (y * 4) + x;
|
||||
uint bit_index = selector_index * cDXT5SelectorBits;
|
||||
|
||||
uint byte_index = bit_index >> 3;
|
||||
uint bit_ofs = bit_index & 7;
|
||||
|
||||
uint v = m_selectors[byte_index];
|
||||
if (byte_index < (cNumSelectorBytes - 1))
|
||||
v |= (m_selectors[byte_index + 1] << 8);
|
||||
|
||||
return (v >> bit_ofs) & 7;
|
||||
}
|
||||
|
||||
inline void set_selector(uint x, uint y, uint val)
|
||||
{
|
||||
CRNLIB_ASSERT((x < 4U) && (y < 4U) && (val < 8U));
|
||||
|
||||
uint selector_index = (y * 4) + x;
|
||||
uint bit_index = selector_index * cDXT5SelectorBits;
|
||||
|
||||
uint byte_index = bit_index >> 3;
|
||||
uint bit_ofs = bit_index & 7;
|
||||
|
||||
uint v = m_selectors[byte_index];
|
||||
if (byte_index < (cNumSelectorBytes - 1))
|
||||
v |= (m_selectors[byte_index + 1] << 8);
|
||||
|
||||
v &= (~(7 << bit_ofs));
|
||||
v |= (val << bit_ofs);
|
||||
|
||||
m_selectors[byte_index] = static_cast<uint8>(v);
|
||||
if (byte_index < (cNumSelectorBytes - 1))
|
||||
m_selectors[byte_index + 1] = static_cast<uint8>(v >> 8);
|
||||
}
|
||||
|
||||
enum { cMaxSelectorValues = 8 };
|
||||
|
||||
// Results written to alpha channel.
|
||||
static uint get_block_values6(color_quad_u8* pDst, uint l, uint h);
|
||||
static uint get_block_values8(color_quad_u8* pDst, uint l, uint h);
|
||||
static uint get_block_values(color_quad_u8* pDst, uint l, uint h);
|
||||
|
||||
static uint get_block_values6(uint* pDst, uint l, uint h);
|
||||
static uint get_block_values8(uint* pDst, uint l, uint h);
|
||||
// pDst must point to an array at least cDXT5SelectorValues long.
|
||||
static uint get_block_values(uint* pDst, uint l, uint h);
|
||||
|
||||
static uint unpack_endpoint(uint packed, uint index);
|
||||
static uint pack_endpoints(uint lo, uint hi);
|
||||
};
|
||||
|
||||
CRNLIB_DEFINE_BITWISE_COPYABLE(dxt5_block);
|
||||
|
||||
struct dxt_pixel_block
|
||||
{
|
||||
color_quad_u8 m_pixels[cDXTBlockSize][cDXTBlockSize]; // [y][x]
|
||||
|
||||
inline void clear()
|
||||
{
|
||||
utils::zero_object(*this);
|
||||
}
|
||||
};
|
||||
|
||||
CRNLIB_DEFINE_BITWISE_COPYABLE(dxt_pixel_block);
|
||||
|
||||
} // namespace crnlib
|
||||
|
||||
|
||||
|
||||
+2138
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,352 @@
|
||||
// File: crn_dxt1.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
#include "crn_dxt.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
struct dxt1_solution_coordinates
|
||||
{
|
||||
inline dxt1_solution_coordinates() : m_low_color(0), m_high_color(0){ }
|
||||
|
||||
inline dxt1_solution_coordinates(uint16 l, uint16 h) : m_low_color(l), m_high_color(h) { }
|
||||
|
||||
inline dxt1_solution_coordinates(const color_quad_u8& l, const color_quad_u8& h, bool scaled = true) :
|
||||
m_low_color(dxt1_block::pack_color(l, scaled)),
|
||||
m_high_color(dxt1_block::pack_color(h, scaled))
|
||||
{
|
||||
}
|
||||
|
||||
inline dxt1_solution_coordinates(vec3F nl, vec3F nh)
|
||||
{
|
||||
#if CRNLIB_DXT_ALT_ROUNDING
|
||||
// Umm, wtf?
|
||||
nl.clamp(0.0f, .999f);
|
||||
nh.clamp(0.0f, .999f);
|
||||
color_quad_u8 l( (int)floor(nl[0] * 32.0f), (int)floor(nl[1] * 64.0f), (int)floor(nl[2] * 32.0f), 255);
|
||||
color_quad_u8 h( (int)floor(nh[0] * 32.0f), (int)floor(nh[1] * 64.0f), (int)floor(nh[2] * 32.0f), 255);
|
||||
#else
|
||||
// Fixes the bins
|
||||
color_quad_u8 l( (int)floor(.5f + nl[0] * 31.0f), (int)floor(.5f + nl[1] * 63.0f), (int)floor(.5f + nl[2] * 31.0f), 255);
|
||||
color_quad_u8 h( (int)floor(.5f + nh[0] * 31.0f), (int)floor(.5f + nh[1] * 63.0f), (int)floor(.5f + nh[2] * 31.0f), 255);
|
||||
#endif
|
||||
|
||||
m_low_color = dxt1_block::pack_color(l, false);
|
||||
m_high_color = dxt1_block::pack_color(h, false);
|
||||
}
|
||||
|
||||
uint16 m_low_color;
|
||||
uint16 m_high_color;
|
||||
|
||||
inline void clear()
|
||||
{
|
||||
m_low_color = 0;
|
||||
m_high_color = 0;
|
||||
}
|
||||
|
||||
inline dxt1_solution_coordinates& canonicalize()
|
||||
{
|
||||
if (m_low_color < m_high_color)
|
||||
utils::swap(m_low_color, m_high_color);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline operator size_t() const { return fast_hash(this, sizeof(*this)); }
|
||||
|
||||
inline bool operator== (const dxt1_solution_coordinates& other) const
|
||||
{
|
||||
uint16 l0 = math::minimum(m_low_color, m_high_color);
|
||||
uint16 h0 = math::maximum(m_low_color, m_high_color);
|
||||
|
||||
uint16 l1 = math::minimum(other.m_low_color, other.m_high_color);
|
||||
uint16 h1 = math::maximum(other.m_low_color, other.m_high_color);
|
||||
|
||||
return (l0 == l1) && (h0 == h1);
|
||||
}
|
||||
|
||||
inline bool operator!= (const dxt1_solution_coordinates& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
inline bool operator< (const dxt1_solution_coordinates& other) const
|
||||
{
|
||||
uint16 l0 = math::minimum(m_low_color, m_high_color);
|
||||
uint16 h0 = math::maximum(m_low_color, m_high_color);
|
||||
|
||||
uint16 l1 = math::minimum(other.m_low_color, other.m_high_color);
|
||||
uint16 h1 = math::maximum(other.m_low_color, other.m_high_color);
|
||||
|
||||
if (l0 < l1)
|
||||
return true;
|
||||
else if (l0 == l1)
|
||||
{
|
||||
if (h0 < h1)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
typedef crnlib::vector<dxt1_solution_coordinates> dxt1_solution_coordinates_vec;
|
||||
|
||||
CRNLIB_DEFINE_BITWISE_COPYABLE(dxt1_solution_coordinates);
|
||||
|
||||
struct unique_color
|
||||
{
|
||||
inline unique_color() { }
|
||||
inline unique_color(const color_quad_u8& color, uint weight) : m_color(color), m_weight(weight) { }
|
||||
|
||||
color_quad_u8 m_color;
|
||||
uint m_weight;
|
||||
|
||||
inline bool operator< (const unique_color& c) const
|
||||
{
|
||||
return *reinterpret_cast<const uint32*>(&m_color) < *reinterpret_cast<const uint32*>(&c.m_color);
|
||||
}
|
||||
|
||||
inline bool operator== (const unique_color& c) const
|
||||
{
|
||||
return *reinterpret_cast<const uint32*>(&m_color) == *reinterpret_cast<const uint32*>(&c.m_color);
|
||||
}
|
||||
};
|
||||
|
||||
CRNLIB_DEFINE_BITWISE_COPYABLE(unique_color);
|
||||
|
||||
class dxt1_endpoint_optimizer
|
||||
{
|
||||
public:
|
||||
dxt1_endpoint_optimizer();
|
||||
|
||||
struct params
|
||||
{
|
||||
params() :
|
||||
m_block_index(0),
|
||||
m_pPixels(NULL),
|
||||
m_num_pixels(0),
|
||||
m_dxt1a_alpha_threshold(128U),
|
||||
m_quality(cCRNDXTQualityUber),
|
||||
m_pixels_have_alpha(false),
|
||||
m_use_alpha_blocks(true),
|
||||
m_perceptual(true),
|
||||
m_grayscale_sampling(false),
|
||||
m_endpoint_caching(true),
|
||||
m_use_transparent_indices_for_black(false),
|
||||
m_force_alpha_blocks(false)
|
||||
{
|
||||
m_color_weights[0] = 1;
|
||||
m_color_weights[1] = 1;
|
||||
m_color_weights[2] = 1;
|
||||
}
|
||||
|
||||
uint m_block_index;
|
||||
|
||||
const color_quad_u8* m_pPixels;
|
||||
uint m_num_pixels;
|
||||
uint m_dxt1a_alpha_threshold;
|
||||
|
||||
crn_dxt_quality m_quality;
|
||||
|
||||
bool m_pixels_have_alpha;
|
||||
bool m_use_alpha_blocks;
|
||||
bool m_perceptual;
|
||||
bool m_grayscale_sampling;
|
||||
bool m_endpoint_caching;
|
||||
bool m_use_transparent_indices_for_black;
|
||||
bool m_force_alpha_blocks;
|
||||
int m_color_weights[3];
|
||||
};
|
||||
|
||||
struct results
|
||||
{
|
||||
inline results() : m_pSelectors(NULL) { }
|
||||
|
||||
uint64 m_error;
|
||||
|
||||
uint16 m_low_color;
|
||||
uint16 m_high_color;
|
||||
|
||||
uint8* m_pSelectors;
|
||||
bool m_alpha_block;
|
||||
};
|
||||
|
||||
struct solution
|
||||
{
|
||||
solution() { }
|
||||
|
||||
solution(const solution& other)
|
||||
{
|
||||
m_results = other.m_results;
|
||||
m_selectors = other.m_selectors;
|
||||
m_results.m_pSelectors = m_selectors.begin();
|
||||
}
|
||||
|
||||
solution& operator= (const solution& rhs)
|
||||
{
|
||||
if (this == &rhs)
|
||||
return *this;
|
||||
|
||||
m_results = rhs.m_results;
|
||||
m_selectors = rhs.m_selectors;
|
||||
m_results.m_pSelectors = m_selectors.begin();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
results m_results;
|
||||
crnlib::vector<uint8> m_selectors;
|
||||
|
||||
inline bool operator< (const solution& other) const
|
||||
{
|
||||
return m_results.m_error < other.m_results.m_error;
|
||||
}
|
||||
static inline bool coords_equal(const solution& lhs, const solution& rhs)
|
||||
{
|
||||
return (lhs.m_results.m_low_color == rhs.m_results.m_low_color) && (lhs.m_results.m_high_color == rhs.m_results.m_high_color);
|
||||
}
|
||||
};
|
||||
typedef crnlib::vector<solution> solution_vec;
|
||||
|
||||
bool compute(const params& p, results& r, solution_vec* pSolutions = NULL);
|
||||
|
||||
private:
|
||||
const params* m_pParams;
|
||||
results* m_pResults;
|
||||
solution_vec* m_pSolutions;
|
||||
|
||||
bool m_perceptual;
|
||||
bool m_has_color_weighting;
|
||||
|
||||
typedef crnlib::vector<unique_color> unique_color_vec;
|
||||
|
||||
//typedef crnlib::hash_map<uint32, uint32, bit_hasher<uint32> > unique_color_hash_map;
|
||||
typedef crnlib::hash_map<uint32, uint32> unique_color_hash_map;
|
||||
unique_color_hash_map m_unique_color_hash_map;
|
||||
|
||||
unique_color_vec m_unique_colors; // excludes transparent colors!
|
||||
unique_color_vec m_temp_unique_colors;
|
||||
|
||||
uint m_total_unique_color_weight;
|
||||
|
||||
bool m_has_transparent_pixels;
|
||||
|
||||
vec3F_array m_norm_unique_colors;
|
||||
vec3F m_mean_norm_color;
|
||||
|
||||
vec3F_array m_norm_unique_colors_weighted;
|
||||
vec3F m_mean_norm_color_weighted;
|
||||
|
||||
vec3F m_principle_axis;
|
||||
|
||||
bool m_all_pixels_grayscale;
|
||||
|
||||
crnlib::vector<uint16> m_unique_packed_colors;
|
||||
crnlib::vector<uint8> m_trial_selectors;
|
||||
|
||||
crnlib::vector<vec3F> m_low_coords;
|
||||
crnlib::vector<vec3F> m_high_coords;
|
||||
|
||||
enum { cMaxPrevResults = 4 };
|
||||
dxt1_solution_coordinates m_prev_results[cMaxPrevResults];
|
||||
uint m_num_prev_results;
|
||||
|
||||
crnlib::vector<vec3I> m_lo_cells;
|
||||
crnlib::vector<vec3I> m_hi_cells;
|
||||
|
||||
uint m_total_evals;
|
||||
|
||||
struct potential_solution
|
||||
{
|
||||
potential_solution() : m_coords(), m_error(UINT64_MAX), m_alpha_block(false), m_valid(false)
|
||||
{
|
||||
}
|
||||
|
||||
dxt1_solution_coordinates m_coords;
|
||||
crnlib::vector<uint8> m_selectors;
|
||||
uint64 m_error;
|
||||
bool m_alpha_block;
|
||||
bool m_valid;
|
||||
|
||||
void clear()
|
||||
{
|
||||
m_coords.clear();
|
||||
m_selectors.resize(0);
|
||||
m_error = UINT64_MAX;
|
||||
m_alpha_block = false;
|
||||
m_valid = false;
|
||||
}
|
||||
|
||||
bool are_selectors_all_equal() const
|
||||
{
|
||||
if (m_selectors.empty())
|
||||
return false;
|
||||
const uint s = m_selectors[0];
|
||||
for (uint i = 1; i < m_selectors.size(); i++)
|
||||
if (m_selectors[i] != s)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
potential_solution m_trial_solution;
|
||||
potential_solution m_best_solution;
|
||||
|
||||
typedef crnlib::hash_map<uint, empty_type> solution_hash_map;
|
||||
solution_hash_map m_solutions_tried;
|
||||
|
||||
bool refine_solution(int refinement_level = 0);
|
||||
|
||||
bool evaluate_solution(
|
||||
const dxt1_solution_coordinates& coords,
|
||||
bool early_out,
|
||||
potential_solution* pBest_solution,
|
||||
bool alternate_rounding = false);
|
||||
|
||||
bool evaluate_solution_uber(
|
||||
potential_solution& solution,
|
||||
const dxt1_solution_coordinates& coords,
|
||||
bool early_out,
|
||||
potential_solution* pBest_solution,
|
||||
bool alternate_rounding = false);
|
||||
|
||||
bool evaluate_solution_fast(
|
||||
potential_solution& solution,
|
||||
const dxt1_solution_coordinates& coords,
|
||||
bool early_out,
|
||||
potential_solution* pBest_solution,
|
||||
bool alternate_rounding = false);
|
||||
|
||||
void clear();
|
||||
void find_unique_colors();
|
||||
bool handle_all_transparent_block();
|
||||
bool handle_solid_block();
|
||||
bool handle_multicolor_block();
|
||||
bool handle_grayscale_block();
|
||||
void compute_pca(vec3F& axis, const vec3F_array& norm_colors, const vec3F& def);
|
||||
void compute_vectors(const vec3F& perceptual_weights);
|
||||
void return_solution(results& results, const potential_solution& solution);
|
||||
void try_combinatorial_encoding();
|
||||
void optimize_endpoint_comps();
|
||||
bool optimize_endpoints(vec3F& low_color, vec3F& high_color);
|
||||
bool try_alpha_as_black_optimization();
|
||||
bool try_average_block_as_solid();
|
||||
bool try_median4(const vec3F& low_color, const vec3F& high_color);
|
||||
|
||||
bool compute_internal(const params& p, results& r, solution_vec* pSolutions);
|
||||
|
||||
unique_color lerp_color(const color_quad_u8& a, const color_quad_u8& b, float f, int rounding = 1);
|
||||
|
||||
inline uint color_distance(bool perceptual, const color_quad_u8& e1, const color_quad_u8& e2, bool alpha);
|
||||
|
||||
static inline vec3F unpack_to_vec3F_raw(uint16 packed_color);
|
||||
static inline vec3F unpack_to_vec3F(uint16 packed_color);
|
||||
};
|
||||
|
||||
inline void swap(dxt1_endpoint_optimizer::solution& a, dxt1_endpoint_optimizer::solution& b)
|
||||
{
|
||||
std::swap(a.m_results, b.m_results);
|
||||
a.m_selectors.swap(b.m_selectors);
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,209 @@
|
||||
// File: crn_dxt5a.cpp
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#include "crn_core.h"
|
||||
#include "crn_dxt5a.h"
|
||||
#include "crn_ryg_dxt.hpp"
|
||||
#include "crn_dxt_fast.h"
|
||||
#include "crn_intersect.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
dxt5_endpoint_optimizer::dxt5_endpoint_optimizer() :
|
||||
m_pParams(NULL),
|
||||
m_pResults(NULL)
|
||||
{
|
||||
m_unique_values.reserve(16);
|
||||
m_unique_value_weights.reserve(16);
|
||||
}
|
||||
|
||||
bool dxt5_endpoint_optimizer::compute(const params& p, results& r)
|
||||
{
|
||||
m_pParams = &p;
|
||||
m_pResults = &r;
|
||||
|
||||
if ((!p.m_num_pixels) || (!p.m_pPixels))
|
||||
return false;
|
||||
|
||||
m_unique_values.resize(0);
|
||||
m_unique_value_weights.resize(0);
|
||||
|
||||
for (uint i = 0; i < 256; i++)
|
||||
m_unique_value_map[i] = -1;
|
||||
|
||||
for (uint i = 0; i < p.m_num_pixels; i++)
|
||||
{
|
||||
uint alpha = p.m_pPixels[i][p.m_comp_index];
|
||||
|
||||
int index = m_unique_value_map[alpha];
|
||||
|
||||
if (index == -1)
|
||||
{
|
||||
index = m_unique_values.size();
|
||||
|
||||
m_unique_value_map[alpha] = index;
|
||||
|
||||
m_unique_values.push_back(static_cast<uint8>(alpha));
|
||||
m_unique_value_weights.push_back(0);
|
||||
}
|
||||
|
||||
m_unique_value_weights[index]++;
|
||||
}
|
||||
|
||||
if (m_unique_values.size() == 1)
|
||||
{
|
||||
r.m_block_type = 0;
|
||||
r.m_error = 0;
|
||||
r.m_first_endpoint = m_unique_values[0];
|
||||
r.m_second_endpoint = m_unique_values[0];
|
||||
memset(r.m_pSelectors, 0, p.m_num_pixels);
|
||||
return true;
|
||||
}
|
||||
|
||||
m_trial_selectors.resize(m_unique_values.size());
|
||||
m_best_selectors.resize(m_unique_values.size());
|
||||
|
||||
r.m_error = UINT64_MAX;
|
||||
|
||||
for (uint i = 0; i < m_unique_values.size() - 1; i++)
|
||||
{
|
||||
const uint low_endpoint = m_unique_values[i];
|
||||
|
||||
for (uint j = i + 1; j < m_unique_values.size(); j++)
|
||||
{
|
||||
const uint high_endpoint = m_unique_values[j];
|
||||
|
||||
evaluate_solution(low_endpoint, high_endpoint);
|
||||
}
|
||||
}
|
||||
|
||||
if ((m_pParams->m_quality >= cCRNDXTQualityBetter) && (m_pResults->m_error))
|
||||
{
|
||||
m_flags.resize(256 * 256);
|
||||
m_flags.clear_all_bits();
|
||||
|
||||
const int cProbeAmount = (m_pParams->m_quality == cCRNDXTQualityUber) ? 16 : 8;
|
||||
|
||||
for (int l_delta = -cProbeAmount; l_delta <= cProbeAmount; l_delta++)
|
||||
{
|
||||
const int l = m_pResults->m_first_endpoint + l_delta;
|
||||
if (l < 0)
|
||||
continue;
|
||||
else if (l > 255)
|
||||
break;
|
||||
|
||||
const uint bit_index = l * 256;
|
||||
|
||||
for (int h_delta = -cProbeAmount; h_delta <= cProbeAmount; h_delta++)
|
||||
{
|
||||
const int h = m_pResults->m_second_endpoint + h_delta;
|
||||
if (h < 0)
|
||||
continue;
|
||||
else if (h > 255)
|
||||
break;
|
||||
|
||||
//if (m_flags.get_bit(bit_index + h))
|
||||
// continue;
|
||||
if ((m_flags.get_bit(bit_index + h)) || (m_flags.get_bit(h * 256 + l)))
|
||||
continue;
|
||||
m_flags.set_bit(bit_index + h);
|
||||
|
||||
evaluate_solution(static_cast<uint>(l), static_cast<uint>(h));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_pResults->m_first_endpoint == m_pResults->m_second_endpoint)
|
||||
{
|
||||
for (uint i = 0; i < m_best_selectors.size(); i++)
|
||||
m_best_selectors[i] = 0;
|
||||
}
|
||||
else if (m_pResults->m_block_type)
|
||||
{
|
||||
//if (l > h)
|
||||
// eight alpha
|
||||
// else
|
||||
// six alpha
|
||||
|
||||
if (m_pResults->m_first_endpoint > m_pResults->m_second_endpoint)
|
||||
{
|
||||
utils::swap(m_pResults->m_first_endpoint, m_pResults->m_second_endpoint);
|
||||
for (uint i = 0; i < m_best_selectors.size(); i++)
|
||||
m_best_selectors[i] = g_six_alpha_invert_table[m_best_selectors[i]];
|
||||
}
|
||||
}
|
||||
else if (!(m_pResults->m_first_endpoint > m_pResults->m_second_endpoint))
|
||||
{
|
||||
utils::swap(m_pResults->m_first_endpoint, m_pResults->m_second_endpoint);
|
||||
for (uint i = 0; i < m_best_selectors.size(); i++)
|
||||
m_best_selectors[i] = g_eight_alpha_invert_table[m_best_selectors[i]];
|
||||
}
|
||||
|
||||
for (uint i = 0; i < m_pParams->m_num_pixels; i++)
|
||||
{
|
||||
uint alpha = m_pParams->m_pPixels[i][m_pParams->m_comp_index];
|
||||
|
||||
int index = m_unique_value_map[alpha];
|
||||
|
||||
m_pResults->m_pSelectors[i] = m_best_selectors[index];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void dxt5_endpoint_optimizer::evaluate_solution(uint low_endpoint, uint high_endpoint)
|
||||
{
|
||||
for (uint block_type = 0; block_type < (m_pParams->m_use_both_block_types ? 2U : 1U); block_type++)
|
||||
{
|
||||
uint selector_values[8];
|
||||
|
||||
if (!block_type)
|
||||
dxt5_block::get_block_values8(selector_values, low_endpoint, high_endpoint);
|
||||
else
|
||||
dxt5_block::get_block_values6(selector_values, low_endpoint, high_endpoint);
|
||||
|
||||
uint64 trial_error = 0;
|
||||
|
||||
for (uint i = 0; i < m_unique_values.size(); i++)
|
||||
{
|
||||
const uint val = m_unique_values[i];
|
||||
const uint weight = m_unique_value_weights[i];
|
||||
|
||||
uint best_selector_error = UINT_MAX;
|
||||
uint best_selector = 0;
|
||||
|
||||
for (uint j = 0; j < 8; j++)
|
||||
{
|
||||
int selector_error = val - selector_values[j];
|
||||
selector_error = selector_error * selector_error * (int)weight;
|
||||
|
||||
if (static_cast<uint>(selector_error) < best_selector_error)
|
||||
{
|
||||
best_selector_error = selector_error;
|
||||
best_selector = j;
|
||||
if (!best_selector_error)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_trial_selectors[i] = static_cast<uint8>(best_selector);
|
||||
trial_error += best_selector_error;
|
||||
|
||||
if (trial_error > m_pResults->m_error)
|
||||
break;
|
||||
}
|
||||
|
||||
if (trial_error < m_pResults->m_error)
|
||||
{
|
||||
m_pResults->m_error = trial_error;
|
||||
m_pResults->m_first_endpoint = static_cast<uint8>(low_endpoint);
|
||||
m_pResults->m_second_endpoint = static_cast<uint8>(high_endpoint);
|
||||
m_pResults->m_block_type = static_cast<uint8>(block_type);
|
||||
m_best_selectors.swap(m_trial_selectors);
|
||||
|
||||
if (!trial_error)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,66 @@
|
||||
// File: crn_dxt5a.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
#include "crn_dxt.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
class dxt5_endpoint_optimizer
|
||||
{
|
||||
public:
|
||||
dxt5_endpoint_optimizer();
|
||||
|
||||
struct params
|
||||
{
|
||||
params() :
|
||||
m_block_index(0),
|
||||
m_pPixels(NULL),
|
||||
m_num_pixels(0),
|
||||
m_comp_index(3),
|
||||
m_quality(cCRNDXTQualityUber),
|
||||
m_use_both_block_types(true)
|
||||
{
|
||||
}
|
||||
|
||||
uint m_block_index;
|
||||
|
||||
const color_quad_u8* m_pPixels;
|
||||
uint m_num_pixels;
|
||||
uint m_comp_index;
|
||||
|
||||
crn_dxt_quality m_quality;
|
||||
|
||||
bool m_use_both_block_types;
|
||||
};
|
||||
|
||||
struct results
|
||||
{
|
||||
uint8* m_pSelectors;
|
||||
|
||||
uint64 m_error;
|
||||
|
||||
uint8 m_first_endpoint;
|
||||
uint8 m_second_endpoint;
|
||||
|
||||
uint8 m_block_type; // 1 if 6-alpha, otherwise 8-alpha
|
||||
};
|
||||
|
||||
bool compute(const params& p, results& r);
|
||||
|
||||
private:
|
||||
const params* m_pParams;
|
||||
results* m_pResults;
|
||||
|
||||
crnlib::vector<uint8> m_unique_values;
|
||||
crnlib::vector<uint> m_unique_value_weights;
|
||||
|
||||
crnlib::vector<uint8> m_trial_selectors;
|
||||
crnlib::vector<uint8> m_best_selectors;
|
||||
int m_unique_value_map[256];
|
||||
|
||||
sparse_bit_array m_flags;
|
||||
|
||||
void evaluate_solution(uint low_endpoint, uint high_endpoint);
|
||||
};
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,362 @@
|
||||
// File: crn_dxt_endpoint_refiner.cpp
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#include "crn_core.h"
|
||||
#include "crn_dxt_endpoint_refiner.h"
|
||||
#include "crn_dxt1.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
dxt_endpoint_refiner::dxt_endpoint_refiner() :
|
||||
m_pParams(NULL),
|
||||
m_pResults(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
bool dxt_endpoint_refiner::refine(const params& p, results& r)
|
||||
{
|
||||
if (!p.m_num_pixels)
|
||||
return false;
|
||||
|
||||
m_pParams = &p;
|
||||
m_pResults = &r;
|
||||
|
||||
r.m_error = UINT64_MAX;
|
||||
r.m_low_color = 0;
|
||||
r.m_high_color = 0;
|
||||
|
||||
double alpha2_sum = 0.0f;
|
||||
double beta2_sum = 0.0f;
|
||||
double alphabeta_sum = 0.0f;
|
||||
|
||||
vec<3, double> alphax_sum( 0.0f );
|
||||
vec<3, double> betax_sum( 0.0f );
|
||||
|
||||
vec<3, double> first_color( 0.0f );
|
||||
|
||||
// This linear solver is from Squish.
|
||||
for( uint i = 0; i < p.m_num_pixels; ++i )
|
||||
{
|
||||
uint8 c = p.m_pSelectors[i];
|
||||
|
||||
double k;
|
||||
if (p.m_dxt1_selectors)
|
||||
k = g_dxt1_to_linear[c] * 1.0f/3.0f;
|
||||
else
|
||||
k = g_dxt5_to_linear[c] * 1.0f/7.0f;
|
||||
|
||||
double alpha = 1.0f - k;
|
||||
double beta = k;
|
||||
|
||||
vec<3, double> x;
|
||||
|
||||
if (p.m_dxt1_selectors)
|
||||
x.set( p.m_pPixels[i][0] * 1.0f/255.0f, p.m_pPixels[i][1] * 1.0f/255.0f, p.m_pPixels[i][2] * 1.0f/255.0f );
|
||||
else
|
||||
x.set( p.m_pPixels[i][p.m_alpha_comp_index]/255.0f );
|
||||
|
||||
if (!i)
|
||||
first_color = x;
|
||||
|
||||
alpha2_sum += alpha*alpha;
|
||||
beta2_sum += beta*beta;
|
||||
alphabeta_sum += alpha*beta;
|
||||
alphax_sum += alpha*x;
|
||||
betax_sum += beta*x;
|
||||
}
|
||||
|
||||
// zero where non-determinate
|
||||
vec<3, double> a, b;
|
||||
if( beta2_sum == 0.0f )
|
||||
{
|
||||
a = alphax_sum / alpha2_sum;
|
||||
b.clear();
|
||||
}
|
||||
else if( alpha2_sum == 0.0f )
|
||||
{
|
||||
a.clear();
|
||||
b = betax_sum / beta2_sum;
|
||||
}
|
||||
else
|
||||
{
|
||||
double factor = alpha2_sum*beta2_sum - alphabeta_sum*alphabeta_sum;
|
||||
if (factor != 0.0f)
|
||||
{
|
||||
a = ( alphax_sum*beta2_sum - betax_sum*alphabeta_sum ) / factor;
|
||||
b = ( betax_sum*alpha2_sum - alphax_sum*alphabeta_sum ) / factor;
|
||||
}
|
||||
else
|
||||
{
|
||||
a = first_color;
|
||||
b = first_color;
|
||||
}
|
||||
}
|
||||
|
||||
vec3F l(0.0f), h(0.0f);
|
||||
l = a;
|
||||
h = b;
|
||||
|
||||
l.clamp(0.0f, 1.0f);
|
||||
h.clamp(0.0f, 1.0f);
|
||||
|
||||
if (p.m_dxt1_selectors)
|
||||
optimize_dxt1(l, h);
|
||||
else
|
||||
optimize_dxt5(l, h);
|
||||
|
||||
//if (r.m_low_color < r.m_high_color)
|
||||
// utils::swap(r.m_low_color, r.m_high_color);
|
||||
|
||||
return r.m_error < p.m_error_to_beat;
|
||||
}
|
||||
|
||||
void dxt_endpoint_refiner::optimize_dxt5(vec3F low_color, vec3F high_color)
|
||||
{
|
||||
float nl = low_color[0];
|
||||
float nh = high_color[0];
|
||||
|
||||
#if CRNLIB_DXT_ALT_ROUNDING
|
||||
nl = math::clamp(nl, 0.0f, .999f);
|
||||
nh = math::clamp(nh, 0.0f, .999f);
|
||||
uint il = (int)floor(nl * 256.0f);
|
||||
uint ih = (int)floor(nh * 256.0f);
|
||||
#else
|
||||
uint il = (int)floor(.5f + math::clamp(nl, 0.0f, 1.0f) * 255.0f);
|
||||
uint ih = (int)floor(.5f + math::clamp(nh, 0.0f, 1.0f) * 255.0f);
|
||||
#endif
|
||||
|
||||
crnlib::vector<uint> trial_solutions;
|
||||
trial_solutions.reserve(256);
|
||||
trial_solutions.push_back(il | (ih << 8));
|
||||
|
||||
sparse_bit_array flags;
|
||||
flags.resize(256 * 256);
|
||||
|
||||
flags.set_bit((il * 256) + ih);
|
||||
|
||||
const int cProbeAmount = 11;
|
||||
|
||||
for (int l_delta = -cProbeAmount; l_delta <= cProbeAmount; l_delta++)
|
||||
{
|
||||
const int l = il + l_delta;
|
||||
if (l < 0)
|
||||
continue;
|
||||
else if (l > 255)
|
||||
break;
|
||||
|
||||
const uint bit_index = l * 256;
|
||||
|
||||
for (int h_delta = -cProbeAmount; h_delta <= cProbeAmount; h_delta++)
|
||||
{
|
||||
const int h = ih + h_delta;
|
||||
if (h < 0)
|
||||
continue;
|
||||
else if (h > 255)
|
||||
break;
|
||||
|
||||
if ((flags.get_bit(bit_index + h)) || (flags.get_bit(h * 256 + l)))
|
||||
continue;
|
||||
|
||||
flags.set_bit(bit_index + h);
|
||||
|
||||
trial_solutions.push_back(l | (h << 8));
|
||||
}
|
||||
}
|
||||
|
||||
for (uint trial = 0; trial < trial_solutions.size(); trial++)
|
||||
{
|
||||
uint l = trial_solutions[trial] & 0xFF;
|
||||
uint h = trial_solutions[trial] >> 8;
|
||||
|
||||
if (l == h)
|
||||
{
|
||||
if (h)
|
||||
h--;
|
||||
else
|
||||
l++;
|
||||
}
|
||||
else if (l < h)
|
||||
{
|
||||
utils::swap(l, h);
|
||||
}
|
||||
|
||||
CRNLIB_ASSERT(l > h);
|
||||
|
||||
uint values[cDXT5SelectorValues];
|
||||
dxt5_block::get_block_values8(values, l, h);
|
||||
|
||||
uint total_error = 0;
|
||||
|
||||
for (uint j = 0; j < m_pParams->m_num_pixels; j++)
|
||||
{
|
||||
int p = m_pParams->m_pPixels[j][m_pParams->m_alpha_comp_index];
|
||||
int c = values[m_pParams->m_pSelectors[j]];
|
||||
|
||||
int error = p - c;
|
||||
error *= error;
|
||||
|
||||
total_error += error;
|
||||
|
||||
if (total_error > m_pResults->m_error)
|
||||
break;
|
||||
}
|
||||
|
||||
if (total_error < m_pResults->m_error)
|
||||
{
|
||||
m_pResults->m_error = total_error;
|
||||
m_pResults->m_low_color = static_cast<uint16>(l);
|
||||
m_pResults->m_high_color = static_cast<uint16>(h);
|
||||
|
||||
if (m_pResults->m_error == 0)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dxt_endpoint_refiner::optimize_dxt1(vec3F low_color, vec3F high_color)
|
||||
{
|
||||
uint selector_hist[4];
|
||||
utils::zero_object(selector_hist);
|
||||
for (uint i = 0; i < m_pParams->m_num_pixels; i++)
|
||||
selector_hist[m_pParams->m_pSelectors[i]]++;
|
||||
|
||||
dxt1_solution_coordinates c(low_color, high_color);
|
||||
|
||||
for (uint pass = 0; pass < 8; pass++)
|
||||
{
|
||||
const uint64 initial_error = m_pResults->m_error;
|
||||
|
||||
dxt1_solution_coordinates_vec coords_to_try;
|
||||
|
||||
coords_to_try.resize(0);
|
||||
|
||||
color_quad_u8 lc(dxt1_block::unpack_color(c.m_low_color, false));
|
||||
color_quad_u8 hc(dxt1_block::unpack_color(c.m_high_color, false));
|
||||
|
||||
for (int i = 0; i < 27; i++)
|
||||
{
|
||||
if (13 == i) continue;
|
||||
|
||||
const int ir = (i % 3) - 1;
|
||||
const int ig = ((i / 3) % 3) - 1;
|
||||
const int ib = ((i / 9) % 3) - 1;
|
||||
|
||||
int r = lc.r + ir;
|
||||
int g = lc.g + ig;
|
||||
int b = lc.b + ib;
|
||||
if ((r < 0) || (r > 31)|| (g < 0) || (g > 63) || (b < 0) || (b > 31)) continue;
|
||||
|
||||
coords_to_try.push_back(
|
||||
dxt1_solution_coordinates(dxt1_block::pack_color(r, g, b, false), c.m_high_color)
|
||||
);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 27; i++)
|
||||
{
|
||||
if (13 == i) continue;
|
||||
|
||||
const int ir = (i % 3) - 1;
|
||||
const int ig = ((i / 3) % 3) - 1;
|
||||
const int ib = ((i / 9) % 3) - 1;
|
||||
|
||||
int r = hc.r + ir;
|
||||
int g = hc.g + ig;
|
||||
int b = hc.b + ib;
|
||||
if ((r < 0) || (r > 31)|| (g < 0) || (g > 63) || (b < 0) || (b > 31)) continue;
|
||||
|
||||
coords_to_try.push_back(dxt1_solution_coordinates(c.m_low_color, dxt1_block::pack_color(r, g, b, false)));
|
||||
}
|
||||
|
||||
std::sort(coords_to_try.begin(), coords_to_try.end());
|
||||
|
||||
dxt1_solution_coordinates_vec::const_iterator p_last = std::unique(coords_to_try.begin(), coords_to_try.end());
|
||||
uint num_coords_to_try = (uint)(p_last - coords_to_try.begin());
|
||||
|
||||
for (uint i = 0; i < num_coords_to_try; i++)
|
||||
{
|
||||
color_quad_u8 block_colors[4];
|
||||
uint16 l = coords_to_try[i].m_low_color;
|
||||
uint16 h = coords_to_try[i].m_high_color;
|
||||
if (l < h)
|
||||
utils::swap(l, h);
|
||||
else if (l == h)
|
||||
{
|
||||
color_quad_u8 lc(dxt1_block::unpack_color(l, false));
|
||||
color_quad_u8 hc(dxt1_block::unpack_color(h, false));
|
||||
|
||||
bool retry = false;
|
||||
if ((selector_hist[0] + selector_hist[2]) > (selector_hist[1] + selector_hist[3]))
|
||||
{
|
||||
// l affects the output more than h, so muck with h
|
||||
if (hc[2] != 0)
|
||||
hc[2]--;
|
||||
else if (hc[0] != 0)
|
||||
hc[0]--;
|
||||
else if (hc[1] != 0)
|
||||
hc[1]--;
|
||||
else
|
||||
retry = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// h affects the output more than l, so muck with l
|
||||
if (lc[2] != 31)
|
||||
lc[2]++;
|
||||
else if (lc[0] != 31)
|
||||
lc[0]++;
|
||||
else if (lc[1] != 63)
|
||||
lc[1]++;
|
||||
else
|
||||
retry = true;
|
||||
}
|
||||
|
||||
if (retry)
|
||||
{
|
||||
if (l == 0)
|
||||
l++;
|
||||
else
|
||||
h--;
|
||||
}
|
||||
else
|
||||
{
|
||||
l = dxt1_block::pack_color(lc, false);
|
||||
h = dxt1_block::pack_color(hc, false);
|
||||
}
|
||||
|
||||
CRNLIB_ASSERT(l > h);
|
||||
}
|
||||
|
||||
dxt1_block::get_block_colors4(block_colors, l, h);
|
||||
|
||||
uint total_error = 0;
|
||||
|
||||
for (uint j = 0; j < m_pParams->m_num_pixels; j++)
|
||||
{
|
||||
const color_quad_u8& c = block_colors[m_pParams->m_pSelectors[j]];
|
||||
total_error += color::color_distance(m_pParams->m_perceptual, c, m_pParams->m_pPixels[j], false);
|
||||
|
||||
if (total_error > m_pResults->m_error)
|
||||
break;
|
||||
}
|
||||
|
||||
if (total_error < m_pResults->m_error)
|
||||
{
|
||||
m_pResults->m_error = total_error;
|
||||
m_pResults->m_low_color = l;
|
||||
m_pResults->m_high_color = h;
|
||||
CRNLIB_ASSERT(l > h);
|
||||
if (m_pResults->m_error == 0)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_pResults->m_error == initial_error)
|
||||
break;
|
||||
|
||||
c.m_low_color = m_pResults->m_low_color;
|
||||
c.m_high_color = m_pResults->m_high_color;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
// File: crn_dxt_endpoint_refiner.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
#include "crn_dxt.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
// TODO: Experimental/Not fully implemented
|
||||
class dxt_endpoint_refiner
|
||||
{
|
||||
public:
|
||||
dxt_endpoint_refiner();
|
||||
|
||||
struct params
|
||||
{
|
||||
params() :
|
||||
m_block_index(0),
|
||||
m_pPixels(NULL),
|
||||
m_num_pixels(0),
|
||||
m_pSelectors(NULL),
|
||||
m_alpha_comp_index(0),
|
||||
m_error_to_beat(UINT64_MAX),
|
||||
m_dxt1_selectors(true),
|
||||
m_perceptual(true),
|
||||
m_highest_quality(true)
|
||||
{
|
||||
}
|
||||
|
||||
uint m_block_index;
|
||||
|
||||
const color_quad_u8* m_pPixels;
|
||||
uint m_num_pixels;
|
||||
|
||||
const uint8* m_pSelectors;
|
||||
|
||||
uint m_alpha_comp_index;
|
||||
|
||||
uint64 m_error_to_beat;
|
||||
|
||||
bool m_dxt1_selectors;
|
||||
bool m_perceptual;
|
||||
bool m_highest_quality;
|
||||
};
|
||||
|
||||
struct results
|
||||
{
|
||||
uint16 m_low_color;
|
||||
uint16 m_high_color;
|
||||
uint64 m_error;
|
||||
};
|
||||
|
||||
bool refine(const params& p, results& r);
|
||||
|
||||
private:
|
||||
const params* m_pParams;
|
||||
results* m_pResults;
|
||||
|
||||
void optimize_dxt1(vec3F low_color, vec3F high_color);
|
||||
void optimize_dxt5(vec3F low_color, vec3F high_color);
|
||||
};
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,916 @@
|
||||
// File: crn_dxt_fast.cpp
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
// Parts of this module are derived from RYG's excellent public domain DXTx compressor.
|
||||
#include "crn_core.h"
|
||||
#include "crn_dxt_fast.h"
|
||||
#include "crn_ryg_dxt.hpp"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
namespace dxt_fast
|
||||
{
|
||||
static inline int mul_8bit(int a, int b)
|
||||
{
|
||||
int t = a * b + 128;
|
||||
return (t + (t >> 8)) >> 8;
|
||||
}
|
||||
|
||||
static inline color_quad_u8& unpack_color(color_quad_u8& c, uint v)
|
||||
{
|
||||
uint rv = (v & 0xf800) >> 11;
|
||||
uint gv = (v & 0x07e0) >> 5;
|
||||
uint bv = (v & 0x001f) >> 0;
|
||||
|
||||
c.r = ryg_dxt::Expand5[rv];
|
||||
c.g = ryg_dxt::Expand6[gv];
|
||||
c.b = ryg_dxt::Expand5[bv];
|
||||
c.a = 0;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static inline uint pack_color(const color_quad_u8& c)
|
||||
{
|
||||
return (mul_8bit(c.r, 31) << 11) + (mul_8bit(c.g, 63) << 5) + mul_8bit(c.b, 31);
|
||||
}
|
||||
|
||||
static inline void lerp_color(color_quad_u8& result, const color_quad_u8& p1, const color_quad_u8& p2, uint f)
|
||||
{
|
||||
CRNLIB_ASSERT(f <= 255);
|
||||
|
||||
result.r = static_cast<uint8>(p1.r + mul_8bit(p2.r - p1.r, f));
|
||||
result.g = static_cast<uint8>(p1.g + mul_8bit(p2.g - p1.g, f));
|
||||
result.b = static_cast<uint8>(p1.b + mul_8bit(p2.b - p1.b, f));
|
||||
}
|
||||
|
||||
static inline void eval_colors(color_quad_u8* pColors, uint c0, uint c1)
|
||||
{
|
||||
unpack_color(pColors[0], c0);
|
||||
unpack_color(pColors[1], c1);
|
||||
|
||||
#if 0
|
||||
lerp_color(pColors[2], pColors[0], pColors[1], 0x55);
|
||||
lerp_color(pColors[3], pColors[0], pColors[1], 0xAA);
|
||||
#else
|
||||
pColors[2].r = (pColors[0].r*2+pColors[1].r)/3;
|
||||
pColors[2].g = (pColors[0].g*2+pColors[1].g)/3;
|
||||
pColors[2].b = (pColors[0].b*2+pColors[1].b)/3;
|
||||
|
||||
pColors[3].r = (pColors[1].r*2+pColors[0].r)/3;
|
||||
pColors[3].g = (pColors[1].g*2+pColors[0].g)/3;
|
||||
pColors[3].b = (pColors[1].b*2+pColors[0].b)/3;
|
||||
#endif
|
||||
}
|
||||
|
||||
// false if all selectors equal
|
||||
static bool match_block_colors(uint n, const color_quad_u8* pBlock, const color_quad_u8* pColors, uint8* pSelectors)
|
||||
{
|
||||
int dirr = pColors[0].r - pColors[1].r;
|
||||
int dirg = pColors[0].g - pColors[1].g;
|
||||
int dirb = pColors[0].b - pColors[1].b;
|
||||
|
||||
int stops[4];
|
||||
for(int i = 0; i < 4; i++)
|
||||
stops[i] = pColors[i].r*dirr + pColors[i].g*dirg + pColors[i].b*dirb;
|
||||
|
||||
// 0 2 3 1
|
||||
int c0Point = stops[1] + stops[3];
|
||||
int halfPoint = stops[3] + stops[2];
|
||||
int c3Point = stops[2] + stops[0];
|
||||
|
||||
//dirr *= 2;
|
||||
//dirg *= 2;
|
||||
//dirb *= 2;
|
||||
c0Point >>= 1;
|
||||
halfPoint >>= 1;
|
||||
c3Point >>= 1;
|
||||
|
||||
bool status = false;
|
||||
for (uint i = 0; i < n; i++)
|
||||
{
|
||||
int dot = pBlock[i].r*dirr + pBlock[i].g*dirg + pBlock[i].b*dirb;
|
||||
|
||||
uint8 s;
|
||||
if (dot < halfPoint)
|
||||
s = (dot < c0Point) ? 1 : 3;
|
||||
else
|
||||
s = (dot < c3Point) ? 2 : 0;
|
||||
|
||||
pSelectors[i] = s;
|
||||
|
||||
if (s != pSelectors[0])
|
||||
status = true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static bool optimize_block_colors(uint n, const color_quad_u8* block, uint& max16, uint& min16, uint ave_color[3], float axis[3])
|
||||
{
|
||||
int min[3], max[3];
|
||||
|
||||
for(uint ch = 0; ch < 3; ch++)
|
||||
{
|
||||
const uint8 *bp = ((const uint8 *) block) + ch;
|
||||
int minv, maxv;
|
||||
|
||||
int64 muv = bp[0];
|
||||
minv = maxv = bp[0];
|
||||
|
||||
const uint l = n << 2;
|
||||
for (uint i = 4; i < l; i += 4)
|
||||
{
|
||||
muv += bp[i];
|
||||
minv = math::minimum<int>(minv, bp[i]);
|
||||
maxv = math::maximum<int>(maxv, bp[i]);
|
||||
}
|
||||
|
||||
ave_color[ch] = static_cast<int>((muv + (n / 2)) / n);
|
||||
min[ch] = minv;
|
||||
max[ch] = maxv;
|
||||
}
|
||||
|
||||
if ((min[0] == max[0]) && (min[1] == max[1]) && (min[2] == max[2]))
|
||||
return false;
|
||||
|
||||
// determine covariance matrix
|
||||
double cov[6];
|
||||
for(int i=0;i<6;i++)
|
||||
cov[i] = 0;
|
||||
|
||||
for(uint i=0;i<n;i++)
|
||||
{
|
||||
double r = (int)block[i].r - (int)ave_color[0];
|
||||
double g = (int)block[i].g - (int)ave_color[1];
|
||||
double b = (int)block[i].b - (int)ave_color[2];
|
||||
|
||||
cov[0] += r*r;
|
||||
cov[1] += r*g;
|
||||
cov[2] += r*b;
|
||||
cov[3] += g*g;
|
||||
cov[4] += g*b;
|
||||
cov[5] += b*b;
|
||||
}
|
||||
|
||||
double covf[6],vfr,vfg,vfb;
|
||||
for(int i=0;i<6;i++)
|
||||
covf[i] = cov[i] * (1.0f/255.0f);
|
||||
|
||||
vfr = max[0] - min[0];
|
||||
vfg = max[1] - min[1];
|
||||
vfb = max[2] - min[2];
|
||||
|
||||
static const uint nIterPower = 4;
|
||||
for(uint iter = 0; iter < nIterPower; iter++)
|
||||
{
|
||||
double r = vfr*covf[0] + vfg*covf[1] + vfb*covf[2];
|
||||
double g = vfr*covf[1] + vfg*covf[3] + vfb*covf[4];
|
||||
double b = vfr*covf[2] + vfg*covf[4] + vfb*covf[5];
|
||||
|
||||
vfr = r;
|
||||
vfg = g;
|
||||
vfb = b;
|
||||
}
|
||||
|
||||
double magn = math::maximum(math::maximum(fabs(vfr),fabs(vfg)),fabs(vfb));
|
||||
int v_r, v_g, v_b;
|
||||
|
||||
if (magn < 4.0f) // too small, default to luminance
|
||||
{
|
||||
v_r = 148;
|
||||
v_g = 300;
|
||||
v_b = 58;
|
||||
|
||||
axis[0] = (float)v_r;
|
||||
axis[1] = (float)v_g;
|
||||
axis[2] = (float)v_b;
|
||||
}
|
||||
else
|
||||
{
|
||||
magn = 512.0f / magn;
|
||||
vfr *= magn;
|
||||
vfg *= magn;
|
||||
vfb *= magn;
|
||||
v_r = static_cast<int>(vfr);
|
||||
v_g = static_cast<int>(vfg);
|
||||
v_b = static_cast<int>(vfb);
|
||||
|
||||
axis[0] = (float)vfr;
|
||||
axis[1] = (float)vfg;
|
||||
axis[2] = (float)vfb;
|
||||
}
|
||||
|
||||
int mind = block[0].r * v_r + block[0].g * v_g + block[0].b * v_b;
|
||||
int maxd = mind;
|
||||
color_quad_u8 minp(block[0]);
|
||||
color_quad_u8 maxp(block[0]);
|
||||
|
||||
for(uint i = 1; i < n; i++)
|
||||
{
|
||||
int dot = block[i].r * v_r + block[i].g * v_g + block[i].b * v_b;
|
||||
|
||||
if (dot < mind)
|
||||
{
|
||||
mind = dot;
|
||||
minp = block[i];
|
||||
}
|
||||
|
||||
if (dot > maxd)
|
||||
{
|
||||
maxd = dot;
|
||||
maxp = block[i];
|
||||
}
|
||||
}
|
||||
|
||||
max16 = pack_color(maxp);
|
||||
min16 = pack_color(minp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// The refinement function. (Clever code, part 2)
|
||||
// Tries to optimize colors to suit block contents better.
|
||||
// (By solving a least squares system via normal equations+Cramer's rule)
|
||||
static bool refine_block(uint n, const color_quad_u8 *block, uint &max16, uint &min16, const uint8* pSelectors)
|
||||
{
|
||||
static const int w1Tab[4] = { 3,0,2,1 };
|
||||
|
||||
static const int prods_0[4] = { 0x00,0x00,0x02,0x02 };
|
||||
static const int prods_1[4] = { 0x00,0x09,0x01,0x04 };
|
||||
static const int prods_2[4] = { 0x09,0x00,0x04,0x01 };
|
||||
|
||||
double akku_0 = 0;
|
||||
double akku_1 = 0;
|
||||
double akku_2 = 0;
|
||||
double At1_r, At1_g, At1_b;
|
||||
double At2_r, At2_g, At2_b;
|
||||
|
||||
At1_r = At1_g = At1_b = 0;
|
||||
At2_r = At2_g = At2_b = 0;
|
||||
for(uint i = 0; i < n; i++)
|
||||
{
|
||||
double r = block[i].r;
|
||||
double g = block[i].g;
|
||||
double b = block[i].b;
|
||||
int step = pSelectors[i];
|
||||
|
||||
int w1 = w1Tab[step];
|
||||
|
||||
akku_0 += prods_0[step];
|
||||
akku_1 += prods_1[step];
|
||||
akku_2 += prods_2[step];
|
||||
At1_r += w1*r;
|
||||
At1_g += w1*g;
|
||||
At1_b += w1*b;
|
||||
At2_r += r;
|
||||
At2_g += g;
|
||||
At2_b += b;
|
||||
}
|
||||
|
||||
At2_r = 3*At2_r - At1_r;
|
||||
At2_g = 3*At2_g - At1_g;
|
||||
At2_b = 3*At2_b - At1_b;
|
||||
|
||||
double xx = akku_2;
|
||||
double yy = akku_1;
|
||||
double xy = akku_0;
|
||||
|
||||
double t = xx * yy - xy * xy;
|
||||
if (!yy || !xx || (fabs(t) < .0000125f))
|
||||
return false;
|
||||
|
||||
double frb = (3.0f * 31.0f / 255.0f) / t;
|
||||
double fg = frb * (63.0f / 31.0f);
|
||||
|
||||
uint oldMin = min16;
|
||||
uint oldMax = max16;
|
||||
|
||||
// solve.
|
||||
max16 = math::clamp<int>(static_cast<int>((At1_r*yy - At2_r*xy)*frb+0.5f),0,31) << 11;
|
||||
max16 |= math::clamp<int>(static_cast<int>((At1_g*yy - At2_g*xy)*fg +0.5f),0,63) << 5;
|
||||
max16 |= math::clamp<int>(static_cast<int>((At1_b*yy - At2_b*xy)*frb+0.5f),0,31) << 0;
|
||||
|
||||
min16 = math::clamp<int>(static_cast<int>((At2_r*xx - At1_r*xy)*frb+0.5f),0,31) << 11;
|
||||
min16 |= math::clamp<int>(static_cast<int>((At2_g*xx - At1_g*xy)*fg +0.5f),0,63) << 5;
|
||||
min16 |= math::clamp<int>(static_cast<int>((At2_b*xx - At1_b*xy)*frb+0.5f),0,31) << 0;
|
||||
|
||||
return (oldMin != min16) || (oldMax != max16);
|
||||
}
|
||||
|
||||
// false if all selectors equal
|
||||
static bool determine_selectors(uint n, const color_quad_u8* block, uint min16, uint max16, uint8* pSelectors)
|
||||
{
|
||||
color_quad_u8 color[4];
|
||||
|
||||
if (max16 != min16)
|
||||
{
|
||||
eval_colors(color, min16, max16);
|
||||
|
||||
return match_block_colors(n, block, color, pSelectors);
|
||||
}
|
||||
|
||||
memset(pSelectors, 0, n);
|
||||
return false;
|
||||
}
|
||||
|
||||
static uint64 determine_error(uint n, const color_quad_u8* block, uint min16, uint max16, uint64 early_out_error)
|
||||
{
|
||||
color_quad_u8 color[4];
|
||||
|
||||
eval_colors(color, min16, max16);
|
||||
|
||||
int dirr = color[0].r - color[1].r;
|
||||
int dirg = color[0].g - color[1].g;
|
||||
int dirb = color[0].b - color[1].b;
|
||||
|
||||
int stops[4];
|
||||
for(int i = 0; i < 4; i++)
|
||||
stops[i] = color[i].r*dirr + color[i].g*dirg + color[i].b*dirb;
|
||||
|
||||
// 0 2 3 1
|
||||
int c0Point = stops[1] + stops[3];
|
||||
int halfPoint = stops[3] + stops[2];
|
||||
int c3Point = stops[2] + stops[0];
|
||||
|
||||
c0Point >>= 1;
|
||||
halfPoint >>= 1;
|
||||
c3Point >>= 1;
|
||||
|
||||
uint64 total_error = 0;
|
||||
|
||||
for (uint i = 0; i < n; i++)
|
||||
{
|
||||
const color_quad_u8& a = block[i];
|
||||
|
||||
uint s = 0;
|
||||
if (min16 != max16)
|
||||
{
|
||||
int dot = a.r*dirr + a.g*dirg + a.b*dirb;
|
||||
|
||||
if (dot < halfPoint)
|
||||
s = (dot < c0Point) ? 1 : 3;
|
||||
else
|
||||
s = (dot < c3Point) ? 2 : 0;
|
||||
}
|
||||
|
||||
const color_quad_u8& b = color[s];
|
||||
|
||||
int e = a[0] - b[0];
|
||||
total_error += e * e;
|
||||
|
||||
e = a[1] - b[1];
|
||||
total_error += e * e;
|
||||
|
||||
e = a[2] - b[2];
|
||||
total_error += e * e;
|
||||
|
||||
if (total_error >= early_out_error)
|
||||
break;
|
||||
}
|
||||
|
||||
return total_error;
|
||||
}
|
||||
|
||||
static bool refine_endpoints(uint n, const color_quad_u8* pBlock, uint& low16, uint& high16, uint8* pSelectors)
|
||||
{
|
||||
bool optimized = false;
|
||||
|
||||
const int limits[3] = { 31, 63, 31 };
|
||||
|
||||
for (uint trial = 0; trial < 2; trial++)
|
||||
{
|
||||
color_quad_u8 color[4];
|
||||
eval_colors(color, low16, high16);
|
||||
|
||||
uint64 total_error[3] = { 0, 0, 0 };
|
||||
|
||||
for (uint i = 0; i < n; i++)
|
||||
{
|
||||
const color_quad_u8& a = pBlock[i];
|
||||
|
||||
const uint s = pSelectors[i];
|
||||
const color_quad_u8& b = color[s];
|
||||
|
||||
int e = a[0] - b[0];
|
||||
total_error[0] += e * e;
|
||||
|
||||
e = a[1] - b[1];
|
||||
total_error[1] += e * e;
|
||||
|
||||
e = a[2] - b[2];
|
||||
total_error[2] += e * e;
|
||||
}
|
||||
|
||||
color_quad_u8 endpoints[2];
|
||||
endpoints[0] = dxt1_block::unpack_color((uint16)low16, false);
|
||||
endpoints[1] = dxt1_block::unpack_color((uint16)high16, false);
|
||||
|
||||
color_quad_u8 expanded_endpoints[2];
|
||||
expanded_endpoints[0] = dxt1_block::unpack_color((uint16)low16, true);
|
||||
expanded_endpoints[1] = dxt1_block::unpack_color((uint16)high16, true);
|
||||
|
||||
bool trial_optimized = false;
|
||||
|
||||
for (uint axis = 0; axis < 3; axis++)
|
||||
{
|
||||
if (!total_error[axis])
|
||||
continue;
|
||||
|
||||
const sU8* const pExpand = (axis == 1) ? ryg_dxt::Expand6 : ryg_dxt::Expand5;
|
||||
|
||||
for (uint e = 0; e < 2; e++)
|
||||
{
|
||||
uint v[4];
|
||||
v[e^1] = expanded_endpoints[e^1][axis];
|
||||
|
||||
for (int t = -1; t <= 1; t += 2)
|
||||
{
|
||||
int a = endpoints[e][axis] + t;
|
||||
if ((a < 0) || (a > limits[axis]))
|
||||
continue;
|
||||
|
||||
v[e] = pExpand[a];
|
||||
|
||||
//int delta = v[1] - v[0];
|
||||
//v[2] = v[0] + mul_8bit(delta, 0x55);
|
||||
//v[3] = v[0] + mul_8bit(delta, 0xAA);
|
||||
|
||||
v[2] = (v[0] * 2 + v[1]) / 3;
|
||||
v[3] = (v[0] + v[1] * 2) / 3;
|
||||
|
||||
uint64 axis_error = 0;
|
||||
|
||||
for (uint i = 0; i < n; i++)
|
||||
{
|
||||
const color_quad_u8& p = pBlock[i];
|
||||
|
||||
int e = v[pSelectors[i]] - p[axis];
|
||||
|
||||
axis_error += e * e;
|
||||
|
||||
if (axis_error >= total_error[axis])
|
||||
break;
|
||||
}
|
||||
|
||||
if (axis_error < total_error[axis])
|
||||
{
|
||||
//total_error[axis] = axis_error;
|
||||
|
||||
endpoints[e][axis] = (uint8)a;
|
||||
expanded_endpoints[e][axis] = (uint8)v[e];
|
||||
|
||||
if (e)
|
||||
high16 = dxt1_block::pack_color(endpoints[1], false);
|
||||
else
|
||||
low16 = dxt1_block::pack_color(endpoints[0], false);
|
||||
|
||||
determine_selectors(n, pBlock, low16, high16, pSelectors);
|
||||
|
||||
eval_colors(color, low16, high16);
|
||||
|
||||
utils::zero_object(total_error);
|
||||
|
||||
for (uint i = 0; i < n; i++)
|
||||
{
|
||||
const color_quad_u8& a = pBlock[i];
|
||||
|
||||
const uint s = pSelectors[i];
|
||||
const color_quad_u8& b = color[s];
|
||||
|
||||
int e = a[0] - b[0];
|
||||
total_error[0] += e * e;
|
||||
|
||||
e = a[1] - b[1];
|
||||
total_error[1] += e * e;
|
||||
|
||||
e = a[2] - b[2];
|
||||
total_error[2] += e * e;
|
||||
}
|
||||
|
||||
trial_optimized = true;
|
||||
}
|
||||
|
||||
} // t
|
||||
|
||||
} // e
|
||||
} // axis
|
||||
|
||||
if (!trial_optimized)
|
||||
break;
|
||||
|
||||
optimized = true;
|
||||
|
||||
} // for ( ; ; )
|
||||
|
||||
return optimized;
|
||||
}
|
||||
|
||||
static void refine_endpoints2(uint n, const color_quad_u8* pBlock, uint& low16, uint& high16, uint8* pSelectors, float axis[3])
|
||||
{
|
||||
uint64 orig_error = determine_error(n, pBlock, low16, high16, UINT64_MAX);
|
||||
if (!orig_error)
|
||||
return;
|
||||
|
||||
float l = 1.0f / sqrt(axis[0]*axis[0] + axis[1]*axis[1] + axis[2]*axis[2]);
|
||||
vec3F principle_axis(axis[0] * l, axis[1] * l, axis[2] * l);
|
||||
|
||||
const float dist_per_trial = 0.027063293f;
|
||||
|
||||
const uint cMaxProbeRange = 8;
|
||||
uint probe_low[cMaxProbeRange * 2 + 1];
|
||||
uint probe_high[cMaxProbeRange * 2 + 1];
|
||||
|
||||
int probe_range = 8;
|
||||
uint num_iters = 4;
|
||||
|
||||
const uint num_trials = probe_range * 2 + 1;
|
||||
|
||||
vec3F scaled_principle_axis(principle_axis * dist_per_trial);
|
||||
scaled_principle_axis[0] *= 31.0f;
|
||||
scaled_principle_axis[1] *= 63.0f;
|
||||
scaled_principle_axis[2] *= 31.0f;
|
||||
vec3F initial_ofs(scaled_principle_axis * (float)-probe_range);
|
||||
initial_ofs[0] += .5f;
|
||||
initial_ofs[1] += .5f;
|
||||
initial_ofs[2] += .5f;
|
||||
|
||||
uint64 cur_error = orig_error;
|
||||
|
||||
for (uint iter = 0; iter < num_iters; iter++)
|
||||
{
|
||||
color_quad_u8 endpoints[2];
|
||||
|
||||
endpoints[0] = dxt1_block::unpack_color((uint16)low16, false);
|
||||
endpoints[1] = dxt1_block::unpack_color((uint16)high16, false);
|
||||
|
||||
vec3F low_color(endpoints[0][0], endpoints[0][1], endpoints[0][2]);
|
||||
vec3F high_color(endpoints[1][0], endpoints[1][1], endpoints[1][2]);
|
||||
|
||||
vec3F probe_low_color(low_color + initial_ofs);
|
||||
for (uint i = 0; i < num_trials; i++)
|
||||
{
|
||||
int r = math::clamp((int)floor(probe_low_color[0]), 0, 31);
|
||||
int g = math::clamp((int)floor(probe_low_color[1]), 0, 63);
|
||||
int b = math::clamp((int)floor(probe_low_color[2]), 0, 31);
|
||||
probe_low[i] = b | (g << 5U) | (r << 11U);
|
||||
|
||||
probe_low_color += scaled_principle_axis;
|
||||
}
|
||||
|
||||
vec3F probe_high_color(high_color + initial_ofs);
|
||||
for (uint i = 0; i < num_trials; i++)
|
||||
{
|
||||
int r = math::clamp((int)floor(probe_high_color[0]), 0, 31);
|
||||
int g = math::clamp((int)floor(probe_high_color[1]), 0, 63);
|
||||
int b = math::clamp((int)floor(probe_high_color[2]), 0, 31);
|
||||
probe_high[i] = b | (g << 5U) | (r << 11U);
|
||||
|
||||
probe_high_color += scaled_principle_axis;
|
||||
}
|
||||
|
||||
uint best_l = low16;
|
||||
uint best_h = high16;
|
||||
|
||||
enum { cMaxHash = 4 };
|
||||
uint64 hash[cMaxHash];
|
||||
for (uint i = 0; i < cMaxHash; i++)
|
||||
hash[i] = 0;
|
||||
|
||||
uint c = best_l | (best_h << 16);
|
||||
c = fast_hash(&c, sizeof(c));
|
||||
hash[(c >> 6) & 3] = 1ULL << (c & 63);
|
||||
|
||||
for (uint i = 0; i < num_trials; i++)
|
||||
{
|
||||
for (uint j = 0; j < num_trials; j++)
|
||||
{
|
||||
uint l = probe_low[i];
|
||||
uint h = probe_high[j];
|
||||
if (l < h)
|
||||
utils::swap(l, h);
|
||||
|
||||
uint c = l | (h << 16);
|
||||
c = fast_hash(&c, sizeof(c));
|
||||
uint64 mask = 1ULL << (c & 63);
|
||||
uint ofs = (c >> 6) & 3;
|
||||
if (hash[ofs] & mask)
|
||||
continue;
|
||||
|
||||
hash[ofs] |= mask;
|
||||
|
||||
uint64 new_error = determine_error(n, pBlock, l, h, cur_error);
|
||||
if (new_error < cur_error)
|
||||
{
|
||||
best_l = l;
|
||||
best_h = h;
|
||||
cur_error = new_error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool improved = false;
|
||||
|
||||
if ((best_l != low16) || (best_h != high16))
|
||||
{
|
||||
low16 = best_l;
|
||||
high16 = best_h;
|
||||
|
||||
determine_selectors(n, pBlock, low16, high16, pSelectors);
|
||||
improved = true;
|
||||
}
|
||||
|
||||
if (refine_endpoints(n, pBlock, low16, high16, pSelectors))
|
||||
{
|
||||
improved = true;
|
||||
|
||||
uint64 cur_error = determine_error(n, pBlock, low16, high16, UINT64_MAX);
|
||||
if (!cur_error)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!improved)
|
||||
break;
|
||||
|
||||
} // iter
|
||||
|
||||
//uint64 end_error = determine_error(n, pBlock, low16, high16, UINT64_MAX);
|
||||
//if (end_error > orig_error) DebugBreak();
|
||||
}
|
||||
|
||||
static void compress_solid_block(uint n, uint ave_color[3], uint& low16, uint& high16, uint8* pSelectors)
|
||||
{
|
||||
uint r = ave_color[0];
|
||||
uint g = ave_color[1];
|
||||
uint b = ave_color[2];
|
||||
|
||||
memset(pSelectors, 2, n);
|
||||
|
||||
low16 = (ryg_dxt::OMatch5[r][0]<<11) | (ryg_dxt::OMatch6[g][0]<<5) | ryg_dxt::OMatch5[b][0];
|
||||
high16 = (ryg_dxt::OMatch5[r][1]<<11) | (ryg_dxt::OMatch6[g][1]<<5) | ryg_dxt::OMatch5[b][1];
|
||||
}
|
||||
|
||||
void compress_color_block(uint n, const color_quad_u8* block, uint& low16, uint& high16, uint8* pSelectors, bool refine)
|
||||
{
|
||||
CRNLIB_ASSERT((n & 15) == 0);
|
||||
|
||||
uint ave_color[3];
|
||||
float axis[3];
|
||||
|
||||
if (!optimize_block_colors(n, block, low16, high16, ave_color, axis))
|
||||
{
|
||||
compress_solid_block(n, ave_color, low16, high16, pSelectors);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!determine_selectors(n, block, low16, high16, pSelectors))
|
||||
compress_solid_block(n, ave_color, low16, high16, pSelectors);
|
||||
else
|
||||
{
|
||||
if (refine_block(n, block, low16, high16, pSelectors))
|
||||
determine_selectors(n, block, low16, high16, pSelectors);
|
||||
|
||||
if (refine)
|
||||
refine_endpoints2(n, block, low16, high16, pSelectors, axis);
|
||||
}
|
||||
}
|
||||
|
||||
if (low16 < high16)
|
||||
{
|
||||
utils::swap(low16, high16);
|
||||
for (uint i = 0; i < n; i++)
|
||||
pSelectors[i] ^= 1;
|
||||
}
|
||||
}
|
||||
|
||||
void compress_color_block(dxt1_block* pDXT1_block, const color_quad_u8* pBlock, bool refine)
|
||||
{
|
||||
uint8 color_selectors[16];
|
||||
uint low16, high16;
|
||||
dxt_fast::compress_color_block(16, pBlock, low16, high16, color_selectors, refine);
|
||||
|
||||
pDXT1_block->set_low_color(static_cast<uint16>(low16));
|
||||
pDXT1_block->set_high_color(static_cast<uint16>(high16));
|
||||
|
||||
uint mask = 0;
|
||||
for (int i = 15; i >= 0; i--)
|
||||
{
|
||||
mask <<= 2;
|
||||
mask |= color_selectors[i];
|
||||
}
|
||||
|
||||
pDXT1_block->m_selectors[0] = (uint8)(mask & 0xFF);
|
||||
pDXT1_block->m_selectors[1] = (uint8)((mask >> 8) & 0xFF);
|
||||
pDXT1_block->m_selectors[2] = (uint8)((mask >> 16) & 0xFF);
|
||||
pDXT1_block->m_selectors[3] = (uint8)((mask >> 24) & 0xFF);
|
||||
}
|
||||
|
||||
void compress_alpha_block(uint n, const color_quad_u8* block, uint& low8, uint& high8, uint8* pSelectors, uint comp_index)
|
||||
{
|
||||
int min, max;
|
||||
min = max = block[0][comp_index];
|
||||
|
||||
for (uint i = 1; i < n; i++)
|
||||
{
|
||||
min = math::minimum<int>(min, block[i][comp_index]);
|
||||
max = math::maximum<int>(max, block[i][comp_index]);
|
||||
}
|
||||
|
||||
low8 = max;
|
||||
high8 = min;
|
||||
|
||||
int dist = max-min;
|
||||
int bias = min*7 - (dist >> 1);
|
||||
int dist4 = dist*4;
|
||||
int dist2 = dist*2;
|
||||
|
||||
for (uint i = 0; i < n; i++)
|
||||
{
|
||||
int a = block[i][comp_index]*7 - bias;
|
||||
int ind,t;
|
||||
|
||||
t = (dist4 - a) >> 31; ind = t & 4; a -= dist4 & t;
|
||||
t = (dist2 - a) >> 31; ind += t & 2; a -= dist2 & t;
|
||||
t = (dist - a) >> 31; ind += t & 1;
|
||||
|
||||
ind = -ind & 7;
|
||||
ind ^= (2 > ind);
|
||||
|
||||
pSelectors[i] = static_cast<uint8>(ind);
|
||||
}
|
||||
}
|
||||
|
||||
void compress_alpha_block(dxt5_block* pDXT5_block, const color_quad_u8* pBlock, uint comp_index)
|
||||
{
|
||||
uint8 selectors[16];
|
||||
uint low8, high8;
|
||||
|
||||
compress_alpha_block(16, pBlock, low8, high8, selectors, comp_index);
|
||||
|
||||
pDXT5_block->set_low_alpha(low8);
|
||||
pDXT5_block->set_high_alpha(high8);
|
||||
|
||||
uint mask = 0;
|
||||
uint bits = 0;
|
||||
uint8* pDst = pDXT5_block->m_selectors;
|
||||
|
||||
for (uint i = 0; i < 16; i++)
|
||||
{
|
||||
mask |= (selectors[i] << bits);
|
||||
|
||||
if ((bits += 3) >= 8)
|
||||
{
|
||||
*pDst++ = static_cast<uint8>(mask);
|
||||
mask >>= 8;
|
||||
bits -= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void find_representative_colors(uint n, const color_quad_u8* pBlock, color_quad_u8& lo, color_quad_u8& hi)
|
||||
{
|
||||
uint64 ave64[3];
|
||||
ave64[0] = 0;
|
||||
ave64[1] = 0;
|
||||
ave64[2] = 0;
|
||||
|
||||
for (uint i = 0; i < n; i++)
|
||||
{
|
||||
ave64[0] += pBlock[i].r;
|
||||
ave64[1] += pBlock[i].g;
|
||||
ave64[2] += pBlock[i].b;
|
||||
}
|
||||
|
||||
uint ave[3];
|
||||
ave[0] = static_cast<uint>((ave64[0] + (n / 2)) / n);
|
||||
ave[1] = static_cast<uint>((ave64[1] + (n / 2)) / n);
|
||||
ave[2] = static_cast<uint>((ave64[2] + (n / 2)) / n);
|
||||
|
||||
int furthest_dist = -1;
|
||||
uint furthest_index = 0;
|
||||
for (uint i = 0; i < n; i++)
|
||||
{
|
||||
int r = pBlock[i].r - ave[0];
|
||||
int g = pBlock[i].g - ave[1];
|
||||
int b = pBlock[i].b - ave[2];
|
||||
int dist = r*r + g*g + b*b;
|
||||
if (dist > furthest_dist)
|
||||
{
|
||||
furthest_dist = dist;
|
||||
furthest_index = i;
|
||||
}
|
||||
}
|
||||
|
||||
color_quad_u8 lo_color(pBlock[furthest_index]);
|
||||
|
||||
int opp_dist = -1;
|
||||
uint opp_index = 0;
|
||||
for (uint i = 0; i < n; i++)
|
||||
{
|
||||
int r = pBlock[i].r - lo_color.r;
|
||||
int g = pBlock[i].g - lo_color.g;
|
||||
int b = pBlock[i].b - lo_color.b;
|
||||
int dist = r*r + g*g + b*b;
|
||||
if (dist > opp_dist)
|
||||
{
|
||||
opp_dist = dist;
|
||||
opp_index = i;
|
||||
}
|
||||
}
|
||||
|
||||
color_quad_u8 hi_color(pBlock[opp_index]);
|
||||
|
||||
for (uint i = 0; i < 3; i++)
|
||||
{
|
||||
lo_color[i] = static_cast<uint8>((lo_color[i] + ave[i]) >> 1);
|
||||
hi_color[i] = static_cast<uint8>((hi_color[i] + ave[i]) >> 1);
|
||||
}
|
||||
|
||||
const uint cMaxIters = 4;
|
||||
for (uint iter_index = 0; iter_index < cMaxIters; iter_index++)
|
||||
{
|
||||
if ((lo_color[0] == hi_color[0]) && (lo_color[1] == hi_color[1]) && (lo_color[2] == hi_color[2]))
|
||||
break;
|
||||
|
||||
uint64 new_color[2][3];
|
||||
uint weight[2];
|
||||
|
||||
utils::zero_object(new_color);
|
||||
utils::zero_object(weight);
|
||||
|
||||
int vec_r = hi_color[0] - lo_color[0];
|
||||
int vec_g = hi_color[1] - lo_color[1];
|
||||
int vec_b = hi_color[2] - lo_color[2];
|
||||
|
||||
int lo_dot = vec_r * lo_color[0] + vec_g * lo_color[1] + vec_b * lo_color[2];
|
||||
int hi_dot = vec_r * hi_color[0] + vec_g * hi_color[1] + vec_b * hi_color[2];
|
||||
int mid_dot = lo_dot + hi_dot;
|
||||
|
||||
vec_r *= 2;
|
||||
vec_g *= 2;
|
||||
vec_b *= 2;
|
||||
|
||||
for (uint i = 0; i < n; i++)
|
||||
{
|
||||
const color_quad_u8& c = pBlock[i];
|
||||
|
||||
const int dot = c[0] * vec_r + c[1] * vec_g + c[2] * vec_b;
|
||||
const uint match_index = (dot > mid_dot);
|
||||
|
||||
new_color[match_index][0] += c.r;
|
||||
new_color[match_index][1] += c.g;
|
||||
new_color[match_index][2] += c.b;
|
||||
weight[match_index]++;
|
||||
}
|
||||
|
||||
if ((!weight[0]) || (!weight[1]))
|
||||
break;
|
||||
|
||||
uint8 new_color8[2][3];
|
||||
|
||||
for (uint j = 0; j < 2; j++)
|
||||
for (uint i = 0; i < 3; i++)
|
||||
new_color8[j][i] = static_cast<uint8>((new_color[j][i] + (weight[j] / 2)) / weight[j]);
|
||||
|
||||
if ((new_color8[0][0] == lo_color[0]) && (new_color8[0][1] == lo_color[1]) && (new_color8[0][2] == lo_color[2]) &&
|
||||
(new_color8[1][0] == hi_color[0]) && (new_color8[1][1] == hi_color[1]) && (new_color8[1][2] == hi_color[2]))
|
||||
break;
|
||||
|
||||
for (uint i = 0; i < 3; i++)
|
||||
{
|
||||
lo_color[i] = new_color8[0][i];
|
||||
hi_color[i] = new_color8[1][i];
|
||||
}
|
||||
}
|
||||
|
||||
uint energy[2] = { 0, 0 };
|
||||
for (uint i = 0; i < 3; i++)
|
||||
{
|
||||
energy[0] += lo_color[i] * lo_color[i];
|
||||
energy[1] += hi_color[i] * hi_color[i];
|
||||
}
|
||||
|
||||
if (energy[0] > energy[1])
|
||||
utils::swap(lo_color, hi_color);
|
||||
|
||||
lo = lo_color;
|
||||
hi = hi_color;
|
||||
}
|
||||
|
||||
} // namespace dxt_fast
|
||||
|
||||
} // namespace crnlib
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// File: crn_dxt_fast.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
#include "crn_color.h"
|
||||
#include "crn_dxt.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
namespace dxt_fast
|
||||
{
|
||||
void compress_color_block(uint n, const color_quad_u8* block, uint& low16, uint& high16, uint8* pSelectors, bool refine = false);
|
||||
void compress_color_block(dxt1_block* pDXT1_block, const color_quad_u8* pBlock, bool refine = false);
|
||||
|
||||
void compress_alpha_block(uint n, const color_quad_u8* block, uint& low8, uint& high8, uint8* pSelectors, uint comp_index);
|
||||
void compress_alpha_block(dxt5_block* pDXT5_block, const color_quad_u8* pBlock, uint comp_index);
|
||||
|
||||
void find_representative_colors(uint n, const color_quad_u8* pBlock, color_quad_u8& lo, color_quad_u8& hi);
|
||||
|
||||
} // namespace dxt_fast
|
||||
|
||||
} // namespace crnlib
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,440 @@
|
||||
// File: crn_dxt_hc.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
#include "crn_dxt1.h"
|
||||
#include "crn_dxt5a.h"
|
||||
#include "crn_dxt_endpoint_refiner.h"
|
||||
#include "crn_image.h"
|
||||
#include "crn_dxt.h"
|
||||
#include "crn_image.h"
|
||||
#include "crn_dxt_hc_common.h"
|
||||
#include "crn_tree_clusterizer.h"
|
||||
#include "crn_task_pool.h"
|
||||
#include "crn_spinlock.h"
|
||||
|
||||
#define CRN_NO_FUNCTION_DEFINITIONS
|
||||
#include "../inc/crnlib.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
const uint cTotalCompressionPhases = 25;
|
||||
|
||||
class dxt_hc
|
||||
{
|
||||
public:
|
||||
dxt_hc();
|
||||
~dxt_hc();
|
||||
|
||||
struct pixel_chunk
|
||||
{
|
||||
pixel_chunk() { clear(); }
|
||||
|
||||
dxt_pixel_block m_blocks[cChunkBlockHeight][cChunkBlockWidth];
|
||||
|
||||
const color_quad_u8& operator() (uint cx, uint cy) const
|
||||
{
|
||||
CRNLIB_ASSERT((cx < cChunkPixelWidth) && (cy < cChunkPixelHeight));
|
||||
|
||||
return m_blocks[cy >> cBlockPixelHeightShift][cx >> cBlockPixelWidthShift].m_pixels
|
||||
[cy & (cBlockPixelHeight - 1)][cx & (cBlockPixelWidth - 1)];
|
||||
}
|
||||
|
||||
color_quad_u8& operator() (uint cx, uint cy)
|
||||
{
|
||||
CRNLIB_ASSERT((cx < cChunkPixelWidth) && (cy < cChunkPixelHeight));
|
||||
|
||||
return m_blocks[cy >> cBlockPixelHeightShift][cx >> cBlockPixelWidthShift].m_pixels
|
||||
[cy & (cBlockPixelHeight - 1)][cx & (cBlockPixelWidth - 1)];
|
||||
}
|
||||
|
||||
inline void clear()
|
||||
{
|
||||
utils::zero_object(*this);
|
||||
m_weight = 1.0f;
|
||||
}
|
||||
|
||||
float m_weight;
|
||||
};
|
||||
|
||||
typedef crnlib::vector<pixel_chunk> pixel_chunk_vec;
|
||||
|
||||
struct params
|
||||
{
|
||||
params() :
|
||||
m_color_endpoint_codebook_size(3072),
|
||||
m_color_selector_codebook_size(3072),
|
||||
m_alpha_endpoint_codebook_size(3072),
|
||||
m_alpha_selector_codebook_size(3072),
|
||||
m_adaptive_tile_color_psnr_derating(2.0f), // was 3.4f
|
||||
m_adaptive_tile_alpha_psnr_derating(2.0f),
|
||||
m_adaptive_tile_color_alpha_weighting_ratio(3.0f),
|
||||
m_num_levels(0),
|
||||
m_format(cDXT1),
|
||||
m_hierarchical(true),
|
||||
m_perceptual(true),
|
||||
m_debugging(false),
|
||||
m_pProgress_func(NULL),
|
||||
m_pProgress_func_data(NULL)
|
||||
{
|
||||
m_alpha_component_indices[0] = 3;
|
||||
m_alpha_component_indices[1] = 0;
|
||||
|
||||
for (uint i = 0; i < cCRNMaxLevels; i++)
|
||||
{
|
||||
m_levels[i].m_first_chunk = 0;
|
||||
m_levels[i].m_num_chunks = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Valid range for codebook sizes: [32,8192] (non-power of two values are okay)
|
||||
uint m_color_endpoint_codebook_size;
|
||||
uint m_color_selector_codebook_size;
|
||||
|
||||
uint m_alpha_endpoint_codebook_size;
|
||||
uint m_alpha_selector_codebook_size;
|
||||
|
||||
// Higher values cause fewer 8x4, 4x8, and 4x4 blocks to be utilized less often (lower quality/smaller files).
|
||||
// Lower values cause the encoder to use large tiles less often (better quality/larger files).
|
||||
// Valid range: [0.0,100.0].
|
||||
// A value of 0 will cause the encoder to only use tiles larger than 4x4 if doing so would incur to quality loss.
|
||||
float m_adaptive_tile_color_psnr_derating;
|
||||
|
||||
float m_adaptive_tile_alpha_psnr_derating;
|
||||
|
||||
float m_adaptive_tile_color_alpha_weighting_ratio;
|
||||
|
||||
uint m_alpha_component_indices[2];
|
||||
|
||||
struct miplevel_desc
|
||||
{
|
||||
uint m_first_chunk;
|
||||
uint m_num_chunks;
|
||||
};
|
||||
// The mip level data is optional!
|
||||
miplevel_desc m_levels[cCRNMaxLevels];
|
||||
uint m_num_levels;
|
||||
|
||||
dxt_format m_format;
|
||||
|
||||
// If m_hierarchical is false, only 4x4 blocks will be used by the encoder (leading to higher quality/larger files).
|
||||
bool m_hierarchical;
|
||||
|
||||
// If m_perceptual is true, perceptual color metrics will be used by the encoder.
|
||||
bool m_perceptual;
|
||||
|
||||
bool m_debugging;
|
||||
|
||||
crn_progress_callback_func m_pProgress_func;
|
||||
void* m_pProgress_func_data;
|
||||
};
|
||||
|
||||
void clear();
|
||||
|
||||
// Main compression function
|
||||
bool compress(const params& p, uint num_chunks, const pixel_chunk* pChunks, task_pool& task_pool);
|
||||
|
||||
// Output accessors
|
||||
inline uint get_num_chunks() const { return m_num_chunks; }
|
||||
|
||||
struct chunk_encoding
|
||||
{
|
||||
chunk_encoding() { utils::zero_object(*this); };
|
||||
|
||||
// Index into g_chunk_encodings.
|
||||
uint8 m_encoding_index;
|
||||
|
||||
// Number of tiles, endpoint indices.
|
||||
uint8 m_num_tiles;
|
||||
|
||||
// Color, alpha0, alpha1
|
||||
enum { cColorIndex = 0, cAlpha0Index = 1, cAlpha1Index = 2 };
|
||||
uint16 m_endpoint_indices[3][cChunkMaxTiles];
|
||||
uint16 m_selector_indices[3][cChunkBlockHeight][cChunkBlockWidth]; // [block_y][block_x]
|
||||
};
|
||||
|
||||
typedef crnlib::vector<chunk_encoding> chunk_encoding_vec;
|
||||
|
||||
inline const chunk_encoding& get_chunk_encoding(uint chunk_index) const { return m_chunk_encoding[chunk_index]; }
|
||||
inline const chunk_encoding_vec& get_chunk_encoding_vec() const { return m_chunk_encoding; }
|
||||
|
||||
struct selectors
|
||||
{
|
||||
selectors() { utils::zero_object(*this); }
|
||||
|
||||
uint8 m_selectors[cBlockPixelHeight][cBlockPixelWidth];
|
||||
|
||||
uint8 get_by_index(uint i) const { CRNLIB_ASSERT(i < (cBlockPixelWidth * cBlockPixelHeight)); return *(&m_selectors[0][0] + i); }
|
||||
void set_by_index(uint i, uint v) { CRNLIB_ASSERT(i < (cBlockPixelWidth * cBlockPixelHeight)); *(&m_selectors[0][0] + i) = static_cast<uint8>(v); }
|
||||
};
|
||||
typedef crnlib::vector<selectors> selectors_vec;
|
||||
|
||||
// Color endpoints
|
||||
inline uint get_color_endpoint_codebook_size() const { return m_color_endpoints.size(); }
|
||||
inline uint get_color_endpoint(uint codebook_index) const { return m_color_endpoints[codebook_index]; }
|
||||
const crnlib::vector<uint>& get_color_endpoint_vec() const { return m_color_endpoints; }
|
||||
|
||||
// Color selectors
|
||||
uint get_color_selector_codebook_size() const { return m_color_selectors.size(); }
|
||||
const selectors& get_color_selectors(uint codebook_index) const { return m_color_selectors[codebook_index]; }
|
||||
const crnlib::vector<selectors>& get_color_selectors_vec() const { return m_color_selectors; }
|
||||
|
||||
// Alpha endpoints
|
||||
inline uint get_alpha_endpoint_codebook_size() const { return m_alpha_endpoints.size(); }
|
||||
inline uint get_alpha_endpoint(uint codebook_index) const { return m_alpha_endpoints[codebook_index]; }
|
||||
const crnlib::vector<uint>& get_alpha_endpoint_vec() const { return m_alpha_endpoints; }
|
||||
|
||||
// Alpha selectors
|
||||
uint get_alpha_selector_codebook_size() const { return m_alpha_selectors.size(); }
|
||||
const selectors& get_alpha_selectors(uint codebook_index) const { return m_alpha_selectors[codebook_index]; }
|
||||
const crnlib::vector<selectors>& get_alpha_selectors_vec() const { return m_alpha_selectors; }
|
||||
|
||||
// Debug images
|
||||
const pixel_chunk_vec& get_compressed_chunk_pixels() const { return m_dbg_chunk_pixels; }
|
||||
const pixel_chunk_vec& get_compressed_chunk_pixels_tile_vis() const { return m_dbg_chunk_pixels_tile_vis; }
|
||||
const pixel_chunk_vec& get_compressed_chunk_pixels_color_quantized() const { return m_dbg_chunk_pixels_color_quantized; }
|
||||
const pixel_chunk_vec& get_compressed_chunk_pixels_alpha_quantized() const { return m_dbg_chunk_pixels_alpha_quantized; }
|
||||
const pixel_chunk_vec& get_compressed_chunk_pixels_final() const { return m_dbg_chunk_pixels_final; }
|
||||
|
||||
const pixel_chunk_vec& get_compressed_chunk_pixels_orig_color_selectors() const { return m_dbg_chunk_pixels_orig_color_selectors; }
|
||||
const pixel_chunk_vec& get_compressed_chunk_pixels_quantized_color_selectors() const { return m_dbg_chunk_pixels_quantized_color_selectors; }
|
||||
const pixel_chunk_vec& get_compressed_chunk_pixels_final_color_selectors() const { return m_dbg_chunk_pixels_final_color_selectors; }
|
||||
|
||||
const pixel_chunk_vec& get_compressed_chunk_pixels_orig_alpha_selectors() const { return m_dbg_chunk_pixels_orig_alpha_selectors; }
|
||||
const pixel_chunk_vec& get_compressed_chunk_pixels_quantized_alpha_selectors() const { return m_dbg_chunk_pixels_quantized_alpha_selectors; }
|
||||
const pixel_chunk_vec& get_compressed_chunk_pixels_final_alpha_selectors() const { return m_dbg_chunk_pixels_final_alpha_selectors; }
|
||||
|
||||
static void create_debug_image_from_chunks(uint num_chunks_x, uint num_chunks_y, const pixel_chunk_vec& chunks, const chunk_encoding_vec *pChunk_encodings, image_u8& img, bool serpentine_scan, int comp_index = -1);
|
||||
|
||||
private:
|
||||
params m_params;
|
||||
|
||||
uint m_num_chunks;
|
||||
const pixel_chunk* m_pChunks;
|
||||
|
||||
chunk_encoding_vec m_chunk_encoding;
|
||||
|
||||
uint m_num_alpha_blocks; // 0, 1, or 2
|
||||
bool m_has_color_blocks;
|
||||
bool m_has_alpha0_blocks;
|
||||
bool m_has_alpha1_blocks;
|
||||
|
||||
struct compressed_tile
|
||||
{
|
||||
uint m_endpoint_cluster_index;
|
||||
uint m_first_endpoint;
|
||||
uint m_second_endpoint;
|
||||
|
||||
uint8 m_selectors[cChunkPixelWidth * cChunkPixelHeight];
|
||||
|
||||
void set_selector(uint x, uint y, uint s)
|
||||
{
|
||||
CRNLIB_ASSERT((x < m_pixel_width) && (y < m_pixel_height));
|
||||
m_selectors[x + y * m_pixel_width] = static_cast<uint8>(s);
|
||||
}
|
||||
|
||||
uint get_selector(uint x, uint y) const
|
||||
{
|
||||
CRNLIB_ASSERT((x < m_pixel_width) && (y < m_pixel_height));
|
||||
return m_selectors[x + y * m_pixel_width];
|
||||
}
|
||||
|
||||
uint8 m_pixel_width;
|
||||
uint8 m_pixel_height;
|
||||
|
||||
uint8 m_layout_index;
|
||||
|
||||
bool m_alpha_encoding;
|
||||
};
|
||||
|
||||
struct compressed_chunk
|
||||
{
|
||||
compressed_chunk() { utils::zero_object(*this); }
|
||||
|
||||
uint8 m_encoding_index;
|
||||
|
||||
uint8 m_num_tiles;
|
||||
|
||||
compressed_tile m_tiles[cChunkMaxTiles];
|
||||
compressed_tile m_quantized_tiles[cChunkMaxTiles];
|
||||
|
||||
uint16 m_endpoint_cluster_index[cChunkMaxTiles];
|
||||
uint16 m_selector_cluster_index[cChunkBlockHeight][cChunkBlockWidth];
|
||||
};
|
||||
|
||||
typedef crnlib::vector<compressed_chunk> compressed_chunk_vec;
|
||||
enum
|
||||
{
|
||||
cColorChunks = 0,
|
||||
cAlpha0Chunks = 1,
|
||||
cAlpha1Chunks = 2,
|
||||
|
||||
cNumCompressedChunkVecs = 3
|
||||
};
|
||||
compressed_chunk_vec m_compressed_chunks[cNumCompressedChunkVecs];
|
||||
|
||||
int32 m_encoding_hist[cNumChunkEncodings];
|
||||
|
||||
int32 m_total_tiles;
|
||||
|
||||
void compress_dxt1_block(
|
||||
dxt1_endpoint_optimizer::results& results,
|
||||
uint chunk_index, const image_u8& chunk, uint x_ofs, uint y_ofs, uint width, uint height,
|
||||
uint8* pSelectors);
|
||||
|
||||
void compress_dxt5_block(
|
||||
dxt5_endpoint_optimizer::results& results,
|
||||
uint chunk_index, const image_u8& chunk, uint x_ofs, uint y_ofs, uint width, uint height, uint component_index,
|
||||
uint8* pAlpha_selectors);
|
||||
|
||||
void determine_compressed_chunks_task(uint64 data, void* pData_ptr);
|
||||
bool determine_compressed_chunks();
|
||||
|
||||
struct tile_cluster
|
||||
{
|
||||
tile_cluster() : m_first_endpoint(0), m_second_endpoint(0), m_error(0), m_alpha_encoding(false) { }
|
||||
|
||||
// first = chunk, second = tile
|
||||
// if an alpha tile, second's upper 16 bits contains the alpha index (0 or 1)
|
||||
crnlib::vector< std::pair<uint, uint> > m_tiles;
|
||||
|
||||
uint m_first_endpoint;
|
||||
uint m_second_endpoint;
|
||||
uint64 m_error;
|
||||
|
||||
bool m_alpha_encoding;
|
||||
};
|
||||
|
||||
typedef crnlib::vector<tile_cluster> tile_cluster_vec;
|
||||
|
||||
tile_cluster_vec m_color_clusters;
|
||||
tile_cluster_vec m_alpha_clusters;
|
||||
|
||||
selectors_vec m_color_selectors;
|
||||
selectors_vec m_alpha_selectors;
|
||||
|
||||
// For each selector, this array indicates every chunk/tile/tile block that use this color selector.
|
||||
struct block_id
|
||||
{
|
||||
block_id() { utils::zero_object(*this); }
|
||||
|
||||
block_id(uint chunk_index, uint alpha_index, uint tile_index, uint block_x, uint block_y) :
|
||||
m_chunk_index(chunk_index), m_alpha_index((uint8)alpha_index), m_tile_index((uint8)tile_index), m_block_x((uint8)block_x), m_block_y((uint8)block_y) { }
|
||||
|
||||
uint m_chunk_index;
|
||||
uint8 m_alpha_index;
|
||||
uint8 m_tile_index;
|
||||
uint8 m_block_x;
|
||||
uint8 m_block_y;
|
||||
};
|
||||
|
||||
typedef crnlib::vector< crnlib::vector< block_id > > chunk_blocks_using_selectors_vec;
|
||||
chunk_blocks_using_selectors_vec m_chunk_blocks_using_color_selectors;
|
||||
chunk_blocks_using_selectors_vec m_chunk_blocks_using_alpha_selectors; // second's upper 16 bits contain alpha index!
|
||||
|
||||
crnlib::vector<uint> m_color_endpoints; // not valid until end, only for user access
|
||||
crnlib::vector<uint> m_alpha_endpoints; // not valid until end, only for user access
|
||||
|
||||
// Debugging
|
||||
pixel_chunk_vec m_dbg_chunk_pixels;
|
||||
pixel_chunk_vec m_dbg_chunk_pixels_tile_vis;
|
||||
pixel_chunk_vec m_dbg_chunk_pixels_color_quantized;
|
||||
pixel_chunk_vec m_dbg_chunk_pixels_alpha_quantized;
|
||||
|
||||
pixel_chunk_vec m_dbg_chunk_pixels_orig_color_selectors;
|
||||
pixel_chunk_vec m_dbg_chunk_pixels_quantized_color_selectors;
|
||||
pixel_chunk_vec m_dbg_chunk_pixels_final_color_selectors;
|
||||
|
||||
pixel_chunk_vec m_dbg_chunk_pixels_orig_alpha_selectors;
|
||||
pixel_chunk_vec m_dbg_chunk_pixels_quantized_alpha_selectors;
|
||||
pixel_chunk_vec m_dbg_chunk_pixels_final_alpha_selectors;
|
||||
|
||||
pixel_chunk_vec m_dbg_chunk_pixels_final;
|
||||
|
||||
uint32 m_main_thread_id;
|
||||
bool m_canceled;
|
||||
task_pool* m_pTask_pool;
|
||||
|
||||
int m_prev_phase_index;
|
||||
int m_prev_percentage_complete;
|
||||
|
||||
typedef vec<6, float> vec6F;
|
||||
typedef vec<16, float> vec16F;
|
||||
typedef tree_clusterizer<vec2F> vec2F_tree_vq;
|
||||
typedef tree_clusterizer<vec6F> vec6F_tree_vq;
|
||||
typedef tree_clusterizer<vec16F> vec16F_tree_vq;
|
||||
|
||||
struct assign_color_endpoint_clusters_state
|
||||
{
|
||||
CRNLIB_NO_COPY_OR_ASSIGNMENT_OP(assign_color_endpoint_clusters_state);
|
||||
|
||||
assign_color_endpoint_clusters_state(vec6F_tree_vq& vq, crnlib::vector< crnlib::vector<vec6F> >& training_vecs) :
|
||||
m_vq(vq), m_training_vecs(training_vecs) { }
|
||||
|
||||
vec6F_tree_vq& m_vq;
|
||||
crnlib::vector< crnlib::vector<vec6F> >& m_training_vecs;
|
||||
};
|
||||
|
||||
struct create_selector_codebook_state
|
||||
{
|
||||
CRNLIB_NO_COPY_OR_ASSIGNMENT_OP(create_selector_codebook_state);
|
||||
|
||||
create_selector_codebook_state(dxt_hc& hc, bool alpha_blocks, uint comp_index_start, uint comp_index_end, vec16F_tree_vq& selector_vq, chunk_blocks_using_selectors_vec& chunk_blocks_using_selectors, selectors_vec& selectors_cb) :
|
||||
m_hc(hc),
|
||||
m_alpha_blocks(alpha_blocks),
|
||||
m_comp_index_start(comp_index_start),
|
||||
m_comp_index_end(comp_index_end),
|
||||
m_selector_vq(selector_vq),
|
||||
m_chunk_blocks_using_selectors(chunk_blocks_using_selectors),
|
||||
m_selectors_cb(selectors_cb)
|
||||
{
|
||||
}
|
||||
|
||||
dxt_hc& m_hc;
|
||||
bool m_alpha_blocks;
|
||||
uint m_comp_index_start;
|
||||
uint m_comp_index_end;
|
||||
vec16F_tree_vq& m_selector_vq;
|
||||
chunk_blocks_using_selectors_vec& m_chunk_blocks_using_selectors;
|
||||
selectors_vec& m_selectors_cb;
|
||||
|
||||
mutable spinlock m_chunk_blocks_using_selectors_lock;
|
||||
};
|
||||
|
||||
void assign_color_endpoint_clusters_task(uint64 data, void* pData_ptr);
|
||||
bool determine_color_endpoint_clusters();
|
||||
|
||||
struct determine_alpha_endpoint_clusters_state
|
||||
{
|
||||
vec2F_tree_vq m_vq;
|
||||
crnlib::vector< crnlib::vector<vec2F> > m_training_vecs[2];
|
||||
};
|
||||
|
||||
void determine_alpha_endpoint_clusters_task(uint64 data, void* pData_ptr);
|
||||
bool determine_alpha_endpoint_clusters();
|
||||
|
||||
void determine_color_endpoint_codebook_task(uint64 data, void* pData_ptr);
|
||||
bool determine_color_endpoint_codebook();
|
||||
|
||||
void determine_alpha_endpoint_codebook_task(uint64 data, void* pData_ptr);
|
||||
bool determine_alpha_endpoint_codebook();
|
||||
|
||||
void create_quantized_debug_images();
|
||||
|
||||
void create_selector_codebook_task(uint64 data, void* pData_ptr);
|
||||
bool create_selector_codebook(bool alpha_blocks);
|
||||
|
||||
bool refine_quantized_color_endpoints();
|
||||
bool refine_quantized_color_selectors();
|
||||
bool refine_quantized_alpha_endpoints();
|
||||
bool refine_quantized_alpha_selectors();
|
||||
void create_final_debug_image();
|
||||
bool create_chunk_encodings();
|
||||
bool update_progress(uint phase_index, uint subphase_index, uint subphase_total);
|
||||
bool compress_internal(const params& p, uint num_chunks, const pixel_chunk* pChunks);
|
||||
};
|
||||
|
||||
CRNLIB_DEFINE_BITWISE_COPYABLE(dxt_hc::pixel_chunk);
|
||||
CRNLIB_DEFINE_BITWISE_COPYABLE(dxt_hc::chunk_encoding);
|
||||
CRNLIB_DEFINE_BITWISE_COPYABLE(dxt_hc::selectors);
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,47 @@
|
||||
// File: crn_dxt_hc_common.cpp
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#include "crn_core.h"
|
||||
#include "crn_dxt_hc_common.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
chunk_encoding_desc g_chunk_encodings[cNumChunkEncodings] =
|
||||
{
|
||||
{ 1, { { 0, 0, 8, 8, 0 } } },
|
||||
|
||||
{ 2, { { 0, 0, 8, 4, 1 }, { 0, 4, 8, 4, 2 } } },
|
||||
{ 2, { { 0, 0, 4, 8, 3 }, { 4, 0, 4, 8, 4 } } },
|
||||
|
||||
{ 3, { { 0, 0, 8, 4, 1 }, { 0, 4, 4, 4, 7 }, { 4, 4, 4, 4, 8 } } },
|
||||
{ 3, { { 0, 4, 8, 4, 2 }, { 0, 0, 4, 4, 5 }, { 4, 0, 4, 4, 6 } } },
|
||||
|
||||
{ 3, { { 0, 0, 4, 8, 3 }, { 4, 0, 4, 4, 6 }, { 4, 4, 4, 4, 8 } } },
|
||||
{ 3, { { 4, 0, 4, 8, 4 }, { 0, 0, 4, 4, 5 }, { 0, 4, 4, 4, 7 } } },
|
||||
|
||||
{ 4, { { 0, 0, 4, 4, 5 }, { 4, 0, 4, 4, 6 }, { 0, 4, 4, 4, 7 }, { 4, 4, 4, 4, 8 } } }
|
||||
};
|
||||
|
||||
chunk_tile_desc g_chunk_tile_layouts[cNumChunkTileLayouts] =
|
||||
{
|
||||
// 2x2
|
||||
{ 0, 0, 8, 8, 0 },
|
||||
|
||||
// 2x1
|
||||
{ 0, 0, 8, 4, 1 },
|
||||
{ 0, 4, 8, 4, 2 },
|
||||
|
||||
// 1x2
|
||||
{ 0, 0, 4, 8, 3 },
|
||||
{ 4, 0, 4, 8, 4 },
|
||||
|
||||
// 1x1
|
||||
{ 0, 0, 4, 4, 5 },
|
||||
{ 4, 0, 4, 4, 6 },
|
||||
{ 0, 4, 4, 4, 7 },
|
||||
{ 4, 4, 4, 4, 8 }
|
||||
};
|
||||
|
||||
} // namespace crnlib
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// File: crn_dxt_hc_common.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
struct chunk_tile_desc
|
||||
{
|
||||
// These values are in pixels, and always a multiple of cBlockPixelWidth/cBlockPixelHeight.
|
||||
uint m_x_ofs;
|
||||
uint m_y_ofs;
|
||||
uint m_width;
|
||||
uint m_height;
|
||||
uint m_layout_index;
|
||||
};
|
||||
|
||||
struct chunk_encoding_desc
|
||||
{
|
||||
uint m_num_tiles;
|
||||
chunk_tile_desc m_tiles[4];
|
||||
};
|
||||
|
||||
const uint cChunkPixelWidth = 8;
|
||||
const uint cChunkPixelHeight = 8;
|
||||
const uint cChunkBlockWidth = 2;
|
||||
const uint cChunkBlockHeight = 2;
|
||||
|
||||
const uint cChunkMaxTiles = 4;
|
||||
|
||||
const uint cBlockPixelWidthShift = 2;
|
||||
const uint cBlockPixelHeightShift = 2;
|
||||
|
||||
const uint cBlockPixelWidth = 4;
|
||||
const uint cBlockPixelHeight = 4;
|
||||
|
||||
const uint cNumChunkEncodings = 8;
|
||||
extern chunk_encoding_desc g_chunk_encodings[cNumChunkEncodings];
|
||||
|
||||
const uint cNumChunkTileLayouts = 9;
|
||||
const uint cFirst4x4ChunkTileLayout = 5;
|
||||
extern chunk_tile_desc g_chunk_tile_layouts[cNumChunkTileLayouts];
|
||||
|
||||
} // namespace crnlib
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,218 @@
|
||||
// File: crn_dxt_image.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
#include "crn_dxt1.h"
|
||||
#include "crn_dxt5a.h"
|
||||
#include "crn_image.h"
|
||||
|
||||
#define CRNLIB_SUPPORT_ATI_COMPRESS 0
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
class task_pool;
|
||||
|
||||
class dxt_image
|
||||
{
|
||||
public:
|
||||
dxt_image();
|
||||
dxt_image(const dxt_image& other);
|
||||
dxt_image& operator= (const dxt_image& rhs);
|
||||
|
||||
void clear();
|
||||
|
||||
inline bool is_valid() const { return m_blocks_x > 0; }
|
||||
|
||||
uint get_width() const { return m_width; }
|
||||
uint get_height() const { return m_height; }
|
||||
|
||||
uint get_blocks_x() const { return m_blocks_x; }
|
||||
uint get_blocks_y() const { return m_blocks_y; }
|
||||
uint get_total_blocks() const { return m_blocks_x * m_blocks_y; }
|
||||
|
||||
uint get_elements_per_block() const { return m_num_elements_per_block; }
|
||||
uint get_bytes_per_block() const { return m_bytes_per_block; }
|
||||
|
||||
dxt_format get_format() const { return m_format; }
|
||||
|
||||
bool has_color() const { return (m_format == cDXT1) || (m_format == cDXT1A) || (m_format == cDXT3) || (m_format == cDXT5); }
|
||||
|
||||
// Will be pretty slow if the image is DXT1, as this method scans for alpha blocks/selectors.
|
||||
bool has_alpha() const;
|
||||
|
||||
enum element_type
|
||||
{
|
||||
cUnused = 0,
|
||||
|
||||
cColor,
|
||||
|
||||
cAlpha3,
|
||||
cAlpha5,
|
||||
};
|
||||
|
||||
element_type get_element_type(uint element_index) const { CRNLIB_ASSERT(element_index < m_num_elements_per_block); return m_element_type[element_index]; }
|
||||
|
||||
//Returns -1 for RGB, or [0,3]
|
||||
int8 get_element_component_index(uint element_index) const { CRNLIB_ASSERT(element_index < m_num_elements_per_block); return m_element_component_index[element_index]; }
|
||||
|
||||
struct element
|
||||
{
|
||||
uint8 m_bytes[8];
|
||||
|
||||
uint get_le_word(uint index) const { CRNLIB_ASSERT(index < 4); return m_bytes[index*2] | (m_bytes[index * 2 + 1] << 8); }
|
||||
uint get_be_word(uint index) const { CRNLIB_ASSERT(index < 4); return m_bytes[index*2 + 1] | (m_bytes[index * 2] << 8); }
|
||||
|
||||
void set_le_word(uint index, uint val) { CRNLIB_ASSERT((index < 4) && (val <= UINT16_MAX)); m_bytes[index*2] = static_cast<uint8>(val & 0xFF); m_bytes[index * 2 + 1] = static_cast<uint8>((val >> 8) & 0xFF); }
|
||||
void set_be_word(uint index, uint val) { CRNLIB_ASSERT((index < 4) && (val <= UINT16_MAX)); m_bytes[index*2+1] = static_cast<uint8>(val & 0xFF); m_bytes[index * 2] = static_cast<uint8>((val >> 8) & 0xFF); }
|
||||
|
||||
void clear()
|
||||
{
|
||||
memset(this, 0, sizeof(*this));
|
||||
}
|
||||
};
|
||||
|
||||
typedef crnlib::vector<element> element_vec;
|
||||
|
||||
bool init(dxt_format fmt, uint width, uint height, bool clear_elements);
|
||||
bool init(dxt_format fmt, uint width, uint height, uint num_elements, element* pElements, bool create_copy);
|
||||
|
||||
struct pack_params
|
||||
{
|
||||
pack_params()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
m_quality = cCRNDXTQualityUber;
|
||||
m_perceptual = true;
|
||||
m_grayscale_sampling = false;
|
||||
m_use_both_block_types = true;
|
||||
m_endpoint_caching = true;
|
||||
m_compressor = cCRNDXTCompressorCRN;
|
||||
m_pProgress_callback = NULL;
|
||||
m_pProgress_callback_user_data_ptr = NULL;
|
||||
m_dxt1a_alpha_threshold = 128;
|
||||
m_num_helper_threads = 0;
|
||||
m_progress_start = 0;
|
||||
m_progress_range = 100;
|
||||
m_use_transparent_indices_for_black = false;
|
||||
m_pTask_pool = NULL;
|
||||
m_color_weights[0] = 1;
|
||||
m_color_weights[1] = 1;
|
||||
m_color_weights[2] = 1;
|
||||
}
|
||||
|
||||
void init(const crn_comp_params ¶ms)
|
||||
{
|
||||
m_perceptual = (params.m_flags & cCRNCompFlagPerceptual) != 0;
|
||||
m_num_helper_threads = params.m_num_helper_threads;
|
||||
m_use_both_block_types = (params.m_flags & cCRNCompFlagUseBothBlockTypes) != 0;
|
||||
m_use_transparent_indices_for_black = (params.m_flags & cCRNCompFlagUseTransparentIndicesForBlack) != 0;
|
||||
m_dxt1a_alpha_threshold = params.m_dxt1a_alpha_threshold;
|
||||
m_quality = params.m_dxt_quality;
|
||||
m_endpoint_caching = (params.m_flags & cCRNCompFlagDisableEndpointCaching) == 0;
|
||||
m_grayscale_sampling = (params.m_flags & cCRNCompFlagGrayscaleSampling) != 0;
|
||||
m_compressor = params.m_dxt_compressor_type;
|
||||
}
|
||||
|
||||
uint m_dxt1a_alpha_threshold;
|
||||
|
||||
uint m_num_helper_threads;
|
||||
|
||||
crn_dxt_quality m_quality;
|
||||
|
||||
crn_dxt_compressor_type m_compressor;
|
||||
|
||||
bool m_perceptual;
|
||||
bool m_grayscale_sampling;
|
||||
bool m_use_both_block_types;
|
||||
bool m_endpoint_caching;
|
||||
bool m_use_transparent_indices_for_black;
|
||||
|
||||
typedef bool (*progress_callback_func)(uint percentage_complete, void* pUser_data_ptr);
|
||||
progress_callback_func m_pProgress_callback;
|
||||
void* m_pProgress_callback_user_data_ptr;
|
||||
|
||||
uint m_progress_start;
|
||||
uint m_progress_range;
|
||||
|
||||
task_pool *m_pTask_pool;
|
||||
|
||||
int m_color_weights[3];
|
||||
};
|
||||
|
||||
bool init(dxt_format fmt, const image_u8& img, const pack_params& p = dxt_image::pack_params());
|
||||
|
||||
bool unpack(image_u8& img) const;
|
||||
|
||||
void endian_swap();
|
||||
|
||||
uint get_num_elements() const { return m_elements.size(); }
|
||||
|
||||
const element_vec& get_element_vec() const { return m_elements; }
|
||||
element_vec& get_element_vec() { return m_elements; }
|
||||
|
||||
const element& get_element(uint block_x, uint block_y, uint element_index) const;
|
||||
element& get_element(uint block_x, uint block_y, uint element_index);
|
||||
|
||||
const element* get_element_ptr() const { return m_pElements; }
|
||||
element* get_element_ptr() { return m_pElements; }
|
||||
|
||||
uint get_size_in_bytes() const { return m_elements.size() * sizeof(element); }
|
||||
uint get_row_pitch_in_bytes() const { return m_blocks_x * m_bytes_per_block; }
|
||||
|
||||
color_quad_u8 get_pixel(uint x, uint y) const;
|
||||
uint get_pixel_alpha(uint x, uint y, uint element_index) const;
|
||||
|
||||
void set_pixel(uint x, uint y, const color_quad_u8& c, bool perceptual = true);
|
||||
|
||||
// get_block_pixels() only sets those components stored in the image!
|
||||
void get_block_pixels(uint block_x, uint block_y, color_quad_u8* pPixels) const;
|
||||
|
||||
void set_block_pixels(uint block_x, uint block_y, const color_quad_u8* pPixels, const pack_params& p, dxt1_endpoint_optimizer& dxt1_optimizer, dxt5_endpoint_optimizer& dxt5_optimizer);
|
||||
void set_block_pixels(uint block_x, uint block_y, const color_quad_u8* pPixels, const pack_params& p);
|
||||
|
||||
void get_block_endpoints(uint block_x, uint block_y, uint element_index, uint& packed_low_endpoint, uint& packed_high_endpoint) const;
|
||||
|
||||
// Returns a value representing the component(s) that where actually set, where -1 = RGB.
|
||||
// This method does not always set every component!
|
||||
int get_block_endpoints(uint block_x, uint block_y, uint element_index, color_quad_u8& low_endpoint, color_quad_u8& high_endpoint, bool scaled = true) const;
|
||||
|
||||
// pColors should point to a 16 entry array, to handle DXT3.
|
||||
// Returns the number of block colors: 3, 4, 6, 8, or 16.
|
||||
uint get_block_colors(uint block_x, uint block_y, uint element_index, color_quad_u8* pColors);
|
||||
|
||||
uint get_selector(uint x, uint y, uint element_index) const;
|
||||
|
||||
void change_dxt1_to_dxt1a();
|
||||
|
||||
private:
|
||||
element_vec m_elements;
|
||||
element* m_pElements;
|
||||
|
||||
uint m_width;
|
||||
uint m_height;
|
||||
|
||||
uint m_blocks_x;
|
||||
uint m_blocks_y;
|
||||
uint m_total_blocks;
|
||||
uint m_total_elements;
|
||||
|
||||
uint m_num_elements_per_block; // 1 or 2
|
||||
uint m_bytes_per_block; // 8 or 16
|
||||
|
||||
int8 m_element_component_index[2];
|
||||
element_type m_element_type[2];
|
||||
|
||||
dxt_format m_format; // DXT1, 1A, 3, 5, N/3DC, or A
|
||||
|
||||
bool init_internal(dxt_format fmt, uint width, uint height);
|
||||
void init_task(uint64 data, void* pData_ptr);
|
||||
|
||||
#if CRNLIB_SUPPORT_ATI_COMPRESS
|
||||
bool init_ati_compress(dxt_format fmt, const image_u8& img, const pack_params& p);
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,206 @@
|
||||
// File: crn_dynamic_stream.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
#include "crn_data_stream.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
class dynamic_stream : public data_stream
|
||||
{
|
||||
public:
|
||||
dynamic_stream(uint initial_size, const wchar_t* pName = L"dynamic_stream", uint attribs = cDataStreamSeekable | cDataStreamWritable | cDataStreamReadable) :
|
||||
data_stream(pName, attribs),
|
||||
m_ofs(0)
|
||||
{
|
||||
open(initial_size, pName, attribs);
|
||||
}
|
||||
|
||||
dynamic_stream(const void* pBuf, uint size, const wchar_t* pName = L"dynamic_stream", uint attribs = cDataStreamSeekable | cDataStreamWritable | cDataStreamReadable) :
|
||||
data_stream(pName, attribs),
|
||||
m_ofs(0)
|
||||
{
|
||||
open(pBuf, size, pName, attribs);
|
||||
}
|
||||
|
||||
dynamic_stream() :
|
||||
data_stream(),
|
||||
m_ofs(0)
|
||||
{
|
||||
open();
|
||||
}
|
||||
|
||||
virtual ~dynamic_stream()
|
||||
{
|
||||
}
|
||||
|
||||
bool open(uint initial_size = 0, const wchar_t* pName = L"dynamic_stream", uint attribs = cDataStreamSeekable | cDataStreamWritable | cDataStreamReadable)
|
||||
{
|
||||
close();
|
||||
|
||||
m_opened = true;
|
||||
m_buf.clear();
|
||||
m_buf.resize(initial_size);
|
||||
m_ofs = 0;
|
||||
m_name.set(pName ? pName : L"dynamic_stream");
|
||||
m_attribs = static_cast<attribs_t>(attribs);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool reopen(const wchar_t* pName, uint attribs)
|
||||
{
|
||||
if (!m_opened)
|
||||
{
|
||||
return open(0, pName, attribs);
|
||||
}
|
||||
|
||||
m_name.set(pName ? pName : L"dynamic_stream");
|
||||
m_attribs = static_cast<attribs_t>(attribs);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool open(const void* pBuf, uint size, const wchar_t* pName = L"dynamic_stream", uint attribs = cDataStreamSeekable | cDataStreamWritable | cDataStreamReadable)
|
||||
{
|
||||
if (!m_opened)
|
||||
{
|
||||
m_opened = true;
|
||||
m_buf.resize(size);
|
||||
if (size)
|
||||
{
|
||||
CRNLIB_ASSERT(pBuf);
|
||||
memcpy(&m_buf[0], pBuf, size);
|
||||
}
|
||||
m_ofs = 0;
|
||||
m_name.set(pName ? pName : L"dynamic_stream");
|
||||
m_attribs = static_cast<attribs_t>(attribs);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool close()
|
||||
{
|
||||
if (m_opened)
|
||||
{
|
||||
m_opened = false;
|
||||
m_buf.clear();
|
||||
m_ofs = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const crnlib::vector<uint8>& get_buf() const { return m_buf; }
|
||||
crnlib::vector<uint8>& get_buf() { return m_buf; }
|
||||
|
||||
void reserve(uint size)
|
||||
{
|
||||
if (m_opened)
|
||||
{
|
||||
m_buf.reserve(size);
|
||||
}
|
||||
}
|
||||
|
||||
virtual const void* get_ptr() const { return m_buf.empty() ? NULL : &m_buf[0]; }
|
||||
|
||||
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_buf.size());
|
||||
|
||||
uint bytes_left = m_buf.size() - m_ofs;
|
||||
|
||||
len = math::minimum<uint>(len, bytes_left);
|
||||
|
||||
if (len)
|
||||
memcpy(pBuf, &m_buf[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_buf.size());
|
||||
|
||||
uint new_ofs = m_ofs + len;
|
||||
if (new_ofs > m_buf.size())
|
||||
m_buf.resize(new_ofs);
|
||||
|
||||
memcpy(&m_buf[m_ofs], pBuf, len);
|
||||
m_ofs = new_ofs;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
virtual bool flush()
|
||||
{
|
||||
if (!m_opened)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual uint64 get_size()
|
||||
{
|
||||
if (!m_opened)
|
||||
return 0;
|
||||
|
||||
return m_buf.size();
|
||||
}
|
||||
|
||||
virtual uint64 get_remaining()
|
||||
{
|
||||
if (!m_opened)
|
||||
return 0;
|
||||
|
||||
CRNLIB_ASSERT(m_ofs <= m_buf.size());
|
||||
|
||||
return m_buf.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_buf.size())
|
||||
return false;
|
||||
|
||||
m_ofs = static_cast<uint>(new_ofs);
|
||||
|
||||
post_seek();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
crnlib::vector<uint8> m_buf;
|
||||
uint m_ofs;
|
||||
};
|
||||
|
||||
} // namespace crnlib
|
||||
|
||||
@@ -0,0 +1,695 @@
|
||||
// File: crn_dynamic_string.cpp
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#include "crn_core.h"
|
||||
#include "crn_dynamic_string.h"
|
||||
#include "crn_dynamic_wstring.h"
|
||||
#include "crn_winhdr.h"
|
||||
#include <stdio.h>
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
dynamic_string g_empty_dynamic_string;
|
||||
|
||||
dynamic_string::dynamic_string(eVarArg dummy, const char* p, ...) :
|
||||
m_buf_size(0), m_len(0), m_pStr(NULL)
|
||||
{
|
||||
dummy;
|
||||
|
||||
CRNLIB_ASSERT(p);
|
||||
|
||||
va_list args;
|
||||
va_start(args, p);
|
||||
format_args(p, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
dynamic_string::dynamic_string(const char* p) :
|
||||
m_buf_size(0), m_len(0), m_pStr(NULL)
|
||||
{
|
||||
CRNLIB_ASSERT(p);
|
||||
set(p);
|
||||
}
|
||||
|
||||
dynamic_string::dynamic_string(const char* p, uint len) :
|
||||
m_buf_size(0), m_len(0), m_pStr(NULL)
|
||||
{
|
||||
CRNLIB_ASSERT(p);
|
||||
set_from_buf(p, len);
|
||||
}
|
||||
|
||||
dynamic_string::dynamic_string(const dynamic_string& other) :
|
||||
m_buf_size(0), m_len(0), m_pStr(NULL)
|
||||
{
|
||||
set(other);
|
||||
}
|
||||
|
||||
dynamic_string::dynamic_string(const wchar_t* pStr) :
|
||||
m_buf_size(0), m_len(0), m_pStr(NULL)
|
||||
{
|
||||
set(pStr);
|
||||
}
|
||||
|
||||
dynamic_string& dynamic_string::set(const wchar_t *pStr)
|
||||
{
|
||||
uint len = static_cast<uint>(wcslen(pStr));
|
||||
if (!len)
|
||||
{
|
||||
clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const uint num_needed = WideCharToMultiByte(CP_ACP, 0, pStr, len, NULL, 0, NULL, NULL);
|
||||
if (num_needed <= 0)
|
||||
{
|
||||
clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
if (!ensure_buf(num_needed, false))
|
||||
{
|
||||
clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const uint num_written = WideCharToMultiByte(CP_ACP, 0, pStr, len, get_ptr_raw(), num_needed, NULL, NULL);
|
||||
CRNLIB_ASSERT(num_written == num_needed);
|
||||
|
||||
get_ptr_raw()[num_written] = 0;
|
||||
m_len = static_cast<uint16>(num_written);
|
||||
|
||||
check();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
dynamic_wstring& dynamic_string::as_utf16(dynamic_wstring &buf)
|
||||
{
|
||||
buf.set(get_ptr());
|
||||
return buf;
|
||||
}
|
||||
|
||||
void dynamic_string::clear()
|
||||
{
|
||||
check();
|
||||
|
||||
if (m_pStr)
|
||||
{
|
||||
crnlib_delete_array(m_pStr);
|
||||
m_pStr = NULL;
|
||||
|
||||
m_len = 0;
|
||||
m_buf_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void dynamic_string::empty()
|
||||
{
|
||||
truncate(0);
|
||||
}
|
||||
|
||||
void dynamic_string::optimize()
|
||||
{
|
||||
if (!m_len)
|
||||
clear();
|
||||
else
|
||||
{
|
||||
uint min_buf_size = math::next_pow2((uint)m_len + 1);
|
||||
if (m_buf_size > min_buf_size)
|
||||
{
|
||||
char* p = crnlib_new_array<char>(min_buf_size);
|
||||
memcpy(p, m_pStr, m_len + 1);
|
||||
|
||||
crnlib_delete_array(m_pStr);
|
||||
m_pStr = p;
|
||||
|
||||
m_buf_size = static_cast<uint16>(min_buf_size);
|
||||
|
||||
check();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int dynamic_string::compare(const char* p, bool case_sensitive) const
|
||||
{
|
||||
CRNLIB_ASSERT(p);
|
||||
|
||||
const int result = (case_sensitive ? strcmp : _stricmp)(get_ptr_priv(), p);
|
||||
|
||||
if (result < 0)
|
||||
return -1;
|
||||
else if (result > 0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dynamic_string::compare(const dynamic_string& rhs, bool case_sensitive) const
|
||||
{
|
||||
return compare(rhs.get_ptr_priv(), case_sensitive);
|
||||
}
|
||||
|
||||
dynamic_string& dynamic_string::set(const char* p, uint max_len)
|
||||
{
|
||||
CRNLIB_ASSERT(p);
|
||||
|
||||
const uint len = math::minimum<uint>(max_len, static_cast<uint>(strlen(p)));
|
||||
CRNLIB_ASSERT(len < UINT16_MAX);
|
||||
|
||||
if ((!len) || (len >= UINT16_MAX))
|
||||
clear();
|
||||
else if ((m_pStr) && (p >= m_pStr) && (p < (m_pStr + m_buf_size)))
|
||||
{
|
||||
if (m_pStr != p)
|
||||
memmove(m_pStr, p, len);
|
||||
m_pStr[len] = '\0';
|
||||
m_len = static_cast<uint16>(len);
|
||||
}
|
||||
else if (ensure_buf(len, false))
|
||||
{
|
||||
m_len = static_cast<uint16>(len);
|
||||
memcpy(m_pStr, p, m_len + 1);
|
||||
}
|
||||
|
||||
check();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
dynamic_string& dynamic_string::set(const dynamic_string& other, uint max_len)
|
||||
{
|
||||
if (this == &other)
|
||||
{
|
||||
if (max_len < m_len)
|
||||
{
|
||||
m_pStr[max_len] = '\0';
|
||||
m_len = static_cast<uint16>(max_len);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const uint len = math::minimum<uint>(max_len, other.m_len);
|
||||
|
||||
if (!len)
|
||||
clear();
|
||||
else if (ensure_buf(len, false))
|
||||
{
|
||||
m_len = static_cast<uint16>(len);
|
||||
memcpy(m_pStr, other.get_ptr_priv(), m_len);
|
||||
m_pStr[len] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
check();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool dynamic_string::set_len(uint new_len, char fill_char)
|
||||
{
|
||||
if ((new_len >= UINT16_MAX) || (!fill_char))
|
||||
return false;
|
||||
|
||||
uint cur_len = m_len;
|
||||
|
||||
if (ensure_buf(new_len, true))
|
||||
{
|
||||
if (new_len > cur_len)
|
||||
memset(m_pStr + cur_len, fill_char, new_len - cur_len);
|
||||
|
||||
m_pStr[new_len] = 0;
|
||||
|
||||
m_len = static_cast<uint16>(new_len);
|
||||
|
||||
check();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
dynamic_string& dynamic_string::set_from_buf(const void* pBuf, uint buf_size)
|
||||
{
|
||||
CRNLIB_ASSERT(pBuf);
|
||||
|
||||
if (buf_size >= UINT16_MAX)
|
||||
{
|
||||
clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
if ((buf_size) && (memchr(pBuf, 0, buf_size) != NULL))
|
||||
{
|
||||
CRNLIB_ASSERT(0);
|
||||
clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
if (ensure_buf(buf_size, false))
|
||||
{
|
||||
if (buf_size)
|
||||
memcpy(m_pStr, pBuf, buf_size);
|
||||
|
||||
m_pStr[buf_size] = 0;
|
||||
|
||||
m_len = static_cast<uint16>(buf_size);
|
||||
|
||||
check();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
dynamic_string& dynamic_string::set_char(uint index, char c)
|
||||
{
|
||||
CRNLIB_ASSERT(index <= m_len);
|
||||
|
||||
if (!c)
|
||||
truncate(index);
|
||||
else if (index < m_len)
|
||||
{
|
||||
m_pStr[index] = c;
|
||||
|
||||
check();
|
||||
}
|
||||
else if (index == m_len)
|
||||
append_char(c);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
dynamic_string& dynamic_string::append_char(char c)
|
||||
{
|
||||
if (ensure_buf(m_len + 1))
|
||||
{
|
||||
m_pStr[m_len] = c;
|
||||
m_pStr[m_len + 1] = '\0';
|
||||
m_len++;
|
||||
check();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
dynamic_string& dynamic_string::truncate(uint new_len)
|
||||
{
|
||||
if (new_len < m_len)
|
||||
{
|
||||
m_pStr[new_len] = '\0';
|
||||
m_len = static_cast<uint16>(new_len);
|
||||
check();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
dynamic_string& dynamic_string::tolower()
|
||||
{
|
||||
if (m_len)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
_strlwr_s(get_ptr_priv(), m_buf_size);
|
||||
#else
|
||||
strlwr(get_ptr_priv());
|
||||
#endif
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
dynamic_string& dynamic_string::toupper()
|
||||
{
|
||||
if (m_len)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
_strupr_s(get_ptr_priv(), m_buf_size);
|
||||
#else
|
||||
strupr(get_ptr_priv());
|
||||
#endif
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
dynamic_string& dynamic_string::append(const char* p)
|
||||
{
|
||||
CRNLIB_ASSERT(p);
|
||||
|
||||
uint len = static_cast<uint>(strlen(p));
|
||||
uint new_total_len = m_len + len;
|
||||
if ((new_total_len) && ensure_buf(new_total_len))
|
||||
{
|
||||
memcpy(m_pStr + m_len, p, len + 1);
|
||||
m_len = static_cast<uint16>(m_len + len);
|
||||
check();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
dynamic_string& dynamic_string::append(const dynamic_string& other)
|
||||
{
|
||||
uint len = other.m_len;
|
||||
uint new_total_len = m_len + len;
|
||||
if ((new_total_len) && ensure_buf(new_total_len))
|
||||
{
|
||||
memcpy(m_pStr + m_len, other.get_ptr_priv(), len + 1);
|
||||
m_len = static_cast<uint16>(m_len + len);
|
||||
check();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
dynamic_string operator+ (const char* p, const dynamic_string& a)
|
||||
{
|
||||
return dynamic_string(p).append(a);
|
||||
}
|
||||
|
||||
dynamic_string operator+ (const dynamic_string& a, const char* p)
|
||||
{
|
||||
return dynamic_string(a).append(p);
|
||||
}
|
||||
|
||||
dynamic_string operator+ (const dynamic_string& a, const dynamic_string& b)
|
||||
{
|
||||
return dynamic_string(a).append(b);
|
||||
}
|
||||
|
||||
dynamic_string& dynamic_string::format_args(const char* p, va_list args)
|
||||
{
|
||||
CRNLIB_ASSERT(p);
|
||||
|
||||
const uint cBufSize = 4096;
|
||||
char buf[cBufSize];
|
||||
|
||||
#ifdef _MSC_VER
|
||||
int l = vsnprintf_s(buf, cBufSize, _TRUNCATE, p, args);
|
||||
#else
|
||||
int l = vsnprintf(buf, cBufSize, p, args);
|
||||
#endif
|
||||
if (l <= 0)
|
||||
clear();
|
||||
else if (ensure_buf(l, false))
|
||||
{
|
||||
memcpy(m_pStr, buf, l + 1);
|
||||
|
||||
m_len = static_cast<uint16>(l);
|
||||
|
||||
check();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
dynamic_string& dynamic_string::format(const char* p, ...)
|
||||
{
|
||||
CRNLIB_ASSERT(p);
|
||||
|
||||
va_list args;
|
||||
va_start(args, p);
|
||||
format_args(p, args);
|
||||
va_end(args);
|
||||
return *this;
|
||||
}
|
||||
|
||||
dynamic_string& dynamic_string::crop(uint start, uint len)
|
||||
{
|
||||
if (start >= m_len)
|
||||
{
|
||||
clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
len = math::minimum<uint>(len, m_len - start);
|
||||
|
||||
if (start)
|
||||
memmove(get_ptr_priv(), get_ptr_priv() + start, len);
|
||||
|
||||
m_pStr[len] = '\0';
|
||||
|
||||
m_len = static_cast<uint16>(len);
|
||||
|
||||
check();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
dynamic_string& dynamic_string::substring(uint start, uint end)
|
||||
{
|
||||
CRNLIB_ASSERT(start <= end);
|
||||
if (start > end)
|
||||
return *this;
|
||||
return crop(start, end - start);
|
||||
}
|
||||
|
||||
dynamic_string& dynamic_string::left(uint len)
|
||||
{
|
||||
return substring(0, len);
|
||||
}
|
||||
|
||||
dynamic_string& dynamic_string::mid(uint start, uint len)
|
||||
{
|
||||
return crop(start, len);
|
||||
}
|
||||
|
||||
dynamic_string& dynamic_string::right(uint start)
|
||||
{
|
||||
return substring(start, get_len());
|
||||
}
|
||||
|
||||
dynamic_string& dynamic_string::tail(uint num)
|
||||
{
|
||||
return substring(math::maximum<int>(static_cast<int>(get_len()) - static_cast<int>(num), 0), get_len());
|
||||
}
|
||||
|
||||
dynamic_string& dynamic_string::unquote()
|
||||
{
|
||||
if (m_len >= 2)
|
||||
{
|
||||
if ( ((*this)[0] == '\"') && ((*this)[m_len - 1] == '\"') )
|
||||
{
|
||||
return mid(1, m_len - 2);
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
int dynamic_string::find_left(const char* p, bool case_sensitive) const
|
||||
{
|
||||
CRNLIB_ASSERT(p);
|
||||
|
||||
const int p_len = (int)strlen(p);
|
||||
|
||||
for (int i = 0; i <= (m_len - p_len); i++)
|
||||
if ((case_sensitive ? strncmp : _strnicmp)(p, &m_pStr[i], p_len) == 0)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool dynamic_string::contains(const char* p, bool case_sensitive) const
|
||||
{
|
||||
return find_left(p, case_sensitive) >= 0;
|
||||
}
|
||||
|
||||
uint dynamic_string::count_char(char c) const
|
||||
{
|
||||
uint count = 0;
|
||||
for (uint i = 0; i < m_len; i++)
|
||||
if (m_pStr[i] == c)
|
||||
count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
int dynamic_string::find_left(char c) const
|
||||
{
|
||||
for (uint i = 0; i < m_len; i++)
|
||||
if (m_pStr[i] == c)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int dynamic_string::find_right(char c) const
|
||||
{
|
||||
for (int i = (int)m_len - 1; i >= 0; i--)
|
||||
if (m_pStr[i] == c)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int dynamic_string::find_right(const char* p, bool case_sensitive) const
|
||||
{
|
||||
CRNLIB_ASSERT(p);
|
||||
const int p_len = (int)strlen(p);
|
||||
|
||||
for (int i = m_len - p_len; i >= 0; i--)
|
||||
if ((case_sensitive ? strncmp : _strnicmp)(p, &m_pStr[i], p_len) == 0)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
dynamic_string& dynamic_string::trim()
|
||||
{
|
||||
int s, e;
|
||||
for (s = 0; s < (int)m_len; s++)
|
||||
if (!isspace(m_pStr[s]))
|
||||
break;
|
||||
|
||||
for (e = m_len - 1; e > s; e--)
|
||||
if (!isspace(m_pStr[e]))
|
||||
break;
|
||||
|
||||
return crop(s, e - s + 1);
|
||||
}
|
||||
|
||||
dynamic_string& dynamic_string::trim_crlf()
|
||||
{
|
||||
int s = 0, e;
|
||||
|
||||
for (e = m_len - 1; e > s; e--)
|
||||
if ((m_pStr[e] != 13) && (m_pStr[e] != 10))
|
||||
break;
|
||||
|
||||
return crop(s, e - s + 1);
|
||||
}
|
||||
|
||||
dynamic_string& dynamic_string::remap(int from_char, int to_char)
|
||||
{
|
||||
for (uint i = 0; i < m_len; i++)
|
||||
if (m_pStr[i] == from_char)
|
||||
m_pStr[i] = (char)to_char;
|
||||
return *this;
|
||||
}
|
||||
|
||||
#ifdef CRNLIB_BUILD_DEBUG
|
||||
void dynamic_string::check() const
|
||||
{
|
||||
if (!m_pStr)
|
||||
{
|
||||
CRNLIB_ASSERT(!m_buf_size && !m_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
CRNLIB_ASSERT(m_buf_size);
|
||||
CRNLIB_ASSERT((m_buf_size == UINT16_MAX) || math::is_power_of_2((uint32)m_buf_size));
|
||||
CRNLIB_ASSERT(m_len < m_buf_size);
|
||||
CRNLIB_ASSERT(strlen(m_pStr) == m_len);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool dynamic_string::ensure_buf(uint len, bool preserve_contents)
|
||||
{
|
||||
uint buf_size_needed = len + 1;
|
||||
|
||||
CRNLIB_ASSERT(buf_size_needed <= UINT16_MAX);
|
||||
|
||||
if (buf_size_needed <= UINT16_MAX)
|
||||
{
|
||||
if (buf_size_needed > m_buf_size)
|
||||
expand_buf(buf_size_needed, preserve_contents);
|
||||
}
|
||||
|
||||
return m_buf_size >= buf_size_needed;
|
||||
}
|
||||
|
||||
bool dynamic_string::expand_buf(uint new_buf_size, bool preserve_contents)
|
||||
{
|
||||
new_buf_size = math::minimum<uint>(UINT16_MAX, math::next_pow2(math::maximum<uint>(m_buf_size, new_buf_size)));
|
||||
|
||||
if (new_buf_size != m_buf_size)
|
||||
{
|
||||
char* p = crnlib_new_array<char>(new_buf_size);
|
||||
|
||||
if (preserve_contents)
|
||||
memcpy(p, get_ptr_priv(), m_len + 1);
|
||||
|
||||
crnlib_delete_array(m_pStr);
|
||||
m_pStr = p;
|
||||
|
||||
m_buf_size = static_cast<uint16>(new_buf_size);
|
||||
|
||||
if (preserve_contents)
|
||||
check();
|
||||
}
|
||||
|
||||
return m_buf_size >= new_buf_size;
|
||||
}
|
||||
|
||||
void dynamic_string::swap(dynamic_string& other)
|
||||
{
|
||||
utils::swap(other.m_buf_size, m_buf_size);
|
||||
utils::swap(other.m_len, m_len);
|
||||
utils::swap(other.m_pStr, m_pStr);
|
||||
}
|
||||
|
||||
int dynamic_string::serialize(void* pBuf, uint buf_size, bool little_endian) const
|
||||
{
|
||||
uint buf_left = buf_size;
|
||||
|
||||
if (m_len > UINT16_MAX)
|
||||
return -1;
|
||||
|
||||
if (!utils::write_val((uint16)m_len, pBuf, buf_left, little_endian))
|
||||
return -1;
|
||||
|
||||
if (buf_left < m_len)
|
||||
return -1;
|
||||
|
||||
memcpy(pBuf, get_ptr(), m_len);
|
||||
|
||||
buf_left -= m_len;
|
||||
|
||||
return buf_size - buf_left;
|
||||
}
|
||||
|
||||
int dynamic_string::deserialize(const void* pBuf, uint buf_size, bool little_endian)
|
||||
{
|
||||
uint buf_left = buf_size;
|
||||
|
||||
if (buf_left < sizeof(uint16)) return -1;
|
||||
|
||||
uint16 l;
|
||||
if (!utils::read_obj(l, pBuf, buf_left, little_endian))
|
||||
return -1;
|
||||
|
||||
if (buf_left < l)
|
||||
return -1;
|
||||
|
||||
set_from_buf(pBuf, l);
|
||||
|
||||
buf_left -= l;
|
||||
|
||||
return buf_size - buf_left;
|
||||
}
|
||||
|
||||
void dynamic_string::translate_lf_to_crlf()
|
||||
{
|
||||
if (find_left(0x0A) < 0)
|
||||
return;
|
||||
|
||||
dynamic_string tmp;
|
||||
tmp.ensure_buf(m_len + 2);
|
||||
|
||||
// normal sequence is 0x0D 0x0A (CR LF, \r\n)
|
||||
|
||||
int prev_char = -1;
|
||||
for (uint i = 0; i < get_len(); i++)
|
||||
{
|
||||
const int cur_char = (*this)[i];
|
||||
|
||||
if ((cur_char == 0x0A) && (prev_char != 0x0D))
|
||||
tmp.append_char(0x0D);
|
||||
|
||||
tmp.append_char(cur_char);
|
||||
|
||||
prev_char = cur_char;
|
||||
}
|
||||
|
||||
swap(tmp);
|
||||
}
|
||||
|
||||
dynamic_string& dynamic_string::operator= (const dynamic_wstring& rhs)
|
||||
{
|
||||
return set(rhs.get_ptr());
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,163 @@
|
||||
// File: crn_dynamic_string.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
class dynamic_wstring;
|
||||
|
||||
class dynamic_string
|
||||
{
|
||||
friend class dynamic_wstring;
|
||||
|
||||
public:
|
||||
inline dynamic_string() : m_buf_size(0), m_len(0), m_pStr(NULL) { }
|
||||
dynamic_string(eVarArg dummy, const char* p, ...);
|
||||
dynamic_string(const char* p);
|
||||
dynamic_string(const char* p, uint len);
|
||||
dynamic_string(const dynamic_string& other);
|
||||
|
||||
inline ~dynamic_string() { if (m_pStr) crnlib_delete_array(m_pStr); }
|
||||
|
||||
explicit dynamic_string(const wchar_t* pStr);
|
||||
dynamic_string& set(const wchar_t *pStr);
|
||||
dynamic_wstring& as_utf16(dynamic_wstring &buf);
|
||||
|
||||
// Truncates the string to 0 chars and frees the buffer.
|
||||
void clear();
|
||||
void optimize();
|
||||
|
||||
// Truncates the string to 0 chars, but does not free the buffer.
|
||||
void empty();
|
||||
|
||||
inline uint get_len() const { return m_len; }
|
||||
inline bool is_empty() const { return !m_len; }
|
||||
|
||||
inline const char* get_ptr() const { return m_pStr ? m_pStr : ""; }
|
||||
|
||||
inline const char* get_ptr_raw() const { return m_pStr; }
|
||||
inline char* get_ptr_raw() { return m_pStr; }
|
||||
|
||||
inline char operator[] (uint i) const { CRNLIB_ASSERT(i <= m_len); return get_ptr()[i]; }
|
||||
|
||||
inline operator size_t() const { return fast_hash(get_ptr(), m_len) ^ fast_hash(&m_len, sizeof(m_len)); }
|
||||
|
||||
int compare(const char* p, bool case_sensitive = false) const;
|
||||
int compare(const dynamic_string& rhs, bool case_sensitive = false) const;
|
||||
|
||||
inline bool operator== (const dynamic_string& rhs) const { return compare(rhs) == 0; }
|
||||
inline bool operator== (const char* p) const { return compare(p) == 0; }
|
||||
|
||||
inline bool operator!= (const dynamic_string& rhs) const { return compare(rhs) != 0; }
|
||||
inline bool operator!= (const char* p) const { return compare(p) != 0; }
|
||||
|
||||
inline bool operator< (const dynamic_string& rhs) const { return compare(rhs) < 0; }
|
||||
inline bool operator< (const char* p) const { return compare(p) < 0; }
|
||||
|
||||
inline bool operator> (const dynamic_string& rhs) const { return compare(rhs) > 0; }
|
||||
inline bool operator> (const char* p) const { return compare(p) > 0; }
|
||||
|
||||
inline bool operator<= (const dynamic_string& rhs) const { return compare(rhs) <= 0; }
|
||||
inline bool operator<= (const char* p) const { return compare(p) <= 0; }
|
||||
|
||||
inline bool operator>= (const dynamic_string& rhs) const { return compare(rhs) >= 0; }
|
||||
inline bool operator>= (const char* p) const { return compare(p) >= 0; }
|
||||
|
||||
friend inline bool operator== (const char* p, const dynamic_string& rhs) { return rhs.compare(p) == 0; }
|
||||
|
||||
dynamic_string& set(const char* p, uint max_len = UINT_MAX);
|
||||
dynamic_string& set(const dynamic_string& other, uint max_len = UINT_MAX);
|
||||
|
||||
bool set_len(uint new_len, char fill_char = ' ');
|
||||
|
||||
// Set from non-zero terminated buffer.
|
||||
dynamic_string& set_from_buf(const void* pBuf, uint buf_size);
|
||||
|
||||
dynamic_string& operator= (const dynamic_string& rhs) { return set(rhs); }
|
||||
dynamic_string& operator= (const dynamic_wstring& rhs);
|
||||
dynamic_string& operator= (const char* p) { return set(p); }
|
||||
|
||||
dynamic_string& set_char(uint index, char c);
|
||||
dynamic_string& append_char(char c);
|
||||
dynamic_string& append_char(int c) { CRNLIB_ASSERT((c >= 0) && (c <= 255)); return append_char(static_cast<char>(c)); }
|
||||
dynamic_string& truncate(uint new_len);
|
||||
dynamic_string& tolower();
|
||||
dynamic_string& toupper();
|
||||
|
||||
dynamic_string& append(const char* p);
|
||||
dynamic_string& append(const dynamic_string& other);
|
||||
dynamic_string& operator += (const char* p) { return append(p); }
|
||||
dynamic_string& operator += (const dynamic_string& other) { return append(other); }
|
||||
|
||||
friend dynamic_string operator+ (const char* p, const dynamic_string& a);
|
||||
friend dynamic_string operator+ (const dynamic_string& a, const char* p);
|
||||
friend dynamic_string operator+ (const dynamic_string& a, const dynamic_string& b);
|
||||
|
||||
dynamic_string& format_args(const char* p, va_list args);
|
||||
dynamic_string& format(const char* p, ...);
|
||||
|
||||
dynamic_string& crop(uint start, uint len);
|
||||
dynamic_string& substring(uint start, uint end);
|
||||
dynamic_string& left(uint len);
|
||||
dynamic_string& mid(uint start, uint len);
|
||||
dynamic_string& right(uint start);
|
||||
dynamic_string& tail(uint num);
|
||||
|
||||
dynamic_string& unquote();
|
||||
|
||||
uint count_char(char c) const;
|
||||
|
||||
int find_left(const char* p, bool case_sensitive = false) const;
|
||||
int find_left(char c) const;
|
||||
|
||||
int find_right(char c) const;
|
||||
int find_right(const char* p, bool case_sensitive = false) const;
|
||||
|
||||
bool contains(const char* p, bool case_sensitive = false) const;
|
||||
|
||||
dynamic_string& trim();
|
||||
dynamic_string& trim_crlf();
|
||||
|
||||
dynamic_string& remap(int from_char, int to_char);
|
||||
|
||||
void swap(dynamic_string& other);
|
||||
|
||||
// Returns -1 on failure, or the number of bytes written.
|
||||
int serialize(void* pBuf, uint buf_size, bool little_endian) const;
|
||||
|
||||
// Returns -1 on failure, or the number of bytes read.
|
||||
int deserialize(const void* pBuf, uint buf_size, bool little_endian);
|
||||
|
||||
void translate_lf_to_crlf();
|
||||
|
||||
private:
|
||||
uint16 m_buf_size;
|
||||
uint16 m_len;
|
||||
char* m_pStr;
|
||||
|
||||
#ifdef CRNLIB_BUILD_DEBUG
|
||||
void check() const;
|
||||
#else
|
||||
inline void check() const { }
|
||||
#endif
|
||||
|
||||
bool expand_buf(uint new_buf_size, bool preserve_contents);
|
||||
|
||||
const char* get_ptr_priv() const { return m_pStr ? m_pStr : ""; }
|
||||
char* get_ptr_priv() { return (char*)(m_pStr ? m_pStr : ""); }
|
||||
|
||||
bool ensure_buf(uint len, bool preserve_contents = true);
|
||||
};
|
||||
|
||||
typedef crnlib::vector<dynamic_string> dynamic_string_array;
|
||||
|
||||
extern dynamic_string g_empty_dynamic_string;
|
||||
|
||||
CRNLIB_DEFINE_BITWISE_MOVABLE(dynamic_string);
|
||||
|
||||
inline void swap (dynamic_string& a, dynamic_string& b)
|
||||
{
|
||||
a.swap(b);
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,715 @@
|
||||
// File: crn_dynamic_wstring.cpp
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#include "crn_core.h"
|
||||
#include "crn_dynamic_wstring.h"
|
||||
#include "crn_winhdr.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
dynamic_wstring g_empty_dynamic_wstring;
|
||||
|
||||
dynamic_wstring::dynamic_wstring(eVarArg dummy, const wchar_t* p, ...) :
|
||||
m_buf_size(0), m_len(0), m_pStr(NULL)
|
||||
{
|
||||
dummy;
|
||||
|
||||
CRNLIB_ASSERT(p);
|
||||
|
||||
va_list args;
|
||||
va_start(args, p);
|
||||
format_args(p, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
dynamic_wstring::dynamic_wstring(const wchar_t* p) :
|
||||
m_buf_size(0), m_len(0), m_pStr(NULL)
|
||||
{
|
||||
CRNLIB_ASSERT(p);
|
||||
set(p);
|
||||
}
|
||||
|
||||
dynamic_wstring::dynamic_wstring(const wchar_t* p, uint len) :
|
||||
m_buf_size(0), m_len(0), m_pStr(NULL)
|
||||
{
|
||||
CRNLIB_ASSERT(p);
|
||||
set_from_buf(p, len);
|
||||
}
|
||||
|
||||
dynamic_wstring::dynamic_wstring(const dynamic_wstring& other) :
|
||||
m_buf_size(0), m_len(0), m_pStr(NULL)
|
||||
{
|
||||
set(other);
|
||||
}
|
||||
|
||||
void dynamic_wstring::clear()
|
||||
{
|
||||
check();
|
||||
|
||||
if (m_pStr)
|
||||
{
|
||||
crnlib_delete_array(m_pStr);
|
||||
m_pStr = NULL;
|
||||
|
||||
m_len = 0;
|
||||
m_buf_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void dynamic_wstring::empty()
|
||||
{
|
||||
truncate(0);
|
||||
}
|
||||
|
||||
void dynamic_wstring::optimize()
|
||||
{
|
||||
if (!m_len)
|
||||
clear();
|
||||
else
|
||||
{
|
||||
uint min_buf_size = math::next_pow2((uint)m_len + 1);
|
||||
if (m_buf_size > min_buf_size)
|
||||
{
|
||||
wchar_t* p = crnlib_new_array<wchar_t>(min_buf_size);
|
||||
memcpy(p, m_pStr, (m_len + 1) * sizeof(wchar_t));
|
||||
|
||||
crnlib_delete_array(m_pStr);
|
||||
m_pStr = p;
|
||||
|
||||
m_buf_size = static_cast<uint16>(min_buf_size);
|
||||
|
||||
check();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int dynamic_wstring::compare(const wchar_t* p, bool case_sensitive) const
|
||||
{
|
||||
CRNLIB_ASSERT(p);
|
||||
|
||||
const int result = (case_sensitive ? wcscmp : _wcsicmp)(get_ptr_priv(), p);
|
||||
|
||||
if (result < 0)
|
||||
return -1;
|
||||
else if (result > 0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dynamic_wstring::compare(const dynamic_wstring& rhs, bool case_sensitive) const
|
||||
{
|
||||
return compare(rhs.get_ptr_priv(), case_sensitive);
|
||||
}
|
||||
|
||||
dynamic_wstring& dynamic_wstring::set(const wchar_t* p, uint max_len)
|
||||
{
|
||||
CRNLIB_ASSERT(p);
|
||||
|
||||
const uint len = math::minimum<uint>(max_len, static_cast<uint>(wcslen(p)));
|
||||
CRNLIB_ASSERT(len < UINT16_MAX);
|
||||
|
||||
if ((!len) || (len >= UINT16_MAX))
|
||||
clear();
|
||||
else if ((m_pStr) && (p >= m_pStr) && (p < (m_pStr + m_buf_size)))
|
||||
{
|
||||
if (m_pStr != p)
|
||||
memmove(m_pStr, p, len * sizeof(wchar_t));
|
||||
m_pStr[len] = L'\0';
|
||||
m_len = static_cast<uint16>(len);
|
||||
}
|
||||
else if (ensure_buf(len, false))
|
||||
{
|
||||
m_len = static_cast<uint16>(len);
|
||||
memcpy(m_pStr, p, (m_len + 1) * sizeof(wchar_t));
|
||||
}
|
||||
|
||||
check();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
dynamic_wstring& dynamic_wstring::set(const dynamic_wstring& other, uint max_len)
|
||||
{
|
||||
if (this == &other)
|
||||
{
|
||||
if (max_len < m_len)
|
||||
{
|
||||
m_pStr[max_len] = L'\0';
|
||||
m_len = static_cast<uint16>(max_len);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const uint len = math::minimum<uint>(max_len, other.m_len);
|
||||
|
||||
if (!len)
|
||||
clear();
|
||||
else if (ensure_buf(len, false))
|
||||
{
|
||||
m_len = static_cast<uint16>(len);
|
||||
memcpy(m_pStr, other.get_ptr_priv(), m_len * sizeof(wchar_t));
|
||||
m_pStr[len] = L'\0';
|
||||
}
|
||||
}
|
||||
|
||||
check();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool dynamic_wstring::set_len(uint new_len, wchar_t fill_char)
|
||||
{
|
||||
if ((new_len >= UINT16_MAX) || (!fill_char))
|
||||
return false;
|
||||
|
||||
uint cur_len = m_len;
|
||||
|
||||
if (ensure_buf(new_len, true))
|
||||
{
|
||||
if (new_len > cur_len)
|
||||
{
|
||||
for (uint i = 0; i < (new_len - cur_len); i++)
|
||||
m_pStr[cur_len + i] = fill_char;
|
||||
}
|
||||
|
||||
m_pStr[new_len] = L'\0';
|
||||
|
||||
m_len = static_cast<uint16>(new_len);
|
||||
|
||||
check();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
dynamic_wstring& dynamic_wstring::set_from_buf(const void* pBuf, uint buf_size, bool little_endian)
|
||||
{
|
||||
CRNLIB_ASSERT(pBuf);
|
||||
|
||||
if (buf_size >= UINT16_MAX)
|
||||
{
|
||||
clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
for (uint i = 0; i < buf_size; i++)
|
||||
{
|
||||
if (static_cast<const wchar_t*>(pBuf)[i] == L'\0')
|
||||
{
|
||||
CRNLIB_ASSERT(0);
|
||||
clear();
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
|
||||
if (ensure_buf(buf_size, false))
|
||||
{
|
||||
utils::copy_words(reinterpret_cast<uint16*>(m_pStr), reinterpret_cast<const uint16*>(pBuf), buf_size, c_crnlib_little_endian_platform != little_endian);
|
||||
|
||||
m_pStr[buf_size] = L'\0';
|
||||
|
||||
m_len = static_cast<uint16>(buf_size);
|
||||
|
||||
check();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
dynamic_wstring& dynamic_wstring::set_char(uint index, wchar_t c)
|
||||
{
|
||||
CRNLIB_ASSERT(index <= m_len);
|
||||
|
||||
if (!c)
|
||||
truncate(index);
|
||||
else if (index < m_len)
|
||||
{
|
||||
m_pStr[index] = c;
|
||||
|
||||
check();
|
||||
}
|
||||
else if (index == m_len)
|
||||
append_char(c);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
dynamic_wstring& dynamic_wstring::append_char(wchar_t c)
|
||||
{
|
||||
if (ensure_buf(m_len + 1))
|
||||
{
|
||||
m_pStr[m_len] = c;
|
||||
m_pStr[m_len + 1] = L'\0';
|
||||
m_len++;
|
||||
check();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
dynamic_wstring& dynamic_wstring::truncate(uint new_len)
|
||||
{
|
||||
if (new_len < m_len)
|
||||
{
|
||||
m_pStr[new_len] = L'\0';
|
||||
m_len = static_cast<uint16>(new_len);
|
||||
check();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
dynamic_wstring& dynamic_wstring::tolower()
|
||||
{
|
||||
if (m_len)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
_wcslwr_s(get_ptr_priv(), m_buf_size);
|
||||
#else
|
||||
_wcslwr(get_ptr_priv());
|
||||
#endif
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
dynamic_wstring& dynamic_wstring::toupper()
|
||||
{
|
||||
if (m_len)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
_wcsupr_s(get_ptr_priv(), m_buf_size);
|
||||
#else
|
||||
_wcsupr(get_ptr_priv());
|
||||
#endif
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
dynamic_wstring& dynamic_wstring::append(const wchar_t* p)
|
||||
{
|
||||
CRNLIB_ASSERT(p);
|
||||
|
||||
uint len = static_cast<uint>(wcslen(p));
|
||||
uint new_total_len = m_len + len;
|
||||
if ((new_total_len) && ensure_buf(new_total_len))
|
||||
{
|
||||
memcpy(m_pStr + m_len, p, (len + 1) * sizeof(wchar_t));
|
||||
m_len = static_cast<uint16>(m_len + len);
|
||||
check();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
dynamic_wstring& dynamic_wstring::append(const dynamic_wstring& other)
|
||||
{
|
||||
uint len = other.m_len;
|
||||
uint new_total_len = m_len + len;
|
||||
if ((new_total_len) && ensure_buf(new_total_len))
|
||||
{
|
||||
memcpy(m_pStr + m_len, other.get_ptr_priv(), (len + 1) * sizeof(wchar_t));
|
||||
m_len = static_cast<uint16>(m_len + len);
|
||||
check();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
dynamic_wstring operator+ (const wchar_t* p, const dynamic_wstring& a)
|
||||
{
|
||||
return dynamic_wstring(p).append(a);
|
||||
}
|
||||
|
||||
dynamic_wstring operator+ (const dynamic_wstring& a, const wchar_t* p)
|
||||
{
|
||||
return dynamic_wstring(a).append(p);
|
||||
}
|
||||
|
||||
dynamic_wstring operator+ (const dynamic_wstring& a, const dynamic_wstring& b)
|
||||
{
|
||||
return dynamic_wstring(a).append(b);
|
||||
}
|
||||
|
||||
dynamic_wstring& dynamic_wstring::format_args(const wchar_t* p, va_list args)
|
||||
{
|
||||
CRNLIB_ASSERT(p);
|
||||
|
||||
const uint cBufSize = 4096;
|
||||
wchar_t buf[cBufSize];
|
||||
|
||||
#ifdef _MSC_VER
|
||||
int l = _vsnwprintf_s(buf, cBufSize, _TRUNCATE, p, args);
|
||||
#else
|
||||
int l = _vsnwprintf(buf, cBufSize, p, args);
|
||||
#endif
|
||||
if (l <= 0)
|
||||
clear();
|
||||
else if (ensure_buf(l, false))
|
||||
{
|
||||
memcpy(m_pStr, buf, (l + 1) * sizeof(wchar_t));
|
||||
|
||||
m_len = static_cast<uint16>(l);
|
||||
|
||||
check();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
dynamic_wstring& dynamic_wstring::format(const wchar_t* p, ...)
|
||||
{
|
||||
CRNLIB_ASSERT(p);
|
||||
|
||||
va_list args;
|
||||
va_start(args, p);
|
||||
format_args(p, args);
|
||||
va_end(args);
|
||||
return *this;
|
||||
}
|
||||
|
||||
dynamic_wstring& dynamic_wstring::crop(uint start, uint len)
|
||||
{
|
||||
if (start >= m_len)
|
||||
{
|
||||
clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
len = math::minimum<uint>(len, m_len - start);
|
||||
|
||||
if (start)
|
||||
memmove(get_ptr_priv(), get_ptr_priv() + start, len * sizeof(wchar_t));
|
||||
|
||||
m_pStr[len] = L'\0';
|
||||
|
||||
m_len = static_cast<uint16>(len);
|
||||
|
||||
check();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
dynamic_wstring& dynamic_wstring::substring(uint start, uint end)
|
||||
{
|
||||
CRNLIB_ASSERT(start <= end);
|
||||
if (start > end)
|
||||
return *this;
|
||||
return crop(start, end - start);
|
||||
}
|
||||
|
||||
dynamic_wstring& dynamic_wstring::left(uint len)
|
||||
{
|
||||
return substring(0, len);
|
||||
}
|
||||
|
||||
dynamic_wstring& dynamic_wstring::mid(uint start, uint len)
|
||||
{
|
||||
return crop(start, len);
|
||||
}
|
||||
|
||||
dynamic_wstring& dynamic_wstring::right(uint start)
|
||||
{
|
||||
return substring(start, get_len());
|
||||
}
|
||||
|
||||
dynamic_wstring& dynamic_wstring::tail(uint num)
|
||||
{
|
||||
return substring(math::maximum<int>(static_cast<int>(get_len()) - static_cast<int>(num), 0), get_len());
|
||||
}
|
||||
|
||||
dynamic_wstring& dynamic_wstring::unquote()
|
||||
{
|
||||
if (m_len >= 2)
|
||||
{
|
||||
if ( ((*this)[0] == L'\"') && ((*this)[m_len - 1] == L'\"') )
|
||||
{
|
||||
return mid(1, m_len - 2);
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
int dynamic_wstring::find_left(const wchar_t* p, bool case_sensitive) const
|
||||
{
|
||||
CRNLIB_ASSERT(p);
|
||||
|
||||
const int p_len = (int)wcslen(p);
|
||||
|
||||
for (int i = 0; i <= (m_len - p_len); i++)
|
||||
if ((case_sensitive ? wcsncmp : _wcsnicmp)(p, &m_pStr[i], p_len) == 0)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool dynamic_wstring::contains(const wchar_t* p, bool case_sensitive) const
|
||||
{
|
||||
return find_left(p, case_sensitive) >= 0;
|
||||
}
|
||||
|
||||
uint dynamic_wstring::count_char(wchar_t c) const
|
||||
{
|
||||
uint count = 0;
|
||||
for (uint i = 0; i < m_len; i++)
|
||||
if (m_pStr[i] == c)
|
||||
count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
int dynamic_wstring::find_left(wchar_t c) const
|
||||
{
|
||||
for (uint i = 0; i < m_len; i++)
|
||||
if (m_pStr[i] == c)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int dynamic_wstring::find_right(wchar_t c) const
|
||||
{
|
||||
for (int i = (int)m_len - 1; i >= 0; i--)
|
||||
if (m_pStr[i] == c)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int dynamic_wstring::find_right(const wchar_t* p, bool case_sensitive) const
|
||||
{
|
||||
CRNLIB_ASSERT(p);
|
||||
const int p_len = (int)wcslen(p);
|
||||
|
||||
for (int i = m_len - p_len; i >= 0; i--)
|
||||
if ((case_sensitive ? wcsncmp : _wcsnicmp)(p, &m_pStr[i], p_len) == 0)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
dynamic_wstring& dynamic_wstring::trim()
|
||||
{
|
||||
int s, e;
|
||||
for (s = 0; s < (int)m_len; s++)
|
||||
if (!iswspace(m_pStr[s]))
|
||||
break;
|
||||
|
||||
for (e = m_len - 1; e > s; e--)
|
||||
if (!iswspace(m_pStr[e]))
|
||||
break;
|
||||
|
||||
return crop(s, e - s + 1);
|
||||
}
|
||||
|
||||
dynamic_wstring& dynamic_wstring::trim_crlf()
|
||||
{
|
||||
int s = 0, e;
|
||||
|
||||
for (e = m_len - 1; e > s; e--)
|
||||
if ((m_pStr[e] != 13) && (m_pStr[e] != 10))
|
||||
break;
|
||||
|
||||
return crop(s, e - s + 1);
|
||||
}
|
||||
|
||||
dynamic_wstring& dynamic_wstring::remap(int from_char, int to_char)
|
||||
{
|
||||
for (uint i = 0; i < m_len; i++)
|
||||
if (m_pStr[i] == from_char)
|
||||
m_pStr[i] = (wchar_t)to_char;
|
||||
return *this;
|
||||
}
|
||||
|
||||
#ifdef CRNLIB_BUILD_DEBUG
|
||||
void dynamic_wstring::check() const
|
||||
{
|
||||
if (!m_pStr)
|
||||
{
|
||||
CRNLIB_ASSERT(!m_buf_size && !m_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
CRNLIB_ASSERT(m_buf_size);
|
||||
CRNLIB_ASSERT((m_buf_size == UINT16_MAX) || math::is_power_of_2((uint32)m_buf_size));
|
||||
CRNLIB_ASSERT(m_len < m_buf_size);
|
||||
CRNLIB_ASSERT(wcslen(m_pStr) == m_len);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool dynamic_wstring::ensure_buf(uint len, bool preserve_contents)
|
||||
{
|
||||
uint buf_size_needed = len + 1;
|
||||
|
||||
CRNLIB_ASSERT(buf_size_needed <= UINT16_MAX);
|
||||
|
||||
if (buf_size_needed <= UINT16_MAX)
|
||||
{
|
||||
if (buf_size_needed > m_buf_size)
|
||||
expand_buf(buf_size_needed, preserve_contents);
|
||||
}
|
||||
|
||||
return m_buf_size >= buf_size_needed;
|
||||
}
|
||||
|
||||
bool dynamic_wstring::expand_buf(uint new_buf_size, bool preserve_contents)
|
||||
{
|
||||
new_buf_size = math::minimum<uint>(UINT16_MAX, math::next_pow2(math::maximum<uint>(m_buf_size, new_buf_size)));
|
||||
|
||||
if (new_buf_size != m_buf_size)
|
||||
{
|
||||
wchar_t* p = crnlib_new_array<wchar_t>(new_buf_size);
|
||||
|
||||
if (preserve_contents)
|
||||
memcpy(p, get_ptr_priv(), (m_len + 1) * sizeof(wchar_t));
|
||||
|
||||
crnlib_delete_array(m_pStr);
|
||||
m_pStr = p;
|
||||
|
||||
m_buf_size = static_cast<uint16>(new_buf_size);
|
||||
|
||||
if (preserve_contents)
|
||||
check();
|
||||
}
|
||||
|
||||
return m_buf_size >= new_buf_size;
|
||||
}
|
||||
|
||||
void dynamic_wstring::swap(dynamic_wstring& other)
|
||||
{
|
||||
utils::swap(other.m_buf_size, m_buf_size);
|
||||
utils::swap(other.m_len, m_len);
|
||||
utils::swap(other.m_pStr, m_pStr);
|
||||
}
|
||||
|
||||
int dynamic_wstring::serialize(void* pBuf, uint buf_size, bool little_endian) const
|
||||
{
|
||||
CRNLIB_ASSERT(pBuf);
|
||||
|
||||
uint buf_left = buf_size;
|
||||
|
||||
if (m_len > UINT16_MAX)
|
||||
return -1;
|
||||
|
||||
if (!utils::write_val((uint16)m_len, pBuf, buf_left, little_endian))
|
||||
return -1;
|
||||
|
||||
if (buf_left < (m_len * sizeof(wchar_t)))
|
||||
return -1;
|
||||
|
||||
utils::copy_words(reinterpret_cast<uint16*>(pBuf), reinterpret_cast<const uint16*>(get_ptr_priv()), m_len, little_endian != c_crnlib_little_endian_platform);
|
||||
|
||||
buf_left -= m_len * sizeof(wchar_t);
|
||||
|
||||
return buf_size - buf_left;
|
||||
}
|
||||
|
||||
int dynamic_wstring::deserialize(const void* pBuf, uint buf_size, bool little_endian)
|
||||
{
|
||||
CRNLIB_ASSERT(pBuf);
|
||||
|
||||
uint buf_left = buf_size;
|
||||
|
||||
if (buf_left < sizeof(uint16)) return -1;
|
||||
|
||||
uint16 l;
|
||||
if (!utils::read_obj(l, pBuf, buf_left, little_endian))
|
||||
return -1;
|
||||
|
||||
if (buf_left < (l * sizeof(wchar_t)))
|
||||
return -1;
|
||||
|
||||
set_from_buf(pBuf, l, little_endian);
|
||||
|
||||
buf_left -= l * sizeof(wchar_t);
|
||||
|
||||
return buf_size - buf_left;
|
||||
}
|
||||
|
||||
dynamic_wstring::dynamic_wstring(const char* p) :
|
||||
m_buf_size(0), m_len(0), m_pStr(NULL)
|
||||
{
|
||||
set(p);
|
||||
}
|
||||
|
||||
dynamic_wstring::dynamic_wstring(const dynamic_string& s) :
|
||||
m_buf_size(0), m_len(0), m_pStr(NULL)
|
||||
{
|
||||
set(s.get_ptr());
|
||||
}
|
||||
|
||||
dynamic_wstring& dynamic_wstring::set(const char* p)
|
||||
{
|
||||
CRNLIB_ASSERT(p);
|
||||
if (!p)
|
||||
{
|
||||
clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
uint l = static_cast<uint>(strlen(p));
|
||||
if (!l)
|
||||
{
|
||||
clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const uint num_needed = static_cast<uint>(MultiByteToWideChar(CP_ACP, 0, p, l, NULL, 0));
|
||||
if (!num_needed)
|
||||
{
|
||||
clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
if (!ensure_buf(num_needed, false))
|
||||
{
|
||||
clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const uint num_written = static_cast<uint>(MultiByteToWideChar(CP_ACP, 0, p, l, m_pStr, num_needed));
|
||||
CRNLIB_ASSERT(num_needed == num_written);
|
||||
|
||||
m_pStr[num_written] = L'\0';
|
||||
m_len = static_cast<uint16>(num_written);
|
||||
|
||||
check();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
dynamic_string& dynamic_wstring::as_ansi(dynamic_string& buf)
|
||||
{
|
||||
if (!m_len)
|
||||
{
|
||||
buf.clear();
|
||||
return buf;
|
||||
}
|
||||
|
||||
const uint num_needed = WideCharToMultiByte(CP_ACP, 0, m_pStr, m_len, NULL, 0, NULL, NULL);
|
||||
if (num_needed <= 0)
|
||||
{
|
||||
buf.clear();
|
||||
return buf;
|
||||
}
|
||||
|
||||
if (!buf.ensure_buf(num_needed, false))
|
||||
{
|
||||
buf.clear();
|
||||
return buf;
|
||||
}
|
||||
|
||||
const uint num_written = WideCharToMultiByte(CP_ACP, 0, m_pStr, m_len, buf.get_ptr_raw(), num_needed, NULL, NULL);
|
||||
CRNLIB_ASSERT(num_written == num_needed);
|
||||
|
||||
buf.get_ptr_raw()[num_written] = 0;
|
||||
buf.m_len = static_cast<uint16>(num_written);
|
||||
|
||||
buf.check();
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
dynamic_wstring& dynamic_wstring::operator= (const dynamic_string& rhs)
|
||||
{
|
||||
return set(rhs.get_ptr());
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,159 @@
|
||||
// File: crn_dynamic_wstring.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
// UCS-2 string class (plane 0 characters only)
|
||||
class dynamic_wstring
|
||||
{
|
||||
public:
|
||||
inline dynamic_wstring() : m_buf_size(0), m_len(0), m_pStr(NULL) { }
|
||||
dynamic_wstring(eVarArg dummy, const wchar_t* p, ...);
|
||||
dynamic_wstring(const wchar_t* p);
|
||||
dynamic_wstring(const wchar_t* p, uint len);
|
||||
dynamic_wstring(const dynamic_wstring& other);
|
||||
|
||||
// Conversion from UCS-2 to ANSI and vice versa
|
||||
explicit dynamic_wstring(const char* p);
|
||||
explicit dynamic_wstring(const dynamic_string& s);
|
||||
dynamic_wstring& set(const char* p);
|
||||
dynamic_string& as_ansi(dynamic_string& buf);
|
||||
|
||||
inline ~dynamic_wstring() { CRNLIB_ASSUME(sizeof(wchar_t) == sizeof(uint16)); if (m_pStr) crnlib_delete_array(m_pStr); }
|
||||
|
||||
// Truncates the string to 0 chars and frees the buffer.
|
||||
void clear();
|
||||
void optimize();
|
||||
|
||||
// Truncates the string to 0 chars, but does not free the buffer.
|
||||
void empty();
|
||||
|
||||
inline uint get_len() const { return m_len; }
|
||||
inline bool is_empty() const { return !m_len; }
|
||||
|
||||
inline const wchar_t* get_ptr() const { return m_pStr ? m_pStr : L""; }
|
||||
|
||||
inline const wchar_t* get_ptr_raw() const { return m_pStr; }
|
||||
inline wchar_t* get_ptr_raw() { return m_pStr; }
|
||||
|
||||
inline wchar_t operator[] (uint i) const { CRNLIB_ASSERT(i <= m_len); return get_ptr()[i]; }
|
||||
|
||||
inline operator size_t() const { return fast_hash(get_ptr(), m_len * sizeof(wchar_t)) ^ fast_hash(&m_len, sizeof(m_len)); }
|
||||
|
||||
int compare(const wchar_t* p, bool case_sensitive = false) const;
|
||||
int compare(const dynamic_wstring& rhs, bool case_sensitive = false) const;
|
||||
|
||||
inline bool operator== (const dynamic_wstring& rhs) const { return compare(rhs) == 0; }
|
||||
inline bool operator== (const wchar_t* p) const { return compare(p) == 0; }
|
||||
|
||||
inline bool operator!= (const dynamic_wstring& rhs) const { return compare(rhs) != 0; }
|
||||
inline bool operator!= (const wchar_t* p) const { return compare(p) != 0; }
|
||||
|
||||
inline bool operator< (const dynamic_wstring& rhs) const { return compare(rhs) < 0; }
|
||||
inline bool operator< (const wchar_t* p) const { return compare(p) < 0; }
|
||||
|
||||
inline bool operator> (const dynamic_wstring& rhs) const { return compare(rhs) > 0; }
|
||||
inline bool operator> (const wchar_t* p) const { return compare(p) > 0; }
|
||||
|
||||
inline bool operator<= (const dynamic_wstring& rhs) const { return compare(rhs) <= 0; }
|
||||
inline bool operator<= (const wchar_t* p) const { return compare(p) <= 0; }
|
||||
|
||||
inline bool operator>= (const dynamic_wstring& rhs) const { return compare(rhs) >= 0; }
|
||||
inline bool operator>= (const wchar_t* p) const { return compare(p) >= 0; }
|
||||
|
||||
friend inline bool operator== (const wchar_t* p, const dynamic_wstring& rhs) { return rhs.compare(p) == 0; }
|
||||
|
||||
dynamic_wstring& set(const wchar_t* p, uint max_len = UINT_MAX);
|
||||
dynamic_wstring& set(const dynamic_wstring& other, uint max_len = UINT_MAX);
|
||||
|
||||
bool set_len(uint new_len, wchar_t fill_char = ' ');
|
||||
|
||||
// Set from non-zero terminated buffer.
|
||||
// little_endian is the endianness of the buffer's data
|
||||
dynamic_wstring& set_from_buf(const void* pBuf, uint buf_size, bool little_endian = c_crnlib_little_endian_platform);
|
||||
|
||||
dynamic_wstring& operator= (const dynamic_wstring& rhs) { return set(rhs); }
|
||||
dynamic_wstring& operator= (const dynamic_string& rhs);
|
||||
dynamic_wstring& operator= (const wchar_t* p) { return set(p); }
|
||||
dynamic_wstring& operator= (const char* p) { return set(p); }
|
||||
|
||||
dynamic_wstring& set_char(uint index, wchar_t c);
|
||||
dynamic_wstring& append_char(wchar_t c);
|
||||
dynamic_wstring& append_char(int c) { CRNLIB_ASSERT((c >= 0) && (c <= 0xFFFF)); return append_char(static_cast<wchar_t>(c)); }
|
||||
dynamic_wstring& truncate(uint new_len);
|
||||
dynamic_wstring& tolower();
|
||||
dynamic_wstring& toupper();
|
||||
|
||||
dynamic_wstring& append(const wchar_t* p);
|
||||
dynamic_wstring& append(const dynamic_wstring& other);
|
||||
dynamic_wstring& operator += (const wchar_t* p) { return append(p); }
|
||||
dynamic_wstring& operator += (const dynamic_wstring& other) { return append(other); }
|
||||
|
||||
friend dynamic_wstring operator+ (const wchar_t* p, const dynamic_wstring& a);
|
||||
friend dynamic_wstring operator+ (const dynamic_wstring& a, const wchar_t* p);
|
||||
friend dynamic_wstring operator+ (const dynamic_wstring& a, const dynamic_wstring& b);
|
||||
|
||||
dynamic_wstring& format_args(const wchar_t* p, va_list args);
|
||||
dynamic_wstring& format(const wchar_t* p, ...);
|
||||
|
||||
dynamic_wstring& crop(uint start, uint len);
|
||||
dynamic_wstring& substring(uint start, uint end);
|
||||
dynamic_wstring& left(uint len);
|
||||
dynamic_wstring& mid(uint start, uint len);
|
||||
dynamic_wstring& right(uint start);
|
||||
dynamic_wstring& tail(uint num);
|
||||
|
||||
dynamic_wstring& unquote();
|
||||
|
||||
uint count_char(wchar_t c) const;
|
||||
|
||||
int find_left(const wchar_t* p, bool case_sensitive = false) const;
|
||||
int find_left(wchar_t c) const;
|
||||
|
||||
int find_right(wchar_t c) const;
|
||||
int find_right(const wchar_t* p, bool case_sensitive = false) const;
|
||||
|
||||
bool contains(const wchar_t* p, bool case_sensitive = false) const;
|
||||
|
||||
dynamic_wstring& trim();
|
||||
dynamic_wstring& trim_crlf();
|
||||
|
||||
dynamic_wstring& remap(int from_char, int to_char);
|
||||
|
||||
void swap(dynamic_wstring& other);
|
||||
|
||||
int serialize(void* pBuf, uint buf_size, bool little_endian) const;
|
||||
int deserialize(const void* pBuf, uint buf_size, bool little_endian);
|
||||
|
||||
private:
|
||||
// These values are in characters, not bytes!
|
||||
uint16 m_buf_size;
|
||||
uint16 m_len;
|
||||
wchar_t* m_pStr;
|
||||
|
||||
#ifdef CRNLIB_BUILD_DEBUG
|
||||
void check() const;
|
||||
#else
|
||||
void check() const { }
|
||||
#endif
|
||||
|
||||
bool ensure_buf(uint len, bool preserve_contents = true);
|
||||
bool expand_buf(uint new_buf_size, bool preserve_contents);
|
||||
|
||||
const wchar_t* get_ptr_priv() const { return m_pStr ? m_pStr : L""; }
|
||||
wchar_t* get_ptr_priv() { return (wchar_t*)(m_pStr ? m_pStr : L""); }
|
||||
};
|
||||
|
||||
typedef crnlib::vector<dynamic_wstring> dynamic_wstring_array;
|
||||
|
||||
extern dynamic_wstring g_empty_dynamic_wstring;
|
||||
|
||||
CRNLIB_DEFINE_BITWISE_MOVABLE(dynamic_wstring);
|
||||
|
||||
inline void swap (dynamic_wstring& a, dynamic_wstring& b)
|
||||
{
|
||||
a.swap(b);
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,27 @@
|
||||
// File: crn_event.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
class event
|
||||
{
|
||||
CRNLIB_NO_COPY_OR_ASSIGNMENT_OP(event);
|
||||
|
||||
public:
|
||||
event(bool manual_reset = false, bool initial_state = false, const char* pName = NULL);
|
||||
~event();
|
||||
|
||||
inline void *get_handle(void) const { return m_handle; }
|
||||
|
||||
void set(void);
|
||||
void reset(void);
|
||||
void pulse(void);
|
||||
bool wait(uint32 milliseconds = UINT32_MAX);
|
||||
|
||||
private:
|
||||
void *m_handle;
|
||||
};
|
||||
|
||||
} // namespace crnlib
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
// File: crn_hash.cpp
|
||||
// See Paul Hsieh's page at: http://www.azillionmonkeys.com/qed/hash.html
|
||||
// Also see http://www.concentric.net/~Ttwang/tech/inthash.htm,
|
||||
// http://burtleburtle.net/bob/hash/integer.html
|
||||
#include "crn_core.h"
|
||||
|
||||
#undef get16bits
|
||||
#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
|
||||
|| defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
|
||||
#define get16bits(d) (*((const uint16 *) (d)))
|
||||
#endif
|
||||
|
||||
#if !defined (get16bits)
|
||||
#define get16bits(d) ((((uint32)(((const uint8 *)(d))[1])) << 8)\
|
||||
+(uint32)(((const uint8 *)(d))[0]) )
|
||||
#endif
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
uint32 fast_hash (const void* p, int len)
|
||||
{
|
||||
const char * data = static_cast<const char *>(p);
|
||||
|
||||
uint32 hash = len, tmp;
|
||||
int rem;
|
||||
|
||||
if (len <= 0 || data == NULL) return 0;
|
||||
|
||||
rem = len & 3;
|
||||
len >>= 2;
|
||||
|
||||
/* Main loop */
|
||||
for (;len > 0; len--) {
|
||||
hash += get16bits (data);
|
||||
tmp = (get16bits (data+2) << 11) ^ hash;
|
||||
hash = (hash << 16) ^ tmp;
|
||||
data += 2*sizeof (uint16);
|
||||
hash += hash >> 11;
|
||||
}
|
||||
|
||||
/* Handle end cases */
|
||||
switch (rem) {
|
||||
case 3: hash += get16bits (data);
|
||||
hash ^= hash << 16;
|
||||
hash ^= data[sizeof (uint16)] << 18;
|
||||
hash += hash >> 11;
|
||||
break;
|
||||
case 2: hash += get16bits (data);
|
||||
hash ^= hash << 11;
|
||||
hash += hash >> 17;
|
||||
break;
|
||||
case 1: hash += *data;
|
||||
hash ^= hash << 10;
|
||||
hash += hash >> 1;
|
||||
}
|
||||
|
||||
/* Force "avalanching" of final 127 bits */
|
||||
hash ^= hash << 3;
|
||||
hash += hash >> 5;
|
||||
hash ^= hash << 4;
|
||||
hash += hash >> 17;
|
||||
hash ^= hash << 25;
|
||||
hash += hash >> 6;
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,34 @@
|
||||
// File: crn_hash.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
uint32 fast_hash (const void* p, int len);
|
||||
|
||||
// 4-byte integer hash, full avalanche
|
||||
inline uint32 bitmix32c(uint32 a)
|
||||
{
|
||||
a = (a+0x7ed55d16) + (a<<12);
|
||||
a = (a^0xc761c23c) ^ (a>>19);
|
||||
a = (a+0x165667b1) + (a<<5);
|
||||
a = (a+0xd3a2646c) ^ (a<<9);
|
||||
a = (a+0xfd7046c5) + (a<<3);
|
||||
a = (a^0xb55a4f09) ^ (a>>16);
|
||||
return a;
|
||||
}
|
||||
|
||||
// 4-byte integer hash, full avalanche, no constants
|
||||
inline uint32 bitmix32(uint32 a)
|
||||
{
|
||||
a -= (a<<6);
|
||||
a ^= (a>>17);
|
||||
a -= (a<<9);
|
||||
a ^= (a<<4);
|
||||
a -= (a<<3);
|
||||
a ^= (a<<10);
|
||||
a ^= (a>>15);
|
||||
return a;
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,155 @@
|
||||
// File: crn_hash_map.cpp
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#include "crn_core.h"
|
||||
#include "crn_hash_map.h"
|
||||
#include "crn_rand.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
#if 0
|
||||
class counted_obj
|
||||
{
|
||||
public:
|
||||
counted_obj(uint v = 0) :
|
||||
m_val(v)
|
||||
{
|
||||
m_count++;
|
||||
}
|
||||
|
||||
counted_obj(const counted_obj& obj) :
|
||||
m_val(obj.m_val)
|
||||
{
|
||||
m_count++;
|
||||
}
|
||||
|
||||
~counted_obj()
|
||||
{
|
||||
CRNLIB_ASSERT(m_count > 0);
|
||||
m_count--;
|
||||
}
|
||||
|
||||
static uint m_count;
|
||||
|
||||
uint m_val;
|
||||
|
||||
operator size_t() const { return m_val; }
|
||||
|
||||
bool operator== (const counted_obj& rhs) const { return m_val == rhs.m_val; }
|
||||
bool operator== (const uint rhs) const { return m_val == rhs; }
|
||||
|
||||
};
|
||||
|
||||
uint counted_obj::m_count;
|
||||
|
||||
void hash_map_test()
|
||||
{
|
||||
random r0, r1;
|
||||
|
||||
uint seed = 0;
|
||||
for ( ; ; )
|
||||
{
|
||||
seed++;
|
||||
|
||||
typedef crnlib::hash_map<counted_obj, counted_obj> my_hash_map;
|
||||
my_hash_map m;
|
||||
|
||||
const uint n = r0.irand(1, 100000);
|
||||
|
||||
printf("%u\n", n);
|
||||
|
||||
r1.seed(seed);
|
||||
|
||||
crnlib::vector<int> q;
|
||||
|
||||
uint count = 0;
|
||||
for (uint i = 0; i < n; i++)
|
||||
{
|
||||
uint v = r1.urand32() & 0x7FFFFFFF;
|
||||
my_hash_map::insert_result res = m.insert(counted_obj(v), counted_obj(v ^ 0xdeadbeef));
|
||||
if (res.second)
|
||||
{
|
||||
count++;
|
||||
q.push_back(v);
|
||||
}
|
||||
}
|
||||
|
||||
CRNLIB_VERIFY(m.size() == count);
|
||||
|
||||
r1.seed(seed);
|
||||
|
||||
my_hash_map cm(m);
|
||||
m.clear();
|
||||
m = cm;
|
||||
cm.reset();
|
||||
|
||||
for (uint i = 0; i < n; i++)
|
||||
{
|
||||
uint v = r1.urand32() & 0x7FFFFFFF;
|
||||
my_hash_map::const_iterator it = m.find(counted_obj(v));
|
||||
CRNLIB_VERIFY(it != m.end());
|
||||
CRNLIB_VERIFY(it->first == v);
|
||||
CRNLIB_VERIFY(it->second == (v ^ 0xdeadbeef));
|
||||
}
|
||||
|
||||
for (uint t = 0; t < 2; t++)
|
||||
{
|
||||
const uint nd = r0.irand(1, q.size() + 1);
|
||||
for (uint i = 0; i < nd; i++)
|
||||
{
|
||||
uint p = r0.irand(0, q.size());
|
||||
|
||||
int k = q[p];
|
||||
if (k >= 0)
|
||||
{
|
||||
q[p] = -k - 1;
|
||||
|
||||
bool s = m.erase(counted_obj(k));
|
||||
CRNLIB_VERIFY(s);
|
||||
}
|
||||
}
|
||||
|
||||
typedef crnlib::hash_map<uint, empty_type> uint_hash_set;
|
||||
uint_hash_set s;
|
||||
|
||||
for (uint i = 0; i < q.size(); i++)
|
||||
{
|
||||
int v = q[i];
|
||||
|
||||
if (v >= 0)
|
||||
{
|
||||
my_hash_map::const_iterator it = m.find(counted_obj(v));
|
||||
CRNLIB_VERIFY(it != m.end());
|
||||
CRNLIB_VERIFY(it->first == (uint)v);
|
||||
CRNLIB_VERIFY(it->second == ((uint)v ^ 0xdeadbeef));
|
||||
|
||||
s.insert(v);
|
||||
}
|
||||
else
|
||||
{
|
||||
my_hash_map::const_iterator it = m.find(counted_obj(-v - 1));
|
||||
CRNLIB_VERIFY(it == m.end());
|
||||
}
|
||||
}
|
||||
|
||||
uint found_count = 0;
|
||||
for (my_hash_map::const_iterator it = m.begin(); it != m.end(); ++it)
|
||||
{
|
||||
CRNLIB_VERIFY(it->second == ((uint)it->first ^ 0xdeadbeef));
|
||||
|
||||
uint_hash_set::const_iterator fit(s.find((uint)it->first));
|
||||
CRNLIB_VERIFY(fit != s.end());
|
||||
|
||||
CRNLIB_VERIFY(fit->first == it->first);
|
||||
|
||||
found_count++;
|
||||
}
|
||||
|
||||
CRNLIB_VERIFY(found_count == s.size());
|
||||
}
|
||||
|
||||
CRNLIB_VERIFY(counted_obj::m_count == m.size() * 2);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,871 @@
|
||||
// File: crn_hash_map.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
//
|
||||
// Notes:
|
||||
// Hash function ref: http://www.brpreiss.com/books/opus4/html/page215.html
|
||||
// Compared for speed against VC9's std::hash_map.
|
||||
// Linear probing, auto resizes on ~50% load factor.
|
||||
// Uses Knuth's multiplicative method (Fibonacci hashing).
|
||||
#pragma once
|
||||
#include "crn_sparse_array.h"
|
||||
#include "crn_sparse_bit_array.h"
|
||||
#include "crn_hash.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
template <typename T>
|
||||
struct hasher
|
||||
{
|
||||
inline size_t operator() (const T& key) const { return static_cast<size_t>(key); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct bit_hasher
|
||||
{
|
||||
inline size_t operator() (const T& key) const { return static_cast<size_t>(fast_hash(&key, sizeof(key))); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct equal_to
|
||||
{
|
||||
inline bool operator()(const T& a, const T& b) const { return a == b; }
|
||||
};
|
||||
|
||||
// Important: The Hasher and Equals objects must be bitwise movable!
|
||||
template<typename Key, typename Value = empty_type, typename Hasher = hasher<Key>, typename Equals = equal_to<Key> >
|
||||
class hash_map
|
||||
{
|
||||
friend class iterator;
|
||||
friend class const_iterator;
|
||||
|
||||
enum state
|
||||
{
|
||||
cStateInvalid = 0,
|
||||
cStateValid = 1
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
cMinHashSize = 4U
|
||||
};
|
||||
|
||||
public:
|
||||
typedef hash_map<Key, Value, Hasher, Equals> hash_map_type;
|
||||
typedef std::pair<Key, Value> value_type;
|
||||
typedef Key key_type;
|
||||
typedef Value referent_type;
|
||||
typedef Hasher hasher_type;
|
||||
typedef Equals equals_type;
|
||||
|
||||
hash_map() :
|
||||
m_hash_shift(32), m_num_valid(0), m_grow_threshold(0)
|
||||
{
|
||||
}
|
||||
|
||||
hash_map(const hash_map& other) :
|
||||
m_values(other.m_values),
|
||||
m_hash_shift(other.m_hash_shift),
|
||||
m_hasher(other.m_hasher),
|
||||
m_equals(other.m_equals),
|
||||
m_num_valid(other.m_num_valid),
|
||||
m_grow_threshold(other.m_grow_threshold)
|
||||
{
|
||||
}
|
||||
|
||||
hash_map& operator= (const hash_map& other)
|
||||
{
|
||||
if (this == &other)
|
||||
return *this;
|
||||
|
||||
clear();
|
||||
|
||||
m_values = other.m_values;
|
||||
m_hash_shift = other.m_hash_shift;
|
||||
m_num_valid = other.m_num_valid;
|
||||
m_grow_threshold = other.m_grow_threshold;
|
||||
m_hasher = other.m_hasher;
|
||||
m_equals = other.m_equals;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline ~hash_map()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
const Equals& get_equals() const { return m_equals; }
|
||||
Equals& get_equals() { return m_equals; }
|
||||
|
||||
void set_equals(const Equals& equals) { m_equals = equals; }
|
||||
|
||||
const Hasher& get_hasher() const { return m_hasher; }
|
||||
Hasher& get_hasher() { return m_hasher; }
|
||||
|
||||
void set_hasher(const Hasher& hasher) { m_hasher = hasher; }
|
||||
|
||||
inline void clear()
|
||||
{
|
||||
if (!m_values.empty())
|
||||
{
|
||||
if (CRNLIB_HAS_DESTRUCTOR(Key) || CRNLIB_HAS_DESTRUCTOR(Value))
|
||||
{
|
||||
node* p = &get_node(0);
|
||||
node* p_end = p + m_values.size();
|
||||
|
||||
uint num_remaining = m_num_valid;
|
||||
while (p != p_end)
|
||||
{
|
||||
if (p->state)
|
||||
{
|
||||
destruct_value_type(p);
|
||||
num_remaining--;
|
||||
if (!num_remaining)
|
||||
break;
|
||||
}
|
||||
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
m_values.clear_no_destruction();
|
||||
|
||||
m_hash_shift = 32;
|
||||
m_num_valid = 0;
|
||||
m_grow_threshold = 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline void reset()
|
||||
{
|
||||
if (!m_num_valid)
|
||||
return;
|
||||
|
||||
if (CRNLIB_HAS_DESTRUCTOR(Key) || CRNLIB_HAS_DESTRUCTOR(Value))
|
||||
{
|
||||
node* p = &get_node(0);
|
||||
node* p_end = p + m_values.size();
|
||||
|
||||
uint num_remaining = m_num_valid;
|
||||
while (p != p_end)
|
||||
{
|
||||
if (p->state)
|
||||
{
|
||||
destruct_value_type(p);
|
||||
p->state = cStateInvalid;
|
||||
|
||||
num_remaining--;
|
||||
if (!num_remaining)
|
||||
break;
|
||||
}
|
||||
|
||||
p++;
|
||||
}
|
||||
}
|
||||
else if (sizeof(node) <= 32)
|
||||
{
|
||||
memset(&m_values[0], 0, m_values.size_in_bytes());
|
||||
}
|
||||
else
|
||||
{
|
||||
node* p = &get_node(0);
|
||||
node* p_end = p + m_values.size();
|
||||
|
||||
uint num_remaining = m_num_valid;
|
||||
while (p != p_end)
|
||||
{
|
||||
if (p->state)
|
||||
{
|
||||
p->state = cStateInvalid;
|
||||
|
||||
num_remaining--;
|
||||
if (!num_remaining)
|
||||
break;
|
||||
}
|
||||
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
m_num_valid = 0;
|
||||
}
|
||||
|
||||
inline uint size()
|
||||
{
|
||||
return m_num_valid;
|
||||
}
|
||||
|
||||
inline uint get_table_size()
|
||||
{
|
||||
return m_values.size();
|
||||
}
|
||||
|
||||
inline bool empty()
|
||||
{
|
||||
return !m_num_valid;
|
||||
}
|
||||
|
||||
inline void reserve(uint new_capacity)
|
||||
{
|
||||
uint new_hash_size = math::maximum(1U, new_capacity);
|
||||
|
||||
new_hash_size = new_hash_size * 2U;
|
||||
|
||||
if (!math::is_power_of_2(new_hash_size))
|
||||
new_hash_size = math::next_pow2(new_hash_size);
|
||||
|
||||
new_hash_size = math::maximum<uint>(cMinHashSize, new_hash_size);
|
||||
|
||||
if (new_hash_size > m_values.size())
|
||||
rehash(new_hash_size);
|
||||
}
|
||||
|
||||
class const_iterator;
|
||||
|
||||
class iterator
|
||||
{
|
||||
friend class hash_map<Key, Value, Hasher, Equals>;
|
||||
friend class hash_map<Key, Value, Hasher, Equals>::const_iterator;
|
||||
|
||||
public:
|
||||
inline iterator() : m_pTable(NULL), m_index(0) { }
|
||||
inline iterator(hash_map_type& table, uint index) : m_pTable(&table), m_index(index) { }
|
||||
inline iterator(const iterator& other) : m_pTable(other.m_pTable), m_index(other.m_index) { }
|
||||
|
||||
inline iterator& operator= (const iterator& other)
|
||||
{
|
||||
m_pTable = other.m_pTable;
|
||||
m_index = other.m_index;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// post-increment
|
||||
inline iterator operator++(int)
|
||||
{
|
||||
iterator result(*this);
|
||||
++*this;
|
||||
return result;
|
||||
}
|
||||
|
||||
// pre-increment
|
||||
inline iterator& operator++()
|
||||
{
|
||||
probe();
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline value_type& operator*() const { return *get_cur(); }
|
||||
inline value_type* operator->() const { return get_cur(); }
|
||||
|
||||
inline bool operator == (const iterator& b) const { return (m_pTable == b.m_pTable) && (m_index == b.m_index); }
|
||||
inline bool operator != (const iterator& b) const { return !(*this == b); }
|
||||
inline bool operator == (const const_iterator& b) const { return (m_pTable == b.m_pTable) && (m_index == b.m_index); }
|
||||
inline bool operator != (const const_iterator& b) const { return !(*this == b); }
|
||||
|
||||
private:
|
||||
hash_map_type* m_pTable;
|
||||
uint m_index;
|
||||
|
||||
inline value_type* get_cur() const
|
||||
{
|
||||
CRNLIB_ASSERT(m_pTable && (m_index < m_pTable->m_values.size()));
|
||||
CRNLIB_ASSERT(m_pTable->get_node_state(m_index) == cStateValid);
|
||||
|
||||
return &m_pTable->get_node(m_index);
|
||||
}
|
||||
|
||||
inline void probe()
|
||||
{
|
||||
CRNLIB_ASSERT(m_pTable);
|
||||
m_index = m_pTable->find_next(m_index);
|
||||
}
|
||||
};
|
||||
|
||||
class const_iterator
|
||||
{
|
||||
friend class hash_map<Key, Value, Hasher, Equals>;
|
||||
friend class hash_map<Key, Value, Hasher, Equals>::iterator;
|
||||
|
||||
public:
|
||||
inline const_iterator() : m_pTable(NULL), m_index(0) { }
|
||||
inline const_iterator(const hash_map_type& table, uint index) : m_pTable(&table), m_index(index) { }
|
||||
inline const_iterator(const iterator& other) : m_pTable(other.m_pTable), m_index(other.m_index) { }
|
||||
inline const_iterator(const const_iterator& other) : m_pTable(other.m_pTable), m_index(other.m_index) { }
|
||||
|
||||
inline const_iterator& operator= (const const_iterator& other)
|
||||
{
|
||||
m_pTable = other.m_pTable;
|
||||
m_index = other.m_index;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline const_iterator& operator= (const iterator& other)
|
||||
{
|
||||
m_pTable = other.m_pTable;
|
||||
m_index = other.m_index;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// post-increment
|
||||
inline const_iterator operator++(int)
|
||||
{
|
||||
const_iterator result(*this);
|
||||
++*this;
|
||||
return result;
|
||||
}
|
||||
|
||||
// pre-increment
|
||||
inline const_iterator& operator++()
|
||||
{
|
||||
probe();
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline const value_type& operator*() const { return *get_cur(); }
|
||||
inline const value_type* operator->() const { return get_cur(); }
|
||||
|
||||
inline bool operator == (const const_iterator& b) const { return (m_pTable == b.m_pTable) && (m_index == b.m_index); }
|
||||
inline bool operator != (const const_iterator& b) const { return !(*this == b); }
|
||||
inline bool operator == (const iterator& b) const { return (m_pTable == b.m_pTable) && (m_index == b.m_index); }
|
||||
inline bool operator != (const iterator& b) const { return !(*this == b); }
|
||||
|
||||
private:
|
||||
const hash_map_type* m_pTable;
|
||||
uint m_index;
|
||||
|
||||
inline const value_type* get_cur() const
|
||||
{
|
||||
CRNLIB_ASSERT(m_pTable && (m_index < m_pTable->m_values.size()));
|
||||
CRNLIB_ASSERT(m_pTable->get_node_state(m_index) == cStateValid);
|
||||
|
||||
return &m_pTable->get_node(m_index);
|
||||
}
|
||||
|
||||
inline void probe()
|
||||
{
|
||||
CRNLIB_ASSERT(m_pTable);
|
||||
m_index = m_pTable->find_next(m_index);
|
||||
}
|
||||
};
|
||||
|
||||
inline const_iterator begin() const
|
||||
{
|
||||
if (!m_num_valid)
|
||||
return end();
|
||||
|
||||
return const_iterator(*this, find_next(-1));
|
||||
}
|
||||
|
||||
inline const_iterator end() const
|
||||
{
|
||||
return const_iterator(*this, m_values.size());
|
||||
}
|
||||
|
||||
inline iterator begin()
|
||||
{
|
||||
if (!m_num_valid)
|
||||
return end();
|
||||
|
||||
return iterator(*this, find_next(-1));
|
||||
}
|
||||
|
||||
inline iterator end()
|
||||
{
|
||||
return iterator(*this, m_values.size());
|
||||
}
|
||||
|
||||
typedef std::pair<iterator, bool> insert_result;
|
||||
|
||||
inline insert_result insert(const Key& k, const Value& v = Value())
|
||||
{
|
||||
insert_result result;
|
||||
if (!insert_no_grow(result, k, v))
|
||||
{
|
||||
grow();
|
||||
|
||||
// This must succeed.
|
||||
if (!insert_no_grow(result, k, v))
|
||||
{
|
||||
CRNLIB_FAIL("insert() failed");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline insert_result insert(const value_type& v)
|
||||
{
|
||||
return insert(v.first, v.second);
|
||||
}
|
||||
|
||||
inline const_iterator find(const Key& k) const
|
||||
{
|
||||
return const_iterator(*this, find_index(k));
|
||||
}
|
||||
|
||||
inline iterator find(const Key& k)
|
||||
{
|
||||
return iterator(*this, find_index(k));
|
||||
}
|
||||
|
||||
inline bool erase(const Key& k)
|
||||
{
|
||||
int i = find_index(k);
|
||||
|
||||
if (i >= static_cast<int>(m_values.size()))
|
||||
return false;
|
||||
|
||||
node* pDst = &get_node(i);
|
||||
destruct_value_type(pDst);
|
||||
pDst->state = cStateInvalid;
|
||||
|
||||
m_num_valid--;
|
||||
|
||||
for ( ; ; )
|
||||
{
|
||||
int r, j = i;
|
||||
|
||||
node* pSrc = pDst;
|
||||
|
||||
do
|
||||
{
|
||||
if (!i)
|
||||
{
|
||||
i = m_values.size() - 1;
|
||||
pSrc = &get_node(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
i--;
|
||||
pSrc--;
|
||||
}
|
||||
|
||||
if (!pSrc->state)
|
||||
return true;
|
||||
|
||||
r = hash_key(pSrc->first);
|
||||
|
||||
} while ((i <= r && r < j) || (r < j && j < i) || (j < i && i <= r));
|
||||
|
||||
move_node(pDst, pSrc);
|
||||
|
||||
pDst = pSrc;
|
||||
}
|
||||
}
|
||||
|
||||
inline void swap(hash_map_type& other)
|
||||
{
|
||||
m_values.swap(other.m_values);
|
||||
utils::swap(m_hash_shift, other.m_hash_shift);
|
||||
utils::swap(m_num_valid, other.m_num_valid);
|
||||
utils::swap(m_grow_threshold, other.m_grow_threshold);
|
||||
utils::swap(m_hasher, other.m_hasher);
|
||||
utils::swap(m_equals, other.m_equals);
|
||||
}
|
||||
|
||||
private:
|
||||
struct node : public value_type
|
||||
{
|
||||
uint8 state;
|
||||
};
|
||||
|
||||
static inline void construct_value_type(value_type* pDst, const Key& k, const Value& v)
|
||||
{
|
||||
if (CRNLIB_IS_BITWISE_COPYABLE(Key))
|
||||
memcpy(&pDst->first, &k, sizeof(Key));
|
||||
else
|
||||
scalar_type<Key>::construct(&pDst->first, k);
|
||||
|
||||
if (CRNLIB_IS_BITWISE_COPYABLE(Value))
|
||||
memcpy(&pDst->second, &v, sizeof(Value));
|
||||
else
|
||||
scalar_type<Value>::construct(&pDst->second, v);
|
||||
}
|
||||
|
||||
static inline void construct_value_type(value_type* pDst, const value_type* pSrc)
|
||||
{
|
||||
if ((CRNLIB_IS_BITWISE_COPYABLE(Key)) && (CRNLIB_IS_BITWISE_COPYABLE(Value)))
|
||||
{
|
||||
memcpy(pDst, pSrc, sizeof(value_type));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (CRNLIB_IS_BITWISE_COPYABLE(Key))
|
||||
memcpy(&pDst->first, &pSrc->first, sizeof(Key));
|
||||
else
|
||||
scalar_type<Key>::construct(&pDst->first, pSrc->first);
|
||||
|
||||
if (CRNLIB_IS_BITWISE_COPYABLE(Value))
|
||||
memcpy(&pDst->second, &pSrc->second, sizeof(Value));
|
||||
else
|
||||
scalar_type<Value>::construct(&pDst->second, pSrc->second);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void destruct_value_type(value_type* p)
|
||||
{
|
||||
scalar_type<Key>::destruct(&p->first);
|
||||
scalar_type<Value>::destruct(&p->second);
|
||||
}
|
||||
|
||||
static inline void move_node(node* pDst, node* pSrc)
|
||||
{
|
||||
CRNLIB_ASSERT(!pDst->state);
|
||||
|
||||
if (CRNLIB_IS_BITWISE_MOVABLE(Key) && CRNLIB_IS_BITWISE_MOVABLE(Value))
|
||||
{
|
||||
memcpy(pDst, pSrc, sizeof(node));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (CRNLIB_IS_BITWISE_MOVABLE(Key))
|
||||
memcpy(&pDst->first, &pSrc->first, sizeof(Key));
|
||||
else
|
||||
{
|
||||
scalar_type<Key>::construct(&pDst->first, pSrc->first);
|
||||
scalar_type<Key>::destruct(&pSrc->first);
|
||||
}
|
||||
|
||||
if (CRNLIB_IS_BITWISE_MOVABLE(Value))
|
||||
memcpy(&pDst->second, &pSrc->second, sizeof(Value));
|
||||
else
|
||||
{
|
||||
scalar_type<Value>::construct(&pDst->second, pSrc->second);
|
||||
scalar_type<Value>::destruct(&pSrc->second);
|
||||
}
|
||||
|
||||
pDst->state = cStateValid;
|
||||
}
|
||||
|
||||
pSrc->state = cStateInvalid;
|
||||
}
|
||||
|
||||
struct raw_node
|
||||
{
|
||||
inline raw_node()
|
||||
{
|
||||
node* p = reinterpret_cast<node*>(this);
|
||||
p->state = cStateInvalid;
|
||||
}
|
||||
|
||||
inline ~raw_node()
|
||||
{
|
||||
node* p = reinterpret_cast<node*>(this);
|
||||
if (p->state)
|
||||
hash_map_type::destruct_value_type(p);
|
||||
}
|
||||
|
||||
inline raw_node(const raw_node& other)
|
||||
{
|
||||
node* pDst = reinterpret_cast<node*>(this);
|
||||
const node* pSrc = reinterpret_cast<const node*>(&other);
|
||||
|
||||
if (pSrc->state)
|
||||
{
|
||||
hash_map_type::construct_value_type(pDst, pSrc);
|
||||
pDst->state = cStateValid;
|
||||
}
|
||||
else
|
||||
pDst->state = cStateInvalid;
|
||||
}
|
||||
|
||||
inline raw_node& operator= (const raw_node& rhs)
|
||||
{
|
||||
if (this == &rhs)
|
||||
return *this;
|
||||
|
||||
node* pDst = reinterpret_cast<node*>(this);
|
||||
const node* pSrc = reinterpret_cast<const node*>(&rhs);
|
||||
|
||||
if (pSrc->state)
|
||||
{
|
||||
if (pDst->state)
|
||||
{
|
||||
pDst->first = pSrc->first;
|
||||
pDst->second = pSrc->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
hash_map_type::construct_value_type(pDst, pSrc);
|
||||
pDst->state = cStateValid;
|
||||
}
|
||||
}
|
||||
else if (pDst->state)
|
||||
{
|
||||
hash_map_type::destruct_value_type(pDst);
|
||||
pDst->state = cStateInvalid;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
uint8 m_bits[sizeof(node)];
|
||||
};
|
||||
|
||||
typedef crnlib::vector<raw_node> node_vector;
|
||||
|
||||
node_vector m_values;
|
||||
uint m_hash_shift;
|
||||
|
||||
Hasher m_hasher;
|
||||
Equals m_equals;
|
||||
|
||||
uint m_num_valid;
|
||||
|
||||
uint m_grow_threshold;
|
||||
|
||||
inline int hash_key(const Key& k) const
|
||||
{
|
||||
CRNLIB_ASSERT((1U << (32U - m_hash_shift)) == m_values.size());
|
||||
|
||||
uint hash = static_cast<uint>(m_hasher(k));
|
||||
|
||||
// Fibonacci hashing
|
||||
hash = (2654435769U * hash) >> m_hash_shift;
|
||||
|
||||
CRNLIB_ASSERT(hash < m_values.size());
|
||||
return hash;
|
||||
}
|
||||
|
||||
inline const node& get_node(uint index) const
|
||||
{
|
||||
return *reinterpret_cast<const node*>(&m_values[index]);
|
||||
}
|
||||
|
||||
inline node& get_node(uint index)
|
||||
{
|
||||
return *reinterpret_cast<node*>(&m_values[index]);
|
||||
}
|
||||
|
||||
inline state get_node_state(uint index) const
|
||||
{
|
||||
return static_cast<state>(get_node(index).state);
|
||||
}
|
||||
|
||||
inline void set_node_state(uint index, bool valid)
|
||||
{
|
||||
get_node(index).state = valid;
|
||||
}
|
||||
|
||||
inline void grow()
|
||||
{
|
||||
rehash(math::maximum<uint>(cMinHashSize, m_values.size() * 2U));
|
||||
}
|
||||
|
||||
inline void rehash(uint new_hash_size)
|
||||
{
|
||||
CRNLIB_ASSERT(new_hash_size >= m_num_valid);
|
||||
CRNLIB_ASSERT(math::is_power_of_2(new_hash_size));
|
||||
|
||||
if ((new_hash_size < m_num_valid) || (new_hash_size == m_values.size()))
|
||||
return;
|
||||
|
||||
hash_map new_map;
|
||||
new_map.m_values.resize(new_hash_size);
|
||||
new_map.m_hash_shift = 32U - math::floor_log2i(new_hash_size);
|
||||
CRNLIB_ASSERT(new_hash_size == (1U << (32U - new_map.m_hash_shift)));
|
||||
new_map.m_grow_threshold = UINT_MAX;
|
||||
|
||||
node* pNode = reinterpret_cast<node*>(m_values.begin());
|
||||
node* pNode_end = pNode + m_values.size();
|
||||
|
||||
while (pNode != pNode_end)
|
||||
{
|
||||
if (pNode->state)
|
||||
{
|
||||
new_map.move_into(pNode);
|
||||
|
||||
if (new_map.m_num_valid == m_num_valid)
|
||||
break;
|
||||
}
|
||||
|
||||
pNode++;
|
||||
}
|
||||
|
||||
new_map.m_grow_threshold = (new_hash_size + 1U) >> 1U;
|
||||
|
||||
m_values.clear_no_destruction();
|
||||
m_hash_shift = 32;
|
||||
|
||||
swap(new_map);
|
||||
}
|
||||
|
||||
inline uint find_next(int index) const
|
||||
{
|
||||
index++;
|
||||
|
||||
if (index >= static_cast<int>(m_values.size()))
|
||||
return index;
|
||||
|
||||
const node* pNode = &get_node(index);
|
||||
|
||||
for ( ; ; )
|
||||
{
|
||||
if (pNode->state)
|
||||
break;
|
||||
|
||||
if (++index >= static_cast<int>(m_values.size()))
|
||||
break;
|
||||
|
||||
pNode++;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
inline uint find_index(const Key& k) const
|
||||
{
|
||||
if (m_num_valid)
|
||||
{
|
||||
int index = hash_key(k);
|
||||
const node* pNode = &get_node(index);
|
||||
|
||||
if (pNode->state)
|
||||
{
|
||||
if (m_equals(pNode->first, k))
|
||||
return index;
|
||||
|
||||
const int orig_index = index;
|
||||
|
||||
for ( ; ; )
|
||||
{
|
||||
if (!index)
|
||||
{
|
||||
index = m_values.size() - 1;
|
||||
pNode = &get_node(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
index--;
|
||||
pNode--;
|
||||
}
|
||||
|
||||
if (index == orig_index)
|
||||
break;
|
||||
|
||||
if (!pNode->state)
|
||||
break;
|
||||
|
||||
if (m_equals(pNode->first, k))
|
||||
return index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m_values.size();
|
||||
}
|
||||
|
||||
inline bool insert_no_grow(insert_result& result, const Key& k, const Value& v = Value())
|
||||
{
|
||||
if (!m_values.size())
|
||||
return false;
|
||||
|
||||
int index = hash_key(k);
|
||||
node* pNode = &get_node(index);
|
||||
|
||||
if (pNode->state)
|
||||
{
|
||||
if (m_equals(pNode->first, k))
|
||||
{
|
||||
result.first = iterator(*this, index);
|
||||
result.second = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
const int orig_index = index;
|
||||
|
||||
for ( ; ; )
|
||||
{
|
||||
if (!index)
|
||||
{
|
||||
index = m_values.size() - 1;
|
||||
pNode = &get_node(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
index--;
|
||||
pNode--;
|
||||
}
|
||||
|
||||
if (orig_index == index)
|
||||
return false;
|
||||
|
||||
if (!pNode->state)
|
||||
break;
|
||||
|
||||
if (m_equals(pNode->first, k))
|
||||
{
|
||||
result.first = iterator(*this, index);
|
||||
result.second = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_num_valid >= m_grow_threshold)
|
||||
return false;
|
||||
|
||||
construct_value_type(pNode, k, v);
|
||||
|
||||
pNode->state = cStateValid;
|
||||
|
||||
m_num_valid++;
|
||||
CRNLIB_ASSERT(m_num_valid <= m_values.size());
|
||||
|
||||
result.first = iterator(*this, index);
|
||||
result.second = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void move_into(node* pNode)
|
||||
{
|
||||
int index = hash_key(pNode->first);
|
||||
node* pDst_node = &get_node(index);
|
||||
|
||||
if (pDst_node->state)
|
||||
{
|
||||
const int orig_index = index;
|
||||
|
||||
for ( ; ; )
|
||||
{
|
||||
if (!index)
|
||||
{
|
||||
index = m_values.size() - 1;
|
||||
pDst_node = &get_node(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
index--;
|
||||
pDst_node--;
|
||||
}
|
||||
|
||||
if (index == orig_index)
|
||||
{
|
||||
CRNLIB_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pDst_node->state)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
move_node(pDst_node, pNode);
|
||||
|
||||
m_num_valid++;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Key, typename Value, typename Hasher, typename Equals>
|
||||
struct bitwise_movable< hash_map<Key, Value, Hasher, Equals> > { enum { cFlag = true }; };
|
||||
|
||||
template<typename Key, typename Value, typename Hasher, typename Equals>
|
||||
inline void swap(hash_map<Key, Value, Hasher, Equals>& a, hash_map<Key, Value, Hasher, Equals>& b)
|
||||
{
|
||||
a.swap(b);
|
||||
}
|
||||
|
||||
extern void hash_map_test();
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,64 @@
|
||||
// File: crn_helpers.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
|
||||
#define CRNLIB_NO_COPY_OR_ASSIGNMENT_OP(c) c(const c&); c& operator= (const c&);
|
||||
#define CRNLIB_NO_HEAP_ALLOC() private: static void* operator new(size_t); static void* operator new[](size_t);
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
namespace helpers
|
||||
{
|
||||
template<typename T> struct rel_ops
|
||||
{
|
||||
friend bool operator!=(const T& x, const T& y) { return (!(x == y)); }
|
||||
friend bool operator> (const T& x, const T& y) { return (y < x); }
|
||||
friend bool operator<=(const T& x, const T& y) { return (!(y < x)); }
|
||||
friend bool operator>=(const T& x, const T& y) { return (!(x < y)); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline T* construct(T* p)
|
||||
{
|
||||
return new (static_cast<void*>(p)) T;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
inline T* construct(T* p, const U& init)
|
||||
{
|
||||
return new (static_cast<void*>(p)) T(init);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void construct_array(T* p, uint n)
|
||||
{
|
||||
T* q = p + n;
|
||||
for ( ; p != q; ++p)
|
||||
new (static_cast<void*>(p)) T;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
void construct_array(T* p, uint n, const U& init)
|
||||
{
|
||||
T* q = p + n;
|
||||
for ( ; p != q; ++p)
|
||||
new (static_cast<void*>(p)) T(init);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void destruct(T* p)
|
||||
{
|
||||
p;
|
||||
p->~T();
|
||||
}
|
||||
|
||||
template <typename T> inline void destruct_array(T* p, uint n)
|
||||
{
|
||||
T* q = p + n;
|
||||
for ( ; p != q; ++p)
|
||||
p->~T();
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,387 @@
|
||||
// File: crn_huffman_codes.cpp
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#include "crn_core.h"
|
||||
#include "crn_huffman_codes.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
struct sym_freq
|
||||
{
|
||||
uint m_freq;
|
||||
uint16 m_left;
|
||||
uint16 m_right;
|
||||
|
||||
inline bool operator< (const sym_freq& other) const
|
||||
{
|
||||
return m_freq > other.m_freq;
|
||||
}
|
||||
};
|
||||
|
||||
static inline sym_freq* radix_sort_syms(uint num_syms, sym_freq* syms0, sym_freq* syms1)
|
||||
{
|
||||
const uint cMaxPasses = 2;
|
||||
uint hist[256 * cMaxPasses];
|
||||
|
||||
memset(hist, 0, sizeof(hist[0]) * 256 * cMaxPasses);
|
||||
|
||||
sym_freq* p = syms0;
|
||||
sym_freq* q = syms0 + (num_syms >> 1) * 2;
|
||||
|
||||
for ( ; p != q; p += 2)
|
||||
{
|
||||
const uint freq0 = p[0].m_freq;
|
||||
const uint freq1 = p[1].m_freq;
|
||||
|
||||
hist[ freq0 & 0xFF]++;
|
||||
hist[256 + ((freq0 >> 8) & 0xFF)]++;
|
||||
|
||||
hist[ freq1 & 0xFF]++;
|
||||
hist[256 + ((freq1 >> 8) & 0xFF)]++;
|
||||
}
|
||||
|
||||
if (num_syms & 1)
|
||||
{
|
||||
const uint freq = p->m_freq;
|
||||
|
||||
hist[ freq & 0xFF]++;
|
||||
hist[256 + ((freq >> 8) & 0xFF)]++;
|
||||
}
|
||||
|
||||
sym_freq* pCur_syms = syms0;
|
||||
sym_freq* pNew_syms = syms1;
|
||||
|
||||
for (uint pass = 0; pass < cMaxPasses; pass++)
|
||||
{
|
||||
const uint* pHist = &hist[pass << 8];
|
||||
|
||||
uint offsets[256];
|
||||
|
||||
uint cur_ofs = 0;
|
||||
for (uint i = 0; i < 256; i += 2)
|
||||
{
|
||||
offsets[i] = cur_ofs;
|
||||
cur_ofs += pHist[i];
|
||||
|
||||
offsets[i+1] = cur_ofs;
|
||||
cur_ofs += pHist[i+1];
|
||||
}
|
||||
|
||||
const uint pass_shift = pass << 3;
|
||||
|
||||
sym_freq* p = pCur_syms;
|
||||
sym_freq* q = pCur_syms + (num_syms >> 1) * 2;
|
||||
|
||||
for ( ; p != q; p += 2)
|
||||
{
|
||||
uint c0 = p[0].m_freq;
|
||||
uint c1 = p[1].m_freq;
|
||||
|
||||
if (pass)
|
||||
{
|
||||
c0 >>= 8;
|
||||
c1 >>= 8;
|
||||
}
|
||||
|
||||
c0 &= 0xFF;
|
||||
c1 &= 0xFF;
|
||||
|
||||
if (c0 == c1)
|
||||
{
|
||||
uint dst_offset0 = offsets[c0];
|
||||
|
||||
offsets[c0] = dst_offset0 + 2;
|
||||
|
||||
pNew_syms[dst_offset0] = p[0];
|
||||
pNew_syms[dst_offset0 + 1] = p[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
uint dst_offset0 = offsets[c0]++;
|
||||
uint dst_offset1 = offsets[c1]++;
|
||||
|
||||
pNew_syms[dst_offset0] = p[0];
|
||||
pNew_syms[dst_offset1] = p[1];
|
||||
}
|
||||
}
|
||||
|
||||
if (num_syms & 1)
|
||||
{
|
||||
uint c = ((p->m_freq) >> pass_shift) & 0xFF;
|
||||
|
||||
uint dst_offset = offsets[c];
|
||||
offsets[c] = dst_offset + 1;
|
||||
|
||||
pNew_syms[dst_offset] = *p;
|
||||
}
|
||||
|
||||
sym_freq* t = pCur_syms;
|
||||
pCur_syms = pNew_syms;
|
||||
pNew_syms = t;
|
||||
}
|
||||
|
||||
#ifdef CRNLIB_ASSERTS_ENABLED
|
||||
uint prev_freq = 0;
|
||||
for (uint i = 0; i < num_syms; i++)
|
||||
{
|
||||
CRNLIB_ASSERT(!(pCur_syms[i].m_freq < prev_freq));
|
||||
prev_freq = pCur_syms[i].m_freq;
|
||||
}
|
||||
#endif
|
||||
|
||||
return pCur_syms;
|
||||
}
|
||||
|
||||
struct huffman_work_tables
|
||||
{
|
||||
enum { cMaxInternalNodes = cHuffmanMaxSupportedSyms };
|
||||
|
||||
sym_freq syms0[cHuffmanMaxSupportedSyms + 1 + cMaxInternalNodes];
|
||||
sym_freq syms1[cHuffmanMaxSupportedSyms + 1 + cMaxInternalNodes];
|
||||
|
||||
uint16 queue[cMaxInternalNodes];
|
||||
};
|
||||
|
||||
void* create_generate_huffman_codes_tables()
|
||||
{
|
||||
return crnlib_new<huffman_work_tables>();
|
||||
}
|
||||
|
||||
void free_generate_huffman_codes_tables(void* p)
|
||||
{
|
||||
crnlib_delete(static_cast<huffman_work_tables*>(p));
|
||||
}
|
||||
|
||||
#if USE_CALCULATE_MINIMUM_REDUNDANCY
|
||||
/* calculate_minimum_redundancy() written by
|
||||
Alistair Moffat, alistair@cs.mu.oz.au,
|
||||
Jyrki Katajainen, jyrki@diku.dk
|
||||
November 1996.
|
||||
*/
|
||||
static void calculate_minimum_redundancy(int A[], int n) {
|
||||
int root; /* next root node to be used */
|
||||
int leaf; /* next leaf to be used */
|
||||
int next; /* next value to be assigned */
|
||||
int avbl; /* number of available nodes */
|
||||
int used; /* number of internal nodes */
|
||||
int dpth; /* current depth of leaves */
|
||||
|
||||
/* check for pathological cases */
|
||||
if (n==0) { return; }
|
||||
if (n==1) { A[0] = 0; return; }
|
||||
|
||||
/* first pass, left to right, setting parent pointers */
|
||||
A[0] += A[1]; root = 0; leaf = 2;
|
||||
for (next=1; next < n-1; next++) {
|
||||
/* select first item for a pairing */
|
||||
if (leaf>=n || A[root]<A[leaf]) {
|
||||
A[next] = A[root]; A[root++] = next;
|
||||
} else
|
||||
A[next] = A[leaf++];
|
||||
|
||||
/* add on the second item */
|
||||
if (leaf>=n || (root<next && A[root]<A[leaf])) {
|
||||
A[next] += A[root]; A[root++] = next;
|
||||
} else
|
||||
A[next] += A[leaf++];
|
||||
}
|
||||
|
||||
/* second pass, right to left, setting internal depths */
|
||||
A[n-2] = 0;
|
||||
for (next=n-3; next>=0; next--)
|
||||
A[next] = A[A[next]]+1;
|
||||
|
||||
/* third pass, right to left, setting leaf depths */
|
||||
avbl = 1; used = dpth = 0; root = n-2; next = n-1;
|
||||
while (avbl>0) {
|
||||
while (root>=0 && A[root]==dpth) {
|
||||
used++; root--;
|
||||
}
|
||||
while (avbl>used) {
|
||||
A[next--] = dpth; avbl--;
|
||||
}
|
||||
avbl = 2*used; dpth++; used = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool generate_huffman_codes(void* pContext, uint num_syms, const uint16* pFreq, uint8* pCodesizes, uint& max_code_size, uint& total_freq_ret)
|
||||
{
|
||||
if ((!num_syms) || (num_syms > cHuffmanMaxSupportedSyms))
|
||||
return false;
|
||||
|
||||
huffman_work_tables& state = *static_cast<huffman_work_tables*>(pContext);;
|
||||
|
||||
uint max_freq = 0;
|
||||
uint total_freq = 0;
|
||||
|
||||
uint num_used_syms = 0;
|
||||
for (uint i = 0; i < num_syms; i++)
|
||||
{
|
||||
uint freq = pFreq[i];
|
||||
|
||||
if (!freq)
|
||||
pCodesizes[i] = 0;
|
||||
else
|
||||
{
|
||||
total_freq += freq;
|
||||
max_freq = math::maximum(max_freq, freq);
|
||||
|
||||
sym_freq& sf = state.syms0[num_used_syms];
|
||||
sf.m_left = (uint16)i;
|
||||
sf.m_right = UINT16_MAX;
|
||||
sf.m_freq = freq;
|
||||
num_used_syms++;
|
||||
}
|
||||
}
|
||||
|
||||
total_freq_ret = total_freq;
|
||||
|
||||
if (num_used_syms == 1)
|
||||
{
|
||||
pCodesizes[state.syms0[0].m_left] = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
sym_freq* syms = radix_sort_syms(num_used_syms, state.syms0, state.syms1);
|
||||
|
||||
#if USE_CALCULATE_MINIMUM_REDUNDANCY
|
||||
int x[cHuffmanMaxSupportedSyms];
|
||||
for (uint i = 0; i < num_used_syms; i++)
|
||||
x[i] = state.syms0[i].m_freq;
|
||||
|
||||
calculate_minimum_redundancy(x, num_used_syms);
|
||||
|
||||
uint max_len = 0;
|
||||
for (uint i = 0; i < num_used_syms; i++)
|
||||
{
|
||||
uint len = x[i];
|
||||
max_len = math::maximum(len, max_len);
|
||||
pCodesizes[state.syms0[i].m_left] = static_cast<uint8>(len);
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
// Dummy node
|
||||
sym_freq& sf = state.syms0[num_used_syms];
|
||||
sf.m_left = UINT16_MAX;
|
||||
sf.m_right = UINT16_MAX;
|
||||
sf.m_freq = UINT_MAX;
|
||||
|
||||
uint next_internal_node = num_used_syms + 1;
|
||||
|
||||
uint queue_front = 0;
|
||||
uint queue_end = 0;
|
||||
|
||||
uint next_lowest_sym = 0;
|
||||
|
||||
uint num_nodes_remaining = num_used_syms;
|
||||
do
|
||||
{
|
||||
uint left_freq = syms[next_lowest_sym].m_freq;
|
||||
uint left_child = next_lowest_sym;
|
||||
|
||||
if ((queue_end > queue_front) && (syms[state.queue[queue_front]].m_freq < left_freq))
|
||||
{
|
||||
left_child = state.queue[queue_front];
|
||||
left_freq = syms[left_child].m_freq;
|
||||
|
||||
queue_front++;
|
||||
}
|
||||
else
|
||||
next_lowest_sym++;
|
||||
|
||||
uint right_freq = syms[next_lowest_sym].m_freq;
|
||||
uint right_child = next_lowest_sym;
|
||||
|
||||
if ((queue_end > queue_front) && (syms[state.queue[queue_front]].m_freq < right_freq))
|
||||
{
|
||||
right_child = state.queue[queue_front];
|
||||
right_freq = syms[right_child].m_freq;
|
||||
|
||||
queue_front++;
|
||||
}
|
||||
else
|
||||
next_lowest_sym++;
|
||||
|
||||
const uint internal_node_index = next_internal_node;
|
||||
next_internal_node++;
|
||||
|
||||
CRNLIB_ASSERT(next_internal_node < CRNLIB_ARRAYSIZE(state.syms0));
|
||||
|
||||
syms[internal_node_index].m_freq = left_freq + right_freq;
|
||||
syms[internal_node_index].m_left = static_cast<uint16>(left_child);
|
||||
syms[internal_node_index].m_right = static_cast<uint16>(right_child);
|
||||
|
||||
CRNLIB_ASSERT(queue_end < huffman_work_tables::cMaxInternalNodes);
|
||||
state.queue[queue_end] = static_cast<uint16>(internal_node_index);
|
||||
queue_end++;
|
||||
|
||||
num_nodes_remaining--;
|
||||
|
||||
} while (num_nodes_remaining > 1);
|
||||
|
||||
CRNLIB_ASSERT(next_lowest_sym == num_used_syms);
|
||||
CRNLIB_ASSERT((queue_end - queue_front) == 1);
|
||||
|
||||
uint cur_node_index = state.queue[queue_front];
|
||||
|
||||
uint32* pStack = (syms == state.syms0) ? (uint32*)state.syms1 : (uint32*)state.syms0;
|
||||
uint32* pStack_top = pStack;
|
||||
|
||||
uint max_level = 0;
|
||||
|
||||
for ( ; ; )
|
||||
{
|
||||
uint level = cur_node_index >> 16;
|
||||
uint node_index = cur_node_index & 0xFFFF;
|
||||
|
||||
uint left_child = syms[node_index].m_left;
|
||||
uint right_child = syms[node_index].m_right;
|
||||
|
||||
uint next_level = (cur_node_index + 0x10000) & 0xFFFF0000;
|
||||
|
||||
if (left_child < num_used_syms)
|
||||
{
|
||||
max_level = math::maximum(max_level, level);
|
||||
|
||||
pCodesizes[syms[left_child].m_left] = static_cast<uint8>(level + 1);
|
||||
|
||||
if (right_child < num_used_syms)
|
||||
{
|
||||
pCodesizes[syms[right_child].m_left] = static_cast<uint8>(level + 1);
|
||||
|
||||
if (pStack == pStack_top) break;
|
||||
cur_node_index = *--pStack;
|
||||
}
|
||||
else
|
||||
{
|
||||
cur_node_index = next_level | right_child;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (right_child < num_used_syms)
|
||||
{
|
||||
max_level = math::maximum(max_level, level);
|
||||
|
||||
pCodesizes[syms[right_child].m_left] = static_cast<uint8>(level + 1);
|
||||
|
||||
cur_node_index = next_level | left_child;
|
||||
}
|
||||
else
|
||||
{
|
||||
*pStack++ = next_level | left_child;
|
||||
|
||||
cur_node_index = next_level | right_child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
max_code_size = max_level + 1;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// File: crn_huffman_codes.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
const uint cHuffmanMaxSupportedSyms = 8192;
|
||||
|
||||
void* create_generate_huffman_codes_tables();
|
||||
void free_generate_huffman_codes_tables(void* p);
|
||||
|
||||
bool generate_huffman_codes(void* pContext, uint num_syms, const uint16* pFreq, uint8* pCodesizes, uint& max_code_size, uint& total_freq_ret);
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,612 @@
|
||||
// File: crn_image.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
#include "crn_color.h"
|
||||
#include "crn_vec.h"
|
||||
#include "crn_pixel_format.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
template<typename color_type>
|
||||
class image
|
||||
{
|
||||
public:
|
||||
typedef color_type color_t;
|
||||
|
||||
typedef crnlib::vector<color_type> pixel_buf_t;
|
||||
|
||||
image() :
|
||||
m_width(0),
|
||||
m_height(0),
|
||||
m_pitch(0),
|
||||
m_total(0),
|
||||
m_comp_flags(pixel_format_helpers::cDefaultCompFlags),
|
||||
m_pPixels(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
image(uint width, uint height, uint pitch = UINT_MAX, const color_type& background = color_type::make_black(), uint flags = pixel_format_helpers::cDefaultCompFlags) :
|
||||
m_comp_flags(flags)
|
||||
{
|
||||
CRNLIB_ASSERT((width > 0) && (height > 0));
|
||||
if (pitch == UINT_MAX)
|
||||
pitch = width;
|
||||
|
||||
m_pixel_buf.resize(pitch * height);
|
||||
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
m_pitch = pitch;
|
||||
m_total = m_pitch * m_height;
|
||||
|
||||
m_pPixels = &m_pixel_buf.front();
|
||||
|
||||
set_all(background);
|
||||
}
|
||||
|
||||
image(color_type* pPixels, uint width, uint height, uint pitch = UINT_MAX, uint flags = pixel_format_helpers::cDefaultCompFlags)
|
||||
{
|
||||
alias(pPixels, width, height, pitch, flags);
|
||||
}
|
||||
|
||||
image& operator= (const image& other)
|
||||
{
|
||||
if (this == &other)
|
||||
return *this;
|
||||
|
||||
if (other.m_pixel_buf.empty())
|
||||
{
|
||||
// This doesn't look very safe - let's make a new instance.
|
||||
//m_pixel_buf.clear();
|
||||
//m_pPixels = other.m_pPixels;
|
||||
|
||||
const uint total_pixels = other.m_pitch * other.m_height;
|
||||
if ((total_pixels) && (other.m_pPixels))
|
||||
{
|
||||
m_pixel_buf.resize(total_pixels);
|
||||
m_pixel_buf.insert(0, other.m_pPixels, m_pixel_buf.size());
|
||||
m_pPixels = &m_pixel_buf.front();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pixel_buf.clear();
|
||||
m_pPixels = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pixel_buf = other.m_pixel_buf;
|
||||
m_pPixels = &m_pixel_buf.front();
|
||||
}
|
||||
|
||||
m_width = other.m_width;
|
||||
m_height = other.m_height;
|
||||
m_pitch = other.m_pitch;
|
||||
m_total = other.m_total;
|
||||
m_comp_flags = other.m_comp_flags;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
image(const image& other) :
|
||||
m_width(0), m_height(0), m_pitch(0), m_total(0), m_comp_flags(pixel_format_helpers::cDefaultCompFlags), m_pPixels(NULL)
|
||||
{
|
||||
*this = other;
|
||||
}
|
||||
|
||||
void alias(color_type* pPixels, uint width, uint height, uint pitch = UINT_MAX, uint flags = pixel_format_helpers::cDefaultCompFlags)
|
||||
{
|
||||
m_pixel_buf.clear();
|
||||
|
||||
m_pPixels = pPixels;
|
||||
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
m_pitch = (pitch == UINT_MAX) ? width : pitch;
|
||||
m_total = m_pitch * m_height;
|
||||
m_comp_flags = flags;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
m_pPixels = NULL;
|
||||
m_pixel_buf.clear();
|
||||
m_width = 0;
|
||||
m_height = 0;
|
||||
m_pitch = 0;
|
||||
m_total = 0;
|
||||
m_comp_flags = pixel_format_helpers::cDefaultCompFlags;
|
||||
}
|
||||
|
||||
inline bool is_valid() const { return m_total > 0; }
|
||||
|
||||
inline pixel_format_helpers::component_flags get_comp_flags() const { return static_cast<pixel_format_helpers::component_flags>(m_comp_flags); }
|
||||
inline void set_comp_flags(pixel_format_helpers::component_flags new_flags) { m_comp_flags = new_flags; }
|
||||
inline void reset_comp_flags() { m_comp_flags = pixel_format_helpers::cDefaultCompFlags; }
|
||||
|
||||
inline bool is_component_valid(uint index) const { CRNLIB_ASSERT(index < 4U); return utils::is_flag_set(m_comp_flags, index); }
|
||||
inline void set_component_valid(uint index, bool state) { CRNLIB_ASSERT(index < 4U); utils::set_flag(m_comp_flags, index, state); }
|
||||
|
||||
inline bool has_rgb() const { return is_component_valid(0) || is_component_valid(1) || is_component_valid(2); }
|
||||
inline bool has_alpha() const { return is_component_valid(3); }
|
||||
|
||||
inline bool is_grayscale() const { return utils::is_bit_set(m_comp_flags, pixel_format_helpers::cCompFlagGrayscale); }
|
||||
inline void set_grayscale(bool state) { utils::set_bit(m_comp_flags, pixel_format_helpers::cCompFlagGrayscale, state); }
|
||||
|
||||
void set_all(const color_type& c)
|
||||
{
|
||||
for (uint i = 0; i < m_total; i++)
|
||||
m_pPixels[i] = c;
|
||||
}
|
||||
|
||||
void convert_to_grayscale()
|
||||
{
|
||||
for (uint y = 0; y < m_height; y++)
|
||||
for (uint x = 0; x < m_width; x++)
|
||||
{
|
||||
color_type c((*this)(x, y));
|
||||
typename color_type::component_t l = static_cast< typename color_type::component_t >(c.get_luma());
|
||||
c.r = l;
|
||||
c.g = l;
|
||||
c.b = l;
|
||||
(*this)(x, y) = c;
|
||||
}
|
||||
|
||||
set_grayscale(true);
|
||||
}
|
||||
|
||||
void swizzle(uint r, uint g, uint b, uint a)
|
||||
{
|
||||
for (uint y = 0; y < m_height; y++)
|
||||
for (uint x = 0; x < m_width; x++)
|
||||
{
|
||||
const color_type& c = (*this)(x, y);
|
||||
|
||||
(*this)(x, y) = color_type(c[r], c[g], c[b], c[a]);
|
||||
}
|
||||
}
|
||||
|
||||
void set_alpha_to_luma()
|
||||
{
|
||||
for (uint y = 0; y < m_height; y++)
|
||||
for (uint x = 0; x < m_width; x++)
|
||||
{
|
||||
color_type c((*this)(x, y));
|
||||
typename color_type::component_t l = static_cast< typename color_type::component_t >(c.get_luma());
|
||||
c.a = l;
|
||||
(*this)(x, y) = c;
|
||||
}
|
||||
|
||||
set_component_valid(3, true);
|
||||
}
|
||||
|
||||
bool extract_block(color_type* pDst, uint x, uint y, uint w, uint h, bool flip_xy = false) const
|
||||
{
|
||||
if ((x >= m_width) || (y >= m_height))
|
||||
return false;
|
||||
|
||||
if (flip_xy)
|
||||
{
|
||||
for (uint y_ofs = 0; y_ofs < h; y_ofs++)
|
||||
for (uint x_ofs = 0; x_ofs < w; x_ofs++)
|
||||
pDst[x_ofs * 4 + y_ofs] = get_clamped(x_ofs + x, y_ofs + y);
|
||||
}
|
||||
else if (((x + w) > m_width) || ((y + h) > m_height))
|
||||
{
|
||||
for (uint y_ofs = 0; y_ofs < h; y_ofs++)
|
||||
for (uint x_ofs = 0; x_ofs < w; x_ofs++)
|
||||
*pDst++ = get_clamped(x_ofs + x, y_ofs + y);
|
||||
}
|
||||
else
|
||||
{
|
||||
const color_type* pSrc = get_scanline(y) + x;
|
||||
|
||||
for (uint i = h; i; i--)
|
||||
{
|
||||
memcpy(pDst, pSrc, w * sizeof(color_type));
|
||||
pDst += w;
|
||||
|
||||
pSrc += m_pitch;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void fill(uint x, uint y, uint w, uint h, const color_type& c)
|
||||
{
|
||||
CRNLIB_ASSERT((x + w) <= m_width);
|
||||
CRNLIB_ASSERT((y + h) <= m_height);
|
||||
|
||||
color_type* p = get_scanline(y) + x;
|
||||
|
||||
for (uint i = h; i; i--)
|
||||
{
|
||||
color_type* q = p;
|
||||
for (uint j = w; j; j--)
|
||||
*q++ = c;
|
||||
p += m_pitch;
|
||||
}
|
||||
}
|
||||
|
||||
void draw_box(int x, int y, uint width, uint height, const color_type& c)
|
||||
{
|
||||
draw_line(x, y, x + width - 1, y, c);
|
||||
draw_line(x, y, x, y + height - 1, c);
|
||||
draw_line(x + width - 1, y, x + width - 1, y + height - 1, c);
|
||||
draw_line(x, y + height - 1, x + width - 1, y + height - 1, c);
|
||||
}
|
||||
|
||||
// No clipping!
|
||||
bool copy(uint src_x, uint src_y, uint src_w, uint src_h, uint dst_x, uint dst_y, const image& src)
|
||||
{
|
||||
if ( ((src_x + src_w) > src.get_width()) || ((src_y + src_h) > src.get_height()) )
|
||||
return false;
|
||||
|
||||
if ( ((dst_x + src_w) > get_width()) || ((dst_y + src_h) > get_height()) )
|
||||
return false;
|
||||
|
||||
const color_type* pS = &src(src_x, src_y);
|
||||
color_type* pD = &(*this)(dst_x, dst_y);
|
||||
|
||||
const uint bytes_to_copy = src_w * sizeof(color_type);
|
||||
for (uint i = src_h; i; i--)
|
||||
{
|
||||
memcpy(pD, pS, bytes_to_copy);
|
||||
|
||||
pS += src.get_pitch();
|
||||
pD += get_pitch();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// With clipping.
|
||||
void blit(int dst_x, int dst_y, const image& src)
|
||||
{
|
||||
uint src_x = 0;
|
||||
uint src_y = 0;
|
||||
|
||||
if (dst_x < 0)
|
||||
{
|
||||
src_x = -dst_x;
|
||||
if (src_x >= src.get_width())
|
||||
return;
|
||||
dst_x = 0;
|
||||
}
|
||||
|
||||
if (dst_y < 0)
|
||||
{
|
||||
src_y = -dst_y;
|
||||
if (src_y >= src.get_height())
|
||||
return;
|
||||
dst_y = 0;
|
||||
}
|
||||
|
||||
if ((dst_x >= (int)m_width) || (dst_y >= (int)m_height))
|
||||
return;
|
||||
|
||||
uint width = math::minimum(m_width - dst_x, src.get_width() - src_x);
|
||||
uint height = math::minimum(m_height - dst_y, src.get_height() - src_y);
|
||||
|
||||
bool success = copy(src_x, src_y, width, height, dst_x, dst_y, src);
|
||||
success;
|
||||
CRNLIB_ASSERT(success);
|
||||
}
|
||||
|
||||
bool resize(uint new_width, uint new_height, uint new_pitch = UINT_MAX, const color_type background = color_type::make_black())
|
||||
{
|
||||
if (new_pitch == UINT_MAX)
|
||||
new_pitch = new_width;
|
||||
|
||||
if ((new_width == m_width) && (new_height == m_height) && (new_pitch == m_pitch))
|
||||
return true;
|
||||
|
||||
if ((!new_width) || (!new_height) || (!new_pitch))
|
||||
{
|
||||
clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
pixel_buf_t existing_pixels;
|
||||
existing_pixels.swap(m_pixel_buf);
|
||||
|
||||
if (!m_pixel_buf.try_resize(new_height * new_pitch))
|
||||
{
|
||||
clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint y = 0; y < new_height; y++)
|
||||
{
|
||||
for (uint x = 0; x < new_width; x++)
|
||||
{
|
||||
if ((x < m_width) && (y < m_height))
|
||||
m_pixel_buf[x + y * new_pitch] = existing_pixels[x + y * m_pitch];
|
||||
else
|
||||
m_pixel_buf[x + y * new_pitch] = background;
|
||||
}
|
||||
}
|
||||
|
||||
m_width = new_width;
|
||||
m_height = new_height;
|
||||
m_pitch = new_pitch;
|
||||
m_total = new_pitch * new_height;
|
||||
m_pPixels = &m_pixel_buf.front();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline uint get_width() const { return m_width; }
|
||||
inline uint get_height() const { return m_height; }
|
||||
inline uint get_total_pixels() const { return m_width * m_height; }
|
||||
|
||||
inline uint get_pitch() const { return m_pitch; }
|
||||
inline uint get_pitch_in_bytes() const { return m_pitch * sizeof(color_type); }
|
||||
|
||||
// Returns pitch * height, NOT width * height!
|
||||
inline uint get_total() const { return m_total; }
|
||||
|
||||
inline uint get_block_width(uint block_size) const { return (m_width + block_size - 1) / block_size; }
|
||||
inline uint get_block_height(uint block_size) const { return (m_height + block_size - 1) / block_size; }
|
||||
inline uint get_total_blocks(uint block_size) const { return get_block_width(block_size) * get_block_height(block_size); }
|
||||
|
||||
inline uint get_size_in_bytes() const { return sizeof(color_type) * m_total; }
|
||||
|
||||
inline const color_type* get_pixels() const { return m_pPixels; }
|
||||
inline color_type* get_pixels() { return m_pPixels; }
|
||||
|
||||
inline const color_type& operator() (uint x, uint y) const
|
||||
{
|
||||
CRNLIB_ASSERT((x < m_width) && (y < m_height));
|
||||
return m_pPixels[x + y * m_pitch];
|
||||
}
|
||||
|
||||
inline color_type& operator() (uint x, uint y)
|
||||
{
|
||||
CRNLIB_ASSERT((x < m_width) && (y < m_height));
|
||||
return m_pPixels[x + y * m_pitch];
|
||||
}
|
||||
|
||||
inline const color_type& get_clamped (int x, int y) const
|
||||
{
|
||||
x = math::clamp<int>(x, 0, m_width - 1);
|
||||
y = math::clamp<int>(y, 0, m_height - 1);
|
||||
return (*this)((uint)x, (uint)y);
|
||||
}
|
||||
|
||||
// Sample image with bilinear filtering.
|
||||
// (x,y) - Continuous coordinates, where pixel centers are at (.5,.5), valid image coords are (0,width] and (0,height].
|
||||
void get_filtered(float x, float y, color_type& result) const
|
||||
{
|
||||
x -= .5f;
|
||||
y -= .5f;
|
||||
|
||||
int ix = (int)floor(x);
|
||||
int iy = (int)floor(y);
|
||||
float wx = x - ix;
|
||||
float wy = y - iy;
|
||||
|
||||
color_type a(get_clamped(ix, iy));
|
||||
color_type b(get_clamped(ix + 1, iy));
|
||||
color_type c(get_clamped(ix, iy + 1));
|
||||
color_type d(get_clamped(ix + 1, iy + 1));
|
||||
|
||||
for (uint i = 0; i < 4; i++)
|
||||
{
|
||||
double top = math::lerp<double>(a[i], b[i], wx);
|
||||
double bot = math::lerp<double>(c[i], d[i], wx);
|
||||
double m = math::lerp<double>(top, bot, wy);
|
||||
|
||||
if (!color_type::component_traits::cFloat)
|
||||
m += .5f;
|
||||
|
||||
result.set_component(i, static_cast< typename color_type::parameter_t >(m));
|
||||
}
|
||||
}
|
||||
|
||||
void get_filtered(float x, float y, vec4F& result) const
|
||||
{
|
||||
x -= .5f;
|
||||
y -= .5f;
|
||||
|
||||
int ix = (int)floor(x);
|
||||
int iy = (int)floor(y);
|
||||
float wx = x - ix;
|
||||
float wy = y - iy;
|
||||
|
||||
color_type a(get_clamped(ix, iy));
|
||||
color_type b(get_clamped(ix + 1, iy));
|
||||
color_type c(get_clamped(ix, iy + 1));
|
||||
color_type d(get_clamped(ix + 1, iy + 1));
|
||||
|
||||
for (uint i = 0; i < 4; i++)
|
||||
{
|
||||
float top = math::lerp<float>(a[i], b[i], wx);
|
||||
float bot = math::lerp<float>(c[i], d[i], wx);
|
||||
float m = math::lerp<float>(top, bot, wy);
|
||||
|
||||
result[i] = m;
|
||||
}
|
||||
}
|
||||
|
||||
inline void set_pixel(uint x, uint y, const color_type& c)
|
||||
{
|
||||
CRNLIB_ASSERT((x < m_width) && (y < m_height));
|
||||
m_pPixels[x + y * m_pitch] = c;
|
||||
}
|
||||
|
||||
inline void set_pixel_clipped(int x, int y, const color_type& c)
|
||||
{
|
||||
if ((x < 0) || (x >= (int)m_width) || (y < 0) || (y >= (int)m_height))
|
||||
return;
|
||||
|
||||
m_pPixels[x + y * m_pitch] = c;
|
||||
}
|
||||
|
||||
inline const color_type* get_scanline(uint y) const
|
||||
{
|
||||
CRNLIB_ASSERT(y < m_height);
|
||||
return &m_pPixels[y * m_pitch];
|
||||
}
|
||||
|
||||
inline color_type* get_scanline(uint y)
|
||||
{
|
||||
CRNLIB_ASSERT(y < m_height);
|
||||
return &m_pPixels[y * m_pitch];
|
||||
}
|
||||
|
||||
inline const color_type* get_ptr() const
|
||||
{
|
||||
return m_pPixels;
|
||||
}
|
||||
|
||||
inline color_type* get_ptr()
|
||||
{
|
||||
return m_pPixels;
|
||||
}
|
||||
|
||||
inline void swap(image& other)
|
||||
{
|
||||
utils::swap(m_width, other.m_width);
|
||||
utils::swap(m_height, other.m_height);
|
||||
utils::swap(m_pitch, other.m_pitch);
|
||||
utils::swap(m_total, other.m_total);
|
||||
utils::swap(m_comp_flags, other.m_comp_flags);
|
||||
utils::swap(m_pPixels, other.m_pPixels);
|
||||
m_pixel_buf.swap(other.m_pixel_buf);
|
||||
}
|
||||
|
||||
void draw_line(int xs, int ys, int xe, int ye, const color_type& color)
|
||||
{
|
||||
if (xs > xe)
|
||||
{
|
||||
utils::swap(xs, xe);
|
||||
utils::swap(ys, ye);
|
||||
}
|
||||
|
||||
int dx = xe - xs, dy = ye - ys;
|
||||
|
||||
if (!dx)
|
||||
{
|
||||
if (ys > ye)
|
||||
utils::swap(ys, ye);
|
||||
for (int i = ys ; i <= ye ; i++)
|
||||
set_pixel_clipped(xs, i, color);
|
||||
}
|
||||
else if (!dy)
|
||||
{
|
||||
for (int i = xs ; i < xe ; i++)
|
||||
set_pixel_clipped(i, ys, color);
|
||||
}
|
||||
else if (dy > 0)
|
||||
{
|
||||
if (dy <= dx)
|
||||
{
|
||||
int e = 2 * dy - dx;
|
||||
int e_no_inc = 2 * dy;
|
||||
int e_inc = 2 * (dy - dx);
|
||||
rasterize_line(xs, ys, xe, ye, 0, 1, e, e_inc, e_no_inc, color);
|
||||
}
|
||||
else
|
||||
{
|
||||
int e = 2 * dx - dy;
|
||||
int e_no_inc = 2 * dx;
|
||||
int e_inc = 2 * (dx - dy);
|
||||
rasterize_line(xs, ys, xe, ye, 1, 1, e, e_inc, e_no_inc, color);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dy = -dy;
|
||||
|
||||
if (dy <= dx)
|
||||
{
|
||||
int e = 2 * dy - dx;
|
||||
int e_no_inc = 2 * dy;
|
||||
int e_inc = 2 * (dy - dx);
|
||||
rasterize_line(xs, ys, xe, ye, 0, -1, e, e_inc, e_no_inc, color);
|
||||
}
|
||||
else
|
||||
{
|
||||
int e = 2 * dx - dy;
|
||||
int e_no_inc = (2 * dx);
|
||||
int e_inc = 2 * (dx - dy);
|
||||
rasterize_line(xe, ye, xs, ys, 1, -1, e, e_inc, e_no_inc, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const pixel_buf_t& get_pixel_buf() const { return m_pixel_buf; }
|
||||
pixel_buf_t& get_pixel_buf() { return m_pixel_buf; }
|
||||
|
||||
private:
|
||||
uint m_width;
|
||||
uint m_height;
|
||||
uint m_pitch;
|
||||
uint m_total;
|
||||
uint m_comp_flags;
|
||||
|
||||
color_type* m_pPixels;
|
||||
|
||||
pixel_buf_t m_pixel_buf;
|
||||
|
||||
void rasterize_line(int xs, int ys, int xe, int ye, int pred, int inc_dec, int e, int e_inc, int e_no_inc, const color_type& color)
|
||||
{
|
||||
int start, end, var;
|
||||
|
||||
if (pred)
|
||||
{
|
||||
start = ys;
|
||||
end = ye;
|
||||
var = xs;
|
||||
|
||||
for (int i = start; i <= end; i++)
|
||||
{
|
||||
set_pixel_clipped(var, i, color);
|
||||
|
||||
if (e < 0)
|
||||
e += e_no_inc;
|
||||
else
|
||||
{
|
||||
var += inc_dec;
|
||||
e += e_inc;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
start = xs;
|
||||
end = xe;
|
||||
var = ys;
|
||||
|
||||
for (int i = start; i <= end; i++)
|
||||
{
|
||||
set_pixel_clipped(i, var, color);
|
||||
|
||||
if (e < 0)
|
||||
e += e_no_inc;
|
||||
else
|
||||
{
|
||||
var += inc_dec;
|
||||
e += e_inc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
typedef image<color_quad_u8> image_u8;
|
||||
typedef image<color_quad_i16> image_i16;
|
||||
typedef image<color_quad_u16> image_u16;
|
||||
typedef image<color_quad_i32> image_i32;
|
||||
typedef image<color_quad_u32> image_u32;
|
||||
typedef image<color_quad_f> image_f;
|
||||
|
||||
template<typename color_type>
|
||||
inline void swap(image<color_type>& a, image<color_type>& b)
|
||||
{
|
||||
a.swap(b);
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,141 @@
|
||||
// File: crn_image_utils.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
#include "crn_image.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
enum pixel_format;
|
||||
|
||||
namespace image_utils
|
||||
{
|
||||
bool load_from_file_stb(const wchar_t* pFilename, image_u8& img);
|
||||
|
||||
enum
|
||||
{
|
||||
cSaveIgnoreAlpha = 1,
|
||||
cSaveGrayscale = 2
|
||||
};
|
||||
|
||||
const int cSaveLuma = -1;
|
||||
|
||||
bool save_to_file_stb(const wchar_t* pFilename, const image_u8& img, uint save_flags = 0, int comp_index = cSaveLuma);
|
||||
|
||||
bool load_from_file(image_u8& dest, const wchar_t* pFilename, int flags = 0);
|
||||
|
||||
bool save_to_grayscale_file(const wchar_t* pFilename, const image_u8& src, int component, int flags = 0);
|
||||
|
||||
bool save_to_file(const wchar_t* pFilename, const image_u8& src, int flags = 0, bool ignore_alpha = false);
|
||||
|
||||
bool has_alpha(const image_u8& img);
|
||||
bool is_normal_map(const image_u8& img, const wchar_t* pFilename = NULL);
|
||||
void renorm_normal_map(image_u8& img);
|
||||
|
||||
struct resample_params
|
||||
{
|
||||
resample_params() :
|
||||
m_dst_width(0),
|
||||
m_dst_height(0),
|
||||
m_pFilter("lanczos4"),
|
||||
m_filter_scale(1.0f),
|
||||
m_srgb(true),
|
||||
m_wrapping(false),
|
||||
m_first_comp(0),
|
||||
m_num_comps(4),
|
||||
m_source_gamma(2.2f), // 1.75f
|
||||
m_multithreaded(true)
|
||||
{
|
||||
}
|
||||
|
||||
uint m_dst_width;
|
||||
uint m_dst_height;
|
||||
const char* m_pFilter;
|
||||
float m_filter_scale;
|
||||
bool m_srgb;
|
||||
bool m_wrapping;
|
||||
uint m_first_comp;
|
||||
uint m_num_comps;
|
||||
float m_source_gamma;
|
||||
bool m_multithreaded;
|
||||
};
|
||||
|
||||
bool resample_single_thread(const image_u8& src, image_u8& dst, const resample_params& params);
|
||||
bool resample_multithreaded(const image_u8& src, image_u8& dst, const resample_params& params);
|
||||
bool resample(const image_u8& src, image_u8& dst, const resample_params& params);
|
||||
|
||||
bool compute_delta(image_u8& dest, image_u8& a, image_u8& b, uint scale = 2);
|
||||
|
||||
class error_metrics
|
||||
{
|
||||
public:
|
||||
error_metrics() { utils::zero_this(this); }
|
||||
|
||||
void print(const wchar_t* pName) const;
|
||||
|
||||
// If num_channels==0, luma error is computed.
|
||||
// If pHist != NULL, it must point to a 256 entry array.
|
||||
bool compute(const image_u8& a, const image_u8& b, uint first_channel, uint num_channels, bool average_component_error = true);
|
||||
|
||||
uint mMax;
|
||||
double mMean;
|
||||
double mMeanSquared;
|
||||
double mRootMeanSquared;
|
||||
double mPeakSNR;
|
||||
|
||||
inline bool operator== (const error_metrics& other) const
|
||||
{
|
||||
return mPeakSNR == other.mPeakSNR;
|
||||
}
|
||||
|
||||
inline bool operator< (const error_metrics& other) const
|
||||
{
|
||||
return mPeakSNR < other.mPeakSNR;
|
||||
}
|
||||
|
||||
inline bool operator> (const error_metrics& other) const
|
||||
{
|
||||
return mPeakSNR > other.mPeakSNR;
|
||||
}
|
||||
};
|
||||
|
||||
void print_image_metrics(const image_u8& src_img, const image_u8& dst_img);
|
||||
|
||||
double compute_block_ssim(uint n, const uint8* pX, const uint8* pY);
|
||||
double compute_ssim(const image_u8& a, const image_u8& b, int channel_index);
|
||||
void print_ssim(const image_u8& src_img, const image_u8& dst_img);
|
||||
|
||||
enum conversion_type
|
||||
{
|
||||
cConversion_Invalid = -1,
|
||||
|
||||
cConversion_To_CCxY,
|
||||
cConversion_From_CCxY,
|
||||
|
||||
cConversion_To_xGxR,
|
||||
cConversion_From_xGxR,
|
||||
|
||||
cConversion_To_xGBR,
|
||||
cConversion_From_xGBR,
|
||||
|
||||
cConversion_To_AGBR,
|
||||
cConversion_From_AGBR,
|
||||
|
||||
cConversion_XY_to_XYZ,
|
||||
|
||||
cConversion_Y_To_A,
|
||||
|
||||
cConversion_A_To_RGBA,
|
||||
cConversion_Y_To_RGB,
|
||||
|
||||
cConversionTotal
|
||||
};
|
||||
|
||||
void convert_image(image_u8& img, conversion_type conv_type);
|
||||
|
||||
image_utils::conversion_type get_conversion_type(bool cooking, pixel_format fmt);
|
||||
|
||||
image_utils::conversion_type get_image_conversion_type_from_crn_format(crn_format fmt);
|
||||
|
||||
double compute_std_dev(uint n, const color_quad_u8* pPixels, uint first_channel, uint num_channels);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
// File: crn_intersect.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
#include "crn_ray.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
namespace intersection
|
||||
{
|
||||
enum result
|
||||
{
|
||||
cBackfacing = -1,
|
||||
cFailure = 0,
|
||||
cSuccess,
|
||||
cParallel,
|
||||
cInside,
|
||||
};
|
||||
|
||||
// Returns cInside, cSuccess, or cFailure.
|
||||
// Algorithm: Graphics Gems 1
|
||||
template<typename vector_type, typename scalar_type, typename ray_type, typename aabb_type>
|
||||
result ray_aabb(vector_type& coord, scalar_type& t, const ray_type& ray, const aabb_type& box)
|
||||
{
|
||||
enum
|
||||
{
|
||||
cNumDim = vector_type::num_elements,
|
||||
cRight = 0,
|
||||
cLeft = 1,
|
||||
cMiddle = 2
|
||||
};
|
||||
|
||||
bool inside = true;
|
||||
int quadrant[cNumDim];
|
||||
scalar_type candidate_plane[cNumDim];
|
||||
|
||||
for (int i = 0; i < cNumDim; i++)
|
||||
{
|
||||
if (ray.get_origin()[i] < box[0][i])
|
||||
{
|
||||
quadrant[i] = cLeft;
|
||||
candidate_plane[i] = box[0][i];
|
||||
inside = false;
|
||||
}
|
||||
else if (ray.get_origin()[i] > box[1][i])
|
||||
{
|
||||
quadrant[i] = cRight;
|
||||
candidate_plane[i] = box[1][i];
|
||||
inside = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
quadrant[i] = cMiddle;
|
||||
}
|
||||
}
|
||||
|
||||
if (inside)
|
||||
{
|
||||
coord = ray.get_origin();
|
||||
t = 0.0f;
|
||||
return cInside;
|
||||
}
|
||||
|
||||
scalar_type max_t[cNumDim];
|
||||
for (int i = 0; i < cNumDim; i++)
|
||||
{
|
||||
if ((quadrant[i] != cMiddle) && (ray.get_direction()[i] != 0.0f))
|
||||
max_t[i] = (candidate_plane[i] - ray.get_origin()[i]) / ray.get_direction()[i];
|
||||
else
|
||||
max_t[i] = -1.0f;
|
||||
}
|
||||
|
||||
int which_plane = 0;
|
||||
for (int i = 1; i < cNumDim; i++)
|
||||
if (max_t[which_plane] < max_t[i])
|
||||
which_plane = i;
|
||||
|
||||
if (max_t[which_plane] < 0.0f)
|
||||
return cFailure;
|
||||
|
||||
for (int i = 0; i < cNumDim; i++)
|
||||
{
|
||||
if (i != which_plane)
|
||||
{
|
||||
coord[i] = ray.get_origin()[i] + max_t[which_plane] * ray.get_direction()[i];
|
||||
|
||||
if ( (coord[i] < box[0][i]) || (coord[i] > box[1][i]) )
|
||||
return cFailure;
|
||||
}
|
||||
else
|
||||
{
|
||||
coord[i] = candidate_plane[i];
|
||||
}
|
||||
|
||||
CRNLIB_ASSERT(coord[i] >= box[0][i] && coord[i] <= box[1][i]);
|
||||
}
|
||||
|
||||
t = max_t[which_plane];
|
||||
return cSuccess;
|
||||
}
|
||||
|
||||
template<typename vector_type, typename scalar_type, typename ray_type, typename aabb_type>
|
||||
result ray_aabb(bool& started_within, vector_type& coord, scalar_type& t, const ray_type& ray, const aabb_type& box)
|
||||
{
|
||||
if (!box.contains(ray.get_origin()))
|
||||
{
|
||||
started_within = false;
|
||||
return ray_aabb(coord, t, ray, box);
|
||||
}
|
||||
|
||||
started_within = true;
|
||||
|
||||
float diag_dist = box.diagonal_length() * 1.5f;
|
||||
ray_type outside_ray(ray.eval(diag_dist), -ray.get_direction());
|
||||
|
||||
result res(ray_aabb(coord, t, outside_ray, box));
|
||||
if (res != cSuccess)
|
||||
return res;
|
||||
|
||||
t = math::maximum(0.0f, diag_dist - t);
|
||||
return cSuccess;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
// File: crn_lzma_codec.cpp
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#include "crn_core.h"
|
||||
#include "crn_lzma_codec.h"
|
||||
#include "crn_strutils.h"
|
||||
#include "crn_checksum.h"
|
||||
#include "lzma_lzmalib.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
lzma_codec::lzma_codec() :
|
||||
m_pCompress(LzmaCompress),
|
||||
m_pUncompress(LzmaUncompress)
|
||||
{
|
||||
CRNLIB_ASSUME(cLZMAPropsSize == LZMA_PROPS_SIZE);
|
||||
}
|
||||
|
||||
lzma_codec::~lzma_codec()
|
||||
{
|
||||
}
|
||||
|
||||
bool lzma_codec::pack(const void* p, uint n, crnlib::vector<uint8>& buf)
|
||||
{
|
||||
if (n > 1024U*1024U*1024U)
|
||||
return false;
|
||||
|
||||
uint max_comp_size = n + math::maximum<uint>(128, n >> 8);
|
||||
buf.resize(sizeof(header) + max_comp_size);
|
||||
|
||||
header* pHDR = reinterpret_cast<header*>(&buf[0]);
|
||||
uint8* pComp_data = &buf[sizeof(header)];
|
||||
|
||||
utils::zero_object(*pHDR);
|
||||
|
||||
pHDR->m_uncomp_size = n;
|
||||
pHDR->m_adler32 = adler32(p, n);
|
||||
|
||||
if (n)
|
||||
{
|
||||
size_t destLen = 0;
|
||||
size_t outPropsSize = 0;
|
||||
int status = SZ_ERROR_INPUT_EOF;
|
||||
|
||||
for (uint trial = 0; trial < 3; trial++)
|
||||
{
|
||||
destLen = max_comp_size;
|
||||
outPropsSize = cLZMAPropsSize;
|
||||
|
||||
status = (*m_pCompress)(pComp_data, &destLen, reinterpret_cast<const unsigned char*>(p), n,
|
||||
pHDR->m_lzma_props, &outPropsSize,
|
||||
-1, /* 0 <= level <= 9, default = 5 */
|
||||
0, /* default = (1 << 24) */
|
||||
-1, /* 0 <= lc <= 8, default = 3 */
|
||||
-1, /* 0 <= lp <= 4, default = 0 */
|
||||
-1, /* 0 <= pb <= 4, default = 2 */
|
||||
-1, /* 5 <= fb <= 273, default = 32 */
|
||||
(g_number_of_processors > 1) ? 2 : 1
|
||||
);
|
||||
|
||||
if (status != SZ_ERROR_OUTPUT_EOF)
|
||||
break;
|
||||
|
||||
max_comp_size += ((n+1)/2);
|
||||
buf.resize(sizeof(header) + max_comp_size);
|
||||
pHDR = reinterpret_cast<header*>(&buf[0]);
|
||||
pComp_data = &buf[sizeof(header)];
|
||||
}
|
||||
|
||||
if (status != SZ_OK)
|
||||
{
|
||||
buf.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
pHDR->m_comp_size = static_cast<uint>(destLen);
|
||||
|
||||
buf.resize(CRNLIB_SIZEOF_U32(header) + static_cast<uint32>(destLen));
|
||||
}
|
||||
|
||||
pHDR->m_sig = header::cSig;
|
||||
pHDR->m_checksum = static_cast<uint8>(adler32((uint8*)pHDR + header::cChecksumSkipBytes, sizeof(header) - header::cChecksumSkipBytes));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool lzma_codec::unpack(const void* p, uint n, crnlib::vector<uint8>& buf)
|
||||
{
|
||||
buf.resize(0);
|
||||
|
||||
if (n < sizeof(header))
|
||||
return false;
|
||||
|
||||
const header& hdr = *static_cast<const header*>(p);
|
||||
if (hdr.m_sig != header::cSig)
|
||||
return false;
|
||||
|
||||
if (static_cast<uint8>(adler32((const uint8*)&hdr + header::cChecksumSkipBytes, sizeof(hdr) - header::cChecksumSkipBytes)) != hdr.m_checksum)
|
||||
return false;
|
||||
|
||||
if (!hdr.m_uncomp_size)
|
||||
return true;
|
||||
|
||||
if (!hdr.m_comp_size)
|
||||
return false;
|
||||
|
||||
if (hdr.m_uncomp_size > 1024U*1024U*1024U)
|
||||
return false;
|
||||
|
||||
if (!buf.try_resize(hdr.m_uncomp_size))
|
||||
return false;
|
||||
|
||||
const uint8* pComp_data = static_cast<const uint8*>(p) + sizeof(header);
|
||||
size_t srcLen = n - sizeof(header);
|
||||
if (srcLen < hdr.m_comp_size)
|
||||
return false;
|
||||
|
||||
size_t destLen = hdr.m_uncomp_size;
|
||||
|
||||
int status = (*m_pUncompress)(&buf[0], &destLen, pComp_data, &srcLen,
|
||||
hdr.m_lzma_props, cLZMAPropsSize);
|
||||
|
||||
if ((status != SZ_OK) || (destLen != hdr.m_uncomp_size))
|
||||
{
|
||||
buf.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (adler32(&buf[0], buf.size()) != hdr.m_adler32)
|
||||
{
|
||||
buf.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,60 @@
|
||||
// File: crn_lzma_codec.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
#include "crn_packed_uint.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
class lzma_codec
|
||||
{
|
||||
public:
|
||||
lzma_codec();
|
||||
~lzma_codec();
|
||||
|
||||
// Always available, because we're statically linking in lzmalib now vs. dynamically loading the DLL.
|
||||
const bool is_initialized() const { return true; }
|
||||
|
||||
bool pack(const void* p, uint n, crnlib::vector<uint8>& buf);
|
||||
|
||||
bool unpack(const void* p, uint n, crnlib::vector<uint8>& buf);
|
||||
|
||||
private:
|
||||
typedef int (__stdcall *LzmaCompressFuncPtr)(unsigned char *dest, size_t *destLen, const unsigned char *src, size_t srcLen,
|
||||
unsigned char *outProps, size_t *outPropsSize, /* *outPropsSize must be = 5 */
|
||||
int level, /* 0 <= level <= 9, default = 5 */
|
||||
unsigned dictSize, /* default = (1 << 24) */
|
||||
int lc, /* 0 <= lc <= 8, default = 3 */
|
||||
int lp, /* 0 <= lp <= 4, default = 0 */
|
||||
int pb, /* 0 <= pb <= 4, default = 2 */
|
||||
int fb, /* 5 <= fb <= 273, default = 32 */
|
||||
int numThreads /* 1 or 2, default = 2 */
|
||||
);
|
||||
|
||||
typedef int (__stdcall *LzmaUncompressFuncPtr)(unsigned char *dest, size_t *destLen, const unsigned char *src, size_t *srcLen,
|
||||
const unsigned char *props, size_t propsSize);
|
||||
|
||||
LzmaCompressFuncPtr m_pCompress;
|
||||
LzmaUncompressFuncPtr m_pUncompress;
|
||||
|
||||
enum { cLZMAPropsSize = 5 };
|
||||
|
||||
#pragma pack(push)
|
||||
#pragma pack(1)
|
||||
struct header
|
||||
{
|
||||
enum { cSig = 'L' | ('0' << 8), cChecksumSkipBytes = 3 };
|
||||
packed_uint<2> m_sig;
|
||||
uint8 m_checksum;
|
||||
|
||||
uint8 m_lzma_props[cLZMAPropsSize];
|
||||
|
||||
packed_uint<4> m_comp_size;
|
||||
packed_uint<4> m_uncomp_size;
|
||||
|
||||
packed_uint<4> m_adler32;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
};
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,76 @@
|
||||
// File: crn_math.cpp
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#include "crn_core.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
namespace math
|
||||
{
|
||||
uint g_bitmasks[32] =
|
||||
{
|
||||
1U << 0U, 1U << 1U, 1U << 2U, 1U << 3U,
|
||||
1U << 4U, 1U << 5U, 1U << 6U, 1U << 7U,
|
||||
1U << 8U, 1U << 9U, 1U << 10U, 1U << 11U,
|
||||
1U << 12U, 1U << 13U, 1U << 14U, 1U << 15U,
|
||||
1U << 16U, 1U << 17U, 1U << 18U, 1U << 19U,
|
||||
1U << 20U, 1U << 21U, 1U << 22U, 1U << 23U,
|
||||
1U << 24U, 1U << 25U, 1U << 26U, 1U << 27U,
|
||||
1U << 28U, 1U << 29U, 1U << 30U, 1U << 31U
|
||||
};
|
||||
|
||||
double compute_entropy(const uint8* p, uint n)
|
||||
{
|
||||
uint hist[256];
|
||||
utils::zero_object(hist);
|
||||
|
||||
for (uint i = 0; i < n; i++)
|
||||
hist[*p++]++;
|
||||
|
||||
double entropy = 0.0f;
|
||||
|
||||
const double invln2 = 1.0f/log(2.0f);
|
||||
for (uint i = 0; i < 256; i++)
|
||||
{
|
||||
if (!hist[i])
|
||||
continue;
|
||||
|
||||
double prob = static_cast<double>(hist[i]) / n;
|
||||
entropy += (-log(prob) * invln2) * hist[i];
|
||||
}
|
||||
|
||||
return entropy;
|
||||
}
|
||||
|
||||
void compute_lower_pow2_dim(int& width, int& height)
|
||||
{
|
||||
const int tex_width = width;
|
||||
const int tex_height = height;
|
||||
|
||||
width = 1;
|
||||
for ( ; ; )
|
||||
{
|
||||
if ((width * 2) > tex_width)
|
||||
break;
|
||||
width *= 2;
|
||||
}
|
||||
|
||||
height = 1;
|
||||
for ( ; ; )
|
||||
{
|
||||
if ((height * 2) > tex_height)
|
||||
break;
|
||||
height *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
void compute_upper_pow2_dim(int& width, int& height)
|
||||
{
|
||||
if (!math::is_power_of_2((uint32)width))
|
||||
width = math::next_pow2((uint32)width);
|
||||
|
||||
if (!math::is_power_of_2((uint32)height))
|
||||
height = math::next_pow2((uint32)height);
|
||||
}
|
||||
|
||||
} // namespace math
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,222 @@
|
||||
// File: crn_math.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
|
||||
#if defined(_M_IX86) && defined(_MSC_VER)
|
||||
#include <intrin.h>
|
||||
#pragma intrinsic(__emulu)
|
||||
unsigned __int64 __emulu(unsigned int a,unsigned int b );
|
||||
#endif
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
namespace math
|
||||
{
|
||||
const float cNearlyInfinite = 1.0e+37f;
|
||||
|
||||
const float cDegToRad = 0.01745329252f;
|
||||
const float cRadToDeg = 57.29577951f;
|
||||
|
||||
extern uint g_bitmasks[32];
|
||||
|
||||
template<typename T> inline bool within_closed_range(T a, T b, T c) { return (a >= b) && (a <= c); }
|
||||
|
||||
template<typename T> inline bool within_open_range(T a, T b, T c) { return (a >= b) && (a < c); }
|
||||
|
||||
// Yes I know these should probably be pass by ref, not val:
|
||||
// http://www.stepanovpapers.com/notes.pdf
|
||||
// Just don't use them on non-simple (non built-in) types!
|
||||
template<typename T> inline T minimum(T a, T b) { return (a < b) ? a : b; }
|
||||
|
||||
template<typename T> inline T minimum(T a, T b, T c) { return minimum(minimum(a, b), c); }
|
||||
|
||||
template<typename T> inline T maximum(T a, T b) { return (a > b) ? a : b; }
|
||||
|
||||
template<typename T> inline T maximum(T a, T b, T c) { return maximum(maximum(a, b), c); }
|
||||
|
||||
template<typename T, typename U> inline T lerp(T a, T b, U c) { return a + (b - a) * c; }
|
||||
|
||||
template<typename T> inline T clamp(T value, T low, T high) { return (value < low) ? low : ((value > high) ? high : value); }
|
||||
|
||||
template<typename T> inline T saturate(T value) { return (value < 0.0f) ? 0.0f : ((value > 1.0f) ? 1.0f : value); }
|
||||
|
||||
inline int float_to_int(float f) { return static_cast<int>(f); }
|
||||
|
||||
inline uint float_to_uint(float f) { return static_cast<uint>(f); }
|
||||
|
||||
inline int float_to_int(double f) { return static_cast<int>(f); }
|
||||
|
||||
inline uint float_to_uint(double f) { return static_cast<uint>(f); }
|
||||
|
||||
inline int float_to_int_round(float f) { return static_cast<int>((f < 0.0f) ? -floor(-f + .5f) : floor(f + .5f)); }
|
||||
|
||||
inline uint float_to_uint_round(float f) { return static_cast<uint>((f < 0.0f) ? 0.0f : floor(f + .5f)); }
|
||||
|
||||
template<typename T> inline int sign(T value) { return (value < 0) ? -1 : ((value > 0) ? 1 : 0); }
|
||||
|
||||
template<typename T> inline T square(T value) { return value * value; }
|
||||
|
||||
inline bool is_power_of_2(uint32 x) { return x && ((x & (x - 1U)) == 0U); }
|
||||
inline bool is_power_of_2(uint64 x) { return x && ((x & (x - 1U)) == 0U); }
|
||||
|
||||
template<typename T> inline T align_up_value(T x, uint alignment)
|
||||
{
|
||||
CRNLIB_ASSERT(is_power_of_2(alignment));
|
||||
uint q = static_cast<uint>(x);
|
||||
q = (q + alignment - 1) & (~(alignment - 1));
|
||||
return static_cast<T>(q);
|
||||
}
|
||||
|
||||
template<typename T> inline T align_down_value(T x, uint alignment)
|
||||
{
|
||||
CRNLIB_ASSERT(is_power_of_2(alignment));
|
||||
uint q = static_cast<uint>(x);
|
||||
q = q & (~(alignment - 1));
|
||||
return static_cast<T>(q);
|
||||
}
|
||||
|
||||
template<typename T> inline T get_align_up_value_delta(T x, uint alignment)
|
||||
{
|
||||
return align_up_value(x, alignment) - x;
|
||||
}
|
||||
|
||||
// From "Hackers Delight"
|
||||
inline uint32 next_pow2(uint32 val)
|
||||
{
|
||||
val--;
|
||||
val |= val >> 16;
|
||||
val |= val >> 8;
|
||||
val |= val >> 4;
|
||||
val |= val >> 2;
|
||||
val |= val >> 1;
|
||||
return val + 1;
|
||||
}
|
||||
|
||||
inline uint64 next_pow2(uint64 val)
|
||||
{
|
||||
val--;
|
||||
val |= val >> 32;
|
||||
val |= val >> 16;
|
||||
val |= val >> 8;
|
||||
val |= val >> 4;
|
||||
val |= val >> 2;
|
||||
val |= val >> 1;
|
||||
return val + 1;
|
||||
}
|
||||
|
||||
inline uint floor_log2i(uint v)
|
||||
{
|
||||
uint l = 0;
|
||||
while (v > 1U)
|
||||
{
|
||||
v >>= 1;
|
||||
l++;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
inline uint ceil_log2i(uint v)
|
||||
{
|
||||
uint l = floor_log2i(v);
|
||||
if ((l != cIntBits) && (v > (1U << l)))
|
||||
l++;
|
||||
return l;
|
||||
}
|
||||
|
||||
// Returns the total number of bits needed to encode v.
|
||||
inline uint total_bits(uint v)
|
||||
{
|
||||
uint l = 0;
|
||||
while (v > 0U)
|
||||
{
|
||||
v >>= 1;
|
||||
l++;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
// Actually counts the number of set bits, but hey
|
||||
inline uint bitmask_size(uint mask)
|
||||
{
|
||||
uint size = 0;
|
||||
while (mask)
|
||||
{
|
||||
mask &= (mask - 1U);
|
||||
size++;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
inline uint bitmask_ofs(uint mask)
|
||||
{
|
||||
if (!mask)
|
||||
return 0;
|
||||
uint ofs = 0;
|
||||
while ((mask & 1U) == 0)
|
||||
{
|
||||
mask >>= 1U;
|
||||
ofs++;
|
||||
}
|
||||
return ofs;
|
||||
}
|
||||
|
||||
// See Bit Twiddling Hacks (public domain)
|
||||
// http://www-graphics.stanford.edu/~seander/bithacks.html
|
||||
inline uint count_trailing_zero_bits(uint v)
|
||||
{
|
||||
uint c = 32; // c will be the number of zero bits on the right
|
||||
|
||||
static const unsigned int B[] = { 0x55555555, 0x33333333, 0x0F0F0F0F, 0x00FF00FF, 0x0000FFFF };
|
||||
static const unsigned int S[] = { 1, 2, 4, 8, 16 }; // Our Magic Binary Numbers
|
||||
|
||||
for (int i = 4; i >= 0; --i) // unroll for more speed
|
||||
{
|
||||
if (v & B[i])
|
||||
{
|
||||
v <<= S[i];
|
||||
c -= S[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (v)
|
||||
{
|
||||
c--;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
inline uint count_leading_zero_bits(uint v)
|
||||
{
|
||||
uint temp;
|
||||
uint result = 32U;
|
||||
|
||||
temp = (v >> 16U); if (temp) { result -= 16U; v = temp; }
|
||||
temp = (v >> 8U); if (temp) { result -= 8U; v = temp; }
|
||||
temp = (v >> 4U); if (temp) { result -= 4U; v = temp; }
|
||||
temp = (v >> 2U); if (temp) { result -= 2U; v = temp; }
|
||||
temp = (v >> 1U); if (temp) { result -= 1U; v = temp; }
|
||||
|
||||
if (v & 1U)
|
||||
result--;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline uint64 emulu(uint32 a, uint32 b)
|
||||
{
|
||||
#if defined(_M_IX86) && defined(_MSC_VER)
|
||||
return __emulu(a, b);
|
||||
#else
|
||||
return static_cast<uint64>(a) * static_cast<uint64>(b);
|
||||
#endif
|
||||
}
|
||||
|
||||
double compute_entropy(const uint8* p, uint n);
|
||||
|
||||
void compute_lower_pow2_dim(int& width, int& height);
|
||||
void compute_upper_pow2_dim(int& width, int& height);
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
|
||||
@@ -0,0 +1,565 @@
|
||||
// File: crn_matrix.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
|
||||
#include "crn_vec.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
template<class X, class Y, class Z> Z& matrix_mul_helper(Z& result, const X& lhs, const Y& rhs)
|
||||
{
|
||||
CRNLIB_ASSUME(Z::num_rows == X::num_rows);
|
||||
CRNLIB_ASSUME(Z::num_cols == Y::num_cols);
|
||||
CRNLIB_ASSUME(X::num_cols == Y::num_rows);
|
||||
CRNLIB_ASSERT((&result != &lhs) && (&result != &rhs));
|
||||
for (int r = 0; r < X::num_rows; r++)
|
||||
for (int c = 0; c < Y::num_cols; c++)
|
||||
{
|
||||
typename Z::scalar_type s = lhs(r, 0) * rhs(0, c);
|
||||
for (uint i = 1; i < X::num_cols; i++)
|
||||
s += lhs(r, i) * rhs(i, c);
|
||||
result(r, c) = s;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class X, class Y, class Z> Z& matrix_mul_helper_transpose_lhs(Z& result, const X& lhs, const Y& rhs)
|
||||
{
|
||||
CRNLIB_ASSUME(Z::num_rows == X::num_cols);
|
||||
CRNLIB_ASSUME(Z::num_cols == Y::num_cols);
|
||||
CRNLIB_ASSUME(X::num_rows == Y::num_rows);
|
||||
for (int r = 0; r < X::num_cols; r++)
|
||||
for (int c = 0; c < Y::num_cols; c++)
|
||||
{
|
||||
typename Z::scalar_type s = lhs(0, r) * rhs(0, c);
|
||||
for (uint i = 1; i < X::num_rows; i++)
|
||||
s += lhs(i, r) * rhs(i, c);
|
||||
result(r, c) = s;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class X, class Y, class Z> Z& matrix_mul_helper_transpose_rhs(Z& result, const X& lhs, const Y& rhs)
|
||||
{
|
||||
CRNLIB_ASSUME(Z::num_rows == X::num_rows);
|
||||
CRNLIB_ASSUME(Z::num_cols == Y::num_rows);
|
||||
CRNLIB_ASSUME(X::num_cols == Y::num_cols);
|
||||
for (int r = 0; r < X::num_rows; r++)
|
||||
for (int c = 0; c < Y::num_rows; c++)
|
||||
{
|
||||
typename Z::scalar_type s = lhs(r, 0) * rhs(c, 0);
|
||||
for (uint i = 1; i < X::num_cols; i++)
|
||||
s += lhs(r, i) * rhs(c, i);
|
||||
result(r, c) = s;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<uint R, uint C, typename T>
|
||||
class matrix
|
||||
{
|
||||
public:
|
||||
typedef T scalar_type;
|
||||
enum { num_rows = R, num_cols = C };
|
||||
|
||||
typedef vec<R, T> col_vec;
|
||||
typedef vec<(R > 1) ? (R - 1) : 0, T> subcol_vec;
|
||||
|
||||
typedef vec<C, T> row_vec;
|
||||
typedef vec<(C > 1) ? (C - 1) : 0, T> subrow_vec;
|
||||
|
||||
inline matrix() { }
|
||||
|
||||
inline matrix(eClear) { clear(); }
|
||||
|
||||
inline matrix(const T* p) { set(p); }
|
||||
|
||||
inline matrix(const matrix& other)
|
||||
{
|
||||
for (uint i = 0; i < R; i++)
|
||||
m_rows[i] = other.m_rows[i];
|
||||
}
|
||||
|
||||
inline matrix& operator= (const matrix& rhs)
|
||||
{
|
||||
if (this != &rhs)
|
||||
for (uint i = 0; i < R; i++)
|
||||
m_rows[i] = rhs.m_rows[i];
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline matrix(T val00, T val01,
|
||||
T val10, T val11)
|
||||
{
|
||||
set(val00, val01, val10, val11);
|
||||
}
|
||||
|
||||
inline matrix(T val00, T val01, T val02,
|
||||
T val10, T val11, T val12,
|
||||
T val20, T val21, T val22)
|
||||
{
|
||||
set(val00, val01, val02, val10, val11, val12, val20, val21, val22);
|
||||
}
|
||||
|
||||
inline matrix(T val00, T val01, T val02, T val03,
|
||||
T val10, T val11, T val12, T val13,
|
||||
T val20, T val21, T val22, T val23,
|
||||
T val30, T val31, T val32, T val33)
|
||||
{
|
||||
set(val00, val01, val02, val03, val10, val11, val12, val13, val20, val21, val22, val23, val30, val31, val32, val33);
|
||||
}
|
||||
|
||||
inline void set(const float* p)
|
||||
{
|
||||
for (uint i = 0; i < R; i++)
|
||||
{
|
||||
m_rows[i].set(p);
|
||||
p += C;
|
||||
}
|
||||
}
|
||||
|
||||
inline void set(T val00, T val01,
|
||||
T val10, T val11)
|
||||
{
|
||||
m_rows[0].set(val00, val01);
|
||||
if (R >= 2)
|
||||
{
|
||||
m_rows[1].set(val10, val11);
|
||||
|
||||
for (uint i = 2; i < R; i++)
|
||||
m_rows[i].clear();
|
||||
}
|
||||
}
|
||||
|
||||
inline void set(T val00, T val01, T val02,
|
||||
T val10, T val11, T val12,
|
||||
T val20, T val21, T val22)
|
||||
{
|
||||
m_rows[0].set(val00, val01, val02);
|
||||
if (R >= 2)
|
||||
{
|
||||
m_rows[1].set(val10, val11, val12);
|
||||
if (R >= 3)
|
||||
{
|
||||
m_rows[2].set(val20, val21, val22);
|
||||
|
||||
for (uint i = 3; i < R; i++)
|
||||
m_rows[i].clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void set(T val00, T val01, T val02, T val03,
|
||||
T val10, T val11, T val12, T val13,
|
||||
T val20, T val21, T val22, T val23,
|
||||
T val30, T val31, T val32, T val33)
|
||||
{
|
||||
m_rows[0].set(val00, val01, val02, val03);
|
||||
if (R >= 2)
|
||||
{
|
||||
m_rows[1].set(val10, val11, val12, val13);
|
||||
if (R >= 3)
|
||||
{
|
||||
m_rows[2].set(val20, val21, val22, val23);
|
||||
|
||||
if (R >= 4)
|
||||
{
|
||||
m_rows[3].set(val30, val31, val32, val33);
|
||||
|
||||
for (uint i = 4; i < R; i++)
|
||||
m_rows[i].clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline T operator() (uint r, uint c) const
|
||||
{
|
||||
CRNLIB_ASSERT((r < R) && (c < C));
|
||||
return m_rows[r][c];
|
||||
}
|
||||
|
||||
inline T& operator() (uint r, uint c)
|
||||
{
|
||||
CRNLIB_ASSERT((r < R) && (c < C));
|
||||
return m_rows[r][c];
|
||||
}
|
||||
|
||||
inline const row_vec& operator[] (uint r) const
|
||||
{
|
||||
CRNLIB_ASSERT(r < R);
|
||||
return m_rows[r];
|
||||
}
|
||||
|
||||
inline row_vec& operator[] (uint r)
|
||||
{
|
||||
CRNLIB_ASSERT(r < R);
|
||||
return m_rows[r];
|
||||
}
|
||||
|
||||
inline const row_vec& get_row (uint r) const { return (*this)[r]; }
|
||||
inline row_vec& get_row (uint r) { return (*this)[r]; }
|
||||
|
||||
inline col_vec get_col(uint c) const
|
||||
{
|
||||
CRNLIB_ASSERT(c < C);
|
||||
col_vec result;
|
||||
for (uint i = 0; i < R; i++)
|
||||
result[i] = m_rows[i][c];
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void set_col(uint c, const col_vec& col)
|
||||
{
|
||||
CRNLIB_ASSERT(c < C);
|
||||
for (uint i = 0; i < R; i++)
|
||||
m_rows[i][c] = col[i];
|
||||
}
|
||||
|
||||
inline void set_col(uint c, const subcol_vec& col)
|
||||
{
|
||||
CRNLIB_ASSERT(c < C);
|
||||
for (uint i = 0; i < (R - 1); i++)
|
||||
m_rows[i][c] = col[i];
|
||||
|
||||
m_rows[R - 1][c] = 0.0f;
|
||||
}
|
||||
|
||||
inline const row_vec& get_translate() const
|
||||
{
|
||||
return m_rows[R - 1];
|
||||
}
|
||||
|
||||
inline matrix& set_translate(const row_vec& r)
|
||||
{
|
||||
m_rows[R - 1] = r;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline matrix& set_translate(const subrow_vec& r)
|
||||
{
|
||||
m_rows[R - 1] = row_vec(r).as_point();
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline const T* get_ptr() const { return reinterpret_cast<const T*>(&m_rows[0]); }
|
||||
inline T* get_ptr() { return reinterpret_cast< T*>(&m_rows[0]); }
|
||||
|
||||
inline matrix& operator+= (const matrix& other)
|
||||
{
|
||||
for (uint i = 0; i < R; i++)
|
||||
m_rows[i] += other.m_rows[i];
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline matrix& operator-= (const matrix& other)
|
||||
{
|
||||
for (uint i = 0; i < R; i++)
|
||||
m_rows[i] -= other.m_rows[i];
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline matrix& operator*= (T val)
|
||||
{
|
||||
for (uint i = 0; i < R; i++)
|
||||
m_rows[i] *= val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline matrix& operator/= (T val)
|
||||
{
|
||||
for (uint i = 0; i < R; i++)
|
||||
m_rows[i] /= val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline matrix& operator*= (const matrix& other)
|
||||
{
|
||||
matrix result;
|
||||
matrix_mul_helper(result, *this, other);
|
||||
*this = result;
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend inline matrix operator+ (const matrix& lhs, const matrix& rhs)
|
||||
{
|
||||
matrix result;
|
||||
for (uint i = 0; i < R; i++)
|
||||
result[i] = lhs.m_rows[i] + rhs.m_rows[i];
|
||||
return result;
|
||||
}
|
||||
|
||||
friend inline matrix operator- (const matrix& lhs, const matrix& rhs)
|
||||
{
|
||||
matrix result;
|
||||
for (uint i = 0; i < R; i++)
|
||||
result[i] = lhs.m_rows[i] - rhs.m_rows[i];
|
||||
return result;
|
||||
}
|
||||
|
||||
friend inline matrix operator* (const matrix& lhs, T val)
|
||||
{
|
||||
matrix result;
|
||||
for (uint i = 0; i < R; i++)
|
||||
result[i] = lhs.m_rows[i] * val;
|
||||
return result;
|
||||
}
|
||||
|
||||
friend inline matrix operator/ (const matrix& lhs, T val)
|
||||
{
|
||||
matrix result;
|
||||
for (uint i = 0; i < R; i++)
|
||||
result[i] = lhs.m_rows[i] / val;
|
||||
return result;
|
||||
}
|
||||
|
||||
friend inline matrix operator* (T val, const matrix& rhs)
|
||||
{
|
||||
matrix result;
|
||||
for (uint i = 0; i < R; i++)
|
||||
result[i] = val * rhs.m_rows[i];
|
||||
return result;
|
||||
}
|
||||
|
||||
friend inline matrix operator* (const matrix& lhs, const matrix& rhs)
|
||||
{
|
||||
matrix result;
|
||||
return matrix_mul_helper(result, lhs, rhs);
|
||||
}
|
||||
|
||||
friend inline row_vec operator* (const col_vec& a, const matrix& b)
|
||||
{
|
||||
return transform(a, b);
|
||||
}
|
||||
|
||||
inline matrix operator+ () const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline matrix operator- () const
|
||||
{
|
||||
matrix result;
|
||||
for (uint i = 0; i < R; i++)
|
||||
result[i] = -m_rows[i];
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void clear(void)
|
||||
{
|
||||
for (uint i = 0; i < R; i++)
|
||||
m_rows[i].clear();
|
||||
}
|
||||
|
||||
inline void set_zero_matrix()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
inline void set_identity_matrix()
|
||||
{
|
||||
for (uint i = 0; i < R; i++)
|
||||
{
|
||||
m_rows[i].clear();
|
||||
m_rows[i][i] = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
inline matrix& set_scale_matrix(float s)
|
||||
{
|
||||
clear();
|
||||
for (int i = 0; i < (R - 1); i++)
|
||||
m_rows[i][i] = s;
|
||||
m_rows[R - 1][C - 1] = 1.0f;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline matrix& set_scale_matrix(const row_vec& s)
|
||||
{
|
||||
clear();
|
||||
for (uint i = 0; i < R; i++)
|
||||
m_rows[i][i] = s[i];
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline matrix& set_translate_matrix(const row_vec& s)
|
||||
{
|
||||
set_identity_matrix();
|
||||
set_translate(s);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline matrix& set_translate_matrix(float x, float y)
|
||||
{
|
||||
set_identity_matrix();
|
||||
set_translate(row_vec(x, y).as_point());
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline matrix& set_translate_matrix(float x, float y, float z)
|
||||
{
|
||||
set_identity_matrix();
|
||||
set_translate(row_vec(x, y, z).as_point());
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline matrix get_transposed(void) const
|
||||
{
|
||||
matrix result;
|
||||
for (uint i = 0; i < R; i++)
|
||||
for (uint j = 0; j < C; j++)
|
||||
result.m_rows[i][j] = m_rows[j][i];
|
||||
return result;
|
||||
}
|
||||
|
||||
inline matrix& transpose_in_place(void)
|
||||
{
|
||||
matrix result;
|
||||
for (uint i = 0; i < R; i++)
|
||||
for (uint j = 0; j < C; j++)
|
||||
result.m_rows[i][j] = m_rows[j][i];
|
||||
*this = result;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// This method transforms a column vec by a matrix (D3D-style).
|
||||
static inline row_vec transform(const col_vec& a, const matrix& b)
|
||||
{
|
||||
row_vec result(b[0] * a[0]);
|
||||
for (uint r = 1; r < R; r++)
|
||||
result += b[r] * a[r];
|
||||
return result;
|
||||
}
|
||||
|
||||
// This method transforms a column vec by a matrix. Last component of vec is assumed to be 1.
|
||||
static inline row_vec transform_point(const col_vec& a, const matrix& b)
|
||||
{
|
||||
row_vec result(0);
|
||||
for (int r = 0; r < (R - 1); r++)
|
||||
result += b[r] * a[r];
|
||||
result += b[R - 1];
|
||||
return result;
|
||||
}
|
||||
|
||||
// This method transforms a column vec by a matrix. Last component of vec is assumed to be 0.
|
||||
static inline row_vec transform_vector(const col_vec& a, const matrix& b)
|
||||
{
|
||||
row_vec result(0);
|
||||
for (int r = 0; r < (R - 1); r++)
|
||||
result += b[r] * a[r];
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline subcol_vec transform_point(const subcol_vec& a, const matrix& b)
|
||||
{
|
||||
subcol_vec result(0);
|
||||
for (int r = 0; r < R; r++)
|
||||
{
|
||||
const T s = (r < subcol_vec::num_elements) ? a[r] : 1.0f;
|
||||
for (int c = 0; c < (C - 1); c++)
|
||||
result[c] += b[r][c] * s;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline subcol_vec transform_vector(const subcol_vec& a, const matrix& b)
|
||||
{
|
||||
subcol_vec result(0);
|
||||
for (int r = 0; r < (R - 1); r++)
|
||||
{
|
||||
const T s = a[r];
|
||||
for (int c = 0; c < (C - 1); c++)
|
||||
result[c] += b[r][c] * s;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// This method transforms a column vec by the transpose of a matrix.
|
||||
static inline col_vec transform_transposed(const matrix& b, const col_vec& a)
|
||||
{
|
||||
CRNLIB_ASSUME(R == C);
|
||||
col_vec result;
|
||||
for (uint r = 0; r < R; r++)
|
||||
result[r] = b[r] * a;
|
||||
return result;
|
||||
}
|
||||
|
||||
// This method transforms a column vec by the transpose of a matrix. Last component of vec is assumed to be 0.
|
||||
static inline col_vec transform_vector_transposed(const matrix& b, const col_vec& a)
|
||||
{
|
||||
CRNLIB_ASSUME(R == C);
|
||||
col_vec result;
|
||||
for (uint r = 0; r < R; r++)
|
||||
{
|
||||
T s = 0;
|
||||
for (uint c = 0; c < (C - 1); c++)
|
||||
s += b[r][c] * a[c];
|
||||
|
||||
result[r] = s;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// This method transforms a matrix by a row vector (OGL style).
|
||||
static inline col_vec transform(const matrix& b, const row_vec& a)
|
||||
{
|
||||
col_vec result;
|
||||
for (int r = 0; r < R; r++)
|
||||
result[r] = b[r] * a;
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline matrix& multiply(matrix& result, const matrix& lhs, const matrix& rhs)
|
||||
{
|
||||
return matrix_mul_helper(result, lhs, rhs);
|
||||
}
|
||||
|
||||
static inline matrix make_scale_matrix(float s)
|
||||
{
|
||||
return matrix().set_scale_matrix(s);
|
||||
}
|
||||
|
||||
static inline matrix make_scale_matrix(const row_vec& s)
|
||||
{
|
||||
return matrix().set_scale_matrix(s);
|
||||
}
|
||||
|
||||
static inline matrix make_scale_matrix(float x, float y)
|
||||
{
|
||||
CRNLIB_ASSUME(R >= 3 && C >= 3);
|
||||
matrix result;
|
||||
result.clear();
|
||||
result.m_rows[0][0] = x;
|
||||
result.m_rows[1][1] = y;
|
||||
result.m_rows[2][2] = 1.0f;
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline matrix make_scale_matrix(float x, float y, float z)
|
||||
{
|
||||
CRNLIB_ASSUME(R >= 4 && C >= 4);
|
||||
matrix result;
|
||||
result.clear();
|
||||
result.m_rows[0][0] = x;
|
||||
result.m_rows[1][1] = y;
|
||||
result.m_rows[2][2] = z;
|
||||
result.m_rows[3][3] = 1.0f;
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
row_vec m_rows[R];
|
||||
};
|
||||
|
||||
typedef matrix<2, 2, float> matrix22F;
|
||||
typedef matrix<2, 2, double> matrix22D;
|
||||
|
||||
typedef matrix<3, 3, float> matrix33F;
|
||||
typedef matrix<3, 3, double> matrix33D;
|
||||
|
||||
typedef matrix<4, 4, float> matrix44F;
|
||||
typedef matrix<4, 4, double> matrix44D;
|
||||
|
||||
typedef matrix<8, 8, float> matrix88F;
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,289 @@
|
||||
// File: crn_mem.cpp
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#include "crn_core.h"
|
||||
#include "crn_spinlock.h"
|
||||
#include "crn_console.h"
|
||||
#include "../inc/crnlib.h"
|
||||
#include <malloc.h>
|
||||
#include "crn_winhdr.h"
|
||||
|
||||
#define CRNLIB_MEM_STATS 0
|
||||
|
||||
#ifndef CRNLIB_USE_WIN32_API
|
||||
#define _msize malloc_usable_size
|
||||
#endif
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
#if CRNLIB_MEM_STATS
|
||||
#if CRNLIB_64BIT_POINTERS
|
||||
typedef LONGLONG mem_stat_t;
|
||||
#define CRNLIB_MEM_COMPARE_EXCHANGE InterlockedCompareExchange64
|
||||
#else
|
||||
typedef LONG mem_stat_t;
|
||||
#define CRNLIB_MEM_COMPARE_EXCHANGE InterlockedCompareExchange
|
||||
#endif
|
||||
|
||||
static volatile mem_stat_t g_total_blocks;
|
||||
static volatile mem_stat_t g_total_allocated;
|
||||
static volatile mem_stat_t g_max_allocated;
|
||||
|
||||
static mem_stat_t update_total_allocated(int block_delta, mem_stat_t byte_delta)
|
||||
{
|
||||
mem_stat_t cur_total_blocks;
|
||||
for ( ; ; )
|
||||
{
|
||||
cur_total_blocks = (mem_stat_t)g_total_blocks;
|
||||
mem_stat_t new_total_blocks = static_cast<mem_stat_t>(cur_total_blocks + block_delta);
|
||||
CRNLIB_ASSERT(new_total_blocks >= 0);
|
||||
if (CRNLIB_MEM_COMPARE_EXCHANGE(&g_total_blocks, new_total_blocks, cur_total_blocks) == cur_total_blocks)
|
||||
break;
|
||||
}
|
||||
|
||||
mem_stat_t cur_total_allocated, new_total_allocated;
|
||||
for ( ; ; )
|
||||
{
|
||||
cur_total_allocated = g_total_allocated;
|
||||
new_total_allocated = static_cast<mem_stat_t>(cur_total_allocated + byte_delta);
|
||||
CRNLIB_ASSERT(new_total_allocated >= 0);
|
||||
if (CRNLIB_MEM_COMPARE_EXCHANGE(&g_total_allocated, new_total_allocated, cur_total_allocated) == cur_total_allocated)
|
||||
break;
|
||||
}
|
||||
for ( ; ; )
|
||||
{
|
||||
mem_stat_t cur_max_allocated = g_max_allocated;
|
||||
mem_stat_t new_max_allocated = CRNLIB_MAX(new_total_allocated, cur_max_allocated);
|
||||
if (CRNLIB_MEM_COMPARE_EXCHANGE(&g_max_allocated, new_max_allocated, cur_max_allocated) == cur_max_allocated)
|
||||
break;
|
||||
}
|
||||
return new_total_allocated;
|
||||
}
|
||||
#endif // CRNLIB_MEM_STATS
|
||||
|
||||
static void* crnlib_default_realloc(void* p, size_t size, size_t* pActual_size, bool movable, void* pUser_data)
|
||||
{
|
||||
pUser_data;
|
||||
|
||||
void* p_new;
|
||||
|
||||
if (!p)
|
||||
{
|
||||
p_new = ::malloc(size);
|
||||
CRNLIB_ASSERT( (reinterpret_cast<ptr_bits_t>(p_new) & (CRNLIB_MIN_ALLOC_ALIGNMENT - 1)) == 0 );
|
||||
|
||||
if (pActual_size)
|
||||
*pActual_size = p_new ? ::_msize(p_new) : 0;
|
||||
}
|
||||
else if (!size)
|
||||
{
|
||||
::free(p);
|
||||
p_new = NULL;
|
||||
|
||||
if (pActual_size)
|
||||
*pActual_size = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
void* p_final_block = p;
|
||||
#ifdef WIN32
|
||||
p_new = ::_expand(p, size);
|
||||
#else
|
||||
|
||||
p_new = NULL;
|
||||
#endif
|
||||
|
||||
if (p_new)
|
||||
{
|
||||
CRNLIB_ASSERT( (reinterpret_cast<ptr_bits_t>(p_new) & (CRNLIB_MIN_ALLOC_ALIGNMENT - 1)) == 0 );
|
||||
p_final_block = p_new;
|
||||
}
|
||||
else if (movable)
|
||||
{
|
||||
p_new = ::realloc(p, size);
|
||||
|
||||
if (p_new)
|
||||
{
|
||||
CRNLIB_ASSERT( (reinterpret_cast<ptr_bits_t>(p_new) & (CRNLIB_MIN_ALLOC_ALIGNMENT - 1)) == 0 );
|
||||
p_final_block = p_new;
|
||||
}
|
||||
}
|
||||
|
||||
if (pActual_size)
|
||||
*pActual_size = ::_msize(p_final_block);
|
||||
}
|
||||
|
||||
return p_new;
|
||||
}
|
||||
|
||||
static size_t crnlib_default_msize(void* p, void* pUser_data)
|
||||
{
|
||||
pUser_data;
|
||||
return p ? _msize(p) : 0;
|
||||
}
|
||||
|
||||
static crn_realloc_func g_pRealloc = crnlib_default_realloc;
|
||||
static crn_msize_func g_pMSize = crnlib_default_msize;
|
||||
static void* g_pUser_data;
|
||||
|
||||
void crnlib_mem_error(const char* p_msg)
|
||||
{
|
||||
crnlib_assert(p_msg, __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
void* crnlib_malloc(size_t size, size_t* pActual_size)
|
||||
{
|
||||
size = (size + sizeof(uint32) - 1U) & ~(sizeof(uint32) - 1U);
|
||||
if (!size)
|
||||
size = sizeof(uint32);
|
||||
|
||||
if (size > CRNLIB_MAX_POSSIBLE_BLOCK_SIZE)
|
||||
{
|
||||
crnlib_mem_error("crnlib_malloc: size too big");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t actual_size = size;
|
||||
uint8* p_new = static_cast<uint8*>((*g_pRealloc)(NULL, size, &actual_size, true, g_pUser_data));
|
||||
|
||||
if (pActual_size)
|
||||
*pActual_size = actual_size;
|
||||
|
||||
if ((!p_new) || (actual_size < size))
|
||||
{
|
||||
crnlib_mem_error("crnlib_malloc: out of memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CRNLIB_ASSERT((reinterpret_cast<ptr_bits_t>(p_new) & (CRNLIB_MIN_ALLOC_ALIGNMENT - 1)) == 0);
|
||||
|
||||
#if CRNLIB_MEM_STATS
|
||||
CRNLIB_ASSERT((*g_pMSize)(p_new, g_pUser_data) == actual_size);
|
||||
update_total_allocated(1, static_cast<mem_stat_t>(actual_size));
|
||||
#endif
|
||||
|
||||
return p_new;
|
||||
}
|
||||
|
||||
void* crnlib_realloc(void* p, size_t size, size_t* pActual_size, bool movable)
|
||||
{
|
||||
if ((ptr_bits_t)p & (CRNLIB_MIN_ALLOC_ALIGNMENT - 1))
|
||||
{
|
||||
crnlib_mem_error("crnlib_realloc: bad ptr");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (size > CRNLIB_MAX_POSSIBLE_BLOCK_SIZE)
|
||||
{
|
||||
crnlib_mem_error("crnlib_malloc: size too big");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if CRNLIB_MEM_STATS
|
||||
size_t cur_size = p ? (*g_pMSize)(p, g_pUser_data) : 0;
|
||||
CRNLIB_ASSERT(!p || (cur_size >= sizeof(uint32)));
|
||||
#endif
|
||||
if ((size) && (size < sizeof(uint32)))
|
||||
size = sizeof(uint32);
|
||||
|
||||
size_t actual_size = size;
|
||||
void* p_new = (*g_pRealloc)(p, size, &actual_size, movable, g_pUser_data);
|
||||
|
||||
if (pActual_size)
|
||||
*pActual_size = actual_size;
|
||||
|
||||
CRNLIB_ASSERT((reinterpret_cast<ptr_bits_t>(p_new) & (CRNLIB_MIN_ALLOC_ALIGNMENT - 1)) == 0);
|
||||
|
||||
#if CRNLIB_MEM_STATS
|
||||
CRNLIB_ASSERT(!p_new || ((*g_pMSize)(p_new, g_pUser_data) == actual_size));
|
||||
|
||||
int num_new_blocks = 0;
|
||||
if (p)
|
||||
{
|
||||
if (!p_new)
|
||||
num_new_blocks = -1;
|
||||
}
|
||||
else if (p_new)
|
||||
{
|
||||
num_new_blocks = 1;
|
||||
}
|
||||
update_total_allocated(num_new_blocks, static_cast<mem_stat_t>(actual_size) - static_cast<mem_stat_t>(cur_size));
|
||||
#endif
|
||||
|
||||
return p_new;
|
||||
}
|
||||
|
||||
void* crnlib_calloc(size_t count, size_t size, size_t* pActual_size)
|
||||
{
|
||||
size_t total = count * size;
|
||||
void *p = crnlib_malloc(total, pActual_size);
|
||||
if (p) memset(p, 0, total);
|
||||
return p;
|
||||
}
|
||||
|
||||
void crnlib_free(void* p)
|
||||
{
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
if (reinterpret_cast<ptr_bits_t>(p) & (CRNLIB_MIN_ALLOC_ALIGNMENT - 1))
|
||||
{
|
||||
crnlib_mem_error("crnlib_free: bad ptr");
|
||||
return;
|
||||
}
|
||||
|
||||
#if CRNLIB_MEM_STATS
|
||||
size_t cur_size = (*g_pMSize)(p, g_pUser_data);
|
||||
CRNLIB_ASSERT(cur_size >= sizeof(uint32));
|
||||
update_total_allocated(-1, -static_cast<mem_stat_t>(cur_size));
|
||||
#endif
|
||||
|
||||
(*g_pRealloc)(p, 0, NULL, true, g_pUser_data);
|
||||
}
|
||||
|
||||
size_t crnlib_msize(void* p)
|
||||
{
|
||||
if (!p)
|
||||
return 0;
|
||||
|
||||
if (reinterpret_cast<ptr_bits_t>(p) & (CRNLIB_MIN_ALLOC_ALIGNMENT - 1))
|
||||
{
|
||||
crnlib_mem_error("crnlib_msize: bad ptr");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (*g_pMSize)(p, g_pUser_data);
|
||||
}
|
||||
|
||||
void crnlib_print_mem_stats()
|
||||
{
|
||||
#if CRNLIB_MEM_STATS
|
||||
if (console::is_initialized())
|
||||
{
|
||||
console::debug(L"crnlib_print_mem_stats:");
|
||||
console::debug(L"Current blocks: %u, allocated: %I64u, max ever allocated: %I64i", g_total_blocks, (int64)g_total_allocated, (int64)g_max_allocated);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("crnlib_print_mem_stats:\n");
|
||||
printf("Current blocks: %u, allocated: %I64u, max ever allocated: %I64i\n", g_total_blocks, (int64)g_total_allocated, (int64)g_max_allocated);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
|
||||
void crn_set_memory_callbacks(crn_realloc_func pRealloc, crn_msize_func pMSize, void* pUser_data)
|
||||
{
|
||||
if ((!pRealloc) || (!pMSize))
|
||||
{
|
||||
crnlib::g_pRealloc = crnlib::crnlib_default_realloc;
|
||||
crnlib::g_pMSize = crnlib::crnlib_default_msize;
|
||||
crnlib::g_pUser_data = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
crnlib::g_pRealloc = pRealloc;
|
||||
crnlib::g_pMSize = pMSize;
|
||||
crnlib::g_pUser_data = pUser_data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
// File: crn_mem.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
|
||||
#ifndef CRNLIB_MIN_ALLOC_ALIGNMENT
|
||||
#define CRNLIB_MIN_ALLOC_ALIGNMENT sizeof(size_t) * 2
|
||||
#endif
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
#if CRNLIB_64BIT_POINTERS
|
||||
const uint64 CRNLIB_MAX_POSSIBLE_BLOCK_SIZE = 0x400000000ULL;
|
||||
#else
|
||||
const uint32 CRNLIB_MAX_POSSIBLE_BLOCK_SIZE = 0x7FFF0000U;
|
||||
#endif
|
||||
|
||||
void* crnlib_malloc(size_t size, size_t* pActual_size = NULL);
|
||||
void* crnlib_realloc(void* p, size_t size, size_t* pActual_size = NULL, bool movable = true);
|
||||
void* crnlib_calloc(size_t count, size_t size, size_t* pActual_size = NULL);
|
||||
void crnlib_free(void* p);
|
||||
size_t crnlib_msize(void* p);
|
||||
void crnlib_print_mem_stats();
|
||||
void crnlib_mem_error(const char* p_msg);
|
||||
|
||||
// omfg - there must be a better way
|
||||
|
||||
template<typename T>
|
||||
inline T* crnlib_new()
|
||||
{
|
||||
T* p = static_cast<T*>(crnlib_malloc(sizeof(T)));
|
||||
if (CRNLIB_IS_SCALAR_TYPE(T))
|
||||
return p;
|
||||
return helpers::construct(p);
|
||||
}
|
||||
|
||||
template<typename T, typename A>
|
||||
inline T* crnlib_new(const A& init0)
|
||||
{
|
||||
T* p = static_cast<T*>(crnlib_malloc(sizeof(T)));
|
||||
return new (static_cast<void*>(p)) T(init0);
|
||||
}
|
||||
|
||||
template<typename T, typename A>
|
||||
inline T* crnlib_new(A& init0)
|
||||
{
|
||||
T* p = static_cast<T*>(crnlib_malloc(sizeof(T)));
|
||||
return new (static_cast<void*>(p)) T(init0);
|
||||
}
|
||||
|
||||
template<typename T, typename A, typename B>
|
||||
inline T* crnlib_new(const A& init0, const B& init1)
|
||||
{
|
||||
T* p = static_cast<T*>(crnlib_malloc(sizeof(T)));
|
||||
return new (static_cast<void*>(p)) T(init0, init1);
|
||||
}
|
||||
|
||||
template<typename T, typename A, typename B, typename C>
|
||||
inline T* crnlib_new(const A& init0, const B& init1, const C& init2)
|
||||
{
|
||||
T* p = static_cast<T*>(crnlib_malloc(sizeof(T)));
|
||||
return new (static_cast<void*>(p)) T(init0, init1, init2);
|
||||
}
|
||||
|
||||
template<typename T, typename A, typename B, typename C, typename D>
|
||||
inline T* crnlib_new(const A& init0, const B& init1, const C& init2, const D& init3)
|
||||
{
|
||||
T* p = static_cast<T*>(crnlib_malloc(sizeof(T)));
|
||||
return new (static_cast<void*>(p)) T(init0, init1, init2, init3);
|
||||
}
|
||||
|
||||
template<typename T, typename A, typename B, typename C, typename D, typename E>
|
||||
inline T* crnlib_new(const A& init0, const B& init1, const C& init2, const D& init3, const E& init4)
|
||||
{
|
||||
T* p = static_cast<T*>(crnlib_malloc(sizeof(T)));
|
||||
return new (static_cast<void*>(p)) T(init0, init1, init2, init3, init4);
|
||||
}
|
||||
|
||||
template<typename T, typename A, typename B, typename C, typename D, typename E, typename F>
|
||||
inline T* crnlib_new(const A& init0, const B& init1, const C& init2, const D& init3, const E& init4, const F& init5)
|
||||
{
|
||||
T* p = static_cast<T*>(crnlib_malloc(sizeof(T)));
|
||||
return new (static_cast<void*>(p)) T(init0, init1, init2, init3, init4, init5);
|
||||
}
|
||||
|
||||
template<typename T, typename A, typename B, typename C, typename D, typename E, typename F, typename G>
|
||||
inline T* crnlib_new(const A& init0, const B& init1, const C& init2, const D& init3, const E& init4, const F& init5, const G& init6)
|
||||
{
|
||||
T* p = static_cast<T*>(crnlib_malloc(sizeof(T)));
|
||||
return new (static_cast<void*>(p)) T(init0, init1, init2, init3, init4, init5, init6);
|
||||
}
|
||||
|
||||
template<typename T, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H>
|
||||
inline T* crnlib_new(const A& init0, const B& init1, const C& init2, const D& init3, const E& init4, const F& init5, const G& init6, const H& init7)
|
||||
{
|
||||
T* p = static_cast<T*>(crnlib_malloc(sizeof(T)));
|
||||
return new (static_cast<void*>(p)) T(init0, init1, init2, init3, init4, init5, init6, init7);
|
||||
}
|
||||
|
||||
template<typename T, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I>
|
||||
inline T* crnlib_new(const A& init0, const B& init1, const C& init2, const D& init3, const E& init4, const F& init5, const G& init6, const H& init7, const I& init8)
|
||||
{
|
||||
T* p = static_cast<T*>(crnlib_malloc(sizeof(T)));
|
||||
return new (static_cast<void*>(p)) T(init0, init1, init2, init3, init4, init5, init6, init7, init8);
|
||||
}
|
||||
|
||||
template<typename T, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J>
|
||||
inline T* crnlib_new(const A& init0, const B& init1, const C& init2, const D& init3, const E& init4, const F& init5, const G& init6, const H& init7, const I& init8, const J& init9)
|
||||
{
|
||||
T* p = static_cast<T*>(crnlib_malloc(sizeof(T)));
|
||||
return new (static_cast<void*>(p)) T(init0, init1, init2, init3, init4, init5, init6, init7, init8, init9);
|
||||
}
|
||||
|
||||
template<typename T, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K>
|
||||
inline T* crnlib_new(const A& init0, const B& init1, const C& init2, const D& init3, const E& init4, const F& init5, const G& init6, const H& init7, const I& init8, const J& init9, const K& init10)
|
||||
{
|
||||
T* p = static_cast<T*>(crnlib_malloc(sizeof(T)));
|
||||
return new (static_cast<void*>(p)) T(init0, init1, init2, init3, init4, init5, init6, init7, init8, init9, init10);
|
||||
}
|
||||
|
||||
template<typename T, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K, typename L>
|
||||
inline T* crnlib_new(const A& init0, const B& init1, const C& init2, const D& init3, const E& init4, const F& init5, const G& init6, const H& init7, const I& init8, const J& init9, const K& init10, const L& init11)
|
||||
{
|
||||
T* p = static_cast<T*>(crnlib_malloc(sizeof(T)));
|
||||
return new (static_cast<void*>(p)) T(init0, init1, init2, init3, init4, init5, init6, init7, init8, init9, init10, init11);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T* crnlib_new_array(uint32 num)
|
||||
{
|
||||
if (!num) num = 1;
|
||||
|
||||
uint64 total = CRNLIB_MIN_ALLOC_ALIGNMENT + sizeof(T) * num;
|
||||
if (total > CRNLIB_MAX_POSSIBLE_BLOCK_SIZE)
|
||||
{
|
||||
crnlib_mem_error("crnlib_new_array: Array too large!");
|
||||
return NULL;
|
||||
}
|
||||
uint8* q = static_cast<uint8*>(crnlib_malloc(static_cast<size_t>(total)));
|
||||
|
||||
T* p = reinterpret_cast<T*>(q + CRNLIB_MIN_ALLOC_ALIGNMENT);
|
||||
|
||||
reinterpret_cast<uint32*>(p)[-1] = num;
|
||||
reinterpret_cast<uint32*>(p)[-2] = ~num;
|
||||
|
||||
if (!CRNLIB_IS_SCALAR_TYPE(T))
|
||||
{
|
||||
helpers::construct_array(p, num);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void crnlib_delete(T* p)
|
||||
{
|
||||
if (p)
|
||||
{
|
||||
if (!CRNLIB_IS_SCALAR_TYPE(T))
|
||||
{
|
||||
helpers::destruct(p);
|
||||
}
|
||||
crnlib_free(p);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void crnlib_delete_array(T* p)
|
||||
{
|
||||
if (p)
|
||||
{
|
||||
const uint32 num = reinterpret_cast<uint32*>(p)[-1];
|
||||
const uint32 num_check = reinterpret_cast<uint32*>(p)[-2];
|
||||
CRNLIB_ASSERT(num && (num == ~num_check));
|
||||
if (num == ~num_check)
|
||||
{
|
||||
if (!CRNLIB_IS_SCALAR_TYPE(T))
|
||||
{
|
||||
helpers::destruct_array(p, num);
|
||||
}
|
||||
|
||||
crnlib_free(reinterpret_cast<uint8*>(p) - CRNLIB_MIN_ALLOC_ALIGNMENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,40 @@
|
||||
// File: crn_mutex.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
class mutex
|
||||
{
|
||||
mutex(const mutex&);
|
||||
mutex& operator= (const mutex&);
|
||||
|
||||
public:
|
||||
mutex(unsigned int spin_count = 0);
|
||||
~mutex();
|
||||
void lock();
|
||||
void unlock();
|
||||
void set_spin_count(unsigned int count);
|
||||
|
||||
private:
|
||||
int m_buf[12];
|
||||
|
||||
#ifdef CRNLIB_BUILD_DEBUG
|
||||
unsigned int m_lock_count;
|
||||
#endif
|
||||
};
|
||||
|
||||
class scoped_mutex
|
||||
{
|
||||
scoped_mutex(const scoped_mutex&);
|
||||
scoped_mutex& operator= (const scoped_mutex&);
|
||||
|
||||
public:
|
||||
inline scoped_mutex(mutex& m) : m_mutex(m) { m_mutex.lock(); }
|
||||
inline ~scoped_mutex() { m_mutex.unlock(); }
|
||||
|
||||
private:
|
||||
mutex& m_mutex;
|
||||
};
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,91 @@
|
||||
// File: crn_packed_uint
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
template<unsigned int N>
|
||||
struct packed_uint
|
||||
{
|
||||
inline packed_uint() { }
|
||||
|
||||
inline packed_uint(unsigned int val) { *this = val; }
|
||||
|
||||
inline packed_uint(const packed_uint& other) { *this = other; }
|
||||
|
||||
inline packed_uint& operator= (const packed_uint& rhs)
|
||||
{
|
||||
if (this != &rhs)
|
||||
memcpy(m_buf, rhs.m_buf, sizeof(m_buf));
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline packed_uint& operator= (unsigned int val)
|
||||
{
|
||||
#ifdef CRNLIB_BUILD_DEBUG
|
||||
if (N == 1)
|
||||
{
|
||||
CRNLIB_ASSERT(val <= 0xFFU);
|
||||
}
|
||||
else if (N == 2)
|
||||
{
|
||||
CRNLIB_ASSERT(val <= 0xFFFFU);
|
||||
}
|
||||
else if (N == 3)
|
||||
{
|
||||
CRNLIB_ASSERT(val <= 0xFFFFFFU);
|
||||
}
|
||||
#endif
|
||||
|
||||
val <<= (8U * (4U - N));
|
||||
|
||||
for (unsigned int i = 0; i < N; i++)
|
||||
{
|
||||
m_buf[i] = static_cast<unsigned char>(val >> 24U);
|
||||
val <<= 8U;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline operator unsigned int() const
|
||||
{
|
||||
switch (N)
|
||||
{
|
||||
case 1: return m_buf[0];
|
||||
case 2: return (m_buf[0] << 8U) | m_buf[1];
|
||||
case 3: return (m_buf[0] << 16U) | (m_buf[1] << 8U) | (m_buf[2]);
|
||||
default: return (m_buf[0] << 24U) | (m_buf[1] << 16U) | (m_buf[2] << 8U) | (m_buf[3]);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char m_buf[N];
|
||||
};
|
||||
template<typename T>
|
||||
class packed_value
|
||||
{
|
||||
public:
|
||||
packed_value() { }
|
||||
packed_value(T val) { *this = val; }
|
||||
|
||||
inline operator T() const
|
||||
{
|
||||
T result = 0;
|
||||
for (int i = sizeof(T) - 1; i >= 0; i--)
|
||||
result = static_cast<T>((result << 8) | m_bytes[i]);
|
||||
return result;
|
||||
}
|
||||
packed_value& operator= (T val)
|
||||
{
|
||||
for (int i = 0; i < sizeof(T); i++)
|
||||
{
|
||||
m_bytes[i] = static_cast<uint8>(val);
|
||||
val >>= 8;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
private:
|
||||
uint8 m_bytes[sizeof(T)];
|
||||
};
|
||||
} // namespace crnlib
|
||||
|
||||
@@ -0,0 +1,317 @@
|
||||
// File: crn_pixel_format.cpp
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#include "crn_core.h"
|
||||
#include "crn_pixel_format.h"
|
||||
#include "crn_image.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
namespace pixel_format_helpers
|
||||
{
|
||||
const pixel_format g_all_pixel_formats[] =
|
||||
{
|
||||
PIXEL_FMT_DXT1,
|
||||
PIXEL_FMT_DXT2,
|
||||
PIXEL_FMT_DXT3,
|
||||
PIXEL_FMT_DXT4,
|
||||
PIXEL_FMT_DXT5,
|
||||
PIXEL_FMT_3DC,
|
||||
PIXEL_FMT_DXN,
|
||||
PIXEL_FMT_DXT5A,
|
||||
PIXEL_FMT_DXT5_CCxY,
|
||||
PIXEL_FMT_DXT5_xGxR,
|
||||
PIXEL_FMT_DXT5_xGBR,
|
||||
PIXEL_FMT_DXT5_AGBR,
|
||||
PIXEL_FMT_DXT1A,
|
||||
PIXEL_FMT_R8G8B8,
|
||||
PIXEL_FMT_L8,
|
||||
PIXEL_FMT_A8,
|
||||
PIXEL_FMT_A8L8,
|
||||
PIXEL_FMT_A8R8G8B8
|
||||
};
|
||||
|
||||
uint get_num_formats()
|
||||
{
|
||||
return sizeof(g_all_pixel_formats) / sizeof(g_all_pixel_formats[0]);
|
||||
}
|
||||
|
||||
pixel_format get_pixel_format_by_index(uint index)
|
||||
{
|
||||
CRNLIB_ASSERT(index < get_num_formats());
|
||||
return g_all_pixel_formats[index];
|
||||
}
|
||||
|
||||
const wchar_t* get_pixel_format_string(pixel_format fmt)
|
||||
{
|
||||
switch (fmt)
|
||||
{
|
||||
case PIXEL_FMT_INVALID: return L"INVALID";
|
||||
case PIXEL_FMT_DXT1: return L"DXT1";
|
||||
case PIXEL_FMT_DXT1A: return L"DXT1A";
|
||||
case PIXEL_FMT_DXT2: return L"DXT2";
|
||||
case PIXEL_FMT_DXT3: return L"DXT3";
|
||||
case PIXEL_FMT_DXT4: return L"DXT4";
|
||||
case PIXEL_FMT_DXT5: return L"DXT5";
|
||||
case PIXEL_FMT_3DC: return L"3DC";
|
||||
case PIXEL_FMT_DXN: return L"DXN";
|
||||
case PIXEL_FMT_DXT5A: return L"DXT5A";
|
||||
case PIXEL_FMT_DXT5_CCxY: return L"DXT5_CCxY";
|
||||
case PIXEL_FMT_DXT5_xGxR: return L"DXT5_xGxR";
|
||||
case PIXEL_FMT_DXT5_xGBR: return L"DXT5_xGBR";
|
||||
case PIXEL_FMT_DXT5_AGBR: return L"DXT5_AGBR";
|
||||
case PIXEL_FMT_R8G8B8: return L"R8G8B8";
|
||||
case PIXEL_FMT_A8R8G8B8: return L"A8R8G8B8";
|
||||
case PIXEL_FMT_A8: return L"A8";
|
||||
case PIXEL_FMT_L8: return L"L8";
|
||||
case PIXEL_FMT_A8L8: return L"A8L8";
|
||||
default: break;
|
||||
}
|
||||
CRNLIB_ASSERT(false);
|
||||
return L"?";
|
||||
}
|
||||
|
||||
const char* get_pixel_format_stringa(pixel_format fmt)
|
||||
{
|
||||
switch (fmt)
|
||||
{
|
||||
case PIXEL_FMT_INVALID: return "INVALID";
|
||||
case PIXEL_FMT_DXT1: return "DXT1";
|
||||
case PIXEL_FMT_DXT1A: return "DXT1A";
|
||||
case PIXEL_FMT_DXT2: return "DXT2";
|
||||
case PIXEL_FMT_DXT3: return "DXT3";
|
||||
case PIXEL_FMT_DXT4: return "DXT4";
|
||||
case PIXEL_FMT_DXT5: return "DXT5";
|
||||
case PIXEL_FMT_3DC: return "3DC";
|
||||
case PIXEL_FMT_DXN: return "DXN";
|
||||
case PIXEL_FMT_DXT5A: return "DXT5A";
|
||||
case PIXEL_FMT_DXT5_CCxY: return "DXT5_CCxY";
|
||||
case PIXEL_FMT_DXT5_xGxR: return "DXT5_xGxR";
|
||||
case PIXEL_FMT_DXT5_xGBR: return "DXT5_xGBR";
|
||||
case PIXEL_FMT_DXT5_AGBR: return "DXT5_AGBR";
|
||||
case PIXEL_FMT_R8G8B8: return "R8G8B8";
|
||||
case PIXEL_FMT_A8R8G8B8: return "A8R8G8B8";
|
||||
case PIXEL_FMT_A8: return "A8";
|
||||
case PIXEL_FMT_L8: return "L8";
|
||||
case PIXEL_FMT_A8L8: return "A8L8";
|
||||
default: break;
|
||||
}
|
||||
CRNLIB_ASSERT(false);
|
||||
return "?";
|
||||
}
|
||||
|
||||
const wchar_t* get_crn_format_string(crn_format fmt)
|
||||
{
|
||||
switch (fmt)
|
||||
{
|
||||
case cCRNFmtDXT1: return L"DXT1";
|
||||
case cCRNFmtDXT3: return L"DXT3";
|
||||
case cCRNFmtDXT5: return L"DXT5";
|
||||
case cCRNFmtDXT5_CCxY: return L"DXT5_CCxY";
|
||||
case cCRNFmtDXT5_xGBR: return L"DXT5_xGBR";
|
||||
case cCRNFmtDXT5_AGBR: return L"DXT5_AGBR";
|
||||
case cCRNFmtDXT5_xGxR: return L"DXT5_xGxR";
|
||||
case cCRNFmtDXN_XY: return L"DXN_XY";
|
||||
case cCRNFmtDXN_YX: return L"DXN_YX";
|
||||
case cCRNFmtDXT5A: return L"DXT5A";
|
||||
default: break;
|
||||
}
|
||||
CRNLIB_ASSERT(false);
|
||||
return L"?";
|
||||
}
|
||||
|
||||
const char* get_crn_format_stringa(crn_format fmt)
|
||||
{
|
||||
switch (fmt)
|
||||
{
|
||||
case cCRNFmtDXT1: return "DXT1";
|
||||
case cCRNFmtDXT3: return "DXT3";
|
||||
case cCRNFmtDXT5: return "DXT5";
|
||||
case cCRNFmtDXT5_CCxY: return "DXT5_CCxY";
|
||||
case cCRNFmtDXT5_xGBR: return "DXT5_xGBR";
|
||||
case cCRNFmtDXT5_AGBR: return "DXT5_AGBR";
|
||||
case cCRNFmtDXT5_xGxR: return "DXT5_xGxR";
|
||||
case cCRNFmtDXN_XY: return "DXN_XY";
|
||||
case cCRNFmtDXN_YX: return "DXN_YX";
|
||||
case cCRNFmtDXT5A: return "DXT5A";
|
||||
default: break;
|
||||
}
|
||||
CRNLIB_ASSERT(false);
|
||||
return "?";
|
||||
}
|
||||
|
||||
component_flags get_component_flags(pixel_format fmt)
|
||||
{
|
||||
// These flags are for *uncooked* pixels, i.e. after after adding Z to DXN maps, or converting YCC maps to RGB, etc.
|
||||
|
||||
uint flags = cCompFlagRValid | cCompFlagGValid | cCompFlagBValid | cCompFlagAValid | cCompFlagGrayscale;
|
||||
switch (fmt)
|
||||
{
|
||||
case PIXEL_FMT_DXT1:
|
||||
{
|
||||
flags = cCompFlagRValid | cCompFlagGValid | cCompFlagBValid;
|
||||
break;
|
||||
}
|
||||
case PIXEL_FMT_DXT1A:
|
||||
{
|
||||
flags = cCompFlagRValid | cCompFlagGValid | cCompFlagBValid | cCompFlagAValid;
|
||||
break;
|
||||
}
|
||||
case PIXEL_FMT_DXT2:
|
||||
case PIXEL_FMT_DXT3:
|
||||
{
|
||||
flags = cCompFlagRValid | cCompFlagGValid | cCompFlagBValid | cCompFlagAValid;
|
||||
break;
|
||||
}
|
||||
case PIXEL_FMT_DXT4:
|
||||
case PIXEL_FMT_DXT5:
|
||||
{
|
||||
flags = cCompFlagRValid | cCompFlagGValid | cCompFlagBValid | cCompFlagAValid;
|
||||
break;
|
||||
}
|
||||
case PIXEL_FMT_DXT5A:
|
||||
{
|
||||
flags = cCompFlagAValid;
|
||||
break;
|
||||
}
|
||||
case PIXEL_FMT_DXT5_CCxY:
|
||||
{
|
||||
flags = cCompFlagRValid | cCompFlagGValid | cCompFlagBValid | cCompFlagLumaChroma;
|
||||
break;
|
||||
}
|
||||
case PIXEL_FMT_DXT5_xGBR:
|
||||
{
|
||||
flags = cCompFlagRValid | cCompFlagGValid | cCompFlagBValid | cCompFlagNormalMap;
|
||||
break;
|
||||
}
|
||||
case PIXEL_FMT_DXT5_AGBR:
|
||||
{
|
||||
flags = cCompFlagRValid | cCompFlagGValid | cCompFlagBValid | cCompFlagAValid | cCompFlagNormalMap;
|
||||
break;
|
||||
}
|
||||
case PIXEL_FMT_DXT5_xGxR:
|
||||
{
|
||||
flags = cCompFlagRValid | cCompFlagGValid | cCompFlagBValid | cCompFlagNormalMap;
|
||||
break;
|
||||
}
|
||||
case PIXEL_FMT_3DC:
|
||||
{
|
||||
flags = cCompFlagRValid | cCompFlagGValid | cCompFlagBValid | cCompFlagNormalMap;
|
||||
break;
|
||||
}
|
||||
case PIXEL_FMT_DXN:
|
||||
{
|
||||
flags = cCompFlagRValid | cCompFlagGValid | cCompFlagBValid | cCompFlagNormalMap;
|
||||
break;
|
||||
}
|
||||
case PIXEL_FMT_R8G8B8:
|
||||
{
|
||||
flags = cCompFlagRValid | cCompFlagGValid | cCompFlagBValid;
|
||||
break;
|
||||
}
|
||||
case PIXEL_FMT_A8R8G8B8:
|
||||
{
|
||||
flags = cCompFlagRValid | cCompFlagGValid | cCompFlagBValid | cCompFlagAValid;
|
||||
break;
|
||||
}
|
||||
case PIXEL_FMT_A8:
|
||||
{
|
||||
flags = cCompFlagAValid;
|
||||
break;
|
||||
}
|
||||
case PIXEL_FMT_L8:
|
||||
{
|
||||
flags = cCompFlagRValid | cCompFlagGValid | cCompFlagBValid | cCompFlagGrayscale;
|
||||
break;
|
||||
}
|
||||
case PIXEL_FMT_A8L8:
|
||||
{
|
||||
flags = cCompFlagRValid | cCompFlagGValid | cCompFlagBValid | cCompFlagAValid | cCompFlagGrayscale;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
CRNLIB_ASSERT(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return static_cast<component_flags>(flags);
|
||||
}
|
||||
|
||||
crn_format convert_pixel_format_to_best_crn_format(pixel_format crn_fmt)
|
||||
{
|
||||
crn_format fmt = cCRNFmtDXT1;
|
||||
switch (crn_fmt)
|
||||
{
|
||||
case PIXEL_FMT_DXT1:
|
||||
case PIXEL_FMT_DXT1A:
|
||||
fmt = cCRNFmtDXT1;
|
||||
break;
|
||||
case PIXEL_FMT_DXT2:
|
||||
case PIXEL_FMT_DXT3:
|
||||
case PIXEL_FMT_DXT4:
|
||||
case PIXEL_FMT_DXT5:
|
||||
fmt = cCRNFmtDXT5;
|
||||
break;
|
||||
case PIXEL_FMT_3DC:
|
||||
fmt = cCRNFmtDXN_YX;
|
||||
break;
|
||||
case PIXEL_FMT_DXN:
|
||||
fmt = cCRNFmtDXN_XY;
|
||||
break;
|
||||
case PIXEL_FMT_DXT5A:
|
||||
fmt = cCRNFmtDXT5A;
|
||||
break;
|
||||
case PIXEL_FMT_R8G8B8:
|
||||
case PIXEL_FMT_L8:
|
||||
fmt = cCRNFmtDXT1;
|
||||
break;
|
||||
case PIXEL_FMT_A8R8G8B8:
|
||||
case PIXEL_FMT_A8:
|
||||
case PIXEL_FMT_A8L8:
|
||||
fmt = cCRNFmtDXT5;
|
||||
break;
|
||||
case PIXEL_FMT_DXT5_CCxY:
|
||||
fmt = cCRNFmtDXT5_CCxY;
|
||||
break;
|
||||
case PIXEL_FMT_DXT5_xGBR:
|
||||
fmt = cCRNFmtDXT5_xGBR;
|
||||
break;
|
||||
case PIXEL_FMT_DXT5_AGBR:
|
||||
fmt = cCRNFmtDXT5_AGBR;
|
||||
break;
|
||||
case PIXEL_FMT_DXT5_xGxR:
|
||||
fmt = cCRNFmtDXT5_xGxR;
|
||||
break;
|
||||
default:
|
||||
{
|
||||
CRNLIB_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return fmt;
|
||||
}
|
||||
|
||||
pixel_format convert_crn_format_to_pixel_format(crn_format fmt)
|
||||
{
|
||||
switch (fmt)
|
||||
{
|
||||
case cCRNFmtDXT1: return PIXEL_FMT_DXT1;
|
||||
case cCRNFmtDXT3: return PIXEL_FMT_DXT3;
|
||||
case cCRNFmtDXT5: return PIXEL_FMT_DXT5;
|
||||
case cCRNFmtDXT5_CCxY: return PIXEL_FMT_DXT5_CCxY;
|
||||
case cCRNFmtDXT5_xGxR: return PIXEL_FMT_DXT5_xGxR;
|
||||
case cCRNFmtDXT5_xGBR: return PIXEL_FMT_DXT5_xGBR;
|
||||
case cCRNFmtDXT5_AGBR: return PIXEL_FMT_DXT5_AGBR;
|
||||
case cCRNFmtDXN_XY: return PIXEL_FMT_DXN;
|
||||
case cCRNFmtDXN_YX: return PIXEL_FMT_3DC;
|
||||
case cCRNFmtDXT5A: return PIXEL_FMT_DXT5A;
|
||||
default: break;
|
||||
}
|
||||
|
||||
return PIXEL_FMT_INVALID;
|
||||
}
|
||||
|
||||
} // namespace pixel_format
|
||||
|
||||
} // namespace crnlib
|
||||
|
||||
@@ -0,0 +1,278 @@
|
||||
// File: crn_pixel_format.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
#include "crn_dxt.h"
|
||||
#include "../inc/crnlib.h"
|
||||
#include "../inc/dds_defs.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
namespace pixel_format_helpers
|
||||
{
|
||||
uint get_num_formats();
|
||||
pixel_format get_pixel_format_by_index(uint index);
|
||||
|
||||
const wchar_t* get_pixel_format_string(pixel_format fmt);
|
||||
const char* get_pixel_format_stringa(pixel_format fmt);
|
||||
|
||||
const wchar_t* get_crn_format_string(crn_format fmt);
|
||||
const char* get_crn_format_stringa(crn_format fmt);
|
||||
|
||||
inline bool is_grayscale(pixel_format fmt)
|
||||
{
|
||||
switch (fmt)
|
||||
{
|
||||
case PIXEL_FMT_L8:
|
||||
case PIXEL_FMT_A8L8:
|
||||
return true;
|
||||
default: break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool is_dxt1(pixel_format fmt)
|
||||
{
|
||||
return (fmt == PIXEL_FMT_DXT1) || (fmt == PIXEL_FMT_DXT1A);
|
||||
}
|
||||
|
||||
inline bool has_alpha(pixel_format fmt)
|
||||
{
|
||||
switch (fmt)
|
||||
{
|
||||
case PIXEL_FMT_DXT1A:
|
||||
case PIXEL_FMT_DXT2:
|
||||
case PIXEL_FMT_DXT3:
|
||||
case PIXEL_FMT_DXT4:
|
||||
case PIXEL_FMT_DXT5:
|
||||
case PIXEL_FMT_DXT5A:
|
||||
case PIXEL_FMT_A8R8G8B8:
|
||||
case PIXEL_FMT_A8:
|
||||
case PIXEL_FMT_A8L8:
|
||||
return true;
|
||||
default: break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool is_alpha_only(pixel_format fmt)
|
||||
{
|
||||
switch (fmt)
|
||||
{
|
||||
case PIXEL_FMT_A8:
|
||||
case PIXEL_FMT_DXT5A:
|
||||
return true;
|
||||
default: break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool is_normal_map(pixel_format fmt)
|
||||
{
|
||||
switch (fmt)
|
||||
{
|
||||
case PIXEL_FMT_3DC:
|
||||
case PIXEL_FMT_DXN:
|
||||
case PIXEL_FMT_DXT5_xGBR:
|
||||
case PIXEL_FMT_DXT5_xGxR:
|
||||
case PIXEL_FMT_DXT5_AGBR:
|
||||
return true;
|
||||
default: break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline int is_dxt(pixel_format fmt)
|
||||
{
|
||||
switch (fmt)
|
||||
{
|
||||
case PIXEL_FMT_DXT1:
|
||||
case PIXEL_FMT_DXT1A:
|
||||
case PIXEL_FMT_DXT2:
|
||||
case PIXEL_FMT_DXT3:
|
||||
case PIXEL_FMT_DXT4:
|
||||
case PIXEL_FMT_DXT5:
|
||||
case PIXEL_FMT_3DC:
|
||||
case PIXEL_FMT_DXT5A:
|
||||
case PIXEL_FMT_DXN:
|
||||
case PIXEL_FMT_DXT5_CCxY:
|
||||
case PIXEL_FMT_DXT5_xGxR:
|
||||
case PIXEL_FMT_DXT5_xGBR:
|
||||
case PIXEL_FMT_DXT5_AGBR:
|
||||
return true;
|
||||
default: break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline int get_fundamental_format(pixel_format fmt)
|
||||
{
|
||||
switch (fmt)
|
||||
{
|
||||
case PIXEL_FMT_DXT1A:
|
||||
return PIXEL_FMT_DXT1;
|
||||
case PIXEL_FMT_DXT5_CCxY:
|
||||
case PIXEL_FMT_DXT5_xGxR:
|
||||
case PIXEL_FMT_DXT5_xGBR:
|
||||
case PIXEL_FMT_DXT5_AGBR:
|
||||
return PIXEL_FMT_DXT5;
|
||||
default: break;
|
||||
}
|
||||
return fmt;
|
||||
}
|
||||
|
||||
inline dxt_format get_dxt_format(pixel_format fmt)
|
||||
{
|
||||
switch (fmt)
|
||||
{
|
||||
case PIXEL_FMT_DXT1: return cDXT1;
|
||||
case PIXEL_FMT_DXT1A: return cDXT1A;
|
||||
case PIXEL_FMT_DXT2: return cDXT3;
|
||||
case PIXEL_FMT_DXT3: return cDXT3;
|
||||
case PIXEL_FMT_DXT4: return cDXT5;
|
||||
case PIXEL_FMT_DXT5: return cDXT5;
|
||||
case PIXEL_FMT_3DC: return cDXN_YX;
|
||||
case PIXEL_FMT_DXT5A: return cDXT5A;
|
||||
case PIXEL_FMT_DXN: return cDXN_XY;
|
||||
case PIXEL_FMT_DXT5_CCxY: return cDXT5;
|
||||
case PIXEL_FMT_DXT5_xGxR: return cDXT5;
|
||||
case PIXEL_FMT_DXT5_xGBR: return cDXT5;
|
||||
case PIXEL_FMT_DXT5_AGBR: return cDXT5;
|
||||
default: break;
|
||||
}
|
||||
return cDXTInvalid;
|
||||
}
|
||||
|
||||
inline pixel_format from_dxt_format(dxt_format dxt_fmt)
|
||||
{
|
||||
switch (dxt_fmt)
|
||||
{
|
||||
case cDXT1:
|
||||
return PIXEL_FMT_DXT1;
|
||||
case cDXT1A:
|
||||
return PIXEL_FMT_DXT1A;
|
||||
case cDXT3:
|
||||
return PIXEL_FMT_DXT3;
|
||||
case cDXT5:
|
||||
return PIXEL_FMT_DXT5;
|
||||
case cDXN_XY:
|
||||
return PIXEL_FMT_DXN;
|
||||
case cDXN_YX:
|
||||
return PIXEL_FMT_3DC;
|
||||
case cDXT5A:
|
||||
return PIXEL_FMT_DXT5A;
|
||||
default: break;
|
||||
}
|
||||
CRNLIB_ASSERT(false);
|
||||
return PIXEL_FMT_INVALID;
|
||||
}
|
||||
|
||||
inline bool is_pixel_format_non_srgb(pixel_format fmt)
|
||||
{
|
||||
switch (fmt)
|
||||
{
|
||||
case PIXEL_FMT_3DC:
|
||||
case PIXEL_FMT_DXN:
|
||||
case PIXEL_FMT_DXT5A:
|
||||
case PIXEL_FMT_DXT5_CCxY:
|
||||
case PIXEL_FMT_DXT5_xGxR:
|
||||
case PIXEL_FMT_DXT5_xGBR:
|
||||
case PIXEL_FMT_DXT5_AGBR:
|
||||
return true;
|
||||
default: break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool is_crn_format_non_srgb(crn_format fmt)
|
||||
{
|
||||
switch (fmt)
|
||||
{
|
||||
case cCRNFmtDXN_XY:
|
||||
case cCRNFmtDXN_YX:
|
||||
case cCRNFmtDXT5A:
|
||||
case cCRNFmtDXT5_CCxY:
|
||||
case cCRNFmtDXT5_xGxR:
|
||||
case cCRNFmtDXT5_xGBR:
|
||||
case cCRNFmtDXT5_AGBR:
|
||||
return true;
|
||||
default: break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline uint get_bpp(pixel_format fmt)
|
||||
{
|
||||
switch (fmt)
|
||||
{
|
||||
case PIXEL_FMT_DXT1: return 4;
|
||||
case PIXEL_FMT_DXT1A: return 4;
|
||||
case PIXEL_FMT_DXT2: return 8;
|
||||
case PIXEL_FMT_DXT3: return 8;
|
||||
case PIXEL_FMT_DXT4: return 8;
|
||||
case PIXEL_FMT_DXT5: return 8;
|
||||
case PIXEL_FMT_3DC: return 8;
|
||||
case PIXEL_FMT_DXT5A: return 4;
|
||||
case PIXEL_FMT_R8G8B8: return 24;
|
||||
case PIXEL_FMT_A8R8G8B8: return 32;
|
||||
case PIXEL_FMT_A8: return 8;
|
||||
case PIXEL_FMT_L8: return 8;
|
||||
case PIXEL_FMT_A8L8: return 16;
|
||||
case PIXEL_FMT_DXN: return 8;
|
||||
case PIXEL_FMT_DXT5_CCxY: return 8;
|
||||
case PIXEL_FMT_DXT5_xGxR: return 8;
|
||||
case PIXEL_FMT_DXT5_xGBR: return 8;
|
||||
case PIXEL_FMT_DXT5_AGBR: return 8;
|
||||
default: break;
|
||||
}
|
||||
CRNLIB_ASSERT(false);
|
||||
return 0;
|
||||
};
|
||||
|
||||
inline uint get_dxt_bytes_per_block(pixel_format fmt)
|
||||
{
|
||||
switch (fmt)
|
||||
{
|
||||
case PIXEL_FMT_DXT1: return 8;
|
||||
case PIXEL_FMT_DXT1A: return 8;
|
||||
case PIXEL_FMT_DXT5A: return 8;
|
||||
|
||||
case PIXEL_FMT_DXT2: return 16;
|
||||
case PIXEL_FMT_DXT3: return 16;
|
||||
case PIXEL_FMT_DXT4: return 16;
|
||||
case PIXEL_FMT_DXT5: return 16;
|
||||
case PIXEL_FMT_3DC: return 16;
|
||||
case PIXEL_FMT_DXN: return 16;
|
||||
case PIXEL_FMT_DXT5_CCxY: return 16;
|
||||
case PIXEL_FMT_DXT5_xGxR: return 16;
|
||||
case PIXEL_FMT_DXT5_xGBR: return 16;
|
||||
case PIXEL_FMT_DXT5_AGBR: return 16;
|
||||
default: break;
|
||||
}
|
||||
CRNLIB_ASSERT(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum component_flags
|
||||
{
|
||||
cCompFlagRValid = 1,
|
||||
cCompFlagGValid = 2,
|
||||
cCompFlagBValid = 4,
|
||||
cCompFlagAValid = 8,
|
||||
|
||||
cCompFlagGrayscale = 16,
|
||||
cCompFlagNormalMap = 32,
|
||||
cCompFlagLumaChroma = 64,
|
||||
|
||||
cDefaultCompFlags = cCompFlagRValid | cCompFlagGValid | cCompFlagBValid | cCompFlagAValid
|
||||
};
|
||||
|
||||
component_flags get_component_flags(pixel_format fmt);
|
||||
|
||||
crn_format convert_pixel_format_to_best_crn_format(pixel_format crn_fmt);
|
||||
|
||||
pixel_format convert_crn_format_to_pixel_format(crn_format fmt);
|
||||
|
||||
} // namespace pixel_format_helpers
|
||||
|
||||
} // namespace crnlib
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// File: crn_platform.cpp
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#include "crn_core.h"
|
||||
#include "crn_winhdr.h"
|
||||
|
||||
bool crnlib_is_debugger_present(void)
|
||||
{
|
||||
return IsDebuggerPresent() != 0;
|
||||
}
|
||||
|
||||
void crnlib_debug_break(void)
|
||||
{
|
||||
DebugBreak();
|
||||
}
|
||||
|
||||
void crnlib_output_debug_string(const char* p)
|
||||
{
|
||||
OutputDebugStringA(p);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// File: crn_platform.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
|
||||
#ifdef CRNLIB_PLATFORM_PC
|
||||
const bool c_crnlib_little_endian_platform = true;
|
||||
#else
|
||||
const bool c_crnlib_little_endian_platform = false;
|
||||
#endif
|
||||
|
||||
const bool c_crnlib_big_endian_platform = !c_crnlib_little_endian_platform;
|
||||
|
||||
inline bool crnlib_is_little_endian() { return c_crnlib_little_endian_platform; }
|
||||
inline bool crnlib_is_big_endian() { return c_crnlib_big_endian_platform; }
|
||||
|
||||
inline bool crnlib_is_pc()
|
||||
{
|
||||
#ifdef CRNLIB_PLATFORM_PC
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool crnlib_is_x86()
|
||||
{
|
||||
#ifdef CRNLIB_PLATFORM_PC_X86
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool crnlib_is_x64()
|
||||
{
|
||||
#ifdef CRNLIB_PLATFORM_PC_X64
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool crnlib_is_debugger_present(void);
|
||||
void crnlib_debug_break(void);
|
||||
void crnlib_output_debug_string(const char* p);
|
||||
|
||||
// actually in crnlib_assert.cpp
|
||||
void crnlib_assert(const char* pExp, const char* pFile, unsigned line);
|
||||
void crnlib_fail(const char* pExp, const char* pFile, unsigned line);
|
||||
@@ -0,0 +1,356 @@
|
||||
// File: crn_prefix_coding.cpp
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#include "crn_core.h"
|
||||
#include "crn_prefix_coding.h"
|
||||
//#include "rand.h"
|
||||
|
||||
#ifdef CRNLIB_BUILD_DEBUG
|
||||
//#define TEST_DECODER_TABLES
|
||||
#endif
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
|
||||
namespace prefix_coding
|
||||
{
|
||||
bool limit_max_code_size(uint num_syms, uint8* pCodesizes, uint max_code_size)
|
||||
{
|
||||
const uint cMaxEverCodeSize = 34;
|
||||
|
||||
if ((!num_syms) || (num_syms > cMaxSupportedSyms) || (max_code_size < 1) || (max_code_size > cMaxEverCodeSize))
|
||||
return false;
|
||||
|
||||
uint num_codes[cMaxEverCodeSize + 1];
|
||||
utils::zero_object(num_codes);
|
||||
|
||||
bool should_limit = false;
|
||||
|
||||
for (uint i = 0; i < num_syms; i++)
|
||||
{
|
||||
uint c = pCodesizes[i];
|
||||
if (c)
|
||||
{
|
||||
CRNLIB_ASSERT(c <= cMaxEverCodeSize);
|
||||
|
||||
num_codes[c]++;
|
||||
if (c > max_code_size)
|
||||
should_limit = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!should_limit)
|
||||
return true;
|
||||
|
||||
uint ofs = 0;
|
||||
uint next_sorted_ofs[cMaxEverCodeSize + 1];
|
||||
for (uint i = 1; i <= cMaxEverCodeSize; i++)
|
||||
{
|
||||
next_sorted_ofs[i] = ofs;
|
||||
ofs += num_codes[i];
|
||||
}
|
||||
|
||||
if ((ofs < 2) || (ofs > cMaxSupportedSyms))
|
||||
return true;
|
||||
|
||||
if (ofs > (1U << max_code_size))
|
||||
return false;
|
||||
|
||||
for (uint i = max_code_size + 1; i <= cMaxEverCodeSize; i++)
|
||||
num_codes[max_code_size] += num_codes[i];
|
||||
|
||||
// Technique of adjusting tree to enforce maximum code size from LHArc.
|
||||
|
||||
uint total = 0;
|
||||
for (uint i = max_code_size; i; --i)
|
||||
total += (num_codes[i] << (max_code_size - i));
|
||||
|
||||
if (total == (1U << max_code_size))
|
||||
return true;
|
||||
|
||||
do
|
||||
{
|
||||
num_codes[max_code_size]--;
|
||||
|
||||
uint i;
|
||||
for (i = max_code_size - 1; i; --i)
|
||||
{
|
||||
if (!num_codes[i])
|
||||
continue;
|
||||
num_codes[i]--;
|
||||
num_codes[i + 1] += 2;
|
||||
break;
|
||||
}
|
||||
if (!i)
|
||||
return false;
|
||||
|
||||
total--;
|
||||
} while (total != (1U << max_code_size));
|
||||
|
||||
uint8 new_codesizes[cMaxSupportedSyms];
|
||||
uint8* p = new_codesizes;
|
||||
for (uint i = 1; i <= max_code_size; i++)
|
||||
{
|
||||
uint n = num_codes[i];
|
||||
if (n)
|
||||
{
|
||||
memset(p, i, n);
|
||||
p += n;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint i = 0; i < num_syms; i++)
|
||||
{
|
||||
const uint c = pCodesizes[i];
|
||||
if (c)
|
||||
{
|
||||
uint ofs = next_sorted_ofs[c];
|
||||
next_sorted_ofs[c] = ofs + 1;
|
||||
|
||||
pCodesizes[i] = static_cast<uint8>(new_codesizes[ofs]);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool generate_codes(uint num_syms, const uint8* pCodesizes, uint16* pCodes)
|
||||
{
|
||||
uint num_codes[cMaxExpectedCodeSize + 1];
|
||||
utils::zero_object(num_codes);
|
||||
|
||||
for (uint i = 0; i < num_syms; i++)
|
||||
{
|
||||
uint c = pCodesizes[i];
|
||||
if (c)
|
||||
{
|
||||
CRNLIB_ASSERT(c <= cMaxExpectedCodeSize);
|
||||
num_codes[c]++;
|
||||
}
|
||||
}
|
||||
|
||||
uint code = 0;
|
||||
|
||||
uint next_code[cMaxExpectedCodeSize + 1];
|
||||
next_code[0] = 0;
|
||||
|
||||
for (uint i = 1; i <= cMaxExpectedCodeSize; i++)
|
||||
{
|
||||
next_code[i] = code;
|
||||
|
||||
code = (code + num_codes[i]) << 1;
|
||||
}
|
||||
|
||||
if (code != (1 << (cMaxExpectedCodeSize + 1)))
|
||||
{
|
||||
uint t = 0;
|
||||
for (uint i = 1; i <= cMaxExpectedCodeSize; i++)
|
||||
{
|
||||
t += num_codes[i];
|
||||
if (t > 1)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint i = 0; i < num_syms; i++)
|
||||
{
|
||||
uint c = pCodesizes[i];
|
||||
if (c)
|
||||
{
|
||||
CRNLIB_ASSERT(next_code[c] <= UINT16_MAX);
|
||||
pCodes[i] = static_cast<uint16>(next_code[c]++);
|
||||
|
||||
CRNLIB_ASSERT(math::total_bits(pCodes[i]) <= pCodesizes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool generate_decoder_tables(uint num_syms, const uint8* pCodesizes, decoder_tables* pTables, uint table_bits)
|
||||
{
|
||||
uint min_codes[cMaxExpectedCodeSize];
|
||||
|
||||
if ((!num_syms) || (table_bits > cMaxTableBits))
|
||||
return false;
|
||||
|
||||
pTables->m_num_syms = num_syms;
|
||||
|
||||
uint num_codes[cMaxExpectedCodeSize + 1];
|
||||
utils::zero_object(num_codes);
|
||||
|
||||
for (uint i = 0; i < num_syms; i++)
|
||||
{
|
||||
uint c = pCodesizes[i];
|
||||
if (c)
|
||||
num_codes[c]++;
|
||||
}
|
||||
|
||||
uint sorted_positions[cMaxExpectedCodeSize + 1];
|
||||
|
||||
uint code = 0;
|
||||
|
||||
uint total_used_syms = 0;
|
||||
uint max_code_size = 0;
|
||||
uint min_code_size = UINT_MAX;
|
||||
for (uint i = 1; i <= cMaxExpectedCodeSize; i++)
|
||||
{
|
||||
const uint n = num_codes[i];
|
||||
|
||||
if (!n)
|
||||
pTables->m_max_codes[i - 1] = 0;//UINT_MAX;
|
||||
else
|
||||
{
|
||||
min_code_size = math::minimum(min_code_size, i);
|
||||
max_code_size = math::maximum(max_code_size, i);
|
||||
|
||||
min_codes[i - 1] = code;
|
||||
|
||||
pTables->m_max_codes[i - 1] = code + n - 1;
|
||||
pTables->m_max_codes[i - 1] = 1 + ((pTables->m_max_codes[i - 1] << (16 - i)) | ((1 << (16 - i)) - 1));
|
||||
|
||||
pTables->m_val_ptrs[i - 1] = total_used_syms;
|
||||
|
||||
sorted_positions[i] = total_used_syms;
|
||||
|
||||
code += n;
|
||||
total_used_syms += n;
|
||||
}
|
||||
|
||||
code <<= 1;
|
||||
}
|
||||
|
||||
pTables->m_total_used_syms = total_used_syms;
|
||||
|
||||
if (total_used_syms > pTables->m_cur_sorted_symbol_order_size)
|
||||
{
|
||||
pTables->m_cur_sorted_symbol_order_size = total_used_syms;
|
||||
|
||||
if (!math::is_power_of_2(total_used_syms))
|
||||
pTables->m_cur_sorted_symbol_order_size = math::minimum<uint>(num_syms, math::next_pow2(total_used_syms));
|
||||
|
||||
if (pTables->m_sorted_symbol_order)
|
||||
{
|
||||
crnlib_delete_array(pTables->m_sorted_symbol_order);
|
||||
pTables->m_sorted_symbol_order = NULL;
|
||||
}
|
||||
|
||||
pTables->m_sorted_symbol_order = crnlib_new_array<uint16>(pTables->m_cur_sorted_symbol_order_size);
|
||||
}
|
||||
|
||||
pTables->m_min_code_size = static_cast<uint8>(min_code_size);
|
||||
pTables->m_max_code_size = static_cast<uint8>(max_code_size);
|
||||
|
||||
for (uint i = 0; i < num_syms; i++)
|
||||
{
|
||||
uint c = pCodesizes[i];
|
||||
if (c)
|
||||
{
|
||||
CRNLIB_ASSERT(num_codes[c]);
|
||||
|
||||
uint sorted_pos = sorted_positions[c]++;
|
||||
|
||||
CRNLIB_ASSERT(sorted_pos < total_used_syms);
|
||||
|
||||
pTables->m_sorted_symbol_order[sorted_pos] = static_cast<uint16>(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (table_bits <= pTables->m_min_code_size)
|
||||
table_bits = 0;
|
||||
pTables->m_table_bits = table_bits;
|
||||
|
||||
if (table_bits)
|
||||
{
|
||||
uint table_size = 1 << table_bits;
|
||||
if (table_size > pTables->m_cur_lookup_size)
|
||||
{
|
||||
pTables->m_cur_lookup_size = table_size;
|
||||
|
||||
if (pTables->m_lookup)
|
||||
{
|
||||
crnlib_delete_array(pTables->m_lookup);
|
||||
pTables->m_lookup = NULL;
|
||||
}
|
||||
|
||||
pTables->m_lookup = crnlib_new_array<uint32>(table_size);
|
||||
}
|
||||
|
||||
memset(pTables->m_lookup, 0xFF, static_cast<uint>(sizeof(pTables->m_lookup[0])) * (1UL << table_bits));
|
||||
|
||||
for (uint codesize = 1; codesize <= table_bits; codesize++)
|
||||
{
|
||||
if (!num_codes[codesize])
|
||||
continue;
|
||||
|
||||
const uint fillsize = table_bits - codesize;
|
||||
const uint fillnum = 1 << fillsize;
|
||||
|
||||
const uint min_code = min_codes[codesize - 1];
|
||||
const uint max_code = pTables->get_unshifted_max_code(codesize);
|
||||
const uint val_ptr = pTables->m_val_ptrs[codesize - 1];
|
||||
|
||||
for (uint code = min_code; code <= max_code; code++)
|
||||
{
|
||||
const uint sym_index = pTables->m_sorted_symbol_order[ val_ptr + code - min_code ];
|
||||
CRNLIB_ASSERT( pCodesizes[sym_index] == codesize );
|
||||
|
||||
for (uint j = 0; j < fillnum; j++)
|
||||
{
|
||||
const uint t = j + (code << fillsize);
|
||||
|
||||
CRNLIB_ASSERT(t < (1U << table_bits));
|
||||
|
||||
CRNLIB_ASSERT(pTables->m_lookup[t] == UINT32_MAX);
|
||||
|
||||
pTables->m_lookup[t] = sym_index | (codesize << 16U);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (uint i = 0; i < cMaxExpectedCodeSize; i++)
|
||||
pTables->m_val_ptrs[i] -= min_codes[i];
|
||||
|
||||
pTables->m_table_max_code = 0;
|
||||
pTables->m_decode_start_code_size = pTables->m_min_code_size;
|
||||
|
||||
if (table_bits)
|
||||
{
|
||||
uint i;
|
||||
for (i = table_bits; i >= 1; i--)
|
||||
{
|
||||
if (num_codes[i])
|
||||
{
|
||||
pTables->m_table_max_code = pTables->m_max_codes[i - 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i >= 1)
|
||||
{
|
||||
pTables->m_decode_start_code_size = table_bits + 1;
|
||||
for (uint i = table_bits + 1; i <= max_code_size; i++)
|
||||
{
|
||||
if (num_codes[i])
|
||||
{
|
||||
pTables->m_decode_start_code_size = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sentinels
|
||||
pTables->m_max_codes[cMaxExpectedCodeSize] = UINT_MAX;
|
||||
pTables->m_val_ptrs[cMaxExpectedCodeSize] = 0xFFFFF;
|
||||
|
||||
pTables->m_table_shift = 32 - pTables->m_table_bits;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace prefix_codig
|
||||
|
||||
|
||||
} // namespace crnlib
|
||||
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
// File: crn_prefix_coding.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
namespace prefix_coding
|
||||
{
|
||||
const uint cMaxExpectedCodeSize = 16;
|
||||
const uint cMaxSupportedSyms = 8192;
|
||||
const uint cMaxTableBits = 11;
|
||||
|
||||
bool limit_max_code_size(uint num_syms, uint8* pCodesizes, uint max_code_size);
|
||||
|
||||
bool generate_codes(uint num_syms, const uint8* pCodesizes, uint16* pCodes);
|
||||
|
||||
class decoder_tables
|
||||
{
|
||||
public:
|
||||
inline decoder_tables() :
|
||||
m_table_shift(0), m_table_max_code(0), m_decode_start_code_size(0), m_cur_lookup_size(0), m_lookup(NULL), m_cur_sorted_symbol_order_size(0), m_sorted_symbol_order(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
inline decoder_tables(const decoder_tables& other) :
|
||||
m_table_shift(0), m_table_max_code(0), m_decode_start_code_size(0), m_cur_lookup_size(0), m_lookup(NULL), m_cur_sorted_symbol_order_size(0), m_sorted_symbol_order(NULL)
|
||||
{
|
||||
*this = other;
|
||||
}
|
||||
|
||||
decoder_tables& operator= (const decoder_tables& other)
|
||||
{
|
||||
if (this == &other)
|
||||
return *this;
|
||||
|
||||
clear();
|
||||
|
||||
memcpy(this, &other, sizeof(*this));
|
||||
|
||||
if (other.m_lookup)
|
||||
{
|
||||
m_lookup = crnlib_new_array<uint32>(m_cur_lookup_size);
|
||||
memcpy(m_lookup, other.m_lookup, sizeof(m_lookup[0]) * m_cur_lookup_size);
|
||||
}
|
||||
|
||||
if (other.m_sorted_symbol_order)
|
||||
{
|
||||
m_sorted_symbol_order = crnlib_new_array<uint16>(m_cur_sorted_symbol_order_size);
|
||||
memcpy(m_sorted_symbol_order, other.m_sorted_symbol_order, sizeof(m_sorted_symbol_order[0]) * m_cur_sorted_symbol_order_size);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline void clear()
|
||||
{
|
||||
if (m_lookup)
|
||||
{
|
||||
crnlib_delete_array(m_lookup);
|
||||
m_lookup = 0;
|
||||
m_cur_lookup_size = 0;
|
||||
}
|
||||
|
||||
if (m_sorted_symbol_order)
|
||||
{
|
||||
crnlib_delete_array(m_sorted_symbol_order);
|
||||
m_sorted_symbol_order = NULL;
|
||||
m_cur_sorted_symbol_order_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline ~decoder_tables()
|
||||
{
|
||||
if (m_lookup)
|
||||
crnlib_delete_array(m_lookup);
|
||||
|
||||
if (m_sorted_symbol_order)
|
||||
crnlib_delete_array(m_sorted_symbol_order);
|
||||
}
|
||||
|
||||
// DO NOT use any complex classes here - it is bitwise copied.
|
||||
|
||||
uint m_num_syms;
|
||||
uint m_total_used_syms;
|
||||
uint m_table_bits;
|
||||
uint m_table_shift;
|
||||
uint m_table_max_code;
|
||||
uint m_decode_start_code_size;
|
||||
|
||||
uint8 m_min_code_size;
|
||||
uint8 m_max_code_size;
|
||||
|
||||
uint m_max_codes[cMaxExpectedCodeSize + 1];
|
||||
int m_val_ptrs[cMaxExpectedCodeSize + 1];
|
||||
|
||||
uint m_cur_lookup_size;
|
||||
uint32* m_lookup;
|
||||
|
||||
uint m_cur_sorted_symbol_order_size;
|
||||
uint16* m_sorted_symbol_order;
|
||||
|
||||
inline uint get_unshifted_max_code(uint len) const
|
||||
{
|
||||
CRNLIB_ASSERT( (len >= 1) && (len <= cMaxExpectedCodeSize) );
|
||||
uint k = m_max_codes[len - 1];
|
||||
if (!k)
|
||||
return UINT_MAX;
|
||||
return (k - 1) >> (16 - len);
|
||||
}
|
||||
};
|
||||
|
||||
bool generate_decoder_tables(uint num_syms, const uint8* pCodesizes, decoder_tables* pTables, uint table_bits);
|
||||
|
||||
} // namespace prefix_coding
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,909 @@
|
||||
// File: crn_qdxt.cpp
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#include "crn_core.h"
|
||||
#include "crn_qdxt1.h"
|
||||
#include "crn_dxt1.h"
|
||||
#include "crn_dxt_fast.h"
|
||||
#include "crn_image_utils.h"
|
||||
#include "crn_dxt_hc_common.h"
|
||||
|
||||
#define GENERATE_DEBUG_IMAGES 0
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
qdxt1::qdxt1(task_pool& task_pool) :
|
||||
m_pTask_pool(&task_pool),
|
||||
m_main_thread_id(0),
|
||||
m_canceled(false),
|
||||
m_progress_start(0),
|
||||
m_progress_range(100),
|
||||
m_num_blocks(0),
|
||||
m_pBlocks(NULL),
|
||||
m_pDst_elements(NULL),
|
||||
m_elements_per_block(0),
|
||||
m_max_selector_clusters(0),
|
||||
m_prev_percentage_complete(-1),
|
||||
m_selector_clusterizer(task_pool)
|
||||
{
|
||||
}
|
||||
|
||||
qdxt1::~qdxt1()
|
||||
{
|
||||
}
|
||||
|
||||
void qdxt1::clear()
|
||||
{
|
||||
m_main_thread_id = 0;
|
||||
m_num_blocks = 0;
|
||||
m_pBlocks = 0;
|
||||
m_pDst_elements = NULL;
|
||||
m_elements_per_block = 0;
|
||||
m_params.clear();
|
||||
m_endpoint_clusterizer.clear();
|
||||
m_endpoint_cluster_indices.clear();
|
||||
m_max_selector_clusters = 0;
|
||||
m_canceled = false;
|
||||
m_progress_start = 0;
|
||||
m_progress_range = 100;
|
||||
m_selector_clusterizer.clear();
|
||||
|
||||
for (uint i = 0; i <= qdxt1_params::cMaxQuality; i++)
|
||||
m_cached_selector_cluster_indices[i].clear();
|
||||
|
||||
m_cluster_hash.clear();
|
||||
|
||||
m_prev_percentage_complete = -1;
|
||||
}
|
||||
|
||||
bool qdxt1::init(uint n, const dxt_pixel_block* pBlocks, const qdxt1_params& params)
|
||||
{
|
||||
clear();
|
||||
|
||||
CRNLIB_ASSERT(n && pBlocks);
|
||||
|
||||
m_main_thread_id = get_current_thread_id();
|
||||
|
||||
m_num_blocks = n;
|
||||
m_pBlocks = pBlocks;
|
||||
m_params = params;
|
||||
|
||||
m_endpoint_clusterizer.reserve_training_vecs(m_num_blocks);
|
||||
|
||||
m_progress_start = 0;
|
||||
m_progress_range = 75;
|
||||
|
||||
const bool debugging = false;
|
||||
image_u8 debug_img;
|
||||
|
||||
if ((m_params.m_hierarchical) && (m_params.m_num_mips))
|
||||
{
|
||||
vec6F_clusterizer::training_vec_array& training_vecs = m_endpoint_clusterizer.get_training_vecs();
|
||||
training_vecs.resize(m_num_blocks);
|
||||
|
||||
uint encoding_hist[cNumChunkEncodings];
|
||||
utils::zero_object(encoding_hist);
|
||||
|
||||
uint total_processed_blocks = 0;
|
||||
uint next_progress_threshold = 512;
|
||||
|
||||
for (uint level = 0; level < m_params.m_num_mips; level++)
|
||||
{
|
||||
const qdxt1_params::mip_desc& level_desc = m_params.m_mip_desc[level];
|
||||
|
||||
const uint num_chunks_x = (level_desc.m_block_width + cChunkBlockWidth - 1) / cChunkBlockWidth;
|
||||
const uint num_chunks_y = (level_desc.m_block_height + cChunkBlockHeight - 1) / cChunkBlockHeight;
|
||||
|
||||
const uint level_width = level_desc.m_block_width * 4;
|
||||
const uint level_height = level_desc.m_block_height * 4;
|
||||
|
||||
if (debugging)
|
||||
debug_img.resize(num_chunks_x * cChunkPixelWidth, num_chunks_y * cChunkPixelHeight);
|
||||
|
||||
for (uint chunk_y = 0; chunk_y < num_chunks_y; chunk_y++)
|
||||
{
|
||||
for (uint chunk_x = 0; chunk_x < num_chunks_x; chunk_x++)
|
||||
{
|
||||
color_quad_u8 chunk_pixels[cChunkPixelWidth * cChunkPixelHeight];
|
||||
|
||||
for (uint y = 0; y < cChunkPixelHeight; y++)
|
||||
{
|
||||
const uint pix_y = math::minimum<uint>(chunk_y * cChunkPixelHeight + y, level_height - 1);
|
||||
|
||||
const uint outer_block_index = level_desc.m_first_block + ((pix_y >> 2) * level_desc.m_block_width);
|
||||
|
||||
for (uint x = 0; x < cChunkPixelWidth; x++)
|
||||
{
|
||||
const uint pix_x = math::minimum<uint>(chunk_x * cChunkPixelWidth + x, level_width - 1);
|
||||
|
||||
const uint block_index = outer_block_index + (pix_x >> 2);
|
||||
|
||||
const dxt_pixel_block& block = m_pBlocks[block_index];
|
||||
|
||||
const color_quad_u8& p = block.m_pixels[pix_y & 3][pix_x & 3];
|
||||
|
||||
chunk_pixels[x + y * 8] = p;
|
||||
}
|
||||
}
|
||||
|
||||
struct layout_results
|
||||
{
|
||||
uint m_low_color;
|
||||
uint m_high_color;
|
||||
uint8 m_selectors[cChunkPixelWidth * cChunkPixelHeight];
|
||||
uint64 m_error;
|
||||
//float m_penalty;
|
||||
};
|
||||
layout_results layouts[cNumChunkTileLayouts];
|
||||
|
||||
for (uint l = 0; l < cNumChunkTileLayouts; l++)
|
||||
{
|
||||
const uint width = g_chunk_tile_layouts[l].m_width;
|
||||
const uint height = g_chunk_tile_layouts[l].m_height;
|
||||
const uint x_ofs = g_chunk_tile_layouts[l].m_x_ofs;
|
||||
const uint y_ofs = g_chunk_tile_layouts[l].m_y_ofs;
|
||||
|
||||
color_quad_u8 layout_pixels[cChunkPixelWidth * cChunkPixelHeight];
|
||||
for (uint y = 0; y < height; y++)
|
||||
for (uint x = 0; x < width; x++)
|
||||
layout_pixels[x + y * width] = chunk_pixels[(x_ofs + x) + (y_ofs + y) * cChunkPixelWidth];
|
||||
|
||||
const uint n = width * height;
|
||||
dxt_fast::compress_color_block(n, layout_pixels, layouts[l].m_low_color, layouts[l].m_high_color, layouts[l].m_selectors);
|
||||
|
||||
color_quad_u8 c[4];
|
||||
dxt1_block::get_block_colors(c, static_cast<uint16>(layouts[l].m_low_color), static_cast<uint16>(layouts[l].m_high_color));
|
||||
|
||||
uint64 error = 0;
|
||||
for (uint i = 0; i < n; i++)
|
||||
error += color::elucidian_distance(layout_pixels[i], c[layouts[l].m_selectors[i]], false);
|
||||
|
||||
layouts[l].m_error = error;
|
||||
|
||||
#if 0
|
||||
if ((width > 4) || (height > 4))
|
||||
{
|
||||
const uint dist = color::elucidian_distance(
|
||||
dxt1_block::unpack_color(static_cast<uint16>(layouts[l].m_low_color), true),
|
||||
dxt1_block::unpack_color(static_cast<uint16>(layouts[l].m_high_color), true), false);
|
||||
|
||||
layouts[l].m_penalty = math::clamp((sqrt((float)dist) - 75.0f) / 150.0f, 0.0f, 2.0f);
|
||||
if ((width == 8) && (height == 8))
|
||||
layouts[l].m_penalty *= 2.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
layouts[l].m_penalty = 0.0f;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
double best_peak_snr = -1.0f;
|
||||
uint best_encoding = 0;
|
||||
|
||||
for (uint e = 0; e < cNumChunkEncodings; e++)
|
||||
{
|
||||
const chunk_encoding_desc& encoding_desc = g_chunk_encodings[e];
|
||||
|
||||
double total_error = 0;
|
||||
|
||||
for (uint t = 0; t < encoding_desc.m_num_tiles; t++)
|
||||
total_error += (double)layouts[encoding_desc.m_tiles[t].m_layout_index].m_error;
|
||||
|
||||
//double mean_squared = total_error * (1.0f / (16.0f * 3.0f));
|
||||
double mean_squared = total_error * (1.0f / (64.0f * 3.0f));
|
||||
double root_mean_squared = sqrt(mean_squared);
|
||||
|
||||
double peak_snr = 999999.0f;
|
||||
if (mean_squared)
|
||||
peak_snr = math::clamp<double>(log10(255.0f / root_mean_squared) * 20.0f, 0.0f, 500.0f);
|
||||
|
||||
float adaptive_tile_color_psnr_derating = 2.4f;
|
||||
//if (level)
|
||||
// adaptive_tile_color_psnr_derating = math::lerp(adaptive_tile_color_psnr_derating * .5f, .3f, math::maximum((level - 1) / float(m_params.m_num_mips - 2), 1.0f));
|
||||
if ((level) && (adaptive_tile_color_psnr_derating > .25f))
|
||||
{
|
||||
adaptive_tile_color_psnr_derating = math::maximum(.25f, adaptive_tile_color_psnr_derating / powf(3.0f, static_cast<float>(level)));
|
||||
}
|
||||
|
||||
float color_derating = math::lerp( 0.0f, adaptive_tile_color_psnr_derating, (g_chunk_encodings[e].m_num_tiles - 1) / 3.0f );
|
||||
peak_snr = peak_snr - color_derating;
|
||||
|
||||
//for (uint t = 0; t < encoding_desc.m_num_tiles; t++)
|
||||
// peak_snr -= (double)layouts[encoding_desc.m_tiles[t].m_layout_index].m_penalty;
|
||||
|
||||
if (peak_snr > best_peak_snr)
|
||||
{
|
||||
best_peak_snr = peak_snr;
|
||||
best_encoding = e;
|
||||
}
|
||||
}
|
||||
|
||||
encoding_hist[best_encoding]++;
|
||||
|
||||
const chunk_encoding_desc& encoding_desc = g_chunk_encodings[best_encoding];
|
||||
|
||||
for (uint t = 0; t < encoding_desc.m_num_tiles; t++)
|
||||
{
|
||||
const chunk_tile_desc& tile_desc = encoding_desc.m_tiles[t];
|
||||
|
||||
uint layout_index = tile_desc.m_layout_index;
|
||||
const layout_results& layout = layouts[layout_index];
|
||||
color_quad_u8 c[4];
|
||||
if (debugging)
|
||||
dxt1_block::get_block_colors(c, static_cast<uint16>(layout.m_low_color), static_cast<uint16>(layout.m_high_color));
|
||||
|
||||
color_quad_u8 tile_pixels[cChunkPixelWidth * cChunkPixelHeight];
|
||||
|
||||
for (uint y = 0; y < tile_desc.m_height; y++)
|
||||
{
|
||||
const uint pix_y = y + tile_desc.m_y_ofs;
|
||||
|
||||
for (uint x = 0; x < tile_desc.m_width; x++)
|
||||
{
|
||||
const uint pix_x = x + tile_desc.m_x_ofs;
|
||||
|
||||
tile_pixels[x + y * tile_desc.m_width] = chunk_pixels[pix_x + pix_y * cChunkPixelWidth];
|
||||
|
||||
if (debugging)
|
||||
debug_img(chunk_x * 8 + pix_x, chunk_y * 8 + pix_y) = c[layout.m_selectors[x + y * tile_desc.m_width]];
|
||||
}
|
||||
}
|
||||
|
||||
color_quad_u8 l, h;
|
||||
dxt_fast::find_representative_colors(tile_desc.m_width * tile_desc.m_height, tile_pixels, l, h);
|
||||
|
||||
//const uint dist = color::color_distance(m_params.m_perceptual, l, h, false);
|
||||
const uint dist = color::elucidian_distance(l, h, false);
|
||||
|
||||
const uint cColorDistToWeight = 5000;
|
||||
const uint cMaxWeight = 8;
|
||||
uint weight = math::clamp<uint>(dist / cColorDistToWeight, 1, cMaxWeight);
|
||||
|
||||
vec6F ev;
|
||||
|
||||
ev[0] = l[0]; ev[1] = l[1]; ev[2] = l[2];
|
||||
ev[3] = h[0]; ev[4] = h[1]; ev[5] = h[2];
|
||||
|
||||
for (uint y = 0; y < (tile_desc.m_height >> 2); y++)
|
||||
{
|
||||
uint block_y = chunk_y * cChunkBlockHeight + y + (tile_desc.m_y_ofs >> 2);
|
||||
if (block_y >= level_desc.m_block_height)
|
||||
continue;
|
||||
|
||||
for (uint x = 0; x < (tile_desc.m_width >> 2); x++)
|
||||
{
|
||||
uint block_x = chunk_x * cChunkBlockWidth + x + (tile_desc.m_x_ofs >> 2);
|
||||
if (block_x >= level_desc.m_block_width)
|
||||
break;
|
||||
|
||||
uint block_index = level_desc.m_first_block + block_x + block_y * level_desc.m_block_width;
|
||||
|
||||
training_vecs[block_index].first = ev;
|
||||
training_vecs[block_index].second = weight;
|
||||
|
||||
total_processed_blocks++;
|
||||
|
||||
//if (debugging)
|
||||
//{
|
||||
// debug_img(block_x, block_y) = l;
|
||||
// debug_img(block_x + level_desc.m_block_width, block_y) = h;
|
||||
//}
|
||||
|
||||
} // x
|
||||
} // y
|
||||
} //t
|
||||
|
||||
if (total_processed_blocks >= next_progress_threshold)
|
||||
{
|
||||
next_progress_threshold += 512;
|
||||
|
||||
if (!update_progress(total_processed_blocks, m_num_blocks - 1))
|
||||
return false;
|
||||
}
|
||||
|
||||
} // chunk_x
|
||||
} // chunk_y
|
||||
|
||||
#if GENERATE_DEBUG_IMAGES
|
||||
if (debugging)
|
||||
image_utils::save_to_file_stb(dynamic_wstring(cVarArg, L"debug_%u.tga", level).get_ptr(), debug_img, image_utils::cSaveIgnoreAlpha);
|
||||
#endif
|
||||
|
||||
} // level
|
||||
|
||||
#if 0
|
||||
trace("chunk encoding hist: ");
|
||||
for (uint i = 0; i < cNumChunkEncodings; i++)
|
||||
trace("%u ", encoding_hist[i]);
|
||||
trace("\n");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
for (uint block_index = 0; block_index < m_num_blocks; block_index++)
|
||||
{
|
||||
if ((block_index & 511) == 0)
|
||||
{
|
||||
if (!update_progress(block_index, m_num_blocks - 1))
|
||||
return false;
|
||||
}
|
||||
|
||||
color_quad_u8 l, h;
|
||||
dxt_fast::find_representative_colors(cDXTBlockSize * cDXTBlockSize, &m_pBlocks[block_index].m_pixels[0][0], l, h);
|
||||
|
||||
//const uint dist = color::color_distance(m_params.m_perceptual, l, h, false);
|
||||
const uint dist = color::elucidian_distance(l, h, false);
|
||||
|
||||
const uint cColorDistToWeight = 5000;
|
||||
const uint cMaxWeight = 8;
|
||||
uint weight = math::clamp<uint>(dist / cColorDistToWeight, 1, cMaxWeight);
|
||||
|
||||
vec6F ev;
|
||||
|
||||
ev[0] = l[0]; ev[1] = l[1]; ev[2] = l[2];
|
||||
ev[3] = h[0]; ev[4] = h[1]; ev[5] = h[2];
|
||||
|
||||
m_endpoint_clusterizer.add_training_vec(ev, weight);
|
||||
}
|
||||
}
|
||||
|
||||
const uint cMaxEndpointClusters = 65535U;
|
||||
|
||||
m_progress_start = 75;
|
||||
m_progress_range = 20;
|
||||
|
||||
if (!m_endpoint_clusterizer.generate_codebook(cMaxEndpointClusters, generate_codebook_progress_callback, this))
|
||||
return false;
|
||||
|
||||
crnlib::hash_map<uint, empty_type> selector_hash;
|
||||
|
||||
m_progress_start = 95;
|
||||
m_progress_range = 5;
|
||||
|
||||
for (uint block_index = 0; block_index < m_num_blocks; block_index++)
|
||||
{
|
||||
if ((block_index & 511) == 0)
|
||||
{
|
||||
if (!update_progress(block_index, m_num_blocks - 1))
|
||||
return false;
|
||||
}
|
||||
|
||||
dxt1_block dxt_blk;
|
||||
dxt_fast::compress_color_block(&dxt_blk, &m_pBlocks[block_index].m_pixels[0][0]);
|
||||
|
||||
uint selectors = dxt_blk.m_selectors[0] | (dxt_blk.m_selectors[1] << 8) | (dxt_blk.m_selectors[2] << 16) | (dxt_blk.m_selectors[3] << 24);
|
||||
|
||||
selector_hash.insert(selectors);
|
||||
}
|
||||
|
||||
m_max_selector_clusters = selector_hash.size() + 128;
|
||||
|
||||
// trace("max endpoint clusters: %u\n", m_endpoint_clusterizer.get_codebook_size());
|
||||
// trace("max selector clusters: %u\n", m_max_selector_clusters);
|
||||
|
||||
update_progress(1, 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool qdxt1::update_progress(uint value, uint max_value)
|
||||
{
|
||||
if (!m_params.m_pProgress_func)
|
||||
return true;
|
||||
|
||||
uint percentage = max_value ? (m_progress_start + (value * m_progress_range + (max_value / 2)) / max_value) : 100;
|
||||
if ((int)percentage == m_prev_percentage_complete)
|
||||
return true;
|
||||
m_prev_percentage_complete = percentage;
|
||||
|
||||
if (!m_params.m_pProgress_func(m_params.m_progress_start + (percentage * m_params.m_progress_range) / 100U, m_params.m_pProgress_data))
|
||||
{
|
||||
m_canceled = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void qdxt1::pack_endpoints_task(uint64 data, void* pData_ptr)
|
||||
{
|
||||
pData_ptr;
|
||||
const uint thread_index = static_cast<uint>(data);
|
||||
|
||||
crnlib::vector<color_quad_u8> cluster_pixels;
|
||||
cluster_pixels.reserve(1024);
|
||||
|
||||
crnlib::vector<uint8> selectors;
|
||||
selectors.reserve(1024);
|
||||
|
||||
dxt1_endpoint_optimizer optimizer;
|
||||
dxt1_endpoint_optimizer::params p;
|
||||
dxt1_endpoint_optimizer::results r;
|
||||
|
||||
p.m_quality = m_params.m_dxt_quality;
|
||||
p.m_use_alpha_blocks = m_params.m_use_alpha_blocks;
|
||||
p.m_dxt1a_alpha_threshold = m_params.m_dxt1a_alpha_threshold;
|
||||
p.m_perceptual = m_params.m_perceptual;
|
||||
|
||||
uint cluster_index_progress_mask = math::next_pow2(m_endpoint_cluster_indices.size() / 100);
|
||||
cluster_index_progress_mask /= 2;
|
||||
cluster_index_progress_mask = math::maximum<uint>(cluster_index_progress_mask, 8);
|
||||
cluster_index_progress_mask -= 1;
|
||||
|
||||
cluster_id cid;
|
||||
const crnlib::vector<uint32>& indices = cid.m_cells;
|
||||
|
||||
for (uint cluster_index = 0; cluster_index < m_endpoint_cluster_indices.size(); cluster_index++)
|
||||
{
|
||||
if (m_canceled)
|
||||
return;
|
||||
|
||||
if ((cluster_index & cluster_index_progress_mask) == 0)
|
||||
{
|
||||
if (get_current_thread_id() == m_main_thread_id)
|
||||
{
|
||||
if (!update_progress(cluster_index, m_endpoint_cluster_indices.size() - 1))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_pTask_pool->get_num_threads())
|
||||
{
|
||||
if ((cluster_index % (m_pTask_pool->get_num_threads() + 1)) != thread_index)
|
||||
continue;
|
||||
}
|
||||
|
||||
const crnlib::vector<uint>& cluster_indices = m_endpoint_cluster_indices[cluster_index];
|
||||
|
||||
selectors.resize(cluster_indices.size() * cDXTBlockSize * cDXTBlockSize);
|
||||
|
||||
bool found = false;
|
||||
uint32 found_endpoints = 0;
|
||||
|
||||
cid.set(cluster_indices);
|
||||
|
||||
{
|
||||
scoped_spinlock lock(m_cluster_hash_lock);
|
||||
|
||||
cluster_hash::const_iterator it(m_cluster_hash.find(cid));
|
||||
if (it != m_cluster_hash.end())
|
||||
{
|
||||
CRNLIB_ASSERT(cid == it->first);
|
||||
|
||||
found = true;
|
||||
found_endpoints = it->second;
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
{
|
||||
const uint16 low_color = static_cast<uint16>(found_endpoints);
|
||||
const uint16 high_color = static_cast<uint16>((found_endpoints >> 16U));
|
||||
|
||||
color_quad_u8 block_colors[4];
|
||||
dxt1_block::get_block_colors(block_colors, low_color, high_color);
|
||||
|
||||
const bool is_alpha_block = (low_color <= high_color);
|
||||
|
||||
for (uint block_iter = 0; block_iter < indices.size(); block_iter++)
|
||||
{
|
||||
const uint block_index = indices[block_iter];
|
||||
|
||||
const color_quad_u8* pSrc_pixels = &m_pBlocks[block_index].m_pixels[0][0];
|
||||
|
||||
for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
|
||||
{
|
||||
dxt1_block& dxt_block = get_block(block_index);
|
||||
|
||||
dxt_block.set_low_color(static_cast<uint16>(low_color));
|
||||
dxt_block.set_high_color(static_cast<uint16>(high_color));
|
||||
|
||||
uint mask = 0;
|
||||
for (int i = 15; i >= 0; i--)
|
||||
{
|
||||
mask <<= 2;
|
||||
|
||||
const color_quad_u8& c = pSrc_pixels[i];
|
||||
|
||||
uint dist0 = color::color_distance(m_params.m_perceptual, c, block_colors[0], false);
|
||||
uint dist1 = color::color_distance(m_params.m_perceptual, c, block_colors[1], false);
|
||||
uint dist2 = color::color_distance(m_params.m_perceptual, c, block_colors[2], false);
|
||||
|
||||
uint selector = 0, best_dist = dist0;
|
||||
|
||||
if (dist1 < best_dist) { selector = 1; best_dist = dist1; }
|
||||
if (dist2 < best_dist) { selector = 2; best_dist = dist2; }
|
||||
|
||||
if (!is_alpha_block)
|
||||
{
|
||||
uint dist3 = color::color_distance(m_params.m_perceptual, c, block_colors[3], false);
|
||||
if (dist3 < best_dist) { selector = 3; }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c.a < m_params.m_dxt1a_alpha_threshold)
|
||||
selector = 3;
|
||||
}
|
||||
|
||||
mask |= selector;
|
||||
}
|
||||
|
||||
dxt_block.m_selectors[0] = static_cast<uint8>(mask & 0xFF);
|
||||
dxt_block.m_selectors[1] = static_cast<uint8>((mask >> 8) & 0xFF);
|
||||
dxt_block.m_selectors[2] = static_cast<uint8>((mask >> 16) & 0xFF);
|
||||
dxt_block.m_selectors[3] = static_cast<uint8>((mask >> 24) & 0xFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cluster_pixels.resize(indices.size() * cDXTBlockSize * cDXTBlockSize);
|
||||
|
||||
color_quad_u8* pDst = &cluster_pixels[0];
|
||||
|
||||
bool has_alpha_pixels = false;
|
||||
|
||||
for (uint block_iter = 0; block_iter < indices.size(); block_iter++)
|
||||
{
|
||||
const uint block_index = indices[block_iter];
|
||||
|
||||
const color_quad_u8* pSrc_pixels = &m_pBlocks[block_index].m_pixels[0][0];
|
||||
|
||||
for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
|
||||
{
|
||||
const color_quad_u8& src = pSrc_pixels[i];
|
||||
|
||||
if (src.a < m_params.m_dxt1a_alpha_threshold)
|
||||
has_alpha_pixels = true;
|
||||
|
||||
*pDst++ = src;
|
||||
}
|
||||
}
|
||||
|
||||
p.m_block_index = cluster_index;
|
||||
p.m_num_pixels = cluster_pixels.size();
|
||||
p.m_pPixels = cluster_pixels.begin();
|
||||
|
||||
r.m_pSelectors = selectors.begin();
|
||||
|
||||
uint low_color, high_color;
|
||||
if ((m_params.m_dxt_quality != cCRNDXTQualitySuperFast) || (has_alpha_pixels))
|
||||
{
|
||||
p.m_pixels_have_alpha = has_alpha_pixels;
|
||||
|
||||
optimizer.compute(p, r);
|
||||
low_color = r.m_low_color;
|
||||
high_color = r.m_high_color;
|
||||
}
|
||||
else
|
||||
{
|
||||
dxt_fast::compress_color_block(cluster_pixels.size(), cluster_pixels.begin(), low_color, high_color, selectors.begin(), true);
|
||||
}
|
||||
|
||||
const uint8* pSrc_selectors = selectors.begin();
|
||||
|
||||
for (uint block_iter = 0; block_iter < indices.size(); block_iter++)
|
||||
{
|
||||
const uint block_index = indices[block_iter];
|
||||
|
||||
dxt1_block& dxt_block = get_block(block_index);
|
||||
|
||||
dxt_block.set_low_color(static_cast<uint16>(low_color));
|
||||
dxt_block.set_high_color(static_cast<uint16>(high_color));
|
||||
|
||||
uint mask = 0;
|
||||
for (int i = 15; i >= 0; i--)
|
||||
{
|
||||
mask <<= 2;
|
||||
mask |= pSrc_selectors[i];
|
||||
}
|
||||
pSrc_selectors += (cDXTBlockSize * cDXTBlockSize);
|
||||
|
||||
dxt_block.m_selectors[0] = static_cast<uint8>(mask & 0xFF);
|
||||
dxt_block.m_selectors[1] = static_cast<uint8>((mask >> 8) & 0xFF);
|
||||
dxt_block.m_selectors[2] = static_cast<uint8>((mask >> 16) & 0xFF);
|
||||
dxt_block.m_selectors[3] = static_cast<uint8>((mask >> 24) & 0xFF);
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
scoped_spinlock lock(m_cluster_hash_lock);
|
||||
|
||||
m_cluster_hash.insert(cid, low_color | (high_color << 16));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
struct optimize_selectors_params
|
||||
{
|
||||
CRNLIB_NO_COPY_OR_ASSIGNMENT_OP(optimize_selectors_params);
|
||||
|
||||
optimize_selectors_params(
|
||||
crnlib::vector< crnlib::vector<uint> >& selector_cluster_indices) :
|
||||
m_selector_cluster_indices(selector_cluster_indices)
|
||||
{
|
||||
}
|
||||
|
||||
crnlib::vector< crnlib::vector<uint> >& m_selector_cluster_indices;
|
||||
};
|
||||
|
||||
void qdxt1::optimize_selectors_task(uint64 data, void* pData_ptr)
|
||||
{
|
||||
const uint thread_index = static_cast<uint>(data);
|
||||
|
||||
optimize_selectors_params& task_params = *static_cast<optimize_selectors_params*>(pData_ptr);
|
||||
|
||||
crnlib::vector<uint> block_categories[2];
|
||||
block_categories[0].reserve(2048);
|
||||
block_categories[1].reserve(2048);
|
||||
|
||||
for (uint cluster_index = 0; cluster_index < task_params.m_selector_cluster_indices.size(); cluster_index++)
|
||||
{
|
||||
if (m_canceled)
|
||||
return;
|
||||
|
||||
if ((cluster_index & 255) == 0)
|
||||
{
|
||||
if (get_current_thread_id() == m_main_thread_id)
|
||||
{
|
||||
if (!update_progress(cluster_index, task_params.m_selector_cluster_indices.size() - 1))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_pTask_pool->get_num_threads())
|
||||
{
|
||||
if ((cluster_index % (m_pTask_pool->get_num_threads() + 1)) != thread_index)
|
||||
continue;
|
||||
}
|
||||
|
||||
const crnlib::vector<uint>& selector_indices = task_params.m_selector_cluster_indices[cluster_index];
|
||||
|
||||
if (selector_indices.size() <= 1)
|
||||
continue;
|
||||
|
||||
block_categories[0].resize(0);
|
||||
block_categories[1].resize(0);
|
||||
|
||||
for (uint block_iter = 0; block_iter < selector_indices.size(); block_iter++)
|
||||
{
|
||||
const uint block_index = selector_indices[block_iter];
|
||||
|
||||
const dxt1_block& src_block = get_block(block_index);
|
||||
|
||||
if (!src_block.is_alpha_block())
|
||||
block_categories[0].push_back(block_index);
|
||||
else
|
||||
{
|
||||
bool has_alpha_pixels = false;
|
||||
|
||||
if (m_params.m_dxt1a_alpha_threshold > 0)
|
||||
{
|
||||
const color_quad_u8* pSrc_pixels = &m_pBlocks[block_index].m_pixels[0][0];
|
||||
|
||||
for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
|
||||
{
|
||||
const color_quad_u8& src = pSrc_pixels[i];
|
||||
if (src.a < m_params.m_dxt1a_alpha_threshold)
|
||||
{
|
||||
has_alpha_pixels = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (has_alpha_pixels)
|
||||
continue;
|
||||
|
||||
block_categories[1].push_back(block_index);
|
||||
}
|
||||
}
|
||||
|
||||
dxt1_block blk;
|
||||
utils::zero_object(blk);
|
||||
|
||||
for (uint block_type = 0; block_type <= 1; block_type++)
|
||||
{
|
||||
const crnlib::vector<uint>& block_indices = block_categories[block_type];
|
||||
if (block_indices.size() <= 1)
|
||||
continue;
|
||||
|
||||
for (uint y = 0; y < 4; y++)
|
||||
{
|
||||
for (uint x = 0; x < 4; x++)
|
||||
{
|
||||
uint best_s = 0;
|
||||
uint64 best_error = 0xFFFFFFFFFFULL;
|
||||
|
||||
uint max_s = 4;
|
||||
if (block_type == 1)
|
||||
max_s = 3;
|
||||
|
||||
for (uint s = 0; s < max_s; s++)
|
||||
{
|
||||
uint64 total_error = 0;
|
||||
|
||||
for (uint block_iter = 0; block_iter < block_indices.size(); block_iter++)
|
||||
{
|
||||
const uint block_index = block_indices[block_iter];
|
||||
|
||||
const color_quad_u8& orig_color = m_pBlocks[block_index].m_pixels[y][x];
|
||||
|
||||
const dxt1_block& dst_block = get_block(block_index);
|
||||
|
||||
color_quad_u8 colors[4];
|
||||
dxt1_block::get_block_colors(colors, static_cast<uint16>(dst_block.get_low_color()), static_cast<uint16>(dst_block.get_high_color()));
|
||||
|
||||
uint error = color::color_distance(m_params.m_perceptual, orig_color, colors[s], false);
|
||||
|
||||
total_error += error;
|
||||
}
|
||||
|
||||
if (total_error < best_error)
|
||||
{
|
||||
best_error = total_error;
|
||||
best_s = s;
|
||||
}
|
||||
}
|
||||
|
||||
blk.set_selector(x, y, best_s);
|
||||
|
||||
} // x
|
||||
} // y
|
||||
|
||||
for (uint block_iter = 0; block_iter < block_indices.size(); block_iter++)
|
||||
{
|
||||
const uint block_index = block_indices[block_iter];
|
||||
|
||||
dxt1_block& dst_block = get_block(block_index);
|
||||
|
||||
memcpy(dst_block.m_selectors, blk.m_selectors, sizeof(dst_block.m_selectors));
|
||||
}
|
||||
}
|
||||
|
||||
} // cluster_index
|
||||
}
|
||||
|
||||
bool qdxt1::generate_codebook_progress_callback(uint percentage_completed, void* pData)
|
||||
{
|
||||
return static_cast<qdxt1*>(pData)->update_progress(percentage_completed, 100U);
|
||||
}
|
||||
|
||||
bool qdxt1::create_selector_clusters(uint max_selector_clusters, crnlib::vector< crnlib::vector<uint> >& selector_cluster_indices)
|
||||
{
|
||||
m_progress_start = m_progress_range;
|
||||
m_progress_range = 33;
|
||||
|
||||
weighted_selector_vec_array selector_vecs(m_num_blocks);
|
||||
|
||||
for (uint block_iter = 0; block_iter < m_num_blocks; block_iter++)
|
||||
{
|
||||
dxt1_block& dxt1_block = get_block(block_iter);
|
||||
|
||||
vec16F sv;
|
||||
float* pDst = &sv[0];
|
||||
|
||||
for (uint y = 0; y < 4; y++)
|
||||
for (uint x = 0; x < 4; x++)
|
||||
*pDst++ = g_dxt1_to_linear[dxt1_block.get_selector(x, y)];
|
||||
|
||||
const color_quad_u8 first_color(dxt1_block::unpack_color((uint16)dxt1_block.get_low_color(), true));
|
||||
const color_quad_u8 second_color(dxt1_block::unpack_color((uint16)dxt1_block.get_high_color(), true));
|
||||
const uint dist = color::color_distance(m_params.m_perceptual, first_color, second_color, false);
|
||||
|
||||
const uint cColorDistToWeight = 2000;
|
||||
const uint cMaxWeight = 2048;
|
||||
uint weight = math::clamp<uint>(dist / cColorDistToWeight, 1, cMaxWeight);
|
||||
|
||||
selector_vecs[block_iter].m_vec = sv;
|
||||
selector_vecs[block_iter].m_weight = weight;
|
||||
}
|
||||
|
||||
return m_selector_clusterizer.create_clusters(
|
||||
selector_vecs, max_selector_clusters, selector_cluster_indices, generate_codebook_progress_callback, this);
|
||||
}
|
||||
|
||||
bool qdxt1::pack(dxt1_block* pDst_elements, uint elements_per_block, const qdxt1_params& params, float quality_power_mul)
|
||||
{
|
||||
CRNLIB_ASSERT(m_num_blocks);
|
||||
|
||||
m_main_thread_id = get_current_thread_id();
|
||||
m_canceled = false;
|
||||
|
||||
m_pDst_elements = pDst_elements;
|
||||
m_elements_per_block = elements_per_block;
|
||||
m_params = params;
|
||||
if (!m_params.m_use_alpha_blocks)
|
||||
m_params.m_dxt1a_alpha_threshold = 0;
|
||||
|
||||
m_prev_percentage_complete = -1;
|
||||
|
||||
CRNLIB_ASSERT(m_params.m_quality_level <= qdxt1_params::cMaxQuality);
|
||||
const float quality = m_params.m_quality_level / (float)qdxt1_params::cMaxQuality;
|
||||
const float endpoint_quality = powf(quality, 1.8f * quality_power_mul);
|
||||
const float selector_quality = powf(quality, 1.65f * quality_power_mul);
|
||||
|
||||
//const uint max_endpoint_clusters = math::clamp<uint>(static_cast<uint>(m_endpoint_clusterizer.get_codebook_size() * endpoint_quality), 128U, m_endpoint_clusterizer.get_codebook_size());
|
||||
//const uint max_selector_clusters = math::clamp<uint>(static_cast<uint>(m_max_selector_clusters * selector_quality), 150U, m_max_selector_clusters);
|
||||
const uint max_endpoint_clusters = math::clamp<uint>(static_cast<uint>(m_endpoint_clusterizer.get_codebook_size() * endpoint_quality), 96U, m_endpoint_clusterizer.get_codebook_size());
|
||||
const uint max_selector_clusters = math::clamp<uint>(static_cast<uint>(m_max_selector_clusters * selector_quality), 128U, m_max_selector_clusters);
|
||||
|
||||
if (quality >= 1.0f)
|
||||
{
|
||||
m_endpoint_cluster_indices.resize(m_num_blocks);
|
||||
for (uint i = 0; i < m_num_blocks; i++)
|
||||
{
|
||||
m_endpoint_cluster_indices[i].resize(1);
|
||||
m_endpoint_cluster_indices[i][0] = i;
|
||||
}
|
||||
}
|
||||
else
|
||||
m_endpoint_clusterizer.retrieve_clusters(max_endpoint_clusters, m_endpoint_cluster_indices);
|
||||
|
||||
// trace("endpoint clusters: %u\n", m_endpoint_cluster_indices.size());
|
||||
|
||||
uint total_blocks = 0;
|
||||
uint max_blocks = 0;
|
||||
for (uint i = 0; i < m_endpoint_cluster_indices.size(); i++)
|
||||
{
|
||||
uint num = m_endpoint_cluster_indices[i].size();
|
||||
total_blocks += num;
|
||||
max_blocks = math::maximum(max_blocks, num);
|
||||
}
|
||||
#if 0
|
||||
trace("Num clusters: %u, Average blocks per cluster: %u, Max blocks per cluster: %u\n",
|
||||
m_endpoint_cluster_indices.size(),
|
||||
total_blocks / m_endpoint_cluster_indices.size(),
|
||||
max_blocks);
|
||||
#endif
|
||||
|
||||
crnlib::vector< crnlib::vector<uint> >& selector_cluster_indices = m_cached_selector_cluster_indices[params.m_quality_level];
|
||||
|
||||
m_progress_start = 0;
|
||||
if (quality >= 1.0f)
|
||||
m_progress_range = 100;
|
||||
else if (selector_cluster_indices.empty())
|
||||
m_progress_range = (m_params.m_dxt_quality == cCRNDXTQualitySuperFast) ? 10 : 33;
|
||||
else
|
||||
m_progress_range = (m_params.m_dxt_quality == cCRNDXTQualitySuperFast) ? 10 : 50;
|
||||
|
||||
for (uint i = 0; i <= m_pTask_pool->get_num_threads(); i++)
|
||||
m_pTask_pool->queue_object_task(this, &qdxt1::pack_endpoints_task, i);
|
||||
m_pTask_pool->join();
|
||||
|
||||
if (m_canceled)
|
||||
return false;
|
||||
|
||||
if (quality >= 1.0f)
|
||||
return true;
|
||||
|
||||
if (selector_cluster_indices.empty())
|
||||
{
|
||||
create_selector_clusters(max_selector_clusters, selector_cluster_indices);
|
||||
|
||||
if (m_canceled)
|
||||
{
|
||||
selector_cluster_indices.clear();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_progress_start += m_progress_range;
|
||||
m_progress_range = 100 - m_progress_start;
|
||||
|
||||
optimize_selectors_params optimize_selectors_task_params(selector_cluster_indices);
|
||||
|
||||
for (uint i = 0; i <= m_pTask_pool->get_num_threads(); i++)
|
||||
m_pTask_pool->queue_object_task(this, &qdxt1::optimize_selectors_task, i, &optimize_selectors_task_params);
|
||||
|
||||
m_pTask_pool->join();
|
||||
|
||||
return !m_canceled;
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
// File: crn_qdxt1.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
#include "crn_dxt.h"
|
||||
#include "crn_task_pool.h"
|
||||
#include "crn_spinlock.h"
|
||||
#include "crn_hash_map.h"
|
||||
#include "crn_clusterizer.h"
|
||||
#include "crn_hash.h"
|
||||
#include "crn_threaded_clusterizer.h"
|
||||
#include "crn_dxt_image.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
struct qdxt1_params
|
||||
{
|
||||
qdxt1_params()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
m_quality_level = cMaxQuality;
|
||||
m_dxt_quality = cCRNDXTQualityUber;
|
||||
m_perceptual = true;
|
||||
m_dxt1a_alpha_threshold = 0;
|
||||
m_use_alpha_blocks = true;
|
||||
m_pProgress_func = NULL;
|
||||
m_pProgress_data = NULL;
|
||||
m_num_mips = 0;
|
||||
m_hierarchical = true;
|
||||
utils::zero_object(m_mip_desc);
|
||||
m_progress_start = 0;
|
||||
m_progress_range = 100;
|
||||
}
|
||||
|
||||
void init(const dxt_image::pack_params &pp, int quality_level, bool hierarchical)
|
||||
{
|
||||
m_dxt_quality = pp.m_quality;
|
||||
m_hierarchical = hierarchical;
|
||||
m_perceptual = pp.m_perceptual;
|
||||
m_use_alpha_blocks = pp.m_use_both_block_types;
|
||||
m_quality_level = quality_level;
|
||||
m_dxt1a_alpha_threshold = pp.m_dxt1a_alpha_threshold;
|
||||
}
|
||||
|
||||
enum { cMaxQuality = cCRNMaxQualityLevel };
|
||||
uint m_quality_level;
|
||||
|
||||
uint m_dxt1a_alpha_threshold;
|
||||
crn_dxt_quality m_dxt_quality;
|
||||
bool m_perceptual;
|
||||
bool m_use_alpha_blocks;
|
||||
bool m_hierarchical;
|
||||
|
||||
struct mip_desc
|
||||
{
|
||||
uint m_first_block;
|
||||
uint m_block_width;
|
||||
uint m_block_height;
|
||||
};
|
||||
|
||||
uint m_num_mips;
|
||||
enum { cMaxMips = 128 };
|
||||
mip_desc m_mip_desc[cMaxMips];
|
||||
|
||||
typedef bool (*progress_callback_func)(uint percentage_completed, void* pProgress_data);
|
||||
progress_callback_func m_pProgress_func;
|
||||
void* m_pProgress_data;
|
||||
uint m_progress_start;
|
||||
uint m_progress_range;
|
||||
};
|
||||
|
||||
class qdxt1
|
||||
{
|
||||
CRNLIB_NO_COPY_OR_ASSIGNMENT_OP(qdxt1);
|
||||
|
||||
public:
|
||||
qdxt1(task_pool& task_pool);
|
||||
~qdxt1();
|
||||
|
||||
void clear();
|
||||
|
||||
bool init(uint n, const dxt_pixel_block* pBlocks, const qdxt1_params& params);
|
||||
|
||||
uint get_num_blocks() const { return m_num_blocks; }
|
||||
const dxt_pixel_block* get_blocks() const { return m_pBlocks; }
|
||||
|
||||
bool pack(dxt1_block* pDst_elements, uint elements_per_block, const qdxt1_params& params, float quality_power_mul);
|
||||
|
||||
private:
|
||||
task_pool* m_pTask_pool;
|
||||
uint32 m_main_thread_id;
|
||||
bool m_canceled;
|
||||
|
||||
uint m_progress_start;
|
||||
uint m_progress_range;
|
||||
|
||||
uint m_num_blocks;
|
||||
const dxt_pixel_block* m_pBlocks;
|
||||
|
||||
dxt1_block* m_pDst_elements;
|
||||
uint m_elements_per_block;
|
||||
qdxt1_params m_params;
|
||||
|
||||
uint m_max_selector_clusters;
|
||||
|
||||
int m_prev_percentage_complete;
|
||||
|
||||
typedef vec<6, float> vec6F;
|
||||
typedef clusterizer<vec6F> vec6F_clusterizer;
|
||||
vec6F_clusterizer m_endpoint_clusterizer;
|
||||
|
||||
crnlib::vector< crnlib::vector<uint> > m_endpoint_cluster_indices;
|
||||
|
||||
typedef vec<16, float> vec16F;
|
||||
typedef threaded_clusterizer<vec16F> vec16F_clusterizer;
|
||||
|
||||
typedef vec16F_clusterizer::weighted_vec weighted_selector_vec;
|
||||
typedef vec16F_clusterizer::weighted_vec_array weighted_selector_vec_array;
|
||||
|
||||
vec16F_clusterizer m_selector_clusterizer;
|
||||
|
||||
crnlib::vector< crnlib::vector<uint> > m_cached_selector_cluster_indices[qdxt1_params::cMaxQuality + 1];
|
||||
|
||||
struct cluster_id
|
||||
{
|
||||
cluster_id() : m_hash(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
cluster_id(const crnlib::vector<uint>& indices)
|
||||
{
|
||||
set(indices);
|
||||
}
|
||||
|
||||
void set(const crnlib::vector<uint>& indices)
|
||||
{
|
||||
m_cells.resize(indices.size());
|
||||
|
||||
for (uint i = 0; i < indices.size(); i++)
|
||||
m_cells[i] = static_cast<uint32>(indices[i]);
|
||||
|
||||
std::sort(m_cells.begin(), m_cells.end());
|
||||
|
||||
m_hash = fast_hash(&m_cells[0], sizeof(m_cells[0]) * m_cells.size());
|
||||
}
|
||||
|
||||
bool operator< (const cluster_id& rhs) const
|
||||
{
|
||||
return m_cells < rhs.m_cells;
|
||||
}
|
||||
|
||||
bool operator== (const cluster_id& rhs) const
|
||||
{
|
||||
if (m_hash != rhs.m_hash)
|
||||
return false;
|
||||
|
||||
return m_cells == rhs.m_cells;
|
||||
}
|
||||
|
||||
crnlib::vector<uint32> m_cells;
|
||||
|
||||
size_t m_hash;
|
||||
|
||||
operator size_t() const { return m_hash; }
|
||||
};
|
||||
|
||||
typedef crnlib::hash_map<cluster_id, uint> cluster_hash;
|
||||
cluster_hash m_cluster_hash;
|
||||
spinlock m_cluster_hash_lock;
|
||||
|
||||
static bool generate_codebook_dummy_progress_callback(uint percentage_completed, void* pData);
|
||||
static bool generate_codebook_progress_callback(uint percentage_completed, void* pData);
|
||||
bool update_progress(uint value, uint max_value);
|
||||
void pack_endpoints_task(uint64 data, void* pData_ptr);
|
||||
void optimize_selectors_task(uint64 data, void* pData_ptr);
|
||||
bool create_selector_clusters(uint max_selector_clusters, crnlib::vector< crnlib::vector<uint> >& selector_cluster_indices);
|
||||
|
||||
inline dxt1_block& get_block(uint index) const { return m_pDst_elements[index * m_elements_per_block]; }
|
||||
};
|
||||
|
||||
CRNLIB_DEFINE_BITWISE_MOVABLE(qdxt1::cluster_id);
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,826 @@
|
||||
// File: crn_qdxt5.cpp
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#include "crn_core.h"
|
||||
#include "crn_qdxt5.h"
|
||||
#include "crn_dxt5a.h"
|
||||
#include "crn_image.h"
|
||||
#include "crn_image_utils.h"
|
||||
#include "crn_dxt_fast.h"
|
||||
#include "crn_dxt_hc_common.h"
|
||||
|
||||
#define QDXT5_DEBUGGING 0
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
qdxt5::qdxt5(task_pool& task_pool) :
|
||||
m_pTask_pool(&task_pool),
|
||||
m_main_thread_id(0),
|
||||
m_canceled(false),
|
||||
m_progress_start(0),
|
||||
m_progress_range(100),
|
||||
m_num_blocks(0),
|
||||
m_pBlocks(NULL),
|
||||
m_pDst_elements(NULL),
|
||||
m_elements_per_block(0),
|
||||
m_max_selector_clusters(0),
|
||||
m_prev_percentage_complete(-1),
|
||||
m_selector_clusterizer(task_pool)
|
||||
{
|
||||
}
|
||||
|
||||
qdxt5::~qdxt5()
|
||||
{
|
||||
}
|
||||
|
||||
void qdxt5::clear()
|
||||
{
|
||||
m_main_thread_id = 0;
|
||||
m_num_blocks = 0;
|
||||
m_pBlocks = 0;
|
||||
m_pDst_elements = NULL;
|
||||
m_elements_per_block = 0;
|
||||
m_params.clear();
|
||||
m_endpoint_clusterizer.clear();
|
||||
m_endpoint_cluster_indices.clear();
|
||||
m_max_selector_clusters = 0;
|
||||
m_canceled = false;
|
||||
m_progress_start = 0;
|
||||
m_progress_range = 100;
|
||||
m_selector_clusterizer.clear();
|
||||
|
||||
for (uint i = 0; i <= qdxt5_params::cMaxQuality; i++)
|
||||
m_cached_selector_cluster_indices[i].clear();
|
||||
|
||||
m_cluster_hash.clear();
|
||||
|
||||
m_prev_percentage_complete = -1;
|
||||
}
|
||||
|
||||
bool qdxt5::init(uint n, const dxt_pixel_block* pBlocks, const qdxt5_params& params)
|
||||
{
|
||||
clear();
|
||||
|
||||
CRNLIB_ASSERT(n && pBlocks);
|
||||
|
||||
m_main_thread_id = get_current_thread_id();
|
||||
|
||||
m_num_blocks = n;
|
||||
m_pBlocks = pBlocks;
|
||||
m_params = params;
|
||||
|
||||
m_endpoint_clusterizer.reserve_training_vecs(m_num_blocks);
|
||||
|
||||
m_progress_start = 0;
|
||||
m_progress_range = 75;
|
||||
|
||||
image_u8 debug_img;
|
||||
|
||||
const bool debugging = true;
|
||||
|
||||
if ((m_params.m_hierarchical) && (m_params.m_num_mips))
|
||||
{
|
||||
vec2F_clusterizer::training_vec_array& training_vecs = m_endpoint_clusterizer.get_training_vecs();
|
||||
training_vecs.resize(m_num_blocks);
|
||||
|
||||
uint encoding_hist[cNumChunkEncodings];
|
||||
utils::zero_object(encoding_hist);
|
||||
|
||||
uint total_processed_blocks = 0;
|
||||
uint next_progress_threshold = 512;
|
||||
|
||||
for (uint level = 0; level < m_params.m_num_mips; level++)
|
||||
{
|
||||
const qdxt5_params::mip_desc& level_desc = m_params.m_mip_desc[level];
|
||||
|
||||
const uint num_chunks_x = (level_desc.m_block_width + cChunkBlockWidth - 1) / cChunkBlockWidth;
|
||||
const uint num_chunks_y = (level_desc.m_block_height + cChunkBlockHeight - 1) / cChunkBlockHeight;
|
||||
|
||||
const uint level_width = level_desc.m_block_width * 4;
|
||||
const uint level_height = level_desc.m_block_height * 4;
|
||||
|
||||
if (debugging)
|
||||
debug_img.resize(num_chunks_x * cChunkPixelWidth, num_chunks_y * cChunkPixelHeight);
|
||||
|
||||
for (uint chunk_y = 0; chunk_y < num_chunks_y; chunk_y++)
|
||||
{
|
||||
for (uint chunk_x = 0; chunk_x < num_chunks_x; chunk_x++)
|
||||
{
|
||||
color_quad_u8 chunk_pixels[cChunkPixelWidth * cChunkPixelHeight];
|
||||
|
||||
for (uint y = 0; y < cChunkPixelHeight; y++)
|
||||
{
|
||||
const uint pix_y = math::minimum<uint>(chunk_y * cChunkPixelHeight + y, level_height - 1);
|
||||
|
||||
const uint outer_block_index = level_desc.m_first_block + ((pix_y >> 2) * level_desc.m_block_width);
|
||||
|
||||
for (uint x = 0; x < cChunkPixelWidth; x++)
|
||||
{
|
||||
const uint pix_x = math::minimum<uint>(chunk_x * cChunkPixelWidth + x, level_width - 1);
|
||||
|
||||
const uint block_index = outer_block_index + (pix_x >> 2);
|
||||
|
||||
const dxt_pixel_block& block = m_pBlocks[block_index];
|
||||
|
||||
const color_quad_u8& p = block.m_pixels[pix_y & 3][pix_x & 3];
|
||||
|
||||
chunk_pixels[x + y * 8] = p;
|
||||
}
|
||||
}
|
||||
|
||||
struct layout_results
|
||||
{
|
||||
uint m_low_color;
|
||||
uint m_high_color;
|
||||
uint8 m_selectors[cChunkPixelWidth * cChunkPixelHeight];
|
||||
uint64 m_error;
|
||||
//float m_penalty;
|
||||
};
|
||||
layout_results layouts[cNumChunkTileLayouts];
|
||||
|
||||
for (uint l = 0; l < cNumChunkTileLayouts; l++)
|
||||
{
|
||||
const uint width = g_chunk_tile_layouts[l].m_width;
|
||||
const uint height = g_chunk_tile_layouts[l].m_height;
|
||||
const uint x_ofs = g_chunk_tile_layouts[l].m_x_ofs;
|
||||
const uint y_ofs = g_chunk_tile_layouts[l].m_y_ofs;
|
||||
|
||||
color_quad_u8 layout_pixels[cChunkPixelWidth * cChunkPixelHeight];
|
||||
for (uint y = 0; y < height; y++)
|
||||
for (uint x = 0; x < width; x++)
|
||||
layout_pixels[x + y * width] = chunk_pixels[(x_ofs + x) + (y_ofs + y) * cChunkPixelWidth];
|
||||
|
||||
const uint n = width * height;
|
||||
dxt_fast::compress_alpha_block(n, layout_pixels, layouts[l].m_low_color, layouts[l].m_high_color, layouts[l].m_selectors, m_params.m_comp_index);
|
||||
|
||||
uint c[dxt5_block::cMaxSelectorValues];
|
||||
dxt5_block::get_block_values(c, layouts[l].m_low_color, layouts[l].m_high_color);
|
||||
|
||||
uint64 error = 0;
|
||||
for (uint i = 0; i < n; i++)
|
||||
error += math::square((int)layout_pixels[i][m_params.m_comp_index] - (int)c[layouts[l].m_selectors[i]]);
|
||||
|
||||
layouts[l].m_error = error;
|
||||
}
|
||||
|
||||
double best_peak_snr = -1.0f;
|
||||
uint best_encoding = 0;
|
||||
|
||||
for (uint e = 0; e < cNumChunkEncodings; e++)
|
||||
{
|
||||
const chunk_encoding_desc& encoding_desc = g_chunk_encodings[e];
|
||||
|
||||
double total_error = 0;
|
||||
|
||||
for (uint t = 0; t < encoding_desc.m_num_tiles; t++)
|
||||
total_error += (double)layouts[encoding_desc.m_tiles[t].m_layout_index].m_error;
|
||||
|
||||
double mean_squared = total_error * (1.0f / 64.0f);
|
||||
double root_mean_squared = sqrt(mean_squared);
|
||||
|
||||
double peak_snr = 999999.0f;
|
||||
if (mean_squared)
|
||||
peak_snr = math::clamp<double>(log10(255.0f / root_mean_squared) * 20.0f, 0.0f, 500.0f);
|
||||
|
||||
float adaptive_tile_alpha_psnr_derating = 2.4f;
|
||||
//if (level)
|
||||
// adaptive_tile_alpha_psnr_derating = math::lerp(adaptive_tile_alpha_psnr_derating * .5f, .3f, math::maximum((level - 1) / float(m_params.m_num_mips - 2), 1.0f));
|
||||
if ((level) && (adaptive_tile_alpha_psnr_derating > .25f))
|
||||
{
|
||||
adaptive_tile_alpha_psnr_derating = math::maximum(.25f, adaptive_tile_alpha_psnr_derating / powf(3.0f, static_cast<float>(level)));
|
||||
}
|
||||
|
||||
float alpha_derating = math::lerp( 0.0f, adaptive_tile_alpha_psnr_derating, (g_chunk_encodings[e].m_num_tiles - 1) / 3.0f );
|
||||
peak_snr = peak_snr - alpha_derating;
|
||||
|
||||
//for (uint t = 0; t < encoding_desc.m_num_tiles; t++)
|
||||
// peak_snr -= (double)layouts[encoding_desc.m_tiles[t].m_layout_index].m_penalty;
|
||||
|
||||
if (peak_snr > best_peak_snr)
|
||||
{
|
||||
best_peak_snr = peak_snr;
|
||||
best_encoding = e;
|
||||
}
|
||||
}
|
||||
|
||||
encoding_hist[best_encoding]++;
|
||||
|
||||
const chunk_encoding_desc& encoding_desc = g_chunk_encodings[best_encoding];
|
||||
|
||||
for (uint t = 0; t < encoding_desc.m_num_tiles; t++)
|
||||
{
|
||||
const chunk_tile_desc& tile_desc = encoding_desc.m_tiles[t];
|
||||
|
||||
uint layout_index = tile_desc.m_layout_index;
|
||||
const layout_results& layout = layouts[layout_index];
|
||||
|
||||
uint c[dxt5_block::cMaxSelectorValues];
|
||||
if (debugging)
|
||||
dxt5_block::get_block_values(c, layout.m_low_color, layout.m_high_color);
|
||||
|
||||
color_quad_u8 tile_pixels[cChunkPixelWidth * cChunkPixelHeight];
|
||||
|
||||
for (uint y = 0; y < tile_desc.m_height; y++)
|
||||
{
|
||||
const uint pix_y = y + tile_desc.m_y_ofs;
|
||||
|
||||
for (uint x = 0; x < tile_desc.m_width; x++)
|
||||
{
|
||||
const uint pix_x = x + tile_desc.m_x_ofs;
|
||||
|
||||
uint a = chunk_pixels[pix_x + pix_y * cChunkPixelWidth][m_params.m_comp_index];
|
||||
|
||||
tile_pixels[x + y * tile_desc.m_width].set(a, a, a, 255);
|
||||
|
||||
if (debugging)
|
||||
debug_img(chunk_x * 8 + pix_x, chunk_y * 8 + pix_y) = c[layout.m_selectors[x + y * tile_desc.m_width]];
|
||||
}
|
||||
}
|
||||
|
||||
color_quad_u8 l, h;
|
||||
dxt_fast::find_representative_colors(tile_desc.m_width * tile_desc.m_height, tile_pixels, l, h);
|
||||
|
||||
const uint dist = math::square<int>((int)l[0] - (int)h[0]);
|
||||
|
||||
const int cAlphaErrorToWeight = 8;
|
||||
const uint cMaxWeight = 8;
|
||||
uint weight = math::clamp<uint>(dist / cAlphaErrorToWeight, 1, cMaxWeight);
|
||||
|
||||
vec2F ev;
|
||||
|
||||
ev[0] = l[0];
|
||||
ev[1] = h[0];
|
||||
|
||||
for (uint y = 0; y < (tile_desc.m_height >> 2); y++)
|
||||
{
|
||||
uint block_y = chunk_y * cChunkBlockHeight + y + (tile_desc.m_y_ofs >> 2);
|
||||
if (block_y >= level_desc.m_block_height)
|
||||
continue;
|
||||
|
||||
for (uint x = 0; x < (tile_desc.m_width >> 2); x++)
|
||||
{
|
||||
uint block_x = chunk_x * cChunkBlockWidth + x + (tile_desc.m_x_ofs >> 2);
|
||||
if (block_x >= level_desc.m_block_width)
|
||||
break;
|
||||
|
||||
uint block_index = level_desc.m_first_block + block_x + block_y * level_desc.m_block_width;
|
||||
|
||||
training_vecs[block_index].first = ev;
|
||||
training_vecs[block_index].second = weight;
|
||||
|
||||
total_processed_blocks++;
|
||||
|
||||
} // x
|
||||
} // y
|
||||
} //t
|
||||
|
||||
if (total_processed_blocks >= next_progress_threshold)
|
||||
{
|
||||
next_progress_threshold += 512;
|
||||
|
||||
if (!update_progress(total_processed_blocks, m_num_blocks - 1))
|
||||
return false;
|
||||
}
|
||||
|
||||
} // chunk_x
|
||||
} // chunk_y
|
||||
|
||||
#if QDXT5_DEBUGGING
|
||||
if (debugging)
|
||||
image_utils::save_to_file_stb(dynamic_wstring(cVarArg, L"debug_%u.tga", level).get_ptr(), debug_img, image_utils::cSaveIgnoreAlpha);
|
||||
#endif
|
||||
|
||||
} // level
|
||||
|
||||
#if 0
|
||||
trace("chunk encoding hist: ");
|
||||
for (uint i = 0; i < cNumChunkEncodings; i++)
|
||||
trace("%u ", encoding_hist[i]);
|
||||
trace("\n");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
for (uint block_index = 0; block_index < m_num_blocks; block_index++)
|
||||
{
|
||||
if ((block_index & 511) == 0)
|
||||
{
|
||||
if (!update_progress(block_index, m_num_blocks - 1))
|
||||
return false;
|
||||
}
|
||||
|
||||
color_quad_u8 c[16];
|
||||
for (uint y = 0; y < cDXTBlockSize; y++)
|
||||
for (uint x = 0; x < cDXTBlockSize; x++)
|
||||
c[x+y*cDXTBlockSize].set(m_pBlocks[block_index].m_pixels[y][x][m_params.m_comp_index], 255);
|
||||
|
||||
color_quad_u8 l, h;
|
||||
dxt_fast::find_representative_colors(cDXTBlockSize * cDXTBlockSize, c, l, h);
|
||||
|
||||
const uint dist = math::square<int>((int)l[0] - (int)h[0]);
|
||||
|
||||
const int cAlphaErrorToWeight = 8;
|
||||
const uint cMaxWeight = 8;
|
||||
uint weight = math::clamp<uint>(dist / cAlphaErrorToWeight, 1, cMaxWeight);
|
||||
|
||||
vec2F ev;
|
||||
|
||||
ev[0] = l[0];
|
||||
ev[1] = h[0];
|
||||
|
||||
m_endpoint_clusterizer.add_training_vec(ev, weight);
|
||||
}
|
||||
}
|
||||
|
||||
const uint cMaxEndpointClusters = 65535U;
|
||||
|
||||
m_progress_start = 75;
|
||||
m_progress_range = 20;
|
||||
|
||||
if (!m_endpoint_clusterizer.generate_codebook(cMaxEndpointClusters, generate_codebook_progress_callback, this))
|
||||
return false;
|
||||
|
||||
crnlib::hash_map<uint64, empty_type> selector_hash;
|
||||
|
||||
m_progress_start = 95;
|
||||
m_progress_range = 5;
|
||||
|
||||
for (uint block_index = 0; block_index < m_num_blocks; block_index++)
|
||||
{
|
||||
if ((block_index & 511) == 0)
|
||||
{
|
||||
if (!update_progress(block_index, m_num_blocks - 1))
|
||||
return false;
|
||||
}
|
||||
|
||||
dxt5_block dxt_blk;
|
||||
dxt_fast::compress_alpha_block(&dxt_blk, &m_pBlocks[block_index].m_pixels[0][0], m_params.m_comp_index);
|
||||
|
||||
uint64 selectors = 0;
|
||||
for (uint i = 0; i < dxt5_block::cNumSelectorBytes; i++)
|
||||
selectors |= static_cast<uint64>(dxt_blk.m_selectors[i]) << (i * 8U);
|
||||
|
||||
selector_hash.insert(selectors);
|
||||
}
|
||||
|
||||
m_max_selector_clusters = selector_hash.size() + 128;
|
||||
|
||||
update_progress(1, 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool qdxt5::update_progress(uint value, uint max_value)
|
||||
{
|
||||
if (!m_params.m_pProgress_func)
|
||||
return true;
|
||||
|
||||
uint percentage = max_value ? (m_progress_start + (value * m_progress_range + (max_value / 2)) / max_value) : 100;
|
||||
if ((int)percentage == m_prev_percentage_complete)
|
||||
return true;
|
||||
m_prev_percentage_complete = percentage;
|
||||
|
||||
if (!m_params.m_pProgress_func(m_params.m_progress_start + (percentage * m_params.m_progress_range) / 100U, m_params.m_pProgress_data))
|
||||
{
|
||||
m_canceled = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void qdxt5::pack_endpoints_task(uint64 data, void* pData_ptr)
|
||||
{
|
||||
pData_ptr;
|
||||
const uint thread_index = static_cast<uint>(data);
|
||||
|
||||
crnlib::vector<color_quad_u8> cluster_pixels;
|
||||
cluster_pixels.reserve(1024);
|
||||
|
||||
crnlib::vector<uint8> selectors;
|
||||
selectors.reserve(1024);
|
||||
|
||||
dxt5_endpoint_optimizer optimizer;
|
||||
dxt5_endpoint_optimizer::params p;
|
||||
dxt5_endpoint_optimizer::results r;
|
||||
|
||||
p.m_quality = m_params.m_dxt_quality;
|
||||
p.m_comp_index = m_params.m_comp_index;
|
||||
p.m_use_both_block_types = m_params.m_use_both_block_types;
|
||||
|
||||
uint cluster_index_progress_mask = math::next_pow2(m_endpoint_cluster_indices.size() / 100);
|
||||
cluster_index_progress_mask /= 2;
|
||||
cluster_index_progress_mask = math::maximum<uint>(cluster_index_progress_mask, 8);
|
||||
cluster_index_progress_mask -= 1;
|
||||
|
||||
for (uint cluster_index = 0; cluster_index < m_endpoint_cluster_indices.size(); cluster_index++)
|
||||
{
|
||||
if (m_canceled)
|
||||
return;
|
||||
|
||||
if ((cluster_index & cluster_index_progress_mask) == 0)
|
||||
{
|
||||
if (get_current_thread_id() == m_main_thread_id)
|
||||
{
|
||||
if (!update_progress(cluster_index, m_endpoint_cluster_indices.size() - 1))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_pTask_pool->get_num_threads())
|
||||
{
|
||||
if ((cluster_index % (m_pTask_pool->get_num_threads() + 1)) != thread_index)
|
||||
continue;
|
||||
}
|
||||
|
||||
const crnlib::vector<uint>& cluster_indices = m_endpoint_cluster_indices[cluster_index];
|
||||
|
||||
selectors.resize(cluster_indices.size() * cDXTBlockSize * cDXTBlockSize);
|
||||
|
||||
cluster_pixels.resize(cluster_indices.size() * cDXTBlockSize * cDXTBlockSize);
|
||||
|
||||
color_quad_u8* pDst = &cluster_pixels[0];
|
||||
|
||||
for (uint block_iter = 0; block_iter < cluster_indices.size(); block_iter++)
|
||||
{
|
||||
const uint block_index = cluster_indices[block_iter];
|
||||
|
||||
const color_quad_u8* pSrc_pixels = &m_pBlocks[block_index].m_pixels[0][0];
|
||||
|
||||
for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
|
||||
{
|
||||
const color_quad_u8& src = pSrc_pixels[i];
|
||||
|
||||
*pDst++ = src;
|
||||
}
|
||||
}
|
||||
|
||||
p.m_block_index = cluster_index;
|
||||
p.m_num_pixels = cluster_pixels.size();
|
||||
p.m_pPixels = cluster_pixels.begin();
|
||||
|
||||
r.m_pSelectors = selectors.begin();
|
||||
|
||||
uint low_color;
|
||||
uint high_color;
|
||||
if (m_params.m_dxt_quality != cCRNDXTQualitySuperFast)
|
||||
{
|
||||
optimizer.compute(p, r);
|
||||
low_color = r.m_first_endpoint;
|
||||
high_color = r.m_second_endpoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
dxt_fast::compress_alpha_block(cluster_pixels.size(), cluster_pixels.begin(), low_color, high_color, selectors.begin(), m_params.m_comp_index);
|
||||
}
|
||||
|
||||
const uint8* pSrc_selectors = selectors.begin();
|
||||
|
||||
for (uint block_iter = 0; block_iter < cluster_indices.size(); block_iter++)
|
||||
{
|
||||
const uint block_index = cluster_indices[block_iter];
|
||||
|
||||
dxt5_block& dxt_block = get_block(block_index);
|
||||
|
||||
dxt_block.set_low_alpha(low_color);
|
||||
dxt_block.set_high_alpha(high_color);
|
||||
|
||||
for (uint y = 0; y < 4; y++)
|
||||
for (uint x = 0; x < 4; x++)
|
||||
dxt_block.set_selector(x, y, *pSrc_selectors++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct optimize_selectors_params
|
||||
{
|
||||
CRNLIB_NO_COPY_OR_ASSIGNMENT_OP(optimize_selectors_params);
|
||||
|
||||
optimize_selectors_params(
|
||||
crnlib::vector< crnlib::vector<uint> >& selector_cluster_indices) :
|
||||
m_selector_cluster_indices(selector_cluster_indices)
|
||||
{
|
||||
}
|
||||
|
||||
crnlib::vector< crnlib::vector<uint> >& m_selector_cluster_indices;
|
||||
};
|
||||
|
||||
void qdxt5::optimize_selectors_task(uint64 data, void* pData_ptr)
|
||||
{
|
||||
const uint thread_index = static_cast<uint>(data);
|
||||
|
||||
optimize_selectors_params& task_params = *static_cast<optimize_selectors_params*>(pData_ptr);
|
||||
|
||||
crnlib::vector<uint> block_categories[2];
|
||||
block_categories[0].reserve(2048);
|
||||
block_categories[1].reserve(2048);
|
||||
|
||||
for (uint cluster_index = 0; cluster_index < task_params.m_selector_cluster_indices.size(); cluster_index++)
|
||||
{
|
||||
if (m_canceled)
|
||||
return;
|
||||
|
||||
if ((cluster_index & 255) == 0)
|
||||
{
|
||||
if (get_current_thread_id() == m_main_thread_id)
|
||||
{
|
||||
if (!update_progress(cluster_index, task_params.m_selector_cluster_indices.size() - 1))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_pTask_pool->get_num_threads())
|
||||
{
|
||||
if ((cluster_index % (m_pTask_pool->get_num_threads() + 1)) != thread_index)
|
||||
continue;
|
||||
}
|
||||
|
||||
const crnlib::vector<uint>& selector_indices = task_params.m_selector_cluster_indices[cluster_index];
|
||||
|
||||
if (selector_indices.size() <= 1)
|
||||
continue;
|
||||
|
||||
block_categories[0].resize(0);
|
||||
block_categories[1].resize(0);
|
||||
|
||||
for (uint block_iter = 0; block_iter < selector_indices.size(); block_iter++)
|
||||
{
|
||||
const uint block_index = selector_indices[block_iter];
|
||||
|
||||
const dxt5_block& src_block = get_block(block_index);
|
||||
|
||||
block_categories[src_block.is_alpha6_block()].push_back(block_index);
|
||||
}
|
||||
|
||||
dxt5_block blk;
|
||||
utils::zero_object(blk);
|
||||
|
||||
for (uint block_type = 0; block_type <= 1; block_type++)
|
||||
{
|
||||
const crnlib::vector<uint>& block_indices = block_categories[block_type];
|
||||
if (block_indices.size() <= 1)
|
||||
continue;
|
||||
|
||||
for (uint y = 0; y < cDXTBlockSize; y++)
|
||||
{
|
||||
for (uint x = 0; x < cDXTBlockSize; x++)
|
||||
{
|
||||
uint best_s = 0;
|
||||
uint64 best_error = 0xFFFFFFFFFFULL;
|
||||
|
||||
for (uint s = 0; s < dxt5_block::cMaxSelectorValues; s++)
|
||||
{
|
||||
uint64 total_error = 0;
|
||||
|
||||
for (uint block_iter = 0; block_iter < block_indices.size(); block_iter++)
|
||||
{
|
||||
const uint block_index = block_indices[block_iter];
|
||||
|
||||
const color_quad_u8& orig_color = m_pBlocks[block_index].m_pixels[y][x];
|
||||
|
||||
const dxt5_block& dst_block = get_block(block_index);
|
||||
|
||||
uint values[dxt5_block::cMaxSelectorValues];
|
||||
dxt5_block::get_block_values(values, dst_block.get_low_alpha(), dst_block.get_high_alpha());
|
||||
|
||||
int error = math::square((int)orig_color[m_params.m_comp_index] - (int)values[s]);
|
||||
|
||||
total_error += error;
|
||||
}
|
||||
|
||||
if (total_error < best_error)
|
||||
{
|
||||
best_error = total_error;
|
||||
best_s = s;
|
||||
}
|
||||
}
|
||||
|
||||
blk.set_selector(x, y, best_s);
|
||||
|
||||
} // x
|
||||
} // y
|
||||
|
||||
for (uint block_iter = 0; block_iter < block_indices.size(); block_iter++)
|
||||
{
|
||||
const uint block_index = block_indices[block_iter];
|
||||
|
||||
dxt5_block& dst_block = get_block(block_index);
|
||||
|
||||
memcpy(dst_block.m_selectors, blk.m_selectors, sizeof(dst_block.m_selectors));
|
||||
}
|
||||
}
|
||||
|
||||
} // cluster_index
|
||||
}
|
||||
|
||||
bool qdxt5::generate_codebook_progress_callback(uint percentage_completed, void* pData)
|
||||
{
|
||||
return static_cast<qdxt5*>(pData)->update_progress(percentage_completed, 100U);
|
||||
}
|
||||
|
||||
bool qdxt5::create_selector_clusters(uint max_selector_clusters, crnlib::vector< crnlib::vector<uint> >& selector_cluster_indices)
|
||||
{
|
||||
weighted_selector_vec_array selector_vecs[2];
|
||||
crnlib::vector<uint> selector_vec_remap[2];
|
||||
|
||||
for (uint block_type = 0; block_type < 2; block_type++)
|
||||
{
|
||||
for (uint block_iter = 0; block_iter < m_num_blocks; block_iter++)
|
||||
{
|
||||
dxt5_block& dxt5_block = get_block(block_iter);
|
||||
if ((uint)dxt5_block.is_alpha6_block() != block_type)
|
||||
continue;
|
||||
|
||||
vec16F sv;
|
||||
float* pDst = &sv[0];
|
||||
|
||||
bool uses_absolute_values = false;
|
||||
|
||||
for (uint y = 0; y < 4; y++)
|
||||
{
|
||||
for (uint x = 0; x < 4; x++)
|
||||
{
|
||||
const uint s = dxt5_block.get_selector(x, y);
|
||||
|
||||
float f;
|
||||
if (dxt5_block.is_alpha6_block())
|
||||
{
|
||||
if (s >= 6)
|
||||
{
|
||||
uses_absolute_values = true;
|
||||
f = 0.0f;
|
||||
}
|
||||
else
|
||||
f = g_dxt5_alpha6_to_linear[s];
|
||||
}
|
||||
else
|
||||
f = g_dxt5_to_linear[s];
|
||||
|
||||
*pDst++ = f;
|
||||
}
|
||||
}
|
||||
|
||||
if (uses_absolute_values)
|
||||
continue;
|
||||
|
||||
int low_alpha = dxt5_block.get_low_alpha();
|
||||
int high_alpha = dxt5_block.get_high_alpha();
|
||||
int dist = math::square(low_alpha - high_alpha);
|
||||
|
||||
const uint cAlphaDistToWeight = 8;
|
||||
const uint cMaxWeight = 2048;
|
||||
uint weight = math::clamp<uint>(dist / cAlphaDistToWeight, 1, cMaxWeight);
|
||||
|
||||
selector_vecs[block_type].resize(selector_vecs[block_type].size() + 1);
|
||||
selector_vecs[block_type].back().m_vec = sv;
|
||||
selector_vecs[block_type].back().m_weight = weight;
|
||||
|
||||
selector_vec_remap[block_type].push_back(block_iter);
|
||||
}
|
||||
}
|
||||
|
||||
selector_cluster_indices.clear();
|
||||
|
||||
for (uint block_type = 0; block_type < 2; block_type++)
|
||||
{
|
||||
if (selector_vecs[block_type].empty())
|
||||
continue;
|
||||
|
||||
if ((selector_vecs[block_type].size() / (float)m_num_blocks) < .01f)
|
||||
continue;
|
||||
uint max_clusters = static_cast<uint>((math::emulu(selector_vecs[block_type].size(), max_selector_clusters) + (m_num_blocks - 1)) / m_num_blocks);
|
||||
max_clusters = math::minimum(math::maximum(64U, max_clusters), selector_vecs[block_type].size());
|
||||
if (max_clusters >= selector_vecs[block_type].size())
|
||||
continue;
|
||||
|
||||
#if QDXT5_DEBUGGING
|
||||
trace("max_clusters (%u): %u\n", block_type, max_clusters);
|
||||
#endif
|
||||
|
||||
crnlib::vector< crnlib::vector<uint> > block_type_selector_cluster_indices;
|
||||
|
||||
if (!block_type)
|
||||
{
|
||||
m_progress_start = m_progress_range;
|
||||
m_progress_range = 16;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_progress_start = m_progress_range + 16;
|
||||
m_progress_range = 17;
|
||||
}
|
||||
|
||||
if (!m_selector_clusterizer.create_clusters(
|
||||
selector_vecs[block_type], max_clusters, block_type_selector_cluster_indices, generate_codebook_progress_callback, this))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint first_cluster = selector_cluster_indices.size();
|
||||
selector_cluster_indices.enlarge(block_type_selector_cluster_indices.size());
|
||||
|
||||
for (uint i = 0; i < block_type_selector_cluster_indices.size(); i++)
|
||||
{
|
||||
crnlib::vector<uint>& indices = selector_cluster_indices[first_cluster + i];
|
||||
indices.swap(block_type_selector_cluster_indices[i]);
|
||||
|
||||
for (uint j = 0; j < indices.size(); j++)
|
||||
indices.at(j) = selector_vec_remap[block_type][indices.at(j)];
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool qdxt5::pack(dxt5_block* pDst_elements, uint elements_per_block, const qdxt5_params& params)
|
||||
{
|
||||
CRNLIB_ASSERT(m_num_blocks);
|
||||
|
||||
m_main_thread_id = get_current_thread_id();
|
||||
m_canceled = false;
|
||||
|
||||
m_pDst_elements = pDst_elements;
|
||||
m_elements_per_block = elements_per_block;
|
||||
m_params = params;
|
||||
|
||||
m_prev_percentage_complete = -1;
|
||||
|
||||
CRNLIB_ASSERT(m_params.m_quality_level <= qdxt5_params::cMaxQuality);
|
||||
const float quality = m_params.m_quality_level / (float)qdxt5_params::cMaxQuality;
|
||||
const float endpoint_quality = powf(quality, 2.1f);
|
||||
const float selector_quality = powf(quality, 1.65f);
|
||||
|
||||
const uint max_endpoint_clusters = math::clamp<uint>(static_cast<uint>(m_endpoint_clusterizer.get_codebook_size() * endpoint_quality), 16U, m_endpoint_clusterizer.get_codebook_size());
|
||||
const uint max_selector_clusters = math::clamp<uint>(static_cast<uint>(m_max_selector_clusters * selector_quality), 32U, m_max_selector_clusters);
|
||||
|
||||
#if QDXT5_DEBUGGING
|
||||
trace("max endpoint clusters: %u\n", max_endpoint_clusters);
|
||||
trace("max selector clusters: %u\n", max_selector_clusters);
|
||||
#endif
|
||||
|
||||
if (quality >= 1.0f)
|
||||
{
|
||||
m_endpoint_cluster_indices.resize(m_num_blocks);
|
||||
for (uint i = 0; i < m_num_blocks; i++)
|
||||
{
|
||||
m_endpoint_cluster_indices[i].resize(1);
|
||||
m_endpoint_cluster_indices[i][0] = i;
|
||||
}
|
||||
}
|
||||
else
|
||||
m_endpoint_clusterizer.retrieve_clusters(max_endpoint_clusters, m_endpoint_cluster_indices);
|
||||
|
||||
uint total_blocks = 0;
|
||||
uint max_blocks = 0;
|
||||
for (uint i = 0; i < m_endpoint_cluster_indices.size(); i++)
|
||||
{
|
||||
uint num = m_endpoint_cluster_indices[i].size();
|
||||
total_blocks += num;
|
||||
max_blocks = math::maximum(max_blocks, num);
|
||||
}
|
||||
|
||||
crnlib::vector< crnlib::vector<uint> >& selector_cluster_indices = m_cached_selector_cluster_indices[params.m_quality_level];
|
||||
|
||||
m_progress_start = 0;
|
||||
if (quality >= 1.0f)
|
||||
m_progress_range = 100;
|
||||
else if (selector_cluster_indices.empty())
|
||||
m_progress_range = (m_params.m_dxt_quality == cCRNDXTQualitySuperFast) ? 10 : 33;
|
||||
else
|
||||
m_progress_range = (m_params.m_dxt_quality == cCRNDXTQualitySuperFast) ? 10 : 50;
|
||||
|
||||
for (uint i = 0; i <= m_pTask_pool->get_num_threads(); i++)
|
||||
m_pTask_pool->queue_object_task(this, &qdxt5::pack_endpoints_task, i);
|
||||
m_pTask_pool->join();
|
||||
|
||||
if (m_canceled)
|
||||
return false;
|
||||
|
||||
if (quality >= 1.0f)
|
||||
return true;
|
||||
|
||||
if (selector_cluster_indices.empty())
|
||||
{
|
||||
create_selector_clusters(max_selector_clusters, selector_cluster_indices);
|
||||
|
||||
if (m_canceled)
|
||||
{
|
||||
selector_cluster_indices.clear();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_progress_start += m_progress_range;
|
||||
m_progress_range = 100 - m_progress_start;
|
||||
|
||||
optimize_selectors_params optimize_selectors_task_params(selector_cluster_indices);
|
||||
|
||||
for (uint i = 0; i <= m_pTask_pool->get_num_threads(); i++)
|
||||
m_pTask_pool->queue_object_task(this, &qdxt5::optimize_selectors_task, i, &optimize_selectors_task_params);
|
||||
|
||||
m_pTask_pool->join();
|
||||
|
||||
return !m_canceled;
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,196 @@
|
||||
// File: crn_qdxt5.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
#include "crn_task_pool.h"
|
||||
#include "crn_spinlock.h"
|
||||
#include "crn_hash_map.h"
|
||||
#include "crn_clusterizer.h"
|
||||
#include "crn_hash.h"
|
||||
#include "crn_threaded_clusterizer.h"
|
||||
#include "crn_dxt.h"
|
||||
#include "crn_dxt_image.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
struct qdxt5_params
|
||||
{
|
||||
qdxt5_params()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
m_quality_level = cMaxQuality;
|
||||
m_dxt_quality = cCRNDXTQualityUber;
|
||||
|
||||
m_pProgress_func = NULL;
|
||||
m_pProgress_data = NULL;
|
||||
m_num_mips = 0;
|
||||
m_hierarchical = true;
|
||||
utils::zero_object(m_mip_desc);
|
||||
|
||||
m_comp_index = 3;
|
||||
m_progress_start = 0;
|
||||
m_progress_range = 100;
|
||||
|
||||
m_use_both_block_types = true;
|
||||
}
|
||||
|
||||
void init(const dxt_image::pack_params &pp, int quality_level, bool hierarchical, int comp_index = 3)
|
||||
{
|
||||
m_dxt_quality = pp.m_quality;
|
||||
m_hierarchical = hierarchical;
|
||||
m_comp_index = comp_index;
|
||||
m_use_both_block_types = pp.m_use_both_block_types;
|
||||
m_quality_level = quality_level;
|
||||
}
|
||||
|
||||
enum { cMaxQuality = cCRNMaxQualityLevel };
|
||||
uint m_quality_level;
|
||||
crn_dxt_quality m_dxt_quality;
|
||||
bool m_hierarchical;
|
||||
|
||||
struct mip_desc
|
||||
{
|
||||
uint m_first_block;
|
||||
uint m_block_width;
|
||||
uint m_block_height;
|
||||
};
|
||||
|
||||
uint m_num_mips;
|
||||
enum { cMaxMips = 128 };
|
||||
mip_desc m_mip_desc[cMaxMips];
|
||||
|
||||
typedef bool (*progress_callback_func)(uint percentage_completed, void* pProgress_data);
|
||||
progress_callback_func m_pProgress_func;
|
||||
void* m_pProgress_data;
|
||||
uint m_progress_start;
|
||||
uint m_progress_range;
|
||||
|
||||
uint m_comp_index;
|
||||
|
||||
bool m_use_both_block_types;
|
||||
};
|
||||
|
||||
class qdxt5
|
||||
{
|
||||
CRNLIB_NO_COPY_OR_ASSIGNMENT_OP(qdxt5);
|
||||
|
||||
public:
|
||||
qdxt5(task_pool& task_pool);
|
||||
~qdxt5();
|
||||
|
||||
void clear();
|
||||
|
||||
bool init(uint n, const dxt_pixel_block* pBlocks, const qdxt5_params& params);
|
||||
|
||||
uint get_num_blocks() const { return m_num_blocks; }
|
||||
const dxt_pixel_block* get_blocks() const { return m_pBlocks; }
|
||||
|
||||
bool pack(dxt5_block* pDst_elements, uint elements_per_block, const qdxt5_params& params);
|
||||
|
||||
private:
|
||||
task_pool* m_pTask_pool;
|
||||
uint32 m_main_thread_id;
|
||||
bool m_canceled;
|
||||
|
||||
uint m_progress_start;
|
||||
uint m_progress_range;
|
||||
|
||||
uint m_num_blocks;
|
||||
const dxt_pixel_block* m_pBlocks;
|
||||
|
||||
dxt5_block* m_pDst_elements;
|
||||
uint m_elements_per_block;
|
||||
qdxt5_params m_params;
|
||||
|
||||
uint m_max_selector_clusters;
|
||||
|
||||
int m_prev_percentage_complete;
|
||||
|
||||
typedef vec<2, float> vec2F;
|
||||
typedef clusterizer<vec2F> vec2F_clusterizer;
|
||||
vec2F_clusterizer m_endpoint_clusterizer;
|
||||
|
||||
crnlib::vector< crnlib::vector<uint> > m_endpoint_cluster_indices;
|
||||
|
||||
typedef vec<16, float> vec16F;
|
||||
typedef threaded_clusterizer<vec16F> vec16F_clusterizer;
|
||||
|
||||
typedef vec16F_clusterizer::weighted_vec weighted_selector_vec;
|
||||
typedef vec16F_clusterizer::weighted_vec_array weighted_selector_vec_array;
|
||||
|
||||
vec16F_clusterizer m_selector_clusterizer;
|
||||
|
||||
crnlib::vector< crnlib::vector<uint> > m_cached_selector_cluster_indices[qdxt5_params::cMaxQuality + 1];
|
||||
|
||||
struct cluster_id
|
||||
{
|
||||
cluster_id() : m_hash(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
cluster_id(const crnlib::vector<uint>& indices)
|
||||
{
|
||||
set(indices);
|
||||
}
|
||||
|
||||
void set(const crnlib::vector<uint>& indices)
|
||||
{
|
||||
m_cells.resize(indices.size());
|
||||
|
||||
for (uint i = 0; i < indices.size(); i++)
|
||||
m_cells[i] = static_cast<uint32>(indices[i]);
|
||||
|
||||
std::sort(m_cells.begin(), m_cells.end());
|
||||
|
||||
m_hash = fast_hash(&m_cells[0], sizeof(m_cells[0]) * m_cells.size());
|
||||
}
|
||||
|
||||
bool operator< (const cluster_id& rhs) const
|
||||
{
|
||||
return m_cells < rhs.m_cells;
|
||||
}
|
||||
|
||||
bool operator== (const cluster_id& rhs) const
|
||||
{
|
||||
if (m_hash != rhs.m_hash)
|
||||
return false;
|
||||
|
||||
return m_cells == rhs.m_cells;
|
||||
}
|
||||
|
||||
crnlib::vector<uint32> m_cells;
|
||||
|
||||
size_t m_hash;
|
||||
|
||||
operator size_t() const { return m_hash; }
|
||||
};
|
||||
|
||||
typedef crnlib::hash_map<cluster_id, uint> cluster_hash;
|
||||
cluster_hash m_cluster_hash;
|
||||
spinlock m_cluster_hash_lock;
|
||||
|
||||
static bool generate_codebook_dummy_progress_callback(uint percentage_completed, void* pData);
|
||||
static bool generate_codebook_progress_callback(uint percentage_completed, void* pData);
|
||||
bool update_progress(uint value, uint max_value);
|
||||
void pack_endpoints_task(uint64 data, void* pData_ptr);
|
||||
void optimize_selectors_task(uint64 data, void* pData_ptr);
|
||||
bool create_selector_clusters(uint max_selector_clusters, crnlib::vector< crnlib::vector<uint> >& selector_cluster_indices);
|
||||
|
||||
inline dxt5_block& get_block(uint index) const { return m_pDst_elements[index * m_elements_per_block]; }
|
||||
};
|
||||
|
||||
} // namespace crnlib
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,365 @@
|
||||
// File: crn_rand.cpp
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
// See:
|
||||
// http://www.ciphersbyritter.com/NEWS4/RANDC.HTM
|
||||
// http://burtleburtle.net/bob/rand/smallprng.html
|
||||
// http://www.cs.ucl.ac.uk/staff/d.jones/GoodPracticeRNG.pdf
|
||||
// See GPG7, page 120, or http://www.lomont.org/Math/Papers/2008/Lomont_PRNG_2008.pdf
|
||||
#include "crn_core.h"
|
||||
#include "crn_rand.h"
|
||||
#include "crn_hash.h"
|
||||
|
||||
#define znew (z=36969*(z&65535)+(z>>16))
|
||||
#define wnew (w=18000*(w&65535)+(w>>16))
|
||||
#define MWC ((znew<<16)+wnew )
|
||||
#define SHR3 (jsr^=(jsr<<17), jsr^=(jsr>>13), jsr^=(jsr<<5))
|
||||
#define CONG (jcong=69069*jcong+1234567)
|
||||
#define FIB ((b=a+b),(a=b-a))
|
||||
#define KISS ((MWC^CONG)+SHR3)
|
||||
#define LFIB4 (c++,t[c]=t[c]+t[UC(c+58)]+t[UC(c+119)]+t[UC(c+178)])
|
||||
#define SWB (c++,bro=(x<y),t[c]=(x=t[UC(c+34)])-(y=t[UC(c+19)]+bro))
|
||||
#define UNI (KISS*2.328306e-10)
|
||||
#define VNI ((long) KISS)*4.656613e-10
|
||||
#define UC (unsigned char) /*a cast operation*/
|
||||
|
||||
//#define rot(x,k) (((x)<<(k))|((x)>>(32-(k))))
|
||||
#define rot(x,k) CRNLIB_ROTATE_LEFT(x,k)
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
static const double cNorm = 1.0 / (double)0x100000000ULL;
|
||||
|
||||
kiss99::kiss99()
|
||||
{
|
||||
x = 123456789;
|
||||
y = 362436000;
|
||||
z = 521288629;
|
||||
c = 7654321;
|
||||
}
|
||||
|
||||
void kiss99::seed(uint32 i, uint32 j, uint32 k)
|
||||
{
|
||||
x = i;
|
||||
y = j;
|
||||
z = k;
|
||||
c = 7654321;
|
||||
}
|
||||
|
||||
inline uint32 kiss99::next()
|
||||
{
|
||||
x = 69069*x+12345;
|
||||
|
||||
y ^= (y<<13);
|
||||
y ^= (y>>17);
|
||||
y ^= (y<<5);
|
||||
|
||||
uint64 t = c;
|
||||
t += (698769069ULL*z);
|
||||
c = static_cast<uint32>(t >> 32);
|
||||
z = static_cast<uint32>(t);
|
||||
|
||||
return (x+y+z);
|
||||
}
|
||||
|
||||
inline uint32 ranctx::next()
|
||||
{
|
||||
uint32 e = a - rot(b, 27);
|
||||
a = b ^ rot(c, 17);
|
||||
b = c + d;
|
||||
c = d + e;
|
||||
d = e + a;
|
||||
return d;
|
||||
}
|
||||
|
||||
void ranctx::seed(uint32 seed)
|
||||
{
|
||||
a = 0xf1ea5eed, b = c = d = seed;
|
||||
for (uint32 i=0; i<20; ++i)
|
||||
next();
|
||||
}
|
||||
|
||||
well512::well512()
|
||||
{
|
||||
seed(0xDEADBE3F);
|
||||
}
|
||||
|
||||
void well512::seed(uint32 seed[well512::cStateSize])
|
||||
{
|
||||
memcpy(m_state, seed, sizeof(m_state));
|
||||
m_index = 0;
|
||||
}
|
||||
|
||||
void well512::seed(uint32 seed)
|
||||
{
|
||||
uint32 jsr = utils::swap32(seed) ^ 0xAAC29377;
|
||||
|
||||
for (uint i = 0; i < cStateSize; i++)
|
||||
{
|
||||
SHR3;
|
||||
seed = bitmix32c(seed);
|
||||
|
||||
m_state[i] = seed ^ jsr;
|
||||
}
|
||||
m_index = 0;
|
||||
}
|
||||
|
||||
void well512::seed(uint32 seed1, uint32 seed2, uint32 seed3)
|
||||
{
|
||||
uint32 jsr = seed2;
|
||||
uint32 jcong = seed3;
|
||||
|
||||
for (uint i = 0; i < cStateSize; i++)
|
||||
{
|
||||
SHR3;
|
||||
seed1 = bitmix32c(seed1);
|
||||
CONG;
|
||||
|
||||
m_state[i] = seed1 ^ jsr ^ jcong;
|
||||
}
|
||||
m_index = 0;
|
||||
}
|
||||
|
||||
inline uint32 well512::next()
|
||||
{
|
||||
uint32 a, b, c, d;
|
||||
a = m_state[m_index];
|
||||
c = m_state[(m_index+13)&15];
|
||||
b = a^c^(a<<16)^(c<<15);
|
||||
c = m_state[(m_index+9)&15];
|
||||
c ^= (c>>11);
|
||||
a = m_state[m_index] = b^c;
|
||||
d = a^((a<<5)&0xDA442D20UL);
|
||||
m_index = (m_index + 15)&15;
|
||||
a = m_state[m_index];
|
||||
m_state[m_index] = a^b^d^(a<<2)^(b<<18)^(c<<28);
|
||||
return m_state[m_index];
|
||||
}
|
||||
|
||||
random::random()
|
||||
{
|
||||
seed(12345,65435,34221);
|
||||
}
|
||||
|
||||
random::random(uint32 i)
|
||||
{
|
||||
seed(i);
|
||||
}
|
||||
|
||||
void random::seed(uint32 i1, uint32 i2, uint32 i3)
|
||||
{
|
||||
m_ranctx.seed(i1^i2^i3);
|
||||
|
||||
m_kiss99.seed(i1, i2, i3);
|
||||
|
||||
m_well512.seed(i1, i2, i3);
|
||||
|
||||
for (uint i = 0; i < 100; i++)
|
||||
urand32();
|
||||
}
|
||||
|
||||
void random::seed(uint32 i)
|
||||
{
|
||||
uint32 jsr = i;
|
||||
SHR3; SHR3;
|
||||
uint32 jcong = utils::swap32(~jsr);
|
||||
CONG; CONG;
|
||||
uint32 i1 = SHR3 ^ CONG;
|
||||
uint32 i2 = SHR3 ^ CONG;
|
||||
uint32 i3 = SHR3 + CONG;
|
||||
seed(i1, i2, i3);
|
||||
}
|
||||
|
||||
uint32 random::urand32()
|
||||
{
|
||||
return m_kiss99.next() ^ (m_ranctx.next() + m_well512.next());
|
||||
}
|
||||
|
||||
uint32 random::fast_urand32()
|
||||
{
|
||||
return m_well512.next();
|
||||
}
|
||||
|
||||
uint32 random::bit()
|
||||
{
|
||||
uint32 k = urand32();
|
||||
return (k ^ (k >> 6) ^ (k >> 10) ^ (k >> 30)) & 1;
|
||||
}
|
||||
|
||||
double random::drand(double l, double h)
|
||||
{
|
||||
CRNLIB_ASSERT(l <= h);
|
||||
if (l >= h)
|
||||
return l;
|
||||
|
||||
return math::clamp(l + (h - l) * (urand32() * cNorm), l, h);
|
||||
}
|
||||
|
||||
float random::frand(float l, float h)
|
||||
{
|
||||
CRNLIB_ASSERT(l <= h);
|
||||
if (l >= h)
|
||||
return l;
|
||||
|
||||
float r = static_cast<float>(l + (h - l) * (urand32() * cNorm));
|
||||
|
||||
return math::clamp<float>(r, l, h);
|
||||
}
|
||||
|
||||
int random::irand(int l, int h)
|
||||
{
|
||||
CRNLIB_ASSERT(l < h);
|
||||
if (l >= h)
|
||||
return l;
|
||||
|
||||
uint32 range = static_cast<uint32>(h - l);
|
||||
|
||||
uint32 rnd = urand32();
|
||||
|
||||
#if defined(_M_IX86) && defined(_MSC_VER)
|
||||
//uint32 rnd_range = static_cast<uint32>(__emulu(range, rnd) >> 32U);
|
||||
uint32 x[2];
|
||||
*reinterpret_cast<uint64*>(x) = __emulu(range, rnd);
|
||||
uint32 rnd_range = x[1];
|
||||
#else
|
||||
uint32 rnd_range = static_cast<uint32>((((uint64)range) * ((uint64)rnd)) >> 32U);
|
||||
#endif
|
||||
|
||||
int result = l + rnd_range;
|
||||
CRNLIB_ASSERT((result >= l) && (result < h));
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
ALGORITHM 712, COLLECTED ALGORITHMS FROM ACM.
|
||||
THIS WORK PUBLISHED IN TRANSACTIONS ON MATHEMATICAL SOFTWARE,
|
||||
VOL. 18, NO. 4, DECEMBER, 1992, PP. 434-435.
|
||||
The function returns a normally distributed pseudo-random number
|
||||
with a given mean and standard devaiation. Calls are made to a
|
||||
function subprogram which must return independent random
|
||||
numbers uniform in the interval (0,1).
|
||||
The algorithm uses the ratio of uniforms method of A.J. Kinderman
|
||||
and J.F. Monahan augmented with quadratic bounding curves.
|
||||
*/
|
||||
double random::gaussian(double mean, double stddev)
|
||||
{
|
||||
double q,u,v,x,y;
|
||||
|
||||
/*
|
||||
Generate P = (u,v) uniform in rect. enclosing acceptance region
|
||||
Make sure that any random numbers <= 0 are rejected, since
|
||||
gaussian() requires uniforms > 0, but RandomUniform() delivers >= 0.
|
||||
*/
|
||||
do {
|
||||
u = drand(0, 1);
|
||||
v = drand(0, 1);
|
||||
if (u <= 0.0 || v <= 0.0) {
|
||||
u = 1.0;
|
||||
v = 1.0;
|
||||
}
|
||||
v = 1.7156 * (v - 0.5);
|
||||
|
||||
/* Evaluate the quadratic form */
|
||||
x = u - 0.449871;
|
||||
y = fabs(v) + 0.386595;
|
||||
q = x * x + y * (0.19600 * y - 0.25472 * x);
|
||||
|
||||
/* Accept P if inside inner ellipse */
|
||||
if (q < 0.27597)
|
||||
break;
|
||||
|
||||
/* Reject P if outside outer ellipse, or outside acceptance region */
|
||||
} while ((q > 0.27846) || (v * v > -4.0 * log(u) * u * u));
|
||||
|
||||
/* Return ratio of P's coordinates as the normal deviate */
|
||||
return (mean + stddev * v / u);
|
||||
}
|
||||
|
||||
void random::test()
|
||||
{
|
||||
}
|
||||
|
||||
fast_random::fast_random() :
|
||||
jsr(0xABCD917A),
|
||||
jcong(0x17F3DEAD)
|
||||
{
|
||||
}
|
||||
|
||||
fast_random::fast_random(const fast_random& other) :
|
||||
jsr(other.jsr), jcong(other.jcong)
|
||||
{
|
||||
}
|
||||
|
||||
fast_random::fast_random(uint32 i)
|
||||
{
|
||||
seed(i);
|
||||
}
|
||||
|
||||
fast_random& fast_random::operator=(const fast_random& other)
|
||||
{
|
||||
jsr = other.jsr;
|
||||
jcong = other.jcong;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void fast_random::seed(uint32 i)
|
||||
{
|
||||
jsr = i;
|
||||
SHR3;
|
||||
SHR3;
|
||||
jcong = (~i) ^ 0xDEADBEEF;
|
||||
|
||||
SHR3;
|
||||
CONG;
|
||||
}
|
||||
|
||||
uint32 fast_random::urand32()
|
||||
{
|
||||
return SHR3 ^ CONG;
|
||||
}
|
||||
|
||||
int fast_random::irand(int l, int h)
|
||||
{
|
||||
CRNLIB_ASSERT(l < h);
|
||||
if (l >= h)
|
||||
return l;
|
||||
|
||||
uint32 range = static_cast<uint32>(h - l);
|
||||
|
||||
uint32 rnd = urand32();
|
||||
|
||||
#if defined(_M_IX86) && defined(_MSC_VER)
|
||||
//uint32 rnd_range = static_cast<uint32>(__emulu(range, rnd) >> 32U);
|
||||
uint32 x[2];
|
||||
*reinterpret_cast<uint64*>(x) = __emulu(range, rnd);
|
||||
uint32 rnd_range = x[1];
|
||||
#else
|
||||
uint32 rnd_range = static_cast<uint32>((((uint64)range) * ((uint64)rnd)) >> 32U);
|
||||
#endif
|
||||
|
||||
int result = l + rnd_range;
|
||||
CRNLIB_ASSERT((result >= l) && (result < h));
|
||||
return result;
|
||||
}
|
||||
|
||||
double fast_random::drand(double l, double h)
|
||||
{
|
||||
CRNLIB_ASSERT(l <= h);
|
||||
if (l >= h)
|
||||
return l;
|
||||
|
||||
return math::clamp(l + (h - l) * (urand32() * cNorm), l, h);
|
||||
}
|
||||
|
||||
float fast_random::frand(float l, float h)
|
||||
{
|
||||
CRNLIB_ASSERT(l <= h);
|
||||
if (l >= h)
|
||||
return l;
|
||||
|
||||
float r = static_cast<float>(l + (h - l) * (urand32() * cNorm));
|
||||
|
||||
return math::clamp<float>(r, l, h);
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
// File: crn_rand.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
class kiss99
|
||||
{
|
||||
public:
|
||||
kiss99();
|
||||
|
||||
void seed(uint32 i, uint32 j, uint32 k);
|
||||
|
||||
inline uint32 next();
|
||||
|
||||
private:
|
||||
uint32 x;
|
||||
uint32 y;
|
||||
uint32 z;
|
||||
uint32 c;
|
||||
};
|
||||
|
||||
class well512
|
||||
{
|
||||
public:
|
||||
well512();
|
||||
|
||||
enum { cStateSize = 16 };
|
||||
void seed(uint32 seed[cStateSize]);
|
||||
void seed(uint32 seed);
|
||||
void seed(uint32 seed1, uint32 seed2, uint32 seed3);
|
||||
|
||||
inline uint32 next();
|
||||
|
||||
private:
|
||||
uint32 m_state[cStateSize];
|
||||
uint32 m_index;
|
||||
};
|
||||
|
||||
class ranctx
|
||||
{
|
||||
public:
|
||||
ranctx() { seed(0xDE149737); }
|
||||
|
||||
void seed(uint32 seed);
|
||||
|
||||
inline uint32 next();
|
||||
|
||||
private:
|
||||
uint32 a;
|
||||
uint32 b;
|
||||
uint32 c;
|
||||
uint32 d;
|
||||
};
|
||||
|
||||
class random
|
||||
{
|
||||
public:
|
||||
random();
|
||||
random(uint32 i);
|
||||
|
||||
void seed(uint32 i);
|
||||
void seed(uint32 i1, uint32 i2, uint32 i3);
|
||||
|
||||
uint32 urand32();
|
||||
|
||||
// "Fast" variant uses no multiplies.
|
||||
uint32 fast_urand32();
|
||||
|
||||
uint32 bit();
|
||||
|
||||
// Returns random between [0, 1)
|
||||
double drand(double l, double h);
|
||||
|
||||
float frand(float l, float h);
|
||||
|
||||
// Returns random between [l, h)
|
||||
int irand(int l, int h);
|
||||
|
||||
double gaussian(double mean, double stddev);
|
||||
|
||||
void test();
|
||||
|
||||
private:
|
||||
ranctx m_ranctx;
|
||||
kiss99 m_kiss99;
|
||||
well512 m_well512;
|
||||
};
|
||||
|
||||
// Simpler, minimal state PRNG
|
||||
class fast_random
|
||||
{
|
||||
public:
|
||||
fast_random();
|
||||
fast_random(uint32 i);
|
||||
fast_random(const fast_random& other);
|
||||
fast_random& operator=(const fast_random& other);
|
||||
|
||||
void seed(uint32 i);
|
||||
|
||||
uint32 urand32();
|
||||
|
||||
int irand(int l, int h);
|
||||
|
||||
double drand(double l, double h);
|
||||
|
||||
float frand(float l, float h);
|
||||
|
||||
private:
|
||||
uint32 jsr;
|
||||
uint32 jcong;
|
||||
};
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,52 @@
|
||||
// File: crn_ray.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
#include "crn_vec.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
template<typename vector_type>
|
||||
class ray
|
||||
{
|
||||
public:
|
||||
typedef vector_type vector_t;
|
||||
typedef typename vector_type::scalar_type scalar_type;
|
||||
|
||||
inline ray() { }
|
||||
inline ray(eClear) { clear(); }
|
||||
inline ray(const vector_type& origin, const vector_type& direction) : m_origin(origin), m_direction(direction) { }
|
||||
|
||||
inline void clear()
|
||||
{
|
||||
m_origin.clear();
|
||||
m_direction.clear();
|
||||
}
|
||||
|
||||
inline const vector_type& get_origin(void) const { return m_origin; }
|
||||
inline void set_origin(const vector_type& origin) { m_origin = origin; }
|
||||
|
||||
inline const vector_type& get_direction(void) const { return m_direction; }
|
||||
inline void set_direction(const vector_type& direction) { m_direction = direction; }
|
||||
|
||||
inline scalar_type set_endpoints(const vector_type& start, const vector_type& end, const vector_type& def)
|
||||
{
|
||||
m_origin = start;
|
||||
|
||||
m_direction = end - start;
|
||||
return m_direction.normalize(&def);
|
||||
}
|
||||
|
||||
inline vector_type eval(scalar_type t) const
|
||||
{
|
||||
return m_origin + m_direction * t;
|
||||
}
|
||||
|
||||
private:
|
||||
vector_type m_origin;
|
||||
vector_type m_direction;
|
||||
};
|
||||
|
||||
typedef ray<vec2F> ray2F;
|
||||
typedef ray<vec3F> ray3F;
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,81 @@
|
||||
// File: crn_rect.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
#include "crn_vec.h"
|
||||
#include "crn_hash.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
class rect
|
||||
{
|
||||
public:
|
||||
inline rect()
|
||||
{
|
||||
}
|
||||
|
||||
inline rect(eClear)
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
inline rect(int left, int top, int right, int bottom)
|
||||
{
|
||||
set(left, top, right, bottom);
|
||||
}
|
||||
|
||||
inline rect(const vec2I& lo, const vec2I& hi)
|
||||
{
|
||||
m_corner[0] = lo;
|
||||
m_corner[1] = hi;
|
||||
}
|
||||
|
||||
inline rect(const vec2I& point)
|
||||
{
|
||||
m_corner[0] = point;
|
||||
m_corner[1].set(point[0] + 1, point[1] + 1);
|
||||
}
|
||||
|
||||
inline void clear()
|
||||
{
|
||||
m_corner[0].clear();
|
||||
m_corner[1].clear();
|
||||
}
|
||||
|
||||
inline void set(int left, int top, int right, int bottom)
|
||||
{
|
||||
m_corner[0].set(left, top);
|
||||
m_corner[1].set(right, bottom);
|
||||
}
|
||||
|
||||
inline void set(const vec2I& lo, const vec2I& hi)
|
||||
{
|
||||
m_corner[0] = lo;
|
||||
m_corner[1] = hi;
|
||||
}
|
||||
|
||||
inline void set(const vec2I& point)
|
||||
{
|
||||
m_corner[0] = point;
|
||||
m_corner[1].set(point[0] + 1, point[1] + 1);
|
||||
}
|
||||
|
||||
inline uint get_width() const { return m_corner[1][0] - m_corner[0][0]; }
|
||||
inline uint get_height() const { return m_corner[1][1] - m_corner[0][1]; }
|
||||
|
||||
inline int get_left() const { return m_corner[0][0]; }
|
||||
inline int get_top() const { return m_corner[0][1]; }
|
||||
inline int get_right() const { return m_corner[1][0]; }
|
||||
inline int get_bottom() const { return m_corner[1][1]; }
|
||||
|
||||
inline bool is_empty() const { return (m_corner[1][0] <= m_corner[0][0]) || (m_corner[1][1] <= m_corner[0][1]); }
|
||||
|
||||
inline uint get_dimension(uint axis) const { return m_corner[1][axis] - m_corner[0][axis]; }
|
||||
|
||||
inline const vec2I& operator[] (uint i) const { CRNLIB_ASSERT(i < 2); return m_corner[i]; }
|
||||
inline vec2I& operator[] (uint i) { CRNLIB_ASSERT(i < 2); return m_corner[i]; }
|
||||
|
||||
private:
|
||||
vec2I m_corner[2];
|
||||
};
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,337 @@
|
||||
// File: crn_resample_filters.cpp
|
||||
// RG: This is public domain code, originally derived from Graphics Gems 3, see: http://code.google.com/p/imageresampler/
|
||||
#include "crn_core.h"
|
||||
#include "crn_resample_filters.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
#define M_PI 3.14159265358979323846
|
||||
|
||||
// To add your own filter, insert the new function below and update the filter table.
|
||||
// There is no need to make the filter function particularly fast, because it's
|
||||
// only called during initializing to create the X and Y axis contributor tables.
|
||||
|
||||
#define BOX_FILTER_SUPPORT (0.5f)
|
||||
static float box_filter(float t) /* pulse/Fourier window */
|
||||
{
|
||||
// make_clist() calls the filter function with t inverted (pos = left, neg = right)
|
||||
if ((t >= -0.5f) && (t < 0.5f))
|
||||
return 1.0f;
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
#define TENT_FILTER_SUPPORT (1.0f)
|
||||
static float tent_filter(float t) /* box (*) box, bilinear/triangle */
|
||||
{
|
||||
if (t < 0.0f)
|
||||
t = -t;
|
||||
|
||||
if (t < 1.0f)
|
||||
return 1.0f - t;
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
#define BELL_SUPPORT (1.5f)
|
||||
static float bell_filter(float t) /* box (*) box (*) box */
|
||||
{
|
||||
if (t < 0.0f)
|
||||
t = -t;
|
||||
|
||||
if (t < .5f)
|
||||
return (.75f - (t * t));
|
||||
|
||||
if (t < 1.5f)
|
||||
{
|
||||
t = (t - 1.5f);
|
||||
return (.5f * (t * t));
|
||||
}
|
||||
|
||||
return (0.0f);
|
||||
}
|
||||
|
||||
#define B_SPLINE_SUPPORT (2.0f)
|
||||
static float B_spline_filter(float t) /* box (*) box (*) box (*) box */
|
||||
{
|
||||
float tt;
|
||||
|
||||
if (t < 0.0f)
|
||||
t = -t;
|
||||
|
||||
if (t < 1.0f)
|
||||
{
|
||||
tt = t * t;
|
||||
return ((.5f * tt * t) - tt + (2.0f / 3.0f));
|
||||
}
|
||||
else if (t < 2.0f)
|
||||
{
|
||||
t = 2.0f - t;
|
||||
return ((1.0f / 6.0f) * (t * t * t));
|
||||
}
|
||||
|
||||
return (0.0f);
|
||||
}
|
||||
|
||||
// Dodgson, N., "Quadratic Interpolation for Image Resampling"
|
||||
#define QUADRATIC_SUPPORT 1.5f
|
||||
static float quadratic(float t, const float R)
|
||||
{
|
||||
if (t < 0.0f)
|
||||
t = -t;
|
||||
if (t < QUADRATIC_SUPPORT)
|
||||
{
|
||||
float tt = t * t;
|
||||
if (t <= .5f)
|
||||
return (-2.0f * R) * tt + .5f * (R + 1.0f);
|
||||
else
|
||||
return (R * tt) + (-2.0f * R - .5f) * t + (3.0f / 4.0f) * (R + 1.0f);
|
||||
}
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
static float quadratic_interp_filter(float t)
|
||||
{
|
||||
return quadratic(t, 1.0f);
|
||||
}
|
||||
|
||||
static float quadratic_approx_filter(float t)
|
||||
{
|
||||
return quadratic(t, .5f);
|
||||
}
|
||||
|
||||
static float quadratic_mix_filter(float t)
|
||||
{
|
||||
return quadratic(t, .8f);
|
||||
}
|
||||
|
||||
// Mitchell, D. and A. Netravali, "Reconstruction Filters in Computer Graphics."
|
||||
// Computer Graphics, Vol. 22, No. 4, pp. 221-228.
|
||||
// (B, C)
|
||||
// (1/3, 1/3) - Defaults recommended by Mitchell and Netravali
|
||||
// (1, 0) - Equivalent to the Cubic B-Spline
|
||||
// (0, 0.5) - Equivalent to the Catmull-Rom Spline
|
||||
// (0, C) - The family of Cardinal Cubic Splines
|
||||
// (B, 0) - Duff's tensioned B-Splines.
|
||||
static float mitchell(float t, const float B, const float C)
|
||||
{
|
||||
float tt;
|
||||
|
||||
tt = t * t;
|
||||
|
||||
if(t < 0.0f)
|
||||
t = -t;
|
||||
|
||||
if(t < 1.0f)
|
||||
{
|
||||
t = (((12.0f - 9.0f * B - 6.0f * C) * (t * tt))
|
||||
+ ((-18.0f + 12.0f * B + 6.0f * C) * tt)
|
||||
+ (6.0f - 2.0f * B));
|
||||
|
||||
return (t / 6.0f);
|
||||
}
|
||||
else if (t < 2.0f)
|
||||
{
|
||||
t = (((-1.0f * B - 6.0f * C) * (t * tt))
|
||||
+ ((6.0f * B + 30.0f * C) * tt)
|
||||
+ ((-12.0f * B - 48.0f * C) * t)
|
||||
+ (8.0f * B + 24.0f * C));
|
||||
|
||||
return (t / 6.0f);
|
||||
}
|
||||
|
||||
return (0.0f);
|
||||
}
|
||||
|
||||
#define MITCHELL_SUPPORT (2.0f)
|
||||
static float mitchell_filter(float t)
|
||||
{
|
||||
return mitchell(t, 1.0f / 3.0f, 1.0f / 3.0f);
|
||||
}
|
||||
|
||||
#define CATMULL_ROM_SUPPORT (2.0f)
|
||||
static float catmull_rom_filter(float t)
|
||||
{
|
||||
return mitchell(t, 0.0f, .5f);
|
||||
}
|
||||
|
||||
static double sinc(double x)
|
||||
{
|
||||
x = (x * M_PI);
|
||||
|
||||
if ((x < 0.01f) && (x > -0.01f))
|
||||
return 1.0f + x*x*(-1.0f/6.0f + x*x*1.0f/120.0f);
|
||||
|
||||
return sin(x) / x;
|
||||
}
|
||||
|
||||
static float clean(double t)
|
||||
{
|
||||
const float EPSILON = .0000125f;
|
||||
if (fabs(t) < EPSILON)
|
||||
return 0.0f;
|
||||
return (float)t;
|
||||
}
|
||||
|
||||
//static double blackman_window(double x)
|
||||
//{
|
||||
// return .42f + .50f * cos(M_PI*x) + .08f * cos(2.0f*M_PI*x);
|
||||
//}
|
||||
|
||||
static double blackman_exact_window(double x)
|
||||
{
|
||||
return 0.42659071f + 0.49656062f * cos(M_PI*x) + 0.07684867f * cos(2.0f*M_PI*x);
|
||||
}
|
||||
|
||||
#define BLACKMAN_SUPPORT (3.0f)
|
||||
static float blackman_filter(float t)
|
||||
{
|
||||
if (t < 0.0f)
|
||||
t = -t;
|
||||
|
||||
if (t < 3.0f)
|
||||
//return clean(sinc(t) * blackman_window(t / 3.0f));
|
||||
return clean(sinc(t) * blackman_exact_window(t / 3.0f));
|
||||
else
|
||||
return (0.0f);
|
||||
}
|
||||
|
||||
#define GAUSSIAN_SUPPORT (1.25f)
|
||||
static float gaussian_filter(float t) // with blackman window
|
||||
{
|
||||
if (t < 0)
|
||||
t = -t;
|
||||
if (t < GAUSSIAN_SUPPORT)
|
||||
return clean(exp(-2.0f * t * t) * sqrt(2.0f / M_PI) * blackman_exact_window(t / GAUSSIAN_SUPPORT));
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
// Windowed sinc -- see "Jimm Blinn's Corner: Dirty Pixels" pg. 26.
|
||||
#define LANCZOS3_SUPPORT (3.0f)
|
||||
static float lanczos3_filter(float t)
|
||||
{
|
||||
if (t < 0.0f)
|
||||
t = -t;
|
||||
|
||||
if (t < 3.0f)
|
||||
return clean(sinc(t) * sinc(t / 3.0f));
|
||||
else
|
||||
return (0.0f);
|
||||
}
|
||||
|
||||
#define LANCZOS4_SUPPORT (4.0f)
|
||||
static float lanczos4_filter(float t)
|
||||
{
|
||||
if (t < 0.0f)
|
||||
t = -t;
|
||||
|
||||
if (t < 4.0f)
|
||||
return clean(sinc(t) * sinc(t / 4.0f));
|
||||
else
|
||||
return (0.0f);
|
||||
}
|
||||
|
||||
#define LANCZOS6_SUPPORT (6.0f)
|
||||
static float lanczos6_filter(float t)
|
||||
{
|
||||
if (t < 0.0f)
|
||||
t = -t;
|
||||
|
||||
if (t < 6.0f)
|
||||
return clean(sinc(t) * sinc(t / 6.0f));
|
||||
else
|
||||
return (0.0f);
|
||||
}
|
||||
|
||||
#define LANCZOS12_SUPPORT (12.0f)
|
||||
static float lanczos12_filter(float t)
|
||||
{
|
||||
if (t < 0.0f)
|
||||
t = -t;
|
||||
|
||||
if (t < 12.0f)
|
||||
return clean(sinc(t) * sinc(t / 12.0f));
|
||||
else
|
||||
return (0.0f);
|
||||
}
|
||||
|
||||
static double bessel0(double x)
|
||||
{
|
||||
const double EPSILON_RATIO = 1E-16;
|
||||
double xh, sum, pow, ds;
|
||||
int k;
|
||||
|
||||
xh = 0.5 * x;
|
||||
sum = 1.0;
|
||||
pow = 1.0;
|
||||
k = 0;
|
||||
ds = 1.0;
|
||||
while (ds > sum * EPSILON_RATIO) // FIXME: Shouldn't this stop after X iterations for max. safety?
|
||||
{
|
||||
++k;
|
||||
pow = pow * (xh / k);
|
||||
ds = pow * pow;
|
||||
sum = sum + ds;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
static const float KAISER_ALPHA = 4.0;
|
||||
static double kaiser(double alpha, double half_width, double x)
|
||||
{
|
||||
const double ratio = (x / half_width);
|
||||
return bessel0(alpha * sqrt(1 - ratio * ratio)) / bessel0(alpha);
|
||||
}
|
||||
|
||||
#define KAISER_SUPPORT 3
|
||||
static float kaiser_filter(float t)
|
||||
{
|
||||
if (t < 0.0f)
|
||||
t = -t;
|
||||
|
||||
if (t < KAISER_SUPPORT)
|
||||
{
|
||||
// db atten
|
||||
const float att = 40.0f;
|
||||
const float alpha = (float)(exp(log((double)0.58417 * (att - 20.96)) * 0.4) + 0.07886 * (att - 20.96));
|
||||
//const float alpha = KAISER_ALPHA;
|
||||
return (float)clean(sinc(t) * kaiser(alpha, KAISER_SUPPORT, t));
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
const resample_filter g_resample_filters[] =
|
||||
{
|
||||
{ "box", box_filter, BOX_FILTER_SUPPORT },
|
||||
{ "tent", tent_filter, TENT_FILTER_SUPPORT },
|
||||
{ "bell", bell_filter, BELL_SUPPORT },
|
||||
{ "b-spline", B_spline_filter, B_SPLINE_SUPPORT },
|
||||
{ "mitchell", mitchell_filter, MITCHELL_SUPPORT },
|
||||
{ "lanczos3", lanczos3_filter, LANCZOS3_SUPPORT },
|
||||
{ "blackman", blackman_filter, BLACKMAN_SUPPORT },
|
||||
{ "lanczos4", lanczos4_filter, LANCZOS4_SUPPORT },
|
||||
{ "lanczos6", lanczos6_filter, LANCZOS6_SUPPORT },
|
||||
{ "lanczos12", lanczos12_filter, LANCZOS12_SUPPORT },
|
||||
{ "kaiser", kaiser_filter, KAISER_SUPPORT },
|
||||
{ "gaussian", gaussian_filter, GAUSSIAN_SUPPORT },
|
||||
{ "catmullrom", catmull_rom_filter, CATMULL_ROM_SUPPORT },
|
||||
{ "quadratic_interp", quadratic_interp_filter, QUADRATIC_SUPPORT },
|
||||
{ "quadratic_approx", quadratic_approx_filter, QUADRATIC_SUPPORT },
|
||||
{ "quadratic_mix", quadratic_mix_filter, QUADRATIC_SUPPORT },
|
||||
};
|
||||
|
||||
const int g_num_resample_filters = sizeof(g_resample_filters) / sizeof(g_resample_filters[0]);
|
||||
|
||||
int find_resample_filter(const char* pName)
|
||||
{
|
||||
for (int i = 0; i < g_num_resample_filters; i++)
|
||||
if (_stricmp(pName, g_resample_filters[i].name) == 0)
|
||||
return i;
|
||||
return cInvalidIndex;
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,21 @@
|
||||
// File: crn_resample_filters.h
|
||||
// RG: This is public domain code, originally derived from Graphics Gems 3, see: http://code.google.com/p/imageresampler/
|
||||
#pragma once
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
typedef float (*resample_filter_func)(float t);
|
||||
|
||||
struct resample_filter
|
||||
{
|
||||
char name[32];
|
||||
resample_filter_func func;
|
||||
float support;
|
||||
};
|
||||
|
||||
extern const resample_filter g_resample_filters[];
|
||||
extern const int g_num_resample_filters;
|
||||
|
||||
int find_resample_filter(const char* pName);
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,884 @@
|
||||
// File: crn_resampler.h
|
||||
// RG: This is public domain code, originally derived from Graphics Gems 3, see: http://code.google.com/p/imageresampler/
|
||||
#include "crn_core.h"
|
||||
#include "crn_resampler.h"
|
||||
#include "crn_resample_filters.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
#define resampler_assert CRNLIB_ASSERT
|
||||
|
||||
static inline int resampler_range_check(int v, int h) { h; resampler_assert((v >= 0) && (v < h)); return v; }
|
||||
|
||||
#ifndef max
|
||||
#define max(a,b) (((a) > (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#ifndef min
|
||||
#define min(a,b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE (1)
|
||||
#endif
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE (0)
|
||||
#endif
|
||||
|
||||
#define RESAMPLER_DEBUG 0
|
||||
|
||||
// (x mod y) with special handling for negative x values.
|
||||
static inline int posmod(int x, int y)
|
||||
{
|
||||
if (x >= 0)
|
||||
return (x % y);
|
||||
else
|
||||
{
|
||||
int m = (-x) % y;
|
||||
|
||||
if (m != 0)
|
||||
m = y - m;
|
||||
|
||||
return (m);
|
||||
}
|
||||
}
|
||||
|
||||
// Float to int cast with truncation.
|
||||
static inline int cast_to_int(Resample_Real i)
|
||||
{
|
||||
return (int)i;
|
||||
}
|
||||
|
||||
/* Ensure that the contributing source sample is
|
||||
* within bounds. If not, reflect, clamp, or wrap.
|
||||
*/
|
||||
int Resampler::reflect(const int j, const int src_x, const Boundary_Op boundary_op)
|
||||
{
|
||||
int n;
|
||||
|
||||
if (j < 0)
|
||||
{
|
||||
if (boundary_op == BOUNDARY_REFLECT)
|
||||
{
|
||||
n = -j;
|
||||
|
||||
if (n >= src_x)
|
||||
n = src_x - 1;
|
||||
}
|
||||
else if (boundary_op == BOUNDARY_WRAP)
|
||||
n = posmod(j, src_x);
|
||||
else
|
||||
n = 0;
|
||||
}
|
||||
else if (j >= src_x)
|
||||
{
|
||||
if (boundary_op == BOUNDARY_REFLECT)
|
||||
{
|
||||
n = (src_x - j) + (src_x - 1);
|
||||
|
||||
if (n < 0)
|
||||
n = 0;
|
||||
}
|
||||
else if (boundary_op == BOUNDARY_WRAP)
|
||||
n = posmod(j, src_x);
|
||||
else
|
||||
n = src_x - 1;
|
||||
}
|
||||
else
|
||||
n = j;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
// The make_clist() method generates, for all destination samples,
|
||||
// the list of all source samples with non-zero weighted contributions.
|
||||
Resampler::Contrib_List* Resampler::make_clist(
|
||||
int src_x, int dst_x, Boundary_Op boundary_op,
|
||||
Resample_Real (*Pfilter)(Resample_Real),
|
||||
Resample_Real filter_support,
|
||||
Resample_Real filter_scale,
|
||||
Resample_Real src_ofs)
|
||||
{
|
||||
typedef struct
|
||||
{
|
||||
// The center of the range in DISCRETE coordinates (pixel center = 0.0f).
|
||||
Resample_Real center;
|
||||
int left, right;
|
||||
} Contrib_Bounds;
|
||||
|
||||
int i, j, k, n, left, right;
|
||||
Resample_Real total_weight;
|
||||
Resample_Real xscale, center, half_width, weight;
|
||||
Contrib_List* Pcontrib;
|
||||
Contrib* Pcpool;
|
||||
Contrib* Pcpool_next;
|
||||
Contrib_Bounds* Pcontrib_bounds;
|
||||
|
||||
if ((Pcontrib = (Contrib_List*)crnlib_calloc(dst_x, sizeof(Contrib_List))) == NULL)
|
||||
return NULL;
|
||||
|
||||
Pcontrib_bounds = (Contrib_Bounds*)crnlib_calloc(dst_x, sizeof(Contrib_Bounds));
|
||||
if (!Pcontrib_bounds)
|
||||
{
|
||||
crnlib_free(Pcontrib);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
const Resample_Real oo_filter_scale = 1.0f / filter_scale;
|
||||
|
||||
const Resample_Real NUDGE = 0.5f;
|
||||
xscale = dst_x / (Resample_Real)src_x;
|
||||
|
||||
if (xscale < 1.0f)
|
||||
{
|
||||
int total;
|
||||
|
||||
/* Handle case when there are fewer destination
|
||||
* samples than source samples (downsampling/minification).
|
||||
*/
|
||||
|
||||
// stretched half width of filter
|
||||
half_width = (filter_support / xscale) * filter_scale;
|
||||
|
||||
// Find the range of source sample(s) that will contribute to each destination sample.
|
||||
|
||||
for (i = 0, n = 0; i < dst_x; i++)
|
||||
{
|
||||
// Convert from discrete to continuous coordinates, scale, then convert back to discrete.
|
||||
center = ((Resample_Real)i + NUDGE) / xscale;
|
||||
center -= NUDGE;
|
||||
center += src_ofs;
|
||||
|
||||
left = cast_to_int((Resample_Real)floor(center - half_width));
|
||||
right = cast_to_int((Resample_Real)ceil(center + half_width));
|
||||
|
||||
Pcontrib_bounds[i].center = center;
|
||||
Pcontrib_bounds[i].left = left;
|
||||
Pcontrib_bounds[i].right = right;
|
||||
|
||||
n += (right - left + 1);
|
||||
}
|
||||
|
||||
/* Allocate memory for contributors. */
|
||||
|
||||
if ((n == 0) || ((Pcpool = (Contrib*)crnlib_calloc(n, sizeof(Contrib))) == NULL))
|
||||
{
|
||||
crnlib_free(Pcontrib);
|
||||
crnlib_free(Pcontrib_bounds);
|
||||
return NULL;
|
||||
}
|
||||
total = n;
|
||||
|
||||
Pcpool_next = Pcpool;
|
||||
|
||||
/* Create the list of source samples which
|
||||
* contribute to each destination sample.
|
||||
*/
|
||||
|
||||
for (i = 0; i < dst_x; i++)
|
||||
{
|
||||
int max_k = -1;
|
||||
Resample_Real max_w = -1e+20f;
|
||||
|
||||
center = Pcontrib_bounds[i].center;
|
||||
left = Pcontrib_bounds[i].left;
|
||||
right = Pcontrib_bounds[i].right;
|
||||
|
||||
Pcontrib[i].n = 0;
|
||||
Pcontrib[i].p = Pcpool_next;
|
||||
Pcpool_next += (right - left + 1);
|
||||
resampler_assert ((Pcpool_next - Pcpool) <= total);
|
||||
|
||||
total_weight = 0;
|
||||
|
||||
for (j = left; j <= right; j++)
|
||||
total_weight += (*Pfilter)((center - (Resample_Real)j) * xscale * oo_filter_scale);
|
||||
const Resample_Real norm = static_cast<Resample_Real>(1.0f / total_weight);
|
||||
|
||||
total_weight = 0;
|
||||
|
||||
#if RESAMPLER_DEBUG
|
||||
printf("%i: ", i);
|
||||
#endif
|
||||
|
||||
for (j = left; j <= right; j++)
|
||||
{
|
||||
weight = (*Pfilter)((center - (Resample_Real)j) * xscale * oo_filter_scale) * norm;
|
||||
if (weight == 0.0f)
|
||||
continue;
|
||||
|
||||
n = reflect(j, src_x, boundary_op);
|
||||
|
||||
#if RESAMPLER_DEBUG
|
||||
printf("%i(%f), ", n, weight);
|
||||
#endif
|
||||
|
||||
/* Increment the number of source
|
||||
* samples which contribute to the
|
||||
* current destination sample.
|
||||
*/
|
||||
|
||||
k = Pcontrib[i].n++;
|
||||
|
||||
Pcontrib[i].p[k].pixel = (unsigned short)n; /* store src sample number */
|
||||
Pcontrib[i].p[k].weight = weight; /* store src sample weight */
|
||||
|
||||
total_weight += weight; /* total weight of all contributors */
|
||||
|
||||
if (weight > max_w)
|
||||
{
|
||||
max_w = weight;
|
||||
max_k = k;
|
||||
}
|
||||
}
|
||||
|
||||
#if RESAMPLER_DEBUG
|
||||
printf("\n\n");
|
||||
#endif
|
||||
|
||||
//resampler_assert(Pcontrib[i].n);
|
||||
//resampler_assert(max_k != -1);
|
||||
if ((max_k == -1) || (Pcontrib[i].n == 0))
|
||||
{
|
||||
crnlib_free(Pcpool);
|
||||
crnlib_free(Pcontrib);
|
||||
crnlib_free(Pcontrib_bounds);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (total_weight != 1.0f)
|
||||
Pcontrib[i].p[max_k].weight += 1.0f - total_weight;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Handle case when there are more
|
||||
* destination samples than source
|
||||
* samples (upsampling).
|
||||
*/
|
||||
|
||||
half_width = filter_support * filter_scale;
|
||||
|
||||
// Find the source sample(s) that contribute to each destination sample.
|
||||
|
||||
for (i = 0, n = 0; i < dst_x; i++)
|
||||
{
|
||||
// Convert from discrete to continuous coordinates, scale, then convert back to discrete.
|
||||
center = ((Resample_Real)i + NUDGE) / xscale;
|
||||
center -= NUDGE;
|
||||
center += src_ofs;
|
||||
|
||||
left = cast_to_int((Resample_Real)floor(center - half_width));
|
||||
right = cast_to_int((Resample_Real)ceil(center + half_width));
|
||||
|
||||
Pcontrib_bounds[i].center = center;
|
||||
Pcontrib_bounds[i].left = left;
|
||||
Pcontrib_bounds[i].right = right;
|
||||
|
||||
n += (right - left + 1);
|
||||
}
|
||||
|
||||
/* Allocate memory for contributors. */
|
||||
|
||||
int total = n;
|
||||
if ((total == 0) || ((Pcpool = (Contrib*)crnlib_calloc(total, sizeof(Contrib))) == NULL))
|
||||
{
|
||||
crnlib_free(Pcontrib);
|
||||
crnlib_free(Pcontrib_bounds);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Pcpool_next = Pcpool;
|
||||
|
||||
/* Create the list of source samples which
|
||||
* contribute to each destination sample.
|
||||
*/
|
||||
|
||||
for (i = 0; i < dst_x; i++)
|
||||
{
|
||||
int max_k = -1;
|
||||
Resample_Real max_w = -1e+20f;
|
||||
|
||||
center = Pcontrib_bounds[i].center;
|
||||
left = Pcontrib_bounds[i].left;
|
||||
right = Pcontrib_bounds[i].right;
|
||||
|
||||
Pcontrib[i].n = 0;
|
||||
Pcontrib[i].p = Pcpool_next;
|
||||
Pcpool_next += (right - left + 1);
|
||||
resampler_assert((Pcpool_next - Pcpool) <= total);
|
||||
|
||||
total_weight = 0;
|
||||
for (j = left; j <= right; j++)
|
||||
total_weight += (*Pfilter)((center - (Resample_Real)j) * oo_filter_scale);
|
||||
|
||||
const Resample_Real norm = static_cast<Resample_Real>(1.0f / total_weight);
|
||||
|
||||
total_weight = 0;
|
||||
|
||||
#if RESAMPLER_DEBUG
|
||||
printf("%i: ", i);
|
||||
#endif
|
||||
|
||||
for (j = left; j <= right; j++)
|
||||
{
|
||||
weight = (*Pfilter)((center - (Resample_Real)j) * oo_filter_scale) * norm;
|
||||
if (weight == 0.0f)
|
||||
continue;
|
||||
|
||||
n = reflect(j, src_x, boundary_op);
|
||||
|
||||
#if RESAMPLER_DEBUG
|
||||
printf("%i(%f), ", n, weight);
|
||||
#endif
|
||||
|
||||
/* Increment the number of source
|
||||
* samples which contribute to the
|
||||
* current destination sample.
|
||||
*/
|
||||
|
||||
k = Pcontrib[i].n++;
|
||||
|
||||
Pcontrib[i].p[k].pixel = (unsigned short)n; /* store src sample number */
|
||||
Pcontrib[i].p[k].weight = weight; /* store src sample weight */
|
||||
|
||||
total_weight += weight; /* total weight of all contributors */
|
||||
|
||||
if (weight > max_w)
|
||||
{
|
||||
max_w = weight;
|
||||
max_k = k;
|
||||
}
|
||||
}
|
||||
|
||||
#if RESAMPLER_DEBUG
|
||||
printf("\n\n");
|
||||
#endif
|
||||
|
||||
//resampler_assert(Pcontrib[i].n);
|
||||
//resampler_assert(max_k != -1);
|
||||
|
||||
if ((max_k == -1) || (Pcontrib[i].n == 0))
|
||||
{
|
||||
crnlib_free(Pcpool);
|
||||
crnlib_free(Pcontrib);
|
||||
crnlib_free(Pcontrib_bounds);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (total_weight != 1.0f)
|
||||
Pcontrib[i].p[max_k].weight += 1.0f - total_weight;
|
||||
}
|
||||
}
|
||||
|
||||
#if RESAMPLER_DEBUG
|
||||
printf("*******\n");
|
||||
#endif
|
||||
|
||||
crnlib_free(Pcontrib_bounds);
|
||||
|
||||
return Pcontrib;
|
||||
}
|
||||
|
||||
void Resampler::resample_x(Sample* Pdst, const Sample* Psrc)
|
||||
{
|
||||
resampler_assert(Pdst);
|
||||
resampler_assert(Psrc);
|
||||
|
||||
int i, j;
|
||||
Sample total;
|
||||
Contrib_List *Pclist = m_Pclist_x;
|
||||
Contrib *p;
|
||||
|
||||
for (i = m_resample_dst_x; i > 0; i--, Pclist++)
|
||||
{
|
||||
#if CRNLIB_RESAMPLER_DEBUG_OPS
|
||||
total_ops += Pclist->n;
|
||||
#endif
|
||||
|
||||
for (j = Pclist->n, p = Pclist->p, total = 0; j > 0; j--, p++)
|
||||
total += Psrc[p->pixel] * p->weight;
|
||||
|
||||
*Pdst++ = total;
|
||||
}
|
||||
}
|
||||
|
||||
void Resampler::scale_y_mov(Sample* Ptmp, const Sample* Psrc, Resample_Real weight, int dst_x)
|
||||
{
|
||||
int i;
|
||||
|
||||
#if CRNLIB_RESAMPLER_DEBUG_OPS
|
||||
total_ops += dst_x;
|
||||
#endif
|
||||
|
||||
// Not += because temp buf wasn't cleared.
|
||||
for (i = dst_x; i > 0; i--)
|
||||
*Ptmp++ = *Psrc++ * weight;
|
||||
}
|
||||
|
||||
void Resampler::scale_y_add(Sample* Ptmp, const Sample* Psrc, Resample_Real weight, int dst_x)
|
||||
{
|
||||
#if CRNLIB_RESAMPLER_DEBUG_OPS
|
||||
total_ops += dst_x;
|
||||
#endif
|
||||
|
||||
for (int i = dst_x; i > 0; i--)
|
||||
(*Ptmp++) += *Psrc++ * weight;
|
||||
}
|
||||
|
||||
void Resampler::clamp(Sample* Pdst, int n)
|
||||
{
|
||||
while (n > 0)
|
||||
{
|
||||
Sample x = *Pdst;
|
||||
*Pdst++ = clamp_sample(x);
|
||||
n--;
|
||||
}
|
||||
}
|
||||
|
||||
void Resampler::resample_y(Sample* Pdst)
|
||||
{
|
||||
int i, j;
|
||||
Sample* Psrc;
|
||||
Contrib_List* Pclist = &m_Pclist_y[m_cur_dst_y];
|
||||
|
||||
Sample* Ptmp = m_delay_x_resample ? m_Ptmp_buf : Pdst;
|
||||
resampler_assert(Ptmp);
|
||||
|
||||
/* Process each contributor. */
|
||||
|
||||
for (i = 0; i < Pclist->n; i++)
|
||||
{
|
||||
/* locate the contributor's location in the scan
|
||||
* buffer -- the contributor must always be found!
|
||||
*/
|
||||
|
||||
for (j = 0; j < MAX_SCAN_BUF_SIZE; j++)
|
||||
if (m_Pscan_buf->scan_buf_y[j] == Pclist->p[i].pixel)
|
||||
break;
|
||||
|
||||
resampler_assert(j < MAX_SCAN_BUF_SIZE);
|
||||
|
||||
Psrc = m_Pscan_buf->scan_buf_l[j];
|
||||
|
||||
if (!i)
|
||||
scale_y_mov(Ptmp, Psrc, Pclist->p[i].weight, m_intermediate_x);
|
||||
else
|
||||
scale_y_add(Ptmp, Psrc, Pclist->p[i].weight, m_intermediate_x);
|
||||
|
||||
/* If this source line doesn't contribute to any
|
||||
* more destination lines then mark the scanline buffer slot
|
||||
* which holds this source line as free.
|
||||
* (The max. number of slots used depends on the Y
|
||||
* axis sampling factor and the scaled filter width.)
|
||||
*/
|
||||
|
||||
if (--m_Psrc_y_count[resampler_range_check(Pclist->p[i].pixel, m_resample_src_y)] == 0)
|
||||
{
|
||||
m_Psrc_y_flag[resampler_range_check(Pclist->p[i].pixel, m_resample_src_y)] = FALSE;
|
||||
m_Pscan_buf->scan_buf_y[j] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now generate the destination line */
|
||||
|
||||
if (m_delay_x_resample) // Was X resampling delayed until after Y resampling?
|
||||
{
|
||||
resampler_assert(Pdst != Ptmp);
|
||||
resample_x(Pdst, Ptmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
resampler_assert(Pdst == Ptmp);
|
||||
}
|
||||
|
||||
if (m_lo < m_hi)
|
||||
clamp(Pdst, m_resample_dst_x);
|
||||
}
|
||||
|
||||
bool Resampler::put_line(const Sample* Psrc)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (m_cur_src_y >= m_resample_src_y)
|
||||
return false;
|
||||
|
||||
/* Does this source line contribute
|
||||
* to any destination line? if not,
|
||||
* exit now.
|
||||
*/
|
||||
|
||||
if (!m_Psrc_y_count[resampler_range_check(m_cur_src_y, m_resample_src_y)])
|
||||
{
|
||||
m_cur_src_y++;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Find an empty slot in the scanline buffer. (FIXME: Perf. is terrible here with extreme scaling ratios.) */
|
||||
|
||||
for (i = 0; i < MAX_SCAN_BUF_SIZE; i++)
|
||||
if (m_Pscan_buf->scan_buf_y[i] == -1)
|
||||
break;
|
||||
|
||||
/* If the buffer is full, exit with an error. */
|
||||
|
||||
if (i == MAX_SCAN_BUF_SIZE)
|
||||
{
|
||||
m_status = STATUS_SCAN_BUFFER_FULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_Psrc_y_flag[resampler_range_check(m_cur_src_y, m_resample_src_y)] = TRUE;
|
||||
m_Pscan_buf->scan_buf_y[i] = m_cur_src_y;
|
||||
|
||||
/* Does this slot have any memory allocated to it? */
|
||||
|
||||
if (!m_Pscan_buf->scan_buf_l[i])
|
||||
{
|
||||
if ((m_Pscan_buf->scan_buf_l[i] = (Sample*)crnlib_malloc(m_intermediate_x * sizeof(Sample))) == NULL)
|
||||
{
|
||||
m_status = STATUS_OUT_OF_MEMORY;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Resampling on the X axis first?
|
||||
if (m_delay_x_resample)
|
||||
{
|
||||
resampler_assert(m_intermediate_x == m_resample_src_x);
|
||||
|
||||
// Y-X resampling order
|
||||
memcpy(m_Pscan_buf->scan_buf_l[i], Psrc, m_intermediate_x * sizeof(Sample));
|
||||
}
|
||||
else
|
||||
{
|
||||
resampler_assert(m_intermediate_x == m_resample_dst_x);
|
||||
|
||||
// X-Y resampling order
|
||||
resample_x(m_Pscan_buf->scan_buf_l[i], Psrc);
|
||||
}
|
||||
|
||||
m_cur_src_y++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const Resampler::Sample* Resampler::get_line()
|
||||
{
|
||||
int i;
|
||||
|
||||
/* If all the destination lines have been
|
||||
* generated, then always return NULL.
|
||||
*/
|
||||
|
||||
if (m_cur_dst_y == m_resample_dst_y)
|
||||
return NULL;
|
||||
|
||||
/* Check to see if all the required
|
||||
* contributors are present, if not,
|
||||
* return NULL.
|
||||
*/
|
||||
|
||||
for (i = 0; i < m_Pclist_y[m_cur_dst_y].n; i++)
|
||||
if (!m_Psrc_y_flag[resampler_range_check(m_Pclist_y[m_cur_dst_y].p[i].pixel, m_resample_src_y)])
|
||||
return NULL;
|
||||
|
||||
resample_y(m_Pdst_buf);
|
||||
|
||||
m_cur_dst_y++;
|
||||
|
||||
return m_Pdst_buf;
|
||||
}
|
||||
|
||||
Resampler::~Resampler()
|
||||
{
|
||||
int i;
|
||||
|
||||
#if CRNLIB_RESAMPLER_DEBUG_OPS
|
||||
printf("actual ops: %i\n", total_ops);
|
||||
#endif
|
||||
|
||||
crnlib_free(m_Pdst_buf);
|
||||
m_Pdst_buf = NULL;
|
||||
|
||||
if (m_Ptmp_buf)
|
||||
{
|
||||
crnlib_free(m_Ptmp_buf);
|
||||
m_Ptmp_buf = NULL;
|
||||
}
|
||||
|
||||
/* Don't deallocate a contibutor list
|
||||
* if the user passed us one of their own.
|
||||
*/
|
||||
|
||||
if ((m_Pclist_x) && (!m_clist_x_forced))
|
||||
{
|
||||
crnlib_free(m_Pclist_x->p);
|
||||
crnlib_free(m_Pclist_x);
|
||||
m_Pclist_x = NULL;
|
||||
}
|
||||
|
||||
if ((m_Pclist_y) && (!m_clist_y_forced))
|
||||
{
|
||||
crnlib_free(m_Pclist_y->p);
|
||||
crnlib_free(m_Pclist_y);
|
||||
m_Pclist_y = NULL;
|
||||
}
|
||||
|
||||
crnlib_free(m_Psrc_y_count);
|
||||
m_Psrc_y_count = NULL;
|
||||
|
||||
crnlib_free(m_Psrc_y_flag);
|
||||
m_Psrc_y_flag = NULL;
|
||||
|
||||
if (m_Pscan_buf)
|
||||
{
|
||||
for (i = 0; i < MAX_SCAN_BUF_SIZE; i++)
|
||||
crnlib_free(m_Pscan_buf->scan_buf_l[i]);
|
||||
|
||||
crnlib_free(m_Pscan_buf);
|
||||
m_Pscan_buf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void Resampler::restart()
|
||||
{
|
||||
if (STATUS_OKAY != m_status)
|
||||
return;
|
||||
|
||||
m_cur_src_y = m_cur_dst_y = 0;
|
||||
|
||||
int i, j;
|
||||
for (i = 0; i < m_resample_src_y; i++)
|
||||
{
|
||||
m_Psrc_y_count[i] = 0;
|
||||
m_Psrc_y_flag[i] = FALSE;
|
||||
}
|
||||
|
||||
for (i = 0; i < m_resample_dst_y; i++)
|
||||
{
|
||||
for (j = 0; j < m_Pclist_y[i].n; j++)
|
||||
m_Psrc_y_count[resampler_range_check(m_Pclist_y[i].p[j].pixel, m_resample_src_y)]++;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_SCAN_BUF_SIZE; i++)
|
||||
{
|
||||
m_Pscan_buf->scan_buf_y[i] = -1;
|
||||
|
||||
crnlib_free(m_Pscan_buf->scan_buf_l[i]);
|
||||
m_Pscan_buf->scan_buf_l[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Resampler::Resampler(int src_x, int src_y,
|
||||
int dst_x, int dst_y,
|
||||
Boundary_Op boundary_op,
|
||||
Resample_Real sample_low, Resample_Real sample_high,
|
||||
const char* Pfilter_name,
|
||||
Contrib_List* Pclist_x,
|
||||
Contrib_List* Pclist_y,
|
||||
Resample_Real filter_x_scale,
|
||||
Resample_Real filter_y_scale,
|
||||
Resample_Real src_x_ofs,
|
||||
Resample_Real src_y_ofs)
|
||||
{
|
||||
int i, j;
|
||||
Resample_Real support, (*func)(Resample_Real);
|
||||
|
||||
resampler_assert(src_x > 0);
|
||||
resampler_assert(src_y > 0);
|
||||
resampler_assert(dst_x > 0);
|
||||
resampler_assert(dst_y > 0);
|
||||
|
||||
#if CRNLIB_RESAMPLER_DEBUG_OPS
|
||||
total_ops = 0;
|
||||
#endif
|
||||
|
||||
m_lo = sample_low;
|
||||
m_hi = sample_high;
|
||||
|
||||
m_delay_x_resample = false;
|
||||
m_intermediate_x = 0;
|
||||
m_Pdst_buf = NULL;
|
||||
m_Ptmp_buf = NULL;
|
||||
m_clist_x_forced = false;
|
||||
m_Pclist_x = NULL;
|
||||
m_clist_y_forced = false;
|
||||
m_Pclist_y = NULL;
|
||||
m_Psrc_y_count = NULL;
|
||||
m_Psrc_y_flag = NULL;
|
||||
m_Pscan_buf = NULL;
|
||||
m_status = STATUS_OKAY;
|
||||
|
||||
m_resample_src_x = src_x;
|
||||
m_resample_src_y = src_y;
|
||||
m_resample_dst_x = dst_x;
|
||||
m_resample_dst_y = dst_y;
|
||||
|
||||
m_boundary_op = boundary_op;
|
||||
|
||||
if ((m_Pdst_buf = (Sample*)crnlib_malloc(m_resample_dst_x * sizeof(Sample))) == NULL)
|
||||
{
|
||||
m_status = STATUS_OUT_OF_MEMORY;
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the specified filter.
|
||||
|
||||
if (Pfilter_name == NULL)
|
||||
Pfilter_name = CRNLIB_RESAMPLER_DEFAULT_FILTER;
|
||||
|
||||
for (i = 0; i < g_num_resample_filters; i++)
|
||||
if (strcmp(Pfilter_name, g_resample_filters[i].name) == 0)
|
||||
break;
|
||||
|
||||
if (i == g_num_resample_filters)
|
||||
{
|
||||
m_status = STATUS_BAD_FILTER_NAME;
|
||||
return;
|
||||
}
|
||||
|
||||
func = g_resample_filters[i].func;
|
||||
support = g_resample_filters[i].support;
|
||||
|
||||
/* Create contributor lists, unless the user supplied custom lists. */
|
||||
|
||||
if (!Pclist_x)
|
||||
{
|
||||
m_Pclist_x = make_clist(m_resample_src_x, m_resample_dst_x, m_boundary_op, func, support, filter_x_scale, src_x_ofs);
|
||||
if (!m_Pclist_x)
|
||||
{
|
||||
m_status = STATUS_OUT_OF_MEMORY;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Pclist_x = Pclist_x;
|
||||
m_clist_x_forced = true;
|
||||
}
|
||||
|
||||
if (!Pclist_y)
|
||||
{
|
||||
m_Pclist_y = make_clist(m_resample_src_y, m_resample_dst_y, m_boundary_op, func, support, filter_y_scale, src_y_ofs);
|
||||
if (!m_Pclist_y)
|
||||
{
|
||||
m_status = STATUS_OUT_OF_MEMORY;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Pclist_y = Pclist_y;
|
||||
m_clist_y_forced = true;
|
||||
}
|
||||
|
||||
if ((m_Psrc_y_count = (int*)crnlib_calloc(m_resample_src_y, sizeof(int))) == NULL)
|
||||
{
|
||||
m_status = STATUS_OUT_OF_MEMORY;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((m_Psrc_y_flag = (unsigned char*)crnlib_calloc(m_resample_src_y, sizeof(unsigned char))) == NULL)
|
||||
{
|
||||
m_status = STATUS_OUT_OF_MEMORY;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Count how many times each source line
|
||||
* contributes to a destination line.
|
||||
*/
|
||||
|
||||
for (i = 0; i < m_resample_dst_y; i++)
|
||||
for (j = 0; j < m_Pclist_y[i].n; j++)
|
||||
m_Psrc_y_count[resampler_range_check(m_Pclist_y[i].p[j].pixel, m_resample_src_y)]++;
|
||||
|
||||
if ((m_Pscan_buf = (Scan_Buf*)crnlib_malloc(sizeof(Scan_Buf))) == NULL)
|
||||
{
|
||||
m_status = STATUS_OUT_OF_MEMORY;
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_SCAN_BUF_SIZE; i++)
|
||||
{
|
||||
m_Pscan_buf->scan_buf_y[i] = -1;
|
||||
m_Pscan_buf->scan_buf_l[i] = NULL;
|
||||
}
|
||||
|
||||
m_cur_src_y = m_cur_dst_y = 0;
|
||||
{
|
||||
// Determine which axis to resample first by comparing the number of multiplies required
|
||||
// for each possibility.
|
||||
int x_ops = count_ops(m_Pclist_x, m_resample_dst_x);
|
||||
int y_ops = count_ops(m_Pclist_y, m_resample_dst_y);
|
||||
|
||||
// Hack 10/2000: Weight Y axis ops a little more than X axis ops.
|
||||
// (Y axis ops use more cache resources.)
|
||||
int xy_ops = x_ops * m_resample_src_y +
|
||||
(4 * y_ops * m_resample_dst_x)/3;
|
||||
|
||||
int yx_ops = (4 * y_ops * m_resample_src_x)/3 +
|
||||
x_ops * m_resample_dst_y;
|
||||
|
||||
#if CRNLIB_RESAMPLER_DEBUG_OPS
|
||||
printf("src: %i %i\n", m_resample_src_x, m_resample_src_y);
|
||||
printf("dst: %i %i\n", m_resample_dst_x, m_resample_dst_y);
|
||||
printf("x_ops: %i\n", x_ops);
|
||||
printf("y_ops: %i\n", y_ops);
|
||||
printf("xy_ops: %i\n", xy_ops);
|
||||
printf("yx_ops: %i\n", yx_ops);
|
||||
#endif
|
||||
|
||||
// Now check which resample order is better. In case of a tie, choose the order
|
||||
// which buffers the least amount of data.
|
||||
if ((xy_ops > yx_ops) ||
|
||||
((xy_ops == yx_ops) && (m_resample_src_x < m_resample_dst_x))
|
||||
)
|
||||
{
|
||||
m_delay_x_resample = true;
|
||||
m_intermediate_x = m_resample_src_x;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_delay_x_resample = false;
|
||||
m_intermediate_x = m_resample_dst_x;
|
||||
}
|
||||
#if CRNLIB_RESAMPLER_DEBUG_OPS
|
||||
printf("delaying: %i\n", m_delay_x_resample);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (m_delay_x_resample)
|
||||
{
|
||||
if ((m_Ptmp_buf = (Sample*)crnlib_malloc(m_intermediate_x * sizeof(Sample))) == NULL)
|
||||
{
|
||||
m_status = STATUS_OUT_OF_MEMORY;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Resampler::get_clists(Contrib_List** ptr_clist_x, Contrib_List** ptr_clist_y)
|
||||
{
|
||||
if (ptr_clist_x)
|
||||
*ptr_clist_x = m_Pclist_x;
|
||||
|
||||
if (ptr_clist_y)
|
||||
*ptr_clist_y = m_Pclist_y;
|
||||
}
|
||||
|
||||
int Resampler::get_filter_num()
|
||||
{
|
||||
return g_num_resample_filters;
|
||||
}
|
||||
|
||||
const char* Resampler::get_filter_name(int filter_num)
|
||||
{
|
||||
if ((filter_num < 0) || (filter_num >= g_num_resample_filters))
|
||||
return NULL;
|
||||
else
|
||||
return g_resample_filters[filter_num].name;
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,173 @@
|
||||
// File: crn_resampler.h
|
||||
// RG: This is public domain code, originally derived from Graphics Gems 3, see: http://code.google.com/p/imageresampler/
|
||||
#pragma once
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
#define CRNLIB_RESAMPLER_DEBUG_OPS 0
|
||||
#define CRNLIB_RESAMPLER_DEFAULT_FILTER "lanczos4"
|
||||
|
||||
#define CRNLIB_RESAMPLER_MAX_DIMENSION 16384
|
||||
|
||||
// float or double
|
||||
typedef float Resample_Real;
|
||||
|
||||
class Resampler
|
||||
{
|
||||
public:
|
||||
typedef Resample_Real Sample;
|
||||
|
||||
struct Contrib
|
||||
{
|
||||
Resample_Real weight;
|
||||
unsigned short pixel;
|
||||
};
|
||||
|
||||
struct Contrib_List
|
||||
{
|
||||
unsigned short n;
|
||||
Contrib* p;
|
||||
};
|
||||
|
||||
enum Boundary_Op
|
||||
{
|
||||
BOUNDARY_WRAP = 0,
|
||||
BOUNDARY_REFLECT = 1,
|
||||
BOUNDARY_CLAMP = 2
|
||||
};
|
||||
|
||||
enum Status
|
||||
{
|
||||
STATUS_OKAY = 0,
|
||||
STATUS_OUT_OF_MEMORY = 1,
|
||||
STATUS_BAD_FILTER_NAME = 2,
|
||||
STATUS_SCAN_BUFFER_FULL = 3
|
||||
};
|
||||
|
||||
// src_x/src_y - Input dimensions
|
||||
// dst_x/dst_y - Output dimensions
|
||||
// boundary_op - How to sample pixels near the image boundaries
|
||||
// sample_low/sample_high - Clamp output samples to specified range, or disable clamping if sample_low >= sample_high
|
||||
// Pclist_x/Pclist_y - Optional pointers to contributor lists from another instance of a Resampler
|
||||
// src_x_ofs/src_y_ofs - Offset input image by specified amount (fractional values okay)
|
||||
Resampler(
|
||||
int src_x, int src_y,
|
||||
int dst_x, int dst_y,
|
||||
Boundary_Op boundary_op = BOUNDARY_CLAMP,
|
||||
Resample_Real sample_low = 0.0f, Resample_Real sample_high = 0.0f,
|
||||
const char* Pfilter_name = CRNLIB_RESAMPLER_DEFAULT_FILTER,
|
||||
Contrib_List* Pclist_x = NULL,
|
||||
Contrib_List* Pclist_y = NULL,
|
||||
Resample_Real filter_x_scale = 1.0f,
|
||||
Resample_Real filter_y_scale = 1.0f,
|
||||
Resample_Real src_x_ofs = 0.0f,
|
||||
Resample_Real src_y_ofs = 0.0f);
|
||||
|
||||
~Resampler();
|
||||
|
||||
// Reinits resampler so it can handle another frame.
|
||||
void restart();
|
||||
|
||||
// false on out of memory.
|
||||
bool put_line(const Sample* Psrc);
|
||||
|
||||
// NULL if no scanlines are currently available (give the resampler more scanlines!)
|
||||
const Sample* get_line();
|
||||
|
||||
Status status() const { return m_status; }
|
||||
|
||||
// Returned contributor lists can be shared with another Resampler.
|
||||
void get_clists(Contrib_List** ptr_clist_x, Contrib_List** ptr_clist_y);
|
||||
Contrib_List* get_clist_x() const { return m_Pclist_x; }
|
||||
Contrib_List* get_clist_y() const { return m_Pclist_y; }
|
||||
|
||||
// Filter accessors.
|
||||
static int get_filter_num();
|
||||
static const char* get_filter_name(int filter_num);
|
||||
|
||||
static Contrib_List* make_clist(
|
||||
int src_x, int dst_x, Boundary_Op boundary_op,
|
||||
Resample_Real (*Pfilter)(Resample_Real),
|
||||
Resample_Real filter_support,
|
||||
Resample_Real filter_scale,
|
||||
Resample_Real src_ofs);
|
||||
|
||||
private:
|
||||
Resampler();
|
||||
Resampler(const Resampler& o);
|
||||
Resampler& operator= (const Resampler& o);
|
||||
|
||||
#ifdef CRNLIB_RESAMPLER_DEBUG_OPS
|
||||
int total_ops;
|
||||
#endif
|
||||
|
||||
int m_intermediate_x;
|
||||
|
||||
int m_resample_src_x;
|
||||
int m_resample_src_y;
|
||||
int m_resample_dst_x;
|
||||
int m_resample_dst_y;
|
||||
|
||||
Boundary_Op m_boundary_op;
|
||||
|
||||
Sample* m_Pdst_buf;
|
||||
Sample* m_Ptmp_buf;
|
||||
|
||||
Contrib_List* m_Pclist_x;
|
||||
Contrib_List* m_Pclist_y;
|
||||
|
||||
bool m_clist_x_forced;
|
||||
bool m_clist_y_forced;
|
||||
|
||||
bool m_delay_x_resample;
|
||||
|
||||
int* m_Psrc_y_count;
|
||||
unsigned char* m_Psrc_y_flag;
|
||||
|
||||
// The maximum number of scanlines that can be buffered at one time.
|
||||
enum { MAX_SCAN_BUF_SIZE = CRNLIB_RESAMPLER_MAX_DIMENSION };
|
||||
|
||||
struct Scan_Buf
|
||||
{
|
||||
int scan_buf_y[MAX_SCAN_BUF_SIZE];
|
||||
Sample* scan_buf_l[MAX_SCAN_BUF_SIZE];
|
||||
};
|
||||
|
||||
Scan_Buf* m_Pscan_buf;
|
||||
|
||||
int m_cur_src_y;
|
||||
int m_cur_dst_y;
|
||||
|
||||
Status m_status;
|
||||
|
||||
void resample_x(Sample* Pdst, const Sample* Psrc);
|
||||
void scale_y_mov(Sample* Ptmp, const Sample* Psrc, Resample_Real weight, int dst_x);
|
||||
void scale_y_add(Sample* Ptmp, const Sample* Psrc, Resample_Real weight, int dst_x);
|
||||
void clamp(Sample* Pdst, int n);
|
||||
void resample_y(Sample* Pdst);
|
||||
|
||||
static int reflect(const int j, const int src_x, const Boundary_Op boundary_op);
|
||||
|
||||
inline int count_ops(Contrib_List* Pclist, int k)
|
||||
{
|
||||
int i, t = 0;
|
||||
for (i = 0; i < k; i++)
|
||||
t += Pclist[i].n;
|
||||
return (t);
|
||||
}
|
||||
|
||||
Resample_Real m_lo;
|
||||
Resample_Real m_hi;
|
||||
|
||||
inline Resample_Real clamp_sample(Resample_Real f) const
|
||||
{
|
||||
if (f < m_lo)
|
||||
f = m_lo;
|
||||
else if (f > m_hi)
|
||||
f = m_hi;
|
||||
return f;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace crnlib
|
||||
|
||||
@@ -0,0 +1,609 @@
|
||||
// File: crn_ryg_dxt.cpp
|
||||
// RYG's real-time DXT compressor - Public domain.
|
||||
#include "crn_core.h"
|
||||
#include "crn_ryg_types.hpp"
|
||||
#include "crn_ryg_dxt.hpp"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (disable: 4244) // conversion from 'a' to 'b', possible loss of data
|
||||
#endif
|
||||
|
||||
namespace ryg_dxt
|
||||
{
|
||||
// Couple of tables...
|
||||
sU8 Expand5[32];
|
||||
sU8 Expand6[64];
|
||||
sU8 OMatch5[256][2];
|
||||
sU8 OMatch6[256][2];
|
||||
sU8 OMatch5_3[256][2];
|
||||
sU8 OMatch6_3[256][2];
|
||||
sU8 QuantRBTab[256+16];
|
||||
sU8 QuantGTab[256+16];
|
||||
|
||||
static sInt Mul8Bit(sInt a,sInt b)
|
||||
{
|
||||
sInt t = a*b + 128;
|
||||
return (t + (t >> 8)) >> 8;
|
||||
}
|
||||
|
||||
union Pixel
|
||||
{
|
||||
struct
|
||||
{
|
||||
sU8 b,g,r,a;
|
||||
};
|
||||
sU32 v;
|
||||
|
||||
void From16Bit(sU16 v)
|
||||
{
|
||||
sInt rv = (v & 0xf800) >> 11;
|
||||
sInt gv = (v & 0x07e0) >> 5;
|
||||
sInt bv = (v & 0x001f) >> 0;
|
||||
|
||||
a = 0;
|
||||
r = Expand5[rv];
|
||||
g = Expand6[gv];
|
||||
b = Expand5[bv];
|
||||
}
|
||||
|
||||
sU16 As16Bit() const
|
||||
{
|
||||
return (Mul8Bit(r,31) << 11) + (Mul8Bit(g,63) << 5) + Mul8Bit(b,31);
|
||||
}
|
||||
|
||||
void LerpRGB(const Pixel &p1,const Pixel &p2,sInt f)
|
||||
{
|
||||
r = p1.r + Mul8Bit(p2.r - p1.r,f);
|
||||
g = p1.g + Mul8Bit(p2.g - p1.g,f);
|
||||
b = p1.b + Mul8Bit(p2.b - p1.b,f);
|
||||
}
|
||||
};
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
static void PrepareOptTable4(sU8 *Table,const sU8 *expand,sInt size)
|
||||
{
|
||||
for(sInt i=0;i<256;i++)
|
||||
{
|
||||
sInt bestErr = 256;
|
||||
|
||||
for(sInt min=0;min<size;min++)
|
||||
{
|
||||
for(sInt max=0;max<size;max++)
|
||||
{
|
||||
sInt mine = expand[min];
|
||||
sInt maxe = expand[max];
|
||||
//sInt err = sAbs(maxe + Mul8Bit(mine-maxe,0x55) - i);
|
||||
sInt err = sAbs(((maxe*2+mine)/3) - i);
|
||||
err += ((sAbs(maxe-mine)*8)>>8); // approx. .03f
|
||||
|
||||
if(err < bestErr)
|
||||
{
|
||||
Table[i*2+0] = max;
|
||||
Table[i*2+1] = min;
|
||||
bestErr = err;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void PrepareOptTable3(sU8 *Table,const sU8 *expand,sInt size)
|
||||
{
|
||||
for(sInt i=0;i<256;i++)
|
||||
{
|
||||
sInt bestErr = 256;
|
||||
|
||||
for(sInt min=0;min<size;min++)
|
||||
{
|
||||
for(sInt max=0;max<size;max++)
|
||||
{
|
||||
sInt mine = expand[min];
|
||||
sInt maxe = expand[max];
|
||||
sInt err = sAbs(((mine + maxe) >> 1) - i);
|
||||
err += ((sAbs(maxe-mine)*8)>>8); // approx. .03f
|
||||
|
||||
if(err < bestErr)
|
||||
{
|
||||
Table[i*2+0] = max;
|
||||
Table[i*2+1] = min;
|
||||
bestErr = err;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void EvalColors(Pixel *color,sU16 c0,sU16 c1)
|
||||
{
|
||||
color[0].From16Bit(c0);
|
||||
color[1].From16Bit(c1);
|
||||
color[2].LerpRGB(color[0],color[1],0x55);
|
||||
color[3].LerpRGB(color[0],color[1],0xaa);
|
||||
}
|
||||
|
||||
// Block dithering function. Simply dithers a block to 565 RGB.
|
||||
// (Floyd-Steinberg)
|
||||
static void DitherBlock(Pixel *dest,const Pixel *block)
|
||||
{
|
||||
sInt err[8],*ep1 = err,*ep2 = err+4;
|
||||
|
||||
// process channels seperately
|
||||
for(sInt ch=0;ch<3;ch++)
|
||||
{
|
||||
sU8 *bp = (sU8 *) block;
|
||||
sU8 *dp = (sU8 *) dest;
|
||||
sU8 *quant = (ch == 1) ? QuantGTab+8 : QuantRBTab+8;
|
||||
|
||||
bp += ch;
|
||||
dp += ch;
|
||||
sSetMem(err,0,sizeof(err));
|
||||
|
||||
for(sInt y=0;y<4;y++)
|
||||
{
|
||||
// pixel 0
|
||||
dp[ 0] = quant[bp[ 0] + ((3*ep2[1] + 5*ep2[0]) >> 4)];
|
||||
ep1[0] = bp[ 0] - dp[ 0];
|
||||
|
||||
// pixel 1
|
||||
dp[ 4] = quant[bp[ 4] + ((7*ep1[0] + 3*ep2[2] + 5*ep2[1] + ep2[0]) >> 4)];
|
||||
ep1[1] = bp[ 4] - dp[ 4];
|
||||
|
||||
// pixel 2
|
||||
dp[ 8] = quant[bp[ 8] + ((7*ep1[1] + 3*ep2[3] + 5*ep2[2] + ep2[1]) >> 4)];
|
||||
ep1[2] = bp[ 8] - dp[ 8];
|
||||
|
||||
// pixel 3
|
||||
dp[12] = quant[bp[12] + ((7*ep1[2] + 5*ep2[3] + ep2[2]) >> 4)];
|
||||
ep1[3] = bp[12] - dp[12];
|
||||
|
||||
// advance to next line
|
||||
sSwap(ep1,ep2);
|
||||
bp += 16;
|
||||
dp += 16;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The color matching function
|
||||
static sU32 MatchColorsBlock(const Pixel *block,const Pixel *color,sBool dither)
|
||||
{
|
||||
sU32 mask = 0;
|
||||
sInt dirr = color[0].r - color[1].r;
|
||||
sInt dirg = color[0].g - color[1].g;
|
||||
sInt dirb = color[0].b - color[1].b;
|
||||
|
||||
sInt dots[16];
|
||||
for(sInt i=0;i<16;i++)
|
||||
dots[i] = block[i].r*dirr + block[i].g*dirg + block[i].b*dirb;
|
||||
|
||||
sInt stops[4];
|
||||
for(sInt i=0;i<4;i++)
|
||||
stops[i] = color[i].r*dirr + color[i].g*dirg + color[i].b*dirb;
|
||||
|
||||
sInt c0Point = (stops[1] + stops[3]) >> 1;
|
||||
sInt halfPoint = (stops[3] + stops[2]) >> 1;
|
||||
sInt c3Point = (stops[2] + stops[0]) >> 1;
|
||||
|
||||
if(!dither)
|
||||
{
|
||||
// the version without dithering is straightforward
|
||||
for(sInt i=15;i>=0;i--)
|
||||
{
|
||||
mask <<= 2;
|
||||
sInt dot = dots[i];
|
||||
|
||||
if(dot < halfPoint)
|
||||
mask |= (dot < c0Point) ? 1 : 3;
|
||||
else
|
||||
mask |= (dot < c3Point) ? 2 : 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// with floyd-steinberg dithering (see above)
|
||||
sInt err[8],*ep1 = err,*ep2 = err+4;
|
||||
sInt *dp = dots;
|
||||
|
||||
c0Point <<= 4;
|
||||
halfPoint <<= 4;
|
||||
c3Point <<= 4;
|
||||
for(sInt i=0;i<8;i++)
|
||||
err[i] = 0;
|
||||
|
||||
for(sInt y=0;y<4;y++)
|
||||
{
|
||||
sInt dot,lmask,step;
|
||||
|
||||
// pixel 0
|
||||
dot = (dp[0] << 4) + (3*ep2[1] + 5*ep2[0]);
|
||||
if(dot < halfPoint)
|
||||
step = (dot < c0Point) ? 1 : 3;
|
||||
else
|
||||
step = (dot < c3Point) ? 2 : 0;
|
||||
|
||||
ep1[0] = dp[0] - stops[step];
|
||||
lmask = step;
|
||||
|
||||
// pixel 1
|
||||
dot = (dp[1] << 4) + (7*ep1[0] + 3*ep2[2] + 5*ep2[1] + ep2[0]);
|
||||
if(dot < halfPoint)
|
||||
step = (dot < c0Point) ? 1 : 3;
|
||||
else
|
||||
step = (dot < c3Point) ? 2 : 0;
|
||||
|
||||
ep1[1] = dp[1] - stops[step];
|
||||
lmask |= step<<2;
|
||||
|
||||
// pixel 2
|
||||
dot = (dp[2] << 4) + (7*ep1[1] + 3*ep2[3] + 5*ep2[2] + ep2[1]);
|
||||
if(dot < halfPoint)
|
||||
step = (dot < c0Point) ? 1 : 3;
|
||||
else
|
||||
step = (dot < c3Point) ? 2 : 0;
|
||||
|
||||
ep1[2] = dp[2] - stops[step];
|
||||
lmask |= step<<4;
|
||||
|
||||
// pixel 3
|
||||
dot = (dp[3] << 4) + (7*ep1[2] + 5*ep2[3] + ep2[2]);
|
||||
if(dot < halfPoint)
|
||||
step = (dot < c0Point) ? 1 : 3;
|
||||
else
|
||||
step = (dot < c3Point) ? 2 : 0;
|
||||
|
||||
ep1[3] = dp[3] - stops[step];
|
||||
lmask |= step<<6;
|
||||
|
||||
// advance to next line
|
||||
sSwap(ep1,ep2);
|
||||
dp += 4;
|
||||
mask |= lmask << (y*8);
|
||||
}
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
// The color optimization function. (Clever code, part 1)
|
||||
static void OptimizeColorsBlock(const Pixel *block,sU16 &max16,sU16 &min16)
|
||||
{
|
||||
static const sInt nIterPower = 4;
|
||||
|
||||
// determine color distribution
|
||||
sInt mu[3],min[3],max[3];
|
||||
|
||||
for(sInt ch=0;ch<3;ch++)
|
||||
{
|
||||
const sU8 *bp = ((const sU8 *) block) + ch;
|
||||
sInt muv,minv,maxv;
|
||||
|
||||
muv = minv = maxv = bp[0];
|
||||
for(sInt i=4;i<64;i+=4)
|
||||
{
|
||||
muv += bp[i];
|
||||
minv = sMin<sInt>(minv,bp[i]);
|
||||
maxv = sMax<sInt>(maxv,bp[i]);
|
||||
}
|
||||
|
||||
mu[ch] = (muv + 8) >> 4;
|
||||
min[ch] = minv;
|
||||
max[ch] = maxv;
|
||||
}
|
||||
|
||||
// determine covariance matrix
|
||||
sInt cov[6];
|
||||
for(sInt i=0;i<6;i++)
|
||||
cov[i] = 0;
|
||||
|
||||
for(sInt i=0;i<16;i++)
|
||||
{
|
||||
sInt r = block[i].r - mu[2];
|
||||
sInt g = block[i].g - mu[1];
|
||||
sInt b = block[i].b - mu[0];
|
||||
|
||||
cov[0] += r*r;
|
||||
cov[1] += r*g;
|
||||
cov[2] += r*b;
|
||||
cov[3] += g*g;
|
||||
cov[4] += g*b;
|
||||
cov[5] += b*b;
|
||||
}
|
||||
|
||||
// convert covariance matrix to float, find principal axis via power iter
|
||||
sF32 covf[6],vfr,vfg,vfb;
|
||||
for(sInt i=0;i<6;i++)
|
||||
covf[i] = cov[i] / 255.0f;
|
||||
|
||||
vfr = max[2] - min[2];
|
||||
vfg = max[1] - min[1];
|
||||
vfb = max[0] - min[0];
|
||||
|
||||
for(sInt iter=0;iter<nIterPower;iter++)
|
||||
{
|
||||
sF32 r = vfr*covf[0] + vfg*covf[1] + vfb*covf[2];
|
||||
sF32 g = vfr*covf[1] + vfg*covf[3] + vfb*covf[4];
|
||||
sF32 b = vfr*covf[2] + vfg*covf[4] + vfb*covf[5];
|
||||
|
||||
vfr = r;
|
||||
vfg = g;
|
||||
vfb = b;
|
||||
}
|
||||
|
||||
sF32 magn = sMax(sMax(sFAbs(vfr),sFAbs(vfg)),sFAbs(vfb));
|
||||
sInt v_r,v_g,v_b;
|
||||
|
||||
if(magn < 4.0f) // too small, default to luminance
|
||||
{
|
||||
v_r = 148;
|
||||
v_g = 300;
|
||||
v_b = 58;
|
||||
}
|
||||
else
|
||||
{
|
||||
magn = 512.0f / magn;
|
||||
v_r = vfr * magn;
|
||||
v_g = vfg * magn;
|
||||
v_b = vfb * magn;
|
||||
}
|
||||
|
||||
// Pick colors at extreme points
|
||||
sInt mind = 0x7fffffff,maxd = -0x7fffffff;
|
||||
Pixel minp,maxp;
|
||||
|
||||
for(sInt i=0;i<16;i++)
|
||||
{
|
||||
sInt dot = block[i].r*v_r + block[i].g*v_g + block[i].b*v_b;
|
||||
|
||||
if(dot < mind)
|
||||
{
|
||||
mind = dot;
|
||||
minp = block[i];
|
||||
}
|
||||
|
||||
if(dot > maxd)
|
||||
{
|
||||
maxd = dot;
|
||||
maxp = block[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Reduce to 16 bit colors
|
||||
max16 = maxp.As16Bit();
|
||||
min16 = minp.As16Bit();
|
||||
}
|
||||
|
||||
// The refinement function. (Clever code, part 2)
|
||||
// Tries to optimize colors to suit block contents better.
|
||||
// (By solving a least squares system via normal equations+Cramer's rule)
|
||||
static sBool RefineBlock(const Pixel *block,sU16 &max16,sU16 &min16,sU32 mask)
|
||||
{
|
||||
static const sInt w1Tab[4] = { 3,0,2,1 };
|
||||
static const sInt prods[4] = { 0x090000,0x000900,0x040102,0x010402 };
|
||||
// ^some magic to save a lot of multiplies in the accumulating loop...
|
||||
|
||||
sInt akku = 0;
|
||||
sInt At1_r,At1_g,At1_b;
|
||||
sInt At2_r,At2_g,At2_b;
|
||||
sU32 cm = mask;
|
||||
|
||||
At1_r = At1_g = At1_b = 0;
|
||||
At2_r = At2_g = At2_b = 0;
|
||||
for(sInt i=0;i<16;i++,cm>>=2)
|
||||
{
|
||||
sInt step = cm&3;
|
||||
sInt w1 = w1Tab[step];
|
||||
sInt r = block[i].r;
|
||||
sInt g = block[i].g;
|
||||
sInt b = block[i].b;
|
||||
|
||||
akku += prods[step];
|
||||
At1_r += w1*r;
|
||||
At1_g += w1*g;
|
||||
At1_b += w1*b;
|
||||
At2_r += r;
|
||||
At2_g += g;
|
||||
At2_b += b;
|
||||
}
|
||||
|
||||
At2_r = 3*At2_r - At1_r;
|
||||
At2_g = 3*At2_g - At1_g;
|
||||
At2_b = 3*At2_b - At1_b;
|
||||
|
||||
// extract solutions and decide solvability
|
||||
sInt xx = akku >> 16;
|
||||
sInt yy = (akku >> 8) & 0xff;
|
||||
sInt xy = (akku >> 0) & 0xff;
|
||||
|
||||
if(!yy || !xx || xx*yy == xy*xy)
|
||||
return sFALSE;
|
||||
|
||||
sF32 frb = 3.0f * 31.0f / 255.0f / (xx*yy - xy*xy);
|
||||
sF32 fg = frb * 63.0f / 31.0f;
|
||||
|
||||
sU16 oldMin = min16;
|
||||
sU16 oldMax = max16;
|
||||
|
||||
// solve.
|
||||
max16 = sClamp<sInt>((At1_r*yy - At2_r*xy)*frb+0.5f,0,31) << 11;
|
||||
max16 |= sClamp<sInt>((At1_g*yy - At2_g*xy)*fg +0.5f,0,63) << 5;
|
||||
max16 |= sClamp<sInt>((At1_b*yy - At2_b*xy)*frb+0.5f,0,31) << 0;
|
||||
|
||||
min16 = sClamp<sInt>((At2_r*xx - At1_r*xy)*frb+0.5f,0,31) << 11;
|
||||
min16 |= sClamp<sInt>((At2_g*xx - At1_g*xy)*fg +0.5f,0,63) << 5;
|
||||
min16 |= sClamp<sInt>((At2_b*xx - At1_b*xy)*frb+0.5f,0,31) << 0;
|
||||
|
||||
return oldMin != min16 || oldMax != max16;
|
||||
}
|
||||
|
||||
// Color block compression
|
||||
static void CompressColorBlock(sU8 *dest,const sU32 *src,sInt quality)
|
||||
{
|
||||
const Pixel *block = (const Pixel *) src;
|
||||
Pixel dblock[16],color[4];
|
||||
|
||||
// check if block is constant
|
||||
sU32 min,max;
|
||||
min = max = block[0].v;
|
||||
|
||||
for(sInt i=1;i<16;i++)
|
||||
{
|
||||
min = sMin(min,block[i].v);
|
||||
max = sMax(max,block[i].v);
|
||||
}
|
||||
|
||||
// perform block compression
|
||||
sU16 min16,max16;
|
||||
sU32 mask;
|
||||
|
||||
if(min != max) // no constant color
|
||||
{
|
||||
// first step: compute dithered version for PCA if desired
|
||||
if(quality)
|
||||
DitherBlock(dblock,block);
|
||||
|
||||
// second step: pca+map along principal axis
|
||||
OptimizeColorsBlock(quality ? dblock : block,max16,min16);
|
||||
if(max16 != min16)
|
||||
{
|
||||
EvalColors(color,max16,min16);
|
||||
mask = MatchColorsBlock(block,color,quality != 0);
|
||||
}
|
||||
else
|
||||
mask = 0;
|
||||
|
||||
// third step: refine
|
||||
if(RefineBlock(quality ? dblock : block,max16,min16,mask))
|
||||
{
|
||||
if(max16 != min16)
|
||||
{
|
||||
EvalColors(color,max16,min16);
|
||||
mask = MatchColorsBlock(block,color,quality != 0);
|
||||
}
|
||||
else
|
||||
mask = 0;
|
||||
}
|
||||
|
||||
}
|
||||
else // constant color
|
||||
{
|
||||
sInt r = block[0].r;
|
||||
sInt g = block[0].g;
|
||||
sInt b = block[0].b;
|
||||
|
||||
mask = 0xaaaaaaaa;
|
||||
max16 = (OMatch5[r][0]<<11) | (OMatch6[g][0]<<5) | OMatch5[b][0];
|
||||
min16 = (OMatch5[r][1]<<11) | (OMatch6[g][1]<<5) | OMatch5[b][1];
|
||||
}
|
||||
|
||||
// write the color block
|
||||
if(max16 < min16)
|
||||
{
|
||||
sSwap(max16,min16);
|
||||
mask ^= 0x55555555;
|
||||
}
|
||||
|
||||
((sU16 *) dest)[0] = max16;
|
||||
((sU16 *) dest)[1] = min16;
|
||||
((sU32 *) dest)[1] = mask;
|
||||
}
|
||||
|
||||
// Alpha block compression (this is easy for a change)
|
||||
static void CompressAlphaBlock(sU8 *dest,const sU32 *src,sInt quality)
|
||||
{
|
||||
quality;
|
||||
const Pixel *block = (const Pixel *) src;
|
||||
|
||||
// find min/max color
|
||||
sInt min,max;
|
||||
min = max = block[0].a;
|
||||
|
||||
for(sInt i=1;i<16;i++)
|
||||
{
|
||||
min = sMin<sInt>(min,block[i].a);
|
||||
max = sMax<sInt>(max,block[i].a);
|
||||
}
|
||||
|
||||
// encode them
|
||||
*dest++ = max;
|
||||
*dest++ = min;
|
||||
|
||||
// determine bias and emit color indices
|
||||
sInt dist = max-min;
|
||||
sInt bias = min*7 - (dist >> 1);
|
||||
sInt dist4 = dist*4;
|
||||
sInt dist2 = dist*2;
|
||||
sInt bits = 0,mask=0;
|
||||
|
||||
for(sInt i=0;i<16;i++)
|
||||
{
|
||||
sInt a = block[i].a*7 - bias;
|
||||
sInt ind,t;
|
||||
|
||||
// select index (hooray for bit magic)
|
||||
t = (dist4 - a) >> 31; ind = t & 4; a -= dist4 & t;
|
||||
t = (dist2 - a) >> 31; ind += t & 2; a -= dist2 & t;
|
||||
t = (dist - a) >> 31; ind += t & 1;
|
||||
|
||||
ind = -ind & 7;
|
||||
ind ^= (2 > ind);
|
||||
|
||||
// write index
|
||||
mask |= ind << bits;
|
||||
if((bits += 3) >= 8)
|
||||
{
|
||||
*dest++ = mask;
|
||||
mask >>= 8;
|
||||
bits -= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
void sInitDXT()
|
||||
{
|
||||
for(sInt i=0;i<32;i++)
|
||||
Expand5[i] = (i<<3)|(i>>2);
|
||||
|
||||
for(sInt i=0;i<64;i++)
|
||||
Expand6[i] = (i<<2)|(i>>4);
|
||||
|
||||
for(sInt i=0;i<256+16;i++)
|
||||
{
|
||||
sInt v = sClamp(i-8,0,255);
|
||||
QuantRBTab[i] = Expand5[Mul8Bit(v,31)];
|
||||
QuantGTab[i] = Expand6[Mul8Bit(v,63)];
|
||||
}
|
||||
|
||||
PrepareOptTable4(&OMatch5[0][0],Expand5,32);
|
||||
PrepareOptTable4(&OMatch6[0][0],Expand6,64);
|
||||
|
||||
PrepareOptTable3(&OMatch5_3[0][0],Expand5,32);
|
||||
PrepareOptTable3(&OMatch6_3[0][0],Expand6,64);
|
||||
}
|
||||
|
||||
void sCompressDXTBlock(sU8 *dest,const sU32 *src,sBool alpha,sInt quality)
|
||||
{
|
||||
CRNLIB_ASSERT(Expand5[1]);
|
||||
|
||||
// if alpha specified, compress alpha as well
|
||||
if(alpha)
|
||||
{
|
||||
CompressAlphaBlock(dest,src,quality);
|
||||
dest += 8;
|
||||
}
|
||||
|
||||
// compress the color part
|
||||
CompressColorBlock(dest,src,quality);
|
||||
}
|
||||
|
||||
void sCompressDXT5ABlock(sU8 *dest,const sU32 *src,sInt quality)
|
||||
{
|
||||
CRNLIB_ASSERT(Expand5[1]);
|
||||
|
||||
CompressAlphaBlock(dest,src,quality);
|
||||
}
|
||||
|
||||
} // namespace ryg_dxt
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
// File: crn_semaphore.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
class semaphore
|
||||
{
|
||||
CRNLIB_NO_COPY_OR_ASSIGNMENT_OP(semaphore);
|
||||
|
||||
public:
|
||||
semaphore(int32 initialCount = 0, int32 maximumCount = 1, const char* pName = NULL);
|
||||
~semaphore();
|
||||
|
||||
inline void *get_handle(void) const { return m_handle; }
|
||||
|
||||
void release(int32 releaseCount = 1, int32 *pPreviousCount = NULL);
|
||||
|
||||
bool wait(uint32 milliseconds = UINT32_MAX);
|
||||
|
||||
private:
|
||||
void *m_handle;
|
||||
};
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,399 @@
|
||||
// File: crn_sparse_array.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
template<typename T, uint Log2N>
|
||||
class sparse_array_traits
|
||||
{
|
||||
public:
|
||||
static inline void* alloc_space(uint size)
|
||||
{
|
||||
return crnlib_malloc(size);
|
||||
}
|
||||
|
||||
static inline void free_space(void* p)
|
||||
{
|
||||
crnlib_free(p);
|
||||
}
|
||||
|
||||
static inline void construct_group(T* p)
|
||||
{
|
||||
scalar_type<T>::construct_array(p, 1U << Log2N);
|
||||
}
|
||||
|
||||
static inline void destruct_group(T* p)
|
||||
{
|
||||
scalar_type<T>::destruct_array(p, 1U << Log2N);
|
||||
}
|
||||
|
||||
static inline void construct_element(T* p)
|
||||
{
|
||||
scalar_type<T>::construct(p);
|
||||
}
|
||||
|
||||
static inline void destruct_element(T* p)
|
||||
{
|
||||
scalar_type<T>::destruct(p);
|
||||
}
|
||||
|
||||
static inline void copy_group(T* pDst, const T* pSrc)
|
||||
{
|
||||
for (uint j = 0; j < (1U << Log2N); j++)
|
||||
pDst[j] = pSrc[j];
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, uint Log2N = 5, template <typename, uint> class Traits = sparse_array_traits>
|
||||
class sparse_array : public Traits<T, Log2N>
|
||||
{
|
||||
public:
|
||||
enum { N = 1U << Log2N };
|
||||
|
||||
inline sparse_array() : m_size(0), m_num_active_groups(0)
|
||||
{
|
||||
init_default();
|
||||
}
|
||||
|
||||
inline sparse_array(uint size) : m_size(0), m_num_active_groups(0)
|
||||
{
|
||||
init_default();
|
||||
|
||||
resize(size);
|
||||
}
|
||||
|
||||
inline sparse_array(const sparse_array& other) : m_size(0), m_num_active_groups(0)
|
||||
{
|
||||
init_default();
|
||||
|
||||
*this = other;
|
||||
}
|
||||
|
||||
inline ~sparse_array()
|
||||
{
|
||||
for (uint i = 0; (i < m_groups.size()) && m_num_active_groups; i++)
|
||||
free_group(m_groups[i]);
|
||||
|
||||
deinit_default();
|
||||
}
|
||||
|
||||
bool assign(const sparse_array& other)
|
||||
{
|
||||
if (this == &other)
|
||||
return true;
|
||||
|
||||
if (!try_resize(other.size()))
|
||||
return false;
|
||||
|
||||
for (uint i = 0; i < other.m_groups.size(); i++)
|
||||
{
|
||||
const T* p = other.m_groups[i];
|
||||
|
||||
T* q = m_groups[i];
|
||||
|
||||
if (p)
|
||||
{
|
||||
if (!q)
|
||||
{
|
||||
q = alloc_group(true);
|
||||
if (!q)
|
||||
return false;
|
||||
|
||||
m_groups[i] = q;
|
||||
}
|
||||
|
||||
copy_group(q, p);
|
||||
}
|
||||
else if (q)
|
||||
{
|
||||
free_group(q);
|
||||
m_groups[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
sparse_array& operator= (const sparse_array& other)
|
||||
{
|
||||
if (!assign(other))
|
||||
{
|
||||
CRNLIB_FAIL("Out of memory");
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator== (const sparse_array& other) const
|
||||
{
|
||||
if (m_size != other.m_size)
|
||||
return false;
|
||||
|
||||
for (uint i = 0; i < m_size; i++)
|
||||
if (!((*this)[i] == other[i]))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator< (const sparse_array& rhs) const
|
||||
{
|
||||
const uint min_size = math::minimum(m_size, rhs.m_size);
|
||||
|
||||
uint i;
|
||||
for (i = 0; i < min_size; i++)
|
||||
if (!((*this)[i] == rhs[i]))
|
||||
break;
|
||||
|
||||
if (i < min_size)
|
||||
return (*this)[i] < rhs[i];
|
||||
|
||||
return m_size < rhs.m_size;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
if (m_groups.size())
|
||||
{
|
||||
for (uint i = 0; (i < m_groups.size()) && m_num_active_groups; i++)
|
||||
free_group(m_groups[i]);
|
||||
|
||||
m_groups.clear();
|
||||
}
|
||||
|
||||
m_size = 0;
|
||||
|
||||
CRNLIB_ASSERT(!m_num_active_groups);
|
||||
}
|
||||
|
||||
bool try_resize(uint size)
|
||||
{
|
||||
if (m_size == size)
|
||||
return true;
|
||||
|
||||
const uint new_num_groups = (size + N - 1) >> Log2N;
|
||||
if (new_num_groups != m_groups.size())
|
||||
{
|
||||
for (uint i = new_num_groups; i < m_groups.size(); i++)
|
||||
free_group(m_groups[i]);
|
||||
|
||||
if (!m_groups.try_resize(new_num_groups))
|
||||
return false;
|
||||
}
|
||||
|
||||
m_size = size;
|
||||
return true;
|
||||
}
|
||||
|
||||
void resize(uint size)
|
||||
{
|
||||
if (!try_resize(size))
|
||||
{
|
||||
CRNLIB_FAIL("Out of memory");
|
||||
}
|
||||
}
|
||||
|
||||
inline uint size() const { return m_size; }
|
||||
inline bool empty() const { return 0 == m_size; }
|
||||
|
||||
inline uint capacity() const { return m_groups.size(); }
|
||||
|
||||
inline const T& operator[] (uint i) const
|
||||
{
|
||||
CRNLIB_ASSERT(i < m_size);
|
||||
const T* p = m_groups[i >> Log2N];
|
||||
const void *t = m_default;
|
||||
return p ? p[i & (N - 1)] : *reinterpret_cast<const T*>(t);
|
||||
}
|
||||
|
||||
inline const T* get(uint i) const
|
||||
{
|
||||
CRNLIB_ASSERT(i < m_size);
|
||||
const T* p = m_groups[i >> Log2N];
|
||||
return p ? &p[i & (N - 1)] : NULL;
|
||||
}
|
||||
|
||||
inline T* get(uint i)
|
||||
{
|
||||
CRNLIB_ASSERT(i < m_size);
|
||||
T* p = m_groups[i >> Log2N];
|
||||
return p ? &p[i & (N - 1)] : NULL;
|
||||
}
|
||||
|
||||
inline bool is_present(uint i) const
|
||||
{
|
||||
CRNLIB_ASSERT(i < m_size);
|
||||
return m_groups[i >> Log2N] != NULL;
|
||||
}
|
||||
|
||||
inline uint get_num_groups() const { return m_groups.size(); }
|
||||
|
||||
inline const T* get_group(uint group_index) const
|
||||
{
|
||||
return m_groups[group_index];
|
||||
}
|
||||
|
||||
inline T* get_group(uint group_index)
|
||||
{
|
||||
return m_groups[group_index];
|
||||
}
|
||||
|
||||
inline uint get_group_size() const
|
||||
{
|
||||
return N;
|
||||
}
|
||||
|
||||
inline T* ensure_valid(uint index)
|
||||
{
|
||||
CRNLIB_ASSERT(index <= m_size);
|
||||
|
||||
const uint group_index = index >> Log2N;
|
||||
|
||||
if (group_index >= m_groups.size())
|
||||
{
|
||||
T* p = alloc_group(true);
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
if (!m_groups.try_push_back(p))
|
||||
{
|
||||
free_group(p);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
T* p = m_groups[group_index];
|
||||
if (!p)
|
||||
{
|
||||
p = alloc_group(true);
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
m_groups[group_index] = p;
|
||||
}
|
||||
|
||||
m_size = math::maximum(index + 1, m_size);
|
||||
|
||||
return p + (index & (N - 1));
|
||||
}
|
||||
|
||||
inline bool set(uint index, const T& obj)
|
||||
{
|
||||
T* p = ensure_valid(index);
|
||||
if (!p)
|
||||
return false;
|
||||
|
||||
*p = obj;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void push_back(const T& obj)
|
||||
{
|
||||
if (!set(m_size, obj))
|
||||
{
|
||||
CRNLIB_FAIL("Out of memory");
|
||||
}
|
||||
}
|
||||
|
||||
inline bool try_push_back(const T& obj)
|
||||
{
|
||||
return set(m_size, obj);
|
||||
}
|
||||
|
||||
inline void pop_back()
|
||||
{
|
||||
CRNLIB_ASSERT(m_size);
|
||||
if (m_size)
|
||||
resize(m_size - 1);
|
||||
}
|
||||
|
||||
inline void unset_range(uint start, uint num)
|
||||
{
|
||||
if (!num)
|
||||
return;
|
||||
|
||||
CRNLIB_ASSERT((start + num) <= capacity());
|
||||
|
||||
const uint num_to_skip = math::minimum(math::get_align_up_value_delta(start, N), num);
|
||||
num -= num_to_skip;
|
||||
|
||||
const uint first_group = (start + num_to_skip) >> Log2N;
|
||||
const uint num_groups = num >> Log2N;
|
||||
|
||||
for (uint i = 0; i < num_groups; i++)
|
||||
{
|
||||
T* p = m_groups[first_group + i];
|
||||
if (p)
|
||||
{
|
||||
free_group(p);
|
||||
m_groups[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void unset_all()
|
||||
{
|
||||
unset_range(0, m_groups.size() << Log2N);
|
||||
}
|
||||
|
||||
inline void swap(sparse_array& other)
|
||||
{
|
||||
utils::swap(m_size, other.m_size);
|
||||
m_groups.swap(other.m_groups);
|
||||
utils::swap(m_num_active_groups, other.m_num_active_groups);
|
||||
}
|
||||
|
||||
private:
|
||||
uint m_size;
|
||||
uint m_num_active_groups;
|
||||
|
||||
crnlib::vector<T*> m_groups;
|
||||
|
||||
uint64 m_default[(sizeof(T) + sizeof(uint64) - 1) / sizeof(uint64)];
|
||||
|
||||
inline T* alloc_group(bool nofail = false)
|
||||
{
|
||||
T* p = static_cast<T*>(alloc_space(N * sizeof(T)));
|
||||
|
||||
if (!p)
|
||||
{
|
||||
if (nofail)
|
||||
return NULL;
|
||||
|
||||
CRNLIB_FAIL("Out of memory");
|
||||
}
|
||||
|
||||
construct_group(p);
|
||||
|
||||
m_num_active_groups++;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
inline void free_group(T* p)
|
||||
{
|
||||
if (p)
|
||||
{
|
||||
CRNLIB_ASSERT(m_num_active_groups);
|
||||
m_num_active_groups--;
|
||||
|
||||
destruct_group(p);
|
||||
|
||||
free_space(p);
|
||||
}
|
||||
}
|
||||
|
||||
inline void init_default()
|
||||
{
|
||||
construct_element(reinterpret_cast<T*>(m_default));
|
||||
}
|
||||
|
||||
inline void deinit_default()
|
||||
{
|
||||
destruct_element(reinterpret_cast<T*>(m_default));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,538 @@
|
||||
// File: crn_sparse_bit_array.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#include "crn_core.h"
|
||||
#include "crn_sparse_bit_array.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
sparse_bit_array::sparse_bit_array() :
|
||||
m_num_groups(0), m_ppGroups(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
sparse_bit_array::sparse_bit_array(uint size) :
|
||||
m_num_groups(0), m_ppGroups(NULL)
|
||||
{
|
||||
resize(size);
|
||||
}
|
||||
|
||||
sparse_bit_array::sparse_bit_array(sparse_bit_array& other)
|
||||
{
|
||||
m_num_groups = other.m_num_groups;
|
||||
m_ppGroups = (uint32**)crnlib_malloc(m_num_groups * sizeof(uint32*));
|
||||
CRNLIB_VERIFY(m_ppGroups);
|
||||
|
||||
for (uint i = 0; i < m_num_groups; i++)
|
||||
{
|
||||
if (other.m_ppGroups[i])
|
||||
{
|
||||
m_ppGroups[i] = alloc_group(false);
|
||||
memcpy(m_ppGroups[i], other.m_ppGroups[i], cBytesPerGroup);
|
||||
}
|
||||
else
|
||||
m_ppGroups[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
sparse_bit_array::~sparse_bit_array()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
sparse_bit_array& sparse_bit_array::operator= (sparse_bit_array& other)
|
||||
{
|
||||
if (this == &other)
|
||||
return *this;
|
||||
|
||||
if (m_num_groups != other.m_num_groups)
|
||||
{
|
||||
clear();
|
||||
|
||||
m_num_groups = other.m_num_groups;
|
||||
m_ppGroups = (uint32**)crnlib_calloc(m_num_groups, sizeof(uint32*));
|
||||
CRNLIB_VERIFY(m_ppGroups);
|
||||
}
|
||||
|
||||
for (uint i = 0; i < m_num_groups; i++)
|
||||
{
|
||||
if (other.m_ppGroups[i])
|
||||
{
|
||||
if (!m_ppGroups[i])
|
||||
m_ppGroups[i] = alloc_group(false);
|
||||
memcpy(m_ppGroups[i], other.m_ppGroups[i], cBytesPerGroup);
|
||||
}
|
||||
else if (m_ppGroups[i])
|
||||
{
|
||||
free_group(m_ppGroups[i]);
|
||||
m_ppGroups[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void sparse_bit_array::clear()
|
||||
{
|
||||
if (!m_num_groups)
|
||||
return;
|
||||
|
||||
for (uint i = 0; i < m_num_groups; i++)
|
||||
free_group(m_ppGroups[i]);
|
||||
|
||||
crnlib_free(m_ppGroups);
|
||||
m_ppGroups = NULL;
|
||||
|
||||
m_num_groups = 0;
|
||||
}
|
||||
|
||||
void sparse_bit_array::swap(sparse_bit_array& other)
|
||||
{
|
||||
utils::swap(m_ppGroups, other.m_ppGroups);
|
||||
utils::swap(m_num_groups, other.m_num_groups);
|
||||
}
|
||||
|
||||
void sparse_bit_array::optimize()
|
||||
{
|
||||
for (uint i = 0; i < m_num_groups; i++)
|
||||
{
|
||||
uint32* s = m_ppGroups[i];
|
||||
if (s)
|
||||
{
|
||||
uint j;
|
||||
for (j = 0; j < cDWORDsPerGroup; j++)
|
||||
if (s[j])
|
||||
break;
|
||||
if (j == cDWORDsPerGroup)
|
||||
{
|
||||
free_group(s);
|
||||
m_ppGroups[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sparse_bit_array::set_bit_range(uint index, uint num)
|
||||
{
|
||||
CRNLIB_ASSERT((index + num) <= (m_num_groups << cBitsPerGroupShift));
|
||||
|
||||
if (!num)
|
||||
return;
|
||||
else if (num == 1)
|
||||
{
|
||||
set_bit(index);
|
||||
return;
|
||||
}
|
||||
|
||||
while ((index & cBitsPerGroupMask) || (num <= cBitsPerGroup))
|
||||
{
|
||||
uint group_index = index >> cBitsPerGroupShift;
|
||||
CRNLIB_ASSERT(group_index < m_num_groups);
|
||||
|
||||
uint32* pGroup = m_ppGroups[group_index];
|
||||
if (!pGroup)
|
||||
{
|
||||
pGroup = alloc_group(true);
|
||||
m_ppGroups[group_index] = pGroup;
|
||||
}
|
||||
|
||||
const uint group_bit_ofs = index & cBitsPerGroupMask;
|
||||
|
||||
const uint dword_bit_ofs = group_bit_ofs & 31;
|
||||
const uint max_bits_to_set = 32 - dword_bit_ofs;
|
||||
|
||||
const uint bits_to_set = math::minimum(max_bits_to_set, num);
|
||||
const uint32 msk = (0xFFFFFFFFU >> (32 - bits_to_set));
|
||||
|
||||
pGroup[group_bit_ofs >> 5] |= (msk << dword_bit_ofs);
|
||||
|
||||
num -= bits_to_set;
|
||||
if (!num)
|
||||
return;
|
||||
|
||||
index += bits_to_set;
|
||||
}
|
||||
|
||||
while (num >= cBitsPerGroup)
|
||||
{
|
||||
uint group_index = index >> cBitsPerGroupShift;
|
||||
CRNLIB_ASSERT(group_index < m_num_groups);
|
||||
|
||||
uint32* pGroup = m_ppGroups[group_index];
|
||||
if (!pGroup)
|
||||
{
|
||||
pGroup = alloc_group(true);
|
||||
m_ppGroups[group_index] = pGroup;
|
||||
}
|
||||
|
||||
memset(pGroup, 0xFF, sizeof(uint32) * cDWORDsPerGroup);
|
||||
|
||||
num -= cBitsPerGroup;
|
||||
index += cBitsPerGroup;
|
||||
}
|
||||
|
||||
while (num)
|
||||
{
|
||||
uint group_index = index >> cBitsPerGroupShift;
|
||||
CRNLIB_ASSERT(group_index < m_num_groups);
|
||||
|
||||
uint32* pGroup = m_ppGroups[group_index];
|
||||
if (!pGroup)
|
||||
{
|
||||
pGroup = alloc_group(true);
|
||||
m_ppGroups[group_index] = pGroup;
|
||||
}
|
||||
|
||||
uint group_bit_ofs = index & cBitsPerGroupMask;
|
||||
|
||||
uint bits_to_set = math::minimum(32U, num);
|
||||
uint32 msk = (0xFFFFFFFFU >> (32 - bits_to_set));
|
||||
|
||||
pGroup[group_bit_ofs >> 5] |= (msk << (group_bit_ofs & 31));
|
||||
|
||||
num -= bits_to_set;
|
||||
index += bits_to_set;
|
||||
}
|
||||
}
|
||||
|
||||
void sparse_bit_array::clear_all_bits()
|
||||
{
|
||||
for (uint i = 0; i < m_num_groups; i++)
|
||||
{
|
||||
uint32* pGroup = m_ppGroups[i];
|
||||
if (pGroup)
|
||||
memset(pGroup, 0, sizeof(uint32) * cDWORDsPerGroup);
|
||||
}
|
||||
}
|
||||
|
||||
void sparse_bit_array::clear_bit_range(uint index, uint num)
|
||||
{
|
||||
CRNLIB_ASSERT((index + num) <= (m_num_groups << cBitsPerGroupShift));
|
||||
|
||||
if (!num)
|
||||
return;
|
||||
else if (num == 1)
|
||||
{
|
||||
clear_bit(index);
|
||||
return;
|
||||
}
|
||||
|
||||
while ((index & cBitsPerGroupMask) || (num <= cBitsPerGroup))
|
||||
{
|
||||
uint group_index = index >> cBitsPerGroupShift;
|
||||
CRNLIB_ASSERT(group_index < m_num_groups);
|
||||
|
||||
const uint group_bit_ofs = index & cBitsPerGroupMask;
|
||||
|
||||
const uint dword_bit_ofs = group_bit_ofs & 31;
|
||||
const uint max_bits_to_set = 32 - dword_bit_ofs;
|
||||
|
||||
const uint bits_to_set = math::minimum(max_bits_to_set, num);
|
||||
|
||||
uint32* pGroup = m_ppGroups[group_index];
|
||||
if (pGroup)
|
||||
{
|
||||
const uint32 msk = (0xFFFFFFFFU >> (32 - bits_to_set));
|
||||
|
||||
pGroup[group_bit_ofs >> 5] &= (~(msk << dword_bit_ofs));
|
||||
}
|
||||
|
||||
num -= bits_to_set;
|
||||
if (!num)
|
||||
return;
|
||||
|
||||
index += bits_to_set;
|
||||
}
|
||||
|
||||
while (num >= cBitsPerGroup)
|
||||
{
|
||||
uint group_index = index >> cBitsPerGroupShift;
|
||||
CRNLIB_ASSERT(group_index < m_num_groups);
|
||||
|
||||
uint32* pGroup = m_ppGroups[group_index];
|
||||
if (pGroup)
|
||||
{
|
||||
free_group(pGroup);
|
||||
m_ppGroups[group_index] = NULL;
|
||||
}
|
||||
|
||||
num -= cBitsPerGroup;
|
||||
index += cBitsPerGroup;
|
||||
}
|
||||
|
||||
while (num)
|
||||
{
|
||||
uint group_index = index >> cBitsPerGroupShift;
|
||||
CRNLIB_ASSERT(group_index < m_num_groups);
|
||||
|
||||
uint bits_to_set = math::minimum(32u, num);
|
||||
|
||||
uint32* pGroup = m_ppGroups[group_index];
|
||||
if (pGroup)
|
||||
{
|
||||
uint group_bit_ofs = index & cBitsPerGroupMask;
|
||||
|
||||
uint32 msk = (0xFFFFFFFFU >> (32 - bits_to_set));
|
||||
|
||||
pGroup[group_bit_ofs >> 5] &= (~(msk << (group_bit_ofs & 31)));
|
||||
}
|
||||
|
||||
num -= bits_to_set;
|
||||
index += bits_to_set;
|
||||
}
|
||||
}
|
||||
|
||||
void sparse_bit_array::resize(uint size)
|
||||
{
|
||||
uint num_groups = (size + cBitsPerGroup - 1) >> cBitsPerGroupShift;
|
||||
if (num_groups == m_num_groups)
|
||||
return;
|
||||
|
||||
if (!num_groups)
|
||||
{
|
||||
clear();
|
||||
return;
|
||||
}
|
||||
|
||||
sparse_bit_array temp;
|
||||
temp.swap(*this);
|
||||
|
||||
m_num_groups = num_groups;
|
||||
m_ppGroups = (uint32**)crnlib_calloc(m_num_groups, sizeof(uint32*));
|
||||
CRNLIB_VERIFY(m_ppGroups);
|
||||
|
||||
uint n = math::minimum(temp.m_num_groups, m_num_groups);
|
||||
for (uint i = 0; i < n; i++)
|
||||
{
|
||||
uint32* p = temp.m_ppGroups[i];
|
||||
if (p)
|
||||
{
|
||||
m_ppGroups[i] = temp.m_ppGroups[i];
|
||||
temp.m_ppGroups[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sparse_bit_array& sparse_bit_array::operator&= (const sparse_bit_array& other)
|
||||
{
|
||||
if (this == &other)
|
||||
return *this;
|
||||
|
||||
CRNLIB_VERIFY(other.m_num_groups == m_num_groups);
|
||||
|
||||
for (uint i = 0; i < m_num_groups; i++)
|
||||
{
|
||||
uint32* d = m_ppGroups[i];
|
||||
if (!d)
|
||||
continue;
|
||||
uint32* s = other.m_ppGroups[i];
|
||||
|
||||
if (!s)
|
||||
{
|
||||
free_group(d);
|
||||
m_ppGroups[i] = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32 oc = 0;
|
||||
for (uint j = 0; j < cDWORDsPerGroup; j++)
|
||||
{
|
||||
uint32 c = d[j] & s[j];
|
||||
d[j] = c;
|
||||
oc |= c;
|
||||
}
|
||||
if (!oc)
|
||||
{
|
||||
free_group(d);
|
||||
m_ppGroups[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
sparse_bit_array& sparse_bit_array::operator|= (const sparse_bit_array& other)
|
||||
{
|
||||
if (this == &other)
|
||||
return *this;
|
||||
|
||||
CRNLIB_VERIFY(other.m_num_groups == m_num_groups);
|
||||
|
||||
for (uint i = 0; i < m_num_groups; i++)
|
||||
{
|
||||
uint32* s = other.m_ppGroups[i];
|
||||
if (!s)
|
||||
continue;
|
||||
|
||||
uint32* d = m_ppGroups[i];
|
||||
if (!d)
|
||||
{
|
||||
d = alloc_group(true);
|
||||
m_ppGroups[i] = d;
|
||||
memcpy(d, s, cBytesPerGroup);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32 oc = 0;
|
||||
for (uint j = 0; j < cDWORDsPerGroup; j++)
|
||||
{
|
||||
uint32 c = d[j] | s[j];
|
||||
d[j] = c;
|
||||
oc |= c;
|
||||
}
|
||||
if (!oc)
|
||||
{
|
||||
free_group(d);
|
||||
m_ppGroups[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
sparse_bit_array& sparse_bit_array::and_not(const sparse_bit_array& other)
|
||||
{
|
||||
if (this == &other)
|
||||
return *this;
|
||||
|
||||
CRNLIB_VERIFY(other.m_num_groups == m_num_groups);
|
||||
|
||||
for (uint i = 0; i < m_num_groups; i++)
|
||||
{
|
||||
uint32* d = m_ppGroups[i];
|
||||
if (!d)
|
||||
continue;
|
||||
uint32* s = other.m_ppGroups[i];
|
||||
if (!s)
|
||||
continue;
|
||||
|
||||
uint32 oc = 0;
|
||||
for (uint j = 0; j < cDWORDsPerGroup; j++)
|
||||
{
|
||||
uint32 c = d[j] & (~s[j]);
|
||||
d[j] = c;
|
||||
oc |= c;
|
||||
}
|
||||
if (!oc)
|
||||
{
|
||||
free_group(d);
|
||||
m_ppGroups[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
int sparse_bit_array::find_first_set_bit(uint index, uint num) const
|
||||
{
|
||||
CRNLIB_ASSERT((index + num) <= (m_num_groups << cBitsPerGroupShift));
|
||||
|
||||
if (!num)
|
||||
return -1;
|
||||
|
||||
while ((index & cBitsPerGroupMask) || (num <= cBitsPerGroup))
|
||||
{
|
||||
uint group_index = index >> cBitsPerGroupShift;
|
||||
CRNLIB_ASSERT(group_index < m_num_groups);
|
||||
|
||||
const uint group_bit_ofs = index & cBitsPerGroupMask;
|
||||
const uint dword_bit_ofs = group_bit_ofs & 31;
|
||||
|
||||
const uint max_bits_to_examine = 32 - dword_bit_ofs;
|
||||
const uint bits_to_examine = math::minimum(max_bits_to_examine, num);
|
||||
|
||||
uint32* pGroup = m_ppGroups[group_index];
|
||||
if (pGroup)
|
||||
{
|
||||
const uint32 msk = (0xFFFFFFFFU >> (32 - bits_to_examine));
|
||||
|
||||
uint bits = pGroup[group_bit_ofs >> 5] & (msk << dword_bit_ofs);
|
||||
if (bits)
|
||||
{
|
||||
uint num_trailing_zeros = math::count_trailing_zero_bits(bits);
|
||||
int set_index = num_trailing_zeros + (index & ~31);
|
||||
CRNLIB_ASSERT(get_bit(set_index));
|
||||
return set_index;
|
||||
}
|
||||
}
|
||||
|
||||
num -= bits_to_examine;
|
||||
if (!num)
|
||||
return -1;
|
||||
|
||||
index += bits_to_examine;
|
||||
}
|
||||
|
||||
while (num >= cBitsPerGroup)
|
||||
{
|
||||
uint group_index = index >> cBitsPerGroupShift;
|
||||
CRNLIB_ASSERT(group_index < m_num_groups);
|
||||
|
||||
uint32* pGroup = m_ppGroups[group_index];
|
||||
if (pGroup)
|
||||
{
|
||||
for (uint i = 0; i < cDWORDsPerGroup; i++)
|
||||
{
|
||||
uint32 bits = pGroup[i];
|
||||
if (bits)
|
||||
{
|
||||
uint num_trailing_zeros = math::count_trailing_zero_bits(bits);
|
||||
|
||||
int set_index = num_trailing_zeros + index + (i << 5);
|
||||
CRNLIB_ASSERT(get_bit(set_index));
|
||||
return set_index;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
num -= cBitsPerGroup;
|
||||
index += cBitsPerGroup;
|
||||
}
|
||||
|
||||
while (num)
|
||||
{
|
||||
uint group_index = index >> cBitsPerGroupShift;
|
||||
CRNLIB_ASSERT(group_index < m_num_groups);
|
||||
|
||||
uint bits_to_examine = math::minimum(32U, num);
|
||||
|
||||
uint32* pGroup = m_ppGroups[group_index];
|
||||
if (pGroup)
|
||||
{
|
||||
uint group_bit_ofs = index & cBitsPerGroupMask;
|
||||
|
||||
uint32 msk = (0xFFFFFFFFU >> (32 - bits_to_examine));
|
||||
|
||||
uint32 bits = pGroup[group_bit_ofs >> 5] & (msk << (group_bit_ofs & 31));
|
||||
if (bits)
|
||||
{
|
||||
uint num_trailing_zeros = math::count_trailing_zero_bits(bits);
|
||||
|
||||
int set_index = num_trailing_zeros + (index & ~31);
|
||||
CRNLIB_ASSERT(get_bit(set_index));
|
||||
return set_index;
|
||||
}
|
||||
}
|
||||
|
||||
num -= bits_to_examine;
|
||||
index += bits_to_examine;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
// File: crn_sparse_bit_array.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
class sparse_bit_array
|
||||
{
|
||||
public:
|
||||
sparse_bit_array();
|
||||
sparse_bit_array(uint size);
|
||||
sparse_bit_array(sparse_bit_array& other);
|
||||
~sparse_bit_array();
|
||||
|
||||
sparse_bit_array& operator= (sparse_bit_array& other);
|
||||
|
||||
void clear();
|
||||
|
||||
inline uint get_size() { return (m_num_groups << cBitsPerGroupShift); }
|
||||
|
||||
void resize(uint size);
|
||||
|
||||
sparse_bit_array& operator&= (const sparse_bit_array& other);
|
||||
sparse_bit_array& operator|= (const sparse_bit_array& other);
|
||||
sparse_bit_array& and_not(const sparse_bit_array& other);
|
||||
|
||||
void swap(sparse_bit_array& other);
|
||||
|
||||
void optimize();
|
||||
|
||||
void set_bit_range(uint index, uint num);
|
||||
void clear_bit_range(uint index, uint num);
|
||||
|
||||
void clear_all_bits();
|
||||
|
||||
inline void set_bit(uint index)
|
||||
{
|
||||
uint group_index = index >> cBitsPerGroupShift;
|
||||
CRNLIB_ASSERT(group_index < m_num_groups);
|
||||
|
||||
uint32* pGroup = m_ppGroups[group_index];
|
||||
if (!pGroup)
|
||||
{
|
||||
pGroup = alloc_group(true);
|
||||
m_ppGroups[group_index] = pGroup;
|
||||
}
|
||||
|
||||
uint bit_ofs = index & (cBitsPerGroup - 1);
|
||||
|
||||
pGroup[bit_ofs >> 5] |= (1U << (bit_ofs & 31));
|
||||
}
|
||||
|
||||
inline void clear_bit(uint index)
|
||||
{
|
||||
uint group_index = index >> cBitsPerGroupShift;
|
||||
CRNLIB_ASSERT(group_index < m_num_groups);
|
||||
|
||||
uint32* pGroup = m_ppGroups[group_index];
|
||||
if (!pGroup)
|
||||
{
|
||||
pGroup = alloc_group(true);
|
||||
m_ppGroups[group_index] = pGroup;
|
||||
}
|
||||
|
||||
uint bit_ofs = index & (cBitsPerGroup - 1);
|
||||
|
||||
pGroup[bit_ofs >> 5] &= (~(1U << (bit_ofs & 31)));
|
||||
}
|
||||
|
||||
inline void set(uint index, bool value)
|
||||
{
|
||||
uint group_index = index >> cBitsPerGroupShift;
|
||||
CRNLIB_ASSERT(group_index < m_num_groups);
|
||||
|
||||
uint32* pGroup = m_ppGroups[group_index];
|
||||
if (!pGroup)
|
||||
{
|
||||
pGroup = alloc_group(true);
|
||||
m_ppGroups[group_index] = pGroup;
|
||||
}
|
||||
|
||||
uint bit_ofs = index & (cBitsPerGroup - 1);
|
||||
|
||||
uint bit = (1U << (bit_ofs & 31));
|
||||
|
||||
uint c = pGroup[bit_ofs >> 5];
|
||||
uint mask = (uint)(-(int)value);
|
||||
|
||||
pGroup[bit_ofs >> 5] = (c & ~bit) | (mask & bit);
|
||||
}
|
||||
|
||||
inline bool get_bit(uint index) const
|
||||
{
|
||||
uint group_index = index >> cBitsPerGroupShift;
|
||||
CRNLIB_ASSERT(group_index < m_num_groups);
|
||||
|
||||
uint32* pGroup = m_ppGroups[group_index];
|
||||
if (!pGroup)
|
||||
return 0;
|
||||
|
||||
uint bit_ofs = index & (cBitsPerGroup - 1);
|
||||
|
||||
uint bit = (1U << (bit_ofs & 31));
|
||||
|
||||
return (pGroup[bit_ofs >> 5] & bit) != 0;
|
||||
}
|
||||
|
||||
inline uint32 get_uint32(uint index) const
|
||||
{
|
||||
uint group_index = index >> cBitsPerGroupShift;
|
||||
CRNLIB_ASSERT(group_index < m_num_groups);
|
||||
|
||||
uint32* pGroup = m_ppGroups[group_index];
|
||||
if (!pGroup)
|
||||
return 0;
|
||||
|
||||
uint bit_ofs = index & (cBitsPerGroup - 1);
|
||||
|
||||
return pGroup[bit_ofs >> 5];
|
||||
}
|
||||
|
||||
inline void set_uint32(uint index, uint32 value) const
|
||||
{
|
||||
uint group_index = index >> cBitsPerGroupShift;
|
||||
CRNLIB_ASSERT(group_index < m_num_groups);
|
||||
|
||||
uint32* pGroup = m_ppGroups[group_index];
|
||||
if (!pGroup)
|
||||
{
|
||||
pGroup = alloc_group(true);
|
||||
m_ppGroups[group_index] = pGroup;
|
||||
}
|
||||
|
||||
uint bit_ofs = index & (cBitsPerGroup - 1);
|
||||
|
||||
pGroup[bit_ofs >> 5] = value;
|
||||
}
|
||||
|
||||
int find_first_set_bit(uint index, uint num) const;
|
||||
|
||||
enum
|
||||
{
|
||||
cDWORDsPerGroupShift = 4U,
|
||||
cDWORDsPerGroup = 1U << cDWORDsPerGroupShift,
|
||||
|
||||
cBitsPerGroupShift = cDWORDsPerGroupShift + 5,
|
||||
cBitsPerGroup = 1U << cBitsPerGroupShift,
|
||||
cBitsPerGroupMask = cBitsPerGroup - 1U,
|
||||
|
||||
cBytesPerGroup = cDWORDsPerGroup * sizeof(uint32)
|
||||
};
|
||||
|
||||
uint get_num_groups() const { return m_num_groups; }
|
||||
uint32** get_groups() { return m_ppGroups; }
|
||||
|
||||
private:
|
||||
uint m_num_groups;
|
||||
uint32** m_ppGroups;
|
||||
|
||||
static inline uint32* alloc_group(bool clear)
|
||||
{
|
||||
uint32* p = (uint32*)crnlib_malloc(cBytesPerGroup);
|
||||
CRNLIB_VERIFY(p);
|
||||
if (clear) memset(p, 0, cBytesPerGroup);
|
||||
return p;
|
||||
}
|
||||
|
||||
static inline void free_group(void* p)
|
||||
{
|
||||
if (p)
|
||||
crnlib_free(p);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,38 @@
|
||||
// File: crn_spinlock.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
// Simple non-recursive spinlock.
|
||||
class spinlock
|
||||
{
|
||||
public:
|
||||
inline spinlock() : m_flag(0) { }
|
||||
|
||||
void lock(uint32 max_spins = 4096, bool yielding = true, bool memoryBarrier = true);
|
||||
|
||||
inline void lock_no_barrier(uint32 max_spins = 4096, bool yielding = true) { lock(max_spins, yielding, false); }
|
||||
|
||||
void unlock();
|
||||
|
||||
inline void unlock_no_barrier() { m_flag = CRNLIB_FALSE; }
|
||||
|
||||
private:
|
||||
volatile int32 m_flag;
|
||||
};
|
||||
|
||||
class scoped_spinlock
|
||||
{
|
||||
scoped_spinlock(const scoped_spinlock&);
|
||||
scoped_spinlock& operator= (const scoped_spinlock&);
|
||||
|
||||
public:
|
||||
inline scoped_spinlock(spinlock& lock) : m_lock(lock) { m_lock.lock(); }
|
||||
inline ~scoped_spinlock() { m_lock.unlock(); }
|
||||
|
||||
private:
|
||||
spinlock& m_lock;
|
||||
};
|
||||
|
||||
} // namespace crnlib
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user