public static void CreateBitmapPerGlyph(GLFontGlyph[] sourceGlyphs, GLFontBitmap[] sourceBitmaps, out GLFontGlyph[] destGlyphs, out GLFontBitmap[] destBitmaps) { destBitmaps = new GLFontBitmap[sourceGlyphs.Length]; destGlyphs = new GLFontGlyph[sourceGlyphs.Length]; for (int i = 0; i < sourceGlyphs.Length; i++) { var sg = sourceGlyphs[i]; destGlyphs[i] = new GLFontGlyph(i, new Rectangle(0, 0, sg.Rect.Width, sg.Rect.Height), sg.YOffset, sg.Character); destBitmaps[i] = new GLFontBitmap(new Bitmap(sg.Rect.Width, sg.Rect.Height, PixelFormat.Format32bppArgb)); GLFontBitmap.Blit(sourceBitmaps[sg.Page].bitmapData, destBitmaps[i].bitmapData, sg.Rect, 0, 0); } }
public static Dictionary <String, int> CalculateKerning(char[] charSet, GLFontGlyph[] glyphs, List <GLFontBitmap> bitmapPages, GLFontKerningConfiguration config) { var kerningPairs = new Dictionary <String, int>(); //we start by computing the index of the first and last non-empty pixel in each row of each glyph XLimits[][] limits = new XLimits[charSet.Length][]; int maxHeight = 0; for (int n = 0; n < charSet.Length; n++) { var rect = glyphs[n].Rect; var page = bitmapPages[glyphs[n].Page]; limits[n] = new XLimits[rect.Height]; maxHeight = Math.Max(rect.Height, maxHeight); int yStart = rect.Y; int yEnd = rect.Y + rect.Height; int xStart = rect.X; int xEnd = rect.X + rect.Width; for (int j = yStart; j < yEnd; j++) { int last = xStart; bool yetToFindFirst = true; for (int i = xStart; i < xEnd; i++) { if (!GLFontBitmap.EmptyAlphaPixel(page.bitmapData, i, j, config.AlphaEmptyPixelTolerance)) { if (yetToFindFirst) { limits[n][j - yStart].Min = i - xStart; yetToFindFirst = false; } last = i; } } limits[n][j - yStart].Max = last - xStart; if (yetToFindFirst) { limits[n][j - yStart].Min = xEnd - 1; } } } //we now bring up each row to the max (or min) of it's two adjacent rows, this is to stop glyphs sliding together too closely var tmp = new XLimits[maxHeight]; for (int n = 0; n < charSet.Length; n++) { //clear tmp for (int j = 0; j < limits[n].Length; j++) { tmp[j] = limits[n][j]; } for (int j = 0; j < limits[n].Length; j++) { if (j != 0) { tmp[j].Min = Math.Min(limits[n][j - 1].Min, tmp[j].Min); tmp[j].Max = Math.Max(limits[n][j - 1].Max, tmp[j].Max); } if (j != limits[n].Length - 1) { tmp[j].Min = Math.Min(limits[n][j + 1].Min, tmp[j].Min); tmp[j].Max = Math.Max(limits[n][j + 1].Max, tmp[j].Max); } } for (int j = 0; j < limits[n].Length; j++) { limits[n][j] = tmp[j]; } } for (int i = 0; i < charSet.Length; i++) { for (int j = 0; j < charSet.Length; j++) { kerningPairs.Add("" + charSet[i] + charSet[j], 1 - Kerning(glyphs[i], glyphs[j], limits[i], limits[j], config)); } } return(kerningPairs); }
private static List <GLFontBitmap> GenerateBitmapSheetsAndRepack(GLFontGlyph[] sourceGlyphs, BitmapData[] sourceBitmaps, int destSheetWidth, int destSheetHeight, out GLFontGlyph[] destGlyphs, int destMargin, bool usePowerOfTwo) { var pages = new List <GLFontBitmap>(); destGlyphs = new GLFontGlyph[sourceGlyphs.Length]; GLFontBitmap currentPage = null; int maxY = sourceGlyphs.Max(g => g.Rect.Height); int finalPageIndex = 0; int finalPageRequiredWidth = 0; int finalPageRequiredHeight = 0; for (int k = 0; k < 2; k++) { bool pre = k == 0; //first iteration is simply to determine the required size of the final page, so that we can crop it in advance int xPos = 0; int yPos = 0; int maxYInRow = 0; int totalTries = 0; for (int i = 0; i < sourceGlyphs.Length; i++) { if (!pre && currentPage == null) { if (finalPageIndex == pages.Count) { int width = Math.Min(destSheetWidth, usePowerOfTwo ? PowerOfTwo(finalPageRequiredWidth) : finalPageRequiredWidth); int height = Math.Min(destSheetHeight, usePowerOfTwo ? PowerOfTwo(finalPageRequiredHeight) : finalPageRequiredHeight); currentPage = new GLFontBitmap(new Bitmap(width, height, PixelFormat.Format32bppArgb)); currentPage.Clear32(255, 255, 255, 0); // clear to white, but totally transparent } else { currentPage = new GLFontBitmap(new Bitmap(destSheetWidth, destSheetHeight, PixelFormat.Format32bppArgb)); currentPage.Clear32(255, 255, 255, 0); // clear to white, but totally transparent } pages.Add(currentPage); } totalTries++; if (totalTries > 10 * sourceGlyphs.Length) { throw new Exception("Failed to fit font into texture pages"); } var rect = sourceGlyphs[i].Rect; if (xPos + rect.Width + 2 * destMargin <= destSheetWidth && yPos + rect.Height + 2 * destMargin <= destSheetHeight) { if (!pre) { //add to page if (sourceBitmaps[sourceGlyphs[i].Page].PixelFormat == PixelFormat.Format32bppArgb) { GLFontBitmap.Blit(sourceBitmaps[sourceGlyphs[i].Page], currentPage.bitmapData, rect.X, rect.Y, rect.Width, rect.Height, xPos + destMargin, yPos + destMargin); } else { GLFontBitmap.BlitMask(sourceBitmaps[sourceGlyphs[i].Page], currentPage.bitmapData, rect.X, rect.Y, rect.Width, rect.Height, xPos + destMargin, yPos + destMargin); } destGlyphs[i] = new GLFontGlyph(pages.Count - 1, new Rectangle(xPos + destMargin, yPos + destMargin, rect.Width, rect.Height), sourceGlyphs[i].YOffset, sourceGlyphs[i].Character); } else { finalPageRequiredWidth = Math.Max(finalPageRequiredWidth, xPos + rect.Width + 2 * destMargin); finalPageRequiredHeight = Math.Max(finalPageRequiredHeight, yPos + rect.Height + 2 * destMargin); } xPos += rect.Width + 2 * destMargin; maxYInRow = Math.Max(maxYInRow, rect.Height); continue; } if (xPos + rect.Width + 2 * destMargin > destSheetWidth) { i--; yPos += maxYInRow + 2 * destMargin; xPos = 0; if (yPos + maxY + 2 * destMargin > destSheetHeight) { yPos = 0; if (!pre) { currentPage = null; } else { finalPageRequiredWidth = 0; finalPageRequiredHeight = 0; finalPageIndex++; } } continue; } } } return(pages); }
private static void RetargetGlyphRectangleOutwards(BitmapData bitmapData, GLFontGlyph glyph, bool setYOffset, byte alphaTolerance) { int startX, endX; int startY, endY; var rect = glyph.Rect; EmptyDel emptyPix; if (bitmapData.PixelFormat == PixelFormat.Format32bppArgb) { emptyPix = delegate(BitmapData data, int x, int y) { return(GLFontBitmap.EmptyAlphaPixel(data, x, y, alphaTolerance)); } } ; else { emptyPix = delegate(BitmapData data, int x, int y) { return(GLFontBitmap.EmptyPixel(data, x, y)); } }; unsafe { for (startX = rect.X; startX >= 0; startX--) { bool foundPix = false; for (int j = rect.Y; j <= rect.Y + rect.Height; j++) { if (!emptyPix(bitmapData, startX, j)) { foundPix = true; break; } } if (foundPix) { startX++; break; } } for (endX = rect.X + rect.Width; endX < bitmapData.Width; endX++) { bool foundPix = false; for (int j = rect.Y; j <= rect.Y + rect.Height; j++) { if (!emptyPix(bitmapData, endX, j)) { foundPix = true; break; } } if (foundPix) { endX--; break; } } for (startY = rect.Y; startY >= 0; startY--) { bool foundPix = false; for (int i = startX; i <= endX; i++) { if (!emptyPix(bitmapData, i, startY)) { foundPix = true; break; } } if (foundPix) { startY++; break; } } for (endY = rect.Y + rect.Height; endY < bitmapData.Height; endY++) { bool foundPix = false; for (int i = startX; i <= endX; i++) { if (!emptyPix(bitmapData, i, endY)) { foundPix = true; break; } } if (foundPix) { endY--; break; } } } glyph.Rect = new Rectangle(startX, startY, endX - startX + 1, endY - startY + 1); if (setYOffset) { glyph.YOffset = glyph.Rect.Y; } }
private static void RetargetGlyphRectangleInwards(BitmapData bitmapData, GLFontGlyph glyph, bool setYOffset, byte alphaTolerance) { int startX, endX; int startY, endY; var rect = glyph.Rect; EmptyDel emptyPix; if (bitmapData.PixelFormat == PixelFormat.Format32bppArgb) { emptyPix = delegate(BitmapData data, int x, int y) { return(GLFontBitmap.EmptyAlphaPixel(data, x, y, alphaTolerance)); } } ; else { emptyPix = delegate(BitmapData data, int x, int y) { return(GLFontBitmap.EmptyPixel(data, x, y)); } }; unsafe { for (startX = rect.X; startX < bitmapData.Width; startX++) { for (int j = rect.Y; j < rect.Y + rect.Height; j++) { if (!emptyPix(bitmapData, startX, j)) { goto Done1; } } } Done1: for (endX = rect.X + rect.Width - 1; endX >= 0; endX--) { for (int j = rect.Y; j < rect.Y + rect.Height; j++) { if (!emptyPix(bitmapData, endX, j)) { goto Done2; } } } Done2: for (startY = rect.Y; startY < bitmapData.Height; startY++) { for (int i = startX; i < endX; i++) { if (!emptyPix(bitmapData, i, startY)) { goto Done3; } } } Done3: for (endY = rect.Y + rect.Height - 1; endY >= 0; endY--) { for (int i = startX; i < endX; i++) { if (!emptyPix(bitmapData, i, endY)) { goto Done4; } } } Done4 :; } if (endY < startY) { startY = endY = rect.Y; } if (endX < startX) { startX = endX = rect.X; } glyph.Rect = new Rectangle(startX, startY, endX - startX + 1, endY - startY + 1); if (setYOffset) { glyph.YOffset = glyph.Rect.Y; } }
public void DownScale32(int newWidth, int newHeight) { GLFontBitmap newBitmap = new GLFontBitmap(new Bitmap(newWidth, newHeight, bitmap.PixelFormat)); if (bitmap.PixelFormat != PixelFormat.Format32bppArgb) { throw new Exception("DownsScale32 only works on 32 bit images"); } float xscale = (float)bitmapData.Width / newWidth; float yscale = (float)bitmapData.Height / newHeight; byte r = 0, g = 0, b = 0, a = 0; float summedR = 0f; float summedG = 0f; float summedB = 0f; float summedA = 0f; int left, right, top, bottom; //the area of old pixels covered by the new bitmap float targetStartX, targetEndX; float targetStartY, targetEndY; float leftF, rightF, topF, bottomF; //edges of new pixel in old pixel coords float weight; float weightScale = xscale * yscale; float totalColourWeight = 0f; for (int m = 0; m < newHeight; m++) { for (int n = 0; n < newWidth; n++) { leftF = n * xscale; rightF = (n + 1) * xscale; topF = m * yscale; bottomF = (m + 1) * yscale; left = (int)leftF; right = (int)rightF; top = (int)topF; bottom = (int)bottomF; if (left < 0) { left = 0; } if (top < 0) { top = 0; } if (right >= bitmapData.Width) { right = bitmapData.Width - 1; } if (bottom >= bitmapData.Height) { bottom = bitmapData.Height - 1; } summedR = 0f; summedG = 0f; summedB = 0f; summedA = 0f; totalColourWeight = 0f; for (int j = top; j <= bottom; j++) { for (int i = left; i <= right; i++) { targetStartX = Math.Max(leftF, i); targetEndX = Math.Min(rightF, i + 1); targetStartY = Math.Max(topF, j); targetEndY = Math.Min(bottomF, j + 1); weight = (targetEndX - targetStartX) * (targetEndY - targetStartY); GetPixel32(i, j, ref r, ref g, ref b, ref a); summedA += weight * a; if (a != 0) { summedR += weight * r; summedG += weight * g; summedB += weight * b; totalColourWeight += weight; } } } summedR /= totalColourWeight; summedG /= totalColourWeight; summedB /= totalColourWeight; summedA /= weightScale; if (summedR < 0) { summedR = 0f; } if (summedG < 0) { summedG = 0f; } if (summedB < 0) { summedB = 0f; } if (summedA < 0) { summedA = 0f; } if (summedR >= 256) { summedR = 255; } if (summedG >= 256) { summedG = 255; } if (summedB >= 256) { summedB = 255; } if (summedA >= 256) { summedA = 255; } newBitmap.PutPixel32(n, m, (byte)summedR, (byte)summedG, (byte)summedB, (byte)summedA); } } this.Free(); this.bitmap = newBitmap.bitmap; this.bitmapData = newBitmap.bitmapData; }