1use dicom_toolkit_core::error::{DcmError, DcmResult};
7use dicom_toolkit_jpeg2000::{encode as j2k_encode, encode_htj2k as htj2k_encode, EncodeOptions};
8
9pub fn encode_jp2k(
24 pixels: &[u8],
25 width: u32,
26 height: u32,
27 bits_per_sample: u8,
28 samples_per_pixel: u8,
29 lossless: bool,
30) -> DcmResult<Vec<u8>> {
31 encode_with_mode(
32 pixels,
33 width,
34 height,
35 bits_per_sample,
36 samples_per_pixel,
37 lossless,
38 false,
39 )
40}
41
42pub fn encode_htj2k(
44 pixels: &[u8],
45 width: u32,
46 height: u32,
47 bits_per_sample: u8,
48 samples_per_pixel: u8,
49 lossless: bool,
50) -> DcmResult<Vec<u8>> {
51 encode_with_mode(
52 pixels,
53 width,
54 height,
55 bits_per_sample,
56 samples_per_pixel,
57 lossless,
58 true,
59 )
60}
61
62fn encode_with_mode(
63 pixels: &[u8],
64 width: u32,
65 height: u32,
66 bits_per_sample: u8,
67 samples_per_pixel: u8,
68 lossless: bool,
69 use_ht_block_coding: bool,
70) -> DcmResult<Vec<u8>> {
71 let codec_name = if use_ht_block_coding {
72 "HTJ2K"
73 } else {
74 "JPEG 2000"
75 };
76
77 if bits_per_sample == 0 || bits_per_sample > 16 {
78 return Err(DcmError::CompressionError {
79 reason: format!("{codec_name}: unsupported bit depth {bits_per_sample}"),
80 });
81 }
82
83 let bit_depth = bits_per_sample;
84
85 let max_levels = (width.min(height) as f64).log2().floor() as u8;
88 let num_levels = max_levels.min(5);
89
90 let options = EncodeOptions {
91 num_decomposition_levels: num_levels,
92 reversible: lossless,
93 guard_bits: if lossless { 1 } else { 2 },
94 use_ht_block_coding,
95 ..Default::default()
96 };
97
98 let encode_impl = if use_ht_block_coding {
99 htj2k_encode
100 } else {
101 j2k_encode
102 };
103
104 encode_impl(
105 pixels,
106 width,
107 height,
108 samples_per_pixel,
109 bit_depth,
110 false, &options,
112 )
113 .map_err(|e| DcmError::CompressionError {
114 reason: format!("{codec_name} encode error: {e}"),
115 })
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 fn assert_lossless_htj2k_roundtrip(
123 pixels: &[u8],
124 width: u32,
125 height: u32,
126 bits_per_sample: u8,
127 samples_per_pixel: u8,
128 ) {
129 let encoded = encode_htj2k(
130 pixels,
131 width,
132 height,
133 bits_per_sample,
134 samples_per_pixel,
135 true,
136 )
137 .expect("HTJ2K encode");
138 assert!(encoded.windows(2).any(|window| window == [0xFF, 0x50]));
139
140 let decoded = crate::jp2k::decoder::decode_jp2k(&encoded).expect("HTJ2K decode");
141 assert_eq!(decoded.width, width);
142 assert_eq!(decoded.height, height);
143 assert_eq!(decoded.bits_per_sample, bits_per_sample);
144 assert_eq!(decoded.components, samples_per_pixel);
145 assert_eq!(decoded.pixels, pixels);
146 }
147
148 fn gradient_u8(width: u32, height: u32) -> Vec<u8> {
149 let mut pixels = Vec::with_capacity((width * height) as usize);
150 for y in 0..height {
151 for x in 0..width {
152 pixels.push(((x * 17 + y * 31) % 256) as u8);
153 }
154 }
155 pixels
156 }
157
158 #[test]
159 fn encode_grayscale_8bit() {
160 let pixels: Vec<u8> = (0..64).collect();
161 let result = encode_jp2k(&pixels, 8, 8, 8, 1, true);
162 assert!(result.is_ok(), "encode failed: {:?}", result.err());
163 let encoded = result.unwrap();
164 assert!(encoded.len() > 4);
166 assert_eq!(encoded[0], 0xFF);
167 assert_eq!(encoded[1], 0x4F);
168 }
169
170 #[test]
171 fn encode_grayscale_16bit() {
172 let mut pixels = Vec::with_capacity(32);
174 for i in 0u16..16 {
175 pixels.extend_from_slice(&(i * 256).to_le_bytes());
176 }
177 let result = encode_jp2k(&pixels, 4, 4, 16, 1, true);
178 assert!(result.is_ok(), "encode failed: {:?}", result.err());
179 }
180
181 #[test]
182 fn encode_rgb_8bit() {
183 let pixels: Vec<u8> = (0..192).map(|i| (i & 0xFF) as u8).collect();
184 let result = encode_jp2k(&pixels, 8, 8, 8, 3, true);
185 assert!(result.is_ok(), "encode failed: {:?}", result.err());
186 }
187
188 #[test]
189 fn encode_lossy() {
190 let pixels: Vec<u8> = (0..64).collect();
191 let result = encode_jp2k(&pixels, 8, 8, 8, 1, false);
192 assert!(result.is_ok(), "encode failed: {:?}", result.err());
193 }
194
195 #[test]
196 fn encode_roundtrip_8bit() {
197 let original: Vec<u8> = (0..64).collect();
198 let encoded = encode_jp2k(&original, 8, 8, 8, 1, true).unwrap();
199
200 let decoded = crate::jp2k::decoder::decode_jp2k(&encoded).unwrap();
202 assert_eq!(decoded.width, 8);
203 assert_eq!(decoded.height, 8);
204 assert_eq!(decoded.bits_per_sample, 8);
205 assert_eq!(decoded.components, 1);
206 assert_eq!(decoded.pixels, original);
207 }
208
209 #[test]
210 fn encode_roundtrip_16bit() {
211 let mut original = Vec::with_capacity(32);
212 for i in 0u16..16 {
213 original.extend_from_slice(&(i * 257).to_le_bytes());
214 }
215
216 let encoded = encode_jp2k(&original, 4, 4, 16, 1, true).unwrap();
217 let decoded = crate::jp2k::decoder::decode_jp2k(&encoded).unwrap();
218
219 assert_eq!(decoded.width, 4);
220 assert_eq!(decoded.height, 4);
221 assert_eq!(decoded.bits_per_sample, 16);
222 assert_eq!(decoded.components, 1);
223 assert_eq!(decoded.pixels, original);
224 }
225
226 #[test]
227 fn encode_roundtrip_12bit_in_u16_container() {
228 let mut original = Vec::with_capacity(32);
229 for i in 0u16..16 {
230 original.extend_from_slice(&((i * 257) & 0x0FFF).to_le_bytes());
231 }
232
233 let encoded = encode_jp2k(&original, 4, 4, 12, 1, true).unwrap();
234 let decoded = crate::jp2k::decoder::decode_jp2k(&encoded).unwrap();
235
236 assert_eq!(decoded.width, 4);
237 assert_eq!(decoded.height, 4);
238 assert_eq!(decoded.bits_per_sample, 12);
239 assert_eq!(decoded.components, 1);
240 assert_eq!(decoded.pixels, original);
241 }
242
243 #[test]
244 fn encode_htj2k_roundtrip_12bit() {
245 let mut original = Vec::with_capacity(32);
246 for _ in 0..16 {
247 original.extend_from_slice(&2048u16.to_le_bytes());
248 }
249
250 let encoded = encode_htj2k(&original, 4, 4, 12, 1, true).unwrap();
251 assert!(encoded.windows(2).any(|window| window == [0xFF, 0x50]));
252
253 let decoded = crate::jp2k::decoder::decode_jp2k(&encoded).unwrap();
254 assert_eq!(decoded.width, 4);
255 assert_eq!(decoded.height, 4);
256 assert_eq!(decoded.bits_per_sample, 12);
257 assert_eq!(decoded.components, 1);
258 assert_eq!(decoded.pixels, original);
259 }
260
261 #[test]
262 fn encode_htj2k_lossless_roundtrip_varied_12bit() {
263 let mut original = Vec::with_capacity(32);
264 for i in 0u16..16 {
265 original.extend_from_slice(&((i * 257) & 0x0FFF).to_le_bytes());
266 }
267
268 let encoded = encode_htj2k(&original, 4, 4, 12, 1, true).unwrap();
269 let decoded = crate::jp2k::decoder::decode_jp2k(&encoded).unwrap();
270 assert_eq!(decoded.width, 4);
271 assert_eq!(decoded.height, 4);
272 assert_eq!(decoded.bits_per_sample, 12);
273 assert_eq!(decoded.components, 1);
274 assert_eq!(decoded.pixels, original);
275 }
276
277 #[test]
278 fn encode_htj2k_lossy_is_parseable() {
279 let pixels: Vec<u8> = (0..64).collect();
280 let encoded = encode_htj2k(&pixels, 8, 8, 8, 1, false).unwrap();
281 assert!(encoded.windows(2).any(|window| window == [0xFF, 0x50]));
282
283 let decoded = crate::jp2k::decoder::decode_jp2k(&encoded).unwrap();
284 assert_eq!(decoded.width, 8);
285 assert_eq!(decoded.height, 8);
286 assert_eq!(decoded.bits_per_sample, 8);
287 assert_eq!(decoded.components, 1);
288 }
289
290 #[test]
291 fn encode_htj2k_lossless_roundtrip_gradient_8bit() {
292 let gradient: Vec<u8> = (0..64).collect();
293 assert_lossless_htj2k_roundtrip(&gradient, 8, 8, 8, 1);
294 }
295
296 #[test]
297 fn encode_htj2k_lossless_roundtrip_large_16bit() {
298 let mut ramp = Vec::with_capacity(48 * 24 * 2);
299 for y in 0u16..24 {
300 for x in 0u16..48 {
301 let value = x * 521 + y * 997;
302 ramp.extend_from_slice(&value.to_le_bytes());
303 }
304 }
305
306 assert_lossless_htj2k_roundtrip(&ramp, 48, 24, 16, 1);
307 }
308
309 #[test]
310 fn encode_htj2k_lossy_large_gradient_has_stable_decode() {
311 let pixels = gradient_u8(128, 128);
312 let encoded = encode_htj2k(&pixels, 128, 128, 8, 1, false).expect("lossy HTJ2K encode");
313 assert!(encoded.windows(2).any(|window| window == [0xFF, 0x50]));
314 assert!(!encoded.is_empty());
315
316 let decoded = crate::jp2k::decoder::decode_jp2k(&encoded).expect("lossy HTJ2K decode");
317 assert_eq!(decoded.width, 128);
318 assert_eq!(decoded.height, 128);
319 assert_eq!(decoded.bits_per_sample, 8);
320 assert_eq!(decoded.components, 1);
321 }
322}