Io image raw

From The Foundry MODO SDK wiki
Revision as of 20:37, 9 September 2013 by Adissid (Talk | contribs) (Created page with "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. ==Functionality== ==Code Walk...")

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

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.

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.