Commit | Line | Data |
---|---|---|
f04d8b1c NR |
1 | package be.nikiroo.jvcard.tui; |
2 | ||
3 | import java.awt.Graphics; | |
4 | import java.awt.Image; | |
5 | import java.awt.image.BufferedImage; | |
6 | import java.awt.image.ImageObserver; | |
7 | ||
8 | import com.googlecode.lanterna.TerminalSize; | |
9 | ||
10 | public class ImageText { | |
11 | private Image image; | |
12 | private TerminalSize size; | |
13 | private String text; | |
14 | private boolean ready; | |
15 | ||
16 | public ImageText(Image image, TerminalSize size) { | |
17 | setImage(image, size); | |
18 | } | |
19 | ||
20 | public void setImage(Image image) { | |
21 | setImage(image, size); | |
22 | } | |
23 | ||
24 | public void setImage(TerminalSize size) { | |
25 | setImage(image, size); | |
26 | } | |
27 | ||
28 | public void setImage(Image image, TerminalSize size) { | |
29 | this.text = null; | |
30 | this.ready = false; | |
31 | this.size = size; | |
32 | if (image != null) { | |
33 | this.image = image; | |
34 | } | |
35 | } | |
36 | ||
37 | public String getText() { | |
38 | if (text == null) { | |
39 | if (image == null) | |
40 | return ""; | |
41 | ||
42 | int w = size.getColumns() * 2; | |
43 | int h = size.getRows() * 2; | |
44 | BufferedImage buff = new BufferedImage(w, h, | |
45 | BufferedImage.TYPE_INT_ARGB); | |
46 | Graphics gfx = buff.getGraphics(); | |
47 | ||
48 | TerminalSize srcSize = getSize(image); | |
49 | int x = 0; | |
50 | int y = 0; | |
51 | if (srcSize.getColumns() > srcSize.getRows()) { | |
52 | double ratio = (double) srcSize.getRows() | |
53 | / (double) srcSize.getColumns(); | |
54 | h = (int) Math.round(ratio * h); | |
55 | y = (buff.getHeight() - h) / 2; | |
56 | } else { | |
57 | double ratio = (double) srcSize.getColumns() | |
58 | / (double) srcSize.getRows(); | |
59 | w = (int) Math.round(ratio * w); | |
60 | x = (buff.getWidth() - w) / 2; | |
61 | ||
62 | } | |
63 | ||
64 | if (gfx.drawImage(image, x, y, w, h, new ImageObserver() { | |
65 | @Override | |
66 | public boolean imageUpdate(Image img, int infoflags, int x, | |
67 | int y, int width, int height) { | |
68 | ImageText.this.ready = true; | |
69 | return true; | |
70 | } | |
71 | })) { | |
72 | ready = true; | |
73 | } | |
74 | ||
75 | while (!ready) { | |
76 | try { | |
77 | Thread.sleep(100); | |
78 | } catch (InterruptedException e) { | |
79 | } | |
80 | } | |
81 | ||
82 | gfx.dispose(); | |
83 | ||
84 | int[][] square = new int[2][2]; | |
85 | StringBuilder builder = new StringBuilder(); | |
86 | for (int row = 0; row < buff.getHeight(); row += 2) { | |
87 | if (row > 0) | |
88 | builder.append('\n'); | |
89 | ||
90 | for (int col = 0; col < buff.getWidth(); col += 2) { | |
91 | square[0][0] = buff.getRGB(col, row); | |
92 | square[0][1] = buff.getRGB(col, row + 1); | |
93 | square[1][0] = buff.getRGB(col + 1, row); | |
94 | square[1][1] = buff.getRGB(col + 1, row + 1); | |
95 | builder.append(getChar(square)); | |
96 | } | |
97 | } | |
98 | ||
99 | text = builder.toString(); | |
100 | } | |
101 | ||
102 | return text; | |
103 | } | |
104 | ||
105 | @Override | |
106 | public String toString() { | |
107 | return getText(); | |
108 | } | |
109 | ||
110 | static private TerminalSize getSize(Image img) { | |
111 | TerminalSize size = null; | |
112 | while (size == null) { | |
113 | int w = img.getWidth(null); | |
114 | int h = img.getHeight(null); | |
115 | if (w > -1 && h > -1) { | |
116 | size = new TerminalSize(w, h); | |
117 | } else { | |
118 | try { | |
119 | Thread.sleep(100); | |
120 | } catch (InterruptedException e) { | |
121 | } | |
122 | } | |
123 | } | |
124 | ||
125 | return size; | |
126 | } | |
127 | ||
128 | static private char getChar(int[][] square) { | |
129 | int choice = 0; | |
130 | if (rgb2hsl(square[0][0])[3] > 50) | |
131 | choice += 1; | |
132 | if (rgb2hsl(square[0][1])[3] > 50) | |
133 | choice += 2; | |
134 | if (rgb2hsl(square[1][0])[3] > 50) | |
135 | choice += 4; | |
136 | if (rgb2hsl(square[1][1])[3] > 50) | |
137 | choice += 8; | |
138 | ||
139 | switch (choice) { | |
140 | case 0: | |
141 | return ' '; | |
142 | case 1: | |
143 | return '▘'; | |
144 | case 2: | |
145 | return '▝'; | |
146 | case 3: | |
147 | return '▀'; | |
148 | case 4: | |
149 | return '▖'; | |
150 | case 5: | |
151 | return '▌'; | |
152 | case 6: | |
153 | return '▞'; | |
154 | case 7: | |
155 | return '▛'; | |
156 | case 8: | |
157 | return '▗'; | |
158 | case 9: | |
159 | return '▚'; | |
160 | case 10: | |
161 | return '▐'; | |
162 | case 11: | |
163 | return '▜'; | |
164 | case 12: | |
165 | return '▄'; | |
166 | case 13: | |
167 | return '▙'; | |
168 | case 14: | |
169 | return '▟'; | |
170 | case 15: | |
171 | return '█'; | |
172 | } | |
173 | ||
174 | return ' '; | |
175 | } | |
176 | ||
177 | // return [a, h, s, l]; a/s/l: 0 to 100%, h = 0 to 359° | |
178 | static int[] rgb2hsl(int argb) { | |
179 | double a, r, g, b; | |
180 | a = ((argb & 0xff000000) >> 24) / 255.0; | |
181 | r = ((argb & 0x00ff0000) >> 16) / 255.0; | |
182 | g = ((argb & 0x0000ff00) >> 8) / 255.0; | |
183 | b = ((argb & 0x000000ff)) / 255.0; | |
184 | ||
185 | double rgbMin, rgbMax; | |
186 | rgbMin = Math.min(r, Math.min(g, b)); | |
187 | rgbMax = Math.max(r, Math.max(g, b)); | |
188 | ||
189 | double l; | |
190 | l = (rgbMin + rgbMax) / 2; | |
191 | ||
192 | double s; | |
193 | if (rgbMin == rgbMax) { | |
194 | s = 0; | |
195 | } else { | |
196 | if (l <= 0.5) { | |
197 | s = (rgbMax - rgbMin) / (rgbMax + rgbMin); | |
198 | } else { | |
199 | s = (rgbMax - rgbMin) / (2.0 - rgbMax - rgbMin); | |
200 | } | |
201 | } | |
202 | ||
203 | double h; | |
204 | if (r > g && r > b) { | |
205 | h = (g - b) / (rgbMax - rgbMin); | |
206 | } else if (g > b) { | |
207 | h = 2.0 + (b - r) / (rgbMax - rgbMin); | |
208 | } else { | |
209 | h = 4.0 + (r - g) / (rgbMax - rgbMin); | |
210 | } | |
211 | ||
212 | int aa = (int) Math.round(100 * a); | |
213 | int hh = (int) (60 * h); | |
214 | if (hh < 0) | |
215 | hh += 360; | |
216 | int ss = (int) Math.round(100 * s); | |
217 | int ll = (int) Math.round(100 * l); | |
218 | ||
219 | return new int[] { aa, hh, ss, ll }; | |
220 | } | |
221 | } |