258 lines
8.6 KiB
C
258 lines
8.6 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdbool.h>
|
|
#include <SDL2/SDL.h>
|
|
#include <SDL2/SDL_events.h>
|
|
#include <SDL2/SDL_image.h>
|
|
#include <SDL2/SDL_render.h>
|
|
|
|
#define STB_IMAGE_IMPLEMENTATION
|
|
#define STBI_FAILURE_USERMSG
|
|
#include "stb_image.h"
|
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
|
#include "stb_image_write.h"
|
|
|
|
typedef struct image_t {
|
|
char* filename;
|
|
unsigned char* image_data;
|
|
int id, width, height, channels;
|
|
} image_t;
|
|
|
|
typedef struct tex_rect_t {
|
|
image_t* img;
|
|
int x,y,w,h;
|
|
} tex_rect_t;
|
|
|
|
image_t* read_image_infos(int argc, char* argv[], int* image_count) {
|
|
image_t* images = calloc(argc - 1, sizeof(image_t));
|
|
if (images == NULL) {
|
|
fprintf(stderr, "Failed to alloc image data array");
|
|
exit(-1);
|
|
}
|
|
*image_count = argc - 1;
|
|
for (int i = 0; i < *image_count; i++) {
|
|
image_t* 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 images;
|
|
}
|
|
|
|
int pack_textures(image_t images[],
|
|
tex_rect_t tex_rects[],
|
|
int image_count,
|
|
int* size,
|
|
int* totalh,
|
|
int* maxw) {
|
|
if (tex_rects == NULL) { return -1; }
|
|
*totalh = 0, *maxw = 0;
|
|
for (int i = 0; i < image_count; i++) {
|
|
tex_rects[i] = (tex_rect_t){
|
|
&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;
|
|
*size = (int)pow(2, exp);
|
|
return max;
|
|
}
|
|
|
|
int load_texture(image_t* img) {
|
|
int w,h,c;
|
|
img->image_data = (unsigned char*)stbi_load(img->filename, &w, &h, &c, 4);
|
|
if (img->image_data == 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 write_to_atlas(image_t images[],
|
|
int image_count,
|
|
tex_rect_t tex_rects[],
|
|
unsigned char* buf,
|
|
int maxw)
|
|
{
|
|
int channels = images[0].channels;
|
|
for (int i = 0; i < image_count; i++) {
|
|
tex_rect_t 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++) {
|
|
if (images[i].image_data[img_idx + 3] != 0) {
|
|
buf[buf_idx + j] = images[i].image_data[img_idx + j];
|
|
} else {
|
|
buf[buf_idx + 3] = 0xFF;
|
|
}
|
|
}
|
|
img_col++; buf_col++;
|
|
if (img_col >= rect.w) {
|
|
img_col = buf_col = 0;
|
|
img_row++; buf_row++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int sdl_test(image_t* images, int image_count, tex_rect_t* 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++) {
|
|
tex_rect_t rect = tex_rects[i];
|
|
unsigned char* img = images[i].image_data;
|
|
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);
|
|
}
|
|
// unsigned char converted[512 * 512 * 4];
|
|
|
|
// for (int i = 0; i < 512 * 512; i++) {
|
|
// converted[i*4+0] = (pixels[i] >> 24) & 0xff;
|
|
// converted[i*4+1] = (pixels[i] >> 16) & 0xff;
|
|
// converted[i*4+2] = (pixels[i] >> 8 ) & 0xff;
|
|
// converted[i*4+3] = (pixels[i] ) & 0xff;
|
|
// }
|
|
|
|
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);
|
|
}
|
|
|
|
int image_count;
|
|
//-------------------------------------
|
|
// First get the image info, make sure the file is an image and it exists
|
|
//-------------------------------------
|
|
image_t* images = read_image_infos(argc, argv, &image_count);
|
|
|
|
//-------------------------------------
|
|
// Now run the packing algorithm. This is super inefficient
|
|
// texture packing but I just want to get it working first
|
|
//-------------------------------------
|
|
tex_rect_t* tex_rects = calloc(image_count, sizeof(tex_rect_t));
|
|
int size, totalh, maxw;
|
|
int max = pack_textures(images, tex_rects, image_count, &size, &totalh, &maxw);
|
|
|
|
//-------------------------------------
|
|
// 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 < image_count; i++) {
|
|
int result = load_texture(&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));
|
|
// write_to_atlas(images, image_count, tex_rects, buf, maxw);
|
|
|
|
// stbi_write_png("test.png", max, max, 4, buf, maxw * 4);
|
|
|
|
sdl_test(images, image_count, tex_rects);
|
|
|
|
//-------------------------------------
|
|
// Free them all, but eventually we would free after we finished writing them individually
|
|
//-------------------------------------
|
|
for (int i = 0; i < image_count; i++) {
|
|
stbi_image_free(images[i].image_data);
|
|
}
|
|
free(images);
|
|
free(tex_rects);
|
|
return 0;
|
|
}
|