diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 9864659..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,64 +0,0 @@ -# Check that everything (tests, benches, etc) builds in std environments -precheck_steps: &precheck_steps - docker: - - image: jamwaffles/circleci-embedded-graphics:1.40.0-cimg - auth: - username: jamwaffles - password: $DOCKERHUB_PASSWORD - steps: - - checkout - - restore_cache: - key: v1-{{ .Environment.CIRCLE_PROJECT_REPONAME }}-{{ .Environment.CIRCLE_JOB }}-{{ checksum "Cargo.toml" }} - - run: rustup default ${RUST_VERSION:-stable} - - run: rustup component add rustfmt - - run: cargo update - - run: just build - - save_cache: - key: v1-{{ .Environment.CIRCLE_PROJECT_REPONAME }}-{{ .Environment.CIRCLE_JOB }}-{{ checksum "Cargo.toml" }} - paths: - - ./target - - /home/circleci/.cargo/registry - -# Build crates for embedded target -target_steps: &target_steps - docker: - - image: jamwaffles/circleci-embedded-graphics:1.40.0-cimg - auth: - username: jamwaffles - password: $DOCKERHUB_PASSWORD - steps: - - checkout - - restore_cache: - keys: - - v1-{{ .Environment.CIRCLE_PROJECT_REPONAME }}-{{ .Environment.CIRCLE_JOB }}-{{ checksum "Cargo.toml" }} - - run: just install-targets - - run: cargo update - - run: just build-targets --release - - save_cache: - key: v1-{{ .Environment.CIRCLE_PROJECT_REPONAME }}-{{ .Environment.CIRCLE_JOB }}-{{ checksum "Cargo.toml" }} - paths: - - ./target - - /home/circleci/.cargo/registry - -version: 2 -jobs: - precheck-stable: - <<: *precheck_steps - precheck-beta: - environment: - - RUST_VERSION: "beta" - <<: *precheck_steps - - all-targets: - <<: *target_steps - -build_jobs: &build_jobs - jobs: - - precheck-stable - - precheck-beta - - all-targets - -workflows: - version: 2 - build_all: - <<: *build_jobs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..bd230b4 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,60 @@ +name: CI + +on: [push, pull_request] + +jobs: + rustfmt: + name: Check formatting + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt + - run: cargo fmt --check + + tests: + name: Tests (stable) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - run: cargo test + + tests-msrv: + name: Tests (MSRV) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@1.81 + - run: cargo test + + build-no_std: + name: Build no_std + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + target: thumbv7m-none-eabi + - run: cargo build --target thumbv7m-none-eabi + + build-benches: + name: Build benches + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - run: cargo bench --no-run + + check-readme: + name: Check readme + runs-on: ubuntu-latest + steps: + - uses: taiki-e/install-action@just + - uses: taiki-e/install-action@v2 + with: + tool: cargo-readme + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - run: just check-readme diff --git a/CHANGELOG.md b/CHANGELOG.md index 033786d..91b540e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,39 @@ ## [Unreleased] - ReleaseDate +### Changed + +- **(breaking)** [#22](https://github.com/embedded-graphics/tinytga/pull/22) Use 1.81 as MSRV. + +## [0.5.0] - 2023-05-17 + +### Added + +- [#16](https://github.com/embedded-graphics/tinytga/pull/16) Added support for bottom right and top right image origins. + +### Changed + +- **(breaking)** [#16](https://github.com/embedded-graphics/tinytga/pull/16) Use 1.61 as MSRV. +- **(breaking)** [#16](https://github.com/embedded-graphics/tinytga/pull/16) Replaced `ImageType` enum with `DataType` and `Compression`. +- **(breaking)** [#16](https://github.com/embedded-graphics/tinytga/pull/16) Color types used with `Tga` are now required to implement `From + From + From`. +- [#16](https://github.com/embedded-graphics/tinytga/pull/16) Improved drawing performance for bottom left origin images by using `fill_contiguous`. +- [#16](https://github.com/embedded-graphics/tinytga/pull/16) Use correct lifetimes for `RawTga::image_id`, `RawTga::developer_dictionary` and `RawTga::extension_area`. +- **(breaking)** [#18](https://github.com/embedded-graphics/tinytga/pull/18) Updated `embedded-graphics` to `0.8`. + +### Removed + +- **(breaking)** [#16](https://github.com/embedded-graphics/tinytga/pull/16) Removed `DynamicTga`, use `Tga` instead. + +## [0.4.1] - 2021-06-16 + +### Changed + +- [#10](https://github.com/embedded-graphics/tinytga/pull/10) Bump embedded-graphics minimum version from 0.7.0 to 0.7.1 + +## [0.4.0] - 2021-06-06 + +## [0.4.0-beta.1] - 2021-05-24 + ## [0.4.0-alpha.1] - 2020-12-27 ### Changed @@ -101,8 +134,12 @@ - [#218](https://github.com/embedded-graphics/embedded-graphics/pull/218) Test README examples in CI and update them to work with latest crate versions. -[unreleased]: https://github.com/embedded-graphics/tinytga/compare/v0.4.0-alpha.1...HEAD +[unreleased]: https://github.com/embedded-graphics/tinytga/compare/v0.5.0...HEAD +[0.5.0]: https://github.com/embedded-graphics/tinytga/compare/v0.4.1...v0.5.0 +[0.4.1]: https://github.com/embedded-graphics/tinytga/compare/v0.4.0...v0.4.1 +[0.4.0]: https://github.com/embedded-graphics/tinytga/compare/v0.4.0-beta.1...v0.4.0 +[0.4.0-beta.1]: https://github.com/embedded-graphics/tinytga/compare/v0.4.0-alpha.1...v0.4.0-beta.1 [0.4.0-alpha.1]: https://github.com/embedded-graphics/tinytga/compare/after-split...v0.4.0-alpha.1 [0.4.0-alpha.1 - `embedded-graphics` repository]: https://github.com/embedded-graphics/embedded-graphics/compare/tinytga-v0.3.2...before-split [0.3.2]: https://github.com/embedded-graphics/embedded-graphics/compare/tinytga-v0.3.0...tinytga-v0.3.2 diff --git a/Cargo.toml b/Cargo.toml index 879cd6e..e0ff24b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "tinytga" -version = "0.4.0-alpha.1" +version = "0.5.0" description = "No-std, low memory footprint TGA image loader" -authors = ["James Waples "] -edition = "2018" +authors = ["James Waples ", "Ralf Fuest "] +edition = "2021" repository = "https://github.com/embedded-graphics/tinytga" documentation = "https://docs.rs/tinytga" categories = ["embedded", "no-std", "multimedia::images"] @@ -12,17 +12,19 @@ readme = "./README.md" license = "MIT OR Apache-2.0" exclude = [ "/.github/", - "/.circleci/", ".gitignore", ] -[badges] -circle-ci = { repository = "embedded-graphics/tinytga", branch = "master" } +[[bench]] +name = "draw" +harness = false [dependencies] -embedded-graphics-core = "0.1.1" -nom = { version = "6.0.1", default-features = false } +embedded-graphics = "0.8.0" +nom = { version = "7.1.1", default-features = false } [dev-dependencies] -embedded-graphics = "0.7.0-alpha.2" paste = "1.0" +criterion = "0.3.5" +clap = { version = "3.2.22", features = ["derive"] } +embedded-graphics-simulator = { version = "0.5.0", default-features = false } diff --git a/README.md b/README.md index 1c58ff5..32c849d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # TinyTGA -[![Build Status](https://circleci.com/gh/embedded-graphics/tinytga/tree/master.svg?style=shield)](https://circleci.com/gh/embedded-graphics/tinytga/tree/master) +[![CI Status](https://github.com/embedded-graphics/tinytga/actions/workflows/ci.yml/badge.svg)](https://github.com/embedded-graphics/tinytga/actions/workflows/ci.yml) [![Crates.io](https://img.shields.io/crates/v/tinytga.svg)](https://crates.io/crates/tinytga) [![Docs.rs](https://docs.rs/tinytga/badge.svg)](https://docs.rs/tinytga) [![embedded-graphics on Matrix](https://img.shields.io/matrix/rust-embedded-graphics:matrix.org)](https://matrix.to/#/#rust-embedded-graphics:matrix.org) @@ -20,10 +20,6 @@ the TGA file. But it is also possible to directly access the raw pixel represent This example demonstrates how a TGA image can be drawn to a [embedded-graphics] draw target. -The code uses the `Tga` struct and only works if the color format inside the TGA file is known -at compile time. While this makes the code less flexible it offers the best performance by -making sure that no unnecessary color conversions are used. - ```rust use embedded_graphics::{image::Image, pixelcolor::Rgb888, prelude::*}; use tinytga::Tga; @@ -38,26 +34,6 @@ let image = Image::new(&tga, Point::zero()); image.draw(&mut display)?; ``` -### Using `DynamicTga` to draw an image - -The previous example had the limitation that the color format needed to be known at compile -time. In some use cases this can be a problem, for example if user supplied images should -be displayed. To handle these cases `DynamicTga` can be used, which performs color conversion -if necessary. - -```rust -use embedded_graphics::{image::Image, pixelcolor::Rgb888, prelude::*}; -use tinytga::DynamicTga; - -// Include an image from a local path as bytes -let data = include_bytes!("../tests/chessboard_4px_rle.tga"); - -let tga = DynamicTga::from_slice(data).unwrap(); - -let image = Image::new(&tga, Point::zero()); - -image.draw(&mut display)?; -``` ### Accessing pixels using an embedded-graphics color type If [embedded-graphics] is not used to draw the TGA image, the color types provided by @@ -66,7 +42,7 @@ If [embedded-graphics] is not used to draw the TGA image, the color types provid ```rust use embedded_graphics::{prelude::*, pixelcolor::Rgb888}; -use tinytga::{Bpp, ImageOrigin, ImageType, RawPixel, Tga, TgaHeader}; +use tinytga::Tga; // Include an image from a local path as bytes let data = include_bytes!("../tests/chessboard_4px_rle.tga"); @@ -90,7 +66,7 @@ accessed with the `pixels` method on ```rust use embedded_graphics::{prelude::*, pixelcolor::Rgb888}; -use tinytga::{Bpp, ImageOrigin, ImageType, RawPixel, RawTga, TgaHeader}; +use tinytga::{Bpp, Compression, DataType, ImageOrigin, RawPixel, RawTga, TgaHeader}; // Include an image from a local path as bytes. let data = include_bytes!("../tests/chessboard_4px_rle.tga"); @@ -104,7 +80,8 @@ assert_eq!( TgaHeader { id_len: 0, has_color_map: false, - image_type: ImageType::RleTruecolor, + data_type: DataType::TrueColor, + compression: Compression::Rle, color_map_start: 0, color_map_len: 0, color_map_depth: None, @@ -124,13 +101,15 @@ let pixels: Vec<_> = img.pixels().collect(); ## Embedded-graphics drawing performance -`Tga` should by used instead of `DynamicTga` when possible to reduce the risk of -accidentally adding unnecessary color conversions. - `tinytga` uses different code paths to draw images with different `ImageOrigin`s. The performance difference between the origins will depend on the display driver, but using images with the origin at the top left corner will generally result in the best performance. +## Minimum supported Rust version + +The minimum supported Rust version for tinytga is `1.81` or greater. +Ensure you have the correct version of Rust installed, preferably through . + [embedded-graphics]: https://docs.rs/embedded-graphics ## License diff --git a/README.tpl b/README.tpl index f806f24..fbd2ecc 100644 --- a/README.tpl +++ b/README.tpl @@ -1,6 +1,6 @@ # TinyTGA -[![Build Status](https://circleci.com/gh/embedded-graphics/tinytga/tree/master.svg?style=shield)](https://circleci.com/gh/embedded-graphics/tinytga/tree/master) +[![CI Status](https://github.com/embedded-graphics/tinytga/actions/workflows/ci.yml/badge.svg)](https://github.com/embedded-graphics/tinytga/actions/workflows/ci.yml) [![Crates.io](https://img.shields.io/crates/v/tinytga.svg)](https://crates.io/crates/tinytga) [![Docs.rs](https://docs.rs/tinytga/badge.svg)](https://docs.rs/tinytga) [![embedded-graphics on Matrix](https://img.shields.io/matrix/rust-embedded-graphics:matrix.org)](https://matrix.to/#/#rust-embedded-graphics:matrix.org) diff --git a/benches/draw.rs b/benches/draw.rs new file mode 100644 index 0000000..f2055ef --- /dev/null +++ b/benches/draw.rs @@ -0,0 +1,93 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use embedded_graphics::{ + image::Image, + pixelcolor::{Gray8, Rgb555, Rgb888}, + prelude::*, +}; +use tinytga::Tga; + +// TODO: use e-g framebuffer when it's added +struct Framebuffer { + pixels: [[C; 240]; 320], +} + +impl> Framebuffer { + pub fn new() -> Self { + let color = C::from(Rgb888::BLACK); + + Self { + pixels: [[color; 240]; 320], + } + } +} + +impl DrawTarget for Framebuffer { + type Color = C; + type Error = std::convert::Infallible; + + fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> + where + I: IntoIterator>, + { + for Pixel(p, c) in pixels { + self.pixels[p.y as usize][p.x as usize] = c; + } + + Ok(()) + } +} + +impl OriginDimensions for Framebuffer { + fn size(&self) -> embedded_graphics::prelude::Size { + Size::new(240, 320) + } +} + +macro_rules! bench { + ($c:expr, $color_type:ty, $file:expr) => { + $c.bench_function(concat!(stringify!($color_type), " ", $file), |b| { + let mut fb = Framebuffer::<$color_type>::new(); + b.iter(|| { + let bmp = Tga::<$color_type>::from_slice(include_bytes!(concat!( + "../tests/", + $file, + ".tga" + ))) + .unwrap(); + Image::new(&bmp, Point::zero()).draw(&mut fb).unwrap(); + }) + }); + }; + + ($c:expr, $color_type:ty) => { + bench!($c, $color_type, "logo_type1_16bpp_bl"); + bench!($c, $color_type, "logo_type1_16bpp_tl"); + bench!($c, $color_type, "logo_type1_24bpp_bl"); + bench!($c, $color_type, "logo_type1_24bpp_tl"); + bench!($c, $color_type, "logo_type2_16bpp_bl"); + bench!($c, $color_type, "logo_type2_16bpp_tl"); + bench!($c, $color_type, "logo_type2_24bpp_bl"); + bench!($c, $color_type, "logo_type2_24bpp_tl"); + bench!($c, $color_type, "logo_type3_bl"); + bench!($c, $color_type, "logo_type3_tl"); + bench!($c, $color_type, "logo_type9_16bpp_bl"); + bench!($c, $color_type, "logo_type9_16bpp_tl"); + bench!($c, $color_type, "logo_type9_24bpp_bl"); + bench!($c, $color_type, "logo_type9_24bpp_tl"); + bench!($c, $color_type, "logo_type10_16bpp_bl"); + bench!($c, $color_type, "logo_type10_16bpp_tl"); + bench!($c, $color_type, "logo_type10_24bpp_bl"); + bench!($c, $color_type, "logo_type10_24bpp_tl"); + bench!($c, $color_type, "logo_type11_bl"); + bench!($c, $color_type, "logo_type11_tl"); + }; +} + +fn draw_benchmarks(c: &mut Criterion) { + bench!(c, Rgb888); + bench!(c, Rgb555); + bench!(c, Gray8); +} + +criterion_group!(benches, draw_benchmarks); +criterion_main!(benches); diff --git a/doc/releasing.md b/doc/releasing.md new file mode 100644 index 0000000..b405ae8 --- /dev/null +++ b/doc/releasing.md @@ -0,0 +1,62 @@ +# Release process + +Target audience: crate maintainers who wish to release `tinytga`. + +> Please take a cautious approach to this. If any step doesn't feel right or doesn't succeed smoothly, stop and rectify any issues before continuing. + +## On GitHub + +- Check that all desired PRs are merged and all desired issues are closed/resolved. +- Check that the latest master build passed in all CI checks. + +## On your local machine + +- `cd` to the repository root +- Check that `cargo-release` is installed and available in `$PATH`: + + ```bash + cargo release --version + ``` + +- Ensure you have the latest changes with `git switch master` and `git pull --rebase` +- Check that your local repository is clean with no uncommitted changes and no unpushed commits. Ideally, use `git reset --hard origin/master` to ensure your local state is up to date with `origin/master`. You may need to change `origin` to the name of the remote pointing to . +- Before a **stable** release: + - Search the repository for any `TODO` or `FIXME` comments. If any need resolving before release, stop this process and fix them with one or more PRs. +- Check that the crate version in `Cargo.toml` matches the latest released versions on . +- Run `just build` to ensure the build passes locally. + - If the build fails for any reason, stop the release process and fix any issues by creating PRs. The upstream master branch must remain the source of truth. Restart this checklist once `just build` passes. +- Double check the release level (major, minor, patch) +- Release the crate: + + ```bash + cargo release --push-remote + ``` + + Where `` is `major`, `minor`, `patch`, or a specific SemVer version number, and where `` is the git remote for the upstream repository `embedded-graphics/tinytga`. + +## Post release + +- Check that the release command pushed a Git tag when the crate was published, something like `v0.4.0-beta.1` or `v0.3.1`. +- For the new tag, go to its page at e.g. , click Edit tag and draft a release: + + - Copy and paste the tag into the `Release title` field. + - Copy and paste the latest released section out of the crate's `CHANGELOG.md` file into the `Describe this release` field. Do not include the version header, e.g.: + + ```markdown + ### Added + + - [#111](https://github.com/embedded-graphics/tinytga/pull/111) Added something + + ### Removed + + - [#222](https://github.com/embedded-graphics/tinytga/pull/222) Removed a thing + ``` + + - For `alpha` or `beta` releases, check the `This is a pre-release` checkbox. + - Hit Publish release + +- Check that the release is displayed on the [repository homepage](https://github.com/embedded-graphics/tinytga). +- Post a link to the released tag (e.g. ) to the embedded-graphics Matrix room at +- If you are @jamwaffles, post a Tweet tagging @rustembedded with a happy announcement message. + +- Check the other repositories in the [embedded-graphics organization](https://github.com/embedded-graphics) for dependencies on `tinytga`. The version should be updated to the latest releases made whilst following this guide. diff --git a/examples/display.rs b/examples/display.rs new file mode 100644 index 0000000..3acb5fd --- /dev/null +++ b/examples/display.rs @@ -0,0 +1,75 @@ +//! This example displays TGA images using the embedded-graphics simulator. +//! +//! Basic usage: `cargo run --example display -- TGA_FILE` +//! +//! More usage and arguments can be listed by running `cargo run --example display -- --help` + +use clap::{ArgEnum, Parser}; +use embedded_graphics::{ + image::Image, + pixelcolor::{BinaryColor, Gray8, Rgb555, Rgb565, Rgb888}, + prelude::*, +}; +use embedded_graphics_simulator::{ + OutputSettings, OutputSettingsBuilder, SimulatorDisplay, Window, +}; +use std::{fs, num::NonZeroU32, path::PathBuf}; +use tinytga::Tga; + +#[derive(Debug, Clone, Copy, ArgEnum)] +#[clap(rename_all = "PascalCase")] +enum ColorType { + Rgb555, + Rgb565, + Rgb888, + Gray8, + BinaryColor, +} + +#[derive(Parser)] +struct Args { + /// Pixel scale + #[clap(long, default_value = "1")] + scale: NonZeroU32, + + /// Display color type + #[clap(arg_enum, long, default_value = "Rgb888")] + color_type: ColorType, + + /// BMP file + bmp_file: PathBuf, +} + +fn display_tga(data: &[u8], settings: &OutputSettings) +where + C: PixelColor + From + From + From + Into, +{ + let bmp = Tga::::from_slice(&data).unwrap(); + + let mut display = SimulatorDisplay::::new(bmp.size()); + + Image::new(&bmp, Point::zero()) + .draw(&mut display.color_converted()) + .unwrap(); + + let mut window = Window::new("TGA viewer", &settings); + window.show_static(&display); +} + +fn main() { + let args = Args::parse(); + + let settings = OutputSettingsBuilder::new() + .scale(args.scale.into()) + .build(); + + let data = fs::read(&args.bmp_file).unwrap(); + + match args.color_type { + ColorType::Rgb555 => display_tga::(&data, &settings), + ColorType::Rgb565 => display_tga::(&data, &settings), + ColorType::Rgb888 => display_tga::(&data, &settings), + ColorType::Gray8 => display_tga::(&data, &settings), + ColorType::BinaryColor => display_tga::(&data, &settings), + } +} diff --git a/justfile b/justfile index 9f6beef..755dfc8 100644 --- a/justfile +++ b/justfile @@ -11,9 +11,9 @@ build: check-formatting test build-benches check-readme check-links build-benches: cargo bench --no-run -# Run cargo test in release mode +# Run cargo test test: - cargo test --release + cargo test # Check the formatting check-formatting: diff --git a/release.toml b/release.toml index 074cfe4..204727b 100644 --- a/release.toml +++ b/release.toml @@ -1,4 +1,3 @@ -no-dev-version = true pre-release-replacements = [ {file="CHANGELOG.md", prerelease=true, search="[Uu]nreleased", replace="{{version}}"}, {file="CHANGELOG.md", prerelease=true, search="\\.\\.\\.HEAD", replace="...{{tag_name}}"}, diff --git a/src/color_map.rs b/src/color_map.rs index cea1b2e..8b8a238 100644 --- a/src/color_map.rs +++ b/src/color_map.rs @@ -1,4 +1,7 @@ use crate::{parse_error::ParseError, Bpp, TgaHeader}; +use embedded_graphics::{ + iterator::raw::RawDataSlice, pixelcolor::raw::LittleEndian, prelude::PixelColor, +}; use nom::bytes::complete::take; /// Color map. @@ -48,7 +51,7 @@ impl<'a> ColorMap<'a> { /// Returns the raw color value for a color map entry. pub fn get_raw(&self, index: usize) -> Option { - //TODO: use start_index + //TODO: use start_index and add test if index >= usize::from(self.length) { return None; } @@ -72,4 +75,15 @@ impl<'a> ColorMap<'a> { ]), }) } + + pub(crate) fn get(&self, index: usize) -> Option + where + C: PixelColor + From, + RawDataSlice<'a, C::Raw, LittleEndian>: IntoIterator, + { + RawDataSlice::new(self.data) + .into_iter() + .nth(index) + .map(|r| C::from(r)) + } } diff --git a/src/dynamic_tga.rs b/src/dynamic_tga.rs deleted file mode 100644 index 88f34f3..0000000 --- a/src/dynamic_tga.rs +++ /dev/null @@ -1,92 +0,0 @@ -use core::marker::PhantomData; -use embedded_graphics_core::{ - pixelcolor::{Gray8, Rgb555, Rgb888}, - prelude::*, -}; - -use crate::{parse_error::ParseError, raw_tga::RawTga, Bpp}; - -/// Dynamic TGA image. -/// -/// `DynamicTga` can be used to draw images that don't have a known color type -/// at compile time, for example user supplied images. If the color type is -/// known at compile time consider using the [`Tga`] for improved performance. -/// -/// `DynamicTga` works with all draw targets that use a color type that implements -/// `From` for `Gray8`, `Rgb555` and `Rgb888`. -/// -/// [`Tga`]: struct.Tga.html -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] -pub struct DynamicTga<'a, C> { - raw: RawTga<'a>, - color_type: ColorType, - target_color_type: PhantomData, -} - -impl<'a, C> DynamicTga<'a, C> -where - C: PixelColor + From + From + From, -{ - /// Parses a TGA image from a byte slice. - pub fn from_slice(data: &'a [u8]) -> Result { - let raw = RawTga::from_slice(data)?; - - let color_type = match (raw.color_bpp(), raw.image_type().is_monochrome()) { - (Bpp::Bits8, true) => ColorType::Gray8, - (Bpp::Bits16, false) => ColorType::Rgb555, - (Bpp::Bits24, false) => ColorType::Rgb888, - _ => { - return Err(ParseError::UnsupportedDynamicTgaType( - raw.image_type(), - raw.color_bpp(), - )) - } - }; - - Ok(Self { - raw, - color_type, - target_color_type: PhantomData, - }) - } - - /// Returns a reference to the raw TGA image. - /// - /// The [`RawTga`] object can be used to access lower level details about the TGA file. - /// - /// [`RawTga`]: struct.RawTga.html - pub fn as_raw(&self) -> &RawTga<'a> { - &self.raw - } -} - -impl OriginDimensions for DynamicTga<'_, C> { - fn size(&self) -> Size { - self.raw.size() - } -} - -impl ImageDrawable for DynamicTga<'_, C> -where - C: PixelColor + From + From + From, -{ - type Color = C; - - fn draw(&self, target: &mut D) -> Result<(), D::Error> - where - D: DrawTarget, - { - match self.color_type { - ColorType::Gray8 => self.raw.draw(&mut target.color_converted::()), - ColorType::Rgb555 => self.raw.draw(&mut target.color_converted::()), - ColorType::Rgb888 => self.raw.draw(&mut target.color_converted::()), - } - } -} - -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] -enum ColorType { - Gray8, - Rgb555, - Rgb888, -} diff --git a/src/footer.rs b/src/footer.rs index e727de0..eb1b4d5 100644 --- a/src/footer.rs +++ b/src/footer.rs @@ -81,7 +81,7 @@ fn offset(input: &[u8]) -> IResult<&[u8], Option> { map(le_u32, |offset| NonZeroUsize::new(offset as usize))(input) } -fn parse_footer<'a>(input: &'a [u8]) -> IResult<&[u8], TgaFooter> { +fn parse_footer(input: &[u8]) -> IResult<&[u8], TgaFooter> { let footer_start = input .len() .checked_sub(TGA_FOOTER_LENGTH) diff --git a/src/header.rs b/src/header.rs index b186962..42da7d4 100644 --- a/src/header.rs +++ b/src/header.rs @@ -1,10 +1,11 @@ -use crate::parse_error::ParseError; use nom::{ combinator::{map, map_opt, map_res}, number::complete::{le_u16, le_u8}, IResult, }; +use crate::parse_error::ParseError; + /// Bits per pixel. #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] #[non_exhaustive] @@ -59,60 +60,47 @@ impl Bpp { } } -/// Image type +/// Image data compression. #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] -pub enum ImageType { - /// Image contains no pixel data - Empty = 0, - - /// Color mapped image - ColorMapped = 1, - - /// Truecolor image - Truecolor = 2, - - /// Monochrome (greyscale) image - Monochrome = 3, - - /// Run length encoded color mapped image - RleColorMapped = 9, - - /// Run length encoded RGB image - RleTruecolor = 10, +pub enum Compression { + /// Uncompressed image data. + Uncompressed, + /// Run-length encoded image data. + Rle, +} - /// Run length encoded monochrome (greyscale) image - RleMonochrome = 11, +/// Image data type. +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] +pub enum DataType { + /// No image data. + NoData, + /// Color mapped. + ColorMapped, + /// True color. + TrueColor, + /// Black and white or grayscale. + BlackAndWhite, } -impl ImageType { - fn parse(input: &[u8]) -> IResult<&[u8], Self> { - map_res(le_u8, |b| match b { - 0 => Ok(Self::Empty), - 1 => Ok(Self::ColorMapped), - 2 => Ok(Self::Truecolor), - 3 => Ok(Self::Monochrome), - 9 => Ok(Self::RleColorMapped), - 10 => Ok(Self::RleTruecolor), - 11 => Ok(Self::RleMonochrome), - other => Err(ParseError::UnsupportedImageType(other)), - })(input) +fn parse_image_type(image_type: u8) -> Result<(DataType, Compression), ParseError> { + if image_type & !0b1011 != 0 || image_type == 8 { + return Err(ParseError::UnsupportedImageType(image_type)); } - /// Returns `true` when the image is RLE encoded. - pub fn is_rle(self) -> bool { - match self { - ImageType::RleColorMapped | ImageType::RleTruecolor | ImageType::RleMonochrome => true, - _ => false, - } - } + let data_type = match image_type & 0x3 { + 1 => DataType::ColorMapped, + 2 => DataType::TrueColor, + 3 => DataType::BlackAndWhite, + _ => DataType::NoData, + }; - /// Returns `true` when the image is monochrome. - pub fn is_monochrome(self) -> bool { - match self { - ImageType::Monochrome | ImageType::RleMonochrome => true, - _ => false, - } - } + let compression = if image_type & 0x8 != 0 { + Compression::Rle + } else { + Compression::Uncompressed + }; + + Ok((data_type, compression)) } /// Image origin @@ -139,10 +127,7 @@ impl ImageOrigin { } pub(crate) fn is_bottom(self) -> bool { - match self { - Self::BottomLeft | Self::BottomRight => true, - _ => false, - } + matches!(self, Self::BottomLeft | Self::BottomRight) } } @@ -157,8 +142,11 @@ pub struct TgaHeader { /// Whether a color map is included in the image data pub has_color_map: bool, - /// Image type - pub image_type: ImageType, + /// Data type. + pub data_type: DataType, + + /// Compression. + pub compression: Compression, /// Color map origin pub color_map_start: u16, @@ -195,7 +183,7 @@ impl TgaHeader { pub(crate) fn parse(input: &[u8]) -> IResult<&[u8], Self> { let (input, id_len) = le_u8(input)?; let (input, has_color_map) = has_color_map(input)?; - let (input, image_type) = ImageType::parse(input)?; + let (input, (data_type, compression)) = map_res(le_u8, parse_image_type)(input)?; let (input, color_map_start) = le_u16(input)?; let (input, color_map_len) = le_u16(input)?; let (input, color_map_depth) = Bpp::parse_opt(input)?; @@ -214,7 +202,8 @@ impl TgaHeader { TgaHeader { id_len, has_color_map, - image_type, + data_type, + compression, color_map_start, color_map_len, color_map_depth, diff --git a/src/lib.rs b/src/lib.rs index 35656b0..0ee6022 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,10 +11,6 @@ //! //! This example demonstrates how a TGA image can be drawn to a [embedded-graphics] draw target. //! -//! The code uses the [`Tga`] struct and only works if the color format inside the TGA file is known -//! at compile time. While this makes the code less flexible it offers the best performance by -//! making sure that no unnecessary color conversions are used. -//! //! ```rust //! # fn main() -> Result<(), core::convert::Infallible> { //! # let mut display = embedded_graphics::mock_display::MockDisplay::default(); @@ -32,29 +28,6 @@ //! # Ok::<(), core::convert::Infallible>(()) } //! ``` //! -//! ## Using `DynamicTga` to draw an image -//! -//! The previous example had the limitation that the color format needed to be known at compile -//! time. In some use cases this can be a problem, for example if user supplied images should -//! be displayed. To handle these cases [`DynamicTga`] can be used, which performs color conversion -//! if necessary. -//! -//! ```rust -//! # fn main() -> Result<(), core::convert::Infallible> { -//! # let mut display = embedded_graphics::mock_display::MockDisplay::::default(); -//! use embedded_graphics::{image::Image, pixelcolor::Rgb888, prelude::*}; -//! use tinytga::DynamicTga; -//! -//! // Include an image from a local path as bytes -//! let data = include_bytes!("../tests/chessboard_4px_rle.tga"); -//! -//! let tga = DynamicTga::from_slice(data).unwrap(); -//! -//! let image = Image::new(&tga, Point::zero()); -//! -//! image.draw(&mut display)?; -//! # Ok::<(), core::convert::Infallible>(()) } -//! ``` //! ## Accessing pixels using an embedded-graphics color type //! //! If [embedded-graphics] is not used to draw the TGA image, the color types provided by @@ -63,7 +36,7 @@ //! //! ```rust //! use embedded_graphics::{prelude::*, pixelcolor::Rgb888}; -//! use tinytga::{Bpp, ImageOrigin, ImageType, RawPixel, Tga, TgaHeader}; +//! use tinytga::Tga; //! //! // Include an image from a local path as bytes //! let data = include_bytes!("../tests/chessboard_4px_rle.tga"); @@ -87,7 +60,7 @@ //! //! ```rust //! use embedded_graphics::{prelude::*, pixelcolor::Rgb888}; -//! use tinytga::{Bpp, ImageOrigin, ImageType, RawPixel, RawTga, TgaHeader}; +//! use tinytga::{Bpp, Compression, DataType, ImageOrigin, RawPixel, RawTga, TgaHeader}; //! //! // Include an image from a local path as bytes. //! let data = include_bytes!("../tests/chessboard_4px_rle.tga"); @@ -101,7 +74,8 @@ //! TgaHeader { //! id_len: 0, //! has_color_map: false, -//! image_type: ImageType::RleTruecolor, +//! data_type: DataType::TrueColor, +//! compression: Compression::Rle, //! color_map_start: 0, //! color_map_len: 0, //! color_map_depth: None, @@ -121,20 +95,19 @@ //! //! # Embedded-graphics drawing performance //! -//! [`Tga`] should by used instead of [`DynamicTga`] when possible to reduce the risk of -//! accidentally adding unnecessary color conversions. -//! //! `tinytga` uses different code paths to draw images with different [`ImageOrigin`]s. //! The performance difference between the origins will depend on the display driver, but using //! images with the origin at the top left corner will generally result in the best performance. //! +//! # Minimum supported Rust version +//! +//! The minimum supported Rust version for tinytga is `1.81` or greater. +//! Ensure you have the correct version of Rust installed, preferably through . +//! //! [`ImageOrigin`]: enum.ImageOrigin.html //! [embedded-graphics]: https://docs.rs/embedded-graphics //! [`Tga`]: ./struct.Tga.html //! [`RawTga`]: ./struct.RawTga.html -//! [`DynamicTga`]: ./struct.DynamicTga.html -//! [`image_type`]: ./struct.TgaHeader.html#structfield.image_type -//! [`pixel_data`]: ./struct.Tga.html#structfield.pixel_data #![no_std] #![deny(missing_docs)] @@ -148,25 +121,30 @@ #![deny(unused_qualifications)] mod color_map; -mod dynamic_tga; mod footer; mod header; -mod packet; mod parse_error; mod pixels; -mod raw_pixels; +mod raw_iter; mod raw_tga; use core::marker::PhantomData; -use embedded_graphics_core::prelude::*; +use embedded_graphics::{ + pixelcolor::{ + raw::{RawU16, RawU24, RawU8}, + Gray8, Rgb555, Rgb888, + }, + prelude::*, + primitives::Rectangle, +}; +use raw_iter::{RawColors, Rle, Uncompressed}; pub use crate::{ color_map::ColorMap, - dynamic_tga::DynamicTga, - header::{Bpp, ImageOrigin, ImageType, TgaHeader}, + header::{Bpp, Compression, DataType, ImageOrigin, TgaHeader}, parse_error::ParseError, pixels::Pixels, - raw_pixels::{RawPixel, RawPixels}, + raw_iter::{RawPixel, RawPixels}, raw_tga::RawTga, }; @@ -176,44 +154,38 @@ pub struct Tga<'a, C> { /// Raw TGA file. raw: RawTga<'a>, + image_color_type: ColorType, + /// Color type. - color_type: PhantomData, + target_color_type: PhantomData, } impl<'a, C> Tga<'a, C> where - C: PixelColor + From<::Raw>, + C: PixelColor + From + From + From, { /// Parses a TGA image from a byte slice. - /// - /// # Errors - /// - /// If the bit depth of the source image does not match the bit depth of the output color type - /// `C`, this method will return a [`ParseError::MismatchedBpp`] error. - /// - /// [`ParseError::MismatchedBpp`]: enum.ParseError.html#variant.MismatchedBpp pub fn from_slice(data: &'a [u8]) -> Result { let raw = RawTga::from_slice(data)?; - Self::from_raw(raw) - } - - /// Converts a raw TGA object into a embedded-graphics TGA object. - /// - /// # Errors - /// - /// If the bit depth of the source image does not match the bit depth of the output color type - /// `C`, this method will return a [`ParseError::MismatchedBpp`] error. - /// - /// [`ParseError::MismatchedBpp`]: enum.ParseError.html#variant.MismatchedBpp - pub fn from_raw(raw: RawTga<'a>) -> Result { - if C::Raw::BITS_PER_PIXEL != usize::from(raw.color_bpp().bits()) { - return Err(ParseError::MismatchedBpp(raw.color_bpp().bits())); - } + let image_color_type = match (raw.color_bpp(), raw.data_type()) { + (Bpp::Bits8, DataType::BlackAndWhite) => ColorType::Gray8, + (Bpp::Bits16, DataType::ColorMapped) => ColorType::Rgb555, + (Bpp::Bits16, DataType::TrueColor) => ColorType::Rgb555, + (Bpp::Bits24, DataType::ColorMapped) => ColorType::Rgb888, + (Bpp::Bits24, DataType::TrueColor) => ColorType::Rgb888, + _ => { + return Err(ParseError::UnsupportedTgaType( + raw.data_type(), + raw.color_bpp(), + )); + } + }; Ok(Tga { raw, - color_type: PhantomData, + image_color_type, + target_color_type: PhantomData, }) } @@ -227,8 +199,113 @@ where } /// Returns an iterator over the pixels in this image. - pub fn pixels<'b>(&'b self) -> Pixels<'b, 'a, C> { - Pixels::new(self.raw.pixels()) + pub fn pixels(&self) -> Pixels<'_, C> { + Pixels::new(self) + } + + fn draw_colors( + &self, + target: &mut D, + mut colors: impl Iterator, + ) -> Result<(), D::Error> + where + D: DrawTarget, + { + let bounding_box = self.bounding_box(); + if bounding_box.is_zero_sized() { + return Ok(()); + } + + let origin = self.raw.image_origin(); + + // TGA files with the origin in the top left corner can be drawn using `fill_contiguous`. + // All other origins are drawn by falling back to `draw_iter`. + match origin { + ImageOrigin::TopLeft => target.fill_contiguous(&bounding_box, colors), + ImageOrigin::BottomLeft => { + let mut row_rect = + Rectangle::new(Point::zero(), Size::new(bounding_box.size.width, 1)); + + for y in bounding_box.rows().rev() { + row_rect.top_left.y = y; + let row_colors = (&mut colors).take(bounding_box.size.width as usize); + target.fill_contiguous(&row_rect, row_colors)?; + } + + Ok(()) + } + ImageOrigin::TopRight => { + let max_x = bounding_box.bottom_right().map(|p| p.x).unwrap_or_default(); + + bounding_box + .points() + .zip(colors) + .map(|(p, c)| Pixel(Point::new(max_x - p.x, p.y), c)) + .draw(target) + } + ImageOrigin::BottomRight => { + let bottom_right = bounding_box.bottom_right().unwrap_or_default(); + + bounding_box + .points() + .zip(colors) + .map(|(p, c)| Pixel(bottom_right - p, c)) + .draw(target) + } + } + } + + fn draw_regular( + &self, + target: &mut D, + colors: RawColors<'a, CI::Raw, F>, + ) -> Result<(), D::Error> + where + D: DrawTarget, + CI: PixelColor + From + Into, + RawColors<'a, CI::Raw, F>: Iterator, + { + self.draw_colors(target, colors.map(|c| CI::from(c).into())) + } + + fn draw_color_mapped( + &self, + target: &mut D, + indices: RawColors<'a, R, F>, + ) -> Result<(), D::Error> + where + D: DrawTarget, + R: RawData, + R::Storage: Into, + RawColors<'a, R, F>: Iterator, + { + let color_map = if let Some(color_map) = self.raw.color_map() { + color_map + } else { + return Ok(()); + }; + + match self.image_color_type { + ColorType::Rgb555 => { + let colors = indices.map(|index| { + let index = index.into_inner().into() as usize; + color_map.get::(index).unwrap().into() + }); + + self.draw_colors(target, colors) + } + ColorType::Rgb888 => { + let colors = indices.map(|index| { + let index = index.into_inner().into() as usize; + color_map.get::(index).unwrap().into() + }); + + self.draw_colors(target, colors) + } + // Color mapped Gray8 images aren't supported. Using a color map for Gray8 images + // doesn't make sense, because this encoding will always be larger than a type 3 image. + ColorType::Gray8 => Ok(()), + } } } @@ -240,7 +317,7 @@ impl OriginDimensions for Tga<'_, C> { impl ImageDrawable for Tga<'_, C> where - C: PixelColor + From<::Raw>, + C: PixelColor + From + From + From, { type Color = C; @@ -248,6 +325,82 @@ where where D: DrawTarget, { - self.raw.draw(target) + match self.raw.image_data_bpp() { + Bpp::Bits8 => match self.raw.compression() { + Compression::Uncompressed => { + let colors = RawColors::::new(&self.raw); + + if self.raw.color_map().is_some() { + self.draw_color_mapped(target, colors) + } else { + self.draw_regular::<_, Gray8, _>(target, colors) + } + } + Compression::Rle => { + let colors = RawColors::::new(&self.raw); + + if self.raw.color_map().is_some() { + self.draw_color_mapped(target, colors) + } else { + self.draw_regular::<_, Gray8, _>(target, colors) + } + } + }, + Bpp::Bits16 => match self.raw.compression() { + Compression::Uncompressed => { + let colors = RawColors::::new(&self.raw); + + if self.raw.color_map().is_some() { + self.draw_color_mapped(target, colors) + } else { + self.draw_regular::<_, Rgb555, _>(target, colors) + } + } + Compression::Rle => { + let colors = RawColors::::new(&self.raw); + + if self.raw.color_map().is_some() { + self.draw_color_mapped(target, colors) + } else { + self.draw_regular::<_, Rgb555, _>(target, colors) + } + } + }, + Bpp::Bits24 => match self.raw.compression() { + Compression::Uncompressed => { + let colors = RawColors::::new(&self.raw); + + if self.raw.color_map().is_some() { + self.draw_color_mapped(target, colors) + } else { + self.draw_regular::<_, Rgb888, _>(target, colors) + } + } + Compression::Rle => { + let colors = RawColors::::new(&self.raw); + + if self.raw.color_map().is_some() { + self.draw_color_mapped(target, colors) + } else { + self.draw_regular::<_, Rgb888, _>(target, colors) + } + } + }, + Bpp::Bits32 => Ok(()), + } + } + + fn draw_sub_image(&self, target: &mut D, area: &Rectangle) -> Result<(), D::Error> + where + D: DrawTarget, + { + self.draw(&mut target.translated(-area.top_left).clipped(area)) } } + +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] +pub(crate) enum ColorType { + Gray8, + Rgb555, + Rgb888, +} diff --git a/src/packet.rs b/src/packet.rs deleted file mode 100644 index 375db91..0000000 --- a/src/packet.rs +++ /dev/null @@ -1,91 +0,0 @@ -use nom::{bytes::complete::take, number::complete::le_u8, IResult}; - -#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Hash, Eq, Ord)] -pub enum PacketType { - Raw, - Rle, -} - -#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Hash, Eq, Ord)] -pub struct Packet<'a> { - packet_type: PacketType, - pixel_count: usize, - data: &'a [u8], - bytes_per_pixel: u8, -} - -impl<'a> Packet<'a> { - /// Parses a packet in a RLE encoded file. - pub fn parse(input: &'a [u8], bytes_per_pixel: u8) -> IResult<&'a [u8], Self> { - let (input, type_and_count) = le_u8(input)?; - - // The pixel count is encoded in the lower 7 bits and the actual number of pixels - // is one more than the value stored in the packet. - let pixel_count = usize::from(type_and_count & 0x7F) + 1; - - // The packet type is encoded in the upper bit: 0 -> Raw, 1 -> Rle - let packet_type; - let (input, data) = if type_and_count & 0x80 != 0 { - packet_type = PacketType::Rle; - - // RLE packets always contain a single pixel - take(bytes_per_pixel)(input)? - } else { - packet_type = PacketType::Raw; - - // Raw packets contain `pixel_count` pixels - take(pixel_count * usize::from(bytes_per_pixel))(input)? - }; - - Ok(( - input, - Self { - packet_type, - pixel_count, - data, - bytes_per_pixel, - }, - )) - } - - pub fn from_uncompressed( - image_data: &'a [u8], - pixel_count: usize, - bytes_per_pixel: u8, - ) -> Self { - Self { - packet_type: PacketType::Raw, - pixel_count, - data: image_data, - bytes_per_pixel, - } - } -} - -impl Iterator for Packet<'_> { - type Item = u32; - - fn next(&mut self) -> Option { - let bytes_per_pixel = usize::from(self.bytes_per_pixel); - - if self.pixel_count == 0 || self.data.len() < bytes_per_pixel { - return None; - } - - self.pixel_count -= 1; - - let color = match self.bytes_per_pixel { - 1 => u32::from(self.data[0]), - 2 => u32::from_le_bytes([self.data[0], self.data[1], 0, 0]), - 3 => u32::from_le_bytes([self.data[0], self.data[1], self.data[2], 0]), - 4 => u32::from_le_bytes([self.data[0], self.data[1], self.data[2], self.data[3]]), - _ => 0, - }; - - if self.packet_type == PacketType::Raw { - self.data = &self.data[bytes_per_pixel..]; - } - - Some(color) - } -} diff --git a/src/parse_error.rs b/src/parse_error.rs index ff8e097..100a21c 100644 --- a/src/parse_error.rs +++ b/src/parse_error.rs @@ -1,4 +1,4 @@ -use crate::header::{Bpp, ImageType}; +use crate::header::{Bpp, DataType}; /// Possible parse errors #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] @@ -27,6 +27,6 @@ pub enum ParseError { /// [`Tga::from_slice`]: struct.Tga.html#method.from_slice MismatchedBpp(u8), - /// The image type and bits per pixel combination isn't supported by `DynamicTga`. - UnsupportedDynamicTgaType(ImageType, Bpp), + /// Unsupported combination of image type and bits per pixel. + UnsupportedTgaType(DataType, Bpp), } diff --git a/src/pixels.rs b/src/pixels.rs index 2ecad6a..15c1140 100644 --- a/src/pixels.rs +++ b/src/pixels.rs @@ -1,37 +1,58 @@ -use core::marker::PhantomData; -use embedded_graphics_core::prelude::*; +use embedded_graphics::{ + pixelcolor::{ + raw::{RawU16, RawU24, RawU8}, + Gray8, Rgb555, Rgb888, + }, + prelude::*, +}; -use crate::RawPixels; +use crate::{ColorType, RawPixel, RawPixels, Tga}; /// Iterator over individual TGA pixels. /// /// See the [`pixels`] method for additional information. /// /// [`pixels`]: struct.Tga.html#method.pixels -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] -pub struct Pixels<'a, 'b, C> { - raw: RawPixels<'a, 'b>, - color_type: PhantomData, +#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] +pub struct Pixels<'a, C> { + tga: &'a Tga<'a, C>, + raw_pixels: RawPixels<'a>, } -impl<'a, 'b, C> Pixels<'a, 'b, C> { - pub(crate) fn new(raw: RawPixels<'a, 'b>) -> Self { +impl<'a, C> Pixels<'a, C> +where + C: PixelColor + From + From + From, +{ + pub(crate) fn new(tga: &'a Tga<'a, C>) -> Self { Self { - raw, - color_type: PhantomData, + tga, + raw_pixels: RawPixels::new(&tga.raw), } } } -impl Iterator for Pixels<'_, '_, C> +impl Iterator for Pixels<'_, C> where - C: PixelColor + From<::Raw>, + C: PixelColor + From + From + From, { type Item = Pixel; fn next(&mut self) -> Option { - self.raw - .next() - .map(|p| Pixel(p.position, C::Raw::from_u32(p.color).into())) + let RawPixel { + position, + mut color, + } = self.raw_pixels.next()?; + + if let Some(color_map) = self.tga.raw.color_map() { + color = color_map.get_raw(color as usize).unwrap() + } + + let color = match self.tga.image_color_type { + ColorType::Gray8 => Gray8::from(RawU8::from_u32(color)).into(), + ColorType::Rgb555 => Rgb555::from(RawU16::from_u32(color)).into(), + ColorType::Rgb888 => Rgb888::from(RawU24::from_u32(color)).into(), + }; + + Some(Pixel(position, color)) } } diff --git a/src/raw_iter.rs b/src/raw_iter.rs new file mode 100644 index 0000000..aeb48d7 --- /dev/null +++ b/src/raw_iter.rs @@ -0,0 +1,275 @@ +use core::{convert::TryInto, marker::PhantomData}; + +use embedded_graphics::{ + pixelcolor::raw::{RawU16, RawU24, RawU32, RawU8}, + prelude::*, +}; + +use crate::{raw_tga::RawTga, Bpp, Compression}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Uncompressed {} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Rle {} + +#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] +pub struct RawColors<'a, R, F> { + remaining_data: &'a [u8], + + rle_pixel: u32, + rle_repeat: u8, + rle_take_raw: u8, + + raw_data_type: PhantomData, + format: PhantomData, +} + +impl<'a, R: RawData, F> RawColors<'a, R, F> { + pub fn new(raw_tga: &'a RawTga<'a>) -> Self { + debug_assert_eq!( + usize::from(raw_tga.image_data_bpp().bits()), + R::BITS_PER_PIXEL + ); + + let image_data = raw_tga.image_data(); + + Self { + remaining_data: image_data, + rle_pixel: 0, + rle_repeat: 0, + rle_take_raw: 0, + raw_data_type: PhantomData, + format: PhantomData, + } + } +} + +trait NextColor { + fn next_color(&mut self) -> Option; +} + +impl<'a, F> NextColor for RawColors<'a, RawU8, F> { + fn next_color(&mut self) -> Option { + self.remaining_data.split_first().map(|(r, rest)| { + self.remaining_data = rest; + RawU8::new(*r) + }) + } +} + +impl<'a, F> NextColor for RawColors<'a, RawU16, F> { + fn next_color(&mut self) -> Option { + if self.remaining_data.len() < 2 { + return None; + } + + let (bytes, rest) = self.remaining_data.split_at(2); + self.remaining_data = rest; + + Some(RawU16::new(u16::from_le_bytes(bytes.try_into().unwrap()))) + } +} + +impl<'a, F> NextColor for RawColors<'a, RawU24, F> { + fn next_color(&mut self) -> Option { + if self.remaining_data.len() < 3 { + return None; + } + + let (bytes, rest) = self.remaining_data.split_at(3); + self.remaining_data = rest; + + let mut bytes_padded = [0u8; 4]; + bytes_padded[0..3].copy_from_slice(bytes); + + Some(RawU24::new(u32::from_le_bytes(bytes_padded))) + } +} + +impl<'a, F> NextColor for RawColors<'a, RawU32, F> { + fn next_color(&mut self) -> Option { + if self.remaining_data.len() < 4 { + return None; + } + + let (bytes, rest) = self.remaining_data.split_at(4); + self.remaining_data = rest; + + Some(RawU32::new(u32::from_le_bytes(bytes.try_into().unwrap()))) + } +} + +impl<'a, R> Iterator for RawColors<'a, R, Uncompressed> +where + Self: NextColor, + R: RawData, +{ + type Item = R; + + fn next(&mut self) -> Option { + self.next_color().or_else(|| Some(R::from_u32(0))) + } +} + +impl<'a, R> Iterator for RawColors<'a, R, Rle> +where + Self: NextColor, + R: RawData, + R::Storage: Into, +{ + type Item = R; + + fn next(&mut self) -> Option { + loop { + if self.rle_repeat > 0 { + self.rle_repeat -= 1; + break Some(R::from_u32(self.rle_pixel)); + } else if self.rle_take_raw > 0 { + self.rle_take_raw -= 1; + break self.next_color(); + } else { + let (type_and_count, rest) = self.remaining_data.split_first()?; + self.remaining_data = rest; + + // The pixel count is encoded in the lower 7 bits and the actual number of pixels + // is one more than the value stored in the packet. + let pixel_count = (*type_and_count & 0x7F) + 1; + + // The packet type is encoded in the upper bit: 0 -> Raw, 1 -> Rle + if *type_and_count & 0x80 != 0 { + let pixel = self.next_color()?; + + self.rle_repeat = pixel_count; + self.rle_pixel = pixel.into_inner().into(); + } else { + self.rle_take_raw = pixel_count; + } + } + } + } +} + +#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] +enum DynamicRawColors<'a> { + Bpp8Uncompressed(RawColors<'a, RawU8, Uncompressed>), + Bpp8Rle(RawColors<'a, RawU8, Rle>), + Bpp16Uncompressed(RawColors<'a, RawU16, Uncompressed>), + Bpp16Rle(RawColors<'a, RawU16, Rle>), + Bpp24Uncompressed(RawColors<'a, RawU24, Uncompressed>), + Bpp24Rle(RawColors<'a, RawU24, Rle>), + Bpp32Uncompressed(RawColors<'a, RawU32, Uncompressed>), + Bpp32Rle(RawColors<'a, RawU32, Rle>), +} + +/// Iterator over individual TGA pixels. +/// +/// See the [`pixels`] method for additional information. +/// +/// [`pixels`]: struct.RawTga.html#method.pixels +#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] +pub struct RawPixels<'a> { + raw_tga: &'a RawTga<'a>, + colors: DynamicRawColors<'a>, + position: Point, +} + +impl<'a> RawPixels<'a> { + pub(crate) fn new(raw_tga: &'a RawTga<'a>) -> Self { + let colors = match (raw_tga.image_data_bpp(), raw_tga.compression()) { + (Bpp::Bits8, Compression::Uncompressed) => { + DynamicRawColors::Bpp8Uncompressed(RawColors::new(raw_tga)) + } + (Bpp::Bits8, Compression::Rle) => DynamicRawColors::Bpp8Rle(RawColors::new(raw_tga)), + (Bpp::Bits16, Compression::Uncompressed) => { + DynamicRawColors::Bpp16Uncompressed(RawColors::new(raw_tga)) + } + (Bpp::Bits16, Compression::Rle) => DynamicRawColors::Bpp16Rle(RawColors::new(raw_tga)), + (Bpp::Bits24, Compression::Uncompressed) => { + DynamicRawColors::Bpp24Uncompressed(RawColors::new(raw_tga)) + } + (Bpp::Bits24, Compression::Rle) => DynamicRawColors::Bpp24Rle(RawColors::new(raw_tga)), + (Bpp::Bits32, Compression::Uncompressed) => { + DynamicRawColors::Bpp32Uncompressed(RawColors::new(raw_tga)) + } + (Bpp::Bits32, Compression::Rle) => DynamicRawColors::Bpp32Rle(RawColors::new(raw_tga)), + }; + + let start_y = if raw_tga.image_origin().is_bottom() { + raw_tga.size().height.saturating_sub(1) + } else { + 0 + }; + + Self { + raw_tga, + colors, + position: Point::new(0, start_y as i32), + } + } + + /// Returns the next pixel position. + fn next_position(&mut self) -> Option { + if self.position.y < 0 || self.position.y >= self.raw_tga.size().height as i32 { + return None; + } + + let position = self.position; + + self.position.x += 1; + + if self.position.x >= self.raw_tga.size().width as i32 { + self.position.x = 0; + + if self.raw_tga.image_origin().is_bottom() { + self.position.y -= 1; + } else { + self.position.y += 1; + } + } + + Some(position) + } +} + +impl Iterator for RawPixels<'_> { + type Item = RawPixel; + + fn next(&mut self) -> Option { + let position = self.next_position()?; + + let color = match &mut self.colors { + DynamicRawColors::Bpp8Uncompressed(colors) => u32::from(colors.next()?.into_inner()), + DynamicRawColors::Bpp8Rle(colors) => u32::from(colors.next()?.into_inner()), + DynamicRawColors::Bpp16Uncompressed(colors) => u32::from(colors.next()?.into_inner()), + DynamicRawColors::Bpp16Rle(colors) => u32::from(colors.next()?.into_inner()), + DynamicRawColors::Bpp24Uncompressed(colors) => colors.next()?.into_inner(), + DynamicRawColors::Bpp24Rle(colors) => colors.next()?.into_inner(), + DynamicRawColors::Bpp32Uncompressed(colors) => colors.next()?.into_inner(), + DynamicRawColors::Bpp32Rle(colors) => colors.next()?.into_inner(), + }; + + Some(RawPixel::new(position, color)) + } +} + +/// Pixel with raw pixel color. +/// +/// This struct is returned by the [`RawPixels`] iterator. +/// +/// [`RawPixels`]: struct.RawPixels.html +#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Default)] +pub struct RawPixel { + /// The position relative to the top left corner of the image. + pub position: Point, + + /// The raw pixel color. + pub color: u32, +} + +impl RawPixel { + /// Creates a new raw pixel. + pub const fn new(position: Point, color: u32) -> Self { + Self { position, color } + } +} diff --git a/src/raw_pixels.rs b/src/raw_pixels.rs deleted file mode 100644 index 9bacd93..0000000 --- a/src/raw_pixels.rs +++ /dev/null @@ -1,127 +0,0 @@ -use crate::{packet::Packet, raw_tga::RawTga}; -use embedded_graphics_core::prelude::*; - -/// Iterator over individual TGA pixels. -/// -/// See the [`pixels`] method for additional information. -/// -/// [`pixels`]: struct.RawTga.html#method.pixels -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] -pub struct RawPixels<'a, 'b> { - /// Reference to original TGA image - raw_tga: &'a RawTga<'b>, - - position: Point, - - packet: Packet<'b>, - - remaining_data: &'b [u8], -} - -impl<'a, 'b> RawPixels<'a, 'b> { - pub(crate) fn new(raw_tga: &'a RawTga<'b>) -> Self { - let size = raw_tga.size(); - let remaining_pixels = size.width as usize * size.height as usize; - - let image_data = raw_tga.image_data(); - - let (first_packet_pixels, data) = if raw_tga.image_type().is_rle() { - (0, image_data) - } else { - (remaining_pixels, &image_data[0..0]) - }; - - let packet = Packet::from_uncompressed( - raw_tga.image_data(), - first_packet_pixels, - raw_tga.image_data_bpp().bytes(), - ); - - let start_y = if raw_tga.image_origin().is_bottom() { - raw_tga.size().height.saturating_sub(1) - } else { - 0 - }; - - Self { - raw_tga: raw_tga, - packet, - remaining_data: data, - position: Point::new(0, start_y as i32), - } - } - - /// Returns the next pixel position. - fn next_position(&mut self) -> Option { - if self.position.y < 0 || self.position.y >= self.raw_tga.size().height as i32 { - return None; - } - - let position = self.position; - - self.position.x += 1; - - if self.position.x >= self.raw_tga.size().width as i32 { - self.position.x = 0; - - if self.raw_tga.image_origin().is_bottom() { - self.position.y -= 1; - } else { - self.position.y += 1; - } - } - - Some(position) - } -} - -impl<'a, 'b> Iterator for RawPixels<'a, 'b> { - type Item = RawPixel; - - fn next(&mut self) -> Option { - let position = self.next_position()?; - - let color = if let Some(color) = self.packet.next() { - color - } else { - match Packet::parse(self.remaining_data, self.raw_tga.image_data_bpp().bytes()) { - Ok((data, packet)) => { - self.remaining_data = data; - self.packet = packet; - - self.packet.next().unwrap_or(0) - } - Err(_) => 0, - } - }; - - let color = if let Some(color_map) = self.raw_tga.color_map() { - color_map.get_raw(color as usize).unwrap_or(0) - } else { - color - }; - - Some(RawPixel::new(position, color)) - } -} - -/// Pixel with raw pixel color. -/// -/// This struct is returned by the [`RawPixels`] iterator. -/// -/// [`RawPixels`]: struct.RawPixels.html -#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Default)] -pub struct RawPixel { - /// The position relative to the top left corner of the image. - pub position: Point, - - /// The raw pixel color. - pub color: u32, -} - -impl RawPixel { - /// Creates a new raw pixel. - pub fn new(position: Point, color: u32) -> Self { - Self { position, color } - } -} diff --git a/src/raw_tga.rs b/src/raw_tga.rs index e64b169..d9b71e5 100644 --- a/src/raw_tga.rs +++ b/src/raw_tga.rs @@ -1,24 +1,24 @@ -use embedded_graphics_core::{prelude::*, primitives::Rectangle}; +use embedded_graphics::prelude::*; use nom::{bytes::complete::take, IResult}; use crate::{ color_map::ColorMap, footer::TgaFooter, - header::{Bpp, ImageOrigin, ImageType, TgaHeader}, + header::{Bpp, ImageOrigin, TgaHeader}, parse_error::ParseError, - pixels::Pixels, - raw_pixels::RawPixels, + raw_iter::RawPixels, + Compression, DataType, }; /// Raw TGA image. /// /// `RawTga` can be used to access lower level information about a TGA file and to access the /// raw pixel data. It can be created directly by using the [`from_slice`] constructor or accessed -/// by calling [`raw`] method of a [`Tga`] object. +/// by calling [`as_raw`] method of a [`Tga`] object. /// /// [`from_slice`]: #method.from_slice /// [`Tga`]: struct.Tga.html -/// [`raw`]: struct.Tga.html#method.raw +/// [`as_raw`]: struct.Tga.html#method.as_raw #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] pub struct RawTga<'a> { /// Image data @@ -33,8 +33,11 @@ pub struct RawTga<'a> { /// Image size size: Size, - /// Image type - image_type: ImageType, + /// Data type + data_type: DataType, + + /// Compression + compression: Compression, /// Bits per pixel bpp: Bpp, @@ -65,7 +68,8 @@ impl<'a> RawTga<'a> { size, bpp: header.pixel_depth, image_origin: header.image_origin, - image_type: header.image_type, + data_type: header.data_type, + compression: header.compression, }) } @@ -101,9 +105,14 @@ impl<'a> RawTga<'a> { self.image_origin } - /// Returns the image type. - pub fn image_type(&self) -> ImageType { - self.image_type + /// Returns the data type. + pub fn data_type(&self) -> DataType { + self.data_type + } + + /// Returns the compression type. + pub fn compression(&self) -> Compression { + self.compression } /// Returns the raw image data contained in this image. @@ -128,7 +137,7 @@ impl<'a> RawTga<'a> { } /// Returns an iterator over the raw pixels in this image. - pub fn pixels<'b>(&'b self) -> RawPixels<'b, 'a> { + pub fn pixels(&self) -> RawPixels<'_> { RawPixels::new(self) } @@ -152,7 +161,7 @@ impl<'a> RawTga<'a> { /// # Performance /// /// To save memory the footer is parsed every time this method is called. - pub fn developer_directory(&self) -> Option<&[u8]> { + pub fn developer_directory(&self) -> Option<&'a [u8]> { TgaFooter::parse(self.data).and_then(|footer| footer.developer_directory(self.data)) } @@ -161,7 +170,7 @@ impl<'a> RawTga<'a> { /// # Performance /// /// To save memory the footer is parsed every time this method is called. - pub fn extension_area(&self) -> Option<&[u8]> { + pub fn extension_area(&self) -> Option<&'a [u8]> { TgaFooter::parse(self.data).and_then(|footer| footer.extension_area(self.data)) } @@ -172,7 +181,7 @@ impl<'a> RawTga<'a> { /// # Performance /// /// To save memory the header is parsed every time this method is called. - pub fn image_id(&self) -> Option<&[u8]> { + pub fn image_id(&self) -> Option<&'a [u8]> { let (input, header) = TgaHeader::parse(self.data).ok()?; parse_image_id(input, &header) @@ -180,24 +189,6 @@ impl<'a> RawTga<'a> { .map(|(_input, id)| id) .filter(|id| !id.is_empty()) } - - pub(crate) fn draw(&self, target: &mut D) -> Result<(), D::Error> - where - D: DrawTarget, - D::Color: From<::Raw>, - { - let pixels = Pixels::::new(self.pixels()); - - // TGA files with the origin in the top left corner can be drawn using `fill_contiguous`. - // All other origins are drawn by falling back to `draw_iter`. - if self.image_origin() == ImageOrigin::TopLeft { - let bounding_box = Rectangle::new(Point::zero(), self.size); - - target.fill_contiguous(&bounding_box, pixels.map(|Pixel(_, color)| color)) - } else { - target.draw_iter(pixels) - } - } } fn parse_image_id<'a>(input: &'a [u8], header: &TgaHeader) -> IResult<&'a [u8], &'a [u8]> { diff --git a/tests/cbw8.rs b/tests/cbw8.rs index df6953a..fe0cac1 100644 --- a/tests/cbw8.rs +++ b/tests/cbw8.rs @@ -1,4 +1,4 @@ -use tinytga::{Bpp, ImageOrigin, ImageType, RawTga, TgaHeader}; +use tinytga::{Bpp, Compression, DataType, ImageOrigin, RawTga, TgaHeader}; #[test] fn cbw8() { @@ -14,7 +14,8 @@ fn cbw8() { TgaHeader { id_len: 26, has_color_map: false, - image_type: ImageType::RleMonochrome, + data_type: DataType::BlackAndWhite, + compression: Compression::Rle, color_map_start: 0, color_map_len: 0, color_map_depth: None, diff --git a/tests/chequerboard-uncompressed-topleft.rs b/tests/chequerboard-uncompressed-topleft.rs index fe4e292..24e2df0 100644 --- a/tests/chequerboard-uncompressed-topleft.rs +++ b/tests/chequerboard-uncompressed-topleft.rs @@ -1,4 +1,4 @@ -use tinytga::{Bpp, ImageOrigin, ImageType, RawTga, TgaHeader}; +use tinytga::{Bpp, Compression, DataType, ImageOrigin, RawTga, TgaHeader}; #[test] fn chequerboard_uncompressed_topleft() { @@ -18,7 +18,8 @@ fn chequerboard_uncompressed_topleft() { TgaHeader { id_len: 0, has_color_map: false, - image_type: ImageType::Monochrome, + data_type: DataType::BlackAndWhite, + compression: Compression::Uncompressed, color_map_start: 0, color_map_len: 0, color_map_depth: None, diff --git a/tests/chessboard_4px_raw.rs b/tests/chessboard_4px_raw.rs index 36b0a6c..bdcf88a 100644 --- a/tests/chessboard_4px_raw.rs +++ b/tests/chessboard_4px_raw.rs @@ -1,4 +1,4 @@ -use tinytga::{Bpp, ImageOrigin, ImageType, RawTga, TgaHeader}; +use tinytga::{Bpp, Compression, DataType, ImageOrigin, RawTga, TgaHeader}; #[test] fn chessboard_4px_raw() { @@ -15,7 +15,8 @@ fn chessboard_4px_raw() { TgaHeader { id_len: 0, has_color_map: false, - image_type: ImageType::Truecolor, + data_type: DataType::TrueColor, + compression: Compression::Uncompressed, color_map_start: 0, color_map_len: 0, color_map_depth: None, diff --git a/tests/chessboard_4px_rle.rs b/tests/chessboard_4px_rle.rs index 8b568e3..eaa62e0 100644 --- a/tests/chessboard_4px_rle.rs +++ b/tests/chessboard_4px_rle.rs @@ -1,4 +1,4 @@ -use tinytga::{Bpp, ImageOrigin, ImageType, RawTga, TgaHeader}; +use tinytga::{Bpp, Compression, DataType, ImageOrigin, RawTga, TgaHeader}; #[test] fn chessboard_4px_rle() { @@ -15,7 +15,8 @@ fn chessboard_4px_rle() { TgaHeader { id_len: 0, has_color_map: false, - image_type: ImageType::RleTruecolor, + data_type: DataType::TrueColor, + compression: Compression::Rle, color_map_start: 0, color_map_len: 0, color_map_depth: None, diff --git a/tests/chessboard_rle.rs b/tests/chessboard_rle.rs index e554ea7..14e087a 100644 --- a/tests/chessboard_rle.rs +++ b/tests/chessboard_rle.rs @@ -1,4 +1,4 @@ -use tinytga::{Bpp, ImageOrigin, ImageType, RawTga, TgaHeader}; +use tinytga::{Bpp, Compression, DataType, ImageOrigin, RawTga, TgaHeader}; #[test] fn chessboard_rle() { @@ -15,7 +15,8 @@ fn chessboard_rle() { TgaHeader { id_len: 0, has_color_map: false, - image_type: ImageType::RleTruecolor, + data_type: DataType::TrueColor, + compression: Compression::Rle, color_map_start: 0, color_map_len: 0, color_map_depth: None, diff --git a/tests/chessboard_uncompressed.rs b/tests/chessboard_uncompressed.rs index 8eb247e..fa0454f 100644 --- a/tests/chessboard_uncompressed.rs +++ b/tests/chessboard_uncompressed.rs @@ -1,4 +1,4 @@ -use tinytga::{Bpp, ImageOrigin, ImageType, RawTga, TgaHeader}; +use tinytga::{Bpp, Compression, DataType, ImageOrigin, RawTga, TgaHeader}; #[test] fn chessboard_uncompressed() { @@ -15,7 +15,8 @@ fn chessboard_uncompressed() { TgaHeader { id_len: 0, has_color_map: false, - image_type: ImageType::Truecolor, + data_type: DataType::TrueColor, + compression: Compression::Uncompressed, color_map_start: 0, color_map_len: 0, color_map_depth: None, diff --git a/tests/coordinates.rs b/tests/coordinates.rs index d3f7b26..23487a0 100644 --- a/tests/coordinates.rs +++ b/tests/coordinates.rs @@ -1,5 +1,5 @@ use embedded_graphics::prelude::*; -use tinytga::{Bpp, ImageOrigin, ImageType, RawTga, TgaHeader}; +use tinytga::{Bpp, Compression, DataType, ImageOrigin, RawTga, TgaHeader}; #[test] fn coordinates() { @@ -16,7 +16,8 @@ fn coordinates() { TgaHeader { id_len: 0, has_color_map: false, - image_type: ImageType::Truecolor, + data_type: DataType::TrueColor, + compression: Compression::Uncompressed, color_map_start: 0, color_map_len: 0, color_map_depth: None, diff --git a/tests/embedded_graphics.rs b/tests/embedded_graphics.rs index abdb6ce..5463912 100644 --- a/tests/embedded_graphics.rs +++ b/tests/embedded_graphics.rs @@ -5,7 +5,7 @@ use embedded_graphics::{ prelude::*, }; use paste::paste; -use tinytga::{DynamicTga, Tga}; +use tinytga::Tga; const CHESSBOARD_PATTERN: &[&str] = &[ "WKWK", // @@ -32,52 +32,51 @@ const COLOR_PATTERN: &[&str] = &[ #[test] fn chessboard_compressed() { - let tga: Tga = Tga::from_slice(include_bytes!("./chessboard_4px_rle.tga")).unwrap(); + let tga = Tga::from_slice(include_bytes!("./chessboard_4px_rle.tga")).unwrap(); let image = Image::new(&tga, Point::zero()); - let mut display = MockDisplay::new(); + let mut display = MockDisplay::::new(); image.draw(&mut display).unwrap(); - assert_eq!(display, MockDisplay::from_pattern(CHESSBOARD_PATTERN)); + display.assert_pattern(CHESSBOARD_PATTERN); } #[test] fn chessboard_uncompressed() { - let tga: Tga = Tga::from_slice(include_bytes!("./chessboard_raw.tga")).unwrap(); + let tga = Tga::from_slice(include_bytes!("./chessboard_raw.tga")).unwrap(); let image = Image::new(&tga, Point::zero()); - let mut display = MockDisplay::new(); + let mut display = MockDisplay::::new(); image.draw(&mut display).unwrap(); - assert_eq!(display, MockDisplay::from_pattern(CHESSBOARD_PATTERN)); + display.assert_pattern(CHESSBOARD_PATTERN); } fn test_tga(data: &[u8], pattern: &[&str]) where - C: PixelColor + From<::Raw> + ColorMapping, + C: PixelColor + From + From + From + ColorMapping, { - let tga: Tga = Tga::from_slice(data).unwrap(); + let tga = Tga::from_slice(data).unwrap(); let image = Image::new(&tga, Point::zero()); - let mut display = MockDisplay::new(); + let mut display = MockDisplay::::new(); image.draw(&mut display).unwrap(); - assert_eq!(display, MockDisplay::from_pattern(pattern)); + display.assert_pattern(pattern); } -fn test_dynamic_tga(data: &[u8], pattern: &[&str]) +fn test_pixels_iter(data: &[u8], pattern: &[&str]) where - C: PixelColor + From<::Raw> + Into + ColorMapping, + C: PixelColor + From + From + From + ColorMapping, { - let tga = DynamicTga::from_slice(data).unwrap(); - let image = Image::new(&tga, Point::zero()); + let tga = Tga::from_slice(data).unwrap(); - let mut display = MockDisplay::new(); - image.draw(&mut display).unwrap(); + let mut display = MockDisplay::::new(); + tga.pixels().draw(&mut display).unwrap(); let expected: MockDisplay = MockDisplay::::from_pattern(pattern).map(|c| c.into()); - assert_eq!(display, expected); + display.assert_eq(&expected); } macro_rules! test_tga { @@ -94,13 +93,13 @@ macro_rules! test_tga { } #[test] - fn [<$image_type _bl_dynamic>]() { - test_dynamic_tga::<$color_type>(include_bytes!(concat!(stringify!($image_type), "_bl.tga")), $pattern); + fn [<$image_type _bl_pixels_iter>]() { + test_pixels_iter::<$color_type>(include_bytes!(concat!(stringify!($image_type), "_bl.tga")), $pattern); } #[test] - fn [<$image_type _tl_dynamic>]() { - test_dynamic_tga::<$color_type>(include_bytes!(concat!(stringify!($image_type), "_tl.tga")), $pattern); + fn [<$image_type _tl_pixels_iter>]() { + test_pixels_iter::<$color_type>(include_bytes!(concat!(stringify!($image_type), "_tl.tga")), $pattern); } } }; diff --git a/tests/errors.rs b/tests/errors.rs index 071c5b5..cab1192 100644 --- a/tests/errors.rs +++ b/tests/errors.rs @@ -1,10 +1,6 @@ -use embedded_graphics::{ - pixelcolor::{Gray8, Rgb888}, - prelude::*, - primitives::Rectangle, -}; +use embedded_graphics::{prelude::*, primitives::Rectangle}; use std::iter::repeat; -use tinytga::{ParseError, RawPixel, RawTga, Tga}; +use tinytga::{ParseError, RawPixel, RawTga}; #[test] fn color_map() { @@ -51,17 +47,17 @@ fn image_data_truncated() { assert_eq!(pixels, expected); } -#[test] -fn mismatched_bpp() { - // type2_tl_24bpp.tga is a 24 BPP image - assert_eq!( - Tga::::from_slice(include_bytes!("../tests/type2_24bpp_tl.tga")), - Err(ParseError::MismatchedBpp(24)) - ); - - // type3_tl.tga is a 8 BPP image - assert_eq!( - Tga::::from_slice(include_bytes!("../tests/type3_tl.tga")), - Err(ParseError::MismatchedBpp(8)) - ); -} +// #[test] +// fn mismatched_bpp() { +// // type2_tl_24bpp.tga is a 24 BPP image +// assert_eq!( +// Tga::::from_slice(include_bytes!("../tests/type2_24bpp_tl.tga")), +// Err(ParseError::MismatchedBpp(24)) +// ); + +// // type3_tl.tga is a 8 BPP image +// assert_eq!( +// Tga::::from_slice(include_bytes!("../tests/type3_tl.tga")), +// Err(ParseError::MismatchedBpp(8)) +// ); +// } diff --git a/tests/logo.rs b/tests/logo.rs new file mode 100644 index 0000000..a570a1d --- /dev/null +++ b/tests/logo.rs @@ -0,0 +1,456 @@ +use embedded_graphics::{ + image::{Image, ImageRawBE, ImageRawLE}, + pixelcolor::{Gray8, Rgb555, Rgb888}, + prelude::*, +}; +use tinytga::{Bpp, Compression, DataType, ImageOrigin, Tga}; + +const WIDTH: usize = 240; +const HEIGHT: usize = 320; + +// TODO: use e-g framebuffer when it's added +#[derive(Debug, PartialEq)] +struct Framebuffer { + pixels: [[C; 240]; 320], +} + +impl + std::fmt::Debug> Framebuffer { + pub fn new() -> Self { + let color = C::from(Rgb888::BLACK); + + Self { + pixels: [[color; WIDTH]; HEIGHT], + } + } + + pub fn from_image(image: impl ImageDrawable) -> Self { + let mut framebuffer = Framebuffer::::new(); + Image::new(&image, Point::zero()) + .draw(&mut framebuffer) + .unwrap(); + framebuffer + } + + pub fn pixels(&self) -> impl Iterator + '_ { + self.pixels.iter().flatten().copied() + } + + pub fn assert_eq(&self, expected: &Self) { + let zipped = || self.pixels().zip(expected.pixels()); + + let errors = zipped().filter(|(a, b)| a != b).count(); + let first_error = zipped() + .enumerate() + .find(|(_, (a, b))| a != b) + .map(|(i, (a, b))| (Point::new((i % WIDTH) as i32, (i / WIDTH) as i32), a, b)); + + if self != expected { + let first_error = first_error.unwrap(); + panic!( + "framebuffer differs from expected\n{} errors\nfirst error at ({}): {:?} (expected {:?})", + errors, + first_error.0, + first_error.1, + first_error.2, + ); + } + } +} + +impl DrawTarget for Framebuffer { + type Color = C; + type Error = std::convert::Infallible; + + fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> + where + I: IntoIterator>, + { + for Pixel(p, c) in pixels { + self.pixels[p.y as usize][p.x as usize] = c; + } + + Ok(()) + } +} + +impl OriginDimensions for Framebuffer { + fn size(&self) -> embedded_graphics::prelude::Size { + Size::new(240, 320) + } +} + +fn expected_rgb555() -> Framebuffer { + Framebuffer::from_image(ImageRawLE::::new( + include_bytes!("logo_rgb555.raw"), + WIDTH as u32, + )) +} + +fn expected_rgb888() -> Framebuffer { + Framebuffer::from_image(ImageRawBE::::new( + include_bytes!("logo_rgb888.raw"), + WIDTH as u32, + )) +} + +fn expected_gray8() -> Framebuffer { + Framebuffer::from_image(ImageRawBE::::new( + include_bytes!("logo_gray8.raw"), + WIDTH as u32, + )) +} + +#[track_caller] +fn assert_format( + tga: &Tga, + data_type: DataType, + compression: Compression, + pixel_depth: Bpp, + image_origin: ImageOrigin, + color_map_depth: Option, +) where + C: PixelColor + From + From + From, +{ + assert_eq!(tga.as_raw().header().data_type, data_type); + assert_eq!(tga.as_raw().header().compression, compression); + assert_eq!(tga.as_raw().header().pixel_depth, pixel_depth); + assert_eq!(tga.as_raw().header().image_origin, image_origin); + + assert_eq!(tga.as_raw().header().color_map_depth, color_map_depth); + assert_eq!(tga.as_raw().header().color_map_start, 0); + if color_map_depth.is_some() { + assert!(tga.as_raw().header().color_map_len > 0); + } else { + assert_eq!(tga.as_raw().header().color_map_len, 0); + } +} + +#[test] +fn logo_type1_16bpp_tl() { + let tga = Tga::::from_slice(include_bytes!("logo_type1_16bpp_tl.tga")).unwrap(); + assert_format( + &tga, + DataType::ColorMapped, + Compression::Uncompressed, + Bpp::Bits8, + ImageOrigin::TopLeft, + Some(Bpp::Bits16), + ); + + Framebuffer::from_image(tga).assert_eq(&expected_rgb555()); +} + +#[test] +fn logo_type1_16bpp_bl() { + let tga = Tga::::from_slice(include_bytes!("logo_type1_16bpp_bl.tga")).unwrap(); + assert_format( + &tga, + DataType::ColorMapped, + Compression::Uncompressed, + Bpp::Bits8, + ImageOrigin::BottomLeft, + Some(Bpp::Bits16), + ); + + Framebuffer::from_image(tga).assert_eq(&expected_rgb555()); +} + +#[test] +fn logo_type1_24bpp_tl() { + let tga = Tga::::from_slice(include_bytes!("logo_type1_24bpp_tl.tga")).unwrap(); + assert_format( + &tga, + DataType::ColorMapped, + Compression::Uncompressed, + Bpp::Bits8, + ImageOrigin::TopLeft, + Some(Bpp::Bits24), + ); + + Framebuffer::from_image(tga).assert_eq(&expected_rgb888()); +} + +#[test] +fn logo_type1_24bpp_bl() { + let tga = Tga::::from_slice(include_bytes!("logo_type1_24bpp_bl.tga")).unwrap(); + assert_format( + &tga, + DataType::ColorMapped, + Compression::Uncompressed, + Bpp::Bits8, + ImageOrigin::BottomLeft, + Some(Bpp::Bits24), + ); + + Framebuffer::from_image(tga).assert_eq(&expected_rgb888()); +} + +#[test] +fn logo_type2_16bpp_tl() { + let tga = Tga::::from_slice(include_bytes!("logo_type2_16bpp_tl.tga")).unwrap(); + assert_format( + &tga, + DataType::TrueColor, + Compression::Uncompressed, + Bpp::Bits16, + ImageOrigin::TopLeft, + None, + ); + + Framebuffer::from_image(tga).assert_eq(&expected_rgb555()); +} + +#[test] +fn logo_type2_16bpp_bl() { + let tga = Tga::::from_slice(include_bytes!("logo_type2_16bpp_bl.tga")).unwrap(); + assert_format( + &tga, + DataType::TrueColor, + Compression::Uncompressed, + Bpp::Bits16, + ImageOrigin::BottomLeft, + None, + ); + + Framebuffer::from_image(tga).assert_eq(&expected_rgb555()); +} + +#[test] +fn logo_type2_24bpp_tl() { + let tga = Tga::::from_slice(include_bytes!("logo_type2_24bpp_tl.tga")).unwrap(); + assert_format( + &tga, + DataType::TrueColor, + Compression::Uncompressed, + Bpp::Bits24, + ImageOrigin::TopLeft, + None, + ); + + Framebuffer::from_image(tga).assert_eq(&expected_rgb888()); +} + +#[test] +fn logo_type2_24bpp_bl() { + let tga = Tga::::from_slice(include_bytes!("logo_type2_24bpp_bl.tga")).unwrap(); + assert_format( + &tga, + DataType::TrueColor, + Compression::Uncompressed, + Bpp::Bits24, + ImageOrigin::BottomLeft, + None, + ); + + Framebuffer::from_image(tga).assert_eq(&expected_rgb888()); +} + +#[test] +fn logo_type2_24bpp_tr() { + let tga = Tga::::from_slice(include_bytes!("logo_type2_24bpp_tr.tga")).unwrap(); + assert_format( + &tga, + DataType::TrueColor, + Compression::Uncompressed, + Bpp::Bits24, + ImageOrigin::TopRight, + None, + ); + + Framebuffer::from_image(tga).assert_eq(&expected_rgb888()); +} + +#[test] +fn logo_type2_24bpp_br() { + let tga = Tga::::from_slice(include_bytes!("logo_type2_24bpp_br.tga")).unwrap(); + assert_format( + &tga, + DataType::TrueColor, + Compression::Uncompressed, + Bpp::Bits24, + ImageOrigin::BottomRight, + None, + ); + + Framebuffer::from_image(tga).assert_eq(&expected_rgb888()); +} + +#[test] +fn logo_type3_tl() { + let tga = Tga::::from_slice(include_bytes!("logo_type3_tl.tga")).unwrap(); + assert_format( + &tga, + DataType::BlackAndWhite, + Compression::Uncompressed, + Bpp::Bits8, + ImageOrigin::TopLeft, + None, + ); + + Framebuffer::from_image(tga).assert_eq(&expected_gray8()); +} + +#[test] +fn logo_type3_bl() { + let tga = Tga::::from_slice(include_bytes!("logo_type3_bl.tga")).unwrap(); + assert_format( + &tga, + DataType::BlackAndWhite, + Compression::Uncompressed, + Bpp::Bits8, + ImageOrigin::BottomLeft, + None, + ); + + Framebuffer::from_image(tga).assert_eq(&expected_gray8()); +} + +#[test] +fn logo_type9_16bpp_tl() { + let tga = Tga::::from_slice(include_bytes!("logo_type9_16bpp_tl.tga")).unwrap(); + assert_format( + &tga, + DataType::ColorMapped, + Compression::Rle, + Bpp::Bits8, + ImageOrigin::TopLeft, + Some(Bpp::Bits16), + ); + + Framebuffer::from_image(tga).assert_eq(&expected_rgb555()); +} + +#[test] +fn logo_type9_16bpp_bl() { + let tga = Tga::::from_slice(include_bytes!("logo_type9_16bpp_bl.tga")).unwrap(); + assert_format( + &tga, + DataType::ColorMapped, + Compression::Rle, + Bpp::Bits8, + ImageOrigin::BottomLeft, + Some(Bpp::Bits16), + ); + + Framebuffer::from_image(tga).assert_eq(&expected_rgb555()); +} + +#[test] +fn logo_type9_24bpp_tl() { + let tga = Tga::::from_slice(include_bytes!("logo_type9_24bpp_tl.tga")).unwrap(); + assert_format( + &tga, + DataType::ColorMapped, + Compression::Rle, + Bpp::Bits8, + ImageOrigin::TopLeft, + Some(Bpp::Bits24), + ); + + Framebuffer::from_image(tga).assert_eq(&expected_rgb888()); +} + +#[test] +fn logo_type9_24bpp_bl() { + let tga = Tga::::from_slice(include_bytes!("logo_type9_24bpp_bl.tga")).unwrap(); + assert_format( + &tga, + DataType::ColorMapped, + Compression::Rle, + Bpp::Bits8, + ImageOrigin::BottomLeft, + Some(Bpp::Bits24), + ); + + Framebuffer::from_image(tga).assert_eq(&expected_rgb888()); +} + +#[test] +fn logo_type10_16bpp_tl() { + let tga = Tga::::from_slice(include_bytes!("logo_type10_16bpp_tl.tga")).unwrap(); + assert_format( + &tga, + DataType::TrueColor, + Compression::Rle, + Bpp::Bits16, + ImageOrigin::TopLeft, + None, + ); + + Framebuffer::from_image(tga).assert_eq(&expected_rgb555()); +} + +#[test] +fn logo_type10_16bpp_bl() { + let tga = Tga::::from_slice(include_bytes!("logo_type10_16bpp_bl.tga")).unwrap(); + assert_format( + &tga, + DataType::TrueColor, + Compression::Rle, + Bpp::Bits16, + ImageOrigin::BottomLeft, + None, + ); + + Framebuffer::from_image(tga).assert_eq(&expected_rgb555()); +} + +#[test] +fn logo_type10_24bpp_tl() { + let tga = Tga::::from_slice(include_bytes!("logo_type10_24bpp_tl.tga")).unwrap(); + assert_format( + &tga, + DataType::TrueColor, + Compression::Rle, + Bpp::Bits24, + ImageOrigin::TopLeft, + None, + ); + + Framebuffer::from_image(tga).assert_eq(&expected_rgb888()); +} + +#[test] +fn logo_type10_24bpp_bl() { + let tga = Tga::::from_slice(include_bytes!("logo_type10_24bpp_bl.tga")).unwrap(); + assert_format( + &tga, + DataType::TrueColor, + Compression::Rle, + Bpp::Bits24, + ImageOrigin::BottomLeft, + None, + ); + + Framebuffer::from_image(tga).assert_eq(&expected_rgb888()); +} + +#[test] +fn logo_type11_tl() { + let tga = Tga::::from_slice(include_bytes!("logo_type11_tl.tga")).unwrap(); + assert_format( + &tga, + DataType::BlackAndWhite, + Compression::Rle, + Bpp::Bits8, + ImageOrigin::TopLeft, + None, + ); + + Framebuffer::from_image(tga).assert_eq(&expected_gray8()); +} + +#[test] +fn logo_type11_bl() { + let tga = Tga::::from_slice(include_bytes!("logo_type11_bl.tga")).unwrap(); + assert_format( + &tga, + DataType::BlackAndWhite, + Compression::Rle, + Bpp::Bits8, + ImageOrigin::BottomLeft, + None, + ); + + Framebuffer::from_image(tga).assert_eq(&expected_gray8()); +} diff --git a/tests/logo_gray8.raw b/tests/logo_gray8.raw new file mode 100644 index 0000000..db0afd2 --- /dev/null +++ b/tests/logo_gray8.raw @@ -0,0 +1 @@ +F=ha$RJun3,XQ{A:d]$F?ja(TLvo]]3?I,Y/3Qgq |S]A:C:e(},^bl$ISH8<?s#q{&k]g0CN(V14Lw q{$pXb7?I,c.2[gq}S]D:C=g({+`fppf$I3/SJ7NC;At#qg]{%l]{$"qg3C;7N,V0SI4Ny llbv#qS+'v]7??:I0d*XN-]gqgq~N.*XF9I??=h'v]S+a]v$ lg(I40SR2NC6Ju"qg]{$nX{%#qb3C;8N,X/SI3Q{ pgPPgp {S,05(|]?C~ :I)}]_lS,gq GNgqN3',.X8Ix?='vbWeX+]{$6u\a5! {-\aQrbI\akOFg\a 4-} \a8 q4\aahVS\abKBm\a$~+%}&\a;|m=\aThV\\ar@8u\E*)B+CG%}|[bmaxyP?/18(GJ#yrhmbX|} O<7;4!RTurnqbgK&&G|3;>-7[bqShruVnJ*-@&*EG%};|^fm[az|Pr?/27,&IL"yErhmbdU"!N|5/}\/!u3\On\O\hLCm\#3,$\7|m;\ThVY\nB:q\&*$})\6u\B-#u1\)GrbI\xaVKg\Z|4-} \>0 u3\#Qn\S\rhKBm\O#2+%\49|m<\ZVnruVK&)B0CG+|[bmhxyV@/19)GJ#}xhmgX|} O=7:5!SQ z~GrnqbJhM$&Dm|3;>-} 7T\q;RhruVTnJ&*@q$+DG%}&;|[bm>[axyPbr?/28u{*&MJ#}+{XDrbGQ3[XNb,x7/yu/!u2nRIn\KJ(hMCm|3,} h7|m;aFShVV=nC:q&*$}(~d;|m?]:[aPb0r?7uz,&"y0qXErbHN3aVKg,|5-} t/ u3lRQn\RJ$hKBm#}"g9<`DT\=ru&)}cCF[7[b,ruw-1pVGIN0ag(|} s04kHQS?$hm$&e;=^DT\=ru&*|YDGQ7[b,|uvoTL(jcF?$d]A:{XQ3,unRJ(haF= \ No newline at end of file diff --git a/tests/logo_rgb555.raw b/tests/logo_rgb555.raw new file mode 100644 index 0000000..3feb53e Binary files /dev/null and b/tests/logo_rgb555.raw differ diff --git a/tests/logo_rgb888.raw b/tests/logo_rgb888.raw new file mode 100644 index 0000000..3ee66ba --- /dev/null +++ b/tests/logo_rgb888.raw @@ -0,0 +1 @@ +𡥨@FM''7=Ddhk''''''^bdꎒ''''''''''&5'''''''''''' /MSZ''''''''''''''''EKRptw''''''''''''''''''''jnp뙞'''''''''''''''''''''''',5@''''''''''''''''''''''''''%-8TXZ''''''''''''''''''''''''''''''KQX}''''''''''''''''''''''''''''''''''w{~ .'''''''''''''''''''''''''''''''''''''=CJ''''''''''''''''''''''''''''''''''''''''5;Aaeg''''''''''''''''''''''''''''''''''''''''''''X\_낆''''''''''''''''''''''''''''''''''''''''''''''''z𬪮&5'''''''''''''''''''''''''''''''''''''''''''''''''' .@FM'''''''''''''''''''''''''''''''''''''''''''''''''''''':@Ggkm''''''''''''''''''''''''''''''''''''''''''''''''''''''''''^bdꎒ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''!*4'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' /OU\''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''GMTtxz''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''kor뙞''''''''''''''''''''''''''''''''''''2ss2'''''''''''''''''''''''''''''''''''',5@''''''''''''''''''''''''''''''''''''Le[y,'''''''''''''''''''''''''''''''''''%-8SYa''''''''''''''''''''''''''''''''''''8Q?Z''''''''''''''''''''''''''''''''''''KQX''''''''''''''''''''''''''''''''''''$:'<''''''''''''''''''''''''''''''''''''w{~𡥨 .'''''''''''''''''''''''''''''''''''.f s/''''''''''''''''''''''''''''''''''''=CJ''''''''''''''''''''''''''''''''''''F_Sr''''''''''''''''''''''''''''''''''''5;Aaeg''''''''''''''''''''''''''''''''''''1K3M''''''''''''''''''''''''''''''''''''[_b慉''''''''''''''''''''''''''''''''''''!7 y$:''''''''''''''''''''''''''''''''''''}&5''''''''''''''''''''''''''''''''''',[yf-''''''''''''''''''''''''''''''''''' .CIP''''''''''''''''''''''''''''''''''''F_Jd'''''''''''''''''''''''''''''''''''':@Gptw'''''''''''''''''''''''''''''''''''')>.H''''''''''''''''''''''''''''''''''''gkmꐔ''''''''''''''''''''''''''''''''''''2s5'''''''''''''''''''''''''''''''''''')1<''''''''''''''''''''''''''''''''''',Sr_~,'''''''''''''''''''''''''''''''''''!*4TXZ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''Le[y,'''''''''''''''''''''''''''''''''''%-8`df''''''''''''''''''''''''''''''''''''8Q3''''''''2s.H''''''''''''''''''''''''''''''''''''gkm뒖''''''''''''''''''''''''''''''''''''2s,F'''''''''''')>5'''''''''''''''''''''''''''''''''''',5@''''''''''''''''''''''''''''''''''',SrF_''''''''''''''''B]_~,'''''''''''''''''''''''''''''''''''%-8TXZ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''mqt .'''''''''''''''''''''''''''''''''''.f 5N''''''''''''''''''''''''''/Is/''''''''''''''''''''''''''''''''''''17>''''''''''''''''''''''''''''''''''''LeLe''''''''''''''''''''''''''''''F_[y,''''''''''''''''''''''''''''''''''')15''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''2s,F''''''''''''''''''''''''''''''''''''jnp뙞''''''''''''''''''''''''''''''''''''2m.H'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''')> y3'''''''''''''''''''''''''''''''''''',5@''''''''''''''''''''''''''''''''''',SrIc''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''F__~,'''''''''''''''''''''''''''''''''''%-8TXZ''''''''''''''''''''''''''''''''''''8Qf-'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''',[y?Z''''''''''''''''''''''''''''''''''''KQXw{~'''''''''''''''''''''''''''''''''''''<!7''''''''''''''''''''''''''''''''''''cc''''''''''''''''''''''''''''''''''''!7'<''''''''''''''''''''''''''''''''''''w{~'''''''''''''''''''''''''''''''''''.f 3M''''''''''''''''''''''''''''''''''''?Z''''8Q/I'''''''''''''''''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x'''''''''''''''''''''''''''''''.c''''''''Yx/'''''''''''''''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x''''''''''''''''''''''''''''''j.'''''''''',{n''''''''''''''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x''''''''''''''''''''''''''''''''''''''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x'''''''''''''''''''''''',{f''''''''''''''''''''''[y.''''''''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x'''''''''''''''''''''''[y.''''''''''''''''''''''''.c'''''''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x'''''''''''''''''''''8Q)>'''''''''''''''''''''''''''''<D]'''''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x'''''''''''''''''''2Le'''''''''''''''''''''''''''''''' D_5'''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x''''''''''''''''''_~q''''''''''''''''''''''''''''''''''''qc''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x''''''''''''''''''3''''''''''''''''''''''''''''''''''''''/''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x'''''''''''''''''' 8Q''''''''''''''''''''''''''''''''''''''''''.C''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x''''''''''''''''''Sr''''''''''''''''''''''''''''''''''''''''''''''Le''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x'''''''''''''''''','''''''''''''''''''''''''''''''''''''''''''''''''t''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x''''''''''''''''''5''''''''''''''''''''''''''''''''''''''''''''''''''''2''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x''''''''''''''''''9S''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''Yx ''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x''''''''''''''''''B]'''''''''''''''''''''''''''''''''''''''''''''''''''''''.C''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x''''''''''''''''''j'''''''''''''''''''''''''''''''''''''''''''''''''''''''2''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''k'''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''Jd5''''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''-B?Z''''''''''''''''''''''''''''''''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x'''''''''''''''''''''''''''''''''''''''''.c''''''''''''''''''''''''''''''''''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x''''''''''''''''''''''''''''''''''''''''f.'''''''''''''''''''''''''''''''''''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x''''''''''''''''''''''''''''''''''''"8B]'''''''''''''''''''''''''''''''''''''''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x'''''''''''''''''''''''''''''''''',{n'''''''''''''''''''''''''''''''''''''''''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x'''''''''''''''''''''''''''''''''Yx .''''''''''''''''''''''''''''''''''''''''''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x'''''''''''''''''''''''''''''''8Q/I''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x''''''''''''''''''''''','''''2On''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x''''''''''''''''''''','','',t{''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x''''''''''''''''''','','''Ic3'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x''''''''''''''''''''''''-B 8Q'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x'''''''''''''''''''','/ [y'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x'''''''''''''''''''''f,''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x'''''''''''''''''''B]'<''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x'''''''''''''''''' '''''''''''''''''''''''''''''''''''''''''''''''''''''''On ''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x''''''''''''''''''F_'''''''''''''''''''''''''''''''''''''''''''''''''''''''-B''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x''''''''''''''''''q'''''''''''''''''''''''''''''''''''''''''''''''''''''''2 ''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x''''''''''''''''''/'''''''''''''''''''''''''''''''''''''''''''''''''''''''c''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x''''''''''''''''''1K'''''''''''''''''''''''''''''''''''''''''''''''''''''''B]''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x''''''''''''''''',Qp''''''''''''''''''''''''''''''''''''''''''''''''''''''')>''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x''''''''''''''',''{'''''''''''''''''''''''''''''''''''''''''''''''''''''''-{''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x''''''''''''','''' 3'''''''''''''''''''''''''''''''''''''''''''''''''''''''_~''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x''''''''''',''''',8Q,''''''''''''''''''''''''''''''''''''''''''''''''''''''8Q''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x'''''''''',''',''' [y'',''''''''''''''''''''''''''''''''''''''''''''''''''''!7''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x'''''''',''''''',,,'''''''''''''''''''''''''''''''''''''''''''''''''''''',r''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x'''''''''''',,'''''<'',',''''''''''''''''''''''''''''''''''''''''''''''''''Sr ''''''''''''''''''r''''''''''''''''''''''''''''''''''''''x''''''''''''''''''''''''''''''''''''''''''''''''''''''''/''''''''''''''''''''''''''''''''''', .'''''''''''''''''''''''''''''''''''-{''''''''''''''''''''''''''''''''''''''m''''''''''''''''''''''''''''''''''''$:&=''''''''''''''''''''''''''''''''''''c''''''''''''''''''''''''''''''''''''''Jd''''''''''''''''''''''''''''''''''''B]F_''''''''''''''''''''''''''''''''''''A[''''''''''''''''''''''''''''''''''''''(=''''''''''''''''''''''''''''''''''''fj''''''''''''''''''''''''''''''''''''$:''''''''''''''''''''''''''''''''''''''.'''''''''''''''''''''''''''''''''''/ 2''''''''''''''''''''''''''''''''''',{''''''''''''''''''''''''''''''''''''''[y''''''''''''''''''''''''''''''''''''-B .C''''''''''''''''''''''''''''''''''''Yx'''''''''''''''''''''''''''''''''''''''$:'''''''''''''''''''''''''''''''''''''''''''''''''''''''Vt/'''''''''''''''''''''''''''''''''''. .'''''''''''''''''''''''''''''''''''-{'''''''''''''''''''''''''''''''''''''''''''''''''''''''',{j'''''''''''''''''''''''''''''''''''')>(=''''''''''''''''''''''''''''''''''''_~''''''''''''''''''''''''''''''''''''''''''''''''''''''''''"8A[''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''8Q'<r''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''9S(=''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''!7?Z''''''''''''''''''''''''''''''''''''''''''''''''''''''''3M ,F'''''''r'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''B]5''''''''''''''''''''''''''''''''''''''''''''''''''''2 Ic'''''''''r'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''e,'''''''''''''''''''''''''''''''''''''''''''''''''kn'''''''''''r''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''2 Qk''''''''''''''''''''''''''''''''''''''''''''''F_2''''''''''''r''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''-B3M'''''''''''''''''''''''''''''''''''''''''',A 1G''''''''''''''r''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''Jd2''''''''''''''''''''''''''''''''''''''2 Le''''''''''''''''r''''''''''''''''''''''''''''''''''''''5''''''''''''''''''''''''''''''''''''tm''''''''''''''''''''''''''''''''''''ft,'''''''''''''''''r'''''''''''''''''''''''''''''''''''''','''''''''''''''''''''''''''''''''''2Le''''''''''''''''''''''''''''''''B]5'''''''''''''''''''r'''''''''''''''''''''''''''''''''''''' Qk''''''''''''''''''''''''''''''''''''8Q)>''''''''''''''''''''''''''''$:''''''''''''''''''''''''''''''''''''e .'''''''''',rf''''''''''''''''''''''''''''''r''''''''''''''''''''''''''''''''''''''.'''''''''''''''''''''''''''''''''''.[y''''''''Qp  +'''''''''''''''''''''''''''''''r''''''''''''''''''''''''''''''''''''''c'''''''''''''''''''''''''''''''''''')>''''''''''''''''''''''''''''''''''''''2''''''''''''''''''''''''''''''''''', .'''''''''''''''''''''''''''''''''''+''''''''''''''''''''''''''''''''''''''m''''''''''''''''''''''''''''''''''''$:'<''''''''''''''''''''''''''''''''''''c''''''''''''''''''''''''''''''''''''''Le''''''''''''''''''''''''''''''''''''B]F_''''''''''''''''''''''''''''''''''''A['''''''''''''''''''''''''''''''''''''' (=''''''''''''''''''''''''''''''''''''fc'''''''''''''''''''''''''''''''''''''<'''''''''''''''''''''''''''''''''''''''Yx.'''''''''''''''''''''''''''''''''''/ 2''''''''''''''''''''''''''''''''''',{[y''''''''''''''''''''''''''''''''''''''''',_~'''''''''''''''''''''''''''''''''''',A .H''''''''''''''''''''''''''''''''''''Sr .''''''''''''''''''''''''''''''''''''''''''''$:5N''''''''''''''''''''''''''''''''''''w{~TXZ''''''''''''''''''''''''''''''''''''Sr/''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''-{Yx''''''''''''''''''''''''''''''''''''KQX,5@''''''''''''''''''''''''''''''''''''qm''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''_~{,'''''''''''''''''''''''''''''''''''%-8뙞''''''''''''''''''''''''''''''''''''"8D]''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''8Q'<''''''''''''''''''''''''''''''''''''ptw''''''''''''''''''''''''''''''''''''9S(=''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''$:;P''''''''''''''''''''''''''''''''''''mqtTXZ''''''''''''''''''''''''''''''''''''Vt.''''''''''''''''''-{[y''''''''''''''''''''''''''''''''''''GMT,5@''''''''''''''''''''''''''''''''''',xk''''''''''''''''[y-'''''''''''''''''''''''''''''''''''%-8뒖''''''''''''''''''''''''''''''''''''"8A[''''''''''''8Q'<''''''''''''''''''''''''''''''''''''ptw''''''''''''''''''''''''''''''''''''9S'<''''''''!7 )>''''''''''''''''''''''''''''''''''''dhk''''''''''''''''''''''''''''''''''''F_Jd''''''''''''''''''''''''''''''''''''[_b=CJ''''''''''''''''''''''''''''''''''''er''''''''''''''''''''''''''''''''''''7=D𬪮 /'''''''''''''''''''''''''''''''''''22''''''''''''''''''''''''''''''''''' .悆''''''''''''''''''''''''''''''''''''-B 1G''''''''''''''''''''''''''''''''''''z`df'''''''''''''''''''''''''''''''''''' QkVt''''''''''''''''''''''''''''''''''''X\_17>''''''''''''''''''''''''''''''''''''q{,'''''''''''''''''''''''''''''''''''%-8𡥨''''''''''''''''''''''''''''''''''''25''''''''''''''''''''''''''''''''''''txz''''''''''''''''''''''''''''''''''''8Q''''''''''''''''''''''''''''''''''''q{,'''''''''''''''''''''''''''''''''''%-8뙞''''''''''''''''''''''''''''''''''''"8!7''''''''''''''''''''''''''''''''''''txz''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''korOU\''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''GMT!*4'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' /ꎒ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''gkm''''''''''''''''''''''''''''''''''''''''''''''''''''''''''`df@FM'''''''''''''''''''''''''''''''''''''''''''''''''''''':@G󬱳&5'''''''''''''''''''''''''''''''''''''''''''''''''' .悆''''''''''''''''''''''''''''''''''''''''''''''''zaeg''''''''''''''''''''''''''''''''''''''''''''X\_=CJ''''''''''''''''''''''''''''''''''''''''5;A .'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''w{~TXZ''''''''''''''''''''''''''''''KQX,5@''''''''''''''''''''''''''%-8뙞''''''''''''''''''''''''ptw''''''''''''''''''''jnpMSZ''''''''''''''''EKR!*4'''''''''''' /ꎒ''''''''''dhk''''''^bd@FM''7=D𡥨 \ No newline at end of file diff --git a/tests/logo_type10_16bpp_bl.tga b/tests/logo_type10_16bpp_bl.tga new file mode 100644 index 0000000..dd0a2b5 Binary files /dev/null and b/tests/logo_type10_16bpp_bl.tga differ diff --git a/tests/logo_type10_16bpp_tl.tga b/tests/logo_type10_16bpp_tl.tga new file mode 100644 index 0000000..278085f Binary files /dev/null and b/tests/logo_type10_16bpp_tl.tga differ diff --git a/tests/logo_type10_24bpp_bl.tga b/tests/logo_type10_24bpp_bl.tga new file mode 100644 index 0000000..b432e19 Binary files /dev/null and b/tests/logo_type10_24bpp_bl.tga differ diff --git a/tests/logo_type10_24bpp_tl.tga b/tests/logo_type10_24bpp_tl.tga new file mode 100644 index 0000000..4e615e6 Binary files /dev/null and b/tests/logo_type10_24bpp_tl.tga differ diff --git a/tests/logo_type11_bl.tga b/tests/logo_type11_bl.tga new file mode 100644 index 0000000..cef72c0 Binary files /dev/null and b/tests/logo_type11_bl.tga differ diff --git a/tests/logo_type11_tl.tga b/tests/logo_type11_tl.tga new file mode 100644 index 0000000..f883349 Binary files /dev/null and b/tests/logo_type11_tl.tga differ diff --git a/tests/logo_type1_16bpp_bl.tga b/tests/logo_type1_16bpp_bl.tga new file mode 100644 index 0000000..1f20446 Binary files /dev/null and b/tests/logo_type1_16bpp_bl.tga differ diff --git a/tests/logo_type1_16bpp_tl.tga b/tests/logo_type1_16bpp_tl.tga new file mode 100644 index 0000000..1b1d062 Binary files /dev/null and b/tests/logo_type1_16bpp_tl.tga differ diff --git a/tests/logo_type1_24bpp_bl.tga b/tests/logo_type1_24bpp_bl.tga new file mode 100644 index 0000000..99b7bf3 Binary files /dev/null and b/tests/logo_type1_24bpp_bl.tga differ diff --git a/tests/logo_type1_24bpp_tl.tga b/tests/logo_type1_24bpp_tl.tga new file mode 100644 index 0000000..9a97db8 Binary files /dev/null and b/tests/logo_type1_24bpp_tl.tga differ diff --git a/tests/logo_type2_16bpp_bl.tga b/tests/logo_type2_16bpp_bl.tga new file mode 100644 index 0000000..a595d6b Binary files /dev/null and b/tests/logo_type2_16bpp_bl.tga differ diff --git a/tests/logo_type2_16bpp_tl.tga b/tests/logo_type2_16bpp_tl.tga new file mode 100644 index 0000000..41b453a Binary files /dev/null and b/tests/logo_type2_16bpp_tl.tga differ diff --git a/tests/logo_type2_24bpp_bl.tga b/tests/logo_type2_24bpp_bl.tga new file mode 100644 index 0000000..f2859e5 Binary files /dev/null and b/tests/logo_type2_24bpp_bl.tga differ diff --git a/tests/logo_type2_24bpp_br.tga b/tests/logo_type2_24bpp_br.tga new file mode 100644 index 0000000..fba0bb9 Binary files /dev/null and b/tests/logo_type2_24bpp_br.tga differ diff --git a/tests/logo_type2_24bpp_tl.tga b/tests/logo_type2_24bpp_tl.tga new file mode 100644 index 0000000..88e2604 Binary files /dev/null and b/tests/logo_type2_24bpp_tl.tga differ diff --git a/tests/logo_type2_24bpp_tr.tga b/tests/logo_type2_24bpp_tr.tga new file mode 100644 index 0000000..979c610 Binary files /dev/null and b/tests/logo_type2_24bpp_tr.tga differ diff --git a/tests/logo_type3_bl.tga b/tests/logo_type3_bl.tga new file mode 100644 index 0000000..cbdb983 Binary files /dev/null and b/tests/logo_type3_bl.tga differ diff --git a/tests/logo_type3_tl.tga b/tests/logo_type3_tl.tga new file mode 100644 index 0000000..98f92a4 Binary files /dev/null and b/tests/logo_type3_tl.tga differ diff --git a/tests/logo_type9_16bpp_bl.tga b/tests/logo_type9_16bpp_bl.tga new file mode 100644 index 0000000..435a2d4 Binary files /dev/null and b/tests/logo_type9_16bpp_bl.tga differ diff --git a/tests/logo_type9_16bpp_tl.tga b/tests/logo_type9_16bpp_tl.tga new file mode 100644 index 0000000..4c288e2 Binary files /dev/null and b/tests/logo_type9_16bpp_tl.tga differ diff --git a/tests/logo_type9_24bpp_bl.tga b/tests/logo_type9_24bpp_bl.tga new file mode 100644 index 0000000..2bc6da8 Binary files /dev/null and b/tests/logo_type9_24bpp_bl.tga differ diff --git a/tests/logo_type9_24bpp_tl.tga b/tests/logo_type9_24bpp_tl.tga new file mode 100644 index 0000000..5d1365c Binary files /dev/null and b/tests/logo_type9_24bpp_tl.tga differ diff --git a/tests/types.rs b/tests/types.rs index 01b30be..17cdf9e 100644 --- a/tests/types.rs +++ b/tests/types.rs @@ -1,9 +1,10 @@ -use tinytga::{Bpp, ImageOrigin, ImageType, RawTga, TgaHeader}; +use tinytga::{Bpp, Compression, DataType, ImageOrigin, RawTga, TgaHeader}; const HEADER_DEFAULT: TgaHeader = TgaHeader { id_len: 0, has_color_map: false, - image_type: ImageType::Empty, + data_type: DataType::NoData, + compression: Compression::Uncompressed, color_map_start: 0, color_map_len: 0, color_map_depth: None, @@ -24,7 +25,7 @@ fn type1_16bpp_bl() { tga.header(), TgaHeader { has_color_map: true, - image_type: ImageType::ColorMapped, + data_type: DataType::ColorMapped, color_map_start: 0, color_map_len: 8, color_map_depth: Some(Bpp::Bits16), @@ -46,7 +47,7 @@ fn type1_24bpp_bl() { tga.header(), TgaHeader { has_color_map: true, - image_type: ImageType::ColorMapped, + data_type: DataType::ColorMapped, color_map_start: 0, color_map_len: 8, color_map_depth: Some(Bpp::Bits24), @@ -68,7 +69,7 @@ fn type1_16bpp_tl() { tga.header(), TgaHeader { has_color_map: true, - image_type: ImageType::ColorMapped, + data_type: DataType::ColorMapped, color_map_start: 0, color_map_len: 8, color_map_depth: Some(Bpp::Bits16), @@ -91,7 +92,7 @@ fn type1_24bpp_tl() { tga.header(), TgaHeader { has_color_map: true, - image_type: ImageType::ColorMapped, + data_type: DataType::ColorMapped, color_map_start: 0, color_map_len: 8, color_map_depth: Some(Bpp::Bits24), @@ -113,7 +114,7 @@ fn type2_16bpp_bl() { assert_eq!( tga.header(), TgaHeader { - image_type: ImageType::Truecolor, + data_type: DataType::TrueColor, pixel_depth: Bpp::Bits16, ..HEADER_DEFAULT } @@ -132,7 +133,7 @@ fn type2_24bpp_bl() { assert_eq!( tga.header(), TgaHeader { - image_type: ImageType::Truecolor, + data_type: DataType::TrueColor, pixel_depth: Bpp::Bits24, ..HEADER_DEFAULT } @@ -151,7 +152,7 @@ fn type2_16bpp_tl() { assert_eq!( tga.header(), TgaHeader { - image_type: ImageType::Truecolor, + data_type: DataType::TrueColor, pixel_depth: Bpp::Bits16, image_origin: ImageOrigin::TopLeft, ..HEADER_DEFAULT @@ -171,7 +172,7 @@ fn type2_24bpp_tl() { assert_eq!( tga.header(), TgaHeader { - image_type: ImageType::Truecolor, + data_type: DataType::TrueColor, pixel_depth: Bpp::Bits24, image_origin: ImageOrigin::TopLeft, ..HEADER_DEFAULT @@ -191,7 +192,7 @@ fn type3_bl() { assert_eq!( tga.header(), TgaHeader { - image_type: ImageType::Monochrome, + data_type: DataType::BlackAndWhite, ..HEADER_DEFAULT } ); @@ -209,7 +210,7 @@ fn type3_tl() { assert_eq!( tga.header(), TgaHeader { - image_type: ImageType::Monochrome, + data_type: DataType::BlackAndWhite, image_origin: ImageOrigin::TopLeft, ..HEADER_DEFAULT } @@ -229,7 +230,8 @@ fn type9_16bpp() { tga.header(), TgaHeader { has_color_map: true, - image_type: ImageType::RleColorMapped, + data_type: DataType::ColorMapped, + compression: Compression::Rle, color_map_start: 0, color_map_len: 8, color_map_depth: Some(Bpp::Bits16), @@ -251,7 +253,8 @@ fn type9_24bpp_bl() { tga.header(), TgaHeader { has_color_map: true, - image_type: ImageType::RleColorMapped, + data_type: DataType::ColorMapped, + compression: Compression::Rle, color_map_start: 0, color_map_len: 8, color_map_depth: Some(Bpp::Bits24), @@ -273,7 +276,8 @@ fn type9_16bpp_tl() { tga.header(), TgaHeader { has_color_map: true, - image_type: ImageType::RleColorMapped, + data_type: DataType::ColorMapped, + compression: Compression::Rle, color_map_start: 0, color_map_len: 8, color_map_depth: Some(Bpp::Bits16), @@ -296,7 +300,8 @@ fn type9_24bpp_tl() { tga.header(), TgaHeader { has_color_map: true, - image_type: ImageType::RleColorMapped, + data_type: DataType::ColorMapped, + compression: Compression::Rle, color_map_start: 0, color_map_len: 8, color_map_depth: Some(Bpp::Bits24), @@ -318,7 +323,8 @@ fn type10_16bpp_bl() { assert_eq!( tga.header(), TgaHeader { - image_type: ImageType::RleTruecolor, + data_type: DataType::TrueColor, + compression: Compression::Rle, pixel_depth: Bpp::Bits16, ..HEADER_DEFAULT } @@ -337,7 +343,8 @@ fn type10_24bpp_bl() { assert_eq!( tga.header(), TgaHeader { - image_type: ImageType::RleTruecolor, + data_type: DataType::TrueColor, + compression: Compression::Rle, pixel_depth: Bpp::Bits24, ..HEADER_DEFAULT } @@ -356,7 +363,8 @@ fn type10_16bpp_tl() { assert_eq!( tga.header(), TgaHeader { - image_type: ImageType::RleTruecolor, + data_type: DataType::TrueColor, + compression: Compression::Rle, pixel_depth: Bpp::Bits16, image_origin: ImageOrigin::TopLeft, ..HEADER_DEFAULT @@ -376,7 +384,8 @@ fn type10_24bpp_tl() { assert_eq!( tga.header(), TgaHeader { - image_type: ImageType::RleTruecolor, + data_type: DataType::TrueColor, + compression: Compression::Rle, pixel_depth: Bpp::Bits24, image_origin: ImageOrigin::TopLeft, ..HEADER_DEFAULT @@ -396,7 +405,8 @@ fn type11_bl() { assert_eq!( tga.header(), TgaHeader { - image_type: ImageType::RleMonochrome, + data_type: DataType::BlackAndWhite, + compression: Compression::Rle, ..HEADER_DEFAULT } ); @@ -414,7 +424,8 @@ fn type11_tl() { assert_eq!( tga.header(), TgaHeader { - image_type: ImageType::RleMonochrome, + data_type: DataType::BlackAndWhite, + compression: Compression::Rle, image_origin: ImageOrigin::TopLeft, ..HEADER_DEFAULT } diff --git a/tests/ubw8.rs b/tests/ubw8.rs index 12b0877..860d6e1 100644 --- a/tests/ubw8.rs +++ b/tests/ubw8.rs @@ -1,4 +1,4 @@ -use tinytga::{Bpp, ImageOrigin, ImageType, RawTga, TgaHeader}; +use tinytga::{Bpp, Compression, DataType, ImageOrigin, RawTga, TgaHeader}; #[test] fn ubw8() { @@ -14,7 +14,8 @@ fn ubw8() { TgaHeader { id_len: 26, has_color_map: false, - image_type: ImageType::Monochrome, + data_type: DataType::BlackAndWhite, + compression: Compression::Uncompressed, color_map_start: 0, color_map_len: 0, color_map_depth: None,