fast image operations
Diffstat (limited to 'src/lib.rs')
| -rw-r--r-- | src/lib.rs | 904 |
1 files changed, 746 insertions, 158 deletions
@@ -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; |