Cross Platform C#
Slimming iOS and Android Pics for Mobility
Pictures are worth a thousand words, and some can be storage-hungry, which can be bad for mobile apps and sites. Here's a trick for slimming them down.
Nothing can be as helpful as a picture. Text is great, but there is a reason why pictures are worth a thousand words. Unfortunately, as the cameras of devices have gotten better, the amount of space that pictures take up has gotten larger. With a late-2015/early-2016 phone, a picture can easily be 2MB. Start trying to upload those via e-mail or to a service (Instagram, Facebook, Twitter), and there are always problems with connectivity or otherwise. Let's examine some simple routines to minimize the size of an image.
The General Upload Process
The size of images can be hard to work with, especially over a somewhat unreliable wireless connection. Let's think through how the images are going to be uploaded. The general process is going to be to take a picture or video with the camera and then to take the image's bytes and upload them.
The first question is how to upload the image's bytes. In working with my friend, I initially would translate the image in a byte array, convert the byte array to a base64 string, upload the string, and let the Web service convert the base64 string back to a byte array and operations would continue on. I found out the hard way that as images and video became larger, this did not scale well on the device nor on the server. With a reliable Wi-Fi connection, this "worked for me," but never seemed to work well in the field where the user could be on a 3G-or-worse network connection.
Overall, this was a bad strategy for several reasons:
- The size of the image. Trying to upload a 2MB file from an application is a hard thing to do due to unreliable network connections.
- Trying to convert the byte array of the image within an application and perform the upload can be problematic. This conversion to a base64 string and back on the server is sloppy. Instead, a better strategy is to post the data to the server (or at least it feels that way).
- Instead of uploading via the application, uploading via an OS service, such as an Android Service, can allow the application to be asynchronous. On iOS, a similar operation can be performed via the iOS APIs.
This article will only look at minimizing the size of the images.
Images in iOS
The cameras in the iPhone and iPad produce great images. The UIImage is the primary object that allows code to interact with images. Here's some code to slim down an image:
var imgOriginal = UIImage.FromBundle ("MessyDesk.jpg");
var currentSize = imgOriginal.Size;
var jpgSize = imgOriginal.AsJPEG ().Length;
Console.WriteLine ("original image size: " + jpgSize.ToString ());
UIGraphics.BeginImageContext (new System.Drawing.SizeF (240f, 320f));
var imgSmall = UIImage.FromBundle ("MessyDesk.jpg");
imgSmall.Draw (new System.Drawing.PointF(0, 0, 240f, 320f));
imgSmall = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext ();
Console.WriteLine ("small image size: " + imgSmall.AsJPEG ().Length.ToString ());
This code will take an image called MessyDesk.jpg, load it, perform some operations on it, output some data to the immediate window and, finally, display it on screen.
The original image shown in Figure 1 is more than 7MB when loaded; the resolution is 3024x4032. I'm sure that there are services that require this resolution, but for most mobile apps, it's excessive. Much smaller images would work just as well for the user and would also decrease the memory used by the application. While it sounds like a small issue, the less memory an application uses, the more stable it will be. An application that uses too much memory will be killed by the underlying OS. By reducing the size of the image, the application will use less memory, and the stability of the application will be improved.
The resized image shown in Figure 2 has a resolution of 240x320 and is only 86KB. Notice that it renders more smoothly. (This is a simulation of what you'd see on a mobile app, so if you're viewing this article on a computer or regular browser, take that into account.)
A final note on image resizing is that taking a large image and attempting to display it in a small space can result in artifacts and basically a final display that isn't smooth at all. It's most evident when you compare the figures -- Figures 1 and 2 are the same image, but Figure 2 is greatly reduced in size, much smoother to view and does not have the artifacts at all. Overall, the smaller image is better to view in many ways and even more so from mobile apps.
Images in Android
Images taken by the camera in Android carry many of the same issues as in iOS. In fact, the problem is a bit worse, because recent Android devices tend to have cameras that take much higher resolution pictures.
Note: I hesitate to say that images in Android are better or worse than iOS due solely to image resolution. Image quality is affected by resolution, as well as the supporting software used to enhance the images. I'm just saying that the images produced by my Nexus 6p are larger than the images produced by my iPhone 6 Plus.
Android makes the resizing operation a bit simpler. The images can be resized using the bitmap's static method .CreateScaledBitmap. This method will take a bitmap as a parameter, as well as a new width and height. The output will be a bitmap object that has been scaled to the new height and width.
Other Strategies and Issues
Resizing an image is the first step in reducing the size of the image. The next step is to understand the images operate. JPEG is a format that throws away certain pieces of data in an attempt to minimize the file size. PNG is a graphics format that doesn't throw data away when it attempts to compress the data. For camera pictures, JPEG tends to result in smaller file sizes. It is possible to increase the amount of compression on JPEG to decrease the size of the image. The tradeoff for this is typically image quality. You can increase the compression; however, you might also decrease the quality of the image such that it doen't display well.
You might also want to handle issues on the server by storing the images in their largest dimensions and only sending smaller versions of the images. That's a different article.
Screenshot Your iPhone or iPad: Bonus
Have you ever needed to programmatically take a screenshot of your iPhone/iPad? I had this come up and I wanted to share the code. I found some ObjectiveC code and translated it to C#/Xamarin, which I've used a few times (enjoy!):
var keyWindow = UIApplication.SharedApplication.KeyWindow;
var rect = keyWindow.Bounds;
UIGraphics.BeginImageContextWithOptions(rect.Size, this.View.Opaque, 0f);
CoreGraphics.CGContext context = UIGraphics.GetCurrentContext();
keyWindow.Layer.RenderInContext(context);
var img = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
var strm = img.AsPNG().AsStream();
This code will get the window, create an image context, get the image from the context, close the context and, finally, get a stream that represents the PNG image.
Image Is Everything
Pictures are definitely worth a thousand words. With this article, I hope you can take that from being 1,000KB of space to a much smaller amount and still take advantage of the camera in your device.
About the Author
Wallace (Wally) B. McClure has authored books on iPhone programming with Mono/Monotouch, Android programming with Mono for Android, application architecture, ADO.NET, SQL Server and AJAX. He's a Microsoft MVP, an ASPInsider and a partner at Scalable Development Inc. He maintains a blog, and can be followed on Twitter.