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
open Color
open Rgb24
module type REDUCER = sig
val find_nearest : Color.rgb Color.map -> Color.rgb -> int
end
let diffusion_table =
let sum = 1.0 +. 1.0 +. 1.0 /. sqrt 2.0 in
let base = 1.0 /. sum in
[| [| 0.0 ; base |];
[| base; base /. (sqrt 2.0) |] |]
module ErrorDiffuse( R : REDUCER ) = struct
let f src colormap =
if colormap.max > 256 then
raise (Invalid_argument "Rgb24.to_index8: too large colormap");
let error_table =
Array.init 2
(fun _ ->
Array.init (src.width + 1) (fun _ -> {r = 0; g = 0; b = 0})) in
let get_error x y =
let y' = y mod 2 in error_table.(y').(x) in
let add_error x y rgb =
let y' = y mod 2 in
let rgb' = error_table.(y').(x) in
error_table.(y').(x) <- Color.plus rgb rgb' in
let next_line y =
let y' = y mod 2 in
for x = 0 to src.width do
error_table.(y').(x) <- {r=0; g=0; b=0}
done in
let id8 = Index8.create (src.width) (src.height) in
id8.Index8.colormap <- colormap;
for y = 0 to src.height - 1 do
for x = 0 to src.width -1 do
let ideal_rgb = Color.plus (unsafe_get src x y) (get_error x y) in
let c = R.find_nearest colormap ideal_rgb in
Index8.unsafe_set id8 x y c;
let new_error = Color.minus ideal_rgb colormap.map.(c) in
for ey = 0 to 1 do
for ex = 0 to 1 do
let diffuse_rgb = {
r= truncate (float new_error.r *. diffusion_table.(ex).(ey));
g= truncate (float new_error.g *. diffusion_table.(ex).(ey));
b= truncate (float new_error.b *. diffusion_table.(ex).(ey)); } in
add_error (x+ex) (y+ey) diffuse_rgb
done
done
done;
next_line y
done;
id8
end
module ErrorDiffuseIndex8 = ErrorDiffuse(Color.Rgb)
let error_diffuse = ErrorDiffuseIndex8.f