My previous post was about a solution to Bloaty Digi-cam photos, since the solution itself was a little tiresome to carry out manually, and there seemed to be no way to automate the process, I decided to do some good in the world, by writing a small application that would do that. Folks I a newbie to software development, and I know how amateurish the whole thing looks, as i write more and more code the more experienced I become,much like a pilot, the more he flies, the more he gains. But not every minute of his flying counts, there will be some tumbling and fumbling in the beginning. The same (I believe) applies to coding, not merely the lines of code,but the quality of it. You’ll soon see where all this corny talk of mine leads to.
My delusion on the “Apparent success”?
How long did it take to write this app?
Well, an afternoon, by my standards that’s remarkable(actually remarkably deluded), I said to my self, but as my application went to the testing department, everything went down the trenches. The rest of this post is my interesting journey… “Developing an Application, with C# and a bag of chips..and .....Oh a Profiler”
As usual I fired up visual studio, I not a Microsoft shil, but I’ve got to say that in my book there’s no software development without VS.
Then I phrased the problem in my mind, I need to somehow replicate whatever MS Paint is doing to reduce the size of the images. You’ll find it weird as I did, when you’ve read my previous post. Because effectively what paint was doing we just saving the JPEG to JPEG, no format conversion, re-coding , no rescaling , but it did change its resolution, but only a little.
So in order it achieve that I just have to Load the JPEG image and save it to a file stream again. Seems simple enough.
For those of u from Java background, in .net there are a lot of Nifty classes to play around with Images, using which i can save to and read from many formats Jpg,Bmp,tiff, etc..,The problem was it didn't quite have the effect of MS paint. The file size was still sky-high. So I remembered about the FreeImage library, which i thankfully heard about a year ago. I forgot all the documentation, but it was easy the only problem was it was an unmanaged (ie non- .net ) library, so there is a magic called P/Invoke involved in using a non-managed library, from a managed framework. I know how java programmers feel about this, but dont forget there are a lot of performance incentives involved with a little pain incurred, by writing a wrapper to the non-managed libraries such as FreeImage. Any way I ended up writing a C# wrapper to two important function i needed from the massive 2MB library.
They were
[DllImport("FreeImage.dll")]
public static extern int FreeImage_Load(int format, string filename, int flags);
[DllImport("FreeImage.dll")]
public static extern bool FreeImage_Save(int format, int handle, string filename, int flags);
the parameters are self-explanatory, int format is the file format for Jpeg it was 2,
I implemented these things with the following function
public void SaveToJpegOptimized(string UnOptimizedFile,string OptimizedFile)
{
try
{
int a;
a = FreeImage_Load(FIF.FIF_JPEG, UnOptimizedFile, 0);
FreeImage_Save(FIF.FIF_JPEG, a, OptimizedFile, 0);
}
catch(Exception exp)
{
System.Windows.Forms.MessageBox.Show(@"Houston we have a problem " + exp.Message.ToString () );
}
}
Actually there was another function which the application critically needed. More on that later.
Apparently All I had to do was Load the Jpeg and save it.So the logic was sketched. Now coding time…
Some people are religious about whether they should design the UI and code or backwards. I figured out it was better to Design the UI since the application logic was pretty much completed.
so here it is. the completed UI
I could’ve done this a little professionally, but i wanted it to be intuitive and not intimidating for non-geek users. Since were talking about talking to the Hard Disk (pun intended), I decided to go for Asynchronous operation, and It paid off. Especially when the user chooses to operate on multiple files. And with the Minimize to tray option the app sits quietly and does the job on the system tray.
Everything was going great until i decided to put the app to the test.
I mercilessly made it to chew on two hundred files
The application before eating the 23rd file, drank 440MB of Precious Memory.
I smelled MEMORY LEAK….
It was time to call the super hero at times of such peril.
TADA the Profiler, a blessing to the developer,
It quickly showed me where I went wrong Of course an experienced developer would a found out the cause in a snap,but it proved difficult for me.
The cause of the application to consume that much memory was that i forgot to call an important function.
[DllImport("FreeImage.dll")]
public static extern void FreeImage_Unload(int handle);
The FreeImage_Unload(int handle) de-allocates the memory needed to hold the image in memory.
private void Folderworkermethod()
{
try
{
string[] files = Directory.GetFiles(textBox2.Text, "*.jpg", SearchOption.TopDirectoryOnly);
DirectoryInfo drin = new DirectoryInfo(textBox2.Text);
FileInfo fileinfo;
progressBar2.Value = 0;
foreach (var file in files)
{
fileinfo = new FileInfo(file);
oldfoldersize += fileinfo.Length;
}
progressBar2.Maximum = files.Length;
drin = null;
oldfoldersize = oldfoldersize / 1024;
lblfoldersize.Text = oldfoldersize.ToString() + "KB";
string tname = "";
this.img = new ImageUtil();
progressBar2.Step = 10;
int filecnt = 0;
foreach (var file in files)
{
filecnt++;
tname = file + ".temp";
if (File.Exists(file + ".temp"))
File.Delete(file + ".temp");
File.Move(file, tname);
img.SaveToJpegOptimized(tname, file);
System.IO.File.Delete(tname);
tname = "";
progressBar2.Value = progressBar2.Value + 1;
lblfilecnt.Text = "File " + filecnt + " of " + files.Length.ToString();
}
foreach (var file in files)
{
fileinfo = new FileInfo(file);
newfoldersize += fileinfo.Length;
}
newfoldersize = newfoldersize / 1024;
lblfolderafter.Text = newfoldersize.ToString() + "KB";
lblfoldersizesav.Text = (oldfoldersize - newfoldersize).ToString();
img = null;
files = null;
}
catch (Exception)
{
}
}
At line number 35 you can see that after a file has been processed the memory can be reused, which the application failed to do.
The code
public void SaveToJpegOptimized(string UnOptimizedFile,string OptimizedFile)
{
try
{
int a;
a = FreeImage_Load(5, UnOptimizedFile, 0);
FreeImage_Save(5, a, OptimizedFile, 0);
}
catch(Exception exp)
{
System.Windows.Forms.MessageBox.Show(@"Houston we have a problem " + exp.Message.ToString () );
}
}
Became
public void SaveToJpegOptimized(string UnOptimizedFile,string OptimizedFile)
{
try
{
int a;
a = FreeImage_Load(5,UnOptimizedFile, 0);
FreeImage_Save(5, a, OptimizedFile, 0);
FreeImage_Unload(a);
}
catch(Exception exp)
{
System.Windows.Forms.MessageBox.Show(@"Houston we have a problem " + exp.Message.ToString () );
}
}
That’s right all it took was a single line extra. 8
Folks this is the side effect of managed programming The GC cannot interfere with the unmanaged code region, in C++ as we all know it is left to the coder to dispose all the objects with the( long forgotten )Destructor.(the nostalgic memories may return to haunt you :) . Any way That was it instant cold relief, I mean instant relief from Memory leak.I prescribe a good dose of profiling and experimentation.
Hope the boredom didn't kill you :) .
Until the next adventure
Vivek