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:
parent
6187f09dcf
commit
04ac46135f
@ -148,13 +148,13 @@ static void transform_shader(SkPaint* paint, const SkMatrix& ctm) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void emit_pdf_color(SkColor color, SkWStream* result) {
|
static void emit_pdf_color(SkColor4f color, SkWStream* result) {
|
||||||
SkASSERT(SkColorGetA(color) == 0xFF); // We handle alpha elsewhere.
|
SkASSERT(color.fA == 1); // We handle alpha elsewhere.
|
||||||
SkPDFUtils::AppendColorComponent(SkColorGetR(color), result);
|
SkPDFUtils::AppendColorComponentF(color.fR, result);
|
||||||
result->writeText(" ");
|
result->writeText(" ");
|
||||||
SkPDFUtils::AppendColorComponent(SkColorGetG(color), result);
|
SkPDFUtils::AppendColorComponentF(color.fG, result);
|
||||||
result->writeText(" ");
|
result->writeText(" ");
|
||||||
SkPDFUtils::AppendColorComponent(SkColorGetB(color), result);
|
SkPDFUtils::AppendColorComponentF(color.fB, result);
|
||||||
result->writeText(" ");
|
result->writeText(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,7 +165,7 @@ void remove_color_filter(SkPaint* paint) {
|
|||||||
if (SkShader* shader = paint->getShader()) {
|
if (SkShader* shader = paint->getShader()) {
|
||||||
paint->setShader(shader->makeWithColorFilter(paint->refColorFilter()));
|
paint->setShader(shader->makeWithColorFilter(paint->refColorFilter()));
|
||||||
} else {
|
} else {
|
||||||
paint->setColor(cf->filterColor(paint->getColor()));
|
paint->setColor4f(cf->filterColor4f(paint->getColor4f(), nullptr), nullptr);
|
||||||
}
|
}
|
||||||
paint->setColorFilter(nullptr);
|
paint->setColorFilter(nullptr);
|
||||||
}
|
}
|
||||||
@ -1699,13 +1699,13 @@ void SkPDFDevice::populateGraphicStateEntryFromPaint(
|
|||||||
entry->fMatrix = matrix;
|
entry->fMatrix = matrix;
|
||||||
entry->fClipStackGenID = clipStack ? clipStack->getTopmostGenID()
|
entry->fClipStackGenID = clipStack ? clipStack->getTopmostGenID()
|
||||||
: SkClipStack::kWideOpenGenID;
|
: SkClipStack::kWideOpenGenID;
|
||||||
entry->fColor = SkColorSetA(paint.getColor(), 0xFF);
|
SkColor4f color = paint.getColor4f();
|
||||||
|
entry->fColor = {color.fR, color.fG, color.fB, 1};
|
||||||
entry->fShaderIndex = -1;
|
entry->fShaderIndex = -1;
|
||||||
|
|
||||||
// PDF treats a shader as a color, so we only set one or the other.
|
// PDF treats a shader as a color, so we only set one or the other.
|
||||||
sk_sp<SkPDFObject> pdfShader;
|
sk_sp<SkPDFObject> pdfShader;
|
||||||
SkShader* shader = paint.getShader();
|
SkShader* shader = paint.getShader();
|
||||||
SkColor color = paint.getColor();
|
|
||||||
if (shader) {
|
if (shader) {
|
||||||
if (SkShader::kColor_GradientType == shader->asAGradient(nullptr)) {
|
if (SkShader::kColor_GradientType == shader->asAGradient(nullptr)) {
|
||||||
// We don't have to set a shader just for a color.
|
// We don't have to set a shader just for a color.
|
||||||
@ -1715,8 +1715,9 @@ void SkPDFDevice::populateGraphicStateEntryFromPaint(
|
|||||||
gradientInfo.fColorOffsets = nullptr;
|
gradientInfo.fColorOffsets = nullptr;
|
||||||
gradientInfo.fColorCount = 1;
|
gradientInfo.fColorCount = 1;
|
||||||
SkAssertResult(shader->asAGradient(&gradientInfo) == SkShader::kColor_GradientType);
|
SkAssertResult(shader->asAGradient(&gradientInfo) == SkShader::kColor_GradientType);
|
||||||
entry->fColor = SkColorSetA(gradientColor, 0xFF);
|
color = SkColor4f::FromColor(gradientColor);
|
||||||
color = gradientColor;
|
entry->fColor ={color.fR, color.fG, color.fB, 1};
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// PDF positions patterns relative to the initial transform, so
|
// PDF positions patterns relative to the initial transform, so
|
||||||
// we need to apply the current transform to the shader parameters.
|
// we need to apply the current transform to the shader parameters.
|
||||||
@ -1744,11 +1745,11 @@ void SkPDFDevice::populateGraphicStateEntryFromPaint(
|
|||||||
}
|
}
|
||||||
|
|
||||||
sk_sp<SkPDFDict> newGraphicState;
|
sk_sp<SkPDFDict> newGraphicState;
|
||||||
if (color == paint.getColor()) {
|
if (color == paint.getColor4f()) {
|
||||||
newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(fDocument->canon(), paint);
|
newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(fDocument->canon(), paint);
|
||||||
} else {
|
} else {
|
||||||
SkPaint newPaint = paint;
|
SkPaint newPaint = paint;
|
||||||
newPaint.setColor(color);
|
newPaint.setColor4f(color, nullptr);
|
||||||
newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(fDocument->canon(), newPaint);
|
newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(fDocument->canon(), newPaint);
|
||||||
}
|
}
|
||||||
entry->fGraphicStateIndex = find_or_add(&fGraphicStateResources, std::move(newGraphicState));
|
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
|
// In the case of alpha images with shaders, the shader's coordinate
|
||||||
// system is the image's coordiantes.
|
// system is the image's coordiantes.
|
||||||
tmpPaint.setShader(sk_ref_sp(paint.getShader()));
|
tmpPaint.setShader(sk_ref_sp(paint.getShader()));
|
||||||
tmpPaint.setColor(paint.getColor());
|
tmpPaint.setColor4f(paint.getColor4f(), nullptr);
|
||||||
canvas->clear(0x00000000);
|
canvas->clear(0x00000000);
|
||||||
canvas->drawImage(imageSubset.image().get(), 0, 0, &tmpPaint);
|
canvas->drawImage(imageSubset.image().get(), 0, 0, &tmpPaint);
|
||||||
paint.setShader(nullptr);
|
paint.setShader(nullptr);
|
||||||
|
@ -123,7 +123,7 @@ public:
|
|||||||
struct GraphicStateEntry {
|
struct GraphicStateEntry {
|
||||||
SkMatrix fMatrix = SkMatrix::I();
|
SkMatrix fMatrix = SkMatrix::I();
|
||||||
uint32_t fClipStackGenID = SkClipStack::kWideOpenGenID;
|
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.
|
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.
|
SkPaint::Style fTextFill = SkPaint::kFill_Style; // Only if TextScaleX is non-zero.
|
||||||
int fShaderIndex = -1;
|
int fShaderIndex = -1;
|
||||||
|
@ -244,21 +244,16 @@ void SkPDFUtils::ApplyPattern(int objectIndex, SkWStream* content) {
|
|||||||
content->writeText(" scn\n");
|
content->writeText(" scn\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t SkPDFUtils::ColorToDecimal(uint8_t value, char result[5]) {
|
// return "x/pow(10, places)", given 0<x<pow(10, places)
|
||||||
if (value == 255 || value == 0) {
|
// result points to places+2 chars.
|
||||||
result[0] = value ? '1' : '0';
|
static size_t print_permil_as_decimal(int x, char* result, unsigned places) {
|
||||||
result[1] = '\0';
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
// int x = 0.5 + (1000.0 / 255.0) * value;
|
|
||||||
int x = SkFixedRoundToInt((SK_Fixed1 * 1000 / 255) * value);
|
|
||||||
result[0] = '.';
|
result[0] = '.';
|
||||||
for (int i = 3; i > 0; --i) {
|
for (int i = places; i > 0; --i) {
|
||||||
result[i] = '0' + x % 10;
|
result[i] = '0' + x % 10;
|
||||||
x /= 10;
|
x /= 10;
|
||||||
}
|
}
|
||||||
int j;
|
int j;
|
||||||
for (j = 3; j > 1; --j) {
|
for (j = places; j > 1; --j) {
|
||||||
if (result[j] != '0') {
|
if (result[j] != '0') {
|
||||||
break;
|
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) {
|
bool SkPDFUtils::InverseTransformBBox(const SkMatrix& matrix, SkRect* bbox) {
|
||||||
SkMatrix inverse;
|
SkMatrix inverse;
|
||||||
if (!matrix.invert(&inverse)) {
|
if (!matrix.invert(&inverse)) {
|
||||||
|
@ -66,11 +66,19 @@ void ApplyPattern(int objectIndex, SkWStream* content);
|
|||||||
// Converts (value / 255.0) with three significant digits of accuracy.
|
// Converts (value / 255.0) with three significant digits of accuracy.
|
||||||
// Writes value as string into result. Returns strlen() of result.
|
// Writes value as string into result. Returns strlen() of result.
|
||||||
size_t ColorToDecimal(uint8_t value, char result[5]);
|
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) {
|
inline void AppendColorComponent(uint8_t value, SkWStream* wStream) {
|
||||||
char buffer[5];
|
char buffer[5];
|
||||||
size_t len = SkPDFUtils::ColorToDecimal(value, buffer);
|
size_t len = SkPDFUtils::ColorToDecimal(value, buffer);
|
||||||
wStream->write(buffer, len);
|
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) {
|
inline void AppendScalar(SkScalar value, SkWStream* stream) {
|
||||||
char result[kMaximumSkFloatToDecimalLength];
|
char result[kMaximumSkFloatToDecimalLength];
|
||||||
|
Loading…
Reference in New Issue
Block a user