SkHighContrastFilter.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. /*
  2. * Copyright 2017 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/core/SkString.h"
  8. #include "include/effects/SkHighContrastFilter.h"
  9. #include "include/private/SkColorData.h"
  10. #include "src/core/SkArenaAlloc.h"
  11. #include "src/core/SkEffectPriv.h"
  12. #include "src/core/SkRasterPipeline.h"
  13. #include "src/core/SkReadBuffer.h"
  14. #include "src/core/SkWriteBuffer.h"
  15. #if SK_SUPPORT_GPU
  16. #include "include/gpu/GrContext.h"
  17. #include "src/gpu/GrColorSpaceInfo.h"
  18. #include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
  19. #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
  20. #endif
  21. using InvertStyle = SkHighContrastConfig::InvertStyle;
  22. class SkHighContrast_Filter : public SkColorFilter {
  23. public:
  24. SkHighContrast_Filter(const SkHighContrastConfig& config) {
  25. fConfig = config;
  26. // Clamp contrast to just inside -1 to 1 to avoid division by zero.
  27. fConfig.fContrast = SkScalarPin(fConfig.fContrast,
  28. -1.0f + FLT_EPSILON,
  29. 1.0f - FLT_EPSILON);
  30. }
  31. ~SkHighContrast_Filter() override {}
  32. #if SK_SUPPORT_GPU
  33. std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(
  34. GrRecordingContext*, const GrColorSpaceInfo&) const override;
  35. #endif
  36. bool onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const override;
  37. protected:
  38. void flatten(SkWriteBuffer&) const override;
  39. private:
  40. SK_FLATTENABLE_HOOKS(SkHighContrast_Filter)
  41. SkHighContrastConfig fConfig;
  42. friend class SkHighContrastFilter;
  43. typedef SkColorFilter INHERITED;
  44. };
  45. bool SkHighContrast_Filter::onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const {
  46. SkRasterPipeline* p = rec.fPipeline;
  47. SkArenaAlloc* alloc = rec.fAlloc;
  48. if (!shaderIsOpaque) {
  49. p->append(SkRasterPipeline::unpremul);
  50. }
  51. // Linearize before applying high-contrast filter.
  52. auto tf = alloc->make<skcms_TransferFunction>();
  53. if (rec.fDstCS) {
  54. rec.fDstCS->transferFn(&tf->g);
  55. } else {
  56. // Historically we approximate untagged destinations as gamma 2.
  57. // TODO: sRGB?
  58. *tf = {2,1, 0,0,0,0,0};
  59. }
  60. p->append(SkRasterPipeline::parametric, tf);
  61. if (fConfig.fGrayscale) {
  62. float r = SK_LUM_COEFF_R;
  63. float g = SK_LUM_COEFF_G;
  64. float b = SK_LUM_COEFF_B;
  65. float* matrix = alloc->makeArray<float>(12);
  66. matrix[0] = matrix[1] = matrix[2] = r;
  67. matrix[3] = matrix[4] = matrix[5] = g;
  68. matrix[6] = matrix[7] = matrix[8] = b;
  69. p->append(SkRasterPipeline::matrix_3x4, matrix);
  70. }
  71. if (fConfig.fInvertStyle == InvertStyle::kInvertBrightness) {
  72. float* matrix = alloc->makeArray<float>(12);
  73. matrix[0] = matrix[4] = matrix[8] = -1;
  74. matrix[9] = matrix[10] = matrix[11] = 1;
  75. p->append(SkRasterPipeline::matrix_3x4, matrix);
  76. } else if (fConfig.fInvertStyle == InvertStyle::kInvertLightness) {
  77. p->append(SkRasterPipeline::rgb_to_hsl);
  78. float* matrix = alloc->makeArray<float>(12);
  79. matrix[0] = matrix[4] = matrix[11] = 1;
  80. matrix[8] = -1;
  81. p->append(SkRasterPipeline::matrix_3x4, matrix);
  82. p->append(SkRasterPipeline::hsl_to_rgb);
  83. }
  84. if (fConfig.fContrast != 0.0) {
  85. float* matrix = alloc->makeArray<float>(12);
  86. float c = fConfig.fContrast;
  87. float m = (1 + c) / (1 - c);
  88. float b = (-0.5f * m + 0.5f);
  89. matrix[0] = matrix[4] = matrix[8] = m;
  90. matrix[9] = matrix[10] = matrix[11] = b;
  91. p->append(SkRasterPipeline::matrix_3x4, matrix);
  92. }
  93. p->append(SkRasterPipeline::clamp_0);
  94. p->append(SkRasterPipeline::clamp_1);
  95. // Re-encode back from linear.
  96. auto invTF = alloc->make<skcms_TransferFunction>();
  97. if (rec.fDstCS) {
  98. rec.fDstCS->invTransferFn(&invTF->g);
  99. } else {
  100. // See above... historically untagged == gamma 2 in this filter.
  101. *invTF ={0.5f,1, 0,0,0,0,0};
  102. }
  103. p->append(SkRasterPipeline::parametric, invTF);
  104. if (!shaderIsOpaque) {
  105. p->append(SkRasterPipeline::premul);
  106. }
  107. return true;
  108. }
  109. void SkHighContrast_Filter::flatten(SkWriteBuffer& buffer) const {
  110. buffer.writeBool(fConfig.fGrayscale);
  111. buffer.writeInt(static_cast<int>(fConfig.fInvertStyle));
  112. buffer.writeScalar(fConfig.fContrast);
  113. }
  114. sk_sp<SkFlattenable> SkHighContrast_Filter::CreateProc(SkReadBuffer& buffer) {
  115. SkHighContrastConfig config;
  116. config.fGrayscale = buffer.readBool();
  117. config.fInvertStyle = buffer.read32LE(InvertStyle::kLast);
  118. config.fContrast = buffer.readScalar();
  119. return SkHighContrastFilter::Make(config);
  120. }
  121. sk_sp<SkColorFilter> SkHighContrastFilter::Make(
  122. const SkHighContrastConfig& config) {
  123. if (!config.isValid()) {
  124. return nullptr;
  125. }
  126. return sk_make_sp<SkHighContrast_Filter>(config);
  127. }
  128. void SkHighContrastFilter::RegisterFlattenables() {
  129. SK_REGISTER_FLATTENABLE(SkHighContrast_Filter);
  130. }
  131. #if SK_SUPPORT_GPU
  132. class HighContrastFilterEffect : public GrFragmentProcessor {
  133. public:
  134. static std::unique_ptr<GrFragmentProcessor> Make(const SkHighContrastConfig& config,
  135. bool linearize) {
  136. return std::unique_ptr<GrFragmentProcessor>(new HighContrastFilterEffect(config,
  137. linearize));
  138. }
  139. const char* name() const override { return "HighContrastFilter"; }
  140. const SkHighContrastConfig& config() const { return fConfig; }
  141. bool linearize() const { return fLinearize; }
  142. std::unique_ptr<GrFragmentProcessor> clone() const override {
  143. return Make(fConfig, fLinearize);
  144. }
  145. private:
  146. HighContrastFilterEffect(const SkHighContrastConfig& config, bool linearize)
  147. : INHERITED(kHighContrastFilterEffect_ClassID, kNone_OptimizationFlags)
  148. , fConfig(config)
  149. , fLinearize(linearize) {}
  150. GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
  151. virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps,
  152. GrProcessorKeyBuilder* b) const override;
  153. bool onIsEqual(const GrFragmentProcessor& other) const override {
  154. const HighContrastFilterEffect& that = other.cast<HighContrastFilterEffect>();
  155. return fConfig.fGrayscale == that.fConfig.fGrayscale &&
  156. fConfig.fInvertStyle == that.fConfig.fInvertStyle &&
  157. fConfig.fContrast == that.fConfig.fContrast &&
  158. fLinearize == that.fLinearize;
  159. }
  160. SkHighContrastConfig fConfig;
  161. bool fLinearize;
  162. typedef GrFragmentProcessor INHERITED;
  163. };
  164. class GLHighContrastFilterEffect : public GrGLSLFragmentProcessor {
  165. public:
  166. static void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*);
  167. protected:
  168. void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
  169. void emitCode(EmitArgs& args) override;
  170. private:
  171. UniformHandle fContrastUni;
  172. typedef GrGLSLFragmentProcessor INHERITED;
  173. };
  174. GrGLSLFragmentProcessor* HighContrastFilterEffect::onCreateGLSLInstance() const {
  175. return new GLHighContrastFilterEffect();
  176. }
  177. void HighContrastFilterEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
  178. GrProcessorKeyBuilder* b) const {
  179. GLHighContrastFilterEffect::GenKey(*this, caps, b);
  180. }
  181. void GLHighContrastFilterEffect::onSetData(const GrGLSLProgramDataManager& pdm,
  182. const GrFragmentProcessor& proc) {
  183. const HighContrastFilterEffect& hcfe = proc.cast<HighContrastFilterEffect>();
  184. pdm.set1f(fContrastUni, hcfe.config().fContrast);
  185. }
  186. void GLHighContrastFilterEffect::GenKey(
  187. const GrProcessor& proc, const GrShaderCaps&, GrProcessorKeyBuilder* b) {
  188. const HighContrastFilterEffect& hcfe = proc.cast<HighContrastFilterEffect>();
  189. b->add32(static_cast<uint32_t>(hcfe.config().fGrayscale));
  190. b->add32(static_cast<uint32_t>(hcfe.config().fInvertStyle));
  191. b->add32(hcfe.linearize() ? 1 : 0);
  192. }
  193. void GLHighContrastFilterEffect::emitCode(EmitArgs& args) {
  194. const HighContrastFilterEffect& hcfe = args.fFp.cast<HighContrastFilterEffect>();
  195. const SkHighContrastConfig& config = hcfe.config();
  196. const char* contrast;
  197. fContrastUni = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType,
  198. "contrast", &contrast);
  199. GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
  200. fragBuilder->codeAppendf("half4 color = %s;", args.fInputColor);
  201. // Unpremultiply. The max() is to guard against 0 / 0.
  202. fragBuilder->codeAppendf("half nonZeroAlpha = max(color.a, 0.0001);");
  203. fragBuilder->codeAppendf("color = half4(color.rgb / nonZeroAlpha, nonZeroAlpha);");
  204. if (hcfe.linearize()) {
  205. fragBuilder->codeAppend("color.rgb = color.rgb * color.rgb;");
  206. }
  207. // Grayscale.
  208. if (config.fGrayscale) {
  209. fragBuilder->codeAppendf("half luma = dot(color, half4(%f, %f, %f, 0));",
  210. SK_LUM_COEFF_R, SK_LUM_COEFF_G, SK_LUM_COEFF_B);
  211. fragBuilder->codeAppendf("color = half4(luma, luma, luma, 0);");
  212. }
  213. if (config.fInvertStyle == InvertStyle::kInvertBrightness) {
  214. fragBuilder->codeAppendf("color = half4(1, 1, 1, 1) - color;");
  215. }
  216. if (config.fInvertStyle == InvertStyle::kInvertLightness) {
  217. // Convert from RGB to HSL.
  218. fragBuilder->codeAppendf("half fmax = max(color.r, max(color.g, color.b));");
  219. fragBuilder->codeAppendf("half fmin = min(color.r, min(color.g, color.b));");
  220. fragBuilder->codeAppendf("half l = (fmax + fmin) / 2;");
  221. fragBuilder->codeAppendf("half h;");
  222. fragBuilder->codeAppendf("half s;");
  223. fragBuilder->codeAppendf("if (fmax == fmin) {");
  224. fragBuilder->codeAppendf(" h = 0;");
  225. fragBuilder->codeAppendf(" s = 0;");
  226. fragBuilder->codeAppendf("} else {");
  227. fragBuilder->codeAppendf(" half d = fmax - fmin;");
  228. fragBuilder->codeAppendf(" s = l > 0.5 ?");
  229. fragBuilder->codeAppendf(" d / (2 - fmax - fmin) :");
  230. fragBuilder->codeAppendf(" d / (fmax + fmin);");
  231. // We'd like to just write "if (color.r == fmax) { ... }". On many GPUs, running the
  232. // angle_d3d9_es2 config, that failed. It seems that max(x, y) is not necessarily equal
  233. // to either x or y. Tried several ways to fix it, but this was the only reasonable fix.
  234. fragBuilder->codeAppendf(" if (color.r >= color.g && color.r >= color.b) {");
  235. fragBuilder->codeAppendf(" h = (color.g - color.b) / d + ");
  236. fragBuilder->codeAppendf(" (color.g < color.b ? 6 : 0);");
  237. fragBuilder->codeAppendf(" } else if (color.g >= color.b) {");
  238. fragBuilder->codeAppendf(" h = (color.b - color.r) / d + 2;");
  239. fragBuilder->codeAppendf(" } else {");
  240. fragBuilder->codeAppendf(" h = (color.r - color.g) / d + 4;");
  241. fragBuilder->codeAppendf(" }");
  242. fragBuilder->codeAppendf("}");
  243. fragBuilder->codeAppendf("h /= 6;");
  244. fragBuilder->codeAppendf("l = 1.0 - l;");
  245. // Convert back from HSL to RGB.
  246. SkString hue2rgbFuncName;
  247. const GrShaderVar gHue2rgbArgs[] = {
  248. GrShaderVar("p", kHalf_GrSLType),
  249. GrShaderVar("q", kHalf_GrSLType),
  250. GrShaderVar("t", kHalf_GrSLType),
  251. };
  252. fragBuilder->emitFunction(kHalf_GrSLType,
  253. "hue2rgb",
  254. SK_ARRAY_COUNT(gHue2rgbArgs),
  255. gHue2rgbArgs,
  256. "if (t < 0)"
  257. " t += 1;"
  258. "if (t > 1)"
  259. " t -= 1;"
  260. "if (t < 1/6.)"
  261. " return p + (q - p) * 6 * t;"
  262. "if (t < 1/2.)"
  263. " return q;"
  264. "if (t < 2/3.)"
  265. " return p + (q - p) * (2/3. - t) * 6;"
  266. "return p;",
  267. &hue2rgbFuncName);
  268. fragBuilder->codeAppendf("if (s == 0) {");
  269. fragBuilder->codeAppendf(" color = half4(l, l, l, 0);");
  270. fragBuilder->codeAppendf("} else {");
  271. fragBuilder->codeAppendf(" half q = l < 0.5 ? l * (1 + s) : l + s - l * s;");
  272. fragBuilder->codeAppendf(" half p = 2 * l - q;");
  273. fragBuilder->codeAppendf(" color.r = %s(p, q, h + 1/3.);", hue2rgbFuncName.c_str());
  274. fragBuilder->codeAppendf(" color.g = %s(p, q, h);", hue2rgbFuncName.c_str());
  275. fragBuilder->codeAppendf(" color.b = %s(p, q, h - 1/3.);", hue2rgbFuncName.c_str());
  276. fragBuilder->codeAppendf("}");
  277. }
  278. // Contrast.
  279. fragBuilder->codeAppendf("if (%s != 0) {", contrast);
  280. fragBuilder->codeAppendf(" half m = (1 + %s) / (1 - %s);", contrast, contrast);
  281. fragBuilder->codeAppendf(" half off = (-0.5 * m + 0.5);");
  282. fragBuilder->codeAppendf(" color = m * color + off;");
  283. fragBuilder->codeAppendf("}");
  284. // Clamp.
  285. fragBuilder->codeAppendf("color = saturate(color);");
  286. if (hcfe.linearize()) {
  287. fragBuilder->codeAppend("color.rgb = sqrt(color.rgb);");
  288. }
  289. // Restore the original alpha and premultiply.
  290. fragBuilder->codeAppendf("color.a = %s.a;", args.fInputColor);
  291. fragBuilder->codeAppendf("color.rgb *= color.a;");
  292. // Copy to the output color.
  293. fragBuilder->codeAppendf("%s = color;", args.fOutputColor);
  294. }
  295. std::unique_ptr<GrFragmentProcessor> SkHighContrast_Filter::asFragmentProcessor(
  296. GrRecordingContext*, const GrColorSpaceInfo& csi) const {
  297. bool linearize = !csi.isLinearlyBlended();
  298. return HighContrastFilterEffect::Make(fConfig, linearize);
  299. }
  300. #endif