Skip to content

Commit

Permalink
enable support for odd dimensions for rgb inputs
Browse files Browse the repository at this point in the history
The library supports input color formats UHDR_IMG_FMT_24bppYCbCrP010,
UHDR_IMG_FMT_12bppYCbCr420. Both these formats have chroma planes subsampled by
2, horizontally and vertically. Each chroma pixel corresponds to 4 luma pixels.
Library holds this attribute too tightly and evaluates chroma plane dimensions
as half of luma plane dimensions (round_down(w / 2) and round_down(h/2)). It is
possible to have 420 subsampled images with odd dimensions where the edge
chroma pixels correspond to 1 or 2 luma pixels but this was not handled. For
rgb inputs, this restriction need not be enforced. The current change lifts
this gating.

Test: ./ultrahdr_unit_test
  • Loading branch information
ram-mohan committed Sep 18, 2024
1 parent 0a3812c commit aad8d89
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 17 deletions.
8 changes: 6 additions & 2 deletions fuzzer/ultrahdr_enc_fuzzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,14 @@ void UltraHdrEncFuzzer::process() {
auto multi_channel_gainmap = mFdp.ConsumeBool();

int width = mFdp.ConsumeIntegralInRange<int>(kMinWidth, kMaxWidth);
width = (width >> 1) << 1;
if (hdr_img_fmt == UHDR_IMG_FMT_24bppYCbCrP010 || sdr_img_fmt == UHDR_IMG_FMT_12bppYCbCr420) {
width = (width >> 1) << 1;
}

int height = mFdp.ConsumeIntegralInRange<int>(kMinHeight, kMaxHeight);
height = (height >> 1) << 1;
if (hdr_img_fmt == UHDR_IMG_FMT_24bppYCbCrP010 || sdr_img_fmt == UHDR_IMG_FMT_12bppYCbCr420) {
height = (height >> 1) << 1;
}

// gainmap scale factor
auto gm_scale_factor = mFdp.ConsumeIntegralInRange<int>(1, 128);
Expand Down
11 changes: 10 additions & 1 deletion lib/src/jpegencoderhelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,19 @@ uhdr_error_info_t JpegEncoderHelper::encode(const uint8_t* planes[3], const size
if (format == UHDR_IMG_FMT_8bppYCbCr400) {
cinfo.input_components = 1;
cinfo.in_color_space = JCS_GRAYSCALE;
} else {
} else if (format == UHDR_IMG_FMT_12bppYCbCr420 || format == UHDR_IMG_FMT_24bppYCbCr444 ||
format == UHDR_IMG_FMT_16bppYCbCr422 || format == UHDR_IMG_FMT_16bppYCbCr440 ||
format == UHDR_IMG_FMT_12bppYCbCr411 || format == UHDR_IMG_FMT_10bppYCbCr410) {
cinfo.input_components = 3;
cinfo.in_color_space = JCS_YCbCr;
isGainMapImg = false;
} else {
status.error_code = UHDR_CODEC_ERROR;
status.has_detail = 1;
snprintf(status.detail, sizeof status.detail,
"unrecognized input color format for encoding, color format %d", format);
jpeg_destroy_compress(&cinfo);
return status;
}
}
jpeg_set_defaults(&cinfo);
Expand Down
88 changes: 74 additions & 14 deletions lib/src/ultrahdr_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,54 +145,111 @@ uhdr_error_info_t apply_effects(uhdr_encoder_private* enc) {
int left = (std::max)(0, crop_effect->m_left);
int right = (std::min)((int)hdr_raw_entry->w, crop_effect->m_right);
int crop_width = right - left;
if (crop_width <= 0 || (crop_width % 2 != 0)) {
if (crop_width <= 0) {
uhdr_error_info_t status;
status.error_code = UHDR_CODEC_INVALID_PARAM;
status.has_detail = 1;
snprintf(status.detail, sizeof status.detail,
"unexpected crop dimensions. crop width is expected to be > 0 and even, crop "
"width is %d",
"unexpected crop dimensions. crop width is expected to be > 0, crop width is %d",
crop_width);
return status;
}
if (crop_width % 2 != 0 && hdr_raw_entry->fmt == UHDR_IMG_FMT_24bppYCbCrP010) {
uhdr_error_info_t status;
status.error_code = UHDR_CODEC_INVALID_PARAM;
status.has_detail = 1;
snprintf(status.detail, sizeof status.detail,
"unexpected crop dimensions. crop width is expected to even for format "
"{UHDR_IMG_FMT_24bppYCbCrP010}, crop width is %d",
crop_width);
return status;
}

int top = (std::max)(0, crop_effect->m_top);
int bottom = (std::min)((int)hdr_raw_entry->h, crop_effect->m_bottom);
int crop_height = bottom - top;
if (crop_height <= 0 || (crop_height % 2 != 0)) {
if (crop_height <= 0) {
uhdr_error_info_t status;
status.error_code = UHDR_CODEC_INVALID_PARAM;
status.has_detail = 1;
snprintf(status.detail, sizeof status.detail,
"unexpected crop dimensions. crop height is expected to be > 0 and even, crop "
"height is %d",
"unexpected crop dimensions. crop height is expected to be > 0, crop height is %d",
crop_height);
return status;
}
if (crop_height % 2 != 0 && hdr_raw_entry->fmt == UHDR_IMG_FMT_24bppYCbCrP010) {
uhdr_error_info_t status;
status.error_code = UHDR_CODEC_INVALID_PARAM;
status.has_detail = 1;
snprintf(status.detail, sizeof status.detail,
"unexpected crop dimensions. crop height is expected to even for format "
"{UHDR_IMG_FMT_24bppYCbCrP010}. crop height is %d",
crop_height);
return status;
}
apply_crop(hdr_raw_entry.get(), left, top, crop_width, crop_height);
if (enc->m_raw_images.find(UHDR_SDR_IMG) != enc->m_raw_images.end()) {
auto& sdr_raw_entry = enc->m_raw_images.find(UHDR_SDR_IMG)->second;
if (crop_width % 2 != 0 && sdr_raw_entry->fmt == UHDR_IMG_FMT_12bppYCbCr420) {
uhdr_error_info_t status;
status.error_code = UHDR_CODEC_INVALID_PARAM;
status.has_detail = 1;
snprintf(status.detail, sizeof status.detail,
"unexpected crop dimensions. crop width is expected to even for format "
"{UHDR_IMG_FMT_12bppYCbCr420}, crop width is %d",
crop_width);
return status;
}
if (crop_height % 2 != 0 && sdr_raw_entry->fmt == UHDR_IMG_FMT_12bppYCbCr420) {
uhdr_error_info_t status;
status.error_code = UHDR_CODEC_INVALID_PARAM;
status.has_detail = 1;
snprintf(status.detail, sizeof status.detail,
"unexpected crop dimensions. crop height is expected to even for format "
"{UHDR_IMG_FMT_12bppYCbCr420}. crop height is %d",
crop_height);
return status;
}
apply_crop(sdr_raw_entry.get(), left, top, crop_width, crop_height);
}
continue;
} else if (nullptr != dynamic_cast<uhdr_resize_effect_t*>(it)) {
auto resize_effect = dynamic_cast<uhdr_resize_effect_t*>(it);
int dst_w = resize_effect->m_width;
int dst_h = resize_effect->m_height;
if (dst_w == 0 || dst_h == 0 || dst_w % 2 != 0 || dst_h % 2 != 0) {
auto& hdr_raw_entry = enc->m_raw_images.find(UHDR_HDR_IMG)->second;
if (dst_w <= 0 || dst_h <= 0) {
uhdr_error_info_t status;
status.error_code = UHDR_CODEC_INVALID_PARAM;
snprintf(status.detail, sizeof status.detail,
"destination dimension cannot be zero or odd. dest image width is %d, dest image "
"destination dimensions cannot be <= zero. dest image width is %d, dest image "
"height is %d",
dst_w, dst_h);
return status;
}
auto& hdr_raw_entry = enc->m_raw_images.find(UHDR_HDR_IMG)->second;
if ((dst_w % 2 != 0 || dst_h % 2 != 0) && hdr_raw_entry->fmt == UHDR_IMG_FMT_24bppYCbCrP010) {
uhdr_error_info_t status;
status.error_code = UHDR_CODEC_INVALID_PARAM;
snprintf(status.detail, sizeof status.detail,
"destination dimensions cannot be odd for format {UHDR_IMG_FMT_24bppYCbCrP010}. "
"dest image width is %d, dest image height is %d",
dst_w, dst_h);
return status;
}
hdr_img =
apply_resize(dynamic_cast<uhdr_resize_effect_t*>(it), hdr_raw_entry.get(), dst_w, dst_h);
if (enc->m_raw_images.find(UHDR_SDR_IMG) != enc->m_raw_images.end()) {
auto& sdr_raw_entry = enc->m_raw_images.find(UHDR_SDR_IMG)->second;
if ((dst_w % 2 != 0 || dst_h % 2 != 0) &&
sdr_raw_entry->fmt == UHDR_IMG_FMT_12bppYCbCr420) {
uhdr_error_info_t status;
status.error_code = UHDR_CODEC_INVALID_PARAM;
snprintf(status.detail, sizeof status.detail,
"destination dimensions cannot be odd for format {UHDR_IMG_FMT_12bppYCbCr420}. "
"dest image width is %d, dest image height is %d",
dst_w, dst_h);
return status;
}
sdr_img = apply_resize(dynamic_cast<uhdr_resize_effect_t*>(it), sdr_raw_entry.get(), dst_w,
dst_h);
}
Expand Down Expand Up @@ -318,12 +375,12 @@ uhdr_error_info_t apply_effects(uhdr_decoder_private* dec) {
((float)dec->m_decoded_img_buffer.get()->h) / dec->m_gainmap_img_buffer.get()->h;
int dst_gm_w = dst_w / wd_ratio;
int dst_gm_h = dst_h / ht_ratio;
if (dst_w == 0 || dst_h == 0 || dst_gm_w == 0 || dst_gm_h == 0) {
if (dst_w <= 0 || dst_h <= 0 || dst_gm_w <= 0 || dst_gm_h <= 0) {
uhdr_error_info_t status;
status.error_code = UHDR_CODEC_INVALID_PARAM;
snprintf(status.detail, sizeof status.detail,
"destination dimension cannot be zero. dest image width is %d, dest image height "
"is %d, dest gainmap width is %d, dest gainmap height is %d",
"destination dimension cannot be <= zero. dest image width is %d, dest image "
"height is %d, dest gainmap width is %d, dest gainmap height is %d",
dst_w, dst_h, dst_gm_w, dst_gm_h);
return status;
}
Expand Down Expand Up @@ -693,11 +750,14 @@ uhdr_error_info_t uhdr_enc_set_raw_image(uhdr_codec_private_t* enc, uhdr_raw_ima
"invalid input color transfer for hdr intent image %d, expects one of {UHDR_CT_HLG, "
"UHDR_CT_LINEAR, UHDR_CT_PQ}",
img->ct);
} else if (img->w % 2 != 0 || img->h % 2 != 0) {
} else if ((img->w % 2 != 0 || img->h % 2 != 0) &&
(img->fmt == UHDR_IMG_FMT_12bppYCbCr420 || img->fmt == UHDR_IMG_FMT_24bppYCbCrP010)) {
status.error_code = UHDR_CODEC_INVALID_PARAM;
status.has_detail = 1;
snprintf(status.detail, sizeof status.detail,
"image dimensions cannot be odd, received image dimensions %dx%d", img->w, img->h);
"image dimensions cannot be odd for formats {UHDR_IMG_FMT_12bppYCbCr420, "
"UHDR_IMG_FMT_24bppYCbCrP010}, received image dimensions %dx%d",
img->w, img->h);
} else if ((int)img->w < ultrahdr::kMinWidth || (int)img->h < ultrahdr::kMinHeight) {
status.error_code = UHDR_CODEC_INVALID_PARAM;
status.has_detail = 1;
Expand Down

0 comments on commit aad8d89

Please sign in to comment.