Package com.bbn.openmap.image

Source Code of com.bbn.openmap.image.AbstractImageFormatter

// **********************************************************************
//
// <copyright>
//
//  BBN Technologies
//  10 Moulton Street
//  Cambridge, MA 02138
//  (617) 873-8000
//
//  Copyright (C) BBNT Solutions LLC. All rights reserved.
//
// </copyright>
// **********************************************************************
//
// $Source: /cvs/distapps/openmap/src/openmap/com/bbn/openmap/image/AbstractImageFormatter.java,v $
// $RCSfile: AbstractImageFormatter.java,v $
// $Revision: 1.5.2.4 $
// $Date: 2008/01/25 17:44:27 $
// $Author: dietrick $
//
// **********************************************************************

package com.bbn.openmap.image;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Properties;

import com.bbn.openmap.Layer;
import com.bbn.openmap.MapBean;
import com.bbn.openmap.PropertyConsumer;
import com.bbn.openmap.proj.Proj;
import com.bbn.openmap.proj.Projection;
import com.bbn.openmap.util.Debug;

/**
* The abstract implementation of the ImageFormatter. The
* ImageFormatter deals with most of the image meanderings of Java,
* while letting you create an image in a specific format. The
* ImageFormatter's responsibility has grown slightly, since it now
* contains the BufferedImage that it will be formatting. Thisis to
* make things go smoother for different uses of the formatter - some
* image formats, for instance, really need to utilize a special
* implementation of a Graphics in order to create the data file they
* want. The new definition allows for that. Generally, however,
* you'll want to either hand the MapBean to the formatter to get the
* image bytes, or, as in the case of the ImageServer, get a Graphics
* from the formatter, paint the map into it, then retrieve the image
* bytes after that.
*/
public abstract class AbstractImageFormatter implements ImageFormatter,
        PropertyConsumer, PropertyChangeListener {

    protected BufferedImage bufferedImage;
    protected String propertiesPrefix;

    public AbstractImageFormatter() {}

    /** Set the properties of the image formatter. */
    public void setProperties(String prefix, Properties props) {}

    /**
     * Convert a BufferedImage to a image file format...
     *
     * @param bi a BufferedImage..
     */
    public abstract byte[] formatImage(BufferedImage bi);

    /**
     * Create a new instance of the same type of formatter. If you are
     * running in a multi-threaded environment, you'll need to provide
     * a new instance of the formatter to each thread, since the image
     * and graphics that are being drawn into for each thread are
     * contained within.
     *
     * @return a new instance of this type of formatter, with the same
     *         properties set.
     */
    public abstract ImageFormatter makeClone();

    /**
     * Take a MapBean, and get the image bytes that represent the
     * current state.
     *
     * @param map the MapBean.
     * @return byte[] representing an image of the map in it's current
     *         state.
     */
    public byte[] getImageFromMapBean(MapBean map) {
        return getImageFromMapBean(map, -1, -1, false);
    }

    /**
     * Take a MapBean, and get the image bytes that represent the
     * current state.
     *
     * @param map the MapBean.
     * @param width the pixel width of the desired image.
     * @param height the pixel height of the desired image.
     * @return byte[] representing an image of the map in it's current
     *         state.
     */
    public byte[] getImageFromMapBean(MapBean map, int width, int height) {
        return getImageFromMapBean(map, width, height, true);
    }

    /**
     * Take a MapBean, and get the image bytes that represent the
     * current state.
     *
     * @param map the MapBean.
     * @param width the pixel width of the desired image.
     * @param height the pixel height of the desired image.
     * @param scaleImage true to resize image based on scale
     * @return byte[] representing an image of the map in it's current
     *         state.
     */
    public byte[] getImageFromMapBean(MapBean map, int width, int height,
                                      boolean scaleImage) {
        if (map == null) {
            return new byte[0];
        }

        Proj proj = (Proj) map.getProjection();

        boolean needToScale = (width != proj.getWidth() || height != proj.getHeight());

        if (Debug.debugging("formatter")) {
            Debug.output("AIF: called with w:" + width + ", h:" + height
                    + ", need to scale (" + needToScale + ")"
                    + " and scaleImage (" + scaleImage + ")");
        }

        if (width == -1)
            width = proj.getWidth();
        if (height == -1)
            height = proj.getHeight();

        Graphics graphics = getGraphics(width, height);

        if (!needToScale) {
            if (Debug.debugging("formatter")) {
                Debug.output("AIF: don't need to scale, painting normally.");
            }
            // This way just paints what the MapBean is displaying.
            map.paintAll(graphics);
        } else {
            // One problem with this approach is that it will
            // use the ProjectionPainter interface on the layers. So,
            // you may not get the same image that is on the map. All
            // layers on the map will get painted in the image - so if
            // a layer hasn't painted itself on the map window, you
            // will see it in the image.

            // This lets us know what the layers are
            map.addPropertyChangeListener(this);

            // Layers should be set...
            com.bbn.openmap.LatLonPoint cp = new com.bbn.openmap.LatLonPoint(map.getCenter());

            double scaleMod = 1f;// scale factor for image scale
            // If we need to scale the image,
            // figure out the scale factor.
            if (scaleImage) {
                if (Debug.debugging("formatter")) {
                    Debug.output("AIF: scaling image to w:" + width + ", h:"
                            + height);
                }
                double area1 = (double) proj.getHeight()
                        * (double) proj.getWidth();
                double area2 = (double) height * (double) width;
                scaleMod = Math.sqrt(area1 / area2);
            }

            Proj tp = (Proj) com.bbn.openmap.proj.ProjectionFactory.makeProjection(map.getProjection()
                    .getClass(),
                    cp.getLatitude(),
                    cp.getLongitude(),
                    map.getScale() * (float) scaleMod,
                    width,
                    height);

            tp.drawBackground((Graphics2D) graphics, map.getBckgrnd());

            if (layers != null) {
                for (int i = layers.length - 1; i >= 0; i--) {
                    Projection oldProj = layers[i].getProjection();
                    layers[i].renderDataForProjection(tp, graphics);
                    if (Debug.debugging("formatter")) {
                        Debug.output("AbstractImageFormatter: rendering "
                                + layers[i].getName());
                    }
                    // Need to set the old Projection object on the
                    // Layer, not the current MapBean Proj object. If
                    // you set the MapBean Proj object, make sure you
                    // clone it first. The Layer will do a check on
                    // the Projection object it has against any new
                    // ones it receives. If it has the original from
                    // the MapBean, the check it does will return a
                    // false negative, and the layer will think it
                    // doesn't have to do anything.

                    if (oldProj != null && oldProj == map.getProjection()) {
                        // Seems like a lot of users are getting
                        // burned by manually setting the same
                        // projection on the MapBean as they are on
                        // the layers, and the layers are freezing up
                        // after they are used to create an image.

                        // I don't see how this problem is manifesting
                        // itself, but this code section is an attempt
                        // to help.
                        oldProj = oldProj.makeClone();
                    }

                    layers[i].setProjection(oldProj);
                }

            } else {
                Debug.output("AbstractImageFormatter can't get layers from map!");
            }

            map.removePropertyChangeListener(this);
            layers = null;
        }

        return getImageBytes();
    }

    /**
     * Return the applicable Graphics to use to paint the layers into.
     * If the internal BufferedImage hasn't been created yet, or has
     * been set to null, then a new buffered Image is created, set to
     * the size specified by the height and width. The ImageGenerator
     * extends MapBean. Remember to dispose of the graphics object
     * when you are done with it. Uses the default
     * BufferedImage.TYPE_INT_RGB colormodel.
     *
     * @param width pixel width of Graphics.
     * @param height pixel height of Graphics.
     * @return Graphics object to use.
     * @see java.awt.image.BufferedImage
     */
    public Graphics getGraphics(int width, int height) {
        return getGraphics(width, height, BufferedImage.TYPE_INT_RGB);
    }

    /**
     * Return the applicable Graphics to use to paint the layers into.
     * If the internal BufferedImage hasn't been created yet, or has
     * been set to null, then a new buffered Image is created, set to
     * the size specified by the height and width. The ImageGenerator
     * extends MapBean. Remember to dispose of the graphics object
     * when you are done with it. Lets you select the image type.
     *
     * @param width pixel width of Graphics.
     * @param height pixel height of Graphics.
     * @param imageType image type - see BufferedImage
     * @return java.awt.Graphics object to use.
     * @see java.awt.image.BufferedImage
     */
    public Graphics getGraphics(int width, int height, int imageType) {
        bufferedImage = new BufferedImage(width, height, imageType);

        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        Graphics g = ge.createGraphics(bufferedImage);
        g.setClip(0, 0, width, height);
        return g;
    }

    /**
     * Return the BufferedImage contained within the formatter.
     *
     * @return the BufferedImage.
     */
    public BufferedImage getBufferedImage() {
        return bufferedImage;
    }

    /**
     * Return the BufferedImage contained within the formatter.
     *
     * @param bi the BufferedImage to use in this formatter.
     */
    public void setBufferedImage(BufferedImage bi) {
        bufferedImage = bi;
    }

    /**
     * Scale the internal BufferedImage to the pixel dimensions, and
     * then return it.
     *
     * @param scaledWidth the desired pixel width of the image.
     * @param scaledHeight the desired pixel height of the image.
     * @return the scaled BufferedImage.
     */
    public BufferedImage getScaledBufferedImage(int scaledWidth,
                                                int scaledHeight) {

        if (bufferedImage == null) {
            return null;
        }

        if (Debug.debugging("formatter")) {
            Debug.output("Formatter: scaling image to : " + scaledWidth + ", "
                    + scaledHeight);
        }

        java.awt.Image image = ImageScaler.getOptimalScalingImage(bufferedImage, scaledWidth,
                scaledHeight);

        if (Debug.debugging("formatter")) {
            Debug.output("Formatter: creating scaled image...");
        }

        try {

            BufferedImage buffi = BufferedImageHelper.getBufferedImage(image,
                    0,
                    0,
                    -1,
                    -1);

            // Do this here, in case something bad happens in the
            // buffered image creation, so at least the original image
            // is retained.
            bufferedImage = buffi;
        } catch (InterruptedException ie) {
            Debug.error("Formatter: Something bad happened during scaling! \n"
                    + ie);
        }

        if (Debug.debugging("formatter")) {
            Debug.output("Formatter: image successfully scaled");
        }

        return bufferedImage;
    }

    /**
     * Return the image bytes of the formatted image.
     *
     * @return byte[] representing the image.
     */
    public byte[] getImageBytes() {
        BufferedImage bi = getBufferedImage();
        if (bi == null) {
            return new byte[0];
        } else {
            Debug.message("formatter",
                    "Formatter: creating formatted image bytes...");
            return formatImage(bi);
        }
    }

    /**
     * Scale the internal BufferedImage, then return the image bytes
     * of the formatted image.
     *
     * @param scaledWidth the desired pixel width of the image.
     * @param scaledHeight the desired pixel height of the image.
     * @return byte[] representing the image.
     */
    public byte[] getScaledImageBytes(int scaledWidth, int scaledHeight) {
        BufferedImage bi = getScaledBufferedImage(scaledWidth, scaledHeight);
        if (bi == null) {
            return new byte[0];
        } else {
            Debug.message("formatter",
                    "Formatter: creating formatted image bytes...");
            return formatImage(bi);
        }
    }

    /**
     * Set the layers and image type in the properties.
     */
    public void setProperties(Properties props) {
        setProperties((String) null, props);
    }

    /**
     * Part of the PropertyConsumer interface. Doesn't do anything
     * yet.
     */
    public Properties getProperties(Properties props) {
        if (props == null) {
            props = new Properties();
        }
        return props;
    }

    /**
     * Part of the PropertyConsumer interface.
     */
    public Properties getPropertyInfo(Properties list) {
        if (list == null) {
            list = new Properties();
        }
        return list;

    }

    /**
     * Part of the PropertyConsumer interface. Set the Properties
     * prefix to use to scope the relevant properties passed into the
     * setProperties method.
     */
    public void setPropertyPrefix(String prefix) {
        propertiesPrefix = prefix;
    }

    /**
     * Part of the PropertyConsumer interface. Get the Properties
     * prefix used to scope the relevant properties passed into the
     * setProperties method.
     */
    public String getPropertyPrefix() {
        return propertiesPrefix;
    }

    /**
     * Used when the layers from the MapBean are needed, in order to
     * use the renderDataForProjection method.
     */
    protected Layer[] layers = null;

    /**
     * Used when the layers from the MapBean are needed, in order to
     * use the renderDataForProjection method. Sets the Layer[] by
     * adding the formatter as a PropertyChangeListener to the
     * MapBean. Remember to remove the formatter from the MapBean as a
     * PropertyChangeListener.
     */
    public void propertyChange(PropertyChangeEvent pce) {
        String propName = pce.getPropertyName();
        if (propName == MapBean.LayersProperty) {
            layers = (Layer[]) pce.getNewValue();
        }
    }
}
TOP

Related Classes of com.bbn.openmap.image.AbstractImageFormatter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.