| OLD | NEW |
| 1 // Copyright (c) 2016 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/gfx/color_transform.h" | 5 #include "ui/gfx/color_transform.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <cmath> | 8 #include <cmath> |
| 9 #include <list> | 9 #include <list> |
| 10 #include <memory> | 10 #include <memory> |
| 11 #include <sstream> |
| 11 | 12 |
| 12 #include "base/logging.h" | 13 #include "base/logging.h" |
| 13 #include "base/memory/ptr_util.h" | 14 #include "base/memory/ptr_util.h" |
| 14 #include "base/strings/stringprintf.h" | 15 #include "base/strings/stringprintf.h" |
| 15 #include "third_party/qcms/src/qcms.h" | 16 #include "third_party/qcms/src/qcms.h" |
| 16 #include "ui/gfx/color_space.h" | 17 #include "ui/gfx/color_space.h" |
| 17 #include "ui/gfx/icc_profile.h" | 18 #include "ui/gfx/icc_profile.h" |
| 18 #include "ui/gfx/skia_color_space_util.h" | 19 #include "ui/gfx/skia_color_space_util.h" |
| 19 #include "ui/gfx/transform.h" | 20 #include "ui/gfx/transform.h" |
| 20 | 21 |
| 21 #ifndef THIS_MUST_BE_INCLUDED_AFTER_QCMS_H | 22 #ifndef THIS_MUST_BE_INCLUDED_AFTER_QCMS_H |
| 22 extern "C" { | 23 extern "C" { |
| 23 #include "third_party/qcms/src/chain.h" | 24 #include "third_party/qcms/src/chain.h" |
| 24 }; | 25 }; |
| 25 #endif | 26 #endif |
| 26 | 27 |
| 27 using std::exp; | 28 using std::exp; |
| 28 using std::log; | 29 using std::log; |
| 29 using std::max; | 30 using std::max; |
| 30 using std::min; | 31 using std::min; |
| 31 using std::pow; | 32 using std::pow; |
| 32 using std::sqrt; | 33 using std::sqrt; |
| 33 | 34 |
| 34 namespace gfx { | 35 namespace gfx { |
| 35 | 36 |
| 36 namespace { | 37 namespace { |
| 37 | 38 |
| 39 void InitStringStream(std::stringstream* ss) { |
| 40 ss->imbue(std::locale::classic()); |
| 41 ss->precision(8); |
| 42 *ss << std::scientific; |
| 43 } |
| 44 |
| 38 std::string Str(float f) { | 45 std::string Str(float f) { |
| 39 return base::StringPrintf("%+1.8e", f); | 46 std::stringstream ss; |
| 47 InitStringStream(&ss); |
| 48 ss << f; |
| 49 return ss.str(); |
| 40 } | 50 } |
| 41 | 51 |
| 42 // Helper for scoped QCMS profiles. | 52 // Helper for scoped QCMS profiles. |
| 43 struct QcmsProfileDeleter { | 53 struct QcmsProfileDeleter { |
| 44 void operator()(qcms_profile* p) { | 54 void operator()(qcms_profile* p) { |
| 45 if (p) { | 55 if (p) { |
| 46 qcms_profile_release(p); | 56 qcms_profile_release(p); |
| 47 } | 57 } |
| 48 } | 58 } |
| 49 }; | 59 }; |
| 50 using ScopedQcmsProfile = std::unique_ptr<qcms_profile, QcmsProfileDeleter>; | 60 using ScopedQcmsProfile = std::unique_ptr<qcms_profile, QcmsProfileDeleter>; |
| 51 | 61 |
| 52 Transform Invert(const Transform& t) { | 62 Transform Invert(const Transform& t) { |
| 53 Transform ret = t; | 63 Transform ret = t; |
| 54 if (!t.GetInverse(&ret)) { | 64 if (!t.GetInverse(&ret)) { |
| 55 LOG(ERROR) << "Inverse should alsways be possible."; | 65 LOG(ERROR) << "Inverse should always be possible."; |
| 56 } | 66 } |
| 57 return ret; | 67 return ret; |
| 58 } | 68 } |
| 59 | 69 |
| 60 float FromLinear(ColorSpace::TransferID id, float v) { | 70 float FromLinear(ColorSpace::TransferID id, float v) { |
| 61 switch (id) { | 71 switch (id) { |
| 62 case ColorSpace::TransferID::SMPTEST2084_NON_HDR: | 72 case ColorSpace::TransferID::SMPTEST2084_NON_HDR: |
| 63 // Should already be handled. | 73 // Should already be handled. |
| 64 break; | 74 break; |
| 65 | 75 |
| (...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 252 | 262 |
| 253 // Join methods, returns true if the |next| transform was successfully | 263 // Join methods, returns true if the |next| transform was successfully |
| 254 // assimilated into |this|. | 264 // assimilated into |this|. |
| 255 // If Join() returns true, |next| is no longer needed and can be deleted. | 265 // If Join() returns true, |next| is no longer needed and can be deleted. |
| 256 virtual bool Join(ColorTransformStep* next) { return false; } | 266 virtual bool Join(ColorTransformStep* next) { return false; } |
| 257 | 267 |
| 258 // Return true if this is a null transform. | 268 // Return true if this is a null transform. |
| 259 virtual bool IsNull() { return false; } | 269 virtual bool IsNull() { return false; } |
| 260 virtual void Transform(ColorTransform::TriStim* color, size_t num) const = 0; | 270 virtual void Transform(ColorTransform::TriStim* color, size_t num) const = 0; |
| 261 virtual bool CanAppendShaderSource() { return false; } | 271 virtual bool CanAppendShaderSource() { return false; } |
| 262 virtual void AppendShaderSource(std::string* result) { NOTREACHED(); } | 272 virtual void AppendShaderSource(std::stringstream* result) { NOTREACHED(); } |
| 263 | 273 |
| 264 private: | 274 private: |
| 265 DISALLOW_COPY_AND_ASSIGN(ColorTransformStep); | 275 DISALLOW_COPY_AND_ASSIGN(ColorTransformStep); |
| 266 }; | 276 }; |
| 267 | 277 |
| 268 class ColorTransformInternal : public ColorTransform { | 278 class ColorTransformInternal : public ColorTransform { |
| 269 public: | 279 public: |
| 270 ColorTransformInternal(const ColorSpace& from, | 280 ColorTransformInternal(const ColorSpace& from, |
| 271 const ColorSpace& to, | 281 const ColorSpace& to, |
| 272 Intent intent); | 282 Intent intent); |
| (...skipping 20 matching lines...) Expand all Loading... |
| 293 // Retrieve the ICC profile from which |color_space| was created, only if that | 303 // Retrieve the ICC profile from which |color_space| was created, only if that |
| 294 // is a more precise representation of the color space than the primaries and | 304 // is a more precise representation of the color space than the primaries and |
| 295 // transfer function in |color_space|. | 305 // transfer function in |color_space|. |
| 296 ScopedQcmsProfile GetQCMSProfileIfNecessary(const ColorSpace& color_space); | 306 ScopedQcmsProfile GetQCMSProfileIfNecessary(const ColorSpace& color_space); |
| 297 | 307 |
| 298 std::list<std::unique_ptr<ColorTransformStep>> steps_; | 308 std::list<std::unique_ptr<ColorTransformStep>> steps_; |
| 299 gfx::ColorSpace src_; | 309 gfx::ColorSpace src_; |
| 300 gfx::ColorSpace dst_; | 310 gfx::ColorSpace dst_; |
| 301 }; | 311 }; |
| 302 | 312 |
| 303 #define SRC(...) \ | |
| 304 do { \ | |
| 305 *result += std::string(" ") + base::StringPrintf(__VA_ARGS__) + \ | |
| 306 std::string("\n"); \ | |
| 307 } while (0) | |
| 308 | |
| 309 class ColorTransformNull : public ColorTransformStep { | 313 class ColorTransformNull : public ColorTransformStep { |
| 310 public: | 314 public: |
| 311 ColorTransformNull* GetNull() override { return this; } | 315 ColorTransformNull* GetNull() override { return this; } |
| 312 bool IsNull() override { return true; } | 316 bool IsNull() override { return true; } |
| 313 void Transform(ColorTransform::TriStim* color, size_t num) const override {} | 317 void Transform(ColorTransform::TriStim* color, size_t num) const override {} |
| 314 bool CanAppendShaderSource() override { return true; } | 318 bool CanAppendShaderSource() override { return true; } |
| 315 void AppendShaderSource(std::string* result) override {} | 319 void AppendShaderSource(std::stringstream* result) override {} |
| 316 }; | 320 }; |
| 317 | 321 |
| 318 class ColorTransformMatrix : public ColorTransformStep { | 322 class ColorTransformMatrix : public ColorTransformStep { |
| 319 public: | 323 public: |
| 320 explicit ColorTransformMatrix(const class Transform& matrix) | 324 explicit ColorTransformMatrix(const class Transform& matrix) |
| 321 : matrix_(matrix) {} | 325 : matrix_(matrix) {} |
| 322 ColorTransformMatrix* GetMatrix() override { return this; } | 326 ColorTransformMatrix* GetMatrix() override { return this; } |
| 323 bool Join(ColorTransformStep* next_untyped) override { | 327 bool Join(ColorTransformStep* next_untyped) override { |
| 324 ColorTransformMatrix* next = next_untyped->GetMatrix(); | 328 ColorTransformMatrix* next = next_untyped->GetMatrix(); |
| 325 if (!next) | 329 if (!next) |
| 326 return false; | 330 return false; |
| 327 class Transform tmp = next->matrix_; | 331 class Transform tmp = next->matrix_; |
| 328 tmp *= matrix_; | 332 tmp *= matrix_; |
| 329 matrix_ = tmp; | 333 matrix_ = tmp; |
| 330 return true; | 334 return true; |
| 331 } | 335 } |
| 332 | 336 |
| 333 bool IsNull() override { | 337 bool IsNull() override { |
| 334 return SkMatrixIsApproximatelyIdentity(matrix_.matrix()); | 338 return SkMatrixIsApproximatelyIdentity(matrix_.matrix()); |
| 335 } | 339 } |
| 336 | 340 |
| 337 void Transform(ColorTransform::TriStim* colors, size_t num) const override { | 341 void Transform(ColorTransform::TriStim* colors, size_t num) const override { |
| 338 for (size_t i = 0; i < num; i++) | 342 for (size_t i = 0; i < num; i++) |
| 339 matrix_.TransformPoint(colors + i); | 343 matrix_.TransformPoint(colors + i); |
| 340 } | 344 } |
| 341 | 345 |
| 342 bool CanAppendShaderSource() override { return true; } | 346 bool CanAppendShaderSource() override { return true; } |
| 343 | 347 |
| 344 void AppendShaderSource(std::string* result) override { | 348 void AppendShaderSource(std::stringstream* result) override { |
| 345 const SkMatrix44& m = matrix_.matrix(); | 349 const SkMatrix44& m = matrix_.matrix(); |
| 346 SRC("color = mat3(%+1.8e, %+1.8e, %+1.8e,", // column 1 | 350 *result << " color = mat3("; |
| 347 m.get(0, 0), m.get(1, 0), m.get(2, 0)); | 351 *result << m.get(0, 0) << ", " << m.get(1, 0) << ", " << m.get(2, 0) << ","; |
| 348 SRC(" %+1.8e, %+1.8e, %+1.8e,", // column 2 | 352 *result << std::endl; |
| 349 m.get(0, 1), m.get(1, 1), m.get(2, 1)); | 353 *result << " "; |
| 350 SRC(" %+1.8e, %+1.8e, %+1.8e) * color;", // column 3 | 354 *result << m.get(0, 1) << ", " << m.get(1, 1) << ", " << m.get(2, 1) << ","; |
| 351 m.get(0, 2), m.get(1, 2), m.get(2, 2)); | 355 *result << std::endl; |
| 352 SRC("color = vec3(%+1.8e, %+1.8e, %+1.8e) + color;", // column 4 | 356 *result << " "; |
| 353 m.get(0, 3), m.get(1, 3), m.get(2, 3)); | 357 *result << m.get(0, 2) << ", " << m.get(1, 2) << ", " << m.get(2, 2) << ")"; |
| 358 *result << " * color;" << std::endl; |
| 359 |
| 360 // Only print the translational component if it isn't the identity. |
| 361 if (m.get(0, 3) != 0.f || m.get(1, 3) != 0.f || m.get(2, 3) != 0.f) { |
| 362 *result << " color += vec3("; |
| 363 *result << m.get(0, 3) << ", " << m.get(1, 3) << ", " << m.get(2, 3); |
| 364 *result << ");" << std::endl; |
| 365 } |
| 354 } | 366 } |
| 355 | 367 |
| 356 private: | 368 private: |
| 357 class Transform matrix_; | 369 class Transform matrix_; |
| 358 }; | 370 }; |
| 359 | 371 |
| 360 class ColorTransformSkTransferFn : public ColorTransformStep { | 372 class ColorTransformSkTransferFn : public ColorTransformStep { |
| 361 public: | 373 public: |
| 362 explicit ColorTransformSkTransferFn(const SkColorSpaceTransferFn& fn) | 374 explicit ColorTransformSkTransferFn(const SkColorSpaceTransferFn& fn) |
| 363 : fn_(fn) {} | 375 : fn_(fn) {} |
| (...skipping 22 matching lines...) Expand all Loading... |
| 386 void Transform(ColorTransform::TriStim* colors, size_t num) const override { | 398 void Transform(ColorTransform::TriStim* colors, size_t num) const override { |
| 387 for (size_t i = 0; i < num; i++) { | 399 for (size_t i = 0; i < num; i++) { |
| 388 colors[i].set_x(SkTransferFnEval(fn_, colors[i].x())); | 400 colors[i].set_x(SkTransferFnEval(fn_, colors[i].x())); |
| 389 colors[i].set_y(SkTransferFnEval(fn_, colors[i].y())); | 401 colors[i].set_y(SkTransferFnEval(fn_, colors[i].y())); |
| 390 colors[i].set_z(SkTransferFnEval(fn_, colors[i].z())); | 402 colors[i].set_z(SkTransferFnEval(fn_, colors[i].z())); |
| 391 } | 403 } |
| 392 } | 404 } |
| 393 | 405 |
| 394 bool CanAppendShaderSource() override { return true; } | 406 bool CanAppendShaderSource() override { return true; } |
| 395 | 407 |
| 396 void AppendShaderSourceChannel(std::string* result, const char* value) { | 408 void AppendShaderSourceChannel(std::stringstream* result, const char* value) { |
| 397 const float kEpsilon = 1.f / 1024.f; | 409 const float kEpsilon = 1.f / 1024.f; |
| 398 | 410 |
| 399 // Construct the linear segment | 411 // Construct the linear segment |
| 400 // linear = C * x + F | 412 // linear = C * x + F |
| 401 // Elide operations that will be close to the identity. | 413 // Elide operations that will be close to the identity. |
| 402 std::string linear = value; | 414 std::string linear = value; |
| 403 if (std::abs(fn_.fC - 1.f) > kEpsilon) | 415 if (std::abs(fn_.fC - 1.f) > kEpsilon) |
| 404 linear = Str(fn_.fC) + " * " + linear; | 416 linear = Str(fn_.fC) + " * " + linear; |
| 405 if (std::abs(fn_.fF) > kEpsilon) | 417 if (std::abs(fn_.fF) > kEpsilon) |
| 406 linear = linear + " + " + Str(fn_.fF); | 418 linear = linear + " + " + Str(fn_.fF); |
| 407 | 419 |
| 408 // Construct the nonlinear segment. | 420 // Construct the nonlinear segment. |
| 409 // nonlinear = pow(A * x + B, G) + E | 421 // nonlinear = pow(A * x + B, G) + E |
| 410 // Elide operations (especially the pow) that will be close to the | 422 // Elide operations (especially the pow) that will be close to the |
| 411 // identity. | 423 // identity. |
| 412 std::string nonlinear = value; | 424 std::string nonlinear = value; |
| 413 if (std::abs(fn_.fA - 1.f) > kEpsilon) | 425 if (std::abs(fn_.fA - 1.f) > kEpsilon) |
| 414 nonlinear = Str(fn_.fA) + " * " + nonlinear; | 426 nonlinear = Str(fn_.fA) + " * " + nonlinear; |
| 415 if (std::abs(fn_.fB) > kEpsilon) | 427 if (std::abs(fn_.fB) > kEpsilon) |
| 416 nonlinear = nonlinear + " + " + Str(fn_.fB); | 428 nonlinear = nonlinear + " + " + Str(fn_.fB); |
| 417 if (std::abs(fn_.fG - 1.f) > kEpsilon) | 429 if (std::abs(fn_.fG - 1.f) > kEpsilon) |
| 418 nonlinear = "pow(" + nonlinear + ", " + Str(fn_.fG) + ")"; | 430 nonlinear = "pow(" + nonlinear + ", " + Str(fn_.fG) + ")"; |
| 419 if (std::abs(fn_.fE) > kEpsilon) | 431 if (std::abs(fn_.fE) > kEpsilon) |
| 420 nonlinear = nonlinear + " + " + Str(fn_.fE); | 432 nonlinear = nonlinear + " + " + Str(fn_.fE); |
| 421 | 433 |
| 422 // Add both parts, skpping the if clause if possible. | 434 // Add both parts, skpping the if clause if possible. |
| 423 if (fn_.fD > kEpsilon) { | 435 if (fn_.fD > kEpsilon) { |
| 424 SRC("if (%s < %f)", value, fn_.fD); | 436 *result << " if (" << value << " < " << Str(fn_.fD) << ")" << std::endl; |
| 425 SRC(" %s = %s;", value, linear.c_str()); | 437 *result << " " << value << " = " << linear << ";" << std::endl; |
| 426 SRC("else"); | 438 *result << " else" << std::endl; |
| 427 SRC(" %s = %s;", value, nonlinear.c_str()); | 439 *result << " " << value << " = " << nonlinear << ";" << std::endl; |
| 428 } else { | 440 } else { |
| 429 SRC("%s = %s;", value, nonlinear.c_str()); | 441 *result << " " << value << " = " << nonlinear << ";" << std::endl; |
| 430 } | 442 } |
| 431 } | 443 } |
| 432 | 444 |
| 433 void AppendShaderSource(std::string* result) override { | 445 void AppendShaderSource(std::stringstream* result) override { |
| 434 // Append the transfer function for each channel. | 446 // Append the transfer function for each channel. |
| 435 AppendShaderSourceChannel(result, "color.r"); | 447 AppendShaderSourceChannel(result, "color.r"); |
| 436 AppendShaderSourceChannel(result, "color.g"); | 448 AppendShaderSourceChannel(result, "color.g"); |
| 437 AppendShaderSourceChannel(result, "color.b"); | 449 AppendShaderSourceChannel(result, "color.b"); |
| 438 } | 450 } |
| 439 | 451 |
| 440 private: | 452 private: |
| 441 SkColorSpaceTransferFn fn_; | 453 SkColorSpaceTransferFn fn_; |
| 442 }; | 454 }; |
| 443 | 455 |
| (...skipping 365 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 809 if (dst_profile) { | 821 if (dst_profile) { |
| 810 steps_.push_back(base::MakeUnique<QCMSColorTransform>( | 822 steps_.push_back(base::MakeUnique<QCMSColorTransform>( |
| 811 GetXYZD50Profile(), std::move(dst_profile))); | 823 GetXYZD50Profile(), std::move(dst_profile))); |
| 812 } | 824 } |
| 813 | 825 |
| 814 if (intent != Intent::TEST_NO_OPT) | 826 if (intent != Intent::TEST_NO_OPT) |
| 815 Simplify(); | 827 Simplify(); |
| 816 } | 828 } |
| 817 | 829 |
| 818 std::string ColorTransformInternal::GetShaderSource() const { | 830 std::string ColorTransformInternal::GetShaderSource() const { |
| 819 std::string result; | 831 std::stringstream result; |
| 820 result += "vec3 DoColorConversion(vec3 color) {\n"; | 832 InitStringStream(&result); |
| 833 result << "vec3 DoColorConversion(vec3 color) {" << std::endl; |
| 821 for (const auto& step : steps_) | 834 for (const auto& step : steps_) |
| 822 step->AppendShaderSource(&result); | 835 step->AppendShaderSource(&result); |
| 823 result += " return color;\n"; | 836 result << " return color;" << std::endl; |
| 824 result += "}\n"; | 837 result << "}" << std::endl; |
| 825 return result; | 838 return result.str(); |
| 826 } | 839 } |
| 827 | 840 |
| 828 bool ColorTransformInternal::CanGetShaderSource() const { | 841 bool ColorTransformInternal::CanGetShaderSource() const { |
| 829 for (const auto& step : steps_) { | 842 for (const auto& step : steps_) { |
| 830 if (!step->CanAppendShaderSource()) | 843 if (!step->CanAppendShaderSource()) |
| 831 return false; | 844 return false; |
| 832 } | 845 } |
| 833 return true; | 846 return true; |
| 834 } | 847 } |
| 835 | 848 |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 872 const ColorSpace& to, | 885 const ColorSpace& to, |
| 873 Intent intent) { | 886 Intent intent) { |
| 874 return std::unique_ptr<ColorTransform>( | 887 return std::unique_ptr<ColorTransform>( |
| 875 new ColorTransformInternal(from, to, intent)); | 888 new ColorTransformInternal(from, to, intent)); |
| 876 } | 889 } |
| 877 | 890 |
| 878 ColorTransform::ColorTransform() {} | 891 ColorTransform::ColorTransform() {} |
| 879 ColorTransform::~ColorTransform() {} | 892 ColorTransform::~ColorTransform() {} |
| 880 | 893 |
| 881 } // namespace gfx | 894 } // namespace gfx |
| OLD | NEW |