Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / multimedia / GDI

Reflected Image in C# with GDI and Unchecked Code

4.43/5 (3 votes)
11 Apr 2022CPOL 4.9K  
Creating a optionally sheared mirrored image from a source image in a collage
This tip shows how to make a fast reflected image from an original image with an optional shear factor and some blurring.

Sample result.

Code

The code uses unsafe code to speed up the creation of opacity gradient, so the code must be compiled with the unsafe flag enabled.

The code is mostly self explanatory.

C#
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;

namespace BitmapTools
{
  public static class BitmapExtensions
  {
    public struct Pixel
    {
      public byte B;
      public byte G;
      public byte R;
      public byte A;
    }

    /// <summary>
    /// Creates a bitmap with the original (bm) image on top and under that
    /// a reflected image. 
    /// </summary>
    /// <param name="bm">original image</param>
    /// <param name="verticalCompress">Percentage of height of the original image 
    /// that the reflection must be.</param>
    /// <param name="offsetAngle">Angle that the reflection must shear. 
    /// Positive to the left.</param>
    /// <param name="colorBlur">If true then the reflection get a little blur.</param>
    /// <returns></returns>
    public static Bitmap ReflectImage(Bitmap bm, float verticalCompress, 
                                      int offsetAngle, bool colorBlur = true)
    {
      // calculating the size of the resulting bitmap and the reflected image.
      int reflectionHeight = (int)(bm.Height - (bm.Height * verticalCompress / 100));
      int newwidth = bm.Width;
      int newheight = bm.Height + reflectionHeight;
      int offset;

      offset = (int)(Math.Sin(offsetAngle * Math.PI / 180) * reflectionHeight);
      newwidth = bm.Width + Math.Abs(offset);

      Bitmap resultBitmap = new Bitmap(newwidth, newheight, PixelFormat.Format32bppArgb);
      resultBitmap.SetResolution(bm.VerticalResolution, bm.HorizontalResolution);

      // create the bitmap with a positive of the original
      Bitmap reflectionSrc = 
             new Bitmap(bm.Width, reflectionHeight, PixelFormat.Format32bppArgb);
      using (Graphics g = Graphics.FromImage(reflectionSrc))
      {
        g.DrawImage(bm, new Rectangle(0, 0, bm.Width, reflectionHeight), 
                    0, 0, bm.Width, bm.Height, GraphicsUnit.Pixel);
      }

      // now flip the image to get a mirror image.
      reflectionSrc.RotateFlip(RotateFlipType.RotateNoneFlipY);

      // now fade to transparent. Can be done with managed code but too slow.
      // add conditionally add some blur.
      BitmapData bmd = reflectionSrc.LockBits(
          new Rectangle(Point.Empty, reflectionSrc.Size),
          ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);

      unsafe
      {
        unchecked
        {
          Pixel* ps = (Pixel*)bmd.Scan0.ToPointer();
          for (int y = 0; y < reflectionSrc.Height; y++)
          {
            int alpha = (int)((float)(1 - (y / (float)reflectionHeight)) * (float)0xFF);
            for (int x = 0; x < reflectionSrc.Width; x++, ps++)
            {
              ps->A = (byte)alpha;
              if (colorBlur)
              {
                ps->R = (byte)((ps->R * 3 + ps->G * 2 + ps->B * 2) / 7);
                ps->G = (byte)((ps->R * 2 + ps->G * 3 + ps->B * 2) / 7);
                ps->B = (byte)((ps->R * 2 + ps->G * 2 + ps->B * 3) / 7);
              }
            }
          }
        }
      }
      reflectionSrc.UnlockBits(bmd);

      // now create the canvas to draw the original and the flipped image with
      // the shear factor
      using (Graphics gr = Graphics.FromImage(resultBitmap))
      {
        // draw original on top of the result image
        gr.DrawImage(bm, offset <= 0 ? 0 : offset, 0, bm.Width, bm.Height);

        float shearFactor = (float)(offsetAngle * Math.PI / 180);

        if (offsetAngle != 0)
        {
          Matrix matrix = new Matrix();
          matrix.Shear(-shearFactor, 0);
          gr.Transform = matrix;
        }
        // remember shear translates coordinates like x' = x + (Sx * y) and y' = y + (Sy * x)
        // Sy = 0 in this case.

        gr.DrawImage(reflectionSrc,
          new Rectangle((offset <= 0 ? 0 : offset) + (int)(shearFactor * (float)bm.Height),
          bm.Height, newwidth, reflectionHeight),
          0, 0, newwidth, reflectionHeight,
          GraphicsUnit.Pixel);
        gr.ResetTransform();
      }
      reflectionSrc.Dispose();
      return resultBitmap;
    }
  }
}

How to Use the Code

Here is a little sample:

C#
private void Form1_Paint(object sender, PaintEventArgs e)
{
  Bitmap loadedImage = new Bitmap(@"Some Image.jpg");
  using (Bitmap bm = BitmapExtensions.ReflectImage(loadedImage, 50, 12, true))
  {
    e.Graphics.DrawImage(bm, 5, 5);
  }
}

One can save the resulting image with transparency in JPG, but officially JPG doesn't support transparency, so it is better to save in PNG.

C#
Bitmap loadedImage = new Bitmap(@"Some Image.jpg");
using (Bitmap bm = BitmapExtensions.ReflectImage(loadedImage, 50, 12, true))
{
  bm.Save(@"Some Image.png", ImageFormat.Png);
}

History

  • 11th April, 2022: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)