Skip to content

Commit 32162fb

Browse files
authored
Merge pull request #52 from agruzdev/message-proc-2
New API for diagnostic messages
2 parents 868a246 + 027449d commit 32162fb

4 files changed

Lines changed: 336 additions & 76 deletions

File tree

Source/FreeImage.h

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -859,6 +859,21 @@ FI_ENUM(FREE_IMAGE_ALPHA_OPERATION) {
859859
};
860860

861861

862+
// Message struct ---------------------------------------------------------
863+
864+
865+
FI_STRUCT(FIMESSAGE);
866+
867+
FI_ENUM(FREE_IMAGE_SEVERITY) {
868+
FISEV_VERBOSE = 0,
869+
FISEV_INFO = 200,
870+
FISEV_WARNING = 300,
871+
FISEV_ERROR = 400,
872+
FISEV_NONE = 0x1000
873+
};
874+
875+
876+
862877

863878
#ifdef __cplusplus
864879
extern "C" {
@@ -895,6 +910,40 @@ DLL_API void DLL_CALLCONV FreeImage_SetOutputMessageStdCall(FreeImage_OutputMess
895910
DLL_API void DLL_CALLCONV FreeImage_SetOutputMessage(FreeImage_OutputMessageFunction omf);
896911
DLL_API void DLL_CALLCONV FreeImage_OutputMessageProc(int fif, const char *fmt, ...);
897912

913+
914+
/**
915+
* ctx - Opaque handle for message processing context
916+
* func - Address of message processing function
917+
*/
918+
typedef void (DLL_CALLCONV *FreeImage_ProcessMessageFunction)(void* ctx, const FIMESSAGE* msg);
919+
920+
/**
921+
* Adds a message processing function for this thread only.
922+
* The `func` must not be nullptr.
923+
* On success retuns non-zero ID of the registered processor, that can later be used in FreeImage_RemoveProcessMessageFunction()
924+
*/
925+
DLL_API uint32_t DLL_CALLCONV FreeImage_AddProcessMessageFunction(void* ctx, FreeImage_ProcessMessageFunction func);
926+
927+
/**
928+
* Removes a previously added message processor.
929+
* Returns true on success, false if processor ID is not valid.
930+
*/
931+
DLL_API FIBOOL DLL_CALLCONV FreeImage_RemoveProcessMessageFunction(uint32_t id);
932+
933+
/**
934+
* Invokes message processor for this message instance
935+
*/
936+
DLL_API void DLL_CALLCONV FreeImage_ProcessMessage(const FIMESSAGE* msg);
937+
938+
939+
DLL_API FIMESSAGE* DLL_CALLCONV FreeImage_CreateMessage(FREE_IMAGE_FORMAT scope, FREE_IMAGE_SEVERITY severity, const char* what);
940+
DLL_API void DLL_CALLCONV FreeImage_DeleteMessage(FIMESSAGE* msg);
941+
DLL_API FREE_IMAGE_FORMAT DLL_CALLCONV FreeImage_GetMessageScope(const FIMESSAGE* msg);
942+
DLL_API FREE_IMAGE_SEVERITY DLL_CALLCONV FreeImage_GetMessageSeverity(const FIMESSAGE* msg);
943+
DLL_API const char* DLL_CALLCONV FreeImage_GetMessageString(const FIMESSAGE* msg);
944+
945+
946+
898947
// Allocate / Clone / Unload routines ---------------------------------------
899948

900949
DLL_API FIBITMAP *DLL_CALLCONV FreeImage_Allocate(int width, int height, int bpp, unsigned red_mask FI_DEFAULT(0), unsigned green_mask FI_DEFAULT(0), unsigned blue_mask FI_DEFAULT(0));

Source/FreeImage.hpp

Lines changed: 160 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include <cassert>
1414
#include <filesystem>
15+
#include <functional>
1516
#include <limits>
1617
#include <memory>
1718
#include <optional>
@@ -250,6 +251,15 @@ namespace fi
250251
eSrcAlpha = FIAO_SrcAlpha
251252
};
252253

254+
enum class Severity
255+
{
256+
eVerbose = FISEV_VERBOSE,
257+
eInfo = FISEV_INFO,
258+
eWarning = FISEV_WARNING,
259+
eError = FISEV_ERROR,
260+
eNone = FISEV_NONE
261+
};
262+
253263

254264
class ImageError
255265
: public std::runtime_error
@@ -325,6 +335,148 @@ namespace fi
325335
}
326336

327337

338+
/**
339+
* Const access to a message allocated inside FreeImage
340+
*/
341+
class MessageView
342+
{
343+
public:
344+
explicit
345+
MessageView(const FIMESSAGE* handle)
346+
: mHandle(handle)
347+
{ }
348+
349+
MessageView(const MessageView&) = default;
350+
MessageView(MessageView&&) noexcept = default;
351+
352+
// no need to release
353+
~MessageView() = default;
354+
355+
MessageView& operator=(const MessageView&) = default;
356+
MessageView& operator=(MessageView&&) noexcept = default;
357+
358+
operator bool() const {
359+
return (mHandle != nullptr);
360+
}
361+
362+
const char* GetCString() const {
363+
return FreeImage_GetMessageString(mHandle);
364+
}
365+
366+
std::string GetString() const {
367+
const char* str = FreeImage_GetMessageString(mHandle);
368+
return str ? std::string(str) : std::string();
369+
}
370+
371+
Severity GetSeverity() const {
372+
return static_cast<Severity>(FreeImage_GetMessageSeverity(mHandle));
373+
}
374+
375+
ImageFormat GetScope() const {
376+
return static_cast<ImageFormat>(FreeImage_GetMessageScope(mHandle));
377+
}
378+
379+
protected:
380+
const FIMESSAGE* mHandle;
381+
};
382+
383+
384+
/**
385+
* Mutable user allocated message instance
386+
*/
387+
class Message
388+
: public MessageView
389+
{
390+
public:
391+
Message(ImageFormat scope, Severity severity, const char* what)
392+
: MessageView(FREEIMAGERE_CHECKED_CALL(FreeImage_CreateMessage, static_cast<FREE_IMAGE_FORMAT>(scope), static_cast<FREE_IMAGE_SEVERITY>(severity), what))
393+
{ }
394+
395+
Message(ImageFormat scope, Severity severity, const std::string& what)
396+
: Message(scope, severity, what.c_str())
397+
{ }
398+
399+
Message(const Message&) = delete;
400+
401+
Message(Message&& other) noexcept
402+
: MessageView(other.mHandle)
403+
{
404+
other.mHandle = nullptr;
405+
}
406+
407+
// not virtual, just reusing methods MessageView
408+
~Message() {
409+
FreeImage_DeleteMessage(GetMutable());
410+
}
411+
412+
Message& operator=(const Message&) = delete;
413+
414+
Message& operator=(Message&& other) noexcept
415+
{
416+
if (this != &other) {
417+
FreeImage_DeleteMessage(GetMutable());
418+
mHandle = other.mHandle;
419+
other.mHandle = nullptr;
420+
}
421+
return *this;
422+
}
423+
424+
private:
425+
FIMESSAGE* GetMutable() {
426+
// Message created by FreeImage_CreateMessage() is actually a dynamic object, so can safely remove const
427+
return const_cast<FIMESSAGE*>(mHandle);
428+
}
429+
};
430+
431+
432+
using MessageProcessFunction = std::function<void(const MessageView&)>;
433+
434+
435+
/**
436+
* Registers a message processor for life time of this guard instance
437+
*/
438+
class MessageProcessFunctionGuard
439+
{
440+
public:
441+
MessageProcessFunctionGuard(MessageProcessFunction func)
442+
: mFunc(std::move(func))
443+
{
444+
mProcId = FREEIMAGERE_CHECKED_CALL(FreeImage_AddProcessMessageFunction, this, &MessageProcessFunctionGuard::Wrapper);
445+
}
446+
447+
MessageProcessFunctionGuard(const MessageProcessFunctionGuard&) = delete;
448+
MessageProcessFunctionGuard(MessageProcessFunctionGuard&& other) noexcept = delete;
449+
450+
~MessageProcessFunctionGuard()
451+
{
452+
if (mProcId) {
453+
FreeImage_RemoveProcessMessageFunction(mProcId);
454+
}
455+
}
456+
457+
MessageProcessFunctionGuard& operator=(const MessageProcessFunctionGuard&) = delete;
458+
MessageProcessFunctionGuard& operator=(MessageProcessFunctionGuard&& other) noexcept = delete;
459+
460+
/**
461+
* Do not call FreeImage_RemoveProcessMessageFunction() in destcrutor
462+
*/
463+
uint32_t Release() {
464+
const uint32_t tmp = mProcId;
465+
mProcId = 0;
466+
return tmp;
467+
}
468+
469+
private:
470+
static
471+
void DLL_CALLCONV Wrapper(void* thiz, const FIMESSAGE* msg) {
472+
static_cast<MessageProcessFunctionGuard*>(thiz)->mFunc(MessageView(msg));
473+
}
474+
475+
MessageProcessFunction mFunc;
476+
uint32_t mProcId{ 0 };
477+
};
478+
479+
328480

329481
class Tag
330482
{
@@ -359,13 +511,15 @@ namespace fi
359511

360512
Tag& operator=(Tag&& other) noexcept
361513
{
362-
if (mCallDelete && mHandle) {
363-
FreeImage_DeleteTag(mHandle);
514+
if (this != &other) {
515+
if (mCallDelete) {
516+
FreeImage_DeleteTag(mHandle);
517+
}
518+
mHandle = other.mHandle;
519+
mCallDelete = other.mCallDelete;
520+
other.mHandle = nullptr;
521+
other.mCallDelete = false;
364522
}
365-
mHandle = other.mHandle;
366-
mCallDelete = other.mCallDelete;
367-
other.mHandle = nullptr;
368-
other.mCallDelete = false;
369523
return *this;
370524
}
371525

0 commit comments

Comments
 (0)