/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.wicket.markup.html.image.resource;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.lang.ref.SoftReference;
import org.apache.wicket.request.resource.DynamicImageResource;
import org.apache.wicket.util.time.Time;
/**
* A DynamicImageResource subclass that allows easy rendering of regeneratable (unbuffered) dynamic
* images. A RenderedDynamicImageResource implements the abstract method render(Graphics2D) to
* create/re-create a given image on-the-fly. When a RenderedDynamicImageResource is serialized, the
* image state is transient, which means it will disappear when the resource is sent over the wire
* and then will be recreated when required.
* <p>
* The format of the image (and therefore the resource's extension) can be specified with
* setFormat(String). The default format is "PNG" because JPEG is lossy and makes generated images
* look bad and GIF has patent issues.
*
* @see org.apache.wicket.markup.html.image.resource.DefaultButtonImageResource
* @see org.apache.wicket.markup.html.image.resource.DefaultButtonImageResourceFactory
* @author Jonathan Locke
* @author Gili Tzabari
* @author Johan Compagner
*/
public abstract class RenderedDynamicImageResource extends DynamicImageResource
{
private static final long serialVersionUID = 1L;
/** Height of image */
private int height = 100;
/** Transient image data so that image only needs to be generated once per VM */
private transient SoftReference<byte[]> imageData;
/** Type of image (one of BufferedImage.TYPE_*) */
private int type = BufferedImage.TYPE_INT_RGB;
/** Width of image */
private int width = 100;
/**
* Constructor.
*
* @param width
* Width of image
* @param height
* Height of image
*/
public RenderedDynamicImageResource(final int width, final int height)
{
this.width = width;
this.height = height;
}
/**
* Constructor.
*
* @param width
* Width of image
* @param height
* Height of image
* @param format
* The format of the image (jpg, png or gif)
*/
public RenderedDynamicImageResource(final int width, final int height, String format)
{
super(format);
this.width = width;
this.height = height;
}
/**
* @return Returns the height.
*/
public synchronized int getHeight()
{
return height;
}
/**
* @return Returns the type (one of BufferedImage.TYPE_*).
*/
public synchronized int getType()
{
return type;
}
/**
* @return Returns the width.
*/
public synchronized int getWidth()
{
return width;
}
/**
* Causes the image to be redrawn the next time its requested.
*/
public synchronized void invalidate()
{
imageData = null;
}
/**
* @param height
* The height to set.
*/
public synchronized void setHeight(int height)
{
this.height = height;
invalidate();
}
/**
* @param type
* The type to set (one of BufferedImage.TYPE_*).
*/
public synchronized void setType(int type)
{
this.type = type;
invalidate();
}
/**
* @param width
* The width to set.
*/
public synchronized void setWidth(int width)
{
this.width = width;
invalidate();
}
@Override
protected byte[] getImageData(Attributes attributes)
{
// get image data is always called in sync block
byte[] data = null;
if (imageData != null)
{
data = imageData.get();
}
if (data == null)
{
data = render(attributes);
imageData = new SoftReference<byte[]>(data);
setLastModifiedTime(Time.now());
}
return data;
}
/**
* Renders this image
*
* @param attributes
* the current request attributes
* @return The image data
*/
protected byte[] render(final Attributes attributes)
{
while (true)
{
final BufferedImage image = new BufferedImage(getWidth(), getHeight(), getType());
if (render((Graphics2D)image.getGraphics(), attributes))
{
return toImageData(image);
}
}
}
/**
* Override this method to provide your rendering code.
*
* @param graphics
* The graphics context to render on.
* @param attributes
* the current request attributes
* @return {@code true} if the image was rendered. {@code false} if the image size was changed
* by the rendering implementation and the image should be re-rendered at the new size.
*/
protected abstract boolean render(Graphics2D graphics, final Attributes attributes);
}