otf_pixels_core/lib.rs
1//! Core engine for `otf-pixels`: the op graph, tiles, codec traits, and the
2//! M1 reference evaluator.
3//!
4//! Most users want the [`otf-pixels`] facade instead — this crate is the
5//! substrate it and the op/codec crates are built on. Depend on it directly
6//! when implementing a codec or an op.
7//!
8//! [`otf-pixels`]: https://docs.rs/otf-pixels
9//!
10//! # The model
11//!
12//! An [`Image`] is a handle onto a node of an immutable lazy DAG. Chaining ops
13//! builds graph structure and computes descriptors; it reads no pixels. Pixels
14//! move only when a terminal pulls them, at which point demand propagates
15//! *backwards* through [`Op::input_regions`] and pixels flow *forwards* through
16//! [`Op::compute`] (ADR-0001).
17//!
18//! ```
19//! use otf_pixels_core::{Format, Image, ImageDescriptor, PixelFormat, TileBuf, evaluate};
20//! use otf_pixels_core::{BufferSource, Producer, Region};
21//! use std::sync::Arc;
22//!
23//! # fn main() -> Result<(), otf_pixels_core::PixelsError> {
24//! let descriptor = ImageDescriptor::new(2, 2, PixelFormat::Gray8)?;
25//! let pixels = TileBuf::from_vec(descriptor.region(), PixelFormat::Gray8, vec![1, 2, 3, 4])?;
26//! let source = BufferSource::new(descriptor, Arc::new(pixels))?;
27//!
28//! // Construction and chaining do no pixel work.
29//! let image = Image::from_producer(Arc::new(source), Format::Raw);
30//! assert_eq!(image.metadata()?.width, 2);
31//!
32//! // A terminal pulls.
33//! assert_eq!(evaluate(&image)?.bytes(), &[1, 2, 3, 4]);
34//! # Ok(())
35//! # }
36//! ```
37//!
38//! # Errors never panic
39//!
40//! Every fallible path returns [`PixelsError`]. Malformed input is a value, not
41//! a panic — this crate forbids `unsafe` and denies `unwrap`/`expect`/`panic!`
42//! outside tests, because a hostile image must not be able to take down a
43//! process embedding the engine (ARCHITECTURE §Failure model).
44//!
45//! # Concurrency
46//!
47//! The core is synchronous (ADR-0005). [`Image`] is `Send + Sync` and cheap to
48//! clone, so an async host integrates by running pipelines on its own worker
49//! threads and meeting the engine at the [`Source`]/[`Sink`] boundary.
50
51mod cache;
52mod codec;
53mod error;
54mod eval;
55mod geometry;
56mod graph;
57mod io;
58mod op;
59mod orientation;
60mod pixel;
61mod plan;
62mod pool;
63mod schedule;
64mod shrink;
65mod source;
66mod tile;
67
68#[cfg(any(test, feature = "testing"))]
69pub mod testing;
70
71pub use cache::{CacheStats, TileCache, TileKey};
72pub use codec::{
73 Animation, Codec, DecodeCapability, Decoder, EncodeOptions, Encoder, Format, Metadata,
74};
75pub use error::{ErrorCode, Limit, PixelsError, Result};
76pub use eval::{demand, evaluate, evaluate_rows};
77pub use geometry::{ImageDescriptor, Limits, Region};
78pub use graph::{Image, Node, NodeId};
79pub use io::{Prefixed, Sink, Source};
80pub use op::{AccessPattern, Op, Producer};
81pub use orientation::Orientation;
82pub use pixel::{ChannelLayout, ColorModel, PixelFormat, Sample, SampleKind};
83pub use plan::{NodePlan, Plan, PlanOptions, TileShape};
84pub use pool::ThreadPool;
85pub use schedule::{RunStats, Scheduler, SchedulerOptions, evaluate_tiled};
86pub use shrink::{Reduction, shrink_on_load};
87pub use source::{BufferSource, DecodedSource};
88pub use tile::{Tile, TileBuf, TileMut, copy_region};