XIL Device Porting and Extensibility Guide
  Search only this book
Download this book in PDF

Compute Devices

4

About Compute Devices

Compute handlers are the basic operation grouping in the XIL library. A single compute handler can contain the implementation of one or more atomic XIL image functions and/or one or more molecules. (General aspects of molecules are described in Chapter 1, "Overview."). Compute handlers are device specific; they provide an implementation of the operational capability of a device. In addition, compute handlers integrate with storage handlers (see Chapter 5, "Storage Devices") and with I/O handlers (see Chapter 3, "I/O Devices"). They also are used to implement the compress and decompress part of XIL compression and decompression (see Chapter 6,
"Compression/Decompression").
Each compute handler must contain the following global function:
XilDeviceComputeType* XilCreateComputeType()

This function provides device initialization and describes the list of implemented image processing functions that need to be added to the global list of available functions.
XilDeviceComputeType is an empty class. Unlike I/O, compression, and storage devices, no instances of a compute device exist. The compute handler subclasses XilDeviceComputeType, adding the functions that have been implemented. The default memory device has all the functions implemented. In the derived class, you need only implement any additional functions you desire.

Implementing an XIL Function

To create a compute device, you must implement an XIL function. There are several examples of this in this document. Appendix A, "Sample Molecule," shows an example of creating a two-atom molecule. The section "Sample Compute Device Handler" on page 134," shows how to create an atom that uses a different storage mechanism from the standard memory storage. The functions that may be implemented are listed in Appendix B, "XIL Atomic Functions."
The language for implementing XIL functions is C++. All image functions are members of the XilImage class. Class members, such as XilImage::getBands(), are available for use in compute handlers.
The arguments to each element (atomic or molecular) in a compute handler are the op and the op_count. For the implementation in Appendix A, "Sample Molecule," the prototype looks like this:

  int  
  XilDeviceComputeTypeMemory::Rescale16Convert16to8(  
       XilOp* op,       // a pointer into the DAG  
       int op_count)    // the number of combined ops to be done  

The XilOp class holds the information required to store the XIL operation in the DAG. The op parameter is a pointer that represents a specific XIL operation on the DAG. The source and destination images of atomic functions must be accessible to the operation. These images are stored in the XilOp object. The parameters of XIL functions also are stored in the XilOp object. The XilOp class contains member functions that enable you to extract the image and parameter information for an operation. See the section "The XilOp Class" in Chapter 1 for a complete description of this class. The number of image sources supported by an XIL operation and the XilOp member functions that you must use to extract the images sources and to extract an XIL function's parameters from the XilOp object are listed in Appendix C, "XilOp Object."
Access to the image data is obtained via three member functions of the XilImage class:
Table 4-1 XilImage
Member FunctionDefinition
getStorage()Returns a pointer to a structure that describes the data storage for the specified storage type. It does not take child offsets into account.
getMemoryStorage()Returns a pointer that describes the memory layout. It does not allow you to specify the storage type. It does take child offsets into account.
requestStorage()Returns a pointer to a structure that describes the data storage for the specified storage, but, unlike getStorage(), does not force a propagation.
These member functions are discussed in detail in the section "The XilImage Class" in Chapter 1.
An example of the use of getMemoryStorage() is as follows:

  // get source's memory storage  
  XilMemoryStorageShort *short_storage;  
  short_storage = (XilMemoryStorageShort *)src->getMemoryStorage();  
  if (short_storage==NULL) {  
       return XIL_FAILURE;  
  }  
  Xil_signed16 *src_base_addr = (Xil_signed16 *)short_storage->data;  
  unsigned long src_next_pixel = short_storage->pixel_stride;  
  unsigned long src_next_scan = short_storage->scanline_stride;  

Almost all image operations in the XIL library will be affected by regions of interest (ROIs). XIL ROIs may be of arbitrary shape. The internal representation in the current release is a series of rectangles, much like the X11 region, except that the XIL library uses 32-bit quantities instead of 16-bit quantities to define the position and extent of the rectangles. The internal function XiliGetRoiList() can be used to obtain the intersection of the source and destination ROIs. For a detailed discussion of ROIs, see the section "The XilRoi Class" in Chapter 1.

Note - The values produced by the implementation of an XIL function should match as closely as possible the values produced by the memory port. This has several implications. The results of operations should be clamped; that is, intermediate results should be tested against the maximum and minimum values of the destination data type before they are converted to that data type. For many of the simple functions, any difference from the default version should not be tolerated. More complicated operations, where there are many floating-point or fixed-point calculations done for each pixel, do not always allow pixel-for-pixel accuracy with any sort of reasonable code. Often, new algorithms provide slightly different values. It is up to the implementor of the algorithm to make sure that there are no systematic differences between the new implementation and the old one.

The XIL Test Suite can aid you in verifying new implementations of XIL functions. The XIL Test Suite enables you to perform regression tests of new code against verified reference signatures and includes the capability of specifying a tolerance for the comparison. This test suite is described in a separate document, XIL Test Suite User's Guide, which is part of this release.
Error handling in an implementation is performed by calling the notifyError() member function of the system state. A macro XIL_ERROR that simplifies this interface is defined in XilError.h. Both compute handler examples use this interface. The method used to add error messages for device-dependent errors is discussed in Chapter 2, "More on Writing Device Handlers."

Adding a Compute Device

When writing a compute device, you can implement support for one or more atomic or molecular functions. The default (memory) compute device contains implementations of all the XIL atomic functions, as well as a few molecules.
Each subclass of a compute handler can implement multiple atomic or molecular functions. Each member function (routine) of a subclass can be an entry point for multiple atoms or molecules. Each routine can perform
equivalent functions or can contain common code that branches out for specific parameter values. For example, in the code below, Affine8 is a common entry point for several types of affine operations on the byte data type.

  /* XILCONFIG: Affine8= affineNN8() */  
  /* XILCONFIG: Affine8= affineBL8() */  
  /* XILCONFIG: Affine8= affineBC8() */  
  XilDeviceComputeTypeMemory::Affine8(  
       XilOp   *op,          // a pointer into the DAG  
       int     op_count)     // number of combined operations  
  {  
                .  
                .  
  }  

Molecules also can share a common entry point. In the example below, the DecompressDither8 treats the rescale as an optional operation, which can be replaced with some reasonable default value. Therefore, this routine must be written to handle a chain of two or three operations. The code checks the operation number of the second operation using the utility function XilLookupOpNumber(), which returns the operation number for a specified routine name.

  /* XILCONFIG: DecompressDither8= */  
       ordereddither8_8(decompress_Codec())  
  /* XILCONFIG: DecompressDither8= */  
       ordereddither8_8(rescale(decompress_Codec())  
  
  int  
  XilDeviceComputeTypeCodecMemory::DecompressDither8(  
       XilOp*       *op,         // a pointer into the DAG  
       int          op_count)    // number of combined operations  
  {  
       // Pull everything needed off the chain of operations  
       // First, the ordered dither operation  
       dst = op->getDst();  
       cube = (XilLookup *)op->getObjParam(1);  
       dmask = (XilDitherMask *)op->getObjParam(2);  


       // move to next operation along the chain  
       op = op->getOp1();  
  
       // Now, look at the next operation number and test for the  
       // optional rescale operation  
       rescale8_opnum == XilLookupOpNumber("rescale8")  
       if (op->getOp() == rescale8_opnum) {  
           rescale = (float *)op->getPtrParam(1);  
           offset = (float *)op->getPtrParam(2);  
           // move to next operation along the chain  
           op = op->getOp1();  
       } else {  
           // default values for optional rescale  
           rescale = 0;  
           offset = 0;  
       }  
  
       // Now, the decompress operation  
       dc = (XilDeviceCompressionCodec*)  
           (op->getSrcCis())->getDeviceCompression();  
       dc->seek((int)op->getLongParam(1));  
                .  
                .  
  }  

The next example illustrates that you can have multiple routines in the same file. And, a file can contain both atoms and molecules and the XILCONFIG lines can be in any order (meaning they do not have to be in the order the routines are presented in the file).

  /* XILCONFIG: SubtractAdd8 = add8(subtract8()) */  
  /* XILCONFIG: Add8 = add8() */  
  /* XILCONFIG: MultiplyAdd8 = add8(multiply8())) */  
  
  XilDeviceComputeTypeMemory::Add8(XilOp *op, int op_count)  
  {  
                .  
                .  
  }  


  XilDeviceComputeTypeMemory::MultiplyAdd8(XilOp *op, int  
  op_count)  
  {  
                .  
                .  
  }  
  
  XilDeviceComputeTypeMemory::SubtractAdd8(XilOp *op, int  
  op_count)  
  {  
                .  
                .  
  }  

The compute device implementation follows these steps:
  1. Subclass the XilDeviceComputeType class, including the functions for this specific compute device. As an example, consider the case of a device that can add 8-bit images. The implementation would include a subclass like:


  class XilDeviceComputeTypeMyDevice: public XilDeviceComputeType{  
       public:  
           int MyAdd8(XilOp* op, int count);  
  };  

  1. After the implementation is compiled, link it into the execution table so that it will be referenced properly by the core code. The XIL library provides a nearly automatic means of doing this. For you to use this method, the implementation file must contain a comment line of the following type:

/* XILCONFIG: MyAdd8 = add8() */

The add8() indicates the XIL atom that the implementation MyAdd8 represents. Remember that multiple atoms and/or molecules may be implemented within a single routine and within a single file. For each atom and molecule, an appropriate XILCONFIG line must be present. The atomic functions available for implementing are described in Appendix B, "XIL Atomic Functions."
  1. After you have added the configuration lines, run the executable xilcompdesc. This executable automatically generates a file that, when compiled, will provide a routine to correctly integrate the implementation of the subclass into the XIL execution table. The executable

  $XILHOME/bin/xilcompdesc classname [files]

  parses the given files (or the standard input) looking for the configuration
  lines described above. It produces a C++ source file on standard output that
  is the implementation of a member function of the given class called
  describeMembers(). describeMembers() should be invoked inside
  XilCreateComputeType(). When invoked, describeMembers() adds
  all the member functions from this compute handler into the available
  function tree. For example,


  // routine called by the XIL core to initialize the band_memory  
  // compute device  
  XilDeviceComputeType* XilCreateComputeType()  
  {  
       XilDeviceComputeTypeBandMemory* device;  
  
       // create an instantiation of the device  
       device= new XilDeviceComputeTypeBandMemory();  
  
       // register with the core the functions that this device  
       // implements  
       if (device->describeMembers()==XIL_FAILURE) {  
           delete device;  
           return NULL;  
       } else {  
           return device;  
       }  
  }  

  1. Finally, there is a file /opt/SUNWits/Graphics-sw/xil/lib /xil.compute that lists the compute handlers and their dependencies (see Chapter 2, "More on Writing Device Handlers," for more information on

installing handlers). You must edit this file to give the library instructions on the order in which to load the compute modules (see the following section "Loading Compute Handlers"). The format looks like this:

  computememory  
  computeSUNWgx ioSUNWgx  
  computeMYCOMPANYmyhandler  

Loading Compute Handlers

As part of xil_open(), the library parses the xil.compute file, looking for the appropriate modules in /opt/SUNWits/Graphics-sw/xil/lib. The name of the compute handler is listed first, followed by any other handlers on which the compute handler is dependent. The compute handlers that have no dependents are loaded as they are reached in the xil.compute file.
In the following example, the first line loads all the functions in xilcomputememory.so, and the second line has a dependent, so xilcomputeSUNWgx.so and xilioSUNWgx.so are not loaded. The third line loads xilcomputeMYCOMPANYmyhandler.so.

  computememory  
  computeSUNWgx ioSUNWgx  
  computeMYCOMPANYmyhandler  

If this handler contains, for example, the add8() function, this new version replaces the existing memory version (which was loaded by the first line) in the function tree. Actually, each node in the function tree is kept as a linked list of function pointers. When the new version of add8() is encountered, it is simply added to the head of the list. When the add8() atom occurs in the course of processing, the first function in the list is called. If, for some reason, the function returns XIL_FAILURE, the second function in the list is called, and so forth down the list, until the memory version is called. This strategy allows a function implementation to limit the range of parameters for which it works, leaving undesired parameter sets to the memory version, which will work for all legal parameters.
Let's consider another example with compute handlers that have dependents:

  computeSUNWgx    ioSUNWgx  
  computeCell_SUNWgx Cell ioSUNWGX  

The compute handlers that have dependents are loaded when all the dependents have been loaded. For instance, the computeSUNWgx handler is dependent on the ioSUNWgx handler. When this I/O handler is loaded and initialized, then the compute handlers in xil.compute that have ioSUNWgx as their remaining dependency are loaded. Since ioSUNWgx is the only dependent of computeSUNWgx, this compute handler is loaded at that time. For example,

  xil_create_from_device(state, "ioSUNWgx", NULL);  
  // causes ioSUNWgx handler to be loaded  
  // once the I/O handler is loaded, then computeSUNWgx is loaded  

The computeCell_SUNWgx handler still has Cell as a dependent, so it would not be loaded yet.

Adding a New Molecule

The XIL core code determines the function that will be called for each combination of atomic functions. The first thing you must do when adding a new molecule is to decide what the molecule is to do.
XIL supports both single and multiple branch molecules. Single branch molecules can be described in the form:
function_1(function_2(...(function_N())...))

An example of code that can be written as a molecule is:

  xil_add(im1, im2, im3)  
  xil_add(im3, im4, im5)  

This code can be rewritten as a molecule of the form
xil_add(xil_add())

The output of the first add is used as an input to the second one.
An example of code that can be written as a multiple branch molecule is:

  xil_add(im1,im2,im3)  
  xil_add(im4,im5,im6)  
  xil_subtract(im3,im6,im7)  

Both sources in the subtract operation have dependencies. The left branch, which branches into source1 of the subtract operation, is the first add operation. The right branch, which branches into source2 of the subtract operation, is the second add operation.
The routines use the existing interface getOp1() to follow the left-most branch of the chain op operations. Two new interfaces on the XilOp class exist to access the right branch chain of ops: getOp2(), associated with source2, and getOp3(), associated with source3. See the section "Manipulating Molecules" in this chapter for more information.
Also, you can have a linear molecule along the right branch of the chain of operations. For example,

  xil_add(im1,im2,im3);  
  xil_subtract(im4,im3,im5);  

This code can be rewritten as a molecule of the form:
xil_subtract(,xil_add())

Adding a molecule is one example of adding a compute device. In the file that contains the implementation of a single branch molecule, the following configuration line must be included:
/* XILCONFIG: SingleBranchMoleculeName = atom2(atom1()) */
where atom2 and atom1 refer to existing atomic functions listed in Appendix B, "XIL Atomic Functions."
In the file that contains the implementation of a multiple branch molecule, the following configuration line must be included:
/* XILCONFIG: MultiBranchMoleculeName = atom1(atom2(),atom3()) */
In the file that contains the implementation of a linear molecule along the right branch of the chain of operations, the following configuration line must be included:
/* XILCONFIG: RightBranchMoleculeName = atom1(,atom2);
The configuration is done using xilcompdesc in the manner described previously for other compute handlers. The file /opt/SUNWits/ Graphics-sw/xil/lib/xil.compute must be updated in order for the compute handler to be loaded.
Like other compute handlers, molecules may execute on processors other than the host CPU. If it would be advantageous to allow the image data to remain in nonhost memory, a storage handler for the device is required. Storage device handlers are described in Chapter 5, "Storage Devices."
Appendix A, "Sample Molecule," shows an example of a molecule made up of rescale16 followed by convert16to8, using the standard memory storage handler. It is important to note that, like new atoms, molecules should strive to give the same answers as would be obtained through the atomic path. As an example, consider a molecule subtract_const8(add_const8()) that first adds a constant value to an 8-bit image and then subtracts away a possibly different constant value. It might seem reasonable to simply subtract the two constants from each other and add the difference to the source image. However, this could cause the molecule to behave differently from the atomic path. If the initial add causes the image values to exceed the maximum value for the data type (255), the result of the initial add must be clamped to that maximum value. Subtracting the second constant would subtract from 255, not from the source value plus the first constant. Since either the molecular or atomic path may be taken, depending on how the application is written, it is vital that they behave alike.

Manipulating Molecules

A molecule is a chain of atomic operations. For a molecule, the chain must be properly followed to extract in a logical order the parameters and images from the XilOp object. The op_count parameter is useful to determine how many atomic operations exist along the chain, in case molecules of varying lengths share the same routine.
The op passed to the routine is the op associated with the last operation in the chain (the operation that writes its output to a destination image). The molecule must extract the destination image and the function's parameters from the XilOp object. Then, to move up the chain of operations, you can use the following functions:
  • getOp1(), get pointer to op that has source1 as its destination
  • getOp2(), get pointer to op that has source2 as its destination
  • getOp3(), get pointer to op that has source3 as its destination
For information about the XilOp class, see the section "The XilOp Class" in Chapter 1.

Molecules and I/O Devices

I/O handlers may have their capture() or display() functions included as part of a molecule. For example, the final destination of a decompression molecule might be the display to enable digital video.

Note - Whenever the device is the final step in a molecule, it is important to mark the buffer memory as invalid by calling setMemoryValid(FALSE) from the device. Subsequent writes to the device may cause a capture() from the device to set the buffer.

In this case, the compute handler containing the molecule has a dependency on the I/O handler, since the latter contains the information to initialize the I/O device and contains the attributes. The xil.compute file contains this dependency information. The compute handlers that have dependents are loaded when all the dependents have been loaded.
Consider the following example:

  computememory  
  computeSUNWgx ioSUNWgx  
  computeMYCOMPANYmyhandler  
  computeMYCOMPANYmyotherhandler ioMYCOMPANYmyiodevice  

The computeSUNWgx handler contains several molecules that depend on the gx I/O handler. Before the computeSUNWgx handler is loaded, the ioSUNWgx handler must be loaded and initialized. The computeMYCOMPANYmyotherhandler handler contains molecules that depend on ioMYCOMPANYmyiodevice I/O handler. Before the computeMYCOMPANYmyotherhandler handler is loaded, the ioMYCOMPANYmyiodevice handler must be loaded and initialized.

Sample Compute Device Handler

This example illustrates a compute handler for images that are stored in a band-sequential format. The atomic function that the compute handler implements is add8(), the arithmetic addition of two 8-bit images. This compute handler requires a corresponding storage handler that stores images in band-sequential format. The example at the end of Chapter 5, "Storage Devices," illustrates such a handler. The files in this example include:
  • XilDeviceComputeTypeBandMemory.h and XilDeviceComputeTypeBandMemory.cc, which describe and implement the compute handler classes
  • Add8BandMemory.cc, which is the band-sequential implementation of add8()
  • band_memory_utils.cc, which contains utility functions for handling child images

XilDeviceComputeTypeBandMemory.h

Code Example 4-1 XilDeviceComputeTypeBandMemory.h

  //  
  // foo  
  //  
  #include <xil/XilDeviceComputeType.h>  
  
  class XilDeviceComputeTypeBandMemory : public XilDeviceComputeType {  
   public:  
     // constructor  
     XilDeviceComputeTypeBandMemory()  
     : XilDeviceComputeType("XilDeviceComputeBandMemory") {};  
  
     // destructor  
     ~XilDeviceComputeTypeBandMemory();  
  
     // local describeMembers routine  
     int describeMembers();  
  
     // routines that this compute device implements  
     int Add8(XilOp* op, int count);  
  };  

XilDeviceComputeTypeBandMemory.cc

Code Example 4-2 XilDeviceComputeTypeBandMemory.cc

  //  
  // foo  
  //  
  
  #include <xil/xili.h>  
  #include "XilDeviceComputeTypeBandMemory.h"  
  
  XilDeviceComputeTypeBandMemory::~XilDeviceComputeTypeBandMemory() {}  
  
  // routine called by the XIL core to initialize the band_memory  
  // compute device  
  XilDeviceComputeType* XilCreateComputeType()  
  {  
     XilDeviceComputeTypeBandMemory* device;  
  
     // create an instantiation of the device  
     device= new XilDeviceComputeTypeBandMemory();  
     if (device==NULL) {  
        XIL_ERROR(NULL,XIL_ERROR_RESOURCE,"di-1",TRUE);  
        return NULL;  
     }  
  
     // register with the core the functions that this device implements  
     if (device->describeMembers()==XIL_FAILURE) {  
        delete device;  
        return NULL;  
     } else {  
        return device;  
     }  
  }  

Add8BandMemory.cc

Code Example 4-3 Add8BandMemory.cc (1 of 6)

  //This line lets emacs recognize this as -*- C++ -*- Code  
  //------------------------------------------------------------------------  
  //  
  //  File:        Add8BandMemory.cc  
  //  Project:     XIL  
  //  Created:     93/04/30  
  //  RespEngr:    Chuck Mosher  
  //  Revision:    1.2  
  //  Last Mod:    11:34:47, 23 Mar 1994  
  //  
  //  Description:  
  //  
  //  This routine performs the arithmetic addition of 2 8-bit images  
  //  that are in band-sequential memory format  
  //  
  //  
  //------------------------------------------------------------------------  
  #pragma ident "@(#)Add8BandMemory.cc1.2\t94/03/23  "  
  
  #include <xil/XilDefines.h>  
  #include <xil/XilError.h>  
  #include <xil/XilImage.h>  
  #include <xil/XilOp.h>  
  #include <xil/XilRoi.h>  
  #include <xil/XilRoiList.h>  
  
  #include "../storage_device_handler/XilBandMemoryDefines.h"  
  
  //  
  // Class declaration of this particular compute function  
  //  
  class XilDeviceComputeTypeBandMemory : public XilDeviceComputeType {  
   public:  
     int Add8(XilOp* op, int count);  
     ~XilDeviceComputeTypeBandMemory();  
  };  
  //  
  // Global function which returns band-sequential storage information.  
  // This could also be handled locally by the compute functions.  
  //  

Code Example 4-3 Add8BandMemory.cc (2 of 6)

  XilBandMemoryStorage *getBandMemoryStorage(XilImage *);  
  
  //  
  // Line which describes which atomic function (or set of functions) that  
  // this routine implements.  The utility function "xilcompdesc" will cause  
  // this description to be included in the local version of describeMembers.cc  
  
  /* XILCONFIG: Add8 = add8() */  
  
  //  
  // The compute routine  
  //  
  XilDeviceComputeTypeBandMemory::Add8(  
      XilOp *op,      // a pointer into the DAG.  
      int)         // unused parameter (number of combined ops to be done).  
  {  
     /* region location */  
     long x,y;  
  
     /* region size */  
     unsigned int x_size,y_size;  
  
     /* the images */  
     XilImage *src1,*src2,*dest;  
  
     /* the base addresses */  
     Xil_unsigned8 *src1_base_addr,*src2_base_addr,*dest_base_addr;  
  
     /* pointer to the current band */  
     Xil_unsigned8 *src1_band,*src2_band,*dest_band;  
  
     /* pointer to the current scanline */  
     Xil_unsigned8 *src1_scanline,*src2_scanline,*dest_scanline;  
  
     /* pointer to the current pixel */  
     Xil_unsigned8 *src1_pixel,*src2_pixel,*dest_pixel;  
  
     /* next band offset (in bytes), range=1..65535 */  
     unsigned long src1_next_band,src2_next_band,dest_next_band;  
  
     /* next scanline offset (in bytes), range=1..(65535*255) */  
     unsigned long src1_next_scan,src2_next_scan,dest_next_scan;  

Code Example 4-3 Add8BandMemory.cc (3 of 6)

     /* x offset of image origin, range=1..65535 */  
     long src1_x_origin,src2_x_origin,dest_x_origin;  
  
     /* y offset of image origin, range=1..65535 */  
     long src1_y_origin,src2_y_origin,dest_y_origin;  
  
     /* the number of bands, range=1..255 */  
     unsigned int nbands;  
  
     /* loop counters */  
     unsigned int pixel_count, scanline_count, band_count;  
  
     /* get information about source 1 */  
     src1= op->getSrc1();  
     src1->getOrigin(&src1_x_origin,&src1_y_origin);  
  
     /* get source 1's memory storage */  
     XilBandMemoryStorageByte *storage;  
     storage= (XilBandMemoryStorageByte *)getBandMemoryStorage(src1);  
     if (storage==NULL) {  
        return XIL_FAILURE;  
     }  
     src1_base_addr= (Xil_unsigned8 *)storage->data;  
     src1_next_band= storage->band_stride;  
     src1_next_scan= storage->scanline_stride;  
     delete storage;  
  
     /* get information about source 2 */  
     src2= op->getSrc2();  
     src2->getOrigin(&src2_x_origin,&src2_y_origin);  
  
     /* get source 2's memory storage */  
     storage= (XilBandMemoryStorageByte *)getBandMemoryStorage(src2);  
     if (storage==NULL) {  
        return XIL_FAILURE;  
     }  
     src2_base_addr= (Xil_unsigned8 *)storage->data;  
     src2_next_band= storage->band_stride;  
     src2_next_scan= storage->scanline_stride;  
     delete storage;  
     /* get information about the destination */  
     dest= op->getDst();  

Code Example 4-3 Add8BandMemory.cc (4 of 6)

     dest->getOrigin(&dest_x_origin,&dest_y_origin);  
  
     /* get the destination's memory storage */  
     storage= (XilBandMemoryStorageByte *)getBandMemoryStorage(dest);  
     if (storage==NULL) {  
        return XIL_FAILURE;  
     }  
     dest_base_addr= (Xil_unsigned8 *)storage->data;  
     dest_next_band= storage->band_stride;  
     dest_next_scan= storage->scanline_stride;  
     delete storage;  
  
     /* get the number of bands (same for all images) */  
     dest->getDimensions(NULL,NULL,&nbands);  
  
     /* get the ROI (and the list of rectangles) that comprises the intersection  
        of the ROIs of src1, src2, and dest */  
     XilRoi* roi;  
     XilRoiList* roi_list= XiliGetRoiList(&roi,dest,src1,src2);  
     if (roi_list==NULL) {  
        XIL_ERROR(src1->getSystemState(), XIL_ERROR_SYSTEM, "di-12", FALSE);  
        return XIL_FAILURE;  
     }  
  
     /*  
      *  Now that we've intersected to determine the pixels that will  
      *  be touched in the destination, set the pixelsTouchedRoi on  
      *  the image.  
      */  
     dest->setPixelsTouchedRoi(roi);  
     dest->setPixelsTouchedRoi_flag(TRUE);  
  
     /* operate on each ROI, all of the ROI's are guaranteed not to go outside  
      * any of the images  
      */  
     while (roi_list->next(&x,&y,&x_size,&y_size)) {  
        // adjust starting addresses to take image origins into account  
        src1_band= src1_base_addr+((y+src1_y_origin)*src1_next_scan)+  
                       ((x+src1_x_origin));  
        src2_band= src2_base_addr+((y+src2_y_origin)*src2_next_scan)+  
                       ((x+src2_x_origin));  

Code Example 4-3 Add8BandMemory.cc (5 of 6)

        dest_band= dest_base_addr+((y+dest_y_origin)*dest_next_scan)+  
                       ((x+dest_x_origin));  
  
        band_count = nbands;  
        do { /* each band */  
  
           /* point to the first scanline of the band */  
           src1_scanline= src1_band;  
           src2_scanline= src2_band;  
           dest_scanline= dest_band;  
  
           scanline_count=y_size;  
           do { /* each scanline */  
  
              /* point to the first pixel of the scanline */  
              src1_pixel= src1_scanline;  
              src2_pixel= src2_scanline;  
              dest_pixel= dest_scanline;  
  
              pixel_count= x_size;  
              do { // each pixel  
  
                  /* result cannot be greater than MAXBYTE */  
                  int result = (int)(*src1_pixel + *src2_pixel);  
       *dest_pixel = ((result>>8) ? (0xff) : (result & 0xff));  
  
                 /* move to next data element */  
                 src1_pixel++;  
                 src2_pixel++;  
                 dest_pixel++;  
              } while (--pixel_count);  
  
              /* move to the next scanline */  
              src1_scanline+=src1_next_scan;  
              src2_scanline+=src2_next_scan;  
              dest_scanline+=dest_next_scan;  
           } while (--scanline_count);  
  
           /* move to the next band */  
           src1_band+=src1_next_band;  
           src2_band+=src2_next_band;  
           dest_band+=dest_next_band;  

Code Example 4-3 Add8BandMemory.cc (6 of 6)

        } while (--band_count);  
     }  
  
     /* get rid of the roi_list  
      * (the roi stored in dest "pixelsTouchedRoi"  
      * will be destroyed by the xil core)  
      */  
     roi_list->destroy();  
  
     return(XIL_SUCCESS);  
  }  

band_memory_utils.cc

Code Example 4-4 band_memory_utils.cc (1 of 3)

  //  
  //  Utility routine(s) that are needed by the band_memory compute routines  
  //  
  #include <xil/XilDefines.h>  
  #include <xil/XilError.h>  
  #include <xil/XilImage.h>  
  #include <xil/XilOp.h>  
  
  #include "../storage_device_handler/XilBandMemoryDefines.h"  
  
  //  
  //  Device storage information returned by the getStorage() function  
  //  is always with respect to the parent image.  IHVs must write a  
  //  routine similar to this one to handle the case of child images.  
  //  This routine adjusts the image data pointer and offset information  
  //  in case the image being requested is a child.  
  //  
  XilBandMemoryStorage*  
  getBandMemoryStorage(XilImage *image)  
  {  
      XilBandMemoryStorage  *storage;  
      XilBandMemoryStorage  *parent_storage;  
  
      // get the parent storage description  
      parent_storage= (XilBandMemoryStorage*)image->getStorage("band_memory");  
      if (parent_storage==NULL) {  
          return NULL;  
      }  
  
      //  
      // Allocate the storage description to return to the compute routine.  
      // The compute routine will then be responsible for deleting it.  
      //  
      // Note that this is done differently in the XIL memory storage driver  
      // and associated memory compute routines (the memory storage driver  
      // just passes back a reference that the compute routine does not delete).  
      // It is up to the IHV writing these functions to decide how they want  
      // to implement this.  
      //  

Code Example 4-4 band_memory_utils.cc (2 of 3)

      storage = new XilBandMemoryStorage;  
      if (storage==NULL) {  
          return NULL;  
      }  
  
      //  
      //  Get image offset information  
      //  
      unsigned int offsetX, offsetY, offsetBand;  
      image->getChildOffsets(&offsetX,&offsetY,&offsetBand);  
  
      if(offsetX || offsetY || offsetBand) {  
       //  
          //  If this is a child, take offsets into account  
       //  
          XilDataType datatype= image->getDataType();  
          switch (datatype) {  
            case XIL_BIT:  
              storage->bit.scanline_stride= parent_storage->bit.scanline_stride;  
              storage->bit.band_stride= parent_storage->bit.band_stride;  
              storage->bit.offset= (unsigned char)  
                   (((unsigned long)parent_storage->bit.offset +  
                offsetX)%(unsigned long)8);  
              storage->bit.data= parent_storage->bit.data +  
                         parent_storage->bit.band_stride*offsetBand +  
                         parent_storage->bit.scanline_stride*offsetY  +  
                    offsetX;  
              break;  
  
            case XIL_BYTE:  
              storage->byte.scanline_stride= parent_storage->byte.scanline_stride;  
              storage->byte.band_stride= parent_storage->byte.band_stride;  
              storage->byte.data= parent_storage->byte.data +  
                         parent_storage->byte.band_stride*offsetBand +  
                         parent_storage->byte.scanline_stride*offsetY  +  
                    offsetX;  
              break;  
  
            case XIL_SHORT:  
              storage->shrt.scanline_stride= parent_storage->shrt.scanline_stride;  
              storage->shrt.band_stride= parent_storage->shrt.band_stride;  
              storage->shrt.data= parent_storage->shrt.data +  

Code Example 4-4 band_memory_utils.cc (3 of 3)

                         parent_storage->shrt.band_stride*offsetBand +  
                         parent_storage->shrt.scanline_stride*offsetY  +  
                    offsetX;  
              break;  
  
            case XIL_FLOAT:  
              storage->flt.scanline_stride= parent_storage->flt.scanline_stride;  
              storage->flt.band_stride= parent_storage->flt.band_stride;  
              storage->flt.data= parent_storage->flt.data +  
                         parent_storage->flt.band_stride*offsetBand +  
                         parent_storage->flt.scanline_stride*offsetY  +  
                    offsetX;  
              break;  
          }  
  
      }  
      else {  
       //  
       // can just copy the parent description  
       //  
          memcpy(storage, parent_storage, sizeof(XilBandMemoryStorage));  
      }  
  
      return(storage);  
  }