1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
open Stdlib
open ImageUtil
open Image
module ReadPPM : ReadImage = struct
let extensions = ["ppm"; "pgm"; "pbm"; "pnm"]
let (content:chunk_reader) =
let magic = ref "" in
let width = ref (-1) and height = ref (-1) in
let max_val = ref 1 in
let scanner = Scanf.Scanning.from_function (fun () -> chunk_char content) in
let rec () =
try
Scanf.bscanf scanner "#%[^\n\r]%[\t\n\r]" (fun _ _ -> () );
pass_comments ()
with _ -> ()
in
Scanf.bscanf scanner "%s%[\t\n ]" (fun mn _ -> magic := mn);
pass_comments ();
if not (List.mem !magic ["P1"; "P2"; "P3"; "P4"; "P5"; "P6"]) then
raise (Corrupted_image "Invalid magic number...");
if List.mem !magic ["P1"; "P4"] then begin
Scanf.bscanf scanner "%u%[\t\n ]" (fun w _ -> width := w);
pass_comments ();
Scanf.bscanf scanner "%u%1[\t\n ]" (fun h _ -> height := h)
end else begin
begin try
Scanf.bscanf scanner "%u%[\t\n ]" (fun w _ -> width := w);
with Stdlib.Scanf.Scan_failure _ ->
raise(Image.Corrupted_image "PPM: invalid width")
end;
pass_comments ();
begin try
Scanf.bscanf scanner "%u%[\t\n ]" (fun h _ -> height := h);
with Stdlib.Scanf.Scan_failure _ ->
raise(Image.Corrupted_image "PPM: invalid height")
end;
pass_comments ();
begin try
Scanf.bscanf scanner "%u%1[\t\n ]" (fun mv _ -> max_val := mv);
with Stdlib.Scanf.Scan_failure _ ->
raise(Image.Corrupted_image "PPM: invalid max_val")
end
end;
!magic,!width,!height,!max_val,scanner
let size ich =
let _,w,h,_,_ = read_header ich in
close_chunk_reader ich;
w, h
let parsefile (ich:chunk_reader) =
let prev_byte = ref None in
try
let magic,w,h,max_val,scanner =
(function | `Bytes 1 -> let b = ich (`Bytes 1) in
prev_byte := Some b; b
| orig -> ich orig )
|> read_header
in
let content : chunk_reader = function
| `Bytes 1 -> begin match !prev_byte with | Some x -> prev_byte:=None; x
| None -> ich (`Bytes 1) end
| orig -> ich orig
in
match magic with
| "P1" | "P2" ->
let image = create_grey ~max_val:max_val w h in
for y = 0 to h - 1 do
for x = 0 to w - 1 do
begin try
Scanf.bscanf scanner "%d%[\t\n ]" (fun v _ ->
write_grey image x y v)
with Stdlib.Scanf.Scan_failure _ ->
raise(Image.Corrupted_image "PPM: Invalid grayscale pixel data")
end
done
done;
image
| "P3" ->
let image = create_rgb ~max_val:max_val w h in
for y = 0 to h - 1 do
for x = 0 to w - 1 do
Scanf.bscanf scanner "%d%[\t\n ]%d%[\t\n ]%d%[\t\n ]"
(fun r _ g _ b _ ->
write_rgb image x y r g b)
done
done;
image
| "P4" ->
let image = create_grey ~max_val:1 w h in
for y = 0 to h - 1 do
let x = ref 0 in
let byte = ref 0 in
while !x < w do
if !x mod 8 = 0 then
byte := chunk_byte content;
let byte_pos = !x mod 8 in
let v = (!byte lsr (7 - byte_pos)) land 1 in
write_grey image !x y v;
incr x
done
done;
image
| "P5" ->
let image = create_grey ~max_val:max_val w h in
for y = 0 to h - 1 do
for x = 0 to w - 1 do
if max_val <= 255 then (
let b0 = chunk_byte content in
write_grey image x y b0)
else (
let b0 = chunk_byte content in
let b1 = chunk_byte content in
write_grey image x y ((b0 lsl 8) + b1))
done
done;
image
| "P6" ->
let image = create_rgb ~max_val:max_val w h in
for y = 0 to h - 1 do
for x = 0 to w - 1 do
if max_val <= 255 then (
let r = chunk_byte content in
let g = chunk_byte content in
let b = chunk_byte content in
write_rgb image x y r g b)
else (
let r1 = chunk_byte content in
let r0 = chunk_byte content in
let g1 = chunk_byte content in
let g0 = chunk_byte content in
let b1 = chunk_byte content in
let b0 = chunk_byte content in
let r = (r1 lsl 8) + r0 in
let g = (g1 lsl 8) + g0 in
let b = (b1 lsl 8) + b0 in
write_rgb image x y r g b)
done
done;
image
| _ ->
raise (Corrupted_image "Invalid magic number...")
with End_of_file ->
raise (Corrupted_image "Truncated file")
end
type ppm_mode = Binary | ASCII
let write_ppm (och:ImageUtil.chunk_writer) img mode =
let w = img.width and h = img.height in
(match img.pixels, mode, img.max_val with
| (RGB _ | RGBA _) , Binary, mv ->
chunk_printf och "P6\n%i %i %i\n" w h mv;
for y = 0 to h - 1 do
for x = 0 to w - 1 do
read_rgb img x y (fun r g b ->
if mv < 256
then begin
chunk_printf och "%c%c%c"
(char_of_int r) (char_of_int g) (char_of_int b)
end else begin
let r0 = char_of_int (r mod 256) in
let r1 = char_of_int (r lsr 8) in
let g0 = char_of_int (g mod 256) in
let g1 = char_of_int (g lsr 8) in
let b0 = char_of_int (b mod 256) in
let b1 = char_of_int (b lsr 8) in
chunk_printf och "%c%c%c%c%c%c" r1 r0 g1 g0 b1 b0
end)
done
done
| (RGB _ | RGBA _) , ASCII, mv ->
chunk_printf och "P3\n%i %i %i\n" w h mv;
for y = 0 to h - 1 do
for x = 0 to w - 1 do
read_rgb img x y (fun r g b ->
chunk_printf och "%i %i %i\n" r g b)
done;
done
| (Grey _ | GreyA _), Binary, 1 ->
chunk_printf och "P4\n%i %i\n" w h;
for y = 0 to h - 1 do
let byte = ref 0 in
let pos = ref 0 in
let output_bit b =
let bitmask = b lsl (7 - !pos) in
byte := !byte lor bitmask;
incr pos;
if !pos = 8 then begin
chunk_write_char och (char_of_int !byte);
byte := 0;
pos := 0;
end
in
let flush_byte () =
if !pos <> 0 then chunk_write_char och (char_of_int !byte)
in
for x = 0 to w - 1 do
read_grey img x y (fun g ->
output_bit g)
done;
flush_byte ()
done
| (Grey _ | GreyA _), ASCII, 1 ->
let = Printf.sprintf "P1\n%i %i\n" w h in
chunk_write och header;
for y = 0 to h - 1 do
for x = 0 to w - 1 do
read_grey img x y (fun g ->
chunk_printf och "%i\n" g)
done;
done
| (Grey _ | GreyA _), Binary, mv ->
let = Printf.sprintf "P5\n%i %i %i\n" w h mv in
chunk_write och header;
for y = 0 to h - 1 do
for x = 0 to w - 1 do
read_grey img x y (fun g ->
if mv < 256
then chunk_write_char och (char_of_int g)
else begin
let gl0 = char_of_int (g mod 256) in
let gl1 = char_of_int (g lsr 8) in
chunk_printf och "%c%c" gl1 gl0
end)
done;
done
| (Grey _ | GreyA _), ASCII, mv ->
let = Printf.sprintf "P2\n%i %i %i\n" w h mv in
chunk_write och header;
for y = 0 to h - 1 do
for x = 0 to w - 1 do
read_grey img x y (fun g ->
chunk_printf och "%i\n" g)
done;
done
);
close_chunk_writer och
include ReadPPM
let write cw img = write_ppm cw img Binary