Chromium Code Reviews| 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, |