From bc59ac6b12bbded2117fe3aa9643b2d138e5ddda Mon Sep 17 00:00:00 2001 From: halcanary Date: Fri, 23 Jan 2015 04:18:53 -0800 Subject: Simplify SkPDFShader class. Now invalid objects are never created. "Constructors should not do real work" I have verified that all test PDFs render identically. Review URL: https://codereview.chromium.org/862113004 --- src/pdf/SkPDFShader.cpp | 280 ++++++++++++++++++++++++++---------------------- src/pdf/SkPDFShader.h | 5 +- 2 files changed, 155 insertions(+), 130 deletions(-) (limited to 'src') diff --git a/src/pdf/SkPDFShader.cpp b/src/pdf/SkPDFShader.cpp index 1b9d9eb5b8..aad62f53ca 100644 --- a/src/pdf/SkPDFShader.cpp +++ b/src/pdf/SkPDFShader.cpp @@ -505,16 +505,21 @@ private: void AllocateGradientInfoStorage(); }; +static void remove_from_canon(SkPDFShader* shader) { + SkAutoMutexAcquire lock(SkPDFCanon::GetShaderMutex()); + SkPDFCanon::GetCanon().removeShader(shader); +} + class SkPDFFunctionShader : public SkPDFDict, public SkPDFShader { SK_DECLARE_INST_COUNT(SkPDFFunctionShader) public: - explicit SkPDFFunctionShader(SkPDFShader::State* state); + static SkPDFObject* Create(SkAutoTDelete*); + virtual ~SkPDFFunctionShader() { - SkPDFShader::RemoveFromCanonIfValid(this); + remove_from_canon(this); fResources.unrefAll(); } - bool isValid() SK_OVERRIDE { return fResources.count() > 0; } SkPDFObject* toPDFObject() SK_OVERRIDE { return this; } void getResources(const SkTSet& knownResourceObjects, @@ -525,11 +530,9 @@ public: } private: - static SkPDFObject* RangeObject(); - SkTDArray fResources; - - SkPDFStream* makePSFunction(const SkString& psCode, SkPDFArray* domain); + explicit SkPDFFunctionShader(SkPDFShader::State* state) + : SkPDFDict("Pattern"), SkPDFShader(state) {} typedef SkPDFDict INHERITED; }; @@ -540,18 +543,17 @@ private: */ class SkPDFAlphaFunctionShader : public SkPDFStream, public SkPDFShader { public: - explicit SkPDFAlphaFunctionShader(SkPDFShader::State* state); - virtual ~SkPDFAlphaFunctionShader() { - SkPDFShader::RemoveFromCanonIfValid(this); - } + static SkPDFObject* Create(SkAutoTDelete*); + + virtual ~SkPDFAlphaFunctionShader() { remove_from_canon(this); } - bool isValid() SK_OVERRIDE { - return fColorShader.get() != NULL; - } SkPDFObject* toPDFObject() SK_OVERRIDE { return this; } private: - SkPDFGraphicState* CreateSMaskGraphicState(); + explicit SkPDFAlphaFunctionShader(SkPDFShader::State* state); + + static SkPDFGraphicState* CreateSMaskGraphicState( + const SkPDFShader::State&); void getResources(const SkTSet& knownResourceObjects, SkTSet* newResourceObjects) SK_OVERRIDE { @@ -566,13 +568,13 @@ private: class SkPDFImageShader : public SkPDFStream, public SkPDFShader { public: - explicit SkPDFImageShader(SkPDFShader::State* state); + static SkPDFObject* Create(SkAutoTDelete*); + virtual ~SkPDFImageShader() { - SkPDFShader::RemoveFromCanonIfValid(this); + remove_from_canon(this); fResources.unrefAll(); } - bool isValid() SK_OVERRIDE { return size() > 0; } SkPDFObject* toPDFObject() SK_OVERRIDE { return this; } void getResources(const SkTSet& knownResourceObjects, @@ -584,37 +586,22 @@ public: private: SkTSet fResources; + explicit SkPDFImageShader(SkPDFShader::State* state) : SkPDFShader(state) {} }; 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) { - SkAutoTDelete state(inState); - if (state->fType == SkShader::kNone_GradientType && - state->fImage.isNull()) { +SkPDFObject* SkPDFShader::GetPDFShaderByState( + SkAutoTDelete* autoState) { + const State& state = **autoState; + 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 @@ -622,22 +609,18 @@ SkPDFObject* SkPDFShader::GetPDFShaderByState(State* inState) { return NULL; } - SkPDFShader* pdfShader = SkPDFCanon::GetCanon().findShader(*state); + SkPDFShader* pdfShader = SkPDFCanon::GetCanon().findShader(state); if (pdfShader) { - SkASSERT(pdfShader->isValid()); return SkRef(pdfShader->toPDFObject()); } // The PDFShader takes ownership of the shaderSate. - 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()))); + if (state.fType == SkShader::kNone_GradientType) { + return SkPDFImageShader::Create(autoState); + } else if (state.GradientHasAlpha()) { + return SkPDFAlphaFunctionShader::Create(autoState); } else { - return SkPDFShader::AddToCanonIfValid( - SkNEW_ARGS(SkPDFFunctionShader, (state.detach()))); + return SkPDFFunctionShader::Create(autoState); } } @@ -647,28 +630,9 @@ SkPDFObject* SkPDFShader::GetPDFShader(const SkShader& shader, const SkIRect& surfaceBBox, SkScalar rasterScale) { SkAutoMutexAcquire lock(SkPDFCanon::GetShaderMutex()); - return GetPDFShaderByState( + SkAutoTDelete state( SkNEW_ARGS(State, (shader, matrix, surfaceBBox, rasterScale))); -} - - -// static -SkPDFObject* SkPDFFunctionShader::RangeObject() { - SkPDFCanon::GetShaderMutex().assertHeld(); - static SkPDFArray* range = NULL; - // This method is only used with CanonicalShadersMutex, so it's safe to - // populate domain. - if (range == NULL) { - range = new SkPDFArray; - range->reserve(6); - range->appendInt(0); - range->appendInt(1); - range->appendInt(0); - range->appendInt(1); - range->appendInt(0); - range->appendInt(1); - } - return range; + return GetPDFShaderByState(&state); } static SkPDFResourceDict* get_gradient_resource_dict( @@ -730,12 +694,15 @@ static SkStream* create_pattern_fill_content(int gsIndex, SkRect& bounds) { * Creates a ExtGState with the SMask set to the luminosityShader in * luminosity mode. The shader pattern extends to the bbox. */ -SkPDFGraphicState* SkPDFAlphaFunctionShader::CreateSMaskGraphicState() { +SkPDFGraphicState* SkPDFAlphaFunctionShader::CreateSMaskGraphicState( + const SkPDFShader::State& state) { SkRect bbox; - bbox.set(fShaderState->fBBox); + bbox.set(state.fBBox); - SkAutoTUnref luminosityShader(SkPDFShader::GetPDFShaderByState( - fShaderState->CreateAlphaToLuminosityState())); + SkAutoTDelete alphaToLuminosityState( + state.CreateAlphaToLuminosityState()); + SkAutoTUnref luminosityShader( + SkPDFShader::GetPDFShaderByState(&alphaToLuminosityState)); SkAutoTDelete alphaStream(create_pattern_fill_content(-1, bbox)); @@ -750,28 +717,46 @@ SkPDFGraphicState* SkPDFAlphaFunctionShader::CreateSMaskGraphicState() { SkPDFGraphicState::kLuminosity_SMaskMode); } -SkPDFAlphaFunctionShader::SkPDFAlphaFunctionShader(SkPDFShader::State* state) - : SkPDFShader(state) { +SkPDFObject* SkPDFAlphaFunctionShader::Create( + SkAutoTDelete* autoState) { + const SkPDFShader::State& state = **autoState; SkRect bbox; - bbox.set(fShaderState->fBBox); + bbox.set(state.fBBox); - fColorShader.reset( - SkPDFShader::GetPDFShaderByState(state->CreateOpaqueState())); + SkAutoTDelete opaqueState(state.CreateOpaqueState()); + + SkPDFObject* colorShader = SkPDFShader::GetPDFShaderByState(&opaqueState); + if (!colorShader) { + return NULL; + } // Create resource dict with alpha graphics state as G0 and // pattern shader as P0, then write content stream. - SkAutoTUnref alphaGs(CreateSMaskGraphicState()); - fResourceDict.reset( - get_gradient_resource_dict(fColorShader.get(), alphaGs.get())); + SkAutoTUnref alphaGs( + SkPDFAlphaFunctionShader::CreateSMaskGraphicState(state)); + + SkPDFAlphaFunctionShader* alphaFunctionShader = + SkNEW_ARGS(SkPDFAlphaFunctionShader, (autoState->detach())); + + alphaFunctionShader->fColorShader.reset(colorShader); + + alphaFunctionShader->fResourceDict.reset(get_gradient_resource_dict( + alphaFunctionShader->fColorShader.get(), alphaGs.get())); SkAutoTDelete colorStream( create_pattern_fill_content(0, bbox)); - setData(colorStream.get()); + alphaFunctionShader->setData(colorStream.get()); - populate_tiling_pattern_dict(this, bbox, fResourceDict.get(), + populate_tiling_pattern_dict(alphaFunctionShader, bbox, + alphaFunctionShader->fResourceDict.get(), SkMatrix::I()); + SkPDFCanon::GetCanon().addShader(alphaFunctionShader); + return alphaFunctionShader; } +SkPDFAlphaFunctionShader::SkPDFAlphaFunctionShader(SkPDFShader::State* state) + : SkPDFShader(state) {} + // Finds affine and persp such that in = affine * persp. // but it returns the inverse of perspective matrix. static bool split_perspective(const SkMatrix in, SkMatrix* affine, @@ -810,18 +795,50 @@ static bool split_perspective(const SkMatrix in, SkMatrix* affine, return true; } -SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state) - : SkPDFDict("Pattern"), SkPDFShader(state) { +namespace { +SkPDFObject* create_range_object() { + SkPDFArray* range = SkNEW(SkPDFArray); + range->reserve(6); + range->appendInt(0); + range->appendInt(1); + range->appendInt(0); + range->appendInt(1); + range->appendInt(0); + range->appendInt(1); + return range; +} + +template void unref(T* ptr) { ptr->unref();} +} // namespace + +SK_DECLARE_STATIC_LAZY_PTR(SkPDFObject, rangeObject, + create_range_object, unref); + +static SkPDFStream* make_ps_function(const SkString& psCode, + SkPDFArray* domain) { + SkAutoDataUnref funcData( + SkData::NewWithCopy(psCode.c_str(), psCode.size())); + SkPDFStream* result = SkNEW_ARGS(SkPDFStream, (funcData.get())); + result->insertInt("FunctionType", 4); + result->insert("Domain", domain); + result->insert("Range", rangeObject.get()); + return result; +} + +SkPDFObject* SkPDFFunctionShader::Create( + SkAutoTDelete* autoState) { + const SkPDFShader::State& state = **autoState; + 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 = &fShaderState->fInfo; + const SkShader::GradientInfo* info = &state.fInfo; transformPoints[0] = info->fPoint[0]; transformPoints[1] = info->fPoint[1]; - switch (fShaderState->fType) { + switch (state.fType) { case SkShader::kLinear_GradientType: codeFunction = &linearCode; break; @@ -831,10 +848,9 @@ SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state) codeFunction = &radialCode; break; case SkShader::kRadial2_GradientType: { - // Bail out if the radii are the same. Empty fResources signals - // an error and isValid will return false. + // Bail out if the radii are the same. if (info->fRadius[0] == info->fRadius[1]) { - return; + return NULL; } transformPoints[1] = transformPoints[0]; SkScalar dr = info->fRadius[1] - info->fRadius[0]; @@ -856,7 +872,7 @@ SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state) case SkShader::kColor_GradientType: case SkShader::kNone_GradientType: default: - return; + return NULL; } // Move any scaling (assuming a unit gradient) or translation @@ -866,8 +882,8 @@ SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state) SkMatrix mapperMatrix; unitToPointsMatrix(transformPoints, &mapperMatrix); - SkMatrix finalMatrix = fShaderState->fCanvasTransform; - finalMatrix.preConcat(fShaderState->fShaderTransform); + SkMatrix finalMatrix = state.fCanvasTransform; + finalMatrix.preConcat(state.fShaderTransform); finalMatrix.preConcat(mapperMatrix); // Preserves as much as posible in the final matrix, and only removes @@ -879,14 +895,14 @@ SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state) if (finalMatrix.hasPerspective()) { if (!split_perspective(finalMatrix, &finalMatrix, &perspectiveInverseOnly)) { - return; + return NULL; } } SkRect bbox; - bbox.set(fShaderState->fBBox); + bbox.set(state.fBBox); if (!inverseTransformBBox(finalMatrix, &bbox)) { - return; + return NULL; } SkAutoTUnref domain(new SkPDFArray); @@ -898,14 +914,14 @@ SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state) SkString functionCode; // The two point radial gradient further references - // fShaderState->fInfo + // state.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 (fShaderState->fType == SkShader::kRadial2_GradientType) { + if (state.fType == SkShader::kRadial2_GradientType) { SkShader::GradientInfo twoPointRadialInfo = *info; SkMatrix inverseMapperMatrix; if (!mapperMatrix.invert(&inverseMapperMatrix)) { - return; + return NULL; } inverseMapperMatrix.mapPoints(twoPointRadialInfo.fPoint, 2); twoPointRadialInfo.fRadius[0] = @@ -922,18 +938,31 @@ SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state) pdfShader->insertName("ColorSpace", "DeviceRGB"); pdfShader->insert("Domain", domain.get()); - SkPDFStream* function = makePSFunction(functionCode, domain.get()); + SkPDFStream* function = make_ps_function(functionCode, domain.get()); pdfShader->insert("Function", new SkPDFObjRef(function))->unref(); - fResources.push(function); // Pass ownership to resource list. - insertInt("PatternType", 2); - insert("Matrix", SkPDFUtils::MatrixToArray(finalMatrix))->unref(); - insert("Shading", pdfShader.get()); + SkAutoTUnref matrixArray( + SkPDFUtils::MatrixToArray(finalMatrix)); + + SkPDFFunctionShader* pdfFunctionShader = + SkNEW_ARGS(SkPDFFunctionShader, (autoState->detach())); + + pdfFunctionShader->fResources.push(function); + // Pass ownership to resource list. + + pdfFunctionShader->insertInt("PatternType", 2); + pdfFunctionShader->insert("Matrix", matrixArray.get()); + pdfFunctionShader->insert("Shading", pdfShader.get()); + + SkPDFCanon::GetCanon().addShader(pdfFunctionShader); + return pdfFunctionShader; } -SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) - : SkPDFShader(state) { - fShaderState->fImage.lockPixels(); +SkPDFObject* SkPDFImageShader::Create( + SkAutoTDelete* autoState) { + const SkPDFShader::State& state = **autoState; + + state.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 @@ -941,15 +970,15 @@ SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) // Map clip bounds to shader space to ensure the device is large enough // to handle fake clamping. - SkMatrix finalMatrix = fShaderState->fCanvasTransform; - finalMatrix.preConcat(fShaderState->fShaderTransform); + SkMatrix finalMatrix = state.fCanvasTransform; + finalMatrix.preConcat(state.fShaderTransform); SkRect deviceBounds; - deviceBounds.set(fShaderState->fBBox); + deviceBounds.set(state.fBBox); if (!inverseTransformBBox(finalMatrix, &deviceBounds)) { - return; + return NULL; } - const SkBitmap* image = &fShaderState->fImage; + const SkBitmap* image = &state.fImage; SkRect bitmapBounds; image->getBounds(&bitmapBounds); @@ -958,8 +987,8 @@ SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* 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] = fShaderState->fImageTileModes[0]; - tileModes[1] = fShaderState->fImageTileModes[1]; + tileModes[0] = state.fImageTileModes[0]; + tileModes[1] = state.fImageTileModes[1]; if (tileModes[0] != SkShader::kClamp_TileMode || tileModes[1] != SkShader::kClamp_TileMode) { deviceBounds.join(bitmapBounds); @@ -1132,23 +1161,22 @@ SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) // Put the canvas into the pattern stream (fContent). SkAutoTDelete content(pattern.content()); - setData(content.get()); + + SkPDFImageShader* imageShader = + SkNEW_ARGS(SkPDFImageShader, (autoState->detach())); + imageShader->setData(content.get()); + SkPDFResourceDict* resourceDict = pattern.getResourceDict(); - resourceDict->getReferencedResources(fResources, &fResources, false); + resourceDict->getReferencedResources(imageShader->fResources, + &imageShader->fResources, false); - populate_tiling_pattern_dict(this, patternBBox, + populate_tiling_pattern_dict(imageShader, patternBBox, pattern.getResourceDict(), finalMatrix); - fShaderState->fImage.unlockPixels(); -} + imageShader->fShaderState->fImage.unlockPixels(); -SkPDFStream* SkPDFFunctionShader::makePSFunction(const SkString& psCode, SkPDFArray* domain) { - SkAutoDataUnref funcData(SkData::NewWithCopy(psCode.c_str(), psCode.size())); - SkPDFStream* result = new SkPDFStream(funcData.get()); - result->insertInt("FunctionType", 4); - result->insert("Domain", domain); - result->insert("Range", RangeObject()); - return result; + SkPDFCanon::GetCanon().addShader(imageShader); + return imageShader; } bool SkPDFShader::State::operator==(const SkPDFShader::State& b) const { diff --git a/src/pdf/SkPDFShader.h b/src/pdf/SkPDFShader.h index e0abff8f68..10532156ad 100644 --- a/src/pdf/SkPDFShader.h +++ b/src/pdf/SkPDFShader.h @@ -54,14 +54,11 @@ protected: // This is an internal method. // CanonicalShadersMutex() should already be acquired. // This also takes ownership of shaderState. - static SkPDFObject* GetPDFShaderByState(State* shaderState); - static SkPDFObject* AddToCanonIfValid(SkPDFShader*); - static void RemoveFromCanonIfValid(SkPDFShader*); + static SkPDFObject* GetPDFShaderByState(SkAutoTDelete*); SkPDFShader(State*); virtual ~SkPDFShader(); - virtual bool isValid() = 0; virtual SkPDFObject* toPDFObject() = 0; }; -- cgit v1.2.3