diff options
author | 2011-10-05 19:57:55 +0000 | |
---|---|---|
committer | 2011-10-05 19:57:55 +0000 | |
commit | fa6ac938e64fe11b442d05fe8a90ddac2d1951f9 (patch) | |
tree | 09e1bfdb59c0549a9cff72d1551376a25e262ce7 | |
parent | 322878907f6c5c5fb8abdbce7d348a3cd66ff2fa (diff) |
Fixup rendering of empty paths (including inverted fills)
Add GM and Sample that draw empty paths with various styles and fills
Review URL: http://codereview.appspot.com/5185047/
git-svn-id: http://skia.googlecode.com/svn/trunk@2414 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r-- | gm/emptypath.cpp | 138 | ||||
-rw-r--r-- | gpu/include/GrTypes.h | 4 | ||||
-rw-r--r-- | gpu/src/GrContext.cpp | 12 | ||||
-rw-r--r-- | gpu/src/GrGpu.cpp | 4 | ||||
-rw-r--r-- | gpu/src/GrTesselatedPathRenderer.cpp | 2 | ||||
-rw-r--r-- | gyp/SampleApp.gyp | 1 | ||||
-rw-r--r-- | gyp/gm.gyp | 1 | ||||
-rw-r--r-- | include/core/SkCanvas.h | 2 | ||||
-rw-r--r-- | samplecode/SampleEmptyPath.cpp | 130 | ||||
-rw-r--r-- | src/core/SkCanvas.cpp | 12 |
10 files changed, 299 insertions, 7 deletions
diff --git a/gm/emptypath.cpp b/gm/emptypath.cpp new file mode 100644 index 0000000000..dd33f68742 --- /dev/null +++ b/gm/emptypath.cpp @@ -0,0 +1,138 @@ + +/* + * 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 "gm.h" +#include "SkCanvas.h" +#include "SkPaint.h" +#include "SkRandom.h" + +namespace skiagm { + +class EmptyPathGM : public GM { +public: + EmptyPathGM() {} + +protected: + SkString onShortName() { + return SkString("emptypath"); + } + + SkISize onISize() { return make_isize(600, 280); } + + void drawBG(SkCanvas* canvas) { + canvas->drawColor(SK_ColorWHITE); + } + + void drawEmpty(SkCanvas* canvas, + SkColor color, + const SkRect& clip, + SkPaint::Style style, + SkPath::FillType fill) { + SkPath path; + path.setFillType(fill); + SkPaint paint; + paint.setColor(color); + paint.setStyle(style); + canvas->save(); + canvas->clipRect(clip); + canvas->drawPath(path, paint); + canvas->restore(); + } + + virtual void onDraw(SkCanvas* canvas) { + this->drawBG(canvas); + struct FillAndName { + SkPath::FillType fFill; + const char* fName; + }; + static const FillAndName gFills[] = { + {SkPath::kWinding_FillType, "Winding"}, + {SkPath::kEvenOdd_FillType, "Even / Odd"}, + {SkPath::kInverseWinding_FillType, "Inverse Winding"}, + {SkPath::kInverseEvenOdd_FillType, "Inverse Even / Odd"}, + }; + struct StyleAndName { + SkPaint::Style fStyle; + const char* fName; + }; + static const StyleAndName gStyles[] = { + {SkPaint::kFill_Style, "Fill"}, + {SkPaint::kStroke_Style, "Stroke"}, + {SkPaint::kStrokeAndFill_Style, "Stroke And Fill"}, + }; + + SkPaint titlePaint; + titlePaint.setColor(SK_ColorBLACK); + titlePaint.setAntiAlias(true); + titlePaint.setLCDRenderText(true); + titlePaint.setTextSize(15 * SK_Scalar1); + const char title[] = "Empty Paths Drawn Into Rectangle Clips With " + "Indicated Style and Fill"; + canvas->drawText(title, strlen(title), + 20 * SK_Scalar1, + 20 * SK_Scalar1, + titlePaint); + + SkRandom rand; + SkRect rect = SkRect::MakeWH(100*SK_Scalar1, 30*SK_Scalar1); + int i = 0; + canvas->save(); + canvas->translate(10 * SK_Scalar1, 0); + canvas->save(); + for (int style = 0; style < SK_ARRAY_COUNT(gStyles); ++style) { + for (int fill = 0; fill < SK_ARRAY_COUNT(gFills); ++fill) { + if (0 == i % 4) { + canvas->restore(); + canvas->translate(0, rect.height() + 40 * SK_Scalar1); + canvas->save(); + } else { + canvas->translate(rect.width() + 40 * SK_Scalar1, 0); + } + ++i; + + + SkColor color = rand.nextU(); + color = 0xff000000| color; // force solid + this->drawEmpty(canvas, color, rect, + gStyles[style].fStyle, gFills[fill].fFill); + + SkPaint rectPaint; + rectPaint.setColor(SK_ColorBLACK); + rectPaint.setStyle(SkPaint::kStroke_Style); + rectPaint.setStrokeWidth(-1); + rectPaint.setAntiAlias(true); + canvas->drawRect(rect, rectPaint); + + SkPaint labelPaint; + labelPaint.setColor(color); + labelPaint.setAntiAlias(true); + labelPaint.setLCDRenderText(true); + labelPaint.setTextSize(12 * SK_Scalar1); + canvas->drawText(gStyles[style].fName, + strlen(gStyles[style].fName), + 0, rect.height() + 15 * SK_Scalar1, + labelPaint); + canvas->drawText(gFills[fill].fName, + strlen(gFills[fill].fName), + 0, rect.height() + 28 * SK_Scalar1, + labelPaint); + } + } + canvas->restore(); + canvas->restore(); + } + +private: + typedef GM INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static GM* MyFactory(void*) { return new EmptyPathGM; } +static GMRegistry reg(MyFactory); + +} diff --git a/gpu/include/GrTypes.h b/gpu/include/GrTypes.h index af43c1f540..8bbdecf7dc 100644 --- a/gpu/include/GrTypes.h +++ b/gpu/include/GrTypes.h @@ -435,7 +435,7 @@ enum GrPathFill { kPathFillCount }; -static inline GrPathFill NonInvertedFill(GrPathFill fill) { +static inline GrPathFill GrNonInvertedFill(GrPathFill fill) { static const GrPathFill gNonInvertedFills[] = { kWinding_PathFill, // kWinding_PathFill kEvenOdd_PathFill, // kEvenOdd_PathFill @@ -452,7 +452,7 @@ static inline GrPathFill NonInvertedFill(GrPathFill fill) { return gNonInvertedFills[fill]; } -static inline bool IsFillInverted(GrPathFill fill) { +static inline bool GrIsFillInverted(GrPathFill fill) { static const bool gIsFillInverted[] = { false, // kWinding_PathFill false, // kEvenOdd_PathFill diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp index e0c7b5559f..d9fb9e85c8 100644 --- a/gpu/src/GrContext.cpp +++ b/gpu/src/GrContext.cpp @@ -1445,6 +1445,16 @@ void GrContext::drawVertices(const GrPaint& paint, void GrContext::drawPath(const GrPaint& paint, const GrPath& path, GrPathFill fill, const GrPoint* translate) { + if (path.isEmpty()) { +#if GR_DEBUG + GrPrintf("Empty path should have been caught by canvas.\n"); +#endif + if (GrIsFillInverted(fill)) { + this->drawPaint(paint); + } + return; + } + GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory); // An Assumption here is that path renderer would use some form of tweaking @@ -1503,7 +1513,7 @@ void GrContext::drawPath(const GrPaint& paint, const GrPath& path, } } this->cleanupOffscreenAA(target, pr, &record); - if (IsFillInverted(fill) && bound != clipIBounds) { + if (GrIsFillInverted(fill) && bound != clipIBounds) { GrDrawTarget::AutoDeviceCoordDraw adcd(target, stageMask); GrRect rect; if (clipIBounds.fTop < bound.fTop) { diff --git a/gpu/src/GrGpu.cpp b/gpu/src/GrGpu.cpp index 7b98ffa0b0..850fa60c4e 100644 --- a/gpu/src/GrGpu.cpp +++ b/gpu/src/GrGpu.cpp @@ -596,8 +596,8 @@ bool GrGpu::setupClipAndFlushState(GrPrimitiveType type) { fillInverted = false; } else { fill = clip.getPathFill(c); - fillInverted = IsFillInverted(fill); - fill = NonInvertedFill(fill); + fillInverted = GrIsFillInverted(fill); + fill = GrNonInvertedFill(fill); clipPath = &clip.getPath(c); pr = this->getClipPathRenderer(*clipPath, fill); if (NULL == pr) { diff --git a/gpu/src/GrTesselatedPathRenderer.cpp b/gpu/src/GrTesselatedPathRenderer.cpp index 5d544f2912..3c4bb0100c 100644 --- a/gpu/src/GrTesselatedPathRenderer.cpp +++ b/gpu/src/GrTesselatedPathRenderer.cpp @@ -366,7 +366,7 @@ void GrTesselatedPathRenderer::drawPath(GrDrawTarget::StageBitfield stages) { } } - bool inverted = IsFillInverted(fFill); + bool inverted = GrIsFillInverted(fFill); if (inverted) { maxPts += 4; subpathCnt++; diff --git a/gyp/SampleApp.gyp b/gyp/SampleApp.gyp index fd84893f62..dfd1e4c352 100644 --- a/gyp/SampleApp.gyp +++ b/gyp/SampleApp.gyp @@ -59,6 +59,7 @@ '../samplecode/SampleDrawLooper.cpp', '../samplecode/SampleEffects.cpp', '../samplecode/SampleEmboss.cpp', + '../samplecode/SampleEmptyPath.cpp', '../samplecode/SampleEncode.cpp', '../samplecode/SampleExtractAlpha.cpp', '../samplecode/SampleFillType.cpp', diff --git a/gyp/gm.gyp b/gyp/gm.gyp index f0aeb64714..6226546b3f 100644 --- a/gyp/gm.gyp +++ b/gyp/gm.gyp @@ -14,6 +14,7 @@ '../gm/blurs.cpp', '../gm/complexclip.cpp', '../gm/complexclip2.cpp', + '../gm/emptypath.cpp', '../gm/filltypes.cpp', '../gm/filltypespersp.cpp', '../gm/gmmain.cpp', diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h index a6c95285b6..6ca9fa08a9 100644 --- a/include/core/SkCanvas.h +++ b/include/core/SkCanvas.h @@ -854,6 +854,8 @@ private: const SkRect& dst, const SkPaint* paint); void internalDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst, const SkPaint* paint); + void internalDrawPaint(const SkPaint& paint); + void drawDevice(SkDevice*, int x, int y, const SkPaint*); // shared by save() and saveLayer() diff --git a/samplecode/SampleEmptyPath.cpp b/samplecode/SampleEmptyPath.cpp new file mode 100644 index 0000000000..5c865c66fd --- /dev/null +++ b/samplecode/SampleEmptyPath.cpp @@ -0,0 +1,130 @@ + +/* + * 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 "SkView.h" +#include "SkCanvas.h" +#include "SkPaint.h" +#include "SkRandom.h" + +class EmptyPathView : public SampleView { +public: + EmptyPathView() {} + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "EmptyPath"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + void drawEmpty(SkCanvas* canvas, + SkColor color, + const SkRect& clip, + SkPaint::Style style, + SkPath::FillType fill) { + SkPath path; + path.setFillType(fill); + SkPaint paint; + paint.setColor(color); + paint.setStyle(style); + canvas->save(); + canvas->clipRect(clip); + canvas->drawPath(path, paint); + canvas->restore(); + } + + virtual void onDrawContent(SkCanvas* canvas) { + struct FillAndName { + SkPath::FillType fFill; + const char* fName; + }; + static const FillAndName gFills[] = { + {SkPath::kWinding_FillType, "Winding"}, + {SkPath::kEvenOdd_FillType, "Even / Odd"}, + {SkPath::kInverseWinding_FillType, "Inverse Winding"}, + {SkPath::kInverseEvenOdd_FillType, "Inverse Even / Odd"}, + }; + struct StyleAndName { + SkPaint::Style fStyle; + const char* fName; + }; + static const StyleAndName gStyles[] = { + {SkPaint::kFill_Style, "Fill"}, + {SkPaint::kStroke_Style, "Stroke"}, + {SkPaint::kStrokeAndFill_Style, "Stroke And Fill"}, + }; + + SkPaint titlePaint; + titlePaint.setColor(SK_ColorBLACK); + titlePaint.setAntiAlias(true); + titlePaint.setLCDRenderText(true); + titlePaint.setTextSize(24 * SK_Scalar1); + const char title[] = "Empty Paths Drawn Into Rectangle Clips With Indicated Style and Fill"; + canvas->drawText(title, strlen(title), + 40 * SK_Scalar1, + 100*SK_Scalar1, + titlePaint); + + SkRandom rand; + SkRect rect = SkRect::MakeWH(125*SK_Scalar1, 100*SK_Scalar1); + int i = 0; + canvas->save(); + canvas->translate(80 * SK_Scalar1, 0); + canvas->save(); + for (int style = 0; style < SK_ARRAY_COUNT(gStyles); ++style) { + for (int fill = 0; fill < SK_ARRAY_COUNT(gFills); ++fill) { + if (0 == i % 4) { + canvas->restore(); + canvas->translate(0, rect.height() + 50 * SK_Scalar1); + canvas->save(); + } else { + canvas->translate(rect.width() + 100 * SK_Scalar1, 0); + } + ++i; + + + SkColor color = rand.nextU(); + color = 0xff000000| color; // force solid + this->drawEmpty(canvas, color, rect, + gStyles[style].fStyle, gFills[fill].fFill); + + SkPaint rectPaint; + rectPaint.setColor(SK_ColorBLACK); + rectPaint.setStyle(SkPaint::kStroke_Style); + rectPaint.setStrokeWidth(-1); + rectPaint.setAntiAlias(true); + canvas->drawRect(rect, rectPaint); + + char label[1024]; + sprintf(label, "%s, %s", gStyles[style].fName, + gFills[fill].fName); + SkPaint labelPaint; + labelPaint.setColor(color); + labelPaint.setAntiAlias(true); + labelPaint.setLCDRenderText(true); + canvas->drawText(label, strlen(label), + 0, rect.height() + 15 * SK_Scalar1, + labelPaint); + } + } + canvas->restore(); + canvas->restore(); + } + +private: + typedef SampleView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new EmptyPathView; } +static SkViewRegister reg(MyFactory); + diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp index 3ee13bccc5..70b53fe994 100644 --- a/src/core/SkCanvas.cpp +++ b/src/core/SkCanvas.cpp @@ -1217,6 +1217,10 @@ void SkCanvas::clear(SkColor color) { } void SkCanvas::drawPaint(const SkPaint& paint) { + this->internalDrawPaint(paint); +} + +void SkCanvas::internalDrawPaint(const SkPaint& paint) { CHECK_NOTHING_TO_DRAW(paint); LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type) @@ -1269,7 +1273,7 @@ void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) { void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) { CHECK_NOTHING_TO_DRAW(paint); - if (paint.canComputeFastBounds()) { + if (!path.isInverseFillType() && paint.canComputeFastBounds()) { SkRect storage; const SkRect& bounds = path.getBounds(); if (this->quickReject(paint.computeFastBounds(bounds, &storage), @@ -1277,6 +1281,12 @@ void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) { return; } } + if (path.isEmpty()) { + if (path.isInverseFillType()) { + this->internalDrawPaint(paint); + } + return; + } LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type) |