Index: src/codec/SkPngCodec.cpp |
diff --git a/src/codec/SkPngCodec.cpp b/src/codec/SkPngCodec.cpp |
index 66215a6c73eef2ffa6206ab30795b6b7d544932c..6de757469c115f5d31ddaa9c06bb154ec7430bc9 100644 |
--- a/src/codec/SkPngCodec.cpp |
+++ b/src/codec/SkPngCodec.cpp |
@@ -16,6 +16,75 @@ |
#include "SkSwizzler.h" |
#include "SkTemplates.h" |
+#if defined(__SSE2__) |
msarett
2016/01/26 22:35:02
Is there another file we can put this in?
Seems t
mtklein
2016/01/26 22:52:17
I do think there will be more of these.
We'll def
mtklein_C
2016/01/27 00:21:26
I've moved things to a new file, src/codec/SkPngFi
|
+ #include "pngstruct.h" |
+ |
+ // Returns bytewise |x-y|. |
+ static __m128i absdiff_u8(__m128i x, __m128i y) { |
+ // One of these two saturated subtractions will be the answer, the other zero. |
+ return _mm_or_si128(_mm_subs_epu8(x,y), _mm_subs_epu8(y,x)); |
+ } |
+ |
+ // Bytewise c ? t : e. |
+ static __m128i if_then_else(__m128i c, __m128i t, __m128i e) { |
+ // SSE 4.1+ would be: return _mm_blendv_epi8(e,t,c); |
+ return _mm_or_si128(_mm_and_si128(c, t), _mm_andnot_si128(c, e)); |
+ } |
+ |
+ static void sk_paeth4_sse2(png_row_infop row_info, png_bytep row_u8, png_const_bytep prev_u8) { |
+ auto prev = (const uint32_t*)prev_u8; |
+ auto row = (uint32_t*)row_u8; |
+ int n = row_info->rowbytes / 4; |
+ |
+ // Paeth tries to predict pixel d using the pixel to the left of it, a, |
+ // and two pixels from the previous row, b and c: |
+ // prev: c b |
+ // row: a d |
+ // The Paeth function predicts d to be whichever of a, b, or c is nearest to p=a+b-c. |
+ |
+ // The first pixel has no left context, and so uses an Up filter, p = b. |
+ // This works naturally with our main loop's p = a+b-c if we force a and c to zero. |
+ // Here we zero b and d, which become c and a respectively at the start of the loop. |
+ __m128i c, b = _mm_setzero_si128(), |
+ a, d = _mm_setzero_si128(); |
+ |
+ while (n --> 0) { |
+ c = b; b = _mm_cvtsi32_si128(*prev++); |
+ a = d; d = _mm_cvtsi32_si128(*row); |
+ |
+ // We can't express p in 8 bits, but luckily we can use this faux p instead: |
+ __m128i min = _mm_min_epu8(a,b), |
+ max = _mm_max_epu8(a,b), |
+ faux_p = _mm_adds_epu8(min, _mm_subs_epu8(max, c)); |
+ |
+ // We could use faux_p for calculating all three of pa,pb,pc, |
+ // but it's a little quicker to calculate the correct pa and pb directly. |
+ __m128i pa = absdiff_u8(b,c), // |a+b-c - a| == |b-c| |
+ pb = absdiff_u8(a,c), // |a+b-c - b| == |a-c| |
+ faux_pc = absdiff_u8(faux_p, c); |
+ |
+ // From here, things are straightforward. Find the smallest distance to p... |
+ __m128i smallest = _mm_min_epu8(_mm_min_epu8(pa, pb), faux_pc); |
+ |
+ // ... then the predictor is the input corresponding to that smallest distance, |
+ // breaking ties in favor of a over b over c. |
+ __m128i nearest = if_then_else(_mm_cmpeq_epi8(smallest, pa), a, |
+ if_then_else(_mm_cmpeq_epi8(smallest, pb), b, |
+ c)); |
+ |
+ // We've reconstructed d! Leave it for next round to become a, and write it out. |
+ d = _mm_add_epi8(d, nearest); |
+ *row++ = _mm_cvtsi128_si32(d); |
+ } |
+ } |
+ |
+ extern "C" void sk_png_init_filter_functions_sse2(png_structp png, unsigned int bpp) { |
+ if (bpp == 4) { |
+ png->read_filter[PNG_FILTER_VALUE_PAETH-1] = sk_paeth4_sse2; |
+ } |
+ } |
+#endif |
+ |
/////////////////////////////////////////////////////////////////////////////// |
// Helper macros |
/////////////////////////////////////////////////////////////////////////////// |
@@ -331,8 +400,8 @@ static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader, |
} |
break; |
case PNG_COLOR_TYPE_GRAY_ALPHA: |
- //FIXME: support gray with alpha as a color type |
- //convert to RGBA |
+ //FIXME: support gray with alpha as a color type |
+ //convert to RGBA |
png_set_gray_to_rgb(png_ptr); |
skColorType = kN32_SkColorType; |
skAlphaType = kUnpremul_SkAlphaType; |
@@ -406,7 +475,7 @@ SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo, |
SkCodecPrintf("setjmp long jump!\n"); |
return kInvalidInput; |
} |
- png_read_update_info(fPng_ptr, fInfo_ptr); |
+ png_read_update_info(fPng_ptr, fInfo_ptr); |
//srcColorType was determined in read_header() which determined png color type |
const SkColorType srcColorType = this->getInfo().colorType(); |
@@ -422,7 +491,7 @@ SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo, |
break; |
case kGray_8_SkColorType: |
fSrcConfig = SkSwizzler::kGray; |
- break; |
+ break; |
case kN32_SkColorType: |
if (this->getInfo().alphaType() == kOpaque_SkAlphaType) { |
fSrcConfig = SkSwizzler::kRGB; |
@@ -433,7 +502,7 @@ SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo, |
default: |
//would have exited before now if the colorType was supported by png |
SkASSERT(false); |
- } |
+ } |
// Copy the color table to the client if they request kIndex8 mode |
copy_color_table(requestedInfo, fColorTable, ctable, ctableCount); |
@@ -624,8 +693,8 @@ public: |
SkCodecPrintf("setjmp long jump!\n"); |
return false; |
} |
- //there is a potential tradeoff of memory vs speed created by putting this in a loop. |
- //calling png_read_rows in a loop is insignificantly slower than calling it once with count |
+ //there is a potential tradeoff of memory vs speed created by putting this in a loop. |
+ //calling png_read_rows in a loop is insignificantly slower than calling it once with count |
//as png_read_rows has it's own loop which calls png_read_row count times. |
for (int row = 0; row < count; row++) { |
png_read_rows(this->png_ptr(), &fSrcRow, png_bytepp_NULL, 1); |
@@ -656,7 +725,7 @@ public: |
Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options, |
SkPMColor ctable[], int* ctableCount) override { |
if (!conversion_possible(dstInfo, this->getInfo())) { |
- return kInvalidConversion; |
+ return kInvalidConversion; |
} |
const Result result = this->initializeSwizzler(dstInfo, options, ctable, |