Files
unity/crnlib/crn_arealist.cpp
T
Alexander Suvorov 3e12aff909 Fix miscellaneous compiler warnings
DXT Testing:

The modified algorithm has been tested on the Kodak test set using 64-bit build with default settings (running on Windows 10, i7-4790, 3.6GHz). All the decompressed test images are identical to the images being compressed and decompressed using original version of Crunch (revision ea9b8d8).

[Compressing Kodak set without mipmaps using DXT1 encoding]
Original: 1582222 bytes / 28.866 sec
Modified: 1468204 bytes / 11.858 sec
Improvement: 7.21% (compression ratio) / 58.92% (compression time)

[Compressing Kodak set with mipmaps using DXT1 encoding]
Original: 2065243 bytes / 36.878 sec
Modified: 1914805 bytes / 15.625 sec
Improvement: 7.28% (compression ratio) / 57.63% (compression time)

ETC Testing:

The modified algorithm has been tested on the Kodak test set using 64-bit build with default settings (running on Windows 10, i7-4790, 3.6GHz). The ETC1 quantization parameters have been selected in such a way, so that ETC1 compression gives approximately the same average Luma PSNR as the corresponding DXT1 compression (which is equal to 34.044 dB for the Kodak test set compressed without mipmaps using DXT1 encoding and default quality settings).

[Compressing Kodak set without mipmaps using ETC1 encoding]
Total size: 1607858 bytes
Total time: 17.181 sec
Average bitrate: 1.363 bpp
Average Luma PSNR: 34.050 dB
2017-09-11 13:52:21 +02:00

626 lines
17 KiB
C++

// File: crn_arealist.cpp - 2D shape algebra (currently unused)
// See Copyright Notice and license at the end of inc/crnlib.h
// Ported from the PowerView DOS image viewer, a product I wrote back in 1993. Not currently used in the open source release of crnlib.
#include "crn_core.h"
#include "crn_arealist.h"
#define RECT_DEBUG
namespace crnlib {
static void area_fatal_error(const char*, const char* pMsg, ...) {
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