| /* | |
| * 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 "sk_tool_utils.h" | |
| #include "SampleCode.h" | |
| #include "SkView.h" | |
| #include "SkCanvas.h" | |
| #include "SkPath.h" | |
| #include "SkRegion.h" | |
| #include "SkShader.h" | |
| #include "SkUtils.h" | |
| #include "SkImage.h" | |
| #include "SkSurface.h" | |
| #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 = true; | |
| fShowSkeleton = true; | |
| fUseGPU = false; | |
| fUseClip = false; | |
| fRectAsOval = false; | |
| fUseTriangle = false; | |
| 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 getUseGPU() const { return fUseGPU; } | |
| void setUseGPU(bool ug) { fUseGPU = ug; } | |
| bool getTriangle() const { return fUseTriangle; } | |
| void setTriangle(bool ut) { fUseTriangle = ut; } | |
| void toggleRectAsOval() { fRectAsOval = !fRectAsOval; } | |
| 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); | |
| fShader.reset(sk_tool_utils::create_checkerboard_shader( | |
| 0xFFCCCCCC, 0xFFFFFFFF, zoom)); | |
| SkImageInfo info = SkImageInfo::MakeN32Premul(width, height); | |
| fMinSurface.reset(SkSurface::NewRaster(info)); | |
| info = info.makeWH(width * zoom, height * zoom); | |
| fMaxSurface.reset(SkSurface::NewRaster(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]); | |
| private: | |
| bool fAA, fGrid, fShowSkeleton, fUseGPU, fUseClip, fRectAsOval, fUseTriangle; | |
| Style fStyle; | |
| int fW, fH, fZoom; | |
| SkMatrix fMatrix, fInverse; | |
| SkRect fBounds, fClipRect; | |
| SkAutoTUnref<SkShader> fShader; | |
| SkAutoTUnref<SkSurface> fMinSurface; | |
| SkAutoTUnref<SkSurface> fMaxSurface; | |
| void setupPaint(SkPaint* paint) { | |
| bool aa = this->getAA(); | |
| switch (fStyle) { | |
| case kHair_Style: | |
| paint->setStrokeWidth(0); | |
| break; | |
| case kStroke_Style: | |
| paint->setStrokeWidth(SK_Scalar1); | |
| 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; | |
| if (fUseGPU && fAA) { | |
| SkRect rr = r; | |
| rr.inset(SkIntToScalar(fZoom)/2, SkIntToScalar(fZoom)/2); | |
| path.addRect(rr); | |
| path.moveTo(rr.fLeft, rr.fTop); | |
| path.lineTo(rr.fRight, rr.fBottom); | |
| rr = r; | |
| rr.inset(-SkIntToScalar(fZoom)/2, -SkIntToScalar(fZoom)/2); | |
| path.addRect(rr); | |
| } else { | |
| fRectAsOval ? path.addOval(r) : path.addRect(r); | |
| if (fUseGPU) { | |
| path.moveTo(r.fLeft, r.fTop); | |
| path.lineTo(r.fRight, r.fBottom); | |
| } | |
| } | |
| max->drawPath(path, paint); | |
| } | |
| void copyMinToMax() { | |
| erase(fMaxSurface); | |
| SkCanvas* canvas = fMaxSurface->getCanvas(); | |
| canvas->save(); | |
| canvas->concat(fMatrix); | |
| fMinSurface->draw(canvas, 0, 0, NULL); | |
| canvas->restore(); | |
| SkPaint paint; | |
| paint.setXfermodeMode(SkXfermode::kClear_Mode); | |
| 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(NULL); | |
| } | |
| 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]); | |
| switch (fStyle) { | |
| case kHair_Style: | |
| if (fUseGPU) { | |
| SkPaint p; | |
| p.setStyle(SkPaint::kStroke_Style); | |
| p.setStrokeWidth(SK_Scalar1 * fZoom); | |
| SkPath dst; | |
| p.getFillPath(path, &dst); | |
| path.addPath(dst); | |
| } | |
| break; | |
| case kStroke_Style: { | |
| SkPaint p; | |
| p.setStyle(SkPaint::kStroke_Style); | |
| p.setStrokeWidth(SK_Scalar1 * fZoom); | |
| SkPath dst; | |
| p.getFillPath(path, &dst); | |
| path = dst; | |
| if (fUseGPU) { | |
| path.moveTo(dst.getPoint(0)); | |
| path.lineTo(dst.getPoint(2)); | |
| } | |
| } break; | |
| } | |
| 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); | |
| 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, SkRegion::kIntersect_Op, true); | |
| } | |
| fMinSurface->getCanvas()->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, 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, NULL); | |
| } | |
| 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); | |
| 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, NULL); | |
| } | |
| 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); | |
| 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, NULL); | |
| } | |
| /////////////////////////////////////////////////////////////////////////////////////////////////// | |
| class IndexClick : public SkView::Click { | |
| int fIndex; | |
| public: | |
| IndexClick(SkView* v, int index) : SkView::Click(v), fIndex(index) {} | |
| static int GetIndex(SkView::Click* click) { | |
| return ((IndexClick*)click)->fIndex; | |
| } | |
| }; | |
| class DrawLineView : public SampleView { | |
| FatBits fFB; | |
| SkPoint fPts[3]; | |
| bool fIsRect; | |
| public: | |
| DrawLineView() { | |
| fFB.setWHZ(24, 16, 48); | |
| fPts[0].set(48, 48); | |
| fPts[1].set(48 * 5, 48 * 4); | |
| fPts[2].set(48 * 2, 48 * 6); | |
| fIsRect = false; | |
| } | |
| void setStyle(FatBits::Style s) { | |
| fFB.setStyle(s); | |
| this->inval(NULL); | |
| } | |
| protected: | |
| bool onQuery(SkEvent* evt) override { | |
| if (SampleCode::TitleQ(*evt)) { | |
| SampleCode::TitleR(evt, "FatBits"); | |
| return true; | |
| } | |
| SkUnichar uni; | |
| if (SampleCode::CharQ(*evt, &uni)) { | |
| switch (uni) { | |
| case 'c': | |
| fFB.setUseClip(!fFB.getUseClip()); | |
| this->inval(NULL); | |
| return true; | |
| case 'r': | |
| fIsRect = !fIsRect; | |
| this->inval(NULL); | |
| return true; | |
| case 'o': | |
| fFB.toggleRectAsOval(); | |
| this->inval(NULL); | |
| return true; | |
| case 'x': | |
| fFB.setGrid(!fFB.getGrid()); | |
| this->inval(NULL); | |
| return true; | |
| case 's': | |
| if (FatBits::kStroke_Style == fFB.getStyle()) { | |
| this->setStyle(FatBits::kHair_Style); | |
| } else { | |
| this->setStyle(FatBits::kStroke_Style); | |
| } | |
| return true; | |
| case 'a': | |
| fFB.setAA(!fFB.getAA()); | |
| this->inval(NULL); | |
| return true; | |
| case 'w': | |
| fFB.setShowSkeleton(!fFB.getShowSkeleton()); | |
| this->inval(NULL); | |
| return true; | |
| case 'g': | |
| fFB.setUseGPU(!fFB.getUseGPU()); | |
| this->inval(NULL); | |
| return true; | |
| case 't': | |
| fFB.setTriangle(!fFB.getTriangle()); | |
| this->inval(NULL); | |
| return true; | |
| } | |
| } | |
| return this->INHERITED::onQuery(evt); | |
| } | |
| virtual void onDrawContent(SkCanvas* canvas) { | |
| 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 %s", | |
| fFB.getAA() ? "AA" : "BW", | |
| FatBits::kHair_Style == fFB.getStyle() ? "Hair" : "Stroke", | |
| fFB.getUseGPU() ? "GPU" : "CPU", | |
| fFB.getUseClip() ? "clip" : "noclip"); | |
| SkPaint paint; | |
| paint.setAntiAlias(true); | |
| paint.setTextSize(16); | |
| paint.setColor(SK_ColorBLUE); | |
| canvas->drawText(str.c_str(), str.size(), 10, 16, paint); | |
| } | |
| } | |
| virtual SkView::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 (fPts[i].equalsWithinTolerance(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); | |
| } | |
| this->inval(NULL); | |
| return true; | |
| } | |
| private: | |
| typedef SampleView INHERITED; | |
| }; | |
| ////////////////////////////////////////////////////////////////////////////// | |
| static SkView* MyFactory() { return new DrawLineView; } | |
| static SkViewRegister reg(MyFactory); |