自己紹介

今日やりたいこと

簡単な1フレームのSWFをベクターデータとして再現できるようになる

はじめに

アジェンダ

SWFのベクター表現

SWFでベクターを描画するアクションは3つだけ

正方形を書いてみよう

// drawRectを使う
public function draw(): void 
{
    var shape: Shape = new Shape();

    shape.graphics.lineStyle(4, 0x0000ff);
    shape.graphics.beginFill(0xff0000, 1.0);
    shape.graphics.drawRect(50, 70, 140, 140);

    addChild(shape);
}
              

正方形を書いてみよう

// SWFで使える機能のみを使う
public function lineTo(): Shape 
{
    var shape: Shape = new Shape();

    shape.graphics.lineStyle(4, 0x0000ff);
    shape.graphics.beginFill(0xff0000, 1.0);

    shape.graphics.moveTo(50, 70);
    shape.graphics.lineTo(50, 210);
    shape.graphics.lineTo(190, 210);
    shape.graphics.lineTo(190, 70);
    shape.graphics.lineTo(50, 70);

    addChild(shape);
}
              

円を書いてみよう

// drawCircleを使う
public function draw(): void 
{
    var shape: Shape = new Shape();

    shape.graphics.lineStyle(4, 0x0000ff);
    shape.graphics.beginFill(0xff0000, 1.0);
    shape.graphics.drawCircle(100, 100, 90);

    addChild(shape);
}
              

円を書いてみよう

// SWFで使えるものだけを使う
public function draw(): void 
{
    var shape: Shape = new Shape();

    shape.graphics.lineStyle(4, 0x0000ff);
    shape.graphics.beginFill(0xff0000, 1.0);

    shape.graphics.moveTo(163.60, 163.60);
    shape.graphics.curveTo(137.30, 190.00, 100.00, 190.00);
    shape.graphics.curveTo(62.65, 190.00, 36.30, 163.60);
    shape.graphics.curveTo(10.00, 137.30, 10.00, 100.00);
    shape.graphics.curveTo(10.00, 62.65, 36.30, 36.30);
    shape.graphics.curveTo(62.65, 10.00, 100.00, 10.00);
    shape.graphics.curveTo(137.30, 10.00, 163.60, 36.30);
    shape.graphics.curveTo(190.00, 62.65, 190.00, 100.00);
    shape.graphics.curveTo(190.00, 137.30, 163.60, 163.60);

    addChild(shape);
}
              

SWFフォーマットにおけるベクターデータのまとめ


moveTolineTocurveTo塗り
があれば、どんな言語にも
必ずコンバートできる

コンバートしてみよう

  • 1フレーム×1レイヤー
  • シンボル化していない
  • 隣接する色は無い
  • 最適化をしている

コンバートしてみよう

swfdumpで中身を見てみよう

$ swfdump --shapes sample/sample.swf
[002]       146 DEFINESHAPE defines id 0001
                 | fillstyles(02)        linestyles(00)
                 | 1 ) SOLID 000066ff
                 | 2 ) SOLID ff0000ff
                 |
                 | fill: 00/02 line:00 - moveTo 189.85 69.05
                 | fill: 00/02 line:00 - splineTo (199.95 79.15) 199.95 93.45
                 | fill: 00/02 line:00 - splineTo (199.95 107.75) 189.85 117.85
                 | fill: 00/02 line:00 - splineTo (180.55 127.15) 167.75 127.90
                 | fill: 00/02 line:00 - lineTo 209.70 200.65
                 | fill: 00/02 line:00 - lineTo 121.25 200.65
                 | fill: 00/02 line:00 - lineTo 163.30 127.90
                 | fill: 00/02 line:00 - splineTo (150.40 127.20) 141.05 117.85
                 | fill: 00/02 line:00 - splineTo (130.95 107.75) 130.95 93.45
                 | fill: 00/02 line:00 - splineTo (130.95 79.15) 141.05 69.05
                 | fill: 00/02 line:00 - splineTo (151.15 58.95) 165.45 58.95
                 | fill: 00/02 line:00 - splineTo (179.75 58.95) 189.85 69.05
                 | fill: 00/01 line:00 - moveTo 106.00 93.45
                 | fill: 00/01 line:00 - splineTo (106.00 107.75) 95.90 117.85
                 | fill: 00/01 line:00 - splineTo (90.50 123.25) 83.95 125.75
                 | fill: 00/01 line:00 - lineTo 116.20 125.75
                 | fill: 00/01 line:00 - lineTo 72.00 202.35
                 | fill: 00/01 line:00 - lineTo 27.75 125.75
                 | fill: 00/01 line:00 - lineTo 59.10 125.75
                 | fill: 00/01 line:00 - splineTo (52.50 123.25) 47.10 117.85
                 | fill: 00/01 line:00 - splineTo (37.00 107.75) 37.00 93.45
                 | fill: 00/01 line:00 - splineTo (37.00 79.15) 47.10 69.05
                 | fill: 00/01 line:00 - splineTo (57.20 58.95) 71.50 58.95
                 | fill: 00/01 line:00 - splineTo (85.80 58.95) 95.90 69.05
                 | fill: 00/01 line:00 - splineTo (106.00 79.15) 106.00 93.45
                 |
          

対応するコードを書いてみよう

ActionScript3.0で書いてみよう

public function shape(): Shape
{
    var shape: Shape = new Shape();

    shape.graphics.beginFill(0xff0000, 1); // 2
    
    shape.graphics.moveTo(189.85, 69.05); // fill 00/02, line 00
    shape.graphics.curveTo(199.95, 79.15, 199.95, 93.45); // fill 00/02, line 00
    shape.graphics.curveTo(199.95, 107.75, 189.85, 117.85); // fill 00/02, line 00
    shape.graphics.curveTo(180.55, 127.15, 167.75, 127.90); // fill 00/02, line 00
    shape.graphics.lineTo(209.70, 200.65); // fill 00/02, line 00
    shape.graphics.lineTo(121.25, 200.65); // fill 00/02, line 00
    shape.graphics.lineTo(163.30, 127.90); // fill 00/02, line 00
    shape.graphics.curveTo(150.40, 127.20, 141.05, 117.85); // fill 00/02, line 00
    shape.graphics.curveTo(130.95, 107.75, 130.95, 93.45); // fill 00/02, line 00
    shape.graphics.curveTo(130.95, 79.15, 141.05, 69.05); // fill 00/02, line 00
    shape.graphics.curveTo(151.15, 58.95, 165.45, 58.95); // fill 00/02, line 00
    shape.graphics.curveTo(179.75, 58.95, 189.85, 69.05); // fill 00/02, line 00

    shape.graphics.beginFill(0x000066, 1); // 1
    shape.graphics.moveTo(106.00, 93.45); // fill 00/01, line 00
    shape.graphics.curveTo(106.00, 107.75, 95.90, 117.85); // fill 00/01, line 00
    shape.graphics.curveTo(90.50, 123.25, 83.95, 125.75); // fill 00/01, line 00
    shape.graphics.lineTo(116.20, 125.75); // fill 00/01, line 00
    shape.graphics.lineTo(72.00, 202.35); // fill 00/01, line 00
    shape.graphics.lineTo(27.75, 125.75); // fill 00/01, line 00
    shape.graphics.lineTo(59.10, 125.75); // fill 00/01, line 00
    shape.graphics.curveTo(52.50, 123.25, 47.10, 117.85); // fill 00/01, line 00
    shape.graphics.curveTo(37.00, 107.75, 37.00, 93.45); // fill 00/01, line 00
    shape.graphics.curveTo(37.00, 79.15, 47.10, 69.05); // fill 00/01, line 00
    shape.graphics.curveTo(57.20, 58.95, 71.50, 58.95); // fill 00/01, line 00
    shape.graphics.curveTo(85.80, 58.95, 95.90, 69.05); // fill 00/01, line 00
    shape.graphics.curveTo(106.00, 79.15, 106.00, 93.45); // fill 00/01, line 00

    return shape;
}
          

対応するコードを書いてみよう

JavaScriptでも書いてみよう

onload = function() {
  var canvas = document.getElementById('shape');
  var ctx    = canvas.getContext('2d');

  ctx.beginPath();
  ctx.fillStyle = '#ff0000'; // 2
  ctx.moveTo(189.85, 69.05); // fill 00/02, line 00
  ctx.quadraticCurveTo(199.95, 79.15, 199.95, 93.45); // fill 00/02, line 00
  ctx.quadraticCurveTo(199.95, 107.75, 189.85, 117.85); // fill 00/02, line 00
  ctx.quadraticCurveTo(180.55, 127.15, 167.75, 127.90); // fill 00/02, line 00
  ctx.lineTo(209.70, 200.65); // fill 00/02, line 00
  ctx.lineTo(121.25, 200.65); // fill 00/02, line 00
  ctx.lineTo(163.30, 127.90); // fill 00/02, line 00
  ctx.quadraticCurveTo(150.40, 127.20, 141.05, 117.85); // fill 00/02, line 00
  ctx.quadraticCurveTo(130.95, 107.75, 130.95, 93.45); // fill 00/02, line 00
  ctx.quadraticCurveTo(130.95, 79.15, 141.05, 69.05); // fill 00/02, line 00
  ctx.quadraticCurveTo(151.15, 58.95, 165.45, 58.95); // fill 00/02, line 00
  ctx.quadraticCurveTo(179.75, 58.95, 189.85, 69.05); // fill 00/02, line 00
  ctx.fill();

  ctx.beginPath();
  ctx.fillStyle = '#000066'; // 1
  ctx.moveTo(106.00, 93.45); // fill 00/01, line 00
  ctx.quadraticCurveTo(106.00, 107.75, 95.90, 117.85); // fill 00/01, line 00
  ctx.quadraticCurveTo(90.50, 123.25, 83.95, 125.75); // fill 00/01, line 00
  ctx.lineTo(116.20, 125.75); // fill 00/01, line 00
  ctx.lineTo(72.00, 202.35); // fill 00/01, line 00
  ctx.lineTo(27.75, 125.75); // fill 00/01, line 00
  ctx.lineTo(59.10, 125.75); // fill 00/01, line 00
  ctx.quadraticCurveTo(52.50, 123.25, 47.10, 117.85); // fill 00/01, line 00
  ctx.quadraticCurveTo(37.00, 107.75, 37.00, 93.45); // fill 00/01, line 00
  ctx.quadraticCurveTo(37.00, 79.15, 47.10, 69.05); // fill 00/01, line 00
  ctx.quadraticCurveTo(57.20, 58.95, 71.50, 58.95); // fill 00/01, line 00
  ctx.quadraticCurveTo(85.80, 58.95, 95.90, 69.05); // fill 00/01, line 00
  ctx.quadraticCurveTo(106.00, 79.15, 106.00, 93.45); // fill 00/01, line 00
  ctx.fill();
};
          

対応するコードを書いてみよう

完成

Shapeの詳解

Shapeタグの種類

DefineShape 初期
≧ SWF1
DefineShape2 Styleを増量
≧ SWF2
DefineShape3 アルファをサポート
≧ SWF3
DefineShape4 線の種類が増加
≧ SWF8

DefineShape*タグの構造

Header Tag type = 2, 22, 32, 83
ShapeId characterId (Define*の中でユニーク)
ShapeBounds 矩形の境界 中心点とか (0,0) の位置とかが決まる
Shapes ベクターデータに関する諸々の情報
FillStyles 塗りの情報
LineStyles 線の情報
NumFillBits FillStyleのインデックスサイズ
NumLineBits LineStyleのインデックスサイズ
ShapeRecords moveToとか

FillStyle (塗り)

FillStyleType 0x00 = 塗りつぶし
0x10-0x13 = グラデーション
0x40-0x43 = 画像
Color RGB(Shape1,2) RGBA(Shape3)
GradientMatrix グラデーションの配置(回転や始点など)
Gradient グラデーションの情報
BitmapId 画像のcharacterId
BitmapMatrix 画像のMatrix

LineStyle (線)

Width 線の太さ
Color RGB(Shape1,2) RGBA(Shape3)

SWF8以降のlineStyle2で項目が増えますが省略します

ShapeRecord

以下の4種類で構成

EndShapeRecord レコードの終了
StyleChangeRecord
  • 新しいStyleListの更新
  • Styleの変更
  • moveTo
StraightEdgeRecord lineTo
CurvedEdgeRecord curveTo

正方形のタグを見てみよう

DefineShape[2]: 
    ShapeID: 1
    ShapeBounds: { Xmin: 960, Xmax: 3840, Ymin: 1360, Ymax: 4240 }
    Shapes: 
        FillStyles: 
            [1] FILLSTYLE1: { Color: #ff0000 }
        LineStyles: 
            [1] LINESTYLE1: { With: 80, Color: #0000ff }
        ShapeRecords: 
            StyleChangeRecord: MoveTo( 3800, 4200 ), FillStyle1: 1, LineStyle: 1
            StraightEdgeRecord: LineTo( -2800, 0 )
            StraightEdgeRecord: LineTo( 0, -2800 )
            StraightEdgeRecord: LineTo( 2800, 0 )
            StraightEdgeRecord: LineTo( 0, 2800 )
            EndShapeRecord: 

相対値→絶対値

パスの座標は相対値

絶対値に変更するには

(toX', toY') = (x + toX, y + toY)

curveToではどうなるか

(controleX', controleY') = (x + controleX, y + controleY)
(toX', toY') = (x + controleX + toX, y + controleY + toY)

Shapesの構成

  • 予め(fill|line)Styleを定義
  • (fill|line)Styleは複数種類定義できる
  • shapeRecordでは色の変更や線の変更をすることができる
  • 定義済みスタイルはshapeRecordで更新することができる
  • shapeRecordは(fill|line)Styleのインデックスを示しStyleを適用する

この図形を再現してみよう

この図形を再現してみよう

swfdump

[002]        65 DEFINESHAPE defines id 0001
                 | fillstyles(03)        linestyles(00)
                 | 1 ) SOLID ff0000ff    
                 | 2 ) SOLID ffff00ff    
                 | 3 ) SOLID 0000ccff    
                 |
                 | fill: 02/03 line:00 - moveTo 161.00 101.00
                 | fill: 02/03 line:00 - lineTo 161.00 156.00
                 | fill: 02/03 line:00 - lineTo 103.00 156.00
                 | fill: 02/00 line:00 - lineTo 103.00 210.00
                 | fill: 02/00 line:00 - lineTo 212.00 210.00
                 | fill: 02/00 line:00 - lineTo 212.00 101.00
                 | fill: 02/00 line:00 - lineTo 161.00 101.00
                 | fill: 01/00 line:00 - lineTo 161.00 44.00
                 | fill: 01/00 line:00 - lineTo 49.00 44.00
                 | fill: 01/00 line:00 - lineTo 49.00 156.00
                 | fill: 01/00 line:00 - lineTo 103.00 156.00
                 | fill: 01/03 line:00 - lineTo 103.00 101.00
                 | fill: 01/03 line:00 - lineTo 161.00 101.00
        

この図形を再現してみよう

ActionScript

public function draw(): void 
{
    var shape: Shape = new Shape();

    shape.graphics.beginFill(0xffff00, 1.0);

    shape.graphics.moveTo( 161.00, 101.00 ); // 2, 3
    shape.graphics.lineTo( 161.00, 156.00 ); // 2, 3
    shape.graphics.lineTo( 103.00, 156.00 ); // 2, 3
    shape.graphics.lineTo( 103.00, 210.00 ); // 2, 0
    shape.graphics.lineTo( 212.00, 210.00 ); // 2, 0
    shape.graphics.lineTo( 212.00, 101.00 ); // 2, 0
    shape.graphics.lineTo( 161.00, 101.00 ); // 2, 0

    shape.graphics.beginFill(0xff0000, 1.0);

    shape.graphics.lineTo( 161.00, 44.00  ); // 1, 0
    shape.graphics.lineTo( 49.00 , 44.00  ); // 1, 0
    shape.graphics.lineTo( 49.00 , 156.00 ); // 1, 0
    shape.graphics.lineTo( 103.00, 156.00 ); // 1, 0
    shape.graphics.lineTo( 103.00, 101.00 ); // 1, 3
    shape.graphics.lineTo( 161.00, 101.00 ); // 1, 3
    addChild( shape );
}
        

この図形を再現してみよう

完成???

完成していない!

タグをしっかりと見てみよう

DefineShape[2]:
    ShapeID: 1
    ShapeBounds: { Xmin: 980, Xmax: 4240, Ymin: 880, Ymax: 4200 }
    Shapes:
        FillStyles:
            [1] FILLSTYLE1: { Color: #ff0000 }
            [2] FILLSTYLE1: { Color: #ffff00 }
            [3] FILLSTYLE1: { Color: #0000cc }
        LineStyles:
        ShapeRecords:
            StyleChangeRecord: MoveTo( 3220, 2020 ), FillStyle0: 2, FillStyle1: 3
            StraightEdgeRecord: LineTo( 0, 1100 )
            StraightEdgeRecord: LineTo( -1160, 0 )
            StyleChangeRecord: FillStyle1: 0
            StraightEdgeRecord: LineTo( 0, 1080 )
            StraightEdgeRecord: LineTo( 2180, 0 )
            StraightEdgeRecord: LineTo( 0, -2180 )
            StraightEdgeRecord: LineTo( -1020, 0 )
            StyleChangeRecord: FillStyle0: 1
            StraightEdgeRecord: LineTo( 0, -1140 )
            StraightEdgeRecord: LineTo( -2240, 0 )
            StraightEdgeRecord: LineTo( 0, 2240 )
            StraightEdgeRecord: LineTo( 1080, 0 )
            StyleChangeRecord: FillStyle1: 3
            StraightEdgeRecord: LineTo( 0, -1100 )
            StraightEdgeRecord: LineTo( 1160, 0 )
            EndShapeRecord:
        

気づいたこと

fillStyle0fillStyle12種類あり両方にStyleが設定されている

なんじゃこりゃあ?

fillStyle0とfillStyle1

fillStyle0 進行方向右側の塗り
fillStyle1 進行方向左側の塗り

fillStyle0 & fillStyle1をふまえて書き直してみる

fill[1] #ff0000 fill[2] #ffff00 fill[3] #0000cc
MoveTo(161, 101) (0) MoveTo(161, 101) (1)
LineTo(161, 156) (0) LineTo(161, 156) (1)
LineTo(103, 156) (0) LineTo(103, 156) (1)
LineTo(103, 210) (0)
LineTo(212, 210) (0)
LineTo(212, 101) (0)
LineTo(161, 101) (0)
LineTo(161, 44) (0)
LineTo(49, 44) (0)
LineTo(49, 156) (0)
LineTo(103, 156) (0)
LineTo(103, 101) (0) LineTo(103, 101) (1)
LineTo(161, 101) (0) LineTo(161, 101) (1)
  • fillStyle0 = 右
  • fillStyle1 = 左

fillStyle0 & fillStyle1をふまえて書き直してみる

public function draw(): void 
{
    var shape: Shape = new Shape();

    // 略

    // fillStyle0 の分を追加
    shape.graphics.beginFill(0x0000cc, 1.0);
    shape.graphics.moveTo( 161.00, 101.00 ); // 2, 3
    shape.graphics.lineTo( 161.00, 156.00 ); // 2, 3
    shape.graphics.lineTo( 103.00, 156.00 ); // 2, 3
    shape.graphics.lineTo( 103.00, 101.00 ); // 1, 3
    shape.graphics.lineTo( 161.00, 101.00 ); // 1, 3

    addChild( shape );
}
        

ここまでで基本的なShapeをコンバートしました

しかし

  • 実際には1レイヤーだけのswfなんて存在しない
  • 複数のShapeを組み合わせてつかう
  • Shapeを配置する為にSpriteとルートオブジェクトが持っているのがPlaceObject

PlaceObject

Depth 配置位置
DisplayObjectContainer::addChildAt() のindexに当たる
またタイムラインの位置(小さいほど奥)
CharacterId Define*を指し示すID
Matrix 配置、拡大・縮小、回転
ColorTransform 色の変形
Name インスタンス名(PlaceObject2)
Ratio Morphingの比率(PlaceObject2)
Cripp* Crippingに関する情報(PlaceObject2)

PlaceObject

ActionSCriptの対応

CharacterId Define*.characterIdを取り出す
Depth DisplayObjectContainer.addChildAt
Matrix DisplayObject.transform.matrix
ColorTransform DisplayObject.transform.colorTransform

少し複雑なベクターをASにしてみよう

  • パーツ毎にシンボル化している
  • MovieCrip(Shape)の入れ子もある

ActionScript3.0での配置のしかた

DEFINESPRITE defines id 0002
         PLACEOBJECT2 places id 0001 at depth 0002
          | Matrix             
          | 1.000 0.000 -57.75 
          | 0.000 1.000  15.30 
         SHOWFRAME 1 (00:00:00,000)
         END
PLACEOBJECT2 places id 0002 at depth 0001 name "ar"
 | Matrix             
 | 1.000 0.000 157.20 
 | 0.000 1.000 141.35 
var shape_0001:Shape = create_shape_0002();
shape_0001.transform.matrix = new Matrix(1, 0, 0, 1, -57.75, 15.30);

var sprite_0002:Sprite = new Sprite();
sprite_0002.addChildAt(shape_0001, 2);

sprite_0002.transform.matrix = new Matrix(1, 0, 0, 1, 157.20, 141.35);
addChildAt(sprite_0002, 1);

Gradient

FillStyleTypeによる違い

FillStyleType グラデーションタイプ
0x10 Liner
0x12 Radial

Gradient

SpreadMode

0 = Pad
1 = Reflect
2 = Repeat

Gradient

InterpolationMode

0 = Normal RGB
1 = Linear RGB

Gradient

GradientRecords

GradientRecord(control point)の配列

GradientRecord[0]
Ratio 0-255までの位置
Color RGB or RGBA (Shapeによる)
{ Ratio = 0, Color = #FFF }
GradientRecord[2] { Ratio = 255, Color = #000 }

Gradient

虹の場合

FillStyleType 0x10 = Liner
SpreadMode 0 = Pad
InterpolationMode 0 = Normal RGB
GradientRecords
ratio color
0 #ff0000
42 #ffff00
93 #00ff00
127 #00ffff
170 #0000ff
212 #ff00ff
255 #ff0000

Gradient

ActionScript3.0の対応

// 虹の場合
public function draw(): void {
  var shape: Shape = new Shape();
  shape.graphics.lineStyle(1, 0x000000);

  var matrix: Matrix = new Matrix();
  matrix.createGradientBox(200, 200);
  shape.graphics.beginGradientFill(
    'linear',  // type
    [0xff0000, 0xffff00, 0x00ff00, 0x00ffff, 0x0000ff, 0xff00ff, 0xff0000], // colors
    [1.0,      1.0,      1.0,      1.0,      1.0,      1.0,      1.0     ], // alphas
    [0,        42,       93,       127,      170,      212,      255     ], // ratios
    matrix,  // GradientMatrix
    'pad', // spreadMethod 
    'rgb'  // interpolationMethod 
  );
  shape.graphics.drawRect( 0, 0, 200, 200 );

  shape.x = 20;
  shape.y = 20;

  addChild(shape);
}
        

Gradient

JavaScript(Canvas)の場合

// 虹の場合
onload = function() {
  var canvas = document.getElementById('shape');
  var ctx    = canvas.getContext('2d');

  ctx.beginPath();
  var grad  = ctx.createLinearGradient(20,0, 220,0);
  grad.addColorStop(0   / 255, '#ff0000');
  grad.addColorStop(42  / 255, '#ffff00');
  grad.addColorStop(93  / 255, '#00ff00');
  grad.addColorStop(127 / 255, '#00ffff');
  grad.addColorStop(170 / 255, '#0000ff');
  grad.addColorStop(212 / 255, '#ff00ff');
  grad.addColorStop(255 / 255, '#ff0000');

  ctx.strokeStyle = '#000000';
  ctx.lineWidth = 1;
  ctx.fillStyle = grad;
  ctx.rect(20,20,200,200);
  ctx.fill();
  ctx.stroke();
};
        

Bitmap

説明しきれなくてごめんなさい

簡単なSWFをSVGにコンバートしてみよう

利用するSVGのタグ

defs オブジェクト定義
g グループ化
<g id="内部参照可能な名前" transform="matrix(Matrix)" >
path
<path d="パスの情報" fill="塗り" 
      stroke="線の色" stroke-width="線の太さ" viewBox="矩形" >
moveTo M x y
lineTo L x y
curveTo Q cx cy ax ay
use
<use xlink:href="#内部参照" transform="matrix(Matrix)" >
linearGradient
radialGradient
<linearGradient id="g_1_0" spreadMethod="pad">
  <stop stop-color="#ffffff" offset="0%"/>
  <stop stop-color="#000000" offset="100%"/>
</linearGradient>

簡単なSWFをコンバートしてみた

Thank you!

sample code and this slide