| /* |
| * Copyright 2021 Google LLC |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "gm/gm.h" |
| #include "include/core/SkCanvas.h" |
| #include "include/core/SkColor.h" |
| #include "include/core/SkColorFilter.h" |
| #include "include/core/SkImage.h" |
| #include "include/core/SkMatrix.h" |
| #include "include/core/SkPaint.h" |
| #include "include/core/SkPoint.h" |
| #include "include/core/SkRect.h" |
| #include "include/core/SkRefCnt.h" |
| #include "include/core/SkScalar.h" |
| #include "include/core/SkShader.h" |
| #include "include/core/SkSize.h" |
| #include "include/core/SkString.h" |
| #include "include/core/SkTileMode.h" |
| #include "include/core/SkTypes.h" |
| #include "include/effects/SkGradientShader.h" |
| #include "include/effects/SkImageFilters.h" |
| #include "include/effects/SkLumaColorFilter.h" |
| #include "include/effects/SkPerlinNoiseShader.h" |
| #include "include/effects/SkRuntimeEffect.h" |
| #include "src/core/SkRuntimeEffectPriv.h" |
| #include "tools/Resources.h" |
| |
| #include <math.h> |
| |
| // A tint filter maps colors to a given range (gradient), based on the input luminance: |
| // |
| // c' = lerp(lo, hi, luma(c)) |
| static sk_sp<SkColorFilter> MakeTintColorFilter(SkColor lo, SkColor hi, bool useSkSL) { |
| const auto r_lo = SkColorGetR(lo), |
| g_lo = SkColorGetG(lo), |
| b_lo = SkColorGetB(lo), |
| a_lo = SkColorGetA(lo), |
| r_hi = SkColorGetR(hi), |
| g_hi = SkColorGetG(hi), |
| b_hi = SkColorGetB(hi), |
| a_hi = SkColorGetA(hi); |
| |
| // We map component-wise: |
| // |
| // r' = lo.r + (hi.r - lo.r) * luma |
| // g' = lo.g + (hi.g - lo.g) * luma |
| // b' = lo.b + (hi.b - lo.b) * luma |
| // a' = lo.a + (hi.a - lo.a) * luma |
| // |
| // The input luminance is stored in the alpha channel |
| // (and RGB are cleared -- see SkLumaColorFilter). Thus: |
| const float tint_matrix[] = { |
| 0, 0, 0, (r_hi - r_lo) / 255.0f, SkIntToScalar(r_lo) / 255.0f, |
| 0, 0, 0, (g_hi - g_lo) / 255.0f, SkIntToScalar(g_lo) / 255.0f, |
| 0, 0, 0, (b_hi - b_lo) / 255.0f, SkIntToScalar(b_lo) / 255.0f, |
| 0, 0, 0, (a_hi - a_lo) / 255.0f, SkIntToScalar(a_lo) / 255.0f, |
| }; |
| |
| sk_sp<SkColorFilter> inner = SkLumaColorFilter::Make(), |
| outer = SkColorFilters::Matrix(tint_matrix); |
| |
| // Prove that we can implement compose-color-filter using runtime effects |
| if (useSkSL) { |
| auto [effect, error] = SkRuntimeEffect::MakeForColorFilter(SkString(R"( |
| uniform colorFilter inner; |
| uniform colorFilter outer; |
| half4 main(half4 c) { return outer.eval(inner.eval(c)); } |
| )")); |
| SkASSERT(effect); |
| sk_sp<SkColorFilter> children[] = { inner, outer }; |
| return effect->makeColorFilter(nullptr, children, std::size(children)); |
| } else { |
| return outer->makeComposed(inner); |
| } |
| } |
| |
| DEF_SIMPLE_GM(composeCF, canvas, 200, 200) { |
| // This GM draws a simple color-filter network, using the existing "makeComposed" API, and also |
| // using a runtime color filter that does the same thing. |
| SkPaint paint; |
| const SkColor gradient_colors[] = {SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorRED}; |
| paint.setShader(SkGradientShader::MakeSweep( |
| 50, 50, gradient_colors, nullptr, std::size(gradient_colors))); |
| |
| canvas->save(); |
| for (bool useSkSL : {false, true}) { |
| auto cf0 = MakeTintColorFilter(0xff300000, 0xffa00000, useSkSL); // red tint |
| auto cf1 = MakeTintColorFilter(0xff003000, 0xff00a000, useSkSL); // green tint |
| |
| paint.setColorFilter(cf0); |
| canvas->drawRect({0, 0, 100, 100}, paint); |
| canvas->translate(100, 0); |
| |
| paint.setColorFilter(cf1); |
| canvas->drawRect({0, 0, 100, 100}, paint); |
| |
| canvas->restore(); |
| canvas->translate(0, 100); |
| } |
| } |
| |
| DEF_SIMPLE_GM(composeCFIF, canvas, 604, 200) { |
| // This GM draws a ::Shader image filter composed with a ::ColorFilter image filter in two |
| // ways (direct and via ::Compose). This ensures the use (or non-use in this case) of the source |
| // image is the same across both means of composition. |
| auto cf = MakeTintColorFilter(0xff300000, 0xffa00000, /*useSkSL=*/false); |
| auto shader = SkShaders::MakeTurbulence(0.01f, 0.01f, 2, 0.f); |
| |
| auto shaderIF = SkImageFilters::Shader(shader, SkImageFilters::Dither::kNo); |
| auto directCompose = SkImageFilters::ColorFilter(cf, shaderIF); |
| auto indirectCompose = SkImageFilters::Compose( |
| /*outer=*/SkImageFilters::ColorFilter(cf, nullptr), |
| /*inner=*/shaderIF); |
| |
| { // Directly draw the shader composed with the color filter |
| canvas->save(); |
| canvas->clipRect({0, 0, 200, 200}); |
| SkPaint p; |
| p.setShader(shader); |
| p.setColorFilter(cf); |
| canvas->drawPaint(p); |
| canvas->restore(); |
| } |
| canvas->translate(202, 0); |
| { // Draw with the directly composed image filter |
| canvas->save(); |
| canvas->clipRect({0, 0, 200, 200}); |
| SkPaint p; |
| p.setImageFilter(directCompose); |
| canvas->drawPaint(p); |
| canvas->restore(); |
| } |
| canvas->translate(202, 0); |
| { |
| // Draw with the indirectly composed image filter |
| canvas->save(); |
| canvas->clipRect({0, 0, 200, 200}); |
| SkPaint p; |
| p.setImageFilter(indirectCompose); |
| canvas->drawPaint(p); |
| canvas->restore(); |
| } |
| } |