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
3 // found in the LICENSE file.
4 
5 #include "ui/gfx/color_transform.h"
6 
7 #include <algorithm>
8 #include <cmath>
9 #include <list>
10 #include <memory>
11 #include <sstream>
12 #include <utility>
13 
14 #include "base/logging.h"
15 #include "base/notreached.h"
16 #include "third_party/skia/include/core/SkColor.h"
17 #include "third_party/skia/include/third_party/skcms/skcms.h"
18 #include "ui/gfx/color_space.h"
19 #include "ui/gfx/icc_profile.h"
20 #include "ui/gfx/skia_color_space_util.h"
21 #include "ui/gfx/transform.h"
22 
23 using std::abs;
24 using std::copysign;
25 using std::exp;
26 using std::log;
27 using std::max;
28 using std::min;
29 using std::pow;
30 using std::sqrt;
31 using std::endl;
32 
33 namespace gfx {
34 
35 namespace {
36 
InitStringStream(std::stringstream * ss)37 void InitStringStream(std::stringstream* ss) {
38   ss->imbue(std::locale::classic());
39   ss->precision(8);
40   *ss << std::scientific;
41 }
42 
Str(float f)43 std::string Str(float f) {
44   std::stringstream ss;
45   InitStringStream(&ss);
46   ss << f;
47   return ss.str();
48 }
49 
Invert(const Transform & t)50 Transform Invert(const Transform& t) {
51   Transform ret = t;
52   if (!t.GetInverse(&ret)) {
53     LOG(ERROR) << "Inverse should always be possible.";
54   }
55   return ret;
56 }
57 
FromLinear(ColorSpace::TransferID id,float v)58 float FromLinear(ColorSpace::TransferID id, float v) {
59   switch (id) {
60     case ColorSpace::TransferID::LOG:
61       if (v < 0.01f)
62         return 0.0f;
63       return 1.0f + log(v) / log(10.0f) / 2.0f;
64 
65     case ColorSpace::TransferID::LOG_SQRT:
66       if (v < sqrt(10.0f) / 1000.0f)
67         return 0.0f;
68       return 1.0f + log(v) / log(10.0f) / 2.5f;
69 
70     case ColorSpace::TransferID::IEC61966_2_4: {
71       float a = 1.099296826809442f;
72       float b = 0.018053968510807f;
73       if (v < -b)
74         return -a * pow(-v, 0.45f) + (a - 1.0f);
75       else if (v <= b)
76         return 4.5f * v;
77       return a * pow(v, 0.45f) - (a - 1.0f);
78     }
79 
80     case ColorSpace::TransferID::BT1361_ECG: {
81       float a = 1.099f;
82       float b = 0.018f;
83       float l = 0.0045f;
84       if (v < -l)
85         return -(a * pow(-4.0f * v, 0.45f) + (a - 1.0f)) / 4.0f;
86       else if (v <= b)
87         return 4.5f * v;
88       else
89         return a * pow(v, 0.45f) - (a - 1.0f);
90     }
91 
92     default:
93       // Handled by skcms_TransferFunction.
94       break;
95   }
96   NOTREACHED();
97   return 0;
98 }
99 
ToLinear(ColorSpace::TransferID id,float v)100 float ToLinear(ColorSpace::TransferID id, float v) {
101   switch (id) {
102     case ColorSpace::TransferID::LOG:
103       if (v < 0.0f)
104         return 0.0f;
105       return pow(10.0f, (v - 1.0f) * 2.0f);
106 
107     case ColorSpace::TransferID::LOG_SQRT:
108       if (v < 0.0f)
109         return 0.0f;
110       return pow(10.0f, (v - 1.0f) * 2.5f);
111 
112     case ColorSpace::TransferID::IEC61966_2_4: {
113       float a = 1.099296826809442f;
114       // Equal to FromLinear(ColorSpace::TransferID::IEC61966_2_4, -a).
115       float from_linear_neg_a = -1.047844f;
116       // Equal to FromLinear(ColorSpace::TransferID::IEC61966_2_4, b).
117       float from_linear_b = 0.081243f;
118       if (v < from_linear_neg_a)
119         return -pow((a - 1.0f - v) / a, 1.0f / 0.45f);
120       else if (v <= from_linear_b)
121         return v / 4.5f;
122       return pow((v + a - 1.0f) / a, 1.0f / 0.45f);
123     }
124 
125     case ColorSpace::TransferID::BT1361_ECG: {
126       float a = 1.099f;
127       // Equal to FromLinear(ColorSpace::TransferID::BT1361_ECG, -l).
128       float from_linear_neg_l = -0.020250f;
129       // Equal to FromLinear(ColorSpace::TransferID::BT1361_ECG, b).
130       float from_linear_b = 0.081000f;
131       if (v < from_linear_neg_l)
132         return -pow((1.0f - a - v * 4.0f) / a, 1.0f / 0.45f) / 4.0f;
133       else if (v <= from_linear_b)
134         return v / 4.5f;
135       return pow((v + a - 1.0f) / a, 1.0f / 0.45f);
136     }
137 
138     default:
139       // Handled by skcms_TransferFunction.
140       break;
141   }
142   NOTREACHED();
143   return 0;
144 }
145 
GetTransferMatrix(const gfx::ColorSpace & color_space)146 Transform GetTransferMatrix(const gfx::ColorSpace& color_space) {
147   SkMatrix44 transfer_matrix;
148   color_space.GetTransferMatrix(&transfer_matrix);
149   return Transform(transfer_matrix);
150 }
151 
GetRangeAdjustMatrix(const gfx::ColorSpace & color_space,int bit_depth)152 Transform GetRangeAdjustMatrix(const gfx::ColorSpace& color_space,
153                                int bit_depth) {
154   SkMatrix44 range_adjust_matrix;
155   color_space.GetRangeAdjustMatrix(bit_depth, &range_adjust_matrix);
156   return Transform(range_adjust_matrix);
157 }
158 
GetPrimaryTransform(const gfx::ColorSpace & color_space)159 Transform GetPrimaryTransform(const gfx::ColorSpace& color_space) {
160   SkMatrix44 primary_matrix;
161   color_space.GetPrimaryMatrix(&primary_matrix);
162   return Transform(primary_matrix);
163 }
164 
165 }  // namespace
166 
167 class ColorTransformMatrix;
168 class ColorTransformSkTransferFn;
169 class ColorTransformFromLinear;
170 class ColorTransformFromBT2020CL;
171 class ColorTransformNull;
172 
173 class ColorTransformStep {
174  public:
ColorTransformStep()175   ColorTransformStep() {}
~ColorTransformStep()176   virtual ~ColorTransformStep() {}
GetFromLinear()177   virtual ColorTransformFromLinear* GetFromLinear() { return nullptr; }
GetFromBT2020CL()178   virtual ColorTransformFromBT2020CL* GetFromBT2020CL() { return nullptr; }
GetSkTransferFn()179   virtual ColorTransformSkTransferFn* GetSkTransferFn() { return nullptr; }
GetMatrix()180   virtual ColorTransformMatrix* GetMatrix() { return nullptr; }
GetNull()181   virtual ColorTransformNull* GetNull() { return nullptr; }
182 
183   // Join methods, returns true if the |next| transform was successfully
184   // assimilated into |this|.
185   // If Join() returns true, |next| is no longer needed and can be deleted.
Join(ColorTransformStep * next)186   virtual bool Join(ColorTransformStep* next) { return false; }
187 
188   // Return true if this is a null transform.
IsNull()189   virtual bool IsNull() { return false; }
190   virtual void Transform(ColorTransform::TriStim* color, size_t num) const = 0;
191   // In the shader, |hdr| will appear before |src|, so any helper functions that
192   // are created should be put in |hdr|. Any helper functions should have
193   // |step_index| included in the function name, to ensure that there are no
194   // naming conflicts.
195   virtual void AppendShaderSource(std::stringstream* hdr,
196                                   std::stringstream* src,
197                                   size_t step_index) const = 0;
198   virtual void AppendSkShaderSource(std::stringstream* src) const = 0;
199 
200  private:
201   DISALLOW_COPY_AND_ASSIGN(ColorTransformStep);
202 };
203 
204 class ColorTransformInternal : public ColorTransform {
205  public:
206   ColorTransformInternal(const ColorSpace& src,
207                          int src_bit_depth,
208                          const ColorSpace& dst,
209                          int dst_bit_depth,
210                          Intent intent);
211   ~ColorTransformInternal() override;
212 
GetSrcColorSpace() const213   gfx::ColorSpace GetSrcColorSpace() const override { return src_; }
GetDstColorSpace() const214   gfx::ColorSpace GetDstColorSpace() const override { return dst_; }
215 
Transform(TriStim * colors,size_t num) const216   void Transform(TriStim* colors, size_t num) const override {
217     for (const auto& step : steps_) {
218       step->Transform(colors, num);
219     }
220   }
221   std::string GetShaderSource() const override;
222   std::string GetSkShaderSource() const override;
IsIdentity() const223   bool IsIdentity() const override { return steps_.empty(); }
NumberOfStepsForTesting() const224   size_t NumberOfStepsForTesting() const override { return steps_.size(); }
225 
226  private:
227   void AppendColorSpaceToColorSpaceTransform(const ColorSpace& src,
228                                              int src_bit_depth,
229                                              const ColorSpace& dst,
230                                              int dst_bit_depth);
231   void Simplify();
232 
233   std::list<std::unique_ptr<ColorTransformStep>> steps_;
234   gfx::ColorSpace src_;
235   gfx::ColorSpace dst_;
236 };
237 
238 class ColorTransformNull : public ColorTransformStep {
239  public:
GetNull()240   ColorTransformNull* GetNull() override { return this; }
IsNull()241   bool IsNull() override { return true; }
Transform(ColorTransform::TriStim * color,size_t num) const242   void Transform(ColorTransform::TriStim* color, size_t num) const override {}
AppendShaderSource(std::stringstream * hdr,std::stringstream * src,size_t step_index) const243   void AppendShaderSource(std::stringstream* hdr,
244                           std::stringstream* src,
245                           size_t step_index) const override {}
AppendSkShaderSource(std::stringstream * src) const246   void AppendSkShaderSource(std::stringstream* src) const override {}
247 };
248 
249 class ColorTransformMatrix : public ColorTransformStep {
250  public:
ColorTransformMatrix(const class Transform & matrix)251   explicit ColorTransformMatrix(const class Transform& matrix)
252       : matrix_(matrix) {}
GetMatrix()253   ColorTransformMatrix* GetMatrix() override { return this; }
Join(ColorTransformStep * next_untyped)254   bool Join(ColorTransformStep* next_untyped) override {
255     ColorTransformMatrix* next = next_untyped->GetMatrix();
256     if (!next)
257       return false;
258     class Transform tmp = next->matrix_;
259     tmp *= matrix_;
260     matrix_ = tmp;
261     return true;
262   }
263 
IsNull()264   bool IsNull() override {
265     return SkMatrixIsApproximatelyIdentity(matrix_.matrix());
266   }
267 
Transform(ColorTransform::TriStim * colors,size_t num) const268   void Transform(ColorTransform::TriStim* colors, size_t num) const override {
269     for (size_t i = 0; i < num; i++)
270       matrix_.TransformPoint(colors + i);
271   }
272 
273 
AppendShaderSource(std::stringstream * hdr,std::stringstream * src,size_t step_index) const274   void AppendShaderSource(std::stringstream* hdr,
275                           std::stringstream* src,
276                           size_t step_index) const override {
277     const SkMatrix44& m = matrix_.matrix();
278     *src << "  color = mat3(";
279     *src << m.get(0, 0) << ", " << m.get(1, 0) << ", " << m.get(2, 0) << ",";
280     *src << endl;
281     *src << "               ";
282     *src << m.get(0, 1) << ", " << m.get(1, 1) << ", " << m.get(2, 1) << ",";
283     *src << endl;
284     *src << "               ";
285     *src << m.get(0, 2) << ", " << m.get(1, 2) << ", " << m.get(2, 2) << ")";
286     *src << " * color;" << endl;
287 
288     // Only print the translational component if it isn't the identity.
289     if (m.get(0, 3) != 0.f || m.get(1, 3) != 0.f || m.get(2, 3) != 0.f) {
290       *src << "  color += vec3(";
291       *src << m.get(0, 3) << ", " << m.get(1, 3) << ", " << m.get(2, 3);
292       *src << ");" << endl;
293     }
294   }
295 
AppendSkShaderSource(std::stringstream * src) const296   void AppendSkShaderSource(std::stringstream* src) const override {
297     const SkMatrix44& m = matrix_.matrix();
298     *src << "  color = half4x4(";
299     *src << m.get(0, 0) << ", " << m.get(1, 0) << ", " << m.get(2, 0) << ", 0,";
300     *src << endl;
301     *src << "               ";
302     *src << m.get(0, 1) << ", " << m.get(1, 1) << ", " << m.get(2, 1) << ", 0,";
303     *src << endl;
304     *src << "               ";
305     *src << m.get(0, 2) << ", " << m.get(1, 2) << ", " << m.get(2, 2) << ", 0,";
306     *src << endl;
307     *src << "0, 0, 0, 1)";
308     *src << " * color;" << endl;
309 
310     // Only print the translational component if it isn't the identity.
311     if (m.get(0, 3) != 0.f || m.get(1, 3) != 0.f || m.get(2, 3) != 0.f) {
312       *src << "  color += half4(";
313       *src << m.get(0, 3) << ", " << m.get(1, 3) << ", " << m.get(2, 3);
314       *src << ", 0);" << endl;
315     }
316   }
317 
318  private:
319   class Transform matrix_;
320 };
321 
322 class ColorTransformPerChannelTransferFn : public ColorTransformStep {
323  public:
ColorTransformPerChannelTransferFn(bool extended)324   explicit ColorTransformPerChannelTransferFn(bool extended)
325       : extended_(extended) {}
326 
Transform(ColorTransform::TriStim * colors,size_t num) const327   void Transform(ColorTransform::TriStim* colors, size_t num) const override {
328     for (size_t i = 0; i < num; i++) {
329       ColorTransform::TriStim& c = colors[i];
330       if (extended_) {
331         c.set_x(copysign(Evaluate(abs(c.x())), c.x()));
332         c.set_y(copysign(Evaluate(abs(c.y())), c.y()));
333         c.set_z(copysign(Evaluate(abs(c.z())), c.z()));
334       } else {
335         c.set_x(Evaluate(c.x()));
336         c.set_y(Evaluate(c.y()));
337         c.set_z(Evaluate(c.z()));
338       }
339     }
340   }
341 
AppendShaderSource(std::stringstream * hdr,std::stringstream * src,size_t step_index) const342   void AppendShaderSource(std::stringstream* hdr,
343                           std::stringstream* src,
344                           size_t step_index) const override {
345     *hdr << "float TransferFn" << step_index << "(float v) {" << endl;
346     AppendTransferShaderSource(hdr, true /* is_glsl */);
347     *hdr << "  return v;" << endl;
348     *hdr << "}" << endl;
349     if (extended_) {
350       *src << "  color.r = sign(color.r) * TransferFn" << step_index
351            << "(abs(color.r));" << endl;
352       *src << "  color.g = sign(color.g) * TransferFn" << step_index
353            << "(abs(color.g));" << endl;
354       *src << "  color.b = sign(color.b) * TransferFn" << step_index
355            << "(abs(color.b));" << endl;
356     } else {
357       *src << "  color.r = TransferFn" << step_index << "(color.r);" << endl;
358       *src << "  color.g = TransferFn" << step_index << "(color.g);" << endl;
359       *src << "  color.b = TransferFn" << step_index << "(color.b);" << endl;
360     }
361   }
362 
AppendSkShaderSource(std::stringstream * src) const363   void AppendSkShaderSource(std::stringstream* src) const override {
364     if (extended_) {
365       *src << "{  half v = abs(color.r);" << endl;
366       AppendTransferShaderSource(src, false /* is_glsl */);
367       *src << "  color.r = sign(color.r) * v; }" << endl;
368       *src << "{  half v = abs(color.g);" << endl;
369       AppendTransferShaderSource(src, false /* is_glsl */);
370       *src << "  color.g = sign(color.g) * v; }" << endl;
371       *src << "{  half v = abs(color.b);" << endl;
372       AppendTransferShaderSource(src, false /* is_glsl */);
373       *src << "  color.b = sign(color.b) * v; }" << endl;
374     } else {
375       *src << "{  half v = color.r;" << endl;
376       AppendTransferShaderSource(src, false /* is_glsl */);
377       *src << "  color.r = v; }" << endl;
378       *src << "{  half v = color.g;" << endl;
379       AppendTransferShaderSource(src, false /* is_glsl */);
380       *src << "  color.g = v; }" << endl;
381       *src << "{  half v = color.b;" << endl;
382       AppendTransferShaderSource(src, false /* is_glsl */);
383       *src << "  color.b = v; }" << endl;
384     }
385   }
386 
387   virtual float Evaluate(float x) const = 0;
388   virtual void AppendTransferShaderSource(std::stringstream* src,
389                                           bool is_glsl) const = 0;
390 
391  protected:
392   // True if the transfer function is extended to be defined for all real
393   // values by point symmetry.
394   bool extended_ = false;
395 };
396 
397 // This class represents the piecewise-HDR function using three new parameters,
398 // P, Q, and R. The function is defined as:
399 //            0         : x < 0
400 //     T(x) = sRGB(x/P) : x < P
401 //            Q*x+R     : x >= P
402 // This then expands to
403 //            0                : x < 0
404 //     T(x) = C*x/P+F          : x < P*D
405 //            (A*x/P+B)**G + E : x < P
406 //            Q*x+R            : else
407 class ColorTransformPiecewiseHDR : public ColorTransformPerChannelTransferFn {
408  public:
GetParams(const gfx::ColorSpace color_space,skcms_TransferFunction * fn,float * p,float * q,float * r)409   static void GetParams(const gfx::ColorSpace color_space,
410                         skcms_TransferFunction* fn,
411                         float* p,
412                         float* q,
413                         float* r) {
414     float sdr_joint = 1;
415     float hdr_level = 1;
416     color_space.GetPiecewiseHDRParams(&sdr_joint, &hdr_level);
417 
418     // P is exactly |sdr_joint|.
419     *p = sdr_joint;
420 
421     if (sdr_joint < 1.f) {
422       // Q and R are computed such that |sdr_joint| maps to 1 and 1) maps to
423       // |hdr_level|.
424       *q = (hdr_level - 1.f) / (1.f - sdr_joint);
425       *r = (1.f - hdr_level * sdr_joint) / (1.f - sdr_joint);
426     } else {
427       // If |sdr_joint| is exactly 1, then just saturate at 1 (there is no HDR).
428       *q = 0;
429       *r = 1;
430     }
431 
432     // Compute |fn| so that, at x, it evaluates to sRGB(x*P).
433     ColorSpace::CreateSRGB().GetTransferFunction(fn);
434     fn->d *= sdr_joint;
435     if (sdr_joint != 0) {
436       // If |sdr_joint| is 0, then we will never evaluate |fn| anyway.
437       fn->a /= sdr_joint;
438       fn->c /= sdr_joint;
439     }
440   }
InvertParams(skcms_TransferFunction * fn,float * p,float * q,float * r)441   static void InvertParams(skcms_TransferFunction* fn,
442                            float* p,
443                            float* q,
444                            float* r) {
445     *fn = SkTransferFnInverse(*fn);
446     float old_p = *p;
447     float old_q = *q;
448     float old_r = *r;
449     *p = old_q * old_p + old_r;
450     if (old_q != 0.f) {
451       *q = 1.f / old_q;
452       *r = -old_r / old_q;
453     } else {
454       *q = 0.f;
455       *r = 1.f;
456     }
457   }
458 
ColorTransformPiecewiseHDR(const skcms_TransferFunction fn,float p,float q,float r)459   ColorTransformPiecewiseHDR(const skcms_TransferFunction fn,
460                              float p,
461                              float q,
462                              float r)
463       : ColorTransformPerChannelTransferFn(false),
464         fn_(fn),
465         p_(p),
466         q_(q),
467         r_(r) {}
468 
469   // ColorTransformPerChannelTransferFn implementation:
Evaluate(float v) const470   float Evaluate(float v) const override {
471     if (v < 0)
472       return 0;
473     else if (v < fn_.d)
474       return fn_.c * v + fn_.f;
475     else if (v < p_)
476       return std::pow(fn_.a * v + fn_.b, fn_.g) + fn_.e;
477     else
478       return q_ * v + r_;
479   }
AppendTransferShaderSource(std::stringstream * result,bool is_glsl) const480   void AppendTransferShaderSource(std::stringstream* result,
481                                   bool is_glsl) const override {
482     *result << "  if (v < 0.0) {\n";
483     *result << "    v = 0.0;\n";
484     *result << "  } else if (v < " << Str(fn_.d) << ") {\n";
485     *result << "    v = " << Str(fn_.c) << " * v + " << Str(fn_.f) << ";"
486             << endl;
487     *result << "  } else if (v < " << Str(p_) << ") {\n";
488     *result << "    v = pow(" << Str(fn_.a) << " * v + " << Str(fn_.b) << ", "
489             << Str(fn_.g) << ") + " << Str(fn_.e) << ";\n";
490     *result << "  } else {\n";
491     *result << "    v = " << Str(q_) << " * v + " << Str(r_) << ";\n";
492     *result << "  }\n";
493   }
494 
495  private:
496   // Parameters of the SDR part.
497   const skcms_TransferFunction fn_;
498   // The SDR joint. Below this value in the domain, the function is defined by
499   // |fn_|.
500   const float p_;
501   // The slope of the linear HDR part.
502   const float q_;
503   // The intercept of the linear HDR part.
504   const float r_;
505 };
506 
507 class ColorTransformSkTransferFn : public ColorTransformPerChannelTransferFn {
508  public:
ColorTransformSkTransferFn(const skcms_TransferFunction & fn,bool extended)509   explicit ColorTransformSkTransferFn(const skcms_TransferFunction& fn,
510                                       bool extended)
511       : ColorTransformPerChannelTransferFn(extended), fn_(fn) {}
512   // ColorTransformStep implementation.
GetSkTransferFn()513   ColorTransformSkTransferFn* GetSkTransferFn() override { return this; }
Join(ColorTransformStep * next_untyped)514   bool Join(ColorTransformStep* next_untyped) override {
515     ColorTransformSkTransferFn* next = next_untyped->GetSkTransferFn();
516     if (!next)
517       return false;
518     if (!extended_ && !next->extended_ &&
519         SkTransferFnsApproximatelyCancel(fn_, next->fn_)) {
520       // Set to be the identity.
521       fn_.a = 1;
522       fn_.b = 0;
523       fn_.c = 1;
524       fn_.d = 0;
525       fn_.e = 0;
526       fn_.f = 0;
527       fn_.g = 1;
528       return true;
529     }
530     return false;
531   }
IsNull()532   bool IsNull() override { return SkTransferFnIsApproximatelyIdentity(fn_); }
533 
534   // ColorTransformPerChannelTransferFn implementation:
Evaluate(float v) const535   float Evaluate(float v) const override {
536     // Note that the sign-extension is performed by the caller.
537     return SkTransferFnEvalUnclamped(fn_, v);
538   }
AppendTransferShaderSource(std::stringstream * result,bool is_glsl) const539   void AppendTransferShaderSource(std::stringstream* result,
540                                   bool is_glsl) const override {
541     const float kEpsilon = 1.f / 1024.f;
542 
543     // Construct the linear segment
544     //   linear = C * x + F
545     // Elide operations that will be close to the identity.
546     std::string linear = "v";
547     if (std::abs(fn_.c - 1.f) > kEpsilon)
548       linear = Str(fn_.c) + " * " + linear;
549     if (std::abs(fn_.f) > kEpsilon)
550       linear = linear + " + " + Str(fn_.f);
551 
552     // Construct the nonlinear segment.
553     //   nonlinear = pow(A * x + B, G) + E
554     // Elide operations (especially the pow) that will be close to the
555     // identity.
556     std::string nonlinear = "v";
557     if (std::abs(fn_.a - 1.f) > kEpsilon)
558       nonlinear = Str(fn_.a) + " * " + nonlinear;
559     if (std::abs(fn_.b) > kEpsilon)
560       nonlinear = nonlinear + " + " + Str(fn_.b);
561     if (std::abs(fn_.g - 1.f) > kEpsilon)
562       nonlinear = "pow(" + nonlinear + ", " + Str(fn_.g) + ")";
563     if (std::abs(fn_.e) > kEpsilon)
564       nonlinear = nonlinear + " + " + Str(fn_.e);
565 
566     *result << "  if (v < " << Str(fn_.d) << ")" << endl;
567     *result << "    v = " << linear << ";" << endl;
568     *result << "  else" << endl;
569     *result << "    v = " << nonlinear << ";" << endl;
570   }
571 
572  private:
573   skcms_TransferFunction fn_;
574 };
575 
576 class ColorTransformHLGFromLinear : public ColorTransformPerChannelTransferFn {
577  public:
ColorTransformHLGFromLinear(float sdr_white_level)578   explicit ColorTransformHLGFromLinear(float sdr_white_level)
579       : ColorTransformPerChannelTransferFn(false),
580         sdr_scale_factor_(sdr_white_level /
581                           gfx::ColorSpace::kDefaultSDRWhiteLevel) {}
582 
583   // ColorTransformPerChannelTransferFn implementation:
Evaluate(float v) const584   float Evaluate(float v) const override {
585     v *= sdr_scale_factor_;
586 
587     // Spec: http://www.arib.or.jp/english/html/overview/doc/2-STD-B67v1_0.pdf
588     constexpr float a = 0.17883277f;
589     constexpr float b = 0.28466892f;
590     constexpr float c = 0.55991073f;
591     v = max(0.0f, v);
592     if (v <= 1)
593       return 0.5f * sqrt(v);
594     return a * log(v - b) + c;
595   }
596 
AppendTransferShaderSource(std::stringstream * src,bool is_glsl) const597   void AppendTransferShaderSource(std::stringstream* src,
598                                   bool is_glsl) const override {
599     std::string scalar_type = is_glsl ? "float" : "half";
600     *src << "  v = v * " << sdr_scale_factor_ << ";\n"
601          << "  v = max(0.0, v);\n"
602          << "  " << scalar_type << " a = 0.17883277;\n"
603          << "  " << scalar_type << " b = 0.28466892;\n"
604          << "  " << scalar_type << " c = 0.55991073;\n"
605          << "  if (v <= 1.0)\n"
606             "    v = 0.5 * sqrt(v);\n"
607             "  else\n"
608             "    v = a * log(v - b) + c;\n";
609   }
610 
611  private:
612   const float sdr_scale_factor_;
613 };
614 
615 class ColorTransformPQFromLinear : public ColorTransformPerChannelTransferFn {
616  public:
ColorTransformPQFromLinear(float sdr_white_level)617   explicit ColorTransformPQFromLinear(float sdr_white_level)
618       : ColorTransformPerChannelTransferFn(false),
619         sdr_white_level_(sdr_white_level) {}
620 
621   // ColorTransformPerChannelTransferFn implementation:
Evaluate(float v) const622   float Evaluate(float v) const override {
623     v *= sdr_white_level_ / 10000.0f;
624     v = max(0.0f, v);
625     float m1 = (2610.0f / 4096.0f) / 4.0f;
626     float m2 = (2523.0f / 4096.0f) * 128.0f;
627     float c1 = 3424.0f / 4096.0f;
628     float c2 = (2413.0f / 4096.0f) * 32.0f;
629     float c3 = (2392.0f / 4096.0f) * 32.0f;
630     float p = powf(v, m1);
631     return powf((c1 + c2 * p) / (1.0f + c3 * p), m2);
632   }
AppendTransferShaderSource(std::stringstream * src,bool is_glsl) const633   void AppendTransferShaderSource(std::stringstream* src,
634                                   bool is_glsl) const override {
635     std::string scalar_type = is_glsl ? "float" : "half";
636     *src << "  v *= " << sdr_white_level_
637          << " / 10000.0;\n"
638             "  v = max(0.0, v);\n"
639          << "  " << scalar_type << " m1 = (2610.0 / 4096.0) / 4.0;\n"
640          << "  " << scalar_type << " m2 = (2523.0 / 4096.0) * 128.0;\n"
641          << "  " << scalar_type << " c1 = 3424.0 / 4096.0;\n"
642          << "  " << scalar_type << " c2 = (2413.0 / 4096.0) * 32.0;\n"
643          << "  " << scalar_type
644          << " c3 = (2392.0 / 4096.0) * 32.0;\n"
645             "  v =  pow((c1 + c2 * pow(v, m1)) / \n"
646             "           (1.0 + c3 * pow(v, m1)), m2);\n";
647   }
648 
649  private:
650   const float sdr_white_level_;
651 };
652 
653 class ColorTransformHLGToLinear : public ColorTransformPerChannelTransferFn {
654  public:
ColorTransformHLGToLinear(float sdr_white_level)655   explicit ColorTransformHLGToLinear(float sdr_white_level)
656       : ColorTransformPerChannelTransferFn(false),
657         sdr_scale_factor_(gfx::ColorSpace::kDefaultSDRWhiteLevel /
658                           sdr_white_level) {}
659 
660   // ColorTransformPerChannelTransferFn implementation:
Evaluate(float v) const661   float Evaluate(float v) const override {
662     // Spec: http://www.arib.or.jp/english/html/overview/doc/2-STD-B67v1_0.pdf
663     v = max(0.0f, v);
664     constexpr float a = 0.17883277f;
665     constexpr float b = 0.28466892f;
666     constexpr float c = 0.55991073f;
667     if (v <= 0.5f)
668       v = v * v * 4.0f;
669     else
670       v = exp((v - c) / a) + b;
671     return v * sdr_scale_factor_;
672   }
673 
AppendTransferShaderSource(std::stringstream * src,bool is_glsl) const674   void AppendTransferShaderSource(std::stringstream* src,
675                                   bool is_glsl) const override {
676     std::string scalar_type = is_glsl ? "float" : "half";
677 
678     *src << "  v = max(0.0, v);\n"
679          << "  " << scalar_type << " a = 0.17883277;\n"
680          << "  " << scalar_type << " b = 0.28466892;\n"
681          << "  " << scalar_type << " c = 0.55991073;\n"
682          << "  if (v <= 0.5)\n"
683             "    v = v * v * 4.0;\n"
684             "  else\n"
685             "    v = exp((v - c) / a) + b;\n"
686             "  v = v * "
687          << sdr_scale_factor_ << ";\n";
688   }
689 
690  private:
691   const float sdr_scale_factor_;
692 };
693 
694 class ColorTransformPQToLinear : public ColorTransformPerChannelTransferFn {
695  public:
ColorTransformPQToLinear(float sdr_white_level)696   explicit ColorTransformPQToLinear(float sdr_white_level)
697       : ColorTransformPerChannelTransferFn(false),
698         sdr_white_level_(sdr_white_level) {}
699 
700   // ColorTransformPerChannelTransferFn implementation:
Evaluate(float v) const701   float Evaluate(float v) const override {
702     v = max(0.0f, v);
703     float m1 = (2610.0f / 4096.0f) / 4.0f;
704     float m2 = (2523.0f / 4096.0f) * 128.0f;
705     float c1 = 3424.0f / 4096.0f;
706     float c2 = (2413.0f / 4096.0f) * 32.0f;
707     float c3 = (2392.0f / 4096.0f) * 32.0f;
708     float p = pow(v, 1.0f / m2);
709     v = powf(max(p - c1, 0.0f) / (c2 - c3 * p), 1.0f / m1);
710     v *= 10000.0f / sdr_white_level_;
711     return v;
712   }
AppendTransferShaderSource(std::stringstream * src,bool is_glsl) const713   void AppendTransferShaderSource(std::stringstream* src,
714                                   bool is_glsl) const override {
715     std::string scalar_type = is_glsl ? "float" : "half";
716     *src << "  v = max(0.0, v);\n"
717          << "  " << scalar_type << " m1 = (2610.0 / 4096.0) / 4.0;\n"
718          << "  " << scalar_type << " m2 = (2523.0 / 4096.0) * 128.0;\n"
719          << "  " << scalar_type << " c1 = 3424.0 / 4096.0;\n"
720          << "  " << scalar_type << " c2 = (2413.0 / 4096.0) * 32.0;\n"
721          << "  " << scalar_type << " c3 = (2392.0 / 4096.0) * 32.0;\n";
722     if (is_glsl) {
723       *src << "  #ifdef GL_FRAGMENT_PRECISION_HIGH\n"
724               "  highp float v2 = v;\n"
725               "  #else\n"
726               "  float v2 = v;\n"
727               "  #endif\n";
728     } else {
729       *src << "  " << scalar_type << " v2 = v;\n";
730     }
731     *src << "  v2 = pow(max(pow(v2, 1.0 / m2) - c1, 0.0) /\n"
732             "              (c2 - c3 * pow(v2, 1.0 / m2)), 1.0 / m1);\n"
733             "  v = v2 * 10000.0 / "
734          << sdr_white_level_ << ";\n";
735   }
736 
737  private:
738   const float sdr_white_level_;
739 };
740 
741 class ColorTransformFromLinear : public ColorTransformPerChannelTransferFn {
742  public:
743   // ColorTransformStep implementation.
ColorTransformFromLinear(ColorSpace::TransferID transfer)744   explicit ColorTransformFromLinear(ColorSpace::TransferID transfer)
745       : ColorTransformPerChannelTransferFn(false), transfer_(transfer) {}
GetFromLinear()746   ColorTransformFromLinear* GetFromLinear() override { return this; }
IsNull()747   bool IsNull() override { return transfer_ == ColorSpace::TransferID::LINEAR; }
748 
749   // ColorTransformPerChannelTransferFn implementation:
Evaluate(float v) const750   float Evaluate(float v) const override { return FromLinear(transfer_, v); }
AppendTransferShaderSource(std::stringstream * src,bool is_glsl) const751   void AppendTransferShaderSource(std::stringstream* src,
752                                   bool is_glsl) const override {
753     std::string scalar_type = is_glsl ? "float" : "half";
754     // This is a string-ized copy-paste from FromLinear.
755     switch (transfer_) {
756       case ColorSpace::TransferID::LOG:
757         *src << "  if (v < 0.01)\n"
758                 "    v = 0.0;\n"
759                 "  else\n"
760                 "    v =  1.0 + log(v) / log(10.0) / 2.0;\n";
761         return;
762       case ColorSpace::TransferID::LOG_SQRT:
763         *src << "  if (v < sqrt(10.0) / 1000.0)\n"
764                 "    v = 0.0;\n"
765                 "  else\n"
766                 "    v = 1.0 + log(v) / log(10.0) / 2.5;\n";
767         return;
768       case ColorSpace::TransferID::IEC61966_2_4:
769         *src << "  " << scalar_type << " a = 1.099296826809442;\n"
770              << "  " << scalar_type << " b = 0.018053968510807;\n"
771              << "  if (v < -b)\n"
772                 "    v = -a * pow(-v, 0.45) + (a - 1.0);\n"
773                 "  else if (v <= b)\n"
774                 "    v = 4.5 * v;\n"
775                 "  else\n"
776                 "    v = a * pow(v, 0.45) - (a - 1.0);\n";
777         return;
778       case ColorSpace::TransferID::BT1361_ECG:
779         *src << "  " << scalar_type << " a = 1.099;\n"
780              << "  " << scalar_type << " b = 0.018;\n"
781              << "  " << scalar_type << " l = 0.0045;\n"
782              << "  if (v < -l)\n"
783                 "    v = -(a * pow(-4.0 * v, 0.45) + (a - 1.0)) / 4.0;\n"
784                 "  else if (v <= b)\n"
785                 "    v = 4.5 * v;\n"
786                 "  else\n"
787                 "    v = a * pow(v, 0.45) - (a - 1.0);\n";
788         return;
789       default:
790         break;
791     }
792     NOTREACHED();
793   }
794 
795  private:
796   friend class ColorTransformToLinear;
797   ColorSpace::TransferID transfer_;
798 };
799 
800 class ColorTransformToLinear : public ColorTransformPerChannelTransferFn {
801  public:
ColorTransformToLinear(ColorSpace::TransferID transfer)802   explicit ColorTransformToLinear(ColorSpace::TransferID transfer)
803       : ColorTransformPerChannelTransferFn(false), transfer_(transfer) {}
804   // ColorTransformStep implementation:
Join(ColorTransformStep * next_untyped)805   bool Join(ColorTransformStep* next_untyped) override {
806     ColorTransformFromLinear* next = next_untyped->GetFromLinear();
807     if (!next)
808       return false;
809     if (transfer_ == next->transfer_) {
810       transfer_ = ColorSpace::TransferID::LINEAR;
811       return true;
812     }
813     return false;
814   }
IsNull()815   bool IsNull() override { return transfer_ == ColorSpace::TransferID::LINEAR; }
816 
817   // ColorTransformPerChannelTransferFn implementation:
Evaluate(float v) const818   float Evaluate(float v) const override { return ToLinear(transfer_, v); }
819 
820   // This is a string-ized copy-paste from ToLinear.
AppendTransferShaderSource(std::stringstream * src,bool is_glsl) const821   void AppendTransferShaderSource(std::stringstream* src,
822                                   bool is_glsl) const override {
823     std::string scalar_type = is_glsl ? "float" : "half";
824     switch (transfer_) {
825       case ColorSpace::TransferID::LOG:
826         *src << "  if (v < 0.0)\n"
827                 "    v = 0.0;\n"
828                 "  else\n"
829                 "    v = pow(10.0, (v - 1.0) * 2.0);\n";
830         return;
831       case ColorSpace::TransferID::LOG_SQRT:
832         *src << "  if (v < 0.0)\n"
833                 "    v = 0.0;\n"
834                 "  else\n"
835                 "    v = pow(10.0, (v - 1.0) * 2.5);\n";
836         return;
837       case ColorSpace::TransferID::IEC61966_2_4:
838         *src << "  " << scalar_type << " a = 1.099296826809442;\n"
839              << "  " << scalar_type << " from_linear_neg_a = -1.047844;\n"
840              << "  " << scalar_type << " from_linear_b = 0.081243;\n"
841              << "  if (v < from_linear_neg_a)\n"
842                 "    v = -pow((a - 1.0 - v) / a, 1.0 / 0.45);\n"
843                 "  else if (v <= from_linear_b)\n"
844                 "    v = v / 4.5;\n"
845                 "  else\n"
846                 "    v = pow((v + a - 1.0) / a, 1.0 / 0.45);\n";
847         return;
848       case ColorSpace::TransferID::BT1361_ECG:
849         *src << "  " << scalar_type << " a = 1.099;\n"
850              << "  " << scalar_type << " from_linear_neg_l = -0.020250;\n"
851              << "  " << scalar_type << " from_linear_b = 0.081000;\n"
852              << "  if (v < from_linear_neg_l)\n"
853                 "    v = -pow((1.0 - a - v * 4.0) / a, 1.0 / 0.45) / 4.0;\n"
854                 "  else if (v <= from_linear_b)\n"
855                 "    v = v / 4.5;\n"
856                 "  else\n"
857                 "    v = pow((v + a - 1.0) / a, 1.0 / 0.45);\n";
858         return;
859       default:
860         break;
861     }
862     NOTREACHED();
863   }
864 
865  private:
866   ColorSpace::TransferID transfer_;
867 };
868 
869 // BT2020 Constant Luminance is different than most other
870 // ways to encode RGB values as YUV. The basic idea is that
871 // transfer functions are applied on the Y value instead of
872 // on the RGB values. However, running the transfer function
873 // on the U and V values doesn't make any sense since they
874 // are centered at 0.5. To work around this, the transfer function
875 // is applied to the Y, R and B values, and then the U and V
876 // values are calculated from that.
877 // In our implementation, the YUV->RGB matrix is used to
878 // convert YUV to RYB (the G value is replaced with an Y value.)
879 // Then we run the transfer function like normal, and finally
880 // this class is inserted as an extra step which takes calculates
881 // the U and V values.
882 class ColorTransformFromBT2020CL : public ColorTransformStep {
883  public:
Transform(ColorTransform::TriStim * YUV,size_t num) const884   void Transform(ColorTransform::TriStim* YUV, size_t num) const override {
885     for (size_t i = 0; i < num; i++) {
886       float Y = YUV[i].x();
887       float U = YUV[i].y() - 0.5;
888       float V = YUV[i].z() - 0.5;
889       float B_Y, R_Y;
890       if (U <= 0) {
891         B_Y = U * (-2.0 * -0.9702);
892       } else {
893         B_Y = U * (2.0 * 0.7910);
894       }
895       if (V <= 0) {
896         R_Y = V * (-2.0 * -0.8591);
897       } else {
898         R_Y = V * (2.0 * 0.4969);
899       }
900       // Return an RYB value, later steps will fix it.
901       YUV[i] = ColorTransform::TriStim(R_Y + Y, Y, B_Y + Y);
902     }
903   }
AppendShaderSource(std::stringstream * hdr,std::stringstream * src,size_t step_index) const904   void AppendShaderSource(std::stringstream* hdr,
905                           std::stringstream* src,
906                           size_t step_index) const override {
907     *hdr << "vec3 BT2020_YUV_to_RYB_Step" << step_index << "(vec3 color) {"
908          << endl;
909     *hdr << "  float Y = color.x;" << endl;
910     *hdr << "  float U = color.y - 0.5;" << endl;
911     *hdr << "  float V = color.z - 0.5;" << endl;
912     *hdr << "  float B_Y = 0.0;" << endl;
913     *hdr << "  float R_Y = 0.0;" << endl;
914     *hdr << "  if (U <= 0.0) {" << endl;
915     *hdr << "    B_Y = U * (-2.0 * -0.9702);" << endl;
916     *hdr << "  } else {" << endl;
917     *hdr << "    B_Y = U * (2.0 * 0.7910);" << endl;
918     *hdr << "  }" << endl;
919     *hdr << "  if (V <= 0.0) {" << endl;
920     *hdr << "    R_Y = V * (-2.0 * -0.8591);" << endl;
921     *hdr << "  } else {" << endl;
922     *hdr << "    R_Y = V * (2.0 * 0.4969);" << endl;
923     *hdr << "  }" << endl;
924     *hdr << "  return vec3(R_Y + Y, Y, B_Y + Y);" << endl;
925     *hdr << "}" << endl;
926 
927     *src << "  color.rgb = BT2020_YUV_to_RYB_Step" << step_index
928          << "(color.rgb);" << endl;
929   }
930 
AppendSkShaderSource(std::stringstream * src) const931   void AppendSkShaderSource(std::stringstream* src) const override {
932     NOTREACHED();
933   }
934 };
935 
AppendColorSpaceToColorSpaceTransform(const ColorSpace & src,int src_bit_depth,const ColorSpace & dst,int dst_bit_depth)936 void ColorTransformInternal::AppendColorSpaceToColorSpaceTransform(
937     const ColorSpace& src,
938     int src_bit_depth,
939     const ColorSpace& dst,
940     int dst_bit_depth) {
941   steps_.push_back(std::make_unique<ColorTransformMatrix>(
942       GetRangeAdjustMatrix(src, src_bit_depth)));
943 
944   if (src.GetMatrixID() == ColorSpace::MatrixID::BT2020_CL) {
945     // BT2020 CL is a special case.
946     steps_.push_back(std::make_unique<ColorTransformFromBT2020CL>());
947   } else {
948     steps_.push_back(
949         std::make_unique<ColorTransformMatrix>(Invert(GetTransferMatrix(src))));
950   }
951 
952   // If the target color space is not defined, just apply the adjust and
953   // tranfer matrices. This path is used by YUV to RGB color conversion
954   // when full color conversion is not enabled.
955   if (!dst.IsValid())
956     return;
957 
958   skcms_TransferFunction src_to_linear_fn;
959   if (src.GetTransferFunction(&src_to_linear_fn)) {
960     steps_.push_back(std::make_unique<ColorTransformSkTransferFn>(
961         src_to_linear_fn, src.HasExtendedSkTransferFn()));
962   } else if (src.GetTransferID() == ColorSpace::TransferID::ARIB_STD_B67) {
963     float sdr_white_level = 0.f;
964     src.GetSDRWhiteLevel(&sdr_white_level);
965     steps_.push_back(
966         std::make_unique<ColorTransformHLGToLinear>(sdr_white_level));
967   } else if (src.GetTransferID() == ColorSpace::TransferID::SMPTEST2084) {
968     float sdr_white_level = 0.f;
969     src.GetSDRWhiteLevel(&sdr_white_level);
970     steps_.push_back(
971         std::make_unique<ColorTransformPQToLinear>(sdr_white_level));
972   } else if (src.GetTransferID() == ColorSpace::TransferID::PIECEWISE_HDR) {
973     skcms_TransferFunction fn;
974     float p, q, r;
975     ColorTransformPiecewiseHDR::GetParams(src, &fn, &p, &q, &r);
976     steps_.push_back(std::make_unique<ColorTransformPiecewiseHDR>(fn, p, q, r));
977   } else {
978     steps_.push_back(
979         std::make_unique<ColorTransformToLinear>(src.GetTransferID()));
980   }
981 
982   if (src.GetMatrixID() == ColorSpace::MatrixID::BT2020_CL) {
983     // BT2020 CL is a special case.
984     steps_.push_back(
985         std::make_unique<ColorTransformMatrix>(Invert(GetTransferMatrix(src))));
986   }
987   steps_.push_back(
988       std::make_unique<ColorTransformMatrix>(GetPrimaryTransform(src)));
989 
990   steps_.push_back(
991       std::make_unique<ColorTransformMatrix>(Invert(GetPrimaryTransform(dst))));
992   if (dst.GetMatrixID() == ColorSpace::MatrixID::BT2020_CL) {
993     // BT2020 CL is a special case.
994     steps_.push_back(
995         std::make_unique<ColorTransformMatrix>(GetTransferMatrix(dst)));
996   }
997 
998   skcms_TransferFunction dst_from_linear_fn;
999   if (dst.GetInverseTransferFunction(&dst_from_linear_fn)) {
1000     steps_.push_back(std::make_unique<ColorTransformSkTransferFn>(
1001         dst_from_linear_fn, dst.HasExtendedSkTransferFn()));
1002   } else if (dst.GetTransferID() == ColorSpace::TransferID::ARIB_STD_B67) {
1003     float sdr_white_level = 0.f;
1004     dst.GetSDRWhiteLevel(&sdr_white_level);
1005     steps_.push_back(
1006         std::make_unique<ColorTransformHLGFromLinear>(sdr_white_level));
1007   } else if (dst.GetTransferID() == ColorSpace::TransferID::SMPTEST2084) {
1008     float sdr_white_level = 0.f;
1009     dst.GetSDRWhiteLevel(&sdr_white_level);
1010     steps_.push_back(
1011         std::make_unique<ColorTransformPQFromLinear>(sdr_white_level));
1012   } else if (dst.GetTransferID() == ColorSpace::TransferID::PIECEWISE_HDR) {
1013     skcms_TransferFunction fn;
1014     float p, q, r;
1015     ColorTransformPiecewiseHDR::GetParams(dst, &fn, &p, &q, &r);
1016     ColorTransformPiecewiseHDR::InvertParams(&fn, &p, &q, &r);
1017     steps_.push_back(std::make_unique<ColorTransformPiecewiseHDR>(fn, p, q, r));
1018   } else {
1019     steps_.push_back(
1020         std::make_unique<ColorTransformFromLinear>(dst.GetTransferID()));
1021   }
1022 
1023   if (dst.GetMatrixID() == ColorSpace::MatrixID::BT2020_CL) {
1024     NOTREACHED();
1025   } else {
1026     steps_.push_back(
1027         std::make_unique<ColorTransformMatrix>(GetTransferMatrix(dst)));
1028   }
1029 
1030   steps_.push_back(std::make_unique<ColorTransformMatrix>(
1031       Invert(GetRangeAdjustMatrix(dst, dst_bit_depth))));
1032 }
1033 
ColorTransformInternal(const ColorSpace & src,int src_bit_depth,const ColorSpace & dst,int dst_bit_depth,Intent intent)1034 ColorTransformInternal::ColorTransformInternal(const ColorSpace& src,
1035                                                int src_bit_depth,
1036                                                const ColorSpace& dst,
1037                                                int dst_bit_depth,
1038                                                Intent intent)
1039     : src_(src), dst_(dst) {
1040   // If no source color space is specified, do no transformation.
1041   // TODO(ccameron): We may want dst assume sRGB at some point in the future.
1042   if (!src_.IsValid())
1043     return;
1044   AppendColorSpaceToColorSpaceTransform(src_, src_bit_depth, dst_,
1045                                         dst_bit_depth);
1046   if (intent != Intent::TEST_NO_OPT)
1047     Simplify();
1048 }
1049 
GetShaderSource() const1050 std::string ColorTransformInternal::GetShaderSource() const {
1051   std::stringstream hdr;
1052   std::stringstream src;
1053   InitStringStream(&hdr);
1054   InitStringStream(&src);
1055   src << "vec3 DoColorConversion(vec3 color) {" << endl;
1056   size_t step_index = 0;
1057   for (const auto& step : steps_)
1058     step->AppendShaderSource(&hdr, &src, step_index++);
1059   src << "  return color;" << endl;
1060   src << "}" << endl;
1061   return hdr.str() + src.str();
1062 }
1063 
GetSkShaderSource() const1064 std::string ColorTransformInternal::GetSkShaderSource() const {
1065   std::stringstream src;
1066   InitStringStream(&src);
1067   for (const auto& step : steps_)
1068     step->AppendSkShaderSource(&src);
1069   return src.str();
1070 }
1071 
~ColorTransformInternal()1072 ColorTransformInternal::~ColorTransformInternal() {}
1073 
Simplify()1074 void ColorTransformInternal::Simplify() {
1075   for (auto iter = steps_.begin(); iter != steps_.end();) {
1076     std::unique_ptr<ColorTransformStep>& this_step = *iter;
1077 
1078     // Try to Join |next_step| into |this_step|. If successful, re-visit the
1079     // step before |this_step|.
1080     auto iter_next = iter;
1081     iter_next++;
1082     if (iter_next != steps_.end()) {
1083       std::unique_ptr<ColorTransformStep>& next_step = *iter_next;
1084       if (this_step->Join(next_step.get())) {
1085         steps_.erase(iter_next);
1086         if (iter != steps_.begin())
1087           --iter;
1088         continue;
1089       }
1090     }
1091 
1092     // If |this_step| step is a no-op, remove it, and re-visit the step before
1093     // |this_step|.
1094     if (this_step->IsNull()) {
1095       iter = steps_.erase(iter);
1096       if (iter != steps_.begin())
1097         --iter;
1098       continue;
1099     }
1100 
1101     ++iter;
1102   }
1103 }
1104 
1105 // static
NewColorTransform(const ColorSpace & src,int src_bit_depth,const ColorSpace & dst,int dst_bit_depth,Intent intent)1106 std::unique_ptr<ColorTransform> ColorTransform::NewColorTransform(
1107     const ColorSpace& src,
1108     int src_bit_depth,
1109     const ColorSpace& dst,
1110     int dst_bit_depth,
1111     Intent intent) {
1112   return std::make_unique<ColorTransformInternal>(src, src_bit_depth, dst,
1113                                                   dst_bit_depth, intent);
1114 }
1115 
ColorTransform()1116 ColorTransform::ColorTransform() {}
~ColorTransform()1117 ColorTransform::~ColorTransform() {}
1118 
1119 }  // namespace gfx
1120