Image File Reflector
A simple utility to quickly create reflected versions of an image file (*.GIF, *.PNG, etc.)

Introduction
If you have an image file and need to create a reflection (left, right, above, or below) with a gradient fade out, this simple utility can help you. The C# code that does the work may help you too. If you want something more powerful, consider Gimp, which is free, very powerful, and comes with source code, but has a steep learning curve.
Background
I needed a simple utility to quickly and easily create a reflection of an image file and assumed someone had already done this. Indeed Fco. Xavier Marin had (see CodeProject ReflectionPicture
). But not being able to use it out of the box, I wrote this utility to meet my needs. It will convert a common image file which can then optionally be saved as a GIF, JPEG, PNG, TIF, etc. Others have saved me time with their submissions. This is a small submission to help offset some of my debt to the CodeProject community.
Credits
An excellent source of information on the issues related to manipulating Bitmaps can be found on BobPowell.net. There you will find detailed explanations of things like how and when to use functions like LockBits, so I will not repeat it here.
The files in this project's Images folder are the same as those in Fco. Xavier Marin ReflectionPicture
project.
Using the Code
If you have Visual Studio 2005, then you should be able to use the project source code "out of the box" -- simply build and run. It is reasonably documented. A side benefit of the code for Bitmap/Image newbies is it has simple examples of how to create a Bitmap, how to color it and change the alpha value of each pixel, etc. You can find the source code in VVX_ImageTools.cs, which is included in the project. You will undoubtedly find that you can build a better UI or write better code.
WARNING: Unsafe Code Ahead
Managed code can be used to manipulate Bitmaps. But it can be a little too slow. To get the job done a little faster, the code in VVX_ImageTools.cs uses "unsafe" code in two methods -- DoShowImageAndReflection(...)
and DoConvertToFormat32bppArgb(...)
-- with the code marked as unsafe{...}
as in the code fragment shown below.
For this code to compile in Visual Studio 2005, in the Project menu select Properties > Build and then check "Allow Unsafe Code".
Code for Converting a Bitmap to a Format32bppArgb Pixel Format
In order for gradient fade-out to work, lower resolution (i.e., less than Format32bppArgb
) files have to be upgraded. The code fragment below (found in VVX_ImageTools.cs) does the job, though you might have a better way to do it.
public bool DoConvertToFormat32bppArgb(Bitmap bmpSrc, ref Bitmap bmpTgt)
{
bool bRet = false;
try
{
PixelFormat pixFmtSrc = bmpSrc.PixelFormat;
PixelFormat pixFmtTgt = PixelFormat.Format32bppArgb;
if (pixFmtSrc != PixelFormat.Format32bppRgb)
{
int iWidth = bmpSrc.Width;
int iHeight = bmpSrc.Height;
// create a bitmap with the same size but desired format
bmpTgt = new Bitmap(bmpSrc.Width, bmpSrc.Height, pixFmtTgt);
//Ref: http://www.bobpowell.net/lockingbits.htm
// lock all the bits
Rectangle rc = new Rectangle(0, 0, iWidth, iHeight);
BitmapData bmdTgt = bmpTgt.LockBits(rc, ImageLockMode.ReadOnly, pixFmtTgt);
unsafe
{
//NOTE: For this to work in VS2005, in Project > Properties > Build
// you must check "Allow Unsafe Code"
//--- loop through each scan line and COPY each pixel from current
// format to desired format, e.g., Format32bppRgb
int PixelSize = 4;
for (int y = 0; y < bmdTgt.Height; y++)
{
byte* row = (byte*)bmdTgt.Scan0 + (y * bmdTgt.Stride);
for (int x = 0; x < bmdTgt.Width; x++)
{
//---get the color from the source
Color clr = bmpSrc.GetPixel(x, y);
//---set color attributes in the target
// Each of the 4 bytes of this 32-bit pixel must be set
int xRef = x * PixelSize;
row[xRef + 0] = clr.B;
row[xRef + 1] = clr.G;
row[xRef + 2] = clr.R;
if (mbMakeTransparent && (clr == this.mColorToMakeTransparent))
row[xRef + 3] = 0; // 0 is 100% transparent
else
row[xRef + 3] = 255; // 255 is 100% opaque
}
}
}
//now unlock all the bits we locked earlier
bmpTgt.UnlockBits(bmdTgt);
}
bRet = true;
}
catch (Exception ex)
{
this.msErrorMsg += this.msEOL + ex.ToString();
bRet = false;
}
return bRet;
}
Automatically Upgrades Low Resolution Images
If the source file opened is not 32-bits per pixel, i.e. ARGB compatible, the image is automatically "upgraded" using the method shown above and then saved with a modified extension. For example, if you open a 24-bit RGB image file, say test.bmp, then it will be saved as test.bmp.32bppArgb.png. The code that does this is shown below:
if (bmpReflection.PixelFormat != PixelFormat.Format32bppArgb)
{
DoConvertToFormat32bppArgb(bmpOriginal, ref bmpReflection);
if (true)
{
string sConvertedFile = msFile+".32bppArgb.png";
this.SaveAs(bmpReflection, sConvertedFile);
this.DoUpdateErrorMsg(sConvertedFile + " will work faster!");
}
bmpReflection.RotateFlip(this.menRotateFlipType);
}
This approach works best for my needs. However, if you don't like it, either change the 'true
' in the above code to 'false
' or do something that works best for you.
Code for Saving a Bitmap with Different File Types
public bool SaveBitmap(Bitmap bmp, string sOutputFilename)
{
bool bRet = false;
try
{
FileInfo fiOutputFile = new FileInfo(sOutputFilename);
//determine the image format from the file name
ImageFormat imgFmtWant = ImageFormat.Png;
switch (fiOutputFile.Extension.ToUpper())
{
case ".BMP" : imgFmtWant = ImageFormat.Bmp; break;
case ".EMF" : imgFmtWant = ImageFormat.Emf; break;
case ".EXF" :
case ".EXIF": imgFmtWant = ImageFormat.Exif; break;
case ".GIF" : imgFmtWant = ImageFormat.Gif; break;
case ".ICO" : imgFmtWant = ImageFormat.Icon; break;
case ".JPG" :
case ".JPEG": imgFmtWant = ImageFormat.Jpeg; break;
case ".PNG" : imgFmtWant = ImageFormat.Png; break;
case ".TIF" :
case ".TIFF": imgFmtWant = ImageFormat.Tiff; break;
case ".WMF" : imgFmtWant = ImageFormat.Wmf; break;
default: // none of the above, so add PNG to the name
sOutputFilename += ".png";
this.DoUpdateErrorMsg("WARNING: Output file name modified;
Extension '.png' added.");
break;
}
bmp.Save(sOutputFilename, imgFmtWant);
bRet = true;
}
catch (Exception ex)
{
this.msErrorMsg += this.msEOL + ex.ToString();
bRet = false;
}
return bRet;
}
History
- 6th March, 2007: Initial post