ArcObjects Library Reference  

WatermarkFunction

About the Create a custom raster function Sample

[C#]

WatermarkFunction.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using ESRI.ArcGIS.DataSourcesGDB;
using ESRI.ArcGIS.DataSourcesRaster;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.ADF.CATIDs;

namespace CustomFunction
{
    /// <summary>
    /// Enumerator for the location of the watermark.
    /// </summary>
    [Guid("148F193A-0D46-4d4c-BC9C-A05AC4BE0BAB")]
    [ComVisible(true)]
    public enum esriWatermarkLocation
    {
        esriWatermarkTopLeft = 0,
        esriWatermarkTopRight,
        esriWatermarkCenter,
        esriWatermarkBottomLeft,
        esriWatermarkBottomRight
    };

    [Guid("168721E7-7010-4a36-B886-F644437B164D")]
    [ClassInterface(ClassInterfaceType.None)]
    [ProgId("CustomFunction.WatermarkFunction")]
    [ComVisible(true)]
    public class WatermarkFunction : IRasterFunction, 
                                     IPersistVariant,
                                     IDocumentVersionSupportGEN,
                                     IXMLSerialize,
                                     IXMLVersionSupport
    {
        #region Private Members
        UID myUID; // UID for the Watermark Function.
        IRasterInfo myRasterInfo; // Raster Info for the Watermark Function
        rstPixelType myPixeltype; // Pixel Type of the Watermark Function.
        string myName; // Name of the Watermark Function.
        string myDescription; // Description of the Watermark Function.
        bool myValidFlag; // Flag to specify validity of the Watermark Function.
        string myWatermarkImagePath; // Path to the Watermark Image.
        double myBlendPercentage; // Percentage of the blending.
        double blendValue; // Actual value of the blend percentage.
        esriWatermarkLocation myWatermarkLocation; // Location of the Watermark.
        IRasterFunctionHelper myFunctionHelper; // Raster Function Helper object.
        Bitmap myWatermarkImage; // Watermark Image object.
        #endregion

        public WatermarkFunction()
        {
            myWatermarkImagePath = "";
            myBlendPercentage = 50.00; // Default value for the blending percentage.
            blendValue = 0.50; // Default value for the blend value.
            myWatermarkLocation = esriWatermarkLocation.esriWatermarkBottomRight;

            myName = "WatermarkFunction";
            myPixeltype = rstPixelType.PT_UNKNOWN; // Default value for the pixel type.
            myDescription = "Add a watermark to the request.";
            myValidFlag = true;

            myFunctionHelper = new RasterFunctionHelperClass();

            myWatermarkImage = null;

            myUID = new UIDClass();
            myUID.Value = "{" + "168721E7-7010-4a36-B886-F644437B164D" + "}";
        }

        #region IRasterFunction Members
        /// <summary>
        /// Name of the Raster Function.
        /// </summary>
        public string Name
        {
            get
            {
                return myName;
            }
            set
            {
                myName = value;
            }
        }

        /// <summary>
        /// Pixel Type of the Raster Function
        /// </summary>
        public rstPixelType PixelType
        {
            get
            {
                return myPixeltype;
            }
            set
            {
                myPixeltype = value;
            }
        }

        /// <summary>
        /// Output Raster Info for the Raster Function
        /// </summary>
        public IRasterInfo RasterInfo
        {
            get 
            {
                return myRasterInfo; 
            }
        }

        /// <summary>
        /// Description of the Raster Function
        /// </summary>
        public string Description
        {
            get
            {
                return myDescription;
            }
            set
            {
                myDescription = value;
            }
        }

        /// <summary>
        /// Initialize the Raster function using the argument object. This is one of the two
        /// main functions to implement for a custom Raster function. The raster object is 
        /// dereferenced if required and given to the RasterFuntionHelper object to bind.
        /// </summary>
        /// <param name="pArguments">Arguments object used for initialization</param>
        public void Bind(object pArguments)
        {
            try
            {
                // Check if the Arguments object is of the correct type.
                IWatermarkFunctionArguments watermarkFuncArgs = null;
                if (pArguments is IWatermarkFunctionArguments)
                {
                    watermarkFuncArgs = (IWatermarkFunctionArguments)pArguments;
                    myBlendPercentage = watermarkFuncArgs.BlendPercentage;
                    myWatermarkImagePath = watermarkFuncArgs.WatermarkImagePath;
                    myWatermarkLocation = watermarkFuncArgs.WatermarkLocation;
                    
                    object inputRaster = watermarkFuncArgs.Raster;
                    if (watermarkFuncArgs.Raster is IRasterFunctionVariable)
                    {
                        IRasterFunctionVariable rasterFunctionVariable =
                            (IRasterFunctionVariable)watermarkFuncArgs.Raster;
                        inputRaster = rasterFunctionVariable.Value;
                    }
                    
                    // Call the Bind method of the Raster Function Helper object.
                    myFunctionHelper.Bind(inputRaster);
                }
                else
                {
                    // Throw an error if incorrect arguments object is passed.
                    throw new System.Exception(
                        "Incorrect arguments object. Expected: IWatermarkFunctionArguments");
                }

                // Get the raster info and Pixel Type from the RasterFunctionHelper object.
                myRasterInfo = myFunctionHelper.RasterInfo;
                myPixeltype = myRasterInfo.PixelType;

                // Convert blending percentage to blending value.
                if (myBlendPercentage >= 0.0 && myBlendPercentage <= 100.0)
                    blendValue = myBlendPercentage / 100.0;
                else /// A value of 50% is used as default.
                    blendValue = 0.50;

                if (myWatermarkImagePath != "")
                {
                    // Load the watermark image from the path provided
                    myWatermarkImage = new Bitmap(myWatermarkImagePath);
                    // and check the pixel type of the loaded image to see if its compatible.
                    if (myWatermarkImage.PixelFormat != System.Drawing.Imaging.PixelFormat.Format32bppArgb &&
                        myWatermarkImage.PixelFormat != System.Drawing.Imaging.PixelFormat.Format24bppRgb)
                    {
                        // Throw error if the image is not compatible.
                        throw new System.Exception(
                            "Invalid watermark image. Please provide one with 8 bits per band in ARGB or RGB format.");
                    }

                    // Cleanup
                    myWatermarkImage.Dispose();
                    myWatermarkImage = null;
                }
            }
            catch (Exception exc)
            {
                #region Cleanup
                if (myWatermarkImage != null)
                    myWatermarkImage.Dispose();
                myWatermarkImage = null;
                #endregion

                System.Exception myExc = new System.Exception(
                    "Exception caught in Bind method of Watermark Function. " + exc.Message, exc);
                throw myExc;
            }
        }

        /// <summary>
        /// Read pixels from the input Raster and fill the PixelBlock provided with processed pixels.
        /// The RasterFunctionHelper object is used to handle pixel type conversion and resampling.
        /// The watermark image is then blended to the bottom right corner of the pixel block. 
        /// </summary>
        /// <param name="pTlc">Point to start the reading from in the Raster</param>
        /// <param name="pRaster">Reference Raster for the PixelBlock</param>
        /// <param name="pPixelBlock">PixelBlock to be filled in</param>
        public void Read(IPnt pTlc, IRaster pRaster, IPixelBlock pPixelBlock)
        {
            BitmapData wMBitmapData = null;
            double pixelValue = 0.0;
            int wmRow = 0;
            int wmCol = 0;
            try
            {
                // Call Read method of the Raster Function Helper object.
                myFunctionHelper.Read(pTlc, null, pRaster, pPixelBlock);

                int wMBandOffset = 0;

                #region Reference Raster Properties
                // Get the pixel type of the reference raster to see if 
                // it is compatible (8 bit).
                IRasterProps referenceProps = (IRasterProps)pRaster;
                if (referenceProps.PixelType != rstPixelType.PT_UCHAR &&
                    referenceProps.PixelType != rstPixelType.PT_CHAR)
                {
                    throw new System.Exception(
                        "Function can only be applied to 8bit data.");
                }

                #endregion

                #region Load watermark image object
                // Create new image object for the watermark image.
                myWatermarkImage = new Bitmap(myWatermarkImagePath);
                // Read number of bands of the watermark image.
                if (myWatermarkImage.PixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppArgb)
                    wMBandOffset = 4;
                else
                {
                    if (myWatermarkImage.PixelFormat == System.Drawing.Imaging.PixelFormat.Format24bppRgb)
                        wMBandOffset = 3;
                    else
                    {
                        throw new System.Exception(
                            "Invalid bitmap. Please provide one with 8bits per band in ARGB or RGB format.");
                    }
                }
                #endregion

                int pBHeight = pPixelBlock.Height;
                int pBWidth = pPixelBlock.Width;
                int wMHeight = myWatermarkImage.Height;
                int wMWidth = myWatermarkImage.Width;
                int wMRowIndex = 0;
                int wMColIndex = 0;
                int pBRowIndex = 0;
                int pBColIndex = 0;
                int endRow = 0;
                int endCol = 0;
                bool waterStartCol = false;
                bool waterStartRow = false;

                // Calculate the row/column values that specify where to start the blending.
                #region Calculate Indices
                /// If the number of rows of the pixelblock are more than the watermark image
                endRow = pBHeight;
                if (pBHeight >= wMHeight)
                {
                    /// Set the row index to start blending in the pixelblock.
                    switch (myWatermarkLocation)
                    {
                        case esriWatermarkLocation.esriWatermarkTopLeft:
                            {
                                pBRowIndex = 0;
                                endRow = pBRowIndex + wMHeight;
                                break;
                            }
                        case esriWatermarkLocation.esriWatermarkTopRight:
                            {
                                pBRowIndex = 0;
                                endRow = pBRowIndex + wMHeight;
                                break;
                            }
                        case esriWatermarkLocation.esriWatermarkCenter:
                            {
                                pBRowIndex = (pBHeight / 2) - (wMHeight / 2);
                                endRow = pBRowIndex + wMHeight;
                                break;
                            }
                        case esriWatermarkLocation.esriWatermarkBottomLeft:
                            {
                                pBRowIndex = pBHeight - wMHeight;
                                break;
                            }
                        case esriWatermarkLocation.esriWatermarkBottomRight:
                            {
                                pBRowIndex = pBHeight - wMHeight;
                                break;
                            }
                        default:
                            break;
                    }

                    if (myWatermarkLocation == esriWatermarkLocation.esriWatermarkCenter)
                    {
                        pBRowIndex = (pBHeight / 2) - (wMHeight / 2);
                        endRow = pBRowIndex + wMHeight;
                    }
                }
                else /// If the number of rows of the watermark image is more than that of the pixelblock.
                {
                    /// Set the row index to start blending in the watermark image.
                    wMRowIndex = (wMHeight - pBHeight);
                    waterStartRow = true;
                }

                /// If the number of cols of the pixelblock are more than the watermark image
                endCol = pBWidth;
                if (pBWidth >= wMWidth)
                {
                    /// Set the col index to start blending in the pixelblock.
                    /// Set the row index to start blending in the pixelblock.
                    switch (myWatermarkLocation)
                    {
                        case esriWatermarkLocation.esriWatermarkTopLeft:
                            {
                                pBColIndex = 0;
                                endCol = pBColIndex + wMWidth;
                                break;
                            }
                        case esriWatermarkLocation.esriWatermarkTopRight:
                            {
                                pBColIndex = pBWidth - wMWidth;
                                break;
                            }
                        case esriWatermarkLocation.esriWatermarkCenter:
                            {
                                pBColIndex = (pBWidth / 2) - (wMWidth / 2);
                                endCol = pBColIndex + wMWidth;
                                break;
                            }
                        case esriWatermarkLocation.esriWatermarkBottomLeft:
                            {
                                pBColIndex = 0;
                                endCol = pBColIndex + wMWidth;
                                break;
                            }
                        case esriWatermarkLocation.esriWatermarkBottomRight:
                            {
                                pBColIndex = pBWidth - wMWidth;
                                break;
                            }
                        default:
                            break;
                    }
                }
                else /// If the number of cols of the watermark image is more than that of the pixelblock.
                {
                    /// Set the col index to start blending in the watermark image.
                    wMColIndex = (wMWidth - pBWidth);
                    waterStartCol = true;
                }
                #endregion

                #region Prepare Watermark Image for reading
                // Get the pixels from the watermark image using the lockbits function.
                wMBitmapData = myWatermarkImage.LockBits(new Rectangle(0, 0, wMWidth, wMHeight),
                    ImageLockMode.ReadOnly, myWatermarkImage.PixelFormat);

                System.IntPtr wMScan0 = wMBitmapData.Scan0;
                int wMStride = wMBitmapData.Stride;
                #endregion

                // The unsafe keyword is used so that pointers can be used to access pixels
                // from the watermark image.
                unsafe
                {
                    int wMPaddingOffset = wMStride - (myWatermarkImage.Width * wMBandOffset);

                    // Start filling from the correct row, col in the pixelblock 
                    // using the indices calculated above
                    System.Array pixelValues;
                    if (pPixelBlock.Planes == 3)
                    {
                        if (wMBandOffset == 4) // To check for transparency in WM Image
                        {
                            #region 3 Band PixelBlock
                            for (int nBand = 0; nBand < pPixelBlock.Planes; ++nBand)
                            {
                                byte* wMStartByte = (byte*)(void*)wMScan0;

                                /// If the number of rows of the watermark image are more than the request.
                                if (waterStartRow) /// Skip to the correct row in the watermark image.
                                    wMStartByte += (wMStride * wMRowIndex);

                                IPixelBlock3 ipPixelBlock = (IPixelBlock3)pPixelBlock;
                                pixelValues = (System.Array)(ipPixelBlock.get_PixelData(nBand));
                                for (int i = pBRowIndex; i < endRow; i++, ++wmRow)
                                {
                                    /// If the number of cols of the watermark image are more than the request.
                                    if (waterStartCol) /// Skip to the correct column in the watermark image.                                
                                        wMStartByte += (wMColIndex * wMBandOffset);

                                    for (int k = pBColIndex; k < endCol; k++, ++wmCol)
                                    {
                                        pixelValue = Convert.ToDouble(pixelValues.GetValue(k, i));
                                        if (Convert.ToDouble(wMStartByte[3]) != 0.0 &&
                                            Convert.ToByte(ipPixelBlock.GetNoDataMaskVal(nBand, k, i)) == 1)
                                        {
                                            // Blend the pixelValue from the PixelBlock with the corresponding
                                            // pixel from the watermark image.
                                            pixelValue = ((1 - blendValue) * pixelValue) + (blendValue *
                                                Convert.ToDouble(wMStartByte[2 - nBand]));
                                        }
                                        pixelValues.SetValue(Convert.ToByte(pixelValue), k, i);

                                        wMStartByte += wMBandOffset;
                                    }
                                    wMStartByte += wMPaddingOffset;
                                }
                                ((IPixelBlock3)pPixelBlock).set_PixelData(nBand, pixelValues);
                            }
                            #endregion
                        }
                        else
                        {
                            #region 3 Band PixelBlock
                            for (int nBand = 0; nBand < pPixelBlock.Planes; ++nBand)
                            {
                                byte* wMStartByte = (byte*)(void*)wMScan0;

                                /// If the number of rows of the watermark image are more than the request.
                                if (waterStartRow) /// Skip to the correct row in the watermark image.
                                    wMStartByte += (wMStride * wMRowIndex);

                                IPixelBlock3 ipPixelBlock = (IPixelBlock3)pPixelBlock;
                                pixelValues = (System.Array)(ipPixelBlock.get_PixelData(nBand));
                                for (int i = pBRowIndex; i < endRow; i++, ++wmRow)
                                {
                                    /// If the number of cols of the watermark image are more than the request.
                                    if (waterStartCol) /// Skip to the correct column in the watermark image.                                
                                        wMStartByte += (wMColIndex * wMBandOffset);

                                    for (int k = pBColIndex; k < endCol; k++, ++wmCol)
                                    {
                                        pixelValue = Convert.ToDouble(pixelValues.GetValue(k, i));
                                        if (Convert.ToByte(ipPixelBlock.GetNoDataMaskVal(nBand, k, i)) == 1)
                                        {
                                            // Blend the pixelValue from the PixelBlock with the corresponding
                                            // pixel from the watermark image.
                                            pixelValue = ((1 - blendValue) * pixelValue) + (blendValue *
                                                Convert.ToDouble(wMStartByte[2 - nBand]));
                                        }
                                        pixelValues.SetValue(Convert.ToByte(pixelValue), k, i);

                                        wMStartByte += wMBandOffset;
                                    }
                                    wMStartByte += wMPaddingOffset;
                                }
                                ((IPixelBlock3)pPixelBlock).set_PixelData(nBand, pixelValues);
                            }
                            #endregion
                        }
                    }
                    else
                    {
                        if (wMBandOffset == 4) // To check for transparency in WM Image
                        {
                            #region Not 3 Band PixelBlock
                            for (int nBand = 0; nBand < pPixelBlock.Planes; ++nBand)
                            {
                                byte* wMStartByte = (byte*)(void*)wMScan0;

                                /// If the number of rows of the watermark image are more than the request.
                                if (waterStartRow) /// Skip to the correct row in the watermark image.
                                    wMStartByte += (wMStride * wMRowIndex);

                                IPixelBlock3 ipPixelBlock = (IPixelBlock3)pPixelBlock;
                                pixelValues = (System.Array)(ipPixelBlock.get_PixelData(nBand));
                                int nooftimes = 0;
                                int noofskips = 0;
                                for (int i = pBRowIndex; i < endRow; i++, ++wmRow)
                                {
                                    /// If the number of cols of the watermark image are more than the request.
                                    if (waterStartCol) /// Skip to the correct column in the watermark image.                                
                                        wMStartByte += (wMColIndex * wMBandOffset);

                                    for (int k = pBColIndex; k < endCol; k++, ++wmCol)
                                    {
                                        pixelValue = Convert.ToDouble(pixelValues.GetValue(k, i));
                                        if (Convert.ToByte(ipPixelBlock.GetNoDataMaskVal(nBand, k, i)) == 1)
                                        //Convert.ToDouble(wMStartByte[3]) != 0.0 &&
                                        {
                                            // Calculate the average value of the pixels of the watermark image
                                            double avgValue = (Convert.ToDouble(wMStartByte[0]) +
                                                               Convert.ToDouble(wMStartByte[1]) +
                                                               Convert.ToDouble(wMStartByte[2])) / 3;

                                            // and blend it with the pixelValue from the PixelBlock.
                                            pixelValue = ((1 - blendValue) * pixelValue) +
                                                         (blendValue * avgValue);
                                        }
                                        pixelValues.SetValue(Convert.ToByte(pixelValue), k, i);

                                        ++nooftimes;
                                        noofskips += wMBandOffset;
                                        wMStartByte += wMBandOffset;
                                    }
                                    wMStartByte += wMPaddingOffset;
                                }
                                ((IPixelBlock3)pPixelBlock).set_PixelData(nBand, pixelValues);
                            }
                            #endregion
                        }
                        else
                        {
                            #region Not 3 Band PixelBlock
                            for (int nBand = 0; nBand < pPixelBlock.Planes; ++nBand)
                            {
                                byte* wMStartByte = (byte*)(void*)wMScan0;

                                /// If the number of rows of the watermark image are more than the request.
                                if (waterStartRow) /// Skip to the correct row in the watermark image.
                                    wMStartByte += (wMStride * wMRowIndex);

                                IPixelBlock3 ipPixelBlock = (IPixelBlock3)pPixelBlock;
                                pixelValues = (System.Array)(ipPixelBlock.get_PixelData(nBand));
                                int nooftimes = 0;
                                int noofskips = 0;
                                for (int i = pBRowIndex; i < endRow; i++, ++wmRow)
                                {
                                    /// If the number of cols of the watermark image are more than the request.
                                    if (waterStartCol) /// Skip to the correct column in the watermark image.                                
                                        wMStartByte += (wMColIndex * wMBandOffset);

                                    for (int k = pBColIndex; k < endCol; k++, ++wmCol)
                                    {
                                        pixelValue = Convert.ToDouble(pixelValues.GetValue(k, i));
                                        if (Convert.ToByte(ipPixelBlock.GetNoDataMaskVal(nBand, k, i)) == 1)
                                        {
                                            // Calculate the average value of the pixels of the watermark image
                                            double avgValue = (Convert.ToDouble(wMStartByte[0]) +
                                                               Convert.ToDouble(wMStartByte[1]) +
                                                               Convert.ToDouble(wMStartByte[2])) / 3;

                                            // and blend it with the pixelValue from the PixelBlock.
                                            pixelValue = ((1 - blendValue) * pixelValue) +
                                                         (blendValue * avgValue);
                                        }
                                        pixelValues.SetValue(Convert.ToByte(pixelValue), k, i);

                                        ++nooftimes;
                                        noofskips += wMBandOffset;
                                        wMStartByte += wMBandOffset;
                                    }
                                    wMStartByte += wMPaddingOffset;
                                }
                                ((IPixelBlock3)pPixelBlock).set_PixelData(nBand, pixelValues);
                            }
                            #endregion
                        }
                    }
                }

                #region Cleanup
                myWatermarkImage.UnlockBits(wMBitmapData);
                myWatermarkImage.Dispose();
                myWatermarkImage = null;
                wMBitmapData = null;
                wMScan0 = (System.IntPtr)null;
                wMStride = 0;
                #endregion
            }
            catch (Exception exc)
            {
                #region Cleanup
                if (wMBitmapData != null)
                    myWatermarkImage.UnlockBits(wMBitmapData);
                wMBitmapData = null;
                if (myWatermarkImage != null)
                    myWatermarkImage.Dispose();
                myWatermarkImage = null;
                #endregion

                System.Exception myExc = new System.Exception(
                    "Exception caught in Read method of Watermark Function. " + exc.Message, exc);
                throw myExc;
            }
        }

        /// <summary>
        /// Update the Raster Function
        /// </summary>
        public void Update()
        {
            try
            {
            }
            catch (Exception exc)
            {
                System.Exception myExc = new System.Exception(
                    "Exception caught in Update method of Watermark Function", exc);
                throw myExc;
            }
        }

        /// <summary>
        /// Property that specifies whether the Raster Function is still valid.
        /// </summary>
        public bool Valid
        {
            get { return myValidFlag; }
        }
        #endregion

        #region IPersistVariant Members
        /// <summary>
        /// UID to identify the function.
        /// </summary>
        public UID ID
        {
            get
            {
                return myUID;
            }
        }

        /// <summary>
        /// Load the properties of the function from the stream provided
        /// </summary>
        /// <param name="Stream">Stream that contains the serialized form of the function</param>
        public void Load(IVariantStream Stream)
        {
            if (Stream is IDocumentVersion)
            {
                IDocumentVersion docVersion = (IDocumentVersion)Stream;
                if (docVersion.DocumentVersion >= esriArcGISVersion.esriArcGISVersion10)
                {
                    esriArcGISVersion streamVersion = (esriArcGISVersion)((int)Stream.Read());
                    if (streamVersion >= esriArcGISVersion.esriArcGISVersion10)
                    {
                        myName = (string)Stream.Read();
                        myDescription = (string)Stream.Read();
                        myPixeltype = (rstPixelType)((int)Stream.Read());
                    }
                }
            }
        }

        /// <summary>
        /// Save the properties of the function to the stream provided
        /// </summary>
        /// <param name="Stream">Stream to which to serialize the function into</param>
        public void Save(IVariantStream Stream)
        {
            if (Stream is IDocumentVersion)
            {
                IDocumentVersion docVersion = (IDocumentVersion)Stream;
                if (docVersion.DocumentVersion >= esriArcGISVersion.esriArcGISVersion10)
                {
                    Stream.Write((int)esriArcGISVersion.esriArcGISVersion10);
                    Stream.Write(myName);
                    Stream.Write(myDescription);
                    Stream.Write((int)myPixeltype);
                }
            }
        }
        #endregion

        #region IDocumentVersionSupportGEN Members
        /// <summary>
        /// Convert the instance into an object supported by the given version
        /// </summary>
        /// <param name="docVersion">Version to convert to</param>
        /// <returns>Object that supports given version</returns>
        public object ConvertToSupportedObject(esriArcGISVersion docVersion)
        {
            return null;
        }

        /// <summary>
        /// Check if the object is supported at the given version
        /// </summary>
        /// <param name="docVersion">Version to check against</param>
        /// <returns>True if the object is supported</returns>
        public bool IsSupportedAtVersion(esriArcGISVersion docVersion)
        {
            if (docVersion >= esriArcGISVersion.esriArcGISVersion10)
                return true;
            else
                return false;
        }

        #endregion

        #region IXMLSerialize Members
        /// <summary>
        /// Deserialize the Raster Function from the datastream provided
        /// </summary>
        /// <param name="data">Xml stream to deserialize the function from</param>
        public void Deserialize(IXMLSerializeData data)
        {
            myName = data.GetString(data.Find("Name"));
            myDescription = data.GetString(data.Find("Description"));
            myPixeltype = (rstPixelType)(data.GetInteger(data.Find("PixelType")));
        }

        /// <summary>
        /// Serialize the Raster Function into the stream provided.
        /// </summary>
        /// <param name="data">Xml stream to serialize the function into</param>
        public void Serialize(IXMLSerializeData data)
        {
            data.TypeName = "WatermarkFunction";
            data.TypeNamespaceURI = @"http://www.esri.com/schemas/ArcGIS/10.1";
            data.AddString("Name", myName);
            data.AddString("Description", myDescription);
            data.AddInteger("PixelType", (int)myPixeltype);
        }
        #endregion

        #region IXMLVersionSupport Members
        /// <summary>
        /// Returns the namespaces supported by the object
        /// </summary>
        public string MinNamespaceSupported
        {
            get { return @"http://www.esri.com/schemas/ArcGIS/10.1"; }
        }
        #endregion

        #region COM Registration Function(s)
        [ComRegisterFunction()]
        static void Reg(string regKey)
        {
            ESRI.ArcGIS.ADF.CATIDs.RasterFunctions.Register(regKey);
        }

        [ComUnregisterFunction()]
        static void Unreg(string regKey)
        {
            ESRI.ArcGIS.ADF.CATIDs.RasterFunctions.Unregister(regKey);
        }
        #endregion
    }

    [Guid("933A9DEF-D56F-4e37-911D-AC16982CA697")]
    [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IWatermarkFunctionArguments : IRasterFunctionArguments, IPersistVariant, IXMLSerialize
    {
        #region WatermarkFunctionArguments Members
        [DispId(0x50505001)]
        object Raster
        {
            get;
            set;
        }

        [DispId(0x50505002)]
        string WatermarkImagePath
        {
            get;
            set;
        }

        [DispId(0x50505003)]
        double BlendPercentage
        {
            get;
            set;
        }

        [DispId(0x50505004)]
        esriWatermarkLocation WatermarkLocation
        {
            get;
            set;
        }
        #endregion
    }

    [Guid("996DC8E5-086B-41b5-919A-A3B9B86F2B30")]
    [ClassInterface(ClassInterfaceType.None)]
    [ProgId("CustomFunction.WatermarkFunctionArguments")]
    [ComVisible(true)]
    public class WatermarkFunctionArguments : IWatermarkFunctionArguments, 
                                              IPersistVariant,
                                              IDocumentVersionSupportGEN,
                                              IXMLSerialize,
                                              IXMLVersionSupport
    {
        #region Private Members
        string myName;
        string myDescription;
        UID myUID; // UID for the Watermark Function.
        IPropertySet myProperties; // Property set to store the properties of the arguments object.
        #endregion

        public WatermarkFunctionArguments()
        {
            myName = "WatermarkFunctionArguments";
            myDescription = "Arguments object for the Watermark Function";

            // Set default values
            myProperties = new PropertySetClass();
            myProperties.SetProperty("Raster", null);
            myProperties.SetProperty("WatermarkImagePath", "");
            myProperties.SetProperty("BlendPercentage", 50.00);
            myProperties.SetProperty("WatermarkLocation", CustomFunction.esriWatermarkLocation.esriWatermarkBottomRight);

            myUID = new UIDClass();
            myUID.Value = "{" + "996DC8E5-086B-41b5-919A-A3B9B86F2B30" + "}";
        }

        #region WatermarkFunctionArguments Members
        /// <summary>
        /// Raster to apply the raster function to.
        /// </summary>
        public object Raster
        {
            get
            {
                return GetDereferencedValue("Raster");
            }
            set
            {
                myProperties.SetProperty("Raster", value);
            }
        }

        /// <summary>
        /// Path to the image to blend into the raster.
        /// </summary>
        public string WatermarkImagePath
        {
            get
            {
                return Convert.ToString(GetDereferencedValue("WatermarkImagePath"));
            }
            set
            {
                myProperties.SetProperty("WatermarkImagePath", value);
            }
        }

        /// <summary>
        /// Percentage value by which to blend the watermark image with the raster
        /// </summary>
        public double BlendPercentage
        {
            get
            {
                return Convert.ToDouble(GetDereferencedValue("BlendPercentage"));
            }
            set
            {
                myProperties.SetProperty("BlendPercentage", value);
            }
        }

        public esriWatermarkLocation WatermarkLocation
        {
            get
            {
                return (esriWatermarkLocation)(Convert.ToInt16(GetDereferencedValue("WatermarkLocation")));
            }
            set
            {
                myProperties.SetProperty("WatermarkLocation", value);
            }
        }

        /// <summary>
        /// Dereference and return the value of the property whose name is given if necessary.
        /// </summary>
        /// <param name="name">Name of the property to check.</param>
        /// <returns>Dereferenced value of the property corresponding to the name given.</returns>
        public object GetDereferencedValue(string name)
        {
            object value = myProperties.GetProperty(name);
            if (value != null && value is IRasterFunctionVariable && !(value is IRasterFunctionTemplate))
            {
                IRasterFunctionVariable rFVar = (IRasterFunctionVariable)value;
                return rFVar.Value;
            }
            return value;
        }
        #endregion

        #region IRasterFunctionArguments Members
        /// <summary>
        /// A list of files associated with the raster
        /// </summary>        
        public IStringArray FileList
        {
            get 
            {
                object rasterObject = myProperties.GetProperty("Raster");
                IRasterDataset rasterDataset = null;
                if (rasterObject is IRasterDataset)
                    rasterDataset = (IRasterDataset)rasterObject;
                else if (rasterObject is IName)
                    rasterDataset = (IRasterDataset)((IName)rasterObject).Open();
                if (rasterDataset != null && rasterDataset is IRasterDatasetInfo)
                {
                    IRasterDatasetInfo rasterDatasetInfo = (IRasterDatasetInfo)rasterDataset;
                    return rasterDatasetInfo.FileList;
                }
                else
                    return null; 
            }
        }

        /// <summary>
        /// Get the value associated with the name provided.
        /// </summary>
        /// <param name="Name">Name of the property</param>
        /// <returns>Value of the property name provided</returns>
        public object GetValue(string Name)
        {
            return myProperties.GetProperty(Name);
        }

        /// <summary>
        /// A list of all the names in the property set.
        /// </summary>
        public IStringArray Names
        {
            get // Generate a list of names in the propertyset.
            {
                object names = null, values = null;
                myProperties.GetAllProperties(out names, out values);
                IStringArray myNames = new StrArray();
                string[] nameArray = (string[])names;
                for (int i = 0; i < nameArray.GetLength(0);++i)
                    myNames.Add(nameArray[i]);
                return myNames; 
            }
        }

        /// <summary>
        /// Set the given property name to the given value
        /// </summary>
        /// <param name="Name">Name of the property</param>
        /// <param name="Value">Value of the property</param>
        public void PutValue(string Name, object Value)
        {
            myProperties.SetProperty(Name, Value);
        }

        /// <summary>
        /// Remove the value of the property name provided
        /// </summary>
        /// <param name="Name">Name of the property to be removed</param>
        public void Remove(string Name)
        {
            myProperties.RemoveProperty(Name);
        }

        /// <summary>
        /// Clear the property set of all names and values.
        /// </summary>
        public void RemoveAll()
        {
            myProperties = null;
            myProperties = new PropertySetClass();
        }

        /// <summary>
        /// A list of all the values in the property set
        /// </summary>
        public IVariantArray Values
        {
            get // Generate a list of values in the propertyset.
            {
                object names = null, values = null;
                myProperties.GetAllProperties(out names, out values);
                IVariantArray myValues = new VarArray();
                object[] valArray = (object[])values;
                for (int i = 0; i < valArray.GetLength(0); ++i)
                    myValues.Add(valArray[i]);
                return myValues;
            }
        }

        /// <summary>
        /// Resolve variables containing field names with the corresponding values.
        /// </summary>
        /// <param name="pRow">The row corresponding to the function raster dataset.</param>
        /// <param name="pPropertySet">Property Set to add the list of the names and the resolved values to.</param>
        public void Resolve(IRow pRow, IPropertySet pPropertySet)
        {
            try
            {
                ResolveRasterVal(pRow);
                ResolveBlendPVal(pRow);
                ResolveWatermarkPathVal(pRow);
            }
            catch (Exception exc)
            {
                System.Exception myExc = new System.Exception(
                    "Exception caught in Resolve: " + exc.Message, exc);
                throw myExc;
            }
        }

        /// <summary>
        /// Update the variables containing field names to their updated values.
        /// </summary>
        /// <param name="pRow">The row corresponding to the function raster dataset.</param>
        /// <param name="pPropertySet">Property Set to add the list of the names and the updated values to.</param>
        /// <param name="pTemplateArguments">The arguments object containing the properties to update if</param>
        public void Update(IRow pRow, IPropertySet pPropertySet, IRasterFunctionArguments pTemplateArguments)
        {
            Resolve(pRow, pPropertySet);
        }

        /// <summary>
        /// Resolve the 'Raster' variable if it contains field names with the corresponding values.
        /// </summary>
        /// <param name="pRow">The row corresponding to the function raster dataset.</param>
        private void ResolveRasterVal(IRow pRow)
        {
            try
            {
                // Get the Raster property.
                object myRasterObject = myProperties.GetProperty("Raster");
                // Check to see if it is a variable
                if (myRasterObject is IRasterFunctionVariable)
                {
                    IRasterFunctionVariable rasterVar = ((IRasterFunctionVariable)myRasterObject);
                    object rasterVal = FindPropertyInRow(rasterVar, pRow);
                    if (rasterVal != null && rasterVal is string)
                    {
                        // Open the Raster Dataset from the path provided.
                        string datasetPath = (string)rasterVal;
                        IRasterDataset rasterDataset = null;
                        rasterVar.Value = rasterDataset;
                    }
                }
            }
            catch (Exception exc)
            {
                System.Exception myExc = new System.Exception(
                    "Exception caught in ResolveRasterVal: " + exc.Message, exc);
                throw myExc;
            }
        }

        /// <summary>
        /// Open the Raster Dataset given the path to the file.
        /// </summary>
        /// <param name="path">Path to the Raster Dataset file.</param>
        /// <returns>The opened Raster Dataset.</returns>
        private IRasterDataset OpenRasterDataset(string path)
        {
            try
            {
                string inputWorkspace = System.IO.Path.GetDirectoryName(path);
                string inputDatasetName = System.IO.Path.GetFileName(path);
                Type factoryType = Type.GetTypeFromProgID("esriDataSourcesRaster.RasterWorkspaceFactory");
                IWorkspaceFactory workspaceFactory = (IWorkspaceFactory)Activator.CreateInstance(factoryType);
                IWorkspace workspace = workspaceFactory.OpenFromFile(inputWorkspace, 0);
                IRasterWorkspace rasterWorkspace = (IRasterWorkspace)workspace;
                IRasterDataset myRasterDataset = rasterWorkspace.OpenRasterDataset(inputDatasetName);
                return myRasterDataset;
            }
            catch (Exception exc)
            {
                throw exc;
            }
        }

        /// <summary>
        /// Resolve the 'BlendPercentage' variable if it contains field names with the corresponding values.
        /// </summary>
        /// <param name="pRow">The row corresponding to the function raster dataset.</param>
        private void ResolveBlendPVal(IRow pRow)
        {
            // Get the BlendPercentage property.
            object myRasterObject = myProperties.GetProperty("BlendPercentage");
            // Check to see if it is a variable
            if (myRasterObject is IRasterFunctionVariable)
            {
                IRasterFunctionVariable bpVar = ((IRasterFunctionVariable)myRasterObject);
                object rasterVal = FindPropertyInRow(bpVar, pRow);
                if (rasterVal != null && rasterVal is string)
                {
                    // Get the blend percentage value from string
                    try { bpVar.Value = Convert.ToDouble((string)rasterVal); }
                    catch (Exception) { }
                }
            }
        }

        /// <summary>
        /// Resolve the 'WatermarkImagePath' variable if it contains field names with the corresponding values.
        /// </summary>
        /// <param name="pRow">The row corresponding to the function raster dataset.</param>
        private void ResolveWatermarkPathVal(IRow pRow)
        {
            // Get the WatermarkImagePath property.
            object myRasterObject = myProperties.GetProperty("WatermarkImagePath");
            // Check to see if it is a variable
            if (myRasterObject is IRasterFunctionVariable)
            {
                IRasterFunctionVariable wipVar = ((IRasterFunctionVariable)myRasterObject);
                object rasterVal = FindPropertyInRow(wipVar, pRow);
                if (rasterVal != null && rasterVal is string)
                {
                    // Get the blend percentage value from string
                    wipVar.Value = (string)rasterVal;
                }
            }
        }

        /// <summary>
        /// Check the Name and Alias properties of the given Raster Function Variable to see
        /// if they contain a reference to a field and get the value of the corresponding field if needed.
        /// </summary>
        /// <param name="rasterFunctionVar">The Raster Function Variable to check.</param>
        /// <param name="pRow">The row corresponding to the function raster dataset.</param>
        /// <returns></returns>
        private object FindPropertyInRow(IRasterFunctionVariable rasterFunctionVar, IRow pRow)
        {
            string varName = "";
            IStringArray varNames = new StrArrayClass();
            varName = rasterFunctionVar.Name;
            // If the name of  the variable contains '@Field'
            if (varName.Contains("@Field."))
                varNames.Add(varName); // Add it to the list of names.
            // Check the aliases of the variable
            for (int i = 0; i < rasterFunctionVar.Aliases.Count; ++i)
            {
                // Check the list of aliases for the '@Field' string
                varName = rasterFunctionVar.Aliases.get_Element(i);
                if (varName.Contains("@Field."))
                    varNames.Add(varName); // and add any that are found to the list of names.
            }

            // Use the list of names and find the value by looking up the appropriate field.
            for (int i = 0; i < varNames.Count; ++i)
            {
                // Get the variable name containing the field string
                varName = varNames.get_Element(i);
                // Replace the '@Field' with nothing to get just the name of the field.
                string fieldName = varName.Replace("@Field.", "");
                IFields rowFields = pRow.Fields;
                // Look up the index of the field name in the row.
                int fieldIndex = rowFields.FindField(fieldName);
                // If it is a valid index and the field type is string, return the value.
                if (fieldIndex != -1 && 
                   ((rowFields.get_Field(fieldIndex)).Type == esriFieldType.esriFieldTypeString))
                    return pRow.get_Value(fieldIndex);
            }
            // If no value has been returned yet, return null.
            return null;
        }
        #endregion

        #region IPersistVariant Members
        /// <summary>
        /// UID to identify the object.
        /// </summary>
        public UID ID
        {
            get
            {
                return myUID;
            }
        }

        /// <summary>
        /// Load the properties of the argument object from the stream provided
        /// </summary>
        /// <param name="Stream">Stream that contains the serialized form of the argument object</param>
        public void Load(IVariantStream Stream)
        {
            if (Stream is IDocumentVersion)
            {
                IDocumentVersion docVersion = (IDocumentVersion)Stream;
                if (docVersion.DocumentVersion >= esriArcGISVersion.esriArcGISVersion10)
                {
                    esriArcGISVersion streamVersion = (esriArcGISVersion)((int)Stream.Read());
                    if (streamVersion >= esriArcGISVersion.esriArcGISVersion10)
                    {
                        myName = (string)Stream.Read();
                        myDescription = (string)Stream.Read();
                        myProperties = (IPropertySet)Stream.Read();
                    }
                }
            }
        }

        /// <summary>
        /// Save the properties of the argument object to the stream provided
        /// </summary>
        /// <param name="Stream">Stream to which to serialize the argument object into</param>
        public void Save(IVariantStream Stream)
        {
            if (Stream is IDocumentVersion)
            {
                IDocumentVersion docVersion = (IDocumentVersion)Stream;
                if (docVersion.DocumentVersion >= esriArcGISVersion.esriArcGISVersion10)
                {
                    object names = null, values = null;
                    myProperties.GetAllProperties(out names, out values);
                    string[] nameArray = (string[])names;
                    object[] valArray = (object[])values;
                    for (int i = 0; i < nameArray.GetLength(0); ++i)
                    {
                        if (valArray[i] is IDataset)
                        {
                            IName myDatasetName = ((IDataset)valArray[i]).FullName;
                            myProperties.SetProperty(nameArray[i], myDatasetName);
                        }
                    }
                    Stream.Write((int)esriArcGISVersion.esriArcGISVersion10);
                    Stream.Write(myName);
                    Stream.Write(myDescription);
                    Stream.Write(myProperties);
                }
            }
        }
        #endregion

        #region IDocumentVersionSupportGEN Members
        /// <summary>
        /// Convert the instance into an object supported by the given version
        /// </summary>
        /// <param name="docVersion">Version to convert to</param>
        /// <returns>Object that supports given version</returns>
        public object ConvertToSupportedObject(esriArcGISVersion docVersion)
        {
            return null;
        }

        /// <summary>
        /// Check if the object is supported at the given version
        /// </summary>
        /// <param name="docVersion">Version to check against</param>
        /// <returns>True if the object is supported</returns>
        public bool IsSupportedAtVersion(esriArcGISVersion docVersion)
        {
            if (docVersion >= esriArcGISVersion.esriArcGISVersion10)
                return true;
            else
                return false;
        }
        #endregion

        #region IXMLSerialize Members
        /// <summary>
        /// Deserialize the argument object from the stream provided
        /// </summary>
        /// <param name="data">Xml stream to deserialize the argument object from</param>
        public void Deserialize(IXMLSerializeData data)
        {
            int nameIndex = data.Find("Names");
            int valIndex = data.Find("Values");
            if (nameIndex != -1 && valIndex != -1)
            {
                IStringArray myNames = (IStringArray)data.GetVariant(nameIndex);
                IVariantArray myValues = (IVariantArray)data.GetVariant(valIndex);
                for (int i = 0; i < myNames.Count; ++i)
                {
                    myProperties.SetProperty(myNames.get_Element(i), 
                        myValues.get_Element(i));
                }
            }
        }

        /// <summary>
        /// Serialize the argument object into the stream provided.
        /// </summary>
        /// <param name="data">Xml stream to serialize the argument object into</param>
        public void Serialize(IXMLSerializeData data)
        {
            #region Prepare PropertySet
            object names = null, values = null;
            myProperties.GetAllProperties(out names, out values);
            IStringArray myNames = new StrArray();
            string[] nameArray = (string[])names;
            IVariantArray myValues = new VarArray();
            object[] valArray = (object[])values;
            for (int i = 0; i < nameArray.GetLength(0); ++i)
            {
                myNames.Add(nameArray[i]);
                if (valArray[i] is IDataset)
                {
                    IName myDatasetName = ((IDataset)valArray[i]).FullName;
                    myValues.Add(myDatasetName);
                }
                else
                    myValues.Add(valArray[i]);
            }
            #endregion
            data.TypeName = "WatermarkFunctionArguments";
            data.TypeNamespaceURI = @"http://www.esri.com/schemas/ArcGIS/10.1";
            data.AddObject("Names", myNames);
            data.AddObject("Values", myValues);
        }
        #endregion    
    
        #region IXMLVersionSupport Members
        /// <summary>
        /// Returns the namespaces supported by the object
        /// </summary>
        public string MinNamespaceSupported
        {
            get { return @"http://www.esri.com/schemas/ArcGIS/10.1"; }
        }
        #endregion
    }
}

[Visual Basic .NET]

WatermarkFunction.vb

Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports System.IO
Imports System.Drawing
Imports System.Drawing.Imaging
Imports System.Runtime.InteropServices
Imports ESRI.ArcGIS.DataSourcesGDB
Imports ESRI.ArcGIS.DataSourcesRaster
Imports ESRI.ArcGIS.esriSystem
Imports ESRI.ArcGIS.Geodatabase
Imports ESRI.ArcGIS.Geometry
Imports ESRI.ArcGIS.ADF.CATIDs

Namespace CustomFunction
    ''' <summary>
    ''' Enumerator for the location of the watermark.
    ''' </summary>
    <Guid("148F193A-0D46-4d4c-BC9C-A05AC4BE0BAB")> _
    <ComVisible(True)> _
    Public Enum esriWatermarkLocation
        esriWatermarkTopLeft = 0
        esriWatermarkTopRight
        esriWatermarkCenter
        esriWatermarkBottomLeft
        esriWatermarkBottomRight
    End Enum

    <Guid("168721E7-7010-4a36-B886-F644437B164D")> _
    <ClassInterface(ClassInterfaceType.None)> _
    <ProgId("CustomFunction.WatermarkFunction")> _
    <ComVisible(True)> _
    Public Class WatermarkFunction
        Implements IRasterFunction
        Implements IPersistVariant
        Implements IDocumentVersionSupportGEN
        Implements IXMLSerialize
        Implements IXMLVersionSupport
#Region "Private Members"
        Private myUID As UID
        ' UID for the Watermark Function.
        Private myRasterInfo As IRasterInfo
        ' Raster Info for the Watermark Function
        Private myPixeltype As rstPixelType
        ' Pixel Type of the Watermark Function.
        Private myName As String
        ' Name of the Watermark Function.
        Private myDescription As String
        ' Description of the Watermark Function.
        Private myValidFlag As Boolean
        ' Flag to specify validity of the Watermark Function.
        Private myWatermarkImagePath As String
        ' Path to the Watermark Image.
        Private myBlendPercentage As Double
        ' Percentage of the blending.
        Private blendValue As Double
        ' Actual value of the blend percentage.
        Private myWatermarkLocation As esriWatermarkLocation
        ' Location of the Watermark.
        Private myFunctionHelper As IRasterFunctionHelper
        ' Raster Function Helper object.
        Private myWatermarkImage As Bitmap
        ' Watermark Image object.
#End Region
        Public Sub New()
            myWatermarkImagePath = ""
            myBlendPercentage = 50.0
            ' Default value for the blending percentage.
            blendValue = 0.5
            ' Default value for the blend value.
            myWatermarkLocation = esriWatermarkLocation.esriWatermarkBottomRight

            myName = "WatermarkFunction"
            myPixeltype = rstPixelType.PT_UNKNOWN
            ' Default value for the pixel type.
            myDescription = "Add a watermark to the request."
            myValidFlag = True

            myFunctionHelper = New RasterFunctionHelperClass()

            myWatermarkImage = Nothing

            myUID = New UIDClass()
            myUID.Value = "{" & "168721E7-7010-4a36-B886-F644437B164D" & "}"
        End Sub

#Region "IRasterFunction Members"
        ''' <summary>
        ''' Name of the Raster Function.
        ''' </summary>
        Public Property Name() As String Implements IRasterFunction.Name
            Get
                Return myName
            End Get
            Set(ByVal value As String)
                myName = value
            End Set
        End Property

        ''' <summary>
        ''' Pixel Type of the Raster Function
        ''' </summary>
        Public Property PixelType() As rstPixelType Implements IRasterFunction.PixelType
            Get
                Return myPixeltype
            End Get
            Set(ByVal value As rstPixelType)
                myPixeltype = value
            End Set
        End Property

        ''' <summary>
        ''' Output Raster Info for the Raster Function
        ''' </summary>
        Public ReadOnly Property RasterInfo() As IRasterInfo Implements IRasterFunction.RasterInfo
            Get
                Return myRasterInfo
            End Get
        End Property

        ''' <summary>
        ''' Description of the Raster Function
        ''' </summary>
        Public Property Description() As String Implements IRasterFunction.Description
            Get
                Return myDescription
            End Get
            Set(ByVal value As String)
                myDescription = value
            End Set
        End Property

        ''' <summary>
        ''' Initialize the Raster function using the argument object. This is one of the two
        ''' main functions to implement for a custom Raster function. The raster object is 
        ''' dereferenced if required and given to the RasterFuntionHelper object to bind.
        ''' </summary>
        ''' <param name="pArguments">Arguments object used for initialization</param>
        Public Sub Bind(ByVal pArguments As Object) Implements IRasterFunction.Bind
            Try
                ' Check if the Arguments object is of the correct type.
                Dim watermarkFuncArgs As IWatermarkFunctionArguments = Nothing
                If TypeOf pArguments Is IWatermarkFunctionArguments Then
                    watermarkFuncArgs = DirectCast(pArguments, IWatermarkFunctionArguments)
                    myBlendPercentage = watermarkFuncArgs.BlendPercentage
                    myWatermarkImagePath = watermarkFuncArgs.WatermarkImagePath
                    myWatermarkLocation = watermarkFuncArgs.WatermarkLocation

                    Dim inputRaster As Object = watermarkFuncArgs.Raster
                    If TypeOf watermarkFuncArgs.Raster Is IRasterFunctionVariable Then
                        Dim rasterFunctionVariable As IRasterFunctionVariable = DirectCast(watermarkFuncArgs.Raster, IRasterFunctionVariable)
                        inputRaster = rasterFunctionVariable.Value
                    End If

                    ' Call the Bind method of the Raster Function Helper object.
                    myFunctionHelper.Bind(inputRaster)
                Else
                    ' Throw an error if incorrect arguments object is passed.
                    Throw New System.Exception("Incorrect arguments object. Expected: IWatermarkFunctionArguments")
                End If

                ' Get the raster info and Pixel Type from the RasterFunctionHelper object.
                myRasterInfo = myFunctionHelper.RasterInfo
                myPixeltype = myRasterInfo.PixelType

                ' Convert blending percentage to blending value.
                If myBlendPercentage >= 0.0 AndAlso myBlendPercentage <= 100.0 Then
                    blendValue = myBlendPercentage / 100.0
                Else
                    ''' A value of 50% is used as default.
                    blendValue = 0.5
                End If

                If myWatermarkImagePath <> "" Then
                    ' Load the watermark image from the path provided
                    myWatermarkImage = New Bitmap(myWatermarkImagePath)
                    ' and check the pixel type of the loaded image to see if its compatible.
                    If myWatermarkImage.PixelFormat <> System.Drawing.Imaging.PixelFormat.Format32bppArgb AndAlso myWatermarkImage.PixelFormat <> System.Drawing.Imaging.PixelFormat.Format24bppRgb Then
                        ' Throw error if the image is not compatible.
                        Throw New System.Exception("Invalid watermark image. Please provide one with 8 bits per band in ARGB or RGB format.")
                    End If

                    ' Cleanup
                    myWatermarkImage.Dispose()
                    myWatermarkImage = Nothing
                End If
            Catch exc As Exception
                '#Region "Cleanup"
                If myWatermarkImage IsNot Nothing Then
                    myWatermarkImage.Dispose()
                End If
                myWatermarkImage = Nothing
                '#End Region

                Dim myExc As New System.Exception("Exception caught in Bind method of Watermark Function. " & exc.Message, exc)
                Throw myExc
            End Try
        End Sub

        ''' <summary>
        ''' Read pixels from the input Raster and fill the PixelBlock provided with processed pixels.
        ''' The RasterFunctionHelper object is used to handle pixel type conversion and resampling.
        ''' The watermark image is then blended to the bottom right corner of the pixel block. 
        ''' </summary>
        ''' <param name="pTlc">Point to start the reading from in the Raster</param>
        ''' <param name="pRaster">Reference Raster for the PixelBlock</param>
        ''' <param name="pPixelBlock">PixelBlock to be filled in</param>
        Public Sub Read(ByVal pTlc As IPnt, ByVal pRaster As IRaster, ByVal pPixelBlock As IPixelBlock) Implements IRasterFunction.Read
            Dim wMBitmapData As BitmapData = Nothing
            Dim pixelValue As Double = 0.0
            Dim wmRow As Integer = 0
            Dim wmCol As Integer = 0
            Try
                ' Call Read method of the Raster Function Helper object.
                myFunctionHelper.Read(pTlc, Nothing, pRaster, pPixelBlock)

                Dim wMBandOffset As Integer = 0

                '#Region "Reference Raster Properties"
                ' Get the pixel type of the reference raster to see if 
                ' it is compatible (8 bit).
                Dim referenceProps As IRasterProps = DirectCast(pRaster, IRasterProps)
                If referenceProps.PixelType <> rstPixelType.PT_UCHAR AndAlso referenceProps.PixelType <> rstPixelType.PT_CHAR Then
                    Throw New System.Exception("Function can only be applied to 8bit data.")
                End If

                '#End Region

                '#Region "Load watermark image object"
                ' Create new image object for the watermark image.
                myWatermarkImage = New Bitmap(myWatermarkImagePath)
                ' Read number of bands of the watermark image.
                If myWatermarkImage.PixelFormat = System.Drawing.Imaging.PixelFormat.Format32bppArgb Then
                    wMBandOffset = 4
                Else
                    If myWatermarkImage.PixelFormat = System.Drawing.Imaging.PixelFormat.Format24bppRgb Then
                        wMBandOffset = 3
                    Else
                        Throw New System.Exception("Invalid bitmap. Please provide one with 8bits per band in ARGB or RGB format.")
                    End If
                End If
                '#End Region

                Dim pBHeight As Integer = pPixelBlock.Height
                Dim pBWidth As Integer = pPixelBlock.Width
                Dim wMHeight As Integer = myWatermarkImage.Height
                Dim wMWidth As Integer = myWatermarkImage.Width
                Dim wMRowIndex As Integer = 0
                Dim wMColIndex As Integer = 0
                Dim pBRowIndex As Integer = 0
                Dim pBColIndex As Integer = 0
                Dim endRow As Integer = 0
                Dim endCol As Integer = 0
                Dim waterStartCol As Boolean = False
                Dim waterStartRow As Boolean = False

                ' Calculate the row/column values that specify where to start the blending.
                '#Region "Calculate Indices"
                ''' If the number of rows of the pixelblock are more than the watermark image
                endRow = pBHeight
                If pBHeight >= wMHeight Then
                    ''' Set the row index to start blending in the pixelblock.
                    Select Case myWatermarkLocation
                        Case esriWatermarkLocation.esriWatermarkTopLeft
                            If True Then
                                pBRowIndex = 0
                                endRow = pBRowIndex + wMHeight
                                Exit Select
                            End If
                        Case esriWatermarkLocation.esriWatermarkTopRight
                            If True Then
                                pBRowIndex = 0
                                endRow = pBRowIndex + wMHeight
                                Exit Select
                            End If
                        Case esriWatermarkLocation.esriWatermarkCenter
                            If True Then
                                pBRowIndex = (pBHeight \ 2) - (wMHeight \ 2)
                                endRow = pBRowIndex + wMHeight
                                Exit Select
                            End If
                        Case esriWatermarkLocation.esriWatermarkBottomLeft
                            If True Then
                                pBRowIndex = pBHeight - wMHeight
                                Exit Select
                            End If
                        Case esriWatermarkLocation.esriWatermarkBottomRight
                            If True Then
                                pBRowIndex = pBHeight - wMHeight
                                Exit Select
                            End If
                        Case Else
                            Exit Select
                    End Select

                    If myWatermarkLocation = esriWatermarkLocation.esriWatermarkCenter Then
                        pBRowIndex = (pBHeight \ 2) - (wMHeight \ 2)
                        endRow = pBRowIndex + wMHeight
                    End If
                Else
                    ''' If the number of rows of the watermark image is more than that of the pixelblock.
                    ''' Set the row index to start blending in the watermark image.
                    wMRowIndex = (wMHeight - pBHeight)
                    waterStartRow = True
                End If

                ''' If the number of cols of the pixelblock are more than the watermark image
                endCol = pBWidth
                If pBWidth >= wMWidth Then
                    ''' Set the col index to start blending in the pixelblock.
                    ''' Set the row index to start blending in the pixelblock.
                    Select Case myWatermarkLocation
                        Case esriWatermarkLocation.esriWatermarkTopLeft
                            If True Then
                                pBColIndex = 0
                                endCol = pBColIndex + wMWidth
                                Exit Select
                            End If
                        Case esriWatermarkLocation.esriWatermarkTopRight
                            If True Then
                                pBColIndex = pBWidth - wMWidth
                                Exit Select
                            End If
                        Case esriWatermarkLocation.esriWatermarkCenter
                            If True Then
                                pBColIndex = (pBWidth \ 2) - (wMWidth \ 2)
                                endCol = pBColIndex + wMWidth
                                Exit Select
                            End If
                        Case esriWatermarkLocation.esriWatermarkBottomLeft
                            If True Then
                                pBColIndex = 0
                                endCol = pBColIndex + wMWidth
                                Exit Select
                            End If
                        Case esriWatermarkLocation.esriWatermarkBottomRight
                            If True Then
                                pBColIndex = pBWidth - wMWidth
                                Exit Select
                            End If
                        Case Else
                            Exit Select
                    End Select
                Else
                    ''' If the number of cols of the watermark image is more than that of the pixelblock.
                    ''' Set the col index to start blending in the watermark image.
                    wMColIndex = (wMWidth - pBWidth)
                    waterStartCol = True
                End If
                '#End Region

                '#Region "Prepare Watermark Image for reading"
                ' Get the pixels from the watermark image using the lockbits function.
                wMBitmapData = myWatermarkImage.LockBits(New Rectangle(0, 0, wMWidth, wMHeight), _
                                                         ImageLockMode.[ReadOnly], myWatermarkImage.PixelFormat)

                Dim wMScan0 As System.IntPtr = wMBitmapData.Scan0
                Dim wMStride As Integer = wMBitmapData.Stride
                '#End Region

                ' The unsafe keyword is used so that pointers can be used to access pixels
                ' from the watermark image.
                Dim wMPaddingOffset As Integer = wMStride - (myWatermarkImage.Width * wMBandOffset)

                Dim wMBufferSize As Integer = wMStride * wMHeight
                Dim wMLocalBuffer(wMBufferSize) As Byte
                Call Marshal.Copy(wMScan0, wMLocalBuffer, 0, wMBufferSize)
                Dim wMLocalBufferIndex As Integer = 0

                ' Start filling from the correct row, col in the pixelblock 
                ' using the indices calculated above
                Dim pixelValues As System.Array
                If pPixelBlock.Planes = 3 Then
                    If wMBandOffset = 4 Then
                        ' To check for transparency in WM Image
                        '#Region "3 Band PixelBlock"
                        For nBand As Integer = 0 To pPixelBlock.Planes - 1
                            'Dim wMStartByte As Pointer(Of Byte) = CType(CType(wMScan0, Pointer(Of System.Void)), Pointer(Of Byte))
                            wMLocalBufferIndex = 0

                            ''' If the number of rows of the watermark image are more than the request.
                            If waterStartRow Then
                                ''' Skip to the correct row in the watermark image.
                                wMLocalBufferIndex += (wMStride * wMRowIndex)
                            End If

                            Dim ipPixelBlock As IPixelBlock3 = DirectCast(pPixelBlock, IPixelBlock3)
                            pixelValues = DirectCast(ipPixelBlock.PixelData(nBand), System.Array)
                            Dim i As Integer = pBRowIndex
                            While i < endRow
                                ''' If the number of cols of the watermark image are more than the request.
                                If waterStartCol Then
                                    ''' Skip to the correct column in the watermark image.                                
                                    wMLocalBufferIndex += (wMColIndex * wMBandOffset)
                                End If

                                Dim k As Integer = pBColIndex
                                While k < endCol
                                    pixelValue = Convert.ToDouble(pixelValues.GetValue(k, i))
                                    If Convert.ToDouble(wMLocalBuffer(wMLocalBufferIndex + 3)) <> 0.0 AndAlso _
                                        Convert.ToByte(ipPixelBlock.GetNoDataMaskVal(nBand, k, i)) = 1 Then
                                        ' Blend the pixelValue from the PixelBlock with the corresponding
                                        ' pixel from the watermark image.
                                        pixelValue = ((1 - blendValue) * pixelValue) +
                                            (blendValue * Convert.ToDouble(wMLocalBuffer(wMLocalBufferIndex + (2 - nBand))))
                                    End If
                                    pixelValues.SetValue(Convert.ToByte(pixelValue), k, i)

                                    wMLocalBufferIndex += wMBandOffset
                                    k += 1
                                    wmCol += 1
                                End While
                                wMLocalBufferIndex += wMPaddingOffset
                                i += 1
                                wmRow += 1
                            End While
                            DirectCast(pPixelBlock, IPixelBlock3).PixelData(nBand) = pixelValues
                            '#End Region
                        Next
                    Else
                        '#Region "3 Band PixelBlock"
                        For nBand As Integer = 0 To pPixelBlock.Planes - 1
                            'Dim wMStartByte As Pointer(Of Byte) = CType(CType(wMScan0, Pointer(Of System.Void)), Pointer(Of Byte))
                            wMLocalBufferIndex = 0

                            ''' If the number of rows of the watermark image are more than the request.
                            If waterStartRow Then
                                ''' Skip to the correct row in the watermark image.
                                wMLocalBufferIndex += (wMStride * wMRowIndex)
                            End If

                            Dim ipPixelBlock As IPixelBlock3 = DirectCast(pPixelBlock, IPixelBlock3)
                            pixelValues = DirectCast(ipPixelBlock.PixelData(nBand), System.Array)
                            Dim i As Integer = pBRowIndex
                            While i < endRow
                                ''' If the number of cols of the watermark image are more than the request.
                                If waterStartCol Then
                                    ''' Skip to the correct column in the watermark image.                                
                                    wMLocalBufferIndex += (wMColIndex * wMBandOffset)
                                End If

                                Dim k As Integer = pBColIndex
                                While k < endCol
                                    pixelValue = Convert.ToDouble(pixelValues.GetValue(k, i))
                                    If Convert.ToByte(ipPixelBlock.GetNoDataMaskVal(nBand, k, i)) = 1 Then
                                        ' Blend the pixelValue from the PixelBlock with the corresponding
                                        ' pixel from the watermark image.
                                        pixelValue = ((1 - blendValue) * pixelValue) +
                                            (blendValue * Convert.ToDouble(wMLocalBuffer(wMLocalBufferIndex + (2 - nBand))))
                                    End If
                                    pixelValues.SetValue(Convert.ToByte(pixelValue), k, i)

                                    wMLocalBufferIndex += wMBandOffset
                                    k += 1
                                    wmCol += 1
                                End While
                                wMLocalBufferIndex += wMPaddingOffset
                                i += 1
                                wmRow += 1
                            End While
                            DirectCast(pPixelBlock, IPixelBlock3).PixelData(nBand) = pixelValues
                            '#End Region
                        Next
                    End If
                Else
                    If wMBandOffset = 4 Then
                        ' To check for transparency in WM Image
                        '#Region "Not 3 Band PixelBlock"
                        For nBand As Integer = 0 To pPixelBlock.Planes - 1
                            ' Dim wMStartByte As Pointer(Of Byte) = CType(CType(wMScan0, Pointer(Of System.Void)), Pointer(Of Byte))
                            wMLocalBufferIndex = 0

                            ''' If the number of rows of the watermark image are more than the request.
                            If waterStartRow Then
                                ''' Skip to the correct row in the watermark image.
                                wMLocalBufferIndex += (wMStride * wMRowIndex)
                            End If

                            Dim ipPixelBlock As IPixelBlock3 = DirectCast(pPixelBlock, IPixelBlock3)
                            pixelValues = DirectCast(ipPixelBlock.PixelData(nBand), System.Array)
                            Dim nooftimes As Integer = 0
                            Dim noofskips As Integer = 0
                            Dim i As Integer = pBRowIndex
                            While i < endRow
                                ''' If the number of cols of the watermark image are more than the request.
                                If waterStartCol Then
                                    ''' Skip to the correct column in the watermark image.                                
                                    wMLocalBufferIndex += (wMColIndex * wMBandOffset)
                                End If

                                Dim k As Integer = pBColIndex
                                While k < endCol
                                    pixelValue = Convert.ToDouble(pixelValues.GetValue(k, i))
                                    If Convert.ToByte(ipPixelBlock.GetNoDataMaskVal(nBand, k, i)) = 1 Then
                                        'Convert.ToDouble(wMStartByte[3]) != 0.0 &&
                                        ' Calculate the average value of the pixels of the watermark image
                                        Dim avgValue As Double = (Convert.ToDouble(wMLocalBuffer(wMLocalBufferIndex + 0)) + _
                                                                  Convert.ToDouble(wMLocalBuffer(wMLocalBufferIndex + 1)) + _
                                                                  Convert.ToDouble(wMLocalBuffer(wMLocalBufferIndex + 2))) / 3

                                        ' and blend it with the pixelValue from the PixelBlock.
                                        pixelValue = ((1 - blendValue) * pixelValue) + (blendValue * avgValue)
                                    End If
                                    pixelValues.SetValue(Convert.ToByte(pixelValue), k, i)

                                    nooftimes += 1
                                    noofskips += wMBandOffset
                                    wMLocalBufferIndex += wMBandOffset
                                    k += 1
                                    wmCol += 1
                                End While
                                wMLocalBufferIndex += wMPaddingOffset
                                i += 1
                                wmRow += 1
                            End While
                            DirectCast(pPixelBlock, IPixelBlock3).PixelData(nBand) = pixelValues
                            '#End Region
                        Next
                    Else
                        '#Region "Not 3 Band PixelBlock"
                        For nBand As Integer = 0 To pPixelBlock.Planes - 1
                            'Dim wMStartByte As Pointer(Of Byte) = CType(CType(wMScan0, Pointer(Of System.Void)), Pointer(Of Byte))
                            wMLocalBufferIndex = 0

                            ''' If the number of rows of the watermark image are more than the request.
                            If waterStartRow Then
                                ''' Skip to the correct row in the watermark image.
                                wMLocalBufferIndex += (wMStride * wMRowIndex)
                            End If

                            Dim ipPixelBlock As IPixelBlock3 = DirectCast(pPixelBlock, IPixelBlock3)
                            pixelValues = DirectCast(ipPixelBlock.PixelData(nBand), System.Array)
                            Dim nooftimes As Integer = 0
                            Dim noofskips As Integer = 0
                            Dim i As Integer = pBRowIndex
                            While i < endRow
                                ''' If the number of cols of the watermark image are more than the request.
                                If waterStartCol Then
                                    ''' Skip to the correct column in the watermark image.                                
                                    wMLocalBufferIndex += (wMColIndex * wMBandOffset)
                                End If

                                Dim k As Integer = pBColIndex
                                While k < endCol
                                    pixelValue = Convert.ToDouble(pixelValues.GetValue(k, i))
                                    If Convert.ToByte(ipPixelBlock.GetNoDataMaskVal(nBand, k, i)) = 1 Then
                                        ' Calculate the average value of the pixels of the watermark image
                                        Dim avgValue As Double = (Convert.ToDouble(wMLocalBuffer(wMLocalBufferIndex + 0)) + _
                                                                  Convert.ToDouble(wMLocalBuffer(wMLocalBufferIndex + 1)) + _
                                                                  Convert.ToDouble(wMLocalBuffer(wMLocalBufferIndex + 2))) / 3

                                        ' and blend it with the pixelValue from the PixelBlock.
                                        pixelValue = ((1 - blendValue) * pixelValue) + (blendValue * avgValue)
                                    End If
                                    pixelValues.SetValue(Convert.ToByte(pixelValue), k, i)

                                    nooftimes += 1
                                    noofskips += wMBandOffset
                                    wMLocalBufferIndex += wMBandOffset
                                    k += 1
                                    wmCol += 1
                                End While
                                wMLocalBufferIndex += wMPaddingOffset
                                i += 1
                                wmRow += 1
                            End While
                            DirectCast(pPixelBlock, IPixelBlock3).PixelData(nBand) = pixelValues
                            '#End Region
                        Next
                    End If
                End If

                '#Region "Cleanup"
                myWatermarkImage.UnlockBits(wMBitmapData)
                myWatermarkImage.Dispose()
                myWatermarkImage = Nothing
                wMBitmapData = Nothing
                wMScan0 = CType(Nothing, System.IntPtr)
                '#End Region
                wMStride = 0
            Catch exc As Exception
                '#Region "Cleanup"
                If wMBitmapData IsNot Nothing Then
                    myWatermarkImage.UnlockBits(wMBitmapData)
                End If
                wMBitmapData = Nothing
                If myWatermarkImage IsNot Nothing Then
                    myWatermarkImage.Dispose()
                End If
                myWatermarkImage = Nothing
                '#End Region

                Dim myExc As New System.Exception("Exception caught in Read method of Watermark Function. " & exc.Message, exc)
                Throw myExc
            End Try
        End Sub

        ''' <summary>
        ''' Update the Raster Function
        ''' </summary>
        Public Sub Update() Implements IRasterFunction.Update
            Try
            Catch exc As Exception
                Dim myExc As New System.Exception("Exception caught in Update method of Watermark Function", exc)
                Throw myExc
            End Try
        End Sub

        ''' <summary>
        ''' Property that specifies whether the Raster Function is still valid.
        ''' </summary>
        Public ReadOnly Property Valid() As Boolean Implements IRasterFunction.Valid
            Get
                Return myValidFlag
            End Get
        End Property
#End Region

#Region "IPersistVariant Members"
        ''' <summary>
        ''' UID to identify the function.
        ''' </summary>
        Public ReadOnly Property ID() As UID Implements IPersistVariant.ID
            Get
                Return myUID
            End Get
        End Property

        ''' <summary>
        ''' Load the properties of the function from the stream provided
        ''' </summary>
        ''' <param name="Stream">Stream that contains the serialized form of the function</param>
        Public Sub Load(ByVal Stream As IVariantStream) Implements IPersistVariant.Load
            If TypeOf Stream Is IDocumentVersion Then
                Dim docVersion As IDocumentVersion = DirectCast(Stream, IDocumentVersion)
                If docVersion.DocumentVersion >= esriArcGISVersion.esriArcGISVersion10 Then
                    Dim streamVersion As esriArcGISVersion = CType(CInt(Stream.Read()), esriArcGISVersion)
                    If streamVersion >= esriArcGISVersion.esriArcGISVersion10 Then
                        myName = DirectCast(Stream.Read(), String)
                        myDescription = DirectCast(Stream.Read(), String)
                        myPixeltype = CType(CInt(Stream.Read()), rstPixelType)
                    End If
                End If
            End If
        End Sub

        ''' <summary>
        ''' Save the properties of the function to the stream provided
        ''' </summary>
        ''' <param name="Stream">Stream to which to serialize the function into</param>
        Public Sub Save(ByVal Stream As IVariantStream) Implements IPersistVariant.Save
            If TypeOf Stream Is IDocumentVersion Then
                Dim docVersion As IDocumentVersion = DirectCast(Stream, IDocumentVersion)
                If docVersion.DocumentVersion >= esriArcGISVersion.esriArcGISVersion10 Then
                    Stream.Write(CInt(esriArcGISVersion.esriArcGISVersion10))
                    Stream.Write(myName)
                    Stream.Write(myDescription)
                    Stream.Write(CInt(myPixeltype))
                End If
            End If
        End Sub
#End Region

#Region "IDocumentVersionSupportGEN Members"
        ''' <summary>
        ''' Convert the instance into an object supported by the given version
        ''' </summary>
        ''' <param name="docVersion">Version to convert to</param>
        ''' <returns>Object that supports given version</returns>
        Public Function ConvertToSupportedObject(ByVal docVersion As esriArcGISVersion) As Object Implements IDocumentVersionSupportGEN.ConvertToSupportedObject
            Return Nothing
        End Function

        ''' <summary>
        ''' Check if the object is supported at the given version
        ''' </summary>
        ''' <param name="docVersion">Version to check against</param>
        ''' <returns>True if the object is supported</returns>
        Public Function IsSupportedAtVersion(ByVal docVersion As esriArcGISVersion) As Boolean Implements IDocumentVersionSupportGEN.IsSupportedAtVersion
            If docVersion >= esriArcGISVersion.esriArcGISVersion10 Then
                Return True
            Else
                Return False
            End If
        End Function

#End Region

#Region "IXMLSerialize Members"
        ''' <summary>
        ''' Deserialize the Raster Function from the datastream provided
        ''' </summary>
        ''' <param name="data">Xml stream to deserialize the function from</param>
        Public Sub Deserialize(ByVal data As IXMLSerializeData) Implements IXMLSerialize.Deserialize
            myName = data.GetString(data.Find("Name"))
            myDescription = data.GetString(data.Find("Description"))
            myPixeltype = CType(data.GetInteger(data.Find("PixelType")), rstPixelType)
        End Sub

        ''' <summary>
        ''' Serialize the Raster Function into the stream provided.
        ''' </summary>
        ''' <param name="data">Xml stream to serialize the function into</param>
        Public Sub Serialize(ByVal data As IXMLSerializeData) Implements IXMLSerialize.Serialize
            data.TypeName = "WatermarkFunction"
            data.TypeNamespaceURI = "http://www.esri.com/schemas/ArcGIS/10.1"
            data.AddString("Name", myName)
            data.AddString("Description", myDescription)
            data.AddInteger("PixelType", CInt(myPixeltype))
        End Sub
#End Region

#Region "IXMLVersionSupport Members"
        ''' <summary>
        ''' Returns the namespaces supported by the object
        ''' </summary>
        Public ReadOnly Property MinNamespaceSupported() As String Implements IXMLVersionSupport.MinNamespaceSupported
            Get
                Return "http://www.esri.com/schemas/ArcGIS/10.1"
            End Get
        End Property
#End Region

#Region "COM Registration Function(s)"
        <ComRegisterFunction()> _
        Private Shared Sub Reg(ByVal regKey As String)
            ESRI.ArcGIS.ADF.CATIDs.RasterFunctions.Register(regKey)
        End Sub

        <ComUnregisterFunction()> _
        Private Shared Sub Unreg(ByVal regKey As String)
            ESRI.ArcGIS.ADF.CATIDs.RasterFunctions.Unregister(regKey)
        End Sub
#End Region
    End Class

    <Guid("933A9DEF-D56F-4e37-911D-AC16982CA697")> _
    <InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)> _
    Public Interface IWatermarkFunctionArguments
        Inherits IRasterFunctionArguments
        Inherits IPersistVariant
        Inherits IXMLSerialize
#Region "WatermarkFunctionArguments Members"
        <DispId(&H50505001)> _
        Property Raster() As Object

        <DispId(&H50505002)> _
        Property WatermarkImagePath() As String

        <DispId(&H50505003)> _
        Property BlendPercentage() As Double

        <DispId(&H50505004)> _
        Property WatermarkLocation() As esriWatermarkLocation
#End Region
    End Interface

    <Guid("996DC8E5-086B-41b5-919A-A3B9B86F2B30")> _
    <ClassInterface(ClassInterfaceType.None)> _
    <ProgId("CustomFunction.WatermarkFunctionArguments")> _
    <ComVisible(True)> _
    Public Class WatermarkFunctionArguments
        Implements IWatermarkFunctionArguments
        Implements IPersistVariant
        Implements IDocumentVersionSupportGEN
        Implements IXMLSerialize
        Implements IXMLVersionSupport
#Region "Private Members"
        Private myName As String
        Private myDescription As String
        Private myUID As UID
        ' UID for the Watermark Function.
        Private myProperties As IPropertySet
        ' Property set to store the properties of the arguments object.
#End Region
        Public Sub New()
            myName = "WatermarkFunctionArguments"
            myDescription = "Arguments object for the Watermark Function"

            ' Set default values
            myProperties = New PropertySetClass()
            myProperties.SetProperty("Raster", Nothing)
            myProperties.SetProperty("WatermarkImagePath", "")
            myProperties.SetProperty("BlendPercentage", 50.0)
            myProperties.SetProperty("WatermarkLocation", CustomFunction.esriWatermarkLocation.esriWatermarkBottomRight)

            myUID = New UIDClass()
            myUID.Value = "{" & "996DC8E5-086B-41b5-919A-A3B9B86F2B30" & "}"
        End Sub

#Region "WatermarkFunctionArguments Members"
        ''' <summary>
        ''' Raster to apply the raster function to.
        ''' </summary>
        Public Property Raster() As Object Implements IWatermarkFunctionArguments.Raster
            Get
                Return GetDereferencedValue("Raster")
            End Get
            Set(ByVal value As Object)
                myProperties.SetProperty("Raster", value)
            End Set
        End Property

        ''' <summary>
        ''' Path to the image to blend into the raster.
        ''' </summary>
        Public Property WatermarkImagePath() As String Implements IWatermarkFunctionArguments.WatermarkImagePath
            Get
                Return Convert.ToString(GetDereferencedValue("WatermarkImagePath"))
            End Get
            Set(ByVal value As String)
                myProperties.SetProperty("WatermarkImagePath", value)
            End Set
        End Property

        ''' <summary>
        ''' Percentage value by which to blend the watermark image with the raster
        ''' </summary>
        Public Property BlendPercentage() As Double Implements IWatermarkFunctionArguments.BlendPercentage
            Get
                Return Convert.ToDouble(GetDereferencedValue("BlendPercentage"))
            End Get
            Set(ByVal value As Double)
                myProperties.SetProperty("BlendPercentage", value)
            End Set
        End Property

        Public Property WatermarkLocation() As esriWatermarkLocation Implements IWatermarkFunctionArguments.WatermarkLocation
            Get
                Return CType(Convert.ToInt16(GetDereferencedValue("WatermarkLocation")), esriWatermarkLocation)
            End Get
            Set(ByVal value As esriWatermarkLocation)
                myProperties.SetProperty("WatermarkLocation", value)
            End Set
        End Property

        ''' <summary>
        ''' Dereference and return the value of the property whose name is given if necessary.
        ''' </summary>
        ''' <param name="name">Name of the property to check.</param>
        ''' <returns>Dereferenced value of the property corresponding to the name given.</returns>
        Public Function GetDereferencedValue(ByVal name As String) As Object
            Dim value As Object = myProperties.GetProperty(name)
            If value IsNot Nothing AndAlso TypeOf value Is IRasterFunctionVariable AndAlso Not (TypeOf value Is IRasterFunctionTemplate) Then
                Dim rFVar As IRasterFunctionVariable = DirectCast(value, IRasterFunctionVariable)
                Return rFVar.Value
            End If
            Return value
        End Function
#End Region

#Region "IRasterFunctionArguments Members"
        ''' <summary>
        ''' A list of files associated with the raster
        ''' </summary>        
        Public ReadOnly Property FileList() As IStringArray Implements IRasterFunctionArguments.FileList
            Get
                Dim rasterObject As Object = myProperties.GetProperty("Raster")
                Dim rasterDataset As IRasterDataset = Nothing
                If TypeOf rasterObject Is IRasterDataset Then
                    rasterDataset = DirectCast(rasterObject, IRasterDataset)
                ElseIf TypeOf rasterObject Is IName Then
                    rasterDataset = DirectCast(DirectCast(rasterObject, IName).Open(), IRasterDataset)
                End If
                If rasterDataset IsNot Nothing AndAlso TypeOf rasterDataset Is IRasterDatasetInfo Then
                    Dim rasterDatasetInfo As IRasterDatasetInfo = DirectCast(rasterDataset, IRasterDatasetInfo)
                    Return rasterDatasetInfo.FileList
                Else
                    Return Nothing
                End If
            End Get
        End Property

        ''' <summary>
        ''' Get the value associated with the name provided.
        ''' </summary>
        ''' <param name="Name">Name of the property</param>
        ''' <returns>Value of the property name provided</returns>
        Public Function GetValue(ByVal Name As String) As Object Implements IRasterFunctionArguments.GetValue
            Return myProperties.GetProperty(Name)
        End Function

        ''' <summary>
        ''' A list of all the names in the property set.
        ''' </summary>
        Public ReadOnly Property Names() As IStringArray Implements IRasterFunctionArguments.Names
            Get
                ' Generate a list of names in the propertyset.
                Dim names__1 As Object = Nothing, values As Object = Nothing
                myProperties.GetAllProperties(names__1, values)
                Dim myNames As IStringArray = New StrArray()
                Dim nameArray As String() = DirectCast(names__1, String())
                For i As Integer = 0 To nameArray.GetLength(0) - 1
                    myNames.Add(nameArray(i))
                Next
                Return myNames
            End Get
        End Property

        ''' <summary>
        ''' Set the given property name to the given value
        ''' </summary>
        ''' <param name="Name">Name of the property</param>
        ''' <param name="Value">Value of the property</param>
        Public Sub PutValue(ByVal Name As String, ByVal Value As Object) Implements IRasterFunctionArguments.PutValue
            myProperties.SetProperty(Name, Value)
        End Sub

        ''' <summary>
        ''' Remove the value of the property name provided
        ''' </summary>
        ''' <param name="Name">Name of the property to be removed</param>
        Public Sub Remove(ByVal Name As String) Implements IRasterFunctionArguments.Remove
            myProperties.RemoveProperty(Name)
        End Sub

        ''' <summary>
        ''' Clear the property set of all names and values.
        ''' </summary>
        Public Sub RemoveAll() Implements IRasterFunctionArguments.RemoveAll
            myProperties = Nothing
            myProperties = New PropertySetClass()
        End Sub

        ''' <summary>
        ''' A list of all the values in the property set
        ''' </summary>
        Public ReadOnly Property Values() As IVariantArray Implements IRasterFunctionArguments.Values
            Get
                ' Generate a list of values in the propertyset.
                Dim names As Object = Nothing, values__1 As Object = Nothing
                myProperties.GetAllProperties(names, values__1)
                Dim myValues As IVariantArray = New VarArray()
                Dim valArray As Object() = DirectCast(values__1, Object())
                For i As Integer = 0 To valArray.GetLength(0) - 1
                    myValues.Add(valArray(i))
                Next
                Return myValues
            End Get
        End Property

        ''' <summary>
        ''' Resolve variables containing field names with the corresponding values.
        ''' </summary>
        ''' <param name="pRow">The row corresponding to the function raster dataset.</param>
        ''' <param name="pPropertySet">Property Set to add the list of the names and the resolved values to.</param>
        Public Sub Resolve(ByVal pRow As IRow, ByVal pPropertySet As IPropertySet) Implements IRasterFunctionArguments.Resolve
            Try
                ResolveRasterVal(pRow)
                ResolveBlendPVal(pRow)
                ResolveWatermarkPathVal(pRow)
            Catch exc As Exception
                Dim myExc As New System.Exception("Exception caught in Resolve: " & exc.Message, exc)
                Throw myExc
            End Try
        End Sub

        ''' <summary>
        ''' Update the variables containing field names to their updated values.
        ''' </summary>
        ''' <param name="pRow">The row corresponding to the function raster dataset.</param>
        ''' <param name="pPropertySet">Property Set to add the list of the names and the updated values to.</param>
        ''' <param name="pTemplateArguments">The arguments object containing the properties to update if</param>
        Public Sub Update(ByVal pRow As IRow, ByVal pPropertySet As IPropertySet, ByVal pTemplateArguments As IRasterFunctionArguments) Implements IRasterFunctionArguments.Update
            Resolve(pRow, pPropertySet)
        End Sub

        ''' <summary>
        ''' Resolve the 'Raster' variable if it contains field names with the corresponding values.
        ''' </summary>
        ''' <param name="pRow">The row corresponding to the function raster dataset.</param>
        Private Sub ResolveRasterVal(ByVal pRow As IRow)
            Try
                ' Get the Raster property.
                Dim myRasterObject As Object = myProperties.GetProperty("Raster")
                ' Check to see if it is a variable
                If TypeOf myRasterObject Is IRasterFunctionVariable Then
                    Dim rasterVar As IRasterFunctionVariable = DirectCast(myRasterObject, IRasterFunctionVariable)
                    Dim rasterVal As Object = FindPropertyInRow(rasterVar, pRow)
                    If rasterVal IsNot Nothing AndAlso TypeOf rasterVal Is String Then
                        ' Open the Raster Dataset from the path provided.
                        Dim datasetPath As String = DirectCast(rasterVal, String)
                        Dim rasterDataset As IRasterDataset = Nothing
                        rasterVar.Value = rasterDataset
                    End If
                End If
            Catch exc As Exception
                Dim myExc As New System.Exception("Exception caught in ResolveRasterVal: " & exc.Message, exc)
                Throw myExc
            End Try
        End Sub

        ''' <summary>
        ''' Open the Raster Dataset given the path to the file.
        ''' </summary>
        ''' <param name="path">Path to the Raster Dataset file.</param>
        ''' <returns>The opened Raster Dataset.</returns>
        Private Function OpenRasterDataset(ByVal path As String) As IRasterDataset
            Try
                Dim inputWorkspace As String = System.IO.Path.GetDirectoryName(path)
                Dim inputDatasetName As String = System.IO.Path.GetFileName(path)
                Dim factoryType As Type = Type.GetTypeFromProgID("esriDataSourcesRaster.RasterWorkspaceFactory")
                Dim workspaceFactory As IWorkspaceFactory = DirectCast(Activator.CreateInstance(factoryType), IWorkspaceFactory)
                Dim workspace As IWorkspace = workspaceFactory.OpenFromFile(inputWorkspace, 0)
                Dim rasterWorkspace As IRasterWorkspace = DirectCast(workspace, IRasterWorkspace)
                Dim myRasterDataset As IRasterDataset = rasterWorkspace.OpenRasterDataset(inputDatasetName)
                Return myRasterDataset
            Catch exc As Exception
                Throw exc
            End Try
        End Function

        ''' <summary>
        ''' Resolve the 'BlendPercentage' variable if it contains field names with the corresponding values.
        ''' </summary>
        ''' <param name="pRow">The row corresponding to the function raster dataset.</param>
        Private Sub ResolveBlendPVal(ByVal pRow As IRow)
            ' Get the BlendPercentage property.
            Dim myRasterObject As Object = myProperties.GetProperty("BlendPercentage")
            ' Check to see if it is a variable
            If TypeOf myRasterObject Is IRasterFunctionVariable Then
                Dim bpVar As IRasterFunctionVariable = DirectCast(myRasterObject, IRasterFunctionVariable)
                Dim rasterVal As Object = FindPropertyInRow(bpVar, pRow)
                If rasterVal IsNot Nothing AndAlso TypeOf rasterVal Is String Then
                    ' Get the blend percentage value from string
                    Try
                        bpVar.Value = Convert.ToDouble(DirectCast(rasterVal, String))
                    Catch generatedExceptionName As Exception
                    End Try
                End If
            End If
        End Sub

        ''' <summary>
        ''' Resolve the 'WatermarkImagePath' variable if it contains field names with the corresponding values.
        ''' </summary>
        ''' <param name="pRow">The row corresponding to the function raster dataset.</param>
        Private Sub ResolveWatermarkPathVal(ByVal pRow As IRow)
            ' Get the WatermarkImagePath property.
            Dim myRasterObject As Object = myProperties.GetProperty("WatermarkImagePath")
            ' Check to see if it is a variable
            If TypeOf myRasterObject Is IRasterFunctionVariable Then
                Dim wipVar As IRasterFunctionVariable = DirectCast(myRasterObject, IRasterFunctionVariable)
                Dim rasterVal As Object = FindPropertyInRow(wipVar, pRow)
                If rasterVal IsNot Nothing AndAlso TypeOf rasterVal Is String Then
                    ' Get the blend percentage value from string
                    wipVar.Value = DirectCast(rasterVal, String)
                End If
            End If
        End Sub

        ''' <summary>
        ''' Check the Name and Alias properties of the given Raster Function Variable to see
        ''' if they contain a reference to a field and get the value of the corresponding field if needed.
        ''' </summary>
        ''' <param name="rasterFunctionVar">The Raster Function Variable to check.</param>
        ''' <param name="pRow">The row corresponding to the function raster dataset.</param>
        ''' <returns></returns>
        Private Function FindPropertyInRow(ByVal rasterFunctionVar As IRasterFunctionVariable, ByVal pRow As IRow) As Object
            Dim varName As String = ""
            Dim varNames As IStringArray = New StrArrayClass()
            varName = rasterFunctionVar.Name
            ' If the name of  the variable contains '@Field'
            If varName.Contains("@Field.") Then
                varNames.Add(varName)
            End If
            ' Add it to the list of names.
            ' Check the aliases of the variable
            For i As Integer = 0 To rasterFunctionVar.Aliases.Count - 1
                ' Check the list of aliases for the '@Field' string
                varName = rasterFunctionVar.Aliases.Element(i)
                If varName.Contains("@Field.") Then
                    varNames.Add(varName)
                    ' and add any that are found to the list of names.
                End If
            Next

            ' Use the list of names and find the value by looking up the appropriate field.
            For i As Integer = 0 To varNames.Count - 1
                ' Get the variable name containing the field string
                varName = varNames.Element(i)
                ' Replace the '@Field' with nothing to get just the name of the field.
                Dim fieldName As String = varName.Replace("@Field.", "")
                Dim rowFields As IFields = pRow.Fields
                ' Look up the index of the field name in the row.
                Dim fieldIndex As Integer = rowFields.FindField(fieldName)
                ' If it is a valid index and the field type is string, return the value.
                If fieldIndex <> -1 AndAlso ((rowFields.Field(fieldIndex)).Type = esriFieldType.esriFieldTypeString) Then
                    Return pRow.Value(fieldIndex)
                End If
            Next
            ' If no value has been returned yet, return null.
            Return Nothing
        End Function
#End Region

#Region "IPersistVariant Members"
        ''' <summary>
        ''' UID to identify the object.
        ''' </summary>
        Public ReadOnly Property ID() As UID Implements IPersistVariant.ID
            Get
                Return myUID
            End Get
        End Property

        ''' <summary>
        ''' Load the properties of the argument object from the stream provided
        ''' </summary>
        ''' <param name="Stream">Stream that contains the serialized form of the argument object</param>
        Public Sub Load(ByVal Stream As IVariantStream) Implements IPersistVariant.Load
            If TypeOf Stream Is IDocumentVersion Then
                Dim docVersion As IDocumentVersion = DirectCast(Stream, IDocumentVersion)
                If docVersion.DocumentVersion >= esriArcGISVersion.esriArcGISVersion10 Then
                    Dim streamVersion As esriArcGISVersion = CType(CInt(Stream.Read()), esriArcGISVersion)
                    If streamVersion >= esriArcGISVersion.esriArcGISVersion10 Then
                        myName = DirectCast(Stream.Read(), String)
                        myDescription = DirectCast(Stream.Read(), String)
                        myProperties = DirectCast(Stream.Read(), IPropertySet)
                    End If
                End If
            End If
        End Sub

        ''' <summary>
        ''' Save the properties of the argument object to the stream provided
        ''' </summary>
        ''' <param name="Stream">Stream to which to serialize the argument object into</param>
        Public Sub Save(ByVal Stream As IVariantStream) Implements IPersistVariant.Save
            If TypeOf Stream Is IDocumentVersion Then
                Dim docVersion As IDocumentVersion = DirectCast(Stream, IDocumentVersion)
                If docVersion.DocumentVersion >= esriArcGISVersion.esriArcGISVersion10 Then
                    Dim names As Object = Nothing, values As Object = Nothing
                    myProperties.GetAllProperties(names, values)
                    Dim nameArray As String() = DirectCast(names, String())
                    Dim valArray As Object() = DirectCast(values, Object())
                    For i As Integer = 0 To nameArray.GetLength(0) - 1
                        If TypeOf valArray(i) Is IDataset Then
                            Dim myDatasetName As IName = DirectCast(valArray(i), IDataset).FullName
                            myProperties.SetProperty(nameArray(i), myDatasetName)
                        End If
                    Next
                    Stream.Write(CInt(esriArcGISVersion.esriArcGISVersion10))
                    Stream.Write(myName)
                    Stream.Write(myDescription)
                    Stream.Write(myProperties)
                End If
            End If
        End Sub
#End Region

#Region "IDocumentVersionSupportGEN Members"
        ''' <summary>
        ''' Convert the instance into an object supported by the given version
        ''' </summary>
        ''' <param name="docVersion">Version to convert to</param>
        ''' <returns>Object that supports given version</returns>
        Public Function ConvertToSupportedObject(ByVal docVersion As esriArcGISVersion) As Object Implements IDocumentVersionSupportGEN.ConvertToSupportedObject
            Return Nothing
        End Function

        ''' <summary>
        ''' Check if the object is supported at the given version
        ''' </summary>
        ''' <param name="docVersion">Version to check against</param>
        ''' <returns>True if the object is supported</returns>
        Public Function IsSupportedAtVersion(ByVal docVersion As esriArcGISVersion) As Boolean Implements IDocumentVersionSupportGEN.IsSupportedAtVersion
            If docVersion >= esriArcGISVersion.esriArcGISVersion10 Then
                Return True
            Else
                Return False
            End If
        End Function
#End Region

#Region "IXMLSerialize Members"
        ''' <summary>
        ''' Deserialize the argument object from the stream provided
        ''' </summary>
        ''' <param name="data">Xml stream to deserialize the argument object from</param>
        Public Sub Deserialize(ByVal data As IXMLSerializeData) Implements IXMLSerialize.Deserialize
            Dim nameIndex As Integer = data.Find("Names")
            Dim valIndex As Integer = data.Find("Values")
            If nameIndex <> -1 AndAlso valIndex <> -1 Then
                Dim myNames As IStringArray = DirectCast(data.GetVariant(nameIndex), IStringArray)
                Dim myValues As IVariantArray = DirectCast(data.GetVariant(valIndex), IVariantArray)
                For i As Integer = 0 To myNames.Count - 1
                    myProperties.SetProperty(myNames.Element(i), myValues.Element(i))
                Next
            End If
        End Sub

        ''' <summary>
        ''' Serialize the argument object into the stream provided.
        ''' </summary>
        ''' <param name="data">Xml stream to serialize the argument object into</param>
        Public Sub Serialize(ByVal data As IXMLSerializeData) Implements IXMLSerialize.Serialize
            '#Region "Prepare PropertySet"
            Dim names As Object = Nothing, values As Object = Nothing
            myProperties.GetAllProperties(names, values)
            Dim myNames As IStringArray = New StrArray()
            Dim nameArray As String() = DirectCast(names, String())
            Dim myValues As IVariantArray = New VarArray()
            Dim valArray As Object() = DirectCast(values, Object())
            For i As Integer = 0 To nameArray.GetLength(0) - 1
                myNames.Add(nameArray(i))
                If TypeOf valArray(i) Is IDataset Then
                    Dim myDatasetName As IName = DirectCast(valArray(i), IDataset).FullName
                    myValues.Add(myDatasetName)
                Else
                    myValues.Add(valArray(i))
                End If
            Next
            '#End Region
            data.TypeName = "WatermarkFunctionArguments"
            data.TypeNamespaceURI = "http://www.esri.com/schemas/ArcGIS/10.1"
            data.AddObject("Names", myNames)
            data.AddObject("Values", myValues)
        End Sub
#End Region

#Region "IXMLVersionSupport Members"
        ''' <summary>
        ''' Returns the namespaces supported by the object
        ''' </summary>
        Public ReadOnly Property MinNamespaceSupported() As String Implements IXMLVersionSupport.MinNamespaceSupported
            Get
                Return "http://www.esri.com/schemas/ArcGIS/10.1"
            End Get
        End Property
#End Region
    End Class
End Namespace