Use ExtendedColorType when encoding (#2142)
This commit is contained in:
parent
3b1fbcf2ae
commit
a0ebeabafc
35 changed files with 353 additions and 335 deletions
|
|
@ -245,6 +245,6 @@ fn main() {
|
|||
let buffer: &[u8] = unimplemented!(); // Generate the image data
|
||||
|
||||
// Save the buffer as "image.png"
|
||||
image::save_buffer("image.png", buffer, 800, 600, image::ColorType::Rgb8).unwrap()
|
||||
image::save_buffer("image.png", buffer, 800, 600, image::ExtendedColorType::Rgb8).unwrap()
|
||||
}
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
extern crate criterion;
|
||||
|
||||
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
|
||||
use image::ExtendedColorType;
|
||||
use image::{codecs::bmp::BmpEncoder, codecs::jpeg::JpegEncoder, ColorType};
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{BufWriter, Seek, SeekFrom, Write};
|
||||
|
||||
trait Encoder {
|
||||
fn encode_raw(&self, into: &mut Vec<u8>, im: &[u8], dims: u32, color: ColorType);
|
||||
fn encode_bufvec(&self, into: &mut Vec<u8>, im: &[u8], dims: u32, color: ColorType);
|
||||
fn encode_file(&self, file: &File, im: &[u8], dims: u32, color: ColorType);
|
||||
fn encode_raw(&self, into: &mut Vec<u8>, im: &[u8], dims: u32, color: ExtendedColorType);
|
||||
fn encode_bufvec(&self, into: &mut Vec<u8>, im: &[u8], dims: u32, color: ExtendedColorType);
|
||||
fn encode_file(&self, file: &File, im: &[u8], dims: u32, color: ExtendedColorType);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
|
|
@ -50,9 +51,8 @@ type BenchGroup<'a> = criterion::BenchmarkGroup<'a, criterion::measurement::Wall
|
|||
///
|
||||
/// For compressed formats this is surely not representative of encoding a normal image but it's a
|
||||
/// start for benchmarking.
|
||||
fn encode_zeroed(group: &mut BenchGroup, with: &dyn Encoder, size: u32, color: ColorType) {
|
||||
let bytes = size as usize * usize::from(color.bytes_per_pixel());
|
||||
let im = vec![0; bytes * bytes];
|
||||
fn encode_zeroed(group: &mut BenchGroup, with: &dyn Encoder, size: u32, color: ExtendedColorType) {
|
||||
let im = vec![0; (color.bits_per_pixel() as usize * size as usize + 7) / 8 * size as usize];
|
||||
|
||||
group.bench_with_input(
|
||||
BenchmarkId::new(format!("zero-{:?}-rawvec", color), size),
|
||||
|
|
@ -87,7 +87,7 @@ fn encode_definition(criterion: &mut Criterion, def: &BenchDef) {
|
|||
|
||||
for &color in def.colors {
|
||||
for &size in def.sizes {
|
||||
encode_zeroed(&mut group, def.with, size, color);
|
||||
encode_zeroed(&mut group, def.with, size, color.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -97,22 +97,22 @@ struct Bmp;
|
|||
struct Jpeg;
|
||||
|
||||
trait EncoderBase {
|
||||
fn encode(&self, into: impl Write, im: &[u8], dims: u32, color: ColorType);
|
||||
fn encode(&self, into: impl Write, im: &[u8], dims: u32, color: ExtendedColorType);
|
||||
}
|
||||
|
||||
impl<T: EncoderBase> Encoder for T {
|
||||
fn encode_raw(&self, into: &mut Vec<u8>, im: &[u8], dims: u32, color: ColorType) {
|
||||
fn encode_raw(&self, into: &mut Vec<u8>, im: &[u8], dims: u32, color: ExtendedColorType) {
|
||||
into.clear();
|
||||
self.encode(into, im, dims, color);
|
||||
}
|
||||
|
||||
fn encode_bufvec(&self, into: &mut Vec<u8>, im: &[u8], dims: u32, color: ColorType) {
|
||||
fn encode_bufvec(&self, into: &mut Vec<u8>, im: &[u8], dims: u32, color: ExtendedColorType) {
|
||||
into.clear();
|
||||
let buf = BufWriter::new(into);
|
||||
self.encode(buf, im, dims, color);
|
||||
}
|
||||
|
||||
fn encode_file(&self, mut file: &File, im: &[u8], dims: u32, color: ColorType) {
|
||||
fn encode_file(&self, mut file: &File, im: &[u8], dims: u32, color: ExtendedColorType) {
|
||||
file.seek(SeekFrom::Start(0)).unwrap();
|
||||
let buf = BufWriter::new(file);
|
||||
self.encode(buf, im, dims, color);
|
||||
|
|
@ -120,14 +120,14 @@ impl<T: EncoderBase> Encoder for T {
|
|||
}
|
||||
|
||||
impl EncoderBase for Bmp {
|
||||
fn encode(&self, mut into: impl Write, im: &[u8], size: u32, color: ColorType) {
|
||||
fn encode(&self, mut into: impl Write, im: &[u8], size: u32, color: ExtendedColorType) {
|
||||
let mut x = BmpEncoder::new(&mut into);
|
||||
x.encode(im, size, size, color).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl EncoderBase for Jpeg {
|
||||
fn encode(&self, mut into: impl Write, im: &[u8], size: u32, color: ColorType) {
|
||||
fn encode(&self, mut into: impl Write, im: &[u8], size: u32, color: ExtendedColorType) {
|
||||
let mut x = JpegEncoder::new(&mut into);
|
||||
x.encode(im, size, size, color).unwrap();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ extern crate image;
|
|||
|
||||
use image::codecs::openexr::*;
|
||||
use image::io::Limits;
|
||||
use image::ColorType;
|
||||
use image::ExtendedColorType;
|
||||
use image::ImageDecoder;
|
||||
use image::ImageEncoder;
|
||||
use image::ImageResult;
|
||||
|
|
@ -45,7 +45,7 @@ fn roundtrip(bytes: &[u8]) -> ImageResult<()> {
|
|||
write: impl Write + Seek,
|
||||
(width, height, data): &(u32, u32, Vec<u8>),
|
||||
) -> ImageResult<()> {
|
||||
OpenExrEncoder::new(write).write_image(data.as_slice(), *width, *height, ColorType::Rgba32F)
|
||||
OpenExrEncoder::new(write).write_image(data.as_slice(), *width, *height, ExtendedColorType::Rgba32F)
|
||||
}
|
||||
|
||||
let decoded_image = read_as_rgba_byte_image(Cursor::new(bytes))?;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
use std::cmp::Ordering;
|
||||
use std::iter::Iterator;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::error::ImageResult;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
/// The [AVIF] specification defines an image derivative of the AV1 bitstream, an open video codec.
|
||||
///
|
||||
/// [AVIF]: https://aomediacodec.github.io/av1-avif/
|
||||
use std::convert::TryFrom;
|
||||
use std::error::Error;
|
||||
use std::io::Read;
|
||||
use std::marker::PhantomData;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use crate::color::{FromColor, Luma, LumaA, Rgb, Rgba};
|
|||
use crate::error::{
|
||||
EncodingError, ParameterError, ParameterErrorKind, UnsupportedError, UnsupportedErrorKind,
|
||||
};
|
||||
use crate::{ColorType, ImageBuffer, ImageEncoder, ImageFormat, Pixel};
|
||||
use crate::{ExtendedColorType, ImageBuffer, ImageEncoder, ImageFormat, Pixel};
|
||||
use crate::{ImageError, ImageResult};
|
||||
|
||||
use bytemuck::{try_cast_slice, try_cast_slice_mut, Pod, PodCastError};
|
||||
|
|
@ -104,10 +104,9 @@ impl<W: Write> ImageEncoder for AvifEncoder<W> {
|
|||
data: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
color: ColorType,
|
||||
color: ExtendedColorType,
|
||||
) -> ImageResult<()> {
|
||||
let expected_buffer_len =
|
||||
(width as u64 * height as u64).saturating_mul(color.bytes_per_pixel() as u64);
|
||||
let expected_buffer_len = color.buffer_size(width, height);
|
||||
assert_eq!(
|
||||
expected_buffer_len,
|
||||
data.len() as u64,
|
||||
|
|
@ -134,7 +133,7 @@ impl<W: Write> ImageEncoder for AvifEncoder<W> {
|
|||
|
||||
impl<W: Write> AvifEncoder<W> {
|
||||
// Does not currently do anything. Mirrors behaviour of old config function.
|
||||
fn set_color(&mut self, _color: ColorType) {
|
||||
fn set_color(&mut self, _color: ExtendedColorType) {
|
||||
// self.config.color_space = ColorSpace::RGB;
|
||||
}
|
||||
|
||||
|
|
@ -143,7 +142,7 @@ impl<W: Write> AvifEncoder<W> {
|
|||
data: &'buf [u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
color: ColorType,
|
||||
color: ExtendedColorType,
|
||||
) -> ImageResult<RgbColor<'buf>> {
|
||||
// Error wrapping utility for color dependent buffer dimensions.
|
||||
fn try_from_raw<P: Pixel + 'static>(
|
||||
|
|
@ -210,7 +209,7 @@ impl<W: Write> AvifEncoder<W> {
|
|||
}
|
||||
|
||||
match color {
|
||||
ColorType::Rgb8 => {
|
||||
ExtendedColorType::Rgb8 => {
|
||||
// ravif doesn't do any checks but has some asserts, so we do the checks.
|
||||
let img = try_from_raw::<Rgb<u8>>(data, width, height)?;
|
||||
// Now, internally ravif uses u32 but it takes usize. We could do some checked
|
||||
|
|
@ -227,7 +226,7 @@ impl<W: Write> AvifEncoder<W> {
|
|||
height as usize,
|
||||
)))
|
||||
}
|
||||
ColorType::Rgba8 => {
|
||||
ExtendedColorType::Rgba8 => {
|
||||
// ravif doesn't do any checks but has some asserts, so we do the checks.
|
||||
let img = try_from_raw::<Rgba<u8>>(data, width, height)?;
|
||||
// Now, internally ravif uses u32 but it takes usize. We could do some checked
|
||||
|
|
@ -245,31 +244,31 @@ impl<W: Write> AvifEncoder<W> {
|
|||
)))
|
||||
}
|
||||
// we need a separate buffer..
|
||||
ColorType::L8 => {
|
||||
ExtendedColorType::L8 => {
|
||||
let image = try_from_raw::<Luma<u8>>(data, width, height)?;
|
||||
Ok(RgbColor::Rgba8(convert_into(fallback, image)))
|
||||
}
|
||||
ColorType::La8 => {
|
||||
ExtendedColorType::La8 => {
|
||||
let image = try_from_raw::<LumaA<u8>>(data, width, height)?;
|
||||
Ok(RgbColor::Rgba8(convert_into(fallback, image)))
|
||||
}
|
||||
// we need to really convert data..
|
||||
ColorType::L16 => {
|
||||
ExtendedColorType::L16 => {
|
||||
let buffer = cast_buffer(data)?;
|
||||
let image = try_from_raw::<Luma<u16>>(&buffer, width, height)?;
|
||||
Ok(RgbColor::Rgba8(convert_into(fallback, image)))
|
||||
}
|
||||
ColorType::La16 => {
|
||||
ExtendedColorType::La16 => {
|
||||
let buffer = cast_buffer(data)?;
|
||||
let image = try_from_raw::<LumaA<u16>>(&buffer, width, height)?;
|
||||
Ok(RgbColor::Rgba8(convert_into(fallback, image)))
|
||||
}
|
||||
ColorType::Rgb16 => {
|
||||
ExtendedColorType::Rgb16 => {
|
||||
let buffer = cast_buffer(data)?;
|
||||
let image = try_from_raw::<Rgb<u16>>(&buffer, width, height)?;
|
||||
Ok(RgbColor::Rgba8(convert_into(fallback, image)))
|
||||
}
|
||||
ColorType::Rgba16 => {
|
||||
ExtendedColorType::Rgba16 => {
|
||||
let buffer = cast_buffer(data)?;
|
||||
let image = try_from_raw::<Rgba<u16>>(&buffer, width, height)?;
|
||||
Ok(RgbColor::Rgba8(convert_into(fallback, image)))
|
||||
|
|
@ -278,7 +277,7 @@ impl<W: Write> AvifEncoder<W> {
|
|||
_ => Err(ImageError::Unsupported(
|
||||
UnsupportedError::from_format_and_kind(
|
||||
ImageFormat::Avif.into(),
|
||||
UnsupportedErrorKind::Color(color.into()),
|
||||
UnsupportedErrorKind::Color(color),
|
||||
),
|
||||
)),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
use std::cmp::{self, Ordering};
|
||||
use std::convert::TryFrom;
|
||||
use std::io::{self, Read, Seek, SeekFrom};
|
||||
use std::iter::{repeat, Iterator, Rev};
|
||||
use std::iter::{repeat, Rev};
|
||||
use std::slice::ChunksMut;
|
||||
use std::{error, fmt};
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use crate::error::{
|
|||
EncodingError, ImageError, ImageFormatHint, ImageResult, ParameterError, ParameterErrorKind,
|
||||
};
|
||||
use crate::image::ImageEncoder;
|
||||
use crate::{color, ImageFormat};
|
||||
use crate::{ExtendedColorType, ImageFormat};
|
||||
|
||||
const BITMAPFILEHEADER_SIZE: u32 = 14;
|
||||
const BITMAPINFOHEADER_SIZE: u32 = 40;
|
||||
|
|
@ -22,7 +22,7 @@ impl<'a, W: Write + 'a> BmpEncoder<'a, W> {
|
|||
BmpEncoder { writer: w }
|
||||
}
|
||||
|
||||
/// Encodes the image `image` that has dimensions `width` and `height` and `ColorType` `c`.
|
||||
/// Encodes the image `image` that has dimensions `width` and `height` and `ExtendedColorType` `c`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
|
|
@ -33,7 +33,7 @@ impl<'a, W: Write + 'a> BmpEncoder<'a, W> {
|
|||
image: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
c: color::ColorType,
|
||||
c: ExtendedColorType,
|
||||
) -> ImageResult<()> {
|
||||
self.encode_with_palette(image, width, height, c, None)
|
||||
}
|
||||
|
|
@ -50,10 +50,10 @@ impl<'a, W: Write + 'a> BmpEncoder<'a, W> {
|
|||
image: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
c: color::ColorType,
|
||||
c: ExtendedColorType,
|
||||
palette: Option<&[[u8; 3]]>,
|
||||
) -> ImageResult<()> {
|
||||
if palette.is_some() && c != color::ColorType::L8 && c != color::ColorType::La8 {
|
||||
if palette.is_some() && c != ExtendedColorType::L8 && c != ExtendedColorType::La8 {
|
||||
return Err(ImageError::IoError(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
format!(
|
||||
|
|
@ -63,8 +63,7 @@ impl<'a, W: Write + 'a> BmpEncoder<'a, W> {
|
|||
)));
|
||||
}
|
||||
|
||||
let expected_buffer_len =
|
||||
(width as u64 * height as u64).saturating_mul(c.bytes_per_pixel() as u64);
|
||||
let expected_buffer_len = c.buffer_size(width, height);
|
||||
assert_eq!(
|
||||
expected_buffer_len,
|
||||
image.len() as u64,
|
||||
|
|
@ -141,12 +140,12 @@ impl<'a, W: Write + 'a> BmpEncoder<'a, W> {
|
|||
|
||||
// write image data
|
||||
match c {
|
||||
color::ColorType::Rgb8 => self.encode_rgb(image, width, height, row_pad_size, 3)?,
|
||||
color::ColorType::Rgba8 => self.encode_rgba(image, width, height, row_pad_size, 4)?,
|
||||
color::ColorType::L8 => {
|
||||
ExtendedColorType::Rgb8 => self.encode_rgb(image, width, height, row_pad_size, 3)?,
|
||||
ExtendedColorType::Rgba8 => self.encode_rgba(image, width, height, row_pad_size, 4)?,
|
||||
ExtendedColorType::L8 => {
|
||||
self.encode_gray(image, width, height, row_pad_size, 1, palette)?
|
||||
}
|
||||
color::ColorType::La8 => {
|
||||
ExtendedColorType::La8 => {
|
||||
self.encode_gray(image, width, height, row_pad_size, 2, palette)?
|
||||
}
|
||||
_ => {
|
||||
|
|
@ -274,13 +273,13 @@ impl<'a, W: Write> ImageEncoder for BmpEncoder<'a, W> {
|
|||
buf: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
color_type: color::ColorType,
|
||||
color_type: ExtendedColorType,
|
||||
) -> ImageResult<()> {
|
||||
self.encode(buf, width, height, color_type)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_unsupported_error_message(c: color::ColorType) -> String {
|
||||
fn get_unsupported_error_message(c: ExtendedColorType) -> String {
|
||||
format!(
|
||||
"Unsupported color type {:?}. Supported types: RGB(8), RGBA(8), Gray(8), GrayA(8).",
|
||||
c
|
||||
|
|
@ -288,16 +287,19 @@ fn get_unsupported_error_message(c: color::ColorType) -> String {
|
|||
}
|
||||
|
||||
/// Returns a tuple representing: (dib header size, written pixel size, palette color count).
|
||||
fn get_pixel_info(c: color::ColorType, palette: Option<&[[u8; 3]]>) -> io::Result<(u32, u32, u32)> {
|
||||
fn get_pixel_info(
|
||||
c: ExtendedColorType,
|
||||
palette: Option<&[[u8; 3]]>,
|
||||
) -> io::Result<(u32, u32, u32)> {
|
||||
let sizes = match c {
|
||||
color::ColorType::Rgb8 => (BITMAPINFOHEADER_SIZE, 3, 0),
|
||||
color::ColorType::Rgba8 => (BITMAPV4HEADER_SIZE, 4, 0),
|
||||
color::ColorType::L8 => (
|
||||
ExtendedColorType::Rgb8 => (BITMAPINFOHEADER_SIZE, 3, 0),
|
||||
ExtendedColorType::Rgba8 => (BITMAPV4HEADER_SIZE, 4, 0),
|
||||
ExtendedColorType::L8 => (
|
||||
BITMAPINFOHEADER_SIZE,
|
||||
1,
|
||||
palette.map(|p| p.len()).unwrap_or(256) as u32,
|
||||
),
|
||||
color::ColorType::La8 => (
|
||||
ExtendedColorType::La8 => (
|
||||
BITMAPINFOHEADER_SIZE,
|
||||
1,
|
||||
palette.map(|p| p.len()).unwrap_or(256) as u32,
|
||||
|
|
@ -317,11 +319,12 @@ fn get_pixel_info(c: color::ColorType, palette: Option<&[[u8; 3]]>) -> io::Resul
|
|||
mod tests {
|
||||
use super::super::BmpDecoder;
|
||||
use super::BmpEncoder;
|
||||
use crate::color::ColorType;
|
||||
|
||||
use crate::image::ImageDecoder;
|
||||
use crate::ExtendedColorType;
|
||||
use std::io::Cursor;
|
||||
|
||||
fn round_trip_image(image: &[u8], width: u32, height: u32, c: ColorType) -> Vec<u8> {
|
||||
fn round_trip_image(image: &[u8], width: u32, height: u32, c: ExtendedColorType) -> Vec<u8> {
|
||||
let mut encoded_data = Vec::new();
|
||||
{
|
||||
let mut encoder = BmpEncoder::new(&mut encoded_data);
|
||||
|
|
@ -340,7 +343,7 @@ mod tests {
|
|||
#[test]
|
||||
fn round_trip_single_pixel_rgb() {
|
||||
let image = [255u8, 0, 0]; // single red pixel
|
||||
let decoded = round_trip_image(&image, 1, 1, ColorType::Rgb8);
|
||||
let decoded = round_trip_image(&image, 1, 1, ExtendedColorType::Rgb8);
|
||||
assert_eq!(3, decoded.len());
|
||||
assert_eq!(255, decoded[0]);
|
||||
assert_eq!(0, decoded[1]);
|
||||
|
|
@ -353,27 +356,27 @@ mod tests {
|
|||
let mut encoded_data = Vec::new();
|
||||
let image = vec![0u8; 3 * 40_000 * 40_000]; // 40_000x40_000 pixels, 3 bytes per pixel, allocated on the heap
|
||||
let mut encoder = BmpEncoder::new(&mut encoded_data);
|
||||
let result = encoder.encode(&image, 40_000, 40_000, ColorType::Rgb8);
|
||||
let result = encoder.encode(&image, 40_000, 40_000, ExtendedColorType::Rgb8);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip_single_pixel_rgba() {
|
||||
let image = [1, 2, 3, 4];
|
||||
let decoded = round_trip_image(&image, 1, 1, ColorType::Rgba8);
|
||||
let decoded = round_trip_image(&image, 1, 1, ExtendedColorType::Rgba8);
|
||||
assert_eq!(&decoded[..], &image[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip_3px_rgb() {
|
||||
let image = [0u8; 3 * 3 * 3]; // 3x3 pixels, 3 bytes per pixel
|
||||
let _decoded = round_trip_image(&image, 3, 3, ColorType::Rgb8);
|
||||
let _decoded = round_trip_image(&image, 3, 3, ExtendedColorType::Rgb8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip_gray() {
|
||||
let image = [0u8, 1, 2]; // 3 pixels
|
||||
let decoded = round_trip_image(&image, 3, 1, ColorType::L8);
|
||||
let decoded = round_trip_image(&image, 3, 1, ExtendedColorType::L8);
|
||||
// should be read back as 3 RGB pixels
|
||||
assert_eq!(9, decoded.len());
|
||||
assert_eq!(0, decoded[0]);
|
||||
|
|
@ -390,7 +393,7 @@ mod tests {
|
|||
#[test]
|
||||
fn round_trip_graya() {
|
||||
let image = [0u8, 0, 1, 0, 2, 0]; // 3 pixels, each with an alpha channel
|
||||
let decoded = round_trip_image(&image, 1, 3, ColorType::La8);
|
||||
let decoded = round_trip_image(&image, 1, 3, ExtendedColorType::La8);
|
||||
// should be read back as 3 RGB pixels
|
||||
assert_eq!(9, decoded.len());
|
||||
assert_eq!(0, decoded[0]);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
//!
|
||||
//! Note: this module only implements bare DXT encoding/decoding, it does not parse formats that can contain DXT files like .dds
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::io::{self, Read};
|
||||
|
||||
use crate::color::ColorType;
|
||||
|
|
|
|||
|
|
@ -16,15 +16,15 @@
|
|||
//! # Related Links
|
||||
//! * <https://tools.suckless.org/farbfeld/> - the farbfeld specification
|
||||
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::i64;
|
||||
use std::io::{self, Read, Seek, SeekFrom, Write};
|
||||
|
||||
use crate::color::ColorType;
|
||||
use crate::color::ExtendedColorType;
|
||||
use crate::error::{
|
||||
DecodingError, ImageError, ImageResult, UnsupportedError, UnsupportedErrorKind,
|
||||
};
|
||||
use crate::image::{self, ImageDecoder, ImageDecoderRect, ImageEncoder, ImageFormat};
|
||||
use crate::ColorType;
|
||||
|
||||
/// farbfeld Reader
|
||||
pub struct FarbfeldReader<R: Read> {
|
||||
|
|
@ -68,8 +68,8 @@ impl<R: Read> FarbfeldReader<R> {
|
|||
if crate::utils::check_dimension_overflow(
|
||||
reader.width,
|
||||
reader.height,
|
||||
// ColorType is always rgba16
|
||||
ColorType::Rgba16.bytes_per_pixel(),
|
||||
// ExtendedColorType is always rgba16
|
||||
8,
|
||||
) {
|
||||
return Err(ImageError::Unsupported(
|
||||
UnsupportedError::from_format_and_kind(
|
||||
|
|
@ -297,13 +297,13 @@ impl<W: Write> ImageEncoder for FarbfeldEncoder<W> {
|
|||
buf: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
color_type: ColorType,
|
||||
color_type: ExtendedColorType,
|
||||
) -> ImageResult<()> {
|
||||
if color_type != ColorType::Rgba16 {
|
||||
if color_type != ExtendedColorType::Rgba16 {
|
||||
return Err(ImageError::Unsupported(
|
||||
UnsupportedError::from_format_and_kind(
|
||||
ImageFormat::Farbfeld.into(),
|
||||
UnsupportedErrorKind::Color(color_type.into()),
|
||||
UnsupportedErrorKind::Color(color_type),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,8 +26,6 @@
|
|||
//! ```
|
||||
#![allow(clippy::while_let_loop)]
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::convert::TryInto;
|
||||
use std::io::{self, Cursor, Read, Write};
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
|
|
@ -46,6 +44,7 @@ use crate::error::{
|
|||
use crate::image::{AnimationDecoder, ImageDecoder, ImageFormat};
|
||||
use crate::io::Limits;
|
||||
use crate::traits::Pixel;
|
||||
use crate::ExtendedColorType;
|
||||
use crate::ImageBuffer;
|
||||
|
||||
/// GIF decoder
|
||||
|
|
@ -487,18 +486,18 @@ impl<W: Write> GifEncoder<W> {
|
|||
data: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
color: ColorType,
|
||||
color: ExtendedColorType,
|
||||
) -> ImageResult<()> {
|
||||
let (width, height) = self.gif_dimensions(width, height)?;
|
||||
match color {
|
||||
ColorType::Rgb8 => self.encode_gif(Frame::from_rgb(width, height, data)),
|
||||
ColorType::Rgba8 => {
|
||||
ExtendedColorType::Rgb8 => self.encode_gif(Frame::from_rgb(width, height, data)),
|
||||
ExtendedColorType::Rgba8 => {
|
||||
self.encode_gif(Frame::from_rgba(width, height, &mut data.to_owned()))
|
||||
}
|
||||
_ => Err(ImageError::Unsupported(
|
||||
UnsupportedError::from_format_and_kind(
|
||||
ImageFormat::Gif.into(),
|
||||
UnsupportedErrorKind::Color(color.into()),
|
||||
UnsupportedErrorKind::Color(color),
|
||||
),
|
||||
)),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,7 @@ use crate::Primitive;
|
|||
use num_traits::identities::Zero;
|
||||
#[cfg(test)]
|
||||
use std::borrow::Cow;
|
||||
use std::convert::TryFrom;
|
||||
use std::io::{self, BufRead, Cursor, Read, Seek};
|
||||
use std::iter::Iterator;
|
||||
use std::marker::PhantomData;
|
||||
use std::num::{ParseFloatError, ParseIntError};
|
||||
use std::path::Path;
|
||||
|
|
@ -1018,7 +1016,6 @@ pub fn read_raw_file<P: AsRef<Path>>(path: P) -> ::std::io::Result<Vec<Rgb<f32>>
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use std::io::Cursor;
|
||||
|
||||
#[test]
|
||||
fn dimension_overflow() {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
use byteorder::{LittleEndian, ReadBytesExt};
|
||||
use std::convert::TryFrom;
|
||||
use std::io::{Read, Seek, SeekFrom};
|
||||
use std::{error, fmt};
|
||||
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@ use byteorder::{LittleEndian, WriteBytesExt};
|
|||
use std::borrow::Cow;
|
||||
use std::io::{self, Write};
|
||||
|
||||
use crate::color::ColorType;
|
||||
use crate::error::{ImageError, ImageResult, ParameterError, ParameterErrorKind};
|
||||
use crate::image::ImageEncoder;
|
||||
|
||||
use crate::codecs::png::PngEncoder;
|
||||
use crate::ExtendedColorType;
|
||||
|
||||
// Enum value indicating an ICO image (as opposed to a CUR image):
|
||||
const ICO_IMAGE_TYPE: u16 = 1;
|
||||
|
|
@ -28,7 +28,7 @@ pub struct IcoFrame<'a> {
|
|||
width: u8,
|
||||
// Stored as `0 => 256, n => n`
|
||||
height: u8,
|
||||
color_type: ColorType,
|
||||
color_type: ExtendedColorType,
|
||||
}
|
||||
|
||||
impl<'a> IcoFrame<'a> {
|
||||
|
|
@ -39,7 +39,7 @@ impl<'a> IcoFrame<'a> {
|
|||
encoded_image: impl Into<Cow<'a, [u8]>>,
|
||||
width: u32,
|
||||
height: u32,
|
||||
color_type: ColorType,
|
||||
color_type: ExtendedColorType,
|
||||
) -> ImageResult<Self> {
|
||||
let encoded_image = encoded_image.into();
|
||||
|
||||
|
|
@ -72,7 +72,12 @@ impl<'a> IcoFrame<'a> {
|
|||
/// Construct a new `IcoFrame` by encoding `buf` as a PNG
|
||||
///
|
||||
/// The `width` and `height` must be between 1 and 256 (inclusive)
|
||||
pub fn as_png(buf: &[u8], width: u32, height: u32, color_type: ColorType) -> ImageResult<Self> {
|
||||
pub fn as_png(
|
||||
buf: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
color_type: ExtendedColorType,
|
||||
) -> ImageResult<Self> {
|
||||
let mut image_data: Vec<u8> = Vec::new();
|
||||
PngEncoder::new(&mut image_data).write_image(buf, width, height, color_type)?;
|
||||
|
||||
|
|
@ -136,10 +141,9 @@ impl<W: Write> ImageEncoder for IcoEncoder<W> {
|
|||
buf: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
color_type: ColorType,
|
||||
color_type: ExtendedColorType,
|
||||
) -> ImageResult<()> {
|
||||
let expected_buffer_len =
|
||||
(width as u64 * height as u64).saturating_mul(color_type.bytes_per_pixel() as u64);
|
||||
let expected_buffer_len = color_type.buffer_size(width, height);
|
||||
assert_eq!(
|
||||
expected_buffer_len,
|
||||
buf.len() as u64,
|
||||
|
|
@ -166,7 +170,7 @@ fn write_direntry<W: Write>(
|
|||
w: &mut W,
|
||||
width: u8,
|
||||
height: u8,
|
||||
color: ColorType,
|
||||
color: ExtendedColorType,
|
||||
data_start: u32,
|
||||
data_size: u32,
|
||||
) -> io::Result<()> {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
#![allow(clippy::too_many_arguments)]
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::convert::TryFrom;
|
||||
use std::io::{self, Write};
|
||||
|
||||
use crate::error::{
|
||||
|
|
@ -10,7 +9,7 @@ use crate::error::{
|
|||
};
|
||||
use crate::image::{ImageEncoder, ImageFormat};
|
||||
use crate::utils::clamp;
|
||||
use crate::{ColorType, GenericImageView, ImageBuffer, Luma, LumaA, Pixel, Rgb, Rgba};
|
||||
use crate::{ExtendedColorType, GenericImageView, ImageBuffer, Luma, LumaA, Pixel, Rgb, Rgba};
|
||||
|
||||
use super::entropy::build_huff_lut_const;
|
||||
use super::transform;
|
||||
|
|
@ -446,10 +445,9 @@ impl<W: Write> JpegEncoder<W> {
|
|||
image: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
color_type: ColorType,
|
||||
color_type: ExtendedColorType,
|
||||
) -> ImageResult<()> {
|
||||
let expected_buffer_len =
|
||||
(width as u64 * height as u64).saturating_mul(color_type.bytes_per_pixel() as u64);
|
||||
let expected_buffer_len = color_type.buffer_size(width, height);
|
||||
assert_eq!(
|
||||
expected_buffer_len,
|
||||
image.len() as u64,
|
||||
|
|
@ -458,22 +456,22 @@ impl<W: Write> JpegEncoder<W> {
|
|||
);
|
||||
|
||||
match color_type {
|
||||
ColorType::L8 => {
|
||||
ExtendedColorType::L8 => {
|
||||
let image: ImageBuffer<Luma<_>, _> =
|
||||
ImageBuffer::from_raw(width, height, image).unwrap();
|
||||
self.encode_image(&image)
|
||||
}
|
||||
ColorType::La8 => {
|
||||
ExtendedColorType::La8 => {
|
||||
let image: ImageBuffer<LumaA<_>, _> =
|
||||
ImageBuffer::from_raw(width, height, image).unwrap();
|
||||
self.encode_image(&image)
|
||||
}
|
||||
ColorType::Rgb8 => {
|
||||
ExtendedColorType::Rgb8 => {
|
||||
let image: ImageBuffer<Rgb<_>, _> =
|
||||
ImageBuffer::from_raw(width, height, image).unwrap();
|
||||
self.encode_image(&image)
|
||||
}
|
||||
ColorType::Rgba8 => {
|
||||
ExtendedColorType::Rgba8 => {
|
||||
let image: ImageBuffer<Rgba<_>, _> =
|
||||
ImageBuffer::from_raw(width, height, image).unwrap();
|
||||
self.encode_image(&image)
|
||||
|
|
@ -481,7 +479,7 @@ impl<W: Write> JpegEncoder<W> {
|
|||
_ => Err(ImageError::Unsupported(
|
||||
UnsupportedError::from_format_and_kind(
|
||||
ImageFormat::Jpeg.into(),
|
||||
UnsupportedErrorKind::Color(color_type.into()),
|
||||
UnsupportedErrorKind::Color(color_type),
|
||||
),
|
||||
)),
|
||||
}
|
||||
|
|
@ -579,7 +577,7 @@ impl<W: Write> JpegEncoder<W> {
|
|||
build_scan_header(&mut buf, &self.components[..num_components]);
|
||||
self.writer.write_segment(SOS, &buf)?;
|
||||
|
||||
if color_type.has_color() {
|
||||
if let ExtendedColorType::Rgb8 = color_type {
|
||||
self.encode_rgb(image)
|
||||
} else {
|
||||
self.encode_gray(image)
|
||||
|
|
@ -674,7 +672,7 @@ impl<W: Write> ImageEncoder for JpegEncoder<W> {
|
|||
buf: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
color_type: ColorType,
|
||||
color_type: ExtendedColorType,
|
||||
) -> ImageResult<()> {
|
||||
self.encode(buf, width, height, color_type)
|
||||
}
|
||||
|
|
@ -856,10 +854,9 @@ mod tests {
|
|||
#[cfg(feature = "benchmarks")]
|
||||
use test::Bencher;
|
||||
|
||||
use crate::color::ColorType;
|
||||
use crate::error::ParameterErrorKind::DimensionMismatch;
|
||||
use crate::image::ImageDecoder;
|
||||
use crate::{ImageEncoder, ImageError};
|
||||
use crate::{ExtendedColorType, ImageEncoder, ImageError};
|
||||
|
||||
use super::super::JpegDecoder;
|
||||
use super::{
|
||||
|
|
@ -888,7 +885,7 @@ mod tests {
|
|||
{
|
||||
let encoder = JpegEncoder::new_with_quality(&mut encoded_img, 100);
|
||||
encoder
|
||||
.write_image(&img, 1, 1, ColorType::Rgb8)
|
||||
.write_image(&img, 1, 1, ExtendedColorType::Rgb8)
|
||||
.expect("Could not encode image");
|
||||
}
|
||||
|
||||
|
|
@ -914,7 +911,7 @@ mod tests {
|
|||
{
|
||||
let encoder = JpegEncoder::new_with_quality(&mut encoded_img, 100);
|
||||
encoder
|
||||
.write_image(&img[..], 2, 2, ColorType::L8)
|
||||
.write_image(&img[..], 2, 2, ExtendedColorType::L8)
|
||||
.expect("Could not encode image");
|
||||
}
|
||||
|
||||
|
|
@ -964,7 +961,7 @@ mod tests {
|
|||
// Try to encode an image that is too large
|
||||
let mut encoded = Vec::new();
|
||||
let encoder = JpegEncoder::new_with_quality(&mut encoded, 100);
|
||||
let result = encoder.write_image(&img, 65_536, 1, ColorType::L8);
|
||||
let result = encoder.write_image(&img, 65_536, 1, ExtendedColorType::L8);
|
||||
match result {
|
||||
Err(ImageError::Parameter(err)) => {
|
||||
assert_eq!(err.kind(), DimensionMismatch)
|
||||
|
|
|
|||
|
|
@ -220,36 +220,14 @@ fn write_buffer(
|
|||
unaligned_bytes: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
color_type: ColorType,
|
||||
color_type: ExtendedColorType,
|
||||
) -> ImageResult<()> {
|
||||
let width = width as usize;
|
||||
let height = height as usize;
|
||||
|
||||
{
|
||||
// check whether the buffer is large enough for the specified dimensions
|
||||
let expected_byte_count = width
|
||||
.checked_mul(height)
|
||||
.and_then(|size| size.checked_mul(color_type.bytes_per_pixel() as usize));
|
||||
|
||||
// if the width and height does not match the length of the bytes, the arguments are invalid
|
||||
let has_invalid_size_or_overflowed = expected_byte_count
|
||||
.map(|expected_byte_count| unaligned_bytes.len() < expected_byte_count)
|
||||
// otherwise, size calculation overflowed, is bigger than memory,
|
||||
// therefore data is too small, so it is invalid.
|
||||
.unwrap_or(true);
|
||||
|
||||
if has_invalid_size_or_overflowed {
|
||||
return Err(ImageError::Encoding(EncodingError::new(
|
||||
ImageFormatHint::Exact(ImageFormat::OpenExr),
|
||||
"byte buffer not large enough for the specified dimensions and f32 pixels",
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
let bytes_per_pixel = color_type.bytes_per_pixel() as usize;
|
||||
let bytes_per_pixel = color_type.bits_per_pixel() as usize / 8;
|
||||
|
||||
match color_type {
|
||||
ColorType::Rgb32F => {
|
||||
ExtendedColorType::Rgb32F => {
|
||||
exr::prelude::Image // TODO compression method zip??
|
||||
::from_channels(
|
||||
(width, height),
|
||||
|
|
@ -270,7 +248,7 @@ fn write_buffer(
|
|||
.map_err(to_image_err)?;
|
||||
}
|
||||
|
||||
ColorType::Rgba32F => {
|
||||
ExtendedColorType::Rgba32F => {
|
||||
exr::prelude::Image // TODO compression method zip??
|
||||
::from_channels(
|
||||
(width, height),
|
||||
|
|
@ -333,10 +311,9 @@ where
|
|||
buf: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
color_type: ColorType,
|
||||
color_type: ExtendedColorType,
|
||||
) -> ImageResult<()> {
|
||||
let expected_buffer_len =
|
||||
(width as u64 * height as u64).saturating_mul(color_type.bytes_per_pixel() as u64);
|
||||
let expected_buffer_len = color_type.buffer_size(width, height);
|
||||
assert_eq!(
|
||||
expected_buffer_len,
|
||||
buf.len() as u64,
|
||||
|
|
@ -377,7 +354,7 @@ mod test {
|
|||
bytemuck::cast_slice(image.as_raw().as_slice()),
|
||||
image.width(),
|
||||
image.height(),
|
||||
ColorType::Rgb32F,
|
||||
ExtendedColorType::Rgb32F,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -390,7 +367,7 @@ mod test {
|
|||
bytemuck::cast_slice(image.as_raw().as_slice()),
|
||||
image.width(),
|
||||
image.height(),
|
||||
ColorType::Rgba32F,
|
||||
ExtendedColorType::Rgba32F,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
//! * <http://www.w3.org/TR/PNG/> - The PNG Specification
|
||||
//!
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::io::{Read, Write};
|
||||
|
||||
|
|
@ -500,7 +499,7 @@ pub enum FilterType {
|
|||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
enum BadPngRepresentation {
|
||||
ColorType(ColorType),
|
||||
ColorType(ExtendedColorType),
|
||||
}
|
||||
|
||||
impl<W: Write> PngEncoder<W> {
|
||||
|
|
@ -542,22 +541,22 @@ impl<W: Write> PngEncoder<W> {
|
|||
data: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
color: ColorType,
|
||||
color: ExtendedColorType,
|
||||
) -> ImageResult<()> {
|
||||
let (ct, bits) = match color {
|
||||
ColorType::L8 => (png::ColorType::Grayscale, png::BitDepth::Eight),
|
||||
ColorType::L16 => (png::ColorType::Grayscale, png::BitDepth::Sixteen),
|
||||
ColorType::La8 => (png::ColorType::GrayscaleAlpha, png::BitDepth::Eight),
|
||||
ColorType::La16 => (png::ColorType::GrayscaleAlpha, png::BitDepth::Sixteen),
|
||||
ColorType::Rgb8 => (png::ColorType::Rgb, png::BitDepth::Eight),
|
||||
ColorType::Rgb16 => (png::ColorType::Rgb, png::BitDepth::Sixteen),
|
||||
ColorType::Rgba8 => (png::ColorType::Rgba, png::BitDepth::Eight),
|
||||
ColorType::Rgba16 => (png::ColorType::Rgba, png::BitDepth::Sixteen),
|
||||
ExtendedColorType::L8 => (png::ColorType::Grayscale, png::BitDepth::Eight),
|
||||
ExtendedColorType::L16 => (png::ColorType::Grayscale, png::BitDepth::Sixteen),
|
||||
ExtendedColorType::La8 => (png::ColorType::GrayscaleAlpha, png::BitDepth::Eight),
|
||||
ExtendedColorType::La16 => (png::ColorType::GrayscaleAlpha, png::BitDepth::Sixteen),
|
||||
ExtendedColorType::Rgb8 => (png::ColorType::Rgb, png::BitDepth::Eight),
|
||||
ExtendedColorType::Rgb16 => (png::ColorType::Rgb, png::BitDepth::Sixteen),
|
||||
ExtendedColorType::Rgba8 => (png::ColorType::Rgba, png::BitDepth::Eight),
|
||||
ExtendedColorType::Rgba16 => (png::ColorType::Rgba, png::BitDepth::Sixteen),
|
||||
_ => {
|
||||
return Err(ImageError::Unsupported(
|
||||
UnsupportedError::from_format_and_kind(
|
||||
ImageFormat::Png.into(),
|
||||
UnsupportedErrorKind::Color(color.into()),
|
||||
UnsupportedErrorKind::Color(color),
|
||||
),
|
||||
))
|
||||
}
|
||||
|
|
@ -606,17 +605,16 @@ impl<W: Write> ImageEncoder for PngEncoder<W> {
|
|||
buf: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
color_type: ColorType,
|
||||
color_type: ExtendedColorType,
|
||||
) -> ImageResult<()> {
|
||||
use byteorder::{BigEndian, ByteOrder, NativeEndian};
|
||||
use ColorType::*;
|
||||
use ExtendedColorType::*;
|
||||
|
||||
let expected_bufffer_len =
|
||||
(width as u64 * height as u64).saturating_mul(color_type.bytes_per_pixel() as u64);
|
||||
let expected_buffer_len = color_type.buffer_size(width, height);
|
||||
assert_eq!(
|
||||
expected_bufffer_len,
|
||||
expected_buffer_len,
|
||||
buf.len() as u64,
|
||||
"Invalid buffer length: expected {expected_bufffer_len} got {} for {width}x{height} image",
|
||||
"Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image",
|
||||
buf.len(),
|
||||
);
|
||||
|
||||
|
|
@ -687,10 +685,7 @@ impl std::error::Error for BadPngRepresentation {}
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::image::ImageDecoder;
|
||||
use crate::ImageFormat;
|
||||
|
||||
use std::io::{Cursor, Read};
|
||||
use std::io::Cursor;
|
||||
|
||||
#[test]
|
||||
fn ensure_no_decoder_off_by_one() {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
use std::convert::TryFrom;
|
||||
use std::convert::TryInto;
|
||||
use std::error;
|
||||
use std::fmt::{self, Display};
|
||||
use std::io::{self, Read};
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use std::io::Write;
|
|||
use super::AutoBreak;
|
||||
use super::{ArbitraryHeader, ArbitraryTuplType, BitmapHeader, GraymapHeader, PixmapHeader};
|
||||
use super::{HeaderRecord, PnmHeader, PnmSubtype, SampleEncoding};
|
||||
use crate::color::{ColorType, ExtendedColorType};
|
||||
use crate::color::ExtendedColorType;
|
||||
use crate::error::{
|
||||
ImageError, ImageResult, ParameterError, ParameterErrorKind, UnsupportedError,
|
||||
UnsupportedErrorKind,
|
||||
|
|
@ -144,27 +144,20 @@ impl<W: Write> PnmEncoder<W> {
|
|||
image: S,
|
||||
width: u32,
|
||||
height: u32,
|
||||
color: ColorType,
|
||||
color: ExtendedColorType,
|
||||
) -> ImageResult<()>
|
||||
where
|
||||
S: Into<FlatSamples<'s>>,
|
||||
{
|
||||
let image = image.into();
|
||||
match self.header {
|
||||
HeaderStrategy::Dynamic => {
|
||||
self.write_dynamic_header(image, width, height, color.into())
|
||||
}
|
||||
HeaderStrategy::Dynamic => self.write_dynamic_header(image, width, height, color),
|
||||
HeaderStrategy::Subtype(subtype) => {
|
||||
self.write_subtyped_header(subtype, image, width, height, color.into())
|
||||
self.write_subtyped_header(subtype, image, width, height, color)
|
||||
}
|
||||
HeaderStrategy::Chosen(ref header) => {
|
||||
Self::write_with_header(&mut self.writer, header, image, width, height, color)
|
||||
}
|
||||
HeaderStrategy::Chosen(ref header) => Self::write_with_header(
|
||||
&mut self.writer,
|
||||
header,
|
||||
image,
|
||||
width,
|
||||
height,
|
||||
color.into(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -295,10 +288,9 @@ impl<W: Write> ImageEncoder for PnmEncoder<W> {
|
|||
buf: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
color_type: ColorType,
|
||||
color_type: ExtendedColorType,
|
||||
) -> ImageResult<()> {
|
||||
let expected_buffer_len =
|
||||
(width as u64 * height as u64).saturating_mul(color_type.bytes_per_pixel() as u64);
|
||||
let expected_buffer_len = color_type.buffer_size(width, height);
|
||||
assert_eq!(
|
||||
expected_buffer_len,
|
||||
buf.len() as u64,
|
||||
|
|
|
|||
|
|
@ -20,11 +20,11 @@ mod header;
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::color::ColorType;
|
||||
use crate::image::ImageDecoder;
|
||||
use crate::ExtendedColorType;
|
||||
use byteorder::{ByteOrder, NativeEndian};
|
||||
|
||||
fn execute_roundtrip_default(buffer: &[u8], width: u32, height: u32, color: ColorType) {
|
||||
fn execute_roundtrip_default(buffer: &[u8], width: u32, height: u32, color: ExtendedColorType) {
|
||||
let mut encoded_buffer = Vec::new();
|
||||
|
||||
{
|
||||
|
|
@ -47,7 +47,7 @@ mod tests {
|
|||
|
||||
assert_eq!(header.width(), width);
|
||||
assert_eq!(header.height(), height);
|
||||
assert_eq!(loaded_color, color);
|
||||
assert_eq!(ExtendedColorType::from(loaded_color), color);
|
||||
assert_eq!(loaded_image.as_slice(), buffer);
|
||||
}
|
||||
|
||||
|
|
@ -55,7 +55,7 @@ mod tests {
|
|||
buffer: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
color: ColorType,
|
||||
color: ExtendedColorType,
|
||||
subtype: PnmSubtype,
|
||||
) {
|
||||
let mut encoded_buffer = Vec::new();
|
||||
|
|
@ -81,11 +81,11 @@ mod tests {
|
|||
assert_eq!(header.width(), width);
|
||||
assert_eq!(header.height(), height);
|
||||
assert_eq!(header.subtype(), subtype);
|
||||
assert_eq!(loaded_color, color);
|
||||
assert_eq!(ExtendedColorType::from(loaded_color), color);
|
||||
assert_eq!(loaded_image.as_slice(), buffer);
|
||||
}
|
||||
|
||||
fn execute_roundtrip_u16(buffer: &[u16], width: u32, height: u32, color: ColorType) {
|
||||
fn execute_roundtrip_u16(buffer: &[u16], width: u32, height: u32, color: ExtendedColorType) {
|
||||
let mut encoded_buffer = Vec::new();
|
||||
|
||||
{
|
||||
|
|
@ -111,7 +111,7 @@ mod tests {
|
|||
|
||||
assert_eq!(header.width(), width);
|
||||
assert_eq!(header.height(), height);
|
||||
assert_eq!(loaded_color, color);
|
||||
assert_eq!(ExtendedColorType::from(loaded_color), color);
|
||||
assert_eq!(loaded_image, buffer_u8);
|
||||
}
|
||||
|
||||
|
|
@ -125,20 +125,20 @@ mod tests {
|
|||
255, 0, 0, 0,
|
||||
];
|
||||
|
||||
execute_roundtrip_default(&buf, 4, 4, ColorType::L8);
|
||||
execute_roundtrip_with_subtype(&buf, 4, 4, ColorType::L8, PnmSubtype::ArbitraryMap);
|
||||
execute_roundtrip_default(&buf, 4, 4, ExtendedColorType::L8);
|
||||
execute_roundtrip_with_subtype(&buf, 4, 4, ExtendedColorType::L8, PnmSubtype::ArbitraryMap);
|
||||
execute_roundtrip_with_subtype(
|
||||
&buf,
|
||||
4,
|
||||
4,
|
||||
ColorType::L8,
|
||||
ExtendedColorType::L8,
|
||||
PnmSubtype::Graymap(SampleEncoding::Ascii),
|
||||
);
|
||||
execute_roundtrip_with_subtype(
|
||||
&buf,
|
||||
4,
|
||||
4,
|
||||
ColorType::L8,
|
||||
ExtendedColorType::L8,
|
||||
PnmSubtype::Graymap(SampleEncoding::Binary),
|
||||
);
|
||||
}
|
||||
|
|
@ -157,20 +157,26 @@ mod tests {
|
|||
255, 255, 255,
|
||||
255, 255, 255,
|
||||
];
|
||||
execute_roundtrip_default(&buf, 3, 3, ColorType::Rgb8);
|
||||
execute_roundtrip_with_subtype(&buf, 3, 3, ColorType::Rgb8, PnmSubtype::ArbitraryMap);
|
||||
execute_roundtrip_default(&buf, 3, 3, ExtendedColorType::Rgb8);
|
||||
execute_roundtrip_with_subtype(
|
||||
&buf,
|
||||
3,
|
||||
3,
|
||||
ColorType::Rgb8,
|
||||
ExtendedColorType::Rgb8,
|
||||
PnmSubtype::ArbitraryMap,
|
||||
);
|
||||
execute_roundtrip_with_subtype(
|
||||
&buf,
|
||||
3,
|
||||
3,
|
||||
ExtendedColorType::Rgb8,
|
||||
PnmSubtype::Pixmap(SampleEncoding::Binary),
|
||||
);
|
||||
execute_roundtrip_with_subtype(
|
||||
&buf,
|
||||
3,
|
||||
3,
|
||||
ColorType::Rgb8,
|
||||
ExtendedColorType::Rgb8,
|
||||
PnmSubtype::Pixmap(SampleEncoding::Ascii),
|
||||
);
|
||||
}
|
||||
|
|
@ -179,6 +185,6 @@ mod tests {
|
|||
fn roundtrip_u16() {
|
||||
let buf: [u16; 6] = [0, 1, 0xFFFF, 0x1234, 0x3412, 0xBEAF];
|
||||
|
||||
execute_roundtrip_u16(&buf, 6, 1, ColorType::L16);
|
||||
execute_roundtrip_u16(&buf, 6, 1, ExtendedColorType::L16);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use crate::{
|
||||
error::{DecodingError, EncodingError},
|
||||
ColorType, ImageDecoder, ImageEncoder, ImageError, ImageFormat, ImageResult,
|
||||
ColorType, ExtendedColorType, ImageDecoder, ImageEncoder, ImageError, ImageFormat, ImageResult,
|
||||
};
|
||||
use std::io::{Read, Write};
|
||||
|
||||
|
|
@ -71,17 +71,19 @@ impl<W: Write> ImageEncoder for QoiEncoder<W> {
|
|||
buf: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
color_type: ColorType,
|
||||
color_type: ExtendedColorType,
|
||||
) -> ImageResult<()> {
|
||||
if !matches!(color_type, ColorType::Rgba8 | ColorType::Rgb8) {
|
||||
if !matches!(
|
||||
color_type,
|
||||
ExtendedColorType::Rgba8 | ExtendedColorType::Rgb8
|
||||
) {
|
||||
return Err(ImageError::Encoding(EncodingError::new(
|
||||
ImageFormat::Qoi.into(),
|
||||
format!("unsupported color type {color_type:?}. Supported are Rgba8 and Rgb8."),
|
||||
)));
|
||||
}
|
||||
|
||||
let expected_buffer_len =
|
||||
(width as u64 * height as u64).saturating_mul(color_type.bytes_per_pixel() as u64);
|
||||
let expected_buffer_len = color_type.buffer_size(width, height);
|
||||
assert_eq!(
|
||||
expected_buffer_len,
|
||||
buf.len() as u64,
|
||||
|
|
|
|||
|
|
@ -7,10 +7,7 @@ use crate::{
|
|||
image::{ImageDecoder, ImageFormat},
|
||||
};
|
||||
use byteorder::ReadBytesExt;
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
io::{self, Read, Seek},
|
||||
};
|
||||
use std::io::{self, Read, Seek};
|
||||
|
||||
struct ColorMap {
|
||||
/// sizes in bytes
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use super::header::Header;
|
||||
use crate::{
|
||||
codecs::tga::header::ImageType, error::EncodingError, ColorType, ImageEncoder, ImageError,
|
||||
ImageFormat, ImageResult,
|
||||
codecs::tga::header::ImageType, error::EncodingError, ExtendedColorType, ImageEncoder,
|
||||
ImageError, ImageFormat, ImageResult,
|
||||
};
|
||||
use std::{convert::TryFrom, error, fmt, io::Write};
|
||||
use std::{error, fmt, io::Write};
|
||||
|
||||
/// Errors that can occur during encoding and saving of a TGA image.
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
|
|
@ -85,10 +85,14 @@ impl<W: Write> TgaEncoder<W> {
|
|||
}
|
||||
|
||||
/// Writes the run-length encoded buffer to the writer
|
||||
fn run_length_encode(&mut self, image: &[u8], color_type: ColorType) -> ImageResult<()> {
|
||||
fn run_length_encode(
|
||||
&mut self,
|
||||
image: &[u8],
|
||||
color_type: ExtendedColorType,
|
||||
) -> ImageResult<()> {
|
||||
use PacketType::*;
|
||||
|
||||
let bytes_per_pixel = color_type.bytes_per_pixel();
|
||||
let bytes_per_pixel = color_type.bits_per_pixel() / 8;
|
||||
let capacity_in_bytes = usize::from(MAX_RUN_LENGTH) * usize::from(bytes_per_pixel);
|
||||
|
||||
// Buffer to temporarily store pixels
|
||||
|
|
@ -162,10 +166,9 @@ impl<W: Write> TgaEncoder<W> {
|
|||
buf: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
color_type: ColorType,
|
||||
color_type: ExtendedColorType,
|
||||
) -> ImageResult<()> {
|
||||
let expected_buffer_len =
|
||||
(width as u64 * height as u64).saturating_mul(color_type.bytes_per_pixel() as u64);
|
||||
let expected_buffer_len = color_type.buffer_size(width, height);
|
||||
assert_eq!(
|
||||
expected_buffer_len,
|
||||
buf.len() as u64,
|
||||
|
|
@ -192,10 +195,11 @@ impl<W: Write> TgaEncoder<W> {
|
|||
// Write run-length encoded image data
|
||||
|
||||
match color_type {
|
||||
ColorType::Rgb8 | ColorType::Rgba8 => {
|
||||
ExtendedColorType::Rgb8 | ExtendedColorType::Rgba8 => {
|
||||
let mut image = Vec::from(buf);
|
||||
|
||||
for pixel in image.chunks_mut(usize::from(color_type.bytes_per_pixel())) {
|
||||
for pixel in image.chunks_mut(usize::from(color_type.bits_per_pixel() / 8))
|
||||
{
|
||||
pixel.swap(0, 2);
|
||||
}
|
||||
|
||||
|
|
@ -210,10 +214,11 @@ impl<W: Write> TgaEncoder<W> {
|
|||
// Write uncompressed image data
|
||||
|
||||
match color_type {
|
||||
ColorType::Rgb8 | ColorType::Rgba8 => {
|
||||
ExtendedColorType::Rgb8 | ExtendedColorType::Rgba8 => {
|
||||
let mut image = Vec::from(buf);
|
||||
|
||||
for pixel in image.chunks_mut(usize::from(color_type.bytes_per_pixel())) {
|
||||
for pixel in image.chunks_mut(usize::from(color_type.bits_per_pixel() / 8))
|
||||
{
|
||||
pixel.swap(0, 2);
|
||||
}
|
||||
|
||||
|
|
@ -237,7 +242,7 @@ impl<W: Write> ImageEncoder for TgaEncoder<W> {
|
|||
buf: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
color_type: ColorType,
|
||||
color_type: ExtendedColorType,
|
||||
) -> ImageResult<()> {
|
||||
self.encode(buf, width, height, color_type)
|
||||
}
|
||||
|
|
@ -246,7 +251,7 @@ impl<W: Write> ImageEncoder for TgaEncoder<W> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{EncoderError, TgaEncoder};
|
||||
use crate::{codecs::tga::TgaDecoder, ColorType, ImageDecoder, ImageError};
|
||||
use crate::{codecs::tga::TgaDecoder, ExtendedColorType, ImageDecoder, ImageError};
|
||||
use std::{error::Error, io::Cursor};
|
||||
|
||||
#[test]
|
||||
|
|
@ -260,7 +265,7 @@ mod tests {
|
|||
// Try to encode an image that is too large
|
||||
let mut encoded = Vec::new();
|
||||
let encoder = TgaEncoder::new(&mut encoded);
|
||||
let result = encoder.encode(&img, dimension, 1, ColorType::L8);
|
||||
let result = encoder.encode(&img, dimension, 1, ExtendedColorType::L8);
|
||||
|
||||
match result {
|
||||
Err(ImageError::Encoding(err)) => {
|
||||
|
|
@ -290,7 +295,7 @@ mod tests {
|
|||
// Try to encode an image that is too large
|
||||
let mut encoded = Vec::new();
|
||||
let encoder = TgaEncoder::new(&mut encoded);
|
||||
let result = encoder.encode(&img, 1, dimension, ColorType::L8);
|
||||
let result = encoder.encode(&img, 1, dimension, ExtendedColorType::L8);
|
||||
|
||||
match result {
|
||||
Err(ImageError::Encoding(err)) => {
|
||||
|
|
@ -317,7 +322,7 @@ mod tests {
|
|||
let mut encoded_data = Vec::new();
|
||||
let encoder = TgaEncoder::new(&mut encoded_data).disable_rle();
|
||||
encoder
|
||||
.encode(&image, 5, 1, ColorType::Rgb8)
|
||||
.encode(&image, 5, 1, ExtendedColorType::Rgb8)
|
||||
.expect("could not encode image");
|
||||
|
||||
encoded_data
|
||||
|
|
@ -327,7 +332,7 @@ mod tests {
|
|||
let mut encoded_data = Vec::new();
|
||||
let encoder = TgaEncoder::new(&mut encoded_data);
|
||||
encoder
|
||||
.encode(&image, 5, 1, ColorType::Rgb8)
|
||||
.encode(&image, 5, 1, ExtendedColorType::Rgb8)
|
||||
.expect("could not encode image");
|
||||
|
||||
encoded_data
|
||||
|
|
@ -339,7 +344,12 @@ mod tests {
|
|||
mod compressed {
|
||||
use super::*;
|
||||
|
||||
fn round_trip_image(image: &[u8], width: u32, height: u32, c: ColorType) -> Vec<u8> {
|
||||
fn round_trip_image(
|
||||
image: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
c: ExtendedColorType,
|
||||
) -> Vec<u8> {
|
||||
let mut encoded_data = Vec::new();
|
||||
{
|
||||
let encoder = TgaEncoder::new(&mut encoded_data);
|
||||
|
|
@ -359,7 +369,7 @@ mod tests {
|
|||
let image = [
|
||||
255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
];
|
||||
let decoded = round_trip_image(&image, 5, 1, ColorType::Rgb8);
|
||||
let decoded = round_trip_image(&image, 5, 1, ExtendedColorType::Rgb8);
|
||||
assert_eq!(decoded.len(), image.len());
|
||||
assert_eq!(decoded.as_slice(), image);
|
||||
}
|
||||
|
|
@ -367,7 +377,7 @@ mod tests {
|
|||
#[test]
|
||||
fn round_trip_gray() {
|
||||
let image = [0, 1, 2];
|
||||
let decoded = round_trip_image(&image, 3, 1, ColorType::L8);
|
||||
let decoded = round_trip_image(&image, 3, 1, ExtendedColorType::L8);
|
||||
assert_eq!(decoded.len(), image.len());
|
||||
assert_eq!(decoded.as_slice(), image);
|
||||
}
|
||||
|
|
@ -375,7 +385,7 @@ mod tests {
|
|||
#[test]
|
||||
fn round_trip_graya() {
|
||||
let image = [0, 1, 2, 3, 4, 5];
|
||||
let decoded = round_trip_image(&image, 1, 3, ColorType::La8);
|
||||
let decoded = round_trip_image(&image, 1, 3, ExtendedColorType::La8);
|
||||
assert_eq!(decoded.len(), image.len());
|
||||
assert_eq!(decoded.as_slice(), image);
|
||||
}
|
||||
|
|
@ -383,7 +393,7 @@ mod tests {
|
|||
#[test]
|
||||
fn round_trip_single_pixel_rgb() {
|
||||
let image = [0, 1, 2];
|
||||
let decoded = round_trip_image(&image, 1, 1, ColorType::Rgb8);
|
||||
let decoded = round_trip_image(&image, 1, 1, ExtendedColorType::Rgb8);
|
||||
assert_eq!(decoded.len(), image.len());
|
||||
assert_eq!(decoded.as_slice(), image);
|
||||
}
|
||||
|
|
@ -391,7 +401,7 @@ mod tests {
|
|||
#[test]
|
||||
fn round_trip_three_pixel_rgb() {
|
||||
let image = [0, 1, 2, 0, 1, 2, 0, 1, 2];
|
||||
let decoded = round_trip_image(&image, 3, 1, ColorType::Rgb8);
|
||||
let decoded = round_trip_image(&image, 3, 1, ExtendedColorType::Rgb8);
|
||||
assert_eq!(decoded.len(), image.len());
|
||||
assert_eq!(decoded.as_slice(), image);
|
||||
}
|
||||
|
|
@ -399,7 +409,7 @@ mod tests {
|
|||
#[test]
|
||||
fn round_trip_3px_rgb() {
|
||||
let image = [0; 3 * 3 * 3]; // 3x3 pixels, 3 bytes per pixel
|
||||
let decoded = round_trip_image(&image, 3, 3, ColorType::Rgb8);
|
||||
let decoded = round_trip_image(&image, 3, 3, ExtendedColorType::Rgb8);
|
||||
assert_eq!(decoded.len(), image.len());
|
||||
assert_eq!(decoded.as_slice(), image);
|
||||
}
|
||||
|
|
@ -407,7 +417,7 @@ mod tests {
|
|||
#[test]
|
||||
fn round_trip_different() {
|
||||
let image = [0, 1, 2, 0, 1, 3, 0, 1, 4];
|
||||
let decoded = round_trip_image(&image, 3, 1, ColorType::Rgb8);
|
||||
let decoded = round_trip_image(&image, 3, 1, ExtendedColorType::Rgb8);
|
||||
assert_eq!(decoded.len(), image.len());
|
||||
assert_eq!(decoded.as_slice(), image);
|
||||
}
|
||||
|
|
@ -415,7 +425,7 @@ mod tests {
|
|||
#[test]
|
||||
fn round_trip_different_2() {
|
||||
let image = [0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 4];
|
||||
let decoded = round_trip_image(&image, 4, 1, ColorType::Rgb8);
|
||||
let decoded = round_trip_image(&image, 4, 1, ExtendedColorType::Rgb8);
|
||||
assert_eq!(decoded.len(), image.len());
|
||||
assert_eq!(decoded.as_slice(), image);
|
||||
}
|
||||
|
|
@ -423,7 +433,7 @@ mod tests {
|
|||
#[test]
|
||||
fn round_trip_different_3() {
|
||||
let image = [0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 4, 0, 1, 2];
|
||||
let decoded = round_trip_image(&image, 5, 1, ColorType::Rgb8);
|
||||
let decoded = round_trip_image(&image, 5, 1, ExtendedColorType::Rgb8);
|
||||
assert_eq!(decoded.len(), image.len());
|
||||
assert_eq!(decoded.as_slice(), image);
|
||||
}
|
||||
|
|
@ -436,7 +446,7 @@ mod tests {
|
|||
let (width, height) = (image.width(), image.height());
|
||||
let image = image.as_rgb8().unwrap().to_vec();
|
||||
|
||||
let decoded = round_trip_image(&image, width, height, ColorType::Rgb8);
|
||||
let decoded = round_trip_image(&image, width, height, ExtendedColorType::Rgb8);
|
||||
assert_eq!(decoded.len(), image.len());
|
||||
assert_eq!(decoded.as_slice(), image);
|
||||
}
|
||||
|
|
@ -445,7 +455,12 @@ mod tests {
|
|||
mod uncompressed {
|
||||
use super::*;
|
||||
|
||||
fn round_trip_image(image: &[u8], width: u32, height: u32, c: ColorType) -> Vec<u8> {
|
||||
fn round_trip_image(
|
||||
image: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
c: ExtendedColorType,
|
||||
) -> Vec<u8> {
|
||||
let mut encoded_data = Vec::new();
|
||||
{
|
||||
let encoder = TgaEncoder::new(&mut encoded_data).disable_rle();
|
||||
|
|
@ -464,7 +479,7 @@ mod tests {
|
|||
#[test]
|
||||
fn round_trip_single_pixel_rgb() {
|
||||
let image = [0, 1, 2];
|
||||
let decoded = round_trip_image(&image, 1, 1, ColorType::Rgb8);
|
||||
let decoded = round_trip_image(&image, 1, 1, ExtendedColorType::Rgb8);
|
||||
assert_eq!(decoded.len(), image.len());
|
||||
assert_eq!(decoded.as_slice(), image);
|
||||
}
|
||||
|
|
@ -472,7 +487,7 @@ mod tests {
|
|||
#[test]
|
||||
fn round_trip_single_pixel_rgba() {
|
||||
let image = [0, 1, 2, 3];
|
||||
let decoded = round_trip_image(&image, 1, 1, ColorType::Rgba8);
|
||||
let decoded = round_trip_image(&image, 1, 1, ExtendedColorType::Rgba8);
|
||||
assert_eq!(decoded.len(), image.len());
|
||||
assert_eq!(decoded.as_slice(), image);
|
||||
}
|
||||
|
|
@ -480,7 +495,7 @@ mod tests {
|
|||
#[test]
|
||||
fn round_trip_gray() {
|
||||
let image = [0, 1, 2];
|
||||
let decoded = round_trip_image(&image, 3, 1, ColorType::L8);
|
||||
let decoded = round_trip_image(&image, 3, 1, ExtendedColorType::L8);
|
||||
assert_eq!(decoded.len(), image.len());
|
||||
assert_eq!(decoded.as_slice(), image);
|
||||
}
|
||||
|
|
@ -488,7 +503,7 @@ mod tests {
|
|||
#[test]
|
||||
fn round_trip_graya() {
|
||||
let image = [0, 1, 2, 3, 4, 5];
|
||||
let decoded = round_trip_image(&image, 1, 3, ColorType::La8);
|
||||
let decoded = round_trip_image(&image, 1, 3, ExtendedColorType::La8);
|
||||
assert_eq!(decoded.len(), image.len());
|
||||
assert_eq!(decoded.as_slice(), image);
|
||||
}
|
||||
|
|
@ -496,7 +511,7 @@ mod tests {
|
|||
#[test]
|
||||
fn round_trip_3px_rgb() {
|
||||
let image = [0; 3 * 3 * 3]; // 3x3 pixels, 3 bytes per pixel
|
||||
let decoded = round_trip_image(&image, 3, 3, ColorType::Rgb8);
|
||||
let decoded = round_trip_image(&image, 3, 3, ExtendedColorType::Rgb8);
|
||||
assert_eq!(decoded.len(), image.len());
|
||||
assert_eq!(decoded.as_slice(), image);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
error::{UnsupportedError, UnsupportedErrorKind},
|
||||
ColorType, ImageError, ImageFormat, ImageResult,
|
||||
ExtendedColorType, ImageError, ImageFormat, ImageResult,
|
||||
};
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
use std::io::{Read, Write};
|
||||
|
|
@ -80,7 +80,7 @@ pub(crate) struct Header {
|
|||
impl Header {
|
||||
/// Load the header with values from pixel information.
|
||||
pub(crate) fn from_pixel_info(
|
||||
color_type: ColorType,
|
||||
color_type: ExtendedColorType,
|
||||
width: u16,
|
||||
height: u16,
|
||||
use_rle: bool,
|
||||
|
|
@ -89,19 +89,19 @@ impl Header {
|
|||
|
||||
if width > 0 && height > 0 {
|
||||
let (num_alpha_bits, other_channel_bits, image_type) = match (color_type, use_rle) {
|
||||
(ColorType::Rgba8, true) => (8, 24, ImageType::RunTrueColor),
|
||||
(ColorType::Rgb8, true) => (0, 24, ImageType::RunTrueColor),
|
||||
(ColorType::La8, true) => (8, 8, ImageType::RunGrayScale),
|
||||
(ColorType::L8, true) => (0, 8, ImageType::RunGrayScale),
|
||||
(ColorType::Rgba8, false) => (8, 24, ImageType::RawTrueColor),
|
||||
(ColorType::Rgb8, false) => (0, 24, ImageType::RawTrueColor),
|
||||
(ColorType::La8, false) => (8, 8, ImageType::RawGrayScale),
|
||||
(ColorType::L8, false) => (0, 8, ImageType::RawGrayScale),
|
||||
(ExtendedColorType::Rgba8, true) => (8, 24, ImageType::RunTrueColor),
|
||||
(ExtendedColorType::Rgb8, true) => (0, 24, ImageType::RunTrueColor),
|
||||
(ExtendedColorType::La8, true) => (8, 8, ImageType::RunGrayScale),
|
||||
(ExtendedColorType::L8, true) => (0, 8, ImageType::RunGrayScale),
|
||||
(ExtendedColorType::Rgba8, false) => (8, 24, ImageType::RawTrueColor),
|
||||
(ExtendedColorType::Rgb8, false) => (0, 24, ImageType::RawTrueColor),
|
||||
(ExtendedColorType::La8, false) => (8, 8, ImageType::RawGrayScale),
|
||||
(ExtendedColorType::L8, false) => (0, 8, ImageType::RawGrayScale),
|
||||
_ => {
|
||||
return Err(ImageError::Unsupported(
|
||||
UnsupportedError::from_format_and_kind(
|
||||
ImageFormat::Tga.into(),
|
||||
UnsupportedErrorKind::Color(color_type.into()),
|
||||
UnsupportedErrorKind::Color(color_type),
|
||||
),
|
||||
))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
extern crate tiff;
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::io::{self, Cursor, Read, Seek, Write};
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
|
|
@ -325,48 +324,53 @@ impl<W: Write + Seek> TiffEncoder<W> {
|
|||
///
|
||||
/// Panics if `width * height * color_type.bytes_per_pixel() != data.len()`.
|
||||
#[track_caller]
|
||||
pub fn encode(self, data: &[u8], width: u32, height: u32, color: ColorType) -> ImageResult<()> {
|
||||
let expected_buffer_len =
|
||||
(width as u64 * height as u64).saturating_mul(color.bytes_per_pixel() as u64);
|
||||
pub fn encode(
|
||||
self,
|
||||
buf: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
color_type: ExtendedColorType,
|
||||
) -> ImageResult<()> {
|
||||
let expected_buffer_len = color_type.buffer_size(width, height);
|
||||
assert_eq!(
|
||||
expected_buffer_len,
|
||||
data.len() as u64,
|
||||
buf.len() as u64,
|
||||
"Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image",
|
||||
data.len(),
|
||||
buf.len(),
|
||||
);
|
||||
|
||||
let mut encoder =
|
||||
tiff::encoder::TiffEncoder::new(self.w).map_err(ImageError::from_tiff_encode)?;
|
||||
match color {
|
||||
ColorType::L8 => {
|
||||
encoder.write_image::<tiff::encoder::colortype::Gray8>(width, height, data)
|
||||
match color_type {
|
||||
ExtendedColorType::L8 => {
|
||||
encoder.write_image::<tiff::encoder::colortype::Gray8>(width, height, buf)
|
||||
}
|
||||
ColorType::Rgb8 => {
|
||||
encoder.write_image::<tiff::encoder::colortype::RGB8>(width, height, data)
|
||||
ExtendedColorType::Rgb8 => {
|
||||
encoder.write_image::<tiff::encoder::colortype::RGB8>(width, height, buf)
|
||||
}
|
||||
ColorType::Rgba8 => {
|
||||
encoder.write_image::<tiff::encoder::colortype::RGBA8>(width, height, data)
|
||||
ExtendedColorType::Rgba8 => {
|
||||
encoder.write_image::<tiff::encoder::colortype::RGBA8>(width, height, buf)
|
||||
}
|
||||
ColorType::L16 => encoder.write_image::<tiff::encoder::colortype::Gray16>(
|
||||
ExtendedColorType::L16 => encoder.write_image::<tiff::encoder::colortype::Gray16>(
|
||||
width,
|
||||
height,
|
||||
u8_slice_as_u16(data)?,
|
||||
u8_slice_as_u16(buf)?,
|
||||
),
|
||||
ColorType::Rgb16 => encoder.write_image::<tiff::encoder::colortype::RGB16>(
|
||||
ExtendedColorType::Rgb16 => encoder.write_image::<tiff::encoder::colortype::RGB16>(
|
||||
width,
|
||||
height,
|
||||
u8_slice_as_u16(data)?,
|
||||
u8_slice_as_u16(buf)?,
|
||||
),
|
||||
ColorType::Rgba16 => encoder.write_image::<tiff::encoder::colortype::RGBA16>(
|
||||
ExtendedColorType::Rgba16 => encoder.write_image::<tiff::encoder::colortype::RGBA16>(
|
||||
width,
|
||||
height,
|
||||
u8_slice_as_u16(data)?,
|
||||
u8_slice_as_u16(buf)?,
|
||||
),
|
||||
_ => {
|
||||
return Err(ImageError::Unsupported(
|
||||
UnsupportedError::from_format_and_kind(
|
||||
ImageFormat::Tiff.into(),
|
||||
UnsupportedErrorKind::Color(color.into()),
|
||||
UnsupportedErrorKind::Color(color_type),
|
||||
),
|
||||
))
|
||||
}
|
||||
|
|
@ -384,7 +388,7 @@ impl<W: Write + Seek> ImageEncoder for TiffEncoder<W> {
|
|||
buf: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
color_type: ColorType,
|
||||
color_type: ExtendedColorType,
|
||||
) -> ImageResult<()> {
|
||||
self.encode(buf, width, height, color_type)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
use std::convert::TryFrom;
|
||||
use std::io::{Read, Seek};
|
||||
|
||||
use crate::buffer::ConvertBuffer;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use std::io::Write;
|
|||
|
||||
use crate::{
|
||||
error::{EncodingError, UnsupportedError, UnsupportedErrorKind},
|
||||
ColorType, ImageEncoder, ImageError, ImageFormat, ImageResult,
|
||||
ExtendedColorType, ImageEncoder, ImageError, ImageFormat, ImageResult,
|
||||
};
|
||||
|
||||
/// WebP Encoder.
|
||||
|
|
@ -30,33 +30,38 @@ impl<W: Write> WebPEncoder<W> {
|
|||
///
|
||||
/// Panics if `width * height * color.bytes_per_pixel() != data.len()`.
|
||||
#[track_caller]
|
||||
pub fn encode(self, data: &[u8], width: u32, height: u32, color: ColorType) -> ImageResult<()> {
|
||||
let expected_buffer_len =
|
||||
(width as u64 * height as u64).saturating_mul(color.bytes_per_pixel() as u64);
|
||||
pub fn encode(
|
||||
self,
|
||||
buf: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
color_type: ExtendedColorType,
|
||||
) -> ImageResult<()> {
|
||||
let expected_buffer_len = color_type.buffer_size(width, height);
|
||||
assert_eq!(
|
||||
expected_buffer_len,
|
||||
data.len() as u64,
|
||||
buf.len() as u64,
|
||||
"Invalid buffer length: expected {expected_buffer_len} got {} for {width}x{height} image",
|
||||
data.len(),
|
||||
buf.len(),
|
||||
);
|
||||
|
||||
let color = match color {
|
||||
ColorType::L8 => image_webp::ColorType::L8,
|
||||
ColorType::La8 => image_webp::ColorType::La8,
|
||||
ColorType::Rgb8 => image_webp::ColorType::Rgb8,
|
||||
ColorType::Rgba8 => image_webp::ColorType::Rgba8,
|
||||
let color_type = match color_type {
|
||||
ExtendedColorType::L8 => image_webp::ColorType::L8,
|
||||
ExtendedColorType::La8 => image_webp::ColorType::La8,
|
||||
ExtendedColorType::Rgb8 => image_webp::ColorType::Rgb8,
|
||||
ExtendedColorType::Rgba8 => image_webp::ColorType::Rgba8,
|
||||
_ => {
|
||||
return Err(ImageError::Unsupported(
|
||||
UnsupportedError::from_format_and_kind(
|
||||
ImageFormat::WebP.into(),
|
||||
UnsupportedErrorKind::Color(color.into()),
|
||||
UnsupportedErrorKind::Color(color_type),
|
||||
),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
self.inner
|
||||
.encode(data, width, height, color)
|
||||
.encode(buf, width, height, color_type)
|
||||
.map_err(ImageError::from_webp_encode)
|
||||
}
|
||||
}
|
||||
|
|
@ -68,7 +73,7 @@ impl<W: Write> ImageEncoder for WebPEncoder<W> {
|
|||
buf: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
color_type: ColorType,
|
||||
color_type: ExtendedColorType,
|
||||
) -> ImageResult<()> {
|
||||
self.encode(buf, width, height, color_type)
|
||||
}
|
||||
|
|
@ -97,7 +102,7 @@ mod tests {
|
|||
img.inner_pixels(),
|
||||
img.width(),
|
||||
img.height(),
|
||||
crate::ColorType::Rgba8,
|
||||
crate::ExtendedColorType::Rgba8,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
|
|
|||
40
src/color.rs
40
src/color.rs
|
|
@ -187,6 +187,46 @@ impl ExtendedColorType {
|
|||
| ExtendedColorType::Cmyk8 => 4,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of bits per pixel for this color type.
|
||||
pub fn bits_per_pixel(&self) -> u16 {
|
||||
match *self {
|
||||
ExtendedColorType::A8 => 8,
|
||||
ExtendedColorType::L1 => 1,
|
||||
ExtendedColorType::La1 => 2,
|
||||
ExtendedColorType::Rgb1 => 3,
|
||||
ExtendedColorType::Rgba1 => 4,
|
||||
ExtendedColorType::L2 => 2,
|
||||
ExtendedColorType::La2 => 4,
|
||||
ExtendedColorType::Rgb2 => 6,
|
||||
ExtendedColorType::Rgba2 => 8,
|
||||
ExtendedColorType::L4 => 4,
|
||||
ExtendedColorType::La4 => 8,
|
||||
ExtendedColorType::Rgb4 => 12,
|
||||
ExtendedColorType::Rgba4 => 16,
|
||||
ExtendedColorType::L8 => 8,
|
||||
ExtendedColorType::La8 => 16,
|
||||
ExtendedColorType::Rgb8 => 24,
|
||||
ExtendedColorType::Rgba8 => 32,
|
||||
ExtendedColorType::L16 => 16,
|
||||
ExtendedColorType::La16 => 32,
|
||||
ExtendedColorType::Rgb16 => 48,
|
||||
ExtendedColorType::Rgba16 => 64,
|
||||
ExtendedColorType::Rgb32F => 96,
|
||||
ExtendedColorType::Rgba32F => 128,
|
||||
ExtendedColorType::Bgr8 => 24,
|
||||
ExtendedColorType::Bgra8 => 32,
|
||||
ExtendedColorType::Cmyk8 => 32,
|
||||
ExtendedColorType::Unknown(bpp) => bpp as u16,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of bytes required to hold a width x height image of this color type.
|
||||
pub(crate) fn buffer_size(self, width: u32, height: u32) -> u64 {
|
||||
let bpp = self.bits_per_pixel() as u64;
|
||||
let row_pitch = (width as u64 * bpp + 7) / 8;
|
||||
row_pitch.saturating_mul(height as u64)
|
||||
}
|
||||
}
|
||||
impl From<ColorType> for ExtendedColorType {
|
||||
fn from(c: ColorType) -> Self {
|
||||
|
|
|
|||
|
|
@ -15,11 +15,11 @@ use crate::color::{self, IntoColor};
|
|||
use crate::error::{ImageError, ImageResult, ParameterError, ParameterErrorKind};
|
||||
use crate::flat::FlatSamples;
|
||||
use crate::image::{GenericImage, GenericImageView, ImageDecoder, ImageEncoder, ImageFormat};
|
||||
use crate::imageops;
|
||||
use crate::io::free_functions;
|
||||
use crate::math::resize_dimensions;
|
||||
use crate::traits::Pixel;
|
||||
use crate::{image, Luma, LumaA};
|
||||
use crate::{imageops, ExtendedColorType};
|
||||
use crate::{Rgb32FImage, Rgba32FImage};
|
||||
|
||||
/// A Dynamic Image
|
||||
|
|
@ -819,7 +819,7 @@ impl DynamicImage {
|
|||
pub fn write_to<W: Write + Seek>(&self, w: &mut W, format: ImageFormat) -> ImageResult<()> {
|
||||
let bytes = self.inner_bytes();
|
||||
let (width, height) = self.dimensions();
|
||||
let color = self.color();
|
||||
let color: ExtendedColorType = self.color().into();
|
||||
|
||||
// TODO do not repeat this match statement across the crate
|
||||
|
||||
|
|
@ -1108,18 +1108,15 @@ where
|
|||
///
|
||||
/// This will lead to corrupted files if the buffer contains malformed data. Currently only
|
||||
/// jpeg, png, ico, pnm, bmp, exr and tiff files are supported.
|
||||
pub fn save_buffer<P>(
|
||||
path: P,
|
||||
pub fn save_buffer(
|
||||
path: impl AsRef<Path>,
|
||||
buf: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
color: color::ColorType,
|
||||
) -> ImageResult<()>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
color: impl Into<ExtendedColorType>,
|
||||
) -> ImageResult<()> {
|
||||
// thin wrapper function to strip generics before calling save_buffer_impl
|
||||
free_functions::save_buffer_impl(path.as_ref(), buf, width, height, color)
|
||||
free_functions::save_buffer_impl(path.as_ref(), buf, width, height, color.into())
|
||||
}
|
||||
|
||||
/// Saves the supplied buffer to a file at the path specified
|
||||
|
|
@ -1130,19 +1127,23 @@ where
|
|||
/// This will lead to corrupted files if the buffer contains
|
||||
/// malformed data. Currently only jpeg, png, ico, bmp, exr and
|
||||
/// tiff files are supported.
|
||||
pub fn save_buffer_with_format<P>(
|
||||
path: P,
|
||||
pub fn save_buffer_with_format(
|
||||
path: impl AsRef<Path>,
|
||||
buf: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
color: color::ColorType,
|
||||
color: impl Into<ExtendedColorType>,
|
||||
format: ImageFormat,
|
||||
) -> ImageResult<()>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
) -> ImageResult<()> {
|
||||
// thin wrapper function to strip generics
|
||||
free_functions::save_buffer_with_format_impl(path.as_ref(), buf, width, height, color, format)
|
||||
free_functions::save_buffer_with_format_impl(
|
||||
path.as_ref(),
|
||||
buf,
|
||||
width,
|
||||
height,
|
||||
color.into(),
|
||||
format,
|
||||
)
|
||||
}
|
||||
|
||||
/// Writes the supplied buffer to a writer in the specified format.
|
||||
|
|
@ -1157,19 +1158,16 @@ where
|
|||
///
|
||||
/// Assumes the writer is buffered. In most cases,
|
||||
/// you should wrap your writer in a `BufWriter` for best performance.
|
||||
pub fn write_buffer_with_format<W>(
|
||||
pub fn write_buffer_with_format<W: Write + Seek>(
|
||||
buffered_writer: &mut W,
|
||||
buf: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
color: color::ColorType,
|
||||
color: impl Into<ExtendedColorType>,
|
||||
format: ImageFormat,
|
||||
) -> ImageResult<()>
|
||||
where
|
||||
W: Write + Seek,
|
||||
{
|
||||
) -> ImageResult<()> {
|
||||
// thin wrapper function to strip generics
|
||||
free_functions::write_buffer_impl(buffered_writer, buf, width, height, color, format)
|
||||
free_functions::write_buffer_impl(buffered_writer, buf, width, height, color.into(), format)
|
||||
}
|
||||
|
||||
/// Create a new image from a byte slice
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
#![allow(clippy::too_many_arguments)]
|
||||
use std::convert::TryFrom;
|
||||
use std::ffi::OsStr;
|
||||
use std::io::{self, Write};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
|
@ -770,7 +769,7 @@ pub trait ImageEncoder {
|
|||
buf: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
color_type: ColorType,
|
||||
color_type: ExtendedColorType,
|
||||
) -> ImageResult<()>;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -546,7 +546,7 @@ where
|
|||
mod test {
|
||||
|
||||
use super::*;
|
||||
use crate::{GrayImage, ImageBuffer};
|
||||
use crate::GrayImage;
|
||||
|
||||
macro_rules! assert_pixels_eq {
|
||||
($actual:expr, $expected:expr) => {{
|
||||
|
|
|
|||
|
|
@ -3,17 +3,14 @@ use std::io::{BufRead, BufWriter, Seek};
|
|||
use std::path::Path;
|
||||
use std::u32;
|
||||
|
||||
use crate::codecs::*;
|
||||
use crate::{codecs::*, ExtendedColorType};
|
||||
|
||||
use crate::dynimage::DynamicImage;
|
||||
use crate::error::{ImageError, ImageFormatHint, ImageResult};
|
||||
use crate::error::{UnsupportedError, UnsupportedErrorKind};
|
||||
use crate::image::ImageFormat;
|
||||
#[allow(unused_imports)] // When no features are supported
|
||||
use crate::image::{ImageDecoder, ImageEncoder};
|
||||
use crate::{
|
||||
color,
|
||||
error::{UnsupportedError, UnsupportedErrorKind},
|
||||
};
|
||||
|
||||
/// Create a new image from a Reader.
|
||||
///
|
||||
|
|
@ -36,7 +33,7 @@ pub(crate) fn save_buffer_impl(
|
|||
buf: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
color: color::ColorType,
|
||||
color: ExtendedColorType,
|
||||
) -> ImageResult<()> {
|
||||
let format = ImageFormat::from_path(path)?;
|
||||
save_buffer_with_format_impl(path, buf, width, height, color, format)
|
||||
|
|
@ -49,7 +46,7 @@ pub(crate) fn save_buffer_with_format_impl(
|
|||
buf: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
color: color::ColorType,
|
||||
color: ExtendedColorType,
|
||||
format: ImageFormat,
|
||||
) -> ImageResult<()> {
|
||||
let buffered_file_write = &mut BufWriter::new(File::create(path)?); // always seekable
|
||||
|
|
@ -63,7 +60,7 @@ pub(crate) fn write_buffer_impl<W: std::io::Write + Seek>(
|
|||
buf: &[u8],
|
||||
width: u32,
|
||||
height: u32,
|
||||
color: color::ColorType,
|
||||
color: ExtendedColorType,
|
||||
format: ImageFormat,
|
||||
) -> ImageResult<()> {
|
||||
match format {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
//! Input and output of images.
|
||||
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use crate::{error, ColorType, ImageError, ImageResult};
|
||||
|
||||
pub(crate) mod free_functions;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,10 @@
|
|||
use num_traits::{Bounded, Num, NumCast};
|
||||
use std::ops::AddAssign;
|
||||
|
||||
use crate::color::{ColorType, Luma, LumaA, Rgb, Rgba};
|
||||
use crate::{
|
||||
color::{Luma, LumaA, Rgb, Rgba},
|
||||
ExtendedColorType,
|
||||
};
|
||||
|
||||
/// Types which are safe to treat as an immutable byte slice in a pixel layout
|
||||
/// for image encoding.
|
||||
|
|
@ -173,40 +176,40 @@ pub trait PixelWithColorType: Pixel + self::private::SealedPixelWithColorType {
|
|||
/// such as `Rgb8`, `La16` or `Rgba32F`.
|
||||
/// This is needed for automatically detecting
|
||||
/// a color format when saving an image as a file.
|
||||
const COLOR_TYPE: ColorType;
|
||||
const COLOR_TYPE: ExtendedColorType;
|
||||
}
|
||||
|
||||
impl PixelWithColorType for Rgb<u8> {
|
||||
const COLOR_TYPE: ColorType = ColorType::Rgb8;
|
||||
const COLOR_TYPE: ExtendedColorType = ExtendedColorType::Rgb8;
|
||||
}
|
||||
impl PixelWithColorType for Rgb<u16> {
|
||||
const COLOR_TYPE: ColorType = ColorType::Rgb16;
|
||||
const COLOR_TYPE: ExtendedColorType = ExtendedColorType::Rgb16;
|
||||
}
|
||||
impl PixelWithColorType for Rgb<f32> {
|
||||
const COLOR_TYPE: ColorType = ColorType::Rgb32F;
|
||||
const COLOR_TYPE: ExtendedColorType = ExtendedColorType::Rgb32F;
|
||||
}
|
||||
|
||||
impl PixelWithColorType for Rgba<u8> {
|
||||
const COLOR_TYPE: ColorType = ColorType::Rgba8;
|
||||
const COLOR_TYPE: ExtendedColorType = ExtendedColorType::Rgba8;
|
||||
}
|
||||
impl PixelWithColorType for Rgba<u16> {
|
||||
const COLOR_TYPE: ColorType = ColorType::Rgba16;
|
||||
const COLOR_TYPE: ExtendedColorType = ExtendedColorType::Rgba16;
|
||||
}
|
||||
impl PixelWithColorType for Rgba<f32> {
|
||||
const COLOR_TYPE: ColorType = ColorType::Rgba32F;
|
||||
const COLOR_TYPE: ExtendedColorType = ExtendedColorType::Rgba32F;
|
||||
}
|
||||
|
||||
impl PixelWithColorType for Luma<u8> {
|
||||
const COLOR_TYPE: ColorType = ColorType::L8;
|
||||
const COLOR_TYPE: ExtendedColorType = ExtendedColorType::L8;
|
||||
}
|
||||
impl PixelWithColorType for Luma<u16> {
|
||||
const COLOR_TYPE: ColorType = ColorType::L16;
|
||||
const COLOR_TYPE: ExtendedColorType = ExtendedColorType::L16;
|
||||
}
|
||||
impl PixelWithColorType for LumaA<u8> {
|
||||
const COLOR_TYPE: ColorType = ColorType::La8;
|
||||
const COLOR_TYPE: ExtendedColorType = ExtendedColorType::La8;
|
||||
}
|
||||
impl PixelWithColorType for LumaA<u16> {
|
||||
const COLOR_TYPE: ColorType = ColorType::La16;
|
||||
const COLOR_TYPE: ExtendedColorType = ExtendedColorType::La16;
|
||||
}
|
||||
|
||||
/// Prevents down-stream users from implementing the `Primitive` trait
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
//! Compares the decoding results with reference renderings.
|
||||
use std::convert::TryInto;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue