Source file archimedes_cairo.ml
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
(** Cairo Archimedes plugin *)
module A = Archimedes
open Bigarray
module M = A.Matrix
module B : A.Backend.Capabilities =
struct
include Cairo
let name = "cairo"
type t = Cairo.context
let set_line_cap t c =
set_line_cap t (match c with
| A.Backend.BUTT -> Cairo.BUTT
| A.Backend.ROUND -> Cairo.ROUND
| A.Backend.SQUARE -> Cairo.SQUARE)
let get_line_cap t = (match get_line_cap t with
| Cairo.BUTT -> A.Backend.BUTT
| Cairo.ROUND -> A.Backend.ROUND
| Cairo.SQUARE -> A.Backend.SQUARE)
let set_line_join t j =
set_line_join t (match j with
| A.Backend.JOIN_MITER -> Cairo.JOIN_MITER
| A.Backend.JOIN_ROUND -> Cairo.JOIN_ROUND
| A.Backend.JOIN_BEVEL -> Cairo.JOIN_BEVEL)
let get_line_join t = (match get_line_join t with
| Cairo.JOIN_MITER -> A.Backend.JOIN_MITER
| Cairo.JOIN_ROUND -> A.Backend.JOIN_ROUND
| Cairo.JOIN_BEVEL -> A.Backend.JOIN_BEVEL)
let path_extents t =
let e = Cairo.Path.extents t in
{ M.x = e.Cairo.x; y = e.Cairo.y; w = e.Cairo.w; h = e.Cairo.h }
let close_path t = Cairo.Path.close t
let clear_path t = Cairo.Path.clear t
let set_dash t ofs arr = set_dash t ~ofs arr
let set_matrix t m =
let m' = { Cairo.xx = m.M.xx; xy = m.M.xy;
yx = m.M.yx; yy = m.M.yy;
x0 = m.M.x0; y0 = m.M.y0 } in
set_matrix t m'
let get_matrix t =
let m = get_matrix t in
{ M.xx = m.Cairo.xx; xy = m.Cairo.xy;
yx = m.Cairo.yx; yy = m.Cairo.yy;
x0 = m.Cairo.x0; y0 = m.Cairo.y0 }
let flipy _ = true
let set_color t c =
let r,g,b,a = A.Color.get_rgba c in
Cairo.set_source_rgba t r g b a
let arc t ~r ~a1 ~a2 =
let x,y = Cairo.Path.get_current_point t in
let x = x -. r *. cos a1
and y = y -. r *. sin a1 in
arc t x y ~r ~a1 ~a2
entity CTM -- never = { Cairo.xx = 1.; xy = 0.; yx = 0.; yy = 1.; x0 = 0.; y0 = 0. }
let show t =
Cairo.Surface.flush (get_target t)
let clip_rectangle t x y ~w ~h =
Cairo.Path.clear t;
Cairo.rectangle t x y ~w ~h;
Cairo.clip t
better error message for options *)
let make ~options width height =
let surface = match options with
| ["PDF"; fname] -> PDF.create fname width height
| ["PS"; fname] -> PS.create fname width height
| ["SVG"; fname] -> SVG.create fname ~w:width ~h:height
| ["PNG"; _] ->
Image.create Image.ARGB32 (truncate width) (truncate height)
| [] ->
Image.create Image.ARGB32 (truncate width) (truncate height)
| _ ->
let opt = String.concat "; " options in
failwith("Archimedes_cairo.make: options [" ^ opt
^ "] not understood") in
let cr = Cairo.create surface in
Cairo.set_line_cap cr Cairo.ROUND;
cr
let close ~options t =
let surface = Cairo.get_target t in
(match options with
| ["PNG"; fname] -> PNG.write surface fname;
| _ -> ());
Surface.finish surface
let stroke cr =
let m = Cairo.get_matrix cr in
Cairo.set_matrix cr id;
Cairo.stroke cr;
Cairo.set_matrix cr m
let stroke_preserve cr =
let m = Cairo.get_matrix cr in
Cairo.set_matrix cr id;
Cairo.stroke_preserve cr;
Cairo.set_matrix cr m
module P = Archimedes_internals.Path
let path_to_cairo cr = function
| P.Move_to(x, y) -> Cairo.move_to cr x y
| P.Line_to(x, y) ->
if x = x && y = y then
Cairo.line_to cr x y
| P.Curve_to(_, _, x1, y1, x2, y2, x3, y3) ->
Cairo.curve_to cr x1 y1 x2 y2 x3 y3
| P.Close(_, _) -> Cairo.Path.close cr
| P.Array(x, y, i0, i1) ->
if i0 <= i1 then
for i = i0 to i1 do
if x.(i) = x.(i) && y.(i) = y.(i) then Cairo.line_to cr x.(i) y.(i)
done
else
for i = i0 downto i1 do
if x.(i) = x.(i) && y.(i) = y.(i) then Cairo.line_to cr x.(i) y.(i)
done
| P.Fortran(x, y, i0, i1) ->
if i0 <= i1 then
for i = i0 to i1 do
if x.{i} = x.{i} && y.{i} = y.{i} then Cairo.line_to cr x.{i} y.{i}
done
else
for i = i0 downto i1 do
if x.{i} = x.{i} && y.{i} = y.{i} then Cairo.line_to cr x.{i} y.{i}
done
| P.C(x, y, i0, i1) ->
if i0 <= i1 then
for i = i0 to i1 do
if x.{i} = x.{i} && y.{i} = y.{i} then Cairo.line_to cr x.{i} y.{i}
done
else
for i = i0 downto i1 do
if x.{i} = x.{i} && y.{i} = y.{i} then Cairo.line_to cr x.{i} y.{i}
done
let stroke_path_preserve cr p =
Cairo.Path.clear cr;
P.iter p (path_to_cairo cr);
let m = Cairo.get_matrix cr in
Cairo.set_matrix cr id;
Cairo.stroke cr;
Cairo.set_matrix cr m
let fill_path_preserve cr p =
Cairo.Path.clear cr;
P.iter p (path_to_cairo cr);
Cairo.fill cr
let fill_with_color cr c =
let source = Cairo.get_source cr in
set_color cr c;
let op = Cairo.get_operator cr in
Cairo.set_operator cr Cairo.SOURCE;
Cairo.fill cr;
Cairo.set_operator cr op;
Cairo.set_source cr source
let select_font_face t slant weight family =
let slant = match slant with
| A.Backend.Upright -> Cairo.Upright
| A.Backend.Italic -> Cairo.Italic
and weight = match weight with
| A.Backend.Normal -> Cairo.Normal
| A.Backend.Bold -> Cairo.Bold in
Cairo.select_font_face t ~slant ~weight family
let show_text cr ~rotate ~x ~y pos text =
let dx, dy = user_to_device_distance cr (cos rotate) (sin rotate) in
let angle = atan2 dy dx in
Cairo.save cr;
Cairo.move_to cr x y;
Cairo.set_matrix cr id;
Cairo.rotate cr angle;
let te = Cairo.text_extents cr text in
let x0 = match pos with
| A.Backend.CC | A.Backend.CT | A.Backend.CB ->
te.x_bearing +. 0.5 *. te.width
| A.Backend.RC | A.Backend.RT | A.Backend.RB ->
te.x_bearing
| A.Backend.LC | A.Backend.LT | A.Backend.LB ->
te.x_bearing +. te.width
and y0 = match pos with
| A.Backend.CC | A.Backend.RC | A.Backend.LC ->
te.y_bearing +. 0.5 *. te.height
| A.Backend.CT | A.Backend.RT | A.Backend.LT ->
te.y_bearing +. te.height
| A.Backend.CB | A.Backend.RB | A.Backend.LB ->
te.y_bearing
in
Cairo.rel_move_to cr (-. x0) (-. y0);
Cairo.show_text cr text;
Cairo.stroke cr;
Cairo.restore cr
let text_extents t text =
Cairo.save t;
Cairo.set_matrix t id;
let te = Cairo.text_extents t text in
Cairo.restore t;
let x,y = Cairo.device_to_user_distance t te.x_bearing te.y_bearing in
let w,h = Cairo.device_to_user_distance t te.width te.height in
{ M.x = x; y = -. y; w = w; h = h }
end
let () =
let module U = A.Backend.Register(B) in ()