Graphics
This document gives an review of what is possible with PROforma when you do
not want to use text. Some of these things also have their consequences when
displaying text, but we have chosen to make the font management a separate document as there is so much to say about it.
Note that the PROforma functions (they all return an error code) use a
consistent naming scheme (as introduced in syslib). The name always starts with
the general module name in capitals ("PF" for PROforma), followed by some words
defining the action. Each word starts with a capital, and the most important
words (indicating a group of commands) are given first. For example
"PFLineWidth" is a PROforma command (PF), concerning lines (Line), and
specifically the width (Width).
All commands in PROforma actually require a
gstate as an access point to PROforma.
Therefore we have introduced some commands to create and delete gstates.
To make sure that you can draw part of a picture without affecting the
current graphics state, you can save the graphics state, so that it can be
restored later. The graphics state which is saved includes the CTM, linewidth, paper and drawing colour,
clipping path, current font (when it
remains loaded), fontsize, pagebbox and
origin, flatness and current position,
path drawing method, usecache,
extraeos, printescape,...
- PFGstateInit
-
Create a new gstate. The client has to specify which driver to use and
the device which should be used for that driver. The client also
determines the size of the page and gets to know how many passes are
necessary to render a complete page.
Drivers can be specified either by name, or using a driverid. The
driverid's are not fixed and depend on the configuration. Positive
driverid's are interpretted as a pointer to a string containing the
driver name (case dependent).
Some driverid's and names are reserved : "default driver", "screen
driver" and "dummy driver" are the reserved name, and the values are
PF_DRIVER_DEFAULT, PF_DRIVER_SCREEN and
PF_DRIVER_DUMMY.
You can also specify the device to which the driver should send it's
output. However is NULL or "" is passed, then the
output will be sent to the default device (as can be configured). Note
that some drivers are only capable of sending their image to one
device.
The size of the maximum area depends on the type of the driver. A
printer driver will typically have a 595x842pt area, matching an A4
page (excluding marging forced by the printer).
The screen driver has a maximum size of 720x540pt. This is
approximately a 12inch screen (10x7.5 inch).
The client can specify the requested page size and position on the
device. However, this area is clipped to the actually usable area.
If the resulting area is non-existing (so no part
of the requested area is usable on that printer), an error is reported.
When a gstate is opened, PROforma assumes you want to use the entire
page. So the PageOrigin is set to the position of the top left pixel
of the PageBbox on the device.
Most internal structures are initialised properly by PROforma like
the flatness, drawing colour (black), paper colour (white),... However
some things are not initialised like the linewidth, and the path type
(so this should be set or a path will not become visible).
- PFGstateRemove
-
Remove the given gstate from memory. This releases all the fonts which
are not used by other gstates (and not resident), and also releases the
current path,...
- PFGstateSave
-
Save the current graphics state. This command does not affect anything,
but allows the gstate to be restored later. The graphics states are
store in lifo (last in, first out) stack. This command should not be
called while building a path which has to be drawn.
- PFGstateRestore
-
Restore the graphics state to an earlier saved version. This command
should not be called while building a path which has to be drawn.
PROforma also includes routines to enquire about the available printer drivers.
This can be necessary because driverid's are not fixed. They can vary between
versions and/or configurations of PROforma. You need a gstate to able to
query the available drivers. For this purpose you could initialise a gstate
with PF_DRIVER_DUMMY as driver (the dummy driver does not allow you to
draw anything and is specificaly intended to query PROforma).
- PFDriverCount
-
Get the number of available drivers. This count does not include the
screen driver as this has a fixed driverid (PF_DRIVER_SCREEN).
This command also assures that the next call to PFDriverNext
for this gstate will return the first driver in the list.
- PFDriverNext
-
Get the driverid and name of the next driver in the list. This list is
not sorted !
To make it easy for a client to produce moved, slanted, scaled,
rotated,... images, PROforma uses a transformation
matrix. This matrix converts given coordinates to coordinates in default
user space (which are then, internally,
converted to device space). There is
always a current transformation matrix. The default does nothing (unity
matrix).
To allow the client to set the matrix to a certain state, which can be
altered and later recovered, it is possible to save and restore the CTM.
- PFCTMMove
-
Move the origin of the current transformation matrix. This means that all
objects are actually moved over the given distance. This command could be
simulated by adding the given coordinates to all following absolute
coordinates. This routine is a macro for PFCTMSet.
- PFCTMScale
-
Scale the current transformation matrix. The origin remains in the same
position. All following objects are enlarged by the given factor. This
command maintains the ratio, so everything is scaled by an equal amount in
all directions. This routine is a macro for PFCTMSet.
- PFCTMXScale
-
Scale the current transformation matrix along the x axis. The origin
remains in the same position. All following objects are enlarged along the
x axis by the given factor. This command does not maintain the ratio,
scaling is only along the x axis (which can be rotated) ! This routine is a
macro for PFCTMSet.
- PFCTMYScale
-
Scale the current transformation matrix along the y axis. The origin
remains in the same position. All following object are enlarged along the
y axis by the given factor. This command does not maintain the ratio,
scaling is only along the y axis (which can be rotated) ! This routine is a
macro for PFCTMSet.
- PFCTMSet
-
Set the current transformation matrix to the given value, which
is relative to the previous value of the CTM.
This command should not be performed during the building of a
path as that would give problems when closing the current
subpath (just as the other commands which change the CTM).
- PFCTMReset
-
Reset the current transformation matrix to the standard values,
being all measurements in default user space, the origin at the top left,
and axisses extending right and down.
This command clears the list of CTM's which are saved.
Default user space is normally 1/72 inch (approx .35mm), but this can be
changed with PFPageScale.
- PFCTMSave
-
This command allows the client to save the values of the CTM,
so that it can later be recovered. Can be called as often as
needed (memory permitting, not that it uses much).
- PFCTMRestore
-
Restore the CTM to a previously saved CTM. You should
consider the list of CTM's as as last in, first out (LIFO) stack.
PFCTMRestore removes the last topmost CTM from the stack and makes
it the current.
- PFCTMRestoreKeep
-
This command is functionally equivalent to
PFCTMRestore(gstate);
PFCTMSave(gstate);
It restores the last saved CTM, but doesn't remove that value from the stack of
saved CTMs.
Because PROforma actually attempts to produce pages as fast and efficient as
possible, the client has to know how the path has to be visualised before it is
actually built.
- PFPathMethod
-
Set the drawing method for the following paths. This is a general routine
to set this drawing method, with a parameter. Especially useful when the
path is defined in a data structure. Macros are provided to the individual
cases (given below). If an invalid drawing method is passed, the drawing
method is undefined (nothing is drawn).
- PFPathStroked
-
Notify PROforma that all future path construction commands will
apply to stroked paths. Stroked paths have a thickness (as
specified by PFLineWidth), and always have round caps and round
joins.
Stroked lines are always visible. This means that even zero width lines are
drawn one pixel wide (also called hairline). This is done to make sure that
they don't disappear from the final result. Note however that hairlines can
be so thin on certain high resolution devices that they may be invisible.
Also some devices (like some laser printers) are very bad at colouring
small areas, which can have the same result.
- PFPathFilled
-
Notify PROforma that all future path construction commands will
apply to paths which are filled using the winding rule. Please note that
areas which are less than a pixel wide or tall can disappear from the final
result.
To determine whether an area is filled by the winding rule.
Initialise the winding counter to zero and draw a line to infinity. For
every edge which is crossed you should add one to the winding counter if
the edge goes up (left if the edge is horizontal). If the edge goes down
(right for horizontal), then you should subtract one from the counter. The
area will be filled when the winding counter is not zero. All areas have
to be closed for this rule to work. The direction of the path is very
important as can be seen in the picture.
- PFPathEOFilled
-
Notify PROforma that all future path construction commands will
apply to paths which are filled using the even odd rule. Please note that
areas which are less than a pixel wide or tall can disappear from the final
result, (see "PROforma Imaging Model").
To determine whether an area is filled by the even odd rule.
Initialise the winding counter to zero and draw a line to infinity. For
every edge which is crossed you should add one to the winding counter. If
the resulting winding counter is odd, then the area will be filled. All
areas should be closed for this rule to work.
- PFColourGray
-
Select the current grayshade for drawing. Grayshades are given
in percentages. All devices have a few distinct grayshades. Higher
resolution devices have more grayshades than low resolution devices.
- PFColourRGB
-
Select the current colour for drawing. RGB colours use an additive colour
model, where the red, green and blue components are given. Each component
is a percentage, ranging from black (0) to the pure colour (100).
Devices always have a native colourspace. If that is not RGB, then the
colour which is given is transformed to the devices native colourspace.
- PFColourCMYK
-
Select the current colour for drawing. CMYK colours use a subtractive
colour model, where the cyan (kind of blue), magenta (kind of red), yellow
and black components are given. Each component is a percentage, ranging
from white (0) to the pure colour (100). The black component exists because
mixing cyan, magenta and yellow inks, usually turns out more dark brown
than black. Therefore the black component should be removed and given
separately. Devices always have a native colourspace. If that is not CMYK,
then the colour which is given is transformed to the devices native
colourspace.
- PFPatternMask, PFPatternMaskUser
-
When a colour doesn't match a solid colour on the output device, then
PROforma will produce a fastpattern to approximate the colour. This is a
8x8 (one or more) pattern. You can determine how such patterns are built
(how colours are spread in the pattern) with these calls.
PFPatternMask uses a choice of builtin methods, while the
User variant allows you to build your own distribution method.
- PFLineWidth
-
Set the linewidth in user coordinates for stroking. All lines and curves
which are drawn stroked after this command is given will have the given
linewidth until the next PFLineWidth command. The linewidth should not be
changed while the path is built as this could cause unexpected results.
- PFFlatness
-
Set the flatness. This is the amount of tolerance the approximation of the
bezier curves allows. A high value
increases drawing speed, but decreases the accuracy. If this
value is too high, curves will degenerate to polygons.
To aid in special constructions, you can define a
clipping path. This means
that of any following drawings, only the parts which fall inside the clipping
path are visible.
- PFClipClear
-
Clear the clipping path and the current path (initial state).
The parts of the path which are not inside the
PageBbox will
never be visualised, irrespective of the clipping path.
- PFPathClip
-
Convert the path which was built into the current clipping path.
The path will actually be clipped according to the previous
clipping path. The path will also be cleared by this command, and
the current point will be reset.
The path will be clipped as drawn with PFPathFilled unless the
current drawing mode is PFPathEOFilled. If the linewidth is
hairline (less than a pixel thick), then the clipping path will make
everything invisible !
Clipping paths are a powerful tool. They allow you to look through an area.
It can be viewed as if the drawing which you draw afterwards is the building
of a large pattern which is used to fill the area indicated by the path which
is clipped.
Thus a clipping path can for example be used if you want to produce graphics
with a gradient fill (the colour tone changes). This can be achieved by using
the shape of the drawing to define a clipping path, and then draw a series of
blocks with slightly differing colours, which are clipped. This will produce
the proper result.
Of course you also need commands to build a path. This path can then be drawn,
or used as a clipping path. It is strongly
advised that no other operators are
called while building a path, or between building the path and the
actual drawing or clipping. If you do call other operators, the behaviour may
be quite unexpected (see PROforma sessions,
especially the text show operators should not be used).
- PFMoveTo
-
Set the current point to the given
absolute position. If you were drawing a filled path which was not
closed, this will be done automatically. This commands actually starts
a new subpath.
- PFMoveR
-
Move the current point by the given
distance. If you were drawing a filled path which was not closed, this
will be done automatically. This commands actually starts a new
subpath.
- PFLineTo
-
Construct a line from the current
point to the given point in absolute coordinates. After this
command, the endpoint will be the new current point.
- PFLineR
-
Construct a line from the current
point with the given displacements. After this command, the
endpoint will be the new current point.
- PFCurveTo
-
Construct a bezier curve from the
current point to the given
endpoint, using the given control points for direction (all
absolute coordinates). After this command, the endpoint will be the new
current point.
- PFCurveR
-
Construct a bezier curve from the
current point to the given
endpoint, using the given control points for direction (all
relative coordinates). After this command, the endpoint will be the new
current point.
- PFClosePath
-
Make sure the current subpath is
closed. A line segment will be added from the end of the subpath (the
current point) to the beginning.
- PFPathDraw
-
Make sure the path which was built is
actually rendered in the buffer. The path will be empty after this
command, and the current point reset.
- PFPathClear
-
Clear the current path (make it
empty). This also resets the current
point.
PROforma also provides some high level drawing routines. These actually use
the routines above, but are easier that duplicating the code each time.
- PFArcTo
-
Add a circular arc the the path, starting at the current point and
ending at the given point. You have to give the coordinates of the
point where the tangents cross. This routine only works properly
for arc which cover less than 90 degrees.
- PFArcR
-
Same as PFArc but with relative coordinates.
- PFCircle
-
Add a circle subpath to the current path. The center of the circle
will be at the current point.
- PFPie
-
With the current point as center, build a pie. You have to specify the
cosinus and sinus of the start and end degree and the radius. You can
also choose whther the pie is closed or not. A pie which is not closed
is just part of a circle.
- PFRectangle
-
Starting from the current point, build a rectangle subpath. The
rectangle can have rounded corners with the given radius.
- PFPageBboxReset
-
Reset the PageBbox and PageOrigin
to the original values when the gstate
was initialised (so reset it to the entire visible page).
- PFPageBboxRestore
-
Reset the PageBbox to the entire
visible page, and set the PageOrigin to the point which is already at
the top left of this area. This allows the client to restore the
PageBbox after a PFPageScroll or PFPageBboxSet
command.
- PFPageBboxSet
-
Set the new size of the PageBbox
and moves the origin (relative to previous value) of visible area.
All parameters to this function are in the default user space.
- PFPageOriginSet
-
Move the PageOrigin relative to the previous value. The
PageOrigin is the coordinate of the top left point in the
PageBbox.
All parameters to this function are in the default user space.
- PFPageScale
-
This routine allows the client to set the scaling of a page.
It is useful in cases where the page is zoomed in (as in
LINEdesign). The default user space is
actually changed from
the previous value (initially 72 points/inch) to something
else (e.g. to 144 pt/inch if factor was 2).
Please note that this changes both the size of the
PageBbox and
the PageOrigin. Both are multiplied by the inverse of the scale.
PFPageScale allows the client to transparently change the
imaginary size of the page. Contrary to PFCTMScale which has
no effect on any parameters in default user space (such as for
PFPageBboxSet).
- PFPageBboxGet
-
Get the current size and origin of the PageBbox, and the current value
of the PageOrigin. All returned values are given in default user space.
- PFPageShow
-
Copy the device buffer to the device. Will display the (part of) the
page which has already been rendered.
If the page will be built in several passes, this routine
automatically adjusts the internal structures to render the next
pass. In all cases it automatically adjusts the PageBbox to make the
entire page visible. The client is responsible for rebuilding the page.
If this was the last pass of a page, the internal structures
will be adjusted for the first pass again.
This command also allows multiple copies of the page to be
produced. However this option will only work if the device
supports this (like a laser printer). The actual image in the buffer is
not cleared by this command.
- PFPageScroll
-
Allows the client to efficiently scroll in interactive
applications.
It is only allowed to scroll in one direction at a time
(horizontal or vertical). The PageBbox and PageOrigin are
automatically adjusted to fit the newly visible space (which probably
has to be cleared first).
- PFPageClear
-
Clear the page using the current paper colour (which by default is
white). This will only clear the area which falls inside the current
PageBbox, as this allows efficient
redrawing of the screen for interactive applications.
- PFPaperColourGray
-
Select the paper colour. Grayshades are given
in percentages. All devices have a few distinct grayshades. Higher
resolution devices have more grayshades than low resolution devices.
- PFPaperColourRGB
-
Select the current paper colour. RGB colours use an additive colour
model, where the red, green and blue components are given. Each
component is a percentage, ranging from black (0) to the pure colour
(100). Devices always have a native colourspace. If that is not RGB,
then the colour which is given is transformed to the devices native
colourspace.
- PFPaperColourCMYK
-
Select the current paper colour. CMYK colours use a subtractive
colour model, where the cyan (kind of blue), magenta (kind of red),
yellow and black components are given. Each component is a percentage,
ranging from white (0) to the pure colour (100). The black component
exists because mixing cyan, magenta and yellow inks, usually turns out
more dark brown than black. Therefore the black component should be
removed and given separately. Devices always have a native colourspace.
If that is not CMYK, then the colour which is given is transformed to
the devices native colourspace.
- PFDisplayMode
-
This command can be used to set the
display mode which should be
used for the page. On screen drivers, this allows you to make sure the
users does not have to wait for the drawing to finish before they can
see something.
Some devices (usually laser printers) can
easily produce following copies after the first one at great speed. This is
supported by PROforma.
- PFCopies
-
Set the number of copies which should be produced. Only works if
the device supports it (and the driver of course). If not
supported this command returns an error.
A special library call is provided to display bitmaps. This is particularly
important for DTP applications. Bitmaps can be visualised in any orientation.
In fact a picture can encapsulate any kind of graphical object. A picture could
also consist of vector graphics and/or (possibly preformatted) text. Picture drivers could be written for all these
purposes.
Pictures always have to be in memory (an image of the file on disk) before
they can be used by a picture driver (IOFileLoad). PROforma can then
be used to try to recognize which picture driver can display the picture.
However, recognizing a picture is not always possible. Sometimes, you have to
know the type of the picture in advance (or let the user choose). The picture
can be loaded with code like :
{
Size size;
Channel file;
char *base;
IOOpenPath(file,OPEN_OLD,path,&file);
IOLength(file,&size);
MEMAllocate(size+sizeof(FileInfo),&base);
IOFileInfoGet(file,(FileInfo *)base);
IOFileLoad(file,size,base+sizeof(FileInfo));
IOClose(file);
PFMoveTo(gstate,xpos,ypos);
PFPictureDisplay(gstate, driver, base,
xsize, xsize);
}
Picture drivers (like printer drivers), can be identified either by name
or by driverid. Some driverid's are also reserved for some specific drivers.
- PFPictureCount
-
Get the number of available picture drivers. This also assures that the
next call to PFPictureNext will return the first picture
driver which is available.
- PFPictureNext
-
Get the name and id of the next printer driver in the list.
- PFPictureRecognize
-
Let the given picture driver test whether it can recognize the given
picture. If it can, the picture driver can visualise the picture.
However, the picture driver is only allowed to recognize picture of
which it is very sure that it can be displayed. Therefore, if the
file format does not include a descriptor (e.g. the type is only
indicated by a file extension), then the driver probably has to reject
pictures which it can display.
So the answers are either
- YES, I can display the picture
- NO, I don't know if I can display the picture
All the other commands assume that the picture which is passed can be
displayed by the chosen picture driver !
The following code can be used to recognize the driver which can
display the picture.
/* figure out the picture driver id */
{
int recognized=FALSE;
int id;
PFPictureCount(gstate,NULL);
while (!recognized && !PFPictureNext(gstate,&id,NULL))
{
if (PFPictureRecognize(gstate,id,picture base)==ERR_OK)
recognized=TRUE;
}
if (recognized)
the picture id is now stored in id
}
- PFPictureRatio
-
Get the aspect ration of the picture, if known.
- PFPictureColourCount
-
Get the number of colours which are used in the picture. This command
allows you to figure out how many colours are used in the picture, so
that the user can display the picture with different colours. If
changing the colours is not possible, this command will indicate that.
- PFPictureColourGrayGet
PFPictureColourRGBGet
PFPictureColourCMYKGet -
Get the colours which are by default used in the picture. The colours
are given in the requested colour space. The colours are filled in an
array of the size as returned by PFPictureColourCount.
- PFPictureDisplay
PFPictureDisplayGray
PFPictureDisplayRGB
PFPictureDisplayCMYK -
Display the picture at the given size at the position indicated by the
current point. When using
PFPictureDisplay, the default colours for the picture will be
used. The other comands need you to pass the colours which should be
used (in the colourspace which is indicated by the command).
- PFWindowMove
-
This command adjust the internal structures to a gstate to
match the position of the screen window (it does nothing for
non-screen gstates). This should be called if the owning job
uses the Pointer Environment and the window has been moved.
- PFWindowSubSet
-
The concept of a WindowSub can be
used recursively. A WindowSub is somewhat similar to the PageBbox, however,
the pagebbox is used to redraw part of something, while the WindowSub
is used to draw the contents of a part of the gstate, as a WindowSub
on the screen. Any following
PageBbox has to fall inside the current WindowSub. Any successive
WindowSubs are also confined to fall inside the current WindowSub.
Any call of PFPageScale is local inside the current and
subsequent WindowSubs.
The new WindowSub is always relative with the currect PageBbox, and
the coordinates are in user space, which should not be rotated.
When the WindowSub is set, the PageOrigin is at (0,0).
- PFWindowSubRestore
-
The concept of a WindowSub can be used recursively, so restoring the
WindowSub will only restore the the changes since the matching call of
PFWindowSubSet (this acts as a LIFO stack, like
PFCTMSet and PFCTMRestore).
The CTM and PageScale, PageBbox and PageOrigin are restored to their
original values.
PROGS, Professional & Graphical Software
last edited December 4, 1996