android实现图片橡皮擦和快速染色功能

作者:爱折腾的猫 时间:2023-09-08 01:30:02 

本文为大家分享了android实现图片橡皮擦和快速染色的具体代码,供大家参考,具体内容如下

源码地址:Eraselmg

1.染色

     关于染色部分,可以分别设置调整画笔的大小和画笔的透明度,画笔已经设置了模糊效果。画笔的特效可以调整下面一行代码:

android实现图片橡皮擦和快速染色功能

2.橡皮擦

  橡皮擦的实现用了两个canvas,一个临时的,一个是作用在ImageTouchView上显示的,代码里面有注释,这里不再详细介绍。

3.功能展示:

原图:

android实现图片橡皮擦和快速染色功能

画笔设置界面:

android实现图片橡皮擦和快速染色功能

(1)画笔大小为32,透明度为255(不透明)。如下图:

android实现图片橡皮擦和快速染色功能

(2)画笔大小为32,透明度为10,如下图:

android实现图片橡皮擦和快速染色功能

融合的效果跟画笔的透明度有关系,也跟背景图片的相应区域颜色有关,所以透明度的值自行调整得出满意效果。

(3)擦除

擦除前图像:

android实现图片橡皮擦和快速染色功能

部分擦除后:

android实现图片橡皮擦和快速染色功能

4.Bitmap处理相关的类BitmapUtils:


package com.jiangjie.utils;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Bitmap.Config;
public class BitmapUtils {

/**
 * 缩放图片
 */
public static void bitmapScale(Bitmap baseBitmap, Paint paint, float x, float y) {
 // 因为要将图片放大,所以要根据放大的尺寸重新创建Bitmap
 Bitmap scaleBitmap = Bitmap.createBitmap(
   (int) (baseBitmap.getWidth() * x),
   (int) (baseBitmap.getHeight() * y), baseBitmap.getConfig());
 Canvas canvas = new Canvas(scaleBitmap);
 // 初始化Matrix对象
 Matrix matrix = new Matrix();
 // 根据传入的参数设置缩放比例
 matrix.setScale(x, y);
 // 根据缩放比例,把图片draw到Canvas上
 canvas.drawBitmap(baseBitmap, matrix,paint);
}

/**
 * 图片旋转
 */
public static void bitmapRotate(Bitmap baseBitmap, Paint paint,float degrees) {
 // 创建一个和原图一样大小的图片
 Bitmap afterBitmap = Bitmap.createBitmap(baseBitmap.getWidth(),
   baseBitmap.getHeight(), baseBitmap.getConfig());
 Canvas canvas = new Canvas(afterBitmap);
 Matrix matrix = new Matrix();
 // 根据原图的中心位置旋转
 matrix.setRotate(degrees, baseBitmap.getWidth() / 2,
   baseBitmap.getHeight() / 2);
 canvas.drawBitmap(baseBitmap, matrix, paint);
}

/**
 * 图片移动
 */
public static void bitmapTranslate(Bitmap baseBitmap, Paint paint, float dx, float dy) {
 // 需要根据移动的距离来创建图片的拷贝图大小
 Bitmap afterBitmap = Bitmap.createBitmap(
   (int) (baseBitmap.getWidth() + dx),
   (int) (baseBitmap.getHeight() + dy), baseBitmap.getConfig());
 Canvas canvas = new Canvas(afterBitmap);
 Matrix matrix = new Matrix();
 // 设置移动的距离
 matrix.setTranslate(dx, dy);
 canvas.drawBitmap(baseBitmap, matrix, paint);
}

/**
 * 倾斜图片
 */
public static void bitmapSkew(Bitmap baseBitmap, Paint paint, float dx, float dy) {
 // 根据图片的倾斜比例,计算变换后图片的大小,
 Bitmap afterBitmap = Bitmap.createBitmap(baseBitmap.getWidth()
   + (int) (baseBitmap.getWidth() * dx), baseBitmap.getHeight()
   + (int) (baseBitmap.getHeight() * dy), baseBitmap.getConfig());
 Canvas canvas = new Canvas(afterBitmap);
 Matrix matrix = new Matrix();
 // 设置图片倾斜的比例
 matrix.setSkew(dx, dy);
 canvas.drawBitmap(baseBitmap, matrix, paint);
}

public static Bitmap decodeFromResource(Context context, int id) {
 Resources res = context.getResources();
 Bitmap bitmap = BitmapFactory.decodeResource(res,id).copy(Bitmap.Config.ARGB_8888, true);
 return bitmap;
}

/**
 * 保存图片到SD卡
 */
public static void saveToSdCard(String path, Bitmap bitmap) {
if (null != bitmap && null != path && !path.equalsIgnoreCase("")) {
try {
File file = new File(path);
FileOutputStream outputStream = null;
//创建文件,并写入内容
outputStream = new FileOutputStream(new File(path), true);
bitmap.compress(Bitmap.CompressFormat.PNG, 30, outputStream);
outputStream.flush();
outputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

}
}

/**
 * 复制bitmap
 */
public static Bitmap duplicateBitmap(Bitmap bmpSrc, int width, int height) {
if (null == bmpSrc) {
return null;
}

int bmpSrcWidth = bmpSrc.getWidth();
int bmpSrcHeight = bmpSrc.getHeight();

Bitmap bmpDest = Bitmap.createBitmap(width, height, Config.ARGB_8888);
if (null != bmpDest) {
Canvas canvas = new Canvas(bmpDest);
Rect viewRect = new Rect();
final Rect rect = new Rect(0, 0, bmpSrcWidth, bmpSrcHeight);
if (bmpSrcWidth <= width && bmpSrcHeight <= height) {
viewRect.set(rect);
} else if (bmpSrcHeight > height && bmpSrcWidth <= width) {
viewRect.set(0, 0, bmpSrcWidth, height);
} else if (bmpSrcHeight <= height && bmpSrcWidth > width) {
viewRect.set(0, 0, width, bmpSrcWidth);
} else if (bmpSrcHeight > height && bmpSrcWidth > width) {
viewRect.set(0, 0, width, height);
}
canvas.drawBitmap(bmpSrc, rect, viewRect, null);
}

return bmpDest;
}

/**
 * 复制bitmap
 */
public static Bitmap duplicateBitmap(Bitmap bmpSrc) {
if (null == bmpSrc) {
return null;
}

int bmpSrcWidth = bmpSrc.getWidth();
int bmpSrcHeight = bmpSrc.getHeight();

Bitmap bmpDest = Bitmap.createBitmap(bmpSrcWidth, bmpSrcHeight,
Config.ARGB_8888);
if (null != bmpDest) {
Canvas canvas = new Canvas(bmpDest);
final Rect rect = new Rect(0, 0, bmpSrcWidth, bmpSrcHeight);

canvas.drawBitmap(bmpSrc, rect, rect, null);
}

return bmpDest;
}

/**
 * bitmap转字节码
 */
public static byte[] bitampToByteArray(Bitmap bitmap) {
byte[] array = null;
try {
if (null != bitmap) {
ByteArrayOutputStream os = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
array = os.toByteArray();
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}

return array;
}

/**
 * 字节码转bitmap
 */
public static Bitmap byteArrayToBitmap(byte[] array) {
if (null == array) {
return null;
}

return BitmapFactory.decodeByteArray(array, 0, array.length);
}

}

5.图像旋转,缩放,橡皮擦和染色功能如下:


package com.jiangjie.ps;

import com.jiangjie.utils.PaintConstants;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.FloatMath;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;

public class ImageTouchView extends ImageView{
public Matrix matrix = new Matrix();

Matrix savedMatrix = new Matrix();
/** 屏幕的分辨率*/
private DisplayMetrics dm;
/** 当前模式*/
int mode = PaintConstants.MODE.NONE;

/** 存储float类型的x,y值,就是你点下的坐标的X和Y*/
PointF prev = new PointF();
PointF curPosition = new PointF();
PointF mid = new PointF();
float dist = 1f;

float oldRotation = 0;
float oldDistX = 1f;
float oldDistY = 1f;

/**位图对象*/
private Bitmap bitmap = null;
private Paint paint;
private Context context;

private Path path;
private Path tempPath;
//定义一个内存中的图片,该图片将作为缓冲区
Bitmap cacheBitmap = null;

//定义cacheBitmap上的Canvas对象
Canvas cacheCanvas = null;
private Paint cachePaint = null;

private String TAG = "APP";

int x = 0;
int y = 0;

public ImageTouchView(Context context) {
super(context);
}

public ImageTouchView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
Log.i(TAG, "ImageTouchView(Context context, AttributeSet attrs)=>");

setupView();
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

if(mode == PaintConstants.MODE.COLORING){
canvas.drawPath(tempPath, paint);
}

}

public void setupView(){

//获取屏幕分辨率,需要根据分辨率来使用图片居中
dm = getContext().getResources().getDisplayMetrics();
//根据MyImageView来获取bitmap对象
BitmapDrawable bd = (BitmapDrawable)this.getDrawable();
if(bd != null){
bitmap = bd.getBitmap();
// bitmap = setBitmapAlpha(bitmap, 100);
center(true, true);
}
setCoverBitmap(bitmap);
this.setImageMatrix(matrix);

this.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Matrix matrixTemp = new Matrix();
matrixTemp.set(matrix);
//view的触摸坐标的转换
matrixTemp.invert(matrixTemp);
Log.i(TAG, "Touch screen.");

switch (event.getAction() & MotionEvent.ACTION_MASK) {
// 主点按下
case MotionEvent.ACTION_DOWN:
 savedMatrix.set(matrix);
 prev.set(event.getX(), event.getY());

float[] pointPrevInit = new float[]{prev.x, prev.y};
 matrixTemp.mapPoints(pointPrevInit);
 path.moveTo(pointPrevInit[0], pointPrevInit[1]);
 tempPath.moveTo(event.getX(), event.getY());

mode = PaintConstants.MODE.DRAG;
 Log.i(TAG, "ACTION_DOWN=>.");
 break;
 // 副点按下
case MotionEvent.ACTION_POINTER_DOWN:
 dist = spacing(event);
 oldRotation = rotation(event);
 oldDistX = spacingX(event);
 oldDistY = spacingY(event);
 // 如果连续两点距离大于10,则判定为多点模式
 if (spacing(event) > 10f) {
 savedMatrix.set(matrix);
 midPoint(mid, event);
 mode = PaintConstants.MODE.ZOOM;
 }
 break;
case MotionEvent.ACTION_UP:
 Log.i(TAG, "ACTION_UP=>.");
 if(mode == PaintConstants.MODE.COLORING){
 cachePaint.setColor(PaintConstants.PEN_COLOR);
 cachePaint.setStrokeWidth(PaintConstants.PEN_SIZE);
 cachePaint.setAlpha(PaintConstants.TRANSPARENT);
 cachePaint.setMaskFilter(new BlurMaskFilter(5, PaintConstants.BLUR_TYPE));

cacheCanvas.drawPath(path, cachePaint);
 path.reset();
 tempPath.reset();
 }
 break;

case MotionEvent.ACTION_POINTER_UP:
 mode = PaintConstants.MODE.NONE;
 break;

case MotionEvent.ACTION_MOVE:
 if(!PaintConstants.SELECTOR.KEEP_IMAGE){
 if (mode == PaintConstants.MODE.DRAG) {
 matrix.set(savedMatrix);
 matrix.postTranslate(event.getX() - prev.x, event.getY() - prev.y);
 } else if (mode == PaintConstants.MODE.ZOOM) {
 float rotation = (rotation(event) - oldRotation)/2;
 float newDistX = spacingX(event);
 float newDistY = spacingY(event);
 float scaleX = newDistX-oldDistX;
 float scaleY = newDistY-oldDistY;

float newDist = spacing(event);
 if (newDist > 10f) {
 matrix.set(savedMatrix);
 float tScale = newDist / dist;
 tScale = tScale>1?1+((tScale-1)/2):1-(1-tScale)/2;
 if(PaintConstants.SELECTOR.KEEP_SCALE){
  matrix.postScale(tScale, tScale, mid.x, mid.y);// 縮放
 }else{
  if(Math.abs(scaleX)>=Math.abs(scaleY)){
  matrix.postScale(tScale, 1, mid.x, mid.y);// 縮放
  }else{
  matrix.postScale(1, tScale, mid.x, mid.y);// 縮放
  }
 }
 if(PaintConstants.SELECTOR.HAIR_RURN)
  matrix.postRotate(rotation, mid.x, mid.y);// 旋轉
 }
 }
 }else{
 float[] pointPrev = new float[]{prev.x, prev.y};
 float[] pointStop= new float[]{event.getX(), event.getY()};

//view的触摸坐标的转换
 matrixTemp.mapPoints(pointPrev);
 matrixTemp.mapPoints(pointStop);

if(PaintConstants.SELECTOR.COLORING){
 //染色功能
 mode = PaintConstants.MODE.COLORING;
 paint.reset();
 paint = new Paint(Paint.DITHER_FLAG);
 paint.setColor(Color.RED);
 //设置画笔风格
 paint.setStyle(Paint.Style.STROKE);
 paint.setStrokeWidth(1);
 //反锯齿
 paint.setAntiAlias(true);
 paint.setDither(true);
 paint.setColor(PaintConstants.PEN_COLOR);
 paint.setStrokeWidth(PaintConstants.PEN_SIZE);

path.quadTo(pointPrev[0],pointPrev[1],pointStop[0],pointStop[1]);
 tempPath.quadTo(prev.x, prev.y,event.getX(), event.getY());

// 更新开始点的位置
 prev.set(event.getX(), event.getY());

ImageTouchView.this.setImageBitmap(cacheBitmap);

}else if(PaintConstants.SELECTOR.ERASE){
 //橡皮擦功能

mode = PaintConstants.MODE.ERASE;

paint.reset();
 paint.setColor(Color.TRANSPARENT);
 paint.setAntiAlias(false);
 paint.setStyle(Paint.Style.STROKE);
 paint.setStrokeWidth(16);
 paint.setStrokeJoin(Paint.Join.ROUND);
 paint.setStrokeCap(Paint.Cap.ROUND);
 paint.setAlpha(0);
 paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
 paint.setStrokeWidth(PaintConstants.ERASE_SIZE);

prev.set(event.getX(), event.getY());

cacheCanvas.drawLine(pointPrev[0],pointPrev[1],pointStop[0],pointStop[1], paint);
 ImageTouchView.this.setImageBitmap(cacheBitmap);
 }
 }
}
ImageTouchView.this.setImageMatrix(matrix);
invalidate();

return true;
}
});
}

/**
* 横向、纵向居中
*/
protected void center(boolean horizontal, boolean vertical) {
RectF rect = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight());

float height = rect.height();
float width = rect.width();

float deltaX = 0, deltaY = 0;

if (vertical) {
// 图片小于屏幕大小,则居中显示。大于屏幕,上方留空则往上移,下方留空则往下移
int screenHeight = dm.heightPixels;
if (height < screenHeight) {
deltaY = (screenHeight - height) / 2 - rect.top;
} else if (rect.top > 0) {
deltaY = -rect.top;
} else if (rect.bottom < screenHeight) {
deltaY = this.getHeight() - rect.bottom;
}
}

if (horizontal) {
int screenWidth = dm.widthPixels;
if (width < screenWidth) {
deltaX = (screenWidth - width) / 2 - rect.left;
} else if (rect.left > 0) {
deltaX = -rect.left;
} else if (rect.right < screenWidth) {
deltaX = screenWidth - rect.right;
}
}
matrix.postTranslate(deltaX, deltaY);
}

private float spacingX(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
return x;
}
private float spacingY(MotionEvent event) {
float y = event.getY(0) - event.getY(1);
return y;
}  
// 取旋转角度
private float rotation(MotionEvent event) {
double delta_x = (event.getX(0) - event.getX(1));
double delta_y = (event.getY(0) - event.getY(1));
double radians = Math.atan2(delta_y, delta_x);
return (float) Math.toDegrees(radians);
}

/**
* 两点的距离
*/
private float spacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return FloatMath.sqrt(x * x + y * y);
}

/**
* 两点的中点
*/
private void midPoint(PointF point, MotionEvent event) {
float x = event.getX(0) + event.getX(1);
float y = event.getY(0) + event.getY(1);
point.set(x / 2, y / 2);
}

/**
*
* @param bm
* @note set cover bitmap , which overlay on background.
*/
private void setCoverBitmap(Bitmap bitmap) {
// setting paint
paint = new Paint();

cacheBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888);
cacheCanvas = new Canvas();
cacheCanvas.setBitmap(cacheBitmap);
cacheCanvas.drawBitmap( bitmap, 0, 0, null);

path = new Path();
tempPath = new Path();

//设置画笔的颜色
cachePaint = new Paint();
//设置画笔风格
cachePaint.setStyle(Paint.Style.STROKE);
//反锯齿
cachePaint.setAntiAlias(true);
cachePaint.setStrokeJoin(Paint.Join.ROUND);
cachePaint.setStrokeCap(Paint.Cap.ROUND);
cachePaint.setXfermode(new PorterDuffXfermode(Mode.SRC_ATOP));
//设置画笔模糊效果
cachePaint.setMaskFilter(new BlurMaskFilter(5, PaintConstants.BLUR_TYPE));

}

}

来源:https://blog.csdn.net/guang09080908/article/details/41408077

标签:android,橡皮擦,染色
0
投稿

猜你喜欢

  • Android studio开发实现计算器功能

    2022-02-12 19:43:05
  • 【Redis缓存机制】详解Java连接Redis_Jedis_事务

    2023-05-23 19:59:55
  • Entity Framework代码优先(Code First)模式

    2022-07-28 05:31:05
  • Unity UI实现拖拽旋转

    2023-04-10 06:44:59
  • C#调用摄像头实现拍照功能的示例代码

    2023-02-14 16:21:41
  • Mac OS上安装Tomcat服务器的简单步骤

    2022-11-26 21:06:42
  • Android实战教程第五篇之一键锁屏应用

    2023-12-03 18:47:36
  • 详解C语言的mem系列函数

    2021-07-10 13:10:18
  • 浅析Spring 中 Bean 的理解与使用

    2023-07-09 03:12:03
  • sprng和struts有什么区别?

    2022-03-02 02:13:35
  • java 多态性详解及简单实例

    2021-10-06 23:29:50
  • Android Gradle依赖管理、去除重复依赖、忽略的方式

    2023-06-08 16:07:12
  • Java DatabaseMetaData用法案例详解

    2023-08-18 19:08:42
  • 详解Springboot自定义异常处理

    2021-11-04 17:15:12
  • C#异常捕获机制图文详解

    2023-05-22 19:39:20
  • Matlab实现获取文件夹下所有指定后缀的文件

    2021-06-17 23:57:49
  • C#异步编程Task的创建方式

    2023-07-23 06:22:43
  • c#代码生成URL地址的示例

    2022-02-17 09:44:08
  • 详谈jvm--Java中init和clinit的区别

    2022-01-10 10:35:22
  • Android设置theme中可能遇到的坑

    2021-08-05 06:20:26
  • asp之家 软件编程 m.aspxhome.com