Forum: Mikrocontroller und Digitale Elektronik C Code für die Ausgabe diverser Fonts auf einem GLCD


von Felix (Gast)


Angehängte Dateien:

Lesenswert?

Hallo liebe Foren Teilnehmer,

Ich bin noch nicht richtig sattelfest in der Programmiersprache C und 
wage mich derzeit an ein GLCD heran. Die meisten Grundfunktionen passen 
soweit aber nun hänge ich an der Programmierung entsprechender Schriften 
und möchte das Rad auch nicht neu erfinden.

Ich habe im Netz folgende Software gefunden:
http://www.eran.io/the-dot-factory-an-lcd-font-and-image-generator/

Mit dieser Software kann man beliebeige Fonts aus dem PC in C Arrays 
wandeln, siehe auch Beispiel im Anhang. Soweit so gut. Nun kommt mein 
Problem. Der erste Teil, die Fonts sind mir noch klar. In meinem GLCD 
Code habe ich ja eine set/clear Pixel Funktion und wenn ich das Array 
Character für Character lese dann setze ich eben ein Pixel wenn die BIT 
Position 1 ist und wenn 0 dann eben nicht. Was ich nicht hinbekomme ist 
der Umstand das die Zeichen eine variable Breite haben. Die Information 
sitzt ja in...

const FONT_CHAR_INFO georgia_12ptDescriptors[] =

aber wie kann ich das jetzt alles zusammenbauen ? Ich bräuchte einen 
Lösungsansatz wie ich aus dem Array nun alles zusammenbekomme so das ich 
die Schrift, genauso wie im Array dargestellt, ausgeben kann.

Danke für eure Hilfe

Lieben Gruß

von Georg (Gast)


Lesenswert?

Felix schrieb:
> Ich bräuchte einen
> Lösungsansatz wie ich aus dem Array nun alles zusammenbekomme

Wenn du schon pixelweise arbeitest (ist halt langsam), dann setzt du 
eben nacheinander alle Pixel eines Zeichens, und dann gehst du nach 
rechts um die Breite des Zeichens plus Abstand (z.B. 1 Pixel). Und für 
die nächste Zeile nach unten um die Höhe des Fonts plus Abstand (es sind 
ja alle Zeichen gleich hoch). Jeweils bis das Display aufhört.

Georg

von Felix (Gast)


Lesenswert?

Hallo Georg,

>> Wenn du schon pixelweise arbeitest (ist halt langsam)
ich bin für jeden Vorschlag offen. Wie würdest du es machen ?

von Harry L. (mysth)


Lesenswert?

Felix schrieb:
> Hallo Georg,
>
>>> Wenn du schon pixelweise arbeitest (ist halt langsam)
> ich bin für jeden Vorschlag offen. Wie würdest du es machen ?

Mach es erstmal Pixel für Pixel!
Wenn du das verstanden hast, verstehst du auch, wie man sowas optimieren 
kann.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Felix schrieb:
> Der erste Teil, die Fonts sind mir noch klar. In meinem GLCD Code habe
> ich ja eine set/clear Pixel Funktion und wenn ich das Array Character
> für Character lese dann setze ich eben ein Pixel wenn die BIT Position
> 1 ist und wenn 0 dann eben nicht.

Funktioniert das denn schon für ein einzelnes Zeichen?

: Bearbeitet durch Moderator
von foobar (Gast)


Lesenswert?

In dem Descriptors-Array hast du für jedes Zeichen drinstehen, wie breit 
es ist (in Pixel/Bits) und ab wo es im Bitmaps-Array anfängt.  Wenn es 
mehr als 8 Pixel/Bits breit ist, werden pro Zeile 2 Bytes (vermutlich 
bei mehr als 16 Pixel 3 Bytes, etc) benutzt.

Das einzig verwirrende ist die Höhe: In deinem Beispiel steht in der 
Info-Struct als Höhe "2", die Zeichen selbst scheinen aber 15 Zeilen 
hoch zu sein.  Irgendwas stimmt da nicht.

Schnell runter getippt (die struct-element-Namen hab ich mir 
ausgedacht):
1
unsigned
2
draw_char(FONT_INFO *f, unsigned sx, unsigned sy, unsigned c)
3
{
4
   unsigned height, width, x, y, bits;
5
   unsigned char *p;
6
7
   if (c == ' ')
8
      return f->space_width;
9
10
   if (c < f->first || c > f->last)
11
      return 0;
12
13
   c -= f->first;
14
   height = f->height;
15
   width = f->descriptors[c].width;
16
   p = f->bitmap + f->desciptors[c].offset;
17
18
   bits = 0; // avoid warning
19
   for (y = 0; y < height; ++y)
20
      for (x = 0; x < width; ++x)
21
      {
22
         if (x % 8 == 0)
23
            bits = *p++;
24
         if (bits & 0x80)
25
            setpixel(sx + x, sy + y)
26
         bits <<= 1;
27
       }
28
29
    return width;
30
}

von W.S. (Gast)


Lesenswert?

Felix schrieb:
> aber wie kann ich das jetzt alles zusammenbauen ? Ich bräuchte einen
> Lösungsansatz...

Den gibt es doch schon seit einer gefühlten Ewigkeit. Lade dir hier im 
Forum die Lernbetty herunter, da findest du sowohl Fonts als auch ein 
dazu passendes GDI.

Ich hab mal dein Beispiel und die von dir genannte Seite angeschaut.

Nun, man kann es ja so machen, das Prinzip ist ähnlich dem, was ich vor 
etwa 7 Jahren bei der Lernbetty
Beitrag "Die Lernbetty: Die SwissBetty von Pollin als ARM-Evalboard"
gemacht habe - aber es ist sowohl Platzverschwendung als auch eine 
Umständlichkeit dadurch, daß zum einen die Glyphen immer 
verschwenderisch als volle Bytes gespeichert werden und zum anderen, daß 
jeweils mehrere Strukturen für nur einen Font herumfliegen: 
georgia_12ptBitmaps, georgia_12ptDescriptors und georgia_12ptFontInfo.

Ich hatte das damals anders gemacht: ein Font ist da ein einziger Block 
von Bytes, dies aber mit einer inneren Struktur:
- Zuerst der Font-Header, der enthält
  - HCap = Höhe der Capitals über der Schreiblinie
  - Height = Gesamthöhe
  - Ascent = Höhe über der Schreiblinie
  - Descnt = Tiefe unter der schreiblinie
  - Min und  Max = kleinstes und größtes enthaltenes Zeichen
- dann die Char-Header, die enthalten:
  - OffHi und OffLo = Offset des Glyphs im Font
  - DeltaX = zu zeichnende Zeichenbreite
  - DeltaY = zu zeichnende Zeichenhöhe
- dann alle Glyphs als Bitstreams, also ohne Align auf Bytegrenze.
Wie du sehen kannst, ist das Beispiel von dir so ziemlich exakt 
dasselbe, bloß daß die 3 Teile Fontheader, Charheaders, Glyphs stehen da 
in umgekehrter Reihenfolge und die Glyphs sind mit viel leerem Platz auf 
Bytes aufgerundet.

So, die Lernbetty ist jetzt etwa 7 Jahre her und dort ist auch eine 
passende Funktion dabei, die ein Zeichen im gewählten Font an beliebiger 
Stelle im Display-RAM zeichnet. Wichtig dabe ist, daß diese Funktion die 
tatsächliche Zeichenbreite zurückliefert, damit man Strings ausgeben 
kann, auch wenn der Font anders als Courier eben keine festen 
Zeichenbreiten hat.
Siehe auch:
https://www.mikrocontroller.net/attachment/291357/Fonts_machen.zip
https://www.mikrocontroller.net/attachment/301321/Fonts-machen-2.zip



Georg schrieb:
> Wenn du schon pixelweise arbeitest (ist halt langsam),...

Das ist hier ohne Alternative, denn letztendlich muß man immer 
pixelweise arbeiten, denn die Pixel müssen ja auf dem Display zu sehen 
sein.

Eine Alternative in Form von Adobe- oder TrueType-Fonts kann man auf 
einem µC vergessen. Das wäre sowohl von der CPU als auch von den hier 
üblichen GLCD's nicht wirklich machbar.

W.S.

von foobar (Gast)


Lesenswert?

> ... Platzverschwendung ...
> ... langsam ...

Erstmal überhaupt was hinbekommen, optimieren kann man später.

> muß man immer pixelweise arbeiten

Ich denke, Georg meinte, dass es schneller wäre, gleich mehrere Pixel in 
einem Rutsch (I2C-Kommando) zu bearbeiten.

> Wichtig dabe ist, daß diese Funktion die tatsächliche Zeichenbreite
> zurückliefert,

Ach so, ja, draw_string fehlte oben noch:
1
void
2
draw_string(FONT_INFO *f, unsigned sx, unsigned sy, unsigned char *s)
3
{
4
   while (*s)
5
      sx += draw_char(f, sx, sy, *s++);
6
}

von Philipp K. (philipp_k59)


Lesenswert?

Es kommt auch auf den GLCD chip an, manche erlauben aneienander gereihte 
PixelBytes, bzw erwarten diese sogar oder haben einen Char Buffer zum 
beschreiben.

Um die optimale Vorrgehensweise zu wählen oder allgemein einen guten Tip 
zu  müsste man wissen welcher Controller das ist.

von NichtWichtig (Gast)


Lesenswert?

Es gibt so viele Varianten wie man Displays ansteuern und Fonts 
vorhalten kann das es fast unmöglich ist da die Eingangsfrage mit DER 
Lösung zu beantworten.

Oft liegen Fonts so vor und Displays wollen es anders rum haben, dann 
ist immer Umrechnerei dazwischen nötig.

Will man Produkte in Umlauf bringen ist das Kopieren von Fonts zudem ein 
rechtliches Problem, mal eben einen WindowsFont zu "klauen" kann Ärger 
geben.

Je nach Rahmenbedingungen lohnt es sich das fertige Display Image 
komplett im Speicher des µC zu beschreiben und dann nur die geänderten 
Bereiche optimal schnell ans Display zu senden.

von Georg (Gast)


Lesenswert?

foobar schrieb:
> Ich denke, Georg meinte, dass es schneller wäre, gleich mehrere Pixel in
> einem Rutsch (I2C-Kommando) zu bearbeiten

So isses - aber bei LCDs ist sowieso in den meisten Fällen ein halbes 
oder ganzes Byte zu schreiben, also auf einmal 4 oder 8 Pixel 
nebeneinander. Wenn aber die Position dafür nicht mit einer 
Zeichengrenze übereinstimmt wird das kompliziert. I.A. ist es am 
einfachsten, die Pixel in einem RAM-Buffer zu setzen und diesen Buffer 
dann byteweise an das LCD zu senden.

Im konkreten Fall sollte der TO erst mal alles pixelweise machen, bis es 
funktioniert. Optimieren kann er das später, wenn er mehr Erfahrung hat 
und es überhaupt von Interesse ist.

Georg

von W.S. (Gast)


Lesenswert?

Philipp K. schrieb:
> Es kommt auch auf den GLCD chip an

Nein, eigentlich nicht.

Und warum nicht?

Nun, weil man, um sinnvoll ein Grafik-Display ausnutzen zu können, im 
RAM des µC einen Bildspeicher vorhalten sollte, auf den die diversen 
Grafikroutinen ihr Zeugs zeichnen. Die Übertragung von diesem 
Bildspeicher in's Display ist dabei eine völlig andere Ebene.

Auf diese Weise trennt man die Lowlevel-Angelegenheiten (z.B. Transport 
der Pixel per SPI o.ä. zum Display) von der nächst höheren Schicht (z.B. 
Kreise, Linie, Flächen zeichnen, Schrift zeichnen, Bilder zeichnen). 
Diese Schicht ist dann nur noch wenig hardwareabhängig (nur noch 
Pixelanzahl X und Y, sowie Anzahl der Farben). Und auf diese Schicht 
setzt dann die nächste Schicht auf: grafische Elemente sich darstellen 
lassen (Buttons, Frames, Labels, Edits, Checkboxen, usw.)

So. und aus alldem sieht man auch, daß es für grafische Displays eine 
gewisse Mindestgröße des µC gibt, unterhalb derer man so ein Display 
zwar prinzipiell ansteuern kann, aber nicht mehr sinnvoll verwenden 
kann, eben weil der µC damit bereits überfordert ist.

W.S.

von NichtWichtig (Gast)


Lesenswert?

So sieht's aus!

von Larry (Gast)


Lesenswert?

> Nein, eigentlich nicht.

Eigentlich schon. Mancher kann naemlich blitten.

Dann reduziert sich der Aufwand ein Zeichen zu schreiben auf:
- Komplettes Zeichen in den Off-Display-Speicher schreiben.
- Zeichen auf die Zielposition blitten.

Wenn der komplette Zeichensatz ins Off-Display-RAM passt,
muss man den natuerlich nur einmal kopieren.

von NichtWichtig (Gast)


Lesenswert?

Spannend: Zeig mal Beispiel-Displays die das können.

von W.S. (Gast)


Lesenswert?

NichtWichtig schrieb:
> Spannend

Ach nö.

Soweit ich mich erinnere, können die besseren Chips von Solomon sowas. 
Aber sowas ist umständlich. Man gibt die Koordinate vor, ebenso die 
Fensterbreite und -höhe. Dann muß man "bloß" noch die Pixel dort 
hineinfüllen, also entweder die Hintergrundfarbe oder die 
Vordergrundfarbe. Wenn die je 6 Bit RGB oder größer ist und der µC ein 8 
Bitter, dann wird das schon ein Klimmzug - selbst in Assembler. Wie das 
bei Solomon mit Palettenmodi aussieht, weiß ich momentan nicht. Ach, 
lies mal selber.

W.S.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.