Friday 31 March 2017

Source Code Overview - Java Tutorials ( Page 2 of 4 )

BillData.java

The BillData class is mostly just a data structure for encapsulating attributes associated with individual billboards. It contains three variables. The first variable stores the URL to which the billboard is a link. The second variable has an Image that the applet uses to draw on the screen. The third variable includes a pixel array of the image in RGB format.

The pixel array is used by transitions in combination with another BillData pixel array to create the cells for transition animation. The array is only one-dimensional. The pixels in it are arranged in such a way that the first element in this array is the top-left corner of the image. The second element is the pixel just to the right of this corner. Elements that follow are the pixels to the right of this one, and so on, until the rightmost pixel is reached. Then the leftmost pixel on the next line of the image is used. This continues until the last index in the array, which corresponds to the pixel on the bottom-right corner of the image.

You might notice that Robert has made all of the variables in this class public. Normally, it is good programming practice to hide the data members that should be read only by other classes. This is done by making them protected or private and then creating functions to return references to the variables. Unfortunately, in Java this increases the size of the compiled bytecode even when the one line function is made final and the code is compiled with optimizations. So to make the applet smaller and hence faster to download, Robert made the data members public.

The Constructor
The constructor for a BillData object simply initializes the URL and Image variables with the two parameters passed in. Initializing the pixel array is done in a separate method, because it is very processor-intensive. This gives the applet a chance to initialize the pixel array only when it needs it.

initPixels( )
The initPixels( ) method creates the pixel array from the image using the Java core class: java.awt.image.PixelGrabber.

The Code
Here is the source code for the BillData class:

  import java.net.*;
  import java.awt.*;
  import java.awt.image.*;

  public class BillData {
    public URL link;
    public Image image;
    public int[] image_pixels;

    public BillData(URL link, Image image) {
      this.link = link;
      this.image = image;
    }

    public void initPixels(int image_width, int image_height) {
      image_pixels = new int[image_width * image_height];
      PixelGrabber pixel_grabber = new
      PixelGrabber(image.getSource(), 0, 0,
           image_width, image_height, image_pixels, 0, image_width);
      try {
        pixel_grabber.grabPixels();
      }
      catch (InterruptedException e) {
        image_pixels = null;
      }
    }
  }


BillTransition.java

The BillTransition class is used as a base class for other transition classes. These other classes create transition cells between two individual billboard images. This abstract class contains variables and methods that are common to all transitions. 

There are no constructors provided in the BillTransition class. This is because the applet does not use “new” to create new instances and instead uses the factory method, java.lang.Class.newInstance( ). Objects created in this way have no way to directly initialize themselves using parameters in constructors. This factory method indirectly creates objects using a default constructor, one without any parameters. The BillTransition class provides a number of overloaded init( ) methods to initialize
instances with parameters.

In previous versions of DynamicBillboard, Robert used static variables within different transition classes to store data that only needed to be initialized once. It was discovered, however, that when more than one instance of the applet ran from a web server, the applets would share the static variables. This led to some problems if one applet needed a different static value than the other when the applets were different sizes. An example is the FadeTransition class used to create an array whose size depended on the dimensions of the applet. When another DynamicBillboard was created with dimensions that were smaller than the previous applet, it would overwrite this array with an array too small for the first applet. This would cause the first applet to crash.

Robert introduced the static hash table called object_table in this version of the applet to work around this problem. Now transition classes can store data inside this hash table using the transition name in conjunction with the applet size as a key. When this data needs to be used, the applet can look to see if it exists for the applet’s size within the hash table. If it does not, then it can create the data and store it in the hash table for later use. Now if there is more than one applet on a web server and both are the same size, then only one has to initialize the data.

init( )
The init( ) method is overloaded three times. The first method, which has three parameters, is abstract and must be overridden by classes derived from this class. The other two methods initialize data members within this class. Robert’s intention was to have the init( ) method of classes that are derived from this class call one of these two methods to initialize data members of BillTransition.

createCellFromWorkPixels( )
The createCellFromWorkPixels( ) method is used to perform the common task of converting the work_pixels array into an Image object. Notice that it uses the owner variable to complete this task. This is the only reason the owner variable is needed by transition classes. When a transition has completed assembly of a new cell in the work_pixels array, it should call this method.

The Code
Here is the source code for the BillTransition class:

  import java.util.*;
  import java.awt.*;
  import java.awt.image.*;

  public abstract class BillTransition {
    static Hashtable object_table = new Hashtable(20);

    public Image[] cells;
    public int delay;

    Component owner;
    int cell_w;
    int cell_h;
    int pixels_per_cell;
    int[] current_pixels;
    int[] next_pixels;
    int[] work_pixels;

    public abstract void
    init(Component owner, int[] current_pixels, int[] next_pixels);

    final protected void
    init(Component owner, int[] current_pixels, int[] next_pixels,
                          int number_of_cells, int delay) {
      this.delay = delay;
      this.next_pixels = next_pixels;
      this.current_pixels = current_pixels;
      this.owner = owner;

      cells = new Image[number_of_cells];
      cell_w = owner.getSize().width;
      cell_h = owner.getSize().height;
      pixels_per_cell = cell_w * cell_h;
      work_pixels = new int[pixels_per_cell];
    }

    final protected void
    init(Component owner, int[] current_pixels, int[] next_pixels,
                          int number_of_cells) {
     init(owner, current_pixels, next_pixels, number_of_cells, 120);
    }

    final void createCellFromWorkPixels(int cell) {
      cells[cell] = owner.createImage(
        new MemoryImageSource(cell_w, cell_h,
                              work_pixels, 0, cell_w));
      owner.prepareImage(cells[cell], null);
    }
  }


ColumnTransition.java

The ColumnTransition class changes one image into another by drawing increasingly large columns of the new image onto the old image. The column sizes increase to the left, and the same pixels are always drawn on the left side of each column. This makes the billboard appear to be sliding in from behind the old billboard through vertical slots in the current billboard.

To create the cells for this transition, the billboard space is split up into a number of columns, each column 24 pixels wide. Each of the seven image cells the transition will create will have pixels on the left side of each column from the old image and pixels on the right side from the new image. The first cell that is created starts out with only the three right pixels in each column taken from the new image. With each successive cell, three more pixels are filled in from the new image. The last cell has only the three left pixels in each column from the old image.

Because the width of the image space is most likely not perfectly divisible by 24, there will be some remaining pixels remaining on the right side of the image. These pixels are accounted for in each cell with the rightmost_columns_max_width and rightmost_columns_x_start variables.

init( )
The init( ) function starts by calling the base class’ init( ) method to initialize the variables contained within this base class. It goes on to initialize the variables associated with the rightmost column and then copies all of the pixels from the current billboard into the work pixels. The loop that follows creates all of the cell frames.

The nextCell( ) method changes work_pixels, and the method inherited from the BillTransition class, createCellFromWorkPixels( ), is used to convert these pixels into an image. Because the process of creating the cells can be very demanding on the CPU, Robert tells the thread to sleep occasionally to allow other threads to run.

nextCell( )
The nextCell( ) method modifies the work_pixels array for the next cell. It loops through each line of the image starting from the bottom line and fills part of each column by copying pixels from the next billboard onto the work_pixels array. It does not ever need to copy pixels from the old billboard, because these were already copied to the array in the init( ) method.

It’s worth repeating that the pixel arrays used to form the images are only one-dimensional. Every width pixel represents one horizontal line of the image.

The Code
Here is the source code for the ColumnTransition class:

  import java.awt.*;
  import java.awt.image.*;

  public class ColumnTransition extends BillTransition {
    final static int CELLS = 7;
    final static int WIDTH_INCREMENT = 3;
    final static int MAX_COLUMN_WIDTH = 24;

    int rightmost_columns_max_width;
    int rightmost_columns_x_start;
    int column_width = WIDTH_INCREMENT;

    public void init(Component owner, int[] current, int[] next) {
      init(owner, current, next, CELLS, 200);

      rightmost_columns_max_width = cell_w % MAX_COLUMN_WIDTH;
      rightmost_columns_x_start = cell_w - rightmost_columns_max
                                                            _ width;

      System.arraycopy(current_pixels, 0,
                       work_pixels, 0, pixels_per_cell);

      for(int c = 0; c < CELLS; ++c) {
        try { Thread.sleep(100); } catch (InterruptedException e) {}
        NextCell();
        try { Thread.sleep(100); } catch (InterruptedException e) {}
        createCellFromWorkPixels(c);
        column_width += WIDTH_INCREMENT;
      }
      work_pixels = null;
    }

    void NextCell() {
      int old_column_width = MAX_COLUMN_WIDTH - column_width;
      for(int p = pixels_per_cell - cell_w; p >= 0; p -= cell_w) {
        for (int x = 0; x < rightmost_columns_x_start; x +=
             MAX_COLUMN_WIDTH) {
          System.arraycopy(next_pixels, x + p, work_pixels,
                old_column_width + x + p, column_width);
        }
        if(old_column_width <= rightmost_columns_max_width) {
          System.arraycopy(next_pixels, rightmost_columns_x_start + 
                        p, work_pixels, rightmost_columns_x_start +
                                        old_column_width + p - 1,
                                       rightmost_columns_max_width -
                                       old_column_width + 1);
        }
      }
    }
  }

No comments:

Post a Comment