diff --git a/src/cram.c b/src/cram.c index 793b847..2944eca 100644 --- a/src/cram.c +++ b/src/cram.c @@ -110,6 +110,10 @@ typedef struct RectPackContext Rect *freeRectangles; int32_t freeRectangleCount; int32_t freeRectangleCapacity; + + Rect *newFreeRectangles; + int32_t newFreeRectangleCount; + int32_t newFreeRectangleCapacity; } RectPackContext; typedef struct PackScoreInfo @@ -237,10 +241,14 @@ RectPackContext* Cram_Internal_InitRectPacker(uint32_t width, uint32_t height) context->freeRectangles[0].h = height; context->freeRectangleCount = 1; + context->newFreeRectangleCapacity = INITIAL_FREE_RECTANGLE_CAPACITY; + context->newFreeRectangles = Cram_malloc(sizeof(Rect) * context->freeRectangleCapacity); + context->newFreeRectangleCount = 0; + return context; } -/* Uses the bottom left (Tetris) heuristic. */ +/* Uses the short side fit heuristic. */ /* TODO: make the heuristic configurable? */ void Cram_Internal_Score( RectPackContext *context, @@ -249,8 +257,8 @@ void Cram_Internal_Score( PackScoreInfo *scoreInfo ) { Rect *freeRect; - int32_t areaFit; int32_t shortestSide; + int32_t longestSide; int32_t i; scoreInfo->score = INT32_MAX; @@ -262,13 +270,13 @@ void Cram_Internal_Score( if (freeRect->w >= width && freeRect->h >= height) { - areaFit = freeRect->w * freeRect->h - width * height; shortestSide = Cram_min(freeRect->w - width, freeRect->h - height); + longestSide = Cram_max(freeRect->w - width, freeRect->h - height); - if (areaFit < scoreInfo->score || (areaFit == scoreInfo->score && shortestSide < scoreInfo->secondaryScore)) + if (shortestSide < scoreInfo->score || (shortestSide == scoreInfo->score && longestSide < scoreInfo->secondaryScore)) { - scoreInfo->score = areaFit; - scoreInfo->secondaryScore = shortestSide; + scoreInfo->score = shortestSide; + scoreInfo->secondaryScore = longestSide; scoreInfo->x = freeRect->x; scoreInfo->y = freeRect->y; } @@ -287,45 +295,63 @@ static inline uint8_t Cram_Internal_Contains(Rect* a, Rect* b) void Cram_Internal_PruneRects(RectPackContext* context) { int32_t i, j; - Rect* a; - Rect* b; - for (i = context->freeRectangleCount - 1; i >= 0; i -= 1) + for (i = 0; i < context->freeRectangleCount; i += 1) { - a = &context->freeRectangles[i]; - - for (j = context->freeRectangleCount - 1; j > i; j -= 1) + for (j = 0; j < context->newFreeRectangleCount;) { - b = &context->freeRectangles[j]; - - if (Cram_Internal_Contains(b, a)) + if (Cram_Internal_Contains(&context->freeRectangles[i], &context->newFreeRectangles[j])) { /* plug the hole */ - context->freeRectangles[j] = context->freeRectangles[context->freeRectangleCount - 1]; - context->freeRectangleCount -= 1; - break; + context->newFreeRectangles[j] = context->newFreeRectangles[context->newFreeRectangleCount - 1]; + context->newFreeRectangleCount -= 1; } - - if (Cram_Internal_Contains(a, b)) + else { - /* plug the hole */ - context->freeRectangles[j] = context->freeRectangles[context->freeRectangleCount - 1]; - context->freeRectangleCount -= 1; + j += 1; } } } -} -static inline void Cram_Internal_AddFreeRect(RectPackContext *context, Rect rect) -{ - if (context->freeRectangleCount == context->freeRectangleCapacity) + if (context->freeRectangleCapacity < context->freeRectangleCount + context->newFreeRectangleCount) { - context->freeRectangleCapacity *= 2; + context->freeRectangleCapacity = context->freeRectangleCount + context->newFreeRectangleCount; context->freeRectangles = Cram_realloc(context->freeRectangles, sizeof(Rect) * context->freeRectangleCapacity); } - context->freeRectangles[context->freeRectangleCount] = rect; - context->freeRectangleCount += 1; + for (i = 0; i < context->newFreeRectangleCount; i += 1) + { + context->freeRectangles[context->freeRectangleCount] = context->newFreeRectangles[i]; + context->freeRectangleCount += 1; + } +} + +static inline void Cram_Internal_AddNewFreeRect(RectPackContext *context, Rect rect) +{ + int32_t i; + + for (i = context->newFreeRectangleCount - 1; i >= 0; i -= 1) + { + if (Cram_Internal_Contains(&context->newFreeRectangles[i], &rect)) + { + return; + } + + if (Cram_Internal_Contains(&rect, &context->newFreeRectangles[i])) + { + context->newFreeRectangles[i] = context->newFreeRectangles[context->newFreeRectangleCount - 1]; + context->newFreeRectangleCount -= 1; + } + } + + if (context->newFreeRectangleCount == context->newFreeRectangleCapacity) + { + context->newFreeRectangleCapacity *= 2; + context->newFreeRectangles = Cram_realloc(context->newFreeRectangles, sizeof(Rect) * context->newFreeRectangleCapacity); + } + + context->newFreeRectangles[context->newFreeRectangleCount] = rect; + context->newFreeRectangleCount += 1; } uint8_t Cram_Internal_SplitRect(RectPackContext *context, Rect *rect, Rect *freeRect) @@ -351,7 +377,7 @@ uint8_t Cram_Internal_SplitRect(RectPackContext *context, Rect *rect, Rect *free { newRect = *freeRect; newRect.w = rect->x - freeRect->x; - Cram_Internal_AddFreeRect(context, newRect); + Cram_Internal_AddNewFreeRect(context, newRect); } /* Right side */ @@ -360,7 +386,7 @@ uint8_t Cram_Internal_SplitRect(RectPackContext *context, Rect *rect, Rect *free newRect = *freeRect; newRect.x = rect->x + rect->w; newRect.w = freeRect->x + freeRect->w - (rect->x + rect->w); - Cram_Internal_AddFreeRect(context, newRect); + Cram_Internal_AddNewFreeRect(context, newRect); } } @@ -371,7 +397,7 @@ uint8_t Cram_Internal_SplitRect(RectPackContext *context, Rect *rect, Rect *free { newRect = *freeRect; newRect.h = rect->y - freeRect->y; - Cram_Internal_AddFreeRect(context, newRect); + Cram_Internal_AddNewFreeRect(context, newRect); } /* Bottom side */ @@ -380,7 +406,7 @@ uint8_t Cram_Internal_SplitRect(RectPackContext *context, Rect *rect, Rect *free newRect = *freeRect; newRect.y = rect->y + rect->h; newRect.h = freeRect->y + freeRect->h - (rect->y + rect->h); - Cram_Internal_AddFreeRect(context, newRect); + Cram_Internal_AddNewFreeRect(context, newRect); } } @@ -409,6 +435,8 @@ void Cram_Internal_PlaceRect(RectPackContext *context, Rect *rect) } Cram_Internal_PruneRects(context); + + context->newFreeRectangleCount = 0; } /* Given rects with width and height, modifies rects with packed x and y positions. */