(I'm putting this out here since Google queries for this situation
didn't turn up anything useful for me.)
Last night I had to add some text ± 5000 PNG files and I knew about
Zach Beane's ZPNG package for reading PNGs but I didn't know of a
package that would read them.
A quicklisp:system-apropos turned up png-read written by Ramarren
which looked simple enough to use so I went with that. After
initially failing to get ZPNG to use png-read's image-data slot I gave
up and wrote a little loop to copy the values over to zpng:data-array.
This was succesful after one or two tries:
(asdf :png-read)
(asdf :zpng)
;; http://imgur.com/qtriH.png
(defparameter png (png-read:read-png-file "qtriH.png"))
(defparameter zpng (make-instance 'zpng:png :color-type :truecolor
:width (png-read:width png)
:height (png-read:height png)))
(loop for y from 0 below (zpng:height zpng)
do (loop for x from 0 below (zpng:width zpng)
do (loop for rgb from 0 below 3
do (setf (aref (zpng:data-array zpng) y x rgb)
(aref (png-read:image-data png) x y rgb)))))
(zpng:write-png zpng "tmp.png")
I'm sure Zach had a good reason to reverse the x and y in ZPNG but I
can't deduce it.
So, reading and writing the files was working but now I realized ZPNG
didn't have any functionality for generating text. (Why should it?
It shouldn't.) I started thinking of Vecto since I had used that
before and also recalled that it actually used ZPNG for saving PNGs.
Now all I needed to do was getting the original image into Vecto so
text could be written over it.
Going through Vecto's source showed me I could get at the ZPNG object
through the (shadowed) *GRAPHICS-STATE* global. It wasn't exported
from the Vecto package but since I was in hack mode anyway it didn't
really matter. I used the (slightly adapted) code above, drew some
text, did a VECTO:SAVE-PNG and got an empty image.
In Vecto's source I noticed the ZPNG object had four channels instead
of the three I was using so I set the fourth channel (alpha) to opaque
and got my input PNG back with the custom text generated by Vecto.
Done!
(asdf :png-read)
(asdf :vecto)
(use-package :vecto)
;; http://imgur.com/qtriH.png
(defparameter png (png-read:read-png-file "qtriH.png"))
(defparameter zpng (make-instance 'zpng:png :color-type :truecolor
:width (png-read:width png)
:height (png-read:height png)))
(with-canvas (:width (zpng:width zpng) :height (zpng:height zpng))
(loop for y from 0 below (zpng:height zpng)
do (loop for x from 0 below (zpng:width zpng)
do (loop for rgb from 0 below 4
do (if (< rgb 3)
(setf (aref (zpng:data-array (vecto::image vecto::*graphics-state*)) y x rgb)
(aref (png-read:image-data png) x y rgb))
(setf (aref (zpng:data-array (vecto::image vecto::*graphics-state*)) y x rgb)
255)))))
(set-font (get-font "font.ttf") 32)
(draw-centered-string (floor (/ (zpng:width zpng) 2))
(- (zpng:height zpng) 80)
"Hello, World!")
(save-png "tmp.png"))
Labels: common-lisp, lisp, png, png-read, vecto, zpng