SkColorSpaceXformSteps.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. /*
  2. * Copyright 2018 Google Inc.
  3. *
  4. * Use of this source code is governed by a BSD-style license that can be
  5. * found in the LICENSE file.
  6. */
  7. #include "include/third_party/skcms/skcms.h"
  8. #include "src/core/SkColorSpacePriv.h"
  9. #include "src/core/SkColorSpaceXformSteps.h"
  10. #include "src/core/SkRasterPipeline.h"
  11. // TODO(mtklein): explain the logic of this file
  12. bool SkColorSpaceXformSteps::Required(SkColorSpace* src, SkColorSpace* dst) {
  13. // Any SkAlphaType will work fine here as long as we use the same one.
  14. SkAlphaType at = kPremul_SkAlphaType;
  15. return 0 != SkColorSpaceXformSteps(src, at,
  16. dst, at).flags.mask();
  17. // TODO(mtklein): quicker impl. that doesn't construct an SkColorSpaceXformSteps?
  18. }
  19. SkColorSpaceXformSteps::SkColorSpaceXformSteps(SkColorSpace* src, SkAlphaType srcAT,
  20. SkColorSpace* dst, SkAlphaType dstAT) {
  21. // Opaque outputs are treated as the same alpha type as the source input.
  22. // TODO: we'd really like to have a good way of explaining why we think this is useful.
  23. if (dstAT == kOpaque_SkAlphaType) {
  24. dstAT = srcAT;
  25. }
  26. // We have some options about what to do with null src or dst here.
  27. // This pair seems to be the most consistent with legacy expectations.
  28. if (!src) { src = sk_srgb_singleton(); }
  29. if (!dst) { dst = src; }
  30. if (src->hash() == dst->hash() && srcAT == dstAT) {
  31. SkASSERT(SkColorSpace::Equals(src,dst));
  32. return;
  33. }
  34. this->flags.unpremul = srcAT == kPremul_SkAlphaType;
  35. this->flags.linearize = !src->gammaIsLinear();
  36. this->flags.gamut_transform = src->toXYZD50Hash() != dst->toXYZD50Hash();
  37. this->flags.encode = !dst->gammaIsLinear();
  38. this->flags.premul = srcAT != kOpaque_SkAlphaType && dstAT == kPremul_SkAlphaType;
  39. if (this->flags.gamut_transform) {
  40. float row_major[9]; // TODO: switch src_to_dst_matrix to row-major
  41. src->gamutTransformTo(dst, row_major);
  42. this->src_to_dst_matrix[0] = row_major[0];
  43. this->src_to_dst_matrix[1] = row_major[3];
  44. this->src_to_dst_matrix[2] = row_major[6];
  45. this->src_to_dst_matrix[3] = row_major[1];
  46. this->src_to_dst_matrix[4] = row_major[4];
  47. this->src_to_dst_matrix[5] = row_major[7];
  48. this->src_to_dst_matrix[6] = row_major[2];
  49. this->src_to_dst_matrix[7] = row_major[5];
  50. this->src_to_dst_matrix[8] = row_major[8];
  51. } else {
  52. #ifdef SK_DEBUG
  53. skcms_Matrix3x3 srcM, dstM;
  54. src->toXYZD50(&srcM);
  55. dst->toXYZD50(&dstM);
  56. SkASSERT(0 == memcmp(&srcM, &dstM, 9*sizeof(float)) && "Hash collision");
  57. #endif
  58. }
  59. // Fill out all the transfer functions we'll use.
  60. src-> transferFn(&this->srcTF .g);
  61. dst->invTransferFn(&this->dstTFInv.g);
  62. this->srcTF_is_sRGB = src->gammaCloseToSRGB();
  63. this->dstTF_is_sRGB = dst->gammaCloseToSRGB();
  64. // If we linearize then immediately reencode with the same transfer function, skip both.
  65. if ( this->flags.linearize &&
  66. !this->flags.gamut_transform &&
  67. this->flags.encode &&
  68. src->transferFnHash() == dst->transferFnHash())
  69. {
  70. #ifdef SK_DEBUG
  71. float dstTF[7];
  72. dst->transferFn(dstTF);
  73. for (int i = 0; i < 7; i++) {
  74. SkASSERT( (&srcTF.g)[i] == dstTF[i] && "Hash collision" );
  75. }
  76. #endif
  77. this->flags.linearize = false;
  78. this->flags.encode = false;
  79. }
  80. // Skip unpremul...premul if there are no non-linear operations between.
  81. if ( this->flags.unpremul &&
  82. !this->flags.linearize &&
  83. !this->flags.encode &&
  84. this->flags.premul)
  85. {
  86. this->flags.unpremul = false;
  87. this->flags.premul = false;
  88. }
  89. }
  90. void SkColorSpaceXformSteps::apply(float* rgba) const {
  91. if (flags.unpremul) {
  92. // I don't know why isfinite(x) stopped working on the Chromecast bots...
  93. auto is_finite = [](float x) { return x*0 == 0; };
  94. float invA = is_finite(1.0f / rgba[3]) ? 1.0f / rgba[3] : 0;
  95. rgba[0] *= invA;
  96. rgba[1] *= invA;
  97. rgba[2] *= invA;
  98. }
  99. if (flags.linearize) {
  100. skcms_TransferFunction tf;
  101. memcpy(&tf, &srcTF, 7*sizeof(float));
  102. rgba[0] = skcms_TransferFunction_eval(&tf, rgba[0]);
  103. rgba[1] = skcms_TransferFunction_eval(&tf, rgba[1]);
  104. rgba[2] = skcms_TransferFunction_eval(&tf, rgba[2]);
  105. }
  106. if (flags.gamut_transform) {
  107. float temp[3] = { rgba[0], rgba[1], rgba[2] };
  108. for (int i = 0; i < 3; ++i) {
  109. rgba[i] = src_to_dst_matrix[ i] * temp[0] +
  110. src_to_dst_matrix[3 + i] * temp[1] +
  111. src_to_dst_matrix[6 + i] * temp[2];
  112. }
  113. }
  114. if (flags.encode) {
  115. skcms_TransferFunction tf;
  116. memcpy(&tf, &dstTFInv, 7*sizeof(float));
  117. rgba[0] = skcms_TransferFunction_eval(&tf, rgba[0]);
  118. rgba[1] = skcms_TransferFunction_eval(&tf, rgba[1]);
  119. rgba[2] = skcms_TransferFunction_eval(&tf, rgba[2]);
  120. }
  121. if (flags.premul) {
  122. rgba[0] *= rgba[3];
  123. rgba[1] *= rgba[3];
  124. rgba[2] *= rgba[3];
  125. }
  126. }
  127. void SkColorSpaceXformSteps::apply(SkRasterPipeline* p, bool src_is_normalized) const {
  128. #if defined(SK_LEGACY_SRGB_STAGE_CHOICE)
  129. src_is_normalized = true;
  130. #endif
  131. if (flags.unpremul) { p->append(SkRasterPipeline::unpremul); }
  132. if (flags.linearize) {
  133. if (src_is_normalized && srcTF_is_sRGB) {
  134. p->append(SkRasterPipeline::from_srgb);
  135. } else if (srcTF.a == 1 &&
  136. srcTF.b == 0 &&
  137. srcTF.c == 0 &&
  138. srcTF.d == 0 &&
  139. srcTF.e == 0 &&
  140. srcTF.f == 0) {
  141. p->append(SkRasterPipeline::gamma_, &srcTF.g);
  142. } else {
  143. p->append(SkRasterPipeline::parametric, &srcTF);
  144. }
  145. }
  146. if (flags.gamut_transform) {
  147. p->append(SkRasterPipeline::matrix_3x3, &src_to_dst_matrix);
  148. }
  149. if (flags.encode) {
  150. if (src_is_normalized && dstTF_is_sRGB) {
  151. p->append(SkRasterPipeline::to_srgb);
  152. } else if (dstTFInv.a == 1 &&
  153. dstTFInv.b == 0 &&
  154. dstTFInv.c == 0 &&
  155. dstTFInv.d == 0 &&
  156. dstTFInv.e == 0 &&
  157. dstTFInv.f == 0) {
  158. p->append(SkRasterPipeline::gamma_, &dstTFInv.g);
  159. } else {
  160. p->append(SkRasterPipeline::parametric, &dstTFInv);
  161. }
  162. }
  163. if (flags.premul) { p->append(SkRasterPipeline::premul); }
  164. }