Difference between revisions of "Item mandelbrot"
Line 23: | Line 23: | ||
We want this class to create a filter with which to apply our mandelbrot pattern. First, we inherit from [[Filter_(lx-filter.hpp)#.284.29_SDK:_ILxStackFilter_interface|CLxImpl_StackFilter]] to be able to compare, query, and convert our filter. We then inherit from CLxImpl_ImageFilter to be able to actually build an image filter, as the mandelbrot pattern is ultimately an image. Finally, we inherit from CLxImpl_ImageFilterMetrics so we can can keep track of the values in the filter. | We want this class to create a filter with which to apply our mandelbrot pattern. First, we inherit from [[Filter_(lx-filter.hpp)#.284.29_SDK:_ILxStackFilter_interface|CLxImpl_StackFilter]] to be able to compare, query, and convert our filter. We then inherit from CLxImpl_ImageFilter to be able to actually build an image filter, as the mandelbrot pattern is ultimately an image. Finally, we inherit from CLxImpl_ImageFilterMetrics so we can can keep track of the values in the filter. | ||
+ | <syntaxhighlight> | ||
class CMandelbrotFilter : | class CMandelbrotFilter : | ||
public CLxImpl_StackFilter, | public CLxImpl_StackFilter, | ||
Line 55: | Line 56: | ||
void GenerateFBuffer (const int w, const int h, float *fbuf); | void GenerateFBuffer (const int w, const int h, float *fbuf); | ||
void ConvertFBufferToImage (const float *fbuf, const int w, const int h, CLxUser_ImageWrite &wimg); | void ConvertFBufferToImage (const float *fbuf, const int w, const int h, CLxUser_ImageWrite &wimg); | ||
− | }; | + | };</syntaxhighlight> |
We want to be able to create threads and then share our work among them, so we inherit from [[Thread_(lx-thread.hpp)#.2822.29_SDK:_ILxSharedWork_interface|CLxImpl_SharedWork]]. | We want to be able to create threads and then share our work among them, so we inherit from [[Thread_(lx-thread.hpp)#.2822.29_SDK:_ILxSharedWork_interface|CLxImpl_SharedWork]]. | ||
Line 61: | Line 62: | ||
Continuing along with the theme of nested code, we create an object of the type CMandelBrotFilter, the class we just wrote. Following this, we have three classes with the share prefix, indicating that they are redeclarations of virtual functions from the CLxImplSharedWork class. The Spawn class starts everything off, creating a work object that holds the total number of lines in the image that we want our filter to work on. Next, our Evaluate function takes one line of the image and uses the filter on it. The Share function brings these two together, having the work object tell many threads to use the Evaluate method at once. | Continuing along with the theme of nested code, we create an object of the type CMandelBrotFilter, the class we just wrote. Following this, we have three classes with the share prefix, indicating that they are redeclarations of virtual functions from the CLxImplSharedWork class. The Spawn class starts everything off, creating a work object that holds the total number of lines in the image that we want our filter to work on. Next, our Evaluate function takes one line of the image and uses the filter on it. The Share function brings these two together, having the work object tell many threads to use the Evaluate method at once. | ||
+ | <syntaxhighlight> | ||
class CMandlebrotWork : | class CMandlebrotWork : | ||
public CLxImpl_SharedWork | public CLxImpl_SharedWork | ||
Line 74: | Line 76: | ||
float Pixel (double x, double y); | float Pixel (double x, double y); | ||
− | }; | + | };</syntaxhighlight> |
We want to implement our package by creating an instance of it, so we inherit from the [[Package_(lx-package.hpp)#.2825.29_SDK:_ILxPackageInstance_interface|CLxImpl_PackageInstance class]]. We also need some of the methods that [[Clip_(lx-clip.hpp)#.281.29_SDK:_ILxVideoClipItem_interface|CLxImpl_VideoClipItem]], as the superclass of all image related clips, includes so we inherit from it. | We want to implement our package by creating an instance of it, so we inherit from the [[Package_(lx-package.hpp)#.2825.29_SDK:_ILxPackageInstance_interface|CLxImpl_PackageInstance class]]. We also need some of the methods that [[Clip_(lx-clip.hpp)#.281.29_SDK:_ILxVideoClipItem_interface|CLxImpl_VideoClipItem]], as the superclass of all image related clips, includes so we inherit from it. | ||
Line 82: | Line 84: | ||
The functions with the vclip prefix are redeclarations of virtual functions declared in the ClxVideoClipItem class. The Prepfilter function sets the channels we need to allocate our filter and records the indices we use for those channels in a cache. The AllocFilter function creates a filter using the channel data from the channels we set in PrepFilter. Cleanup releases the cache we used to set the values of the channels that we used. | The functions with the vclip prefix are redeclarations of virtual functions declared in the ClxVideoClipItem class. The Prepfilter function sets the channels we need to allocate our filter and records the indices we use for those channels in a cache. The AllocFilter function creates a filter using the channel data from the channels we set in PrepFilter. Cleanup releases the cache we used to set the values of the channels that we used. | ||
+ | <syntaxhighlight> | ||
class CMandelbrotInstance : | class CMandelbrotInstance : | ||
public CLxImpl_PackageInstance, | public CLxImpl_PackageInstance, | ||
Line 102: | Line 105: | ||
unsigned itr; | unsigned itr; | ||
} GenData; | } GenData; | ||
− | }; | + | };</syntaxhighlight> |
We want to construct a package for the mandelbrot filter, so we inherit from [[Package_(lx-package.hpp)|CLxImpl_Package]]. | We want to construct a package for the mandelbrot filter, so we inherit from [[Package_(lx-package.hpp)|CLxImpl_Package]]. | ||
Line 108: | Line 111: | ||
Three functions have the pkg prefix, indicating that they are redeclarations of virtual functions in the CLxImplPackage class. The SetupChannels function and the TestInterface function adds channels and attaches an interface to our package. Meanwhile, the Attach function, continuing on with the theme of nested code, creates a CMandekBrotInstance item and attaches it to the package that we are creating. | Three functions have the pkg prefix, indicating that they are redeclarations of virtual functions in the CLxImplPackage class. The SetupChannels function and the TestInterface function adds channels and attaches an interface to our package. Meanwhile, the Attach function, continuing on with the theme of nested code, creates a CMandekBrotInstance item and attaches it to the package that we are creating. | ||
+ | <syntaxhighlight> | ||
class CMandelbrotPackage : | class CMandelbrotPackage : | ||
public CLxImpl_Package | public CLxImpl_Package | ||
Line 118: | Line 122: | ||
LxResult pkg_TestInterface (const LXtGUID *guid) LXx_OVERRIDE; | LxResult pkg_TestInterface (const LXtGUID *guid) LXx_OVERRIDE; | ||
LxResult pkg_Attach (void **ppvObj) LXx_OVERRIDE; | LxResult pkg_Attach (void **ppvObj) LXx_OVERRIDE; | ||
− | }; | + | };</syntaxhighlight> |
This class creates a [[Factory_Object|factory]] for all the object, which takes the objects that we specify and exports them, even is they are not servers. Only CMandelBrotPackage is exported as a server in this fle. Since the polymorph object has to exist for the lifetime of any instance, the are explicitly allocated as part of the global module and freed at shutdown. | This class creates a [[Factory_Object|factory]] for all the object, which takes the objects that we specify and exports them, even is they are not servers. Only CMandelBrotPackage is exported as a server in this fle. Since the polymorph object has to exist for the lifetime of any instance, the are explicitly allocated as part of the global module and freed at shutdown. | ||
+ | <syntaxhighlight> | ||
class CFactories { | class CFactories { | ||
public: | public: | ||
Line 140: | Line 145: | ||
} | } | ||
− | } *pF; | + | } *pF;</syntaxhighlight> |
===[[Server_Tags|Server Tags]]=== | ===[[Server_Tags|Server Tags]]=== | ||
Line 148: | Line 153: | ||
The tags here indicate that our Mandelbrot item type is a subtype of video clip meaning an image that may change over time. | The tags here indicate that our Mandelbrot item type is a subtype of video clip meaning an image that may change over time. | ||
+ | <syntaxhighlight> | ||
LXtTagInfoDesc CMandelbrotPackage::descInfo[] = { | LXtTagInfoDesc CMandelbrotPackage::descInfo[] = { | ||
{ LXsPKG_SUPERTYPE, LXsITYPE_VIDEOCLIP }, | { LXsPKG_SUPERTYPE, LXsITYPE_VIDEOCLIP }, | ||
{ 0 } | { 0 } | ||
− | }; | + | };</syntaxhighlight> |
===[[Initialize_(index)|Initialize]]=== | ===[[Initialize_(index)|Initialize]]=== | ||
Line 159: | Line 165: | ||
This function exports a server with the package and staticdesc interfaces that is dependent on the CMandelbrotPackage. It also creates the factories using the function references in 138-164. | This function exports a server with the package and staticdesc interfaces that is dependent on the CMandelbrotPackage. It also creates the factories using the function references in 138-164. | ||
+ | <syntaxhighlight> | ||
void | void | ||
initialize () | initialize () | ||
Line 170: | Line 177: | ||
pF = new CFactories; | pF = new CFactories; | ||
− | } | + | }</syntaxhighlight> |
===Helper Function=== | ===Helper Function=== | ||
Line 176: | Line 183: | ||
This function destroys the factories so they can persist while their objects are in use. | This function destroys the factories so they can persist while their objects are in use. | ||
+ | <syntaxhighlight> | ||
void | void | ||
cleanup () | cleanup () | ||
{ | { | ||
delete pF; | delete pF; | ||
− | } | + | }</syntaxhighlight> |
===Implementations=== | ===Implementations=== | ||
Line 186: | Line 194: | ||
Here the channels for the package are created and their default values set using the [[Package_(lx-package.hpp)#.2815.29_SDK:_ILxAddChannel_interface|AddChannel]] interface and the [[Package_(lx-package.hpp)#.2815.29_SDK:_ILxAddChannel_interface|NewChannel]] utility | Here the channels for the package are created and their default values set using the [[Package_(lx-package.hpp)#.2815.29_SDK:_ILxAddChannel_interface|AddChannel]] interface and the [[Package_(lx-package.hpp)#.2815.29_SDK:_ILxAddChannel_interface|NewChannel]] utility | ||
+ | <syntaxhighlight> | ||
#define Cs_ITERATIONS "iterations" | #define Cs_ITERATIONS "iterations" | ||
#define Cs_SIZE "size" | #define Cs_SIZE "size" | ||
Line 198: | Line 207: | ||
{ | { | ||
... | ... | ||
− | } | + | }</syntaxhighlight> |
This section uses [[Wrap_(lx-wrap.hpp)#CLxGenericPolymorph|TestInterface]] so that nexus knows what interfaces instances of this package support. This is necessary as to prevent query loops. | This section uses [[Wrap_(lx-wrap.hpp)#CLxGenericPolymorph|TestInterface]] so that nexus knows what interfaces instances of this package support. This is necessary as to prevent query loops. | ||
+ | <syntaxhighlight> | ||
LxResult | LxResult | ||
CMandelbrotPackage::pkg_TestInterface ( | CMandelbrotPackage::pkg_TestInterface ( | ||
Line 207: | Line 217: | ||
{ | { | ||
... | ... | ||
− | } | + | }</syntaxhighlight> |
Here, Attach is called to create a new instance of this item. The returned object implements a specific item of this type in the scene using the [[Wrap_(lx-wrap.hpp)#CLxPolymorph|Alloc]] method. | Here, Attach is called to create a new instance of this item. The returned object implements a specific item of this type in the scene using the [[Wrap_(lx-wrap.hpp)#CLxPolymorph|Alloc]] method. | ||
+ | <syntaxhighlight> | ||
LxResult | LxResult | ||
CMandelbrotPackage::pkg_Attach ( | CMandelbrotPackage::pkg_Attach ( | ||
Line 216: | Line 227: | ||
{ | { | ||
... | ... | ||
− | } | + | }</syntaxhighlight> |
The instance is the implementation of the item, and this set of code allocates one for each item in the scene. The instant can respond to a set of events. | The instance is the implementation of the item, and this set of code allocates one for each item in the scene. The instant can respond to a set of events. | ||
+ | <syntaxhighlight> | ||
LxResult | LxResult | ||
CMandelbrotInstance::pins_Initialize ( | CMandelbrotInstance::pins_Initialize ( | ||
Line 226: | Line 238: | ||
{ | { | ||
... | ... | ||
− | } | + | }</syntaxhighlight> |
The VideoClipItem interface allows this item to function as a generic video clip. PrepFilter allows the item to select channels needed for filter allocation, and record the indices for those in a cache. | The VideoClipItem interface allows this item to function as a generic video clip. PrepFilter allows the item to select channels needed for filter allocation, and record the indices for those in a cache. | ||
+ | <syntaxhighlight> | ||
LxResult | LxResult | ||
CMandelbrotInstance::vclip_PrepFilter ( | CMandelbrotInstance::vclip_PrepFilter ( | ||
Line 236: | Line 249: | ||
{ | { | ||
... | ... | ||
− | } | + | }</syntaxhighlight> |
AllocFilter creates the image filter for a given frame. | AllocFilter creates the image filter for a given frame. | ||
+ | <syntaxhighlight> | ||
LxResult | LxResult | ||
CMandelbrotInstance::vclip_AllocFilter ( | CMandelbrotInstance::vclip_AllocFilter ( | ||
Line 247: | Line 261: | ||
{ | { | ||
... | ... | ||
− | } | + | }</syntaxhighlight> |
This section cleans up some data. | This section cleans up some data. | ||
+ | <syntaxhighlight> | ||
void | void | ||
CMandelbrotInstance::vclip_Cleanup ( | CMandelbrotInstance::vclip_Cleanup ( | ||
Line 256: | Line 271: | ||
{ | { | ||
... | ... | ||
− | } | + | }</syntaxhighlight> |
This section places the image onto the image stack where it generates target image on the fly | This section places the image onto the image stack where it generates target image on the fly | ||
+ | <syntaxhighlight> | ||
const char * | const char * | ||
CMandelbrotFilter::filt_Type (void) | CMandelbrotFilter::filt_Type (void) | ||
Line 271: | Line 287: | ||
... | ... | ||
} | } | ||
+ | </syntaxhighlight> | ||
This uses Compare to compare a filter which has been evaluated to a new one being computed. If the size has changed we require the image to be remade from scratch. For other changes the past result is compatible with the current result so some previous work can be reused. | This uses Compare to compare a filter which has been evaluated to a new one being computed. If the size has changed we require the image to be remade from scratch. For other changes the past result is compatible with the current result so some previous work can be reused. | ||
+ | <syntaxhighlight> | ||
unsigned | unsigned | ||
CMandelbrotFilter::filt_Compare ( | CMandelbrotFilter::filt_Compare ( | ||
Line 279: | Line 297: | ||
{ | { | ||
... | ... | ||
− | } | + | }</syntaxhighlight> |
This section will be called to convert a past evaluation to a compatible filter. We always use the existing image, and if only the color parameters have changed we can reuse the previous gradient input map. | This section will be called to convert a past evaluation to a compatible filter. We always use the existing image, and if only the color parameters have changed we can reuse the previous gradient input map. | ||
+ | <syntaxhighlight> | ||
LxResult | LxResult | ||
CMandelbrotFilter::filt_Convert ( | CMandelbrotFilter::filt_Convert ( | ||
Line 288: | Line 307: | ||
{ | { | ||
... | ... | ||
− | } | + | }</syntaxhighlight> |
Here, generators allocate an image and fill it in. This filter maintains a reference to the image so it can be reused as needed. Filters that generate and forget would instead simply return the image. | Here, generators allocate an image and fill it in. This filter maintains a reference to the image so it can be reused as needed. Filters that generate and forget would instead simply return the image. | ||
+ | <syntaxhighlight> | ||
LxResult | LxResult | ||
CMandelbrotFilter::imf_Generate ( | CMandelbrotFilter::imf_Generate ( | ||
Line 300: | Line 320: | ||
{ | { | ||
... | ... | ||
− | } | + | }</syntaxhighlight> |
Generate full filter(the main function in this section) will create the image filter at its full size, using the width and height from the channels on the Mandelbrot item. | Generate full filter(the main function in this section) will create the image filter at its full size, using the width and height from the channels on the Mandelbrot item. | ||
+ | <syntaxhighlight> | ||
LxResult | LxResult | ||
CMandelbrotFilter::GenerateFullFilter (void) | CMandelbrotFilter::GenerateFullFilter (void) | ||
{ | { | ||
... | ... | ||
− | } | + | }</syntaxhighlight> |
Generate small filter does the same as above, except it works on a smaller resolution, using a temporary float buffer. GenerateFBuffer() converts the buffer to the image | Generate small filter does the same as above, except it works on a smaller resolution, using a temporary float buffer. GenerateFBuffer() converts the buffer to the image | ||
+ | <syntaxhighlight> | ||
LxResult | LxResult | ||
CMandelbrotFilter::GenerateSmallFilter ( | CMandelbrotFilter::GenerateSmallFilter ( | ||
Line 319: | Line 341: | ||
{ | { | ||
... | ... | ||
− | } | + | }</syntaxhighlight> |
We generate the buffer in the threads. We allocate a work object using Alloc(F), initialize it to the image and process it. | We generate the buffer in the threads. We allocate a work object using Alloc(F), initialize it to the image and process it. | ||
+ | <syntaxhighlight> | ||
void | void | ||
CMandelbrotFilter::GenerateFBuffer ( | CMandelbrotFilter::GenerateFBuffer ( | ||
Line 330: | Line 353: | ||
{ | { | ||
... | ... | ||
− | } | + | }</syntaxhighlight> |
This section converts the raw buffered values to colors. This could be done in the workers too, since setting pixels should be thread-safe. | This section converts the raw buffered values to colors. This could be done in the workers too, since setting pixels should be thread-safe. | ||
+ | <syntaxhighlight> | ||
void | void | ||
CMandelbrotFilter::ConvertFBufferToImage ( | CMandelbrotFilter::ConvertFBufferToImage ( | ||
Line 342: | Line 366: | ||
{ | { | ||
... | ... | ||
− | } | + | }</syntaxhighlight> |
Adds a metrics interface for getting size and type info. | Adds a metrics interface for getting size and type info. | ||
+ | <syntaxhighlight> | ||
LxResult | LxResult | ||
CMandelbrotFilter::imfmet_Generate ( | CMandelbrotFilter::imfmet_Generate ( | ||
Line 351: | Line 376: | ||
{ | { | ||
... | ... | ||
− | } | + | }</syntaxhighlight> |
The class created here(CMandelbrotWork) is a way to spread the work among threads. Each work object holds a current line and number of lines remaining initialized to zero. The master work object will be initialized to the total lines in the image, and the threading system will allocate enough new work objects to keep the system's core busy. | The class created here(CMandelbrotWork) is a way to spread the work among threads. Each work object holds a current line and number of lines remaining initialized to zero. The master work object will be initialized to the total lines in the image, and the threading system will allocate enough new work objects to keep the system's core busy. | ||
+ | <syntaxhighlight> | ||
LxResult | LxResult | ||
CMandlebrotWork::share_Spawn ( | CMandlebrotWork::share_Spawn ( | ||
Line 360: | Line 386: | ||
{ | { | ||
... | ... | ||
− | } | + | }</syntaxhighlight> |
Work objects that have work will be asked to share with those that don't. In this case we transfer one line from this work object to the other. This is currently the only valid split mode, although it could transfer more lines to reduce contention. | Work objects that have work will be asked to share with those that don't. In this case we transfer one line from this work object to the other. This is currently the only valid split mode, although it could transfer more lines to reduce contention. | ||
+ | <syntaxhighlight> | ||
LxResult | LxResult | ||
CMandlebrotWork::share_Share ( | CMandlebrotWork::share_Share ( | ||
Line 370: | Line 397: | ||
{ | { | ||
... | ... | ||
− | } | + | }</syntaxhighlight> |
Evaluate(the main function here) processes one piece of work, in this case a line of the image. The floating point buffer is filled with values from -1 to 1. | Evaluate(the main function here) processes one piece of work, in this case a line of the image. The floating point buffer is filled with values from -1 to 1. | ||
+ | <syntaxhighlight> | ||
LxResult | LxResult | ||
CMandlebrotWork::share_Evaluate () | CMandlebrotWork::share_Evaluate () | ||
{ | { | ||
... | ... | ||
− | } | + | }</syntaxhighlight> |
Here, we compute a single pixel. This is the standard Mandelbrot loop, except that the final iteration count is scaled 0 to 1 in a log scale. Points in the set are assigned -1. | Here, we compute a single pixel. This is the standard Mandelbrot loop, except that the final iteration count is scaled 0 to 1 in a log scale. Points in the set are assigned -1. | ||
+ | <syntaxhighlight> | ||
float | float | ||
CMandlebrotWork::Pixel ( | CMandlebrotWork::Pixel ( | ||
Line 388: | Line 417: | ||
{ | { | ||
... | ... | ||
− | } | + | }</syntaxhighlight> |
[[Category: SDK Examples]] | [[Category: SDK Examples]] |
Latest revision as of 18:39, 16 September 2013
Item_mandelbrot 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.
This plugin adds the mandelbrot pattern to modo. A mandelbrot is a shading pattern that you overlay onto whatever you wish.
You can access the pattern by going to the model tab->Shading->Add Layer->mandelbrot.
A unit sphere before the mandelbrot pattern is applied
The two sides of the above unit sphere after the mandelbrot pattern is applied
Contents
Code Walkthrough
Class Declarations
This plugin uses a sort of nested code structure wherein we create classes that we later use in other classes which we then again use in our final package class.
This class forms the base of the structure.
We want this class to create a filter with which to apply our mandelbrot pattern. First, we inherit from CLxImpl_StackFilter to be able to compare, query, and convert our filter. We then inherit from CLxImpl_ImageFilter to be able to actually build an image filter, as the mandelbrot pattern is ultimately an image. Finally, we inherit from CLxImpl_ImageFilterMetrics so we can can keep track of the values in the filter.
class CMandelbrotFilter : public CLxImpl_StackFilter, public CLxImpl_ImageFilter, public CLxImpl_ImageFilterMetrics { public: CMandelbrotPackage *src_pkg; CLxUser_GradientFilter g_filt[4]; unsigned i_w, i_h, i_max; double x_0, x_s, y_0, y_s; float s_col[4]; CLxUser_ImageWrite w_img; float *f_buf; double max_log; bool use_image, gen_buffer; const char * filt_Type (void) LXx_OVERRIDE; unsigned filt_Compare (ILxUnknownID other) LXx_OVERRIDE; LxResult filt_Convert (ILxUnknownID other) LXx_OVERRIDE; unsigned imf_Type (void) LXx_OVERRIDE; LxResult imf_Generate (int width, int height, ILxUnknownID monitor, void **ppvObj) LXx_OVERRIDE; LxResult imfmet_Generate (LXtImageMetrics *metrics) LXx_OVERRIDE; private: LxResult GenerateFullFilter (void); LxResult GenerateSmallFilter (const int w, const int h, CLxUser_ImageWrite &wimg); void GenerateFBuffer (const int w, const int h, float *fbuf); void ConvertFBufferToImage (const float *fbuf, const int w, const int h, CLxUser_ImageWrite &wimg); };
We want to be able to create threads and then share our work among them, so we inherit from CLxImpl_SharedWork.
Continuing along with the theme of nested code, we create an object of the type CMandelBrotFilter, the class we just wrote. Following this, we have three classes with the share prefix, indicating that they are redeclarations of virtual functions from the CLxImplSharedWork class. The Spawn class starts everything off, creating a work object that holds the total number of lines in the image that we want our filter to work on. Next, our Evaluate function takes one line of the image and uses the filter on it. The Share function brings these two together, having the work object tell many threads to use the Evaluate method at once.
class CMandlebrotWork : public CLxImpl_SharedWork { public: CMandelbrotFilter *src_filt; float *fbuf; int i_y, n_y, w, h; LxResult share_Evaluate (); LxResult share_Spawn (void **ppvObj); LxResult share_Share (ILxUnknownID other, unsigned int split); float Pixel (double x, double y); };
We want to implement our package by creating an instance of it, so we inherit from the CLxImpl_PackageInstance class. We also need some of the methods that CLxImpl_VideoClipItem, as the superclass of all image related clips, includes so we inherit from it.
We create a CLxUser_Item object called m_item. The functions with the pins prefix are redeclarations of virtual functions in the CLxPackageInstance class. The Initialize function sets the m_item object as an instance, and the Cleanup function clears it, removing it as an instance.
The functions with the vclip prefix are redeclarations of virtual functions declared in the ClxVideoClipItem class. The Prepfilter function sets the channels we need to allocate our filter and records the indices we use for those channels in a cache. The AllocFilter function creates a filter using the channel data from the channels we set in PrepFilter. Cleanup releases the cache we used to set the values of the channels that we used.
class CMandelbrotInstance : public CLxImpl_PackageInstance, public CLxImpl_VideoClipItem { public: CMandelbrotPackage *src_pkg; CLxUser_Item m_item; LxResult pins_Initialize (ILxUnknownID item, ILxUnknownID super) LXx_OVERRIDE; void pins_Cleanup (void) LXx_OVERRIDE; LxResult vclip_PrepFilter (ILxUnknownID eval, void **cache) LXx_OVERRIDE; LxResult vclip_AllocFilter (ILxUnknownID attr, void *cache, void **ppvObj) LXx_OVERRIDE; void vclip_Cleanup (void *cache) LXx_OVERRIDE; typedef struct st_GenData { unsigned w, h, cx, cy; unsigned rad, grd, col; unsigned itr; } GenData; };
We want to construct a package for the mandelbrot filter, so we inherit from CLxImpl_Package.
Three functions have the pkg prefix, indicating that they are redeclarations of virtual functions in the CLxImplPackage class. The SetupChannels function and the TestInterface function adds channels and attaches an interface to our package. Meanwhile, the Attach function, continuing on with the theme of nested code, creates a CMandekBrotInstance item and attaches it to the package that we are creating.
class CMandelbrotPackage : public CLxImpl_Package { public: CLxUser_ImageService img_svc; static LXtTagInfoDesc descInfo[]; LxResult pkg_SetupChannels (ILxUnknownID addChan) LXx_OVERRIDE; LxResult pkg_TestInterface (const LXtGUID *guid) LXx_OVERRIDE; LxResult pkg_Attach (void **ppvObj) LXx_OVERRIDE; };
This class creates a factory for all the object, which takes the objects that we specify and exports them, even is they are not servers. Only CMandelBrotPackage is exported as a server in this fle. Since the polymorph object has to exist for the lifetime of any instance, the are explicitly allocated as part of the global module and freed at shutdown.
class CFactories { public: CLxPolymorph<CMandelbrotInstance> inst; CLxPolymorph<CMandelbrotFilter> filt; CLxPolymorph<CMandlebrotWork> work; CFactories () { inst.AddInterface (new CLxIfc_PackageInstance <CMandelbrotInstance>); inst.AddInterface (new CLxIfc_VideoClipItem <CMandelbrotInstance>); filt.AddInterface (new CLxIfc_StackFilter <CMandelbrotFilter>); filt.AddInterface (new CLxIfc_ImageFilter <CMandelbrotFilter>); filt.AddInterface (new CLxIfc_ImageFilterMetrics<CMandelbrotFilter>); work.AddInterface (new CLxIfc_SharedWork <CMandlebrotWork>); } } *pF;
Server Tags
Servers tags are examined when the server is initialized, and give information about the server. We set the tags in this case by taking descinfo[] arrays and associating the relevant data with the corresponding flags.
The tags here indicate that our Mandelbrot item type is a subtype of video clip meaning an image that may change over time.
LXtTagInfoDesc CMandelbrotPackage::descInfo[] = { { LXsPKG_SUPERTYPE, LXsITYPE_VIDEOCLIP }, { 0 } };
Initialize
Intialize is called when we add the plugin to modo, and is the utility that exports the server.
This function exports a server with the package and staticdesc interfaces that is dependent on the CMandelbrotPackage. It also creates the factories using the function references in 138-164.
void initialize () { CLxGenericPolymorph *srv; srv = new CLxPolymorph<CMandelbrotPackage>; srv->AddInterface (new CLxIfc_Package <CMandelbrotPackage>); srv->AddInterface (new CLxIfc_StaticDesc<CMandelbrotPackage>); thisModule.AddServer ("mandelbrot", srv); pF = new CFactories; }
Helper Function
This function destroys the factories so they can persist while their objects are in use.
void cleanup () { delete pF; }
Implementations
Here the channels for the package are created and their default values set using the AddChannel interface and the NewChannel utility
#define Cs_ITERATIONS "iterations" #define Cs_SIZE "size" #define Cs_CENTER "center" #define Cs_RADIUS "radius" #define Cs_COLOR "color" #define Cs_SETCOL "setColor" LxResult CMandelbrotPackage::pkg_SetupChannels ( ILxUnknownID addChan) { ... }
This section uses TestInterface so that nexus knows what interfaces instances of this package support. This is necessary as to prevent query loops.
LxResult CMandelbrotPackage::pkg_TestInterface ( const LXtGUID *guid) { ... }
Here, Attach is called to create a new instance of this item. The returned object implements a specific item of this type in the scene using the Alloc method.
LxResult CMandelbrotPackage::pkg_Attach ( void **ppvObj) { ... }
The instance is the implementation of the item, and this set of code allocates one for each item in the scene. The instant can respond to a set of events.
LxResult CMandelbrotInstance::pins_Initialize ( ILxUnknownID item, ILxUnknownID super) { ... }
The VideoClipItem interface allows this item to function as a generic video clip. PrepFilter allows the item to select channels needed for filter allocation, and record the indices for those in a cache.
LxResult CMandelbrotInstance::vclip_PrepFilter ( ILxUnknownID evalObj, void **cache) { ... }
AllocFilter creates the image filter for a given frame.
LxResult CMandelbrotInstance::vclip_AllocFilter ( ILxUnknownID attrObj, void *cache, void **ppvObj) { ... }
This section cleans up some data.
void CMandelbrotInstance::vclip_Cleanup ( void *cache) { ... }
This section places the image onto the image stack where it generates target image on the fly
const char * CMandelbrotFilter::filt_Type (void) { ... } unsigned CMandelbrotFilter::imf_Type (void) { ... }
This uses Compare to compare a filter which has been evaluated to a new one being computed. If the size has changed we require the image to be remade from scratch. For other changes the past result is compatible with the current result so some previous work can be reused.
unsigned CMandelbrotFilter::filt_Compare ( ILxUnknownID other) { ... }
This section will be called to convert a past evaluation to a compatible filter. We always use the existing image, and if only the color parameters have changed we can reuse the previous gradient input map.
LxResult CMandelbrotFilter::filt_Convert ( ILxUnknownID other) { ... }
Here, generators allocate an image and fill it in. This filter maintains a reference to the image so it can be reused as needed. Filters that generate and forget would instead simply return the image.
LxResult CMandelbrotFilter::imf_Generate ( int width, int height, ILxUnknownID monitor, void **ppvObj) { ... }
Generate full filter(the main function in this section) will create the image filter at its full size, using the width and height from the channels on the Mandelbrot item.
LxResult CMandelbrotFilter::GenerateFullFilter (void) { ... }
Generate small filter does the same as above, except it works on a smaller resolution, using a temporary float buffer. GenerateFBuffer() converts the buffer to the image
LxResult CMandelbrotFilter::GenerateSmallFilter ( const int w, const int h, CLxUser_ImageWrite &wimg) { ... }
We generate the buffer in the threads. We allocate a work object using Alloc(F), initialize it to the image and process it.
void CMandelbrotFilter::GenerateFBuffer ( const int w, const int h, float *fbuf) { ... }
This section converts the raw buffered values to colors. This could be done in the workers too, since setting pixels should be thread-safe.
void CMandelbrotFilter::ConvertFBufferToImage ( const float *fbuf, const int w, const int h, CLxUser_ImageWrite &wimg) { ... }
Adds a metrics interface for getting size and type info.
LxResult CMandelbrotFilter::imfmet_Generate ( LXtImageMetrics *metrics) { ... }
The class created here(CMandelbrotWork) is a way to spread the work among threads. Each work object holds a current line and number of lines remaining initialized to zero. The master work object will be initialized to the total lines in the image, and the threading system will allocate enough new work objects to keep the system's core busy.
LxResult CMandlebrotWork::share_Spawn ( void **ppvObj) { ... }
Work objects that have work will be asked to share with those that don't. In this case we transfer one line from this work object to the other. This is currently the only valid split mode, although it could transfer more lines to reduce contention.
LxResult CMandlebrotWork::share_Share ( ILxUnknownID other, unsigned int split) { ... }
Evaluate(the main function here) processes one piece of work, in this case a line of the image. The floating point buffer is filled with values from -1 to 1.
LxResult CMandlebrotWork::share_Evaluate () { ... }
Here, we compute a single pixel. This is the standard Mandelbrot loop, except that the final iteration count is scaled 0 to 1 in a log scale. Points in the set are assigned -1.
float CMandlebrotWork::Pixel ( double x, double y) { ... }