Path: blob/master/thirdparty/libwebp/src/dsp/alpha_processing_sse2.c
9913 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* WEBP_RESTRICT alpha,21int alpha_stride, int width, int height,22uint8_t* WEBP_RESTRICT 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((int)0xffffff00); // to preserve RGB29const __m128i all_0xff = _mm_set_epi32(0, 0, ~0, ~0);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* WEBP_RESTRICT alpha,75int alpha_stride, int width, int height,76uint32_t* WEBP_RESTRICT dst,77int dst_stride) {78int i, j;79const __m128i zero = _mm_setzero_si128();80const int limit = width & ~15;81for (j = 0; j < height; ++j) {82for (i = 0; i < limit; i += 16) { // process 16 alpha bytes83const __m128i a0 = _mm_loadu_si128((const __m128i*)&alpha[i]);84const __m128i a1 = _mm_unpacklo_epi8(zero, a0); // note the 'zero' first!85const __m128i b1 = _mm_unpackhi_epi8(zero, a0);86const __m128i a2_lo = _mm_unpacklo_epi16(a1, zero);87const __m128i b2_lo = _mm_unpacklo_epi16(b1, zero);88const __m128i a2_hi = _mm_unpackhi_epi16(a1, zero);89const __m128i b2_hi = _mm_unpackhi_epi16(b1, zero);90_mm_storeu_si128((__m128i*)&dst[i + 0], a2_lo);91_mm_storeu_si128((__m128i*)&dst[i + 4], a2_hi);92_mm_storeu_si128((__m128i*)&dst[i + 8], b2_lo);93_mm_storeu_si128((__m128i*)&dst[i + 12], b2_hi);94}95for (; i < width; ++i) dst[i] = alpha[i] << 8;96alpha += alpha_stride;97dst += dst_stride;98}99}100101static int ExtractAlpha_SSE2(const uint8_t* WEBP_RESTRICT argb, int argb_stride,102int width, int height,103uint8_t* WEBP_RESTRICT alpha, int alpha_stride) {104// alpha_and stores an 'and' operation of all the alpha[] values. The final105// value is not 0xff if any of the alpha[] is not equal to 0xff.106uint32_t alpha_and = 0xff;107int i, j;108const __m128i a_mask = _mm_set1_epi32(0xff); // to preserve alpha109const __m128i all_0xff = _mm_set_epi32(0, 0, ~0, ~0);110__m128i all_alphas = all_0xff;111112// We must be able to access 3 extra bytes after the last written byte113// 'src[4 * width - 4]', because we don't know if alpha is the first or the114// last byte of the quadruplet.115const int limit = (width - 1) & ~7;116117for (j = 0; j < height; ++j) {118const __m128i* src = (const __m128i*)argb;119for (i = 0; i < limit; i += 8) {120// load 32 argb bytes121const __m128i a0 = _mm_loadu_si128(src + 0);122const __m128i a1 = _mm_loadu_si128(src + 1);123const __m128i b0 = _mm_and_si128(a0, a_mask);124const __m128i b1 = _mm_and_si128(a1, a_mask);125const __m128i c0 = _mm_packs_epi32(b0, b1);126const __m128i d0 = _mm_packus_epi16(c0, c0);127// store128_mm_storel_epi64((__m128i*)&alpha[i], d0);129// accumulate eight alpha 'and' in parallel130all_alphas = _mm_and_si128(all_alphas, d0);131src += 2;132}133for (; i < width; ++i) {134const uint32_t alpha_value = argb[4 * i];135alpha[i] = alpha_value;136alpha_and &= alpha_value;137}138argb += argb_stride;139alpha += alpha_stride;140}141// Combine the eight alpha 'and' into a 8-bit mask.142alpha_and &= _mm_movemask_epi8(_mm_cmpeq_epi8(all_alphas, all_0xff));143return (alpha_and == 0xff);144}145146static void ExtractGreen_SSE2(const uint32_t* WEBP_RESTRICT argb,147uint8_t* WEBP_RESTRICT alpha, int size) {148int i;149const __m128i mask = _mm_set1_epi32(0xff);150const __m128i* src = (const __m128i*)argb;151152for (i = 0; i + 16 <= size; i += 16, src += 4) {153const __m128i a0 = _mm_loadu_si128(src + 0);154const __m128i a1 = _mm_loadu_si128(src + 1);155const __m128i a2 = _mm_loadu_si128(src + 2);156const __m128i a3 = _mm_loadu_si128(src + 3);157const __m128i b0 = _mm_srli_epi32(a0, 8);158const __m128i b1 = _mm_srli_epi32(a1, 8);159const __m128i b2 = _mm_srli_epi32(a2, 8);160const __m128i b3 = _mm_srli_epi32(a3, 8);161const __m128i c0 = _mm_and_si128(b0, mask);162const __m128i c1 = _mm_and_si128(b1, mask);163const __m128i c2 = _mm_and_si128(b2, mask);164const __m128i c3 = _mm_and_si128(b3, mask);165const __m128i d0 = _mm_packs_epi32(c0, c1);166const __m128i d1 = _mm_packs_epi32(c2, c3);167const __m128i e = _mm_packus_epi16(d0, d1);168// store169_mm_storeu_si128((__m128i*)&alpha[i], e);170}171if (i + 8 <= size) {172const __m128i a0 = _mm_loadu_si128(src + 0);173const __m128i a1 = _mm_loadu_si128(src + 1);174const __m128i b0 = _mm_srli_epi32(a0, 8);175const __m128i b1 = _mm_srli_epi32(a1, 8);176const __m128i c0 = _mm_and_si128(b0, mask);177const __m128i c1 = _mm_and_si128(b1, mask);178const __m128i d = _mm_packs_epi32(c0, c1);179const __m128i e = _mm_packus_epi16(d, d);180_mm_storel_epi64((__m128i*)&alpha[i], e);181i += 8;182}183for (; i < size; ++i) alpha[i] = argb[i] >> 8;184}185186//------------------------------------------------------------------------------187// Non-dither premultiplied modes188189#define MULTIPLIER(a) ((a) * 0x8081)190#define PREMULTIPLY(x, m) (((x) * (m)) >> 23)191192// We can't use a 'const int' for the SHUFFLE value, because it has to be an193// immediate in the _mm_shufflexx_epi16() instruction. We really need a macro.194// We use: v / 255 = (v * 0x8081) >> 23, where v = alpha * {r,g,b} is a 16bit195// value.196#define APPLY_ALPHA(RGBX, SHUFFLE) do { \197const __m128i argb0 = _mm_loadu_si128((const __m128i*)&(RGBX)); \198const __m128i argb1_lo = _mm_unpacklo_epi8(argb0, zero); \199const __m128i argb1_hi = _mm_unpackhi_epi8(argb0, zero); \200const __m128i alpha0_lo = _mm_or_si128(argb1_lo, kMask); \201const __m128i alpha0_hi = _mm_or_si128(argb1_hi, kMask); \202const __m128i alpha1_lo = _mm_shufflelo_epi16(alpha0_lo, SHUFFLE); \203const __m128i alpha1_hi = _mm_shufflelo_epi16(alpha0_hi, SHUFFLE); \204const __m128i alpha2_lo = _mm_shufflehi_epi16(alpha1_lo, SHUFFLE); \205const __m128i alpha2_hi = _mm_shufflehi_epi16(alpha1_hi, SHUFFLE); \206/* alpha2 = [ff a0 a0 a0][ff a1 a1 a1] */ \207const __m128i A0_lo = _mm_mullo_epi16(alpha2_lo, argb1_lo); \208const __m128i A0_hi = _mm_mullo_epi16(alpha2_hi, argb1_hi); \209const __m128i A1_lo = _mm_mulhi_epu16(A0_lo, kMult); \210const __m128i A1_hi = _mm_mulhi_epu16(A0_hi, kMult); \211const __m128i A2_lo = _mm_srli_epi16(A1_lo, 7); \212const __m128i A2_hi = _mm_srli_epi16(A1_hi, 7); \213const __m128i A3 = _mm_packus_epi16(A2_lo, A2_hi); \214_mm_storeu_si128((__m128i*)&(RGBX), A3); \215} while (0)216217static void ApplyAlphaMultiply_SSE2(uint8_t* rgba, int alpha_first,218int w, int h, int stride) {219const __m128i zero = _mm_setzero_si128();220const __m128i kMult = _mm_set1_epi16((short)0x8081);221const __m128i kMask = _mm_set_epi16(0, 0xff, 0xff, 0, 0, 0xff, 0xff, 0);222const int kSpan = 4;223while (h-- > 0) {224uint32_t* const rgbx = (uint32_t*)rgba;225int i;226if (!alpha_first) {227for (i = 0; i + kSpan <= w; i += kSpan) {228APPLY_ALPHA(rgbx[i], _MM_SHUFFLE(2, 3, 3, 3));229}230} else {231for (i = 0; i + kSpan <= w; i += kSpan) {232APPLY_ALPHA(rgbx[i], _MM_SHUFFLE(0, 0, 0, 1));233}234}235// Finish with left-overs.236for (; i < w; ++i) {237uint8_t* const rgb = rgba + (alpha_first ? 1 : 0);238const uint8_t* const alpha = rgba + (alpha_first ? 0 : 3);239const uint32_t a = alpha[4 * i];240if (a != 0xff) {241const uint32_t mult = MULTIPLIER(a);242rgb[4 * i + 0] = PREMULTIPLY(rgb[4 * i + 0], mult);243rgb[4 * i + 1] = PREMULTIPLY(rgb[4 * i + 1], mult);244rgb[4 * i + 2] = PREMULTIPLY(rgb[4 * i + 2], mult);245}246}247rgba += stride;248}249}250#undef MULTIPLIER251#undef PREMULTIPLY252253//------------------------------------------------------------------------------254// Alpha detection255256static int HasAlpha8b_SSE2(const uint8_t* src, int length) {257const __m128i all_0xff = _mm_set1_epi8((char)0xff);258int i = 0;259for (; i + 16 <= length; i += 16) {260const __m128i v = _mm_loadu_si128((const __m128i*)(src + i));261const __m128i bits = _mm_cmpeq_epi8(v, all_0xff);262const int mask = _mm_movemask_epi8(bits);263if (mask != 0xffff) return 1;264}265for (; i < length; ++i) if (src[i] != 0xff) return 1;266return 0;267}268269static int HasAlpha32b_SSE2(const uint8_t* src, int length) {270const __m128i alpha_mask = _mm_set1_epi32(0xff);271const __m128i all_0xff = _mm_set1_epi8((char)0xff);272int i = 0;273// We don't know if we can access the last 3 bytes after the last alpha274// value 'src[4 * length - 4]' (because we don't know if alpha is the first275// or the last byte of the quadruplet). Hence the '-3' protection below.276length = length * 4 - 3; // size in bytes277for (; i + 64 <= length; i += 64) {278const __m128i a0 = _mm_loadu_si128((const __m128i*)(src + i + 0));279const __m128i a1 = _mm_loadu_si128((const __m128i*)(src + i + 16));280const __m128i a2 = _mm_loadu_si128((const __m128i*)(src + i + 32));281const __m128i a3 = _mm_loadu_si128((const __m128i*)(src + i + 48));282const __m128i b0 = _mm_and_si128(a0, alpha_mask);283const __m128i b1 = _mm_and_si128(a1, alpha_mask);284const __m128i b2 = _mm_and_si128(a2, alpha_mask);285const __m128i b3 = _mm_and_si128(a3, alpha_mask);286const __m128i c0 = _mm_packs_epi32(b0, b1);287const __m128i c1 = _mm_packs_epi32(b2, b3);288const __m128i d = _mm_packus_epi16(c0, c1);289const __m128i bits = _mm_cmpeq_epi8(d, all_0xff);290const int mask = _mm_movemask_epi8(bits);291if (mask != 0xffff) return 1;292}293for (; i + 32 <= length; i += 32) {294const __m128i a0 = _mm_loadu_si128((const __m128i*)(src + i + 0));295const __m128i a1 = _mm_loadu_si128((const __m128i*)(src + i + 16));296const __m128i b0 = _mm_and_si128(a0, alpha_mask);297const __m128i b1 = _mm_and_si128(a1, alpha_mask);298const __m128i c = _mm_packs_epi32(b0, b1);299const __m128i d = _mm_packus_epi16(c, c);300const __m128i bits = _mm_cmpeq_epi8(d, all_0xff);301const int mask = _mm_movemask_epi8(bits);302if (mask != 0xffff) return 1;303}304for (; i <= length; i += 4) if (src[i] != 0xff) return 1;305return 0;306}307308static void AlphaReplace_SSE2(uint32_t* src, int length, uint32_t color) {309const __m128i m_color = _mm_set1_epi32((int)color);310const __m128i zero = _mm_setzero_si128();311int i = 0;312for (; i + 8 <= length; i += 8) {313const __m128i a0 = _mm_loadu_si128((const __m128i*)(src + i + 0));314const __m128i a1 = _mm_loadu_si128((const __m128i*)(src + i + 4));315const __m128i b0 = _mm_srai_epi32(a0, 24);316const __m128i b1 = _mm_srai_epi32(a1, 24);317const __m128i c0 = _mm_cmpeq_epi32(b0, zero);318const __m128i c1 = _mm_cmpeq_epi32(b1, zero);319const __m128i d0 = _mm_and_si128(c0, m_color);320const __m128i d1 = _mm_and_si128(c1, m_color);321const __m128i e0 = _mm_andnot_si128(c0, a0);322const __m128i e1 = _mm_andnot_si128(c1, a1);323_mm_storeu_si128((__m128i*)(src + i + 0), _mm_or_si128(d0, e0));324_mm_storeu_si128((__m128i*)(src + i + 4), _mm_or_si128(d1, e1));325}326for (; i < length; ++i) if ((src[i] >> 24) == 0) src[i] = color;327}328329// -----------------------------------------------------------------------------330// Apply alpha value to rows331332static void MultARGBRow_SSE2(uint32_t* const ptr, int width, int inverse) {333int x = 0;334if (!inverse) {335const int kSpan = 2;336const __m128i zero = _mm_setzero_si128();337const __m128i k128 = _mm_set1_epi16(128);338const __m128i kMult = _mm_set1_epi16(0x0101);339const __m128i kMask = _mm_set_epi16(0, 0xff, 0, 0, 0, 0xff, 0, 0);340for (x = 0; x + kSpan <= width; x += kSpan) {341// To compute 'result = (int)(a * x / 255. + .5)', we use:342// tmp = a * v + 128, result = (tmp * 0x0101u) >> 16343const __m128i A0 = _mm_loadl_epi64((const __m128i*)&ptr[x]);344const __m128i A1 = _mm_unpacklo_epi8(A0, zero);345const __m128i A2 = _mm_or_si128(A1, kMask);346const __m128i A3 = _mm_shufflelo_epi16(A2, _MM_SHUFFLE(2, 3, 3, 3));347const __m128i A4 = _mm_shufflehi_epi16(A3, _MM_SHUFFLE(2, 3, 3, 3));348// here, A4 = [ff a0 a0 a0][ff a1 a1 a1]349const __m128i A5 = _mm_mullo_epi16(A4, A1);350const __m128i A6 = _mm_add_epi16(A5, k128);351const __m128i A7 = _mm_mulhi_epu16(A6, kMult);352const __m128i A10 = _mm_packus_epi16(A7, zero);353_mm_storel_epi64((__m128i*)&ptr[x], A10);354}355}356width -= x;357if (width > 0) WebPMultARGBRow_C(ptr + x, width, inverse);358}359360static void MultRow_SSE2(uint8_t* WEBP_RESTRICT const ptr,361const uint8_t* WEBP_RESTRICT const alpha,362int width, int inverse) {363int x = 0;364if (!inverse) {365const __m128i zero = _mm_setzero_si128();366const __m128i k128 = _mm_set1_epi16(128);367const __m128i kMult = _mm_set1_epi16(0x0101);368for (x = 0; x + 8 <= width; x += 8) {369const __m128i v0 = _mm_loadl_epi64((__m128i*)&ptr[x]);370const __m128i a0 = _mm_loadl_epi64((const __m128i*)&alpha[x]);371const __m128i v1 = _mm_unpacklo_epi8(v0, zero);372const __m128i a1 = _mm_unpacklo_epi8(a0, zero);373const __m128i v2 = _mm_mullo_epi16(v1, a1);374const __m128i v3 = _mm_add_epi16(v2, k128);375const __m128i v4 = _mm_mulhi_epu16(v3, kMult);376const __m128i v5 = _mm_packus_epi16(v4, zero);377_mm_storel_epi64((__m128i*)&ptr[x], v5);378}379}380width -= x;381if (width > 0) WebPMultRow_C(ptr + x, alpha + x, width, inverse);382}383384//------------------------------------------------------------------------------385// Entry point386387extern void WebPInitAlphaProcessingSSE2(void);388389WEBP_TSAN_IGNORE_FUNCTION void WebPInitAlphaProcessingSSE2(void) {390WebPMultARGBRow = MultARGBRow_SSE2;391WebPMultRow = MultRow_SSE2;392WebPApplyAlphaMultiply = ApplyAlphaMultiply_SSE2;393WebPDispatchAlpha = DispatchAlpha_SSE2;394WebPDispatchAlphaToGreen = DispatchAlphaToGreen_SSE2;395WebPExtractAlpha = ExtractAlpha_SSE2;396WebPExtractGreen = ExtractGreen_SSE2;397398WebPHasAlpha8b = HasAlpha8b_SSE2;399WebPHasAlpha32b = HasAlpha32b_SSE2;400WebPAlphaReplace = AlphaReplace_SSE2;401}402403#else // !WEBP_USE_SSE2404405WEBP_DSP_INIT_STUB(WebPInitAlphaProcessingSSE2)406407#endif // WEBP_USE_SSE2408409410