]> git.nihav.org Git - nihav.git/blobdiff - nihav-commonfmt/src/codecs/gifenc.rs
gifenc: support grayscale input directly
[nihav.git] / nihav-commonfmt / src / codecs / gifenc.rs
index 6bb9263055606df4ffd1832160a3b915c1c8f256..475323516d3ad191bc5aded641848ac1b8529fc7 100644 (file)
@@ -2,6 +2,16 @@ use nihav_core::codecs::*;
 use nihav_core::io::byteio::*;
 use nihav_core::io::bitwriter::*;
 
+const GRAY_FORMAT: NAPixelFormaton = NAPixelFormaton {
+        model: ColorModel::YUV(YUVSubmodel::YUVJ),
+        components: 1,
+        comp_info: [Some(NAPixelChromaton{h_ss: 0, v_ss: 0, packed: false, depth: 8, shift: 0, comp_offs: 0, next_elem: 1}), None, None, None, None],
+        elem_size: 1,
+        be: true,
+        alpha: false,
+        palette: false,
+    };
+
 #[derive(Clone,Copy,Default,PartialEq)]
 enum CompressionLevel {
     None,
@@ -134,7 +144,12 @@ impl LZWEncoder {
 
         match self.level {
             CompressionLevel::None => {
+                let sym_limit = 1 << (clr_bits + 1);
                 for &b in src.iter() {
+                    if self.dict.cur_size >= sym_limit {
+                        bw.write(u32::from(self.dict.clear_code), self.dict.bit_len);
+                        self.dict.reset();
+                    }
                     bw.write(u32::from(b), self.dict.bit_len);
                     self.dict.add(usize::from(b), 0);
                 }
@@ -206,6 +221,7 @@ struct GIFEncoder {
     first:      bool,
     width:      usize,
     height:     usize,
+    grayscale:  bool,
     lzw:        LZWEncoder,
     p_trans:    bool,
     tr_idx:     Option<u8>,
@@ -223,6 +239,7 @@ impl GIFEncoder {
             first:      true,
             width:      0,
             height:     0,
+            grayscale:  false,
             lzw:        LZWEncoder::new(),
             p_trans:    false,
             tr_idx:     None,
@@ -254,7 +271,8 @@ impl NAEncoder for GIFEncoder {
             },
             NACodecTypeInfo::Audio(_) => Err(EncoderError::FormatError),
             NACodecTypeInfo::Video(vinfo) => {
-                let outinfo = NAVideoInfo::new(vinfo.width, vinfo.height, false, PAL8_FORMAT);
+                let format = if vinfo.format == GRAY_FORMAT { GRAY_FORMAT } else { PAL8_FORMAT };
+                let outinfo = NAVideoInfo::new(vinfo.width, vinfo.height, false, format);
                 let mut ofmt = *encinfo;
                 ofmt.format = NACodecTypeInfo::Video(outinfo);
                 Ok(ofmt)
@@ -270,8 +288,12 @@ impl NAEncoder for GIFEncoder {
                 if vinfo.width > 65535 || vinfo.height > 65535 {
                     return Err(EncoderError::FormatError);
                 }
+                if vinfo.format != PAL8_FORMAT && vinfo.format != GRAY_FORMAT {
+                    return Err(EncoderError::FormatError);
+                }
                 self.width  = vinfo.width;
                 self.height = vinfo.height;
+                self.grayscale = vinfo.format == GRAY_FORMAT;
 
                 let edata = self.tr_idx.map(|val| vec![val]);
 
@@ -314,11 +336,19 @@ impl NAEncoder for GIFEncoder {
 
                 let cur_pal = &src[buf.get_offset(1)..][..768];
                 if self.first {
-                    self.pal.copy_from_slice(cur_pal);
+                    if !self.grayscale {
+                        self.pal.copy_from_slice(cur_pal);
+                    } else {
+                        for (i, pal) in self.pal.chunks_exact_mut(3).enumerate() {
+                            pal[0] = i as u8;
+                            pal[1] = i as u8;
+                            pal[2] = i as u8;
+                        }
+                    }
                 }
 
                 let mut pal_changed = false;
-                if !self.first {
+                if !self.first && !self.grayscale {
                     let mut used = [false; 256];
                     for &b in self.cur_frm.iter() {
                         used[usize::from(b)] = true;
@@ -454,13 +484,25 @@ impl NAEncoder for GIFEncoder {
         self.pkt = Some(NAPacket::new(self.stream.clone().unwrap(), frm.ts, self.first, dbuf));
         self.first = false;
 
-        if let NABufferType::Video(ref buf) = frm.get_buffer() {
-            let paloff = buf.get_offset(1);
-            let data = buf.get_data();
+        if !self.grayscale {
+            if let NABufferType::Video(ref buf) = frm.get_buffer() {
+                let paloff = buf.get_offset(1);
+                let data = buf.get_data();
+                let mut pal = [0; 1024];
+                let srcpal = &data[paloff..][..768];
+                for (dclr, sclr) in pal.chunks_exact_mut(4).zip(srcpal.chunks_exact(3)) {
+                    dclr[..3].copy_from_slice(sclr);
+                }
+                if let Some(ref mut pkt) = &mut self.pkt {
+                    pkt.side_data.push(NASideData::Palette(true, Arc::new(pal)));
+                }
+            }
+        } else {
             let mut pal = [0; 1024];
-            let srcpal = &data[paloff..][..768];
-            for (dclr, sclr) in pal.chunks_exact_mut(4).zip(srcpal.chunks_exact(3)) {
-                dclr[..3].copy_from_slice(sclr);
+            for (i, quad) in pal.chunks_exact_mut(4).enumerate() {
+                quad[0] = i as u8;
+                quad[1] = i as u8;
+                quad[2] = i as u8;
             }
             if let Some(ref mut pkt) = &mut self.pkt {
                 pkt.side_data.push(NASideData::Palette(true, Arc::new(pal)));
@@ -636,7 +678,7 @@ mod test {
         let enc_options = &[
                 NAOption { name: "compr", value: NAValue::String("none".to_string()) },
             ];
-        test_gif_encoder_single("none.gif", enc_options, &[0x2767a289, 0xdef9ad30, 0xca4c289b, 0x1fd0ec19]);
+        test_gif_encoder_single("none.gif", enc_options, &[0x32900cff, 0xef979bb0, 0x2d0355e8, 0x424bddee]);
     }
     #[test]
     fn test_gif_single_fast() {