/*
* Copyright 2011 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SampleCode.h"
#include "SkBlurMask.h"
#include "SkCanvas.h"
#include "SkView.h"
#include "Sk1DPathEffect.h"
#include "Sk2DPathEffect.h"
#include "SkBlurMaskFilter.h"
#include "SkColorMatrixFilter.h"
#include "SkColorPriv.h"
#include "SkCornerPathEffect.h"
#include "SkDashPathEffect.h"
#include "SkDiscretePathEffect.h"
#include "SkEmbossMaskFilter.h"
#include "SkReadBuffer.h"
#include "SkWriteBuffer.h"
#include "SkGradientShader.h"
#include "SkLayerRasterizer.h"
#include "SkMath.h"
#include "SkPath.h"
#include "SkPictureRecorder.h"
#include "SkRegion.h"
#include "SkShader.h"
#include "SkCornerPathEffect.h"
#include "SkPathMeasure.h"
#include "SkPicture.h"
#include "SkRandom.h"
#include "SkTypeface.h"
#include "SkUtils.h"
#include <math.h>
#include "DecodeFile.h"
static inline SkPMColor rgb2gray(SkPMColor c) {
unsigned r = SkGetPackedR32(c);
unsigned g = SkGetPackedG32(c);
unsigned b = SkGetPackedB32(c);
unsigned x = (r * 5 + g * 7 + b * 4) >> 4;
return SkPackARGB32(0, x, x, x) | (c & (SK_A32_MASK << SK_A32_SHIFT));
}
class SkGrayScaleColorFilter : public SkColorFilter {
public:
virtual void filterSpan(const SkPMColor src[], int count,
SkPMColor result[]) const override {
for (int i = 0; i < count; i++)
result[i] = rgb2gray(src[i]);
}
};
class SkChannelMaskColorFilter : public SkColorFilter {
public:
SkChannelMaskColorFilter(U8CPU redMask, U8CPU greenMask, U8CPU blueMask) {
fMask = SkPackARGB32(0xFF, redMask, greenMask, blueMask);
}
virtual void filterSpan(const SkPMColor src[], int count,
SkPMColor result[]) const override {
SkPMColor mask = fMask;
for (int i = 0; i < count; i++) {
result[i] = src[i] & mask;
}
}
private:
SkPMColor fMask;
};
///////////////////////////////////////////////////////////
static void r0(SkLayerRasterizer::Builder* rastBuilder, SkPaint& p) {
p.setMaskFilter(SkBlurMaskFilter::Make(kNormal_SkBlurStyle,
SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(3)),
SkBlurMaskFilter::kNone_BlurFlag));
rastBuilder->addLayer(p, SkIntToScalar(3), SkIntToScalar(3));
p.setMaskFilter(nullptr);
p.setStyle(SkPaint::kStroke_Style);
p.setStrokeWidth(SK_Scalar1);
rastBuilder->addLayer(p);
p.setAlpha(0x11);
p.setStyle(SkPaint::kFill_Style);
p.setBlendMode(SkBlendMode::kSrc);
rastBuilder->addLayer(p);
}
static void r1(SkLayerRasterizer::Builder* rastBuilder, SkPaint& p) {
rastBuilder->addLayer(p);
p.setAlpha(0x40);
p.setBlendMode(SkBlendMode::kSrc);
p.setStyle(SkPaint::kStroke_Style);
p.setStrokeWidth(SK_Scalar1*2);
rastBuilder->addLayer(p);
}
static void r2(SkLayerRasterizer::Builder* rastBuilder, SkPaint& p) {
p.setStyle(SkPaint::kStrokeAndFill_Style);
p.setStrokeWidth(SK_Scalar1*4);
rastBuilder->addLayer(p);
p.setStyle(SkPaint::kStroke_Style);
p.setStrokeWidth(SK_Scalar1*3/2);
p.setBlendMode(SkBlendMode::kClear);
rastBuilder->addLayer(p);
}
static void r3(SkLayerRasterizer::Builder* rastBuilder, SkPaint& p) {
p.setStyle(SkPaint::kStroke_Style);
p.setStrokeWidth(SK_Scalar1*3);
rastBuilder->addLayer(p);
p.setAlpha(0x20);
p.setStyle(SkPaint::kFill_Style);
p.setBlendMode(SkBlendMode::kSrc);
rastBuilder->addLayer(p);
}
static void r4(SkLayerRasterizer::Builder* rastBuilder, SkPaint& p) {
p.setAlpha(0x60);
rastBuilder->addLayer(p, SkIntToScalar(3), SkIntToScalar(3));
p.setAlpha(0xFF);
p.setBlendMode(SkBlendMode::kClear);
rastBuilder->addLayer(p, SK_Scalar1*3/2, SK_Scalar1*3/2);
p.setBlendMode(SkBlendMode::kSrcOver);
rastBuilder->addLayer(p);
}
static void r5(SkLayerRasterizer::Builder* rastBuilder, SkPaint& p) {
rastBuilder->addLayer(p);
p.setPathEffect(SkDiscretePathEffect::Make(SK_Scalar1*4, SK_Scalar1*3));
p.setBlendMode(SkBlendMode::kSrcOut);
rastBuilder->addLayer(p);
}
static void r6(SkLayerRasterizer::Builder* rastBuilder, SkPaint& p) {
rastBuilder->addLayer(p);
p.setAntiAlias(false);
SkLayerRasterizer::Builder rastBuilder2;
r5(&rastBuilder2, p);
p.setRasterizer(rastBuilder2.detach());
p.setBlendMode(SkBlendMode::kClear);
rastBuilder->addLayer(p);
}
class Dot2DPathEffect : public Sk2DPathEffect {
public:
Dot2DPathEffect(SkScalar radius, const SkMatrix& matrix)
: Sk2DPathEffect(matrix), fRadius(radius) {}
SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Dot2DPathEffect)
protected:
void next(const SkPoint& loc, int u, int v, SkPath* dst) const override {
dst->addCircle(loc.fX, loc.fY, fRadius);
}
void flatten(SkWriteBuffer& buffer) const override {
this->INHERITED::flatten(buffer);
buffer.writeScalar(fRadius);
}
private:
SkScalar fRadius;
typedef Sk2DPathEffect INHERITED;
};
static void r7(SkLayerRasterizer::Builder* rastBuilder, SkPaint& p) {
SkMatrix lattice;
lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0);
lattice.postSkew(SK_Scalar1/3, 0, 0, 0);
p.setPathEffect(sk_make_sp<Dot2DPathEffect>(SK_Scalar1*4, lattice));
rastBuilder->addLayer(p);
}
static void r8(SkLayerRasterizer::Builder* rastBuilder, SkPaint& p) {
rastBuilder->addLayer(p);
SkMatrix lattice;
lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0);
lattice.postSkew(SK_Scalar1/3, 0, 0, 0);
p.setPathEffect(sk_make_sp<Dot2DPathEffect>(SK_Scalar1*2, lattice));
p.setBlendMode(SkBlendMode::kClear);
rastBuilder->addLayer(p);
p.setPathEffect(nullptr);
p.setBlendMode(SkBlendMode::kSrcOver);
p.setStyle(SkPaint::kStroke_Style);
p.setStrokeWidth(SK_Scalar1);
rastBuilder->addLayer(p);
}
static void r9(SkLayerRasterizer::Builder* rastBuilder, SkPaint& p) {
rastBuilder->addLayer(p);
SkMatrix lattice;
lattice.setScale(SK_Scalar1, SK_Scalar1*6, 0, 0);
lattice.postRotate(SkIntToScalar(30), 0, 0);
p.setPathEffect(SkLine2DPathEffect::Make(SK_Scalar1*2, lattice));
p.setBlendMode(SkBlendMode::kClear);
rastBuilder->addLayer(p);
p.setPathEffect(nullptr);
p.setBlendMode(SkBlendMode::kSrcOver);
p.setStyle(SkPaint::kStroke_Style);
p.setStrokeWidth(SK_Scalar1);
rastBuilder->addLayer(p);
}
typedef void (*raster_proc)(SkLayerRasterizer::Builder*, SkPaint&);
static const raster_proc gRastProcs[] = {
r0, r1, r2, r3, r4, r5, r6, r7, r8, r9
};
static const struct {
SkColor fMul, fAdd;
} gLightingColors[] = {
{ 0x808080, 0x800000 }, // general case
{ 0x707070, 0x707070 }, // no-pin case
{ 0xFFFFFF, 0x800000 }, // just-add case
{ 0x808080, 0x000000 }, // just-mul case
{ 0xFFFFFF, 0x000000 } // identity case
};
static void apply_shader(SkPaint* paint, int index) {
raster_proc proc = gRastProcs[index];
if (proc) {
SkPaint p;
SkLayerRasterizer::Builder rastBuilder;
p.setAntiAlias(true);
proc(&rastBuilder, p);
paint->setRasterizer(rastBuilder.detach());
}
#ifdef SK_SUPPORT_LEGACY_EMBOSSMASKFILTER
SkScalar dir[] = { SK_Scalar1, SK_Scalar1, SK_Scalar1 };
paint->setMaskFilter(SkBlurMaskFilter::MakeEmboss(
SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(3)), dir,
SK_Scalar1/4, SkIntToScalar(4)));
paint->setColor(SK_ColorBLUE);
#endif
}
class DemoView : public SampleView {
public:
DemoView() {}
protected:
// overrides from SkEventSink
virtual bool onQuery(SkEvent* evt) {
if (SampleCode::TitleQ(*evt)) {
SampleCode::TitleR(evt, "Demo");
return true;
}
return this->INHERITED::onQuery(evt);
}
virtual bool onClick(Click* click) {
return this->INHERITED::onClick(click);
}
void makePath(SkPath& path) {
path.addCircle(SkIntToScalar(20), SkIntToScalar(20), SkIntToScalar(20),
SkPath::kCCW_Direction);
for (int index = 0; index < 10; index++) {
SkScalar x = (float) cos(index / 10.0f * 2 * 3.1415925358f);
SkScalar y = (float) sin(index / 10.0f * 2 * 3.1415925358f);
x *= index & 1 ? 7 : 14;
y *= index & 1 ? 7 : 14;
x += SkIntToScalar(20);
y += SkIntToScalar(20);
if (index == 0)
path.moveTo(x, y);
else
path.lineTo(x, y);
}
path.close();
}
virtual void onDrawContent(SkCanvas* canvas) {
canvas->save();
this->drawPicture(canvas, 0);
canvas->restore();
{
SkPictureRecorder recorder;
{
SkCanvas* record = recorder.beginRecording(320, 480, nullptr, 0);
this->drawPicture(record, 120);
}
sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
canvas->translate(0, SkIntToScalar(120));
SkRect clip;
clip.set(0, 0, SkIntToScalar(160), SkIntToScalar(160));
do {
canvas->save();
canvas->clipRect(clip);
picture->playback(canvas);
canvas->restore();
if (clip.fRight < SkIntToScalar(320))
clip.offset(SkIntToScalar(160), 0);
else if (clip.fBottom < SkIntToScalar(480))
clip.offset(-SkIntToScalar(320), SkIntToScalar(160));
else
break;
} while (true);
}
}
void drawPicture(SkCanvas* canvas, int spriteOffset) {
SkMatrix matrix; matrix.reset();
SkPaint paint;
SkPath path;
SkPoint start = {0, 0};
SkPoint stop = { SkIntToScalar(40), SkIntToScalar(40) };
SkRect rect = {0, 0, SkIntToScalar(40), SkIntToScalar(40) };
SkRect rect2 = {0, 0, SkIntToScalar(65), SkIntToScalar(20) };
SkScalar left = 0, top = 0, x = 0, y = 0;
int index;
char ascii[] = "ascii...";
int asciiLength = sizeof(ascii) - 1;
char utf8[] = "utf8" "\xe2\x80\xa6";
short utf16[] = {'u', 't', 'f', '1', '6', 0x2026 };
short utf16simple[] = {'u', 't', 'f', '1', '6', '!' };
makePath(path);
SkTDArray<SkPoint>(pos);
pos.setCount(asciiLength);
for (index = 0; index < asciiLength; index++)
pos[index].set(SkIntToScalar((unsigned int)index * 10),
SkIntToScalar((unsigned int)index * 2));
SkTDArray<SkPoint>(pos2);
pos2.setCount(asciiLength);
for (index = 0; index < asciiLength; index++)
pos2[index].set(SkIntToScalar((unsigned int)index * 10),
SkIntToScalar(20));
// shaders
SkPoint linearPoints[] = { { 0, 0, }, { SkIntToScalar(40), SkIntToScalar(40) } };
SkColor linearColors[] = { SK_ColorRED, SK_ColorBLUE };
SkScalar* linearPos = nullptr;
int linearCount = 2;
SkShader::TileMode linearMode = SkShader::kMirror_TileMode;
auto linear = SkGradientShader::MakeLinear(linearPoints,
linearColors, linearPos, linearCount, linearMode);
SkPoint radialCenter = { SkIntToScalar(25), SkIntToScalar(25) };
SkScalar radialRadius = SkIntToScalar(25);
SkColor radialColors[] = { SK_ColorGREEN, SK_ColorGRAY, SK_ColorRED };
SkScalar radialPos[] = { 0, SkIntToScalar(3) / 5, SkIntToScalar(1)};
int radialCount = 3;
SkShader::TileMode radialMode = SkShader::kRepeat_TileMode;
auto radial = SkGradientShader::MakeRadial(radialCenter,
radialRadius, radialColors, radialPos, radialCount,
radialMode);
SkEmbossMaskFilter::Light light;
light.fDirection[0] = SK_Scalar1/2;
light.fDirection[1] = SK_Scalar1/2;
light.fDirection[2] = SK_Scalar1/3;
light.fAmbient = 0x48;
light.fSpecular = 0x80;
auto lightingFilter = SkColorMatrixFilter::MakeLightingFilter(
0xff89bc45, 0xff112233);
canvas->save();
canvas->translate(SkIntToScalar(0), SkIntToScalar(5));
paint.setAntiAlias(true);
paint.setFilterQuality(kLow_SkFilterQuality);
// !!! draw through a clip
paint.setColor(SK_ColorLTGRAY);
paint.setStyle(SkPaint::kFill_Style);
SkRect clip = {0, 0, SkIntToScalar(320), SkIntToScalar(120)};
canvas->clipRect(clip);
paint.setShader(SkShader::MakeBitmapShader(fTx,
SkShader::kMirror_TileMode, SkShader::kRepeat_TileMode));
canvas->drawPaint(paint);
canvas->save();
// line (exercises xfermode, colorShader, colorFilter, filterShader)
paint.setColor(SK_ColorGREEN);
paint.setStrokeWidth(SkIntToScalar(10));
paint.setStyle(SkPaint::kStroke_Style);
paint.setBlendMode(SkBlendMode::kXor);
paint.setColorFilter(lightingFilter);
canvas->drawLine(start.fX, start.fY, stop.fX, stop.fY, paint); // should not be green
paint.setBlendMode(SkBlendMode::kSrcOver);
paint.setColorFilter(nullptr);
// rectangle
paint.setStyle(SkPaint::kFill_Style);
canvas->translate(SkIntToScalar(50), 0);
paint.setColor(SK_ColorYELLOW);
paint.setShader(linear);
paint.setPathEffect(pathEffectTest());
canvas->drawRect(rect, paint);
paint.setPathEffect(nullptr);
// circle w/ emboss & transparent (exercises 3dshader)
canvas->translate(SkIntToScalar(50), 0);
paint.setMaskFilter(SkEmbossMaskFilter::Make(
SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(12)/5), light));
canvas->drawOval(rect, paint);
canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
// paint.setShader(transparentShader)->unref();
canvas->drawOval(rect, paint);
canvas->translate(0, SkIntToScalar(-10));
// path
canvas->translate(SkIntToScalar(50), 0);
paint.setColor(SK_ColorRED);
paint.setStyle(SkPaint::kStroke_Style);
paint.setStrokeWidth(SkIntToScalar(5));
paint.setShader(radial);
paint.setMaskFilter(nullptr);
canvas->drawPath(path, paint);
paint.setShader(nullptr);
// bitmap
canvas->translate(SkIntToScalar(50), 0);
paint.setStyle(SkPaint::kFill_Style);
canvas->drawBitmap(fBug, left, top, &paint);
canvas->translate(-SkIntToScalar(30), SkIntToScalar(30));
paint.setShader(shaderTest()); // test compose shader
canvas->drawRect(rect2, paint);
paint.setShader(nullptr);
canvas->restore();
// text
canvas->translate(0, SkIntToScalar(60));
canvas->save();
paint.setColor(SK_ColorGRAY);
canvas->drawPosText(ascii, asciiLength, pos.begin(), paint);
canvas->drawPosText(ascii, asciiLength, pos2.begin(), paint);
canvas->translate(SkIntToScalar(50), 0);
paint.setColor(SK_ColorCYAN);
canvas->drawText(utf8, sizeof(utf8) - 1, x, y, paint);
canvas->translate(SkIntToScalar(30), 0);
paint.setColor(SK_ColorMAGENTA);
paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
matrix.setTranslate(SkIntToScalar(10), SkIntToScalar(10));
canvas->drawTextOnPath((void*) utf16, sizeof(utf16), path, &matrix, paint);
canvas->translate(0, SkIntToScalar(20));
canvas->drawTextOnPath((void*) utf16simple, sizeof(utf16simple), path, &matrix, paint);
canvas->restore();
canvas->translate(0, SkIntToScalar(60));
paint.setTextEncoding(SkPaint::kUTF8_TextEncoding);
canvas->restore();
}
virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) {
fClickPt.set(x, y);
this->inval(nullptr);
return this->INHERITED::onFindClickHandler(x, y, modi);
}
sk_sp<SkPathEffect> pathEffectTest() {
static const int gXY[] = { 1, 0, 0, -1, 2, -1, 3, 0, 2, 1, 0, 1 };
SkScalar gPhase = 0;
SkPath path;
path.moveTo(SkIntToScalar(gXY[0]), SkIntToScalar(gXY[1]));
for (unsigned i = 2; i < SK_ARRAY_COUNT(gXY); i += 2)
path.lineTo(SkIntToScalar(gXY[i]), SkIntToScalar(gXY[i+1]));
path.close();
path.offset(SkIntToScalar(-6), 0);
auto outer = SkPath1DPathEffect::Make(path, SkIntToScalar(12),
gPhase, SkPath1DPathEffect::kRotate_Style);
auto inner = SkDiscretePathEffect::Make(SkIntToScalar(2),
SkIntToScalar(1)/10); // SkCornerPathEffect(SkIntToScalar(2));
return SkPathEffect::MakeCompose(outer, inner);
}
sk_sp<SkShader> shaderTest() {
SkPoint pts[] = { { 0, 0, }, { SkIntToScalar(100), 0 } };
SkColor colors[] = { SK_ColorRED, SK_ColorBLUE };
auto shaderA = SkGradientShader::MakeLinear(pts, colors, nullptr,
2, SkShader::kClamp_TileMode);
pts[1].set(0, SkIntToScalar(100));
SkColor colors2[] = {SK_ColorBLACK, SkColorSetARGB(0x80, 0, 0, 0)};
auto shaderB = SkGradientShader::MakeLinear(pts, colors2, nullptr,
2, SkShader::kClamp_TileMode);
return SkShader::MakeComposeShader(std::move(shaderA), std::move(shaderB),
SkBlendMode::kDstIn);
}
virtual void startTest() {
decode_file("/Users/caryclark/Desktop/bugcirc.gif", &fBug);
decode_file("/Users/caryclark/Desktop/tbcirc.gif", &fTb);
decode_file("/Users/caryclark/Desktop/05psp04.gif", &fTx);
}
void drawRaster(SkCanvas* canvas) {
for (size_t index = 0; index < SK_ARRAY_COUNT(gRastProcs); index++)
drawOneRaster(canvas);
}
void drawOneRaster(SkCanvas* canvas) {
canvas->save();
SkScalar x = SkIntToScalar(20);
SkScalar y = SkIntToScalar(40);
SkPaint paint;
paint.setAntiAlias(true);
paint.setTextSize(SkIntToScalar(48));
paint.setTypeface(SkTypeface::MakeFromName("sans-serif",
SkFontStyle::FromOldStyle(SkTypeface::kBold)));
SkString str("GOOGLE");
for (size_t i = 0; i < SK_ARRAY_COUNT(gRastProcs); i++) {
apply_shader(&paint, (int)i);
// paint.setMaskFilter(nullptr);
// paint.setColor(SK_ColorBLACK);
#if 01
int index = i % SK_ARRAY_COUNT(gLightingColors);
paint.setColorFilter(SkColorMatrixFilter::MakeLightingFilter(
gLightingColors[index].fMul,
gLightingColors[index].fAdd));
#endif
canvas->drawText(str.c_str(), str.size(), x, y, paint);
SkRect oval = { x, y - SkIntToScalar(40), x + SkIntToScalar(40), y };
paint.setStyle(SkPaint::kStroke_Style);
canvas->drawOval(oval, paint);
paint.setStyle(SkPaint::kFill_Style);
y += paint.getFontSpacing();
}
canvas->restore();
}
private:
SkPoint fClickPt;
SkBitmap fBug, fTb, fTx;
typedef SampleView INHERITED;
};
//////////////////////////////////////////////////////////////////////////////
static SkView* MyFactory() { return new DemoView; }
static SkViewRegister reg(MyFactory);