;;; ;;; | png-to-text.lisp ;;; | What it says on the tin ;;; +------------------------ ;;; ;;; ./png-to-text.lisp ;;; ;;; Author: Andres V. ;;; License: GPL V3 ;;; Anyone and everyone is allowed to use and change this, ;;; do whatever you want. ;;; Notes: This only supports PNG files. (ql:quickload "png") (defun flatten (l) (cond ((null l) nil) ((atom l) (list l)) (t (loop for a in l appending (flatten a))))) (defun average (l) (/ (apply #'+ l) (list-length l))) (defun normalize-pixel (image x y) "Gray-scale-ifies the pixel to be a single value" (let ((values (loop for c from 0 to (1- (png:image-channels image)) :collect (if (and (< x (png:image-width image)) (< y (png:image-height image))) (aref image y x c) 0)))) (average values))) (defun average-brightness (image x y w h) "Returns the average brightness in a scale from 0 to 1 of the pixel group" (let ((values (flatten (loop for i from x to (1- (+ x w)) :collect (loop for j from y to (1-(+ y h)) :collect (normalize-pixel image i j)))))) (/ (average values) 256))) (defvar color-blocksy (list '(4/5 "█") '(3/5 "▓") '(2/5 "▒") '(1/5 "░") '(0/5 " "))) (defun selector (number selectors) "Takes a number to compare to and a selectors list composed of a number to compare to and a return value" (second (find-if (lambda (x) (>= number (first x))) selectors))) (defun select-block (brightness) (selector brightness color-blocksy)) (defun blockify-image (image w h) (let* ((sw (floor (png:image-width image) w)) (sh (floor (png:image-height image) h))) (loop for i from 0 to (1- w) :collect (loop for j from 0 to (1- h) :collect (select-block (average-brightness image (* i sw) (* j sh) sw sh)))))) (defun print-blocky-image (stream blocky-image) (dolist (line blocky-image) (format stream "~{~A~}~%" line))) (defun rotate-list (l w h) (loop for j from 0 to (1- h) :collect (loop for i from 0 to (1- w) :collect (nth j (nth (- (1- w) i) l))))) (defun flip-list (l) (loop for i in l :collect (reverse i))) (defun process-file (filename w h) (with-open-file (in filename :element-type '(unsigned-byte 8)) (let* ((image (png:decode in)) (blocks (blockify-image image w h))) (print-blocky-image t (flip-list (rotate-list blocks w h))) ))) (defun denil (x) (if (eq x 'nil) "" x)) (defun main () (let* ((fname (nth 1 *posix-argv*)) (widths (nth 2 *posix-argv*)) (heights (nth 3 *posix-argv*)) (width (parse-integer (denil widths) :junk-allowed t)) (height (parse-integer (denil heights) :junk-allowed t))) (if (or (eq width 'nil) (eq height 'nil) (eq fname 'nil)) (format t "Usage:~%$png-to-text ~%") (process-file fname width height))))