Loading libs/ultrahdr/gainmapmath.cpp +132 −17 Original line number Diff line number Diff line Loading @@ -119,34 +119,39 @@ static float clampPixelFloat(float value) { return (value < 0.0f) ? 0.0f : (value > kMaxPixelFloat) ? kMaxPixelFloat : value; } // See IEC 61966-2-1, Equation F.7. // See IEC 61966-2-1/Amd 1:2003, Equation F.7. static const float kSrgbR = 0.2126f, kSrgbG = 0.7152f, kSrgbB = 0.0722f; float srgbLuminance(Color e) { return kSrgbR * e.r + kSrgbG * e.g + kSrgbB * e.b; } // See ECMA TR/98, Section 7. static const float kSrgbRCr = 1.402f, kSrgbGCb = 0.34414f, kSrgbGCr = 0.71414f, kSrgbBCb = 1.772f; // See ITU-R BT.709-6, Section 3. // Uses the same coefficients for deriving luma signal as // IEC 61966-2-1/Amd 1:2003 states for luminance, so we reuse the luminance // function above. static const float kSrgbCb = 1.8556f, kSrgbCr = 1.5748f; Color srgbYuvToRgb(Color e_gamma) { return {{{ clampPixelFloat(e_gamma.y + kSrgbRCr * e_gamma.v), clampPixelFloat(e_gamma.y - kSrgbGCb * e_gamma.u - kSrgbGCr * e_gamma.v), clampPixelFloat(e_gamma.y + kSrgbBCb * e_gamma.u) }}}; Color srgbRgbToYuv(Color e_gamma) { float y_gamma = srgbLuminance(e_gamma); return {{{ y_gamma, (e_gamma.b - y_gamma) / kSrgbCb, (e_gamma.r - y_gamma) / kSrgbCr }}}; } // See ECMA TR/98, Section 7. static const float kSrgbYR = 0.299f, kSrgbYG = 0.587f, kSrgbYB = 0.114f; static const float kSrgbUR = -0.1687f, kSrgbUG = -0.3313f, kSrgbUB = 0.5f; static const float kSrgbVR = 0.5f, kSrgbVG = -0.4187f, kSrgbVB = -0.0813f; // See ITU-R BT.709-6, Section 3. // Same derivation to BT.2100's YUV->RGB, below. Similar to srgbRgbToYuv, we // can reuse the luminance coefficients since they are the same. static const float kSrgbGCb = kSrgbB * kSrgbCb / kSrgbG; static const float kSrgbGCr = kSrgbR * kSrgbCr / kSrgbG; Color srgbRgbToYuv(Color e_gamma) { return {{{ kSrgbYR * e_gamma.r + kSrgbYG * e_gamma.g + kSrgbYB * e_gamma.b, kSrgbUR * e_gamma.r + kSrgbUG * e_gamma.g + kSrgbUB * e_gamma.b, kSrgbVR * e_gamma.r + kSrgbVG * e_gamma.g + kSrgbVB * e_gamma.b }}}; Color srgbYuvToRgb(Color e_gamma) { return {{{ clampPixelFloat(e_gamma.y + kSrgbCr * e_gamma.v), clampPixelFloat(e_gamma.y - kSrgbGCb * e_gamma.u - kSrgbGCr * e_gamma.v), clampPixelFloat(e_gamma.y + kSrgbCb * e_gamma.u) }}}; } // See IEC 61966-2-1, Equations F.5 and F.6. // See IEC 61966-2-1/Amd 1:2003, Equations F.5 and F.6. float srgbInvOetf(float e_gamma) { if (e_gamma <= 0.04045f) { return e_gamma / 12.92f; Loading Loading @@ -178,13 +183,38 @@ Color srgbInvOetfLUT(Color e_gamma) { //////////////////////////////////////////////////////////////////////////////// // Display-P3 transformations // See SMPTE EG 432-1, Table 7-2. // See SMPTE EG 432-1, Equation 7-8. static const float kP3R = 0.20949f, kP3G = 0.72160f, kP3B = 0.06891f; float p3Luminance(Color e) { return kP3R * e.r + kP3G * e.g + kP3B * e.b; } // See ITU-R BT.601-7, Sections 2.5.1 and 2.5.2. // Unfortunately, calculation of luma signal differs from calculation of // luminance for Display-P3, so we can't reuse p3Luminance here. static const float kP3YR = 0.299f, kP3YG = 0.587f, kP3YB = 0.114f; static const float kP3Cb = 1.772f, kP3Cr = 1.402f; Color p3RgbToYuv(Color e_gamma) { float y_gamma = kP3YR * e_gamma.r + kP3YG * e_gamma.g + kP3YB * e_gamma.b; return {{{ y_gamma, (e_gamma.b - y_gamma) / kP3Cb, (e_gamma.r - y_gamma) / kP3Cr }}}; } // See ITU-R BT.601-7, Sections 2.5.1 and 2.5.2. // Same derivation to BT.2100's YUV->RGB, below. Similar to p3RgbToYuv, we must // use luma signal coefficients rather than the luminance coefficients. static const float kP3GCb = kP3YB * kP3Cb / kP3YG; static const float kP3GCr = kP3YR * kP3Cr / kP3YG; Color p3YuvToRgb(Color e_gamma) { return {{{ clampPixelFloat(e_gamma.y + kP3Cr * e_gamma.v), clampPixelFloat(e_gamma.y - kP3GCb * e_gamma.u - kP3GCr * e_gamma.v), clampPixelFloat(e_gamma.y + kP3Cb * e_gamma.u) }}}; } //////////////////////////////////////////////////////////////////////////////// // BT.2100 transformations - according to ITU-R BT.2100-2 Loading @@ -197,6 +227,8 @@ float bt2100Luminance(Color e) { } // See ITU-R BT.2100-2, Table 6, Derivation of colour difference signals. // BT.2100 uses the same coefficients for calculating luma signal and luminance, // so we reuse the luminance function here. static const float kBt2100Cb = 1.8814f, kBt2100Cr = 1.4746f; Color bt2100RgbToYuv(Color e_gamma) { Loading @@ -206,6 +238,10 @@ Color bt2100RgbToYuv(Color e_gamma) { (e_gamma.r - y_gamma) / kBt2100Cr }}}; } // See ITU-R BT.2100-2, Table 6, Derivation of colour difference signals. // // Similar to bt2100RgbToYuv above, we can reuse the luminance coefficients. // // Derived by inversing bt2100RgbToYuv. The derivation for R and B are pretty // straight forward; we just invert the formulas for U and V above. But deriving // the formula for G is a bit more complicated: Loading Loading @@ -440,6 +476,85 @@ ColorTransformFn getHdrConversionFn(ultrahdr_color_gamut sdr_gamut, } } // All of these conversions are derived from the respective input YUV->RGB conversion followed by // the RGB->YUV for the receiving encoding. They are consistent with the RGB<->YUV functions in this // file, given that we uses BT.709 encoding for sRGB and BT.601 encoding for Display-P3, to match // DataSpace. Color yuv709To601(Color e_gamma) { return {{{ 1.0f * e_gamma.y + 0.101579f * e_gamma.u + 0.196076f * e_gamma.v, 0.0f * e_gamma.y + 0.989854f * e_gamma.u + -0.110653f * e_gamma.v, 0.0f * e_gamma.y + -0.072453f * e_gamma.u + 0.983398f * e_gamma.v }}}; } Color yuv709To2100(Color e_gamma) { return {{{ 1.0f * e_gamma.y + -0.016969f * e_gamma.u + 0.096312f * e_gamma.v, 0.0f * e_gamma.y + 0.995306f * e_gamma.u + -0.051192f * e_gamma.v, 0.0f * e_gamma.y + 0.011507f * e_gamma.u + 1.002637f * e_gamma.v }}}; } Color yuv601To709(Color e_gamma) { return {{{ 1.0f * e_gamma.y + -0.118188f * e_gamma.u + -0.212685f * e_gamma.v, 0.0f * e_gamma.y + 1.018640f * e_gamma.u + 0.114618f * e_gamma.v, 0.0f * e_gamma.y + 0.075049f * e_gamma.u + 1.025327f * e_gamma.v }}}; } Color yuv601To2100(Color e_gamma) { return {{{ 1.0f * e_gamma.y + -0.128245f * e_gamma.u + -0.115879f * e_gamma.v, 0.0f * e_gamma.y + 1.010016f * e_gamma.u + 0.061592f * e_gamma.v, 0.0f * e_gamma.y + 0.086969f * e_gamma.u + 1.029350f * e_gamma.v }}}; } Color yuv2100To709(Color e_gamma) { return {{{ 1.0f * e_gamma.y + 0.018149f * e_gamma.u + -0.095132f * e_gamma.v, 0.0f * e_gamma.y + 1.004123f * e_gamma.u + 0.051267f * e_gamma.v, 0.0f * e_gamma.y + -0.011524f * e_gamma.u + 0.996782f * e_gamma.v }}}; } Color yuv2100To601(Color e_gamma) { return {{{ 1.0f * e_gamma.y + 0.117887f * e_gamma.u + 0.105521f * e_gamma.v, 0.0f * e_gamma.y + 0.995211f * e_gamma.u + -0.059549f * e_gamma.v, 0.0f * e_gamma.y + -0.084085f * e_gamma.u + 0.976518f * e_gamma.v }}}; } void transformYuv420(jr_uncompressed_ptr image, size_t x_chroma, size_t y_chroma, ColorTransformFn fn) { Color yuv1 = getYuv420Pixel(image, x_chroma * 2, y_chroma * 2 ); Color yuv2 = getYuv420Pixel(image, x_chroma * 2 + 1, y_chroma * 2 ); Color yuv3 = getYuv420Pixel(image, x_chroma * 2, y_chroma * 2 + 1); Color yuv4 = getYuv420Pixel(image, x_chroma * 2 + 1, y_chroma * 2 + 1); yuv1 = fn(yuv1); yuv2 = fn(yuv2); yuv3 = fn(yuv3); yuv4 = fn(yuv4); Color new_uv = (yuv1 + yuv2 + yuv3 + yuv4) / 4.0f; size_t pixel_y1_idx = x_chroma * 2 + y_chroma * 2 * image->width; size_t pixel_y2_idx = (x_chroma * 2 + 1) + y_chroma * 2 * image->width; size_t pixel_y3_idx = x_chroma * 2 + (y_chroma * 2 + 1) * image->width; size_t pixel_y4_idx = (x_chroma * 2 + 1) + (y_chroma * 2 + 1) * image->width; uint8_t& y1_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_y1_idx]; uint8_t& y2_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_y2_idx]; uint8_t& y3_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_y3_idx]; uint8_t& y4_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_y4_idx]; size_t pixel_count = image->width * image->height; size_t pixel_uv_idx = x_chroma + y_chroma * (image->width / 2); uint8_t& u_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_count + pixel_uv_idx]; uint8_t& v_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_count * 5 / 4 + pixel_uv_idx]; y1_uint = static_cast<uint8_t>(floor(yuv1.y * 255.0f + 0.5f)); y2_uint = static_cast<uint8_t>(floor(yuv2.y * 255.0f + 0.5f)); y3_uint = static_cast<uint8_t>(floor(yuv3.y * 255.0f + 0.5f)); y4_uint = static_cast<uint8_t>(floor(yuv4.y * 255.0f + 0.5f)); u_uint = static_cast<uint8_t>(floor(new_uv.u * 255.0f + 128.0f + 0.5f)); v_uint = static_cast<uint8_t>(floor(new_uv.v * 255.0f + 128.0f + 0.5f)); } //////////////////////////////////////////////////////////////////////////////// // Gain map calculations Loading libs/ultrahdr/icc.cpp +94 −2 Original line number Diff line number Diff line Loading @@ -14,6 +14,10 @@ * limitations under the License. */ #ifndef USE_BIG_ENDIAN #define USE_BIG_ENDIAN true #endif #include <ultrahdr/icc.h> #include <ultrahdr/gainmapmath.h> #include <vector> Loading Loading @@ -540,13 +544,21 @@ sp<DataStruct> IccHelper::writeIccProfile(ultrahdr_transfer_function tf, size_t tag_table_size = kICCTagTableEntrySize * tags.size(); size_t profile_size = kICCHeaderSize + tag_table_size + tag_data_size; sp<DataStruct> dataStruct = sp<DataStruct>::make(profile_size + kICCIdentifierSize); // Write identifier, chunk count, and chunk ID if (!dataStruct->write(kICCIdentifier, sizeof(kICCIdentifier)) || !dataStruct->write8(1) || !dataStruct->write8(1)) { ALOGE("writeIccProfile(): error in identifier"); return dataStruct; } // Write the header. header.data_color_space = Endian_SwapBE32(Signature_RGB); header.pcs = Endian_SwapBE32(tf == ULTRAHDR_TF_PQ ? Signature_Lab : Signature_XYZ); header.size = Endian_SwapBE32(profile_size); header.tag_count = Endian_SwapBE32(tags.size()); sp<DataStruct> dataStruct = sp<DataStruct>::make(profile_size); if (!dataStruct->write(&header, sizeof(header))) { ALOGE("writeIccProfile(): error in header"); return dataStruct; Loading Loading @@ -582,4 +594,84 @@ sp<DataStruct> IccHelper::writeIccProfile(ultrahdr_transfer_function tf, return dataStruct; } bool IccHelper::tagsEqualToMatrix(const Matrix3x3& matrix, const uint8_t* red_tag, const uint8_t* green_tag, const uint8_t* blue_tag) { sp<DataStruct> red_tag_test = write_xyz_tag(matrix.vals[0][0], matrix.vals[1][0], matrix.vals[2][0]); sp<DataStruct> green_tag_test = write_xyz_tag(matrix.vals[0][1], matrix.vals[1][1], matrix.vals[2][1]); sp<DataStruct> blue_tag_test = write_xyz_tag(matrix.vals[0][2], matrix.vals[1][2], matrix.vals[2][2]); return memcmp(red_tag, red_tag_test->getData(), kColorantTagSize) == 0 && memcmp(green_tag, green_tag_test->getData(), kColorantTagSize) == 0 && memcmp(blue_tag, blue_tag_test->getData(), kColorantTagSize) == 0; } ultrahdr_color_gamut IccHelper::readIccColorGamut(void* icc_data, size_t icc_size) { // Each tag table entry consists of 3 fields of 4 bytes each. static const size_t kTagTableEntrySize = 12; if (icc_data == nullptr || icc_size < sizeof(ICCHeader) + kICCIdentifierSize) { return ULTRAHDR_COLORGAMUT_UNSPECIFIED; } if (memcmp(icc_data, kICCIdentifier, sizeof(kICCIdentifier)) != 0) { return ULTRAHDR_COLORGAMUT_UNSPECIFIED; } uint8_t* icc_bytes = reinterpret_cast<uint8_t*>(icc_data) + kICCIdentifierSize; ICCHeader* header = reinterpret_cast<ICCHeader*>(icc_bytes); // Use 0 to indicate not found, since offsets are always relative to start // of ICC data and therefore a tag offset of zero would never be valid. 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) { 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, // last 4 bytes are the tag length in bytes. if (red_primary_offset == 0 && *tag_entry_start == Endian_SwapBE32(kTAG_rXYZ)) { red_primary_offset = Endian_SwapBE32(*(tag_entry_start+1)); red_primary_size = Endian_SwapBE32(*(tag_entry_start+2)); } else if (green_primary_offset == 0 && *tag_entry_start == Endian_SwapBE32(kTAG_gXYZ)) { green_primary_offset = Endian_SwapBE32(*(tag_entry_start+1)); green_primary_size = Endian_SwapBE32(*(tag_entry_start+2)); } else if (blue_primary_offset == 0 && *tag_entry_start == Endian_SwapBE32(kTAG_bXYZ)) { blue_primary_offset = Endian_SwapBE32(*(tag_entry_start+1)); blue_primary_size = Endian_SwapBE32(*(tag_entry_start+2)); } } if (red_primary_offset == 0 || red_primary_size != kColorantTagSize || kICCIdentifierSize + red_primary_offset + red_primary_size > icc_size || green_primary_offset == 0 || green_primary_size != kColorantTagSize || kICCIdentifierSize + green_primary_offset + green_primary_size > icc_size || blue_primary_offset == 0 || blue_primary_size != kColorantTagSize || kICCIdentifierSize + blue_primary_offset + blue_primary_size > icc_size) { return ULTRAHDR_COLORGAMUT_UNSPECIFIED; } uint8_t* red_tag = icc_bytes + red_primary_offset; uint8_t* green_tag = icc_bytes + green_primary_offset; uint8_t* blue_tag = icc_bytes + blue_primary_offset; // Serialize tags as we do on encode and compare what we find to that to // determine the gamut (since we don't have a need yet for full deserialize). if (tagsEqualToMatrix(kSRGB, red_tag, green_tag, blue_tag)) { return ULTRAHDR_COLORGAMUT_BT709; } else if (tagsEqualToMatrix(kDisplayP3, red_tag, green_tag, blue_tag)) { return ULTRAHDR_COLORGAMUT_P3; } else if (tagsEqualToMatrix(kRec2020, red_tag, green_tag, blue_tag)) { return ULTRAHDR_COLORGAMUT_BT2100; } // Didn't find a match to one of the profiles we write; indicate the gamut // is unspecified since we don't understand it. return ULTRAHDR_COLORGAMUT_UNSPECIFIED; } } // namespace android::ultrahdr libs/ultrahdr/include/ultrahdr/gainmapmath.h +57 −8 Original line number Diff line number Diff line Loading @@ -218,24 +218,30 @@ struct ShepardsIDW { // except for those concerning transfer functions. /* * Calculate the luminance of a linear RGB sRGB pixel, according to IEC 61966-2-1. * Calculate the luminance of a linear RGB sRGB pixel, according to * IEC 61966-2-1/Amd 1:2003. * * [0.0, 1.0] range in and out. */ float srgbLuminance(Color e); /* * Convert from OETF'd srgb YUV to RGB, according to ECMA TR/98. * Convert from OETF'd srgb RGB to YUV, according to ITU-R BT.709-6. * * BT.709 YUV<->RGB matrix is used to match expectations for DataSpace. */ Color srgbYuvToRgb(Color e_gamma); Color srgbRgbToYuv(Color e_gamma); /* * Convert from OETF'd srgb RGB to YUV, according to ECMA TR/98. * Convert from OETF'd srgb YUV to RGB, according to ITU-R BT.709-6. * * BT.709 YUV<->RGB matrix is used to match expectations for DataSpace. */ Color srgbRgbToYuv(Color e_gamma); Color srgbYuvToRgb(Color e_gamma); /* * Convert from srgb to linear, according to IEC 61966-2-1. * Convert from srgb to linear, according to IEC 61966-2-1/Amd 1:2003. * * [0.0, 1.0] range in and out. */ Loading @@ -257,6 +263,20 @@ constexpr size_t kSrgbInvOETFNumEntries = 1 << kSrgbInvOETFPrecision; */ float p3Luminance(Color e); /* * Convert from OETF'd P3 RGB to YUV, according to ITU-R BT.601-7. * * BT.601 YUV<->RGB matrix is used to match expectations for DataSpace. */ Color p3RgbToYuv(Color e_gamma); /* * Convert from OETF'd P3 YUV to RGB, according to ITU-R BT.601-7. * * BT.601 YUV<->RGB matrix is used to match expectations for DataSpace. */ Color p3YuvToRgb(Color e_gamma); //////////////////////////////////////////////////////////////////////////////// // BT.2100 transformations - according to ITU-R BT.2100-2 Loading @@ -269,12 +289,16 @@ float p3Luminance(Color e); float bt2100Luminance(Color e); /* * Convert from OETF'd BT.2100 RGB to YUV. * Convert from OETF'd BT.2100 RGB to YUV, according to ITU-R BT.2100-2. * * BT.2100 YUV<->RGB matrix is used to match expectations for DataSpace. */ Color bt2100RgbToYuv(Color e_gamma); /* * Convert from OETF'd BT.2100 YUV to RGB. * Convert from OETF'd BT.2100 YUV to RGB, according to ITU-R BT.2100-2. * * BT.2100 YUV<->RGB matrix is used to match expectations for DataSpace. */ Color bt2100YuvToRgb(Color e_gamma); Loading Loading @@ -358,6 +382,31 @@ inline Color identityConversion(Color e) { return e; } */ ColorTransformFn getHdrConversionFn(ultrahdr_color_gamut sdr_gamut, ultrahdr_color_gamut hdr_gamut); /* * Convert between YUV encodings, according to ITU-R BT.709-6, ITU-R BT.601-7, and ITU-R BT.2100-2. * * Bt.709 and Bt.2100 have well-defined YUV encodings; Display-P3's is less well defined, but is * treated as Bt.601 by DataSpace, hence we do the same. */ Color yuv709To601(Color e_gamma); Color yuv709To2100(Color e_gamma); Color yuv601To709(Color e_gamma); Color yuv601To2100(Color e_gamma); Color yuv2100To709(Color e_gamma); Color yuv2100To601(Color e_gamma); /* * Performs a transformation at the chroma x and y coordinates provided on a YUV420 image. * * Apply the transformation by determining transformed YUV for each of the 4 Y + 1 UV; each Y gets * this result, and UV gets the averaged result. * * x_chroma and y_chroma should be less than or equal to half the image's width and height * respecitively, since input is 4:2:0 subsampled. */ void transformYuv420(jr_uncompressed_ptr image, size_t x_chroma, size_t y_chroma, ColorTransformFn fn); //////////////////////////////////////////////////////////////////////////////// // Gain map calculations Loading libs/ultrahdr/include/ultrahdr/icc.h +23 −2 Original line number Diff line number Diff line Loading @@ -56,12 +56,16 @@ enum { Signature_XYZ = 0x58595A20, }; typedef uint32_t FourByteTag; static inline constexpr FourByteTag SetFourByteTag(char a, char b, char c, char d) { return (((uint32_t)a << 24) | ((uint32_t)b << 16) | ((uint32_t)c << 8) | (uint32_t)d); } static constexpr char kICCIdentifier[] = "ICC_PROFILE"; // 12 for the actual identifier, +2 for the chunk count and chunk index which // will always follow. static constexpr size_t kICCIdentifierSize = 14; // This is equal to the header size according to the ICC specification (128) // plus the size of the tag count (4). We include the tag count since we // always require it to be present anyway. Loading @@ -70,6 +74,10 @@ static constexpr size_t kICCHeaderSize = 132; // Contains a signature (4), offset (4), and size (4). static constexpr size_t kICCTagTableEntrySize = 12; // size should be 20; 4 bytes for type descriptor, 4 bytes reserved, 12 // bytes for a single XYZ number type (4 bytes per coordinate). static constexpr size_t kColorantTagSize = 20; static constexpr uint32_t kDisplay_Profile = SetFourByteTag('m', 'n', 't', 'r'); static constexpr uint32_t kRGB_ColorSpace = SetFourByteTag('R', 'G', 'B', ' '); static constexpr uint32_t kXYZ_PCSSpace = SetFourByteTag('X', 'Y', 'Z', ' '); Loading Loading @@ -225,9 +233,22 @@ private: static void compute_lut_entry(const Matrix3x3& src_to_XYZD50, float rgb[3]); static sp<DataStruct> write_clut(const uint8_t* grid_points, const uint8_t* grid_16); // Checks if a set of xyz tags is equivalent to a 3x3 Matrix. Each input // tag buffer assumed to be at least kColorantTagSize in size. static bool tagsEqualToMatrix(const Matrix3x3& matrix, const uint8_t* red_tag, const uint8_t* green_tag, const uint8_t* blue_tag); public: // Output includes JPEG embedding identifier and chunk information, but not // APPx information. static sp<DataStruct> writeIccProfile(const ultrahdr_transfer_function tf, const ultrahdr_color_gamut gamut); // NOTE: this function is not robust; it can infer gamuts that IccHelper // writes out but should not be considered a reference implementation for // robust parsing of ICC profiles or their gamuts. static ultrahdr_color_gamut readIccColorGamut(void* icc_data, size_t icc_size); }; } // namespace android::ultrahdr Loading libs/ultrahdr/include/ultrahdr/jpegdecoderhelper.h +9 −6 Original line number Diff line number Diff line Loading @@ -83,11 +83,14 @@ public: */ size_t getEXIFSize(); /* * Returns the position offset of EXIF package * (4 bypes offset to FF sign, the byte after FF E1 XX XX <this byte>), * or -1 if no EXIF exists. * Returns the ICC data from the image. */ int getEXIFPos() { return mExifPos; } void* getICCPtr(); /* * Returns the decompressed ICC buffer size. This method must be called only after * calling decompressImage() or getCompressedImageParameters(). */ size_t getICCSize(); /* * Decompresses metadata of the image. All vectors are owned by the caller. */ Loading @@ -112,12 +115,12 @@ private: std::vector<JOCTET> mXMPBuffer; // The buffer that holds EXIF Data. std::vector<JOCTET> mEXIFBuffer; // The buffer that holds ICC Data. std::vector<JOCTET> mICCBuffer; // Resolution of the decompressed image. size_t mWidth; size_t mHeight; // Position of EXIF package, default value is -1 which means no EXIF package appears. size_t mExifPos; }; } /* namespace android::ultrahdr */ Loading Loading
libs/ultrahdr/gainmapmath.cpp +132 −17 Original line number Diff line number Diff line Loading @@ -119,34 +119,39 @@ static float clampPixelFloat(float value) { return (value < 0.0f) ? 0.0f : (value > kMaxPixelFloat) ? kMaxPixelFloat : value; } // See IEC 61966-2-1, Equation F.7. // See IEC 61966-2-1/Amd 1:2003, Equation F.7. static const float kSrgbR = 0.2126f, kSrgbG = 0.7152f, kSrgbB = 0.0722f; float srgbLuminance(Color e) { return kSrgbR * e.r + kSrgbG * e.g + kSrgbB * e.b; } // See ECMA TR/98, Section 7. static const float kSrgbRCr = 1.402f, kSrgbGCb = 0.34414f, kSrgbGCr = 0.71414f, kSrgbBCb = 1.772f; // See ITU-R BT.709-6, Section 3. // Uses the same coefficients for deriving luma signal as // IEC 61966-2-1/Amd 1:2003 states for luminance, so we reuse the luminance // function above. static const float kSrgbCb = 1.8556f, kSrgbCr = 1.5748f; Color srgbYuvToRgb(Color e_gamma) { return {{{ clampPixelFloat(e_gamma.y + kSrgbRCr * e_gamma.v), clampPixelFloat(e_gamma.y - kSrgbGCb * e_gamma.u - kSrgbGCr * e_gamma.v), clampPixelFloat(e_gamma.y + kSrgbBCb * e_gamma.u) }}}; Color srgbRgbToYuv(Color e_gamma) { float y_gamma = srgbLuminance(e_gamma); return {{{ y_gamma, (e_gamma.b - y_gamma) / kSrgbCb, (e_gamma.r - y_gamma) / kSrgbCr }}}; } // See ECMA TR/98, Section 7. static const float kSrgbYR = 0.299f, kSrgbYG = 0.587f, kSrgbYB = 0.114f; static const float kSrgbUR = -0.1687f, kSrgbUG = -0.3313f, kSrgbUB = 0.5f; static const float kSrgbVR = 0.5f, kSrgbVG = -0.4187f, kSrgbVB = -0.0813f; // See ITU-R BT.709-6, Section 3. // Same derivation to BT.2100's YUV->RGB, below. Similar to srgbRgbToYuv, we // can reuse the luminance coefficients since they are the same. static const float kSrgbGCb = kSrgbB * kSrgbCb / kSrgbG; static const float kSrgbGCr = kSrgbR * kSrgbCr / kSrgbG; Color srgbRgbToYuv(Color e_gamma) { return {{{ kSrgbYR * e_gamma.r + kSrgbYG * e_gamma.g + kSrgbYB * e_gamma.b, kSrgbUR * e_gamma.r + kSrgbUG * e_gamma.g + kSrgbUB * e_gamma.b, kSrgbVR * e_gamma.r + kSrgbVG * e_gamma.g + kSrgbVB * e_gamma.b }}}; Color srgbYuvToRgb(Color e_gamma) { return {{{ clampPixelFloat(e_gamma.y + kSrgbCr * e_gamma.v), clampPixelFloat(e_gamma.y - kSrgbGCb * e_gamma.u - kSrgbGCr * e_gamma.v), clampPixelFloat(e_gamma.y + kSrgbCb * e_gamma.u) }}}; } // See IEC 61966-2-1, Equations F.5 and F.6. // See IEC 61966-2-1/Amd 1:2003, Equations F.5 and F.6. float srgbInvOetf(float e_gamma) { if (e_gamma <= 0.04045f) { return e_gamma / 12.92f; Loading Loading @@ -178,13 +183,38 @@ Color srgbInvOetfLUT(Color e_gamma) { //////////////////////////////////////////////////////////////////////////////// // Display-P3 transformations // See SMPTE EG 432-1, Table 7-2. // See SMPTE EG 432-1, Equation 7-8. static const float kP3R = 0.20949f, kP3G = 0.72160f, kP3B = 0.06891f; float p3Luminance(Color e) { return kP3R * e.r + kP3G * e.g + kP3B * e.b; } // See ITU-R BT.601-7, Sections 2.5.1 and 2.5.2. // Unfortunately, calculation of luma signal differs from calculation of // luminance for Display-P3, so we can't reuse p3Luminance here. static const float kP3YR = 0.299f, kP3YG = 0.587f, kP3YB = 0.114f; static const float kP3Cb = 1.772f, kP3Cr = 1.402f; Color p3RgbToYuv(Color e_gamma) { float y_gamma = kP3YR * e_gamma.r + kP3YG * e_gamma.g + kP3YB * e_gamma.b; return {{{ y_gamma, (e_gamma.b - y_gamma) / kP3Cb, (e_gamma.r - y_gamma) / kP3Cr }}}; } // See ITU-R BT.601-7, Sections 2.5.1 and 2.5.2. // Same derivation to BT.2100's YUV->RGB, below. Similar to p3RgbToYuv, we must // use luma signal coefficients rather than the luminance coefficients. static const float kP3GCb = kP3YB * kP3Cb / kP3YG; static const float kP3GCr = kP3YR * kP3Cr / kP3YG; Color p3YuvToRgb(Color e_gamma) { return {{{ clampPixelFloat(e_gamma.y + kP3Cr * e_gamma.v), clampPixelFloat(e_gamma.y - kP3GCb * e_gamma.u - kP3GCr * e_gamma.v), clampPixelFloat(e_gamma.y + kP3Cb * e_gamma.u) }}}; } //////////////////////////////////////////////////////////////////////////////// // BT.2100 transformations - according to ITU-R BT.2100-2 Loading @@ -197,6 +227,8 @@ float bt2100Luminance(Color e) { } // See ITU-R BT.2100-2, Table 6, Derivation of colour difference signals. // BT.2100 uses the same coefficients for calculating luma signal and luminance, // so we reuse the luminance function here. static const float kBt2100Cb = 1.8814f, kBt2100Cr = 1.4746f; Color bt2100RgbToYuv(Color e_gamma) { Loading @@ -206,6 +238,10 @@ Color bt2100RgbToYuv(Color e_gamma) { (e_gamma.r - y_gamma) / kBt2100Cr }}}; } // See ITU-R BT.2100-2, Table 6, Derivation of colour difference signals. // // Similar to bt2100RgbToYuv above, we can reuse the luminance coefficients. // // Derived by inversing bt2100RgbToYuv. The derivation for R and B are pretty // straight forward; we just invert the formulas for U and V above. But deriving // the formula for G is a bit more complicated: Loading Loading @@ -440,6 +476,85 @@ ColorTransformFn getHdrConversionFn(ultrahdr_color_gamut sdr_gamut, } } // All of these conversions are derived from the respective input YUV->RGB conversion followed by // the RGB->YUV for the receiving encoding. They are consistent with the RGB<->YUV functions in this // file, given that we uses BT.709 encoding for sRGB and BT.601 encoding for Display-P3, to match // DataSpace. Color yuv709To601(Color e_gamma) { return {{{ 1.0f * e_gamma.y + 0.101579f * e_gamma.u + 0.196076f * e_gamma.v, 0.0f * e_gamma.y + 0.989854f * e_gamma.u + -0.110653f * e_gamma.v, 0.0f * e_gamma.y + -0.072453f * e_gamma.u + 0.983398f * e_gamma.v }}}; } Color yuv709To2100(Color e_gamma) { return {{{ 1.0f * e_gamma.y + -0.016969f * e_gamma.u + 0.096312f * e_gamma.v, 0.0f * e_gamma.y + 0.995306f * e_gamma.u + -0.051192f * e_gamma.v, 0.0f * e_gamma.y + 0.011507f * e_gamma.u + 1.002637f * e_gamma.v }}}; } Color yuv601To709(Color e_gamma) { return {{{ 1.0f * e_gamma.y + -0.118188f * e_gamma.u + -0.212685f * e_gamma.v, 0.0f * e_gamma.y + 1.018640f * e_gamma.u + 0.114618f * e_gamma.v, 0.0f * e_gamma.y + 0.075049f * e_gamma.u + 1.025327f * e_gamma.v }}}; } Color yuv601To2100(Color e_gamma) { return {{{ 1.0f * e_gamma.y + -0.128245f * e_gamma.u + -0.115879f * e_gamma.v, 0.0f * e_gamma.y + 1.010016f * e_gamma.u + 0.061592f * e_gamma.v, 0.0f * e_gamma.y + 0.086969f * e_gamma.u + 1.029350f * e_gamma.v }}}; } Color yuv2100To709(Color e_gamma) { return {{{ 1.0f * e_gamma.y + 0.018149f * e_gamma.u + -0.095132f * e_gamma.v, 0.0f * e_gamma.y + 1.004123f * e_gamma.u + 0.051267f * e_gamma.v, 0.0f * e_gamma.y + -0.011524f * e_gamma.u + 0.996782f * e_gamma.v }}}; } Color yuv2100To601(Color e_gamma) { return {{{ 1.0f * e_gamma.y + 0.117887f * e_gamma.u + 0.105521f * e_gamma.v, 0.0f * e_gamma.y + 0.995211f * e_gamma.u + -0.059549f * e_gamma.v, 0.0f * e_gamma.y + -0.084085f * e_gamma.u + 0.976518f * e_gamma.v }}}; } void transformYuv420(jr_uncompressed_ptr image, size_t x_chroma, size_t y_chroma, ColorTransformFn fn) { Color yuv1 = getYuv420Pixel(image, x_chroma * 2, y_chroma * 2 ); Color yuv2 = getYuv420Pixel(image, x_chroma * 2 + 1, y_chroma * 2 ); Color yuv3 = getYuv420Pixel(image, x_chroma * 2, y_chroma * 2 + 1); Color yuv4 = getYuv420Pixel(image, x_chroma * 2 + 1, y_chroma * 2 + 1); yuv1 = fn(yuv1); yuv2 = fn(yuv2); yuv3 = fn(yuv3); yuv4 = fn(yuv4); Color new_uv = (yuv1 + yuv2 + yuv3 + yuv4) / 4.0f; size_t pixel_y1_idx = x_chroma * 2 + y_chroma * 2 * image->width; size_t pixel_y2_idx = (x_chroma * 2 + 1) + y_chroma * 2 * image->width; size_t pixel_y3_idx = x_chroma * 2 + (y_chroma * 2 + 1) * image->width; size_t pixel_y4_idx = (x_chroma * 2 + 1) + (y_chroma * 2 + 1) * image->width; uint8_t& y1_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_y1_idx]; uint8_t& y2_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_y2_idx]; uint8_t& y3_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_y3_idx]; uint8_t& y4_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_y4_idx]; size_t pixel_count = image->width * image->height; size_t pixel_uv_idx = x_chroma + y_chroma * (image->width / 2); uint8_t& u_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_count + pixel_uv_idx]; uint8_t& v_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_count * 5 / 4 + pixel_uv_idx]; y1_uint = static_cast<uint8_t>(floor(yuv1.y * 255.0f + 0.5f)); y2_uint = static_cast<uint8_t>(floor(yuv2.y * 255.0f + 0.5f)); y3_uint = static_cast<uint8_t>(floor(yuv3.y * 255.0f + 0.5f)); y4_uint = static_cast<uint8_t>(floor(yuv4.y * 255.0f + 0.5f)); u_uint = static_cast<uint8_t>(floor(new_uv.u * 255.0f + 128.0f + 0.5f)); v_uint = static_cast<uint8_t>(floor(new_uv.v * 255.0f + 128.0f + 0.5f)); } //////////////////////////////////////////////////////////////////////////////// // Gain map calculations Loading
libs/ultrahdr/icc.cpp +94 −2 Original line number Diff line number Diff line Loading @@ -14,6 +14,10 @@ * limitations under the License. */ #ifndef USE_BIG_ENDIAN #define USE_BIG_ENDIAN true #endif #include <ultrahdr/icc.h> #include <ultrahdr/gainmapmath.h> #include <vector> Loading Loading @@ -540,13 +544,21 @@ sp<DataStruct> IccHelper::writeIccProfile(ultrahdr_transfer_function tf, size_t tag_table_size = kICCTagTableEntrySize * tags.size(); size_t profile_size = kICCHeaderSize + tag_table_size + tag_data_size; sp<DataStruct> dataStruct = sp<DataStruct>::make(profile_size + kICCIdentifierSize); // Write identifier, chunk count, and chunk ID if (!dataStruct->write(kICCIdentifier, sizeof(kICCIdentifier)) || !dataStruct->write8(1) || !dataStruct->write8(1)) { ALOGE("writeIccProfile(): error in identifier"); return dataStruct; } // Write the header. header.data_color_space = Endian_SwapBE32(Signature_RGB); header.pcs = Endian_SwapBE32(tf == ULTRAHDR_TF_PQ ? Signature_Lab : Signature_XYZ); header.size = Endian_SwapBE32(profile_size); header.tag_count = Endian_SwapBE32(tags.size()); sp<DataStruct> dataStruct = sp<DataStruct>::make(profile_size); if (!dataStruct->write(&header, sizeof(header))) { ALOGE("writeIccProfile(): error in header"); return dataStruct; Loading Loading @@ -582,4 +594,84 @@ sp<DataStruct> IccHelper::writeIccProfile(ultrahdr_transfer_function tf, return dataStruct; } bool IccHelper::tagsEqualToMatrix(const Matrix3x3& matrix, const uint8_t* red_tag, const uint8_t* green_tag, const uint8_t* blue_tag) { sp<DataStruct> red_tag_test = write_xyz_tag(matrix.vals[0][0], matrix.vals[1][0], matrix.vals[2][0]); sp<DataStruct> green_tag_test = write_xyz_tag(matrix.vals[0][1], matrix.vals[1][1], matrix.vals[2][1]); sp<DataStruct> blue_tag_test = write_xyz_tag(matrix.vals[0][2], matrix.vals[1][2], matrix.vals[2][2]); return memcmp(red_tag, red_tag_test->getData(), kColorantTagSize) == 0 && memcmp(green_tag, green_tag_test->getData(), kColorantTagSize) == 0 && memcmp(blue_tag, blue_tag_test->getData(), kColorantTagSize) == 0; } ultrahdr_color_gamut IccHelper::readIccColorGamut(void* icc_data, size_t icc_size) { // Each tag table entry consists of 3 fields of 4 bytes each. static const size_t kTagTableEntrySize = 12; if (icc_data == nullptr || icc_size < sizeof(ICCHeader) + kICCIdentifierSize) { return ULTRAHDR_COLORGAMUT_UNSPECIFIED; } if (memcmp(icc_data, kICCIdentifier, sizeof(kICCIdentifier)) != 0) { return ULTRAHDR_COLORGAMUT_UNSPECIFIED; } uint8_t* icc_bytes = reinterpret_cast<uint8_t*>(icc_data) + kICCIdentifierSize; ICCHeader* header = reinterpret_cast<ICCHeader*>(icc_bytes); // Use 0 to indicate not found, since offsets are always relative to start // of ICC data and therefore a tag offset of zero would never be valid. 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) { 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, // last 4 bytes are the tag length in bytes. if (red_primary_offset == 0 && *tag_entry_start == Endian_SwapBE32(kTAG_rXYZ)) { red_primary_offset = Endian_SwapBE32(*(tag_entry_start+1)); red_primary_size = Endian_SwapBE32(*(tag_entry_start+2)); } else if (green_primary_offset == 0 && *tag_entry_start == Endian_SwapBE32(kTAG_gXYZ)) { green_primary_offset = Endian_SwapBE32(*(tag_entry_start+1)); green_primary_size = Endian_SwapBE32(*(tag_entry_start+2)); } else if (blue_primary_offset == 0 && *tag_entry_start == Endian_SwapBE32(kTAG_bXYZ)) { blue_primary_offset = Endian_SwapBE32(*(tag_entry_start+1)); blue_primary_size = Endian_SwapBE32(*(tag_entry_start+2)); } } if (red_primary_offset == 0 || red_primary_size != kColorantTagSize || kICCIdentifierSize + red_primary_offset + red_primary_size > icc_size || green_primary_offset == 0 || green_primary_size != kColorantTagSize || kICCIdentifierSize + green_primary_offset + green_primary_size > icc_size || blue_primary_offset == 0 || blue_primary_size != kColorantTagSize || kICCIdentifierSize + blue_primary_offset + blue_primary_size > icc_size) { return ULTRAHDR_COLORGAMUT_UNSPECIFIED; } uint8_t* red_tag = icc_bytes + red_primary_offset; uint8_t* green_tag = icc_bytes + green_primary_offset; uint8_t* blue_tag = icc_bytes + blue_primary_offset; // Serialize tags as we do on encode and compare what we find to that to // determine the gamut (since we don't have a need yet for full deserialize). if (tagsEqualToMatrix(kSRGB, red_tag, green_tag, blue_tag)) { return ULTRAHDR_COLORGAMUT_BT709; } else if (tagsEqualToMatrix(kDisplayP3, red_tag, green_tag, blue_tag)) { return ULTRAHDR_COLORGAMUT_P3; } else if (tagsEqualToMatrix(kRec2020, red_tag, green_tag, blue_tag)) { return ULTRAHDR_COLORGAMUT_BT2100; } // Didn't find a match to one of the profiles we write; indicate the gamut // is unspecified since we don't understand it. return ULTRAHDR_COLORGAMUT_UNSPECIFIED; } } // namespace android::ultrahdr
libs/ultrahdr/include/ultrahdr/gainmapmath.h +57 −8 Original line number Diff line number Diff line Loading @@ -218,24 +218,30 @@ struct ShepardsIDW { // except for those concerning transfer functions. /* * Calculate the luminance of a linear RGB sRGB pixel, according to IEC 61966-2-1. * Calculate the luminance of a linear RGB sRGB pixel, according to * IEC 61966-2-1/Amd 1:2003. * * [0.0, 1.0] range in and out. */ float srgbLuminance(Color e); /* * Convert from OETF'd srgb YUV to RGB, according to ECMA TR/98. * Convert from OETF'd srgb RGB to YUV, according to ITU-R BT.709-6. * * BT.709 YUV<->RGB matrix is used to match expectations for DataSpace. */ Color srgbYuvToRgb(Color e_gamma); Color srgbRgbToYuv(Color e_gamma); /* * Convert from OETF'd srgb RGB to YUV, according to ECMA TR/98. * Convert from OETF'd srgb YUV to RGB, according to ITU-R BT.709-6. * * BT.709 YUV<->RGB matrix is used to match expectations for DataSpace. */ Color srgbRgbToYuv(Color e_gamma); Color srgbYuvToRgb(Color e_gamma); /* * Convert from srgb to linear, according to IEC 61966-2-1. * Convert from srgb to linear, according to IEC 61966-2-1/Amd 1:2003. * * [0.0, 1.0] range in and out. */ Loading @@ -257,6 +263,20 @@ constexpr size_t kSrgbInvOETFNumEntries = 1 << kSrgbInvOETFPrecision; */ float p3Luminance(Color e); /* * Convert from OETF'd P3 RGB to YUV, according to ITU-R BT.601-7. * * BT.601 YUV<->RGB matrix is used to match expectations for DataSpace. */ Color p3RgbToYuv(Color e_gamma); /* * Convert from OETF'd P3 YUV to RGB, according to ITU-R BT.601-7. * * BT.601 YUV<->RGB matrix is used to match expectations for DataSpace. */ Color p3YuvToRgb(Color e_gamma); //////////////////////////////////////////////////////////////////////////////// // BT.2100 transformations - according to ITU-R BT.2100-2 Loading @@ -269,12 +289,16 @@ float p3Luminance(Color e); float bt2100Luminance(Color e); /* * Convert from OETF'd BT.2100 RGB to YUV. * Convert from OETF'd BT.2100 RGB to YUV, according to ITU-R BT.2100-2. * * BT.2100 YUV<->RGB matrix is used to match expectations for DataSpace. */ Color bt2100RgbToYuv(Color e_gamma); /* * Convert from OETF'd BT.2100 YUV to RGB. * Convert from OETF'd BT.2100 YUV to RGB, according to ITU-R BT.2100-2. * * BT.2100 YUV<->RGB matrix is used to match expectations for DataSpace. */ Color bt2100YuvToRgb(Color e_gamma); Loading Loading @@ -358,6 +382,31 @@ inline Color identityConversion(Color e) { return e; } */ ColorTransformFn getHdrConversionFn(ultrahdr_color_gamut sdr_gamut, ultrahdr_color_gamut hdr_gamut); /* * Convert between YUV encodings, according to ITU-R BT.709-6, ITU-R BT.601-7, and ITU-R BT.2100-2. * * Bt.709 and Bt.2100 have well-defined YUV encodings; Display-P3's is less well defined, but is * treated as Bt.601 by DataSpace, hence we do the same. */ Color yuv709To601(Color e_gamma); Color yuv709To2100(Color e_gamma); Color yuv601To709(Color e_gamma); Color yuv601To2100(Color e_gamma); Color yuv2100To709(Color e_gamma); Color yuv2100To601(Color e_gamma); /* * Performs a transformation at the chroma x and y coordinates provided on a YUV420 image. * * Apply the transformation by determining transformed YUV for each of the 4 Y + 1 UV; each Y gets * this result, and UV gets the averaged result. * * x_chroma and y_chroma should be less than or equal to half the image's width and height * respecitively, since input is 4:2:0 subsampled. */ void transformYuv420(jr_uncompressed_ptr image, size_t x_chroma, size_t y_chroma, ColorTransformFn fn); //////////////////////////////////////////////////////////////////////////////// // Gain map calculations Loading
libs/ultrahdr/include/ultrahdr/icc.h +23 −2 Original line number Diff line number Diff line Loading @@ -56,12 +56,16 @@ enum { Signature_XYZ = 0x58595A20, }; typedef uint32_t FourByteTag; static inline constexpr FourByteTag SetFourByteTag(char a, char b, char c, char d) { return (((uint32_t)a << 24) | ((uint32_t)b << 16) | ((uint32_t)c << 8) | (uint32_t)d); } static constexpr char kICCIdentifier[] = "ICC_PROFILE"; // 12 for the actual identifier, +2 for the chunk count and chunk index which // will always follow. static constexpr size_t kICCIdentifierSize = 14; // This is equal to the header size according to the ICC specification (128) // plus the size of the tag count (4). We include the tag count since we // always require it to be present anyway. Loading @@ -70,6 +74,10 @@ static constexpr size_t kICCHeaderSize = 132; // Contains a signature (4), offset (4), and size (4). static constexpr size_t kICCTagTableEntrySize = 12; // size should be 20; 4 bytes for type descriptor, 4 bytes reserved, 12 // bytes for a single XYZ number type (4 bytes per coordinate). static constexpr size_t kColorantTagSize = 20; static constexpr uint32_t kDisplay_Profile = SetFourByteTag('m', 'n', 't', 'r'); static constexpr uint32_t kRGB_ColorSpace = SetFourByteTag('R', 'G', 'B', ' '); static constexpr uint32_t kXYZ_PCSSpace = SetFourByteTag('X', 'Y', 'Z', ' '); Loading Loading @@ -225,9 +233,22 @@ private: static void compute_lut_entry(const Matrix3x3& src_to_XYZD50, float rgb[3]); static sp<DataStruct> write_clut(const uint8_t* grid_points, const uint8_t* grid_16); // Checks if a set of xyz tags is equivalent to a 3x3 Matrix. Each input // tag buffer assumed to be at least kColorantTagSize in size. static bool tagsEqualToMatrix(const Matrix3x3& matrix, const uint8_t* red_tag, const uint8_t* green_tag, const uint8_t* blue_tag); public: // Output includes JPEG embedding identifier and chunk information, but not // APPx information. static sp<DataStruct> writeIccProfile(const ultrahdr_transfer_function tf, const ultrahdr_color_gamut gamut); // NOTE: this function is not robust; it can infer gamuts that IccHelper // writes out but should not be considered a reference implementation for // robust parsing of ICC profiles or their gamuts. static ultrahdr_color_gamut readIccColorGamut(void* icc_data, size_t icc_size); }; } // namespace android::ultrahdr Loading
libs/ultrahdr/include/ultrahdr/jpegdecoderhelper.h +9 −6 Original line number Diff line number Diff line Loading @@ -83,11 +83,14 @@ public: */ size_t getEXIFSize(); /* * Returns the position offset of EXIF package * (4 bypes offset to FF sign, the byte after FF E1 XX XX <this byte>), * or -1 if no EXIF exists. * Returns the ICC data from the image. */ int getEXIFPos() { return mExifPos; } void* getICCPtr(); /* * Returns the decompressed ICC buffer size. This method must be called only after * calling decompressImage() or getCompressedImageParameters(). */ size_t getICCSize(); /* * Decompresses metadata of the image. All vectors are owned by the caller. */ Loading @@ -112,12 +115,12 @@ private: std::vector<JOCTET> mXMPBuffer; // The buffer that holds EXIF Data. std::vector<JOCTET> mEXIFBuffer; // The buffer that holds ICC Data. std::vector<JOCTET> mICCBuffer; // Resolution of the decompressed image. size_t mWidth; size_t mHeight; // Position of EXIF package, default value is -1 which means no EXIF package appears. size_t mExifPos; }; } /* namespace android::ultrahdr */ Loading