Chromium Code Reviews| Index: src/pdf/SkPDFShader.cpp |
| diff --git a/src/pdf/SkPDFShader.cpp b/src/pdf/SkPDFShader.cpp |
| index 618fb6e8c3386b0049f4659d3536ce9d3c4cb08a..e5ed3895b8ffd1ca119d8da48eb66e78b9202bbb 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, |
| + 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->getResources(knownResourceObjects, |
| + newResourceObjects, |
| + true); |
| + } |
| + |
| + SkAutoTUnref<SkPDFObject> fColorShader; |
| + SkAutoTUnref<SkPDFResourceDict> fResourceDict; |
| +}; |
| + |
| class SkPDFImageShader : public SkPDFStream, public SkPDFShader { |
| public: |
| explicit SkPDFImageShader(SkPDFShader::State* state); |
| @@ -466,21 +513,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 |
| @@ -506,10 +542,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 +564,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 +614,114 @@ SkPDFObject* SkPDFFunctionShader::RangeObject() { |
| return range; |
| } |
| +static SkPDFResourceDict* get_gradient_resource_dict( |
| + SkPDFObject* functionShader, |
| + SkPDFObject* gState) { |
| + SkPDFResourceDict* dict = new SkPDFResourceDict(); |
| + |
| + 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/"); |
| + content.writeText(SkPDFResourceDict::getResourceName( |
| + SkPDFResourceDict::kPattern_ResourceType, 0).c_str()); |
| + content.writeText(" SCN/"); |
| + content.writeText(SkPDFResourceDict::getResourceName( |
| + SkPDFResourceDict::kPattern_ResourceType, 0).c_str()); |
| + content.writeText(" 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() { |
| + SkRect bbox; |
| + bbox.set(fState.get()->fBBox); |
| + |
| + SkAutoTUnref<SkPDFObject> luminosityShader( |
| + SkPDFShader::GetPDFShaderByState( |
| + fState->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) { |
| + SkRect bbox; |
| + bbox.set(fState.get()->fBBox); |
| + |
| + fColorShader.reset( |
| + SkPDFShader::GetPDFShaderByState(state->CreateOpaqueState())); |
| + |
| + SkAutoTUnref<SkPDFGraphicState> alphaGs(CreateSMaskGraphicState()); |
| + fResourceDict.reset( |
| + get_gradient_resource_dict(fColorShader.get(), alphaGs.get())); |
| + |
| + SkString setGs = SkString(); |
|
vandebo (ex-Chrome)
2013/07/15 16:40:06
nit: This has changed enough that I think the orig
ducky
2013/07/15 18:04:10
Done.
|
| + setGs.append("/"); |
| + setGs.append(SkPDFResourceDict::getResourceName( |
| + SkPDFResourceDict::kExtGState_ResourceType, 0).c_str()); |
| + setGs.append(" gs"); |
| + SkAutoTUnref<SkData> colorStream( |
| + create_pattern_fill_content(setGs.c_str(), 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 +997,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 +1110,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); |
| +} |