All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.fit.cssbox.layout.ContentImage Maven / Gradle / Ivy

Go to download

CSSBox is an (X)HTML/CSS rendering engine written in pure Java. Its primary purpose is to provide a complete information about the rendered page suitable for further processing. However, it also allows displaying the rendered document.

There is a newer version: 5.0.2
Show newest version
/*
 * ContentImage.java
 * Copyright (c) 2005-2012 Radek Burget
 *
 * CSSBox is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *  
 * CSSBox 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 Lesser General Public License for more details.
 *  
 * You should have received a copy of the GNU Lesser General Public License
 * along with CSSBox. If not, see .
 *
 * Created on 12.6.2012, 9:30:12 by burgetr
 */
package org.fit.cssbox.layout;

import java.awt.Container;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.net.URL;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Generic image used in the page content (used by ReplacedImage and BackgroundImage).
 * 
 * @author burgetr
 */
public abstract class ContentImage extends ReplacedContent implements ImageObserver
{
    private static Logger log = LoggerFactory.getLogger(ContentImage.class);
    
    /** Used when there are no image data */
    protected final int DEFAULT_IMAGE_WIDTH = 0;
    /** Used when there are no image data */
    protected final int DEFAULT_IMAGE_HEIGHT = 0;
    
    /** Image loading timeout [ms] */
    protected int loadTimeout;
    
    protected boolean loadImages; //is the image loading switched on?
    protected boolean caching; //use picture caching?
    protected URL url; //image url
    protected Image image; //the loaded image
    protected int width = -1;
    protected int height = -1;

    protected ImageObserver observer; //assigned observer (if used)
    protected Container container; //component's container, for repaint
    protected Toolkit toolkit; //system default toolkit
    protected boolean abort; //error or abort flag during loading in image observer
    
    public ContentImage(ElementBox owner)
    {
        super(owner);
        this.loadImages = true;
        this.observer = this;
        this.toolkit = Toolkit.getDefaultToolkit();
        this.caching = true;
        this.container = null;
        this.abort = false;
        this.loadTimeout = owner.getViewport().getConfig().getImageLoadTimeout();
    }

    /**
     * Sets the component's container used by internal {@link ImageObserver}
     * when there is need to repaint (e.g. animated gif). If not set, rendering
     * may not work correctly. Null does not cause {@link NullPointerException}
     * 
     * @param container
     *            the new container
     * @see ImageObserver
     * @see Container
     */
    public void setContainer(Container container)
    {
        this.container = container;
    }

    /**
     * Gets the container. May return null.
     * 
     * @return the container
     */
    public Container getContainer()
    {
        return this.container;
    }

    /**
     * Fires repaint event within t milliseconds.
     * 
     * @param t
     *            the time to repaint within.
     * 
     * @see java.awt.Component#repaint(long, int, int, int, int)
     */
    protected void repaint(int t)
    {
        if (container != null)
        {
            Rectangle bounds = owner.getAbsoluteBounds();
            container.repaint(t, bounds.x, bounds.y, bounds.width, bounds.height);
        }
    }

    protected Image loadImage(boolean cache)
    {
        Image img;
        if (url != null)
        {
            if (cache)
            {
                // get image and cache
                img = toolkit.getImage(url);
            }
            else
            {
                // do not cache, just get image
                img = toolkit.createImage(url);
            }

            // start loading and preparation
            toolkit.prepareImage(img, -1, -1, observer);
            return img;
        }
        return null;
    }

    /**
     * Checks if caching is used when loading images.
     * 
     * @return true, if caching is used
     * 
     * @see ReplacedImage#setCaching(boolean)
     */
    public boolean isCaching()
    {
        return caching;
    }

    /**
     * Sets the caching. If set to true, image is checked, if present in cache.
     * If not, it is loaded and placed to cache. If set to False image is just
     * loaded, any cache and caching is avoided.
     * 
     * @param val
     *            indicates, whether use cache. Default is true.
     * @see Toolkit#getImage(URL)
     * @see Toolkit#createImage(URL)
     */
    public void setCaching(boolean val)
    {
        caching = val;
    }

    /**
     * Gets the url.
     * 
     * @return the url of the image.
     */
    public URL getUrl()
    {
        return url;
    }

    /**
     * Gets the image.
     * 
     * @return the image.
     */
    public Image getImage()
    {
        return image;
    }

    /**
     * Gets the loaded image as BufferedImage object. May return null if no data
     * available.
     * 
     * @return the buffered image.
     */
    public BufferedImage getBufferedImage()
    {
        if (image == null || abort)
            return null;
        
        BufferedImage img = new BufferedImage(getIntrinsicWidth(), getIntrinsicHeight(), BufferedImage.TYPE_INT_RGB);
        Graphics2D g = img.createGraphics();
        g.drawImage(image, null, null);
        g.dispose();

        return img;
    }

    /**
     * Sets the image observer.
     * 
     * @param observer
     *            the new image observer.
     * @see ImageObserver
     */
    public void setImageObserver(ImageObserver observer)
    {
        this.observer = observer;
    }

    /**
     * Gets the image observer.
     * 
     * @return the image observer
     * @see ImageObserver
     */
    public ImageObserver getImageObserver()
    {
        return this.observer;
    }

    @Override
    public int getIntrinsicHeight()
    {
        if (height > -1)
        {
            return height; //previously computed height
        }
        else
        {
            if (loadImages)
            {
                if (image == null)
                    image = loadImage(caching);
                height = obtainImageHeight();
                return height;
            }
            else
            {
                return DEFAULT_IMAGE_HEIGHT;
            }
        }
    }

    @Override
    public int getIntrinsicWidth()
    {
        if (width > -1)
        {
            return width; //previously computed width
        }
        else
        {
            if (loadImages)
            {
                if (image == null)
                    image = loadImage(caching);
                width = obtainImageWidth();
                return width;// if there was an error, height == DEFAULT_IMAGE_HEIGHT
            }
            else
            {
                return DEFAULT_IMAGE_WIDTH;
            }
        }
    }

    private int obtainImageWidth()
    {
        int width = -1;
        abort = false;
        int loadtime = 0;
        while (!abort && image != null && (width = image.getWidth(observer)) == -1)
        {
            try
            {
                if (loadtime > loadTimeout)
                {
                    image = null;
                    abort = true;
                    log.warn("Image loading aborted for timeout: " + url + " " + loadtime);
                }
                Thread.sleep(25);
                loadtime += 25;
            } catch (Exception e)
            {
                image = null;
                abort = true;
                log.warn("Image loading aborted: " + e.getMessage());
            }
        }
        if (width == -1) width = DEFAULT_IMAGE_WIDTH;
        return width;
    }

    private int obtainImageHeight()
    {
        int height = -1;
        abort = false;
        int loadtime = 0;
        while (!abort && image != null && (height = image.getHeight(observer)) == -1)
        {
            try
            {
                if (loadtime > loadTimeout)
                {
                    image = null;
                    abort = true;
                    log.warn("Image loading aborted for timeout: " + url);
                }
                else
                    Thread.sleep(25);
                loadtime += 25;
            } catch (Exception e)
            {
                image = null;
                abort = true;
                log.warn("Image loading aborted: " + e.getMessage());
            }
        }

        if (height == -1) height = DEFAULT_IMAGE_HEIGHT;
        return height;
    }
    
    @Override
    public float getIntrinsicRatio()
    {
        return (float) getIntrinsicWidth() / (float) getIntrinsicHeight();
    }

    /**
     * Releases the image resources.
     */
    public void dispose()
    {
        if (image != null) image.flush();
        image = null;
    }

    /**
     * Resets all data to default state, releases the image resources.
     */
    public void reset()
    {
        abort = false;
        width = -1;
        height = -1;
        if (image != null) image.flush();
        image = null;
    }

    /**
     * Aborts waiting for image data to determine width and/or height. This
     * method is called from ImageObserver. If custom ImageObserver is provided
     * and ABORT or ERROR flags are set during loading, call this method to
     * abort waiting. Otherwise infinite loop will be created, resulting in
     * dead-lock.
     * 
     * @see ImageObserver
     * @see ImageObserver#ABORT
     * @see ImageObserver#ERROR
     */
    public synchronized void abort()
    {
        abort = true;
        image = null;
    }

    public boolean imageUpdate(Image img, int flags, int x, int y, int newWidth, int newHeight)
    {
        // http://www.permadi.com/tutorial/javaImgObserverAndAnimGif/
        if (image == null || image != img) { return false; }

        // error
        if ((flags & (ABORT | ERROR)) != 0)
        {
            synchronized (this)
            {
                if (image == img)
                {
                    abort();
                }
            }

            repaint(0);
            return false;
        }

        // Repaint when done or when new pixels arrive:
        // FRAMEBITS - another complete frame of a multi-frame image which was
        // previously drawn is now available to be drawn again.
        // ALLBITS - static image which was previously drawn is now complete and
        // can be drawn again in its final form.
        if ((flags & (FRAMEBITS | ALLBITS)) != 0)
        {
            repaint(0);
        }

        // hint : provide some "Loading..." animation...
        // else if ((flags & SOMEBITS) != 0) {}
        return ((flags & ALLBITS) == 0) /* && owner.isVisible() */;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy