Blog | Tag | Local | Guest | Login | Write |  RSS
Dot Font Generator

안녕하세요. 이번시간에는 TextBlock에 표시되는 글자를 Pixel단위로 쪼개어 Dot Font로 활용하는 방법에 대해서 소개해 드리겠습니다. 방법이야 여러가지가 있겠지만 저는 RenderTargetBitmp을 사용해 텍스트의 이미지를 생성하고 해당 이미지를 MemoryStream으로 복사 한 뒤 픽셀 단위로 문자열에 해당하는 부분을 검출하는 방법을 사용했습니다. 아래는 시연동영상입니다.



Generate Text에 변환할 문자를 입력하고 적당히 Spacing 을 조절한 뒤 Generate 버튼을 누르면 변환이 이루어집니다. Result 부분에는 변환된 점의 좌표가 출력되면 Preview에는 해당 점을 직접 그렸을 경우 미리보기가 가능합니다. 소스코드는 아래와 같습니다. 이번 예제에서의 XAML은 대부분 UI구성과 관련된 내용만 포함 하고 있으므로 설명은 하지 않고 C#코드에서 실제 Dot Font를 생성하는 부분만 설명을 하도록 하겠습니다. (전체 소스코드는 첨부하도록 하겠습니다.)

아래는 C#코드입니다.

  1. public partial class Window1 : Window   
  2. {   
  3.     public Window1()   
  4.     {   
  5.         InitializeComponent();   
  6.     }   
  7.   
  8.     private void BtnGenerate_Click(object sender, RoutedEventArgs e)   
  9.     {   
  10.   
  11.         ResultTextBlock.Text = TxtText.Text;   
  12.         List<POINT> Result = GenerateDotFont(ResultTextBlock, (int)SliderSpacing.Value);   
  13.         DrawDotFont(Result);   
  14.         LstResult.ItemsSource = Result;   
  15.               
  16.     }   
  17.   
  18.     public void DrawDotFont(List<POINT> DotFont)   
  19.     {   
  20.   
  21.         DrawingVisual DrawingVisual = new DrawingVisual();   
  22.         DrawingContext DrawingContext = DrawingVisual.RenderOpen();   
  23.   
  24.         foreach (Point Dot in DotFont)   
  25.         {   
  26.             DrawingContext.DrawRectangle(Brushes.White, null,   
  27.                 new Rect(Dot.X,Dot.Y,2,2));   
  28.         }   
  29.   
  30.         DrawingContext.Close();   
  31.   
  32.         RenderTargetBitmap bmp = new RenderTargetBitmap((int)ResultTextCanvas.Width, (int)ResultTextCanvas.Height, 96, 96, PixelFormats.Pbgra32);   
  33.         bmp.Render(DrawingVisual);   
  34.         ResultTextCanvas.Background = new ImageBrush(bmp);   
  35.   
  36.     }   
  37.   
  38.   
  39.   
  40.     public List<POINT> GenerateDotFont(TextBlock ReferenceTextBlock,int Space)   
  41.     {   
  42.   
  43.         int Width = (int)ReferenceTextBlock.Width;   
  44.         int Height = (int)ReferenceTextBlock.Height;   
  45.   
  46.         FormattedText text = new FormattedText(ReferenceTextBlock.Text,   
  47.                 CultureInfo.CurrentCulture,   
  48.                 FlowDirection.LeftToRight,   
  49.                 new Typeface(ReferenceTextBlock.FontFamily, ReferenceTextBlock.FontStyle, ReferenceTextBlock.FontWeight, ReferenceTextBlock.FontStretch),   
  50.                 ReferenceTextBlock.FontSize, ReferenceTextBlock.Foreground);   
  51.   
  52.         DrawingVisual DrawingVisual = new DrawingVisual();   
  53.         DrawingContext DrawingContext = DrawingVisual.RenderOpen();   
  54.         DrawingContext.DrawText(text, new Point(0, 0));   
  55.         DrawingContext.Close();   
  56.   
  57.         RenderTargetBitmap bmp = new RenderTargetBitmap(Width, Height, 96, 96, PixelFormats.Pbgra32);   
  58.         bmp.Render(DrawingVisual);   
  59.   
  60.         BmpBitmapEncoder Encoder = new BmpBitmapEncoder();   
  61.         Encoder.Frames.Add(BitmapFrame.Create(bmp));   
  62.   
  63.         MemoryStream MemoryStream = new MemoryStream();   
  64.         Encoder.Save(MemoryStream);   
  65.   
  66.         // Header Skip   
  67.         MemoryStream.Seek(54, SeekOrigin.Begin);   
  68.   
  69.         byte[] Buff = new byte[MemoryStream.Length - 54];   
  70.         MemoryStream.Read(Buff, 0, (int)(MemoryStream.Length - 54));   
  71.   
  72.         List<POINT> Result = new List<POINT>();   
  73.         int HeightOffSet = 0;   
  74.         for (int Y = 0; Y < Height; Y+=Space)   
  75.         {   
  76.             HeightOffSet = Width * Y;   
  77.   
  78.             for (int X = 0; X < Width; X+=Space)   
  79.             {   
  80.                 if (Buff[((HeightOffSet + X) << 2 )] == 255)   
  81.                     Result.Add(new Point(X, Height - Y));   
  82.   
  83.             }   
  84.         }   
  85.   
  86.         return Result;   
  87.   
  88.     }   
  89.   
  90. }  

코드를 보면 2가지 함수를 확인 하실 수 있을 것입니다. DrawDotFont는 List<Point> Type의 Dot Data가 포함된 DotList를 사용하여 Canvas에 DotFont를 그려주는 역할을 하며 GenerateDotFont는 ReferenceTextBlock의 정보에 맞춰 DotFont를 생성하는 역할을 합니다.

우리가 자세히 살펴봐야하는 함수는 GenerateDotFont 인데요, 아래와 같이 총 5개의 과정을 통해 시작됩니다.

1. DrawingVisual과 DrawingContext를 사용해서 TextVisual을 생성합니다.
2. RenderTargetBitmap을 사용하여 DrawingVisual객체를 렌더링 합니다.
3. 렌더링한 RenderTargetBitmap객체를 BmpBitmapEncoder를 사용하여 Bmp형식으로 변환합니다.
4. MemoryStream을 사용하여 Bmp로 변환된 객체의 픽셀정보를 가져옵니다.
5. Spacing만큼 픽셀을 건너 뛰면서 해당 픽셀이 글자 부분인지를 체크합니다.

다소 접하기 어려운 Class를 활용하는 예제라서 처음 보기에는 어려워 보일 수 있는 코드지만, 한줄한줄 따라가다보면 쉽게 이해하실 수 있을것입니다. 그럼 오늘 포스팅은 여기까지 하고 기타 질문은 리플이나 메일로 보내주시면 답변드리겠습니다.
(조만간 Dot Font를 활용한 예제를 올리도록 하겠습니다.)

전체 소스코드입니다.