Data Browser - Viewing Site  Sector 23 Code Bank Logged in as:  Guest  




           


Image Dissolving in C# - Windows Forms
To dissolve an image, you can compute the Image returned from a function that takes a source image, a destination image, and an alpha value between 0 and 1 that sets the opacity of the images. Looping this over several opacities gives the illusion of 'dissolving'.

There are many samples on the internet that do just that with pointers in unsafe mode. This function is an extension of that work, but with several improvements:
1) It's in safe mode. You won't have to reset all your compilation settings to 'compile unsafe'.
2) Works with images of differeing sizes (assumes images are centered). If this isn't what you want, I explain below the simple changes needed to do something else. Centering the dissolved images looks way better than dissolving two images (of different sizes) that are left-top aligned.
3) When images are different sizes, you can specify the background color of your form for a clean dissolve into 'nothingness'.

Note that if the images are different sizes, you need to know the 'default background color' for which to color "empty space" that is generated from where one image exceeds the dimensions of another. For this function, you will need to set DefaultBackColor to an appropriate color (such as Colors.White, or the background of your form.
Also, if the images are different sizes, the output will differ depending on whether they are overlayed as 'centered' or 'left-top'. For example, if they are displayed in a PictureBox, this will be determined from the SizeMode. The code below computes the resulting image with the assumption that the two images are CENTERED atop one another. If they are left aligned, you will need to modify the code and set all of: xDestOffset, xSourceOffset, yDestOffset, and ySourceOffset to 0 (no offsets from top left). If the images are the same size, this of course doesn't matter.
Therefore, if you know the images are always the same sized, or you are left-aligning the images, or you don't care about background colors, this function can be trimmed to improve the speed.
But, for two images of different images centered in a picturebox/panel/etc with a certain Form Background Color, this function should work wonders just as it is.
Since this function works with an image of type Bitmap, certain image types will not 'dissolve' if they aren't compatable with simple bitmap data. In that case, the function returns null, which you should interpret as "go straight to the destination image". If you have images of type Image, you can always try (Bitmap)myImage and see if it works (simple .jpg images work fine for me when I do this).



///
/// Get dissolved image from source and destination.
///

/// source bitmap
/// destination bitmap
/// opacity
///
public Bitmap ImageDissolving(Bitmap bmpSource, Bitmap bmpDest, double alphaValue)
{
if(bmpSource == null)
bmpSource = new Bitmap(1, 1); // blank
if(bmpDest == null)
bmpDest = new Bitmap(1, 1); // blank
byte defaultR = DefaultBackColor.R; // color to use if no image data available at point
byte defaultG = DefaultBackColor.G;
byte defaultB = DefaultBackColor.B;
int srcWidth = bmpSource.Width; // compute now to save processing time
int srcHeight = bmpSource.Height;
int destWidth = bmpDest.Width;
int destHeight = bmpDest.Height;
int newWidth = Math.Max(srcWidth, destWidth);
int newHeight = Math.Max(srcHeight, destHeight);

// how much displaced from the left corner of the resulting image the image is (centered)
int xDestOffset = Math.Max(0, (srcWidth - destWidth)*3/2);
int xSourceOffset = Math.Max(0, (destWidth - srcWidth)*3/2);
int yDestOffset = Math.Max(0, (srcHeight - destHeight)/2);
int ySourceOffset = Math.Max(0, (destHeight - srcHeight)/2);

Bitmap bmpresult = null;
try
{
bmpresult = new Bitmap(newWidth, newHeight);
BitmapData data1 = bmpSource.LockBits(new Rectangle(0, 0, srcWidth, srcHeight), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
BitmapData data2 = bmpDest.LockBits(new Rectangle(0, 0, destWidth, destHeight), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
BitmapData data3 = bmpresult.LockBits(new Rectangle(0 , 0, newWidth, newHeight), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int remain1 = data1.Stride - data1.Width * 3;
int remain2 = data2.Stride - data2.Width * 3;
int remain3 = data3.Stride - data3.Width * 3;
int offset1 = 0; int offset2 = 0; int offset3 = 0;
byte ptr1 = 255; byte ptr2 = 255; byte ptr3 = 255;
int srcTotalWidth = srcWidth * 3; // r + b + g
int destTotalWidth = destWidth * 3; // r + b + g
int newTotalWidth = newWidth * 3; // r + b + g
// calculate outside of loop to help speed
int srcEndofXData = xSourceOffset + srcTotalWidth;
int srcEndofYData = srcHeight + ySourceOffset;
int destEndofXData = xDestOffset + destTotalWidth;
int destEndofYData = destHeight + yDestOffset;
int mod = 0; // temp. var. to calculate modulus
for(int y = 0; y < newHeight; y ++) //(i)
{ // y
for(int x = 0; x < newTotalWidth; x ++) // (j)
{ // x
if(x > = xSourceOffset && x < srcEndofXData && y > = ySourceOffset && y < srcEndofYData)
{
ptr1 = Marshal.ReadByte(data1.Scan0, offset1); // a valid point
offset1++;
}
else
{ // use default
mod = x%3;
switch(mod)
{
case 0:
ptr1=defaultR;
break;
case 1:
ptr1=defaultG;
break;
case 2:
ptr1=defaultB;
break;
}
}
if(x > = xDestOffset && x < destEndofXData && y > = yDestOffset && y < destEndofYData) // a valid point
{
ptr2 = Marshal.ReadByte(data2.Scan0, offset2);
offset2++;
}
else
{ // use default
mod = x%3;
switch(mod)
{
case 0:
ptr2=defaultR;
break;
case 1:
ptr2=defaultG;
break;
case 2:
ptr2=defaultB;
break;
}
}
ptr3 = Marshal.ReadByte(data3.Scan0, offset3); // resulting img
byte b = (byte)(alphaValue*ptr1+(1-alphaValue)*ptr2);
Marshal.WriteByte(data3.Scan0, offset3, b);
offset3++;
}
if(y > = ySourceOffset && y < srcEndofYData)
offset1 += remain1;
if(y > = yDestOffset && y < destEndofYData)
offset2 += remain2;
offset3 += remain3;
}
bmpSource.UnlockBits(data1);
bmpDest.UnlockBits(data2);
bmpresult.UnlockBits(data3);
}
catch
{ // Invalid bitmap format, or already locked. Ignore.
return bmpresult; // no 'dissolve'
}
return bmpresult;
} // dissolve image

Created By: amos 2/14/2006 9:54:40 PM