png.ml 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. (*
  2. * PNG File Format Library
  3. * Copyright (c)2005 Nicolas Cannasse
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program; if not, write to the Free Software
  17. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  18. *)
  19. type grey_bits =
  20. | GBits1
  21. | GBits2
  22. | GBits4
  23. | GBits8
  24. | GBits16
  25. type grey_alpha_bits =
  26. | GABits8
  27. | GABits16
  28. type true_bits =
  29. | TBits8
  30. | TBits16
  31. type index_bits =
  32. | IBits1
  33. | IBits2
  34. | IBits4
  35. | IBits8
  36. type alpha =
  37. | NoAlpha
  38. | HaveAlpha
  39. type color =
  40. | ClGreyScale of grey_bits
  41. | ClGreyAlpha of grey_alpha_bits
  42. | ClTrueColor of true_bits * alpha
  43. | ClIndexed of index_bits
  44. type header = {
  45. png_width : int;
  46. png_height : int;
  47. png_color : color;
  48. png_interlace : bool;
  49. }
  50. type chunk_id = string
  51. type chunk =
  52. | CEnd
  53. | CHeader of header
  54. | CData of string
  55. | CPalette of string
  56. | CUnknown of chunk_id * string
  57. type png = chunk list
  58. type error_msg =
  59. | Invalid_header
  60. | Invalid_file
  61. | Truncated_file
  62. | Invalid_CRC
  63. | Invalid_colors
  64. | Unsupported_colors
  65. | Invalid_datasize
  66. | Invalid_filter of int
  67. | Invalid_array
  68. exception Error of error_msg
  69. let error_msg = function
  70. | Invalid_header -> "Invalid header"
  71. | Invalid_file -> "Invalid file"
  72. | Truncated_file -> "Truncated file"
  73. | Invalid_CRC -> "Invalid CRC"
  74. | Invalid_colors -> "Invalid color model"
  75. | Unsupported_colors -> "Unsupported color model"
  76. | Invalid_datasize -> "Invalid data size"
  77. | Invalid_filter f -> "Invalid filter " ^ string_of_int f
  78. | Invalid_array -> "Invalid array"
  79. let error msg = raise (Error msg)
  80. let is_upper c = ((int_of_char c) land 32) <> 0
  81. let is_critical id = is_upper id.[0]
  82. let is_public id = is_upper id.[1]
  83. let is_reseverd id = is_upper id.[2]
  84. let is_safe_to_copy id = is_upper id.[3]
  85. let is_id_char c =
  86. (c >= '\065' && c <= '\090') || (c >= '\097' && c <= '\122')
  87. let rec header = function
  88. | [] -> error Invalid_file
  89. | CHeader h :: _ -> h
  90. | _ :: l -> header l
  91. let data f =
  92. let rec loop acc = function
  93. | [] ->
  94. (match List.rev acc with
  95. | [] -> error Invalid_file
  96. | l -> String.concat "" l)
  97. | CData s :: l -> loop (s :: acc) l
  98. | _ :: l -> loop acc l
  99. in
  100. loop [] f
  101. let color_bits = function
  102. | ClGreyScale g -> (match g with
  103. | GBits1 -> 1
  104. | GBits2 -> 2
  105. | GBits4 -> 4
  106. | GBits8 -> 8
  107. | GBits16 -> 16)
  108. | ClGreyAlpha g -> (match g with
  109. | GABits8 -> 8
  110. | GABits16 -> 16)
  111. | ClTrueColor (t,_) -> (match t with
  112. | TBits8 -> 8
  113. | TBits16 -> 16)
  114. | ClIndexed i -> (match i with
  115. | IBits1 -> 1
  116. | IBits2 -> 2
  117. | IBits4 -> 4
  118. | IBits8 -> 8)
  119. let crc_table = Array.init 256 (fun n ->
  120. let c = ref (Int32.of_int n) in
  121. for k = 0 to 7 do
  122. if Int32.logand !c 1l <> 0l then
  123. c := Int32.logxor 0xEDB88320l (Int32.shift_right_logical !c 1)
  124. else
  125. c := (Int32.shift_right_logical !c 1);
  126. done;
  127. !c)
  128. let input_crc ch =
  129. let crc = ref 0xFFFFFFFFl in
  130. let update c =
  131. let c = Int32.of_int (int_of_char c) in
  132. let k = Array.unsafe_get crc_table (Int32.to_int (Int32.logand (Int32.logxor !crc c) 0xFFl)) in
  133. crc := Int32.logxor k (Int32.shift_right_logical !crc 8)
  134. in
  135. let ch2 = IO.create_in
  136. ~read:(fun () ->
  137. let c = IO.read ch in
  138. update c;
  139. c
  140. )
  141. ~input:(fun s p l ->
  142. let l = IO.input ch s p l in
  143. for i = 0 to l - 1 do
  144. update (Bytes.get s (p+i))
  145. done;
  146. l
  147. )
  148. ~close:(fun () ->
  149. IO.close_in ch
  150. )
  151. in
  152. ch2 , (fun () -> Int32.logxor !crc 0xFFFFFFFFl)
  153. let output_crc ch =
  154. let crc = ref 0xFFFFFFFFl in
  155. let update c =
  156. let c = Int32.of_int (int_of_char c) in
  157. let k = Array.unsafe_get crc_table (Int32.to_int (Int32.logand (Int32.logxor !crc c) 0xFFl)) in
  158. crc := Int32.logxor k (Int32.shift_right_logical !crc 8)
  159. in
  160. let ch2 = IO.create_out
  161. ~write:(fun c ->
  162. IO.write ch c;
  163. update c;
  164. )
  165. ~output:(fun s p l ->
  166. let l = IO.output ch s p l in
  167. for i = 0 to l - 1 do
  168. update (Bytes.get s (p+i))
  169. done;
  170. l
  171. )
  172. ~flush:(fun () ->
  173. IO.flush ch
  174. )
  175. ~close:(fun () ->
  176. IO.close_out ch
  177. )
  178. in
  179. ch2 , (fun () -> Int32.logxor !crc 0xFFFFFFFFl)
  180. let parse_header ch =
  181. let width = IO.BigEndian.read_i32 ch in
  182. let height = IO.BigEndian.read_i32 ch in
  183. if width < 0 || height < 0 then error Invalid_header;
  184. let bits = IO.read_byte ch in
  185. let color = IO.read_byte ch in
  186. let color = (match color with
  187. | 0 -> ClGreyScale (match bits with 1 -> GBits1 | 2 -> GBits2 | 4 -> GBits4 | 8 -> GBits8 | 16 -> GBits16 | _ -> error Invalid_colors)
  188. | 2 -> ClTrueColor ((match bits with 8 -> TBits8 | 16 -> TBits16 | _ -> error Invalid_colors) , NoAlpha)
  189. | 3 -> ClIndexed (match bits with 1 -> IBits1 | 2 -> IBits2 | 4 -> IBits4 | 8 -> IBits8 | _ -> error Invalid_colors)
  190. | 4 -> ClGreyAlpha (match bits with 8 -> GABits8 | 16 -> GABits16 | _ -> error Invalid_colors)
  191. | 6 -> ClTrueColor ((match bits with 8 -> TBits8 | 16 -> TBits16 | _ -> error Invalid_colors) , HaveAlpha)
  192. | _ -> error Invalid_colors)
  193. in
  194. let compress = IO.read_byte ch in
  195. let filter = IO.read_byte ch in
  196. if compress <> 0 || filter <> 0 then error Invalid_header;
  197. let interlace = IO.read_byte ch in
  198. let interlace = (match interlace with 0 -> false | 1 -> true | _ -> error Invalid_header) in
  199. {
  200. png_width = width;
  201. png_height = height;
  202. png_color = color;
  203. png_interlace = interlace;
  204. }
  205. let parse_chunk ch =
  206. let len = IO.BigEndian.read_i32 ch in
  207. let ch2 , crc = input_crc ch in
  208. let id = IO.nread_string ch2 4 in
  209. if len < 0 || not (is_id_char id.[0]) || not (is_id_char id.[1]) || not (is_id_char id.[2]) || not (is_id_char id.[3]) then error Invalid_file;
  210. let data = IO.nread_string ch2 len in
  211. let crc_val = IO.BigEndian.read_real_i32 ch in
  212. if crc_val <> crc() then error Invalid_CRC;
  213. match id with
  214. | "IEND" -> CEnd
  215. | "IHDR" -> CHeader (parse_header (IO.input_string data))
  216. | "IDAT" -> CData data
  217. | "PLTE" -> CPalette data
  218. | _ -> CUnknown (id,data)
  219. let png_sign = "\137\080\078\071\013\010\026\010"
  220. let parse ch =
  221. let sign = (try IO.nread_string ch (String.length png_sign) with IO.No_more_input -> error Invalid_header) in
  222. if sign <> png_sign then error Invalid_header;
  223. let rec loop acc =
  224. match parse_chunk ch with
  225. | CEnd -> List.rev acc
  226. | c -> loop (c :: acc)
  227. in
  228. try
  229. loop []
  230. with
  231. | IO.No_more_input -> error Truncated_file
  232. | IO.Overflow _ -> error Invalid_file
  233. let write_chunk ch cid cdata =
  234. IO.BigEndian.write_i32 ch (String.length cdata);
  235. let ch2 , crc = output_crc ch in
  236. IO.nwrite_string ch2 cid;
  237. IO.nwrite_string ch2 cdata;
  238. IO.BigEndian.write_real_i32 ch (crc())
  239. let write_header real_ch h =
  240. let ch = IO.output_string() in
  241. IO.BigEndian.write_i32 ch h.png_width;
  242. IO.BigEndian.write_i32 ch h.png_height;
  243. IO.write_byte ch (color_bits h.png_color);
  244. IO.write_byte ch (match h.png_color with
  245. | ClGreyScale _ -> 0
  246. | ClTrueColor (_,NoAlpha) -> 2
  247. | ClIndexed _ -> 3
  248. | ClGreyAlpha _ -> 4
  249. | ClTrueColor (_,HaveAlpha) -> 6);
  250. IO.write_byte ch 0;
  251. IO.write_byte ch 0;
  252. IO.write_byte ch (if h.png_interlace then 1 else 0);
  253. let data = IO.close_out ch in
  254. write_chunk real_ch "IHDR" data
  255. let write ch png =
  256. IO.nwrite_string ch png_sign;
  257. List.iter (function
  258. | CEnd -> write_chunk ch "IEND" ""
  259. | CHeader h -> write_header ch h
  260. | CData s -> write_chunk ch "IDAT" s
  261. | CPalette s -> write_chunk ch "PLTE" s
  262. | CUnknown (id,data) -> write_chunk ch id data
  263. ) png
  264. let filter png data =
  265. let head = header png in
  266. let w = head.png_width in
  267. let h = head.png_height in
  268. match head.png_color with
  269. | ClGreyScale _
  270. | ClGreyAlpha _
  271. | ClIndexed _
  272. | ClTrueColor (TBits16,_) -> error Unsupported_colors
  273. | ClTrueColor (TBits8,alpha) ->
  274. let alpha = (match alpha with NoAlpha -> false | HaveAlpha -> true) in
  275. let buf = Bytes.create (w * h * 4) in
  276. let nbytes = if alpha then 4 else 3 in
  277. let stride = nbytes * w + 1 in
  278. if String.length data < h * stride then error Invalid_datasize;
  279. let bp = ref 0 in
  280. let get p = int_of_char (String.unsafe_get data p) in
  281. let bget p = int_of_char (Bytes.unsafe_get buf p) in
  282. let set v = Bytes.unsafe_set buf !bp (Char.unsafe_chr v); incr bp in
  283. let filters = [|
  284. (fun x y v -> v
  285. );
  286. (fun x y v ->
  287. let v2 = if x = 0 then 0 else bget (!bp - 4) in
  288. v + v2
  289. );
  290. (fun x y v ->
  291. let v2 = if y = 0 then 0 else bget (!bp - 4*w) in
  292. v + v2
  293. );
  294. (fun x y v ->
  295. let v2 = if x = 0 then 0 else bget (!bp - 4) in
  296. let v3 = if y = 0 then 0 else bget (!bp - 4*w) in
  297. v + (v2 + v3) / 2
  298. );
  299. (fun x y v ->
  300. let a = if x = 0 then 0 else bget (!bp - 4) in
  301. let b = if y = 0 then 0 else bget (!bp - 4*w) in
  302. let c = if x = 0 || y = 0 then 0 else bget (!bp - 4 - 4*w) in
  303. let p = a + b - c in
  304. let pa = abs (p - a) in
  305. let pb = abs (p - b) in
  306. let pc = abs (p - c) in
  307. let d = (if pa <= pb && pa <= pc then a else if pb <= pc then b else c) in
  308. v + d
  309. );
  310. |] in
  311. for y = 0 to h - 1 do
  312. let f = get (y * stride) in
  313. let f = (if f < 5 then filters.(f) else error (Invalid_filter f)) in
  314. for x = 0 to w - 1 do
  315. let p = x * nbytes + y * stride in
  316. if not alpha then begin
  317. set 255;
  318. for c = 1 to 3 do
  319. let v = get (p + c) in
  320. set (f x y v)
  321. done;
  322. end else begin
  323. let v = get (p + 4) in
  324. let a = f x y v in
  325. set a;
  326. for c = 1 to 3 do
  327. let v = get (p + c) in
  328. set (f x y v)
  329. done;
  330. end;
  331. done;
  332. done;
  333. Bytes.to_string buf
  334. let make ~width ~height ~pixel ~compress =
  335. let data = Bytes.create (width * height * 4 + height) in
  336. let p = ref 0 in
  337. let set v = Bytes.unsafe_set data !p (Char.unsafe_chr v); incr p in
  338. for y = 0 to height - 1 do
  339. set 0;
  340. for x = 0 to width - 1 do
  341. let c = pixel x y in
  342. let ic = Int32.to_int c in
  343. (* RGBA *)
  344. set (ic lsr 16);
  345. set (ic lsr 8);
  346. set ic;
  347. set (Int32.to_int (Int32.shift_right_logical c 24));
  348. done;
  349. done;
  350. let data = Bytes.to_string data in
  351. let data = compress data in
  352. let header = {
  353. png_width = width;
  354. png_height = height;
  355. png_color = ClTrueColor (TBits8,HaveAlpha);
  356. png_interlace = false;
  357. } in
  358. [CHeader header; CData data; CEnd]