wfc_rs/
lib.rs

1use std::ffi;
2
3use libc;
4
5#[allow(dead_code)]
6
7#[link(name="wfc")]
8extern {
9    fn wfc_init(wfc: *mut std::ffi::c_void);
10
11    fn wfc_run(wfc: *mut std::ffi::c_void, max_collapse_cnt: libc::c_int) -> libc::c_int;
12
13    fn wfc_export(wfc: *const std::ffi::c_void, filename: *const libc::c_char) -> libc::c_int;
14
15    fn wfc_destroy(wfc: *mut std::ffi::c_void);
16
17    fn wfc_img_copy(image: *const WfcImageRaw) -> *mut WfcImageRaw;
18
19    fn wfc_img_destroy(image: *mut WfcImageRaw);
20
21    fn wfc_output_image(wfc: *mut std::ffi::c_void) -> *mut WfcImageRaw;
22
23    fn wfc_img_load(filename: *const libc::c_char) -> *mut WfcImageRaw;
24
25    fn wfc_img_create(width: libc::c_int, height: libc::c_int, component_cnt: libc::c_int) -> *mut WfcImageRaw;
26
27    fn wfc_overlapping(output_width: libc::c_int,
28                       output_height: libc::c_int,
29                       image: *mut WfcImageRaw,
30                       tile_width: libc::c_int,
31                       tile_height: libc::c_int,
32                       expand_input: libc::c_int,
33                       xflip_tiles: libc::c_int,
34                       yflip_tiles: libc::c_int,
35                       rotate_tiles: libc::c_int) -> *mut std::ffi::c_void;
36}
37
38#[repr(C)]
39pub struct WfcImageRaw {
40    pub data: *mut i8,
41    pub component_cnt: libc::c_int,
42    pub width: libc::c_int,
43    pub height: libc::c_int,
44}
45
46pub struct WfcImage {
47    pub img: *mut WfcImageRaw,
48}
49
50impl WfcImage {
51    pub fn new(data: *mut i8,
52               component_cnt: libc::c_int,
53               width: libc::c_int,
54               height: libc::c_int) -> WfcImage {
55        unsafe {
56            let layout = std::alloc::Layout::new::<WfcImageRaw>();
57            let raw_img: *mut WfcImageRaw = std::alloc::alloc(layout) as *mut WfcImageRaw;
58
59            (*raw_img).data = data;
60            (*raw_img).component_cnt = component_cnt;
61            (*raw_img).width = width;
62            (*raw_img).height = height;
63
64            return WfcImage { img: raw_img };
65        }
66    }
67
68    pub fn empty() -> WfcImage {
69        return WfcImage { img: std::ptr::null_mut() };
70    }
71
72    pub fn from_vec(width: i32, height: i32, component_cnt: i32, data: Vec<u8>) -> WfcImage {
73        unsafe {
74            let image_ptr = wfc_img_create(width, height, component_cnt);
75            let length = data.len();
76            std::ptr::copy_nonoverlapping(data.as_ptr() as *mut u8, (*image_ptr).data as *mut u8, length);
77
78            return WfcImage { img: image_ptr };
79        }
80    }
81
82    pub fn from_file(filename: &str) -> Option<WfcImage> {
83        unsafe {
84            let c_filename = ffi::CString::new(filename).ok()?;
85            let image: WfcImage = WfcImage { img: wfc_img_load(c_filename.as_ptr()) };
86            return Some(image);
87        }
88    }
89
90    pub fn vec(&self) -> Vec<u8> {
91        unsafe {
92            let length = self.num_bytes();
93            let data: *mut u8 = libc::malloc(length) as *mut u8;
94            std::ptr::copy_nonoverlapping((*self.img).data as *mut u8, data, length);
95
96            std::mem::forget(data);
97
98            return Vec::from_raw_parts(data, length, length);
99        }
100    }
101
102    pub fn num_bytes(&self) -> usize {
103        unsafe {
104            return ((*self.img).width * (*self.img).height * (*self.img).component_cnt) as usize;
105        }
106    }
107
108    pub fn component_cnt(&self) -> libc::c_int {
109        unsafe {
110            return (*self.img).component_cnt;
111        }
112    }
113
114    pub fn width(&self) -> libc::c_int {
115        unsafe {
116            return(*self.img).width;
117        }
118    }
119
120    pub fn height(&self) -> libc::c_int {
121        unsafe {
122            return(*self.img).height;
123        }
124    }
125}
126
127impl Drop for WfcImage {
128    fn drop(&mut self) {
129        unsafe {
130            wfc_img_destroy(self.img as *mut WfcImageRaw);
131        }
132    }
133}
134
135/// The main Wfc structure. This structure is normally created
136/// by calling overlapping to match the underlying C function wfc_overlapping.
137///
138/// Once created, the Wfc can be used to create an image with 'run', and this
139/// image can be saved with 'export'.
140pub struct Wfc {
141    pub wfc: *mut libc::c_void,
142    pub image: WfcImage,
143}
144
145impl Wfc {
146    pub fn from_raw_parts(wfc: *mut libc::c_void, image: WfcImage) -> Wfc {
147        return Wfc { wfc, image };
148    }
149
150    pub fn overlapping(output_width: i32,
151                       output_height: i32,
152                       image: WfcImage,
153                       tile_width: i32,
154                       tile_height: i32,
155                       expand_input: bool,
156                       xflip_tiles: bool,
157                       yflip_tiles: bool,
158                       rotate_tiles: bool) -> Option<Wfc> {
159        unsafe {
160            let wfc = wfc_overlapping(output_width,
161                                      output_height,
162                                      image.img.as_mut()?,
163                                      tile_width,
164                                      tile_height,
165                                      expand_input as i32,
166                                      xflip_tiles as i32,
167                                      yflip_tiles as i32,
168                                      rotate_tiles as i32);
169
170            if wfc.is_null() {
171                return None;
172            }
173
174            return Some(Wfc::from_raw_parts(wfc, image));
175        }
176    }
177
178    pub fn run(&mut self, max_collapse_cnt: Option<i32>, seed: Option<u32>) -> Result<(), &str> {
179        unsafe {
180            let wfc_ptr = self.wfc.as_mut().ok_or("Wfc pointer invalid")?;
181            wfc_init(wfc_ptr);
182
183            // wfc sets the srand seed with time, but only uses rand in wfc_rand.
184            // If given a seed, we can apply it between wfc_init and wfc_run.
185            if let Some(seed) = seed {
186                libc::srand(seed)
187            }
188
189            let max_cnt = max_collapse_cnt.unwrap_or(-1);
190            let result: libc::c_int = wfc_run(wfc_ptr, max_cnt);
191
192            if result == 0 {
193                return Err("wfc_run returned an error!");
194            } else {
195                return Ok(());
196            }
197        }
198    }
199
200    pub fn export(&mut self, filename: &str) -> Result<(), &str> {
201        unsafe {
202            let c_filename = ffi::CString::new(filename).map_err(|_| "Filename to CString error")?;
203            let wfc_ptr = self.wfc.as_mut().ok_or("Wfc pointer invalid")?;
204            let result = wfc_export(wfc_ptr, c_filename.as_ptr());
205
206            if result == 0 {
207                return Err("wfc_export returned an error!");
208            } else {
209                return Ok(());
210            }
211        }
212    }
213
214    pub fn output_image(&mut self) -> Option<WfcImage> {
215        unsafe {
216            let img = wfc_output_image(self.wfc.as_mut()?);
217            if img.is_null() {
218                return Some(WfcImage { img: img });
219            } else {
220                return None;
221            }
222        };
223    }
224
225    /// Convenience function for extracting a copy of the input
226    ///
227    pub fn vec(&mut self) -> Vec<u8> {
228        return self.image.vec();
229    }
230}
231
232impl Drop for Wfc {
233    fn drop(&mut self) {
234        unsafe {
235            // Swap the image for an empty one. By moving the image into this stack
236            // frame, it should be cleaned up after wfc_destroy is called.
237            let _image = std::mem::replace(&mut self.image, WfcImage::empty());
238
239            if let Some(wfc_ptr) = self.wfc.as_mut() {
240                wfc_destroy(wfc_ptr);
241            }
242        }
243    }
244}
245
246#[test]
247pub fn test_overlapping() {
248    {
249        let image = WfcImage::from_file("data/cave.png").unwrap();
250        let _wfc = Wfc::overlapping(32, 32, image, 3, 3, true, true, true, true);
251    }
252    {
253        let image = WfcImage::from_file("data/cave.png").unwrap();
254        let _wfc = Wfc::overlapping(32, 32, image, 3, 3, true, true, true, true);
255    }
256    {
257        let image = WfcImage::from_file("data/cave.png").unwrap();
258        let _wfc = Wfc::overlapping(32, 32, image, 3, 3, true, true, true, true);
259    }
260}
261
262#[test]
263pub fn test_run() {
264    let image = WfcImage::from_file("data/cave.png").unwrap();
265
266    let mut wfc = Wfc::overlapping(32, 32, image, 3, 3, true, true, true, true).unwrap();
267
268    let result = wfc.run(Some(10), Some(1));
269    assert_eq!(Ok(()), result);
270}
271
272#[test]
273pub fn test_export() {
274    let image = WfcImage::from_file("data/cave.png").unwrap();
275
276    let mut wfc = Wfc::overlapping(32, 32, image, 3, 3, true, true, true, true).unwrap();
277    let result = wfc.run(Some(100), Some(1));
278    assert_eq!(Ok(()), result);
279
280    wfc.export("output.png").unwrap();
281    std::fs::remove_file("output.png").unwrap();
282}
283
284#[test]
285pub fn test_image() {
286    let image = WfcImage::from_file("data/cave.png").unwrap();
287
288    let bytes = image.vec();
289    assert_eq!(image.num_bytes(), bytes.len());
290}
291