Creating raster tilesets almost invariably leads to the creation of some blank tiles – covering those areas of space where no features were present in the underlying dataset. Depending on the image format you use for your tiles, and the method you used to create them, those “blank” tiles may be pure white, or some other solid colour, or they may have an alpha channel set to be fully transparent.
Here’s an example of a directory of tiles I just created. In this particular z/x directory, more than half the tiles are blank. Windows explorer shows them as black but that’s because it doesn’t process the alpha channel correctly. They are actually all 256px x 256px PNG images, filled with ARGB (0, 0, 0, 0):
What to do with these tiles? Well, there’s two schools of thought:
I can see arguments in favour of both sides. But, for my current project, disk and cache space is at a premium, so I decided I wanted to delete any blank tiles from my dataset. To determine which files were blank, I initially thought of testing the filesize of the image. However, even though I knew that every tile was of a fixed dimension (256px x 256px), an empty tile can still vary in filesize according to the compression algorithm used. Then I thought I could loop through each pixel in the image and use GetPixel() to retrieve the data to see whether the entire image was the same colour, but it turns out that GetPixel() is slooooowwwww….
The best solution I’ve found is to use an unsafe method, BitMap.LockBits to provide direct access to the pixel byte data of the image, and then read and compare the byte values directly. In my case, my image tiles are 32bit PNG files, which use 4 bytes per pixel (BGRA), and my “blank” tiles are completely transparent (Alpha channel = 0). Therefore, in my case I used the following function, which returns true if all the pixels in the image are completely transparent, or false otherwise:
public static Boolean IsEmpty(string imageFileName) { using (Bitmap b = ReadFileAsImage(imageFileName)) { System.Drawing.Imaging.BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, b.PixelFormat); unsafe { int PixelSize = 4; // Assume 4Bytes per pixel ARGB for (int y = 0; y < b.Height; y++) { byte* p = (byte*)bmData.Scan0 + (y * bmData.Stride); for (int x = 0; x < b.Width; x++) { byte blue = p[x * PixelSize]; // Blue value. Just in case needed later byte green = p[x * PixelSize + 1]; // Green. Ditto byte red = p[x * PixelSize + 2]; // Red. Ditto byte alpha = p[x * PixelSize + 3]; if (alpha > 0) return false; } } } b.UnlockBits(bmData); } return true; }
It needs to be compiled with the /unsafe option (well, it did say in the title that this post was dangerous!). Then, I just walked through the directory structure of my tile images, passing each file into this function and deleting those where IsEmpty() returned true.
This is a video showing the Silverlight Bing Maps Control for Windows Phone 7 showing some serious lagging behaviour on my HTC 7 Trophy. As you can see the built-in Windows Phone maps app performs normally (note that it’s a native thing and doesn’t use the Bing Maps Control), but the two user apps, Map Mania and Slim Tanken, both show some serious lagging while pinching/zooming