Copyright (c) Hyperion Entertainment and contributors.

DR2D IFF 2-D Objects

From AmigaOS Documentation Wiki
Revision as of 20:21, 10 May 2012 by Steven Solie (talk | contribs) (Created page with "= DR2D = 2-D Object standard format <pre> Description by Ross Cunniff and John Orr A standard IFF FORM to describe 2D drawings has been sorely needed for a long time. Seve...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

DR2D

2-D Object standard format

Description by Ross Cunniff and John Orr

A standard IFF FORM to describe 2D drawings has been sorely needed for
a long time.  Several commercial drawing packages have been available
for some time but none has established its file format as the Amiga
standard.  The absence of a 2D drawing standard hinders the
development of applications that use 2D drawings as it forces each
application to understand several private standards instead of a
single one.  Without a standard, data exchange for both the developer
and the user is difficult, if not impossible.

The DR2D FORM fills this void.  This FORM was developed by Taliesin,
Inc. for use as the native file format for their two-dimensional
structured drawing package, ProVector.  Saxon Industries and Soft
Logik Publishing Corporation are planning to support this new FORM in
the near future.

Many of the values stored in the DR2D FORM are stored as IEEE single
precision floating point numbers.  These numbers consist of 32 bits,
arranged as follows:

 _______________________________________________________________________
| s e e e e e e e | e m m m m m m m | m m m m m m m m | m m m m m m m m |
 -----------------------------------------------------------------------
  31            24  23            16  15            8   7             0


where: 

        s       is the sign of the number where 1 is negative and 0 is
                  positive. 
        e       is the 8 bit exponent in excess 127 form.  This number
                  is the power of two to which the mantissa is raised
                  (Excess 127 form means that 127 is added to the
                  exponent before packing it into the IEEE number.) 
        m       is the 23 bit mantissa.  It ranges from 1.0000000 to
                  1.999999..., where the leading base-ten one is
                  assumed. 

An IEEE single precision with the value of 0.0000000 has all its bits
cleared. 









The DR2D Chunks


FORM (0x464F524D)       /* All drawings are a FORM */

        struct FORMstruct {
            ULONG       ID;             /* DR2D */
            ULONG       Size;
        };


DR2D (0x44523244)  /* ID of 2D drawing */


The DR2D chunks are broken up into three groups: the global drawing
attribute chunks, the object attribute chunks, and the object chunks.
The global drawing attribute chunks describe elements of a 2D drawing
that are common to many objects in the drawing.  Document preferences,
palette information, and custom fill patterns are typical
document-wide settings defined in global drawing attribute chunks.
The object attribute chunks are used to set certain properties of the
object chunk(s) that follows the object attribute chunk.  The current
fill pattern, dash pattern, and line color are all set using an object
attribute chunk.  Object chunks describe the actual DR2D drawing.
Polygons, text, and bitmaps are found in these chunks.



The Global Drawing Attribute Chunks

The following chunks describe global attributes of a DR2D document.

DRHD (0x44524844)       /* Drawing header */

The DRHD chunk contains the upper left and lower right extremes of the
document in (X, Y) coordinates.  This chunk is required and should
only appear once in a document in the outermost layer of the DR2D file
(DR2Ds can be nested).

        struct DRHDstruct {
            ULONG       ID;
            ULONG       Size;                   /* Always 16 */
            IEEE        XLeft, YTop,
                        XRight, YBot;
        };


The point (XLeft,YTop) is the upper left corner of the project and the
point (XRight,YBot) is its lower right corner.  These coordinates not
only supply the size and position of the document in a coordinate
system, they also supply the project's orientation.  If XLeft <
XRight, the X-axis increases toward the right.  If YTop < YBot, the
Y-axis increases toward the bottom.  Other combinations are possible;
for example in Cartesian coordinates, XLeft would be less than XRight
but YTop would be greater than YBot.





PPRF (0x50505249)       /* Page preferences */

The PPRF chunk contains preference settings for ProVector.  Although
this chunk is not required, its use is encouraged because it contains
some important environment information.

        struct PPRFstruct {
            ULONG       ID;
            ULONG       Size;
            char        Prefs[Size];
        };

DR2D stores preferences as a concatenation of several null-terminated
strings, in the Prefs[] array.  The strings can appear in any order.
The currently supported strings are:

                Units=<unit-type>
                Portrait=<boolean>
                PageType=<page-type>
                GridSize=<number>

where:
                <unit-type>     is either Inch, Cm, or Pica
                <boolean>       is either True or False
                <page-type>     is either Standard, Legal, B4, B5, A3,
                                  A4, A5, or Custom 
                <number>        is a floating-point number

The DR2D FORM does not require this chunk to explicitly state all the
possible preferences.  In the absence of any particular preference
string, a DR2D reader should fall back on the default value.  The
defaults are:

                Units=Inch
                Portrait=True
                PageType=Standard
                GridSize=1.0


CMAP (0x434D4150)       /* Color map (Same as ILBM CMAP) */

This chunk is identical to the ILBM CMAP chunk as described in the IFF
ILBM documentation.

        struct CMAPstruct {
            ULONG       ID;
            ULONG       Size;
            UBYTE       ColorMap[Size];
        };

ColorMap is an array of 24-bit RGB color values.  The 24-bit value is
spread across three bytes, the first of which contains the red
intensity, the next contains the green intensity, and the third
contains the blue intensity.  Because DR2D stores its colors with
24-bit accuracy, DR2D readers must not make the mistake that some ILBM
readers do in assuming the CMAP chunk colors correspond directly to
Amiga color registers.



FONS (0x464F4E53)       /* Font chunk (Same as FTXT FONS chunk) */

The FONS chunk contains information about a font used in the DR2D
FORM.  ProVector does not include support for Amiga fonts.  Instead,
ProVector uses fonts defined in the OFNT FORM which is documented
later in this article.

        struct FONSstruct {
            ULONG       ID;
            ULONG       Size;
            UBYTE       FontID;         /* ID the font is referenced by */
            UBYTE       Pad1;           /* Always 0 */
            UBYTE       Proportional;   /* Is it proportional? */
            UBYTE       Serif;          /* does it have serifs? */
            CHAR        Name[Size-4];   /* The name of the font */
        };

The UBYTE FontID field is the number DR2D assigns to this font.
References to this font by other DR2D chunks are made using this
number.    

The Proportional and Serif fields indicate properties of this font.
Specifically, Proportional indicates if this font is proportional, and
Serif indicates if this font has serifs.  These two options were
created to allow for font substitution in case the specified font is
not available.  They are set according to these values:

        0       The DR2D writer didn't know if this font is
                  proportional/has serifs. 
        1       No, this font is not proportional/does not have
                  serifs. 
        2       Yes, this font is proportional/does have serifs.

The last field, Name[], is a NULL terminated string containing the
name of the font. 


DASH (0x44415348)       /* Line dash pattern for edges */

This chunk describes the on-off dash pattern associated with a line.

        struct DASHstruct {
            ULONG       ID;
            ULONG       Size;
            USHORT      DashID;                 /* ID of the dash pattern */
            USHORT      NumDashes;              /* Should always be even */
            IEEE        Dashes[NumDashes];      /* On-off pattern */
        };

DashID is the number assigned to this specific dash pattern.
References to this dash pattern by other DR2D chunks are made using
this number.

The Dashes[] array contains the actual dash pattern.  The first number
in the array (element 0) is the length of the ``on'' portion of the
pattern.  The second number (element 1) specifies the ``off'' portion
of the pattern.  If there are more entries in the Dashes array, the
pattern will continue.  Even-index elements specify the length of an
``on'' span, while odd-index elements specify the length of an ``off''
span.  There must be an even number of entries.  These lengths are not
in the same units as specified in the PPRF chunk, but are multiples of
the line width, so a line of width 2.5 and a dash pattern of 1.0, 2.0
would have an ``on'' span of length 1.0 x 2.5 = 2.5 followed by an
``off'' span of length 2.0 x 2.5 = 5.  The following figure shows
several dash pattern examples.  Notice that for lines longer than the
dash pattern, the pattern repeats.


[figure 1 - dash patterns]

By convention, DashID 0 is reserved to mean `No line pattern at all',
i.e. the edges are invisible.  This DASH pattern should not be defined
by a DR2D DASH chunk.  Again by convention, a NumDashes of 0 means
that the line is solid.


AROW (0x41524F57)       /* An arrow-head pattern */

The AROW chunk describes an arrowhead pattern.  DR2D open polygons
(OPLY) can have arrowheads attached to their endpoints.  See the
description of the OPLY chunk later in this article for more
information on the OPLY chunk.


        #define ARROW_FIRST  0x01 /* Draw an arrow on the OPLY's first point */
        #define ARROW_LAST   0x02 /* Draw an arrow on the OPLY's last point */

        struct AROWstruct {
            ULONG       ID;
            ULONG       Size;
            UBYTE       Flags;          /* Flags, from ARROW_*, above */
            UBYTE       Pad0;           /* Should always 0 */
            USHORT      ArrowID;        /* Name of the arrow head */
            USHORT      NumPoints;
            IEEE        ArrowPoints[NumPoints*2];
        };


The Flags field specifies which end(s) of an OPLY to place an
arrowhead based on the #defines above.  ArrowID is the number by which
an OPLY will reference this arrowhead pattern.

The coordinates in the array ArrowPoints[] define the arrowhead's
shape.  These points form a closed polygon.  See the section on the
OPLY/CPLY object chunks for a descriptionof how DR2D defines shapes.
The arrowhead is drawn in the same coordinate system relative to the
endpoint of the OPLY the arrowhead is attached to.  The arrowhead's
origin (0,0) coincides with the OPLY's endpoint.  DR2D assumes that
the arrowhead represented in the AROW chunk is pointing to the right
so the proper rotation can be applied to the arrowhead.  The arrow is
filled according to the current fill pattern set in the ATTR object
attribute chunk.



FILL (0x46494C4C)       /* Object-oriented fill pattern */

The FILL chunk defines a fill pattern.  This chunk is only valid
inside nested DR2D FORMs.  The GRUP object chunk section of this
article contans an example of the FILL chunk.

        struct FILLstruct {
            ULONG       ID;
            ULONG       Size;
            USHORT      FillID;                 /* ID of the fill */
        };

FillID is the number by which the ATTR object attribute chunk
references fill patterns.  The FILL chunk must be the first chunk
inside a nested DR2D FORM.  A FILL is followed by one DR2D object plus
any of the object attribute chunks (ATTR, BBOX) associated with the
object.

[Figure 2 - fill patterns]


DR2D makes a ``tile'' out of the fill pattern, giving it a virtual
bounding box based on the extreme X and Y values of the FILL's object
(Fig. A).  The bounding box shown in Fig. A surrounding the pattern
(the two ellipses) is invisible to the user.  In concept, this
rectangle is pasted on the page left to right, top to bottom like
floor tiles (Fig. B).  Again, the bounding boxes are not visible.  The
only portion of this tiled pattern that is visible is the part that
overlaps the object (Fig. C) being filled.  The object's path is
called a clipping path, as it ``clips'' its shape from the tiled
pattern (Fig. D).  Note that the fill is only masked on top of
underlying objects, so any ``holes'' in the pattern will act as a
window, leaving visible underlying objects.


LAYR (0x4C415952)       /* Define a layer */

A DR2D project is broken up into one or more layers.  Each DR2D object
is in one of these layers.  Layers provide several useful features.
Any particular layer can be ``turned off'', so that the objects in the
layer are not displayed.  This eliminates the unnecessary display of
objects not currently needed on the screen.  Also, the user can lock a
layer to protect the layer's objects from accidental changes.
        
        struct LAYRstruct {
            ULONG    ID;
            ULONG    Size;
            USHORT   LayerID;              /* ID of the layer */
            char     LayerName[16];        /* Null terminated and padded */
            UBYTE    Flags;                /* Flags, from LF_*, below */
            UBYTE    Pad0;                 /* Always 0 */
        };    

LayerID is the number assigned to this layer.  As the field's name
indicates, LayerName[] is the NULL terminated name of the layer.
Flags is a bit field who's bits are set according to the #defines
below:

        #define LF_ACTIVE       0x01    /* Active for editing */
        #define LF_DISPLAYED    0x02    /* Displayed on the screen */

If the LF_ACTIVE bit is set, this layer is unlocked.  A set
LF_DISPLAYED bit indicates that this layer is currently visible on the
screen.  A cleared LF_DISPLAYED bit implies that LF_ACTIVE is not set.
The reason for this is to keep the user from accidentally editing
layers that are invisible.


The Object Attribute Chunks


ATTR (0x41545452)       /* Object attributes */

The ATTR chunk sets various attributes for the objects that follow it.
The attributes stay in effect until the next ATTR changes the
attributes, or the enclosing FORM ends, whichever comes first.


        /* Various fill types */
        #define FT_NONE         0    /* No fill                 */
        #define FT_COLOR        1    /* Fill with color from palette */
        #define FT_OBJECTS      2    /* Fill with tiled objects */

        
        struct ATTRstruct {
            ULONG       ID;
            ULONG       Size;
            UBYTE       FillType;    /* One of FT_*, above      */
            UBYTE       JoinType;    /* One of JT_*, below      */
            UBYTE       DashPattern; /* ID of edge dash pattern */
            UBYTE       ArrowHead;   /* ID of arrowhead to use  */
            USHORT      FillValue;   /* Color or object with which to fill */
            USHORT      EdgeValue;   /* Edge color index        */
            USHORT      WhichLayer;  /* ID of layer it's in     */
            IEEE        EdgeThick;   /* Line width              */
        };



FillType specifies what kind of fill to use on this ATTR chunk's
objects.  A value of FT_NONE means that this ATTR chunk's objects are
not filled.  FT_COLOR indicates that the objects should be filled in
with a color.  That color's ID (from the CMAP chunk) is stored in the
FillValue field.  If FillType is equal to FT_OBJECTS, FillValue
contains the ID of a fill pattern defined in a FILL chunk.

JoinType determines which style of line join to use when connecting
the edges of line segments.  The field contains one of these four
values:

        /* Join types */
        #define JT_NONE    0            /* Don't do line joins */
        #define JT_MITER        1       /* Mitered join */
        #define JT_BEVEL        2       /* Beveled join */
        #define JT_ROUND        3       /* Round join */

DashPattern and ArrowHead contain the ID of the dash pattern and arrow
head for this ATTR's objects.  A DashPattern of zero means that there
is no dash pattern so lines will be invisible.  If ArrowHead is 0,
OPLYs have no arrow head.  EdgeValue is the color of the line
segments.  WhichLayer contains the ID of the layer this ATTR's objects
are in.  EdgeThick is the width of this ATTR's line segments.




BBOX (0x42424F48)       /* Bounding box of next object in FORM */


The BBOX chunk supplies the dimensions and position of a bounding box
surrounding the DR2D object that follows this chunk in the FORM.  A
BBOX chunk can apply to a FILL or AROW as well as a DR2D object.  The
BBOX chunk appears just before its DR2D object, FILL, or AROW chunk.

        struct BBOXstruct {
            ULONG       ID;
            ULONG       Size;
            IEEE                XMin, YMin,     /* Bounding box of obj. */
                                XMax, YMax;     /* including line width */
        };

In a Cartesian coordinate system, the point (XMin, YMin) is the
coordinate of the lower left hand corner of the bounding box and
(XMax, YMax) is the upper right.  These coordinates take into
consideration the width of the lines making up the bounding box.


XTRN (0x5854524E)       /* Externally controlled object */


The XTRN chunk was created primarily to allow ProVector to link DR2D
objects to ARexx functions. 

        struct XTRNstruct {
            ULONG   ID;
            ULONG   Size;
            short   ApplCallBacks;             /* From #defines, below */
            short   ApplNameLength;
            char    ApplName[ApplNameLength];  /* Name of ARexx func to call */
        };

ApplName[] contains the name of the ARexx script ProVector calls when
the user manipulates the object in some way.  The ApplCallBacks field
specifies the particular action that triggers calling the ARexx script
according to the #defines listed below.

        /* Flags for ARexx script callbacks */
        #define    X_CLONE     0x0001    /* The object has been cloned */
        #define    X_MOVE      0x0002    /* The object has been moved */
        #define    X_ROTATE    0x0004    /* The object has been rotated */
        #define    X_RESIZE    0x0008    /* The object has been resized */
        #define    X_CHANGE    0x0010    /* An attribute (see ATTR) of the 
                                            object has changed */
        #define    X_DELETE    0x0020    /* The object has been deleted */
        #define    X_CUT       0x0040    /* The object has been deleted, but
                                            stored in the clipboard */
        #define    X_COPY      0x0080    /* The object has been copied to the
                                            clipboard */
        #define    X_UNGROUP   0x0100    /* The object has been ungrouped */





For example, given the XTRN object:

        FORM xxxx DR2D {
                XTRN xxxx { X_RESIZE | X_MOVE, 10, "Dimension" }
                ATTR xxxx { 0, 0, 1, 0, 0, 0, 0.0 }
                FORM xxxx DR2D {
                        GRUP xxxx { 2 }
                        STXT xxxx { 0, 0.5, 1.0, 6.0, 5.0, 0.0, 4, "3.0" }
                        OPLY xxxx { 2, { 5.5, 5.5, 8.5, 5.5 } }
                }
        }

ProVector would call the ARexx script named Dimension if the user
resized or moved this object.  What exactly ProVector sends depends
upon what the user does to the object.  The following list shows what
string(s) ProVector sends according to which flag(s) are set.  The
parameters are described below.

        X_CLONE     ``appl CLONE objID dx dy''
        X_MOVE      ``appl MOVE objID dx dy''
        X_ROTATE    ``appl ROTATE objID cx cy angle''
        X_RESIZE    ``appl RESIZE objID cx cy sx sy''
        X_CHANGE    ``appl CHANGE objID et ev ft fv ew jt fn''
        X_DELETE    ``appl DELETE objID''
        X_CUT       ``appl CUT objID''
        X_COPY      ``appl COPY objID''
        X_UNGROUP   ``appl UNGROUP objID''

where:
        appl is the name of the ARexx script
        CLONE, MOVE, ROTATE, RESIZE, etc. are literal strings
        objID is the object ID that ProVector assigns to this object
        (dx, dy) is the position offset of the CLONE or MOVE
        (cx, cy) is the point around which the object is rotated or resized
        angle is the angle (in degrees) the object is rotated
        sx and sy are the scaling factors in the horizontal and
          vertical directions, respectively. 
        et is the edge type (the dash pattern index)
        ev is the edge value (the edge color index)
        ft is the fill type
        fv is the fill index
        ew is the edge weight
        jt is the join type
        fn is the font name

The X_CHANGE message reflects changes to the attributes found in the
ATTR chunk. 

If the user resized the XTRN object shown above by factor of 2,
ProVector would call the ARexx script Dimension like this: 

        Dimension RESIZE 1985427 7.0 4.75 2.0 2.0



The Object Chunks


The following chunks define the objects available in the DR2D FORM.


VBM  (0x56424D20)       /* Virtual BitMap */

The VBM chunk contains the position, dimensions, and file name of an
ILBM image. 


struct VBMstruct {
    IEEE        XPos, YPos,     /* Virtual coords */
                XSize, YSize,   /* Virtual size */
                Rotation;       /* in degrees */
    USHORT      PathLen;        /* Length of dir path */
    char        Path[PathLen];  /* Null-terminated path of file */
};


The coordinate (XPos, YPos) is the position of the upper left hand
corner of the bitmap and the XSize and YSize fields supply the x and y
dimensions to which the image should be scaled.  Rotation tells how
many degrees to rotate the ILBM around its upper left hand corner.
ProVector does not currently support rotation of bitmaps and will
ignore this value.  Path contains the name of the ILBM file and may
also contain a partial or full path to the file.  DR2D readers should
not assume the path is correct.  The full path to an ILBM on one
system may not match the path to the same ILBM on another system.  If
a DR2D reader cannot locate an ILBM file based on the full path name
or the file name itself (looking in the current directory), it should
ask the user where to find the image.
 


CPLY (0x43504C59)       /* Closed polygon */
OPLY (0x4F504C59)       /* Open polygon */

Polygons are the basic components of almost all 2D objects in the DR2D
FORM.  Lines, squares, circles, and arcs are all examples of DR2D
polygons.  There are two types of DR2D polygons, the open polygon
(OPLY) and the closed polygon (CPLY).  The difference between a closed
and open polygon is that the computer adds a line segment connecting
the endpoints of a closed polygon so that it is a continuous path.  An
open polygon's endpoints do not have to meet, like the endpoints of a
line segment.

        struct POLYstruct {
            ULONG       ID;
            ULONG       Size;
            USHORT      NumPoints;
            IEEE        PolyPoints[2*NumPoints];
        };

The NumPoints field contains the number of points in the polygon and
the PolyPoints array contains the (X, Y) coordinates of the points of
the non-curved parts of polygons.  The even index elements are X
coordinates and the odd index elements are Y coordinates.

[Figure 3 - Bezier curves]

DR2D uses Bezier cubic sections, or cubic splines, to describe curves
in polygons.  A set of four coordinates (P1 through P4) defines the
shape of a cubic spline.  The first coordinate (P1) is the point where
the curve begins.  The line from the first to the second coordinate
(P1 to P2) is tangent to the curve at the first point.  The line from
P3 to P4 is tangent to the cubic section, where it ends at P4.

The coordinates describing the cubic section are stored in the
PolyPoints[] array with the coordinates of the normal points.  DR2D
inserts an indicator point before a set of cubic section points to
differentiate a normal point from the points that describe a curve.
An indicator point has an X value of 0xFFFFFFFF.  The indicator
point's Y value is a bit field.  If this bit field's low-order bit is
set, the points that follow the indicator point make up a cubic
section.

The second lowest order bit in the indicator point's bit field is the
MOVETO flag.  If this bit is set, the point (or set of cubic section
points) starts a new polygon, or subpolygon.  This subpolygon will
appear to be completely separate from other polygons but there is an
important connection between a polygon and its subpolygon.
Subpolygons make it possible to create holes in polygons.  An example
of a polygon with a hole is the letter ``O''.  The ``O'' is a filled
circular polygon with a smaller circular polygon within it.  The
reason the inner polygon isn't covered up when the outer polygon is
filled is that DR2D fills are done using the even-odd rule.

The even-odd rule determines if a point is ``inside'' a polygon by
drawing a ray outward from that point and counting the number of path
segments the ray crosses.  If the number is even, the point is outside
the object and shouldn't be filled.  Conversely, an odd number of
crossings means the point is inside and should be filled.  DR2D only
applies the even-odd rule to a polygon and its subpolygons, so no
other objects are considered in the calculations.

Taliesin, Inc. supplied the following algorithm to illustrate the
format of DR2D polygons.  OPLYs, CPLYs, AROWs, and ProVector's outline
fonts all use the same format:


        typedef union {
            IEEE num;
            LONG bits;
        } Coord;

        #define INDICATOR       0xFFFFFFFF
        #define IND_SPLINE      0x00000001
        #define IND_MOVETO      0x00000002

        /* A common pitfall in attempts to support DR2D has
                been to fail to recognize the case when an
                INDICATOR point indicates the following
                coordinate to be the first point of BOTH a
                Bezier cubic and a sub-polygon, ie. the
                value of the flag = (IND_CURVE | IND_MOVETO) */

        Coord   Temp0, Temp1;
        int     FirstPoint, i, Increment;

        /* Initialize the path */
        NewPath();
        FirstPoint = 1;

        /* Draw the path */
        i = 0;
        while( i < NumPoints ) {
            Temp0.num = PolyPoints[2*i];    Temp1.num = PolyPoints[2*i + 1];
            if( Temp0.bits == INDICATOR ) {
                /* Increment past the indicator */
                Increment = 1;
                if( Temp1.bits & IND_MOVETO ) {
                    /* Close and fill, if appropriate */
                    if( ID == CPLY ) {
                        FillPath();
                    }
                    else {
                        StrokePath();
                    }

                    /* Set up the new path */
                    NewPath();
                    FirstPoint = 1;
                }
                if( Temp1.bits & IND_CURVE ) {
                    /* The next 4 points are Bezier cubic control points */
                    if( FirstPoint )
                        MoveTo( PolyPoints[2*i + 2], PolyPoints[2*i + 3] );
                    else
                        LineTo( PolyPoints[2*i + 2], PolyPoints[2*i + 3] );
                    CurveTo(    PolyPoints[2*i + 4], PolyPoints[2*i + 5],
                                PolyPoints[2*i + 6], PolyPoints[2*i + 7],
                                PolyPoints[2*i + 8], PolyPoints[2*i + 9] );
                    FirstPoint = 0;
                    /* Increment past the control points */
                    Increment += 4;
                }
            }
            else {
                if( FirstPoint )
                    MoveTo(     PolyPoints[2*i], PolyPoints[2*i + 1] );
                else
                    LineTo(     PolyPoints[2*i], PolyPoints[2*i + 1] );
                FirstPoint = 0;

                /* Increment past the last endpoint */
                Increment = 1;
            }

            /* Add the increment */
            i += Increment;
        }

        /* Close the last path */
        if( ID == CPLY ) {
            FillPath();
        }
        else {
            StrokePath();
        }



GRUP (0x47525550)       /* Group */

The GRUP chunk combines several DR2D objects into one.  This chunk is
only valid inside nested DR2D FORMs, and must be the first chunk in
the FORM.

        struct GROUPstruct {
            ULONG       ID;
            ULONG       Size;
            USHORT      NumObjs;
        };

The NumObjs field contains the number of objects contained in this
group.  Note that the layer of the GRUP FORM overrides the layer of
objects within the GRUP.  The following example illustrates the layout
of the GRUP (and FILL) chunk.

       FORM { DR2D              /* Top-level drawing... */
                DRHD { ... }    /* Confirmed by presence of DRHD chunk */
                CMAP { ... }    /* Various other things... */
                FONS { ... }
                FORM { DR2D             /* A nested form... */
                        FILL { 1 }      /* Ah!  The fill-pattern table */
                        CPLY { ... }    /* with only 1 object */
                }
                FORM { DR2D             /* Yet another nested form */
                        GRUP { ..., 3 } /* Ah! A group of 3 objects */
                        TEXT { ... }
                        CPLY { ... }
                        OPLY { ... }
                }
                FORM { DR2D             /* Still another nested form */
                        GRUP { ..., 2 } /* A GRUP with 2 objects */
                        OPLY { ... }
                        TEXT { ... }
                }
        }



STXT (0x53545854)               /* Simple text */

The STXT chunk contains a text string along with some information on
how and where to render the text. 

        struct STXTstruct {
            ULONG       ID;
            ULONG       Size;
            UBYTE       Pad0;           /* Always 0 (for future expansion) */
            UBYTE       WhichFont;      /* Which font to use */
            IEEE        CharW, CharH,   /* W/H of an individual char */
                        BaseX, BaseY,   /* Start of baseline */
                        Rotation;       /* Angle of text (in degrees) */
            USHORT      NumChars;
            char        TextChars[NumChars];
        };

The text string is in the character array, TextChars[].  The ID of the
font used to render the text is WhichFont.  The font's ID is set in a
FONS chunk.  The starting point of the baseline of the text is (BaseX,
BaseY).  This is the point around which the text is rotated.  If the
Rotation field is zero (degrees), the text's baseline will originate
at (BaseX, BaseY) and move to the right.  CharW and CharH are used to
scale the text after rotation.  CharW is the average character width
and CharH is the average character height.  The CharW/H fields are
comparable to an X and Y font size.  



TPTH (0x54505448)               /* A text string along a path */

This chunk defines a path (polygon) and supplies a string to render
along the edge of the path.

        struct TPTHstruct {
            ULONG   ID;
            ULONG   Size;
            UBYTE   Justification;      /* see defines, below */
            UBYTE   WhichFont;          /* Which font to use */
            IEEE    CharW, CharH;       /* W/H of an individual char    */
            USHORT  NumChars;           /* Number of chars in the string */
            USHORT  NumPoints;          /* Number of points in the path */
            char    TextChars[NumChars];/* PAD TO EVEN #! */
            IEEE    Path[2*NumPoints];  /* The path on which the text lies */
        };

WhichFont contains the ID of the font used to render the text.
Justification controls how the text is justified on the line.
Justification can be one of the following values:

        #define J_LEFT          0x00    /* Left justified */
        #define J_RIGHT         0x01    /* Right justified */
        #define J_CENTER        0x02    /* Center text */
        #define J_SPREAD        0x03    /* Spread text across path */

CharW and CharH are the average width and height of the font
characters and are akin to X and Y font sizes, respectively.  A
negative FontH implies that the font is upsidedown.  Note that CharW
must not be negative.  NumChars is the number of characters in the
TextChars[] string, the string containing the text to be rendered.
NumPoints is the number of points in the Path[] array.  Path[] is the
path along which the text is rendered.  The path itself is not
rendered.  The points of Path[] are in the same format as the points
of a DR2D polygon.
 


A Simple DR2D Example

Here is a (symbolic) DR2D FORM:

    FORM { DR2D
            DRHD 16 { 0.0, 0.0, 10.0, 8.0 }
            CMAP  6 { 0,0,0, 255,255,255 }
            FONS  9 { 1, 0, 1, 0, "Roman" } 0
            DASH 12 { 1, 2, {1.0, 1.0} }
            ATTR 14 { 0, 0, 1, 0, 0, 0, 0, 0.0 }
            BBOX 16 { 2.0, 2.0, 8.0, 6.0 }
            FORM { DR2D
                  GRUP  2 { 2 }
                  BBOX 16 { 3.0, 4.0, 7.0, 5.0 }
                  STXT 36 { 0,1, 0.5, 1.0, 3.0, 5.0, 0.0, 12, "Hello, World" }
                  BBOX 16 { 2.0, 2.0, 8.0, 6.0 }
                  OPLY 42 { 5, {2.0,2.0, 8.0,2.0, 8.0,6.0, 2.0,6.0, 2.0,2.0 }
            }
    }


[Figure 4 - Simple DR2D drawing]


The OFNT FORM


OFNT    (0x4F464E54)    /* ID of outline font file */

ProVector's outline fonts are stored in an IFF FORM called OFNT.  This
IFF is a separate file from a DR2D.  DR2D's FONS chunk refers only to
fonts defined in the OFNT form.


OFHD    (0x4F464844)    /* ID of OutlineFontHeaDer */

This chunk contains some basic information on the font.

        struct OFHDstruct {
            char   FontName[32];   /* Font name, null padded */
            short  FontAttrs;      /* See FA_*, below */
            IEEE   FontTop,        /* Typical height above baseline */
                   FontBot,        /* Typical descent below baseline */
                   FontWidth;      /* Typical width, i.e. of the letter O */
        };

        #define FA_BOLD         0x0001
        #define FA_OBLIQUE      0x0002
        #define FA_SERIF        0x0004

The FontName field is a NULL terminated string containing the name of
this font.  FontAttrs is a bit field with flags for several font
attributes.  The flags, as defined above, are bold, oblique, and
serif.  The unused higher order bits are reserved for later use.  The
other fields describe the average dimensions of the characters in this
font.  FontTop is the average height above the baseline, FontBot is
the average descent below the baseline, and FontWidth is the average
character width.


KERN    (0x4B45524C)    /* Kerning pair */

The KERN chunk describes a kerning pair.  A kerning pair sets the
distance between a specific pair of characters.  

struct KERNstruct {
    short   Ch1, Ch2;           /* The pair to kern (allows for 16 bits...) */
    IEEE    XDisplace,          /* Amount to displace -left +right */
            YDisplace;          /* Amount to displace -down +up */
};

The Ch1 and Ch2 fields contain the pair of characters to kern.  These
characters are typically stored as ASCII codes.  Notice that OFNT
stores the characters as a 16-bit value.  Normally, characters are
stored as 8-bit values.  The wary programmer will be sure to cast
assigns properly to avoid problems with assigning an 8-bit value to a
16-bit variable.  The remaining fields, XDisplace and YDisplace,
supply the baseline shift from Ch1 to Ch2.




CHDF    (0x43484446)    /* Character definition */

This chunk defines the shape of ProVector's outline fonts.  

struct CHDFstruct {
    short   Ch;         /* The character we're defining (ASCII) */
    short   NumPoints;  /* The number of points in the definition */
    IEEE    XWidth,     /* Position for next char on baseline - X */
            YWidth;     /* Position for next char on baseline - Y */
 /* IEEE    Points[2*NumPoints]*/       /* The actual points */
};


#define INDICATOR       0xFFFFFFFF      /* If X == INDICATOR, Y is an action */
#define IND_SPLINE      0x00000001      /* Next 4 pts are spline control pts */
#define IND_MOVETO      0x00000002         /* Start new subpoly */
#define IND_STROKE      0x00000004      /* Stroke previous path */
#define IND_FILL        0x00000008      /* Fill previous path */

Ch is the value (normally ASCII) of the character outline this chunk
defines.  Like Ch1 and Ch2 in the KERN chunk, Ch is stored as a 16-bit
value.  (XWidth,YWidth) is the offset to the baseline for the
following character.  OFNT outlines are defined using the same method
used to define DR2D's polygons (see the description of OPLY/CPLY for
details).

Because the OFNT FORM does not have an ATTR chunk, it needed an
alternative to make fills and strokes possible.  There are two extra
bits used in font indicator points not found in polygon indicator
points, the IND_STROKE and IND_FILL bits (see defines above).  These
two defines describe how to render the current path when rendering
fonts.

The current path remains invisible until the path is either filled
and/or stroked.  When the IND_FILL bit is set, the currently defined
path is filled in with the current fill pattern (as specified in the
current ATTR chunk).  A set IND_STROKE bit indicates that the
currently defined path itself should be rendered.  The current ATTR's
chunk dictates the width of the line, as well as several other
attributes of the line.  These two bits apply only to the OFNT FORM
and should not be used in describing DR2D polygons.