Import imgref 1.9.4 upstream upstream/1.9.4
authorDongHun Kwak <dh0128.kwak@samsung.com>
Thu, 6 Apr 2023 07:21:13 +0000 (16:21 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Thu, 6 Apr 2023 07:21:13 +0000 (16:21 +0900)
13 files changed:
.cargo_vcs_info.json [new file with mode: 0644]
.github/workflows/cargo.yml [new file with mode: 0644]
.gitignore [new file with mode: 0644]
Cargo.toml [new file with mode: 0644]
Cargo.toml.orig [new file with mode: 0644]
LICENSE [new file with mode: 0644]
README.md [new file with mode: 0644]
benches/iterbench.rs [new file with mode: 0644]
imgref.png [new file with mode: 0644]
src/iter.rs [new file with mode: 0644]
src/lib.rs [new file with mode: 0644]
src/ops.rs [new file with mode: 0644]
src/traits.rs [new file with mode: 0644]

diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644 (file)
index 0000000..f5cda61
--- /dev/null
@@ -0,0 +1,6 @@
+{
+  "git": {
+    "sha1": "40f08c14b437cb4aab2e37b11889a4245bcd655f"
+  },
+  "path_in_vcs": ""
+}
\ No newline at end of file
diff --git a/.github/workflows/cargo.yml b/.github/workflows/cargo.yml
new file mode 100644 (file)
index 0000000..fb0ee84
--- /dev/null
@@ -0,0 +1,31 @@
+name: CI
+
+on:
+  push:
+  pull_request:
+    branches:
+      # Branches from forks have the form 'user:branch-name' so we only run
+      # this job on pull_request events for branches that look like fork
+      # branches. Without this we would end up running this job twice for non
+      # forked PRs, once for the push and then once for opening the PR.
+    - '**:**'
+
+jobs:
+  test:
+    name: Test
+    strategy:
+      matrix:
+        rust:
+          - stable
+          - nightly
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - uses: actions-rs/toolchain@v1
+        with:
+          profile: minimal
+          toolchain: ${{ matrix.rust }}
+          override: true
+      - uses: actions-rs/cargo@v1
+        with:
+          command: test
diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..a9d37c5
--- /dev/null
@@ -0,0 +1,2 @@
+target
+Cargo.lock
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644 (file)
index 0000000..318febb
--- /dev/null
@@ -0,0 +1,39 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+name = "imgref"
+version = "1.9.4"
+authors = ["Kornel Lesiński <kornel@geekhood.net>"]
+description = "A trivial struct for interchange of 2d-dimensional pixel buffers with width, height & stride"
+homepage = "https://lib.rs/crates/imgref"
+documentation = "https://docs.rs/imgref/"
+readme = "README.md"
+keywords = [
+    "interoperability",
+    "stride",
+    "image",
+    "frame",
+    "vec2d",
+]
+categories = [
+    "rust-patterns",
+    "multimedia::images",
+]
+license = "CC0-1.0 OR Apache-2.0"
+repository = "https://github.com/kornelski/imgref"
+
+[package.metadata.docs.rs]
+targets = ["x86_64-unknown-linux-gnu"]
+
+[badges.maintenance]
+status = "actively-developed"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644 (file)
index 0000000..e0b9a98
--- /dev/null
@@ -0,0 +1,19 @@
+[package]
+authors = ["Kornel Lesiński <kornel@geekhood.net>"]
+categories = ["rust-patterns", "multimedia::images"]
+description = "A trivial struct for interchange of 2d-dimensional pixel buffers with width, height & stride"
+documentation = "https://docs.rs/imgref/"
+homepage = "https://lib.rs/crates/imgref"
+keywords = ["interoperability", "stride", "image", "frame", "vec2d"]
+license = "CC0-1.0 OR Apache-2.0"
+name = "imgref"
+readme = "README.md"
+repository = "https://github.com/kornelski/imgref"
+version = "1.9.4"
+edition = "2018"
+
+[badges]
+maintenance = { status = "actively-developed" }
+
+[package.metadata.docs.rs]
+targets = ["x86_64-unknown-linux-gnu"]
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..0e259d4
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,121 @@
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+    LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+    INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+    REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+    PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+    THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+    HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+  i. the right to reproduce, adapt, distribute, perform, display,
+     communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+     likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+     subject to the limitations in paragraph 4(a), below;
+  v. rights protecting the extraction, dissemination, use and reuse of data
+     in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+     European Parliament and of the Council of 11 March 1996 on the legal
+     protection of databases, and under any national implementation
+     thereof, including any amended or successor version of such
+     directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+     world based on applicable law or treaty, and any national
+     implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+    surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+    warranties of any kind concerning the Work, express, implied,
+    statutory or otherwise, including without limitation warranties of
+    title, merchantability, fitness for a particular purpose, non
+    infringement, or the absence of latent or other defects, accuracy, or
+    the present or absence of errors, whether or not discoverable, all to
+    the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+    that may apply to the Work or any use thereof, including without
+    limitation any person's Copyright and Related Rights in the Work.
+    Further, Affirmer disclaims responsibility for obtaining any necessary
+    consents, permissions or other rights required for any use of the
+    Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+    party to this document and has no duty or obligation with respect to
+    this CC0 or use of the Work.
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..b2703ac
--- /dev/null
+++ b/README.md
@@ -0,0 +1,65 @@
+# 2D slice of a `Vec`
+
+This is a lowest common denominator struct for working with image fragments in Rust code. It represents a 2-dimensional vector and rectangular slices of it.
+
+* [API Reference](https://docs.rs/imgref)
+* [Installation](https://crates.io/crates/imgref)
+
+In graphics code it's very common to pass `width` and `height` along with a `Vec` of pixels — all as separate arguments. This gets very repetitive, and can lead to errors.
+
+This crate is a simple struct that adds dimensions to the underlying buffer. This makes it easier to correctly keep track of the image size and allows passing images with just one function argument instead three or four.
+
+Additionally, it has a concept of a `stride`, which allows defining sub-regions of images without copying, as well as padding (e.g. buffers for video frames may require to be a multiple of 8, regardless of logical image size).
+
+For convenience, it implements iterators for pixels/rows and indexing with `img[(x,y)]`.
+
+```rust
+use imgref::*;
+
+fn main() {
+    let img = Img::new(vec![0; 1000], 50, 20); // 1000 pixels of a 50×20 image
+
+    let new_image = some_image_processing_function(img.as_ref()); // Use imgvec.as_ref() instead of &imgvec for better efficiency
+
+    println!("New size is {}×{}", new_image.width(), new_image.height());
+    println!("And the top left pixel is {:?}", new_image[(0u32,0u32)]);
+
+    let first_row_slice = &new_image[0];
+
+    for row in new_image.rows() {
+        …
+    }
+    for px in new_image.pixels() {
+        …
+    }
+
+    // slice (x, y, width, height) by reference - no copy!
+    let fragment = img.sub_image(5, 5, 15, 15);
+
+    // create a vec of pixels without stride, for compatibility with software
+    // that expects pixels without any "gaps"
+    let (vec, width, height) = fragment.to_contiguous_buf();
+}
+```
+
+## Type aliases
+
+<img src="imgref.png" width="446" alt="Illustration: stride is width of the whole buffer.">
+
+These are described in [more detail in the reference](https://docs.rs/imgref).
+
+### `ImgVec`
+
+It owns its pixels (held in a `Vec`). It's analogous to a 2-dimensional `Vec`. Use this type to create and return new images from functions.
+
+Don't use `&ImgVec`. Instead call `ImgVec.as_ref()` to get a reference (`ImgRef`) from it (explicit `.as_ref()` call is required, because Rust doesn't support [custom conversions](https://github.com/rust-lang/rfcs/pull/1524) yet.)
+
+### `ImgRef`
+
+`ImgRef` is a reference to pixels owned by some other `ImgVec` or a slice. It's analogous to a 2-dimensional `&[]`.
+
+Use this type to accept read-only images as arguments in functions. Note that `ImgRef` is a `Copy` type. Pass `ImgRef`, and *not* `&ImgRef`.
+
+### Requirements
+
+* Latest stable Rust (1.42+)
diff --git a/benches/iterbench.rs b/benches/iterbench.rs
new file mode 100644 (file)
index 0000000..3f771a7
--- /dev/null
@@ -0,0 +1,41 @@
+#![feature(test)]
+
+extern crate test;
+use imgref::*;
+use test::Bencher;
+
+#[bench]
+fn iter_count(bench: &mut Bencher) {
+    let img = Img::new_stride(vec![0x11223344u32; 802*600], 800, 600, 802);
+
+    bench.iter(|| {
+        img.pixels().count()
+    });
+}
+
+#[bench]
+fn iter_sum(bench: &mut Bencher) {
+    let img = Img::new_stride(vec![0x11223344u32; 802*600], 800, 600, 802);
+
+    bench.iter(|| {
+        img.pixels().map(|p| p as usize).sum::<usize>()
+    });
+}
+
+#[bench]
+fn stride_ignorant_sum(bench: &mut Bencher) {
+    let img = Img::new_stride(vec![0x11223344u32; 802*600], 800, 600, 802);
+
+    bench.iter(|| {
+        img.buf().iter().cloned().map(|p| p as usize).sum::<usize>()
+    });
+}
+
+#[bench]
+fn chunked_sum(bench: &mut Bencher) {
+    let img = Img::new_stride(vec![0x11223344u32; 802*600], 800, 600, 802);
+
+    bench.iter(|| {
+        img.buf().chunks(img.stride()).flat_map(|row| row[0..img.width()].iter()).cloned().map(|p| p as usize).sum::<usize>()
+    });
+}
diff --git a/imgref.png b/imgref.png
new file mode 100644 (file)
index 0000000..f534f6f
Binary files /dev/null and b/imgref.png differ
diff --git a/src/iter.rs b/src/iter.rs
new file mode 100644 (file)
index 0000000..5b69e8e
--- /dev/null
@@ -0,0 +1,338 @@
+use core::num::NonZeroUsize;
+use core::iter::FusedIterator;
+use std::marker::PhantomData;
+use std::slice;
+
+/// Rows of the image. Call `Img.rows()` to create it.
+///
+/// Each element is a slice `width` pixels wide. Ignores padding, if there's any.
+#[derive(Debug)]
+#[must_use]
+pub struct RowsIter<'a, T> {
+    pub(crate) inner: slice::Chunks<'a, T>,
+    pub(crate) width: usize,
+}
+
+impl<'a, T: 'a> Iterator for RowsIter<'a, T> {
+    type Item = &'a [T];
+
+    #[inline]
+    fn next(&mut self) -> Option<Self::Item> {
+        match self.inner.next() {
+            Some(s) => {
+                // guaranteed during creation of chunks iterator
+                debug_assert!(s.len() >= self.width);
+                unsafe {
+                    Some(s.get_unchecked(0..self.width))
+                }
+            },
+            None => None,
+        }
+    }
+
+    #[inline]
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.inner.size_hint()
+    }
+
+    #[inline]
+    fn nth(&mut self, n: usize) -> Option<Self::Item> {
+        match self.inner.nth(n) {
+            Some(s) => {
+                // guaranteed during creation of chunks iterator
+                debug_assert!(s.len() >= self.width);
+                unsafe {
+                    Some(s.get_unchecked(0..self.width))
+                }
+            },
+            None => None,
+        }
+    }
+
+    #[inline]
+    fn count(self) -> usize {
+        self.inner.count()
+    }
+}
+
+impl<'a, T> ExactSizeIterator for RowsIter<'a, T> {
+    #[inline]
+    fn len(&self) -> usize {
+        self.inner.len()
+    }
+}
+
+impl<'a, T> FusedIterator for RowsIter<'a, T> {}
+
+impl<'a, T: 'a> DoubleEndedIterator for RowsIter<'a, T> {
+    #[inline]
+    fn next_back(&mut self) -> Option<Self::Item> {
+        match self.inner.next_back() {
+            Some(s) => {
+                // guaranteed during creation of chunks iterator
+                debug_assert!(s.len() >= self.width);
+                unsafe {
+                    Some(s.get_unchecked(0..self.width))
+                }
+            },
+            None => None,
+        }
+    }
+}
+
+/// Rows of the image. Call `Img.rows_mut()` to create it.
+///
+/// Each element is a slice `width` pixels wide. Ignores padding, if there's any.
+#[derive(Debug)]
+#[must_use]
+pub struct RowsIterMut<'a, T> {
+    pub(crate) width: usize,
+    pub(crate) inner: slice::ChunksMut<'a, T>,
+}
+
+impl<'a, T: 'a> Iterator for RowsIterMut<'a, T> {
+    type Item = &'a mut [T];
+
+    #[inline]
+    fn next(&mut self) -> Option<Self::Item> {
+        match self.inner.next() {
+            Some(s) => Some(&mut s[0..self.width]),
+            None => None,
+        }
+    }
+
+    #[inline]
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.inner.size_hint()
+    }
+
+    #[inline]
+    fn nth(&mut self, n: usize) -> Option<Self::Item> {
+        match self.inner.nth(n) {
+            Some(s) => Some(&mut s[0..self.width]),
+            None => None,
+        }
+    }
+
+    #[inline]
+    fn count(self) -> usize {
+        self.inner.count()
+    }
+}
+
+impl<'a, T> ExactSizeIterator for RowsIterMut<'a, T> {}
+impl<'a, T> FusedIterator for RowsIterMut<'a, T> {}
+
+impl<'a, T: 'a> DoubleEndedIterator for RowsIterMut<'a, T> {
+    #[inline]
+    fn next_back(&mut self) -> Option<Self::Item> {
+        match self.inner.next_back() {
+            Some(s) => Some(&mut s[0..self.width]),
+            None => None,
+        }
+    }
+}
+
+
+/// Iterates over pixels in the (sub)image. Call `Img.pixels()` to create it.
+///
+/// Ignores padding, if there's any.
+#[must_use]
+pub struct PixelsIter<'a, T: Copy> {
+    inner: PixelsRefIter<'a, T>
+}
+
+impl<'a, T: Copy + 'a> PixelsIter<'a, T> {
+    #[inline(always)]
+    pub(crate) fn new(img: super::ImgRef<'a, T>) -> Self {
+        Self {
+            inner: PixelsRefIter::new(img)
+        }
+    }
+}
+
+impl<'a, T: Copy + 'a> Iterator for PixelsIter<'a, T> {
+    type Item = T;
+
+    #[inline(always)]
+    fn next(&mut self) -> Option<Self::Item> {
+        self.inner.next().copied()
+    }
+}
+
+impl<'a, T: Copy> ExactSizeIterator for PixelsIter<'a, T> {
+    #[inline]
+    fn len(&self) -> usize {
+        self.inner.len()
+    }
+}
+
+/// Iterates over pixels in the (sub)image. Call `Img.pixels_ref()` to create it.
+///
+/// Ignores padding, if there's any.
+#[derive(Debug)]
+#[must_use]
+pub struct PixelsRefIter<'a, T> {
+    current: *const T,
+    current_line_end: *const T,
+    rows_left: usize,
+    width: NonZeroUsize,
+    pad: usize,
+    _dat: PhantomData<&'a [T]>,
+}
+
+unsafe impl<T> Send for PixelsRefIter<'_, T> where T: Send {}
+unsafe impl<T> Sync for PixelsRefIter<'_, T> where T: Sync {}
+
+impl<'a, T: 'a> PixelsRefIter<'a, T> {
+    #[inline]
+    pub(crate) fn new(img: super::ImgRef<'a, T>) -> Self {
+        let width = NonZeroUsize::new(img.width()).expect("width > 0");
+        let height = img.height();
+        let stride = img.stride();
+        assert!(stride >= width.get());
+        let pad = stride - width.get();
+        debug_assert!(img.buf().len() + stride >= stride * height + width.get(),
+            "buffer len {} is less than {} (({}+{})x{})", img.buf().len(),
+            stride * height - pad, width, pad, height);
+        Self {
+            current: img.buf().as_ptr(),
+            current_line_end: img.buf()[width.get()..].as_ptr(),
+            width,
+            rows_left: height,
+            pad,
+            _dat: PhantomData,
+        }
+    }
+}
+
+impl<'a, T: 'a> Iterator for PixelsRefIter<'a, T> {
+    type Item = &'a T;
+
+    #[inline(always)]
+    fn next(&mut self) -> Option<Self::Item> {
+        unsafe {
+            if self.current >= self.current_line_end {
+                if self.rows_left <= 1 {
+                    return None;
+                }
+                self.rows_left -= 1;
+                self.current = self.current_line_end.add(self.pad);
+                self.current_line_end = self.current.add(self.width.get());
+            }
+            let px = &*self.current;
+            self.current = self.current.add(1);
+            Some(px)
+        }
+    }
+
+    #[inline]
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        let this_line = unsafe {
+            self.current_line_end.offset_from(self.current)
+        };
+        debug_assert!(this_line >= 0);
+        let len = this_line as usize + (self.rows_left - 1) * self.width.get();
+        (len, Some(len))
+    }
+}
+
+impl<'a, T: Copy> ExactSizeIterator for PixelsRefIter<'a, T> {
+}
+
+/// Iterates over pixels in the (sub)image. Call `Img.pixels_mut()` to create it.
+///
+/// Ignores padding, if there's any.
+#[derive(Debug)]
+#[must_use]
+pub struct PixelsIterMut<'a, T> {
+    current: *mut T,
+    current_line_end: *mut T,
+    y: usize,
+    width: NonZeroUsize,
+    pad: usize,
+    _dat: PhantomData<&'a mut [T]>,
+}
+
+unsafe impl<T> Send for PixelsIterMut<'_, T> where T: Send {}
+unsafe impl<T> Sync for PixelsIterMut<'_, T> where T: Sync {}
+
+impl<'a, T: 'a> PixelsIterMut<'a, T> {
+    #[inline]
+    pub(crate) fn new(img: &mut super::ImgRefMut<'a, T>) -> Self {
+        let width = NonZeroUsize::new(img.width()).expect("width > 0");
+        let stride = img.stride();
+        debug_assert!(!img.buf().is_empty() && img.buf().len() + stride >= stride * img.height() + width.get());
+        Self {
+            current: img.buf_mut().as_mut_ptr(),
+            current_line_end: img.buf_mut()[width.get()..].as_mut_ptr(),
+            width,
+            y: img.height(),
+            pad: stride - width.get(),
+            _dat: PhantomData,
+        }
+    }
+}
+
+impl<'a, T: 'a> Iterator for PixelsIterMut<'a, T> {
+    type Item = &'a mut T;
+
+    #[inline(always)]
+    fn next(&mut self) -> Option<Self::Item> {
+        unsafe {
+            if self.current >= self.current_line_end {
+                self.y -= 1;
+                if self.y == 0 {
+                    return None;
+                }
+                self.current = self.current_line_end.add(self.pad);
+                self.current_line_end = self.current.add(self.width.get());
+            }
+            let px = &mut *self.current;
+            self.current = self.current.add(1);
+            Some(px)
+        }
+    }
+}
+
+#[test]
+fn iter() {
+    let img = super::Img::new(vec![1u8, 2], 1, 2);
+    let mut it = img.pixels();
+    assert_eq!(Some(1), it.next());
+    assert_eq!(Some(2), it.next());
+    assert_eq!(None, it.next());
+
+    let buf = vec![1u8; (16 + 3) * (8 + 1)];
+    for width in 1..16 {
+        for height in 1..8 {
+            for pad in 0..3 {
+                let stride = width + pad;
+                let img = super::Img::new_stride(&buf[..stride * height + stride - width], width, height, stride);
+                assert_eq!(width * height, img.pixels().map(|a| a as usize).sum(), "{}x{}", width, height);
+                assert_eq!(width * height, img.pixels().count(), "{}x{}", width, height);
+                assert_eq!(height, img.rows().count());
+
+                let mut iter1 = img.pixels();
+                let mut left = width * height;
+                while let Some(_px) = iter1.next() {
+                    left -= 1;
+                    assert_eq!(left, iter1.len());
+                }
+                assert_eq!(0, iter1.len()); iter1.next();
+                assert_eq!(0, iter1.len());
+
+                let mut iter2 = img.rows();
+                match iter2.next() {
+                    Some(_) => {
+                        assert_eq!(height - 1, iter2.size_hint().0);
+                        assert_eq!(height - 1, iter2.filter(|_| true).count());
+                    },
+                    None => {
+                        assert_eq!(height, 0);
+                    },
+                };
+            }
+        }
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644 (file)
index 0000000..1c05346
--- /dev/null
@@ -0,0 +1,877 @@
+//! In graphics code it's very common to pass `width` and `height` along with a `Vec` of pixels,
+//! all as separate arguments. This is tedious, and can lead to errors.
+//!
+//! This crate is a simple struct that adds dimensions to the underlying buffer. This makes it easier to correctly keep track
+//! of the image size and allows passing images with just one function argument instead three or four.
+//!
+//! Additionally, it has a concept of a `stride`, which allows defining sub-regions of images without copying,
+//! as well as handling padding (e.g. buffers for video frames may require to be a multiple of 8, regardless of logical image size).
+//!
+//! For convenience, there are iterators over rows or all pixels of a (sub)image and
+//! pixel-based indexing directly with `img[(x,y)]` (where `x`/`y` can be `u32` as well as `usize`).
+//!
+//! `Img<Container>` type has aliases for common uses:
+//!
+//! * Owned: `ImgVec<T>` → `Img<Vec<T>>`  (use it in `struct`s and return types)
+//! * Reference: `ImgRef<T>` → `Img<&[T]>` (use it in function arguments)
+//! * Mutable reference: `ImgRefMut<T>` → `Img<&mut [T]>`
+//!
+//! It is assumed that the container is [one element per pixel](https://crates.io/crates/rgb/), e.g. `Vec<RGBA>`,
+//! and _not_ a `Vec<u8>` where 4 `u8` elements are interpreted as one pixel.
+//!
+//!
+//!  ```rust
+//!  use imgref::*;
+//!  # fn some_image_processing_function(img: ImgRef<u8>) -> ImgVec<u8> { img.new_buf(img.buf().to_vec()) }
+//!
+//!  fn main() {
+//!      let img = Img::new(vec![0; 1000], 50, 20); // 1000 pixels of a 50×20 image
+//!
+//!      let new_image = some_image_processing_function(img.as_ref()); // Use imgvec.as_ref() instead of &imgvec for better efficiency
+//!
+//!      println!("New size is {}×{}", new_image.width(), new_image.height());
+//!      println!("And the top left pixel is {:?}", new_image[(0u32,0u32)]);
+//!
+//!      let first_row_slice = &new_image[0];
+//!
+//!      for row in new_image.rows() {
+//!          // …
+//!      }
+//!      for px in new_image.pixels() {
+//!          // …
+//!      }
+//!
+//!      // slice (x, y, width, height) by reference - no copy!
+//!      let fragment = img.sub_image(5, 5, 15, 15);
+//!
+//!      //
+//!      let (vec, width, height) = fragment.to_contiguous_buf();
+//!  }
+//!  ```
+
+use std::borrow::Cow;
+use std::slice;
+
+mod traits;
+
+mod ops;
+pub use ops::*;
+
+mod iter;
+pub use iter::*;
+
+/// Image owning its pixels.
+///
+/// A 2D array of pixels. The pixels are oriented top-left first and rows are `stride` pixels wide.
+///
+/// If size of the `buf` is larger than `width`*`height`, then any excess space is a padding (see `width_padded()`/`height_padded()`).
+pub type ImgVec<Pixel> = Img<Vec<Pixel>>;
+
+/// Reference to pixels inside another image.
+/// Pass this structure by value (i.e. `ImgRef`, not `&ImgRef`).
+///
+/// Only `width` of pixels of every `stride` can be modified. The `buf` may be longer than `height`*`stride`, but the extra space should be ignored.
+pub type ImgRef<'a, Pixel> = Img<&'a [Pixel]>;
+
+/// Same as `ImgRef`, but mutable
+/// Pass this structure by value (i.e. `ImgRef`, not `&ImgRef`).
+///
+pub type ImgRefMut<'a, Pixel> = Img<&'a mut [Pixel]>;
+
+/// Additional methods that depend on buffer size
+///
+/// To use these methods you need:
+///
+/// ```rust
+/// use imgref::*;
+/// ```
+pub trait ImgExt<Pixel> {
+    /// Maximum possible width of the data, including the stride.
+    ///
+    /// # Panics
+    ///
+    /// This method may panic if the underlying buffer is not at least `height()*stride()` pixels large.
+    fn width_padded(&self) -> usize;
+
+    /// Height in number of full strides.
+    /// If the underlying buffer is not an even multiple of strides, the last row is ignored.
+    ///
+    /// # Panics
+    ///
+    /// This method may panic if the underlying buffer is not at least `height()*stride()` pixels large.
+    fn height_padded(&self) -> usize;
+
+    /// Iterate over the entire buffer as rows, including all padding
+    ///
+    /// Rows will have up to `stride` width, but the last row may be shorter.
+    fn rows_padded(&self) -> slice::Chunks<'_, Pixel>;
+
+    /// Borrow the container
+    fn as_ref(&self) -> ImgRef<Pixel>;
+}
+
+/// Additional methods that depend on buffer size
+///
+/// To use these methods you need:
+///
+/// ```rust
+/// use imgref::*;
+/// ```
+pub trait ImgExtMut<Pixel> {
+    /// Iterate over the entire buffer as rows, including all padding
+    ///
+    /// Rows will have up to `stride` width, but the last row may be shorter.
+    fn rows_padded_mut(&mut self) -> slice::ChunksMut<'_, Pixel>;
+
+    /// Borrow the container mutably
+    fn as_mut(&mut self) -> ImgRefMut<Pixel>;
+}
+
+/// Basic struct used for both owned (alias `ImgVec`) and borrowed (alias `ImgRef`) image fragments.
+///
+/// Note: the fields are `pub` only because of borrow checker limitations. Please consider them as read-only.
+#[derive(Debug, Copy, Clone)]
+pub struct Img<Container> {
+    /// Storage for the pixels. Usually `Vec<Pixel>` or `&[Pixel]`. See `ImgVec` and `ImgRef`.
+    ///
+    /// Note that future version will make this field private. Use `.rows()` and `.pixels()` iterators where possible, or `buf()`/`buf_mut()`/`into_buf()`.
+    #[deprecated(note = "Don't access struct fields directly. Use buf(), buf_mut() or into_buf()")]
+    pub buf: Container,
+
+    /// Number of pixels to skip in the container to advance to the next row.
+    ///
+    /// Note: pixels between `width` and `stride` may not be usable, and may not even exist in the last row.
+    #[deprecated(note = "Don't access struct fields directly. Use stride()")]
+    pub stride: usize,
+    /// Width of the image in pixels.
+    ///
+    /// Note that this isn't same as the width of the row in the `buf`, see `stride`
+    #[deprecated(note = "Don't access struct fields directly. Use width()")]
+    pub width: u32,
+    /// Height of the image in pixels.
+    #[deprecated(note = "Don't access struct fields directly. Use height()")]
+    pub height: u32,
+}
+
+impl<Container> Img<Container> {
+    /// Width of the image in pixels.
+    ///
+    /// Note that this isn't same as the width of the row in image data, see `stride()`
+    #[inline(always)]
+    #[allow(deprecated)]
+    pub fn width(&self) -> usize {self.width as usize}
+
+    /// Height of the image in pixels.
+    #[inline(always)]
+    #[allow(deprecated)]
+    pub fn height(&self) -> usize {self.height as usize}
+
+    /// Number of _pixels_ to skip in the container to advance to the next row.
+    ///
+    /// Note the last row may have fewer pixels than the stride.
+    /// Some APIs use number of *bytes* for a stride. You may need to multiply this one by number of pixels.
+    #[inline(always)]
+    #[allow(deprecated)]
+    pub fn stride(&self) -> usize {self.stride}
+
+    /// Immutable reference to the pixel storage. Warning: exposes stride. Use `pixels()` or `rows()` insetad.
+    ///
+    /// See also `into_contiguous_buf()`.
+    #[inline(always)]
+    #[allow(deprecated)]
+    pub fn buf(&self) -> &Container {&self.buf}
+
+    /// Mutable reference to the pixel storage. Warning: exposes stride. Use `pixels_mut()` or `rows_mut()` insetad.
+    ///
+    /// See also `into_contiguous_buf()`.
+    #[inline(always)]
+    #[allow(deprecated)]
+    pub fn buf_mut(&mut self) -> &mut Container {&mut self.buf}
+
+    /// Get the pixel storage by consuming the image. Be careful about stride — see `into_contiguous_buf()` for a safe version.
+    #[inline(always)]
+    #[allow(deprecated)]
+    pub fn into_buf(self) -> Container {self.buf}
+
+    #[deprecated(note = "this was meant to be private, use new_buf() and/or rows()")]
+    pub fn rows_buf<'a, T: 'a>(&self, buf: &'a [T]) -> RowsIter<'a, T> {
+        self.rows_buf_internal(buf)
+    }
+
+    #[inline]
+    fn rows_buf_internal<'a, T: 'a>(&self, buf: &'a [T]) -> RowsIter<'a, T> {
+        let stride = self.stride();
+        debug_assert!(self.width() <= self.stride());
+        debug_assert!(buf.len() >= self.width() * self.height());
+        assert!(stride > 0);
+        let non_padded = &buf[0..stride * self.height() + self.width() - stride];
+        RowsIter {
+            width: self.width(),
+            inner: non_padded.chunks(stride),
+        }
+    }
+}
+
+impl<Pixel,Container> ImgExt<Pixel> for Img<Container> where Container: AsRef<[Pixel]> {
+    #[inline(always)]
+    fn width_padded(&self) -> usize {
+        self.stride()
+    }
+
+    #[inline(always)]
+    fn height_padded(&self) -> usize {
+        let len = self.buf().as_ref().len();
+        assert_eq!(0, len % self.stride());
+        len / self.stride()
+    }
+
+    /// Iterate over the entire buffer as rows, including all padding
+    ///
+    /// Rows will have up to `stride` width, but the last row may be shorter.
+    #[inline(always)]
+    fn rows_padded(&self) -> slice::Chunks<'_, Pixel> {
+        self.buf().as_ref().chunks(self.stride())
+    }
+
+    #[inline(always)]
+    #[allow(deprecated)]
+    fn as_ref(&self) -> ImgRef<Pixel> {
+        Img {
+            buf: self.buf.as_ref(),
+            width: self.width,
+            height: self.height,
+            stride: self.stride,
+        }
+    }
+}
+
+impl<Pixel,Container> ImgExtMut<Pixel> for Img<Container> where Container: AsMut<[Pixel]> {
+    /// Iterate over the entire buffer as rows, including all padding
+    ///
+    /// Rows will have up to `stride` width, but the last row may be shorter.
+    ///
+    /// # Panics
+    ///
+    /// If stride is 0
+    #[inline]
+    #[must_use]
+    fn rows_padded_mut(&mut self) -> slice::ChunksMut<'_, Pixel> {
+        let stride = self.stride();
+        self.buf_mut().as_mut().chunks_mut(stride)
+    }
+
+    #[inline(always)]
+    #[allow(deprecated)]
+    fn as_mut(&mut self) -> ImgRefMut<Pixel> {
+        Img {
+            buf: self.buf.as_mut(),
+            width: self.width,
+            height: self.height,
+            stride: self.stride,
+        }
+    }
+}
+
+#[inline]
+fn sub_image(left: usize, top: usize, width: usize, height: usize, stride: usize, buf_len: usize) -> (usize, usize, usize) {
+    let start = stride * top + left;
+    let full_strides_end = start + stride * height;
+    // when left > 0 and height is full, the last line is shorter than the stride
+    let end = if buf_len >= full_strides_end {
+        full_strides_end
+    } else {
+        debug_assert!(height > 0);
+        let min_strides_len = full_strides_end + width - stride;
+        debug_assert!(buf_len >= min_strides_len, "the buffer is too small to fit the subimage");
+        // if can't use full buffer, then shrink to min required (last line having exact width)
+        min_strides_len
+    };
+    (start, end, stride)
+}
+
+impl<'a, T> ImgRef<'a, T> {
+    /// Make a reference for a part of the image, without copying any pixels.
+    ///
+    /// # Panics
+    ///
+    /// It will panic if sub_image is outside of the image area
+    /// (left + width must be <= container width, etc.)
+    #[inline]
+    #[must_use]
+    pub fn sub_image(&self, left: usize, top: usize, width: usize, height: usize) -> Self {
+        assert!(top + height <= self.height());
+        assert!(left + width <= self.width());
+        let (start, end, stride) = sub_image(left, top, width, height, self.stride(), self.buf().len());
+        let buf = &self.buf()[start..end];
+        Self::new_stride(buf, width, height, stride)
+    }
+
+    #[inline]
+    /// Iterate over whole rows of pixels as slices
+    ///
+    /// # Panics
+    ///
+    /// If stride is 0
+    ///
+    /// See also `pixels()`
+    pub fn rows(&self) -> RowsIter<'_, T> {
+        self.rows_buf_internal(self.buf())
+    }
+
+    /// Deprecated
+    ///
+    /// Note: it iterates **all** pixels in the underlying buffer, not just limited by width/height.
+    #[deprecated(note = "Size of this buffer is unpredictable. Use .rows() instead")]
+    pub fn iter(&self) -> std::slice::Iter<'_, T> {
+        self.buf().iter()
+    }
+}
+
+impl<'a, T: Clone> ImgRef<'a, T> {
+    /// Returns a reference to the buffer, width, height. Guarantees that the buffer is contiguous,
+    /// i.e. it's `width*height` elements long, and `[x + y*width]` addresses each pixel.
+    ///
+    /// It will create a copy if the buffer isn't contiguous (width != stride).
+    /// For a more efficient version, see `into_contiguous_buf()`
+    #[allow(deprecated)]
+    #[must_use]
+    pub fn to_contiguous_buf(&self) -> (Cow<'a, [T]>, usize, usize) {
+        let width = self.width();
+        let height = self.height();
+        let stride = self.stride();
+        if width == stride {
+            return (Cow::Borrowed(self.buf), width, height)
+        }
+        let mut buf = Vec::with_capacity(width*height);
+        for row in self.rows() {
+            buf.extend_from_slice(row);
+        }
+        (Cow::Owned(buf), width, height)
+    }
+}
+
+impl<'a, T> ImgRefMut<'a, T> {
+    /// Turn this into immutable reference, and slice a subregion of it
+    #[inline]
+    #[allow(deprecated)]
+    #[must_use]
+    pub fn sub_image(&'a mut self, left: usize, top: usize, width: usize, height: usize) -> ImgRef<'a, T> {
+        self.as_ref().sub_image(left, top, width, height)
+    }
+
+    /// Trim this image without copying.
+    /// Note that mutable borrows are exclusive, so it's not possible to have more than
+    /// one mutable subimage at a time.
+    #[inline]
+    #[allow(deprecated)]
+    #[must_use]
+    pub fn sub_image_mut(&mut self, left: usize, top: usize, width: usize, height: usize) -> ImgRefMut<'_, T> {
+        assert!(top+height <= self.height());
+        assert!(left+width <= self.width());
+        let (start, end, stride) = sub_image(left, top, width, height, self.stride(), self.buf.len());
+        let buf = &mut self.buf[start..end];
+        ImgRefMut::new_stride(buf, width, height, stride)
+    }
+
+    /// Make mutable reference immutable
+    #[inline]
+    #[must_use]
+    pub fn as_ref(&self) -> ImgRef<'_, T> {
+        self.new_buf(self.buf().as_ref())
+    }
+}
+
+impl<'a, T: Copy> ImgRef<'a, T> {
+    /// Iterate `width*height` pixels in the `Img`, ignoring padding area
+    ///
+    /// If you want to iterate in parallel, parallelize `rows()` instead.
+    ///
+    /// # Panics
+    ///
+    /// if width is 0
+    #[inline]
+    pub fn pixels(&self) -> PixelsIter<'_, T> {
+        PixelsIter::new(*self)
+    }
+}
+
+impl<'a, T> ImgRef<'a, T> {
+    /// Iterate `width*height` pixels in the `Img`, by reference, ignoring padding area
+    ///
+    /// If you want to iterate in parallel, parallelize `rows()` instead.
+    ///
+    /// # Panics
+    ///
+    /// if width is 0
+    #[inline]
+    pub fn pixels_ref(&self) -> PixelsRefIter<'_, T> {
+        PixelsRefIter::new(*self)
+    }
+}
+
+impl<'a, T: Copy> ImgRefMut<'a, T> {
+    /// # Panics
+    ///
+    /// If you want to iterate in parallel, parallelize `rows()` instead.
+    ///
+    /// if width is 0
+    #[inline]
+    pub fn pixels(&self) -> PixelsIter<'_, T> {
+        PixelsIter::new(self.as_ref())
+    }
+
+    /// If you want to iterate in parallel, parallelize `rows()` instead.
+    /// # Panics
+    ///
+    /// if width is 0
+    #[inline]
+    pub fn pixels_mut(&mut self) -> PixelsIterMut<'_, T> {
+        PixelsIterMut::new(self)
+    }
+}
+
+impl<T: Copy> ImgVec<T> {
+    /// If you want to iterate in parallel, parallelize `rows()` instead.
+    /// # Panics
+    ///
+    /// if width is 0
+    #[inline]
+    pub fn pixels(&self) -> PixelsIter<'_, T> {
+        PixelsIter::new(self.as_ref())
+    }
+
+    /// If you want to iterate in parallel, parallelize `rows()` instead.
+    /// # Panics
+    ///
+    /// if width is 0
+    #[inline]
+    pub fn pixels_mut(&mut self) -> PixelsIterMut<'_, T> {
+        PixelsIterMut::new(&mut self.as_mut())
+    }
+}
+
+impl<'a, T> ImgRefMut<'a, T> {
+    /// Iterate over whole rows as slices
+    ///
+    /// # Panics
+    ///
+    /// if stride is 0
+    #[inline]
+    pub fn rows(&self) -> RowsIter<'_, T> {
+        self.rows_buf_internal(&self.buf()[..])
+    }
+
+    /// Iterate over whole rows as slices
+    ///
+    /// # Panics
+    ///
+    /// if stride is 0
+    #[inline]
+    #[allow(deprecated)]
+    pub fn rows_mut(&mut self) -> RowsIterMut<'_, T> {
+        let stride = self.stride();
+        let width = self.width();
+        let height = self.height();
+        let non_padded = &mut self.buf[0..stride * height + width - stride];
+        RowsIterMut {
+            width,
+            inner: non_padded.chunks_mut(stride),
+        }
+    }
+}
+
+/// Deprecated. Use .rows() or .pixels() iterators which are more predictable
+impl<Container> IntoIterator for Img<Container> where Container: IntoIterator {
+    type Item = Container::Item;
+    type IntoIter = Container::IntoIter;
+    /// Deprecated. Use .rows() or .pixels() iterators which are more predictable
+    fn into_iter(self) -> Container::IntoIter {
+        self.into_buf().into_iter()
+    }
+}
+
+impl<T> ImgVec<T> {
+    /// Create a mutable view into a region within the image. See `sub_image()` for read-only views.
+    #[allow(deprecated)]
+    #[must_use]
+    pub fn sub_image_mut(&mut self, left: usize, top: usize, width: usize, height: usize) -> ImgRefMut<'_, T> {
+        assert!(top+height <= self.height());
+        assert!(left+width <= self.width());
+        let start = self.stride * top + left;
+        let min_buf_size = if self.height > 0 {self.stride * height + width - self.stride} else {0};
+        let buf = &mut self.buf[start .. start + min_buf_size];
+        Img::new_stride(buf, width, height, self.stride)
+    }
+
+    #[inline]
+    #[must_use]
+    /// Make a reference for a part of the image, without copying any pixels.
+    pub fn sub_image(&self, left: usize, top: usize, width: usize, height: usize) -> ImgRef<'_, T> {
+        self.as_ref().sub_image(left, top, width, height)
+    }
+
+    /// Make a reference to this image to pass it to functions without giving up ownership
+    ///
+    /// The reference should be passed by value (`ImgRef`, not `&ImgRef`).
+    ///
+    /// If you need a mutable reference, see `as_mut()` and `sub_image_mut()`
+    #[inline]
+    #[must_use]
+    pub fn as_ref(&self) -> ImgRef<'_, T> {
+        self.new_buf(self.buf().as_ref())
+    }
+
+    /// Make a mutable reference to the entire image
+    ///
+    /// The reference should be passed by value (`ImgRefMut`, not `&mut ImgRefMut`).
+    ///
+    /// See also `sub_image_mut()` and `rows_mut()`
+    #[inline]
+    pub fn as_mut(&mut self) -> ImgRefMut<'_, T> {
+        let width = self.width();
+        let height = self.height();
+        let stride = self.stride();
+        Img::new_stride(self.buf_mut().as_mut(), width, height, stride)
+    }
+
+    #[deprecated(note = "Size of this buffer may be unpredictable. Use .rows() instead")]
+    pub fn iter(&self) -> std::slice::Iter<'_, T> {
+        self.buf().iter()
+    }
+
+    /// Iterate over rows of the image as slices
+    ///
+    /// Each slice is guaranteed to be exactly `width` pixels wide.
+    ///
+    /// This iterator is a good candidate for parallelization (e.g. rayon's `par_bridge()`)
+    #[inline]
+    pub fn rows(&self) -> RowsIter<'_, T> {
+        self.rows_buf_internal(self.buf())
+    }
+
+    /// Iterate over rows of the image as mutable slices
+    ///
+    /// Each slice is guaranteed to be exactly `width` pixels wide.
+    ///
+    /// This iterator is a good candidate for parallelization (e.g. rayon's `par_bridge()`)
+    #[inline]
+    #[allow(deprecated)]
+    pub fn rows_mut(&mut self) -> RowsIterMut<'_, T> {
+        let stride = self.stride();
+        let width = self.width();
+        let height = self.height();
+        let non_padded = &mut self.buf[0..stride * height + width - stride];
+        RowsIterMut {
+            width,
+            inner: non_padded.chunks_mut(stride),
+        }
+    }
+}
+
+impl<Container> Img<Container> {
+    /// Same as `new()`, except each row is located `stride` number of pixels after the previous one.
+    ///
+    /// Stride can be equal to `width` or larger. If it's larger, then pixels between end of previous row and start of the next are considered a padding, and may be ignored.
+    ///
+    /// The `Container` is usually a `Vec` or a slice.
+    #[inline]
+    #[allow(deprecated)]
+    pub fn new_stride(buf: Container, width: usize, height: usize, stride: usize) -> Self {
+        assert!(stride > 0);
+        assert!(stride >= width as usize);
+        debug_assert!(height < <u32>::max_value() as usize);
+        debug_assert!(width < <u32>::max_value() as usize);
+        Img {
+            buf,
+            width: width as u32,
+            height: height as u32,
+            stride,
+        }
+    }
+
+    /// Create new image with `Container` (which can be `Vec`, `&[]` or something else) with given `width` and `height` in pixels.
+    ///
+    /// Assumes the pixels in container are contiguous, layed out row by row with `width` pixels per row and at least `height` rows.
+    ///
+    /// If the container is larger than `width`×`height` pixels, the extra rows are a considered a padding and may be ignored.
+    #[inline]
+    pub fn new(buf: Container, width: usize, height: usize) -> Self {
+        Self::new_stride(buf, width, height, width)
+    }
+}
+
+impl<T: Copy> Img<Vec<T>> {
+    /// Returns the buffer, width, height. Guarantees that the buffer is contiguous,
+    /// i.e. it's `width*height` elements long, and `[x + y*width]` addresses each pixel.
+    ///
+    /// Efficiently performs operation in-place. For other containers use `pixels().collect()`.
+    #[allow(deprecated)]
+    #[must_use]
+    pub fn into_contiguous_buf(mut self) -> (Vec<T>, usize, usize) {
+        let (_, w, h) = self.as_contiguous_buf();
+        (self.buf, w, h)
+    }
+
+    /// Returns a reference to the buffer, width, height. Guarantees that the buffer is contiguous,
+    /// i.e. it's `width*height` elements long, and `[x + y*width]` addresses each pixel.
+    ///
+    /// Efficiently performs operation in-place. For other containers use `pixels().collect()`.
+    #[allow(deprecated)]
+    #[must_use]
+    pub fn as_contiguous_buf(&mut self) -> (&[T], usize, usize) {
+        let width = self.width();
+        let height = self.height();
+        let stride = self.stride();
+        if width != stride {
+            for row in 1..height {
+                self.buf.copy_within(row * stride .. row * stride + width, row * width);
+            }
+        }
+        self.buf.truncate(width * height);
+        (&mut self.buf, width, height)
+    }
+}
+
+impl<OldContainer> Img<OldContainer> {
+    /// A convenience method for creating an image of the same size and stride, but with a new buffer.
+    #[inline]
+    pub fn map_buf<NewContainer, OldPixel, NewPixel, F>(self, callback: F) -> Img<NewContainer>
+        where NewContainer: AsRef<[NewPixel]>, OldContainer: AsRef<[OldPixel]>, F: FnOnce(OldContainer) -> NewContainer {
+        let width = self.width();
+        let height = self.height();
+        let stride = self.stride();
+        let old_buf_len = self.buf().as_ref().len();
+        #[allow(deprecated)]
+        let new_buf = callback(self.buf);
+        assert_eq!(old_buf_len, new_buf.as_ref().len());
+        Img::new_stride(new_buf, width, height, stride)
+    }
+
+    /// A convenience method for creating an image of the same size and stride, but with a new buffer.
+    #[inline]
+    pub fn new_buf<NewContainer, OldPixel, NewPixel>(&self, new_buf: NewContainer) -> Img<NewContainer>
+        where NewContainer: AsRef<[NewPixel]>, OldContainer: AsRef<[OldPixel]> {
+        assert_eq!(self.buf().as_ref().len(), new_buf.as_ref().len());
+        Img::new_stride(new_buf, self.width(), self.height(), self.stride())
+    }
+}
+
+impl<T: Clone> From<Img<Cow<'_, [T]>>> for Img<Vec<T>> {
+    #[allow(deprecated)]
+    fn from(img: Img<Cow<'_, [T]>>) -> Self {
+        Img {
+            width: img.width,
+            height: img.height,
+            stride: img.stride,
+            buf: img.buf.into_owned(),
+        }
+    }
+}
+
+impl<T: Clone> From<ImgVec<T>> for Img<Cow<'static, [T]>> {
+    #[allow(deprecated)]
+    fn from(img: ImgVec<T>) -> Self {
+        Img {
+            width: img.width,
+            height: img.height,
+            stride: img.stride,
+            buf: img.buf.into(),
+        }
+    }
+}
+
+impl<'a, T: Clone> From<ImgRef<'a, T>> for Img<Cow<'a, [T]>> {
+    #[allow(deprecated)]
+    fn from(img: ImgRef<'a, T>) -> Self {
+        Img {
+            buf: img.buf.into(),
+            width: img.width,
+            height: img.height,
+            stride: img.stride,
+        }
+    }
+}
+
+impl<T: Clone> Img<Cow<'_, [T]>> {
+    /// Convert underlying buffer to owned (e.g. slice to vec)
+    ///
+    /// See also `to_contiguous_buf().0.into_owned()`
+    #[allow(deprecated)]
+    pub fn into_owned(self) -> ImgVec<T> {
+        match self.buf {
+            Cow::Borrowed(_) => {
+                let tmp = self.as_ref();
+                let (buf, w, h) = tmp.to_contiguous_buf();
+                ImgVec::new(buf.into_owned(), w, h)
+            },
+            Cow::Owned(buf) => Img {
+                buf,
+                width: self.width,
+                height: self.height,
+                stride: self.stride,
+            },
+        }
+    }
+}
+
+impl<T> Img<T> where T: ToOwned {
+    /// Convert underlying buffer to owned (e.g. slice to vec)
+    ///
+    /// See also `to_contiguous_buf().0.into_owned()`
+    #[allow(deprecated)]
+    pub fn to_owned(&self) -> Img<T::Owned> {
+        Img {
+            buf: self.buf.to_owned(),
+            width: self.width,
+            height: self.height,
+            stride: self.stride,
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    mod with_opinionated_container {
+        use super::*;
+
+        struct IDontDeriveAnything;
+
+        #[test]
+        fn compiles() {
+            let _ = Img::new(IDontDeriveAnything, 1, 1);
+        }
+    }
+
+    #[test]
+    fn with_vec() {
+        let bytes = vec![0u8;20];
+        let old = Img::new_stride(bytes, 10,2,10);
+        let _ = old.new_buf(vec![6u16;20]);
+    }
+
+    #[test]
+    fn zero() {
+        let bytes = vec![0u8];
+        let mut img = Img::new_stride(bytes,0,0,1);
+        let _ = img.sub_image(0,0,0,0);
+        let _ = img.sub_image_mut(0,0,0,0);
+        let _ = img.as_ref();
+    }
+
+    #[test]
+    fn zero_width() {
+        let bytes = vec![0u8];
+        let mut img = Img::new_stride(bytes,0,1,1);
+        let _ = img.sub_image(0,1,0,0);
+        let _ = img.sub_image_mut(0,0,0,1);
+    }
+
+    #[test]
+    fn zero_height() {
+        let bytes = vec![0u8];
+        let mut img = Img::new_stride(bytes,1,0,1);
+        assert_eq!(0, img.rows().count());
+        let _ = img.sub_image(1,0,0,0);
+        let _ = img.sub_image_mut(0,0,1,0);
+    }
+
+    #[test]
+    #[allow(deprecated)]
+    fn with_slice() {
+        let bytes = vec![0u8;20];
+        let _ = Img::new_stride(bytes.as_slice(), 10,2,10);
+        let vec = ImgVec::new_stride(bytes, 10,2,10);
+        for _ in vec.iter() {}
+        assert_eq!(2, vec.rows().count());
+        for _ in vec.as_ref().buf().iter() {}
+        for _ in vec {}
+    }
+
+    #[test]
+    fn sub() {
+        let img = Img::new_stride(vec![1,2,3,4,
+                       5,6,7,8,
+                       9], 3, 2, 4);
+        assert_eq!(img.buf()[img.stride()], 5);
+        assert_eq!(img.buf()[img.stride() + img.width()-1], 7);
+
+        assert_eq!(img.pixels().count(), img.width() * img.height());
+        assert_eq!(img.pixels().sum::<i32>(), 24);
+
+        {
+        let refimg = img.as_ref();
+        let refimg2 = refimg; // Test is Copy
+
+        // sub-image with stride hits end of the buffer
+        let s1 = refimg.sub_image(1, 0, refimg.width()-1, refimg.height());
+        let _ = s1.sub_image(1, 0, s1.width()-1, s1.height());
+
+        let subimg = refimg.sub_image(1, 1, 2, 1);
+        assert_eq!(subimg.pixels().count(), subimg.width() * subimg.height());
+
+        assert_eq!(subimg.buf()[0], 6);
+        assert_eq!(subimg.stride(), refimg2.stride());
+        assert!(subimg.stride() * subimg.height() + subimg.width() - subimg.stride() <= subimg.buf().len());
+        assert_eq!(refimg.buf()[0], 1);
+        assert_eq!(1, subimg.rows().count());
+        }
+
+        let mut img = img;
+        let mut subimg = img.sub_image_mut(1, 1, 2, 1);
+        assert_eq!(1, subimg.rows().count());
+        assert_eq!(1, subimg.rows_mut().count());
+        assert_eq!(1, subimg.rows_mut().rev().count());
+        assert_eq!(1, subimg.rows_mut().fuse().rev().count());
+        assert_eq!(subimg.buf()[0], 6);
+    }
+
+    #[test]
+    fn rows() {
+        let img = ImgVec::new_stride(vec![0u8; 10000], 10, 15, 100);
+        assert_eq!(img.height(), img.rows().count());
+        assert_eq!(img.height(), img.rows().rev().count());
+        assert_eq!(img.height(), img.rows().fuse().rev().count());
+    }
+
+    #[test]
+    fn mut_pixels() {
+        for y in 1..15 {
+            for x in 1..10 {
+                let mut img = ImgVec::new_stride(vec![0u8; 10000], x, y, 100);
+                assert_eq!(x*y, img.pixels_mut().count());
+                assert_eq!(x*y, img.as_mut().pixels().count());
+                assert_eq!(x*y, img.as_mut().pixels_mut().count());
+                assert_eq!(x*y, img.as_mut().as_ref().pixels().count());
+            }
+        }
+    }
+
+    #[test]
+    fn into_contiguous_buf() {
+        for in_h in [1, 2, 3, 38, 39, 40, 41].iter().copied() {
+            for in_w in [1, 2, 3, 120, 121].iter().copied() {
+                for stride in [in_w, 121, 122, 166, 242, 243].iter().copied() {
+                    let img = ImgVec::new_stride((0..10000).map(|x| x as u8).collect(), in_w, in_h, stride)
+                        .map_buf(|x| x);
+                    let pixels: Vec<_> = img.pixels().collect();
+                    let (buf, w, h) = img.into_contiguous_buf();
+                    assert_eq!(pixels, buf);
+                    assert_eq!(in_w*in_h, buf.len());
+                    assert_eq!(10000, buf.capacity());
+                    assert_eq!(in_w, w);
+                    assert_eq!(in_h, h);
+                }
+            }
+        }
+
+        let img = ImgVec::new((0..55*33).map(|x| x as u8).collect(), 55, 33);
+        let pixels: Vec<_> = img.pixels().collect();
+        let tmp = img.as_ref();
+        let (buf, ..) = tmp.to_contiguous_buf();
+        assert_eq!(&pixels[..], &buf[..]);
+        let (buf, ..) = img.into_contiguous_buf();
+        assert_eq!(pixels, buf);
+    }
+}
diff --git a/src/ops.rs b/src/ops.rs
new file mode 100644 (file)
index 0000000..9efea60
--- /dev/null
@@ -0,0 +1,116 @@
+use super::Img;
+use std::ops;
+
+macro_rules! impl_imgref_index {
+    ($container:ty, $index:ty) => {
+        impl<'a, Pixel: Copy> ops::Index<($index, $index)> for Img<$container> {
+            type Output = Pixel;
+            #[inline(always)]
+            /// Read a pixel at `(x,y)` location (e.g. px = `img[(x,y)]`)
+            ///
+            /// Coordinates may be outside `width`/`height` if the buffer has enough padding.
+            /// The x coordinate can't exceed `stride`.
+            fn index(&self, index: ($index, $index)) -> &Self::Output {
+                let stride = self.stride();
+                debug_assert_eq!(stride, stride as $index as usize);
+                debug_assert!(index.0 < stride as $index);
+                &self.buf()[(index.1 * (stride as $index) + index.0) as usize]
+            }
+        }
+    };
+}
+
+macro_rules! impl_imgref_index_mut {
+    ($container:ty, $index:ty) => {
+        impl<'a, Pixel: Copy> ops::IndexMut<($index, $index)> for Img<$container> {
+            #[inline(always)]
+            /// Write a pixel at `(x,y)` location (e.g. `img[(x,y)] = px`)
+            ///
+            /// Coordinates may be outside `width`/`height` if the buffer has enough padding.
+            /// The x coordinate can't exceed `stride`.
+            fn index_mut(&mut self, index: ($index, $index)) -> &mut Self::Output {
+                let stride = self.stride();
+                debug_assert_eq!(stride, stride as $index as usize);
+                debug_assert!(index.0 < stride as $index);
+                &mut self.buf_mut()[(index.1 * (stride as $index) + index.0) as usize]
+            }
+        }
+    };
+}
+
+impl_imgref_index! {&'a [Pixel], usize}
+impl_imgref_index! {&'a [Pixel], u32}
+impl_imgref_index! {&'a mut [Pixel], usize}
+impl_imgref_index! {&'a mut [Pixel], u32}
+impl_imgref_index_mut! {&'a mut [Pixel], usize}
+impl_imgref_index_mut! {&'a mut [Pixel], u32}
+impl_imgref_index! {Vec<Pixel>, usize}
+impl_imgref_index! {Vec<Pixel>, u32}
+impl_imgref_index_mut! {Vec<Pixel>, usize}
+impl_imgref_index_mut! {Vec<Pixel>, u32}
+
+#[test]
+fn index() {
+    let mut img = Img::new_stride(vec![1,2,3,4,5,6,7,8], 2, 2, 3);
+    assert_eq!(1, img[(0u32,0u32)]);
+    assert_eq!(2, img.as_ref()[(1usize,0usize)]);
+    assert_eq!(3, img.as_ref()[(2u32,0u32)]);
+    assert_eq!(4, img[(0usize,1usize)]);
+    assert_eq!(8, img[(1usize,2usize)]);
+    assert_eq!(5, img.sub_image_mut(1,1,1,1)[(0usize,0usize)]);
+}
+
+
+macro_rules! impl_imgref_row_index {
+    ($container:ty) => {
+        impl<'a, Pixel: Copy> ops::Index<usize> for Img<$container> {
+            type Output = [Pixel];
+            #[inline(always)]
+            /// Take n-th row as a slice. Same as .rows().nth(n).unwrap()
+            ///
+            /// Slice length is guaranteed to equal image width.
+            /// Row must be within image height.
+            fn index(&self, row: usize) -> &Self::Output {
+                let stride = self.stride();
+                let width = self.width();
+                let start = row * stride;
+                &self.buf()[start .. start + width]
+            }
+        }
+    };
+}
+
+macro_rules! impl_imgref_row_index_mut {
+    ($container:ty) => {
+        impl<'a, Pixel: Copy> ops::IndexMut<usize> for Img<$container> {
+            #[inline(always)]
+            /// Take n-th row as a mutable slice. Same as .rows().nth(n).unwrap()
+            ///
+            /// Slice length is guaranteed to equal image width.
+            /// Row must be within image height.
+            fn index_mut(&mut self, row: usize) -> &mut Self::Output {
+                let stride = self.stride();
+                let width = self.width();
+                let start = row * stride;
+                &mut self.buf_mut()[start .. start + width]
+            }
+        }
+    };
+}
+
+impl_imgref_row_index! {&'a [Pixel]}
+impl_imgref_row_index! {&'a mut [Pixel]}
+impl_imgref_row_index_mut! {&'a mut [Pixel]}
+impl_imgref_row_index! {Vec<Pixel>}
+impl_imgref_row_index_mut! {Vec<Pixel>}
+
+#[test]
+fn index_by_row() {
+    let mut img = Img::new_stride(vec![1,2,3,4,5,6,7,8], 2, 2, 3);
+    assert_eq!(&[1,2], &img[0]);
+    assert_eq!(&[4,5], &img[1]);
+    assert_eq!(&[1,2], &img.as_ref()[0]);
+    assert_eq!(&[4,5], &img.as_ref()[1]);
+    assert_eq!(&[1,2], &img.as_mut()[0]);
+    assert_eq!(&[4,5], &img.as_mut()[1]);
+}
diff --git a/src/traits.rs b/src/traits.rs
new file mode 100644 (file)
index 0000000..69954f3
--- /dev/null
@@ -0,0 +1,148 @@
+use std::hash::{Hasher, Hash};
+use crate::{ImgRef, ImgVec, ImgRefMut};
+
+impl<'a, T: Hash> Hash for ImgRef<'a, T> {
+    #[allow(deprecated)]
+    #[inline]
+    fn hash<H: Hasher>(&self, state: &mut H) {
+        self.width.hash(state);
+        self.height.hash(state);
+        for row in self.rows() {
+            Hash::hash_slice(row, state);
+        }
+    }
+}
+
+impl<'a, T: Hash> Hash for ImgRefMut<'a, T> {
+    #[allow(deprecated)]
+    #[inline]
+    fn hash<H: Hasher>(&self, state: &mut H) {
+        self.as_ref().hash(state);
+    }
+}
+
+impl<T: Hash> Hash for ImgVec<T> {
+    #[inline(always)]
+    fn hash<H: Hasher>(&self, state: &mut H) {
+        self.as_ref().hash(state);
+    }
+}
+
+impl<'a, 'b, T, U> PartialEq<ImgRef<'b, U>> for ImgRef<'a, T> where T: PartialEq<U> {
+    #[allow(deprecated)]
+    #[inline]
+    fn eq(&self, other: &ImgRef<'b, U>) -> bool {
+        self.width == other.width &&
+        self.height == other.height &&
+        self.rows().zip(other.rows()).all(|(a,b)| a == b)
+    }
+}
+
+impl<'a, 'b, T, U> PartialEq<ImgRefMut<'b, U>> for ImgRefMut<'a, T> where T: PartialEq<U> {
+    #[allow(deprecated)]
+    #[inline]
+    fn eq(&self, other: &ImgRefMut<'b, U>) -> bool {
+        self.as_ref().eq(&other.as_ref())
+    }
+}
+
+
+impl<T, U> PartialEq<ImgVec<U>> for ImgVec<T> where T: PartialEq<U> {
+    #[allow(deprecated)]
+    #[inline(always)]
+    fn eq(&self, other: &ImgVec<U>) -> bool {
+        self.as_ref().eq(&other.as_ref())
+    }
+}
+
+impl<'a, T, U> PartialEq<ImgRef<'a, U>> for ImgVec<T> where T: PartialEq<U> {
+    #[allow(deprecated)]
+    #[inline(always)]
+    fn eq(&self, other: &ImgRef<'a, U>) -> bool {
+        self.as_ref().eq(other)
+    }
+}
+
+impl<'a, T, U> PartialEq<ImgVec<U>> for ImgRef<'a, T> where T: PartialEq<U> {
+    #[allow(deprecated)]
+    #[inline(always)]
+    fn eq(&self, other: &ImgVec<U>) -> bool {
+        self.eq(&other.as_ref())
+    }
+}
+
+impl<'a, 'b, T, U> PartialEq<ImgRef<'b, U>> for ImgRefMut<'a, T> where T: PartialEq<U> {
+    #[allow(deprecated)]
+    #[inline(always)]
+    fn eq(&self, other: &ImgRef<'b, U>) -> bool {
+        self.as_ref().eq(other)
+    }
+}
+
+impl<'a, 'b, T, U> PartialEq<ImgRefMut<'b, U>> for ImgRef<'a, T> where T: PartialEq<U> {
+    #[allow(deprecated)]
+    #[inline(always)]
+    fn eq(&self, other: &ImgRefMut<'b, U>) -> bool {
+        self.eq(&other.as_ref())
+    }
+}
+
+impl<'a, T: Eq> Eq for ImgRefMut<'a, T> {
+}
+
+impl<'a, T: Eq> Eq for ImgRef<'a, T> {
+}
+
+impl<T: Eq> Eq for ImgVec<T> {
+}
+
+#[test]
+fn test_eq_hash() {
+    #[derive(Debug)]
+    struct Comparable(u16);
+    impl PartialEq<u8> for Comparable {
+        fn eq(&self, other: &u8) -> bool { self.0 == *other as u16 }
+    }
+
+    let newtype = ImgVec::new(vec![Comparable(0), Comparable(1), Comparable(2), Comparable(3)], 2, 2);
+    let mut img1 = ImgVec::new(vec![0u8, 1, 2, 3], 2, 2);
+    let img_ne = ImgVec::new(vec![0u8, 1, 2, 3], 4, 1);
+    let img2 = ImgVec::new_stride(vec![0u8, 1, 255, 2, 3, 255], 2, 2, 3);
+    let mut img3 = ImgVec::new_stride(vec![0u8, 1, 255, 2, 3], 2, 2, 3);
+
+    assert_eq!(newtype, img1);
+    equiv(&img1, &img2);
+    equiv(&img2, &img3);
+    equiv(&img1, &img3);
+
+    assert_ne!(img1, img_ne);
+    assert_eq!(img1.as_ref(), img2);
+    assert_eq!(img2, img3.as_ref());
+    equiv(&img1.as_ref(), &img3.as_ref());
+    equiv(&img1.as_mut(), &img3.as_mut());
+    assert_eq!(img2.as_ref(), img3.as_mut());
+
+    let mut map = HashSet::new();
+    img3[(0usize,0usize)] = 100;
+    assert_ne!(img1, img3);
+    assert!(map.insert(img1));
+    assert!(map.insert(img3));
+    assert!(map.insert(img_ne));
+    assert!(!map.insert(img2));
+}
+
+#[cfg(test)]
+use std::fmt::Debug;
+#[cfg(test)]
+use std::collections::HashSet;
+
+#[cfg(test)]
+fn equiv<A>(a: &A, b: &A) where A: Eq + PartialEq + Hash + Debug {
+    assert_eq!(a, b);
+    let mut map = HashSet::new();
+    assert!(map.insert(a));
+    assert!(!map.insert(b));
+    assert!(!map.insert(a));
+    assert!(map.remove(b));
+    assert!(map.is_empty());
+}