/*
* Copyright 2014 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "gm.h"
#if SK_SUPPORT_GPU
#include "GrFragmentProcessor.h"
#include "GrCoordTransform.h"
#include "gl/GrGLProcessor.h"
#include "gl/builders/GrGLProgramBuilder.h"
#include "Resources.h"
#include "SkReadBuffer.h"
#include "SkShader.h"
#include "SkStream.h"
#include "SkTypeface.h"
#include "SkWriteBuffer.h"
namespace skiagm {
///////////////////////////////////////////////////////////////////////////////
class DCShader : public SkShader {
public:
DCShader(const SkMatrix& matrix) : fDeviceMatrix(matrix) {}
SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(DCShader);
void flatten(SkWriteBuffer& buf) const override {
buf.writeMatrix(fDeviceMatrix);
}
bool asFragmentProcessor(GrContext*, const SkPaint& paint, const SkMatrix& viewM,
const SkMatrix* localMatrix, GrColor* color,
GrFragmentProcessor** fp) const override;
#ifndef SK_IGNORE_TO_STRING
void toString(SkString* str) const override {
str->appendf("DCShader: ()");
}
#endif
private:
const SkMatrix fDeviceMatrix;
};
SkFlattenable* DCShader::CreateProc(SkReadBuffer& buf) {
SkMatrix matrix;
buf.readMatrix(&matrix);
return SkNEW_ARGS(DCShader, (matrix));
}
class DCFP : public GrFragmentProcessor {
public:
DCFP(const SkMatrix& m) : fDeviceTransform(kDevice_GrCoordSet, m) {
this->addCoordTransform(&fDeviceTransform);
this->initClassID<DCFP>();
}
void getGLProcessorKey(const GrGLSLCaps& caps,
GrProcessorKeyBuilder* b) const override {}
GrGLFragmentProcessor* createGLInstance() const override {
class DCGLFP : public GrGLFragmentProcessor {
void emitCode(GrGLFPBuilder* builder,
const GrFragmentProcessor& fp,
const char* outputColor,
const char* inputColor,
const TransformedCoordsArray& coords,
const TextureSamplerArray& samplers) {
GrGLFragmentBuilder* fpb = builder->getFragmentShaderBuilder();
fpb->codeAppendf("vec2 c = %s;", fpb->ensureFSCoords2D(coords, 0).c_str());
fpb->codeAppend("vec2 r = mod(c, vec2(20.0));");
fpb->codeAppend("vec4 color = vec4(0.5*sin(c.x / 15.0) + 0.5,"
"0.5*cos((c.x + c.y) / 15.0) + 0.5,"
"(r.x + r.y) / 20.0,"
"distance(r, vec2(15.0)) / 20.0 + 0.2);");
fpb->codeAppendf("color.rgb *= color.a;"
"%s = color * %s;",
outputColor, GrGLSLExpr4(inputColor).c_str());
}
void setData(const GrGLProgramDataManager&, const GrProcessor&) override {}
};
return SkNEW(DCGLFP);
}
const char* name() const override { return "DCFP"; }
void onComputeInvariantOutput(GrInvariantOutput* inout) const override {
inout->mulByUnknownFourComponents();
}
private:
bool onIsEqual(const GrFragmentProcessor&) const override { return true; }
GrCoordTransform fDeviceTransform;
};
bool DCShader::asFragmentProcessor(GrContext*, const SkPaint& paint, const SkMatrix& viewM,
const SkMatrix* localMatrix, GrColor* color,
GrFragmentProcessor** fp) const {
*fp = SkNEW_ARGS(DCFP, (fDeviceMatrix));
*color = GrColorPackA4(paint.getAlpha());
return true;
}
class DCShaderGM : public GM {
public:
DCShaderGM() {
this->setBGColor(0xFFAABBCC);
}
~DCShaderGM() override {
for (int i = 0; i < fPrims.count(); ++i) {
SkDELETE(fPrims[i]);
}
}
protected:
SkString onShortName() override {
return SkString("dcshader");
}
SkISize onISize() override { return SkISize::Make(1000, 900); }
void onOnceBeforeDraw() override {
struct Rect : public Prim {
SkRect draw(SkCanvas* canvas, const SkPaint& paint) override {
SkRect rect = SkRect::MakeXYWH(0, 0, 50, 50);
canvas->drawRect(rect, paint);
return rect;
}
};
struct Circle : public Prim {
SkRect draw(SkCanvas* canvas, const SkPaint& paint) override {
static const SkScalar radius = 25;
canvas->drawCircle(radius, radius, radius, paint);
return SkRect::MakeXYWH(0, 0, 2 * radius, 2 * radius);
}
};
struct RRect : public Prim {
SkRect draw(SkCanvas* canvas, const SkPaint& paint) override {
SkRRect rrect;
rrect.setRectXY(SkRect::MakeXYWH(0, 0, 50, 50), 10, 10);
canvas->drawRRect(rrect, paint);
return rrect.getBounds();
}
};
struct DRRect : public Prim {
SkRect draw(SkCanvas* canvas, const SkPaint& paint) override {
SkRRect outerRRect;
outerRRect.setRectXY(SkRect::MakeXYWH(0, 0, 50, 50), 5, 5);
SkRRect innerRRect;
innerRRect.setRectXY(SkRect::MakeXYWH(5, 8, 35, 30), 8, 3);
canvas->drawDRRect(outerRRect, innerRRect, paint);
return outerRRect.getBounds();
}
};
struct Path : public Prim {
SkRect draw(SkCanvas* canvas, const SkPaint& paint) override {
SkPath path;
path.addCircle(15, 15, 10);
path.addOval(SkRect::MakeXYWH(2, 2, 22, 37));
path.setFillType(SkPath::kEvenOdd_FillType);
canvas->drawPath(path, paint);
return path.getBounds();
}
};
struct Points : public Prim {
Points(SkCanvas::PointMode mode) : fMode(mode) {}
SkRect draw(SkCanvas* canvas, const SkPaint& paint) override {
SkRandom random;
SkPoint points[500];
SkRect bounds = SkRect::MakeWH(50, 50);
int count = SkToInt(SK_ARRAY_COUNT(points));
if (SkCanvas::kPoints_PointMode != fMode) {
count = SkTMin(count, 10);
}
for (int p = 0; p < count; ++p) {
points[p].fX = random.nextUScalar1() * bounds.width();
points[p].fY = random.nextUScalar1() * bounds.width();
}
canvas->drawPoints(fMode, count, points, paint);
return bounds;
}
SkCanvas::PointMode fMode;
};
struct Text : public Prim {
SkRect draw(SkCanvas* canvas, const SkPaint& origPaint) override {
SkPaint paint = origPaint;
paint.setTextSize(30.f);
this->setFont(&paint);
const char* text = this->text();
static const SkVector offset = SkVector::Make(10, 10);
canvas->drawText(text, strlen(text), offset.fX, offset.fY, paint);
SkRect bounds;
paint.measureText(text, strlen(text), &bounds);
bounds.offset(offset);
return bounds;
}
virtual void setFont(SkPaint* paint) {
sk_tool_utils::set_portable_typeface(paint);
}
virtual const char* text() const { return "Hello, Skia!"; }
};
struct BmpText : public Text {
void setFont(SkPaint* paint) override {
if (!fTypeface) {
fTypeface.reset(GetResourceAsTypeface("/fonts/Funkster.ttf"));
}
paint->setTypeface(fTypeface);
}
const char* text() const override { return "Hi, Skia!"; }
SkAutoTUnref<SkTypeface> fTypeface;
};
fPrims.push_back(SkNEW(Rect));
fPrims.push_back(SkNEW(Circle));
fPrims.push_back(SkNEW(RRect));
fPrims.push_back(SkNEW(DRRect));
fPrims.push_back(SkNEW(Path));
fPrims.push_back(SkNEW(Points(SkCanvas::kPoints_PointMode)));
fPrims.push_back(SkNEW(Points(SkCanvas::kLines_PointMode)));
fPrims.push_back(SkNEW(Points(SkCanvas::kPolygon_PointMode)));
fPrims.push_back(SkNEW(Text));
fPrims.push_back(SkNEW(BmpText));
}
void onDraw(SkCanvas* canvas) override {
// This GM exists to test a specific feature of the GPU backend. It does not work with the
// sw rasterizer, tile modes, etc.
if (NULL == canvas->getGrContext()) {
this->drawGpuOnlyMessage(canvas);
return;
}
SkPaint paint;
SkTArray<SkMatrix> devMats;
devMats.push_back().reset();
devMats.push_back().setRotate(45, 500, 500);
devMats.push_back().setRotate(-30, 200, 200);
devMats.back().setPerspX(-SK_Scalar1 / 2000);
devMats.back().setPerspY(SK_Scalar1 / 1000);
SkTArray<SkMatrix> viewMats;
viewMats.push_back().setScale(0.75f, 0.75f);
viewMats.push_back().setRotate(45, 50, 50);
viewMats.back().postScale(0.5f, 1.1f);
canvas->translate(10, 20);
canvas->save();
SkScalar tx = 0, maxTy = 0;
static const SkScalar kW = 900;
for (int aa = 0; aa < 2; ++aa) {
for (int i = 0; i < fPrims.count(); ++i) {
for (int j = 0; j < devMats.count(); ++j) {
for (int k = 0; k < viewMats.count(); ++k) {
paint.setShader(SkNEW_ARGS(DCShader, (devMats[j])))->unref();
paint.setAntiAlias(SkToBool(aa));
canvas->save();
canvas->concat(viewMats[k]);
SkRect bounds = fPrims[i]->draw(canvas, paint);
canvas->restore();
viewMats[k].mapRect(&bounds);
// add margins
bounds.fRight += 20;
bounds.fBottom += 20;
canvas->translate(bounds.fRight, 0);
tx += bounds.fRight;
maxTy = SkTMax(bounds.fBottom, maxTy);
if (tx > kW) {
tx = 0;
canvas->restore();
canvas->translate(0, maxTy);
canvas->save();
maxTy = 0;
}
}
}
}
}
canvas->restore();
}
private:
struct Prim {
virtual ~Prim() {}
virtual SkRect draw(SkCanvas*, const SkPaint&) = 0;
};
SkTArray<Prim*> fPrims;
typedef GM INHERITED;
};
DEF_GM( return SkNEW(DCShaderGM); )
}
#endif