The module identifier has to be "PROforma external driver".
/* picture driver definition */ typedef struct _PICTdriver { struct _PICTdriver *next; /* next driver in list of drivers */ int identifier; /* identifies type of driver */ char name[PF_MAXDRIVERNAME];/* name of driver - the name of a driver */ /* should not start with "-" or " " !! */ /* can we recognize the picture */ Error (*Recognize)(Gstate, char *base); Error (*AspectRatio)(Gstate, char *base, int *xratio, int *yratio); /* display the picture */ Error (*Display)(Gstate, char *base, pt xsiz, pt ysiz, Error (*ColourSelect)(Gstate, int)); /* info on colours used in the picture */ Error (*ColourInfo)(Gstate, char *base, int *count, int *colourspace); Error (*ColourGet)(Gstate, char *base, int which, void *colour); /* for future extensions */ Error Handle(int command, ...); } PICTdriver;
The next pointer allows you to define several picture drivers in one external module. However, an external module can only contain one type of drivers (in this case picture drivers).
Each driver contains an identifier which indicates the type of driver. For a picture driver, the value has to be PF_PICTUREDRIVER.
Each picture driver should have a (preferably unique) name. It is adviseable to make these names as descriptive as possible, e.g. "QL mode 4 screen, 512x256". Note that driver names are case sensitive !
A picture which is passed to the picture driver is just a block of memory. It is intended that this block starts by the FileInfo structure of the file, followed by a copy of the picture file itself, as if it was loaded with the following code
#include "mem_h" #include "io_h" #include "PROforma_h" #define catch(x) if (err=(x)) return err; else ... { Error err; char *base; Channel file; Size size; catch( IOOpenPath(filename,OPEN_OLD,path,&file) ); catch( IOLength(file,&size) ); catch( MEMAllocate(size+sizeof(FileInfo),&base) ); catch( IOFileInfo(file, (FileInfo *)base) ); catch( IOLoadFile(file,size,base+sizeof(FileInfo)) ); IOClose(file); ... PFPictureDisplay(gstate, id, base, xsize, ysize); }
This is the only picture driver access function which can not assume that the picture is of a suitable type. Pictures may only be recognized when you are quite sure it is not supposed to be displayed by another picture driver.
The aspect ratio is returned as two integers. This means that when the picture is displayed witha with of xratio, then the height should be yratio to preserve the aspect ratio of the original.
Please note that the ColourSelect parameter is invalid when the picture has fixed colours. In that case the PFColourXXX commands should be called directly to set the drawing colour.
This command is used to query the default colours which can be used to display a picture. A picture driver can however choose only to support fixed colours (especially for real-colour images). In that case, zero (0) can be returned as the number of colours.
The possible value for the colourspace are PF_COLOURSPACE_RGB, PF_COLOURSPACE_GRAYSHADE or PF_COLOURSPACE_CMYK.
To start with, the file with all the definition of the data structures which are used by PROforma has to be loaded. Most of this doesn't concern the author of picture drivers, by you do need the definition of the picture driver structure. Accidently, this also includes PROforma_h.
As the definition of PROforma Core routines is not included in the header files, import the PFPictureElement function.
#include "gstate_h" Error PFPictureElement(Gstate gstate, pt xsiz, pt ysiz, pt xorg, pt yorg);Next up, define the actual structure of the picture data, as this is used by all the member functions. For the sake of the example, I have defined a very simple picture format, including the data needed to recognize the picture, and the aspect ratio (which is optional). The picture itself has one byte for each pixel, giving 256 distinct colours.
typedef struct { char identifier[24]; /* "example picture format" */ short xsiz, ysiz; /* size in pixels */ short xratio, yratio; /* aspect ratio (0 if not known) */ unsigned char data[2]; /* start of picture data */ } Picture;
Start with the real work. For starters, try to recognize a picture as being of the correct type. You should always try to build in as many checks as possible, as illustrated here by assuring that the picture has a real size, and that the aspect ratio is possible (zero indicactes that the ratio is not known).
static Error Recognize(Gstate gstate, char *base) { FileInfo *fi=(FileInfo *)base; Picture *pict=(Picture *)(base+sizeof(FileInfo)); if (fi->type==FILETYPE_NORMAL && STRSameCD(pict->identifier,"example picture format") && pict->xsiz>0 && pict->ysiz>0) return ERR_OK; else return ERR_INAM; }Get the aspect ratio of the picture. We can assume that the picture passed is of correct type. If the aspect ratio is defined in the picture, than return that. If not, give an error.
static Error AspectRatio(Gstate gstate, char *base, int *xratio, int *yratio) { Picture *pict=(Picture *)(base+sizeof(FileInfo)); if (pict->xratio && pict->yratio) { *xratio=pict->xratio; *yratio=pict->yratio; return ERR_OK; } else return ERR_ITNF; }
Get information about the number of colours used, and the colourspace. In this simple example, all pictures have 256 colours, and as pictures usually originate from a screen, the colours will be given as red, green and blue components.
static Error ColourInfo(Gstate gstate, char *base, int *count, int *space) { *count=256; *space=PF_COLOURSPACE_RGB; return ERR_OK; }
Get the default colours used for the picture. The default colours only have to be defined here (except when the colours are fixed). Although the default colours are often embedded in the picture format, they have to be calculated in this example.
The colour is calculated by looking at the bits. Each colour component has to bits allocated to it, and the two remaining bits can increase the intensity of the colour.
static Error ColourGet(Gstate gstate, char *base, int which, void *colour) { /* the colours are 8 bit : iirrggbb (i for intensity) */ ColourRGB *rgb=(ColourRGB *)colour; int intensity=((which>>6)&3)+1; rgb->red =intensity * ((which>>4)&3) * (pt_hundred/12); rgb->green=intensity * ((which>>2)&3) * (pt_hundred/12); rgb->blue =intensity * ((which )&3) * (pt_hundred/12); return ERR_OK; }
To make sure that a picture is always drawn as fast as possible, the background of the picture is drawn first. If this is completely invisible, you can stop immediately.
Each line is also cleared to the background colour before drawing the individual pixels (or spans). This can also indicate that a line can be discarded, especially when drawing on screen (where speed is most important).
When drawing the spans, unnecessary drawing is not done, by making sure that the colour is different from the background colour. This could give a larger speed gain if the background colour would be the most used colour in the picture. However, actually determining that colour each time the picture is displayed, would probably slow the displaying of the picture down.
static Error Display(Gstate gstate, char *base, pt xsiz, pt ysiz, Error (*ColourSelect)(Gstate, int)) { Picture *pict=(Picture *)(base+sizeof(FileInfo)); int xpix,ypix=pict->ysiz; int colour, backcolour=0; short bit=0x80; int length, lastcolour; unsigned char *linestart=pict->data; unsigned char *linepos; int posx; pt posy=0; /* make sure that at least some part of the picture is visible */ ColourSelect(gstate,backcolour); if (PFPictureElement(gstate,xsiz,ysiz,0,posy)) return ERR_OK; xsiz/=pict->xsiz; ysiz/=pict->ysiz; while(ypix) { posx=0; linepos=linestart; /* see if this line is clipped - and set background colour */ ColourSelect(gstate,backcolour); if (!PFPictureElement(gstate,xsiz*pict->xsiz,ysiz,0,posy)) { length=1; /* get colour */ lastcolour=*linepos; for(xpix=pict->xsiz-1; xpix; xpix--) { /* get colour */ colour=*linepos++; if (colour!=lastcolour) { if (lastcolour!=backcolour) { ColourSelect(gstate,lastcolour); PFPictureElement(gstate,xsiz*length,ysiz,xsiz*posx,posy); } /* skip pixels on page */ posx+=length; lastcolour=colour; length=1; } else length++; } /* there may be a sequence left at the end of the line */ if (colour!=backcolour) { ColourSelect(gstate,colour); PFPictureElement(gstate,xsiz*length,ysiz,xsiz*posx,posy); } } posy+=ysiz; ypix--; linestart+=pict->xsiz; } return ERR_OK; }
We also need a dummy routine, for future compatibility with possible extensions of the picture drivers.
Error Handle(int command, ...) { return ERR_NIMP; }
To finish the driver, only the actual driver definition has to be written. The structure is called init to make sure PROforma (the external module system to be precise) knows where to find the picture driver definition.
PICTdriver init = { NULL, PF_PICTUREDRIVER, "example 256 colour picture", Recognize, AspectRatio, Display, ColourInfo, ColourGet, Handle };
pict_example_pfd : pict_example_o core-dll_o ${LD} -ms -opict_example_pfd \ pict_example_o core-dll_o \ -lsms -sxmod mkxmod pict_example_pfd \"PROforma external driver\"