Archive for March, 2008

Rotate, Flip, and Resize Images in .NET

Often times if you’re writing a web application that deals with user uploaded photographs and other images, a nice feature set to have is one that allows users to do basic manipulations on their photos, such as rotating, flipping, and resizing (the last being very important for download friendly images). Luckily, the .NET framework makes such functionality easy to implement.

Read the Image

Once the image has been uploaded and saved into a directory somewhere accessible by the web server, the first step common to all the procedures is to take the image data inside of the file and get it into memory usable by .NET. We do this by loading the image into a System.Drawing.Bitmap object. One of the constructors of Bitmap class can take the filename of an image, thereby automatically decoding the format and reading in the data into the Bitmap object. .NET supports the most common image formats, such as JPEG, GIF, BMP, and PNG.

        Dim PhotoBMP As Bitmap

        PhotoBMP = New Bitmap(“myfile.jpg”)

Above we have statically specified a filename, but you may, of course, dynamically pass in the filename saved on the webserver.

Flips and Rotations

Reorienting the image is incredibly easy. The Bitmap class contains a method called “RotateFlip” that does what you would think – rotates and/or flips. The RotateFlip method takes a single argument, one of RotateFlipType type. Intellisense will enumerate all the options, but each combination of rotation and flip is included: 90, 180, 270 degrees, and flips across X, Y or both axes.

        PhotoBMP.RotateFlip(RotateFlipType.Rotate270FlipNone)

Above we rotate our image clockwise 270 degrees (i.e. counter-clockwise 90 degrees). We do not perform a flip.

        PhotoBMP.RotateFlip(RotateFlipType.RotateNoneFlipY)

Above we flip our image across the Y axis but do not perform a rotation. You may also combine both flips and rotations simultaneously if desired.

Saving (and Converting) the Image

At this point, our reorientation has only been performed upon the bitmap data we have in memory, our file is still untouched. We can save our changes back to the original file, or to a new file if desired. The Bitmap class contains an easy to use Save method.

        PhotoBMP.Save(“myfile.jpg”)

Save is overloaded in a number of ways, but you may also specify a filetype as the second argument. In our example, if we wanted to convert our jpg to a png:

        PhotoBMP.Save(“myfile.png”,ImageFormat.Png)

Very easy as can be seen, no need to lookup file formats and encodings. You may also want to read in the current extension to direct what actions are taken. I use the following regular expression to parse the ending extension of the file.

        Regex.Match(FileName, ".[^.]+$")

Resizing Images

Resizing images is a little more difficult, but not too bad once you know the procedure. You start by loading bitmap data in the same fashion we did for rotating and flipping. Assuming you want to keep the image in proportion when changing the size, as well as resize with a maximum resolution in mind, we’ll need to calculate the aspect ratio of the image and our conversion coefficient. This may sound complicated, but fear not.

Calculate Aspect Ratio

There are a few methods of doing the math, but in general we know the maximum dimension we want. E.g. 1024×768, 800×600, 640×480, etc. We calculate the aspect ratio of our maximum desired size by dividing the width by the height. In these examples it is 1.333.

Then we must calculate the aspect ratio of the current image. We can obtain the width and height of our bitmap with the width and height properties of the Bitmap class. We know if we have a greater aspect ratio than our target max size’s aspect ratio, then our width is larger and is the confining dimension. If our aspect ratio is less, our height is larger and is the confining dimension. Once we know our confining dimension, to get the conversion coefficient, we divide our current image confining dimension size by the target confining dimension size.

E.g. If we’re converting 2048×1800 to a maximum of 1024×768, we calculate our target image aspect ratio, which we already know is 1.333. Then we calculate our original image aspect ratio, which is 1.138. This is less than 1.333, so we know height is our confining dimension. So we divide 1800 by 768 to get our conversion coefficient, or 2.344. Now we can calculate our target width size by dividing our current width by 2.344, which is 874. Our new image will by 874×768.

Confused? It’s not really as bad as it sounds. First you’re figuring out which dimension will match the largest dimension of your target size, then you’re decreasing the other dimension by the same amount. Here it is in code.

        OrigAspectRatio = PhotoBMP.Width / PhotoBMP.Height

        TargetAspectRatio = 1024 / 768

        If OrigAspectRatio < TargetAspectRatio
   	     ‘Height is confining
	     NewHeight = 768
	     NewWidth = PhotoBMP.Width / (PhotoBMP.Height / 768)
        Else
	     ‘Width is confining
	     NewWidth = 1024
  	     NewHeight = PhotoBMP.Height / (PhotoBMP.Width / 1024)
        End If

Perform the Resize

Now we have our target width and height via good old fashioned math, now the technical bits for .NET to perform its magic. .NET can perform resizing via the Graphics class, by resizing data from the original bitmap data into a new, smaller bitmap. First we make a new, blank Bitmap of the correct new size.

        NewBMP = New Bitmap(newWidth, newHeight)

Then we create a new Graphics image using the reference of this new Bitmap object.

        ConvertGraphics = Graphics.FromImage(NewBMP)

Then we specify the method of scaling the image. Different methods work better for different amounts of scaling, but High Quality Bicubic is the one I tend to use.

        ConvertGraphics.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic

Now comes the actual conversion, we use the DrawImage method to draw our original bitmap into the new bitmap at the new size (at the starting coordinates of 0,0 in the new bitmap

        ConvertGraphics.DrawImage(PhotoBMP, 0, 0, NewBMP.Width, NewBMP.Height)

Dispose our old bitmap

        PhotoBMP.Dispose()

Save our new

        NewBMP.Save(“new.jpg”, ImageFormat.Jpeg)

And dispose

        NewBMP.Dispose()

And we’re done! You can also use the Graphics object to perform a number of paint-oriented operations on your image, such as lines and drawing text, but that you can have fun experimenting with.

With the exception of resizing which requires a few calculations, .NET makes image manipulation exteremly easy, and when combined into a web application, offers users some useful tools at virtually no cost to you.

Dynamically Truncate SQL Parameters in .NET

[SQL Server] String or Binary data would be truncated.

Run into this error before? The cause is usually fairly clear, we’re attempting to insert or update a field in a table with more data than it can hold. E.g. we’ve executed a SQL insert statement that attempts to assign a string 100 characters in length to a varchar field that can hold 80 characters max. Microsoft SQL Server complains, and we receive a nice error in our .NET application.

Solution 1: Don’t send too much data

This is an easy solution (and not the purpose of the article) and not always possible, but it is completely valid and often times desirable if you are writing an application that works with a SQL database in a static manner where the fields you’re updating are known ahead of time. There are a number of ways to ensure you don’t exceed the maximum size of a field.

If your data is coming from a form item, such as a text box, ensure the maximum size of the text box doesn’t exceed the maximum size of the field in the database. If you’re working with items that cannot be limited, simply take a substring of the data and manually truncate it to a size allowed by the field in the database. Many times we know what data and what sizes we’re working with at design time.

Solution 2: Read field sizes from database and dynamically truncate data

There are many times when solution 1 is not an option, mostly in situations where the database we’re working with is unknown, whether partially or fully, at design time. I’ve worked on a number of ETL tools and interoperability oriented applications where I can’t know the field size in the database at design time, it must be discovered at runtime.

One of the great parts about ADO.NET is the ease in which it deals with parameters. At the core of executing SQL statements is the SqlCommand (or OleDbCommand, or odbcCommand) class. After obtaining an instance associated with a database connection, we assign our SQL statement to it and all our parameters and values to it. In our case, the size of the parameter values we assign to it may exceed the maximum size of our receiving database fields. We need a method of iterating through each parameter and ensuring the value assigned isn’t too big.

Reading table schema

The heart of this routine is detecting which table you’re dealing with, then reading the schema of that table, then discovering the max size of each field.

I start by declaring a sub procedure that takes a SqlCommand as argument by reference.

        Sub TruncateParameters(ByRef passCommand As SqlCommand)

I then declared the following variables

        Dim targetTable As String
        Dim LCV As Integer
        Dim tableCommand As SqlCommand
        Dim tableReader As SqlDataReader
        Dim tempRow As DataRow

targetTable is the name of the table we’ll be checking. I read it from the actual SQL statement in the SqlCommand – but I was dealing with fairly simple SQL statements. You may want to manually pass the table names in if you’re dealing with SQL statements that will be difficult to parse.

tableCommand, tableReader, and tempRow will be used to read in the schema of our targetTable.

Next I parse the SQL statement inside of passCommand to obtain the targetTable name, but again, you may want to manually pass the name of the table ahead of time if you’re dealing with difficult to parse SQL statements. Also, if you are familiar with regular expressions, you can pare this routine down quite a bit – I present it here completely algorithmically for people who aren’t familiar with regular expressions.

       If passCommand.CommandText.ToLower().IndexOf("update") > -1 Then
             'UPDATE command
             targetTable = passCommand.CommandText.Substring(passCommand.CommandText.ToLower().IndexOf("update") + 7)
       Else
             'INSERT command
             targetTable = passCommand.CommandText.Substring(passCommand.CommandText.ToLower().IndexOf("insert") + 12)
       End If

       targetTable = targetTable.Substring(0, targetTable.IndexOf(" "))

Next we read (up to) 1 row from our target table to obtain the schema.

        ' Open Data Reader for schema table
        tableCommand = New SqlCommand("SELECT TOP 1 * FROM " & targetTable, passCommand.Connection)
        tableReader = tableCommand.ExecuteReader()

This is where the magic happens – we iterate through each parameter in our passCommand, examine the field matching the parameter, get its max length, then truncate the value assigned to each respective parameter to the max size of the field. This routine assumes that you’ve used the same parameter names as field names. If you haven’t, you would need to do further parsing of the SQL statement to deduce a parameter name -> field name mapping. This routine is also targeted for strings, but would work just as well for binary data assuming you had a method of truncating such data. Also, there are a lot of shortcuts for doing case insensitive compares, my style is to convert to lower case and compare as I can never remember what is case sensitive and what is not. Feel free to use your own style.

        ' Loop through each parameter

        For LCV = 0 To passCommand.Parameters.Count - 1
            For Each tempRow In tableReader.GetSchemaTable().Rows
                If "@" & tempRow("ColumnName").ToString().ToLower() = passCommand.Parameters.Item(LCV).ParameterName.ToLower() Then
                    If tempRow("DataType").ToString().ToLower() = "system.string" Then
                        If passCommand.Parameters.Item(LCV).Value.ToString().Length > tempRow("ColumnSize") Then
                            passCommand.Parameters.Item(LCV).Value = passCommand.Parameters.Item(LCV).Value.ToString().Substring(0, tempRow("ColumnSize"))
                        End If
                        Exit For
                    End If
                End If
            Next
        Next

Finally, we close out our connection and end the sub procedure.

        tableReader.Close()

End Sub

There is some work involved, but the idea of the above routine can be customized to fit your situation. Once you have a solid parameter truncating routine in your arsenal, you can call it for any SqlCommand object, and ensure you never get a “String or Binary data would be truncated.” error message again!

Play Adventure Games, Be a Better Programmer

> Open trapdoor
> Go down
> Turn on lantern

I can’t count the number of times I typed the sentences above on my faithful Commodore 64 immediately after getting home from school every weekday afternoon. An explanation to the uninitiated: these simple statements were a way of communicating with a style of game known as “interactive fiction”, or “text adventure”. The particular one that started it all for me was the classic masterpiece known as Zork, where every day I could explore a mysterious underground empire from my room. While the magic of text adventures is an article in itself, after playing Zork once I was completely hooked. Not only did I seek out more adventures to sink my teeth into, I was also lucky enough that my parents purchased a PC for me soon after. From there I discovered the wonder of graphical adventure games, like the Monkey Island series from Lucas Arts and the Kings Quest series from Sierra. From then, and even to this day, I was a complete adventure game junky. No other genre of game could come close to giving me as much enjoyment.

How are adventure games relevant though?

For those who haven’t played an adventure game before, the premise is generally one of accomplishing goals through a series of small tasks. For example, in the popular classic from Lucas Arts, “The Secret of Monkey Island” (spoilers ahead!), at one point in the game it’s necessary to obtain money to purchase a number of items. There isn’t, of course, a pile of money sitting on the ground – that would be too easy. You need to earn it, in this case, from getting paid to get yourself shot out of a cannon at the circus. However, they won’t stick you in the cannon unless you’re wearing a helmet. Guess what? You need to find a helmet, and there is no helmet in the game. There is, however, a cooking pot in the tavern’s kitchen that would fit perfectly on your head. However, you can’t get into the kitchen because the cook won’t let you in…

As you can see, there is a goal (obtain sum of money), and a number of tasks involved in getting there (get into kitchen, get pot, wear it, fire out of cannon). Before you can accomplish a task, there are a number of prerequisite tasks that must be completed first. Each of those tasks has its own set of tasks to complete first, etc. In Computer Science, we call this step-by-step procedure an algorithm.

Algorithms are the heart of programming

Computers, the powerful machines they are, are at their core relatively dumb. They need to be given a list of instructions to follow. They can perform about 4 billion of them a second, but at the end of the day they still need that list of procedures, outlined step-by-step. This is where you, the programmer comes in. Given a goal you wish to accomplish, you must devise a series of steps to lead to that goal, then translate these steps into a language the computer (ultimately) recognizes, such as Visual Basic, C++, or Java.

The same mentality that guides us to get the helmet, to get the cannon, to get the money, is the same process that guides us to be good programmers: to think in steps. A top down approach of taking an overall goal, breaking it into smaller pieces, further dividing each of those pieces into easy steps, until we’ve turned a very large and complex problem into a series of tiny, easy to handle tasks. And sometimes in programming we need to take alternate avenues – just as there is no helmet in Money Island but a cooking pot which makes an excellent substitute, there will be many times in programming where alternative over conventional thinking is necessary to accomplish a goal.

Why adventure games build these skills effectively

The one issue with programming, especially if you’re new to it as I imagine many people reading this article are, is it is a fairly foreign concept. You’re so busy struggling with the syntax of the new language you’re learning that the overall design of the program itself suffers. While this is a necessary part of learning to program, wouldn’t it be great if there were other exercises you could do in the meantime to strengthen your algorithm building skills?

Enter adventure games. They are a great tool to improve your “algorithmic dexterity” as they are enjoyable and usually take place in recognizable situations that you are accustomed to. We all know what a cooking pot is, and we all know we can put it on our head. We know if we turn a lantern on, it’s going to provide light. These are situations we’re used to seeing. However, these common situations put to the extreme focus of step-by-step procedures are what build our instruction-making skills – and adventure games are the absolute masters of requiring this brand of thinking for hours at a time.

So start playing games!

I don’t want to give the wrong impression – learning to program is a lot of hard (but enjoyable!) work, and there are some dry moments when it takes memorization and reading a book/screen. However, when dealing with the flowcharts academia will make you draw out when learning algorithms (a favorite seems to be “Draw out your morning routine!”), or if you’re learning on your own, try supplementing the boring with a little fun. Fire up an adventure game and train your algorithmic brain! Not only will you build a necessary skill, but you’ll also have fun doing it.

« Previous PageNext Page »