From 7c218ec19c9b9d514dbf46cea9c55276ef4016b8 Mon Sep 17 00:00:00 2001 From: gered Date: Sun, 30 Mar 2014 07:56:50 -0400 Subject: [PATCH] add initial support for converting images (by url) to ascii --- src/toascii/models/image.clj | 62 ++++++++++++++++++++++++++++++++ src/toascii/routes/api/image.clj | 31 ++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 src/toascii/models/image.clj create mode 100644 src/toascii/routes/api/image.clj diff --git a/src/toascii/models/image.clj b/src/toascii/models/image.clj new file mode 100644 index 0000000..74bbcc2 --- /dev/null +++ b/src/toascii/models/image.clj @@ -0,0 +1,62 @@ +(ns toascii.models.image + "Largely based on the Claskii library: https://github.com/LauJensen/Claskii" + (:import (java.awt Color RenderingHints Graphics2D Image) + (java.awt.image BufferedImage) + (javax.swing ImageIcon) + (javax.imageio ImageIO) + (java.io File) + (java.net URL URLEncoder URI)) + (:require [cemerick.url :as url] + [toascii.util :refer [query-param-url->java-url]]) + (:use hiccup.core)) + +(def ascii-chars [\# \A \@ \% \$ \+ \= \* \: \, \. \space]) + +(defmacro get-properties [obj & properties] + (let [target (gensym)] + `(let [~target ~obj] + (vector ~@(for [property properties] + `(~property ~target)))))) + +(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." + [url new-width] + (let [^Image image (ImageIO/read url) + 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 ascii [^BufferedImage img x y color?] + (let [[red green blue] (get-properties (Color. (.getRGB img x y)) + .getRed .getGreen .getBlue) + peak (apply max [red green blue]) + idx (if (zero? peak) + (dec (count ascii-chars)) + (dec (int (+ 1/2 (* (count ascii-chars) (/ peak 255)))))) + output (nth ascii-chars (if (pos? idx) idx 0)) ] + (if color? + (html [:span {:style (format "color: rgb(%s,%s,%s);" red green blue)} output]) + output))) + +(defn convert-image [url w color?] + (let [java-url (query-param-url->java-url url) + ^Image raw-image (scale-image java-url w) + ascii-image (->> (for [y (range (.getHeight raw-image)) + x (range (.getWidth raw-image))] + (ascii raw-image x y color?)) + (partition w)) + output (->> ascii-image + (interpose (if color? "
" \newline)) + flatten)] + (if color? + (html [:pre {:style "font-size:5pt; letter-spacing:1px; + line-height:4pt; font-weight:bold;"} + output]) + (println output)))) \ No newline at end of file diff --git a/src/toascii/routes/api/image.clj b/src/toascii/routes/api/image.clj new file mode 100644 index 0000000..556b6df --- /dev/null +++ b/src/toascii/routes/api/image.clj @@ -0,0 +1,31 @@ +(ns toascii.routes.api.image + (:require [clojure.string :as str] + [liberator.core :refer [defresource]] + [compojure.core :refer [ANY]] + [toascii.route-utils :refer [register-routes]] + [toascii.models.image :refer [convert-image]])) + +(defresource render-image [{:keys [url width color format] :as params}] + :media-type-available? + (fn [ctx] + (let [type (condp = format + "html" "text/html" + "text" "text/plain" + "text/html")] + {:representation {:media-type type}})) + :malformed? + (fn [_] + (cond + (str/blank? url) {:error "Missing image url"})) + :handle-ok + (fn [ctx] + (let [rendered (convert-image url 64 true)] + (if (= "text/html" (get-in ctx [:representation :media-type])) + rendered + rendered))) + :handle-malformed + (fn [ctx] + (:error ctx))) + +(register-routes api-image-routes + (ANY "/api/image" {params :params} (render-image params))) \ No newline at end of file