372 lines
13 KiB
C++
372 lines
13 KiB
C++
|
|
/*
|
||
|
|
* 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/SkBitmap.h"
|
||
|
|
#include "include/core/SkCanvas.h"
|
||
|
|
#include "include/core/SkColorFilter.h"
|
||
|
|
#include "include/core/SkColorSpace.h"
|
||
|
|
#include "include/core/SkImage.h"
|
||
|
|
#include "include/core/SkPaint.h"
|
||
|
|
#include "include/core/SkRRect.h"
|
||
|
|
#include "include/effects/SkGradientShader.h"
|
||
|
|
#include "include/gpu/GrRecordingContext.h"
|
||
|
|
#include "src/core/SkColorFilterPriv.h"
|
||
|
|
#include "tools/Resources.h"
|
||
|
|
#include "tools/ToolUtils.h"
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
|
||
|
|
sk_sp<SkShader> create_gradient_shader(SkRect r,
|
||
|
|
const std::array<SkColor, 3>& colors,
|
||
|
|
const std::array<float, 3>& offsets) {
|
||
|
|
SkPoint pts[2] = { {r.fLeft, r.fTop}, {r.fRight, r.fTop} };
|
||
|
|
|
||
|
|
return SkGradientShader::MakeLinear(pts, colors.data(), offsets.data(), std::size(colors),
|
||
|
|
SkTileMode::kClamp);
|
||
|
|
}
|
||
|
|
|
||
|
|
sk_sp<SkShader> create_image_shader(SkCanvas* destCanvas, SkTileMode tmX, SkTileMode tmY) {
|
||
|
|
SkBitmap bitmap;
|
||
|
|
|
||
|
|
{
|
||
|
|
SkImageInfo ii = SkImageInfo::Make(64, 64, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
|
||
|
|
bitmap.allocPixels(ii);
|
||
|
|
bitmap.eraseColor(SK_ColorWHITE);
|
||
|
|
|
||
|
|
SkCanvas tmpCanvas(bitmap);
|
||
|
|
|
||
|
|
SkColor colors[3][3] = {
|
||
|
|
{ SK_ColorRED, SK_ColorDKGRAY, SK_ColorBLUE },
|
||
|
|
{ SK_ColorLTGRAY, SK_ColorCYAN, SK_ColorYELLOW },
|
||
|
|
{ SK_ColorGREEN, SK_ColorWHITE, SK_ColorMAGENTA }
|
||
|
|
};
|
||
|
|
|
||
|
|
for (int y = 0; y < 3; ++y) {
|
||
|
|
for (int x = 0; x < 3; ++x) {
|
||
|
|
SkPaint paint;
|
||
|
|
paint.setColor(colors[y][x]);
|
||
|
|
tmpCanvas.drawRect(SkRect::MakeXYWH(x*21, y*21, 22, 22), paint);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
bitmap.setAlphaType(kOpaque_SkAlphaType);
|
||
|
|
bitmap.setImmutable();
|
||
|
|
}
|
||
|
|
|
||
|
|
sk_sp<SkImage> img = SkImage::MakeFromBitmap(bitmap);
|
||
|
|
img = ToolUtils::MakeTextureImage(destCanvas, std::move(img));
|
||
|
|
if (img) {
|
||
|
|
return img->makeShader(tmX, tmY, SkSamplingOptions());
|
||
|
|
} else {
|
||
|
|
return nullptr;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
sk_sp<SkShader> create_blend_shader(SkCanvas* destCanvas, SkBlendMode bm) {
|
||
|
|
constexpr SkColor4f kTransYellow = {1.0f, 1.0f, 0.0f, 0.5f};
|
||
|
|
|
||
|
|
sk_sp<SkShader> dst = SkShaders::Color(kTransYellow, nullptr);
|
||
|
|
return SkShaders::Blend(bm,
|
||
|
|
std::move(dst),
|
||
|
|
create_image_shader(destCanvas,
|
||
|
|
SkTileMode::kRepeat, SkTileMode::kRepeat));
|
||
|
|
}
|
||
|
|
|
||
|
|
sk_sp<SkColorFilter> create_grayscale_colorfilter() {
|
||
|
|
float matrix[20] = {};
|
||
|
|
matrix[0] = matrix[5] = matrix[10] = 0.2126f;
|
||
|
|
matrix[1] = matrix[6] = matrix[11] = 0.7152f;
|
||
|
|
matrix[2] = matrix[7] = matrix[12] = 0.0722f;
|
||
|
|
matrix[18] = 1.0f;
|
||
|
|
return SkColorFilters::Matrix(matrix);
|
||
|
|
}
|
||
|
|
|
||
|
|
void draw_image_shader_tile(SkCanvas* canvas, SkRect clipRect) {
|
||
|
|
SkPaint p;
|
||
|
|
p.setShader(create_image_shader(canvas, SkTileMode::kClamp, SkTileMode::kRepeat));
|
||
|
|
|
||
|
|
SkPath path;
|
||
|
|
path.moveTo(1, 1);
|
||
|
|
path.lineTo(32, 127);
|
||
|
|
path.lineTo(96, 127);
|
||
|
|
path.lineTo(127, 1);
|
||
|
|
path.lineTo(63, 32);
|
||
|
|
path.close();
|
||
|
|
|
||
|
|
canvas->save();
|
||
|
|
canvas->clipRect(clipRect);
|
||
|
|
canvas->scale(0.5f, 0.5f);
|
||
|
|
canvas->drawPath(path, p);
|
||
|
|
|
||
|
|
canvas->save();
|
||
|
|
canvas->concat(SkMatrix::RotateDeg(90, {64, 64}));
|
||
|
|
canvas->translate(128, 0);
|
||
|
|
canvas->drawPath(path, p);
|
||
|
|
canvas->restore();
|
||
|
|
canvas->restore();
|
||
|
|
}
|
||
|
|
|
||
|
|
void draw_gradient_tile(SkCanvas* canvas, SkRect clipRect) {
|
||
|
|
SkRect r{1, 1, 127, 127};
|
||
|
|
SkPaint p;
|
||
|
|
p.setShader(create_gradient_shader(r,
|
||
|
|
{ SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE },
|
||
|
|
{ 0.0f, 0.75f, 1.0f }));
|
||
|
|
|
||
|
|
canvas->save();
|
||
|
|
canvas->clipRect(clipRect);
|
||
|
|
canvas->translate(128, 0);
|
||
|
|
canvas->scale(0.5f, 0.5f);
|
||
|
|
canvas->drawRect(r, p);
|
||
|
|
|
||
|
|
canvas->save();
|
||
|
|
canvas->concat(SkMatrix::RotateDeg(90, {64, 64}));
|
||
|
|
canvas->translate(128, 0);
|
||
|
|
canvas->drawRect(r, p);
|
||
|
|
canvas->restore();
|
||
|
|
canvas->restore();
|
||
|
|
}
|
||
|
|
|
||
|
|
void draw_colorfilter_swatches(SkCanvas* canvas, SkRect clipRect) {
|
||
|
|
static constexpr int kNumTilesPerSide = 3;
|
||
|
|
|
||
|
|
SkSize tileSize = { clipRect.width() / kNumTilesPerSide, clipRect.height() / kNumTilesPerSide };
|
||
|
|
|
||
|
|
// Quantize to four colors
|
||
|
|
uint8_t table1[256];
|
||
|
|
for (int i = 0; i < 256; ++i) {
|
||
|
|
table1[i] = (i/64) * 85;
|
||
|
|
}
|
||
|
|
|
||
|
|
// table2 is a band-pass filter for 85-170.
|
||
|
|
// table3 re-expands that range to 0..255
|
||
|
|
uint8_t table2[256], table3[256];
|
||
|
|
for (int i = 0; i < 256; ++i) {
|
||
|
|
if (i >= 85 && i <= 170) {
|
||
|
|
table2[i] = i;
|
||
|
|
table3[i] = ((i - 85) / 85.0f) * 255.0f;
|
||
|
|
} else {
|
||
|
|
table2[i] = 0;
|
||
|
|
table3[i] = 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr SkColor SK_ColorGREY = SkColorSetARGB(0xFF, 0x80, 0x80, 0x80);
|
||
|
|
|
||
|
|
sk_sp<SkColorFilter> colorFilters[kNumTilesPerSide*kNumTilesPerSide];
|
||
|
|
static const std::array<SkColor, 3> kGradientColors[kNumTilesPerSide*kNumTilesPerSide] = {
|
||
|
|
{ SK_ColorBLACK, SK_ColorGREY, SK_ColorWHITE },
|
||
|
|
{ SK_ColorBLACK, SK_ColorGREY, SK_ColorWHITE },
|
||
|
|
{ SK_ColorBLACK, SK_ColorGREY, SK_ColorWHITE },
|
||
|
|
{ SK_ColorBLACK, SK_ColorGREY, SK_ColorWHITE },
|
||
|
|
{ 0x00000000, 0x80000000, 0xFF000000 }, // the Gaussian CF uses alpha only
|
||
|
|
{ SK_ColorBLACK, SK_ColorGREY, SK_ColorWHITE },
|
||
|
|
{ SK_ColorBLACK, SK_ColorGREY, SK_ColorWHITE },
|
||
|
|
{ SK_ColorBLACK, SK_ColorGREY, SK_ColorWHITE },
|
||
|
|
{ SK_ColorBLACK, SK_ColorGREY, SK_ColorWHITE },
|
||
|
|
};
|
||
|
|
|
||
|
|
colorFilters[0] = SkColorFilters::Lighting(SK_ColorLTGRAY, 0xFF440000);
|
||
|
|
colorFilters[1] = SkColorFilters::Table(table1);
|
||
|
|
colorFilters[2] = SkColorFilters::Compose(SkColorFilters::TableARGB(nullptr, table3,
|
||
|
|
table3, table3),
|
||
|
|
SkColorFilters::TableARGB(nullptr, table2,
|
||
|
|
table2, table2));
|
||
|
|
colorFilters[3] = SkColorFilters::Blend(SK_ColorGREEN, SkBlendMode::kMultiply);
|
||
|
|
colorFilters[4] = SkColorFilterPriv::MakeGaussian();
|
||
|
|
|
||
|
|
colorFilters[5] = SkColorFilters::LinearToSRGBGamma();
|
||
|
|
colorFilters[6] = SkColorFilters::SRGBToLinearGamma();
|
||
|
|
|
||
|
|
SkPaint p;
|
||
|
|
|
||
|
|
canvas->save();
|
||
|
|
canvas->clipRect(clipRect);
|
||
|
|
canvas->translate(clipRect.fLeft, clipRect.fTop);
|
||
|
|
|
||
|
|
for (int y = 0; y < kNumTilesPerSide; ++y) {
|
||
|
|
for (int x = 0; x < kNumTilesPerSide; ++x) {
|
||
|
|
SkRect r = SkRect::MakeXYWH(x * tileSize.width(), y * tileSize.height(),
|
||
|
|
tileSize.width(), tileSize.height()).makeInset(1.0f,
|
||
|
|
1.0f);
|
||
|
|
int colorFilterIndex = x*kNumTilesPerSide+y;
|
||
|
|
p.setShader(create_gradient_shader(r,
|
||
|
|
kGradientColors[colorFilterIndex],
|
||
|
|
{ 0.0f, 0.5f, 1.0f }));
|
||
|
|
p.setColorFilter(colorFilters[colorFilterIndex]);
|
||
|
|
canvas->drawRect(r, p);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
canvas->restore();
|
||
|
|
}
|
||
|
|
|
||
|
|
void draw_blend_mode_swatches(SkCanvas* canvas, SkRect clipRect) {
|
||
|
|
static const int kTileHeight = 16;
|
||
|
|
static const int kTileWidth = 16;
|
||
|
|
static const SkColor4f kOpaqueWhite { 1.0f, 1.0f, 1.0f, 1.0f };
|
||
|
|
static const SkColor4f kTransBluish { 0.0f, 0.5f, 1.0f, 0.5f };
|
||
|
|
static const SkColor4f kTransWhite { 1.0f, 1.0f, 1.0f, 0.75f };
|
||
|
|
|
||
|
|
SkPaint dstPaint;
|
||
|
|
dstPaint.setColor(kOpaqueWhite);
|
||
|
|
dstPaint.setBlendMode(SkBlendMode::kSrc);
|
||
|
|
dstPaint.setAntiAlias(false);
|
||
|
|
|
||
|
|
SkPaint srcPaint;
|
||
|
|
srcPaint.setColor(kTransBluish);
|
||
|
|
srcPaint.setAntiAlias(false);
|
||
|
|
|
||
|
|
SkRect r = SkRect::MakeXYWH(clipRect.fLeft, clipRect.fTop, kTileWidth, kTileHeight);
|
||
|
|
|
||
|
|
// For the first pass we draw: transparent bluish on top of opaque white
|
||
|
|
// For the second pass we draw: transparent white on top of transparent bluish
|
||
|
|
for (int passes = 0; passes < 2; ++passes) {
|
||
|
|
for (int i = 0; i <= (int)SkBlendMode::kLastCoeffMode; ++i) {
|
||
|
|
if (r.fLeft+kTileWidth > clipRect.fRight) {
|
||
|
|
r.offsetTo(clipRect.fLeft, r.fTop+kTileHeight);
|
||
|
|
}
|
||
|
|
|
||
|
|
canvas->drawRect(r.makeInset(1.0f, 1.0f), dstPaint);
|
||
|
|
srcPaint.setBlendMode(static_cast<SkBlendMode>(i));
|
||
|
|
canvas->drawRect(r.makeInset(2.0f, 2.0f), srcPaint);
|
||
|
|
|
||
|
|
r.offset(kTileWidth, 0.0f);
|
||
|
|
}
|
||
|
|
|
||
|
|
r.offsetTo(clipRect.fLeft, r.fTop+kTileHeight);
|
||
|
|
srcPaint.setColor(kTransWhite);
|
||
|
|
dstPaint.setColor(kTransBluish);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
} // anonymous namespace
|
||
|
|
|
||
|
|
namespace skiagm {
|
||
|
|
|
||
|
|
// This is just for bootstrapping Graphite.
|
||
|
|
class GraphiteStartGM : public GM {
|
||
|
|
public:
|
||
|
|
GraphiteStartGM() {
|
||
|
|
this->setBGColor(SK_ColorBLACK);
|
||
|
|
GetResourceAsBitmap("images/color_wheel.gif", &fBitmap);
|
||
|
|
}
|
||
|
|
|
||
|
|
protected:
|
||
|
|
static constexpr int kTileWidth = 128;
|
||
|
|
static constexpr int kTileHeight = 128;
|
||
|
|
static constexpr int kWidth = 3 * kTileWidth;
|
||
|
|
static constexpr int kHeight = 3 * kTileHeight;
|
||
|
|
static constexpr int kClipInset = 4;
|
||
|
|
|
||
|
|
SkString onShortName() override {
|
||
|
|
return SkString("graphitestart");
|
||
|
|
}
|
||
|
|
|
||
|
|
SkISize onISize() override {
|
||
|
|
return SkISize::Make(kWidth, kHeight);
|
||
|
|
}
|
||
|
|
|
||
|
|
void onDraw(SkCanvas* canvas) override {
|
||
|
|
|
||
|
|
const SkRect clipRect = SkRect::MakeWH(kWidth, kHeight).makeInset(kClipInset, kClipInset);
|
||
|
|
|
||
|
|
canvas->save();
|
||
|
|
canvas->clipRRect(SkRRect::MakeRectXY(clipRect, 32.f, 32.f), true);
|
||
|
|
|
||
|
|
// Upper-left corner
|
||
|
|
draw_image_shader_tile(canvas, SkRect::MakeXYWH(0, 0, kTileWidth, kTileHeight));
|
||
|
|
|
||
|
|
// Upper-middle tile
|
||
|
|
draw_gradient_tile(canvas, SkRect::MakeXYWH(kTileWidth, 0, kTileWidth, kTileHeight));
|
||
|
|
|
||
|
|
// Upper-right corner
|
||
|
|
draw_colorfilter_swatches(canvas, SkRect::MakeXYWH(2*kTileWidth, 0,
|
||
|
|
kTileWidth, kTileWidth));
|
||
|
|
|
||
|
|
// Middle-left tile
|
||
|
|
{
|
||
|
|
SkPaint p;
|
||
|
|
p.setColor(SK_ColorRED);
|
||
|
|
|
||
|
|
SkRect r = SkRect::MakeXYWH(0, kTileHeight, kTileWidth, kTileHeight);
|
||
|
|
canvas->drawRect(r.makeInset(1.0f, 1.0f), p);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Middle-middle tile
|
||
|
|
{
|
||
|
|
SkPaint p;
|
||
|
|
p.setShader(create_blend_shader(canvas, SkBlendMode::kModulate));
|
||
|
|
|
||
|
|
SkRect r = SkRect::MakeXYWH(kTileWidth, kTileHeight, kTileWidth, kTileHeight);
|
||
|
|
canvas->drawRect(r.makeInset(1.0f, 1.0f), p);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Middle-right tile
|
||
|
|
{
|
||
|
|
sk_sp<SkImage> image(GetResourceAsImage("images/mandrill_128.png"));
|
||
|
|
sk_sp<SkShader> shader;
|
||
|
|
|
||
|
|
if (image) {
|
||
|
|
shader = image->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat, {});
|
||
|
|
shader = shader->makeWithColorFilter(create_grayscale_colorfilter());
|
||
|
|
}
|
||
|
|
|
||
|
|
SkPaint p;
|
||
|
|
p.setShader(std::move(shader));
|
||
|
|
|
||
|
|
SkRect r = SkRect::MakeXYWH(2*kTileWidth, kTileHeight, kTileWidth, kTileHeight);
|
||
|
|
canvas->drawRect(r.makeInset(1.0f, 1.0f), p);
|
||
|
|
}
|
||
|
|
|
||
|
|
canvas->restore();
|
||
|
|
|
||
|
|
// Bottom-left corner
|
||
|
|
#if defined(SK_GRAPHITE)
|
||
|
|
// TODO: failing serialize test on Linux, not sure what's going on
|
||
|
|
canvas->writePixels(fBitmap, 0, 2*kTileHeight);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
// Bottom-middle tile
|
||
|
|
draw_blend_mode_swatches(canvas, SkRect::MakeXYWH(kTileWidth, 2*kTileHeight,
|
||
|
|
kTileWidth, kTileHeight));
|
||
|
|
|
||
|
|
// Bottom-right corner
|
||
|
|
{
|
||
|
|
const SkRect kTile = SkRect::MakeXYWH(2*kTileWidth, 2*kTileHeight,
|
||
|
|
kTileWidth, kTileHeight);
|
||
|
|
|
||
|
|
SkPaint circlePaint;
|
||
|
|
circlePaint.setColor(SK_ColorBLUE);
|
||
|
|
circlePaint.setBlendMode(SkBlendMode::kSrc);
|
||
|
|
|
||
|
|
canvas->clipRect(kTile);
|
||
|
|
canvas->drawRect(kTile.makeInset(10, 20), circlePaint);
|
||
|
|
|
||
|
|
SkPaint restorePaint;
|
||
|
|
restorePaint.setBlendMode(SkBlendMode::kPlus);
|
||
|
|
|
||
|
|
canvas->saveLayer(nullptr, &restorePaint);
|
||
|
|
circlePaint.setColor(SK_ColorRED);
|
||
|
|
circlePaint.setBlendMode(SkBlendMode::kSrc);
|
||
|
|
|
||
|
|
canvas->drawRect(kTile.makeInset(15, 25), circlePaint);
|
||
|
|
canvas->restore();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
SkBitmap fBitmap;
|
||
|
|
};
|
||
|
|
|
||
|
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
|
|
||
|
|
DEF_GM(return new GraphiteStartGM;)
|
||
|
|
|
||
|
|
} // namespace skiagm
|