Vektor-Font in C

Wechseln zu: Navigation, Suche
Eine PNG-Grafik für "Hallo Welt!" in den Zoom-Stufen 1-5, erstellt aus einer mit VEKFONT erzeugten PBM-Grafik.

von gjlayde

VEKFONT ist ein C-Modul zur Erzeugung von Vektor-Schriften. Ursprünglich wurde die Software für einen Mikrocontroller der AVR-Familie geschrieben, um in einer Scope-Uhr Zeichen und Texte auf die Röhre auszugeben. Die Software ist aber so allgemein gehalten, dass sie ebenso auf einem PC einsetzbar ist. So wurden die Beispiel-Grafiken, die in diesem Artikel zu sehen sind, nicht auf einem AVR erzeugt (was problemlos möglich wäre), sondern direkt auf einem PC.

Die Software ist in einem weiten Bereich via Defines konfigurierbar, so dass sie an die eigenen Bedürfnisse angepasst werden kann, ohne die Quelle ändern zu müssen.

Einsatzgebiet

Eine SVG-Grafik für "Hallo Welt!" in den Zoom-Stufen 1-3 und unterschiedlicher Stiftdicke gezeichnet

VEKFONT eignet sich zur Erzeugung von Zeichen und Texten auf flüchtigen Anzeigen wie Elektronenröhren und x/y-Medien wie einem Plotter. Pro Aufruf der Hauptroutine wird über einen Callback-Mechanismus immer genau ein Pixel ausgegeben, so dass die Routine auch in einer Interrupt-Service-Routine (ISR) verwendet werden kann. Über einen zweiten Callback wird über einen Sprung der Ausgabeposition informiert, was zur Strahlaustastung bei einer Röhre oder zum Anheben des Stiftes bei einem Plotter genutzt werden kann.

Die Software ist auf Performance ausgelegt. Auf einem µC sollen möglichst wenig der kostbaren Ressourcen "Laufzeit" und "Programmspeicher" verbraucht werden. Gleichwohl ist VEKFONT parametrierbar und kann angepasst werden, indem z. B. nur Teile des Zeichensatzes wie Großbuchstaben verwendet werden, was zur Platzersparnis führt.

Interface

Um einen Text zu erzeugen, müssen zunächst einige Einstellungen vorgenommen und die Callbacks implementiert werden, dazu gehört z. B. das Initialisieren der verwendeten vecfont_t-Struktur

vecfont_t
Die Struktur, die für die Ausgabe genutzt wird. Nur einige der wichtigsten Felder werden im folgenden erläutert.
char* .text
Der auszugebende Text als Null-terminierter String.
uint8_t .zoom
Die Vergrößerungsstufe. Nur ganzzahlige Zoom-Stufen sind realisiert.
void .beam_xy (vf_uint_t x, vf_uint_t y)
Die Callback-Funktion wird aufgerufen, wenn ein Pixel an die Position (x,y) auszugeben ist.
void .beam_skip (void)
Die Callback-Funktion wird aufgerufen, wenn die Ausgabe-Position einen Sprung macht.
vf_uint_t .start.x
vf_uint_t .start.y
Die Start-Koordinaten legen die untere, linke Ecke des ersten Zeichens fest. Hier beginnt die Text-Ausgabe.

Nach der Initialisierung wird die Funktion vecfont_draw_letters so lange aufgerufen, bis sie einen Wert ungleich Null liefert.

uint8_t vecfont_draw_letters (vecfont_t *vf)
Bei jedem Aufruf wird genau ein Pixel ausgegeben, d.h. der Callback vf->beam_xy wird genau einmal aufgerufen. Zusätzlich kann ein Aufruf von vf->beam_skip stattfinden, der über einen Sprung der Ausgabeposition informiert.
return-Wert ist
== 0
Falls die Ausgabe noch nicht fertig ist und noch weitere Pixel auszugeben sind.
!= 0
Falls die Ausgebe fertig ist und alle Pixel des Textes ausgegeben wurden.

Ein simples Beispielprogramm für den PC, das einfach nur Punkte in einem zweidimensionalen Array setzt, könnte so aussehen:

#include "vektor-zeichen.h"

// Unser Vektorfont-Objekt
static vecfont_t font;

// Ein Array von 256 x 256 Pixeln
static char pixel[256][256];

static void beam_skip (void)
{
    // Der Strahl macht jetzt einen Sprung
}

static void beam_xy (vf_uint_t x, vf_uint_t y)
{
    // Es wird ein neuer Pixel gezeichnet
    pixel[x][y] = 1;
}

int main ()
{
    // Größe, Position und Text angeben
    font.zoom    = 2;
    font.start.x = 10;
    font.start.y = 100;
    font.text = "Hallo Welt!";

    // Callbacks festlegen
    font.beam_skip = beam_skip;
    font.beam_xy   = beam_xy;

    // Text rauspixeln
    while (!vecfont_draw_letters (&font));

    // Ausgabe von pixel[][], z.B. als Grafik
    // ...

    return 0;
}

Übersetzt werden kann das ganze mit

gcc -std=gnu99 main.c vektor-zeichen.c vektor-zeichen-data.c -DVEKFONT='(-1)' -O2 -o main.exe

Konfigurierung

Die Konfiguration der Software geschieht i.W. beim Übersetzen über das Define VEKFONT. Die meisten Features können darüber aktiviert bzw. deaktiviert werden, so dass deaktivierte und unbenutzte Features nicht unnötig Programmspeicher und/oder Laufzeit auffressen.

Standardmäßig sind alle Features deaktiviert, was dem gcc-Kommandozeilenargument -DVEKFONT='0' entspricht. Alle Features werden aktiviert durch -DVEKFONT='(-1)', was alle Bits in der Bitmaske VEKFONT setzt.

Um einen Zeichensatz zu erhalten, der Kleinbuchstaben und Umlaute unterstützt, kann man also beim Compilieren angeben

-DVEKFONT='(VF_LOWERCASE | VF_UMLAUTS)'.

Im einzelnen besteht VEKFONT aus den folgenden Schaltern:

VF_UMLAUTS
Der Zeichensatz enthält Umlaute und Ligaturen (ß)
VF_LOWERCASE
Der Zeichensatz enthält Kleinbuchstaben
VF_ARROWS
Der Zeichensatz enthält Pfeil-Symbole, ansprechbar via <, >, \, ^
VF_PUNKT
Der Zeichensatz kennt Punktuation wie +, -, *, ?, ...
VF_MAP_UMLAUTS
Falls der Zeichensatz keine Umlaute enthält wird Ä als AE dargestellt, etc.
VF_MAP_LOWERCASE
Falls der Zeichensatz keine Kleinbuchstaben enthält werden Kleinbuchstaben als Großbuchstaben dargestellt.
VF_SHRINKY
Kleinbuchstaben 'e' und 's' ragen 1 Pixel über die Mittellinie und werden daher zu groß. Wird dieses Flag gesetzt, dann werden diese Buchstaben etwas geschrumpft, damit sie die Höhe der anderen Kleinbuchstaben erhalten.
VF_PROPORTIONAL
Zur Laufzeit kann via .proportional zwischen Fixed-Font und Proportionalschrift umgestellt werden. Auf den Platzbedarf des Zeichensatzes hat das keinen Einfluss, das Programm wird aber etwas langsamer und größer.
VF_ALWAYS_PROPORTIONAL
Die Schrift wird immer als Proportionalschrift angezeigt. Das Bit ist stärker als VF_PROPORTIONAL

Eine eingehende Dokumentation findet sich im Header vektor-zeichen.h.

Performance

Der Resourcen-Verbrauch von VEKFONT kann in recht weiten Grenzen angepasst werden. An Speicherverbrauch auf einem AVR muß man rechnen

  • Flash: 1/2–1 kByte für den Zeichensatz, abhängig von Konfiguration
  • Flash: 1/2–1 kByte für den Programmcode, abhängig von Konfiguration
  • RAM: einige Bytes + Größe der Lookup-Tabelle. Hinzu kommt die Große der Vecfont_t-Struktur, die man selbst anlegen muß.

Lookup-Tabelle

Die Lookup-Tabelle dient zum schnellen Auffinden der Zeicheninformation zu einem auszugebenden Zeichen. Standardmässig ist die Lookup-Tabelle deaktiviert, was zu einer längeren Ausführungszeit bei RAM-Ersparnis führt. Aktiviert wird die Lookup-Tabelle, indem man ihren Start und ihr Ende – jeweils in Form von ASCII-Codes – festlegt, was über die folgenden Makros geschieht:

VF_LOOKUP_START
VF_LOOKUP_END
Definiert ersten und letzten ASCII-Code der Zeichen, die in die Lookup-Tabelle aufgenommen werden. Die Tabelle wird dynamisch aufgebaut, so daß die allererste Ausgabe eines Zeichens länger dauert als die Nachfolgenden Ausgaben, weil bei der Erstausgabe nach der Zeicheninformation gesucht werden muss.

Beispiel:

-DVF_LOOKUP_START=0x20 -DVF_LOOKUP_END=127

Verwendete Arithmetik

Standardmässig wird mit einer 8-Bit Arithmetik hantiert. Falls dies für die anzusteuernde Anzeige nicht ausreicht, kann per

-DVF_INT_BITS=16

zu einer 16-Bit Arithmetik umgestellt werden. Dies hat Einfluß auf die verwendeten Typen wie vf_uint_t.

Feste Zoom-Stufe

Falls nur Texte in einer einzigen Zoom-Stufe ausgegeben werden und daher keine Notwendigkeit besteht, den Zoom zur Laufzeit zu ändern, so kann er über das Makro VF_ZOOM angegeben werden. Das spart Code und Laufzeit. Beispiel:

-DVF_ZOOM=2 

Download

Dieses Archiv enthält alle Quellen, ein Makefile sowie die Beispielprogramme pc-vecfont.c (PC), pc-vecfont-svg.c (PC), avr-vecfont.c (AVR).

Als Software wird benötigt:

  • GNU make
  • Eine gcc-Host-Toolchain
  • Eine avr-gcc-Toolchain
  • convert aus dem ImageMagick-Paket zum Umwandeln von PBM nach PNG.
  • Optional ein SVG-Betrachter wie Inkview oder FireFox.
pc-vecfont.c
Erstellt aus dem per Kommandozeile angegebenen Text eine PBM-Grafik mit dem Text in 5 Zoom-Stufen. Eine einfache Beispiel-Implementierung.
pc-vecfont-svg.c
Erstellt aus dem per Kommandozeile angegebenen Text eine SVG-Grafik mit dem Text in 3 Zoom-Stufen. Eine wegen des SVG-Formats etwas kompliziertere Implementierung.
avr-vecfont.c
avr-vecfont-callbacks.h
Implementiert den Vektor-Font für AVR, indem die Callbacks über Hook-Makros auf Inline-Funktionen abgebildet werden. Diese Implementierung zielt auf minimalen Flash-Verbrauch und kurze Laufzeit und verwendet daher eine Lookup-Tabelle von 'A'-'Z' zum schnellen Auffinden der Zeichen-Information. Die Zoom-Stufe ist im Makefile hartcodiert auf 2 eingestellt, um Rechnungen zur Laufzeit zu vermeiden. Software inklusive Zeichensatzinformation belegen weniger als 1kByte Speicher.
Für eingehende Dokumentation und Verständnis ist ein Blick in die Quellen angesagt.

Forum