Dawoodoz
If someone gets a new version of the library with additional data members and forgets to update the header, it can lead to crashes from not having allocated enough memory in the stack.
Because the library uses the same header as the rest of the program, you'd get compile errors if the files get out of sync. It's especially not a problem in this case because the library has been made single-header, so the header and implementation are in the same file. Having the state struct in the header portion allows it to be allocated on the stack. That said, the library already makes some heap allocations, so one more isn't a big deal. It could be worth returning an opaque pointer from msf_gif_begin() to avoid having any implementation details in the header, as a matter of cleanliness/user-friendliness.
Dawoodoz
I would just hide the "incremental API" with its exposed types and go for the "all-at-once" API. I don't really see a use case for streaming live video in a gif image
The incremental API only requires keeping one frame in memory at a time, so its memory usage is bounded and relatively small. Keeping all frames in memory at once for a particularly high-resolution, high-framerate, and/or long gif can easily eat up gigabytes of memory. Also, the incremental API allows spreading the work over multiple frames, meaning you can easily encode the gif as it's being recorded in a single-threaded program, without taking a huge frame hitch when the recording ends. Finally, the incremental API is simpler, and the behavior of the all-at-once API can be emulated using the incremental one anyway, if needed. The ONLY advantage of the all-at-once API is that it can be more aggressively multi-threaded. In all other respects, it is strictly inferior.
Dawoodoz
"MSF_GIF_HPP" could be made a little longer in case of naming conflicts.
In what way would this help? Just making it arbitrarily longer isn't going to do anything. Uniqueness is what matters for name conflicts, not length. The only way there will realistically be a name collision is if there is another header also named "msf_gif.h" in the same codebase, which is extremely unlikely and if it happens, they can just rename the include guard.
Dawoodoz
Personally, I never cared for *.hpp because it just adds confusion.
.h is for C-compatible headers,
.hpp is for C++ headers. What's confusing about that?
Dawoodoz
"msf_gif_begin" does not say that it is writing if someone's just skimming the calls without looking it up in this external header's documentation
If someone manages to introduce a bug in their code because they couldn't be bothered to read a few lines of documentation (which is right there inline with the function definitions they're referencing) and blindly assumed that a function which takes a file path string as a parameter, in a library whose sole functionality is writing files to disk, does not write anything to disk, that's on them. There's nothing I can do.
Dawoodoz
"rbits, gbits, bbits" looks like bit-masks of undefined size for uint32_t pixels. Changing the size of int with a new compiler or target can change the effect of a bit-wise negation and cause problems in complex compression algorithms that you don't want to revisit on a bad day.
In this case they're numbers of bits rather than bitmasks, and aren't used during compression.
Dawoodoz
Use of "bool" can be a pain across different languages with different non-zero definitions of true. Just something to think about if you plan to release a future dynamic link library without changing the API. Defining it as an integer and saying that non-zero means true will be more beginner friendly for those who don't know that a bool is an integer under the hood with all its pitfalls. It can also serve for additional states without breaking backward compatibility.
Any non-zero value will be handled correctly by the library, and I completely don't buy the idea that having an integer argument for what is conceptually a boolean would be somehow more beginner-friendly than using a boolean argument for what is conceptually a boolean. Also, the possibility of adding additional states is mutually exclusive with treating any non-zero value as true.
Dawoodoz
Any exposed types of undefined size would be a usability problem in a future dynamic link library, because the ABI might change depending on which compiler was used and the other language may have different names for the types.
Can you be more specific? In what circumstances would this be a problem?
Dawoodoz
"const char * path" will not work from folders with eastern symbols unless you specify that it takes multi-char UTF-8 encoding. UTF-32 is a lot easier to work with in algorithms by not having complex pattern matching to find a symbol, but you will still have basic combination characters.
Setting aside the questionableness of calling non-ASCII text "eastern symbols", that's a good point. On reasonable operating systems it's fine because the filepath gets passed straight to fopen() without modification, but on windows fopen() doesn't handle UTF8 text, so I'll probably need to add a platform-specific code path there to handle UTF8->UTF16 conversion. Using UTF32 here wouldn't make any sense.