This commit is contained in:
@@ -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
@@ -0,0 +1,59 @@
|
||||
// File: crn_strutils.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
char* strcpy_safe(char* pDst, uint dst_len, const char* pSrc);
|
||||
|
||||
bool int_to_string(int value, char* pDst, uint len);
|
||||
bool uint_to_string(uint value, char* pDst, uint len);
|
||||
|
||||
bool string_to_int(const char*& pBuf, int& value);
|
||||
bool string_to_int(const wchar_t*& pBuf, int& value);
|
||||
|
||||
bool string_to_uint(const char*& pBuf, uint& value);
|
||||
bool string_to_uint(const wchar_t*& pBuf, uint& value);
|
||||
|
||||
bool string_to_int64(const char*& pBuf, int64& value);
|
||||
bool string_to_uint64(const char*& pBuf, uint64& value);
|
||||
|
||||
bool string_to_bool(const char* p, bool& value);
|
||||
bool string_to_bool(const wchar_t* p, bool& value);
|
||||
|
||||
bool string_to_float(const char*& p, float& value, uint round_digit = 10U);
|
||||
bool string_to_float(const wchar_t*& p, float& value, uint round_digit = 10U);
|
||||
|
||||
bool split_path(const char* p, dynamic_string* pDrive, dynamic_string* pDir, dynamic_string* pFilename, dynamic_string* pExt);
|
||||
bool split_path(const wchar_t* p, dynamic_wstring* pDrive, dynamic_wstring* pDir, dynamic_wstring* pFilename, dynamic_wstring* pExt);
|
||||
|
||||
bool split_path(const char* p, dynamic_string& path, dynamic_string& filename);
|
||||
bool split_path(const wchar_t* p, dynamic_wstring& path, dynamic_wstring& filename);
|
||||
|
||||
bool get_pathname(const char* p, dynamic_string& path);
|
||||
bool get_pathname(const wchar_t* p, dynamic_wstring& path);
|
||||
|
||||
bool get_filename(const char* p, dynamic_string& filename);
|
||||
bool get_filename(const wchar_t* p, dynamic_wstring& filename);
|
||||
|
||||
void combine_path(dynamic_string& dst, const char* pA, const char* pB);
|
||||
void combine_path(dynamic_wstring& dst, const wchar_t* pA, const wchar_t* pB);
|
||||
|
||||
void combine_path(dynamic_string& dst, const char* pA, const char* pB, const char* pC);
|
||||
void combine_path(dynamic_wstring& dst, const wchar_t* pA, const wchar_t* pB, const wchar_t* pC);
|
||||
void combine_path(dynamic_wstring& dst, const wchar_t* pA, const wchar_t* pB, const wchar_t* pC, const wchar_t *pD);
|
||||
|
||||
bool full_path(dynamic_string& path);
|
||||
bool full_path(dynamic_wstring& path);
|
||||
|
||||
bool get_extension(dynamic_string& filename);
|
||||
bool get_extension(dynamic_wstring& filename);
|
||||
|
||||
bool remove_extension(dynamic_string& filename);
|
||||
bool remove_extension(dynamic_wstring& filename);
|
||||
|
||||
bool create_path(const dynamic_wstring& path);
|
||||
|
||||
void trim_trailing_seperator(dynamic_wstring& path);
|
||||
|
||||
} // namespace crnlib
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,519 @@
|
||||
// File: crn_symbol_codec.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
#include "crn_prefix_coding.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
class symbol_codec;
|
||||
class adaptive_arith_data_model;
|
||||
|
||||
const uint cSymbolCodecArithMinLen = 0x01000000U;
|
||||
const uint cSymbolCodecArithMaxLen = 0xFFFFFFFFU;
|
||||
|
||||
const uint cSymbolCodecArithProbBits = 11;
|
||||
const uint cSymbolCodecArithProbScale = 1 << cSymbolCodecArithProbBits;
|
||||
const uint cSymbolCodecArithProbMoveBits = 5;
|
||||
|
||||
const uint cSymbolCodecArithProbMulBits = 8;
|
||||
const uint cSymbolCodecArithProbMulScale = 1 << cSymbolCodecArithProbMulBits;
|
||||
|
||||
class symbol_histogram
|
||||
{
|
||||
public:
|
||||
inline symbol_histogram(uint size = 0) : m_hist(size) { }
|
||||
|
||||
inline void clear() { m_hist.clear(); }
|
||||
|
||||
inline uint size() const { return static_cast<uint>(m_hist.size()); }
|
||||
|
||||
inline void inc_freq(uint x, uint amount = 1)
|
||||
{
|
||||
uint h = m_hist[x];
|
||||
CRNLIB_ASSERT( amount <= (0xFFFFFFFF - h) );
|
||||
m_hist[x] = h + amount;
|
||||
}
|
||||
|
||||
inline void set_all(uint val) { for (uint i = 0; i < m_hist.size(); i++) m_hist[i] = val; }
|
||||
|
||||
inline void resize(uint new_size) { m_hist.resize(new_size); }
|
||||
|
||||
inline const uint* get_ptr() const { return m_hist.empty() ? NULL : &m_hist.front(); }
|
||||
|
||||
double calc_entropy() const;
|
||||
|
||||
uint operator[] (uint i) const { return m_hist[i]; }
|
||||
uint& operator[] (uint i) { return m_hist[i]; }
|
||||
|
||||
uint64 get_total() const;
|
||||
|
||||
private:
|
||||
crnlib::vector<uint> m_hist;
|
||||
};
|
||||
|
||||
class adaptive_huffman_data_model
|
||||
{
|
||||
public:
|
||||
adaptive_huffman_data_model(bool encoding = true, uint total_syms = 0);
|
||||
adaptive_huffman_data_model(const adaptive_huffman_data_model& other);
|
||||
~adaptive_huffman_data_model();
|
||||
|
||||
adaptive_huffman_data_model& operator= (const adaptive_huffman_data_model& rhs);
|
||||
|
||||
void clear();
|
||||
|
||||
void init(bool encoding, uint total_syms);
|
||||
void reset();
|
||||
|
||||
void rescale();
|
||||
|
||||
uint get_total_syms() const { return m_total_syms; }
|
||||
uint get_cost(uint sym) const { return m_code_sizes[sym]; }
|
||||
|
||||
public:
|
||||
uint m_total_syms;
|
||||
|
||||
uint m_update_cycle;
|
||||
uint m_symbols_until_update;
|
||||
|
||||
uint m_total_count;
|
||||
|
||||
crnlib::vector<uint16> m_sym_freq;
|
||||
|
||||
crnlib::vector<uint16> m_codes;
|
||||
crnlib::vector<uint8> m_code_sizes;
|
||||
|
||||
prefix_coding::decoder_tables* m_pDecode_tables;
|
||||
|
||||
uint8 m_decoder_table_bits;
|
||||
bool m_encoding;
|
||||
|
||||
void update();
|
||||
|
||||
friend class symbol_codec;
|
||||
};
|
||||
|
||||
class static_huffman_data_model
|
||||
{
|
||||
public:
|
||||
static_huffman_data_model();
|
||||
static_huffman_data_model(const static_huffman_data_model& other);
|
||||
~static_huffman_data_model();
|
||||
|
||||
static_huffman_data_model& operator= (const static_huffman_data_model& rhs);
|
||||
|
||||
void clear();
|
||||
|
||||
bool init(bool encoding, uint total_syms, const uint16* pSym_freq, uint code_size_limit);
|
||||
bool init(bool encoding, uint total_syms, const uint* pSym_freq, uint code_size_limit);
|
||||
bool init(bool encoding, uint total_syms, const uint8* pCode_sizes, uint code_size_limit);
|
||||
bool init(bool encoding, const symbol_histogram& hist, uint code_size_limit);
|
||||
|
||||
uint get_total_syms() const { return m_total_syms; }
|
||||
uint get_cost(uint sym) const { return m_code_sizes[sym]; }
|
||||
|
||||
const uint8* get_code_sizes() const { return m_code_sizes.empty() ? NULL : &m_code_sizes[0]; }
|
||||
|
||||
private:
|
||||
uint m_total_syms;
|
||||
|
||||
crnlib::vector<uint16> m_codes;
|
||||
crnlib::vector<uint8> m_code_sizes;
|
||||
|
||||
prefix_coding::decoder_tables* m_pDecode_tables;
|
||||
|
||||
bool m_encoding;
|
||||
|
||||
bool prepare_decoder_tables();
|
||||
uint compute_decoder_table_bits() const;
|
||||
|
||||
friend class symbol_codec;
|
||||
};
|
||||
|
||||
class adaptive_bit_model
|
||||
{
|
||||
public:
|
||||
adaptive_bit_model();
|
||||
adaptive_bit_model(float prob0);
|
||||
adaptive_bit_model(const adaptive_bit_model& other);
|
||||
|
||||
adaptive_bit_model& operator= (const adaptive_bit_model& rhs);
|
||||
|
||||
void clear();
|
||||
void set_probability_0(float prob0);
|
||||
void update(uint bit);
|
||||
|
||||
float get_cost(uint bit) const;
|
||||
|
||||
public:
|
||||
uint16 m_bit_0_prob;
|
||||
|
||||
friend class symbol_codec;
|
||||
friend class adaptive_arith_data_model;
|
||||
};
|
||||
|
||||
class adaptive_arith_data_model
|
||||
{
|
||||
public:
|
||||
adaptive_arith_data_model(bool encoding = true, uint total_syms = 0);
|
||||
adaptive_arith_data_model(const adaptive_arith_data_model& other);
|
||||
~adaptive_arith_data_model();
|
||||
|
||||
adaptive_arith_data_model& operator= (const adaptive_arith_data_model& rhs);
|
||||
|
||||
void clear();
|
||||
|
||||
void init(bool encoding, uint total_syms);
|
||||
void reset();
|
||||
|
||||
uint get_total_syms() const { return m_total_syms; }
|
||||
float get_cost(uint sym) const;
|
||||
|
||||
private:
|
||||
uint m_total_syms;
|
||||
typedef crnlib::vector<adaptive_bit_model> adaptive_bit_model_vector;
|
||||
adaptive_bit_model_vector m_probs;
|
||||
|
||||
friend class symbol_codec;
|
||||
};
|
||||
|
||||
#if (defined(_XBOX) || defined(_WIN64))
|
||||
#define CRNLIB_SYMBOL_CODEC_USE_64_BIT_BUFFER 1
|
||||
#else
|
||||
#define CRNLIB_SYMBOL_CODEC_USE_64_BIT_BUFFER 0
|
||||
#endif
|
||||
|
||||
class symbol_codec
|
||||
{
|
||||
public:
|
||||
symbol_codec();
|
||||
|
||||
void clear();
|
||||
|
||||
// Encoding
|
||||
void start_encoding(uint expected_file_size);
|
||||
uint encode_transmit_static_huffman_data_model(static_huffman_data_model& model, bool simulate, static_huffman_data_model* pDelta_model = NULL );
|
||||
void encode_bits(uint bits, uint num_bits);
|
||||
void encode_align_to_byte();
|
||||
void encode(uint sym, adaptive_huffman_data_model& model);
|
||||
void encode(uint sym, static_huffman_data_model& model);
|
||||
void encode_truncated_binary(uint v, uint n);
|
||||
static uint encode_truncated_binary_cost(uint v, uint n);
|
||||
void encode_golomb(uint v, uint m);
|
||||
void encode_rice(uint v, uint m);
|
||||
static uint encode_rice_get_cost(uint v, uint m);
|
||||
void encode(uint bit, adaptive_bit_model& model, bool update_model = true);
|
||||
void encode(uint sym, adaptive_arith_data_model& model);
|
||||
|
||||
inline void encode_enable_simulation(bool enabled) { m_simulate_encoding = enabled; }
|
||||
inline bool encode_get_simulation() { return m_simulate_encoding; }
|
||||
inline uint encode_get_total_bits_written() const { return m_total_bits_written; }
|
||||
|
||||
void stop_encoding(bool support_arith);
|
||||
|
||||
const crnlib::vector<uint8>& get_encoding_buf() const { return m_output_buf; }
|
||||
crnlib::vector<uint8>& get_encoding_buf() { return m_output_buf; }
|
||||
|
||||
// Decoding
|
||||
|
||||
typedef void (*need_bytes_func_ptr)(size_t num_bytes_consumed, void *pPrivate_data, const uint8* &pBuf, size_t &buf_size, bool &eof_flag);
|
||||
|
||||
bool start_decoding(const uint8* pBuf, size_t buf_size, bool eof_flag = true, need_bytes_func_ptr pNeed_bytes_func = NULL, void *pPrivate_data = NULL);
|
||||
void decode_set_input_buffer(const uint8* pBuf, size_t buf_size, const uint8* pBuf_next, bool eof_flag = true);
|
||||
inline uint64 decode_get_bytes_consumed() const { return m_pDecode_buf_next - m_pDecode_buf; }
|
||||
inline uint64 decode_get_bits_remaining() const { return ((m_pDecode_buf_end - m_pDecode_buf_next) << 3) + m_bit_count; }
|
||||
void start_arith_decoding();
|
||||
bool decode_receive_static_huffman_data_model(static_huffman_data_model& model, static_huffman_data_model* pDeltaModel);
|
||||
uint decode_bits(uint num_bits);
|
||||
uint decode_peek_bits(uint num_bits);
|
||||
void decode_remove_bits(uint num_bits);
|
||||
void decode_align_to_byte();
|
||||
int decode_remove_byte_from_bit_buf();
|
||||
uint decode(adaptive_huffman_data_model& model);
|
||||
uint decode(static_huffman_data_model& model);
|
||||
uint decode_truncated_binary(uint n);
|
||||
uint decode_golomb(uint m);
|
||||
uint decode_rice(uint m);
|
||||
uint decode(adaptive_bit_model& model, bool update_model = true);
|
||||
uint decode(adaptive_arith_data_model& model);
|
||||
uint64 stop_decoding();
|
||||
|
||||
uint get_total_model_updates() const { return m_total_model_updates; }
|
||||
|
||||
public:
|
||||
const uint8* m_pDecode_buf;
|
||||
const uint8* m_pDecode_buf_next;
|
||||
const uint8* m_pDecode_buf_end;
|
||||
size_t m_decode_buf_size;
|
||||
bool m_decode_buf_eof;
|
||||
|
||||
need_bytes_func_ptr m_pDecode_need_bytes_func;
|
||||
void* m_pDecode_private_data;
|
||||
|
||||
#if CRNLIB_SYMBOL_CODEC_USE_64_BIT_BUFFER
|
||||
typedef uint64 bit_buf_t;
|
||||
enum { cBitBufSize = 64 };
|
||||
#else
|
||||
typedef uint32 bit_buf_t;
|
||||
enum { cBitBufSize = 32 };
|
||||
#endif
|
||||
|
||||
bit_buf_t m_bit_buf;
|
||||
int m_bit_count;
|
||||
|
||||
uint m_total_model_updates;
|
||||
|
||||
crnlib::vector<uint8> m_output_buf;
|
||||
crnlib::vector<uint8> m_arith_output_buf;
|
||||
|
||||
struct output_symbol
|
||||
{
|
||||
uint m_bits;
|
||||
|
||||
enum { cArithSym = -1, cAlignToByteSym = -2 };
|
||||
int16 m_num_bits;
|
||||
|
||||
uint16 m_arith_prob0;
|
||||
};
|
||||
crnlib::vector<output_symbol> m_output_syms;
|
||||
|
||||
uint m_total_bits_written;
|
||||
bool m_simulate_encoding;
|
||||
|
||||
uint m_arith_base;
|
||||
uint m_arith_value;
|
||||
uint m_arith_length;
|
||||
uint m_arith_total_bits;
|
||||
|
||||
bool m_support_arith;
|
||||
|
||||
void put_bits_init(uint expected_size);
|
||||
void record_put_bits(uint bits, uint num_bits);
|
||||
|
||||
void arith_propagate_carry();
|
||||
void arith_renorm_enc_interval();
|
||||
void arith_start_encoding();
|
||||
void arith_stop_encoding();
|
||||
|
||||
void put_bits(uint bits, uint num_bits);
|
||||
void put_bits_align_to_byte();
|
||||
void flush_bits();
|
||||
void assemble_output_buf(bool support_arith);
|
||||
|
||||
void get_bits_init();
|
||||
uint get_bits(uint num_bits);
|
||||
void remove_bits(uint num_bits);
|
||||
|
||||
void decode_need_bytes();
|
||||
|
||||
enum
|
||||
{
|
||||
cNull,
|
||||
cEncoding,
|
||||
cDecoding
|
||||
} m_mode;
|
||||
};
|
||||
|
||||
#define CRNLIB_SYMBOL_CODEC_USE_MACROS 1
|
||||
|
||||
#ifdef _XBOX
|
||||
#define CRNLIB_READ_BIG_ENDIAN_UINT32(p) *reinterpret_cast<const uint32*>(p)
|
||||
#elif defined(_MSC_VER)
|
||||
#define CRNLIB_READ_BIG_ENDIAN_UINT32(p) _byteswap_ulong(*reinterpret_cast<const uint32*>(p))
|
||||
#else
|
||||
#define CRNLIB_READ_BIG_ENDIAN_UINT32(p) utils::swap32(*reinterpret_cast<const uint32*>(p))
|
||||
#endif
|
||||
|
||||
#if CRNLIB_SYMBOL_CODEC_USE_MACROS
|
||||
#define CRNLIB_SYMBOL_CODEC_DECODE_DECLARE(codec) \
|
||||
uint arith_value; \
|
||||
uint arith_length; \
|
||||
symbol_codec::bit_buf_t bit_buf; \
|
||||
int bit_count; \
|
||||
const uint8* pDecode_buf_next;
|
||||
|
||||
#define CRNLIB_SYMBOL_CODEC_DECODE_BEGIN(codec) \
|
||||
arith_value = codec.m_arith_value; \
|
||||
arith_length = codec.m_arith_length; \
|
||||
bit_buf = codec.m_bit_buf; \
|
||||
bit_count = codec.m_bit_count; \
|
||||
pDecode_buf_next = codec.m_pDecode_buf_next;
|
||||
|
||||
#define CRNLIB_SYMBOL_CODEC_DECODE_END(codec) \
|
||||
codec.m_arith_value = arith_value; \
|
||||
codec.m_arith_length = arith_length; \
|
||||
codec.m_bit_buf = bit_buf; \
|
||||
codec.m_bit_count = bit_count; \
|
||||
codec.m_pDecode_buf_next = pDecode_buf_next;
|
||||
|
||||
#define CRNLIB_SYMBOL_CODEC_DECODE_GET_BITS(codec, result, num_bits) \
|
||||
{ \
|
||||
while (bit_count < (int)(num_bits)) \
|
||||
{ \
|
||||
uint c = 0; \
|
||||
if (pDecode_buf_next == codec.m_pDecode_buf_end) \
|
||||
{ \
|
||||
CRNLIB_SYMBOL_CODEC_DECODE_END(codec) \
|
||||
codec.decode_need_bytes(); \
|
||||
CRNLIB_SYMBOL_CODEC_DECODE_BEGIN(codec) \
|
||||
if (pDecode_buf_next < codec.m_pDecode_buf_end) c = *pDecode_buf_next++; \
|
||||
} \
|
||||
else \
|
||||
c = *pDecode_buf_next++; \
|
||||
bit_count += 8; \
|
||||
bit_buf |= (static_cast<symbol_codec::bit_buf_t>(c) << (symbol_codec::cBitBufSize - bit_count)); \
|
||||
} \
|
||||
result = num_bits ? static_cast<uint>(bit_buf >> (symbol_codec::cBitBufSize - (num_bits))) : 0; \
|
||||
bit_buf <<= (num_bits); \
|
||||
bit_count -= (num_bits); \
|
||||
}
|
||||
|
||||
#define CRNLIB_SYMBOL_CODEC_DECODE_ARITH_BIT(codec, result, model) \
|
||||
{ \
|
||||
if (arith_length < cSymbolCodecArithMinLen) \
|
||||
{ \
|
||||
uint c; \
|
||||
CRNLIB_SYMBOL_CODEC_DECODE_GET_BITS(codec, c, 8); \
|
||||
arith_value = (arith_value << 8) | c; \
|
||||
arith_length <<= 8; \
|
||||
} \
|
||||
uint x = model.m_bit_0_prob * (arith_length >> cSymbolCodecArithProbBits); \
|
||||
result = (arith_value >= x); \
|
||||
if (!result) \
|
||||
{ \
|
||||
model.m_bit_0_prob += ((cSymbolCodecArithProbScale - model.m_bit_0_prob) >> cSymbolCodecArithProbMoveBits); \
|
||||
arith_length = x; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
model.m_bit_0_prob -= (model.m_bit_0_prob >> cSymbolCodecArithProbMoveBits); \
|
||||
arith_value -= x; \
|
||||
arith_length -= x; \
|
||||
} \
|
||||
}
|
||||
|
||||
#if CRNLIB_SYMBOL_CODEC_USE_64_BIT_BUFFER
|
||||
#define CRNLIB_SYMBOL_CODEC_DECODE_ADAPTIVE_HUFFMAN(codec, result, model) \
|
||||
{ \
|
||||
const prefix_coding::decoder_tables* pTables = model.m_pDecode_tables; \
|
||||
if (bit_count < 24) \
|
||||
{ \
|
||||
uint c = 0; \
|
||||
pDecode_buf_next += sizeof(uint32); \
|
||||
if (pDecode_buf_next >= codec.m_pDecode_buf_end) \
|
||||
{ \
|
||||
pDecode_buf_next -= sizeof(uint32); \
|
||||
while (bit_count < 24) \
|
||||
{ \
|
||||
CRNLIB_SYMBOL_CODEC_DECODE_END(codec) \
|
||||
codec.decode_need_bytes(); \
|
||||
CRNLIB_SYMBOL_CODEC_DECODE_BEGIN(codec) \
|
||||
if (pDecode_buf_next < codec.m_pDecode_buf_end) c = *pDecode_buf_next++; \
|
||||
bit_count += 8; \
|
||||
bit_buf |= (static_cast<symbol_codec::bit_buf_t>(c) << (symbol_codec::cBitBufSize - bit_count)); \
|
||||
} \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
c = CRNLIB_READ_BIG_ENDIAN_UINT32(pDecode_buf_next - sizeof(uint32)); \
|
||||
bit_count += 32; \
|
||||
bit_buf |= (static_cast<symbol_codec::bit_buf_t>(c) << (symbol_codec::cBitBufSize - bit_count)); \
|
||||
} \
|
||||
} \
|
||||
uint k = static_cast<uint>((bit_buf >> (symbol_codec::cBitBufSize - 16)) + 1); \
|
||||
uint len; \
|
||||
if (k <= pTables->m_table_max_code) \
|
||||
{ \
|
||||
uint32 t = pTables->m_lookup[bit_buf >> (symbol_codec::cBitBufSize - pTables->m_table_bits)]; \
|
||||
result = t & UINT16_MAX; \
|
||||
len = t >> 16; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
len = pTables->m_decode_start_code_size; \
|
||||
for ( ; ; ) \
|
||||
{ \
|
||||
if (k <= pTables->m_max_codes[len - 1]) \
|
||||
break; \
|
||||
len++; \
|
||||
} \
|
||||
int val_ptr = pTables->m_val_ptrs[len - 1] + static_cast<int>(bit_buf >> (symbol_codec::cBitBufSize - len)); \
|
||||
if (((uint)val_ptr >= model.m_total_syms)) val_ptr = 0; \
|
||||
result = pTables->m_sorted_symbol_order[val_ptr]; \
|
||||
} \
|
||||
bit_buf <<= len; \
|
||||
bit_count -= len; \
|
||||
uint freq = model.m_sym_freq[result]; \
|
||||
freq++; \
|
||||
model.m_sym_freq[result] = static_cast<uint16>(freq); \
|
||||
if (freq == UINT16_MAX) model.rescale(); \
|
||||
if (--model.m_symbols_until_update == 0) \
|
||||
{ \
|
||||
model.update(); \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
#define CRNLIB_SYMBOL_CODEC_DECODE_ADAPTIVE_HUFFMAN(codec, result, model) \
|
||||
{ \
|
||||
const prefix_coding::decoder_tables* pTables = model.m_pDecode_tables; \
|
||||
while (bit_count < (symbol_codec::cBitBufSize - 8)) \
|
||||
{ \
|
||||
uint c = 0; \
|
||||
if (pDecode_buf_next == codec.m_pDecode_buf_end) \
|
||||
{ \
|
||||
CRNLIB_SYMBOL_CODEC_DECODE_END(codec) \
|
||||
codec.decode_need_bytes(); \
|
||||
CRNLIB_SYMBOL_CODEC_DECODE_BEGIN(codec) \
|
||||
if (pDecode_buf_next < codec.m_pDecode_buf_end) c = *pDecode_buf_next++; \
|
||||
} \
|
||||
else \
|
||||
c = *pDecode_buf_next++; \
|
||||
bit_count += 8; \
|
||||
bit_buf |= (static_cast<symbol_codec::bit_buf_t>(c) << (symbol_codec::cBitBufSize - bit_count)); \
|
||||
} \
|
||||
uint k = static_cast<uint>((bit_buf >> (symbol_codec::cBitBufSize - 16)) + 1); \
|
||||
uint len; \
|
||||
if (k <= pTables->m_table_max_code) \
|
||||
{ \
|
||||
uint32 t = pTables->m_lookup[bit_buf >> (symbol_codec::cBitBufSize - pTables->m_table_bits)]; \
|
||||
result = t & UINT16_MAX; \
|
||||
len = t >> 16; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
len = pTables->m_decode_start_code_size; \
|
||||
for ( ; ; ) \
|
||||
{ \
|
||||
if (k <= pTables->m_max_codes[len - 1]) \
|
||||
break; \
|
||||
len++; \
|
||||
} \
|
||||
int val_ptr = pTables->m_val_ptrs[len - 1] + static_cast<int>(bit_buf >> (symbol_codec::cBitBufSize - len)); \
|
||||
if (((uint)val_ptr >= model.m_total_syms)) val_ptr = 0; \
|
||||
result = pTables->m_sorted_symbol_order[val_ptr]; \
|
||||
} \
|
||||
bit_buf <<= len; \
|
||||
bit_count -= len; \
|
||||
uint freq = model.m_sym_freq[result]; \
|
||||
freq++; \
|
||||
model.m_sym_freq[result] = static_cast<uint16>(freq); \
|
||||
if (freq == UINT16_MAX) model.rescale(); \
|
||||
if (--model.m_symbols_until_update == 0) \
|
||||
{ \
|
||||
model.update(); \
|
||||
} \
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
#define CRNLIB_SYMBOL_CODEC_DECODE_DECLARE(codec)
|
||||
#define CRNLIB_SYMBOL_CODEC_DECODE_BEGIN(codec)
|
||||
#define CRNLIB_SYMBOL_CODEC_DECODE_END(codec)
|
||||
|
||||
#define CRNLIB_SYMBOL_CODEC_DECODE_GET_BITS(codec, result, num_bits) result = codec.decode_bits(num_bits);
|
||||
#define CRNLIB_SYMBOL_CODEC_DECODE_ARITH_BIT(codec, result, model) result = codec.decode(model);
|
||||
#define CRNLIB_SYMBOL_CODEC_DECODE_ADAPTIVE_HUFFMAN(codec, result, model) result = codec.decode(model);
|
||||
#endif
|
||||
|
||||
} // namespace crnlib
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
// File: crn_task_pool.cpp
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#include "crn_core.h"
|
||||
#include "crn_task_pool.h"
|
||||
#include <process.h>
|
||||
|
||||
#include "crn_winhdr.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
task_pool::task_pool() :
|
||||
m_num_threads(0),
|
||||
m_num_outstanding_tasks(0),
|
||||
m_exit_flag(false)
|
||||
{
|
||||
utils::zero_object(m_threads);
|
||||
}
|
||||
|
||||
task_pool::task_pool(uint num_threads) :
|
||||
m_num_threads(0),
|
||||
m_num_outstanding_tasks(0),
|
||||
m_exit_flag(false)
|
||||
{
|
||||
utils::zero_object(m_threads);
|
||||
bool status = init(num_threads);
|
||||
CRNLIB_VERIFY(status);
|
||||
}
|
||||
|
||||
task_pool::~task_pool()
|
||||
{
|
||||
deinit();
|
||||
}
|
||||
|
||||
bool task_pool::init(uint num_threads)
|
||||
{
|
||||
CRNLIB_ASSERT(num_threads <= cMaxThreads);
|
||||
num_threads = math::minimum<uint>(num_threads, cMaxThreads);
|
||||
|
||||
deinit();
|
||||
|
||||
m_task_condition_var.lock();
|
||||
|
||||
m_num_threads = num_threads;
|
||||
|
||||
bool succeeded = true;
|
||||
for (uint i = 0; i < num_threads; i++)
|
||||
{
|
||||
m_threads[i] = (HANDLE)_beginthreadex(NULL, 32768, thread_func, this, 0, NULL);
|
||||
|
||||
CRNLIB_ASSERT(m_threads[i] != 0);
|
||||
if (!m_threads[i])
|
||||
{
|
||||
succeeded = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_task_condition_var.unlock();
|
||||
|
||||
if (!succeeded)
|
||||
{
|
||||
deinit();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void task_pool::deinit()
|
||||
{
|
||||
if (m_num_threads)
|
||||
{
|
||||
m_task_condition_var.lock();
|
||||
|
||||
m_exit_flag = true;
|
||||
|
||||
m_task_condition_var.unlock();
|
||||
|
||||
for (uint i = 0; i < m_num_threads; i++)
|
||||
{
|
||||
if (m_threads[i])
|
||||
{
|
||||
for ( ; ; )
|
||||
{
|
||||
uint32 result = WaitForSingleObject(m_threads[i], 1000);
|
||||
if (result == WAIT_OBJECT_0)
|
||||
break;
|
||||
}
|
||||
|
||||
CloseHandle(m_threads[i]);
|
||||
|
||||
m_threads[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
m_num_threads = 0;
|
||||
|
||||
m_exit_flag = false;
|
||||
}
|
||||
|
||||
m_tasks.clear();
|
||||
m_num_outstanding_tasks = 0;
|
||||
}
|
||||
|
||||
uint task_pool::get_num_threads() const
|
||||
{
|
||||
return m_num_threads;
|
||||
}
|
||||
|
||||
void task_pool::queue_task(task_callback_func pFunc, uint64 data, void* pData_ptr)
|
||||
{
|
||||
CRNLIB_ASSERT(pFunc);
|
||||
|
||||
m_task_condition_var.lock();
|
||||
|
||||
task tsk;
|
||||
tsk.m_callback = pFunc;
|
||||
tsk.m_data = data;
|
||||
tsk.m_pData_ptr = pData_ptr;
|
||||
tsk.m_flags = 0;
|
||||
m_tasks.push_back(tsk);
|
||||
|
||||
m_num_outstanding_tasks++;
|
||||
|
||||
m_task_condition_var.unlock();
|
||||
}
|
||||
|
||||
// It's the object's responsibility to crnlib_delete pObj within the execute_task() method, if needed!
|
||||
void task_pool::queue_task(executable_task* pObj, uint64 data, void* pData_ptr)
|
||||
{
|
||||
CRNLIB_ASSERT(pObj);
|
||||
|
||||
m_task_condition_var.lock();
|
||||
|
||||
task tsk;
|
||||
tsk.m_pObj = pObj;
|
||||
tsk.m_data = data;
|
||||
tsk.m_pData_ptr = pData_ptr;
|
||||
tsk.m_flags = cTaskFlagObject;
|
||||
m_tasks.push_back(tsk);
|
||||
|
||||
m_num_outstanding_tasks++;
|
||||
|
||||
m_task_condition_var.unlock();
|
||||
}
|
||||
|
||||
bool task_pool::join_condition_func(void* pCallback_data_ptr, uint64 callback_data)
|
||||
{
|
||||
callback_data;
|
||||
|
||||
task_pool* pPool = static_cast<task_pool*>(pCallback_data_ptr);
|
||||
|
||||
return (!pPool->m_num_outstanding_tasks) || pPool->m_exit_flag;
|
||||
}
|
||||
|
||||
void task_pool::process_task(task& tsk)
|
||||
{
|
||||
if (tsk.m_flags & cTaskFlagObject)
|
||||
tsk.m_pObj->execute_task(tsk.m_data, tsk.m_pData_ptr);
|
||||
else
|
||||
tsk.m_callback(tsk.m_data, tsk.m_pData_ptr);
|
||||
|
||||
m_task_condition_var.lock();
|
||||
|
||||
m_num_outstanding_tasks--;
|
||||
|
||||
m_task_condition_var.unlock();
|
||||
}
|
||||
|
||||
void task_pool::join()
|
||||
{
|
||||
for ( ; ; )
|
||||
{
|
||||
m_task_condition_var.lock();
|
||||
|
||||
if (!m_tasks.empty())
|
||||
{
|
||||
task tsk(m_tasks.front());
|
||||
m_tasks.pop_front();
|
||||
|
||||
m_task_condition_var.unlock();
|
||||
|
||||
process_task(tsk);
|
||||
}
|
||||
else
|
||||
{
|
||||
int result = m_task_condition_var.wait(join_condition_func, this);
|
||||
result;
|
||||
CRNLIB_ASSERT(result >= 0);
|
||||
|
||||
m_task_condition_var.unlock();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool task_pool::wait_condition_func(void* pCallback_data_ptr, uint64 callback_data)
|
||||
{
|
||||
callback_data;
|
||||
|
||||
task_pool* pPool = static_cast<task_pool*>(pCallback_data_ptr);
|
||||
|
||||
return (!pPool->m_tasks.empty()) || pPool->m_exit_flag;
|
||||
}
|
||||
|
||||
unsigned __stdcall task_pool::thread_func(void* pContext)
|
||||
{
|
||||
//set_thread_name(GetCurrentThreadId(), "taskpoolhelper");
|
||||
|
||||
task_pool* pPool = static_cast<task_pool*>(pContext);
|
||||
|
||||
for ( ; ; )
|
||||
{
|
||||
pPool->m_task_condition_var.lock();
|
||||
|
||||
int result = pPool->m_task_condition_var.wait(wait_condition_func, pPool);
|
||||
|
||||
CRNLIB_ASSERT(result >= 0);
|
||||
|
||||
if ((result < 0) || (pPool->m_exit_flag))
|
||||
{
|
||||
pPool->m_task_condition_var.unlock();
|
||||
break;
|
||||
}
|
||||
|
||||
if (pPool->m_tasks.empty())
|
||||
pPool->m_task_condition_var.unlock();
|
||||
else
|
||||
{
|
||||
task tsk(pPool->m_tasks.front());
|
||||
pPool->m_tasks.pop_front();
|
||||
|
||||
pPool->m_task_condition_var.unlock();
|
||||
|
||||
pPool->process_task(tsk);
|
||||
}
|
||||
}
|
||||
|
||||
_endthreadex(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user