Suggest P3 for wide gamut images in SkAndroidCodec

This will prevent us from clipping the gamut
to sRGB.

BUG=skia:

Change-Id: Ifc34369d96aa9dd92ae2af72aac1cfa17fdc4b94
Reviewed-on: https://skia-review.googlesource.com/8025
Commit-Queue: Matt Sarett <msarett@google.com>
Reviewed-by: Leon Scroggins <scroggo@google.com>
diff --git a/include/codec/SkAndroidCodec.h b/include/codec/SkAndroidCodec.h
index e30a2aa..8e9ad4e 100644
--- a/include/codec/SkAndroidCodec.h
+++ b/include/codec/SkAndroidCodec.h
@@ -79,9 +79,6 @@
      *  @param outputColorType Color type that the client will decode to
      *
      *  Returns the appropriate color space to decode to.
-     *
-     *  For now, this just returns a default.  This could be updated to take
-     *  requests for wide gamut modes or specific output spaces.
      */
     sk_sp<SkColorSpace> computeOutputColorSpace(SkColorType outputColorType);
 
diff --git a/src/codec/SkAndroidCodec.cpp b/src/codec/SkAndroidCodec.cpp
index 5fc7bfc..c1f12fa 100644
--- a/src/codec/SkAndroidCodec.cpp
+++ b/src/codec/SkAndroidCodec.cpp
@@ -17,6 +17,49 @@
     return sampleSize > 0;
 }
 
+/**
+ *  Loads the gamut as a set of three points (triangle).
+ */
+static void load_gamut(SkPoint rgb[], const SkMatrix44& xyz) {
+    // rx = rX / (rX + rY + rZ)
+    // ry = rY / (rX + rY + rZ)
+    // gx, gy, bx, and gy are calulcated similarly.
+    float rSum = xyz.get(0, 0) + xyz.get(1, 0) + xyz.get(2, 0);
+    float gSum = xyz.get(0, 1) + xyz.get(1, 1) + xyz.get(2, 1);
+    float bSum = xyz.get(0, 2) + xyz.get(1, 2) + xyz.get(2, 2);
+    rgb[0].fX = xyz.get(0, 0) / rSum;
+    rgb[0].fY = xyz.get(1, 0) / rSum;
+    rgb[1].fX = xyz.get(0, 1) / gSum;
+    rgb[1].fY = xyz.get(1, 1) / gSum;
+    rgb[2].fX = xyz.get(0, 2) / bSum;
+    rgb[2].fY = xyz.get(1, 2) / bSum;
+}
+
+/**
+ *  Calculates the area of the triangular gamut.
+ */
+static float calculate_area(SkPoint abc[]) {
+    SkPoint a = abc[0];
+    SkPoint b = abc[1];
+    SkPoint c = abc[2];
+    return 0.5f * SkTAbs(a.fX*b.fY + b.fX*c.fY - a.fX*c.fY - c.fX*b.fY - b.fX*a.fY);
+}
+
+static const float kSRGB_D50_GamutArea = 0.084f;
+
+static bool is_wide_gamut(const SkColorSpace* colorSpace) {
+    // Determine if the source image has a gamut that is wider than sRGB.  If so, we
+    // will use P3 as the output color space to avoid clipping the gamut.
+    const SkMatrix44* toXYZD50 = as_CSB(colorSpace)->toXYZD50();
+    if (toXYZD50) {
+        SkPoint rgb[3];
+        load_gamut(rgb, *toXYZD50);
+        return calculate_area(rgb) > kSRGB_D50_GamutArea;
+    }
+
+    return false;
+}
+
 SkAndroidCodec::SkAndroidCodec(SkCodec* codec)
     : fInfo(codec->getInfo())
     , fCodec(codec)
@@ -131,6 +174,11 @@
         case kRGBA_8888_SkColorType:
         case kBGRA_8888_SkColorType:
         case kIndex_8_SkColorType:
+            if (is_wide_gamut(fCodec->getInfo().colorSpace())) {
+                return SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
+                                             SkColorSpace::kDCIP3_D65_Gamut);
+            }
+
             return SkColorSpace::MakeSRGB();
         case kRGBA_F16_SkColorType:
             return SkColorSpace::MakeSRGBLinear();