This document license
About SWF
Brief History
What is SWF?
The geometry in SWF Table of all the SWF tags sorted by number and version
How the structures are described
The File Header
Descriptions of each tag
Common Structures
Appendix A
History of this reference
Copyright (c) 2002-2005 Made to Order Software, Ltd.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Please, note that MP3 audio encoders and decoders can freely be used as long as you don't generate any revenue from them. If you intend to sell or buy a product which uses an MP3 audio encoder or encoder or both, you most certainly want to know more about licensing issues in regard to that concept. You can find all the necessary information on the following site: http://www.mp3-tech.org/
This document was created because the one found on OpenSWF.org was not quite as explainatory as it could have been (though it was good already).
This document includes code examples for really difficult points (like bit fields) and it will explain with words what is really not clear otherwise.
The tags are sorted by number within the corresponding SWF version. It would be necessary to create tables where the tags are sorted differently (alphabetically, by type/category, etc.)
SWF (pronounced like swiff by some) is a file format used to describe movies built of mainly two elements: vector based objects and images. The newest versions also accept sounds and many different possible interaction with the end user.
The file format was first created by Macromedia and had a main goal: create small files of highly intertaining animations. The idea was to have a format which could be reused by a player running on any system and which would work with slower network (such as a browser used with a modem). The format is fairly simple also.
This document will, we hope, help you in developing your own players and/or generators of SWF file formats.
The SWF file formats uses several types of objects. The ones used the most are called shapes. These are vector based objects which can be rendered really fast in 2D. The other type of graphical objects are images, fonts, colors and matrices. More information about the SWF geometry is given in the Appendix A below.
|
The following table lists the types used in this document. The comment will explain in more details how each type is used. The notes below also include example where it applies.
|
BROWSER NOTE: I.E. interprets the ']' character as a space character. I use Netscape so I'm fine! However if you know of a fix, I'd appreciate the hint.
* short, long and fixed values are saved in little endian in the file when they are not found in a file; the following is how you can compute a short value when you read two bytes for an unsigned short:
long read_short(FILE *f) { unsigned char input; fread(input, 2, 1, f); return input[0] + input[1] * 256; }
* char, short, long and fixed values which are not part of a bit field are always aligned on a byte.
** a bit field which specifies one of char, short or long is aligned on a byte
** all bit fields are always declared from the MSB to the LSB of the bytes read from the input file; the first bit read in the file correspond to the MSB of the value being read; as a side effect, the bytes in the file appear as if they were defined in big endian order (which is the opposite of the char, short, long and fixed declared outside a bit field); the following is a slow but working algorithm to read bit fields:
/* global variables (could be in a structure or an object) */ long mask, count, last_byte; /* call once before to read a bit field to reset the parameters */ void start_read_bits(void) { mask = 0x80; count = 0; } /* call for each bit field */ long read_bits(long bit_size) { /* NOTE: any value is at most 32 bits */ /* the result could also be an unsigned long */ long result; unsigned long bit; bit = 1 << (bit_size - 1); while(bit != 0) { if(mask == 0x80) { last_byte = read_input(); } if(last_byte & mask) { result |= bit; } mask /= 2; if(mask == 0) { mask = 0x80; } bit /= 2; } }
struct swf_header { unsigned char f_magic[3]; 'FWS' or 'CWS' unsigned char f_version; unsigned long f_file_length; } struct swf_header_movie { swf_rect f_frame_size; unsigned short f_frame_rate; unsigned short f_frame_count; };
The f_magic[3] array is defined as the characters: 'FWS' (it is going backward probably because it was supposed to be read in a little endian as a long word). A movie can be compressed when the version is set to 6. In this case, the magic characters are: 'CWS'.
The f_version is a value from 1 to 6 (the maximum at time of writing, the maximum will continue to increase).
The f_file_length is exactly what it says. That's useful for all these network connections which don't give you the size of the file. In case of a compressed movie, this is the total length of the uncompressed movie (useful to allocate the destination buffer for zlib).
The f_frame_size is a rectangle definition in TWIPS used to set the size of the frame on the screen. The minx and miny are usually not necessary in this rectangle definition.
The parameter in the swf_header_movie structure are part of the buffer which gets compressed in a V6.x movie (in other words, only the very first 8 bytes of the resulting file aren't compressed).
The f_frame_rate is a fixed value of 8.8 bits. It represents the number of frames per second the movie should be played at.
The f_frame_count is a counter of the number of SHOW FRAME within that movie. Most of the tools will compute this number automatically.
These tags declares a JPEG image bitmap. The V1.0 doesn't include the encoding tables which are defined in the JPEGTables instead. All the DefineBits entries will use the only JPEGTables tag. The other tags will define the corresponding tables for each image. The DefineBitsJPEG3 will also include an alpha channel bitplane (8 bits). This alpha channel is compressed using the ZLIB scheme as with the Lossless image formats.
struct swf_definebitsjpeg { swf_tag f_tag; /* 6, 21 or 35 */ unsigned short f_id; if(f_tag == 35) { /* sizeof(f_encoding) + sizeof(f_image_data) */ unsigned long f_offset_to_alpha; } if(f_tag != 6) { unsigned char f_encoding[<variable size>]; } unsigned char f_image_data[<variable size>]; if(f_tag == 35) { unsigned char f_alpha[<variable size>]; } };
The f_encoding should include 0xFF 0xDB and 0xFF 0xC4 entries. The f_image_data buffer should include the 0xFF 0xE0, 0xFF 0xC0 and 0xFF 0xDA. Both buffers need to be started and ended with a 0xFF 0xD8 and 0xFF 0xD9. The f_alpha buffer is compressed with ZLIB as defined in the DefineBitsLossless tag (this is similar to the PNG format).
These tags declares a loss-less image bitmap. It has a small header followed by an optional colormap and the bitmap data. When we have a colormap, the bitmap data is an array of indices in the colormap which is aligned to 32 bits on a per row basis.
There are three supported formats:
Format No. (bits) |
Color Format | Comments | |
---|---|---|---|
Without Alpha |
With Alpha |
||
3 (8 bits*) |
RGB | RGBA | Uses a colormap with up to 256 entries of 24 or 32 bits colors |
4 (16 bits*) |
RGB555 | RGB555 | There is no alpha available in this format. The data is saved in big endian (it is NOT a U16 like some doc. mention). The colors looks like this (most significant bit first): 0RRRRRGGGGGBBBBB. You should certainly always use the DefineBitsLossless tag for this format. |
5 (32 bits) |
XRGB | ARGB | Uses a strange scheme for colors. Most probably because the alpha was added later and thus inserted in place of the X to keep some backward compatibility with older versions. |
4 - width & 3
when
width & 3
is not zero.).
In 16 bits, you need to add two bytes at the end of each row
when the width of the image is odd.
The f_colormap, f_indices and f_bitmap are all compressed with the ZLIB scheme.
struct swf_definebitslossless { swf_tag f_tag; /* 20 or 36 */ unsigned short f_id; unsigned char f_format; /* 3, 4 or 5 */ unsigned short f_width; unsigned short f_height; if(f_format == 3) { unsigned char f_colormap_count; if(f_tag == DefineBitsLossless) { swf_rgb f_colormap[f_colormap_count]; } else { swf_rgba f_colormap[f_colormap_count]; } unsigned char f_indices[((f_width + 3) & -4) * f_height]; } else { if(f_tag == DefineBitsLossless) { swf_xrgb f_bitmap[f_width * f_height]; } else { swf_argb f_bitmap[f_width * f_height]; } } };
The interactivity of the SWF format comes from the buttons. All the buttons have an ID and can be placed in the display list like any other shape.
A buttons has different states. Some states can be entered only when the button was in a specific state before (like a button being pushed).
Buttons can be represented graphically in any manner you want. Each state can use a different edit text, shape, sprite or text to render the button.
struct swf_definebutton { swf_tag f_tag; /* 7 */ unsigned short f_id; swf_button f_buttons; swf_action f_actions; };
The f_buttons and f_actions are null terminated arrays (the end marker in either case is a byte set to zero).
There will always be at least one f_buttons since the object require at least one shape to be drawn (though the shape can very well be transparent and empty).
There is no need for any action. The action(s) are executed whenever the button is pushed. Note that it is possible to execute actions also when the mouse moves over a button (in, out, over) with the use of a sprite in V5.0+. However, in this case it is certainly preferable to use a DefineButton2 instead.
The DefineButton2 is very similar to the DefineButton tag. The list of actions was however changed in a list of actions to execute on a condition. Whenever an event occur, the plugin checks for that condition within all the buttons which can possibly catch that event. If it does, then the corresponding actions are executed.
struct swf_definebutton2 { swf_tag f_tag; /* 34 */ unsigned short f_id; unsigned f_reserved : 7; unsigned f_menu : 1; unsigned short f_buttons_size; swf_button f_buttons; swf_condition f_conditions; };
The f_buttons_size is equal to the size of the f_buttons buffer plus 2 (the size of the f_buttons_size field itself).
The DefineButton doesn't include any means to transform the colors of the shapes it includes. This tag was thus added just so one can transform a button colors. It is wise to use the new DefineButton2 instead so the transformation can be applied on a per state basis.
struct swf_definebuttoncxform { swf_tag f_tag; /* 23 */ unsigned short f_button_id; swf_color_transform f_color_transform; };
The f_button_id is a reference to the button which is to be transformed with the specified color matrix.
The DefineButtonSound can be used to emit a sound when an event occur on the specified button. It is likely better to use sprites that you display using actions than to use this tag. You will have access to more events and conditions and also can avoid sound effects on the conditions included in this tag.
enum { DEFINE_BUTTON_SOUND_CONDITION_POINTER_LEAVE = 0, DEFINE_BUTTON_SOUND_CONDITION_POINTER_ENTER = 1, DEFINE_BUTTON_SOUND_CONDITION_POINTER_PUSH = 2, DEFINE_BUTTON_SOUND_CONDITION_POINTER_RELEASE_INSIDE = 3, DEFINE_BUTTON_SOUND_CONDITION_MAX = 4 }; struct swf_definebuttonsound { swf_tag f_tag; /* 17 */ unsigned short f_button_id; swf_soundinfo f_button_sound_condition[DEFINE_BUTTON_SOUND_CONDITION_MAX]; };
The f_button_id is a reference to the button which will get the given sound effects.
There are four f_button_sound_condition. Each have a reference to a sound and some information on how to play it. The four conditions are given in the enumeration preceeding the DefineButtonSound structure.
Additional interactivity has been added in V4.0 of the SWF format. This is given by the use of edit boxes which offer the end users a way to enter new text as if the SWF movie was in fact an interactive form.
The text is defined in a variable (accessible in action scripts). It can be dynamically assigned and retreived. It is legal to have an empty string as the variable name (not dynamically accessible).
struct swf_defineedittext { swf_tag f_tag; /* 37 */ unsigned short f_id; swf_rect f_rect; unsigned f_edit_has_text : 1; unsigned f_edit_word_wrap : 1; unsigned f_edit_multiline : 1; unsigned f_edit_password : 1; unsigned f_edit_readonly : 1; unsigned f_edit_has_color : 1; unsigned f_edit_has_max_length : 1; unsigned f_edit_has_font : 1; if(version >= 6) { unsigned f_edit_reserved : 1; unsigned f_edit_auto_size : 1; } else { unsigned f_edit_reserved : 2; } unsigned f_edit_has_layout : 1; unsigned f_edit_no_select : 1; unsigned f_edit_border : 1; unsigned f_edit_reserved : 1; unsigned f_edit_html : 1; unsigned f_edit_use_outlines : 1; if(f_edit_has_font) { unsigned short f_edit_font_ref; unsigned short f_edit_font_height; } if(f_edit_has_color) { swf_rgba f_edit_color; } if(f_edit_has_max_length) { unsigned short f_edit_max_length; } if(f_edit_has_layout) { unsigned char f_edit_align; unsigned short f_edit_left_margin; unsigned short f_edit_right_margin; signed short f_edit_indent; signed short f_edit_leading; } string f_edit_variable_name; if(f_edit_has_text) { string f_edit_initial_text; } };
The f_edit_word_wrap flag will be set to true (1) in order to have words which go beyond the right side of the box to appear on the next line instead. This only works if you have the f_edit_multiline flag also set to true.
The f_edit_multiline flag can be used to create an edit text field which accepts new lines and can wrap words.
The f_edit_readonly flag ensure that the end user can't modify the text in the edit area.
The f_edit_has_color & f_edit_color will be used to indicate the color of the text. Note that it is possible to ask for a border and a background to be drawn (see the f_edit_border flag below) but these items colors can't be defined.
The f_edit_has_max_length & f_edit_max_length can be used to ensure the user can't type more than a certain number of letters and digits.
The f_edit_password flag is used to visually transform the typed characters to asterisks. The edit text field variable has the proper typed characters.
The f_edit_border will be used to not only draw a border, but also have a white background. Make sure you don't select a white color for your font or you won't see any text in this case. The color of the border is likely to be black. If you want to have better control of these colors you will have to draw your own background and borders.
The f_edit_auto_size flag requests the player to automatically resize the object to the text. Thus, you don't need to know the size of the text at the time you create an edit text, plus different fonts from different platforms will always fit the edit text (but maybe not the screen...).
The f_edit_use_outlines flag will be used to tell whether the specified SWF internal font should be used. When not set, a default font is choosen by the plugin. The font should include a mapping so it can be drawn properly.
The f_edit_align can be set to the following values:
Alignment Value Left 0x00 Right 0x01 Center 0x02 Justify* 0x03 * justification doesn't seem to work yet.
The f_edit_indent is the first line indentation in a multiline box of text. This is added to the left margin. The f_edit_leading is the number of extra pixels to skip to reach the following line. It should be put to zero to have the default font leading value.
The f_edit_left/right_margin indicate how many TWIPS to not use on the sides. If you don't use a border, these are rather useless.
The f_edit_html flag, when set, means the contents of this edit text is a basic HTML text. The following table shows you the tags which the Macromedia plugin understands.
|
For more information about HTML, please, refer to a full HTML documentation. You can find the complete specification at http://www.w3.org/. It was written by the MIT, INRIA and Keio and that's very well written!
WARNING: | There seems to be a problem with the use of a system font when that font doesn't exist on your system. At this time I do not know if it only happens with this object or whether others would also be affected too. Anyway, when it happens you get nothing in the text area. |
It is common to use the DefineFont tag in order to create an array of shapes later re-used to draw strings of text on the screen. Note that the definition of the shape within a font is limited since it can't include any specific fill and/or line style. Also, each shape is assumed to be defined within a 1024x1024 square. This square is called the EM Square. Fig 1. below shows you the EM Square and how it is used. The characters baseline can be placed anywhere within the EM Square (it certainly can be outside too if you wish?!?). The baseline is the position where the Y coordinate of the font is set to 0. The characters have to be drawn over that line to be properly defined. Only letters such as g, j, p and q will have a part drawn below. This means all the main characters will use negative Y coordinates. The Y coordinates increase from top to bottom (opposite the TrueType fonts and possibly others too). The width gives the number of TWIPs between this character and the next to be drawn on the right. The drawing should not go outside the EM Square (what happens in this case is not specified, it is likely that what is drawn outside will be lost but it can have some side effects too).
Though it is possible to define a font which draws from right to left (such as an Arabic or Farsi font), it may cause problems (I didn't try yet...)
With SSWF, you can see the EM Square of a character adding this code in your glyph definition (where <descent> is the descent value as saved in the layout of the font):
glyph "test" { ... move: 0, -<descent>; points { 0, 1024; 1024, 1024; 1024, 0; 0, 0; }; ... };
The font structure defines the font ID (which is common with a corresponding DefineFontInfo) an array of offsets and an array of glyphs. Note that if a DefineFontInfo tag is to be saved, you need to have the glyphs ordered in ascending order ('a' before 'b', etc.) This is important for the definition of the map present in the DefineFontInfo.
You must use a DefineFont2 if a DefineEditText references a font. It will either fail or crash a plugin if you use this font definition instead.
struct swf_definefont { swf_tag f_tag; /* 10 */ unsigned short f_font_id; /* there is always at least one glyph */ f_font_glyphs_count = f_font_offsets[0] / 2; unsigned short f_font_offsets[f_font_glyphs_count]; swf_shape f_font_shapes[f_font_glyphs_count]; };
The f_offsets array is a list of byte offsets given from the beginning of the f_offsets array itself to the beginning of the corresponding shape. (If it were possible to write such structure in C, then ...) In C one would write the following to find the shape in the font tag:
struct swf_definefont *df; df = ... character67 = (struct swf_shape *) ((char *) df->f_offsets + df->f_offsets[67]);
It is common to use the DefineFont2 tag in order to create an array of shapes later re-used to draw strings of text on the screen. This tag must be used whenever a DefineEditText references a font in which case it is suggested you include a full description including layouts.
The array of glyphs must be ordered in ascending order (the smaller glyph number saved first; thus 'a' must be saved before 'b', etc.).
All the characters should be defined in a 1024x1024 square (in pixels) to be drawn with the best possible quality. This square is called the EM square.
struct swf_definefont2 { swf_tag f_tag; /* 48 */ unsigned short f_font2_id; unsigned f_font2_has_layout : 1; if(version >= 6) { unsigned f_font2_reserved : 3; } else { unsigned f_font2_shiftjis : 1; unsigned f_font2_unicode : 1; unsigned f_font2_ansii : 1; } unsigned f_font2_wide_offsets : 1; unsigned f_font2_wide : 1; /* always 1 in v6.x+ */ unsigned f_font2_italic : 1; unsigned f_font2_bold : 1; if(version >= 6) { unsigned char f_font2_language; } else { unsigned char f_font2_reserved; } unsigned char f_font2_name_length; unsigned char f_font2_name[f_font2_name_length]; unsigned short f_font2_glyphs_count; if(f_font2_wide_offsets) { unsigned long f_font2_offsets[f_font2_glyphs_count + 1]; } else { unsigned short f_font2_offsets[f_font2_glyphs_count + 1]; } swf_shape f_font2_shapes[f_font2_glyphs_count]; if(f_font_info_wide) { unsigned short f_font2_map[f_font2_glyphs_count]; } else { unsigned char f_font2_map[f_font2_glyphs_count]; } if(f_font2_has_layout) { signed short f_font2_ascent; signed short f_font2_descent; signed short f_font2_leading_height; signed short f_font2_advance[f_font2_glyphs_count]; swf_rect f_font2_bounds[f_font2_glyphs_count]; signed short f_font2_kerning_count; swf_kerning f_font2_kerning[f_font2_kerning_count]; } };
Since V6.x the f_font2_wide must always be set to 1.
The f_offsets array is a list of byte offsets given from the beginning of the f_offsets array itself to the beginning of the corresponding shape. Note that there is always at least 1 offset. The last offset entry indicates the offset of the data coming after the list of shapes. This is very important if the font is to be used with a DefineEditText. However, some tools won't save these extra two or four bytes. Thus, a reader should be capable of doing the work without them (note however that the Macrodia plugins can't - instead it crashes).
(If it were possible to write such structure in C, then ...) In C one would write the following to find the shape in the font tag:
struct swf_definefont2 *df; df = ... character67 = (struct swf_shape *) ((char *) df->f_offsets + df->f_offsets[67]);
A DefineFontInfo tag will be used to complete the definition of a DefineFont tag. It uses the exact same id (f_font_info_id = f_font_id). You must have the corresponding font definition appearing before the DefineFontInfo since it will use the number of glyphs defined in the DefineFont to know the size of the map definition in the DefineFontInfo tag.
When it looks like it perfectly matches an existing system font, the plugin is likely to use that system font. It is also possible to force the use of the system font by declaring an empty DefineFont tag (i.e. no glyph declaration at all).
The use of system fonts usually ensures a much better quality of smaller prints.
struct swf_definefontinfo { swf_tag f_tag; /* 13 or 62 */ unsigned short f_font_info_id; unsigned char f_font_info_name_length; unsigned char f_font_info_name[f_name_length]; if(version >= 7) { unsigned f_font_info_reserved : 2; unsigned f_font_info_small_text : 1; unsigned f_font_info_reserved : 2; } else if(version >= 6) { unsigned f_font_info_reserved : 5; } else { unsigned f_font_info_reserved : 2; unsigned f_font_info_unicode : 1; unsigned f_font_info_shiftjis : 1; unsigned f_font_info_ansii : 1; } unsigned f_font_info_italic : 1; unsigned f_font_info_bold : 1; unsigned f_font_info_wide : 1; /* always 1 in v6.x+ */ if(version >= 6) { unsigned char f_font_info_language; } if(f_font_info_wide) { unsigned short f_font_info_map[f_font_glyphs_count]; } else { unsigned char f_font_info_map[f_font_glyphs_count]; } };
The f_font_info_wide flag must be set to 1 in v6.x movies.
Note that the flag f_font_info_small_text is the same bit as the flag f_font_info_unicode in SWF version 5 or less.
Since version 6, the font name has to be encoded in UTF-8 instead of whatever encoding you want.
Define some information about the tool which generated this SWF movie file.
struct swf_defineinfo { swf_tag f_tag; /* 31 */ unsigned long f_version; string f_info; };
The information seems to be formatted with names written between periods (.). The two I found are "com" (comment?) and "commands" (used when you edit the movie?).
These are probably the most important tags in this reference. They are used to define a shape using Bezier curves and lines with different styles. The DefineShape of V1.0 is usually enough unless you need a large number of styles or you want to specify colors with an alpha channel (RGBA).
The DefineMorphShape can be used to render an intermediate shape between two defined shapes. All the points and control points of both shapes must match. This is because the rendering of the morphing shapes is just an interpolation between both shapes points and control points positions. The interpolation is a very simple linear function (note however that you still can use a non-linear transformation effect in the end.) Most of the parameters in a shape definition are doubled when this tag is used. It otherwise looks very similar.
struct swf_defineshape { swf_tag f_tag; /* 2, 22 or 32 */ unsigned short f_id; swf_rect f_rect; if(f_tag == DefineMorphShape) { swf_rect f_rect_morph; unsigned long f_offset_morph; swf_morph_shape_with_style f_morph_shape_with_style; } else { swf_shape_with_style f_shape_with_style; } };
The f_offset_morph 32 bits value gives the offset from after that value to the start of the start of the second shape. In other words, this value can be used to skip the styles and the first shape at once.
A DefineSound tag declares a set of samples of a sound effect or a music.
The sound samples can be compressed or not, stereo or not and 8 or 16 bits. The different modes are not all available in V2.0.
struct swf_definesound { swf_tag f_tag; /* 14 */ unsigned short f_sound_id; unsigned f_sound_format : 4; unsigned f_sound_rate : 2; unsigned f_sound_is_16bits : 1; unsigned f_sound_is_stereo : 1; unsigned long f_sound_samples_count; unsigned char f_sound_data[<variable size>]; };
The f_sound_is_16bits is always set to 1 (16bits samples) if the
samples are compressed (neither Raw
nor
Uncompressed
).
The f_sound_rate represents the rate at which the samples are defined. The rate at which it will be played on the target computers may differ. The following equation can be used to determine the rate:
rate = 5512.5 * 2 ** f_sound_rate
It yields the following values (the rate of 5512.5 is rounded down to 5512):
|
The f_sound_samples_count value is the exact number of samples not the size of the data in byte. Thus, in stereo, it represents the number of pairs. To know the byte size, use the total size of the tag minus the header (11 or 13 depending on whether the size of the tag is larger than 64 - it is more than likely that it will be 13).
The f_sound_format can be one of the following values:
|
The f_sound_data depends on the sound format. The following describes the different formats as used in the DefineSound and the SoundStreamBlock tags.
8 bits data is saved in an array of signed char
.
The value 0 represents silence. The samples can otherwise have
values between -128 and +127.
16 bits data is saved in an array of signed short
.
The value 0 represents silence. The samples can otherwise have values
between -32768 and +32767.
By default, the data will be encoded in little endian. However, the
RAW
format doesn't specify the endianess of the data
saved in that case. You should avoid using RAW
16 bits
data. Use Uncompressed
data instead, compress it in
some of the available compression formats (including RAW
8 bits data). A player may wish to avoid playing any sound saved in
RAW
16 bits to avoid any problem.
Mono sound saves only one channel of sound. It will be played back on both output (left and right) channels. This is often enough for most sound effects and voice.
For better quality music and sound effects, you can save the data in stereo. In this case, the samples for each channel (left and right) are interleaved, with the data for the left channel first. Thus, you will have: LRLRLRLRLR... In 8 bits, you get one byte for the left channel, then one byte for the right, one for the left, one for the right, etc. In 16 bits, you get two bytes for the left then two for the right channel, etc.
The RAW
encoding is an uncompressed endian unspecified
encoding. You can use this format to save small 8 bits samples sound
effects.
Audio differential pulse code modulation compression scheme. This is pretty good compression for sound effects.
The ADPCM tables used by the SWF players are as follow:
int swf_adpcm_2bits[ 2] = { -1, 2 }; int swf_adpcm_3bits[ 4] = { -1, -1, 2, 4 }; int swf_adpcm_4bits[ 8] = { -1, -1, -1, -1, 2, 4, 6, 8 }; int swf_adpcm_5bits[16] = { -1, -1, -1, -1, -1, -1, -1, -1, 1, 2, 4, 6, 8, 10, 13, 16 };
The ADPCM data is composed of a 2 bits encoding size (2 to 5 bits) and an array of 4096 left (mono) or left and right (stereo) samples.
struct swf_adpcm_header { unsigned f_encoding : 2; };
The number of bits for the compression is f_encoding + 2
.
struct swf_adpcm_mono { unsigned short f_first_sample; unsigned f_first_index : 6; unsigned f_data[4096] : f_encoding + 2; }; struct swf_adpcm_stereo { unsigned short f_first_sample_left; unsigned f_first_index_left : 6; unsigned short f_first_sample_right; unsigned f_first_index_right : 6; unsigned f_data[8192] : f_encoding + 2; };
IMPORTANT LICENSING NOTES: please, see This document license above for information about the Audio MPEG licensing rights.
The SWF players which support movie v4.x and better will also support MPEG1 audio compression. This is a good quality high compression scheme. The players need to support constant and variable bit rates, and MPEG1 Layer 3, v2 and v2.5. For more information about MPEG you probably want to check out this web site: http://www.mp3-tech.org/.
In SWF movies, you need to save a seeking point (position of the data to play in a given frame) before the MP3 frames themselves. It is also called the initial latency. I will make this clearer once I understand better what it means.
An MP3 frame is described below. This is exactly what you will find in any music file.
struct swf_mp3_header { unsigned f_sync_word : 11; unsigned f_version : 2; unsigned f_layer : 2; unsigned f_no_protection : 1; unsigned f_bit_rate : 4; unsigned f_sample_rate : 2; unsigned f_padding : 1; unsigned f_reserved : 1; unsigned f_channel_mode : 2; unsigned f_mode_extension : 2; unsigned f_copyright : 1; unsigned f_original : 1; unsigned f_emphasis : 2; if(f_no_protection == 0) { unsigned short f_check_sum; } unsigned char f_data[variable size]; };
The f_sync_word are 11 bits set to 1's only. This can be used to synchronize to the next frame without knowing the exact size of the previous frame.
The f_version can be one of the following:
Note: if the MPEG version 2.5 isn't use, then the f_sync_word can be viewed as 12 bits and the f_version as 1 bit.
In SWF movies, the f_layer must be set to III (which is 1). The valid MPEG layers are as follow:
The f_no_protection determines whether a checksum is defined right after the 32 bits header. If there is a checksum, it is a 16 bit value which represents the total of all the words in the frame data.
The f_bit_rate determines the rate at which the following data shall be taken as. The version and layer have also an effect on determining what the rate is from this f_bit_rate value. Since SWF only accepts Layer III data, we can only accepts a few set of rates as follow. MP3 players (and thus SWF players) must support variable bit rates. Thus, each frame may use a different value for the f_bit_rate field.
|
1 free - means any (variable) bit rate
2bad - means you can't properly use this value
The f_sample_rate defines the rate at which the encoded samples will be played at. This rate may vary and be equal or smaller than the rate indicated in the DefineSound header. The rate definition depends on the MPEG version as follow:
|
The f_padding will be set to 1 if the stream includes pads (one extra slot - 8 bits of data). This is used to ensure that the sound is exactly the right size. Useful only if your sound is very long and synchronized with the images.
The f_reserved isn't used and must be set to zero in SWF files.
The f_channel_mode determines the mode used to compress stereophonic audio. Note that the Dual Channel mode is viewed as a stereo stream by SWF. It can be one of the following:
The f_mode_extension determines whether the intensity stereo (L+R -- bit 5) and middle side stereo (L-R -- bit 4) are used (set bit to 1) or not (set bit to 0) in joint stereo. f_mode_extension is usually always set to 3.
The f_copyright field is a boolean value which specify whether the corresponding audio is copyrighted or not. The default is to set it to 1 (copyrighted).
The f_original field is a boolean value which specify whether the corresponding audio is a copy or the actual original sound track. It's usually set to 0 (a copy) in SWF movies.
The f_emphasis field can be one of the following values. It is rarely used. It tells the decoder to re-equalize the sounds.
This is a newly supported scheme to encode speech (and audio) of either better quality or smaller bit rate. Thus you can either put more sound in your files resulting in a similar file size or make the entire file smaller so it downloads faster.
The Nellymoser encoding and decoding scheme have not yet been released in the public domain and it is likely that won't ever be. Thus, you won't find any info here about this compression scheme.
Feel free to check out the http://www.nellymoser.com web site for more info about this compression scheme.
A sprite is a set of SWF tags defining an animated object which can then be used as a simple object. A sprite can't contain another sprite.
The following are the tags which are accepted within the list of tags present in the Sprite:
The data array of tags should always be terminted by an End tag though this can be infered some players may not support a non-terminated list.
struct swf_definesprite { swf_tag f_tag; /* 39 */ unsigned short f_id; unsigned short f_frame_count; ... <data>; swf_tag f_end; };
Define an object of text so the SWF player can draw a string. The only difference between the DefineText and DefineText2 tags is that the latter supports RGBA colors. This can be seen in one of the swf_text_record structures.
struct swf_definetext { swf_tag f_tag; /* 11 or 33 */ unsigned short f_id; swf_rect f_rect; swf_matrix f_matrix; unsigned char f_glyph_bits; unsigned char f_advance_bits; swf_text_record f_text_record; };
This tag defines a video stream. To playback the video stream, one needs to add a list of VideoFrame tags.
struct swf_definevideostream { swf_tag f_tag; /* 60 */ unsigned short f_id; unsigned short f_frame_count; unsigned short f_width; /* WARNING: this is in pixels */ unsigned short f_height; unsigned char f_reserved : 5; unsigned char f_deblocking : 2; unsigned char f_smoothing : 1; unsigned char f_codec; };
The f_width and f_height are defined in pixels. This is rather uncommon in SWF so it is to be noted multiple times.
The f_deblocking parameter can be set to 0 (use the video packet information); 1 (always off) or 2 (always on). All videos are saved in small blocks of about 32x32 pixels (there are different sizes.) Turning this feature on lets the player mix colors between blocks for better output quality.
The f_smoothing flag can be set to 1 to have the player smooth the video before to render it on the output screen.
The f_codec number specifies which codec was used to compress the video. At this time, the following are defined:
Number | Name | Comments | SWF Version |
---|---|---|---|
2 | Sorenson H.263 | 6 | |
3 | Screen Video | 7 |
*the version specified here is the version in which the tags appeared -- however, actions of higher versions can be used with older version tags and thus this version doesn't indicate the version of all the actions used in this tag
The DoAction tag will be used to execute an action in place. Usually, actions are used on buttons to add interactivity to the SWF movies. In V1.0 you had only one dynamic branch (WaitForFrame). In V4.0 you can test many different things such as a position, angle or sound track cursor position. Now, in V5.0 and V6.0 you have a complete set of scripting capabilities with string and arithmetic operations.
The DoInitAction tag is used when some initialization of a sprite is necessary. These actions will be carried on the sprite only once. These are outside of the given sprite and will reference the sprite so all the actions are automatically applied to the sprite without you having to do a SetTarget.
The following describs the data in the DoAction and DoInitAction tags:
struct swf_doaction { swf_tag f_tag; /* 12 or 59 */ if(f_tag == DoInitAction) { unsigned short f_sprite; } swf_action f_action_record[variable]; };The f_sprite is a reference to the sprite which will be initialized.
The f_action_record is an array of actions terminated by an End action.
The following is a list of all the actions available in SWF movies. The Version column will be used to know what version of Flash player you need in order to use the given action (otherwise it is likely to be ignored or worse, make the player crash). Note that Macromedia defines all the actions as being part of V3.0+. Thus, any action mark as being available in earlier versions (V1.0 or V2.0) may in fact not be (though the DoAction and DefineButton tags were part of V1.0!!!)
The Length (Stacked) column specifies the length of the data following the property (only with the property ID is 0x80 to 0xFF) and what will be pushed onto the stack. All the expressions work as in polish notation. The actions which don't push anything on the stack have nothing written between parenthesis.
The Data & Operation column specifies what data follows the action and what the operation is. If there is no data and no operation, then n.a. is used. The data will be described as a list of fields as in the other structures described in this document. The operations will be written as closely as possible to a C like operation (though strings are managed in a much different way than C!) Anything which is poped from the stack will be given a letter and a digit. The digit represents the count and the letter the type of the data (a count of 1 represents the first pop, a count of 2 represents the second pop, etc.) The following column (Comments) will explain how the operation uses the data when appropriate.
The data types used are as follow:
|
1when I don't know whether an integer or a float
should be specified I will use 'n' as well. This should
be correct most of the time anyway.
2an object reference can be obtained by evaluating
the name of that object; thus GetVariable("carrot") will return a
reference to the carrot object.
The following lists all the actions by type. Thoses which have the comment (typed) operates taking the type of its arguments in account as defined in ECMA-262 Section 11.6.1 (arithmetic), 11.8.5 (comparison), 11.9.3 (equality) which you can certainly find somewhere on the Internet. Version 3 is available here: ECMA-262 V3.0. The functions which aren't typed will behave by (1) trying to transform parameters in values, then perform the operation with numbers only or (2) when strings can't be transformed in values, perform a string operation.
Actions by TypeAction Script ControlBranch Always [0x99]
Branch If True [0x9D]
Call Function [0x3D]
Call Method [0x52]
Declare Function [0x9B]
Declare Function (V7) [0x8E]
End [0x00]
Return [0x3E]
Throw [0x2A]
Try [0x8F]Stack ControlDuplicate [0x4C]
Push Data [0x96]
Swap [0x4D]Action Script ContextSet Target [0x8B]
Set Target (dynamic) [0x20]
With [0x94]Movie ControlCall Frame [0x9E]
Get URL [0x83]
Get URL2 [0x9A]
Goto Expression [0x9F]
Goto Frame [0x81]
Goto Label [0x8C]
Next Frame [0x04]
Play [0x06]
Previous Frame [0x05]
Stop [0x07]
Toggle Quality [0x08]
Wait For Frame [0x8A]
Wait For Frame (dynamic) [0x8D]SoundStop Sound [0x09]ArithmeticAdd [0x0A]
Add (typed) [0x47]
Decrement [0x51]
Divide [0x0D]
Increment [0x50]
Integral Part [0x18]
Modulo [0x3F]
Multiply [0x0C]
Number [0x4A]
Subtract [0x0B]ComparisonsEqual [0x0E]
Equal (typed) [0x49]
Strict Equal [0x66]
Greater Than (typed) [0x67]
Less Than [0x0F]
Less Than (typed) [0x48]
String Equal [0x13]
String Greater Than [0x68]
String Less Than [0x29]Logical and Bit WiseAnd [0x60]
Logical And [0x10]
Logical Not [0x12]
Logical Or [0x11]
Or [0x61]
Shift Left [0x63]
Shift Right [0x64]
Shift Right Unsigned [0x65]
XOr [0x62]Strings & Characters (See the String Object also)Chr [0x33]
Chr (multi-bytes) [0x37]
Concatenate Strings [0x21]
Ord [0x32]
Ord (multi-bytes) [0x36]
String [0x4B]
String Length [0x14]
String Length (multi-bytes) [0x31]
SubString [0x15]
SubString (multi-bytes) [0x35]PropertiesGet Property [0x22]
Set Property [0x23]ObjectsCast Object [0x2B]
Declare Array [0x42]
Declare Dictionary [0x88]
Declare Object [0x43]
Delete [0x3A]
Delete All [0x3B]
Duplicate Sprite [0x24]
Enumerate [0x46]
Enumerate Object [0x55]
Extends [0x69]
Get Member [0x4E]
Get Target [0x45]
Implements [0x2C]
Instance Of [0x54]
New [0x40]
New Method [0x53]
Remove Sprite [0x25]
Set Member [0x4F]
Type Of [0x44]VariablesDeclare Local Variable [0x41]
Get Variable [0x1C]
Set Local Variable [0x3C]
Set Variable [0x1D]MiscellaneousGet Timer [0x34]
Random [0x30]
Start Drag [0x27]
Stop Drag [0x28]
Store Register [0x87]
Trace [0x26]Internal Functions & ConstantsColor Object
Math Object
String Object
WARNING: the order of unstacking values in regard to some operators is to be confirmed
|
The following is the table of types accepted in the Push Data action.
|
The following is the list of currently accepted properties or fields for the Get Property and the Set Property. Note that the properties can be specified with either an integer (type 7, requires V5.0+) or a single precision floating point (type 1, V4.0 compatible). And since strings are automatically transformed in a value when required, one can use a string to represent the property number (type 0). It works with a double value, I even tested a boolean and null and it works. Obviously it isn't a good idea to use these. The default should be a single precision float. Please, see the Push Data action for more information about the types.
|
Since Flash V5.x, you can use internal functions which are available at all times. These are called Methods. These methods are called using the Call Function action with the name of the object and function separated by a period. A few of these internal functions are duplicates of some direct action script instructions.
Similarly, you can acces internal constants which are also available at all times. These are called Properties. These properties are read using the Get Variable action with the name of the object and constnat separated by a period. This can increase the precision of some values such a PI and E and give you information about the system on which the plugin is running. These properties are usually read-only.
The name of the objects and their functions are case insensitive.
Thus "Math.SIN"
is equivalent to "math.sin"
.
The internal functions were defined in different objects. The following is a list of these objects followed by a table for each of the function of each object. Note that a complete declaration is given below. Only the function or constant name can be used. The rest of the prototype is only here to help you know how to call the function or what type is the constant.
Sample to compute the sine of an angle given in degree:
// expect the angle on the top of the stack here Push Data (string) "math.pi" Get Variable Multiply Push Data (float) 180.0 Swap Divide Push Data (integer) 1 (string) "math.sin" Call Function // the result is on the top of the stack // i.e.: math.sin(<input> * math.pi / 180.0);
|
|
NOTE: you can use a String object like any other string; for instance, to put the contents of the String object in a DefineTextField variable, you simply do:
push "myTextFieldVariable", "myStringObject"; get variable; set variable;
(assuming you saved your string in a variable named
"myStringObject"
and the name of the variable
in the text field is "myTextFieldVariable"
).
Most of the time, positions can be given out of bounds and the functions will still perform as expected as if that position was 0 or string.length.
|
The Export tag works in conjunction with the Import tag. The Export gives a list of definitions made visible to the external world. Thus these definitions are in effect available to be imported by another movie.
The Export tag is a list of identifiers which are the identifiers of the objects defined within this movie and gives a name to that object. The name is the external reference and that's what the Player will use to know how to retrieve the data from another movie. Each name need to be different, however there is no other restriction.
There should be only one Export. It is not clearly defined anywhere, but it is likely that the players will stop at the first export they find and not try to see if other Exports are defined in your SWF movies. It is not clear whether a movie using Export can itself Import other movies.
struct swf_export { swf_tag f_tag; /* 56 */ unsigned short f_count; swf_external f_symbol[<count>]; };
You should at least have one external reference.
The identifiers defined in this list must match an object in the movie which includes this tag.
The FrameLabel tag gives a textual name to a frame. This name can also be used as an anchor in V6.x+ and whenever specified in this way.
struct swf_framelabel { swf_tag f_tag; /* 43 */ string f_label; if(version >= 6) { /* optional field */ unsigned short f_flags; } };
At this time, the optional field f_flags must be set to 1 if present. This
means it has to be used as an anchor when the URL to the SWF movie includes a
#>frame label<
at the end.
The f_label is a null terminated string.
The Import tag works in conjunction with the Export tag. The Import tag gives the name of another movie and a list of external names as defined for export in that other movie. There is also a list of identifiers which represent the identifier the object(s) will have in this movie (the movie with the Import tag) and they don't need to match the source movie identifiers.
The list of identifiers given in the list of the Import tag must be unique within the entire movie. The names are only used to match the names present in the Export tag of the other movie. Thus, these can be duplicates of named sprite in this movie.
There should be only one Import per referenced movie (it would be a waste to have more). It is not clear whether a movie can Export definitions when it itself Import definitions. Also, it isn't clear what happens if such an external reference fails (I assume the corresponding objects are defined as being empty shapes).
struct swf_import { swf_tag f_tag; /* 57 */ string f_url; unsigned short f_count; swf_external f_symbol[<count>]; };
You should at least have one external reference.
The f_url parameter is a standard URL which names the object to be loaded and searched for an Export tag.
The identifiers defined in must be unique with the entire movie.
The protection tag is totally useless. The SWF format is an open format, otherwise how would you have so many players and tools to work with SWF movies? Thus, you can pretend to protect your movies, but anyone with a simple binary editor can transform the tag and make it another which has no such effect. Also, swf_dump and some other tools (such as flasm) can read your movie anyway.
For the sake of defining what you have in each tag, there are the protection tags fully described.
struct swf_protect { swf_tag f_tag; /* 24, 58 or 64 */ if(version >= 5) { if(tag == ProtectDebug2) { unsigned short f_reserved;1 } /* the password is optional when tag == Protect */ string f_md5_password; } };
1 | The f_reserved must be set to zero. |
According to Macromedia, you can find some free implementation of the MD5
algorithm by Poul-Henning Kamp in FreeBSD in the file
src/lib/libcrypt/crypt-md5.c
.
This tag will be used to specify where and how to place an object in the next frame. The PlaceObject2 is much different and is presented below.
The f_depth field is used to indicate at which depth the character is inserted in the current frame. There can be any number of object per depth value, however, the players may or may not properly redraw them. Usually the last added item in a given depth is drawn behind the previously added objects. Placing one object per depth is safer so you be sure of the drawing order.
The f_id is a reference to an object previously defined with one of the Define... tags.
struct swf_placeobject { swf_tag f_tag; /* 4 */ unsigned short f_id; unsigned short f_depth; swf_matrix f_matrix; if(f_tag_data_real_size is large enough) {1 swf_color_transform f_color_transform; } };
1 | The f_color_transform is an optional field. The size of the tag data determines whether it was saved or not. |
This tag will be used to specify where and how to place an object in the next frame. The PlaceObject is much different and is presented just before.
The f_depth field is used to indicate at which depth the character is inserted in the current frame. There can be only one object per depth value (thus a maximum of 65536 objects can appear on a single frame).
If both f_place_has_move and f_place_has_id are zero, the character is removed from the screen (same as RemoveObject2).
The f_clipping_depth parameter is used to tell the player to use the linked shape (DefineShape) or text (DefineText) as a mask for all the objects inserted in the display list from f_depth to f_clipping_depth inclusive. The mask itself isn't draw in the screen. For instance, you could create a sprite which draws a burning fire. To place this fire in a text, insert the text with this clipping feature with a depth, say, of 7 and clipping depth of 8 and place the fire at a depth of 8 (note that to have an animation, the fire will certainly be a sprite). The fire will only appear in the text letters. Obviously this is somewhat limited since the f_clipping_depth is hard coded and not a range (Macromedia should have used depth + clip like in SSWF instead). Note that it doesn't seem to work with duplicated sprites even if these are placed at the right depth.
NOTE: |
At this time I checked and I can tell that the following objects
will work for clipping purposes:
and the following won't work:
The following need to be checked, I also added a comment telling whether I think it has a chance to work:
|
The morph position will only be used when the place object references a DefineMorphShape object. It defines a linear position between the first and second shapes (0 - draws shape 1 and 65535 - draws shape 2).
struct swf_placeobject2 { swf_tag f_tag; /* 26 */ /* NOTE: the following flags can be read as one byte also */ if(version >= 5) { unsigned f_place_has_actions : 1; } else { unsigned f_place_reserved : 1; } unsigned f_place_has_clipping_depth : 1; unsigned f_place_has_name : 1; unsigned f_place_has_morph_position : 1; unsigned f_place_has_color_transform : 1; unsigned f_place_has_matrix : 1; unsigned f_place_has_id : 1; unsigned f_place_has_move : 1; unsigned short f_depth; if(f_place_has_id) { unsigned short f_id; } if(f_place_has_matrix) { swf_matrix f_matrix; } if(f_place_has_color_transform) { swf_color_transform f_color_transform; } if(f_place_has_morph_position) { unsigned short f_morph_position; } if(f_place_has_name) { string f_name; } if(f_place_has_clipping_depth) { unsigned short f_clipping_depth; } if(f_place_has_actions) { /* only in V5.0 */ unsigned short f_reserved; if(version >= 6) { unsigned long f_all_flags; } else { unsigned short f_all_flags; } swf_event f_event[<variable>]; if(version >= 6) { unsigned long f_end; /* always zero */ } else { unsigned short f_end; /* always zero */ } } };
Remove the specified object from the display list. If the same object was placed multiple times at the specified depth, only the last copy is removed. When only a depth is specified, the last object placed at that depth is removed from the list. Note that in V3.0 it is possible to use the PlaceObject2 in order to replace an object at a given depth without having to remove it first.
struct swf_removeobject { swf_tag f_tag; /* 5 or 28 */ if(f_tag == RemoveObject) { short f_id; } short f_depth; };
This tag is used to change the default limits for script execution.
struct swf_scriptlimits { swf_tag f_tag; /* 65 */ unsigned short f_max_recursion_depth; unsigned short f_timeout_seconds; };
The maximum recursion depth is 256 by default. Any value, except zero (0) is valid.
The f_timeout_seconds parameter specifies the number of seconds before the players opens a dialog box saying that the SWF animation is stuck.
This can be very useful if you have some heavy initialization which takes more resources than a few seconds (~15 seconds by default), and/or has a lot of recusivity (or just calls? to be tested...). You can then set large limits for the initialization to run fine, and then put some much lower limits afterward so as to ensure that the other scripts don't use too much resources.
This tag is used to specify the background color. It should always be included at the start of every .swf file. Only an RGB color can be used (i.e. there is no way to have a transparent SWF animation with Flash at this time - it seems)
struct swf_setbackgroundcolor { swf_tag f_tag; /* 9 */ swf_rgb f_rgb; };
This tag defines the tab index of any text object (static and dynamic text objects.)
struct swf_settabindex { swf_tag f_tag; /* 66 */ unsigned short f_depth; unsigned short f_tab_index; };
The depth references the object which is assigned the tab index. The tab index defines the order in which objects are sorted to know where to go next when the tab key is pressed.
This empty tag signals to the player to display the current frame. The player will then fall asleep until it is time to draw the next frame (well... actually, it should prepare the next frame and then sleep if necessary).
struct swf_showframe { swf_tag f_tag; /* 2 */ };
The SoundStreamBlock tag defines the data of a sound effect previously defined with a SoundStreamHead or a SoundStreamHead2 tag.
struct swf_soundstreamblock { swf_tag f_tag; /* 19 */ unsigned char f_sound_data[variable size]; };
The data depends on the SoundStreamHead[2] definition and is variable in size. Please, see the DefineSound tag for more information about sound data.
The SoundStreamHead[2] tag defines a sound effect which is to be loaded with a set of SoundStreamBlock tags. It defines the sound once and for all.
Streaming sound has a strong side effect when playing a movie: it will force a synchronization between the images and the audio. Thus some images may be dropped if the drawing isn't fast enough.
Streaming sound effect should be either used in the main movie or in a sprite which needs a sound track properly synchronized to the sprite animation. Otherwise, it is much better to use a DefineSound and StartSound pair of tags instead.
It seems (though it isn't documented) that using a SoundStreamHead tag by itself (without any blocks) will be taken as a hint of how to play all the other sounds (see the f_playback_rate)
Important note: there can be only one streaming sound per movie clip including the main movie. If you need multiple sound effects or music to be played back, these will have to be merged at the time you create the movie in a single sound which will then be played back as a simple sound effect.
struct swf_soundstreamhead { swf_tag f_tag; /* 18 or 45 */ unsigned f_reserved : 4; unsigned f_playback_rate : 2; unsigned f_playback_size : 1; unsigned f_playback_stereo : 1; unsigned f_compression : 4; unsigned f_sound_rate : 2; unsigned f_sound_size : 1; unsigned f_sound_stereo : 1; unsigned short f_sample_size; if(f_compression == 2) { signed short f_latency_seek; } };
Some of the fields in the SoundStreamHead tag can't have all the possible values when defined in an older version of SWF. What follows describs each field in more details.
The f_playback_rate and f_sound_rate define the rate at which the data should be played and the exact rate of the data found in the SWF file.
rate = 5512.5 * 2 ** f_playback_rate rate = 5512.5 * 2 ** f_sound_rate
The f_playback_size and f_sound_size define the number of bits found in the data (0 for 8 bits and 1 for 16 bits.) Note that with a SoundStreamHead tag (18) only 16 bits data are allowed (both are always set to 1). If the compression mode isn't Raw or Uncompressed then the f_sound_size must be set to 1.
The f_playback_stereo and f_sound_stereo define whether the sound should be played on both speakers and whether the data includes both channels.
The f_compression entry defines the compression mode. The list of sound compression modes can be found with the DefineSound tag. Note that with a SoundStreamHead tag (18) only the ADPCM compression is accepted. All the compression modes are accepted in a SoundStreamHead2 tag (45).
The f_sample_size represents the average number of samples per frame. It is used to ensure a proper synchronization. Note that there can be more (and even less in MP3) samples within a given frame.
The f_latency_seek is usually zero. It exists only when the MP3 compression mode is used. It defines the number of samples to skip at the beginning of the very first frame (I'm not totally sure what this is to tell you the truth... I'll tell you more once I know more).
The StartSound tag is used to playback a sound as defined with the DefineSound.
struct swf_startsound { swf_tag f_tag; /* 15 */ swf_soundinfo f_sound_info; };
The VideoFrame tag is used to render one frame. It includes the data of the video to be drawn.
struct swf_startsound { swf_tag f_tag; /* 61 */ unsigned short f_id; unsigned short f_frame; unsigned char f_video_data[variable size]; };
The f_id parameter is a reference to a DefineVideoStream defined before this tag.
The f_frame parameter defines which frame needs to be rendered.
The f_video_data content depends on the codec defined in the DefineVideoStream tag. Once I really know what that data is, I will update this documentation. Note that the Sorenson H.263 encoding is actually a subset of MPEG-2.
struct swf_tag { unsigned short f_tag_and_size; f_tag = f_tag_and_size >> 6; f_tag_data_size = f_tag_and_size & 0x3F; if(f_tag_data_size == 63) { unsigned long f_tag_data_real_size; } else { f_tag_data_real_size = f_tag_data_size; } };
struct swf_rgb { unsigned char f_red; unsigned char f_green; unsigned char f_blue; };
struct swf_xrgb { unsigned char f_pad; unsigned char f_red; unsigned char f_green; unsigned char f_blue; };Images without an alpha channel which are saved using 32 bits (format 5) use XRGB colors.
struct swf_rgba { unsigned char f_red; unsigned char f_green; unsigned char f_blue; unsigned char f_alpha; };
struct swf_argb { unsigned char f_alpha; unsigned char f_red; unsigned char f_green; unsigned char f_blue; };Images using an alpha channel and 32 bits (format 5) will use ARGB instead of RGBA.
The rectangles are very well compressed in an SWF file. These make use of a 5 bits size which specifies how many bits are present in the following four fields. Don't forget that the bits are read from the MSB to the LSB and in big endian like when multiple bytes are necessary.
struct swf_rect { /* char align; - the doc says it's not aligned, the code says otherwise so far... let's wait and see */ unsigned f_size : 5; signed f_x_min : f_size; signed f_x_max : f_size; signed f_y_min : f_size; signed f_y_max : f_size; };
The following table defines the number of TWIPs to move left or right before to draw the 2nd character when the 1st one was drawn right before it. For instance, the letters AV may be drawn really close so the V is written over the A. To the contrary, WI may be seperated some more so the I doesn't get merged to the top of the W.
The computation to move the drawing pen is done as follow:
/* writing 'AV' */ x += f_font2_advance['A'] + f_kerning['AV'].f_kerning_adjustment;where 'x' is the position at which f_kerning_code1 was draw.
struct swf_kerning { if(f_font2_wide) { unsigned short f_kerning_code1; unsigned short f_kerning_code2; } else { unsigned char f_kerning_code1; unsigned char f_kerning_code2; } signed short f_kerning_adjustment; };
Fonts uses this declaration which doesn't include any style (fill or line) definitions. The drawing will use fill 0 when the inside of the shape shouldn't be drawn and 1 when it is to be filled. The line style shouldn't be used.
struct swf_shape { swf_styles_count f_styles_count; swf_shape_record f_shape_records[variable]; };
The array of shape records starts with a set of styles definition and then is followed by shape records which is null terminated.
struct swf_morph_shape_with_style { swf_styles f_styles; swf_shape_record f_shape_records[variable]; char align; swf_styles_count f_styles_count; swf_shape_record f_shape_records_morph[variable]; };
Note that f_shape_records_morph can't include any reference to styles and lines, nor include new styles. It is likely that the f_styles_count will always be 0x11. Also, it is always byte aligned.
The array of shape records starts with a set of styles definition and then is followed by shape records which is null terminated.
struct swf_shape_with_style { swf_styles f_styles; swf_shape_record f_shape_records[variable]; };
The shape records are typed. Depending on that type, the contents vary. The following defines one structure for each type. The shape record is a union of these structures.
It is important to note that the f_shape_move_x and f_shape_move_y are not deltas from the current point, but a position from the current shape origin. All the other positions are defined as deltas from the previous position, including the anchors which are deltas from the control point position!
The control point defines how much the curve is curved. Please, see The geometry in SWF for more information.
/* if f_shape_record_type = 0 & f_end_of_shape = 0 then that's the end of the list of shape records */ struct swf_shape_record_end { unsigned f_shape_record_type : 1; unsigned f_end_of_shape : 5; }; /* f_shape_record_type = 0 & at least one of the following five bits is not zero then change style, fill and position setup */ struct swf_shape_record_setup { unsigned f_shape_record_type : 1; if(f_tag == DefineShape) {1 unsigned f_shape_reserved : 1; /* always zero */ } else { unsigned f_shape_has_new_styles : 1; } unsigned f_shape_has_line_style : 1; unsigned f_shape_has_fill_style1 : 1; unsigned f_shape_has_fill_style0 : 1; unsigned f_shape_has_move_to : 1; if(f_shape_has_move_to) { unsigned f_shape_move_size : 5; signed f_shape_move_x : f_shape_move_size; signed f_shape_move_y : f_shape_move_size; } if(f_shape_has_fill_style0) { unsigned f_shape_fill_style0 : f_fill_bits_count; } if(f_shape_has_fill_style1) { unsigned f_shape_fill_style1 : f_fill_bits_count; } if(f_shape_has_line_style) { unsigned f_shape_line_style : f_line_bits_count; } if(f_shape_has_new_styles) { swf_styles f_styles; } }; /* f_shape_record_type = 1 -- edge record */ struct swf_shape_record_edge { unsigned f_shape_record_type : 1; unsigned f_shape_edge_type : 1; unsigned f_shape_coord_size : 4; f_shape_coord_real_size = f_shape_coord_size + 2; if(f_shape_edge_type == 0) { signed f_shape_control_delta_x : f_shape_coord_real_size; signed f_shape_control_delta_y : f_shape_coord_real_size; signed f_shape_anchor_delta_x : f_shape_coord_real_size; signed f_shape_anchor_delta_y : f_shape_coord_real_size; } else { unsigned f_shape_line_has_x_and_y : 1; if(f_shape_line_has_x_and_y == 1) { signed f_shape_delta_x : f_shape_coord_real_size; signed f_shape_delta_y : f_shape_coord_real_size; } else { unsigned f_shape_line_has_x_or_y : 1; if(f_shape_line_has_x_or_y == 0) { signed f_shape_delta_x : f_shape_coord_real_size; } else { signed f_shape_delta_y : f_shape_coord_real_size; } } } }; union swf_shape_record { swf_shape_record_end f_shape_end; swf_shape_record_setup f_shape_setup; swf_shape_record_edge f_shape_edge; };
1 |
It looks (from my tests with the official Macromedia Flash plugin) there is always a bit at this position. It seems however that it can't be set to 1 in V1.0 (i.e. when the DefineShape tag is used). |
This structure is found in the shape with style and change style structures.
struct swf_styles { swf_fill_style_array f_fill_styles; swf_line_style_array f_line_styles; swf_styles_count f_styles_count; };
Note that the line & fill bits are declared as "unsigned char" because they will always be aligned. The proper definition would probably be a bit field though.
struct swf_styles { unsigned char f_fill_bits_count : 4; unsigned char f_line_bits_count : 4; };
The array of fill styles starts with a counter. When DefineShape is used, the counter can be any value from 0 (no style) to 255. When DefineShape2 or DefineShape3 are used, the value 255 is reserved so you can declare more than 255 styles.
struct swf_fill_style_array { unsigned char f_count; if(f_tag != DefineShape && f_count == 255) { unsigned short f_real_count; } else { f_real_count = f_count; } swf_fill_style f_fill_style[f_real_count]; };
The fill style is defined in the first byte. The values are defined below. Depending on that value, the fill style structure changes as shown below. The swf_fill_style is a union of all these other structures.
/* f_type = 0x00 - solid fill */ struct swf_fill_style_solid { unsigned char f_type; if(f_tag == DefineMorphShape) { swf_rgba f_rgba; swf_rgba f_rgba_morph; } else if(f_tag == DefineShape3) { swf_rgba f_rgba; } else { swf_rgb f_rgb; } }; /* f_type = 0x10 - linear gradient fill, 0x12 - radial gradient fill */ struct swf_fill_style_gradient { unsigned char f_type; swf_matrix f_gradient_matrix; if(f_tag == DefineMorphShape) { swf_matrix f_gradient_matrix_morph; } swf_gradient f_gradient; }; /* f_type = 0x40 - tilled bitmap fill with smoothed edges, 0x41 - clipped bitmap fill with smoothed edges, 0x42 - tilled bitmap fill with hard edges (V7.0), 0x43 - clipped bitmap fill with hard edges (V7.0) */ struct swf_fill_style_bitmap { unsigned char f_type; unsigned short f_bitmap_ref; swf_matrix f_bitmap_matrix; if(f_tag == DefineMorphShape) { swf_matrix f_bitmap_matrix_morph; } }; union swf_fill_style { unsigned char f_type; swf_fill_style_solid f_solid; swf_fill_style_gradient f_gradient; swf_fill_style_bitmap f_bitmap; };
The array of line styles starts with a counter. When DefineShape is used, the counter can be any value from 0 (no style) to 255. When DefineShape2 or DefineShape3 are used, the value 255 is reserved so you can declare more than 255 styles.
struct swf_line_style_array { unsigned char f_count; if(f_tag != DefineShape && f_count == 255) { unsigned short f_real_count; } else { f_real_count = f_count; } swf_line_style f_line_style[f_real_count]; };
The width of the line is in TWIPS (1/20th of a pixel).
struct swf_line_style { if(f_tag == DefineMorphShape) { unsigned short f_width; unsigned short f_width_morph; swf_rgba f_rgba; swf_rgba f_rgba_morph; } else if(f_tag == DefineShape3) { unsigned short f_width; swf_rgba f_rgba; } else { unsigned short f_width; swf_rgb f_rgb; } };
The default scale is 1.0. The default rotate is 0.0. The default translate is (0, 0).
The scale is a ratio. The rotate is an angle in radian. The translate is in TWIPs.
struct swf_matrix { char align; unsigned f_has_scale : 1; if(f_has_scale) { unsigned f_scale_bits : 5; signed fixed f_scale_x : f_scale_bits; signed fixed f_scale_y : f_scale_bits; } unsigned f_has_rotate : 1; if(f_has_rotate) { unsigned f_rotate_bits : 5; signed fixed f_rotate_skew0 : f_rotate_bits; signed fixed f_rotate_skew1 : f_rotate_bits; } unsigned f_translate_bits : 5; signed f_translate_x : f_rotate_bits; signed f_translate_y : f_rotate_bits; };
The swf_text_record structure is a structure composed of a text style definition followed by characters. Multiple records can follow each others. The list is ended with one byte set to 0.
WARNING: it seems that Macromedia didn't
think about a file having two records of type glyph one after
another (it makes their plugins crash); you will have to insert a
setup record between each glyph record (the setup can be empty: i.e.
add one byte equal to 0x80
). The very first setup has to
at least define the font.
NOTE: this has been corrected by Macromedia it now shows as one structure including the style and an array of glyphs. This fixes the problem at once. It however makes the structure look a bit more complicated.
struct swf_text_record_end { unsigned f_end : 8; /* all zeroes */ }; struct swf_text_record_setup { unsigned f_type_setup : 1; /* always one */ unsigned f_reserved : 3; unsigned f_has_font : 1; unsigned f_has_color : 1; unsigned f_has_move_y : 1; unsigned f_has_move_x : 1; if(f_has_font) { unsigned short f_font_ref; } if(f_has_color) { if(tag == DefineText) { swf_rgb f_color; } else { /* if tag is DefineText2 */ swf_rgba f_color; } } if(f_has_move_x) { signed short f_move_x; } if(f_has_move_y) { signed short f_move_y; } if(f_has_font) { unsigned short f_font_height; } }; struct swf_text_record_glyphs { unsigned f_type_glyph : 1; /* always zero */ unsigned f_glyph_count : 7; /* at least one */ swf_text_entry f_entry[f_glyph_count]; }; struct swf_text_record_string { unsigned f_type_setup : 1; /* always one */ unsigned f_reserved : 3; unsigned f_has_font : 1; unsigned f_has_color : 1; unsigned f_has_move_y : 1; unsigned f_has_move_x : 1; if(f_has_font) { unsigned short f_font_ref; } if(f_has_color) { if(tag == DefineText) { swf_rgb f_color; } else { /* if tag is DefineText2 */ swf_rgba f_color; } } if(f_has_move_x) { signed short f_move_x; } if(f_has_move_y) { signed short f_move_y; } if(f_has_font) { unsigned short f_font_height; } unsigned char f_glyph_count; /* at least one */ swf_text_entry f_entry[f_glyph_count]; }; union swf_text_record { unsigned f_flags : 8; swf_text_record_end f_end; if(version >= 7) { swf_text_record_string f_string; } else { swf_text_record_setup f_setup; swf_text_record_glyphs f_glyphs; } };
The very first byte of a record determines its type. When it is set to zero, it is the end of text records. In all versions (though it was not defined that way before), you need to alternate the setup and glyph records. It seems that even older versions would support more than 127 characters, however, if you plan to use 128 to 255 characters in a text records, I recommand you create a version 7 movie. So, in other words, go ahead and use the swf_text_record_string with f_glyph_count set to a value from 1 to 127 in a version 1 to 6 movie.
To make sure that none of the setup records are recognized as the end record, you should always set the bit 7 to 1 (f_type_setup). You don't otherwise have to have any font, color or displacement definition in setups (except the very first which needs to specify a font).
The f_glyph_count must be at least 1. If you don't have any characters, just don't create a text entry.
The f_move_x and f_move_y always specify a position from the origin where the text object is placed like in a shape.
The swf_text_entry structure defines a list of characters and the number of TWIPs to skip to go to the next character. Note that the advance is a signed value. Thus you can write characters from right to left languages such as Arabic in a native way. The number of bits used to define each field of this structure is defined in the DefineText or DefineText2 tags.
struct swf_text_entry { unsigned f_glyph_index : f_glyph_bits; signed f_advance : f_advance_bits; };
The f_count
field is limited to a value of 1 to 8 only.
struct swf_gradient { unsigned char f_count; swf_gradient_record f_gradient_record[f_count]; };
The first record position should be 0 and the last 255. The intermediate should use the corresponding value depending on their position in the gradient effect. The position field is called ratio in the OpenSWF.org document which doesn't seem to be a correct designation.
struct swf_gradient_record { if(f_tag == DefineMorphShape) { unsigned char f_position; swf_rgba f_rgba; unsigned char f_position_morph; swf_rgba f_rgba_morph; } else if(f_tag == DefineShape3) { unsigned char f_position; swf_rgba f_rgba; } else { unsigned char f_position; swf_rgb f_rgb; } };
A linear gradient is defined from left to right. A radial from inside to outside. In order to see the full effect of the gradient, one needs to define its matrix properly. The gradients are always drawn in a square with coordinates -819.2, -819.2 to +819.2, +819.2 (in pixels, that's 16384 in TWIPs). The usual is to scale the gradient square down, translate to the proper position and rotate as necessary. There is no point in rotating a radial gradient.
IMPORTANT NOTE: If you use positions (see f_position) which are too close to each others, you are likely to see a reverse effect of what you would expect (Well... at least in the Macromedia plugin V5.0 — the gradient goes the wrong way between each color change!!!).
The image in Fig 1. shows you a radial fill using pure red as the color at position 0 and pure green at position 255. It is often used to draw a round corner of an object such as a button.
The image in Fig 2. shows you a linear fill using pure red as the color at position 0 and pure green at position 255. It goes from left to right when no rotation is applied. Using a rotation provides means to have the colors going top to bottom or in diagonals.
When the f_color_<component>_mult are not defined in the input file, use 1.0 by default. When the f_color_<component>_add are not defined in the input file, use 0.0 by default.
The factors are saved as 8.8 fixed values (divide by 256 to obtain a proper floating point value). Note that the values are limited to a signed 16 bits value. This allows for any value between -128.0 and +127.98828.
When the resulting color is defined, the multiplication is applied first as in:
result-component = source-component * component-mult + component-add;The result is then clamped between 0.0 and 1.0.
struct swf_color_transform { char align; unsigned f_color_has_add : 1; unsigned f_color_has_mult : 1; unsigned f_color_bits : 4; if(f_color_has_mult) { signed f_color_red_mult : f_color_bits; signed f_color_green_mult : f_color_bits; signed f_color_blue_mult : f_color_bits; if(f_tag == PlaceObject2) { signed f_color_alpha_mult : f_color_bits; } } if(f_color_has_add) { signed f_color_red_add : f_color_bits; signed f_color_green_add : f_color_bits; signed f_color_blue_add : f_color_bits; if(f_tag == PlaceObject2) { signed f_color_alpha_add : f_color_bits; } } };
An action is defined with an identifier, a size and some data (pretty much like a tag). Because the number of actions is much more limited than tags, the ID is only 7 bits. There is one bit used to know whether there is a size parameter. If not, then there is no data for that action. If there is a size, then a 2 byte value is used to save the information about that action.
Please, see the DoAction tag for a list of all the supported actions.
/* basic definition of an action entry */ struct swf_action { char align; unsigned f_action_has_length : 1; unsigned f_action_id : 7; if(f_action_has_length) { unsigned short f_action_length; unsigned char f_action_data[f_action_length]; } };
WARNING: | the f_action_has_length is actually taken in account in the determination of the f_action_id; thus if the first byte is 0x0A, it is not the same as 0x8A. |
A button structure defines a state and a corresponding shape reference. The shape will be affected by the specified matrix whenever used.
There are many acceptable combinaisons. The object which is referenced is drawn when its state matches the current state of the button. If only the f_button_state_hit_test is set, then the shape is always displayed.
In order to define the area where the button can be clicked, it is necessary to set the f_button_state_hit_test flag to 1. Also, when this flag is set, only a shape can be referenced (no edit text, sprite or text object will work in this case).
When the f_button_state_hit_test is set, the square used to delimit the referenced shape will be used to determine whether the mouse is over the button or not.
Shapes referenced with the f_button_state_down flag set are drawn when a mouse button is being pushed over this button.
Shapes referenced with the f_button_state_up flag set are drawn when no mouse button is being pushed over this button. When neither up or down is specified, up us assumed.
Shapes referenced with the f_button_state_over flag set are drawn when the mouse is moved over this button.
The f_button_layer is used like a depth parameter. The smallest layer is drawn first (behind) and the highest layer is drawn last (on top of all the other shapes).
Though four flags allow for 16 different states, you are likely to only use a few. The hit test can appear on each state. The down and up won't usually be used together, though, if they are the shape will be drawn when the button is clicked or not.
struct swf_button { char align; unsigned f_button_state_reserved : 4; unsigned f_button_state_hit_test : 1; unsigned f_button_state_down : 1; unsigned f_button_state_over : 1; unsigned f_button_state_up : 1; if(any f_button_state_... != 0) { unsigned short f_button_reference; unsigned short f_button_layer; swf_matrix f_matrix; } };
The structure is always aligned to a byte. If all of the f_button_state_... flags are zeroes, then the entry is an EOB (End Of Buttons) entry.
An event is defined in a PlaceObject2 tag. It is a record of events terminated with a set of zero flags. Events are similar to conditions.
struct swf_event { char align; if(version >= 6) { unsigned f_event_reserved : 13; if(version >= 7) { unsigned f_event_construct : 1; } else { unsigned f_event_reserved : 1; } unsigned f_event_key_press : 1; unsigned f_event_drag_out : 1; unsigned f_event_drag_over : 1; unsigned f_event_roll_out : 1; unsigned f_event_roll_over : 1; unsigned f_event_release_outside : 1; unsigned f_event_release : 1; unsigned f_event_press : 1; unsigned f_event_initialize : 1; } else { unsigned f_event_reserved : 7; } unsigned f_event_data : 1; unsigned f_event_key_up : 1; unsigned f_event_key_down : 1; unsigned f_event_mouse_up : 1; unsigned f_event_mouse_down : 1; unsigned f_event_mouse_move : 1; unsigned f_event_unload : 1; unsigned f_event_enter_frame : 1; unsigned f_event_onload : 1; unsigned long f_event_length; swf_action f_action_record[variable]; };
Note: the number of actions is variable, the f_event_length parameter indicates the number of bytes and can be used to skip all the actions at once. The action array must always be terminated by an End action entry.
A condition is defined in a DefineButton2 tag. It is a record of conditions terminated with a set of zero flags. Conditions are similar to events.
struct swf_condition { unsigned short f_condition_length; unsigned f_condition_key : 7; unsigned f_condition_menu_leave : 1; unsigned f_condition_menu_enter : 1; unsigned f_condition_pointer_release_ouside : 1; unsigned f_condition_pointer_drag_enter : 1; unsigned f_condition_pointer_drag_leave : 1; unsigned f_condition_pointer_release_inside : 1; unsigned f_condition_pointer_push : 1; unsigned f_condition_pointer_leave : 1; unsigned f_condition_pointer_enter : 1; swf_action f_action_record[variable]; };
Note: the number of actions is variable, the f_condition_length parameter indicates the number of bytes and can be used to skip all the conditions and actions at once. The action array must always be terminated by an End action entry.
An external reference is a per of entries: an identifier and a name. The name is called the external symbol and is used to match the necessary definitions between two movies using Export and Import
struct swf_external { unsigned short f_id; string f_symbol_name; };
Information on how to playback a sound effect. These are found in a StartSound and a DefineButtonSound.
struct swf_soundinfo { unsigned short f_sound_id; unsigned f_reserved : 2; unsigned f_stop_playback : 1; unsigned f_no_multiple : 1; unsigned f_has_envelope : 1; unsigned f_has_loops : 1; unsigned f_has_out_point : 1; unsigned f_has_in_point : 1; if(f_has_in_point) { unsigned long f_in_point; } if(f_has_out_point) { unsigned long f_out_point; } if(f_has_loop_count) { unsigned short f_loop_count; } if(f_has_envelope) { unsigned char f_envelope_count; swf_envelope f_envelope[f_envelope_count]; } };
The f_sound_id is a reference to an earlier DefineSound tag.
The f_stop_playback can be set to 1 in which case the sound stops as soon as the next ShowFrame is reached. All the other flags should be set to 0 when this one is 1.
The f_no_multiple flag indicates whether the same sound effect can be played more than once at a time.
The f_in/out_point indicate the start and end points where the sound should start playing and where it will end. f_in_point should always be smaller than f_out_point. By default, f_in_point is taken as being 0 and f_out_point is set to the f_sound_samples_count value.
The f_loop_count defines the number of time the sound will be played back. I don't know yet whether there is a special value which means playback forever.
When playing back a sound effect it is possible to modulate the sound to generate different effects (such as a fade in and out). The following defines the stereo volume of the sound.
struct swf_envelope { unsigned long f_position; unsigned short f_volume_left; unsigned short f_volume_right; };
The position is always given as if the sample data was defined with a rate of 44100 bytes per seconds. For instance, the sample number 1 in a sound effect with a sample rate of 5.5K is given as position 8 in the envelope. All of these positions should be within the f_in_point and f_out_point.
Mono sound should use the same value for the left and right volumes. Note that it will automatically be avaraged if necessary.
Since version 7 of SWF, there is a new way to create a function which allows you to not only name parameters but also to put their content in a register. This is done by specifying a register number along an (optional) parameter name.
struct swf_params { unsigned char f_param_register; string f_param_name; };
The f_param_register specifies whether the corresponding parameter will be saved in a register (when it's not zero) or in a named variable or both.
Note that the auto-generated variables (those defined by the "preload" flags to the Declare Function (V7)) are also saved in registers. You have to make sure you save your own variables in registers that are not already in use by these system variables.
The f_param_name string will be ignored whenever the f_param_register parameter is not zero. Otherwise, it is used to save the corresponding parameter in a variable of that name. Since up to 255 registers can be used, it rarely will be necessary to save local variables in named variables.
The most common and simple geometric information are the object coordinates on the output screen. These are defined in TWIPs. There are 20 twips per pixels. Note that an embedded SWF file can be enlarged and/or reduced thus changing this basic scaling factor. To have exactly 20 twips per pixel you must ensure that the EMBED and/or OBJECT tags use a WIDTH and HEIGHT with exactly the same value as in the rectangle defined in the SWF header file divided by 20. The coordinates are defined from the top-left of the screen area to the bottom-right (x increases from the left to the right as expected; y increases from top to bottom as on most graphical devices on computers). The following shows you the coordinates system.
Because one can use scaling and translations, the coordinates can easilly be inverted to have the y coordinates grow from bottom to top. However, to create your own player or generate proper SWF files, you need to know how the raw coordinates system works.
There are limits to everything including coordinates. In order to enable all sorts of objects to be drawn, one should look at the result in a pixel environment (ie. as it will be drawn on the final screen). The idea of using TWIPs is to enable some interested people to zoom in (up to 20 times!) and still keep a high quality (this isn't true for images though).
The coordinates are often transformed with the use of a matrix. The matrix is similar to a transformation matrix in Postscript. It includes a set of scaling factors, rotation angles and translations.
When only the scaling factors are used (no rotation) then these are ratios as one would expect. If a rotation is also applied, then the scaling ratios will be affected accordingly.
The translations are in TWIPS like any coordinates and also they are applied last (thus, it represents the position at which the shape is drawn on the output screen).
The math formula is as simple as: Q = MP + T. Q is the resulting point, P is the source point, M is the scaling and rotation factors and T is the final translation.
With the use of a three dimensional set of matrices, one can compute a single matrix which includes all the transformations.
|
Thus, the matrix saved in the SWF file is the product of the matrix in figure 2 and the matrix in figure 5.
A matrix multiplication is the sum of the products of lines (left matrix) and rows (right matrix).
m11 = s11 * r11 + s12 * r21 + s13 * r31 + s14 * r41 m12 = s21 * r11 + s22 * r21 + s23 * r31 + s24 * r41 m13 = s31 * r11 + s32 * r21 + s33 * r31 + s34 * r41 m14 = s41 * r11 + s42 * r21 + s43 * r31 + s44 * r41 m21 = s11 * r12 + s12 * r22 + s13 * r32 + s14 * r42 m22 = s21 * r12 + s22 * r22 + s23 * r32 + s24 * r42 m23 = s31 * r12 + s32 * r22 + s33 * r32 + s34 * r42 m24 = s41 * r12 + s42 * r22 + s43 * r32 + s44 * r42 m31 = s11 * r13 + s12 * r23 + s13 * r33 + s14 * r43 m32 = s21 * r13 + s22 * r23 + s23 * r33 + s24 * r43 m33 = s31 * r13 + s32 * r23 + s33 * r33 + s34 * r43 m34 = s41 * r13 + s42 * r23 + s43 * r33 + s44 * r43 m41 = s11 * r14 + s12 * r24 + s13 * r34 + s14 * r44 m42 = s21 * r14 + s22 * r24 + s23 * r34 + s24 * r44 m43 = s31 * r14 + s32 * r24 + s33 * r34 + s34 * r44 m44 = s41 * r14 + s42 * r24 + s43 * r34 + s44 * r44
Though you shouldn't need to find the scaling factors and rotation
angle from an SWF matrix, it is possible to find one if you know
the other. This is done using a multiplication of either the inverse
scaling (use: 1/Sx
and 1/Su
instead of
Sx
and Sy
for the scaling matrix) or the
inverse rotation (use: -angle
instead of angle
in the Z rotation
matrix).
For those who still wonder what I'm talking about, there are the four computations you need from a scaling factor and an angle in radiant:
SWFmatrix11 = Sx * cos(angle) SWFmatrix12 = Sy * sin(angle) SWFmatrix21 = -Sx * sin(angle) SWFmatrix22 = Sy * cos(angle)
SWFmatrix11
and SWFmatrix22
are saved in the
(x, y) scale respectively and the SWFmatrix21
and
SWFmatrix12
are the rotation skew0 and skew1 values
respectively.
Edges are used to define a shape vector based and also coordinates where images need to be drawn. The edges are always coordinates from where ever your last point was to where ever you want the next point to be (a little like a turtle in LOGO).
For vector based shapes to be placed anywhere in the screen and easilly transformed with matrices, you should always create them centered properly (i.e. the center of the shape should be placed whereever you think it is the most appropriate in order to enable easy rotations - i.e. in a cercle, the center of the circle should be selected and in a square the center of that square).
The fill styles and line styles can all be used together. The line style is fairly easy to understand. There is a width in TWIPS and a color. When a filled shape is being drawn using a line style it is used to draw the borders of the shape. There can be one or two fill styles. They both are drawn one after another wherever an area has to be filled. The filling scheme is a very simple even-odd scheme (i.e. don't draw till first line being crossed, then draw till next line, then don't draw till next line, etc.)
The edges are defined either as a straight line record (a set of (x, y) coordinates) or a curve record (two sets of (x, y) coordinates). These coordinates are not absolute. Instead these are added to the previous coordinates. This usually enables for much better compression since these numbers are always very small.
The encoding even enables the definition of straight lines with the use of only the x or y offset for straight lines. Thus, if you create a square, with coordinates (-10,-10) and (+10,+10) you would define it as follow in SWF:
| |
Edges - Fig 2. |
The curves are simple B-splines defined by only three points (see Edges - Fig 3.):
Curves perfectly go through the starting and ending points which can therefore be perfectly continued by a straight line or another curve.
A curve is defined as six curve segments Q3 to Q8. These are defined by duplicating the starting and ending points four times each giving a set of points similar to [A, A, A, A, B, C, C, C, C]. Edges - Fig. 4 shows the computation used to define each segment (i varying from 3 to 8). The parameter u can be given values from 0 to 1. The number of values will depend on the precision you need to draw the resulting curve.
Though the math can be simplified in the case of SWF (since we only have three points), the following shows you a simple C code which can be used to draw such a curve.
struct point { double x; double y; }; const double Bmatrix[4][4] = { { -1, 3, -3, 1 }, { 3, -6, 3, 0 }, { -3, 0, 3, 0 }, { 1, 3, 1, 0 } }; void compute_point(double u, const struct point *input_points, struct point *result_point) { double Umatrix[4], Pmatrix[4][4], Imatrix[4], Rmatrix[4]; /* compute the U factors */ Umatrix[0] = u * u * u; Umatrix[1] = u * u; Umatrix[2] = u; Umatrix[3] = 1; /* because there is no Z it is set to zero */ Pmatrix[0][0] = input_points[0].x; Pmatrix[0][1] = input_points[0].y; Pmatrix[0][2] = 0; Pmatrix[0][3] = 1; Pmatrix[1][0] = input_points[1].x; Pmatrix[1][1] = input_points[1].y; Pmatrix[1][2] = 0; Pmatrix[1][3] = 1; Pmatrix[2][0] = input_points[2].x; Pmatrix[2][1] = input_points[2].y; Pmatrix[2][2] = 0; Pmatrix[2][3] = 1; Pmatrix[3][0] = input_points[3].x; Pmatrix[3][1] = input_points[3].y; Pmatrix[3][2] = 0; Pmatrix[3][3] = 1; /* compute I = UB */ Imatrix[0] = Umatrix[0] * Bmatrix[0][0] + Umatrix[1] * Bmatrix[1][0] + Umatrix[2] * Bmatrix[2][0] + Umatrix[3] * Bmatrix[3][0]; Imatrix[1] = Umatrix[0] * Bmatrix[0][1] + Umatrix[1] * Bmatrix[1][1] + Umatrix[2] * Bmatrix[2][1] + Umatrix[3] * Bmatrix[3][1]; Imatrix[2] = Umatrix[0] * Bmatrix[0][2] + Umatrix[1] * Bmatrix[1][2] + Umatrix[2] * Bmatrix[2][2] + Umatrix[3] * Bmatrix[3][2]; Imatrix[3] = Umatrix[0] * Bmatrix[0][3] + Umatrix[1] * Bmatrix[1][3] + Umatrix[2] * Bmatrix[2][3] + Umatrix[3] * Bmatrix[3][3]; /* I = I x 1/6 */ Imatrix[0] /= 6.0; Imatrix[1] /= 6.0; Imatrix[2] /= 6.0; Imatrix[3] /= 6.0; /* R = IP */ Rmatrix[0] = Imatrix[0] * Pmatrix[0][0] + Imatrix[1] * Pmatrix[1][0] + Imatrix[2] * Pmatrix[2][0] + Imatrix[3] * Pmatrix[3][0]; Rmatrix[1] = Imatrix[0] * Pmatrix[0][1] + Imatrix[1] * Pmatrix[1][1] + Imatrix[2] * Pmatrix[2][1] + Imatrix[3] * Pmatrix[3][1]; Rmatrix[2] = Imatrix[0] * Pmatrix[0][2] + Imatrix[1] * Pmatrix[1][2] + Imatrix[2] * Pmatrix[2][2] + Imatrix[3] * Pmatrix[3][2]; Rmatrix[3] = Imatrix[0] * Pmatrix[0][3] + Imatrix[1] * Pmatrix[1][3] + Imatrix[2] * Pmatrix[2][3] + Imatrix[3] * Pmatrix[3][3]; /* copy the result in user supplied result point */ result_point[0].x = Rmatrix[0]; result_point[0].y = Rmatrix[1]; } void compute_curve(long repeat, const struct point *input_points, struct point *result_points) { /* we assume that the input_points are the three points of interest (namely: start, control and end) */ /* we assume that the result_points is a large enough array to receive the resulting points (depends on the repeat parameter) */ struct point *points[9]; int p, i; double u; /* transform so the compute_point can easilly be used */ points[0] = input_points[0]; points[1] = input_points[0]; points[2] = input_points[0]; points[3] = input_points[0]; points[4] = input_points[1]; points[5] = input_points[2]; points[6] = input_points[2]; points[7] = input_points[2]; points[8] = input_points[2]; /* we could have a way to define the very first and last points without calling the sub-function since these are equal to the input_points[0] and input_points[2] respectively */ for(p = 0, i = 3; i <= 8; i++) { for(u = 0.0; u < 1.0; u += 1.0 / repeat, p++) { compute_point(u, points + i, result_points + p); } } }
Any good C programmer will see many possible simplifications
in the compute_point
code. The two main simplications
are the Rmatrix[2]
and Rmatrix[3]
which
are not required (and therefore don't need to be computed). The
division by 6 could be applied to the B matrix.
Umatrix[3]
being 1.0, it could be ignored in the
computations. etc. etc. etc.
It is possible in SWF to use gradient fills. The gradient definitions are pretty raw and require you to draw large objects (that you can scale down later if you wish). A radial fill will usually be used to draw a round corner or a big & smooth dot. A linear fill can be used to draw objects which go from one color to another. The linear fill goes from left to right by default. It can be rotation as required though. Yet, in either case what is drawn in the shape object needs to be at the right scale and in the right direction. This may not always prove easy to deal with!
There are some additional technical information with the description of the gradient records.
When appropriate, images can also be included in SWF files. All the images can be full color and also have an alpha channel.
In order to draw an image on the screen, it is necessary to use a fill style
and a shape. Thus, you need at least three tags: TagLossless or TagJPEG,
a TagShape and a TagPlace in order to draw an image on the screen. The fill
style of the shape needs to include a matrix with a scale of 20x20 in
order to draw the image at the original sizes. Also the rectangle used to
draw around the image will use 20 twips per pixels of the image. Like
with other shapes, if it is necessary to rotate the image by the center,
then the shape will have to be defined with a MOVE to
(width / -2.0, height / -2.0)
and the image rectangle draw
around the center. With edges, this means a set of positions
such as:
If you want to draw the image only once, you should make sure that the fill is of type clipped. A tilled image could be drawn multiple times.
There is an example of a 640x480 image:
Tag: DefineBitsLossless
Object ID: 1
Format: 5 (32 bits ARGB)
Bitmap sizes: 640x480
Tag: DefineShape
Object ID: 2
Rectangle: (0, 0) - (12800, 9600)
Fill Style #1:
Clipped Bitmap, ID: 1
Matrix:
Scale 20x20
Translate: (-6400, -4800)
Edges:
Move (-6400, -4800)
Use fill #1
Delta (12800, 0)
Delta (0, 9600)
Delta (-12800, 0)
Delta (0, -9600)
Tag: PlaceObject
Place Object: #2
Depth: 1
Matrix:
Translate: (6400, 4800)
Note that it is possible that one pixel will be missing using such values. It isn't rare to add 20 to the Edges deltas in order to include the missing pixel (this is mainly due to the computation of the anti-aliasing effect).
May 10, 2005 | Many changes to the Declare Function (V7) for clarification and some error fixes (i.e. it only supports 255 variables for parameters, I mentioned 256 in different places; the bits are defined on a short which means the bytes are swapped in the SWF files; I added the arguments as one of the internal parameters; I added some comments about how to load or generate these flags; better explanation for the preload vs. suppress flags). |
March 25, 2005 | Fixed the name swf_protect pointed out by Benoit Perrot (thanks!). |
January 18, 2005 | Fixed the text record information (swf_text_record) which changed with version 7. The change was pinpointed by Thatcher Ulrich (thanks!), the author of gameswf. |
October 15, 2004 | Fixed the sound sample definition so it properly defines the samples as being signed. |
October 03, 2004 |
Added Declare Function (V7).
Added swf_params. Added information about the 256 registers available in SWF version 7. Ameliorated the Push Register Data documentation. Added Extends (SWF version 7). Added Throw (SWF version 7). Added Try (SWF version 7). Added Cast Object (SWF version 7). Added Implements (SWF version 7). Added ScriptLimits (SWF version 7). Added SetTabIndex (SWF version 7). |
September 30, 2004 | Moved the geometry explainations at the end in Appendix A. If I ever decide to cut the file in parts, that would become a seperate part. |
September 14, 2004 | Fixed the sample shown for the computation of the SWF coordinates. |
July 17, 2004 |
Fixed some English grammar.
Added a link back to the home page.
Added a warning about non-existant fonts on a system
when referencing a system font from an
|
February 20, 2004 | Fixed many points by adding a new line (it looks nicer). |
June 07, 2003 |
Fixed the tag_import to 57 instead of 56 (tag_export ).
(Special thanks to Thatcher Ulrich - http://tulrich.com.)
|
May 30, 2003 |
Fixed the f_edit_indent & f_edit_leading from
unsigned short to signed short.
(by Thatcher Ulrich - http://tulrich.com.)
|
December 6, 2002 |
Added different sound tags and the corresponding common structures.
Added the DefineButtonSound tag. |
November 28, 2002 | Added the language entry in the DefineFont2 and DefineFontInfo tags. Added some other information about fonts. |
October 30, 2002 |
The DefineFont2 tag was fixed. It is necessary to have an
extra offset which actually represents the total size of
the glyphs.
Added the Color and Math objects. Changed the limit on the number of entries in a dictionary from 256 to 65534 (not sure what 65535 may be used for). Removed the 2nd instance of properties. The PlaceObject2 describs the clipping mechanism available with it and which objects can be used to clip others. Added the DoInitAction tag with complete definitions (V6.0) Added the ProtectDebug[2] tags with complete definitions (V5.0 & V6.0) Added the Export and Export tags with complete definitions (V5.0) |
October 10, 2002 | Fixed some information about several tags and actions. Added tag 58 (Password?). |
October 8, 2002 | Added tag 49 (comment about the generator of an SWF movie). Fixed some information about the property sets. |
October 3, 2002 | Fixed the definition of the DefineEditText. It doesn't talk about the button anymore and includes what and how it works. |
June 17, 2002 | Creation of this document based on the different documents available on the OpenSWF.org web site (mainly the SWF File Reference.) And once the sswf tool started to work, on how the plugin would behave with the files generated by it. |
This document was last modified on Sep 10 2005.