1
0
Fork 0
mirror of https://github.com/bjornbytes/lovr.git synced 2024-07-08 23:23:38 +00:00
lovr/src/loaders/texture.c

217 lines
6.4 KiB
C
Raw Normal View History

2016-11-26 07:54:45 +00:00
#include "loaders/texture.h"
2017-07-22 10:42:05 +00:00
#include "math/math.h"
#include "lib/dds.h"
2017-03-11 10:19:33 +00:00
#include "lib/stb/stb_image.h"
2016-11-26 07:54:45 +00:00
#include <stdlib.h>
2017-10-31 08:14:09 +00:00
#include <stdbool.h>
2017-01-22 01:29:20 +00:00
#include <string.h>
2016-11-26 07:54:45 +00:00
2017-07-22 10:11:43 +00:00
const TextureFormat FORMAT_RGB = {
.glInternalFormat = GL_RGB,
.glFormat = GL_RGB,
2017-10-31 08:14:09 +00:00
.compressed = false,
2017-08-27 02:31:03 +00:00
.blockBytes = 3
2017-07-22 10:11:43 +00:00
};
const TextureFormat FORMAT_RGBA = {
.glInternalFormat = GL_RGBA,
.glFormat = GL_RGBA,
2017-10-31 08:14:09 +00:00
.compressed = false,
2017-07-22 10:11:43 +00:00
.blockBytes = 4
2017-06-19 00:28:15 +00:00
};
2017-07-22 10:14:36 +00:00
const TextureFormat FORMAT_DXT1 = {
.glInternalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT,
.glFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT,
2017-10-31 08:14:09 +00:00
.compressed = true,
2017-07-22 10:14:36 +00:00
.blockBytes = 8
};
const TextureFormat FORMAT_DXT3 = {
.glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT,
.glFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT,
2017-10-31 08:14:09 +00:00
.compressed = true,
2017-07-22 10:14:36 +00:00
.blockBytes = 16
};
const TextureFormat FORMAT_DXT5 = {
.glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT,
.glFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT,
2017-10-31 08:14:09 +00:00
.compressed = true,
2017-07-22 10:14:36 +00:00
.blockBytes = 16
};
2017-07-22 10:42:05 +00:00
#define FOUR_CC(a, b, c, d) ((uint32_t) (((d)<<24) | ((c)<<16) | ((b)<<8) | (a)))
2017-08-09 08:57:18 +00:00
// Modified from ddsparse (https://bitbucket.org/slime73/ddsparse)
2017-07-22 10:42:05 +00:00
static int parseDDS(uint8_t* data, size_t size, TextureData* textureData) {
if (size < sizeof(uint32_t) + sizeof(DDSHeader) || *(uint32_t*) data != FOUR_CC('D', 'D', 'S', ' ')) {
return 1;
}
// Header
size_t offset = sizeof(uint32_t);
DDSHeader* header = (DDSHeader*) (data + offset);
offset += sizeof(DDSHeader);
if (header->size != sizeof(DDSHeader) || header->format.size != sizeof(DDSPixelFormat)) {
return 1;
}
// DX10 header
if ((header->format.flags & DDPF_FOURCC) && (header->format.fourCC == FOUR_CC('D', 'X', '1', '0'))) {
if (size < (sizeof(uint32_t) + sizeof(DDSHeader) + sizeof(DDSHeader10))) {
return 1;
}
DDSHeader10* header10 = (DDSHeader10*) (data + offset);
offset += sizeof(DDSHeader10);
// Only accept 2D textures
D3D10ResourceDimension dimension = header10->resourceDimension;
if (dimension != D3D10_RESOURCE_DIMENSION_TEXTURE2D && dimension != D3D10_RESOURCE_DIMENSION_UNKNOWN) {
return 1;
}
// Can't deal with texture arrays and cubemaps.
if (header10->arraySize > 1) {
return 1;
}
// Ensure DXT 1/3/5
switch (header10->dxgiFormat) {
case DXGI_FORMAT_BC1_TYPELESS:
case DXGI_FORMAT_BC1_UNORM:
case DXGI_FORMAT_BC1_UNORM_SRGB:
textureData->format = FORMAT_DXT1;
break;
case DXGI_FORMAT_BC2_TYPELESS:
case DXGI_FORMAT_BC2_UNORM:
case DXGI_FORMAT_BC2_UNORM_SRGB:
textureData->format = FORMAT_DXT3;
break;
case DXGI_FORMAT_BC3_TYPELESS:
case DXGI_FORMAT_BC3_UNORM:
case DXGI_FORMAT_BC3_UNORM_SRGB:
textureData->format = FORMAT_DXT5;
break;
default:
return 1;
}
} else {
if ((header->format.flags & DDPF_FOURCC) == 0) {
return 1;
}
// Ensure DXT 1/3/5
switch (header->format.fourCC) {
2017-07-23 10:17:51 +00:00
case FOUR_CC('D', 'X', 'T', '1'): textureData->format = FORMAT_DXT1; break;
case FOUR_CC('D', 'X', 'T', '3'): textureData->format = FORMAT_DXT3; break;
case FOUR_CC('D', 'X', 'T', '5'): textureData->format = FORMAT_DXT5; break;
default: return 1;
2017-07-22 10:42:05 +00:00
}
}
int width = textureData->width = header->width;
int height = textureData->height = header->height;
int mipmapCount = header->mipMapCount;
// Load mipmaps
2017-07-23 01:15:03 +00:00
vec_init(&textureData->mipmaps.list);
2017-07-22 10:42:05 +00:00
for (int i = 0; i < mipmapCount; i++) {
size_t numBlocksWide = width ? MAX(1, (width + 3) / 4) : 0;
size_t numBlocksHigh = height ? MAX(1, (height + 3) / 4) : 0;
size_t mipmapSize = numBlocksWide * numBlocksHigh * textureData->format.blockBytes;
// Overflow check
if (mipmapSize == 0 || (offset + mipmapSize) > size) {
2017-07-23 01:15:03 +00:00
vec_deinit(&textureData->mipmaps.list);
2017-07-22 10:42:05 +00:00
return 1;
}
Mipmap mipmap = { .width = width, .height = height, .data = &data[offset], .size = mipmapSize };
2017-07-23 01:15:03 +00:00
vec_push(&textureData->mipmaps.list, mipmap);
2017-07-22 10:42:05 +00:00
offset += mipmapSize;
2017-07-23 01:15:03 +00:00
width = MAX(width >> 1, 1);
height = MAX(height >> 1, 1);
2017-07-22 10:42:05 +00:00
}
textureData->data = NULL;
return 0;
}
2017-02-03 23:16:30 +00:00
TextureData* lovrTextureDataGetBlank(int width, int height, uint8_t value, TextureFormat format) {
2016-11-27 10:06:47 +00:00
TextureData* textureData = malloc(sizeof(TextureData));
if (!textureData) return NULL;
2017-07-22 10:11:43 +00:00
size_t size = width * height * format.blockBytes;
2017-01-09 05:29:16 +00:00
textureData->width = width;
textureData->height = height;
2017-02-03 23:16:30 +00:00
textureData->format = format;
2017-07-23 01:15:03 +00:00
textureData->data = memset(malloc(size), value, size);
2017-10-31 08:14:09 +00:00
textureData->mipmaps.generated = false;
2017-07-23 01:15:03 +00:00
textureData->blob = NULL;
2016-11-27 10:06:47 +00:00
return textureData;
}
2017-02-06 04:30:17 +00:00
TextureData* lovrTextureDataGetEmpty(int width, int height, TextureFormat format) {
2017-01-09 06:51:43 +00:00
TextureData* textureData = malloc(sizeof(TextureData));
if (!textureData) return NULL;
textureData->width = width;
textureData->height = height;
2017-02-06 04:30:17 +00:00
textureData->format = format;
2017-06-19 00:28:15 +00:00
textureData->data = NULL;
2017-10-31 08:14:09 +00:00
textureData->mipmaps.generated = false;
2017-07-23 01:15:03 +00:00
textureData->blob = NULL;
2017-01-09 06:51:43 +00:00
return textureData;
}
2017-04-02 12:55:21 +00:00
TextureData* lovrTextureDataFromBlob(Blob* blob) {
2016-11-26 07:54:45 +00:00
TextureData* textureData = malloc(sizeof(TextureData));
if (!textureData) return NULL;
2017-07-22 10:42:05 +00:00
if (!parseDDS(blob->data, blob->size, textureData)) {
textureData->blob = blob;
lovrRetain(&blob->ref);
return textureData;
}
stbi_set_flip_vertically_on_load(0);
2017-06-19 00:28:15 +00:00
textureData->format = FORMAT_RGBA;
textureData->data = stbi_load_from_memory(blob->data, blob->size, &textureData->width, &textureData->height, NULL, 4);
2017-10-31 08:14:09 +00:00
textureData->mipmaps.generated = true;
2017-07-23 01:15:03 +00:00
textureData->blob = NULL;
2016-11-26 07:54:45 +00:00
2017-06-19 00:28:15 +00:00
if (!textureData->data) {
lovrThrow("Could not load texture data from '%s'", blob->name);
2017-06-19 00:28:15 +00:00
free(textureData);
return NULL;
2016-11-26 07:54:45 +00:00
}
2016-12-01 04:32:14 +00:00
2017-06-19 00:28:15 +00:00
return textureData;
2016-11-26 07:54:45 +00:00
}
2017-02-06 04:30:17 +00:00
void lovrTextureDataResize(TextureData* textureData, int width, int height, uint8_t value) {
2017-07-23 01:15:03 +00:00
if (textureData->format.compressed || textureData->mipmaps.generated) {
lovrThrow("Can't resize a compressed texture or a texture with generated mipmaps.");
2017-07-23 01:15:03 +00:00
}
2017-07-22 10:11:43 +00:00
int size = width * height * textureData->format.blockBytes;
2017-02-06 04:30:17 +00:00
textureData->width = width;
textureData->height = height;
textureData->data = realloc(textureData->data, size);
memset(textureData->data, value, size);
}
2017-02-19 09:54:58 +00:00
void lovrTextureDataDestroy(TextureData* textureData) {
2017-07-23 01:15:03 +00:00
if (textureData->blob) {
lovrRelease(&textureData->blob->ref);
}
if (textureData->format.compressed) {
vec_deinit(&textureData->mipmaps.list);
}
2017-02-19 09:54:58 +00:00
free(textureData->data);
free(textureData);
}