| Index: src/pdf/SkPDFShader.cpp | 
| diff --git a/src/pdf/SkPDFShader.cpp b/src/pdf/SkPDFShader.cpp | 
| index 890bf447d5c46a8b0e4943ab0704bfef233195d7..2a144a6926f6a63a456cdb16038ea924d83c2e87 100644 | 
| --- a/src/pdf/SkPDFShader.cpp | 
| +++ b/src/pdf/SkPDFShader.cpp | 
| @@ -10,6 +10,7 @@ | 
| #include "SkPDFShader.h" | 
|  | 
| #include "SkData.h" | 
| +#include "SkPDFCanon.h" | 
| #include "SkPDFCatalog.h" | 
| #include "SkPDFDevice.h" | 
| #include "SkPDFFormXObject.h" | 
| @@ -509,13 +510,12 @@ class SkPDFFunctionShader : public SkPDFDict, public SkPDFShader { | 
| public: | 
| explicit SkPDFFunctionShader(SkPDFShader::State* state); | 
| virtual ~SkPDFFunctionShader() { | 
| -        if (isValid()) { | 
| -            RemoveShader(this); | 
| -        } | 
| +        SkPDFShader::RemoveFromCanonIfValid(this); | 
| fResources.unrefAll(); | 
| } | 
|  | 
| bool isValid() SK_OVERRIDE { return fResources.count() > 0; } | 
| +    SkPDFObject* toPDFObject() SK_OVERRIDE { return this; } | 
|  | 
| void getResources(const SkTSet<SkPDFObject*>& knownResourceObjects, | 
| SkTSet<SkPDFObject*>* newResourceObjects) SK_OVERRIDE { | 
| @@ -528,7 +528,6 @@ private: | 
| static SkPDFObject* RangeObject(); | 
|  | 
| SkTDArray<SkPDFObject*> fResources; | 
| -    SkAutoTDelete<const SkPDFShader::State> fState; | 
|  | 
| SkPDFStream* makePSFunction(const SkString& psCode, SkPDFArray* domain); | 
| typedef SkPDFDict INHERITED; | 
| @@ -543,18 +542,15 @@ class SkPDFAlphaFunctionShader : public SkPDFStream, public SkPDFShader { | 
| public: | 
| explicit SkPDFAlphaFunctionShader(SkPDFShader::State* state); | 
| virtual ~SkPDFAlphaFunctionShader() { | 
| -        if (isValid()) { | 
| -            RemoveShader(this); | 
| -        } | 
| +        SkPDFShader::RemoveFromCanonIfValid(this); | 
| } | 
|  | 
| bool isValid() SK_OVERRIDE { | 
| return fColorShader.get() != NULL; | 
| } | 
| +    SkPDFObject* toPDFObject() SK_OVERRIDE { return this; } | 
|  | 
| private: | 
| -    SkAutoTDelete<const SkPDFShader::State> fState; | 
| - | 
| SkPDFGraphicState* CreateSMaskGraphicState(); | 
|  | 
| void getResources(const SkTSet<SkPDFObject*>& knownResourceObjects, | 
| @@ -572,13 +568,12 @@ class SkPDFImageShader : public SkPDFStream, public SkPDFShader { | 
| public: | 
| explicit SkPDFImageShader(SkPDFShader::State* state); | 
| virtual ~SkPDFImageShader() { | 
| -        if (isValid()) { | 
| -            RemoveShader(this); | 
| -        } | 
| +        SkPDFShader::RemoveFromCanonIfValid(this); | 
| fResources.unrefAll(); | 
| } | 
|  | 
| bool isValid() SK_OVERRIDE { return size() > 0; } | 
| +    SkPDFObject* toPDFObject() SK_OVERRIDE { return this; } | 
|  | 
| void getResources(const SkTSet<SkPDFObject*>& knownResourceObjects, | 
| SkTSet<SkPDFObject*>* newResourceObjects) SK_OVERRIDE { | 
| @@ -589,18 +584,37 @@ public: | 
|  | 
| private: | 
| SkTSet<SkPDFObject*> fResources; | 
| -    SkAutoTDelete<const SkPDFShader::State> fState; | 
| }; | 
|  | 
| -SkPDFShader::SkPDFShader() {} | 
| +SkPDFShader::SkPDFShader(SkPDFShader::State* s) : fShaderState(s) {} | 
| + | 
| +SkPDFShader::~SkPDFShader() {} | 
| + | 
| +void SkPDFShader::RemoveFromCanonIfValid(SkPDFShader* shader) { | 
| +    if (shader->isValid()) { | 
| +        SkAutoMutexAcquire lock(SkPDFCanon::GetShaderMutex()); | 
| +        SkPDFCanon::GetCanon().removeShader(shader); | 
| +    } | 
| +} | 
| + | 
| +bool SkPDFShader::equals(const SkPDFShader::State& state) const { | 
| +    return state == *fShaderState.get(); | 
| +} | 
| + | 
| +SkPDFObject* SkPDFShader::AddToCanonIfValid(SkPDFShader* shader) { | 
| +    if (!shader->isValid()) { | 
| +        SkDELETE(shader); | 
| +        return NULL; | 
| +    } | 
| +    SkPDFCanon::GetCanon().addShader(shader); | 
| +    return shader->toPDFObject(); | 
| +} | 
|  | 
| // static | 
| SkPDFObject* SkPDFShader::GetPDFShaderByState(State* inState) { | 
| -    SkPDFObject* result; | 
| - | 
| -    SkAutoTDelete<State> shaderState(inState); | 
| -    if (shaderState.get()->fType == SkShader::kNone_GradientType && | 
| -            shaderState.get()->fImage.isNull()) { | 
| +    SkAutoTDelete<State> state(inState); | 
| +    if (state->fType == SkShader::kNone_GradientType && | 
| +        state->fImage.isNull()) { | 
| // TODO(vandebo) This drops SKComposeShader on the floor.  We could | 
| // handle compose shader by pulling things up to a layer, drawing with | 
| // the first shader, applying the xfer mode and drawing again with the | 
| @@ -608,50 +622,23 @@ SkPDFObject* SkPDFShader::GetPDFShaderByState(State* inState) { | 
| return NULL; | 
| } | 
|  | 
| -    ShaderCanonicalEntry entry(NULL, shaderState.get()); | 
| -    int index = CanonicalShaders().find(entry); | 
| -    if (index >= 0) { | 
| -        result = CanonicalShaders()[index].fPDFShader; | 
| -        result->ref(); | 
| -        return result; | 
| +    SkPDFShader* pdfShader = SkPDFCanon::GetCanon().findShader(*state); | 
| +    if (pdfShader) { | 
| +        SkASSERT(pdfShader->isValid()); | 
| +        return SkRef(pdfShader->toPDFObject()); | 
| } | 
|  | 
| -    bool valid = false; | 
| // The PDFShader takes ownership of the shaderSate. | 
| -    if (shaderState.get()->fType == SkShader::kNone_GradientType) { | 
| -        SkPDFImageShader* imageShader = | 
| -            new SkPDFImageShader(shaderState.detach()); | 
| -        valid = imageShader->isValid(); | 
| -        result = imageShader; | 
| +    if (state->fType == SkShader::kNone_GradientType) { | 
| +        return SkPDFShader::AddToCanonIfValid( | 
| +                SkNEW_ARGS(SkPDFImageShader, (state.detach()))); | 
| +    } else if (state->GradientHasAlpha()) { | 
| +        return SkPDFShader::AddToCanonIfValid( | 
| +                SkNEW_ARGS(SkPDFAlphaFunctionShader, (state.detach()))); | 
| } else { | 
| -        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; | 
| -        return NULL; | 
| +        return SkPDFShader::AddToCanonIfValid( | 
| +                SkNEW_ARGS(SkPDFFunctionShader, (state.detach()))); | 
| } | 
| -    entry.fPDFShader = result; | 
| -    CanonicalShaders().push(entry); | 
| -    return result;  // return the reference that came from new. | 
| -} | 
| - | 
| -// 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 | 
| @@ -659,27 +646,15 @@ SkPDFObject* SkPDFShader::GetPDFShader(const SkShader& shader, | 
| const SkMatrix& matrix, | 
| const SkIRect& surfaceBBox, | 
| SkScalar rasterScale) { | 
| -    SkAutoMutexAcquire lock(CanonicalShadersMutex()); | 
| +    SkAutoMutexAcquire lock(SkPDFCanon::GetShaderMutex()); | 
| return GetPDFShaderByState( | 
| SkNEW_ARGS(State, (shader, matrix, surfaceBBox, rasterScale))); | 
| } | 
|  | 
| -// static | 
| -SkTDArray<SkPDFShader::ShaderCanonicalEntry>& SkPDFShader::CanonicalShaders() { | 
| -    SkPDFShader::CanonicalShadersMutex().assertHeld(); | 
| -    static SkTDArray<ShaderCanonicalEntry> gCanonicalShaders; | 
| -    return gCanonicalShaders; | 
| -} | 
| - | 
| -SK_DECLARE_STATIC_MUTEX(gCanonicalShadersMutex); | 
| -// static | 
| -SkBaseMutex& SkPDFShader::CanonicalShadersMutex() { | 
| -    return gCanonicalShadersMutex; | 
| -} | 
|  | 
| // static | 
| SkPDFObject* SkPDFFunctionShader::RangeObject() { | 
| -    SkPDFShader::CanonicalShadersMutex().assertHeld(); | 
| +    SkPDFCanon::GetShaderMutex().assertHeld(); | 
| static SkPDFArray* range = NULL; | 
| // This method is only used with CanonicalShadersMutex, so it's safe to | 
| // populate domain. | 
| @@ -757,11 +732,10 @@ static SkStream* create_pattern_fill_content(int gsIndex, SkRect& bounds) { | 
| */ | 
| SkPDFGraphicState* SkPDFAlphaFunctionShader::CreateSMaskGraphicState() { | 
| SkRect bbox; | 
| -    bbox.set(fState.get()->fBBox); | 
| +    bbox.set(fShaderState->fBBox); | 
|  | 
| -    SkAutoTUnref<SkPDFObject> luminosityShader( | 
| -            SkPDFShader::GetPDFShaderByState( | 
| -                 fState->CreateAlphaToLuminosityState())); | 
| +    SkAutoTUnref<SkPDFObject> luminosityShader(SkPDFShader::GetPDFShaderByState( | 
| +            fShaderState->CreateAlphaToLuminosityState())); | 
|  | 
| SkAutoTUnref<SkStream> alphaStream(create_pattern_fill_content(-1, bbox)); | 
|  | 
| @@ -777,9 +751,9 @@ SkPDFGraphicState* SkPDFAlphaFunctionShader::CreateSMaskGraphicState() { | 
| } | 
|  | 
| SkPDFAlphaFunctionShader::SkPDFAlphaFunctionShader(SkPDFShader::State* state) | 
| -        : fState(state) { | 
| +    : SkPDFShader(state) { | 
| SkRect bbox; | 
| -    bbox.set(fState.get()->fBBox); | 
| +    bbox.set(fShaderState->fBBox); | 
|  | 
| fColorShader.reset( | 
| SkPDFShader::GetPDFShaderByState(state->CreateOpaqueState())); | 
| @@ -837,18 +811,17 @@ static bool split_perspective(const SkMatrix in, SkMatrix* affine, | 
| } | 
|  | 
| SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state) | 
| -        : SkPDFDict("Pattern"), | 
| -          fState(state) { | 
| +    : SkPDFDict("Pattern"), SkPDFShader(state) { | 
| SkString (*codeFunction)(const SkShader::GradientInfo& info, | 
| const SkMatrix& perspectiveRemover) = NULL; | 
| SkPoint transformPoints[2]; | 
|  | 
| // Depending on the type of the gradient, we want to transform the | 
| // coordinate space in different ways. | 
| -    const SkShader::GradientInfo* info = &fState.get()->fInfo; | 
| +    const SkShader::GradientInfo* info = &fShaderState->fInfo; | 
| transformPoints[0] = info->fPoint[0]; | 
| transformPoints[1] = info->fPoint[1]; | 
| -    switch (fState.get()->fType) { | 
| +    switch (fShaderState->fType) { | 
| case SkShader::kLinear_GradientType: | 
| codeFunction = &linearCode; | 
| break; | 
| @@ -893,8 +866,8 @@ SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state) | 
| SkMatrix mapperMatrix; | 
| unitToPointsMatrix(transformPoints, &mapperMatrix); | 
|  | 
| -    SkMatrix finalMatrix = fState.get()->fCanvasTransform; | 
| -    finalMatrix.preConcat(fState.get()->fShaderTransform); | 
| +    SkMatrix finalMatrix = fShaderState->fCanvasTransform; | 
| +    finalMatrix.preConcat(fShaderState->fShaderTransform); | 
| finalMatrix.preConcat(mapperMatrix); | 
|  | 
| // Preserves as much as posible in the final matrix, and only removes | 
| @@ -911,7 +884,7 @@ SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state) | 
| } | 
|  | 
| SkRect bbox; | 
| -    bbox.set(fState.get()->fBBox); | 
| +    bbox.set(fShaderState->fBBox); | 
| if (!inverseTransformBBox(finalMatrix, &bbox)) { | 
| return; | 
| } | 
| @@ -924,10 +897,11 @@ SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state) | 
| domain->appendScalar(bbox.fBottom); | 
|  | 
| SkString functionCode; | 
| -    // The two point radial gradient further references fState.get()->fInfo | 
| +    // The two point radial gradient further references | 
| +    // fShaderState->fInfo | 
| // in translating from x, y coordinates to the t parameter. So, we have | 
| // to transform the points and radii according to the calculated matrix. | 
| -    if (fState.get()->fType == SkShader::kRadial2_GradientType) { | 
| +    if (fShaderState->fType == SkShader::kRadial2_GradientType) { | 
| SkShader::GradientInfo twoPointRadialInfo = *info; | 
| SkMatrix inverseMapperMatrix; | 
| if (!mapperMatrix.invert(&inverseMapperMatrix)) { | 
| @@ -957,8 +931,9 @@ SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state) | 
| insert("Shading", pdfShader.get()); | 
| } | 
|  | 
| -SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) : fState(state) { | 
| -    fState.get()->fImage.lockPixels(); | 
| +SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) | 
| +    : SkPDFShader(state) { | 
| +    fShaderState->fImage.lockPixels(); | 
|  | 
| // The image shader pattern cell will be drawn into a separate device | 
| // in pattern cell space (no scaling on the bitmap, though there may be | 
| @@ -966,15 +941,15 @@ SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) : fState(state) { | 
|  | 
| // Map clip bounds to shader space to ensure the device is large enough | 
| // to handle fake clamping. | 
| -    SkMatrix finalMatrix = fState.get()->fCanvasTransform; | 
| -    finalMatrix.preConcat(fState.get()->fShaderTransform); | 
| +    SkMatrix finalMatrix = fShaderState->fCanvasTransform; | 
| +    finalMatrix.preConcat(fShaderState->fShaderTransform); | 
| SkRect deviceBounds; | 
| -    deviceBounds.set(fState.get()->fBBox); | 
| +    deviceBounds.set(fShaderState->fBBox); | 
| if (!inverseTransformBBox(finalMatrix, &deviceBounds)) { | 
| return; | 
| } | 
|  | 
| -    const SkBitmap* image = &fState.get()->fImage; | 
| +    const SkBitmap* image = &fShaderState->fImage; | 
| SkRect bitmapBounds; | 
| image->getBounds(&bitmapBounds); | 
|  | 
| @@ -983,8 +958,8 @@ SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) : fState(state) { | 
| // For clamp modes, we're only interested in the clip region, whether | 
| // or not the main bitmap is in it. | 
| SkShader::TileMode tileModes[2]; | 
| -    tileModes[0] = fState.get()->fImageTileModes[0]; | 
| -    tileModes[1] = fState.get()->fImageTileModes[1]; | 
| +    tileModes[0] = fShaderState->fImageTileModes[0]; | 
| +    tileModes[1] = fShaderState->fImageTileModes[1]; | 
| if (tileModes[0] != SkShader::kClamp_TileMode || | 
| tileModes[1] != SkShader::kClamp_TileMode) { | 
| deviceBounds.join(bitmapBounds); | 
| @@ -1164,7 +1139,7 @@ SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) : fState(state) { | 
| populate_tiling_pattern_dict(this, patternBBox, | 
| pattern.getResourceDict(), finalMatrix); | 
|  | 
| -    fState.get()->fImage.unlockPixels(); | 
| +    fShaderState->fImage.unlockPixels(); | 
| } | 
|  | 
| SkPDFStream* SkPDFFunctionShader::makePSFunction(const SkString& psCode, SkPDFArray* domain) { | 
| @@ -1176,16 +1151,6 @@ SkPDFStream* SkPDFFunctionShader::makePSFunction(const SkString& psCode, SkPDFAr | 
| return result; | 
| } | 
|  | 
| -SkPDFShader::ShaderCanonicalEntry::ShaderCanonicalEntry(SkPDFObject* pdfShader, const State* state) | 
| -    : fPDFShader(pdfShader) | 
| -    , fState(state) | 
| -{} | 
| - | 
| -bool SkPDFShader::ShaderCanonicalEntry::operator==(const ShaderCanonicalEntry& b) const { | 
| -    return fPDFShader == b.fPDFShader || | 
| -           (fState != NULL && b.fState != NULL && *fState == *b.fState); | 
| -} | 
| - | 
| bool SkPDFShader::State::operator==(const SkPDFShader::State& b) const { | 
| if (fType != b.fType || | 
| fCanvasTransform != b.fCanvasTransform || | 
|  |