CalRGB color correction
authorBo Xu <bo_xu@foxitsoftware.com>
Fri, 25 Jul 2014 00:50:59 +0000 (17:50 -0700)
committerBo Xu <bo_xu@foxitsoftware.com>
Fri, 25 Jul 2014 00:50:59 +0000 (17:50 -0700)
BUG=pdfium:19
R=thestig@chromium.org

Review URL: https://codereview.chromium.org/403163002

core/include/fxcrt/fx_basic.h
core/src/fpdfapi/fpdf_page/fpdf_page_colors.cpp
core/src/fxcrt/fx_basic_util.cpp

index ece2b43..22ba611 100644 (file)
@@ -1578,4 +1578,50 @@ typedef enum {
 } FX_ProgressiveStatus;
 #define ProgressiveStatus      FX_ProgressiveStatus
 #define FX_NAMESPACE_DECLARE(namespace, type)       namespace::type
+
+class CFX_Vector_3by1 : public CFX_Object
+{
+public:
+
+    CFX_Vector_3by1() :
+        a(0.0f), b(0.0f), c(0.0f)
+    {}
+
+    CFX_Vector_3by1(FX_FLOAT a1, FX_FLOAT b1, FX_FLOAT c1):
+        a(a1), b(b1), c(c1)
+    {}
+
+    FX_FLOAT a;
+    FX_FLOAT b;
+    FX_FLOAT c;
+};
+class CFX_Matrix_3by3 : public CFX_Object
+{
+public:
+
+    CFX_Matrix_3by3():
+        a(0.0f), b(0.0f), c(0.0f), d(0.0f), e(0.0f), f(0.0f), g(0.0f), h(0.0f), i(0.0f)
+    {}
+
+    CFX_Matrix_3by3(FX_FLOAT a1, FX_FLOAT b1, FX_FLOAT c1, FX_FLOAT d1, FX_FLOAT e1, FX_FLOAT f1, FX_FLOAT g1, FX_FLOAT h1, FX_FLOAT i1) :
+        a(a1), b(b1), c(c1), d(d1), e(e1), f(f1), g(g1), h(h1), i(i1)
+    {}
+
+    CFX_Matrix_3by3 Inverse();
+
+    CFX_Matrix_3by3 Multiply(const CFX_Matrix_3by3 &m);
+
+    CFX_Vector_3by1 TransformVector(const CFX_Vector_3by1 &v);
+
+    FX_FLOAT a;
+    FX_FLOAT b;
+    FX_FLOAT c;
+    FX_FLOAT d;
+    FX_FLOAT e;
+    FX_FLOAT f;
+    FX_FLOAT g;
+    FX_FLOAT h;
+    FX_FLOAT i;
+};
+
 #endif
index 1cd1097..467fb63 100644 (file)
@@ -207,56 +207,59 @@ const FX_BYTE g_sRGBSamples2[] = {
     241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 246, 246, 247, 247, 248,
     248, 249, 249, 250, 250, 251, 251, 251, 252, 252, 253, 253, 254, 254, 255, 255,
 };
-static void XYZ_to_sRGB(FX_FLOAT X, FX_FLOAT Y, FX_FLOAT Z, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B)
+
+static FX_FLOAT RGB_Conversion(FX_FLOAT colorComponent)
 {
-    FX_FLOAT R1 = 3.2410f * X - 1.5374f * Y - 0.4986f * Z;
-    FX_FLOAT G1 = -0.9692f * X + 1.8760f * Y + 0.0416f * Z;
-    FX_FLOAT B1 =  0.0556f * X - 0.2040f * Y + 1.0570f * Z;
-    if (R1 > 1) {
-        R1 = 1;
-    }
-    if (R1 < 0) {
-        R1 = 0;
-    }
-    if (G1 > 1) {
-        G1 = 1;
-    }
-    if (G1 < 0) {
-        G1 = 0;
-    }
-    if (B1 > 1) {
-        B1 = 1;
+    if (colorComponent > 1) {
+        colorComponent = 1;
     }
-    if (B1 < 0) {
-        B1 = 0;
+    if (colorComponent < 0) {
+        colorComponent = 0;
     }
-    int scale = (int)(R1 * 1023);
+    int scale = (int)(colorComponent * 1023);
     if (scale < 0) {
         scale = 0;
     }
     if (scale < 192) {
-        R = (g_sRGBSamples1[scale] / 255.0f);
-    } else {
-        R = (g_sRGBSamples2[scale / 4 - 48] / 255.0f);
-    }
-    scale = (int)(G1 * 1023);
-    if (scale < 0) {
-        scale = 0;
-    }
-    if (scale < 192) {
-        G = (g_sRGBSamples1[scale] / 255.0f);
-    } else {
-        G = (g_sRGBSamples2[scale / 4 - 48] / 255.0f);
-    }
-    scale = (int)(B1 * 1023);
-    if (scale < 0) {
-        scale = 0;
+        colorComponent = (g_sRGBSamples1[scale] / 255.0f);
     }
-    if (scale < 192) {
-        B = (g_sRGBSamples1[scale] / 255.0f);
-    } else {
-        B = (g_sRGBSamples2[scale / 4 - 48] / 255.0f);
+    else {
+        colorComponent = (g_sRGBSamples2[scale / 4 - 48] / 255.0f);
     }
+    return colorComponent;
+}
+
+static void XYZ_to_sRGB(FX_FLOAT X, FX_FLOAT Y, FX_FLOAT Z, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B)
+{
+    FX_FLOAT R1 = 3.2410f * X - 1.5374f * Y - 0.4986f * Z;
+    FX_FLOAT G1 = -0.9692f * X + 1.8760f * Y + 0.0416f * Z;
+    FX_FLOAT B1 =  0.0556f * X - 0.2040f * Y + 1.0570f * Z;
+
+    R = RGB_Conversion(R1);
+    G = RGB_Conversion(G1);
+    B = RGB_Conversion(B1);
+}
+
+static void XYZ_to_sRGB_WhitePoint(FX_FLOAT X, FX_FLOAT Y, FX_FLOAT Z, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT& B, FX_FLOAT Xw, FX_FLOAT Yw, FX_FLOAT Zw)
+{
+    // The following RGB_xyz is based on
+    // sRGB value {Rx,Ry}={0.64, 0.33}, {Gx,Gy}={0.30, 0.60}, {Bx,By}={0.15, 0.06}
+
+    FX_FLOAT Rx = 0.64f, Ry = 0.33f;
+    FX_FLOAT Gx = 0.30f, Gy = 0.60f;
+    FX_FLOAT Bx = 0.15f, By = 0.06f;
+    CFX_Matrix_3by3 RGB_xyz(Rx, Gx, Bx, Ry, Gy, By, 1 - Rx - Ry, 1 - Gx - Gy, 1 - Bx - By);
+    CFX_Vector_3by1 whitePoint(Xw, Yw, Zw);
+    CFX_Vector_3by1 XYZ(X, Y, Z);
+
+    CFX_Vector_3by1 RGB_Sum_XYZ = RGB_xyz.Inverse().TransformVector(whitePoint);
+    CFX_Matrix_3by3 RGB_SUM_XYZ_DIAG(RGB_Sum_XYZ.a, 0, 0, 0, RGB_Sum_XYZ.b, 0, 0, 0, RGB_Sum_XYZ.c);
+    CFX_Matrix_3by3 M = RGB_xyz.Multiply(RGB_SUM_XYZ_DIAG);
+    CFX_Vector_3by1 RGB = M.Inverse().TransformVector(XYZ);
+
+    R = RGB_Conversion(RGB.a);
+    G = RGB_Conversion(RGB.b);
+    B = RGB_Conversion(RGB.c);
 }
 class CPDF_CalGray : public CPDF_ColorSpace
 {
@@ -386,7 +389,7 @@ FX_BOOL CPDF_CalRGB::GetRGB(FX_FLOAT* pBuf, FX_FLOAT& R, FX_FLOAT& G, FX_FLOAT&
         Y = B_;
         Z = C_;
     }
-    XYZ_to_sRGB(X, Y, Z, R, G, B);
+    XYZ_to_sRGB_WhitePoint(X, Y, Z, R, G, B, m_WhitePoint[0], m_WhitePoint[1], m_WhitePoint[2]);
     return TRUE;
 }
 FX_BOOL CPDF_CalRGB::SetRGB(FX_FLOAT* pBuf, FX_FLOAT R, FX_FLOAT G, FX_FLOAT B) const
index dc5eea7..5a40c2b 100644 (file)
@@ -442,3 +442,46 @@ FX_WCHAR FX_GetFolderSeparator()
     return '/';
 #endif
 }
+
+CFX_Matrix_3by3 CFX_Matrix_3by3::Inverse()
+{
+    FX_FLOAT det = a*(e*i - f*h) - b*(i*d - f*g) + c*(d*h - e*g);
+    if (FXSYS_fabs(det) < 0.0000001)
+        return CFX_Matrix_3by3();
+    else
+        return CFX_Matrix_3by3(
+            (e*i - f*h) / det,
+            -(b*i - c*h) / det,
+            (b*f - c*e) / det,
+            -(d*i - f*g) / det,
+            (a*i - c*g) / det,
+            -(a*f - c*d) / det,
+            (d*h - e*g) / det,
+            -(a*h - b*g) / det,
+            (a*e - b*d) / det
+        );
+}
+
+CFX_Matrix_3by3 CFX_Matrix_3by3::Multiply(const CFX_Matrix_3by3 &m)
+{
+    return CFX_Matrix_3by3(
+        a*m.a + b*m.d + c*m.g,
+        a*m.b + b*m.e + c*m.h,
+        a*m.c + b*m.f + c*m.i,
+        d*m.a + e*m.d + f*m.g,
+        d*m.b + e*m.e + f*m.h,
+        d*m.c + e*m.f + f*m.i,
+        g*m.a + h*m.d + i*m.g,
+        g*m.b + h*m.e + i*m.h,
+        g*m.c + h*m.f + i*m.i
+      );
+}
+
+CFX_Vector_3by1 CFX_Matrix_3by3::TransformVector(const CFX_Vector_3by1 &v)
+{
+    return CFX_Vector_3by1(
+        a * v.a + b * v.b + c * v.c,
+        d * v.a + e * v.b + f * v.c,
+        g * v.a + h * v.b + i * v.c
+    );
+}