diff options
Diffstat (limited to 'lib/src')
-rw-r--r-- | lib/src/icc.cpp | 7 | ||||
-rw-r--r-- | lib/src/jpegdecoderhelper.cpp | 81 | ||||
-rw-r--r-- | lib/src/jpegr.cpp | 117 | ||||
-rw-r--r-- | lib/src/jpegrutils.cpp | 30 |
4 files changed, 130 insertions, 105 deletions
diff --git a/lib/src/icc.cpp b/lib/src/icc.cpp index b838660..0b4b341 100644 --- a/lib/src/icc.cpp +++ b/lib/src/icc.cpp @@ -633,6 +633,13 @@ ultrahdr_color_gamut IccHelper::readIccColorGamut(void* icc_data, size_t icc_siz size_t red_primary_offset = 0, green_primary_offset = 0, blue_primary_offset = 0; size_t red_primary_size = 0, green_primary_size = 0, blue_primary_size = 0; for (size_t tag_idx = 0; tag_idx < Endian_SwapBE32(header->tag_count); ++tag_idx) { + if (icc_size < kICCIdentifierSize + sizeof(ICCHeader) + ((tag_idx + 1) * kTagTableEntrySize)) { + ALOGE( + "Insufficient buffer size during icc parsing. tag index %zu, header %zu, tag size %zu, " + "icc size %zu", + tag_idx, kICCIdentifierSize + sizeof(ICCHeader), kTagTableEntrySize, icc_size); + return ULTRAHDR_COLORGAMUT_UNSPECIFIED; + } uint32_t* tag_entry_start = reinterpret_cast<uint32_t*>(icc_bytes + sizeof(ICCHeader) + tag_idx * kTagTableEntrySize); // first 4 bytes are the tag signature, next 4 bytes are the tag offset, diff --git a/lib/src/jpegdecoderhelper.cpp b/lib/src/jpegdecoderhelper.cpp index 8a8278d..70efb87 100644 --- a/lib/src/jpegdecoderhelper.cpp +++ b/lib/src/jpegdecoderhelper.cpp @@ -108,14 +108,14 @@ JpegDecoderHelper::JpegDecoderHelper() {} JpegDecoderHelper::~JpegDecoderHelper() {} -bool JpegDecoderHelper::decompressImage(const void* image, int length, bool decodeToRGBA) { +bool JpegDecoderHelper::decompressImage(const void* image, int length, decode_mode_t decodeTo) { if (image == nullptr || length <= 0) { ALOGE("Image size can not be handled: %d", length); return false; } mResultBuffer.clear(); mXMPBuffer.clear(); - return decode(image, length, decodeToRGBA); + return decode(image, length, decodeTo); } void* JpegDecoderHelper::getDecompressedImagePtr() { return mResultBuffer.data(); } @@ -187,7 +187,7 @@ bool JpegDecoderHelper::extractEXIF(const void* image, int length) { return true; } -bool JpegDecoderHelper::decode(const void* image, int length, bool decodeToRGBA) { +bool JpegDecoderHelper::decode(const void* image, int length, decode_mode_t decodeTo) { bool status = true; jpeg_decompress_struct cinfo; jpegrerror_mgr myerr; @@ -256,7 +256,7 @@ bool JpegDecoderHelper::decode(const void* image, int length, bool decodeToRGBA) goto CleanUp; } - if (decodeToRGBA) { + if (decodeTo == DECODE_TO_RGBA) { // The primary image is expected to be yuv420 sampling if (cinfo.jpeg_color_space != JCS_YCbCr) { status = false; @@ -273,7 +273,7 @@ bool JpegDecoderHelper::decode(const void* image, int length, bool decodeToRGBA) // 4 bytes per pixel mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 4); cinfo.out_color_space = JCS_EXT_RGBA; - } else { + } else if (decodeTo == DECODE_TO_YCBCR) { if (cinfo.jpeg_color_space == JCS_YCbCr) { if (cinfo.comp_info[0].h_samp_factor != 2 || cinfo.comp_info[0].v_samp_factor != 2 || cinfo.comp_info[1].h_samp_factor != 1 || cinfo.comp_info[1].v_samp_factor != 1 || @@ -292,6 +292,10 @@ bool JpegDecoderHelper::decode(const void* image, int length, bool decodeToRGBA) } cinfo.out_color_space = cinfo.jpeg_color_space; cinfo.raw_data_out = TRUE; + } else { + status = decodeTo == PARSE_ONLY; + jpeg_destroy_decompress(&cinfo); + return status; } cinfo.dct_method = JDCT_ISLOW; @@ -316,71 +320,8 @@ bool JpegDecoderHelper::decompress(jpeg_decompress_struct* cinfo, const uint8_t* : decompressYUV(cinfo, dest)); } -bool JpegDecoderHelper::getCompressedImageParameters(const void* image, int length, size_t* pWidth, - size_t* pHeight, std::vector<uint8_t>* iccData, - std::vector<uint8_t>* exifData) { - jpeg_decompress_struct cinfo; - jpegrerror_mgr myerr; - cinfo.err = jpeg_std_error(&myerr.pub); - myerr.pub.error_exit = jpegrerror_exit; - myerr.pub.output_message = output_message; - - if (setjmp(myerr.setjmp_buffer)) { - jpeg_destroy_decompress(&cinfo); - return false; - } - jpeg_create_decompress(&cinfo); - - jpeg_save_markers(&cinfo, kAPP1Marker, 0xFFFF); - jpeg_save_markers(&cinfo, kAPP2Marker, 0xFFFF); - - jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length); - cinfo.src = &mgr; - if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK) { - jpeg_destroy_decompress(&cinfo); - return false; - } - - if (pWidth != nullptr) { - *pWidth = cinfo.image_width; - } - if (pHeight != nullptr) { - *pHeight = cinfo.image_height; - } - - if (iccData != nullptr) { - for (jpeg_marker_struct* marker = cinfo.marker_list; marker; marker = marker->next) { - if (marker->marker != kAPP2Marker) { - continue; - } - if (marker->data_length <= kICCMarkerHeaderSize || - memcmp(marker->data, kICCSig, sizeof(kICCSig)) != 0) { - continue; - } - - iccData->insert(iccData->end(), marker->data, marker->data + marker->data_length); - } - } - - if (exifData != nullptr) { - bool exifAppears = false; - for (jpeg_marker_struct* marker = cinfo.marker_list; marker && !exifAppears; - marker = marker->next) { - if (marker->marker != kAPP1Marker) { - continue; - } - - const unsigned int len = marker->data_length; - if (len >= sizeof(kExifIdCode) && !memcmp(marker->data, kExifIdCode, sizeof(kExifIdCode))) { - exifData->resize(len, 0); - memcpy(static_cast<void*>(exifData->data()), marker->data, len); - exifAppears = true; - } - } - } - - jpeg_destroy_decompress(&cinfo); - return true; +bool JpegDecoderHelper::getCompressedImageParameters(const void* image, int length) { + return decode(image, length, PARSE_ONLY); } bool JpegDecoderHelper::decompressRGBA(jpeg_decompress_struct* cinfo, const uint8_t* dest) { diff --git a/lib/src/jpegr.cpp b/lib/src/jpegr.cpp index ed4fef1..74ef134 100644 --- a/lib/src/jpegr.cpp +++ b/lib/src/jpegr.cpp @@ -558,13 +558,13 @@ status_t JpegR::encodeJPEGR(jr_compressed_ptr yuv420jpg_image_ptr, // We just want to check if ICC is present, so don't do a full decode. Note, // this doesn't verify that the ICC is valid. JpegDecoderHelper decoder; - std::vector<uint8_t> icc; - decoder.getCompressedImageParameters(yuv420jpg_image_ptr->data, yuv420jpg_image_ptr->length, - /* pWidth */ nullptr, /* pHeight */ nullptr, &icc, - /* exifData */ nullptr); + if (!decoder.getCompressedImageParameters(yuv420jpg_image_ptr->data, + yuv420jpg_image_ptr->length)) { + return ERROR_JPEGR_DECODE_ERROR; + } // Add ICC if not already present. - if (icc.size() > 0) { + if (decoder.getICCSize() > 0) { JPEGR_CHECK(appendGainMap(yuv420jpg_image_ptr, gainmapjpg_image_ptr, /* exif */ nullptr, /* icc */ nullptr, /* icc size */ 0, metadata, dest)); } else { @@ -577,12 +577,12 @@ status_t JpegR::encodeJPEGR(jr_compressed_ptr yuv420jpg_image_ptr, return JPEGR_NO_ERROR; } -status_t JpegR::getJPEGRInfo(jr_compressed_ptr jpegr_image_ptr, jr_info_ptr jpeg_image_info_ptr) { +status_t JpegR::getJPEGRInfo(jr_compressed_ptr jpegr_image_ptr, jr_info_ptr jpegr_image_info_ptr) { if (jpegr_image_ptr == nullptr || jpegr_image_ptr->data == nullptr) { ALOGE("received nullptr for compressed jpegr image"); return ERROR_JPEGR_BAD_PTR; } - if (jpeg_image_info_ptr == nullptr) { + if (jpegr_image_info_ptr == nullptr) { ALOGE("received nullptr for compressed jpegr info struct"); return ERROR_JPEGR_BAD_PTR; } @@ -592,13 +592,16 @@ status_t JpegR::getJPEGRInfo(jr_compressed_ptr jpegr_image_ptr, jr_info_ptr jpeg if (status != JPEGR_NO_ERROR && status != ERROR_JPEGR_GAIN_MAP_IMAGE_NOT_FOUND) { return status; } - - JpegDecoderHelper jpeg_dec_obj_hdr; - if (!jpeg_dec_obj_hdr.getCompressedImageParameters( - primary_image.data, primary_image.length, &jpeg_image_info_ptr->width, - &jpeg_image_info_ptr->height, jpeg_image_info_ptr->iccData, - jpeg_image_info_ptr->exifData)) { - return ERROR_JPEGR_DECODE_ERROR; + status = parseJpegInfo(&primary_image, jpegr_image_info_ptr->primaryImgInfo, + &jpegr_image_info_ptr->width, &jpegr_image_info_ptr->height); + if (status != JPEGR_NO_ERROR) { + return status; + } + if (jpegr_image_info_ptr->gainmapImgInfo != nullptr) { + status = parseJpegInfo(&gainmap_image, jpegr_image_info_ptr->gainmapImgInfo); + if (status != JPEGR_NO_ERROR) { + return status; + } } return status; @@ -641,8 +644,9 @@ status_t JpegR::decodeJPEGR(jr_compressed_ptr jpegr_image_ptr, jr_uncompressed_p } JpegDecoderHelper jpeg_dec_obj_yuv420; - if (!jpeg_dec_obj_yuv420.decompressImage(primary_jpeg_image.data, primary_jpeg_image.length, - (output_format == ULTRAHDR_OUTPUT_SDR))) { + if (!jpeg_dec_obj_yuv420.decompressImage( + primary_jpeg_image.data, primary_jpeg_image.length, + (output_format == ULTRAHDR_OUTPUT_SDR) ? DECODE_TO_RGBA : DECODE_TO_YCBCR)) { return ERROR_JPEGR_DECODE_ERROR; } @@ -1010,29 +1014,39 @@ status_t JpegR::applyGainMap(jr_uncompressed_ptr yuv420_image_ptr, return ERROR_JPEGR_BAD_METADATA; } - // TODO: remove once map scaling factor is computed based on actual map dims - size_t image_width = yuv420_image_ptr->width; - size_t image_height = yuv420_image_ptr->height; - size_t map_width = image_width / kMapDimensionScaleFactor; - size_t map_height = image_height / kMapDimensionScaleFactor; - if (map_width != gainmap_image_ptr->width || map_height != gainmap_image_ptr->height) { + if (yuv420_image_ptr->width % gainmap_image_ptr->width != 0 || + yuv420_image_ptr->height % gainmap_image_ptr->height != 0) { ALOGE( - "gain map dimensions and primary image dimensions are not to scale, computed gain map " - "resolution is %zux%zu, received gain map resolution is %zux%zu", - map_width, map_height, gainmap_image_ptr->width, gainmap_image_ptr->height); - return ERROR_JPEGR_RESOLUTION_MISMATCH; + "gain map dimensions scale factor value is not an integer, primary image resolution is " + "%zux%zu, received gain map resolution is %zux%zu", + yuv420_image_ptr->width, yuv420_image_ptr->height, gainmap_image_ptr->width, + gainmap_image_ptr->height); + return ERROR_JPEGR_UNSUPPORTED_MAP_SCALE_FACTOR; } + if (yuv420_image_ptr->width * gainmap_image_ptr->height != + yuv420_image_ptr->height * gainmap_image_ptr->width) { + ALOGE( + "gain map dimensions scale factor values for height and width are different, \n primary " + "image resolution is %zux%zu, received gain map resolution is %zux%zu", + yuv420_image_ptr->width, yuv420_image_ptr->height, gainmap_image_ptr->width, + gainmap_image_ptr->height); + return ERROR_JPEGR_UNSUPPORTED_MAP_SCALE_FACTOR; + } + // TODO: Currently map_scale_factor is of type size_t, but it could be changed to a float + // later. + size_t map_scale_factor = yuv420_image_ptr->width / gainmap_image_ptr->width; + dest->width = yuv420_image_ptr->width; dest->height = yuv420_image_ptr->height; - ShepardsIDW idwTable(kMapDimensionScaleFactor); + ShepardsIDW idwTable(map_scale_factor); float display_boost = (std::min)(max_display_boost, metadata->maxContentBoost); GainLUT gainLUT(metadata, display_boost); JobQueue jobQueue; std::function<void()> applyRecMap = [yuv420_image_ptr, gainmap_image_ptr, dest, &jobQueue, - &idwTable, output_format, &gainLUT, - display_boost]() -> void { + &idwTable, output_format, &gainLUT, display_boost, + map_scale_factor]() -> void { size_t width = yuv420_image_ptr->width; size_t rowStart, rowEnd; @@ -1049,11 +1063,7 @@ status_t JpegR::applyGainMap(jr_uncompressed_ptr yuv420_image_ptr, Color rgb_sdr = srgbInvOetf(rgb_gamma_sdr); #endif float gain; - // TODO: determine map scaling factor based on actual map dims - size_t map_scale_factor = kMapDimensionScaleFactor; // TODO: If map_scale_factor is guaranteed to be an integer, then remove the following. - // Currently map_scale_factor is of type size_t, but it could be changed to a float - // later. if (map_scale_factor != floorf(map_scale_factor)) { gain = sampleMap(gainmap_image_ptr, map_scale_factor, x, y); } else { @@ -1110,7 +1120,7 @@ status_t JpegR::applyGainMap(jr_uncompressed_ptr yuv420_image_ptr, for (int th = 0; th < threads - 1; th++) { workers.push_back(std::thread(applyRecMap)); } - const int rowStep = threads == 1 ? yuv420_image_ptr->height : kJobSzInRows; + const int rowStep = threads == 1 ? yuv420_image_ptr->height : map_scale_factor; for (size_t rowStart = 0; rowStart < yuv420_image_ptr->height;) { int rowEnd = (std::min)(rowStart + rowStep, yuv420_image_ptr->height); jobQueue.enqueueJob(rowStart, rowEnd); @@ -1177,6 +1187,45 @@ status_t JpegR::extractPrimaryImageAndGainMap(jr_compressed_ptr jpegr_image_ptr, return JPEGR_NO_ERROR; } +status_t JpegR::parseJpegInfo(jr_compressed_ptr jpeg_image_ptr, j_info_ptr jpeg_image_info_ptr, + size_t* img_width, size_t* img_height) { + JpegDecoderHelper jpeg_dec_obj; + if (!jpeg_dec_obj.getCompressedImageParameters(jpeg_image_ptr->data, jpeg_image_ptr->length)) { + return ERROR_JPEGR_DECODE_ERROR; + } + size_t imgWidth, imgHeight; + imgWidth = jpeg_dec_obj.getDecompressedImageWidth(); + imgHeight = jpeg_dec_obj.getDecompressedImageHeight(); + + if (jpeg_image_info_ptr != nullptr) { + jpeg_image_info_ptr->width = imgWidth; + jpeg_image_info_ptr->height = imgHeight; + jpeg_image_info_ptr->imgData.resize(jpeg_image_ptr->length, 0); + memcpy(static_cast<void*>(jpeg_image_info_ptr->imgData.data()), jpeg_image_ptr->data, + jpeg_image_ptr->length); + if (jpeg_dec_obj.getICCSize() != 0) { + jpeg_image_info_ptr->iccData.resize(jpeg_dec_obj.getICCSize(), 0); + memcpy(static_cast<void*>(jpeg_image_info_ptr->iccData.data()), jpeg_dec_obj.getICCPtr(), + jpeg_dec_obj.getICCSize()); + } + if (jpeg_dec_obj.getEXIFSize() != 0) { + jpeg_image_info_ptr->exifData.resize(jpeg_dec_obj.getEXIFSize(), 0); + memcpy(static_cast<void*>(jpeg_image_info_ptr->exifData.data()), jpeg_dec_obj.getEXIFPtr(), + jpeg_dec_obj.getEXIFSize()); + } + if (jpeg_dec_obj.getXMPSize() != 0) { + jpeg_image_info_ptr->xmpData.resize(jpeg_dec_obj.getXMPSize(), 0); + memcpy(static_cast<void*>(jpeg_image_info_ptr->xmpData.data()), jpeg_dec_obj.getXMPPtr(), + jpeg_dec_obj.getXMPSize()); + } + } + if (img_width != nullptr && img_height != nullptr) { + *img_width = imgWidth; + *img_height = imgHeight; + } + return JPEGR_NO_ERROR; +} + // JPEG/R structure: // SOI (ff d8) // diff --git a/lib/src/jpegrutils.cpp b/lib/src/jpegrutils.cpp index 77cb26b..bfc847f 100644 --- a/lib/src/jpegrutils.cpp +++ b/lib/src/jpegrutils.cpp @@ -451,7 +451,35 @@ bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, ultrahdr_metadata_st xmp_size -= nameSpace.size() + 1; XMPXmlHandler handler; - // We need to remove tail data until the closing tag. Otherwise parser will throw an error. + // xml parser fails to parse packet header, wrapper. remove them before handing the data to + // parser. if there is no packet header, do nothing otherwise go to the position of '<' without + // '?' after it. + int offset = 0; + for (int i = 0; i < xmp_size; ++i) { + if (xmp_data[i] == '<') { + if (xmp_data[i + 1] != '?') { + offset = i; + break; + } + } + } + xmp_data += offset; + xmp_size -= offset; + + // If there is no packet wrapper, do nothing other wise go to the position of last '>' without '?' + // before it. + offset = 0; + for (int i = xmp_size - 1; i >= 1; --i) { + if (xmp_data[i] == '>') { + if (xmp_data[i - 1] != '?') { + offset = xmp_size - (i + 1); + break; + } + } + } + xmp_size -= offset; + + // remove padding while (xmp_data[xmp_size - 1] != '>' && xmp_size > 1) { xmp_size--; } |