756 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			756 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|  | #pragma once
 | ||
|  | 
 | ||
|  | #include <kinc/global.h>
 | ||
|  | 
 | ||
|  | #include <stdbool.h>
 | ||
|  | #include <stddef.h>
 | ||
|  | #include <stdint.h>
 | ||
|  | 
 | ||
|  | /*! \file image.h
 | ||
|  |     \brief Functionality for creating and loading images. Image loading supports PNG, JPEG and the custom K format. K files can contain image data that uses | ||
|  |    texture-compression (see kinc_image_compression). | ||
|  | */ | ||
|  | 
 | ||
|  | #ifdef __cplusplus
 | ||
|  | extern "C" { | ||
|  | #endif
 | ||
|  | 
 | ||
|  | typedef enum kinc_image_compression { | ||
|  | 	KINC_IMAGE_COMPRESSION_NONE, | ||
|  | 	KINC_IMAGE_COMPRESSION_DXT5, | ||
|  | 	KINC_IMAGE_COMPRESSION_ASTC, | ||
|  | 	KINC_IMAGE_COMPRESSION_PVRTC | ||
|  | } kinc_image_compression_t; | ||
|  | 
 | ||
|  | typedef enum kinc_image_format { | ||
|  | 	KINC_IMAGE_FORMAT_RGBA32, | ||
|  | 	KINC_IMAGE_FORMAT_GREY8, | ||
|  | 	KINC_IMAGE_FORMAT_RGB24, | ||
|  | 	KINC_IMAGE_FORMAT_RGBA128, | ||
|  | 	KINC_IMAGE_FORMAT_RGBA64, | ||
|  | 	KINC_IMAGE_FORMAT_A32, | ||
|  | 	KINC_IMAGE_FORMAT_BGRA32, | ||
|  | 	KINC_IMAGE_FORMAT_A16 | ||
|  | } kinc_image_format_t; | ||
|  | 
 | ||
|  | typedef struct kinc_image { | ||
|  | 	int width, height, depth; | ||
|  | 	kinc_image_format_t format; | ||
|  | 	unsigned internal_format; | ||
|  | 	kinc_image_compression_t compression; | ||
|  | 	void *data; | ||
|  | 	size_t data_size; | ||
|  | } kinc_image_t; | ||
|  | 
 | ||
|  | typedef struct kinc_image_read_callbacks { | ||
|  | 	size_t (*read)(void *user_data, void *data, size_t size); | ||
|  | 	void (*seek)(void *user_data, size_t pos); | ||
|  | 	size_t (*pos)(void *user_data); | ||
|  | 	size_t (*size)(void *user_data); | ||
|  | } kinc_image_read_callbacks_t; | ||
|  | 
 | ||
|  | /// <summary>
 | ||
|  | /// Creates a 2D kinc_image in the provided memory.
 | ||
|  | /// </summary>
 | ||
|  | /// <returns>The size that's occupied by the image in memory in bytes</returns>
 | ||
|  | KINC_FUNC size_t kinc_image_init(kinc_image_t *image, void *memory, int width, int height, kinc_image_format_t format); | ||
|  | 
 | ||
|  | /// <summary>
 | ||
|  | /// Creates a 3D kinc_image in the provided memory.
 | ||
|  | /// </summary>
 | ||
|  | /// <returns>The size that's occupied by the image in memory in bytes</returns>
 | ||
|  | KINC_FUNC size_t kinc_image_init3d(kinc_image_t *image, void *memory, int width, int height, int depth, kinc_image_format_t format); | ||
|  | 
 | ||
|  | /// <summary>
 | ||
|  | /// Peeks into an image file and figures out the size it will occupy in memory.
 | ||
|  | /// </summary>
 | ||
|  | /// <returns>The memory size in bytes that will be used when loading the image</returns>
 | ||
|  | KINC_FUNC size_t kinc_image_size_from_file(const char *filename); | ||
|  | 
 | ||
|  | /// <summary>
 | ||
|  | /// Peeks into an image that is loaded via callback functions and figures out the size it will occupy in memory.
 | ||
|  | /// </summary>
 | ||
|  | /// <returns>The memory size in bytes that will be used when loading the image</returns>
 | ||
|  | KINC_FUNC size_t kinc_image_size_from_callbacks(kinc_image_read_callbacks_t callbacks, void *user_data, const char *format); | ||
|  | 
 | ||
|  | /// <summary>
 | ||
|  | /// Peeks into an image file that resides in memory and figures out the size it will occupy in memory once it is uncompressed.
 | ||
|  | /// </summary>
 | ||
|  | /// <param name="data">The encoded data</param>
 | ||
|  | /// <param name="data_size">The size of the encoded data</param>
 | ||
|  | /// <param name="format_hint">Something like "png" can help, it also works to just put in the filename</param>
 | ||
|  | /// <returns>The memory size in bytes that will be used when loading the image</returns>
 | ||
|  | KINC_FUNC size_t kinc_image_size_from_encoded_bytes(void *data, size_t data_size, const char *format_hint); | ||
|  | 
 | ||
|  | /// <summary>
 | ||
|  | /// Loads an image from a file.
 | ||
|  | /// </summary>
 | ||
|  | /// <returns>The memory size in bytes that will be used when loading the image</returns>
 | ||
|  | KINC_FUNC size_t kinc_image_init_from_file(kinc_image_t *image, void *memory, const char *filename); | ||
|  | 
 | ||
|  | /// <summary>
 | ||
|  | /// Loads an image file using callbacks.
 | ||
|  | /// </summary>
 | ||
|  | /// <returns>The memory size in bytes that will be used when loading the image</returns>
 | ||
|  | KINC_FUNC size_t kinc_image_init_from_callbacks(kinc_image_t *image, void *memory, kinc_image_read_callbacks_t callbacks, void *user_data, const char *format); | ||
|  | 
 | ||
|  | /// <summary>
 | ||
|  | /// Loads an image file from a memory.
 | ||
|  | /// </summary>
 | ||
|  | /// <returns>The memory size in bytes that will be used when loading the image</returns>
 | ||
|  | KINC_FUNC size_t kinc_image_init_from_encoded_bytes(kinc_image_t *image, void *memory, void *data, size_t data_size, const char *format); | ||
|  | 
 | ||
|  | /// <summary>
 | ||
|  | /// Creates a 2D image from memory.
 | ||
|  | /// </summary>
 | ||
|  | KINC_FUNC void kinc_image_init_from_bytes(kinc_image_t *image, void *data, int width, int height, kinc_image_format_t format); | ||
|  | 
 | ||
|  | /// <summary>
 | ||
|  | /// Creates a 3D image from memory.
 | ||
|  | /// </summary>
 | ||
|  | KINC_FUNC void kinc_image_init_from_bytes3d(kinc_image_t *image, void *data, int width, int height, int depth, kinc_image_format_t format); | ||
|  | 
 | ||
|  | /// <summary>
 | ||
|  | /// Destroys an image. This does not free the user-provided memory.
 | ||
|  | /// </summary>
 | ||
|  | KINC_FUNC void kinc_image_destroy(kinc_image_t *image); | ||
|  | 
 | ||
|  | /// <summary>
 | ||
|  | /// Gets the color value of a 32 bit pixel. If this doesn't fit the format of the image please use kinc_image_at_raw instead.
 | ||
|  | /// </summary>
 | ||
|  | /// <returns>One 32 bit color value</returns>
 | ||
|  | KINC_FUNC uint32_t kinc_image_at(kinc_image_t *image, int x, int y); | ||
|  | 
 | ||
|  | /// <summary>
 | ||
|  | /// Gets a pointer to the color-data of one pixel.
 | ||
|  | /// </summary>
 | ||
|  | /// <returns>A pointer to the color-data of the pixel pointed to by x and y</returns>
 | ||
|  | KINC_FUNC void *kinc_image_at_raw(kinc_image_t *image, int x, int y); | ||
|  | 
 | ||
|  | /// <summary>
 | ||
|  | /// Provides access to the image data.
 | ||
|  | /// </summary>
 | ||
|  | /// <returns>A pointer to the image data</returns>
 | ||
|  | KINC_FUNC uint8_t *kinc_image_get_pixels(kinc_image_t *image); | ||
|  | 
 | ||
|  | /// <summary>
 | ||
|  | /// Gets the size in bytes of a single pixel for a given image format.
 | ||
|  | /// </summary>
 | ||
|  | /// <returns>The size of one pixel in bytes</returns>
 | ||
|  | KINC_FUNC int kinc_image_format_sizeof(kinc_image_format_t format); | ||
|  | 
 | ||
|  | #ifdef KINC_IMPLEMENTATION_ROOT
 | ||
|  | #define KINC_IMPLEMENTATION
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | #ifdef KINC_IMPLEMENTATION
 | ||
|  | 
 | ||
|  | #include "image.h"
 | ||
|  | 
 | ||
|  | #ifdef KINC_LZ4X
 | ||
|  | #include <kinc/libs/lz4x.h>
 | ||
|  | #else
 | ||
|  | #include <kinc/io/lz4/lz4.h>
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | #ifdef KINC_IMPLEMENTATION_ROOT
 | ||
|  | #undef KINC_IMPLEMENTATION
 | ||
|  | #endif
 | ||
|  | #include <kinc/log.h>
 | ||
|  | #ifdef KINC_IMPLEMENTATION_ROOT
 | ||
|  | #define KINC_IMPLEMENTATION
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | #undef KINC_IMPLEMENTATION
 | ||
|  | #include <kinc/graphics4/graphics.h>
 | ||
|  | #include <kinc/io/filereader.h>
 | ||
|  | #include <kinc/math/core.h>
 | ||
|  | #define KINC_IMPLEMENTATION
 | ||
|  | 
 | ||
|  | #include <string.h>
 | ||
|  | 
 | ||
|  | #define BUFFER_SIZE 4096 * 4096 * 4
 | ||
|  | static uint8_t buffer[BUFFER_SIZE]; | ||
|  | static size_t buffer_offset = 0; | ||
|  | static uint8_t *last_allocated_pointer = 0; | ||
|  | 
 | ||
|  | static void *buffer_malloc(size_t size) { | ||
|  | 	uint8_t *current = &buffer[buffer_offset]; | ||
|  | 	buffer_offset += size + sizeof(size_t); | ||
|  | 	if (buffer_offset > BUFFER_SIZE) { | ||
|  | 		kinc_log(KINC_LOG_LEVEL_ERROR, "Not enough memory on image.c Buffer."); | ||
|  | 		return NULL; | ||
|  | 	} | ||
|  | 	memcpy(current, &size, sizeof(*current)); | ||
|  | 	last_allocated_pointer = current + sizeof(size_t); | ||
|  | 	return current + sizeof(size_t); | ||
|  | } | ||
|  | 
 | ||
|  | static void *buffer_realloc(void *p, size_t size) { | ||
|  | 	if (p == NULL) { | ||
|  | 		return buffer_malloc(size); | ||
|  | 	} | ||
|  | 	uint8_t *old_pointer = (uint8_t *)p; | ||
|  | 	size_t old_size = *(size_t *)(old_pointer - sizeof(size_t)); | ||
|  | 	if (size <= old_size) { | ||
|  | 		return old_pointer; | ||
|  | 	} | ||
|  | 	else { | ||
|  | 		if (last_allocated_pointer == old_pointer) { | ||
|  | 			size_t last_size = &buffer[buffer_offset] - old_pointer; | ||
|  | 			size_t size_diff = size - last_size; | ||
|  | 			buffer_offset += size_diff + sizeof(size_t); | ||
|  | 			return old_pointer; | ||
|  | 		} | ||
|  | 		uint8_t *new_pointer = (uint8_t *)buffer_malloc(size); | ||
|  | 		if (new_pointer == NULL) { | ||
|  | 			return NULL; | ||
|  | 		} | ||
|  | 		memcpy(new_pointer, old_pointer, old_size < size ? old_size : size); | ||
|  | 		return new_pointer; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | static void buffer_free(void *p) {} | ||
|  | 
 | ||
|  | #define STBI_MALLOC(sz) buffer_malloc(sz)
 | ||
|  | #define STBI_REALLOC(p, newsz) buffer_realloc(p, newsz)
 | ||
|  | #define STBI_FREE(p) buffer_free(p)
 | ||
|  | 
 | ||
|  | #define STB_IMAGE_IMPLEMENTATION
 | ||
|  | #define STB_IMAGE_STATIC
 | ||
|  | #ifdef __clang__
 | ||
|  | #pragma clang diagnostic ignored "-Wunused-function"
 | ||
|  | #endif
 | ||
|  | #include <kinc/libs/stb_image.h>
 | ||
|  | #include <stdio.h>
 | ||
|  | #include <string.h>
 | ||
|  | 
 | ||
|  | #include <stdint.h>
 | ||
|  | 
 | ||
|  | typedef struct { | ||
|  | 	kinc_image_read_callbacks_t callbacks; | ||
|  | 	void *user_data; | ||
|  | } read_data; | ||
|  | 
 | ||
|  | // fill 'data' with 'size' bytes.  return number of bytes actually read
 | ||
|  | static int stb_read(void *user, char *data, int size) { | ||
|  | 	read_data *reader = (read_data *)user; | ||
|  | 	return (int)reader->callbacks.read(reader->user_data, data, (size_t)size); | ||
|  | } | ||
|  | 
 | ||
|  | // skip the next 'n' bytes, or 'unget' the last -n bytes if negative
 | ||
|  | static void stb_skip(void *user, int n) { | ||
|  | 	read_data *reader = (read_data *)user; | ||
|  | 	reader->callbacks.seek(reader->user_data, reader->callbacks.pos(reader->user_data) + (size_t)n); | ||
|  | } | ||
|  | 
 | ||
|  | // returns nonzero if we are at end of file/data
 | ||
|  | static int stb_eof(void *user) { | ||
|  | 	read_data *reader = (read_data *)user; | ||
|  | 	return reader->callbacks.pos(reader->user_data) == reader->callbacks.size(reader->user_data); | ||
|  | } | ||
|  | 
 | ||
|  | static _Bool endsWith(const char *str, const char *suffix) { | ||
|  | 	if (str == NULL || suffix == NULL) | ||
|  | 		return 0; | ||
|  | 	size_t lenstr = strlen(str); | ||
|  | 	size_t lensuffix = strlen(suffix); | ||
|  | 	if (lensuffix > lenstr) | ||
|  | 		return 0; | ||
|  | 	return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0; | ||
|  | } | ||
|  | 
 | ||
|  | static size_t loadImageSize(kinc_image_read_callbacks_t callbacks, void *user_data, const char *filename) { | ||
|  | 	if (endsWith(filename, "k")) { | ||
|  | 		uint8_t data[4]; | ||
|  | 		callbacks.read(user_data, data, 4); | ||
|  | 		int width = kinc_read_s32le(data); | ||
|  | 		callbacks.read(user_data, data, 4); | ||
|  | 		int height = kinc_read_s32le(data); | ||
|  | 
 | ||
|  | 		char fourcc[5]; | ||
|  | 		callbacks.read(user_data, fourcc, 4); | ||
|  | 		fourcc[4] = 0; | ||
|  | 
 | ||
|  | 		if (strcmp(fourcc, "LZ4 ") == 0) { | ||
|  | 			return width * height * 4; | ||
|  | 		} | ||
|  | 		else if (strcmp(fourcc, "LZ4F") == 0) { | ||
|  | 			return width * height * 16; | ||
|  | 		} | ||
|  | 		else if (strcmp(fourcc, "ASTC") == 0) { | ||
|  | 			return width * height * 4; // just an upper bound
 | ||
|  | 		} | ||
|  | 		else if (strcmp(fourcc, "DXT5") == 0) { | ||
|  | 			return width * height; // just an upper bound
 | ||
|  | 		} | ||
|  | 		else { | ||
|  | 			kinc_log(KINC_LOG_LEVEL_ERROR, "Unknown fourcc in .k file."); | ||
|  | 			return 0; | ||
|  | 		} | ||
|  | 	} | ||
|  | 	else if (endsWith(filename, "pvr")) { | ||
|  | 		uint8_t data[4]; | ||
|  | 		callbacks.read(user_data, data, 4); // version
 | ||
|  | 		callbacks.read(user_data, data, 4); // flags
 | ||
|  | 		callbacks.read(user_data, data, 4); // pixelFormat1
 | ||
|  | 		callbacks.read(user_data, data, 4); // colourSpace
 | ||
|  | 		callbacks.read(user_data, data, 4); // channelType
 | ||
|  | 		callbacks.read(user_data, data, 4); | ||
|  | 		uint32_t hh = kinc_read_u32le(data); | ||
|  | 		callbacks.read(user_data, data, 4); | ||
|  | 		uint32_t ww = kinc_read_u32le(data); | ||
|  | 
 | ||
|  | 		return ww * hh / 2; | ||
|  | 	} | ||
|  | 	else if (endsWith(filename, "hdr")) { | ||
|  | 		stbi_io_callbacks stbi_callbacks; | ||
|  | 		stbi_callbacks.eof = stb_eof; | ||
|  | 		stbi_callbacks.read = stb_read; | ||
|  | 		stbi_callbacks.skip = stb_skip; | ||
|  | 
 | ||
|  | 		read_data reader; | ||
|  | 		reader.callbacks = callbacks; | ||
|  | 		reader.user_data = user_data; | ||
|  | 
 | ||
|  | 		int x, y, comp; | ||
|  | 		stbi_info_from_callbacks(&stbi_callbacks, &reader, &x, &y, &comp); | ||
|  | 		buffer_offset = 0; | ||
|  | 		return x * y * 16; | ||
|  | 	} | ||
|  | 	else { | ||
|  | 		stbi_io_callbacks stbi_callbacks; | ||
|  | 		stbi_callbacks.eof = stb_eof; | ||
|  | 		stbi_callbacks.read = stb_read; | ||
|  | 		stbi_callbacks.skip = stb_skip; | ||
|  | 
 | ||
|  | 		read_data reader; | ||
|  | 		reader.callbacks = callbacks; | ||
|  | 		reader.user_data = user_data; | ||
|  | 
 | ||
|  | 		int x, y, comp; | ||
|  | 		stbi_info_from_callbacks(&stbi_callbacks, &reader, &x, &y, &comp); | ||
|  | 		buffer_offset = 0; | ||
|  | 		return x * y * 4; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | static bool loadImage(kinc_image_read_callbacks_t callbacks, void *user_data, const char *filename, uint8_t *output, size_t *outputSize, int *width, int *height, | ||
|  |                       kinc_image_compression_t *compression, kinc_image_format_t *format, unsigned *internalFormat) { | ||
|  | 	*format = KINC_IMAGE_FORMAT_RGBA32; | ||
|  | 	if (endsWith(filename, "k")) { | ||
|  | 		uint8_t data[4]; | ||
|  | 		callbacks.read(user_data, data, 4); | ||
|  | 		*width = kinc_read_s32le(data); | ||
|  | 		callbacks.read(user_data, data, 4); | ||
|  | 		*height = kinc_read_s32le(data); | ||
|  | 
 | ||
|  | 		char fourcc[5]; | ||
|  | 		callbacks.read(user_data, fourcc, 4); | ||
|  | 		fourcc[4] = 0; | ||
|  | 
 | ||
|  | 		int compressedSize = (int)callbacks.size(user_data) - 12; | ||
|  | 
 | ||
|  | 		if (strcmp(fourcc, "LZ4 ") == 0) { | ||
|  | 			*compression = KINC_IMAGE_COMPRESSION_NONE; | ||
|  | 			*internalFormat = 0; | ||
|  | 			*outputSize = (size_t)(*width * *height * 4); | ||
|  | 			callbacks.read(user_data, buffer, compressedSize); | ||
|  | 			LZ4_decompress_safe((char *)buffer, (char *)output, compressedSize, (int)*outputSize); | ||
|  | 			return true; | ||
|  | 		} | ||
|  | 		else if (strcmp(fourcc, "LZ4F") == 0) { | ||
|  | 			*compression = KINC_IMAGE_COMPRESSION_NONE; | ||
|  | 			*internalFormat = 0; | ||
|  | 			*outputSize = (size_t)(*width * *height * 16); | ||
|  | 			callbacks.read(user_data, buffer, compressedSize); | ||
|  | 			LZ4_decompress_safe((char *)buffer, (char *)output, compressedSize, (int)*outputSize); | ||
|  | 			*format = KINC_IMAGE_FORMAT_RGBA128; | ||
|  | 			return true; | ||
|  | 		} | ||
|  | 		else if (strcmp(fourcc, "ASTC") == 0) { | ||
|  | 			*compression = KINC_IMAGE_COMPRESSION_ASTC; | ||
|  | 			*outputSize = (size_t)(*width * *height * 4); | ||
|  | 			callbacks.read(user_data, buffer, compressedSize); | ||
|  | 			*outputSize = LZ4_decompress_safe((char *)buffer, (char *)output, compressedSize, (int)*outputSize); | ||
|  | 
 | ||
|  | 			uint8_t blockdim_x = 6; | ||
|  | 			uint8_t blockdim_y = 6; | ||
|  | 			*internalFormat = (blockdim_x << 8) + blockdim_y; | ||
|  | 
 | ||
|  | 			return true; | ||
|  | 
 | ||
|  | 			/*int index = 0;
 | ||
|  | 			index += 4; // magic
 | ||
|  | 			u8 blockdim_x = astcdata[index++]; | ||
|  | 			u8 blockdim_y = astcdata[index++]; | ||
|  | 			++index; // blockdim_z
 | ||
|  | 			internalFormat = (blockdim_x << 8) + blockdim_y; | ||
|  | 			u8 xsize[4]; | ||
|  | 			xsize[0] = astcdata[index++]; | ||
|  | 			xsize[1] = astcdata[index++]; | ||
|  | 			xsize[2] = astcdata[index++]; | ||
|  | 			xsize[3] = 0; | ||
|  | 			this->width = *(unsigned*)&xsize[0]; | ||
|  | 			u8 ysize[4]; | ||
|  | 			ysize[0] = astcdata[index++]; | ||
|  | 			ysize[1] = astcdata[index++]; | ||
|  | 			ysize[2] = astcdata[index++]; | ||
|  | 			ysize[3] = 0; | ||
|  | 			this->height = *(unsigned*)&ysize[0]; | ||
|  | 			u8 zsize[3]; | ||
|  | 			zsize[0] = astcdata[index++]; | ||
|  | 			zsize[1] = astcdata[index++]; | ||
|  | 			zsize[2] = astcdata[index++]; | ||
|  | 			u8* all = (u8*)astcdata[index]; | ||
|  | 			dataSize -= 16; | ||
|  | 			this->data = new u8[dataSize]; | ||
|  | 			for (int i = 0; i < dataSize; ++i) { | ||
|  | 			data[i] = all[16 + i]; | ||
|  | 			} | ||
|  | 			free(astcdata);*/ | ||
|  | 		} | ||
|  | 		else if (strcmp(fourcc, "DXT5") == 0) { | ||
|  | 			*compression = KINC_IMAGE_COMPRESSION_DXT5; | ||
|  | 			*outputSize = (size_t)(*width * *height); | ||
|  | 			callbacks.read(user_data, buffer, compressedSize); | ||
|  | 			*outputSize = LZ4_decompress_safe((char *)buffer, (char *)output, compressedSize, (int)*outputSize); | ||
|  | 			*internalFormat = 0; | ||
|  | 			return true; | ||
|  | 		} | ||
|  | 		else { | ||
|  | 			kinc_log(KINC_LOG_LEVEL_ERROR, "Unknown fourcc in .k file."); | ||
|  | 			return false; | ||
|  | 		} | ||
|  | 	} | ||
|  | 	else if (endsWith(filename, "pvr")) { | ||
|  | 		uint8_t data[4]; | ||
|  | 		callbacks.read(user_data, data, 4); // version
 | ||
|  | 		callbacks.read(user_data, data, 4); // flags
 | ||
|  | 		callbacks.read(user_data, data, 4); // pixelFormat1
 | ||
|  | 		callbacks.read(user_data, data, 4); // colourSpace
 | ||
|  | 		callbacks.read(user_data, data, 4); // channelType
 | ||
|  | 		callbacks.read(user_data, data, 4); | ||
|  | 		uint32_t hh = kinc_read_u32le(data); | ||
|  | 		callbacks.read(user_data, data, 4); | ||
|  | 		uint32_t ww = kinc_read_u32le(data); | ||
|  | 		callbacks.read(user_data, data, 4); // depth
 | ||
|  | 		callbacks.read(user_data, data, 4); // numSurfaces
 | ||
|  | 		callbacks.read(user_data, data, 4); // numFaces
 | ||
|  | 		callbacks.read(user_data, data, 4); // mipMapCount
 | ||
|  | 		callbacks.read(user_data, data, 4); | ||
|  | 
 | ||
|  | 		callbacks.read(user_data, data, 4); | ||
|  | 		uint32_t meta1fourcc = kinc_read_u32le(data); | ||
|  | 		callbacks.read(user_data, data, 4); // meta1key
 | ||
|  | 		callbacks.read(user_data, data, 4); // meta1size
 | ||
|  | 		callbacks.read(user_data, data, 4); | ||
|  | 		uint32_t meta1data = kinc_read_u32le(data); | ||
|  | 
 | ||
|  | 		callbacks.read(user_data, data, 4); | ||
|  | 		uint32_t meta2fourcc = kinc_read_u32le(data); | ||
|  | 		callbacks.read(user_data, data, 4); // meta2key
 | ||
|  | 		callbacks.read(user_data, data, 4); // meta2size
 | ||
|  | 		callbacks.read(user_data, data, 4); | ||
|  | 		uint32_t meta2data = kinc_read_u32le(data); | ||
|  | 
 | ||
|  | 		int w = 0; | ||
|  | 		int h = 0; | ||
|  | 
 | ||
|  | 		if (meta1fourcc == 0) | ||
|  | 			w = meta1data; | ||
|  | 		if (meta1fourcc == 1) | ||
|  | 			h = meta1data; | ||
|  | 		if (meta2fourcc == 0) | ||
|  | 			w = meta2data; | ||
|  | 		if (meta2fourcc == 1) | ||
|  | 			h = meta2data; | ||
|  | 
 | ||
|  | 		*width = w; | ||
|  | 		*height = h; | ||
|  | 		*compression = KINC_IMAGE_COMPRESSION_PVRTC; | ||
|  | 		*internalFormat = 0; | ||
|  | 
 | ||
|  | 		*outputSize = (size_t)(ww * hh / 2); | ||
|  | 		callbacks.read(user_data, output, *outputSize); | ||
|  | 		return true; | ||
|  | 	} | ||
|  | 	else if (endsWith(filename, "hdr")) { | ||
|  | 		*compression = KINC_IMAGE_COMPRESSION_NONE; | ||
|  | 		*internalFormat = 0; | ||
|  | 
 | ||
|  | 		stbi_io_callbacks stbi_callbacks; | ||
|  | 		stbi_callbacks.eof = stb_eof; | ||
|  | 		stbi_callbacks.read = stb_read; | ||
|  | 		stbi_callbacks.skip = stb_skip; | ||
|  | 
 | ||
|  | 		read_data reader; | ||
|  | 		reader.callbacks = callbacks; | ||
|  | 		reader.user_data = user_data; | ||
|  | 
 | ||
|  | 		int comp; | ||
|  | 		float *uncompressed = stbi_loadf_from_callbacks(&stbi_callbacks, &reader, width, height, &comp, 4); | ||
|  | 		if (uncompressed == NULL) { | ||
|  | 			kinc_log(KINC_LOG_LEVEL_ERROR, stbi_failure_reason()); | ||
|  | 			return false; | ||
|  | 		} | ||
|  | 		*outputSize = (size_t)(*width * *height * 16); | ||
|  | 		memcpy(output, uncompressed, *outputSize); | ||
|  | 		*format = KINC_IMAGE_FORMAT_RGBA128; | ||
|  | 		buffer_offset = 0; | ||
|  | 		return true; | ||
|  | 	} | ||
|  | 	else { | ||
|  | 		*compression = KINC_IMAGE_COMPRESSION_NONE; | ||
|  | 		*internalFormat = 0; | ||
|  | 
 | ||
|  | 		stbi_io_callbacks stbi_callbacks; | ||
|  | 		stbi_callbacks.eof = stb_eof; | ||
|  | 		stbi_callbacks.read = stb_read; | ||
|  | 		stbi_callbacks.skip = stb_skip; | ||
|  | 
 | ||
|  | 		read_data reader; | ||
|  | 		reader.callbacks = callbacks; | ||
|  | 		reader.user_data = user_data; | ||
|  | 
 | ||
|  | 		int comp; | ||
|  | 		uint8_t *uncompressed = stbi_load_from_callbacks(&stbi_callbacks, &reader, width, height, &comp, 4); | ||
|  | 		if (uncompressed == NULL) { | ||
|  | 			kinc_log(KINC_LOG_LEVEL_ERROR, stbi_failure_reason()); | ||
|  | 			return false; | ||
|  | 		} | ||
|  | 		for (int y = 0; y < *height; ++y) { | ||
|  | 			for (int x = 0; x < *width; ++x) { | ||
|  | 				float r = uncompressed[y * *width * 4 + x * 4 + 0] / 255.0f; | ||
|  | 				float g = uncompressed[y * *width * 4 + x * 4 + 1] / 255.0f; | ||
|  | 				float b = uncompressed[y * *width * 4 + x * 4 + 2] / 255.0f; | ||
|  | 				float a = uncompressed[y * *width * 4 + x * 4 + 3] / 255.0f; | ||
|  | 				r *= a; | ||
|  | 				g *= a; | ||
|  | 				b *= a; | ||
|  | 				output[y * *width * 4 + x * 4 + 0] = (uint8_t)kinc_round(r * 255.0f); | ||
|  | 				output[y * *width * 4 + x * 4 + 1] = (uint8_t)kinc_round(g * 255.0f); | ||
|  | 				output[y * *width * 4 + x * 4 + 2] = (uint8_t)kinc_round(b * 255.0f); | ||
|  | 				output[y * *width * 4 + x * 4 + 3] = (uint8_t)kinc_round(a * 255.0f); | ||
|  | 			} | ||
|  | 		} | ||
|  | 		*outputSize = (size_t)(*width * *height * 4); | ||
|  | 		buffer_offset = 0; | ||
|  | 		return true; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | int kinc_image_format_sizeof(kinc_image_format_t format) { | ||
|  | 	switch (format) { | ||
|  | 	case KINC_IMAGE_FORMAT_RGBA128: | ||
|  | 		return 16; | ||
|  | 	case KINC_IMAGE_FORMAT_RGBA32: | ||
|  | 	case KINC_IMAGE_FORMAT_BGRA32: | ||
|  | 		return 4; | ||
|  | 	case KINC_IMAGE_FORMAT_RGBA64: | ||
|  | 		return 8; | ||
|  | 	case KINC_IMAGE_FORMAT_A32: | ||
|  | 		return 4; | ||
|  | 	case KINC_IMAGE_FORMAT_A16: | ||
|  | 		return 2; | ||
|  | 	case KINC_IMAGE_FORMAT_GREY8: | ||
|  | 		return 1; | ||
|  | 	case KINC_IMAGE_FORMAT_RGB24: | ||
|  | 		return 3; | ||
|  | 	} | ||
|  | 	return -1; | ||
|  | } | ||
|  | 
 | ||
|  | // static bool formatIsFloatingPoint(kinc_image_format_t format) {
 | ||
|  | //	return format == KINC_IMAGE_FORMAT_RGBA128 || format == KINC_IMAGE_FORMAT_RGBA64 || format == KINC_IMAGE_FORMAT_A32 || format == KINC_IMAGE_FORMAT_A16;
 | ||
|  | //}
 | ||
|  | 
 | ||
|  | size_t kinc_image_init(kinc_image_t *image, void *memory, int width, int height, kinc_image_format_t format) { | ||
|  | 	return kinc_image_init3d(image, memory, width, height, 1, format); | ||
|  | } | ||
|  | 
 | ||
|  | size_t kinc_image_init3d(kinc_image_t *image, void *memory, int width, int height, int depth, kinc_image_format_t format) { | ||
|  | 	image->width = width; | ||
|  | 	image->height = height; | ||
|  | 	image->depth = depth; | ||
|  | 	image->format = format; | ||
|  | 	image->compression = KINC_IMAGE_COMPRESSION_NONE; | ||
|  | 	image->data = memory; | ||
|  | 	return width * height * depth * kinc_image_format_sizeof(format); | ||
|  | } | ||
|  | 
 | ||
|  | void kinc_image_init_from_bytes(kinc_image_t *image, void *data, int width, int height, kinc_image_format_t format) { | ||
|  | 	kinc_image_init_from_bytes3d(image, data, width, height, 1, format); | ||
|  | } | ||
|  | 
 | ||
|  | void kinc_image_init_from_bytes3d(kinc_image_t *image, void *data, int width, int height, int depth, kinc_image_format_t format) { | ||
|  | 	image->width = width; | ||
|  | 	image->height = height; | ||
|  | 	image->depth = depth; | ||
|  | 	image->format = format; | ||
|  | 	image->compression = KINC_IMAGE_COMPRESSION_NONE; | ||
|  | 	image->data = data; | ||
|  | } | ||
|  | 
 | ||
|  | static size_t read_callback(void *user_data, void *data, size_t size) { | ||
|  | 	return kinc_file_reader_read((kinc_file_reader_t *)user_data, data, size); | ||
|  | } | ||
|  | 
 | ||
|  | static size_t size_callback(void *user_data) { | ||
|  | 	return kinc_file_reader_size((kinc_file_reader_t *)user_data); | ||
|  | } | ||
|  | 
 | ||
|  | static size_t pos_callback(void *user_data) { | ||
|  | 	return kinc_file_reader_pos((kinc_file_reader_t *)user_data); | ||
|  | } | ||
|  | 
 | ||
|  | static void seek_callback(void *user_data, size_t pos) { | ||
|  | 	kinc_file_reader_seek((kinc_file_reader_t *)user_data, pos); | ||
|  | } | ||
|  | 
 | ||
|  | struct kinc_internal_image_memory { | ||
|  | 	uint8_t *data; | ||
|  | 	size_t size; | ||
|  | 	size_t offset; | ||
|  | }; | ||
|  | 
 | ||
|  | static size_t memory_read_callback(void *user_data, void *data, size_t size) { | ||
|  | 	struct kinc_internal_image_memory *memory = (struct kinc_internal_image_memory *)user_data; | ||
|  | 	size_t read_size = memory->size - memory->offset < size ? memory->size - memory->offset : size; | ||
|  | 	memcpy(data, &memory->data[memory->offset], read_size); | ||
|  | 	memory->offset += read_size; | ||
|  | 	return read_size; | ||
|  | } | ||
|  | 
 | ||
|  | static size_t memory_size_callback(void *user_data) { | ||
|  | 	struct kinc_internal_image_memory *memory = (struct kinc_internal_image_memory *)user_data; | ||
|  | 	return memory->size; | ||
|  | } | ||
|  | 
 | ||
|  | static size_t memory_pos_callback(void *user_data) { | ||
|  | 	struct kinc_internal_image_memory *memory = (struct kinc_internal_image_memory *)user_data; | ||
|  | 	return memory->offset; | ||
|  | } | ||
|  | 
 | ||
|  | static void memory_seek_callback(void *user_data, size_t pos) { | ||
|  | 	struct kinc_internal_image_memory *memory = (struct kinc_internal_image_memory *)user_data; | ||
|  | 	memory->offset = pos; | ||
|  | } | ||
|  | 
 | ||
|  | size_t kinc_image_size_from_callbacks(kinc_image_read_callbacks_t callbacks, void *user_data, const char *filename) { | ||
|  | 	return loadImageSize(callbacks, user_data, filename); | ||
|  | } | ||
|  | 
 | ||
|  | size_t kinc_image_size_from_file(const char *filename) { | ||
|  | 	kinc_file_reader_t reader; | ||
|  | 	if (kinc_file_reader_open(&reader, filename, KINC_FILE_TYPE_ASSET)) { | ||
|  | 		kinc_image_read_callbacks_t callbacks; | ||
|  | 		callbacks.read = read_callback; | ||
|  | 		callbacks.size = size_callback; | ||
|  | 		callbacks.pos = pos_callback; | ||
|  | 		callbacks.seek = seek_callback; | ||
|  | 
 | ||
|  | 		size_t dataSize = loadImageSize(callbacks, &reader, filename); | ||
|  | 		kinc_file_reader_close(&reader); | ||
|  | 		return dataSize; | ||
|  | 	} | ||
|  | 	return 0; | ||
|  | } | ||
|  | 
 | ||
|  | size_t kinc_image_size_from_encoded_bytes(void *data, size_t data_size, const char *format) { | ||
|  | 	kinc_image_read_callbacks_t callbacks; | ||
|  | 	callbacks.read = memory_read_callback; | ||
|  | 	callbacks.size = memory_size_callback; | ||
|  | 	callbacks.pos = memory_pos_callback; | ||
|  | 	callbacks.seek = memory_seek_callback; | ||
|  | 
 | ||
|  | 	struct kinc_internal_image_memory image_memory; | ||
|  | 	image_memory.data = (uint8_t *)data; | ||
|  | 	image_memory.size = data_size; | ||
|  | 	image_memory.offset = 0; | ||
|  | 
 | ||
|  | 	return loadImageSize(callbacks, &image_memory, format); | ||
|  | } | ||
|  | 
 | ||
|  | size_t kinc_image_init_from_callbacks(kinc_image_t *image, void *memory, kinc_image_read_callbacks_t callbacks, void *user_data, const char *filename) { | ||
|  | 	size_t dataSize = 0; | ||
|  | 	loadImage(callbacks, user_data, filename, memory, &dataSize, &image->width, &image->height, &image->compression, &image->format, &image->internal_format); | ||
|  | 	image->data = memory; | ||
|  | 	image->data_size = dataSize; | ||
|  | 	return dataSize; | ||
|  | } | ||
|  | 
 | ||
|  | size_t kinc_image_init_from_file(kinc_image_t *image, void *memory, const char *filename) { | ||
|  | 	kinc_file_reader_t reader; | ||
|  | 	if (kinc_file_reader_open(&reader, filename, KINC_FILE_TYPE_ASSET)) { | ||
|  | 		kinc_image_read_callbacks_t callbacks; | ||
|  | 		callbacks.read = read_callback; | ||
|  | 		callbacks.size = size_callback; | ||
|  | 		callbacks.pos = pos_callback; | ||
|  | 		callbacks.seek = seek_callback; | ||
|  | 
 | ||
|  | 		size_t dataSize = 0; | ||
|  | 		loadImage(callbacks, &reader, filename, memory, &dataSize, &image->width, &image->height, &image->compression, &image->format, &image->internal_format); | ||
|  | 		kinc_file_reader_close(&reader); | ||
|  | 		image->data = memory; | ||
|  | 		image->data_size = dataSize; | ||
|  | 		image->depth = 1; | ||
|  | 		return dataSize; | ||
|  | 	} | ||
|  | 	return 0; | ||
|  | } | ||
|  | 
 | ||
|  | size_t kinc_image_init_from_encoded_bytes(kinc_image_t *image, void *memory, void *data, size_t data_size, const char *format) { | ||
|  | 	kinc_image_read_callbacks_t callbacks; | ||
|  | 	callbacks.read = memory_read_callback; | ||
|  | 	callbacks.size = memory_size_callback; | ||
|  | 	callbacks.pos = memory_pos_callback; | ||
|  | 	callbacks.seek = memory_seek_callback; | ||
|  | 
 | ||
|  | 	struct kinc_internal_image_memory image_memory; | ||
|  | 	image_memory.data = (uint8_t *)data; | ||
|  | 	image_memory.size = data_size; | ||
|  | 	image_memory.offset = 0; | ||
|  | 
 | ||
|  | 	size_t dataSize = 0; | ||
|  | 	loadImage(callbacks, &image_memory, format, memory, &dataSize, &image->width, &image->height, &image->compression, &image->format, &image->internal_format); | ||
|  | 	image->data = memory; | ||
|  | 	image->data_size = dataSize; | ||
|  | 	image->depth = 1; | ||
|  | 	return dataSize; | ||
|  | } | ||
|  | 
 | ||
|  | void kinc_image_destroy(kinc_image_t *image) { | ||
|  | 	// user has to free the data
 | ||
|  | 	image->data = NULL; | ||
|  | } | ||
|  | 
 | ||
|  | uint32_t kinc_image_at(kinc_image_t *image, int x, int y) { | ||
|  | 	if (image->data == NULL) { | ||
|  | 		return 0; | ||
|  | 	} | ||
|  | 	else { | ||
|  | 		return *(uint32_t *)&((uint8_t *)image->data)[image->width * kinc_image_format_sizeof(image->format) * y + x * kinc_image_format_sizeof(image->format)]; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | void *kinc_image_at_raw(kinc_image_t *image, int x, int y) { | ||
|  | 	if (image->data == NULL) { | ||
|  | 		return NULL; | ||
|  | 	} | ||
|  | 	else { | ||
|  | 		return &((uint8_t *)image->data)[image->width * kinc_image_format_sizeof(image->format) * y + x * kinc_image_format_sizeof(image->format)]; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | uint8_t *kinc_image_get_pixels(kinc_image_t *image) { | ||
|  | 	return (uint8_t *)image->data; | ||
|  | } | ||
|  | 
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | #ifdef __cplusplus
 | ||
|  | } | ||
|  | #endif
 |