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