gif-0.10.2/.gitignore010064400017500001750000000000521347447342200125570ustar0000000000000000target examples *.sublime-* .* Cargo.lock gif-0.10.2/.travis.yml010064400017500001750000000001021347447342200126740ustar0000000000000000language: rust rust: - 1.24.1 - nightly script: cargo test -v gif-0.10.2/Cargo.toml.orig010064400017500001750000000011051347447342200134560ustar0000000000000000[package] name = "gif" license = "MIT/Apache-2.0" version = "0.10.2" description = "GIF de- and encoder" authors = ["nwin "] readme = "README.md" homepage = "https://github.com/image-rs/image-gif" repository = "https://github.com/image-rs/image-gif" documentation = "https://docs.rs/gif" exclude = [ "tests/*", "gif-afl/*", ] [dependencies] lzw = "0.10" color_quant = "1.0" [dev-dependencies] glob = "0.3" [features] default = ["raii_no_panic"] raii_no_panic = [] c_api = ["libc"] [dependencies.libc] version = "0.2.1" optional = true gif-0.10.2/Cargo.toml0000644000000021510000000000000077150ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g. crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] name = "gif" version = "0.10.2" authors = ["nwin "] exclude = ["tests/*", "gif-afl/*"] description = "GIF de- and encoder" homepage = "https://github.com/image-rs/image-gif" documentation = "https://docs.rs/gif" readme = "README.md" license = "MIT/Apache-2.0" repository = "https://github.com/image-rs/image-gif" [dependencies.color_quant] version = "1.0" [dependencies.libc] version = "0.2.1" optional = true [dependencies.lzw] version = "0.10" [dev-dependencies.glob] version = "0.3" [features] c_api = ["libc"] default = ["raii_no_panic"] raii_no_panic = [] gif-0.10.2/Cargo.toml.orig0000644000000021520000000000000106550ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] name = "gif" version = "0.10.2" authors = ["nwin "] exclude = ["tests/*", "gif-afl/*"] description = "GIF de- and encoder" homepage = "https://github.com/image-rs/image-gif" documentation = "https://docs.rs/gif" readme = "README.md" license = "MIT/Apache-2.0" repository = "https://github.com/image-rs/image-gif" [dependencies.color_quant] version = "1.0" [dependencies.libc] version = "0.2.1" optional = true [dependencies.lzw] version = "0.10" [dev-dependencies.glob] version = "0.3" [features] c_api = ["libc"] default = ["raii_no_panic"] raii_no_panic = [] gif-0.10.2/LICENSE-APACHE010064400017500001750000000251361347447342200125250ustar0000000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.gif-0.10.2/LICENSE-MIT010064400017500001750000000020571347447342200122320ustar0000000000000000The MIT License (MIT) Copyright (c) 2015 nwin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. gif-0.10.2/README.md010064400017500001750000000051361347447342200120560ustar0000000000000000# GIF en- and decoding library [![Build Status](https://travis-ci.org/image-rs/image-gif.svg?branch=master)](https://travis-ci.org/image-rs/image-gif) GIF en- and decoder written in Rust ([API Documentation](https://docs.rs/gif/)). # GIF encoding and decoding library This library provides all functions necessary to de- and encode GIF files. ## High level interface The high level interface consists of the two types [`Encoder`](https://docs.rs/gif/0.10.1/gif/struct.Encoder.html) and [`Decoder`](https://docs.rs/gif/0.10.1/gif/struct.Decoder.html). They as builders for the actual en- and decoders and can be used to set various options beforehand. ### Decoding GIF files ```rust // Open the file use std::fs::File; use gif::SetParameter; let mut decoder = gif::Decoder::new(File::open("tests/samples/sample_1.gif").unwrap()); // Configure the decoder such that it will expand the image to RGBA. decoder.set(gif::ColorOutput::RGBA); // Read the file header let mut decoder = decoder.read_info().unwrap(); while let Some(frame) = decoder.read_next_frame().unwrap() { // Process every frame } ``` ### Encoding GIF files The encoder can be used so save simple computer generated images: ```rust use gif::{Frame, Encoder, Repeat, SetParameter}; use std::fs::File; use std::borrow::Cow; let color_map = &[0xFF, 0xFF, 0xFF, 0, 0, 0]; let (width, height) = (6, 6); let beacon_states = [[ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, ], [ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, ]]; let mut image = File::create("target/beacon.gif").unwrap(); let mut encoder = Encoder::new(&mut image, width, height, color_map).unwrap(); encoder.set(Repeat::Infinite).unwrap(); for state in &beacon_states { let mut frame = Frame::default(); frame.width = width; frame.height = height; frame.buffer = Cow::Borrowed(&*state); encoder.write_frame(&frame).unwrap(); } ``` [`Frame::from_*`](https://docs.rs/gif/0.10.1/gif/struct.Frame.html) can be used to convert a true color image to a paletted image with a maximum of 256 colors: ```rust use std::fs::File; // Get pixel data from some source let mut pixels: Vec = vec![0; 30_000]; // Create frame from data let frame = gif::Frame::from_rgb(100, 100, &mut *pixels); // Create encoder let mut image = File::create("target/indexed_color.gif").unwrap(); let mut encoder = gif::Encoder::new(&mut image, frame.width, frame.height, &[]).unwrap(); // Write frame to file encoder.write_frame(&frame).unwrap(); ``` gif-0.10.2/src/c_api.rs010064400017500001750000000301311347447342200130000ustar0000000000000000//! C API, drop-in replacement for libgif #![allow(non_snake_case)] #![allow(non_camel_case_types)] #![allow(dead_code)] use std::cmp; use std::mem; use std::ptr; use std::boxed; use std::fs::File; use std::ffi::CStr; use std::str; use std::slice; use libc::{free, c_int, c_uint, c_char, c_uchar, c_void}; use reader::{Decoder, Reader, Decoded}; use c_api_utils::{CInterface, CFile, FnInputFile}; use util; /// NOTE As of rust issue #954 `bool` is compatible with c_bool. pub type c_bool = bool; pub type GifPixelType = c_uchar; pub type GifRowType = *mut c_uchar; pub type GifByteType = c_uchar; pub type GifPrefixType = c_uint; pub type GifWord = c_int; #[repr(C)] pub struct GifColorType { pub Red: GifByteType, pub Green: GifByteType, pub Blue: GifByteType } #[repr(C)] pub struct ColorMapObject { pub ColorCount: c_int, pub BitsPerPixel: c_int, pub SortFlag: c_bool, /// on malloc(3) heap pub Colors: *mut GifColorType // TODO USE MALLOC for this } #[repr(C)] pub struct ExtensionBlock { pub ByteCount: c_int, /// on malloc(3) heap pub Bytes: *mut GifByteType, // TODO USE MALLOC for this /// The block function code pub Function: c_int //#define CONTINUE_EXT_FUNC_CODE 0x00 /* continuation subblock */ //#define COMMENT_EXT_FUNC_CODE 0xfe /* comment */ //#define GRAPHICS_EXT_FUNC_CODE 0xf9 /* graphics control (GIF89) */ //#define PLAINTEXT_EXT_FUNC_CODE 0x01 /* plaintext */ //#define APPLICATION_EXT_FUNC_CODE 0xff /* application block */ } #[repr(C)] pub struct SavedImage { pub ImageDesc: GifImageDesc, /// on malloc(3) heap pub RasterBits: *mut GifByteType, /// Count of extensions before image pub ExtensionBlockCount: c_int, /// Extensions before image pub ExtensionBlocks: *mut ExtensionBlock } #[repr(C)] pub struct GifImageDesc { /// Current image dimensions. (left) pub Left: GifWord, /// Current image dimensions. (top) pub Top: GifWord, /// Current image dimensions. (width) pub Width: GifWord, /// Current image dimensions. (height) pub Height: GifWord, /// Sequential/Interlaced lines. pub Interlace: c_bool, /// The local color map pub ColorMap: *mut ColorMapObject } #[repr(C)] pub struct GifFileType { /// Size of virtual canvas (width) pub SWidth: GifWord, /// Size of virtual canvas (height) pub SHeight: GifWord, /// How many colors can we generate? pub SColorResolution: GifWord, /// Background color for virtual canvas pub SBackGroundColor: GifWord, /// Used to compute pixel aspect ratio pub AspectByte: GifByteType, /// Global colormap, NULL if nonexistent. pub SColorMap: *mut ColorMapObject, /// Number of current image (both APIs) pub ImageCount: c_int, /// Current image (low-level API) pub Image: GifImageDesc, /// Image sequence (high-level API) pub SavedImages: *mut SavedImage, /// Count extensions past last image pub ExtensionBlockCount: c_int, /// Extensions past last image pub ExtensionBlocks: *mut ExtensionBlock, /// Last error condition reported pub Error: c_int, /// hook to attach user data (TVT) pub UserData: *mut c_void, /// Don't mess with this! pub Private: *mut c_void, } #[repr(C)] pub enum GifRecordType { UNDEFINED_RECORD_TYPE, SCREEN_DESC_RECORD_TYPE, IMAGE_DESC_RECORD_TYPE, /* Begin with ',' */ EXTENSION_RECORD_TYPE, /* Begin with '!' */ TERMINATE_RECORD_TYPE /* Begin with ';' */ } /// Input callback for DGifOpen. Returns `c_int` bytes input the buffer /// and returns the number of bytes read. pub type InputFunc = extern "C" fn(*mut GifFileType, *mut GifByteType, c_int) -> c_int; const D_GIF_SUCCEEDED : c_int = 0; const D_GIF_ERR_OPEN_FAILED : c_int = 101; /* And DGif possible errors. */ const D_GIF_ERR_READ_FAILED : c_int = 102; const D_GIF_ERR_NOT_GIF_FILE : c_int = 103; const D_GIF_ERR_NO_SCRN_DSCR : c_int = 104; const D_GIF_ERR_NO_IMAG_DSCR : c_int = 105; const D_GIF_ERR_NO_COLOR_MAP : c_int = 106; const D_GIF_ERR_WRONG_RECORD : c_int = 107; const D_GIF_ERR_DATA_TOO_BIG : c_int = 108; const D_GIF_ERR_NOT_ENOUGH_MEM: c_int = 109; const D_GIF_ERR_CLOSE_FAILED : c_int = 110; const D_GIF_ERR_NOT_READABLE : c_int = 111; const D_GIF_ERR_IMAGE_DEFECT : c_int = 112; const D_GIF_ERR_EOF_TOO_SOON : c_int = 113; const GIF_ERROR: c_int = 0; const GIF_OK : c_int = 1; macro_rules! try_capi { ($val:expr, $err:expr, $code:expr, $retval:expr) => ( match $val { Ok(val) => val, Err(_) => { if $err != ptr::null_mut() { *$err = $code } return $retval } } ); ($val:expr) => ( match $val { Ok(val) => val, Err(_) => return GIF_ERROR } ); } macro_rules! try_get_decoder { ($this:expr) => ( if $this != ptr::null_mut() { let decoder: &mut &mut CInterface = mem::transmute((*$this).Private); decoder } else { return GIF_ERROR } ); } #[no_mangle] pub unsafe extern "C" fn DGifOpenFileName(gif_file_name: *const c_char, err: *mut c_int) -> *mut GifFileType { let file = try_capi!( File::open(try_capi!( str::from_utf8(CStr::from_ptr(gif_file_name).to_bytes()), err, D_GIF_ERR_OPEN_FAILED, ptr::null_mut() )), err, D_GIF_ERR_OPEN_FAILED, ptr::null_mut() ); let mut decoder = try_capi!( Decoder::new(file).read_info(), err, D_GIF_ERR_READ_FAILED, ptr::null_mut() ).into_c_interface(); let this: *mut GifFileType = Box::into_raw(Box::new(mem::zeroed())); decoder.read_screen_desc(&mut *this); let decoder = Box::into_raw(Box::new(Box::into_raw(decoder))); (*this).Private = mem::transmute(decoder); this } #[no_mangle] pub unsafe extern "C" fn DGifOpenFileHandle(fp: c_int, err: *mut c_int) -> *mut GifFileType { let mut decoder = try_capi!( Decoder::new(CFile::new(fp)).read_info(), err, D_GIF_ERR_READ_FAILED, ptr::null_mut() ).into_c_interface(); let this: *mut GifFileType = Box::into_raw(Box::new(mem::zeroed())); decoder.read_screen_desc(&mut *this); let decoder = Box::into_raw(Box::new(Box::into_raw(decoder))); (*this).Private = mem::transmute(decoder); this } /* #[no_mangle] pub unsafe extern "C" fn DGifSlurp(this: *mut GifFileType) -> c_int { match try_get_decoder!(this).read_to_end(mem::transmute(this)) { Ok(()) => GIF_OK, Err(_) => GIF_ERROR } } */ #[no_mangle] pub unsafe extern "C" fn DGifOpen(user_data: *mut c_void, read_fn: InputFunc, err: *mut c_int) -> *mut GifFileType { let this: *mut GifFileType = Box::into_raw(Box::new(mem::zeroed())); (*this).UserData = user_data; let decoder = try_capi!( Decoder::new(FnInputFile::new(read_fn, this)).read_info(), err, D_GIF_ERR_READ_FAILED, { // TODO: check if it is ok and expected to free GifFileType // This is unclear since the API exposes the whole struct to the read // function and not only the user data let _: Box = Box::from_raw(this); ptr::null_mut() } ).into_c_interface(); let decoder = Box::into_raw(Box::new(Box::into_raw(decoder))); (*this).Private = mem::transmute(decoder); this } /// Closes the file and also frees all data structures. #[no_mangle] pub unsafe extern "C" fn DGifCloseFile(this: *mut GifFileType, _: *mut c_int) -> c_int { if this != ptr::null_mut() { let this: Box = Box::from_raw(this); let _: Box> = mem::transmute(this.Private); for image in slice::from_raw_parts_mut(this.SavedImages, this.ImageCount as usize) { free(mem::transmute(image.RasterBits)); if image.ImageDesc.ColorMap != ptr::null_mut() { free(mem::transmute((*image.ImageDesc.ColorMap).Colors)) } free(mem::transmute(image.ImageDesc.ColorMap)); if image.ExtensionBlockCount != 0 { GifFreeExtensions(&mut image.ExtensionBlockCount, &mut image.ExtensionBlocks) } } free(mem::transmute(this.SavedImages)); } GIF_OK } // legacy but needed API #[no_mangle] pub unsafe extern "C" fn DGifGetScreenDesc(_: *mut GifFileType) -> c_int { GIF_OK } /* #[no_mangle] pub unsafe extern "C" fn DGifGetRecordType(this: *mut GifFileType, record_type: *mut GifRecordType) -> c_int { use common::Block::*; use self::GifRecordType::*; *record_type = match try_capi!(try_get_decoder!(this).next_record_type()) { Image => IMAGE_DESC_RECORD_TYPE, Extension => EXTENSION_RECORD_TYPE, Trailer => TERMINATE_RECORD_TYPE }; GIF_OK } */ #[no_mangle] pub unsafe extern "C" fn DGifGetImageDesc(this: *mut GifFileType) -> c_int { match try_get_decoder!(this).current_image_buffer() { Ok(_) => GIF_OK, Err(_) => GIF_ERROR } } #[no_mangle] pub unsafe extern "C" fn DGifGetLine(this: *mut GifFileType, line: *mut GifPixelType, len: c_int) -> c_int { let (buffer, offset) = try_capi!(try_get_decoder!(this).current_image_buffer()); let buffer = &buffer[*offset..]; let len = cmp::min(buffer.len(), len as usize); *offset = *offset + len; let line = slice::from_raw_parts_mut(line, len); util::copy_memory(buffer, line); GIF_OK } //int DGifGetPixel(GifFileType *GifFile, GifPixelType GifPixel); //int DGifGetComment(GifFi leType *GifFile, char *GifComment); /// Returns the type of the extension and the first extension sub-block `(size, data...)` #[no_mangle] pub unsafe extern "C" fn DGifGetExtension(this: *mut GifFileType, ext_type: *mut c_int, ext_block: *mut *const GifByteType) -> c_int { use common::Block::*; let decoder = try_get_decoder!(this); match try_capi!(decoder.next_record_type()) { Image | Trailer => { if ext_block != ptr::null_mut() { *ext_block = ptr::null_mut(); } if ext_type != ptr::null_mut() { *ext_type = 0; } } Extension => { match try_capi!(decoder.decode_next()) { Some(Decoded::SubBlockFinished(type_, data)) | Some(Decoded::BlockFinished(type_, data)) => { if ext_block != ptr::null_mut() { *ext_block = data.as_ptr(); } if ext_type != ptr::null_mut() { *ext_type = type_ as c_int; } } _ => return GIF_ERROR } } } GIF_OK } /// Returns the next extension sub-block `(size, data...)` #[no_mangle] pub unsafe extern "C" fn DGifGetExtensionNext(this: *mut GifFileType, ext_block: *mut *const GifByteType) -> c_int { // TODO extract next sub block let mut decoder = try_get_decoder!(this); if decoder.last_ext().2 { if ext_block != ptr::null_mut() { *ext_block = ptr::null_mut(); } GIF_OK } else { match try_capi!(decoder.decode_next()) { Some(Decoded::SubBlockFinished(_, data)) | Some(Decoded::BlockFinished(_, data)) => { if ext_block != ptr::null_mut() { *ext_block = data.as_ptr(); } GIF_OK } _ => GIF_ERROR } } } /* /// This function reallocs `ext_blocks` and copies `data` #[no_mangle] pub unsafe extern "C" fn GifAddExtensionBlock(block_count: *mut c_int, ext_blocks: *mut *const ExtensionBlock, ext_type: c_int, len: c_uint, data: *const c_uchar) -> c_int { GIF_OK } */ #[no_mangle] pub unsafe extern "C" fn GifFreeExtensions(block_count: *mut c_int, ext_blocks: *mut *mut ExtensionBlock) { if ext_blocks == ptr::null_mut() || block_count == ptr::null_mut() { return } for i in 0..(*block_count) as isize { let block = (*ext_blocks).offset(i); free(mem::transmute((*block).Bytes)); } free(mem::transmute(ext_blocks)); *ext_blocks = ptr::null_mut(); *block_count = 0; } gif-0.10.2/src/c_api_utils.rs010064400017500001750000000066071347447342200142330ustar0000000000000000use std::io::{self, Read}; use std::mem; use std::ops; use std::slice; use libc::{malloc, size_t, c_int, read, close}; use common::Block; use reader::{Decoded, DecodingError, PLTE_CHANNELS}; use c_api::{GifFileType, SavedImage, ColorMapObject, GifColorType, c_bool, InputFunc }; use util; pub trait CInterface { fn read_screen_desc(&mut self, &mut GifFileType); fn current_image_buffer(&mut self) -> Result<(&[u8], &mut usize), DecodingError>; //fn seek_to(&mut self, position: Progress) -> Result<(), DecodingError>; fn last_ext(&self) -> (u8, &[u8], bool); fn next_record_type(&mut self) -> Result; fn decode_next(&mut self) -> Result, DecodingError>; //unsafe fn read_to_end(&mut self, &mut GifFileType) -> Result<(), DecodingError>; } pub unsafe fn saved_images_new(count: usize) -> *mut SavedImage { mem::transmute::<_, *mut SavedImage>(malloc( (mem::size_of::() * count) as size_t )) } pub unsafe fn copy_data(buf: &[u8]) -> *mut u8 { let data = mem::transmute::<_, *mut u8>(malloc( (mem::size_of::() * buf.len()) as size_t )); util::copy_memory(buf, slice::from_raw_parts_mut(data, buf.len())); //for (i, &b) in buf.iter().enumerate() { // *data.offset(i as isize) = b //} data } pub unsafe fn copy_colormap(map: &Option>) -> *mut ColorMapObject { let map: &[u8] = match *map { Some(ref map) => &*map, None => &[] }; let new_map = mem::transmute::<_, *mut ColorMapObject>(malloc(mem::size_of::() as size_t)); (*new_map).ColorCount = (map.len()/PLTE_CHANNELS) as c_int; (*new_map).BitsPerPixel = 8; (*new_map).SortFlag = false; let colors = mem::transmute::<_, *mut GifColorType>(malloc( (mem::size_of::() * (*new_map).ColorCount as usize) as size_t )); for (i, c) in map.chunks(PLTE_CHANNELS).enumerate() { *colors.offset(i as isize) = GifColorType { Red: c[0], Green: c[1], Blue: c[2], } } (*new_map).Colors = colors; new_map } /// A simple wrapper around a C file handle pub struct CFile { fp: c_int } impl CFile { pub fn new(fp: c_int) -> CFile { CFile { fp: fp } } } impl Read for CFile { fn read(&mut self, buf: &mut [u8]) -> io::Result { let count = unsafe { read( self.fp, mem::transmute(buf.as_mut_ptr()), buf.len() as size_t ) }; match count { -1 => Err(io::Error::last_os_error()), n => Ok(n as usize) } } } impl ops::Drop for CFile { fn drop(&mut self) { unsafe {close(self.fp)}; } } /// A wrapper around `InputFunc` pub struct FnInputFile { func: InputFunc, file: *mut GifFileType } impl FnInputFile { pub fn new(func: InputFunc, file: *mut GifFileType) -> FnInputFile { FnInputFile { func: func, file: file } } } impl Read for FnInputFile { fn read(&mut self, buf: &mut [u8]) -> io::Result { let count = unsafe { (self.func)( self.file, mem::transmute(buf.as_mut_ptr()), buf.len() as c_int ) }; match count { -1 => Err(io::Error::from_raw_os_error(count)), n => Ok(n as usize) } } } gif-0.10.2/src/common.rs010064400017500001750000000167721347447342200132340ustar0000000000000000//! Common common used both by decoder and encoder extern crate color_quant; use std::mem; use std::borrow::Cow; /// Disposal method #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[repr(u8)] pub enum DisposalMethod { /// StreamingDecoder is not required to take any action. Any = 0, /// Do not dispose. Keep = 1, /// Restore to background color. Background = 2, /// Restore to previous. Previous = 3, } impl DisposalMethod { /// Converts `u8` to `Option` pub fn from_u8(n: u8) -> Option { if n <= 3 { Some(unsafe { mem::transmute(n) }) } else { None } } } /// Known GIF block types #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[repr(u8)] pub enum Block { /// Image block. Image = 0x2C, /// Extension block. Extension = 0x21, /// Image trailer. Trailer = 0x3B } impl Block { /// Converts `u8` to `Option` pub fn from_u8(n: u8) -> Option { match n { 0x2C | 0x21 | 0x3B => { Some(unsafe { mem::transmute(n) }) } _ => None } } } /// Known GIF extensions #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[repr(u8)] pub enum Extension { /// Text extension. Text = 0x01, /// Control extension. Control = 0xF9, /// Comment extension. Comment = 0xFE, /// Application extension. Application = 0xFF } impl Extension { /// Converts `u8` to `Option` pub fn from_u8(n: u8) -> Option { match n { 0x01 | 0xF9 | 0xFE | 0xFF => { Some(unsafe { mem::transmute(n) }) } _ => None } } } /// A GIF frame #[derive(Debug, Clone)] pub struct Frame<'a> { /// Frame delay in units of 10 ms. pub delay: u16, /// Disposal method. pub dispose: DisposalMethod, /// Transparent index (if available). pub transparent: Option, /// True if the frame needs user input to be displayed. pub needs_user_input: bool, /// Offset from the top border of the canvas. pub top: u16, /// Offset from the left border of the canvas. pub left: u16, /// Width of the frame. pub width: u16, /// Height of the frame. pub height: u16, /// True if the image is interlaced. pub interlaced: bool, /// Frame local color palette if available. pub palette: Option>, /// Buffer containing the image data. /// Only indices unless configured differently. pub buffer: Cow<'a, [u8]> } impl<'a> Default for Frame<'a> { fn default() -> Frame<'a> { Frame { delay: 0, dispose: DisposalMethod::Keep, transparent: None, needs_user_input: false, top: 0, left: 0, width: 0, height: 0, interlaced: false, palette: None, buffer: Cow::Borrowed(&[]) } } } impl Frame<'static> { /// Creates a frame from pixels in RGBA format. /// *Note: This method is not optimized for speed.* /// /// # Panics: /// * If the length of pixels does not equal `width * height * 4`. pub fn from_rgba(width: u16, height: u16, pixels: &mut [u8]) -> Frame<'static> { Frame::from_rgba_speed(width, height, pixels, 1) } /// Creates a frame from pixels in RGBA format. /// `speed` is a value in the range [1, 30]. /// The higher the value the faster it runs at the cost of image quality. /// A `speed` of 10 is a good compromise between speed and quality. /// /// # Panics: /// * If the length of pixels does not equal `width * height * 4`. /// * If `speed < 1` or `speed > 30` pub fn from_rgba_speed(width: u16, height: u16, pixels: &mut [u8], speed: i32) -> Frame<'static> { assert_eq!(width as usize * height as usize * 4, pixels.len(), "Too much or too little pixel data for the given width and height to create a GIF Frame"); assert!(speed >= 1 && speed <= 30, "speed needs to be in the range [1, 30]"); let mut frame = Frame::default(); let mut transparent = None; for pix in pixels.chunks_mut(4) { if pix[3] != 0 { pix[3] = 0xFF; } else { transparent = Some([pix[0], pix[1], pix[2], pix[3]]) } } frame.width = width; frame.height = height; let nq = color_quant::NeuQuant::new(speed, 256, pixels); frame.buffer = Cow::Owned(pixels.chunks(4).map(|pix| nq.index_of(pix) as u8).collect()); frame.palette = Some(nq.color_map_rgb()); frame.transparent = if let Some(t) = transparent { Some(nq.index_of(&t) as u8) } else { None }; frame } /// Creates a frame from a palette and indexed pixels. /// /// # Panics: /// * If the length of pixels does not equal `width * height`. /// * If the length of palette > `256 * 3`. pub fn from_palette_pixels(width: u16, height: u16, pixels: &[u8], palette: &[u8], transparent: Option) -> Frame<'static> { assert_eq!(width as usize * height as usize, pixels.len(), "Too many or too little pixels for the given width and height to create a GIF Frame"); assert!(palette.len() <= 256*3, "Too many palette values to create a GIF Frame"); let mut frame = Frame::default(); frame.width = width; frame.height = height; frame.buffer = Cow::Owned(pixels.to_vec()); frame.palette = Some(palette.to_vec()); frame.transparent = transparent; frame } /// Creates a frame from indexed pixels in the global palette. /// /// # Panics: /// * If the length of pixels does not equal `width * height`. pub fn from_indexed_pixels(width: u16, height: u16, pixels: &[u8], transparent: Option) -> Frame<'static> { assert_eq!(width as usize * height as usize, pixels.len(), "Too many or too little pixels for the given width and height to create a GIF Frame"); let mut frame = Frame::default(); frame.width = width; frame.height = height; frame.buffer = Cow::Owned(pixels.to_vec()); frame.palette = None; frame.transparent = transparent; frame } /// Creates a frame from pixels in RGB format. /// *Note: This method is not optimized for speed.* /// /// # Panics: /// * If the length of pixels does not equal `width * height * 3`. pub fn from_rgb(width: u16, height: u16, pixels: &[u8]) -> Frame<'static> { Frame::from_rgb_speed(width, height, pixels, 1) } /// Creates a frame from pixels in RGB format. /// `speed` is a value in the range [1, 30]. /// The higher the value the faster it runs at the cost of image quality. /// A `speed` of 10 is a good compromise between speed and quality. /// /// # Panics: /// * If the length of pixels does not equal `width * height * 3`. /// * If `speed < 1` or `speed > 30` pub fn from_rgb_speed(width: u16, height: u16, pixels: &[u8], speed: i32) -> Frame<'static> { assert_eq!(width as usize * height as usize * 3, pixels.len(), "Too much or too little pixel data for the given width and height to create a GIF Frame"); let mut vec: Vec = Vec::with_capacity(pixels.len() + width as usize * height as usize); for v in pixels.chunks(3) { vec.extend([v[0], v[1], v[2], 0xFF].iter().cloned()) } Frame::from_rgba_speed(width, height, &mut vec, speed) } } gif-0.10.2/src/encoder.rs010064400017500001750000000235301347447342200133510ustar0000000000000000 //! # Minimal gif encoder use std::cmp::min; use std::io; use std::io::prelude::*; use lzw; use traits::{Parameter, WriteBytesExt}; use common::{Block, Frame, Extension, DisposalMethod}; use util; /// Number of repetitions pub enum Repeat { /// Finite number of repetitions Finite(u16), /// Infinite number of repetitions Infinite } impl Parameter> for Repeat { type Result = Result<(), io::Error>; fn set_param(self, this: &mut Encoder) -> Self::Result { this.write_extension(ExtensionData::Repetitions(self)) } } /// Extension data. pub enum ExtensionData { /// Control extension. Use `ExtensionData::new_control_ext` to construct. Control { /// Flags. flags: u8, /// Frame delay. delay: u16, /// Transparent index. trns: u8 }, /// Sets the number of repetitions Repetitions(Repeat) } impl ExtensionData { /// Constructor for control extension data. /// /// `delay` is given in units of 10 ms. pub fn new_control_ext(delay: u16, dispose: DisposalMethod, needs_user_input: bool, trns: Option) -> ExtensionData { let mut flags = 0; let trns = match trns { Some(trns) => { flags |= 1; trns as u8 }, None => 0 }; flags |= (needs_user_input as u8) << 1; flags |= (dispose as u8) << 2; ExtensionData::Control { flags: flags, delay: delay, trns: trns } } } struct BlockWriter<'a, W: Write + 'a> { w: &'a mut W, bytes: usize, buf: [u8; 0xFF] } impl<'a, W: Write + 'a> BlockWriter<'a, W> { fn new(w: &'a mut W) -> BlockWriter<'a, W> { BlockWriter { w: w, bytes: 0, buf: [0; 0xFF] } } } impl<'a, W: Write + 'a> Write for BlockWriter<'a, W> { fn write(&mut self, buf: &[u8]) -> io::Result { let to_copy = min(buf.len(), 0xFF - self.bytes); util::copy_memory(&buf[..to_copy], &mut self.buf[self.bytes..]); self.bytes += to_copy; if self.bytes == 0xFF { self.bytes = 0; self.w.write_le(0xFFu8)?; self.w.write_all(&self.buf)?; } Ok(to_copy) } fn flush(&mut self) -> io::Result<()> { return Err(io::Error::new( io::ErrorKind::Other, "Cannot flush a BlockWriter, use `drop` instead." )) } } impl<'a, W: Write + 'a> Drop for BlockWriter<'a, W> { #[cfg(feature = "raii_no_panic")] fn drop(&mut self) { if self.bytes > 0 { let _ = self.w.write_le(self.bytes as u8); let _ = self.w.write_all(&self.buf[..self.bytes]); } } #[cfg(not(feature = "raii_no_panic"))] fn drop(&mut self) { if self.bytes > 0 { self.w.write_le(self.bytes as u8).unwrap(); self.w.write_all(&self.buf[..self.bytes]).unwrap(); } } } /// GIF encoder. pub struct Encoder { w: W, global_palette: bool, width: u16, height: u16 } impl Encoder { /// Creates a new encoder. /// /// `global_palette` gives the global color palette in the format `[r, g, b, ...]`, /// if no global palette shall be used an empty slice may be supplied. pub fn new(w: W, width: u16, height: u16, global_palette: &[u8]) -> io::Result { Encoder { w: w, global_palette: false, width: width, height: height }.write_global_palette(global_palette) } /// Writes the global color palette. pub fn write_global_palette(mut self, palette: &[u8]) -> io::Result { self.global_palette = true; let mut flags = 0; flags |= 0b1000_0000; let num_colors = palette.len() / 3; if num_colors > 256 { return Err(io::Error::new(io::ErrorKind::InvalidInput, "Too many colors")); } flags |= flag_size(num_colors); flags |= flag_size(num_colors) << 4; // wtf flag self.write_screen_desc(flags)?; self.write_color_table(palette)?; Ok(self) } /// Writes a frame to the image. /// /// Note: This function also writes a control extension if necessary. pub fn write_frame(&mut self, frame: &Frame) -> io::Result<()> { // TODO commented off to pass test in lib.rs //if frame.delay > 0 || frame.transparent.is_some() { self.write_extension(ExtensionData::new_control_ext( frame.delay, frame.dispose, frame.needs_user_input, frame.transparent ))?; //} self.w.write_le(Block::Image as u8)?; self.w.write_le(frame.left)?; self.w.write_le(frame.top)?; self.w.write_le(frame.width)?; self.w.write_le(frame.height)?; let mut flags = 0; if frame.interlaced { flags |= 0b0100_0000; } match frame.palette { Some(ref palette) => { flags |= 0b1000_0000; let num_colors = palette.len() / 3; if num_colors > 256 { return Err(io::Error::new(io::ErrorKind::InvalidInput, "Too many colors")); } flags |= flag_size(num_colors); self.w.write_le(flags)?; self.write_color_table(palette) }, None => if !self.global_palette { return Err(io::Error::new( io::ErrorKind::InvalidInput, "The GIF format requires a color palette but none was given." )) } else { self.w.write_le(flags) } }?; self.write_image_block(&frame.buffer) } fn write_image_block(&mut self, data: &[u8]) -> io::Result<()> { { let min_code_size: u8 = match flag_size(*data.iter().max().unwrap_or(&0) as usize + 1) + 1 { 1 => 2, // As per gif spec: The minimal code size has to be >= 2 n => n }; self.w.write_le(min_code_size)?; let mut bw = BlockWriter::new(&mut self.w); let mut enc = lzw::Encoder::new(lzw::LsbWriter::new(&mut bw), min_code_size)?; enc.encode_bytes(data)?; } self.w.write_le(0u8) } fn write_color_table(&mut self, table: &[u8]) -> io::Result<()> { let num_colors = table.len() / 3; if num_colors > 256 { return Err(io::Error::new(io::ErrorKind::InvalidInput, "Too many colors")); } let size = flag_size(num_colors); self.w.write_all(&table[..num_colors * 3])?; // Waste some space as of gif spec for _ in 0..((2 << size) - num_colors) { self.w.write_all(&[0, 0, 0])? } Ok(()) } /// Writes an extension to the image. /// /// It is normally not necessary to call this method manually. pub fn write_extension(&mut self, extension: ExtensionData) -> io::Result<()> { use self::ExtensionData::*; // 0 finite repetitions can only be achieved // if the corresponting extension is not written if let Repetitions(Repeat::Finite(0)) = extension { return Ok(()) } self.w.write_le(Block::Extension as u8)?; match extension { Control { flags, delay, trns } => { self.w.write_le(Extension::Control as u8)?; self.w.write_le(4u8)?; self.w.write_le(flags)?; self.w.write_le(delay)?; self.w.write_le(trns)?; } Repetitions(repeat) => { self.w.write_le(Extension::Application as u8)?; self.w.write_le(11u8)?; self.w.write(b"NETSCAPE2.0")?; self.w.write_le(3u8)?; self.w.write_le(1u8)?; match repeat { Repeat::Finite(no) => self.w.write_le(no)?, Repeat::Infinite => self.w.write_le(0u16)?, } } } self.w.write_le(0u8) } /// Writes a raw extension to the image. /// /// This method can be used to write an unsupported extesion to the file. `func` is the extension /// identifier (e.g. `Extension::Application as u8`). `data` are the extension payload blocks. If any /// contained slice has a lenght > 255 it is automatically divided into sub-blocks. pub fn write_raw_extension(&mut self, func: u8, data: &[&[u8]]) -> io::Result<()> { self.w.write_le(Block::Extension as u8)?; self.w.write_le(func as u8)?; for block in data { for chunk in block.chunks(0xFF) { self.w.write_le(chunk.len() as u8)?; self.w.write_all(chunk)?; } } self.w.write_le(0u8) } /// Writes the logical screen desriptor fn write_screen_desc(&mut self, flags: u8) -> io::Result<()> { self.w.write_all(b"GIF89a")?; self.w.write_le(self.width)?; self.w.write_le(self.height)?; self.w.write_le(flags)?; // packed field self.w.write_le(0u8)?; // bg index self.w.write_le(0u8) // aspect ratio } } impl Drop for Encoder { #[cfg(feature = "raii_no_panic")] fn drop(&mut self) { let _ = self.w.write_le(Block::Trailer as u8); } #[cfg(not(feature = "raii_no_panic"))] fn drop(&mut self) { self.w.write_le(Block::Trailer as u8).unwrap() } } // Color table size converted to flag bits fn flag_size(size: usize) -> u8 { match size { 0 ...2 => 0, 3 ...4 => 1, 5 ...8 => 2, 7 ...16 => 3, 17 ...32 => 4, 33 ...64 => 5, 65 ...128 => 6, 129...256 => 7, _ => 7 } } gif-0.10.2/src/lib.rs010064400017500001750000000114611347447342200125000ustar0000000000000000//! # GIF en- and decoding library [![Build Status](https://travis-ci.org/PistonDevelopers/image-gif.svg?branch=master)](https://travis-ci.org/PistonDevelopers/image-gif) //! //! GIF en- and decoder written in Rust ([API Documentation](http://www.piston.rs/image/gif/index.html)). //! //! # GIF encoding and decoding library //! //! This library provides all functions necessary to de- and encode GIF files. //! //! ## High level interface //! //! The high level interface consists of the two types //! [`Encoder`](struct.Encoder.html) and [`Decoder`](struct.Decoder.html). //! They as builders for the actual en- and decoders and can be used to set various //! options beforehand. //! //! ### Decoding GIF files //! //! ```rust //! // Open the file //! use std::fs::File; //! use gif::SetParameter; //! let mut decoder = gif::Decoder::new(File::open("tests/samples/sample_1.gif").unwrap()); //! // Configure the decoder such that it will expand the image to RGBA. //! decoder.set(gif::ColorOutput::RGBA); //! // Read the file header //! let mut decoder = decoder.read_info().unwrap(); //! while let Some(frame) = decoder.read_next_frame().unwrap() { //! // Process every frame //! } //! ``` //! //! //! //! ### Encoding GIF files //! //! The encoder can be used so save simple computer generated images: //! //! ```rust //! use gif::{Frame, Encoder, Repeat, SetParameter}; //! use std::fs::File; //! use std::borrow::Cow; //! //! let color_map = &[0xFF, 0xFF, 0xFF, 0, 0, 0]; //! let (width, height) = (6, 6); //! let mut beacon_states = [[ //! 0, 0, 0, 0, 0, 0, //! 0, 1, 1, 0, 0, 0, //! 0, 1, 1, 0, 0, 0, //! 0, 0, 0, 1, 1, 0, //! 0, 0, 0, 1, 1, 0, //! 0, 0, 0, 0, 0, 0, //! ], [ //! 0, 0, 0, 0, 0, 0, //! 0, 1, 1, 0, 0, 0, //! 0, 1, 0, 0, 0, 0, //! 0, 0, 0, 0, 1, 0, //! 0, 0, 0, 1, 1, 0, //! 0, 0, 0, 0, 0, 0, //! ]]; //! let mut image = File::create("tests/samples/beacon.gif").unwrap();; //! let mut encoder = Encoder::new(&mut image, width, height, color_map).unwrap(); //! encoder.set(Repeat::Infinite).unwrap(); //! for state in &beacon_states { //! let mut frame = Frame::default(); //! frame.width = width; //! frame.height = height; //! frame.buffer = Cow::Borrowed(&*state); //! encoder.write_frame(&frame).unwrap(); //! } //! ``` //! //! [`Frame::from_*`](struct.Frame.html) can be used to convert a true color image to a paletted //! image with a maximum of 256 colors: //! //! ```rust //! use std::fs::File; //! //! // Get pixel data from some source //! let mut pixels: Vec = vec![0; 30_000]; //! // Create frame from data //! let frame = gif::Frame::from_rgb(100, 100, &mut *pixels); //! // Create encoder //! let mut image = File::create("target/indexed_color.gif").unwrap(); //! let mut encoder = gif::Encoder::new(&mut image, frame.width, frame.height, &[]).unwrap(); //! // Write frame to file //! encoder.write_frame(&frame).unwrap(); //! ``` //! //! ## C API //! //! The C API is unstable and widely untested. It can be activated using the feature flag `c_api`. // TODO: make this compile // ```rust // use gif::{Frame, Encoder}; // use std::fs::File; // let color_map = &[0, 0, 0, 0xFF, 0xFF, 0xFF]; // let mut frame = Frame::default(); // // Generate checkerboard lattice // for (i, j) in (0..10).zip(0..10) { // frame.buffer.push(if (i * j) % 2 == 0 { // 1 // } else { // 0 // }) // } // # (|| { // { // let mut file = File::create("test.gif")?; // let mut encoder = Encoder::new(&mut file, 100, 100); // encoder.write_global_palette(color_map)?.write_frame(&frame) // } // # })().unwrap(); // ``` #![deny(missing_docs)] // Uncomment to run tests //#![cfg_attr(test, feature(test))] #[cfg(feature = "c_api")] extern crate libc; extern crate lzw; mod traits; mod common; mod util; mod reader; mod encoder; #[cfg(feature = "c_api")] mod c_api_utils; #[cfg(feature = "c_api")] pub mod c_api; pub use traits::{SetParameter, Parameter}; pub use common::{Block, Extension, DisposalMethod, Frame}; pub use reader::{StreamingDecoder, Decoded, DecodingError}; /// StreamingDecoder configuration parameters pub use reader::{ColorOutput, MemoryLimit, Extensions}; pub use reader::{Reader, Decoder}; pub use encoder::{Encoder, ExtensionData, Repeat}; #[cfg(test)] #[test] fn round_trip() { use std::io::prelude::*; use std::fs::File; let mut data = vec![]; File::open("tests/samples/sample_1.gif").unwrap().read_to_end(&mut data).unwrap(); let mut decoder = Decoder::new(&*data).read_info().unwrap(); let palette: Vec = decoder.palette().unwrap().into(); let frame = decoder.read_next_frame().unwrap().unwrap(); let mut data2 = vec![]; { let mut encoder = Encoder::new(&mut data2, frame.width, frame.height, &palette).unwrap(); encoder.write_frame(frame).unwrap(); } assert_eq!(&data[..], &data2[..]) }gif-0.10.2/src/reader/decoder.rs010064400017500001750000000466311347447342200146100ustar0000000000000000use std::cmp; use std::mem; use std::default::Default; use std::io; use std::fmt; use std::error; use lzw; use traits::Parameter; use common::{Frame, Block, Extension, DisposalMethod}; /// GIF palettes are RGB pub const PLTE_CHANNELS: usize = 3; #[derive(Debug)] /// Decoding error. pub enum DecodingError { /// Returned if the image is found to be malformed. Format(&'static str), /// Internal (logic) error. Internal(&'static str), /// Wraps `std::io::Error`. Io(io::Error), } impl fmt::Display for DecodingError { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match *self { DecodingError::Format(ref d) => d.fmt(fmt), DecodingError::Internal(ref d) => d.fmt(fmt), DecodingError::Io(ref err) => err.fmt(fmt), } } } impl error::Error for DecodingError { fn description(&self) -> &str { match *self { DecodingError::Format(ref d) => d, DecodingError::Internal(ref d) => d, DecodingError::Io(ref err) => err.description(), } } fn cause(&self) -> Option<&error::Error> { match *self { DecodingError::Io(ref err) => Some(err), _ => None, } } } impl From for DecodingError { fn from(err: io::Error) -> Self { DecodingError::Io(err) } } /// Configures how extensions should be handled #[derive(PartialEq, Debug)] pub enum Extensions { /// Saves all extention data Save, /// Skips the data of unknown extensions /// and extracts the data from known ones Skip } impl Parameter for Extensions { type Result = (); fn set_param(self, this: &mut StreamingDecoder) { this.skip_extensions = match self { Extensions::Skip => true, Extensions::Save => false, } } } /// Indicates whether a certain object has been decoded #[derive(Debug)] pub enum Decoded<'a> { /// Decoded nothing. Nothing, /// Global palette. GlobalPalette(Vec), /// Index of the background color in the global palette. BackgroundColor(u8), /// Decoded the image trailer. Trailer, /// The start of a block. BlockStart(Block), /// Decoded a sub-block. More sub-block are available. SubBlockFinished(u8, &'a [u8]), /// Decoded the last (or only) sub-block of a block. BlockFinished(u8, &'a [u8]), /// Decoded all information of the next frame. /// The returned frame does **not** any image data. Frame(&'a Frame<'static>), /// Decoded some data of the current frame. Data(&'a [u8]), /// No more data available the current frame. DataEnd, } /// Internal state of the GIF decoder #[derive(Debug)] enum State { Magic(usize, [u8; 6]), U16Byte1(U16Value, u8), U16(U16Value), Byte(ByteValue), GlobalPalette(usize), BlockStart(Option), BlockEnd(u8), ExtensionBlock(u8), SkipBlock(usize), LocalPalette(usize), LzwInit(u8), DecodeSubBlock(usize), FrameDecoded, Trailer } use self::State::*; /// U16 values that may occur in a GIF image #[derive(Debug)] enum U16Value { /// Logical screen descriptor width ScreenWidth, /// Logical screen descriptor height ScreenHeight, /// Delay time Delay, /// Left frame offset ImageLeft, /// Top frame offset ImageTop, /// Frame width ImageWidth, /// Frame height ImageHeight, } /// Single byte screen descriptor values #[derive(Debug)] enum ByteValue { GlobalFlags, Background { table_size: usize }, AspectRatio { table_size: usize }, ControlFlags, ImageFlags, TransparentIdx, CodeSize, } /// GIF decoder which supports streaming #[derive(Debug)] pub struct StreamingDecoder { state: Option, lzw_reader: Option>, skip_extensions: bool, version: &'static str, width: u16, height: u16, global_color_table: Vec, background_color: [u8; 4], /// ext buffer ext: (u8, Vec, bool), /// Frame data current: Option>, } impl StreamingDecoder { /// Creates a new streaming decoder pub fn new() -> StreamingDecoder { StreamingDecoder { state: Some(Magic(0, [0; 6])), lzw_reader: None, skip_extensions: true, version: "", width: 0, height: 0, global_color_table: Vec::new(), background_color: [0, 0, 0, 0xFF], ext: (0, Vec::with_capacity(256), true), // 0xFF + 1 byte length current: None } } /// Updates the internal state of the decoder. /// /// Returns the number of bytes consumed from the input buffer /// and the last decoding result. pub fn update<'a>(&'a mut self, mut buf: &[u8]) -> Result<(usize, Decoded<'a>), DecodingError> { // NOTE: Do not change the function signature without double-checking the // unsafe block! let len = buf.len(); while buf.len() > 0 && self.state.is_some() { match self.next_state(buf) { Ok((bytes, Decoded::Nothing)) => { buf = &buf[bytes..] } Ok((bytes, Decoded::Trailer)) => { buf = &buf[bytes..]; break } Ok((bytes, result)) => { buf = &buf[bytes..]; return Ok( (len-buf.len(), // This transmute just casts the lifetime away. Since Rust only // has SESE regions, this early return cannot be worked out and // such that the borrow region of self includes the whole block. // The explixit lifetimes in the function signature ensure that // this is safe. // ### NOTE // To check that everything is sound, return the result without // the match (e.g. `return Ok(self.next_state(buf)?)`). If // it compiles the returned lifetime is correct. unsafe { mem::transmute::(result) } )) } Err(err) => return Err(err) } } Ok((len-buf.len(), Decoded::Nothing)) } /// Returns the data of the last extension that has been decoded. pub fn last_ext(&self) -> (u8, &[u8], bool) { (self.ext.0, &*self.ext.1, self.ext.2) } #[inline(always)] /// Current frame info as a mutable ref. pub fn current_frame_mut<'a>(&'a mut self) -> &'a mut Frame<'static> { self.current.as_mut().unwrap() } #[inline(always)] /// Current frame info as a ref. pub fn current_frame<'a>(&'a self) -> &'a Frame<'static> { self.current.as_ref().unwrap() } /// Width of the image pub fn width(&self) -> u16 { self.width } /// Height of the image pub fn height(&self) -> u16 { self.height } fn next_state<'a>(&'a mut self, buf: &[u8]) -> Result<(usize, Decoded<'a>), DecodingError> { macro_rules! goto ( ($n:expr, $state:expr) => ({ self.state = Some($state); Ok(($n, Decoded::Nothing)) }); ($state:expr) => ({ self.state = Some($state); Ok((1, Decoded::Nothing)) }); ($n:expr, $state:expr, emit $res:expr) => ({ self.state = Some($state); Ok(($n, $res)) }); ($state:expr, emit $res:expr) => ({ self.state = Some($state); Ok((1, $res)) }) ); let b = buf[0]; // Driver should ensure that state is never None let state = self.state.take().unwrap(); //println!("{:?}", state); match state { Magic(i, mut version) => if i < 6 { version[i] = b; goto!(Magic(i+1, version)) } else if &version[..3] == b"GIF" { self.version = match &version[3..] { b"87a" => "87a", b"89a" => "89a", _ => return Err(DecodingError::Format("unsupported GIF version")) }; goto!(U16Byte1(U16Value::ScreenWidth, b)) } else { Err(DecodingError::Format("malformed GIF header")) }, U16(next) => goto!(U16Byte1(next, b)), U16Byte1(next, value) => { use self::U16Value::*; let value = ((b as u16) << 8) | value as u16; match (next, value) { (ScreenWidth, width) => { self.width = width; goto!(U16(U16Value::ScreenHeight)) }, (ScreenHeight, height) => { self.height = height; goto!(Byte(ByteValue::GlobalFlags)) }, (Delay, delay) => { self.ext.1.push(value as u8); self.ext.1.push(b); self.current_frame_mut().delay = delay; goto!(Byte(ByteValue::TransparentIdx)) }, (ImageLeft, left) => { self.current_frame_mut().left = left; goto!(U16(U16Value::ImageTop)) }, (ImageTop, top) => { self.current_frame_mut().top = top; goto!(U16(U16Value::ImageWidth)) }, (ImageWidth, width) => { self.current_frame_mut().width = width; goto!(U16(U16Value::ImageHeight)) }, (ImageHeight, height) => { self.current_frame_mut().height = height; goto!(Byte(ByteValue::ImageFlags)) } } } Byte(value) => { use self::ByteValue::*; match value { GlobalFlags => { let global_table = b & 0x80 != 0; let entries = if global_table { let entries = PLTE_CHANNELS*(1 << ((b & 0b111) + 1) as usize); self.global_color_table.reserve_exact(entries); entries } else { 0usize }; goto!(Byte(Background { table_size: entries })) }, Background { table_size } => { goto!( Byte(AspectRatio { table_size: table_size }), emit Decoded::BackgroundColor(b) ) }, AspectRatio { table_size } => { goto!(GlobalPalette(table_size)) }, ControlFlags => { self.ext.1.push(b); let control_flags = b; if control_flags & 1 != 0 { // Set to Some(...), gets overwritten later self.current_frame_mut().transparent = Some(0) } self.current_frame_mut().needs_user_input = control_flags & 0b10 != 0; self.current_frame_mut().dispose = match DisposalMethod::from_u8( (control_flags & 0b11100) >> 2 ) { Some(method) => method, None => DisposalMethod::Any }; goto!(U16(U16Value::Delay)) } TransparentIdx => { self.ext.1.push(b); if let Some(ref mut idx) = self.current_frame_mut().transparent { *idx = b } goto!(SkipBlock(0)) //goto!(AwaitBlockEnd) } ImageFlags => { let local_table = (b & 0b1000_0000) != 0; let interlaced = (b & 0b0100_0000) != 0; let table_size = b & 0b0000_0111; self.current_frame_mut().interlaced = interlaced; if local_table { let entries = PLTE_CHANNELS * (1 << (table_size + 1)); self.current_frame_mut().palette = Some(Vec::with_capacity(entries)); goto!(LocalPalette(entries)) } else { goto!(Byte(CodeSize)) } }, CodeSize => goto!(LzwInit(b)) } } GlobalPalette(left) => { let n = cmp::min(left, buf.len()); if left > 0 { self.global_color_table.extend(buf[..n].iter().cloned()); goto!(n, GlobalPalette(left - n)) } else { let idx = self.background_color[0]; match self.global_color_table.chunks(PLTE_CHANNELS).nth(idx as usize) { Some(chunk) => for i in 0..PLTE_CHANNELS { self.background_color[i] = chunk[i] }, None => self.background_color[0] = 0 } goto!(BlockStart(Block::from_u8(b)), emit Decoded::GlobalPalette( mem::replace(&mut self.global_color_table, Vec::new()) )) } } BlockStart(type_) => { use common::Block::*; match type_ { Some(Image) => { self.add_frame(); goto!(U16Byte1(U16Value::ImageLeft, b), emit Decoded::BlockStart(Image)) } Some(Extension) => goto!(ExtensionBlock(b), emit Decoded::BlockStart(Extension)), Some(Trailer) => goto!(0, State::Trailer, emit Decoded::BlockStart(Trailer)), None => { return Err(DecodingError::Format( "unknown block type encountered" ))} } } BlockEnd(terminator) => { if terminator == 0 { if b == Block::Trailer as u8 { goto!(0, BlockStart(Some(Block::Trailer))) } else { goto!(BlockStart(Block::from_u8(b))) } } else { return Err(DecodingError::Format( "expected block terminator not found" )) } } ExtensionBlock(type_) => { use common::Extension::*; self.ext.0 = type_; self.ext.1.clear(); self.ext.1.push(b); if let Some(ext) = Extension::from_u8(type_) { match ext { Control => { goto!(self.read_control_extension(b)?) } Text | Comment | Application => { goto!(SkipBlock(b as usize)) } } } else { return Err(DecodingError::Format( "unknown extention block encountered" )) } } SkipBlock(left) => { let n = cmp::min(left, buf.len()); if left > 0 { self.ext.1.push(b); goto!(n, SkipBlock(left - n)) } else { if b == 0 { self.ext.2 = true; goto!(BlockEnd(b), emit Decoded::BlockFinished(self.ext.0, &self.ext.1)) } else { self.ext.2 = false; goto!(SkipBlock(b as usize), emit Decoded::SubBlockFinished(self.ext.0,&self.ext.1)) } } } LocalPalette(left) => { let n = cmp::min(left, buf.len()); if left > 0 { self.current_frame_mut().palette .as_mut().unwrap().extend(buf[..n].iter().cloned()); goto!(n, LocalPalette(left - n)) } else { goto!(LzwInit(b)) } } LzwInit(code_size) => { // LZW spec: max 12 bits per code if code_size > 11 { return Err(DecodingError::Format( "invalid minimal code size" )) } self.lzw_reader = Some(lzw::Decoder::new(lzw::LsbReader::new(), code_size)); goto!(DecodeSubBlock(b as usize), emit Decoded::Frame(self.current_frame_mut())) } DecodeSubBlock(left) => { if left > 0 { let n = cmp::min(left, buf.len()); let decoder = self.lzw_reader.as_mut().unwrap(); let (consumed, bytes) = decoder.decode_bytes(&buf[..n])?; goto!(consumed, DecodeSubBlock(left - consumed), emit Decoded::Data(bytes)) } else if b != 0 { // decode next sub-block goto!(DecodeSubBlock(b as usize)) } else { // The end of the lzw stream is only reached if left == 0 and an additional call // to `decode_bytes` results in an empty slice. let decoder = self.lzw_reader.as_mut().unwrap(); let (_, bytes) = decoder.decode_bytes(&[])?; if bytes.len() > 0 { goto!(0, DecodeSubBlock(0), emit Decoded::Data(bytes)) } else { // end of image data reached self.current = None; goto!(0, FrameDecoded, emit Decoded::DataEnd) } } } FrameDecoded => { goto!(BlockEnd(b)) } Trailer => { self.state = None; Ok((1, Decoded::Trailer)) //panic!("EOF {:?}", self) } } } fn read_control_extension(&mut self, b: u8) -> Result { self.add_frame(); self.ext.1.push(b); if b != 4 { return Err(DecodingError::Format( "control extension has wrong length" )) } Ok(Byte(ByteValue::ControlFlags)) } fn add_frame(&mut self) { if self.current.is_none() { self.current = Some(Frame::default()) } } } gif-0.10.2/src/reader/mod.rs010064400017500001750000000460541347447342200137610ustar0000000000000000use std::borrow::Cow; use std::io; use std::cmp; use std::mem; use std::iter; use std::io::prelude::*; use traits::{Parameter, SetParameter}; use common::Frame; use util; mod decoder; pub use self::decoder::{ PLTE_CHANNELS, StreamingDecoder, Decoded, DecodingError, Extensions }; const N_CHANNELS: usize = 4; impl Parameter> for T where T: Parameter, R: Read { type Result = (); fn set_param(self, this: &mut Decoder) { this.decoder.set(self); } } /// Output mode for the image data #[derive(PartialEq, Debug)] #[repr(u8)] pub enum ColorOutput { /// The decoder expands the image data to 32bit RGBA. /// This affects: /// /// - The buffer buffer of the `Frame` returned by `Reader::read_next_frame`. /// - `Reader::fill_buffer`, `Reader::buffer_size` and `Reader::line_length`. RGBA = 0, /// The decoder returns the raw indexed data. Indexed = 1, } impl Parameter> for ColorOutput { type Result = (); fn set_param(self, this: &mut Decoder) { this.color_output = self } } #[derive(Debug)] /// Memory limit in bytes. `MemoryLimit::Some(0)` means /// that there is no memory limit set. pub struct MemoryLimit(pub u32); impl Parameter> for MemoryLimit { type Result = (); fn set_param(self, this: &mut Decoder) { let MemoryLimit(limit) = self; this.memory_limit = limit } } /// GIF decoder pub struct Decoder { r: R, decoder: StreamingDecoder, memory_limit: u32, color_output: ColorOutput, } impl Decoder { /// Creates a new decoder builder pub fn new(r: R) -> Decoder { Decoder { r: r, decoder: StreamingDecoder::new(), memory_limit: 50_000_000, // 50 MB color_output: ColorOutput::Indexed } } /// Reads the logical screen descriptor including the global color palette /// /// Returns a `Reader`. All decoder configuration has to be done beforehand. pub fn read_info(self) -> Result, DecodingError> { Reader::new(self.r, self.decoder, self.color_output, self.memory_limit).init() } } struct ReadDecoder { reader: io::BufReader, decoder: StreamingDecoder, at_eof: bool } impl ReadDecoder { fn decode_next(&mut self) -> Result, DecodingError> { while !self.at_eof { let (consumed, result) = { let buf = self.reader.fill_buf()?; if buf.len() == 0 { return Err(DecodingError::Format( "unexpected EOF" )) } self.decoder.update(buf)? }; self.reader.consume(consumed); match result { Decoded::Nothing => (), Decoded::BlockStart(::common::Block::Trailer) => { self.at_eof = true }, result => return Ok(unsafe{ // FIXME: #6393 Some(mem::transmute::(result)) }), } } Ok(None) } } #[allow(dead_code)] /// GIF decoder pub struct Reader { decoder: ReadDecoder, color_output: ColorOutput, memory_limit: u32, bg_color: Option, global_palette: Option>, current_frame: Frame<'static>, buffer: Vec, // Offset in current frame offset: usize } impl Reader where R: Read { fn new(reader: R, decoder: StreamingDecoder, color_output: ColorOutput, memory_limit: u32 ) -> Reader { Reader { decoder: ReadDecoder { reader: io::BufReader::new(reader), decoder: decoder, at_eof: false }, bg_color: None, global_palette: None, buffer: Vec::with_capacity(32), color_output: color_output, memory_limit: memory_limit, current_frame: Frame::default(), offset: 0 } } fn init(mut self) -> Result { loop { match self.decoder.decode_next()? { Some(Decoded::BackgroundColor(bg_color)) => { self.bg_color = Some(bg_color) } Some(Decoded::GlobalPalette(palette)) => { self.global_palette = if palette.len() > 0 { Some(palette) } else { None }; break }, Some(_) => { // Unreachable since this loop exists after the global // palette has been read. unreachable!() }, None => return Err(DecodingError::Format( "File does not contain any image data" )) } } // If the background color is invalid, ignore it if let &Some(ref palette) = &self.global_palette { if self.bg_color.unwrap_or(0) as usize >= palette.len() { self.bg_color = None; } } Ok(self) } /// Returns the next frame info pub fn next_frame_info(&mut self) -> Result>, DecodingError> { loop { match self.decoder.decode_next()? { Some(Decoded::Frame(frame)) => { self.current_frame = frame.clone(); if frame.palette.is_none() && self.global_palette.is_none() { return Err(DecodingError::Format( "No color table available for current frame." )) } if self.memory_limit > 0 && ( (frame.width as u32 * frame.height as u32) > self.memory_limit ) { return Err(DecodingError::Format( "Image is too large to decode." )) } break }, Some(_) => (), None => return Ok(None) } } Ok(Some(&self.current_frame)) } /// Reads the next frame from the image. /// /// Do not call `Self::next_frame_info` beforehand. /// Deinterlaces the result. pub fn read_next_frame(&mut self) -> Result>, DecodingError> { if self.next_frame_info()?.is_some() { let mut vec = vec![0; self.buffer_size()]; self.read_into_buffer(&mut vec)?; self.current_frame.buffer = Cow::Owned(vec); self.current_frame.interlaced = false; Ok(Some(&self.current_frame)) } else { Ok(None) } } /// Reads the data of the current frame into a pre-allocated buffer. /// /// `Self::next_frame_info` needs to be called beforehand. /// The length of `buf` must be at least `Self::buffer_size`. /// Deinterlaces the result. pub fn read_into_buffer(&mut self, buf: &mut [u8]) -> Result<(), DecodingError> { if self.current_frame.interlaced { let width = self.line_length(); let height = self.current_frame.height as usize; for row in (InterlaceIterator { len: height, next: 0, pass: 0 }) { if !self.fill_buffer(&mut buf[row*width..][..width])? { return Err(DecodingError::Format("Image truncated")) } } } else { let buf = &mut buf[..self.buffer_size()]; if !self.fill_buffer(buf)? { return Err(DecodingError::Format("Image truncated")) } }; Ok(()) } /// Reads data of the current frame into a pre-allocated buffer until the buffer has been /// filled completely. /// /// `Self::next_frame_info` needs to be called beforehand. Returns `true` if the supplied /// buffer could be filled completely. Should not be called after `false` had been returned. pub fn fill_buffer(&mut self, mut buf: &mut [u8]) -> Result { use self::ColorOutput::*; const PLTE_CHANNELS: usize = 3; macro_rules! handle_data( ($data:expr) => { match self.color_output { RGBA => { let transparent = self.current_frame.transparent; let palette: &[u8] = match self.current_frame.palette { Some(ref table) => &*table, None => &*self.global_palette.as_ref().unwrap(), }; let len = cmp::min(buf.len()/N_CHANNELS, $data.len()); for (rgba, &idx) in buf[..len*N_CHANNELS].chunks_mut(N_CHANNELS).zip($data.iter()) { let plte_offset = PLTE_CHANNELS * idx as usize; if palette.len() >= plte_offset + PLTE_CHANNELS { let colors = &palette[plte_offset..]; rgba[0] = colors[0]; rgba[1] = colors[1]; rgba[2] = colors[2]; rgba[3] = if let Some(t) = transparent { if t == idx { 0x00 } else { 0xFF } } else { 0xFF } } } (len, N_CHANNELS) }, Indexed => { let len = cmp::min(buf.len(), $data.len()); util::copy_memory(&$data[..len], &mut buf[..len]); (len, 1) } } } ); let buf_len = self.buffer.len(); if buf_len > 0 { let (len, channels) = handle_data!(&self.buffer); // This is WRONG!!!! Cuts form the wrong side… self.buffer.truncate(buf_len-len); let buf_ = buf; buf = &mut buf_[len*channels..]; if buf.len() == 0 { return Ok(true) } } loop { match self.decoder.decode_next()? { Some(Decoded::Data(data)) => { let (len, channels) = handle_data!(data); let buf_ = buf; buf = &mut buf_[len*channels..]; // shorten buf if buf.len() > 0 { continue } else if len < data.len() { self.buffer.extend(data[len..].iter().map(|&v| v)); } return Ok(true) }, Some(_) => return Ok(false), // make sure that no important result is missed None => return Ok(false) } } } /// Output buffer size pub fn buffer_size(&self) -> usize { self.line_length() * self.current_frame.height as usize } /// Line length of the current frame pub fn line_length(&self) -> usize { use self::ColorOutput::*; match self.color_output { RGBA => self.current_frame.width as usize * N_CHANNELS, Indexed => self.current_frame.width as usize } } /// Returns the color palette relevant for the current (next) frame pub fn palette(&self) -> Result<&[u8], DecodingError> { // TODO prevent planic Ok(match self.current_frame.palette { Some(ref table) => &*table, None => &*self.global_palette.as_ref().ok_or(DecodingError::Format( "No color table available for current frame." ))?, }) } /// The global color palette pub fn global_palette(&self) -> Option<&[u8]> { self.global_palette.as_ref().map(|v| &**v) } /// Width of the image pub fn width(&self) -> u16 { self.decoder.decoder.width() } /// Height of the image pub fn height(&self) -> u16 { self.decoder.decoder.height() } /// Index of the background color in the global palette pub fn bg_color(&self) -> Option { self.bg_color.map(|v| v as usize) } } struct InterlaceIterator { len: usize, next: usize, pass: usize } impl iter::Iterator for InterlaceIterator { type Item = usize; fn next(&mut self) -> Option { if self.len == 0 || self.pass > 3 { return None } let mut next = self.next + [8, 8, 4, 2][self.pass]; while next >= self.len { next = [4, 2, 1, 0][self.pass]; self.pass += 1; } mem::swap(&mut next, &mut self.next); Some(next) } } #[cfg(test)] mod test { use std::fs::File; use super::{Decoder, InterlaceIterator}; /* Commented because test::Bencher is unstable extern crate test; use std::io::prelude::*; #[bench] fn bench_tiny(b: &mut test::Bencher) { let mut data = Vec::new(); File::open("tests/samples/sample_1.gif").unwrap().read_to_end(&mut data).unwrap(); b.iter(|| { let mut decoder = Decoder::new(&*data).read_info().unwrap(); let frame = decoder.read_next_frame().unwrap().unwrap(); test::black_box(frame); }); let mut decoder = Decoder::new(&*data).read_info().unwrap(); b.bytes = decoder.read_next_frame().unwrap().unwrap().buffer.len() as u64 } #[bench] fn bench_big(b: &mut test::Bencher) { let mut data = Vec::new(); File::open("tests/sample_big.gif").unwrap().read_to_end(&mut data).unwrap(); b.iter(|| { let mut decoder = Decoder::new(&*data).read_info().unwrap(); let frame = decoder.read_next_frame().unwrap().unwrap(); test::black_box(frame); }); let mut decoder = Decoder::new(&*data).read_info().unwrap(); b.bytes = decoder.read_next_frame().unwrap().unwrap().buffer.len() as u64 }*/ #[test] fn test_simple_indexed() { let mut decoder = Decoder::new(File::open("tests/samples/sample_1.gif").unwrap()).read_info().unwrap(); let frame = decoder.read_next_frame().unwrap().unwrap(); assert_eq!(&*frame.buffer, &[ 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1 ][..]) } #[test] fn test_interlace_iterator() { for &(len, expect) in &[ (0, &[][..]), (1, &[0][..]), (2, &[0, 1][..]), (3, &[0, 2, 1][..]), (4, &[0, 2, 1, 3][..]), (5, &[0, 4, 2, 1, 3][..]), (6, &[0, 4, 2, 1, 3, 5][..]), (7, &[0, 4, 2, 6, 1, 3, 5][..]), (8, &[0, 4, 2, 6, 1, 3, 5, 7][..]), (9, &[0, 8, 4, 2, 6, 1, 3, 5, 7][..]), (10, &[0, 8, 4, 2, 6, 1, 3, 5, 7, 9][..]), (11, &[0, 8, 4, 2, 6, 10, 1, 3, 5, 7, 9][..]), (12, &[0, 8, 4, 2, 6, 10, 1, 3, 5, 7, 9, 11][..]), (13, &[0, 8, 4, 12, 2, 6, 10, 1, 3, 5, 7, 9, 11][..]), (14, &[0, 8, 4, 12, 2, 6, 10, 1, 3, 5, 7, 9, 11, 13][..]), (15, &[0, 8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13][..]), (16, &[0, 8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15][..]), (17, &[0, 8, 16, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15][..]), ] { let iter = InterlaceIterator { len: len, next: 0, pass: 0 }; let lines = iter.collect::>(); assert_eq!(lines, expect); } } } #[cfg(feature = "c_api")] mod c_interface { use std::io::prelude::*; use std::ptr; use std::borrow::Cow; use libc::c_int; use common::Block; use c_api::{self, GifWord}; use c_api_utils::{CInterface, copy_colormap, copy_data, saved_images_new}; use super::decoder::{Decoded, DecodingError}; use super::{Reader}; impl Reader where R: Read + 'static { /// Converts `Reader` into `CInterface`. pub fn into_c_interface(self) -> Box { Box::new(self) } } impl CInterface for Reader { fn read_screen_desc(&mut self, this: &mut c_api::GifFileType) { this.SWidth = self.width() as GifWord; this.SHeight = self.height() as GifWord; this.SColorResolution = 255;//self.global_palette().len() as GifWord; this.SBackGroundColor = self.bg_color().unwrap_or(0) as GifWord; this.AspectByte = 0; self.offset = 0; } fn current_image_buffer(&mut self) -> Result<(&[u8], &mut usize), DecodingError> { if let Cow::Borrowed(_) = self.current_frame.buffer { self.read_next_frame()?; } Ok((&self.current_frame.buffer, &mut self.offset)) } fn last_ext(&self) -> (u8, &[u8], bool) { self.decoder.decoder.last_ext() } fn next_record_type(&mut self) -> Result { loop { match self.decoder.decode_next()? { Some(Decoded::BlockStart(type_)) => return Ok(type_), Some(_) => (), None => return Ok(Block::Trailer) } } } fn decode_next(&mut self) -> Result, DecodingError> { self.decoder.decode_next() } /* unsafe fn read_to_end(&mut self, this: &mut c_api::GifFileType) -> Result<(), DecodingError> { self.read_screen_desc(this)?; self.read_to_end()?; this.ImageCount = self.frames().len() as c_int; let images = saved_images_new(this.ImageCount as usize); for (i, frame) in self.frames().iter().enumerate() { *images.offset(i as isize) = c_api::SavedImage { ImageDesc: c_api::GifImageDesc { Left: frame.left as GifWord, Top: frame.top as GifWord, Width: frame.width as GifWord, Height: frame.height as GifWord, Interlace: num::FromPrimitive::from_u8(frame.interlaced as u8).unwrap(), ColorMap: copy_colormap(&frame.palette) }, // on malloc(3) heap RasterBits: copy_data(&*frame.buffer), ExtensionBlockCount: 0, ExtensionBlocks: ptr::null_mut() } } this.SavedImages = images; Ok(()) }*/ } } gif-0.10.2/src/traits.rs010064400017500001750000000036031347447342200132370ustar0000000000000000//! Traits used in this library use std::io; /// Configuration parameter trait. /// /// Use the list of implementors to see which parameters are available for which struct. pub trait Parameter { /// Result type of `set_param`. // TODO: Use default type () when associated type defaults get stable. type Result; /// Sets `self` as a parameter of `Object`. fn set_param(self, &mut Object) -> Self::Result; } /// Implemented for objects that have parameters. /// /// Provides a unified `set`-method to simplify the configuration. pub trait SetParameter: Sized { /// Sets `value` as a parameter of `self`. fn set>(&mut self, value: T) -> >::Result { value.set_param(self) } } impl SetParameter for T {} /// Writer extension to write little endian data pub trait WriteBytesExt { /// Writes `T` to a bytes stream. Least significant byte first. fn write_le(&mut self, n: T) -> io::Result<()>; /* #[inline] fn write_byte(&mut self, n: u8) -> io::Result<()> where Self: Write { self.write_all(&[n]) } */ } impl WriteBytesExt for W { #[inline] fn write_le(&mut self, n: u8) -> io::Result<()> { self.write_all(&[n]) } } impl WriteBytesExt for W { #[inline] fn write_le(&mut self, n: u16) -> io::Result<()> { self.write_all(&[n as u8, (n>>8) as u8]) } } impl WriteBytesExt for W { #[inline] fn write_le(&mut self, n: u32) -> io::Result<()> { self.write_le(n as u16)?; self.write_le((n >> 16) as u16) } } impl WriteBytesExt for W { #[inline] fn write_le(&mut self, n: u64) -> io::Result<()> { self.write_le(n as u32)?; self.write_le((n >> 32) as u32) } }gif-0.10.2/src/util.rs010064400017500001750000000011371347447342200127060ustar0000000000000000//! Utility functions // Copies data from `src` to `dst` // // Panics if the length of `dst` is less than the length of `src`. // NOTE: this is a copy-paste of the unstable function `std::slice::bytes::copy_memory`. #[inline] pub fn copy_memory(src: &[u8], dst: &mut [u8]) { let len_src = src.len(); assert!(dst.len() >= len_src); // `dst` is unaliasable, so we know statically it doesn't overlap // with `src`. unsafe { ::std::ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr(), len_src); } }gif-0.10.2/.cargo_vcs_info.json0000644000000001120000000000000117120ustar00{ "git": { "sha1": "8318addc1f48ba7c92392fc5a4e4e42f2f167703" } }