Skip to main content

sixel/
encoder.rs

1use sixel::*;
2use status;
3use optflags;
4use pixelformat::{Pixel, PixelFormatChan};
5
6use std::cell::Cell;
7use status::Status;
8use std::os::raw;
9use std::path::Path;
10
11pub struct Encoder {
12    encoder: *mut sixel::Encoder,
13}
14
15impl Encoder {
16    pub fn new() -> Status<Encoder> {
17        use std::ptr;
18
19        let mut encoder: *mut sixel::Encoder  = ptr::null_mut() as *mut _;
20
21        unsafe {
22            let result = sixel_encoder_new(&mut encoder,
23                                           ptr::null_mut() as *mut Allocator);
24
25            status::from_libsixel(result)?;
26        }
27
28
29        Ok(Encoder { encoder })
30    }
31
32    #[deprecated]
33    pub fn create() -> Encoder {
34        let encoder;
35        unsafe {
36            encoder = sixel_encoder_create();
37        }
38
39        Encoder { encoder }
40
41    }
42
43    pub fn encode_file(&self, source: &Path) -> Status<()> {
44        use msc;
45
46        let cstr = msc::path_to_c_str(source)?;
47
48        let result = unsafe { sixel_encoder_encode(self.encoder, cstr.as_ptr()) };
49        status::from_libsixel(result)
50    }
51
52    pub fn encode_bytes(&self, frame: QuickFrame) -> Status<()> {
53        use std::os::raw::c_int;
54        use std::os::raw::c_uchar;
55
56        // Not used?
57        let palette: Vec<crate::pixelformat::Color3> = vec![];
58
59        let result = unsafe {
60            sixel_encoder_encode_bytes(self.encoder,
61                                       frame.pixels.as_ptr() as *mut c_uchar,
62                                       frame.width as c_int,
63                                       frame.height as c_int,
64                                       frame.format,
65                                       palette.as_ptr() as *mut c_uchar,
66                                       palette.len() as c_int)
67        };
68        status::from_libsixel(result)
69    }
70}
71
72// Optflags
73impl Encoder {
74    pub fn set_cancel(&self, cancel: Canceller) -> Status<()> {
75        let result =
76            unsafe { sixel_encoder_set_cancel_flag(self.encoder, (&cancel.flag).as_ptr()) };
77        status::from_libsixel(result)
78    }
79
80    fn set_opt(&self, opt: Optflag, arg: *const raw::c_char) -> Status<()> {
81        let result = unsafe { sixel_encoder_setopt(self.encoder, opt, arg) };
82        status::from_libsixel(result)
83    }
84
85    pub fn set_output(&self, file: &Path) -> Status<()> {
86        use msc;
87
88        let cstr = msc::path_to_c_str(file)?;
89
90        self.set_opt(Optflag::OutFile, cstr.as_ptr())
91    }
92
93    pub fn set_bit_mode(&self, mode: optflags::BitMode) -> Status<()> {
94        use std::ptr;
95        use optflags::BitMode;
96
97        let mode_flag = match mode {
98            BitMode::SevenBit => Optflag::UseSevenBitMode,
99            BitMode::EightBit => Optflag::UseEightBitMode,
100        };
101
102        self.set_opt(mode_flag, ptr::null())
103    }
104
105    pub fn enable_gri_arg_limit(&self) -> Status<()> {
106        use std::ptr;
107
108        self.set_opt(Optflag::HasGRIArgLimit, ptr::null())
109    }
110
111    pub fn set_num_colors_str(&self, num_colors: &str) -> Status<()> {
112        use std::ffi::CString;
113
114        let cstr = match CString::new(num_colors.as_bytes()) {
115            Ok(s) => s,
116            Err(_) => return Err(status::Error::BadArgument),
117        };
118
119        self.set_opt(Optflag::NumColors, cstr.as_ptr())
120    }
121
122    // Calls Encoder::set_colors, but allocates a new String
123    pub fn set_num_colors(&self, num_colors: u8) -> Status<()> {
124        self.set_num_colors_str(&num_colors.to_string())
125    }
126
127    pub fn set_color_option<'a>(&self, option: optflags::ColorOption<'a>) -> Status<()> {
128        use optflags::ColorOption::*;
129        match option {
130            Monochrome => self.use_monochrome(),
131            Builtin(palette) => self.use_builtin_palette(palette),
132            Mapfile(file) => self.use_mapfile(file),
133            Highcolor => self.use_high_color(),
134
135        }
136    }
137
138    fn use_mapfile(&self, file: &Path) -> Status<()> {
139        use msc;
140
141        let cstr = msc::path_to_c_str(file)?;
142
143        self.set_opt(Optflag::Mapfile, cstr.as_ptr())
144    }
145
146    fn use_monochrome(&self) -> Status<()> {
147        use std::ptr;
148
149        self.set_opt(Optflag::Monochrome, ptr::null())
150    }
151
152    fn use_high_color(&self) -> Status<()> {
153        use std::ptr;
154
155        self.set_opt(Optflag::UseHighColor, ptr::null())
156    }
157
158    fn use_builtin_palette(&self, option: &str) -> Status<()> {
159        use std::ffi::CString;
160
161        let cstr = match CString::new(option.as_bytes()) {
162            Ok(s) => s,
163            Err(_) => return Err(status::Error::BadArgument),
164        };
165
166        self.set_opt(Optflag::BuiltinPalette, cstr.as_ptr())
167    }
168
169    pub fn set_diffusion_str(&self, method: &str) -> Status<()> {
170        use std::ffi::CString;
171
172        let cstr = match CString::new(method.as_bytes()) {
173            Ok(s) => s,
174            Err(_) => return Err(status::Error::BadArgument),
175        };
176
177        self.set_opt(Optflag::Diffusion, cstr.as_ptr())
178    }
179
180    pub fn set_diffusion(&self, method: optflags::DiffusionMethod) -> Status<()> {
181        self.set_diffusion_str(method.to_str())
182    }
183
184    pub fn set_find_largest_str(&self, option: &str) -> Status<()> {
185        use std::ffi::CString;
186
187        let cstr = match CString::new(option.as_bytes()) {
188            Ok(s) => s,
189            Err(_) => return Err(status::Error::BadArgument),
190        };
191
192        self.set_opt(Optflag::FindLargest, cstr.as_ptr())
193    }
194
195    pub fn set_find_largest(&self, opt: optflags::FindLargestOpt) -> Status<()> {
196        self.set_find_largest_str(opt.to_str())
197    }
198
199    pub fn set_color_select_str(&self, opt: &str) -> Status<()> {
200        use std::ffi::CString;
201
202        let cstr = match CString::new(opt.as_bytes()) {
203            Ok(s) => s,
204            Err(_) => return Err(status::Error::BadArgument),
205        };
206
207        self.set_opt(Optflag::SelectColor, cstr.as_ptr())
208    }
209
210    pub fn set_color_select(&self, meth: optflags::ColorSelectionMethod) -> Status<()> {
211        self.set_color_select_str(meth.to_str())
212    }
213
214    pub fn set_crop_str(&self, crop: &str) -> Status<()> {
215        use std::ffi::CString;
216
217        let cstr = match CString::new(crop.as_bytes()) {
218            Ok(s) => s,
219            Err(_) => return Err(status::Error::BadArgument),
220        };
221
222        self.set_opt(Optflag::CropRegion, cstr.as_ptr())
223    }
224
225    pub fn set_crop(&self, width: i64, height: i64, x: i64, y: i64) -> Status<()> {
226        let crop_str = format!("{}x{}+{}+{}", width, height, x, y);
227        self.set_crop_str(&crop_str)
228    }
229
230    pub fn set_width_str(&self, width: &str) -> Status<()> {
231        use std::ffi::CString;
232
233        let cstr = match CString::new(width.as_bytes()) {
234            Ok(s) => s,
235            Err(_) => return Err(status::Error::BadArgument),
236        };
237
238        self.set_opt(Optflag::Width, cstr.as_ptr())
239    }
240
241    pub fn set_width(&self, width: optflags::SizeSpecification) -> Status<()> {
242        self.set_width_str(&width.to_string())
243    }
244
245    pub fn set_height_str(&self, height: &str) -> Status<()> {
246        use std::ffi::CString;
247
248        let cstr = match CString::new(height.as_bytes()) {
249            Ok(s) => s,
250            Err(_) => return Err(status::Error::BadArgument),
251        };
252
253        self.set_opt(Optflag::Height, cstr.as_ptr())
254    }
255
256    pub fn set_height(&self, height: optflags::SizeSpecification) -> Status<()> {
257        self.set_height_str(&height.to_string())
258    }
259
260    pub fn set_resampling_str(&self, meth: &str) -> Status<()> {
261        use std::ffi::CString;
262
263        let cstr = match CString::new(meth.as_bytes()) {
264            Ok(s) => s,
265            Err(_) => return Err(status::Error::BadArgument),
266        };
267
268        self.set_opt(Optflag::Resampling, cstr.as_ptr())
269    }
270
271    pub fn set_resampling(&self, meth: optflags::ResampleMethod) -> Status<()> {
272        self.set_resampling_str(meth.to_str())
273    }
274
275    pub fn set_quality_str(&self, opt: &str) -> Status<()> {
276        use std::ffi::CString;
277
278        let cstr = match CString::new(opt.as_bytes()) {
279            Ok(s) => s,
280            Err(_) => return Err(status::Error::BadArgument),
281        };
282
283        self.set_opt(Optflag::QualityMode, cstr.as_ptr())
284    }
285
286    pub fn set_quality(&self, opt: optflags::Quality) -> Status<()> {
287        self.set_quality_str(opt.to_str())
288    }
289
290    pub fn set_loopmode_str(&self, mode: &str) -> Status<()> {
291        use std::ffi::CString;
292
293        let cstr = match CString::new(mode.as_bytes()) {
294            Ok(s) => s,
295            Err(_) => return Err(status::Error::BadArgument),
296        };
297
298        self.set_opt(Optflag::LoopMode, cstr.as_ptr())
299    }
300
301    pub fn set_loopmode(&self, mode: optflags::LoopMode) -> Status<()> {
302        self.set_loopmode_str(mode.to_str())
303    }
304
305    pub fn set_palette_type_str(&self, opt: &str) -> Status<()> {
306        use std::ffi::CString;
307
308        let cstr = match CString::new(opt.as_bytes()) {
309            Ok(s) => s,
310            Err(_) => return Err(status::Error::BadArgument),
311        };
312
313        self.set_opt(Optflag::PaletteType, cstr.as_ptr())
314    }
315
316    pub fn set_palette_type(&self, opt: optflags::PaletteType) -> Status<()> {
317        self.set_palette_type_str(opt.to_str())
318    }
319
320    pub fn set_background_color_str(&self, color: &str) -> Status<()> {
321        use std::ffi::CString;
322
323        let cstr = match CString::new(color.as_bytes()) {
324            Ok(s) => s,
325            Err(_) => return Err(status::Error::BadArgument),
326        };
327
328        self.set_opt(Optflag::BackgroundColor, cstr.as_ptr())
329    }
330
331    pub fn set_background_color(&self, red: u8, green: u8, blue: u8) -> Status<()> {
332        let color_str = format!("#{:0>3}{:0>3}{:0>3}", red, green, blue);
333
334        self.set_background_color_str(&color_str)
335    }
336
337    pub fn use_insecure(&self) -> Status<()> {
338        use std::ptr;
339
340        self.set_opt(Optflag::Insecure, ptr::null())
341    }
342
343    pub fn use_invert(&self) -> Status<()> {
344        use std::ptr;
345
346        self.set_opt(Optflag::InvertBackground, ptr::null())
347    }
348
349    pub fn use_macro(&self) -> Status<()> {
350        use std::ptr;
351
352        self.set_opt(Optflag::UseMacro, ptr::null())
353    }
354
355    pub fn set_macro_number_str(&self, num: &str) -> Status<()> {
356        use std::ffi::CString;
357
358        let cstr = match CString::new(num.as_bytes()) {
359            Ok(s) => s,
360            Err(_) => return Err(status::Error::BadArgument),
361        };
362
363        self.set_opt(Optflag::UseMacro, cstr.as_ptr())
364    }
365
366    pub fn set_macro_number(&self, num: i64) -> Status<()> {
367        let num_str = format!("{}", num);
368        self.set_macro_number_str(&num_str)
369    }
370
371    pub fn ignore_delay(&self) -> Status<()> {
372        use std::ptr;
373
374        self.set_opt(Optflag::IgnoreGIFDelay, ptr::null())
375    }
376
377    pub fn use_verbose(&self) -> Status<()> {
378        use std::ptr;
379
380        self.set_opt(Optflag::Verbose, ptr::null())
381    }
382
383    pub fn use_static(&self) -> Status<()> {
384        use std::ptr;
385
386        self.set_opt(Optflag::StaticGIF, ptr::null())
387    }
388
389    pub fn use_penetrate(&self) -> Status<()> {
390        use std::ptr;
391
392        self.set_opt(Optflag::PenetrateScreen, ptr::null())
393    }
394
395    pub fn set_encode_policy_str(&self, pol: &str) -> Status<()> {
396        use std::ffi::CString;
397
398        let cstr = match CString::new(pol.as_bytes()) {
399            Ok(s) => s,
400            Err(_) => return Err(status::Error::BadArgument),
401        };
402
403        self.set_opt(Optflag::EncodingPolicy, cstr.as_ptr())
404    }
405
406    pub fn set_encode_policy(&self, pol: optflags::EncodePolicy) -> Status<()> {
407        self.set_encode_policy_str(pol.to_str())
408    }
409
410    pub fn set_complexion_score_str(&self, score: &str) -> Status<()> {
411        use std::ffi::CString;
412
413        let cstr = match CString::new(score.as_bytes()) {
414            Ok(s) => s,
415            Err(_) => return Err(status::Error::BadArgument),
416        };
417
418        self.set_opt(Optflag::ComplexionScore, cstr.as_ptr())
419    }
420
421    pub fn set_complexion_score(&self, score: i64) -> Status<()> {
422        let score_str = format!("{}", score);
423        self.set_complexion_score_str(&score_str)
424    }
425
426    pub fn use_pipe_mode(&self) -> Status<()> {
427        use std::ptr;
428
429        self.set_opt(Optflag::PipeInput, ptr::null())
430    }
431}
432
433impl Clone for Encoder {
434    fn clone(&self) -> Encoder {
435        unsafe {
436            sixel_encoder_ref(self.encoder);
437        }
438
439        Encoder { encoder: self.encoder }
440    }
441}
442
443impl Drop for Encoder {
444    fn drop(&mut self) {
445        unsafe {
446            sixel_encoder_unref(self.encoder);
447        }
448    }
449}
450
451
452// TODO: Get working with stack values
453pub struct Canceller {
454    flag: Box<Cell<raw::c_int>>,
455}
456
457impl Canceller {
458    pub fn new() -> Canceller {
459        let flag: Box<Cell<raw::c_int>> = Box::new(Cell::new(0));
460        // let flag_raw = Box::into_raw(flag);
461        // let flag;
462        // unsafe {
463        //     flag: &'a Cell<raw::c_int> = &*flag_raw;
464        // }
465
466        Canceller { flag }
467    }
468
469    pub fn cancel(&self) {
470        self.flag.set(1);
471        // unsafe {
472        //     // *self.flag = 1;
473        // }
474    }
475
476    pub fn reset(&self) {
477        self.flag.set(0);
478        // unsafe {
479        //     // *self.flag = 0;
480        // }
481    }
482}
483
484// impl<'a> Drop for Canceller<'a> {
485//     fn drop(&mut self) {
486//         unsafe {
487//             Box::from_raw(self.flag.as_ptr());
488//         }
489//     }
490// }
491
492pub struct QuickFrameBuilder {
493    width: usize,
494    height: usize,
495    format: PixelFormat,
496}
497
498impl QuickFrameBuilder {
499    pub fn new() -> QuickFrameBuilder {
500        QuickFrameBuilder {
501            width: 0,
502            height: 0,
503            format: PixelFormat::RGB888,
504        }
505    }
506
507    pub fn width(mut self, width: usize) -> QuickFrameBuilder {
508        self.width = width;
509        self
510    }
511    pub fn height(mut self, height: usize) -> QuickFrameBuilder {
512        self.height = height;
513        self
514    }
515    pub fn format(mut self, format: PixelFormat) -> QuickFrameBuilder {
516        self.format = format;
517        self
518    }
519
520    pub fn finalize(self) -> QuickFrame {
521        let depth = self.format.channels_per_pixel() as usize;
522        let size = self.width * self.height * depth;
523        let pixels: Vec<u8> = Vec::with_capacity(size);
524
525        QuickFrame {
526            width: self.width,
527            height: self.height,
528            format: self.format,
529            pixels,
530        }
531    }
532    pub fn pixels(self, pixels: Vec<u8>) -> QuickFrame {
533        let depth = self.format.channels_per_pixel() as usize;
534        let size = self.width * self.height * depth;
535
536        assert_eq!(size, pixels.len());
537
538        QuickFrame {
539            width: self.width,
540            height: self.height,
541            format: self.format,
542            pixels,
543        }
544    }
545}
546
547pub struct QuickFrame {
548    pixels: Vec<u8>,
549    width: usize,
550    height: usize,
551    format: PixelFormat,
552}
553
554impl QuickFrame {
555    pub fn row(&self, row: usize) -> &[u8] {
556        let row_len = self.width * self.format.channels_per_pixel() as usize;
557
558        let row_start = (row - 1) * row_len;
559        let next_row_start = row * row_len;
560
561        &self.pixels[row_start..next_row_start]
562    }
563    pub fn row_mut(&mut self, row: usize) -> &mut [u8] {
564        let row_len = self.width * self.format.channels_per_pixel() as usize;
565
566        let row_start = (row - 1) * row_len;
567        let next_row_start = row * row_len;
568
569        &mut self.pixels[row_start..next_row_start]
570    }
571
572    pub fn pixel(&self, row: usize, column: usize) -> &[u8] {
573        let depth = self.format.channels_per_pixel() as usize;
574        let row_len = self.width * depth;
575
576        let row_start = (row - 1) * row_len;
577        let col_pos = column * depth;
578
579        let start = row_start + col_pos;
580        let end = start + depth;
581
582        &self.pixels[start..end]
583    }
584
585    pub fn pixel_mut(&mut self, row: usize, column: usize) -> &mut [u8] {
586        let depth = self.format.channels_per_pixel() as usize;
587        let row_len = self.width * depth;
588
589        let row_start = (row - 1) * row_len;
590        let col_pos = column * depth;
591
592        let start = row_start + col_pos;
593        let end = start + depth;
594
595        &mut self.pixels[start..end]
596    }
597
598    pub fn color(&self, row: usize, column: usize, depth: usize) -> u8 {
599        let pix_depth = self.format.channels_per_pixel() as usize;
600
601        assert!(depth < pix_depth,
602                "Gave a depth of {} when a pixel is only {} bytes deep",
603                depth,
604                pix_depth);
605
606        let row_len = self.width * pix_depth;
607
608        let row_start = (row - 1) * row_len;
609        let col_pos = column * pix_depth;
610
611        self.pixels[row_start + col_pos + depth]
612    }
613
614    pub fn set_color(&mut self, row: usize, column: usize, depth: usize, color: u8) {
615        let pix_depth = self.format.channels_per_pixel() as usize;
616
617        assert!(depth < pix_depth,
618                "Gave a depth of {} when a pixel is only {} bytes deep",
619                depth,
620                pix_depth);
621
622        let row_len = self.width * pix_depth;
623
624        let row_start = (row - 1) * row_len;
625        let col_pos = column * pix_depth;
626
627        self.pixels[row_start + col_pos + depth] = color;
628    }
629}