Difference between revisions of "Io image raw"

From The Foundry MODO SDK wiki
Jump to: navigation, search
(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...")
 
Line 8: Line 8:
 
===Class Declarations===
 
===Class Declarations===
  
class CRawLoader;
+
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 CRawImage : public CLxImpl_Image
 
  class CRawImage : public CLxImpl_Image
Line 38: Line 38:
 
  };
 
  };
  
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.
+
We want to create a loader, so we inherit from CLxImpl_Loader1. Inside the class, we declare a file pointer and a [[Factory_Object|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.
  
 
<pre>
 
<pre>
Line 119: Line 119:
 
};</pre>
 
};</pre>
  
We want to create a loader, so we inherit from CLxImpl_Loader1. Inside the class, we declare a file pointer and a [[Factory_Object|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.
+
We want to create a [[Saver_Object|saver object]], so we have this class inherit from [[Io_(lx-io.hpp)#Saver|CLxImpl_Saver]]. Inside the class we have the main function, which performs the meat of saving the file, followed by the server tags.
  
 
  class CRawSaver : public CLxImpl_Saver
 
  class CRawSaver : public CLxImpl_Saver
Line 131: Line 131:
 
         static LXtTagInfoDesc descInfo[];
 
         static LXtTagInfoDesc descInfo[];
 
  };
 
  };
 
We want to create a [[Saver_Object|saver object]], so we have this class inherit from [[Io_(lx-io.hpp)#Saver|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|Server Tags]]===
 
===[[Server_Tags|Server Tags]]===
 +
 +
The tags, here corresponding to the loader, indicate that the [[Overview#nexus_Servers|server]] can load images and provide a file pattern.
  
 
  LXtTagInfoDesc CRawLoader::descInfo[] = {
 
  LXtTagInfoDesc CRawLoader::descInfo[] = {
Line 143: Line 143:
 
  };
 
  };
  
The tags, here corresponding to the loader, indicate that the [[Overview#nexus_Servers|server]] can load images and provide a file pattern.
+
Here, the tags indicate that the saver class above saves objects of the image class with the file extension RAW.
  
 
  LXtTagInfoDesc CRawSaver::descInfo[] = {
 
  LXtTagInfoDesc CRawSaver::descInfo[] = {
Line 151: Line 151:
 
         { 0 }
 
         { 0 }
 
  };
 
  };
 
Here, the tags indicate that the saver class above saves objects of the image class with the file extension RAW.
 
  
 
===[[Initialize_(index)|Initialization]]===
 
===[[Initialize_(index)|Initialization]]===
 +
 +
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.
  
 
         void
 
         void
Line 162: Line 162:
 
         LXx_ADD_SERVER (Loader1, CRawLoader, "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===
 
===Helper Functions===
 +
 +
This function queries the user for an int value and returns that value.
  
 
         static int
 
         static int
Line 178: Line 178:
 
         return value;
 
         return value;
 
  }
 
  }
 
This function queries the user for an int value and returns that value.
 
  
 
===Implementations===
 
===Implementations===
 +
 +
This method retrieves the options we will be using to import the file
  
 
         void
 
         void
Line 222: Line 222:
 
  }
 
  }
  
This method retrieves the options we will be using to import the file
+
The constructor indicates that the loader will be using the [[Image_Interface|image interface]].
 
   
 
   
 
  CRawLoader::CRawLoader ()
 
  CRawLoader::CRawLoader ()
Line 229: Line 229:
 
  }
 
  }
  
The constructor indicates that the loader will be using the [[Image_Interface|image interface]].
+
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.
  
 
<pre>
 
<pre>
Line 307: Line 307:
 
</pre>
 
</pre>
  
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.
+
This function opens the instance we choose.
  
 
         LxResult
 
         LxResult
Line 337: Line 337:
 
  }
 
  }
  
This function opens the instance we choose.
+
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
 
         LxResult
Line 378: Line 378:
 
  }
 
  }
  
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.
+
Cleanup is called after a failed recognize or after load-object completes, regardless of the outcome.
  
 
         LxResult
 
         LxResult
Line 392: Line 392:
 
  }
 
  }
  
Cleanup is called after a failed recognize or after load-object completes, regardless of the outcome.
+
These functions are utilities that retrieve information that deal with the image in question
  
 
<pre>
 
<pre>
Line 516: Line 516:
 
</pre>
 
</pre>
  
These functions are utilities that retrieve information that deal with the image in question
+
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.
  
 
<pre>
 
<pre>
Line 566: Line 566:
 
}
 
}
 
</pre>
 
</pre>
 
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.
 

Revision as of 21:42, 10 September 2013

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

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 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);
};

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 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 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.

class CRawSaver : public CLxImpl_Saver
{
   public:
       virtual LxResult	 sav_Save (
                                       ILxUnknownID	 source,
                                       const char	*filename,
                                       ILxUnknownID	 monitor) LXx_OVERRIDE;

       static LXtTagInfoDesc	 descInfo[];
};

Server Tags

The tags, here corresponding to the loader, indicate that the server can load images and provide a file pattern.

LXtTagInfoDesc	 CRawLoader::descInfo[] = {
       { LXsLOD_CLASSLIST,	LXa_IMAGE	},
       { LXsLOD_DOSPATTERN,	"*.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.

LXtTagInfoDesc	 CRawSaver::descInfo[] = {
       { LXsSAV_OUTCLASS,	LXa_IMAGE	},
       { LXsSAV_DOSTYPE,	"RAW"		},
       { LXsSRV_USERNAME,	"Raw Image"	},
       { 0 }
};

Initialization

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.

       void
initialize ()
{
       LXx_ADD_SERVER (Saver,  CRawSaver,  "raw_RGB");
       LXx_ADD_SERVER (Loader1, CRawLoader, "raw_RGB");
}

Helper Functions

This function queries the user for an int value and returns that value.

       static int
GetUserInt (const char *prefKey, int defaultValue = 0)
{
       int	value = defaultValue;
       CLxReadUserValue ruvUser;
       if (ruvUser.Query (prefKey)) {
               value = ruvUser.GetInt ();
       }

       return value;
}

Implementations

This method retrieves the options we will be using to import the file

       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);
}

The constructor indicates that the loader will be using the image interface.

CRawLoader::CRawLoader ()
{
       raw_factory.AddInterface (new CLxIfc_Image<CRawImage>);
}

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_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;
}

This function opens the instance we choose.

       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;
}

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_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;
}

Cleanup is called after a failed recognize or after load-object completes, regardless of the outcome.

       LxResult
CRawLoader::load_Cleanup (
       LXtLoadAccess1		*load)
{
       if (open_file) {
               fclose (open_file);
               open_file = 0;
       }

       return LXe_OK;
}

These functions are utilities that retrieve information that deal with the image in question

        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;
}

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.

        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;
}