advertisement
javaboutique
Search Tips
Articles  |   Tutorials  |   Reviews  |   Tools  |   by Category  |   by Date  |   by Name  |   Submit  |   Source  |   Forums  |  
javaboutique
Browse DevX


Partners & Affiliates











advertisement

PhantomScroll


/*
 *  PhantomScroll.java 1.0 25 Jan 2000
 *  Copyright (c) 2000 Uldarico Muico Jr.
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

import java.applet.*;
import java.awt.*;
import java.awt.image.*;
import java.net.*;
import java.io.*;
import java.util.*;

/**
 *  The PhantomScroll applet is a horizontal text scroller with fade effects.
 *  @version    1.0 25 Jan 2000
 *  @author     Uldarico Muico Jr.
 */
public class PhantomScroll extends Applet implements Runnable
{
    /**
     *  Ease-of-use constant for bgDisplay parameter of setBackground method. Indicates that the
     *  background image should be tiled.
     *  @see    #setBackground(Image, Color, int)
     */
    public  static final int    TILE            = 0;

    /**
     *  Ease-of-use constant for bgDisplay parameter of setBackground method. Indicates that the
     *  background image should be centered.
     *  @see    #setBackground(Image, Color, int)
     */
    public  static final int    CENTER          = 1;

    private Thread              scrollThread    = null;
    private int[]               bgPixels;
    private int[]               fgPixels;
    private int[]               backPixels;
    private int[]               canvasPixels;
    private int[]               textWidths;
    private int[][]             fadePixels;
    private int[][]             textPixels;
    private int[][][]           linkRanges;
    private Hashtable[][]       links;
    private Dimension           bgSize          = new Dimension();
    private Dimension           fgSize          = new Dimension();
    private Image               bgImage;
    private Image               fgImage;
    private int                 textHeight      = 0;
    private int                 currentText     = 0;
    private int                 width;
    private int                 height;
    private int                 offset;
    private int                 bgDisplay;
    private int                 textDisp;
    private int                 speed;
    private int                 delay;
    private int                 fadeIndex       = 0;
    private int                 clearAmount;
    private int                 count;
    private int                 textCount       = 0;
    private int                 currentLink     = -1;
    private int                 pressedLink     = -1;
    private int                 mouseX;
    private int                 mouseY;
    private int                 amplitude;
    private int                 period;
    private Color               bgColor;
    private Color               fgColor;
    private Color               linkColor;
    private Color               activeLinkColor;
    private boolean             fadeIncremented = true;
    private boolean             pressed;
    private boolean             entered         = false;
    private String              defaultTarget;

    /**
     *  Returns information about this applet.
     *  @return the information
     */
    public String getAppletInfo()
    {
        return  "Uldarico Muico Jr., um@mail.com\n\r"
             +  "PhantomScroll 1.0 January 25, 2000\n\r"
             +  "Copyright (c) 2000 Uldarico Muico, Jr.\n\r";
    }

    /**
     *  Returns information about the parameters that are understood by this applet.
     *  @return the information
     */
    public String[][] getParameterInfo()
    {
        String[][] info = { { "text",               "HTML",     "message to be displayed" },
                            { "textFile",           "URL",      "text file that contains message" },
                            { "font",               "String",   "name of font" },
                            { "fontStyle",          "0-4",      "font style" },
                            { "fontSize",           "int",      "font size" },
                            { "bgImage",            "URL",      "background image" },
                            { "bgDisplay",          "0-1",      "method of displaying background image" },
                            { "bgColor",            "RGB",      "background color" },
                            { "fgImage",            "URL",      "foreground/text image pattern" },
                            { "fgColor",            "RGB",      "foreground/text color" },
                            { "linkColor",          "RGB",      "color of hyperlinks" },
                            { "activeLinkColor",    "RGB",      "color of active hyperlinks" },
                            { "speed",              "int",      "average pixels per iteration" },
                            { "delay",              "int",      "milliseconds per iteration" },
                            { "amplitude",          "int",      "amplitude (pixels) of oscillation" },
                            { "period",             "int",      "period (iterations) per oscillation" },
                            { "trail",              "0.0-1.0",  "intensity of text trail" },
                            { "target",             "String",   "name of the default target frame" } };
        return info;
    }

    /**
     *  Initializes the applet. This method loads and processes the parameters.
     */
    public void init()
    {
        String value;
        width           = size().width;
        height          = size().height;
        linkColor       = parseColor(getParameter("linkColor"), new Color(0x0000FF));
        activeLinkColor = parseColor(getParameter("activeLinkColor"), new Color(0xFF0000));
        setForeground(parseColor(getParameter("fgColor"), new Color(0xFFFFFF)));
        setBackground(parseColor(getParameter("bgColor"), new Color(0x000000)));
        setTarget(getParameter("target"));
        value = getParameter("font");
        Font font = new Font((value == null) ? "Helvetica" : value,
                             parseInt(getParameter("fontStyle"), Font.PLAIN),
                             parseInt(getParameter("fontSize"), 12));
        value = getParameter("text");
        if (value != null)
            setText(value, font);
        else
            setText(parseURL(getParameter("textFile"), null, true), font);
        value = getParameter("bgImage");
        if (value != null)
            setBackground(parseImage(value, null), null, parseInt(getParameter("bgDisplay"), TILE) % 2);
        value = getParameter("fgImage");
        if (value != null)
            setForeground(parseImage(value, null));
        setSpeed(parseInt(getParameter("speed"), 5));
        setDelay(parseInt(getParameter("delay"), 80));
        setAmplitude(parseInt(getParameter("amplitude"), 6));
        setPeriod(parseInt(getParameter("period"), 30));
        setTrail(parseUnity(getParameter("trail"), 0.8));
    }

    /**
     *  Starts the scrolling animation.
     *  @see    #stop()
     */
    public void start()
    {
        if (scrollThread == null)
        {
            scrollThread = new Thread(this);
            scrollThread.start();
        }
    }

    /**
     *  Stops the scrolling animation.
     *  @see    #start()
     */
    public void stop()
    {
        scrollThread = null;
    }

    /**
     *  Invoked when the scroller's internal thread is started through a call to the <i>start</i>
     *  method. This method animates the scroller.
     *  @see    #start()
     */
    public void run()
    {
        while (scrollThread != null)
        {
            repaint();
            count++;
            if (!pressed)
            {
                int disp = -speed;
                if (period != 0)
                    disp -= (int) (amplitude * Math.cos(2.0 * Math.PI * count / period));
                handleDisplacement(disp);
            }
            if (fadeIncremented)
                fadeIndex++;
            else
                fadeIndex--;
            if (fadeIndex >= fadePixels.length)
            {
                fadeIndex--;
                fadeIncremented = false;
            }
            else if (fadeIndex < 0)
            {
                fadeIndex++;
                fadeIncremented = true;
            }
            try
            {
                scrollThread.sleep(delay);
            }
            catch (InterruptedException e)
            {
                scrollThread = null;
            }
        }
    }

    /**
     *  Performs a binary search of the hyperlink to which the cursor is pointing.
     *  @return the index of the link
     */
    private int searchActiveLink()
    {
        int top = linkRanges[currentText].length - 1;
        int bottom = 0;
        while (bottom <= top)
        {
            int middle = (top + bottom) / 2;
            int position = mouseX - offset;
            if (position < linkRanges[currentText][middle][0])
                top = middle - 1;
            else if (position > linkRanges[currentText][middle][1])
                bottom = middle + 1;
            else
                return middle;
        }
        return -1;
    }

    /**
     *  Redraws the portion of the canvas that has been affected since the last animation frame.
     *  @param  g the graphics context
     *  @see    #paint(Graphics)
     */
    public void update(Graphics g)
    {
        if (textPixels != null)
        {
            int prevLink = currentLink;
            if (entered && textDisp <= mouseY && mouseY <= textDisp + textHeight && linkRanges[currentText].length > 0)
            {
                currentLink = searchActiveLink();
                if (currentLink != prevLink)
                    showStatus((currentLink == -1) ? "" : ((String) links[currentText][currentLink].get("alt")));
            }
            else if (prevLink >= 0)
            {
                showStatus("");
                currentLink = -1;
            }
            overlayPixels(backPixels, width, textHeight, 0, 0, width, textHeight,
                          canvasPixels, width, textHeight, 0, 0, true, false);
            overlayPixels(textPixels[currentText], textWidths[currentText], textHeight,
                          0, 0, textWidths[currentText], textHeight,
                          canvasPixels, width, textHeight, offset, 0, false, true);
            Image textImage = createImage(new MemoryImageSource(width, textHeight, canvasPixels, 0, width));
            g.drawImage(textImage, 0, textDisp, null);
            textImage.flush();
        }
    }

    /**
     *  Paints the component. This method is called when the contents of the component should be
     *  painted in response to the component first being shown or damage needing repair.
     *  @param  g the graphics context
     *  @see    #update(Graphics)
     */
    public void paint(Graphics g)
    {
        if (bgPixels != null)
        {
            if (bgDisplay == TILE)
                for (int j = 0; j < height; j += bgSize.height)
                    for (int i = 0; i < width; i += bgSize.width)
                        g.drawImage(bgImage, i, j, bgColor, null);
            else if (bgDisplay == CENTER)
            {
                g.setColor(bgColor);
                g.fillRect(0, 0, width, height);
                g.drawImage(bgImage, (width - bgSize.width) / 2, (height - bgSize.height) / 2, null);
            }
        }
        else
        {
            g.setColor(bgColor);
            g.fillRect(0, 0, width, height);
        }
        update(g);
    }

    /**
     *  Paints the component and returns as soon as the component has completed painting itself.
     */
    private void paint()
    {
        Graphics graphics = getGraphics();
        if (graphics != null)
        {
            paint(graphics);
            graphics.dispose();
        }
    }

    /**
     *  Repaints the component when the image has changed. This method begins processing the
     *  background and foreground images after it has been notified that they have been completely
     *  loaded.
     *  @param  img     the image being observed
     *  @param  flags   see imageUpdate for more information
     *  @param  x       the x coordinate
     *  @param  y       the y coordinate
     *  @param  width   the width
     *  @param  height  the height
     *  @return true if the flags indicate that the image is completely loaded; false otherwise
     */
    public boolean imageUpdate(Image img, int flags, int x, int y, int w, int h)
    {
        if ((flags & ALLBITS) != 0)
        {
            if (img == bgImage)
                setBackground(bgImage, bgColor, bgDisplay);
            else if (img == fgImage)
                setForeground(fgImage);
        }
        return super.imageUpdate(img, flags, x, y, w, h);
    }

    /**
     *  Determines the current text and offset
     *  @param  disp    the displacement on the current offset
     */
    private void handleDisplacement(int disp)
    {
        offset += disp;
        if (offset + textWidths[currentText] < 0)
        {
            offset = width + offset + textWidths[currentText];
            currentText++;
            if (currentText == textCount)
                currentText = 0;
        }
        else if (offset > width)
        {
            currentText--;
            if (currentText < 0)
                currentText = textCount - 1;
            offset = offset - width - textWidths[currentText];
        }
    }

    /**
     *  This method is called when the mouse first enters this component.
     *  @param  event   the event that caused the action
     *  @param  x       the x coordinate
     *  @param  y       the y coordiante
     *  @return true
     */
    public boolean mouseEnter(Event event, int x, int y)
    {
        mouseX = x;
        mouseY = y;
        entered = true;
        return true;
    }

    /**
     *  This method is called when the mouse exits this component.
     *  @param  event   the event that caused the action
     *  @param  x       the x coordinate
     *  @param  y       the y coordiante
     *  @return true
     */
    public boolean mouseExit(Event event, int x, int y)
    {
        entered = false;
        return true;
    }

    /**
     *  This method is called when the mouse button is released inside this component.
     *  @param  event   the event that caused the action
     *  @param  x       the x coordinate
     *  @param  y       the y coordiante
     *  @return true
     */
    public boolean mouseUp(Event event, int x, int y)
    {
        pressed = false;
        if (currentLink >= 0 && currentLink == pressedLink)
        {
            String target = (String) links[currentText][currentLink].get("target");
            getAppletContext().showDocument(
                (URL) links[currentText][currentLink].get("href"),
                (target == null) ? defaultTarget : target);
        }
        return true;
    }

    /**
     *  This method is called when the mouse button is pushed inside this component.
     *  @param  event   the event that caused the action
     *  @param  x       the x coordinate
     *  @param  y       the y coordiante
     *  @return true
     */
    public boolean mouseDown(Event event, int x, int y)
    {
        pressed = true;
        pressedLink = (currentLink >= 0) ? currentLink : -1;
        return true;
    }

    /**
     *  This method is called when the mouse is moved inside this component with the mouse button
     *  not pushed.
     *  @param  event   the event that caused the action
     *  @param  x       the x coordinate
     *  @param  y       the y coordiante
     *  @return true
     */
    public boolean mouseMove(Event event, int x, int y)
    {
        mouseX = x;
        mouseY = y;
        if (scrollThread == null)
            repaint();
        return true;
    }

    /**
     *  This method is called when the mouse button is moved inside this component with the button
     *  pushed.
     *  @param  event   the event that caused the action
     *  @param  x       the x coordinate
     *  @param  y       the y coordiante
     *  @return true
     */
    public boolean mouseDrag(Event event, int x, int y)
    {
        handleDisplacement(x - mouseX);
        mouseX = x;
        mouseY = y;
        repaint();
        return true;
    }

    /**
     *  Resizes this component to the specified width and height.
     *  @param  w   the width
     *  @param  h   the height
     */
    public void resize(int w, int h)
    {
        width = w;
        height = h;
        super.resize(w, h);
        width = size().width;
        height = size().height;
        updateBackground();
    }

    /**
     *  Determines the preferred size of the component.
     *  @return the preferred size of this component
     */
    public Dimension preferredSize()
    {
        return new Dimension(width, height);
    }

    /**
     *  Returns the background color.
     *  @return the background color
     */
    public Color getBackgroundColor()
    {
        return new Color(bgColor.getRGB());
    }

    /**
     *  Returns the background image.
     *  @return the background image
     */
    public Image getBackgroundImage()
    {
        return bgImage;
    }

    /**
     *  Returns the foreground color.
     *  @return the foreground color
     */
    public Color getForegroundColor()
    {
        return new Color(fgColor.getRGB());
    }

    /**
     *  Returns the foreground image.
     *  @return the foreground image
     */
    public Image getForegroundImage()
    {
        return fgImage;
    }

    /**
     *  Returns the hyperlink color.
     *  @return the hyperlink color
     */
    public Color getLinkColor()
    {
        return new Color(linkColor.getRGB());
    }

    /**
     *  Returns the active hyperlink color.
     *  @return the active hyperlink color
     */
    public Color getActiveLinkColor()
    {
        return new Color(activeLinkColor.getRGB());
    }

    /**
     *  Returns the speed
     *  @return the number of pixels to shift between frames/iterations
     */
    public int getSpeed()
    {
        return speed;
    }

    /**
     *  Returns the delay between frames/iterations.
     *  @return the delay in milliseconds
     */
    public int getDelay()
    {
        return delay;
    }

    /**
     *  Returns the amplitude of the oscillation.
     *  @return the number of pixels by which to oscillate in either direction
     */
    public int getAmplitude()
    {
        return amplitude;
    }

    /**
     *  Returns the period of the oscillation.
     *  @return the number of frames/iteration to complet an oscillation
     */
    public int getPeriod()
    {
        return period;
    }

    /**
     *  Returns the default target frame.
     *  @return the name of default target frame
     */
    public String getTarget()
    {
        return new String(defaultTarget);
    }

    /**
     *  Updates the background pixels. This method should be called when any aspect of the
     *  background has changed.
     */
    private void updateBackground()
    {
        if (textPixels != null)
        {
            textDisp        = (height - textHeight) / 2;
            backPixels      = new int[width * textHeight];
            canvasPixels    = new int[width * textHeight];
            int bgPixel = bgColor.getRGB();
            int i, j;
            for (i = 0; i < width * textHeight; i++)
                canvasPixels[i] = 0xFF000000 | bgPixel;
            if (bgPixels != null)
            {
                if (bgDisplay == TILE)
                    for (j = -(textDisp % bgSize.height); j < textHeight; j += bgSize.height)
                        for (i = 0; i < width; i += bgSize.width)
                            overlayPixels(bgPixels, bgSize.width, bgSize.height, 0, 0, bgSize.width, bgSize.height,
                                          canvasPixels, width, textHeight, i, j, false, false);
                else if (bgDisplay == CENTER)
                    overlayPixels(bgPixels, bgSize.width, bgSize.height, 0, 0, bgSize.width, bgSize.height,
                                  canvasPixels, width, textHeight,
                                  (width - bgSize.width) / 2, (textHeight - bgSize.height) / 2, false, false);
            }
            for (i = 0; i < width * textHeight; i++)
                backPixels[i] = 0xFF000000 | (0xFFFFFF & canvasPixels[i]);
        }
    }

    /**
     *  Sets the background image.
     *  @param  image   the image to be displayed in the background
     *  @param  color   the background color to use for any transparent pixels
     *  @param  display the method by which to display the image
     *  @see    #TILE
     *  @see    #CENTER
     */
    public void setBackground(Image image, Color color, int display)
    {
        bgImage     = image;
        bgDisplay   = display % 2;
        if (color != null)
            bgColor = new Color(color.getRGB());
        if (prepareImage(image, this))
        {
            bgPixels        = getPixels(bgImage);
            bgSize.width    = bgImage.getWidth(null);
            bgSize.height   = bgImage.getHeight(null);
            updateBackground();
            paint();
        }
    }

    /**
     *  Sets the background to a solid color.
     *  @param  color   the background color
     */
    public void setBackground(Color color)
    {
        bgColor     = new Color(color.getRGB());
        bgPixels    = null;
        bgImage     = null;
        updateBackground();
        paint();
    }

    /**
     *  Sets the foreground image pattern.
     *  @param  image   the foreground image
     */
    public void setForeground(Image image)
    {
        fgImage = image;
        if (prepareImage(image, this))
        {
            fgPixels        = getPixels(fgImage);
            fgSize.width    = fgImage.getWidth(null);
            fgSize.height   = fgImage.getHeight(null);
            updateForeground();
            repaint();
        }
    }

    /**
     *  Sets the foreground to a solid color.
     *  @param  color   the foreground color
     */
    public void setForeground(Color color)
    {
        fgColor = new Color(color.getRGB());
        fgPixels = null;
        updateForeground();
        repaint();
    }

    /**
     *  Updates the foreground pixels. This method should be called when any aspect of the
     *  foreground has changed.
     */
    private void updateForeground()
    {
        if (textPixels != null)
        {
            if (fgPixels != null)
            {
                for (int k = 0; k < textCount; k++)
                    for (int j = 0; j < textHeight; j++)
                        for (int i = 0; i < textWidths[k]; i++)
                        {
                            int index = j * textWidths[k] + i;
                            textPixels[k][index] = (textPixels[k][index] & 0xFF000000)
                                | (0xFFFFFF & fgPixels[(j % fgSize.height) * fgSize.width + (i % fgSize.width)]);
                        }
            }
            else
            {
                int pixel = 0xFFFFFF & fgColor.getRGB();
                for (int k = 0; k < textCount; k++)
                    for (int i = 0; i < textWidths[k] * textHeight; i++)
                        textPixels[k][i] = (textPixels[k][i] & 0xFF000000) | pixel;
            }
            for (int i = 0; i < textCount; i++)
                for (int j = 0; j < linkRanges[i].length; j++)
                {
                    int w = linkRanges[i][j][1] - linkRanges[i][j][0] + 1;
                    colorPixels(textPixels[i], textWidths[i], textHeight, linkRanges[i][j][0] - w / 8, 0,
                                5 * w / 4, textHeight, linkColor.getRGB());
                }
        }
    }

    /**
     *  Sets the color of the hyperlinks.
     *  @param  color   the hyperlink color
     */
    public void setLinkColor(Color color)
    {
        linkColor = new Color(color.getRGB());
        updateForeground();
    }

    /**
     *  Sets the color of the hyperlink to which the mouse cursor is pointing
     *  @param  color   the hyperlink color
     */
    public void setActiveLinkColor(Color color)
    {
        activeLinkColor = new Color(color.getRGB());
    }

    /**
     *  Sets the speed of the scrolling motion
     *  @param  speed   the number of pixels per animation frame
     */
    public void setSpeed(int speed)
    {
        this.speed = speed;
    }

    /**
     *  Sets the frame rate.
     *  @param  delay   the number of milliseconds between animation frames
     */
    public void setDelay(int delay)
    {
        this.delay = delay;
    }

    /**
     *  Sets the amplitude of the oscillation.
     *  @param  amplitude   the number of pixels by which to oscillate in either direction
     */
    public void setAmplitude(int amplitude)
    {
        this.amplitude = amplitude;
    }

    /**
     *  Sets the period (inverse of frequency) of the oscillation.
     *  @param  period  the number of iterations/frames to complete one oscillation
     */
    public void setPeriod(int period)
    {
        this.period = period;
    }

    /**
     *  Sets the fading trail effect.
     *  @param  intensity   the intensity of the trail effect. Values range from 0.0 to 1.0.
     */
    public void setTrail(double intensity)
    {
        clearAmount = 0xFF - (int) (0xFF * normalize(intensity));
    }

    /**
     *  Sets the default target frame.
     *  @param  target the default target
     */
    public void setTarget(String target)
    {
        defaultTarget = (target == null) ? "_self" : new String(target);
    }

    /**
     *  Sets the text to be scrolled from a text file.
     *  @param  path    the path of the file that contains the text to be scrolled
     *  @param  font    the font to use in displaying the text
     */
    public void setText(URL path, Font font)
    {
        String defaultText = "PhantomScroll was developed by <a href=\"mailto:um@mail.com\">Uldarico Muico Jr.</a>";
        String contents = defaultText;
        if (path != null)
        {
            try
            {
                DataInputStream stream = new DataInputStream(path.openConnection().getInputStream());
                contents = stream.readLine();
                String line;
                while ((line = stream.readLine()) != null)
                    contents = contents + line;
                stream.close();

            }
            catch (IOException e)
            {
                e.printStackTrace();
                contents = defaultText;
            }
        }
        setText(contents, font);
    }

    /**
     *  Sets the text to be scrolled. Hyperlinks and breaks may be indicated as in HTML.
     *  @param  text    the text to be scrolled
     *  @param  font    the font to use in displaying the text
     */
    public void setText(String text, Font font)
    {
        text = text + "<br>";
        Vector  modifiedText    = new Vector();
        Vector  stack           = new Vector();
        Vector  data            = new Vector();
        int     prevIndex       = 0;
        textCount = 0;
        while (true)
        {
            int index = text.indexOf('<', prevIndex);
            if (index == -1) break;
            int endIndex = searchBracket(text, index);
            Properties tagProps = parseTagProperties(text.substring(index + 1, endIndex));
            while (modifiedText.size() <= textCount)
                modifiedText.addElement(new String());
            while (data.size() <= textCount)
                data.addElement(new Vector());
            String s = (String) modifiedText.elementAt(textCount);
            modifiedText.setElementAt(s.concat(text.substring(prevIndex, index)), textCount);
            String type = tagProps.getProperty("type");
            if (type.equals("br"))
            {
                if (stack.size() > 0)
                {
                    String tail = text.substring(endIndex + 1);
                    text = text.substring(0, endIndex + 1);
                    for (int i = 0; i < stack.size(); i++)
                    {
                        Hashtable table = (Hashtable) stack.elementAt(i);
                        String t = (String) table.get("type");
                        text = text + "</" + t + ">";
                        tail = ((String) table.get("tag")) + tail;
                        table.remove("tag");
                    }
                    text = text + "<br>" + tail;
                }
                else
                    textCount++;
            }
            else if (type.equals("a"))
            {
                URL         url     = parseURL(tagProps.getProperty("href"), getDocumentBase(), false);
                String      target  = tagProps.getProperty("target");
                String      alt     = tagProps.getProperty("alt");
                Hashtable   table   = new Hashtable();
                table.put("type", "a");
                table.put("href", url);
                if (target != null)
                    table.put("target", target);
                int[] range = new int[2];
                range[0] = ((String) modifiedText.elementAt(textCount)).length();
                table.put("range", range);
                table.put("tag", text.substring(index, endIndex + 1));
                table.put("alt", (alt == null) ? url.toString() : alt);
                stack.addElement(table);
            }
            else if (type.equals("/a"))
            {
                int i = stack.size() - 1;
                while (i >= 0)
                {
                    Hashtable table = (Hashtable) stack.elementAt(i);
                    if (table.get("type") == "a")
                    {
                        int[] range = (int[]) table.get("range");
                        range[1] = ((String) modifiedText.elementAt(textCount)).length() - 1;
                        ((Vector) data.elementAt(textCount)).addElement(table);
                        stack.removeElementAt(i);
                        break;
                    }
                    i--;
                }
            }
            prevIndex = endIndex + 1;
        }
        stack.removeAllElements();
        FontMetrics metrics = getFontMetrics(font);
        textWidths  = new int[textCount];
        links       = new Hashtable[textCount][];
        linkRanges  = new int[textCount][][];
        textPixels  = new int[textCount][];
        for (int i = 0; i < textCount; i++)
        {
            String      t       = (String) modifiedText.elementAt(i);
            int[]       buffer  = createTextPixels(t, font, fgColor);
            Dimension   size    = getTextSize(t, metrics);
            textWidths[i]   = size.width;
            textHeight      = size.height;
            textPixels[i]   = new int[textWidths[i] * textHeight];
            Vector textData = (Vector) data.elementAt(i);
            linkRanges[i]   = new int[textData.size()][];
            links[i]        = new Hashtable[textData.size()];
            for (int j = 0; j < textData.size(); j++)
            {
                links[i][j]         = (Hashtable) textData.elementAt(j);
                int[] indices       = (int[]) links[i][j].get("range");
                linkRanges[i][j]    = new int[2];
                linkRanges[i][j][0] = metrics.stringWidth(t.substring(0, indices[0])) + metrics.charWidth(' ');
                linkRanges[i][j][1] = linkRanges[i][j][0] + metrics.stringWidth(t.substring(indices[0], indices[1] + 1));
                for (int k = linkRanges[i][j][0]; k <= linkRanges[i][j][1] - 1; k++)
                    buffer[(metrics.getAscent() + 2) * textWidths[i] + k] |= 0xFF000000;

            }
            filterPixels(buffer, textWidths[i], textHeight,
                        0, 0, textWidths[i], textHeight,
                        textPixels[i], textWidths[i], textHeight, 0, 0, 0);
        }
        offset = width;
        updateBackground();
        updateForeground();
        createFade();
    }

    /**
     *  Searches for a left angle bracket.
     *  @param  s       the string in which to search
     *  @param  index   the starting index into the string
     *  @return the index of the first matching bracket
     */
    private int searchBracket(String s, int index)
    {
        while (true)
        {
            char ch = s.charAt(index);
            if (ch == '>')
                return index;
            else if (ch == '"' || ch == '\'')
                index = s.indexOf(ch, index + 1);
            if (index == -1)
                return -1;
            index++;
        }
    }

    /**
     *  Parses the contents of an HTML tag.
     *  @param  value   the text to be parsed excluding the tag delimeters
     *  @return the properties
     */
    private Properties parseTagProperties(String value)
    {
        Properties      properties      = new Properties();
        StreamTokenizer tokenizer       = new StreamTokenizer(new StringBufferInputStream(value));
        String          previousToken   = null;
        tokenizer.resetSyntax();
        tokenizer.wordChars(0x21, 0xFFFF);
        tokenizer.quoteChar('\'');
        tokenizer.quoteChar('"');
        tokenizer.ordinaryChar('=');
        try
        {
            if (tokenizer.nextToken() != tokenizer.TT_EOF)
                properties.put("type", tokenizer.sval);
            int type = tokenizer.nextToken();
            while (type != tokenizer.TT_EOF)
            {
                if (type == '=' && previousToken != null)
                {
                    tokenizer.nextToken();
                    properties.put(previousToken.toLowerCase(), tokenizer.sval);
                    previousToken = null;
                }
                else if (type == tokenizer.TT_WORD)
                    previousToken = tokenizer.sval;
                type = tokenizer.nextToken();
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        return properties;
    }

    /**
     *  Creates the alpha values for those pixels near the left and right edges of the applet.
     */
    private void createFade()
    {
        double  fadeDepth   = 0.8;
        int     fadePeriod  = textHeight;
        int     upperX      = width / 2 + 1;
        int     upperY      = fadePeriod / 2 + 1;
        fadePixels = new int[upperY][];
        for (int j = 0; j < upperY; j++)
        {
            fadePixels[j] = new int[upperX];
            double penetration = fadeDepth * (0.65 - 0.35 * Math.cos(2.0 * Math.PI * j / fadePeriod));
            int i = 0;
            while (i < (int) (penetration * upperX))
            {
                fadePixels[j][i] = (int) ((0.5 - 0.5 * Math.cos(2.0 * Math.PI / penetration * i / width)) * 0xFF);
                i++;
            }
            while (i < upperX)
            {
                fadePixels[j][i] = 0xFF;
                i++;
            }
        }
    }

    /**
     *  Computes the dimensions of text to be displayed on the screen.
     *  @param  text    the text to be displayed
     *  @param  metrics the font metrics
     *  @return the size
     */
    private Dimension getTextSize(String text, FontMetrics metrics)
    {
        Dimension textSize = new Dimension();
        textSize.width = metrics.stringWidth(text) + 2 * metrics.charWidth(' ');
        textSize.height += metrics.getAscent() + metrics.getDescent() + 3;
        return textSize;
    }

    /**
     *  Creates the pixels with the alpha mask that represents the message to be displayed.
     *  @param  text    the string to be displayed
     *  @param  font    the font of the text
     *  @param  color   the initial color of the text
     */
    private int[] createTextPixels(String text, Font font, Color color)
    {
        FontMetrics metrics     = getFontMetrics(font);
        Dimension   textSize    = getTextSize(text, metrics);
        int         size        = textSize.width * textSize.height;
        int         lineHeight  = metrics.getAscent() + metrics.getDescent();
        int         pixel       = ((color == null) ? 0xFF7F7F7F : color.getRGB()) & 0xFFFFFF;
        Image       textImage   = createImage(textSize.width, textSize.height);
        Graphics    graphics    = textImage.getGraphics();
        int[]       values      = new int[4];
        graphics.setColor(new Color(0xFF000000));
        graphics.fillRect(0, 0, textSize.width, textSize.height);
        graphics.setColor(new Color(0xFFFFFFFF));
        graphics.setFont(font);
        graphics.drawString(text, (textSize.width - metrics.stringWidth(text)) / 2, metrics.getAscent());
        graphics.dispose();
        int[] pixels = getPixels(textImage);
        for (int index = 0; index < size; index++)
        {
            decomposePixel(pixels[index], values);
            int value = ((values[1] + values[2] + values[3]) / 3) << 24;
            pixels[index] = value | pixel;
        }
        return pixels;
    }

    /**
     *  Parses a string representation of an URL.
     *  @param  value           the string representation
     *  @param  defaultValue    the value if the the parsing fails
     *  @param  codeBased       indicates whether the relative URL is code-based or document-based
     *  @return the parsed URL
     */
    private URL parseURL(String value, URL defaultValue, boolean codeBased)
    {
        if (value == null)
            return defaultValue;
        try
        {
            if (value.indexOf(":") != -1)
                return new URL(value);
            else
                return new URL((codeBased) ? getCodeBase() : getDocumentBase(), value);
        }
        catch (MalformedURLException e)
        {
            e.printStackTrace();
            return defaultValue;
        }
    }

    /**
     *  Parses a string representation of an image. The string is simply the URL of the image.
     *  @param  value           the string representation
     *  @param  defaultValue    the value if the the parsing fails
     *  @return the image
     */
    private Image parseImage(String value, Image defaultValue)
    {
        if (value != null)
            return getImage(parseURL(value, null, true));
        else
            return defaultValue;
    }

    /**
     *  Parses a string representation of an integer.
     *  @param  value           the string representation
     *  @param  defaultValue    the value if the the parsing fails
     *  @return the parsed integer
     */
    private int parseInt(String value, int defaultValue)
    {
        if (value == null)
            return defaultValue;
        try
        {
            return Integer.parseInt(value);
        }
        catch (NumberFormatException e)
        {
            e.printStackTrace();
            return defaultValue;
        }
    }

    /**
     *  Parses a string representation of a floating-point number in the interval 0.0 and 1.0.
     *  @param  value           the string representation
     *  @param  defaultValue    the value if the the parsing fails
     *  @return the parsed floating-point number
     */
    private double parseUnity(String value, double defaultValue)
    {
        if (value == null)
            return defaultValue;
        try
        {
            return normalize(Double.valueOf(value).doubleValue());
        }
        catch (NumberFormatException e)
        {
            e.printStackTrace();
            return defaultValue;
        }
    }

    /**
     *  Parses a string representation of a color in RGB decimal or hexadecimal format.
     *  @param  value           the string representation
     *  @param  defaultValue    the value if the the parsing fails
     *  @return the parsed color
     */
    private Color parseColor(String value, Color defaultValue)
    {
        if (value == null)
            return defaultValue;
        try
        {
            if (value.indexOf(" ") != -1)
            {
                StringTokenizer tokenizer = new StringTokenizer(value);
                int pixel = 0xFF;
                while (tokenizer.hasMoreTokens())
                    pixel = (pixel << 8) | Integer.parseInt(tokenizer.nextToken());
                return new Color(pixel);
            }
            else
            {
                int pixel = Integer.parseInt(value, 16);
                return new Color(0xFF000000 | pixel);
            }
        }
        catch (NumberFormatException e)
        {
            e.printStackTrace();
            return defaultValue;
        }
    }

    /**
     *  Retrieves the pixels of an image.
     *  @param  image   the image from which to get the pixels
     *  @return the pixels
     */
    private int[] getPixels(Image image)
    {
        int             width   = image.getWidth(null);
        int             height  = image.getHeight(null);
        int[]           pixels  = new int[width * height];
        PixelGrabber    grabber = new PixelGrabber(image, 0, 0, width, height, pixels, 0, width);
        try
        {
            grabber.grabPixels();
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
            return null;
        }
        return pixels;
    }

    /**
     *  Restrains a floating-point number to the interval 0.0 to 1.0.
     *  @param  x   the number to correct
     *  @return the corrected number
     */
    private double normalize(double x)
    {
        if (x < 0.0)
            x = 0.0;
        else if (x > 1.0)
            x = 1.0;
        return x;
    }

    /**
     *  Computes the effective bounds for an image region that linearly maps to another
     *  @param  srcW    the width of the source image
     *  @param  srcH    the height of the source image
     *  @param  srcX    the x coordinate of the starting pixel in the source coordinate system
     *  @param  srcY    the y coordinate of the starting pixel in the source coordinate system
     *  @param  destW   the width of the destination image
     *  @param  destH   the height of the destination image
     *  @param  destX   the x coordinate of the starting pixel in the destination coordinate system
     *  @param  destY   the x coordinate of the starting pixel in the destination coordinate system
     *  @param  w       the width of the region
     *  @param  h       the height of the region
     *  @return the effective bounds in the destination coordinate system
     */
    private Rectangle effectiveBounds(int srcW, int srcH, int srcX, int srcY,
                                      int destW, int destH, int destX, int destY,
                                      int w, int h)
    {
        Rectangle rect = new Rectangle(srcX, srcY, w, h);
        rect = rect.intersection(new Rectangle(srcW, srcH));
        rect.translate(destX - srcX, destY - srcY);
        return rect.intersection(new Rectangle(destW, destH));
    }

    /**
     *  Decomposes the alpha, red, green, and blue components of a pixel.
     *  @param  pixel   the pixel to decompose
     *  @param  values  the buffer to hold the components
     */
    private void decomposePixel(int pixel, int[] values)
    {
        values[0] = (pixel >> 24) & 0xFF;
        values[1] = (pixel >> 16) & 0xFF;
        values[2] = (pixel >> 8)  & 0xFF;
        values[3] =  pixel        & 0xFF;
    }

    /**
     *  Applies an anti-aliasing filter to a region of an image.
     *  @param  srcPixels   the source pixels
     *  @param  srcW        the width of the source image
     *  @param  srcH        the height of the source image
     *  @param  srcX        the x coordinate of the starting pixel in the source coordinate system
     *  @param  srcY        the y coordinate of the starting pixel in the source coordinate system
     *  @param  w           the width of the region
     *  @param  h           the height of the region
     *  @param  destPixels  the destination pixels
     *  @param  destW       the width of the destination image
     *  @param  destH       the height of the destination image
     *  @param  destX       the x coordinate of the starting pixel in the destination coordinate system
     *  @param  destY       the x coordinate of the starting pixel in the destination coordinate system
     *  @param  bgValue     the default background color
     */
    private void filterPixels(int[] srcPixels, int srcW, int srcH,
                             int srcX, int srcY, int w, int h,
                             int[] destPixels, int destW, int destH,
                             int destX, int destY, int bgValue)
    {
        Rectangle   rect        = effectiveBounds(srcW, srcH, srcX, srcY, destW, destH, destX, destY, w, h);
        int         i;
        int         bufferSize  = srcW * srcH;
        int         dx          = destX - srcX;
        int         dy          = destY - srcY;
        int[]       sums        = new int[3];
        bgValue = 0xFFFFFF | bgValue;
        rect.translate(-dx, -dy);
        int upperX = rect.x + rect.width + 1;
        int upperY = rect.y + rect.height + 1;
        for (int column = rect.x - 1; column < upperX; column++)
        {
            for (i = 0; i < 3; i++)
                sums[i] = 0;
            for (int row = rect.y - 1; row < upperY; row++)
            {
                int bottomIndex     = (row + 2) % 3;
                int srcIndex        = (row + 1) * srcW + column;
                sums[bottomIndex]   = 0;
                for (i = -1; i <= 1; i++)
                {
                    int batchIndex = srcIndex + i;
                    if (batchIndex >= 0 && batchIndex < bufferSize && column + i >= 0 && column + i < srcW)
                        sums[bottomIndex] += ((srcPixels[batchIndex] >> 24) & 0xFF) * ((i == 0) ? 3 : 1);
                }
                int destColumn  = column + dx;
                int destRow     = row + dy;
                if (destColumn >= 0 && destColumn < destW && destRow >= 0 && destRow < destH)
                {
                    int sum         = 0;
                    int topIndex    = row + 3;
                    int centerIndex = topIndex + 1;
                    bottomIndex     = topIndex + 2;
                    for (i = topIndex; i <= bottomIndex; i++)
                        sum += ((i == centerIndex) ? 3 : 1) * sums[i % 3];
                    int pixel;
                    if (row >= 0 && row < srcH && column >= 0 & column < srcW)
                        pixel = srcPixels[row * srcW + column] & 0xFFFFFF;
                    else
                        pixel = bgValue;
                    destPixels[destRow * destW + destColumn] = ((sum / 25) << 24) | pixel;
                }
            }
        }
    }

    /**
     *  Overlays a solid color onto a region of an image.
     *  @param  pixels  the pixels
     *  @param  width   the width of the image
     *  @param  height  the height of the image
     *  @param  x       the x coordinate of the starting pixel
     *  @param  x       the y coordinate of the starting pixel
     *  @param  w       the width of the region
     *  @param  h       the height of the region
     */
    private void colorPixels(int[] pixels, int width, int height, int x, int y, int w, int h, int color)
    {
        int         size        = width * height;
        int[]       values      = new int[4];
        int[]       cValues     = new int[4];
        float[]     HSBValues   = new float[3];
        float[]     HSBCValues  = new float[3];
        decomposePixel(color, cValues);
        Color.RGBtoHSB(cValues[1], cValues[2], cValues[3], HSBCValues);
        for (int j = 0; j < h; j++)
            if (y + j >= 0 && y + j < height)
                for (int i = 0; i < w; i++)
                    if (x + i >= 0 && x + i < width)
                    {
                        double intensity = Math.cos(i * Math.PI / w);
                        for (int k = 0; k < 4; k++)
                            intensity *= intensity;
                        intensity = 1.0 - intensity;
                        int amount = (int) (0xFF * intensity);
                        int index = (j + y) * width + i + x;
                        decomposePixel(pixels[index], values);
                        Color.RGBtoHSB(values[1], values[2], values[3], HSBValues);
                        float saturation = HSBValues[1];
                        float brightness = HSBValues[2];
                        values[1]   = (amount * cValues[1] + (0xFF - amount) * values[1]) / 0xFF;
                        values[2]   = (amount * cValues[2] + (0xFF - amount) * values[2]) / 0xFF;
                        values[3]   = (amount * cValues[3] + (0xFF - amount) * values[3]) / 0xFF;
                        Color.RGBtoHSB(values[1], values[2], values[3], HSBValues);
                        HSBValues[1]    = (float) (intensity * HSBCValues[1] + (1.0 - intensity) * saturation);
                        HSBValues[2]    = (float) (0.4 * intensity * HSBCValues[2] + (1.0 - 0.4 * intensity) * brightness);
                        pixels[index]   = (values[0] << 24)
                                        | (0xFFFFFF & Color.HSBtoRGB(HSBValues[0], HSBValues[1], HSBValues[2]));
                    }
    }

    /**
     *  Overlays an image onto another. The alpha pixels determine the opacity of the images.
     *  @param  srcPixels   the source pixels
     *  @param  srcW        the width of the source image
     *  @param  srcH        the height of the source image
     *  @param  srcX        the x coordinate of the starting pixel in the source coordinate system
     *  @param  srcY        the y coordinate of the starting pixel in the source coordinate system
     *  @param  w           the width of the region
     *  @param  h           the height of the region
     *  @param  destPixels  the destination pixels
     *  @param  destW       the width of the destination image
     *  @param  destH       the height of the destination image
     *  @param  destX       the x coordinate of the starting pixel in the destination coordinate system
     *  @param  destY       the x coordinate of the starting pixel in the destination coordinate system
     *  @param  clearing    indicates that the source pixels are refreshing the canvas
     *  @param  fading      indicates that the source pixels are to be faded near the edges
     */
    private void overlayPixels(int[] srcPixels, int srcW, int srcH,
                               int srcX, int srcY, int w, int h,
                               int[] destPixels, int destW, int destH,
                               int destX, int destY, boolean clearing, boolean fading)
    {
        Rectangle   rect        = effectiveBounds(srcW, srcH, srcX, srcY, destW, destH, destX, destY, w, h);
        int         dx          = destX - srcX;
        int         dy          = destY - srcY;
        int         i;
        int         j           = rect.y * destW;
        int         k           = (rect.y - dy) * srcW;
        int         upperI      = rect.x + rect.width;
        int         upperJ      = (rect.y + rect.height) * destW;
        int         fadeI;
        int         fadeJ       = fadeIndex;
        int         center      = width / 2;
        int[]       srcRGB      = new int[4];
        int[]       destRGB     = new int[4];
        int[]       cRGB        = new int[4];
        boolean     incremented = fadeIncremented;
        decomposePixel(activeLinkColor.getRGB(), cRGB);
        while (j < upperJ)
        {
            if (rect.x <= center)
                fadeI = rect.x;
            else
                fadeI = 2 * center - rect.x;
            for (i = rect.x; i < upperI; i++)
            {
                int srcPixel = srcPixels[k + i - dx];
                decomposePixel(srcPixel, srcRGB);
                if (srcRGB[0] != 0)
                {
                    int destIndex = j + i;
                    int destPixel = destPixels[destIndex];
                    if (currentLink >= 0 && !clearing
                        && linkRanges[currentText][currentLink][0] <= i - destX
                        && i - destX <= linkRanges[currentText][currentLink][1])
                    {
                        srcRGB[1]   = cRGB[1];
                        srcRGB[2]   = cRGB[2];
                        srcRGB[3]   = cRGB[3];
                    }
                    if (clearing)
                        srcRGB[0]   = clearAmount;
                    else if (fading)
                        srcRGB[0]   = srcRGB[0] * fadePixels[fadeJ][fadeI] / 0xFF;
                    int destAmount = 0xFF - srcRGB[0];
                    decomposePixel(destPixel, destRGB);
                    destRGB[1]              = (destAmount * destRGB[1] + srcRGB[0] * srcRGB[1]) / 0xFF;
                    destRGB[2]              = (destAmount * destRGB[2] + srcRGB[0] * srcRGB[2]) / 0xFF;
                    destRGB[3]              = (destAmount * destRGB[3] + srcRGB[0] * srcRGB[3]) / 0xFF;
                    destPixels[destIndex]   = 0xFF000000
                                            | (destRGB[1] << 16)
                                            | (destRGB[2] << 8)
                                            |  destRGB[3];
                }
                if (i < center)
                    fadeI++;
                else
                    fadeI--;
            }
            if (incremented)
                fadeJ++;
            else
                fadeJ--;
            if (fadeJ >= fadePixels.length)
            {
                fadeJ--;
                incremented = false;
            }
            else if (fadeJ < 0)
            {
                fadeJ++;
                incremented = true;
            }
            j += destW;
            k += srcW;
        }
    }
}

Back to the PhantomScroll applet

How to Add Java Applets to Your Site

New on the Java Boutique:

New Review:

Time Management Made Easy with the Quartz Enterprise Job Scheduler
Why not just use the Java timer API? This open source scheduling API boasts simplicity, ease-of-integration, a well-rounded feature set, and it's free!

New Applet:

Reverse Complement
Reverse Complement is a simple applet that converts DNA or RNA sequences into three useful formats.

Elsewhere on internet.com:

WebDeveloper Java
Lots of Java information on webdeveloper.com

WDVL Java
Thorough Java resource at the Web Developer's Virtual Library.

ScriptSearch Java
Hundreds of free Java code files to download.

jGuru: Your View of the Java Universe
Customizable portal with online training, FAQs, regular news updates, and tutorials.

 Intel Go Parallel Portal
 Internet.com eBook Library
 IBM Software Construction Toolbox
 Microsoft RIA Development Center
 Destination .NET
XML error: not well-formed (invalid token) at line 43
advertisement
Receive Articles via our XML/RSS feed
Receive Articles via our XML/RSS feed

JavaBytes
Internet Cyclone
This powerful, easy-to-use, internet optimizer is for Windows 95, 98, ME, NT, 2000 and XP. It's designed to automatically optimize your Windows settings, boosting your Internet connection up to 200%.

Google Hopes Chrome Will Help, Not Hurt Firefox
Remember Figlets? They're Back With Zend
Microsoft Readies an App Store Competitor?
Google: Chrome Browser Will Make Money
Sam Ramji: Microsoft's Man in Open Source
Google to Shake Up Browsers With Own Launch
Mozilla's Ubquity Mashup: For The Masses?
iPhone Users Just Want to Have Fun
Oops! I Fixed the Linux Kernel
Jim Zemlin: The New Center of Linux Gravity

Code Around C#'s Using Statement to Release Unmanaged Resources
Writing Functional Code with RDFa
BitLocker Brings Encryption to Windows Server 2008
Network Know-How: Exploring Network Algorithms
Create a Durable and Reliable WCF Service with MSMQ 4.0
The Baker's Dozen: 13 Tips for SQL Server 2008 and SSRS 2008
Book Excerpt: Microsoft Expression Blend Unleashed
Develop a Mobile RSS Feed the Easy Way
State of the Semantic Web: Know Where to Look
A 3D Exploration of the HTML Canvas Element

Advertising Info  |   Member Services  |   Contact Us  |   Help  |   Feedback  |   Site Map  |   Network Map  |   About



JupiterOnlineMedia

internet.comearthweb.comDevx.commediabistro.comGraphics.com

Search:

Jupitermedia Corporation has two divisions: Jupiterimages and JupiterOnlineMedia

Jupitermedia Corporate Info


Legal Notices, Licensing, Reprints, & Permissions, Privacy Policy.

Advertise | Newsletters | Tech Jobs | Shopping | E-mail Offers

Solutions
Whitepapers and eBooks
Intel PDF: Virtualization Delivers Data Center Efficiency
Intel eBook: Managing the Evolving Data Center
Microsoft Article: BitLocker Brings Encryption to Windows Server 2008
Symantec eBook: The Guide to E-Mail Archiving and Management
Microsoft Article: RODCs Transform Branch Office Security
Go Parallel Article: James Reinders on the Intel Parallel Studio Beta Program
Avaya Article: Advancing the State of the Art in Customer Service
Adobe Acrobat Connect Pro: Web Conferencing and eLearning Whitepapers
Avaya Article: Avaya AE Services Provide Rapid Telephony Integration with Facebook
Go Parallel Article: Getting Started with TBB on Windows
HP eBook: Storage Networking , Part 1
MORE WHITEPAPERS, EBOOKS, AND ARTICLES
Webcasts
Intel Seminar: Efficiencies in Hardware/Software Virtualization
HP Webcast: Disaster Recovery Planning
Go Parallel Video: Performance and Threading Tools for Game Developers
HP Video: StorageWorks EVA4400 and Oracle
HP Webcast: Storage Is Changing Fast - Be Ready or Be Left Behind
MORE WEBCASTS, PODCASTS, AND VIDEOS
Downloads and eKits
IBM TCO eKIT: Your IT Budget is Under Attack, Get in Control
IBM Energy Efficiency eKIT: Learn How to Reduce Costs
30-Day Trial: SPAMfighter Exchange Module
Red Gate Download: SQL Toolbelt and free High-Performance SQL Code eBook
Iron Speed Designer Application Generator
MORE DOWNLOADS, EKITS, AND FREE TRIALS
Tutorials and Demos
Microsoft Article: Silverlight Streaming--Free Video Hosting for All
Featured Algorithm: Intel Threading Building Blocks - parallel_reduce
HP Demo: StorageWorks EVA4400
MORE TUTORIALS, DEMOS AND STEP-BY-STEP GUIDES