home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Archive Magazine 1996
/
ARCHIVE_96.iso
/
discs
/
mag_discs
/
volume_8
/
issue_06
/
risc_os
/
DrawModule
< prev
next >
Wrap
Text File
|
1988-10-04
|
27KB
|
562 lines
Draw module specification
=========================
The current Draw module does not implement the floating point parts of this
specification. Upgrading it to do so is a possible future development, but
not a likely one.
Path format
===========
A path is a sequence of one or more of the following path elements, each of
which is a sequence of words:
0 n = end of path, with n bytes remaining in buffer (n is
only important for output buffers, and will not be
read for input paths).
1 ptr = pointer to continuation of path.
2 x y = move to (x,y), starting new subpath. This subpath
will affect winding numbers and so is filled
normally.
3 x y = move to (x,y), starting new subpath. This subpath
does not affect winding numbers when filling (used
internally for thin-stroking paths). Note that any
subpath started by this element will always be
considered "closed" and so will never be closed by
the "close open subpaths" operation.
4 = close current subpath with a gap (c.f. element type
7).
5 = close current subpath with a line (c.f. element type
8).
6 x1 y1 x2 y2 x3 y3 = Bezier curve to (x3,y3), with control points at
(x1,y1) and (x2,y2);
7 x y = gap to (x,y), not starting new subpath - used
internally for the gaps in non-thickened dashed
lines.
8 x y = line to (x,y).
Only the bottom byte of the element type (i.e. the first word in each of the
sequences above) is significant: the remaining three bytes are free for use
in any way a client of the Draw module sees fit. (On output to the input
path, the Draw module will leave these three bytes unchanged; on output to a
standard output path, the Draw module will store zeroes in these three
bytes.)
The element types noted as being used internally above can quite legitimately
be used externally - the notes are simply to explain why these types exist at
all.
Note that there are order constraints on these elements - in particular,
element types 4 to 8 may not be used when there is no current subpath,
element types 2 and 3 start a new current subpath (ending any previous
subpath), and element types 4 and 5 end the current subpath.
Two lines, gaps or Bezier curves are considered to be adjacent if they both
appear in the same subpath and one is defined immediately after the other, or
if one of them is the first line, gap or Bezier curve of a subpath and the
other closes that subpath (i.e. it is a path element type 4 or 5 and ends the
subpath concerned). Adjacency of lines is used to decide whether to use a
join to connect them when thickening a path.
A subpath is called closed if it ends with a path element of type 4 or 5 (so
that its last line, gap or Bezier curve is adjacent to its first one);
otherwise, it is called open. Note that a subpath which happens to end at the
same point that it started is not closed unless it actually ends with a type
4 or 5 path element: this can make a difference to whether its first and last
line segments are connected with a join or with two caps when the path is
thickened.
The Draw module SWIs
====================
All calls come in two versions, one a fixed point version and the other
floating point. The former is faster but allows a smaller range of user
co-ordinate systems and is sometimes less accurate.
For the fixed point versions, all numbers that relate to distances in the
co-ordinate space (e.g. co-ordinates, line widths, etc.) are expressed in
user-defined units before transformation. When the transformed path is output
to the screen (in particular, when Draw_Fill or Draw_Stroke is called), the
units after transformation are 1/256 of an OS unit (i.e. approx. 1/(256*180)
inch = 1/46080 inch = 1/640 point). The default flatness is 512 (2 OS units),
which produces reasonable results when output is to the screen and the
identity matrix is used. All dimensionless quantities have 16 binary places
(i.e. &10000 represents the constant 1.0).
Note that in the standard matrix:
( a b 0 )
( c d 0 )
( e f 1 ),
a, b, c and d are dimensionless quantities (so have 16 binary places), while
e and f represent a translation in the output co-ordinate space and so are
expressed in output units (i.e. units after transformation). This varying
interpretation of matrix elements is unpleasant, but necessary to retain
reasonable behaviour (e.g. we want the inverses of all sensible matrices to
be representable with reasonable accuracy).
The floating point versions use IEEE single precision floating point numbers
in all circumstances. They are distinguished by the suffix FP on the name of
the call. The default flatness is 2.0, for the same reason as above.
Note: unless otherwise specified below, all co-ordinates are user
co-ordinates. PostScript concepts that are not redefined here should be
interpreted as PostScript interprets them.
A further note: all calls use the colour (both pixel pattern and operation)
set up for the OS VDU routines.
This means in particular that they can do things that PostScript cannot do -
e.g. fill using operations such as AND, OR, EOR that pay attention to the
current contents of the screen. Given this fact, I see no reason to restrict
the module to PostScript's capabilities in other areas. This has resulted in
the following additional non-PostScript abilities for the Draw module:
(a) Choice of fill style - e.g. fill including/excluding boundary, fill to
halfway through boundary, fill exterior, etc.
(b) "Positive winding number" and "negative winding number" rules as well
as PostScript's "non-zero winding number" and "even-odd winding number"
rules.
(c) Line cap enhancements - particularly differing "leading" and "trailing"
line caps and triangular line caps (together, these allow arrowed lines
and similar effects).
Any program which wants to remain PostScript-compatible (which includes e.g.
working with printer drivers) will of course have to avoid using these
enhancements.
Draw_Fill, Draw_FillFP
======================
These imitate the PostScript "fill" operator - i.e. they close open subpaths,
flatten the path, transform it to standard co-ordinates, fix it if the call
is Draw_FillFP and fill the result.
"Closing open subpaths" means adding a path element of type 5 ("close with
line") to the end of any open subpaths in the path.
"Flattening" a path means bisecting any Bezier curves it contains
recursively, at least until each of the resulting Bezier curves lies
completely within a specified distance of the line segment joining its
endpoints, and then replacing each such of the resulting Bezier curves by the
line segment joining its endpoints. The specified distance is called the
"flatness".
Entry: R0 points to the path.
R1 contains the fill style:
Bits 0,1: 0 = non-zero winding number rule;
1 = negative winding number rule;
2 = even-odd winding number rule;
3 = positive winding number rule;
Bit 2: Set if non-boundary exterior pixels are to be
plotted, clear if they are not;
Bit 3: Set if boundary exterior pixels are to be plotted,
clear if they are not;
Bit 4: Set if boundary interior pixels are to be plotted,
clear if they are not.
Bit 5: Set if non-boundary interior pixels are to be
plotted, clear if they are not;
Bits 6-31 are reserved and should be zero.
R1=0 is a special case. According to the above, it means "don't
fill anything, non-zero rule" - a pretty useless operation. For
convenience, it specifies a sensible default fill style, namely
&30 (i.e. "fill to halfway through boundary, non-zero rule").
R2 points to the matrix, held as 6 words in memory (in the order
a,b,c,d,e,f for the matrix above); R2=0 means the identity
matrix. Note that the code assumes that one user-defined unit is
a small fraction of a pixel, and matrices that multiply
distances by large factors may lead to inaccurate results.
R3 contains the flatness, in user co-ordinates; R3=0 means default
flatness.
Exit: R0 is corrupt (if V clear),
or points to error block (if V set).
R1-R11 preserved.
Some of the terms used above need defining. Briefly, pixels whose centres lie
inside the path according to the winding number rule chosen are interior
pixels, and pixels that would be plotted if the (flattened) path were plotted
with thin line segments (i.e. 1 pixel wide line segments) are boundary
pixels. If you are happy with these definitions, skip down to the section on
Draw_Stroke. If you want more precise details, read on...
To determine whether a pixel is an "interior" pixel, calculate the winding
number W of the path around the central point of the pixel. The pixel is then
an interior pixel if:
* the non-zero winding number rule is selected and W <> 0.
* the negative winding number rule is selected and W < 0.
* the even-odd winding number rule is selected and W is odd.
* the positive winding number rule is selected and W > 0.
Otherwise, the pixel is an exterior pixel.
Lines (including flattened Bezier curves) and gaps both contribute to the
winding number calculation, provided the subpath that contains them started
with an "move to" path element of type 2. Those in subpaths which started
with a "move to" path element of type 3 are ignored.
To determine whether a pixel is a "boundary" pixel, consider the "diamond"
that can be inscribed in the pixel by joining the centres of its sides. If a
line (or part of a flattened Bezier curve, but not a gap) in the path passes
through this "diamond", the pixel is a boundary pixel. Otherwise, it is a
non-boundary pixel.
For the purposes of all the calculations above, regard the path as consisting
of mathematical (i.e. infinitely thin) line segments. If e.g. a pixel centre
or a vertex of a "diamond" lies precisely on the path, resolve the problem by
regarding the path as lying very slightly to the right of its real position.
If this doesn't help (because the line segment involved is exactly
horizontal), regard it as lying very alightly above its real position.
Draw_Stroke, Draw_StrokeFP
==========================
These emulate the PostScript "stroke" operator - i.e. they:
(a) flatten the path;
(b) apply a dash pattern to the path;
(c) thicken the path, using the current line joins and caps;
(d) re-flatten the path if the specified thickness is non-zero;
(e) transform the path to standard co-ordinates;
(f) fix the path if the call is Draw_StrokeFP;
(g) fill the resulting path.
"Applying a dash pattern to a path" means replacing the line segments it
containns by an appropriate sequence of lines and dashes, determined from the
dash pattern and the distance along the path from the start of the current
line segment to the start of the line segment. It is not possible to apply a
dash pattern to a path containing a Bezier curve.
"Thickening" a path means:
(a) If the thickness is zero, converting all "move to" path elements of
type 2 to "move to" path elements of type 3, and otherwise leaving the
path unchanged. Line caps and joins are ignored.
(b) If the thickness is non-zero, expanding each line segment in the path
to a subpath defining a rectangle centred on the original line segment
and of width equal to the specified thickness and expanding endpoints
of line segments to subpaths defining appropriately sized and shaped
caps and joins. Joins are used at common endpoints of two adjacent line
segments. Caps are used at other endpoints, with the leading or
trailing cap style being used depending on whether the endpoint
concerned is a leading or trailing endpoint as the path is traversed.
It is not possible to thicken a path with non-zero thickness if it contains a
Bezier curve.
Except in the special case of the line width being 0 (which means that as
thin a path as possible is plotted, with line caps and joins being ignored),
thickening the path is likely to result in a path that winds multiply around
some pixels and/or in multiple plotting of some pixels. It is therefore
inadvisable to use winding number rules other than the non-zero one, or
non-idempotent colour operations (e.g. EOR). It is also inadvisable to try
plotting the non-boundary exterior pixels. The on-screen results of doing any
of these will probably not be what you expect!
Entry: R0 points to the path.
R1 contains the fill style, as for Draw_Fill, except that R1=0 has a
slightly different interpretation. If the line width is non-zero,
it means &30 as above. If the line width is zero, it means &18
(i.e. "fill boundary"), as the flattened and thickened path will
in fact have no interior in this case! For zero width lines, the
default fill style is usually the only sensible one.
In addition, the top bit of R1 can be set to require the Draw
module to plot the stroke all at once (i.e. using the "plot
normally" option of Draw_ProcessPath rather than the "plot subpath
by subpath" option). The main effects of this change are that the
code will never double-plot a pixel, but also that it uses more
workspace - possibly quite a lot more!
R2 points to the matrix or contains 0, as for Draw_Fill.
R3 contains the flatness or contains 0, as for Draw_Fill.
R4 contains the line thickness, or 0 for a thin stroke (one pixel
wide, with line caps and joins being ignored).
R5 points to a line cap and join specification, which is a word-
aligned four word area containing:
Offset 0: Byte 0 holds join style:
0 = mitered joins;
1 = round joins;
2 = bevelled joins.
Byte 1 holds leading cap style:
0 = butt caps;
1 = round caps;
2 = projecting square caps;
3 = triangular caps.
Byte 2 holds trailing cap style, with the same
allowed values as for leading caps.
Byte 3 is reserved and should be zero.
Offset 4: This holds a parameter associated with joins. For
mitered joins, this is the miter limit. It is not
used for the other join types. Note that the miter
limit is a dimensionless quantity and so (for
Draw_Stroke) is a fixed point number with 16 binary
places.
Offset 8: This holds a parameter associated with leading caps.
For triangular caps, this contains two 16 bit
unsigned numbers, each of which should be regarded as
having 8 binary places. The most significant part of
the number specifies how many line widths the cap
goes beyond the end of the line, and the least
significant part how many line widths it goes to the
side of the centre of the line. Thus e.g. &1000100
means a cap going 1 line width beyond the end of the
line and 1 line width to each side of its centre -
i.e. the cap width is twice the line width, and its
shape is a 45/90/45 degree triangle. This parameter
is not used for the other cap styles.
Offset 12: This holds a similar parameter associated with
trailing caps.
The module will make no attempt to read the last three words if
the corresponding cap or join style does not use them, so it is
legitimate to use a smaller area than four words if these
parameters are not required.
R6 points to a dash pattern, or contains 0 if an undashed line is
required. A dash pattern must be word-aligned and has the
following format:
Offset 0: Distance into the dash pattern to start.
Offset 4: Number N of elements in the dash pattern.
Offsets 8 to 4N+4: Elements of the dash pattern, each of which
is a distance.
The dash pattern starts with a dash of length equal to the first
element above, then a gap of length equal to the second element
above, etc. When the last element of the dash pattern has been
used, the module starts again from the beginning. Note that if the
number of elements of the dash pattern is odd, the distances will
be interpreted differently the second time through, with the first
element being a gap, the second a dash, etc.
Exit: R0 is corrupt (if V clear),
or points to error block (if V set).
R1-R11 preserved.
Draw_StrokePath, Draw_StrokePathFP
==================================
Entry: R0, R2-R6 set as for Draw_Stroke.
R1 points to a word-aligned output buffer (which holds words 0, N,
then N free bytes), or contains 0 to ask for the required output
buffer size (i.e. N+8).
Exit: If V set, R0 points to an error block.
If V clear and R1 was non-zero on entry, the output path has been
inserted into the buffer, ending with an end of path indicator (i.e.
words 0, N, where N is the number of free bytes following in the
buffer). R0 points to this end of path indicator (and so is a
suitable output buffer value for a subsequent path generating call
that is to append to the path).
If V clear and R1 was zero, R0 holds the required length.
R1-R11 preserved.
This call puts the given path through all the stages done by Draw_Stroke or
Draw_StrokeFP above, except the final fill. It then either saves the path
that would be filled in a specified buffer or reports on how big a buffer is
required, as requested.
Draw_FlattenPath, Draw_FlattenPathFP
====================================
Entry: R0 points to the input path.
R1 points to a word-aligned output buffer (which holds words 0, N,
then N free bytes), or contains 0 to ask for the required output
buffer size (i.e. N+8).
R2 contains the flatness, or 0 if the default flatness is to be used.
Exit: If V set, R0 points to an error block.
If V clear and R1 was non-zero, the output path has been inserted
into the buffer, ending with an end of path indicator (i.e. words 0,
N, where N is the number of free bytes following in the buffer). R0
points to this end of path indicator.
If V clear and R1 was zero, R0 holds the required length.
R1-R11 preserved.
This call flattens the given path. It then either saves the resulting path in
a specified buffer or reports on how big a buffer is required, as requested.
Draw_TransformPath, Draw_TransformPathFP
========================================
Entry: R0 points to the input path.
R1 points to a word-aligned output buffer (which holds words 0, N,
then N free bytes), or contains 0 to transform the path in situ.
R2 points to the matrix or contains 0, as for Draw_Fill.
R3 is zero for fixed point output, &80000000 for floating point
output. All other values are reserved.
Exit: If V set, R0 points to an error block.
If V clear and R1 was 0, R0 is corrupt.
If V clear and R1 pointed to an output buffer, the output path has
been inserted into the buffer, ending with an end of path indicator
(i.e. words 0, N, where N is the number of free bytes following in
the buffer). R0 points to this end of path indicator.
R1-R11 preserved.
This call puts the path through a transformation matrix and possibly fixes or
floats it. It then either outputs the resulting path to a specified output
buffer or puts it into the memory originally occupied by the input path.
(Note that this call will never change the length of the path or indeed of
any of its components. This last option is therefore safe - something which
was not true for the two preceding calls.)
Draw_ProcessPath, Draw_ProcessPathFP
====================================
This allows a path to be put through a general set of the processes used when
doing Stroke or Fill. One or more of the following can be requested, in the
order given:
(a) close open subpaths;
(b) flatten the path;
(c) apply a dash pattern to the path;
(d) thicken the path;
(e) re-flatten the path;
(f) transform the path by a matrix;
(g) fix a floating point path or float a fixed point one;
(h) output the resulting path by one of six techniques:
(i) output to a specified output buffer;
(ii) count how long an output buffer is required for the given path
and operations.
(iii) fill the path normally - not legitimate for a floating point
path;
(iv) fill the path subpath by subpath (this option saves workspace and
can sometimes be used legitimately - for example, Draw_Stroke
will often use it). Also not legitimate for a floating point path.
(v) output to the input buffer (i.e. modify the path in situ). Only
valid if the path's length cannot be changed by the call - i.e.
if steps (a) to (e) above are not requested.
(vi) output the path's bounding box, in transformed co-ordinates. This
bounding box does not pay attention to pixel boundaries and is
"inclusive" on all sides.
All combinations of these are allowed, other than the exceptions noted above.
However, some are likely to produce errors (e.g. thickening a non- flattened
path) and others are likely to produce strange results (e.g. filling a path
containing open subpaths).
All the calls above (Draw_Fill, etc.) translate into specific calls to
Draw_ProcessPath or Draw_ProcessPathFP: they are provided to ensure that
suitable names exist for common operations and to reduce the number of
registers requiring setting up and/or preserving.
Entry: R0 points to the input path.
R1 contains a fill style in its bottom 6 bits, as for Draw_Fill,
except that R1=0 has no special meaning. Other bits have the
following meanings:
Bits 6-26 are reserved and should be zero.
Bit 27: set if open subpaths are to be closed (step (a) above).
Bit 28: set if the path is to be flattened initially (step (b)
above).
Bit 29: set if the path is to be thickened (step (d) above).
Bit 30: set if path is to be re-flattened (step (e) above).
Bit 31: set if output path should be floating point, clear if
it should be fixed point (step (g) above).
R2 points to a matrix, or contains 0 if no transformation by a matrix
is to be done (step (f) above).
R3 contains the flatness, or 0 for the default flatness (used in
steps (b) and (e) above).
R4 contains the line thickness (used in step (d) above). R5 points to
a line join and cap specification (used in step (d) above).
R6 points to a dash pattern, or contains 0 if the path is not to be
dashed (step (c) above).
R7 points to a word-aligned output buffer (which holds words 0, N,
then N free bytes), or contains one of the following special
values:
0: output to the input path. This will only be accepted if the
requested operations on the path are ones that cannot change
its length - i.e. transforming the path by a matrix and
fixing or floating it.
1: fill the path normally.
2: fill the path subpath by subpath.
3: count the size of output buffer required.
&80000000+address: output the bounding box of the processed path
to the (word-aligned) address and the three following words,
in the order lowX, lowY, highX, highY. Note: if this option
is applied to an non-flat path, a valid but non-minimal
bounding box will be produced.
Exit: R1-R11 preserved.
If V set, R0 points to an error block.
If V clear:
If R7 was 0, 1 or 2 on entry, R0 is corrupt.
If R7 was 3 on entry, R0 holds the size of output buffer required
(i.e. N+8 if the output buffer should be words 0, N, followed
by N free bytes).
If R7 pointed to an output buffer, the output path will have been
inserted into the buffer and R0 points to the new end of path
indicator.
The Draw module vector DrawV
============================
This provides a means of indirecting the calls above.
Entry: R0-R7 hold values appropriate for the call concerned.
R8 specifies which call is involved:
0: Draw_ProcessPath
1: Draw_ProcessPathFP
2: Draw_Fill
3: Draw_FillFP
4: Draw_Stroke
5: Draw_StrokeFP
6: Draw_StrokePath
7: Draw_StrokePathFP
8: Draw_FlattenPath
9: Draw_FlattenPathFP
10: Draw_TransformPath
11: Draw_TransformPathFP
Exit: R0 holds a suitable return value.
V set or clear as appropriate.
R1-R11 corrupt.