home *** CD-ROM | disk | FTP | other *** search
/ Chip 1998 November / Chip_1998-11_cd.bin / tema / Cafe / main.bin / MemoryImageSource.java < prev    next >
Text File  |  1997-05-20  |  16KB  |  488 lines

  1. /*
  2.  * @(#)MemoryImageSource.java    1.18 96/11/23
  3.  * 
  4.  * Copyright (c) 1995, 1996 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  * CopyrightVersion 1.1_beta
  20.  * 
  21.  */
  22.  
  23. package java.awt.image;
  24.  
  25. import java.awt.image.ImageConsumer;
  26. import java.awt.image.ImageProducer;
  27. import java.awt.image.ColorModel;
  28. import java.util.Hashtable;
  29. import java.util.Vector;
  30. import java.util.Enumeration;
  31.  
  32. /**
  33.  * This class is an implementation of the ImageProducer interface which
  34.  * uses an array to produce pixel values for an Image.  Here is an example
  35.  * which calculates a 100x100 image representing a fade from black to blue
  36.  * along the X axis and a fade from black to red along the Y axis:
  37.  * <pre>
  38.  * 
  39.  *    int w = 100;
  40.  *    int h = 100;
  41.  *    int pix[] = new int[w * h];
  42.  *    int index = 0;
  43.  *    for (int y = 0; y < h; y++) {
  44.  *        int red = (y * 255) / (h - 1);
  45.  *        for (int x = 0; x < w; x++) {
  46.  *        int blue = (x * 255) / (w - 1);
  47.  *        pix[index++] = (255 << 24) | (red << 16) | blue;
  48.  *        }
  49.  *    }
  50.  *    Image img = createImage(new MemoryImageSource(w, h, pix, 0, w));
  51.  * 
  52.  * </pre>
  53.  * The MemoryImageSource is also capable of managing a memory image which
  54.  * varies over time to allow animation or custom rendering.  Here is an
  55.  * example showing how to set up the animation source and signal changes
  56.  * in the data (adapted from the MemoryAnimationSourceDemo by Garth Dickie):
  57.  * <pre>
  58.  *
  59.  *    int pixels[];
  60.  *    MemoryImageSource source;
  61.  *
  62.  *    public void init() {
  63.  *        int width = 50;
  64.  *        int height = 50;
  65.  *        int size = width * height;
  66.  *        pixels = new int[size];
  67.  *
  68.  *        int value = getBackground().getRGB();
  69.  *        for (int i = 0; i < size; i++) {
  70.  *        pixels[i] = value;
  71.  *        }
  72.  *
  73.  *        source = new MemoryImageSource(width, height, pixels, 0, width);
  74.  *        source.setAnimated(true);
  75.  *        image = createImage(source);
  76.  *    }
  77.  *
  78.  *    public void run() {
  79.  *        Thread me = Thread.currentThread( );
  80.  *        me.setPriority(Thread.MIN_PRIORITY);
  81.  *
  82.  *        while (true) {
  83.  *        try {
  84.  *            thread.sleep(10);
  85.  *        } catch( InterruptedException e ) {
  86.  *            return;
  87.  *        }
  88.  *
  89.  *        // Modify the values in the pixels array at (x, y, w, h)
  90.  *
  91.  *        // Send the new data to the interested ImageConsumers
  92.  *        source.newPixels(x, y, w, h);
  93.  *        }
  94.  *    }
  95.  *
  96.  * </pre>
  97.  *
  98.  * @see ImageProducer
  99.  *
  100.  * @version    1.18 11/23/96
  101.  * @author     Jim Graham
  102.  * @author    Animation capabilities inspired by the
  103.  *        MemoryAnimationSource class written by Garth Dickie
  104.  */
  105. public class MemoryImageSource implements ImageProducer {
  106.     int width;
  107.     int height;
  108.     ColorModel model;
  109.     Object pixels;
  110.     int pixeloffset;
  111.     int pixelscan;
  112.     Hashtable properties;
  113.     Vector theConsumers = new Vector();
  114.     boolean animating;
  115.     boolean fullbuffers;
  116.  
  117.     /**
  118.      * Constructs an ImageProducer object which uses an array of bytes
  119.      * to produce data for an Image object.
  120.      * @see java.awt.Component#createImage
  121.      */
  122.     public MemoryImageSource(int w, int h, ColorModel cm,
  123.                  byte[] pix, int off, int scan) {
  124.     initialize(w, h, cm, (Object) pix, off, scan, null);
  125.     }
  126.  
  127.     /**
  128.      * Constructs an ImageProducer object which uses an array of bytes
  129.      * to produce data for an Image object.
  130.      * @see java.awt.Component#createImage
  131.      */
  132.     public MemoryImageSource(int w, int h, ColorModel cm,
  133.                  byte[] pix, int off, int scan, Hashtable props) {
  134.     initialize(w, h, cm, (Object) pix, off, scan, props);
  135.     }
  136.  
  137.     /**
  138.      * Constructs an ImageProducer object which uses an array of integers
  139.      * to produce data for an Image object.
  140.      * @see java.awt.Component#createImage
  141.      */
  142.     public MemoryImageSource(int w, int h, ColorModel cm,
  143.                  int[] pix, int off, int scan) {
  144.     initialize(w, h, cm, (Object) pix, off, scan, null);
  145.     }
  146.  
  147.     /**
  148.      * Constructs an ImageProducer object which uses an array of integers
  149.      * to produce data for an Image object.
  150.      * @see java.awt.Component#createImage
  151.      */
  152.     public MemoryImageSource(int w, int h, ColorModel cm,
  153.                  int[] pix, int off, int scan, Hashtable props) {
  154.     initialize(w, h, cm, (Object) pix, off, scan, props);
  155.     }
  156.  
  157.     private void initialize(int w, int h, ColorModel cm,
  158.                 Object pix, int off, int scan, Hashtable props) {
  159.     width = w;
  160.     height = h;
  161.     model = cm;
  162.     pixels = pix;
  163.     pixeloffset = off;
  164.     pixelscan = scan;
  165.     if (props == null) {
  166.         props = new Hashtable();
  167.     }
  168.     properties = props;
  169.     }
  170.  
  171.     /**
  172.      * Constructs an ImageProducer object which uses an array of integers
  173.      * in the default RGB ColorModel to produce data for an Image object.
  174.      * @see java.awt.Component#createImage
  175.      * @see ColorModel#getRGBdefault
  176.      */
  177.     public MemoryImageSource(int w, int h, int pix[], int off, int scan) {
  178.     initialize(w, h, ColorModel.getRGBdefault(),
  179.            (Object) pix, off, scan, null);
  180.     }
  181.  
  182.     /**
  183.      * Constructs an ImageProducer object which uses an array of integers
  184.      * in the default RGB ColorModel to produce data for an Image object.
  185.      * @see java.awt.Component#createImage
  186.      * @see ColorModel#getRGBdefault
  187.      */
  188.     public MemoryImageSource(int w, int h, int pix[], int off, int scan,
  189.                  Hashtable props) {
  190.     initialize(w, h, ColorModel.getRGBdefault(),
  191.            (Object) pix, off, scan, props);
  192.     }
  193.  
  194.     /**
  195.      * Adds an ImageConsumer to the list of consumers interested in
  196.      * data for this image.
  197.      * @see ImageConsumer
  198.      */
  199.     public synchronized void addConsumer(ImageConsumer ic) {
  200.     if (theConsumers.contains(ic)) {
  201.         return;
  202.     }
  203.     theConsumers.addElement(ic);
  204.     try {
  205.         initConsumer(ic);
  206.         sendPixels(ic, 0, 0, width, height);
  207.         if (isConsumer(ic)) {
  208.         ic.imageComplete(animating
  209.                  ? ImageConsumer.SINGLEFRAMEDONE
  210.                  : ImageConsumer.STATICIMAGEDONE);
  211.         if (!animating && isConsumer(ic)) {
  212.             ic.imageComplete(ImageConsumer.IMAGEERROR);
  213.             removeConsumer(ic);
  214.         }
  215.         }
  216.     } catch (Exception e) {
  217.         if (isConsumer(ic)) {
  218.         ic.imageComplete(ImageConsumer.IMAGEERROR);
  219.         }
  220.     }
  221.     }
  222.  
  223.     /**
  224.      * Determine if an ImageConsumer is on the list of consumers currently
  225.      * interested in data for this image.
  226.      * @return true if the ImageConsumer is on the list; false otherwise
  227.      * @see ImageConsumer
  228.      */
  229.     public synchronized boolean isConsumer(ImageConsumer ic) {
  230.     return theConsumers.contains(ic);
  231.     }
  232.  
  233.     /**
  234.      * Remove an ImageConsumer from the list of consumers interested in
  235.      * data for this image.
  236.      * @see ImageConsumer
  237.      */
  238.     public synchronized void removeConsumer(ImageConsumer ic) {
  239.     theConsumers.removeElement(ic);
  240.     }
  241.  
  242.     /**
  243.      * Adds an ImageConsumer to the list of consumers interested in
  244.      * data for this image, and immediately start delivery of the
  245.      * image data through the ImageConsumer interface.
  246.      * @see ImageConsumer
  247.      */
  248.     public void startProduction(ImageConsumer ic) {
  249.     addConsumer(ic);
  250.     }
  251.  
  252.     /**
  253.      * Requests that a given ImageConsumer have the image data delivered
  254.      * one more time in top-down, left-right order.
  255.      * @see ImageConsumer
  256.      */
  257.     public void requestTopDownLeftRightResend(ImageConsumer ic) {
  258.     // Ignored.  The data is either single frame and already in TDLR
  259.     // format or it is multi-frame and TDLR resends aren't critical.
  260.     }
  261.  
  262.     /**
  263.      * Change this memory image into a multi-frame animation or a
  264.      * single-frame static image depending on the animated parameter.
  265.      * <p>This method should be called immediately after the
  266.      * MemoryImageSource is constructed and before an image is
  267.      * created with it to ensure that all ImageConsumers will
  268.      * receive the correct multi-frame data.  If an ImageConsumer
  269.      * is added to this ImageProducer before this flag is set then
  270.      * that ImageConsumer will see only a snapshot of the pixel
  271.      * data that was available when it connected.
  272.      * @param animated true if the image is a multi-frame animation
  273.      */
  274.     public synchronized void setAnimated(boolean animated) {
  275.     this.animating = animated;
  276.     if (!animating) {
  277.         Enumeration enum = theConsumers.elements();
  278.         while (enum.hasMoreElements()) {
  279.             ImageConsumer ic = (ImageConsumer) enum.nextElement();
  280.         ic.imageComplete(ImageConsumer.STATICIMAGEDONE);
  281.         if (isConsumer(ic)) {
  282.             ic.imageComplete(ImageConsumer.IMAGEERROR);
  283.         }
  284.         }
  285.         theConsumers.removeAllElements();
  286.     }
  287.     }
  288.  
  289.     /**
  290.      * Specify whether this animated memory image should always be
  291.      * updated by sending the complete buffer of pixels whenever
  292.      * there is a change.
  293.      * This flag is ignored if the animation flag is not turned on
  294.      * through the setAnimated() method.
  295.      * <p>This method should be called immediately after the
  296.      * MemoryImageSource is constructed and before an image is
  297.      * created with it to ensure that all ImageConsumers will
  298.      * receive the correct pixel delivery hints.
  299.      * @param fullbuffers true if the complete pixel buffer should always
  300.      * be sent
  301.      * @see #setAnimated
  302.      */
  303.     public synchronized void setFullBufferUpdates(boolean fullbuffers) {
  304.     if (this.fullbuffers == fullbuffers) {
  305.         return;
  306.     }
  307.     this.fullbuffers = fullbuffers;
  308.     if (animating) {
  309.         Enumeration enum = theConsumers.elements();
  310.         while (enum.hasMoreElements()) {
  311.             ImageConsumer ic = (ImageConsumer) enum.nextElement();
  312.         ic.setHints(fullbuffers
  313.                 ? (ImageConsumer.TOPDOWNLEFTRIGHT |
  314.                    ImageConsumer.COMPLETESCANLINES)
  315.                 : ImageConsumer.RANDOMPIXELORDER);
  316.         }
  317.     }
  318.     }
  319.  
  320.     /**
  321.      * Send a whole new buffer of pixels to any ImageConsumers that
  322.      * are currently interested in the data for this image and notify
  323.      * them that an animation frame is complete.
  324.      * This method only has effect if the animation flag has been
  325.      * turned on through the setAnimated() method.
  326.      * @see ImageConsumer
  327.      * @see #setAnimated
  328.      */
  329.     public void newPixels() {
  330.     newPixels(0, 0, width, height, true);
  331.     }
  332.  
  333.     /**
  334.      * Send a rectangular region of the buffer of pixels to any
  335.      * ImageConsumers that are currently interested in the data for
  336.      * this image and notify them that an animation frame is complete.
  337.      * This method only has effect if the animation flag has been
  338.      * turned on through the setAnimated() method.
  339.      * If the full buffer update flag was turned on with the
  340.      * setFullBufferUpdates() method then the rectangle parameters
  341.      * will be ignored and the entire buffer will always be sent.
  342.      * @param x the x coordinate of the upper left corner of the rectangle
  343.      * of pixels to be sent
  344.      * @param y the y coordinate of the upper left corner of the rectangle
  345.      * of pixels to be sent
  346.      * @param w the width of the rectangle of pixels to be sent
  347.      * @param h the height of the rectangle of pixels to be sent
  348.      * @see ImageConsumer
  349.      * @see #setAnimated
  350.      * @see #setFullBufferUpdates
  351.      */
  352.     public synchronized void newPixels(int x, int y, int w, int h) {
  353.     newPixels(x, y, w, h, true);
  354.     }
  355.  
  356.     /**
  357.      * Send a rectangular region of the buffer of pixels to any
  358.      * ImageConsumers that are currently interested in the data for
  359.      * this image.
  360.      * If the framenotify parameter is true then the consumers are
  361.      * also notified that an animation frame is complete.
  362.      * This method only has effect if the animation flag has been
  363.      * turned on through the setAnimated() method.
  364.      * If the full buffer update flag was turned on with the
  365.      * setFullBufferUpdates() method then the rectangle parameters
  366.      * will be ignored and the entire buffer will always be sent.
  367.      * @param x the x coordinate of the upper left corner of the rectangle
  368.      * of pixels to be sent
  369.      * @param y the y coordinate of the upper left corner of the rectangle
  370.      * of pixels to be sent
  371.      * @param w the width of the rectangle of pixels to be sent
  372.      * @param h the height of the rectangle of pixels to be sent
  373.      * @param framenotify true if the consumers should be sent a
  374.      * SINGLEFRAMEDONE notification
  375.      * @see ImageConsumer
  376.      * @see #setAnimated
  377.      * @see #setFullBufferUpdates
  378.      */
  379.     public synchronized void newPixels(int x, int y, int w, int h,
  380.                        boolean framenotify) {
  381.     if (animating) {
  382.         if (fullbuffers) {
  383.         x = y = 0;
  384.         w = width;
  385.         h = height;
  386.         } else {
  387.         if (x < 0) {
  388.             w += x;
  389.             x = 0;
  390.         }
  391.         if (x + w > width) {
  392.             w = width - x;
  393.         }
  394.         if (y < 0) {
  395.             h += y;
  396.             y = 0;
  397.         }
  398.         if (y + h > height) {
  399.             h = height - y;
  400.         }
  401.         }
  402.         if ((w <= 0 || h <= 0) && !framenotify) {
  403.         return;
  404.         }
  405.         Enumeration enum = theConsumers.elements();
  406.         while (enum.hasMoreElements()) {
  407.             ImageConsumer ic = (ImageConsumer) enum.nextElement();
  408.         if (w > 0 && h > 0) {
  409.             sendPixels(ic, x, y, w, h);
  410.         }
  411.         if (framenotify && isConsumer(ic)) {
  412.             ic.imageComplete(ImageConsumer.SINGLEFRAMEDONE);
  413.         }
  414.         }
  415.     }
  416.     }
  417.  
  418.     /**
  419.      * Change to a new byte array to hold the pixels for this image.
  420.      * If the animation flag has been turned on through the setAnimated()
  421.      * method, then the new pixels will be immediately delivered to any
  422.      * ImageConsumers that are currently interested in the data for
  423.      * this image.
  424.      * @see #setAnimated
  425.      */
  426.     public synchronized void newPixels(byte[] newpix, ColorModel newmodel,
  427.                        int offset, int scansize) {
  428.     this.pixels = newpix;
  429.     this.model = newmodel;
  430.     this.pixeloffset = offset;
  431.     this.pixelscan = scansize;
  432.     newPixels();
  433.     }
  434.  
  435.     /**
  436.      * Change to a new int array to hold the pixels for this image.
  437.      * If the animation flag has been turned on through the setAnimated()
  438.      * method, then the new pixels will be immediately delivered to any
  439.      * ImageConsumers that are currently interested in the data for
  440.      * this image.
  441.      * @see #setAnimated
  442.      */
  443.     public synchronized void newPixels(int[] newpix, ColorModel newmodel,
  444.                        int offset, int scansize) {
  445.     this.pixels = newpix;
  446.     this.model = newmodel;
  447.     this.pixeloffset = offset;
  448.     this.pixelscan = scansize;
  449.     newPixels();
  450.     }
  451.  
  452.     private void initConsumer(ImageConsumer ic) {
  453.     if (isConsumer(ic)) {
  454.         ic.setDimensions(width, height);
  455.     }
  456.     if (isConsumer(ic)) {
  457.         ic.setProperties(properties);
  458.     }
  459.     if (isConsumer(ic)) {
  460.         ic.setColorModel(model);
  461.     }
  462.     if (isConsumer(ic)) {
  463.         ic.setHints(animating
  464.             ? (fullbuffers
  465.                ? (ImageConsumer.TOPDOWNLEFTRIGHT |
  466.                   ImageConsumer.COMPLETESCANLINES)
  467.                : ImageConsumer.RANDOMPIXELORDER)
  468.             : (ImageConsumer.TOPDOWNLEFTRIGHT |
  469.                ImageConsumer.COMPLETESCANLINES |
  470.                ImageConsumer.SINGLEPASS |
  471.                ImageConsumer.SINGLEFRAME));
  472.     }
  473.     }
  474.  
  475.     private void sendPixels(ImageConsumer ic, int x, int y, int w, int h) {
  476.     int off = pixeloffset + pixelscan * y + x;
  477.     if (isConsumer(ic)) {
  478.         if (pixels instanceof byte[]) {
  479.         ic.setPixels(x, y, w, h, model,
  480.                  ((byte[]) pixels), off, pixelscan);
  481.         } else {
  482.         ic.setPixels(x, y, w, h, model,
  483.                  ((int[]) pixels), off, pixelscan);
  484.         }
  485.     }
  486.     }
  487. }
  488.