| Index: src/pdf/SkPngPredictors.cpp
|
| diff --git a/src/pdf/SkPngPredictors.cpp b/src/pdf/SkPngPredictors.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..adc49698aaf593f5edc681d17fc182e4f6fe9e5e
|
| --- /dev/null
|
| +++ b/src/pdf/SkPngPredictors.cpp
|
| @@ -0,0 +1,219 @@
|
| +/*
|
| + * Copyright 2016 Google Inc.
|
| + *
|
| + * Use of this source code is governed by a BSD-style license that can be
|
| + * found in the LICENSE file.
|
| + */
|
| +
|
| +#include "SkPngPredictors.h"
|
| +#include "SkTypes.h"
|
| +
|
| +// SkPngPredictors: Implement PNG-style prediction filters on a
|
| +// scanline-by-scanline basis for SkPDF.
|
| +
|
| +// See <https://www.w3.org/TR/PNG/#9Filters> for reference.
|
| +// Where used, a,b,c,x have the same meaning as in the standard.
|
| +
|
| +// "The approach that has by now become standard is known as the
|
| +// minimum sum of absolute differences heuristic and was first
|
| +// proposed by Lee Daniel Crocker in February 1995. In this
|
| +// approach, the filtered bytes are treated as signed [bytes]. The
|
| +// absolute value of each is then summed, and the filter type that
|
| +// produces the smallest sum is chosen."
|
| +
|
| +static int8_t weight(uint8_t x) {
|
| + return SkTAbs(reinterpret_cast<int8_t&>(x));
|
| +}
|
| +
|
| +// Type:0; Name:None; Filt(x) = Orig(x)
|
| +// BPP == bytes-per-pixel
|
| +// track: iff true, do not write into out; instead calculate "cost" of out.
|
| +template<int BPP, bool track>
|
| +static int none(int width, const uint8_t* prev,
|
| + const uint8_t* scanline, uint8_t* out) {
|
| + int64_t w = 0;
|
| + (void)prev;
|
| + static_assert(BPP > 0, "");
|
| + if (track) {
|
| + const uint8_t* end = &scanline[BPP * width];
|
| + while (scanline != end) {
|
| + w += weight(*scanline++);
|
| + }
|
| + } else {
|
| + if (out != scanline) {
|
| + memcpy(out, scanline, width * BPP);
|
| + }
|
| + }
|
| + return SkToInt(w);
|
| +}
|
| +
|
| +// Type:1; Name:Sub; Filt(x) = Orig(x) - Orig(a)
|
| +template<int BPP, bool track>
|
| +static int sub(int width, const uint8_t* prev,
|
| + const uint8_t* scanline, uint8_t* out) {
|
| + static_assert(BPP > 0, "");
|
| + (void)prev; // not used
|
| + int64_t w = 0; // Use int64: SkToInt will catch overflow in worst case.
|
| + uint8_t a[BPP]{}; // a = left
|
| + const uint8_t* end = &scanline[BPP * width];
|
| + while (scanline != end) {
|
| + for (int i = 0; i < BPP; ++i) {
|
| + uint8_t x = *scanline++;
|
| + uint8_t v = x - a[i];
|
| + if (track) { w += weight(v); } else { *out++ = v; }
|
| + a[i] = x;
|
| + }
|
| + }
|
| + return SkToInt(w);
|
| +}
|
| +// Type:2; Name:Up; Filt(x) = Orig(x) - Orig(b)
|
| +template<int BPP, bool track>
|
| +static int up(int width, const uint8_t* prev,
|
| + const uint8_t* scanline, uint8_t* out) {
|
| + if (prev) {
|
| + int64_t w = 0;
|
| + const uint8_t* end = &scanline[BPP * width];
|
| + while (scanline != end) {
|
| + for (int i = 0; i < BPP; ++i) {
|
| + uint8_t v = *scanline++ - *prev++;
|
| + if (track) { w += weight(v); } else { *out++ = v; }
|
| + }
|
| + }
|
| + return SkToInt(w);
|
| + } else {
|
| + return none<BPP, track>(width, prev, scanline, out);
|
| + }
|
| +}
|
| +
|
| +// Type:3; Name:Average; Filt(x) = Orig(x) - floor((Orig(a) + Orig(b)) / 2)
|
| +template<int BPP, bool track>
|
| +static int average(int width, const uint8_t* prev,
|
| + const uint8_t* scanline, uint8_t* out) {
|
| + static_assert(BPP > 0, "");
|
| + int64_t w = 0;
|
| + const uint8_t* end = &scanline[BPP * width];
|
| + if (prev) {
|
| + uint8_t a[BPP]{}; // a = left, b = above
|
| + while (scanline != end) {
|
| + for (int i = 0; i < BPP; ++i) {
|
| + uint8_t x = *scanline++;
|
| + uint8_t v = x - SkToU8(((int)a[i] + (int)(*prev++)) / 2);
|
| + if (track) { w += weight(v); } else { *out++ = v; }
|
| + a[i] = x;
|
| + }
|
| + }
|
| + } else {
|
| + uint8_t a[BPP]{};
|
| + while (scanline != end) {
|
| + for (int i = 0; i < BPP; ++i) {
|
| + uint8_t x = *scanline++;
|
| + uint8_t v = x - a[i] / 2; // average with zero.
|
| + if (track) { w += weight(v); } else { *out++ = v; }
|
| + a[i] = x;
|
| + }
|
| + }
|
| + }
|
| + return SkToInt(w);
|
| +}
|
| +
|
| +// Type:4; Name: Paeth;
|
| +// Filt(x) = Orig(x) - PaethPredictor(Orig(a), Orig(b), Orig(c))
|
| +template<int BPP, bool track>
|
| +static int paeth(int width, const uint8_t* prev, const uint8_t* scanline, uint8_t* out) {
|
| + static_assert(BPP > 0, "");
|
| + // a = left, b = above, c = upper left
|
| + if (!prev) {
|
| + return sub<BPP, track>(width, prev, scanline, out);
|
| + }
|
| + int64_t w = 0;
|
| + uint8_t a[BPP]{}, c[BPP]{};
|
| + const uint8_t* end = &scanline[BPP * width];
|
| + while (scanline != end) {
|
| + for (int i = 0; i < BPP; ++i) {
|
| + uint8_t b = *prev++;
|
| + uint8_t x = *scanline++;
|
| + int pa = SkTAbs<int>(b - c[i]);
|
| + int pb = SkTAbs<int>(a[i] - c[i]);
|
| + int pc = SkTAbs<int>((b - c[i]) + (a[i] - c[i]));
|
| + uint8_t pred = a[i];
|
| + if (pb < pa) { pred = b; pa = pb; }
|
| + if (pc < pa) { pred = c[i]; }
|
| + uint8_t v = x - pred;
|
| + if (track) { w += weight(v); } else { *out++ = v; }
|
| + a[i] = x;
|
| + c[i] = b;
|
| + }
|
| + }
|
| + return SkToInt(w);
|
| +}
|
| +
|
| +// prev == out is okay.
|
| +// prev can be null.
|
| +// Return the type that is used.
|
| +template<int BPP>
|
| +uint8_t png_encode(int width,
|
| + const uint8_t* prev,
|
| + const uint8_t* scanline,
|
| + uint8_t* out) {
|
| + int scores[5];
|
| + scores[0] = none<BPP, true>(width, prev, scanline, nullptr);
|
| + scores[1] = sub<BPP, true>(width, prev, scanline, nullptr);
|
| + scores[2] = up<BPP, true>(width, prev, scanline, nullptr);
|
| + scores[3] = average<BPP, true>(width, prev, scanline, nullptr);
|
| + scores[4] = paeth<BPP, true>(width, prev, scanline, nullptr);
|
| + uint8_t best = 0;
|
| + int best_score = scores[0];
|
| + for (uint8_t i = 1; i < SK_ARRAY_COUNT(scores); ++i) {
|
| + if (scores[i] < best_score) {
|
| + best = i;
|
| + best_score = scores[i];
|
| + }
|
| + }
|
| + // 9087 === 0
|
| + // 2346 === 1
|
| + // 6698 === 2
|
| + // 12747 === 3
|
| + // 6216 === 4
|
| + // SkDebugf("=== %d\n", best);
|
| + switch (best) {
|
| + case 0:
|
| + (void)none<BPP, false>(width, prev, scanline, out);
|
| + return best;
|
| + case 1:
|
| + (void)sub<BPP, false>(width, prev, scanline, out);
|
| + return best;
|
| + case 2:
|
| + (void)up<BPP, false>(width, prev, scanline, out);
|
| + return best;
|
| + case 3:
|
| + (void)average<BPP, false>(width, prev, scanline, out);
|
| + return best;
|
| + case 4:
|
| + (void)paeth<BPP, false>(width, prev, scanline, out);
|
| + return best;
|
| + }
|
| + SkASSERT(false);
|
| + return 0;
|
| +}
|
| +
|
| +uint8_t SkPngEncode3(int width, const uint8_t* prev,
|
| + const uint8_t* scanline, uint8_t* out) {
|
| + return png_encode<3>(width, prev, scanline, out);
|
| +}
|
| +
|
| +uint8_t SkPngEncode1(int width, const uint8_t* prev,
|
| + const uint8_t* scanline, uint8_t* out) {
|
| + return png_encode<1>(width, prev, scanline, out);
|
| +}
|
| +
|
| +uint8_t SkPaethEncode3(int width, const uint8_t* prev,
|
| + const uint8_t* scanline, uint8_t* out) {
|
| + (void)paeth<3, false>(width, prev, scanline, out);
|
| + return 4;
|
| +}
|
| +
|
| +uint8_t SkPaethEncode1(int width, const uint8_t* prev,
|
| + const uint8_t* scanline, uint8_t* out) {
|
| + (void)paeth<1, false>(width, prev, scanline, out);
|
| + return 4;
|
| +}
|
|
|