| Index: src/pdf/SkPDFShader.cpp
|
| diff --git a/src/pdf/SkPDFShader.cpp b/src/pdf/SkPDFShader.cpp
|
| index 618fb6e8c3386b0049f4659d3536ce9d3c4cb08a..9d5c0eee3e50a003a953adf3576b63b0f4aef300 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) { return State(rhs); }
|
| + void AllocateGradientInfoStorage();
|
| };
|
|
|
| class SkPDFFunctionShader : public SkPDFDict, public SkPDFShader {
|
| @@ -441,6 +455,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 fColorPattern.get() != NULL;
|
| + }
|
| +
|
| +private:
|
| + SkAutoTDelete<const SkPDFShader::State> fState;
|
| +
|
| + void getResources(const SkTSet<SkPDFObject*>& knownResourceObjects,
|
| + SkTSet<SkPDFObject*>* newResourceObjects) {
|
| + fResourceDict->getResources(knownResourceObjects,
|
| + newResourceObjects,
|
| + true);
|
| + }
|
| +
|
| + SkAutoTUnref<SkPDFObject> fColorPattern;
|
| + SkAutoTUnref<SkPDFGraphicState> fAlphaGs;
|
| +
|
| + SkAutoTUnref<SkPDFResourceDict> fResourceDict;
|
| +};
|
| +
|
| class SkPDFImageShader : public SkPDFStream, public SkPDFShader {
|
| public:
|
| explicit SkPDFImageShader(SkPDFShader::State* state);
|
| @@ -466,21 +514,11 @@ 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 +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 =
|
| + 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 +566,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 +616,94 @@ 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.
|
| + */
|
| +static SkPDFGraphicState* create_smask_graphic_state(SkPDFObject* luminosityShader, SkRect& bbox) {
|
| + 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) {
|
| + SkRect bbox;
|
| + bbox.set(fState.get()->fBBox);
|
| +
|
| + fColorPattern.reset(SkPDFShader::GetPDFShaderByState(state->CreateOpaqueState()));
|
| +
|
| + SkAutoTUnref<SkPDFObject> luminosityShader(SkPDFShader::GetPDFShaderByState(
|
| + state->CreateAlphaToLuminosityState()));
|
| + fAlphaGs.reset(create_smask_graphic_state(luminosityShader.get(), bbox));
|
| +
|
| + fResourceDict.reset(get_gradient_resource_dict(fColorPattern.get(), fAlphaGs.get()));
|
| +
|
| + SkAutoTUnref<SkData> colorStream(create_pattern_fill_content("/G0 gs", bbox));
|
| + setData(colorStream.get());
|
| +
|
| + populate_tiling_pattern_dict(this, bbox, fResourceDict.get(), SkMatrix::I());
|
| +}
|
| +
|
| SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state)
|
| : SkPDFDict("Pattern"),
|
| fState(state) {
|
| @@ -829,28 +979,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 +1092,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);
|
| +}
|
|
|