1use std::{ffi::CString, io};
2
3use crate::{interop::RustWStream, prelude::*, DataTable, Pixmap};
4use skia_bindings as sb;
5
6bitflags! {
7 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
8 pub struct FilterFlag: u32 {
9 const ZERO = sb::SkPngEncoder_FilterFlag::kZero as _;
10 const NONE = sb::SkPngEncoder_FilterFlag::kNone as _;
11 const SUB = sb::SkPngEncoder_FilterFlag::kSub as _;
12 const UP = sb::SkPngEncoder_FilterFlag::kUp as _;
13 const AVG = sb::SkPngEncoder_FilterFlag::kAvg as _;
14 const PAETH = sb::SkPngEncoder_FilterFlag::kPaeth as _;
15 const ALL = Self::NONE.bits() | Self::SUB.bits() | Self::UP.bits() | Self::AVG.bits() | Self::PAETH.bits();
16 }
17}
18native_transmutable!(sb::SkPngEncoder_FilterFlag, FilterFlag, filter_flag_layout);
19
20#[derive(Debug, Clone, PartialEq, Eq)]
21#[non_exhaustive]
22pub struct Options {
23 pub filter_flags: FilterFlag,
24 pub z_lib_level: i32,
25 pub comments: Vec<Comment>,
26 // TODO: ICCProfile
27 // TODO: ICCProfileDescription
28}
29
30impl Default for Options {
31 fn default() -> Self {
32 Self {
33 filter_flags: FilterFlag::ALL,
34 z_lib_level: 6,
35 comments: vec![],
36 }
37 }
38}
39
40impl Options {
41 fn comments_to_data_table(&self) -> Option<DataTable> {
42 let mut comments: Vec = Vec::with_capacity(self.comments.len() * 2);
43 for c: &Comment in self.comments.iter() {
44 comments.push(CString::new(c.keyword.as_str()).ok()?);
45 comments.push(CString::new(c.text.as_str()).ok()?);
46 }
47 let slices: Vec<&[u8]> = comments.iter().map(|c: &CString| c.as_bytes_with_nul()).collect();
48 Some(DataTable::from_slices(&slices))
49 }
50}
51
52#[derive(Debug, Clone, PartialEq, Eq)]
53pub struct Comment {
54 pub keyword: String,
55 pub text: String,
56}
57
58impl Comment {
59 pub fn new(keyword: impl Into<String>, text: impl Into<String>) -> Self {
60 Self {
61 keyword: keyword.into(),
62 text: text.into(),
63 }
64 }
65}
66
67pub fn encode<W: io::Write>(pixmap: &Pixmap, writer: &mut W, options: &Options) -> bool {
68 let Some(comments: RCHandle) = options.comments_to_data_table() else {
69 return false;
70 };
71
72 let mut stream: RustWStream<'_> = RustWStream::new(writer);
73
74 unsafe {
75 sb::C_SkPngEncoder_Encode(
76 stream.stream_mut(),
77 pixmap:pixmap.native(),
78 comments:comments.into_ptr(),
79 filterFlags:options.filter_flags.into_native(),
80 zLibLevel:options.z_lib_level,
81 )
82 }
83}
84
85pub fn encode_image<'a>(
86 context: impl Into<Option<&'a mut crate::gpu::DirectContext>>,
87 img: &crate::Image,
88 options: &Options,
89) -> Option<crate::Data> {
90 crate::Data::from_ptr(unsafe {
91 sb::C_SkPngEncoder_EncodeImage(
92 ctx:context.into().native_ptr_or_null_mut(),
93 img:img.native(),
94 comments:options.comments_to_data_table()?.into_ptr(),
95 filterFlags:options.filter_flags.into_native(),
96 zLibLevel:options.z_lib_level,
97 )
98 })
99}
100
101// TODO: Make
102