OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2012 The Android Open Source Project | 2 * Copyright 2012 The Android Open Source Project |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include <emmintrin.h> | 8 #include <emmintrin.h> |
9 #include "SkBitmapProcState_opts_SSE2.h" | 9 #include "SkBitmapProcState_opts_SSE2.h" |
10 #include "SkBlitRow_opts_SSE2.h" | 10 #include "SkBlitRow_opts_SSE2.h" |
(...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
227 while (count > 0) { | 227 while (count > 0) { |
228 *dst = SkBlendARGB32(*src, *dst, alpha); | 228 *dst = SkBlendARGB32(*src, *dst, alpha); |
229 src++; | 229 src++; |
230 dst++; | 230 dst++; |
231 count--; | 231 count--; |
232 } | 232 } |
233 } | 233 } |
234 | 234 |
235 #define SK_SUPPORT_LEGACY_COLOR32_MATHx | 235 #define SK_SUPPORT_LEGACY_COLOR32_MATHx |
236 | 236 |
237 /* SSE2 version of Color32() | 237 /* SSE2 version of Color32(), portable version is in core/SkBlitRow_D32.cpp */ |
238 * portable version is in core/SkBlitRow_D32.cpp | 238 // Color32 and its SIMD specializations use the blend_perfect algorithm from tes
ts/BlendTest.cpp. |
239 */ | 239 // An acceptable alternative is blend_256_round_alt, which is faster but not qui
te perfect. |
240 // Color32 and its SIMD specializations use the blend_256_round_alt algorithm | |
241 // from tests/BlendTest.cpp. It's not quite perfect, but it's never wrong in th
e | |
242 // interesting edge cases, and it's quite a bit faster than blend_perfect. | |
243 // | |
244 // blend_256_round_alt is our currently blessed algorithm. Please use it or an
analogous one. | |
245 void Color32_SSE2(SkPMColor dst[], const SkPMColor src[], int count, SkPMColor c
olor) { | 240 void Color32_SSE2(SkPMColor dst[], const SkPMColor src[], int count, SkPMColor c
olor) { |
246 switch (SkGetPackedA32(color)) { | 241 switch (SkGetPackedA32(color)) { |
247 case 0: memmove(dst, src, count * sizeof(SkPMColor)); return; | 242 case 0: memmove(dst, src, count * sizeof(SkPMColor)); return; |
248 case 255: sk_memset32(dst, color, count); return; | 243 case 255: sk_memset32(dst, color, count); return; |
249 } | 244 } |
250 | 245 |
251 __m128i colorHigh = _mm_unpacklo_epi8(_mm_setzero_si128(), _mm_set1_epi32(co
lor)); | 246 __m128i color_2x_high = _mm_unpacklo_epi8(_mm_setzero_si128(), _mm_set1_epi3
2(color)), |
252 #ifdef SK_SUPPORT_LEGACY_COLOR32_MATH // blend_256_plus1_trunc, busted | 247 invA_8x = _mm_set1_epi16(255 - SkGetPackedA32(color)); |
253 __m128i colorAndRound = colorHigh; | |
254 #else // blend_256_round_alt, good | |
255 __m128i colorAndRound = _mm_add_epi16(colorHigh, _mm_set1_epi16(128)); | |
256 #endif | |
257 | |
258 unsigned invA = 255 - SkGetPackedA32(color); | |
259 #ifdef SK_SUPPORT_LEGACY_COLOR32_MATH // blend_256_plus1_trunc, busted | |
260 __m128i invA16 = _mm_set1_epi16(invA); | |
261 #else // blend_256_round_alt, good | |
262 SkASSERT(invA + (invA >> 7) < 256); // We should still fit in the low byte
here. | |
263 __m128i invA16 = _mm_set1_epi16(invA + (invA >> 7)); | |
264 #endif | |
265 | 248 |
266 // Does the core work of blending color onto 4 pixels, returning the resulti
ng 4 pixels. | 249 // Does the core work of blending color onto 4 pixels, returning the resulti
ng 4 pixels. |
267 auto kernel = [&](const __m128i& src4) -> __m128i { | 250 auto kernel = [&](const __m128i& src_4x) -> __m128i { |
268 __m128i lo = _mm_mullo_epi16(invA16, _mm_unpacklo_epi8(src4, _mm_setzero
_si128())), | 251 __m128i lo = _mm_mullo_epi16(invA_8x, _mm_unpacklo_epi8(src_4x, _mm_setz
ero_si128())), |
269 hi = _mm_mullo_epi16(invA16, _mm_unpackhi_epi8(src4, _mm_setzero
_si128())); | 252 hi = _mm_mullo_epi16(invA_8x, _mm_unpackhi_epi8(src_4x, _mm_setz
ero_si128())); |
270 return _mm_packus_epi16(_mm_srli_epi16(_mm_add_epi16(colorAndRound, lo),
8), | 253 #ifndef SK_SUPPORT_LEGACY_COLOR32_MATH |
271 _mm_srli_epi16(_mm_add_epi16(colorAndRound, hi),
8)); | 254 lo = _mm_add_epi16(lo, _mm_set1_epi16(128)); |
| 255 hi = _mm_add_epi16(hi, _mm_set1_epi16(128)); |
| 256 lo = _mm_add_epi16(lo, _mm_srli_epi16(lo, 8)); |
| 257 hi = _mm_add_epi16(hi, _mm_srli_epi16(hi, 8)); |
| 258 #endif |
| 259 return _mm_packus_epi16(_mm_srli_epi16(_mm_add_epi16(color_2x_high, lo),
8), |
| 260 _mm_srli_epi16(_mm_add_epi16(color_2x_high, hi),
8)); |
272 }; | 261 }; |
273 | 262 |
274 while (count >= 8) { | 263 while (count >= 8) { |
275 __m128i dst0 = kernel(_mm_loadu_si128((const __m128i*)(src+0))), | 264 __m128i dst0 = kernel(_mm_loadu_si128((const __m128i*)(src+0))), |
276 dst4 = kernel(_mm_loadu_si128((const __m128i*)(src+4))); | 265 dst4 = kernel(_mm_loadu_si128((const __m128i*)(src+4))); |
277 _mm_storeu_si128((__m128i*)(dst+0), dst0); | 266 _mm_storeu_si128((__m128i*)(dst+0), dst0); |
278 _mm_storeu_si128((__m128i*)(dst+4), dst4); | 267 _mm_storeu_si128((__m128i*)(dst+4), dst4); |
279 src += 8; | 268 src += 8; |
280 dst += 8; | 269 dst += 8; |
281 count -= 8; | 270 count -= 8; |
(...skipping 941 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1223 uint32_t dst_expanded = SkExpand_rgb_16(*dst); | 1212 uint32_t dst_expanded = SkExpand_rgb_16(*dst); |
1224 dst_expanded = dst_expanded * (SkAlpha255To256(255 - a) >> 3); | 1213 dst_expanded = dst_expanded * (SkAlpha255To256(255 - a) >> 3); |
1225 // now src and dst expanded are in g:11 r:10 x:1 b:10 | 1214 // now src and dst expanded are in g:11 r:10 x:1 b:10 |
1226 *dst = SkCompact_rgb_16((src_expanded + dst_expanded) >> 5); | 1215 *dst = SkCompact_rgb_16((src_expanded + dst_expanded) >> 5); |
1227 } | 1216 } |
1228 dst += 1; | 1217 dst += 1; |
1229 DITHER_INC_X(x); | 1218 DITHER_INC_X(x); |
1230 } while (--count != 0); | 1219 } while (--count != 0); |
1231 } | 1220 } |
1232 } | 1221 } |
OLD | NEW |