struct heif_error openjpeg_decode_image()

in libheif/plugins/decoder_openjpeg.cc [252:390]


struct heif_error openjpeg_decode_image(void* decoder_raw, struct heif_image** out_img)
{
  struct openjpeg_decoder* decoder = (struct openjpeg_decoder*) decoder_raw;

  OPJ_BOOL success;
  opj_dparameters_t decompression_parameters;
  opj_codec_t* l_codec;

  // Initialize Decoder
  opj_set_default_decoder_parameters(&decompression_parameters);
  l_codec = opj_create_decompress(OPJ_CODEC_J2K);
  success = opj_setup_decoder(l_codec, &decompression_parameters);
  if (!success) {
    struct heif_error err = {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "opj_setup_decoder()"};
    return err;
  }


  // Create Input Stream

  OPJ_BOOL is_read_stream = true;
  opj_stream_t* stream = opj_stream_create_default_memory_stream(decoder, is_read_stream);


  // Read Codestream Header
  opj_image_t* image = NULL;
  success = opj_read_header(stream, l_codec, &image);
  if (!success) {
    struct heif_error err = {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "opj_read_header()"};
    return err;
  }
  else if (image->numcomps != 3 && image->numcomps != 1) {
    //TODO - Handle other numbers of components
    struct heif_error err = {heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, "Number of components must be 3 or 1"};
    return err;
  }
  else if ((image->color_space != OPJ_CLRSPC_UNSPECIFIED) && (image->color_space != OPJ_CLRSPC_SRGB)) {
    //TODO - Handle other colorspaces
    struct heif_error err = {heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, "Colorspace must be SRGB"};
    return err;
  }

  const int width = (image->x1 - image->x0);
  const int height = (image->y1 - image->y0);


  /* Get the decoded image */
  success = opj_decode(l_codec, stream, image);
  if (!success) {
    struct heif_error err = {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "opj_decode()"};
    return err;
  }


  success = opj_end_decompress(l_codec, stream);
  if (!success) {
    struct heif_error err = {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "opj_end_decompress()"};
    return err;
  }


  /* Close the byte stream */
  opj_stream_destroy(stream);


  heif_colorspace colorspace = heif_colorspace_YCbCr;
  heif_chroma chroma = heif_chroma_444; //heif_chroma_interleaved_RGB;

  std::vector<heif_channel> channels;

  if (image->numcomps == 1) {
    colorspace = heif_colorspace_monochrome;
    chroma = heif_chroma_monochrome;
    channels = {heif_channel_Y};
  }
  else if (image->numcomps == 3 &&
           image->comps[1].dx == 1 &&
           image->comps[1].dy == 1) {
    colorspace = heif_colorspace_YCbCr;
    chroma = heif_chroma_444;
    channels = {heif_channel_Y, heif_channel_Cb, heif_channel_Cr};
  }
  else if (image->numcomps == 3 &&
           image->comps[1].dx == 2 &&
           image->comps[1].dy == 1) {
    colorspace = heif_colorspace_YCbCr;
    chroma = heif_chroma_422;
    channels = {heif_channel_Y, heif_channel_Cb, heif_channel_Cr};
  }
  else if (image->numcomps == 3 &&
           image->comps[1].dx == 2 &&
           image->comps[1].dy == 2) {
    colorspace = heif_colorspace_YCbCr;
    chroma = heif_chroma_420;
    channels = {heif_channel_Y, heif_channel_Cb, heif_channel_Cr};
  }
  else {
    struct heif_error err = {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "unsupported image format"};
    return err;
  }


  struct heif_error error = heif_image_create(width, height, colorspace, chroma, out_img);
  if (error.code) {
    return error;
  }

  for (size_t c = 0; c < image->numcomps; c++) {
    const opj_image_comp_t& opj_comp = image->comps[c];

    int bit_depth = opj_comp.prec;
    int cwidth = opj_comp.w;
    int cheight = opj_comp.h;

    error = heif_image_add_plane(*out_img, channels[c], cwidth, cheight, bit_depth);

    int stride = -1;
    uint8_t* p = heif_image_get_plane(*out_img, channels[c], &stride);


    // TODO: a SIMD implementation to convert int32 to uint8 would speed this up
    // https://stackoverflow.com/questions/63774643/how-to-convert-uint32-to-uint8-using-simd-but-not-avx512

    if (stride == cwidth) {
      for (int i = 0; i < cwidth * cheight; i++) {
        p[i] = (uint8_t) opj_comp.data[i];
      }
    }
    else {
      for (int y = 0; y < cheight; y++) {
        for (int x = 0; x < cwidth; x++) {
          p[y * stride + x] = (uint8_t) opj_comp.data[y * cwidth + x];
        }
      }
    }
  }

  return heif_error_ok;
}