@@ -329,6 +329,14 @@ JpgInput::open(const std::string& name, ImageSpec& newspec)
329329
330330 read_icc_profile (&m_cinfo, m_spec); // / try to read icc profile
331331
332+ // Try to interpret as Ultra HDR image.
333+ // The libultrahdr API requires to load the whole file content in memory
334+ // therefore we first check for the presence of the "hdrgm:Version" metadata
335+ // to avoid this costly process when not necessary.
336+ // https://developer.android.com/media/platform/hdr-image-format#signal_of_the_format
337+ if (m_spec.find_attribute (" hdrgm:Version" ))
338+ m_is_uhdr = read_uhdr (m_io);
339+
332340 newspec = m_spec;
333341 return true ;
334342}
@@ -406,6 +414,86 @@ JpgInput::read_icc_profile(j_decompress_ptr cinfo, ImageSpec& spec)
406414
407415
408416
417+ bool
418+ JpgInput::read_uhdr (Filesystem::IOProxy* ioproxy)
419+ {
420+ #if defined(USE_UHDR)
421+ // Read entire file content into buffer.
422+ const size_t buffer_size = ioproxy->size ();
423+ std::vector<unsigned char > buffer (buffer_size);
424+ ioproxy->pread (buffer.data (), buffer_size, 0 );
425+
426+ // Check if this is an actual Ultra HDR image.
427+ const bool detect_uhdr = is_uhdr_image (buffer.data (), buffer.size ());
428+ if (!detect_uhdr)
429+ return false ;
430+
431+ // Create Ultra HDR decoder.
432+ // Do not forget to release it once we don't need it,
433+ // i.e if this function returns false
434+ // or when we call close().
435+ m_uhdr_dec = uhdr_create_decoder ();
436+
437+ // Prepare decoder input.
438+ // Note: we currently do not override any of the
439+ // default settings.
440+ uhdr_compressed_image_t uhdr_compressed;
441+ uhdr_compressed.data = buffer.data ();
442+ uhdr_compressed.data_sz = buffer.size ();
443+ uhdr_compressed.capacity = buffer.size ();
444+ uhdr_dec_set_image (m_uhdr_dec, &uhdr_compressed);
445+
446+ // Decode Ultra HDR image
447+ // and check for decoding errors.
448+ uhdr_error_info_t err_info = uhdr_decode (m_uhdr_dec);
449+
450+ if (err_info.error_code != UHDR_CODEC_OK) {
451+ errorfmt (" Ultra HDR decoding failed with error code {}" ,
452+ int (err_info.error_code ));
453+ if (err_info.has_detail != 0 )
454+ errorfmt (" Additional error details: {}" , err_info.detail );
455+ uhdr_release_decoder (m_uhdr_dec);
456+ return false ;
457+ }
458+
459+ // Update spec with decoded image properties.
460+ // Note: we currently only support a subset of all possible
461+ // Ultra HDR image formats.
462+ uhdr_raw_image_t * uhdr_raw = uhdr_get_decoded_image (m_uhdr_dec);
463+
464+ int nchannels;
465+ TypeDesc desc;
466+ switch (uhdr_raw->fmt ) {
467+ case UHDR_IMG_FMT_32bppRGBA8888:
468+ nchannels = 4 ;
469+ desc = TypeDesc::UINT8;
470+ break ;
471+ case UHDR_IMG_FMT_64bppRGBAHalfFloat:
472+ nchannels = 4 ;
473+ desc = TypeDesc::HALF;
474+ break ;
475+ case UHDR_IMG_FMT_24bppRGB888:
476+ nchannels = 3 ;
477+ desc = TypeDesc::UINT8;
478+ break ;
479+ default :
480+ errorfmt (" Unsupported Ultra HDR image format: {}" , int (uhdr_raw->fmt ));
481+ uhdr_release_decoder (m_uhdr_dec);
482+ return false ;
483+ }
484+
485+ ImageSpec newspec = ImageSpec (uhdr_raw->w , uhdr_raw->h , nchannels, desc);
486+ newspec.extra_attribs = std::move (m_spec.extra_attribs );
487+ m_spec = newspec;
488+
489+ return true ;
490+ #else
491+ return false ;
492+ #endif
493+ }
494+
495+
496+
409497static void
410498cmyk_to_rgb (int n, const unsigned char * cmyk, size_t cmyk_stride,
411499 unsigned char * rgb, size_t rgb_stride)
@@ -453,6 +541,28 @@ JpgInput::read_native_scanline(int subimage, int miplevel, int y, int /*z*/,
453541 OIIO_DASSERT (m_next_scanline == 0 && current_subimage () == subimage);
454542 }
455543
544+ #if defined(USE_UHDR)
545+ if (m_is_uhdr) {
546+ uhdr_raw_image_t * uhdr_raw = uhdr_get_decoded_image (m_uhdr_dec);
547+
548+ unsigned int nbytes;
549+ switch (uhdr_raw->fmt ) {
550+ case UHDR_IMG_FMT_32bppRGBA8888: nbytes = 4 ; break ;
551+ case UHDR_IMG_FMT_64bppRGBAHalfFloat: nbytes = 8 ; break ;
552+ case UHDR_IMG_FMT_24bppRGB888: nbytes = 3 ; break ;
553+ default : return false ;
554+ }
555+
556+ const size_t row_size = uhdr_raw->stride [UHDR_PLANE_PACKED] * nbytes;
557+ unsigned char * top_left = static_cast <unsigned char *>(
558+ uhdr_raw->planes [UHDR_PLANE_PACKED]);
559+ unsigned char * row_data_start = top_left + row_size * y;
560+ memcpy (data, row_data_start, row_size);
561+
562+ return true ;
563+ }
564+ #endif
565+
456566 // Set up our custom error handler
457567 if (setjmp (m_jerr.setjmp_buffer )) {
458568 // Jump to here if there's a libjpeg internal error
@@ -494,6 +604,11 @@ JpgInput::close()
494604 if (m_decomp_create)
495605 jpeg_destroy_decompress (&m_cinfo);
496606 m_decomp_create = false ;
607+ #if defined(USE_UHDR)
608+ if (m_is_uhdr)
609+ uhdr_release_decoder (m_uhdr_dec);
610+ m_is_uhdr = false ;
611+ #endif
497612 close_file ();
498613 }
499614 init (); // Reset to initial state
0 commit comments