A shape is a list of fill styles, a list of line styles and a run of shape records: moves, straight edges and quadratic curves, all in twips. Three tags define shapes in this format, each adding to the one before; a fourth defines morph shapes, which blend between a start and an end shape.
DefineShape (2)
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 2 | UI16 | ShapeId | The character ID |
| 2 | varies | RECT | ShapeBounds | The bounds, including the line widths |
| … | varies | SHAPEWITHSTYLE | Shapes | Styles and records |
Colours are RGB. Each style list has at most 255 entries, and the records cannot start new style lists.
DefineShape2 (22, SWF 2)
The same fields as DefineShape. A style count of 0xFF is followed by a UI16 with the real count, and a style change record may carry new style lists (StateNewStyles). Colours are RGB.
DefineShape3 (32, SWF 3)
The same fields as DefineShape2, with RGBA colours in every fill style, gradient record and line style.
SHAPEWITHSTYLE
| Type | Name | Meaning |
|---|---|---|
| FILLSTYLEARRAY | FillStyles | The fill styles |
| LINESTYLEARRAY | LineStyles | The line styles |
| UB[4] | NumFillBits | Bits in a fill style index in the records |
| UB[4] | NumLineBits | Bits in a line style index in the records |
| SHAPERECORD[] | ShapeRecords | Records to the end record, then padding to a byte |
SHAPE
A shape without style lists, used for font glyphs and for the edges of morph
shapes: UB[4] NumFillBits, UB[4] NumLineBits, then
SHAPERECORD[] to the end record. A glyph uses fill style 1, which is implied, and no
lines.
Fill styles
A FILLSTYLEARRAY is a UI8 FillStyleCount; if it is 0xFF (DefineShape2
and DefineShape3), a UI16 FillStyleCountExtended follows with the real
count. Then the FILLSTYLEs. Records refer to them by index from 1; 0 means no
fill.
| Type | Name | When |
|---|---|---|
| UI8 | FillStyleType | Always |
| RGB or RGBA | Color | Solid (0x00): RGB in DefineShape and DefineShape2, RGBA in DefineShape3 |
| MATRIX | GradientMatrix | Gradients (0x10, 0x12) |
| GRADIENT | Gradient | Gradients |
| UI16 | BitmapId | Bitmaps (0x40, 0x41); 65,535 means none |
| MATRIX | BitmapMatrix | Bitmaps |
| Type | Fill |
|---|---|
| 0x00 | Solid colour |
| 0x10 | Linear gradient |
| 0x12 | Radial gradient |
| 0x40 | Repeating bitmap |
| 0x41 | Clipped bitmap |
Later versions added non-smoothed bitmap fills, 0x42 and 0x43; in which version is not confirmed here, and the explorer reads them in any movie. The bitmap matrix maps bitmap pixels into the shape’s twips, so a scale of 20 draws one bitmap pixel to one screen pixel.
Gradients
A gradient is drawn in a square of its own that runs from −16,384 to 16,384 twips (819.2 pixels) each way; the gradient matrix maps that square onto the shape. A linear gradient sets the colour by x, ratio 0 at the left edge and 255 at the right. A radial gradient sets it by distance from the centre, 255 at a radius of 16,384.
| Type | Name | Meaning |
|---|---|---|
| UI8 | NumGradients | 1 to 8 colour stops. Later formats use the top four bits of this byte for spread and interpolation modes; here they are 0 |
| GRADRECORD × n | GradientRecords | UI8 Ratio (0 to 255), then an RGB or RGBA Color by the shape tag |
Line styles
A LINESTYLEARRAY is a UI8 LineStyleCount, 0xFF then a UI16 for more in
DefineShape2 and DefineShape3, then the LINESTYLEs. Index 0 in a record means no
line.
| Type | Name | Meaning |
|---|---|---|
| UI16 | Width | In twips |
| RGB or RGBA | Color | RGB in DefineShape and DefineShape2, RGBA in DefineShape3 |
A line style here has no cap or join settings; players draw the lines with round ends and joins.
Shape records
The records are packed into bits one after another, with no padding between them; the run is padded to a byte after its end record. The pen starts at (0, 0). The first bit says which kind of record follows: 0 a style change or the end, 1 an edge.
End record
UB[1] TypeFlag 0, then UB[5] EndOfShape 0: six zero
bits.
Style change record
| Type | Name | Meaning |
|---|---|---|
| UB[1] | TypeFlag | 0 |
| UB[1] | StateNewStyles | New style lists follow (DefineShape2 and DefineShape3) |
| UB[1] | StateLineStyle | A line style index follows |
| UB[1] | StateFillStyle1 | A FillStyle1 index follows |
| UB[1] | StateFillStyle0 | A FillStyle0 index follows |
| UB[1] | StateMoveTo | A move follows |
| UB[5] | MoveBits | If StateMoveTo |
| SB[MoveBits] | MoveDeltaX | If StateMoveTo: the new pen x, from the shape’s origin |
| SB[MoveBits] | MoveDeltaY | If StateMoveTo: the new pen y |
| UB[NumFillBits] | FillStyle0 | If StateFillStyle0 |
| UB[NumFillBits] | FillStyle1 | If StateFillStyle1 |
| UB[NumLineBits] | LineStyle | If StateLineStyle |
| FILLSTYLEARRAY, LINESTYLEARRAY, UB[4], UB[4] | FillStyles, LineStyles, NumFillBits, NumLineBits | If StateNewStyles: byte-aligned new lists, which replace the old ones for the records that follow; indexes start again at 1 |
The five flags are not all zero, which is how it differs from the end record. Despite its name, the move is absolute, from the shape’s origin, not from the pen.
Straight edge record
| Type | Name | Meaning |
|---|---|---|
| UB[1] | TypeFlag | 1 |
| UB[1] | StraightFlag | 1 |
| UB[4] | NumBits | The bits in each delta, minus 2 |
| UB[1] | GeneralLineFlag | 1: both deltas follow |
| UB[1] | VertLineFlag | Only if GeneralLineFlag is 0: 1 vertical, 0 horizontal |
| SB[NumBits + 2] | DeltaX | If general, or horizontal |
| SB[NumBits + 2] | DeltaY | If general, or vertical |
Draws a line from the pen to the pen plus the deltas, which then becomes the pen.
Curved edge record
| Type | Name | Meaning |
|---|---|---|
| UB[1] | TypeFlag | 1 |
| UB[1] | StraightFlag | 0 |
| UB[4] | NumBits | The bits in each delta, minus 2 |
| SB[NumBits + 2] | ControlDeltaX | The control point, from the pen |
| SB[NumBits + 2] | ControlDeltaY | |
| SB[NumBits + 2] | AnchorDeltaX | The end point, from the control point |
| SB[NumBits + 2] | AnchorDeltaY |
A quadratic Bézier curve from the pen through the control point’s pull to the anchor, which becomes the pen.
Fills on two sides
Every edge carries the current FillStyle0 and FillStyle1: the fill on each side of it as it is drawn, FillStyle0 on the left and FillStyle1 on the right in the specification’s terms. Flash draws shapes this way so that touching regions share one edge. To fill a shape:
- Walk the records, keeping the pen and the current styles, and turn each edge into a segment with its two fill styles and its line style.
- For each fill style, take the edges that have it as FillStyle1 as they were drawn, and the edges that have it as FillStyle0 reversed.
- Join them end to end into closed outlines, all wound the same way, and fill those. The explorer uses the non-zero rule.
- Draw the lines over the fills, edge by edge, in each edge’s line style.
A worked example
A DefineShape from a Flash 4 movie: a red circle 6.5 pixels wide with a black outline, 54 bytes.
00000891 BF 00 36 00 00 00 02 00 45 FA 0D FA 08 01 00 FF
000008A1 00 00 01 14 00 00 00 00 11 34 ED C0 72 00 BE 08
000008B1 49 82 14 81 25 20 61 84 98 60 05 32 01 4A 18 48
000008C1 86 0B 81 22 E0 20 84 88 20 02 E0 00
BF 00is 0x00BF: code 2, DefineShape, length 63, so the long form;36 00 00 00is 54 bytes.02 00is shape 2.- RECT
45 FA 0D FA 08: Nbits 8, then −65, 65, −65, 65 twips: −3.25 to 3.25 pixels. - Fill styles:
01, one;00 FF 00 00, solid #FF0000. Line styles:01, one;14 00, 20 twips (1 pixel) wide,00 00 00black. 11: NumFillBits 1, NumLineBits 1.34 ED C0 72begins the records:0a style change;0 1 1 0 1: no new styles, a line style, a FillStyle1, no FillStyle0, a move. MoveBits 7, MoveDeltaX 55, MoveDeltaY 0, FillStyle1 1, LineStyle 1.- Eight curved edges follow with NumBits 4, so 6-bit deltas, then the end record and the padding.
DefineMorphShape (46, SWF 3)
| Type | Name | Meaning |
|---|---|---|
| UI16 | CharacterId | The character ID |
| RECT | StartBounds | Bounds of the start shape |
| RECT | EndBounds | Bounds of the end shape |
| UI32 | Offset | From after this field to EndEdges |
| MORPHFILLSTYLEARRAY | MorphFillStyles | Fill styles with start and end values |
| MORPHLINESTYLEARRAY | MorphLineStyles | Line styles with start and end values |
| SHAPE | StartEdges | The start shape’s records |
| SHAPE | EndEdges | The end shape’s records |
Every morph style holds a start and an end value of each of its parts. A morph fill is a UI8 type, then for a solid fill RGBA StartColor and RGBA EndColor; for a gradient a start and an end MATRIX and a morph gradient; for a bitmap a UI16 BitmapId and a start and an end MATRIX. A morph gradient is a UI8 NumGradients, then each record as UI8 StartRatio, RGBA StartColor, UI8 EndRatio, RGBA EndColor. A morph line is UI16 StartWidth, UI16 EndWidth, RGBA StartColor, RGBA EndColor. The array counts work as in shapes: a UI8, or 0xFF and a UI16.
EndEdges has the same edges as StartEdges, edge for edge, with no style changes of its own. The player draws the shape at the PlaceObject2 ratio: each coordinate, colour, width and matrix entry is start + (end − start) × ratio ÷ 65,535.
Flash 5 wrote empty morph shapes with Offset 0; the start edges then end at their end record.
Shape tags from later versions
These came with SWF 8 and have no place in a SWF 1 to 5 movie. Where one turns up, the explorer reads it with the fields below.
| Tag | SWF | Fields |
|---|---|---|
| DefineShape4 (83) | 8 | UI16 ShapeId, RECT ShapeBounds, RECT EdgeBounds, a byte of stroke and winding flags, then SHAPEWITHSTYLE with a longer line style record, LINESTYLE2, and focal gradients |
| DefineMorphShape2 (84) | 8 | DefineMorphShape with start and end edge bounds and a flags byte after EndBounds, and the longer morph line styles |