#include #include #include #include #include #include #include #include #define STB_IMAGE_IMPLEMENTATION #define STBI_FAILURE_USERMSG #include "stb_image.h" #define STB_IMAGE_WRITE_IMPLEMENTATION #include "stb_image_write.h" // #include "../libs/libcyaml/include/cyaml/cyaml.h" typedef struct Image { char* filename; unsigned char* imageData; int id, width, height, channels; } Image; typedef struct TexRect { Image* img; int x,y,w,h; } TexRect; typedef struct { Image* images; int imageCount; } ParseImages; ParseImages parseImages(int argc, char* argv[]) { Image* images = calloc(argc - 1, sizeof(Image)); if (images == NULL) { fprintf(stderr, "Failed to alloc image data array"); exit(-1); } int imageCount = argc - 1; for (int i = 0; i < imageCount; i++) { Image* img = &images[i]; img->filename = argv[i+1]; int result = stbi_info(img->filename, &img->width, &img->height, &img->channels); if (result != 1) { char *errorMsg = "Cannot load image '%s': %s\n"; fprintf(stderr, errorMsg, img->filename, stbi_failure_reason()); exit(-1); } } return (ParseImages) { images , imageCount }; } typedef struct { int size; int totalHeight; int maxWidth; } PackTextures; PackTextures packTextures(Image images[], TexRect texRects[], int imageCount) { int totalh = 0, maxw = 0; for (int i = 0; i < imageCount; i++) { texRects[i] = (TexRect){ &images[i], 0, // Put them all on the 1st column for now totalh, images[i].width, images[i].height, }; totalh += images[i].height; if (images[i].width > maxw) { maxw = images[i].width; } } int max = totalh > maxw ? totalh : maxw; int exp = (int)log2(max) + 1; int size = (int)pow(2, exp); return (PackTextures) { size , totalh , maxw }; } int loadTexture(Image* img) { int w,h,c; img->imageData = (unsigned char*)stbi_load(img->filename, &w, &h, &c, 4); if (img->imageData == NULL) { char *errorMsg = "Could not load data for image '%s': %s"; fprintf(stderr, errorMsg, img->filename, stbi_failure_reason()); // QUESTION: Should we clean up here? Return -1 instead so parent can clean up exit(-1); } return 0; } void writeToAtlas(Image images[], int image_count, TexRect tex_rects[], unsigned char* buf, int maxw) { int channels = images[0].channels; for (int i = 0; i < image_count; i++) { TexRect rect = tex_rects[i]; int buf_row = rect.y; int buf_col = rect.x; int img_row = 0; int img_col = 0; while (img_row < rect.h) { int buf_idx = (buf_row * maxw + buf_col) * channels; int img_idx = (img_row * rect.w + img_col) * channels; for (int j = 0; j < channels; j++) { buf[buf_idx + j] = images[i].imageData[img_idx + j]; } img_col++; buf_col++; if (img_col >= rect.w) { img_col = buf_col = 0; img_row++; buf_row++; } } } } int sdlTest(Image* images, int image_count, TexRect* tex_rects) { SDL_Window* window = NULL; if (SDL_Init(SDL_INIT_EVERYTHING) < 0) { printf("error initializing SDL: %s\n", SDL_GetError()); return 1; } window = SDL_CreateWindow("SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1024, 1024, SDL_WINDOW_SHOWN ); if (window == NULL) { printf( "Window could not be created! SDL_Error: %s\n", SDL_GetError() ); return 1; } SDL_Renderer* renderer = SDL_CreateRenderer(window, 0, SDL_RENDERER_ACCELERATED); SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); SDL_Texture* texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, 512, 512); SDL_Event e; bool quit = false; unsigned int* pixels = calloc(512 * 512, sizeof(unsigned int)); int img_row = 0; int img_col = 0; int buf_row = 0; int channels = images[0].channels; int i = 0; while (quit == false) { while (SDL_PollEvent( &e)) { if (e.type == SDL_QUIT) { quit = true; } } // for (int i = 0; i < image_count; i++) { SDL_RenderClear(renderer); if (i < image_count) { for (int s = 0; s < 8; s++) { TexRect rect = tex_rects[i]; unsigned char* img = images[i].imageData; int img_idx = (img_row * rect.w + img_col) * channels; int color = ((unsigned int)img[img_idx + 0]) << 24 | ((unsigned int)img[img_idx + 1]) << 16 | ((unsigned int)img[img_idx + 2]) << 8 | ((unsigned int)img[img_idx + 3]); pixels[buf_row * 512 + img_col] = color; img_col++; if (img_col >= rect.w) { img_col = 0; img_row++; buf_row++; } if (img_row >= rect.h) { i++; img_row = 0; } } } SDL_UpdateTexture(texture, NULL, pixels, 512 * sizeof(unsigned int)); SDL_SetTextureBlendMode( texture, SDL_BLENDMODE_BLEND ); SDL_RenderCopy(renderer, texture, NULL, NULL); SDL_RenderPresent(renderer); } SDL_DestroyTexture(texture); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); free(pixels); return 0; } int main(int argc, char* argv[]) { if (argc == 1) { fprintf(stderr, "Must provide image files to pack"); exit(-1); } if (argc == 2) { fprintf(stderr, "Please provide at least 2 images to pack"); exit(-1); } //------------------------------------- // First get the image info, make sure the file is an image and it exists //------------------------------------- ParseImages pi = parseImages(argc, argv); Image* images = pi.images; int imageCount = pi.imageCount; //------------------------------------- // Now run the packing algorithm. This is super inefficient // texture packing but I just want to get it working first //------------------------------------- TexRect* texRects = calloc(imageCount, sizeof(TexRect)); // int max = pack_textures(images, tex_rects, image_count, &size, &totalh, &maxw); PackTextures pt = packTextures(images, texRects, imageCount); int size = pt.size; //------------------------------------- // Now load the data into memory, for now, let's just load all images in one go, // however, it would be more optimized if we loaded each image sequentially or // better yet in parallel so that we can write to the different parts of the // texture atlas concurrently //------------------------------------- for (int i = 0; i < imageCount; i++) { int result = loadTexture(&images[i]); if (result != 0) { // It's going to exit() inside the load_texture function for now printf("Something went wrong\n"); } } //------------------------------------- // For each texture rect, draw the corresponding pixels on the atlas //------------------------------------- unsigned char* buf = calloc(size*size*4, sizeof(char)); writeToAtlas(images, imageCount, texRects, buf, size); stbi_write_png("test.png", size, size, 4, buf, size * 4); // sdlTest(images, imageCount, texRects); //------------------------------------- // Free them all, but eventually we would free after we finished writing them individually //------------------------------------- for (int i = 0; i < imageCount; i++) { stbi_image_free(images[i].imageData); } free(buf); free(images); free(texRects); return 0; }