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