pixlzr/data_types/
block.rs

1use std::slice::ChunksExact;
2
3/// ImageBlock
4/// - x: u32
5/// - y: u32
6/// - block: PixlzrBlock
7///   + Raw: PixlzrBlockRaw
8///     - width: u32
9///     - height: u32
10///     - block_value: Option<f32>
11///     - data: RawImage
12///       - alpha: bool
13///       - width: u32
14///       - height: u32
15///       - data: Vec<u8>
16///   + Image: PixlzrBlockImage
17///     - width: u32
18///     - height: u32
19///     - block_value: Option<f32>
20///     - data: DynamicImage
21///
22/// PixlzrBlockRaw <-> PixlzrBlockImage
23/// - PixlzrBlock(Image) -> PixlzrBlockImage
24/// - PixlzrBlock(Raw) -> PixlzrBlockImage
25///
26/// - PixlzrBlock(Raw) -> PixlzrBlockRaw
27/// - PixlzrBlock(Image) -> PixlzrBlockRaw
28/// PixlzrBlock.into() -> PixlzrBlock::{Raw, Image}
29
30#[cfg(feature = "fir")]
31use fast_image_resize::{
32	FilterType as FIR_FilterType, PixelType as FIR_PixelType, ResizeAlg,
33	Resizer,
34};
35
36#[cfg(feature = "image-rs")]
37use image::{
38	imageops::FilterType as I_FilterType, DynamicImage, RgbImage,
39	RgbaImage,
40};
41
42/// Image block representation, with:
43/// - `x: u32, y: u32` as the coordinates of the block
44/// - `block: PixlzrBlock` as the sub-image
45pub struct ImageBlock {
46	pub x: u32,
47	pub y: u32,
48	pub block: PixlzrBlock,
49}
50
51#[derive(Clone, Debug)]
52pub struct RawImage {
53	pub alpha: bool,
54	pub width: u32,
55	pub height: u32,
56	pub data: Vec<u8>,
57}
58
59#[derive(Clone, Debug)]
60pub struct PixlzrBlockRaw {
61	pub width: u32,
62	pub height: u32,
63	pub block_value: Option<f32>,
64	pub data: RawImage,
65}
66
67#[cfg(feature = "image-rs")]
68#[derive(Clone, Debug)]
69pub struct PixlzrBlockImage {
70	pub width: u32,
71	pub height: u32,
72	pub block_value: Option<f32>,
73	pub data: DynamicImage,
74}
75
76#[derive(Clone, Debug)]
77pub enum PixlzrBlock {
78	Raw(PixlzrBlockRaw),
79	#[cfg(feature = "image-rs")]
80	Image(PixlzrBlockImage),
81}
82
83impl From<PixlzrBlockRaw> for PixlzrBlock {
84	fn from(value: PixlzrBlockRaw) -> Self {
85		PixlzrBlock::Raw(value)
86	}
87}
88#[cfg(feature = "image-rs")]
89impl From<PixlzrBlockImage> for PixlzrBlock {
90	fn from(value: PixlzrBlockImage) -> Self {
91		PixlzrBlock::Image(value)
92	}
93}
94
95#[cfg(feature = "image-rs")]
96impl From<PixlzrBlock> for PixlzrBlockImage {
97	fn from(value: PixlzrBlock) -> Self {
98		match value {
99			PixlzrBlock::Image(image) => image,
100			PixlzrBlock::Raw(raw) => {
101				let (width, height, img) =
102					(raw.width, raw.height, raw.data);
103				let buf = img.data;
104				let data: DynamicImage = if img.alpha {
105					RgbaImage::from_raw(width, height, buf).unwrap().into()
106				} else {
107					RgbImage::from_raw(width, height, buf).unwrap().into()
108				};
109				Self {
110					width,
111					height,
112					data,
113					block_value: raw.block_value,
114				}
115			}
116		}
117	}
118}
119
120impl From<PixlzrBlock> for PixlzrBlockRaw {
121	fn from(value: PixlzrBlock) -> Self {
122		match value {
123			PixlzrBlock::Raw(raw) => raw,
124			#[cfg(feature = "image-rs")]
125			PixlzrBlock::Image(image) => {
126				let (width, height, img) =
127					(image.width, image.height, image.data);
128				let data = RawImage {
129					width,
130					height,
131					alpha: img.as_rgba8().is_some(),
132					data: img.into_bytes(),
133				};
134				Self {
135					width,
136					height,
137					block_value: image.block_value,
138					data,
139				}
140			}
141		}
142	}
143}
144
145impl PixlzrBlock {
146	pub fn width(&self) -> u32 {
147		match self {
148			#[cfg(feature = "image-rs")]
149			PixlzrBlock::Image(block) => block.width,
150			PixlzrBlock::Raw(block) => block.width,
151		}
152	}
153	pub fn height(&self) -> u32 {
154		match self {
155			#[cfg(feature = "image-rs")]
156			PixlzrBlock::Image(block) => block.height,
157			PixlzrBlock::Raw(block) => block.height,
158		}
159	}
160	pub fn dimensions(&self) -> (u32, u32) {
161		(self.width(), self.height())
162	}
163	pub fn block_value(&self) -> Option<f32> {
164		match self {
165			#[cfg(feature = "image-rs")]
166			PixlzrBlock::Image(block) => block.block_value,
167			PixlzrBlock::Raw(block) => block.block_value,
168		}
169	}
170	pub fn has_alpha(&self) -> bool {
171		match self {
172			PixlzrBlock::Raw(raw) => raw.data.alpha,
173			#[cfg(feature = "image-rs")]
174			PixlzrBlock::Image(img) => img.data.color().has_alpha(),
175		}
176	}
177	pub fn block_value_was_calculated(&self) -> bool {
178		self.block_value().is_some()
179	}
180	pub fn as_slice(&self) -> &[u8] {
181		match self {
182			#[cfg(feature = "image-rs")]
183			PixlzrBlock::Image(image) => image.data.as_bytes(),
184			PixlzrBlock::Raw(raw) => raw.data.data.as_slice(),
185		}
186	}
187	pub fn set_block_value(&mut self, value: f32) {
188		match self {
189			#[cfg(feature = "image-rs")]
190			PixlzrBlock::Image(image) => image.block_value = Some(value),
191			PixlzrBlock::Raw(raw) => raw.block_value = Some(value),
192		}
193	}
194}
195
196#[allow(clippy::match_wildcard_for_single_variants)]
197impl PixlzrBlock {
198	pub fn as_image(&self) -> Option<&PixlzrBlockImage> {
199		match self {
200			#[cfg(feature = "image-rs")]
201			PixlzrBlock::Image(image) => Some(image),
202			_ => None,
203		}
204	}
205	pub fn as_raw(&self) -> Option<&PixlzrBlockRaw> {
206		match self {
207			PixlzrBlock::Raw(raw) => Some(raw),
208			_ => None,
209		}
210	}
211	pub fn is_image(&self) -> bool {
212		match self {
213			#[cfg(feature = "image-rs")]
214			PixlzrBlock::Image(_) => true,
215			_ => false,
216		}
217	}
218	pub fn is_raw(&self) -> bool {
219		matches!(self, PixlzrBlock::Raw(_))
220	}
221}
222
223impl PixlzrBlock {
224	pub fn pixels(&self) -> ChunksExact<u8> {
225		let chunk_size = 3 + self.has_alpha() as usize;
226		match self {
227			#[cfg(feature = "image-rs")]
228			PixlzrBlock::Image(image) => {
229				image.data.as_bytes().chunks_exact(chunk_size)
230			}
231			PixlzrBlock::Raw(raw) => {
232				raw.data.data.chunks_exact(chunk_size)
233			}
234		}
235	}
236
237	pub fn resize(
238		&self,
239		width: u32,
240		height: u32,
241		filter: I_FilterType,
242	) -> Self {
243		if self.dimensions() == (width, height) {
244			return self.clone();
245		}
246		#[cfg(feature = "image-rs")]
247		#[cfg(not(feature = "fir"))]
248		{
249			let mut img = PixlzrBlockImage::from(self);
250			img.width = width;
251			img.height = height;
252			img.data = img.data.resize_exact(width, height, filter);
253			return img.into();
254		}
255
256		use fast_image_resize::{images::Image, ResizeOptions};
257
258		let alpha = self.has_alpha();
259		let pixel_type = if alpha {
260			FIR_PixelType::U8x4
261		} else {
262			FIR_PixelType::U8x3
263		};
264
265		let mut dst_image = Image::new(width, height, pixel_type);
266
267		let resize_alg = filter_type_to_fir_resizing_alg(
268			filter,
269			width > self.width() || height > self.height(),
270			2,
271		);
272
273		let mut resizer = Resizer::new();
274		let mut bytes = self.as_slice().to_owned();
275		resizer
276			.resize(
277				&Image::from_slice_u8(
278					self.width(),
279					self.height(),
280					&mut bytes,
281					pixel_type,
282				)
283				.unwrap(),
284				&mut dst_image,
285				&ResizeOptions::new().resize_alg(resize_alg),
286			)
287			.unwrap();
288
289		PixlzrBlockRaw {
290			width,
291			height,
292			block_value: None,
293			data: RawImage {
294				alpha,
295				width,
296				height,
297				data: dst_image.into_vec(),
298			},
299		}
300		.into()
301	}
302}
303
304#[cfg(feature = "fir")]
305fn filter_type_to_fir_resizing_alg(
306	filter: I_FilterType,
307	upscale: bool,
308	multiplicity: u8,
309) -> ResizeAlg {
310	match filter {
311		I_FilterType::Nearest => ResizeAlg::Nearest,
312		f if upscale => match f {
313			I_FilterType::Triangle => ResizeAlg::SuperSampling(
314				FIR_FilterType::Bilinear,
315				multiplicity,
316			),
317			I_FilterType::Lanczos3 => ResizeAlg::SuperSampling(
318				FIR_FilterType::Lanczos3,
319				multiplicity,
320			),
321			I_FilterType::Gaussian => ResizeAlg::SuperSampling(
322				FIR_FilterType::Gaussian,
323				multiplicity,
324			),
325			I_FilterType::CatmullRom => ResizeAlg::SuperSampling(
326				FIR_FilterType::CatmullRom,
327				multiplicity,
328			),
329			_ => unreachable!(),
330		},
331		f => match f {
332			I_FilterType::Triangle => {
333				ResizeAlg::Convolution(FIR_FilterType::Hamming)
334			}
335			I_FilterType::Lanczos3 => {
336				ResizeAlg::Convolution(FIR_FilterType::Lanczos3)
337			}
338			I_FilterType::Gaussian => {
339				ResizeAlg::Convolution(FIR_FilterType::Gaussian)
340			}
341			I_FilterType::CatmullRom => {
342				ResizeAlg::Convolution(FIR_FilterType::CatmullRom)
343			}
344			_ => unreachable!(),
345		},
346	}
347}