There's not much to learn, it's a fairly straightforward idea - for each pixel, find the distance to the closest pixel with a different value.
for each pixel closest = max number for each other pixel if pixel contents != other pixel contents D = distance(this pixel, other pixel) if D < closest closest = D
By using a limited search area, it's O(n*m), which is still pretty bad.
Some other things you can try:
* Get rid of the square-root operation from the loop. You can perform the search using squared-distances, and then when you've got the final results, go through and perform a square-root for each distance value.
* Get rid of as many ifs as possible. Actually, get rid of as much of the code inside the inner loop (i.e. CalculateSignedDistance). You can simplify it quite a bit, though it'll still probably be unbearably slow...
float CalculateDistance(Bitmap image, int imageX, int imageY, int scanSize) { Color baseColor = image.GetPixel(imageX, imageY); float closestDistance = float.MaxValue; int startX = imageX - halfScanSize; int endX = startX + scanWidth; int startY = imageY - halfScanSize; int endY = startY + scanHeight; // No need in searching past the images bounds startX = max(0, startX); startY = max(0, startX); endX = min(image.Width, endX); endY = min(image.Height, endY); for (int x = startX; x < endX; ++x) { for (int y = startY; y < endY; ++y) { Color texelColor = image.GetPixel(x, y); if (baseColor.R != texelColor.R) { float dx = imageX - x; float dy = imageY - y; float dist = dx*dx+dy*dy; closestDistance = min(dist, closestDistance); } } } return closestDistance; }........ for (int x = 0; x < signedDistanceBitmap.Width; ++x) { for (int y = 0; y < signedDistanceBitmap.Height; ++y) { float distance = CalculateDistance(bitmap, x, y, halfScanSize); signedDistances[x, y] = distance; } } for (int x = 0; x < textureOutWidth; ++x) { for (int y = 0; y < textureOutHeight; ++y) { float distance = distances[x, y]; if (distance == float.MaxValue) distance = 1.0f; else { distance = Math.Sqrt(distance); distance /= halfScanSize; } if(bitmap.GetPixel(x, y).R == 0) distance = -distance; distance /= 2; distance += 0.5f; // Set the signed distance into the alpha channel signedDistanceBitmap.SetPixel(x, y, Color.FromArgb((int)Math.Round(distance * 255), 255, 255, 255)); } }
* Another optimisation I did was to make an array that was a fraction of the size of the original, e.g. [Bitmap.Width/16, Bitmap.Height/16].
In this smaller array, I stored a value indicating whether each 16x16 block of pixels was all solid, all empty, or a mixture of both. Then I could use this information to do much less calculations on the blank areas of my font textures.
...but after doing all this, and getting my tool to run in a few minutes instead of hours, I just use Photoshop these days, which does it instantly ;/