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; } } [...] }