context: using System;
  using System.Runtime.InteropServices;
  using System.Collections.Generic;
  using System.Drawing;
  using OpenTK;
  using OpenTK.Graphics;
  using OpenTK.Platform;
  using System.Text;
  //using System.Threading.Tasks;
  using ES20 = OpenTK.Graphics.ES20;
  using ES11 = OpenTK.Graphics.ES11;
  
  
  #if IOS
  using OpenTK.Platform.iPhoneOS;
  using MonoTouch.Foundation;
  using MonoTouch.ObjCRuntime;
  #elif ANDROID
  using OpenTK.Platform.Android;
  using Android.Views;
  using Android.Util;
  using Android.Content;
  #else
  
  #endif
  using Emgu.CV;
  using Emgu.CV.Structure;
  
  namespace Emgu.CV.UI.GLView
  {
  #if IOS
     [Register("GLImageView")]
     #endif
     public class GLImageView 
  #if IOS
        : iPhoneOSGameView
  #elif ANDROID
        : AndroidGameView
  #else
        : GLControl
  #endif
     {
  
        private const int _glTextureDimension = 512;
  
        public enum OpenGLVersion
        {
           ES1,
           ES2, 
           GL3
        }
  
  #if ANDROID || IOS
        private OpenGLVersion _glVersion = OpenGLVersion.ES1;
  #else
        private OpenGLVersion _glVersion = OpenGLVersion.GL3;
  #endif
        public OpenGLVersion GlVersion
        {
           get
           {
              return _glVersion;
           }
        }
  
        //int _viewPortWidth, _viewPortHeight;
        private String PackageName;
        private System.Drawing.Size _imageSize;
  
        int[] _textureIds;
        private System.Drawing.Size[] _textureSizes;
        private TextureColor[] _textureColor;
        private bool[] _textureEnabled;
       
        /// <summary>
        /// Individual texture rotations
        /// </summary>
        private float[] _textureRotations;
        private Emgu.CV.CvEnum.FlipType[] _textureFlipMode;
        private bool[] _textureRequiresReset;
  
        private ImageBufferFactory<Image<Bgr, Byte>> _bgrBuffers;
        private String _frameFileName;
  
        private int _gridLines;
        private float[] _verticalGridLines;
        private float[] _horizontalGridLines;
  
        #region GL_20
        private Matrix4 _projectionMatrix;
        private int _textureRenderRGBAProgramHandle;
        private int _textureRenderBGRAProgramHandle;
        private int _lineRenderProgramHandle;
        #endregion
  
        private static readonly float _degreeToRadian = (float)(Math.PI / 180.0);
        private RectangleF[] _rectangles;
  
        private const float _expectedLeft = -1.0f;
        private const float _expectedRight = 1.0f;
        private const float _expectedBottom = -1.0f;
        private const float _expectedTop = 1.0f;
  
        private float _left = _expectedLeft;
        private float _right = _expectedRight;
        private float _top = _expectedTop;
        private float _bottom = _expectedBottom;
        private float _zNear = 0.01f;
        private float _zFar = 1.0f;
  
        static float Min = -1;
        static float Max = 1;
  
        static float RectanglesVertexDepth = 0.1f;
        static float GridVertexDepth = 0.2f;
        static float FrameVertexDepth = 0.3f;
        static float ImageVertexDepth = 0.4f;
  
        static float[][] _textureVertexCoords = new float[][] 
        {
           new float[] { 
               Max, Max, ImageVertexDepth,
               Min, Max, ImageVertexDepth,
               Min, Min, ImageVertexDepth,
               Max, Min, ImageVertexDepth
           },
           new float[] { 
               Max, Max, FrameVertexDepth,
               Min, Max, FrameVertexDepth,
               Min, Min, FrameVertexDepth,
               Max, Min, FrameVertexDepth
           }
        };
  
        static float[] squareTextureCoords =
           new float[] { 
              1, 0,
              0, 0,
              0, 1,
              1, 1
           };
  
        static float[] squareTextureCoordsFlipHorizontal =
           new float[] {
              0, 0, 
              1, 0,
              1, 1, 
              0, 1
           };
  
        static float[] squareTextureCoordsFlipVetical =
           new float[] {
              1, 1,
              0, 1, 
              0, 0,
              1, 0
           };
  
        private float[] MergeArray(float[] lines, float[] newLines)
        {
           if (lines == null)
              return newLines;
           
           if (newLines == null)
              return lines;
  
           int oldLength = lines.Length;
           Array.Resize(ref lines, lines.Length + newLines.Length);
           Array.Copy(newLines, 0, lines, oldLength, newLines.Length);
           return lines;
        }
  
        public void GetGridLines(out float[] lines, out float[] colors)
        {
           float[] color = new float[] { 0.0f, 0.0f, 0.0f, 1.0f };
  
           lines = MergeArray(null, _verticalGridLines);
           lines = MergeArray(lines, _horizontalGridLines);
  
           colors = null;
           if (lines == null)
           {
              return;
           }
  
           //every 3 float represent one point, each point need 4 values for color;
           colors = new float[4 * (lines.Length / 3)];
           for (int i = 0, idx = 0; i < lines.Length / 3; i++)
           {
              colors[idx++] = color[0];
              colors[idx++] = color[1];
              colors[idx++] = color[2];
              colors[idx++] = color[3];
           }
        }
  
        public void GetRectangleLines(out float[] lines, out float[] colors)
        {
           float[] color = new float[] { 1.0f, 1.0f, 1.0f, 1.0f };
  
           colors = null;
           lines = null;
           if (_rectangles == null)
              return;
  
           int pointsCount = _rectangles.Length * 8;
  
           lines = new float[pointsCount * 3];
           int idx = 0;
           foreach (RectangleF rect in _rectangles)
           {
              lines[idx++] = rect.Location.X;
              lines[idx++] = rect.Location.Y;
              lines[idx++] = RectanglesVertexDepth;
              lines[idx++] = rect.Location.X + rect.Width;
              lines[idx++] = rect.Location.Y;
              lines[idx++] = RectanglesVertexDepth;
  
              lines[idx++] = rect.Location.X + rect.Width;
              lines[idx++] = rect.Location.Y;
              lines[idx++] = RectanglesVertexDepth;
              lines[idx++] = rect.Location.X + rect.Width;
              lines[idx++] = rect.Location.Y + rect.Height;
              lines[idx++] = RectanglesVertexDepth;
  
              lines[idx++] = rect.Location.X + rect.Width;
              lines[idx++] = rect.Location.Y + rect.Height;
              lines[idx++] = RectanglesVertexDepth;
              lines[idx++] = rect.Location.X;
              lines[idx++] = rect.Location.Y + rect.Height;
              lines[idx++] = RectanglesVertexDepth;
  
              lines[idx++] = rect.Location.X;
              lines[idx++] = rect.Location.Y + rect.Height;
              lines[idx++] = RectanglesVertexDepth;
              lines[idx++] = rect.Location.X;
              lines[idx++] = rect.Location.Y;
              lines[idx++] = RectanglesVertexDepth;
           }
  
           idx = 0;
           //change the X and Y coordinate such that they range from [-1.0, 1.0] instead of [0.0, 1.0];
           for (int i = 0; i < pointsCount; i++)
           {
              lines[idx] = lines[idx] * 2.0f -1.0f;
              idx++;
              lines[idx] = lines[idx] * 2.0f -1.0f;
              idx += 2;
           }
  
           colors = new float[4 * pointsCount];
           idx = 0;
           for (int i = 0; i < pointsCount; i++)
           {
              colors[idx++] = color[0];
              colors[idx++] = color[1];
              colors[idx++] = color[2];
              colors[idx++] = color[3];
           }
        }
  
        public int GridLines
        {
           get
           {
              return _gridLines;
           }
           set
           {
              _gridLines = value;
              if (_gridLines <= 0)
              {
                 _verticalGridLines = null;
                 _horizontalGridLines = null;
                 _gridLines = 0;
              }
              else
              {
                 _verticalGridLines = new float[GridLines * 6];
                 _horizontalGridLines = new float[GridLines * 6];
                 int idx = 0;
  
                 float increment = (Max - Min) / (GridLines + 1);
                 float current = Min + increment;
                 for (int i = 0; i < GridLines; ++i)
                 {
                    _verticalGridLines[idx] = Min;
                    _horizontalGridLines[idx] = current;
                    idx++;
                    _verticalGridLines[idx] = current;
                    _horizontalGridLines[idx] = Min;
                    idx++;
                    _verticalGridLines[idx] = GridVertexDepth;
                    _horizontalGridLines[idx] = GridVertexDepth;
                    idx++;
                    _verticalGridLines[idx] = Max;
                    _horizontalGridLines[idx] = current;
                    idx++;
                    _verticalGridLines[idx] = current;
                    _horizontalGridLines[idx] = Max;
                    idx++;
                    _verticalGridLines[idx] = GridVertexDepth;
                    _horizontalGridLines[idx] = GridVertexDepth;
                    idx++;
                    current += increment;
                 }
              }
           }
        }
  
        /// <summary>
        /// The rectangles to be drawed. (0,0) point should be the image texture origin where (1,1) should match the opposite corner of the image.
        /// </summary>
        public RectangleF[] Rectangles
        {
           get
           {
              return _rectangles;
           }
           set
           {
              _rectangles = value;
           }
        }
  
        public enum TouchType
        {
           Down,
           Fling, 
           Scroll,
           SingleTapUp
        }
  
        private PointF ViewportPointToWorldPoint(PointF viewportPoint)
        {
           return new PointF(
              (viewportPoint.X / ViewPortWidth) * (_right - _left) + _left,
              ( (ViewPortHeight - viewportPoint.Y) / ViewPortHeight) * (_top - _bottom) + _bottom);
        }
  
        private PointF WorldPointToImagePoint(PointF worldPoint)
        {
           return new PointF((worldPoint.X + 1.0f) / 2.0f, (worldPoint.Y + 1.0f) / 2.0f); 
        }
  
        private static float Dist(PointF p1, PointF p2)
        {
           float dx = p1.X - p2.X;
           float dy = p1.Y - p2.Y;
           return (float) Math.Sqrt(dx * dx + dy * dy);
        }
  
        private static PointF RestrictRange(PointF point, float minX, float maxX, float minY, float maxY)
        {
           if (point.X < minX)
              point.X = minX;
           if (point.X > maxX)
              point.X = maxX;
           if (point.Y < minY)
              point.Y = minY;
           if (point.Y > maxY)
              point.Y = maxY;
           return point;
        }
  
        public System.Drawing.Rectangle GetRectangleInImageCoordinate(int rectangleIndex, System.Drawing.Size imageSize)
        {
           if (_rectangles == null || rectangleIndex >= _rectangles.Length)
              return System.Drawing.Rectangle.Empty;
           System.Drawing.RectangleF rect = _rectangles[rectangleIndex];
           System.Drawing.Rectangle roi = new System.Drawing.Rectangle(
                 new System.Drawing.Point((int)(rect.Location.X * imageSize.Width), (int)((1.0 - rect.Location.Y) * imageSize.Height)),
                 new System.Drawing.Size((int)(rect.Width * imageSize.Width), (int)(rect.Height * imageSize.Height)));
              roi.Location = new System.Drawing.Point(roi.Location.X, roi.Location.Y - roi.Height);
           return roi;
        }
  
        /// <summary>
        /// Update the rectangles location by a touch event
        /// </summary>
        /// <param name="type">The type of touch</param>
        /// <param name="viewportStartPoint">The start point of the touch, may be PointF.Empty if touch type is down</param>
        /// <param name="viewportEndPoint">The end point of the touch</param>
        /// <param name="rectangleIndex">The index of the rectangle when the shape is to be updated.</param>
        public void UpdateRectangleByTouch(TouchType type, PointF viewportStartPoint, PointF viewportEndPoint, int rectangleIndex)
        {
           if (_rectangles == null || rectangleIndex >= _rectangles.Length)
              return;
  
           //PointF startPoint = RestrictRange( WorldPointToImagePoint( ViewportPointToWorldPoint(viewportStartPoint)), 0.0f, 1.0f, 0.0f, 1.0f);
           PointF endPoint = RestrictRange( WorldPointToImagePoint( ViewportPointToWorldPoint(viewportEndPoint) ), 0.0f, 1.0f, 0.0f, 1.0f);
           
           RectangleF rect = _rectangles[rectangleIndex];
  
           float scale = 0.6f;
           RectangleF rectCenterRegion = new RectangleF(new PointF(rect.Location.X + (rect.Width - rect.Width * scale) / 2.0f, rect.Location.Y + (rect.Height -rect.Height * scale) / 2.0f), new SizeF(rect.Width * scale, rect.Height * scale));
  
           if (rectCenterRegion.Contains(endPoint))
           {  // move the center to the end point while keeping the width and height
              float newX = endPoint.X - rect.Width / 2.0f;
              if (newX <= 0)
                 newX = 0;
              if (newX + rect.Width > 1.0f)
                 newX = 1.0f - rect.Width;
  
              float newY = endPoint.Y - rect.Height / 2.0f;
              if (newY <= 0)
                 newY = 0;
              if (newY + rect.Height > 1.0f)
                 newY = 1.0f - rect.Height;
              rect.Location = new PointF(newX, newY);
  
              _rectangles[rectangleIndex] = rect;
              return;
           }
  
           PointF point0 = rect.Location;
           PointF point1 = new PointF(point0.X + rect.Width, point0.Y);
           PointF point2 = new PointF(point0.X + rect.Width, point0.Y + rect.Height);
           PointF point3 = new PointF(point0.X, point0.Y + rect.Height);
  
           float d0 = Dist(endPoint, point0);
           float d1 = Dist(endPoint, point1);
           float d2 = Dist(endPoint, point2);
           float d3 = Dist(endPoint, point3);
  
           float min = Math.Min(Math.Min(d0, d1), Math.Min(d2, d3));
           if (min == d0)
           {
              //PointF newLoc = endPoint;
              //if (newLoc.X < 0)
              //   newLoc.X = 0;
              //if (newLoc.X > 1.0f)
              //   newLoc.X = 1.0f;
              rect.Width = point2.X - endPoint.X;
              //rect.Location = endPoint;           
  
              //if (newLoc.Y < 0)
              //   newLoc.Y = 0;
              //if (newLoc.Y > 1.0f)
              //   newLoc.Y = 1.0f;
              rect.Height = point2.Y - endPoint.Y;
              rect.Location = endPoint;
           }
           else if (min == d1)
           {
              rect.Width = endPoint.X - point0.X;
              if (rect.Width <= 0)
                 rect.Width = 1.0e-5f;
  
  
              rect.Height = point3.Y - endPoint.Y;
              if (rect.Height <= 0)
                 rect.Height = 1.0e-5f;
              rect.Location = new PointF(rect.Location.X, endPoint.Y);
  
              //if (rect.Width + point0.X > 1.0f)
              //   rect.Width = 1.0f - point0.X;
           }
           else if (min == d2)
           {
              rect.Width = endPoint.X - point0.X;
              if (rect.Width <= 0)
                 rect.Width = 1.0e-5f;
              //if (rect.Width + point0.X > 1.0f)
              //   rect.Width = 1.0f - point0.X;
  
              rect.Height = endPoint.Y - point0.Y;
              if (rect.Height <= 0)
                 rect.Height = 1.0e-5f;
              //if (rect.Height + point0.Y > 1.0f)
              //   rect.Height = 1.0f - point0.Y;
  
           }
           else if (min == d3)
           {
              rect.Height = endPoint.Y - point0.Y;
              if (rect.Height <= 0)
                 rect.Height = 1.0e-5f;
  
              rect.Width = point1.X - endPoint.X;
              if (rect.Width <= 0)
                 rect.Width= 1.0e-5f;
              rect.Location = new PointF(endPoint.X, rect.Location.Y);
              //if (rect.Height + point0.Y > 1.0)
              //   rect.Height = 1.0f - point0.Y;
           }
           _rectangles[rectangleIndex] = rect;
        }
  
        public bool[] TextureEnabled
        {
           get { return _textureEnabled; }
        }
  
        /*
        public void SetTextureRotation(int textureIdx, int rotation)
        {
           float oldRotation = _textureRotation[textureIdx];
           if (((oldRotation == 0 || oldRotation == 180) && (rotation == 90 || rotation == 270))
              || ((oldRotation == 90 || oldRotation == 270) && (rotation == 0 || rotation == 180)))
           {
              Size s = _imageSize;
              _imageSize.Width = s.Height;
              _imageSize.Height = s.Width;
           }
  
           _textureRotation[textureIdx] = rotation;
        }*/
  
        public Emgu.CV.CvEnum.FlipType[] TextureFlipMode
        {
           get
           {
              return _textureFlipMode;
           }
        }
  
        /*
        public bool TextureRequiresReset
        {
           get { return _textureRequiresReset; }
           set { _textureRequiresReset = value; }
        }*/
  
  #if IOS
        public GLImageView(RectangleF frame)
           : base(frame)
        {
           Initialize();
        }
  
        /*
        [Export("initWithCoder:")]
        public GLImageView(NSCoder coder)
           : base(coder)
        {
           Initialize();
        }*/
  
        [Export ("layerClass")]
        public static Class LayerClass()
        {
           return iPhoneOSGameView.GetLayerClass();
        }
  #elif ANDROID
        public GLImageView(Context context, GLVersion glVersion)
           : base(context)
        {
           _glVersion = glVersion;
           Initialize();
        }
  
        public GLImageView(Context context, IAttributeSet attrs, GLVersion glVersion) :
           base(context, attrs)
        {
           _glVersion = glVersion;
           Initialize();
        }
  
        public GLImageView(IntPtr handle, Android.Runtime.JniHandleOwnership transfer, GLVersion glVersion)
           : base(handle, transfer)
        {
           _glVersion = glVersion;
           Initialize();
        }
  #else
        public GLImageView()
           :base()
        {
           Initialize();
        }
  #endif
  
  #if IOS || ANDROID
        public override void Pause()
        {
           base.Pause();
           DeleteTextures();
        }
  
        public override void Resume()
        {
           base.Resume();
           GenTextures();
        }
  #endif
  
        private void Initialize()
        {
  #if IOS
           LayerRetainsBacking = true;
           LayerColorFormat = MonoTouch.OpenGLES.EAGLColorFormat.RGBA8;
           CreateFrameBuffer();
  #elif ANDROID
           PackageName = Context.PackageName;
  #else
  #endif
  
           _textureSizes = new System.Drawing.Size[2];
           _textureColor = new TextureColor[2] {
              TextureColor.NotSupported,
              TextureColor.NotSupported
           };
           _textureEnabled = new bool[2];
           _textureRotations = new float[2];
  
           _textureFlipMode = new Emgu.CV.CvEnum.FlipType[2];
           for (int i = 0; i < _textureFlipMode.Length; i++)
           {
              _textureFlipMode[i] = Emgu.CV.CvEnum.FlipType.None;
           }
  
           _textureRequiresReset = new bool[2];
           for (int i = 0; i < _textureRequiresReset.Length; i++)
              _textureRequiresReset[i] = true;
  
           if (_glVersion == OpenGLVersion.ES1)
              _bgrBuffers = new ImageBufferFactory<Image<Bgr, byte>>(s => new Image<Bgr, byte>(s));
            
           _textureIds = new int[2];
           //context = Context;
           //xangle = 45;
           //yangle = 45;
  
  #if IOS
           OnLoad(null);
  #endif
  
           Resize += delegate
           {
              //_viewPortHeight = Height;
              //_viewPortWidth = Width;
              //SetupCamera();
              PerformRenderAll();
           };
           
        }
  
  #if IOS || ANDROID
        // This method is called everytime the context needs
        // to be recreated. Use it to set any egl-specific settings
        // prior to context creation
        protected override void CreateFrameBuffer()
        {
  #if IOS
           ContextRenderingApi = MonoTouch.OpenGLES.EAGLRenderingAPI.OpenGLES1;
  #elif ANDROID
           ContextRenderingApi = _glVersion;
  #endif
           // the default GraphicsMode that is set consists of (16, 16, 0, 0, 2, false)
           try
           {
              Report.Debug(PackageName, "Loading with GL Image View with default settings");
              //if (GraphicsContext == null)
                 // if you don't call this, the context won't be created
                 base.CreateFrameBuffer();
  
              return;
           } catch (Exception ex)
           {
              Report.Error(PackageName, ex.Message);
           }
  
           // Fallback modes
           // If the first attempt at initializing the surface with a default graphics
           // mode fails, then the app can try different configurations. Devices will
           // support different modes, and what is valid for one might not be valid for
           // another. If all options fail, you can set all values to 0, which will
           // ask for the first available configuration the device has without any
           // filtering.
           // After a successful call to base.CreateFrameBuffer(), the GraphicsMode
           // object will have its values filled with the actual values that the
           // device returned.
  
  
           // This is a setting that asks for any available 16-bit color mode with no
           // other filters. It passes 0 to the buffers parameter, which is an invalid
           // setting in the default OpenTK implementation but is valid in some
           // Android implementations, so the AndroidGraphicsMode object allows it.
           try
           {
  #if IOS
  #elif ANDROID
              Report.Debug(PackageName, "Loading with custom Android settings (low mode)");
              GraphicsMode = new AndroidGraphicsMode(16, 0, 0, 0, 0, false);
  #endif
              //if (GraphicsContext == null)
                 // if you don't call this, the context won't be created
                 base.CreateFrameBuffer();
              return;
           } catch (Exception ex)
           {
              Report.Error(PackageName, ex.Message);
           }
  
           // this is a setting that doesn't specify any color values. Certain devices
           // return invalid graphics modes when any color level is requested, and in
           // those cases, the only way to get a valid mode is to not specify anything,
           // even requesting a default value of 0 would return an invalid mode.
           try
           {
  #if IOS
  #elif ANDROID
              Report.Debug(PackageName, "Loading with no Android settings");
              GraphicsMode = new AndroidGraphicsMode(0, 4, 0, 0, 0, false);
  #endif 
              //if (GraphicsContext == null)
                 // if you don't call this, the context won't be created
                 base.CreateFrameBuffer();
              return;
           } catch (Exception ex)
           {
              Report.Error(PackageName, ex.Message);
           }
           throw new Exception("Can't load egl, aborting");
        }
  #endif
  
        protected override void OnLoad(EventArgs e)
        {
           base.OnLoad(e);
  
  #if ANDROID || IOS
           if (_glVersion == OpenGLVersion.ES2)
           {
              ES20.All error = ES20.GL.GetError();
              if (error != ES20.All.NoError)
              {
                 Report.Error(PackageName, String.Format("GL Error before onLoad: {0}", error));
              }
              _textureRenderRGBAProgramHandle = GetRenderRGBATextureProgram();
              _textureRenderBGRAProgramHandle = GetRenderBGRATextureProgram();
  
              _lineRenderProgramHandle = GetRenderLineProgram();
              error = ES20.GL.GetError();
              if (error != ES20.All.NoError)
              {
                 Report.Error(PackageName, String.Format("GL Error during onLoad 0: {0}", error));
              }
  
              GenTextures();
  
              error = ES20.GL.GetError();
              if (error != ES20.All.NoError)
              {
                 Report.Error(PackageName, String.Format("GL Error during onLoad 2: {0}", error));
              }
              for (int i = 0; i < _textureRequiresReset.Length; i++)
                 _textureRequiresReset[i] = true;
  
              if (_textureEnabled[1])
                 LoadFrame();
  
              /*
              if (_textureEnabled[0])
              {
                 Image<Bgr, Byte> img = _bgrBuffers.GetBuffer(0);
                 if (img != null)
                 {
                    LoadTexture(img.Size, img.MIplImage.imageData, TextureColor.RGB, 0);
                 }
              }*/
  
              error = ES20.GL.GetError();
              if (error != ES20.All.NoError)
              {
                 Report.Error(PackageName, String.Format("GL Error during onLoad 3: {0}", error));
              }
  
              ES20.GL.BlendFunc(ES20.All.SrcAlpha, ES20.All.OneMinusSrcAlpha);
              ES20.GL.Enable(ES20.All.Blend);
  
              error = ES20.GL.GetError();
              if (error != ES20.All.NoError)
              {
                 Report.Error(PackageName, String.Format("GL Error during onLoad 4: {0}", error));
              }
           }
           else
           {
              ES11.All error = ES11.GL.GetError();
              if (error != ES11.All.NoError)
              {
                 Report.Error(PackageName, String.Format("GL Error before onLoad: {0}", error));
              }
              ES11.GL.ShadeModel(ES11.All.Smooth);
              ES11.GL.ClearColor(0, 0, 0, 1);
  
              ES11.GL.ClearDepth(1.0f);
              ES11.GL.Enable(ES11.All.DepthTest);
              ES11.GL.DepthFunc(ES11.All.Lequal);
  
              ES11.GL.Enable(ES11.All.CullFace);
              ES11.GL.CullFace(ES11.All.Back);
  
              error = ES11.GL.GetError();
              if (error != ES11.All.NoError)
              {
                 Report.Error(PackageName, String.Format("GL Error during onLoad 0: {0}", error));
              }
  
              ES11.GL.Hint(ES11.All.PerspectiveCorrectionHint, ES11.All.Nicest);
              // create texture ids
              ES11.GL.Enable(ES11.All.Texture2D);
  
              GenTextures();
  
              error = ES11.GL.GetError();
              if (error != ES11.All.NoError)
              {
                 Report.Error(PackageName, String.Format("GL Error during onLoad 2: {0}", error));
              }
              for (int i = 0; i < _textureRequiresReset.Length; i++)
                 _textureRequiresReset[i] = true;
  
              if (_textureEnabled[1])
                 LoadFrame();
  
              if (_textureEnabled[0])
              {
                 Image<Bgr, Byte> img = _bgrBuffers.GetBuffer(0);
                 if (img != null)
                 {
                    LoadTexture(img.Size, img.MIplImage.imageData, TextureColor.RGB, 0);
                 }
              }
  
              error = ES11.GL.GetError();
              if (error != ES11.All.NoError)
              {
                 Report.Error(PackageName, String.Format("GL Error during onLoad 3: {0}", error));
              }
  
              ES11.GL.BlendFunc(ES11.All.SrcAlpha, ES11.All.OneMinusSrcAlpha);
              ES11.GL.Enable(ES11.All.Blend);
  
              error = ES11.GL.GetError();
              if (error != ES11.All.NoError)
              {
                 Report.Error(PackageName, String.Format("GL Error during onLoad 4: {0}", error));
              }
           }
  #else
           if (_glVersion == OpenGLVersion.GL3)
           {
  
              _textureRenderRGBAProgramHandle = GetRenderRGBATextureProgram();
              _textureRenderBGRAProgramHandle = GetRenderBGRATextureProgram();
  
              _lineRenderProgramHandle = GetRenderLineProgram();
  
  
              GenTextures();
  
            
              for (int i = 0; i < _textureRequiresReset.Length; i++)
                 _textureRequiresReset[i] = true;
  
              if (_textureEnabled[1])
                 LoadFrame();
  
              /*
              if (_textureEnabled[0])
              {
                 Image<Bgr, Byte> img = _bgrBuffers.GetBuffer(0);
                 if (img != null)
                 {
                    LoadTexture(img.Size, img.MIplImage.imageData, TextureColor.RGB, 0);
                 }
              }*/
              
              GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
              GL.Enable(EnableCap.Blend);
           }
  
  #endif
           PerformRenderAll();
        }
  
        private void PerformRenderAll()
        {
  #if IOS
           PerformSelector(new Selector("RenderAll"), null, 0.2f);
  #else
           RenderAll();
  #endif
        }
  
        #region ES20
        int ES2LoadShader(ES20.All type, string source)
        {
           int shader = ES20.GL.CreateShader(type);
           if (shader == 0)
           {
              throw new InvalidOperationException(String.Format("Unable to create shader {0}", ES20.GL.GetError()));
           }
  
           int length = 0;
           ES20.GL.ShaderSource(shader, 1, new string[] { source }, (int[])null);
           ES20.GL.CompileShader(shader);
  
           int compiled = 0;
           ES20.GL.GetShader(shader, ES20.All.CompileStatus, out compiled);
           if (compiled == 0)
           {
              length = 0;
              ES20.GL.GetShader(shader, ES20.All.InfoLogLength, out length);
              if (length > 0)
              {
                 var log = new StringBuilder(length);
                 ES20.GL.GetShaderInfoLog(shader, length, out length, log);
                 Report.Debug("GL2", "Couldn't compile shader: " + log.ToString());
              }
  
              ES20.GL.DeleteShader(shader);
              throw new InvalidOperationException("Unable to compile shader of type : " + type.ToString());
           }
  
           return shader;
        }
        #endregion
  
        int GLLoadShader(ShaderType type, string source)
        {
           int shader = GL.CreateShader(type);
           if (shader == 0)
           {
              throw new InvalidOperationException(String.Format("Unable to create shader {0}", GL.GetError()));
           }
  
           int length = 0;
           GL.ShaderSource(shader, source);
           GL.CompileShader(shader);
  
           int compiled = 0;
           GL.GetShader(shader,  ShaderParameter.CompileStatus, out compiled);
           if (compiled == 0)
           {
              length = 0;
              GL.GetShader(shader, ShaderParameter.InfoLogLength, out length);
              if (length > 0)
              {
                 var log = new StringBuilder(length);
                 GL.GetShaderInfoLog(shader, length, out length, log);
                 Report.Debug("GL", "Couldn't compile shader: " + log.ToString());
              }
  
              GL.DeleteShader(shader);
              throw new InvalidOperationException("Unable to compile shader of type : " + type.ToString());
           }
  
           return shader;
        }
  
        private int ViewPortWidth
        {
           get
           {
  #if IOS
              return Size.Width;
  #else
              return Width;
  #endif
           }
        }
  
        private int ViewPortHeight
        {
           get
           {
  #if IOS
              return Size.Height;
  #else
              return Height;
  #endif
           }
        }
  
        /*
        public void SetupCamera()
        {
  
  
        }*/
        
        /*
        public override bool OnTouchEvent(MotionEvent e)
        {
           base.OnTouchEvent(e);
           if (e.Action == MotionEventActions.Down)
           {
              prevx = e.GetX();
              prevy = e.GetY();
           }
           if (e.Action == MotionEventActions.Move)
           {
              float e_x = e.GetX();
              float e_y = e.GetY();
  
              float xdiff = (prevx - e_x);
              float ydiff = (prevy - e_y);
              xangle = xangle + ydiff;
              yangle = yangle + xdiff;
              prevx = e_x;
              prevy = e_y;
           }
           if (e.Action == MotionEventActions.Down || e.Action == MotionEventActions.Move)
              RenderTexture();
           return true;
        }*/
  
  #if IOS || ANDROID
        protected override void OnUnload(EventArgs e)
        {
           base.OnUnload(e);
           DeleteTextures();
        }
  #endif
  
        private void DeleteTextures()
        {
           for (int i = 0; i < _textureSizes.Length; i++)
           {
              _textureSizes[i] = Size.Empty;
              _textureRequiresReset[i] = true;
              _textureEnabled[i] = false;
           }
           if (_glVersion == OpenGLVersion.ES2)
           {
              ES20.GL.DeleteTextures(2, _textureIds);
           }
           else if (_glVersion == OpenGLVersion.ES1)
           {
              ES11.GL.DeleteTextures(2, _textureIds);
           } else
           {
              GL.DeleteTextures(2, _textureIds);
           }
        }
  
        private void GenTextures()
        {
           for (int i = 0; i < _textureSizes.Length; i++)
           {
              _textureSizes[i] = Size.Empty;
              _textureRequiresReset[i] = true;
              _textureEnabled[i] = false;
           }
  
           if (_glVersion == OpenGLVersion.ES2)
           {
              ES20.GL.GenTextures(_textureIds.Length, _textureIds);
           }
           else if (_glVersion == OpenGLVersion.ES1)
           {
              ES11.GL.GenTextures(_textureIds.Length, _textureIds);
           } else
           {
              GL.GenTextures(_textureIds.Length, _textureIds);
           }
        }
  
  
        private float _rotation;
  
        /// <summary>
        /// Get or set the rotation of the whole GLImageView in degree
        /// </summary>
        public float Rotation
        {
           get
           {
              return _rotation;
           }
           set
           {
              _rotation = value;
           }
        }
  
        private void ResetScreen()
        {
           // setup the projection matrix to compensate for the aspect ratio of the imageSize;
           float widthScale = 1.0f;
           float heightScale = 1.0f;
           int viewPortWidth = ViewPortWidth;
           int viewPortHeight = ViewPortHeight;
  
           if (!(_textureSizes[0].IsEmpty || viewPortHeight == 0))
           {
              double viewPortWidthHeight = (double)viewPortWidth / (double)viewPortHeight;
              double imageWidthHeight = (double)_imageSize.Width / (double)_imageSize.Height;
              if (viewPortWidthHeight > imageWidthHeight)
              {
                 //view port is too wide
                 widthScale = (float)(viewPortWidthHeight / imageWidthHeight);
                 float compensation = (float)(viewPortWidthHeight - imageWidthHeight) / 2.0f;
                 _left = _expectedLeft - compensation;
                 _right = _expectedRight + compensation;
                 _top = _expectedTop;
                 _bottom = _expectedBottom;
  
              }
              else
              {
                 heightScale = 1.0f / (float)(viewPortWidthHeight / imageWidthHeight);
                 float compensation = (float)((1.0 / viewPortWidthHeight) - (1.0 / imageWidthHeight)) / 2.0f;
                 //viewport is too tall
                 _bottom = _expectedBottom - compensation;
                 _top = _expectedTop + compensation;
                 _left = _expectedLeft;
                 _right = _expectedRight;
              }
           }
           else
           {
              _bottom = _expectedBottom;
              _top = _expectedTop;
              _left = _expectedLeft;
              _right = _expectedRight;
           }
  
  #if ANDROID || IOS
           if (_glVersion == OpenGLVersion.ES2)
           {
              //_zNear = -10.0f;
              ES20.GL.Viewport(0, 0, viewPortWidth, viewPortHeight);
              _projectionMatrix = OpenTK.Matrix4.Scale(1.0f / widthScale, 1.0f / heightScale, 1.0f);
              //_projectionMatrix = OpenTK.Matrix4.CreateOrthographicOffCenter(_left, _right, _bottom, _top, _zNear, _zFar);
              //_projectionMatrix = OpenTK.Matrix4.Frustum(_left, _right, _bottom, _top, _zNear, _zFar);
              //_projectionMatrix = OpenTK.Matrix4.Frustum(-1, 1, -1, 1, 3, 7);
  
              ES20.All glError = ES20.GL.GetError();
              if (glError != ES20.All.NoError)
              {
                 Report.Warn(PackageName, String.Format("Error after setup camera: {0}", glError));
              }
           }
           else
           {
              ES11.GL.Viewport(0, 0, viewPortWidth, viewPortHeight);
  
              ES11.All glError = ES11.GL.GetError();
              if (glError != ES11.All.NoError)
              {
                 Report.Warn(PackageName, String.Format("Error after setup camera: {0}", glError));
              }
           }
  
           if (_glVersion == OpenGLVersion.ES2)
           {
              ES20.All glError = ES20.GL.GetError();
              if (glError != ES20.All.NoError)
              {
                 Report.Warn(PackageName, String.Format("Error before clearing screen: {0}", glError));
              }
  
  #if DEBUG
              ES20.GL.ClearColor(1.0f, 0.0f, 0.0f, 1);
  #else
              ES20.GL.ClearColor(0.0f, 0.0f, 0.0f, 1);
  #endif       
              ES20.GL.Clear(
  #if IOS
              (int)
  #endif
  (ES20.ClearBufferMask.ColorBufferBit | ES20.ClearBufferMask.DepthBufferBit));
           }
           else //ES 1.1
           {
              ES11.All glError = ES11.GL.GetError();
              if (glError != ES11.All.NoError)
              {
                 Report.Warn(PackageName, String.Format("Error before clearing screen: {0}", glError));
              }
  #if DEBUG
              ES11.GL.ClearColor(1.0f, 0.0f, 0.0f, 1);
  #else
              ES11.GL.ClearColor(0.0f, 0.0f, 0.0f, 1);
  #endif
              ES11.GL.Clear(
  #if IOS
              (int)
  #endif
  (ES11.ClearBufferMask.ColorBufferBit | ES11.ClearBufferMask.DepthBufferBit));
              
              ES11.GL.MatrixMode(ES11.All.Projection);
              ES11.GL.LoadIdentity();
  
              //ES11.GL.Ortho(_expectedLeft, _expectedRight, _expectedBottom, _expectedTop, _zNear, _zFar);
              //ES11.GL.Scale(1.0f / widthScale, 1.0f / heightScale, 1.0f);
              _zNear = -10.0f;
              ES11.GL.Ortho(_left, _right, _bottom, _top, _zNear, _zFar);
           }
  #else
           if (_glVersion == OpenGLVersion.GL3)
           {
              //_zNear = -10.0f;
              GL.Viewport(0, 0, viewPortWidth, viewPortHeight);
              _projectionMatrix = OpenTK.Matrix4.Scale(1.0f / widthScale, 1.0f / heightScale, 1.0f);
              //_projectionMatrix = OpenTK.Matrix4.CreateOrthographicOffCenter(_left, _right, _bottom, _top, _zNear, _zFar);
              //_projectionMatrix = OpenTK.Matrix4.Frustum(_left, _right, _bottom, _top, _zNear, _zFar);
              //_projectionMatrix = OpenTK.Matrix4.Frustum(-1, 1, -1, 1, 3, 7);
              
  
  #if DEBUG
              GL.ClearColor(1.0f, 0.0f, 0.0f, 1);
  #else
              GL.ClearColor(0.0f, 0.0f, 0.0f, 1);
  #endif
              GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
           }
  #endif
        }
  
        private void RenderLines(float[] lines, float[] colors, float lineWidth)
        {
           if (lines == null)
              return;
  #if (ANDROID || IOS)
           if (_glVersion == OpenGLVersion.ES2)
           {
              ES20.GL.UseProgram(_lineRenderProgramHandle);
              // get handle to vertex shader's vPosition member
              int linePositionHandle = ES20.GL.GetAttribLocation(_lineRenderProgramHandle, new StringBuilder("vPosition"));
  
              // Enable a handle to the triangle vertices
              ES20.GL.EnableVertexAttribArray(linePositionHandle);
  
              // Prepare the line data
              ES20.GL.VertexAttribPointer(linePositionHandle, 3, ES20.All.Float, false, 0, lines);
  
              // get handle to fragment shader's vColor member
              int colorHandle = ES20.GL.GetAttribLocation(_lineRenderProgramHandle, new StringBuilder("vColor"));
              ES20.GL.EnableVertexAttribArray(colorHandle);
              ES20.GL.VertexAttribPointer(colorHandle, 4, ES20.All.Float, false, 0, colors);
  
              // get handle to shape's transformation matrix
              int lineMatrixHandle = ES20.GL.GetUniformLocation(_lineRenderProgramHandle, new StringBuilder("uMVPMatrix"));
  
              // Apply the projection and view transformation
              Matrix4 projectionMatrix = Matrix4.Mult(_projectionMatrix, Matrix4.CreateRotationZ(_rotation * _degreeToRadian));
              
              ES20.GL.UniformMatrix4(lineMatrixHandle, false, ref projectionMatrix);
              
  
              ES20.GL.LineWidth(lineWidth);
              ES20.GL.DrawArrays(ES20.All.Lines, 0, lines.Length / 3);
  
              ES20.All glError = ES20.GL.GetError();
              if (glError != ES20.All.NoError)
              {
                 Report.Warn(PackageName, String.Format("Error after drawing lines: {0}", glError));
              }
  
              ES20.GL.DisableVertexAttribArray(linePositionHandle);
              ES20.GL.DisableVertexAttribArray(colorHandle);
           }
           else //ES1.1
           {
              ES11.GL.Disable(ES11.All.Texture2D);
              ES11.GL.EnableClientState(ES11.All.VertexArray);
              ES11.GL.EnableClientState(ES11.All.ColorArray);
  
              ES11.GL.PushMatrix();
              ES11.GL.Rotate(_rotation, 0, 0, 1);
  
              //ES11.GL.LineWidth(lineWidth);
              ES11.GL.VertexPointer(3, ES11.All.Float, 0, lines);
              ES11.GL.ColorPointer(4, ES11.All.Float, 0, colors);
              ES11.GL.DrawArrays(ES11.All.Lines, 0, lines.Length / 3);
  
              ES11.GL.DisableClientState(ES11.All.VertexArray);
              ES11.GL.DisableClientState(ES11.All.ColorArray);
              //GL.Color4(0, 0, 0, 0);
  
              ES11.GL.PopMatrix();
  
              ES11.GL.Enable(ES11.All.Texture2D);
  
              ES11.All glError = ES11.GL.GetError();
              if (glError != ES11.All.NoError)
              {
                 Report.Warn(PackageName, String.Format("Error after drawing lines: {0}", glError));
              }
  
           }
  #else
           if (_glVersion == OpenGLVersion.GL3)
           {
              GL.UseProgram(_lineRenderProgramHandle);
              // get handle to vertex shader's vPosition member
              int linePositionHandle = GL.GetAttribLocation(_lineRenderProgramHandle, "vPosition");
  
              // Enable a handle to the triangle vertices
              GL.EnableVertexAttribArray(linePositionHandle);
  
              // Prepare the line data
              GL.VertexAttribPointer(linePositionHandle, 3, VertexAttribPointerType.Float, false, 0, lines);
  
              // get handle to fragment shader's vColor member
              int colorHandle = GL.GetAttribLocation(_lineRenderProgramHandle, "vColor");
              GL.EnableVertexAttribArray(colorHandle);
              GL.VertexAttribPointer(colorHandle, 4, VertexAttribPointerType.Float, false, 0, colors);
  
              // get handle to shape's transformation matrix
              int lineMatrixHandle = GL.GetUniformLocation(_lineRenderProgramHandle, "uMVPMatrix");
  
              // Apply the projection and view transformation
              Matrix4 projectionMatrix = Matrix4.Mult(_projectionMatrix, Matrix4.CreateRotationZ(_rotation * _degreeToRadian));
  
              GL.UniformMatrix4(lineMatrixHandle, false, ref projectionMatrix);
  
  
              GL.LineWidth(lineWidth);
              GL.DrawArrays(BeginMode.Lines, 0, lines.Length / 3);
  
              GL.DisableVertexAttribArray(linePositionHandle);
              GL.DisableVertexAttribArray(colorHandle);
           }
  #endif
  
        }
  
        private void RenderTextures()
        {
  #if ANDROID || IOS
           if (_glVersion == OpenGLVersion.ES2)
           {
              Matrix4 projectionMatrix = Matrix4.Mult(_projectionMatrix, Matrix4.CreateRotationZ(_rotation * _degreeToRadian));
  
              for (int i = 0; i <= 1; i++)
              {
                 if (_textureEnabled[i] && ES20.GL.IsTexture(_textureIds[i]))
                 {
                    if (_textureColor[i] == TextureColor.RGB || _textureColor[i] == TextureColor.RGBA)
                    {
                       ES20.GL.UseProgram(_textureRenderRGBAProgramHandle);
                    }
                    else if (_textureColor[i] == TextureColor.BGR || _textureColor[i] == TextureColor.BGRA)
                    {
                       ES20.GL.UseProgram(_textureRenderBGRAProgramHandle);
                    }
                    else
                    {
                       continue;
                    }
                    // get handle to vertex shader's vPosition member
                    int positionHandle = ES20.GL.GetAttribLocation(_textureRenderRGBAProgramHandle, new StringBuilder("vPosition"));
                    // Enable a handle to the rectangle vertices
                    ES20.GL.EnableVertexAttribArray(positionHandle);
  
                    int textureCoordHandle = ES20.GL.GetAttribLocation(_textureRenderRGBAProgramHandle, new StringBuilder("TexCoordIn"));
                    ES20.GL.EnableVertexAttribArray(textureCoordHandle);
  
                    // Prepare the rectangle coordinate data
                    ES20.GL.VertexAttribPointer(positionHandle, 3, ES20.All.Float, false, 0, _textureVertexCoords[i]);
  
                    // get handle to fragment shader's vColor member
                    //int colorHandle = GL.GetUniformLocation(_programHandle, new StringBuilder("vColor"));
  
                    // Set color with red, green, blue and alpha (opacity) values
                    //float[] color = new float[] { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };
  
                    // Set color for drawing the triangle
                    //GL.Uniform4(colorHandle, 1, color);
  
                    ES20.All glError = ES20.GL.GetError();
                    if (glError != ES20.All.NoError)
                    {
                       Report.Warn(PackageName, String.Format("Error after setting texture position: {0}", glError));
                    }
  
                    if (_textureFlipMode[i] == Emgu.CV.CvEnum.FLIP.None)
                    {
                       ES20.GL.VertexAttribPointer(textureCoordHandle, 2, ES20.All.Float, false, 0, squareTextureCoords);
                    }
                    else if (_textureFlipMode[i] == Emgu.CV.CvEnum.FLIP.HORIZONTAL)
                    {
                       ES20.GL.VertexAttribPointer(textureCoordHandle, 2, ES20.All.Float, false, 0, squareTextureCoordsFlipHorizontal);
                    }
                    else
                    {
                       throw new NotImplementedException(String.Format("Flip mode of {0} in GLImageView is not implemented", _textureFlipMode[i].ToString()));
                    }
  
                    glError = ES20.GL.GetError();
                    if (glError != ES20.All.NoError)
                    {
                       Report.Warn(PackageName, String.Format("Error after setting texture vertex: {0}", glError));
                    }
  
                    int textureHandle = ES20.GL.GetUniformLocation(_textureRenderRGBAProgramHandle, new StringBuilder("Texture"));
                    ES20.GL.ActiveTexture(ES20.All.Texture0);
                    ES20.GL.BindTexture(ES20.All.Texture2D, _textureIds[i]);
                    ES20.GL.Uniform1(textureHandle, 0);
  
                    glError = ES20.GL.GetError();
                    if (glError != ES20.All.NoError)
                    {
                       Report.Warn(PackageName, String.Format("Error after setting texture: {0}", glError));
                    }
  
                    // get handle to shape's transformation matrix
                    int MVPMatrixHandle = ES20.GL.GetUniformLocation(_textureRenderRGBAProgramHandle, new StringBuilder("uMVPMatrix"));
  
                    if (_textureRotations[i] != 0)
                    {
                       Matrix4 rotationMat = Matrix4.Mult(Matrix4.CreateRotationZ(_textureRotations[i] * _degreeToRadian), projectionMatrix);
                       ES20.GL.UniformMatrix4(MVPMatrixHandle, false, ref rotationMat);
                    }
                    else
                    {
                       // Apply the projection and view transformation
                       ES20.GL.UniformMatrix4(MVPMatrixHandle, false, ref projectionMatrix);
                    }
  
                    glError = ES20.GL.GetError();
                    if (glError != ES20.All.NoError)
                    {
                       Report.Warn(PackageName, String.Format("Error after setting transformation: {0}", glError));
                    }
  
                    ES20.GL.DrawArrays(ES20.All.TriangleFan, 0, 4);
  
                    glError = ES20.GL.GetError();
                    if (glError != ES20.All.NoError)
                    {
                       Report.Warn(PackageName, String.Format("Error after draw array: {0}", glError));
                    }
  
                    ES20.GL.DisableVertexAttribArray(positionHandle);
                    ES20.GL.DisableVertexAttribArray(textureCoordHandle);
                 }
              }
           }
           else //GLES 1.1
           {
              //ES11.GL.PushMatrix();
              //ES11.GL.Rotate(_rotation, 0, 0, 1);
  
              ES11.GL.EnableClientState(ES11.All.VertexArray);
              ES11.GL.EnableClientState(ES11.All.TextureCoordArray);
  
              for (int i = 0; i <= 1; i++)
              {
                 if (_textureEnabled[i] && ES11.GL.IsTexture(_textureIds[i]))
                 {
                    //if (_textureRotations[i] != 0)
                    {
                       ES11.GL.PushMatrix();
                       ES11.GL.Rotate(_rotation + _textureRotations[i], 0, 0, 1.0f);
                    }
                    ES11.GL.BindTexture(ES11.All.Texture2D, _textureIds[i]);
                    ES11.GL.VertexPointer(3, ES11.All.Float, 0, _textureVertexCoords[i]);
  
                    if (_textureFlipMode[i] == Emgu.CV.CvEnum.FLIP.None)
                    {
                       ES11.GL.TexCoordPointer(2, ES11.All.Float, 0, squareTextureCoords);
                    }
                    else if (_textureFlipMode[i] == Emgu.CV.CvEnum.FLIP.HORIZONTAL)
                    {
                       ES11.GL.TexCoordPointer(2, ES11.All.Float, 0, squareTextureCoordsFlipHorizontal);
                    }
                    else
                    {
                       throw new NotImplementedException(String.Format("Flip mode of {0} in GLImageView is not implemented", _textureFlipMode[i].ToString()));
                    }
  
                    ES11.GL.DrawArrays(ES11.All.TriangleFan, 0, 4);
                    //if (_textureRotations[i] != 0)
                    {
                       ES11.GL.PopMatrix();
                    }
                 }
              }
  
              ES11.GL.DisableClientState(ES11.All.VertexArray);
              ES11.GL.DisableClientState(ES11.All.TextureCoordArray);
              //ES11.GL.PopMatrix();
           }
  #else
           if (_glVersion == OpenGLVersion.GL3)
           {
              Matrix4 projectionMatrix = Matrix4.Mult(_projectionMatrix, Matrix4.CreateRotationZ(_rotation * _degreeToRadian));
  
              for (int i = 0; i <= 1; i++)
              {
                 if (_textureEnabled[i] && GL.IsTexture(_textureIds[i]))
                 {
                    if (_textureColor[i] == TextureColor.RGB || _textureColor[i] == TextureColor.RGBA)
                    {
                       GL.UseProgram(_textureRenderRGBAProgramHandle);
                    }
                    else if (_textureColor[i] == TextureColor.BGR || _textureColor[i] == TextureColor.BGRA)
                    {
                       GL.UseProgram(_textureRenderBGRAProgramHandle);
                    }
                    else
                    {
                       continue;
                    }
                    // get handle to vertex shader's vPosition member
                    int positionHandle = GL.GetAttribLocation(_textureRenderRGBAProgramHandle, "vPosition");
                    // Enable a handle to the rectangle vertices
                    GL.EnableVertexAttribArray(positionHandle);
  
                    int textureCoordHandle = GL.GetAttribLocation(_textureRenderRGBAProgramHandle, "TexCoordIn");
                    GL.EnableVertexAttribArray(textureCoordHandle);
  
                    // Prepare the rectangle coordinate data
                    GL.VertexAttribPointer(positionHandle, 3, VertexAttribPointerType.Float, false, 0, _textureVertexCoords[i]);
  
                    // get handle to fragment shader's vColor member
                    //int colorHandle = GL.GetUniformLocation(_programHandle, new StringBuilder("vColor"));
  
                    // Set color with red, green, blue and alpha (opacity) values
                    //float[] color = new float[] { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };
  
                    // Set color for drawing the triangle
                    //GL.Uniform4(colorHandle, 1, color);
  
  
gt:                   if (_textureFlipMode[i] == Emgu.CV.CvEnum.FlipType.None)