SkPDF: more accurate (float) colors from paint

Does not yet affect colors from gradient shaders.

Serialize colors with 4 digits of accuracy, not 3.

Resulting PDFs are usually slightly larger, but render the same when
rendered into an 8-bit buffer.

Change-Id: I64336f3a1f34021f9ddb723bd8a16d51ddfea0f4
Reviewed-on: https://skia-review.googlesource.com/c/161141
Commit-Queue: Hal Canary <halcanary@google.com>
Auto-Submit: Hal Canary <halcanary@google.com>
Reviewed-by: Mike Klein <mtklein@google.com>
This commit is contained in:
Hal Canary 2018-10-10 13:09:43 -04:00 committed by Skia Commit-Bot
parent 6187f09dcf
commit 04ac46135f
4 changed files with 58 additions and 24 deletions

View File

@ -148,13 +148,13 @@ static void transform_shader(SkPaint* paint, const SkMatrix& ctm) {
}
}
static void emit_pdf_color(SkColor color, SkWStream* result) {
SkASSERT(SkColorGetA(color) == 0xFF); // We handle alpha elsewhere.
SkPDFUtils::AppendColorComponent(SkColorGetR(color), result);
static void emit_pdf_color(SkColor4f color, SkWStream* result) {
SkASSERT(color.fA == 1); // We handle alpha elsewhere.
SkPDFUtils::AppendColorComponentF(color.fR, result);
result->writeText(" ");
SkPDFUtils::AppendColorComponent(SkColorGetG(color), result);
SkPDFUtils::AppendColorComponentF(color.fG, result);
result->writeText(" ");
SkPDFUtils::AppendColorComponent(SkColorGetB(color), result);
SkPDFUtils::AppendColorComponentF(color.fB, result);
result->writeText(" ");
}
@ -165,7 +165,7 @@ void remove_color_filter(SkPaint* paint) {
if (SkShader* shader = paint->getShader()) {
paint->setShader(shader->makeWithColorFilter(paint->refColorFilter()));
} else {
paint->setColor(cf->filterColor(paint->getColor()));
paint->setColor4f(cf->filterColor4f(paint->getColor4f(), nullptr), nullptr);
}
paint->setColorFilter(nullptr);
}
@ -1699,13 +1699,13 @@ void SkPDFDevice::populateGraphicStateEntryFromPaint(
entry->fMatrix = matrix;
entry->fClipStackGenID = clipStack ? clipStack->getTopmostGenID()
: SkClipStack::kWideOpenGenID;
entry->fColor = SkColorSetA(paint.getColor(), 0xFF);
SkColor4f color = paint.getColor4f();
entry->fColor = {color.fR, color.fG, color.fB, 1};
entry->fShaderIndex = -1;
// PDF treats a shader as a color, so we only set one or the other.
sk_sp<SkPDFObject> pdfShader;
SkShader* shader = paint.getShader();
SkColor color = paint.getColor();
if (shader) {
if (SkShader::kColor_GradientType == shader->asAGradient(nullptr)) {
// We don't have to set a shader just for a color.
@ -1715,8 +1715,9 @@ void SkPDFDevice::populateGraphicStateEntryFromPaint(
gradientInfo.fColorOffsets = nullptr;
gradientInfo.fColorCount = 1;
SkAssertResult(shader->asAGradient(&gradientInfo) == SkShader::kColor_GradientType);
entry->fColor = SkColorSetA(gradientColor, 0xFF);
color = gradientColor;
color = SkColor4f::FromColor(gradientColor);
entry->fColor ={color.fR, color.fG, color.fB, 1};
} else {
// PDF positions patterns relative to the initial transform, so
// we need to apply the current transform to the shader parameters.
@ -1744,11 +1745,11 @@ void SkPDFDevice::populateGraphicStateEntryFromPaint(
}
sk_sp<SkPDFDict> newGraphicState;
if (color == paint.getColor()) {
if (color == paint.getColor4f()) {
newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(fDocument->canon(), paint);
} else {
SkPaint newPaint = paint;
newPaint.setColor(color);
newPaint.setColor4f(color, nullptr);
newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(fDocument->canon(), newPaint);
}
entry->fGraphicStateIndex = find_or_add(&fGraphicStateResources, std::move(newGraphicState));
@ -1839,7 +1840,7 @@ void SkPDFDevice::internalDrawImageRect(SkKeyedImage imageSubset,
// In the case of alpha images with shaders, the shader's coordinate
// system is the image's coordiantes.
tmpPaint.setShader(sk_ref_sp(paint.getShader()));
tmpPaint.setColor(paint.getColor());
tmpPaint.setColor4f(paint.getColor4f(), nullptr);
canvas->clear(0x00000000);
canvas->drawImage(imageSubset.image().get(), 0, 0, &tmpPaint);
paint.setShader(nullptr);

View File

@ -123,7 +123,7 @@ public:
struct GraphicStateEntry {
SkMatrix fMatrix = SkMatrix::I();
uint32_t fClipStackGenID = SkClipStack::kWideOpenGenID;
SkColor fColor = SK_ColorBLACK;
SkColor4f fColor = {0, 0, 0, 1};
SkScalar fTextScaleX = 1; // Zero means we don't care what the value is.
SkPaint::Style fTextFill = SkPaint::kFill_Style; // Only if TextScaleX is non-zero.
int fShaderIndex = -1;

View File

@ -244,21 +244,16 @@ 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);
// return "x/pow(10, places)", given 0<x<pow(10, places)
// result points to places+2 chars.
static size_t print_permil_as_decimal(int x, char* result, unsigned places) {
result[0] = '.';
for (int i = 3; i > 0; --i) {
for (int i = places; i > 0; --i) {
result[i] = '0' + x % 10;
x /= 10;
}
int j;
for (j = 3; j > 1; --j) {
for (j = places; j > 1; --j) {
if (result[j] != '0') {
break;
}
@ -268,6 +263,36 @@ size_t SkPDFUtils::ColorToDecimal(uint8_t value, char result[5]) {
}
static constexpr int int_pow(int base, unsigned exp, int acc = 1) {
return exp < 1 ? acc
: int_pow(base * base,
exp / 2,
(exp % 2) ? acc * base : acc);
}
size_t SkPDFUtils::ColorToDecimalF(float value, char result[kFloatColorDecimalCount + 2]) {
static constexpr int kFactor = int_pow(10, kFloatColorDecimalCount);
int x = sk_float_round2int(value * kFactor);
if (x >= kFactor || x <= 0) { // clamp to 0-1
result[0] = x > 0 ? '1' : '0';
result[1] = '\0';
return 1;
}
return print_permil_as_decimal(x, result, kFloatColorDecimalCount);
}
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);
return print_permil_as_decimal(x, result, 3);
}
bool SkPDFUtils::InverseTransformBBox(const SkMatrix& matrix, SkRect* bbox) {
SkMatrix inverse;
if (!matrix.invert(&inverse)) {

View File

@ -66,11 +66,19 @@ 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]);
static constexpr unsigned kFloatColorDecimalCount = 4;
size_t ColorToDecimalF(float value, char result[kFloatColorDecimalCount + 2]);
inline void AppendColorComponent(uint8_t value, SkWStream* wStream) {
char buffer[5];
size_t len = SkPDFUtils::ColorToDecimal(value, buffer);
wStream->write(buffer, len);
}
inline void AppendColorComponentF(float value, SkWStream* wStream) {
char buffer[kFloatColorDecimalCount + 2];
size_t len = SkPDFUtils::ColorToDecimalF(value, buffer);
wStream->write(buffer, len);
}
inline void AppendScalar(SkScalar value, SkWStream* stream) {
char result[kMaximumSkFloatToDecimalLength];