001: //==============================================================================
002: //=== Copyright (C) 2001-2007 Food and Agriculture Organization of the
003: //=== United Nations (FAO-UN), United Nations World Food Programme (WFP)
004: //=== and United Nations Environment Programme (UNEP)
005: //===
006: //=== This program is free software; you can redistribute it and/or modify
007: //=== it under the terms of the GNU General Public License as published by
008: //=== the Free Software Foundation; either version 2 of the License, or (at
009: //=== your option) any later version.
010: //===
011: //=== This program is distributed in the hope that it will be useful, but
012: //=== WITHOUT ANY WARRANTY; without even the implied warranty of
013: //=== MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: //=== General Public License for more details.
015: //===
016: //=== You should have received a copy of the GNU General Public License
017: //=== along with this program; if not, write to the Free Software
018: //=== Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
019: //===
020: //=== Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2,
021: //=== Rome - Italy. email: geonetwork@osgeo.org
022: //===
023: //=== Merges different files into one.
024: //===
025: //=== Coded by Emanuele Tajariol - Dec 2002
026: //===
027: //=== Test performed under Java 1.3.1_06 on Linux and JAI 1.1.2 Beta
028: //==============================================================================
029:
030: package org.wfp.vam.intermap.kernel.map.images;
031:
032: import Acme.JPM.Encoders.GifEncoder;
033: import com.sun.image.codec.jpeg.JPEGCodec;
034: import com.sun.image.codec.jpeg.JPEGImageEncoder;
035: import com.sun.media.jai.codec.ImageCodec;
036: import com.sun.media.jai.codec.PNGEncodeParam;
037: import com.sun.media.jai.codecimpl.PNGCodec;
038: import com.sun.media.jai.codecimpl.PNGImageEncoder;
039: import java.awt.AlphaComposite;
040: import java.awt.Color;
041: import java.awt.Graphics2D;
042: import java.awt.Image;
043: import java.awt.image.BufferedImage;
044: import java.io.ByteArrayOutputStream;
045: import java.io.FileOutputStream;
046: import java.io.IOException;
047: import java.io.OutputStream;
048: import java.util.Enumeration;
049: import java.util.List;
050: import java.util.Vector;
051: import javax.swing.ImageIcon;
052:
053: public class ImageMerger {
054: static public final int JPG = 0;
055: static public final int PNG = 1;
056: static public final int GIF = 2;
057:
058: /**
059: * Merge several images into one. The first file supplied will be put at top.
060: * The destination image has the same size as the first input file.
061: *
062: * @param images
063: * is a List of Images or Strings denoting the full path of the input image
064: * @param outputFile
065: * is a String denoting path+filename of the output image
066: * @param format
067: * is JPG or PNG as defined in the public constants.
068: *
069: */
070: public static void mergeAndSave(List images,
071: List<Float> transparency, String outputFile, int format) {
072: BufferedImage bi = merge(images, transparency);
073: saveImage(bi, outputFile, format);
074: }
075:
076: public static void saveImage(BufferedImage bi, String outputFile,
077: int format) {
078: try {
079: OutputStream os = new FileOutputStream(outputFile);
080: //format=2; // DEBUG
081: switch (format) {
082:
083: case JPG:
084: encodeJPG(os, bi);
085: break;
086:
087: case PNG:
088: encodePNG(os, bi);
089: break;
090:
091: case GIF:
092: try {
093: encodeGIF(os, bi);
094: } catch (IOException e) {
095: if (e.getMessage() == "too many colors for a GIF")
096: System.out
097: .println("too many colors for a GIF, will try to generate JPG");
098: else
099: System.out
100: .println("error in encoding GIF file: "
101: + e.getMessage()
102: + "; will try to generate a JPG");
103:
104: encodeJPG(os, bi);
105: }
106: break;
107:
108: default:
109: try {
110: encodePNG(os, bi);
111: break;
112: } catch (IOException e) {
113: System.out.println("error in encoding PNG file: "
114: + e.getMessage()
115: + "; will try to generate a JPG");
116: encodeJPG(os, bi);
117: }
118: }
119: os.close();
120: } catch (Exception e) {
121: e.printStackTrace();
122: }
123: }
124:
125: /** UNUSED */
126: // public static byte[] mergeB(Vector inputFileNames, Vector transparency) {
127: // BufferedImage bi = merge(inputFileNames, transparency);
128: //
129: // try {
130: // ByteArrayOutputStream bos = new ByteArrayOutputStream();
131: // encodeGIF(bos, bi);
132: // // encodePNG(bos, bi);
133: // // encodeJPG(bos, bi);
134: // byte[] out = bos.toByteArray();
135: // bos.close();
136: // return out;
137: // } catch (Exception e) {
138: // e.printStackTrace();
139: // return null;
140: // }
141: // }
142: /**
143: * Encodes the BufferedImage <I>bi</I> into the stream <I>os</I> as a JPG
144: *
145: * --- JAI implementation
146: */
147: /*
148: * public static void encodeJPG(OutputStream os, BufferedImage bi) throws
149: * IOException { // We need to eliminate the alpha channel in order to save
150: * the image as a JPEG file. if(bi.getColorModel().hasAlpha()) bi =
151: * dealpha(bi);
152: *
153: * JPEGImageEncoder encoder =
154: * (JPEGImageEncoder)JPEGCodec.createImageEncoder("jpeg", os, null);
155: * JPEGEncodeParam param = encoder.getParam(); float quality = 0.60f;
156: * param.setQuality(quality,false); encoder.setParam(param);
157: * encoder.encode(bi); }
158: */
159:
160: public static synchronized void encodeJPG(OutputStream os,
161: BufferedImage bi) throws Exception {
162: com.sun.image.codec.jpeg.JPEGEncodeParam encodeParam = null;
163:
164: // We need to eliminate the alpha channel in order to save the image as a
165: // JPEG file.
166: if (bi.getColorModel().hasAlpha())
167: bi = dealpha(bi);
168:
169: // encode JPEG
170: JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(os);
171: com.sun.image.codec.jpeg.JPEGEncodeParam jpgParams = encoder
172: .getDefaultJPEGEncodeParam(bi);
173: jpgParams.setQuality(0.65f, false);
174: encoder.setJPEGEncodeParam(jpgParams);
175: encoder.encode(bi);
176: }
177:
178: public static synchronized void encodeJPG(OutputStream os,
179: BufferedImage bi, float quality) throws Exception {
180: com.sun.image.codec.jpeg.JPEGEncodeParam encodeParam = null;
181:
182: // We need to eliminate the alpha channel in order to save the image as a
183: // JPEG file.
184: if (bi.getColorModel().hasAlpha())
185: bi = dealpha(bi);
186:
187: // encode JPEG
188: JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(os);
189: com.sun.image.codec.jpeg.JPEGEncodeParam jpgParams = encoder
190: .getDefaultJPEGEncodeParam(bi);
191: jpgParams.setQuality(quality, false);
192: encoder.setJPEGEncodeParam(jpgParams);
193:
194: encoder.encode(bi);
195: }
196:
197: /**
198: * Encodes the BufferedImage <I>bi</I> into the stram <I>os</I> --- JAI
199: * implementation
200: */
201: public static synchronized void encodePNG(OutputStream os,
202: BufferedImage bi) throws IOException {
203:
204: PNGEncodeParam pngParams = PNGEncodeParam
205: .getDefaultEncodeParam(bi);
206: // PNGEncodeParam pngParams = new PNGEncodeParam.RGB();
207: pngParams.setBitDepth(8);
208: PNGImageEncoder encoder = (PNGImageEncoder) PNGCodec
209: .createImageEncoder("png", os, pngParams);
210: encoder.setParam(pngParams);
211:
212: // TODO make this into a user setting to allow to select for high or low
213: // quality images
214: // user sets number of colors to be used
215:
216: // PNGEncodeParam.RGB pngParamsRGB = (PNGEncodeParam.RGB)pngParams;
217: // pngParamsRGB.setBitDepth(8);
218: // PNGImageEncoder encoder =
219: // (PNGImageEncoder)PNGCodec.createImageEncoder("png", os, pngParamsRGB);
220: // encoder.setParam(pngParamsRGB);
221:
222: encoder.encode(bi);
223: os.close();
224: }
225:
226: public static synchronized void encodeGIF(OutputStream os,
227: BufferedImage bi) throws IOException {
228:
229: GifEncoder encoder = new GifEncoder(bi, os);
230: encoder.encode();
231: os.close();
232: }
233:
234: /**
235: * Merges the image listed in <I>images</I>, each with the related <I>transparency</I>.
236: * <I>images</I> items can be Images or String (representing the file path of the image).
237: * Images and Strings can also be mixed in the List.
238: * the higher in the image stack it will be printed.
239: */
240: public static BufferedImage merge(List images,
241: List<Float> transparency) {
242: BufferedImage dest = null;
243: Graphics2D destG = null;
244: int rule; // This is SRC for the top image, and DST_OVER for the other ones
245: float alpha;
246: // This is 1.0 for the bottom image, and 0.9 for the other ones
247:
248: for (int i = 0, size = images.size(); i < size; i++) {
249: Object o = images.get(i);
250: Image image;
251: if (o instanceof String) {
252: String filename = (String) o;
253: image = new ImageIcon(filename).getImage();
254: } else if (o instanceof Image) {
255: image = (Image) o;
256: } else
257: throw new IllegalArgumentException(o
258: + " is not an image");
259:
260: rule = AlphaComposite.SRC_OVER; // Default value
261:
262: // Set alpha
263: alpha = transparency.get(i).floatValue();
264: // alpha = 0.9F; // Light transparence effect
265: // alpha = 1F; // Solid colors
266:
267: if (i == 0) {
268: //- init
269: dest = new BufferedImage(image.getWidth(null), image
270: .getHeight(null), BufferedImage.TYPE_INT_ARGB);
271: destG = dest.createGraphics();
272:
273: //- values for top image
274: rule = AlphaComposite.SRC; // Rule for 1st image
275: }
276:
277: // if (i == size - 1)
278: // {
279: // //- value for bottom image
280: // alpha = 1F;
281: // }
282:
283: // System.out.println("i: " + i + "; alpha: " + alpha); // DEBUG
284: destG.setComposite(AlphaComposite.getInstance(rule, alpha));
285: destG.drawImage(image, 0, 0, null);
286: }
287:
288: return dest;
289: }
290:
291: public static BufferedImage merge(String base, String over, int x,
292: int y) {
293: Image ibase = new ImageIcon(base).getImage();
294: Image iover = new ImageIcon(over).getImage();
295: return merge(ibase, iover, x, y);
296: }
297:
298: public static BufferedImage merge(String base, Image over, int x,
299: int y) {
300: return merge(base, over, x, y, 1.0f);
301: }
302:
303: public static BufferedImage merge(String base, Image over, int x,
304: int y, float alpha) {
305: Image ibase = new ImageIcon(base).getImage();
306: return merge(ibase, over, x, y, alpha);
307: }
308:
309: public static BufferedImage merge(Image base, Image over, int x,
310: int y) {
311: return merge(base, over, x, y, 1.0f);
312: }
313:
314: public static BufferedImage merge(Image base, Image over, int x,
315: int y, float alpha) {
316: int bw = base.getWidth(null);
317: int bh = base.getHeight(null);
318: int ow = over.getWidth(null);
319: int oh = over.getHeight(null);
320:
321: BufferedImage dest = new BufferedImage(bw, bh,
322: BufferedImage.TYPE_INT_ARGB);
323: Graphics2D destG = dest.createGraphics();
324:
325: destG.setComposite(AlphaComposite.getInstance(
326: AlphaComposite.SRC, alpha));
327: destG.drawImage(base, 0, 0, null);
328:
329: System.out.println("Compositing images (" + ow + "," + oh
330: + ") over (" + bw + "," + bh + ") @" + x + "+" + y);
331:
332: // negative position starts from lower right corner, with -1 being aligned to the border
333: if (x < 0)
334: x = bw - ow + x + 1;
335:
336: if (y < 0)
337: y = bh - oh + y + 1;
338:
339: System.out.println(" @" + x + "+" + y);
340: destG.setComposite(AlphaComposite.getInstance(
341: AlphaComposite.SRC_OVER, alpha));
342: destG.drawImage(over, x, y, null);
343:
344: return dest;
345: }
346:
347: /**
348: * You cannot convert to JPEG an image with an alpha channel.
349: */
350: public static BufferedImage dealpha(BufferedImage bi) {
351: return dealpha(bi, Color.white);
352: }
353:
354: /**
355: * You cannot convert to JPEG an image with an alpha channel.
356: */
357: public static BufferedImage dealpha(BufferedImage bi,
358: Color background) {
359: BufferedImage ret = new BufferedImage(bi.getWidth(), bi
360: .getHeight(), BufferedImage.TYPE_INT_RGB);
361: Graphics2D retG = ret.createGraphics();
362: retG.setColor(background);
363: retG.fillRect(0, 0, bi.getWidth(), bi.getHeight());
364: retG.drawImage(bi, 0, 0, null);
365: return ret;
366: }
367:
368: /** Just a test... */
369: private static void printCodecs() {
370: Enumeration e = ImageCodec.getCodecs();
371: while (e.hasMoreElements()) {
372: ImageCodec ic = (ImageCodec) e.nextElement();
373: System.out.println(ic.getFormatName());
374: }
375: }
376:
377: public static void main(String args[]) {
378: if (args.length < 2) {
379: System.out
380: .println("Usage: ImageMerger destFile srcFile1 ...\n");
381: System.exit(1);
382: }
383:
384: Vector inFiles = new Vector(args.length - 1);
385: for (int i = 1; i < args.length; i++)
386: inFiles.add(args[i]);
387:
388: mergeAndSave(inFiles, null, args[0], GIF);
389: System.exit(0);
390: }
391: }
392:
393: //===
394: //=== Consider also the following hints to save image file in JVM 1.4
395: //===
396:
397: // ImageWriteParam iwparam = new MyImageWriteParam();
398: // iwparam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT) ;
399: // iwparam.setCompressionQuality(compressionQuality);
400: // try
401: // {
402: // File file = new File(outputFile);
403: // // formatName = "jpeg" | "png" | ...
404: // // bi is a BufferedImage
405: // ImageIO.write(bi, formatName, file);
406: // }
407: // catch (IOException e)
408: // {
409: // e.printStackTrace();
410: // }
411:
412: //=== Following snippet can be used to save JPG with JVM >= 1.2
413: //=== It can't save in formats other than JPG
414:
415: // /** Encodes the BufferedImage <I>bi</I> into the stream <I>os</I>
416: // *
417: // * --- com.sun...jpg package ---
418: // */
419: // public static void encodeJPG(OutputStream os, BufferedImage bi)
420: // throws IOException
421: // {
422: // JPEGImageEncoder encoder =
423: // (JPEGImageEncoder)JPEGCodec.createJPEGEncoder(os);
424: // JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(bi);
425: // float quality = 0.80f;
426: // param.setQuality(quality, false);
427: // encoder.setJPEGEncodeParam(param);
428: // encoder.encode(bi);
429: // }
|