zencodec_io/
encode_write.rs1use core::fmt;
5use std::io::{self, Write};
6
7use zencodec::UnsupportedOperation;
8use zencodec::encode::{AnimationFrameEncoder, Encoder, OutputTiming};
9use zenpixels::PixelSlice;
10
11pub fn write_pending<E: Encoder>(enc: &mut E, w: &mut impl Write) -> io::Result<usize> {
19 let n = enc.pending_output().len();
20 if n > 0 {
21 w.write_all(enc.pending_output())?;
22 enc.consume_output(n);
23 }
24 Ok(n)
25}
26
27pub fn write_pending_animation<A: AnimationFrameEncoder>(
30 enc: &mut A,
31 w: &mut impl Write,
32) -> io::Result<usize> {
33 let n = enc.pending_output().len();
34 if n > 0 {
35 w.write_all(enc.pending_output())?;
36 enc.consume_output(n);
37 }
38 Ok(n)
39}
40
41#[derive(Debug)]
43#[non_exhaustive]
44pub enum EncodeWriteError<E> {
45 Encoder(E),
47 Io(io::Error),
49}
50
51impl<E: fmt::Display> fmt::Display for EncodeWriteError<E> {
52 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 match self {
54 Self::Encoder(e) => write!(f, "encoder: {e}"),
55 Self::Io(e) => write!(f, "write: {e}"),
56 }
57 }
58}
59
60impl<E: core::error::Error + 'static> core::error::Error for EncodeWriteError<E> {
61 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
62 match self {
63 Self::Encoder(e) => Some(e),
64 Self::Io(e) => Some(e),
65 }
66 }
67}
68
69#[derive(Clone, Copy, Debug, PartialEq, Eq)]
71#[non_exhaustive]
72pub struct EncodeWriteReport {
73 pub timing: OutputTiming,
77 pub bytes: u64,
79}
80
81#[deprecated(
100 note = "Use row_encode::encode for policy-controlled rows, or Encoder::encode followed by writing its output for one-shot encoding"
101)]
102pub fn encode_to_writer<E: Encoder>(
103 mut enc: E,
104 pixels: PixelSlice<'_>,
105 w: &mut impl Write,
106) -> Result<EncodeWriteReport, EncodeWriteError<E::Error>> {
107 let (width, height, desc) = (pixels.width(), pixels.rows(), pixels.descriptor());
108 let timing = match enc.begin(width, height, desc) {
109 Ok(t) => t,
110 Err(e) if is_row_level_refusal(&e) => {
111 let out = enc.encode(pixels).map_err(EncodeWriteError::Encoder)?;
113 w.write_all(out.data()).map_err(EncodeWriteError::Io)?;
114 return Ok(EncodeWriteReport {
115 timing: OutputTiming::AT_FINISH,
116 bytes: out.len() as u64,
117 });
118 }
119 Err(e) => return Err(EncodeWriteError::Encoder(e)),
120 };
121 let mut bytes = write_pending(&mut enc, w).map_err(EncodeWriteError::Io)? as u64;
122 let unit = enc.preferred_strip_height().max(1);
123 let strip = unit * 16u32.div_ceil(unit);
124 let mut y = 0;
125 while y < height {
126 let h = strip.min(height - y);
127 let rows = pixels.sub_rows(y, h);
128 enc.push_rows(rows).map_err(EncodeWriteError::Encoder)?;
129 bytes += write_pending(&mut enc, w).map_err(EncodeWriteError::Io)? as u64;
130 y += h;
131 }
132 let rest = enc.finish().map_err(EncodeWriteError::Encoder)?;
133 w.write_all(rest.data()).map_err(EncodeWriteError::Io)?;
134 bytes += rest.len() as u64;
135 Ok(EncodeWriteReport { timing, bytes })
136}
137
138fn is_row_level_refusal<E: core::error::Error + 'static>(e: &E) -> bool {
139 use zencodec::CodecErrorExt as _;
140 match e.error_category() {
141 Some(category) => {
143 category
144 == zencodec::ErrorCategory::Request(zencodec::RequestError::Unsupported(
145 UnsupportedOperation::RowLevelEncode,
146 ))
147 }
148 None => e.unsupported_operation() == Some(&UnsupportedOperation::RowLevelEncode),
149 }
150}