home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2001 July / PCWJUL01.iso / system / sys_home / PhantomScroll.java < prev    next >
Text File  |  2000-05-23  |  54KB  |  1,479 lines

  1. /*
  2.  *  @(#)PhantomScroll.java 1.0 25 Jan 2000
  3.  *  Copyright (c) 2000 Uldarico Muico Jr.
  4.  *
  5.  *  This program is free software; you can redistribute it and/or
  6.  *  modify it under the terms of the GNU General Public License
  7.  *  as published by the Free Software Foundation; either version 2
  8.  *  of the License, or (at your option) any later version.
  9.  *  
  10.  *  This program is distributed in the hope that it will be useful,
  11.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  *  GNU General Public License for more details.
  14.  *  
  15.  *  You should have received a copy of the GNU General Public License
  16.  *  along with this program; if not, write to the Free Software
  17.  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  18.  */
  19.  
  20. import java.applet.*;
  21. import java.awt.*;
  22. import java.awt.image.*;
  23. import java.net.*;
  24. import java.io.*;
  25. import java.util.*;
  26.  
  27. /**
  28.  *  The PhantomScroll applet is a horizontal text scroller with fade effects.
  29.  *  @version    1.0 25 Jan 2000
  30.  *  @author     Uldarico Muico Jr.
  31.  */
  32. public class PhantomScroll extends Applet implements Runnable
  33. {
  34.     /**
  35.      *  Ease-of-use constant for bgDisplay parameter of setBackground method. Indicates that the
  36.      *  background image should be tiled.
  37.      *  @see    #setBackground(Image, Color, int)
  38.      */
  39.     public  static final int    TILE            = 0;
  40.  
  41.     /**
  42.      *  Ease-of-use constant for bgDisplay parameter of setBackground method. Indicates that the
  43.      *  background image should be centered.
  44.      *  @see    #setBackground(Image, Color, int)
  45.      */
  46.     public  static final int    CENTER          = 1;
  47.  
  48.     private Thread              scrollThread    = null;
  49.     private int[]               bgPixels;
  50.     private int[]               fgPixels;
  51.     private int[]               backPixels;
  52.     private int[]               canvasPixels;
  53.     private int[]               textWidths;
  54.     private int[][]             fadePixels;
  55.     private int[][]             textPixels;
  56.     private int[][][]           linkRanges;
  57.     private Hashtable[][]       links;
  58.     private Dimension           bgSize          = new Dimension();
  59.     private Dimension           fgSize          = new Dimension();
  60.     private Image               bgImage;
  61.     private Image               fgImage;
  62.     private int                 textHeight      = 0;
  63.     private int                 currentText     = 0;
  64.     private int                 width;
  65.     private int                 height;
  66.     private int                 offset;
  67.     private int                 bgDisplay;
  68.     private int                 textDisp;
  69.     private int                 speed;
  70.     private int                 delay;
  71.     private int                 fadeIndex       = 0;
  72.     private int                 clearAmount;
  73.     private int                 count;
  74.     private int                 textCount       = 0;
  75.     private int                 currentLink     = -1;
  76.     private int                 pressedLink     = -1;
  77.     private int                 mouseX;
  78.     private int                 mouseY;
  79.     private int                 amplitude;
  80.     private int                 period;
  81.     private Color               bgColor;
  82.     private Color               fgColor;
  83.     private Color               linkColor;
  84.     private Color               activeLinkColor;
  85.     private boolean             fadeIncremented = true;
  86.     private boolean             pressed;
  87.     private boolean             entered         = false;
  88.     private String              defaultTarget;
  89.  
  90.     /**
  91.      *  Returns information about this applet.
  92.      *  @return the information
  93.      */
  94.     public String getAppletInfo()
  95.     {
  96.         return  "Uldarico Muico Jr., um@mail.com\n\r"
  97.              +  "PhantomScroll 1.0 January 25, 2000\n\r"
  98.              +  "Copyright (c) 2000 Uldarico Muico, Jr.\n\r";
  99.     }
  100.  
  101.     /**
  102.      *  Returns information about the parameters that are understood by this applet.
  103.      *  @return the information
  104.      */
  105.     public String[][] getParameterInfo()
  106.     {
  107.         String[][] info = { { "text",               "HTML",     "message to be displayed" },
  108.                             { "textFile",           "URL",      "text file that contains message" },
  109.                             { "font",               "String",   "name of font" },
  110.                             { "fontStyle",          "0-4",      "font style" },
  111.                             { "fontSize",           "int",      "font size" },
  112.                             { "bgImage",            "URL",      "background image" },
  113.                             { "bgDisplay",          "0-1",      "method of displaying background image" },
  114.                             { "bgColor",            "RGB",      "background color" },
  115.                             { "fgImage",            "URL",      "foreground/text image pattern" },
  116.                             { "fgColor",            "RGB",      "foreground/text color" },
  117.                             { "linkColor",          "RGB",      "color of hyperlinks" },
  118.                             { "activeLinkColor",    "RGB",      "color of active hyperlinks" },
  119.                             { "speed",              "int",      "average pixels per iteration" },
  120.                             { "delay",              "int",      "milliseconds per iteration" },
  121.                             { "amplitude",          "int",      "amplitude (pixels) of oscillation" },
  122.                             { "period",             "int",      "period (iterations) per oscillation" },
  123.                             { "trail",              "0.0-1.0",  "intensity of text trail" },
  124.                             { "target",             "String",   "name of the default target frame" } };
  125.         return info;
  126.     }
  127.  
  128.     /**
  129.      *  Initializes the applet. This method loads and processes the parameters.
  130.      */
  131.     public void init()
  132.     {
  133.         String value;
  134.         width           = size().width;
  135.         height          = size().height;
  136.         linkColor       = parseColor(getParameter("linkColor"), new Color(0x0000FF));
  137.         activeLinkColor = parseColor(getParameter("activeLinkColor"), new Color(0xFF0000));
  138.         setForeground(parseColor(getParameter("fgColor"), new Color(0xFFFFFF)));
  139.         setBackground(parseColor(getParameter("bgColor"), new Color(0x000000)));
  140.         setTarget(getParameter("target"));
  141.         value = getParameter("font");
  142.         Font font = new Font((value == null) ? "Helvetica" : value,
  143.                              parseInt(getParameter("fontStyle"), Font.PLAIN),
  144.                              parseInt(getParameter("fontSize"), 12));
  145.         value = getParameter("text");
  146.         if (value != null)
  147.             setText(value, font);
  148.         else
  149.             setText(parseURL(getParameter("textFile"), null, true), font);
  150.         value = getParameter("bgImage");
  151.         if (value != null)
  152.             setBackground(parseImage(value, null), null, parseInt(getParameter("bgDisplay"), TILE) % 2);
  153.         value = getParameter("fgImage");
  154.         if (value != null)
  155.             setForeground(parseImage(value, null));
  156.         setSpeed(parseInt(getParameter("speed"), 5));
  157.         setDelay(parseInt(getParameter("delay"), 80));
  158.         setAmplitude(parseInt(getParameter("amplitude"), 6));
  159.         setPeriod(parseInt(getParameter("period"), 30));
  160.         setTrail(parseUnity(getParameter("trail"), 0.8));
  161.     }
  162.  
  163.     /**
  164.      *  Starts the scrolling animation.
  165.      *  @see    #stop()
  166.      */
  167.     public void start()
  168.     {
  169.         if (scrollThread == null)
  170.         {
  171.             scrollThread = new Thread(this);
  172.             scrollThread.start();
  173.         }
  174.     }
  175.  
  176.     /**
  177.      *  Stops the scrolling animation.
  178.      *  @see    #start()
  179.      */
  180.     public void stop()
  181.     {
  182.         scrollThread = null;
  183.     }
  184.  
  185.     /**
  186.      *  Invoked when the thread is started through a call to the <i>start</i> method.
  187.      *  This method animates the scroller.
  188.      *  @see    #start()
  189.      */
  190.     public void run()
  191.     {
  192.         while (scrollThread != null)
  193.         {
  194.             repaint();
  195.             count++;
  196.             if (!pressed)
  197.             {
  198.                 int disp = -speed;
  199.                 if (period != 0)
  200.                     disp -= (int) (amplitude * Math.cos(2.0 * Math.PI * count / period));
  201.                 handleDisplacement(disp);
  202.             }
  203.             if (fadeIncremented)
  204.                 fadeIndex++;
  205.             else
  206.                 fadeIndex--;
  207.             if (fadeIndex >= fadePixels.length)
  208.             {
  209.                 fadeIndex--;
  210.                 fadeIncremented = false;
  211.             }
  212.             else if (fadeIndex < 0)
  213.             {
  214.                 fadeIndex++;
  215.                 fadeIncremented = true;
  216.             }
  217.             try
  218.             {
  219.                 scrollThread.sleep(delay);
  220.             }
  221.             catch (InterruptedException e)
  222.             {
  223.                 scrollThread = null;
  224.             }
  225.         }
  226.     }
  227.  
  228.     /**
  229.      *  Performs a binary search of the hyperlink to which the cursor is pointing.
  230.      *  @return the index of the link
  231.      */
  232.     private int searchActiveLink()
  233.     {
  234.         int top = linkRanges[currentText].length - 1;
  235.         int bottom = 0;
  236.         while (bottom <= top)
  237.         {
  238.             int middle = (top + bottom) / 2;
  239.             int position = mouseX - offset;
  240.             if (position < linkRanges[currentText][middle][0])
  241.                 top = middle - 1;
  242.             else if (position > linkRanges[currentText][middle][1])
  243.                 bottom = middle + 1;
  244.             else
  245.                 return middle;
  246.         }
  247.         return -1;
  248.     }
  249.  
  250.     /**
  251.      *  Redraws the portion of the canvas that has been affected since the last animation frame.
  252.      *  @param  g the graphics context
  253.      *  @see    #paint(Graphics)
  254.      */
  255.     public void update(Graphics g)
  256.     {
  257.         if (textPixels != null)
  258.         {
  259.             int prevLink = currentLink;
  260.             if (entered && textDisp <= mouseY && mouseY <= textDisp + textHeight && linkRanges[currentText].length > 0)
  261.             {
  262.                 currentLink = searchActiveLink();
  263.                 if (currentLink != prevLink)
  264.                     showStatus((currentLink == -1) ? "" : ((String) links[currentText][currentLink].get("alt")));
  265.             }
  266.             else if (prevLink >= 0)
  267.             {
  268.                 showStatus("");
  269.                 currentLink = -1;
  270.             }
  271.             overlayPixels(backPixels, width, textHeight, 0, 0, width, textHeight,
  272.                           canvasPixels, width, textHeight, 0, 0, true, false);
  273.             overlayPixels(textPixels[currentText], textWidths[currentText], textHeight,
  274.                           0, 0, textWidths[currentText], textHeight,
  275.                           canvasPixels, width, textHeight, offset, 0, false, true);
  276.             Image textImage = createImage(new MemoryImageSource(width, textHeight, canvasPixels, 0, width));
  277.             g.drawImage(textImage, 0, textDisp, null);
  278.             textImage.flush();
  279.         }
  280.     }
  281.  
  282.     /**
  283.      *  Paints the component. This method is called when the contents of the component should be
  284.      *  painted in response to the component first being shown or damage needing repair.
  285.      *  @param  g the graphics context
  286.      *  @see    #update(Graphics)
  287.      */
  288.     public void paint(Graphics g)
  289.     {
  290.         if (bgPixels != null)
  291.         {
  292.             if (bgDisplay == TILE)
  293.                 for (int j = 0; j < height; j += bgSize.height)
  294.                     for (int i = 0; i < width; i += bgSize.width)
  295.                         g.drawImage(bgImage, i, j, bgColor, null);
  296.             else if (bgDisplay == CENTER)
  297.             {
  298.                 g.setColor(bgColor);
  299.                 g.fillRect(0, 0, width, height);
  300.                 g.drawImage(bgImage, (width - bgSize.width) / 2, (height - bgSize.height) / 2, null);
  301.             }
  302.         }
  303.         else
  304.         {
  305.             g.setColor(bgColor);
  306.             g.fillRect(0, 0, width, height);
  307.         }
  308.         update(g);
  309.     }
  310.  
  311.     /**
  312.      *  Paints the component and returns as soon as the component has completed painting itself.
  313.      */
  314.     private void paint()
  315.     {
  316.         Graphics graphics = getGraphics();
  317.         if (graphics != null)
  318.         {
  319.             paint(graphics);
  320.             graphics.dispose();
  321.         }
  322.     }
  323.  
  324.     /**
  325.      *  Repaints the component when the image has changed. This method begins processing the
  326.      *  background and foreground images after it has been notified that they have been completely
  327.      *  loaded.
  328.      *  @param  img     the image being observed
  329.      *  @param  flags   see imageUpdate for more information
  330.      *  @param  x       the x coordinate
  331.      *  @param  y       the y coordinate
  332.      *  @param  width   the width
  333.      *  @param  height  the height
  334.      *  @return true if the flags indicate that the image is completely loaded; false otherwise
  335.      */
  336.     public boolean imageUpdate(Image img, int flags, int x, int y, int w, int h)
  337.     {
  338.         if ((flags & ALLBITS) != 0)
  339.         {
  340.             if (img == bgImage)
  341.                 setBackground(bgImage, bgColor, bgDisplay);
  342.             else if (img == fgImage)
  343.                 setForeground(fgImage);
  344.         }
  345.         return super.imageUpdate(img, flags, x, y, w, h);
  346.     }
  347.  
  348.     /**
  349.      *  Determines the current text and offset
  350.      *  @param  disp    the displacement on the current offset
  351.      */
  352.     private void handleDisplacement(int disp)
  353.     {
  354.         offset += disp;
  355.         if (offset + textWidths[currentText] < 0)
  356.         {
  357.             offset = width + offset + textWidths[currentText];
  358.             currentText++;
  359.             if (currentText == textCount)
  360.                 currentText = 0;
  361.         }
  362.         else if (offset > width)
  363.         {
  364.             currentText--;
  365.             if (currentText < 0)
  366.                 currentText = textCount - 1;
  367.             offset = offset - width - textWidths[currentText];
  368.         }
  369.     }
  370.  
  371.     /**
  372.      *  This method is called when the mouse first enters this component.
  373.      *  @param  event   the event that caused the action 
  374.      *  @param  x       the x coordinate
  375.      *  @param  y       the y coordiante
  376.      *  @return true
  377.      */
  378.     public boolean mouseEnter(Event event, int x, int y)
  379.     {
  380.         mouseX = x;
  381.         mouseY = y;
  382.         entered = true;
  383.         return true;
  384.     }
  385.  
  386.     /**
  387.      *  This method is called when the mouse exits this component.
  388.      *  @param  event   the event that caused the action 
  389.      *  @param  x       the x coordinate
  390.      *  @param  y       the y coordiante
  391.      *  @return true
  392.      */
  393.     public boolean mouseExit(Event event, int x, int y)
  394.     {
  395.         entered = false;
  396.         return true;
  397.     }
  398.  
  399.     /**
  400.      *  This method is called when the mouse button is released inside this component.
  401.      *  @param  event   the event that caused the action 
  402.      *  @param  x       the x coordinate
  403.      *  @param  y       the y coordiante
  404.      *  @return true
  405.      */
  406.     public boolean mouseUp(Event event, int x, int y)
  407.     {
  408.         pressed = false;
  409.         if (currentLink >= 0 && currentLink == pressedLink)
  410.         {
  411.             String target = (String) links[currentText][currentLink].get("target");
  412.             getAppletContext().showDocument(
  413.                 (URL) links[currentText][currentLink].get("href"),
  414.                 (target == null) ? defaultTarget : target);
  415.         }
  416.         return true;
  417.     }
  418.  
  419.     /**
  420.      *  This method is called when the mouse button is pushed inside this component.
  421.      *  @param  event   the event that caused the action 
  422.      *  @param  x       the x coordinate
  423.      *  @param  y       the y coordiante
  424.      *  @return true
  425.      */
  426.     public boolean mouseDown(Event event, int x, int y)
  427.     {
  428.         pressed = true;
  429.         pressedLink = (currentLink >= 0) ? currentLink : -1;
  430.         return true;
  431.     }
  432.  
  433.     /**
  434.      *  This method is called when the mouse is moved inside this component with the mouse button
  435.      *  not pushed.
  436.      *  @param  event   the event that caused the action 
  437.      *  @param  x       the x coordinate
  438.      *  @param  y       the y coordiante
  439.      *  @return true
  440.      */
  441.     public boolean mouseMove(Event event, int x, int y)
  442.     {
  443.         mouseX = x;
  444.         mouseY = y;
  445.         if (scrollThread == null)
  446.             repaint();
  447.         return true;
  448.     }
  449.  
  450.     /**
  451.      *  This method is called when the mouse button is moved inside this component with the button
  452.      *  pushed.
  453.      *  @param  event   the event that caused the action 
  454.      *  @param  x       the x coordinate
  455.      *  @param  y       the y coordiante
  456.      *  @return true
  457.      */
  458.     public boolean mouseDrag(Event event, int x, int y)
  459.     {
  460.         handleDisplacement(x - mouseX);
  461.         mouseX = x;
  462.         mouseY = y;
  463.         repaint();
  464.         return true;
  465.     }
  466.  
  467.     /**
  468.      *  Resizes this component to the specified width and height.
  469.      *  @param  w   the width
  470.      *  @param  h   the height
  471.      */
  472.     public void resize(int w, int h)
  473.     {
  474.         width = w;
  475.         height = h;
  476.         super.resize(w, h);
  477.         width = size().width;
  478.         height = size().height;
  479.         updateBackground();
  480.     }
  481.  
  482.     /**
  483.      *  Determines the preferred size of the component.
  484.      *  @return the preferred size of this component
  485.      */
  486.     public Dimension preferredSize()
  487.     {
  488.         return new Dimension(width, height);
  489.     }
  490.  
  491.     /**
  492.      *  Returns the background color.
  493.      *  @return the background color
  494.      */
  495.     public Color getBackgroundColor()
  496.     {
  497.         return new Color(bgColor.getRGB());
  498.     }
  499.  
  500.     /**
  501.      *  Returns the background image.
  502.      *  @return the background image
  503.      */
  504.     public Image getBackgroundImage()
  505.     {
  506.         return bgImage;
  507.     }
  508.  
  509.     /**
  510.      *  Returns the foreground color.
  511.      *  @return the foreground color
  512.      */
  513.     public Color getForegroundColor()
  514.     {
  515.         return new Color(fgColor.getRGB());
  516.     }
  517.  
  518.     /**
  519.      *  Returns the foreground image.
  520.      *  @return the foreground image
  521.      */
  522.     public Image getForegroundImage()
  523.     {
  524.         return fgImage;
  525.     }
  526.  
  527.     /**
  528.      *  Returns the hyperlink color.
  529.      *  @return the hyperlink color
  530.      */
  531.     public Color getLinkColor()
  532.     {
  533.         return new Color(linkColor.getRGB());
  534.     }
  535.  
  536.     /**
  537.      *  Returns the active hyperlink color.
  538.      *  @return the active hyperlink color
  539.      */
  540.     public Color getActiveLinkColor()
  541.     {
  542.         return new Color(activeLinkColor.getRGB());
  543.     }
  544.  
  545.     /**
  546.      *  Returns the speed
  547.      *  @return the number of pixels to shift between frames/iterations
  548.      */
  549.     public int getSpeed()
  550.     {
  551.         return speed;
  552.     }
  553.  
  554.     /**
  555.      *  Returns the delay between frames/iterations.
  556.      *  @return the delay in milliseconds
  557.      */
  558.     public int getDelay()
  559.     {
  560.         return delay;
  561.     }
  562.  
  563.     /**
  564.      *  Returns the amplitude of the oscillation.
  565.      *  @return the number of pixels by which to oscillate in either direction
  566.      */
  567.     public int getAmplitude()
  568.     {
  569.         return amplitude;
  570.     }
  571.  
  572.     /**
  573.      *  Returns the period of the oscillation.
  574.      *  @return the number of frames/iteration to complet an oscillation
  575.      */
  576.     public int getPeriod()
  577.     {
  578.         return period;
  579.     }
  580.  
  581.     /**
  582.      *  Returns the default target frame.
  583.      *  @return the name of default target frame
  584.      */
  585.     public String getTarget()
  586.     {
  587.         return new String(defaultTarget);
  588.     }
  589.  
  590.     /**
  591.      *  Updates the background pixels. This method should be called when any aspect of the
  592.      *  background has changed.
  593.      */
  594.     private void updateBackground()
  595.     {
  596.         if (textPixels != null)
  597.         {
  598.             textDisp        = (height - textHeight) / 2;
  599.             backPixels      = new int[width * textHeight];
  600.             canvasPixels    = new int[width * textHeight];
  601.             int bgPixel = bgColor.getRGB();
  602.             int i, j;
  603.             for (i = 0; i < width * textHeight; i++)
  604.                 canvasPixels[i] = 0xFF000000 | bgPixel;
  605.             if (bgPixels != null)
  606.             {
  607.                 if (bgDisplay == TILE)
  608.                     for (j = -(textDisp % bgSize.height); j < textHeight; j += bgSize.height)
  609.                         for (i = 0; i < width; i += bgSize.width)
  610.                             overlayPixels(bgPixels, bgSize.width, bgSize.height, 0, 0, bgSize.width, bgSize.height,
  611.                                           canvasPixels, width, textHeight, i, j, false, false);
  612.                 else if (bgDisplay == CENTER)
  613.                     overlayPixels(bgPixels, bgSize.width, bgSize.height, 0, 0, bgSize.width, bgSize.height,
  614.                                   canvasPixels, width, textHeight,
  615.                                   (width - bgSize.width) / 2, (textHeight - bgSize.height) / 2, false, false);
  616.             }
  617.             for (i = 0; i < width * textHeight; i++)
  618.                 backPixels[i] = 0xFF000000 | (0xFFFFFF & canvasPixels[i]);
  619.         }
  620.     }
  621.  
  622.     /**
  623.      *  Sets the background image.
  624.      *  @param  image   the image to be displayed in the background
  625.      *  @param  color   the background color to use for any transparent pixels
  626.      *  @param  display the method by which to display the image
  627.      *  @see    #TILE
  628.      *  @see    #CENTER
  629.      */
  630.     public void setBackground(Image image, Color color, int display)
  631.     {
  632.         bgImage     = image;
  633.         bgDisplay   = display % 2;
  634.         if (color != null)
  635.             bgColor = new Color(color.getRGB());
  636.         if (prepareImage(image, this))
  637.         {
  638.             bgPixels        = getPixels(bgImage);
  639.             bgSize.width    = bgImage.getWidth(null);
  640.             bgSize.height   = bgImage.getHeight(null);
  641.             updateBackground();
  642.             paint();
  643.         }
  644.     }
  645.  
  646.     /**
  647.      *  Sets the background to a solid color.
  648.      *  @param  color   the background color
  649.      */
  650.     public void setBackground(Color color)
  651.     {
  652.         bgColor     = new Color(color.getRGB());
  653.         bgPixels    = null;
  654.         bgImage     = null;
  655.         updateBackground();
  656.         paint();
  657.     }
  658.  
  659.     /**
  660.      *  Sets the foreground image pattern.
  661.      *  @param  image   the foreground image
  662.      */
  663.     public void setForeground(Image image)
  664.     {
  665.         fgImage = image;
  666.         if (prepareImage(image, this))
  667.         {
  668.             fgPixels        = getPixels(fgImage);
  669.             fgSize.width    = fgImage.getWidth(null);
  670.             fgSize.height   = fgImage.getHeight(null);
  671.             updateForeground();
  672.             repaint();
  673.         }
  674.     }
  675.  
  676.     /**
  677.      *  Sets the foreground to a solid color.
  678.      *  @param  color   the foreground color
  679.      */
  680.     public void setForeground(Color color)
  681.     {
  682.         fgColor = new Color(color.getRGB());
  683.         fgPixels = null;
  684.         updateForeground();
  685.         repaint();
  686.     }
  687.  
  688.     /**
  689.      *  Updates the foreground pixels. This method should be called when any aspect of the
  690.      *  foreground has changed.
  691.      */
  692.     private void updateForeground()
  693.     {
  694.         if (textPixels != null)
  695.         {
  696.             if (fgPixels != null)
  697.             {
  698.                 for (int k = 0; k < textCount; k++)
  699.                     for (int j = 0; j < textHeight; j++)
  700.                         for (int i = 0; i < textWidths[k]; i++)
  701.                         {
  702.                             int index = j * textWidths[k] + i;
  703.                             textPixels[k][index] = (textPixels[k][index] & 0xFF000000)
  704.                                 | (0xFFFFFF & fgPixels[(j % fgSize.height) * fgSize.width + (i % fgSize.width)]);
  705.                         }
  706.             }
  707.             else
  708.             {
  709.                 int pixel = 0xFFFFFF & fgColor.getRGB();
  710.                 for (int k = 0; k < textCount; k++)
  711.                     for (int i = 0; i < textWidths[k] * textHeight; i++)
  712.                         textPixels[k][i] = (textPixels[k][i] & 0xFF000000) | pixel;
  713.             }
  714.             for (int i = 0; i < textCount; i++)
  715.                 for (int j = 0; j < linkRanges[i].length; j++)
  716.                 {
  717.                     int w = linkRanges[i][j][1] - linkRanges[i][j][0] + 1;
  718.                     colorPixels(textPixels[i], textWidths[i], textHeight, linkRanges[i][j][0] - w / 8, 0,
  719.                                 5 * w / 4, textHeight, linkColor.getRGB());
  720.                 }
  721.         }
  722.     }
  723.  
  724.     /**
  725.      *  Sets the color of the hyperlinks.
  726.      *  @param  color   the hyperlink color
  727.      */
  728.     public void setLinkColor(Color color)
  729.     {
  730.         linkColor = new Color(color.getRGB());
  731.         updateForeground();
  732.     }
  733.  
  734.     /**
  735.      *  Sets the color of the hyperlink to which the mouse cursor is pointing
  736.      *  @param  color   the hyperlink color
  737.      */
  738.     public void setActiveLinkColor(Color color)
  739.     {
  740.         activeLinkColor = new Color(color.getRGB());
  741.     }
  742.  
  743.     /**
  744.      *  Sets the speed of the scrolling motion
  745.      *  @param  speed   the number of pixels per animation frame
  746.      */
  747.     public void setSpeed(int speed)
  748.     {
  749.         this.speed = speed;
  750.     }
  751.  
  752.     /**
  753.      *  Sets the frame rate.
  754.      *  @param  delay   the number of milliseconds between animation frames
  755.      */
  756.     public void setDelay(int delay)
  757.     {
  758.         this.delay = delay;
  759.     }
  760.  
  761.     /**
  762.      *  Sets the amplitude of the oscillation.
  763.      *  @param  amplitude   the number of pixels by which to oscillate in either direction
  764.      */
  765.     public void setAmplitude(int amplitude)
  766.     {
  767.         this.amplitude = amplitude;
  768.     }
  769.  
  770.     /**
  771.      *  Sets the period (inverse of frequency) of the oscillation.
  772.      *  @param  period  the number of iterations/frames to complete one oscillation
  773.      */
  774.     public void setPeriod(int period)
  775.     {
  776.         this.period = period;
  777.     }
  778.  
  779.     /**
  780.      *  Sets the fading trail effect.
  781.      *  @param  intensity   the intensity of the trail effect. Values range from 0.0 to 1.0.
  782.      */
  783.     public void setTrail(double intensity)
  784.     {
  785.         clearAmount = 0xFF - (int) (0xFF * normalize(intensity));
  786.     }
  787.  
  788.     /**
  789.      *  Sets the default target frame.
  790.      *  @param  target the default target
  791.      */
  792.     public void setTarget(String target)
  793.     {
  794.         defaultTarget = (target == null) ? "_self" : new String(target);
  795.     }
  796.  
  797.     /**
  798.      *  Sets the text to be scrolled from a text file.
  799.      *  @param  path    the path of the file that contains the text to be scrolled
  800.      *  @param  font    the font to use in displaying the text
  801.      */
  802.     public void setText(URL path, Font font)
  803.     {
  804.         String defaultText = "PhantomScroll was developed by <a href=\"mailto:um@mail.com\">Uldarico Muico Jr.</a>";
  805.         String contents = defaultText;
  806.         if (path != null)
  807.         {
  808.             try
  809.             {
  810.                 DataInputStream stream = new DataInputStream(path.openConnection().getInputStream());
  811.                 contents = stream.readLine();
  812.                 String line;
  813.                 while ((line = stream.readLine()) != null)
  814.                     contents = contents + line;
  815.                 stream.close();
  816.  
  817.             }
  818.             catch (IOException e)
  819.             {
  820.                 e.printStackTrace();
  821.                 contents = defaultText;
  822.             }
  823.         }
  824.         setText(contents, font);
  825.     }
  826.  
  827.     /**
  828.      *  Sets the text to be scrolled. Hyperlinks and breaks may be indicated as in HTML.
  829.      *  @param  text    the text to be scrolled
  830.      *  @param  font    the font to use in displaying the text
  831.      */
  832.     public void setText(String text, Font font)
  833.     {
  834.         text = text + "<br>";
  835.         Vector  modifiedText    = new Vector();
  836.         Vector  stack           = new Vector();
  837.         Vector  data            = new Vector();
  838.         int     prevIndex       = 0;
  839.         textCount = 0;
  840.         while (true)
  841.         {
  842.             int index = text.indexOf('<', prevIndex);
  843.             if (index == -1) break;
  844.             int endIndex = searchBracket(text, index);
  845.             Properties tagProps = parseTagProperties(text.substring(index + 1, endIndex));
  846.             while (modifiedText.size() <= textCount)
  847.                 modifiedText.addElement(new String());
  848.             while (data.size() <= textCount)
  849.                 data.addElement(new Vector());
  850.             String s = (String) modifiedText.elementAt(textCount);
  851.             modifiedText.setElementAt(s.concat(text.substring(prevIndex, index)), textCount);
  852.             String type = tagProps.getProperty("type");
  853.             if (type.equals("br"))
  854.             {
  855.                 if (stack.size() > 0)
  856.                 {
  857.                     String tail = text.substring(endIndex + 1);
  858.                     text = text.substring(0, endIndex + 1);
  859.                     for (int i = 0; i < stack.size(); i++)
  860.                     {
  861.                         Hashtable table = (Hashtable) stack.elementAt(i);
  862.                         String t = (String) table.get("type");
  863.                         text = text + "</" + t + ">";
  864.                         tail = ((String) table.get("tag")) + tail;
  865.                         table.remove("tag");
  866.                     }
  867.                     text = text + "<br>" + tail;
  868.                 }
  869.                 else
  870.                     textCount++;
  871.             }
  872.             else if (type.equals("a"))
  873.             {
  874.                 URL         url     = parseURL(tagProps.getProperty("href"), getDocumentBase(), false);
  875.                 String      target  = tagProps.getProperty("target");
  876.                 String      alt     = tagProps.getProperty("alt");
  877.                 Hashtable   table   = new Hashtable();
  878.                 table.put("type", "a");
  879.                 table.put("href", url);
  880.                 if (target != null)
  881.                     table.put("target", target);
  882.                 int[] range = new int[2];
  883.                 range[0] = ((String) modifiedText.elementAt(textCount)).length();
  884.                 table.put("range", range);
  885.                 table.put("tag", text.substring(index, endIndex + 1));
  886.                 table.put("alt", (alt == null) ? url.toString() : alt);
  887.                 stack.addElement(table);
  888.             }
  889.             else if (type.equals("/a"))
  890.             {
  891.                 int i = stack.size() - 1;
  892.                 while (i >= 0)
  893.                 {
  894.                     Hashtable table = (Hashtable) stack.elementAt(i);
  895.                     if (table.get("type") == "a")
  896.                     {
  897.                         int[] range = (int[]) table.get("range");
  898.                         range[1] = ((String) modifiedText.elementAt(textCount)).length() - 1;
  899.                         ((Vector) data.elementAt(textCount)).addElement(table);
  900.                         stack.removeElementAt(i);
  901.                         break;
  902.                     }
  903.                     i--;
  904.                 }
  905.             }
  906.             prevIndex = endIndex + 1;
  907.         }
  908.         stack.removeAllElements();
  909.         FontMetrics metrics = getFontMetrics(font);
  910.         textWidths  = new int[textCount];
  911.         links       = new Hashtable[textCount][];
  912.         linkRanges  = new int[textCount][][];
  913.         textPixels  = new int[textCount][];
  914.         for (int i = 0; i < textCount; i++)
  915.         {
  916.             String      t       = (String) modifiedText.elementAt(i);
  917.             int[]       buffer  = createTextPixels(t, font, fgColor);
  918.             Dimension   size    = getTextSize(t, metrics);
  919.             textWidths[i]   = size.width;
  920.             textHeight      = size.height;
  921.             textPixels[i]   = new int[textWidths[i] * textHeight];
  922.             Vector textData = (Vector) data.elementAt(i);
  923.             linkRanges[i]   = new int[textData.size()][];
  924.             links[i]        = new Hashtable[textData.size()];
  925.             for (int j = 0; j < textData.size(); j++)
  926.             {
  927.                 links[i][j]         = (Hashtable) textData.elementAt(j);
  928.                 int[] indices       = (int[]) links[i][j].get("range");
  929.                 linkRanges[i][j]    = new int[2];
  930.                 linkRanges[i][j][0] = metrics.stringWidth(t.substring(0, indices[0])) + metrics.charWidth(' ');
  931.                 linkRanges[i][j][1] = linkRanges[i][j][0] + metrics.stringWidth(t.substring(indices[0], indices[1] + 1));
  932.                 for (int k = linkRanges[i][j][0]; k <= linkRanges[i][j][1] - 1; k++)
  933.                     buffer[(metrics.getAscent() + 2) * textWidths[i] + k] |= 0xFF000000;
  934.                             
  935.             }
  936.             filterPixels(buffer, textWidths[i], textHeight,
  937.                         0, 0, textWidths[i], textHeight,
  938.                         textPixels[i], textWidths[i], textHeight, 0, 0, 0);
  939.         }
  940.         offset = width;
  941.         updateBackground();
  942.         updateForeground();
  943.         createFade();
  944.     }
  945.  
  946.     /**
  947.      *  Searches for a left angle bracket.
  948.      *  @param  s       the string in which to search
  949.      *  @param  index   the starting index into the string
  950.      *  @return the index of the first matching bracket
  951.      */
  952.     private int searchBracket(String s, int index)
  953.     {
  954.         while (true)
  955.         {
  956.             char ch = s.charAt(index);
  957.             if (ch == '>')
  958.                 return index;
  959.             else if (ch == '"' || ch == '\'')
  960.                 index = s.indexOf(ch, index + 1);
  961.             if (index == -1)
  962.                 return -1;
  963.             index++;
  964.         }
  965.     }
  966.  
  967.     /**
  968.      *  Parses the contents of an HTML tag.
  969.      *  @param  value   the text to be parsed excluding the tag delimeters
  970.      *  @return the properties
  971.      */
  972.     private Properties parseTagProperties(String value)
  973.     {
  974.         Properties      properties      = new Properties();
  975.         StreamTokenizer tokenizer       = new StreamTokenizer(new StringBufferInputStream(value));
  976.         String          previousToken   = null;
  977.         tokenizer.resetSyntax();
  978.         tokenizer.wordChars(0x21, 0xFFFF);
  979.         tokenizer.quoteChar('\'');
  980.         tokenizer.quoteChar('"');
  981.         tokenizer.ordinaryChar('=');
  982.         try
  983.         {
  984.             if (tokenizer.nextToken() != tokenizer.TT_EOF)
  985.                 properties.put("type", tokenizer.sval.toLowerCase());
  986.             int type = tokenizer.nextToken();
  987.             while (type != tokenizer.TT_EOF)
  988.             {
  989.                 if (type == '=' && previousToken != null)
  990.                 {
  991.                     tokenizer.nextToken();
  992.                     properties.put(previousToken.toLowerCase(), tokenizer.sval);
  993.                     previousToken = null;
  994.                 }
  995.                 else if (type == tokenizer.TT_WORD)
  996.                     previousToken = tokenizer.sval;
  997.                 type = tokenizer.nextToken();
  998.             }
  999.         }
  1000.         catch (IOException e)
  1001.         {
  1002.             e.printStackTrace();
  1003.         }
  1004.         return properties;
  1005.     }
  1006.     
  1007.     /**
  1008.      *  Creates the alpha values for those pixels near the left and right edges of the applet.
  1009.      */
  1010.     private void createFade()
  1011.     {
  1012.         double  fadeDepth   = 0.8;
  1013.         int     fadePeriod  = textHeight;
  1014.         int     upperX      = width / 2 + 1;
  1015.         int     upperY      = fadePeriod / 2 + 1;
  1016.         fadePixels = new int[upperY][];
  1017.         for (int j = 0; j < upperY; j++)
  1018.         {
  1019.             fadePixels[j] = new int[upperX];
  1020.             double penetration = fadeDepth * (0.65 - 0.35 * Math.cos(2.0 * Math.PI * j / fadePeriod));
  1021.             int i = 0;
  1022.             while (i < (int) (penetration * upperX))
  1023.             {
  1024.                 fadePixels[j][i] = (int) ((0.5 - 0.5 * Math.cos(2.0 * Math.PI / penetration * i / width)) * 0xFF);
  1025.                 i++;
  1026.             }
  1027.             while (i < upperX)
  1028.             {
  1029.                 fadePixels[j][i] = 0xFF;
  1030.                 i++;
  1031.             }
  1032.         }
  1033.     }
  1034.  
  1035.     /**
  1036.      *  Computes the dimensions of text to be displayed on the screen.
  1037.      *  @param  text    the text to be displayed
  1038.      *  @param  metrics the font metrics
  1039.      *  @return the size
  1040.      */
  1041.     private Dimension getTextSize(String text, FontMetrics metrics)
  1042.     {
  1043.         Dimension textSize = new Dimension();
  1044.         textSize.width = metrics.stringWidth(text) + 2 * metrics.charWidth(' ');
  1045.         textSize.height += metrics.getAscent() + metrics.getDescent() + 3;
  1046.         return textSize;
  1047.     }
  1048.  
  1049.     /**
  1050.      *  Creates the pixels with the alpha mask that represents the message to be displayed.
  1051.      *  @param  text    the string to be displayed
  1052.      *  @param  font    the font of the text
  1053.      *  @param  color   the initial color of the text
  1054.      */
  1055.     private int[] createTextPixels(String text, Font font, Color color)
  1056.     {
  1057.         FontMetrics metrics     = getFontMetrics(font);
  1058.         Dimension   textSize    = getTextSize(text, metrics);
  1059.         int         size        = textSize.width * textSize.height;
  1060.         int         lineHeight  = metrics.getAscent() + metrics.getDescent();
  1061.         int         pixel       = ((color == null) ? 0xFF7F7F7F : color.getRGB()) & 0xFFFFFF;
  1062.         Image       textImage   = createImage(textSize.width, textSize.height);
  1063.         Graphics    graphics    = textImage.getGraphics();
  1064.         int[]       values      = new int[4];
  1065.         graphics.setColor(new Color(0xFF000000));
  1066.         graphics.fillRect(0, 0, textSize.width, textSize.height);
  1067.         graphics.setColor(new Color(0xFFFFFFFF));
  1068.         graphics.setFont(font);
  1069.         graphics.drawString(text, (textSize.width - metrics.stringWidth(text)) / 2, metrics.getAscent());
  1070.         graphics.dispose();
  1071.         int[] pixels = getPixels(textImage);
  1072.         for (int index = 0; index < size; index++)
  1073.         {
  1074.             decomposePixel(pixels[index], values);
  1075.             int value = ((values[1] + values[2] + values[3]) / 3) << 24;
  1076.             pixels[index] = value | pixel;
  1077.         }
  1078.         return pixels;
  1079.     }
  1080.  
  1081.     /**
  1082.      *  Parses a string representation of an URL.
  1083.      *  @param  value           the string representation
  1084.      *  @param  defaultValue    the value if the the parsing fails
  1085.      *  @param  codeBased       indicates whether the relative URL is code-based or document-based
  1086.      *  @return the parsed URL
  1087.      */
  1088.     private URL parseURL(String value, URL defaultValue, boolean codeBased)
  1089.     {
  1090.         if (value == null)
  1091.             return defaultValue;
  1092.         try
  1093.         {
  1094.             if (value.indexOf(":") != -1)
  1095.                 return new URL(value);
  1096.             else
  1097.                 return new URL((codeBased) ? getCodeBase() : getDocumentBase(), value);
  1098.         }
  1099.         catch (MalformedURLException e)
  1100.         {
  1101.             e.printStackTrace();
  1102.             return defaultValue;
  1103.         }
  1104.     }
  1105.  
  1106.     /**
  1107.      *  Parses a string representation of an image. The string is simply the URL of the image.
  1108.      *  @param  value           the string representation
  1109.      *  @param  defaultValue    the value if the the parsing fails
  1110.      *  @return the image
  1111.      */
  1112.     private Image parseImage(String value, Image defaultValue)
  1113.     {
  1114.         if (value != null)
  1115.             return getImage(parseURL(value, null, true));
  1116.         else
  1117.             return defaultValue;
  1118.     }
  1119.  
  1120.     /**
  1121.      *  Parses a string representation of an integer.
  1122.      *  @param  value           the string representation
  1123.      *  @param  defaultValue    the value if the the parsing fails
  1124.      *  @return the parsed integer
  1125.      */
  1126.     private int parseInt(String value, int defaultValue)
  1127.     {
  1128.         if (value == null)
  1129.             return defaultValue;
  1130.         try
  1131.         {
  1132.             return Integer.parseInt(value);
  1133.         }
  1134.         catch (NumberFormatException e)
  1135.         {
  1136.             e.printStackTrace();
  1137.             return defaultValue;
  1138.         }
  1139.     }
  1140.  
  1141.     /**
  1142.      *  Parses a string representation of a floating-point number in the interval 0.0 and 1.0.
  1143.      *  @param  value           the string representation
  1144.      *  @param  defaultValue    the value if the the parsing fails
  1145.      *  @return the parsed floating-point number
  1146.      */
  1147.     private double parseUnity(String value, double defaultValue)
  1148.     {
  1149.         if (value == null)
  1150.             return defaultValue;
  1151.         try
  1152.         {
  1153.             return normalize(Double.valueOf(value).doubleValue());
  1154.         }
  1155.         catch (NumberFormatException e)
  1156.         {
  1157.             e.printStackTrace();
  1158.             return defaultValue;
  1159.         }
  1160.     }
  1161.  
  1162.     /**
  1163.      *  Parses a string representation of a color in RGB decimal or hexadecimal format.
  1164.      *  @param  value           the string representation
  1165.      *  @param  defaultValue    the value if the the parsing fails
  1166.      *  @return the parsed color
  1167.      */
  1168.     private Color parseColor(String value, Color defaultValue)
  1169.     {
  1170.         if (value == null)
  1171.             return defaultValue;
  1172.         try
  1173.         {
  1174.             if (value.indexOf(" ") != -1)
  1175.             {
  1176.                 StringTokenizer tokenizer = new StringTokenizer(value);
  1177.                 int pixel = 0xFF;
  1178.                 while (tokenizer.hasMoreTokens())
  1179.                     pixel = (pixel << 8) | Integer.parseInt(tokenizer.nextToken());
  1180.                 return new Color(pixel);
  1181.             }
  1182.             else
  1183.             {
  1184.                 int pixel = Integer.parseInt(value, 16);
  1185.                 return new Color(0xFF000000 | pixel);
  1186.             }
  1187.         }
  1188.         catch (NumberFormatException e)
  1189.         {
  1190.             e.printStackTrace();
  1191.             return defaultValue;
  1192.         }
  1193.     }
  1194.  
  1195.     /**
  1196.      *  Retrieves the pixels of an image.
  1197.      *  @param  image   the image from which to get the pixels
  1198.      *  @return the pixels
  1199.      */
  1200.     private int[] getPixels(Image image)
  1201.     {
  1202.         int             width   = image.getWidth(null);
  1203.         int             height  = image.getHeight(null);
  1204.         int[]           pixels  = new int[width * height];
  1205.         PixelGrabber    grabber = new PixelGrabber(image, 0, 0, width, height, pixels, 0, width);
  1206.         try
  1207.         {
  1208.             grabber.grabPixels();
  1209.         }
  1210.         catch (InterruptedException e)
  1211.         {
  1212.             e.printStackTrace();
  1213.             return null;
  1214.         }
  1215.         return pixels;
  1216.     }
  1217.  
  1218.     /**
  1219.      *  Restrains a floating-point number to the interval 0.0 to 1.0.
  1220.      *  @param  x   the number to correct
  1221.      *  @return the corrected number
  1222.      */
  1223.     private double normalize(double x)
  1224.     {
  1225.         if (x < 0.0)
  1226.             x = 0.0;
  1227.         else if (x > 1.0)
  1228.             x = 1.0;
  1229.         return x;
  1230.     }
  1231.  
  1232.     /**
  1233.      *  Computes the effective bounds for an image region that linearly maps to another
  1234.      *  @param  srcW    the width of the source image
  1235.      *  @param  srcH    the height of the source image
  1236.      *  @param  srcX    the x coordinate of the starting pixel in the source coordinate system
  1237.      *  @param  srcY    the y coordinate of the starting pixel in the source coordinate system
  1238.      *  @param  destW   the width of the destination image
  1239.      *  @param  destH   the height of the destination image
  1240.      *  @param  destX   the x coordinate of the starting pixel in the destination coordinate system
  1241.      *  @param  destY   the x coordinate of the starting pixel in the destination coordinate system
  1242.      *  @param  w       the width of the region
  1243.      *  @param  h       the height of the region
  1244.      *  @return the effective bounds in the destination coordinate system
  1245.      */
  1246.     private Rectangle effectiveBounds(int srcW, int srcH, int srcX, int srcY,
  1247.                                       int destW, int destH, int destX, int destY,
  1248.                                       int w, int h)
  1249.     {
  1250.         Rectangle rect = new Rectangle(srcX, srcY, w, h);
  1251.         rect = rect.intersection(new Rectangle(srcW, srcH));
  1252.         rect.translate(destX - srcX, destY - srcY);
  1253.         return rect.intersection(new Rectangle(destW, destH));
  1254.     }
  1255.  
  1256.     /**
  1257.      *  Decomposes the alpha, red, green, and blue components of a pixel.
  1258.      *  @param  pixel   the pixel to decompose
  1259.      *  @param  values  the buffer to hold the components
  1260.      */
  1261.     private void decomposePixel(int pixel, int[] values)
  1262.     {
  1263.         values[0] = (pixel >> 24) & 0xFF;
  1264.         values[1] = (pixel >> 16) & 0xFF;
  1265.         values[2] = (pixel >> 8)  & 0xFF;
  1266.         values[3] =  pixel        & 0xFF;
  1267.     }
  1268.  
  1269.     /**
  1270.      *  Applies a blur filter to a region of an image.
  1271.      *  @param  srcPixels   the source pixels
  1272.      *  @param  srcW        the width of the source image
  1273.      *  @param  srcH        the height of the source image
  1274.      *  @param  srcX        the x coordinate of the starting pixel in the source coordinate system
  1275.      *  @param  srcY        the y coordinate of the starting pixel in the source coordinate system
  1276.      *  @param  w           the width of the region
  1277.      *  @param  h           the height of the region
  1278.      *  @param  destPixels  the destination pixels
  1279.      *  @param  destW       the width of the destination image
  1280.      *  @param  destH       the height of the destination image
  1281.      *  @param  destX       the x coordinate of the starting pixel in the destination coordinate system
  1282.      *  @param  destY       the x coordinate of the starting pixel in the destination coordinate system
  1283.      *  @param  bgValue     the default background color
  1284.      */
  1285.     private void filterPixels(int[] srcPixels, int srcW, int srcH,
  1286.                              int srcX, int srcY, int w, int h,
  1287.                              int[] destPixels, int destW, int destH,
  1288.                              int destX, int destY, int bgValue)
  1289.     {
  1290.         Rectangle   rect        = effectiveBounds(srcW, srcH, srcX, srcY, destW, destH, destX, destY, w, h);
  1291.         int         i;
  1292.         int         bufferSize  = srcW * srcH;
  1293.         int         dx          = destX - srcX;
  1294.         int         dy          = destY - srcY;
  1295.         int[]       sums        = new int[3];
  1296.         bgValue = 0xFFFFFF | bgValue;
  1297.         rect.translate(-dx, -dy);
  1298.         int upperX = rect.x + rect.width + 1;
  1299.         int upperY = rect.y + rect.height + 1;
  1300.         for (int column = rect.x - 1; column < upperX; column++)
  1301.         {
  1302.             for (i = 0; i < 3; i++)
  1303.                 sums[i] = 0;
  1304.             for (int row = rect.y - 1; row < upperY; row++)
  1305.             {
  1306.                 int bottomIndex     = (row + 2) % 3;
  1307.                 int srcIndex        = (row + 1) * srcW + column;
  1308.                 sums[bottomIndex]   = 0;
  1309.                 for (i = -1; i <= 1; i++)
  1310.                 {
  1311.                     int batchIndex = srcIndex + i;
  1312.                     if (batchIndex >= 0 && batchIndex < bufferSize && column + i >= 0 && column + i < srcW)
  1313.                         sums[bottomIndex] += ((srcPixels[batchIndex] >> 24) & 0xFF) * ((i == 0) ? 3 : 1);
  1314.                 }
  1315.                 int destColumn  = column + dx;
  1316.                 int destRow     = row + dy;
  1317.                 if (destColumn >= 0 && destColumn < destW && destRow >= 0 && destRow < destH)
  1318.                 {
  1319.                     int sum         = 0;
  1320.                     int topIndex    = row + 3;
  1321.                     int centerIndex = topIndex + 1;
  1322.                     bottomIndex     = topIndex + 2;
  1323.                     for (i = topIndex; i <= bottomIndex; i++)
  1324.                         sum += ((i == centerIndex) ? 3 : 1) * sums[i % 3];
  1325.                     int pixel;
  1326.                     if (row >= 0 && row < srcH && column >= 0 & column < srcW)
  1327.                         pixel = srcPixels[row * srcW + column] & 0xFFFFFF;
  1328.                     else
  1329.                         pixel = bgValue;
  1330.                     destPixels[destRow * destW + destColumn] = ((sum / 25) << 24) | pixel;
  1331.                 }
  1332.             }
  1333.         }
  1334.     }
  1335.  
  1336.     /**
  1337.      *  Overlays a solid color onto a region of an image.
  1338.      *  @param  pixels  the pixels
  1339.      *  @param  width   the width of the image
  1340.      *  @param  height  the height of the image
  1341.      *  @param  x       the x coordinate of the starting pixel
  1342.      *  @param  x       the y coordinate of the starting pixel
  1343.      *  @param  w       the width of the region
  1344.      *  @param  h       the height of the region
  1345.      */
  1346.     private void colorPixels(int[] pixels, int width, int height, int x, int y, int w, int h, int color)
  1347.     {
  1348.         int         size        = width * height;
  1349.         int[]       values      = new int[4];
  1350.         int[]       cValues     = new int[4];
  1351.         float[]     HSBValues   = new float[3];
  1352.         float[]     HSBCValues  = new float[3];
  1353.         decomposePixel(color, cValues);
  1354.         Color.RGBtoHSB(cValues[1], cValues[2], cValues[3], HSBCValues);
  1355.         for (int j = 0; j < h; j++)
  1356.             if (y + j >= 0 && y + j < height)
  1357.                 for (int i = 0; i < w; i++)
  1358.                     if (x + i >= 0 && x + i < width)
  1359.                     {
  1360.                         double intensity = Math.cos(i * Math.PI / w);
  1361.                         for (int k = 0; k < 4; k++)
  1362.                             intensity *= intensity;
  1363.                         intensity = 1.0 - intensity;
  1364.                         int amount = (int) (0xFF * intensity);
  1365.                         int index = (j + y) * width + i + x;
  1366.                         decomposePixel(pixels[index], values);
  1367.                         Color.RGBtoHSB(values[1], values[2], values[3], HSBValues);
  1368.                         float saturation = HSBValues[1];
  1369.                         float brightness = HSBValues[2];
  1370.                         values[1]   = (amount * cValues[1] + (0xFF - amount) * values[1]) / 0xFF;
  1371.                         values[2]   = (amount * cValues[2] + (0xFF - amount) * values[2]) / 0xFF;
  1372.                         values[3]   = (amount * cValues[3] + (0xFF - amount) * values[3]) / 0xFF;
  1373.                         Color.RGBtoHSB(values[1], values[2], values[3], HSBValues);
  1374.                         HSBValues[1]    = (float) (intensity * HSBCValues[1] + (1.0 - intensity) * saturation);
  1375.                         HSBValues[2]    = (float) (0.4 * intensity * HSBCValues[2] + (1.0 - 0.4 * intensity) * brightness);
  1376.                         pixels[index]   = (values[0] << 24)
  1377.                                         | (0xFFFFFF & Color.HSBtoRGB(HSBValues[0], HSBValues[1], HSBValues[2]));
  1378.                     }
  1379.     }
  1380.  
  1381.     /**
  1382.      *  Overlays an image onto another. The alpha pixels determine the opacity of the images.
  1383.      *  @param  srcPixels   the source pixels
  1384.      *  @param  srcW        the width of the source image
  1385.      *  @param  srcH        the height of the source image
  1386.      *  @param  srcX        the x coordinate of the starting pixel in the source coordinate system
  1387.      *  @param  srcY        the y coordinate of the starting pixel in the source coordinate system
  1388.      *  @param  w           the width of the region
  1389.      *  @param  h           the height of the region
  1390.      *  @param  destPixels  the destination pixels
  1391.      *  @param  destW       the width of the destination image
  1392.      *  @param  destH       the height of the destination image
  1393.      *  @param  destX       the x coordinate of the starting pixel in the destination coordinate system
  1394.      *  @param  destY       the x coordinate of the starting pixel in the destination coordinate system
  1395.      *  @param  clearing    indicates that the source pixels are refreshing the canvas
  1396.      *  @param  fading      indicates that the source pixels are to be faded near the edges
  1397.      */
  1398.     private void overlayPixels(int[] srcPixels, int srcW, int srcH,
  1399.                                int srcX, int srcY, int w, int h,
  1400.                                int[] destPixels, int destW, int destH,
  1401.                                int destX, int destY, boolean clearing, boolean fading)
  1402.     {
  1403.         Rectangle   rect        = effectiveBounds(srcW, srcH, srcX, srcY, destW, destH, destX, destY, w, h);
  1404.         int         dx          = destX - srcX;
  1405.         int         dy          = destY - srcY;
  1406.         int         i;
  1407.         int         j           = rect.y * destW;
  1408.         int         k           = (rect.y - dy) * srcW;
  1409.         int         upperI      = rect.x + rect.width;
  1410.         int         upperJ      = (rect.y + rect.height) * destW;
  1411.         int         fadeI;
  1412.         int         fadeJ       = fadeIndex;
  1413.         int         center      = width / 2;
  1414.         int[]       srcRGB      = new int[4];
  1415.         int[]       destRGB     = new int[4];
  1416.         int[]       cRGB        = new int[4];
  1417.         boolean     incremented = fadeIncremented;
  1418.         decomposePixel(activeLinkColor.getRGB(), cRGB);
  1419.         while (j < upperJ)
  1420.         {
  1421.             if (rect.x <= center)
  1422.                 fadeI = rect.x;
  1423.             else
  1424.                 fadeI = 2 * center - rect.x;
  1425.             for (i = rect.x; i < upperI; i++)
  1426.             {
  1427.                 int srcPixel = srcPixels[k + i - dx];
  1428.                 decomposePixel(srcPixel, srcRGB);
  1429.                 if (srcRGB[0] != 0)
  1430.                 {
  1431.                     int destIndex = j + i;
  1432.                     int destPixel = destPixels[destIndex];
  1433.                     if (currentLink >= 0 && !clearing
  1434.                         && linkRanges[currentText][currentLink][0] <= i - destX
  1435.                         && i - destX <= linkRanges[currentText][currentLink][1])
  1436.                     {
  1437.                         srcRGB[1]   = cRGB[1];
  1438.                         srcRGB[2]   = cRGB[2];
  1439.                         srcRGB[3]   = cRGB[3];
  1440.                     }
  1441.                     if (clearing)
  1442.                         srcRGB[0]   = clearAmount;
  1443.                     else if (fading)
  1444.                         srcRGB[0]   = srcRGB[0] * fadePixels[fadeJ][fadeI] / 0xFF;
  1445.                     int destAmount = 0xFF - srcRGB[0];
  1446.                     decomposePixel(destPixel, destRGB);
  1447.                     destRGB[1]              = (destAmount * destRGB[1] + srcRGB[0] * srcRGB[1]) / 0xFF;
  1448.                     destRGB[2]              = (destAmount * destRGB[2] + srcRGB[0] * srcRGB[2]) / 0xFF;
  1449.                     destRGB[3]              = (destAmount * destRGB[3] + srcRGB[0] * srcRGB[3]) / 0xFF;
  1450.                     destPixels[destIndex]   = 0xFF000000
  1451.                                             | (destRGB[1] << 16)
  1452.                                             | (destRGB[2] << 8)
  1453.                                             |  destRGB[3];
  1454.                 }
  1455.                 if (i < center)
  1456.                     fadeI++;
  1457.                 else
  1458.                     fadeI--;
  1459.             }
  1460.             if (incremented)
  1461.                 fadeJ++;
  1462.             else
  1463.                 fadeJ--;
  1464.             if (fadeJ >= fadePixels.length)
  1465.             {
  1466.                 fadeJ--;
  1467.                 incremented = false;
  1468.             }
  1469.             else if (fadeJ < 0)
  1470.             {
  1471.                 fadeJ++;
  1472.                 incremented = true;
  1473.             }
  1474.             j += destW;
  1475.             k += srcW;
  1476.         }
  1477.     }
  1478. }
  1479.