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:
Pär Winzell 2018-02-25 17:28:26 -08:00 committed by GitHub
parent a984f7bf37
commit a8f194f793
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 33 additions and 117 deletions

View File

@ -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"

View File

@ -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;
} }

View File

@ -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);