#!/usr/bin/env escript %% -*- erlang -*- %%! debug verbose % an example shape. spec() -> {{frame_size, 6000, 3000}, {frame_rate, 16}, {frame_count, 160}, [{set_background_color, {rgb, 240, 250, 250}}, sample_shape(), {place_object2, 1}, {show_frame}, {end_tag}]}. sample_shape() -> {define_shape, {shape_id, 1}, {bounds, {rectangle, 2000, 3000, 1000, 2000}}, {shapes, {{fills, [{solid_fill, {rgb, 50, 200, 50}}]}, {lines, [{line_style, 100, {rgb, 100, 255, 100}}, {line_style, 100, {rgb, 50, 150, 50}}]}, {shape_records, [{style_change, [{move_to, 2000, 2000}]}, {style_change, [{line_style, 1}, {fill_style0, 1}]}, {straight_edge, 0, -1000}, {straight_edge, 1000, 0}, {style_change, [{line_style, 2}, {fill_style0, 1}]}, {straight_edge, 0, 1000}, {straight_edge, -1000, 0}, {end_shape}] }}}}. main(_) -> Filename = "made_by_erlang.swf", {ok, S} = file:open(Filename, [binary, write]), file:write(S, swf(spec())), file:close(S). % SWF Structure swf(Spec) -> Version = 10, Rest = swf_rest(Spec), FileLength = size(Rest) + 8, <<"FWS", Version:8/unsigned-little, FileLength:32/unsigned-little, Rest/binary>>. swf_rest({{frame_size, Width, Height}, {frame_rate, FrameRate}, {frame_count, FrameCount}, Tags}) -> FrameSize = rectangle({rectangle, 0, Width, 0, Height}), FrameRateData = fixed8dot8(FrameRate), TagsData = list_to_binary([tag(X) || X <- Tags]), <>. % Basic Data Types nbits_unsigned(XS) -> % Necessary bits size for a list of integer values. Max = lists:max([abs(X) || X <- XS]), trunc(math:log(Max) / math:log(2)) + 1. nbits_signed(XS) -> nbits_unsigned(XS) + 1. fixed8dot8(N)-> IntegerPart = trunc(N), SmallPart = trunc((N - IntegerPart) / 1 * 256), <>. rectangle({rectangle, Xmin, Xmax, Ymin, Ymax}) -> Nbits = nbits_signed([Xmin, Xmax, Ymin, Ymax]), padding(<< Nbits:5, Xmin:Nbits/signed-big, Xmax:Nbits/signed-big, Ymin:Nbits/signed-big, Ymax:Nbits/signed-big>>). rgb({rgb, R, G, B}) -> <>. % Tag Format record_header_body(Type, Body) -> record_header_body(Type, Body, size(Body)). record_header_body(Type, Body, Length) when Length < 63 -> <> = <>, [<>, Body]; record_header_body(Type, Body, Length) -> <> = <>, [<>, <>, Body]. % Control Tags tag({end_tag}) -> record_header_body(0, <<>>); tag({show_frame}) -> record_header_body(1, <<>>); tag({define_shape, {shape_id, ShapeId}, {bounds, Bounds}, {shapes, ShapesWithStyle}}) -> BoundsBits = rectangle(Bounds), Shapes = shape_with_style(ShapesWithStyle), record_header_body(2, list_to_binary([<>, BoundsBits, Shapes])); tag({set_background_color, RGB}) -> record_header_body(9, rgb(RGB)); tag({place_object2, CharacterId}) -> PlaceFlagHasClipActions = 0, PlaceFlagHasClipDepth = 0, PlaceFlagHasName = 0, PlaceFlagHasRatio = 0, PlaceFlagHasColorTransform = 0, PlaceFlagHasMatrix = 0, PlaceFlagHasCharacter = 1, PlaceFlagMove = 0, Depth = 1, record_header_body(26, <>). % Shape record definition. shape_with_style({{fills, Fills}, {lines, Lines}, {shape_records, Shapes}}) -> FillBits = nbits_unsigned([length(Fills)]), LineBits = nbits_unsigned([length(Lines)]), ShapeRecords = shape_records({shape_records, Shapes}, FillBits, LineBits), [fill_style_array({fills, Fills}), line_style_array({lines, Lines}), <>, ShapeRecords]. fill_style_array({fills, FillStyleArray}) -> FillStyleCount = length(FillStyleArray), [<>, [fill_style(X) || X <- FillStyleArray]]. fill_style({solid_fill, RGB}) -> [<<16#00:8>>, rgb(RGB)]. line_style_array({lines, LineStyleArray}) -> LineStyleCount = length(LineStyleArray), [<>, [line_style(X) || X <- LineStyleArray]]. line_style({line_style, Width, RGB}) -> [<>, rgb(RGB)]. shape_records({shape_records, ShapeRecords}, FillBits, LineBits) -> padding(concat_bit([shape_record(X, FillBits, LineBits) || X <- ShapeRecords])). padding(Bits) -> Padding = 8 - bit_size(Bits) rem 8, <>. concat_bit(XS) -> lists:foldl(fun(X, Y) -> <> end, <<>>, XS). shape_record({end_shape}, _, _) -> <<0:1, 0:5>>; shape_record({straight_edge, DeltaX, DeltaY}, _, _) -> NumBits = nbits_signed([DeltaX, DeltaY]), GeneralLineFlag = 1, <<1:1, 1:1, (NumBits - 2):4, GeneralLineFlag:1, DeltaX:NumBits, DeltaY:NumBits>>; shape_record({style_change, ChangeSpec}, FillBits, LineBits) -> TypeFlag = 0, StateNewStyles = 0, StateFillStyle1 = 0, {StateLineStyle, LineStyleData} = case lists:keysearch(line_style, 1, ChangeSpec) of {value, {_, LineStyle}} -> {1, <>}; false -> {0, <<>>} end, {StateFillStyle0, FillStyle0Data} = case lists:keysearch(fill_style0, 1, ChangeSpec) of {value, {_, FillStyle0}} -> {1, <>}; false -> {0, <<>>} end, {StateMoveTo, MoveData} = case lists:keysearch(move_to, 1, ChangeSpec) of {value, {_, MoveDeltaX, MoveDeltaY}} -> MoveBits = nbits_signed([MoveDeltaX, MoveDeltaY]), {1, <>}; false -> {0, <<>>} end, <>.