mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-04-19 19:35:26 +00:00
Update GFXTextureManager and GBitmap
GBitmap Changes: Added all other formats to gbitmap that we support gbitmap now supports cubemaps added converters for all these other formats added stb_image_resize for extrudemips so we can extrude mipmaps for all other formats GFXTextureManager Can now directly make cubemaps and texture arrays based on the GFXTextureProfile API implementations for all functions that cubemaps and arrays needed
This commit is contained in:
parent
975fc924cc
commit
3aef90a6bc
66 changed files with 4235 additions and 2590 deletions
|
|
@ -24,6 +24,11 @@
|
|||
|
||||
#include "platform/platform.h"
|
||||
|
||||
#ifndef STB_IMAGE_RESIZE2_IMPLEMENTATION
|
||||
#define STB_IMAGE_RESIZE2_IMPLEMENTATION
|
||||
#define STBIR_PROFILE
|
||||
#include "gfx/bitmap/loaders/stb/stb_image_resize2.h"
|
||||
#endif // !STB_IMAGE_RESIZE2_IMPLEMENTATION
|
||||
|
||||
void bitmapExtrude5551_c(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth)
|
||||
{
|
||||
|
|
@ -67,7 +72,7 @@ void bitmapExtrude5551_c(const void *srcMip, void *mip, U32 srcHeight, U32 srcWi
|
|||
{
|
||||
U32 a = src[0];
|
||||
U32 c = src[stride];
|
||||
#if defined(TORQUE_OS_MAC)
|
||||
#if defined(TORQUE_BIG_ENDIAN)
|
||||
dst[y] = ((( (a >> 10) + (c >> 10)) >> 1) << 10) |
|
||||
((( ((a >> 5) & 0x1F) + ((c >> 5) & 0x1f)) >> 1) << 5) |
|
||||
((( ((a >> 0) & 0x1F) + ((c >> 0) & 0x1f)) >> 1) << 0);
|
||||
|
|
@ -81,151 +86,434 @@ void bitmapExtrude5551_c(const void *srcMip, void *mip, U32 srcHeight, U32 srcWi
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void bitmapExtrudeRGB_c(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth)
|
||||
|
||||
template <typename T>
|
||||
void bitmapExtrudeGeneric(
|
||||
const T* src, T* dst,
|
||||
U32 srcWidth, U32 srcHeight,
|
||||
U32 channels, U32 bpp)
|
||||
{
|
||||
const U8 *src = (const U8 *) srcMip;
|
||||
U8 *dst = (U8 *) mip;
|
||||
U32 stride = srcHeight != 1 ? (srcWidth) * 3 : 0;
|
||||
U32 srcRowStride = srcHeight != 1 ? (srcWidth * bpp) / sizeof(T) : 0;
|
||||
U32 dstWidth = srcWidth > 1 ? srcWidth / 2 : 1;
|
||||
U32 dstHeight = srcHeight > 1 ? srcHeight / 2 : 1;
|
||||
U32 dstRowStride = dstHeight != 1 ? (dstWidth * bpp) / sizeof(T) : 0;
|
||||
|
||||
U32 width = srcWidth >> 1;
|
||||
U32 height = srcHeight >> 1;
|
||||
if (width == 0) width = 1;
|
||||
if (height == 0) height = 1;
|
||||
|
||||
if (srcWidth != 1)
|
||||
for (U32 y = 0; y < dstHeight; ++y)
|
||||
{
|
||||
for(U32 y = 0; y < height; y++)
|
||||
for (U32 x = 0; x < dstWidth; ++x)
|
||||
{
|
||||
for(U32 x = 0; x < width; x++)
|
||||
for (U32 c = 0; c < channels; ++c)
|
||||
{
|
||||
*dst++ = (U32(*src) + U32(src[3]) + U32(src[stride]) + U32(src[stride+3]) + 2) >> 2;
|
||||
src++;
|
||||
*dst++ = (U32(*src) + U32(src[3]) + U32(src[stride]) + U32(src[stride+3]) + 2) >> 2;
|
||||
src++;
|
||||
*dst++ = (U32(*src) + U32(src[3]) + U32(src[stride]) + U32(src[stride+3]) + 2) >> 2;
|
||||
src += 4;
|
||||
}
|
||||
src += stride; // skip
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(U32 y = 0; y < height; y++)
|
||||
{
|
||||
*dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1;
|
||||
src++;
|
||||
*dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1;
|
||||
src++;
|
||||
*dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1;
|
||||
src += 4;
|
||||
U32 x0 = x * 2;
|
||||
U32 y0 = y * 2;
|
||||
U32 x1 = (x0 + 1 < srcWidth) ? x0 + 1 : x0;
|
||||
U32 y1 = (y0 + 1 < srcHeight) ? y0 + 1 : y0;
|
||||
|
||||
src += stride; // skip
|
||||
if constexpr (std::is_floating_point_v<T>)
|
||||
{
|
||||
T sum = 0;
|
||||
sum += src[y0 * srcRowStride + x0 * channels + c];
|
||||
sum += src[y0 * srcRowStride + x1 * channels + c];
|
||||
sum += src[y1 * srcRowStride + x0 * channels + c];
|
||||
sum += src[y1 * srcRowStride + x1 * channels + c];
|
||||
|
||||
dst[y * dstRowStride + x * channels + c] = sum * 0.25f;
|
||||
}
|
||||
else
|
||||
{
|
||||
U32 sum = 0;
|
||||
sum += src[y0 * srcRowStride + x0 * channels + c];
|
||||
sum += src[y0 * srcRowStride + x1 * channels + c];
|
||||
sum += src[y1 * srcRowStride + x0 * channels + c];
|
||||
sum += src[y1 * srcRowStride + x1 * channels + c];
|
||||
dst[y * dstRowStride + x * channels + c] = T((sum + 2) >> 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void bitmapExtrudeRGBA_c(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth)
|
||||
// 8-bit RGBA
|
||||
auto bitmapExtrudeU8_RGBA = [](const void* src, void* dst, U32 h, U32 w, U32 bpp) {
|
||||
bitmapExtrudeGeneric((const U8*)src, (U8*)dst, w, h, 4, bpp);
|
||||
};
|
||||
|
||||
// 16-bit RGBA (U16 / F32 stored as U16)
|
||||
auto bitmapExtrudeU16_RGBA = [](const void* src, void* dst, U32 h, U32 w, U32 bpp) {
|
||||
bitmapExtrudeGeneric((const U16*)src, (U16*)dst, w, h, 4, bpp);
|
||||
};
|
||||
|
||||
// 32-bit float RGBA
|
||||
auto bitmapExtrudeF32_RGBA = [](const void* src, void* dst, U32 h, U32 w, U32 bpp) {
|
||||
bitmapExtrudeGeneric((const F32*)src, (F32*)dst, w, h, 4, bpp);
|
||||
};
|
||||
|
||||
// RGB U8
|
||||
auto bitmapExtrudeU8_RGB = [](const void* src, void* dst, U32 h, U32 w, U32 bpp) {
|
||||
bitmapExtrudeGeneric((const U8*)src, (U8*)dst, w, h, 3, bpp);
|
||||
};
|
||||
|
||||
void (*bitmapExtrude5551)(const void* srcMip, void* mip, U32 height, U32 width) = bitmapExtrude5551_c;
|
||||
void (*bitmapExtrudeRGB)(const void* srcMip, void* mip, U32 srcHeight, U32 srcWidth, U32 bpp) = bitmapExtrudeU8_RGB;
|
||||
void (*bitmapExtrudeRGBA)(const void* srcMip, void* mip, U32 srcHeight, U32 srcWidth, U32 bpp) = bitmapExtrudeU8_RGBA;
|
||||
void (*bitmapExtrude16BitRGBA)(const void* srcMip, void* mip, U32 srcHeight, U32 srcWidth, U32 bpp) = bitmapExtrudeU16_RGBA;
|
||||
void (*bitmapExtrudeFPRGBA)(const void* srcMip, void* mip, U32 srcHeight, U32 srcWidth, U32 bpp) = bitmapExtrudeU16_RGBA;
|
||||
void (*bitmapExtrudeF32RGBA)(const void* srcMip, void* mip, U32 srcHeight, U32 srcWidth, U32 bpp) = bitmapExtrudeF32_RGBA;
|
||||
|
||||
struct StbResizeDesc
|
||||
{
|
||||
const U8 *src = (const U8 *) srcMip;
|
||||
U8 *dst = (U8 *) mip;
|
||||
U32 stride = srcHeight != 1 ? (srcWidth) * 4 : 0;
|
||||
stbir_datatype datatype;
|
||||
stbir_pixel_layout layout;
|
||||
U32 bytesPerPixel;
|
||||
};
|
||||
|
||||
U32 width = srcWidth >> 1;
|
||||
U32 height = srcHeight >> 1;
|
||||
if (width == 0) width = 1;
|
||||
if (height == 0) height = 1;
|
||||
|
||||
if (srcWidth != 1)
|
||||
inline bool getStbResizeDesc(GFXFormat fmt, StbResizeDesc& out)
|
||||
{
|
||||
switch (fmt)
|
||||
{
|
||||
for(U32 y = 0; y < height; y++)
|
||||
// ---- 1 channel ----
|
||||
case GFXFormatA8:
|
||||
case GFXFormatL8:
|
||||
out = { STBIR_TYPE_UINT8, STBIR_1CHANNEL, 1 };
|
||||
return true;
|
||||
|
||||
case GFXFormatL16:
|
||||
out = { STBIR_TYPE_UINT16, STBIR_1CHANNEL, 2 };
|
||||
return true;
|
||||
|
||||
case GFXFormatR16F:
|
||||
out = { STBIR_TYPE_HALF_FLOAT, STBIR_1CHANNEL, 2 };
|
||||
return true;
|
||||
|
||||
case GFXFormatR32F:
|
||||
out = { STBIR_TYPE_FLOAT, STBIR_1CHANNEL, 4 };
|
||||
return true;
|
||||
|
||||
// ---- 2 channel ----
|
||||
case GFXFormatA8L8:
|
||||
out = { STBIR_TYPE_UINT8, STBIR_2CHANNEL, 2 };
|
||||
return true;
|
||||
|
||||
case GFXFormatR16G16:
|
||||
out = { STBIR_TYPE_UINT16, STBIR_2CHANNEL, 4 };
|
||||
return true;
|
||||
|
||||
case GFXFormatR16G16F:
|
||||
out = { STBIR_TYPE_HALF_FLOAT, STBIR_2CHANNEL, 4 };
|
||||
return true;
|
||||
|
||||
// ---- RGB ----
|
||||
case GFXFormatR8G8B8:
|
||||
out = { STBIR_TYPE_UINT8, STBIR_RGB, 3 };
|
||||
return true;
|
||||
|
||||
case GFXFormatR8G8B8_SRGB:
|
||||
out = { STBIR_TYPE_UINT8_SRGB, STBIR_RGB, 3 };
|
||||
return true;
|
||||
|
||||
// ---- RGBA / RGBX ----
|
||||
case GFXFormatR8G8B8A8:
|
||||
case GFXFormatR8G8B8X8:
|
||||
out = { STBIR_TYPE_UINT8, STBIR_RGBA, 4 };
|
||||
return true;
|
||||
|
||||
case GFXFormatR8G8B8A8_SRGB:
|
||||
out = { STBIR_TYPE_UINT8_SRGB_ALPHA, STBIR_RGBA, 4 };
|
||||
return true;
|
||||
|
||||
case GFXFormatB8G8R8A8:
|
||||
out = { STBIR_TYPE_UINT8, STBIR_BGRA, 4 };
|
||||
return true;
|
||||
|
||||
// ---- 16-bit RGBA ----
|
||||
case GFXFormatR16G16B16A16:
|
||||
out = { STBIR_TYPE_UINT16, STBIR_RGBA, 8 };
|
||||
return true;
|
||||
|
||||
case GFXFormatR16G16B16A16F:
|
||||
out = { STBIR_TYPE_HALF_FLOAT, STBIR_RGBA, 8 };
|
||||
return true;
|
||||
|
||||
// ---- 32-bit RGBA ----
|
||||
case GFXFormatR32G32B32A32F:
|
||||
out = { STBIR_TYPE_FLOAT, STBIR_RGBA, 16 };
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void bitmapStbResizeToOutput(const void* src, U32 srcHeight, U32 srcWidth, void* out, U32 outHeight, U32 outWidth, U32 bpp, GFXFormat format)
|
||||
{
|
||||
StbResizeDesc desc;
|
||||
if (!getStbResizeDesc(format, desc))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const int srcStride = srcWidth * bpp;
|
||||
const int dstStride = outWidth * bpp;
|
||||
|
||||
stbir_resize(
|
||||
src,
|
||||
srcWidth,
|
||||
srcHeight,
|
||||
srcStride,
|
||||
out,
|
||||
outWidth,
|
||||
outHeight,
|
||||
dstStride,
|
||||
desc.layout,
|
||||
desc.datatype,
|
||||
STBIR_EDGE_CLAMP,
|
||||
STBIR_FILTER_MITCHELL);
|
||||
}
|
||||
|
||||
void(*bitmapResizeToOutput)(const void* src, U32 srcHeight, U32 srcWidth, void* out, U32 outHeight, U32 outWidth, U32 bpp, GFXFormat format) = bitmapStbResizeToOutput;
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Format description
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Channel semantics
|
||||
enum ChannelSemantic : U8
|
||||
{
|
||||
CH_NONE,
|
||||
CH_L,
|
||||
CH_A,
|
||||
CH_R,
|
||||
CH_G,
|
||||
CH_B
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Bitmap format descriptor
|
||||
struct GBitmapFormatDesc
|
||||
{
|
||||
U8 channels;
|
||||
ChannelSemantic semantic[4]; // per-channel meaning
|
||||
stbir_datatype datatype;
|
||||
bool srgb;
|
||||
bool premultiplied;
|
||||
bool isFloat;
|
||||
U8 bytesPerChannel;
|
||||
|
||||
bool is8() const { return !isFloat && bytesPerChannel == 1; }
|
||||
bool is16() const { return !isFloat && bytesPerChannel == 2; }
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Table mapping GFXFormat -> descriptor
|
||||
GBitmapFormatDesc getFormatDesc(GFXFormat fmt)
|
||||
{
|
||||
switch (fmt)
|
||||
{
|
||||
// 8-bit formats
|
||||
case GFXFormatA8:
|
||||
return { 1, {CH_A, CH_NONE, CH_NONE, CH_NONE}, STBIR_TYPE_UINT8, false, false, false, 1 };
|
||||
case GFXFormatL8:
|
||||
return { 1, {CH_L, CH_NONE, CH_NONE, CH_NONE}, STBIR_TYPE_UINT8, false, false, false, 1 };
|
||||
case GFXFormatA4L4:
|
||||
return { 2, {CH_L, CH_A, CH_NONE, CH_NONE}, STBIR_TYPE_UINT8, false, false, false, 1 };
|
||||
|
||||
// 16-bit formats
|
||||
case GFXFormatR5G6B5:
|
||||
return { 3, {CH_R, CH_G, CH_B, CH_NONE}, STBIR_TYPE_UINT8, false, false, false, 1 };
|
||||
case GFXFormatR5G5B5A1:
|
||||
return { 4, {CH_R, CH_G, CH_B, CH_A}, STBIR_TYPE_UINT8, false, false, false, 1 };
|
||||
case GFXFormatR5G5B5X1:
|
||||
return { 4, {CH_R, CH_G, CH_B, CH_NONE}, STBIR_TYPE_UINT8, false, false, false, 1 };
|
||||
case GFXFormatA8L8:
|
||||
return { 2, {CH_L, CH_A, CH_NONE, CH_NONE}, STBIR_TYPE_UINT8, false, false, false, 1 };
|
||||
case GFXFormatL16:
|
||||
return { 1, {CH_L, CH_NONE, CH_NONE, CH_NONE}, STBIR_TYPE_UINT16, false, false, false, 2 };
|
||||
case GFXFormatR16F:
|
||||
return { 1, {CH_R, CH_NONE, CH_NONE, CH_NONE}, STBIR_TYPE_HALF_FLOAT, false, false, true, 2 };
|
||||
case GFXFormatD16:
|
||||
return { 1, {CH_L, CH_NONE, CH_NONE, CH_NONE}, STBIR_TYPE_UINT16, false, false, false, 2 };
|
||||
|
||||
// 24-bit formats
|
||||
case GFXFormatR8G8B8:
|
||||
return { 3, {CH_R, CH_G, CH_B, CH_NONE}, STBIR_TYPE_UINT8, false, false, false, 1 };
|
||||
case GFXFormatR8G8B8_SRGB:
|
||||
return { 3, {CH_R, CH_G, CH_B, CH_NONE}, STBIR_TYPE_UINT8_SRGB, true, false, false, 1 };
|
||||
|
||||
// 32-bit formats
|
||||
case GFXFormatR8G8B8A8:
|
||||
case GFXFormatR8G8B8X8:
|
||||
return { 4, {CH_R, CH_G, CH_B, CH_A}, STBIR_TYPE_UINT8, false, false, false, 1 };
|
||||
case GFXFormatB8G8R8A8:
|
||||
return { 4, {CH_B, CH_G, CH_R, CH_A}, STBIR_TYPE_UINT8, false, false, false, 1 };
|
||||
case GFXFormatR8G8B8A8_SRGB:
|
||||
return { 4, {CH_R, CH_G, CH_B, CH_A}, STBIR_TYPE_UINT8_SRGB_ALPHA, true, false, false, 1 };
|
||||
case GFXFormatR16G16:
|
||||
return { 2, {CH_R, CH_G, CH_NONE, CH_NONE}, STBIR_TYPE_UINT16, false, false, false, 2 };
|
||||
case GFXFormatR16G16F:
|
||||
return { 2, {CH_R, CH_G, CH_NONE, CH_NONE}, STBIR_TYPE_HALF_FLOAT, false, false, true, 2 };
|
||||
|
||||
// 64-bit formats
|
||||
case GFXFormatR16G16B16A16:
|
||||
return { 4, {CH_R, CH_G, CH_B, CH_A}, STBIR_TYPE_UINT16, false, false, false, 2 };
|
||||
case GFXFormatR16G16B16A16F:
|
||||
return { 4, {CH_R, CH_G, CH_B, CH_A}, STBIR_TYPE_HALF_FLOAT, false, false, true, 2 };
|
||||
|
||||
// 128-bit formats
|
||||
case GFXFormatR32G32B32A32F:
|
||||
return { 4, {CH_R, CH_G, CH_B, CH_A}, STBIR_TYPE_FLOAT, false, false, true, 4 };
|
||||
|
||||
default: // fallback
|
||||
return { 1, {CH_L, CH_NONE, CH_NONE, CH_NONE}, STBIR_TYPE_UINT8, false, false, false, 1 };
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Conversion plan
|
||||
struct ConversionPlan
|
||||
{
|
||||
bool bitDepthChange;
|
||||
bool channelRepack;
|
||||
bool colorspaceChange;
|
||||
};
|
||||
|
||||
ConversionPlan decideConversion(const GBitmapFormatDesc& src, const GBitmapFormatDesc& dst)
|
||||
{
|
||||
ConversionPlan plan = {};
|
||||
plan.bitDepthChange = src.bytesPerChannel != dst.bytesPerChannel || src.isFloat != dst.isFloat;
|
||||
plan.channelRepack = src.channels != dst.channels || dMemcmp(src.semantic, dst.semantic, sizeof(src.semantic)) != 0;
|
||||
plan.colorspaceChange = src.srgb != dst.srgb;
|
||||
return plan;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Linear representation
|
||||
struct LinearPixel
|
||||
{
|
||||
float r = 0.f, g = 0.f, b = 0.f, a = 1.f;
|
||||
};
|
||||
|
||||
inline float srgbToLinear(float c)
|
||||
{
|
||||
return (c <= 0.04045f) ? c / 12.92f : powf((c + 0.055f) / 1.055f, 2.4f);
|
||||
}
|
||||
|
||||
inline float linearToSrgb(float c)
|
||||
{
|
||||
return (c <= 0.0031308f) ? c * 12.92f : 1.055f * powf(c, 1.f / 2.4f) - 0.055f;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Load a pixel from src format into LinearPixel
|
||||
static inline LinearPixel loadPixel(const void* src, const GBitmapFormatDesc& fmt, U32 index)
|
||||
{
|
||||
LinearPixel p;
|
||||
const U8* base = (const U8*)src + index * fmt.channels * fmt.bytesPerChannel;
|
||||
|
||||
for (U32 c = 0; c < fmt.channels; ++c)
|
||||
{
|
||||
float v = 0.f;
|
||||
if (fmt.is8())
|
||||
v = float(base[c]) / 255.f;
|
||||
else if (fmt.is16())
|
||||
v = float(convert16To8(*(const U16*)(base + c * 2))) / 255.f;
|
||||
else if (fmt.isFloat)
|
||||
{
|
||||
for(U32 x = 0; x < width; x++)
|
||||
{
|
||||
*dst++ = (U32(*src) + U32(src[4]) + U32(src[stride]) + U32(src[stride+4]) + 2) >> 2;
|
||||
src++;
|
||||
*dst++ = (U32(*src) + U32(src[4]) + U32(src[stride]) + U32(src[stride+4]) + 2) >> 2;
|
||||
src++;
|
||||
*dst++ = (U32(*src) + U32(src[4]) + U32(src[stride]) + U32(src[stride+4]) + 2) >> 2;
|
||||
src++;
|
||||
*dst++ = (U32(*src) + U32(src[4]) + U32(src[stride]) + U32(src[stride+4]) + 2) >> 2;
|
||||
src += 5;
|
||||
}
|
||||
src += stride; // skip
|
||||
if (fmt.bytesPerChannel == 2) // half float
|
||||
v = convertHalfToFloat(*(const U16*)(base + c * 2));
|
||||
else // full float
|
||||
v = *(const float*)(base + c * 4);
|
||||
}
|
||||
|
||||
if (fmt.srgb && fmt.semantic[c] != CH_A)
|
||||
v = srgbToLinear(v);
|
||||
|
||||
switch (fmt.semantic[c])
|
||||
{
|
||||
case CH_R: p.r = v; break;
|
||||
case CH_G: p.g = v; break;
|
||||
case CH_B: p.b = v; break;
|
||||
case CH_A: p.a = v; break;
|
||||
case CH_L: p.r = p.g = p.b = v; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(U32 y = 0; y < height; y++)
|
||||
{
|
||||
*dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1;
|
||||
src++;
|
||||
*dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1;
|
||||
src++;
|
||||
*dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1;
|
||||
src++;
|
||||
*dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1;
|
||||
src += 5;
|
||||
return p;
|
||||
}
|
||||
|
||||
src += stride; // skip
|
||||
//--------------------------------------------------------------------------------
|
||||
// Store a LinearPixel into dst format
|
||||
static inline void storePixel(void* dst, const GBitmapFormatDesc& fmt, U32 index, const LinearPixel& p)
|
||||
{
|
||||
U8* base = (U8*)dst + index * fmt.channels * fmt.bytesPerChannel;
|
||||
for (U32 c = 0; c < fmt.channels; ++c)
|
||||
{
|
||||
float v = 0.f;
|
||||
switch (fmt.semantic[c])
|
||||
{
|
||||
case CH_R: v = p.r; break;
|
||||
case CH_G: v = p.g; break;
|
||||
case CH_B: v = p.b; break;
|
||||
case CH_A: v = p.a; break;
|
||||
case CH_L: v = (p.r + p.g + p.b) / 3.f; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (fmt.srgb && fmt.semantic[c] != CH_A)
|
||||
v = linearToSrgb(v);
|
||||
|
||||
if (fmt.is8())
|
||||
base[c] = uint8_t(mClamp(v * 255.f, 0.f, 255.f));
|
||||
else if (fmt.is16())
|
||||
*(U16*)(base + c * 2) = convert8To16(uint8_t(mClamp(v * 255.f, 0.f, 255.f)));
|
||||
else if (fmt.isFloat)
|
||||
{
|
||||
if (fmt.bytesPerChannel == 2) // half float
|
||||
*(U16*)(base + c * 2) = convertFloatToHalf(v);
|
||||
else
|
||||
*(float*)(base + c * 4) = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bitmapExtrudeFPRGBA_c(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth)
|
||||
//--------------------------------------------------------------------------------
|
||||
// Main generalized converter
|
||||
bool bitmapConvertFormat(U8** srcBuffer, U32 pixels, const GBitmapFormatDesc& srcFmt, const GBitmapFormatDesc& dstFmt)
|
||||
{
|
||||
const U16 *src = (const U16 *)srcMip;
|
||||
U16 *dst = (U16 *)mip;
|
||||
U32 stride = srcHeight != 1 ? (srcWidth) * 8 : 0;
|
||||
ConversionPlan plan = decideConversion(srcFmt, dstFmt);
|
||||
if (!plan.bitDepthChange && !plan.channelRepack && !plan.colorspaceChange)
|
||||
return true; // nothing to do
|
||||
|
||||
U32 width = srcWidth >> 1;
|
||||
U32 height = srcHeight >> 1;
|
||||
if (width == 0) width = 1;
|
||||
if (height == 0) height = 1;
|
||||
void* dstBuffer = *srcBuffer;
|
||||
|
||||
if (srcWidth != 1)
|
||||
if (plan.bitDepthChange || plan.channelRepack)
|
||||
dstBuffer = new U8[pixels * dstFmt.channels * dstFmt.bytesPerChannel];
|
||||
|
||||
for (U32 i = 0; i < pixels; ++i)
|
||||
{
|
||||
for (U32 y = 0; y < height; y++)
|
||||
{
|
||||
for (U32 x = 0; x < width; x++)
|
||||
{
|
||||
*dst++ = (U32(*src) + U32(src[4]) + U32(src[stride]) + U32(src[stride + 4]) + 2) >> 2;
|
||||
src++;
|
||||
*dst++ = (U32(*src) + U32(src[4]) + U32(src[stride]) + U32(src[stride + 4]) + 2) >> 2;
|
||||
src++;
|
||||
*dst++ = (U32(*src) + U32(src[4]) + U32(src[stride]) + U32(src[stride + 4]) + 2) >> 2;
|
||||
src++;
|
||||
*dst++ = (U32(*src) + U32(src[4]) + U32(src[stride]) + U32(src[stride + 4]) + 2) >> 2;
|
||||
src += 5;
|
||||
}
|
||||
src += stride; // skip
|
||||
}
|
||||
LinearPixel p = loadPixel(*srcBuffer, srcFmt, i);
|
||||
storePixel(dstBuffer, dstFmt, i, p);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (U32 y = 0; y < height; y++)
|
||||
{
|
||||
*dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1;
|
||||
src++;
|
||||
*dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1;
|
||||
src++;
|
||||
*dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1;
|
||||
src++;
|
||||
*dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1;
|
||||
src += 5;
|
||||
|
||||
src += stride; // skip
|
||||
}
|
||||
if (dstBuffer != *srcBuffer)
|
||||
{
|
||||
delete[](U8*)* srcBuffer;
|
||||
*srcBuffer = (U8*)dstBuffer;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void (*bitmapExtrude5551)(const void *srcMip, void *mip, U32 height, U32 width) = bitmapExtrude5551_c;
|
||||
void (*bitmapExtrudeRGB)(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth) = bitmapExtrudeRGB_c;
|
||||
void (*bitmapExtrudeRGBA)(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth) = bitmapExtrudeRGBA_c;
|
||||
void (*bitmapExtrudeFPRGBA)(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth) = bitmapExtrudeFPRGBA_c;
|
||||
//--------------------------------------------------------------------------------
|
||||
// Entry point for GBitmap::setFormat
|
||||
bool bitmapALLConvertToOutput(U8** src, U32 pixels, GFXFormat srcFormat, GFXFormat dstFormat)
|
||||
{
|
||||
const GBitmapFormatDesc& srcFmt = getFormatDesc(srcFormat);
|
||||
const GBitmapFormatDesc& dstFmt = getFormatDesc(dstFormat);
|
||||
return bitmapConvertFormat(src, pixels, srcFmt, dstFmt);
|
||||
}
|
||||
|
||||
bool(*bitmapConvertToOutput)(U8** src, U32 pixels, GFXFormat srcFormat, GFXFormat dstFormat) = bitmapALLConvertToOutput;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
|
|
@ -238,7 +526,7 @@ void bitmapConvertRGB_to_1555_c(U8 *src, U32 pixels)
|
|||
U32 g = src[1] >> 3;
|
||||
U32 b = src[2] >> 3;
|
||||
|
||||
#if defined(TORQUE_OS_MAC)
|
||||
#if defined(TORQUE_BIG_ENDIAN)
|
||||
*dst++ = 0x8000 | (b << 10) | (g << 5) | (r << 0);
|
||||
#else
|
||||
*dst++ = b | (g << 5) | (r << 10) | 0x8000;
|
||||
|
|
@ -260,7 +548,7 @@ void bitmapConvertRGB_to_5551_c(U8 *src, U32 pixels)
|
|||
U32 g = src[1] >> 3;
|
||||
U32 b = src[2] >> 3;
|
||||
|
||||
#if defined(TORQUE_OS_MAC)
|
||||
#if defined(TORQUE_BIG_ENDIAN)
|
||||
*dst++ = (1 << 15) | (b << 10) | (g << 5) | (r << 0);
|
||||
#else
|
||||
*dst++ = (b << 1) | (g << 6) | (r << 11) | 1;
|
||||
|
|
|
|||
|
|
@ -22,21 +22,148 @@
|
|||
|
||||
#ifndef _BITMAPUTILS_H_
|
||||
#define _BITMAPUTILS_H_
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
#ifndef _TORQUE_TYPES_H_
|
||||
#include "platform/types.h"
|
||||
#endif
|
||||
#ifndef _GFXENUMS_H_
|
||||
#include "gfx/gfxEnums.h"
|
||||
#endif
|
||||
#ifndef _MMATHFN_H_
|
||||
#include "math/mMathFn.h"
|
||||
#endif
|
||||
|
||||
extern void (*bitmapExtrude5551)(const void *srcMip, void *mip, U32 height, U32 width);
|
||||
extern void (*bitmapExtrudeRGB)(const void *srcMip, void *mip, U32 height, U32 width);
|
||||
extern void (*bitmapExtrudeRGBA)(const void *srcMip, void *mip, U32 height, U32 width);
|
||||
extern void(*bitmapExtrudeFPRGBA)(const void *srcMip, void *mip, U32 height, U32 width);
|
||||
extern void (*bitmapExtrudeRGB)(const void *srcMip, void *mip, U32 height, U32 width, U32 bpp);
|
||||
extern void (*bitmapExtrudeRGBA)(const void *srcMip, void *mip, U32 height, U32 width, U32 bpp);
|
||||
extern void (*bitmapExtrude16BitRGBA)(const void *srcMip, void *mip, U32 height, U32 width, U32 bpp);
|
||||
extern void(*bitmapExtrudeFPRGBA)(const void *srcMip, void *mip, U32 height, U32 width, U32 bpp);
|
||||
extern void(*bitmapExtrudeF32RGBA)(const void *srcMip, void *mip, U32 height, U32 width, U32 bpp);
|
||||
|
||||
extern void(*bitmapResizeToOutput)(const void* src, U32 srcHeight, U32 srcWidth, void* out, U32 outHeight, U32 outWidth, U32 bpp, GFXFormat format);
|
||||
extern bool(*bitmapConvertToOutput)(U8** src, U32 pixels, GFXFormat srcFormat, GFXFormat dstFormat);
|
||||
|
||||
extern void (*bitmapConvertRGB_to_5551)(U8 *src, U32 pixels);
|
||||
extern void (*bitmapConvertRGB_to_1555)(U8 *src, U32 pixels);
|
||||
extern void (*bitmapConvertRGB_to_RGBX)( U8 **src, U32 pixels );
|
||||
extern void (*bitmapConvertRGBX_to_RGB)( U8 **src, U32 pixels );
|
||||
extern void (*bitmapConvertA8_to_RGBA)( U8 **src, U32 pixels );
|
||||
|
||||
void bitmapExtrudeRGB_c(const void *srcMip, void *mip, U32 height, U32 width);
|
||||
//-----------------------------------------------------------------------------
|
||||
// Half <-> Float Conversion Utilities
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
inline F32 convertHalfToFloat(U16 h)
|
||||
{
|
||||
U32 sign = (h >> 15) & 0x00000001;
|
||||
U32 exp = (h >> 10) & 0x0000001F;
|
||||
U32 mant = h & 0x000003FF;
|
||||
|
||||
U32 outSign = sign << 31;
|
||||
U32 outExp, outMant;
|
||||
|
||||
if (exp == 0)
|
||||
{
|
||||
if (mant == 0)
|
||||
{
|
||||
// Zero
|
||||
outExp = 0;
|
||||
outMant = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Subnormal number -> normalize
|
||||
exp = 1;
|
||||
while ((mant & 0x00000400) == 0)
|
||||
{
|
||||
mant <<= 1;
|
||||
exp -= 1;
|
||||
}
|
||||
mant &= 0x000003FF;
|
||||
outExp = (exp + (127 - 15)) << 23;
|
||||
outMant = mant << 13;
|
||||
}
|
||||
}
|
||||
else if (exp == 31)
|
||||
{
|
||||
// Inf or NaN
|
||||
outExp = 0xFF << 23;
|
||||
outMant = mant ? (mant << 13) : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Normalized
|
||||
outExp = (exp + (127 - 15)) << 23;
|
||||
outMant = mant << 13;
|
||||
}
|
||||
|
||||
U32 out = outSign | outExp | outMant;
|
||||
F32 result;
|
||||
dMemcpy(&result, &out, sizeof(F32));
|
||||
return result;
|
||||
}
|
||||
|
||||
inline U16 convertFloatToHalf(F32 f)
|
||||
{
|
||||
U32 bits;
|
||||
dMemcpy(&bits, &f, sizeof(U32));
|
||||
|
||||
U32 sign = (bits >> 16) & 0x00008000;
|
||||
U32 exp = ((bits >> 23) & 0x000000FF) - (127 - 15);
|
||||
U32 mant = bits & 0x007FFFFF;
|
||||
|
||||
if (exp <= 0)
|
||||
{
|
||||
if (exp < -10)
|
||||
return (U16)sign; // Too small => 0
|
||||
mant = (mant | 0x00800000) >> (1 - exp);
|
||||
return (U16)(sign | (mant >> 13));
|
||||
}
|
||||
else if (exp == 0xFF - (127 - 15))
|
||||
{
|
||||
if (mant == 0)
|
||||
{
|
||||
// Inf
|
||||
return (U16)(sign | 0x7C00);
|
||||
}
|
||||
else
|
||||
{
|
||||
// NaN
|
||||
mant >>= 13;
|
||||
return (U16)(sign | 0x7C00 | mant | (mant == 0));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (exp > 30)
|
||||
{
|
||||
// Overflow => Inf
|
||||
return (U16)(sign | 0x7C00);
|
||||
}
|
||||
return (U16)(sign | (exp << 10) | (mant >> 13));
|
||||
}
|
||||
}
|
||||
|
||||
// Convert a single 16-bit value (0..65535) to 8-bit (0..255)
|
||||
inline U8 convert16To8(U16 v16)
|
||||
{
|
||||
// Take the top 8 bits as approximation
|
||||
return U8(v16 >> 8);
|
||||
}
|
||||
|
||||
// Convert a single 8-bit value (0..255) to 16-bit (0..65535)
|
||||
inline U16 convert8To16(U8 v8)
|
||||
{
|
||||
// Replicate into high and low byte: 0->0, 255->0xFFFF
|
||||
return (U16(v8) << 8) | v8;
|
||||
}
|
||||
|
||||
inline U8 floatTo8(F32 v)
|
||||
{
|
||||
return U8(mClamp(v * 255.f, 0.f, 255.f));
|
||||
}
|
||||
|
||||
|
||||
#endif //_BITMAPUTILS_H_
|
||||
|
|
|
|||
|
|
@ -34,9 +34,7 @@
|
|||
|
||||
namespace CubemapSaver
|
||||
{
|
||||
const U32 CubeFaces = 6;
|
||||
|
||||
bool save(GFXCubemapHandle cubemap, const Torque::Path &path, GFXFormat compressionFormat)
|
||||
bool save(GFXTexHandle cubemap, const Torque::Path &path, GFXFormat compressionFormat)
|
||||
{
|
||||
if (!cubemap.isValid())
|
||||
{
|
||||
|
|
@ -44,43 +42,24 @@ namespace CubemapSaver
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
GFXCubemap *pCubemap = cubemap.getPointer();
|
||||
const U32 faceSize = pCubemap->getSize();
|
||||
const U32 mipLevels = pCubemap->getMipMapLevels();
|
||||
|
||||
GFXFormat targetFmt = pCubemap->getFormat();
|
||||
//setup render targets
|
||||
GFXTexHandle pTextures[CubeFaces];
|
||||
|
||||
for (U32 face = 0; face < CubeFaces; face++)
|
||||
{
|
||||
pTextures[face].set(faceSize, faceSize, targetFmt,
|
||||
&GFXTexturePersistentProfile, avar("%s() - (line %d)", __FUNCTION__, __LINE__),
|
||||
mipLevels, GFXTextureManager::AA_MATCH_BACKBUFFER);
|
||||
|
||||
// yep t3d has funky z up, need to change the face order
|
||||
GFX->copyResource(pTextures[face], pCubemap, GFXCubemap::zUpFaceIndex(face) );
|
||||
}
|
||||
|
||||
GBitmap *pBitmaps[CubeFaces];
|
||||
bool error = false;
|
||||
const bool compressedFormat = ImageUtil::isCompressedFormat(compressionFormat);
|
||||
const U32 faceSize = cubemap->getWidth();
|
||||
const U32 mipLevels = cubemap->getMipLevels();
|
||||
GFXFormat targetFmt = cubemap->getFormat();
|
||||
const bool hasMips = mipLevels > 1 ? true : false;
|
||||
for (U32 i = 0; i < CubeFaces; i++)
|
||||
|
||||
GBitmap* temp = new GBitmap(faceSize, faceSize, hasMips, targetFmt, 6);
|
||||
bool result = cubemap.copyToBmp(temp);
|
||||
if (!result)
|
||||
{
|
||||
pBitmaps[i] = new GBitmap(faceSize, faceSize, hasMips, targetFmt);
|
||||
bool result = pTextures[i].copyToBmp(pBitmaps[i]);
|
||||
if (!result)
|
||||
{
|
||||
Con::errorf("CubemapSaver: cubemap number %u failed to copy", i);
|
||||
error = true;
|
||||
}
|
||||
Con::errorf("CubemapSaver: cubemap failed to copy");
|
||||
error = true;
|
||||
}
|
||||
|
||||
if (!error)
|
||||
{
|
||||
DDSFile *pDds = DDSFile::createDDSCubemapFileFromGBitmaps(pBitmaps);
|
||||
DDSFile *pDds = DDSFile::createDDSFileFromGBitmap(temp);
|
||||
if (pDds)
|
||||
{
|
||||
// compressed and floating point don't need swizzling
|
||||
|
|
@ -103,14 +82,12 @@ namespace CubemapSaver
|
|||
}
|
||||
|
||||
//cleanup
|
||||
for (U32 i = 0; i < CubeFaces; i++)
|
||||
SAFE_DELETE(pBitmaps[i]);
|
||||
|
||||
SAFE_DELETE(temp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getBitmaps(GFXCubemapHandle cubemap, GFXFormat compressionFormat, GBitmap* faceBitmaps[6])
|
||||
bool getBitmaps(GFXTexHandle cubemap, GFXFormat compressionFormat, GBitmap* faceBitmaps[6])
|
||||
{
|
||||
if (!cubemap.isValid())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -33,9 +33,9 @@
|
|||
namespace CubemapSaver
|
||||
{
|
||||
// save cubemap handle to a dds cubemap with optional compression
|
||||
bool save(GFXCubemapHandle cubemap, const Torque::Path &path, GFXFormat compressionFormat = GFXFormatR8G8B8A8);
|
||||
bool save(GFXTexHandle cubemap, const Torque::Path &path, GFXFormat compressionFormat = GFXFormatR8G8B8A8);
|
||||
|
||||
bool getBitmaps(GFXCubemapHandle cubemap, GFXFormat compressionFormat, GBitmap* faceBitmaps[6]);
|
||||
bool getBitmaps(GFXTexHandle cubemap, GFXFormat compressionFormat, GBitmap* faceBitmaps[6]);
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -652,6 +652,12 @@ Resource<DDSFile> DDSFile::load( const Torque::Path &path, U32 dropMipCount )
|
|||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool DDSFile::isCompressedFormat(GFXFormat fmt)
|
||||
{
|
||||
return (fmt >= GFXFormatBC1 && fmt <= GFXFormatBC5) ||
|
||||
(fmt >= GFXFormatBC1_SRGB && fmt <= GFXFormatBC3_SRGB);
|
||||
}
|
||||
|
||||
DDSFile *DDSFile::createDDSFileFromGBitmap( const GBitmap *gbmp )
|
||||
{
|
||||
if( gbmp == NULL )
|
||||
|
|
@ -666,6 +672,11 @@ DDSFile *DDSFile::createDDSFileFromGBitmap( const GBitmap *gbmp )
|
|||
ret->mDepth = 0;
|
||||
ret->mFormat = gbmp->getFormat();
|
||||
ret->mFlags.set(RGBData);
|
||||
if (gbmp->getNumFaces() == 6)
|
||||
{
|
||||
ret->mFlags.set(RGBData | CubeMapFlag | CubeMap_PosX_Flag | CubeMap_NegX_Flag | CubeMap_PosY_Flag |
|
||||
CubeMap_NegY_Flag | CubeMap_PosZ_Flag | CubeMap_NegZ_Flag);
|
||||
}
|
||||
ret->mBytesPerPixel = gbmp->getBytesPerPixel();
|
||||
ret->mMipMapCount = gbmp->getNumMipLevels();
|
||||
ret->mHasTransparency = gbmp->getHasTransparency();
|
||||
|
|
@ -685,36 +696,39 @@ DDSFile *DDSFile::createDDSFileFromGBitmap( const GBitmap *gbmp )
|
|||
if( ret->mMipMapCount > 1 )
|
||||
ret->mFlags.set(MipMapsFlag);
|
||||
|
||||
// One surface per GBitmap
|
||||
ret->mSurfaces.push_back( new SurfaceData() );
|
||||
|
||||
// Load the mips
|
||||
for( S32 i = 0; i < ret->mMipMapCount; i++ )
|
||||
for (U32 face = 0; face < gbmp->getNumFaces(); face++)
|
||||
{
|
||||
const U32 mipSz = ret->getSurfaceSize(i);
|
||||
ret->mSurfaces.last()->mMips.push_back( new U8[mipSz] );
|
||||
// One surface per GBitmap
|
||||
ret->mSurfaces.push_back(new SurfaceData());
|
||||
|
||||
U8 *mipMem = ret->mSurfaces.last()->mMips.last();
|
||||
|
||||
// If this is a straight copy, just do it, otherwise (ugh)
|
||||
if( ret->mFormat == gbmp->getFormat() )
|
||||
dMemcpy( mipMem, gbmp->getBits(i), mipSz );
|
||||
else
|
||||
// Load the mips
|
||||
for (S32 i = 0; i < ret->mMipMapCount; i++)
|
||||
{
|
||||
// Assumption:
|
||||
AssertFatal( gbmp->getBytesPerPixel() + 1 == ret->mBytesPerPixel, "Assumption failed, not 24->32 bit straight convert." );
|
||||
const U32 mipSz = ret->getSurfaceSize(i);
|
||||
ret->mSurfaces.last()->mMips.push_back(new U8[mipSz]);
|
||||
|
||||
for( S32 pxl = 0; pxl < gbmp->getWidth(i) * gbmp->getHeight(i); pxl++ )
|
||||
U8* mipMem = ret->mSurfaces.last()->mMips.last();
|
||||
|
||||
// If this is a straight copy, just do it, otherwise (ugh)
|
||||
if (ret->mFormat == gbmp->getFormat())
|
||||
dMemcpy(mipMem, gbmp->getBits(i, face), mipSz);
|
||||
else
|
||||
{
|
||||
U8 *dst = &mipMem[pxl * ret->mBytesPerPixel];
|
||||
const U8 *src = &gbmp->getBits(i)[pxl * gbmp->getBytesPerPixel()];
|
||||
dMemcpy( dst, src, gbmp->getBytesPerPixel() * sizeof(U8) );
|
||||
dst[ret->mBytesPerPixel - 1] = 255;
|
||||
}
|
||||
}
|
||||
// Assumption:
|
||||
AssertFatal(gbmp->getBytesPerPixel() + 1 == ret->mBytesPerPixel, "Assumption failed, not 24->32 bit straight convert.");
|
||||
|
||||
// Uncomment to debug-dump each mip level
|
||||
//ret->mSurfaces.last()->dumpImage( ret, i, avar( "%d_Gbmp_xmip%d", ret, i ) );
|
||||
for (S32 pxl = 0; pxl < gbmp->getWidth(i) * gbmp->getHeight(i); pxl++)
|
||||
{
|
||||
U8* dst = &mipMem[pxl * ret->mBytesPerPixel];
|
||||
const U8* src = &gbmp->getBits(i, face)[pxl * gbmp->getBytesPerPixel()];
|
||||
dMemcpy(dst, src, gbmp->getBytesPerPixel() * sizeof(U8));
|
||||
dst[ret->mBytesPerPixel - 1] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
// Uncomment to debug-dump each mip level
|
||||
//ret->mSurfaces.last()->dumpImage( ret, i, avar( "%d_Gbmp_xmip%d", ret, i ) );
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
@ -777,22 +791,50 @@ DDSFile *DDSFile::createDDSCubemapFileFromGBitmaps(GBitmap **gbmps)
|
|||
|
||||
bool DDSFile::decompressToGBitmap(GBitmap *dest)
|
||||
{
|
||||
const bool isCube = isCubemap();
|
||||
const U32 numFaces = isCube ? 6 : 1;
|
||||
// TBD: do we support other formats?
|
||||
if (mFormat != GFXFormatBC1 && mFormat != GFXFormatBC2 && mFormat != GFXFormatBC3)
|
||||
return false;
|
||||
if (!isCompressedFormat(mFormat))
|
||||
{
|
||||
dest->allocateBitmapWithMips(getWidth(), getHeight(), getMipLevels(), mFormat, numFaces);
|
||||
U32 numMips = getMipLevels();
|
||||
|
||||
dest->allocateBitmapWithMips(getWidth(), getHeight(), getMipLevels(), GFXFormatR8G8B8A8);
|
||||
for (U32 face = 0; face < numFaces; face++)
|
||||
{
|
||||
for (U32 i = 0; i < numMips; i++)
|
||||
{
|
||||
U8* addr = dest->getAddress(0, 0, i, face);
|
||||
|
||||
const U8* mipBuffer = mSurfaces[face]->mMips[i];
|
||||
const U32 mipWidth = getWidth(i);
|
||||
const U32 mipHeight = getHeight(i);
|
||||
|
||||
const U32 bpp = dest->getBytesPerPixel();
|
||||
const U32 rowBytes = mipWidth * bpp;
|
||||
|
||||
for (U32 y = 0; y < mipHeight; ++y)
|
||||
{
|
||||
dMemcpy(addr + y * rowBytes, mipBuffer + y * rowBytes, rowBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
dest->allocateBitmapWithMips(getWidth(), getHeight(), getMipLevels(), GFXFormatR8G8B8A8, numFaces);
|
||||
|
||||
// Decompress and copy mips...
|
||||
|
||||
U32 numMips = getMipLevels();
|
||||
|
||||
for (U32 i = 0; i < numMips; i++)
|
||||
for (U32 face = 0; face < numFaces; face++)
|
||||
{
|
||||
U8 *addr = dest->getAddress(0, 0, i);
|
||||
const U8 *mipBuffer = mSurfaces[0]->mMips[i];
|
||||
ImageUtil::decompress(mipBuffer, addr, getWidth(i), getHeight(i), mFormat);
|
||||
for (U32 i = 0; i < numMips; i++)
|
||||
{
|
||||
U8* addr = dest->getAddress(0, 0, i, face);
|
||||
const U8* mipBuffer = mSurfaces[face]->mMips[i];
|
||||
ImageUtil::decompress(mipBuffer, addr, getWidth(i), getHeight(i), mFormat);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -205,6 +205,8 @@ struct DDSFile
|
|||
mSurfaces.clear();
|
||||
}
|
||||
|
||||
bool isCompressedFormat(GFXFormat fmt);
|
||||
|
||||
static DDSFile *createDDSFileFromGBitmap( const GBitmap *gbmp );
|
||||
//Create a single cubemap texture from 6 GBitmap
|
||||
static DDSFile *createDDSCubemapFileFromGBitmaps(GBitmap **gbmps);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -135,13 +135,15 @@ public:
|
|||
GBitmap(const U32 in_width,
|
||||
const U32 in_height,
|
||||
const bool in_extrudeMipLevels = false,
|
||||
const GFXFormat in_format = GFXFormatR8G8B8 );
|
||||
const GFXFormat in_format = GFXFormatR8G8B8,
|
||||
const U32 in_numFaces = 1);
|
||||
|
||||
// This builds a GBitmap with the R8G8B8A8 format using the passed in
|
||||
// data (assumes that there is width * height * 4 U8's in data)
|
||||
GBitmap(const U32 in_width,
|
||||
const U32 in_height,
|
||||
const U8* data );
|
||||
const U8* data,
|
||||
const U32 in_numFaces = 1);
|
||||
|
||||
virtual ~GBitmap();
|
||||
|
||||
|
|
@ -163,12 +165,14 @@ public:
|
|||
void allocateBitmap(const U32 in_width,
|
||||
const U32 in_height,
|
||||
const bool in_extrudeMipLevels = false,
|
||||
const GFXFormat in_format = GFXFormatR8G8B8 );
|
||||
const GFXFormat in_format = GFXFormatR8G8B8,
|
||||
const U32 in_numFaces = 1);
|
||||
|
||||
void allocateBitmapWithMips(const U32 in_width,
|
||||
const U32 in_height,
|
||||
const U32 in_numMips,
|
||||
const GFXFormat in_format = GFXFormatR8G8B8);
|
||||
const U32 in_height,
|
||||
const U32 in_numMips,
|
||||
const GFXFormat in_format = GFXFormatR8G8B8,
|
||||
const U32 in_numFaces = 1);
|
||||
|
||||
void extrudeMipLevels(bool clearBorders = false);
|
||||
void chopTopMips(U32 mipsToChop);
|
||||
|
|
@ -191,16 +195,18 @@ public:
|
|||
|
||||
U32 getWidth(const U32 in_mipLevel = 0) const;
|
||||
U32 getHeight(const U32 in_mipLevel = 0) const;
|
||||
U32 _getFaceOffset(const U32 face = 0) const;
|
||||
U32 getDepth(const U32 in_mipLevel = 0) const;
|
||||
|
||||
U8* getAddress(const S32 in_x, const S32 in_y, const U32 mipLevel = 0);
|
||||
const U8* getAddress(const S32 in_x, const S32 in_y, const U32 mipLevel = 0) const;
|
||||
U8* getAddress(const S32 in_x, const S32 in_y, const U32 mipLevel = 0, const U32 face = 0);
|
||||
const U8* getAddress(const S32 in_x, const S32 in_y, const U32 mipLevel = 0, const U32 face = 0) const;
|
||||
|
||||
const U8* getBits(const U32 in_mipLevel = 0) const;
|
||||
U8* getWritableBits(const U32 in_mipLevel = 0);
|
||||
const U8* getBits(const U32 in_mipLevel = 0, const U32 face = 0) const;
|
||||
U8* getWritableBits(const U32 in_mipLevel = 0, const U32 face = 0);
|
||||
|
||||
U32 getByteSize() const { return mByteSize; }
|
||||
U32 getBytesPerPixel() const { return mBytesPerPixel; }
|
||||
U32 getFormatBytesPerPixel(GFXFormat fmt);
|
||||
|
||||
U32 getSurfaceSize(const U32 mipLevel) const;
|
||||
|
||||
|
|
@ -220,6 +226,7 @@ public:
|
|||
bool getColor(const U32 x, const U32 y, ColorI& rColor) const;
|
||||
bool setColor(const U32 x, const U32 y, const ColorI& rColor);
|
||||
U8 getChanelValueAt(U32 x, U32 y, U32 chan);
|
||||
U32 getNumFaces() const { return mNumFaces; }
|
||||
|
||||
/// This method will combine bitmapA and bitmapB using the operation specified
|
||||
/// by combineOp. The result will be stored in the bitmap that this method is
|
||||
|
|
@ -275,7 +282,7 @@ public:
|
|||
|
||||
private:
|
||||
GFXFormat mInternalFormat;
|
||||
|
||||
U32 mNumFaces; // default 1, set to 6 for cubemap
|
||||
U8* mBits; // Master bytes
|
||||
U32 mByteSize;
|
||||
U32 mWidth;
|
||||
|
|
@ -284,6 +291,7 @@ private:
|
|||
|
||||
U32 mNumMipLevels;
|
||||
U32 mMipLevelOffsets[c_maxMipLevels];
|
||||
U32 mFaceOffsets[6]; // Maximum 6 for cubemaps; could also dynamically allocate if needed
|
||||
|
||||
bool mHasTransparency;
|
||||
|
||||
|
|
@ -316,32 +324,39 @@ inline U32 GBitmap::getHeight(const U32 in_mipLevel) const
|
|||
return (retVal != 0) ? retVal : 1;
|
||||
}
|
||||
|
||||
inline const U8* GBitmap::getBits(const U32 in_mipLevel) const
|
||||
inline U32 GBitmap::_getFaceOffset(const U32 face) const
|
||||
{
|
||||
AssertFatal(face < mNumFaces, "GBitmap::_getFaceOffset: invalid face index");
|
||||
|
||||
return mFaceOffsets[face];
|
||||
}
|
||||
|
||||
inline const U8* GBitmap::getBits(const U32 in_mipLevel, const U32 face) const
|
||||
{
|
||||
AssertFatal(in_mipLevel < mNumMipLevels,
|
||||
avar("GBitmap::getBits: mip level out of range: (%d, %d)",
|
||||
in_mipLevel, mNumMipLevels));
|
||||
|
||||
return &mBits[mMipLevelOffsets[in_mipLevel]];
|
||||
return &mBits[_getFaceOffset(face) + mMipLevelOffsets[in_mipLevel]];
|
||||
}
|
||||
|
||||
inline U8* GBitmap::getWritableBits(const U32 in_mipLevel)
|
||||
inline U8* GBitmap::getWritableBits(const U32 in_mipLevel, const U32 face)
|
||||
{
|
||||
AssertFatal(in_mipLevel < mNumMipLevels,
|
||||
avar("GBitmap::getWritableBits: mip level out of range: (%d, %d)",
|
||||
in_mipLevel, mNumMipLevels));
|
||||
|
||||
return &mBits[mMipLevelOffsets[in_mipLevel]];
|
||||
return &mBits[_getFaceOffset(face) + mMipLevelOffsets[in_mipLevel]];
|
||||
}
|
||||
|
||||
inline U8* GBitmap::getAddress(const S32 in_x, const S32 in_y, const U32 mipLevel)
|
||||
inline U8* GBitmap::getAddress(const S32 in_x, const S32 in_y, const U32 mipLevel, const U32 face)
|
||||
{
|
||||
return (getWritableBits(mipLevel) + (U64)(((in_y * getWidth(mipLevel)) + in_x) * mBytesPerPixel));
|
||||
return (getWritableBits(mipLevel, face) + (U64)(((in_y * getWidth(mipLevel)) + in_x) * mBytesPerPixel));
|
||||
}
|
||||
|
||||
inline const U8* GBitmap::getAddress(const S32 in_x, const S32 in_y, const U32 mipLevel) const
|
||||
inline const U8* GBitmap::getAddress(const S32 in_x, const S32 in_y, const U32 mipLevel, const U32 face) const
|
||||
{
|
||||
return (getBits(mipLevel) + ((in_y * getWidth(mipLevel)) + in_x) * mBytesPerPixel);
|
||||
return (getBits(mipLevel, face) + ((in_y * getWidth(mipLevel)) + in_x) * mBytesPerPixel);
|
||||
}
|
||||
|
||||
template<class T, dsize_t mapLength>
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include "core/stream/memStream.h"
|
||||
#include "core/strings/stringFunctions.h"
|
||||
#include "gfx/bitmap/gBitmap.h"
|
||||
#include "gfx/bitmap/bitmapUtils.h"
|
||||
#include "gfx/bitmap/imageUtils.h"
|
||||
#include "gfx/bitmap/loaders/ies/ies_loader.h"
|
||||
#include "platform/profiler.h"
|
||||
|
|
@ -41,12 +42,12 @@
|
|||
#ifndef STB_IMAGE_IMPLEMENTATION
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STB_IMAGE_STATIC
|
||||
#include "stb_image.h"
|
||||
#include "gfx/bitmap/loaders/stb/stb_image.h"
|
||||
#endif
|
||||
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#define STB_IMAGE_WRITE_STATIC
|
||||
#include "stb_image_write.h"
|
||||
#include "gfx/bitmap/loaders/stb/stb_image_write.h"
|
||||
|
||||
#pragma warning(pop)
|
||||
|
||||
|
|
@ -56,6 +57,38 @@ static bool sReadStreamSTB(Stream& stream, GBitmap* bitmap, U32 len);
|
|||
static bool sWriteSTB(const Torque::Path& path, GBitmap* bitmap, U32 compressionLevel);
|
||||
static bool sWriteStreamSTB(const String& bmType, Stream& stream, GBitmap* bitmap, U32 compressionLevel);
|
||||
|
||||
static GFXFormat determineFormat(bool isHDR, bool is16Bit, int numChannels)
|
||||
{
|
||||
if (isHDR)
|
||||
{
|
||||
// we force hdr to 4 channels.
|
||||
return GFXFormatR32G32B32A32F;
|
||||
}
|
||||
else if (is16Bit)
|
||||
{
|
||||
switch (numChannels)
|
||||
{
|
||||
case 1: return GFXFormatL16;
|
||||
case 2: return GFXFormatA8L8; // No native L16A16, but could add one later
|
||||
case 3: return GFXFormatR16G16B16A16;
|
||||
case 4: return GFXFormatR16G16B16A16;
|
||||
}
|
||||
}
|
||||
else // 8-bit
|
||||
{
|
||||
switch (numChannels)
|
||||
{
|
||||
case 1: return GFXFormatA8;
|
||||
case 2: return GFXFormatA8L8;
|
||||
case 3: return GFXFormatR8G8B8;
|
||||
case 4: return GFXFormatR8G8B8A8;
|
||||
}
|
||||
}
|
||||
|
||||
// fallback
|
||||
return GFXFormatR8G8B8A8;
|
||||
}
|
||||
|
||||
// stbi_write callback / rextimmy.
|
||||
static void stbiWriteFunc(void* context, void* data, int size)
|
||||
{
|
||||
|
|
@ -210,119 +243,55 @@ bool sReadSTB(const Torque::Path& path, GBitmap* bitmap)
|
|||
|
||||
}
|
||||
|
||||
if (!stbi_info(path.getFullPath().c_str(), &x, &y, &channels))
|
||||
{
|
||||
const char* stbErr = stbi_failure_reason();
|
||||
const char* filePath = path.getFullPath().c_str();
|
||||
|
||||
if (!stbErr)
|
||||
stbErr = "Unknown Error!";
|
||||
// Detect format
|
||||
bool isHDR = stbi_is_hdr(filePath);
|
||||
bool is16Bit = stbi_is_16_bit(filePath);
|
||||
|
||||
Con::errorf("STB get file info: %s", stbErr);
|
||||
void* data = nullptr;
|
||||
|
||||
if (isHDR) {
|
||||
data = stbi_loadf(filePath, &x, &y, &n, 4);
|
||||
}
|
||||
else if (is16Bit)
|
||||
data = stbi_load_16(filePath, &x, &y, &n, 0);
|
||||
else
|
||||
data = stbi_load(filePath, &x, &y, &n, 0);
|
||||
|
||||
// do this to map 2 channels to 4, 2 channel not supported by gbitmap yet..
|
||||
if (channels == 2)
|
||||
channels = 4;
|
||||
if (!ext.equal("png"))
|
||||
if (!data)
|
||||
{
|
||||
if (stbi_is_16_bit(path.getFullPath().c_str()))
|
||||
{
|
||||
U16* data = stbi_load_16(path.getFullPath().c_str(), &x, &y, &n, channels);
|
||||
|
||||
// if succesful deal make the bitmap, else try other loaders.
|
||||
if (data)
|
||||
{
|
||||
GFXFormat format;
|
||||
if (n == 1)
|
||||
format = GFXFormatL16;
|
||||
else
|
||||
format = GFXFormatR16G16B16A16; // not sure if this is correct.
|
||||
|
||||
bitmap->deleteImage();
|
||||
|
||||
// actually allocate the bitmap space...
|
||||
bitmap->allocateBitmap(x, y,
|
||||
false, // don't extrude miplevels...
|
||||
format); // use determined format...
|
||||
|
||||
U16* pBase = (U16*)bitmap->getBits();
|
||||
|
||||
U32 rowBytes = bitmap->getByteSize();
|
||||
|
||||
dMemcpy(pBase, data, rowBytes);
|
||||
|
||||
stbi_image_free(data);
|
||||
|
||||
PROFILE_END();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ext.equal("hdr"))
|
||||
{
|
||||
// force load to 4 channel.
|
||||
float* data = stbi_loadf(path.getFullPath().c_str(), &x, &y, &n, 0);
|
||||
|
||||
unsigned char* dataChar = stbi__hdr_to_ldr(data, x, y, n);
|
||||
bitmap->deleteImage();
|
||||
// actually allocate the bitmap space...
|
||||
bitmap->allocateBitmap(x, y,
|
||||
false,
|
||||
GFXFormatR8G8B8);
|
||||
|
||||
U8* pBase = (U8*)bitmap->getBits();
|
||||
|
||||
U32 rowBytes = x * y * n;
|
||||
|
||||
dMemcpy(pBase, dataChar, rowBytes);
|
||||
|
||||
//stbi_image_free(data);
|
||||
stbi_image_free(dataChar);
|
||||
|
||||
PROFILE_END();
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned char* data = stbi_load(path.getFullPath().c_str(), &x, &y, &n, channels);
|
||||
|
||||
bitmap->deleteImage();
|
||||
|
||||
GFXFormat format;
|
||||
|
||||
switch (channels) {
|
||||
case 1:
|
||||
format = GFXFormatA8;
|
||||
break;
|
||||
case 2:
|
||||
format = GFXFormatA8L8;
|
||||
break;
|
||||
case 3:
|
||||
format = GFXFormatR8G8B8;
|
||||
break;
|
||||
case 4:
|
||||
format = GFXFormatR8G8B8A8;
|
||||
break;
|
||||
default:
|
||||
PROFILE_END();
|
||||
Con::errorf("sReadSTB() - Failed to load %s: %s", filePath, stbi_failure_reason());
|
||||
return false;
|
||||
}
|
||||
|
||||
// actually allocate the bitmap space...
|
||||
bitmap->allocateBitmap(x, y,
|
||||
false, // don't extrude miplevels...
|
||||
format); // use determined format...
|
||||
// Determine internal GFX format
|
||||
GFXFormat format = determineFormat(isHDR, is16Bit, n);
|
||||
|
||||
U8* pBase = (U8*)bitmap->getBits();
|
||||
// Allocate bitmap
|
||||
bitmap->deleteImage();
|
||||
bitmap->allocateBitmap(x, y, false, format);
|
||||
|
||||
U32 rowBytes = bitmap->getByteSize();
|
||||
|
||||
dMemcpy(pBase, data, rowBytes);
|
||||
//if (isHDR)
|
||||
//{
|
||||
// U16* pBase = (U16*)bitmap->getBits();
|
||||
// const size_t totalPixels = (size_t)x * (size_t)y;
|
||||
// for (size_t i = 0; i < totalPixels * 4; ++i)
|
||||
// {
|
||||
// pBase[i] = convertFloatToHalf(reinterpret_cast<F32*>(data)[i]); // convert F32 -> U16
|
||||
// }
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
U8* dst = (U8*)bitmap->getBits();
|
||||
U32 byteSize = bitmap->getByteSize();
|
||||
dMemcpy(dst, data, byteSize);
|
||||
//}
|
||||
|
||||
stbi_image_free(data);
|
||||
// Check this bitmap for transparency
|
||||
if (channels == 4)
|
||||
bitmap->checkForTransparency();
|
||||
|
||||
bitmap->checkForTransparency();
|
||||
|
||||
PROFILE_END();
|
||||
return true;
|
||||
|
|
@ -331,45 +300,36 @@ bool sReadSTB(const Torque::Path& path, GBitmap* bitmap)
|
|||
bool sReadStreamSTB(Stream& stream, GBitmap* bitmap, U32 len)
|
||||
{
|
||||
PROFILE_SCOPE(sReadStreamSTB);
|
||||
// only used for font at the moment.
|
||||
|
||||
U8* data = new U8[len];
|
||||
stream.read(len, data);
|
||||
Vector<U8> data(len);
|
||||
stream.read(len, data.address());
|
||||
|
||||
S32 width, height, comp = 0;
|
||||
int x, y, n;
|
||||
bool isHDR = stbi_is_hdr_from_memory(data.address(), len);
|
||||
bool is16Bit = stbi_is_16_bit_from_memory(data.address(), len);
|
||||
|
||||
unsigned char* pixelData = stbi_load_from_memory((const U8*)data, (int)len, &width, &height, &comp, 0);
|
||||
void* pixels = nullptr;
|
||||
if (isHDR)
|
||||
pixels = stbi_loadf_from_memory(data.address(), len, &x, &y, &n, 0);
|
||||
else if (is16Bit)
|
||||
pixels = stbi_load_16_from_memory(data.address(), len, &x, &y, &n, 0);
|
||||
else
|
||||
pixels = stbi_load_from_memory(data.address(), len, &x, &y, &n, 0);
|
||||
|
||||
if (!pixelData)
|
||||
if (!pixels)
|
||||
{
|
||||
const char* stbErr = stbi_failure_reason();
|
||||
|
||||
if (!stbErr)
|
||||
stbErr = "Unknown Error!";
|
||||
|
||||
Con::errorf("sReadStreamSTB Error: %s", stbErr);
|
||||
Con::errorf("sReadStreamSTB() - STB load failed: %s", stbi_failure_reason());
|
||||
return false;
|
||||
}
|
||||
|
||||
GFXFormat format = determineFormat(isHDR, is16Bit, n);
|
||||
bitmap->deleteImage();
|
||||
bitmap->allocateBitmap(x, y, false, format);
|
||||
dMemcpy(bitmap->getWritableBits(0), pixels, bitmap->getByteSize());
|
||||
|
||||
//work out what format we need to use - todo floating point?
|
||||
GFXFormat fmt = GFXFormat_FIRST;
|
||||
switch (comp)
|
||||
{
|
||||
case 1: fmt = GFXFormatA8; break;
|
||||
case 2: fmt = GFXFormatA8L8; break; //todo check this
|
||||
case 3: fmt = GFXFormatR8G8B8; break;
|
||||
case 4: fmt = GFXFormatR8G8B8A8; break;
|
||||
}
|
||||
stbi_image_free(pixels);
|
||||
|
||||
bitmap->allocateBitmap(width, height, false, fmt);
|
||||
|
||||
U8* pBase = bitmap->getWritableBits(0);
|
||||
U32 rowBytes = bitmap->getByteSize();
|
||||
dMemcpy(pBase, pixelData, rowBytes);
|
||||
|
||||
dFree(data);
|
||||
dFree(pixelData);
|
||||
bitmap->checkForTransparency();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -401,47 +361,34 @@ bool sWriteSTB(const Torque::Path& path, GBitmap* bitmap, U32 compressionLevel)
|
|||
GFXFormat format = bitmap->getFormat();
|
||||
String ext = path.getExtension();
|
||||
|
||||
|
||||
// we always have at least 1
|
||||
U32 comp = 1;
|
||||
|
||||
if (format == GFXFormatR8G8B8)
|
||||
{
|
||||
comp = 3;
|
||||
}
|
||||
else if (format == GFXFormatR8G8B8A8 || format == GFXFormatR8G8B8X8 || format == GFXFormatR8G8B8A8_LINEAR_FORCE)
|
||||
{
|
||||
comp = 4;
|
||||
}
|
||||
|
||||
if (ext.equal("png"))
|
||||
{
|
||||
stbi_write_png_compression_level = compressionLevel;
|
||||
if (stbi_write_png(path.getFullPath().c_str(), width, height, comp, bitmap->getWritableBits(), 0))
|
||||
if (stbi_write_png(path.getFullPath().c_str(), width, height, bytes, bitmap->getWritableBits(), 0))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ext.equal("tga"))
|
||||
{
|
||||
if (stbi_write_tga(path.getFullPath().c_str(), width, height, comp, bitmap->getWritableBits()))
|
||||
if (stbi_write_tga(path.getFullPath().c_str(), width, height, bytes, bitmap->getWritableBits()))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ext.equal("bmp"))
|
||||
{
|
||||
if (stbi_write_bmp(path.getFullPath().c_str(), width, height, comp, bitmap->getWritableBits()))
|
||||
if (stbi_write_bmp(path.getFullPath().c_str(), width, height, bytes, bitmap->getWritableBits()))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ext.equal("jpg") || ext.equal("jpeg"))
|
||||
{
|
||||
if (stbi_write_jpg(path.getFullPath().c_str(), width, height, comp, bitmap->getWritableBits(), compressionLevel))
|
||||
if (stbi_write_jpg(path.getFullPath().c_str(), width, height, bytes, bitmap->getWritableBits(), compressionLevel))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ext.equal("hdr"))
|
||||
{
|
||||
if (stbi_write_hdr(path.getFullPath().c_str(), width, height, comp, (const F32*)bitmap->getWritableBits()))
|
||||
if (stbi_write_hdr(path.getFullPath().c_str(), width, height, bytes, (const F32*)bitmap->getWritableBits()))
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* stb_image - v2.28 - public domain image loader - http://nothings.org/stb
|
||||
/* stb_image - v2.30 - public domain image loader - http://nothings.org/stb
|
||||
no warranty implied; use at your own risk
|
||||
|
||||
Do this:
|
||||
|
|
@ -48,6 +48,8 @@ LICENSE
|
|||
|
||||
RECENT REVISION HISTORY:
|
||||
|
||||
2.30 (2024-05-31) avoid erroneous gcc warning
|
||||
2.29 (2023-05-xx) optimizations
|
||||
2.28 (2023-01-29) many error fixes, security errors, just tons of stuff
|
||||
2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes
|
||||
2.26 (2020-07-13) many minor fixes
|
||||
|
|
@ -1072,8 +1074,8 @@ static int stbi__addints_valid(int a, int b)
|
|||
return a <= INT_MAX - b;
|
||||
}
|
||||
|
||||
// returns 1 if the product of two signed shorts is valid, 0 on overflow.
|
||||
static int stbi__mul2shorts_valid(short a, short b)
|
||||
// returns 1 if the product of two ints fits in a signed short, 0 on overflow.
|
||||
static int stbi__mul2shorts_valid(int a, int b)
|
||||
{
|
||||
if (b == 0 || b == -1) return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow
|
||||
if ((a >= 0) == (b >= 0)) return a <= SHRT_MAX/b; // product is positive, so similar to mul2sizes_valid
|
||||
|
|
@ -3384,13 +3386,13 @@ static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int stbi__skip_jpeg_junk_at_end(stbi__jpeg *j)
|
||||
static stbi_uc stbi__skip_jpeg_junk_at_end(stbi__jpeg *j)
|
||||
{
|
||||
// some JPEGs have junk at end, skip over it but if we find what looks
|
||||
// like a valid marker, resume there
|
||||
while (!stbi__at_eof(j->s)) {
|
||||
int x = stbi__get8(j->s);
|
||||
while (x == 255) { // might be a marker
|
||||
stbi_uc x = stbi__get8(j->s);
|
||||
while (x == 0xff) { // might be a marker
|
||||
if (stbi__at_eof(j->s)) return STBI__MARKER_none;
|
||||
x = stbi__get8(j->s);
|
||||
if (x != 0x00 && x != 0xff) {
|
||||
|
|
@ -4176,6 +4178,7 @@ typedef struct
|
|||
{
|
||||
stbi_uc *zbuffer, *zbuffer_end;
|
||||
int num_bits;
|
||||
int hit_zeof_once;
|
||||
stbi__uint32 code_buffer;
|
||||
|
||||
char *zout;
|
||||
|
|
@ -4242,9 +4245,20 @@ stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z)
|
|||
int b,s;
|
||||
if (a->num_bits < 16) {
|
||||
if (stbi__zeof(a)) {
|
||||
return -1; /* report error for unexpected end of data. */
|
||||
if (!a->hit_zeof_once) {
|
||||
// This is the first time we hit eof, insert 16 extra padding btis
|
||||
// to allow us to keep going; if we actually consume any of them
|
||||
// though, that is invalid data. This is caught later.
|
||||
a->hit_zeof_once = 1;
|
||||
a->num_bits += 16; // add 16 implicit zero bits
|
||||
} else {
|
||||
// We already inserted our extra 16 padding bits and are again
|
||||
// out, this stream is actually prematurely terminated.
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
stbi__fill_bits(a);
|
||||
}
|
||||
stbi__fill_bits(a);
|
||||
}
|
||||
b = z->fast[a->code_buffer & STBI__ZFAST_MASK];
|
||||
if (b) {
|
||||
|
|
@ -4309,6 +4323,13 @@ static int stbi__parse_huffman_block(stbi__zbuf *a)
|
|||
int len,dist;
|
||||
if (z == 256) {
|
||||
a->zout = zout;
|
||||
if (a->hit_zeof_once && a->num_bits < 16) {
|
||||
// The first time we hit zeof, we inserted 16 extra zero bits into our bit
|
||||
// buffer so the decoder can just do its speculative decoding. But if we
|
||||
// actually consumed any of those bits (which is the case when num_bits < 16),
|
||||
// the stream actually read past the end so it is malformed.
|
||||
return stbi__err("unexpected end","Corrupt PNG");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (z >= 286) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data
|
||||
|
|
@ -4320,7 +4341,7 @@ static int stbi__parse_huffman_block(stbi__zbuf *a)
|
|||
dist = stbi__zdist_base[z];
|
||||
if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]);
|
||||
if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG");
|
||||
if (zout + len > a->zout_end) {
|
||||
if (len > a->zout_end - zout) {
|
||||
if (!stbi__zexpand(a, zout, len)) return 0;
|
||||
zout = a->zout;
|
||||
}
|
||||
|
|
@ -4464,6 +4485,7 @@ static int stbi__parse_zlib(stbi__zbuf *a, int parse_header)
|
|||
if (!stbi__parse_zlib_header(a)) return 0;
|
||||
a->num_bits = 0;
|
||||
a->code_buffer = 0;
|
||||
a->hit_zeof_once = 0;
|
||||
do {
|
||||
final = stbi__zreceive(a,1);
|
||||
type = stbi__zreceive(a,2);
|
||||
|
|
@ -4619,9 +4641,8 @@ enum {
|
|||
STBI__F_up=2,
|
||||
STBI__F_avg=3,
|
||||
STBI__F_paeth=4,
|
||||
// synthetic filters used for first scanline to avoid needing a dummy row of 0s
|
||||
STBI__F_avg_first,
|
||||
STBI__F_paeth_first
|
||||
// synthetic filter used for first scanline to avoid needing a dummy row of 0s
|
||||
STBI__F_avg_first
|
||||
};
|
||||
|
||||
static stbi_uc first_row_filter[5] =
|
||||
|
|
@ -4630,29 +4651,56 @@ static stbi_uc first_row_filter[5] =
|
|||
STBI__F_sub,
|
||||
STBI__F_none,
|
||||
STBI__F_avg_first,
|
||||
STBI__F_paeth_first
|
||||
STBI__F_sub // Paeth with b=c=0 turns out to be equivalent to sub
|
||||
};
|
||||
|
||||
static int stbi__paeth(int a, int b, int c)
|
||||
{
|
||||
int p = a + b - c;
|
||||
int pa = abs(p-a);
|
||||
int pb = abs(p-b);
|
||||
int pc = abs(p-c);
|
||||
if (pa <= pb && pa <= pc) return a;
|
||||
if (pb <= pc) return b;
|
||||
return c;
|
||||
// This formulation looks very different from the reference in the PNG spec, but is
|
||||
// actually equivalent and has favorable data dependencies and admits straightforward
|
||||
// generation of branch-free code, which helps performance significantly.
|
||||
int thresh = c*3 - (a + b);
|
||||
int lo = a < b ? a : b;
|
||||
int hi = a < b ? b : a;
|
||||
int t0 = (hi <= thresh) ? lo : c;
|
||||
int t1 = (thresh <= lo) ? hi : t0;
|
||||
return t1;
|
||||
}
|
||||
|
||||
static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 };
|
||||
|
||||
// adds an extra all-255 alpha channel
|
||||
// dest == src is legal
|
||||
// img_n must be 1 or 3
|
||||
static void stbi__create_png_alpha_expand8(stbi_uc *dest, stbi_uc *src, stbi__uint32 x, int img_n)
|
||||
{
|
||||
int i;
|
||||
// must process data backwards since we allow dest==src
|
||||
if (img_n == 1) {
|
||||
for (i=x-1; i >= 0; --i) {
|
||||
dest[i*2+1] = 255;
|
||||
dest[i*2+0] = src[i];
|
||||
}
|
||||
} else {
|
||||
STBI_ASSERT(img_n == 3);
|
||||
for (i=x-1; i >= 0; --i) {
|
||||
dest[i*4+3] = 255;
|
||||
dest[i*4+2] = src[i*3+2];
|
||||
dest[i*4+1] = src[i*3+1];
|
||||
dest[i*4+0] = src[i*3+0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// create the png data from post-deflated data
|
||||
static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color)
|
||||
{
|
||||
int bytes = (depth == 16? 2 : 1);
|
||||
int bytes = (depth == 16 ? 2 : 1);
|
||||
stbi__context *s = a->s;
|
||||
stbi__uint32 i,j,stride = x*out_n*bytes;
|
||||
stbi__uint32 img_len, img_width_bytes;
|
||||
stbi_uc *filter_buf;
|
||||
int all_ok = 1;
|
||||
int k;
|
||||
int img_n = s->img_n; // copy it into a local for later
|
||||
|
||||
|
|
@ -4664,8 +4712,11 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r
|
|||
a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into
|
||||
if (!a->out) return stbi__err("outofmem", "Out of memory");
|
||||
|
||||
// note: error exits here don't need to clean up a->out individually,
|
||||
// stbi__do_png always does on error.
|
||||
if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG");
|
||||
img_width_bytes = (((img_n * x * depth) + 7) >> 3);
|
||||
if (!stbi__mad2sizes_valid(img_width_bytes, y, img_width_bytes)) return stbi__err("too large", "Corrupt PNG");
|
||||
img_len = (img_width_bytes + 1) * y;
|
||||
|
||||
// we used to check for exact match between raw_len and img_len on non-interlaced PNGs,
|
||||
|
|
@ -4673,189 +4724,137 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r
|
|||
// so just check for raw_len < img_len always.
|
||||
if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG");
|
||||
|
||||
// Allocate two scan lines worth of filter workspace buffer.
|
||||
filter_buf = (stbi_uc *) stbi__malloc_mad2(img_width_bytes, 2, 0);
|
||||
if (!filter_buf) return stbi__err("outofmem", "Out of memory");
|
||||
|
||||
// Filtering for low-bit-depth images
|
||||
if (depth < 8) {
|
||||
filter_bytes = 1;
|
||||
width = img_width_bytes;
|
||||
}
|
||||
|
||||
for (j=0; j < y; ++j) {
|
||||
stbi_uc *cur = a->out + stride*j;
|
||||
stbi_uc *prior;
|
||||
// cur/prior filter buffers alternate
|
||||
stbi_uc *cur = filter_buf + (j & 1)*img_width_bytes;
|
||||
stbi_uc *prior = filter_buf + (~j & 1)*img_width_bytes;
|
||||
stbi_uc *dest = a->out + stride*j;
|
||||
int nk = width * filter_bytes;
|
||||
int filter = *raw++;
|
||||
|
||||
if (filter > 4)
|
||||
return stbi__err("invalid filter","Corrupt PNG");
|
||||
|
||||
if (depth < 8) {
|
||||
if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG");
|
||||
cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place
|
||||
filter_bytes = 1;
|
||||
width = img_width_bytes;
|
||||
// check filter type
|
||||
if (filter > 4) {
|
||||
all_ok = stbi__err("invalid filter","Corrupt PNG");
|
||||
break;
|
||||
}
|
||||
prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above
|
||||
|
||||
// if first row, use special filter that doesn't sample previous row
|
||||
if (j == 0) filter = first_row_filter[filter];
|
||||
|
||||
// handle first byte explicitly
|
||||
for (k=0; k < filter_bytes; ++k) {
|
||||
switch (filter) {
|
||||
case STBI__F_none : cur[k] = raw[k]; break;
|
||||
case STBI__F_sub : cur[k] = raw[k]; break;
|
||||
case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break;
|
||||
case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break;
|
||||
case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break;
|
||||
case STBI__F_avg_first : cur[k] = raw[k]; break;
|
||||
case STBI__F_paeth_first: cur[k] = raw[k]; break;
|
||||
}
|
||||
// perform actual filtering
|
||||
switch (filter) {
|
||||
case STBI__F_none:
|
||||
memcpy(cur, raw, nk);
|
||||
break;
|
||||
case STBI__F_sub:
|
||||
memcpy(cur, raw, filter_bytes);
|
||||
for (k = filter_bytes; k < nk; ++k)
|
||||
cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]);
|
||||
break;
|
||||
case STBI__F_up:
|
||||
for (k = 0; k < nk; ++k)
|
||||
cur[k] = STBI__BYTECAST(raw[k] + prior[k]);
|
||||
break;
|
||||
case STBI__F_avg:
|
||||
for (k = 0; k < filter_bytes; ++k)
|
||||
cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1));
|
||||
for (k = filter_bytes; k < nk; ++k)
|
||||
cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1));
|
||||
break;
|
||||
case STBI__F_paeth:
|
||||
for (k = 0; k < filter_bytes; ++k)
|
||||
cur[k] = STBI__BYTECAST(raw[k] + prior[k]); // prior[k] == stbi__paeth(0,prior[k],0)
|
||||
for (k = filter_bytes; k < nk; ++k)
|
||||
cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes], prior[k], prior[k-filter_bytes]));
|
||||
break;
|
||||
case STBI__F_avg_first:
|
||||
memcpy(cur, raw, filter_bytes);
|
||||
for (k = filter_bytes; k < nk; ++k)
|
||||
cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1));
|
||||
break;
|
||||
}
|
||||
|
||||
if (depth == 8) {
|
||||
if (img_n != out_n)
|
||||
cur[img_n] = 255; // first pixel
|
||||
raw += img_n;
|
||||
cur += out_n;
|
||||
prior += out_n;
|
||||
} else if (depth == 16) {
|
||||
if (img_n != out_n) {
|
||||
cur[filter_bytes] = 255; // first pixel top byte
|
||||
cur[filter_bytes+1] = 255; // first pixel bottom byte
|
||||
}
|
||||
raw += filter_bytes;
|
||||
cur += output_bytes;
|
||||
prior += output_bytes;
|
||||
} else {
|
||||
raw += 1;
|
||||
cur += 1;
|
||||
prior += 1;
|
||||
}
|
||||
raw += nk;
|
||||
|
||||
// this is a little gross, so that we don't switch per-pixel or per-component
|
||||
if (depth < 8 || img_n == out_n) {
|
||||
int nk = (width - 1)*filter_bytes;
|
||||
#define STBI__CASE(f) \
|
||||
case f: \
|
||||
for (k=0; k < nk; ++k)
|
||||
switch (filter) {
|
||||
// "none" filter turns into a memcpy here; make that explicit.
|
||||
case STBI__F_none: memcpy(cur, raw, nk); break;
|
||||
STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break;
|
||||
STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break;
|
||||
STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break;
|
||||
STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break;
|
||||
STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break;
|
||||
STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break;
|
||||
}
|
||||
#undef STBI__CASE
|
||||
raw += nk;
|
||||
} else {
|
||||
STBI_ASSERT(img_n+1 == out_n);
|
||||
#define STBI__CASE(f) \
|
||||
case f: \
|
||||
for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \
|
||||
for (k=0; k < filter_bytes; ++k)
|
||||
switch (filter) {
|
||||
STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break;
|
||||
STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break;
|
||||
STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break;
|
||||
STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break;
|
||||
STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break;
|
||||
STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break;
|
||||
STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break;
|
||||
}
|
||||
#undef STBI__CASE
|
||||
|
||||
// the loop above sets the high byte of the pixels' alpha, but for
|
||||
// 16 bit png files we also need the low byte set. we'll do that here.
|
||||
if (depth == 16) {
|
||||
cur = a->out + stride*j; // start at the beginning of the row again
|
||||
for (i=0; i < x; ++i,cur+=output_bytes) {
|
||||
cur[filter_bytes+1] = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we make a separate pass to expand bits to pixels; for performance,
|
||||
// this could run two scanlines behind the above code, so it won't
|
||||
// intefere with filtering but will still be in the cache.
|
||||
if (depth < 8) {
|
||||
for (j=0; j < y; ++j) {
|
||||
stbi_uc *cur = a->out + stride*j;
|
||||
stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes;
|
||||
// unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit
|
||||
// png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop
|
||||
// expand decoded bits in cur to dest, also adding an extra alpha channel if desired
|
||||
if (depth < 8) {
|
||||
stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range
|
||||
stbi_uc *in = cur;
|
||||
stbi_uc *out = dest;
|
||||
stbi_uc inb = 0;
|
||||
stbi__uint32 nsmp = x*img_n;
|
||||
|
||||
// note that the final byte might overshoot and write more data than desired.
|
||||
// we can allocate enough data that this never writes out of memory, but it
|
||||
// could also overwrite the next scanline. can it overwrite non-empty data
|
||||
// on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel.
|
||||
// so we need to explicitly clamp the final ones
|
||||
|
||||
// expand bits to bytes first
|
||||
if (depth == 4) {
|
||||
for (k=x*img_n; k >= 2; k-=2, ++in) {
|
||||
*cur++ = scale * ((*in >> 4) );
|
||||
*cur++ = scale * ((*in ) & 0x0f);
|
||||
for (i=0; i < nsmp; ++i) {
|
||||
if ((i & 1) == 0) inb = *in++;
|
||||
*out++ = scale * (inb >> 4);
|
||||
inb <<= 4;
|
||||
}
|
||||
if (k > 0) *cur++ = scale * ((*in >> 4) );
|
||||
} else if (depth == 2) {
|
||||
for (k=x*img_n; k >= 4; k-=4, ++in) {
|
||||
*cur++ = scale * ((*in >> 6) );
|
||||
*cur++ = scale * ((*in >> 4) & 0x03);
|
||||
*cur++ = scale * ((*in >> 2) & 0x03);
|
||||
*cur++ = scale * ((*in ) & 0x03);
|
||||
for (i=0; i < nsmp; ++i) {
|
||||
if ((i & 3) == 0) inb = *in++;
|
||||
*out++ = scale * (inb >> 6);
|
||||
inb <<= 2;
|
||||
}
|
||||
if (k > 0) *cur++ = scale * ((*in >> 6) );
|
||||
if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03);
|
||||
if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03);
|
||||
} else if (depth == 1) {
|
||||
for (k=x*img_n; k >= 8; k-=8, ++in) {
|
||||
*cur++ = scale * ((*in >> 7) );
|
||||
*cur++ = scale * ((*in >> 6) & 0x01);
|
||||
*cur++ = scale * ((*in >> 5) & 0x01);
|
||||
*cur++ = scale * ((*in >> 4) & 0x01);
|
||||
*cur++ = scale * ((*in >> 3) & 0x01);
|
||||
*cur++ = scale * ((*in >> 2) & 0x01);
|
||||
*cur++ = scale * ((*in >> 1) & 0x01);
|
||||
*cur++ = scale * ((*in ) & 0x01);
|
||||
} else {
|
||||
STBI_ASSERT(depth == 1);
|
||||
for (i=0; i < nsmp; ++i) {
|
||||
if ((i & 7) == 0) inb = *in++;
|
||||
*out++ = scale * (inb >> 7);
|
||||
inb <<= 1;
|
||||
}
|
||||
if (k > 0) *cur++ = scale * ((*in >> 7) );
|
||||
if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01);
|
||||
if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01);
|
||||
if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01);
|
||||
if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01);
|
||||
if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01);
|
||||
if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01);
|
||||
}
|
||||
if (img_n != out_n) {
|
||||
int q;
|
||||
// insert alpha = 255
|
||||
cur = a->out + stride*j;
|
||||
|
||||
// insert alpha=255 values if desired
|
||||
if (img_n != out_n)
|
||||
stbi__create_png_alpha_expand8(dest, dest, x, img_n);
|
||||
} else if (depth == 8) {
|
||||
if (img_n == out_n)
|
||||
memcpy(dest, cur, x*img_n);
|
||||
else
|
||||
stbi__create_png_alpha_expand8(dest, cur, x, img_n);
|
||||
} else if (depth == 16) {
|
||||
// convert the image data from big-endian to platform-native
|
||||
stbi__uint16 *dest16 = (stbi__uint16*)dest;
|
||||
stbi__uint32 nsmp = x*img_n;
|
||||
|
||||
if (img_n == out_n) {
|
||||
for (i = 0; i < nsmp; ++i, ++dest16, cur += 2)
|
||||
*dest16 = (cur[0] << 8) | cur[1];
|
||||
} else {
|
||||
STBI_ASSERT(img_n+1 == out_n);
|
||||
if (img_n == 1) {
|
||||
for (q=x-1; q >= 0; --q) {
|
||||
cur[q*2+1] = 255;
|
||||
cur[q*2+0] = cur[q];
|
||||
for (i = 0; i < x; ++i, dest16 += 2, cur += 2) {
|
||||
dest16[0] = (cur[0] << 8) | cur[1];
|
||||
dest16[1] = 0xffff;
|
||||
}
|
||||
} else {
|
||||
STBI_ASSERT(img_n == 3);
|
||||
for (q=x-1; q >= 0; --q) {
|
||||
cur[q*4+3] = 255;
|
||||
cur[q*4+2] = cur[q*3+2];
|
||||
cur[q*4+1] = cur[q*3+1];
|
||||
cur[q*4+0] = cur[q*3+0];
|
||||
for (i = 0; i < x; ++i, dest16 += 4, cur += 6) {
|
||||
dest16[0] = (cur[0] << 8) | cur[1];
|
||||
dest16[1] = (cur[2] << 8) | cur[3];
|
||||
dest16[2] = (cur[4] << 8) | cur[5];
|
||||
dest16[3] = 0xffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (depth == 16) {
|
||||
// force the image data from big-endian to platform-native.
|
||||
// this is done in a separate pass due to the decoding relying
|
||||
// on the data being untouched, but could probably be done
|
||||
// per-line during decode if care is taken.
|
||||
stbi_uc *cur = a->out;
|
||||
stbi__uint16 *cur16 = (stbi__uint16*)cur;
|
||||
|
||||
for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) {
|
||||
*cur16 = (cur[0] << 8) | cur[1];
|
||||
}
|
||||
}
|
||||
|
||||
STBI_FREE(filter_buf);
|
||||
if (!all_ok) return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -5161,9 +5160,11 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
|
|||
// non-paletted with tRNS = constant alpha. if header-scanning, we can stop now.
|
||||
if (scan == STBI__SCAN_header) { ++s->img_n; return 1; }
|
||||
if (z->depth == 16) {
|
||||
for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is
|
||||
for (k = 0; k < s->img_n && k < 3; ++k) // extra loop test to suppress false GCC warning
|
||||
tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is
|
||||
} else {
|
||||
for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger
|
||||
for (k = 0; k < s->img_n && k < 3; ++k)
|
||||
tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -7984,4 +7985,4 @@ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
*/
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue