]> git.nihav.org Git - nihav-encoder.git/commitdiff
allow low-bits palette modes as well master
authorKostya Shishkov <kostya.shishkov@gmail.com>
Sat, 25 Apr 2026 16:33:17 +0000 (18:33 +0200)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Sat, 25 Apr 2026 16:33:17 +0000 (18:33 +0200)
src/palettise.rs
src/transcoder.rs

index 5342c1c5e4416810bab4d598be915d5e35074251..23cdc7746deae83a92bb8a895cc9092310d5fe2c 100644 (file)
@@ -218,6 +218,7 @@ enum PMode {
 pub struct Palettiser {
     pal:    [[u8; 3]; 256],
     nclrs:  usize,
+    nbits:  u8,
     pmode:  PMode,
 }
 
@@ -249,7 +250,7 @@ impl LookupCache {
 #[allow(dead_code)]
 impl Palettiser {
     pub fn get_default_mode() -> PaletteSearchMode { PaletteSearchMode::Full }
-    pub fn new(mode: PaletteSearchMode, pal: &[[u8; 3]; 256], nclrs: usize) -> Self {
+    pub fn new(mode: PaletteSearchMode, pal: &[[u8; 3]; 256], nclrs: usize, nbits: u8) -> Self {
         let pmode = match mode {
                 PaletteSearchMode::Full => {
                     let mut tab = vec![0; 1 << 24];
@@ -259,7 +260,7 @@ impl Palettiser {
                 PaletteSearchMode::Local => PMode::Local(LocalSearch::new(pal)),
                 PaletteSearchMode::KDTree => PMode::Tree(KDTree::new(pal)),
             };
-        Self { pal: *pal, nclrs, pmode }
+        Self { pal: *pal, nclrs, nbits, pmode }
     }
     pub fn search(&self, pix: [u8; 3]) -> usize {
         match &self.pmode {
@@ -271,15 +272,17 @@ impl Palettiser {
             PMode::Tree(kdt) => kdt.search(pix),
         }
     }
-    pub fn set_pal(&mut self, pal: &[[u8; 3]; 256], nclrs: usize) {
+    pub fn set_pal(&mut self, pal: &[[u8; 3]; 256], nclrs: usize, nbits: u8) {
         self.pal.copy_from_slice(pal);
         self.nclrs = nclrs;
+        self.nbits = nbits;
         match &mut self.pmode {
             PMode::Full(ref mut tab) => { gen_full_lut(tab, &pal[..nclrs]); },
             PMode::Local(ref mut ls) => { *ls = LocalSearch::new(&pal[..nclrs]); },
             PMode::Tree(ref mut kdt) => { *kdt = KDTree::new(&pal[..nclrs]); },
         }
     }
+    pub fn get_pal_bits(&self) -> u8 { self.nbits }
     pub fn palettise_frame(&self, pic_in: &NABufferType, pic_out: &mut NABufferType) -> Result<(), &'static str> {
 // todo remap already paletted format
         if matches!(pic_in, NABufferType::None) {
@@ -382,7 +385,7 @@ pub fn create_palettiser(enc_opts: &[OptionArgs]) -> Option<Palettiser> {
     let mut pmode = None;
     let mut pal = std::array::from_fn(|i| [i as u8; 3]);
     let mut pal_is_some = false;
-    let mut nclrs = 256;
+    let mut nclrs = 256usize;
     for opt in enc_opts.iter() {
         match opt.name.as_str() {
             "pal.mode" => {
@@ -463,11 +466,12 @@ pub fn create_palettiser(enc_opts: &[OptionArgs]) -> Option<Palettiser> {
             _ => {},
         }
     }
+    let nbits = nclrs.ilog2() as u8;
     for clr in pal.iter_mut().skip(nclrs) {
         *clr = [0; 3];
     }
     if pmode.is_some() || pal_is_some {
-        Some(Palettiser::new(pmode.unwrap_or_default(), &pal, nclrs))
+        Some(Palettiser::new(pmode.unwrap_or_default(), &pal, nclrs, nbits))
     } else {
         None
     }
@@ -707,7 +711,7 @@ impl BucketCounter {
     }
 }
 
-pub type PalSegment = (usize, [[u8; 3]; 256], usize);
+pub type PalSegment = (usize, [[u8; 3]; 256], usize, u8);
 
 struct MultiCount {
     glbl_hist:  [u64; 1 << 15],
@@ -780,6 +784,7 @@ pub struct ColourCounter {
     debug:      bool,
     multi:      Option<Box<MultiCount>>,
     nclrs:      usize,
+    nbits:      u8,
 }
 
 impl ColourCounter {
@@ -788,6 +793,7 @@ impl ColourCounter {
         let mut debug = false;
         let mut multi = None;
         let mut nclrs = 256;
+        let mut nbits = 8;
         for opt in options.iter() {
             match opt.name.as_str() {
                 "counter" => {
@@ -861,9 +867,25 @@ impl ColourCounter {
                         println!("palette size requires a numeric argument");
                     }
                 },
+                "bits" | "pal_bits" => {
+                    if let Some(val) = opt.value.as_deref() {
+                        if let Ok(pb) = val.parse::<u8>() {
+                            if (1..=8).contains(&pb) {
+                                nbits = pb;
+                            } else {
+                                println!("palette bits should be in range 1..8");
+                            }
+                        } else {
+                            println!("palette bits requires a numeric argument");
+                        }
+                    } else {
+                        println!("palette bits requires a numeric argument");
+                    }
+                },
                 _ => {},
             }
         }
+        nclrs = nclrs.min(1 << nbits);
         let ctype = match counter_type {
                 "full" => CounterType::Brawn(vec![0; 1 << 24]),
                 "bucket" => CounterType::Buckets(BucketCounter::new(nclrs)),
@@ -873,7 +895,7 @@ impl ColourCounter {
             scaler:     None,
             sc_buf:     NABufferType::None,
             oinfo:      NAVideoInfo{ width: 0, height: 0, flipped: false, format: RGB24_FORMAT, bits: 24 },
-            ctype, debug, multi, nclrs,
+            ctype, debug, multi, nclrs, nbits,
         }
     }
     pub fn add(&mut self, clr: [u8; 3]) {
@@ -920,7 +942,7 @@ impl ColourCounter {
             let (pal, nclrs) = self.get_pal();
             self.reset();
             if let Some(ref mut multi) = self.multi {
-                multi.pals.push((multi.fstart, pal, nclrs));
+                multi.pals.push((multi.fstart, pal, nclrs, self.nbits));
                 multi.fstart = multi.frameno - 1;
             }
         }
@@ -965,6 +987,7 @@ impl ColourCounter {
             Err("not a video frame")
         }
     }
+    pub fn get_pal_bits(&self) -> u8 { self.nbits }
     pub fn get_pal(&self) -> ([[u8; 3]; 256], usize) {
         match self.ctype {
             CounterType::Buckets(ref bkt) => bkt.get_pal(self.debug),
@@ -994,13 +1017,14 @@ impl ColourCounter {
     }
     pub fn get_multi_pals(&self) -> Vec<PalSegment> {
         let (last_pal, nclrs) = self.get_pal();
+        let nbits = self.get_pal_bits();
         if let Some(ref multi) = self.multi {
             let mut pals = multi.pals.clone();
             if multi.fstart < multi.frameno {
-                pals.push((multi.fstart, last_pal, nclrs));
+                pals.push((multi.fstart, last_pal, nclrs, nbits));
             }
             return pals;
         }
-        vec![(0, last_pal, nclrs)]
+        vec![(0, last_pal, nclrs, nbits)]
     }
 }
index b70981e9a4fe4b45ae72dfee85e0796b9cdae288..1cff30c0833134beffeb6e6063c3347c1903b9c8 100644 (file)
@@ -247,8 +247,8 @@ impl EncoderInterface for VideoEncodeContext {
         if let Some(ref mut plt) = self.plt {
             if !self.pals.is_empty() && !matches!(buf, NABufferType::None) {
                 if self.pal_frm >= self.pals[0].0 {
-                    let (_, pal, nclrs) = self.pals.remove(0);
-                    plt.set_pal(&pal, nclrs);
+                    let (_, pal, nclrs, nbits) = self.pals.remove(0);
+                    plt.set_pal(&pal, nclrs, nbits);
                 }
                 self.pal_frm += 1;
             }
@@ -980,9 +980,9 @@ impl Transcoder {
         for (src_id, pals) in qsupp.pals.iter() {
             if *src_id == iidx && !pals.is_empty() {
                 if let Some(ref mut p) = plt {
-                    p.set_pal(&pals[0].1, pals[0].2);
+                    p.set_pal(&pals[0].1, pals[0].2, pals[0].3);
                 } else {
-                    plt = Some(Palettiser::new(Palettiser::get_default_mode(), &pals[0].1, pals[0].2));
+                    plt = Some(Palettiser::new(Palettiser::get_default_mode(), &pals[0].1, pals[0].2, pals[0].3));
                 }
             }
         }
@@ -990,8 +990,10 @@ impl Transcoder {
         if plt.is_some() {
             if let NACodecTypeInfo::Video(ref mut vinfo) = oopts.enc_params.format {
                 if !vinfo.format.is_paletted() {
-                    println!("  warning: output stream did not specify paletted output, setting pixfmt=pal8");
+                    let pal_bpp = plt.as_ref().map(|pp| pp.get_pal_bits()).unwrap_or(8);
+                    println!("  warning: output stream did not specify paletted output, setting pixfmt=pal{pal_bpp}");
                     vinfo.format = PAL8_FORMAT;
+                    vinfo.bits = pal_bpp;
                 }
             }
         }