A shape is a list of fill styles and line styles followed by edges: straight lines and quadratic curves, packed into bits. All coordinates are in twips. Four tags define shapes; a CWS movie may hold any of them, DefineShape4 from SWF 8. Offsets are from the start of the tag’s body, in the decompressed data.
DefineShape (2)
| Type | Name | Description |
|---|---|---|
| UI16 | ShapeId | The character ID |
| RECT | ShapeBounds | The bounds, line widths included |
| SHAPEWITHSTYLE | Shapes | The styles and the edges |
SWF 1. Up to 255 fill styles and 255 line styles, RGB colours, and no new styles inside the records.
DefineShape2 (22)
SWF 2. The same fields as DefineShape. Style counts may be 255 or more (a count byte of 0xFF, then a UI16), and style-change records may carry new style lists (StateNewStyles). Colours are RGB.
DefineShape3 (32)
SWF 3. The same fields as DefineShape2, with RGBA colours everywhere: solid fills, gradient records and line styles.
DefineShape4 (83)
| Type | Name | Description |
|---|---|---|
| UI16 | ShapeId | The character ID |
| RECT | ShapeBounds | The bounds, line widths included |
| RECT | EdgeBounds | The bounds without the lines |
| UB[5] | Reserved | 0 |
| UB[1] | UsesFillWindingRule | SWF 10: fill with the non-zero winding rule |
| UB[1] | UsesNonScalingStrokes | At least one line style does not scale |
| UB[1] | UsesScalingStrokes | At least one line style scales |
| SHAPEWITHSTYLE | Shapes | With LINESTYLE2 line styles |
SWF 8. RGBA colours, LINESTYLE2 and focal gradients.
SHAPEWITHSTYLE
| Type | Name | Description |
|---|---|---|
| FILLSTYLEARRAY | FillStyles | The fill styles |
| LINESTYLEARRAY | LineStyles | The line styles |
| UB[4] | NumFillBits | The bits in a fill style index |
| UB[4] | NumLineBits | The bits in a line style index |
| SHAPERECORD[] | ShapeRecords | To an end record, then padded to a byte |
SHAPE
A shape without style lists, used for font glyphs and the edges of morph shapes: UB[4] NumFillBits, UB[4] NumLineBits, then SHAPERECORDs to an end record, padded. A glyph uses fill style 1, which is implied, and no lines.
Fill styles
FILLSTYLEARRAY: a UI8 FillStyleCount; if it is 0xFF (DefineShape2 and later), a UI16 FillStyleCountExtended follows. Then that many FILLSTYLEs. Shape records refer to them by index from 1; 0 means no fill.
FILLSTYLE: a UI8 FillStyleType, then by type:
| Type | Fill | Then |
|---|---|---|
| 0x00 | Solid | RGB Color (DefineShape, DefineShape2) or RGBA (DefineShape3, DefineShape4) |
| 0x10 | Linear gradient | MATRIX GradientMatrix, GRADIENT |
| 0x12 | Radial gradient | MATRIX GradientMatrix, GRADIENT |
| 0x13 | Focal radial gradient | MATRIX GradientMatrix, FOCALGRADIENT (SWF 8, DefineShape4) |
| 0x40 | Repeating bitmap | UI16 BitmapId, MATRIX BitmapMatrix |
| 0x41 | Clipped bitmap | UI16 BitmapId, MATRIX BitmapMatrix |
| 0x42 | Non-smoothed repeating bitmap | UI16 BitmapId, MATRIX BitmapMatrix |
| 0x43 | Non-smoothed clipped bitmap | UI16 BitmapId, MATRIX BitmapMatrix |
Types 0x42 and 0x43 came later than 0x40 and 0x41; the version is not confirmed here. A BitmapId of 65,535 means no bitmap. The bitmap matrix maps bitmap pixels into the shape’s twips, so a scale of 20 draws one bitmap pixel per screen pixel.
Gradients
A gradient is drawn in a square from −16,384 to 16,384 twips (819.2 pixels each way) in gradient space, and GradientMatrix maps that square onto the shape. A linear gradient runs along x, ratio 0 at the left edge and 255 at the right. A radial gradient runs by distance from the centre, with a radius of 16,384 at ratio 255.
GRADIENT, packed into a byte then records:
| Type | Name | Description |
|---|---|---|
| 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 × NumGradients | GradientRecords | Each a UI8 Ratio (0 to 255) and an RGB or RGBA Color, by shape tag |
FOCALGRADIENT is the same, then a FIXED8 FocalPoint from −1 to 1: where the focus sits along the x axis of the gradient circle.
Line styles
LINESTYLEARRAY: a UI8 LineStyleCount (0xFF then a UI16 in DefineShape2 and later), then LINESTYLEs (DefineShape to DefineShape3) or LINESTYLE2s (DefineShape4).
LINESTYLE: UI16 Width in twips, then an RGB or RGBA Color.
LINESTYLE2 (SWF 8):
| Type | Name | Description |
|---|---|---|
| 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 stroke is filled with a FILLSTYLE |
| UB[1] | NoHScaleFlag | Do not scale the width horizontally |
| UB[1] | NoVScaleFlag | Do not scale it vertically |
| UB[1] | PixelHintingFlag | Snap to whole pixels |
| UB[5] | Reserved | 0 |
| UB[1] | NoClose | Do not join the ends of a closed path |
| UB[2] | EndCapStyle | 0 round, 1 none, 2 square |
| UI16 | MiterLimitFactor | 8.8, only if JoinStyle is 2 |
| RGBA | Color | Only if HasFillFlag is 0 |
| FILLSTYLE | FillType | Only if HasFillFlag is 1: a gradient or bitmap stroke |
Shape records
Shape records are packed into bits one after another, with no padding between them. The first bit says which kind a record is; the run ends with an end record and is padded to a byte only there.
EndShapeRecord
| Type | Name | Value |
|---|---|---|
| UB[1] | TypeFlag | 0 |
| UB[5] | EndOfShape | 0 |
StyleChangeRecord
| Type | Name | Description |
|---|---|---|
| UB[1] | TypeFlag | 0 |
| UB[1] | StateNewStyles | New style lists follow (DefineShape2 and later) |
| UB[1] | StateLineStyle | LineStyle follows |
| UB[1] | StateFillStyle1 | FillStyle1 follows |
| UB[1] | StateFillStyle0 | FillStyle0 follows |
| UB[1] | StateMoveTo | A move follows. The five flags are not all 0 (that would be the end record) |
| UB[5] | MoveBits | If StateMoveTo |
| SB[MoveBits] | MoveDeltaX, MoveDeltaY | If StateMoveTo: an absolute position from the shape’s origin, despite the name |
| 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 |
StraightEdgeRecord
| Type | Name | Description |
|---|---|---|
| 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 |
| SB[NumBits + 2] | DeltaX, DeltaY | If GeneralLineFlag |
| UB[1] | VertLineFlag | If not GeneralLineFlag: 1 vertical, 0 horizontal |
| SB[NumBits + 2] | DeltaY or DeltaX | If not GeneralLineFlag: the one delta |
CurvedEdgeRecord
| Type | Name | Description |
|---|---|---|
| UB[1] | TypeFlag | 1 |
| UB[1] | StraightFlag | 0 |
| UB[4] | NumBits | The bits in each delta, minus 2 |
| SB[NumBits + 2] | ControlDeltaX, ControlDeltaY | The control point, relative to the pen |
| SB[NumBits + 2] | AnchorDeltaX, AnchorDeltaY | The end point, relative to the control point |
A quadratic Bézier curve. The pen then sits at the anchor.
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 regions that touch share one edge. To fill a shape:
- For each fill style, collect the edges that have it as FillStyle1, as drawn, and the edges that have it as FillStyle0, reversed.
- Join them end to end. They form closed outlines, all wound the same way.
- Fill those outlines. The explorer uses the non-zero rule.
- Draw the lines over the fills, edge by edge, in each edge’s LineStyle.
A shape, byte by byte
The bytes of a shape are the same in every SWF movie, compressed or not. This red circle, 6.5 pixels wide, is a DefineShape from an uncompressed SWF 4 movie examined for this site:
BF 00 36 00 00 00 02 00 45 FA 0D FA 08 01 00 FF
00 00 01 14 00 00 00 00 11 34 ED C0 72 00 BE 08
49 82 14 81 25 20 61 84 98 60 05 32 01 4A 18 48
86 0B 81 22 E0 20 84 88 20 02 E0 00
BF 00= 0x00BF: code 2, length 63, the long form;36 00 00 00= 54 bytes.02 00: ShapeId 2.- RECT
45 FA 0D FA 08: Nbits 8 (01000), then −65, 65, −65, 65 twips: −3.25 to 3.25 pixels each way. - 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.
Then the records. 34 ED C0 72 in bits is
0 0 1 1 0 1 | 00111 | 0110111 | 0000000 | 1 | 1 …:
- TypeFlag 0, a style change: StateNewStyles 0, StateLineStyle 1, StateFillStyle1 1, StateFillStyle0 0, StateMoveTo 1.
- MoveBits 7; MoveDeltaX 55, MoveDeltaY 0: the pen moves to (55, 0) twips.
- FillStyle1 = 1 (the red fill on the right of the edges) and LineStyle = 1.
The last bits of 0x72, 1 0 0 1 0, start a curved edge:
TypeFlag 1, StraightFlag 0, and with the first bit of 0x00, NumBits 4,
so each delta has 6 bits. Then 000000, 010111,
110000 and 010000: control (0, 23) from the pen, anchor
(−16, 16) from the control point. From (55, 0) the curve bends through (55, 23) to
(39, 39), an eighth of the circle. Seven more curved edges follow with 6-bit deltas,
then the end record, padded to the end of the tag’s last byte.
DefineMorphShape (46)
SWF 3. A shape that changes into another as it plays.
| Type | Name | Description |
|---|---|---|
| UI16 | CharacterId | The character ID |
| RECT | StartBounds | The bounds at the start |
| RECT | EndBounds | The bounds at the end |
| 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 shape at the start |
| SHAPE | EndEdges | The shape at the end |
Morph styles hold a start and an end value of everything: two colours, two matrices, gradient records as StartRatio, StartColor, EndRatio, EndColor (all RGBA), and line widths as StartWidth and EndWidth. Morph gradients have a plain UI8 NumGradients, with no spread or interpolation bits. EndEdges has the same records as StartEdges, edge for edge, without style changes of its own.
The player draws the shape at PlaceObject2’s Ratio ÷ 65,535: each coordinate, colour, width and matrix entry is start + (end − start) × ratio ÷ 65,535. Flash 5 wrote Offset 0 for an empty morph shape; the start edges then end at their end record.
DefineMorphShape2 (84)
SWF 8. DefineMorphShape with these fields after EndBounds:
| Type | Name | Description |
|---|---|---|
| RECT | StartEdgeBounds | The start bounds without the lines |
| RECT | EndEdgeBounds | The end bounds without the lines |
| UB[6] | Reserved | 0 |
| UB[1] | UsesNonScalingStrokes | At least one line style does not scale |
| UB[1] | UsesScalingStrokes | At least one line style scales |
Its line styles are MORPHLINESTYLE2s: LINESTYLE2 with start and end widths and colours or fills.