在做图片处理的时候,特别是两张图片处理的时候,需要把他们重叠在一起,需要显示出不同的重叠效果,PS中有多种图片的处理方式。自己可以尝试的使用一下。
正片叠底中白色全部被屏蔽掉了,将两层底色混合,颜色相乘产生较暗的颜色,一般在制作阴影时常使用该模式。它一般会把颜色的像素较浅显示的不明显,较深的保留,产生较暗的效果。取两图层交集部分叠加后颜色
其实“正片叠底”的计算公式是:A*B/255。(A、B是指的图层A和图层B)。是指:
A图层中的红色通道和B图层中的红色通道所对应的每一个像素的灰阶值相乘,再被255除。得到一个新的红色通道。
A图层中的绿色通道和B图层中的绿色通道所对应的每一个像素的灰阶值相乘,再被255除。得到一个新的绿色通道。
A图层中的蓝色通道和B图层中的蓝色通道所对应的每一个像素的灰阶值相乘,再被255除。得到一个新的蓝色通道。
好像忘了些什么,对了就是透明度。那么透明度的计算公式是怎么样的呢?
透明度也是这样计算的:
A层的透明度乘以B层的透明度然后除以255。[AlphaA*AlphaB/255]特别注意这个公式。这个公式说明这AB两个图层以正片叠底地方式混合没有先后顺序,谁在上层谁在下层都可以。因为混合后的目标图像的透明度是通过他们相乘的积得来的。
同时还需要注意一点:由于他们是相乘的关系,如果其中一个图层的透明度为0,混合之后的图像透明度也是为0;如果其中一层的透明度为255,那么混合之后的图像的透明度是不变的。假设A层的透明度为0(全透明),混合之后的目标透明度为:0
* AlphaB / 255 = 0;如A层图像的透明度为255(不透明),那么混合之后目标透明度为:255 * AlphB / 255 = AlphaB。混合之后的目标图像的透明度就是B层的透明度。如果是AB两层的透明度是介于0 - 255 之间的值,那么混合之后的目标图像的透明度就要比AB其中任何层的透明度都要小。
/**
* Author: wb
* Date: 2016/10/19 09:19.
*/
public class SfvMainView extends SurfaceView implements SurfaceHolder.Callback {
private Context mContext;
private SurfaceHolder mSurfaceHolder;
private MyRunnable mRunnable ;
private Bitmap mSrcBitmap; //源图片
private Bitmap mGraffitiBitmap; //涂鸦图片
private Canvas mGraffitiCanvas; // 用于绘制涂鸦的画布
private Bitmap mCurrentBitmap; // 绘制当前这一条线时用到的图片
private Canvas mCurrentCanvas; // 当前这一条绘制线的画布
private Paint mPdXfPaint; // 绘图的混合模式
private PorterDuffXfermode mPdXfermode; // 定义PorterDuffXfermode变量
private int mViewWidth, mViewHeight; //视图的长度和宽度
private float mPrivateScale; // 图片适应屏幕时的缩放倍数
private float mScale; // 缩放倍数, 图片真实的缩放倍数为 mPrivateScale * mScale
private int mPrivateHeight, mPrivateWidth; // 图片适应屏幕时的大小
private float mCentreTranX, mCenterTranY; // 图片居中时图片左上角的偏移
private float mTransX = 0, mTransY = 0; // 偏移量,图片真实偏移量为 mCentreTranX + mTransX
int[] mWhiteBuffer;//保存白色图片内存,刷新时重新刷新图片
private int mTouchMode; // 触摸模式,触点数量
private float mTouchDownX, mTouchDownY, mLastTouchX, mLastTouchY, mTouchX, mTouchY;
private MyDrawSinglePath mCurrPath; // 当前手写的路径
private boolean mIsPainting = false;//true - 表示正在绘制
// 保存涂鸦操作,便于撤销
private CopyOnWriteArrayList mPathStack = new CopyOnWriteArrayList();
private CopyOnWriteArrayList mPathStackBackup = new CopyOnWriteArrayList();
private Paint mCurrentPaint;
private Path mCanvasPath; //仅用于当前Path的绘制
private PointF mCircleCenter = new PointF();//取色器中点
public SfvMainView(Context context) {
super(context);
mContext = context;
init();
}
public SfvMainView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
init();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
mRunnable = new MyRunnable("Draw");
mRunnable.start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
private void init() {
//关闭硬件加速,硬件加速对混合模式有影响
// setLayerType(View.LAYER_TYPE_SOFTWARE, null);
mSurfaceHolder = getHolder();
mSurfaceHolder.addCallback(this);
mSurfaceHolder.setFormat(PixelFormat.RGBA_8888);
mCurrentPaint = new Paint();
mCanvasPath = new Path();
mScale = 1f;
}
public void setBGBitmap(Bitmap bitmap) {
mSrcBitmap = bitmap;
mPdXfermode = new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY);
mPdXfPaint = new Paint();
mPdXfPaint.setAntiAlias(true);
mPdXfPaint.setFilterBitmap(true);
//创建一张白色的完全不透明的和源图片一样大小的图片,用于绘制涂鸦
mWhiteBuffer = new int[mSrcBitmap.getWidth() * mSrcBitmap.getHeight()];
Arrays.fill(mWhiteBuffer, 0xFFFFFFFF);
mGraffitiBitmap = Bitmap.createBitmap(mWhiteBuffer, mSrcBitmap.getWidth(), mSrcBitmap.getHeight(), Bitmap.Config.ARGB_8888).copy(Bitmap.Config.ARGB_8888, true);
mGraffitiCanvas = new Canvas(mGraffitiBitmap);
mCurrentBitmap = Bitmap.createBitmap(mWhiteBuffer, mSrcBitmap.getWidth(), mSrcBitmap.getHeight(), Bitmap.Config.ARGB_8888).copy(Bitmap.Config.ARGB_8888, true);
mCurrentCanvas = new Canvas(mCurrentBitmap);
}
@Override
protected void onSizeChanged(int w0, int h0, int oldw, int oldh) {
super.onSizeChanged(w0, h0, oldw, oldh);
mViewWidth = w0;
mViewHeight = h0;
int w = mSrcBitmap.getWidth();
int h = mSrcBitmap.getHeight();
float nw = w * 1f / mViewWidth;
float nh = h * 1f / mViewHeight;
if (nw > nh) {
mPrivateScale = 1 / nw;
mPrivateWidth = mViewWidth;
mPrivateHeight = (int) (h * mPrivateScale);
} else {
mPrivateScale = 1 / nh;
mPrivateWidth = (int) (w * mPrivateScale);
mPrivateHeight = mViewHeight;
}
// 使图片居中
mCentreTranX = (mViewWidth - mPrivateWidth) / 2f;
mCenterTranY = (mViewHeight - mPrivateHeight) / 2f;
}
/**
* 将触摸的屏幕坐标转换成实际图片中的坐标
*/
public float screenToBitmapX(float touchX) {
return (touchX - mCentreTranX - mTransX) / (mPrivateScale * mScale);
}
public float screenToBitmapY(float touchY) {
return (touchY - mCenterTranY - mTransY) / (mPrivateScale * mScale);
}
/**
* 将触摸屏幕中的左边装换成Canvas画布的坐标
*/
private float screenToCanvas(float touch) {
return (touch) / (mPrivateScale * mScale);
}
/**
* 坐标换算 计算出偏移量
* (公式由toX()中的公式推算出)
*
* @param touchX 触摸坐标
* @param graffitiX 在涂鸦图片中的坐标
* @return 偏移量
*/
public final float toTransX(float touchX, float graffitiX) {
return -graffitiX * (mPrivateScale * mScale) + touchX - mCentreTranX;
}
public final float toTransY(float touchY, float graffitiY) {
return -graffitiY * (mPrivateScale * mScale) + touchY - mCenterTranY;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
mTouchMode = 1;
if (EventManager.getEventTypes() == EventTypes.EVENT_COLOR_PICKER) {
// getColor2(event);
return true;
} else if (EventManager.getEventTypes() == EventTypes.EVENT_PEN || EventManager.getEventTypes() == EventTypes.EVENT_ERASER) {
//画笔设置的统一接口
setCurrentPaint();
penTouchDown(event.getX(), event.getY());
}
break;
case MotionEvent.ACTION_MOVE:
if (EventManager.getEventTypes() == EventTypes.EVENT_COLOR_PICKER) {//取色器
// getColor2(event);
return true;
} else if (EventManager.getEventTypes() == EventTypes.EVENT_PEN || EventManager.getEventTypes() == EventTypes.EVENT_ERASER) {
if (mTouchMode < 2) { // 单点滑动
mLastTouchX = mTouchX;
mLastTouchY = mTouchY;
mTouchX = event.getX();
mTouchY = event.getY();
mCurrPath.getPath().quadTo(screenToBitmapX(mLastTouchX), screenToBitmapY(mLastTouchY),
screenToBitmapX((mTouchX + mLastTouchX) / 2), screenToBitmapY((mTouchY + mLastTouchY) / 2));
//绘制当前画线
mCanvasPath.quadTo(screenToBitmapX(mLastTouchX), screenToBitmapY(mLastTouchY),
screenToBitmapX((mTouchX + mLastTouchX) / 2), screenToBitmapY((mTouchY + mLastTouchY) / 2));
// initCurrentCanvas();
// mCurrentCanvas.drawPath(mCanvasPath, mCurrentPaint);
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mIsPainting = false;
mTouchMode = 0;
if (EventManager.getEventTypes() == EventTypes.EVENT_COLOR_PICKER) {
// iGetViewHW.noticePicker(false, null, Color.TRANSPARENT);
// iGetViewHW.update();
return true;
} else if (EventManager.getEventTypes() == EventTypes.EVENT_PEN || EventManager.getEventTypes() == EventTypes.EVENT_ERASER) {
mLastTouchX = mTouchX;
mLastTouchY = mTouchY;
mTouchX = event.getX();
mTouchY = event.getY();
initCanvas();//添上这句防止重复绘制
draw(mGraffitiCanvas, mPathStack); // 保存到图片中
}
break;
case MotionEvent.ACTION_POINTER_UP:
mTouchMode--;
if (EventManager.getEventTypes() == EventTypes.EVENT_PEN && mTouchMode == 1) {
int upidx = event.getActionIndex();
int first = (upidx == 0 ? 1 : 0);
penTouchDown(event.getX(first), event.getY(first));
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
mTouchMode++;
if (EventManager.getEventTypes() == EventTypes.EVENT_PEN) {
if (!mCanvasPath.isEmpty()) {//单指画线过程中,出现双触点则停止画线
mCanvasPath.reset();
mPathStack.remove(mPathStack.size() - 1);
}
}
break;
}
return true;
}
/**
* 在画笔的状态下第一个触点按下的情况
*/
private void penTouchDown(float x, float y) {
mIsPainting = true;
mTouchDownX = mTouchX = mLastTouchX = x;
mTouchDownY = mTouchY = mLastTouchY = y;
// 为了仅点击时也能出现绘图,模拟滑动一个像素点
mTouchX++;
mTouchY++;
mCurrPath = new MyDrawSinglePath(GlobalData.getCurrentPen().getPenColor(), GlobalData.getCurrentPen().getPenSize(),
GlobalData.getCurrentPen().getPenAlpha(), GlobalData.getCurrentPen().isBooleanPen());
mCurrPath.getPath().moveTo(screenToBitmapX(mTouchDownX), screenToBitmapY(mTouchDownY));
mPathStack.add(mCurrPath);
mCanvasPath.reset();
mCanvasPath.moveTo(screenToBitmapX(mTouchDownX), screenToBitmapY(mTouchDownY));
// 为了仅点击时也能出现绘图,必须移动path
mCanvasPath.quadTo(screenToBitmapX(mLastTouchX), screenToBitmapY(mLastTouchY),
screenToBitmapX((mTouchX + mLastTouchX) / 2), screenToBitmapY((mTouchY + mLastTouchY) / 2));
}
/**
* 设置画笔
*/
private void setCurrentPaint() {
if (GlobalData.getCurrentPen().isBooleanPen()) {
mCurrentPaint.setStyle(Paint.Style.STROKE);
mCurrentPaint.setStrokeWidth(GlobalData.getCurrentPen().getPenSize());
mCurrentPaint.setColor(GlobalData.getCurrentPen().getPenColor());
mCurrentPaint.setAlpha(GlobalData.getCurrentPen().getPenAlpha());
mCurrentPaint.setAntiAlias(true);
mCurrentPaint.setStrokeJoin(Paint.Join.ROUND);
mCurrentPaint.setStrokeCap(Paint.Cap.ROUND);
mCurrentPaint.setXfermode(null);
} else {
mCurrentPaint.setStyle(Paint.Style.STROKE);
mCurrentPaint.setStrokeWidth(GlobalData.getCurrentPen().getPenSize());
mCurrentPaint.setColor(Color.WHITE);
mCurrentPaint.setAlpha(255);
mCurrentPaint.setAntiAlias(true);
mCurrentPaint.setStrokeJoin(Paint.Join.ROUND);
mCurrentPaint.setStrokeCap(Paint.Cap.ROUND);
mCurrentPaint.setStrokeWidth(GlobalData.getCurrentPen().getPenSize());
}
}
/**
* 初始化涂鸦的绘图
*/
private void initCanvas() {
mGraffitiBitmap.eraseColor(0xFFFFFFFF);
}
/**
* 初始化当前画线的绘图
*/
private void initCurrentCanvas() {
mCurrentBitmap.eraseColor(0xFFFFFFFF);
}
private void draw(Canvas canvas, CopyOnWriteArrayList pathStack) {
mRunnable.suspend();
// 还原堆栈中的记录的操作
for (MyDrawSinglePath path : pathStack) {
canvas.drawPath(path.getPath(), path.getMyPen().getPenPaint());
}
mRunnable.resume();
}
/***********************************************************************************************
* 绘图线程
**********************************************************************************************/
class MyRunnable implements Runnable {
public Thread mThread = null;
private String mName;
public boolean mPause = false;//true-为暂停 false-运行
public MyRunnable(String name) {
mName = name;
}
@Override
public void run() {
synchronized (this) {
while (true && (!mPause)) {
if (mSrcBitmap != null) {
Canvas canvas = mSurfaceHolder.lockCanvas();
canvas.drawColor(Color.WHITE);
// initCurrentCanvas();
mCurrentBitmap.eraseColor(0xFFFFFFFF);
mCurrentCanvas.drawPath(mCanvasPath, mCurrentPaint);
float scale = mPrivateScale * mScale;
float x = (mCentreTranX + mTransX) / scale;
float y = (mCenterTranY + mTransY) / scale;
canvas.scale(scale, scale);
canvas.drawBitmap(mSrcBitmap, x, y, mPdXfPaint);
mPdXfPaint.setXfermode(mPdXfermode);
canvas.drawBitmap(mGraffitiBitmap, x, y, mPdXfPaint);
canvas.drawBitmap(mCurrentBitmap, x, y, mPdXfPaint);
mSurfaceHolder.unlockCanvasAndPost(canvas);
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public void start() {
if (mThread == null) {
mThread = new Thread(this, mName);
mThread.start();
Log.i("aaa", mName + ": start");
}
}
public void suspend() {
mPause = true;
Log.i("aaa", mName + ": suspend");
}
public synchronized void resume() {
mPause = false;
notify();
Log.i("aaa", mName + ": resume");
}
}
}