// static SkPDFObject* SkPDFShader::GetPDFShader(const SkShader& shader, const SkMatrix& matrix, const SkIRect& surfaceBBox) { SkPDFObject* result; SkAutoMutexAcquire lock(CanonicalShadersMutex()); SkAutoTDelete<State> shaderState(new State(shader, matrix, surfaceBBox)); ShaderCanonicalEntry entry(NULL, shaderState.get()); int index = CanonicalShaders().find(entry); if (index >= 0) { result = CanonicalShaders()[index].fPDFShader; result->ref(); return result; } // The PDFShader takes ownership of the shaderSate. if (shaderState.get()->fType == SkShader::kNone_GradientType) { result = new SkPDFImageShader(shaderState.detach()); } else { SkPDFFunctionShader* functionShader = new SkPDFFunctionShader(shaderState.detach()); if (!functionShader->isValid()) { delete functionShader; return NULL; } result = functionShader; } entry.fPDFShader = result; CanonicalShaders().push(entry); return result; // return the reference that came from new. }
// static SkPDFObject* SkPDFShader::GetPDFShaderByState(State* inState) { SkPDFObject* result; 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 // handle compose shader by pulling things up to a layer, drawing with // the first shader, applying the xfer mode and drawing again with the // second shader, then applying the layer to the original drawing. return NULL; } ShaderCanonicalEntry entry(NULL, shaderState.get()); int index = CanonicalShaders().find(entry); if (index >= 0) { result = CanonicalShaders()[index].fPDFShader; result->ref(); return result; } 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; } 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; } entry.fPDFShader = result; CanonicalShaders().push(entry); return result; // return the reference that came from new. }
SkPDFFunctionShader* SkPDFFunctionShader::Create( SkPDFCanon* canon, SkAutoTDelete<SkPDFShader::State>* 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 = &state.fInfo; transformPoints[0] = info->fPoint[0]; transformPoints[1] = info->fPoint[1]; switch (state.fType) { case SkShader::kLinear_GradientType: codeFunction = &linearCode; break; case SkShader::kRadial_GradientType: transformPoints[1] = transformPoints[0]; transformPoints[1].fX += info->fRadius[0]; codeFunction = &radialCode; break; case SkShader::kRadial2_GradientType: { // Bail out if the radii are the same. if (info->fRadius[0] == info->fRadius[1]) { return NULL; } transformPoints[1] = transformPoints[0]; SkScalar dr = info->fRadius[1] - info->fRadius[0]; transformPoints[1].fX += dr; codeFunction = &twoPointRadialCode; break; } case SkShader::kConical_GradientType: { transformPoints[1] = transformPoints[0]; transformPoints[1].fX += SK_Scalar1; codeFunction = &twoPointConicalCode; break; } case SkShader::kSweep_GradientType: transformPoints[1] = transformPoints[0]; transformPoints[1].fX += SK_Scalar1; codeFunction = &sweepCode; break; case SkShader::kColor_GradientType: case SkShader::kNone_GradientType: default: return NULL; } // Move any scaling (assuming a unit gradient) or translation // (and rotation for linear gradient), of the final gradient from // info->fPoints to the matrix (updating bbox appropriately). Now // the gradient can be drawn on on the unit segment. SkMatrix mapperMatrix; unitToPointsMatrix(transformPoints, &mapperMatrix); SkMatrix finalMatrix = state.fCanvasTransform; finalMatrix.preConcat(state.fShaderTransform); finalMatrix.preConcat(mapperMatrix); // Preserves as much as posible in the final matrix, and only removes // the perspective. The inverse of the perspective is stored in // perspectiveInverseOnly matrix and has 3 useful numbers // (p0, p1, p2), while everything else is either 0 or 1. // In this way the shader will handle it eficiently, with minimal code. SkMatrix perspectiveInverseOnly = SkMatrix::I(); if (finalMatrix.hasPerspective()) { if (!split_perspective(finalMatrix, &finalMatrix, &perspectiveInverseOnly)) { return NULL; } } SkRect bbox; bbox.set(state.fBBox); if (!inverse_transform_bbox(finalMatrix, &bbox)) { return NULL; } SkAutoTUnref<SkPDFArray> domain(new SkPDFArray); domain->reserve(4); domain->appendScalar(bbox.fLeft); domain->appendScalar(bbox.fRight); domain->appendScalar(bbox.fTop); domain->appendScalar(bbox.fBottom); SkString functionCode; // The two point radial gradient further references // 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 (state.fType == SkShader::kRadial2_GradientType) { SkShader::GradientInfo twoPointRadialInfo = *info; SkMatrix inverseMapperMatrix; if (!mapperMatrix.invert(&inverseMapperMatrix)) { return NULL; } inverseMapperMatrix.mapPoints(twoPointRadialInfo.fPoint, 2); twoPointRadialInfo.fRadius[0] = inverseMapperMatrix.mapRadius(info->fRadius[0]); twoPointRadialInfo.fRadius[1] = inverseMapperMatrix.mapRadius(info->fRadius[1]); functionCode = codeFunction(twoPointRadialInfo, perspectiveInverseOnly); } else { functionCode = codeFunction(*info, perspectiveInverseOnly); } SkAutoTUnref<SkPDFDict> pdfShader(new SkPDFDict); pdfShader->insertInt("ShadingType", 1); pdfShader->insertName("ColorSpace", "DeviceRGB"); pdfShader->insert("Domain", domain.get()); SkAutoTUnref<SkPDFStream> function( make_ps_function(functionCode, domain.get())); pdfShader->insert("Function", new SkPDFObjRef(function))->unref(); SkAutoTUnref<SkPDFArray> matrixArray( SkPDFUtils::MatrixToArray(finalMatrix)); SkPDFFunctionShader* pdfFunctionShader = SkNEW_ARGS(SkPDFFunctionShader, (autoState->detach())); pdfFunctionShader->insertInt("PatternType", 2); pdfFunctionShader->insert("Matrix", matrixArray.get()); pdfFunctionShader->insert("Shading", pdfShader.get()); canon->addFunctionShader(pdfFunctionShader); return pdfFunctionShader; }