Index: src/pdf/SkPDFShader.cpp |
diff --git a/src/pdf/SkPDFShader.cpp b/src/pdf/SkPDFShader.cpp |
index a0dffb7791fc4724ecc5a16403fc4b4b2efa6b58..df674f175a3a5e4a641b9ebac387dd455eb10e14 100644 |
--- a/src/pdf/SkPDFShader.cpp |
+++ b/src/pdf/SkPDFShader.cpp |
@@ -13,12 +13,15 @@ |
#include "SkData.h" |
#include "SkPDFCatalog.h" |
#include "SkPDFDevice.h" |
+#include "SkPDFFormXObject.h" |
+#include "SkPDFGraphicState.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) { |
@@ -398,7 +401,7 @@ class SkPDFShader::State { |
public: |
SkShader::GradientType fType; |
SkShader::GradientInfo fInfo; |
- SkAutoFree fColorData; |
+ SkAutoFree fColorData; // this provides storage for arrays in fInfo |
vandebo (ex-Chrome)
2013/07/03 17:07:01
Full sentences please.
ducky
2013/07/03 23:32:09
Done.
|
SkMatrix fCanvasTransform; |
SkMatrix fShaderTransform; |
SkIRect fBBox; |
@@ -409,7 +412,13 @@ public: |
explicit State(const SkShader& shader, const SkMatrix& canvasTransform, |
const SkIRect& bbox); |
+ State(const State& other); |
vandebo (ex-Chrome)
2013/07/03 17:07:01
Make this private.
ducky
2013/07/03 23:32:09
Done.
|
+ |
+ SkPDFShader::State* CreateAlphaToRGBState() const; |
vandebo (ex-Chrome)
2013/07/03 17:07:01
nit: CreateLuminosityState()?
vandebo (ex-Chrome)
2013/07/03 17:07:01
nit: Put these methods after the operator== method
ducky
2013/07/03 23:32:09
Changed to CreateAlphaToLuminosityState(). I think
ducky
2013/07/03 23:32:09
Done.
|
+ SkPDFShader::State* CreateOpaqueState() const; |
bool operator==(const State& b) const; |
+ |
+ bool GradientHasAlpha() const; |
}; |
class SkPDFFunctionShader : public SkPDFDict, public SkPDFShader { |
@@ -431,6 +440,8 @@ public: |
newResourceObjects); |
} |
+ SkPDFFunctionShader* CreateAlphaToRGBShader(); |
+ |
private: |
static SkPDFObject* RangeObject(); |
@@ -440,6 +451,37 @@ 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 SkPDFGradientShader : public SkPDFStream, public SkPDFShader { |
+public: |
+ explicit SkPDFGradientShader(SkPDFShader::State* state); |
+ virtual ~SkPDFGradientShader() { |
+ RemoveShader(this); |
+ } |
+ |
+ virtual bool isValid() { return size() > 0; } |
+ |
+private: |
+ SkAutoTDelete<const SkPDFShader::State> fState; |
+ |
+ void getResources(const SkTSet<SkPDFObject*>& knownResourceObjects, |
+ SkTSet<SkPDFObject*>* newResourceObjects) { |
+ GetResourcesHelper(&fResources, |
+ knownResourceObjects, |
+ newResourceObjects); |
+ } |
+ SkTDArray<SkPDFObject*> fResources; |
+ |
+ SkAutoTUnref<SkPDFObject> fColorPattern; |
+ SkAutoTUnref<SkPDFGraphicState> fAlphaGs; |
+ |
+ SkAutoTUnref<SkPDFDict> fResourceDict; |
+}; |
+ |
class SkPDFImageShader : public SkPDFStream, public SkPDFShader { |
public: |
explicit SkPDFImageShader(SkPDFShader::State* state); |
@@ -474,12 +516,11 @@ void SkPDFShader::RemoveShader(SkPDFObject* shader) { |
} |
// 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) { |
vandebo (ex-Chrome)
2013/07/03 17:07:01
Definition order should match declaration order, s
ducky
2013/07/03 23:32:09
Done.
vandebo (ex-Chrome)
2013/07/08 19:02:25
Not done
|
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 |
@@ -505,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()) { |
+ SkPDFGradientShader* gradientShader = |
+ new SkPDFGradientShader(shaderState.detach()); |
+ valid = gradientShader->isValid(); |
+ result = gradientShader; |
+ } else { |
+ SkPDFFunctionShader* functionShader = |
+ new SkPDFFunctionShader(shaderState.detach()); |
+ valid = functionShader->isValid(); |
+ result = functionShader; |
+ } |
} |
if (!valid) { |
delete result; |
@@ -520,6 +568,14 @@ SkPDFObject* SkPDFShader::GetPDFShader(const SkShader& shader, |
} |
// 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; |
@@ -553,6 +609,95 @@ SkPDFObject* SkPDFFunctionShader::RangeObject() { |
return range; |
} |
+static SkPDFDict* getGradientResourceDict( |
+ SkPDFObject* functionShader, |
+ SkPDFObject* gState) { |
+ SkPDFDict* dict = new SkPDFDict(); |
+ |
+ if (functionShader != NULL) { |
+ SkAutoTUnref<SkPDFDict> patterns(new SkPDFDict()); |
+ patterns->insert("P0", new SkPDFObjRef(functionShader))->unref(); |
+ dict->insert("Pattern", patterns.get()); |
+ } |
+ |
+ if (gState != NULL) { |
+ SkAutoTUnref<SkPDFDict> extGState(new SkPDFDict()); |
+ extGState->insert("G0", new SkPDFObjRef(gState))->unref(); |
+ dict->insert("ExtGState", extGState.get()); |
+ } |
+ |
vandebo (ex-Chrome)
2013/07/03 17:07:01
Should this also include a ProcSet entry?
ducky
2013/07/03 23:32:09
Done.
|
+ return dict; |
+} |
+ |
+static void writePatternFillContent(SkWStream* content, SkRect bounds) { |
+ // set pattern |
+ content->writeText("/Pattern CS /Pattern cs /P0 SCN /P0 scn "); |
vandebo (ex-Chrome)
2013/07/03 17:07:01
nit: omit extra whitespace.
ducky
2013/07/03 23:32:09
Done.
|
+ // draw rectangle and fill |
+ content->writeScalarAsText(bounds.left()); |
vandebo (ex-Chrome)
2013/07/03 17:07:01
SkPDFUtils::AppendRectangle()
SkPDFUtils::PaintPat
ducky
2013/07/03 23:32:09
Done.
|
+ content->writeText(" "); |
+ content->writeScalarAsText(bounds.top()); |
+ content->writeText(" "); |
+ content->writeScalarAsText(bounds.right()); |
+ content->writeText(" "); |
+ content->writeScalarAsText(bounds.bottom()); |
+ content->writeText(" re f"); |
+} |
+ |
+SkPDFGradientShader::SkPDFGradientShader(SkPDFShader::State* state) : fState(state) { |
+ SkRect bbox; |
+ bbox.set(fState.get()->fBBox); |
+ |
+ fColorPattern.reset(SkPDFShader::GetPDFShaderByState( |
+ state->CreateOpaqueState())); |
+ fResources.push(fColorPattern.get()); |
+ |
+ // create alpha graphics state, including SMask and alpha luminosity pattern |
+ SkAutoTUnref<SkPDFObject> |
vandebo (ex-Chrome)
2013/07/03 17:07:01
nit indent:
SkAutoTUnref<SkPDFObject> alphaSha
ducky
2013/07/03 23:32:09
Done.
|
+ alphaShader(SkPDFShader::GetPDFShaderByState( |
+ state->CreateAlphaToRGBState())); |
+ SkAutoTUnref<SkPDFDict> |
+ alphaResources(getGradientResourceDict(alphaShader.get(), NULL)); |
+ SkTSet<SkPDFObject*> resourcesArray; |
+ resourcesArray.add(alphaShader.get()); |
+ |
+ SkAutoTDelete<SkDynamicMemoryWStream> alphaContent(new SkDynamicMemoryWStream()); |
+ writePatternFillContent(alphaContent.get(), bbox); |
+ SkAutoTUnref<SkData> alphaData(alphaContent->copyToData()); |
vandebo (ex-Chrome)
2013/07/03 17:07:01
This mess is caused by incomplete conversion of Sk
ducky
2013/07/03 23:32:09
Done.
|
+ SkAutoTUnref<SkMemoryStream> alphaStream(new SkMemoryStream()); |
+ alphaStream->setData(alphaData.get()); |
+ SkAutoTUnref<SkPDFFormXObject> |
+ alphaMask(new SkPDFFormXObject(alphaStream.get(), alphaResources.get(), resourcesArray, bbox)); |
+ |
+ fAlphaGs.reset(SkPDFGraphicState::GetSMaskGraphicState(alphaMask.get(), false, false)); |
vandebo (ex-Chrome)
2013/07/03 17:07:01
Not sure, but I think you want to pull this all ou
ducky
2013/07/03 23:32:09
Done.
|
+ fResources.push(fAlphaGs.get()); |
+ |
+ fResourceDict.reset(getGradientResourceDict(fColorPattern.get(), fAlphaGs.get())); |
+ |
+ SkAutoTUnref<SkPDFArray> bboxArray(new SkPDFArray); |
vandebo (ex-Chrome)
2013/07/03 17:07:01
Use rectToArray here as well.
ducky
2013/07/03 23:32:09
Done.
|
+ bboxArray->reserve(4); |
+ bboxArray->appendScalar(bbox.fLeft); |
+ bboxArray->appendScalar(bbox.fTop); |
+ bboxArray->appendScalar(bbox.fRight); |
+ bboxArray->appendScalar(bbox.fBottom); |
+ |
+ SkAutoTDelete<SkDynamicMemoryWStream> colorContent(new SkDynamicMemoryWStream()); |
+ colorContent->writeText("/G0 gs "); |
+ writePatternFillContent(colorContent.get(), bbox); |
vandebo (ex-Chrome)
2013/07/03 17:07:01
Make this method return an SkDyrnamicMemoryWStream
ducky
2013/07/03 23:32:09
Done.
|
+ SkAutoTUnref<SkData> colorData(colorContent->copyToData()); |
+ SkAutoTUnref<SkMemoryStream> colorStream(new SkMemoryStream()); |
+ colorStream->setData(colorData.get()); |
+ setData(colorStream.get()); |
+ |
+ insertName("Type", "Pattern"); |
vandebo (ex-Chrome)
2013/07/03 17:07:01
Most of this can get pulled out and shared with Sk
ducky
2013/07/03 23:32:09
Done.
|
+ insertInt("PatternType", 1); |
+ insertInt("PaintType", 1); |
+ insertInt("TilingType", 1); |
+ insert("BBox", bboxArray.get()); |
+ insertScalar("XStep", bbox.width()); |
+ insertScalar("YStep", bbox.height()); |
+ insert("Resources", fResourceDict.get()); |
+} |
+ |
SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state) |
: SkPDFDict("Pattern"), |
fState(state) { |
@@ -659,6 +804,16 @@ SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state) |
insert("Shading", pdfShader.get()); |
} |
+/** |
+ * Creates a copy of this gradient PDF shader, with the alpha channel |
+ * mapped to RGB luminosity. Intended to be used as a SMask for gradients |
+ * with transparency. |
+ */ |
+SkPDFFunctionShader* SkPDFFunctionShader::CreateAlphaToRGBShader() { |
vandebo (ex-Chrome)
2013/07/03 17:07:01
This isn't used, remove.
ducky
2013/07/03 23:32:09
D'oh.
|
+ // copy the current state, allocating new storage |
+ return new SkPDFFunctionShader(fState.get()->CreateAlphaToRGBState()); |
+} |
+ |
SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) : fState(state) { |
fState.get()->fImage.lockPixels(); |
@@ -962,3 +1117,80 @@ SkPDFShader::State::State(const SkShader& shader, |
shader.asAGradient(&fInfo); |
} |
} |
+ |
+SkPDFShader::State::State(const SkPDFShader::State& other) |
+ : fType(other.fType), |
+ fCanvasTransform(other.fCanvasTransform), |
+ fShaderTransform(other.fShaderTransform), |
+ fBBox(other.fBBox) |
vandebo (ex-Chrome)
2013/07/03 17:07:01
Also need to copy over fImageTileModes
ducky
2013/07/03 23:32:09
That seems to be for image shaders only, and that
|
+{ |
+ if (fType != SkShader::kNone_GradientType) { |
+ fInfo = other.fInfo; |
+ |
+ // allocate new storage to prevent pointer aliasing |
vandebo (ex-Chrome)
2013/07/03 17:07:01
nit: comment unnecessary.
ducky
2013/07/03 23:32:09
I would actually say that this piece of code is pr
|
+ fColorData.set(sk_malloc_throw( |
vandebo (ex-Chrome)
2013/07/03 17:07:01
All this allocation is the same as the other const
ducky
2013/07/03 23:32:09
Done.
|
+ fInfo.fColorCount * (sizeof(SkColor) + sizeof(SkScalar)))); |
+ fInfo.fColors = reinterpret_cast<SkColor*>(fColorData.get()); |
+ fInfo.fColorOffsets = |
+ reinterpret_cast<SkScalar*>(fInfo.fColors + fInfo.fColorCount); |
+ for (int i = 0; i < fInfo.fColorCount; i++) { |
+ fInfo.fColors[i] = other.fInfo.fColors[i]; |
+ fInfo.fColorOffsets[i] = other.fInfo.fColorOffsets[i]; |
+ } |
+ } else { |
+ // if needed, image state copy constructor can be added here later... |
+ // but only gradients supported for now |
+ SkASSERT(fType != SkShader::kNone_GradientType); |
vandebo (ex-Chrome)
2013/07/03 17:07:01
Put this after line 1126
ducky
2013/07/03 23:32:09
Done.
|
+ } |
+} |
+ |
+/** |
+ * Create a copy of this gradient state with alpha assigned to RGB luminousity. |
+ * Only valid for gradient states! |
+ */ |
+SkPDFShader::State* SkPDFShader::State::CreateAlphaToRGBState() 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 = 1; i < fInfo.fColorCount; i++) { |
+ SkAlpha alpha = SkColorGetA(fInfo.fColors[i]); |
+ if (alpha != opaque) { |
+ return true; |
+ } |
+ } |
+ return false; |
+} |