summaryrefslogtreecommitdiff
path: root/libs/ultrahdr/jpegdecoderhelper.cpp
diff options
context:
space:
mode:
author Nick Deakin <deakin@google.com> 2023-05-19 17:14:45 -0400
committer Nick Deakin <deakin@google.com> 2023-06-08 10:14:12 -0400
commit0db53ee3c9ae908d14c09290a4fb51036df25620 (patch)
tree99fa81400c2b05f540b48c9280fe5117edd871c9 /libs/ultrahdr/jpegdecoderhelper.cpp
parentfcb80a221e7ac3548ff5d751eea14c708a6f43fd (diff)
libultrahdr: correct srgb, p3 calculations and jpeg yuv handling
* Correct luminance calculation for sRGB to utilize actual luminance coefficients for the gamut, rather than 601 luma coefficients. * Correct YUV<->RGB conversion for sRGB to utilize Rec.709 coefficients rather than Rec.601 coefficients as it was previously. * New P3 YUV<->RGB conversion, which uses Rec.601 coefficients. * Also ICC Profile fixes to make things work; more below. * Update things to correctly convert to and from Rec.601 YUV for jpeg encoding; more below. This setup for YUV<->RGB coefficients is chosen to match the expectations of DataSpace when it comes to interpretting YUV encoding of data. Generally, the interpretation is cued off of the color primaries, since the specifications around color primaries generally also specify a YUV interpretation. Display-P3 is a bit of an outlier; the best specification of Display-P3 is in SMPTE EG 432-1, but EG 432-1 doesn't cover YUV interpretation. So, since DataSpace interprets Display-P3 YUV data via the Rec.601 coefficients, we should do the same here. ICC Profile fixes; ICC profiles we wrote were broken before this for a variety of reasons: * The endianness macro wasn't actually swapping endiannesas to provide the correct encoding in our output. * We weren't writing out the identifier for the app segment, including the chunk count and ID. * We were assuming input JPEGs have ICC data, which may not be the case. * We also need to read in the ICC profile during decode to apply the map properly, and we didn't have any mechanism previously to read the ICC profile and determine the gamut of the encoded JPEGR file. * Upon adding ICC reading code to our JPEG decoding, also remove some dead code from previous EXIF reading. * Add a number of tests to verify all of this stuff stays fixed. YUV interpretation and Rec.601: * Previously, we were feeding YUV right into the JPEG encoder; this is problematic because JPEG encoders usually (and definitely in our specific case) expect Rec.601 YUV encoded input data, since this is by definition the format of JPEG YUV data according to ECMA TR/98. * Now properly convert from Rec.709 or Rec.2100 YUV encoding to Rec.601 (when necessary) prior to passing YUV data to the jpeg encoder. * Also make sure we properly interpret decoded YUV output as Rec.601 after decode. * This involved added some new methods to facilitate these conversions. * Added some new tests to verify these conversions. * Note that to do these YUV conversions for subsampled 420 data, we take each set of 4 Y and 1 UV, and calculate the result against each combination. The new Y values each get the corresponding result, and the new UV value is equal to the average of the set. * Note that none of this is a concern for gain map encoding/decoding via JPEG because gain maps are single channel. Bug: 283143961 Test: added new tests, all tests pass Change-Id: Ibc7b1779fc3a8244f85abb581c554963f57dc5a4
Diffstat (limited to 'libs/ultrahdr/jpegdecoderhelper.cpp')
-rw-r--r--libs/ultrahdr/jpegdecoderhelper.cpp52
1 files changed, 29 insertions, 23 deletions
diff --git a/libs/ultrahdr/jpegdecoderhelper.cpp b/libs/ultrahdr/jpegdecoderhelper.cpp
index 0bad4a4de0..fef544452a 100644
--- a/libs/ultrahdr/jpegdecoderhelper.cpp
+++ b/libs/ultrahdr/jpegdecoderhelper.cpp
@@ -93,7 +93,6 @@ static void jpegrerror_exit(j_common_ptr cinfo) {
}
JpegDecoderHelper::JpegDecoderHelper() {
- mExifPos = 0;
}
JpegDecoderHelper::~JpegDecoderHelper() {
@@ -138,6 +137,14 @@ size_t JpegDecoderHelper::getEXIFSize() {
return mEXIFBuffer.size();
}
+void* JpegDecoderHelper::getICCPtr() {
+ return mICCBuffer.data();
+}
+
+size_t JpegDecoderHelper::getICCSize() {
+ return mICCBuffer.size();
+}
+
size_t JpegDecoderHelper::getDecompressedImageWidth() {
return mWidth;
}
@@ -168,31 +175,21 @@ bool JpegDecoderHelper::decode(const void* image, int length, bool decodeToRGBA)
cinfo.src = &mgr;
jpeg_read_header(&cinfo, TRUE);
- // Save XMP data and EXIF data.
- // Here we only handle the first XMP / EXIF package.
- // The parameter pos is used for capturing start offset of EXIF, which is hacky, but working...
+ // Save XMP data, EXIF data, and ICC data.
+ // Here we only handle the first XMP / EXIF / ICC package.
// We assume that all packages are starting with two bytes marker (eg FF E1 for EXIF package),
// two bytes of package length which is stored in marker->original_length, and the real data
- // which is stored in marker->data. The pos is adding up all previous package lengths (
- // 4 bytes marker and length, marker->original_length) before EXIF appears. Note that here we
- // we are using marker->original_length instead of marker->data_length because in case the real
- // package length is larger than the limitation, jpeg-turbo will only copy the data within the
- // limitation (represented by data_length) and this may vary from original_length / real offset.
- // A better solution is making jpeg_marker_struct holding the offset, but currently it doesn't.
+ // which is stored in marker->data.
bool exifAppears = false;
bool xmpAppears = false;
- size_t pos = 2; // position after SOI
+ bool iccAppears = false;
for (jpeg_marker_struct* marker = cinfo.marker_list;
- marker && !(exifAppears && xmpAppears);
+ marker && !(exifAppears && xmpAppears && iccAppears);
marker = marker->next) {
- pos += 4;
- pos += marker->original_length;
-
- if (marker->marker != kAPP1Marker) {
+ if (marker->marker != kAPP1Marker && marker->marker != kAPP2Marker) {
continue;
}
-
const unsigned int len = marker->data_length;
if (!xmpAppears &&
len > kXmpNameSpace.size() &&
@@ -210,7 +207,12 @@ bool JpegDecoderHelper::decode(const void* image, int length, bool decodeToRGBA)
mEXIFBuffer.resize(len, 0);
memcpy(static_cast<void*>(mEXIFBuffer.data()), marker->data, len);
exifAppears = true;
- mExifPos = pos - marker->original_length;
+ } else if (!iccAppears &&
+ len > sizeof(kICCSig) &&
+ !memcmp(marker->data, kICCSig, sizeof(kICCSig))) {
+ mICCBuffer.resize(len, 0);
+ memcpy(static_cast<void*>(mICCBuffer.data()), marker->data, len);
+ iccAppears = true;
}
}
@@ -228,6 +230,7 @@ bool JpegDecoderHelper::decode(const void* image, int length, bool decodeToRGBA)
if (cinfo.jpeg_color_space == JCS_GRAYSCALE) {
// We don't intend to support decoding grayscale to RGBA
status = false;
+ ALOGE("%s: decoding grayscale to RGBA is unsupported", __func__);
goto CleanUp;
}
// 4 bytes per pixel
@@ -242,6 +245,7 @@ bool JpegDecoderHelper::decode(const void* image, int length, bool decodeToRGBA)
cinfo.comp_info[1].v_samp_factor != 1 ||
cinfo.comp_info[2].v_samp_factor != 1) {
status = false;
+ ALOGE("%s: decoding to YUV only supports 4:2:0 subsampling", __func__);
goto CleanUp;
}
mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 3 / 2, 0);
@@ -304,8 +308,12 @@ bool JpegDecoderHelper::getCompressedImageParameters(const void* image, int leng
return false;
}
- *pWidth = cinfo.image_width;
- *pHeight = cinfo.image_height;
+ 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;
@@ -318,9 +326,7 @@ bool JpegDecoderHelper::getCompressedImageParameters(const void* image, int leng
continue;
}
- const unsigned int len = marker->data_length - kICCMarkerHeaderSize;
- const uint8_t *src = marker->data + kICCMarkerHeaderSize;
- iccData->insert(iccData->end(), src, src+len);
+ iccData->insert(iccData->end(), marker->data, marker->data + marker->data_length);
}
}