Chromium Code Reviews| Index: src/pdf/SkPDFShader.cpp |
| diff --git a/src/pdf/SkPDFShader.cpp b/src/pdf/SkPDFShader.cpp |
| index c30d8a4281daf3b6b8b77f3adda312f38c824ff1..2bebe10f99890810b387f5444666b54a309786e9 100644 |
| --- a/src/pdf/SkPDFShader.cpp |
| +++ b/src/pdf/SkPDFShader.cpp |
| @@ -122,6 +122,8 @@ static void interpolateColorCode(SkScalar range, SkScalar* curColor, |
| } |
| } |
| */ |
| +static const int kColorComponents = 3; |
| +typedef SkScalar ColorTuple[kColorComponents]; |
| static void gradientFunctionCode(const SkShader::GradientInfo& info, |
| SkDynamicMemoryWStream* result) { |
| /* We want to linearly interpolate from the previous color to the next. |
| @@ -129,8 +131,7 @@ static void gradientFunctionCode(const SkShader::GradientInfo& info, |
| for interpolation. |
| C{r,g,b}(t, section) = t - offset_(section-1) + t * Multiplier{r,g,b}. |
| */ |
| - static const int kColorComponents = 3; |
| - typedef SkScalar ColorTuple[kColorComponents]; |
| + |
| SkAutoSTMalloc<4, ColorTuple> colorDataAlloc(info.fColorCount); |
| ColorTuple *colorData = colorDataAlloc.get(); |
| const SkScalar scale = SkScalarInvert(SkIntToScalar(255)); |
| @@ -183,6 +184,92 @@ static void gradientFunctionCode(const SkShader::GradientInfo& info, |
| } |
| } |
| +static sk_sp<SkPDFDict> createInterpolationFunction(const ColorTuple& color1, |
| + const ColorTuple& color2) { |
| + auto retval = sk_make_sp<SkPDFDict>(); |
| + |
| + auto c0 = sk_make_sp<SkPDFArray>(); |
| + c0->appendScalar(color1[0]); |
| + c0->appendScalar(color1[1]); |
| + c0->appendScalar(color1[2]); |
| + retval->insertObject("C0", std::move(c0)); |
| + |
| + auto c1 = sk_make_sp<SkPDFArray>(); |
| + c1->appendScalar(color2[0]); |
| + c1->appendScalar(color2[1]); |
| + c1->appendScalar(color2[2]); |
| + retval->insertObject("C1", std::move(c1)); |
| + |
| + auto domain = sk_make_sp<SkPDFArray>(); |
| + domain->appendScalar(0); |
| + domain->appendScalar(1.0f); |
| + retval->insertObject("Domain", std::move(domain)); |
| + |
| + retval->insertInt("FunctionType", 2); |
| + retval->insertScalar("N", 1.0f); |
| + |
| + return retval; |
| +} |
| + |
| +static sk_sp<SkPDFDict> gradientStitchCode(const SkShader::GradientInfo& info) { |
| + auto retval = sk_make_sp<SkPDFDict>(); |
| + |
| + SkAutoSTMalloc<4, ColorTuple> colorDataAlloc(info.fColorCount); |
| + ColorTuple *colorData = colorDataAlloc.get(); |
| + const SkScalar scale = SkScalarInvert(SkIntToScalar(255)); |
| + for (int i = 0; i < info.fColorCount; i++) { |
| + colorData[i][0] = SkScalarMul(SkColorGetR(info.fColors[i]), scale); |
| + colorData[i][1] = SkScalarMul(SkColorGetG(info.fColors[i]), scale); |
| + colorData[i][2] = SkScalarMul(SkColorGetB(info.fColors[i]), scale); |
| + } |
| + |
| + // no need for a stitch function if there are only 2 stops. |
| + if (info.fColorCount == 2) |
| + return createInterpolationFunction(colorData[0], colorData[1]); |
| + |
| + auto encode = sk_make_sp<SkPDFArray>(); |
| + auto bounds = sk_make_sp<SkPDFArray>(); |
| + auto functions = sk_make_sp<SkPDFArray>(); |
| + |
| + auto domain = sk_make_sp<SkPDFArray>(); |
| + domain->appendScalar(0); |
| + domain->appendScalar(1.0f); |
| + retval->insertObject("Domain", std::move(domain)); |
| + retval->insertInt("FunctionType", 3); |
| + |
| + for (int i = 1; i < info.fColorCount; i++) { |
| + SkScalar offset = 0; |
| + if (functions->size() > 0) { |
| + offset = info.fColorOffsets[i-1]; |
| + if (offset >= 0.99998f) { |
| + bounds->appendScalar(1.0f); |
| + } else if (offset == 0) {// if a colorstop has offset 0, remove previous stops. |
|
hal.canary
2016/05/04 17:42:59
does this ever happen?
offsets [0,0,1,1] with a c
|
| + encode = sk_make_sp<SkPDFArray>(); |
| + bounds = sk_make_sp<SkPDFArray>(); |
| + functions = sk_make_sp<SkPDFArray>(); |
| + } else { |
| + bounds->appendScalar(offset); |
| + } |
| + } |
| + |
| + encode->appendScalar(0); |
| + encode->appendScalar(1.0f); |
| + |
| + functions->appendObject(createInterpolationFunction(colorData[i-1], colorData[i])); |
| + |
| + if (offset >= 0.99998f) { // stop at colorstop that has 100% offset. ignore later ones. |
| + break; |
| + } |
| + } |
| + |
| + retval->insertObject("Encode", std::move(encode)); |
| + retval->insertObject("Bounds", std::move(bounds)); |
| + retval->insertObject("Functions", std::move(functions)); |
| + |
| + return retval; |
| +} |
| + |
| + |
| /* Map a value of t on the stack into [0, 1) for Repeat or Mirror tile mode. */ |
| static void tileModeCode(SkShader::TileMode mode, |
| SkDynamicMemoryWStream* result) { |
| @@ -713,109 +800,159 @@ SkPDFFunctionShader* SkPDFFunctionShader::Create( |
| const SkMatrix& perspectiveRemover, |
| SkDynamicMemoryWStream* function) = nullptr; |
| 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::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 nullptr; |
| - } |
| - // 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); |
| + bool doStitchFunctions = (state.fType == SkShader::kLinear_GradientType || |
| + state.fType == SkShader::kRadial_GradientType || |
| + state.fType == SkShader::kConical_GradientType) && |
| + info->fTileMode == SkShader::kClamp_TileMode; |
| 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 nullptr; |
| - } |
| - } |
| - |
| - SkRect bbox; |
| - bbox.set(state.fBBox); |
| - if (!inverse_transform_bbox(finalMatrix, &bbox)) { |
| - return nullptr; |
| - } |
| auto domain = sk_make_sp<SkPDFArray>(); |
| - domain->reserve(4); |
| - domain->appendScalar(bbox.fLeft); |
| - domain->appendScalar(bbox.fRight); |
| - domain->appendScalar(bbox.fTop); |
| - domain->appendScalar(bbox.fBottom); |
| - SkDynamicMemoryWStream functionCode; |
| + int32_t shadingType = 1; |
| + auto pdfShader = sk_make_sp<SkPDFDict>(); |
| // 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::kConical_GradientType) { |
| - SkShader::GradientInfo twoPointRadialInfo = *info; |
| - SkMatrix inverseMapperMatrix; |
| - if (!mapperMatrix.invert(&inverseMapperMatrix)) { |
| - return nullptr; |
| + if (doStitchFunctions) { |
| + pdfShader->insertObject("Function", std::move(gradientStitchCode(*info))); |
|
hal.canary
2016/05/04 12:57:17
don't need the std::move here, it is already a r-v
|
| + shadingType = (state.fType == SkShader::kLinear_GradientType) ? 2 : 3; |
| + |
| + auto extend = sk_make_sp<SkPDFArray>(); |
| + extend->reserve(2); |
| + extend->appendBool(true); |
| + extend->appendBool(true); |
| + pdfShader->insertObject("Extend", std::move(extend)); |
| + |
| + auto coords = sk_make_sp<SkPDFArray>(); |
| + if (state.fType == SkShader::kConical_GradientType) { |
| + coords->reserve(6); |
| + coords->appendScalar(info->fPoint[0].fX); |
| + coords->appendScalar(info->fPoint[0].fY); |
| + coords->appendScalar(info->fRadius[0]); |
| + |
| + coords->appendScalar(info->fPoint[1].fX); |
| + coords->appendScalar(info->fPoint[1].fY); |
| + coords->appendScalar(info->fRadius[1]); |
| + } else if (state.fType == SkShader::kRadial_GradientType) { |
| + coords->reserve(6); |
| + coords->appendScalar(info->fPoint[0].fX); |
| + coords->appendScalar(info->fPoint[0].fY); |
| + coords->appendScalar(0); |
| + |
| + coords->appendScalar(info->fPoint[0].fX); |
| + coords->appendScalar(info->fPoint[0].fY); |
| + coords->appendScalar(info->fRadius[0]); |
| + } else { |
| + coords->reserve(4); |
| + coords->appendScalar(info->fPoint[0].fX); |
| + coords->appendScalar(info->fPoint[0].fY); |
| + |
| + coords->appendScalar(info->fPoint[1].fX); |
| + coords->appendScalar(info->fPoint[1].fY); |
| } |
| - inverseMapperMatrix.mapPoints(twoPointRadialInfo.fPoint, 2); |
| - twoPointRadialInfo.fRadius[0] = |
| - inverseMapperMatrix.mapRadius(info->fRadius[0]); |
| - twoPointRadialInfo.fRadius[1] = |
| - inverseMapperMatrix.mapRadius(info->fRadius[1]); |
| - codeFunction(twoPointRadialInfo, perspectiveInverseOnly, &functionCode); |
| + |
| + pdfShader->insertObject("Coords", std::move(coords)); |
| } else { |
| - codeFunction(*info, perspectiveInverseOnly, &functionCode); |
| + // Depending on the type of the gradient, we want to transform the |
| + // coordinate space in different ways. |
| + 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::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 nullptr; |
| + } |
| + |
| + // 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); |
| + |
| + 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 nullptr; |
| + } |
| + } |
| + |
| + SkRect bbox; |
| + bbox.set(state.fBBox); |
| + if (!inverse_transform_bbox(finalMatrix, &bbox)) { |
| + return nullptr; |
| + } |
| + domain->reserve(4); |
| + domain->appendScalar(bbox.fLeft); |
| + domain->appendScalar(bbox.fRight); |
| + domain->appendScalar(bbox.fTop); |
| + domain->appendScalar(bbox.fBottom); |
| + |
| + SkDynamicMemoryWStream functionCode; |
| + |
| + if (state.fType == SkShader::kConical_GradientType) { |
| + SkShader::GradientInfo twoPointRadialInfo = *info; |
| + SkMatrix inverseMapperMatrix; |
| + if (!mapperMatrix.invert(&inverseMapperMatrix)) { |
| + return nullptr; |
| + } |
| + inverseMapperMatrix.mapPoints(twoPointRadialInfo.fPoint, 2); |
| + twoPointRadialInfo.fRadius[0] = |
| + inverseMapperMatrix.mapRadius(info->fRadius[0]); |
| + twoPointRadialInfo.fRadius[1] = |
| + inverseMapperMatrix.mapRadius(info->fRadius[1]); |
| + codeFunction(twoPointRadialInfo, perspectiveInverseOnly, &functionCode); |
| + } else { |
| + codeFunction(*info, perspectiveInverseOnly, &functionCode); |
| + } |
| + |
| + pdfShader->insertObject("Domain", sk_ref_sp(domain.get())); |
| + |
| + // Call canon->makeRangeObject() instead of |
| + // SkPDFShader::MakeRangeObject() so that the canon can |
| + // deduplicate. |
| + std::unique_ptr<SkStreamAsset> functionStream( |
| + functionCode.detachAsStream()); |
| + auto function = make_ps_function(std::move(functionStream), domain.get(), |
| + canon->makeRangeObject()); |
| + pdfShader->insertObjRef("Function", std::move(function)); |
| } |
| - auto pdfShader = sk_make_sp<SkPDFDict>(); |
| - pdfShader->insertInt("ShadingType", 1); |
| + pdfShader->insertInt("ShadingType", shadingType); |
| pdfShader->insertName("ColorSpace", "DeviceRGB"); |
| - pdfShader->insertObject("Domain", sk_ref_sp(domain.get())); |
| - |
| - // Call canon->makeRangeObject() instead of |
| - // SkPDFShader::MakeRangeObject() so that the canon can |
| - // deduplicate. |
| - std::unique_ptr<SkStreamAsset> functionStream( |
| - functionCode.detachAsStream()); |
| - auto function = make_ps_function(std::move(functionStream), domain.get(), |
| - canon->makeRangeObject()); |
| - pdfShader->insertObjRef("Function", std::move(function)); |
| sk_sp<SkPDFFunctionShader> pdfFunctionShader( |
| new SkPDFFunctionShader(autoState->release())); |