001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.markup.html.image.resource; 018 019import java.awt.Graphics2D; 020import java.awt.image.BufferedImage; 021import java.lang.ref.SoftReference; 022import java.time.Instant; 023import org.apache.wicket.request.resource.DynamicImageResource; 024 025 026/** 027 * A DynamicImageResource subclass that allows easy rendering of regeneratable (unbuffered) dynamic 028 * images. A RenderedDynamicImageResource implements the abstract method render(Graphics2D) to 029 * create/re-create a given image on-the-fly. When a RenderedDynamicImageResource is serialized, the 030 * image state is transient, which means it will disappear when the resource is sent over the wire 031 * and then will be recreated when required. 032 * <p> 033 * The format of the image (and therefore the resource's extension) can be specified with 034 * setFormat(String). The default format is "PNG" because JPEG is lossy and makes generated images 035 * look bad and GIF has patent issues. 036 * 037 * @see org.apache.wicket.markup.html.image.resource.DefaultButtonImageResource 038 * @see org.apache.wicket.markup.html.image.resource.DefaultButtonImageResourceFactory 039 * @author Jonathan Locke 040 * @author Gili Tzabari 041 * @author Johan Compagner 042 */ 043public abstract class RenderedDynamicImageResource extends DynamicImageResource 044{ 045 private static final long serialVersionUID = 1L; 046 047 /** Height of image */ 048 private int height = 100; 049 050 /** Transient image data so that image only needs to be generated once per VM */ 051 private transient SoftReference<byte[]> imageData; 052 053 /** Type of image (one of BufferedImage.TYPE_*) */ 054 private int type = BufferedImage.TYPE_INT_RGB; 055 056 /** Width of image */ 057 private int width = 100; 058 059 /** 060 * Constructor. 061 * 062 * @param width 063 * Width of image 064 * @param height 065 * Height of image 066 */ 067 public RenderedDynamicImageResource(final int width, final int height) 068 { 069 this.width = width; 070 this.height = height; 071 } 072 073 /** 074 * Constructor. 075 * 076 * @param width 077 * Width of image 078 * @param height 079 * Height of image 080 * @param format 081 * The format of the image (jpg, png or gif) 082 */ 083 public RenderedDynamicImageResource(final int width, final int height, String format) 084 { 085 super(format); 086 this.width = width; 087 this.height = height; 088 } 089 090 /** 091 * @return Returns the height. 092 */ 093 public synchronized int getHeight() 094 { 095 return height; 096 } 097 098 /** 099 * @return Returns the type (one of BufferedImage.TYPE_*). 100 */ 101 public synchronized int getType() 102 { 103 return type; 104 } 105 106 /** 107 * @return Returns the width. 108 */ 109 public synchronized int getWidth() 110 { 111 return width; 112 } 113 114 /** 115 * Causes the image to be redrawn the next time its requested. 116 */ 117 public synchronized void invalidate() 118 { 119 imageData = null; 120 } 121 122 /** 123 * @param height 124 * The height to set. 125 */ 126 public synchronized void setHeight(int height) 127 { 128 this.height = height; 129 invalidate(); 130 } 131 132 /** 133 * @param type 134 * The type to set (one of BufferedImage.TYPE_*). 135 */ 136 public synchronized void setType(int type) 137 { 138 this.type = type; 139 invalidate(); 140 } 141 142 /** 143 * @param width 144 * The width to set. 145 */ 146 public synchronized void setWidth(int width) 147 { 148 this.width = width; 149 invalidate(); 150 } 151 152 @Override 153 protected byte[] getImageData(Attributes attributes) 154 { 155 // get image data is always called in sync block 156 byte[] data = null; 157 if (imageData != null) 158 { 159 data = imageData.get(); 160 } 161 if (data == null) 162 { 163 data = render(attributes); 164 imageData = new SoftReference<byte[]>(data); 165 setLastModifiedTime(Instant.now()); 166 } 167 return data; 168 } 169 170 /** 171 * Renders this image 172 * 173 * @param attributes 174 * the current request attributes 175 * @return The image data 176 */ 177 protected byte[] render(final Attributes attributes) 178 { 179 while (true) 180 { 181 final BufferedImage image = new BufferedImage(getWidth(), getHeight(), getType()); 182 if (render((Graphics2D)image.getGraphics(), attributes)) 183 { 184 return toImageData(image); 185 } 186 } 187 } 188 189 /** 190 * Override this method to provide your rendering code. 191 * 192 * @param graphics 193 * The graphics context to render on. 194 * @param attributes 195 * the current request attributes 196 * @return {@code true} if the image was rendered. {@code false} if the image size was changed 197 * by the rendering implementation and the image should be re-rendered at the new size. 198 */ 199 protected abstract boolean render(Graphics2D graphics, final Attributes attributes); 200}