blob: 5d73024e03481e596ae7300d3aa8531870ad86bb [file] [log] [blame]
/*
* Copyright 2019 Google Inc.
*
* 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/SkLumaColorFilter.h"
#include "include/effects/SkRuntimeEffect.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))
//
// TODO: move to public headers/API?
//
static sk_sp<SkColorFilter> MakeTintColorFilter(SkColor lo, SkColor hi) {
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,
};
return SkColorFilters::Matrix(tint_matrix)->makeComposed(SkLumaColorFilter::Make());
}
namespace {
class MixerCFGM final : public skiagm::GM {
public:
MixerCFGM(const SkSize& tileSize, size_t tileCount, bool runtime)
: fTileSize(tileSize)
, fTileCount(tileCount)
, fRuntime(runtime) {}
protected:
SkString onShortName() override {
return fRuntime ? SkString("mixerCF_runtime")
: SkString("mixerCF");
}
SkISize onISize() override {
return SkISize::Make(fTileSize.width() * 1.2f * fTileCount,
fTileSize.height() * 1.2f * 3); // 3 rows
}
void onDraw(SkCanvas* canvas) override {
SkPaint paint;
const SkColor gradient_colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorRED };
paint.setShader(SkGradientShader::MakeSweep(fTileSize.width() / 2,
fTileSize.height() / 2,
gradient_colors, nullptr,
SK_ARRAY_COUNT(gradient_colors)));
auto cf0 = MakeTintColorFilter(0xff300000, 0xffa00000); // red tint
auto cf1 = MakeTintColorFilter(0xff003000, 0xff00a000); // green tint
this->mixRow(canvas, paint, nullptr, cf1);
this->mixRow(canvas, paint, cf0, nullptr);
this->mixRow(canvas, paint, cf0, cf1);
}
private:
const SkSize fTileSize;
const size_t fTileCount;
const bool fRuntime;
void mixRow(SkCanvas* canvas, SkPaint& paint,
sk_sp<SkColorFilter> cf0, sk_sp<SkColorFilter> cf1) {
sk_sp<SkRuntimeEffect> effect;
if (fRuntime) {
const char* sksl = R"(
uniform shader cf0;
uniform shader cf1;
uniform half t;
half4 main() {
return mix(sample(cf0), sample(cf1), t);
}
)";
effect = std::get<0>(SkRuntimeEffect::Make(SkString(sksl)));
SkASSERT(effect);
}
// We cycle through paint colors on each row, to test how the paint color flows through
// the color-filter network
const SkColor4f paintColors[] = {
{ 1.0f, 1.0f, 1.0f, 1.0f }, // Opaque white
{ 1.0f, 1.0f, 1.0f, 0.5f }, // Translucent white
{ 0.5f, 0.5f, 1.0f, 1.0f }, // Opaque pale blue
{ 0.5f, 0.5f, 1.0f, 0.5f }, // Translucent pale blue
};
canvas->translate(0, fTileSize.height() * 0.1f);
{
SkAutoCanvasRestore arc(canvas, true);
for (size_t i = 0; i < fTileCount; ++i) {
paint.setColor4f(paintColors[i % SK_ARRAY_COUNT(paintColors)]);
float t = static_cast<float>(i) / (fTileCount - 1);
if (fRuntime) {
sk_sp<SkColorFilter> children[] = { cf0, cf1 };
sk_sp<SkData> inputs = SkData::MakeWithCopy(&t, sizeof(t));
paint.setColorFilter(effect->makeColorFilter(inputs, children, 2));
} else {
paint.setColorFilter(SkColorFilters::Lerp(t, cf0, cf1));
}
canvas->translate(fTileSize.width() * 0.1f, 0);
canvas->drawRect(SkRect::MakeWH(fTileSize.width(), fTileSize.height()), paint);
canvas->translate(fTileSize.width() * 1.1f, 0);
}
}
canvas->translate(0, fTileSize.height() * 1.1f);
}
using INHERITED = skiagm::GM;
};
} // namespace
DEF_GM( return new MixerCFGM(SkSize::Make(200, 250), 5, false); )
DEF_GM( return new MixerCFGM(SkSize::Make(200, 250), 5, true); )