better texture transparency test (#78)
Previous to this, a PNG that was on RGBA format would cause its corresponding texture to be flagged as transparent. This is very silly. We now iterate over the bytes, and if any is not 255, THEN there's alpha.
This commit is contained in:
parent
a984f7bf37
commit
a8f194f793
|
@ -12,9 +12,7 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
|
||||||
#include <stb_image.h>
|
#include <stb_image.h>
|
||||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
|
||||||
#include <stb_image_write.h>
|
#include <stb_image_write.h>
|
||||||
|
|
||||||
#include "FBX2glTF.h"
|
#include "FBX2glTF.h"
|
||||||
|
|
|
@ -7,131 +7,50 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
|
||||||
#include <cstring>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include "File_Utils.h"
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#include <stb_image.h>
|
||||||
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
|
#include <stb_image_write.h>
|
||||||
|
|
||||||
#include "Image_Utils.h"
|
#include "Image_Utils.h"
|
||||||
|
|
||||||
// https://www.w3.org/TR/PNG/#11IHDR
|
static bool imageHasTransparentPixels(FILE *f) {
|
||||||
const int PNG_IHDR_CHUNK_START = 8;
|
int width, height, channels;
|
||||||
const int PNG_HEADER_SIZE = PNG_IHDR_CHUNK_START + 8 + 13;
|
// RGBA: we have to load the pixels to figure out if the image is fully opaque
|
||||||
|
uint8_t *pixels = stbi_load_from_file(f, &width, &height, &channels, 0);
|
||||||
enum PNG_ColorType
|
if (pixels != nullptr) {
|
||||||
{
|
int pixelCount = width * height;
|
||||||
Grayscale = 0,
|
for (int ix = 0; ix < pixelCount; ix ++) {
|
||||||
RGB = 2,
|
// test fourth byte (alpha); 255 is 1.0
|
||||||
Palette = 3,
|
if (pixels[4*ix + 3] != 255) {
|
||||||
GrayscaleAlpha = 4,
|
return true;
|
||||||
RGBA = 6
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
static bool ImageIsPNG(char const *fileName, unsigned char const *buffer)
|
|
||||||
{
|
|
||||||
if ((buffer[0] == 0x89 && buffer[1] == 0x50 && buffer[2] == 0x4E && buffer[3] == 0x47)) {
|
|
||||||
// first chunk must be an IHDR
|
|
||||||
return (
|
|
||||||
buffer[PNG_IHDR_CHUNK_START + 4] == 'I' &&
|
|
||||||
buffer[PNG_IHDR_CHUNK_START + 5] == 'H' &&
|
|
||||||
buffer[PNG_IHDR_CHUNK_START + 6] == 'D' &&
|
|
||||||
buffer[PNG_IHDR_CHUNK_START + 7] == 'R');
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ImageProperties PNGProperties(unsigned char const *buffer)
|
|
||||||
{
|
|
||||||
// Extract (big-endian) properties from the PNG IHDR
|
|
||||||
ImageProperties properties;
|
|
||||||
properties.width =
|
|
||||||
(buffer[PNG_IHDR_CHUNK_START + 8] & 0xFF) << 24 | (buffer[PNG_IHDR_CHUNK_START + 9] & 0xFF) << 16 |
|
|
||||||
(buffer[PNG_IHDR_CHUNK_START + 10] & 0xFF) << 8 | (buffer[PNG_IHDR_CHUNK_START + 11] & 0xFF);
|
|
||||||
properties.height =
|
|
||||||
(buffer[PNG_IHDR_CHUNK_START + 12] & 0xFF) << 24 | (buffer[PNG_IHDR_CHUNK_START + 13] & 0xFF) << 16 |
|
|
||||||
(buffer[PNG_IHDR_CHUNK_START + 14] & 0xFF) << 8 | (buffer[PNG_IHDR_CHUNK_START + 15] & 0xFF);
|
|
||||||
properties.occlusion = (buffer[PNG_IHDR_CHUNK_START + 17] == RGBA) ? IMAGE_TRANSPARENT : IMAGE_OPAQUE;
|
|
||||||
|
|
||||||
return properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
// header is broken into multiple structs because TGA headers are packed
|
|
||||||
struct TGA_HeaderStart_t
|
|
||||||
{
|
|
||||||
char IDLength;
|
|
||||||
char ColorMapType;
|
|
||||||
char DataTypeCode;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TGA_HeaderColor_t
|
|
||||||
{
|
|
||||||
short int ColorMapOrigin;
|
|
||||||
short int ColorMapLength;
|
|
||||||
char ColorMapDepth;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TGA_HeaderOrigin_t
|
|
||||||
{
|
|
||||||
short int XOrigin;
|
|
||||||
short int YOrigin;
|
|
||||||
short Width;
|
|
||||||
short Height;
|
|
||||||
char BitsPerPixel;
|
|
||||||
char ImageDescriptor;
|
|
||||||
};
|
|
||||||
|
|
||||||
const int TGA_HEADER_SIZE = 8 + sizeof(TGA_HeaderOrigin_t);
|
|
||||||
|
|
||||||
static bool ImageIsTGA(char const *fileName, unsigned char const *buffer)
|
|
||||||
{
|
|
||||||
// TGA's have pretty ambiguous header so we simply check their file extension
|
|
||||||
size_t len = strlen(fileName);
|
|
||||||
if (len < 4) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#if defined( __unix__ ) || defined( __APPLE__ )
|
|
||||||
return strcasecmp(fileName + len - 4, ".tga") == 0;
|
|
||||||
#else
|
|
||||||
return _stricmp( fileName + len - 4, ".tga" ) == 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static ImageProperties TGAProperties(unsigned char const *buffer)
|
|
||||||
{
|
|
||||||
const TGA_HeaderOrigin_t *header = reinterpret_cast< const TGA_HeaderOrigin_t * >( &(buffer[8]));
|
|
||||||
|
|
||||||
ImageProperties properties;
|
|
||||||
properties.width = header->Width;
|
|
||||||
properties.height = header->Height;
|
|
||||||
properties.occlusion = (header->BitsPerPixel == 32) ? IMAGE_TRANSPARENT : IMAGE_OPAQUE;
|
|
||||||
|
|
||||||
return properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageProperties GetImageProperties(char const *filePath)
|
ImageProperties GetImageProperties(char const *filePath)
|
||||||
{
|
{
|
||||||
ImageProperties defaultProperties;
|
ImageProperties result = {
|
||||||
defaultProperties.width = 1;
|
1,
|
||||||
defaultProperties.height = 1;
|
1,
|
||||||
defaultProperties.occlusion = IMAGE_OPAQUE;
|
IMAGE_OPAQUE,
|
||||||
|
};
|
||||||
|
|
||||||
FILE *f = fopen(filePath, "rb");
|
FILE *f = fopen(filePath, "rb");
|
||||||
if (f == nullptr) {
|
if (f == nullptr) {
|
||||||
return defaultProperties;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This assumes every image file is at least as large as the largest header.
|
int channels;
|
||||||
const int maxHeaderSize = std::max(PNG_HEADER_SIZE, TGA_HEADER_SIZE);
|
int success = stbi_info_from_file(f, &result.width, &result.height, &channels);
|
||||||
unsigned char buffer[maxHeaderSize];
|
|
||||||
if (fread(buffer, (size_t) maxHeaderSize, 1, f) == 1) {
|
if (success && channels == 4 && imageHasTransparentPixels(f)) {
|
||||||
if (ImageIsPNG(filePath, buffer)) {
|
result.occlusion = IMAGE_TRANSPARENT;
|
||||||
return PNGProperties(buffer);
|
|
||||||
} else if (ImageIsTGA(filePath, buffer)) {
|
|
||||||
return TGAProperties(buffer);
|
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
return defaultProperties;
|
|
||||||
}
|
|
||||||
|
|
|
@ -13,15 +13,14 @@
|
||||||
enum ImageOcclusion
|
enum ImageOcclusion
|
||||||
{
|
{
|
||||||
IMAGE_OPAQUE,
|
IMAGE_OPAQUE,
|
||||||
IMAGE_PERFORATED,
|
|
||||||
IMAGE_TRANSPARENT
|
IMAGE_TRANSPARENT
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ImageProperties
|
struct ImageProperties
|
||||||
{
|
{
|
||||||
int width = 0;
|
int width;
|
||||||
int height = 0;
|
int height;
|
||||||
ImageOcclusion occlusion = IMAGE_OPAQUE;
|
ImageOcclusion occlusion;
|
||||||
};
|
};
|
||||||
|
|
||||||
ImageProperties GetImageProperties(char const *filePath);
|
ImageProperties GetImageProperties(char const *filePath);
|
||||||
|
|
Loading…
Reference in New Issue