Advanced Guide
Memory I/O and Others
For the majority of the formats, with the exception of the ones that use external SDKs, the I/O is done by the imBinFile module.
This module can be configured to access other types of media by implementing a driver. There are some predefined drivers see Reference / Utilities / Binary File Access.
One very useful is the Memory Buffer where you can read and write a file in memory. The activation is very simple, it needs to happen just before the
imFileOpen/imFileNew
functions. But the file name must be a pointer to animBinMemoryFileName
structure instead of a string. Se the example bellow:
int old_mode = imBinFileSetCurrentModule(IM_MEMFILE);
imBinMemoryFileName MemFileName; // This structure must exists while the file remains open.
MemFileName.buffer = NULL; // Let the library initializes the buffer,
// but it must be freed the the application, free(MemFileName.buffer) MemFileName.size = 1024; // The initial size
MemFileName.reallocate = 1.5; // The reallocation will increase 50% the buffer.
// This is used only when writing with a variable buffer.
// Use 0 to fix the buffer size.
int error;
imFile* ifile = imFileNew((const char*)&MemFileName, "GIF", &error);
imBinFileSetCurrentModule(old_mode); // The mode needs to be active only for the imFileOpen/imFileNew call.
if (error != IM_ERR_NONE) ....
Another driver interesting is the Subfile where you can read and write from a file that is already open. This is very important for formats that can have an embedded format inside. In this module the file_name is a pointer to an
imBinFile
structure from any other module that uses the imBinFile functions. TheimBinFileSize
will return the full file size, but theimBinFileSeekTo
andimBinFileTell
functions will compensate the position when the subfile was open.Using
imBinFileSetCurrentModule(IM_SUBFILE)
just like the example above will allow you to open a subfile using theimFileOpen/imFileNew
functions.
New Operations
An operation complexity is directly affected by the number of data types it will operate.
If it is only one, than it is as simple as:
void DoProc(imbyte* data, int width, int height) { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { // Do something int offset = y * width + x; data[offset] = 0; } } } void SampleProc(imImage* image) { // a loop for all the color planes for (int d = 0; d < image->depth; d++) { // Notice that the same operation may be used to process each color component DoProc((imbyte*)image->data[d], image->width, image->height); } }Or if you want to use templates to allow a more number of types:
template <class T> void DoProc2(const T* src_data, T* dst_data, int count) { for (int i = 0; i < count; i++) { src_data[i] = dst_data[i]; // or a more low level approach *src_data++ = *dst_data++; } } // This is a sample that do not depends on the spatial distribution of the data. // It uses data[0], the pointer where all depths depends on. void SampleProc2(const imImage* src_image, imImage* dst_image) { int total_count = src_image->count * src_image->depth; switch(src_image->data_type) { case IM_BYTE: DoProc((imbyte*)src_image->data[0], (imbyte*)dst_image->data[0], total_count); break; case IM_USHORT: DoProc((imushort*)src_image->data[0], (imushort*)dst_image->data[0], total_count); break; case IM_INT: DoProc((int*)src_image->data[0], (int*)dst_image->data[0], total_count); break; case IM_FLOAT: DoProc((float*)src_image->data[0], (float*)dst_image->data[0], total_count); break; case IM_CFLOAT: DoProc((imcfloat*)src_image->data[0], (imcfloat*)dst_image->data[0], total_count); break; } }The first sample can be implemented in C, but the second sample can not, it must be in C++. Check the manual and the source code for many operations already available.
New File Formats
Again the easiest way is to look at the source code of an already implemented format. The RAS, BMP, TGA and SGI formats are very simple to follow.
Basically you have to implement a class that inherits from imFormat and implement its virtual methods. You can use the imBinFile functions for I/O or use an external SDK.
For more information see Reference / Image Storage / File Format SDK.
Counters
To add support for the counter callback to a new operation is very simple. The following code shows how:
int counter = imCounterBegin("Process Test 1"); imCounterTotal(counter, count_steps, "Processing"); for (int i = 0; i < count_steps; i++) { // Do something if (!imCounterInc(counter)) return IM_ERR_COUNTER; } imCounterEnd(counter);Every time you call
imCounterTotal
between aimCounterBegin
/imCounterEnd
for the same counter means that you are starting a count at that counter. So one operation can be composed by many sub-operations and still have a counter to display progress. For example, each call to theimFileReadImageData
starts a new count for the same counter.A nice thing to do when counting is not to display too small progress. To accomplish that in the implementation of the counter callback consider a minimum delay from one display to another.
Names Convention
To improve the readability of the code we use a very simple naming convention:
- Global Functions and Types - "im[Object][Action]" using first capitals (imFileOpen)
- Local Functions and Types - "i[Object][Action]" using first capitals (iTIFFGetCompIndex)
- Local Static Variables - same as local functions and types (iFormatCount)
- Local Static Tables - same as local functions and types with "Table" suffix (iTIFFCompTable)
- Variables and Members - no prefix, all lower case (width)
- Defines and Enumerations - all capitals (IM_ERR_NONE)
C x C++ Usage
The library main API is in C. We adopt this because of the many C programmers out there. Some of the API is also available in C++ for those addicted to classes.
Internally C++ is used to implement the format driver base architecture. A virtual base class that every drivers inherits from. This made a lot of things easier to the driver development. But we keep it simple, no multiple inheritance, no exception handling, no complicated classes.
But because we need several data types C++ templates were inevitable used (since we do not like long macros everywhere). But they are used only for processing functions, not classes.