Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(934)

Unified Diff: src/pdf/SkPDFShader.cpp

Issue 1925233003: SkPDF: Use type 2/3 shading for gradient shaders (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/pdf/SkPDFShader.cpp
diff --git a/src/pdf/SkPDFShader.cpp b/src/pdf/SkPDFShader.cpp
index c30d8a4281daf3b6b8b77f3adda312f38c824ff1..48e53c652ae5614b974d2be7557b78e6c25fea0c 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", sk_ref_sp(c0.get()));
hal.canary 2016/04/29 09:56:24 retval->insertObject("C0", c0); should work. sk_
+
+ auto c1 = sk_make_sp<SkPDFArray>();
+ c1->appendScalar(color2[0]);
+ c1->appendScalar(color2[1]);
+ c1->appendScalar(color2[2]);
+ retval->insertObject("C1", sk_ref_sp(c1.get()));
+
+ auto domain = sk_make_sp<SkPDFArray>();
+ domain->appendScalar(0);
+ domain->appendScalar(1.0f);
+ retval->insertObject("Domain", sk_ref_sp(domain.get()));
+
+ 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", sk_ref_sp(domain.get()));
+ 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.
+ 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", sk_ref_sp(encode.get()));
+ retval->insertObject("Bounds", sk_ref_sp(bounds.get()));
+ retval->insertObject("Functions", sk_ref_sp(functions.get()));
+
+ 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;
caryclark 2016/05/04 17:38:26 It is unfortunate that this leaves the old way of
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", sk_ref_sp(gradientStitchCode(*info).get()));
+ 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", sk_ref_sp(extend.get()));
+
+ 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", sk_ref_sp(coords.get()));
} 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()));
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698