/// <summary> /// Build the Image Planes: Render the Fonts to the Images. /// </summary> /// <returns></returns> private void BuildImagePlanes() { ////////////////////////////////////////////////////////////////////////// // This canvas is to help get metrics ////////////////////////////////////////////////////////////////////////// Graphics canvas = Graphics.FromImage(mImagePages[0]); canvas.TextContrast = 0; Font aDrawFont = new Font(mFontComboBox.Text, mCurrentFontSize, mCurrentStyle, GraphicsUnit.Pixel); canvas.TextRenderingHint = mTextHint; ////////////////////////////////////////////////////////////////////////// // Some padding to stay away from the edges of the textures float fX = 2; float fY = 2; // Measurements for placing characters int nTexWidth = 0; int nMaxRowHeight = 0; int nBaseline = mCurrentFontSize * mAscent / emHeight; // mAscent: http://msdn.microsoft.com/en-us/library/xwf9s90b.aspx int nChars = 0; // int nTextures = 0; // current texture for placement float fMaxWidth = mTextureSize; // of texture/image plane float CHARACTERSPACING = (float)mCharacterSpacing; ////////////////////////////////////////////////////////////////////////// // For getting Kerning offsets using Graphics::MeasureCharacterRanges ////////////////////////////////////////////////////////////////////////// StringFormat aStringFormat = new StringFormat(StringFormat.GenericTypographic); //aStringFormat.Alignment = StringAlignment.Center; aStringFormat.LineAlignment = StringAlignment.Center; aStringFormat.Trimming = StringTrimming.None; ////////////////////////////////////////////////////////////////////////// // Test Image to scan for pixel boundaries ////////////////////////////////////////////////////////////////////////// const int aGlyphTextureSize = 128; Bitmap aGlyphImage = new Bitmap(aGlyphTextureSize, aGlyphTextureSize, PixelFormat.Format32bppArgb); Graphics aGlyphGraphic = Graphics.FromImage(aGlyphImage); CharacterRange[] characterRanges = { new CharacterRange(0, 1) }; aStringFormat.SetMeasurableCharacterRanges(characterRanges); ////////////////////////////////////////////////////////////////////////// // output array ArrayList emittedChars = new ArrayList(); Color aTestColor = Color.FromArgb(0xFF, 0, 0, 0); uint color = 0xff000000; SolidBrush blackBrush = new SolidBrush(Color.Black); SolidBrush whiteBrush = new SolidBrush(Color.White); RectangleF glyphTexRect = new RectangleF(0, 0, aGlyphTextureSize, aGlyphTextureSize); Rectangle r = new Rectangle(0, 0, aGlyphImage.Width, aGlyphImage.Height); aGlyphGraphic.TextRenderingHint = mTextHint; foreach (Char c in mCharaterSet) { // Get an idea of character width/height float aCharacterWidth = 0; // TextRenderer.MeasureText(c.ToString(), aDrawFont, new Size(0, 0), TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter | TextFormatFlags.Internal | TextFormatFlags.NoPadding | TextFormatFlags.NoFullWidthCharacterBreak).Width; float aCharacterHeight = aDrawFont.Height + mTopPadding + mBottomPadding; float advanceXShift = 0; // additional x correction fix for character float advance = 0; ////////////////////////////////////////////////////////////////////////// // This next section is necessary to get the best // advancing posible. Basically, it measures the exact pixel widths of // the glyph. (JPOAG) // Speed optimized (dmbreaker) ////////////////////////////////////////////////////////////////////////// { Char aTestChar = (c == ' ') ? '-' : c; aGlyphGraphic.Clear(Color.Black); //aGlyphGraphic.FillRectangle(blackBrush, 0, 0, aGlyphTextureSize, aGlyphTextureSize); aGlyphGraphic.DrawString(aTestChar.ToString(), aDrawFont, whiteBrush, glyphTexRect, aStringFormat); int aLastX = 0; int aFirstX = aGlyphTextureSize; BitmapData bmd = aGlyphImage.LockBits(r, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); unsafe // unsafe, but much faster { int stride = bmd.Stride; uint alpha_mask = 0xff000000; byte *ptr = (byte *)(void *)bmd.Scan0; { for (int aY = 0; aY < aGlyphTextureSize; aY++) { byte *row = ptr + (aY * stride); uint *tmpP = (uint *)row; for (int aX = 0; aX < aGlyphTextureSize; aX++) { //uint pixel = ((uint*)row)[aX]; uint pixel = *tmpP; if (color != pixel) { uint alpha = (pixel & alpha_mask) >> 24; if (alpha >= 16) { if (aX > aLastX) { aLastX = aX; } if (aX < aFirstX) { aFirstX = aX; } } } ++tmpP; } } } } aGlyphImage.UnlockBits(bmd); if ((aLastX - aFirstX) > 0) { advance = (aLastX - aFirstX + 1 > 0) ? aLastX - aFirstX + 1 : 0; advanceXShift = aFirstX; } } aCharacterWidth = advance + mRightPadding + mLeftPadding; if ((fX + aCharacterWidth) > fMaxWidth) // Row full, go to next line { if (((long)fX) > nTexWidth) { nTexWidth = (int)fX; } fX = 2.0f; fY += (float)nMaxRowHeight + CHARACTERSPACING; } if ((fY + nMaxRowHeight) > fMaxWidth) // Image Graphic full, go to next Page { if (nTextures < (KTEXT_MAXPAGES - 1)) { /* Create another texture */ nTextures++; fX = 2; fY = 2; nMaxRowHeight = 0; } else { /* Out of texture pages; stop adding characters */ // K_LOG("KTrueText: %s: maximum graphics per font reached, some characters will be missing, WARNING.", _szFileName); break; } } KTrueTextChar lpChar = new KTrueTextChar(); /* Store location */ lpChar.c = c; lpChar.nGraphic = nTextures; lpChar.sx1 = fX; lpChar.sy1 = fY; lpChar.sx2 = fX + (float)aCharacterWidth; lpChar.sy2 = fY + (float)aCharacterHeight; lpChar.fXOffset = (float)-mLeftPadding; lpChar.fYOffset = (float)-mTopPadding; lpChar.fWidth = (float)advance + mLeftPadding; // advancing width lpChar.fXFix = advanceXShift; nChars++; emittedChars.Add(lpChar); if (aCharacterHeight > nMaxRowHeight) { nMaxRowHeight = ((int)aCharacterHeight + (int)CHARACTERSPACING); } // advance placement cursor fX += (float)(aCharacterWidth + CHARACTERSPACING); } ////////////////////////////////////////////////////////////////////////// // Clear unused image planes so they don't draw in picture boxes ////////////////////////////////////////////////////////////////////////// for (int n = nTextures + 1; n < KTEXT_MAXPAGES; n++) { Graphics aTempCanvas = Graphics.FromImage(mImagePages[n]); aTempCanvas.Clear(Color.Transparent); } ////////////////////////////////////////////////////////////////////////// // Prepare Image Planes ////////////////////////////////////////////////////////////////////////// SetupGraphicPlanes(); Graphics[] aGraphicArray = new Graphics[KTEXT_MAXPAGES]; for (int i = 0; i < KTEXT_MAXPAGES; i++) { aGraphicArray[i] = Graphics.FromImage(mImagePages[i]); aGraphicArray[i].TextContrast = 0; aGraphicArray[i].TextRenderingHint = mTextHint; } /* Second pass: render characters to pixel buffer */ foreach (KTrueTextChar lpChar in emittedChars) { if (lpChar.nGraphic >= KTEXT_MAXPAGES || lpChar.nGraphic < 0) { continue; } RectangleF aBoundsRect = new RectangleF(lpChar.sx1, lpChar.sy1, (lpChar.sx2 - lpChar.sx1), (lpChar.sy2 - lpChar.sy1)); RectangleF aDrawRect = new RectangleF(lpChar.sx1 + mLeftPadding - lpChar.fXFix, lpChar.sy1 + mTopPadding, (lpChar.sx2 - lpChar.sx1) - mLeftPadding - mRightPadding, (lpChar.sy2 - lpChar.sy1) - mTopPadding - mBottomPadding); aGraphicArray[lpChar.nGraphic].DrawString(lpChar.c.ToString(), aDrawFont, new SolidBrush(mTextColor), aDrawRect, aStringFormat); // Pink Rectangle: DrawString Bounds if (mDebugRects.Checked) { aGraphicArray[lpChar.nGraphic].DrawRectangle(Pens.Fuchsia, Rectangle.Round(aBoundsRect)); } Region[] strRegions = aGraphicArray[lpChar.nGraphic].MeasureCharacterRanges(lpChar.c.ToString(), aDrawFont, aDrawRect, aStringFormat); RectangleF measureRect1 = strRegions[0].GetBounds(canvas); // Yellow Rectangle: Tight Bounding box for character if (mDebugRects.Checked) { aGraphicArray[lpChar.nGraphic].DrawRectangle(Pens.Yellow, Rectangle.Round(measureRect1)); } } ////////////////////////////////////////////////////////////////////////// // should cause redraw ////////////////////////////////////////////////////////////////////////// mPictureBox1.Image = mImagePages[0]; mPictureBox2.Image = mImagePages[1]; mPictureBox3.Image = mImagePages[2]; mPictureBox4.Image = mImagePages[3]; mPictureBox5.Image = mImagePages[4]; mPictureBox6.Image = mImagePages[5]; mPictureBox7.Image = mImagePages[6]; mPictureBox8.Image = mImagePages[7]; }
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { /************************************************************************/ /* Step 1: Ensure the Image planes are built without debug Rects */ /************************************************************************/ ////////////////////////////////////////////////////////////////////////// // This canvas is to help get metrics ////////////////////////////////////////////////////////////////////////// Graphics canvas = Graphics.FromImage(mImagePages[0]); canvas.TextContrast = 0; Font aDrawFont = new Font(mFontName, mCurrentFontSize, mCurrentStyle, GraphicsUnit.Pixel); canvas.TextRenderingHint = mTextHint; ////////////////////////////////////////////////////////////////////////// // Some padding to stay away from the edges of the textures float fX = 2; float fY = 2; // Measurements for placing characters int nTexWidth = 0; int nMaxRowHeight = 0; int nBaseline = mCurrentFontSize * mAscent / emHeight; // Ascent: http://msdn.microsoft.com/en-us/library/xwf9s90b.aspx int nDescent = mCurrentFontSize * mDescent / emHeight; // Descent: http://msdn.microsoft.com/en-us/library/xwf9s90b.aspx int nLineSpacing = mCurrentFontSize * mLineSpacing / emHeight; // Descent: http://msdn.microsoft.com/en-us/library/xwf9s90b.aspx int nChars = 0; // int nTextures = 0; // current texture for placement float fMaxWidth = mTextureSize; // of texture/image plane float CHARACTERSPACING = (float)mCharacterSpacing; ////////////////////////////////////////////////////////////////////////// // For getting Kerning offsets using Graphics::MeasureCharacterRanges ////////////////////////////////////////////////////////////////////////// StringFormat aStringFormat = new StringFormat(StringFormat.GenericTypographic); //aStringFormat.Alignment = StringAlignment.Center; aStringFormat.LineAlignment = StringAlignment.Center; aStringFormat.Trimming = StringTrimming.None; CharacterRange[] characterRanges = { new CharacterRange(0, 1) }; aStringFormat.SetMeasurableCharacterRanges(characterRanges); ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // Test Image to scan for pixel boundaries ////////////////////////////////////////////////////////////////////////// const int aGlyphTextureSize = 128; Bitmap aGlyphImage = new Bitmap(aGlyphTextureSize, aGlyphTextureSize, PixelFormat.Format32bppArgb); Graphics aGlyphGraphic = Graphics.FromImage(aGlyphImage); ////////////////////////////////////////////////////////////////////////// // output array ArrayList emittedChars = new ArrayList(); float aTotalCount = (float)mCharaterSet.Length * 3; float aCurrentCount = 0; Color aTestColor = Color.FromArgb(0xFF, 0, 0, 0); uint color = 0xff000000; SolidBrush blackBrush = new SolidBrush(Color.Black); SolidBrush whiteBrush = new SolidBrush(Color.White); RectangleF glyphTexRect = new RectangleF(0, 0, aGlyphTextureSize, aGlyphTextureSize); Rectangle r = new Rectangle(0, 0, aGlyphImage.Width, aGlyphImage.Height); aGlyphGraphic.TextRenderingHint = mTextHint; foreach (Char c in mCharaterSet) { // Get an idea of character width/height float aCharacterWidth = 0; float aCharacterHeight = aDrawFont.Height + mTopPadding + mBottomPadding; float advanceXShift = 0; // additional x correction fix for character float advance = 0; ////////////////////////////////////////////////////////////////////////// // This next section is necessary to get the best // advancing posible. Basically, it measures the exact pixel widths of // the glyph. (JPOAG) // Speed optimized (dmbreaker) ////////////////////////////////////////////////////////////////////////// { Char aTestChar = (c == ' ') ? '-' : c; aGlyphGraphic.Clear(Color.Black); //aGlyphGraphic.FillRectangle(blackBrush, 0, 0, aGlyphTextureSize, aGlyphTextureSize); aGlyphGraphic.DrawString(aTestChar.ToString(), aDrawFont, whiteBrush, glyphTexRect, aStringFormat); int aLastX = 0; int aFirstX = aGlyphTextureSize; BitmapData bmd = aGlyphImage.LockBits(r, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); unsafe // unsafe, but much faster { int stride = bmd.Stride; uint alpha_mask = 0xff000000; byte *ptr = (byte *)(void *)bmd.Scan0; { for (int aY = 0; aY < aGlyphTextureSize; aY++) { byte *row = ptr + (aY * stride); uint *tmpP = (uint *)row; for (int aX = 0; aX < aGlyphTextureSize; aX++) { uint pixel = *tmpP; if (color != pixel) { uint alpha = (pixel & alpha_mask) >> 24; if (alpha >= 16) { if (aX > aLastX) { aLastX = aX; } if (aX < aFirstX) { aFirstX = aX; } } } ++tmpP; } } } } aGlyphImage.UnlockBits(bmd); if ((aLastX - aFirstX) > 0) { advance = (aLastX - aFirstX + 1 > 0) ? aLastX - aFirstX + 1 : 0; advanceXShift = aFirstX; } } aCharacterWidth = advance + mRightPadding + mLeftPadding; if ((fX + aCharacterWidth) > fMaxWidth) // Row full, go to next line { if (((long)fX) > nTexWidth) { nTexWidth = (int)fX; } fX = 2.0f; fY += (float)nMaxRowHeight + CHARACTERSPACING; } if ((fY + nMaxRowHeight) > fMaxWidth) // Image Graphic full, go to next Page { if (nTextures < (KTEXT_MAXPAGES - 1)) { /* Create another texture */ nTextures++; fX = 2f; fY = 2; nMaxRowHeight = 0; } else { /* Out of texture pages; stop adding characters */ // K_LOG("KTrueText: %s: maximum graphics per font reached, some characters will be missing, WARNING.", _szFileName); break; } } KTrueTextChar lpChar = new KTrueTextChar(); /* Store location */ lpChar.c = c; lpChar.nGraphic = nTextures; lpChar.sx1 = fX; lpChar.sy1 = fY; lpChar.sx2 = fX + (float)aCharacterWidth; lpChar.sy2 = fY + (float)aCharacterHeight; lpChar.fXOffset = (float)-mLeftPadding; lpChar.fYOffset = (float)-mTopPadding; lpChar.fWidth = (float)advance + mLeftPadding; // advancing width lpChar.fXFix = advanceXShift; nChars++; emittedChars.Add(lpChar); if (aCharacterHeight > nMaxRowHeight) { nMaxRowHeight = ((int)aCharacterHeight + (int)CHARACTERSPACING); } // advance placement cursor fX += (float)(aCharacterWidth + CHARACTERSPACING); aCurrentCount++; backgroundWorker1.ReportProgress((int)(aCurrentCount / aTotalCount * 100.0f)); } int nGeneratedPlanes = nTextures + 1; ////////////////////////////////////////////////////////////////////////// // Clear unused image planes so they don't draw in picture boxes ////////////////////////////////////////////////////////////////////////// for (int n = nTextures + 1; n < KTEXT_MAXPAGES; n++) { Graphics aTempCanvas = Graphics.FromImage(mImagePages[n]); aTempCanvas.Clear(Color.Transparent); } ////////////////////////////////////////////////////////////////////////// // Prepare Image Planes ////////////////////////////////////////////////////////////////////////// SetupGraphicPlanes(); Graphics[] aGraphicArray = new Graphics[KTEXT_MAXPAGES]; for (int i = 0; i < KTEXT_MAXPAGES; i++) { aGraphicArray[i] = Graphics.FromImage(mImagePages[i]); aGraphicArray[i].TextContrast = 0; aGraphicArray[i].TextRenderingHint = mTextHint; } /* Second pass: render characters to pixel buffer */ foreach (KTrueTextChar lpChar in emittedChars) { if (lpChar.nGraphic >= KTEXT_MAXPAGES || lpChar.nGraphic < 0) { continue; } RectangleF aBoundsRect = new RectangleF(lpChar.sx1, lpChar.sy1, (lpChar.sx2 - lpChar.sx1), (lpChar.sy2 - lpChar.sy1)); RectangleF aDrawRect = new RectangleF(lpChar.sx1 + mLeftPadding - lpChar.fXFix, lpChar.sy1 + mTopPadding, (lpChar.sx2 - lpChar.sx1) - mLeftPadding - mRightPadding, (lpChar.sy2 - lpChar.sy1) - mTopPadding - mBottomPadding); aGraphicArray[lpChar.nGraphic].DrawString(lpChar.c.ToString(), aDrawFont, new SolidBrush(mTextColor), aDrawRect, aStringFormat); /* */ if (mDebugRectsChecked) { RectangleF aAdvanceRect = new RectangleF(lpChar.sx1 + mLeftPadding, lpChar.sy1 + mTopPadding, (lpChar.sx2 - lpChar.sx1) - mLeftPadding - mRightPadding, (lpChar.sy2 - lpChar.sy1) - mTopPadding - mBottomPadding); aGraphicArray[lpChar.nGraphic].DrawRectangle(Pens.Fuchsia, Rectangle.Round(aAdvanceRect)); } /* */ aCurrentCount++; backgroundWorker1.ReportProgress((int)(aCurrentCount / aTotalCount * 100.0f)); } /************************************************************************/ /* Part B: Save Image Planes to Files */ /************************************************************************/ ////////////////////////////////////////////////////////////////////////// // First: Validate Output Directory. ////////////////////////////////////////////////////////////////////////// String exePath = Assembly.GetExecutingAssembly().Location; int lastSlash = exePath.LastIndexOf('\\'); exePath = exePath.Remove(lastSlash + 1); String aSavePath = exePath; if (mDirectoryPathText.Text != "" && mDirectoryPathText.Text != ".\\") { aSavePath = mDirectoryPathText.Text; } String aFileExt = ".png"; ImageFormat anImageFormat = ImageFormat.Png; switch (mImageFormatIndex) { case 0: aFileExt = ".png"; anImageFormat = ImageFormat.Png; break; case 1: aFileExt = ".bmp"; anImageFormat = ImageFormat.Bmp; break; case 2: aFileExt = ".jpg"; anImageFormat = ImageFormat.Jpeg; break; case 3: aFileExt = ".tiff"; anImageFormat = ImageFormat.Tiff; break; case 4: aFileExt = ".gif"; anImageFormat = ImageFormat.Gif; break; } String anImageFileName = mImageFileNameEditBox.Text; if (!anImageFileName.Contains("%d")) { anImageFileName += "_page%d"; } anImageFileName = anImageFileName.Replace("%d", "{0:d}"); ArrayList anImageFileNameArray = new ArrayList(); if (mWriteImagesChecked) { for (int i = 0; i < nGeneratedPlanes; i++) { String anImageOutputFileName = String.Format("{0}{1}{2}", aSavePath, String.Format(anImageFileName, i), aFileExt); anImageFileNameArray.Add(String.Format("{0}{1}", String.Format(anImageFileName, i), aFileExt)); FileStream anOutFile = new FileStream(anImageOutputFileName, FileMode.Create); mImagePages[i].Save(anOutFile, anImageFormat); anOutFile.Close(); } } /************************************************************************/ /* Step the Last: Save Descriptor */ /************************************************************************/ if (mWriteDescriptorChecked) { Encoding aTextEncoding = Encoding.UTF8; switch (mDescriptorEncodingIndex) { case 0: aTextEncoding = Encoding.ASCII; break; case 1: aTextEncoding = Encoding.UTF8; break; case 2: aTextEncoding = Encoding.Unicode; break; case 3: aTextEncoding = Encoding.Unicode; break; case 4: aTextEncoding = Encoding.BigEndianUnicode; break; } // Sexy XML Format if (mDescriptorFormatIndex == 0) { String anXMLFileName = aSavePath + mDescriptorFileNameEditBox.Text + ".xml"; XmlTextWriter aWriter = new XmlTextWriter(anXMLFileName, aTextEncoding); aWriter.IndentChar = '\t'; aWriter.Formatting = Formatting.Indented; aWriter.WriteComment("Sexy-Kanji Font AutoGenerated by Kanji Font Builder (JPoag)"); aWriter.WriteStartElement("KanjiFont"); aWriter.WriteAttributeString("name", mCurrentFontFamily.Name); aWriter.WriteAttributeString("style", mCurrentStyle.ToString()); aWriter.WriteAttributeString("size", mCurrentFontSize.ToString()); aWriter.WriteAttributeString("kerning", mGlobalKerning.ToString()); aWriter.WriteAttributeString("ascent", nBaseline.ToString()); aWriter.WriteAttributeString("descent", nDescent.ToString()); aWriter.WriteAttributeString("height", aDrawFont.Height.ToString()); aWriter.WriteAttributeString("line_spacing", nLineSpacing.ToString()); aWriter.WriteStartElement("ImagePlanes"); aWriter.WriteAttributeString("count", nGeneratedPlanes.ToString()); for (int i = 0; i < anImageFileNameArray.Count; i++) { aWriter.WriteStartElement("Plane"); aWriter.WriteAttributeString("id", i.ToString()); aWriter.WriteAttributeString("path", anImageFileNameArray[i].ToString()); aWriter.WriteEndElement(/*"Plane"*/); } aWriter.WriteEndElement(/*ImagePlanes*/); if (emittedChars.Count > 0) { aWriter.WriteStartElement("FontTable"); foreach (KTrueTextChar lpChar in emittedChars) { aWriter.WriteStartElement("Char"); aWriter.WriteAttributeString("code", lpChar.c.ToString()); aWriter.WriteAttributeString("graphic_id", lpChar.nGraphic.ToString()); aWriter.WriteAttributeString("sx1", lpChar.sx1.ToString()); aWriter.WriteAttributeString("sy1", lpChar.sy1.ToString()); aWriter.WriteAttributeString("sx2", lpChar.sx2.ToString()); aWriter.WriteAttributeString("sy2", lpChar.sy2.ToString()); aWriter.WriteAttributeString("advance", lpChar.fWidth.ToString()); aWriter.WriteAttributeString("x_offset", lpChar.fXOffset.ToString()); aWriter.WriteAttributeString("y_offset", lpChar.fYOffset.ToString()); aWriter.WriteEndElement(/*Char*/); aCurrentCount++; backgroundWorker1.ReportProgress((int)(aCurrentCount / aTotalCount * 100.0f)); } aWriter.WriteEndElement(/*FontTable*/); } aWriter.WriteEndElement(/*"KanjiFont"*/); aWriter.Close(); } // Kanji Binary format else if (mDescriptorFormatIndex == 1) { String aKFNTFileName = aSavePath + mDescriptorFileNameEditBox.Text + ".kfnt"; StreamWriter aWriter = new StreamWriter(aKFNTFileName, false, Encoding.ASCII); aWriter.WriteLine("%ktext 1 {0:d} {1:d}", nGeneratedPlanes, emittedChars.Count); foreach (KTrueTextChar lpChar in emittedChars) { aWriter.WriteLine("{0:d} {1:d} {2:d} {3:d} {4:d} {5:d} {6:d} {7:d} {8:d} {9:d}", (ushort)lpChar.c, (long)lpChar.sx1, (long)lpChar.sy1, (long)lpChar.sx2, (long)lpChar.sy2, (long)lpChar.fWidth, (long)lpChar.sy2 - (long)lpChar.sy1, lpChar.nGraphic, (long)lpChar.fXOffset, (long)lpChar.fYOffset); aCurrentCount++; backgroundWorker1.ReportProgress((int)(aCurrentCount / aTotalCount * 100.0f)); } aWriter.Close(); } } // END: if (mWriteDescriptorCheckBox.Checked) } // END: backgroundWorker1_DoWork