replace manual image manipulation with our own new clj-image2ascii lib
This commit is contained in:
parent
7c29069559
commit
aae01476a0
|
@ -13,6 +13,7 @@
|
||||||
[environ "0.4.0"]
|
[environ "0.4.0"]
|
||||||
[clj-metasearch "0.1.1"]
|
[clj-metasearch "0.1.1"]
|
||||||
[clj-figlet "0.1.1"]
|
[clj-figlet "0.1.1"]
|
||||||
|
[clj-image2ascii "0.1.0-SNAPSHOT"]
|
||||||
[com.cemerick/url "0.1.1"]
|
[com.cemerick/url "0.1.1"]
|
||||||
[criterium "0.4.3" :scope "test"]]
|
[criterium "0.4.3" :scope "test"]]
|
||||||
:source-paths ["src/clojure"]
|
:source-paths ["src/clojure"]
|
||||||
|
|
|
@ -1,57 +1,19 @@
|
||||||
(ns toascii.models.image
|
(ns toascii.models.image
|
||||||
(:import (java.awt RenderingHints Graphics2D Image)
|
(:import (java.awt.image BufferedImage))
|
||||||
(java.awt.image BufferedImage Raster)
|
(:require [toascii.util :refer [query-param-url->java-url]]
|
||||||
(javax.imageio ImageIO)
|
[clj-image2ascii.core :as i2a])
|
||||||
(java.io File)
|
|
||||||
(java.net URL)
|
|
||||||
(toascii.images ImageToAscii))
|
|
||||||
(:require [clojure.string :as str]
|
|
||||||
[cemerick.url :as url]
|
|
||||||
[toascii.util :refer [query-param-url->java-url]])
|
|
||||||
(:use hiccup.core))
|
(:use hiccup.core))
|
||||||
|
|
||||||
(defn get-image-by-url
|
(defn get-image [^String url]
|
||||||
(^BufferedImage [^String url]
|
(let [java-url (query-param-url->java-url url)]
|
||||||
(try
|
(i2a/get-image-by-url java-url)))
|
||||||
(let [java-url (query-param-url->java-url url)]
|
|
||||||
(ImageIO/read java-url))
|
|
||||||
(catch Exception ex))))
|
|
||||||
|
|
||||||
(defn get-image-by-file
|
|
||||||
"returns a BufferedImage loaded from the file specified, or null if an error occurs."
|
|
||||||
(^BufferedImage [^File file]
|
|
||||||
(try
|
|
||||||
(ImageIO/read file)
|
|
||||||
(catch Exception ex))))
|
|
||||||
|
|
||||||
(defn scale-image
|
|
||||||
"takes a source image specified by the uri (a filename or a URL) and scales it proportionally
|
|
||||||
using the new width, returning the newly scaled image."
|
|
||||||
(^BufferedImage [^BufferedImage image new-width]
|
|
||||||
(let [new-height (* (/ new-width (.getWidth image))
|
|
||||||
(.getHeight image))
|
|
||||||
scaled-image (BufferedImage. new-width new-height BufferedImage/TYPE_INT_RGB)
|
|
||||||
gfx2d (doto (.createGraphics scaled-image)
|
|
||||||
(.setRenderingHint RenderingHints/KEY_INTERPOLATION
|
|
||||||
RenderingHints/VALUE_INTERPOLATION_BILINEAR)
|
|
||||||
(.drawImage image 0 0 new-width new-height nil)
|
|
||||||
(.dispose))]
|
|
||||||
scaled-image)))
|
|
||||||
|
|
||||||
(defn convert-image
|
|
||||||
"converts the image to an ascii representation. a multiline string will be returned,
|
|
||||||
which will be formatted for html if color? is true, or plain text if false. if
|
|
||||||
scale-to-width is specified the resulting image will be scaled proportionally from
|
|
||||||
the source image."
|
|
||||||
([^BufferedImage image color?]
|
|
||||||
(convert-image image nil color?))
|
|
||||||
([^BufferedImage image scale-to-width color?]
|
|
||||||
(let [current-width (.getWidth image)
|
|
||||||
new-width (or scale-to-width current-width)
|
|
||||||
final-image (if-not (= new-width current-width)
|
|
||||||
(scale-image image new-width)
|
|
||||||
image)]
|
|
||||||
(ImageToAscii/convert final-image color?))))
|
|
||||||
|
|
||||||
(defn wrap-pre-tag [s]
|
(defn wrap-pre-tag [s]
|
||||||
(str "<pre style=\"font-size:6pt; letter-spacing:1px; line-height:5pt; font-weight:bold;\">" s "</pre>"))
|
(str "<pre style=\"font-size:6pt; letter-spacing:1px; line-height:5pt; font-weight:bold;\">" s "</pre>"))
|
||||||
|
|
||||||
|
(defn image->ascii [^BufferedImage image scale-to-width color? html?]
|
||||||
|
(let [converted (i2a/convert-image image scale-to-width color?)
|
||||||
|
ascii (:image converted)]
|
||||||
|
(if html?
|
||||||
|
(wrap-pre-tag ascii)
|
||||||
|
ascii)))
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
[liberator.core :refer [defresource]]
|
[liberator.core :refer [defresource]]
|
||||||
[compojure.core :refer [ANY]]
|
[compojure.core :refer [ANY]]
|
||||||
[toascii.route-utils :refer [register-routes]]
|
[toascii.route-utils :refer [register-routes]]
|
||||||
[toascii.models.image :refer [convert-image get-image-by-url wrap-pre-tag]]
|
[toascii.models.image :refer [image->ascii get-image]]
|
||||||
[toascii.util :refer [parse-int parse-boolean]]))
|
[toascii.util :refer [parse-int parse-boolean]]))
|
||||||
|
|
||||||
(defresource render-image [{:keys [url width color format] :as params}]
|
(defresource render-image [{:keys [url width color format] :as params}]
|
||||||
|
@ -26,21 +26,15 @@
|
||||||
(parse-boolean color)) {:error "Cannot output colored plain text version of image."}))
|
(parse-boolean color)) {:error "Cannot output colored plain text version of image."}))
|
||||||
:exists?
|
:exists?
|
||||||
(fn [_]
|
(fn [_]
|
||||||
(if-let [image (get-image-by-url url)]
|
(if-let [image (get-image url)]
|
||||||
{:image image}
|
{:image image}
|
||||||
[false {:error "Image could not be loaded."}]))
|
[false {:error "Image could not be loaded."}]))
|
||||||
:handle-ok
|
:handle-ok
|
||||||
(fn [ctx]
|
(fn [ctx]
|
||||||
(let [html? (= "text/html" (get-in ctx [:representation :media-type]))
|
(let [html? (= "text/html" (get-in ctx [:representation :media-type]))
|
||||||
color? (or (and html? (nil? color))
|
color? (or (and html? (nil? color))
|
||||||
(parse-boolean color))
|
(parse-boolean color))]
|
||||||
output (convert-image
|
(image->ascii (:image ctx) (parse-int width) color? html?)))
|
||||||
(:image ctx)
|
|
||||||
(parse-int width)
|
|
||||||
color?)]
|
|
||||||
(if html?
|
|
||||||
(wrap-pre-tag output)
|
|
||||||
output)))
|
|
||||||
:handle-malformed
|
:handle-malformed
|
||||||
(fn [ctx]
|
(fn [ctx]
|
||||||
(:error ctx))
|
(:error ctx))
|
||||||
|
|
|
@ -1,83 +0,0 @@
|
||||||
package toascii.images;
|
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
// 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'
|
|
||||||
};
|
|
||||||
|
|
||||||
// modification of java.lang.Integer.toUnsignedString -- no garbage generated, but limited to max value
|
|
||||||
// of 255 ...hence the 'unsigned byte' thing :)
|
|
||||||
private static void unsignedByteToHex(int unsignedByte, StringBuilder sb) {
|
|
||||||
for (int i = 0; i < 2; ++i) {
|
|
||||||
int index = sb.length + 1 - i;
|
|
||||||
if (unsignedByte != 0) {
|
|
||||||
sb.chars[index] = digits[unsignedByte & 15];
|
|
||||||
unsignedByte >>>= 4;
|
|
||||||
} else
|
|
||||||
sb.chars[index] = '0';
|
|
||||||
}
|
|
||||||
sb.length += 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 StringBuilder sb = new StringBuilder(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:#");
|
|
||||||
unsignedByteToHex(r, sb);
|
|
||||||
unsignedByteToHex(g, sb);
|
|
||||||
unsignedByteToHex(b, sb);
|
|
||||||
sb.append(";\">");
|
|
||||||
sb.append(pixelChar);
|
|
||||||
sb.append("</span>");
|
|
||||||
} else
|
|
||||||
sb.append(pixelChar);
|
|
||||||
}
|
|
||||||
if (useColor)
|
|
||||||
sb.append("<br>");
|
|
||||||
else
|
|
||||||
sb.append('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
Binary file not shown.
Before Width: | Height: | Size: 135 KiB |
Reference in a new issue