We have the typed images, and a generic image definition, with a basic (and very fragile, for the moment) text input/output format. It's time to decide how to organise the input-output to external standard formats, like PNG, TIFF, JPEG...
standard libraries
The first decision is to use only the standard and well-known,
well-tested, well-distributed libraries to do it, and only #include
the relevant headers; it may seem obvious, but it was not done like
that until now. This means that:
- to build the megawave library, you need the headers for these
libraries on your system: you will need the
libxxx-devpackages on Debian-based linux distributions, or thelibxxx-develpackages on RPM-based linux distributions; out of the linux world, I don't know yet... - to use the megawave library, you need the run-time versions of these
libraries (ie
libxxxpackages)
This should not be a problem, as packages systems handle the dependencies. We may also provide an "all-inclusive" binary version of megawave, for quick tests. And it would solve all sorts of API/compiler/standard compatability issues.
And by "standard libraries", I mean libpng, libtiff, libjpeg, jasper, giflib, netpbm, ...
By the way, I can't find any "standard" library for the BMP files. But who need BMP file?
structures and formats
The main question is then «how should we handle the multiplicity?». We
currently have 3 internal raster image formats (based on uint8,
uint16 and flt32); we may add sooner or later the other natural
types (uint32, uint64 and flt64), counting to 6. If we implement
the color images as different structures (which is not certain), it
would double this number. Then, later, why not 3D-images (if they are
not the same thing as movies).
The natural image file formats are png and jpeg. Legacy formats,
like pnm, tiff, gif can be useful too. And jpeg2000. Same for
scientific data formats like netcdf of dicom. And then we will
have to think about extracting frames from mpeg, avc, vp (and
variants), huffyuv (lossless) video?
Basically, we have many internal structures, and we want to be able to fill them from many different kinds of external files.
in / out matrix
Let's just consider 3 internal structures, and 3 file formats. The
naive design, ie one function per input/output couple, looks like
that:
design 1.dot design 1.svg
So, 9 functions for the moment, with lots of code duplication. Then,
if we want to add one file format, we need to write 4 functions (the
generic load_foo plus the three load_foo_xx interfaces), and
modify 3 other ones (load_xximage). This will grow exponentially,
the the result will be a lot of unmaintainable code.
multiple in / multiple out
One simplification is to remove the matrix, and allow direct dialogue
between the "image" side and the "file" side.
design 2 direct.dot design 2 direct.svg
The it would be somehow more easy to add a new format, we still need to update many new functions each time we want to add support for a new format; this big "cross-interaction" at the center has to be removed.
front-ends
The same design, with front-ends added, limits the dependencies.
design 2 indirect.dot design 2 indirect.svg
Now, each time we add a new format, there is one new function to write
(load_foo) and one to update (load_file). And the two different
parts of the code ("image" and "file") are connected by a single
interface.
One problem remains: the load_file functions must be able to return
different outputs, depending on who called them.
This could be done by:
- returning a
(void *)pointer; no type checking, dangerous, buggy, etc. - filling a pointer passed as argument; doing so, the memory would be
allocated in the "file" side of the code, then used in the
other side, and the
load_xxfunction would need to accessmw_new_xximage, which means cross-dependencies. - returning a generic
mw_image; but this generic structure is an overlay, not a building brick for themw_xximages
details
The solution is to split the process in two parts:
- gather meta-information from the file (shape, data type,
layers...), then create the empty
mw_xximagestructure and allocate the correct memory size - gather the image data, filled in the memory area whose address is passed as a parameter
This work-flow would look like that:
design detail.dot design detail.svg
This design is modular; for example,
- it's easy to change the way the image format is selected (parameter? file name extension? magic number?)
- it's possible to collect only the meta-data, or only the raster data
- for any new file format, we need to add two functions and update two functions
- for any new internal format, we just need to implement the same calls as the already-defined formats do