jp2k/
lib.rs

1/*!
2
3# Rust bindings to OpenJPEG
4
5Supports loading JPEG2000 images into `image::DynamicImage`.
6
7Forked from https://framagit.org/leoschwarz/jpeg2000-rust before its GPL-v3 relicensing, with some additional features:
8
9* Specify decoding area and quality layers in addition to reduction factor
10* Improved OpenJPEG -> DynamicImage loading process
11* Get basic metadata from JPEG2000 headings
12* Docs (albeit minimal ones)
13
14This library brings its own libopenjpeg, which is statically linked. If you just need raw FFI bindings, see
15[openjpeg2-sys](https://crates.io/crates/openjpeg2-sys) or [openjpeg-sys](https://crates.io/crates/openjpeg-sys).
16
17
18## Usage
19
20```rust,no_run
21fn main() {
22    let bytes = include_bytes!("./rust-logo-512x512-blk.jp2");
23
24    let jp2k::Image(img) = jp2k::Image::from_bytes(
25        bytes,
26        jp2k::Codec::JP2,
27        Some(jp2k::DecodeParams::default().with_decoding_area(0, 0, 256, 256))
28    )
29    .unwrap();
30
31    let mut output = std::path::Path::new("examples/output/result.png");
32    let _ = img.save(&mut output);
33}
34```
35
36## Original warnings and license statement
37
38### Warning
39Please be advised that using C code means this crate is likely vulnerable to various memory exploits, e.g. see [http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-8332](CVE-2016-8332) for an actual example from the past.
40
41As soon as someone writes an efficient JPEG2000 decoder in pure Rust you should probably switch over to that.
42
43### License
44You can use the Rust code in the directories `src` and `openjp2-sys/src` under the terms of either the MIT license (`LICENSE-MIT` file) or the Apache license (`LICENSE-APACHE` file). Please note that this will link statically to OpenJPEG, which has its own license which you can find at `openjpeg-sys/libopenjpeg/LICENSE` (you might have to check out the git submodule first).
45*/
46
47pub mod err {
48    #[derive(Debug)]
49    pub enum Error {
50        NulError(std::ffi::NulError),
51        Io(std::io::Error),
52        Boxed(Box<dyn std::error::Error + Send + Sync>),
53    }
54
55    impl Error {
56        pub fn boxed<E: Into<Box<dyn std::error::Error + 'static + Send + Sync>>>(e: E) -> Self {
57            Error::Boxed(e.into())
58        }
59    }
60
61    impl std::fmt::Display for Error {
62        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63            use Error::*;
64            match self {
65                NulError(ref e) => {
66                    write!(f, "{}", e)?;
67                }
68                Io(ref e) => {
69                    write!(f, "{}", e)?;
70                }
71                Boxed(ref e) => {
72                    write!(f, "{}", e)?;
73                }
74            }
75
76            Ok(())
77        }
78    }
79
80    impl From<std::ffi::NulError> for Error {
81        fn from(t: std::ffi::NulError) -> Self {
82            Error::NulError(t)
83        }
84    }
85
86    impl std::error::Error for Error {}
87
88    pub type Result<T> = std::result::Result<T, Error>;
89}
90
91#[cfg(feature = "docs-rs")]
92#[path = "ffi.ref.rs"]
93mod ffi;
94
95#[cfg(not(feature = "docs-rs"))]
96mod ffi;
97
98use std::ffi::CString;
99use std::os::raw::c_void;
100use std::ptr::{self, NonNull};
101
102pub use ffi::{CODEC_FORMAT, COLOR_SPACE};
103
104struct InnerDecodeParams(ffi::opj_dparameters);
105
106impl Default for InnerDecodeParams {
107    fn default() -> Self {
108        let mut new = unsafe { std::mem::zeroed::<ffi::opj_dparameters>() };
109        unsafe {
110            ffi::opj_set_default_decoder_parameters(&mut new as *mut _);
111        }
112        InnerDecodeParams(new)
113    }
114}
115
116#[derive(Debug, Clone, Default)]
117struct DecodingArea {
118    x0: i32,
119    y0: i32,
120    x1: i32,
121    y1: i32,
122}
123
124/// Parameters used to decode JPEG2000 image
125#[derive(Debug, Clone, Default)]
126pub struct DecodeParams {
127    default_color_space: Option<COLOR_SPACE>,
128    reduce_factor: Option<u32>,
129    decoding_area: Option<DecodingArea>,
130    quality_layers: Option<u32>,
131    num_threads: Option<i32>,
132}
133
134impl DecodeParams {
135    /// Used when the library cannot determine color space
136    pub fn with_default_colorspace(mut self, color_space: COLOR_SPACE) -> Self {
137        self.default_color_space = Some(color_space);
138        self
139    }
140
141    /// Image will be "scaled" to dim / (2 ^ reduce_factor)
142    pub fn with_reduce_factor(mut self, reduce_factor: u32) -> Self {
143        self.reduce_factor = Some(reduce_factor);
144        self
145    }
146
147    pub fn with_num_threads(mut self, num: i32) -> Self {
148        self.num_threads = Some(num);
149        self
150    }
151
152    /// Image will be "cropped" to the specified decoding area, with width = x1 - x0 and height y1 - y0
153    pub fn with_decoding_area(mut self, x0: i32, y0: i32, x1: i32, y1: i32) -> Self {
154        self.decoding_area = Some(DecodingArea { x0, y0, x1, y1 });
155        self
156    }
157
158    /// Will only use the specified number of quality layers
159    pub fn with_quality_layers(mut self, quality_layers: u32) -> Self {
160        self.quality_layers = Some(quality_layers);
161        self
162    }
163
164    fn value_for_discard_level(u: u32, discard_level: u32) -> u32 {
165        let div = 1 << discard_level;
166        let quot = u / div;
167        let rem = u % div;
168        if rem > 0 {
169            quot + 1
170        } else {
171            quot
172        }
173    }
174}
175
176pub struct Stream(*mut ffi::opj_stream_t);
177
178impl Drop for Stream {
179    fn drop(&mut self) {
180        unsafe {
181            ffi::opj_stream_destroy(self.0);
182        }
183    }
184}
185
186impl Stream {
187    pub fn from_file<T: Into<Vec<u8>>>(file_name: T) -> err::Result<Self> {
188        let file_name = CString::new(file_name)?;
189        let ptr = unsafe { ffi::opj_stream_create_default_file_stream(file_name.as_ptr(), 1) };
190        Ok(Stream(ptr))
191    }
192
193    pub fn from_bytes(buf: &[u8]) -> err::Result<Self> {
194        #[derive(Debug)]
195        struct SliceWithOffset<'a> {
196            buf: &'a [u8],
197            offset: usize,
198        }
199
200        unsafe extern "C" fn opj_stream_free_user_data_fn(p_user_data: *mut c_void) {
201            drop(Box::from_raw(p_user_data as *mut SliceWithOffset))
202        }
203
204        unsafe extern "C" fn opj_stream_read_fn(
205            p_buffer: *mut c_void,
206            p_nb_bytes: usize,
207            p_user_data: *mut c_void,
208        ) -> usize {
209            if p_buffer.is_null() {
210                return 0;
211            }
212
213            let user_data = p_user_data as *mut SliceWithOffset;
214
215            let len = (&*user_data).buf.len();
216
217            let offset = (&*user_data).offset;
218
219            let bytes_left = len - offset;
220
221            let bytes_read = std::cmp::min(bytes_left, p_nb_bytes);
222
223            let slice = &(&*user_data).buf[offset..offset + bytes_read];
224
225            std::ptr::copy_nonoverlapping(slice.as_ptr(), p_buffer as *mut u8, bytes_read);
226
227            (*user_data).offset += bytes_read;
228
229            bytes_read
230        }
231
232        let buf_len = buf.len();
233        let user_data = Box::new(SliceWithOffset { buf, offset: 0 });
234
235        let ptr = unsafe {
236            let jp2_stream = ffi::opj_stream_default_create(1);
237            ffi::opj_stream_set_read_function(jp2_stream, Some(opj_stream_read_fn));
238            ffi::opj_stream_set_user_data_length(jp2_stream, buf_len as u64);
239            ffi::opj_stream_set_user_data(
240                jp2_stream,
241                Box::into_raw(user_data) as *mut c_void,
242                Some(opj_stream_free_user_data_fn),
243            );
244            jp2_stream
245        };
246
247        Ok(Stream(ptr))
248    }
249}
250
251pub struct Codec(NonNull<ffi::opj_codec_t>);
252
253impl Drop for Codec {
254    fn drop(&mut self) {
255        unsafe {
256            ffi::opj_destroy_codec(self.0.as_ptr());
257        }
258    }
259}
260
261impl Codec {
262    pub fn jp2() -> Self {
263        Self::create(CODEC_FORMAT::OPJ_CODEC_JP2).expect("Known format `JP2` should not fail")
264    }
265
266    pub fn create(format: CODEC_FORMAT) -> err::Result<Self> {
267        match NonNull::new(unsafe { ffi::opj_create_decompress(format) }) {
268            Some(ptr) => Ok(Codec(ptr)),
269            None => Err(err::Error::boxed("Setting up the decoder failed.")),
270        }
271    }
272}
273
274#[derive(Debug)]
275pub struct Info {
276    pub width: u32,
277    pub height: u32,
278}
279
280impl Info {
281    pub fn build(codec: Codec, stream: Stream) -> err::Result<Self> {
282        let mut params = InnerDecodeParams::default();
283
284        params.0.flags |= ffi::OPJ_DPARAMETERS_DUMP_FLAG;
285
286        if unsafe { ffi::opj_setup_decoder(codec.0.as_ptr(), &mut params.0) } != 1 {
287            return Err(err::Error::boxed("Setting up the decoder failed."));
288        }
289
290        let mut img = Image::new();
291
292        if unsafe { ffi::opj_read_header(stream.0, codec.0.as_ptr(), &mut img.0) } != 1 {
293            return Err(err::Error::boxed("Failed to read header."));
294        }
295
296        Ok(Info { width: img.width(), height: img.height() })
297    }
298}
299
300#[derive(Debug)]
301pub struct Image(pub *mut ffi::opj_image_t);
302
303impl Drop for Image {
304    fn drop(&mut self) {
305        unsafe {
306            ffi::opj_image_destroy(self.0);
307        }
308    }
309}
310
311impl Image {
312    fn new() -> Self {
313        Image(ptr::null_mut())
314    }
315
316    pub fn width(&self) -> u32 {
317        unsafe { (&*self.0).x1 - (&*self.0).x0 }
318    }
319
320    pub fn height(&self) -> u32 {
321        unsafe { (&*self.0).y1 - (&*self.0).y0 }
322    }
323
324    pub fn num_components(&self) -> u32 {
325        unsafe { (*self.0).numcomps }
326    }
327
328    pub fn components(&self) -> &[ffi::opj_image_comp_t] {
329        let comps_len = self.num_components();
330        unsafe { std::slice::from_raw_parts((*self.0).comps, comps_len as usize) }
331    }
332
333    pub fn factor(&self) -> u32 {
334        unsafe { (*(*self.0).comps).factor }
335    }
336
337    pub fn color_space(&self) -> COLOR_SPACE {
338        unsafe { (*self.0).color_space }
339    }
340}
341
342pub struct Component(*mut ffi::opj_image_comp_t);
343
344#[derive(Debug)]
345pub struct ImageBuffer {
346    pub buffer: Vec<u8>,
347    pub width: u32,
348    pub height: u32,
349    pub num_bands: usize,
350}
351
352impl ImageBuffer {
353    pub fn build(codec: Codec, stream: Stream, params: DecodeParams) -> err::Result<Self> {
354        let mut inner_params = InnerDecodeParams::default();
355
356        if let Some(reduce_factor) = params.reduce_factor {
357            inner_params.0.cp_reduce = reduce_factor;
358        }
359
360        if let Some(quality_layers) = params.quality_layers {
361            inner_params.0.cp_layer = quality_layers;
362        }
363
364        if unsafe { ffi::opj_setup_decoder(codec.0.as_ptr(), &mut inner_params.0) } != 1 {
365            return Err(err::Error::boxed("Setting up the decoder failed."));
366        }
367
368        if let Some(num_threads) = params.num_threads {
369            if unsafe { ffi::opj_codec_set_threads(codec.0.as_ptr(), num_threads) } != 1 {
370                return Err(err::Error::boxed("Could not set specified threads."));
371            }
372        }
373
374        let mut img = Image::new();
375
376        if unsafe { ffi::opj_read_header(stream.0, codec.0.as_ptr(), &mut img.0) } != 1 {
377            return Err(err::Error::boxed("Failed to read header."));
378        }
379
380        if let Some(DecodingArea { x0, y0, x1, y1 }) = params.decoding_area {
381            if unsafe { ffi::opj_set_decode_area(codec.0.as_ptr(), img.0, x0, y0, x1, y1) } != 1 {
382                return Err(err::Error::boxed("Setting up the decoding area failed."));
383            }
384        }
385
386        if unsafe { ffi::opj_decode(codec.0.as_ptr(), stream.0, img.0) } != 1 {
387            return Err(err::Error::boxed("Failed to read image."));
388        }
389
390        // if unsafe { ffi::opj_end_decompress(codec.0.as_ptr(), stream.0) } != 1 {
391        //     return Err(err::Error::boxed("Ending decoding failed."));
392        // }
393
394        drop(codec);
395        drop(stream);
396
397        let width = img.width();
398        let height = img.height();
399        let factor = img.factor();
400
401        let width = DecodeParams::value_for_discard_level(width, factor);
402        let height = DecodeParams::value_for_discard_level(height, factor);
403
404        let num_bands;
405
406        let buffer = unsafe {
407            match img.components() {
408                [comp_r] => {
409                    num_bands = 1;
410                    std::slice::from_raw_parts(comp_r.data, (width * height) as usize)
411                        .iter()
412                        .map(|x| *x as u8)
413                        .collect::<Vec<_>>()
414                }
415
416                [comp_r, comp_g, comp_b] => {
417                    let r = std::slice::from_raw_parts(comp_r.data, (width * height) as usize);
418                    let g = std::slice::from_raw_parts(comp_g.data, (width * height) as usize);
419                    let b = std::slice::from_raw_parts(comp_b.data, (width * height) as usize);
420
421                    num_bands = 3;
422
423                    let buffer = Vec::with_capacity((width * height * num_bands) as usize);
424
425                    r.iter().zip(g.iter()).zip(b.iter()).fold(buffer, |mut acc, ((r, g), b)| {
426                        acc.extend_from_slice(&[*r as u8, *g as u8, *b as u8]);
427                        acc
428                    })
429                }
430                [comp_r, comp_g, comp_b, comp_a] => {
431                    let r = std::slice::from_raw_parts(comp_r.data, (width * height) as usize);
432                    let g = std::slice::from_raw_parts(comp_g.data, (width * height) as usize);
433                    let b = std::slice::from_raw_parts(comp_b.data, (width * height) as usize);
434                    let a = std::slice::from_raw_parts(comp_a.data, (width * height) as usize);
435
436                    num_bands = 4;
437
438                    let buffer = Vec::with_capacity((width * height * num_bands) as usize);
439
440                    r.iter().zip(g.iter()).zip(b.iter()).zip(a.iter()).fold(
441                        buffer,
442                        |mut acc, (((r, g), b), a)| {
443                            acc.extend_from_slice(&[*r as u8, *g as u8, *b as u8, *a as u8]);
444                            acc
445                        },
446                    )
447                }
448                _ => {
449                    return Err(err::Error::boxed(
450                        "Operation not supported for that number of components",
451                    ));
452                }
453            }
454        };
455
456        Ok(ImageBuffer { buffer, width, height, num_bands: num_bands as usize })
457    }
458}