| Index: src/pdf/SkPDFShader.cpp
|
| diff --git a/src/pdf/SkPDFShader.cpp b/src/pdf/SkPDFShader.cpp
|
| index ef41c017cea37e6f82a2252ba663fdee84b838bf..9394f1b9597c67d25e77a99af74e2bf938fdd4c8 100644
|
| --- a/src/pdf/SkPDFShader.cpp
|
| +++ b/src/pdf/SkPDFShader.cpp
|
| @@ -13,13 +13,15 @@
|
| #include "SkData.h"
|
| #include "SkPDFCatalog.h"
|
| #include "SkPDFDevice.h"
|
| -#include "SkPDFTypes.h"
|
| +#include "SkPDFFormXObject.h"
|
| +#include "SkPDFGraphicState.h"
|
| #include "SkPDFResourceDict.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 +401,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 +410,20 @@ public:
|
| uint32_t fPixelGeneration;
|
| SkShader::TileMode fImageTileModes[2];
|
|
|
| - explicit State(const SkShader& shader, const SkMatrix& canvasTransform,
|
| - const SkIRect& bbox);
|
| + 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 +454,40 @@ 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 fColorShader.get() != NULL;
|
| + }
|
| +
|
| +private:
|
| + SkAutoTDelete<const SkPDFShader::State> fState;
|
| +
|
| + SkPDFGraphicState* CreateSMaskGraphicState();
|
| +
|
| + void getResources(const SkTSet<SkPDFObject*>& knownResourceObjects,
|
| + SkTSet<SkPDFObject*>* newResourceObjects) {
|
| + fResourceDict->getReferencedResources(knownResourceObjects,
|
| + newResourceObjects,
|
| + true);
|
| + }
|
| +
|
| + SkAutoTUnref<SkPDFObject> fColorShader;
|
| + SkAutoTUnref<SkPDFResourceDict> fResourceDict;
|
| +};
|
| +
|
| class SkPDFImageShader : public SkPDFStream, public SkPDFShader {
|
| public:
|
| explicit SkPDFImageShader(SkPDFShader::State* state);
|
| @@ -468,21 +515,10 @@ 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) {
|
| +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
|
| @@ -508,10 +544,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 =
|
| + SkNEW_ARGS(SkPDFAlphaFunctionShader, (shaderState.detach()));
|
| + valid = gradientShader->isValid();
|
| + result = gradientShader;
|
| + } else {
|
| + SkPDFFunctionShader* functionShader =
|
| + SkNEW_ARGS(SkPDFFunctionShader, (shaderState.detach()));
|
| + valid = functionShader->isValid();
|
| + result = functionShader;
|
| + }
|
| }
|
| if (!valid) {
|
| delete result;
|
| @@ -523,6 +566,24 @@ 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(
|
| + SkNEW_ARGS(State, (shader, matrix, surfaceBBox)));
|
| +}
|
| +
|
| +// static
|
| SkTDArray<SkPDFShader::ShaderCanonicalEntry>& SkPDFShader::CanonicalShaders() {
|
| // This initialization is only thread safe with gcc.
|
| static SkTDArray<ShaderCanonicalEntry> gCanonicalShaders;
|
| @@ -556,6 +617,108 @@ SkPDFObject* SkPDFFunctionShader::RangeObject() {
|
| return range;
|
| }
|
|
|
| +static SkPDFResourceDict* get_gradient_resource_dict(
|
| + SkPDFObject* functionShader,
|
| + SkPDFObject* gState) {
|
| + SkPDFResourceDict* dict = new SkPDFResourceDict();
|
| +
|
| + if (functionShader != NULL) {
|
| + dict->insertResourceAsReference(
|
| + SkPDFResourceDict::kPattern_ResourceType, 0, functionShader);
|
| + }
|
| + if (gState != NULL) {
|
| + dict->insertResourceAsReference(
|
| + SkPDFResourceDict::kExtGState_ResourceType, 0, gState);
|
| + }
|
| +
|
| + return dict;
|
| +}
|
| +
|
| +static void populate_tiling_pattern_dict(SkPDFDict* pattern,
|
| + SkRect& bbox, SkPDFDict* resources,
|
| + const SkMatrix& matrix) {
|
| + const int kTiling_PatternType = 1;
|
| + const int kColoredTilingPattern_PaintType = 1;
|
| + const int kConstantSpacing_TilingType = 1;
|
| +
|
| + pattern->insertName("Type", "Pattern");
|
| + pattern->insertInt("PatternType", kTiling_PatternType);
|
| + pattern->insertInt("PaintType", kColoredTilingPattern_PaintType);
|
| + pattern->insertInt("TilingType", kConstantSpacing_TilingType);
|
| + 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.
|
| + * @param gsIndex A graphics state resource index to apply, or <0 if no
|
| + * graphics state to apply.
|
| + */
|
| +static SkStream* create_pattern_fill_content(int gsIndex, SkRect& bounds) {
|
| + SkDynamicMemoryWStream content;
|
| + if (gsIndex >= 0) {
|
| + SkPDFUtils::ApplyGraphicState(gsIndex, &content);
|
| + }
|
| + SkPDFUtils::ApplyPattern(0, &content);
|
| + SkPDFUtils::AppendRectangle(bounds, &content);
|
| + SkPDFUtils::PaintPath(SkPaint::kFill_Style, SkPath::kEvenOdd_FillType,
|
| + &content);
|
| +
|
| + return content.detachAsStream();
|
| +}
|
| +
|
| +/**
|
| + * Creates a ExtGState with the SMask set to the luminosityShader in
|
| + * luminosity mode. The shader pattern extends to the bbox.
|
| + */
|
| +SkPDFGraphicState* SkPDFAlphaFunctionShader::CreateSMaskGraphicState() {
|
| + SkRect bbox;
|
| + bbox.set(fState.get()->fBBox);
|
| +
|
| + SkAutoTUnref<SkPDFObject> luminosityShader(
|
| + SkPDFShader::GetPDFShaderByState(
|
| + fState->CreateAlphaToLuminosityState()));
|
| +
|
| + SkAutoTUnref<SkStream> alphaStream(create_pattern_fill_content(-1, 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) {
|
| + SkRect bbox;
|
| + bbox.set(fState.get()->fBBox);
|
| +
|
| + fColorShader.reset(
|
| + SkPDFShader::GetPDFShaderByState(state->CreateOpaqueState()));
|
| +
|
| + // Create resource dict with alpha graphics state as G0 and
|
| + // pattern shader as P0, then write content stream.
|
| + SkAutoTUnref<SkPDFGraphicState> alphaGs(CreateSMaskGraphicState());
|
| + fResourceDict.reset(
|
| + get_gradient_resource_dict(fColorShader.get(), alphaGs.get()));
|
| +
|
| + SkAutoTUnref<SkStream> colorStream(
|
| + create_pattern_fill_content(0, bbox));
|
| + setData(colorStream.get());
|
| +
|
| + populate_tiling_pattern_dict(this, bbox, fResourceDict.get(),
|
| + SkMatrix::I());
|
| +}
|
| +
|
| SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state)
|
| : SkPDFDict("Pattern"),
|
| fState(state) {
|
| @@ -831,28 +994,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<SkStream> content(pattern.content());
|
| setData(content.get());
|
| SkPDFResourceDict* resourceDict = pattern.getResourceDict();
|
| resourceDict->getReferencedResources(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();
|
| }
|
| @@ -958,11 +1107,86 @@ 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);
|
| + for (int i = 0; i < fInfo.fColorCount; i++) {
|
| + newState->fInfo.fColors[i] = SkColorSetA(fInfo.fColors[i],
|
| + SK_AlphaOPAQUE);
|
| + }
|
| +
|
| + 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;
|
| + }
|
| +
|
| + for (int i = 0; i < fInfo.fColorCount; i++) {
|
| + SkAlpha alpha = SkColorGetA(fInfo.fColors[i]);
|
| + if (alpha != SK_AlphaOPAQUE) {
|
| + 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);
|
| +}
|
|
|