/*
* Copyright 2014 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkBlitMask.h"
#include "SkColor.h"
#include "SkColorPriv.h"
#include "SkOpts.h"
SkBlitMask::BlitLCD16RowProc SkBlitMask::BlitLCD16RowFactory(bool isOpaque) {
BlitLCD16RowProc proc = PlatformBlitRowProcs16(isOpaque);
if (proc) {
return proc;
}
if (isOpaque) {
return SkBlitLCD16OpaqueRow;
} else {
return SkBlitLCD16Row;
}
}
static void D32_LCD16_Proc(void* SK_RESTRICT dst, size_t dstRB,
const void* SK_RESTRICT mask, size_t maskRB,
SkColor color, int width, int height) {
SkPMColor* dstRow = (SkPMColor*)dst;
const uint16_t* srcRow = (const uint16_t*)mask;
SkPMColor opaqueDst;
SkBlitMask::BlitLCD16RowProc proc = nullptr;
bool isOpaque = (0xFF == SkColorGetA(color));
proc = SkBlitMask::BlitLCD16RowFactory(isOpaque);
SkASSERT(proc != nullptr);
if (isOpaque) {
opaqueDst = SkPreMultiplyColor(color);
} else {
opaqueDst = 0; // ignored
}
do {
proc(dstRow, srcRow, color, width, opaqueDst);
dstRow = (SkPMColor*)((char*)dstRow + dstRB);
srcRow = (const uint16_t*)((const char*)srcRow + maskRB);
} while (--height != 0);
}
///////////////////////////////////////////////////////////////////////////////
bool SkBlitMask::BlitColor(const SkPixmap& device, const SkMask& mask,
const SkIRect& clip, SkColor color) {
int x = clip.fLeft, y = clip.fTop;
if (device.colorType() == kN32_SkColorType && mask.fFormat == SkMask::kA8_Format) {
SkOpts::blit_mask_d32_a8(device.writable_addr32(x,y), device.rowBytes(),
(const SkAlpha*)mask.getAddr(x,y), mask.fRowBytes,
color, clip.width(), clip.height());
return true;
}
if (device.colorType() == kN32_SkColorType && mask.fFormat == SkMask::kLCD16_Format) {
// TODO: Is this reachable code? Seems like no.
D32_LCD16_Proc(device.writable_addr32(x,y), device.rowBytes(),
mask.getAddr(x,y), mask.fRowBytes,
color, clip.width(), clip.height());
return true;
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
static void BW_RowProc_Blend(
SkPMColor* SK_RESTRICT dst, const void* maskIn, const SkPMColor* SK_RESTRICT src, int count) {
const uint8_t* SK_RESTRICT mask = static_cast<const uint8_t*>(maskIn);
int i, octuple = (count + 7) >> 3;
for (i = 0; i < octuple; ++i) {
int m = *mask++;
if (m & 0x80) { dst[0] = SkPMSrcOver(src[0], dst[0]); }
if (m & 0x40) { dst[1] = SkPMSrcOver(src[1], dst[1]); }
if (m & 0x20) { dst[2] = SkPMSrcOver(src[2], dst[2]); }
if (m & 0x10) { dst[3] = SkPMSrcOver(src[3], dst[3]); }
if (m & 0x08) { dst[4] = SkPMSrcOver(src[4], dst[4]); }
if (m & 0x04) { dst[5] = SkPMSrcOver(src[5], dst[5]); }
if (m & 0x02) { dst[6] = SkPMSrcOver(src[6], dst[6]); }
if (m & 0x01) { dst[7] = SkPMSrcOver(src[7], dst[7]); }
src += 8;
dst += 8;
}
count &= 7;
if (count > 0) {
int m = *mask;
do {
if (m & 0x80) { dst[0] = SkPMSrcOver(src[0], dst[0]); }
m <<= 1;
src += 1;
dst += 1;
} while (--count > 0);
}
}
static void BW_RowProc_Opaque(
SkPMColor* SK_RESTRICT dst, const void* maskIn, const SkPMColor* SK_RESTRICT src, int count) {
const uint8_t* SK_RESTRICT mask = static_cast<const uint8_t*>(maskIn);
int i, octuple = (count + 7) >> 3;
for (i = 0; i < octuple; ++i) {
int m = *mask++;
if (m & 0x80) { dst[0] = src[0]; }
if (m & 0x40) { dst[1] = src[1]; }
if (m & 0x20) { dst[2] = src[2]; }
if (m & 0x10) { dst[3] = src[3]; }
if (m & 0x08) { dst[4] = src[4]; }
if (m & 0x04) { dst[5] = src[5]; }
if (m & 0x02) { dst[6] = src[6]; }
if (m & 0x01) { dst[7] = src[7]; }
src += 8;
dst += 8;
}
count &= 7;
if (count > 0) {
int m = *mask;
do {
if (m & 0x80) { dst[0] = SkPMSrcOver(src[0], dst[0]); }
m <<= 1;
src += 1;
dst += 1;
} while (--count > 0);
}
}
static void A8_RowProc_Blend(
SkPMColor* SK_RESTRICT dst, const void* maskIn, const SkPMColor* SK_RESTRICT src, int count) {
const uint8_t* SK_RESTRICT mask = static_cast<const uint8_t*>(maskIn);
for (int i = 0; i < count; ++i) {
if (mask[i]) {
dst[i] = SkBlendARGB32(src[i], dst[i], mask[i]);
}
}
}
// expand the steps that SkAlphaMulQ performs, but this way we can
// exand.. add.. combine
// instead of
// expand..combine add expand..combine
//
#define EXPAND0(v, m, s) ((v) & (m)) * (s)
#define EXPAND1(v, m, s) (((v) >> 8) & (m)) * (s)
#define COMBINE(e0, e1, m) ((((e0) >> 8) & (m)) | ((e1) & ~(m)))
static void A8_RowProc_Opaque(
SkPMColor* SK_RESTRICT dst, const void* maskIn, const SkPMColor* SK_RESTRICT src, int count) {
const uint8_t* SK_RESTRICT mask = static_cast<const uint8_t*>(maskIn);
for (int i = 0; i < count; ++i) {
int m = mask[i];
if (m) {
m += (m >> 7);
#if 1
// this is slightly slower than the expand/combine version, but it
// is much closer to the old results, so we use it for now to reduce
// rebaselining.
dst[i] = SkAlphaMulQ(src[i], m) + SkAlphaMulQ(dst[i], 256 - m);
#else
uint32_t v = src[i];
uint32_t s0 = EXPAND0(v, rbmask, m);
uint32_t s1 = EXPAND1(v, rbmask, m);
v = dst[i];
uint32_t d0 = EXPAND0(v, rbmask, m);
uint32_t d1 = EXPAND1(v, rbmask, m);
dst[i] = COMBINE(s0 + d0, s1 + d1, rbmask);
#endif
}
}
}
static int upscale31To255(int value) {
value = (value << 3) | (value >> 2);
return value;
}
static int src_alpha_blend(int src, int dst, int srcA, int mask) {
return dst + SkAlphaMul(src - SkAlphaMul(srcA, dst), mask);
}
static void LCD16_RowProc_Blend(
SkPMColor* SK_RESTRICT dst, const void* maskIn, const SkPMColor* SK_RESTRICT src, int count) {
const uint16_t* SK_RESTRICT mask = static_cast<const uint16_t*>(maskIn);
for (int i = 0; i < count; ++i) {
uint16_t m = mask[i];
if (0 == m) {
continue;
}
SkPMColor s = src[i];
SkPMColor d = dst[i];
int srcA = SkGetPackedA32(s);
int srcR = SkGetPackedR32(s);
int srcG = SkGetPackedG32(s);
int srcB = SkGetPackedB32(s);
srcA += srcA >> 7;
/* We want all of these in 5bits, hence the shifts in case one of them
* (green) is 6bits.
*/
int maskR = SkGetPackedR16(m) >> (SK_R16_BITS - 5);
int maskG = SkGetPackedG16(m) >> (SK_G16_BITS - 5);
int maskB = SkGetPackedB16(m) >> (SK_B16_BITS - 5);
maskR = upscale31To255(maskR);
maskG = upscale31To255(maskG);
maskB = upscale31To255(maskB);
int dstR = SkGetPackedR32(d);
int dstG = SkGetPackedG32(d);
int dstB = SkGetPackedB32(d);
// LCD blitting is only supported if the dst is known/required
// to be opaque
dst[i] = SkPackARGB32(0xFF,
src_alpha_blend(srcR, dstR, srcA, maskR),
src_alpha_blend(srcG, dstG, srcA, maskG),
src_alpha_blend(srcB, dstB, srcA, maskB));
}
}
static void LCD16_RowProc_Opaque(
SkPMColor* SK_RESTRICT dst, const void* maskIn, const SkPMColor* SK_RESTRICT src, int count) {
const uint16_t* SK_RESTRICT mask = static_cast<const uint16_t*>(maskIn);
for (int i = 0; i < count; ++i) {
uint16_t m = mask[i];
if (0 == m) {
continue;
}
SkPMColor s = src[i];
SkPMColor d = dst[i];
int srcR = SkGetPackedR32(s);
int srcG = SkGetPackedG32(s);
int srcB = SkGetPackedB32(s);
/* We want all of these in 5bits, hence the shifts in case one of them
* (green) is 6bits.
*/
int maskR = SkGetPackedR16(m) >> (SK_R16_BITS - 5);
int maskG = SkGetPackedG16(m) >> (SK_G16_BITS - 5);
int maskB = SkGetPackedB16(m) >> (SK_B16_BITS - 5);
// Now upscale them to 0..32, so we can use blend32
maskR = SkUpscale31To32(maskR);
maskG = SkUpscale31To32(maskG);
maskB = SkUpscale31To32(maskB);
int dstR = SkGetPackedR32(d);
int dstG = SkGetPackedG32(d);
int dstB = SkGetPackedB32(d);
// LCD blitting is only supported if the dst is known/required
// to be opaque
dst[i] = SkPackARGB32(0xFF,
SkBlend32(srcR, dstR, maskR),
SkBlend32(srcG, dstG, maskG),
SkBlend32(srcB, dstB, maskB));
}
}
SkBlitMask::RowProc SkBlitMask::RowFactory(SkColorType ct,
SkMask::Format format,
RowFlags flags) {
// make this opt-in until chrome can rebaseline
RowProc proc = PlatformRowProcs(ct, format, flags);
if (proc) {
return proc;
}
static const RowProc gProcs[] = {
// need X coordinate to handle BW
false ? (RowProc)BW_RowProc_Blend : nullptr, // suppress unused warning
false ? (RowProc)BW_RowProc_Opaque : nullptr, // suppress unused warning
(RowProc)A8_RowProc_Blend, (RowProc)A8_RowProc_Opaque,
(RowProc)LCD16_RowProc_Blend, (RowProc)LCD16_RowProc_Opaque,
};
int index;
switch (ct) {
case kN32_SkColorType:
switch (format) {
case SkMask::kBW_Format: index = 0; break;
case SkMask::kA8_Format: index = 2; break;
case SkMask::kLCD16_Format: index = 4; break;
default:
return nullptr;
}
if (flags & kSrcIsOpaque_RowFlag) {
index |= 1;
}
SkASSERT((size_t)index < SK_ARRAY_COUNT(gProcs));
return gProcs[index];
default:
break;
}
return nullptr;
}