packing and write implementation

pull/1/head
cosmonaut 2022-07-19 15:43:09 -07:00
parent bd8194afc4
commit 02ddf8c5e1
6 changed files with 3277 additions and 152 deletions

View File

@ -1,8 +1,8 @@
cmake_minimum_required(VERSION 2.8.12)
project(Cram C)
option(BUILD_CLI "Build command line executable" ON)
option(BUILD_SHARED_LIBS "Build shared library" ON)
option(USE_SDL2 "Use SDL2" ON)
SET(LIB_MAJOR_VERSION "0")
SET(LIB_MINOR_VERSION "3")
@ -38,11 +38,7 @@ if(UNIX)
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE)
endif()
if(USE_SDL2)
add_definitions(-DUSE_SDL2)
endif()
add_library(Cram
file(GLOB SOURCE_FILES
#Public header
include/cram.h
#Source
@ -51,6 +47,31 @@ add_library(Cram
src/cram.c
)
set(LINKSTYLE PRIVATE)
if(BUILD_SHARED_LIBS)
set(LINKSTYLE PUBLIC)
endif()
if(BUILD_CLI)
add_executable(cramcli
tools/cli/dirent.h
tools/cli/stb_image_write.h
tools/cli/main.c
)
if(BUILD_SHARED_LIBS)
target_link_libraries(cramcli PUBLIC Cram)
else()
target_link_libraries(cramcli PRIVATE Cram)
endif()
endif()
if(BUILD_SHARED_LIBS)
add_library(Cram SHARED ${SOURCE_FILES})
else()
add_library(Cram STATIC ${SOURCE_FILES})
endif()
# Build flags
if(NOT MSVC)
set_property(TARGET Cram PROPERTY COMPILE_FLAGS "-std=gnu99 -Wall -Wno-strict-aliasing -pedantic")
@ -68,26 +89,3 @@ set_target_properties(Cram PROPERTIES OUTPUT_NAME "Cram"
VERSION ${LIB_VERSION}
SOVERSION ${LIB_MAJOR_VERSION}
)
# SDL2 Dependency
if(USE_SDL2)
if (DEFINED SDL2_INCLUDE_DIRS AND DEFINED SDL2_LIBRARIES)
message(STATUS "using pre-defined SDL2 variables SDL2_INCLUDE_DIRS and SDL2_LIBRARIES")
target_include_directories(Cram PUBLIC "$<BUILD_INTERFACE:${SDL2_INCLUDE_DIRS}>")
target_link_libraries(Cram PUBLIC ${SDL2_LIBRARIES})
else()
# Only try to autodetect if both SDL2 variables aren't explicitly set
find_package(SDL2 CONFIG)
if (TARGET SDL2::SDL2)
message(STATUS "using TARGET SDL2::SDL2")
target_link_libraries(Cram PUBLIC SDL2::SDL2)
elseif (TARGET SDL2)
message(STATUS "using TARGET SDL2")
target_link_libraries(Cram PUBLIC SDL2)
else()
message(STATUS "no TARGET SDL2::SDL2, or SDL2, using variables")
target_include_directories(Cram PUBLIC "$<BUILD_INTERFACE:${SDL2_INCLUDE_DIRS}>")
target_link_libraries(Cram PUBLIC ${SDL2_LIBRARIES})
endif()
endif()
endif()

View File

@ -35,9 +35,7 @@
#define CRAMCALL
#endif
#ifdef USE_SDL2
#include <SDL.h>
#endif
#include <stdint.h>
#ifdef __cplusplus
extern "C"
@ -97,9 +95,8 @@ typedef struct Cram_AtlasData
CRAMAPI Cram_Context* Cram_Init(Cram_ContextCreateInfo *createInfo);
CRAMAPI void Cram_AddFile(Cram_Context *context, const char *path);
CRAMAPI void Cram_AddFolder(Cram_Context *context, const char *path);
CRAMAPI void Cram_Pack(Cram_Context *context);
CRAMAPI int8_t Cram_Pack(Cram_Context *context);
CRAMAPI void Cram_GetPixelData(Cram_Context *context, uint8_t **pPixelData, uint32_t *pWidth, uint32_t *pHeight);
CRAMAPI void Cram_GetAtlasData(Cram_Context *context, Cram_AtlasData **pAtlasData);

View File

@ -26,18 +26,6 @@
#include "cram.h"
#ifdef USE_SDL2
#define Cram_assert SDL_assert
#define Cram_qsort SDL_qsort
#define Cram_malloc SDL_malloc
#define Cram_realloc SDL_realloc
#define Cram_free SDL_free
#define Cram_memcpy SDL_memcpy
#define Cram_strdup SDL_strdup
#else
#ifdef _MSC_VER
#include <assert.h>
@ -47,18 +35,19 @@
#endif /* _MSC_VER */
/* TODO: ifndefs here */
#define Cram_assert assert
#define Cram_qsort qsort
#define Cram_malloc malloc
#define Cram_realloc realloc
#define Cram_free free
#define Cram_memcpy memcpy
#define Cram_memset memset
#define Cram_strdup strdup
#endif /* USE_SDL2 */
#define Cram_max max
#define STBRP_ASSERT Cram_assert
#define STBRP_SORT Cram_sort
#define STBRP_SORT Cram_qsort
#define STBRP_STATIC
#define STB_RECT_PACK_IMPLEMENTATION
@ -69,6 +58,7 @@
#define STBI_REALLOC Cram_realloc
#define STBI_FREE Cram_free
#define STBI_ONLY_PNG
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
@ -76,15 +66,18 @@
/* Structures */
typedef struct Cram_ImageRect
typedef struct Rect
{
int32_t x, y;
int32_t w, h;
} Rect;
typedef struct Cram_Image
{
const char *name;
uint32_t offsetX;
uint32_t offsetY;
uint32_t width;
uint32_t height;
Rect rect;
uint8_t *pixels;
} Cram_ImageRect;
} Cram_Image;
typedef struct Cram_Internal_Context
{
@ -97,9 +90,9 @@ typedef struct Cram_Internal_Context
uint8_t *pixels;
Cram_ImageRect *imageRects;
uint32_t imageRectCount;
uint32_t imageRectCapacity;
Cram_Image *images;
uint32_t imageCount;
uint32_t imageCapacity;
Cram_AtlasData *atlasData;
} Cram_Internal_Context;
@ -123,9 +116,9 @@ Cram_Context* Cram_Init(Cram_ContextCreateInfo *createInfo)
context->padding = createInfo->padding;
context->trim = createInfo->trim;
context->imageRects = Cram_malloc(INITIAL_DATA_CAPACITY * sizeof(Cram_ImageRect));
context->imageRectCapacity = INITIAL_DATA_CAPACITY;
context->imageRectCount = 0;
context->images = Cram_malloc(INITIAL_DATA_CAPACITY * sizeof(Cram_Image));
context->imageCapacity = INITIAL_DATA_CAPACITY;
context->imageCount = 0;
context->atlasData = Cram_malloc(sizeof(Cram_AtlasData));
@ -134,33 +127,91 @@ Cram_Context* Cram_Init(Cram_ContextCreateInfo *createInfo)
return (Cram_Context*) context;
}
static inline uint32_t GetPixelIndex(uint32_t i, uint32_t j, uint32_t width)
static inline uint32_t Cram_Internal_GetPixelIndex(uint32_t x, uint32_t y, uint32_t width)
{
return (i + j * width) * 4;
return x + y * width;
}
/* 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;
}
static uint8_t Cram_Internal_IsRowClear(uint32_t* pixels, uint32_t rowIndex, uint32_t width)
{
int32_t i;
for (i = 0; i < width; i += 1)
{
if ((pixels[Cram_Internal_GetPixelIndex(i, rowIndex, width)] & 0xFF) > 0)
{
return 0;
}
}
return 1;
}
static uint8_t Cram_Internal_IsColumnClear(uint32_t* pixels, uint32_t columnIndex, uint32_t width, uint32_t height)
{
int32_t i;
for (i = 0; i < height; i += 1)
{
if ((pixels[Cram_Internal_GetPixelIndex(columnIndex, i, width)] & 0xFF) > 0)
{
return 0;
}
}
return 1;
}
void Cram_AddFile(Cram_Context *context, const char *path)
{
Cram_Internal_Context *internalContext = (Cram_Internal_Context*) context;
Cram_ImageRect *imageRect;
Cram_Image *image;
uint8_t *pixels;
int32_t leftTrim, topTrim, rightTrim, bottomTrim;
int32_t width, height, numChannels;
uint32_t pixelIndex, pixelOffset;
uint8_t allClear;
int32_t i;
if (internalContext->imageRectCapacity == internalContext->imageRectCount)
if (internalContext->imageCapacity == internalContext->imageCount)
{
internalContext->imageRectCapacity *= 2;
internalContext->imageRects = Cram_realloc(internalContext->imageRects, internalContext->imageRectCapacity * sizeof(Cram_ImageRect));
internalContext->imageCapacity *= 2;
internalContext->images = Cram_realloc(internalContext->images, internalContext->imageCapacity * sizeof(Cram_Image));
}
imageRect = &internalContext->imageRects[internalContext->imageRectCount];
image = &internalContext->images[internalContext->imageCount];
imageRect->name = Cram_Internal_GetTrimmedPath();
/* image->name = Cram_Internal_GetTrimmedPath(); */
pixels = stbi_load_from_file(
pixels = stbi_load(
path,
&width,
&height,
@ -168,124 +219,152 @@ void Cram_AddFile(Cram_Context *context, const char *path)
STBI_rgb_alpha
);
/* Check for trim */
if (internalContext->trim)
{
/* Check for trim */
allClear = 1;
topTrim = -1;
while (allClear)
topTrim = 0;
for (i = 0; i < height; i += 1)
{
topTrim += 1;
if (!Cram_Internal_IsRowClear(pixels, i, width))
{
topTrim = i;
break;
}
}
leftTrim = 0;
for (i = 0; i < width; i += 1)
{
pixelIndex = GetPixelIndex(i, topTrim, width);
/* alpha check */
if (pixels[pixelIndex + 3] > 0)
if (!Cram_Internal_IsColumnClear(pixels, i, width, height))
{
allClear = 0;
leftTrim = i;
break;
}
}
}
allClear = 1;
leftTrim = -1;
while (allClear)
{
leftTrim += 1;
for (i = topTrim; i < height; i += 1)
{
pixelIndex = GetPixelIndex(leftTrim, i, width);
if (pixels[pixelIndex + 3] > 0)
{
allClear = 0;
break;
}
}
}
allClear = 1;
bottomTrim = -1;
while (allClear)
{
bottomTrim += 1;
for (i = width - 1; i >= leftTrim; i -= 1)
{
pixelIndex = GetPixelIndex(i, bottomTrim, width);
if (pixels[pixelIndex + 3] > 0)
{
allClear = 0;
break;
}
}
}
allClear = 1;
rightTrim = -1;
while (allClear)
{
rightTrim += 1;
bottomTrim = height;
for (i = height - 1; i >= topTrim; i -= 1)
{
pixelIndex = GetPixelIndex(rightTrim, i, width);
if (pixels[pixelIndex + 3] > 0)
if (!Cram_Internal_IsRowClear(pixels, i, width))
{
allClear = 0;
bottomTrim = i;
break;
}
}
rightTrim = width;
for (i = width - 1; i >= leftTrim; i -= 1)
{
if (!Cram_Internal_IsColumnClear(pixels, i, width, height))
{
rightTrim = i;
break;
}
}
imageRect->offsetX = leftTrim;
imageRect->offsetY = topTrim;
imageRect->width = width - rightTrim;
imageRect->height = height - bottomTrim;
image->rect.x = leftTrim;
image->rect.y = topTrim;
image->rect.w = rightTrim - leftTrim;
image->rect.h = bottomTrim - topTrim;
}
else
{
imageRect->offsetX = 0;
imageRect->offsetY = 0;
imageRect->width = width;
imageRect->height = height;
image->rect.x = 0;
image->rect.y = 0;
image->rect.w = width;
image->rect.h = height;
}
/* copy */
imageRect->pixels = Cram_malloc(imageRect->width * imageRect->height * 4);
pixelOffset = GetPixelIndex(imageRect->offsetX, imageRect->offsetY, width);
Cram_memcpy(imageRect->pixels, pixels + pixelOffset, (width * height * 4) - pixelOffset);
/* copy */
image->pixels = Cram_malloc(image->rect.w * image->rect.h * 4);
Rect dstRect;
dstRect.x = 0;
dstRect.y = 0;
dstRect.w = image->rect.w;
dstRect.h = image->rect.h;
Cram_Internal_CopyPixels((uint32_t*) image->pixels, image->rect.w, (uint32_t*) pixels, width, &dstRect, &image->rect);
stbi_image_free(pixels);
internalContext->imageRectCount += 1;
internalContext->imageCount += 1;
}
void Cram_AddFolder(Cram_Context *context, const char *path)
{
}
void Cram_Pack(Cram_Context *context)
int8_t Cram_Pack(Cram_Context *context)
{
stbrp_context rectPackContext;
Cram_Internal_Context *internalContext = (Cram_Internal_Context*) context;
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 *rect;
Rect dstRect, srcRect;
int32_t i;
uint32_t maxWidth = 0;
uint32_t maxHeight = 0;
stbrp_init_target(&rectPackContext, internalContext->width, internalContext->height, nodes, numNodes);
/* TODO: pack rects */
for (i = 0; i < internalContext->imageCount; i += 1)
{
rect = &rects[i];
rect->w = internalContext->images[i].rect.w + internalContext->padding;
rect->h = internalContext->images[i].rect.h + internalContext->padding;
}
stbrp_pack_rects(&rectPackContext, rects, internalContext->imageCount);
for (i = 0; i < internalContext->imageCount; i += 1)
{
rect = &rects[i];
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);
}
else
{
return -1;
}
}
internalContext->width = maxWidth;
internalContext->height = maxHeight;
internalContext->pixels = Cram_realloc(internalContext->pixels, internalContext->width * internalContext->height * 4);
Cram_memset(internalContext->pixels, 0, internalContext->width * internalContext->height * 4);
for (i = 0; i < internalContext->imageCount; i += 1)
{
dstRect.x = internalContext->images[i].rect.x;
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.y = 0;
srcRect.w = internalContext->images[i].rect.w;
srcRect.h = internalContext->images[i].rect.h;
Cram_Internal_CopyPixels(
(uint32_t*) internalContext->pixels,
internalContext->width,
(uint32_t*) internalContext->images[i].pixels,
internalContext->images[i].rect.w,
&dstRect,
&srcRect
);
}
Cram_free(nodes);
Cram_free(rects);
return 0;
}
void Cram_GetPixelData(Cram_Context *context, uint8_t **pPixels, uint32_t *pWidth, uint32_t *pHeight)
@ -310,7 +389,7 @@ void Cram_Destroy(Cram_Context *context)
Cram_free(internalContext);
}
Cram_free(internalContext->imageRects);
Cram_free(internalContext->images);
Cram_free(internalContext->atlasData);
Cram_free(internalContext);
}

1212
tools/cli/dirent.h Normal file

File diff suppressed because it is too large Load Diff

115
tools/cli/main.c Normal file
View File

@ -0,0 +1,115 @@
#include "cram.h"
#include "dirent.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
#define MAX_DIR_LENGTH 2048
static Cram_Context *context;
#ifdef _WIN32
#define SEPARATOR "\\"
#endif
#ifdef __unix__
#define SEPARATOR "/"
#endif
static const char* GetFilenameExtension(const char *filename)
{
const char *dot = strrchr(filename, '.');
if (!dot || dot == filename)
{
return "";
}
return dot + 1;
}
/* Mostly taken from K&R C 2nd edition page 182 */
static void dirwalk(char *dir)
{
dirent *dp;
DIR *dfd;
char subname[2048];
if ((dfd = opendir(dir)) == NULL)
{
fprintf(stderr, "Can't open %s\n", dir);
return;
}
while ((dp = readdir(dfd)) != NULL)
{
if ( strcmp(dp->d_name, ".") == 0 ||
strcmp(dp->d_name, "..") == 0
)
{
continue;
}
sprintf(subname, "%s%s%s", dir, SEPARATOR, dp->d_name);
if (dp->d_type == DT_DIR)
{
dirwalk(subname);
}
else
{
if (strcmp(GetFilenameExtension(subname), "png") == 0)
{
Cram_AddFile(context, subname);
}
else
{
fprintf(stdout, "skipping %s\n", subname);
}
}
}
closedir(dfd);
}
int main(int argc, char *argv[])
{
Cram_ContextCreateInfo createInfo;
uint8_t *pixelData;
uint32_t width;
uint32_t height;
if (argc < 2)
{
fprintf(stderr, "Must provide directory!\n");
return 1;
}
createInfo.padding = 0;
createInfo.trim = 1;
createInfo.maxDimension = 8192;
createInfo.name = "test";
context = Cram_Init(&createInfo);
dirwalk(argv[1]);
Cram_Pack(context);
Cram_GetPixelData(context, &pixelData, &width, &height);
stbi_write_png(
"output.png",
width,
height,
4,
pixelData,
width * 4
);
Cram_Destroy(context);
return 0;
}

1724
tools/cli/stb_image_write.h Normal file

File diff suppressed because it is too large Load Diff