1pub mod bio;
2pub mod dwt;
3pub mod error;
4pub mod htj2k;
5pub mod j2k;
6pub mod jp2;
7pub mod jp2_box;
8pub mod marker;
9pub mod mct;
10pub mod mqc;
11pub mod pi;
12pub mod quantize;
13pub mod simd;
14pub mod stream;
15pub mod t1;
16pub mod t2;
17pub mod tgt;
18pub mod tcd;
19pub mod types;
20
21pub use error::{Jp2Error, Result};
24pub use types::CodecFormat;
25
26use tcd::{TcdComponent, TcdParams};
27
28#[derive(Debug, Clone)]
30pub struct Image {
31 pub width: u32,
32 pub height: u32,
33 pub components: Vec<Component>,
34}
35
36#[derive(Debug, Clone)]
38pub struct Component {
39 pub data: Vec<i32>,
40 pub width: u32,
41 pub height: u32,
42 pub precision: u32,
43 pub signed: bool,
44 pub dx: u32,
45 pub dy: u32,
46}
47
48#[derive(Debug, Clone)]
50pub struct EncodeParams {
51 pub lossless: bool,
52 pub num_decomp_levels: u32,
53 pub cblk_width: u32,
54 pub cblk_height: u32,
55 pub format: CodecFormat,
56}
57
58impl Default for EncodeParams {
59 fn default() -> Self {
60 Self {
61 lossless: true,
62 num_decomp_levels: 5,
63 cblk_width: 64,
64 cblk_height: 64,
65 format: CodecFormat::Jp2,
66 }
67 }
68}
69
70pub fn decode(data: &[u8]) -> Result<Image> {
72 decode_with_reduce(data, 0)
73}
74
75pub fn decode_with_reduce(data: &[u8], reduce: u32) -> Result<Image> {
81 if data.len() < 4 {
82 return Err(Jp2Error::InvalidData(
83 "data too short to detect format".to_string(),
84 ));
85 }
86
87 let format = detect_format(data)?;
88
89 let (components_data, comp_info) = match format {
90 CodecFormat::Jp2 => {
91 if reduce > 0 {
92 jp2::jp2_decode(data)?
95 } else {
96 jp2::jp2_decode(data)?
97 }
98 }
99 CodecFormat::J2k => j2k::j2k_decode_with_reduce(data, reduce)?,
100 };
101
102 let width = comp_info[0].width;
104 let height = comp_info[0].height;
105
106 let components = components_data
107 .into_iter()
108 .zip(comp_info.iter())
109 .map(|(data, ci)| Component {
110 data,
111 width: ci.width,
112 height: ci.height,
113 precision: ci.precision,
114 signed: ci.signed,
115 dx: ci.dx,
116 dy: ci.dy,
117 })
118 .collect();
119
120 Ok(Image {
121 width,
122 height,
123 components,
124 })
125}
126
127pub fn decode_region(data: &[u8], x0: u32, y0: u32, x1: u32, y1: u32) -> Result<Image> {
138 if x0 >= x1 || y0 >= y1 {
139 return Err(Jp2Error::InvalidData(
140 "decode_region: empty region (x0 >= x1 or y0 >= y1)".to_string(),
141 ));
142 }
143
144 let full = decode(data)?;
146
147 if x1 > full.width || y1 > full.height {
148 return Err(Jp2Error::InvalidData(format!(
149 "decode_region: region ({x0},{y0})-({x1},{y1}) exceeds image bounds {}x{}",
150 full.width, full.height
151 )));
152 }
153
154 let region_w = x1 - x0;
155 let region_h = y1 - y0;
156
157 let components = full
158 .components
159 .iter()
160 .map(|comp| {
161 let cx0 = x0 / comp.dx;
163 let cy0 = y0 / comp.dy;
164 let cx1 = ((x1 + comp.dx - 1) / comp.dx).min(comp.width);
165 let cy1 = ((y1 + comp.dy - 1) / comp.dy).min(comp.height);
166 let cw = cx1 - cx0;
167 let ch = cy1 - cy0;
168
169 let mut region_data = vec![0i32; (cw * ch) as usize];
170 for y in 0..ch {
171 for x in 0..cw {
172 let src_idx = ((cy0 + y) * comp.width + (cx0 + x)) as usize;
173 let dst_idx = (y * cw + x) as usize;
174 region_data[dst_idx] = comp.data[src_idx];
175 }
176 }
177
178 Component {
179 data: region_data,
180 width: cw,
181 height: ch,
182 precision: comp.precision,
183 signed: comp.signed,
184 dx: comp.dx,
185 dy: comp.dy,
186 }
187 })
188 .collect();
189
190 Ok(Image {
191 width: region_w,
192 height: region_h,
193 components,
194 })
195}
196
197pub fn encode(image: &Image, params: &EncodeParams) -> Result<Vec<u8>> {
199 if image.components.is_empty() {
200 return Err(Jp2Error::InvalidData(
201 "image has no components".to_string(),
202 ));
203 }
204
205 let comp_info: Vec<TcdComponent> = image
206 .components
207 .iter()
208 .map(|c| TcdComponent {
209 width: c.width,
210 height: c.height,
211 precision: c.precision,
212 signed: c.signed,
213 dx: c.dx,
214 dy: c.dy,
215 })
216 .collect();
217
218 let components_data: Vec<Vec<i32>> = image
219 .components
220 .iter()
221 .map(|c| c.data.clone())
222 .collect();
223
224 let use_mct = image.components.len() >= 3;
225
226 let tcd_params = TcdParams {
227 num_res: params.num_decomp_levels + 1,
228 cblk_w: params.cblk_width,
229 cblk_h: params.cblk_height,
230 reversible: params.lossless,
231 num_layers: 1,
232 use_mct,
233 reduce: 0,
234 max_bytes: None,
235 };
236
237 match params.format {
238 CodecFormat::Jp2 => jp2::jp2_encode(&components_data, &comp_info, &tcd_params),
239 CodecFormat::J2k => j2k::j2k_encode(&components_data, &comp_info, &tcd_params),
240 }
241}
242
243fn detect_format(data: &[u8]) -> Result<CodecFormat> {
245 if data.len() < 4 {
246 return Err(Jp2Error::InvalidData(
247 "data too short to detect format".to_string(),
248 ));
249 }
250
251 if data.len() >= 12 {
254 let box_type = u32::from_be_bytes([data[4], data[5], data[6], data[7]]);
255 if box_type == jp2_box::JP2_JP {
256 return Ok(CodecFormat::Jp2);
257 }
258 }
259
260 if data[0] == 0xFF && data[1] == 0x4F {
262 return Ok(CodecFormat::J2k);
263 }
264
265 Err(Jp2Error::InvalidData(
266 "unrecognized format: not JP2 or J2K".to_string(),
267 ))
268}