OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * Copyright 2016 Google Inc. |
| 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. |
| 6 */ |
| 7 |
| 8 #include "SkPngPredictors.h" |
| 9 #include "SkTypes.h" |
| 10 |
| 11 // SkPngPredictors: Implement PNG-style prediction filters on a |
| 12 // scanline-by-scanline basis for SkPDF. |
| 13 |
| 14 // See <https://www.w3.org/TR/PNG/#9Filters> for reference. |
| 15 // Where used, a,b,c,x have the same meaning as in the standard. |
| 16 |
| 17 // "The approach that has by now become standard is known as the |
| 18 // minimum sum of absolute differences heuristic and was first |
| 19 // proposed by Lee Daniel Crocker in February 1995. In this |
| 20 // approach, the filtered bytes are treated as signed [bytes]. The |
| 21 // absolute value of each is then summed, and the filter type that |
| 22 // produces the smallest sum is chosen." |
| 23 |
| 24 static int8_t weight(uint8_t x) { |
| 25 return SkTAbs(reinterpret_cast<int8_t&>(x)); |
| 26 } |
| 27 |
| 28 // Type:0; Name:None; Filt(x) = Orig(x) |
| 29 // BPP == bytes-per-pixel |
| 30 // track: iff true, do not write into out; instead calculate "cost" of out. |
| 31 template<int BPP, bool track> |
| 32 static int none(int width, const uint8_t* prev, |
| 33 const uint8_t* scanline, uint8_t* out) { |
| 34 int64_t w = 0; |
| 35 (void)prev; |
| 36 static_assert(BPP > 0, ""); |
| 37 if (track) { |
| 38 const uint8_t* end = &scanline[BPP * width]; |
| 39 while (scanline != end) { |
| 40 w += weight(*scanline++); |
| 41 } |
| 42 } else { |
| 43 if (out != scanline) { |
| 44 memcpy(out, scanline, width * BPP); |
| 45 } |
| 46 } |
| 47 return SkToInt(w); |
| 48 } |
| 49 |
| 50 // Type:1; Name:Sub; Filt(x) = Orig(x) - Orig(a) |
| 51 template<int BPP, bool track> |
| 52 static int sub(int width, const uint8_t* prev, |
| 53 const uint8_t* scanline, uint8_t* out) { |
| 54 static_assert(BPP > 0, ""); |
| 55 (void)prev; // not used |
| 56 int64_t w = 0; // Use int64: SkToInt will catch overflow in worst case. |
| 57 uint8_t a[BPP]{}; // a = left |
| 58 const uint8_t* end = &scanline[BPP * width]; |
| 59 while (scanline != end) { |
| 60 for (int i = 0; i < BPP; ++i) { |
| 61 uint8_t x = *scanline++; |
| 62 uint8_t v = x - a[i]; |
| 63 if (track) { w += weight(v); } else { *out++ = v; } |
| 64 a[i] = x; |
| 65 } |
| 66 } |
| 67 return SkToInt(w); |
| 68 } |
| 69 // Type:2; Name:Up; Filt(x) = Orig(x) - Orig(b) |
| 70 template<int BPP, bool track> |
| 71 static int up(int width, const uint8_t* prev, |
| 72 const uint8_t* scanline, uint8_t* out) { |
| 73 if (prev) { |
| 74 int64_t w = 0; |
| 75 const uint8_t* end = &scanline[BPP * width]; |
| 76 while (scanline != end) { |
| 77 for (int i = 0; i < BPP; ++i) { |
| 78 uint8_t v = *scanline++ - *prev++; |
| 79 if (track) { w += weight(v); } else { *out++ = v; } |
| 80 } |
| 81 } |
| 82 return SkToInt(w); |
| 83 } else { |
| 84 return none<BPP, track>(width, prev, scanline, out); |
| 85 } |
| 86 } |
| 87 |
| 88 // Type:3; Name:Average; Filt(x) = Orig(x) - floor((Orig(a) + Orig(b)) / 2) |
| 89 template<int BPP, bool track> |
| 90 static int average(int width, const uint8_t* prev, |
| 91 const uint8_t* scanline, uint8_t* out) { |
| 92 static_assert(BPP > 0, ""); |
| 93 int64_t w = 0; |
| 94 const uint8_t* end = &scanline[BPP * width]; |
| 95 if (prev) { |
| 96 uint8_t a[BPP]{}; // a = left, b = above |
| 97 while (scanline != end) { |
| 98 for (int i = 0; i < BPP; ++i) { |
| 99 uint8_t x = *scanline++; |
| 100 uint8_t v = x - SkToU8(((int)a[i] + (int)(*prev++)) / 2); |
| 101 if (track) { w += weight(v); } else { *out++ = v; } |
| 102 a[i] = x; |
| 103 } |
| 104 } |
| 105 } else { |
| 106 uint8_t a[BPP]{}; |
| 107 while (scanline != end) { |
| 108 for (int i = 0; i < BPP; ++i) { |
| 109 uint8_t x = *scanline++; |
| 110 uint8_t v = x - a[i] / 2; // average with zero. |
| 111 if (track) { w += weight(v); } else { *out++ = v; } |
| 112 a[i] = x; |
| 113 } |
| 114 } |
| 115 } |
| 116 return SkToInt(w); |
| 117 } |
| 118 |
| 119 // Type:4; Name: Paeth; |
| 120 // Filt(x) = Orig(x) - PaethPredictor(Orig(a), Orig(b), Orig(c)) |
| 121 template<int BPP, bool track> |
| 122 static int paeth(int width, const uint8_t* prev, const uint8_t* scanline, uint8_
t* out) { |
| 123 static_assert(BPP > 0, ""); |
| 124 // a = left, b = above, c = upper left |
| 125 if (!prev) { |
| 126 return sub<BPP, track>(width, prev, scanline, out); |
| 127 } |
| 128 int64_t w = 0; |
| 129 uint8_t a[BPP]{}, c[BPP]{}; |
| 130 const uint8_t* end = &scanline[BPP * width]; |
| 131 while (scanline != end) { |
| 132 for (int i = 0; i < BPP; ++i) { |
| 133 uint8_t b = *prev++; |
| 134 uint8_t x = *scanline++; |
| 135 int pa = SkTAbs<int>(b - c[i]); |
| 136 int pb = SkTAbs<int>(a[i] - c[i]); |
| 137 int pc = SkTAbs<int>((b - c[i]) + (a[i] - c[i])); |
| 138 uint8_t pred = a[i]; |
| 139 if (pb < pa) { pred = b; pa = pb; } |
| 140 if (pc < pa) { pred = c[i]; } |
| 141 uint8_t v = x - pred; |
| 142 if (track) { w += weight(v); } else { *out++ = v; } |
| 143 a[i] = x; |
| 144 c[i] = b; |
| 145 } |
| 146 } |
| 147 return SkToInt(w); |
| 148 } |
| 149 |
| 150 // prev == out is okay. |
| 151 // prev can be null. |
| 152 // Return the type that is used. |
| 153 template<int BPP> |
| 154 uint8_t png_encode(int width, |
| 155 const uint8_t* prev, |
| 156 const uint8_t* scanline, |
| 157 uint8_t* out) { |
| 158 int scores[5]; |
| 159 scores[0] = none<BPP, true>(width, prev, scanline, nullptr); |
| 160 scores[1] = sub<BPP, true>(width, prev, scanline, nullptr); |
| 161 scores[2] = up<BPP, true>(width, prev, scanline, nullptr); |
| 162 scores[3] = average<BPP, true>(width, prev, scanline, nullptr); |
| 163 scores[4] = paeth<BPP, true>(width, prev, scanline, nullptr); |
| 164 uint8_t best = 0; |
| 165 int best_score = scores[0]; |
| 166 for (uint8_t i = 1; i < SK_ARRAY_COUNT(scores); ++i) { |
| 167 if (scores[i] < best_score) { |
| 168 best = i; |
| 169 best_score = scores[i]; |
| 170 } |
| 171 } |
| 172 // 9087 === 0 |
| 173 // 2346 === 1 |
| 174 // 6698 === 2 |
| 175 // 12747 === 3 |
| 176 // 6216 === 4 |
| 177 // SkDebugf("=== %d\n", best); |
| 178 switch (best) { |
| 179 case 0: |
| 180 (void)none<BPP, false>(width, prev, scanline, out); |
| 181 return best; |
| 182 case 1: |
| 183 (void)sub<BPP, false>(width, prev, scanline, out); |
| 184 return best; |
| 185 case 2: |
| 186 (void)up<BPP, false>(width, prev, scanline, out); |
| 187 return best; |
| 188 case 3: |
| 189 (void)average<BPP, false>(width, prev, scanline, out); |
| 190 return best; |
| 191 case 4: |
| 192 (void)paeth<BPP, false>(width, prev, scanline, out); |
| 193 return best; |
| 194 } |
| 195 SkASSERT(false); |
| 196 return 0; |
| 197 } |
| 198 |
| 199 uint8_t SkPngEncode3(int width, const uint8_t* prev, |
| 200 const uint8_t* scanline, uint8_t* out) { |
| 201 return png_encode<3>(width, prev, scanline, out); |
| 202 } |
| 203 |
| 204 uint8_t SkPngEncode1(int width, const uint8_t* prev, |
| 205 const uint8_t* scanline, uint8_t* out) { |
| 206 return png_encode<1>(width, prev, scanline, out); |
| 207 } |
| 208 |
| 209 uint8_t SkPaethEncode3(int width, const uint8_t* prev, |
| 210 const uint8_t* scanline, uint8_t* out) { |
| 211 (void)paeth<3, false>(width, prev, scanline, out); |
| 212 return 4; |
| 213 } |
| 214 |
| 215 uint8_t SkPaethEncode1(int width, const uint8_t* prev, |
| 216 const uint8_t* scanline, uint8_t* out) { |
| 217 (void)paeth<1, false>(width, prev, scanline, out); |
| 218 return 4; |
| 219 } |
OLD | NEW |