Io image raw
Io_image_raw is a basic example plugin. This wiki page is intended as a walkthrough of the code in order to help you better understand the SDK.
Contents
Functionality
Code Walkthrough
Class Declarations
class CRawLoader;
class CRawImage : public CLxImpl_Image { public: CRawLoader *rawLoader; ILxUnknownID inst_ifc; FILE *open_file; CLxUser_ThreadService threadSvc; virtual ~CRawImage(); virtual void img_Size ( unsigned int *w, unsigned int *h); virtual LXtPixelFormat img_Format (void); virtual LxResult img_GetPixel ( unsigned int x, unsigned int y, LXtPixelFormat type, void *pixel); virtual const void * img_GetLine ( unsigned int y, LXtPixelFormat type, void *buf); };
Here we declare an Interface for opaque image access called CRawImage. As we want it to access and examine an image object, we inherit from CLxImpl_Image. Of note inside the class is a pointer back to the active RAW loader, a basic threading service, and redeclarations of some virtual functions that will allow us to examine the image we choose.
class CRawLoader : public CLxImpl_Loader1 { LXtImageTarget1 img_target; FILE *open_file; /* * Factory to create image interface for opaque image I/O. */ CLxPolymorph<CRawImage> raw_factory; /* * Import options from the Image I/O panel. */ unsigned width; unsigned height; unsigned channels; bool interleaved; unsigned depth; bool byteOrderPC; unsigned headerSize; void GetImportOptions (); size_t scanlineSize; public: CRawLoader (); LxResult load_Recognize (LXtLoadAccess1 *) LXx_OVERRIDE; const LXtImageTarget1* load_GetImageTarget () const { return &img_target; } FILE * load_GetOpenFile () const { return open_file; } unsigned load_GetChannels () const { return channels; } unsigned load_GetDepth () const { return depth; } bool load_GetByteOrderPC () const { return byteOrderPC; } unsigned load_GetHeaderSize () const { return headerSize; } size_t load_GetScanlineSize () const { return scanlineSize; } LxResult load_LoadInstance ( LXtLoadAccess1 *load, void **ppvObj) LXx_OVERRIDE; LxResult load_LoadObject ( LXtLoadAccess1 *load, ILxUnknownID dest) LXx_OVERRIDE; LxResult load_Cleanup (LXtLoadAccess1 *load) LXx_OVERRIDE; static LXtTagInfoDesc descInfo[]; };
We want to create a loader, so we inherit from CLxImpl_Loader1. Inside the class, we declare a file pointer and a factory. Following that, we import options from the Image I/O panel. The remaining functions, except for the last one, simply retrieve information about the target file or actually load the file.
class CRawSaver : public CLxImpl_Saver { public: virtual LxResult sav_Save ( ILxUnknownID source, const char *filename, ILxUnknownID monitor) LXx_OVERRIDE; static LXtTagInfoDesc descInfo[]; };
We want to create a saver object, so we have this class inherit from CLxImpl_Saver. Inside the class we have the main function, which performs the meat of saving the file, followed by the server tags.
Server Tags
LXtTagInfoDesc CRawLoader::descInfo[] = { { LXsLOD_CLASSLIST, LXa_IMAGE }, { LXsLOD_DOSPATTERN, "*.raw" }, { LXsSRV_USERNAME, "Raw Image" }, { 0 } };
The tags, here corresponding to the loader, indicate that the server can load images and provide a file pattern.
LXtTagInfoDesc CRawSaver::descInfo[] = { { LXsSAV_OUTCLASS, LXa_IMAGE }, { LXsSAV_DOSTYPE, "RAW" }, { LXsSRV_USERNAME, "Raw Image" }, { 0 } };
Here, the tags indicate that the saver class above saves objects of the image class with the file extension RAW.
Initialization
void initialize () { LXx_ADD_SERVER (Saver, CRawSaver, "raw_RGB"); LXx_ADD_SERVER (Loader1, CRawLoader, "raw_RGB"); }
Here we indicate that we intend to add two servers to modo. One of them is of the type saver, depends on the class CRawSaver, and is called raw_RGB. The other does the same, but with Loader1, CRawLoader, and raw_RGB respectively.
Helper Functions
static int GetUserInt (const char *prefKey, int defaultValue = 0) { int value = defaultValue; CLxReadUserValue ruvUser; if (ruvUser.Query (prefKey)) { value = ruvUser.GetInt (); } return value; }
This function queries the user for an int value and returns that value.
Implementations
void CRawLoader::GetImportOptions () { width = GetUserInt ("ImageIO.PRAW.width", 1024); height = GetUserInt ("ImageIO.PRAW.height", 1024); int channelsIndex = GetUserInt ("ImageIO.PRAW.channels.index", 1); switch (channelsIndex) { case 0: channels = 1; break; case 1: channels = 3; break; case 2: channels = 4; break; } interleaved = GetUserInt ("ImageIO.PRAW.channels.index", true) ? true : false; int depthIndex = GetUserInt ("ImageIO.PRAW.depth.index", 0); switch (depthIndex) { case 0: depth = 8; break; case 1: depth = 16; break; case 2: depth = 32; break; } byteOrderPC = GetUserInt ("ImageIO.PRAW.byte.order.index", true) ? true : false; headerSize = GetUserInt ("ImageIO.PRAW.header.size", 0); }
This method retrieves the options we will be using to import the file
CRawLoader::CRawLoader () { raw_factory.AddInterface (new CLxIfc_Image<CRawImage>); }
The constructor indicates that the loader will be using the image interface.
LxResult CRawLoader::load_Recognize ( LXtLoadAccess1 *load) { LxResult result = LXe_FAILED; open_file = fopen (load->filename, "rb"); if (open_file) { /* * This format is a bit unusual, in that there is no known data * signature we can compare against, so we instead check that * the file size is equal to the image size and depth specified * in the RAW import options, plus the header size. */ GetImportOptions (); /* * Fetch the file size. */ FSEEK (open_file, 0, SEEK_END); size_t length = FTELL (open_file); FSEEK (open_file, 0, SEEK_SET); /* * Compare against the image size specified by the options. */ scanlineSize = width * channels * (depth / 8); size_t optionSize = height * scanlineSize; if (length == optionSize) { /* * Determine the specified image format. */ switch (channels) { case 1: { img_target.type = (depth == 8) ? LXiIMP_GREY8 : LXiIMP_GREYFP; break; } case 3: { img_target.type = (depth == 8) ? LXiIMP_RGB24 : LXiIMP_RGBFP; break; } case 4: { img_target.type = (depth == 8) ? LXiIMP_RGBA32 : LXiIMP_RGBAFP; break; } } /* * Use the option width and height. */ img_target.w = width; img_target.h = height; img_target.ncolor = 0; load->found = lx::LookupGUID (LXa_IMAGE); /* * Use the opaque image APIs for large images. * (Greater than 8K x 8K) */ load->opaque = width * height > 512 * 512; load->target = &img_target; result = LXe_OK; } } return result; }
Recognize method scans the file to see if it finds the data it can understand. In our case we look for a simple header string.
LxResult CRawLoader::load_LoadInstance (LXtLoadAccess1 *load, void **ppvObj) { LxResult result = LXe_OK; ILxUnknownID inst = raw_factory.Spawn (0); if (inst) { open_file = fopen (load->filename, "rb"); if (open_file) { CRawImage *rawImage = LXCWxOBJ(inst, CRawImage); rawImage->rawLoader = this; rawImage->inst_ifc = inst; rawImage->open_file = open_file; /* * Lose our reference to the open file, * since it now persists with the opaque image. */ open_file = 0; ppvObj[0] = inst; } } return result; }
This function opens the instance we choose.
LxResult CRawLoader::load_LoadObject ( LXtLoadAccess1 *load, ILxUnknownID dest) { CLxUser_ImageWrite wimg (dest); CLxUser_Monitor mon; LXtImageByte *buf; unsigned int y, w, h; LxResult rc, rd; wimg.Size (&w, &h); buf = new LXtImageByte[w * channels]; if (!buf) return LXe_OUTOFMEMORY; if (load->monitor) { mon.set (load->monitor); mon.Init (h); } rc = LXe_OK; for (y = 0; y < h && LXx_OK (rc); y++) { if (fread (buf, 1, w * channels, open_file) != w * channels) rc = LXe_FAILED; else { rd = wimg.SetLine (y, wimg.Format (), buf); if (LXx_FAIL (rd)) rc = rd; else if (mon.Step ()) rc = LXe_ABORT; } } delete[] buf; return rc; }
The load-object method gets an object to fill with data, in this case an image. Using a user wrapper for the image we proceed to fill it from the file line by line. An optional monitor is used to check for user abort.
LxResult CRawLoader::load_Cleanup ( LXtLoadAccess1 *load) { if (open_file) { fclose (open_file); open_file = 0; } return LXe_OK; }
Cleanup is called after a failed recognize or after load-object completes, regardless of the outcome.
void CRawImage::img_Size (unsigned int *w, unsigned int *h) { *w = rawLoader->load_GetImageTarget ()->w; *h = rawLoader->load_GetImageTarget ()->h; } LXtPixelFormat CRawImage::img_Format (void) { return rawLoader->load_GetImageTarget ()->type; } LxResult CRawImage::img_GetPixel ( unsigned int x, unsigned int y, LXtPixelFormat type, void *pixel) { CLxLoc_ThreadMutex mux; if (threadSvc.NewMutex (mux)) { size_t scanlineSize = rawLoader->load_GetScanlineSize (); mux.Enter (); FSEEK (open_file, scanlineSize * y, SEEK_SET); FSEEK (open_file, x * rawLoader->load_GetChannels (), SEEK_CUR); if (type == rawLoader->load_GetImageTarget ()->type) { fread (pixel, 1, rawLoader->load_GetChannels (), open_file); } else { unsigned char r, g, b; fread (&r, 1, 1, open_file); fread (&g, 1, 1, open_file); fread (&b, 1, 1, open_file); LXtImageByte *dstPixel = reinterpret_cast<LXtImageByte*>(pixel); switch (type) { case LXiIMP_RGB24: *dstPixel++ = r; *dstPixel++ = g; *dstPixel++ = b; break; case LXiIMP_RGBA32: *dstPixel++ = r; *dstPixel++ = g; *dstPixel++ = b; *dstPixel = 255; break; case LXiIMP_RGBFP: { LXtImageFloat *dstPixelF = reinterpret_cast<LXtImageFloat*>(pixel); *dstPixelF++ = r / 255.0f; *dstPixelF++ = g / 255.0f; *dstPixelF++ = b / 255.0f; break; } case LXiIMP_RGBAFP: { LXtImageFloat *dstPixelF = reinterpret_cast<LXtImageFloat*>(pixel); *dstPixelF++ = r / 255.0f; *dstPixelF++ = g / 255.0f; *dstPixelF++ = b / 255.0f; *dstPixelF = 1.0; break; } } } mux.Leave (); } return LXe_OK; } const void * CRawImage::img_GetLine ( unsigned int y, LXtPixelFormat type, void *buf) { CLxLoc_ThreadMutex mux; if (threadSvc.NewMutex (mux)) { size_t scanlineSize = rawLoader->load_GetScanlineSize (); mux.Enter (); FSEEK (open_file, scanlineSize * y, SEEK_SET); /* * Read the scanline directly into the buffer. */ if (type == rawLoader->load_GetImageTarget ()->type) { fread (buf, 1, scanlineSize, open_file); mux.Leave (); } else if (type == LXiIMP_RGBA32) { LXtImageByte *nativeBuf = new LXtImageByte[scanlineSize]; fread (nativeBuf, 1, scanlineSize, open_file); mux.Leave (); LXtImageByte *srcIter = nativeBuf; LXtImageByte *dstIter = reinterpret_cast<LXtImageByte*>(buf); for (unsigned x = 0; x < rawLoader->load_GetImageTarget ()->w; ++x) { *dstIter++ = *srcIter++; *dstIter++ = *srcIter++; *dstIter++ = *srcIter++; *dstIter++ = 255; } delete [] nativeBuf; } else { mux.Leave (); } } return buf; }
These functions are utilities that retrieve information that deal with the image in question
LxResult CRawSaver::sav_Save ( ILxUnknownID source, const char *filename, ILxUnknownID monitor) { CLxUser_Image image (source); CLxUser_Monitor mon; FILE *fp; unsigned char buf[4]; unsigned int x, y, w, h; fp = fopen (filename, "wb"); if (!fp) return LXe_FAILED; if (fwrite ("DEMO Raw RGB", 1, 12, fp) != 12) return LXe_FAILED; image.Size (&w, &h); buf[0] = w / 256; buf[1] = w % 256; buf[2] = h / 256; buf[3] = h % 256; if (fwrite (buf, 1, 4, fp) != 4) return LXe_FAILED; if (monitor) { mon.set (monitor); mon.Init (h); } for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { image.GetPixel (x, y, LXiIMP_RGBA32, buf); if (fwrite (buf, 1, 3, fp) != 3) return LXe_FAILED; } if (mon.Step ()) return LXe_ABORT; } fclose (fp); return LXe_OK; }
This is very much the reverse of the load-object method. We extract pixels from the image (just to be different, although by line would be faster) and write them to the file.