2013-08-28 8 views
8

Zanim przejdę do napisu Graphics.DrawString(), chciałbym wiedzieć dokładnie, gdzie będzie każda postać - nie tylko jej przesunięcie i szerokość, ale także dokładne pole z wynurzenie/zejście w taki sposób, że mogę wykonać wykrywanie kolizji na poziomie postaci, zamiast korzystać z całej wysokości linii, którą daje mi Graphics.MeasureString().C# DrawString - zmień ramki ograniczające dla każdego znaku

Nie mogę znaleźć żadnych przykładów, które dokładnie zwracają obwiednię dla każdej postaci. Jak to zrobić w języku C#/GDI +?

Idealnie chciałabym dostać każdy z szare prostokąty jak w tym obrazie:

enter image description here

+0

gdzie jest malowany twój "znak"? Mam nadzieję, że nie jest to postać w "RichTextBox" lub "TextBox", ponieważ nie jest łatwo w ogóle radzić sobie z postaciami w polach tekstowych. –

+0

Zobacz http://www.codeproject.com/Articles/2118/Bypass-Graphics-MeasureString-limitations. Wygląda na to, że szukasz 'graphics.MeasureCharacterRanges'? –

+2

Znalazłem ten artykuł, ale MeasureCharacterRanges() nie zmienia się wraz z wysokością dla każdego znaku w łańcuchu - daje to całą wysokość linii dla każdego. – powlette

Odpowiedz

3

Nie sądzę, że to możliwe, aby dokładnie zmierzyć znaków w ciągu w GDI +. Nawet jeśli zapomnisz pozycjonowania podpikseli i kerningu itp., Co oznacza, że ​​znaki w narysowanym łańcuchu będą miały różne rozmiary w zależności od kontekstu w łańcuchu i pozycji na ekranie oraz ustawień ClearType, MeasureString ma zwyczaj dodawania wypełnienia i krówek, nie można łatwo uzyskać pożądanych informacji z niego.

Jedną z metod sortowania jest zmierzenie pierwszego znaku, następnie pierwszych dwóch znaków, a następnie pierwszych trzech znaków itd., Aby uzyskać przybliżony obraz sytuacji, w której wszystkie zostaną zakończone, gdy zostaną narysowane w jednym trafienie. Wtedy prawdopodobnie będziesz musiał zmierzyć każdy znak ponownie, aby określić jego wysokość. Jeśli MeasureString nie poda wysokości znaków, ale zamiast tego podaje wysokość czcionki, może być konieczne uciekanie się do renderowania każdego znaku poza bitmapę, a następnie skanowanie pikseli w celu ustalenia prawdziwej wysokości postaci.

W tym momencie może się okazać, że lepiej jest wybrać p/invoke off na API czcionki o niższym poziomie, które dostarcza potrzebnych informacji bezpośrednio z czcionki. Ale wtedy prawdopodobnie będziesz musiał użyć czegoś innego niż GDO +, aby go wyrenderować, ponieważ szanse na wyrównanie tego, co wypracowałeś, mogą nie być dobre.

(można powiedzieć, że mam problem zaufania z jakością off GDI + czcionki renderujący jeszcze?)

+0

Nie jesteś jedyny. Renderowanie czcionek GDI + to śmieci, dlatego nikt go nie używa, z wyjątkiem słabych programistów WinForm, którzy nie znają się na niczym. 'TextRenderer.DrawText' jest * znacznie * lepszą opcją. To opakowuje GDI, który uzyskał poprawny tekst od około 1987 roku. –

+0

@CodyGray 'biedni programiści WinForms, którzy nie wiedzą nic lepszego" - takie komentarze nie są pomocne i nie powinny być publikowane na SO. W przypadku konkretnych projektów WinForm może być lepszym wyborem niż inne, takie jak WPF. – deegee

+0

@deegee I * jestem * programistą WinForms. Nie lubię WPF i nadal używam WinForm. To nie był jeden z tych irytujących komentarzy na temat tego, jak WinForms jest do bani; W ogóle w to nie wierzę. Chodziło o to, że klasa 'Graphics' WinForms jest opakowaniem wokół GDI + (nie GDI) i że istnieje oczywiście nazwana metoda" DrawString ", którą programiści nie znają lepiej przy rysowaniu tekstu. To błąd, powinni oni używać nieczytelnej metody 'TextRenderer.DrawText', która owija GDI, aby narysować tekst. Po prostu nie wiedzą o tym, ponieważ są niejasne. Przepraszam, że wprowadzam w błąd. –

0

Załóż niewidzialną okno dla celów pomiarowych z dokładnie tej samej wielkości i czcionek ustawień, co masz w " prawdziwe "okno. Po tym można odliczyć poszczególne obwiednie bohaterów przez zmieszanie więcej danych:

  • prostokąt zaznaczenia indywidualnego charakteru, które mogą dać wskazówkę na lewą i prawą granice znaku. Niestety wysokość ta jest pełna wysokość wiersza
  • wysokości od danych z MeasureCharacterRanges() lub podobną techniką

zgadzam się z @Jason Williams chociaż. "dopełnienie i czynniki krówkowe" mogą nieco zniekształcić wyniki.

0

Jeśli wydajność nie jest krytyczna, myślę, że możesz uzyskać najdokładniejsze wyniki, konwertując znaki na ścieżki i badając je. Nie jestem pewien, w jaki sposób najlepiej poradzić sobie z problemami z kerningiem, ale w przypadku czcionek bez ligatur można by prawdopodobnie wyobrazić granice pól dla postaci w izolacji, mierzone względem ich punktów początkowych, a następnie określając punkty początkowe każdego znaku w kerningu strunowy. W przypadku czcionek z ligaturami poszczególne znaki mogą nie zawsze mieć różne glify. Na przykład, jeśli czcionka ma "ffi" ligaturę, słowo "offing" może zawierać tylko cztery glify: "o", "ffi", "n" i "g"; nie byłoby sensu pytać o obwiednię "i".

2

i made a method and got somethin like this

Oto kod

private void saykaPanel_Paint(object sender, PaintEventArgs e) 
    { 
     string saykaName = "Sayka"; 
     Font usedFont = new Font("Tahoma", 46); 

     int lastPoint = 0; 
     for (int i = 0; i < saykaName.Length; i++) 
     { 
      Rectangle rect = measureAlphabet(saykaName[i], usedFont); 
      e.Graphics.FillRectangle(Brushes.Gray, new Rectangle(lastPoint + rect.Left, rect.Top, rect.Width, rect.Height)); 
      e.Graphics.DrawString(saykaName[i].ToString(), usedFont, Brushes.Black, new PointF(lastPoint, 0)); 
      lastPoint += rect.Width; 

     }   
    } 

    Rectangle measureAlphabet(char alph, Font font) 
    { 
     SizeF size = CreateGraphics().MeasureString(alph.ToString(), font); 
     Bitmap bmp = new Bitmap((int)size.Width, (int)size.Height); 
     Graphics grp = Graphics.FromImage(bmp); 
     grp.FillRectangle(Brushes.White, 0, 0, bmp.Width, bmp.Height); 
     grp.DrawString(alph.ToString(), font, Brushes.Black, new PointF(0, 0)); 
     Bitmap img = (Bitmap)bmp; 
     int x = 0, y = 0, width = 0, height = 0; 

     for (int i = 0; i < img.Width;i++) 
      for (int j = 0; j < img.Height;j++) 
       if (img.GetPixel(i, j).B < 2) 
       { 
        x = i; 
        goto jmp1; 
       } 

    jmp1: ; 
     for (int i = img.Width - 1; i >= 0; i--) 
      for (int j = 0; j < img.Height; j++) 
       if (img.GetPixel(i, j).B < 2) 
       { 
        width = i - x; 
        goto jmp2; 
       } 

    jmp2: ; 
     for (int i = 0; i < img.Height; i++) 
      for (int j = 0; j < img.Width; j++) 
       if (img.GetPixel(j, i).B < 2) 
       { 
        y = i; 
        goto jmp3; 
       } 

    jmp3: ; 
     for (int i = img.Height - 1; i >= 0; i--) 
      for (int j = 0; j < img.Width; j++) 
       if (img.GetPixel(j, i).B < 2) 
       { 
        height = i - y; 
        goto jmp4; 
       } 

    jmp4: ; 
     Rectangle resultRectangle = new Rectangle(x, y, width, height); 
     return resultRectangle; 
    } 

. .

.

.

.

.

Mierzona pierwszy czarny punkt od lewej, a następnie w prawo, potem frm frm i frm dno .. Oceń, czy to pomaga ..

3

EDIT: Jest to rozwiązanie Kiedyś, opierając się na przykładzie Sayka @ i podstęp, aby uzyskać rzeczywistą szerokość char:

Jeśli rysunek ciąg w X, Y użyciu czcionki w grafice G1 można narysować każde char za pomocą tych prostokąty:

public IEnumerable<Rectangle> GetRectangles(Graphics g1) 
{ 
    int left = X; 
    foreach (char ch in word) 
    { 

     //actual width is the (width of XX) - (width of X) to ignore padding 
     var size = g1.MeasureString("" + ch, Font); 
     var size2 = g1.MeasureString("" + ch + ch, Font); 

     using (Bitmap b = new Bitmap((int)size.Width + 2, (int)size.Height + 2)) 
     using (Graphics g = Graphics.FromImage(b)) 
     { 
      g.FillRectangle(Brushes.White, 0, 0, size.Width, size.Height); 
      g.TextRenderingHint = g1.TextRenderingHint; 
      g.DrawString("" + ch, Font, Brushes.Black, 0, 0); 
      int top = -1; 
      int bottom = -1; 

      //find the top row 
      for (int y = 0; top < 0 && y < (int)size.Height - 1; y++) 
      { 
       for (int x = 0; x < (int)size.Width; x++) 
       { 
        if (b.GetPixel(x, y).B < 2) 
        { 
         top = y; 
        } 
       } 
      } 

      //find the bottom row 
      for (int y = (int)(size.Height - 1); bottom < 0 && y > 1; y--) 
      { 
       for (int x = 0; x < (int)size.Width - 1; x++) 
       { 
        if (b.GetPixel(x, y).B < 2) 
        { 
         bottom = y; 
        } 
       } 
      } 
      yield return new Rectangle(left, Y + top, (int)(size2.Width - size.Width), bottom - top); 
     } 
     left += (int)(size2.Width - size.Width); 
    } 

} 

wygląda następująco:

enter image description here

Powiązane problemy