Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 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 | |
| 8 | #include "SkHighContrastFilter.h" |
Mike Reed | b9641bd | 2017-05-04 10:57:40 -0400 | [diff] [blame] | 9 | #include "SkPM4f.h" |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 10 | #include "SkArenaAlloc.h" |
| 11 | #include "SkRasterPipeline.h" |
| 12 | #include "SkReadBuffer.h" |
| 13 | #include "SkString.h" |
| 14 | #include "SkWriteBuffer.h" |
Mike Klein | 8b96b37 | 2017-05-12 12:24:04 -0400 | [diff] [blame] | 15 | #include "../jumper/SkJumper.h" |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 16 | |
| 17 | #if SK_SUPPORT_GPU |
Brian Osman | 8c68c65 | 2017-11-06 15:25:10 -0500 | [diff] [blame] | 18 | #include "GrColorSpaceInfo.h" |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 19 | #include "GrContext.h" |
| 20 | #include "glsl/GrGLSLFragmentProcessor.h" |
| 21 | #include "glsl/GrGLSLFragmentShaderBuilder.h" |
| 22 | #endif |
| 23 | |
| 24 | using InvertStyle = SkHighContrastConfig::InvertStyle; |
| 25 | |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 26 | class SkHighContrast_Filter : public SkColorFilter { |
| 27 | public: |
| 28 | SkHighContrast_Filter(const SkHighContrastConfig& config) { |
| 29 | fConfig = config; |
| 30 | // Clamp contrast to just inside -1 to 1 to avoid division by zero. |
| 31 | fConfig.fContrast = SkScalarPin(fConfig.fContrast, |
| 32 | -1.0f + FLT_EPSILON, |
| 33 | 1.0f - FLT_EPSILON); |
| 34 | } |
| 35 | |
Brian Salomon | d3b6597 | 2017-03-22 12:05:03 -0400 | [diff] [blame] | 36 | ~SkHighContrast_Filter() override {} |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 37 | |
| 38 | #if SK_SUPPORT_GPU |
Brian Salomon | 4cbb6e6 | 2017-10-25 15:12:19 -0400 | [diff] [blame] | 39 | std::unique_ptr<GrFragmentProcessor> asFragmentProcessor( |
| 40 | GrContext*, const GrColorSpaceInfo&) const override; |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 41 | #endif |
| 42 | |
Mike Klein | fdf3103 | 2017-05-09 14:57:58 -0400 | [diff] [blame] | 43 | void onAppendStages(SkRasterPipeline* p, |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 44 | SkColorSpace* dst, |
| 45 | SkArenaAlloc* scratch, |
| 46 | bool shaderIsOpaque) const override; |
| 47 | |
| 48 | SK_TO_STRING_OVERRIDE() |
| 49 | |
| 50 | SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkHighContrast_Filter) |
| 51 | |
| 52 | protected: |
| 53 | void flatten(SkWriteBuffer&) const override; |
| 54 | |
| 55 | private: |
| 56 | SkHighContrastConfig fConfig; |
| 57 | |
| 58 | friend class SkHighContrastFilter; |
| 59 | |
| 60 | typedef SkColorFilter INHERITED; |
| 61 | }; |
| 62 | |
Mike Klein | fdf3103 | 2017-05-09 14:57:58 -0400 | [diff] [blame] | 63 | void SkHighContrast_Filter::onAppendStages(SkRasterPipeline* p, |
Mike Klein | 8b96b37 | 2017-05-12 12:24:04 -0400 | [diff] [blame] | 64 | SkColorSpace* dstCS, |
| 65 | SkArenaAlloc* alloc, |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 66 | bool shaderIsOpaque) const { |
| 67 | if (!shaderIsOpaque) { |
| 68 | p->append(SkRasterPipeline::unpremul); |
| 69 | } |
| 70 | |
Mike Klein | 8b96b37 | 2017-05-12 12:24:04 -0400 | [diff] [blame] | 71 | if (!dstCS) { |
| 72 | // In legacy draws this effect approximately linearizes by squaring. |
| 73 | // When non-legacy, we're already (better) linearized. |
| 74 | auto square = alloc->make<SkJumper_ParametricTransferFunction>(); |
| 75 | square->G = 2.0f; square->A = 1.0f; |
| 76 | square->B = square->C = square->D = square->E = square->F = 0; |
| 77 | |
| 78 | p->append(SkRasterPipeline::parametric_r, square); |
| 79 | p->append(SkRasterPipeline::parametric_g, square); |
| 80 | p->append(SkRasterPipeline::parametric_b, square); |
| 81 | } |
| 82 | |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 83 | if (fConfig.fGrayscale) { |
| 84 | float r = SK_LUM_COEFF_R; |
| 85 | float g = SK_LUM_COEFF_G; |
| 86 | float b = SK_LUM_COEFF_B; |
Mike Klein | 8b96b37 | 2017-05-12 12:24:04 -0400 | [diff] [blame] | 87 | float* matrix = alloc->makeArray<float>(12); |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 88 | matrix[0] = matrix[1] = matrix[2] = r; |
| 89 | matrix[3] = matrix[4] = matrix[5] = g; |
| 90 | matrix[6] = matrix[7] = matrix[8] = b; |
| 91 | p->append(SkRasterPipeline::matrix_3x4, matrix); |
| 92 | } |
| 93 | |
| 94 | if (fConfig.fInvertStyle == InvertStyle::kInvertBrightness) { |
Mike Klein | 8b96b37 | 2017-05-12 12:24:04 -0400 | [diff] [blame] | 95 | float* matrix = alloc->makeArray<float>(12); |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 96 | matrix[0] = matrix[4] = matrix[8] = -1; |
| 97 | matrix[9] = matrix[10] = matrix[11] = 1; |
| 98 | p->append(SkRasterPipeline::matrix_3x4, matrix); |
| 99 | } else if (fConfig.fInvertStyle == InvertStyle::kInvertLightness) { |
| 100 | p->append(SkRasterPipeline::rgb_to_hsl); |
Mike Klein | 8b96b37 | 2017-05-12 12:24:04 -0400 | [diff] [blame] | 101 | float* matrix = alloc->makeArray<float>(12); |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 102 | matrix[0] = matrix[4] = matrix[11] = 1; |
| 103 | matrix[8] = -1; |
| 104 | p->append(SkRasterPipeline::matrix_3x4, matrix); |
| 105 | p->append(SkRasterPipeline::hsl_to_rgb); |
| 106 | } |
| 107 | |
| 108 | if (fConfig.fContrast != 0.0) { |
Mike Klein | 8b96b37 | 2017-05-12 12:24:04 -0400 | [diff] [blame] | 109 | float* matrix = alloc->makeArray<float>(12); |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 110 | float c = fConfig.fContrast; |
| 111 | float m = (1 + c) / (1 - c); |
| 112 | float b = (-0.5f * m + 0.5f); |
| 113 | matrix[0] = matrix[4] = matrix[8] = m; |
| 114 | matrix[9] = matrix[10] = matrix[11] = b; |
| 115 | p->append(SkRasterPipeline::matrix_3x4, matrix); |
| 116 | } |
| 117 | |
| 118 | p->append(SkRasterPipeline::clamp_0); |
| 119 | p->append(SkRasterPipeline::clamp_1); |
| 120 | |
Mike Klein | 8b96b37 | 2017-05-12 12:24:04 -0400 | [diff] [blame] | 121 | if (!dstCS) { |
| 122 | // See the previous if(!dstCS) { ... } |
| 123 | auto sqrt = alloc->make<SkJumper_ParametricTransferFunction>(); |
| 124 | sqrt->G = 0.5f; sqrt->A = 1.0f; |
| 125 | sqrt->B = sqrt->C = sqrt->D = sqrt->E = sqrt->F = 0; |
| 126 | |
| 127 | p->append(SkRasterPipeline::parametric_r, sqrt); |
| 128 | p->append(SkRasterPipeline::parametric_g, sqrt); |
| 129 | p->append(SkRasterPipeline::parametric_b, sqrt); |
| 130 | } |
| 131 | |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 132 | if (!shaderIsOpaque) { |
| 133 | p->append(SkRasterPipeline::premul); |
| 134 | } |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 135 | } |
| 136 | |
| 137 | void SkHighContrast_Filter::flatten(SkWriteBuffer& buffer) const { |
| 138 | buffer.writeBool(fConfig.fGrayscale); |
| 139 | buffer.writeInt(static_cast<int>(fConfig.fInvertStyle)); |
| 140 | buffer.writeScalar(fConfig.fContrast); |
| 141 | } |
| 142 | |
| 143 | sk_sp<SkFlattenable> SkHighContrast_Filter::CreateProc(SkReadBuffer& buffer) { |
| 144 | SkHighContrastConfig config; |
| 145 | config.fGrayscale = buffer.readBool(); |
Mike Reed | de5c502 | 2018-01-26 14:59:12 -0500 | [diff] [blame] | 146 | config.fInvertStyle = buffer.read32LE(InvertStyle::kLast); |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 147 | config.fContrast = buffer.readScalar(); |
Robert Phillips | a83d013 | 2018-01-24 14:46:38 -0500 | [diff] [blame] | 148 | |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 149 | return SkHighContrastFilter::Make(config); |
| 150 | } |
| 151 | |
| 152 | sk_sp<SkColorFilter> SkHighContrastFilter::Make( |
| 153 | const SkHighContrastConfig& config) { |
Mike Reed | de5c502 | 2018-01-26 14:59:12 -0500 | [diff] [blame] | 154 | if (!config.isValid()) { |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 155 | return nullptr; |
Mike Reed | de5c502 | 2018-01-26 14:59:12 -0500 | [diff] [blame] | 156 | } |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 157 | return sk_make_sp<SkHighContrast_Filter>(config); |
| 158 | } |
| 159 | |
| 160 | #ifndef SK_IGNORE_TO_STRING |
| 161 | void SkHighContrast_Filter::toString(SkString* str) const { |
| 162 | str->append("SkHighContrastColorFilter "); |
| 163 | } |
| 164 | #endif |
| 165 | |
| 166 | SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkHighContrastFilter) |
| 167 | SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkHighContrast_Filter) |
| 168 | SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END |
| 169 | |
| 170 | #if SK_SUPPORT_GPU |
| 171 | class HighContrastFilterEffect : public GrFragmentProcessor { |
| 172 | public: |
Brian Osman | 8c68c65 | 2017-11-06 15:25:10 -0500 | [diff] [blame] | 173 | static std::unique_ptr<GrFragmentProcessor> Make(const SkHighContrastConfig& config, |
| 174 | bool linearize) { |
| 175 | return std::unique_ptr<GrFragmentProcessor>(new HighContrastFilterEffect(config, |
| 176 | linearize)); |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 177 | } |
| 178 | |
| 179 | const char* name() const override { return "HighContrastFilter"; } |
| 180 | |
| 181 | const SkHighContrastConfig& config() const { return fConfig; } |
Brian Osman | 8c68c65 | 2017-11-06 15:25:10 -0500 | [diff] [blame] | 182 | bool linearize() const { return fLinearize; } |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 183 | |
Brian Osman | 8c68c65 | 2017-11-06 15:25:10 -0500 | [diff] [blame] | 184 | std::unique_ptr<GrFragmentProcessor> clone() const override { |
| 185 | return Make(fConfig, fLinearize); |
| 186 | } |
Brian Salomon | 164f6ec | 2017-07-25 15:32:17 -0400 | [diff] [blame] | 187 | |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 188 | private: |
Brian Osman | 8c68c65 | 2017-11-06 15:25:10 -0500 | [diff] [blame] | 189 | HighContrastFilterEffect(const SkHighContrastConfig& config, bool linearize) |
Ethan Nicholas | abff956 | 2017-10-09 10:54:08 -0400 | [diff] [blame] | 190 | : INHERITED(kHighContrastFilterEffect_ClassID, kNone_OptimizationFlags) |
Brian Osman | 8c68c65 | 2017-11-06 15:25:10 -0500 | [diff] [blame] | 191 | , fConfig(config) |
| 192 | , fLinearize(linearize) { |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 193 | } |
| 194 | |
| 195 | GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; |
| 196 | |
| 197 | virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps, |
| 198 | GrProcessorKeyBuilder* b) const override; |
| 199 | |
| 200 | bool onIsEqual(const GrFragmentProcessor& other) const override { |
| 201 | const HighContrastFilterEffect& that = other.cast<HighContrastFilterEffect>(); |
| 202 | return fConfig.fGrayscale == that.fConfig.fGrayscale && |
| 203 | fConfig.fInvertStyle == that.fConfig.fInvertStyle && |
Brian Osman | 8c68c65 | 2017-11-06 15:25:10 -0500 | [diff] [blame] | 204 | fConfig.fContrast == that.fConfig.fContrast && |
| 205 | fLinearize == that.fLinearize; |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 206 | } |
| 207 | |
| 208 | SkHighContrastConfig fConfig; |
Brian Osman | 8c68c65 | 2017-11-06 15:25:10 -0500 | [diff] [blame] | 209 | bool fLinearize; |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 210 | |
| 211 | typedef GrFragmentProcessor INHERITED; |
| 212 | }; |
| 213 | |
| 214 | class GLHighContrastFilterEffect : public GrGLSLFragmentProcessor { |
| 215 | public: |
| 216 | static void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*); |
| 217 | |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 218 | protected: |
Brian Salomon | ab015ef | 2017-04-04 10:15:51 -0400 | [diff] [blame] | 219 | void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override; |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 220 | void emitCode(EmitArgs& args) override; |
| 221 | |
| 222 | private: |
| 223 | UniformHandle fContrastUni; |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 224 | |
| 225 | typedef GrGLSLFragmentProcessor INHERITED; |
| 226 | }; |
| 227 | |
| 228 | GrGLSLFragmentProcessor* HighContrastFilterEffect::onCreateGLSLInstance() const { |
Brian Osman | 8c68c65 | 2017-11-06 15:25:10 -0500 | [diff] [blame] | 229 | return new GLHighContrastFilterEffect(); |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 230 | } |
| 231 | |
| 232 | void HighContrastFilterEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps, |
| 233 | GrProcessorKeyBuilder* b) const { |
| 234 | GLHighContrastFilterEffect::GenKey(*this, caps, b); |
| 235 | } |
| 236 | |
Brian Salomon | ab015ef | 2017-04-04 10:15:51 -0400 | [diff] [blame] | 237 | void GLHighContrastFilterEffect::onSetData(const GrGLSLProgramDataManager& pdm, |
| 238 | const GrFragmentProcessor& proc) { |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 239 | const HighContrastFilterEffect& hcfe = proc.cast<HighContrastFilterEffect>(); |
| 240 | pdm.set1f(fContrastUni, hcfe.config().fContrast); |
| 241 | } |
| 242 | |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 243 | void GLHighContrastFilterEffect::GenKey( |
| 244 | const GrProcessor& proc, const GrShaderCaps&, GrProcessorKeyBuilder* b) { |
| 245 | const HighContrastFilterEffect& hcfe = proc.cast<HighContrastFilterEffect>(); |
| 246 | b->add32(static_cast<uint32_t>(hcfe.config().fGrayscale)); |
| 247 | b->add32(static_cast<uint32_t>(hcfe.config().fInvertStyle)); |
Brian Osman | 8c68c65 | 2017-11-06 15:25:10 -0500 | [diff] [blame] | 248 | b->add32(hcfe.linearize() ? 1 : 0); |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 249 | } |
| 250 | |
| 251 | void GLHighContrastFilterEffect::emitCode(EmitArgs& args) { |
Brian Osman | 8c68c65 | 2017-11-06 15:25:10 -0500 | [diff] [blame] | 252 | const HighContrastFilterEffect& hcfe = args.fFp.cast<HighContrastFilterEffect>(); |
| 253 | const SkHighContrastConfig& config = hcfe.config(); |
| 254 | |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 255 | const char* contrast; |
Ethan Nicholas | f7b8820 | 2017-09-18 14:10:39 -0400 | [diff] [blame] | 256 | fContrastUni = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType, |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 257 | "contrast", &contrast); |
| 258 | |
| 259 | if (nullptr == args.fInputColor) { |
Ethan Nicholas | f7b8820 | 2017-09-18 14:10:39 -0400 | [diff] [blame] | 260 | args.fInputColor = "half4(1)"; |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 261 | } |
| 262 | |
| 263 | GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; |
| 264 | |
Ethan Nicholas | f7b8820 | 2017-09-18 14:10:39 -0400 | [diff] [blame] | 265 | fragBuilder->codeAppendf("half4 color = %s;", args.fInputColor); |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 266 | |
| 267 | // Unpremultiply. The max() is to guard against 0 / 0. |
Ethan Nicholas | f7b8820 | 2017-09-18 14:10:39 -0400 | [diff] [blame] | 268 | fragBuilder->codeAppendf("half nonZeroAlpha = max(color.a, 0.00001);"); |
| 269 | fragBuilder->codeAppendf("color = half4(color.rgb / nonZeroAlpha, nonZeroAlpha);"); |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 270 | |
Brian Osman | 8c68c65 | 2017-11-06 15:25:10 -0500 | [diff] [blame] | 271 | if (hcfe.linearize()) { |
| 272 | fragBuilder->codeAppend("color.rgb = color.rgb * color.rgb;"); |
| 273 | } |
| 274 | |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 275 | // Grayscale. |
Brian Osman | 8c68c65 | 2017-11-06 15:25:10 -0500 | [diff] [blame] | 276 | if (config.fGrayscale) { |
Ethan Nicholas | f7b8820 | 2017-09-18 14:10:39 -0400 | [diff] [blame] | 277 | fragBuilder->codeAppendf("half luma = dot(color, half4(%f, %f, %f, 0));", |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 278 | SK_LUM_COEFF_R, SK_LUM_COEFF_G, SK_LUM_COEFF_B); |
Ethan Nicholas | f7b8820 | 2017-09-18 14:10:39 -0400 | [diff] [blame] | 279 | fragBuilder->codeAppendf("color = half4(luma, luma, luma, 0);"); |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 280 | } |
| 281 | |
Brian Osman | 8c68c65 | 2017-11-06 15:25:10 -0500 | [diff] [blame] | 282 | if (config.fInvertStyle == InvertStyle::kInvertBrightness) { |
Ethan Nicholas | f7b8820 | 2017-09-18 14:10:39 -0400 | [diff] [blame] | 283 | fragBuilder->codeAppendf("color = half4(1, 1, 1, 1) - color;"); |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 284 | } |
| 285 | |
Brian Osman | 8c68c65 | 2017-11-06 15:25:10 -0500 | [diff] [blame] | 286 | if (config.fInvertStyle == InvertStyle::kInvertLightness) { |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 287 | // Convert from RGB to HSL. |
Ethan Nicholas | f7b8820 | 2017-09-18 14:10:39 -0400 | [diff] [blame] | 288 | fragBuilder->codeAppendf("half fmax = max(color.r, max(color.g, color.b));"); |
| 289 | fragBuilder->codeAppendf("half fmin = min(color.r, min(color.g, color.b));"); |
| 290 | fragBuilder->codeAppendf("half l = (fmax + fmin) / 2;"); |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 291 | |
Ethan Nicholas | f7b8820 | 2017-09-18 14:10:39 -0400 | [diff] [blame] | 292 | fragBuilder->codeAppendf("half h;"); |
| 293 | fragBuilder->codeAppendf("half s;"); |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 294 | |
| 295 | fragBuilder->codeAppendf("if (fmax == fmin) {"); |
| 296 | fragBuilder->codeAppendf(" h = 0;"); |
| 297 | fragBuilder->codeAppendf(" s = 0;"); |
| 298 | fragBuilder->codeAppendf("} else {"); |
Ethan Nicholas | f7b8820 | 2017-09-18 14:10:39 -0400 | [diff] [blame] | 299 | fragBuilder->codeAppendf(" half d = fmax - fmin;"); |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 300 | fragBuilder->codeAppendf(" s = l > 0.5 ?"); |
| 301 | fragBuilder->codeAppendf(" d / (2 - fmax - fmin) :"); |
| 302 | fragBuilder->codeAppendf(" d / (fmax + fmin);"); |
Brian Osman | 43a0aa5 | 2017-11-07 15:39:55 -0500 | [diff] [blame] | 303 | // We'd like to just write "if (color.r == fmax) { ... }". On many GPUs, running the |
| 304 | // angle_d3d9_es2 config, that failed. It seems that max(x, y) is not necessarily equal |
| 305 | // to either x or y. Tried several ways to fix it, but this was the only reasonable fix. |
| 306 | fragBuilder->codeAppendf(" if (color.r >= color.g && color.r >= color.b) {"); |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 307 | fragBuilder->codeAppendf(" h = (color.g - color.b) / d + "); |
| 308 | fragBuilder->codeAppendf(" (color.g < color.b ? 6 : 0);"); |
Brian Osman | 43a0aa5 | 2017-11-07 15:39:55 -0500 | [diff] [blame] | 309 | fragBuilder->codeAppendf(" } else if (color.g >= color.b) {"); |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 310 | fragBuilder->codeAppendf(" h = (color.b - color.r) / d + 2;"); |
| 311 | fragBuilder->codeAppendf(" } else {"); |
| 312 | fragBuilder->codeAppendf(" h = (color.r - color.g) / d + 4;"); |
| 313 | fragBuilder->codeAppendf(" }"); |
| 314 | fragBuilder->codeAppendf("}"); |
| 315 | fragBuilder->codeAppendf("h /= 6;"); |
| 316 | fragBuilder->codeAppendf("l = 1.0 - l;"); |
| 317 | // Convert back from HSL to RGB. |
| 318 | SkString hue2rgbFuncName; |
| 319 | static const GrShaderVar gHue2rgbArgs[] = { |
Ethan Nicholas | f7b8820 | 2017-09-18 14:10:39 -0400 | [diff] [blame] | 320 | GrShaderVar("p", kHalf_GrSLType), |
| 321 | GrShaderVar("q", kHalf_GrSLType), |
| 322 | GrShaderVar("t", kHalf_GrSLType), |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 323 | }; |
Ethan Nicholas | f7b8820 | 2017-09-18 14:10:39 -0400 | [diff] [blame] | 324 | fragBuilder->emitFunction(kHalf_GrSLType, |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 325 | "hue2rgb", |
| 326 | SK_ARRAY_COUNT(gHue2rgbArgs), |
| 327 | gHue2rgbArgs, |
| 328 | "if (t < 0)" |
| 329 | " t += 1;" |
| 330 | "if (t > 1)" |
| 331 | " t -= 1;" |
| 332 | "if (t < 1/6.)" |
| 333 | " return p + (q - p) * 6 * t;" |
| 334 | "if (t < 1/2.)" |
| 335 | " return q;" |
| 336 | "if (t < 2/3.)" |
| 337 | " return p + (q - p) * (2/3. - t) * 6;" |
| 338 | "return p;", |
| 339 | &hue2rgbFuncName); |
| 340 | fragBuilder->codeAppendf("if (s == 0) {"); |
Ethan Nicholas | f7b8820 | 2017-09-18 14:10:39 -0400 | [diff] [blame] | 341 | fragBuilder->codeAppendf(" color = half4(l, l, l, 0);"); |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 342 | fragBuilder->codeAppendf("} else {"); |
Ethan Nicholas | f7b8820 | 2017-09-18 14:10:39 -0400 | [diff] [blame] | 343 | fragBuilder->codeAppendf(" half q = l < 0.5 ? l * (1 + s) : l + s - l * s;"); |
| 344 | fragBuilder->codeAppendf(" half p = 2 * l - q;"); |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 345 | fragBuilder->codeAppendf(" color.r = %s(p, q, h + 1/3.);", hue2rgbFuncName.c_str()); |
| 346 | fragBuilder->codeAppendf(" color.g = %s(p, q, h);", hue2rgbFuncName.c_str()); |
| 347 | fragBuilder->codeAppendf(" color.b = %s(p, q, h - 1/3.);", hue2rgbFuncName.c_str()); |
| 348 | fragBuilder->codeAppendf("}"); |
| 349 | } |
| 350 | |
| 351 | // Contrast. |
| 352 | fragBuilder->codeAppendf("if (%s != 0) {", contrast); |
Ethan Nicholas | f7b8820 | 2017-09-18 14:10:39 -0400 | [diff] [blame] | 353 | fragBuilder->codeAppendf(" half m = (1 + %s) / (1 - %s);", contrast, contrast); |
| 354 | fragBuilder->codeAppendf(" half off = (-0.5 * m + 0.5);"); |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 355 | fragBuilder->codeAppendf(" color = m * color + off;"); |
| 356 | fragBuilder->codeAppendf("}"); |
| 357 | |
| 358 | // Clamp. |
| 359 | fragBuilder->codeAppendf("color = clamp(color, 0, 1);"); |
| 360 | |
Brian Osman | 8c68c65 | 2017-11-06 15:25:10 -0500 | [diff] [blame] | 361 | if (hcfe.linearize()) { |
| 362 | fragBuilder->codeAppend("color.rgb = sqrt(color.rgb);"); |
| 363 | } |
| 364 | |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 365 | // Restore the original alpha and premultiply. |
| 366 | fragBuilder->codeAppendf("color.a = %s.a;", args.fInputColor); |
| 367 | fragBuilder->codeAppendf("color.rgb *= color.a;"); |
| 368 | |
| 369 | // Copy to the output color. |
| 370 | fragBuilder->codeAppendf("%s = color;", args.fOutputColor); |
| 371 | } |
| 372 | |
Brian Salomon | aff329b | 2017-08-11 09:40:37 -0400 | [diff] [blame] | 373 | std::unique_ptr<GrFragmentProcessor> SkHighContrast_Filter::asFragmentProcessor( |
Brian Osman | 8c68c65 | 2017-11-06 15:25:10 -0500 | [diff] [blame] | 374 | GrContext*, const GrColorSpaceInfo& csi) const { |
| 375 | bool linearize = !csi.isGammaCorrect(); |
| 376 | return HighContrastFilterEffect::Make(fConfig, linearize); |
Dominic Mazzoni | 394d414 | 2017-02-14 11:15:31 -0800 | [diff] [blame] | 377 | } |
| 378 | #endif |