You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
513 lines
14 KiB
513 lines
14 KiB
/*
|
|
* Copyright 2012 Google Inc.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
#include "Sample.h"
|
|
#include "SkBlendMode.h"
|
|
#include "SkCanvas.h"
|
|
#include "SkClipOpPriv.h"
|
|
#include "SkColor.h"
|
|
#include "SkImageInfo.h"
|
|
#include "SkMatrix.h"
|
|
#include "SkPaint.h"
|
|
#include "SkPath.h"
|
|
#include "SkPoint.h"
|
|
#include "SkPointPriv.h"
|
|
#include "SkRect.h"
|
|
#include "SkRefCnt.h"
|
|
#include "SkScalar.h"
|
|
#include "SkShader.h"
|
|
#include "SkString.h"
|
|
#include "SkSurface.h"
|
|
#include "SkTypes.h"
|
|
#include "sk_tool_utils.h"
|
|
|
|
class SkEvent;
|
|
|
|
#define FAT_PIXEL_COLOR SK_ColorBLACK
|
|
#define PIXEL_CENTER_SIZE 3
|
|
#define WIRE_FRAME_COLOR 0xFFFF0000 /*0xFF00FFFF*/
|
|
#define WIRE_FRAME_SIZE 1.5f
|
|
|
|
static SkScalar apply_grid(SkScalar x) {
|
|
const SkScalar grid = 2;
|
|
return SkScalarRoundToScalar(x * grid) / grid;
|
|
}
|
|
|
|
static void apply_grid(SkPoint pts[], int count) {
|
|
for (int i = 0; i < count; ++i) {
|
|
pts[i].set(apply_grid(pts[i].fX), apply_grid(pts[i].fY));
|
|
}
|
|
}
|
|
|
|
static void erase(SkSurface* surface) {
|
|
surface->getCanvas()->clear(SK_ColorTRANSPARENT);
|
|
}
|
|
|
|
class FatBits {
|
|
public:
|
|
FatBits() {
|
|
fAA = false;
|
|
fStyle = kHair_Style;
|
|
fGrid = false;
|
|
fShowSkeleton = true;
|
|
fUseClip = false;
|
|
fRectAsOval = false;
|
|
fUseTriangle = false;
|
|
fStrokeCap = SkPaint::kButt_Cap;
|
|
|
|
fClipRect.set(2, 2, 11, 8 );
|
|
}
|
|
|
|
int getZoom() const { return fZoom; }
|
|
|
|
bool getAA() const { return fAA; }
|
|
void setAA(bool aa) { fAA = aa; }
|
|
|
|
bool getGrid() const { return fGrid; }
|
|
void setGrid(bool g) { fGrid = g; }
|
|
|
|
bool getShowSkeleton() const { return fShowSkeleton; }
|
|
void setShowSkeleton(bool ss) { fShowSkeleton = ss; }
|
|
|
|
bool getTriangle() const { return fUseTriangle; }
|
|
void setTriangle(bool ut) { fUseTriangle = ut; }
|
|
|
|
void toggleRectAsOval() { fRectAsOval = !fRectAsOval; }
|
|
|
|
void togglePixelColors() {
|
|
if (fShader == fShader0) {
|
|
fShader = fShader1;
|
|
} else {
|
|
fShader = fShader0;
|
|
}
|
|
}
|
|
|
|
float fStrokeWidth = 1;
|
|
|
|
bool getUseClip() const { return fUseClip; }
|
|
void setUseClip(bool uc) { fUseClip = uc; }
|
|
|
|
enum Style {
|
|
kHair_Style,
|
|
kStroke_Style,
|
|
};
|
|
Style getStyle() const { return fStyle; }
|
|
void setStyle(Style s) { fStyle = s; }
|
|
|
|
void setWHZ(int width, int height, int zoom) {
|
|
fW = width;
|
|
fH = height;
|
|
fZoom = zoom;
|
|
fBounds.set(0, 0, SkIntToScalar(width * zoom), SkIntToScalar(height * zoom));
|
|
fMatrix.setScale(SkIntToScalar(zoom), SkIntToScalar(zoom));
|
|
fInverse.setScale(SK_Scalar1 / zoom, SK_Scalar1 / zoom);
|
|
fShader0 = sk_tool_utils::create_checkerboard_shader(0xFFDDDDDD, 0xFFFFFFFF, zoom);
|
|
fShader1 = SkShader::MakeColorShader(SK_ColorWHITE);
|
|
fShader = fShader0;
|
|
|
|
SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
|
|
fMinSurface = SkSurface::MakeRaster(info);
|
|
info = info.makeWH(width * zoom, height * zoom);
|
|
fMaxSurface = SkSurface::MakeRaster(info);
|
|
}
|
|
|
|
void drawBG(SkCanvas*);
|
|
void drawFG(SkCanvas*);
|
|
void drawLine(SkCanvas*, SkPoint pts[2]);
|
|
void drawRect(SkCanvas* canvas, SkPoint pts[2]);
|
|
void drawTriangle(SkCanvas* canvas, SkPoint pts[3]);
|
|
|
|
SkPaint::Cap fStrokeCap;
|
|
|
|
private:
|
|
bool fAA, fGrid, fShowSkeleton, fUseClip, fRectAsOval, fUseTriangle;
|
|
Style fStyle;
|
|
int fW, fH, fZoom;
|
|
SkMatrix fMatrix, fInverse;
|
|
SkRect fBounds, fClipRect;
|
|
sk_sp<SkShader> fShader0;
|
|
sk_sp<SkShader> fShader1;
|
|
sk_sp<SkShader> fShader;
|
|
sk_sp<SkSurface> fMinSurface;
|
|
sk_sp<SkSurface> fMaxSurface;
|
|
|
|
void setupPaint(SkPaint* paint) {
|
|
bool aa = this->getAA();
|
|
paint->setStrokeCap(fStrokeCap);
|
|
switch (fStyle) {
|
|
case kHair_Style:
|
|
paint->setStrokeWidth(0);
|
|
break;
|
|
case kStroke_Style:
|
|
paint->setStrokeWidth(fStrokeWidth);
|
|
break;
|
|
}
|
|
paint->setAntiAlias(aa);
|
|
}
|
|
|
|
void setupSkeletonPaint(SkPaint* paint) {
|
|
paint->setStyle(SkPaint::kStroke_Style);
|
|
paint->setStrokeWidth(WIRE_FRAME_SIZE);
|
|
paint->setColor(fShowSkeleton ? WIRE_FRAME_COLOR : 0);
|
|
paint->setAntiAlias(true);
|
|
}
|
|
|
|
void drawTriangleSkeleton(SkCanvas* max, const SkPoint pts[]);
|
|
void drawLineSkeleton(SkCanvas* max, const SkPoint pts[]);
|
|
void drawRectSkeleton(SkCanvas* max, const SkRect& r) {
|
|
SkPaint paint;
|
|
this->setupSkeletonPaint(&paint);
|
|
SkPath path;
|
|
|
|
fRectAsOval ? path.addOval(r) : path.addRect(r);
|
|
max->drawPath(path, paint);
|
|
}
|
|
|
|
void copyMinToMax() {
|
|
erase(fMaxSurface.get());
|
|
SkCanvas* canvas = fMaxSurface->getCanvas();
|
|
canvas->save();
|
|
canvas->concat(fMatrix);
|
|
fMinSurface->draw(canvas, 0, 0, nullptr);
|
|
canvas->restore();
|
|
|
|
SkPaint paint;
|
|
paint.setBlendMode(SkBlendMode::kClear);
|
|
for (int iy = 1; iy < fH; ++iy) {
|
|
SkScalar y = SkIntToScalar(iy * fZoom);
|
|
canvas->drawLine(0, y - SK_ScalarHalf, 999, y - SK_ScalarHalf, paint);
|
|
}
|
|
for (int ix = 1; ix < fW; ++ix) {
|
|
SkScalar x = SkIntToScalar(ix * fZoom);
|
|
canvas->drawLine(x - SK_ScalarHalf, 0, x - SK_ScalarHalf, 999, paint);
|
|
}
|
|
}
|
|
};
|
|
|
|
void FatBits::drawBG(SkCanvas* canvas) {
|
|
SkPaint paint;
|
|
|
|
paint.setShader(fShader);
|
|
canvas->drawRect(fBounds, paint);
|
|
paint.setShader(nullptr);
|
|
}
|
|
|
|
void FatBits::drawFG(SkCanvas* canvas) {
|
|
SkPaint inner, outer;
|
|
|
|
inner.setAntiAlias(true);
|
|
inner.setColor(SK_ColorBLACK);
|
|
inner.setStrokeWidth(PIXEL_CENTER_SIZE);
|
|
|
|
outer.setAntiAlias(true);
|
|
outer.setColor(SK_ColorWHITE);
|
|
outer.setStrokeWidth(PIXEL_CENTER_SIZE + 2);
|
|
|
|
SkScalar half = SkIntToScalar(fZoom) / 2;
|
|
for (int iy = 0; iy < fH; ++iy) {
|
|
SkScalar y = SkIntToScalar(iy * fZoom) + half;
|
|
for (int ix = 0; ix < fW; ++ix) {
|
|
SkScalar x = SkIntToScalar(ix * fZoom) + half;
|
|
|
|
canvas->drawPoint(x, y, outer);
|
|
canvas->drawPoint(x, y, inner);
|
|
}
|
|
}
|
|
|
|
if (fUseClip) {
|
|
SkPaint p;
|
|
p.setStyle(SkPaint::kStroke_Style);
|
|
p.setColor(SK_ColorLTGRAY);
|
|
SkRect r = {
|
|
fClipRect.fLeft * fZoom,
|
|
fClipRect.fTop * fZoom,
|
|
fClipRect.fRight * fZoom,
|
|
fClipRect.fBottom * fZoom
|
|
};
|
|
canvas->drawRect(r, p);
|
|
}
|
|
}
|
|
|
|
void FatBits::drawLineSkeleton(SkCanvas* max, const SkPoint pts[]) {
|
|
SkPaint paint;
|
|
this->setupSkeletonPaint(&paint);
|
|
|
|
SkPath path;
|
|
path.moveTo(pts[0]);
|
|
path.lineTo(pts[1]);
|
|
|
|
if (fStyle == kStroke_Style) {
|
|
SkPaint p;
|
|
p.setStyle(SkPaint::kStroke_Style);
|
|
p.setStrokeWidth(fStrokeWidth * fZoom);
|
|
p.setStrokeCap(fStrokeCap);
|
|
SkPath dst;
|
|
p.getFillPath(path, &dst);
|
|
path = dst;
|
|
|
|
path.moveTo(pts[0]);
|
|
path.lineTo(pts[1]);
|
|
}
|
|
max->drawPath(path, paint);
|
|
}
|
|
|
|
void FatBits::drawLine(SkCanvas* canvas, SkPoint pts[]) {
|
|
SkPaint paint;
|
|
|
|
fInverse.mapPoints(pts, 2);
|
|
|
|
if (fGrid) {
|
|
apply_grid(pts, 2);
|
|
}
|
|
|
|
erase(fMinSurface.get());
|
|
this->setupPaint(&paint);
|
|
paint.setColor(FAT_PIXEL_COLOR);
|
|
if (fUseClip) {
|
|
fMinSurface->getCanvas()->save();
|
|
SkRect r = fClipRect;
|
|
r.inset(SK_Scalar1/3, SK_Scalar1/3);
|
|
fMinSurface->getCanvas()->clipRect(r, kIntersect_SkClipOp, true);
|
|
}
|
|
fMinSurface->getCanvas()->drawLine(pts[0], pts[1], paint);
|
|
if (fUseClip) {
|
|
fMinSurface->getCanvas()->restore();
|
|
}
|
|
this->copyMinToMax();
|
|
|
|
SkCanvas* max = fMaxSurface->getCanvas();
|
|
|
|
fMatrix.mapPoints(pts, 2);
|
|
this->drawLineSkeleton(max, pts);
|
|
|
|
fMaxSurface->draw(canvas, 0, 0, nullptr);
|
|
}
|
|
|
|
void FatBits::drawRect(SkCanvas* canvas, SkPoint pts[2]) {
|
|
SkPaint paint;
|
|
|
|
fInverse.mapPoints(pts, 2);
|
|
|
|
if (fGrid) {
|
|
apply_grid(pts, 2);
|
|
}
|
|
|
|
SkRect r;
|
|
r.set(pts, 2);
|
|
|
|
erase(fMinSurface.get());
|
|
this->setupPaint(&paint);
|
|
paint.setColor(FAT_PIXEL_COLOR);
|
|
{
|
|
SkCanvas* c = fMinSurface->getCanvas();
|
|
fRectAsOval ? c->drawOval(r, paint) : c->drawRect(r, paint);
|
|
}
|
|
this->copyMinToMax();
|
|
|
|
SkCanvas* max = fMaxSurface->getCanvas();
|
|
|
|
fMatrix.mapPoints(pts, 2);
|
|
r.set(pts, 2);
|
|
this->drawRectSkeleton(max, r);
|
|
|
|
fMaxSurface->draw(canvas, 0, 0, nullptr);
|
|
}
|
|
|
|
void FatBits::drawTriangleSkeleton(SkCanvas* max, const SkPoint pts[]) {
|
|
SkPaint paint;
|
|
this->setupSkeletonPaint(&paint);
|
|
|
|
SkPath path;
|
|
path.moveTo(pts[0]);
|
|
path.lineTo(pts[1]);
|
|
path.lineTo(pts[2]);
|
|
path.close();
|
|
|
|
max->drawPath(path, paint);
|
|
}
|
|
|
|
void FatBits::drawTriangle(SkCanvas* canvas, SkPoint pts[3]) {
|
|
SkPaint paint;
|
|
|
|
fInverse.mapPoints(pts, 3);
|
|
|
|
if (fGrid) {
|
|
apply_grid(pts, 3);
|
|
}
|
|
|
|
SkPath path;
|
|
path.moveTo(pts[0]);
|
|
path.lineTo(pts[1]);
|
|
path.lineTo(pts[2]);
|
|
path.close();
|
|
|
|
erase(fMinSurface.get());
|
|
this->setupPaint(&paint);
|
|
paint.setColor(FAT_PIXEL_COLOR);
|
|
fMinSurface->getCanvas()->drawPath(path, paint);
|
|
this->copyMinToMax();
|
|
|
|
SkCanvas* max = fMaxSurface->getCanvas();
|
|
|
|
fMatrix.mapPoints(pts, 3);
|
|
this->drawTriangleSkeleton(max, pts);
|
|
|
|
fMaxSurface->draw(canvas, 0, 0, nullptr);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
class IndexClick : public Sample::Click {
|
|
int fIndex;
|
|
public:
|
|
IndexClick(Sample* v, int index) : Sample::Click(v), fIndex(index) {}
|
|
|
|
static int GetIndex(Sample::Click* click) {
|
|
return ((IndexClick*)click)->fIndex;
|
|
}
|
|
};
|
|
|
|
class DrawLineView : public Sample {
|
|
FatBits fFB;
|
|
SkPoint fPts[3];
|
|
bool fIsRect;
|
|
int fZoom = 64;
|
|
public:
|
|
DrawLineView() {
|
|
fFB.setWHZ(24*2, 16*2, fZoom);
|
|
fPts[0].set(1, 1);
|
|
fPts[1].set(5, 4);
|
|
fPts[2].set(2, 6);
|
|
SkMatrix::MakeScale(SkIntToScalar(fZoom)).mapPoints(fPts, 3);
|
|
fIsRect = false;
|
|
}
|
|
|
|
void setStyle(FatBits::Style s) {
|
|
fFB.setStyle(s);
|
|
}
|
|
|
|
protected:
|
|
bool onQuery(Sample::Event* evt) override {
|
|
if (Sample::TitleQ(*evt)) {
|
|
Sample::TitleR(evt, "FatBits");
|
|
return true;
|
|
}
|
|
SkUnichar uni;
|
|
if (Sample::CharQ(*evt, &uni)) {
|
|
switch (uni) {
|
|
case 'c':
|
|
fFB.setUseClip(!fFB.getUseClip());
|
|
return true;
|
|
case 'r':
|
|
fIsRect = !fIsRect;
|
|
return true;
|
|
case 'o':
|
|
fFB.toggleRectAsOval();
|
|
return true;
|
|
case 'x':
|
|
fFB.setGrid(!fFB.getGrid());
|
|
return true;
|
|
case 's':
|
|
if (FatBits::kStroke_Style == fFB.getStyle()) {
|
|
this->setStyle(FatBits::kHair_Style);
|
|
} else {
|
|
this->setStyle(FatBits::kStroke_Style);
|
|
}
|
|
return true;
|
|
case 'k': {
|
|
const SkPaint::Cap caps[] = {
|
|
SkPaint::kButt_Cap, SkPaint::kRound_Cap, SkPaint::kSquare_Cap,
|
|
};
|
|
fFB.fStrokeCap = caps[(fFB.fStrokeCap + 1) % 3];
|
|
return true;
|
|
} break;
|
|
case 'a':
|
|
fFB.setAA(!fFB.getAA());
|
|
return true;
|
|
case 'w':
|
|
fFB.setShowSkeleton(!fFB.getShowSkeleton());
|
|
return true;
|
|
case 'g':
|
|
fFB.togglePixelColors();
|
|
return true;
|
|
case 't':
|
|
fFB.setTriangle(!fFB.getTriangle());
|
|
return true;
|
|
case '-':
|
|
fFB.fStrokeWidth -= 0.125f;
|
|
return true;
|
|
case '=':
|
|
fFB.fStrokeWidth += 0.125f;
|
|
return true;
|
|
}
|
|
}
|
|
return this->INHERITED::onQuery(evt);
|
|
}
|
|
|
|
void onDrawContent(SkCanvas* canvas) override {
|
|
fFB.drawBG(canvas);
|
|
if (fFB.getTriangle()) {
|
|
fFB.drawTriangle(canvas, fPts);
|
|
}
|
|
else if (fIsRect) {
|
|
fFB.drawRect(canvas, fPts);
|
|
} else {
|
|
fFB.drawLine(canvas, fPts);
|
|
}
|
|
fFB.drawFG(canvas);
|
|
|
|
{
|
|
SkString str;
|
|
str.printf("%s %s %s",
|
|
fFB.getAA() ? "AA" : "BW",
|
|
FatBits::kHair_Style == fFB.getStyle() ? "Hair" : "Stroke",
|
|
fFB.getUseClip() ? "clip" : "noclip");
|
|
SkPaint paint;
|
|
paint.setColor(SK_ColorBLUE);
|
|
SkFont font(nullptr, 16);
|
|
canvas->drawString(str, 10, 16, font, paint);
|
|
}
|
|
}
|
|
|
|
Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
|
|
SkPoint pt = { x, y };
|
|
int index = -1;
|
|
int count = fFB.getTriangle() ? 3 : 2;
|
|
SkScalar tol = 12;
|
|
|
|
for (int i = 0; i < count; ++i) {
|
|
if (SkPointPriv::EqualsWithinTolerance(fPts[i], pt, tol)) {
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
return new IndexClick(this, index);
|
|
}
|
|
|
|
bool onClick(Click* click) override {
|
|
int index = IndexClick::GetIndex(click);
|
|
if (index >= 0 && index <= 2) {
|
|
fPts[index] = click->fCurr;
|
|
} else {
|
|
SkScalar dx = click->fCurr.fX - click->fPrev.fX;
|
|
SkScalar dy = click->fCurr.fY - click->fPrev.fY;
|
|
fPts[0].offset(dx, dy);
|
|
fPts[1].offset(dx, dy);
|
|
fPts[2].offset(dx, dy);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
|
|
typedef Sample INHERITED;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
DEF_SAMPLE( return new DrawLineView(); )
|