Android绘图(二)

(《群英传》)整理笔记:

图像处理之色彩特效处理:
(这一块不太懂,先记录下一个例子)

package com.android.utils;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;

/**
 * 设置图像矩阵的代码
 * @author Administrator
 *
 */
public class ImageHelper {
    public static Bitmap handleImageEffect(Bitmap bm, float hue, float saturation, float lum) {
        Bitmap bmp = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bmp);
        Paint paint = new Paint();

        ColorMatrix hueMatrix = new ColorMatrix();
        hueMatrix.setRotate(0, hue);
        hueMatrix.setRotate(1, hue);
        hueMatrix.setRotate(2, hue);

        ColorMatrix saturationMatrix = new ColorMatrix();
        saturationMatrix.setSaturation(saturation);

        ColorMatrix lumMatrix = new ColorMatrix();
        lumMatrix.setScale(lum, lum, lum, 1);

        ColorMatrix imageMatrix = new ColorMatrix();
        imageMatrix.postConcat(hueMatrix);
        imageMatrix.postConcat(saturationMatrix);
        imageMatrix.postConcat(lumMatrix);

        paint.setColorFilter(new ColorMatrixColorFilter(imageMatrix));
        canvas.drawBitmap(bm, 0, 0, paint);
        return bmp;
    }

    public static Bitmap handleImageNegative(Bitmap bm) {
        int width = bm.getWidth();
        int height = bm.getHeight();
        int color;
        int r, g, b, a;

        Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);

        int[] oldPx = new int[width * height];
        int[] newPx = new int[width * height];
        bm.getPixels(oldPx, 0, width, 0, 0, width, height);

        for (int i = 0; i < width * height; i++) {
            color = oldPx[i];
            r = Color.red(color);
            g = Color.green(color);
            b = Color.blue(color);
            a = Color.alpha(color);

            r = 255 - r;
            g = 255 - g;
            b = 255 - b;

            if (r > 255) {
                r = 255;
            } else if (r < 0) {
                r = 0;
            }
            if (g > 255) {
                g = 255;
            } else if (g < 0) {
                g = 0;
            }
            if (b > 255) {
                b = 255;
            } else if (b < 0) {
                b = 0;
            }
            newPx[i] = Color.argb(a, r, g, b);
        }
        bmp.setPixels(newPx, 0, width, 0, 0, width, height);
        return bmp;
    }

    public static Bitmap handleImagePixelsOldPhoto(Bitmap bm) {
        Bitmap bmp = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), Bitmap.Config.ARGB_8888);
        int width = bm.getWidth();
        int height = bm.getHeight();
        int color = 0;
        int r, g, b, a, r1, g1, b1;

        int[] oldPx = new int[width * height];
        int[] newPx = new int[width * height];

        bm.getPixels(oldPx, 0, bm.getWidth(), 0, 0, width, height);
        for (int i = 0; i < width * height; i++) {
            color = oldPx[i];
            a = Color.alpha(color);
            r = Color.red(color);
            g = Color.green(color);
            b = Color.blue(color);

            r1 = (int) (0.393 * r + 0.769 * g + 0.189 * b);
            g1 = (int) (0.349 * r + 0.686 * g + 0.168 * b);
            b1 = (int) (0.272 * r + 0.534 * g + 0.131 * b);

            if (r1 > 255) {
                r1 = 255;
            }
            if (g1 > 255) {
                g1 = 255;
            }
            if (b1 > 255) {
                b1 = 255;
            }

            newPx[i] = Color.argb(a, r1, g1, b1);
        }
        bmp.setPixels(newPx, 0, width, 0, 0, width, height);
        return bmp;
    }

    public static Bitmap handleImagePixelsRelief(Bitmap bm) {
        Bitmap bmp = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), Bitmap.Config.ARGB_8888);
        int width = bm.getWidth();
        int height = bm.getHeight();
        int color = 0, colorBefore = 0;
        int a, r, g, b;
        int r1, g1, b1;

        int[] oldPx = new int[width * height];
        int[] newPx = new int[width * height];

        bm.getPixels(oldPx, 0, bm.getWidth(), 0, 0, width, height);
        for (int i = 1; i < width * height; i++) {
            colorBefore = oldPx[i - 1];
            a = Color.alpha(colorBefore);
            r = Color.red(colorBefore);
            g = Color.green(colorBefore);
            b = Color.blue(colorBefore);

            color = oldPx[i];
            r1 = Color.red(color);
            g1 = Color.green(color);
            b1 = Color.blue(color);

            r = (r - r1 + 127);
            g = (g - g1 + 127);
            b = (b - b1 + 127);
            if (r > 255) {
                r = 255;
            }
            if (g > 255) {
                g = 255;
            }
            if (b > 255) {
                b = 255;
            }
            newPx[i] = Color.argb(a, r, g, b);
        }
        bmp.setPixels(newPx, 0, width, 0, 0, width, height);
        return bmp;
    }
}
import com.android.utils.ImageHelper;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;

/**
 * 色调、饱和度、亮度调整图片
 * @author Administrator
 *
 */
public class MyView3 extends Activity implements OnSeekBarChangeListener{

     private static int MAX_VALUE = 255;
        private static int MID_VALUE = 127;
        private ImageView mImageView;
        private SeekBar mSeekbarhue, mSeekbarSaturation, mSeekbarLum;
        private float mHue, mStauration, mLum;
        private Bitmap bitmap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.my_view3);
         bitmap = BitmapFactory.decodeResource(getResources(),
                    R.drawable.qie);
            mImageView = (ImageView) findViewById(R.id.imageview);
            mSeekbarhue = (SeekBar) findViewById(R.id.seekbarHue);
            mSeekbarSaturation = (SeekBar) findViewById(R.id.seekbarSaturation);
            mSeekbarLum = (SeekBar) findViewById(R.id.seekbatLum);

            mSeekbarhue.setOnSeekBarChangeListener(this);
            mSeekbarSaturation.setOnSeekBarChangeListener(this);
            mSeekbarLum.setOnSeekBarChangeListener(this);

            mSeekbarhue.setMax(MAX_VALUE);
            mSeekbarSaturation.setMax(MAX_VALUE);
            mSeekbarLum.setMax(MAX_VALUE);

            mSeekbarhue.setProgress(MID_VALUE);
            mSeekbarSaturation.setProgress(MID_VALUE);
            mSeekbarLum.setProgress(MID_VALUE);

            mImageView.setImageBitmap(bitmap);
    }

    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        switch (seekBar.getId()) {
        case R.id.seekbarHue:
            mHue = (progress - MID_VALUE) * 1.0F / MID_VALUE * 180;
            break;
        case R.id.seekbarSaturation:
            mStauration = progress * 1.0F / MID_VALUE;
            break;
        case R.id.seekbatLum:
            mLum = progress * 1.0F / MID_VALUE;
            break;
    }
    mImageView.setImageBitmap(ImageHelper.handleImageEffect(
            bitmap, mHue, mStauration, mLum));
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
    }

}

程序运行后可拖动三个属性条实时改变图像。

(留待以后学习)
【色彩矩阵、颜色矩阵ColorMatrix、常用图像颜色矩阵处理效果(灰度、图像反转、怀旧、去色、高饱和度)、像素点、常用像素点处理效果(底片、老照片、浮雕)】

图像处理之图形特效处理

图像的变形处理包含四类基本变换
Translate:平移变换
Rotate:旋转变换
Scale:缩放变换
Skew:错切变换

像素块分析
drawBitmapMeshh():将图像分成了一个个小块,然后通过改变每一个图像块来修改整个图像。基本上可以实现所有的图像特效。
canvas.drawBitmapMesh(bitmap, meshWidth, meshHeight, verts, vertOffset, colors, colorOffset, paint);
Bitmap:将要扭曲的图像;meshWidth:需要的横向网格数目;meshHeight:需要的纵向网格数目;verts:网格交叉点坐标数组;vertOffset:verts数组中开始跳过的(x, y)坐标对的数目。

示例:旗帜飞扬
核心思想:让图片中每个交织点的横坐标较之前坐标不断变化

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;

public class DrawBitmapMesh extends View {

    private final int WIDTH = 200;
    private final int HEIGHT = 200;
    private int COUNT = (WIDTH + 1) * (HEIGHT + 1);
    private float[] verts = new float[COUNT * 2];
    private float[] orig = new float[COUNT * 2];
    private Bitmap bitmap;
    private float A;
    private float k = 1;

    public DrawBitmapMesh(Context context) {
        super(context);
        initView(context);
    }

    public DrawBitmapMesh(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView(context);
    }

    public DrawBitmapMesh(Context context, AttributeSet attrs,
                              int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context);
    }

    private void initView(Context context) {
        setFocusable(true);
        // 第一步:获取交叉点的坐标,保存在orig数组中
        bitmap = BitmapFactory.decodeResource(context.getResources(),
                R.drawable.test);
        float bitmapWidth = bitmap.getWidth();
        float bitmapHeight = bitmap.getHeight();
        // 通过循环遍历所有的交叉线,并按比例获取其坐标
        int index = 0;
        for (int y = 0; y <= HEIGHT; y++) {
            float fy = bitmapHeight * y / HEIGHT;
            for (int x = 0; x <= WIDTH; x++) {
                float fx = bitmapWidth * x / WIDTH;
                orig[index * 2 + 0] = verts[index * 2 + 0] = fx;
                // 为了避免扭曲后被屏幕遮挡,人为将坐标+100为了让图像下移
                orig[index * 2 + 1] = verts[index * 2 + 1] = fy + 100;
                index += 1;
            }
        }
        A = 50;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        flagWave();
        // 第四步:将处理后的图像绘制出来。每次绘制的时候通过改变相位来改变偏移量,从而造成动态的效果
        k += 0.1F;
        canvas.drawBitmapMesh(bitmap, WIDTH, HEIGHT,
                verts, 0, null, 0, null);
        invalidate();
    }

    // 第二步:改变交叉点的纵坐标的值,使用sin x 来改变交叉点纵坐标的值,保存到vert数组中
    private void flagWave() {
        for (int j = 0; j <= HEIGHT; j++) {
            for (int i = 0; i <= WIDTH; i++) {
                verts[(j * (WIDTH + 1) + i) * 2 + 0] += 0;
                // 第三步:让图像动起来。利用函数的周期性
                float offsetY =
                        (float) Math.sin((float) i / WIDTH * 2 * Math.PI +
                                Math.PI * k);
                verts[(j * (WIDTH + 1) + i) * 2 + 1] =
                        orig[(j * WIDTH + i) * 2 + 1] + offsetY * A;
            }
        }
    }
}

Android图像处理值画笔特效处理:

各种各样的画笔:记号笔、毛笔、蜡笔等。
1、PorterDuffXfermode:
PorterDuffXfermode设置的是两个图层交际区域的显示方式,dst是先画的图形,而src是后画的图形,通过控制遮罩层的图形,来控制下面被这招图形的显示效果,最常用的就是通过DST_IN、SRC_IN模式来实现将一个矩形图片变成圆角图片或者圆形图片的效果。

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

/**
 * 刮刮卡效果
 * 
 * @author Administrator
 *
 */
public class ProterDuffXfermode extends View {

    private Bitmap mBgBitmap, mFgBitmap;
    private Paint mPaint;
    private Canvas mCanvas;
    private Path mPath;

    public ProterDuffXfermode(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    /**
     * 第一步:做一些初始化工作。例如准备好图片,设置好Paint的一些属性
     */
    private void init() {

        mPaint = new Paint();
        // 将画笔的透明度置为0,这样才能显示出擦除的效果
        mPaint.setAlpha(0);
        // 第三步:使用DST_IN模式
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        mPaint.setStyle(Paint.Style.STROKE);
        // 让笔触和连接处更加圆滑一点Paint.Join.ROUND,Paint.Cap.ROUND
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        mPaint.setStrokeWidth(60);
        mPaint.setStrokeCap(Paint.Cap.ROUND);

        mPath = new Path();
        mBgBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.qie);
        mFgBitmap = Bitmap.createBitmap(mBgBitmap.getWidth(), mBgBitmap.getHeight(), Bitmap.Config.ARGB_8888);

        mCanvas = new Canvas(mFgBitmap);
        mCanvas.drawColor(Color.GRAY);

    }

    // 第二步:获取用户手指滑动所产生的路径,使用Path保存用户手指划过的路径(贝塞尔曲线会有更好的显示效果)
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            mPath.reset();
            mPath.moveTo(event.getX(), event.getY());
            break;
        case MotionEvent.ACTION_MOVE:
            mPath.lineTo(event.getX(), event.getY());
            break;
        }
        mCanvas.drawPath(mPath, mPaint);
        invalidate();
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // 第四步:将路径绘制到前面覆盖的图层上面即可
        canvas.drawBitmap(mBgBitmap, 0, 0, null);
        canvas.drawBitmap(mFgBitmap, 0, 0, null);
    }

}

2、Shader
着色器、渲染器。用来实现一系列的渐变、渲染效果。
Android中的Shader包括以下几种:
(1)BitmapShader位图:产生的是一个图像,作用是通过Paint对画布进行指定Bitmap的填充。填充的模式:CLAMP拉伸;REPEAT重复;MIRROR镜像。
(2)LinearGradient线性;
(3)RadialGradient光束;
(4)SweepGradient梯度;
(5)ComposeShader混合。

// 用一张图片创建了一支具有图像填充功能的画笔,并使用这支画笔绘制了一个圆形。
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
        mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
        mPaint = new Paint();
        mPaint.setShader(mBitmapShader);
        canvas.drawCircle(250, 250, 200, mPaint);
// LinearGradient的使用:只需要指定渐变起始的颜色就可以了。
        mPaint = new Paint();
        mPaint.setShader(new LinearGradient(0, 0, 200, 200, Color.BLUE, Color.YELLOW, Shader.TileMode.REPEAT));
        canvas.drawRect(0,0,800,800,mPaint);

但是这些渐变效果不会直接使用在程序里,通常情况下把这种渐变效果作为一个遮罩层来使用。

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;

public class DaoYing extends View {
    private Bitmap mSrcBitmap, mRefBitmap;
    private Paint mPaint;
    private PorterDuffXfermode mXfermode;

    public DaoYing(Context context) {
        super(context);
        initRes(context);
    }

    public DaoYing(Context context, AttributeSet attrs) {
        super(context, attrs);
        initRes(context);
    }

    public DaoYing(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initRes(context);
    }

    /**
     * 第一步:把原图复制一份并进行翻转
     * 
     * @param context
     */
    private void initRes(Context context) {
        mSrcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.qie);
        Matrix matrix = new Matrix();
        // 使用matrix.setScale(1F, -1F)方法来实现图片的垂直翻转。水平翻转同理
        matrix.setScale(1F, -1F);
        mRefBitmap = Bitmap.createBitmap(mSrcBitmap, 0, 0, mSrcBitmap.getWidth(), mSrcBitmap.getHeight(), matrix, true);

        mPaint = new Paint();
        mPaint.setShader(new LinearGradient(0, mSrcBitmap.getHeight(), 0,
                mSrcBitmap.getHeight() + mSrcBitmap.getHeight() / 4, 0XDD000000, 0X10000000, Shader.TileMode.CLAMP));
        mXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // 第二步:绘制两张图片即原图和倒影图
        canvas.drawColor(Color.BLACK);
        canvas.drawBitmap(mSrcBitmap, 0, 0, null);
        canvas.drawBitmap(mRefBitmap, 0, mSrcBitmap.getHeight(), null);
        mPaint.setXfermode(mXfermode);
        // 第三步:绘制渐变效果矩形,并通过Mode.DST_IN模式绘制到倒影图上,形成一个具有过度效果的渐变层
        canvas.drawRect(0, mSrcBitmap.getHeight(), mRefBitmap.getWidth(), mSrcBitmap.getHeight() * 2, mPaint);
        mPaint.setXfermode(null);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值