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

org.xhtmlrenderer.swing.ImageResourceLoader Maven / Gradle / Ivy

Go to download

Flying Saucer is a CSS 2.1 renderer written in Java. This artifact contains the core rendering and layout code as well as Java2D output.

There is a newer version: 9.11.0
Show newest version
package org.xhtmlrenderer.swing;

import org.xhtmlrenderer.extend.FSImage;
import org.xhtmlrenderer.resource.ImageResource;
import org.xhtmlrenderer.util.Configuration;
import org.xhtmlrenderer.util.ImageUtil;
import org.xhtmlrenderer.util.StreamResource;
import org.xhtmlrenderer.util.XRLog;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.logging.Level;

public class ImageResourceLoader {
    public static final RepaintListener NO_OP_REPAINT_LISTENER = doLayout -> XRLog.general(Level.FINE, "No-op repaint requested");
    private final Map _imageCache;

    private final ImageLoadQueue _loadQueue;

    private final int _imageCacheCapacity;

    private RepaintListener _repaintListener = NO_OP_REPAINT_LISTENER;

    private final boolean _useBackgroundImageLoading;

    public ImageResourceLoader() {
        // FIXME
        this(16);
    }

    public ImageResourceLoader(int cacheSize) {
        this._imageCacheCapacity = cacheSize;
        this._useBackgroundImageLoading = Configuration.isTrue("xr.image.background.loading.enable", false);

        if (_useBackgroundImageLoading) {
            this._loadQueue = new ImageLoadQueue();
            final int workerCount = Configuration.valueAsInt("xr.image.background.workers", 5);
            for (int i = 0; i < workerCount; i++) {
                new ImageLoadWorker(_loadQueue).start();
            }
        } else {
            this._loadQueue = null;
        }

        // note we do *not* override removeEldestEntry() here--users of this class must call shrinkImageCache().
        // that's because we don't know when is a good time to flush the cache
        this._imageCache = new LinkedHashMap<>(cacheSize, 0.75f, true);
    }

    public static ImageResource loadImageResourceFromUri(final String uri) {
        if (ImageUtil.isEmbeddedBase64Image(uri)) {
            return loadEmbeddedBase64ImageResource(uri);
        } else {
            StreamResource sr = new StreamResource(uri);
            InputStream is;
            ImageResource ir = null;
            try {
                sr.connect();
                is = sr.bufferedStream();
                try {
                    BufferedImage img = ImageIO.read(is);
                    if (img == null) {
                        throw new IOException("ImageIO.read() returned null");
                    }
                    ir = createImageResource(uri, img);
                } catch (FileNotFoundException e) {
                    XRLog.exception("Can't read image file; image at URI '" + uri + "' not found");
                } catch (IOException e) {
                    XRLog.exception("Can't read image file; unexpected problem for URI '" + uri + "'", e);
                } finally {
                    sr.close();
                }
            } catch (IOException e) {
                // couldn't open stream at URI...
                XRLog.exception("Can't open stream for URI '" + uri + "': " + e.getMessage());
            }
            if (ir == null) {
                ir = createImageResource(uri, null);
            }
            return ir;
        }
    }

    public static ImageResource loadEmbeddedBase64ImageResource(final String uri) {
        BufferedImage bufferedImage = ImageUtil.loadEmbeddedBase64Image(uri);
        if (bufferedImage != null) {
            FSImage image = AWTFSImage.createImage(bufferedImage);
            return new ImageResource(null, image);
        } else {
            return new ImageResource(null, null);
        }
    }

    public synchronized void shrink() {
        int ovr = _imageCache.size() - _imageCacheCapacity;
        Iterator it = _imageCache.keySet().iterator();
        while (it.hasNext() && ovr-- > 0) {
            it.next();
            it.remove();
        }
    }

    public synchronized void clear() {
        _imageCache.clear();
    }

    public ImageResource get(final String uri) {
        return get(uri, -1, -1);
    }

    public synchronized ImageResource get(final String uri, final int width, final int height) {
        if (ImageUtil.isEmbeddedBase64Image(uri)) {
            ImageResource resource = loadEmbeddedBase64ImageResource(uri);
            resource.getImage().scale(width, height);
            return resource;
        } else {
            CacheKey key = new CacheKey(uri, width, height);
            ImageResource ir = _imageCache.get(key);
            if (ir == null) {
                // not loaded, or not loaded at target size

                // loaded a base size?
                ir = _imageCache.get(new CacheKey(uri, -1, -1));

                // no: loaded
                if (ir == null) {
                    if (isImmediateLoadUri(uri)) {
                        XRLog.load(Level.FINE, "Load immediate: " + uri);
                        ir = loadImageResourceFromUri(uri);
                        FSImage awtfsImage = ir.getImage();
                        BufferedImage newImg = ((AWTFSImage) awtfsImage).getImage();
                        loaded(ir, -1, -1);
                        if (width > -1 && height > -1) {
                            XRLog.load(Level.FINE, this + ", scaling " + uri + " to " + width + ", " + height);
                            newImg = ImageUtil.getScaledInstance(newImg, width, height);
                            ir = new ImageResource(ir.getImageUri(), AWTFSImage.createImage(newImg));
                            loaded(ir, width, height);
                        }
                    } else {
                        XRLog.load(Level.FINE, "Image cache miss, URI not yet loaded, queueing: " + uri);
                        MutableFSImage mfsi = new MutableFSImage(_repaintListener);
                        ir = new ImageResource(uri, mfsi);
                        _loadQueue.addToQueue(this, uri, mfsi, width, height);
                    }

                    _imageCache.put(key, ir);
                } else {
                    // loaded at base size, need to scale
                    XRLog.load(Level.FINE, this + ", scaling " + uri + " to " + width + ", " + height);
                    FSImage awtfsImage = ir.getImage();
                    BufferedImage newImg = ((AWTFSImage) awtfsImage).getImage();

                    newImg = ImageUtil.getScaledInstance(newImg, width, height);
                    ir = new ImageResource(ir.getImageUri(), AWTFSImage.createImage(newImg));
                    loaded(ir, width, height);
                }
            }
            return ir;
        }
    }

    public boolean isImmediateLoadUri(final String uri) {
        return ! _useBackgroundImageLoading || uri.startsWith("jar:file:") || uri.startsWith("file:");
    }

    public synchronized void loaded(final ImageResource ir, final int width, final int height) {
        String imageUri = ir.getImageUri();
        if (imageUri != null) {
            _imageCache.put(new CacheKey(imageUri, width, height), ir);
        }
    }

    public static ImageResource createImageResource(final String uri, final BufferedImage img) {
        if (img == null) {
            return new ImageResource(uri, AWTFSImage.createImage(ImageUtil.createTransparentImage(10, 10)));
        } else {
            return new ImageResource(uri, AWTFSImage.createImage(ImageUtil.makeCompatible(img)));
        }
    }

    public void setRepaintListener(final RepaintListener repaintListener) {
        _repaintListener = repaintListener;
    }

    public void stopLoading() {
        if (_loadQueue != null) {
            XRLog.load("By request, clearing pending items from load queue: " + _loadQueue.size());
            _loadQueue.reset();
        }
    }

    private record CacheKey(String uri, int width, int height) {
    }
}

// from-io-loader
// from-cache-loader
// from-fs-loader




© 2015 - 2024 Weber Informatics LLC | Privacy Policy