3306b0d1585e400b07c0757322b242df54a1a1e4
[jalview.git] / src / jalview / util / ImageMaker.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.util;
22
23 import java.awt.Graphics;
24 import java.awt.Graphics2D;
25 import java.awt.RenderingHints;
26 import java.awt.image.BufferedImage;
27 import java.io.File;
28 import java.io.FileOutputStream;
29 import java.io.IOException;
30
31 import javax.imageio.ImageIO;
32
33 import org.jfree.graphics2d.svg.SVGGraphics2D;
34 import org.jfree.graphics2d.svg.SVGHints;
35 import org.jibble.epsgraphics.EpsGraphics2D;
36
37 import jalview.bin.Console;
38 import jalview.io.JalviewFileChooser;
39 import jalview.util.imagemaker.BitmapImageSizing;
40
41 public class ImageMaker
42 {
43   public static final String SVG_DESCRIPTION = "Scalable Vector Graphics";
44
45   public static final String SVG_EXTENSION = "svg";
46
47   public static final String EPS_DESCRIPTION = "Encapsulated Postscript";
48
49   public static final String EPS_EXTENSION = "eps";
50
51   public static final String PNG_EXTENSION = "png";
52
53   public static final String PNG_DESCRIPTION = "Portable  network graphics";
54
55   EpsGraphics2D pg;
56
57   Graphics graphics;
58
59   FileOutputStream out;
60
61   BufferedImage bi;
62
63   TYPE type;
64
65   public enum TYPE
66   {
67     EPS("EPS", MessageManager.getString("label.eps_file"), EPS_EXTENSION,
68             EPS_DESCRIPTION),
69     PNG("PNG", MessageManager.getString("label.png_image"), PNG_EXTENSION,
70             PNG_DESCRIPTION),
71     SVG("SVG", "SVG", SVG_EXTENSION, SVG_DESCRIPTION);
72
73     public final String name;
74
75     public final String label;
76
77     public final String extension;
78
79     public final String description;
80
81     TYPE(String name, String label, String ext, String desc)
82     {
83       this.name = name;
84       this.label = label;
85       this.extension = ext;
86       this.description = desc;
87     }
88
89     public String getName()
90     {
91       return name;
92     }
93
94     public JalviewFileChooser getFileChooser()
95     {
96       return new JalviewFileChooser(extension, description);
97     }
98
99     public String getLabel()
100     {
101       return label;
102     }
103
104   }
105
106   /**
107    * Constructor configures the graphics context ready for writing to
108    * 
109    * @param imageType
110    * @param width
111    * @param height
112    * @param file
113    * @param fileTitle
114    * @param useLineart
115    * @param bitmapscale
116    * @throws IOException
117    */
118   public ImageMaker(TYPE imageType, int width, int height, File file,
119           String fileTitle, boolean useLineart, BitmapImageSizing userBis)
120           throws IOException
121   {
122     this.type = imageType;
123
124     out = new FileOutputStream(file);
125     switch (imageType)
126     {
127     case SVG:
128       setupSVG(width, height, useLineart);
129       break;
130     case EPS:
131       setupEPS(width, height, fileTitle, useLineart);
132       break;
133     case PNG:
134       setupPNG(width, height, userBis);
135       break;
136     default:
137     }
138   }
139
140   public Graphics getGraphics()
141   {
142     return graphics;
143   }
144
145   /**
146    * For SVG or PNG, writes the generated graphics data to the file output
147    * stream. For EPS, flushes the output graphics (which is written to file as
148    * it is generated).
149    */
150   public void writeImage()
151   {
152     try
153     {
154       switch (type)
155       {
156       case EPS:
157         pg.flush();
158         pg.close();
159         break;
160       case SVG:
161         String svgData = ((SVGGraphics2D) getGraphics()).getSVGDocument();
162         out.write(svgData.getBytes());
163         out.flush();
164         out.close();
165         break;
166       case PNG:
167         ImageIO.write(bi, PNG_EXTENSION, out);
168         out.flush();
169         out.close();
170         break;
171       }
172     } catch (Exception ex)
173     {
174       ex.printStackTrace();
175     }
176   }
177
178   /**
179    * Sets up a graphics object for the PNG image to be written on
180    * 
181    * @param width
182    * @param height
183    * @param scale
184    */
185   protected void setupPNG(int width, int height, BitmapImageSizing userBis)
186   {
187     if (width == 0 || height == 0)
188       return;
189
190     BitmapImageSizing bis = ImageMaker.getScaleWidthHeight(width, height,
191             userBis);
192     float usescale = bis.scale;
193     int usewidth = bis.width;
194     int useheight = bis.height;
195
196     bi = new BufferedImage(usewidth, useheight, BufferedImage.TYPE_INT_RGB);
197     graphics = bi.getGraphics();
198     Graphics2D ig2 = (Graphics2D) graphics;
199     ig2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
200             RenderingHints.VALUE_ANTIALIAS_ON);
201     if (usescale > 0.0f)
202     {
203       ig2.scale(usescale, usescale);
204     }
205   }
206
207   /**
208    * A helper method to configure the SVG output graphics, with choice of Text
209    * or Lineart character rendering
210    * 
211    * @param width
212    * @param height
213    * @param useLineart
214    *          true for Lineart character rendering, false for Text
215    */
216   protected void setupSVG(int width, int height, boolean useLineart)
217   {
218     SVGGraphics2D g2 = new SVGGraphics2D(width, height);
219     if (useLineart)
220     {
221       g2.setRenderingHint(SVGHints.KEY_DRAW_STRING_TYPE,
222               SVGHints.VALUE_DRAW_STRING_TYPE_VECTOR);
223     }
224     graphics = g2;
225   }
226
227   /**
228    * A helper method that sets up the EPS graphics output with user choice of
229    * Text or Lineart character rendering
230    * 
231    * @param width
232    * @param height
233    * @param title
234    * @param useLineart
235    *          true for Lineart character rendering, false for Text
236    * @throws IOException
237    */
238   protected void setupEPS(int width, int height, String title,
239           boolean useLineart) throws IOException
240   {
241     pg = new EpsGraphics2D(title, out, 0, 0, width, height);
242     Graphics2D ig2 = pg;
243     ig2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
244             RenderingHints.VALUE_ANTIALIAS_ON);
245     pg.setAccurateTextMode(useLineart);
246     graphics = pg;
247   }
248
249   /**
250    * Takes suggested float scale, int width, int height and create a bounding
251    * box returned as a BitmapImageSizing object with consistent scale, width,
252    * height fields.
253    * 
254    * @param scale
255    * @param bitmapwidth
256    * @param bitmapheight
257    * @return BitmapImageSizing
258    */
259   public static BitmapImageSizing getScaleWidthHeight(int width, int height,
260           float scale, int bitmapwidth, int bitmapheight)
261   {
262     float usescale = 0.0f;
263     int usewidth = width;
264     int useheight = height;
265
266     // use the smallest positive scale (i.e. fit in the box)
267     if (scale > 0.0f)
268     {
269       usescale = scale;
270       usewidth = Math.round(scale * width);
271       useheight = Math.round(scale * height);
272     }
273     if (bitmapwidth > 0)
274     {
275       float wscale = (float) bitmapwidth / width;
276       if (wscale > 0.0f && (usescale == 0.0f || wscale < usescale))
277       {
278         usescale = wscale;
279         usewidth = bitmapwidth;
280         useheight = Math.round(usescale * height);
281       }
282     }
283     if (bitmapheight > 0)
284     {
285       float hscale = (float) bitmapheight / height;
286       if (hscale > 0.0f && (usescale == 0.0f || hscale < usescale))
287       {
288         usescale = hscale;
289         usewidth = Math.round(usescale * width);
290         useheight = bitmapheight;
291       }
292     }
293     return new BitmapImageSizing(usescale, usewidth, useheight);
294   }
295
296   /**
297    * Takes suggested scale, width, height as a BitmapImageSizing object and
298    * create a bounding box returned as a BitmapImageSizing object with
299    * consistent scale, width, height fields.
300    * 
301    * @param bis
302    * @return BitmapImageSizing
303    */
304   public static BitmapImageSizing getScaleWidthHeight(int width, int height,
305           BitmapImageSizing bis)
306   {
307     return ImageMaker.getScaleWidthHeight(width, height, bis.scale,
308             bis.width, bis.height);
309   }
310
311   /**
312    * Takes String versions of suggested float scale, int width, int height and
313    * create a bounding box returned as a BitmapImageSizing object with
314    * consistent scale, width, height fields.
315    * 
316    * @param scaleS
317    * @param widthS
318    * @param heightS
319    * @return BitmapImageSizing
320    */
321   public static BitmapImageSizing parseScaleWidthHeightStrings(
322           String scaleS, String widthS, String heightS)
323   {
324     float scale = 0.0f;
325     int width = 0;
326     int height = 0;
327
328     if (scaleS != null)
329     {
330       try
331       {
332         scale = Float.parseFloat(scaleS);
333       } catch (NumberFormatException e)
334       {
335         Console.warn("Did not understand scale '" + scaleS
336                 + "', won't be used.");
337       }
338     }
339     if (widthS != null)
340     {
341       try
342       {
343         width = Integer.parseInt(widthS);
344       } catch (NumberFormatException e)
345       {
346         Console.warn("Did not understand width '" + widthS
347                 + "', won't be used.");
348       }
349     }
350     if (heightS != null)
351     {
352       try
353       {
354         height = Integer.parseInt(heightS);
355       } catch (NumberFormatException e)
356       {
357         Console.warn("Did not understand height '" + heightS
358                 + "', won't be used.");
359       }
360     }
361
362     return new BitmapImageSizing(scale, width, height);
363   }
364 }