Skip to main content

appcore_filemaker/export/
raster_options.rs

1// =============================================================================
2//        #######
3//     ###       ###     F: raster_options.rs
4//    ##   ## ##   ##    P: AppCore-Runtime
5//         ## ##
6//                       C: 2026/09/07 12:00:00 by dnettoRaw
7//    ##   ## ##   ##    U: 2026/09/07 12:00:00 by dnettoRaw
8//      ###########      S: 1.0.2-rc
9// =============================================================================
10
11//! Explicit per-export raster working-surface limits.
12
13use std::io::Write;
14
15use crate::{
16    ErrorCode, ExportContext, ExportFormat, ExportOutcome, ExportRequest, FileMakerError,
17    OperationControl, ResolvedScene, Result,
18};
19
20/// Limits one RGBA working strip, not codec/assets/output or whole-process memory.
21#[derive(Clone, Copy, Debug, Eq, PartialEq)]
22pub struct RasterOptions {
23    max_strip_bytes: usize,
24    max_strip_rows: u32,
25}
26
27impl Default for RasterOptions {
28    fn default() -> Self {
29        Self {
30            max_strip_bytes: 4 * 1024 * 1024,
31            max_strip_rows: 256,
32        }
33    }
34}
35
36impl RasterOptions {
37    /// Creates limits from 1 byte through 64 MiB and from 1 through 4096 rows.
38    ///
39    /// The independent 4 MiB scanline ceiling still applies. A scanline that
40    /// cannot fit the selected strip-byte limit is rejected, never rounded up.
41    pub fn new(max_strip_bytes: usize, max_strip_rows: u32) -> Result<Self> {
42        if !(1..=64 * 1024 * 1024).contains(&max_strip_bytes)
43            || !(1..=4096).contains(&max_strip_rows)
44        {
45            return Err(FileMakerError::new(
46                ErrorCode::LimitExceeded,
47                "raster strip limits must be 1..=64 MiB and 1..=4096 rows",
48            ));
49        }
50        Ok(Self {
51            max_strip_bytes,
52            max_strip_rows,
53        })
54    }
55
56    /// Maximum RGBA bytes in one working strip.
57    #[must_use]
58    pub const fn max_strip_bytes(self) -> usize {
59        self.max_strip_bytes
60    }
61
62    /// Maximum rows in one working strip.
63    #[must_use]
64    pub const fn max_strip_rows(self) -> u32 {
65        self.max_strip_rows
66    }
67}
68
69/// Streams PNG/JPEG using explicit working-strip limits and cooperative control.
70///
71/// Shares validation, paint overrides and loss reporting with `export_controlled`.
72/// Other formats are rejected before writing. Strip size may change encoding
73/// chunks, not resolved layout; callers own any partial output after failure.
74pub fn export_raster_controlled(
75    scene: &ResolvedScene,
76    request: &ExportRequest,
77    context: &ExportContext<'_>,
78    options: RasterOptions,
79    control: &OperationControl,
80    writer: &mut dyn Write,
81) -> Result<ExportOutcome> {
82    if !matches!(request.format, ExportFormat::Png | ExportFormat::Jpeg) {
83        return Err(FileMakerError::new(
84            ErrorCode::ExportUnsupported,
85            "raster strip options require PNG or JPEG",
86        ));
87    }
88    super::core::export_with_control(
89        scene,
90        request,
91        context,
92        Some(control),
93        Some(options),
94        writer,
95    )
96}