From 6b6e268138258e454d06ce7056dcb1e51f7dac6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=91=D1=80=D0=B0=D0=BD=D0=B8=D0=BC=D0=B8=D1=80=20=D0=9A?= =?UTF-8?q?=D0=B0=D1=80=D0=B0=D1=9F=D0=B8=D1=9B?= Date: Wed, 12 Aug 2026 16:48:53 -0700 Subject: [PATCH] Use zero depth to mean not a volume texture. --- include/bimg/bimg.h | 19 ++++++++++++++ src/bimg_p.h | 2 +- src/image.cpp | 49 +++++++++++++++++------------------- src/image_cubemap_filter.cpp | 12 ++++----- src/image_decode.cpp | 2 +- src/image_decode_wic.cpp | 2 +- src/image_encode.cpp | 4 +-- tools/meta/meta.cpp | 2 +- tools/texturec/texturec.cpp | 6 ++--- 9 files changed, 57 insertions(+), 41 deletions(-) diff --git a/include/bimg/bimg.h b/include/bimg/bimg.h index 2b33507..b3b699d 100644 --- a/include/bimg/bimg.h +++ b/include/bimg/bimg.h @@ -22,7 +22,10 @@ namespace bx namespace bimg { + /// typedef void (*PackFn)(void*, const float*); + + /// typedef void (*UnpackFn)(float*, const void*); /// Texture format enum. @@ -225,6 +228,7 @@ namespace bimg bool cubeMap; //!< Texture is cubemap. }; + /// struct ImageContainer { bx::AllocatorI* m_allocator; @@ -249,6 +253,7 @@ namespace bimg bool m_srgb; }; + /// struct ImageMip { TextureFormat::Enum m_format; @@ -262,6 +267,7 @@ namespace bimg const uint8_t* m_data; }; + /// struct ImageBlockInfo { uint8_t bitsPerPixel; @@ -309,6 +315,19 @@ namespace bimg /// Converts image source format to string. const char* getName(ImageParser::Enum _parser); + /// Returns true if image is a volume (3D) texture. + inline bool isVolume(const ImageContainer& _imageContainer) + { + return 0 != _imageContainer.m_depth; + } + + /// Returns the number of depth slices; one for anything that is not a volume + /// (3D) texture. + inline uint32_t imageGetNumSlices(const ImageContainer& _imageContainer) + { + return 0 != _imageContainer.m_depth ? _imageContainer.m_depth : 1; + } + /// Returns number of mip-maps required for complete mip-map chain. uint8_t imageGetNumMips( TextureFormat::Enum _format diff --git a/src/bimg_p.h b/src/bimg_p.h index 450a166..0fdee3b 100644 --- a/src/bimg_p.h +++ b/src/bimg_p.h @@ -72,7 +72,7 @@ namespace bimg TextureFormat::Enum m_format; uint16_t m_width; uint16_t m_height; - uint16_t m_depth; + uint16_t m_depth; //!< Zero if this is not a volume (3D) texture. uint16_t m_numLayers; uint8_t m_numMips; bool m_cubeMap; diff --git a/src/image.cpp b/src/image.cpp index d3794cc..e6f903a 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -651,10 +651,9 @@ namespace bimg const uint32_t pitch = _imageContainer->m_width*16; const uint32_t slice = _imageContainer->m_height*pitch; - for (uint32_t zz = 0, depth = _imageContainer->m_depth; zz < depth; ++zz) + for (uint32_t zz = 0, depth = mip.m_depth; zz < depth; ++zz) { - const uint32_t srcDataStep = uint32_t(bx::floor(zz * _imageContainer->m_depth / float(depth) ) ); - const uint8_t* srcData = &mip.m_data[srcDataStep*slice]; + const uint8_t* srcData = &mip.m_data[zz*slice]; imageRgba32fToLinear(const_cast(srcData), mip.m_width, mip.m_height, 1, pitch, srcData); } @@ -697,10 +696,9 @@ namespace bimg const uint32_t pitch = _imageContainer->m_width*16; const uint32_t slice = _imageContainer->m_height*pitch; - for (uint32_t zz = 0, depth = _imageContainer->m_depth; zz < depth; ++zz) + for (uint32_t zz = 0, depth = mip.m_depth; zz < depth; ++zz) { - const uint32_t srcDataStep = uint32_t(bx::floor(zz * _imageContainer->m_depth / float(depth) ) ); - const uint8_t* srcData = &mip.m_data[srcDataStep*slice]; + const uint8_t* srcData = &mip.m_data[zz*slice]; imageRgba32fToGamma(const_cast(srcData), mip.m_width, mip.m_height, 1, pitch, srcData); } @@ -3624,14 +3622,10 @@ namespace bimg _width = bx::max(blockWidth * minBlockX, ( (_width + blockWidth - 1) / blockWidth)*blockWidth); _height = bx::max(blockHeight * minBlockY, ( (_height + blockHeight - 1) / blockHeight)*blockHeight); - _depth = bx::max(1, _depth); _numLayers = bx::max(1, _numLayers); - // Reject dimensions that don't fit in 16 bits. No GPU supports textures - // larger than 65535 in any dimension, and the image format/size helpers - // (and ImageContainer::m_numLayers) are 16-bit; allowing larger values - // would truncate and under-allocate, leading to heap overflow writes when - // pixel data is copied in. + const uint32_t numSlices = bx::max(1, _depth); + if (_width > UINT16_MAX || _height > UINT16_MAX || _depth > UINT16_MAX @@ -3640,11 +3634,9 @@ namespace bimg return NULL; } - const uint8_t numMips = _hasMips ? imageGetNumMips(_format, _width, _height, _depth) : 1; - uint64_t size = imageGetSize(NULL, _width, _height, _depth, _cubeMap, _hasMips, uint16_t(_numLayers), _format); + const uint8_t numMips = _hasMips ? imageGetNumMips(_format, _width, _height, numSlices) : 1; + uint64_t size = imageGetSize(NULL, _width, _height, numSlices, _cubeMap, _hasMips, uint16_t(_numLayers), _format); - // ImageContainer::m_size/m_offset are 32-bit, so reject anything that - // can't be addressed within that range to avoid truncated allocations. if (size > UINT32_MAX) { return NULL; @@ -4114,6 +4106,7 @@ namespace bimg uint32_t dxgiFormat = 0; uint32_t arraySize = 1; + bool volume = 0 != (caps[1] & DDSCAPS2_VOLUME); if (DDPF_FOURCC == (pixelFlags & DDPF_FOURCC) && DDS_DX10 == fourcc) { @@ -4129,8 +4122,12 @@ namespace bimg uint32_t miscFlags2; total += bx::read(_reader, miscFlags2, _err); + + volume = DDS_DX10_DIMENSION_TEXTURE3D == dims; } + depth = volume ? bx::max(1, depth) : 0; + BX_UNUSED(total); if (!_err->isOk() ) @@ -4622,7 +4619,7 @@ namespace bimg _imageContainer.m_offset = (uint32_t)offset; _imageContainer.m_width = width; _imageContainer.m_height = height; - _imageContainer.m_depth = bx::max(depth, 1); + _imageContainer.m_depth = depth; _imageContainer.m_format = format; _imageContainer.m_orientation = Orientation::R0; _imageContainer.m_numLayers = uint16_t(bx::max(numberOfArrayElements, 1) ); @@ -5027,11 +5024,11 @@ namespace bimg const uint32_t numFaces = bx::max(faceCount, 1); const uint32_t width = bx::max(pixelWidth, 1); const uint32_t height = bx::max(pixelHeight, 1); - const uint32_t depth = bx::max(pixelDepth, 1); + const uint32_t depth = pixelDepth; const bool cubeMap = (6 == faceCount); if (16 < numMips - || (cubeMap && 1 != depth) ) + || (cubeMap && 0 != depth) ) { BX_ERROR_SET(_err, BIMG_ERROR, "KTX2: Invalid header values."); return false; @@ -5529,7 +5526,7 @@ namespace bimg total += bx::write(_writer, typeSize, _err); total += bx::write(_writer, _width, _err); total += bx::write(_writer, _height, _err); - total += bx::write(_writer, _depth > 1 ? _depth : uint32_t(0), _err); // pixelDepth = 0 for 2D/cube + total += bx::write(_writer, _depth, _err); // pixelDepth = 0 for 2D/cube total += bx::write(_writer, _numLayers > 1 ? _numLayers : uint32_t(0), _err); // layerCount = 0 for non-array total += bx::write(_writer, uint32_t(_cubeMap ? 6 : 1), _err); // faceCount total += bx::write(_writer, numMips, _err); // levelCount @@ -5679,7 +5676,7 @@ namespace bimg total += bx::write(_writer, typeSize, _err); total += bx::write(_writer, _imageContainer.m_width, _err); total += bx::write(_writer, _imageContainer.m_height, _err); - total += bx::write(_writer, _imageContainer.m_depth > 1 ? _imageContainer.m_depth : uint32_t(0), _err); + total += bx::write(_writer, _imageContainer.m_depth, _err); total += bx::write(_writer, _imageContainer.m_numLayers > 1 ? uint32_t(_imageContainer.m_numLayers) : uint32_t(0), _err); total += bx::write(_writer, uint32_t(_imageContainer.m_cubeMap ? 6 : 1), _err); total += bx::write(_writer, numMips, _err); @@ -5877,7 +5874,7 @@ namespace bimg _imageContainer.m_offset = (uint32_t)offset; _imageContainer.m_width = width; _imageContainer.m_height = height; - _imageContainer.m_depth = depth; + _imageContainer.m_depth = 1 < depth ? depth : 0; _imageContainer.m_format = format; _imageContainer.m_orientation = Orientation::R0; _imageContainer.m_numLayers = 1; @@ -7415,7 +7412,7 @@ namespace bimg | DDSD_WIDTH | DDSD_PIXELFORMAT | DDSD_CAPS - | (1 < _depth ? DDSD_DEPTH : 0) + | (0 != _depth ? DDSD_DEPTH : 0) | (1 < _numMips ? DDSD_MIPMAPCOUNT : 0) | (isCompressed(_format) ? DDSD_LINEARSIZE : DDSD_PITCH) ) @@ -7464,7 +7461,7 @@ namespace bimg uint32_t caps[4] = { uint32_t(DDSCAPS_TEXTURE | (1 < _numMips ? DDSCAPS_COMPLEX|DDSCAPS_MIPMAP : 0) ), - uint32_t(_cubeMap ? DDSCAPS2_CUBEMAP|DSCAPS2_CUBEMAP_ALLSIDES : 0), + uint32_t(_cubeMap ? DDSCAPS2_CUBEMAP|DSCAPS2_CUBEMAP_ALLSIDES : (0 != _depth ? DDSCAPS2_VOLUME : 0) ), 0, 0, }; @@ -7482,7 +7479,7 @@ namespace bimg if (UINT32_MAX != dxgiFormat) { total += bx::write(_writer, dxgiFormat, _err); - total += bx::write(_writer, uint32_t(1 < _depth ? DDS_DX10_DIMENSION_TEXTURE3D : DDS_DX10_DIMENSION_TEXTURE2D), _err); // dims + total += bx::write(_writer, uint32_t(0 != _depth ? DDS_DX10_DIMENSION_TEXTURE3D : DDS_DX10_DIMENSION_TEXTURE2D), _err); // dims total += bx::write(_writer, uint32_t(_cubeMap ? DDS_DX10_MISC_TEXTURECUBE : 0), _err); // miscFlags total += bx::write(_writer, uint32_t(_numLayers), _err); // arraySize total += bx::write(_writer, uint32_t(0), _err); // miscFlags2 @@ -7555,7 +7552,7 @@ namespace bimg total += bx::write(_writer, tfi.m_fmt, _err); // glBaseInternalFormat total += bx::write(_writer, _width, _err); total += bx::write(_writer, _height, _err); - total += bx::write(_writer, _depth > 1 ? _depth : uint32_t(0), _err); // For 2D and cube textures pixelDepth must be 0. + total += bx::write(_writer, _depth, _err); // For 2D and cube textures pixelDepth must be 0. total += bx::write(_writer, _numLayers > 1 ? _numLayers : uint32_t(0), _err); // numberOfArrayElements; If the texture is not an array texture, numberOfArrayElements must equal 0. total += bx::write(_writer, _cubeMap ? uint32_t(6) : uint32_t(1), _err); // numberOfFaces; For cubemaps and cubemap arrays this should be 6. For non cubemaps this should be 1 total += bx::write(_writer, uint32_t(_numMips), _err); diff --git a/src/image_cubemap_filter.cpp b/src/image_cubemap_filter.cpp index d969a95..f5b4715 100644 --- a/src/image_cubemap_filter.cpp +++ b/src/image_cubemap_filter.cpp @@ -197,7 +197,7 @@ namespace bimg { BX_ERROR_SCOPE(_err); - if (_input.m_depth != 1 + if (0 != _input.m_depth && _input.m_numLayers != 1 && _input.m_format != TextureFormat::RGBA32F && _input.m_width/2 != _input.m_height) @@ -217,7 +217,7 @@ namespace bimg , _input.m_format , dstWidth , dstWidth - , 1 + , 0 , 1 , true , false @@ -339,7 +339,7 @@ namespace bimg { BX_ERROR_SCOPE(_err); - if (_input.m_depth != 1 + if (0 != _input.m_depth && _input.m_numLayers != 1 && _input.m_format != TextureFormat::RGBA32F && ( (_input.m_width != _input.m_height*6) || (_input.m_width*6 != _input.m_height) ) ) @@ -358,7 +358,7 @@ namespace bimg , _input.m_format , dstWidth , dstWidth - , 1 + , 0 , 1 , true , false @@ -413,7 +413,7 @@ namespace bimg const uint32_t dstPitch = dstWidth*16; const float texelSize = 1.0f / float(dstWidth); - ImageContainer* output = imageAlloc(_allocator, TextureFormat::RGBA32F, dstWidth, dstWidth, 1, 1, true, false); + ImageContainer* output = imageAlloc(_allocator, TextureFormat::RGBA32F, dstWidth, dstWidth, 0, 1, true, false); if (NULL == output) { @@ -1177,7 +1177,7 @@ namespace bimg } } - ImageContainer* output = imageAlloc(_allocator, TextureFormat::RGBA32F, input->m_width, input->m_width, 1, 1, true, true); + ImageContainer* output = imageAlloc(_allocator, TextureFormat::RGBA32F, input->m_width, input->m_width, 0, 1, true, true); if (NULL == output) { diff --git a/src/image_decode.cpp b/src/image_decode.cpp index 99f26f1..6607be0 100644 --- a/src/image_decode.cpp +++ b/src/image_decode.cpp @@ -1405,7 +1405,7 @@ namespace bimg _imageContainer.m_offset = UINT32_MAX; _imageContainer.m_width = _width; _imageContainer.m_height = _height; - _imageContainer.m_depth = 1; + _imageContainer.m_depth = 0; _imageContainer.m_numLayers = 1; _imageContainer.m_numMips = 1; _imageContainer.m_hasAlpha = false; diff --git a/src/image_decode_wic.cpp b/src/image_decode_wic.cpp index 1a0b857..4461ae6 100644 --- a/src/image_decode_wic.cpp +++ b/src/image_decode_wic.cpp @@ -142,7 +142,7 @@ namespace bimg if (0 < width && 0 < height) { - image = imageAlloc(_allocator, TextureFormat::RGBA8, width, height, 1, 1, false, false); + image = imageAlloc(_allocator, TextureFormat::RGBA8, width, height, 0, 1, false, false); if (NULL != image) { hr = converter->CopyPixels(NULL, width * 4, image->m_size, (BYTE*)image->m_data); diff --git a/src/image_encode.cpp b/src/image_encode.cpp index c088f39..bb2068f 100644 --- a/src/image_encode.cpp +++ b/src/image_encode.cpp @@ -630,9 +630,9 @@ namespace bimg const uint32_t dstPitch = _dst->m_width*16; const uint32_t dstSlice = _dst->m_height*dstPitch; - for (uint32_t zz = 0, depth = _dst->m_depth; zz < depth; ++zz, dstData += dstSlice) + for (uint32_t zz = 0, depth = dstMip.m_depth; zz < depth; ++zz, dstData += dstSlice) { - const uint32_t srcDataStep = uint32_t(bx::floor(zz * _src->m_depth / float(_dst->m_depth) ) ); + const uint32_t srcDataStep = uint32_t(bx::floor(zz * srcMip.m_depth / float(depth) ) ); const uint8_t* srcData = &srcMip.m_data[srcDataStep*srcSlice]; void* result = stbir_resize( diff --git a/tools/meta/meta.cpp b/tools/meta/meta.cpp index ea2bce6..78e4623 100644 --- a/tools/meta/meta.cpp +++ b/tools/meta/meta.cpp @@ -128,7 +128,7 @@ static bool processFile(bx::AllocatorI* _allocator, const char* _filePath) , info.m_height ); - if (info.m_depth > 1) + if (0 != info.m_depth) { bx::printf(" x %u", info.m_depth); } diff --git a/tools/texturec/texturec.cpp b/tools/texturec/texturec.cpp index 765d06c..bd8a1ef 100644 --- a/tools/texturec/texturec.cpp +++ b/tools/texturec/texturec.cpp @@ -170,13 +170,13 @@ bimg::ImageContainer* convert(bx::AllocatorI* _allocator, const void* _inputData { outputWidth = bx::max(blockWidth * minBlockX, ( ( (outputWidth>>1) + blockWidth - 1) / blockWidth )*blockWidth); outputHeight = bx::max(blockHeight * minBlockY, ( ( (outputHeight>>1) + blockHeight - 1) / blockHeight)*blockHeight); - outputDepth = bx::max(outputDepth>>1, 1u); + outputDepth = 0 != outputDepth ? bx::max(outputDepth>>1, 1u) : 0; } } if (_options.equirect) { - if (outputDepth == 1 + if (outputDepth == 0 && outputWidth/2 == outputHeight) { if (outputWidth/2 > _options.maxSize) @@ -195,7 +195,7 @@ bimg::ImageContainer* convert(bx::AllocatorI* _allocator, const void* _inputData } else if (_options.strip) { - if (outputDepth == 1 + if (outputDepth == 0 && ( (outputWidth == outputHeight*6) || (outputWidth*6 == outputHeight) ) ) { const bool horizontal = outputWidth == outputHeight*6;