View Javadoc

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              //opacity
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              //position
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              //orientation
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              //rendering hints
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         //make current image background
122         BufferedImage bgImage = image.getAsBufferedImage();
123 
124         //new overlay image based on resource
125         overlayImage = resourceImage.getAsBufferedImage();
126 
127         // Align instance variable overlay image with background image
128         Graphics2D bgGraphics = (Graphics2D) bgImage.createGraphics();
129         int[] coords = this.alignOverlayFor(image, bgGraphics);
130 
131         // draw image on background
132         bgGraphics.setRenderingHints(renderingHints);
133         bgGraphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity));
134         //TODO: fix bug where overlay images are bigger than background.
135         bgGraphics.drawImage(overlayImage, coords[0], coords[1], null);
136         bgGraphics.dispose();
137 
138         // Convert back and return
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         // Init positioning
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         //rotate
193         graphics.rotate(Math.PI * orientation, x + (w1 / 2), y + (h1 / 2));
194 
195         //set to 0/0 for overlay images bigger than the original, else return.
196         return new int[]{x, y};
197     }
198 }