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.
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;
}
public static Bitmap ReflectImage(Bitmap bm, float verticalCompress,
int offsetAngle, bool colorBlur = true)
{
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);
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);
}
reflectionSrc.RotateFlip(RotateFlipType.RotateNoneFlipY);
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);
using (Graphics gr = Graphics.FromImage(resultBitmap))
{
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;
}
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:
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.
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
Started in 1988 programming in Pascal, making a 68000 emulator on a DOS platform. Then from Pascal to Clipper, to C++ and now using C#. I've been programming for all walks of businesses; opticians, opthomologist, carbage collectors, reinforcement steel producers, retail chains, and more.
I'm now travelling through Africa with a home-build expedition truck.