aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/pdf
diff options
context:
space:
mode:
authorGravatar vandebo@chromium.org <vandebo@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2011-05-13 03:50:38 +0000
committerGravatar vandebo@chromium.org <vandebo@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2011-05-13 03:50:38 +0000
commit6112c215fbdd53388e64ece36e6c7bba0fe3a451 (patch)
tree831fe90e87e56a1aea74c3f4148c2939bacb5655 /src/pdf
parentd604481f7a2587f5b400d2a0a68a6491a0d584c7 (diff)
[PDF] Add support for SrcIn, SrcOut, DstIn, DstOut xfermodes.
This change uses the soft mask (aka soft clip) functionality of PDF to implement the xfermodes. It has to put existing content (dst) into a form xobject as well as putting the new (src) content into a different form xobject. It then draws one of them with the other as the soft mask. To accomplish this, we add a call to finishContentEntry after each call to setUpContentEntry - this is kind of a hack, but I don't see a better way to extract src. Unfortunately, soft mask is specified in the Graphic State PDF object (and not in the form xobject), so when handling one of these modes, we add a one time GS object to set the soft mask and invoke a simple GS to reset the soft mask when done. Review URL: http://codereview.appspot.com/4496041 git-svn-id: http://skia.googlecode.com/svn/trunk@1320 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src/pdf')
-rw-r--r--src/pdf/SkPDFDevice.cpp121
-rw-r--r--src/pdf/SkPDFGraphicState.cpp114
-rw-r--r--src/pdf/SkPDFUtils.cpp14
3 files changed, 209 insertions, 40 deletions
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index 1a3324ad2d..6734c0e215 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -423,9 +423,7 @@ void GraphicStackState::updateDrawingState(const GraphicStateEntry& state) {
}
if (state.fGraphicStateIndex != currentEntry()->fGraphicStateIndex) {
- fContentStream->writeText("/G");
- fContentStream->writeDecAsText(state.fGraphicStateIndex);
- fContentStream->writeText(" gs\n");
+ SkPDFUtils::ApplyGraphicState(state.fGraphicStateIndex, fContentStream);
currentEntry()->fGraphicStateIndex = state.fGraphicStateIndex;
}
@@ -556,6 +554,7 @@ void SkPDFDevice::clear(SkColor color) {
}
internalDrawPaint(paint);
+ finishContentEntry(paint);
}
void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) {
@@ -566,6 +565,7 @@ void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) {
}
internalDrawPaint(newPaint);
+ finishContentEntry(newPaint);
}
void SkPDFDevice::internalDrawPaint(const SkPaint& paint) {
@@ -651,6 +651,7 @@ void SkPDFDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode,
default:
SkASSERT(false);
}
+ finishContentEntry(*paint);
}
void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& r,
@@ -676,6 +677,7 @@ void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& r,
SkPDFUtils::AppendRectangle(r, &fCurrentContentEntry->fContent);
SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType,
&fCurrentContentEntry->fContent);
+ finishContentEntry(paint);
}
void SkPDFDevice::drawPath(const SkDraw& d, const SkPath& path,
@@ -703,6 +705,7 @@ void SkPDFDevice::drawPath(const SkDraw& d, const SkPath& path,
SkPDFUtils::EmitPath(path, &fCurrentContentEntry->fContent);
SkPDFUtils::PaintPath(paint.getStyle(), path.getFillType(),
&fCurrentContentEntry->fContent);
+ finishContentEntry(paint);
}
void SkPDFDevice::drawBitmap(const SkDraw& d, const SkBitmap& bitmap,
@@ -798,6 +801,7 @@ void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len,
drawRect(d, r, paint);
}
}
+ finishContentEntry(textPaint);
}
void SkPDFDevice::drawPosText(const SkDraw& d, const void* text, size_t len,
@@ -849,6 +853,7 @@ void SkPDFDevice::drawPosText(const SkDraw& d, const void* text, size_t len,
fCurrentContentEntry->fContent.writeText(" Tj\n");
}
fCurrentContentEntry->fContent.writeText("ET\n");
+ finishContentEntry(textPaint);
}
void SkPDFDevice::drawTextOnPath(const SkDraw& d, const void* text, size_t len,
@@ -889,10 +894,9 @@ void SkPDFDevice::drawDevice(const SkDraw& d, SkDevice* device, int x, int y,
SkPDFDevice* pdfDevice = static_cast<SkPDFDevice*>(device);
SkPDFFormXObject* xobject = new SkPDFFormXObject(pdfDevice);
fXObjectResources.push(xobject); // Transfer reference.
- fCurrentContentEntry->fContent.writeText("/X");
- fCurrentContentEntry->fContent.writeDecAsText(
- fXObjectResources.count() - 1);
- fCurrentContentEntry->fContent.writeText(" Do\n");
+ SkPDFUtils::DrawFormXObject(fXObjectResources.count() - 1,
+ &fCurrentContentEntry->fContent);
+ finishContentEntry(paint);
}
const SkRefPtr<SkPDFDict>& SkPDFDevice::getResourceDict() {
@@ -1038,6 +1042,14 @@ SkStream* SkPDFDevice::content() const {
return result;
}
+void SkPDFDevice::createFormXObjectFromDevice(
+ SkRefPtr<SkPDFFormXObject>* xobject) {
+ *xobject = new SkPDFFormXObject(this);
+ (*xobject)->unref(); // SkRefPtr and new both took a reference.
+ cleanUp(); // Reset this device to have no content.
+ init();
+}
+
bool SkPDFDevice::setUpContentEntry(const SkClipStack* clipStack,
const SkRegion& clipRegion,
const SkMatrix& matrix,
@@ -1081,9 +1093,19 @@ bool SkPDFDevice::setUpContentEntry(const SkClipStack* clipStack,
fCurrentContentEntry = fContentEntries.get();
}
- // TODO(vandebo) For the following modes, we need to calculate various
- // operations between the source and destination shape:
- // SrcIn, DstIn, SrcOut, DstOut, SrcAtop, DestAtop, Xor.
+ // For the following modes, we use both source and destination, but
+ // we use one as a smask for the other, so we have to make form xobjects
+ // out of both of them: SrcIn, DstIn, SrcOut, DstOut.
+ if (xfermode == SkXfermode::kSrcIn_Mode ||
+ xfermode == SkXfermode::kDstIn_Mode ||
+ xfermode == SkXfermode::kSrcOut_Mode ||
+ xfermode == SkXfermode::kDstOut_Mode) {
+ SkASSERT(fDstFormXObject.get() == NULL);
+ createFormXObjectFromDevice(&fDstFormXObject);
+ }
+
+ // TODO(vandebo) Figure out how/if we can handle the following modes:
+ // SrcAtop, DestAtop, Xor.
// These xfer modes don't draw source at all.
if (xfermode == SkXfermode::kClear_Mode ||
@@ -1135,6 +1157,57 @@ bool SkPDFDevice::setUpContentEntryForText(const SkClipStack* clipStack,
return setUpContentEntry(clipStack, clipRegion, matrix, paint, true);
}
+void SkPDFDevice::finishContentEntry(const SkPaint& paint) {
+ SkXfermode::Mode xfermode = SkXfermode::kSrcOver_Mode;
+ if (paint.getXfermode()) {
+ paint.getXfermode()->asMode(&xfermode);
+ }
+ if (xfermode != SkXfermode::kSrcIn_Mode &&
+ xfermode != SkXfermode::kDstIn_Mode &&
+ xfermode != SkXfermode::kSrcOut_Mode &&
+ xfermode != SkXfermode::kDstOut_Mode) {
+ SkASSERT(fDstFormXObject.get() == NULL);
+ return;
+ }
+
+ SkRefPtr<SkPDFFormXObject> srcFormXObject;
+ createFormXObjectFromDevice(&srcFormXObject);
+
+ SkMatrix identity;
+ identity.reset();
+ SkPaint stockPaint;
+ setUpContentEntry(&fExistingClipStack, fExistingClipRegion, identity,
+ stockPaint);
+
+ SkRefPtr<SkPDFGraphicState> sMaskGS;
+ if (xfermode == SkXfermode::kSrcIn_Mode ||
+ xfermode == SkXfermode::kSrcOut_Mode) {
+ sMaskGS = SkPDFGraphicState::getSMaskGraphicState(
+ fDstFormXObject.get(), xfermode == SkXfermode::kSrcOut_Mode);
+ fXObjectResources.push(srcFormXObject.get());
+ srcFormXObject->ref();
+ } else {
+ sMaskGS = SkPDFGraphicState::getSMaskGraphicState(
+ srcFormXObject.get(), xfermode == SkXfermode::kDstOut_Mode);
+ fXObjectResources.push(fDstFormXObject.get());
+ fDstFormXObject->ref();
+ }
+ sMaskGS->unref(); // SkRefPtr and getSMaskGraphicState both took a ref.
+ SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
+ &fCurrentContentEntry->fContent);
+
+ SkPDFUtils::DrawFormXObject(fXObjectResources.count() - 1,
+ &fCurrentContentEntry->fContent);
+
+ sMaskGS = SkPDFGraphicState::getNoSMaskGraphicState();
+ sMaskGS->unref(); // SkRefPtr and getSMaskGraphicState both took a ref.
+ SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
+ &fCurrentContentEntry->fContent);
+
+ fDstFormXObject = NULL;
+ finishContentEntry(stockPaint);
+}
+
void SkPDFDevice::populateGraphicStateEntryFromPaint(
const SkMatrix& matrix,
const SkClipStack& clipStack,
@@ -1212,14 +1285,7 @@ void SkPDFDevice::populateGraphicStateEntryFromPaint(
newGraphicState = SkPDFGraphicState::getGraphicStateForPaint(newPaint);
}
newGraphicState->unref(); // getGraphicState and SkRefPtr both took a ref.
- // newGraphicState has been canonicalized so we can directly compare
- // pointers.
- int resourceIndex = fGraphicStateResources.find(newGraphicState.get());
- if (resourceIndex < 0) {
- resourceIndex = fGraphicStateResources.count();
- fGraphicStateResources.push(newGraphicState.get());
- newGraphicState->ref();
- }
+ int resourceIndex = addGraphicStateResource(newGraphicState.get());
entry->fGraphicStateIndex = resourceIndex;
if (hasText) {
@@ -1230,6 +1296,18 @@ void SkPDFDevice::populateGraphicStateEntryFromPaint(
}
}
+int SkPDFDevice::addGraphicStateResource(SkPDFGraphicState* gs) {
+ // Assumes that gs has been canonicalized (so we can directly compare
+ // pointers).
+ int result = fGraphicStateResources.find(gs);
+ if (result < 0) {
+ result = fGraphicStateResources.count();
+ fGraphicStateResources.push(gs);
+ gs->ref();
+ }
+ return result;
+}
+
void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID) {
SkTypeface* typeface = paint.getTypeface();
if (fCurrentContentEntry->fState.fFont == NULL ||
@@ -1299,8 +1377,7 @@ void SkPDFDevice::internalDrawBitmap(const SkMatrix& matrix,
}
fXObjectResources.push(image); // Transfer reference.
- fCurrentContentEntry->fContent.writeText("/X");
- fCurrentContentEntry->fContent.writeDecAsText(
- fXObjectResources.count() - 1);
- fCurrentContentEntry->fContent.writeText(" Do\n");
+ SkPDFUtils::DrawFormXObject(fXObjectResources.count() - 1,
+ &fCurrentContentEntry->fContent);
+ finishContentEntry(paint);
}
diff --git a/src/pdf/SkPDFGraphicState.cpp b/src/pdf/SkPDFGraphicState.cpp
index e5badae663..48203f6ca5 100644
--- a/src/pdf/SkPDFGraphicState.cpp
+++ b/src/pdf/SkPDFGraphicState.cpp
@@ -14,14 +14,13 @@
* limitations under the License.
*/
+#include "SkPDFFormXObject.h"
#include "SkPDFGraphicState.h"
#include "SkPDFUtils.h"
#include "SkStream.h"
#include "SkTypes.h"
-namespace {
-
-const char* blendModeFromXfermode(SkXfermode::Mode mode) {
+static const char* blend_mode_from_xfermode(SkXfermode::Mode mode) {
switch (mode) {
case SkXfermode::kSrcOver_Mode: return "Normal";
case SkXfermode::kMultiply_Mode: return "Multiply";
@@ -41,13 +40,13 @@ const char* blendModeFromXfermode(SkXfermode::Mode mode) {
case SkXfermode::kSrc_Mode:
case SkXfermode::kDst_Mode:
case SkXfermode::kDstOver_Mode:
- return "Normal";
-
- // TODO(vandebo) Figure out if we can support more of these modes.
case SkXfermode::kSrcIn_Mode:
case SkXfermode::kDstIn_Mode:
case SkXfermode::kSrcOut_Mode:
case SkXfermode::kDstOut_Mode:
+ return "Normal";
+
+ // TODO(vandebo) Figure out if we can support more of these modes.
case SkXfermode::kSrcATop_Mode:
case SkXfermode::kDstATop_Mode:
case SkXfermode::kXor_Mode:
@@ -57,13 +56,23 @@ const char* blendModeFromXfermode(SkXfermode::Mode mode) {
return NULL;
}
-}
-
SkPDFGraphicState::~SkPDFGraphicState() {
SkAutoMutexAcquire lock(canonicalPaintsMutex());
- int index = find(fPaint);
- SkASSERT(index >= 0);
- canonicalPaints().removeShuffle(index);
+ if (!fSMask) {
+ int index = find(fPaint);
+ SkASSERT(index >= 0);
+ canonicalPaints().removeShuffle(index);
+ }
+ fResources.unrefAll();
+}
+
+void SkPDFGraphicState::getResources(SkTDArray<SkPDFObject*>* resourceList) {
+ resourceList->setReserve(resourceList->count() + fResources.count());
+ for (int i = 0; i < fResources.count(); i++) {
+ resourceList->push(fResources[i]);
+ fResources[i]->ref();
+ fResources[i]->getResources(resourceList);
+ }
}
void SkPDFGraphicState::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
@@ -108,14 +117,82 @@ SkPDFGraphicState* SkPDFGraphicState::getGraphicStateForPaint(
}
// static
+SkPDFGraphicState* SkPDFGraphicState::getSMaskGraphicState(
+ SkPDFFormXObject* sMask, bool invert) {
+ // The practical chances of using the same mask more than once are unlikely
+ // enough that it's not worth canonicalizing.
+ SkAutoMutexAcquire lock(canonicalPaintsMutex());
+
+ SkRefPtr<SkPDFDict> sMaskDict = new SkPDFDict("Mask");
+ sMaskDict->unref(); // SkRefPtr and new both took a reference.
+ sMaskDict->insert("S", new SkPDFName("Alpha"))->unref();
+ sMaskDict->insert("G", new SkPDFObjRef(sMask))->unref();
+
+ SkPDFGraphicState* result = new SkPDFGraphicState;
+ result->fPopulated = true;
+ result->fSMask = true;
+ result->insert("Type", new SkPDFName("ExtGState"))->unref();
+ result->insert("SMask", sMaskDict.get());
+ result->fResources.push(sMask);
+ sMask->ref();
+
+ if (invert) {
+ // Acrobat crashes if we use a type 0 function, kpdf crashes if we use
+ // a type 2 function, so we use a type 4 function.
+ SkRefPtr<SkPDFArray> domainAndRange = new SkPDFArray;
+ domainAndRange->unref(); // SkRefPtr and new both took a reference.
+ domainAndRange->reserve(2);
+ domainAndRange->append(new SkPDFInt(0))->unref();
+ domainAndRange->append(new SkPDFInt(1))->unref();
+
+ static const char psInvert[] = "{1 exch sub}";
+ SkRefPtr<SkMemoryStream> psInvertStream =
+ new SkMemoryStream(&psInvert, strlen(psInvert), true);
+ psInvertStream->unref(); // SkRefPtr and new both took a reference.
+
+ SkRefPtr<SkPDFStream> invertFunc =
+ new SkPDFStream(psInvertStream.get());
+ result->fResources.push(invertFunc.get()); // Pass the ref from new.
+ invertFunc->insert("FunctionType", new SkPDFInt(4))->unref();
+ invertFunc->insert("Domain", domainAndRange.get());
+ invertFunc->insert("Range", domainAndRange.get());
+
+ sMaskDict->insert("TR", new SkPDFObjRef(invertFunc.get()))->unref();
+ }
+
+ return result;
+}
+
+// static
+SkPDFGraphicState* SkPDFGraphicState::getNoSMaskGraphicState() {
+ SkAutoMutexAcquire lock(canonicalPaintsMutex());
+ static SkPDFGraphicState* noSMaskGS = NULL;
+ if (!noSMaskGS) {
+ noSMaskGS = new SkPDFGraphicState;
+ noSMaskGS->fPopulated = true;
+ noSMaskGS->fSMask = true;
+ noSMaskGS->insert("Type", new SkPDFName("ExtGState"))->unref();
+ noSMaskGS->insert("SMask", new SkPDFName("None"))->unref();
+ }
+ noSMaskGS->ref();
+ return noSMaskGS;
+}
+
+// static
int SkPDFGraphicState::find(const SkPaint& paint) {
GSCanonicalEntry search(&paint);
return canonicalPaints().find(search);
}
+SkPDFGraphicState::SkPDFGraphicState()
+ : fPopulated(false),
+ fSMask(false) {
+}
+
SkPDFGraphicState::SkPDFGraphicState(const SkPaint& paint)
: fPaint(paint),
- fPopulated(false) {
+ fPopulated(false),
+ fSMask(false) {
}
// populateDict and operator== have to stay in sync with each other.
@@ -154,11 +231,12 @@ void SkPDFGraphicState::populateDict() {
fPaint.getXfermode()->asMode(&xfermode);
// If we don't support the mode, just use kSrcOver_Mode.
if (xfermode < 0 || xfermode > SkXfermode::kLastMode ||
- blendModeFromXfermode(xfermode) == NULL) {
+ blend_mode_from_xfermode(xfermode) == NULL) {
xfermode = SkXfermode::kSrcOver_Mode;
NOT_IMPLEMENTED("unsupported xfermode", false);
}
- insert("BM", new SkPDFName(blendModeFromXfermode(xfermode)))->unref();
+ insert("BM",
+ new SkPDFName(blend_mode_from_xfermode(xfermode)))->unref();
}
}
@@ -185,10 +263,10 @@ bool SkPDFGraphicState::GSCanonicalEntry::operator==(
aXfermode->asMode(&aXfermodeName);
}
if (aXfermodeName < 0 || aXfermodeName > SkXfermode::kLastMode ||
- blendModeFromXfermode(aXfermodeName) == NULL) {
+ blend_mode_from_xfermode(aXfermodeName) == NULL) {
aXfermodeName = SkXfermode::kSrcOver_Mode;
}
- const char* aXfermodeString = blendModeFromXfermode(aXfermodeName);
+ const char* aXfermodeString = blend_mode_from_xfermode(aXfermodeName);
SkASSERT(aXfermodeString != NULL);
SkXfermode::Mode bXfermodeName = SkXfermode::kSrcOver_Mode;
@@ -197,10 +275,10 @@ bool SkPDFGraphicState::GSCanonicalEntry::operator==(
bXfermode->asMode(&bXfermodeName);
}
if (bXfermodeName < 0 || bXfermodeName > SkXfermode::kLastMode ||
- blendModeFromXfermode(bXfermodeName) == NULL) {
+ blend_mode_from_xfermode(bXfermodeName) == NULL) {
bXfermodeName = SkXfermode::kSrcOver_Mode;
}
- const char* bXfermodeString = blendModeFromXfermode(bXfermodeName);
+ const char* bXfermodeString = blend_mode_from_xfermode(bXfermodeName);
SkASSERT(bXfermodeString != NULL);
return strcmp(aXfermodeString, bXfermodeString) == 0;
diff --git a/src/pdf/SkPDFUtils.cpp b/src/pdf/SkPDFUtils.cpp
index 2935f1e97f..a838427c73 100644
--- a/src/pdf/SkPDFUtils.cpp
+++ b/src/pdf/SkPDFUtils.cpp
@@ -171,3 +171,17 @@ void SkPDFUtils::StrokePath(SkWStream* content) {
SkPDFUtils::PaintPath(
SkPaint::kStroke_Style, SkPath::kWinding_FillType, content);
}
+
+// static
+void SkPDFUtils::DrawFormXObject(int objectIndex, SkWStream* content) {
+ content->writeText("/X");
+ content->writeDecAsText(objectIndex);
+ content->writeText(" Do\n");
+}
+
+// static
+void SkPDFUtils::ApplyGraphicState(int objectIndex, SkWStream* content) {
+ content->writeText("/G");
+ content->writeDecAsText(objectIndex);
+ content->writeText(" gs\n");
+}