reformat
[fanfix.git] / src / be / nikiroo / fanfix_swing / gui / book / BookCoverImager.java
1 package be.nikiroo.fanfix_swing.gui.book;
2
3 import java.awt.Color;
4 import java.awt.Graphics;
5 import java.awt.Graphics2D;
6 import java.awt.Polygon;
7 import java.awt.Rectangle;
8 import java.awt.image.BufferedImage;
9 import java.io.ByteArrayInputStream;
10 import java.io.ByteArrayOutputStream;
11 import java.io.IOException;
12 import java.io.InputStream;
13 import java.net.MalformedURLException;
14
15 import javax.imageio.ImageIO;
16
17 import be.nikiroo.fanfix.Instance;
18 import be.nikiroo.fanfix.library.BasicLibrary;
19 import be.nikiroo.fanfix.reader.ui.GuiReaderBookInfo;
20 import be.nikiroo.utils.Image;
21 import be.nikiroo.utils.ui.ImageUtilsAwt;
22 import be.nikiroo.utils.ui.UIUtils;
23
24 /**
25 * This class can create a cover icon ready to use for the graphical
26 * application.
27 *
28 * @author niki
29 */
30 class BookCoverImager {
31 // TODO: export some of the configuration options?
32 static final int COVER_WIDTH = 100;
33 static final int COVER_HEIGHT = 150;
34 static final int SPINE_WIDTH = 5;
35 static final int SPINE_HEIGHT = 5;
36 static final int HOFFSET = 20;
37 static final Color SPINE_COLOR_BOTTOM = new Color(180, 180, 180);
38 static final Color SPINE_COLOR_RIGHT = new Color(100, 100, 100);
39 static final Color BORDER = Color.black;
40
41 public static final Color UNCACHED_ICON_COLOR = Color.green.darker();
42 // new Color(0, 80, 220);
43
44 public static final int TEXT_HEIGHT = 50;
45 public static final int TEXT_WIDTH = COVER_WIDTH + 40;
46
47 //
48
49 static public Color getBackground(boolean enabled, boolean selected,
50 boolean hovered) {
51 Color color = new Color(255, 255, 255, 0);
52 if (!enabled) {
53 } else if (selected && !hovered) {
54 color = new Color(80, 80, 100, 40);
55 } else if (!selected && hovered) {
56 color = new Color(230, 230, 255, 100);
57 } else if (selected && hovered) {
58 color = new Color(200, 200, 255, 100);
59 }
60
61 return color;
62 }
63
64 /**
65 * Draw a partially transparent overlay if needed depending upon the
66 * selection and mouse-hover states on top of the normal component, as well
67 * as a possible "cached" icon if the item is cached.
68 *
69 * @param g
70 * the {@link Graphics} to paint onto
71 * @param enabled
72 * draw an enabled overlay
73 * @param selected
74 * draw a selected overlay
75 * @param hovered
76 * draw a hovered overlay
77 * @param cached
78 * draw a non-cached overlay if needed
79 */
80 static public void paintOverlay(Graphics g, boolean enabled,
81 boolean selected, boolean hovered, boolean cached) {
82 Rectangle clip = g.getClipBounds();
83 if (clip.getWidth() <= 0 || clip.getHeight() <= 0) {
84 return;
85 }
86
87 int h = COVER_HEIGHT;
88 int w = COVER_WIDTH;
89 int xOffset = (TEXT_WIDTH - COVER_WIDTH) - 1;
90 int yOffset = HOFFSET;
91
92 if (BORDER != null) {
93 if (BORDER != null) {
94 g.setColor(BORDER);
95 g.drawRect(xOffset, yOffset, COVER_WIDTH, COVER_HEIGHT);
96 }
97
98 xOffset++;
99 yOffset++;
100 }
101
102 int[] xs = new int[] { xOffset, xOffset + SPINE_WIDTH,
103 xOffset + w + SPINE_WIDTH, xOffset + w };
104 int[] ys = new int[] { yOffset + h, yOffset + h + SPINE_HEIGHT,
105 yOffset + h + SPINE_HEIGHT, yOffset + h };
106 g.setColor(SPINE_COLOR_BOTTOM);
107 g.fillPolygon(new Polygon(xs, ys, xs.length));
108 xs = new int[] { xOffset + w, xOffset + w + SPINE_WIDTH,
109 xOffset + w + SPINE_WIDTH, xOffset + w };
110 ys = new int[] { yOffset, yOffset + SPINE_HEIGHT,
111 yOffset + h + SPINE_HEIGHT, yOffset + h };
112 g.setColor(SPINE_COLOR_RIGHT);
113 g.fillPolygon(new Polygon(xs, ys, xs.length));
114
115 Color color = getBackground(enabled, selected, hovered);
116
117 g.setColor(color);
118 g.fillRect(clip.x, clip.y, clip.width, clip.height);
119
120 UIUtils.drawEllipse3D(g, UNCACHED_ICON_COLOR,
121 COVER_WIDTH + HOFFSET + 30, 10, 20, 20, cached);
122 }
123
124 /**
125 * The width of a cover image.
126 *
127 * @return the width
128 */
129 static public int getCoverWidth() {
130 return SPINE_WIDTH + COVER_WIDTH;
131 }
132
133 /**
134 * The height of a cover image.
135 *
136 * @return the height
137 */
138 static public int getCoverHeight() {
139 return COVER_HEIGHT + HOFFSET;
140 }
141
142 /**
143 * Generate a cover icon based upon the given {@link GuiReaderBookInfo}.
144 *
145 * @param lib
146 * the library the meta comes from (can be NULL)
147 * @param info
148 * the {@link GuiReaderBookInfo}
149 *
150 * @return the image
151 */
152 static public java.awt.Image generateCoverImage(BasicLibrary lib,
153 BookInfo info) {
154 BufferedImage resizedImage = null;
155 String id = getIconId(info);
156
157 InputStream in = Instance.getInstance().getCache().getFromCache(id);
158 if (in != null) {
159 try {
160 resizedImage = ImageUtilsAwt.fromImage(new Image(in));
161 in.close();
162 in = null;
163 } catch (IOException e) {
164 Instance.getInstance().getTraceHandler().error(e);
165 }
166 }
167
168 if (resizedImage == null) {
169 try {
170 Image cover = null;
171 if (info != null) {
172 cover = info.getBaseImage(lib);
173 }
174
175 resizedImage = new BufferedImage(getCoverWidth(),
176 getCoverHeight(), BufferedImage.TYPE_4BYTE_ABGR);
177
178 Graphics2D g = resizedImage.createGraphics();
179 try {
180 if (info != null && info.supportsCover()) {
181 g.setColor(Color.white);
182 g.fillRect(0, HOFFSET, COVER_WIDTH, COVER_HEIGHT);
183
184 if (cover != null) {
185 BufferedImage coverb = ImageUtilsAwt
186 .fromImage(cover);
187 g.drawImage(coverb, 0, HOFFSET, COVER_WIDTH,
188 COVER_HEIGHT, null);
189 } else {
190 g.setColor(Color.black);
191 g.drawLine(0, HOFFSET, COVER_WIDTH,
192 HOFFSET + COVER_HEIGHT);
193 g.drawLine(COVER_WIDTH, HOFFSET, 0,
194 HOFFSET + COVER_HEIGHT);
195 }
196 }
197 } finally {
198 g.dispose();
199 }
200
201 // Only save image with a cover, not the X thing
202 if (id != null && cover != null) {
203 ByteArrayOutputStream out = new ByteArrayOutputStream();
204 ImageIO.write(resizedImage, "png", out);
205 byte[] imageBytes = out.toByteArray();
206 in = new ByteArrayInputStream(imageBytes);
207 Instance.getInstance().getCache().addToCache(in, id);
208 in.close();
209 in = null;
210 }
211 } catch (MalformedURLException e) {
212 Instance.getInstance().getTraceHandler().error(e);
213 } catch (IOException e) {
214 Instance.getInstance().getTraceHandler().error(e);
215 }
216 }
217
218 return resizedImage;
219 }
220
221 /**
222 * Manually clear the icon set for this item.
223 *
224 * @param info
225 * the info about the story or source/type or author
226 */
227 static public void clearIcon(BookInfo info) {
228 String id = getIconId(info);
229 Instance.getInstance().getCache().removeFromCache(id);
230 }
231
232 /**
233 * Get a unique ID from this {@link GuiReaderBookInfo} (note that it can be
234 * a story, a fake item for a source/type or a fake item for an author).
235 *
236 * @param info
237 * the info or NULL for a generic (non unique!) ID
238 * @return the unique ID
239 */
240 static private String getIconId(BookInfo info) {
241 return (info == null ? "" : info.getId() + ".") + "book-thumb_"
242 + SPINE_WIDTH + "x" + COVER_WIDTH + "+" + SPINE_HEIGHT + "+"
243 + COVER_HEIGHT + "@" + HOFFSET;
244 }
245 }