Quantize function missing in Windows .dll

Hi there. I’m currently writing a rust wrapper around the C API (here’s the repo), and the nvttContextQuantize function seems to be missing from the Windows .dll.

Hi mijalko, it’s fantastic to hear you’re putting together a Rust wrapper! Please let me know if there’s anything I can do to help.

Thank you for the bug report, that’s a good find! It’s now fixed in our trunk, and the fix will be in the next release.

It turned out the root cause was the C wrapper declared nvttContextQuantize() like this:

NVTT_API void nvttContextQuantize(const NvttContext * context, NvttSurface * tex, const NvttCompressionOptions * compressionOptions);

But when I was putting together the definition, I missed the const qualifier on the first argument:

void nvttContextQuantize(NvttContext * context, NvttSurface * tex, const NvttCompressionOptions * compressionOptions)

As a result, the nvttContextQuantize symbol was optimized out.

As a quick heads-up, we know of one other bug in the 3.1.6 NVTT C wrapper headers that will be fixed in the next release: the first two elements of the NvttError enum are out of order with respect to the nvtt::Error enum. The correct order should look like this:

typedef enum
} NvttError;

In 3.1.6, this should only affect the value passed to the OutputOptions error handler if the width, height, depth, or mipmap count are negative (in which case it should output 1, which is NVTT_Error_InvalidInput).

Hope this helps!


Hi Neil, thanks for the detailed response! Good to hear it’ll be fixed in the next release. For the time being, I’ve chosen to just remove the quantize function from my wrapper.

As a side note, you asked if there’s anything that might help with my rust wrapper. As a bit of feedback, the two pain points I found were functions internally performing bitshifts, and file I/O.

There are many functions performing bitshifts internally, which cause undefined behaviour if values > 32 are passed in. I assume that they still perform bitshifts based on a previous open sourced version. But it’s not entirely clear what the lower/upper bounds are. Generally just documenting this more would be helpful.

Secondly, a major issue I’ve found is file I/O. To my knowledge, the character encoding of paths in NVTT must be ANSI/ASCII, but rust allows file paths to be any valid UTF8. This causes problems when functions ask for char *, as rust paths generally do not work the same as char *. This is further complicated because different operating systems have different path encoding schemes.

A very simple work around would be to allow file loading from raw bytes, e.g. nvttLoadFile(NvttSurface *img, const void *data, size_t len, format FileFormat), where img will be updated to hold the file data, data/size correspond to raw texture data, and format can be from an enum of all supported files, e.g. png, dds, etc. This removes any need for file paths. A similar function could exist such as nvttWriteFile for saving. Not sure if this API is necessarily correct, but anything that could keep file I/O in memory would be useful.

Hi mijalko,

Quick update: we released NVTT 3.2.0 today, which includes the fixes for nvttContextQuantize and the nvtt::Error enum! In case it helps, I’ve attached unified diffs for the NVTT headers between versions 3.1.6 and 3.2.0 below:

nvtt_wrapper.h.diff (7.5 KB)
nvtt_lowlevel.h.diff (13.9 KB)
nvtt.h.diff (17.1 KB)

I can confirm as well that Surface::quantize still performs bit shifts, which means that the code will invoke undefined behavior if bits is negative or greater than 31 (or for Surface::toRGBE, if exponentBits is negative or greater than 5 or mantissaBits is negative or greater than 31), and that Surface::load() is currently using fopen(), so fileName on Windows will be ANSI, OEM, or UTF-8 (depending on whether an app has called SetFileApisToOEM() or setlocale() previously) - thanks for reporting these! We’re looking at how to improve these, but unfortunately did not have the changes ready in time for the 3.2.0 release.

(Some specific ideas we’ve been thinking about for Surface::quantize() have been to do bounds-checking now that we have the message callback API, or to replace the bit shifts with exp2f. I’ve also been thinking about what fuzz-testing the parts of the API we haven’t tested yet with UndefinedBehaviorSanitizer would look like.)

Thanks again!