FileDocCategorySizeDatePackage
ImageView.javaAPI DocJava SE 5 API33515Fri Aug 26 14:58:18 BST 2005javax.swing.text.html

ImageView

public class ImageView extends View
View of an Image, intended to support the HTML <IMG> tag. Supports scaling via the HEIGHT and WIDTH attributes of the tag. If the image is unable to be loaded any text specified via the ALT attribute will be rendered.

While this class has been part of swing for a while now, it is public as of 1.4.

author
Scott Violet
version
1.56 12/19/03
see
IconView
since
1.4

Fields Summary
private static boolean
sIsInc
If true, when some of the bits are available a repaint is done.

This is set to false as swing does not offer a repaint that takes a delay. If this were true, a bunch of immediate repaints would get generated that end up significantly delaying the loading of the image (or anything else going on for that matter).

private static int
sIncRate
Repaint delay when some of the bits are available.
private static Icon
sPendingImageIcon
Icon used while the image is being loaded.
private static Icon
sMissingImageIcon
Icon used if the image could not be found.
private static final String
PENDING_IMAGE_SRC
File name for sPendingImageIcon.
private static final String
MISSING_IMAGE_SRC
File name for sMissingImageIcon.
private static final String
IMAGE_CACHE_PROPERTY
Document property for image cache.
private static final int
DEFAULT_WIDTH
private static final int
DEFAULT_HEIGHT
private static final int
DEFAULT_BORDER
Default border to use if one is not specified.
private static final int
LOADING_FLAG
private static final int
LINK_FLAG
private static final int
WIDTH_FLAG
private static final int
HEIGHT_FLAG
private static final int
RELOAD_FLAG
private static final int
RELOAD_IMAGE_FLAG
private static final int
SYNC_LOAD_FLAG
private AttributeSet
attr
private Image
image
private int
width
private int
height
private int
state
Bitmask containing some of the above bitmask values. Because the image loading notification can happen on another thread access to this is synchronized (at least for modifying it).
private Container
container
private Rectangle
fBounds
private Color
borderColor
private short
borderSize
private short
leftInset
private short
rightInset
private short
topInset
private short
bottomInset
private ImageObserver
imageObserver
We don't directly implement ImageObserver, instead we use an instance that calls back to us.
private View
altView
Used for alt text. Will be non-null if the image couldn't be found, and there is valid alt text.
private float
vAlign
Alignment along the vertical (Y) axis.
Constructors Summary
public ImageView(Element elem)
Creates a new view that represents an IMG element.

param
elem the element to create a view for




                           
       
    	super(elem);
	fBounds = new Rectangle();
        imageObserver = new ImageHandler();
        state = RELOAD_FLAG | RELOAD_IMAGE_FLAG;
    
Methods Summary
public voidchangedUpdate(javax.swing.event.DocumentEvent e, java.awt.Shape a, javax.swing.text.ViewFactory f)
Invoked when the Elements attributes have changed. Recreates the image.

    	super.changedUpdate(e,a,f);

        synchronized(this) {
            state |= RELOAD_FLAG | RELOAD_IMAGE_FLAG;
        }

        // Assume the worst.
        preferenceChanged(null, true, true);
    
public floatgetAlignment(int axis)
Determines the desired alignment for this view along an axis. This is implemented to give the alignment to the bottom of the icon along the y axis, and the default along the x axis.

param
axis may be either X_AXIS or Y_AXIS
return
the desired alignment; this should be a value between 0.0 and 1.0 where 0 indicates alignment at the origin and 1.0 indicates alignment to the full span away from the origin; an alignment of 0.5 would be the center of the view

	switch (axis) {
	case View.Y_AXIS:
	    return vAlign;
	default:
	    return super.getAlignment(axis);
	}
    
public java.lang.StringgetAltText()
Returns the text to display if the image can't be loaded. This is obtained from the Elements attribute set with the attribute name HTML.Attribute.ALT.

        return (String)getElement().getAttributes().getAttribute
            (HTML.Attribute.ALT);
    
private javax.swing.text.ViewgetAltView()
Returns the view to use for alternate text. This may be null.

        View view;

        synchronized(this) {
            view = altView;
        }
        if (view != null && view.getParent() == null) {
            view.setParent(getParent());
        }
        return view;
    
public javax.swing.text.AttributeSetgetAttributes()
Fetches the attributes to use when rendering. This is implemented to multiplex the attributes specified in the model with a StyleSheet.

        sync();
	return attr;
    
public java.awt.ImagegetImage()
Returns the image to render.

        sync();
        return image;
    
public java.net.URLgetImageURL()
Return a URL for the image source, or null if it could not be determined.

 	String src = (String)getElement().getAttributes().
                             getAttribute(HTML.Attribute.SRC);
 	if (src == null) {
            return null;
        }

	URL reference = ((HTMLDocument)getDocument()).getBase();
        try {
 	    URL u = new URL(reference,src);
	    return u;
        } catch (MalformedURLException e) {
	    return null;
        }
    
private intgetIntAttr(javax.swing.text.html.HTML$Attribute name, int deflt)
Convenience method for getting an integer attribute from the elements AttributeSet.

    	AttributeSet attr = getElement().getAttributes();
    	if (attr.isDefined(name)) {		// does not check parents!
    	    int i;
 	    String val = (String)attr.getAttribute(name);
 	    if (val == null) {
 	    	i = deflt;
            }
 	    else {
 	    	try{
 	            i = Math.max(0, Integer.parseInt(val));
 	    	}catch( NumberFormatException x ) {
 	    	    i = deflt;
 	    	}
            }
	    return i;
	} else
	    return deflt;
    
public javax.swing.IcongetLoadingImageIcon()
Returns the icon to use while in the process of loading the image.

        loadDefaultIconsIfNecessary();
        return sPendingImageIcon;
    
public booleangetLoadsSynchronously()
Returns true if the image should be loaded when first asked for.

        return ((state & SYNC_LOAD_FLAG) != 0);
    
public javax.swing.IcongetNoImageIcon()
Returns the icon to use if the image couldn't be found.

        loadDefaultIconsIfNecessary();
        return sMissingImageIcon;
    
public floatgetPreferredSpan(int axis)
Determines the preferred span for this view along an axis.

param
axis may be either X_AXIS or Y_AXIS
return
the span the view would like to be rendered into; typically the view is told to render into the span that is returned, although there is no guarantee; the parent may choose to resize or break the view

        sync();

        // If the attributes specified a width/height, always use it!
        if (axis == View.X_AXIS && (state & WIDTH_FLAG) == WIDTH_FLAG) {
            getPreferredSpanFromAltView(axis);
            return width + leftInset + rightInset;
        }
        if (axis == View.Y_AXIS && (state & HEIGHT_FLAG) == HEIGHT_FLAG) {
            getPreferredSpanFromAltView(axis);
            return height + topInset + bottomInset;
        }

        Image image = getImage();

        if (image != null) {
            switch (axis) {
            case View.X_AXIS:
                return width + leftInset + rightInset;
            case View.Y_AXIS:
                return height + topInset + bottomInset;
            default:
                throw new IllegalArgumentException("Invalid axis: " + axis);
            }
        }
        else {
            View view = getAltView();
            float retValue = 0f;

            if (view != null) {
                retValue = view.getPreferredSpan(axis);
            }
            switch (axis) {
            case View.X_AXIS:
                return retValue + (float)(width + leftInset + rightInset);
            case View.Y_AXIS:
                return retValue + (float)(height + topInset + bottomInset);
            default:
                throw new IllegalArgumentException("Invalid axis: " + axis);
            }
        }
    
private floatgetPreferredSpanFromAltView(int axis)
Returns the preferred span of the View used to display the alt text, or 0 if the view does not exist.

        if (getImage() == null) {
            View view = getAltView();

            if (view != null) {
                return view.getPreferredSpan(axis);
            }
        }
        return 0f;
    
protected javax.swing.text.html.StyleSheetgetStyleSheet()
Convenience method to get the StyleSheet.

	HTMLDocument doc = (HTMLDocument) getDocument();
	return doc.getStyleSheet();
    
public java.lang.StringgetToolTipText(float x, float y, java.awt.Shape allocation)
For images the tooltip text comes from text specified with the ALT attribute. This is overriden to return getAltText.

see
JTextComponent#getToolTipText

        return getAltText();
    
private booleanhasPixels(java.awt.Image image)
Returns true if the passed in image has a non-zero width and height.

        return image != null &&
            (image.getHeight(imageObserver) > 0) &&
            (image.getWidth(imageObserver) > 0);
    
private booleanisLink()
Returns true if this image within a link?

	return ((state & LINK_FLAG) == LINK_FLAG);
    
private voidloadDefaultIconsIfNecessary()

        try {
            if (sPendingImageIcon == null)
            	sPendingImageIcon = makeIcon(PENDING_IMAGE_SRC);
            if (sMissingImageIcon == null)
            	sMissingImageIcon = makeIcon(MISSING_IMAGE_SRC);
	} catch(Exception x) {
	    System.err.println("ImageView: Couldn't load image icons");
	}
    
private voidloadImage()
Loads the image from the URL getImageURL. This should only be invoked from refreshImage.

        URL src = getImageURL();
        Image newImage = null;
        if (src != null) {
            Dictionary cache = (Dictionary)getDocument().
                                    getProperty(IMAGE_CACHE_PROPERTY);
            if (cache != null) {
                newImage = (Image)cache.get(src);
            }
            else {
                newImage = Toolkit.getDefaultToolkit().createImage(src);
                if (newImage != null && getLoadsSynchronously()) {
                    // Force the image to be loaded by using an ImageIcon.
                    ImageIcon ii = new ImageIcon();
                    ii.setImage(newImage);
                }
            }
        }
        image = newImage;
    
private javax.swing.IconmakeIcon(java.lang.String gifFile)

        /* Copy resource into a byte array.  This is
         * necessary because several browsers consider
         * Class.getResource a security risk because it
         * can be used to load additional classes.
         * Class.getResourceAsStream just returns raw
         * bytes, which we can convert to an image.
         */
	InputStream resource = HTMLEditorKit.getResourceAsStream(gifFile);

        if (resource == null) {
            System.err.println(ImageView.class.getName() + "/" + 
                               gifFile + " not found.");
            return null; 
        }
        BufferedInputStream in = 
            new BufferedInputStream(resource);
        ByteArrayOutputStream out = 
            new ByteArrayOutputStream(1024);
        byte[] buffer = new byte[1024];
        int n;
        while ((n = in.read(buffer)) > 0) {
            out.write(buffer, 0, n);
        }
        in.close();
        out.flush();

        buffer = out.toByteArray();
        if (buffer.length == 0) {
            System.err.println("warning: " + gifFile + 
                               " is zero-length");
            return null;
        }
        return new ImageIcon(buffer);
    
public java.awt.ShapemodelToView(int pos, java.awt.Shape a, javax.swing.text.Position$Bias b)
Provides a mapping from the document model coordinate space to the coordinate space of the view mapped to it.

param
pos the position to convert
param
a the allocated region to render into
return
the bounding box of the given position
exception
BadLocationException if the given position does not represent a valid location in the associated document
see
View#modelToView

	int p0 = getStartOffset();
	int p1 = getEndOffset();
	if ((pos >= p0) && (pos <= p1)) {
	    Rectangle r = a.getBounds();
	    if (pos == p1) {
		r.x += r.width;
	    }
	    r.width = 0;
	    return r;
	}
	return null;
    
public voidpaint(java.awt.Graphics g, java.awt.Shape a)
Paints the View.

param
g the rendering surface to use
param
a the allocated region to render into
see
View#paint

        sync();

	Rectangle rect = (a instanceof Rectangle) ? (Rectangle)a :
                         a.getBounds();

        Image image = getImage();
        Rectangle clip = g.getClipBounds();

	fBounds.setBounds(rect);
        paintHighlights(g, a);
        paintBorder(g, rect);
        if (clip != null) {
            g.clipRect(rect.x + leftInset, rect.y + topInset,
                       rect.width - leftInset - rightInset,
                       rect.height - topInset - bottomInset);
        }
        if (image != null) {
            if (!hasPixels(image)) {
                // No pixels yet, use the default
                Icon icon = (image == null) ? getNoImageIcon() :
                                               getLoadingImageIcon();

                if (icon != null) {
                    icon.paintIcon(getContainer(), g, rect.x + leftInset,
                                   rect.y + topInset);
                }
            }
            else {
                // Draw the image
                g.drawImage(image, rect.x + leftInset, rect.y + topInset,
                            width, height, imageObserver);
            }
        }
        else {
            Icon icon = getNoImageIcon();

            if (icon != null) {
                icon.paintIcon(getContainer(), g, rect.x + leftInset,
                               rect.y + topInset);
            }
            View view = getAltView();
            // Paint the view representing the alt text, if its non-null
            if (view != null && ((state & WIDTH_FLAG) == 0 ||
                                 width > DEFAULT_WIDTH)) {
                // Assume layout along the y direction
                Rectangle altRect = new Rectangle
                    (rect.x + leftInset + DEFAULT_WIDTH, rect.y + topInset,
                     rect.width - leftInset - rightInset - DEFAULT_WIDTH,
                     rect.height - topInset - bottomInset);

                view.paint(g, altRect);
            }
        }
        if (clip != null) {
            // Reset clip.
            g.setClip(clip.x, clip.y, clip.width, clip.height);
        }
    
private voidpaintBorder(java.awt.Graphics g, java.awt.Rectangle rect)

        Color color = borderColor;

        if ((borderSize > 0 || image == null) && color != null) {
            int xOffset = leftInset - borderSize;
            int yOffset = topInset - borderSize;
            g.setColor(color);
            int n = (image == null) ? 1 : borderSize;
	    for (int counter = 0; counter < n; counter++) {
	        g.drawRect(rect.x + xOffset + counter,
                           rect.y + yOffset + counter,
                           rect.width - counter - counter - xOffset -xOffset-1,
                           rect.height - counter - counter -yOffset-yOffset-1);
            }
        }
    
private voidpaintHighlights(java.awt.Graphics g, java.awt.Shape shape)

	if (container instanceof JTextComponent) {
	    JTextComponent tc = (JTextComponent)container;
	    Highlighter h = tc.getHighlighter();
	    if (h instanceof LayeredHighlighter) {
		((LayeredHighlighter)h).paintLayeredHighlights
		    (g, getStartOffset(), getEndOffset(), shape, tc, this);
	    }
	}
    
private voidrefreshImage()
Loads the image and updates the size accordingly. This should be invoked instead of invoking loadImage or updateImageSize directly.

	synchronized(this) {
            // clear out width/height/realoadimage flag and set loading flag
            state = (state | LOADING_FLAG | RELOAD_IMAGE_FLAG | WIDTH_FLAG |
                     HEIGHT_FLAG) ^ (WIDTH_FLAG | HEIGHT_FLAG |
                                     RELOAD_IMAGE_FLAG);
            image = null;
            width = height = 0;
        }

        try {
            // Load the image
            loadImage();

            // And update the size params
            updateImageSize();
        }
        finally {
            synchronized(this) {
                // Clear out state in case someone threw an exception.
                state = (state | LOADING_FLAG) ^ LOADING_FLAG;
            }
        }
    
private voidrepaint(long delay)
Request that this view be repainted. Assumes the view is still at its last-drawn location.

    	if (container != null && fBounds != null) {
	    container.repaint(delay, fBounds.x, fBounds.y, fBounds.width,
                               fBounds.height);
    	}
    
private voidsafePreferenceChanged()
Invokes preferenceChanged on the event displatching thread.

        if (SwingUtilities.isEventDispatchThread()) {
	    Document doc = getDocument();
	    if (doc instanceof AbstractDocument) {
		((AbstractDocument)doc).readLock();
	    }
            preferenceChanged(null, true, true);
	    if (doc instanceof AbstractDocument) {
		((AbstractDocument)doc).readUnlock();
	    }
        }
        else {
            SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        safePreferenceChanged();
                    }
                });
        }
    
public voidsetLoadsSynchronously(boolean newValue)
Sets how the image is loaded. If newValue is true, the image we be loaded when first asked for, otherwise it will be loaded asynchronously. The default is to not load synchronously, that is to load the image asynchronously.

        synchronized(this) {
            if (newValue) {
                state |= SYNC_LOAD_FLAG;
            }
            else {
                state = (state | SYNC_LOAD_FLAG) ^ SYNC_LOAD_FLAG;
            }
        }
    
public voidsetParent(javax.swing.text.View parent)
Establishes the parent view for this view. Seize this moment to cache the AWT Container I'm in.

        View oldParent = getParent();
	super.setParent(parent);
	container = (parent != null) ? getContainer() : null;
        if (oldParent != parent) {
            synchronized(this) {
                state |= RELOAD_FLAG;
            }
        }
    
protected voidsetPropertiesFromAttributes()
Update any cached values that come from attributes.

        StyleSheet sheet = getStyleSheet();
        this.attr = sheet.getViewAttributes(this);

        // Gutters
        borderSize = (short)getIntAttr(HTML.Attribute.BORDER, isLink() ?
                                       DEFAULT_BORDER : 0);

        leftInset = rightInset = (short)(getIntAttr(HTML.Attribute.HSPACE,
                                                    0) + borderSize);
        topInset = bottomInset = (short)(getIntAttr(HTML.Attribute.VSPACE,
                                                    0) + borderSize);

        borderColor = ((StyledDocument)getDocument()).getForeground
                      (getAttributes());

        AttributeSet attr = getElement().getAttributes();

        // Alignment.
        // PENDING: This needs to be changed to support the CSS versions
        // when conversion from ALIGN to VERTICAL_ALIGN is complete.
        Object alignment = attr.getAttribute(HTML.Attribute.ALIGN);

        vAlign = 1.0f;
        if (alignment != null) {
            alignment = alignment.toString();
            if ("top".equals(alignment)) {
                vAlign = 0f;
            }
            else if ("middle".equals(alignment)) {
                vAlign = .5f;
            }
        }

        AttributeSet anchorAttr = (AttributeSet)attr.getAttribute(HTML.Tag.A);
        if (anchorAttr != null && anchorAttr.isDefined
            (HTML.Attribute.HREF)) {
            synchronized(this) {
                state |= LINK_FLAG;
            }
        }
        else {
            synchronized(this) {
                state = (state | LINK_FLAG) ^ LINK_FLAG;
            }
        }
    
public voidsetSize(float width, float height)
Sets the size of the view. This should cause layout of the view if it has any layout duties.

param
width the width >= 0
param
height the height >= 0

        sync();

        if (getImage() == null) {
            View view = getAltView();

            if (view != null) {
		view.setSize(Math.max(0f, width - (float)(DEFAULT_WIDTH + leftInset + rightInset)),
			     Math.max(0f, height - (float)(topInset + bottomInset)));
            }
        }
    
private voidsync()
Makes sure the necessary properties and image is loaded.

        int s = state;
        if ((s & RELOAD_IMAGE_FLAG) != 0) {
            refreshImage();
        }
        s = state;
        if ((s & RELOAD_FLAG) != 0) {
            synchronized(this) {
                state = (state | RELOAD_FLAG) ^ RELOAD_FLAG;
            }
            setPropertiesFromAttributes();
        }
    
private voidupdateAltTextView()
Updates the view representing the alt text.

        String text = getAltText();

        if (text != null) {
            ImageLabelView newView;

            newView = new ImageLabelView(getElement(), text);
            synchronized(this) {
                altView = newView;
            }
        }
    
private voidupdateImageSize()
Recreates and reloads the image. This should only be invoked from refreshImage.

	int newWidth = 0;
	int newHeight = 0;
        int newState = 0;
        Image newImage = getImage();

        if (newImage != null) {
            Element elem = getElement();
	    AttributeSet attr = elem.getAttributes();

            // Get the width/height and set the state ivar before calling
            // anything that might cause the image to be loaded, and thus the
            // ImageHandler to be called.
	    newWidth = getIntAttr(HTML.Attribute.WIDTH, -1);
            if (newWidth > 0) {
                newState |= WIDTH_FLAG;
            }
	    newHeight = getIntAttr(HTML.Attribute.HEIGHT, -1);
            if (newHeight > 0) {
                newState |= HEIGHT_FLAG;
            }

            if (newWidth <= 0) {
		newWidth = newImage.getWidth(imageObserver);
                if (newWidth <= 0) {
                    newWidth = DEFAULT_WIDTH;
                }
            }

            if (newHeight <= 0) {
		newHeight = newImage.getHeight(imageObserver);
                if (newHeight <= 0) {
                    newHeight = DEFAULT_HEIGHT;
                }
            }

	    // Make sure the image starts loading:
            if ((newState & (WIDTH_FLAG | HEIGHT_FLAG)) != 0) {
                Toolkit.getDefaultToolkit().prepareImage(newImage, newWidth,
                                                         newHeight,
                                                         imageObserver);
            }
            else {
                Toolkit.getDefaultToolkit().prepareImage(newImage, -1, -1,
                                                         imageObserver);
            }

            boolean createText = false;
	    synchronized(this) {
                // If imageloading failed, other thread may have called
                // ImageLoader which will null out image, hence we check
                // for it.
                if (image != null) {
                    if ((newState & WIDTH_FLAG) == WIDTH_FLAG || width == 0) {
                        width = newWidth;
                    }
                    if ((newState & HEIGHT_FLAG) == HEIGHT_FLAG ||
                        height == 0) {
                        height = newHeight;
                    }
                }
                else {
                    createText = true;
                    if ((newState & WIDTH_FLAG) == WIDTH_FLAG) {
                        width = newWidth;
                    }
                    if ((newState & HEIGHT_FLAG) == HEIGHT_FLAG) {
                        height = newHeight;
                    }
                }
                state = state | newState;
                state = (state | LOADING_FLAG) ^ LOADING_FLAG;
            }
            if (createText) {
                // Only reset if this thread determined image is null
                updateAltTextView();
	    }
	}
        else {
            width = height = DEFAULT_HEIGHT;
            updateAltTextView();
        }
    
public intviewToModel(float x, float y, java.awt.Shape a, javax.swing.text.Position$Bias[] bias)
Provides a mapping from the view coordinate space to the logical coordinate space of the model.

param
x the X coordinate
param
y the Y coordinate
param
a the allocated region to render into
return
the location within the model that best represents the given point of view
see
View#viewToModel

	Rectangle alloc = (Rectangle) a;
	if (x < alloc.x + alloc.width) {
	    bias[0] = Position.Bias.Forward;
	    return getStartOffset();
	}
	bias[0] = Position.Bias.Backward;
	return getEndOffset();