/*
* Copyright 2015 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 "SkCanvas.h"
#include "SkInterpolator.h"
#include "SkPath.h"
#include "SkRRect.h"
#include "SkTime.h"
// This slide tests out the match up between BW clipping and rendering. It can
// draw a large rect through some clip geometry and draw the same geometry
// normally. Which one is drawn first can be toggled. The pair of objects is translated
// fractionally (via an animator) to expose snapping bugs. The key bindings are:
// 1-9: the different geometries
// t: toggle which is drawn first the clip or the normal geometry
// f: flip-flops which corner the bottom AA clip rect occupies in the complex clip cases
// The possible geometric combinations to test
enum Geometry {
kRect_Geometry,
kRRect_Geometry,
kCircle_Geometry,
kConvexPath_Geometry,
kConcavePath_Geometry,
kRectAndRect_Geometry,
kRectAndRRect_Geometry,
kRectAndConvex_Geometry,
kRectAndConcave_Geometry
};
// The basic rect used is [kMin,kMin]..[kMax,kMax]
static const float kMin = 100.5f;
static const float kMid = 200.0f;
static const float kMax = 299.5f;
// The translation applied to the base AA rect in the combination cases
// (i.e., kRectAndRect through kRectAndConcave)
static const float kXlate = 100.0f;
SkRect create_rect(const SkPoint& offset) {
SkRect r = SkRect::MakeLTRB(kMin, kMin, kMax, kMax);
r.offset(offset);
return r;
}
SkRRect create_rrect(const SkPoint& offset) {
SkRRect rrect;
rrect.setRectXY(create_rect(offset), 10, 10);
return rrect;
}
SkRRect create_circle(const SkPoint& offset) {
SkRRect circle;
circle.setOval(create_rect(offset));
return circle;
}
SkPath create_convex_path(const SkPoint& offset) {
SkPath convexPath;
convexPath.moveTo(kMin, kMin);
convexPath.lineTo(kMax, kMax);
convexPath.lineTo(kMin, kMax);
convexPath.close();
convexPath.offset(offset.fX, offset.fY);
return convexPath;
}
SkPath create_concave_path(const SkPoint& offset) {
SkPath concavePath;
concavePath.moveTo(kMin, kMin);
concavePath.lineTo(kMid, 105.0f);
concavePath.lineTo(kMax, kMin);
concavePath.lineTo(295.0f, kMid);
concavePath.lineTo(kMax, kMax);
concavePath.lineTo(kMid, 295.0f);
concavePath.lineTo(kMin, kMax);
concavePath.lineTo(105.0f, kMid);
concavePath.close();
concavePath.offset(offset.fX, offset.fY);
return concavePath;
}
static void draw_normal_geom(SkCanvas* canvas, const SkPoint& offset, int geom, bool useAA) {
SkPaint p;
p.setAntiAlias(useAA);
p.setColor(SK_ColorBLACK);
switch (geom) {
case kRect_Geometry: // fall thru
case kRectAndRect_Geometry:
canvas->drawRect(create_rect(offset), p);
break;
case kRRect_Geometry: // fall thru
case kRectAndRRect_Geometry:
canvas->drawRRect(create_rrect(offset), p);
break;
case kCircle_Geometry:
canvas->drawRRect(create_circle(offset), p);
break;
case kConvexPath_Geometry: // fall thru
case kRectAndConvex_Geometry:
canvas->drawPath(create_convex_path(offset), p);
break;
case kConcavePath_Geometry: // fall thru
case kRectAndConcave_Geometry:
canvas->drawPath(create_concave_path(offset), p);
break;
}
}
class ClipDrawMatchView : public SampleView {
public:
ClipDrawMatchView() : fTrans(2, 5), fGeom(kRect_Geometry), fClipFirst(true), fSign(1) {
SkScalar values[2];
fTrans.setRepeatCount(999);
values[0] = values[1] = 0;
fTrans.setKeyFrame(0, GetMSecs() + 1000, values);
values[1] = 1;
fTrans.setKeyFrame(1, GetMSecs() + 2000, values);
values[0] = values[1] = 1;
fTrans.setKeyFrame(2, GetMSecs() + 3000, values);
values[1] = 0;
fTrans.setKeyFrame(3, GetMSecs() + 4000, values);
values[0] = 0;
fTrans.setKeyFrame(4, GetMSecs() + 5000, values);
}
protected:
bool onQuery(SkEvent* evt) override {
if (SampleCode::TitleQ(*evt)) {
SampleCode::TitleR(evt, "ClipDrawMatch");
return true;
}
SkUnichar uni;
if (SampleCode::CharQ(*evt, &uni)) {
switch (uni) {
case '1': fGeom = kRect_Geometry; this->inval(nullptr); return true;
case '2': fGeom = kRRect_Geometry; this->inval(nullptr); return true;
case '3': fGeom = kCircle_Geometry; this->inval(nullptr); return true;
case '4': fGeom = kConvexPath_Geometry; this->inval(nullptr); return true;
case '5': fGeom = kConcavePath_Geometry; this->inval(nullptr); return true;
case '6': fGeom = kRectAndRect_Geometry; this->inval(nullptr); return true;
case '7': fGeom = kRectAndRRect_Geometry; this->inval(nullptr); return true;
case '8': fGeom = kRectAndConvex_Geometry; this->inval(nullptr); return true;
case '9': fGeom = kRectAndConcave_Geometry; this->inval(nullptr); return true;
case 'f': fSign = -fSign; this->inval(nullptr); return true;
case 't': fClipFirst = !fClipFirst; this->inval(nullptr); return true;
default: break;
}
}
return this->INHERITED::onQuery(evt);
}
void drawClippedGeom(SkCanvas* canvas, const SkPoint& offset, bool useAA) {
int count = canvas->save();
switch (fGeom) {
case kRect_Geometry:
canvas->clipRect(create_rect(offset), useAA);
break;
case kRRect_Geometry:
canvas->clipRRect(create_rrect(offset), useAA);
break;
case kCircle_Geometry:
canvas->clipRRect(create_circle(offset), useAA);
break;
case kConvexPath_Geometry:
canvas->clipPath(create_convex_path(offset), useAA);
break;
case kConcavePath_Geometry:
canvas->clipPath(create_concave_path(offset), useAA);
break;
case kRectAndRect_Geometry: {
SkRect r = create_rect(offset);
r.offset(fSign * kXlate, fSign * kXlate);
canvas->clipRect(r, true); // AA here forces shader clips
canvas->clipRect(create_rect(offset), useAA);
} break;
case kRectAndRRect_Geometry: {
SkRect r = create_rect(offset);
r.offset(fSign * kXlate, fSign * kXlate);
canvas->clipRect(r, true); // AA here forces shader clips
canvas->clipRRect(create_rrect(offset), useAA);
} break;
case kRectAndConvex_Geometry: {
SkRect r = create_rect(offset);
r.offset(fSign * kXlate, fSign * kXlate);
canvas->clipRect(r, true); // AA here forces shader clips
canvas->clipPath(create_convex_path(offset), useAA);
} break;
case kRectAndConcave_Geometry: {
SkRect r = create_rect(offset);
r.offset(fSign * kXlate, fSign * kXlate);
canvas->clipRect(r, true); // AA here forces shader clips
canvas->clipPath(create_concave_path(offset), useAA);
} break;
}
SkISize size = canvas->getBaseLayerSize();
SkRect bigR = SkRect::MakeWH(SkIntToScalar(size.width()), SkIntToScalar(size.height()));
SkPaint p;
p.setColor(SK_ColorRED);
canvas->drawRect(bigR, p);
canvas->restoreToCount(count);
}
// Draw a big red rect through some clip geometry and also draw that same
// geometry in black. The order in which they are drawn can be swapped.
// This tests whether the clip and normally drawn geometry match up.
void drawGeometry(SkCanvas* canvas, const SkPoint& offset, bool useAA) {
if (fClipFirst) {
this->drawClippedGeom(canvas, offset, useAA);
}
draw_normal_geom(canvas, offset, fGeom, useAA);
if (!fClipFirst) {
this->drawClippedGeom(canvas, offset, useAA);
}
}
void onDrawContent(SkCanvas* canvas) override {
SkScalar trans[2];
fTrans.timeToValues(GetMSecs(), trans);
SkPoint offset;
offset.set(trans[0], trans[1]);
int saveCount = canvas->save();
this->drawGeometry(canvas, offset, false);
canvas->restoreToCount(saveCount);
this->inval(nullptr);
}
SkMSec GetMSecs() const {
return static_cast<SkMSec>(SkTime::GetMSecs() - fStart);
}
private:
SkInterpolator fTrans;
Geometry fGeom;
bool fClipFirst;
int fSign;
const double fStart = SkTime::GetMSecs();
typedef SampleView INHERITED;
};
//////////////////////////////////////////////////////////////////////////////
static SkView* MyFactory() { return new ClipDrawMatchView; }
static SkViewRegister reg(MyFactory);