René Nyffenegger's collection of things on the web
René Nyffenegger on Oracle - Most wanted - Feedback -
 

First steps with JGenerator

One of the core components of Open Laszlo is JGenerator. JGenerator is written in Java and allows to dynamically create Flash movies. I have already written about Laszlo here: Taking a look at Laszlo and here: Using the Laszlo framework as a standalone application. In this article, I want to show how it is possible to use JGenerator to create some flash movies.

Getting JGenerator

First, JGenerator must be downloaded. It can be found on source forge.
I unzipped the file into a directory named C:\Rene. After unzipping the file, the following structure exists: C:\rene\jgenerator-2.2-src\jgenerator-2.2. All important files are below this directory. This is also the directory in which a new directory named lib (C:\rene\jgenerator-2.2-src\jgenerator-2.2\lib). must be created and in which the build.xml and build.properties files will be copied.
The lib directory must be filled with some jar files that are additionally needed to build JGenerator. They are found here (also on source forge). The following files are needed:
  • commons-jexl-1.0-dev.jar
  • fop-0.20.1.jar
  • ftp.jar
  • js.jar
  • junit.jar
  • log4j.jar
  • resin.jar
Lastly, build.xml and build.properties must be gotten as well. They go into the same directory in which JGenerator was unzipped (C:\rene\jgenerator-2.2-src\jgenerator-2.2\build.xml and C:\rene\jgenerator-2.2-src\jgenerator-2.2\build.properties).

Building JGenerator

We can now invoke ant to build the JGenerator jar file:
c:\rene\jgenerator-2.2-src\jgenerator-2.2> ant jar
This command will create a jgen.jar file which is found under the lib directory (C:\rene\jgenerator-2.2-src\jgenerator-2.2\lib).
Note, it is also possible to download that jar file directly from source forge if you want to forego the hassle of compilin JGenerator. It can be downloaded from here).

Examples

A one frame movie with one shape

This example creates a very simple flash move, one that contains one frame only and this frame contains a basic shape.
The frame is created with newFrame().
OneFrameOneShape.java
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;

import java.awt.geom.Rectangle2D;
import java.awt.geom.AffineTransform;

import java.lang.String;

import com.iv.flash.api.FlashFile;
import com.iv.flash.api.Color;
import com.iv.flash.api.Script;
import com.iv.flash.api.Frame;
//import com.iv.flash.api.SetBackgroundColor;

import com.iv.flash.api.shape.Shape;
import com.iv.flash.api.shape.FillStyle;
import com.iv.flash.api.shape.LineStyle;
import com.iv.flash.util.GeomHelper;
import com.iv.flash.util.FlashOutput;

class OneFrameOneShape {
  public static void main(String[] argv) {
    FlashFile flash_file  = new FlashFile(); // FlashFile.newFlashFile();
    Script    main_script = new Script(1);
    Shape     some_shape  = new Shape();
    FillStyle fill_style  = new FillStyle();
    LineStyle line_style  = new LineStyle();

    flash_file.setVersion(6);
    flash_file.setFrameSize(GeomHelper.newRectangle(0, 0, 400*20, 400*20));

    main_script.setMain();
    flash_file.setMainScript(main_script);

    //Color bgColor=new Color(77,77,77);
    //SetBackgroundColor sbgColor=new SetBackgroundColor(bgColor);
    //main_script.setBackgroundColor(sbgColor);

    fill_style.setColor(new Color(255,127,0));

    line_style.setColor(new Color(0,255,127));
    line_style.setWidth(5*20);

    some_shape.setFillStyle0(fill_style);
    some_shape.setLineStyle (line_style);

    Rectangle2D movie_rect = GeomHelper.newRectangle(0, 0, 150*20, 200*20);
    Rectangle2D shape_rect = GeomHelper.newRectangle(50*20, 100*20, 150*20, 200*20);
    some_shape.drawRectangle(shape_rect);
    some_shape.setBounds    (shape_rect);

    Frame the_only_frame = main_script.newFrame();
    the_only_frame.addInstance(some_shape, 1, new AffineTransform(), null);

    try {
        FlashOutput flash_output = flash_file.generate();
        BufferedOutputStream bufd_str = new BufferedOutputStream(new FileOutputStream("OneFrameOneShape.swf"));
        bufd_str.write( flash_output.getBuf(), 0, flash_output.getSize() );
        bufd_str.close();
    } catch( Exception e ) {
      e.printStackTrace();
    }
  }
}
This file can be compiled like so:
C:\my\path> set CLASSPATH=.;c:\rene\jgenerator-2.2-src\jgenerator-2.2\lib\jgen.jar
C:\my\path> javac OneFrameOneShape.java
It is then executed like so:
C:\my\path> java OneFrameOneShape
This creates OneFrameOneShape.swf, the Flash movie.
The swf file can be viewed in a browser with the following snippet:
<object  classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
  codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0"
  id    ="some_id"
  width ="400"
  height="400"
>
 <param name="movie"         value="OneFrameOneShape.swf">
 <param name="quality"       value="high">
 <embed name="JGTest01"      src="OneFrameOneShape.swf"
        quality="high"       type="application/x-shockwave-flash"
  pluginspage="http://www.macromedia.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash">
 </embed>
 <noembed>
   Flash plugin not installed.
 </noembed>
</object>

Setting the Background color

This example creates a one frame movie in which only the background color is set with setBackgroundColor.
BGColor.java
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;

import java.lang.String;

import com.iv.flash.api.*;
import com.iv.flash.api.shape.*;
import com.iv.flash.util.*;

import java.awt.geom.AffineTransform;

public class BGColor {
  public static void main(String[] argv) {
    FlashFile flash_file  = new FlashFile();
    Script    main_script = new Script(1);

    flash_file.setVersion(6);
    flash_file.setFrameSize(GeomHelper.newRectangle(0, 0, 400*20, 400*20));

    main_script.setMain();
    flash_file.setMainScript(main_script);

    Color bgColor=new Color(77,77,77);
    SetBackgroundColor sbgColor=new SetBackgroundColor(bgColor);
    main_script.setBackgroundColor(sbgColor);

    Frame the_only_frame = main_script.newFrame();

    try {
        FlashOutput flash_output = flash_file.generate();
        BufferedOutputStream bufd_str = new BufferedOutputStream(new FileOutputStream("BGColor.swf"));
        bufd_str.write( flash_output.getBuf(), 0, flash_output.getSize() );
        bufd_str.close();
    } catch( Exception e ) {
      e.printStackTrace();
    }
  }
}

A wrapper class for FlashFile

Some common functionaly can be placed in a wrapper class.
FlashFileWrapper.java
import com.iv.flash.api.Script;
import com.iv.flash.api.FlashFile;
import com.iv.flash.util.GeomHelper;
import com.iv.flash.util.FlashOutput;

import java.awt.geom.Rectangle2D;

import java.lang.String;

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;

class FlashFileWrapper {
  
  public FlashFileWrapper(int width, int height, int version) {
    flash_file_  = new FlashFile(); // FlashFile.newFlashFile();
    flash_file_.setVersion(version);
    flash_file_.setFrameSize(GeomHelper.newRectangle(0, 0, width*20, height*20));
  }

  Script createMainScript(int frames) {
    Script main_script = new Script(frames);
    main_script.setMain();
    flash_file_.setMainScript(main_script);
    return main_script;
  }

  public void SaveAs(String filename) {
    try {
        FlashOutput flash_output = flash_file_.generate();
        BufferedOutputStream bufd_str = new BufferedOutputStream(new FileOutputStream(filename));
        bufd_str.write( flash_output.getBuf(), 0, flash_output.getSize() );
        bufd_str.close();
    } catch( Exception e ) {
      e.printStackTrace();
    }
  }

  public FlashFile flash_file_=null;
}

A one frame movie with a picture

The following program reads a jpg (pic.jpg, which must exist) and places it into the only frame.
FrameWithPicture.java
import java.awt.geom.Rectangle2D;
import java.awt.geom.AffineTransform;

import java.lang.String;

import com.iv.flash.api.*;

//import com.iv.flash.api.SetBackgroundColor;

import com.iv.flash.api.image.Bitmap;

class FrameWithPicture {
  public static void main(String[] argv) throws 
    com.iv.flash.util.IVException,
    java.io.IOException
  {
    int width =400;
    int height=400;

    FlashFileWrapper flash_file = new FlashFileWrapper(width, height, 6);

    Script main_script=flash_file.createMainScript(1);

    Bitmap   pic      = Bitmap.newBitmap("pic.jpg");
    Instance pic_inst = pic.newInstance();

    Rectangle2D pic_bounds = pic_inst.getBounds();
    double pic_width =pic_bounds.getWidth ();
    double pic_height=pic_bounds.getHeight();

    pic_inst.matrix=new AffineTransform();
    pic_inst.matrix.translate( 
      20* (width -pic_width /20) / 2, 
      20* (height-pic_height/20) / 2);

    Frame frame = main_script.newFrame();

    frame.addInstance(pic_inst, 1); // 1 being layer

    flash_file.SaveAs("FrameWithPicture.swf");
  }
}

Text

If one wants to use a TTF (true type font) in a flash movie, the true type font must first be converted into a FFT (flash font format). In order to run this example, a font must exist in the flash font format (or fft). Refer to TTF2FFT.java for a program and a description on how to convert true type fonts (ttf) into fft's.
The font is loaded with FontDef.load.
FrameWithText.java
import java.awt.geom.Rectangle2D;
import java.awt.geom.AffineTransform;

import java.lang.String;

import com.iv.flash.api.*;
import com.iv.flash.api.text.*;

public class FrameWithText {
  public static void main(String[] argv) throws 
    com.iv.flash.util.IVException,
    java.io.IOException
  {
    int width =400;
    int height=400;

    FlashFileWrapper flash_file = new FlashFileWrapper(width, height, 6);

    Script main_script=flash_file.createMainScript(1);

    AlphaColor txtColor=new AlphaColor(0,255,77,127);

    Font font_century = FontDef.load("century.fft", flash_file.flash_file_);
    Text text = Text.newText();  // Also sets member matrix = new AffineTransform()
    TextItem textItem=new TextItem("abcd", font_century, 36*20, txtColor);
    text.addTextItem(textItem);
    text.setBounds(50*20, 50*20, 300*20, 300*20);

    Frame frame = main_script.newFrame();

    AffineTransform m = AffineTransform.getRotateInstance(1);
    m.translate(200*20, 15*20);

    frame.addInstance(text, 1, m, null); // 1 being layer

    flash_file.SaveAs("FrameWithText.swf");
  }
}

Multiple Frames

The following example creates 100 frames and writes the frame number into each frame.
There can be at most one character at a given depth. Therefore, the last character must be removed (removeInstance) before a new one can be placed in a frame.
Counter.java
import java.awt.geom.*;
import java.awt.Rectangle;

import java.lang.String;

import com.iv.flash.api.*;
import com.iv.flash.api.button.*;
import com.iv.flash.api.shape.*;
import com.iv.flash.api.text.*;
import com.iv.flash.api.action.*;

public class Counter {
  public static void main(String[] argv) throws 
    com.iv.flash.util.IVException,
    java.io.IOException
  {
    int width =400;
    int height=400;
    int nof_frames=100;

    FlashFileWrapper flash_file = new FlashFileWrapper(width, height, 6);
    flash_file.flash_file_.setFrameRate(10 << 8);

    Script main_script=flash_file.createMainScript(1);

    Font     f =FontDef.load("verdana.fft");
    for (int i_mc=0; i_mc<nof_frames; i_mc++) {
      Text     t =Text.newText();
      TextItem ti=new TextItem(
        Integer.toString(i_mc), f, 20*20, new AlphaColor(200, 20, 20, 255)
      );
      t.addTextItem(ti);
      t.setBounds  (0, 0, 300*20, 300*20);

      Frame frame = main_script.newFrame();
      frame.removeInstance(1);
      frame.addInstance(t, 1, new AffineTransform(), null);
    }

    flash_file.SaveAs("Counter.swf");
  }
}

A button

This example creates two frames. The first frame (as well as the second) has a stop action so that it doesn't automatically advance to the second frame. When the button is clicked, it advances to the second frame.
A button consists of four shapes. These define how the button looks like when the mouse is not over the button (ButtonRecord.Up, when it is over the button (ButtonRecord.Over), when the button is being pressed (ButtonRecord.Over) and the size of the button (ButtonRecord.Hit).
ButtonGoto.java
import java.awt.geom.*;
import java.awt.Rectangle;

import java.lang.String;

import com.iv.flash.api.*;
import com.iv.flash.api.button.*;
import com.iv.flash.api.shape.*;
import com.iv.flash.api.text.*;
import com.iv.flash.api.action.*;

public class ButtonGoto {
  public static void main(String[] argv) throws 
    com.iv.flash.util.IVException,
    java.io.IOException
  {
    int width =400;
    int height=400;

    FlashFileWrapper flash_file = new FlashFileWrapper(width, height, 6);

    Script main_script=flash_file.createMainScript(3);
    flash_file.flash_file_.setFrameRate(1 << 8);

    Shape s = new Shape();
    s.setLineStyle(
      new LineStyle(2*20, new Color(0,0,0))
    );

    s.setFillStyle0(
      FillStyle.newSolid(new Color(20,20,140))
    );

    Rectangle2D r = new Rectangle(0, 0, 100*20, 30*20);
    s.drawRectangle(r);
    s.setBounds    (r);

    Font     f =FontDef.load("verdana.fft");
    Text     t =Text.newText();
    TextItem ti=new TextItem(
      "Click me", f, 20*20, new AlphaColor(200, 20, 20)
    );
    t.addTextItem(ti);
    t.setBounds  (r);

    AffineTransform tr_0 = AffineTransform.getTranslateInstance(90*20, 50*20);
    CXForm          br_0 = CXForm.newIdentity(true);
    CXForm          br_1 = CXForm.newBrightness(0.2, true);

    ButtonRecord br_up_s = new ButtonRecord(ButtonRecord.Up, s, 1, tr_0, br_0);
    ButtonRecord br_up_t = new ButtonRecord(ButtonRecord.Up, t, 2, tr_0, br_0);

    ButtonRecord br_dn_s = new ButtonRecord(ButtonRecord.Down | ButtonRecord.Over, s, 1, tr_0, br_1);
    ButtonRecord br_dn_t = new ButtonRecord(ButtonRecord.Down | ButtonRecord.Over, t, 2, tr_0, br_1);

    ButtonRecord br_ht_s = new ButtonRecord(ButtonRecord.HitTest, s, 1, tr_0, br_0);

    Button2 btn = new Button2();

    btn.addButtonRecord(br_up_s);
    btn.addButtonRecord(br_up_t);

    btn.addButtonRecord(br_dn_s);
    btn.addButtonRecord(br_dn_t);

    btn.addButtonRecord(br_ht_s);

    Program prg = new Program();
    prg.gotoFrame(1);
    prg.play();

    ActionCondition ac=new ActionCondition(
      ActionCondition.OverDownToOverUp, 
      prg
    );

    btn.addActionCondition(ac);

    Text text2 = Text.newText();  // Also sets member matrix = new AffineTransform()
    TextItem textItem2=new TextItem("Frame 2", f, 36*20, new AlphaColor(30, 90, 210));
    text2.addTextItem(textItem2);
    text2.setBounds(0*20, 0*20, 300*20, 300*20);

    Frame frame1 = main_script.newFrame();
    Frame frame2 = main_script.newFrame();

    frame1.addInstance(btn, 1, null, null);

    frame2.removeInstance(1);
    frame2.addInstance(text2, 2, new AffineTransform(), null);

    frame1.addStopAction();
    frame2.addStopAction();

    flash_file.SaveAs("ButtonGoto.swf");
  }
}

A shape

This example has again only one frame on which a curved shape is drawn.
CurvedShape.java
import java.awt.geom.*;
import java.awt.Rectangle;

import java.lang.String;

import com.iv.flash.api.*;
import com.iv.flash.util.*;
import com.iv.flash.api.shape.*;

public class CurvedShape {
  public static void main(String[] argv) throws 
    com.iv.flash.util.IVException,
    java.io.IOException
  {
    int width =400;
    int height=400;
    int nof_frames=100;

    FlashFileWrapper flash_file = new FlashFileWrapper(width, height, 6);
    flash_file.flash_file_.setFrameRate(10 << 8);

    Script main_script=flash_file.createMainScript(1);

    Shape sh = new Shape();

    sh.setLineStyle(new LineStyle(3*20, new AlphaColor(255, 0, 0, 255)));

    sh.drawCurve  (0, 0, 0, 100*20, 100*20, 100*20);
    sh.drawCurveTo(200*20, 100*20,150*20, 200*20);

    Rectangle2D sh_bnd = GeomHelper.newRectangle(0*20, 0*20, 400*20, 400*20);
    sh.setBounds(sh_bnd);

    Frame frame = main_script.newFrame();

    frame.addInstance(sh, 1, AffineTransform.getTranslateInstance(00*20,00*20), null);

    flash_file.SaveAs("CurvedShape.swf");
  }
}

Terminology

JGenerator uses a slightly different jargon from Macromedia's.
A FlashDef, or its inherited classes (Shape, Script, Sound, ...) are called characters in Macromedia Lingo. Such characters can be reused. A definition tag (DefineSpriteTag) is inserted into an SWF movie when a character is defined. Control tags, on the other hand, tell a movie if a character is displayed or hidden.
A character has a depth (or layer) assigned to it which specifies if a character is drawn above or underneath an other character. The lower the layer, the lower it is drawn. The lowest value is 1. No two characters share the same depth in a display list.
A Movie Clip or Movie Sprite in Macromedia's Lingo is represented by the Script class of JGenerator. Every Flash movie has at least on Script: the main or root script.
A set of characters is called a library in a flash movie.
A Timeline can stop, start and play. Frames are placed on a Timeline. A Timeline is fundamental for Scripts.
All objects to be drawn must be placed in a Display list
The Instance class represents the flash file format's PlaceObject2 and PlaceObject tags. PlaceObject2 can add a character (class FlashDef) to a display list, or it can modify the attributes of a character that is already on the display list.
The CXForm class applies color effects to a character.