1
2 package org.freehep.graphicsio;
3
4 import java.awt.Color;
5 import java.awt.Component;
6 import java.awt.Dimension;
7 import java.awt.Graphics;
8 import java.awt.GraphicsConfiguration;
9 import java.awt.Image;
10 import java.awt.RenderingHints;
11 import java.awt.image.BufferedImage;
12 import java.awt.image.RenderedImage;
13 import java.io.File;
14 import java.io.FileNotFoundException;
15 import java.io.FileOutputStream;
16 import java.io.IOException;
17 import java.io.InputStream;
18 import java.io.OutputStream;
19 import java.io.ByteArrayOutputStream;
20 import java.util.Arrays;
21 import java.util.Comparator;
22 import java.util.Enumeration;
23 import java.util.HashMap;
24 import java.util.Iterator;
25 import java.util.Map;
26 import java.util.Properties;
27 import java.util.SortedSet;
28 import java.util.TreeSet;
29
30 import javax.imageio.IIOImage;
31 import javax.imageio.ImageIO;
32 import javax.imageio.ImageReader;
33 import javax.imageio.ImageWriteParam;
34 import javax.imageio.ImageWriter;
35 import javax.imageio.stream.ImageInputStream;
36 import javax.imageio.stream.ImageOutputStream;
37
38 import org.freehep.graphics2d.PixelGraphics2D;
39 import org.freehep.util.UserProperties;
40 import org.freehep.util.io.ASCII85OutputStream;
41 import org.freehep.util.io.FlateOutputStream;
42 import org.freehep.util.images.ImageUtilities;
43 import org.freehep.graphicsio.raw.RawImageWriteParam;
44
45
46
47
48
49
50
51 public class ImageGraphics2D extends PixelGraphics2D {
52
53 private final static String alwaysCompressedFormats[] = {
54 ImageConstants.JPG.toLowerCase(),
55 ImageConstants.JPEG.toLowerCase(),
56 ImageConstants.GIF.toLowerCase()};
57
58 private final static String nonTransparentFormats[] = {
59 ImageConstants.JPG.toLowerCase(),
60 ImageConstants.JPEG.toLowerCase(),
61 ImageConstants.PPM.toLowerCase()};
62
63 public static final String rootKey = "org.freehep.graphicsio";
64
65
66 public static final String TRANSPARENT = "." + PageConstants.TRANSPARENT;
67
68 public static final String BACKGROUND = "." + PageConstants.BACKGROUND;
69
70 public static final String BACKGROUND_COLOR = "."
71 + PageConstants.BACKGROUND_COLOR;
72
73
74 public static final String ANTIALIAS = ".Antialias";
75
76 public static final String ANTIALIAS_TEXT = ".AntialiasText";
77
78
79 public static final String PROGRESSIVE = ".Progressive";
80
81 public static final String COMPRESS = ".Compress";
82
83 public static final String COMPRESS_MODE = ".CompressMode";
84
85 public static final String COMPRESS_DESCRIPTION = ".CompressDescription";
86
87 public static final String COMPRESS_QUALITY = ".CompressQuality";
88
89 private static final Map
90
91 public static Properties getDefaultProperties(String format) {
92 UserProperties properties = (UserProperties) defaultProperties
93 .get(format);
94 if (properties == null) {
95 properties = new UserProperties();
96 defaultProperties.put(format, properties);
97
98 String formatKey = rootKey + "." + format;
99
100
101 if (canWriteTransparent(format)) {
102 properties.setProperty(formatKey + TRANSPARENT, true);
103 properties.setProperty(formatKey + BACKGROUND, false);
104 properties
105 .setProperty(formatKey + BACKGROUND_COLOR, Color.GRAY);
106 } else {
107 properties.setProperty(formatKey + BACKGROUND, false);
108 properties
109 .setProperty(formatKey + BACKGROUND_COLOR, Color.GRAY);
110 }
111
112
113 properties.setProperty(formatKey + ANTIALIAS, true);
114 properties.setProperty(formatKey + ANTIALIAS_TEXT, true);
115
116
117 ImageWriter writer = getPreferredImageWriter(format);
118 if (writer != null) {
119 ImageWriteParam param = writer.getDefaultWriteParam();
120
121
122 if (param.canWriteCompressed()) {
123 param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
124 properties.setProperty(formatKey + COMPRESS, true);
125 String[] compressionTypes = param.getCompressionTypes();
126 String compressionType = param.getCompressionType();
127 properties.setProperty(formatKey + COMPRESS_MODE, compressionType != null ? compressionType : compressionTypes[0]);
128 properties.setProperty(formatKey + COMPRESS_DESCRIPTION,
129 "Custom");
130 float compressionQuality = 0.0f;
131 try {
132 compressionQuality = param.getCompressionQuality();
133 } catch (IllegalStateException e) {
134
135 }
136 properties.setProperty(formatKey + COMPRESS_QUALITY, compressionQuality);
137 } else {
138 properties.setProperty(formatKey + COMPRESS, false);
139 properties.setProperty(formatKey + COMPRESS_MODE, "");
140 properties.setProperty(formatKey + COMPRESS_DESCRIPTION,
141 "Custom");
142 properties.setProperty(formatKey + COMPRESS_QUALITY, 0.0f);
143 }
144
145
146 if (param.canWriteProgressive()) {
147 properties
148 .setProperty(
149 formatKey + PROGRESSIVE,
150 param.getProgressiveMode() != ImageWriteParam.MODE_DISABLED);
151 } else {
152 properties.setProperty(formatKey + PROGRESSIVE, false);
153 }
154 } else {
155 System.err.println(ImageGraphics2D.class
156 + ": No writer for format '" + format + "'.");
157 }
158 }
159 return properties;
160 }
161
162 public void setProperties(Properties newProperties) {
163 if (newProperties == null)
164 return;
165
166 String formatKey = rootKey + "." + format;
167 Properties formatProperties = new Properties();
168 for (Enumeration e = newProperties.propertyNames(); e.hasMoreElements();) {
169 String key = (String) e.nextElement();
170 String value = newProperties.getProperty(key);
171 if (key.indexOf("." + format) < 0) {
172 key = formatKey + key;
173 }
174 formatProperties.setProperty(key, value);
175 }
176 super.setProperties(formatProperties);
177
178 setPropertiesOnGraphics();
179 }
180
181 private void setPropertiesOnGraphics() {
182 String formatKey = rootKey + "." + format;
183 if (isProperty(formatKey + ANTIALIAS)) {
184 setRenderingHint(RenderingHints.KEY_ANTIALIASING,
185 RenderingHints.VALUE_ANTIALIAS_ON);
186 } else {
187 setRenderingHint(RenderingHints.KEY_ANTIALIASING,
188 RenderingHints.VALUE_ANTIALIAS_OFF);
189 }
190
191 if (isProperty(formatKey + ANTIALIAS_TEXT)) {
192 setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
193 RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
194 } else {
195 setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
196 RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
197 }
198
199 if (isProperty(formatKey + TRANSPARENT)) {
200 setBackground(null);
201 } else if (isProperty(formatKey + BACKGROUND)) {
202 setBackground(getPropertyColor(formatKey + BACKGROUND_COLOR));
203 } else {
204 setBackground(component != null ? component.getBackground()
205 : Color.WHITE);
206 }
207 }
208
209 private void setHintsOnGraphics() {
210 if (format.equalsIgnoreCase(ImageConstants.JPG)) {
211
212
213 setRenderingHint(KEY_SYMBOL_BLIT, VALUE_SYMBOL_BLIT_OFF);
214 } else {
215 setRenderingHint(KEY_SYMBOL_BLIT, VALUE_SYMBOL_BLIT_ON);
216 }
217
218 }
219
220 protected OutputStream os;
221
222 protected BufferedImage image;
223
224 protected String format;
225
226 protected Component component;
227
228 public ImageGraphics2D(File file, Dimension size, String format)
229 throws FileNotFoundException {
230 this(new FileOutputStream(file), size, format);
231 }
232
233 public ImageGraphics2D(File file, Component component, String format)
234 throws FileNotFoundException {
235 this(new FileOutputStream(file), component, format);
236 }
237
238 public ImageGraphics2D(OutputStream os, Dimension size, String format) {
239 super();
240 init(os, size, format);
241 component = null;
242 }
243
244 public ImageGraphics2D(OutputStream os, Component component, String format) {
245 super();
246 this.component = component;
247 init(os, component.getSize(), format);
248
249 setColor(component.getForeground());
250 GraphicsConfiguration gc = component.getGraphicsConfiguration();
251 if (gc != null)
252 setTransform(gc.getDefaultTransform());
253 }
254
255 private void init(OutputStream os, Dimension size, String format) {
256 this.os = os;
257 this.format = format;
258
259 initProperties(getDefaultProperties(format));
260
261
262 image = createBufferedImage(format, size.width, size.height);
263 setHostGraphics(image.getGraphics());
264
265
266 setPropertiesOnGraphics();
267
268
269 setHintsOnGraphics();
270
271
272
273
274 hostGraphics.clipRect(0, 0, size.width, size.height);
275 }
276
277 protected ImageGraphics2D(ImageGraphics2D graphics) {
278 super(graphics);
279 image = graphics.image;
280 os = graphics.os;
281 format = graphics.format;
282
283
284 setHintsOnGraphics();
285 }
286
287 public Graphics create() {
288 return new ImageGraphics2D(this);
289 }
290
291 public Graphics create(double x, double y, double width, double height) {
292 ImageGraphics2D imageGraphics = new ImageGraphics2D(this);
293 imageGraphics.translate(x, y);
294 imageGraphics.clipRect(0, 0, width, height);
295 return imageGraphics;
296 }
297
298 public void startExport() {
299 if (getBackground() != null) {
300 clearRect(0.0, 0.0, image.getWidth(), image.getHeight());
301 }
302 }
303
304 public void endExport() {
305 try {
306 write();
307 closeStream();
308 } catch (IOException e) {
309 handleException(e);
310 }
311 }
312
313 protected void write() throws IOException {
314 writeImage((RenderedImage) image, format, getProperties(), os);
315 }
316
317 public void closeStream() throws IOException {
318 os.close();
319 }
320
321
322
323
324
325
326
327 protected void handleException(Exception exception) {
328 System.err.println(exception);
329 }
330
331
332
333
334
335
336
337
338
339 public static BufferedImage createBufferedImage(
340 String format,
341 int width,
342 int height) {
343
344
345
346
347 if (ImageConstants.WBMP.equalsIgnoreCase(format)) {
348 return new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY);
349 }
350
351
352 if (ImageConstants.JPG.equalsIgnoreCase(format)) {
353 return new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
354 }
355
356
357 if (ImageConstants.BMP.equalsIgnoreCase(format)) {
358 return new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
359 }
360
361 return new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
362 }
363
364 public static BufferedImage generateThumbnail(Component component,
365 Dimension size) {
366 int longSide = Math.max(size.width, size.height);
367 if (longSide < 0)
368 return null;
369
370 int componentWidth = component.getBounds().width;
371 int componentHeight = component.getBounds().height;
372
373 BufferedImage image = new BufferedImage(componentWidth,
374 componentHeight, BufferedImage.TYPE_INT_ARGB);
375 Graphics imageGraphics = image.getGraphics();
376 component.print(imageGraphics);
377
378 int width = longSide;
379 int height = longSide;
380 if (componentWidth < componentHeight) {
381 width = componentWidth * size.height / componentHeight;
382 } else {
383 height = componentHeight * size.width / componentWidth;
384 }
385
386 BufferedImage scaled = new BufferedImage(width, height,
387 BufferedImage.TYPE_INT_ARGB);
388 Graphics scaledGraphics = scaled.getGraphics();
389 scaledGraphics.drawImage(image, 0, 0, width, height, null);
390
391 return scaled;
392 }
393
394 public static void writeImage(Image image, String format,
395 Properties properties, OutputStream os) throws IOException {
396
397 writeImage(
398 ImageUtilities.createRenderedImage(image, null, Color.black),
399 format, properties, os);
400 }
401
402 public static void writeImage(RenderedImage image, String format,
403 Properties properties, OutputStream os) throws IOException {
404
405 ImageWriter writer = getPreferredImageWriter(format);
406 if (writer == null)
407 throw new IOException(ImageGraphics2D.class
408 + ": No writer for format '" + format + "'.");
409
410
411 UserProperties user = new UserProperties(properties);
412 ImageWriteParam param = writer.getDefaultWriteParam();
413 if (param instanceof ImageParamConverter) {
414 param = ((ImageParamConverter) param).getWriteParam(user);
415 }
416
417
418 String formatKey = rootKey + "." + format;
419 if (param.canWriteCompressed()) {
420 if (user.isProperty(formatKey + COMPRESS)) {
421 if (user.getProperty(formatKey + COMPRESS_MODE).equals("")) {
422 param.setCompressionMode(ImageWriteParam.MODE_DEFAULT);
423 } else {
424 param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
425 param.setCompressionType(user.getProperty(formatKey
426 + COMPRESS_MODE));
427 param.setCompressionQuality(user.getPropertyFloat(formatKey
428 + COMPRESS_QUALITY));
429 }
430 } else {
431 if (canWriteUncompressed(format)) {
432 param.setCompressionMode(ImageWriteParam.MODE_DISABLED);
433 }
434 }
435 }
436 if (param.canWriteProgressive()) {
437 if (user.isProperty(formatKey + PROGRESSIVE)) {
438 param.setProgressiveMode(ImageWriteParam.MODE_DEFAULT);
439 } else {
440 param.setProgressiveMode(ImageWriteParam.MODE_DISABLED);
441 }
442 }
443
444
445 ImageOutputStream ios = ImageIO.createImageOutputStream(os);
446 writer.setOutput(ios);
447 writer.write(null, new IIOImage(image, null, null), param);
448 writer.dispose();
449 ios.close();
450 }
451
452 public static ImageWriter getPreferredImageWriter(String format) {
453 return (ImageWriter)getImageWriters(ImageIO
454 .getImageWritersByFormatName(format)).first();
455 }
456
457 public static ImageWriter getPreferredImageWriterForMIMEType(String mimeType) {
458 return (ImageWriter)getImageWriters(ImageIO
459 .getImageWritersByMIMEType(mimeType)).first();
460 }
461
462 public static SortedSet
463
464
465
466 SortedSet imageWriters = new TreeSet(new Comparator() {
467 private int order(Object o) {
468 String className = o.getClass().getName();
469 if (className.startsWith("org.freehep.")) {
470 return 0;
471 } else if (className.startsWith("com.sun.imageio.")) {
472 return 1;
473 } else if (className.startsWith("com.sun.media.")) {
474 return 2;
475 }
476 return 3;
477 }
478
479 public int compare(Object arg0, Object arg1) {
480 int order0 = order(arg0);
481 int order1 = order(arg1);
482 return order0 < order1 ? -1 : order0 > order1 ? 1 : 0;
483 }
484 });
485 while (iterator.hasNext()) {
486 imageWriters.add((ImageWriter) iterator.next());
487 }
488 return imageWriters;
489 }
490
491 public static BufferedImage readImage(String format, InputStream is)
492 throws IOException {
493 Iterator iterator = ImageIO.getImageReadersByFormatName(format.toLowerCase());
494 if (!iterator.hasNext()) {
495 throw new IOException(ImageGraphics2D.class
496 + ": No reader for format '" + format + "'.");
497 }
498 ImageReader reader = (ImageReader) iterator.next();
499
500 ImageInputStream iis = ImageIO.createImageInputStream(is);
501 reader.setInput(iis, true);
502 BufferedImage image = reader.read(0);
503 reader.dispose();
504 iis.close();
505 return image;
506 }
507
508 public static boolean canWriteUncompressed(String format) {
509
510
511
512
513 return !Arrays.asList(alwaysCompressedFormats).contains(
514 format.toLowerCase());
515 }
516
517 public static boolean canWriteTransparent(String format) {
518 return !Arrays.asList(nonTransparentFormats).contains(
519 format.toLowerCase());
520 }
521
522
523
524
525
526
527 public static UserProperties getRAWProperties(Color bkg, String code) {
528 UserProperties result = new UserProperties();
529 result.setProperty(RawImageWriteParam.BACKGROUND, bkg);
530 result.setProperty(RawImageWriteParam.CODE, code);
531 result.setProperty(RawImageWriteParam.PAD, 1);
532 return result;
533 }
534
535
536
537
538
539
540
541
542
543
544
545 public static byte[] toByteArray(
546 RenderedImage image,
547 String format,
548 String encoding,
549 Properties props) throws IOException {
550
551 ByteArrayOutputStream bos = new ByteArrayOutputStream();
552 OutputStream os = bos;
553
554 if (ImageConstants.ENCODING_ASCII85.equals(encoding)
555 || ImageConstants.ENCODING_FLATE_ASCII85.equals(encoding)) {
556 os = new ASCII85OutputStream(os);
557 }
558
559 if (ImageConstants.ENCODING_FLATE.equals(encoding)
560 || ImageConstants.ENCODING_FLATE_ASCII85.equals(encoding)) {
561 os = new FlateOutputStream(os);
562 }
563
564
565 if (props == null) {
566 props = new Properties();
567 }
568
569
570 ImageGraphics2D.writeImage(image, format.toLowerCase(), props, os);
571 os.close();
572
573
574 return bos.toByteArray();
575 }
576 }