/// <summary> /// Loads letter iamge from win32 font. /// </summary> /// <param name="cache">should the iamge be cached in tga, xml files.</param> /// <returns>loader letter information.</returns> protected internal override LetterInfo Load(bool cache) { LetterInfo result = null; #if WINDOWS IntPtr bitmapHandle = IntPtr.Zero; byte[] textureData = null; try { if (null == PlatformWindows.SelectFont(this.font.fontRenderingDisplayContext, this.font.fontHandle)) { throw new Exception(); } TextMetric tm = new TextMetric(); if (false == PlatformWindows.GetTextMetrics(this.font.fontRenderingDisplayContext, out tm)) { throw new Exception(); } if (PlatformWindows.GDI_ERROR == PlatformWindows.SetTextAlign(this.font.fontRenderingDisplayContext, PlatformWindows.TA_LEFT | PlatformWindows.TA_TOP | PlatformWindows.TA_UPDATECP)) { throw new Exception(); } ABC[] abc = new ABC[1]; if (false == PlatformWindows.GetCharABCWidths(this.font.fontRenderingDisplayContext, (uint)this.character, (uint)this.character, abc)) { throw new Exception(); } this.offsetX = abc[0].abcA; this.width = (int)(abc[0].abcB + abc[0].abcC); BitmapInfo header = new BitmapInfo(); header.bmiHeader.biSize = Marshal.SizeOf(header); GlyphMetrics metrics = new GlyphMetrics(); MAT2 identity = new MAT2(); identity.eM12.value = 0; identity.eM21.value = 0; identity.eM11.value = 1; identity.eM22.value = 1; if (PlatformWindows.GDI_ERROR == PlatformWindows.GetGlyphOutline(this.font.fontRenderingDisplayContext, this.character, PlatformWindows.GGO_METRICS, out metrics, 0, IntPtr.Zero, ref identity)) { throw new Exception(); } header.bmiHeader.biWidth = metrics.gmBlackBoxX; header.bmiHeader.biHeight = -1 * metrics.gmBlackBoxY; header.bmiHeader.biPlanes = 1; header.bmiHeader.biBitCount = 32; header.bmiHeader.biCompression = 0;// BI_RGB; byte[] bitmapData = null; IntPtr bitmapDataPointer = IntPtr.Zero; bitmapHandle = PlatformWindows.CreateDIBSection(this.font.fontRenderingDisplayContext, ref header, /*DIB_RGB_COLORS*/ 0, out bitmapDataPointer, IntPtr.Zero, 0); if (IntPtr.Zero == bitmapHandle) { int err = Marshal.GetLastWin32Error(); throw new Exception(); } if (null == PlatformWindows.SelectObject(this.font.fontRenderingDisplayContext, bitmapHandle)) { throw new Exception(); } if (PlatformWindows.CLR_INVALID == PlatformWindows.SetBkColor(this.font.fontRenderingDisplayContext, new RGB(new byte[] { 0, 0, 0 }).ToInt32())) { throw new Exception(); } if (PlatformWindows.CLR_INVALID == PlatformWindows.SetTextColor(this.font.fontRenderingDisplayContext, new RGB(new byte[] { 0xff, 0xff, 0xff }).ToInt32())) { throw new Exception(); } if (0 == PlatformWindows.SetBkMode(this.font.fontRenderingDisplayContext, PlatformWindows.OPAQUE)) { throw new Exception(); } if (false == PlatformWindows.MoveToEx(this.font.fontRenderingDisplayContext, 0 - abc[0].abcA, -1 * (tm.tmAscent - metrics.gmptGlyphOrigin.y), IntPtr.Zero)) { throw new Exception(); } this.offsetY = tm.tmAscent - metrics.gmptGlyphOrigin.y - tm.tmDescent - 1; String str = "" + this.character; RECT rect = new RECT(); if (false == PlatformWindows.ExtTextOut(this.font.fontRenderingDisplayContext, 0, 0, (uint)0, ref rect, str, 1, null)) { throw new Exception(); } if (0 == PlatformWindows.SetBkMode(this.font.fontRenderingDisplayContext, PlatformWindows.TRANSPARENT)) { throw new Exception(); } int bitmapWidth = header.bmiHeader.biWidth; int bitmapHeight = -header.bmiHeader.biHeight; int textureWidth = RoundToPowerOf2(bitmapWidth); int textureHeight = RoundToPowerOf2(bitmapHeight); bitmapData = new byte[bitmapWidth * bitmapHeight * 4]; Marshal.Copy(bitmapDataPointer, bitmapData, 0, bitmapWidth * bitmapHeight * 4); textureData = new byte[4 * textureWidth * textureHeight]; for (int j = 0; j < textureHeight; j++) { for (int i = 0; i < textureWidth; i++) { textureData[4 * (i + j * textureWidth) + 0] = 0xff; textureData[4 * (i + j * textureWidth) + 1] = 0xff; textureData[4 * (i + j * textureWidth) + 2] = 0xff; textureData[4 * (i + j * textureWidth) + 3] = (byte)((i >= bitmapWidth || j >= bitmapHeight) ? 0 : bitmapData[(i + bitmapWidth * j) * 4 + 2]); } } if (true == cache) { result = new LetterInfo(); result.width = bitmapWidth; result.height = bitmapHeight; result.bytes = textureData; result.textureWidth = textureWidth; result.textureHeight = textureHeight; } else { this.image = this.engine.CreateImage("Letter [" + this.character + "]", textureWidth, textureHeight, textureData); // this.internalImage = true; } this.textureWidth = textureWidth; this.textureHeight = textureHeight; } catch (Exception) { this.engine.DeleteImage(ref this.image); } if (null != bitmapHandle) { PlatformWindows.DeleteObject(bitmapHandle); } this.loaded = true; #endif return(result); }
/// <summary> /// Caches the first 256 letters. /// </summary> /// <param name="save">are the cached letters supposed to be saved to tga files.</param> private void CacheFirst256Letters(bool save, String folder) { int imageIndex = 0; byte[] imageBuffer = new byte[cacheTextureSize * cacheTextureSize * 4]; int rowStart = 0; int colStart = 0; int rowMaxHeight = 0; int[] imageIndexes = new int[cacheLetters]; int[][] imageUvs = new int[cacheLetters][]; for (int i = 0; i < cacheLetters; i++) { WinLetter letter = (WinLetter)this.letters[i]; LetterInfo letterInfo = letter.Load(true); if (null == letterInfo) { letterInfo = new LetterInfo(); letterInfo.width = 2; letterInfo.height = 2; letterInfo.textureHeight = 2; letterInfo.textureWidth = 2; letterInfo.bytes = new byte[2 * 2 * 4]; } if (colStart + letterInfo.width + 1 > cacheTextureSize) { colStart = 0; rowStart += rowMaxHeight + 1; } if (rowStart + 1 + letterInfo.height >= cacheTextureSize) { this.cachedImages.Add(this.engine.CreateImage(cacheFolder + ToString() + "_" + (imageIndex + 1), cacheTextureSize, cacheTextureSize, imageBuffer)); if (true == save) { byte[] tga = Image.GetTGA(cacheTextureSize, cacheTextureSize, imageBuffer, 32); this.engine.CreateFile(folder + cacheFolder + ToString() + "_" + (imageIndex + 1) + ".tga", tga, (uint)tga.Length); } rowStart = 0; colStart = 0; rowMaxHeight = 0; Array.Clear(imageBuffer, 0, imageBuffer.Length); imageIndex++; } for (int y = 0; y < letterInfo.height; y++) { for (int x = 0; x < letterInfo.width; x++) { imageBuffer[(rowStart + y) * 4 * cacheTextureSize + (colStart + x) * 4 + 0] = letterInfo.bytes[y * letterInfo.textureWidth * 4 + x * 4 + 0]; imageBuffer[(rowStart + y) * 4 * cacheTextureSize + (colStart + x) * 4 + 1] = letterInfo.bytes[y * letterInfo.textureWidth * 4 + x * 4 + 1]; imageBuffer[(rowStart + y) * 4 * cacheTextureSize + (colStart + x) * 4 + 2] = letterInfo.bytes[y * letterInfo.textureWidth * 4 + x * 4 + 2]; imageBuffer[(rowStart + y) * 4 * cacheTextureSize + (colStart + x) * 4 + 3] = letterInfo.bytes[y * letterInfo.textureWidth * 4 + x * 4 + 3]; } } imageUvs[i] = new int[4]; imageUvs[i][0] = colStart; imageUvs[i][1] = rowStart; imageUvs[i][2] = colStart + letterInfo.width + 1; imageUvs[i][3] = rowStart + letterInfo.height + 1; imageIndexes[i] = imageIndex; colStart += letterInfo.width + 1; if (letterInfo.height > rowMaxHeight) { rowMaxHeight = letterInfo.height; } } this.cachedImages.Add(this.engine.CreateImage(folder + cacheFolder + ToString() + "_" + (imageIndex + Int16.MaxValue), cacheTextureSize, cacheTextureSize, imageBuffer)); for (int i = 0; i < cacheLetters; i++) { WinLetter letter = (WinLetter)this.letters[i]; letter.SetCachedData(this.cachedImages[imageIndexes[i]], imageUvs[i][0], imageUvs[i][1], imageUvs[i][2], imageUvs[i][3]); } if (true == save) { byte[] tga = Image.GetTGA(cacheTextureSize, cacheTextureSize, imageBuffer, 32); this.engine.CreateFile(folder + cacheFolder + ToString() + "_" + (imageIndex++) + ".tga", tga, (uint)tga.Length); String xml = "<font>\n"; for (int i = 0; i < cacheLetters; i++) { xml += "\t" + ((WinLetter)this.letters[i]).ToXml(i, imageIndexes[i]) + "\n"; } xml += "</font>"; byte[] bytes = Encoding.UTF8.GetBytes(xml); String cacheFileName = folder + cacheFolder + ToString() + ".xml"; this.engine.Logger.WriteLine(LogLevel.Info, "Caching font to: " + cacheFileName); this.engine.CreateFile(cacheFileName, bytes, (uint)bytes.Length); } }
/// <summary> /// Caches the first 256 letters. /// </summary> /// <param name="save">are the cached letters supposed to be saved to tga files.</param> private void CacheFirst256Letters(bool save, String folder) { int imageIndex = 0; byte[] imageBuffer = new byte[cacheTextureSize * cacheTextureSize * 4]; int rowStart = 0; int colStart = 0; int rowMaxHeight = 0; int[] imageIndexes = new int[cacheLetters]; int[][] imageUvs = new int[cacheLetters][]; for (int i = 0; i < cacheLetters; i++) { WinLetter letter = (WinLetter)this.letters[i]; LetterInfo letterInfo = letter.Load(true); if (null == letterInfo) { letterInfo = new LetterInfo(); letterInfo.width = 2; letterInfo.height = 2; letterInfo.textureHeight = 2; letterInfo.textureWidth = 2; letterInfo.bytes = new byte[2*2*4]; } if (colStart + letterInfo.width + 1 > cacheTextureSize) { colStart = 0; rowStart += rowMaxHeight + 1; } if (rowStart + 1 + letterInfo.height >= cacheTextureSize) { this.cachedImages.Add(this.engine.CreateImage(cacheFolder + ToString() + "_" + (imageIndex+1), cacheTextureSize, cacheTextureSize, imageBuffer)); if (true == save) { byte[] tga = Image.GetTGA(cacheTextureSize, cacheTextureSize, imageBuffer, 32); this.engine.CreateFile(folder + cacheFolder + ToString() + "_" + (imageIndex+1) + ".tga", tga, (uint)tga.Length); } rowStart = 0; colStart = 0; rowMaxHeight = 0; Array.Clear(imageBuffer, 0, imageBuffer.Length); imageIndex++; } for (int y = 0; y < letterInfo.height; y++) { for (int x = 0; x < letterInfo.width; x++) { imageBuffer[(rowStart + y) * 4 * cacheTextureSize + (colStart + x) * 4 + 0] = letterInfo.bytes[y * letterInfo.textureWidth * 4 + x * 4 + 0]; imageBuffer[(rowStart + y) * 4 * cacheTextureSize + (colStart + x) * 4 + 1] = letterInfo.bytes[y * letterInfo.textureWidth * 4 + x * 4 + 1]; imageBuffer[(rowStart + y) * 4 * cacheTextureSize + (colStart + x) * 4 + 2] = letterInfo.bytes[y * letterInfo.textureWidth * 4 + x * 4 + 2]; imageBuffer[(rowStart + y) * 4 * cacheTextureSize + (colStart + x) * 4 + 3] = letterInfo.bytes[y * letterInfo.textureWidth * 4 + x * 4 + 3]; } } imageUvs[i] = new int[4]; imageUvs[i][0] = colStart; imageUvs[i][1] = rowStart; imageUvs[i][2] = colStart + letterInfo.width + 1; imageUvs[i][3] = rowStart + letterInfo.height + 1; imageIndexes[i] = imageIndex; colStart += letterInfo.width + 1; if (letterInfo.height > rowMaxHeight) { rowMaxHeight = letterInfo.height; } } this.cachedImages.Add(this.engine.CreateImage(folder + cacheFolder + ToString() + "_" + (imageIndex + Int16.MaxValue), cacheTextureSize, cacheTextureSize, imageBuffer)); for (int i = 0; i < cacheLetters; i++) { WinLetter letter = (WinLetter)this.letters[i]; letter.SetCachedData(this.cachedImages[imageIndexes[i]], imageUvs[i][0], imageUvs[i][1], imageUvs[i][2], imageUvs[i][3]); } if (true == save) { byte[] tga = Image.GetTGA(cacheTextureSize, cacheTextureSize, imageBuffer, 32); this.engine.CreateFile(folder + cacheFolder + ToString() + "_" + (imageIndex++) + ".tga", tga, (uint)tga.Length); String xml = "<font>\n"; for (int i = 0; i < cacheLetters; i++) { xml += "\t" + ((WinLetter)this.letters[i]).ToXml(i, imageIndexes[i]) + "\n"; } xml += "</font>"; byte[] bytes = Encoding.UTF8.GetBytes(xml); String cacheFileName = folder + cacheFolder + ToString() + ".xml"; this.engine.Logger.WriteLine(LogLevel.Info, "Caching font to: " + cacheFileName); this.engine.CreateFile(cacheFileName, bytes, (uint)bytes.Length); } }
/// <summary> /// Loads letter iamge from win32 font. /// </summary> /// <param name="cache">should the iamge be cached in tga, xml files.</param> /// <returns>loader letter information.</returns> protected internal override LetterInfo Load(bool cache) { LetterInfo result = null; #if WINDOWS IntPtr bitmapHandle = IntPtr.Zero; byte[] textureData = null; try { if (null == PlatformWindows.SelectFont(this.font.fontRenderingDisplayContext, this.font.fontHandle)) { throw new Exception(); } TextMetric tm = new TextMetric(); if (false == PlatformWindows.GetTextMetrics(this.font.fontRenderingDisplayContext, out tm)) { throw new Exception(); } if (PlatformWindows.GDI_ERROR == PlatformWindows.SetTextAlign(this.font.fontRenderingDisplayContext, PlatformWindows.TA_LEFT | PlatformWindows.TA_TOP | PlatformWindows.TA_UPDATECP)) { throw new Exception(); } ABC[] abc = new ABC[1]; if (false == PlatformWindows.GetCharABCWidths(this.font.fontRenderingDisplayContext, (uint)this.character, (uint)this.character, abc)) { throw new Exception(); } this.offsetX = abc[0].abcA; this.width = (int)(abc[0].abcB + abc[0].abcC); BitmapInfo header = new BitmapInfo(); header.bmiHeader.biSize = Marshal.SizeOf(header); GlyphMetrics metrics = new GlyphMetrics(); MAT2 identity = new MAT2(); identity.eM12.value = 0; identity.eM21.value = 0; identity.eM11.value = 1; identity.eM22.value = 1; if (PlatformWindows.GDI_ERROR == PlatformWindows.GetGlyphOutline(this.font.fontRenderingDisplayContext, this.character, PlatformWindows.GGO_METRICS, out metrics, 0, IntPtr.Zero, ref identity)) { throw new Exception(); } header.bmiHeader.biWidth = metrics.gmBlackBoxX; header.bmiHeader.biHeight = -1 * metrics.gmBlackBoxY; header.bmiHeader.biPlanes = 1; header.bmiHeader.biBitCount = 32; header.bmiHeader.biCompression = 0;// BI_RGB; byte[] bitmapData = null; IntPtr bitmapDataPointer = IntPtr.Zero; bitmapHandle = PlatformWindows.CreateDIBSection(this.font.fontRenderingDisplayContext, ref header, /*DIB_RGB_COLORS*/0, out bitmapDataPointer, IntPtr.Zero, 0); if (IntPtr.Zero == bitmapHandle) { int err = Marshal.GetLastWin32Error(); throw new Exception(); } if (null == PlatformWindows.SelectObject(this.font.fontRenderingDisplayContext, bitmapHandle)) { throw new Exception(); } if (PlatformWindows.CLR_INVALID == PlatformWindows.SetBkColor(this.font.fontRenderingDisplayContext, new RGB(new byte[] { 0, 0, 0 }).ToInt32())) { throw new Exception(); } if (PlatformWindows.CLR_INVALID == PlatformWindows.SetTextColor(this.font.fontRenderingDisplayContext, new RGB(new byte[] { 0xff, 0xff, 0xff }).ToInt32())) { throw new Exception(); } if (0 == PlatformWindows.SetBkMode(this.font.fontRenderingDisplayContext, PlatformWindows.OPAQUE)) { throw new Exception(); } if (false == PlatformWindows.MoveToEx(this.font.fontRenderingDisplayContext, 0 - abc[0].abcA, -1 * (tm.tmAscent - metrics.gmptGlyphOrigin.y), IntPtr.Zero)) { throw new Exception(); } this.offsetY = tm.tmAscent - metrics.gmptGlyphOrigin.y - tm.tmDescent - 1; String str = "" + this.character; RECT rect = new RECT(); if (false == PlatformWindows.ExtTextOut(this.font.fontRenderingDisplayContext, 0, 0, (uint)0, ref rect, str, 1, null)) { throw new Exception(); } if (0 == PlatformWindows.SetBkMode(this.font.fontRenderingDisplayContext, PlatformWindows.TRANSPARENT)) { throw new Exception(); } int bitmapWidth = header.bmiHeader.biWidth; int bitmapHeight = -header.bmiHeader.biHeight; int textureWidth = RoundToPowerOf2(bitmapWidth); int textureHeight = RoundToPowerOf2(bitmapHeight); bitmapData = new byte[bitmapWidth * bitmapHeight * 4]; Marshal.Copy(bitmapDataPointer, bitmapData, 0, bitmapWidth * bitmapHeight * 4); textureData = new byte[4 * textureWidth * textureHeight]; for (int j = 0; j < textureHeight; j++) { for (int i = 0; i < textureWidth; i++) { textureData[4 * (i + j * textureWidth) + 0] = 0xff; textureData[4 * (i + j * textureWidth) + 1] = 0xff; textureData[4 * (i + j * textureWidth) + 2] = 0xff; textureData[4 * (i + j * textureWidth) + 3] = (byte)((i >= bitmapWidth || j >= bitmapHeight) ? 0 : bitmapData[(i + bitmapWidth * j) * 4 + 2]); } } if (true == cache) { result = new LetterInfo(); result.width = bitmapWidth; result.height = bitmapHeight; result.bytes = textureData; result.textureWidth = textureWidth; result.textureHeight = textureHeight; } else { this.image = this.engine.CreateImage("Letter [" + this.character + "]", textureWidth, textureHeight, textureData); // this.internalImage = true; } this.textureWidth = textureWidth; this.textureHeight = textureHeight; } catch (Exception) { this.engine.DeleteImage(ref this.image); } if (null != bitmapHandle) { PlatformWindows.DeleteObject(bitmapHandle); } this.loaded = true; #endif return result; }