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. Four tags define shapes, each adding to the one before; two more define 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.
DefineShape4 (83, SWF 8)
| Type | Name | Meaning |
|---|---|---|
| UI16 | ShapeId | The character ID |
| RECT | ShapeBounds | The bounds, including the line widths |
| RECT | EdgeBounds | The bounds without the lines |
| UB[5] | Reserved | 0 |
| UB[1] | UsesFillWindingRule | Fill with the non-zero winding rule (SWF 10) |
| UB[1] | UsesNonScalingStrokes | Some lines do not scale |
| UB[1] | UsesScalingStrokes | Some lines scale |
| SHAPEWITHSTYLE | Shapes | Styles and records; line styles are LINESTYLE2 |
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 later), 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 and 4 |
| MATRIX | GradientMatrix | Gradients (0x10, 0x12, 0x13) |
| GRADIENT or FOCALGRADIENT | Gradient | Gradients; FOCALGRADIENT for 0x13 |
| UI16 | BitmapId | Bitmaps (0x40 to 0x43); 65,535 means none |
| MATRIX | BitmapMatrix | Bitmaps |
| Type | Fill |
|---|---|
| 0x00 | Solid colour |
| 0x10 | Linear gradient |
| 0x12 | Radial gradient |
| 0x13 | Focal radial gradient (SWF 8, DefineShape4) |
| 0x40 | Repeating bitmap |
| 0x41 | Clipped bitmap |
| 0x42 | Non-smoothed repeating bitmap |
| 0x43 | Non-smoothed clipped bitmap |
0x42 and 0x43 came later than 0x40 and 0x41; the version is not confirmed here. 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 |
|---|---|---|
| UB[2] | SpreadMode | SWF 8: 0 pad, 1 reflect, 2 repeat, 3 reserved; 0 before |
| UB[2] | InterpolationMode | SWF 8: 0 normal RGB, 1 linear RGB; 0 before |
| UB[4] | NumGradients | 1 to 8 before SWF 8, 1 to 15 from SWF 8 |
| GRADRECORD × n | GradientRecords | UI8 Ratio (0 to 255), then an RGB or RGBA Color by the shape tag |
| FIXED8 | FocalPoint | FOCALGRADIENT only: −1 to 1, the focus along the x axis of the gradient circle |
Line styles
A LINESTYLEARRAY is a UI8 LineStyleCount, 0xFF then a UI16 for more in
DefineShape2 and later, then LINESTYLEs (DefineShape to DefineShape3) or LINESTYLE2s
(DefineShape4). Index 0 in a record means no line.
LINESTYLE: UI16 Width in twips, then an RGB colour
(DefineShape, DefineShape2) or RGBA (DefineShape3).
LINESTYLE2 (SWF 8):
| Type | Name | Meaning |
|---|---|---|
| UI16 | Width | In twips |
| UB[2] | StartCapStyle | 0 round, 1 none, 2 square |
| UB[2] | JoinStyle | 0 round, 1 bevel, 2 miter |
| UB[1] | HasFillFlag | The line is filled with a fill style |
| UB[1] | NoHScaleFlag | The width does not scale horizontally |
| UB[1] | NoVScaleFlag | The width does not scale vertically |
| UB[1] | PixelHintingFlag | Snap to whole pixels |
| UB[5] | Reserved | 0 |
| UB[1] | NoClose | Do not close a stroke that ends where it started |
| UB[2] | EndCapStyle | 0 round, 1 none, 2 square |
| UI16 | MiterLimitFactor | Only if JoinStyle is 2: 8.8 fixed point |
| RGBA | Color | If HasFillFlag is 0 |
| FILLSTYLE | FillType | If HasFillFlag is 1: a gradient or bitmap stroke |
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 later) |
| 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 DefineShape2 from a SWF 6 movie: a grey circle 2 pixels wide with a red outline, 54 bytes.
00000065 BF 05 30 00 00 00 04 00 35 8A 58 A0 01 00 D3 D3
00000075 D3 01 01 00 FF 00 00 11 2C CA 01 C6 04 68 D1 E8
00000085 D8 04 78 06 B5 1E B4 0C 46 0C 1B 51 9B 48 04 68
00000095 01 8D 19 8C 04 00
BF 05is 0x05BF: code 22, DefineShape2, length 63, so the long form;30 00 00 00is 48 bytes.04 00is shape 4.- RECT
35 8A 58 A0: Nbits 6, then −20, 20, −20, 20 twips: −1 to 1 pixel each way. - Fill styles:
01, one;00 D3 D3 D3, solid #D3D3D3. Line styles:01, one;01 00, 1 twip wide,FF 00 00red. 11: NumFillBits 1, NumLineBits 1.2C CA 01 C6begins the records:0a style change;0 1 0 1 1: no new styles, a line style, no FillStyle1, a FillStyle0, a move. MoveBits00110is 6; MoveDeltaX010100is 20, MoveDeltaY000000is 0; FillStyle01; LineStyle1.- Eight curved edges follow, each
1 0then NumBits0011(5-bit deltas). The first has control (0, 8) and anchor (−6, 6): from (20, 0) through (20, 8) to (14, 14), an eighth of the circle. - The end record’s six zero bits and the padding finish the last bytes.
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 plain UI8 NumGradients, with no spread or interpolation bits, 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.
DefineMorphShape2 (84, SWF 8)
DefineMorphShape with four more fields after EndBounds, and MORPHLINESTYLE2 line styles:
| Type | Name | Meaning |
|---|---|---|
| RECT | StartEdgeBounds | Start bounds without the lines |
| RECT | EndEdgeBounds | End bounds without the lines |
| UB[6] | Reserved | 0 |
| UB[1] | UsesNonScalingStrokes | Some lines do not scale |
| UB[1] | UsesScalingStrokes | Some lines scale |
A MORPHLINESTYLE2 is UI16 StartWidth and UI16 EndWidth, then the same cap, join and flag bits as LINESTYLE2, the miter limit when the join is a miter, then RGBA StartColor and EndColor, or a morph fill style when HasFillFlag is set.