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