1: | | (declare (fixnum)) |
2: | | |
3: | | ;;; make-pgm takes two objects, an exact integer giving the maximum grey scale |
4: | | ;;; and an Array that returns exact integer values between 0 and greys inclusive. |
5: | | |
6: | | (define pgm-greys car) |
7: | | (define pgm-pixels cdr) |
8: | | (define make-pgm cons) |
9: | | |
10: | | (define (read-pgm file) |
11: | | |
12: | | (define (read-pgm-object port) |
13: | | (skip-white-space port) |
14: | | (let ((o (read port))) |
15: | | (read-char port) ; to skip the newline or next whitespace |
16: | | (if (eof-object? o) |
17: | | (error "reached end of pgm file") |
18: | | o))) |
19: | | |
20: | | (define (skip-to-end-of-line port) |
21: | | (let loop ((ch (read-char port))) |
22: | | (if (not (eq? ch #\newline)) |
23: | | (loop (read-char port))))) |
24: | | |
25: | | (define (white-space? ch) |
26: | | (case ch |
27: | | ((#\newline #\space #\tab) #t) |
28: | | (else #f))) |
29: | | |
30: | | (define (skip-white-space port) |
31: | | (let ((ch (peek-char port))) |
32: | | (cond ((white-space? ch) (read-char port) (skip-white-space port)) |
33: | | ((eq? ch #\#) (skip-to-end-of-line port)(skip-white-space port)) |
34: | | (else #f)))) |
35: | | |
36: | | (call-with-input-file |
37: | | file |
38: | | (lambda (port) |
39: | | (let* ((header (read-pgm-object port)) |
40: | | (columns (read-pgm-object port)) |
41: | | (rows (read-pgm-object port)) |
42: | | (greys (read-pgm-object port))) |
43: | | (make-pgm greys |
44: | | (Array->Fixed-array generic-array-manipulators |
45: | | (build-Array (build-Interval '#(0 0) |
46: | | (vector rows columns)) |
47: | | (cond ((or (eq? header 'p5) ;; pgm binary |
48: | | (eq? header 'P5)) |
49: | | (if (< greys 256) |
50: | | (lambda (i j) ;; one byte/pixel |
51: | | (char->integer (read-char port))) |
52: | | (lambda (i j) ;; two bytes/pixel, little-endian |
53: | | (let* ((first-byte (char->integer (read-char port))) |
54: | | (second-byte (char->integer (read-char port)))) |
55: | | (+ (##fixnum.arithmetic-shift-left second-byte 8) first-byte))))) |
56: | | ((or (eq? header 'p2) ;; pgm ascii |
57: | | (eq? header 'P2)) |
58: | | (lambda (i j) |
59: | | (read port))) |
60: | | (else |
61: | | (error "read-pgm: not a pgm file")))))))))) |
62: | | |
63: | | (define (write-pgm pgm-data file #!optional force-ascii) |
64: | | (call-with-output-file |
65: | | file |
66: | | (lambda (port) |
67: | | (let* ((greys (pgm-greys pgm-data)) |
68: | | (pgm-array (pgm-pixels pgm-data)) |
69: | | (array-domain (Array-domain pgm-array)) |
70: | | (rows (Interval-upper-bound array-domain 0)) |
71: | | (columns (Interval-upper-bound array-domain 1))) |
72: | | (if force-ascii |
73: | | (display "P2" port) |
74: | | (display "P5" port)) |
75: | | (newline port) |
76: | | (display columns port) (display " " port) |
77: | | (display rows port) (newline port) |
78: | | (display greys port) (newline port) |
79: | | (Array-for-each (if force-ascii |
80: | | (let ((next-pixel-in-line 1)) |
81: | | (lambda (p) |
82: | | (write p port) |
83: | | (if (zero? (bitwise-and next-pixel-in-line 15)) |
84: | | (begin |
85: | | (newline port) |
86: | | (set! next-pixel-in-line 1)) |
87: | | (begin |
88: | | (display " " port) |
89: | | (set! next-pixel-in-line (+ 1 next-pixel-in-line)))))) |
90: | | (if (< greys 256) |
91: | | (lambda (p) |
92: | .02% | (write-char (integer->char p) port)) |
93: | | (lambda (p) |
94: | | (write-char (integer->char (bitwise-and p 255)) port) |
95: | | (write-char (integer->char (##fixnum.arithmetic-shift-right p 8)) port)))) |
96: | | pgm-array))))) |
97: | | |
98: | | ;;; convert pgm files to and from f64 arrays |
99: | | |
100: | | (define (pgm->f64 pgm) |
101: | | (Array-map f64-array-manipulators |
102: | | (lambda (p) |
103: | | (##flonum.<-fixnum p)) |
104: | | (pgm-pixels pgm))) |
105: | | |
106: | | (define (f64->pgm greys array) |
107: | | (make-pgm greys |
108: | | (Array-map generic-array-manipulators |
109: | | (lambda (g) |
110: | .02% | (max 0 (min greys (##flonum.->fixnum (FLOAT (round g)))))) |
111: | | array))) |
112: | | |
113: | | (define (pgm-concatenate-left-to-right pgm . pgms) |
114: | | |
115: | | (define (every? proc l) |
116: | | (or (null? l) |
117: | | (and (proc (car l)) |
118: | | (every? proc (cdr l))))) |
119: | | |
120: | | (let ((array (pgm-pixels pgm)) |
121: | | (arrays (map pgm-pixels pgms))) |
122: | | (let ((domain (Array-domain array)) |
123: | | (domains (map Array-domain arrays)) |
124: | | (grey-level (pgm-greys pgm)) |
125: | | (grey-levels (map pgm-greys pgms))) |
126: | | (if (and (every? (lambda (grey) |
127: | | (= grey-level grey)) |
128: | | grey-levels) |
129: | | (every? (lambda (d) |
130: | | (= (Interval-upper-bound d 0) |
131: | | (Interval-upper-bound domain 0))) |
132: | | domains)) |
133: | | (let* ((arrays (cons array arrays)) |
134: | | (domains (cons domain domains)) |
135: | | (domain-columns (map (lambda (d) |
136: | | (Interval-upper-bound d 1)) |
137: | | domains)) |
138: | | (accessors (map Array-accessor arrays))) |
139: | | (make-pgm grey-level |
140: | | (build-Array (build-Interval '#(0 0) |
141: | | (vector |
142: | | (Interval-upper-bound domain 0) |
143: | | (apply + domain-columns))) |
144: | | (lambda (i j) |
145: | | (let loop ((j j) |
146: | | (accessors accessors) |
147: | | (domain-columns domain-columns)) |
148: | | (if (< j (car domain-columns)) |
149: | | ((car accessors) i j) |
150: | | (loop (- j (car domain-columns)) |
151: | | (cdr accessors) |
152: | | (cdr domain-columns)))))))) |
153: | | (error "can't concatenate pgm files"))))) |
154: | | |
155: | | (define (pgm-concatenate-top-to-bottom pgm . pgms) |
156: | | |
157: | | (define (every? proc l) |
158: | | (or (null? l) |
159: | | (and (proc (car l)) |
160: | | (every? (cdr l))))) |
161: | | |
162: | | (let ((array (pgm-pixels pgm)) |
163: | | (arrays (map pgm-pixels pgms))) |
164: | | (let ((domain (Array-domain array)) |
165: | | (domains (map Array-domain arrays)) |
166: | | (grey-level (pgm-greys pgm)) |
167: | | (grey-levels (pgm-greys pgms))) |
168: | | (if (and (every? (lambda (grey) |
169: | | (= grey-level grey)) |
170: | | grey-levels) |
171: | | (every? (lambda (d) |
172: | | (= (Interval-upper-bound d 1) |
173: | | (Interval-upper-bound domain 1))) |
174: | | domains)) |
175: | | (let* ((arrays (cons array arrays)) |
176: | | (domains (cons domain domains)) |
177: | | (domain-rows (map (lambda (d) |
178: | | (Interval-upper-bound d 0)) |
179: | | domains)) |
180: | | (accessors (map Array-accessor arrays))) |
181: | | (make-pgm grey-level |
182: | | (build-Array (build-Interval '#(0 0) |
183: | | |
184: | | (vector (apply + domain-rows) |
185: | | (Interval-upper-bound domain 1))) |
186: | | (lambda (i j) |
187: | | (let loop ((i i) |
188: | | (accessors accessors) |
189: | | (domain-rows domain-rows)) |
190: | | (if (< i (car domain-rows)) |
191: | | ((car accessors) i j) |
192: | | (loop (- i (car domain-rows)) |
193: | | (cdr accessors) |
194: | | (cdr domain-rows)))))))) |
195: | | (error "can't concatenate pgm files"))))) |
196: | | |
197: | | (declare (generic)) |