1 /*
   2  *
   3  * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
   4  *
   5  * Redistribution and use in source and binary forms, with or without
   6  * modification, are permitted provided that the following conditions
   7  * are met:
   8  *
   9  *   - Redistributions of source code must retain the above copyright
  10  *     notice, this list of conditions and the following disclaimer.
  11  *
  12  *   - Redistributions in binary form must reproduce the above copyright
  13  *     notice, this list of conditions and the following disclaimer in the
  14  *     documentation and/or other materials provided with the distribution.
  15  *
  16  *   - Neither the name of Oracle nor the names of its
  17  *     contributors may be used to endorse or promote products derived
  18  *     from this software without specific prior written permission.
  19  *
  20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  21  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  22  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31  */
  32 package java2d;
  33 
  34 
  35 import static java.awt.Color.BLACK;
  36 import static java.awt.Color.GRAY;
  37 import static java.awt.Color.RED;
  38 import static java.awt.Color.WHITE;
  39 import static java.awt.Color.YELLOW;
  40 import java.awt.AlphaComposite;
  41 import java.awt.BorderLayout;
  42 import java.awt.Color;
  43 import java.awt.Composite;
  44 import java.awt.Dimension;
  45 import java.awt.Font;
  46 import java.awt.FontMetrics;
  47 import java.awt.GradientPaint;
  48 import java.awt.Graphics;
  49 import java.awt.Graphics2D;
  50 import java.awt.Image;
  51 import java.awt.Paint;
  52 import java.awt.Point;
  53 import java.awt.Rectangle;
  54 import java.awt.RenderingHints;
  55 import java.awt.Shape;
  56 import java.awt.TexturePaint;
  57 import java.awt.Toolkit;
  58 import java.awt.event.ActionEvent;
  59 import java.awt.event.ActionListener;
  60 import java.awt.event.MouseAdapter;
  61 import java.awt.event.MouseEvent;
  62 import java.awt.event.WindowAdapter;
  63 import java.awt.event.WindowEvent;
  64 import java.awt.event.WindowListener;
  65 import java.awt.font.FontRenderContext;
  66 import java.awt.font.TextLayout;
  67 import java.awt.geom.AffineTransform;
  68 import java.awt.geom.Arc2D;
  69 import java.awt.geom.Ellipse2D;
  70 import java.awt.geom.FlatteningPathIterator;
  71 import java.awt.geom.GeneralPath;
  72 import java.awt.geom.Line2D;
  73 import java.awt.geom.PathIterator;
  74 import java.awt.geom.Point2D;
  75 import java.awt.geom.Rectangle2D;
  76 import java.awt.image.BufferedImage;
  77 import java.util.ArrayList;
  78 import java.util.Arrays;
  79 import java.util.List;
  80 import javax.swing.JButton;
  81 import javax.swing.JFrame;
  82 import javax.swing.JPanel;
  83 import javax.swing.JScrollPane;
  84 import javax.swing.JSlider;
  85 import javax.swing.JTable;
  86 import javax.swing.border.BevelBorder;
  87 import javax.swing.border.CompoundBorder;
  88 import javax.swing.border.EmptyBorder;
  89 import javax.swing.border.EtchedBorder;
  90 import javax.swing.border.TitledBorder;
  91 import javax.swing.event.ChangeEvent;
  92 import javax.swing.event.ChangeListener;
  93 import javax.swing.event.TableModelEvent;
  94 import javax.swing.table.AbstractTableModel;
  95 import javax.swing.table.TableColumn;
  96 import javax.swing.table.TableModel;
  97 
  98 
  99 /**
 100  * Introduction to the J2Ddemo.
 101  *
 102  * @author Brian Lichtenwalter
 103  * @author Alexander Kouznetsov
 104  */
 105 @SuppressWarnings("serial")
 106 public class Intro extends JPanel {
 107 
 108     private static final Color myBlack = new Color(20, 20, 20);
 109     private static final Color myWhite = new Color(240, 240, 255);
 110     private static final Color myRed = new Color(149, 43, 42);
 111     private static final Color myBlue = new Color(94, 105, 176);
 112     private static final Color myYellow = new Color(255, 255, 140);
 113     private ScenesTable scenesTable;
 114     private boolean doTable;
 115     private final Surface surface;
 116 
 117     public Intro() {
 118         EmptyBorder eb = new EmptyBorder(80, 110, 80, 110);
 119         BevelBorder bb = new BevelBorder(BevelBorder.LOWERED);
 120         setBorder(new CompoundBorder(eb, bb));
 121         setLayout(new BorderLayout());
 122         setBackground(GRAY);
 123         setToolTipText("click for scene table");
 124         add(surface = new Surface());
 125         addMouseListener(new MouseAdapter() {
 126 
 127             @Override
 128             public void mouseClicked(MouseEvent e) {
 129                 removeAll();
 130                 if ((doTable = !doTable)) {
 131                     setToolTipText("click for animation");
 132                     surface.stop();
 133                     if (scenesTable == null) {
 134                         scenesTable = new ScenesTable(Intro.this);
 135                     }
 136                     add(scenesTable);
 137                 } else {
 138                     setToolTipText("click for scene table");
 139                     surface.start();
 140                     add(surface);
 141                 }
 142                 revalidate();
 143                 repaint();
 144             }
 145         });
 146     }
 147 
 148     public void start() {
 149         if (!doTable) {
 150             surface.start();
 151         }
 152     }
 153 
 154     public void stop() {
 155         if (!doTable) {
 156             surface.stop();
 157         }
 158     }
 159 
 160     public static void main(String argv[]) {
 161         final Intro intro = new Intro();
 162         WindowListener l = new WindowAdapter() {
 163 
 164             @Override
 165             public void windowClosing(WindowEvent e) {
 166                 System.exit(0);
 167             }
 168 
 169             @Override
 170             public void windowDeiconified(WindowEvent e) {
 171                 intro.start();
 172             }
 173 
 174             @Override
 175             public void windowIconified(WindowEvent e) {
 176                 intro.stop();
 177             }
 178         };
 179         JFrame f = new JFrame("Java2D(TM) Demo - Intro");
 180         f.addWindowListener(l);
 181         f.getContentPane().add("Center", intro);
 182         f.pack();
 183         Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
 184         int w = 720;
 185         int h = 510;
 186         f.setLocation(screenSize.width / 2 - w / 2, screenSize.height / 2 - h
 187                 / 2);
 188         f.setSize(w, h);
 189         f.setVisible(true);
 190         intro.start();
 191     }
 192 
 193 
 194     /**
 195      * ScenesTable is the list of scenes known to the Director.
 196      * Scene participation, scene name and scene pause amount columns.
 197      * Global animation delay for scene's steps.
 198      */
 199     static class ScenesTable extends JPanel implements ActionListener,
 200             ChangeListener {
 201         private final Intro intro;
 202         private JTable table;
 203         private TableModel dataModel;
 204 
 205         @SuppressWarnings("LeakingThisInConstructor")
 206         public ScenesTable(final Intro intro) {
 207             this.intro = intro;
 208 
 209             setBackground(WHITE);
 210             setLayout(new BorderLayout());
 211             final String[] names = { "", "Scenes", "Pause" };
 212 
 213             dataModel = new AbstractTableModel() {
 214 
 215                 @Override
 216                 public int getColumnCount() {
 217                     return names.length;
 218                 }
 219 
 220                 @Override
 221                 public int getRowCount() {
 222                     return intro.surface.director.size();
 223                 }
 224 
 225                 @Override
 226                 public Object getValueAt(int row, int col) {
 227                     Surface.Scene scene = intro.surface.director.get(row);
 228                     if (col == 0) {
 229                         return scene.participate;
 230                     } else if (col == 1) {
 231                         return scene.name;
 232                     } else {
 233                         return scene.pauseAmt;
 234                     }
 235                 }
 236 
 237                 @Override
 238                 public String getColumnName(int col) {
 239                     return names[col];
 240                 }
 241 
 242                 @Override
 243                 public Class<?> getColumnClass(int c) {
 244                     return getValueAt(0, c).getClass();
 245                 }
 246 
 247                 @Override
 248                 public boolean isCellEditable(int row, int col) {
 249                     return col != 1 ? true : false;
 250                 }
 251 
 252                 @Override
 253                 public void setValueAt(Object aValue, int row, int col) {
 254                     Surface.Scene scene = intro.surface.director.get(row);
 255                     if (col == 0) {
 256                         scene.participate = aValue;
 257                     } else if (col == 1) {
 258                         scene.name = aValue;
 259                     } else {
 260                         scene.pauseAmt = aValue;
 261                     }
 262                 }
 263             };
 264 
 265             table = new JTable(dataModel);
 266             TableColumn col = table.getColumn("");
 267             col.setWidth(16);
 268             col.setMinWidth(16);
 269             col.setMaxWidth(20);
 270             col = table.getColumn("Pause");
 271             col.setWidth(60);
 272             col.setMinWidth(60);
 273             col.setMaxWidth(60);
 274             table.sizeColumnsToFit(0);
 275 
 276             JScrollPane scrollpane = new JScrollPane(table);
 277             add(scrollpane);
 278 
 279             JPanel panel = new JPanel(new BorderLayout());
 280             JButton b = new JButton("Unselect All");
 281             b.setHorizontalAlignment(JButton.LEFT);
 282             Font font = new Font(Font.SERIF, Font.PLAIN, 10);
 283             b.setFont(font);
 284             b.addActionListener(this);
 285             panel.add("West", b);
 286 
 287             JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, 200,
 288                     (int) intro.surface.sleepAmt);
 289             slider.addChangeListener(this);
 290             TitledBorder tb = new TitledBorder(new EtchedBorder());
 291             tb.setTitleFont(font);
 292             tb.setTitle("Anim delay = " + String.valueOf(intro.surface.sleepAmt)
 293                     + " ms");
 294             slider.setBorder(tb);
 295             slider.setPreferredSize(new Dimension(140, 40));
 296             slider.setMinimumSize(new Dimension(100, 40));
 297             slider.setMaximumSize(new Dimension(180, 40));
 298             panel.add("East", slider);
 299 
 300             add("South", panel);
 301         }
 302 
 303         @Override
 304         public void actionPerformed(ActionEvent e) {
 305             JButton b = (JButton) e.getSource();
 306             b.setSelected(!b.isSelected());
 307             b.setText(b.isSelected() ? "Select All" : "Unselect All");
 308             for (int i = 0; i < intro.surface.director.size(); i++) {
 309                 Surface.Scene scene = intro.surface.director.get(i);
 310                 scene.participate = Boolean.valueOf(!b.isSelected());
 311             }
 312             table.tableChanged(new TableModelEvent(dataModel));
 313         }
 314 
 315         @Override
 316         public void stateChanged(ChangeEvent e) {
 317             JSlider slider = (JSlider) e.getSource();
 318             int value = slider.getValue();
 319             TitledBorder tb = (TitledBorder) slider.getBorder();
 320             tb.setTitle("Anim delay = " + String.valueOf(value) + " ms");
 321             intro.surface.sleepAmt = (long) value;
 322             slider.repaint();
 323         }
 324     }  // End ScenesTable class
 325 
 326 
 327     /**
 328      * Surface is the stage where the Director plays its scenes.
 329      */
 330     static class Surface extends JPanel implements Runnable {
 331 
 332         private final Image dukeanim, duke;
 333         private BufferedImage bimg;
 334         public Director director;
 335         public int index;
 336         public long sleepAmt = 30;
 337         private Thread thread;
 338 
 339         @SuppressWarnings("LeakingThisInConstructor")
 340         public Surface() {
 341             setBackground(myBlack);
 342             setLayout(new BorderLayout());
 343             addMouseListener(new MouseAdapter() {
 344 
 345                 @Override
 346                 public void mouseClicked(MouseEvent e) {
 347                     if (thread == null) {
 348                         start();
 349                     } else {
 350                         stop();
 351                     }
 352                 }
 353             });
 354             dukeanim = DemoImages.getImage("duke.running.gif", this);
 355             duke = DemoImages.getImage("duke.png", this);
 356             director = new Director(this);
 357         }
 358 
 359         public FontMetrics getMetrics(Font font) {
 360             return getFontMetrics(font);
 361         }
 362 
 363         @Override
 364         public void paint(Graphics g) {
 365             Dimension d = getSize();
 366             if (d.width <= 0 || d.height <= 0) {
 367                 return;
 368             }
 369             if (bimg == null || bimg.getWidth() != d.width || bimg.getHeight()
 370                     != d.height) {
 371                 bimg = getGraphicsConfiguration().createCompatibleImage(d.width,
 372                         d.height);
 373                 // reset future scenes
 374                 for (int i = index + 1; i < director.size(); i++) {
 375                     (director.get(i)).reset(d.width, d.height);
 376                 }
 377             }
 378 
 379             Scene scene = director.get(index);
 380             if (scene.index <= scene.length) {
 381                 if (thread != null) {
 382                     scene.step(d.width, d.height);
 383                 }
 384 
 385                 Graphics2D g2 = bimg.createGraphics();
 386                 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
 387                         RenderingHints.VALUE_ANTIALIAS_ON);
 388                 g2.setBackground(getBackground());
 389                 g2.clearRect(0, 0, d.width, d.height);
 390 
 391                 scene.render(d.width, d.height, g2);
 392 
 393                 if (thread != null) {
 394                     // increment scene.index after scene.render
 395                     scene.index++;
 396                 }
 397                 g2.dispose();
 398             }
 399             g.drawImage(bimg, 0, 0, this);
 400         }
 401 
 402         public void start() {
 403             if (thread == null) {
 404                 thread = new Thread(this);
 405                 thread.setPriority(Thread.MIN_PRIORITY);
 406                 thread.setName("Intro");
 407                 thread.start();
 408             }
 409         }
 410 
 411         public synchronized void stop() {
 412             if (thread != null) {
 413                 thread.interrupt();
 414             }
 415             thread = null;
 416             notifyAll();
 417         }
 418 
 419         public void reset() {
 420             index = 0;
 421             Dimension d = getSize();
 422             for (Scene scene : director) {
 423                 scene.reset(d.width, d.height);
 424             }
 425         }
 426 
 427         @Override
 428         @SuppressWarnings("SleepWhileHoldingLock")
 429         public void run() {
 430 
 431             Thread me = Thread.currentThread();
 432 
 433             while (thread == me && !isShowing() || getSize().width <= 0) {
 434                 try {
 435                     Thread.sleep(500);
 436                 } catch (InterruptedException e) {
 437                     return;
 438                 }
 439             }
 440 
 441             if (index == 0) {
 442                 reset();
 443             }
 444 
 445             while (thread == me) {
 446                 Scene scene = director.get(index);
 447                 if (((Boolean) scene.participate).booleanValue()) {
 448                     repaint();
 449                     try {
 450                         Thread.sleep(sleepAmt);
 451                     } catch (InterruptedException e) {
 452                         break;
 453                     }
 454                     if (scene.index > scene.length) {
 455                         scene.pause();
 456                         if (++index >= director.size()) {
 457                             reset();
 458                         }
 459                     }
 460                 } else {
 461                     if (++index >= director.size()) {
 462                         reset();
 463                     }
 464                 }
 465             }
 466             thread = null;
 467         }
 468 
 469 
 470         /**
 471          * Part is a piece of the scene.  Classes must implement Part
 472          * in order to participate in a scene.
 473          */
 474         interface Part {
 475 
 476             public void reset(int newwidth, int newheight);
 477 
 478             public void step(int w, int h);
 479 
 480             public void render(int w, int h, Graphics2D g2);
 481 
 482             public int getBegin();
 483 
 484             public int getEnd();
 485         }
 486 
 487 
 488         /**
 489          * Director is the holder of the scenes, their names & pause amounts
 490          * between scenes.
 491          */
 492         static class Director extends ArrayList<Scene> {
 493 
 494             GradientPaint gp = new GradientPaint(0, 40, myBlue, 38, 2, myBlack);
 495             Font f1 = new Font(Font.SERIF, Font.PLAIN, 200);
 496             Font f2 = new Font(Font.SERIF, Font.PLAIN, 120);
 497             Font f3 = new Font(Font.SERIF, Font.PLAIN, 72);
 498 
 499             public Director(Surface surf) {
 500                 Object partsInfo[][][] = {
 501                 { { "J  -  scale text on gradient", "0" },
 502                     { new GpE(GpE.BURI, myBlack, myBlue, 0, 20),
 503                         new TxE("J", f1, TxE.SCI, myYellow, 2, 20) } },
 504                 { { "2  -  scale & rotate text on gradient", "0" },
 505                     { new GpE(GpE.BURI, myBlue, myBlack, 0, 22),
 506                         new TxE("2", f1, TxE.RI | TxE.SCI, myYellow, 2, 22) } },
 507                 { { "D  -  scale text on gradient", "0" },
 508                     { new GpE(GpE.BURI, myBlack, myBlue, 0, 20),
 509                         new TxE("D", f1, TxE.SCI, myYellow, 2, 20) } },
 510                 { { "J2D demo  -  scale & rotate text on gradient", "1000" },
 511                     { new GpE(GpE.SIH, myBlue, myBlack, 0, 40),
 512                         new TxE("J2D demo", f2, TxE.RI | TxE.SCI, myYellow, 0, 40) } },
 513                 { { "Previous scene dither dissolve out", "0" },
 514                     { new DdE(0, 20, 1, surf) } },
 515                 { { "Graphics Features", "999" },
 516                     { new Temp(Temp.RECT, null, 0, 15),
 517                         new Temp(Temp.IMG, surf.duke, 2, 15),
 518                         new Temp(Temp.RNA | Temp.INA, surf.duke, 16, 130),
 519                         new Features(Features.GRAPHICS, 16, 130, surf) } },
 520                 { { "J2D demo  -  texture text on gradient", "1000" },
 521                     { new GpE(GpE.WI, myBlue, myBlack, 0, 20),
 522                         new GpE(GpE.WD, myBlue, myBlack, 21, 40),
 523                         new TpE(TpE.OI | TpE.NF, myBlack, myYellow, 4, 0, 10),
 524                         new TpE(TpE.OD | TpE.NF, myBlack, myYellow, 4, 11, 20),
 525                         new TpE(TpE.OI | TpE.NF | TpE.HAF, myBlack, myYellow, 5,
 526                         21, 40),
 527                         new TxE("J2D demo", f2, 0, null, 0, 40) } },
 528                 { { "Previous scene random close out", "0" },
 529                     { new CoE(CoE.RAND, 0, 20, surf) } },
 530                 { { "Text Features", "999" },
 531                     { new Temp(Temp.RECT, null, 0, 15),
 532                         new Temp(Temp.IMG, surf.duke, 2, 15),
 533                         new Temp(Temp.RNA | Temp.INA, surf.duke, 16, 130),
 534                         new Features(Features.TEXT, 16, 130, surf) } },
 535                 { { "J2D demo  -  composite text on texture", "1000" },
 536                     { new TpE(TpE.RI, myBlack, gp, 40, 0, 20),
 537                         new TpE(TpE.RD, myBlack, gp, 40, 21, 40),
 538                         new TpE(TpE.RI, myBlack, gp, 40, 41, 60),
 539                         new TxE("J2D demo", f2, TxE.AC, myYellow, 0, 60) } },
 540                 { { "Previous scene dither dissolve out", "0" },
 541                     { new DdE(0, 20, 4, surf) } },
 542                 { { "Imaging Features", "999" },
 543                     { new Temp(Temp.RECT, null, 0, 15),
 544                         new Temp(Temp.IMG, surf.duke, 2, 15),
 545                         new Temp(Temp.RNA | Temp.INA, surf.duke, 16, 130),
 546                         new Features(Features.IMAGES, 16, 130, surf) } },
 547                 { { "J2D demo  -  text on gradient", "1000" },
 548                     { new GpE(GpE.SDH, myBlue, myBlack, 0, 20),
 549                         new GpE(GpE.SIH, myBlue, myBlack, 21, 40),
 550                         new GpE(GpE.SDH, myBlue, myBlack, 41, 50),
 551                         new GpE(GpE.INC | GpE.NF, myRed, myYellow, 0, 50),
 552                         new TxE("J2D demo", f2, TxE.NOP, null, 0, 50) } },
 553                 { { "Previous scene ellipse close out", "0" },
 554                     { new CoE(CoE.OVAL, 0, 20, surf) } },
 555                 { { "Color Features", "999" },
 556                     { new Temp(Temp.RECT, null, 0, 15),
 557                         new Temp(Temp.IMG, surf.duke, 2, 15),
 558                         new Temp(Temp.RNA | Temp.INA, surf.duke, 16, 99),
 559                         new Features(Features.COLOR, 16, 99, surf) } },
 560                 { { "J2D demo  -  composite and rotate text on paints", "2000" },
 561                     { new GpE(GpE.BURI, myBlack, myBlue, 0, 20),
 562                         new GpE(GpE.BURD, myBlack, myBlue, 21, 30),
 563                         new TpE(TpE.OI | TpE.HAF, myBlack, myBlue, 10, 31, 40),
 564                         new TxE("J2D demo", f2, TxE.AC | TxE.RI, myYellow, 0, 40) } },
 565                 { { "Previous scene subimage transform out", "0" },
 566                     { new SiE(60, 60, 0, 40, surf) } },
 567                 { { "CREDITS  -  transform in", "1000" },
 568                     { new LnE(LnE.ACI | LnE.ZOOMI | LnE.RI, 0, 60),
 569                         new TxE("CREDITS", f3, TxE.AC | TxE.SCI, RED, 20, 30),
 570                         new TxE("CREDITS", f3, TxE.SCXD, RED, 31, 38),
 571                         new TxE("CREDITS", f3, TxE.SCXI, RED, 39, 48),
 572                         new TxE("CREDITS", f3, TxE.SCXD, RED, 49, 54),
 573                         new TxE("CREDITS", f3, TxE.SCXI, RED, 55, 60) } },
 574                 { { "CREDITS  -  transform out", "0" },
 575                     { new LnE(LnE.ACD | LnE.ZOOMD | LnE.RD, 0, 45),
 576                         new TxE("CREDITS", f3, 0, RED, 0, 9),
 577                         new TxE("CREDITS", f3, TxE.SCD | TxE.RD, RED, 10, 30) } },
 578                 { { "Contributors", "1000" },
 579                     { new Temp(Temp.RECT, null, 0, 30),
 580                         new Temp(Temp.IMG, surf.dukeanim, 4, 30),
 581                         new Temp(Temp.RNA | Temp.INA, surf.dukeanim, 31, 200),
 582                         new Contributors(34, 200, surf) } }, };
 583 
 584                 for (Object[][] partInfo : partsInfo) {
 585                     List<Part> parts = new ArrayList<Part>();
 586                     for (Object part : partInfo[1]) {
 587                         parts.add((Part) part);
 588                     }
 589                     add(new Scene(parts, partInfo[0][0], partInfo[0][1]));
 590                 }
 591             }
 592         }
 593 
 594 
 595         /**
 596          * Scene is the manager of the parts.
 597          */
 598         static class Scene extends Object {
 599 
 600             public Object name;
 601             public Object participate = Boolean.TRUE;
 602             public Object pauseAmt;
 603             public List<Part> parts;
 604             public int index;
 605             public int length;
 606 
 607             public Scene(List<Part> parts, Object name, Object pauseAmt) {
 608                 this.name = name;
 609                 this.parts = parts;
 610                 this.pauseAmt = pauseAmt;
 611                 for (Part part : parts) {
 612                     int partLength = part.getEnd();
 613                     if (partLength > length) {
 614                         length = partLength;
 615                     }
 616                 }
 617             }
 618 
 619             public void reset(int w, int h) {
 620                 index = 0;
 621                 for (int i = 0; i < parts.size(); i++) {
 622                     (parts.get(i)).reset(w, h);
 623                 }
 624             }
 625 
 626             public void step(int w, int h) {
 627                 for (int i = 0; i < parts.size(); i++) {
 628                     Part part = parts.get(i);
 629                     if (index >= part.getBegin() && index <= part.getEnd()) {
 630                         part.step(w, h);
 631                     }
 632                 }
 633             }
 634 
 635             public void render(int w, int h, Graphics2D g2) {
 636                 for (int i = 0; i < parts.size(); i++) {
 637                     Part part = parts.get(i);
 638                     if (index >= part.getBegin() && index <= part.getEnd()) {
 639                         part.render(w, h, g2);
 640                     }
 641                 }
 642             }
 643 
 644             public void pause() {
 645                 try {
 646                     Thread.sleep(Long.parseLong((String) pauseAmt));
 647                 } catch (Exception ignored) {
 648                 }
 649                 System.gc();
 650             }
 651         } // End Scene class
 652 
 653 
 654         /**
 655          * Text Effect.  Transformation of characters.  Clip or fill.
 656          */
 657         static final class TxE implements Part {
 658 
 659             static final int INC = 1;
 660             static final int DEC = 2;
 661             static final int R = 4;            // rotate
 662             static final int RI = R | INC;
 663             static final int RD = R | DEC;
 664             static final int SC = 8;            // scale
 665             static final int SCI = SC | INC;
 666             static final int SCD = SC | DEC;
 667             static final int SCX = 16;           // scale invert x
 668             static final int SCXI = SCX | SC | INC;
 669             static final int SCXD = SCX | SC | DEC;
 670             static final int SCY = 32;           // scale invert y
 671             static final int SCYI = SCY | SC | INC;
 672             static final int SCYD = SCY | SC | DEC;
 673             static final int AC = 64;           // AlphaComposite
 674             static final int CLIP = 128;          // Clipping
 675             static final int NOP = 512;          // No Paint
 676             private int beginning, ending;
 677             private int type;
 678             private double rIncr, sIncr;
 679             private double sx, sy, rotate;
 680             private Shape shapes[], txShapes[];
 681             private int sw;
 682             private int numRev;
 683             private Paint paint;
 684 
 685             public TxE(String text,
 686                     Font font,
 687                     int type,
 688                     Paint paint,
 689                     int beg,
 690                     int end) {
 691                 this.type = type;
 692                 this.paint = paint;
 693                 this.beginning = beg;
 694                 this.ending = end;
 695 
 696                 setIncrements(2);
 697 
 698                 char[] chars = text.toCharArray();
 699                 shapes = new Shape[chars.length];
 700                 txShapes = new Shape[chars.length];
 701                 FontRenderContext frc = new FontRenderContext(null, true, true);
 702                 TextLayout tl = new TextLayout(text, font, frc);
 703                 sw = (int) tl.getOutline(null).getBounds().getWidth();
 704                 for (int j = 0; j < chars.length; j++) {
 705                     String s = String.valueOf(chars[j]);
 706                     shapes[j] = new TextLayout(s, font, frc).getOutline(null);
 707                 }
 708             }
 709 
 710             public void setIncrements(double numRevolutions) {
 711                 this.numRev = (int) numRevolutions;
 712                 rIncr = 360.0 / ((ending - beginning) / numRevolutions);
 713                 sIncr = 1.0 / (ending - beginning);
 714                 if ((type & SCX) != 0 || (type & SCY) != 0) {
 715                     sIncr *= 2;
 716                 }
 717                 if ((type & DEC) != 0) {
 718                     rIncr = -rIncr;
 719                     sIncr = -sIncr;
 720                 }
 721             }
 722 
 723             @Override
 724             public void reset(int w, int h) {
 725                 if (type == SCXI) {
 726                     sx = -1.0;
 727                     sy = 1.0;
 728                 } else if (type == SCYI) {
 729                     sx = 1.0;
 730                     sy = -1.0;
 731                 } else {
 732                     sx = sy = (type & DEC) != 0 ? 1.0 : 0.0;
 733                 }
 734                 rotate = 0;
 735             }
 736 
 737             @Override
 738             public void step(int w, int h) {
 739 
 740                 float charWidth = w / 2 - sw / 2;
 741 
 742                 for (int i = 0; i < shapes.length; i++) {
 743                     AffineTransform at = new AffineTransform();
 744                     Rectangle2D maxBounds = shapes[i].getBounds();
 745                     at.translate(charWidth, h / 2 + maxBounds.getHeight() / 2);
 746                     charWidth += (float) maxBounds.getWidth() + 1;
 747                     Shape shape = at.createTransformedShape(shapes[i]);
 748                     Rectangle2D b1 = shape.getBounds2D();
 749 
 750                     if ((type & R) != 0) {
 751                         at.rotate(Math.toRadians(rotate));
 752                     }
 753                     if ((type & SC) != 0) {
 754                         at.scale(sx, sy);
 755                     }
 756                     shape = at.createTransformedShape(shapes[i]);
 757                     Rectangle2D b2 = shape.getBounds2D();
 758 
 759                     double xx = (b1.getX() + b1.getWidth() / 2)
 760                             - (b2.getX() + b2.getWidth() / 2);
 761                     double yy = (b1.getY() + b1.getHeight() / 2)
 762                             - (b2.getY() + b2.getHeight() / 2);
 763                     AffineTransform toCenterAT = new AffineTransform();
 764                     toCenterAT.translate(xx, yy);
 765                     toCenterAT.concatenate(at);
 766                     txShapes[i] = toCenterAT.createTransformedShape(shapes[i]);
 767                 }
 768                 // avoid over rotation
 769                 if (Math.abs(rotate) <= numRev * 360) {
 770                     rotate += rIncr;
 771                     if ((type & SCX) != 0) {
 772                         sx += sIncr;
 773                     } else if ((type & SCY) != 0) {
 774                         sy += sIncr;
 775                     } else {
 776                         sx += sIncr;
 777                         sy += sIncr;
 778                     }
 779                 }
 780             }
 781 
 782             @Override
 783             public void render(int w, int h, Graphics2D g2) {
 784                 Composite saveAC = null;
 785                 if ((type & AC) != 0 && sx > 0 && sx < 1) {
 786                     saveAC = g2.getComposite();
 787                     g2.setComposite(AlphaComposite.getInstance(
 788                             AlphaComposite.SRC_OVER, (float) sx));
 789                 }
 790                 GeneralPath path = null;
 791                 if ((type & CLIP) != 0) {
 792                     path = new GeneralPath();
 793                 }
 794                 if (paint != null) {
 795                     g2.setPaint(paint);
 796                 }
 797                 for (int i = 0; i < txShapes.length; i++) {
 798                     if ((type & CLIP) != 0) {
 799                         path.append(txShapes[i], false);
 800                     } else {
 801                         g2.fill(txShapes[i]);
 802                     }
 803                 }
 804                 if ((type & CLIP) != 0) {
 805                     g2.clip(path);
 806                 }
 807                 if (saveAC != null) {
 808                     g2.setComposite(saveAC);
 809                 }
 810             }
 811 
 812             @Override
 813             public int getBegin() {
 814                 return beginning;
 815             }
 816 
 817             @Override
 818             public int getEnd() {
 819                 return ending;
 820             }
 821         } // End TxE class
 822 
 823 
 824         /**
 825          * GradientPaint Effect.  Burst, split, horizontal and
 826          * vertical gradient fill effects.
 827          */
 828         static class GpE implements Part {
 829 
 830             static final int INC = 1;               // increasing
 831             static final int DEC = 2;               // decreasing
 832             static final int CNT = 4;               // center
 833             static final int WID = 8;               // width
 834             static final int WI = WID | INC;
 835             static final int WD = WID | DEC;
 836             static final int HEI = 16;              // height
 837             static final int HI = HEI | INC;
 838             static final int HD = HEI | DEC;
 839             static final int SPL = 32 | CNT;        // split
 840             static final int SIW = SPL | INC | WID;
 841             static final int SDW = SPL | DEC | WID;
 842             static final int SIH = SPL | INC | HEI;
 843             static final int SDH = SPL | DEC | HEI;
 844             static final int BUR = 64 | CNT;        // burst
 845             static final int BURI = BUR | INC;
 846             static final int BURD = BUR | DEC;
 847             static final int NF = 128;             // no fill
 848             private Color c1, c2;
 849             private int beginning, ending;
 850             private float incr, index;
 851             private List<Rectangle2D> rect = new ArrayList<Rectangle2D>();
 852             private List<GradientPaint> grad = new ArrayList<GradientPaint>();
 853             private int type;
 854 
 855             public GpE(int type, Color c1, Color c2, int beg, int end) {
 856                 this.type = type;
 857                 this.c1 = c1;
 858                 this.c2 = c2;
 859                 this.beginning = beg;
 860                 this.ending = end;
 861             }
 862 
 863             @Override
 864             public void reset(int w, int h) {
 865                 incr = 1.0f / (ending - beginning);
 866                 if ((type & CNT) != 0) {
 867                     incr /= 2.3f;
 868                 }
 869                 if ((type & CNT) != 0 && (type & INC) != 0) {
 870                     index = 0.5f;
 871                 } else if ((type & DEC) != 0) {
 872                     index = 1.0f;
 873                     incr = -incr;
 874                 } else {
 875                     index = 0.0f;
 876                 }
 877                 index += incr;
 878             }
 879 
 880             @Override
 881             public void step(int w, int h) {
 882                 rect.clear();
 883                 grad.clear();
 884 
 885                 if ((type & WID) != 0) {
 886                     float w2 = 0, x1 = 0, x2 = 0;
 887                     if ((type & SPL) != 0) {
 888                         w2 = w * 0.5f;
 889                         x1 = w * (1.0f - index);
 890                         x2 = w * index;
 891                     } else {
 892                         w2 = w * index;
 893                         x1 = x2 = w2;
 894                     }
 895                     rect.add(new Rectangle2D.Float(0, 0, w2, h));
 896                     rect.add(new Rectangle2D.Float(w2, 0, w - w2, h));
 897                     grad.add(new GradientPaint(0, 0, c1, x1, 0, c2));
 898                     grad.add(new GradientPaint(x2, 0, c2, w, 0, c1));
 899                 } else if ((type & HEI) != 0) {
 900                     float h2 = 0, y1 = 0, y2 = 0;
 901                     if ((type & SPL) != 0) {
 902                         h2 = h * 0.5f;
 903                         y1 = h * (1.0f - index);
 904                         y2 = h * index;
 905                     } else {
 906                         h2 = h * index;
 907                         y1 = y2 = h2;
 908                     }
 909                     rect.add(new Rectangle2D.Float(0, 0, w, h2));
 910                     rect.add(new Rectangle2D.Float(0, h2, w, h - h2));
 911                     grad.add(new GradientPaint(0, 0, c1, 0, y1, c2));
 912                     grad.add(new GradientPaint(0, y2, c2, 0, h, c1));
 913                 } else if ((type & BUR) != 0) {
 914 
 915                     float w2 = w / 2;
 916                     float h2 = h / 2;
 917 
 918                     rect.add(new Rectangle2D.Float(0, 0, w2, h2));
 919                     rect.add(new Rectangle2D.Float(w2, 0, w2, h2));
 920                     rect.add(new Rectangle2D.Float(0, h2, w2, h2));
 921                     rect.add(new Rectangle2D.Float(w2, h2, w2, h2));
 922 
 923                     float x1 = w * (1.0f - index);
 924                     float x2 = w * index;
 925                     float y1 = h * (1.0f - index);
 926                     float y2 = h * index;
 927 
 928                     grad.add(new GradientPaint(0, 0, c1, x1, y1, c2));
 929                     grad.add(new GradientPaint(w, 0, c1, x2, y1, c2));
 930                     grad.add(new GradientPaint(0, h, c1, x1, y2, c2));
 931                     grad.add(new GradientPaint(w, h, c1, x2, y2, c2));
 932                 } else if ((type & NF) != 0) {
 933                     float y = h * index;
 934                     grad.add(new GradientPaint(0, 0, c1, 0, y, c2));
 935                 }
 936 
 937                 if ((type & INC) != 0 || (type & DEC) != 0) {
 938                     index += incr;
 939                 }
 940             }
 941 
 942             @Override
 943             public void render(int w, int h, Graphics2D g2) {
 944                 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
 945                         RenderingHints.VALUE_ANTIALIAS_OFF);
 946                 for (int i = 0; i < grad.size(); i++) {
 947                     g2.setPaint(grad.get(i));
 948                     if ((type & NF) == 0) {
 949                         g2.fill(rect.get(i));
 950                     }
 951                 }
 952                 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
 953                         RenderingHints.VALUE_ANTIALIAS_ON);
 954             }
 955 
 956             @Override
 957             public int getBegin() {
 958                 return beginning;
 959             }
 960 
 961             @Override
 962             public int getEnd() {
 963                 return ending;
 964             }
 965         } // End GpE class
 966 
 967 
 968         /**
 969          * TexturePaint Effect.  Expand and collapse a texture.
 970          */
 971         static final class TpE implements Part {
 972 
 973             static final int INC = 1;             // increasing
 974             static final int DEC = 2;             // decreasing
 975             static final int OVAL = 4;             // oval
 976             static final int RECT = 8;             // rectangle
 977             static final int HAF = 16;             // half oval or rect size
 978             static final int NF = 32;             // no fill
 979             static final int OI = OVAL | INC;
 980             static final int OD = OVAL | DEC;
 981             static final int RI = RECT | INC;
 982             static final int RD = RECT | DEC;
 983             private Paint p1, p2;
 984             private int beginning, ending;
 985             private float incr, index;
 986             private TexturePaint texture;
 987             private int type;
 988             private int size;
 989             private BufferedImage bimg;
 990             private Rectangle rect;
 991 
 992             public TpE(int type, Paint p1, Paint p2, int size,
 993                     int beg, int end) {
 994                 this.type = type;
 995                 this.p1 = p1;
 996                 this.p2 = p2;
 997                 this.beginning = beg;
 998                 this.ending = end;
 999                 setTextureSize(size);
1000             }
1001 
1002             public void setTextureSize(int size) {
1003                 this.size = size;
1004                 bimg = new BufferedImage(size, size, BufferedImage.TYPE_INT_RGB);
1005                 rect = new Rectangle(0, 0, size, size);
1006             }
1007 
1008             @Override
1009             public void reset(int w, int h) {
1010                 incr = (float) (size) / (float) (ending - beginning);
1011                 if ((type & HAF) != 0) {
1012                     incr /= 2;
1013                 }
1014                 if ((type & DEC) != 0) {
1015                     index = size;
1016                     if ((type & HAF) != 0) {
1017                         index /= 2;
1018                     }
1019                     incr = -incr;
1020                 } else {
1021                     index = 0.0f;
1022                 }
1023                 index += incr;
1024             }
1025 
1026             @Override
1027             public void step(int w, int h) {
1028                 Graphics2D g2 = bimg.createGraphics();
1029                 g2.setPaint(p1);
1030                 g2.fillRect(0, 0, size, size);
1031                 g2.setPaint(p2);
1032                 if ((type & OVAL) != 0) {
1033                     g2.fill(new Ellipse2D.Float(0, 0, index, index));
1034                 } else if ((type & RECT) != 0) {
1035                     g2.fill(new Rectangle2D.Float(0, 0, index, index));
1036                 }
1037                 texture = new TexturePaint(bimg, rect);
1038                 g2.dispose();
1039                 index += incr;
1040             }
1041 
1042             @Override
1043             public void render(int w, int h, Graphics2D g2) {
1044                 g2.setPaint(texture);
1045                 if ((type & NF) == 0) {
1046                     g2.fillRect(0, 0, w, h);
1047                 }
1048             }
1049 
1050             @Override
1051             public int getBegin() {
1052                 return beginning;
1053             }
1054 
1055             @Override
1056             public int getEnd() {
1057                 return ending;
1058             }
1059         } // End TpE class
1060 
1061 
1062         /**
1063          * Close out effect.  Close out the buffered image with different
1064          * geometry shapes.
1065          */
1066         static class CoE implements Part {
1067             private final Surface surf;
1068             static final int WID = 1;
1069             static final int HEI = 2;
1070             static final int OVAL = 4;
1071             static final int RECT = 8;
1072             static final int RAND = 16;
1073             static final int ARC = 32;
1074             private int type;
1075             private int beginning, ending;
1076             private BufferedImage bimg;
1077             private Shape shape;
1078             private double zoom, extent;
1079             private double zIncr, eIncr;
1080             private boolean doRandom;
1081 
1082             public CoE(int type, int beg, int end, Surface surf) {
1083                 this.type = type;
1084                 this.beginning = beg;
1085                 this.ending = end;
1086                 this.surf = surf;
1087                 zIncr = -(2.0 / (ending - beginning));
1088                 eIncr = 360.0 / (ending - beginning);
1089                 doRandom = (type & RAND) != 0;
1090             }
1091 
1092             @Override
1093             public void reset(int w, int h) {
1094                 if (doRandom) {
1095                     int num = (int) (Math.random() * 5.0);
1096                     switch (num) {
1097                         case 0:
1098                             type = OVAL;
1099                             break;
1100                         case 1:
1101                             type = RECT;
1102                             break;
1103                         case 2:
1104                             type = RECT | WID;
1105                             break;
1106                         case 3:
1107                             type = RECT | HEI;
1108                             break;
1109                         case 4:
1110                             type = ARC;
1111                             break;
1112                         default:
1113                             type = OVAL;
1114                     }
1115                 }
1116                 shape = null;
1117                 bimg = null;
1118                 extent = 360.0;
1119                 zoom = 2.0;
1120             }
1121 
1122             @Override
1123             public void step(int w, int h) {
1124                 if (bimg == null) {
1125                     int biw = surf.bimg.getWidth();
1126                     int bih = surf.bimg.getHeight();
1127                     bimg = new BufferedImage(biw, bih,
1128                             BufferedImage.TYPE_INT_RGB);
1129                     Graphics2D big = bimg.createGraphics();
1130                     big.drawImage(surf.bimg, 0, 0, null);
1131                 }
1132                 double z = Math.min(w, h) * zoom;
1133                 if ((type & OVAL) != 0) {
1134                     shape = new Ellipse2D.Double(w / 2 - z / 2, h / 2 - z / 2, z,
1135                             z);
1136                 } else if ((type & ARC) != 0) {
1137                     shape = new Arc2D.Double(-100, -100, w + 200, h + 200, 90,
1138                             extent, Arc2D.PIE);
1139                     extent -= eIncr;
1140                 } else if ((type & RECT) != 0) {
1141                     if ((type & WID) != 0) {
1142                         shape = new Rectangle2D.Double(w / 2 - z / 2, 0, z, h);
1143                     } else if ((type & HEI) != 0) {
1144                         shape = new Rectangle2D.Double(0, h / 2 - z / 2, w, z);
1145                     } else {
1146                         shape = new Rectangle2D.Double(w / 2 - z / 2, h / 2 - z
1147                                 / 2, z, z);
1148                     }
1149                 }
1150                 zoom += zIncr;
1151             }
1152 
1153             @Override
1154             public void render(int w, int h, Graphics2D g2) {
1155                 g2.clip(shape);
1156                 g2.drawImage(bimg, 0, 0, null);
1157             }
1158 
1159             @Override
1160             public int getBegin() {
1161                 return beginning;
1162             }
1163 
1164             @Override
1165             public int getEnd() {
1166                 return ending;
1167             }
1168         } // End CoE class
1169 
1170 
1171         /**
1172          * Dither Dissolve Effect. For each successive step in the animation,
1173          * a pseudo-random starting horizontal position is chosen using list,
1174          * and then the corresponding points created from xlist and ylist are
1175          * blacked out for the current "chunk".  The x and y chunk starting
1176          * positions are each incremented by the associated chunk size, and
1177          * this process is repeated for the number of "steps" in the
1178          * animation, causing an equal number of pseudo-randomly picked
1179          * "blocks" to be blacked out during each step of the animation.
1180          */
1181         static class DdE implements Part {
1182             private final Surface surf;
1183             private int beginning, ending;
1184             private BufferedImage bimg;
1185             private Graphics2D big;
1186             private List<Integer> list, xlist, ylist;
1187             private int xeNum, yeNum;    // element number
1188             private int xcSize, ycSize;  // chunk size
1189             private int inc;
1190             private int blocksize;
1191 
1192             public DdE(int beg, int end, int blocksize, Surface surf) {
1193                 this.beginning = beg;
1194                 this.ending = end;
1195                 this.blocksize = blocksize;
1196                 this.surf = surf;
1197             }
1198 
1199             private void createShuffledLists() {
1200                 int width = bimg.getWidth();
1201                 int height = bimg.getHeight();
1202                 xlist = new ArrayList<Integer>(width);
1203                 ylist = new ArrayList<Integer>(height);
1204                 list = new ArrayList<Integer>(ending - beginning + 1);
1205                 for (int i = 0; i < width; i++) {
1206                     xlist.add(i, i);
1207                 }
1208                 for (int i = 0; i < height; i++) {
1209                     ylist.add(i, i);
1210                 }
1211                 for (int i = 0; i < (ending - beginning + 1); i++) {
1212                     list.add(i, i);
1213                 }
1214                 java.util.Collections.shuffle(xlist);
1215                 java.util.Collections.shuffle(ylist);
1216                 java.util.Collections.shuffle(list);
1217             }
1218 
1219             @Override
1220             public void reset(int w, int h) {
1221                 bimg = null;
1222             }
1223 
1224             @Override
1225             public void step(int w, int h) {
1226                 if (inc > ending) {
1227                     bimg = null;
1228                 }
1229                 if (bimg == null) {
1230                     int biw = surf.bimg.getWidth();
1231                     int bih = surf.bimg.getHeight();
1232                     bimg = new BufferedImage(biw, bih,
1233                             BufferedImage.TYPE_INT_RGB);
1234                     createShuffledLists();
1235                     big = bimg.createGraphics();
1236                     big.drawImage(surf.bimg, 0, 0, null);
1237                     xcSize = (xlist.size() / (ending - beginning)) + 1;
1238                     ycSize = (ylist.size() / (ending - beginning)) + 1;
1239                     xeNum = 0;
1240                     inc = 0;
1241                 }
1242                 xeNum = xcSize * (list.get(inc)).intValue();
1243                 yeNum = -ycSize;
1244                 inc++;
1245             }
1246 
1247             @Override
1248             public void render(int w, int h, Graphics2D g2) {
1249                 big.setColor(myBlack);
1250 
1251                 for (int k = 0; k <= (ending - beginning); k++) {
1252                     if ((xeNum + xcSize) > xlist.size()) {
1253                         xeNum = 0;
1254                     } else {
1255                         xeNum += xcSize;
1256                     }
1257                     yeNum += ycSize;
1258 
1259                     for (int i = xeNum; i < xeNum + xcSize && i < xlist.size();
1260                             i++) {
1261                         for (int j = yeNum; j < yeNum + ycSize && j
1262                                 < ylist.size(); j++) {
1263                             int xval = (xlist.get(i)).intValue();
1264                             int yval = (ylist.get(j)).intValue();
1265                             if (((xval % blocksize) == 0) && ((yval % blocksize)
1266                                     == 0)) {
1267                                 big.fillRect(xval, yval, blocksize, blocksize);
1268                             }
1269                         }
1270                     }
1271                 }
1272 
1273                 g2.drawImage(bimg, 0, 0, null);
1274             }
1275 
1276             @Override
1277             public int getBegin() {
1278                 return beginning;
1279             }
1280 
1281             @Override
1282             public int getEnd() {
1283                 return ending;
1284             }
1285         } // End DdE class
1286 
1287 
1288         /**
1289          * Subimage effect.  Subimage the scene's buffered
1290          * image then rotate and scale down the subimages.
1291          */
1292         static class SiE implements Part {
1293             private final Surface surf;
1294             private int beginning, ending;
1295             private BufferedImage bimg;
1296             private double rIncr, sIncr;
1297             private double scale, rotate;
1298             private int siw, sih;
1299             private List<BufferedImage> subs = new ArrayList<BufferedImage>(20);
1300             private List<Point> pts = new ArrayList<Point>(20);
1301 
1302             public SiE(int siw, int sih, int beg, int end, Surface surf) {
1303                 this.siw = siw;
1304                 this.sih = sih;
1305                 this.beginning = beg;
1306                 this.ending = end;
1307                 this.surf = surf;
1308                 rIncr = 360.0 / (ending - beginning);
1309                 sIncr = 1.0 / (ending - beginning);
1310             }
1311 
1312             @Override
1313             public void reset(int w, int h) {
1314                 scale = 1.0;
1315                 rotate = 0.0;
1316                 bimg = null;
1317                 subs.clear();
1318                 pts.clear();
1319             }
1320 
1321             @Override
1322             public void step(int w, int h) {
1323                 if (bimg == null) {
1324                     int biw = surf.bimg.getWidth();
1325                     int bih = surf.bimg.getHeight();
1326                     bimg = new BufferedImage(biw, bih,
1327                             BufferedImage.TYPE_INT_RGB);
1328                     Graphics2D big = bimg.createGraphics();
1329                     big.drawImage(surf.bimg, 0, 0, null);
1330                     for (int x = 0; x < w && scale > 0.0; x += siw) {
1331                         int ww = x + siw < w ? siw : w - x;
1332                         for (int y = 0; y < h; y += sih) {
1333                             int hh = y + sih < h ? sih : h - y;
1334                             subs.add(bimg.getSubimage(x, y, ww, hh));
1335                             pts.add(new Point(x, y));
1336                         }
1337                     }
1338                 }
1339 
1340                 rotate += rIncr;
1341                 scale -= sIncr;
1342             }
1343 
1344             @Override
1345             public void render(int w, int h, Graphics2D g2) {
1346                 AffineTransform saveTx = g2.getTransform();
1347                 g2.setColor(myBlue);
1348                 for (int i = 0; i < subs.size() && scale > 0.0; i++) {
1349                     BufferedImage bi = subs.get(i);
1350                     Point p = pts.get(i);
1351                     int ww = bi.getWidth();
1352                     int hh = bi.getHeight();
1353                     AffineTransform at = new AffineTransform();
1354                     at.rotate(Math.toRadians(rotate), p.x + ww / 2, p.y + hh / 2);
1355                     at.translate(p.x, p.y);
1356                     at.scale(scale, scale);
1357 
1358                     Rectangle b1 = new Rectangle(0, 0, ww, hh);
1359                     Shape shape = at.createTransformedShape(b1);
1360                     Rectangle2D b2 = shape.getBounds2D();
1361                     double xx = (p.x + ww / 2) - (b2.getX() + b2.getWidth() / 2);
1362                     double yy = (p.y + hh / 2)
1363                             - (b2.getY() + b2.getHeight() / 2);
1364                     AffineTransform toCenterAT = new AffineTransform();
1365                     toCenterAT.translate(xx, yy);
1366                     toCenterAT.concatenate(at);
1367 
1368                     g2.setTransform(toCenterAT);
1369                     g2.drawImage(bi, 0, 0, null);
1370                     g2.draw(b1);
1371                 }
1372                 g2.setTransform(saveTx);
1373             }
1374 
1375             @Override
1376             public int getBegin() {
1377                 return beginning;
1378             }
1379 
1380             @Override
1381             public int getEnd() {
1382                 return ending;
1383             }
1384         } // End SiE class
1385 
1386 
1387         /**
1388          * Line Effect.  Flattened ellipse with lines from the center
1389          * to the edge.  Expand or collapse the ellipse.  Fade in or out
1390          * the lines.
1391          */
1392         static class LnE implements Part {
1393 
1394             static final int INC = 1;
1395             static final int DEC = 2;
1396             static final int R = 4;             // rotate
1397             static final int ZOOM = 8;             // zoom
1398             static final int AC = 32;             // AlphaComposite
1399             static final int RI = R | INC;
1400             static final int RD = R | DEC;
1401             static final int ZOOMI = ZOOM | INC;
1402             static final int ZOOMD = ZOOM | DEC;
1403             static final int ACI = AC | INC;
1404             static final int ACD = AC | DEC;
1405             private int beginning, ending;
1406             private double rIncr, rotate;
1407             private double zIncr, zoom;
1408             private List<Point2D.Double> pts = new ArrayList<Point2D.Double>();
1409             private float alpha, aIncr;
1410             private int type;
1411 
1412             public LnE(int type, int beg, int end) {
1413                 this.type = type;
1414                 this.beginning = beg;
1415                 this.ending = end;
1416                 float range = ending - beginning;
1417                 rIncr = 360.0f / range;
1418                 aIncr = 0.9f / range;
1419                 zIncr = 2.0f / range;
1420                 if ((type & DEC) != 0) {
1421                     rIncr = -rIncr;
1422                     aIncr = -aIncr;
1423                     zIncr = -zIncr;
1424                 }
1425             }
1426 
1427             public void generatePts(int w, int h, double sizeF) {
1428                 pts.clear();
1429                 double size = Math.min(w, h) * sizeF;
1430                 Ellipse2D ellipse = new Ellipse2D.Double(w / 2 - size / 2, h / 2 - size
1431                         / 2, size, size);
1432                 PathIterator pi = ellipse.getPathIterator(null, 0.8);
1433                 while (!pi.isDone()) {
1434                     double[] pt = new double[6];
1435                     switch (pi.currentSegment(pt)) {
1436                         case FlatteningPathIterator.SEG_MOVETO:
1437                         case FlatteningPathIterator.SEG_LINETO:
1438                             pts.add(new Point2D.Double(pt[0], pt[1]));
1439                     }
1440                     pi.next();
1441                 }
1442             }
1443 
1444             @Override
1445             public void reset(int w, int h) {
1446                 if ((type & DEC) != 0) {
1447                     rotate = 360;
1448                     alpha = 1.0f;
1449                     zoom = 2.0;
1450                 } else {
1451                     rotate = alpha = 0;
1452                     zoom = 0;
1453                 }
1454                 if ((type & ZOOM) == 0) {
1455                     generatePts(w, h, 0.5);
1456                 }
1457             }
1458 
1459             @Override
1460             public void step(int w, int h) {
1461                 if ((type & ZOOM) != 0) {
1462                     generatePts(w, h, zoom += zIncr);
1463                 }
1464                 if ((type & RI) != 0 || (type & RI) != 0) {
1465                     rotate += rIncr;
1466                 }
1467                 if ((type & ACI) != 0 || (type & ACD) != 0) {
1468                     alpha += aIncr;
1469                 }
1470             }
1471 
1472             @Override
1473             public void render(int w, int h, Graphics2D g2) {
1474                 Composite saveAC = null;
1475                 if ((type & AC) != 0 && alpha >= 0 && alpha <= 1) {
1476                     saveAC = g2.getComposite();
1477                     g2.setComposite(AlphaComposite.getInstance(
1478                             AlphaComposite.SRC_OVER, alpha));
1479                 }
1480                 AffineTransform saveTx = null;
1481                 if ((type & R) != 0) {
1482                     saveTx = g2.getTransform();
1483                     AffineTransform at = new AffineTransform();
1484                     at.rotate(Math.toRadians(rotate), w / 2, h / 2);
1485                     g2.setTransform(at);
1486                 }
1487                 Point2D p1 = new Point2D.Double(w / 2, h / 2);
1488                 g2.setColor(YELLOW);
1489                 for (Point2D pt : pts) {
1490                     g2.draw(new Line2D.Float(p1, pt));
1491                 }
1492                 if (saveTx != null) {
1493                     g2.setTransform(saveTx);
1494                 }
1495                 if (saveAC != null) {
1496                     g2.setComposite(saveAC);
1497                 }
1498             }
1499 
1500             @Override
1501             public int getBegin() {
1502                 return beginning;
1503             }
1504 
1505             @Override
1506             public int getEnd() {
1507                 return ending;
1508             }
1509         } // End LnE class
1510 
1511 
1512         /**
1513          * Template for Features & Contributors consisting of translating
1514          * blue and red rectangles and an image going from transparent to
1515          * opaque.
1516          */
1517         static class Temp implements Part {
1518 
1519             static final int NOANIM = 1;
1520             static final int RECT = 2;
1521             static final int IMG = 4;
1522             static final int RNA = RECT | NOANIM;
1523             static final int INA = IMG | NOANIM;
1524             private int beginning, ending;
1525             private float alpha, aIncr;
1526             private int type;
1527             private Rectangle rect1, rect2;
1528             private int x, y, xIncr, yIncr;
1529             private Image img;
1530 
1531             public Temp(int type, Image img, int beg, int end) {
1532                 this.type = type;
1533                 this.img = img;
1534                 this.beginning = beg;
1535                 this.ending = end;
1536                 aIncr = 0.9f / (ending - beginning);
1537                 if ((type & NOANIM) != 0) {
1538                     alpha = 1.0f;
1539                 }
1540             }
1541 
1542             @Override
1543             public void reset(int w, int h) {
1544                 rect1 = new Rectangle(8, 20, w - 20, 30);
1545                 rect2 = new Rectangle(20, 8, 30, h - 20);
1546                 if ((type & NOANIM) == 0) {
1547                     alpha = 0.0f;
1548                     xIncr = w / (ending - beginning);
1549                     yIncr = h / (ending - beginning);
1550                     x = w + (int) (xIncr * 1.4);
1551                     y = h + (int) (yIncr * 1.4);
1552                 }
1553             }
1554 
1555             @Override
1556             public void step(int w, int h) {
1557                 if ((type & NOANIM) != 0) {
1558                     return;
1559                 }
1560                 if ((type & RECT) != 0) {
1561                     rect1.setLocation(x -= xIncr, 20);
1562                     rect2.setLocation(20, y -= yIncr);
1563                 }
1564                 if ((type & IMG) != 0) {
1565                     alpha += aIncr;
1566                 }
1567             }
1568 
1569             @Override
1570             public void render(int w, int h, Graphics2D g2) {
1571                 if ((type & RECT) != 0) {
1572                     g2.setColor(myBlue);
1573                     g2.fill(rect1);
1574                     g2.setColor(myRed);
1575                     g2.fill(rect2);
1576                 }
1577                 if ((type & IMG) != 0) {
1578                     Composite saveAC = g2.getComposite();
1579                     if (alpha >= 0 && alpha <= 1) {
1580                         g2.setComposite(AlphaComposite.getInstance(
1581                                 AlphaComposite.SRC_OVER, alpha));
1582                     }
1583                     g2.drawImage(img, 30, 30, null);
1584                     g2.setComposite(saveAC);
1585                 }
1586             }
1587 
1588             @Override
1589             public int getBegin() {
1590                 return beginning;
1591             }
1592 
1593             @Override
1594             public int getEnd() {
1595                 return ending;
1596             }
1597         } // End Temp class
1598 
1599 
1600         /**
1601          * Features of Java2D(TM).  Single character advancement effect.
1602          */
1603         static class Features implements Part {
1604 
1605             static final int GRAPHICS = 0;
1606             static final int TEXT = 1;
1607             static final int IMAGES = 2;
1608             static final int COLOR = 3;
1609             static final Font font1 = new Font(Font.SERIF, Font.BOLD, 38);
1610             static final Font font2 = new Font(Font.SERIF, Font.PLAIN, 24);
1611             private final FontMetrics fm1;
1612             private final FontMetrics fm2;
1613             private static final String table[][] = { { "Graphics", "Antialiased rendering",
1614                     "Bezier paths",
1615                     "Transforms", "Compositing", "Stroking parameters" },
1616                 { "Text", "Extended font support",
1617                     "Advanced text layout", "Dynamic font loading",
1618                     "AttributeSets for font customization" },
1619                 { "Images", "Flexible image layouts",
1620                     "Extended imaging operations",
1621                     "   Convolutions, Lookup Tables",
1622                     "RenderableImage interface" },
1623                 { "Color", "ICC profile support", "Color conversion",
1624                     "Arbitrary color spaces" } };
1625             private String list[];
1626             private int beginning, ending;
1627             private int strH;
1628             private int endIndex, listIndex;
1629             private List<String> v = new ArrayList<String>();
1630 
1631             public Features(int type, int beg, int end, Surface surf) {
1632                 list = table[type];
1633                 this.beginning = beg;
1634                 this.ending = end;
1635                 fm1 = surf.getMetrics(font1);
1636                 fm2 = surf.getMetrics(font2);
1637             }
1638 
1639             @Override
1640             public void reset(int w, int h) {
1641                 strH = (fm2.getAscent() + fm2.getDescent());
1642                 endIndex = 1;
1643                 listIndex = 0;
1644                 v.clear();
1645                 v.add(list[listIndex].substring(0, endIndex));
1646             }
1647 
1648             @Override
1649             public void step(int w, int h) {
1650                 if (listIndex < list.length) {
1651                     if (++endIndex > list[listIndex].length()) {
1652                         if (++listIndex < list.length) {
1653                             endIndex = 1;
1654                             v.add(list[listIndex].substring(0, endIndex));
1655                         }
1656                     } else {
1657                         v.set(listIndex, list[listIndex].substring(0, endIndex));
1658                     }
1659                 }
1660             }
1661 
1662             @Override
1663             public void render(int w, int h, Graphics2D g2) {
1664                 g2.setColor(myWhite);
1665                 g2.setFont(font1);
1666                 g2.drawString(v.get(0), 90, 85);
1667                 g2.setFont(font2);
1668                 for (int i = 1, y = 90; i < v.size(); i++) {
1669                     g2.drawString(v.get(i), 120, y += strH);
1670                 }
1671             }
1672 
1673             @Override
1674             public int getBegin() {
1675                 return beginning;
1676             }
1677 
1678             @Override
1679             public int getEnd() {
1680                 return ending;
1681             }
1682         } // End Features class
1683 
1684 
1685         /**
1686          * Scrolling text of Java2D(TM) contributors.
1687          */
1688         static class Contributors implements Part {
1689 
1690             private static final String members[] = {
1691                 "Brian Lichtenwalter", "Jeannette Hung",
1692                 "Thanh Nguyen", "Jim Graham", "Jerry Evans",
1693                 "John Raley", "Michael Peirce", "Robert Kim",
1694                 "Jennifer Ball", "Deborah Adair", "Paul Charlton",
1695                 "Dmitry Feld", "Gregory Stone", "Richard Blanchard",
1696                 "Link Perry", "Phil Race", "Vincent Hardy",
1697                 "Parry Kejriwal", "Doug Felt", "Rekha Rangarajan",
1698                 "Paula Patel", "Michael Bundschuh", "Joe Warzecha",
1699                 "Joey Beheler", "Aastha Bhardwaj", "Daniel Rice",
1700                 "Chris Campbell", "Shinsuke Fukuda", "Dmitri Trembovetski",
1701                 "Chet Haase", "Jennifer Godinez", "Nicholas Talian",
1702                 "Raul Vera", "Ankit Patel", "Ilya Bagrak",
1703                 "Praveen Mohan", "Rakesh Menon"
1704             };
1705             private static final Font font = new Font(Font.SERIF, Font.PLAIN, 26);
1706             private final FontMetrics fm;
1707             private int beginning, ending;
1708             private int nStrs, strH, index, yh, height;
1709             private List<String> v = new ArrayList<String>();
1710             private List<String> cast =
1711                     new ArrayList<String>(members.length + 3);
1712             private int counter, cntMod;
1713             private GradientPaint gp;
1714 
1715             public Contributors(int beg, int end, Surface surf) {
1716                 this.beginning = beg;
1717                 this.ending = end;
1718                 fm = surf.getMetrics(font);
1719                 java.util.Arrays.sort(members);
1720                 cast.add("CONTRIBUTORS");
1721                 cast.add(" ");
1722                 cast.addAll(Arrays.asList(members));
1723                 cast.add(" ");
1724                 cast.add(" ");
1725                 cntMod = (ending - beginning) / cast.size() - 1;
1726             }
1727 
1728             @Override
1729             public void reset(int w, int h) {
1730                 v.clear();
1731                 strH = (fm.getAscent() + fm.getDescent());
1732                 nStrs = (h - 40) / strH + 1;
1733                 height = strH * (nStrs - 1) + 48;
1734                 index = 0;
1735                 gp = new GradientPaint(0, h / 2, WHITE, 0, h + 20, BLACK);
1736                 counter = 0;
1737             }
1738 
1739             @Override
1740             public void step(int w, int h) {
1741                 if (counter++ % cntMod == 0) {
1742                     if (index < cast.size()) {
1743                         v.add(cast.get(index));
1744                     }
1745                     if ((v.size() == nStrs || index >= cast.size()) && !v.
1746                             isEmpty()) {
1747                         v.remove(0);
1748                     }
1749                     ++index;
1750                 }
1751             }
1752 
1753             @Override
1754             public void render(int w, int h, Graphics2D g2) {
1755                 g2.setPaint(gp);
1756                 g2.setFont(font);
1757                 double remainder = counter % cntMod;
1758                 double incr = 1.0 - remainder / cntMod;
1759                 incr = incr == 1.0 ? 0 : incr;
1760                 int y = (int) (incr * strH);
1761 
1762                 if (index >= cast.size()) {
1763                     y = yh + y;
1764                 } else {
1765                     y = yh = height - v.size() * strH + y;
1766                 }
1767                 for (String s : v) {
1768                     g2.drawString(s, w / 2 - fm.stringWidth(s) / 2, y += strH);
1769                 }
1770             }
1771 
1772             @Override
1773             public int getBegin() {
1774                 return beginning;
1775             }
1776 
1777             @Override
1778             public int getEnd() {
1779                 return ending;
1780             }
1781         } // End Contributors class
1782     } // End Surface class
1783 } // End Intro class
1784