deduplication

pull/1/head
cosmonaut 2022-07-19 16:51:02 -07:00
parent 02ddf8c5e1
commit aeb8ed39f7
3 changed files with 2044 additions and 65 deletions

View File

@ -44,6 +44,7 @@ file(GLOB SOURCE_FILES
#Source #Source
lib/stb_rect_pack.h lib/stb_rect_pack.h
lib/stb_image.h lib/stb_image.h
lib/stb_ds.h
src/cram.c src/cram.c
) )

1895
lib/stb_ds.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -62,8 +62,15 @@
#define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h" #include "stb_image.h"
#define STB_DS_IMPLEMENTATION
#include "stb_ds.h"
#define INITIAL_DATA_CAPACITY 8 #define INITIAL_DATA_CAPACITY 8
#define STBDS_SIZE_T_BITS ((sizeof (size_t)) * 8)
#define STBDS_ROTATE_LEFT(val, n) (((val) << (n)) | ((val) >> (STBDS_SIZE_T_BITS - (n))))
#define STBDS_ROTATE_RIGHT(val, n) (((val) >> (n)) | ((val) << (STBDS_SIZE_T_BITS - (n))))
/* Structures */ /* Structures */
typedef struct Rect typedef struct Rect
@ -76,7 +83,9 @@ typedef struct Cram_Image
{ {
const char *name; const char *name;
Rect rect; Rect rect;
uint8_t *pixels; uint8_t duplicate;
uint8_t *pixels; // will be NULL if duplicate!
size_t hash;
} Cram_Image; } Cram_Image;
typedef struct Cram_Internal_Context typedef struct Cram_Internal_Context
@ -127,41 +136,30 @@ Cram_Context* Cram_Init(Cram_ContextCreateInfo *createInfo)
return (Cram_Context*) context; return (Cram_Context*) context;
} }
static inline uint32_t Cram_Internal_GetPixelIndex(uint32_t x, uint32_t y, uint32_t width) static uint8_t Cram_Internal_IsImageEqual(Cram_Image *a, Cram_Image *b)
{ {
return x + y * width; int32_t i;
} if (a->hash == b->hash && a->rect.w == b->rect.w && a->rect.h == b->rect.h)
/* width and height of source and destination rects must be the same! */
static int8_t Cram_Internal_CopyPixels(
uint32_t *dstPixels,
uint32_t dstPixelWidth,
uint32_t *srcPixels,
uint32_t srcPixelWidth,
Rect *dstRect,
Rect *srcRect
) {
int32_t i, j;
int32_t dstPixelIndex, srcPixelIndex;
if (dstRect->w != srcRect->w || dstRect->h != srcRect->h)
{ {
return -1; for (i = 0; i < a->rect.w * a->rect.h * 4; i += 1)
}
for (i = 0; i < dstRect->w; i += 1)
{
for (j = 0; j < dstRect->h; j += 1)
{ {
dstPixelIndex = Cram_Internal_GetPixelIndex(i + dstRect->x, j + dstRect->y, dstPixelWidth); if (a->pixels[i] != b->pixels[i])
srcPixelIndex = Cram_Internal_GetPixelIndex(i + srcRect->x, j + srcRect->y, srcPixelWidth); {
dstPixels[dstPixelIndex] = srcPixels[srcPixelIndex]; return 0;
}
} }
return 1;
} }
return 0; return 0;
} }
static inline uint32_t Cram_Internal_GetPixelIndex(uint32_t x, uint32_t y, uint32_t width)
{
return x + y * width;
}
static uint8_t Cram_Internal_IsRowClear(uint32_t* pixels, uint32_t rowIndex, uint32_t width) static uint8_t Cram_Internal_IsRowClear(uint32_t* pixels, uint32_t rowIndex, uint32_t width)
{ {
int32_t i; int32_t i;
@ -192,6 +190,36 @@ static uint8_t Cram_Internal_IsColumnClear(uint32_t* pixels, uint32_t columnInde
return 1; return 1;
} }
/* width and height of source and destination rects must be the same! */
static int8_t Cram_Internal_CopyPixels(
uint32_t *dstPixels,
uint32_t dstPixelWidth,
uint32_t *srcPixels,
uint32_t srcPixelWidth,
Rect *dstRect,
Rect *srcRect
) {
int32_t i, j;
int32_t dstPixelIndex, srcPixelIndex;
if (dstRect->w != srcRect->w || dstRect->h != srcRect->h)
{
return -1;
}
for (i = 0; i < dstRect->w; i += 1)
{
for (j = 0; j < dstRect->h; j += 1)
{
dstPixelIndex = Cram_Internal_GetPixelIndex(i + dstRect->x, j + dstRect->y, dstPixelWidth);
srcPixelIndex = Cram_Internal_GetPixelIndex(i + srcRect->x, j + srcRect->y, srcPixelWidth);
dstPixels[dstPixelIndex] = srcPixels[srcPixelIndex];
}
}
return 0;
}
void Cram_AddFile(Cram_Context *context, const char *path) void Cram_AddFile(Cram_Context *context, const char *path)
{ {
Cram_Internal_Context *internalContext = (Cram_Internal_Context*) context; Cram_Internal_Context *internalContext = (Cram_Internal_Context*) context;
@ -247,7 +275,7 @@ void Cram_AddFile(Cram_Context *context, const char *path)
{ {
if (!Cram_Internal_IsRowClear(pixels, i, width)) if (!Cram_Internal_IsRowClear(pixels, i, width))
{ {
bottomTrim = i; bottomTrim = i + 1;
break; break;
} }
} }
@ -257,7 +285,7 @@ void Cram_AddFile(Cram_Context *context, const char *path)
{ {
if (!Cram_Internal_IsColumnClear(pixels, i, width, height)) if (!Cram_Internal_IsColumnClear(pixels, i, width, height))
{ {
rightTrim = i; rightTrim = i + 1;
break; break;
} }
} }
@ -275,7 +303,7 @@ void Cram_AddFile(Cram_Context *context, const char *path)
image->rect.h = height; image->rect.h = height;
} }
/* copy */ /* copy and free source pixels */
image->pixels = Cram_malloc(image->rect.w * image->rect.h * 4); image->pixels = Cram_malloc(image->rect.w * image->rect.h * 4);
Rect dstRect; Rect dstRect;
@ -284,9 +312,28 @@ void Cram_AddFile(Cram_Context *context, const char *path)
dstRect.w = image->rect.w; dstRect.w = image->rect.w;
dstRect.h = image->rect.h; dstRect.h = image->rect.h;
Cram_Internal_CopyPixels((uint32_t*) image->pixels, image->rect.w, (uint32_t*) pixels, width, &dstRect, &image->rect); Cram_Internal_CopyPixels((uint32_t*) image->pixels, image->rect.w, (uint32_t*) pixels, width, &dstRect, &image->rect);
stbi_image_free(pixels); stbi_image_free(pixels);
/* hash */
image->hash = stbds_hash_bytes(image->pixels, image->rect.w * image->rect.h * 4, 0);
/* check if this is a duplicate */
image->duplicate = 0;
for (i = 0; i < internalContext->imageCount; i += 1)
{
if (!internalContext->images[i].duplicate)
{
if (Cram_Internal_IsImageEqual(image, &internalContext->images[i]))
{
/* this is duplicate data! */
image->duplicate = 1;
Cram_free(image->pixels);
image->pixels = NULL;
break;
}
}
}
internalContext->imageCount += 1; internalContext->imageCount += 1;
} }
@ -296,40 +343,64 @@ int8_t Cram_Pack(Cram_Context *context)
Cram_Internal_Context *internalContext = (Cram_Internal_Context*) context; Cram_Internal_Context *internalContext = (Cram_Internal_Context*) context;
uint32_t numNodes = internalContext->width; uint32_t numNodes = internalContext->width;
stbrp_node *nodes = Cram_malloc(sizeof(stbrp_node) * numNodes); stbrp_node *nodes = Cram_malloc(sizeof(stbrp_node) * numNodes);
stbrp_rect *rects = Cram_malloc(sizeof(stbrp_rect) * internalContext->imageCount); stbrp_rect *rects;
uint32_t numRects = 0;
stbrp_rect *rect; stbrp_rect *rect;
Rect dstRect, srcRect; Rect dstRect, srcRect;
int32_t i; int32_t i;
uint32_t maxWidth = 0; uint32_t maxWidth = 0;
uint32_t maxHeight = 0; uint32_t maxHeight = 0;
stbrp_init_target(&rectPackContext, internalContext->width, internalContext->height, nodes, numNodes); /* FIXME: this numRects repetition sucks */
for (i = 0; i < internalContext->imageCount; i += 1) for (i = 0; i < internalContext->imageCount; i += 1)
{ {
rect = &rects[i]; if (!internalContext->images[i].duplicate)
{
rect->w = internalContext->images[i].rect.w + internalContext->padding; numRects += 1;
rect->h = internalContext->images[i].rect.h + internalContext->padding; }
} }
stbrp_pack_rects(&rectPackContext, rects, internalContext->imageCount); rects = Cram_malloc(sizeof(stbrp_rect) * numRects);
stbrp_init_target(&rectPackContext, internalContext->width, internalContext->height, nodes, numNodes);
numRects = 0;
for (i = 0; i < internalContext->imageCount; i += 1) for (i = 0; i < internalContext->imageCount; i += 1)
{ {
rect = &rects[i]; if (!internalContext->images[i].duplicate)
if (rect->was_packed)
{ {
internalContext->images[i].rect.x = rect->x; rect = &rects[numRects];
internalContext->images[i].rect.y = rect->y;
maxWidth = Cram_max(maxWidth, rect->x + rect->w); rect->w = internalContext->images[i].rect.w + internalContext->padding;
maxHeight = Cram_max(maxHeight, rect->y + rect->h); rect->h = internalContext->images[i].rect.h + internalContext->padding;
numRects += 1;
} }
else }
stbrp_pack_rects(&rectPackContext, rects, numRects);
numRects = 0;
for (i = 0; i < internalContext->imageCount; i += 1)
{
if (!internalContext->images[i].duplicate)
{ {
return -1; rect = &rects[numRects];
if (rect->was_packed)
{
internalContext->images[i].rect.x = rect->x;
internalContext->images[i].rect.y = rect->y;
maxWidth = Cram_max(maxWidth, rect->x + rect->w);
maxHeight = Cram_max(maxHeight, rect->y + rect->h);
numRects += 1;
}
else
{
return -1;
}
} }
} }
@ -341,24 +412,27 @@ int8_t Cram_Pack(Cram_Context *context)
for (i = 0; i < internalContext->imageCount; i += 1) for (i = 0; i < internalContext->imageCount; i += 1)
{ {
dstRect.x = internalContext->images[i].rect.x; if (!internalContext->images[i].duplicate)
dstRect.y = internalContext->images[i].rect.y; {
dstRect.w = internalContext->images[i].rect.w; dstRect.x = internalContext->images[i].rect.x;
dstRect.h = internalContext->images[i].rect.h; dstRect.y = internalContext->images[i].rect.y;
dstRect.w = internalContext->images[i].rect.w;
dstRect.h = internalContext->images[i].rect.h;
srcRect.x = 0; srcRect.x = 0;
srcRect.y = 0; srcRect.y = 0;
srcRect.w = internalContext->images[i].rect.w; srcRect.w = internalContext->images[i].rect.w;
srcRect.h = internalContext->images[i].rect.h; srcRect.h = internalContext->images[i].rect.h;
Cram_Internal_CopyPixels( Cram_Internal_CopyPixels(
(uint32_t*) internalContext->pixels, (uint32_t*) internalContext->pixels,
internalContext->width, internalContext->width,
(uint32_t*) internalContext->images[i].pixels, (uint32_t*) internalContext->images[i].pixels,
internalContext->images[i].rect.w, internalContext->images[i].rect.w,
&dstRect, &dstRect,
&srcRect &srcRect
); );
}
} }
Cram_free(nodes); Cram_free(nodes);
@ -383,10 +457,19 @@ void Cram_GetAtlasData(Cram_Context *context, Cram_AtlasData **pAtlasData)
void Cram_Destroy(Cram_Context *context) void Cram_Destroy(Cram_Context *context)
{ {
Cram_Internal_Context *internalContext = (Cram_Internal_Context*) context; Cram_Internal_Context *internalContext = (Cram_Internal_Context*) context;
int32_t i;
if (internalContext->pixels != NULL) if (internalContext->pixels != NULL)
{ {
Cram_free(internalContext); Cram_free(internalContext->pixels);
}
for (i = 0; i < internalContext->imageCount; i += 1)
{
if (!internalContext->images[i].duplicate)
{
Cram_free(internalContext->images[i].pixels);
}
} }
Cram_free(internalContext->images); Cram_free(internalContext->images);