Normalize channels when transforming CMYK to RGB

PDF spec, section 'DeviceCMYK Color Space' states that:
'Each component is specified by a number in the range 0.0 to 1.0'

BUG=chromium:699459

Change-Id: Ie476bbe8740c7b1b33c80be3e3dc4207df5e6e28
Reviewed-on: https://pdfium-review.googlesource.com/2954
Commit-Queue: Nicolás Peña <npm@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index 8656ee7..2a5a6bd 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1745,6 +1745,7 @@
     "core/fpdfapi/edit/cpdf_pagecontentgenerator_unittest.cpp",
     "core/fpdfapi/font/fpdf_font_cid_unittest.cpp",
     "core/fpdfapi/font/fpdf_font_unittest.cpp",
+    "core/fpdfapi/page/cpdf_devicecs_unittest.cpp",
     "core/fpdfapi/page/cpdf_streamcontentparser_unittest.cpp",
     "core/fpdfapi/page/cpdf_streamparser_unittest.cpp",
     "core/fpdfapi/parser/cpdf_array_unittest.cpp",
diff --git a/core/fpdfapi/page/cpdf_devicecs_unittest.cpp b/core/fpdfapi/page/cpdf_devicecs_unittest.cpp
new file mode 100644
index 0000000..287fc02
--- /dev/null
+++ b/core/fpdfapi/page/cpdf_devicecs_unittest.cpp
@@ -0,0 +1,115 @@
+// Copyright 2017 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fpdfapi/page/pageint.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(CPDF_DeviceCSTest, GetRGBFromGray) {
+  FX_FLOAT R;
+  FX_FLOAT G;
+  FX_FLOAT B;
+  CPDF_DeviceCS deviceGray(nullptr, PDFCS_DEVICEGRAY);
+
+  // Test normal values. For gray, only first value from buf should be used.
+  FX_FLOAT buf[3] = {0.43f, 0.11f, 0.34f};
+  ASSERT_TRUE(deviceGray.GetRGB(buf, R, G, B));
+  EXPECT_EQ(0.43f, R);
+  EXPECT_EQ(0.43f, G);
+  EXPECT_EQ(0.43f, B);
+  buf[0] = 0.872f;
+  ASSERT_TRUE(deviceGray.GetRGB(buf, R, G, B));
+  EXPECT_EQ(0.872f, R);
+  EXPECT_EQ(0.872f, G);
+  EXPECT_EQ(0.872f, B);
+
+  // Test boundary values
+  buf[0] = {0.0f};
+  ASSERT_TRUE(deviceGray.GetRGB(buf, R, G, B));
+  EXPECT_EQ(0.0f, R);
+  EXPECT_EQ(0.0f, G);
+  EXPECT_EQ(0.0f, B);
+  buf[0] = 1.0f;
+  ASSERT_TRUE(deviceGray.GetRGB(buf, R, G, B));
+  EXPECT_EQ(1.0f, R);
+  EXPECT_EQ(1.0f, G);
+  EXPECT_EQ(1.0f, B);
+
+  // Test out of range values
+  buf[0] = -0.01f;
+  ASSERT_TRUE(deviceGray.GetRGB(buf, R, G, B));
+  EXPECT_EQ(0.0f, R);
+  EXPECT_EQ(0.0f, G);
+  EXPECT_EQ(0.0f, B);
+  buf[0] = 12.5f;
+  ASSERT_TRUE(deviceGray.GetRGB(buf, R, G, B));
+  EXPECT_EQ(1.0f, R);
+  EXPECT_EQ(1.0f, G);
+  EXPECT_EQ(1.0f, B);
+}
+
+TEST(CPDF_DeviceCSTest, GetRGBFromRGB) {
+  FX_FLOAT R;
+  FX_FLOAT G;
+  FX_FLOAT B;
+  CPDF_DeviceCS deviceRGB(nullptr, PDFCS_DEVICERGB);
+
+  // Test normal values
+  FX_FLOAT buf[3] = {0.13f, 1.0f, 0.652f};
+  ASSERT_TRUE(deviceRGB.GetRGB(buf, R, G, B));
+  EXPECT_EQ(0.13f, R);
+  EXPECT_EQ(1.0f, G);
+  EXPECT_EQ(0.652f, B);
+  buf[0] = 0.0f;
+  buf[1] = 0.52f;
+  buf[2] = 0.78f;
+  ASSERT_TRUE(deviceRGB.GetRGB(buf, R, G, B));
+  EXPECT_EQ(0.0f, R);
+  EXPECT_EQ(0.52f, G);
+  EXPECT_EQ(0.78f, B);
+
+  // Test out of range values
+  buf[0] = -10.5f;
+  buf[1] = 100.0f;
+  ASSERT_TRUE(deviceRGB.GetRGB(buf, R, G, B));
+  EXPECT_EQ(0.0f, R);
+  EXPECT_EQ(1.0f, G);
+  EXPECT_EQ(0.78f, B);
+}
+
+TEST(CPDF_DeviceCSTest, GetRGBFromCMYK) {
+  FX_FLOAT R;
+  FX_FLOAT G;
+  FX_FLOAT B;
+  CPDF_DeviceCS deviceCMYK(nullptr, PDFCS_DEVICECMYK);
+  // Use an error threshold because of the calculations used here.
+  FX_FLOAT eps = 1e-6f;
+  // Test normal values
+  FX_FLOAT buf[4] = {0.6f, 0.5f, 0.3f, 0.9f};
+  ASSERT_TRUE(deviceCMYK.GetRGB(buf, R, G, B));
+  EXPECT_TRUE(std::abs(0.0627451f - R) < eps);
+  EXPECT_TRUE(std::abs(0.0627451f - G) < eps);
+  EXPECT_TRUE(std::abs(0.105882f - B) < eps);
+  buf[0] = 0.15f;
+  buf[2] = 0.0f;
+  ASSERT_TRUE(deviceCMYK.GetRGB(buf, R, G, B));
+  EXPECT_TRUE(std::abs(0.2f - R) < eps);
+  EXPECT_TRUE(std::abs(0.0862745f - G) < eps);
+  EXPECT_TRUE(std::abs(0.164706f - B) < eps);
+  buf[2] = 1.0f;
+  buf[3] = 0.0f;
+  ASSERT_TRUE(deviceCMYK.GetRGB(buf, R, G, B));
+  EXPECT_TRUE(std::abs(0.850980f - R) < eps);
+  EXPECT_TRUE(std::abs(0.552941f - G) < eps);
+  EXPECT_TRUE(std::abs(0.156863f - B) < eps);
+
+  // Test out of range values
+  buf[2] = 1.5f;
+  buf[3] = -0.6f;
+  ASSERT_TRUE(deviceCMYK.GetRGB(buf, R, G, B));
+  EXPECT_TRUE(std::abs(0.850980f - R) < eps);
+  EXPECT_TRUE(std::abs(0.552941f - G) < eps);
+  EXPECT_TRUE(std::abs(0.156863f - B) < eps);
+}
diff --git a/core/fpdfapi/page/fpdf_page_colors.cpp b/core/fpdfapi/page/fpdf_page_colors.cpp
index 69129e4..54b61df 100644
--- a/core/fpdfapi/page/fpdf_page_colors.cpp
+++ b/core/fpdfapi/page/fpdf_page_colors.cpp
@@ -46,13 +46,7 @@
   c = 1.0f - R;
   m = 1.0f - G;
   y = 1.0f - B;
-  k = c;
-  if (m < k) {
-    k = m;
-  }
-  if (y < k) {
-    k = y;
-  }
+  k = std::min(c, std::min(m, y));
 }
 
 void ReverseRGB(uint8_t* pDestBuf, const uint8_t* pSrcBuf, int pixels) {
@@ -101,7 +95,9 @@
         G = 1.0f - std::min(1.0f, pBuf[1] + k);
         B = 1.0f - std::min(1.0f, pBuf[2] + k);
       } else {
-        AdobeCMYK_to_sRGB(pBuf[0], pBuf[1], pBuf[2], pBuf[3], R, G, B);
+        AdobeCMYK_to_sRGB(NormalizeChannel(pBuf[0]), NormalizeChannel(pBuf[1]),
+                          NormalizeChannel(pBuf[2]), NormalizeChannel(pBuf[3]),
+                          R, G, B);
       }
       break;
     default: