Saturday, 28 March 2009

Speeding up GetPixel with Unsafe Code, Part II

In relation to my previous article on replacing GetPixel, I didn't include an example of how to replace SetPixel. So if you haven't figured it out, here's an example that will colour every pixel in a bitmap red, which should point you in the right direction.

private static void SetPixelExample(ref Bitmap bitmap)
{
int CurScanLine = 0;
Rectangle Rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
BitmapData TempBitmapData =
bitmap.LockBits(Rect, ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);

while (CurScanLine < bitmap.Height)
{
for (int x = 0; x < bitmap.Width; x++)
{
SetPixelNew(ref TempBitmapData, x, CurScanLine, Color.Red);
}
CurScanLine++;
}

bitmap.UnlockBits(TempBitmapData);
}

private static unsafe void SetPixelNew(ref BitmapData bitmapData, int x, int y, Color colour)
{
byte* PixelPointer = null; // Pointer to our pixel data
int ColourPlanes = 3;
PixelPointer = (byte*)bitmapData.Scan0 + (y * bitmapData.Stride);
PixelPointer[ColourPlanes * x] = colour.B;
PixelPointer[(ColourPlanes * x) + 1] = colour.G;
PixelPointer[(ColourPlanes * x) + 2] = colour.R;
}

I've used a similar technique to write a speedy PCX image file decoder which decodes the PCX file, places it in a byte array then writes the colour data to a bitmap, which is one possible application to consider.

Labels:

Bookmark and Share

Tuesday, 24 March 2009

Speeding up GetPixel with Unsafe Code

Although GDI+ provides a method, GetPixel, for getting the colour of a particular pixel at a particular set of co-ordinates, you'll quickly discover how slow it is when you come to examine every pixel in even an average sized bitmap.

This is due to
GetPixel locking, then unlocking the bitmap into system memory every time GetPixel is called. The process of locking and unlocking the bitmap itself is what takes the time.

However, with the aid of a little unsafe code, you can easily speed this up. For those unfamiliar with the unsafe keyword, in C#, putting the keyword unsafe before a method generally means it uses pointers. By default, the Visual Studio IDE will not let you compile unsafe code. You'll need to either use the /unsafe compiler switch if compiling from the command line, or tick the "Allow unsafe code" checkbox in the project properties if you're using the Visual Studio IDE:




So why the need for unsafe code?

Instead of using
GetPixel to get the colour of each pixel, we'll lock the bitmap into system memory, which will then give us direct access to the bitmap data via the BitmapData class and the use of a byte pointer. Only when we're finished with the data will we unlock the data, thus saving us potentially thousands of locking and unlocking operations.

To illustrate the speed difference I've written a small program which makes use of two functions. One returns a list of Colors obtained by using the existing .NET
GetPixel method. The second returns a list of Colors via unsafe code.

The speed of the two functions is also measured and displayed crudely.


private void TestGetPixelMethods()
{
Bitmap B = (Bitmap)Bitmap.FromFile(@"C:\MyImage.jpg");

// Execute and Time Managed Code
DateTime StartTime = DateTime.Now;
GetAllPixelsSafe(B);
DateTime FinishTime = DateTime.Now;
TimeSpan Duration = FinishTime - StartTime;
Debug.WriteLine(string.Format("GetAllPixelsSafe : {0} ", Duration));

// Execute and Time unsafe code
StartTime = DateTime.Now;
GetAllPixelsUnsafe(B);
FinishTime = DateTime.Now;
Duration = FinishTime - StartTime;
Debug.WriteLine(string.Format("GetAllPixelsUnsafe: {0} ", Duration));
Debug.WriteLine(string.Empty);
}

/// <summary>
/// Returns a list of Colors representing each pixel in the passed image
/// </summary>
private List<Color> GetAllPixelsSafe(Bitmap b)
{
List<Color> ColorList = new List<Color>();

for (int x = 0; x < b.Width; x++)
{
for (int y = 0; y < b.Height; y++)
{
ColorList.Add(b.GetPixel(x, y));
}
}

return ColorList;
}

/// <summary>
/// Returns a list of Colors representing each pixel in the passed image
/// Uses unsafe code
/// </summary>
private unsafe List<Color> GetAllPixelsUnsafe(Bitmap b)
{
Rectangle Rect = new Rectangle(0, 0, b.Width, b.Height);
BitmapData TempBitmapData =
b.LockBits(Rect, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);

byte* PixelPointer = null; // Pointer to our pixel data
int ColorPlanes = 3; // 3 Color Planes in a 24bpp image
List<Color> ColorList = new List<Color>();

for (int x = 0; x < b.Width; x++)
{
for (int y = 0; y < b.Height; y++)
{
PixelPointer = (byte*)TempBitmapData.Scan0 + y * TempBitmapData.Stride + ColorPlanes * x;
ColorList.Add(Color.FromArgb(PixelPointer[2], PixelPointer[1], PixelPointer[0]));
}
}

b.UnlockBits(TempBitmapData);

return ColorList;
}

When
TestGetPixelMethods is called, the two pixel reading methods are called in turn, and the length of time elapsed for each is output to the debug console.

As this is just an example, note that
GetAllPixelsUnsafe assumes that a 24bpp image is passed. If a 32bpp image were to be passed, PixelFormat.Format32bppArgb would be used and the integer ColorPlanes would need to be set to 4, as a 32bpp image also contains an alpha channel.

Here is the output to my debug console:



Although the measurements are crude, notice that in most cases the method that uses the .NET GetPixel function is taking 0.266s. The method that is using unsafe code is taking 0.0625s. That's about 4.25 times faster, and well worth taking the trouble to implement with image manipulation code that works at the per-pixel level.

Of course, a similar function could be written to replace
SetPixel, which, armed with the above knowledge, you should now be able to implement yourself!

kick it on DotNetKicks.com

Labels:

Bookmark and Share

Monday, 23 March 2009

Who Clicks on the Epic Fail?

I recently had this epic fail of a spam-mail in my inbox:



Quite frankly, I couldn't think of anything less hot than this, but I suppose there's no accounting for taste.

This got my thinking - although I find this particular case laughable, if people didn't make money from spam mail, they wouldn't send it. So who clicks on it all?

I'll tell you. My family members. My mother. My grandmother. Other perfectly intelligent but non tech-savvy people who don't know and shouldn't have to know any better. But what can we do? Not much it would seem, other than to drive home the message that they should be deleted without clicking on them.

I can't explain to my grandmother why she is getting e-mails for certain enlargement products any more than she can explain to me the attraction of Bridge, because it would go in one ear and out the other, and as anyone who does family technical support knows - that's a very awkward conversation.

What to do?

Labels:

Bookmark and Share

Saturday, 21 March 2009

Repeater Control Words of Wisdom

Non-mental note to self.

When using the .NET Repeater Control in which there is a child CheckBox or RadioButton present to allow for data items to be selected, ensure that
autopostback = true for the child control you are using.

If
autopostback is not true, the value of the Checked property will always be false when the RadioButton or Checkbox has been selected and you come to use FindControls in order to determine the state of your child control:
protected void Button1_Click(object sender, EventArgs e)
{
bool CheckedValue;

foreach (RepeaterItem Item in Repeater1.Items)
{
CheckBox CB = (CheckBox)Item.FindControl("MyCheckBox");
CheckedValue = CB.Checked;
}
}
Also ensure that string passed into the "FindControl" function matches the name of the control you are looking for. Not that I'd be foolish enough to make such an elementary typo, of course.

Labels: ,

Bookmark and Share

Thursday, 19 March 2009

Naming Iterators

During a recent peer code review, a for loop caught my eye. It caught my eye because it dawned on me that despite having used variable naming conventions for years, just about every for loop I had used defied the naming convention, and no-one was batting an eyelid.

for (int i = 0; i < WordArray.Length; i++ )
{
WordArray[i] = WordArray[i].Trim();
}

I'm talking about the iterator i, of course. Effectively a local variable, with a meaningless name and according to my convention, starting with the wrong case.

But i is traditional in a for loop, isn't it? It's kind of comforting. As programmers, we just know that i is an iterator. Along with, sometimes, j. But how far should we let this go? Are j and k acceptable?

I think you might make a case for j, if it's really clear what the iterator relates to. Beyond that, things can quickly become confusing.

So here's the new rule we came up with for iterator naming convention:

If there's just one iterator, you can use
i, but if there's more than one nested iterator, give the iterator a meaningful name.

I still love
i, and now we can still be together!

Labels:

Bookmark and Share

Wednesday, 18 March 2009

Edge for iPhone

No, I'm not talking about the 2.5G mobile technology. Nor will I mention that we're getting copy and paste at last! Oh dear, sorry about that.

No, I want to talk about another shiny gem of an iPhone game called
Edge, that I discovered over the weekend. Sadly, no screenshot I can take does it justice, so I strongly recommend you check it out on YouTube.

Retro graphics, a simple concept and a varied 8-bit inspired soundtrack combine to provide something really quite engaging.

Guide your cube around numerous cubic landscapes, each one more fiendish than the last. When your cube reaches the goal tile, based on how many times you dropped off the world, the number of smaller cubes you managed to collect and the time taken, you'll get a rating, which as far as I can tell is between A and D.

Very simple. Insidiously addictive.

Labels: , ,

Bookmark and Share

Monday, 16 March 2009

Using ConfigurationManager to Read the Configuration File in C#

I've seen this done in so many convoluted ways, yet it's very, very simple and only requires one line of actual code. There's a rather lengthy example on MSDN that doesn't help to clarify how easy it is to read a value out of the configuration file and 95% of the time, that's all you want to do.

Firstly, add app.config to your project. (Obviously, if you were developing a web application, you'd be using web.config)

Create an <appsettings> section, and then add the key value pairs that you want to store within it, for example:


<configuration>
<appsettings>
<add key="MagicNumber" value="3">
</add>
</appsettings>
</configuration>

Add a reference to System.Configuration into your project, and add "using System.Configuration" at the top of your code. You can then write the code:


void GetAppSettings()
{
int MagicNumber =
Convert.ToInt32(ConfigurationManager.AppSettings["MagicNumber"]);
}

You'd probably want to add exception handling for a missing or malformed settings file, but that is quite literally, it.

Still people ask, but as they have a habit of saying on Who Wants to be a Millionaire - it's only easy if you know the answer...

Labels: ,

Bookmark and Share

Friday, 13 March 2009

Will Visual Studio run on an Asus Eee 901 PC?

In a word, yes.

I've had an Asus Eee 901 PC for about six months now, and I love it as a web browsing platform, especially when combined with slickness of Google Chrome. But often, I'll want to tinker with a C# idea in the evening without slinking off to the study like some kind of social pariah. This got me thinking about whether the Eee could handle Visual Studio.




I opted for Visual Studio 2005 Express rather than 2008, and declined to install both MSDN and SQL Server Express. Installation itself is a task for the patient, as the Solid State Drive is not the fastest thing.

But once installed, it's very usable indeed. I wouldn't like to spend a lot of time writing code on a 1024x600 screen, but overall I think I can declare it a success - I was half-expecting it to wilt like a tulip in a themonuclear explosion.

It has also served to highlight how much I now rely on the new C# 3.0 syntax on a daily basis, though. Maybe I'll try and shoehorn 2008 on to the poor thing soon.

Labels: ,

Bookmark and Share

Thursday, 12 March 2009

Microsoft Research and ILMerge

Before we start, I'd like to draw your attention to the Microsoft Research website. There's some pretty interesting stuff available to download if you have a dig around.

ILMerge is one such research project. One of the less grandiose ones, no doubt, but it works as advertised. It's a tool which can combine multiple .NET assemblies into a single .NET assembly. You can download it from Microsoft, here.

And the Microsoft Research Website for the project is here.

I've come to the conclusion that ILMerge appeals to my nature because back when games used to be played in DOS, you'd typically have an EXE and maybe a data file or two. And that was it! Ah, such elegant simplicity.

ILMerge is a command line utility, but there is a CodePlex project which puts a GUI on the front if you have an aversion to the command prompt. I can't vouch for the UI, it's written by another third party and I haven't tried it. Besides, who doesn't love command prompts?

There are four basic pieces of information you'll need to provide to ILMerge:
  1. The directory to search for input assemblies. Use /lib:<directory>
  2. The target assembly type. For an exe, use /target:winexe
  3. The output assembly. Use /out:<assemblyname>
  4. The Primary and secondary assemblies. These get listed at the end of the command line, with the primary assembly first.
So, to merge an application which consists of hello.exe and world.dll, which reside in the directory C:\HelloWorld, into a new assembly called helloworld.exe the command line would be:

ilmerge /lib:"C:\HelloWorld" /target:winexe /out :helloworld.exe hello.exe world.dll

We then end up with a shiny new helloworld.exe which can be used in place of both input assemblies. You can verify that the assemblies have been merged by opening up the output file in ILDASM, or your intermediate language disassembler of choice, where you should see all the component parts of your input assemblies.

Labels: ,

Bookmark and Share

Tuesday, 10 March 2009

Obfuscating a String with XOR in C#

Related to my recent posts on conversion and bitwise operators, I'm going to talk a little bit about obfuscating a string with XOR as a practical example of both string to byte conversion, and the bitwise XOR operator.

Although this could also be considered encryption in some schools of thought, on its own this is so weak as an encryption technique I prefer to think of it as obfuscation.

XOR obfuscation does have its place. It can be used to easily thwart simple plain text searches, for example. If you have hardcoded strings in an assembly, you could hardcode obfuscated strings instead, so that the plain text string value does not show up in a search. You could also apply obfuscation before subsequently performing a secure encryption method, thus making it harder for a determined hacker to determine whether or not a valid decryption has been performed - since the decrypted string will still be obfuscated.

But by itself, think of it as one of those magic pens that writes in ink that is only visible under ultraviolet light. Enough to protect data from a casual user or passer-by, but absolutely useless in protecting information from someone that knows what they're trying to do and has some degree of competency.

Here's a function that will obfuscate a string then:


private string XORString(string inputString, byte key)
{
ASCIIEncoding Encoder = new ASCIIEncoding();
byte[] ByteArray = Encoder.GetBytes(inputString);
for (int i = 0; i < ByteArray.Length; i++)
{
ByteArray[i] ^= key;
}

return Encoder.GetString(ByteArray);
}

Pretty simple. Convert our input string into a byte array, then XOR each byte in the array with a key value, and return the reconstructed string.

And here's why you shouldn't rely on this technique to protect anything you want to keep secret:


private void BruteForceXORString(string inputString)
{
ASCIIEncoding Encoder = new ASCIIEncoding();

byte XORVal = 0;
byte[] ByteArray;

while (XORVal < 255)
{
ByteArray = Encoder.GetBytes(inputString);

for (int i = 0; i < ByteArray.Length; i++)
{
ByteArray[i] ^= XORVal;
}

Debug.WriteLine(string.Format(
"{0}: {1}",
XORVal,
Encoder.GetString(ByteArray)));

XORVal++;
}
}

In next to no time, this function will output every possible decoding of an obfuscated string which has been XOR'd with a byte value. Say I'd obfuscated the string "Visual Studio" with an XOR value of 16:


As you can see, there is the original string plain to see after very little effort.

Use with caution, and always ask yourself, "Am I happy hiding this information with a virtual magic pen?"

Labels: ,

Bookmark and Share

Monday, 9 March 2009

Time Out: Ancient Frog

I happened to stumble upon a rather excellent iPhone app called Ancient Frog at the weekend. I thought it was so excellent, I'm sharing it with you.


The premise? Help a variety of flexible frogs get the fly by guiding their limbs, for which there are limited spots to place them, across various surfaces. Not only is it an original and entertaining product, it's developed by a one man band.

I have a bit of a soft spot for Independent Game developers. Previously, I worked on both Thievery and the first incarnation of Alien Swarm, and I'm currently working on an as yet unannounced XBox 360 community game. It can be a hard slog, especially if you're trying to fit in a life and a day job.

Want to support Indie Game devs? This is definitely one to buy if you've got an iPhone or an iTouch.

Labels: , ,

Bookmark and Share

Friday, 6 March 2009

Friday Afternoon Code

I like my cars, and I was only searching for the price of a second hand Aston Martin DB9 out of interest, honest.


Can't remember which website this was, but they really need to fix it. I wonder if that old adage for cars:

You never want one that was built on a Friday afternoon.

...fits for software applications too?

Labels:

Bookmark and Share

Thursday, 5 March 2009

C# Bitwise Operators

It's essential to master the logical bitwise operators if you're working with the individual bits of a byte on a regular basis. In the real world, it's especially useful when interfacing with industrial hardware (hardware engineers love digital I/O), working with flags, or if you're writing an emulator, in which case it's what you'll spend a lot of time doing. So here's a quick reference.

Note that you wouldn't usually create a seperate function just to perform a logical bitwise operation, I've done it in the examples below for clarity.

To test the value of a bit, use the Logical AND operator. In the following code, the bit parameter is the bit(s) to check, and the mask parameter is the value in which to test the set bit.

private bool TestBit(ushort bit, uint mask)
{
// AND
return (mask & bit) == bit ? true : false;
}

i.e. to check if bit 4 was set in a uint called MyValue:

bool Result = TestBit(8, MyValue);

To set the value of a bit, use the Logical OR operator.

private void SetBit(ushort bit, ref uint mask)
{
// OR
mask = (mask | bit);
}

Note that this could also be reduced to the following by using the bitwise OR compound assignment operator:

private void SetBit(ushort bit, ref uint mask)
{
// OR
mask |= bit;
}

To toggle bits between zero and one, use eXclusive OR, also known as XOR. Exculsive OR looks at the two values to be XOR'd together bit by bit and asks, "Is only one of these bits set in each value?" If so, the resultant bit is set. Otherwise, it is cleared.

To clarify, consider the following:

Decimal 7 = Binary 0111
Decimal 2 = Binary 0010
Decimal 5 = Binary 0101

7 XOR 2 = 5

You should be able to see why from looking at the above binary representation. Here's how we'd do it in C#:


private void ToggleBits(uint key, ref uint value)
{
// XOR parameters together
value ^= key;
}

private void PerformOp()
{
uint Value = 7;
uint Key = 2;
ToggleBits(Key, ref Value);
}

Shifting Bits


To shift bits left and right, you can use the << (shift left) or >> (shift right) operators. As with all the other logical bitwise operators, you can also combine these with an equals sign to get a compound shift operator, e.g. <<= Shifting bits to the left has the effect of multiplying your original value by 2. Consider: Decimal 7 = Binary 00111 Shifting the bits left by 1 bit gives us 01110, which is, as expected, 7 multiplied by 2, which is 14. Shifting left again gives us 11100, which is 28, or 14 multiplied by 2. In C#:

private void ShiftLeft(ref uint value, ushort numberOfBitsToShift)
{
// Shift bits left
value <<= numberOfBitsToShift;
}

If you shift a set bit outside the range of your variable, the value is lost. For example, if I shift a whole byte set to 255 to the left, I end up with 254:

11111111 << 1 = 11111110

As shifting bits is less computationally expensive than multiplying values together, historically bit shifting is a common technique employed in assembly language when something has to run as fast as possible. Graphics rendering, for example.


Shifting right has a similar effect, but divides a number by 2. I won't post code for shifting right, I'm sure you get the idea by now. If not, you're in trouble!

I should probably also cover One's and Two's Complement. I'll do that in Part II.

Labels: ,

Bookmark and Share

Wednesday, 4 March 2009

Don't Unplug Your iPhone When Synching

I know, I know. I didn't do it on purpose.

I've previously unplugged my iPhone whilst it was synchronising and gotten away with it, but this time I was not so lucky. The 7.51GB of music and media I had on it have been turned into
Other.


It then complained that it couldn't synchronise my media because there wasn't space. No there isn't. That's because it's already there.

Restore didn't help either, I was still left with Other. As you can't delete other in any way, I now have to do a full reset on my iPhone and then resynchronise it, which will take it at least 2 hours.

Can I afford to have my iPhone off for 2 hours? Probably. Do I want to? No. Do I still love it? Yes. Maybe I'm becoming addicted. It's easily done.

Labels:

Bookmark and Share

Tuesday, 3 March 2009

Google Charts API

Hands up who knew about the Google Charts API? I didn't until recently. And this is coming from someone who knows a thing or two about charts.

What is it then? It's a web based charting API with which you can create a custom chart by simply forming a valid url.

For example, the following url:


http://chart.apis.google.com/chart?cht=p3&chd=t:50,40,10&chs=290x100&chl=Hamsters|Mice|Tigers

Gives us this:




Other than to say it's not limited to just pie charts, I won't go into further detail since you can read all about it in the documentation for yourself. If you haven't seen it before, it's well worth a look.

Labels: ,

Bookmark and Share

Monday, 2 March 2009

Crystal Reports - Cry Havoc and Let Slip the Dogs of War

Ladies and Gentlemen, my candidate for Most Hated Software Technology of the past seven years, the razzies of the software world: Crystal Reports.

I'm sure Shakespeare didn't have Crystal Reports in mind when he was writing Julius Caesar, but in my experience, the phrase perfectly sums up the chaos that ensues when I've had dealings with it in the past.

First up, which flavour would you like? Amongst others, there's 8, 8.5, VS.NET, 9, X and XI. Why the switch to Roman numerals when 10 was reached? I've no idea. It's unlikely you'll find a person who is ever sure which version they have, either. But you can guarantee it's not the version you thought or needed them to have.

Deploying reports in a browser. Great idea in principle, no need to install the runtime on client machines. Which viewer would you like? The vanilla one which looks slightly amateurish, the Java one or the ActiveX one? Just one that works reliably, please. Nothing is ever going to work for all of the people, all of the time, but I'd settle for all of the people some of the time. Not half the people, sometimes.

Moving on, there's the old "a little knowledge is a dangerous thing" chestnut to contend with. And by this, I mean generally non-technical users who have been on a one day training course to display some fields using a SELECT statement. These people subsequently insist that they know about all that database "stuff" and can't comprehend why you would need to go digging around in data cubes and have been doing so for the past week.

Lastly, and this is my biggest pet hate, it's the obscure error messages, and the mind boggling size of the support database. Sure, there's a lot of knowledge in there. There's so much, it's like digging for a needle in a haystack. Why is all this necessary? Why are there so many issues with this software that the knowledge base is bulging at the seams? It's not SAP.

I'm not one to bear a grudge. Maybe it's better these days, maybe some people have nothing but great experiences with it. I'll give it the benefit of the doubt. But for me, there is one thing it's great for. When I've had a long day I take comfort in the fact that I don't work with Crystal any more.

Labels:

Bookmark and Share

Sunday, 1 March 2009

Second Impression of Quake Live

Following on from my first impression of Quake Live, where I did some hardcore waiting, I've now had the chance to play it properly.


Rather than have you sit, drumming your fingers, whilst approximately 260Mb of data downloads into your browser, a staged downloading process is taken. A small core of files is downloaded, then whilst you're playing an initial match against a bot that determines your skill level, the rest of the game continues to download seamlessly in the background and places itself in your Application Data folder:

\Documents and Settings\[user]\Application Data\id software\quakelive

As a technical aside, I'm guessing the format of the PK3 files has changed as they can no longer be opened with WinZip. Anyway, moving on. Once your skill level has been determined, matches at a suitable skill level are chosen for you and highlighted as "best picks":




There's nothing to stop you joining a match with a higher or lower skill ranking than your own, mind you. As with Quake III, there is a myriad of different player models and colours to choose from, and lots of graphic settings to tinker with. Switch the game into full-screen mode and you wouldn't even know you're playing, effectively, in a browser.

With one exception, every game I've played has been smooth and non-laggy. As for the gameplay itself, well, it's pretty much Quake III Arena. Just via your browser. And this is a good thing. Free-For-All, Team DM, InstaGib and Capture The Flag are all present and correct.

It's worth checking what gametype you're playing before you start shooting though. I embarrassingly mistook a Team Deathmatch for a Free For All, and many team mates gave their lives needlessley before I realised all the abuse regarding team kills was directed at me. Sorry, Blue Team.

Currently, match stats are disabled whilst stability issues are sorted, but once they're enabled I'd expect to see the average length of the queue increase again, much to my chagrin.

Pleasingly though, it's a queue worth waiting to get to the front of. Fire up your browser, go make a cup of tea, come back ten minutes later and enjoy.

Just one more gripe. Epic, where the hell is my Unreal Tournament '99 Live?

Labels:

Bookmark and Share