diff options
author | halcanary <halcanary@google.com> | 2016-07-15 13:41:27 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-07-15 13:41:28 -0700 |
commit | eb92cb3e84f49b5eedb22b36acffd895d6a90e5a (patch) | |
tree | 0735e84978ab91cfbf507573ac0726d9ea4cc50e | |
parent | f0c30f55665eb3555ca438855d4995baed182e97 (diff) |
SkPdf: smaller color serialization
SkPDFUtils now has a special function (SkPDFUtils::AppendColorComponent)
just for writing out (color/255) as a decimal with three digits of
precision.
SkPDFUnion now has a type to represent a color component. It holds a
utint_8, but calls into AppendColorComponent to serialize.
Added a unit test that tests all possible input values.
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2151863003
Review-Url: https://codereview.chromium.org/2151863003
-rw-r--r-- | bench/PDFBench.cpp | 16 | ||||
-rw-r--r-- | src/pdf/SkPDFDevice.cpp | 7 | ||||
-rw-r--r-- | src/pdf/SkPDFShader.cpp | 55 | ||||
-rw-r--r-- | src/pdf/SkPDFTypes.cpp | 14 | ||||
-rw-r--r-- | src/pdf/SkPDFTypes.h | 4 | ||||
-rw-r--r-- | src/pdf/SkPDFUtils.cpp | 24 | ||||
-rw-r--r-- | src/pdf/SkPDFUtils.h | 9 | ||||
-rw-r--r-- | tests/PDFPrimitivesTest.cpp | 13 |
8 files changed, 110 insertions, 32 deletions
diff --git a/bench/PDFBench.cpp b/bench/PDFBench.cpp index 8f5d2db5b4..19f8e2f8cd 100644 --- a/bench/PDFBench.cpp +++ b/bench/PDFBench.cpp @@ -164,6 +164,21 @@ struct PDFScalarBench : public Benchmark { } }; +struct PDFColorComponentBench : public Benchmark { + bool isSuitableFor(Backend b) override { + return b == kNonRendering_Backend; + } + const char* onGetName() override { return "PDFColorComponent"; } + void onDraw(int loops, SkCanvas*) override { + char dst[5]; + while (loops-- > 0) { + for (int i = 0; i < 256; ++i) { + (void)SkPDFUtils::ColorToDecimal(SkToU8(i), dst); + } + } + } +}; + struct PDFShaderBench : public Benchmark { sk_sp<SkShader> fShader; const char* onGetName() final { return "PDFShader"; } @@ -232,6 +247,7 @@ DEF_BENCH(return new PDFImageBench;) DEF_BENCH(return new PDFJpegImageBench;) DEF_BENCH(return new PDFCompressionBench;) DEF_BENCH(return new PDFScalarBench;) +DEF_BENCH(return new PDFColorComponentBench;) DEF_BENCH(return new PDFShaderBench;) DEF_BENCH(return new WStreamWriteTextBenchmark;) DEF_BENCH(return new WritePDFTextBenchmark;) diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp index 8e76c442be..973ebea314 100644 --- a/src/pdf/SkPDFDevice.cpp +++ b/src/pdf/SkPDFDevice.cpp @@ -51,12 +51,11 @@ static void replace_srcmode_on_opaque_paint(SkPaint* paint) { static void emit_pdf_color(SkColor color, SkWStream* result) { SkASSERT(SkColorGetA(color) == 0xFF); // We handle alpha elsewhere. - SkScalar colorScale = SkScalarInvert(0xFF); - SkPDFUtils::AppendScalar(SkColorGetR(color) * colorScale, result); + SkPDFUtils::AppendColorComponent(SkColorGetR(color), result); result->writeText(" "); - SkPDFUtils::AppendScalar(SkColorGetG(color) * colorScale, result); + SkPDFUtils::AppendColorComponent(SkColorGetG(color), result); result->writeText(" "); - SkPDFUtils::AppendScalar(SkColorGetB(color) * colorScale, result); + SkPDFUtils::AppendColorComponent(SkColorGetB(color), result); result->writeText(" "); } diff --git a/src/pdf/SkPDFShader.cpp b/src/pdf/SkPDFShader.cpp index 942fe65d26..37df296280 100644 --- a/src/pdf/SkPDFShader.cpp +++ b/src/pdf/SkPDFShader.cpp @@ -40,6 +40,9 @@ static void unitToPointsMatrix(const SkPoint pts[2], SkMatrix* matrix) { matrix->postTranslate(pts[0].fX, pts[0].fY); } +static const int kColorComponents = 3; +typedef uint8_t ColorTuple[kColorComponents]; + /* Assumes t + startOffset is on the stack and does a linear interpolation on t between startOffset and endOffset from prevColor to curColor (for each color component), leaving the result in component order on the stack. It assumes @@ -49,16 +52,16 @@ static void unitToPointsMatrix(const SkPoint pts[2], SkMatrix* matrix) { @param prevColor[components] The previous color components. @param result The result ps function. */ -static void interpolateColorCode(SkScalar range, SkScalar* curColor, - SkScalar* prevColor, +static void interpolateColorCode(SkScalar range, const ColorTuple& curColor, + const ColorTuple& prevColor, SkDynamicMemoryWStream* result) { SkASSERT(range != SkIntToScalar(0)); - static const int kColorComponents = 3; // Figure out how to scale each color component. SkScalar multiplier[kColorComponents]; for (int i = 0; i < kColorComponents; i++) { - multiplier[i] = (curColor[i] - prevColor[i]) / range; + static const SkScalar kColorScale = SkScalarInvert(255); + multiplier[i] = kColorScale * (curColor[i] - prevColor[i]) / range; } // Calculate when we no longer need to keep a copy of the input parameter t. @@ -82,7 +85,7 @@ static void interpolateColorCode(SkScalar range, SkScalar* curColor, } if (multiplier[i] == 0) { - SkPDFUtils::AppendScalar(prevColor[i], result); + SkPDFUtils::AppendColorComponent(prevColor[i], result); result->writeText(" "); } else { if (multiplier[i] != 1) { @@ -90,7 +93,7 @@ static void interpolateColorCode(SkScalar range, SkScalar* curColor, result->writeText(" mul "); } if (prevColor[i] != 0) { - SkPDFUtils::AppendScalar(prevColor[i], result); + SkPDFUtils::AppendColorComponent(prevColor[i], result); result->writeText(" add "); } } @@ -122,8 +125,6 @@ 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. @@ -134,20 +135,19 @@ static void gradientFunctionCode(const SkShader::GradientInfo& info, 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); + 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::AppendScalar(colorData[0][0], result); + SkPDFUtils::AppendColorComponent(colorData[0][0], result); result->writeText(" "); - SkPDFUtils::AppendScalar(colorData[0][1], result); + SkPDFUtils::AppendColorComponent(colorData[0][1], result); result->writeText(" "); - SkPDFUtils::AppendScalar(colorData[0][2], result); + SkPDFUtils::AppendColorComponent(colorData[0][2], result); result->writeText(" }\n"); // The gradient colors. @@ -173,11 +173,11 @@ static void gradientFunctionCode(const SkShader::GradientInfo& info, // Clamp the final color. result->writeText("{pop "); - SkPDFUtils::AppendScalar(colorData[info.fColorCount - 1][0], result); + SkPDFUtils::AppendColorComponent(colorData[info.fColorCount - 1][0], result); result->writeText(" "); - SkPDFUtils::AppendScalar(colorData[info.fColorCount - 1][1], result); + SkPDFUtils::AppendColorComponent(colorData[info.fColorCount - 1][1], result); result->writeText(" "); - SkPDFUtils::AppendScalar(colorData[info.fColorCount - 1][2], result); + SkPDFUtils::AppendColorComponent(colorData[info.fColorCount - 1][2], result); for (int i = 0 ; i < gradients + 1; i++) { result->writeText("} ifelse\n"); @@ -189,15 +189,15 @@ static sk_sp<SkPDFDict> createInterpolationFunction(const ColorTuple& color1, auto retval = sk_make_sp<SkPDFDict>(); auto c0 = sk_make_sp<SkPDFArray>(); - c0->appendScalar(color1[0]); - c0->appendScalar(color1[1]); - c0->appendScalar(color1[2]); + c0->appendColorComponent(color1[0]); + c0->appendColorComponent(color1[1]); + c0->appendColorComponent(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]); + c1->appendColorComponent(color2[0]); + c1->appendColorComponent(color2[1]); + c1->appendColorComponent(color2[2]); retval->insertObject("C1", std::move(c1)); auto domain = sk_make_sp<SkPDFArray>(); @@ -248,11 +248,10 @@ static sk_sp<SkPDFDict> gradientStitchCode(const SkShader::GradientInfo& info) { SkAutoSTMalloc<4, ColorTuple> colorDataAlloc(colorCount); ColorTuple *colorData = colorDataAlloc.get(); - const SkScalar scale = SkScalarInvert(SkIntToScalar(255)); for (int i = 0; i < colorCount; i++) { - colorData[i][0] = SkScalarMul(SkColorGetR(colors[i]), scale); - colorData[i][1] = SkScalarMul(SkColorGetG(colors[i]), scale); - colorData[i][2] = SkScalarMul(SkColorGetB(colors[i]), scale); + 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. diff --git a/src/pdf/SkPDFTypes.cpp b/src/pdf/SkPDFTypes.cpp index f9ab5e492a..8c59a4a008 100644 --- a/src/pdf/SkPDFTypes.cpp +++ b/src/pdf/SkPDFTypes.cpp @@ -118,6 +118,9 @@ void SkPDFUnion::emitObject(SkWStream* stream, case Type::kInt: stream->writeDecAsText(fIntValue); return; + case Type::kColorComponent: + SkPDFUtils::AppendColorComponent(SkToU8(fIntValue), stream); + return; case Type::kBool: stream->writeText(fBoolValue ? "true" : "false"); return; @@ -159,6 +162,7 @@ void SkPDFUnion::addResources(SkPDFObjNumMap* objNumMap, const SkPDFSubstituteMap& substituteMap) const { switch (fType) { case Type::kInt: + case Type::kColorComponent: case Type::kBool: case Type::kScalar: case Type::kName: @@ -185,6 +189,12 @@ SkPDFUnion SkPDFUnion::Int(int32_t value) { return u; } +SkPDFUnion SkPDFUnion::ColorComponent(uint8_t value) { + SkPDFUnion u(Type::kColorComponent); + u.fIntValue = value; + return u; +} + SkPDFUnion SkPDFUnion::Bool(bool value) { SkPDFUnion u(Type::kBool); u.fBoolValue = value; @@ -300,6 +310,10 @@ void SkPDFArray::appendInt(int32_t value) { this->append(SkPDFUnion::Int(value)); } +void SkPDFArray::appendColorComponent(uint8_t value) { + this->append(SkPDFUnion::ColorComponent(value)); +} + void SkPDFArray::appendBool(bool value) { this->append(SkPDFUnion::Bool(value)); } diff --git a/src/pdf/SkPDFTypes.h b/src/pdf/SkPDFTypes.h index cff60e539a..5e4de3a259 100644 --- a/src/pdf/SkPDFTypes.h +++ b/src/pdf/SkPDFTypes.h @@ -90,6 +90,8 @@ public: static SkPDFUnion Scalar(SkScalar); + static SkPDFUnion ColorComponent(uint8_t); + /** These two functions do NOT take ownership of char*, and do NOT copy the string. Suitable for passing in static const strings. For example: @@ -139,6 +141,7 @@ private: kDestroyed object. */ kDestroyed = 0, kInt, + kColorComponent, kBool, kScalar, kName, @@ -212,6 +215,7 @@ public: * @param value The value to add to the array. */ void appendInt(int32_t); + void appendColorComponent(uint8_t); void appendBool(bool); void appendScalar(SkScalar); void appendName(const char[]); diff --git a/src/pdf/SkPDFUtils.cpp b/src/pdf/SkPDFUtils.cpp index b8d65092b0..9e4ac515ba 100644 --- a/src/pdf/SkPDFUtils.cpp +++ b/src/pdf/SkPDFUtils.cpp @@ -7,6 +7,7 @@ #include "SkData.h" +#include "SkFixed.h" #include "SkGeometry.h" #include "SkPDFResourceDict.h" #include "SkPDFUtils.h" @@ -251,6 +252,29 @@ void SkPDFUtils::ApplyPattern(int objectIndex, SkWStream* content) { content->writeText(" scn\n"); } +size_t SkPDFUtils::ColorToDecimal(uint8_t value, char result[5]) { + if (value == 255 || value == 0) { + result[0] = value ? '1' : '0'; + result[1] = '\0'; + return 1; + } + // int x = 0.5 + (1000.0 / 255.0) * value; + int x = SkFixedRoundToInt((SK_Fixed1 * 1000 / 255) * value); + result[0] = '.'; + for (int i = 3; i > 0; --i) { + result[i] = '0' + x % 10; + x /= 10; + } + int j; + for (j = 3; j > 1; --j) { + if (result[j] != '0') { + break; + } + } + result[j + 1] = '\0'; + return j + 1; +} + void SkPDFUtils::AppendScalar(SkScalar value, SkWStream* stream) { char result[kMaximumFloatDecimalLength]; size_t len = SkPDFUtils::FloatToDecimal(SkScalarToFloat(value), result); diff --git a/src/pdf/SkPDFUtils.h b/src/pdf/SkPDFUtils.h index 3ddd3d0933..a9194f2e72 100644 --- a/src/pdf/SkPDFUtils.h +++ b/src/pdf/SkPDFUtils.h @@ -57,6 +57,15 @@ void DrawFormXObject(int objectIndex, SkWStream* content); void ApplyGraphicState(int objectIndex, SkWStream* content); void ApplyPattern(int objectIndex, SkWStream* content); +// Converts (value / 255.0) with three significant digits of accuracy. +// Writes value as string into result. Returns strlen() of result. +size_t ColorToDecimal(uint8_t value, char result[5]); +inline void AppendColorComponent(uint8_t value, SkWStream* wStream) { + char buffer[5]; + size_t len = SkPDFUtils::ColorToDecimal(value, buffer); + wStream->write(buffer, len); +} + // 3 = '-', '.', and '\0' characters. // 9 = number of significant digits // abs(FLT_MIN_10_EXP) = number of zeros in FLT_MIN diff --git a/tests/PDFPrimitivesTest.cpp b/tests/PDFPrimitivesTest.cpp index 9e2a89e609..d816b60823 100644 --- a/tests/PDFPrimitivesTest.cpp +++ b/tests/PDFPrimitivesTest.cpp @@ -491,3 +491,16 @@ DEF_TEST(PDFPrimitives_Scalar, reporter) { check_pdf_scalar_serialization(reporter, inputFloat); } } + +// Test SkPDFUtils:: for accuracy. +DEF_TEST(PDFPrimitives_Color, reporter) { + char buffer[5]; + for (int i = 0; i < 256; ++i) { + size_t len = SkPDFUtils::ColorToDecimal(i, buffer); + REPORTER_ASSERT(reporter, len == strlen(buffer)); + float f; + REPORTER_ASSERT(reporter, 1 == sscanf(buffer, "%f", &f)); + int roundTrip = (int)(0.5 + f * 255); + REPORTER_ASSERT(reporter, roundTrip == i); + } +} |