Skip to content

Commit 9f7c438

Browse files
More convenient Encoder API (image-rs#99)
1 parent 33e7f95 commit 9f7c438

File tree

5 files changed

+100
-147
lines changed

5 files changed

+100
-147
lines changed

benches/encode.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#![allow(unused)]
22

33
use criterion::{black_box, criterion_group, criterion_main, Criterion};
4-
use dds::{header::*, *};
4+
use dds::*;
55
use rand::Rng;
66

77
struct Image<T> {
@@ -100,8 +100,8 @@ where
100100

101101
let image = black_box(image);
102102

103-
let header = Header::new_image(image.size.width, image.size.height, format);
104-
let mut encoder = Encoder::new(black_box(&mut output), format, &header).unwrap();
103+
let mut encoder =
104+
Encoder::new_image(black_box(&mut output), image.size, format, false).unwrap();
105105
encoder.encoding = black_box(options).clone();
106106
let result = encoder.write_surface(black_box(image.view()));
107107
black_box(result).unwrap();
@@ -314,11 +314,9 @@ pub fn generate_mipmaps(c: &mut Criterion) {
314314

315315
let image = black_box(image);
316316

317-
let header =
318-
Header::new_image(image.width(), image.height(), format).with_mipmaps();
319317
let mut encoder =
320-
Encoder::new(black_box(&mut output), format, &header).unwrap();
321-
encoder.mipmaps.generate = true; // enable mipmap generation for this test
318+
Encoder::new_image(black_box(&mut output), image.size(), format, true)
319+
.unwrap();
322320
encoder.mipmaps.resize_filter = filter;
323321
let result = encoder.write_surface(image);
324322
black_box(result).unwrap();

src/encoder.rs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ pub struct Encoder<W> {
2525
pub encoding: EncodeOptions,
2626
/// Options regarding automatic mipmap generation.
2727
///
28-
/// Set `self.mipmaps.generate = true` to enable automatic mipmap generation.
28+
/// Set `self.mipmaps.generate = false` to disable automatic mipmap
29+
/// generation.
2930
///
3031
/// Default: `MipmapOptions::default()`
3132
pub mipmaps: MipmapOptions,
@@ -67,6 +68,31 @@ impl<W> Encoder<W> {
6768
})
6869
}
6970

71+
/// Creates a new encoder for a single image with the given size and format.
72+
///
73+
/// If `mipmaps` is `true`, a full mipmap chain will be declared in the
74+
/// header.
75+
///
76+
/// The header is created using [`Header::new_image`] and immediately
77+
/// written to the writer. For more control over the header, use
78+
/// [`Encoder::new`].
79+
pub fn new_image(
80+
writer: W,
81+
size: Size,
82+
format: Format,
83+
mipmaps: bool,
84+
) -> Result<Self, EncodingError>
85+
where
86+
W: Write,
87+
{
88+
let mut header = Header::new_image(size.width, size.height, format);
89+
if mipmaps {
90+
header = header.with_mipmaps();
91+
}
92+
93+
Self::new(writer, format, &header)
94+
}
95+
7096
/// The format of the pixel data.
7197
pub fn format(&self) -> Format {
7298
self.format
@@ -296,7 +322,7 @@ pub struct MipmapOptions {
296322
/// will **NOT** result in an error. Instead, the encoder will silently
297323
/// ignore the option and assume mipmap generation is disabled.
298324
///
299-
/// Default: `false`
325+
/// Default: `true`
300326
pub generate: bool,
301327
/// Whether the alpha channel (if any) is straight alpha transparency.
302328
///
@@ -323,7 +349,7 @@ pub struct MipmapOptions {
323349
impl Default for MipmapOptions {
324350
fn default() -> Self {
325351
Self {
326-
generate: false,
352+
generate: true,
327353
resize_straight_alpha: true,
328354
resize_filter: ResizeFilter::Box,
329355
}

src/lib.rs

Lines changed: 27 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -65,82 +65,64 @@
6565
//!
6666
//! ### Encoding
6767
//!
68-
//! Since the data of a DDS file is determined by the header, the first step to
69-
//! encoding a DDS file is to create a header. See the documentation of
70-
//! the [`dds::header`](crate::header) module for more details.
68+
//! [`Encoder`] defines the high-level interface for encoding DDS images.
69+
//!
70+
//! A single texture can be encoded as follows:
7171
//!
7272
//! ```no_run
73-
//! use dds::{*, header::*};
73+
//! use dds::*;
7474
//! use std::fs::File;
7575
//!
7676
//! fn save_rgba_image(
7777
//! file: &mut File,
7878
//! image_data: &[u8],
7979
//! width: u32,
8080
//! height: u32,
81+
//! mipmaps: bool,
8182
//! ) -> Result<(), EncodingError> {
82-
//! let format = Format::BC1_UNORM;
83-
//! let header = Header::new_image(width, height, format);
84-
//!
85-
//! let mut encoder = Encoder::new(file, format, &header)?;
83+
//! let size = Size::new(width, height);
84+
//! let mut encoder = Encoder::new_image(file, size, Format::BC7_UNORM, mipmaps)?;
85+
//! // lower quality for faster encoding
8686
//! encoder.encoding.quality = CompressionQuality::Fast;
8787
//!
88-
//! let view = ImageView::new(image_data, Size::new(width, height), ColorFormat::RGBA_U8)
88+
//! let view = ImageView::new(image_data, size, ColorFormat::RGBA_U8)
8989
//! .expect("invalid image data");
9090
//! encoder.write_surface(view)?;
9191
//! encoder.finish()?;
9292
//! Ok(())
9393
//! }
9494
//! ```
9595
//!
96-
//! Note the use of [`Encoder::finish()`]. This method will verify that the
97-
//! file has been created correctly and contains all necessary data. Always
98-
//! use [`Encoder::finish()`] instead of dropping the encoder.
99-
//!
100-
//! To create DDS files with mipmaps, we simply create a header with mipmaps and
101-
//! enable automatic mipmap generation in the encoder:
96+
//! Mipmaps are automatically generated if requested (by default). How mipmaps
97+
//! are generated can be configured using [`Encoder::mipmaps`].
10298
//!
103-
//! ```no_run
104-
//! use dds::{*, header::*};
105-
//! use std::fs::File;
99+
//! Most formats also support encoding options to change the encoded image data
100+
//! in some way. These can be set using the [`Encoder::encoding`] field. E.g.
101+
//! most formats support [dithering](EncodeOptions::dithering) and compressed
102+
//! formats support different [quality levels](EncodeOptions::quality) among
103+
//! others.
106104
//!
107-
//! fn save_rgba_image_with_mipmaps(
108-
//! file: &mut File,
109-
//! image_data: &[u8],
110-
//! width: u32,
111-
//! height: u32,
112-
//! ) -> Result<(), EncodingError> {
113-
//! let format = Format::BC1_UNORM;
114-
//! // Create a header with mipmaps
115-
//! let header = Header::new_image(width, height, format).with_mipmaps();
116-
//!
117-
//! let mut encoder = Encoder::new(file, format, &header)?;
118-
//! encoder.encoding.quality = CompressionQuality::Fast;
119-
//! encoder.mipmaps.generate = true; // Enable automatic mipmap generation
120-
//!
121-
//! let view = ImageView::new(image_data, Size::new(width, height), ColorFormat::RGBA_U8)
122-
//! .expect("invalid image data");
123-
//! encoder.write_surface(view)?;
124-
//! encoder.finish()?;
125-
//! Ok(())
126-
//! }
127-
//! ```
105+
//! Also note the use of [`Encoder::finish()`]. This method verifies that the
106+
//! DDS file contains all necessary data and is valid. ALWAYS use
107+
//! [`Encoder::finish()`] instead of dropping the encoder.
128108
//!
129-
//! Note: If the header does not specify mipmaps, no mipmaps will be generated
130-
//! even if automatic mipmap generation is enabled.
109+
//! #### Texture arrays, cube maps, and volumes
131110
//!
132-
//! For other types of data:
111+
//! Other types of data work slightly differently. You need to create a
112+
//! [`Header`](crate::header::Header) for them manually and pass it to
113+
//! [`Encoder::new`]. After the encoder has been created, the process is
114+
//! similar:
133115
//!
134116
//! - Texture arrays can be encoded using [`Encoder::write_surface`] for each
135117
//! texture in the array.
136118
//! - Cube maps, like texture arrays, can be encoded using [`Encoder::write_surface`]
137119
//! for each face. The order of the faces must be +X, -X, +Y, -Y, +Z, -Z.
138-
//! Writing whole cube maps at once is not supported.
139120
//! - Volumes can be encoded one depth slice at a time using
140121
//! [`Encoder::write_surface`].
141122
//!
142-
//! Automatic mipmap generation is **not** supported for volumes. If enabled,
143-
//! the options will be silently ignored and no mipmaps will be generated.
123+
//! Automatic mipmap generation is **not** supported for volumes. If automatic
124+
//! mipmap generation is enabled, it will silently do nothing. Use
125+
//! [`Encoder::finish()`] to prevent the creation of invalid DDS files.
144126
//!
145127
//! ### Progress reporting
146128
//!

0 commit comments

Comments
 (0)