I guess this is an easy question for someone with Common Lisp experience. Not so much for me, who just started out with LISP.
As you see in the next snippet below, I create a 800 by 600 array of type UNSIGNED BYTE
.
(defun test-binary-save ()
(let*
((width 800)
(height 600)
(arr (make-array (list width height)
:element-type '(mod 256)
:initial-element 0)))
(utilities::save-array-as-pgm "test.pgm" arr)))
And the function in my utilities package is supposed to write that in P5 PGM
format do disk.
(defun save-array-as-pgm (filename buffer)
"Writes a byte array as a PGM file (P5) to a file."
(with-open-file
(stream filename
:element-type '(unsigned-byte 8)
:direction :output
:if-does-not-exist :create
:if-exists :supersede)
(let*
((dimensions (array-dimensions buffer))
(width (first dimensions))
(height (second dimensions))
(header (format nil "P5~A~D ~D~A255~A"
#\newline
width height #\newline
#\newline)))
(loop
:for char :across header
:do (write-byte (char-code char) stream))
;(write-sequence buffer stream) <<-- DOES NOT WORK - is not of type SEQUENCE
))
filename)
The equivalent (and working) C-function which does the same thing looks like this.
static
int
save_pgm
( const char* filename
, size_t width
, size_t height
, const uint8_t* pixels
)
{
if(NULL == filename)
return 0;
if(NULL == pixels)
return 0;
FILE *out = fopen(filename, "wb");
if(NULL != out)
{
fprintf(out, "P5\n%zu %zu\n255\n", width, height);
size_t nbytes = width * height;
fwrite(pixels,1,nbytes,out);
fclose(out);
return 1;
}
return 0;
}
Who can tell me how to fix my save-array-as-pgm
function, preferably with writing the array in one go, instead of using a loop and (write-byte (aref buffer y x) stream)
?
Before I decided to ask this question here, I googled a lot and only found references to some packages which do fancy binary stuff - but this is a simple case and I look for a simple solution.
Common Lisp supports displaced arrays:
CL-USER 6 > (let ((array (make-array (list 3 4)
:initial-element 1
:element-type 'bit)))
(make-array (reduce #'* (array-dimensions array))
:element-type 'bit
:displaced-to array))
#*111111111111
A displaced array has no storage on its own, but uses the storage of another array. It can have different dimensions.
Now there is the question how efficiently the Lisp implementation can access the array through the displaced array.
@BitTicker When optimizing for write-sequence, consider also defining first the 1D array and have the 2D array be displaced to it (a small benchmark with sbcl gives however no significant difference in time between the to approaches)