Index: src/pdf/SkPDFShader.cpp |
diff --git a/src/pdf/SkPDFShader.cpp b/src/pdf/SkPDFShader.cpp |
index 618fb6e8c3386b0049f4659d3536ce9d3c4cb08a..80b6b4d0068a4334e4770c646a2fccfa1c341bb1 100644 |
--- a/src/pdf/SkPDFShader.cpp |
+++ b/src/pdf/SkPDFShader.cpp |
@@ -13,13 +13,16 @@ |
#include "SkData.h" |
#include "SkPDFCatalog.h" |
#include "SkPDFDevice.h" |
-#include "SkPDFTypes.h" |
+#include "SkPDFFormXObject.h" |
+#include "SkPDFGraphicState.h" |
#include "SkPDFResourceDict.h" |
+#include "SkPDFTypes.h" |
#include "SkPDFUtils.h" |
#include "SkScalar.h" |
#include "SkStream.h" |
#include "SkTemplates.h" |
#include "SkThread.h" |
+#include "SkTSet.h" |
#include "SkTypes.h" |
static bool transformBBox(const SkMatrix& matrix, SkRect* bbox) { |
@@ -399,7 +402,7 @@ class SkPDFShader::State { |
public: |
SkShader::GradientType fType; |
SkShader::GradientInfo fInfo; |
- SkAutoFree fColorData; |
+ SkAutoFree fColorData; // This provides storage for arrays in fInfo. |
SkMatrix fCanvasTransform; |
SkMatrix fShaderTransform; |
SkIRect fBBox; |
@@ -408,9 +411,20 @@ public: |
uint32_t fPixelGeneration; |
SkShader::TileMode fImageTileModes[2]; |
- explicit State(const SkShader& shader, const SkMatrix& canvasTransform, |
+ State(const SkShader& shader, const SkMatrix& canvasTransform, |
const SkIRect& bbox); |
+ |
bool operator==(const State& b) const; |
+ |
+ SkPDFShader::State* CreateAlphaToLuminosityState() const; |
+ SkPDFShader::State* CreateOpaqueState() const; |
+ |
+ bool GradientHasAlpha() const; |
+ |
+private: |
+ State(const State& other); |
+ State operator=(const State& rhs); |
+ void AllocateGradientInfoStorage(); |
}; |
class SkPDFFunctionShader : public SkPDFDict, public SkPDFShader { |
@@ -441,6 +455,41 @@ private: |
SkPDFStream* makePSFunction(const SkString& psCode, SkPDFArray* domain); |
}; |
+/** |
+ * A shader for PDF gradients. This encapsulates the function shader |
+ * inside a tiling pattern while providing a common pattern interface. |
+ * The encapsulation allows the use of a SMask for transparency gradients. |
+ */ |
+class SkPDFAlphaFunctionShader : public SkPDFStream, public SkPDFShader { |
+public: |
+ explicit SkPDFAlphaFunctionShader(SkPDFShader::State* state); |
+ virtual ~SkPDFAlphaFunctionShader() { |
+ if (isValid()) { |
+ RemoveShader(this); |
+ } |
+ } |
+ |
+ virtual bool isValid() { |
+ return fColorPattern.get() != NULL; |
+ } |
+ |
+private: |
+ SkAutoTDelete<const SkPDFShader::State> fState; |
+ |
+ SkPDFGraphicState* CreateSMaskGraphicState( |
+ SkPDFShader::State* luminosityState, SkRect& bbox); |
+ |
+ void getResources(const SkTSet<SkPDFObject*>& knownResourceObjects, |
+ SkTSet<SkPDFObject*>* newResourceObjects) { |
+ fResourceDict->getResources(knownResourceObjects, |
+ newResourceObjects, |
+ true); |
+ } |
+ |
+ SkAutoTUnref<SkPDFObject> fColorPattern; |
+ SkAutoTUnref<SkPDFResourceDict> fResourceDict; |
+}; |
+ |
class SkPDFImageShader : public SkPDFStream, public SkPDFShader { |
public: |
explicit SkPDFImageShader(SkPDFShader::State* state); |
@@ -466,21 +515,12 @@ private: |
SkPDFShader::SkPDFShader() {} |
// static |
-void SkPDFShader::RemoveShader(SkPDFObject* shader) { |
- SkAutoMutexAcquire lock(CanonicalShadersMutex()); |
- ShaderCanonicalEntry entry(shader, NULL); |
- int index = CanonicalShaders().find(entry); |
- SkASSERT(index >= 0); |
- CanonicalShaders().removeShuffle(index); |
-} |
- |
-// static |
-SkPDFObject* SkPDFShader::GetPDFShader(const SkShader& shader, |
- const SkMatrix& matrix, |
- const SkIRect& surfaceBBox) { |
+// This is an internal method. |
+// CanonicalShadersMutex() should already be acquired. |
+SkPDFObject* SkPDFShader::GetPDFShaderByState(State* inState) { |
SkPDFObject* result; |
- SkAutoMutexAcquire lock(CanonicalShadersMutex()); |
- SkAutoTDelete<State> shaderState(new State(shader, matrix, surfaceBBox)); |
+ |
+ SkAutoTDelete<State> shaderState(inState); |
if (shaderState.get()->fType == SkShader::kNone_GradientType && |
shaderState.get()->fImage.isNull()) { |
// TODO(vandebo) This drops SKComposeShader on the floor. We could |
@@ -506,10 +546,17 @@ SkPDFObject* SkPDFShader::GetPDFShader(const SkShader& shader, |
valid = imageShader->isValid(); |
result = imageShader; |
} else { |
- SkPDFFunctionShader* functionShader = |
- new SkPDFFunctionShader(shaderState.detach()); |
- valid = functionShader->isValid(); |
- result = functionShader; |
+ if (shaderState.get()->GradientHasAlpha()) { |
+ SkPDFAlphaFunctionShader* gradientShader = |
+ new SkPDFAlphaFunctionShader(shaderState.detach()); |
+ valid = gradientShader->isValid(); |
+ result = gradientShader; |
+ } else { |
+ SkPDFFunctionShader* functionShader = |
+ new SkPDFFunctionShader(shaderState.detach()); |
+ valid = functionShader->isValid(); |
+ result = functionShader; |
+ } |
} |
if (!valid) { |
delete result; |
@@ -521,6 +568,23 @@ SkPDFObject* SkPDFShader::GetPDFShader(const SkShader& shader, |
} |
// static |
+void SkPDFShader::RemoveShader(SkPDFObject* shader) { |
+ SkAutoMutexAcquire lock(CanonicalShadersMutex()); |
+ ShaderCanonicalEntry entry(shader, NULL); |
+ int index = CanonicalShaders().find(entry); |
+ SkASSERT(index >= 0); |
+ CanonicalShaders().removeShuffle(index); |
+} |
+ |
+// static |
+SkPDFObject* SkPDFShader::GetPDFShader(const SkShader& shader, |
+ const SkMatrix& matrix, |
+ const SkIRect& surfaceBBox) { |
+ SkAutoMutexAcquire lock(CanonicalShadersMutex()); |
+ return GetPDFShaderByState(new State(shader, matrix, surfaceBBox)); |
+} |
+ |
+// static |
SkTDArray<SkPDFShader::ShaderCanonicalEntry>& SkPDFShader::CanonicalShaders() { |
// This initialization is only thread safe with gcc. |
static SkTDArray<ShaderCanonicalEntry> gCanonicalShaders; |
@@ -554,6 +618,102 @@ SkPDFObject* SkPDFFunctionShader::RangeObject() { |
return range; |
} |
+static SkPDFResourceDict* get_gradient_resource_dict( |
+ SkPDFObject* functionShader, |
+ SkPDFObject* gState) { |
+ SkPDFResourceDict* dict = new SkPDFResourceDict(true); |
+ |
+ if (functionShader != NULL) { |
+ dict->insertResourceAsRef(SkPDFResourceDict::kPattern_ResourceType, 0, |
+ functionShader); |
+ } |
+ if (gState != NULL) { |
+ dict->insertResourceAsRef(SkPDFResourceDict::kExtGState_ResourceType, 0, |
+ gState); |
+ } |
+ |
+ return dict; |
+} |
+ |
+static void populate_tiling_pattern_dict(SkPDFDict* pattern, |
+ SkRect& bbox, SkPDFDict* resources, |
+ const SkMatrix& matrix) { |
+ pattern->insertName("Type", "Pattern"); |
+ pattern->insertInt("PatternType", 1); |
+ pattern->insertInt("PaintType", 1); |
+ pattern->insertInt("TilingType", 1); |
+ pattern->insert("BBox", SkPDFUtils::RectToArray(bbox))->unref(); |
+ pattern->insertScalar("XStep", bbox.width()); |
+ pattern->insertScalar("YStep", bbox.height()); |
+ pattern->insert("Resources", resources); |
+ if (!matrix.isIdentity()) { |
+ pattern->insert("Matrix", SkPDFUtils::MatrixToArray(matrix))->unref(); |
+ } |
+} |
+ |
+/** |
+ * Creates a content stream which fills the pattern P0 across bounds. |
+ * The argument setup may contain any relevant PDF content stream operators |
+ * and will be prepended to the output content stream. |
+ */ |
+static SkData* create_pattern_fill_content(const char setup[], SkRect& bounds) { |
+ SkDynamicMemoryWStream content; |
+ if (NULL != setup) { |
+ content.writeText(setup); |
+ } |
+ content.writeText("/Pattern CS/Pattern cs/P0 SCN/P0 scn\n"); |
+ |
+ SkPDFUtils::AppendRectangle(bounds, &content); |
+ SkPDFUtils::PaintPath(SkPaint::kFill_Style, SkPath::kEvenOdd_FillType, |
+ &content); |
+ |
+ return content.copyToData(); |
+} |
+ |
+/** |
+ * Creates a ExtGState with the SMask set to the luminosityShader in |
+ * luminosity mode. The shader pattern extends to the bbox. |
+ */ |
+SkPDFGraphicState* SkPDFAlphaFunctionShader::CreateSMaskGraphicState( |
vandebo (ex-Chrome)
2013/07/12 21:41:44
Instead of passing in state, use fState.
Instead o
ducky
2013/07/12 23:32:34
Done.
|
+ SkPDFShader::State* alphaState, SkRect& bbox) { |
+ SkAutoTUnref<SkPDFObject> luminosityShader(SkPDFShader::GetPDFShaderByState( |
vandebo (ex-Chrome)
2013/07/12 21:41:44
This might be easier to read as:
SkAutoTUnref<
ducky
2013/07/12 23:32:34
Done. Using first since there are issues with the
|
+ alphaState->CreateAlphaToLuminosityState())); |
+ |
+ SkAutoTUnref<SkData> alphaStream(create_pattern_fill_content(NULL, bbox)); |
+ |
+ SkAutoTUnref<SkPDFResourceDict> |
+ resources(get_gradient_resource_dict(luminosityShader, NULL)); |
+ |
+ SkAutoTUnref<SkPDFFormXObject> alphaMask( |
+ new SkPDFFormXObject(alphaStream.get(), bbox, resources.get())); |
+ |
+ return SkPDFGraphicState::GetSMaskGraphicState( |
+ alphaMask.get(), false, |
+ SkPDFGraphicState::kLuminosity_SMaskMode); |
+} |
+ |
+SkPDFAlphaFunctionShader::SkPDFAlphaFunctionShader(SkPDFShader::State* state) : |
+ fState(state) { |
vandebo (ex-Chrome)
2013/07/12 21:41:44
nit: : fState(state) {
ducky
2013/07/12 23:32:34
Done.
|
+ SkRect bbox; |
+ bbox.set(fState.get()->fBBox); |
+ |
+ fColorPattern.reset( |
vandebo (ex-Chrome)
2013/07/12 21:41:44
nit: Maybe fColorShader would be a better name, no
ducky
2013/07/12 23:32:34
Done.
|
+ SkPDFShader::GetPDFShaderByState(state->CreateOpaqueState())); |
+ |
+ SkAutoTUnref<SkPDFGraphicState> alphaGs( |
+ CreateSMaskGraphicState(state, bbox)); |
+ |
+ fResourceDict.reset( |
+ get_gradient_resource_dict(fColorPattern.get(), alphaGs.get())); |
+ |
+ SkAutoTUnref<SkData> colorStream( |
+ create_pattern_fill_content("/G0 gs", bbox)); |
vandebo (ex-Chrome)
2013/07/12 21:41:44
you'll need to rebase this on the recent resource
ducky
2013/07/12 23:32:34
Done.
|
+ setData(colorStream.get()); |
+ |
+ populate_tiling_pattern_dict(this, bbox, fResourceDict.get(), |
+ SkMatrix::I()); |
+} |
+ |
SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state) |
: SkPDFDict("Pattern"), |
fState(state) { |
@@ -829,28 +989,14 @@ SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) : fState(state) { |
} |
} |
- SkAutoTUnref<SkPDFArray> patternBBoxArray(new SkPDFArray); |
- patternBBoxArray->reserve(4); |
- patternBBoxArray->appendScalar(patternBBox.fLeft); |
- patternBBoxArray->appendScalar(patternBBox.fTop); |
- patternBBoxArray->appendScalar(patternBBox.fRight); |
- patternBBoxArray->appendScalar(patternBBox.fBottom); |
- |
// Put the canvas into the pattern stream (fContent). |
SkAutoTUnref<SkData> content(pattern.copyContentToData()); |
setData(content.get()); |
SkPDFResourceDict* resourceDict = pattern.getResourceDict(); |
resourceDict->getResources(fResources, &fResources, false); |
- insertName("Type", "Pattern"); |
- insertInt("PatternType", 1); |
- insertInt("PaintType", 1); |
- insertInt("TilingType", 1); |
- insert("BBox", patternBBoxArray.get()); |
- insertScalar("XStep", patternBBox.width()); |
- insertScalar("YStep", patternBBox.height()); |
- insert("Resources", resourceDict); |
- insert("Matrix", SkPDFUtils::MatrixToArray(finalMatrix))->unref(); |
+ populate_tiling_pattern_dict(this, patternBBox, |
+ pattern.getResourceDict(), finalMatrix); |
fState.get()->fImage.unlockPixels(); |
} |
@@ -956,11 +1102,87 @@ SkPDFShader::State::State(const SkShader& shader, |
SkASSERT(matrix.isIdentity()); |
fPixelGeneration = fImage.getGenerationID(); |
} else { |
- fColorData.set(sk_malloc_throw( |
- fInfo.fColorCount * (sizeof(SkColor) + sizeof(SkScalar)))); |
- fInfo.fColors = reinterpret_cast<SkColor*>(fColorData.get()); |
- fInfo.fColorOffsets = |
- reinterpret_cast<SkScalar*>(fInfo.fColors + fInfo.fColorCount); |
+ AllocateGradientInfoStorage(); |
shader.asAGradient(&fInfo); |
} |
} |
+ |
+SkPDFShader::State::State(const SkPDFShader::State& other) |
+ : fType(other.fType), |
+ fCanvasTransform(other.fCanvasTransform), |
+ fShaderTransform(other.fShaderTransform), |
+ fBBox(other.fBBox) |
+{ |
+ // Only gradients supported for now, since that is all that is used. |
+ // If needed, image state copy constructor can be added here later. |
+ SkASSERT(fType != SkShader::kNone_GradientType); |
+ |
+ if (fType != SkShader::kNone_GradientType) { |
+ fInfo = other.fInfo; |
+ |
+ AllocateGradientInfoStorage(); |
+ for (int i = 0; i < fInfo.fColorCount; i++) { |
+ fInfo.fColors[i] = other.fInfo.fColors[i]; |
+ fInfo.fColorOffsets[i] = other.fInfo.fColorOffsets[i]; |
+ } |
+ } |
+} |
+ |
+/** |
+ * Create a copy of this gradient state with alpha assigned to RGB luminousity. |
+ * Only valid for gradient states. |
+ */ |
+SkPDFShader::State* SkPDFShader::State::CreateAlphaToLuminosityState() const { |
+ SkASSERT(fType != SkShader::kNone_GradientType); |
+ |
+ SkPDFShader::State* newState = new SkPDFShader::State(*this); |
+ |
+ for (int i = 0; i < fInfo.fColorCount; i++) { |
+ SkAlpha alpha = SkColorGetA(fInfo.fColors[i]); |
+ newState->fInfo.fColors[i] = SkColorSetARGB(255, alpha, alpha, alpha); |
+ } |
+ |
+ return newState; |
+} |
+ |
+/** |
+ * Create a copy of this gradient state with alpha set to fully opaque |
+ * Only valid for gradient states. |
+ */ |
+SkPDFShader::State* SkPDFShader::State::CreateOpaqueState() const { |
+ SkASSERT(fType != SkShader::kNone_GradientType); |
+ |
+ SkPDFShader::State* newState = new SkPDFShader::State(*this); |
+ SkAlpha opaque = SkColorGetA(SK_ColorBLACK); |
+ for (int i = 0; i < fInfo.fColorCount; i++) { |
+ newState->fInfo.fColors[i] = SkColorSetA(fInfo.fColors[i], opaque); |
+ } |
+ |
+ return newState; |
+} |
+ |
+/** |
+ * Returns true if state is a gradient and the gradient has alpha. |
+ */ |
+bool SkPDFShader::State::GradientHasAlpha() const { |
+ if (fType == SkShader::kNone_GradientType) { |
+ return false; |
+ } |
+ |
+ SkAlpha opaque = SkColorGetA(SK_ColorBLACK); |
+ for (int i = 0; i < fInfo.fColorCount; i++) { |
+ SkAlpha alpha = SkColorGetA(fInfo.fColors[i]); |
+ if (alpha != opaque) { |
+ return true; |
+ } |
+ } |
+ return false; |
+} |
+ |
+void SkPDFShader::State::AllocateGradientInfoStorage() { |
+ fColorData.set(sk_malloc_throw( |
+ fInfo.fColorCount * (sizeof(SkColor) + sizeof(SkScalar)))); |
+ fInfo.fColors = reinterpret_cast<SkColor*>(fColorData.get()); |
+ fInfo.fColorOffsets = |
+ reinterpret_cast<SkScalar*>(fInfo.fColors + fInfo.fColorCount); |
+} |