How can I create an image that is a composition of any number of other images ?

Here is a simple solution that relies on a "source" ResourceReference and a "decoration" ResourceReference array, used to decorate the source.

/**
 * Dynamic image resource
 * 
 * @author Antoine
 * 
 */
public class CompositeImageResource extends RenderedDynamicImageResource {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	/**
	 * Default icon width. Other implementations could obtain this data from the
	 * images meta datas
	 */
	private static final int ICON_WIDTH = 16;

	/**
	 * Default icon height. Other implementations could obtain this data from
	 * the images meta datas
	 */
	private static final int ICON_HEIGHT = 16;

	/**
	 * {@link ResourceReference} to the base image to decorate
	 */
	private final ResourceReference source;

	/**
	 * {@link ResourceReference}[] to use as decorations (will be stacked over
	 * the source)
	 */
	private final ResourceReference[] overlays;

	/**
	 * @param width
	 * @param height
	 */
	public CompositeImageResource(ResourceReference originalIcon,
			ResourceReference... overlays) {
		super(ICON_WIDTH, ICON_HEIGHT);

		// Disable any caching
		setCacheable(false);

		// Set GIF format to stay away from transparency issues on IE < 7
		setFormat("png");

		// Insure transparency management
		setType(BufferedImage.TYPE_INT_ARGB);
		this.source = originalIcon;
		this.overlays = overlays;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.apache.wicket.markup.html.image.resource.RenderedDynamicImageResource#render(java.awt.Graphics2D)
	 */
	@Override
	protected boolean render(Graphics2D g2) {
		this.source.bind(Application.get());
		for (ResourceReference ref : this.overlays) {
			// Not sure this is the right way to ensure binding?
			ref.bind(Application.get());
		}
		BufferedImage baseIcon;
		BufferedImage[] errorIcons = new BufferedImage[overlays.length];
		// Try reading the original images from the resource reference
		// resource
		// streams
		try {
			baseIcon = ImageIO.read(source.getResource().getResourceStream()
					.getInputStream());
			for (int i = 0; i < overlays.length; i++) {
				errorIcons[i] = ImageIO.read(overlays[i].getResource()
						.getResourceStream().getInputStream());
			}
		} catch (IOException e) {
			throw new WicketRuntimeException(e);
		} catch (ResourceStreamNotFoundException e) {
			throw new WicketRuntimeException(e);
		}
		// Prepare g2 for transparency
		g2.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
		Rectangle2D.Double rect = new Rectangle2D.Double(0, 0, ICON_WIDTH,
				ICON_HEIGHT);
		g2.fill(rect);

		// Prepare g2 for image overlay
		g2
				.setComposite(AlphaComposite.getInstance(
						AlphaComposite.SRC_OVER, 1f));
		g2.drawImage(baseIcon, null, null);
		for (Image overlay : errorIcons) {
			g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
					1f));
			g2.drawImage(overlay, null, null);
		}
		return true;
	}

}

Let's see how to use this for implement a Tree that renders the node's icon decorated with errors, notification, etc... images :

import java.io.Serializable;

import javax.swing.tree.TreeNode;

import org.apache.wicket.Component;
import org.apache.wicket.IResourceListener;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.RequestCycle;
import org.apache.wicket.Resource;
import org.apache.wicket.ResourceReference;
import org.apache.wicket.extensions.markup.html.tree.Tree;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.link.Link;


public class DecoratedTree extends Tree {

	static final ResourceReference ERROR = new ResourceReference(
			FormTree.class, "error.gif");


	[...]

	@Override
	protected Component newNodeIcon(MarkupContainer parent, String id,
			final TreeNode node) {
		class IconComponent extends WebMarkupContainer {

			private static final long serialVersionUID = 1L;

			private ResourceReference reference;

			IconComponent(String id) {
				super(id);
				// Obtain the original image resource reference 
				ResourceReference icon = ...
				if (hasError) {
					// Error? Let's use the CompositeImageResource with our icon 
					// and the static ERROR decoration and name accordingly the ResourceReference
					this.reference = new DynamicResourceReference(icon.getName()
						+ "_error", new CompositeImageResource(icon, ERROR));
				} else {
					// No error? let's stick to the source resource reference
					this.reference = icon;
				}
			}

			protected void onComponentTag(ComponentTag tag) {
				super.onComponentTag(tag);
				// Obtain an url to our image, wheter it be the original icon
				// or the generated image
				CharSequence url = reference != null ? RequestCycle.get()
						.urlFor(reference) : this
						.urlFor(IResourceListener.INTERFACE);

				tag.put("style", "background-image: url('"
						+ RequestCycle.get().getOriginalResponse().encodeURL(
								url) + "')");
			}

			@Override
			public void onResourceRequested() {
				this.icon.onResourceRequested();

			}
		}
		return new IconComponent(id);
	}

	/**
	 * A resource reference implem used to wrap the dynamic generated resource
	 */
	private static class DynamicResourceReference extends ResourceReference {		

		//Wrapped resource
		private final Resource res;

		public DynamicResourceReference(String name, Resource res) {
			super(DecoratedTree.class, name);
			this.res = res;
		}

		@Override
		protected Resource newResource() {
			return res;
		}

	}

	[...]
}

  • No labels