ultrahdr icc: update transfer function

Bug: 286444758
Test: jpegr_test.cpp
Manual test:
(1) append icc package to red-noicc.jpeg (attached in
the bug comment #1), then open the image with digital color meter, color measurement should be close to (255, 0, 0).
(2) Dump icc into a file and open the icc file on Mac, read data of each component (wtpt, r/g/bXYZ, r/g/bTRC) that should be the same as the samples in comment #10

Change-Id: I3b4a2b7d283fc143207d2dbce570eadf7568ebec
diff --git a/libs/ultrahdr/icc.cpp b/libs/ultrahdr/icc.cpp
index 1ab3c7c..e41b645 100644
--- a/libs/ultrahdr/icc.cpp
+++ b/libs/ultrahdr/icc.cpp
@@ -19,7 +19,6 @@
 #endif
 
 #include <ultrahdr/icc.h>
-#include <ultrahdr/gainmapmath.h>
 #include <vector>
 #include <utils/Log.h>
 
@@ -218,8 +217,8 @@
     total_length = (((total_length + 2) >> 2) << 2);  // 4 aligned
     sp<DataStruct> dataStruct = sp<DataStruct>::make(total_length);
     dataStruct->write32(Endian_SwapBE32(kTAG_CurveType));     // Type
-    dataStruct->write32(0);                                     // Reserved
-    dataStruct->write32(Endian_SwapBE32(table_entries));  // Value count
+    dataStruct->write32(0);                                   // Reserved
+    dataStruct->write32(Endian_SwapBE32(table_entries));      // Value count
     for (size_t i = 0; i < table_entries; ++i) {
         uint16_t value = reinterpret_cast<const uint16_t*>(table_16)[i];
         dataStruct->write16(value);
@@ -227,14 +226,30 @@
     return dataStruct;
 }
 
-sp<DataStruct> IccHelper::write_trc_tag_for_linear() {
-    int total_length = 16;
-    sp<DataStruct> dataStruct = sp<DataStruct>::make(total_length);
-    dataStruct->write32(Endian_SwapBE32(kTAG_ParaCurveType));  // Type
-    dataStruct->write32(0);                                      // Reserved
-    dataStruct->write32(Endian_SwapBE16(kExponential_ParaCurveType));
-    dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(1.0)));
+sp<DataStruct> IccHelper::write_trc_tag(const TransferFunction& fn) {
+    if (fn.a == 1.f && fn.b == 0.f && fn.c == 0.f
+            && fn.d == 0.f && fn.e == 0.f && fn.f == 0.f) {
+        int total_length = 16;
+        sp<DataStruct> dataStruct = new DataStruct(total_length);
+        dataStruct->write32(Endian_SwapBE32(kTAG_ParaCurveType));  // Type
+        dataStruct->write32(0);                                    // Reserved
+        dataStruct->write32(Endian_SwapBE16(kExponential_ParaCurveType));
+        dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.g)));
+        return dataStruct;
+    }
 
+    int total_length = 40;
+    sp<DataStruct> dataStruct = new DataStruct(total_length);
+    dataStruct->write32(Endian_SwapBE32(kTAG_ParaCurveType));  // Type
+    dataStruct->write32(0);                                    // Reserved
+    dataStruct->write32(Endian_SwapBE16(kGABCDEF_ParaCurveType));
+    dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.g)));
+    dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.a)));
+    dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.b)));
+    dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.c)));
+    dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.d)));
+    dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.e)));
+    dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.f)));
     return dataStruct;
 }
 
@@ -349,7 +364,7 @@
 
     // The "B" curve is required.
     for (size_t i = 0; i < kNumChannels; ++i) {
-        b_curves_data[i] = write_trc_tag_for_linear();
+        b_curves_data[i] = write_trc_tag(kLinear_TransFun);
     }
 
     // The "A" curve and CLUT are optional.
@@ -362,7 +377,7 @@
 
         a_curves_offset = clut_offset + clut->getLength();
         for (size_t i = 0; i < kNumChannels; ++i) {
-            a_curves_data[i] = write_trc_tag_for_linear();
+            a_curves_data[i] = write_trc_tag(kLinear_TransFun);
         }
     }
 
@@ -460,9 +475,9 @@
             tags.emplace_back(kTAG_bTRC,
                     write_trc_tag(kTrcTableSize, reinterpret_cast<uint8_t*>(trc_table.data())));
         } else {
-            tags.emplace_back(kTAG_rTRC, write_trc_tag_for_linear());
-            tags.emplace_back(kTAG_gTRC, write_trc_tag_for_linear());
-            tags.emplace_back(kTAG_bTRC, write_trc_tag_for_linear());
+            tags.emplace_back(kTAG_rTRC, write_trc_tag(kSRGB_TransFun));
+            tags.emplace_back(kTAG_gTRC, write_trc_tag(kSRGB_TransFun));
+            tags.emplace_back(kTAG_bTRC, write_trc_tag(kSRGB_TransFun));
         }
     }
 
diff --git a/libs/ultrahdr/include/ultrahdr/gainmapmath.h b/libs/ultrahdr/include/ultrahdr/gainmapmath.h
index edf152d..6115dfe 100644
--- a/libs/ultrahdr/include/ultrahdr/gainmapmath.h
+++ b/libs/ultrahdr/include/ultrahdr/gainmapmath.h
@@ -51,6 +51,23 @@
 typedef Color (*ColorTransformFn)(Color);
 typedef float (*ColorCalculationFn)(Color);
 
+// A transfer function mapping encoded values to linear values,
+// represented by this 7-parameter piecewise function:
+//
+//   linear = sign(encoded) *  (c*|encoded| + f)       , 0 <= |encoded| < d
+//          = sign(encoded) * ((a*|encoded| + b)^g + e), d <= |encoded|
+//
+// (A simple gamma transfer function sets g to gamma and a to 1.)
+typedef struct TransferFunction {
+    float g, a,b,c,d,e,f;
+} TransferFunction;
+
+static constexpr TransferFunction kSRGB_TransFun =
+    { 2.4f, (float)(1/1.055), (float)(0.055/1.055), (float)(1/12.92), 0.04045f, 0.0f, 0.0f };
+
+static constexpr TransferFunction kLinear_TransFun =
+    { 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+
 inline Color operator+=(Color& lhs, const Color& rhs) {
   lhs.r += rhs.r;
   lhs.g += rhs.g;
diff --git a/libs/ultrahdr/include/ultrahdr/icc.h b/libs/ultrahdr/include/ultrahdr/icc.h
index 7f047f8..971b267 100644
--- a/libs/ultrahdr/include/ultrahdr/icc.h
+++ b/libs/ultrahdr/include/ultrahdr/icc.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_ULTRAHDR_ICC_H
 #define ANDROID_ULTRAHDR_ICC_H
 
+#include <ultrahdr/gainmapmath.h>
 #include <ultrahdr/jpegr.h>
 #include <ultrahdr/jpegrutils.h>
 #include <utils/RefBase.h>
@@ -222,7 +223,7 @@
                                        const ultrahdr_color_gamut gamut);
     static sp<DataStruct> write_xyz_tag(float x, float y, float z);
     static sp<DataStruct> write_trc_tag(const int table_entries, const void* table_16);
-    static sp<DataStruct> write_trc_tag_for_linear();
+    static sp<DataStruct> write_trc_tag(const TransferFunction& fn);
     static float compute_tone_map_gain(const ultrahdr_transfer_function tf, float L);
     static sp<DataStruct> write_cicp_tag(uint32_t color_primaries,
                                          uint32_t transfer_characteristics);