Skip to main content

j2k_core/
buffer.rs

1// SPDX-License-Identifier: Apache-2.0
2
3use crate::{error::BufferError, pixel::PixelFormat};
4
5/// Default cap for host-side codec-owned allocations.
6pub const DEFAULT_MAX_HOST_ALLOCATION_BYTES: usize = 512 * 1024 * 1024;
7
8/// Returns `len` if it is at or below `cap`.
9pub fn ensure_allocation_within_cap(
10    len: usize,
11    cap: usize,
12    what: &'static str,
13) -> Result<usize, BufferError> {
14    if len > cap {
15        return Err(BufferError::AllocationTooLarge {
16            requested: len,
17            cap,
18            what,
19        });
20    }
21    Ok(len)
22}
23
24/// Returns the number of bytes required for a strided image output buffer.
25///
26/// The returned length covers the last written byte of the final row and does
27/// not include trailing padding after that row.
28pub fn strided_output_len(
29    dimensions: (u32, u32),
30    stride: usize,
31    fmt: PixelFormat,
32) -> Result<usize, BufferError> {
33    if dimensions.0 == 0 || dimensions.1 == 0 {
34        return Ok(0);
35    }
36
37    let row_bytes = row_bytes(dimensions.0, fmt)?;
38    stride
39        .checked_mul(dimensions.1 as usize - 1)
40        .and_then(|prefix| prefix.checked_add(row_bytes))
41        .ok_or(BufferError::SizeOverflow {
42            what: "strided output size",
43        })
44}
45
46/// Returns the strided output byte length, rejecting requests over `cap`.
47pub fn strided_output_len_capped(
48    dimensions: (u32, u32),
49    stride: usize,
50    fmt: PixelFormat,
51    cap: usize,
52    what: &'static str,
53) -> Result<usize, BufferError> {
54    let len = strided_output_len(dimensions, stride, fmt)?;
55    ensure_allocation_within_cap(len, cap, what)
56}
57
58/// Validates that `out_len` and `stride` can hold an image output.
59pub fn validate_strided_output_buffer(
60    dimensions: (u32, u32),
61    out_len: usize,
62    stride: usize,
63    fmt: PixelFormat,
64) -> Result<(), BufferError> {
65    if dimensions.0 == 0 || dimensions.1 == 0 {
66        return Ok(());
67    }
68
69    let row_bytes = row_bytes(dimensions.0, fmt)?;
70    if stride < row_bytes {
71        return Err(BufferError::StrideTooSmall { row_bytes, stride });
72    }
73    let required = strided_output_len(dimensions, stride, fmt)?;
74    if out_len < required {
75        return Err(BufferError::OutputTooSmall {
76            required,
77            have: out_len,
78        });
79    }
80    Ok(())
81}
82
83/// Copy tightly packed pixel rows into a caller-provided strided output buffer.
84///
85/// `src` must contain at least `width * height * fmt.bytes_per_pixel()` bytes.
86/// The destination may have row padding, expressed by `stride`.
87pub fn copy_tight_pixels_to_strided_output(
88    src: &[u8],
89    dimensions: (u32, u32),
90    fmt: PixelFormat,
91    out: &mut [u8],
92    stride: usize,
93) -> Result<(), BufferError> {
94    if dimensions.0 == 0 || dimensions.1 == 0 {
95        return Ok(());
96    }
97
98    let row_bytes = row_bytes(dimensions.0, fmt)?;
99    let height = dimensions.1 as usize;
100    let required_src = row_bytes
101        .checked_mul(height)
102        .ok_or(BufferError::SizeOverflow {
103            what: "tight source size",
104        })?;
105    if src.len() < required_src {
106        return Err(BufferError::InputTooSmall {
107            required: required_src,
108            have: src.len(),
109        });
110    }
111    validate_strided_output_buffer(dimensions, out.len(), stride, fmt)?;
112
113    for y in 0..dimensions.1 as usize {
114        let src_row = &src[y * row_bytes..(y + 1) * row_bytes];
115        let dst_start = y * stride;
116        out[dst_start..dst_start + row_bytes].copy_from_slice(src_row);
117    }
118
119    Ok(())
120}
121
122fn row_bytes(width: u32, fmt: PixelFormat) -> Result<usize, BufferError> {
123    (width as usize)
124        .checked_mul(fmt.bytes_per_pixel())
125        .ok_or(BufferError::SizeOverflow {
126            what: "row byte count",
127        })
128}