diff options
-rw-r--r-- | samplecode/SamplePathClip.cpp | 85 | ||||
-rw-r--r-- | src/core/SkEdge.cpp | 14 | ||||
-rw-r--r-- | src/core/SkEdge.h | 2 | ||||
-rw-r--r-- | src/core/SkQuadClipper.cpp | 92 | ||||
-rw-r--r-- | src/core/SkQuadClipper.h | 41 | ||||
-rw-r--r-- | src/core/SkScan_Path.cpp | 23 | ||||
-rw-r--r-- | src/core/core_files.mk | 1 | ||||
-rw-r--r-- | xcode/core/core.xcodeproj/project.pbxproj | 4 | ||||
-rw-r--r-- | xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj | 4 |
9 files changed, 248 insertions, 18 deletions
diff --git a/samplecode/SamplePathClip.cpp b/samplecode/SamplePathClip.cpp new file mode 100644 index 0000000000..f4dd032d13 --- /dev/null +++ b/samplecode/SamplePathClip.cpp @@ -0,0 +1,85 @@ +#include "SampleCode.h" +#include "SkView.h" +#include "SkCanvas.h" +#include "SkGradientShader.h" +#include "SkGraphics.h" +#include "SkImageDecoder.h" +#include "SkPath.h" +#include "SkPorterDuff.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkXfermode.h" +#include "SkColorPriv.h" +#include "SkColorFilter.h" +#include "SkTime.h" +#include "SkTypeface.h" + +class PathClipView : public SkView { +public: + SkRect fOval; + SkPoint fCenter; + + PathClipView() { + fOval.set(0, 0, SkIntToScalar(200), SkIntToScalar(50)); + fCenter.set(SkIntToScalar(250), SkIntToScalar(250)); + } + + virtual ~PathClipView() {} + +protected: + // overrides from SkEventSink + virtual bool onQuery(SkEvent* evt) { + if (SampleCode::TitleQ(*evt)) { + SampleCode::TitleR(evt, "PathClip"); + return true; + } + return this->INHERITED::onQuery(evt); + } + + void drawBG(SkCanvas* canvas) { + canvas->drawColor(SK_ColorWHITE); + } + + virtual void onDraw(SkCanvas* canvas) { + this->drawBG(canvas); + + SkRect oval = fOval; + oval.offset(fCenter.fX - oval.centerX(), fCenter.fY - oval.centerY()); + + SkPaint p; + p.setAntiAlias(true); + + p.setStyle(SkPaint::kStroke_Style); + canvas->drawOval(oval, p); + + SkRect r; + r.set(SkIntToScalar(200), SkIntToScalar(200), + SkIntToScalar(300), SkIntToScalar(300)); + canvas->clipRect(r); + + p.setStyle(SkPaint::kFill_Style); + p.setColor(SK_ColorRED); + canvas->drawRect(r, p); + + p.setColor(0x800000FF); + r.set(SkIntToScalar(150), SkIntToScalar(10), + SkIntToScalar(250), SkIntToScalar(400)); + canvas->drawOval(oval, p); + } + + virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) { + fCenter.set(x, y); + this->inval(NULL); + return NULL; + } + +private: + typedef SkView INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static SkView* MyFactory() { return new PathClipView; } +static SkViewRegister reg(MyFactory); + diff --git a/src/core/SkEdge.cpp b/src/core/SkEdge.cpp index 6efe1bad39..f790d02a8b 100644 --- a/src/core/SkEdge.cpp +++ b/src/core/SkEdge.cpp @@ -173,7 +173,7 @@ static inline int diff_to_shift(SkFDot6 dx, SkFDot6 dy) return (32 - SkCLZ(dist)) >> 1; } -int SkQuadraticEdge::setQuadratic(const SkPoint pts[3], const SkIRect* clip, int shift) +int SkQuadraticEdge::setQuadratic(const SkPoint pts[3], int shift) { SkFDot6 x0, y0, x1, y1, x2, y2; @@ -212,9 +212,6 @@ int SkQuadraticEdge::setQuadratic(const SkPoint pts[3], const SkIRect* clip, int // are we a zero-height quad (line)? if (top == bot) return 0; - // are we completely above or below the clip? - if (clip && (top >= clip->fBottom || bot <= clip->fTop)) - return 0; // compute number of steps needed (1 << shift) { @@ -252,15 +249,6 @@ int SkQuadraticEdge::setQuadratic(const SkPoint pts[3], const SkIRect* clip, int fQLastX = SkFDot6ToFixed(x2); fQLastY = SkFDot6ToFixed(y2); - if (clip) - { - do { - for (;!this->updateQuadratic();) - ; - } while (!this->intersectsClip(*clip)); - this->chopLineWithClip(*clip); - return 1; - } return this->updateQuadratic(); } diff --git a/src/core/SkEdge.h b/src/core/SkEdge.h index 5b0cc75d4c..eb50a4245a 100644 --- a/src/core/SkEdge.h +++ b/src/core/SkEdge.h @@ -75,7 +75,7 @@ struct SkQuadraticEdge : public SkEdge { SkFixed fQDDx, fQDDy; SkFixed fQLastX, fQLastY; - int setQuadratic(const SkPoint pts[3], const SkIRect* clip, int shiftUp); + int setQuadratic(const SkPoint pts[3], int shiftUp); int updateQuadratic(); }; diff --git a/src/core/SkQuadClipper.cpp b/src/core/SkQuadClipper.cpp new file mode 100644 index 0000000000..c6add69745 --- /dev/null +++ b/src/core/SkQuadClipper.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SkQuadClipper.h" +#include "SkGeometry.h" + +static bool chopMonoQuadAtY(SkPoint pts[3], SkScalar y, SkScalar* t) { + /* Solve F(t) = y where F(t) := [0](1-t)^2 + 2[1]t(1-t) + [2]t^2 + * We solve for t, using quadratic equation, hence we have to rearrange + * our cooefficents to look like At^2 + Bt + C + */ + SkScalar A = pts[0].fY - pts[1].fY - pts[1].fY + pts[2].fY; + SkScalar B = 2*(pts[1].fY - pts[0].fY); + SkScalar C = pts[0].fY - y; + + SkScalar roots[2]; // we only expect one, but make room for 2 for safety + int count = SkFindUnitQuadRoots(A, B, C, roots); + if (count) { + *t = roots[0]; + return true; + } + return false; +} + +SkQuadClipper::SkQuadClipper() {} + +void SkQuadClipper::setClip(const SkIRect& clip) { + // conver to scalars, since that's where we'll see the points + fClip.set(clip); +} + +/* If we somehow returned the fact that we had to flip the pts in Y, we could + communicate that to setQuadratic, and then avoid having to flip it back + here (only to have setQuadratic do the flip again) + */ +bool SkQuadClipper::clipQuad(const SkPoint srcPts[3], SkPoint dst[3]) { + bool reverse; + + // we need the data to be monotonically descending in Y + if (srcPts[0].fY > srcPts[2].fY) { + dst[0] = srcPts[2]; + dst[1] = srcPts[1]; + dst[2] = srcPts[0]; + reverse = true; + } else { + memcpy(dst, srcPts, 3 * sizeof(SkPoint)); + reverse = false; + } + + // are we completely above or below + const SkScalar ctop = fClip.fTop; + const SkScalar cbot = fClip.fBottom; + if (dst[2].fY <= ctop || dst[0].fY >= cbot) { + return false; + } + + SkScalar t; + SkPoint tmp[5]; // for SkChopQuadAt + + // are we partially above + if (dst[0].fY < ctop && chopMonoQuadAtY(dst, ctop, &t)) { + SkChopQuadAt(dst, tmp, t); + dst[0] = tmp[2]; + dst[1] = tmp[3]; + } + + // are we partially below + if (dst[2].fY > cbot && chopMonoQuadAtY(dst, cbot, &t)) { + SkChopQuadAt(dst, tmp, t); + dst[1] = tmp[1]; + dst[2] = tmp[2]; + } + + if (reverse) { + SkTSwap<SkPoint>(dst[0], dst[2]); + } + return true; +} + diff --git a/src/core/SkQuadClipper.h b/src/core/SkQuadClipper.h new file mode 100644 index 0000000000..fcb230f7fa --- /dev/null +++ b/src/core/SkQuadClipper.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkQuadClipper_DEFINED +#define SkQuadClipper_DEFINED + +#include "SkPoint.h" +#include "SkRect.h" + +/** This class is initialized with a clip rectangle, and then can be fed quads, + which must already be monotonic in Y. + + In the future, it might return a series of segments, allowing it to clip + also in X, to ensure that all segments fit in a finite coordinate system. + */ +class SkQuadClipper { +public: + SkQuadClipper(); + + void setClip(const SkIRect& clip); + + bool clipQuad(const SkPoint src[3], SkPoint dst[3]); + +private: + SkRect fClip; +}; + +#endif diff --git a/src/core/SkScan_Path.cpp b/src/core/SkScan_Path.cpp index d8f779a32c..fcf1530c80 100644 --- a/src/core/SkScan_Path.cpp +++ b/src/core/SkScan_Path.cpp @@ -20,6 +20,7 @@ #include "SkEdge.h" #include "SkGeometry.h" #include "SkPath.h" +#include "SkQuadClipper.h" #include "SkRegion.h" #include "SkTemplates.h" @@ -306,6 +307,14 @@ static int build_edges(SkEdge edge[], const SkPath& path, SkPath::Iter iter(path, true); SkPoint pts[4]; SkPath::Verb verb; + + SkQuadClipper qclipper; + if (clipRect) { + SkIRect r; + r.set(clipRect->fLeft >> shiftUp, clipRect->fTop >> shiftUp, + clipRect->fRight >> shiftUp, clipRect->fBottom >> shiftUp); + qclipper.setClip(r); + } while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { switch (verb) { @@ -316,17 +325,23 @@ static int build_edges(SkEdge edge[], const SkPath& path, } break; case SkPath::kQuad_Verb: { - SkPoint tmp[5]; + SkPoint tmp[5], clippedPts[3]; SkPoint* p = tmp; int count = SkChopQuadAtYExtrema(pts, tmp); do { - if (((SkQuadraticEdge*)edge)->setQuadratic(p, clipRect, - shiftUp)) - { + const SkPoint* qpts = p; + if (clipRect) { + if (!qclipper.clipQuad(p, clippedPts)) { + goto NEXT_CHOPPED_QUAD; + } + qpts = clippedPts; + } + if (((SkQuadraticEdge*)edge)->setQuadratic(qpts, shiftUp)) { *list++ = edge; edge = (SkEdge*)((char*)edge + sizeof(SkQuadraticEdge)); } + NEXT_CHOPPED_QUAD: p += 2; } while (--count >= 0); break; diff --git a/src/core/core_files.mk b/src/core/core_files.mk index 93a68ae8b8..0200242239 100644 --- a/src/core/core_files.mk +++ b/src/core/core_files.mk @@ -58,6 +58,7 @@ SOURCE := \ SkPixelRef.cpp \ SkPoint.cpp \ SkPtrRecorder.cpp \ + SkQuadClipper.cpp \ SkRasterizer.cpp \ SkRect.cpp \ SkRefCnt.cpp \ diff --git a/xcode/core/core.xcodeproj/project.pbxproj b/xcode/core/core.xcodeproj/project.pbxproj index 2e8b069f0d..74a431ab18 100644 --- a/xcode/core/core.xcodeproj/project.pbxproj +++ b/xcode/core/core.xcodeproj/project.pbxproj @@ -125,6 +125,7 @@ 005F25E60EF94F7900582A90 /* SkWriter32.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F256D0EF94F7900582A90 /* SkWriter32.cpp */; }; 005F25E70EF94F7900582A90 /* SkXfermode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F256E0EF94F7900582A90 /* SkXfermode.cpp */; }; 005F26960EF955D400582A90 /* SkComposeShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 005F26950EF955D400582A90 /* SkComposeShader.cpp */; }; + 007C786A0F3B4D5F0004B142 /* SkQuadClipper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 007C78690F3B4D5F0004B142 /* SkQuadClipper.cpp */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -246,6 +247,7 @@ 005F256D0EF94F7900582A90 /* SkWriter32.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkWriter32.cpp; path = ../../src/core/SkWriter32.cpp; sourceTree = SOURCE_ROOT; }; 005F256E0EF94F7900582A90 /* SkXfermode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkXfermode.cpp; path = ../../src/core/SkXfermode.cpp; sourceTree = SOURCE_ROOT; }; 005F26950EF955D400582A90 /* SkComposeShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkComposeShader.cpp; path = ../../src/core/SkComposeShader.cpp; sourceTree = SOURCE_ROOT; }; + 007C78690F3B4D5F0004B142 /* SkQuadClipper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkQuadClipper.cpp; path = ../../src/core/SkQuadClipper.cpp; sourceTree = SOURCE_ROOT; }; D2AAC046055464E500DB518D /* libcore.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libcore.a; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -273,6 +275,7 @@ 08FB7795FE84155DC02AAC07 /* src */ = { isa = PBXGroup; children = ( + 007C78690F3B4D5F0004B142 /* SkQuadClipper.cpp */, 002884D40EFAB8F80083E387 /* SkStream.cpp */, 002884C70EFAB8B90083E387 /* SkMMapStream.cpp */, 005F26950EF955D400582A90 /* SkComposeShader.cpp */, @@ -580,6 +583,7 @@ 005F26960EF955D400582A90 /* SkComposeShader.cpp in Sources */, 002884C80EFAB8B90083E387 /* SkMMapStream.cpp in Sources */, 002884D50EFAB8F80083E387 /* SkStream.cpp in Sources */, + 007C786A0F3B4D5F0004B142 /* SkQuadClipper.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj b/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj index 1df5c61f0e..5ecdcc28a6 100644 --- a/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj +++ b/xcode/sampleapp/SampleApp.xcodeproj/project.pbxproj @@ -64,6 +64,7 @@ 007A7CBF0F01658C00A2D6EE /* SampleTypeface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 007A7CB00F01658C00A2D6EE /* SampleTypeface.cpp */; }; 007A7CC00F01658C00A2D6EE /* SampleVertices.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 007A7CB10F01658C00A2D6EE /* SampleVertices.cpp */; }; 007A7CC10F01658C00A2D6EE /* SampleXfermodes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 007A7CB20F01658C00A2D6EE /* SampleXfermodes.cpp */; }; + 007C785E0F3B4C230004B142 /* SamplePathClip.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 007C785D0F3B4C230004B142 /* SamplePathClip.cpp */; }; 00A41E4B0EFC312F00C9CBEB /* SampleArc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00A41E4A0EFC312F00C9CBEB /* SampleArc.cpp */; }; 0156F80407C56A3000C6122B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0156F80307C56A3000C6122B /* Foundation.framework */; }; 01FC44D507BD3BB800D228F4 /* Quartz.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 01FC44D407BD3BB800D228F4 /* Quartz.framework */; }; @@ -176,6 +177,7 @@ 007A7CB00F01658C00A2D6EE /* SampleTypeface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleTypeface.cpp; path = ../../samplecode/SampleTypeface.cpp; sourceTree = SOURCE_ROOT; }; 007A7CB10F01658C00A2D6EE /* SampleVertices.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleVertices.cpp; path = ../../samplecode/SampleVertices.cpp; sourceTree = SOURCE_ROOT; }; 007A7CB20F01658C00A2D6EE /* SampleXfermodes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleXfermodes.cpp; path = ../../samplecode/SampleXfermodes.cpp; sourceTree = SOURCE_ROOT; }; + 007C785D0F3B4C230004B142 /* SamplePathClip.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SamplePathClip.cpp; path = ../../samplecode/SamplePathClip.cpp; sourceTree = SOURCE_ROOT; }; 00A41E4A0EFC312F00C9CBEB /* SampleArc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleArc.cpp; path = ../../samplecode/SampleArc.cpp; sourceTree = SOURCE_ROOT; }; 0156F80307C56A3000C6122B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; }; 01FC44D407BD3BB800D228F4 /* Quartz.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quartz.framework; path = /System/Library/Frameworks/Quartz.framework; sourceTree = "<absolute>"; }; @@ -246,6 +248,7 @@ 00A41E4A0EFC312F00C9CBEB /* SampleArc.cpp */, 00003C620EFC22A8000FF73A /* SampleApp.cpp */, 00003C640EFC22A8000FF73A /* SamplePath.cpp */, + 007C785D0F3B4C230004B142 /* SamplePathClip.cpp */, 00003C650EFC22A8000FF73A /* SamplePathEffects.cpp */, ); name = samples; @@ -505,6 +508,7 @@ 007A7CC10F01658C00A2D6EE /* SampleXfermodes.cpp in Sources */, 0041CE3C0F00A12400695E8C /* SampleEncode.cpp in Sources */, 007A7CB60F01658C00A2D6EE /* SampleRegion.cpp in Sources */, + 007C785E0F3B4C230004B142 /* SamplePathClip.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; |