Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions benches/encode.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#![allow(unused)]

use criterion::{black_box, criterion_group, criterion_main, Criterion};
use dds::{header::*, *};
use dds::*;
use rand::Rng;

struct Image<T> {
Expand Down Expand Up @@ -100,8 +100,8 @@ where

let image = black_box(image);

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

let image = black_box(image);

let header =
Header::new_image(image.width(), image.height(), format).with_mipmaps();
let mut encoder =
Encoder::new(black_box(&mut output), format, &header).unwrap();
encoder.mipmaps.generate = true; // enable mipmap generation for this test
Encoder::new_image(black_box(&mut output), image.size(), format, true)
.unwrap();
encoder.mipmaps.resize_filter = filter;
let result = encoder.write_surface(image);
black_box(result).unwrap();
Expand Down
32 changes: 29 additions & 3 deletions src/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ pub struct Encoder<W> {
pub encoding: EncodeOptions,
/// Options regarding automatic mipmap generation.
///
/// Set `self.mipmaps.generate = true` to enable automatic mipmap generation.
/// Set `self.mipmaps.generate = false` to disable automatic mipmap
/// generation.
///
/// Default: `MipmapOptions::default()`
pub mipmaps: MipmapOptions,
Expand Down Expand Up @@ -67,6 +68,31 @@ impl<W> Encoder<W> {
})
}

/// Creates a new encoder for a single image with the given size and format.
///
/// If `mipmaps` is `true`, a full mipmap chain will be declared in the
/// header.
///
/// The header is created using [`Header::new_image`] and immediately
/// written to the writer. For more control over the header, use
/// [`Encoder::new`].
pub fn new_image(
writer: W,
size: Size,
format: Format,
mipmaps: bool,
) -> Result<Self, EncodingError>
where
W: Write,
{
let mut header = Header::new_image(size.width, size.height, format);
if mipmaps {
header = header.with_mipmaps();
}

Self::new(writer, format, &header)
}

/// The format of the pixel data.
pub fn format(&self) -> Format {
self.format
Expand Down Expand Up @@ -296,7 +322,7 @@ pub struct MipmapOptions {
/// will **NOT** result in an error. Instead, the encoder will silently
/// ignore the option and assume mipmap generation is disabled.
///
/// Default: `false`
/// Default: `true`
pub generate: bool,
/// Whether the alpha channel (if any) is straight alpha transparency.
///
Expand All @@ -323,7 +349,7 @@ pub struct MipmapOptions {
impl Default for MipmapOptions {
fn default() -> Self {
Self {
generate: false,
generate: true,
resize_straight_alpha: true,
resize_filter: ResizeFilter::Box,
}
Expand Down
72 changes: 27 additions & 45 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,82 +65,64 @@
//!
//! ### Encoding
//!
//! Since the data of a DDS file is determined by the header, the first step to
//! encoding a DDS file is to create a header. See the documentation of
//! the [`dds::header`](crate::header) module for more details.
//! [`Encoder`] defines the high-level interface for encoding DDS images.
//!
//! A single texture can be encoded as follows:
//!
//! ```no_run
//! use dds::{*, header::*};
//! use dds::*;
//! use std::fs::File;
//!
//! fn save_rgba_image(
//! file: &mut File,
//! image_data: &[u8],
//! width: u32,
//! height: u32,
//! mipmaps: bool,
//! ) -> Result<(), EncodingError> {
//! let format = Format::BC1_UNORM;
//! let header = Header::new_image(width, height, format);
//!
//! let mut encoder = Encoder::new(file, format, &header)?;
//! let size = Size::new(width, height);
//! let mut encoder = Encoder::new_image(file, size, Format::BC7_UNORM, mipmaps)?;
//! // lower quality for faster encoding
//! encoder.encoding.quality = CompressionQuality::Fast;
//!
//! let view = ImageView::new(image_data, Size::new(width, height), ColorFormat::RGBA_U8)
//! let view = ImageView::new(image_data, size, ColorFormat::RGBA_U8)
//! .expect("invalid image data");
//! encoder.write_surface(view)?;
//! encoder.finish()?;
//! Ok(())
//! }
//! ```
//!
//! Note the use of [`Encoder::finish()`]. This method will verify that the
//! file has been created correctly and contains all necessary data. Always
//! use [`Encoder::finish()`] instead of dropping the encoder.
//!
//! To create DDS files with mipmaps, we simply create a header with mipmaps and
//! enable automatic mipmap generation in the encoder:
//! Mipmaps are automatically generated if requested (by default). How mipmaps
//! are generated can be configured using [`Encoder::mipmaps`].
//!
//! ```no_run
//! use dds::{*, header::*};
//! use std::fs::File;
//! Most formats also support encoding options to change the encoded image data
//! in some way. These can be set using the [`Encoder::encoding`] field. E.g.
//! most formats support [dithering](EncodeOptions::dithering) and compressed
//! formats support different [quality levels](EncodeOptions::quality) among
//! others.
//!
//! fn save_rgba_image_with_mipmaps(
//! file: &mut File,
//! image_data: &[u8],
//! width: u32,
//! height: u32,
//! ) -> Result<(), EncodingError> {
//! let format = Format::BC1_UNORM;
//! // Create a header with mipmaps
//! let header = Header::new_image(width, height, format).with_mipmaps();
//!
//! let mut encoder = Encoder::new(file, format, &header)?;
//! encoder.encoding.quality = CompressionQuality::Fast;
//! encoder.mipmaps.generate = true; // Enable automatic mipmap generation
//!
//! let view = ImageView::new(image_data, Size::new(width, height), ColorFormat::RGBA_U8)
//! .expect("invalid image data");
//! encoder.write_surface(view)?;
//! encoder.finish()?;
//! Ok(())
//! }
//! ```
//! Also note the use of [`Encoder::finish()`]. This method verifies that the
//! DDS file contains all necessary data and is valid. ALWAYS use
//! [`Encoder::finish()`] instead of dropping the encoder.
//!
//! Note: If the header does not specify mipmaps, no mipmaps will be generated
//! even if automatic mipmap generation is enabled.
//! #### Texture arrays, cube maps, and volumes
//!
//! For other types of data:
//! Other types of data work slightly differently. You need to create a
//! [`Header`](crate::header::Header) for them manually and pass it to
//! [`Encoder::new`]. After the encoder has been created, the process is
//! similar:
//!
//! - Texture arrays can be encoded using [`Encoder::write_surface`] for each
//! texture in the array.
//! - Cube maps, like texture arrays, can be encoded using [`Encoder::write_surface`]
//! for each face. The order of the faces must be +X, -X, +Y, -Y, +Z, -Z.
//! Writing whole cube maps at once is not supported.
//! - Volumes can be encoded one depth slice at a time using
//! [`Encoder::write_surface`].
//!
//! Automatic mipmap generation is **not** supported for volumes. If enabled,
//! the options will be silently ignored and no mipmaps will be generated.
//! Automatic mipmap generation is **not** supported for volumes. If automatic
//! mipmap generation is enabled, it will silently do nothing. Use
//! [`Encoder::finish()`] to prevent the creation of invalid DDS files.
//!
//! ### Progress reporting
//!
Expand Down
Loading
Loading