1/*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkBlitRow.h"
9#include "SkBlitMask.h"
10#include "SkColorPriv.h"
11#include "SkUtils.h"
12
13#define UNROLL
14
15static void S32_Opaque_BlitRow32(SkPMColor* SK_RESTRICT dst,
16                                 const SkPMColor* SK_RESTRICT src,
17                                 int count, U8CPU alpha) {
18    SkASSERT(255 == alpha);
19    sk_memcpy32(dst, src, count);
20}
21
22static void S32_Blend_BlitRow32(SkPMColor* SK_RESTRICT dst,
23                                const SkPMColor* SK_RESTRICT src,
24                                int count, U8CPU alpha) {
25    SkASSERT(alpha <= 255);
26    if (count > 0) {
27        unsigned src_scale = SkAlpha255To256(alpha);
28        unsigned dst_scale = 256 - src_scale;
29
30#ifdef UNROLL
31        if (count & 1) {
32            *dst = SkAlphaMulQ(*(src++), src_scale) + SkAlphaMulQ(*dst, dst_scale);
33            dst += 1;
34            count -= 1;
35        }
36
37        const SkPMColor* SK_RESTRICT srcEnd = src + count;
38        while (src != srcEnd) {
39            *dst = SkAlphaMulQ(*(src++), src_scale) + SkAlphaMulQ(*dst, dst_scale);
40            dst += 1;
41            *dst = SkAlphaMulQ(*(src++), src_scale) + SkAlphaMulQ(*dst, dst_scale);
42            dst += 1;
43        }
44#else
45        do {
46            *dst = SkAlphaMulQ(*src, src_scale) + SkAlphaMulQ(*dst, dst_scale);
47            src += 1;
48            dst += 1;
49        } while (--count > 0);
50#endif
51    }
52}
53
54static void S32A_Opaque_BlitRow32(SkPMColor* SK_RESTRICT dst,
55                                  const SkPMColor* SK_RESTRICT src,
56                                  int count, U8CPU alpha) {
57    SkASSERT(255 == alpha);
58    if (count > 0) {
59#ifdef UNROLL
60        if (count & 1) {
61            *dst = SkPMSrcOver(*(src++), *dst);
62            dst += 1;
63            count -= 1;
64        }
65
66        const SkPMColor* SK_RESTRICT srcEnd = src + count;
67        while (src != srcEnd) {
68            *dst = SkPMSrcOver(*(src++), *dst);
69            dst += 1;
70            *dst = SkPMSrcOver(*(src++), *dst);
71            dst += 1;
72        }
73#else
74        do {
75            *dst = SkPMSrcOver(*src, *dst);
76            src += 1;
77            dst += 1;
78        } while (--count > 0);
79#endif
80    }
81}
82
83static void S32A_Blend_BlitRow32(SkPMColor* SK_RESTRICT dst,
84                                 const SkPMColor* SK_RESTRICT src,
85                                 int count, U8CPU alpha) {
86    SkASSERT(alpha <= 255);
87    if (count > 0) {
88#ifdef UNROLL
89        if (count & 1) {
90            *dst = SkBlendARGB32(*(src++), *dst, alpha);
91            dst += 1;
92            count -= 1;
93        }
94
95        const SkPMColor* SK_RESTRICT srcEnd = src + count;
96        while (src != srcEnd) {
97            *dst = SkBlendARGB32(*(src++), *dst, alpha);
98            dst += 1;
99            *dst = SkBlendARGB32(*(src++), *dst, alpha);
100            dst += 1;
101        }
102#else
103        do {
104            *dst = SkBlendARGB32(*src, *dst, alpha);
105            src += 1;
106            dst += 1;
107        } while (--count > 0);
108#endif
109    }
110}
111
112///////////////////////////////////////////////////////////////////////////////
113
114static const SkBlitRow::Proc32 gDefault_Procs32[] = {
115    S32_Opaque_BlitRow32,
116    S32_Blend_BlitRow32,
117    S32A_Opaque_BlitRow32,
118    S32A_Blend_BlitRow32
119};
120
121SkBlitRow::Proc32 SkBlitRow::Factory32(unsigned flags) {
122    SkASSERT(flags < SK_ARRAY_COUNT(gDefault_Procs32));
123    // just so we don't crash
124    flags &= kFlags32_Mask;
125
126    SkBlitRow::Proc32 proc = PlatformProcs32(flags);
127    if (NULL == proc) {
128        proc = gDefault_Procs32[flags];
129    }
130    SkASSERT(proc);
131    return proc;
132}
133
134#include "Sk4px.h"
135
136// Color32 uses the blend_256_round_alt algorithm from tests/BlendTest.cpp.
137// It's not quite perfect, but it's never wrong in the interesting edge cases,
138// and it's quite a bit faster than blend_perfect.
139//
140// blend_256_round_alt is our currently blessed algorithm.  Please use it or an analogous one.
141void SkBlitRow::Color32(SkPMColor dst[], const SkPMColor src[], int count, SkPMColor color) {
142    switch (SkGetPackedA32(color)) {
143        case   0: memmove(dst, src, count * sizeof(SkPMColor)); return;
144        case 255: sk_memset32(dst, color, count);               return;
145    }
146
147    unsigned invA = 255 - SkGetPackedA32(color);
148    invA += invA >> 7;
149    SkASSERT(invA < 256);  // We've already handled alpha == 0 above.
150
151    Sk16h colorHighAndRound = Sk4px(color).widenHi() + Sk16h(128);
152    Sk16b invA_16x(invA);
153
154    Sk4px::MapSrc(count, dst, src, [&](const Sk4px& src4) -> Sk4px {
155        return src4.mulWiden(invA_16x).addNarrowHi(colorHighAndRound);
156    });
157}
158