fast image operations
Diffstat (limited to 'src/lib.rs')
-rw-r--r--src/lib.rs904
1 files changed, 746 insertions, 158 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 9de0643..05cb526 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,86 +1,224 @@
//! # fimg
//!
//! Provides fast image operations, such as rotation, flipping, and overlaying.
+//!
+//! ## Organization
+//!
+//! Image types:
+//!
+//! - [`Image`]: the main image type.
+//! - [`DynImage`]: This is the image type you use when, say, loading a png. You should immediately convert this into a
+//! - [`ImageCloner`]: This is... a [`Image`], but about to be cloned. It just allows some simple out-of-place optimizations, that `.clone().op()` dont allow. (produce with [`Image::cloner`])
+//! - [`uninit::Image`]: A uninitialized image. Used for performance optimization.
+//!
+//! ### Operations
+//!
+//! Affine:
+//! - [`Image::rot_90`]
+//! - [`Image::rot_180`]
+//! - [`Image::rot_270`]
+//! - [`Image::flip_h`]
+//! - [`Image::flip_v`]
+//!
+//! Drawing:
+//! - [`Image::box`], [`Image::filled_box`], [`Image::stroked_box`]
+//! - [`Image::circle`], [`Image::border_circle`]
+//! - [`Image::line`], [`Image::thick_line`]
+//! - [`Image::points`]
+//! - [`Image::quad`]
+//! - [`Image::poly`], [`Image::border_poly`]
+//! - [`Image::tri`]
+//! - [`Image::text`]
+//!
+//! Scaling: [`Image::scale`]
+//!
+//! Misc image ops:
+//! - [`Image::repeated`]
+//! - [`Image::overlay`](Overlay), [`Image::overlay_at`](OverlayAt), [`Image::overlay_blended`](BlendingOverlay)
+//! - [`Image::blur`]
+//! - [`Image::crop`]
+//!
+//! ## feature flags
+//!
+//! - `scale`: enables the [`scale`] module.
+//! - `save`: enables [`Image::save`], via the [`png`](https://crates.io/crates/png) crate.
+//! - `text`: enables [`Image::text`], via the [`fontdue`](https://crates.io/crates/fontdue) crate.
+//! - `blur`: enables [`Image::blur`], via the [`stackblur`](https://crates.io/crates/stackblur-iter) crate.
+//! - `real-show`: [`Image::show`], if the `save` feature is enabled, will, by default, simply open the appropriate image viewing program.
+//! if, for some reason, this is inadequate/you dont have a good image viewer, enable the `real-show` feature to make [`Image::show`] open up a window of its own.
+//! without the `real-show` feature, [`Image::show`] will save itself to your temp directory, which you may not want.
+//! - `term`: [`term::print`]. this enables printing images directly to the terminal, if you don't want to open a window or something. supports `{iterm2, kitty, sixel, fallback}` graphics.
+//! - `default`: \[`save`, `scale`\].
+#![cfg_attr(all(feature = "term", windows), windows_subsystem = "console")]
#![feature(
+ type_changing_struct_update,
+ custom_inner_attributes,
slice_swap_unchecked,
generic_const_exprs,
- slice_as_chunks,
- unchecked_math,
+ proc_macro_hygiene,
+ iter_array_chunks,
+ const_trait_impl,
+ core_intrinsics,
+ new_range_api,
+ rustc_private,
portable_simd,
- const_option,
- array_chunks,
+ const_convert,
+ derive_const,
+ try_blocks,
+ const_cmp,
+ doc_cfg,
test
)]
#![warn(
- clippy::missing_docs_in_private_items,
- clippy::multiple_unsafe_ops_per_block,
clippy::undocumented_unsafe_blocks,
clippy::missing_const_for_fn,
clippy::missing_safety_doc,
+ clippy::suboptimal_flops,
unsafe_op_in_unsafe_fn,
clippy::dbg_macro,
+ clippy::use_self,
missing_docs
)]
-#![allow(clippy::zero_prefixed_literal, incomplete_features)]
-
-use std::{num::NonZeroU32, slice::SliceIndex};
+#![allow(
+ clippy::zero_prefixed_literal,
+ mixed_script_confusables,
+ incomplete_features,
+ confusable_idents,
+ internal_features
+)]
+use array_chunks::*;
+use hinted::HintExt;
+use std::{hint::assert_unchecked, intrinsics::transmute_unchecked, num::NonZeroU32, ops::Range};
mod affine;
+#[cfg(feature = "blur")]
+mod blur;
+mod slicing;
+pub use sub::{Cropper, SubImage};
pub mod builder;
+#[doc(hidden)]
+pub mod cloner;
+mod convert;
mod drawing;
-mod overlay;
+mod r#dyn;
+pub mod indexed;
+pub(crate) mod math;
+#[doc(hidden)]
+pub mod overlay;
+mod pack;
+mod span;
+mod sub;
+pub mod uninit;
+#[cfg(feature = "wgpu-convert")]
+mod wgpu_convert;
+pub use pack::Pack;
+pub mod pixels;
+#[cfg(feature = "scale")]
pub mod scale;
-pub use overlay::{Overlay, OverlayAt};
+#[cfg(any(feature = "save", feature = "real-show"))]
+mod show;
+#[cfg(feature = "term")]
+pub mod term;
+pub use cloner::ImageCloner;
+pub use r#dyn::DynImage;
+pub use overlay::{
+ BlendingOverlay, BlendingOverlayAt, ClonerOverlay, ClonerOverlayAt, Overlay, OverlayAt,
+ OverlayAtClipping,
+};
+
+trait CopyWithinUnchecked {
+ /// # Safety
+ ///
+ /// panicless version of [`[T]::copy_within`](`slice::copy_within`), where the slices cant overlap. this uses `memcpy`.
+ /// your slices must be in bounds.
+ /// this isnt a public function, so im not going to say exactly what "in bounds" means.
+ unsafe fn copy_within_unchecked(&mut self, src: Range<usize>, dest: usize);
+}
-/// like assert!(), but causes undefined behaviour at runtime when the condition is not met.
-///
-/// # Safety
-///
-/// UB if condition is false.
-macro_rules! assert_unchecked {
- ($cond:expr) => {{
- if !$cond {
- #[cfg(debug_assertions)]
- let _ = ::core::ptr::NonNull::<()>::dangling().as_ref(); // force unsafe wrapping block
- #[cfg(debug_assertions)]
- panic!("assertion failed: {} returned false", stringify!($cond));
- #[cfg(not(debug_assertions))]
- std::hint::unreachable_unchecked()
- }
- }};
+impl<T> CopyWithinUnchecked for [T] {
+ unsafe fn copy_within_unchecked(&mut self, src: Range<usize>, dest: usize) {
+ let std::ops::Range { start, end } = src;
+ debug_assert!(dest <= self.len() - end - start, "dest is out of bounds");
+ #[allow(clippy::multiple_unsafe_ops_per_block)]
+ // SAFETY: the caller better be good
+ unsafe {
+ let ptr = self.as_mut_ptr();
+ std::ptr::copy_nonoverlapping(ptr.add(start), ptr.add(dest), end - start)
+ };
+ }
+}
+
+trait At {
+ fn at<const C: usize>(self, x: u32, y: u32) -> usize;
+}
+
+impl At for (u32, u32) {
+ #[inline]
+ fn at<const C: usize>(self, x: u32, y: u32) -> usize {
+ #[allow(clippy::multiple_unsafe_ops_per_block)]
+ // SAFETY: me when uncheck math: 😧 (FIXME)
+ let index = unsafe {
+ // y * w + x
+ let tmp = (y as usize).unchecked_mul(self.0 as usize);
+ tmp.unchecked_add(x as usize)
+ };
+ // SAFETY: 🧐 is unsound? 😖
+ unsafe { index.unchecked_mul(C) }
+ }
}
-use assert_unchecked;
-impl Image<&[u8], 3> {
- /// Repeat self till it fills a new image of size x, y
+impl<T: AsRef<[u8]>> Image<T, 3> {
+ /// Tile self till it fills a new image of size x, y
/// # Safety
///
/// UB if self's width is not a multiple of x, or self's height is not a multiple of y
- pub unsafe fn repeated(&self, x: u32, y: u32) -> Image<Vec<u8>, 3> {
- let mut img = Image::alloc(x, y); // could probably optimize this a ton but eh
- for x in 0..(x / self.width()) {
- for y in 0..(y / self.height()) {
- let a: &mut Image<&mut [u8], 3> = &mut img.as_mut();
- // SAFETY: caller upholds
- unsafe { a.overlay_at(self, x * self.width(), y * self.height()) };
+ /// ```
+ /// # use fimg::Image;
+ /// let x: Image<&[u8], 3> = Image::build(8, 8).buf(include_bytes!("../benches/3_8x8.imgbuf"));
+ /// let tiled = unsafe { x.repeated(48, 48) }; // repeat 6 times
+ /// # assert_eq!(tiled.buffer(), b"\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xf7\xcb\xa4\xf7\xcb\xa4\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d\xd3\xae\x8d")
+ /// ```
+ #[must_use = "function does not modify the original image"]
+ pub unsafe fn repeated(&self, out_width: u32, out_height: u32) -> Image<Vec<u8>, 3> {
+ let mut img = uninit::Image::new(
+ out_width.try_into().unwrap(),
+ out_height.try_into().unwrap(),
+ );
+ debug_assert!(out_width % self.width() == 0);
+ debug_assert!(out_height % self.height() == 0);
+ for y in 0..self.height() {
+ // SAFETY: get one row of pixels
+ let from = unsafe {
+ self.buffer
+ .as_ref()
+ .get_unchecked(self.at(0, y)..self.at(0, y) + (self.width() as usize * 3))
+ };
+ debug_assert_eq!(from.len(), self.width() as usize * 3);
+ let first = (0, y)..(self.width(), y);
+ // SAFETY: copy it in
+ unsafe { img.write(from, first.clone()) };
+
+ for x in 1..(out_width / self.width()) {
+ let section = img.at(x * self.width(), y);
+ // SAFETY: copy each row of the image one by one
+ unsafe { img.copy_within(first.clone(), section) };
}
}
- img
+ let first_row = 0..img.at(0, self.height());
+ for y in 1..(out_height / self.height()) {
+ let this_row = img.at(0, y * self.height());
+ // SAFETY: copy entire blocks of image at a time
+ unsafe { img.copy_within(first_row.clone(), this_row) };
+ }
+ // SAFETY: we init
+ unsafe { img.assume_init() }
}
}
-/// calculates a column major index, with unchecked math
-#[inline]
-unsafe fn really_unsafe_index(x: u32, y: u32, w: u32) -> usize {
- // y * w + x
- // SAFETY: FIXME make safe math
- let tmp = unsafe { (y as usize).unchecked_mul(w as usize) };
- // SAFETY: FIXME make safe math
- unsafe { tmp.unchecked_add(x as usize) }
-}
-
/// A image with a variable number of channels, and a nonzero size.
-#[derive(Clone, Debug, PartialEq, Eq)]
+#[derive(Debug, Eq, Hash)]
+#[derive_const(PartialEq)]
+#[repr(C)]
pub struct Image<T, const CHANNELS: usize> {
/// column order 2d slice/vec
buffer: T,
@@ -90,17 +228,53 @@ pub struct Image<T, const CHANNELS: usize> {
height: NonZeroU32,
}
+impl<T: Clone, const CHANNELS: usize> Clone for Image<T, CHANNELS> {
+ /// Returns a duplicate of this image.
+ /// ```
+ /// # use fimg::Image;
+ /// # let i = Image::<Vec<_>, 1>::alloc(5,5);
+ /// let new_i = i.clone();
+ /// ```
+ /// If you find yourself in the pattern of
+ /// ```
+ /// # use fimg::Image;
+ /// # let i = Image::<Vec<_>, 1>::alloc(5,5);
+ /// let mut i = i.clone();
+ /// unsafe { i.rot_90() };
+ /// ```
+ /// STOP!
+ ///
+ /// Instead use
+ /// ```
+ /// # use fimg::Image;
+ /// # let i = Image::<Vec<_>, 1>::alloc(5,5);
+ /// let i = unsafe { i.cloner().rot_90() };
+ /// ```
+ fn clone(&self) -> Self {
+ Self {
+ buffer: self.buffer.clone(),
+ width: self.width,
+ height: self.height,
+ }
+ }
+}
+
impl<T, const CHANNELS: usize> Image<T, CHANNELS> {
+ #[doc(hidden)]
+ pub const fn channels(&self) -> usize {
+ CHANNELS
+ }
+
#[inline]
/// get the height as a [`u32`]
- pub fn height(&self) -> u32 {
- self.height.into()
+ pub const fn height(&self) -> u32 {
+ self.height.get()
}
#[inline]
/// get the width as a [`u32`]
- pub fn width(&self) -> u32 {
- self.width.into()
+ pub const fn width(&self) -> u32 {
+ self.width.get()
}
#[inline]
@@ -137,24 +311,59 @@ impl<T, const CHANNELS: usize> Image<T, CHANNELS> {
pub unsafe fn buffer_mut(&mut self) -> &mut T {
&mut self.buffer
}
+
+ /// # Safety
+ ///
+ /// the output index is not guaranteed to be in bounds
+ #[inline]
+ #[cfg_attr(debug_assertions, track_caller)]
+ pub fn at(&self, x: u32, y: u32) -> usize {
+ (self.width(), self.height()).at::<CHANNELS>(x, y)
+ }
+
+ /// # Safety
+ /// keep the buffer size the same
+ unsafe fn with<U, const N: usize>(&self, x: U) -> Image<U, N> {
+ unsafe { Image::new(self.width, self.height, x) }
+ }
+
+ /// # Safety
+ /// keep the buffer size the same
+ unsafe fn map<U, const N: usize, F: FnOnce(&T) -> U>(&self, f: F) -> Image<U, N> {
+ // SAFETY: we dont change anything, why check
+ unsafe { self.with(f(self.buffer())) }
+ }
+ #[doc(hidden)]
+ pub unsafe fn mapped<U, const N: usize, F: FnOnce(T) -> U>(self, f: F) -> Image<U, N> {
+ // SAFETY: we dont change anything, why check
+ unsafe { Image::new(self.width, self.height, f(self.buffer)) }
+ }
+
+ /// # Safety
+ /// buffer size must be the same.
+ unsafe fn map_into<U: From<T>, const N: usize>(self) -> Image<U, N> {
+ unsafe { self.mapped(Into::into) }
+ }
}
impl<const CHANNELS: usize, T: Clone> Image<&[T], CHANNELS> {
/// Allocate a new `Image<Vec<T>>` from this imageref.
pub fn to_owned(&self) -> Image<Vec<T>, CHANNELS> {
- // SAFETY: we have been constructed already, so must be valid
- unsafe { Image::new(self.width, self.height, self.buffer.to_vec()) }
+ // SAFETY: size not changed
+ unsafe { self.map(|b| b.to_vec()) }
}
}
impl<const CHANNELS: usize, T: Clone> Image<&mut [T], CHANNELS> {
/// Allocate a new `Image<Vec<T>>` from this mutable imageref.
pub fn to_owned(&self) -> Image<Vec<T>, CHANNELS> {
- // SAFETY: we have been constructed already, so must be valid
- unsafe { Image::new(self.width, self.height, self.buffer.to_vec()) }
+ // SAFETY: size not changed
+ unsafe { self.map(|b| b.to_vec()) }
}
}
+impl<const CHANNELS: usize> Copy for Image<&[u8], CHANNELS> {}
+
impl<const CHANNELS: usize> Image<&[u8], CHANNELS> {
#[inline]
#[must_use]
@@ -180,7 +389,7 @@ impl<const CHANNELS: usize> Image<&[u8], CHANNELS> {
/// ```
pub const fn make<'a, const WIDTH: u32, const HEIGHT: u32>() -> Image<&'a [u8], CHANNELS>
where
- [(); CHANNELS * WIDTH as usize * HEIGHT as usize]: Sized,
+ [(); CHANNELS * WIDTH as usize * HEIGHT as usize]:,
{
Image {
width: NonZeroU32::new(WIDTH).expect("passed zero width to builder"),
@@ -190,32 +399,155 @@ impl<const CHANNELS: usize> Image<&[u8], CHANNELS> {
}
}
-impl<T: std::ops::Deref<Target = [u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
+impl<const CHANNELS: usize, const N: usize, T> Image<[T; N], CHANNELS> {
+ /// Box this array image.
+ pub fn boxed(self) -> Image<Box<[T]>, CHANNELS> {
+ // SAFETY: size not changed
+ unsafe { self.map_into() }
+ }
+}
+
+impl<const CHANNELS: usize, T: Copy> Image<&[T], CHANNELS> {
+ /// Box this image.
+ pub fn boxed(self) -> Image<Box<[T]>, CHANNELS> {
+ // SAFETY: size not changed
+ unsafe { self.map_into() }
+ }
+}
+
+impl<const CHANNELS: usize, T> Image<Vec<T>, CHANNELS> {
+ /// Box this owned image.
+ pub fn boxed(self) -> Image<Box<[T]>, CHANNELS> {
+ // SAFETY: ctor
+ unsafe { self.map_into() }
+ }
+}
+
+impl<const CHANNELS: usize, T> Image<Box<[T]>, CHANNELS> {
+ /// Unbox this vec image.
+ pub fn unbox(self) -> Image<Vec<T>, CHANNELS> {
+ // SAFETY: ctor
+ unsafe { self.map_into() }
+ }
+}
+
+#[macro_export]
+/// Create a <code>[Image]<[[u8]; N], C></code> with ease. If your looking for a <code>[Image]<&'static [[u8]]></code>, try [`Image::make`].
+///
+/// ```
+/// let mut i = fimg::make!(4 channels 128 x 128);
+/// ```
+///
+/// Implementation note:
+/// This is doable with a const generic fn, but it returns a `fimg::Image<[u8; fimg::::{impl#7}::array::{constant#1}], _>` which means you cant actually type it, so its useless.
+macro_rules! make {
+ ($channels:literal channels $w:literal x $h: literal) => {
+ unsafe {
+ $crate::Image::<_, $channels>::new(
+ match ::core::num::NonZeroU32::new($w) {
+ ::core::option::Option::Some(n) => n,
+ ::core::option::Option::None => panic!("width is 0"),
+ },
+ match ::core::num::NonZeroU32::new($h) {
+ ::core::option::Option::Some(n) => n,
+ ::core::option::Option::None => panic!("height is 0"),
+ },
+ [0_u8; $channels * $w * $h],
+ )
+ }
+ };
+}
+
+impl<T, const CHANNELS: usize> Image<T, CHANNELS> {
+ /// The size of the underlying buffer.
+ #[allow(clippy::len_without_is_empty)]
+ pub fn len<U>(&self) -> usize
+ where
+ T: AsRef<[U]>,
+ {
+ self.buffer().as_ref().len()
+ }
+
+ /// Transforms the N
+ ///
/// # Safety
///
- /// - UB if x, y is out of bounds
- /// - UB if buffer is too small
+ /// i think you can see why this is a problem.
+ ///
+ /// # WHY???
+ ///
+ /// sometimes rust is silly with generics
+ unsafe fn trans<const N: usize>(self) -> Image<T, N> {
+ unsafe { transmute_unchecked(self) }
+ }
+ /// # Safety
+ ///
+ /// the output index is not guaranteed to be in bounds
#[inline]
- unsafe fn slice(&self, x: u32, y: u32) -> impl SliceIndex<[u8], Output = [u8]> {
- debug_assert!(x < self.width(), "x out of bounds");
- debug_assert!(y < self.height(), "y out of bounds");
- // SAFETY: me when uncheck math: 😧
- let index = unsafe { really_unsafe_index(x, y, self.width()) };
- // SAFETY: 🧐 is unsound? 😖
- let index = unsafe { index.unchecked_mul(CHANNELS) };
- debug_assert!(self.buffer.len() > index);
+ fn slice<U>(&self, x: u32, y: u32) -> Range<usize>
+ where
+ T: AsRef<[U]>,
+ {
+ let index = self.at(x, y);
// SAFETY: as long as the buffer isnt wrong, this is 😄
index..unsafe { index.unchecked_add(CHANNELS) }
}
#[inline]
/// Returns a iterator over every pixel
- pub fn chunked(&self) -> impl Iterator<Item = &[u8; CHANNELS]> {
+ pub fn chunked<'a, U: 'a>(
+ &'a self,
+ ) -> impl DoubleEndedIterator<Item = &'a [U; CHANNELS]> + ExactSizeIterator
+ where
+ T: AsRef<[U]>,
+ {
// SAFETY: 0 sized images illegal
- unsafe { assert_unchecked!(self.buffer.len() > CHANNELS) };
+ unsafe { assert_unchecked(self.len() >= CHANNELS) };
// SAFETY: no half pixels!
- unsafe { assert_unchecked!(self.buffer.len() % CHANNELS == 0) };
- self.buffer.array_chunks::<CHANNELS>()
+ unsafe { assert_unchecked(self.len() % CHANNELS == 0) };
+ self.buffer().as_ref().array_chunks::<CHANNELS>()
+ }
+
+ #[inline]
+ /// Flatten the chunks of this image into a slice of slices.
+ pub fn flatten<U>(&self) -> &[[U; CHANNELS]]
+ where
+ T: AsRef<[U]>,
+ {
+ // SAFETY: buffer cannot have half pixels
+ unsafe { self.buffer().as_ref().as_chunks_unchecked::<CHANNELS>() }
+ }
+
+ /// Create a mutref to this image
+ pub fn as_mut<U>(&mut self) -> Image<&mut [U], CHANNELS>
+ where
+ T: AsMut<[U]>,
+ {
+ // SAFETY: construction went okay
+ unsafe { Image::new(self.width, self.height, self.buffer.as_mut()) }
+ }
+
+ /// Reference this image.
+ pub fn as_ref<U>(&self) -> Image<&[U], CHANNELS>
+ where
+ T: AsRef<[U]>,
+ {
+ // SAFETY: we got constructed okay, parameters must be valid
+ unsafe { Image::new(self.width, self.height, self.buffer().as_ref()) }
+ }
+
+ /// Get a pixel. Optionally. Yeah!
+ pub fn get_pixel<U>(&self, x: u32, y: u32) -> Option<&[U; CHANNELS]>
+ where
+ T: AsRef<[U]>,
+ {
+ ((x < self.width()) & (y < self.height())).then(|| unsafe {
+ self.buffer()
+ .as_ref()
+ .get_unchecked(self.slice(x, y))
+ .as_array()
+ .unwrap_unchecked()
+ })
}
/// Return a pixel at (x, y).
@@ -224,38 +556,242 @@ impl<T: std::ops::Deref<Target = [u8]>, const CHANNELS: usize> Image<T, CHANNELS
/// - UB if x, y is out of bounds
/// - UB if buffer is too small
#[inline]
- pub unsafe fn pixel(&self, x: u32, y: u32) -> [u8; CHANNELS] {
- // SAFETY: we have been told x, y is in bounds
- let idx = unsafe { self.slice(x, y) };
- // SAFETY: slice always returns a valid index
- let ptr = unsafe { self.buffer.get_unchecked(idx).as_ptr().cast() };
- // SAFETY: slice always returns a length of `CHANNELS`, so we `cast()` it for convenience.
- unsafe { *ptr }
+ #[cfg_attr(debug_assertions, track_caller)]
+ pub unsafe fn pixel<U>(&self, x: u32, y: u32) -> &[U; CHANNELS]
+ where
+ T: AsRef<[U]>,
+ {
+ debug_assert!(x < self.width());
+ debug_assert!(y < self.height());
+
+ // SAFETY: x and y in bounds
+ unsafe { self.get_pixel(x, y).unwrap_unchecked() }
+ }
+
+ /// pixels contiguously from start to end
+ /// they gotta be in bounds
+ ///
+ /// i think this is unsound because you can make asref do whatever the fucking fuck you fucking want but thats fucking on you
+ #[cfg_attr(debug_assertions, track_caller)]
+ pub unsafe fn pixels<U: Copy>(&self, r: impl PBounds) -> &[[U; CHANNELS]]
+ where
+ T: AsRef<[U]>,
+ {
+ let b = self.bounds(r);
+ unsafe { self.buffer.as_ref().get_unchecked(b).as_chunks_unchecked() }
+ }
+ /// pixels contiguously from start to end
+ #[cfg_attr(debug_assertions, track_caller)]
+ pub unsafe fn pixels_mut<U: Copy>(&mut self, r: impl PBounds) -> &mut [[U; CHANNELS]]
+ where
+ T: AsRef<[U]> + AsMut<[U]>,
+ {
+ let b = self.bounds(r);
+ unsafe {
+ self.buffer
+ .as_mut()
+ .get_unchecked_mut(b)
+ .as_chunks_unchecked_mut()
+ }
+ }
+
+ /// Returns a [`PixelEntry`]
+ pub fn replace<U: Copy>(
+ &mut self,
+ x: u32,
+ y: u32,
+ f: impl FnOnce([U; CHANNELS]) -> [U; CHANNELS],
+ ) -> Option<[U; CHANNELS]>
+ where
+ T: AsRef<[U]> + AsMut<[U]>,
+ {
+ if x < self.width() && y < self.height() {
+ let x = unsafe { self.pixel_mut(x, y) };
+ let v = *x;
+ *x = f(v);
+ Some(v)
+ } else {
+ None
+ }
}
-}
-impl<T: std::ops::DerefMut<Target = [u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
/// Return a mutable reference to a pixel at (x, y).
/// # Safety
///
/// - UB if x, y is out of bounds
/// - UB if buffer is too small
#[inline]
- pub unsafe fn pixel_mut(&mut self, x: u32, y: u32) -> &mut [u8] {
+ #[cfg_attr(debug_assertions, track_caller)]
+ pub unsafe fn pixel_mut<U: Copy>(&mut self, x: u32, y: u32) -> &mut [U; CHANNELS]
+ where
+ T: AsMut<[U]> + AsRef<[U]>,
+ {
+ debug_assert!(x < self.width());
+ debug_assert!(y < self.height());
+
// SAFETY: we have been told x, y is in bounds.
- let idx = unsafe { self.slice(x, y) };
- // SAFETY: slice should always return a valid index
- unsafe { self.buffer.get_unchecked_mut(idx) }
+ unsafe { self.get_pixel_mut(x, y).unwrap_unchecked() }
+ }
+
+ /// Returns a mutable reference to a pixel at (x, y), if (x, y) is in bounds.
+ pub fn get_pixel_mut<U>(&mut self, x: u32, y: u32) -> Option<&mut [U; CHANNELS]>
+ where
+ T: AsMut<[U]> + AsRef<[U]>,
+ {
+ let sl = self.slice(x, y);
+ ((x < self.width()) & (y < self.height())).then(|| unsafe {
+ self.buffer_mut()
+ .as_mut()
+ .get_unchecked_mut(sl)
+ .as_mut_array()
+ .unwrap_unchecked()
+ })
}
+ /// iterator over columns
+ /// returned iterator returns a iterator for each column
+ ///
+ /// ```text
+ /// ┌ ┐┌ ┐┌ ┐
+ /// │1││2││3│
+ /// │4││5││6│
+ /// │7││8││9│
+ /// └ ┘└ ┘└ ┘
+ /// ```
+ ///
+ /// ```
+ /// # use fimg::Image;
+ /// let img: Image<&[u8],1> = Image::build(2, 3).buf(&[
+ /// 1, 5,
+ /// 2, 4,
+ /// 7, 9
+ /// ]);
+ /// assert_eq!(
+ /// img.cols().map(|x| x.collect::<Vec<_>>()).collect::<Vec<_>>(),
+ /// [[[1], [2], [7]], [[5], [4], [9]]]
+ /// );
+ /// ```
+ #[must_use = "iterators are lazy and do nothing unless consumed"]
+ pub fn cols<'a, U: Copy + 'a>(
+ &'a self,
+ ) -> impl DoubleEndedIterator
+ + ExactSizeIterator<
+ Item = impl ExactSizeIterator + DoubleEndedIterator<Item = &'a [U; CHANNELS]>,
+ >
+ where
+ T: AsRef<[U]>,
+ {
+ (0..self.width()).map(move |x| (0..self.height()).map(move |y| unsafe { self.pixel(x, y) }))
+ }
+
+ /// iterator over rows
+ /// returns a iterator over each row
+ /// ```text
+ /// [ 1 2 3 ]
+ /// [ 4 5 6 ]
+ /// [ 7 8 9 ]
+ /// ```
+ ///
+ /// ```
+ /// # use fimg::Image;
+ /// let img: Image<&[u8],1> = Image::build(2, 3).buf(&[
+ /// 1, 5,
+ /// 2, 4,
+ /// 7, 9
+ /// ]);
+ /// assert_eq!(
+ /// img.rows().collect::<Vec<_>>(),
+ /// [[[1], [5]], [[2], [4]], [[7], [9]]]
+ /// );
+ /// ```
+ #[must_use = "iterators are lazy and do nothing unless consumed"]
+ pub fn rows<'a, U: 'a>(
+ &'a self,
+ ) -> impl ExactSizeIterator + DoubleEndedIterator<Item = &'a [[U; CHANNELS]]>
+ where
+ T: AsRef<[U]>,
+ {
+ self.flatten().chunks_exact(self.width() as usize)
+ }
+
+ /// Itearte the pixels of this image in parse order.
+ /// use [`Image::chunked`] if you just want the pixels.
+ pub fn ordered(
+ &self,
+ ) -> impl ExactSizeIterator + DoubleEndedIterator<Item = (u32, u32)> + use<T, CHANNELS> {
+ let w = self.width();
+ unsafe {
+ (0..self.height())
+ .flat_map(move |y| (0..w).map(move |x| (x, y)))
+ .has(self.width() as usize * self.height() as usize)
+ }
+ }
+
+ /// Iterate the pixels of this image in serpentine order.
+ ///
+ /// # Safety
+ ///
+ /// The points are guaranteed to be on the image.
+ pub fn serpent(
+ &self,
+ ) -> impl ExactSizeIterator + Iterator<Item = (u32, u32)> + use<T, CHANNELS> {
+ let w = self.width();
+ unsafe {
+ (0..self.height() / 2)
+ .flat_map(move |y| {
+ std::iter::chain(
+ (0..w).map(move |x| (x, y * 2)),
+ (0..w).rev().map(move |x| (x, (y * 2) + 1)),
+ )
+ })
+ .take(self.width() as usize * self.height() as usize)
+ .has(self.width() as usize * self.height() as usize)
+ }
+ }
+
+ /// Get the pixels from an iterator.
+ /// # Safety
+ /// the points must be on the image.
+ pub unsafe fn pixels_of<'l, U: Copy + 'l>(
+ &'l self,
+ iterator: impl ExactSizeIterator<Item = (u32, u32)> + 'l,
+ ) -> impl ExactSizeIterator<Item = &'l [U; CHANNELS]>
+ where
+ T: AsRef<[U]>,
+ {
+ iterator.map(move |(x, y)| unsafe { self.pixel(x, y) })
+ }
+}
+
+impl<T: AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
+ /// Bytes of this image.
+ pub fn bytes(&self) -> &[u8] {
+ self.buffer.as_ref()
+ }
+
+ /// Procure a [`ImageCloner`].
+ #[must_use = "function does not modify the original image"]
+ pub fn cloner(&self) -> ImageCloner<'_, CHANNELS> {
+ ImageCloner::from(self.as_ref())
+ }
+}
+
+impl<T: AsMut<[u8]> + AsRef<[u8]>, const CHANNELS: usize> Image<T, CHANNELS> {
#[inline]
/// Returns a iterator over every pixel, mutably
pub fn chunked_mut(&mut self) -> impl Iterator<Item = &mut [u8; CHANNELS]> {
// SAFETY: 0 sized images are not allowed
- unsafe { assert_unchecked!(self.buffer.len() > CHANNELS) };
+ unsafe { assert_unchecked(self.len() >= CHANNELS) };
+ // SAFETY: buffer cannot have half pixels
+ unsafe { assert_unchecked(self.len() % CHANNELS == 0) };
+ self.buffer.as_mut().array_chunks_mut::<CHANNELS>()
+ }
+
+ #[inline]
+ /// Flatten the chunks of this image into a mutable slice of slices.
+ pub fn flatten_mut(&mut self) -> &mut [[u8; CHANNELS]] {
// SAFETY: buffer cannot have half pixels
- unsafe { assert_unchecked!(self.buffer.len() % CHANNELS == 0) };
- self.buffer.array_chunks_mut::<CHANNELS>()
+ unsafe { self.buffer.as_mut().as_chunks_unchecked_mut::<CHANNELS>() }
}
/// Set the pixel at x, y
@@ -264,52 +800,38 @@ impl<T: std::ops::DerefMut<Target = [u8]>, const CHANNELS: usize> Image<T, CHANN
///
/// UB if x, y is out of bounds.
#[inline]
- pub unsafe fn set_pixel(&mut self, x: u32, y: u32, px: [u8; CHANNELS]) {
+ #[cfg_attr(debug_assertions, track_caller)]
+ pub unsafe fn set_pixel(&mut self, x: u32, y: u32, px: &[u8; CHANNELS]) {
// SAFETY: Caller says that x, y is in bounds
let out = unsafe { self.pixel_mut(x, y) };
// SAFETY: px must be CHANNELS long
unsafe { std::ptr::copy_nonoverlapping(px.as_ptr(), out.as_mut_ptr(), CHANNELS) };
}
-}
-
-impl<const CHANNELS: usize> Image<&mut [u8], CHANNELS> {
- /// Downcast the mutable reference
- pub fn as_ref(&self) -> Image<&[u8], CHANNELS> {
- // SAFETY: we got constructed okay, parameters must be valid
- unsafe { Image::new(self.width, self.height, self.buffer) }
- }
- /// Copy this ref image
- pub fn copy(&mut self) -> Image<&mut [u8], CHANNELS> {
- #[allow(clippy::undocumented_unsafe_blocks)]
+ /// Such swap. not actually implemented properly.
+ #[cfg_attr(debug_assertions, track_caller)]
+ pub unsafe fn swap_pixel(&mut self, (x1, y1): (u32, u32), (x2, y2): (u32, u32)) {
unsafe {
- Image::new(self.width, self.height, self.buffer)
+ let &p2 = self.pixel(x2, y2);
+ let &p1 = self.pixel(x1, y1);
+ self.set_pixel(x2, y2, &p1);
+ self.set_pixel(x1, y1, &p2);
}
}
}
-impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> {
- /// Create a reference to this owned image
- pub fn as_ref(&self) -> Image<&[u8], CHANNELS> {
- #[allow(clippy::undocumented_unsafe_blocks)]
- unsafe {
- Image::new(self.width, self.height, &self.buffer)
- }
- }
-}
-
-impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> {
- /// Create a mutable reference to this owned image
- pub fn as_mut(&mut self) -> Image<&mut [u8], CHANNELS> {
+impl<const CHANNELS: usize> Image<&mut [u8], CHANNELS> {
+ /// Copy this ref image
+ pub fn copy(&mut self) -> Image<&mut [u8], CHANNELS> {
#[allow(clippy::undocumented_unsafe_blocks)]
unsafe {
- Image::new(self.width, self.height, &mut self.buffer)
+ Image::new(self.width, self.height, self.buffer)
}
}
}
impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> {
- /// Allocates a new image
+ /// Allocates a new image. If `width` and `height` are constant, try using [`make`].
///
/// # Panics
///
@@ -322,30 +844,51 @@ impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> {
buffer: vec![0; CHANNELS * width as usize * height as usize],
}
}
+
+ /// Consumes and leaks this image, returning a reference to the image.
+ #[must_use = "not using the returned reference is a memory leak"]
+ pub fn leak(self) -> Image<&'static mut [u8], CHANNELS> {
+ // SAFETY: size unchanged
+ unsafe { self.mapped(Vec::leak) }
+ }
+}
+
+impl<const CHANNELS: usize, T: ?Sized> Image<Box<T>, CHANNELS> {
+ /// Consumes and leaks this image, returning a reference to the image.
+ #[must_use = "not using the returned reference is a memory leak"]
+ pub fn leak(self) -> Image<&'static mut T, CHANNELS> {
+ // SAFETY: size unchanged
+ unsafe { self.mapped(Box::leak) }
+ }
+}
+
+#[cfg(feature = "save")]
+/// Write a png image.
+pub trait WritePng {
+ /// Write this png image.
+ fn write(&self, f: &mut impl std::io::Write) -> std::io::Result<()>;
+}
+
+/// Read png.
+#[cfg(feature = "save")]
+pub trait ReadPng
+where
+ Self: Sized,
+{
+ /// Read a png into an image.
+ fn read<T: std::io::BufRead + std::io::Seek>(f: &mut T) -> std::io::Result<Self>;
}
/// helper macro for defining the save() method.
macro_rules! save {
($channels:literal == $clr:ident ($clrhuman:literal)) => {
- impl Image<Vec<u8>, $channels> {
- #[cfg(feature = "save")]
- #[doc = "Save this "]
- #[doc = $clrhuman]
- #[doc = " image."]
- pub fn save(&self, f: impl AsRef<std::path::Path>) {
- self.as_ref().save(f)
- }
- }
-
- impl Image<&[u8], $channels> {
- #[cfg(feature = "save")]
+ #[cfg(feature = "save")]
+ impl<T: AsRef<[u8]>> WritePng for Image<T, $channels> {
#[doc = "Save this "]
#[doc = $clrhuman]
#[doc = " image."]
- pub fn save(&self, f: impl AsRef<std::path::Path>) {
- let p = std::fs::File::create(f).unwrap();
- let w = &mut std::io::BufWriter::new(p);
- let mut enc = png::Encoder::new(w, self.width(), self.height());
+ fn write(&self, f: &mut impl std::io::Write) -> std::io::Result<()> {
+ let mut enc = png::Encoder::new(f, self.width(), self.height());
enc.set_color(png::ColorType::$clr);
enc.set_depth(png::BitDepth::Eight);
enc.set_source_gamma(png::ScaledFloat::new(1.0 / 2.2));
@@ -355,35 +898,88 @@ macro_rules! save {
(0.30000, 0.60000),
(0.15000, 0.06000),
));
- let mut writer = enc.write_header().unwrap();
- writer.write_image_data(self.buffer).unwrap();
+ let mut writer = enc.write_header()?;
+ writer.write_image_data(self.bytes())?;
+ Ok(())
+ }
+ }
+ impl<T: AsRef<[u8]>> Image<T, $channels> {
+ #[cfg(feature = "save")]
+ #[doc = "Save this "]
+ #[doc = $clrhuman]
+ #[doc = " image."]
+ pub fn save(&self, f: impl AsRef<std::path::Path>) {
+ self.write(&mut std::io::BufWriter::new(
+ std::fs::File::create(f).unwrap(),
+ ))
+ .unwrap();
}
}
};
}
-impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> {
+macro_rules! read {
+ ($n:literal) => {
+ #[cfg(feature = "save")]
+ impl ReadPng for Image<Box<[u8]>, $n> {
+ /// Open a PNG image
+ fn read<T: std::io::BufRead + std::io::Seek>(f: &mut T) -> std::io::Result<Self> {
+ use png::Transformations as T;
+ let mut dec = png::Decoder::new(f);
+ match $n {
+ 1 | 3 => dec.set_transformations(T::STRIP_16 | T::EXPAND),
+ 2 | 4 => dec.set_transformations(T::STRIP_16 | T::ALPHA), // alpha implies expand
+ _ => (),
+ }
+ let mut reader = dec.read_info()?;
+ let mut buf = vec![0; reader.output_buffer_size().unwrap()].into_boxed_slice();
+ let info = reader.next_frame(&mut buf)?;
+ use png::ColorType::*;
+ macro_rules! n {
+ ($x:literal) => {
+ Image::<_, $x>::build(info.width, info.height)
+ .buf(buf)
+ .into()
+ };
+ }
+ Ok(match info.color_type {
+ Indexed => unreachable!(), // see EXPAND
+ Grayscale => n![1],
+ GrayscaleAlpha => n![2],
+ Rgb => n![3],
+ Rgba => n![4],
+ })
+ }
+ }
+ };
+}
+impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS>
+where
+ [(); { (CHANNELS <= 4) as usize } - 1]:,
+{
#[cfg(feature = "save")]
+ #[cfg_attr(debug_assertions, track_caller)]
/// Open a PNG image
pub fn open(f: impl AsRef<std::path::Path>) -> Self {
let p = std::fs::File::open(f).unwrap();
- let r = std::io::BufReader::new(p);
- let dec = png::Decoder::new(r);
- let mut reader = dec.read_info().unwrap();
- let mut buf = vec![0; reader.output_buffer_size()];
- let info = reader.next_frame(&mut buf).unwrap();
- use png::ColorType::*;
- match info.color_type {
- Indexed | Grayscale => {
- assert_eq!(CHANNELS, 1, "indexed | grayscale requires one channel")
+ let r = &mut std::io::BufReader::new(p);
+ use core::intrinsics::transmute_unchecked as t;
+ // SAFETY: ... this is idiotic.
+ unsafe {
+ match CHANNELS {
+ 1 => t(Image::<Box<_>, 1>::read(r).unwrap().unbox()),
+ 2 => t(Image::<Box<_>, 2>::read(r).unwrap().unbox()),
+ 3 => t(Image::<Box<_>, 3>::read(r).unwrap().unbox()),
+ 4 => t(Image::<Box<_>, 4>::read(r).unwrap().unbox()),
+ _ => unreachable!(),
}
- Rgb => assert_eq!(CHANNELS, 3, "rgb requires three channels"),
- Rgba => assert_eq!(CHANNELS, 4, "rgba requires four channels"),
- GrayscaleAlpha => assert_eq!(CHANNELS, 2, "ya requires two channels"),
}
- Self::build(info.width, info.height).buf(buf)
}
}
+read!(1);
+read!(2);
+read!(3);
+read!(4);
save!(3 == Rgb("RGB"));
save!(4 == Rgba("RGBA"));
@@ -399,12 +995,4 @@ macro_rules! img {
#[cfg(test)]
use img;
-#[cfg(test)]
-mod tests {
- use super::*;
- #[test]
- fn repeat() {
- let x: Image<&[u8], 3> = Image::build(8, 8).buf(include_bytes!("../benches/3_8x8.imgbuf"));
- unsafe { x.repeated(128, 128) }; // repeat 16 times
- }
-}
+use crate::slicing::PBounds;