aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar vandebo@chromium.org <vandebo@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2010-11-03 23:55:28 +0000
committerGravatar vandebo@chromium.org <vandebo@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2010-11-03 23:55:28 +0000
commit7e2ff7cf7da604b29378978ef5d4655499485368 (patch)
tree0082227cfc627730420595285181016e4d80959a
parenteb6c7596af1a1fc7860e27ff2f678a33b2576c0f (diff)
Add clipping support and some small fixes.
Reorganize how the PDF graphic state stack is managed (fixing several bugs incidentally). Style: fix variables with underscores. Bug: fix image matrix application order, which enabled a small refactor. Review URL: http://codereview.appspot.com/2771042 git-svn-id: http://skia.googlecode.com/svn/trunk@622 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r--include/pdf/SkPDFDevice.h43
-rw-r--r--src/pdf/SkPDFDevice.cpp244
2 files changed, 166 insertions, 121 deletions
diff --git a/include/pdf/SkPDFDevice.h b/include/pdf/SkPDFDevice.h
index 7310229bb7..ada2791ec6 100644
--- a/include/pdf/SkPDFDevice.h
+++ b/include/pdf/SkPDFDevice.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2010 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.
@@ -122,19 +122,31 @@ private:
int fHeight;
SkRefPtr<SkPDFDict> fResourceDict;
- SkRefPtr<SkPDFGraphicState> fCurrentGraphicState;
- SkColor fCurrentColor;
- SkScalar fCurrentTextScaleX;
SkTDArray<SkPDFGraphicState*> fGraphicStateResources;
SkTDArray<SkPDFObject*> fXObjectResources;
- SkString fContent;
-
- // The last requested transforms from SkCanvas (setMatrixClip)
- SkMatrix fCurTransform;
+ // In PDF, transforms and clips can only be undone by popping the graphic
+ // state to before the transform or clip was applied. Because it can be
+ // a lot of work to reapply a clip and because this class has to apply
+ // different transforms to accomplish various operations, the clip is
+ // always applied before a transform and always at a different graphic
+ // state-stack level than a transform. This strategy results in the
+ // following possible states for the graphic state stack:
+ // empty: (identity transform and clip to page)
+ // one entry: a transform
+ // one entry: a clip
+ // two entries: a clip and then a transform
+ struct GraphicStackEntry {
+ SkColor fColor;
+ SkScalar fTextScaleX;
+ SkRefPtr<SkPDFGraphicState> fGraphicState;
+ SkRegion fClip;
+ SkMatrix fTransform;
+ };
+ struct GraphicStackEntry fGraphicStack[3];
+ int fGraphicStackIndex;
- // The transform currently in effect in the PDF content stream.
- SkMatrix fActiveTransform;
+ SkString fContent;
void updateGSFromPaint(const SkPaint& newPaint, SkString* textStaetUpdate);
@@ -144,17 +156,16 @@ private:
SkScalar ctl2X, SkScalar ctl2Y,
SkScalar dstX, SkScalar dstY);
void appendRectangle(SkScalar x, SkScalar y, SkScalar w, SkScalar h);
+ void emitPath(const SkPath& path);
void closePath();
- void strokePath();
void paintPath(SkPaint::Style style, SkPath::FillType fill);
+ void strokePath();
+ void pushGS();
+ void popGS();
void internalDrawBitmap(const SkMatrix& matrix, const SkBitmap& bitmap,
const SkPaint& paint);
- void setTransform(const SkMatrix& matrix);
- void setNoTransform();
- void applyTempTransform(const SkMatrix& matrix);
- void removeTempTransform();
- void applyTransform(const SkMatrix& matrix);
+ SkMatrix setTransform(const SkMatrix& matrix);
};
#endif
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index da2e306d71..e9625cf6a6 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -68,11 +68,11 @@ SkDevice* SkPDFDeviceFactory::newDevice(SkBitmap::Config config,
SkPDFDevice::SkPDFDevice(int width, int height)
: fWidth(width),
fHeight(height),
- fCurrentColor(0),
- fCurrentTextScaleX(SK_Scalar1) {
- fContent.append("q\n");
- fCurTransform.reset();
- fActiveTransform.reset();
+ fGraphicStackIndex(0) {
+ fGraphicStack[0].fColor = SK_ColorBLACK;
+ fGraphicStack[0].fTextScaleX = SK_Scalar1;
+ fGraphicStack[0].fClip.setRect(0,0, width, height);
+ fGraphicStack[0].fTransform.reset();
}
SkPDFDevice::~SkPDFDevice() {
@@ -82,13 +82,34 @@ SkPDFDevice::~SkPDFDevice() {
void SkPDFDevice::setMatrixClip(const SkMatrix& matrix,
const SkRegion& region) {
- // TODO(vandebo) handle clipping
+ // See the comment in the header file above GraphicStackEntry.
+ if (region != fGraphicStack[fGraphicStackIndex].fClip) {
+ while (fGraphicStackIndex > 0)
+ popGS();
+ pushGS();
+
+ SkPath clipPath;
+ if (!region.getBoundaryPath(&clipPath))
+ clipPath.moveTo(SkIntToScalar(-1), SkIntToScalar(-1));
+ emitPath(clipPath);
+
+ SkPath::FillType clipFill = clipPath.getFillType();
+ NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType, false);
+ NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType, false);
+ if (clipFill == SkPath::kEvenOdd_FillType)
+ fContent.append("W* n ");
+ else
+ fContent.append("W n ");
+
+ fGraphicStack[fGraphicStackIndex].fClip = region;
+ }
setTransform(matrix);
- fCurTransform = matrix;
}
void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) {
- setNoTransform();
+ SkMatrix identityTransform;
+ identityTransform.reset();
+ SkMatrix curTransform = setTransform(identityTransform);
SkPaint newPaint = paint;
newPaint.setStyle(SkPaint::kFill_Style);
@@ -96,7 +117,7 @@ void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) {
SkRect all = SkRect::MakeWH(width() + 1, height() + 1);
drawRect(d, all, newPaint);
- setTransform(fCurTransform);
+ setTransform(curTransform);
}
void SkPDFDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode,
@@ -156,9 +177,9 @@ void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& r,
path.addRect(r);
paint.getFillPath(path, &path);
- SkPaint no_effect_paint(paint);
- SkSafeUnref(no_effect_paint.setPathEffect(NULL));
- drawPath(d, path, no_effect_paint);
+ SkPaint noEffectPaint(paint);
+ SkSafeUnref(noEffectPaint.setPathEffect(NULL));
+ drawPath(d, path, noEffectPaint);
return;
}
updateGSFromPaint(paint, NULL);
@@ -173,80 +194,32 @@ void SkPDFDevice::drawPath(const SkDraw& d, const SkPath& path,
const SkPaint& paint) {
if (paint.getPathEffect()) {
// Apply the path effect to path and draw it that way.
- SkPath no_effect_path;
- paint.getFillPath(path, &no_effect_path);
+ SkPath noEffectPath;
+ paint.getFillPath(path, &noEffectPath);
- SkPaint no_effect_paint(paint);
- SkSafeUnref(no_effect_paint.setPathEffect(NULL));
- drawPath(d, no_effect_path, no_effect_paint);
+ SkPaint noEffectPaint(paint);
+ SkSafeUnref(noEffectPaint.setPathEffect(NULL));
+ drawPath(d, noEffectPath, noEffectPaint);
return;
}
updateGSFromPaint(paint, NULL);
- SkPoint args[4];
- SkPath::Iter iter(path, false);
- for (SkPath::Verb verb = iter.next(args);
- verb != SkPath::kDone_Verb;
- verb = iter.next(args)) {
- // args gets all the points, even the implicit first point.
- switch (verb) {
- case SkPath::kMove_Verb:
- moveTo(args[0].fX, args[0].fY);
- break;
- case SkPath::kLine_Verb:
- appendLine(args[1].fX, args[1].fY);
- break;
- case SkPath::kQuad_Verb: {
- // Convert quad to cubic (degree elevation). http://goo.gl/vS4i
- const SkScalar three = SkIntToScalar(3);
- args[1].scale(SkIntToScalar(2));
- SkScalar ctl1X = SkScalarDiv(args[0].fX + args[1].fX, three);
- SkScalar ctl1Y = SkScalarDiv(args[0].fY + args[1].fY, three);
- SkScalar ctl2X = SkScalarDiv(args[2].fX + args[1].fX, three);
- SkScalar ctl2Y = SkScalarDiv(args[2].fY + args[1].fY, three);
- appendCubic(ctl1X, ctl1Y, ctl2X, ctl2Y, args[2].fX, args[2].fY);
- break;
- }
- case SkPath::kCubic_Verb:
- appendCubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY,
- args[3].fX, args[3].fY);
- break;
- case SkPath::kClose_Verb:
- closePath();
- break;
- case SkPath::kDone_Verb:
- break;
- default:
- SkASSERT(false);
- break;
- }
- }
+ emitPath(path);
paintPath(paint.getStyle(), path.getFillType());
}
void SkPDFDevice::drawBitmap(const SkDraw&, const SkBitmap& bitmap,
const SkMatrix& matrix, const SkPaint& paint) {
- SkMatrix scaled;
- // Adjust for origin flip.
- scaled.setScale(1, -1);
- scaled.postTranslate(0, 1);
- scaled.postConcat(fCurTransform);
- // Scale the image up from 1x1 to WxH.
- scaled.postScale(bitmap.width(), bitmap.height());
- scaled.postConcat(matrix);
- internalDrawBitmap(scaled, bitmap, paint);
+ SkMatrix transform = matrix;
+ transform.postConcat(fGraphicStack[fGraphicStackIndex].fTransform);
+ internalDrawBitmap(transform, bitmap, paint);
}
void SkPDFDevice::drawSprite(const SkDraw&, const SkBitmap& bitmap,
int x, int y, const SkPaint& paint) {
- SkMatrix scaled;
- // Adjust for origin flip.
- scaled.setScale(1, -1);
- scaled.postTranslate(0, 1);
- // Scale the image up from 1x1 to WxH.
- scaled.postScale(bitmap.width(), -bitmap.height());
- scaled.postTranslate(x, y);
- internalDrawBitmap(scaled, bitmap, paint);
+ SkMatrix matrix;
+ matrix.setTranslate(x, y);
+ internalDrawBitmap(matrix, bitmap, paint);
}
void SkPDFDevice::drawText(const SkDraw&, const void* text, size_t len,
@@ -375,7 +348,8 @@ SkString SkPDFDevice::content(bool flipOrigin) const {
if (flipOrigin)
result.printf("1 0 0 -1 0 %d cm\n", fHeight);
result.append(fContent);
- result.append("Q");
+ for (int i = 0; i < fGraphicStackIndex; i++)
+ result.append("Q\n");
return result;
}
@@ -401,7 +375,8 @@ void SkPDFDevice::updateGSFromPaint(const SkPaint& newPaint,
newGraphicState->unref(); // getGraphicState and SkRefPtr both took a ref.
// newGraphicState has been canonicalized so we can directly compare
// pointers.
- if (fCurrentGraphicState.get() != newGraphicState.get()) {
+ if (fGraphicStack[fGraphicStackIndex].fGraphicState.get() !=
+ newGraphicState.get()) {
int resourceIndex = fGraphicStateResources.find(newGraphicState.get());
if (resourceIndex < 0) {
resourceIndex = fGraphicStateResources.count();
@@ -411,27 +386,28 @@ void SkPDFDevice::updateGSFromPaint(const SkPaint& newPaint,
fContent.append("/G");
fContent.appendS32(resourceIndex);
fContent.append(" gs\n");
- fCurrentGraphicState = newGraphicState;
+ fGraphicStack[fGraphicStackIndex].fGraphicState = newGraphicState;
}
SkColor newColor = newPaint.getColor();
newColor = SkColorSetA(newColor, 0xFF);
- if (fCurrentColor != newColor) {
+ if (fGraphicStack[fGraphicStackIndex].fColor != newColor) {
SkString colorString = toPDFColor(newColor);
fContent.append(colorString);
fContent.append("RG ");
fContent.append(colorString);
fContent.append("rg\n");
- fCurrentColor = newColor;
+ fGraphicStack[fGraphicStackIndex].fColor = newColor;
}
if (textStateUpdate != NULL &&
- fCurrentTextScaleX != newPaint.getTextScaleX()) {
+ fGraphicStack[fGraphicStackIndex].fTextScaleX !=
+ newPaint.getTextScaleX()) {
SkScalar scale = newPaint.getTextScaleX();
SkScalar pdfScale = scale * 100;
textStateUpdate->appendScalar(pdfScale);
textStateUpdate->append(" Tz\n");
- fCurrentTextScaleX = scale;
+ fGraphicStack[fGraphicStackIndex].fTextScaleX = scale;
}
}
@@ -482,6 +458,47 @@ void SkPDFDevice::appendRectangle(SkScalar x, SkScalar y,
fContent.append(" re\n");
}
+void SkPDFDevice::emitPath(const SkPath& path) {
+ SkPoint args[4];
+ SkPath::Iter iter(path, false);
+ for (SkPath::Verb verb = iter.next(args);
+ verb != SkPath::kDone_Verb;
+ verb = iter.next(args)) {
+ // args gets all the points, even the implicit first point.
+ switch (verb) {
+ case SkPath::kMove_Verb:
+ moveTo(args[0].fX, args[0].fY);
+ break;
+ case SkPath::kLine_Verb:
+ appendLine(args[1].fX, args[1].fY);
+ break;
+ case SkPath::kQuad_Verb: {
+ // Convert quad to cubic (degree elevation). http://goo.gl/vS4i
+ const SkScalar three = SkIntToScalar(3);
+ args[1].scale(SkIntToScalar(2));
+ SkScalar ctl1X = SkScalarDiv(args[0].fX + args[1].fX, three);
+ SkScalar ctl1Y = SkScalarDiv(args[0].fY + args[1].fY, three);
+ SkScalar ctl2X = SkScalarDiv(args[2].fX + args[1].fX, three);
+ SkScalar ctl2Y = SkScalarDiv(args[2].fY + args[1].fY, three);
+ appendCubic(ctl1X, ctl1Y, ctl2X, ctl2Y, args[2].fX, args[2].fY);
+ break;
+ }
+ case SkPath::kCubic_Verb:
+ appendCubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY,
+ args[3].fX, args[3].fY);
+ break;
+ case SkPath::kClose_Verb:
+ closePath();
+ break;
+ case SkPath::kDone_Verb:
+ break;
+ default:
+ SkASSERT(false);
+ break;
+ }
+ }
+}
+
void SkPDFDevice::closePath() {
fContent.append("h\n");
}
@@ -508,44 +525,59 @@ void SkPDFDevice::strokePath() {
paintPath(SkPaint::kStroke_Style, SkPath::kWinding_FillType);
}
+void SkPDFDevice::pushGS() {
+ SkASSERT(fGraphicStackIndex < 2);
+ fContent.append("q\n");
+ fGraphicStackIndex++;
+ fGraphicStack[fGraphicStackIndex] = fGraphicStack[fGraphicStackIndex - 1];
+}
+
+void SkPDFDevice::popGS() {
+ SkASSERT(fGraphicStackIndex > 0);
+ fContent.append("Q\n");
+ fGraphicStackIndex--;
+}
+
void SkPDFDevice::internalDrawBitmap(const SkMatrix& matrix,
const SkBitmap& bitmap,
const SkPaint& paint) {
- setTransform(matrix);
+ SkMatrix scaled;
+ // Adjust for origin flip.
+ scaled.setScale(1, -1);
+ scaled.postTranslate(0, 1);
+ // Scale the image up from 1x1 to WxH.
+ scaled.postScale(bitmap.width(), bitmap.height());
+ scaled.postConcat(matrix);
+ SkMatrix curTransform = setTransform(scaled);
+
SkPDFImage* image = new SkPDFImage(bitmap, paint);
fXObjectResources.push(image); // Transfer reference.
fContent.append("/X");
fContent.appendS32(fXObjectResources.count() - 1);
fContent.append(" Do\n");
- setTransform(fCurTransform);
+ setTransform(curTransform);
}
-void SkPDFDevice::setTransform(const SkMatrix& m) {
- setNoTransform();
- applyTransform(m);
-}
-
-void SkPDFDevice::setNoTransform() {
- if (fActiveTransform.getType() == SkMatrix::kIdentity_Mask)
- return;
- fContent.append("Q q "); // Restore the default transform and save it.
- fCurrentGraphicState = NULL;
- fActiveTransform.reset();
-}
+SkMatrix SkPDFDevice::setTransform(const SkMatrix& m) {
+ SkMatrix old = fGraphicStack[fGraphicStackIndex].fTransform;
+ if (old == m)
+ return old;
-void SkPDFDevice::applyTempTransform(const SkMatrix& m) {
- fContent.append("q ");
- applyTransform(m);
-}
+ if (old.getType() != SkMatrix::kIdentity_Mask) {
+ SkASSERT(fGraphicStackIndex > 0);
+ SkASSERT(fGraphicStack[fGraphicStackIndex - 1].fTransform.getType() ==
+ SkMatrix::kIdentity_Mask);
+ SkASSERT(fGraphicStack[fGraphicStackIndex].fClip ==
+ fGraphicStack[fGraphicStackIndex - 1].fClip);
+ popGS();
+ }
+ if (m.getType() == SkMatrix::kIdentity_Mask)
+ return old;
-void SkPDFDevice::removeTempTransform() {
- fContent.append("Q\n");
- fActiveTransform = fCurTransform;
-}
+ if (fGraphicStackIndex == 0 || fGraphicStack[fGraphicStackIndex].fClip !=
+ fGraphicStack[fGraphicStackIndex - 1].fClip)
+ pushGS();
-void SkPDFDevice::applyTransform(const SkMatrix& m) {
- if (m == fActiveTransform)
- return;
SkScalar transform[6];
SkAssertResult(m.pdfTransform(transform));
for (size_t i = 0; i < SK_ARRAY_COUNT(transform); i++) {
@@ -553,5 +585,7 @@ void SkPDFDevice::applyTransform(const SkMatrix& m) {
fContent.append(" ");
}
fContent.append("cm\n");
- fActiveTransform = m;
+ fGraphicStack[fGraphicStackIndex].fTransform = m;
+
+ return old;
}