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
13 changes: 12 additions & 1 deletion crates/pdf-decode/src/indexed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,18 @@

use crate::error::DecodeError;

/// Expands indexed palette values into their base component bytes.
/// Expands indexed palette values into their base color component bytes.
///
/// Each value in `indices` selects one `base_components`-wide entry from
/// `lookup`. Indices above `hival` are clamped to `hival`, as required for PDF
/// indexed color spaces. The returned bytes contain the selected entries in
/// the same order as the input indices.
///
/// # Errors
///
/// Returns [`DecodeError::InvalidComponentCount`] when `base_components` is
/// zero. Returns [`DecodeError::PaletteLookupOutOfBounds`] when `lookup` does
/// not contain a complete entry for a selected index.
pub fn expand_indexed_values(
indices: &[u8],
lookup: &[u8],
Expand Down
38 changes: 21 additions & 17 deletions crates/pdf-decode/src/samples.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//! Sample code unpacking and normalization helpers.

use std::borrow::Cow;

use num_traits::ToPrimitive;
use pdf_utils::BitReader;

Expand Down Expand Up @@ -49,13 +51,14 @@ pub fn decode_sample_codes(

/// Decodes packed sample codes into byte-sized sample values.
///
/// Eight-bit samples use a direct copy path. Wider sample codes are decoded
/// normally and return an error when a value cannot fit in a byte.
pub fn decode_sample_bytes(
data: &[u8],
/// Eight-bit samples borrow directly from the input. Other sample sizes are
/// decoded into an owned buffer and return an error when a value cannot fit in
/// a byte.
pub fn decode_sample_bytes<'a>(
data: &'a [u8],
bits_per_sample: usize,
layout: SampleLayout,
) -> Result<Vec<u8>, DecodeError> {
) -> Result<Cow<'a, [u8]>, DecodeError> {
validate_bits_per_sample(bits_per_sample)?;

if bits_per_sample == 8 {
Expand All @@ -75,13 +78,14 @@ pub fn decode_sample_bytes(
expected_bytes: sample_count,
actual_bytes: data.len(),
})?;
return Ok(samples.to_vec());
return Ok(Cow::Borrowed(samples));
}

decode_sample_codes(data, bits_per_sample, layout)?
let samples = decode_sample_codes(data, bits_per_sample, layout)?
.into_iter()
.map(|sample| u8::try_from(sample).map_err(|_| DecodeError::InvalidSampleData))
.collect()
.collect::<Result<Vec<_>, _>>()?;
Ok(Cow::Owned(samples))
}

/// Decodes packed sample codes and normalizes them to the `0.0..=1.0` range.
Expand Down Expand Up @@ -368,14 +372,12 @@ mod tests {

#[test]
fn decode_contiguous_8_bit_sample_bytes() {
let samples = decode_sample_bytes(
&[0x12, 0x34, 0x56, 0x78],
8,
SampleLayout::Contiguous { sample_count: 3 },
)
.unwrap();
let data = [0x12, 0x34, 0x56, 0x78];
let samples =
decode_sample_bytes(&data, 8, SampleLayout::Contiguous { sample_count: 3 }).unwrap();

assert_eq!(samples, vec![0x12, 0x34, 0x56]);
assert!(matches!(samples, Cow::Borrowed(_)));
assert_eq!(samples.as_ref(), &data[..3]);
}

#[test]
Expand All @@ -391,7 +393,8 @@ mod tests {
)
.unwrap();

assert_eq!(samples, vec![0x10, 0x20, 0x30, 0x40]);
assert!(matches!(samples, Cow::Borrowed(_)));
assert_eq!(samples.as_ref(), &[0x10, 0x20, 0x30, 0x40]);
}

#[test]
Expand All @@ -407,7 +410,8 @@ mod tests {
)
.unwrap();

assert_eq!(samples, vec![1, 0, 1, 0, 1, 1]);
assert!(matches!(samples, Cow::Owned(_)));
assert_eq!(samples.as_ref(), &[1, 0, 1, 0, 1, 1]);
}

#[test]
Expand Down
18 changes: 9 additions & 9 deletions crates/pdf-image/src/image_xobject.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::sync::Arc;
use std::{borrow::Cow, sync::Arc};

use pdf_color_space::{color_space::ColorSpace, indexed_color_space::IndexedColorSpace};
use pdf_decode::{DecodeMap, SampleLayout, decode_sample_bytes};
use pdf_decode::{DecodeMap, SampleLayout, decode_sample_bytes, expand_indexed_values};
use pdf_filter::filter::{Filter, decode_data_with_resolver, decode_with_resolver};
use pdf_graphics::PixelFormat;
use pdf_object::{
Expand All @@ -11,7 +11,6 @@ use pdf_object::{

use crate::InlineImage;
use crate::error::PdfImageError;
use crate::indexed::expand_indexed_values_to_components;

/// Represents a PDF Image XObject, which is a self-contained raster image.
#[derive(Debug, Clone)]
Expand Down Expand Up @@ -329,9 +328,10 @@ impl ImageXObject {
let sample_codes = Self::decode_image_sample_codes(raw_data, 1, metadata)?;
let sample_max = Self::sample_max(metadata.bits_per_component)?;
let decode = DecodeMap::from_dictionary(dictionary, objects, 1, metadata.image_mask)?;
let decoded_indices = decode.apply_to_bytes(&sample_codes, sample_max, sample_max);
let decoded_indices = decode.apply_to_bytes(sample_codes.as_ref(), sample_max, sample_max);
let base_components = indexed.base.num_color_components();
let image_data = expand_indexed_values_to_components(

let image_data = expand_indexed_values(
&decoded_indices,
&indexed.lookup,
indexed.hival,
Expand Down Expand Up @@ -366,18 +366,18 @@ impl ImageXObject {
stored_color_space: metadata.color_space.clone(),
num_color_components: num_components,
image_data: decode.apply_to_bytes(
&sample_codes,
sample_codes.as_ref(),
Self::sample_max(metadata.bits_per_component)?,
255,
),
})
}

fn decode_image_sample_codes(
raw_data: &[u8],
fn decode_image_sample_codes<'a>(
raw_data: &'a [u8],
samples_per_pixel: usize,
metadata: &ImageMetadata,
) -> Result<Vec<u8>, PdfImageError> {
) -> Result<Cow<'a, [u8]>, PdfImageError> {
Ok(decode_sample_bytes(
raw_data,
metadata.bits_per_component,
Expand Down
168 changes: 0 additions & 168 deletions crates/pdf-image/src/indexed.rs

This file was deleted.

1 change: 0 additions & 1 deletion crates/pdf-image/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
pub mod error;
pub mod image_xobject;
pub mod indexed;
pub mod inline_image;

pub use error::PdfImageError;
Expand Down
Loading