1/*
2 * Copyright 2014 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 "SkBlitMask.h"
9#include "SkColor.h"
10#include "SkColorPriv.h"
11#include "SkOpts.h"
12
13SkBlitMask::BlitLCD16RowProc SkBlitMask::BlitLCD16RowFactory(bool isOpaque) {
14    BlitLCD16RowProc proc = PlatformBlitRowProcs16(isOpaque);
15    if (proc) {
16        return proc;
17    }
18
19    if (isOpaque) {
20        return  SkBlitLCD16OpaqueRow;
21    } else {
22        return  SkBlitLCD16Row;
23    }
24}
25
26static void D32_LCD16_Proc(void* SK_RESTRICT dst, size_t dstRB,
27                           const void* SK_RESTRICT mask, size_t maskRB,
28                           SkColor color, int width, int height) {
29
30    SkPMColor*        dstRow = (SkPMColor*)dst;
31    const uint16_t* srcRow = (const uint16_t*)mask;
32    SkPMColor       opaqueDst;
33
34    SkBlitMask::BlitLCD16RowProc proc = nullptr;
35    bool isOpaque = (0xFF == SkColorGetA(color));
36    proc = SkBlitMask::BlitLCD16RowFactory(isOpaque);
37    SkASSERT(proc != nullptr);
38
39    if (isOpaque) {
40        opaqueDst = SkPreMultiplyColor(color);
41    } else {
42        opaqueDst = 0;  // ignored
43    }
44
45    do {
46        proc(dstRow, srcRow, color, width, opaqueDst);
47        dstRow = (SkPMColor*)((char*)dstRow + dstRB);
48        srcRow = (const uint16_t*)((const char*)srcRow + maskRB);
49    } while (--height != 0);
50}
51
52///////////////////////////////////////////////////////////////////////////////
53
54bool SkBlitMask::BlitColor(const SkPixmap& device, const SkMask& mask,
55                           const SkIRect& clip, SkColor color) {
56    int x = clip.fLeft, y = clip.fTop;
57
58    if (device.colorType() == kN32_SkColorType && mask.fFormat == SkMask::kA8_Format) {
59        SkOpts::blit_mask_d32_a8(device.writable_addr32(x,y), device.rowBytes(),
60                                 (const SkAlpha*)mask.getAddr(x,y), mask.fRowBytes,
61                                 color, clip.width(), clip.height());
62        return true;
63    }
64
65    if (device.colorType() == kN32_SkColorType && mask.fFormat == SkMask::kLCD16_Format) {
66        // TODO: Is this reachable code?  Seems like no.
67        D32_LCD16_Proc(device.writable_addr32(x,y), device.rowBytes(),
68                       mask.getAddr(x,y), mask.fRowBytes,
69                       color, clip.width(), clip.height());
70        return true;
71    }
72
73    return false;
74}
75
76///////////////////////////////////////////////////////////////////////////////
77///////////////////////////////////////////////////////////////////////////////
78
79static void BW_RowProc_Blend(
80        SkPMColor* SK_RESTRICT dst, const void* maskIn, const SkPMColor* SK_RESTRICT src, int count) {
81    const uint8_t* SK_RESTRICT mask = static_cast<const uint8_t*>(maskIn);
82    int i, octuple = (count + 7) >> 3;
83    for (i = 0; i < octuple; ++i) {
84        int m = *mask++;
85        if (m & 0x80) { dst[0] = SkPMSrcOver(src[0], dst[0]); }
86        if (m & 0x40) { dst[1] = SkPMSrcOver(src[1], dst[1]); }
87        if (m & 0x20) { dst[2] = SkPMSrcOver(src[2], dst[2]); }
88        if (m & 0x10) { dst[3] = SkPMSrcOver(src[3], dst[3]); }
89        if (m & 0x08) { dst[4] = SkPMSrcOver(src[4], dst[4]); }
90        if (m & 0x04) { dst[5] = SkPMSrcOver(src[5], dst[5]); }
91        if (m & 0x02) { dst[6] = SkPMSrcOver(src[6], dst[6]); }
92        if (m & 0x01) { dst[7] = SkPMSrcOver(src[7], dst[7]); }
93        src += 8;
94        dst += 8;
95    }
96    count &= 7;
97    if (count > 0) {
98        int m = *mask;
99        do {
100            if (m & 0x80) { dst[0] = SkPMSrcOver(src[0], dst[0]); }
101            m <<= 1;
102            src += 1;
103            dst += 1;
104        } while (--count > 0);
105    }
106}
107
108static void BW_RowProc_Opaque(
109        SkPMColor* SK_RESTRICT dst, const void* maskIn, const SkPMColor* SK_RESTRICT src, int count) {
110    const uint8_t* SK_RESTRICT mask = static_cast<const uint8_t*>(maskIn);
111    int i, octuple = (count + 7) >> 3;
112    for (i = 0; i < octuple; ++i) {
113        int m = *mask++;
114        if (m & 0x80) { dst[0] = src[0]; }
115        if (m & 0x40) { dst[1] = src[1]; }
116        if (m & 0x20) { dst[2] = src[2]; }
117        if (m & 0x10) { dst[3] = src[3]; }
118        if (m & 0x08) { dst[4] = src[4]; }
119        if (m & 0x04) { dst[5] = src[5]; }
120        if (m & 0x02) { dst[6] = src[6]; }
121        if (m & 0x01) { dst[7] = src[7]; }
122        src += 8;
123        dst += 8;
124    }
125    count &= 7;
126    if (count > 0) {
127        int m = *mask;
128        do {
129            if (m & 0x80) { dst[0] = SkPMSrcOver(src[0], dst[0]); }
130            m <<= 1;
131            src += 1;
132            dst += 1;
133        } while (--count > 0);
134    }
135}
136
137static void A8_RowProc_Blend(
138        SkPMColor* SK_RESTRICT dst, const void* maskIn, const SkPMColor* SK_RESTRICT src, int count) {
139    const uint8_t* SK_RESTRICT mask = static_cast<const uint8_t*>(maskIn);
140    for (int i = 0; i < count; ++i) {
141        if (mask[i]) {
142            dst[i] = SkBlendARGB32(src[i], dst[i], mask[i]);
143        }
144    }
145}
146
147// expand the steps that SkAlphaMulQ performs, but this way we can
148//  exand.. add.. combine
149// instead of
150// expand..combine add expand..combine
151//
152#define EXPAND0(v, m, s)    ((v) & (m)) * (s)
153#define EXPAND1(v, m, s)    (((v) >> 8) & (m)) * (s)
154#define COMBINE(e0, e1, m)  ((((e0) >> 8) & (m)) | ((e1) & ~(m)))
155
156static void A8_RowProc_Opaque(
157        SkPMColor* SK_RESTRICT dst, const void* maskIn, const SkPMColor* SK_RESTRICT src, int count) {
158    const uint8_t* SK_RESTRICT mask = static_cast<const uint8_t*>(maskIn);
159    for (int i = 0; i < count; ++i) {
160        int m = mask[i];
161        if (m) {
162            m += (m >> 7);
163#if 1
164            // this is slightly slower than the expand/combine version, but it
165            // is much closer to the old results, so we use it for now to reduce
166            // rebaselining.
167            dst[i] = SkAlphaMulQ(src[i], m) + SkAlphaMulQ(dst[i], 256 - m);
168#else
169            uint32_t v = src[i];
170            uint32_t s0 = EXPAND0(v, rbmask, m);
171            uint32_t s1 = EXPAND1(v, rbmask, m);
172            v = dst[i];
173            uint32_t d0 = EXPAND0(v, rbmask, m);
174            uint32_t d1 = EXPAND1(v, rbmask, m);
175            dst[i] = COMBINE(s0 + d0, s1 + d1, rbmask);
176#endif
177        }
178    }
179}
180
181static int upscale31To255(int value) {
182    value = (value << 3) | (value >> 2);
183    return value;
184}
185
186static int src_alpha_blend(int src, int dst, int srcA, int mask) {
187
188    return dst + SkAlphaMul(src - SkAlphaMul(srcA, dst), mask);
189}
190
191static void LCD16_RowProc_Blend(
192        SkPMColor* SK_RESTRICT dst, const void* maskIn, const SkPMColor* SK_RESTRICT src, int count) {
193    const uint16_t* SK_RESTRICT mask = static_cast<const uint16_t*>(maskIn);
194    for (int i = 0; i < count; ++i) {
195        uint16_t m = mask[i];
196        if (0 == m) {
197            continue;
198        }
199
200        SkPMColor s = src[i];
201        SkPMColor d = dst[i];
202
203        int srcA = SkGetPackedA32(s);
204        int srcR = SkGetPackedR32(s);
205        int srcG = SkGetPackedG32(s);
206        int srcB = SkGetPackedB32(s);
207
208        srcA += srcA >> 7;
209
210        /*  We want all of these in 5bits, hence the shifts in case one of them
211         *  (green) is 6bits.
212         */
213        int maskR = SkGetPackedR16(m) >> (SK_R16_BITS - 5);
214        int maskG = SkGetPackedG16(m) >> (SK_G16_BITS - 5);
215        int maskB = SkGetPackedB16(m) >> (SK_B16_BITS - 5);
216
217        maskR = upscale31To255(maskR);
218        maskG = upscale31To255(maskG);
219        maskB = upscale31To255(maskB);
220
221        int dstR = SkGetPackedR32(d);
222        int dstG = SkGetPackedG32(d);
223        int dstB = SkGetPackedB32(d);
224
225        // LCD blitting is only supported if the dst is known/required
226        // to be opaque
227        dst[i] = SkPackARGB32(0xFF,
228                              src_alpha_blend(srcR, dstR, srcA, maskR),
229                              src_alpha_blend(srcG, dstG, srcA, maskG),
230                              src_alpha_blend(srcB, dstB, srcA, maskB));
231    }
232}
233
234static void LCD16_RowProc_Opaque(
235        SkPMColor* SK_RESTRICT dst, const void* maskIn, const SkPMColor* SK_RESTRICT src, int count) {
236    const uint16_t* SK_RESTRICT mask = static_cast<const uint16_t*>(maskIn);
237    for (int i = 0; i < count; ++i) {
238        uint16_t m = mask[i];
239        if (0 == m) {
240            continue;
241        }
242
243        SkPMColor s = src[i];
244        SkPMColor d = dst[i];
245
246        int srcR = SkGetPackedR32(s);
247        int srcG = SkGetPackedG32(s);
248        int srcB = SkGetPackedB32(s);
249
250        /*  We want all of these in 5bits, hence the shifts in case one of them
251         *  (green) is 6bits.
252         */
253        int maskR = SkGetPackedR16(m) >> (SK_R16_BITS - 5);
254        int maskG = SkGetPackedG16(m) >> (SK_G16_BITS - 5);
255        int maskB = SkGetPackedB16(m) >> (SK_B16_BITS - 5);
256
257        // Now upscale them to 0..32, so we can use blend32
258        maskR = SkUpscale31To32(maskR);
259        maskG = SkUpscale31To32(maskG);
260        maskB = SkUpscale31To32(maskB);
261
262        int dstR = SkGetPackedR32(d);
263        int dstG = SkGetPackedG32(d);
264        int dstB = SkGetPackedB32(d);
265
266        // LCD blitting is only supported if the dst is known/required
267        // to be opaque
268        dst[i] = SkPackARGB32(0xFF,
269                              SkBlend32(srcR, dstR, maskR),
270                              SkBlend32(srcG, dstG, maskG),
271                              SkBlend32(srcB, dstB, maskB));
272    }
273}
274
275SkBlitMask::RowProc SkBlitMask::RowFactory(SkColorType ct,
276                                           SkMask::Format format,
277                                           RowFlags flags) {
278// make this opt-in until chrome can rebaseline
279    RowProc proc = PlatformRowProcs(ct, format, flags);
280    if (proc) {
281        return proc;
282    }
283
284    static const RowProc gProcs[] = {
285        // need X coordinate to handle BW
286        false ? (RowProc)BW_RowProc_Blend : nullptr, // suppress unused warning
287        false ? (RowProc)BW_RowProc_Opaque : nullptr, // suppress unused warning
288        (RowProc)A8_RowProc_Blend,      (RowProc)A8_RowProc_Opaque,
289        (RowProc)LCD16_RowProc_Blend,   (RowProc)LCD16_RowProc_Opaque,
290    };
291
292    int index;
293    switch (ct) {
294        case kN32_SkColorType:
295            switch (format) {
296                case SkMask::kBW_Format:    index = 0; break;
297                case SkMask::kA8_Format:    index = 2; break;
298                case SkMask::kLCD16_Format: index = 4; break;
299                default:
300                    return nullptr;
301            }
302            if (flags & kSrcIsOpaque_RowFlag) {
303                index |= 1;
304            }
305            SkASSERT((size_t)index < SK_ARRAY_COUNT(gProcs));
306            return gProcs[index];
307        default:
308            break;
309    }
310    return nullptr;
311}
312