Ausdrucksstark: Ein Weg zur Erzeugung optimaler Druckergebnisse

Möglicherweise hat sich der eine oder andere schon mal gefragt, auf welche Weise Programme wie Calamus, Gem-Draw oder Scigraph ihre Grafiken auf solch ansprechende Weise zu Papier bringen. Daß dies gar nicht mal so schwer ist, soll der vorliegende Beitrag, der nur einen möglichen Weg beschreibt, zeigen.

Es gilt also, eine im Speicher in irgendeiner Form vorliegende Grafik in verschiedenen Druckdichten und in frei wählbarer Größe auszudrucken, denn manchmal braucht man’s quick & dirty und klein, ein andermal vielleicht groß und sauber. Daß nämlich neben der Art der Darstellung auch die Druckqualität eine wichtige Rolle spielt, wenn man die Haut des Wildschweins gut verkaufen will, ist einzusehen. Und nicht zuletzt kränkeln viele an sich gute PD- und Shareware-Grafikprogramme an den mangelnden Ausgabemöglichkeiten.

Schluß damit!

Deshalb folgt nun die Beschreibung einer Methode, die dem oben beschriebenen Problem Abhilfe schafft. Sie ist sicher nicht die einzige, aber sie ist universell, kurz, recht einfach, und sie arbeitet ohne Tricks. Doch zuerst müssen noch ein paar grundsätzliche Dinge erklärt werden:

Vorarbeit

Programme wie Calamus arbeiten mit objektorientierter Grafik. Ein vom Benutzer gewähltes Objekt, zum Beispiel ein Rechteck, wird also nicht, wie bei einem pixelorientierten Malprogramm, auf die Bitmap der Zeichenfläche projiziert und dann „vergessen“. Es wird vielmehr in eine Liste aufgenommen, von wo es jederzeit (z.B. zum Zeichnen) abgerufen, manipuliert (z.B. zum Verschieben) oder auch wieder gelöscht werden kann.

Jede Darstellung auf dem Bildschirm -bei Calamus etc. erfolgt dies in GEM-Fenstem - ist deshalb nur eine Projektion, eine Visualisierung dieser Objekte. Die Ausgangsdaten, also die Koordinaten, Längen und Radien, welche die Objekte beschreiben, bleiben dabei unverändert, und zwar unabhängig von der gewählten Größe, in der die Grafik auf dem Monitor zu sehen sein soll. Hierzu wird nämlich bei der Ausgabe eine Skalierung vorgenommen, d.h. sämtliche Objektdaten für x- und y-Richtung werden mit zwei Faktoren, den Skalierungsfaktoren, multipliziert, welche bei richtiger Berechnung genau die gewünschte Ausdehnung der Grafik bewirken. Wünscht der Benutzer nun die Grafik in doppelter Größe zu sehen, so werden die Skalierungsfaktoren neu berechnet (in diesem Fall mal 2 genommen) und die Grafik erneut gezeichnet. Was nicht auf den Bildschirm bzw. ins Fenster paßt, wird weggeclippt.

Da man aufgrund der Skalierung bei der Wahl des Koordinatensystems, auf das sich die Grafik bezieht, keineswegs auf die Auflösung des jeweiligen Bildschirms beschränkt ist, kann man dieses beliebig definieren. Günstig ist immer eine Größe, die den Wertebereich eines Variablentyps gut ausnutzt. Im Falle eines 2-Byte-Un-signed-Integers wäre dies z.B. 0..65535. Jede Koordinate eines Objekts, das sich auf ein Koordinatensystem mit dieser Ausdehnung bezieht, kann also in einem solchen Integer gespeichert werden (es spricht jedoch nichts dagegen, hierfür andere Datentypen wie Floating-Point oder Long-Integer zu nehmen).

Bild 1: Zur Bedeutung der Skalierung und der Offset-Werte

Vielleicht dient ein konkretes Beispiel der Veranschaulichung, Bild 1: Das virtuelle Koordinatensystem sei 10001000 Punkte groß. Dies ist also gleichermaßen die Arbeitsfläche, auf die der Benutzer seine Objekte plazieren kann. Soll nun die Grafik komplett und verzerrungsfrei zu sehen sein, so sind die zugehörigen Zeichenroutinen mit den Skalierungsfaktoren 400/1000=0.4 für beide Richtungen zu füttern. Die Grafik wird dann 400400 Pixel groß und paßt folglich auf den Bildschirm. Soll sie in der Mitte des Bildschirms erscheinen, so ist noch der Offset (640-400)/2=120 für die x-Verschiebung mitanzugeben.

Die bei Objektgrafik vorhandenen Möglichkeiten der Skalierung und Verschiebung durch einen Offset werden sich für unsere Methode als wichtig heraus stellen.

Zum Punkt

Angenommen, wir zeichnen die Grafik bildschirmfüllend auf einem SM 124 und drucken dann den Inhalt des Bildschirms, also dessen Bitmap, mit 360360 dpi (Punkte pro Zoll) auf einem 24-Nadler aus. Die Grafik wird dann exakt 640/ 360400/360 Zoll=1.81.1 Zoll=4.52.8 cm groß. Wäre der Bildschirm größer, so würde auch das Ergebnis auf dem Drucker größer werden.

Genau hier setzt die Methode an: Wir schaffen eine eigene Bitmap, deren Ausdruck mit einer gewählten Druckdichte genau die gewünschte Größe ergibt, und zeichnen die Grafik in eben diese Bitmap hinein. Der anschließende Ausdruck erfolgt, als wär’s eine Hardcopy vom Bildschirm, nur wird hier die neue Bitmap herangezogen und nicht die des Bildschirms.

Ein Beispiel: Der Benutzer hat ein Programm, welches seine Grafik in einem virtuellen Koordinatensystem von 3200032000 Punkten aufbaut. Der Benutzer wünscht nun, die Grafik mit 300300 dpi und in einer Größe von 44 Zoll auszudrucken. 4 Zoll mit 300 dpi ergeben 1200 Punkte, die neue Bitmap muß also 1200 1200 Pixel oder 150 Bytes mal 1200 Zeilen groß sein (zum Vergleich: der normale SM 124 stellt 640*400 Pixel bzw. 80 Bytes mal 400 Zeilen dar). Damit die Grafik genau diese Größe annimmt, muß mit dem Skalierungsfaktor 1200/ 32000=0.0375 für x- und y-Richtung gearbeitet werden.

Wie kommt nun aber die Grafik in die neue Bitmap? Schließlich beziehen sich die VDI-Ausgaberoutinen nur auf den Bildschirm und auf solche Ausgabegeräte, für die ein Treiber vorhanden ist.

Wenn dem so ist, dann machen wir es uns zunutze, indem wir die Grafik abschnittsweise auf den Bildschirm zeichnen lassen (unter Zuhilfenahme der Offset-Werte) und anschließend die (zumeist kleinere) Bitmap des Bildschirms in die neue Bitmap exakt an die richtige Stelle hineinkopieren (siehe Bild 2).

Die Grafik in der neuen Bitmap entsteht also durch „Zusammenpuzzlen“ einzelner Teilstücke von der Größe des benutzten Bildschirms. Dies kann man zweifach optimieren: Zum einen sollte man die vorhandene Bildschirmauflösung optimal nutzen (Großbildschirme, Grafikkarten, Bigscreen oder Autoswitch-Overscan sollten also unterstützt werden), und zum anderen ermöglicht die Tatsache, daß immer zeilenweise gedruckt bzw. belichtet wird, eine speicherplatzsparende Verkleinerung der neuen Bitmap. Es genügt nämlich völlig, die Bitmap in ihrer Breite völlig, in der Höhe jedoch nur teilweise mit der Grafik zu füllen, da eben, wie gesagt, immer Zeile für Zeile gedruckt wird.

Nach dem Ausdruck dieses Teilstücks kann der nächste, weiter unten liegende Teil der Grafik in dieselbe Bitmap projiziert werden. Als neue Höhe wählt man zweckmäßigerweise die Höhe des vorhandenen Bildschirms, im obigen Beispiel reduziert sich die Bitmap demnach auf 1200400 Pixel (eine DIN-A4-Grafik mit 360360 dpi würde ca. 1.5 MByte verbrauchen, mit obigem Trick jedoch nur ca. 150 KB).

Der Ausdruck

Es muß nun noch die erzeugte Bitmap, welche einen Teil der Grafik enthält, zu Papier gebracht werden. Die Routinen stellen hierzu die gewünschte Druckdichte ein und lesen die Bitmap entsprechend dem gewählten Drucker byteweise aus. Bei einem 24-Nadler müssen so bei 180 dpi jeweils 24 Zeilen ausgelesen und Bit für Bit auf die Nadeln „geschickt“ werden, bei 90 dpi sind es nur zwölf Zeilen auf jede zweite Nadel, bei einem Laser geschieht die Belichtung dagegen rein byte- und zeilenweise.

Die Vielzahl der Druckdichten führt zu einer Vielzahl von Druckmethoden. Eine genaue Beschreibung für jede einzelne Druckdichte würde hier zu weit führen, in vielen Fällen macht jedoch die Beobachtung des Ausdrucks die Arbeitsweise deutlich.

Probleme

Zwei Schwierigkeiten können auftreten: Füllmuster sind nicht skalierbar, sie werden immer mit konstanter Struktur gezeichnet. Dies führt bei verschiedenen Druckdichten zu unterschiedlichen Erscheinungsformen. Beispielsweise ergibt ein 50%-Grau (der Desktop-Hintergrund) bei 360*360 dpi auf einem 24-Nadler eine fast schwarze Fläche. Hier bleibt es dem Programmierer überlassen, für eine Anpassung, z.B. mittels einer Umrechnungstabelle, zu sorgen.

Das andere Problem ist Text. Dieser müßte strenggenommen auch als Vektor- oder Outline-Font, also objektorientiert zur Verfügung stehen. Da dies nur recht mühsam realisiert werden kann, wird man in vielen Fällen bestrebt sein, die vorhandenen System-Fonts zu nutzen. Doch die sind leider auch nur sehr begrenzt skalierbar.

Bild 2: Übertragung der Objektgrafik in die spezielle Bitmap

Bonbons

Als Zugabe gibt es deshalb eine Routine, welche den 8*16-System-Font auf ganz preiswerte Art vektorisiert und damit beliebig skalierbar macht (Beschreibung siehe unten).

Die Druckroutinen machen nichts anderes, als eine Bitmap beliebiger Größe zu Papier zu bringen. Da diese Bitmap genausogut der Bildschirm sein kann, stellen die Treiber gleichzeitig leistungsfähige Hardcopy-Routinen dar. Und damit eine Hardcopy mit 360*360 dpi nicht immer nur briefmarkengroß herauskommt, gibt’s noch eine Routine dazu, welche eine Bitmap beliebig vergrößern kann.

Bei Experimenten mit Hardcopies muß man übrigens darauf achten, daß bei Autoswitch-Overscan und dem Matrix-Color-Bildschirm die Bildschirmbreite in Bytes nicht gleich der Breite in Pixel/8 ist.

Verbesserungen

Die abgedruckten Listings können sicherlich noch optimiert werden. Eine Quelltextoptimierung bezüglich typischer C-Konstrukte wäre denkbar (auf Kosten der Les- und Übertragbarkeit in eine andere Sprache), ebenso steht einer Erhöhung des Komforts nichts im Wege, beispielsweise mit einer Positionierung des Ausdrucks (linker Rand) oder auch dem Ausblenden des logischen Bildschirms während des Grafikaufbaus mittels Setscreen (XBIOS 5).

Die Routinen

1. veccopy()
Dies ist die Hauptroutine. Sie sorgt für die oben beschriebene Berechnung der Offset- und Skalierungswerte sowie für die Aufteilung der Grafik und das anschließende Kopieren in die neue Ziel-Bitmap. Übergeben werden muß die Ausdehnung des virtuellen Koordinatensystems in Punkten (x_vir, y_vir), die gewünschte Größe des Druckergebnisses in 1/100 Zoll (x_out, y_out) und die gewählte Druckdichte in dpi (x_res, yres). Im letzten Beispiel (s.o.) müßte also die Routine mit veccopy(32000,32000,400,400,300,300) auf gerufen werden.

Bild 3: Textpositionierung in Abhängigkeit vom Bezugspunkt ‘hot*

Die Routine enthält außerdem die Aufrufe der Objektgrafik sowie der Druckertreiber (siehe Listing). Falls die Objektgrafik mit einem VDI-Handle arbeitet, sollte sie mit dem der eigens geöffneten virtuellen Workstation aufgerufen werden. Falls nicht, so tut’s das Handle der physikalischen Workstation (=1) auch, also nicht verwirren lassen!

2. driver_09n(), driver_24n(), driver_hpl()
Hier werden Treiber für die gängigsten Drucker angeboten. driver_09n unterstützt alle Epson(-kompatiblen) 9-Nadler, driver24n() die 24-Nadler von Epson und NEC sowie driver_hpl() alle Hewlett-Packard Laserjets sowie dazu kompatible Drucker.

Die Treiber drucken eine (bereits existierende) Bitmap mit der Druckdichte x res (horizontal) und y_res (vertika1, bei Laserdruckem sind keine unterschiedlichen Druckdichten möglich) aus, deren Anfangsadresse bei map adr liegt und die x_prn_b Bytes breit und y_prn_b Pixel hoch ist.

Folgende Druckdichten sind möglich (in dpi):

9-Nadler:
horizontal: 240, 120, 90, 80, 72
vertikal: 216, 108, 72

24-Nadler:
horizontal: 360, 180, 120, 90, 60
vertikal: 360, 180, 90, 60

Laser:
horizontal: 300, 150, 100, 75
vertikal: 300, 150, 100, 75

3. vectext()
Diese Routine vektorisiert den 8*16-System-Font und macht ihn somit skalierbar. Die groben Abstufungen bleiben jedoch naturgemäß vorhanden. Es werden eingestellte Schreibmodi (replace, xor etc.), Textattribute (outlined, kursiv etc.) sowie Füllmuster berücksichtigt.

Übergabeparameter sind das VDI-Handle handle (oder 1, s.o.), die Position der Grafik (x_pos, y_pos), der Rotationswinkel in 1/10 Grad (angle, nur 90 Grad-Schritte möglich), die Sollbreite width des Textes (width>0: Breite des gesamten Textes, width<0 Breite eines Zeichens), die Sollhöhe height und der Text selbst als Zeiger auf einen String (text). Der Bezugspunkt hot gibt die Positionierung des Textes relativ zu dessen Platzbedarf an, siehe Bild 3.

4. grafik() und vdi() grafik() beschreibt eine kleine Objektgrafik, welche auf ein virtuelles Koordinatensystem von 1000*1000 Punkten bezogen ist (erkennbar an den Koordinaten der diversen vdi()~ Aufrufe).

vdi() ruft in Abhängigkeit des Kennworts ‘was’ die zugehörigen VDI-Routinen auf. Die letzten vier Parameter werden je nach Funktion als Attribute, Koordinaten etc. interpretiert. Vor dem Aufruf der VDI-Funktion werden übergebene Koordinaten jedoch skaliert und mit den Offset-Werten verrechnet. Die Routine stellt also ein Minimalsystem für objektorientierte Grafik dar. Einer Erweiterung mit allen möglichen VDI- und sonstigen Grafikroutinen steht nichts im Wege, solange Skalierung und Verschiebung möglich sind.

5. enlarge()
Das kleine Tool enlarge() vergrößert eine beliebige Bitmap. Zu übergeben sind für Quell- und Ziel-Bitmap jeweils die Breite in Bytes und die Höhe in Pixeln sowie die Adressen der beiden Bitmaps. Alles weitere geht (hoffentlich) aus dem Listing hervor.

/* (c) 1991 MAXON Computer */

#include <stdio.h>
#include <stdlib.h>
#include <tos.h>
#include <vdi.h>
#include <string.h>

#define MIN(a,b)    ((a)<(b)?(a):(b))
#define BIT(x,z)    (((s[x]>>1)&1)<<z)
#define POUT(x)     Cprnout(x)
#define BTST(x,b)   (((x)>>(b))&1)

#define SCALE 1
#define MODE 2
#define LINESTYLE 3 
#define TEXTSTYLE 4 
#define LINE 5
#define TEXT 6
#define BOX 7

void veccopy(int x_vir,int y_vir,int x_out,int y_out,int x_res,int y_res); 
void driver_09n(int x_res,int y_res,int x_prn_b,int y_prn_z,char *map_adr); 
void driver_24n(int x_res,int y_res,int x_prn_b,int y_prn_z,char *map_adr); 
void driver_hpl(int x_res,int y_res,int x_prn_b,int y_prn_z,char *map_adr); 
void vectext(int handle,int x_pos,int y_pos,int angle,int width,int height,int hot,char *text); 
void graphic(int handle,int x_offs,int y_offs, double x_scale,double y_scale); 
void vdi(int handle,int was,int x,int y,long w,long h);

int printer;

main()              /* Ein kleines Demo: */
{
    int dx,dy,xr,yr,clip[]={0,0,639,399};

    do
    {
        Cconws("\x1b\x45");         /* Bildschirm löschen */
        vs_clip(1,1,clip);          /* Clipping setzen */
        graphic(1,400,150,.2,.2);   /* Grafik zeigen */ 
        graphic(1,230,50,.15,.15); 
        graphic(1,440,40,.1,.1); 
        vectext(1,20,200,0,320,80,0,"Vectext"); 
        vectext(1,20,280,0,320,20,0, /* Vectext()- */ 
            "Ein kleines Demo");     /* Beispiele */ 
        vectext(1,20,310,0,-8,32,0,"oben: konstante Textbreite"); 
        vectext(1,20,340,0,-8,32,0,"hier: konstante Zeichenbreite"); 
        printf("\n0=raus, 1=9-Nadler, 2=24-Nadler, "
            "3=HP-Laser, 4=Bildschirm: ");
        scanf("%d",Sprinter); 
        if(printer)
        {
            printf("Breite in mm: "); scanf("%d",&dx);
            printf("Höhe in mm: "); scanf("%d",&dy);
            printf("Druckdichte x: ");scanf("%d",&xr); 
            printf("Druckdichte y: ");scanf("%d",&yr);
            veccopy(1000,1000,dx/.254,dy/.254,xr,yr);
        } /* ^ Grafik ausdrucken */
    }
    while(printer); 
    
    return 0;
}

/*  C-Routine: veccopy() (w) 1991 by M. Kraus
    Zweck : Druckt eine Objektgrafik aus 
    Parameter:  x_vir,y_vir: Ausdehnung des
                virtuellen Koordinatensystems 
                x_out,y_out: Grösse der Grafik in 1/100 Zoll (1 Zoll=25.4 mm) 
                x_res,y_res: Druckdichte in dpi */

void veccopy(int x_vir,int y_vir,int xout,int y_out,int x_res,int y_res)
{
    int dy,x,y,win[11]={1},wout[57],hdl,px[8]={0}, 
        x_mon,y_mon, /* Bildschirmauflösung */
        x_prn,y_prn, /* Grafik in Punkten */
        x_prn_w,     /* Bitmapbreite in Words */
        y_jprn_z,    /* Bitmaphöhe in Zeilen */
        x_offs,y_offs; /* Die Offsetwerte */
    double x_sca1,y_scal; /* Skalierungsfaktoren */ 
    char   *map_adr; /* Zeiger auf Bitmap */
    MFDB src={NULL,0,0,0,0,0,0,0,0}, /* Zum Ko- */
         dst={NULL,0,0,0,0,1,0,0,0}; /* pieren */

    v_opnvwk(win,&hdl,wout); /* Workstation auf */
    if(hdl) /* Falls geklappt */
    {
        x_mon=wout[0]+1; /* Bildschirmauf- */
        y_mon=wout[1]+1; /* lösung auslesen */
        x_prn=dst.fd_w=x_res*(x_out/100.); 
        y_prn=dst.fd_h=y_res*(y_out/100.); 
        x_prn_w=dst.fd_wdwidth=((x_prn-1)/16)+1; 
        y_prn_z=48*(y_mon/48);

        x_scal=1.*x_prn/x_vir; /* Berechnung der */
        y_scal=1.*y_prn/y_vir; /* Skal.-faktoren */

        px[2]=wout[0];px[3]=wout[1]; /* Maximales */
        vs_clip(hd1,l,px); /* ..Clippingrechteck */
        map_adr=dst.fd_addr=(char*)calloc(x_prn_w*2,y_prn_z); /* Neue Bitmap schaffen */
        px[3]=px[7]=y_prn_z-1;  /* Quell- und Ziel- */
                                /* koordinaten */
        for(y=0;y<=y_prn;y+=y_prn_z)
        {                       /* Höhe durchgehen */
            yoffs=-y;           /* Y-Offset */
            for(x=0;x<=x_prn;x+=x_mon)
            {                   /* Breite durchgehen */
                x_offs=-x;      /* X-Offset */
                v_clrwk(hdl);   /* Bildschirm löschen */
                /* Genau hier muss die Objektgrafik mit 
                den Skalierungsfaktoren und den Offsetwerten 
                aufgerufen werden: */
                graphic (hdl,x_offs,y_offs,x_sca1,y_scal);

                px[4]=x;        /* Quell- und Zielkoordinaten */ 
                px[6]=(px[2]=MIN(x_mon,x_prn-x+1)-1)+x; 
                vro_cpyfm(hd1,3,px,&src,&dst);
            }                   /* Bildschirm in Bitmap kopieren */
            dy=MIN(y_prn_z,y_prn-y+1);  /* Restl. Höhe */

            /* Hier muss der gewünschte Druckertreiber aufgerufen 
            werden. Im Beispiel hängt er von der globalen Variablen 
            'printer' ab. */

            if(printer==1)
                driver_09n(x_res,y_res,x_prn_w*2,dy,map_adr); 
            else if(printer==2)
                driver_24n(x_res,y_res,x_prn_w*2,dy,map_adr); 
            else if(printer==3)
                driver_hpl(x_res,y_res,x_prn_w*2,dy,map_adr);
        }
        free(map_adr);      /* Speicher freigeben */
        v_clsvwk(hdl);      /* Workstation schliessen */
    }
}

/*  C-Routine: driver_09n() (w) 1991 by M. Kraus 
    Zweck : Druckt eine Bitmap auf 9-Nadler 
    Parameter:  x_res,yres: Druckdichte in dpi 
                x_prn_b: Bitmapbreite in Bytes 
                y_prn_z: Bitmaphöhe in Pixel 
                map_adr: Adresse der Bitmap */

void driver_09n(int x_res,int y_res,int x_prn_b, 
                int y_prn_z,char *map_adr)
{
    int y1,y2,l,lmax,dk,s[8]={0},p_res; 
    long k,k0,k1,kmax= (long) x_prn__b*y_prn_z; 
    short f1,f2,f3;

    f1=(y_res==72);f2=(y_res==108);f3=(y_res==216); 
    p_res=3*(x_res==240)+(x_res==120)+6*(x_res==90)+4*(x_res==80)+5*(x_res==72); 
    dk=(3-2*f1)*x_prn_b;
    POUT(27);POUT(51);  /* Horizontale Druck- */
    POUT(1+f2+23*f1);   /* dichte einstellen */
    for (y1=0; y1<y_prn_z;y1+=y_res/9)
    {                   /* Bitmap zeilenweise durchgehen */
        for(y2=0;y2<(3-2*f1);y2++)
        {
            POUT(27);POUT(42);POUT(p_res);  /* Grafik-*/ 
            POUT((x_prn_b<<3)&0xFF);        /* daten ankün- */ 
            POUT(((x_prn_b<<3)>>8)&0xFF);   /* digen */

            k0=(long)(y1+y2)*x_prn_b;
            for(k=k0,lmax=0;lmax<8 && k<kmax;lmax++,k+=dk); /* Überschuss aus- */
            for(l=lmax;l<8;s[l++]=0) ; /* blenden */
            for(k=k0;k<k0+x_prn_b;k++) /* Druckzeile */ 
            {                           /* ..auslesen */
                for (k1=k,l=0;l<lmax;k1+=dk,l++)
                    s[1]=map_adr[k1]; /* Byteblock holen */

                if (f2)         /* ..und zum Drucker schicken */
                    for(l=7;l>=0;l--)
                        POUT(BIT(0,7)|BIT(1,5)|BIT(2,3)|BIT(3,1));
                else
                    for(l=7;l>=0;l--)
                        POUT(BIT(0,7)|BIT(1,6)|BIT(2,5)|BIT(3,4)|BIT(4,3)|BIT(5,2)|BIT(6,1)|BIT(7,0));
            }
            POUT(13);POUT(10); /* Zeilenvorschub */
        }
        if (!f1)
        {
            POUT(27);POUT(74);POUT(18+3*f3);POUT(13);
        }                   /* ^ Zeilenvorschub */
    }
}

/*  C-Routine: driver_24n() (w) 1991 by M. Kraus 
    Zweck : Druckt eine Bitmap auf 24-Nadler 
    Parameter: siehe driver_09n() */

void driver_24n (int x_res,int y_res,int x_prn_b, 
                int y_prn_z,char *map_adr)
{
    int y1,y2,l,lmax,dk,s[24]={0},p_res; 
    long k,k0,k1,kmax= (long) x_prn_b*y_prn_z; 
    short f1,f2,f3;

    f1=(y_res==360);f2=1+(y_res==90)+2*(y_res=60);     p_res=32+8*(x_res==360)+7*(x_res==180)+(x_res==120)+6*(x_res==90); 
    dk=(l+f1)*x_prn_b;
    POUT(27+f1);POUT(51);   /* Horizontale Druck- */
    POUT(1+23*!f1);         /* dichte einstellen */
    for(y1=0;y1<y_prn_z;y1+=12*y_res/90)
    {           /* Bitmap zeilenweise durchgehen */
        for(y2=0;y2<=f1;y2++)
        {
            POUT(27);POUT(42);POUT(p_res);  /* Grafik-*/ 
            POUT((x_prn_b<<3)&0xFF);        /* daten ankün- */ 
            POUT(((x_prn_b<<3)>>8)&0xFF);   /* digen */

            k0=(long)(y1+y2)*x_prn_b;
            for(k=k0,lmax=0;lmax<24 && k<kmax;
                lmax+=f2,k+=dk);        /* Überschuss aus- *
            for(l=lmax;l<24;s[l++]=0);  /* blenden */
            for(k=k0;k<k0+x_prn_b;k++)  /* Druckzeile */
            {                           /* ..auslesen */
                for(k1=k,l=0;l<lmax;k1+=dk,l+=f2)
                    s[l]=map_adr[k1];   /* Byteblock holen */

                for(l=7;l>=0;l--)       /* ..und zum Drucker */ 
                {                       /* ..schicken */
                    POUT(BIT(0,7)|BIT(1,6)|BIT(2,5)|
                        BIT(3,4)|BIT(4,3)|BIT(5,2)|
                        BIT(6,1)|BIT(7,0));
                    POUT(BIT(8,7)|BIT(9,6)|BIT(10,5)|
                        BIT(11,4)|BIT(12,3)|BIT(13,2)|
                        BIT(14,1)|BIT(15,0));
                    POUT(BIT(16,7)|BIT(17,6)|BIT(18,5)|
                        BIT(19,4)|BIT(20,3)|BIT(21,2)|
                        BIT(22,1)|BIT(23,0));
                }
            }
            POUT(13);POUT(10);          /* Zeilenvorschub */
        }
        if (f1)
        {
            POUT(27);POUT(74);POUT(23);POUT(13);
        }                       /* ^ Zeilenvorschub */
    }
}

/*  C-Routine: driver_hpl() (w) 1991 by M. Kraus 
    Zweck    : Druckt eine Bitmap auf HP-Laserjet 
    Parameter: siehe driver_09n() */

void driver_hpl(int x_res,int y_res,int x_prn_b,int y_jprn_z,char *map_adr)
{
    long i; 
    char s[10];

    sprintf(s,"\xlb*t%dR",MIN(x_res,y_res));
    Fwrite(3,strlen(s),s);          /* Druckdichte */
    Fwrite(3,5,"\x1b*r0A");         /* Grafikmodus */
    sprintf(s,"\xlb*b%dW",x_prn_b); /* Anzahl d. */
                                    /* ..Grafikbytes */ 
    for(i=0;i<y_prn_z;i++)          /* Höhe der Bitmap */
    {                               /* ..durchgehen */
        Fwrite(3,strlen(s),s);      /* Grafik ankündigen */ 
        Fwrite(3,x_prn_b,mapadr+i*x_prn_b);
    }                       /* ^ Grafik rüberschicken */
    Fwrite(3,4,"\xlb*rB");          /* Ende Grafikmodus */
}

/*  C-Routine:  vectext() (w) 1991 by M. Kraus
    Zweck    :  Generiert Pseudo-Vektorschrift 
    Parameter:  handle: VDI-Handle (oder 1) x_pos,y_pos: Textposition 
                angle: Rotationswinkel (1/10 Grad) 
                width: >0: Gesamtbreite des Textes 
                       <0: Breite eines Zeichens 
                height: Höhe des Textes 
                hot: Bezugspunkt für x_pos/y_pos 
                text: Zeiger auf den Text           */

void vectext(int handle,int x_pos,int y_pos,int angle,int width,int height,int hot,char *text)
{
    int i,j,k,slen,bl,b2,b3,sn,cs,dx,dy,x1,y1, x2, 
    y2,px[8]={0,0,639,15,0,16,639,31},attr[15]; 
    char c,font[2560]={0};
    MFDB src={NULL,0,0,0,0,0,0,0,0},
         dst={NULL,640,32,40,0,1,0,0,0};

    dst.fd_addr=font;                   /* Hintergrund in Buffer */
    vro_cpyfm(handle,3,px,&src,&dst);   /* . moven */
    px[5]=0;px[7]=15;                   /* Platzmachen für */
    vro_cpyfm(handle,3,px,&dst,&src);   /* ..Text */
    vqf_attributes(handle,attr);        /* Einstellungen */ 
    vqt_attributes(handle,attr+5);      /* ..merken */
    vswr_mode(handle,MD_TRANS);         /* Schreibmodus */
    vaf_perimeter(handle,0);            /* Umrandung weg */
    vst_point(handle,13,&i,&i,&i,&i);   /* Texthöhe */ 
    v_gtext(handle,0,13,text);          /* Text zeichnen */
    vro_cpyfm(handle,3,px,&src,&dst);   /* ..und in */ 
                                        /* ..den Buffer kopieren */ 
    px[1]=16;px[3]=31;                  /* Alten Hintergrund re- */
    vro_cpyfm(handle,3,px,&dst,&src);   /* staurier,*/ 
    vqt_extent(handle,text,px);         /* Ausdehnung */
                                        /* ..des Textes feststellen */ 
    vswr_mode(handle,attr[3]); 
    vst_point(handle,attr[12],&i,&i,&i,&i);
                    /* ^ Alte Einstellungen */ 
    slen=(int)strlen(text);             /* Anzahl Zeichen */
    b1=px[2]>>3;                        /* Textbreite in Bytes */
    b2=((width>0)-slen*(width<0))*width; b3=px[2]*(width>0)-px[2]/slen*(width<0); 
    sn=(angle=2700)-(angle==900);       /* Sinus */
    cs=(angle==0)-(angle=1800);         /* Cosinus */
    dx=x_pos-((b2*cs-height*sn)>>1)*(BTST(hot,0)+(BTST(hot,1)>>1)); /* x/y-Verschiebung */
    dy=y_pos-((b2*sn+height*cs)>>1)*(BTST(hot,2)+(BTST(hot,3)>>1)); /* ..für Bezugspunkt */
                        /* ..hot sowie Drehung um angle */ 
    for(j=0;j<16;j++) /* Texthöhe durchgehen */
    {
        y1=(j*height)>>4;
        y2=(((j+1)*height)>>4)-1;
        for(i=0;i<=b1;i++)          /* Textbreite byteweise */ 
        {                           /* ..durchgehen */
            c=font[i+j*80];         /* Ein Byte des Textes */
            k=8*!c;                 /* Bits durchgehen, falls */
            while(k<8)              /* ..welche gesetzt sind */
            {                       /* Bitgruppe suchen und vergrössern */
                while(BTST(c,7-k)==0 && k<8) k++; 
                if(k<8)
                {
                    x1=(int)((k+(i<<3))*(long)width/b3): 
                    while(BTST<c,7-k) && k<8) k++; 
                    x2=(int)((k+(i<<3))*(long)width/b3-1); 
                    px[0]=x1*cs-y1*sn+dx;   /* Rechteck- */
                    px[1]=x1*sn+y1*cs+dy;   /* koordinaten */
                    px[2]=x2*cs-y2*sn+dx;   /* ..drehen */
                    px[3]=x2*sn+y2*cs+dy;
                    v_bar(handle,px);       /* Rechteck */
                }
            }
        }
    }
   vsf_perimeter(handle,attr[4]);           /* Umrandung */
}

/*  C-Routine:  graphic() (w) 1991 by M. Kraus
    Zweck    :  Demo-Objektgrafik, bezogen auf ein           
                1000*1000-Punkte-Koord.system 
    Parameter:  handle: VDI-Handle (oder 1)
                x_offs,y_offs: Ausgabe-Offset 
                x_scale,y_scale: Skalier.fakt. */

void graphic(int handle,int x_offs,int y_offs,double x_scale,double y_scale)
{
    int i;

    vdi(handle,SCALE,x_offs,y_offs,(long)&x_scale,(long)&y_scale); 
    vdi(handle,MODE,1,0,0,0); 
    vdi(handle,LINESTYLE,1,1,0,0); 
    vdi(handle,BOX,0,0,999,999); 
    vdi(handle,LINE,0,0,999,999); 
    for(i=0;i<=400;i+=20)
    {
        vdi(handle,LINE,500+i,500,500,100+i); 
        vdi(handle,LINE,500-i,500,500,100+i); 
        vdi(handle,LINE,500+i,500,500,900-i); 
        vdi(handle,LINE,500-i,500,500,900-i);
    }
    vdi(handle,TEXTSTYLE,400,100,0,4);
    vdi(handle,TEXT,780,200,5,(long)"Objekt-");
    vdi(handle,TEXT,780,300,5,(long)"Graphik");
    vdi(handle,TEXTSTYLE,-40,50,900,20);
    vdi(handle,TEXT,180,700,5,(long)"Das ist");
    vdi(handle,TEXT,250,700,5,(long)"Vektor-");
    vdi(handle,TEXT,320,700,5,(long)"text");
    vdi(handle,TEXTSTYLE,1000,64,0,0);
    vdi(handle,TEXT,500,40,5,(long)"DEMO");
    vdi(handle,TEXT,500,960,5,(long)"DEMO");
}

/*  C-Routine:  vdi() (w) 1991 by M. Kraus
    Zweck    :  Grafisches Minimalsystem 
    Parameter:  handle: VDI-Handle (oder 1)
                was: Kennwort, siehe #defines 
                x,y,w,h: Universalparameter */

void vdi(int handle,int was,int x1,int y1,long x2,long y2)
{
    static double sx=1,sy=1; 
    static int ox,oy,txt_w,txt_h,txt_a; 
    int x1s=ox+(int)(x1*sx),x2s=ox+(int)(x2*sx), 
        y1s=oy+(int)(y1*sy),y2s=oy+(int)(y2*sy),px[10];

    switch (was)
    {
        case SCALE:
            sx=*(double*)x2; sy=*(double*)y2; 
            ox=x1;oy=y1;            /* Skalierung+Offset merken */ 			vsl_color(handle,1);    /* Linien-, Füll- */
            vsf_color(handle,1);    /* und Textfarbe */
            vst_color(handle,1);    /* ..auf schwarz */
            vsf_interior(handle,1); /* Füllmuster */
            break;
        case MODE:
            vswrmode(handle,x1);    /* Schreibmodus */
            break; 
        case LINESTYLE:
            vsl_type(handle,x1);    /* Linienstil */
            vsl_width(handle,y1);   /* Linienbreite */
            break; 
        case TEXTSTYLE:
            if (x2=0||x2=1800)
            { txt_w=x1*sx;txt_h=y1*sy; }
            else                    /* x1,y1=Textbreite und -höhe */
            { txt_w=x1*sy;txt_h=y1*sx; )
            txt_a=(int)x2;          /* Textwinkel */
            vst_effects(handle,(int)y2); /* Effekte */
            break; 
        case LINE:
            px[0]=x1s;px[1]=y1s;px[2]=x2s;px[3]=y2s; 
            v_pline(handle,2,px);   /* Linie von xl/y1 */ 
            break;                  /* ..nach x2/y2 */
        case TEXT:
            vectext(handle,x1s,y1s,txt_a,txt_w,txt_h,(int)x2,(char*)y2); 
            break;                  /* x1/y1=Textpos., x2=Bezugspunkt */ 
        case BOX:
            px[0]=px[6]=px[8]=x1s;px[2]=px[4]=x2s; 
            px[1]=px[3]=px[9]=y1s;px[5]=px[7]=y2s; 
            v_pline(handle,5,px);   /* Rechteck von */
            break;                  /* x1/y1 bis x2/y2 */
    }
}

/*  C-Routine: enlarge() (w) 1991 by M. Kraus
                         (c) 1991 MAXON Computer 
    Zweck    : Vergrössert eine beliebige Bitmap 
    Parameter: x1,y1,x2,y2: siehe #define-Zeilen 
               src,dst: Adresse von Quell- und Ziel-Bitmap, müssen existieren */

#include <stdio.h>
#include <string.h>
#include <tos.h>

#define X1 20           /* Quellbitmapbreite in Bytes */
#define Y1 40           /* ... und Höhe in Zeilen */
#define X2 60           /* Zielbitmapbreite in Bytes */
#define Y2 120          /* ... und Höhe in Zeilen */

void enlarge(int x1,int y1,int x2,int y2,char *src,char *dst);

int main() /* Demo für normale 640*400 Pixel: */ 
{ /* Eine Bitmap wird vergrössert und gezeigt */
    int i,j;
    char src[X1*Y1],dst[X2*Y2]; /* Die Bitmaps */
    char *c;                    /* Zeiger auf Bildschirm */

    c=(char*)Logbase();         /* Bildschirmadresse */
    for(i=0;i<X1*Y1;src[i++]=i&255); /* Quell- */
                                /* Bitmap mit Muster füllen */ 
    enlarge(X1,Y1,X2,Y2,src,dst); /* Vergrössern */
    for(i=0;i<Y1;i++)           /* Quellbitmap zeigen */
        for(j=0;j<X1;c[i*80+j+2000]=src[i*X1+j],j++); 
    for(i=0;i<Y2;i++)           /* Zielbitmap zeigen */
        for(j=0;j<X2;c[i*80+j+8000]=dst[i*X2+j],j++);

    getchar(); 
    return 0;
}

void enlarge(int x1,int y1,int x2,int y2,char *src,char *dst)
{
    int h, i, j, k, a, i2, j2;

    memset(dst,0,x2*y2);        /* Zielbitmap löschen */
                                /* X-Vergrösserung: */ 
    for(i=x2-1;i>=0;i--)         /* Bytes und Bits */
        for(k=i<<3,j=7;j>=0;j--) /* durchgehen */
        {
            a=(int)((long)(k+j)*x1/x2); /* Umrechnen */ 
            for(i2=a>>3,j2=a&7,h=0;h<y1;h++)
                if((src[i2+h*x1]>>(7-j2))&1) /* Bits.. */
                    dst[i+h*x2]|=1<<(7-j);   /* schreiben */
        }
                                /* Y-Vergrösserung: */ 
    for(i=y2-1;i>s=0;i--)       /* Zeilen kopieren */
        memcpy(dst+i*x2,dst+i*y1/y2*x2,x2);
}

Michael Kraus
Aus: ST-Computer 02 / 1992, Seite 104

Links

Copyright-Bestimmungen: siehe Über diese Seite