1 //! Common common used both by decoder and encoder 2 extern crate color_quant; 3 4 use std::borrow::Cow; 5 6 /// Disposal method 7 #[derive(Debug, Copy, Clone, PartialEq, Eq)] 8 #[repr(u8)] 9 pub enum DisposalMethod { 10 /// StreamingDecoder is not required to take any action. 11 Any = 0, 12 /// Do not dispose. 13 Keep = 1, 14 /// Restore to background color. 15 Background = 2, 16 /// Restore to previous. 17 Previous = 3, 18 } 19 20 impl DisposalMethod { 21 /// Converts `u8` to `Option<Self>` from_u8(n: u8) -> Option<DisposalMethod>22 pub fn from_u8(n: u8) -> Option<DisposalMethod> { 23 match n { 24 0 => Some(DisposalMethod::Any), 25 1 => Some(DisposalMethod::Keep), 26 2 => Some(DisposalMethod::Background), 27 3 => Some(DisposalMethod::Previous), 28 _ => None 29 } 30 } 31 } 32 33 /// Known GIF block labels. 34 /// 35 /// Note that the block uniquely specifies the layout of bytes that follow and how they are 36 /// framed. For example, the header always has a fixed length but is followed by a variable amount 37 /// of additional data. An image descriptor may be followed by a local color table depending on 38 /// information read in it. Therefore, it doesn't make sense to continue parsing after encountering 39 /// an unknown block as the semantics of following bytes are unclear. 40 /// 41 /// The extension block provides a common framing for an arbitrary amount of application specific 42 /// data which may be ignored. 43 #[derive(Debug, Copy, Clone, PartialEq, Eq)] 44 #[repr(u8)] 45 pub enum Block { 46 /// Image block. 47 Image = 0x2C, 48 /// Extension block. 49 Extension = 0x21, 50 /// Image trailer. 51 Trailer = 0x3B, 52 } 53 54 impl Block { 55 /// Converts `u8` to `Option<Self>` from_u8(n: u8) -> Option<Block>56 pub fn from_u8(n: u8) -> Option<Block> { 57 match n { 58 0x2C => Some(Block::Image), 59 0x21 => Some(Block::Extension), 60 0x3B => Some(Block::Trailer), 61 _ => None 62 } 63 } 64 } 65 66 /// A newtype wrapper around an arbitrary extension ID. 67 /// 68 /// An extension is some amount of byte data organized in sub-blocks so that one can skip over it 69 /// without knowing the semantics. Though technically you likely want to use a `Application` 70 /// extension, the library tries to stay flexible here. 71 /// 72 /// This allows us to customize the set of impls compared to a raw `u8`. It also clarifies the 73 /// intent and gives some inherent methods for interoperability with known extension types. 74 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 75 pub struct AnyExtension(pub u8); 76 77 /// Known GIF extension labels. 78 /// 79 /// These are extensions which may be interpreted by the library and to which a specification with 80 /// the internal data layout is known. 81 #[derive(Debug, Copy, Clone, PartialEq, Eq)] 82 #[repr(u8)] 83 pub enum Extension { 84 /// Plain Text extension. 85 /// 86 /// This instructs the decoder to render a text as characters in a grid of cells, in a 87 /// mono-spaced font of its choosing. This is seldom actually implemented and ignored by 88 /// ImageMagick. The color is always taken from the global table which further complicates any 89 /// use. No real information on the frame sequencing of this block is available in the 90 /// standard. 91 Text = 0x01, 92 /// Control extension. 93 Control = 0xF9, 94 /// Comment extension. 95 Comment = 0xFE, 96 /// Application extension. 97 /// 98 /// See [ImageMagick] for an idea of commonly recognized extensions. 99 /// 100 /// [ImageMagick]: https://github.com/ImageMagick/ImageMagick/blob/b0b58c6303195928060f55f9c3ca8233ab7f7733/coders/gif.c#L1128 101 Application = 0xFF, 102 } 103 104 impl AnyExtension { 105 /// Decode the label as a known extension. into_known(self) -> Option<Extension>106 pub fn into_known(self) -> Option<Extension> { 107 Extension::from_u8(self.0) 108 } 109 } 110 111 impl From<Extension> for AnyExtension { from(ext: Extension) -> Self112 fn from(ext: Extension) -> Self { 113 AnyExtension(ext as u8) 114 } 115 } 116 117 impl Extension { 118 /// Converts `u8` to a `Extension` if it is known. from_u8(n: u8) -> Option<Extension>119 pub fn from_u8(n: u8) -> Option<Extension> { 120 match n { 121 0x01 => Some(Extension::Text), 122 0xF9 => Some(Extension::Control), 123 0xFE => Some(Extension::Comment), 124 0xFF => Some(Extension::Application), 125 _ => None 126 } 127 } 128 } 129 130 /// A GIF frame 131 #[derive(Debug, Clone)] 132 pub struct Frame<'a> { 133 /// Frame delay in units of 10 ms. 134 pub delay: u16, 135 /// Disposal method. 136 pub dispose: DisposalMethod, 137 /// Transparent index (if available). 138 pub transparent: Option<u8>, 139 /// True if the frame needs user input to be displayed. 140 pub needs_user_input: bool, 141 /// Offset from the top border of the canvas. 142 pub top: u16, 143 /// Offset from the left border of the canvas. 144 pub left: u16, 145 /// Width of the frame. 146 pub width: u16, 147 /// Height of the frame. 148 pub height: u16, 149 /// True if the image is interlaced. 150 pub interlaced: bool, 151 /// Frame local color palette if available. 152 pub palette: Option<Vec<u8>>, 153 /// Buffer containing the image data. 154 /// Only indices unless configured differently. 155 pub buffer: Cow<'a, [u8]> 156 } 157 158 impl<'a> Default for Frame<'a> { default() -> Frame<'a>159 fn default() -> Frame<'a> { 160 Frame { 161 delay: 0, 162 dispose: DisposalMethod::Keep, 163 transparent: None, 164 needs_user_input: false, 165 top: 0, 166 left: 0, 167 width: 0, 168 height: 0, 169 interlaced: false, 170 palette: None, 171 buffer: Cow::Borrowed(&[]) 172 } 173 } 174 } 175 176 impl Frame<'static> { 177 /// Creates a frame from pixels in RGBA format. 178 /// 179 /// This is a lossy method. The `gif` format does not support arbitrary alpha but only a 1-bit 180 /// transparency mask per pixel. Any non-zero alpha value will be interpreted as a fully opaque 181 /// pixel. Additionally, only 256 colors can appear in a single frame. The palette will be 182 /// reduced by the NeuQuant algorithm if necessary. Different frames have independent palettes. 183 /// 184 /// *Note: This method is not optimized for speed.* 185 /// 186 /// # Panics: 187 /// * If the length of pixels does not equal `width * height * 4`. from_rgba(width: u16, height: u16, pixels: &mut [u8]) -> Frame<'static>188 pub fn from_rgba(width: u16, height: u16, pixels: &mut [u8]) -> Frame<'static> { 189 Frame::from_rgba_speed(width, height, pixels, 1) 190 } 191 192 /// Creates a frame from pixels in RGBA format. 193 /// 194 /// `speed` is a value in the range [1, 30]. 195 /// The higher the value the faster it runs at the cost of image quality. 196 /// A `speed` of 10 is a good compromise between speed and quality. 197 /// 198 /// This is a lossy method. The `gif` format does not support arbitrary alpha but only a 1-bit 199 /// transparency mask per pixel. Any non-zero alpha value will be interpreted as a fully opaque 200 /// pixel. Additionally, only 256 colors can appear in a single frame. The palette will be 201 /// reduced by the NeuQuant algorithm if necessary. Different frames have independent palettes. 202 /// 203 /// # Panics: 204 /// * If the length of pixels does not equal `width * height * 4`. 205 /// * If `speed < 1` or `speed > 30` from_rgba_speed(width: u16, height: u16, pixels: &mut [u8], speed: i32) -> Frame<'static>206 pub fn from_rgba_speed(width: u16, height: u16, pixels: &mut [u8], speed: i32) -> Frame<'static> { 207 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"); 208 assert!(speed >= 1 && speed <= 30, "speed needs to be in the range [1, 30]"); 209 let mut frame = Frame::default(); 210 let mut transparent = None; 211 for pix in pixels.chunks_mut(4) { 212 if pix[3] != 0 { 213 pix[3] = 0xFF; 214 } else { 215 transparent = Some([pix[0], pix[1], pix[2], pix[3]]) 216 } 217 } 218 frame.width = width; 219 frame.height = height; 220 let nq = color_quant::NeuQuant::new(speed, 256, pixels); 221 frame.buffer = Cow::Owned(pixels.chunks(4).map(|pix| nq.index_of(pix) as u8).collect()); 222 frame.palette = Some(nq.color_map_rgb()); 223 frame.transparent = if let Some(t) = transparent { 224 Some(nq.index_of(&t) as u8) 225 } else { 226 None 227 }; 228 229 frame 230 } 231 232 /// Creates a frame from a palette and indexed pixels. 233 /// 234 /// # Panics: 235 /// * If the length of pixels does not equal `width * height`. 236 /// * If the length of palette > `256 * 3`. from_palette_pixels(width: u16, height: u16, pixels: &[u8], palette: &[u8], transparent: Option<u8>) -> Frame<'static>237 pub fn from_palette_pixels(width: u16, height: u16, pixels: &[u8], palette: &[u8], transparent: Option<u8>) -> Frame<'static> { 238 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"); 239 assert!(palette.len() <= 256*3, "Too many palette values to create a GIF Frame"); 240 let mut frame = Frame::default(); 241 242 frame.width = width; 243 frame.height = height; 244 245 frame.buffer = Cow::Owned(pixels.to_vec()); 246 frame.palette = Some(palette.to_vec()); 247 248 frame.transparent = transparent; 249 250 frame 251 } 252 253 /// Creates a frame from indexed pixels in the global palette. 254 /// 255 /// # Panics: 256 /// * If the length of pixels does not equal `width * height`. from_indexed_pixels(width: u16, height: u16, pixels: &[u8], transparent: Option<u8>) -> Frame<'static>257 pub fn from_indexed_pixels(width: u16, height: u16, pixels: &[u8], transparent: Option<u8>) -> Frame<'static> { 258 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"); 259 let mut frame = Frame::default(); 260 261 frame.width = width; 262 frame.height = height; 263 264 frame.buffer = Cow::Owned(pixels.to_vec()); 265 frame.palette = None; 266 267 frame.transparent = transparent; 268 269 frame 270 } 271 272 /// Creates a frame from pixels in RGB format. 273 /// 274 /// This is a lossy method. In the `gif` format only 256 colors can appear in a single frame. 275 /// The palette will be reduced by the NeuQuant algorithm if necessary. Different frames have 276 /// independent palettes. 277 /// 278 /// *Note: This method is not optimized for speed.* 279 /// 280 /// # Panics: 281 /// * If the length of pixels does not equal `width * height * 3`. from_rgb(width: u16, height: u16, pixels: &[u8]) -> Frame<'static>282 pub fn from_rgb(width: u16, height: u16, pixels: &[u8]) -> Frame<'static> { 283 Frame::from_rgb_speed(width, height, pixels, 1) 284 } 285 286 /// Creates a frame from pixels in RGB format. 287 /// 288 /// `speed` is a value in the range [1, 30]. 289 /// 290 /// This is a lossy method. In the `gif` format only 256 colors can appear in a single frame. 291 /// The palette will be reduced by the NeuQuant algorithm if necessary. Different frames have 292 /// independent palettes. 293 /// 294 /// The higher the value the faster it runs at the cost of image quality. 295 /// A `speed` of 10 is a good compromise between speed and quality. 296 /// 297 /// # Panics: 298 /// * If the length of pixels does not equal `width * height * 3`. 299 /// * If `speed < 1` or `speed > 30` from_rgb_speed(width: u16, height: u16, pixels: &[u8], speed: i32) -> Frame<'static>300 pub fn from_rgb_speed(width: u16, height: u16, pixels: &[u8], speed: i32) -> Frame<'static> { 301 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"); 302 let mut vec: Vec<u8> = Vec::with_capacity(pixels.len() + width as usize * height as usize); 303 for v in pixels.chunks(3) { 304 vec.extend([v[0], v[1], v[2], 0xFF].iter().cloned()) 305 } 306 Frame::from_rgba_speed(width, height, &mut vec, speed) 307 } 308 required_bytes(&self) -> usize309 pub(crate) fn required_bytes(&self) -> usize { 310 usize::from(self.width) * usize::from(self.height) 311 } 312 } 313