skia_safe/encode_/
png_encoder.rs

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    // TODO: If SkGainmapInfo get out of private/ : fGainmap fGainmapInfo
29}
30
31impl Default for Options {
32    fn default() -> Self {
33        Self {
34            filter_flags: FilterFlag::ALL,
35            z_lib_level: 6,
36            comments: vec![],
37        }
38    }
39}
40
41impl Options {
42    fn comments_to_data_table(&self) -> Option<DataTable> {
43        let mut comments = Vec::with_capacity(self.comments.len() * 2);
44        for c in self.comments.iter() {
45            comments.push(CString::new(c.keyword.as_str()).ok()?);
46            comments.push(CString::new(c.text.as_str()).ok()?);
47        }
48        let slices: Vec<&[u8]> = comments.iter().map(|c| c.as_bytes_with_nul()).collect();
49        Some(DataTable::from_slices(&slices))
50    }
51}
52
53#[derive(Debug, Clone, PartialEq, Eq)]
54pub struct Comment {
55    pub keyword: String,
56    pub text: String,
57}
58
59impl Comment {
60    pub fn new(keyword: impl Into<String>, text: impl Into<String>) -> Self {
61        Self {
62            keyword: keyword.into(),
63            text: text.into(),
64        }
65    }
66}
67
68pub fn encode<W: io::Write>(pixmap: &Pixmap, writer: &mut W, options: &Options) -> bool {
69    let Some(comments) = options.comments_to_data_table() else {
70        return false;
71    };
72
73    let mut stream = RustWStream::new(writer);
74
75    unsafe {
76        sb::C_SkPngEncoder_Encode(
77            stream.stream_mut(),
78            pixmap.native(),
79            comments.into_ptr(),
80            options.filter_flags.into_native(),
81            options.z_lib_level,
82        )
83    }
84}
85
86pub fn encode_image<'a>(
87    context: impl Into<Option<&'a mut crate::gpu::DirectContext>>,
88    img: &crate::Image,
89    options: &Options,
90) -> Option<crate::Data> {
91    crate::Data::from_ptr(unsafe {
92        sb::C_SkPngEncoder_EncodeImage(
93            context.into().native_ptr_or_null_mut(),
94            img.native(),
95            options.comments_to_data_table()?.into_ptr(),
96            options.filter_flags.into_native(),
97            options.z_lib_level,
98        )
99    })
100}
101
102// TODO: Make