'*****************************************************************************
'
' File name: Mfunc.vb
' Location: See Matrox Example Launcher in the MIL Control Center
' 
'
' Synopsis:  This example shows the use of the MIL Function Developer Tool Kit and how 
'            MIL and custom code can be mixed to create a custom MIL function that 
'            accesses the data pointer of a MIL buffer directly in order to process it.
'
'            The example creates a Master MIL function that registers all the parameters 
'            to MIL and calls the Slave function. The Slave function retrieves all the 
'            parameters, gets the pointers to the MIL image buffers, uses them to access 
'            the data directly and adds a constant.
'             
'            Note: The images must be 8-bit unsigned and have a valid host address.
'
' Copyright (C) Matrox Electronic Systems Ltd., 1992-2020.
' All Rights Reserved
'*****************************************************************************
Imports Microsoft.VisualBasic
Imports System
Imports System.Collections.Generic
Imports System.Runtime.InteropServices
Imports System.Text

Imports Matrox.MatroxImagingLibrary

Namespace MFunc
    Friend Class Program
        ' MIL function specifications.
        Private Const FUNCTION_NB_PARAM As Integer = 4
        Private Const FUNCTION_OPCODE_ADD_CONSTANT As Integer = 1
        Private Const FUNCTION_PARAMETER_ERROR_CODE As Integer = 1
        Private Const FUNCTION_SUPPORTED_IMAGE_TYPE As Integer = (8 + MIL.M_UNSIGNED)

        ' Target image file name.
        Private Const IMAGE_FILE As String = MIL.M_IMAGE_PATH & "BoltsNutsWashers.mim"

        ' Master MIL Function definition.
        ' -------------------------------

        Private Shared Function AddConstant(ByVal SrcImageId As MIL_ID, ByVal DstImageId As MIL_ID, ByVal ConstantToAdd As MIL_INT) As MIL_INT
            Dim Func As MIL_ID = MIL.M_NULL
            Dim SlaveReturnValue As MIL_INT = 0

            ' Allocate a MIL function context that will be used to call a target 
            ' Slave function locally on the Host to do the processing.
            Dim SlaveAddConstantDelegate As New MFUNCFCTPTR(AddressOf SlaveAddConstant)
            MIL.MfuncAlloc("AddConstant", _
                           FUNCTION_NB_PARAM, _
                           SlaveAddConstantDelegate, MIL.M_NULL, MIL.M_NULL, _
                           MIL.M_USER_MODULE_1 + FUNCTION_OPCODE_ADD_CONSTANT, _
                           MIL.M_LOCAL + MIL.M_SYNCHRONOUS_FUNCTION, _
                           Func)

            ' Register the parameters.
            MIL.MfuncParamMilId(Func, 1, SrcImageId, MIL.M_IMAGE, MIL.M_IN)
            MIL.MfuncParamMilId(Func, 2, DstImageId, MIL.M_IMAGE, MIL.M_OUT)
            MIL.MfuncParamMilInt(Func, 3, ConstantToAdd)

            ' To pass a pointer to MIL, we need to use a reference type such as an array
            ' to be able to pin the object and prevent the garbage collector from moving the object.
            Dim slaveReturnValueArray As MIL_INT() = New MIL_INT() {-1}
            Dim slaveReturnValueArrayHandle As GCHandle = GCHandle.Alloc(slaveReturnValueArray, GCHandleType.Pinned)

            Try

                ' Get the address of the pinned object, for an array, the address is pointing to the first element.
                Dim slaveReturnValuePtr As IntPtr = slaveReturnValueArrayHandle.AddrOfPinnedObject()
                MIL.MfuncParamDataPointer(Func, 4, slaveReturnValuePtr, MIL_INT.Size * 2, MIL.M_OUT)

                ' Call the target Slave function.
                MIL.MfuncCall(Func)

                SlaveReturnValue = slaveReturnValueArray(0)

                ' Make sure that the delegate survives garbage collection until the slave function is executed.
                GC.KeepAlive(SlaveAddConstantDelegate)
            Finally
                ' Free the allocated GCHandle to allow the array object to be garbage collected.
                If (slaveReturnValueArrayHandle.IsAllocated) Then
                    slaveReturnValueArrayHandle.Free()
                End If
            End Try

            ' Free the MIL function context.
            MIL.MfuncFree(Func)

            Return SlaveReturnValue
        End Function

        ' MIL Slave function definition.
        ' ------------------------------

        Private Shared Sub SlaveAddConstant(ByVal Func As MIL_ID)
            Dim SrcImageId As MIL_ID = MIL.M_NULL
            Dim DstImageId As MIL_ID = MIL.M_NULL
            Dim ConstantToAdd As MIL_INT = 0
            Dim SrcImageDataPtr As MIL_INT = MIL.M_NULL
            Dim DstImageDataPtr As MIL_INT = MIL.M_NULL
            Dim SrcImageSizeX As MIL_INT = 0
            Dim SrcImageSizeY As MIL_INT = 0
            Dim SrcImageType As MIL_INT = 0
            Dim SrcImagePitchByte As MIL_INT = 0
            Dim DstImageSizeX As MIL_INT = 0
            Dim DstImageSizeY As MIL_INT = 0
            Dim DstImageType As MIL_INT = 0
            Dim DstImagePitchByte As MIL_INT = 0
            Dim x As Integer = 0
            Dim y As Integer = 0
            Dim TempValue As Integer = 0
            Dim SlaveReturnValuePtr As IntPtr = IntPtr.Zero

            ' Read the parameters.
            MIL.MfuncParamValue(Func, 1, SrcImageId)
            MIL.MfuncParamValue(Func, 2, DstImageId)
            MIL.MfuncParamValue(Func, 3, ConstantToAdd)
            MIL.MfuncParamValue(Func, 4, SlaveReturnValuePtr)

            ' Lock buffers for direct access.
            MIL.MbufControl(SrcImageId, MIL.M_LOCK, MIL.M_DEFAULT)
            MIL.MbufControl(DstImageId, MIL.M_LOCK, MIL.M_DEFAULT)

            ' Read image information.
            MIL.MbufInquire(SrcImageId, MIL.M_HOST_ADDRESS, SrcImageDataPtr)
            MIL.MbufInquire(SrcImageId, MIL.M_SIZE_X, SrcImageSizeX)
            MIL.MbufInquire(SrcImageId, MIL.M_SIZE_Y, SrcImageSizeY)
            MIL.MbufInquire(SrcImageId, MIL.M_TYPE, SrcImageType)
            MIL.MbufInquire(SrcImageId, MIL.M_PITCH_BYTE, SrcImagePitchByte)
            MIL.MbufInquire(DstImageId, MIL.M_HOST_ADDRESS, DstImageDataPtr)
            MIL.MbufInquire(DstImageId, MIL.M_SIZE_X, DstImageSizeX)
            MIL.MbufInquire(DstImageId, MIL.M_SIZE_Y, DstImageSizeY)
            MIL.MbufInquire(DstImageId, MIL.M_TYPE, DstImageType)
            MIL.MbufInquire(DstImageId, MIL.M_PITCH_BYTE, DstImagePitchByte)

            ' Reduce the destination area to process if necessary.
            If SrcImageSizeX < DstImageSizeX Then
                DstImageSizeX = SrcImageSizeX
            End If
            If SrcImageSizeY < DstImageSizeY Then
                DstImageSizeY = SrcImageSizeY
            End If

            ' If images have the proper depth, execute the operation using a custom code.
            If ((SrcImageType = DstImageType) And (SrcImageType = FUNCTION_SUPPORTED_IMAGE_TYPE) And _
                (SrcImageDataPtr <> MIL.M_NULL) And (DstImageDataPtr <> MIL.M_NULL)) Then
                Dim SrcImagePixels(CType((SrcImageSizeX - 1), Integer), CType((SrcImageSizeY - 1), Integer)) As Byte
                Dim DstImagePixels(CType((DstImageSizeX - 1), Integer), CType((DstImageSizeY - 1), Integer)) As Byte

                MIL.MbufGet(SrcImageId, SrcImagePixels)

                ' Add the constant to the image.
            For y = 0 To CType((DstImageSizeY - 1), Integer)
               For x = 0 To CType((DstImageSizeX - 1), Integer)
                  ' Calculate the value to write.
                  TempValue = CType(SrcImagePixels(x, y) + ConstantToAdd, Integer)

                  ' Write the value if no overflow, else saturate.
                  If TempValue <= &HFF Then
                     DstImagePixels(x, y) = CByte(TempValue)
                  Else
                     DstImagePixels(x, y) = &HFF
                  End If
               Next x
            Next y

                MIL.MbufPut(DstImageId, DstImagePixels)

                ' Return a null error code to the Master function.
                MIL_INT.WriteMilInt(SlaveReturnValuePtr, MIL.M_NULL)
            Else
                ' Report a MIL error.
                MIL.MfuncErrorReport(Func, MIL.M_FUNC_ERROR + FUNCTION_PARAMETER_ERROR_CODE, _
                                     "Invalid parameter.", _
                                     "Image type not supported or invalid target system.", _
                                     "Image depth must be 8-bit and have a valid host address.", _
                                     MIL.M_NULL)

                ' Return an error code to the Master function.

                MIL_INT.WriteMilInt(SlaveReturnValuePtr, MIL.M_FUNC_ERROR + FUNCTION_PARAMETER_ERROR_CODE)
            End If

            ' Unlock buffers.
            MIL.MbufControl(SrcImageId, MIL.M_UNLOCK, MIL.M_DEFAULT)
            MIL.MbufControl(DstImageId, MIL.M_UNLOCK, MIL.M_DEFAULT)

            ' Signal to MIL that the destination buffer was modified.
            MIL.MbufControl(DstImageId, MIL.M_MODIFIED, MIL.M_DEFAULT)
        End Sub

        ' Main to test the custom function. //
        ' --------------------------------- //

        Shared Sub Main(ByVal args() As String)
            Dim MilApplication As MIL_ID = MIL.M_NULL   ' Application identifier.
            Dim MilSystem As MIL_ID = MIL.M_NULL        ' System identifier.
            Dim MilDisplay As MIL_ID = MIL.M_NULL       ' Display identifier.
            Dim MilImage As MIL_ID = MIL.M_NULL         ' Image buffer identifier.
            Dim ReturnValue As MIL_INT = 0              ' Return value of the function.

            ' Allocate default application, system, display and image.
            MIL.MappAllocDefault(MIL.M_DEFAULT, MilApplication, MilSystem, MilDisplay, CType(MIL.M_NULL, IntPtr), CType(MIL.M_NULL, IntPtr))

            ' Load source image into a Host memory image buffer.
            MIL.MbufAlloc2d(MilSystem, MIL.MbufDiskInquire(IMAGE_FILE, MIL.M_SIZE_X), MIL.MbufDiskInquire(IMAGE_FILE, MIL.M_SIZE_Y), 8 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_DISP + MIL.M_HOST_MEMORY, MilImage)
            MIL.MbufLoad(IMAGE_FILE, MilImage)

            ' Display the image.
            MIL.MdispSelect(MilDisplay, MilImage)

            ' Pause.
            Console.WriteLine()
            Console.WriteLine("MIL FUNCTION DEVELOPER'S TOOLKIT:")
            Console.WriteLine("---------------------------------")
            Console.WriteLine()
            Console.WriteLine("This example creates a custom MIL function that processes")
            Console.WriteLine("an image using its data pointer directly.")
            Console.WriteLine()
            Console.WriteLine("Target image was loaded.")
            Console.WriteLine("Press a key to continue.")
            Console.WriteLine()
            Console.ReadKey()

            ' Run the custom function only if the target system's memory is local and accessible.
            If MIL.MsysInquire(MilSystem, MIL.M_LOCATION, MIL.M_NULL) = MIL.M_LOCAL Then
                ' Process the image directly with the custom MIL function.
                ReturnValue = AddConstant(MilImage, MilImage, &H40)

                ' Print a conclusion message.
                If (ReturnValue = MIL.M_NULL) Then
                    Console.WriteLine("The white level of the image was augmented.")
                Else
                    Console.WriteLine("An error was returned by the Slave function.")
                End If
            Else
                Console.WriteLine("This example doesn't run with Distributed MIL.")
            End If

            Console.WriteLine("Press a key to terminate.")
            Console.WriteLine()
            Console.ReadKey()

            ' Free all allocations.
            MIL.MbufFree(MilImage)
            MIL.MappFreeDefault(MilApplication, MilSystem, MilDisplay, MIL.M_NULL, MIL.M_NULL)
        End Sub
    End Class
End Namespace