c393c46201b362e299c60a4706852e438878b9fb
1 package be
.nikiroo
.jvcard
.tui
;
4 import java
.awt
.Graphics
;
6 import java
.awt
.image
.BufferedImage
;
7 import java
.awt
.image
.ImageObserver
;
9 import com
.googlecode
.lanterna
.TerminalSize
;
11 public class ImageText
{
13 private TerminalSize size
;
15 private boolean ready
;
17 private boolean invert
;
21 * Use 5 different "colours" which are actually Unicode
22 * {@link Character}s representing
24 * <li>space (blank)</li>
25 * <li>low shade (░)</li>
26 * <li>medium shade (▒)</li>
27 * <li>high shade (▓)</li>
28 * <li>full block (█)</li>
33 * Use "block" Unicode {@link Character}s up to quarter blocks, thus in
34 * effect doubling the resolution both in vertical and horizontal space.
35 * Note that since 2 {@link Character}s next to each other are square,
36 * we will use 4 blocks per 2 blocks for w/h resolution.
40 * Use {@link Character}s from both {@link Mode#DOUBLE_RESOLUTION} and
41 * {@link Mode#DITHERING}.
45 * Only use ASCII {@link Character}s.
50 public ImageText(Image image
, TerminalSize size
, Mode mode
) {
51 setImage(image
, size
);
55 public void setImage(Image image
) {
56 setImage(image
, size
);
59 public void setImage(TerminalSize size
) {
60 setImage(image
, size
);
63 public void setImage(Image image
, TerminalSize size
) {
72 public void setMode(Mode mode
) {
78 public void setColorInvert(boolean invert
) {
84 public boolean getColorInvert() {
88 public String
getText() {
94 if (mode
== Mode
.DOUBLE_RESOLUTION
|| mode
== Mode
.DOUBLE_DITHERING
)
97 int w
= size
.getColumns() * mult
;
98 int h
= size
.getRows() * mult
;
100 BufferedImage buff
= new BufferedImage(w
, h
,
101 BufferedImage
.TYPE_INT_ARGB
);
103 Graphics gfx
= buff
.getGraphics();
105 TerminalSize srcSize
= getSize(image
);
106 srcSize
= new TerminalSize(srcSize
.getColumns() * 2,
111 if (srcSize
.getColumns() > srcSize
.getRows()) {
112 double ratio
= (double) size
.getColumns()
113 / (double) size
.getRows();
114 ratio
*= (double) srcSize
.getRows()
115 / (double) srcSize
.getColumns();
117 h
= (int) Math
.round(ratio
* h
);
118 y
= (buff
.getHeight() - h
) / 2;
120 double ratio
= (double) size
.getRows()
121 / (double) size
.getColumns();
122 ratio
*= (double) srcSize
.getColumns()
123 / (double) srcSize
.getRows();
125 w
= (int) Math
.round(ratio
* w
);
126 x
= (buff
.getWidth() - w
) / 2;
129 if (gfx
.drawImage(image
, x
, y
, w
, h
, new ImageObserver() {
131 public boolean imageUpdate(Image img
, int infoflags
, int x
,
132 int y
, int width
, int height
) {
133 ImageText
.this.ready
= true;
143 } catch (InterruptedException e
) {
149 StringBuilder builder
= new StringBuilder();
151 for (int row
= 0; row
< buff
.getHeight(); row
+= mult
) {
153 builder
.append('\n');
155 for (int col
= 0; col
< buff
.getWidth(); col
+= mult
) {
157 if (mode
== Mode
.DITHERING
)
158 builder
.append(getDitheringChar(buff
.getRGB(col
,
162 builder
.append(getAsciiChar(buff
.getRGB(col
, row
)));
163 } else if (mult
== 2) {
164 builder
.append(getBlockChar( //
165 buff
.getRGB(col
, row
),//
166 buff
.getRGB(col
+ 1, row
),//
167 buff
.getRGB(col
, row
+ 1),//
168 buff
.getRGB(col
+ 1, row
+ 1),//
169 mode
== Mode
.DOUBLE_DITHERING
//
175 text
= builder
.toString();
182 public String
toString() {
186 static private TerminalSize
getSize(Image img
) {
187 TerminalSize size
= null;
188 while (size
== null) {
189 int w
= img
.getWidth(null);
190 int h
= img
.getHeight(null);
191 if (w
> -1 && h
> -1) {
192 size
= new TerminalSize(w
, h
);
196 } catch (InterruptedException e
) {
204 private float[] tmp
= new float[4];
206 private char getAsciiChar(int pixel
) {
207 float brigthness
= getBrightness(pixel
, tmp
);
208 if (brigthness
< 0.20) {
210 } else if (brigthness
< 0.40) {
212 } else if (brigthness
< 0.60) {
214 } else if (brigthness
< 0.80) {
221 private char getDitheringChar(int pixel
) {
222 float brigthness
= getBrightness(pixel
, tmp
);
223 if (brigthness
< 0.20) {
225 } else if (brigthness
< 0.40) {
227 } else if (brigthness
< 0.60) {
229 } else if (brigthness
< 0.80) {
236 private char getBlockChar(int upperleft
, int upperright
, int lowerleft
,
237 int lowerright
, boolean dithering
) {
238 float trigger
= dithering ?
0.20f
: 0.50f
;
240 if (getBrightness(upperleft
, tmp
) > trigger
)
242 if (getBrightness(upperright
, tmp
) > trigger
)
244 if (getBrightness(lowerleft
, tmp
) > trigger
)
246 if (getBrightness(lowerright
, tmp
) > trigger
)
283 avg
+= getBrightness(upperleft
, tmp
);
284 avg
+= getBrightness(upperright
, tmp
);
285 avg
+= getBrightness(lowerleft
, tmp
);
286 avg
+= getBrightness(lowerright
, tmp
);
291 } else if (avg
< 0.40) {
293 } else if (avg
< 0.60) {
295 } else if (avg
< 0.80) {
308 float getBrightness(int argb
, float[] array
) {
310 return 1 - rgb2hsb(argb
, tmp
)[2];
311 return rgb2hsb(argb
, tmp
)[2];
314 // return [h, s, l, a]; h/s/b/a: 0 to 1 (h is given in 1/360th)
315 // like RGBtoHSB, array can be null or used
316 static float[] rgb2hsb(int argb
, float[] array
) {
318 a
= ((argb
& 0xff000000) >> 24);
319 r
= ((argb
& 0x00ff0000) >> 16);
320 g
= ((argb
& 0x0000ff00) >> 8);
321 b
= ((argb
& 0x000000ff));
324 array
= new float[4];
325 Color
.RGBtoHSB(r
, g
, b
, array
);
331 // // other implementation:
334 // a = ((argb & 0xff000000) >> 24) / 255.0f;
335 // r = ((argb & 0x00ff0000) >> 16) / 255.0f;
336 // g = ((argb & 0x0000ff00) >> 8) / 255.0f;
337 // b = ((argb & 0x000000ff)) / 255.0f;
339 // float rgbMin, rgbMax;
340 // rgbMin = Math.min(r, Math.min(g, b));
341 // rgbMax = Math.max(r, Math.max(g, b));
344 // l = (rgbMin + rgbMax) / 2;
347 // if (rgbMin == rgbMax) {
351 // s = (rgbMax - rgbMin) / (rgbMax + rgbMin);
353 // s = (rgbMax - rgbMin) / (2.0f - rgbMax - rgbMin);
358 // if (r > g && r > b) {
359 // h = (g - b) / (rgbMax - rgbMin);
360 // } else if (g > b) {
361 // h = 2.0f + (b - r) / (rgbMax - rgbMin);
363 // h = 4.0f + (r - g) / (rgbMax - rgbMin);
365 // h /= 6; // from 0 to 1
367 // return new float[] { h, s, l, a };
369 // // // natural mode:
371 // // int aa = (int) Math.round(100 * a);
372 // // int hh = (int) (360 * h);
375 // // int ss = (int) Math.round(100 * s);
376 // // int ll = (int) Math.round(100 * l);
378 // // return new int[] { hh, ss, ll, aa };