1 package org.jmage.filter.merge;
2
3 import org.apache.log4j.Logger;
4 import org.apache.log4j.Priority;
5 import org.jmage.filter.FilterException;
6 import org.jmage.filter.TwinResourceImageFilter;
7
8 import javax.media.jai.JAI;
9 import javax.media.jai.PlanarImage;
10 import java.awt.*;
11 import java.awt.image.BufferedImage;
12 import java.util.HashMap;
13 import java.util.Properties;
14
15 /***
16 * Overlays the image with another image. Specify the following imageProperties to change
17 * the appearance:<p>
18 * <p/>
19 * IMAGE_URI: the image resource to overlay the filtered image with.<br>
20 * OPACITY: [0-100] opacity percentage, where 0 is transparent and 100 is opaque.<br>
21 * ORIENTATION: [0-360] where values are in degrees. For convenience, use the constants defined, i.e "ORIENTATION_NORTH" (the default)<br>
22 * POSITION: [1-9] aligns the text following a keypad layout. Default is [9] POSITION_BOTTOM_RIGHT<br>
23 * BORDEROFFSET: a numeric value indicating how many pixels to offset the text from it's nearest border<br>
24 */
25 public class ImageOverlayFilter extends TwinResourceImageFilter {
26 public static final String OPACITY = "OPACITY";
27
28 public static final String ORIENTATION = "ORIENTATION";
29 public static final String ORIENTATION_NORTH = "0";
30 public static final String ORIENTATION_NORTHEAST = "45";
31 public static final String ORIENTATION_EAST = "90";
32 public static final String ORIENTATION_SOUTHEAST = "135";
33 public static final String ORIENTATION_SOUTH = "180";
34 public static final String ORIENTATION_SOUTHWEST = "225";
35 public static final String ORIENTATION_WEST = "270";
36 public static final String ORIENTATION_NORTHWEST = "315";
37
38 public static final String POSITION = "POSITION";
39 public static final String POSITION_TOP_LEFT = "1";
40 public static final String POSITION_TOP_CENTER = "2";
41 public static final String POSITION_TOP_RIGHT = "3";
42 public static final String POSITION_CENTER_LEFT = "4";
43 public static final String POSITION_CENTER = "5";
44 public static final String POSITION_CENTER_RIGHT = "6";
45 public static final String POSITION_BOTTOM_LEFT = "7";
46 public static final String POSITION_BOTTOM_CENTER = "8";
47 public static final String POSITION_BOTTOM_RIGHT = "9";
48
49 public static final String BORDEROFFSET = "BORDEROFFSET";
50
51 public static final String DEFAULT_ORIENTATION = ORIENTATION_NORTH;
52
53 protected BufferedImage overlayImage;
54 protected String position;
55 protected int xyO;
56 protected RenderingHints renderingHints;
57 protected double orientation;
58 protected float opacity;
59
60 protected static Logger log = Logger.getLogger(ImageOverlayFilter.class.getName());
61
62 private static final String EMPTY_PROPERTIES_ERROR = "properties for ImageOverlayFilter cannot be empty, need at least IMAGE_URI";
63 private static final String OPACITY_RANGE_ERROR = " values are only allowed ranging from 0-100, out of range error: ";
64 private static final String NO_OVERLAYIMAGE_ERROR = "unable to create image for IMAGE_URI: ";
65 private static final String POSITION_RANGE_ERROR = " values are only allowed ranging from 1-9, out of range error: ";
66 private static final String ORIENTATION_RANGE_ERROR = " values are only allowed ranging from 0-360, out of range error: ";
67 private static final String DEFAULT_OPACITY = "100";
68 private static final String DEFAULT_BORDEROFFSET = "0";
69
70 /***
71 * Initialize the ImageFilter
72 */
73 public void initialize(Properties filterProperties) throws FilterException {
74 try {
75 super.initialize(filterProperties);
76
77
78 opacity = Float.valueOf(filterProperties.getProperty(OPACITY, DEFAULT_OPACITY)).floatValue();
79 assert (opacity >= 1f && opacity <= 100f) : OPACITY + OPACITY_RANGE_ERROR + opacity;
80 opacity /= 100f;
81
82
83 position = filterProperties.getProperty(POSITION, POSITION_BOTTOM_RIGHT);
84 int pos = Integer.decode(position).intValue();
85 assert (pos >= 1 && pos <= 9) : POSITION + POSITION_RANGE_ERROR + position;
86
87 xyO = Integer.decode(filterProperties.getProperty(BORDEROFFSET, DEFAULT_BORDEROFFSET)).intValue();
88
89
90 orientation = Double.valueOf(filterProperties.getProperty(ORIENTATION, DEFAULT_ORIENTATION)).doubleValue();
91 assert (orientation >= 0 && orientation <= 360) : ORIENTATION + ORIENTATION_RANGE_ERROR + orientation;
92 orientation /= 180f;
93
94
95 HashMap paramMap = new HashMap();
96 paramMap.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
97 paramMap.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
98 paramMap.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
99 paramMap.put(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
100 renderingHints = new RenderingHints(paramMap);
101
102 this.filterProperties = filterProperties;
103 if (log.isDebugEnabled()) log.debug(INITIALIZED);
104 } catch (Throwable t) {
105 String message = NOT_INITIALIZED + t.getMessage();
106 this.filterProperties = null;
107 if (log.isEnabledFor(Priority.ERROR)) log.error(message);
108 throw new FilterException(message);
109 }
110 }
111
112 /***
113 * Overlay image with another image
114 *
115 * @throws org.jmage.filter.FilterException
116 * if an error occurs during filtering
117 */
118 public PlanarImage filter(PlanarImage image) throws FilterException {
119 super.filter(image);
120
121
122 BufferedImage bgImage = image.getAsBufferedImage();
123
124
125 overlayImage = resourceImage.getAsBufferedImage();
126
127
128 Graphics2D bgGraphics = (Graphics2D) bgImage.createGraphics();
129 int[] coords = this.alignOverlayFor(image, bgGraphics);
130
131
132 bgGraphics.setRenderingHints(renderingHints);
133 bgGraphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity));
134
135 bgGraphics.drawImage(overlayImage, coords[0], coords[1], null);
136 bgGraphics.dispose();
137
138
139 return (PlanarImage) JAI.create("AWTImage", (Image) bgImage);
140 }
141
142 /***
143 * calculates the coordinates to overlay the image at
144 *
145 * @param image the image
146 * @return an integer array with the x/y coordinates
147 */
148 protected int[] alignOverlayFor(PlanarImage image, Graphics2D graphics) {
149
150 int x = 0, y = 0;
151
152 int w0 = image.getWidth(), h0 = image.getHeight();
153 int w1 = overlayImage.getWidth(), h1 = overlayImage.getHeight();
154
155 if (POSITION_BOTTOM_RIGHT.equals(position)) {
156 x = w0 - w1 - xyO;
157 y = h0 - h1 - xyO;
158 }
159 if (POSITION_BOTTOM_CENTER.equals(position)) {
160 x = (w0 / 2) - (w1 / 2);
161 y = h0 - h1 - xyO;
162 }
163 if (POSITION_BOTTOM_LEFT.equals(position)) {
164 x = 0 + xyO;
165 y = h0 - h1 - xyO;
166 }
167 if (POSITION_CENTER_RIGHT.equals(position)) {
168 x = w0 - w1 - xyO;
169 y = (h0 / 2) - (h1 / 2);
170 }
171 if (POSITION_CENTER.equals(position)) {
172 x = (w0 / 2) - (w1 / 2);
173 y = (h0 / 2) - (h1 / 2);
174 }
175 if (POSITION_CENTER_LEFT.equals(position)) {
176 x = 0 + xyO;
177 y = (h0 / 2) - (h1 / 2);
178 }
179 if (POSITION_TOP_RIGHT.equals(position)) {
180 x = w0 - w1 - xyO;
181 y = 0 + xyO;
182 }
183 if (POSITION_TOP_CENTER.equals(position)) {
184 x = (w0 / 2) - (w1 / 2);
185 y = 0 + xyO;
186 }
187 if (POSITION_TOP_LEFT.equals(position)) {
188 x = 0 + xyO;
189 y = 0 + xyO;
190 }
191
192
193 graphics.rotate(Math.PI * orientation, x + (w1 / 2), y + (h1 / 2));
194
195
196 return new int[]{x, y};
197 }
198 }