output image metadata
parent
943253add1
commit
6faceb9d16
|
@ -60,6 +60,7 @@ if(BUILD_CLI)
|
||||||
add_executable(cramcli
|
add_executable(cramcli
|
||||||
tools/cli/lib/dirent.h
|
tools/cli/lib/dirent.h
|
||||||
tools/cli/lib/stb_image_write.h
|
tools/cli/lib/stb_image_write.h
|
||||||
|
tools/cli/json_writer.h
|
||||||
tools/cli/main.c
|
tools/cli/main.c
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -59,6 +59,14 @@
|
||||||
#define Cram_min min
|
#define Cram_min min
|
||||||
#define Cram_max max
|
#define Cram_max max
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define SEPARATOR '\\'
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __unix__
|
||||||
|
#define SEPARATOR '/'
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
|
@ -90,28 +98,19 @@ typedef struct Cram_ContextCreateInfo
|
||||||
|
|
||||||
typedef struct Cram_ImageData
|
typedef struct Cram_ImageData
|
||||||
{
|
{
|
||||||
const char *path;
|
char *name;
|
||||||
|
|
||||||
uint32_t x;
|
int32_t x;
|
||||||
uint32_t y;
|
int32_t y;
|
||||||
uint32_t width;
|
int32_t width;
|
||||||
uint32_t height;
|
int32_t height;
|
||||||
|
|
||||||
uint32_t offsetX;
|
int32_t offsetX;
|
||||||
uint32_t offsetY;
|
int32_t offsetY;
|
||||||
uint32_t trimmedWidth;
|
int32_t untrimmedWidth;
|
||||||
uint32_t trimmedHeight;
|
int32_t untrimmedHeight;
|
||||||
} Cram_ImageData;
|
} Cram_ImageData;
|
||||||
|
|
||||||
typedef struct Cram_AtlasData
|
|
||||||
{
|
|
||||||
const char *name;
|
|
||||||
uint32_t width;
|
|
||||||
uint32_t height;
|
|
||||||
Cram_ImageData *imageDatas;
|
|
||||||
uint32_t imageDataCount;
|
|
||||||
} Cram_AtlasData;
|
|
||||||
|
|
||||||
/* API definition */
|
/* API definition */
|
||||||
|
|
||||||
CRAMAPI Cram_Context* Cram_Init(Cram_ContextCreateInfo *createInfo);
|
CRAMAPI Cram_Context* Cram_Init(Cram_ContextCreateInfo *createInfo);
|
||||||
|
@ -121,7 +120,7 @@ CRAMAPI void Cram_AddFile(Cram_Context *context, const char *path);
|
||||||
CRAMAPI int8_t 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_GetPixelData(Cram_Context *context, uint8_t **pPixelData, uint32_t *pWidth, uint32_t *pHeight);
|
||||||
CRAMAPI void Cram_GetAtlasData(Cram_Context *context, Cram_AtlasData **pAtlasData);
|
CRAMAPI void Cram_GetMetadata(Cram_Context *context, Cram_ImageData **pImage, uint32_t *pImageCount);
|
||||||
|
|
||||||
CRAMAPI void Cram_Destroy(Cram_Context *context);
|
CRAMAPI void Cram_Destroy(Cram_Context *context);
|
||||||
|
|
||||||
|
|
142
src/cram.c
142
src/cram.c
|
@ -53,14 +53,17 @@ typedef struct Rect
|
||||||
int32_t w, h;
|
int32_t w, h;
|
||||||
} Rect;
|
} Rect;
|
||||||
|
|
||||||
typedef struct Cram_Image
|
typedef struct Cram_Image Cram_Image;
|
||||||
|
|
||||||
|
struct Cram_Image
|
||||||
{
|
{
|
||||||
const char *name;
|
char *name;
|
||||||
|
Rect originalRect;
|
||||||
Rect rect;
|
Rect rect;
|
||||||
uint8_t duplicate; /* FIXME: is there a better way to do this? */
|
Cram_Image *duplicateOf;
|
||||||
uint8_t *pixels; /* Will be NULL if duplicate! */
|
uint8_t *pixels; /* Will be NULL if duplicateOf is not NULL! */
|
||||||
size_t hash;
|
size_t hash;
|
||||||
} Cram_Image;
|
};
|
||||||
|
|
||||||
typedef struct Cram_Internal_Context
|
typedef struct Cram_Internal_Context
|
||||||
{
|
{
|
||||||
|
@ -73,11 +76,12 @@ typedef struct Cram_Internal_Context
|
||||||
|
|
||||||
uint8_t *pixels;
|
uint8_t *pixels;
|
||||||
|
|
||||||
Cram_Image *images;
|
Cram_Image **images;
|
||||||
uint32_t imageCount;
|
uint32_t imageCount;
|
||||||
uint32_t imageCapacity;
|
uint32_t imageCapacity;
|
||||||
|
|
||||||
Cram_AtlasData *atlasData;
|
Cram_ImageData *imageDatas;
|
||||||
|
uint32_t imageDataCount;
|
||||||
} Cram_Internal_Context;
|
} Cram_Internal_Context;
|
||||||
|
|
||||||
typedef struct RectPackContext
|
typedef struct RectPackContext
|
||||||
|
@ -486,17 +490,32 @@ Cram_Context* Cram_Init(Cram_ContextCreateInfo *createInfo)
|
||||||
context->padding = createInfo->padding;
|
context->padding = createInfo->padding;
|
||||||
context->trim = createInfo->trim;
|
context->trim = createInfo->trim;
|
||||||
|
|
||||||
context->images = Cram_malloc(INITIAL_DATA_CAPACITY * sizeof(Cram_Image));
|
context->images = Cram_malloc(INITIAL_DATA_CAPACITY * sizeof(Cram_Image*));
|
||||||
context->imageCapacity = INITIAL_DATA_CAPACITY;
|
context->imageCapacity = INITIAL_DATA_CAPACITY;
|
||||||
context->imageCount = 0;
|
context->imageCount = 0;
|
||||||
|
|
||||||
context->atlasData = Cram_malloc(sizeof(Cram_AtlasData));
|
|
||||||
|
|
||||||
context->pixels = NULL;
|
context->pixels = NULL;
|
||||||
|
context->imageDatas = NULL;
|
||||||
|
context->imageDataCount = 0;
|
||||||
|
|
||||||
return (Cram_Context*) context;
|
return (Cram_Context*) context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char* Cram_Internal_GetImageName(const char *path)
|
||||||
|
{
|
||||||
|
char *lastSeparator = strrchr(path, SEPARATOR) + 1;
|
||||||
|
size_t returnBytes = strlen(lastSeparator) + 1;
|
||||||
|
char *name = Cram_malloc(returnBytes);
|
||||||
|
int32_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < returnBytes; i += 1)
|
||||||
|
{
|
||||||
|
name[i] = lastSeparator[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
@ -509,12 +528,13 @@ void Cram_AddFile(Cram_Context *context, const char *path)
|
||||||
if (internalContext->imageCapacity == internalContext->imageCount)
|
if (internalContext->imageCapacity == internalContext->imageCount)
|
||||||
{
|
{
|
||||||
internalContext->imageCapacity *= 2;
|
internalContext->imageCapacity *= 2;
|
||||||
internalContext->images = Cram_realloc(internalContext->images, internalContext->imageCapacity * sizeof(Cram_Image));
|
internalContext->images = Cram_realloc(internalContext->images, internalContext->imageCapacity * sizeof(Cram_Image*));
|
||||||
}
|
}
|
||||||
|
|
||||||
image = &internalContext->images[internalContext->imageCount];
|
image = Cram_malloc(sizeof(Cram_Image));
|
||||||
|
internalContext->images[internalContext->imageCount] = image;
|
||||||
|
|
||||||
/* image->name = Cram_Internal_GetTrimmedPath(); */
|
image->name = Cram_Internal_GetImageName(path);
|
||||||
|
|
||||||
pixels = stbi_load(
|
pixels = stbi_load(
|
||||||
path,
|
path,
|
||||||
|
@ -524,6 +544,11 @@ void Cram_AddFile(Cram_Context *context, const char *path)
|
||||||
STBI_rgb_alpha
|
STBI_rgb_alpha
|
||||||
);
|
);
|
||||||
|
|
||||||
|
image->originalRect.x = 0;
|
||||||
|
image->originalRect.y = 0;
|
||||||
|
image->originalRect.w = width;
|
||||||
|
image->originalRect.h = height;
|
||||||
|
|
||||||
/* Check for trim */
|
/* Check for trim */
|
||||||
if (internalContext->trim)
|
if (internalContext->trim)
|
||||||
{
|
{
|
||||||
|
@ -574,10 +599,7 @@ void Cram_AddFile(Cram_Context *context, const char *path)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
image->rect.x = 0;
|
image->rect = image->originalRect;
|
||||||
image->rect.y = 0;
|
|
||||||
image->rect.w = width;
|
|
||||||
image->rect.h = height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* copy and free source pixels */
|
/* copy and free source pixels */
|
||||||
|
@ -595,15 +617,15 @@ void Cram_AddFile(Cram_Context *context, const char *path)
|
||||||
image->hash = stbds_hash_bytes(image->pixels, image->rect.w * image->rect.h * 4, 0);
|
image->hash = stbds_hash_bytes(image->pixels, image->rect.w * image->rect.h * 4, 0);
|
||||||
|
|
||||||
/* check if this is a duplicate */
|
/* check if this is a duplicate */
|
||||||
image->duplicate = 0;
|
image->duplicateOf = NULL;
|
||||||
for (i = 0; i < internalContext->imageCount; i += 1)
|
for (i = 0; i < internalContext->imageCount; i += 1)
|
||||||
{
|
{
|
||||||
if (!internalContext->images[i].duplicate)
|
if (!internalContext->images[i]->duplicateOf)
|
||||||
{
|
{
|
||||||
if (Cram_Internal_IsImageEqual(image, &internalContext->images[i]))
|
if (Cram_Internal_IsImageEqual(image, internalContext->images[i]))
|
||||||
{
|
{
|
||||||
/* this is duplicate data! */
|
/* this is duplicate data! */
|
||||||
image->duplicate = 1;
|
image->duplicateOf = image;
|
||||||
Cram_free(image->pixels);
|
Cram_free(image->pixels);
|
||||||
image->pixels = NULL;
|
image->pixels = NULL;
|
||||||
break;
|
break;
|
||||||
|
@ -623,15 +645,19 @@ int8_t Cram_Pack(Cram_Context *context)
|
||||||
uint32_t numRects = 0;
|
uint32_t numRects = 0;
|
||||||
Rect *packerRect;
|
Rect *packerRect;
|
||||||
Rect dstRect, srcRect;
|
Rect dstRect, srcRect;
|
||||||
int32_t i;
|
Cram_Image *image;
|
||||||
uint32_t maxWidth = 0;
|
uint32_t maxWidth = 0;
|
||||||
uint32_t maxHeight = 0;
|
uint32_t maxHeight = 0;
|
||||||
|
int32_t i;
|
||||||
|
|
||||||
|
internalContext->imageDataCount = internalContext->imageCount;
|
||||||
|
internalContext->imageDatas = Cram_realloc(internalContext->imageDatas, sizeof(Cram_ImageData) * internalContext->imageDataCount);
|
||||||
|
|
||||||
rectPackContext = Cram_Internal_InitRectPacker(internalContext->width, internalContext->height);
|
rectPackContext = Cram_Internal_InitRectPacker(internalContext->width, internalContext->height);
|
||||||
|
|
||||||
for (i = 0; i < internalContext->imageCount; i += 1)
|
for (i = 0; i < internalContext->imageCount; i += 1)
|
||||||
{
|
{
|
||||||
if (!internalContext->images[i].duplicate)
|
if (!internalContext->images[i]->duplicateOf)
|
||||||
{
|
{
|
||||||
numRects += 1;
|
numRects += 1;
|
||||||
}
|
}
|
||||||
|
@ -642,12 +668,12 @@ int8_t Cram_Pack(Cram_Context *context)
|
||||||
numRects = 0;
|
numRects = 0;
|
||||||
for (i = 0; i < internalContext->imageCount; i += 1)
|
for (i = 0; i < internalContext->imageCount; i += 1)
|
||||||
{
|
{
|
||||||
if (!internalContext->images[i].duplicate)
|
if (!internalContext->images[i]->duplicateOf)
|
||||||
{
|
{
|
||||||
packerRect = &packerRects[numRects];
|
packerRect = &packerRects[numRects];
|
||||||
|
|
||||||
packerRect->w = internalContext->images[i].rect.w + internalContext->padding;
|
packerRect->w = internalContext->images[i]->rect.w + internalContext->padding;
|
||||||
packerRect->h = internalContext->images[i].rect.h + internalContext->padding;
|
packerRect->h = internalContext->images[i]->rect.h + internalContext->padding;
|
||||||
|
|
||||||
numRects += 1;
|
numRects += 1;
|
||||||
}
|
}
|
||||||
|
@ -661,12 +687,12 @@ int8_t Cram_Pack(Cram_Context *context)
|
||||||
numRects = 0;
|
numRects = 0;
|
||||||
for (i = 0; i < internalContext->imageCount; i += 1)
|
for (i = 0; i < internalContext->imageCount; i += 1)
|
||||||
{
|
{
|
||||||
if (!internalContext->images[i].duplicate)
|
if (!internalContext->images[i]->duplicateOf)
|
||||||
{
|
{
|
||||||
packerRect = &packerRects[numRects];
|
packerRect = &packerRects[numRects];
|
||||||
|
|
||||||
internalContext->images[i].rect.x = packerRect->x;
|
internalContext->images[i]->rect.x = packerRect->x;
|
||||||
internalContext->images[i].rect.y = packerRect->y;
|
internalContext->images[i]->rect.y = packerRect->y;
|
||||||
|
|
||||||
maxWidth = Cram_max(maxWidth, packerRect->x + packerRect->w);
|
maxWidth = Cram_max(maxWidth, packerRect->x + packerRect->w);
|
||||||
maxHeight = Cram_max(maxHeight, packerRect->y + packerRect->h);
|
maxHeight = Cram_max(maxHeight, packerRect->y + packerRect->h);
|
||||||
|
@ -683,27 +709,48 @@ int8_t Cram_Pack(Cram_Context *context)
|
||||||
|
|
||||||
for (i = 0; i < internalContext->imageCount; i += 1)
|
for (i = 0; i < internalContext->imageCount; i += 1)
|
||||||
{
|
{
|
||||||
if (!internalContext->images[i].duplicate)
|
if (!internalContext->images[i]->duplicateOf)
|
||||||
{
|
{
|
||||||
dstRect.x = internalContext->images[i].rect.x;
|
dstRect.x = internalContext->images[i]->rect.x;
|
||||||
dstRect.y = internalContext->images[i].rect.y;
|
dstRect.y = internalContext->images[i]->rect.y;
|
||||||
dstRect.w = internalContext->images[i].rect.w;
|
dstRect.w = internalContext->images[i]->rect.w;
|
||||||
dstRect.h = internalContext->images[i].rect.h;
|
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
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (internalContext->images[i]->duplicateOf)
|
||||||
|
{
|
||||||
|
image = internalContext->images[i]->duplicateOf;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
image = internalContext->images[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
internalContext->imageDatas[i].x = image->rect.x;
|
||||||
|
internalContext->imageDatas[i].y = image->rect.y;
|
||||||
|
internalContext->imageDatas[i].width = image->rect.w;
|
||||||
|
internalContext->imageDatas[i].height = image->rect.h;
|
||||||
|
|
||||||
|
internalContext->imageDatas[i].offsetX = image->rect.x - image->originalRect.x;
|
||||||
|
internalContext->imageDatas[i].offsetY = image->rect.y - image->originalRect.y;
|
||||||
|
internalContext->imageDatas[i].untrimmedWidth = image->originalRect.w;
|
||||||
|
internalContext->imageDatas[i].untrimmedHeight = image->originalRect.h;
|
||||||
|
|
||||||
|
internalContext->imageDatas[i].name = strdup(image->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
Cram_free(packerRects);
|
Cram_free(packerRects);
|
||||||
|
@ -719,9 +766,12 @@ void Cram_GetPixelData(Cram_Context *context, uint8_t **pPixels, uint32_t *pWidt
|
||||||
*pHeight = internalContext->height;
|
*pHeight = internalContext->height;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cram_GetAtlasData(Cram_Context *context, Cram_AtlasData **pAtlasData)
|
void Cram_GetMetadata(Cram_Context *context, Cram_ImageData **pImage, uint32_t *pImageCount)
|
||||||
{
|
{
|
||||||
*pAtlasData = ((Cram_Internal_Context*) context)->atlasData;
|
Cram_Internal_Context *internalContext = (Cram_Internal_Context*) context;
|
||||||
|
|
||||||
|
*pImage = internalContext->imageDatas;
|
||||||
|
*pImageCount = internalContext->imageDataCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cram_Destroy(Cram_Context *context)
|
void Cram_Destroy(Cram_Context *context)
|
||||||
|
@ -736,13 +786,17 @@ void Cram_Destroy(Cram_Context *context)
|
||||||
|
|
||||||
for (i = 0; i < internalContext->imageCount; i += 1)
|
for (i = 0; i < internalContext->imageCount; i += 1)
|
||||||
{
|
{
|
||||||
if (!internalContext->images[i].duplicate)
|
if (!internalContext->images[i]->duplicateOf)
|
||||||
{
|
{
|
||||||
Cram_free(internalContext->images[i].pixels);
|
Cram_free(internalContext->images[i]->pixels);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Cram_free(internalContext->images[i]->name);
|
||||||
|
Cram_free(internalContext->images[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Cram_free(internalContext->name);
|
||||||
Cram_free(internalContext->images);
|
Cram_free(internalContext->images);
|
||||||
Cram_free(internalContext->atlasData);
|
Cram_free(internalContext->imageDatas);
|
||||||
Cram_free(internalContext);
|
Cram_free(internalContext);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,156 @@
|
||||||
|
|
||||||
|
#ifndef JSON_WRITER_H
|
||||||
|
#define JSON_WRITER_H
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define INITIAL_JSON_OUTPUT_CAPACITY 2048
|
||||||
|
|
||||||
|
typedef struct JsonBuilder
|
||||||
|
{
|
||||||
|
char *string;
|
||||||
|
size_t index;
|
||||||
|
size_t capacity;
|
||||||
|
size_t indentLevel;
|
||||||
|
} JsonBuilder;
|
||||||
|
|
||||||
|
JsonBuilder* JsonBuilder_Init()
|
||||||
|
{
|
||||||
|
JsonBuilder *builder = malloc(sizeof(JsonBuilder));
|
||||||
|
|
||||||
|
builder->string = malloc(INITIAL_JSON_OUTPUT_CAPACITY);
|
||||||
|
builder->capacity = INITIAL_JSON_OUTPUT_CAPACITY;
|
||||||
|
|
||||||
|
builder->string[0] = '\0';
|
||||||
|
|
||||||
|
strcat(builder->string, "{\n");
|
||||||
|
builder->indentLevel = 1;
|
||||||
|
builder->index = 2;
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonBuilder_Internal_MaybeExpand(JsonBuilder *builder, size_t len)
|
||||||
|
{
|
||||||
|
if (builder->capacity < builder->index + len)
|
||||||
|
{
|
||||||
|
builder->capacity = max(builder->index + len, builder->capacity * 2);
|
||||||
|
builder->string = realloc(builder->string, builder->capacity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonBuilder_Internal_Indent(JsonBuilder *builder)
|
||||||
|
{
|
||||||
|
int32_t i;
|
||||||
|
JsonBuilder_Internal_MaybeExpand(builder, builder->indentLevel);
|
||||||
|
|
||||||
|
for (i = 0; i < builder->indentLevel; i += 1)
|
||||||
|
{
|
||||||
|
strcat(builder->string, "\t");
|
||||||
|
}
|
||||||
|
|
||||||
|
builder->index += builder->indentLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonBuilder_Internal_RemoveTrailingComma(JsonBuilder *builder)
|
||||||
|
{
|
||||||
|
if (builder->string[builder->index - 2] == ',')
|
||||||
|
{
|
||||||
|
builder->index -= 2;
|
||||||
|
builder->index += sprintf(&builder->string[builder->index], "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonBuilder_AppendProperty(JsonBuilder *builder, char *propertyName, char *propertyString, uint8_t isString)
|
||||||
|
{
|
||||||
|
size_t lineLength;
|
||||||
|
|
||||||
|
lineLength = strlen(propertyName) + strlen(propertyString) + 6;
|
||||||
|
if (isString)
|
||||||
|
{
|
||||||
|
lineLength += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonBuilder_Internal_MaybeExpand(builder, lineLength);
|
||||||
|
JsonBuilder_Internal_Indent(builder);
|
||||||
|
|
||||||
|
builder->index += sprintf(&builder->string[builder->index], "\"%s\": ", propertyName);
|
||||||
|
|
||||||
|
if (isString)
|
||||||
|
{
|
||||||
|
builder->index += sprintf(&builder->string[builder->index], "\"%s\",\n", propertyString);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
builder->index += sprintf(&builder->string[builder->index], "%s,\n", propertyString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonBuilder_AppendStringProperty(JsonBuilder *builder, char *propertyName, char *value)
|
||||||
|
{
|
||||||
|
JsonBuilder_AppendProperty(builder, propertyName, value, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonBuilder_AppendIntProperty(JsonBuilder *builder, char *propertyName, int32_t value)
|
||||||
|
{
|
||||||
|
char buffer[65];
|
||||||
|
itoa(value, buffer, 10);
|
||||||
|
JsonBuilder_AppendProperty(builder, propertyName, buffer, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonBuilder_StartObject(JsonBuilder *builder)
|
||||||
|
{
|
||||||
|
JsonBuilder_Internal_Indent(builder);
|
||||||
|
JsonBuilder_Internal_MaybeExpand(builder, 1);
|
||||||
|
builder->index += sprintf(&builder->string[builder->index], "{\n");
|
||||||
|
builder->indentLevel += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonBuilder_EndObject(JsonBuilder *builder)
|
||||||
|
{
|
||||||
|
JsonBuilder_Internal_RemoveTrailingComma(builder);
|
||||||
|
|
||||||
|
builder->indentLevel -= 1;
|
||||||
|
|
||||||
|
JsonBuilder_Internal_Indent(builder);
|
||||||
|
JsonBuilder_Internal_MaybeExpand(builder, 3);
|
||||||
|
builder->index += sprintf(&builder->string[builder->index], "},\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonBuilder_StartArrayProperty(JsonBuilder *builder, char *propertyName)
|
||||||
|
{
|
||||||
|
JsonBuilder_Internal_Indent(builder);
|
||||||
|
JsonBuilder_Internal_MaybeExpand(builder, strlen(propertyName) + 6);
|
||||||
|
builder->index += sprintf(&builder->string[builder->index], "\"%s\": [\n", propertyName);
|
||||||
|
builder->indentLevel += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonBuilder_FinishArrayProperty(JsonBuilder *builder)
|
||||||
|
{
|
||||||
|
JsonBuilder_Internal_RemoveTrailingComma(builder);
|
||||||
|
|
||||||
|
builder->indentLevel -= 1;
|
||||||
|
|
||||||
|
JsonBuilder_Internal_Indent(builder);
|
||||||
|
JsonBuilder_Internal_MaybeExpand(builder, 3);
|
||||||
|
builder->index += sprintf(&builder->string[builder->index], "],\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonBuilder_Finish(JsonBuilder *builder)
|
||||||
|
{
|
||||||
|
builder->indentLevel = 0;
|
||||||
|
|
||||||
|
JsonBuilder_Internal_RemoveTrailingComma(builder);
|
||||||
|
JsonBuilder_Internal_MaybeExpand(builder, 3);
|
||||||
|
|
||||||
|
builder->index += sprintf(&builder->string[builder->index], "}\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonBuilder_Destroy(JsonBuilder *builder)
|
||||||
|
{
|
||||||
|
free(builder->string);
|
||||||
|
free(builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* JSON_WRITER_H */
|
|
@ -1,5 +1,6 @@
|
||||||
#include "cram.h"
|
#include "cram.h"
|
||||||
#include "dirent.h"
|
#include "dirent.h"
|
||||||
|
#include "json_writer.h"
|
||||||
|
|
||||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
#include "stb_image_write.h"
|
#include "stb_image_write.h"
|
||||||
|
@ -8,18 +9,6 @@
|
||||||
|
|
||||||
static Cram_Context *context;
|
static Cram_Context *context;
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
|
|
||||||
#define SEPARATOR "\\"
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __unix__
|
|
||||||
|
|
||||||
#define SEPARATOR "/"
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static const char* GetFilenameExtension(const char *filename)
|
static const char* GetFilenameExtension(const char *filename)
|
||||||
{
|
{
|
||||||
const char *dot = strrchr(filename, '.');
|
const char *dot = strrchr(filename, '.');
|
||||||
|
@ -52,7 +41,7 @@ static void dirwalk(char *dir)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
sprintf(subname, "%s%s%s", dir, SEPARATOR, dp->d_name);
|
sprintf(subname, "%s%c%s", dir, SEPARATOR, dp->d_name);
|
||||||
|
|
||||||
if (dp->d_type == DT_DIR)
|
if (dp->d_type == DT_DIR)
|
||||||
{
|
{
|
||||||
|
@ -102,6 +91,10 @@ int main(int argc, char *argv[])
|
||||||
char *inputDirPath = NULL;
|
char *inputDirPath = NULL;
|
||||||
char *outputDirPath = NULL;
|
char *outputDirPath = NULL;
|
||||||
char *imageOutputFilename;
|
char *imageOutputFilename;
|
||||||
|
char *metadataFilename;
|
||||||
|
JsonBuilder *jsonBuilder;
|
||||||
|
Cram_ImageData *imageDatas;
|
||||||
|
uint32_t imageCount;
|
||||||
int32_t i;
|
int32_t i;
|
||||||
|
|
||||||
/* Set defaults */
|
/* Set defaults */
|
||||||
|
@ -195,8 +188,9 @@ int main(int argc, char *argv[])
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Cram_GetPixelData(context, &pixelData, &width, &height);
|
/* output pixel data */
|
||||||
|
|
||||||
|
Cram_GetPixelData(context, &pixelData, &width, &height);
|
||||||
imageOutputFilename = Cram_malloc(strlen(createInfo.name) + 5);
|
imageOutputFilename = Cram_malloc(strlen(createInfo.name) + 5);
|
||||||
strcpy(imageOutputFilename, createInfo.name);
|
strcpy(imageOutputFilename, createInfo.name);
|
||||||
strcat(imageOutputFilename, ".png");
|
strcat(imageOutputFilename, ".png");
|
||||||
|
@ -210,7 +204,50 @@ int main(int argc, char *argv[])
|
||||||
width * 4
|
width * 4
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/* output json */
|
||||||
|
|
||||||
|
Cram_GetMetadata(context, &imageDatas, &imageCount);
|
||||||
|
|
||||||
|
jsonBuilder = JsonBuilder_Init();
|
||||||
|
JsonBuilder_AppendStringProperty(jsonBuilder, "Name", createInfo.name);
|
||||||
|
JsonBuilder_AppendIntProperty(jsonBuilder, "Width", width);
|
||||||
|
JsonBuilder_AppendIntProperty(jsonBuilder, "Height", height);
|
||||||
|
JsonBuilder_StartArrayProperty(jsonBuilder, "Images");
|
||||||
|
for (i = 0; i < imageCount; i += 1)
|
||||||
|
{
|
||||||
|
JsonBuilder_StartObject(jsonBuilder);
|
||||||
|
JsonBuilder_AppendStringProperty(jsonBuilder, "Name", imageDatas[i].name);
|
||||||
|
JsonBuilder_AppendIntProperty(jsonBuilder, "X", imageDatas[i].x);
|
||||||
|
JsonBuilder_AppendIntProperty(jsonBuilder, "Y", imageDatas[i].y);
|
||||||
|
JsonBuilder_AppendIntProperty(jsonBuilder, "W", imageDatas[i].width);
|
||||||
|
JsonBuilder_AppendIntProperty(jsonBuilder, "H", imageDatas[i].height);
|
||||||
|
JsonBuilder_AppendIntProperty(jsonBuilder, "OffsetX", imageDatas[i].offsetX);
|
||||||
|
JsonBuilder_AppendIntProperty(jsonBuilder, "OffsetY", imageDatas[i].offsetY);
|
||||||
|
JsonBuilder_AppendIntProperty(jsonBuilder, "UntrimmedWidth", imageDatas[i].untrimmedWidth);
|
||||||
|
JsonBuilder_AppendIntProperty(jsonBuilder, "UntrimmedHeight", imageDatas[i].untrimmedHeight);
|
||||||
|
JsonBuilder_EndObject(jsonBuilder);
|
||||||
|
}
|
||||||
|
JsonBuilder_FinishArrayProperty(jsonBuilder);
|
||||||
|
JsonBuilder_Finish(jsonBuilder);
|
||||||
|
|
||||||
|
metadataFilename = Cram_malloc(strlen(createInfo.name) + 6);
|
||||||
|
strcpy(metadataFilename, createInfo.name);
|
||||||
|
strcat(metadataFilename, ".json");
|
||||||
|
|
||||||
|
FILE *jsonOutput = fopen(metadataFilename, "w");
|
||||||
|
if (!jsonOutput)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Could not open JSON file for writing!");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(jsonOutput, jsonBuilder->string);
|
||||||
|
|
||||||
|
JsonBuilder_Destroy(jsonBuilder);
|
||||||
|
fclose(jsonOutput);
|
||||||
|
|
||||||
Cram_free(imageOutputFilename);
|
Cram_free(imageOutputFilename);
|
||||||
|
Cram_free(metadataFilename);
|
||||||
Cram_Destroy(context);
|
Cram_Destroy(context);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Reference in New Issue