1 //! C API, drop-in replacement for libgif
2
3 #![allow(non_snake_case)]
4 #![allow(non_camel_case_types)]
5 #![allow(dead_code)]
6 #![allow(missing_docs)] //FIXME
7
8 use std::cmp;
9 use std::mem;
10 use std::ptr;
11 use std::boxed;
12 use std::fs::File;
13 use std::ffi::CStr;
14 use std::str;
15 use std::slice;
16
17 use libc::{free, c_int, c_uint, c_char, c_uchar, c_void};
18
19 use reader::{Decoder, Reader, Decoded};
20 use c_api_utils::{CInterface, CFile, FnInputFile};
21
22 /// NOTE As of rust issue #954 `bool` is compatible with c_bool.
23 pub type c_bool = bool;
24
25 pub type GifPixelType = c_uchar;
26 pub type GifRowType = *mut c_uchar;
27 pub type GifByteType = c_uchar;
28 pub type GifPrefixType = c_uint;
29 pub type GifWord = c_int;
30
31 #[repr(C)]
32 pub struct GifColorType {
33 pub Red: GifByteType,
34 pub Green: GifByteType,
35 pub Blue: GifByteType
36 }
37
38 #[repr(C)]
39 pub struct ColorMapObject {
40 pub ColorCount: c_int,
41 pub BitsPerPixel: c_int,
42 pub SortFlag: c_bool,
43 /// on malloc(3) heap
44 pub Colors: *mut GifColorType // TODO USE MALLOC for this
45 }
46
47 #[repr(C)]
48 pub struct ExtensionBlock {
49 pub ByteCount: c_int,
50 /// on malloc(3) heap
51 pub Bytes: *mut GifByteType, // TODO USE MALLOC for this
52 /// The block function code
53 pub Function: c_int
54 //#define CONTINUE_EXT_FUNC_CODE 0x00 /* continuation subblock */
55 //#define COMMENT_EXT_FUNC_CODE 0xfe /* comment */
56 //#define GRAPHICS_EXT_FUNC_CODE 0xf9 /* graphics control (GIF89) */
57 //#define PLAINTEXT_EXT_FUNC_CODE 0x01 /* plaintext */
58 //#define APPLICATION_EXT_FUNC_CODE 0xff /* application block */
59 }
60
61 #[repr(C)]
62 pub struct SavedImage {
63 pub ImageDesc: GifImageDesc,
64 /// on malloc(3) heap
65 pub RasterBits: *mut GifByteType,
66 /// Count of extensions before image
67 pub ExtensionBlockCount: c_int,
68 /// Extensions before image
69 pub ExtensionBlocks: *mut ExtensionBlock
70 }
71
72 #[repr(C)]
73 pub struct GifImageDesc {
74 /// Current image dimensions. (left)
75 pub Left: GifWord,
76 /// Current image dimensions. (top)
77 pub Top: GifWord,
78 /// Current image dimensions. (width)
79 pub Width: GifWord,
80 /// Current image dimensions. (height)
81 pub Height: GifWord,
82 /// Sequential/Interlaced lines.
83 pub Interlace: c_bool,
84 /// The local color map
85 pub ColorMap: *mut ColorMapObject
86 }
87
88 #[repr(C)]
89 pub struct GifFileType {
90 /// Size of virtual canvas (width)
91 pub SWidth: GifWord,
92 /// Size of virtual canvas (height)
93 pub SHeight: GifWord,
94 /// How many colors can we generate?
95 pub SColorResolution: GifWord,
96 /// Background color for virtual canvas
97 pub SBackGroundColor: GifWord,
98 /// Used to compute pixel aspect ratio
99 pub AspectByte: GifByteType,
100 /// Global colormap, NULL if nonexistent.
101 pub SColorMap: *mut ColorMapObject,
102 /// Number of current image (both APIs)
103 pub ImageCount: c_int,
104 /// Current image (low-level API)
105 pub Image: GifImageDesc,
106 /// Image sequence (high-level API)
107 pub SavedImages: *mut SavedImage,
108 /// Count extensions past last image
109 pub ExtensionBlockCount: c_int,
110 /// Extensions past last image
111 pub ExtensionBlocks: *mut ExtensionBlock,
112 /// Last error condition reported
113 pub Error: c_int,
114 /// hook to attach user data (TVT)
115 pub UserData: *mut c_void,
116 /// Don't mess with this!
117 pub Private: *mut c_void,
118 }
119
120 #[repr(C)]
121 pub enum GifRecordType {
122 UNDEFINED_RECORD_TYPE,
123 SCREEN_DESC_RECORD_TYPE,
124 IMAGE_DESC_RECORD_TYPE, /* Begin with ',' */
125 EXTENSION_RECORD_TYPE, /* Begin with '!' */
126 TERMINATE_RECORD_TYPE /* Begin with ';' */
127 }
128
129 /// Input callback for DGifOpen. Returns `c_int` bytes input the buffer
130 /// and returns the number of bytes read.
131 pub type InputFunc = extern "C" fn(*mut GifFileType, *mut GifByteType, c_int) -> c_int;
132
133 const D_GIF_SUCCEEDED : c_int = 0;
134 const D_GIF_ERR_OPEN_FAILED : c_int = 101; /* And DGif possible errors. */
135 const D_GIF_ERR_READ_FAILED : c_int = 102;
136 const D_GIF_ERR_NOT_GIF_FILE : c_int = 103;
137 const D_GIF_ERR_NO_SCRN_DSCR : c_int = 104;
138 const D_GIF_ERR_NO_IMAG_DSCR : c_int = 105;
139 const D_GIF_ERR_NO_COLOR_MAP : c_int = 106;
140 const D_GIF_ERR_WRONG_RECORD : c_int = 107;
141 const D_GIF_ERR_DATA_TOO_BIG : c_int = 108;
142 const D_GIF_ERR_NOT_ENOUGH_MEM: c_int = 109;
143 const D_GIF_ERR_CLOSE_FAILED : c_int = 110;
144 const D_GIF_ERR_NOT_READABLE : c_int = 111;
145 const D_GIF_ERR_IMAGE_DEFECT : c_int = 112;
146 const D_GIF_ERR_EOF_TOO_SOON : c_int = 113;
147
148 const GIF_ERROR: c_int = 0;
149 const GIF_OK : c_int = 1;
150
151 macro_rules! try_capi {
152 ($val:expr, $err:expr, $code:expr, $retval:expr) => (
153 match $val {
154 Ok(val) => val,
155 Err(_) => {
156 if $err != ptr::null_mut() {
157 *$err = $code
158 }
159 return $retval
160 }
161 }
162 );
163 ($val:expr) => (
164 match $val {
165 Ok(val) => val,
166 Err(_) => return GIF_ERROR
167 }
168 );
169 }
170
171 macro_rules! try_get_decoder {
172 ($this:expr) => (
173 if $this != ptr::null_mut() {
174 let decoder: &mut &mut CInterface = mem::transmute((*$this).Private);
175 decoder
176 } else {
177 return GIF_ERROR
178 }
179 );
180 }
181
182 #[no_mangle] pub unsafe extern "C"
DGifOpenFileName(gif_file_name: *const c_char, err: *mut c_int) -> *mut GifFileType183 fn DGifOpenFileName(gif_file_name: *const c_char, err: *mut c_int) -> *mut GifFileType {
184 let file = try_capi!(
185 File::open(try_capi!(
186 str::from_utf8(CStr::from_ptr(gif_file_name).to_bytes()),
187 err, D_GIF_ERR_OPEN_FAILED, ptr::null_mut()
188 )),
189 err, D_GIF_ERR_OPEN_FAILED, ptr::null_mut()
190 );
191 let mut decoder = try_capi!(
192 Decoder::new(file).read_info(),
193 err, D_GIF_ERR_READ_FAILED, ptr::null_mut()
194 ).into_c_interface();
195 let this: *mut GifFileType = Box::into_raw(Box::new(mem::zeroed()));
196 decoder.read_screen_desc(&mut *this);
197 let decoder = Box::into_raw(Box::new(Box::into_raw(decoder)));
198 (*this).Private = mem::transmute(decoder);
199 this
200 }
201
202 #[no_mangle] pub unsafe extern "C"
DGifOpenFileHandle(fp: c_int, err: *mut c_int) -> *mut GifFileType203 fn DGifOpenFileHandle(fp: c_int, err: *mut c_int) -> *mut GifFileType {
204 let mut decoder = try_capi!(
205 Decoder::new(CFile::new(fp)).read_info(),
206 err, D_GIF_ERR_READ_FAILED, ptr::null_mut()
207 ).into_c_interface();
208 let this: *mut GifFileType = Box::into_raw(Box::new(mem::zeroed()));
209 decoder.read_screen_desc(&mut *this);
210 let decoder = Box::into_raw(Box::new(Box::into_raw(decoder)));
211 (*this).Private = mem::transmute(decoder);
212 this
213 }
214
215 /*
216 #[no_mangle] pub unsafe extern "C"
217 fn DGifSlurp(this: *mut GifFileType) -> c_int {
218 match try_get_decoder!(this).read_to_end(mem::transmute(this)) {
219 Ok(()) => GIF_OK,
220 Err(_) => GIF_ERROR
221 }
222 }
223 */
224 #[no_mangle] pub unsafe extern "C"
DGifOpen(user_data: *mut c_void, read_fn: InputFunc, err: *mut c_int) -> *mut GifFileType225 fn DGifOpen(user_data: *mut c_void, read_fn: InputFunc, err: *mut c_int) -> *mut GifFileType {
226 let this: *mut GifFileType = Box::into_raw(Box::new(mem::zeroed()));
227 (*this).UserData = user_data;
228 let decoder = try_capi!(
229 Decoder::new(FnInputFile::new(read_fn, this)).read_info(),
230 err, D_GIF_ERR_READ_FAILED, {
231 // TODO: check if it is ok and expected to free GifFileType
232 // This is unclear since the API exposes the whole struct to the read
233 // function and not only the user data
234 let _: Box<GifFileType> = Box::from_raw(this);
235 ptr::null_mut()
236 }
237 ).into_c_interface();
238 let decoder = Box::into_raw(Box::new(Box::into_raw(decoder)));
239 (*this).Private = mem::transmute(decoder);
240 this
241 }
242
243 /// Closes the file and also frees all data structures.
244 #[no_mangle] pub unsafe extern "C"
DGifCloseFile(this: *mut GifFileType, _: *mut c_int) -> c_int245 fn DGifCloseFile(this: *mut GifFileType, _: *mut c_int)
246 -> c_int {
247 if this != ptr::null_mut() {
248 let this: Box<GifFileType> = Box::from_raw(this);
249 let _: Box<Box<CInterface>> = mem::transmute(this.Private);
250 for image in slice::from_raw_parts_mut(this.SavedImages, this.ImageCount as usize) {
251 free(mem::transmute(image.RasterBits));
252 if image.ImageDesc.ColorMap != ptr::null_mut() {
253 free(mem::transmute((*image.ImageDesc.ColorMap).Colors))
254 }
255 free(mem::transmute(image.ImageDesc.ColorMap));
256 if image.ExtensionBlockCount != 0 {
257 GifFreeExtensions(&mut image.ExtensionBlockCount, &mut image.ExtensionBlocks)
258 }
259 }
260 free(mem::transmute(this.SavedImages));
261 }
262 GIF_OK
263 }
264
265 // legacy but needed API
266 #[no_mangle] pub unsafe extern "C"
DGifGetScreenDesc(_: *mut GifFileType) -> c_int267 fn DGifGetScreenDesc(_: *mut GifFileType) -> c_int {
268 GIF_OK
269 }
270 /*
271 #[no_mangle] pub unsafe extern "C"
272 fn DGifGetRecordType(this: *mut GifFileType, record_type: *mut GifRecordType) -> c_int {
273 use common::Block::*;
274 use self::GifRecordType::*;
275 *record_type = match try_capi!(try_get_decoder!(this).next_record_type()) {
276 Image => IMAGE_DESC_RECORD_TYPE,
277 Extension => EXTENSION_RECORD_TYPE,
278 Trailer => TERMINATE_RECORD_TYPE
279 };
280 GIF_OK
281 }
282 */
283 #[no_mangle] pub unsafe extern "C"
DGifGetImageDesc(this: *mut GifFileType) -> c_int284 fn DGifGetImageDesc(this: *mut GifFileType) -> c_int {
285 match try_get_decoder!(this).current_image_buffer() {
286 Ok(_) => GIF_OK,
287 Err(_) => GIF_ERROR
288 }
289 }
290
291 #[no_mangle] pub unsafe extern "C"
DGifGetLine(this: *mut GifFileType, line: *mut GifPixelType, len: c_int) -> c_int292 fn DGifGetLine(this: *mut GifFileType, line: *mut GifPixelType, len: c_int) -> c_int {
293 let (buffer, offset) = try_capi!(try_get_decoder!(this).current_image_buffer());
294 let buffer = &buffer[*offset..];
295 let len = cmp::min(buffer.len(), len as usize);
296 *offset = *offset + len;
297 let line = slice::from_raw_parts_mut(line, len);
298 line.copy_from_slice(&buffer[..len]);
299 GIF_OK
300 }
301 //int DGifGetPixel(GifFileType *GifFile, GifPixelType GifPixel);
302 //int DGifGetComment(GifFi leType *GifFile, char *GifComment);
303
304 /// Returns the type of the extension and the first extension sub-block `(size, data...)`
305 #[no_mangle] pub unsafe extern "C"
DGifGetExtension(this: *mut GifFileType, ext_type: *mut c_int, ext_block: *mut *const GifByteType) -> c_int306 fn DGifGetExtension(this: *mut GifFileType, ext_type: *mut c_int, ext_block: *mut *const GifByteType) -> c_int {
307 use common::Block::*;
308 let decoder = try_get_decoder!(this);
309 match try_capi!(decoder.next_record_type()) {
310 Image | Trailer => {
311 if ext_block != ptr::null_mut() {
312 *ext_block = ptr::null_mut();
313 }
314 if ext_type != ptr::null_mut() {
315 *ext_type = 0;
316 }
317 }
318 Extension => {
319 match try_capi!(decoder.decode_next()) {
320 Some(Decoded::SubBlockFinished(type_, data))
321 | Some(Decoded::BlockFinished(type_, data)) => {
322 if ext_block != ptr::null_mut() {
323 *ext_block = data.as_ptr();
324 }
325 if ext_type != ptr::null_mut() {
326 *ext_type = type_ as c_int;
327 }
328 }
329 _ => return GIF_ERROR
330 }
331 }
332 }
333 GIF_OK
334 }
335
336 /// Returns the next extension sub-block `(size, data...)`
337 #[no_mangle] pub unsafe extern "C"
DGifGetExtensionNext(this: *mut GifFileType, ext_block: *mut *const GifByteType) -> c_int338 fn DGifGetExtensionNext(this: *mut GifFileType, ext_block: *mut *const GifByteType) -> c_int {
339 // TODO extract next sub block
340 let mut decoder = try_get_decoder!(this);
341 if decoder.last_ext().2 {
342 if ext_block != ptr::null_mut() {
343 *ext_block = ptr::null_mut();
344 }
345 GIF_OK
346 } else {
347 match try_capi!(decoder.decode_next()) {
348 Some(Decoded::SubBlockFinished(_, data))
349 | Some(Decoded::BlockFinished(_, data)) => {
350 if ext_block != ptr::null_mut() {
351 *ext_block = data.as_ptr();
352 }
353 GIF_OK
354 }
355 _ => GIF_ERROR
356 }
357 }
358 }
359 /*
360 /// This function reallocs `ext_blocks` and copies `data`
361 #[no_mangle] pub unsafe extern "C"
362 fn GifAddExtensionBlock(block_count: *mut c_int, ext_blocks: *mut *const ExtensionBlock,
363 ext_type: c_int, len: c_uint, data: *const c_uchar) -> c_int {
364 GIF_OK
365 }
366 */
367 #[no_mangle] pub unsafe extern "C"
GifFreeExtensions(block_count: *mut c_int, ext_blocks: *mut *mut ExtensionBlock)368 fn GifFreeExtensions(block_count: *mut c_int, ext_blocks: *mut *mut ExtensionBlock) {
369 if ext_blocks == ptr::null_mut() || block_count == ptr::null_mut() {
370 return
371 }
372 for i in 0..(*block_count) as isize {
373 let block = (*ext_blocks).offset(i);
374 free(mem::transmute((*block).Bytes));
375 }
376 free(mem::transmute(ext_blocks));
377 *ext_blocks = ptr::null_mut();
378 *block_count = 0;
379 }
380