context: using System;
  using System.Drawing;
  using System.Drawing.Imaging;
  
  namespace BaiRong.Core.Images
  {
      public class BitmapFilter
      {
          public const short EDGE_DETECT_KIRSH = 1;
          public const short EDGE_DETECT_PREWITT = 2;
          public const short EDGE_DETECT_SOBEL = 3;
  
          public static bool Invert(Bitmap b)
          {
              // GDI+ still lies to us - the return format is BGR, NOT RGB.
              var bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
  
              var stride = bmData.Stride;
              var Scan0 = bmData.Scan0;
  
              unsafe
              {
                  var p = (byte*)(void*)Scan0;
  
                  var nOffset = stride - b.Width * 3;
                  var nWidth = b.Width * 3;
  
                  for (var y = 0; y < b.Height; ++y)
                  {
                      for (var x = 0; x < nWidth; ++x)
                      {
                          p[0] = (byte)(255 - p[0]);
                          ++p;
                      }
                      p += nOffset;
                  }
              }
  
              b.UnlockBits(bmData);
  
              return true;
          }
  
          public static bool GrayScale(Bitmap b)
          {
              // GDI+ still lies to us - the return format is BGR, NOT RGB.
              var bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
  
              var stride = bmData.Stride;
              var Scan0 = bmData.Scan0;
  
              unsafe
              {
                  var p = (byte*)(void*)Scan0;
  
                  var nOffset = stride - b.Width * 3;
  
                  byte red, green, blue;
  
                  for (var y = 0; y < b.Height; ++y)
                  {
                      for (var x = 0; x < b.Width; ++x)
                      {
                          blue = p[0];
                          green = p[1];
                          red = p[2];
  
                          p[0] = p[1] = p[2] = (byte)(.299 * red + .587 * green + .114 * blue);
  
                          p += 3;
                      }
                      p += nOffset;
                  }
              }
  
              b.UnlockBits(bmData);
  
              return true;
          }
  
          public static bool Brightness(Bitmap b, int nBrightness)
          {
              if (nBrightness < -255 || nBrightness > 255)
                  return false;
  
              // GDI+ still lies to us - the return format is BGR, NOT RGB.
              var bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
  
              var stride = bmData.Stride;
              var Scan0 = bmData.Scan0;
  
              var nVal = 0;
  
              unsafe
              {
                  var p = (byte*)(void*)Scan0;
  
                  var nOffset = stride - b.Width * 3;
                  var nWidth = b.Width * 3;
  
                  for (var y = 0; y < b.Height; ++y)
                  {
                      for (var x = 0; x < nWidth; ++x)
                      {
                          nVal = (int)(p[0] + nBrightness);
  
                          if (nVal < 0) nVal = 0;
                          if (nVal > 255) nVal = 255;
  
                          p[0] = (byte)nVal;
  
                          ++p;
                      }
                      p += nOffset;
                  }
              }
  
              b.UnlockBits(bmData);
  
              return true;
          }
  
          public static bool Contrast(Bitmap b, sbyte nContrast)
          {
              if (nContrast < -100) return false;
              if (nContrast > 100) return false;
  
              double pixel = 0, contrast = (100.0 + nContrast) / 100.0;
  
              contrast *= contrast;
  
              int red, green, blue;
  
              // GDI+ still lies to us - the return format is BGR, NOT RGB.
              var bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
  
              var stride = bmData.Stride;
              var Scan0 = bmData.Scan0;
  
              unsafe
              {
                  var p = (byte*)(void*)Scan0;
  
                  var nOffset = stride - b.Width * 3;
  
                  for (var y = 0; y < b.Height; ++y)
                  {
                      for (var x = 0; x < b.Width; ++x)
                      {
                          blue = p[0];
                          green = p[1];
                          red = p[2];
  
                          pixel = red / 255.0;
                          pixel -= 0.5;
                          pixel *= contrast;
                          pixel += 0.5;
                          pixel *= 255;
                          if (pixel < 0) pixel = 0;
                          if (pixel > 255) pixel = 255;
                          p[2] = (byte)pixel;
  
                          pixel = green / 255.0;
                          pixel -= 0.5;
                          pixel *= contrast;
                          pixel += 0.5;
                          pixel *= 255;
                          if (pixel < 0) pixel = 0;
                          if (pixel > 255) pixel = 255;
                          p[1] = (byte)pixel;
  
                          pixel = blue / 255.0;
                          pixel -= 0.5;
                          pixel *= contrast;
                          pixel += 0.5;
                          pixel *= 255;
                          if (pixel < 0) pixel = 0;
                          if (pixel > 255) pixel = 255;
                          p[0] = (byte)pixel;
  
                          p += 3;
                      }
                      p += nOffset;
                  }
              }
  
              b.UnlockBits(bmData);
  
              return true;
          }
  
          public static bool Gamma(Bitmap b, double red, double green, double blue)
          {
              if (red < .2 || red > 5) return false;
              if (green < .2 || green > 5) return false;
              if (blue < .2 || blue > 5) return false;
  
              var redGamma = new byte[256];
              var greenGamma = new byte[256];
              var blueGamma = new byte[256];
  
              for (var i = 0; i < 256; ++i)
              {
                  redGamma[i] = (byte)Math.Min(255, (int)((255.0 * Math.Pow(i / 255.0, 1.0 / red)) + 0.5));
                  greenGamma[i] = (byte)Math.Min(255, (int)((255.0 * Math.Pow(i / 255.0, 1.0 / green)) + 0.5));
                  blueGamma[i] = (byte)Math.Min(255, (int)((255.0 * Math.Pow(i / 255.0, 1.0 / blue)) + 0.5));
              }
  
              // GDI+ still lies to us - the return format is BGR, NOT RGB.
              var bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
  
              var stride = bmData.Stride;
              var Scan0 = bmData.Scan0;
  
              unsafe
              {
                  var p = (byte*)(void*)Scan0;
  
                  var nOffset = stride - b.Width * 3;
  
                  for (var y = 0; y < b.Height; ++y)
                  {
                      for (var x = 0; x < b.Width; ++x)
                      {
                          p[2] = redGamma[p[2]];
                          p[1] = greenGamma[p[1]];
                          p[0] = blueGamma[p[0]];
  
                          p += 3;
                      }
                      p += nOffset;
                  }
              }
  
              b.UnlockBits(bmData);
  
              return true;
          }
  
          public static bool Color(Bitmap b, int red, int green, int blue)
          {
              if (red < -255 || red > 255) return false;
              if (green < -255 || green > 255) return false;
              if (blue < -255 || blue > 255) return false;
  
              // GDI+ still lies to us - the return format is BGR, NOT RGB.
              var bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
  
              var stride = bmData.Stride;
              var Scan0 = bmData.Scan0;
  
              unsafe
              {
                  var p = (byte*)(void*)Scan0;
  
                  var nOffset = stride - b.Width * 3;
                  int nPixel;
  
                  for (var y = 0; y < b.Height; ++y)
                  {
                      for (var x = 0; x < b.Width; ++x)
                      {
                          nPixel = p[2] + red;
                          nPixel = Math.Max(nPixel, 0);
                          p[2] = (byte)Math.Min(255, nPixel);
  
                          nPixel = p[1] + green;
                          nPixel = Math.Max(nPixel, 0);
                          p[1] = (byte)Math.Min(255, nPixel);
  
                          nPixel = p[0] + blue;
                          nPixel = Math.Max(nPixel, 0);
                          p[0] = (byte)Math.Min(255, nPixel);
  
                          p += 3;
                      }
                      p += nOffset;
                  }
              }
  
              b.UnlockBits(bmData);
  
              return true;
          }
  
          public static bool Conv3x3(Bitmap b, ConvMatrix m)
          {
              // Avoid divide by zero errors
              if (0 == m.Factor) return false;
  
              var bSrc = (Bitmap)b.Clone();
  
              // GDI+ still lies to us - the return format is BGR, NOT RGB.
              var bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
              var bmSrc = bSrc.LockBits(new Rectangle(0, 0, bSrc.Width, bSrc.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
  
              var stride = bmData.Stride;
              var stride2 = stride * 2;
              var Scan0 = bmData.Scan0;
              var SrcScan0 = bmSrc.Scan0;
  
              unsafe
              {
                  var p = (byte*)(void*)Scan0;
                  var pSrc = (byte*)(void*)SrcScan0;
  
                  var nOffset = stride - b.Width * 3;
                  var nWidth = b.Width - 2;
                  var nHeight = b.Height - 2;
  
                  int nPixel;
  
                  for (var y = 0; y < nHeight; ++y)
                  {
                      for (var x = 0; x < nWidth; ++x)
                      {
                          nPixel = ((((pSrc[2] * m.TopLeft) + (pSrc[5] * m.TopMid) + (pSrc[8] * m.TopRight) +
                              (pSrc[2 + stride] * m.MidLeft) + (pSrc[5 + stride] * m.Pixel) + (pSrc[8 + stride] * m.MidRight) +
                              (pSrc[2 + stride2] * m.BottomLeft) + (pSrc[5 + stride2] * m.BottomMid) + (pSrc[8 + stride2] * m.BottomRight)) / m.Factor) + m.Offset);
  
                          if (nPixel < 0) nPixel = 0;
                          if (nPixel > 255) nPixel = 255;
  
                          p[5 + stride] = (byte)nPixel;
  
                          nPixel = ((((pSrc[1] * m.TopLeft) + (pSrc[4] * m.TopMid) + (pSrc[7] * m.TopRight) +
                              (pSrc[1 + stride] * m.MidLeft) + (pSrc[4 + stride] * m.Pixel) + (pSrc[7 + stride] * m.MidRight) +
                              (pSrc[1 + stride2] * m.BottomLeft) + (pSrc[4 + stride2] * m.BottomMid) + (pSrc[7 + stride2] * m.BottomRight)) / m.Factor) + m.Offset);
  
                          if (nPixel < 0) nPixel = 0;
                          if (nPixel > 255) nPixel = 255;
  
                          p[4 + stride] = (byte)nPixel;
  
                          nPixel = ((((pSrc[0] * m.TopLeft) + (pSrc[3] * m.TopMid) + (pSrc[6] * m.TopRight) +
                              (pSrc[0 + stride] * m.MidLeft) + (pSrc[3 + stride] * m.Pixel) + (pSrc[6 + stride] * m.MidRight) +
                              (pSrc[0 + stride2] * m.BottomLeft) + (pSrc[3 + stride2] * m.BottomMid) + (pSrc[6 + stride2] * m.BottomRight)) / m.Factor) + m.Offset);
  
                          if (nPixel < 0) nPixel = 0;
                          if (nPixel > 255) nPixel = 255;
  
                          p[3 + stride] = (byte)nPixel;
  
                          p += 3;
                          pSrc += 3;
                      }
                      p += nOffset;
                      pSrc += nOffset;
                  }
              }
  
              b.UnlockBits(bmData);
              bSrc.UnlockBits(bmSrc);
  
              return true;
          }
  
          public static bool Smooth(Bitmap b, int nWeight /* default to 1 */)
          {
              var m = new ConvMatrix();
              m.SetAll(1);
              m.Pixel = nWeight;
              m.Factor = nWeight + 8;
  
              return Conv3x3(b, m);
          }
  
          public static bool GaussianBlur(Bitmap b, int nWeight /* default to 4*/)
          {
              var m = new ConvMatrix();
              m.SetAll(1);
              m.Pixel = nWeight;
              m.TopMid = m.MidLeft = m.MidRight = m.BottomMid = 2;
              m.Factor = nWeight + 12;
  
              return Conv3x3(b, m);
          }
          public static bool MeanRemoval(Bitmap b, int nWeight /* default to 9*/ )
          {
              var m = new ConvMatrix();
              m.SetAll(-1);
              m.Pixel = nWeight;
              m.Factor = nWeight - 8;
  
              return Conv3x3(b, m);
          }
          public static bool Sharpen(Bitmap b, int nWeight /* default to 11*/ )
          {
              var m = new ConvMatrix();
              m.SetAll(0);
              m.Pixel = nWeight;
              m.TopMid = m.MidLeft = m.MidRight = m.BottomMid = -2;
              m.Factor = nWeight - 8;
  
              return Conv3x3(b, m);
          }
          public static bool EmbossLaplacian(Bitmap b)
          {
              var m = new ConvMatrix();
              m.SetAll(-1);
              m.TopMid = m.MidLeft = m.MidRight = m.BottomMid = 0;
              m.Pixel = 4;
              m.Offset = 127;
  
              return Conv3x3(b, m);
          }
          public static bool EdgeDetectQuick(Bitmap b)
          {
              var m = new ConvMatrix();
              m.TopLeft = m.TopMid = m.TopRight = -1;
              m.MidLeft = m.Pixel = m.MidRight = 0;
              m.BottomLeft = m.BottomMid = m.BottomRight = 1;
  
              m.Offset = 127;
  
              return Conv3x3(b, m);
          }
  
          public static bool EdgeDetectConvolution(Bitmap b, short nType, byte nThreshold)
          {
              var m = new ConvMatrix();
  
              // I need to make a copy of this bitmap BEFORE I alter it 80)
              var bTemp = (Bitmap)b.Clone();
  
              switch (nType)
              {
                  case EDGE_DETECT_SOBEL:
                      m.SetAll(0);
                      m.TopLeft = m.BottomLeft = 1;
                      m.TopRight = m.BottomRight = -1;
                      m.MidLeft = 2;
                      m.MidRight = -2;
                      m.Offset = 0;
                      break;
                  case EDGE_DETECT_PREWITT:
                      m.SetAll(0);
                      m.TopLeft = m.MidLeft = m.BottomLeft = -1;
                      m.TopRight = m.MidRight = m.BottomRight = 1;
                      m.Offset = 0;
                      break;
                  case EDGE_DETECT_KIRSH:
                      m.SetAll(-3);
                      m.Pixel = 0;
                      m.TopLeft = m.MidLeft = m.BottomLeft = 5;
                      m.Offset = 0;
                      break;
              }
  
              Conv3x3(b, m);
  
              switch (nType)
              {
                  case EDGE_DETECT_SOBEL:
                      m.SetAll(0);
                      m.TopLeft = m.TopRight = 1;
                      m.BottomLeft = m.BottomRight = -1;
                      m.TopMid = 2;
                      m.BottomMid = -2;
                      m.Offset = 0;
                      break;
                  case EDGE_DETECT_PREWITT:
                      m.SetAll(0);
                      m.BottomLeft = m.BottomMid = m.BottomRight = -1;
                      m.TopLeft = m.TopMid = m.TopRight = 1;
                      m.Offset = 0;
                      break;
                  case EDGE_DETECT_KIRSH:
                      m.SetAll(-3);
                      m.Pixel = 0;
                      m.BottomLeft = m.BottomMid = m.BottomRight = 5;
                      m.Offset = 0;
                      break;
              }
  
              Conv3x3(bTemp, m);
  
              // GDI+ still lies to us - the return format is BGR, NOT RGB.
              var bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
              var bmData2 = bTemp.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
  
              var stride = bmData.Stride;
              var Scan0 = bmData.Scan0;
              var Scan02 = bmData2.Scan0;
  
              unsafe
              {
                  var p = (byte*)(void*)Scan0;
                  var p2 = (byte*)(void*)Scan02;
  
                  var nOffset = stride - b.Width * 3;
                  var nWidth = b.Width * 3;
  
                  var nPixel = 0;
  
                  for (var y = 0; y < b.Height; ++y)
                  {
                      for (var x = 0; x < nWidth; ++x)
                      {
                          nPixel = (int)Math.Sqrt((p[0] * p[0]) + (p2[0] * p2[0]));
                          if (nPixel < nThreshold) nPixel = nThreshold;
                          if (nPixel > 255) nPixel = 255;
                          p[0] = (byte)nPixel;
                          ++p;
                          ++p2;
                      }
                      p += nOffset;
                      p2 += nOffset;
                  }
              }
  
              b.UnlockBits(bmData);
              bTemp.UnlockBits(bmData2);
  
              return true;
          }
  
          public static bool EdgeDetectHorizontal(Bitmap b)
          {
              var bmTemp = (Bitmap)b.Clone();
  
              // GDI+ still lies to us - the return format is BGR, NOT RGB.
              var bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
              var bmData2 = bmTemp.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
  
              var stride = bmData.Stride;
              var Scan0 = bmData.Scan0;
              var Scan02 = bmData2.Scan0;
  
              unsafe
              {
                  var p = (byte*)(void*)Scan0;
                  var p2 = (byte*)(void*)Scan02;
  
                  var nOffset = stride - b.Width * 3;
                  var nWidth = b.Width * 3;
  
                  var nPixel = 0;
  
                  p += stride;
                  p2 += stride;
  
                  for (var y = 1; y < b.Height - 1; ++y)
                  {
                      p += 9;
                      p2 += 9;
  
                      for (var x = 9; x < nWidth - 9; ++x)
                      {
                          nPixel = ((p2 + stride - 9)[0] +
                              (p2 + stride - 6)[0] +
                              (p2 + stride - 3)[0] +
                              (p2 + stride)[0] +
                              (p2 + stride + 3)[0] +
                              (p2 + stride + 6)[0] +
                              (p2 + stride + 9)[0] -
                              (p2 - stride - 9)[0] -
                              (p2 - stride - 6)[0] -
                              (p2 - stride - 3)[0] -
                              (p2 - stride)[0] -
                              (p2 - stride + 3)[0] -
                              (p2 - stride + 6)[0] -
                              (p2 - stride + 9)[0]);
  
                          if (nPixel < 0) nPixel = 0;
                          if (nPixel > 255) nPixel = 255;
  
                          (p + stride)[0] = (byte)nPixel;
  
                          ++p;
                          ++p2;
                      }
  
                      p += 9 + nOffset;
                      p2 += 9 + nOffset;
                  }
              }
  
              b.UnlockBits(bmData);
              bmTemp.UnlockBits(bmData2);
  
              return true;
          }
  
          public static bool EdgeDetectVertical(Bitmap b)
          {
              var bmTemp = (Bitmap)b.Clone();
  
              // GDI+ still lies to us - the return format is BGR, NOT RGB.
              var bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
              var bmData2 = bmTemp.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
  
              var stride = bmData.Stride;
              var Scan0 = bmData.Scan0;
              var Scan02 = bmData2.Scan0;
  
              unsafe
              {
                  var p = (byte*)(void*)Scan0;
                  var p2 = (byte*)(void*)Scan02;
  
                  var nOffset = stride - b.Width * 3;
                  var nWidth = b.Width * 3;
  
                  var nPixel = 0;
  
                  var nStride2 = stride * 2;
                  var nStride3 = stride * 3;
  
                  p += nStride3;
                  p2 += nStride3;
  
                  for (var y = 3; y < b.Height - 3; ++y)
                  {
                      p += 3;
                      p2 += 3;
  
                      for (var x = 3; x < nWidth - 3; ++x)
                      {
                          nPixel = ((p2 + nStride3 + 3)[0] +
                              (p2 + nStride2 + 3)[0] +
                              (p2 + stride + 3)[0] +
                              (p2 + 3)[0] +
                              (p2 - stride + 3)[0] +
                              (p2 - nStride2 + 3)[0] +
                              (p2 - nStride3 + 3)[0] -
                              (p2 + nStride3 - 3)[0] -
                              (p2 + nStride2 - 3)[0] -
                              (p2 + stride - 3)[0] -
                              (p2 - 3)[0] -
                              (p2 - stride - 3)[0] -
                              (p2 - nStride2 - 3)[0] -
                              (p2 - nStride3 - 3)[0]);
  
                          if (nPixel < 0) nPixel = 0;
                          if (nPixel > 255) nPixel = 255;
  
                          p[0] = (byte)nPixel;
  
                          ++p;
                          ++p2;
                      }
  
                      p += 3 + nOffset;
                      p2 += 3 + nOffset;
                  }
              }
  
              b.UnlockBits(bmData);
              bmTemp.UnlockBits(bmData2);
  
              return true;
          }
  
          public static bool EdgeDetectHomogenity(Bitmap b, byte nThreshold)
          {
              // This one works by working out the greatest difference between a pixel and it's eight neighbours.
              // The threshold allows softer edges to be forced down to black, use 0 to negate it's effect.
              var b2 = (Bitmap)b.Clone();
  
              // GDI+ still lies to us - the return format is BGR, NOT RGB.
              var bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
              var bmData2 = b2.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
  
              var stride = bmData.Stride;
              var Scan0 = bmData.Scan0;
              var Scan02 = bmData2.Scan0;
  
              unsafe
              {
                  var p = (byte*)(void*)Scan0;
                  var p2 = (byte*)(void*)Scan02;
  
                  var nOffset = stride - b.Width * 3;
                  var nWidth = b.Width * 3;
  
                  int nPixel = 0, nPixelMax = 0;
  
                  p += stride;
                  p2 += stride;
  
                  for (var y = 1; y < b.Height - 1; ++y)
                  {
                      p += 3;
                      p2 += 3;
  
                      for (var x = 3; x < nWidth - 3; ++x)
                      {
                          nPixelMax = Math.Abs(p2[0] - (p2 + stride - 3)[0]);
                          nPixel = Math.Abs(p2[0] - (p2 + stride)[0]);
                          if (nPixel > nPixelMax) nPixelMax = nPixel;
  
                          nPixel = Math.Abs(p2[0] - (p2 + stride + 3)[0]);
                          if (nPixel > nPixelMax) nPixelMax = nPixel;
  
                          nPixel = Math.Abs(p2[0] - (p2 - stride)[0]);
                          if (nPixel > nPixelMax) nPixelMax = nPixel;
  
                          nPixel = Math.Abs(p2[0] - (p2 + stride)[0]);
                          if (nPixel > nPixelMax) nPixelMax = nPixel;
  
                          nPixel = Math.Abs(p2[0] - (p2 - stride - 3)[0]);
                          if (nPixel > nPixelMax) nPixelMax = nPixel;
  
                          nPixel = Math.Abs(p2[0] - (p2 - stride)[0]);
                          if (nPixel > nPixelMax) nPixelMax = nPixel;
  
                          nPixel = Math.Abs(p2[0] - (p2 - stride + 3)[0]);
                          if (nPixel > nPixelMax) nPixelMax = nPixel;
  
                          if (nPixelMax < nThreshold) nPixelMax = 0;
  
                          p[0] = (byte)nPixelMax;
  
                          ++p;
                          ++p2;
                      }
  
                      p += 3 + nOffset;
                      p2 += 3 + nOffset;
                  }
              }
  
              b.UnlockBits(bmData);
              b2.UnlockBits(bmData2);
  
              return true;
  
          }
          public static bool EdgeDetectDifference(Bitmap b, byte nThreshold)
          {
              // This one works by working out the greatest difference between a pixel and it's eight neighbours.
              // The threshold allows softer edges to be forced down to black, use 0 to negate it's effect.
              var b2 = (Bitmap)b.Clone();
  
              // GDI+ still lies to us - the return format is BGR, NOT RGB.
              var bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
              var bmData2 = b2.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
  
              var stride = bmData.Stride;
              var Scan0 = bmData.Scan0;
              var Scan02 = bmData2.Scan0;
  
              unsafe
              {
                  var p = (byte*)(void*)Scan0;
                  var p2 = (byte*)(void*)Scan02;
  
                  var nOffset = stride - b.Width * 3;
                  var nWidth = b.Width * 3;
  
                  int nPixel = 0, nPixelMax = 0;
  
                  p += stride;
                  p2 += stride;
  
                  for (var y = 1; y < b.Height - 1; ++y)
                  {
                      p += 3;
                      p2 += 3;
  
                      for (var x = 3; x < nWidth - 3; ++x)
                      {
                          nPixelMax = Math.Abs((p2 - stride + 3)[0] - (p2 + stride - 3)[0]);
                          nPixel = Math.Abs((p2 + stride + 3)[0] - (p2 - stride - 3)[0]);
                          if (nPixel > nPixelMax) nPixelMax = nPixel;
  
                          nPixel = Math.Abs((p2 - stride)[0] - (p2 + stride)[0]);
                          if (nPixel > nPixelMax) nPixelMax = nPixel;
  
                          nPixel = Math.Abs((p2 + 3)[0] - (p2 - 3)[0]);
                          if (nPixel > nPixelMax) nPixelMax = nPixel;
  
                          if (nPixelMax < nThreshold) nPixelMax = 0;
  
                          p[0] = (byte)nPixelMax;
  
                          ++p;
                          ++p2;
                      }
  
                      p += 3 + nOffset;
                      p2 += 3 + nOffset;
                  }
              }
  
              b.UnlockBits(bmData);
              b2.UnlockBits(bmData2);
  
              return true;
  
          }
  
          public static bool EdgeEnhance(Bitmap b, byte nThreshold)
          {
              // This one works by working out the greatest difference between a nPixel and it's eight neighbours.
              // The threshold allows softer edges to be forced down to black, use 0 to negate it's effect.
              var b2 = (Bitmap)b.Clone();
  
              // GDI+ still lies to us - the return format is BGR, NOT RGB.
              var bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
              var bmData2 = b2.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
  
              var stride = bmData.Stride;
              var Scan0 = bmData.Scan0;
              var Scan02 = bmData2.Scan0;
  
              unsafe
              {
                  var p = (byte*)(void*)Scan0;
                  var p2 = (byte*)(void*)Scan02;
  
                  var nOffset = stride - b.Width * 3;
                  var nWidth = b.Width * 3;
  
                  int nPixel = 0, nPixelMax = 0;
  
                  p += stride;
                  p2 += stride;
  
                  for (var y = 1; y < b.Height - 1; ++y)
                  {
                      p += 3;
                      p2 += 3;
  
                      for (var x = 3; x < nWidth - 3; ++x)
                      {
                          nPixelMax = Math.Abs((p2 - stride + 3)[0] - (p2 + stride - 3)[0]);
  
                          nPixel = Math.Abs((p2 + stride + 3)[0] - (p2 - stride - 3)[0]);
  
                          if (nPixel > nPixelMax) nPixelMax = nPixel;
  
                          nPixel = Math.Abs((p2 - stride)[0] - (p2 + stride)[0]);
  
                          if (nPixel > nPixelMax) nPixelMax = nPixel;
  
                          nPixel = Math.Abs((p2 + 3)[0] - (p2 - 3)[0]);
  
                          if (nPixel > nPixelMax) nPixelMax = nPixel;
  
                          if (nPixelMax > nThreshold && nPixelMax > p[0])
                              p[0] = (byte)Math.Max(p[0], nPixelMax);
  
                          ++p;
                          ++p2;
                      }
  
                      p += nOffset + 3;
                      p2 += nOffset + 3;
                  }
              }
  
              b.UnlockBits(bmData);
              b2.UnlockBits(bmData2);
  
              return true;
          }
          public static Bitmap Resize(Bitmap b, int nWidth, int nHeight, bool bBilinear)
          {
              var bTemp = (Bitmap)b.Clone();
              b = new Bitmap(nWidth, nHeight, bTemp.PixelFormat);
  
              var nXFactor = (double)bTemp.Width / (double)nWidth;
              var nYFactor = (double)bTemp.Height / (double)nHeight;
  
              if (bBilinear)
              {
                  double fraction_x, fraction_y, one_minus_x, one_minus_y;
                  int ceil_x, ceil_y, floor_x, floor_y;
                  var c1 = new Color();
                  var c2 = new Color();
                  var c3 = new Color();
                  var c4 = new Color();
                  byte red, green, blue;
  
                  byte b1, b2;
  
                  for (var x = 0; x < b.Width; ++x)
                      for (var y = 0; y < b.Height; ++y)
                      {
                          // Setup
  
                          floor_x = (int)Math.Floor(x * nXFactor);
                          floor_y = (int)Math.Floor(y * nYFactor);
                          ceil_x = floor_x + 1;
                          if (ceil_x >= bTemp.Width) ceil_x = floor_x;
                          ceil_y = floor_y + 1;
                          if (ceil_y >= bTemp.Height) ceil_y = floor_y;
                          fraction_x = x * nXFactor - floor_x;
                          fraction_y = y * nYFactor - floor_y;
                          one_minus_x = 1.0 - fraction_x;
                          one_minus_y = 1.0 - fraction_y;
  
                          c1 = bTemp.GetPixel(floor_x, floor_y);
                          c2 = bTemp.GetPixel(ceil_x, floor_y);
                          c3 = bTemp.GetPixel(floor_x, ceil_y);
                          c4 = bTemp.GetPixel(ceil_x, ceil_y);
  
                          // Blue
                          b1 = (byte)(one_minus_x * c1.B + fraction_x * c2.B);
  
                          b2 = (byte)(one_minus_x * c3.B + fraction_x * c4.B);
  
                          blue = (byte)(one_minus_y * (double)(b1) + fraction_y * (double)(b2));
  
                          // Green
                          b1 = (byte)(one_minus_x * c1.G + fraction_x * c2.G);
  
                          b2 = (byte)(one_minus_x * c3.G + fraction_x * c4.G);
  
                          green = (byte)(one_minus_y * (double)(b1) + fraction_y * (double)(b2));
  
                          // Red
                          b1 = (byte)(one_minus_x * c1.R + fraction_x * c2.R);
  
                          b2 = (byte)(one_minus_x * c3.R + fraction_x * c4.R);
  
                          red = (byte)(one_minus_y * (double)(b1) + fraction_y * (double)(b2));
  
                          b.SetPixel(x, y, System.Drawing.Color.FromArgb(255, red, green, blue));
                      }
              }
              else
              {
                  for (var x = 0; x < b.Width; ++x)
                      for (var y = 0; y < b.Height; ++y)
                          b.SetPixel(x, y, bTemp.GetPixel((int)(Math.Floor(x * nXFactor)), (int)(Math.Floor(y * nYFactor))));
              }
  
              return b;
          }
  
          public static bool OffsetFilterAbs(Bitmap b, Point[,] offset)
          {
              var bSrc = (Bitmap)b.Clone();
  
              // GDI+ still lies to us - the return format is BGR, NOT RGB.
              var bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
              var bmSrc = bSrc.LockBits(new Rectangle(0, 0, bSrc.Width, bSrc.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
  
              var scanline = bmData.Stride;
  
              var Scan0 = bmData.Scan0;
              var SrcScan0 = bmSrc.Scan0;
  
              unsafe
              {
                  var p = (byte*)(void*)Scan0;
                  var pSrc = (byte*)(void*)SrcScan0;
  
                  var nOffset = bmData.Stride - b.Width * 3;
                  var nWidth = b.Width;
                  var nHeight = b.Height;
  
                  int xOffset, yOffset;
  
                  for (var y = 0; y < nHeight; ++y)
                  {
                      for (var x = 0; x < nWidth; ++x)
                      {
                          xOffset = offset[x, y].X;
                          yOffset = offset[x, y].Y;
  
                          if (yOffset >= 0 && yOffset < nHeight && xOffset >= 0 && xOffset < nWidth)
                          {
                              p[0] = pSrc[(yOffset * scanline) + (xOffset * 3)];
                              p[1] = pSrc[(yOffset * scanline) + (xOffset * 3) + 1];
                              p[2] = pSrc[(yOffset * scanline) + (xOffset * 3) + 2];
                          }
  
                          p += 3;
                      }
                      p += nOffset;
                  }
              }
  
              b.UnlockBits(bmData);
              bSrc.UnlockBits(bmSrc);
  
              return true;
          }
  
          public static bool OffsetFilter(Bitmap b, Point[,] offset)
          {
              var bSrc = (Bitmap)b.Clone();
  
              // GDI+ still lies to us - the return format is BGR, NOT RGB.
              var bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
              var bmSrc = bSrc.LockBits(new Rectangle(0, 0, bSrc.Width, bSrc.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
  
              var scanline = bmData.Stride;
  
              var Scan0 = bmData.Scan0;
              var SrcScan0 = bmSrc.Scan0;
  
              unsafe
              {
                  var p = (byte*)(void*)Scan0;
                  var pSrc = (byte*)(void*)SrcScan0;
  
                  var nOffset = bmData.Stride - b.Width * 3;
                  var nWidth = b.Width;
                  var nHeight = b.Height;
  
                  int xOffset, yOffset;
  
                  for (var y = 0; y < nHeight; ++y)
                  {
                      for (var x = 0; x < nWidth; ++x)
                      {
                          xOffset = offset[x, y].X;
                          yOffset = offset[x, y].Y;
  
                          if (y + yOffset >= 0 && y + yOffset < nHeight && x + xOffset >= 0 && x + xOffset < nWidth)
                          {
                              p[0] = pSrc[((y + yOffset) * scanline) + ((x + xOffset) * 3)];
                              p[1] = pSrc[((y + yOffset) * scanline) + ((x + xOffset) * 3) + 1];
                              p[2] = pSrc[((y + yOffset) * scanline) + ((x + xOffset) * 3) + 2];
                          }
  
                          p += 3;
                      }
                      p += nOffset;
                  }
              }
  
              b.UnlockBits(bmData);
              bSrc.UnlockBits(bmSrc);
  
              return true;
          }
  
          public static bool OffsetFilterAntiAlias(Bitmap b, FloatPoint[,] fp)
          {
              var bSrc = (Bitmap)b.Clone();
  
              // GDI+ still lies to us - the return format is BGR, NOT RGB.
              var bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
              var bmSrc = bSrc.LockBits(new Rectangle(0, 0, bSrc.Width, bSrc.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
  
              var scanline = bmData.Stride;
  
              var Scan0 = bmData.Scan0;
              var SrcScan0 = bmSrc.Scan0;
  
              unsafe
              {
                  var p = (byte*)(void*)Scan0;
                  var pSrc = (byte*)(void*)SrcScan0;
  
                  var nOffset = bmData.Stride - b.Width * 3;
                  var nWidth = b.Width;
                  var nHeight = b.Height;
  
                  double xOffset, yOffset;
  
                  double fraction_x, fraction_y, one_minus_x, one_minus_y;
                  int ceil_x, ceil_y, floor_x, floor_y;
                  Byte p1, p2;
  
                  for (var y = 0; y < nHeight; ++y)
                  {
                      for (var x = 0; x < nWidth; ++x)
                      {
                          xOffset = fp[x, y].X;
                          yOffset = fp[x, y].Y;
  
                          // Setup
  
                          floor_x = (int)Math.Floor(xOffset);
                          floor_y = (int)Math.Floor(yOffset);
                          ceil_x = floor_x + 1;
                          ceil_y = floor_y + 1;
                          fraction_x = xOffset - floor_x;
                          fraction_y = yOffset - floor_y;
                          one_minus_x = 1.0 - fraction_x;
                          one_minus_y = 1.0 - fraction_y;
  
                          if (floor_y >= 0 && ceil_y < nHeight && floor_x >= 0 && ceil_x < nWidth)
                          {
                              // Blue
  
                              p1 = (Byte)(one_minus_x * (double)(pSrc[floor_y * scanline + floor_x * 3]) +
                                  fraction_x * (double)(pSrc[floor_y * scanline + ceil_x * 3]));
  
                              p2 = (Byte)(one_minus_x * (double)(pSrc[ceil_y * scanline + floor_x * 3]) +
                                  fraction_x * (double)(pSrc[ceil_y * scanline + 3 * ceil_x]));
  
                              p[x * 3 + y * scanline] = (Byte)(one_minus_y * (double)(p1) + fraction_y * (double)(p2));
  
                              // Green
  
                              p1 = (Byte)(one_minus_x * (double)(pSrc[floor_y * scanline + floor_x * 3 + 1]) +
                                  fraction_x * (double)(pSrc[floor_y * scanline + ceil_x * 3 + 1]));
  
                              p2 = (Byte)(one_minus_x * (double)(pSrc[ceil_y * scanline + floor_x * 3 + 1]) +
                                  fraction_x * (double)(pSrc[ceil_y * scanline + 3 * ceil_x + 1]));
  
                              p[x * 3 + y * scanline + 1] = (Byte)(one_minus_y * (double)(p1) + fraction_y * (double)(p2));
  
                              // Red
  
                              p1 = (Byte)(one_minus_x * (double)(pSrc[floor_y * scanline + floor_x * 3 + 2]) +
                                  fraction_x * (double)(pSrc[floor_y * scanline + ceil_x * 3 + 2]));
  
                              p2 = (Byte)(one_minus_x * (double)(pSrc[ceil_y * scanline + floor_x * 3 + 2]) +
                                  fraction_x * (double)(pSrc[ceil_y * scanline + 3 * ceil_x + 2]));
  
                              p[x * 3 + y * scanline + 2] = (Byte)(one_minus_y * (double)(p1) + fraction_y * (double)(p2));
                          }
                      }
                  }
              }
  
              b.UnlockBits(bmData);
              bSrc.UnlockBits(bmSrc);
  
              return true;
          }
  
          public static Bitmap Flip(Bitmap b, bool bHorz, bool bVert)
          {
              var ptFlip = new Point[b.Width, b.Height];
  
              var nWidth = b.Width;
              var nHeight = b.Height;
  
              for (var x = 0; x < nWidth; ++x)
                  for (var y = 0; y < nHeight; ++y)
                  {
                      ptFlip[x, y].X = (bHorz) ? nWidth - (x + 1) : x;
                      ptFlip[x, y].Y = (bVert) ? nHeight - (y + 1) : y;
                  }
  
              OffsetFilterAbs(b, ptFlip);
  
              return b;
          }
  
          public static Bitmap RotateLeftAndFlipVert(Bitmap b)
          {
              var bTemp = (Bitmap)b.Clone();
              b = new Bitmap(bTemp.Height, bTemp.Width, bTemp.PixelFormat);
  
              for (var x = 0; x < b.Width; ++x)
                  for (var y = 0; y < b.Height; ++y)
                      b.SetPixel(x, y, bTemp.GetPixel(y, x));
  
              return b;
          }
  
          public static Bitmap RotateLeft(Bitmap b)
          {
              b = RotateLeftAndFlipVert(b);
              Flip(b, false, true);
              return b;
          }
  
          public static Bitmap RotateRight(Bitmap b)
          {
              b = RotateLeftAndFlipVert(b);
  
gt:             Flip(b, true, false);