add basic java classes needed for core image -> ascii conversion
copied from another project of mine
This commit is contained in:
parent
4ba01369d5
commit
771577ec7f
64
src/java/clj_image2ascii/java/ImageToAscii.java
Normal file
64
src/java/clj_image2ascii/java/ImageToAscii.java
Normal file
|
@ -0,0 +1,64 @@
|
|||
package clj_image2ascii.java;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
// This method of converting an image to ascii representation is based on the method used in
|
||||
// Claskii (https://github.com/LauJensen/Claskii). Some improvements have been made, such as
|
||||
// a better way of calculating pixel brightness and little tweaks to how the right ASCII
|
||||
// character is selected, as well as obviously a conversion to Java purely for performance.
|
||||
|
||||
public class ImageToAscii {
|
||||
static final char[] asciiChars = {'#', 'A', '@', '%', '$', '+', '=', '*', ':', ',', '.', ' '};
|
||||
static final int numAsciiChars = asciiChars.length - 1;
|
||||
static final int spanLength = "<span style=\"color:#112233;\">X</span>".length();
|
||||
static final int lineTerminatorLength = "<br>".length();
|
||||
|
||||
public static String convert(BufferedImage image, boolean useColor) {
|
||||
final int width = image.getWidth();
|
||||
final int height = image.getHeight();
|
||||
|
||||
final int maxLength = (useColor ?
|
||||
(width * height * spanLength) + (height * lineTerminatorLength) :
|
||||
(width * height) + height);
|
||||
|
||||
final MicroStringBuilder sb = new MicroStringBuilder(maxLength);
|
||||
|
||||
final int[] pixels = image.getRGB(0, 0, width, height, null, 0, width);
|
||||
|
||||
for (int y = 0; y < height; ++y) {
|
||||
for (int x = 0; x < width; ++x) {
|
||||
final int argb = pixels[(y * width) + x];
|
||||
final int r = (0x00ff0000 & argb) >> 16;
|
||||
final int g = (0x0000ff00 & argb) >> 8;
|
||||
final int b = (0x000000ff & argb);
|
||||
final double brightness = Math.sqrt((r * r * 0.241f) +
|
||||
(g * g * 0.691f) +
|
||||
(b * b * 0.068f));
|
||||
int charIndex;
|
||||
if (brightness == 0.0f)
|
||||
charIndex = numAsciiChars;
|
||||
else
|
||||
charIndex = (int)((brightness / 255.0f) * numAsciiChars);
|
||||
|
||||
final char pixelChar = asciiChars[charIndex > 0 ? charIndex : 0];
|
||||
|
||||
if (useColor) {
|
||||
sb.append("<span style=\"color:#");
|
||||
sb.appendAsHex(r);
|
||||
sb.appendAsHex(g);
|
||||
sb.appendAsHex(b);
|
||||
sb.append(";\">");
|
||||
sb.append(pixelChar);
|
||||
sb.append("</span>");
|
||||
} else
|
||||
sb.append(pixelChar);
|
||||
}
|
||||
if (useColor)
|
||||
sb.append("<br>");
|
||||
else
|
||||
sb.append('\n');
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
58
src/java/clj_image2ascii/java/MicroStringBuilder.java
Normal file
58
src/java/clj_image2ascii/java/MicroStringBuilder.java
Normal file
|
@ -0,0 +1,58 @@
|
|||
package clj_image2ascii.java;
|
||||
|
||||
// I wanted a StringBuilder that had an append() method which allowed appending unsigned byte values
|
||||
// as a 2-char hex representation (e.g. 255 => "FF") in a way that did not allocate any extra memory
|
||||
// and could be done "in-place" with the StringBuilder's internal character array. This kind of evolved
|
||||
// into this class being added since I was only using 2 append() methods from java.lang.StringBuilder
|
||||
// anyway. Except for the appendAsHex(), this class does not perform noticeably faster then
|
||||
// java.lang.StringBuilder does.
|
||||
|
||||
public class MicroStringBuilder {
|
||||
// copied from java.lang.Integer.digits (which is private so we can't just reference it, boourns)
|
||||
static final char[] digits = {
|
||||
'0' , '1' , '2' , '3' , '4' , '5' ,
|
||||
'6' , '7' , '8' , '9' , 'a' , 'b' ,
|
||||
'c' , 'd' , 'e' , 'f' , 'g' , 'h' ,
|
||||
'i' , 'j' , 'k' , 'l' , 'm' , 'n' ,
|
||||
'o' , 'p' , 'q' , 'r' , 's' , 't' ,
|
||||
'u' , 'v' , 'w' , 'x' , 'y' , 'z'
|
||||
};
|
||||
|
||||
final char[] chars;
|
||||
int length;
|
||||
|
||||
public MicroStringBuilder(int capacity) {
|
||||
chars = new char[capacity];
|
||||
length = 0;
|
||||
}
|
||||
|
||||
public void append(final String s) {
|
||||
final int srcLength = s.length();
|
||||
s.getChars(0, srcLength, chars, length);
|
||||
length += srcLength;
|
||||
}
|
||||
|
||||
public void append(final char c) {
|
||||
chars[length] = c;
|
||||
++length;
|
||||
}
|
||||
|
||||
// modification of java.lang.Integer.toUnsignedString -- no garbage generated, but limited to max value
|
||||
// of 255 ...hence the 'unsigned byte' thing :)
|
||||
public void appendAsHex(int unsignedByte) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
final int index = length + 1 - i;
|
||||
if (unsignedByte != 0) {
|
||||
chars[index] = digits[unsignedByte & 15];
|
||||
unsignedByte >>>= 4;
|
||||
} else
|
||||
chars[index] = '0';
|
||||
}
|
||||
length += 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new String(chars, 0, length);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue