/* Generate Type 4 function code to map t=[0,1) to the passed gradient, clamping at the edges of the range. The generated code will be of the form: if (t < 0) { return colorData[0][r,g,b]; } else { if (t < info.fColorOffsets[1]) { return linearinterpolation(colorData[0][r,g,b], colorData[1][r,g,b]); } else { if (t < info.fColorOffsets[2]) { return linearinterpolation(colorData[1][r,g,b], colorData[2][r,g,b]); } else { ... } else { return colorData[info.fColorCount - 1][r,g,b]; } ... } } */ static void gradientFunctionCode(const SkShader::GradientInfo& info, SkString* result) { /* We want to linearly interpolate from the previous color to the next. Scale the colors from 0..255 to 0..1 and determine the multipliers 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)); 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); } // Clamp the initial color. result->append("dup 0 le {pop "); result->appendScalar(colorData[0][0]); result->append(" "); result->appendScalar(colorData[0][1]); result->append(" "); result->appendScalar(colorData[0][2]); result->append(" }\n"); // The gradient colors. int gradients = 0; for (int i = 1 ; i < info.fColorCount; i++) { if (info.fColorOffsets[i] == info.fColorOffsets[i - 1]) { continue; } gradients++; result->append("{dup "); result->appendScalar(info.fColorOffsets[i]); result->append(" le {"); if (info.fColorOffsets[i - 1] != 0) { result->appendScalar(info.fColorOffsets[i - 1]); result->append(" sub\n"); } interpolateColorCode(info.fColorOffsets[i] - info.fColorOffsets[i - 1], colorData[i], colorData[i - 1], result); result->append("}\n"); } // Clamp the final color. result->append("{pop "); result->appendScalar(colorData[info.fColorCount - 1][0]); result->append(" "); result->appendScalar(colorData[info.fColorCount - 1][1]); result->append(" "); result->appendScalar(colorData[info.fColorCount - 1][2]); for (int i = 0 ; i < gradients + 1; i++) { result->append("} ifelse\n"); } }
/* Generate Type 4 function code to map t=[0,1) to the passed gradient, clamping at the edges of the range. The generated code will be of the form: if (t < 0) { return colorData[0][r,g,b]; } else { if (t < info.fColorOffsets[1]) { return linearinterpolation(colorData[0][r,g,b], colorData[1][r,g,b]); } else { if (t < info.fColorOffsets[2]) { return linearinterpolation(colorData[1][r,g,b], colorData[2][r,g,b]); } else { ... } else { return colorData[info.fColorCount - 1][r,g,b]; } ... } } */ static void gradient_function_code(const SkShader::GradientInfo& info, SkDynamicMemoryWStream* result) { /* We want to linearly interpolate from the previous color to the next. Scale the colors from 0..255 to 0..1 and determine the multipliers for interpolation. C{r,g,b}(t, section) = t - offset_(section-1) + t * Multiplier{r,g,b}. */ SkAutoSTMalloc<4, ColorTuple> colorDataAlloc(info.fColorCount); ColorTuple *colorData = colorDataAlloc.get(); for (int i = 0; i < info.fColorCount; i++) { colorData[i][0] = SkColorGetR(info.fColors[i]); colorData[i][1] = SkColorGetG(info.fColors[i]); colorData[i][2] = SkColorGetB(info.fColors[i]); } // Clamp the initial color. result->writeText("dup 0 le {pop "); SkPDFUtils::AppendColorComponent(colorData[0][0], result); result->writeText(" "); SkPDFUtils::AppendColorComponent(colorData[0][1], result); result->writeText(" "); SkPDFUtils::AppendColorComponent(colorData[0][2], result); result->writeText(" }\n"); // The gradient colors. int gradients = 0; for (int i = 1 ; i < info.fColorCount; i++) { if (info.fColorOffsets[i] == info.fColorOffsets[i - 1]) { continue; } gradients++; result->writeText("{dup "); SkPDFUtils::AppendScalar(info.fColorOffsets[i], result); result->writeText(" le {"); if (info.fColorOffsets[i - 1] != 0) { SkPDFUtils::AppendScalar(info.fColorOffsets[i - 1], result); result->writeText(" sub\n"); } interpolate_color_code(info.fColorOffsets[i] - info.fColorOffsets[i - 1], colorData[i], colorData[i - 1], result); result->writeText("}\n"); } // Clamp the final color. result->writeText("{pop "); SkPDFUtils::AppendColorComponent(colorData[info.fColorCount - 1][0], result); result->writeText(" "); SkPDFUtils::AppendColorComponent(colorData[info.fColorCount - 1][1], result); result->writeText(" "); SkPDFUtils::AppendColorComponent(colorData[info.fColorCount - 1][2], result); for (int i = 0 ; i < gradients + 1; i++) { result->writeText("} ifelse\n"); } }
static std::unique_ptr<SkPDFDict> gradientStitchCode(const SkShader::GradientInfo& info) { auto retval = SkPDFMakeDict(); // normalize color stops int colorCount = info.fColorCount; std::vector<SkColor> colors(info.fColors, info.fColors + colorCount); std::vector<SkScalar> colorOffsets(info.fColorOffsets, info.fColorOffsets + colorCount); int i = 1; while (i < colorCount - 1) { // ensure stops are in order if (colorOffsets[i - 1] > colorOffsets[i]) { colorOffsets[i] = colorOffsets[i - 1]; } // remove points that are between 2 coincident points if ((colorOffsets[i - 1] == colorOffsets[i]) && (colorOffsets[i] == colorOffsets[i + 1])) { colorCount -= 1; colors.erase(colors.begin() + i); colorOffsets.erase(colorOffsets.begin() + i); } else { i++; } } // find coincident points and slightly move them over for (i = 1; i < colorCount - 1; i++) { if (colorOffsets[i - 1] == colorOffsets[i]) { colorOffsets[i] += 0.00001f; } } // check if last 2 stops coincide if (colorOffsets[i - 1] == colorOffsets[i]) { colorOffsets[i - 1] -= 0.00001f; } SkAutoSTMalloc<4, ColorTuple> colorDataAlloc(colorCount); ColorTuple *colorData = colorDataAlloc.get(); for (int i = 0; i < colorCount; i++) { colorData[i][0] = SkColorGetR(colors[i]); colorData[i][1] = SkColorGetG(colors[i]); colorData[i][2] = SkColorGetB(colors[i]); } // no need for a stitch function if there are only 2 stops. if (colorCount == 2) return createInterpolationFunction(colorData[0], colorData[1]); auto encode = SkPDFMakeArray(); auto bounds = SkPDFMakeArray(); auto functions = SkPDFMakeArray(); retval->insertObject("Domain", SkPDFMakeArray(0, 1)); retval->insertInt("FunctionType", 3); for (int i = 1; i < colorCount; i++) { if (i > 1) { bounds->appendScalar(colorOffsets[i-1]); } encode->appendScalar(0); encode->appendScalar(1.0f); functions->appendObject(createInterpolationFunction(colorData[i-1], colorData[i])); } retval->insertObject("Encode", std::move(encode)); retval->insertObject("Bounds", std::move(bounds)); retval->insertObject("Functions", std::move(functions)); return retval; }