Friday, November 7, 2008

Transparency with GDI

I was working on a gem-matching game for Windows Mobile, which used .NET and GDI. I was having trouble finding a clear tutorial on implementing transparent images in GDI, and decided to write my own.

I discovered this solution by perusing the source of a game called "1945", and though I don't think my code much resembles that code, I still believe credit is due. Here is a link to where I found the game: http://www.codeproject.com/KB/mobile/CfPocket1945.aspx


/*
* SUMMARY
*
* Implementing Transparency in GDI
* Written by Adam Richards
* www.failcode.com
*
* This example illustrates how to create an image with transparency
* in a .NET GDI (Graphics Device Interface) object using C#.
*
* This was written in Visual Studio 2008 Professional, and uses the
* .NET Framework version 3.5.
*
*
* Copyright (c) 2008, Adam Richards
*
* TERMS OF USE:
* - Use at will
* - Use at your own risk
* - Please give me credit when used in a similar context
*
*/


using System;
using System.Drawing;
using System.Windows.Forms;

using System.Drawing.Imaging;
// ^^ This is needed for using the ImageAttributes structure.


namespace TransparencyInGDI
{
public partial class Form1 : Form
{
// Define a couple constants
const int ellipseDiameter = 200;
const int ellipseStrokeWidth = 25;

// Declare a few objects we'll need; they'll be explained as we use them.
Graphics graphicsObject = null;
Bitmap image = null;
ImageAttributes ImageAttributes_Transparency;
Rectangle destRect;
Color transparentColor = Color.FromArgb(255, 0, 255); // Pure Magenta

// A flag used to determine the render state
bool renderTransparently = false;


public Form1()
{
InitializeComponent();

// Initialize the main graphics object
graphicsObject = this.CreateGraphics();

// Define the destination rectangle
destRect = new Rectangle
(
(this.ClientRectangle.Width - ellipseDiameter) / 2, // Horizontal center
15, // 15 from the top
ellipseDiameter, // Width
ellipseDiameter // Height
);

// Create and populate the ImageAttribute struct with the transparent color
ImageAttributes_Transparency = new ImageAttributes();
ImageAttributes_Transparency.SetColorKey(transparentColor, transparentColor);

// Call the function that will create our demo image
CreateImage();
}

///
/// This function creates the image we'll be using to demonstrate our transparency.
/// Note that this image is only created once in the lifetime of this application.
///

public void CreateImage()
{
// Instantiate a new Bitmap object
image = new Bitmap
(
ellipseDiameter + ellipseStrokeWidth, // Width
ellipseDiameter + ellipseStrokeWidth // Height

// NOTE: When drawing a primitive, the stroke's width is centered
// on the shape's specified perimeter/circumference.
// For this reason we'll make our bitmap oversized by
// 2 * (strokeWidth / 2), which is (1 * strokeWidth).
);

// Create a graphics device from our new image and draw to it.
using (Graphics g = Graphics.FromImage(image))
{
g.Clear(transparentColor); // Clear the image with the transparent color.

g.DrawEllipse
(
new Pen(Color.Blue, (float)ellipseStrokeWidth),
ellipseStrokeWidth / 2,
ellipseStrokeWidth / 2,
ellipseDiameter,
ellipseDiameter

// NOTE: Per the NOTE above, we offset the render position of our
// ellipse by 1/2 of the stroke width, so that the very
// outside edges of our ellipse's stroke are drawn within
// the image's boundaries.
);
}
}

///
/// When the "Toggle Transparency" button in our form is clicked, toggle the
/// flag that specifies whether or not to draw the transparent regions, and
/// re-render the scene.
///

private void button1_Click(object sender, EventArgs e)
{
// Flip the transparency-rendering flag
renderTransparently = !renderTransparently;

// Re-render the image
RenderImage();
}

///
/// This function renders the image to the form. It is called when the
/// "Toggle Transparency" button (button1) is clicked.
///

private void RenderImage()
{
// Clear Form1's Graphics object.
graphicsObject.Clear(Color.White);

// Draw a green x before we blit out image so the transparency can be "seen" better...
Pen bgPen = new Pen(Color.Green, 10.0f);
graphicsObject.DrawLine(bgPen, destRect.X, destRect.Y, destRect.Right, destRect.Bottom);
graphicsObject.DrawLine(bgPen, destRect.X, destRect.Bottom, destRect.Right, destRect.Y);

if (!renderTransparently)
{
// This renders the image as created, without transparency,
// using a simple Graphics.DrawImage overload.
graphicsObject.DrawImage
(
image, // The image with the ellipse we created
destRect // The destination rectangle (where in the
// parent form to blit the image).
);
}
else
{
// This renders the image with an overload of the Graphics.DrawImage
// method, which takes our ImageAttributes structure as an argument.
// The ImageAttributes structure holds the value of the color that is
// to be "rendered" as transparent.
graphicsObject.DrawImage
(
image, // The image we created.
destRect, // Where to blit the image
0, 0, // The source rect's X and Y
image.Width, // The source rect's width...
image.Height, // ...and height
GraphicsUnit.Pixel, // The unit of measurement to use
ImageAttributes_Transparency // The ImageAttributes structure
// that we populated with information
// on which color to use as the
// transparent color.
);
}
}

///
/// This is called when the form is invalidated, and thus needs repainting.
///

private void Form1_Paint(object sender, PaintEventArgs e)
{
// The rest of the form repaints itself, just re-render our image...
RenderImage();
}
}
}


Coming once I have access to an open FTP port:
Download Project (Visual Studio C# 2008)

Tuesday, November 4, 2008

Invoking a Method through Reflection

I've heard about reflection before, and wanted to give it a shot.

My goal was to use a string to execute a method of a class, without having to use a comparison for each possible string value. The benefits to this are flexibility, and ease of scalability and maintenance.

If, for example, we're defining a menu system via XML, we can use a string to specify the name of a function to execute when a menu item is selected. Using reflection, we can avoid having to use a switch statement with a case for each possible menu selection; all we'll have to do is make sure an appropriate function exists, and add the name of that function as a textual property of a menu item element within our XML document.

Here is my test case for invoking a method with a string, in the form of a C# Console application.


using System;
using System.Reflection;

/*
*
* A simple demonstration of using reflection to invoke a method of an object
* using a string whose value matches the name of said method, without using
* explicit comparisons such as a case statement. (Breeeeathe!)
*
*/

/*
*
* Written by Adam Richards
* www.failcode.com
*
* Copyright 2008 Adam Richards
*
* TERMS OF USE:
* - Use at will
* - Use at your own risk
* - Please give me credit when used in a similar context
*
*/

namespace ReflectionTest1
{

// A class that provides some simple methods we'll be using soon...
public class TestClass
{
public void help()
{
Console.Write("You can execute a function by typing its name.\n\n");
Console.Write("Available function names are:\n help\n test1\n test2\n quit\n\n");
}
public void test1()
{
Console.Write("Hey cool, you executed function test1!\n\n");
}
public void test2()
{
Console.Write("Woot, function test2 got executed!\n\n");
}
public void quit()
{
Console.Write("Goodbye!\n\n");
Program.timeToQuit = true;
}
}

class Program
{
// A static quit flag--static so we can modify it from TestClass.quit()
public static bool timeToQuit = false;

static void Main(string[] args)
{
// Instantiate our test class
TestClass testClass = new TestClass();

// A string that we'll use to catch the users input, and to run any functions who share
// a name with this string's value. We'll start with the help function, so the user
// has a clue on what to do.
string funcName = "help";

while (true)
{
// Build our binding flags here to keep our reflection invocation clean
BindingFlags bindingFlags =
BindingFlags.DeclaredOnly |
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.InvokeMethod;

try
{
// And here is the reflection magic:
typeof(TestClass).InvokeMember(
funcName, // The string that holds the name of the function
bindingFlags, // The binding flags we built above...
null, // No binder needed since our func's aren't overloaded
testClass, // The instance of our class to call the method of
null); // No arguments need to be passed to our functions
}
catch (Exception ex)
{
// NOTE: Here we compare the exception type to see if the string did not
// match the name of a function. If it didn't, we'll alert the user
// of the situation. Otherwise, it's a different error, which I've
// not explicitly prepared for :) In this case, the only error that
// will throw this exception is the function not existing. In other
// cases, this exception can mean other things. Rework this for
// mission critical applications!

if (ex.GetType() == typeof(MissingMethodException))
Console.Write("Sorry, you have typed a function name that does not exist.\n\n");
else
Console.Write("\n\nAn \"unhandled\" error has occurred...\n\n");
}

// Check our quit flag, and return from Main() if it's been set to true
if (timeToQuit) return;

// If we're still here, read the next line of input from the user
funcName = Console.ReadLine();
}
}
}
}


Download project (Visual C# 2008)
MSDN Page on Reflection