Android下Skia遮罩特效的实现

Android下Skia遮罩特效的实现
大体需求是,有一个文字点阵,叫glyph,为单色点阵,有笔画的地方为黑色,1;无笔画的地方为白色,0;
现在要把这个字画到屏幕,还有一个要求,要用当前颜色画;
分析来分析去,这就是一个简单的rop3操作,或者简单一点,就是一个遮罩特效:使用glyph当遮罩,使用当前颜色画矩形,仅此而以。
但是,目前没有在Skia中找到类似的操作,只找到SkXformode里有这样的定义:
enum Mode {
kClear_Mode, //!< [0, 0]
kSrc_Mode, //!< [Sa, Sc]
kDst_Mode, //!< [Da, Dc]
kSrcOver_Mode, //!< [Sa + Da - Sa*Da, Rc = Sc + (1 -
Sa)*Dc]
kDstOver_Mode, //!< [Sa + Da - Sa*Da, Rc = Dc + (1 -
Da)*Sc]
kSrcIn_Mode, //!< [Sa * Da, Sc * Da]
kDstIn_Mode, //!< [Sa * Da, Sa * Dc]
kSrcOut_Mode, //!< [Sa * (1 - Da), Sc * (1 - Da)]
kDstOut_Mode, //!< [Da * (1 - Sa), Dc * (1 - Sa)]
kSrcATop_Mode, //!< [Da, Sc * Da + (1 - Sa) * Dc]
kDstATop_Mode, //!< [Sa, Sa * Dc + Sc * (1 - Da)]
kXor_Mode, //!< [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1-Sa)* Dc]
......
基本上看不懂,光看公式,没法知道到底是个什么效果;看来只有使用这些东西来模拟遮罩效果了。
试验来试验去,下面的代码可以实现:
1 首先,把单色位图转换成32位
static void depth1to32(const uint16 * data, uint8 * out,
uint8
*
end)
{
uint16 pixel;
uint32 value;
......
// glyph : src SkBitmap
// bitmap : dst SkBitmap
int width;
int height;
width = glyph->width();
height = glyph->height();
uint8 *sbits;
sbits = (uint8 *)data;
int sstride;
sstride = (width + 7) / 8;
uint8 *sline;
sline = sbits;
// check format
uint8 *dbits;
dbits = (uint8 *)out;
int dstride;
dstride = bitmap->rowBytes();
int dbpp; // bytes per pixel
dbpp = bitmap->bytesPerPixel();
uint8 *dline;
dline = dbits;
while (height--)
{
int w = width;
uint8 *dline1 = dline;
uint8 *sline1 = sline;
while(w > 0)
{
uint8 pixel8;
pixel8 = *(sline1++);
for(int i = 0; i < (w < 8 ? w : 8); i++)
{
uint8 c = 1 < < (7 - i);
if(pixel8 & c)
{
*(dline1++) = 0;
*(dline1++) = 0;
*(dline1++) = 0;
// 这个地方,如果最高两个字节是0,则表示透明
*(dline1++) = 0xFF;
}
else
{
*(dline1++) = 0xff;
*(dline1++) = 0xff;
*(dline1++) = 0xff;
// 这个地方,如果最高两个字节是0,则表示透明
*(dline1++) = 0;
}
}
w -= 8;
}
dline += dstride;
sline += sstride;
}
}
......
2 使用当前颜色转换遮罩
SkBitmap *srcbmp = (SkBitmap*)(bitmap);
SkPaint paint;
SkCanvas canvas(*srcbmp);
paint.setXfermodeMode(SkXfermode::kSrcIn_Mode);
paint.setColor(display->foreColor);
SkRect rect = { 0, 0, srcbmp->width(),
srcbmp->height()};
canvas.drawRect(rect, paint);
自此,srcbmp已经变成了当前颜色表示的字符点阵
3 把字符画到屏幕
SkShader* s = SkShader::CreateBitmapShader(*srcbmp,
SkShader::kClamp_TileMode,
SkShader::kClamp_TileMode);
SkMatrix matrix;
matrix.reset();
matrix.postTranslate(x, y);
SkShader *shader = (SkShader *)s;
shader->setLocalMatrix(matrix); // 设置原点
SkPaint *paint = (SkPaint *)paint;
paint->setShader(shader)->unref();
paint->setXfermodeMode(SkXfermode::kSrcOver_Mode);
paint->setColor(display->foreColor);
SkRect rect = { x, y, x + w, y + h};
canvas->drawRect(rect, *paint);
这段代码,相当于把一个部分区域透明的pattern画到屏幕上
这样,我们就实现了字符遮罩的功能,使用当前颜色把字符画到屏幕上了。
当然,我们只是画了一个单色字符,其实也可以画很复杂的图案,类似于PS和Flash里的遮罩层的效果。
以上代码是示意代码,因此有些地方明显无法编译,有些地方明显多此一举,请注意。
另,Skia有类似Mask的概念,但看来看去,好像没有我们这里说的遮罩功能。
如果大家有更好的实现方法,也请告诉我一下。

你可能感兴趣的:(c,android,Flash,Matrix,DST,shader)