diff options
author | commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-07-31 22:54:31 +0000 |
---|---|---|
committer | commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-07-31 22:54:31 +0000 |
commit | 92ffe7d10ef5db05f1f4ffef0cfe898169ba13bf (patch) | |
tree | c393c212095f188b2daf1d556c5a9e86fbda4897 | |
parent | 1958e912e1626d6b5f00976700fbe9d91f9c2a05 (diff) |
Inverse fill support in PDF
BUG= https://code.google.com/p/skia/issues/detail?id=241 (partial fix)
R=edisonn@google.com, vandebo@chromium.org, reed@google.com
Author: richardlin@chromium.org
Review URL: https://chromiumcodereview.appspot.com/19519017
git-svn-id: http://skia.googlecode.com/svn/trunk@10476 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r-- | gm/inversepaths.cpp | 146 | ||||
-rw-r--r-- | gyp/gmslides.gypi | 1 | ||||
-rw-r--r-- | include/config/SkUserConfig.h | 10 | ||||
-rw-r--r-- | include/pdf/SkPDFDevice.h | 2 | ||||
-rw-r--r-- | src/pdf/SkPDFDevice.cpp | 81 |
5 files changed, 240 insertions, 0 deletions
diff --git a/gm/inversepaths.cpp b/gm/inversepaths.cpp new file mode 100644 index 0000000000..dc2b636119 --- /dev/null +++ b/gm/inversepaths.cpp @@ -0,0 +1,146 @@ +/* + * Copyright 2013 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 "SkPath.h" + +namespace skiagm { + +static SkPath generate_square(SkScalar cx, SkScalar cy, SkScalar w) { + SkRect rect = SkRect::MakeXYWH(cx - w / 2, cy - w / 2, w, w); + SkPath path; + path.addRect(rect); + return path; +} + +static SkPath generate_rect_line(SkScalar cx, SkScalar cy, SkScalar l) { + SkRect rect = SkRect::MakeXYWH(cx - l / 2, cy, l, 0); + SkPath path; + path.addRect(rect); + return path; +} + +static SkPath generate_circle(SkScalar cx, SkScalar cy, SkScalar d) { + SkPath path; + path.addCircle(cx, cy, d/2, SkPath::kCW_Direction); + return path; +} + +static SkPath generate_line(SkScalar cx, SkScalar cy, SkScalar l) { + SkPath path; + path.moveTo(cx - l / 2, cy); + path.lineTo(cx + l / 2, cy); + return path; +} + +SkPaint::Style styles[] = { + SkPaint::kStroke_Style, + SkPaint::kStrokeAndFill_Style, + SkPaint::kFill_Style +}; +SkScalar pathSizes[] = { + 40, + 10, + 0 +}; +SkScalar strokeWidths[] = { + 10, + 0 +}; +SkPath ((*paths[])(SkScalar, SkScalar, SkScalar)) = { + generate_square, + generate_rect_line, + generate_circle, + generate_line +}; + +const SkScalar slideWidth = 90, slideHeight = 90; +const SkScalar slideBoundary = 5; + + +class InversePathsGM : public GM { +public: + InversePathsGM() { + + } + +protected: + virtual SkString onShortName() { + return SkString("inverse_paths"); + } + + virtual SkISize onISize() { + return make_isize(800, 900); + } + + virtual void onDraw(SkCanvas* canvas) { + SkScalar cx = slideWidth / 2 + slideBoundary; + SkScalar cy = slideHeight / 2 + slideBoundary; + SkScalar dx = slideWidth + 2 * slideBoundary; + SkScalar dy = slideHeight + 2 * slideBoundary; + + SkRect clipRect = SkRect::MakeLTRB(slideBoundary, slideBoundary, + slideBoundary + slideWidth, + slideBoundary + slideHeight); + SkPaint clipPaint; + clipPaint.setStyle(SkPaint::kStroke_Style); + clipPaint.setStrokeWidth(SkIntToScalar(2)); + + SkPaint outlinePaint; + outlinePaint.setColor(0x40000000); + outlinePaint.setStyle(SkPaint::kStroke_Style); + outlinePaint.setStrokeWidth(SkIntToScalar(0)); + + for (size_t styleIndex = 0; styleIndex < SK_ARRAY_COUNT(styles); + styleIndex++) { + for (size_t sizeIndex = 0; sizeIndex < SK_ARRAY_COUNT(pathSizes); + sizeIndex++) { + SkScalar size = pathSizes[sizeIndex]; + + canvas->save(); + + for (size_t widthIndex = 0; + widthIndex < SK_ARRAY_COUNT(strokeWidths); + widthIndex++) { + SkPaint paint; + paint.setColor(0xff007000); + paint.setStrokeWidth(strokeWidths[widthIndex]); + paint.setStyle(styles[styleIndex]); + + for (size_t pathIndex = 0; + pathIndex < SK_ARRAY_COUNT(paths); + pathIndex++) { + canvas->drawRect(clipRect, clipPaint); + + canvas->save(); + canvas->clipRect(clipRect); + + SkPath path = paths[pathIndex](cx, cy, size); + path.setFillType(SkPath::kInverseWinding_FillType); + canvas->drawPath(path, paint); + + path.setFillType(SkPath::kWinding_FillType); + canvas->drawPath(path, outlinePaint); + + canvas->restore(); + canvas->translate(dx, 0); + } + } + + canvas->restore(); + canvas->translate(0, dy); + } + } + } + +private: + typedef GM INHERITED; +}; + +DEF_GM( return new InversePathsGM; ) +} diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi index 3af7d73632..cb88389fe6 100644 --- a/gyp/gmslides.gypi +++ b/gyp/gmslides.gypi @@ -61,6 +61,7 @@ '../gm/hittestpath.cpp', '../gm/imageblur.cpp', '../gm/imagemagnifier.cpp', + '../gm/inversepaths.cpp', '../gm/lerpmode.cpp', '../gm/lighting.cpp', '../gm/image.cpp', diff --git a/include/config/SkUserConfig.h b/include/config/SkUserConfig.h index 60a69e15e9..236a99a03e 100644 --- a/include/config/SkUserConfig.h +++ b/include/config/SkUserConfig.h @@ -192,4 +192,14 @@ */ //#define SK_SUPPORT_GPU 1 + +/* The PDF generation code uses Path Ops to generate inverse fills and complex + * clipping paths, but at this time, Path Ops is not release ready yet. So, + * the code is hidden behind this #define guard. If you are feeling adventurous + * and want the latest and greatest PDF generation code, uncomment the #define. + * When Path Ops is release ready, the define guards and this user config + * define should be removed entirely. + */ +//#define SK_PDF_USE_PATHOPS + #endif diff --git a/include/pdf/SkPDFDevice.h b/include/pdf/SkPDFDevice.h index cb17134aaa..b781978d17 100644 --- a/include/pdf/SkPDFDevice.h +++ b/include/pdf/SkPDFDevice.h @@ -293,6 +293,8 @@ private: */ void copyContentEntriesToData(ContentEntry* entry, SkWStream* data) const; + bool handleInversePath(const SkDraw& d, const SkPath& origPath, + const SkPaint& paint, bool pathIsMutable); bool handleRectAnnotation(const SkRect& r, const SkMatrix& matrix, const SkPaint& paint); bool handlePointAnnotation(const SkPoint* points, size_t count, diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp index 387bfe8361..eda3616a1f 100644 --- a/src/pdf/SkPDFDevice.cpp +++ b/src/pdf/SkPDFDevice.cpp @@ -16,6 +16,7 @@ #include "SkGlyphCache.h" #include "SkPaint.h" #include "SkPath.h" +#include "SkPathOps.h" #include "SkPDFFont.h" #include "SkPDFFormXObject.h" #include "SkPDFGraphicState.h" @@ -842,6 +843,12 @@ void SkPDFDevice::drawPath(const SkDraw& d, const SkPath& origPath, return; } +#ifdef SK_PDF_USE_PATHOPS + if (handleInversePath(d, origPath, paint, pathIsMutable)) { + return; + } +#endif + if (handleRectAnnotation(pathPtr->getBounds(), *d.fMatrix, paint)) { return; } @@ -1232,6 +1239,80 @@ SkData* SkPDFDevice::copyContentToData() const { return data.copyToData(); } +/* Calculate an inverted path's equivalent non-inverted path, given the + * canvas bounds. + */ +static bool calculate_inverse_path(const SkRect& bounds, const SkPath& invPath, + SkPath* outPath) { + SkASSERT(invPath.isInverseFillType()); + + SkPath clipPath; + clipPath.addRect(bounds); + + return Op(clipPath, invPath, kIntersect_PathOp, outPath); +} + +/* Draws an inverse filled path by using Path Ops to compute the positive + * inverse using the current clip as the inverse bounds. + * Return true if this was an inverse path and was properly handled, + * otherwise returns false and the normal drawing routine should continue, + * either as a (incorrect) fallback or because the path was not inverse + * in the first place. + */ +bool SkPDFDevice::handleInversePath(const SkDraw& d, const SkPath& origPath, + const SkPaint& paint, bool pathIsMutable) { + if (!origPath.isInverseFillType()) { + return false; + } + + if (d.fClip->isEmpty()) { + return false; + } + + SkPath modifiedPath; + SkPath* pathPtr = const_cast<SkPath*>(&origPath); + SkPaint noInversePaint(paint); + + // Merge stroking operations into final path. + if (SkPaint::kStroke_Style == paint.getStyle() || + SkPaint::kStrokeAndFill_Style == paint.getStyle()) { + bool doFillPath = paint.getFillPath(origPath, &modifiedPath); + if (doFillPath) { + noInversePaint.setStyle(SkPaint::kFill_Style); + noInversePaint.setStrokeWidth(0); + pathPtr = &modifiedPath; + } else { + // To be consistent with the raster output, hairline strokes + // are rendered as non-inverted. + modifiedPath.toggleInverseFillType(); + drawPath(d, modifiedPath, paint, NULL, true); + return true; + } + } + + // Get bounds of clip in current transform space + // (clip bounds are given in device space). + SkRect bounds; + SkMatrix transformInverse; + if (!d.fMatrix->invert(&transformInverse)) { + return false; + } + bounds.set(d.fClip->getBounds()); + transformInverse.mapRect(&bounds); + + // Extend the bounds by the line width (plus some padding) + // so the edge doesn't cause a visible stroke. + bounds.outset(paint.getStrokeWidth() + SK_Scalar1, + paint.getStrokeWidth() + SK_Scalar1); + + if (!calculate_inverse_path(bounds, *pathPtr, &modifiedPath)) { + return false; + } + + drawPath(d, modifiedPath, noInversePaint, NULL, true); + return true; +} + bool SkPDFDevice::handleRectAnnotation(const SkRect& r, const SkMatrix& matrix, const SkPaint& p) { SkAnnotation* annotationInfo = p.getAnnotation(); |