Path: blob/master/3rdparty/libwebp/src/dsp/alpha_processing_sse2.c
16348 views
// Copyright 2014 Google Inc. All Rights Reserved.1//2// Use of this source code is governed by a BSD-style license3// that can be found in the COPYING file in the root of the source4// tree. An additional intellectual property rights grant can be found5// in the file PATENTS. All contributing project authors may6// be found in the AUTHORS file in the root of the source tree.7// -----------------------------------------------------------------------------8//9// Utilities for processing transparent channel.10//11// Author: Skal ([email protected])1213#include "src/dsp/dsp.h"1415#if defined(WEBP_USE_SSE2)16#include <emmintrin.h>1718//------------------------------------------------------------------------------1920static int DispatchAlpha_SSE2(const uint8_t* alpha, int alpha_stride,21int width, int height,22uint8_t* dst, int dst_stride) {23// alpha_and stores an 'and' operation of all the alpha[] values. The final24// value is not 0xff if any of the alpha[] is not equal to 0xff.25uint32_t alpha_and = 0xff;26int i, j;27const __m128i zero = _mm_setzero_si128();28const __m128i rgb_mask = _mm_set1_epi32(0xffffff00u); // to preserve RGB29const __m128i all_0xff = _mm_set_epi32(0, 0, ~0u, ~0u);30__m128i all_alphas = all_0xff;3132// We must be able to access 3 extra bytes after the last written byte33// 'dst[4 * width - 4]', because we don't know if alpha is the first or the34// last byte of the quadruplet.35const int limit = (width - 1) & ~7;3637for (j = 0; j < height; ++j) {38__m128i* out = (__m128i*)dst;39for (i = 0; i < limit; i += 8) {40// load 8 alpha bytes41const __m128i a0 = _mm_loadl_epi64((const __m128i*)&alpha[i]);42const __m128i a1 = _mm_unpacklo_epi8(a0, zero);43const __m128i a2_lo = _mm_unpacklo_epi16(a1, zero);44const __m128i a2_hi = _mm_unpackhi_epi16(a1, zero);45// load 8 dst pixels (32 bytes)46const __m128i b0_lo = _mm_loadu_si128(out + 0);47const __m128i b0_hi = _mm_loadu_si128(out + 1);48// mask dst alpha values49const __m128i b1_lo = _mm_and_si128(b0_lo, rgb_mask);50const __m128i b1_hi = _mm_and_si128(b0_hi, rgb_mask);51// combine52const __m128i b2_lo = _mm_or_si128(b1_lo, a2_lo);53const __m128i b2_hi = _mm_or_si128(b1_hi, a2_hi);54// store55_mm_storeu_si128(out + 0, b2_lo);56_mm_storeu_si128(out + 1, b2_hi);57// accumulate eight alpha 'and' in parallel58all_alphas = _mm_and_si128(all_alphas, a0);59out += 2;60}61for (; i < width; ++i) {62const uint32_t alpha_value = alpha[i];63dst[4 * i] = alpha_value;64alpha_and &= alpha_value;65}66alpha += alpha_stride;67dst += dst_stride;68}69// Combine the eight alpha 'and' into a 8-bit mask.70alpha_and &= _mm_movemask_epi8(_mm_cmpeq_epi8(all_alphas, all_0xff));71return (alpha_and != 0xff);72}7374static void DispatchAlphaToGreen_SSE2(const uint8_t* alpha, int alpha_stride,75int width, int height,76uint32_t* dst, int dst_stride) {77int i, j;78const __m128i zero = _mm_setzero_si128();79const int limit = width & ~15;80for (j = 0; j < height; ++j) {81for (i = 0; i < limit; i += 16) { // process 16 alpha bytes82const __m128i a0 = _mm_loadu_si128((const __m128i*)&alpha[i]);83const __m128i a1 = _mm_unpacklo_epi8(zero, a0); // note the 'zero' first!84const __m128i b1 = _mm_unpackhi_epi8(zero, a0);85const __m128i a2_lo = _mm_unpacklo_epi16(a1, zero);86const __m128i b2_lo = _mm_unpacklo_epi16(b1, zero);87const __m128i a2_hi = _mm_unpackhi_epi16(a1, zero);88const __m128i b2_hi = _mm_unpackhi_epi16(b1, zero);89_mm_storeu_si128((__m128i*)&dst[i + 0], a2_lo);90_mm_storeu_si128((__m128i*)&dst[i + 4], a2_hi);91_mm_storeu_si128((__m128i*)&dst[i + 8], b2_lo);92_mm_storeu_si128((__m128i*)&dst[i + 12], b2_hi);93}94for (; i < width; ++i) dst[i] = alpha[i] << 8;95alpha += alpha_stride;96dst += dst_stride;97}98}99100static int ExtractAlpha_SSE2(const uint8_t* argb, int argb_stride,101int width, int height,102uint8_t* alpha, int alpha_stride) {103// alpha_and stores an 'and' operation of all the alpha[] values. The final104// value is not 0xff if any of the alpha[] is not equal to 0xff.105uint32_t alpha_and = 0xff;106int i, j;107const __m128i a_mask = _mm_set1_epi32(0xffu); // to preserve alpha108const __m128i all_0xff = _mm_set_epi32(0, 0, ~0u, ~0u);109__m128i all_alphas = all_0xff;110111// We must be able to access 3 extra bytes after the last written byte112// 'src[4 * width - 4]', because we don't know if alpha is the first or the113// last byte of the quadruplet.114const int limit = (width - 1) & ~7;115116for (j = 0; j < height; ++j) {117const __m128i* src = (const __m128i*)argb;118for (i = 0; i < limit; i += 8) {119// load 32 argb bytes120const __m128i a0 = _mm_loadu_si128(src + 0);121const __m128i a1 = _mm_loadu_si128(src + 1);122const __m128i b0 = _mm_and_si128(a0, a_mask);123const __m128i b1 = _mm_and_si128(a1, a_mask);124const __m128i c0 = _mm_packs_epi32(b0, b1);125const __m128i d0 = _mm_packus_epi16(c0, c0);126// store127_mm_storel_epi64((__m128i*)&alpha[i], d0);128// accumulate eight alpha 'and' in parallel129all_alphas = _mm_and_si128(all_alphas, d0);130src += 2;131}132for (; i < width; ++i) {133const uint32_t alpha_value = argb[4 * i];134alpha[i] = alpha_value;135alpha_and &= alpha_value;136}137argb += argb_stride;138alpha += alpha_stride;139}140// Combine the eight alpha 'and' into a 8-bit mask.141alpha_and &= _mm_movemask_epi8(_mm_cmpeq_epi8(all_alphas, all_0xff));142return (alpha_and == 0xff);143}144145//------------------------------------------------------------------------------146// Non-dither premultiplied modes147148#define MULTIPLIER(a) ((a) * 0x8081)149#define PREMULTIPLY(x, m) (((x) * (m)) >> 23)150151// We can't use a 'const int' for the SHUFFLE value, because it has to be an152// immediate in the _mm_shufflexx_epi16() instruction. We really need a macro.153// We use: v / 255 = (v * 0x8081) >> 23, where v = alpha * {r,g,b} is a 16bit154// value.155#define APPLY_ALPHA(RGBX, SHUFFLE) do { \156const __m128i argb0 = _mm_loadu_si128((const __m128i*)&(RGBX)); \157const __m128i argb1_lo = _mm_unpacklo_epi8(argb0, zero); \158const __m128i argb1_hi = _mm_unpackhi_epi8(argb0, zero); \159const __m128i alpha0_lo = _mm_or_si128(argb1_lo, kMask); \160const __m128i alpha0_hi = _mm_or_si128(argb1_hi, kMask); \161const __m128i alpha1_lo = _mm_shufflelo_epi16(alpha0_lo, SHUFFLE); \162const __m128i alpha1_hi = _mm_shufflelo_epi16(alpha0_hi, SHUFFLE); \163const __m128i alpha2_lo = _mm_shufflehi_epi16(alpha1_lo, SHUFFLE); \164const __m128i alpha2_hi = _mm_shufflehi_epi16(alpha1_hi, SHUFFLE); \165/* alpha2 = [ff a0 a0 a0][ff a1 a1 a1] */ \166const __m128i A0_lo = _mm_mullo_epi16(alpha2_lo, argb1_lo); \167const __m128i A0_hi = _mm_mullo_epi16(alpha2_hi, argb1_hi); \168const __m128i A1_lo = _mm_mulhi_epu16(A0_lo, kMult); \169const __m128i A1_hi = _mm_mulhi_epu16(A0_hi, kMult); \170const __m128i A2_lo = _mm_srli_epi16(A1_lo, 7); \171const __m128i A2_hi = _mm_srli_epi16(A1_hi, 7); \172const __m128i A3 = _mm_packus_epi16(A2_lo, A2_hi); \173_mm_storeu_si128((__m128i*)&(RGBX), A3); \174} while (0)175176static void ApplyAlphaMultiply_SSE2(uint8_t* rgba, int alpha_first,177int w, int h, int stride) {178const __m128i zero = _mm_setzero_si128();179const __m128i kMult = _mm_set1_epi16(0x8081u);180const __m128i kMask = _mm_set_epi16(0, 0xff, 0xff, 0, 0, 0xff, 0xff, 0);181const int kSpan = 4;182while (h-- > 0) {183uint32_t* const rgbx = (uint32_t*)rgba;184int i;185if (!alpha_first) {186for (i = 0; i + kSpan <= w; i += kSpan) {187APPLY_ALPHA(rgbx[i], _MM_SHUFFLE(2, 3, 3, 3));188}189} else {190for (i = 0; i + kSpan <= w; i += kSpan) {191APPLY_ALPHA(rgbx[i], _MM_SHUFFLE(0, 0, 0, 1));192}193}194// Finish with left-overs.195for (; i < w; ++i) {196uint8_t* const rgb = rgba + (alpha_first ? 1 : 0);197const uint8_t* const alpha = rgba + (alpha_first ? 0 : 3);198const uint32_t a = alpha[4 * i];199if (a != 0xff) {200const uint32_t mult = MULTIPLIER(a);201rgb[4 * i + 0] = PREMULTIPLY(rgb[4 * i + 0], mult);202rgb[4 * i + 1] = PREMULTIPLY(rgb[4 * i + 1], mult);203rgb[4 * i + 2] = PREMULTIPLY(rgb[4 * i + 2], mult);204}205}206rgba += stride;207}208}209#undef MULTIPLIER210#undef PREMULTIPLY211212//------------------------------------------------------------------------------213// Alpha detection214215static int HasAlpha8b_SSE2(const uint8_t* src, int length) {216const __m128i all_0xff = _mm_set1_epi8(0xff);217int i = 0;218for (; i + 16 <= length; i += 16) {219const __m128i v = _mm_loadu_si128((const __m128i*)(src + i));220const __m128i bits = _mm_cmpeq_epi8(v, all_0xff);221const int mask = _mm_movemask_epi8(bits);222if (mask != 0xffff) return 1;223}224for (; i < length; ++i) if (src[i] != 0xff) return 1;225return 0;226}227228static int HasAlpha32b_SSE2(const uint8_t* src, int length) {229const __m128i alpha_mask = _mm_set1_epi32(0xff);230const __m128i all_0xff = _mm_set1_epi8(0xff);231int i = 0;232// We don't know if we can access the last 3 bytes after the last alpha233// value 'src[4 * length - 4]' (because we don't know if alpha is the first234// or the last byte of the quadruplet). Hence the '-3' protection below.235length = length * 4 - 3; // size in bytes236for (; i + 64 <= length; i += 64) {237const __m128i a0 = _mm_loadu_si128((const __m128i*)(src + i + 0));238const __m128i a1 = _mm_loadu_si128((const __m128i*)(src + i + 16));239const __m128i a2 = _mm_loadu_si128((const __m128i*)(src + i + 32));240const __m128i a3 = _mm_loadu_si128((const __m128i*)(src + i + 48));241const __m128i b0 = _mm_and_si128(a0, alpha_mask);242const __m128i b1 = _mm_and_si128(a1, alpha_mask);243const __m128i b2 = _mm_and_si128(a2, alpha_mask);244const __m128i b3 = _mm_and_si128(a3, alpha_mask);245const __m128i c0 = _mm_packs_epi32(b0, b1);246const __m128i c1 = _mm_packs_epi32(b2, b3);247const __m128i d = _mm_packus_epi16(c0, c1);248const __m128i bits = _mm_cmpeq_epi8(d, all_0xff);249const int mask = _mm_movemask_epi8(bits);250if (mask != 0xffff) return 1;251}252for (; i + 32 <= length; i += 32) {253const __m128i a0 = _mm_loadu_si128((const __m128i*)(src + i + 0));254const __m128i a1 = _mm_loadu_si128((const __m128i*)(src + i + 16));255const __m128i b0 = _mm_and_si128(a0, alpha_mask);256const __m128i b1 = _mm_and_si128(a1, alpha_mask);257const __m128i c = _mm_packs_epi32(b0, b1);258const __m128i d = _mm_packus_epi16(c, c);259const __m128i bits = _mm_cmpeq_epi8(d, all_0xff);260const int mask = _mm_movemask_epi8(bits);261if (mask != 0xffff) return 1;262}263for (; i <= length; i += 4) if (src[i] != 0xff) return 1;264return 0;265}266267// -----------------------------------------------------------------------------268// Apply alpha value to rows269270static void MultARGBRow_SSE2(uint32_t* const ptr, int width, int inverse) {271int x = 0;272if (!inverse) {273const int kSpan = 2;274const __m128i zero = _mm_setzero_si128();275const __m128i k128 = _mm_set1_epi16(128);276const __m128i kMult = _mm_set1_epi16(0x0101);277const __m128i kMask = _mm_set_epi16(0, 0xff, 0, 0, 0, 0xff, 0, 0);278for (x = 0; x + kSpan <= width; x += kSpan) {279// To compute 'result = (int)(a * x / 255. + .5)', we use:280// tmp = a * v + 128, result = (tmp * 0x0101u) >> 16281const __m128i A0 = _mm_loadl_epi64((const __m128i*)&ptr[x]);282const __m128i A1 = _mm_unpacklo_epi8(A0, zero);283const __m128i A2 = _mm_or_si128(A1, kMask);284const __m128i A3 = _mm_shufflelo_epi16(A2, _MM_SHUFFLE(2, 3, 3, 3));285const __m128i A4 = _mm_shufflehi_epi16(A3, _MM_SHUFFLE(2, 3, 3, 3));286// here, A4 = [ff a0 a0 a0][ff a1 a1 a1]287const __m128i A5 = _mm_mullo_epi16(A4, A1);288const __m128i A6 = _mm_add_epi16(A5, k128);289const __m128i A7 = _mm_mulhi_epu16(A6, kMult);290const __m128i A10 = _mm_packus_epi16(A7, zero);291_mm_storel_epi64((__m128i*)&ptr[x], A10);292}293}294width -= x;295if (width > 0) WebPMultARGBRow_C(ptr + x, width, inverse);296}297298static void MultRow_SSE2(uint8_t* const ptr, const uint8_t* const alpha,299int width, int inverse) {300int x = 0;301if (!inverse) {302const __m128i zero = _mm_setzero_si128();303const __m128i k128 = _mm_set1_epi16(128);304const __m128i kMult = _mm_set1_epi16(0x0101);305for (x = 0; x + 8 <= width; x += 8) {306const __m128i v0 = _mm_loadl_epi64((__m128i*)&ptr[x]);307const __m128i a0 = _mm_loadl_epi64((const __m128i*)&alpha[x]);308const __m128i v1 = _mm_unpacklo_epi8(v0, zero);309const __m128i a1 = _mm_unpacklo_epi8(a0, zero);310const __m128i v2 = _mm_mullo_epi16(v1, a1);311const __m128i v3 = _mm_add_epi16(v2, k128);312const __m128i v4 = _mm_mulhi_epu16(v3, kMult);313const __m128i v5 = _mm_packus_epi16(v4, zero);314_mm_storel_epi64((__m128i*)&ptr[x], v5);315}316}317width -= x;318if (width > 0) WebPMultRow_C(ptr + x, alpha + x, width, inverse);319}320321//------------------------------------------------------------------------------322// Entry point323324extern void WebPInitAlphaProcessingSSE2(void);325326WEBP_TSAN_IGNORE_FUNCTION void WebPInitAlphaProcessingSSE2(void) {327WebPMultARGBRow = MultARGBRow_SSE2;328WebPMultRow = MultRow_SSE2;329WebPApplyAlphaMultiply = ApplyAlphaMultiply_SSE2;330WebPDispatchAlpha = DispatchAlpha_SSE2;331WebPDispatchAlphaToGreen = DispatchAlphaToGreen_SSE2;332WebPExtractAlpha = ExtractAlpha_SSE2;333334WebPHasAlpha8b = HasAlpha8b_SSE2;335WebPHasAlpha32b = HasAlpha32b_SSE2;336}337338#else // !WEBP_USE_SSE2339340WEBP_DSP_INIT_STUB(WebPInitAlphaProcessingSSE2)341342#endif // WEBP_USE_SSE2343344345