public static void CreateBitmapPerGlyph(QFontGlyph[] sourceGlyphs, QBitmap[] sourceBitmaps, out QFontGlyph[] destGlyphs, out QBitmap[] destBitmaps){ destBitmaps = new QBitmap[sourceGlyphs.Length]; destGlyphs = new QFontGlyph[sourceGlyphs.Length]; for(int i = 0; i < sourceGlyphs.Length; i++){ var sg = sourceGlyphs[i]; destGlyphs[i] = new QFontGlyph(i,new Rectangle(0,0,sg.rect.Width,sg.rect.Height),sg.yOffset,sg.character); destBitmaps[i] = new QBitmap(new Bitmap(sg.rect.Width,sg.rect.Height,PixelFormat.Format32bppArgb)); QBitmap.Blit(sourceBitmaps[sg.page].bitmapData,destBitmaps[i].bitmapData,sg.rect,0,0); } }
private static List <QBitmap> GenerateBitmapSheetsAndRepack(QFontGlyph[] sourceGlyphs, BitmapData[] sourceBitmaps, int destSheetWidth, int destSheetHeight, out QFontGlyph[] destGlyphs, int destMargin) { var pages = new List <QBitmap>(); destGlyphs = new QFontGlyph[sourceGlyphs.Length]; QBitmap currentPage = null; int maxY = 0; foreach (var glph in sourceGlyphs) { maxY = Math.Max(glph.rect.Height, maxY); } 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, finalPageRequiredWidth); int height = Math.Min(destSheetHeight, finalPageRequiredHeight); currentPage = new QBitmap(new Bitmap(width, height, PixelFormat.Format32bppArgb)); currentPage.Clear32(255, 255, 255, 0); //clear to white, but totally transparent } else { currentPage = new QBitmap(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) { QBitmap.Blit(sourceBitmaps[sourceGlyphs[i].page], currentPage.bitmapData, rect.X, rect.Y, rect.Width, rect.Height, xPos + destMargin, yPos + destMargin); } else { QBitmap.BlitMask(sourceBitmaps[sourceGlyphs[i].page], currentPage.bitmapData, rect.X, rect.Y, rect.Width, rect.Height, xPos + destMargin, yPos + destMargin); } destGlyphs[i] = new QFontGlyph(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); }
/// <summary> /// Generates the final bitmap sheet for the font /// </summary> /// <param name="sourceGlyphs">A collection of <see cref="QFontGlyph"/>s. These are written to the final bitmap</param> /// <param name="sourceBitmaps"> The source bitmaps for the font (initial bitmap)</param> /// <param name="destSheetWidth">The destination bitmap width</param> /// <param name="destSheetHeight">The destination bitmap height</param> /// <param name="destGlyphs">A collection of <see cref="QFontGlyph"/>s that are placed on the final bitmap sheet</param> /// <param name="destMargin">The margin for the final bitmap sheet</param> /// <returns>A collection of <see cref="QBitmap"/>s. These are the final bitmap sheets</returns> private static List <QBitmap> GenerateBitmapSheetsAndRepack(QFontGlyph[] sourceGlyphs, BitmapData[] sourceBitmaps, int destSheetWidth, int destSheetHeight, out QFontGlyph[] destGlyphs, int destMargin) { var pages = new List <QBitmap>(); destGlyphs = new QFontGlyph[sourceGlyphs.Length]; QBitmap currentPage = null; int maxY = 0; foreach (var glph in sourceGlyphs) { maxY = Math.Max(glph.Rect.Height, maxY); } int finalPageIndex = 0; int finalPageRequiredWidth = 0; int finalPageRequiredHeight = 0; // We loop through the whole process twice. The first time is to determine // the size of the final page, so that we can crop it in advance for (int k = 0; k < 2; k++) { // Whether this is the pre-processing step bool pre = k == 0; int xPos = 0; int yPos = 0; int maxYInRow = 0; int totalTries = 0; // Loop through all the glyphs for (int i = 0; i < sourceGlyphs.Length; i++) { // If this is the second stage and we don't already have a bitmap page, create one if (!pre && currentPage == null) { if (finalPageIndex == pages.Count) { int width = Math.Min(destSheetWidth, finalPageRequiredWidth); int height = Math.Min(destSheetHeight, finalPageRequiredHeight); currentPage = new QBitmap(new Bitmap(width, height, PixelFormat.Format32bppArgb)); currentPage.Clear32(255, 255, 255, 0); //clear to white, but totally transparent } else { currentPage = new QBitmap(new Bitmap(destSheetWidth, destSheetHeight, PixelFormat.Format32bppArgb)); currentPage.Clear32(255, 255, 255, 0); //clear to white, but totally transparent } pages.Add(currentPage); } // Keep track of the number of times we've tried to fit the font onto the texture page totalTries++; if (totalTries > 10 * sourceGlyphs.Length) { throw new Exception("Failed to fit font into texture pages"); } var rect = sourceGlyphs[i].Rect; // If we can fit the glyph onto the page, place it 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) { QBitmap.Blit(sourceBitmaps[sourceGlyphs[i].Page], currentPage.BitmapData, rect.X, rect.Y, rect.Width, rect.Height, xPos + destMargin, yPos + destMargin); } else { QBitmap.BlitMask(sourceBitmaps[sourceGlyphs[i].Page], currentPage.BitmapData, rect.X, rect.Y, rect.Width, rect.Height, xPos + destMargin, yPos + destMargin); } // Add to destination glyph collection destGlyphs[i] = new QFontGlyph(pages.Count - 1, new Rectangle(xPos + destMargin, yPos + destMargin, rect.Width, rect.Height), sourceGlyphs[i].YOffset, sourceGlyphs[i].Character); } else { // Update the final dimensions finalPageRequiredWidth = Math.Max(finalPageRequiredWidth, xPos + rect.Width + 2 * destMargin); finalPageRequiredHeight = Math.Max(finalPageRequiredHeight, yPos + rect.Height + 2 * destMargin); } // Update the current x position xPos += rect.Width + 2 * destMargin; // Update the maximum row height so far maxYInRow = Math.Max(maxYInRow, rect.Height); continue; } // If we reach this, haven't been able to fit glyph onto row // Move down one row and try again if (xPos + rect.Width + 2 * destMargin > destSheetWidth) { // Retry the current glyph on the next row i--; // Change coordinates to next row yPos += maxYInRow + 2 * destMargin; xPos = 0; // Is the next row off the bitmap sheet? if (yPos + maxY + 2 * destMargin > destSheetHeight) { // Reset y position yPos = 0; if (!pre) { // If this is not the second stage, reset the currentPage // This will create a new one on next loop currentPage = null; } else { // If this is the pre-processing stage, update // the finalPageIndex. Reset width and height // since we clearly need one full page and extra finalPageRequiredWidth = 0; finalPageRequiredHeight = 0; finalPageIndex++; } } } } } return(pages); }