1#![deny(missing_docs)]
30#![allow(
31 clippy::cast_lossless,
32 clippy::cast_possible_truncation,
33 clippy::cast_sign_loss
34)]
35use std::fmt::Debug;
36
37#[cfg(feature = "decoding")]
38mod decoding;
39#[cfg(feature = "encoding")]
40mod encoding;
41
42#[cfg(any(feature = "decoding", feature = "encoding"))]
43mod helpers;
44#[cfg(test)]
45mod tests;
46
47#[cfg(feature = "decoding")]
48pub use decoding::{Error, decode_png};
49#[cfg(feature = "encoding")]
50pub use encoding::{CompressionLevel, PngEncodingOptions, encode_png};
51
52#[derive(Debug, PartialEq, Eq, Clone, Copy)]
53pub enum ImageType {
55 R8,
57 R16,
59 Ra8,
61 Ra16,
63 Rgb8,
65 Rgba8,
67 Rgb16,
69 Rgba16,
71}
72
73impl ImageType {
74 #[must_use]
76 pub const fn is_16_bit(&self) -> bool {
77 !matches!(self, Self::R8 | Self::Ra8 | Self::Rgb8 | Self::Rgba8)
78 }
79}
80
81#[derive(PartialEq, Eq, Clone)]
82pub struct Image {
84 pub width: u32,
86 pub height: u32,
88 pub img_type: ImageType,
90 pub data: Vec<u8>,
92}
93
94#[allow(clippy::missing_fields_in_debug)]
95impl Debug for Image {
96 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 f.debug_struct("Image")
98 .field("width", &self.width)
99 .field("height", &self.height)
100 .field("img_type", &self.img_type)
101 .finish()
102 }
103}
104
105impl Image {
106 pub fn add_alpha(&mut self) {
108 match self.img_type {
109 ImageType::R8 => {
110 self.img_type = ImageType::Ra8;
111 self.data = self.data.iter().flat_map(|i| [*i, 0xff]).collect();
112 }
113 ImageType::R16 => {
114 self.img_type = ImageType::Ra16;
115 self.data = self
116 .data
117 .chunks(2)
118 .flat_map(|i| [i[0], i[1], 0xff, 0xff])
119 .collect();
120 }
121 ImageType::Rgb8 => {
122 self.img_type = ImageType::Rgba8;
123 self.data = self
124 .data
125 .chunks(3)
126 .flat_map(|c| [c[0], c[1], c[2], 0xff])
127 .collect();
128 }
129 ImageType::Rgb16 => {
130 self.img_type = ImageType::Rgba16;
131 self.data = self
132 .data
133 .chunks(6)
134 .flat_map(|c| [c[0], c[1], c[2], c[3], c[4], c[5], 0xff, 0xff])
135 .collect();
136 }
137 _ => {}
138 }
139 }
140
141 pub fn add_channels(&mut self) {
143 match self.img_type {
144 ImageType::R8 => {
145 self.img_type = ImageType::Rgb8;
146 self.data = self.data.iter().copied().flat_map(|i| [i, i, i]).collect();
147 }
148 ImageType::R16 => {
149 self.img_type = ImageType::Rgb16;
150 self.data = self
151 .data
152 .chunks(2)
153 .flat_map(|i| [i[0], i[1], i[0], i[1], i[0], i[1]])
154 .collect();
155 }
156 ImageType::Ra8 => {
157 self.img_type = ImageType::Rgba8;
158 self.data = self
159 .data
160 .chunks(2)
161 .flat_map(|i| [i[0], i[0], i[0], i[1]])
162 .collect();
163 }
164 ImageType::Ra16 => {
165 self.img_type = ImageType::Rgba16;
166 self.data = self
167 .data
168 .chunks(4)
169 .flat_map(|i| [i[0], i[1], i[0], i[1], i[0], i[1], i[2], i[3]])
170 .collect();
171 }
172 _ => {}
173 }
174 }
175}