jpeg2k/
codec.rs

1use std::ffi::CStr;
2use std::os::raw::{c_char, c_void};
3use std::ptr;
4
5use log::{log_enabled, Level};
6
7use super::*;
8
9/// The area of the source image to decode.
10///
11/// This is useful for loading a small part of a
12/// very large image.
13///
14/// ```rust
15/// let area = DecodeArea::new(10, 10, 200, 200);
16///
17/// // or from a string:
18/// let area: DecodeArea = "10:10:200:200".parse()?;
19/// let area = DecodeArea::from_str("10:10:200:200")?;
20/// ```
21#[derive(Default, Clone, Copy)]
22pub struct DecodeArea {
23  start_x: u32,
24  start_y: u32,
25  end_x: u32,
26  end_y: u32,
27}
28
29impl std::str::FromStr for DecodeArea {
30  type Err = anyhow::Error;
31  fn from_str(s: &str) -> Result<Self, Self::Err> {
32    let dim = s
33      .splitn(4, ":")
34      .map(|v| v.parse::<u32>())
35      .collect::<Result<Vec<u32>, _>>()?;
36    Ok(Self {
37      start_x: dim.get(0).copied().unwrap_or(0),
38      start_y: dim.get(1).copied().unwrap_or(0),
39      end_x: dim.get(2).copied().unwrap_or(0),
40      end_y: dim.get(3).copied().unwrap_or(0),
41    })
42  }
43}
44
45impl DecodeArea {
46  pub fn new(start_x: u32, start_y: u32, end_x: u32, end_y: u32) -> Self {
47    Self {
48      start_x,
49      start_y,
50      end_x,
51      end_y,
52    }
53  }
54}
55
56#[derive(Clone)]
57pub struct DecodeParameters {
58  params: sys::opj_dparameters,
59  area: Option<DecodeArea>,
60  strict: bool,
61}
62
63impl Default for DecodeParameters {
64  fn default() -> Self {
65    let params = unsafe {
66      let mut ptr = std::mem::zeroed::<sys::opj_dparameters>();
67      sys::opj_set_default_decoder_parameters(&mut ptr as *mut _);
68      ptr
69    };
70    Self {
71      params,
72      area: Default::default(),
73      strict: false,
74    }
75  }
76}
77
78impl DecodeParameters {
79  pub fn new() -> Self {
80    Default::default()
81  }
82
83  /// How much to reduce the image's resolution.
84  ///
85  /// If `reduce == 0`, image is decoded to the full resolution.  This is the default.
86  /// If `reduce > 0`, then original dimension divided by 2^(reduce)
87  pub fn reduce(mut self, reduce: u32) -> Self {
88    self.params.cp_reduce = reduce;
89    self
90  }
91
92  /// Enable/disable strict decoing mode.
93  ///
94  /// If disabled then progressive downloading is supported (truncated codestreams).  This is the default.
95  /// If enabled then partial/truncated codestreams will return an error.
96  pub fn strict(mut self, strict: bool) -> Self {
97    self.strict = strict;
98    self
99  }
100
101  /// The number of quality layers to decode.
102  ///
103  /// If there are less quality layers than the specified number,
104  /// all the quality layers are decoded.
105  ///
106  /// If `layers == 0`, all the quality layers are decoded.  This is the default.
107  /// If `layers > 0`, then only the first `layers` layers are decoded.
108  pub fn layers(mut self, layers: u32) -> Self {
109    self.params.cp_layer = layers;
110    self
111  }
112
113  /// The area to decode.
114  ///
115  /// If `area == None`, then the whole image will be decoded.  This is the defult.
116  pub fn decode_area(mut self, area: Option<DecodeArea>) -> Self {
117    self.area = area;
118    self
119  }
120
121  /// Ignore palette colors, component mappings and color definitions.
122  pub fn ignore_pclr_cmap_cdef(mut self) -> Self {
123    self.params.flags |= sys::OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG;
124    self
125  }
126
127  pub(crate) fn as_ptr(&mut self) -> &mut sys::opj_dparameters {
128    &mut self.params
129  }
130}
131
132#[derive(Clone)]
133pub struct EncodeParameters(sys::opj_cparameters);
134
135impl Default for EncodeParameters {
136  fn default() -> Self {
137    Self(unsafe {
138      let mut ptr = std::mem::zeroed::<sys::opj_cparameters>();
139      sys::opj_set_default_encoder_parameters(&mut ptr as *mut _);
140      ptr
141    })
142  }
143}
144
145pub struct CodestreamTilePartIndex(pub(crate) sys::opj_tp_index_t);
146
147impl std::fmt::Debug for CodestreamTilePartIndex {
148  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149    f.debug_struct("CodestreamTilePartIndex")
150      .field("start_pos", &self.0.start_pos)
151      .field("end_header", &self.0.end_header)
152      .field("end_pos", &self.0.end_pos)
153      .finish()
154  }
155}
156
157pub struct CodestreamPacketInfo(pub(crate) sys::opj_packet_info_t);
158
159impl std::fmt::Debug for CodestreamPacketInfo {
160  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161    f.debug_struct("CodestreamPacketInfo")
162      .field("start_pos", &self.0.start_pos)
163      .field("end_ph_pos", &self.0.end_ph_pos)
164      .field("end_pos", &self.0.end_pos)
165      .field("disto", &self.0.disto)
166      .finish()
167  }
168}
169
170pub struct CodestreamMarker(pub(crate) sys::opj_marker_info_t);
171
172impl std::fmt::Debug for CodestreamMarker {
173  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
174    f.debug_struct("CodestreamMarker")
175      .field("type", &self.0.type_)
176      .field("pos", &self.0.pos)
177      .field("len", &self.0.len)
178      .finish()
179  }
180}
181
182pub struct TileCodingParamInfo(ptr::NonNull<sys::opj_tccp_info_t>);
183
184impl std::fmt::Debug for TileCodingParamInfo {
185  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
186    let info = self.as_ref();
187    f.debug_struct("TileCodingParamInfo")
188      .field("compno", &info.compno)
189      .field("csty", &info.csty)
190      .field("numresolutions", &info.numresolutions)
191      .field("cblkw", &info.cblkw)
192      .field("cblkh", &info.cblkh)
193      .field("cblksty", &info.cblksty)
194      .field("qmfbid", &info.qmfbid)
195      .field("qntsty", &info.qntsty)
196      .field("stepsizes_mant", &info.stepsizes_mant)
197      .field("stepsizes_expn", &info.stepsizes_expn)
198      .field("numgbits", &info.numgbits)
199      .field("roishift", &info.roishift)
200      .field("prcw", &info.prcw)
201      .field("prch", &info.prch)
202      .finish()
203  }
204}
205
206impl TileCodingParamInfo {
207  fn as_ref(&self) -> &sys::opj_tccp_info_t {
208    unsafe { &(*self.0.as_ref()) }
209  }
210}
211
212pub struct TileInfo<'a>(pub(crate) &'a sys::opj_tile_info_v2_t);
213
214impl<'a> std::fmt::Debug for TileInfo<'a> {
215  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
216    f.debug_struct("TileInfo")
217      .field("tileno", &self.0.tileno)
218      .field("csty", &self.0.csty)
219      .field("prg", &self.0.prg)
220      .field("numlayers", &self.0.numlayers)
221      .field("mct", &self.0.mct)
222      .field("tccp_info", &self.tccp_info())
223      .finish()
224  }
225}
226
227impl<'a> TileInfo<'a> {
228  fn tccp_info(&self) -> Option<TileCodingParamInfo> {
229    ptr::NonNull::new(self.0.tccp_info).map(|info| TileCodingParamInfo(info))
230  }
231}
232
233pub struct CodestreamTileIndex(pub(crate) sys::opj_tile_index_t);
234
235impl std::fmt::Debug for CodestreamTileIndex {
236  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
237    f.debug_struct("CodestreamTileIndex")
238      .field("tileno", &self.0.tileno)
239      .field("nb_tps", &self.0.nb_tps)
240      .field("current_nb_tps", &self.0.current_nb_tps)
241      .field("current_tpsno", &self.0.current_tpsno)
242      .field("tp_index", &self.tile_parts())
243      .field("marknum", &self.0.marknum)
244      .field("marker", &self.markers())
245      .field("maxmarknum", &self.0.maxmarknum)
246      .field("nb_packet", &self.0.nb_packet)
247      .field("packet_info", &self.packets())
248      .finish()
249  }
250}
251
252impl CodestreamTileIndex {
253  /// Tile part index.
254  pub fn tile_parts(&self) -> &[CodestreamTilePartIndex] {
255    let num = self.0.nb_tps;
256    unsafe {
257      std::slice::from_raw_parts(
258        self.0.tp_index as *mut CodestreamTilePartIndex,
259        num as usize,
260      )
261    }
262  }
263
264  /// Tile markers.
265  pub fn markers(&self) -> &[CodestreamMarker] {
266    let num = self.0.marknum;
267    unsafe { std::slice::from_raw_parts(self.0.marker as *mut CodestreamMarker, num as usize) }
268  }
269
270  /// Codestream packet info.
271  pub fn packets(&self) -> &[CodestreamPacketInfo] {
272    let num = self.0.nb_packet;
273    unsafe {
274      std::slice::from_raw_parts(
275        self.0.packet_index as *mut CodestreamPacketInfo,
276        num as usize,
277      )
278    }
279  }
280}
281
282pub struct CodestreamIndex(ptr::NonNull<sys::opj_codestream_index_t>);
283
284impl Drop for CodestreamIndex {
285  fn drop(&mut self) {
286    unsafe {
287      sys::opj_destroy_cstr_index(&mut self.0.as_ptr());
288    }
289  }
290}
291
292impl std::fmt::Debug for CodestreamIndex {
293  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
294    let idx = self.as_ref();
295    f.debug_struct("CodestreamIndex")
296      .field("main_head_start", &idx.main_head_start)
297      .field("main_head_end", &idx.main_head_end)
298      .field("codestream_size", &idx.codestream_size)
299      .field("marknum", &idx.marknum)
300      .field("marker", &self.markers())
301      .field("maxmarknum", &idx.maxmarknum)
302      .field("nb_of_tiles", &idx.nb_of_tiles)
303      .field("tile_index", &self.tile_indices())
304      .finish()
305  }
306}
307
308impl CodestreamIndex {
309  fn as_ref(&self) -> &sys::opj_codestream_index_t {
310    unsafe { &(*self.0.as_ref()) }
311  }
312
313  /// Codestream markers.
314  pub fn markers(&self) -> &[CodestreamMarker] {
315    let idx = self.as_ref();
316    let num = idx.marknum;
317    unsafe { std::slice::from_raw_parts(idx.marker as *mut CodestreamMarker, num as usize) }
318  }
319
320  /// Codestream tile indices.
321  pub fn tile_indices(&self) -> &[CodestreamTileIndex] {
322    let idx = self.as_ref();
323    let num = idx.nb_of_tiles;
324    unsafe { std::slice::from_raw_parts(idx.tile_index as *mut CodestreamTileIndex, num as usize) }
325  }
326}
327
328pub struct CodestreamInfo(ptr::NonNull<sys::opj_codestream_info_v2_t>);
329
330impl Drop for CodestreamInfo {
331  fn drop(&mut self) {
332    unsafe {
333      sys::opj_destroy_cstr_info(&mut self.0.as_ptr());
334    }
335  }
336}
337
338impl std::fmt::Debug for CodestreamInfo {
339  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
340    let info = self.as_ref();
341    let tile_info = if info.tile_info.is_null() {
342      TileInfo(&info.m_default_tile_info)
343    } else {
344      TileInfo(unsafe { &*info.tile_info })
345    };
346    f.debug_struct("CodestreamInfo")
347      .field("tx0", &info.tx0)
348      .field("ty0", &info.ty0)
349      .field("tdx", &info.tdx)
350      .field("tdy", &info.tdy)
351      .field("tw", &info.tw)
352      .field("th", &info.th)
353      .field("nbcomps", &info.nbcomps)
354      .field("tile_info", &tile_info)
355      .finish()
356  }
357}
358
359impl CodestreamInfo {
360  fn as_ref(&self) -> &sys::opj_codestream_info_v2_t {
361    unsafe { &(*self.0.as_ref()) }
362  }
363}
364
365pub(crate) struct Codec {
366  codec: ptr::NonNull<sys::opj_codec_t>,
367}
368
369impl Drop for Codec {
370  fn drop(&mut self) {
371    unsafe {
372      sys::opj_destroy_codec(self.codec.as_ptr());
373    }
374  }
375}
376
377extern "C" fn log_info(msg: *const c_char, _data: *mut c_void) {
378  unsafe {
379    log::info!("{:?}", CStr::from_ptr(msg).to_string_lossy());
380  }
381}
382
383extern "C" fn log_warn(msg: *const c_char, _data: *mut c_void) {
384  unsafe {
385    log::warn!("{:?}", CStr::from_ptr(msg).to_string_lossy());
386  }
387}
388
389extern "C" fn log_error(msg: *const c_char, _data: *mut c_void) {
390  unsafe {
391    log::error!("{:?}", CStr::from_ptr(msg).to_string_lossy());
392  }
393}
394
395impl Codec {
396  fn new(fmt: J2KFormat, is_decoder: bool) -> Result<Self> {
397    let format: sys::CODEC_FORMAT = fmt.into();
398    let ptr = unsafe {
399      if is_decoder {
400        ptr::NonNull::new(sys::opj_create_decompress(format))
401      } else {
402        ptr::NonNull::new(sys::opj_create_compress(format))
403      }
404    };
405    if let Some(ptr) = ptr {
406      let null = ptr::null_mut();
407      unsafe {
408        if log_enabled!(Level::Info) {
409          sys::opj_set_info_handler(ptr.as_ptr(), Some(log_info), null);
410        }
411        if log_enabled!(Level::Warn) {
412          sys::opj_set_warning_handler(ptr.as_ptr(), Some(log_warn), null);
413        }
414        sys::opj_set_error_handler(ptr.as_ptr(), Some(log_error), null);
415
416        #[cfg(feature = "threads")]
417        if sys::opj_has_thread_support() == 1 {
418          let num_cpus = sys::opj_get_num_cpus();
419          if sys::opj_codec_set_threads(ptr.as_ptr(), num_cpus) != 1 {
420            log::warn!("Failed to set number of threads: {:?}", num_cpus);
421          }
422        }
423      }
424
425      Ok(Self { codec: ptr })
426    } else {
427      Err(Error::CreateCodecError(format!(
428        "Codec not supported: {:?}",
429        fmt
430      )))
431    }
432  }
433
434  pub(crate) fn as_ptr(&self) -> *mut sys::opj_codec_t {
435    self.codec.as_ptr()
436  }
437}
438
439pub(crate) struct Decoder<'a> {
440  codec: Codec,
441  stream: Stream<'a>,
442}
443
444impl<'a> Decoder<'a> {
445  pub(crate) fn new(stream: Stream<'a>) -> Result<Self> {
446    assert!(stream.is_input());
447    let fmt = stream.format();
448    let codec = Codec::new(fmt, true)?;
449    Ok(Self { codec, stream })
450  }
451
452  #[cfg(feature = "strict-mode")]
453  fn set_strict_mode(&self, mode: bool) -> Result<()> {
454    let res = unsafe { sys::opj_decoder_set_strict_mode(self.as_ptr(), mode as i32) == 1 };
455    if res {
456      Ok(())
457    } else {
458      Err(Error::CreateCodecError(format!(
459        "Failed to set strict mode on decoder."
460      )))
461    }
462  }
463
464  #[cfg(not(feature = "strict-mode"))]
465  fn set_strict_mode(&self, _mode: bool) -> Result<()> {
466    Ok(())
467  }
468
469  pub(crate) fn setup(&self, params: &mut DecodeParameters) -> Result<()> {
470    let res = unsafe { sys::opj_setup_decoder(self.as_ptr(), params.as_ptr()) == 1 };
471    if res {
472      self.set_strict_mode(params.strict)?;
473      Ok(())
474    } else {
475      Err(Error::CreateCodecError(format!(
476        "Failed to setup decoder with parameters."
477      )))
478    }
479  }
480
481  pub(crate) fn read_header(&self) -> Result<Image> {
482    let mut img: *mut sys::opj_image_t = ptr::null_mut();
483
484    let res = unsafe { sys::opj_read_header(self.stream.as_ptr(), self.as_ptr(), &mut img) };
485    // Try wrapping the image pointer before handling any errors.
486    // Since the read header function might have allocated the image structure.
487    let img = Image::new(img)?;
488    if res == 1 {
489      Ok(img)
490    } else {
491      Err(Error::CodecError("Failed to read header".into()))
492    }
493  }
494
495  pub(crate) fn get_codestream_index(&self) -> Result<CodestreamIndex> {
496    let index = ptr::NonNull::new(unsafe { sys::opj_get_cstr_index(self.as_ptr()) })
497      .ok_or_else(|| Error::CodecError("Failed to get codestream index".into()))?;
498    Ok(CodestreamIndex(index))
499  }
500
501  pub(crate) fn get_codestream_info(&self) -> Result<CodestreamInfo> {
502    let info = ptr::NonNull::new(unsafe { sys::opj_get_cstr_info(self.as_ptr()) })
503      .ok_or_else(|| Error::CodecError("Failed to get codestream info".into()))?;
504    Ok(CodestreamInfo(info))
505  }
506
507  pub(crate) fn set_decode_area(&self, img: &Image, params: &DecodeParameters) -> Result<()> {
508    if let Some(area) = &params.area {
509      let res = unsafe {
510        sys::opj_set_decode_area(
511          self.as_ptr(),
512          img.as_ptr(),
513          area.start_x as i32,
514          area.start_y as i32,
515          area.end_x as i32,
516          area.end_y as i32,
517        )
518      };
519      if res != 1 {
520        return Err(Error::CreateCodecError(format!(
521          "Failed to set decode area."
522        )));
523      }
524    }
525    Ok(())
526  }
527
528  pub(crate) fn decode(&self, img: &Image) -> Result<()> {
529    let res = unsafe {
530      sys::opj_decode(self.as_ptr(), self.stream.as_ptr(), img.as_ptr()) == 1
531        && sys::opj_end_decompress(self.as_ptr(), self.stream.as_ptr()) == 1
532    };
533    if res {
534      Ok(())
535    } else {
536      Err(Error::CodecError("Failed to decode image".into()))
537    }
538  }
539
540  pub(crate) fn as_ptr(&self) -> *mut sys::opj_codec_t {
541    self.codec.as_ptr()
542  }
543}
544
545#[cfg(feature = "file-io")]
546pub(crate) struct Encoder<'a> {
547  codec: Codec,
548  stream: Stream<'a>,
549}
550
551#[cfg(feature = "file-io")]
552impl<'a> Encoder<'a> {
553  pub(crate) fn new(stream: Stream<'a>) -> Result<Self> {
554    assert!(!stream.is_input());
555    let fmt = stream.format();
556    let codec = Codec::new(fmt, false)?;
557    Ok(Self { codec, stream })
558  }
559
560  pub(crate) fn setup(&self, mut params: EncodeParameters, img: &Image) -> Result<()> {
561    let res = unsafe { sys::opj_setup_encoder(self.as_ptr(), &mut params.0, img.as_ptr()) };
562    if res == 1 {
563      Ok(())
564    } else {
565      Err(Error::CreateCodecError(format!(
566        "Failed to setup encoder with parameters."
567      )))
568    }
569  }
570
571  pub(crate) fn encode(&self, img: &Image) -> Result<()> {
572    let res = unsafe {
573      sys::opj_start_compress(self.as_ptr(), img.as_ptr(), self.stream.as_ptr()) == 1
574        && sys::opj_encode(self.as_ptr(), self.stream.as_ptr()) == 1
575        && sys::opj_end_compress(self.as_ptr(), self.stream.as_ptr()) == 1
576    };
577    if res {
578      Ok(())
579    } else {
580      Err(Error::CodecError("Failed to encode image".into()))
581    }
582  }
583
584  pub(crate) fn as_ptr(&self) -> *mut sys::opj_codec_t {
585    self.codec.as_ptr()
586  }
587}