Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(108)

Unified Diff: src/pdf/SkPDFShader.cpp

Issue 18585002: Implemented transparent gradients (Closed) Base URL: https://skia.googlecode.com/svn/trunk
Patch Set: Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« src/pdf/SkPDFGraphicState.cpp ('K') | « src/pdf/SkPDFShader.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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;
+}
« src/pdf/SkPDFGraphicState.cpp ('K') | « src/pdf/SkPDFShader.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698