X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fbe%2Fnikiroo%2Fjvcard%2Ftui%2FImageText.java;h=5945b8272898875bacd7a5390c6eec5de29c16b8;hb=a73a906356c971b080c36368e71a15d87e8b8d31;hp=c393c46201b362e299c60a4706852e438878b9fb;hpb=2523246382a333ddeed4df676f544d303c15706e;p=jvcard.git diff --git a/src/be/nikiroo/jvcard/tui/ImageText.java b/src/be/nikiroo/jvcard/tui/ImageText.java index c393c46..5945b82 100644 --- a/src/be/nikiroo/jvcard/tui/ImageText.java +++ b/src/be/nikiroo/jvcard/tui/ImageText.java @@ -8,6 +8,13 @@ import java.awt.image.ImageObserver; import com.googlecode.lanterna.TerminalSize; +/** + * This class converts an {@link Image} into a textual representation that can + * be displayed to the user in a TUI. + * + * @author niki + * + */ public class ImageText { private Image image; private TerminalSize size; @@ -16,6 +23,13 @@ public class ImageText { private Mode mode; private boolean invert; + /** + * Th rendering modes supported by this {@link ImageText} to convert + * {@link Image}s into text. + * + * @author niki + * + */ public enum Mode { /** * Use 5 different "colours" which are actually Unicode @@ -47,47 +61,104 @@ public class ImageText { ASCII, } - public ImageText(Image image, TerminalSize size, Mode mode) { - setImage(image, size); - setMode(mode); + /** + * Create a new {@link ImageText} with the given parameters. Defaults to + * {@link Mode#DOUBLE_DITHERING} and no colour inversion. + * + * @param image + * the source {@link Image} + * @param size + * the final text size to target + */ + public ImageText(Image image, TerminalSize size) { + this(image, size, Mode.DOUBLE_DITHERING, false); } - public void setImage(Image image) { - setImage(image, size); + /** + * Create a new {@link ImageText} with the given parameters. + * + * @param image + * the source {@link Image} + * @param size + * the final text size to target + * @param mode + * the mode of conversion + * @param invert + * TRUE to invert colours rendering + */ + public ImageText(Image image, TerminalSize size, Mode mode, boolean invert) { + setImage(image); + setSize(size); + setMode(mode); + setColorInvert(invert); } - public void setImage(TerminalSize size) { - setImage(image, size); + /** + * Change the source {@link Image}. + * + * @param image + * the new {@link Image} + */ + public void setImage(Image image) { + this.text = null; + this.ready = false; + this.image = image; } - public void setImage(Image image, TerminalSize size) { + /** + * Change the target size of this {@link ImageText}. + * + * @param size + * the new size + */ + public void setSize(TerminalSize size) { this.text = null; this.ready = false; this.size = size; - if (image != null) { - this.image = image; - } } + /** + * Change the image-to-text mode. + * + * @param mode + * the new {@link Mode} + */ public void setMode(Mode mode) { this.mode = mode; this.text = null; this.ready = false; } + /** + * Set the colour-invert mode. + * + * @param invert + * TRUE to inverse the colours + */ public void setColorInvert(boolean invert) { this.invert = invert; this.text = null; this.ready = false; } - public boolean getColorInvert() { + /** + * Check if the colours are inverted. + * + * @return TRUE if the colours are inverted + */ + public boolean isColorInvert() { return invert; } + /** + * Return the textual representation of the included {@link Image}. + * + * @return the {@link String} representation + */ public String getText() { if (text == null) { - if (image == null) + if (image == null || size == null || size.getColumns() == 0 + || size.getRows() == 0) return ""; int mult = 1; @@ -108,7 +179,7 @@ public class ImageText { int x = 0; int y = 0; - if (srcSize.getColumns() > srcSize.getRows()) { + if (srcSize.getColumns() < srcSize.getRows()) { double ratio = (double) size.getColumns() / (double) size.getRows(); ratio *= (double) srcSize.getRows() @@ -154,12 +225,14 @@ public class ImageText { for (int col = 0; col < buff.getWidth(); col += mult) { if (mult == 1) { + char car = ' '; + float brightness = getBrightness(buff.getRGB(col, row)); if (mode == Mode.DITHERING) - builder.append(getDitheringChar(buff.getRGB(col, - row))); - else - // Mode.ASCII - builder.append(getAsciiChar(buff.getRGB(col, row))); + car = getDitheringChar(brightness, " ░▒▓█"); + if (mode == Mode.ASCII) + car = getDitheringChar(brightness, " .-+=o8#"); + + builder.append(car); } else if (mult == 2) { builder.append(getBlockChar( // buff.getRGB(col, row),// @@ -183,6 +256,14 @@ public class ImageText { return getText(); } + /** + * Return the size of the given {@link Image}. + * + * @param img + * the image to measure + * + * @return the size + */ static private TerminalSize getSize(Image img) { TerminalSize size = null; while (size == null) { @@ -201,49 +282,52 @@ public class ImageText { return size; } - private float[] tmp = new float[4]; - - private char getAsciiChar(int pixel) { - float brigthness = getBrightness(pixel, tmp); - if (brigthness < 0.20) { - return ' '; - } else if (brigthness < 0.40) { - return '.'; - } else if (brigthness < 0.60) { - return '+'; - } else if (brigthness < 0.80) { - return '*'; - } else { - return '#'; - } - } - - private char getDitheringChar(int pixel) { - float brigthness = getBrightness(pixel, tmp); - if (brigthness < 0.20) { - return ' '; - } else if (brigthness < 0.40) { - return '░'; - } else if (brigthness < 0.60) { - return '▒'; - } else if (brigthness < 0.80) { - return '▓'; - } else { - return '█'; - } + /** + * Return the {@link Character} corresponding to the given brightness level + * from the evenly-separated given {@link Character}s. + * + * @param brightness + * the brightness level + * @param cars + * the {@link Character}s to choose from, from less bright to + * most bright; MUST contain at least one + * {@link Character} + * + * @return the {@link Character} to use + */ + private char getDitheringChar(float brightness, String cars) { + int index = Math.round(brightness * (cars.length() - 1)); + return cars.charAt(index); } + /** + * Return the {@link Character} corresponding to the 4 given colours in + * {@link Mode#DOUBLE_RESOLUTION} or {@link Mode#DOUBLE_DITHERING} mode. + * + * @param upperleft + * the upper left colour + * @param upperright + * the upper right colour + * @param lowerleft + * the lower left colour + * @param lowerright + * the lower right colour + * @param dithering + * TRUE to use {@link Mode#DOUBLE_DITHERING}, FALSE for + * {@link Mode#DOUBLE_RESOLUTION} + * + * @return the {@link Character} to use + */ private char getBlockChar(int upperleft, int upperright, int lowerleft, int lowerright, boolean dithering) { - float trigger = dithering ? 0.20f : 0.50f; int choice = 0; - if (getBrightness(upperleft, tmp) > trigger) + if (getBrightness(upperleft) > 0.5f) choice += 1; - if (getBrightness(upperright, tmp) > trigger) + if (getBrightness(upperright) > 0.5f) choice += 2; - if (getBrightness(lowerleft, tmp) > trigger) + if (getBrightness(lowerleft) > 0.5f) choice += 4; - if (getBrightness(lowerright, tmp) > trigger) + if (getBrightness(lowerright) > 0.5f) choice += 8; switch (choice) { @@ -280,23 +364,13 @@ public class ImageText { case 15: if (dithering) { float avg = 0; - avg += getBrightness(upperleft, tmp); - avg += getBrightness(upperright, tmp); - avg += getBrightness(lowerleft, tmp); - avg += getBrightness(lowerright, tmp); + avg += getBrightness(upperleft); + avg += getBrightness(upperright); + avg += getBrightness(lowerleft); + avg += getBrightness(lowerright); avg /= 4; - if (avg < 0.20) { - return ' '; - } else if (avg < 0.40) { - return '░'; - } else if (avg < 0.60) { - return '▒'; - } else if (avg < 0.80) { - return '▓'; - } else { - return '█'; - } + return getDitheringChar(avg, " ░▒▓█"); } else { return '█'; } @@ -305,14 +379,43 @@ public class ImageText { return ' '; } - float getBrightness(int argb, float[] array) { + /** + * Temporary array used so not to create a lot of new ones. + */ + private float[] tmp = new float[4]; + + /** + * Return the brightness value to use from the given ARGB colour. + * + * @param argb + * the argb colour + * + * @return the brightness to sue for computations + */ + private float getBrightness(int argb) { if (invert) return 1 - rgb2hsb(argb, tmp)[2]; return rgb2hsb(argb, tmp)[2]; } - // return [h, s, l, a]; h/s/b/a: 0 to 1 (h is given in 1/360th) - // like RGBtoHSB, array can be null or used + /** + * Convert the given ARGB colour in HSL/HSB, either into the supplied array + * or into a new one if array is NULL. + * + *

+ * ARGB pixels are given in 0xAARRGGBB format, while the returned array will + * contain Hue, Saturation, Lightness/Brightness, Alpha, in this order. H, + * S, L and A are all ranging from 0 to 1 (indeed, H is in 1/360th). + *

+ * pixel + * + * @param argb + * the ARGB colour pixel to convert + * @param array + * the array to convert into or NULL to create a new one + * + * @return the array containing the HSL/HSB converted colour + */ static float[] rgb2hsb(int argb, float[] array) { int a, r, g, b; a = ((argb & 0xff000000) >> 24);