content: /**
   * Copyright 2014 Zhenguo Jin
   * <p/>
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   * <p/>
   * http://www.apache.org/licenses/LICENSE-2.0
   * <p/>
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package com.rsen.github.androidutils;
  
  import android.annotation.SuppressLint;
  import android.annotation.TargetApi;
  import android.content.Context;
  import android.content.res.Resources;
  import android.graphics.Bitmap;
  import android.graphics.Bitmap.Config;
  import android.graphics.BitmapFactory;
  import android.graphics.Canvas;
  import android.graphics.Color;
  import android.graphics.ColorMatrix;
  import android.graphics.ColorMatrixColorFilter;
  import android.graphics.LinearGradient;
  import android.graphics.Matrix;
  import android.graphics.Paint;
  import android.graphics.PaintFlagsDrawFilter;
  import android.graphics.PixelFormat;
  import android.graphics.PorterDuff;
  import android.graphics.PorterDuff.Mode;
  import android.graphics.PorterDuffXfermode;
  import android.graphics.Rect;
  import android.graphics.RectF;
  import android.graphics.Shader.TileMode;
  import android.graphics.drawable.Drawable;
  import android.media.ExifInterface;
  import android.os.Build;
  import android.renderscript.Allocation;
  import android.renderscript.Element;
  import android.renderscript.RenderScript;
  import android.renderscript.ScriptIntrinsicBlur;
  import android.util.Log;
  import android.view.View;
  
  import java.io.ByteArrayInputStream;
  import java.io.ByteArrayOutputStream;
  import java.io.IOException;
  import java.io.InputStream;
  
  /**
   * Bitmap工具类主要包括获取Bitmap和对Bitmap的操作
   *
   * @author jingle1267@163.com
   */
  public final class BitmapUtil {
  
      private static final boolean DEBUG = false;
      private static final String TAG = BitmapUtil.class.getSimpleName();
  
      /**
       * Don't let anyone instantiate this class.
       */
      private BitmapUtil() {
          throw new Error("Do not need instantiate!");
      }
  
      /**
       * 图片压缩处理（使用Options的方法）
       * <p/>
       * <br>
       * <b>说明</b> 使用方法：
       * 首先你要将Options的inJustDecodeBounds属性设置为true，BitmapFactory.decode一次图片 。
       * 然后将Options连同期望的宽度和高度一起传递到到本方法中。
       * 之后再使用本方法的返回值做参数调用BitmapFactory.decode创建图片。
       * <p/>
       * <br>
       * <b>说明</b> BitmapFactory创建bitmap会尝试为已经构建的bitmap分配内存
       * ，这时就会很容易导致OOM出现。为此每一种创建方法都提供了一个可选的Options参数
       * ，将这个参数的inJustDecodeBounds属性设置为true就可以让解析方法禁止为bitmap分配内存
       * ，返回值也不再是一个Bitmap对象， 而是null。虽然Bitmap是null了，但是Options的outWidth、
       * outHeight和outMimeType属性都会被赋值。
       *
       * @param reqWidth  目标宽度,这里的宽高只是阀值，实际显示的图片将小于等于这个值
       * @param reqHeight 目标高度,这里的宽高只是阀值，实际显示的图片将小于等于这个值
       */
      public static BitmapFactory.Options calculateInSampleSize(
              final BitmapFactory.Options options, final int reqWidth,
              final int reqHeight) {
          // 源图片的高度和宽度
          final int height = options.outHeight;
          final int width = options.outWidth;
          int inSampleSize = 1;
          if (height > 400 || width > 450) {
              if (height > reqHeight || width > reqWidth) {
                  // 计算出实际宽高和目标宽高的比率
                  final int heightRatio = Math.round((float) height
                          / (float) reqHeight);
                  final int widthRatio = Math.round((float) width
                          / (float) reqWidth);
                  // 选择宽和高中最小的比率作为inSampleSize的值，这样可以保证最终图片的宽和高
                  // 一定都会大于等于目标的宽和高。
                  inSampleSize = heightRatio < widthRatio ? heightRatio
                          : widthRatio;
              }
          }
          // 设置压缩比例
          options.inSampleSize = inSampleSize;
          options.inJustDecodeBounds = false;
          return options;
      }
  
      /**
       * 获取一个指定大小的bitmap
       *
       * @param res       Resources
       * @param resId     图片ID
       * @param reqWidth  目标宽度
       * @param reqHeight 目标高度
       */
      @TargetApi(Build.VERSION_CODES.DONUT)
      public static Bitmap getBitmapFromResource(Resources res, int resId,
                                                 int reqWidth, int reqHeight) {
          // BitmapFactory.Options options = new BitmapFactory.Options();
          // options.inJustDecodeBounds = true;
          // BitmapFactory.decodeResource(res, resId, options);
          // options = BitmapHelper.calculateInSampleSize(options, reqWidth,
          // reqHeight);
          // return BitmapFactory.decodeResource(res, resId, options);
  
          // 通过JNI的形式读取本地图片达到节省内存的目的
          BitmapFactory.Options options = new BitmapFactory.Options();
          options.inPreferredConfig = Config.RGB_565;
          options.inPurgeable = true;
          options.inInputShareable = true;
          InputStream is = res.openRawResource(resId);
          return getBitmapFromStream(is, null, reqWidth, reqHeight);
      }
  
      /**
       * 获取一个指定大小的bitmap
       *
       * @param reqWidth  目标宽度
       * @param reqHeight 目标高度
       */
      public static Bitmap getBitmapFromFile(String pathName, int reqWidth,
                                             int reqHeight) {
          BitmapFactory.Options options = new BitmapFactory.Options();
          options.inJustDecodeBounds = true;
          BitmapFactory.decodeFile(pathName, options);
          options = calculateInSampleSize(options, reqWidth, reqHeight);
          return BitmapFactory.decodeFile(pathName, options);
      }
  
      /**
       * 获取一个指定大小的bitmap
       *
       * @param data      Bitmap的byte数组
       * @param offset    image从byte数组创建的起始位置
       * @param length    the number of bytes, 从offset处开始的长度
       * @param reqWidth  目标宽度
       * @param reqHeight 目标高度
       */
      public static Bitmap getBitmapFromByteArray(byte[] data, int offset,
                                                  int length, int reqWidth, int reqHeight) {
          BitmapFactory.Options options = new BitmapFactory.Options();
          options.inJustDecodeBounds = true;
          BitmapFactory.decodeByteArray(data, offset, length, options);
          options = calculateInSampleSize(options, reqWidth, reqHeight);
          return BitmapFactory.decodeByteArray(data, offset, length, options);
      }
  
      /**
       * 把bitmap转化为bytes
       *
       * @param bitmap 源Bitmap
       * @return Byte数组
       */
      public static byte[] getBytesFromBitmap(Bitmap bitmap) {
          ByteArrayOutputStream baos = new ByteArrayOutputStream();
          bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
          return baos.toByteArray();
      }
  
      /**
       * Stream转换成Byte
       *
       * @param inputStream InputStream
       * @return Byte数组
       */
      public static byte[] getBytesFromStream(InputStream inputStream) {
          ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
          byte[] buffer = new byte[1024];
          int len;
          try {
              while ((len = inputStream.read(buffer)) >= 0) {
                  os.write(buffer, 0, len);
              }
          } catch (IOException e) {
              e.printStackTrace();
          }
          return os.toByteArray();
      }
  
      /**
       * 获取一个指定大小的bitmap
       *
       * @param b Byte数组
       * @return 需要的Bitmap
       */
      public static Bitmap getBitmapFromBytes(byte[] b) {
          if (b.length != 0) {
              return BitmapFactory.decodeByteArray(b, 0, b.length);
          } else {
              return null;
          }
      }
  
      /**
       * 获取一个指定大小的bitmap<br>
       * 实际调用的方法是bitmapFromByteArray(data, 0, data.length, w, h);
       *
       * @param is        从输入流中读取Bitmap
       * @param reqWidth  目标宽度
       * @param reqHeight 目标高度
       */
      public static Bitmap getBitmapFromStream(InputStream is, int reqWidth,
                                               int reqHeight) {
          byte[] data = FileUtils.input2byte(is);
          return getBitmapFromByteArray(data, 0, data.length, reqWidth, reqHeight);
      }
  
      /**
       * 获取一个指定大小的bitmap
       *
       * @param is         从输入流中读取Bitmap
       * @param outPadding If not null, return the padding rect for the bitmap if it
       *                   exists, otherwise set padding to [-1,-1,-1,-1]. If no bitmap
       *                   is returned (null) then padding is unchanged.
       * @param reqWidth   目标宽度
       * @param reqHeight  目标高度
       */
      public static Bitmap getBitmapFromStream(InputStream is, Rect outPadding,
                                               int reqWidth, int reqHeight) {
          BitmapFactory.Options options = new BitmapFactory.Options();
          options.inJustDecodeBounds = true;
          BitmapFactory.decodeStream(is, outPadding, options);
          options = calculateInSampleSize(options, reqWidth, reqHeight);
          return BitmapFactory.decodeStream(is, outPadding, options);
      }
  
      /**
       * 从View获取Bitmap
       *
       * @param view View
       * @return Bitmap
       */
      public static Bitmap getBitmapFromView(View view) {
          Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),
                  Config.ARGB_8888);
          Canvas canvas = new Canvas(bitmap);
  
          view.layout(view.getLeft(), view.getTop(), view.getRight(),
                  view.getBottom());
          view.draw(canvas);
  
          return bitmap;
      }
  
      /**
       * 把一个View的对象转换成bitmap
       *
       * @param view View
       * @return Bitmap
       */
      public static Bitmap getBitmapFromView2(View view) {
  
          view.clearFocus();
          view.setPressed(false);
  
          // 能画缓存就返回false
          boolean willNotCache = view.willNotCacheDrawing();
          view.setWillNotCacheDrawing(false);
          int color = view.getDrawingCacheBackgroundColor();
          view.setDrawingCacheBackgroundColor(0);
          if (color != 0) {
              view.destroyDrawingCache();
          }
          view.buildDrawingCache();
          Bitmap cacheBitmap = view.getDrawingCache();
          if (cacheBitmap == null) {
              if (DEBUG) {
                  Log.e(TAG, "failed getViewBitmap(" + view + ")",
                          new RuntimeException());
              }
              return null;
          }
          Bitmap bitmap = Bitmap.createBitmap(cacheBitmap);
          // Restore the view
          view.destroyDrawingCache();
          view.setWillNotCacheDrawing(willNotCache);
          view.setDrawingCacheBackgroundColor(color);
          return bitmap;
      }
  
      /**
       * 将Drawable转化为Bitmap
       *
       * @param drawable Drawable
       * @return Bitmap
       */
      public static Bitmap getBitmapFromDrawable(Drawable drawable) {
          int width = drawable.getIntrinsicWidth();
          int height = drawable.getIntrinsicHeight();
          Bitmap bitmap = Bitmap.createBitmap(width, height, drawable
                  .getOpacity() != PixelFormat.OPAQUE ? Config.ARGB_8888
                  : Config.RGB_565);
          Canvas canvas = new Canvas(bitmap);
          drawable.setBounds(0, 0, width, height);
          drawable.draw(canvas);
          return bitmap;
  
      }
  
      /**
       * 合并Bitmap
       * @param bgd 背景Bitmap
       * @param fg 前景Bitmap
       * @return 合成后的Bitmap
       */
      public static Bitmap combineImages(Bitmap bgd, Bitmap fg) {
          Bitmap bmp;
  
          int width = bgd.getWidth() > fg.getWidth() ? bgd.getWidth() : fg
                  .getWidth();
          int height = bgd.getHeight() > fg.getHeight() ? bgd.getHeight() : fg
                  .getHeight();
  
          bmp = Bitmap.createBitmap(width, height, Config.ARGB_8888);
          Paint paint = new Paint();
          paint.setXfermode(new PorterDuffXfermode(Mode.SRC_ATOP));
  
          Canvas canvas = new Canvas(bmp);
          canvas.drawBitmap(bgd, 0, 0, null);
          canvas.drawBitmap(fg, 0, 0, paint);
  
          return bmp;
      }
  
      /**
       * 合并
       * @param bgd 后景Bitmap
       * @param fg 前景Bitmap
       * @return 合成后Bitmap
       */
      public static Bitmap combineImagesToSameSize(Bitmap bgd, Bitmap fg) {
          Bitmap bmp;
  
          int width = bgd.getWidth() < fg.getWidth() ? bgd.getWidth() : fg
                  .getWidth();
          int height = bgd.getHeight() < fg.getHeight() ? bgd.getHeight() : fg
                  .getHeight();
  
          if (fg.getWidth() != width && fg.getHeight() != height) {
              fg = zoom(fg, width, height);
          }
          if (bgd.getWidth() != width && bgd.getHeight() != height) {
              bgd = zoom(bgd, width, height);
          }
  
          bmp = Bitmap.createBitmap(width, height, Config.ARGB_8888);
          Paint paint = new Paint();
          paint.setXfermode(new PorterDuffXfermode(Mode.SRC_ATOP));
  
          Canvas canvas = new Canvas(bmp);
          canvas.drawBitmap(bgd, 0, 0, null);
          canvas.drawBitmap(fg, 0, 0, paint);
  
          return bmp;
      }
  
      /**
       * 放大缩小图片
       *
       * @param bitmap 源Bitmap
       * @param w 宽
       * @param h 高
       * @return 目标Bitmap
       */
      public static Bitmap zoom(Bitmap bitmap, int w, int h) {
          int width = bitmap.getWidth();
          int height = bitmap.getHeight();
          Matrix matrix = new Matrix();
          float scaleWidht = ((float) w / width);
          float scaleHeight = ((float) h / height);
          matrix.postScale(scaleWidht, scaleHeight);
          Bitmap newbmp = Bitmap.createBitmap(bitmap, 0, 0, width, height,
                  matrix, true);
          return newbmp;
      }
  
      /**
       * 获得圆角图片的方法
       *
       * @param bitmap 源Bitmap
       * @param roundPx 圆角大小
       * @return 期望Bitmap
       */
      public static Bitmap getRoundedCornerBitmap(Bitmap bitmap, float roundPx) {
  
          Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
                  bitmap.getHeight(), Config.ARGB_8888);
          Canvas canvas = new Canvas(output);
  
          final int color = 0xff424242;
          final Paint paint = new Paint();
          final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
          final RectF rectF = new RectF(rect);
  
          paint.setAntiAlias(true);
          canvas.drawARGB(0, 0, 0, 0);
          paint.setColor(color);
          canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
  
          paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
          canvas.drawBitmap(bitmap, rect, rect, paint);
  
          return output;
      }
  
      /**
       * 获得带倒影的图片方法
       *
       * @param bitmap 源Bitmap
       * @return 带倒影的Bitmap
       */
      public static Bitmap createReflectionBitmap(Bitmap bitmap) {
          final int reflectionGap = 4;
          int width = bitmap.getWidth();
          int height = bitmap.getHeight();
  
          Matrix matrix = new Matrix();
          matrix.preScale(1, -1);
  
          Bitmap reflectionImage = Bitmap.createBitmap(bitmap, 0, height / 2,
                  width, height / 2, matrix, false);
  
          Bitmap bitmapWithReflection = Bitmap.createBitmap(width,
                  (height + height / 2), Config.ARGB_8888);
  
          Canvas canvas = new Canvas(bitmapWithReflection);
          canvas.drawBitmap(bitmap, 0, 0, null);
          Paint deafalutPaint = new Paint();
          canvas.drawRect(0, height, width, height + reflectionGap, deafalutPaint);
  
          canvas.drawBitmap(reflectionImage, 0, height + reflectionGap, null);
  
          Paint paint = new Paint();
          LinearGradient shader = new LinearGradient(0, bitmap.getHeight(), 0,
                  bitmapWithReflection.getHeight() + reflectionGap, 0x70ffffff,
                  0x00ffffff, TileMode.CLAMP);
          paint.setShader(shader);
          // Set the Transfer mode to be porter duff and destination in
          paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
          // Draw a rectangle using the paint with our linear gradient
          canvas.drawRect(0, height, width, bitmapWithReflection.getHeight()
                  + reflectionGap, paint);
  
          return bitmapWithReflection;
      }
  
      /**
       * 压缩图片大小
       *
       * @param image 源Bitmap
       * @return 压缩后的Bitmap
       */
      public static Bitmap compressImage(Bitmap image) {
  
          ByteArrayOutputStream baos = new ByteArrayOutputStream();
          image.compress(Bitmap.CompressFormat.JPEG, 100, baos);// 质量压缩方法，这里100表示不压缩，把压缩后的数据存放到baos中
          int options = 100;
          while (baos.toByteArray().length / 1024 > 100) { // 循环判断如果压缩后图片是否大于100kb,大于继续压缩
              baos.reset();// 重置baos即清空baos
              image.compress(Bitmap.CompressFormat.JPEG, options, baos);// 这里压缩options%，把压缩后的数据存放到baos中
              options -= 10;// 每次都减少10
          }
          ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());// 把压缩后的数据baos存放到ByteArrayInputStream中
          Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);// 把ByteArrayInputStream数据生成图片
          return bitmap;
      }
  
      /**
       * 将彩色图转换为灰度图
       *
       * @param img 源Bitmap
       * @return 返回转换好的位图
       */
      public static Bitmap convertGreyImg(Bitmap img) {
          int width = img.getWidth(); // 获取位图的宽
          int height = img.getHeight(); // 获取位图的高
  
          int[] pixels = new int[width * height]; // 通过位图的大小创建像素点数组
  
          img.getPixels(pixels, 0, width, 0, 0, width, height);
          int alpha = 0xFF << 24;
          for (int i = 0; i < height; i++) {
              for (int j = 0; j < width; j++) {
                  int grey = pixels[width * i + j];
  
                  int red = ((grey & 0x00FF0000) >> 16);
                  int green = ((grey & 0x0000FF00) >> 8);
                  int blue = (grey & 0x000000FF);
  
                  grey = (int) ((float) red * 0.3 + (float) green * 0.59 + (float) blue * 0.11);
                  grey = alpha | (grey << 16) | (grey << 8) | grey;
                  pixels[width * i + j] = grey;
              }
          }
          Bitmap result = Bitmap.createBitmap(width, height, Config.RGB_565);
          result.setPixels(pixels, 0, width, 0, 0, width, height);
          return result;
      }
  
      /**
       * 转换图片成圆形
       *
       * @param bitmap 传入Bitmap对象
       * @return 圆形Bitmap
       */
      public static Bitmap getRoundBitmap(Bitmap bitmap) {
          int width = bitmap.getWidth();
          int height = bitmap.getHeight();
          float roundPx;
          float left, top, right, bottom, dst_left, dst_top, dst_right, dst_bottom;
          if (width <= height) {
              roundPx = width / 2;
              top = 0;
              bottom = width;
              left = 0;
              right = width;
              height = width;
              dst_left = 0;
              dst_top = 0;
              dst_right = width;
              dst_bottom = width;
          } else {
              roundPx = height / 2;
              float clip = (width - height) / 2;
              left = clip;
              right = width - clip;
              top = 0;
              bottom = height;
              width = height;
              dst_left = 0;
              dst_top = 0;
              dst_right = height;
              dst_bottom = height;
          }
  
          Bitmap output = Bitmap.createBitmap(width, height, Config.ARGB_8888);
          Canvas canvas = new Canvas(output);
  
          final int color = 0xff424242;
          final Paint paint = new Paint();
          final Rect src = new Rect((int) left, (int) top, (int) right,
                  (int) bottom);
          final Rect dst = new Rect((int) dst_left, (int) dst_top,
                  (int) dst_right, (int) dst_bottom);
          final RectF rectF = new RectF(dst);
  
          paint.setAntiAlias(true);
  
          canvas.drawARGB(0, 0, 0, 0);
          paint.setColor(color);
          canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
  
          paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
          canvas.drawBitmap(bitmap, src, dst, paint);
          return output;
      }
  
      /**
       * Returns a Bitmap representing the thumbnail of the specified Bitmap. The
       * size of the thumbnail is defined by the dimension
       * android.R.dimen.launcher_application_icon_size.
       * <p/>
       * This method is not thread-safe and should be invoked on the UI thread
       * only.
       *
       * @param bitmap  The bitmap to get a thumbnail of.
       * @param context The application's context.
       * @return A thumbnail for the specified bitmap or the bitmap itself if the
       * thumbnail could not be created.
       */
      public static Bitmap createThumbnailBitmap(Bitmap bitmap, Context context) {
          int sIconWidth = -1;
          int sIconHeight = -1;
          final Resources resources = context.getResources();
          sIconWidth = sIconHeight = (int) resources
                  .getDimension(android.R.dimen.app_icon_size);
  
          final Paint sPaint = new Paint();
          final Rect sBounds = new Rect();
          final Rect sOldBounds = new Rect();
          Canvas sCanvas = new Canvas();
  
          int width = sIconWidth;
          int height = sIconHeight;
  
          sCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG,
                  Paint.FILTER_BITMAP_FLAG));
  
          final int bitmapWidth = bitmap.getWidth();
          final int bitmapHeight = bitmap.getHeight();
  
          if (width > 0 && height > 0) {
              if (width < bitmapWidth || height < bitmapHeight) {
                  final float ratio = (float) bitmapWidth / bitmapHeight;
  
                  if (bitmapWidth > bitmapHeight) {
                      height = (int) (width / ratio);
                  } else if (bitmapHeight > bitmapWidth) {
                      width = (int) (height * ratio);
                  }
  
                  final Config c = (width == sIconWidth && height == sIconHeight) ? bitmap
                          .getConfig() : Config.ARGB_8888;
                  final Bitmap thumb = Bitmap.createBitmap(sIconWidth,
                          sIconHeight, c);
                  final Canvas canvas = sCanvas;
                  final Paint paint = sPaint;
                  canvas.setBitmap(thumb);
                  paint.setDither(false);
                  paint.setFilterBitmap(true);
                  sBounds.set((sIconWidth - width) / 2,
                          (sIconHeight - height) / 2, width, height);
                  sOldBounds.set(0, 0, bitmapWidth, bitmapHeight);
                  canvas.drawBitmap(bitmap, sOldBounds, sBounds, paint);
                  return thumb;
              } else if (bitmapWidth < width || bitmapHeight < height) {
                  final Config c = Config.ARGB_8888;
                  final Bitmap thumb = Bitmap.createBitmap(sIconWidth,
                          sIconHeight, c);
                  final Canvas canvas = sCanvas;
                  final Paint paint = sPaint;
                  canvas.setBitmap(thumb);
                  paint.setDither(false);
                  paint.setFilterBitmap(true);
                  canvas.drawBitmap(bitmap, (sIconWidth - bitmapWidth) / 2,
                          (sIconHeight - bitmapHeight) / 2, paint);
                  return thumb;
              }
          }
  
          return bitmap;
      }
  
      /**
       * 生成水印图片 水印在右下角
       *
       * @param src       the bitmap object you want proecss
       * @param watermark the water mark above the src
       * @return return a bitmap object ,if paramter's length is 0,return null
       */
      public static Bitmap createWatermarkBitmap(Bitmap src, Bitmap watermark) {
          if (src == null) {
              return null;
          }
  
          int w = src.getWidth();
          int h = src.getHeight();
          int ww = watermark.getWidth();
          int wh = watermark.getHeight();
          // create the new blank bitmap
          Bitmap newb = Bitmap.createBitmap(w, h, Config.ARGB_8888);// 创建一个新的和SRC长度宽度一样的位图
          Canvas cv = new Canvas(newb);
          // draw src into
          cv.drawBitmap(src, 0, 0, null);// 在 0，0坐标开始画入src
          // draw watermark into
          cv.drawBitmap(watermark, w - ww + 5, h - wh + 5, null);// 在src的右下角画入水印
          // save all clip
          cv.save(Canvas.ALL_SAVE_FLAG);// 保存
          // store
          cv.restore();// 存储
          return newb;
      }
  
      /**
       * 重新编码Bitmap
       *
       * @param src     需要重新编码的Bitmap
       * @param format  编码后的格式（目前只支持png和jpeg这两种格式）
       * @param quality 重新生成后的bitmap的质量
       * @return 返回重新生成后的bitmap
       */
      public static Bitmap codec(Bitmap src, Bitmap.CompressFormat format,
                                 int quality) {
          ByteArrayOutputStream os = new ByteArrayOutputStream();
          src.compress(format, quality, os);
  
          byte[] array = os.toByteArray();
          return BitmapFactory.decodeByteArray(array, 0, array.length);
      }
  
      /**
       * 图片压缩方法：（使用compress的方法）
       * <p/>
       * <br>
       * <b>说明</b> 如果bitmap本身的大小小于maxSize，则不作处理
       *
       * @param bitmap  要压缩的图片
       * @param maxSize 压缩后的大小，单位kb
       */
      public static void compress(Bitmap bitmap, double maxSize) {
          // 将bitmap放至数组中，意在获得bitmap的大小（与实际读取的原文件要大）
          ByteArrayOutputStream baos = new ByteArrayOutputStream();
          // 格式、质量、输出流
          bitmap.compress(Bitmap.CompressFormat.PNG, 70, baos);
          byte[] b = baos.toByteArray();
          // 将字节换成KB
          double mid = b.length / 1024;
          // 获取bitmap大小 是允许最大大小的多少倍
          double i = mid / maxSize;
          // 判断bitmap占用空间是否大于允许最大空间 如果大于则压缩 小于则不压缩
          if (i > 1) {
              // 缩放图片 此处用到平方根 将宽带和高度压缩掉对应的平方根倍
              // （保持宽高不变，缩放后也达到了最大占用空间的大小）
              bitmap = scale(bitmap, bitmap.getWidth() / Math.sqrt(i),
                      bitmap.getHeight() / Math.sqrt(i));
          }
      }
  
      /**
       * 图片的缩放方法
       *
       * @param src       ：源图片资源
       * @param newWidth  ：缩放后宽度
       * @param newHeight ：缩放后高度
       */
      public static Bitmap scale(Bitmap src, double newWidth, double newHeight) {
          // 记录src的宽高
          float width = src.getWidth();
          float height = src.getHeight();
          // 创建一个matrix容器
          Matrix matrix = new Matrix();
          // 计算缩放比例
          float scaleWidth = ((float) newWidth) / width;
          float scaleHeight = ((float) newHeight) / height;
          // 开始缩放
          matrix.postScale(scaleWidth, scaleHeight);
          // 创建缩放后的图片
          return Bitmap.createBitmap(src, 0, 0, (int) width, (int) height,
                  matrix, true);
      }
  
      /**
       * 图片的缩放方法
       *
       * @param src         ：源图片资源
       * @param scaleMatrix ：缩放规则
       */
      public static Bitmap scale(Bitmap src, Matrix scaleMatrix) {
          return Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(),
                  scaleMatrix, true);
      }
  
      /**
       * 图片的缩放方法
       *
       * @param src    ：源图片资源
       * @param scaleX ：横向缩放比例
       * @param scaleY ：纵向缩放比例
       */
      public static Bitmap scale(Bitmap src, float scaleX, float scaleY) {
          Matrix matrix = new Matrix();
          matrix.postScale(scaleX, scaleY);
          return Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(),
                  matrix, true);
      }
  
      /**
       * 图片的缩放方法
       *
       * @param src   ：源图片资源
       * @param scale ：缩放比例
       */
      public static Bitmap scale(Bitmap src, float scale) {
          return scale(src, scale, scale);
      }
  
      /**
       * 旋转图片
       *
       * @param angle  旋转角度
       * @param bitmap 要旋转的图片
       * @return 旋转后的图片
       */
      public static Bitmap rotate(Bitmap bitmap, int angle) {
          Matrix matrix = new Matrix();
          matrix.postRotate(angle);
          return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
                  bitmap.getHeight(), matrix, true);
      }
  
      /**
       * 水平翻转处理
       *
       * @param bitmap 原图
       * @return 水平翻转后的图片
       */
      public static Bitmap reverseByHorizontal(Bitmap bitmap) {
          Matrix matrix = new Matrix();
          matrix.preScale(-1, 1);
          return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
                  bitmap.getHeight(), matrix, false);
      }
  
      /**
       * 垂直翻转处理
       *
       * @param bitmap 原图
       * @return 垂直翻转后的图片
       */
      public static Bitmap reverseByVertical(Bitmap bitmap) {
          Matrix matrix = new Matrix();
          matrix.preScale(1, -1);
          return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
                  bitmap.getHeight(), matrix, false);
      }
  
      /**
       * 更改图片色系，变亮或变暗
       *
       * @param delta 图片的亮暗程度值，越小图片会越亮，取值范围(0,24)
       * @return
       */
      public static Bitmap adjustTone(Bitmap src, int delta) {
          if (delta >= 24 || delta <= 0) {
              return null;
          }
          // 设置高斯矩阵
          int[] gauss = new int[]{1, 2, 1, 2, 4, 2, 1, 2, 1};
          int width = src.getWidth();
          int height = src.getHeight();
          Bitmap bitmap = Bitmap.createBitmap(width, height,
                  Config.RGB_565);
  
          int pixR = 0;
          int pixG = 0;
          int pixB = 0;
          int pixColor = 0;
          int newR = 0;
          int newG = 0;
          int newB = 0;
          int idx = 0;
          int[] pixels = new int[width * height];
  
          src.getPixels(pixels, 0, width, 0, 0, width, height);
          for (int i = 1, length = height - 1; i < length; i++) {
              for (int k = 1, len = width - 1; k < len; k++) {
                  idx = 0;
                  for (int m = -1; m <= 1; m++) {
                      for (int n = -1; n <= 1; n++) {
                          pixColor = pixels[(i + m) * width + k + n];
                          pixR = Color.red(pixColor);
                          pixG = Color.green(pixColor);
                          pixB = Color.blue(pixColor);
  
                          newR += (pixR * gauss[idx]);
                          newG += (pixG * gauss[idx]);
                          newB += (pixB * gauss[idx]);
                          idx++;
                      }
                  }
                  newR /= delta;
                  newG /= delta;
                  newB /= delta;
                  newR = Math.min(255, Math.max(0, newR));
                  newG = Math.min(255, Math.max(0, newG));
                  newB = Math.min(255, Math.max(0, newB));
                  pixels[i * width + k] = Color.argb(255, newR, newG, newB);
                  newR = 0;
                  newG = 0;
                  newB = 0;
              }
          }
          bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
          return bitmap;
      }
  
      /**
       * 将彩色图转换为黑白图
       *
       * @param bmp 位图
       * @return 返回转换好的位图
       */
      public static Bitmap convertToBlackWhite(Bitmap bmp) {
          int width = bmp.getWidth();
          int height = bmp.getHeight();
          int[] pixels = new int[width * height];
          bmp.getPixels(pixels, 0, width, 0, 0, width, height);
  
          int alpha = 0xFF << 24; // 默认将bitmap当成24色图片
          for (int i = 0; i < height; i++) {
              for (int j = 0; j < width; j++) {
                  int grey = pixels[width * i + j];
  
                  int red = ((grey & 0x00FF0000) >> 16);
                  int green = ((grey & 0x0000FF00) >> 8);
                  int blue = (grey & 0x000000FF);
  
                  grey = (int) (red * 0.3 + green * 0.59 + blue * 0.11);
                  grey = alpha | (grey << 16) | (grey << 8) | grey;
                  pixels[width * i + j] = grey;
              }
          }
          Bitmap newBmp = Bitmap.createBitmap(width, height, Config.RGB_565);
          newBmp.setPixels(pixels, 0, width, 0, 0, width, height);
          return newBmp;
      }
  
      /**
       * 读取图片属性：图片被旋转的角度
       *
       * @param path 图片绝对路径
       * @return 旋转的角度
       */
      @TargetApi(Build.VERSION_CODES.ECLAIR)
      public static int getImageDegree(String path) {
          int degree = 0;
          try {
              ExifInterface exifInterface = new ExifInterface(path);
              int orientation = exifInterface.getAttributeInt(
                      ExifInterface.TAG_ORIENTATION,
                      ExifInterface.ORIENTATION_NORMAL);
              switch (orientation) {
                  case ExifInterface.ORIENTATION_ROTATE_90:
                      degree = 90;
                      break;
                  case ExifInterface.ORIENTATION_ROTATE_180:
                      degree = 180;
                      break;
                  case ExifInterface.ORIENTATION_ROTATE_270:
                      degree = 270;
                      break;
              }
          } catch (IOException e) {
              e.printStackTrace();
          }
          return degree;
      }
  
      /**
       * Apply a blur to a Bitmap
       *
       * @param context    Application context
       * @param sentBitmap Bitmap to be converted
       * @param radius     Desired Radius, 0 < r < 25
       * @return a copy of the image with a blur
       */
      @SuppressLint("InlinedApi")
      public static Bitmap blur(Context context, Bitmap sentBitmap, int radius) {
  
          if (radius < 0) {
              radius = 0;
              if (DEBUG) {
                  LogUtils.w(TAG, "radius must be 0 < r < 25 , forcing radius=0");
              }
          } else if (radius > 25) {
              radius = 25;
              if (DEBUG) {
                  LogUtils.w(TAG, "radius must be 0 < r < 25 , forcing radius=25");
              }
          }
  
          if (Build.VERSION.SDK_INT > 16) {
              Bitmap bitmap = sentBitmap.copy(sentBitmap.getConfig(), true);
  
              final RenderScript rs = RenderScript.create(context);
              final Allocation input = Allocation.createFromBitmap(rs,
                      sentBitmap, Allocation.MipmapControl.MIPMAP_NONE,
                      Allocation.USAGE_SCRIPT);
              final Allocation output = Allocation.createTyped(rs,
                      input.getType());
              final ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs,
                      Element.U8_4(rs));
              script.setRadius(radius /* e.g. 3.f */);
              script.setInput(input);
              script.forEach(output);
              output.copyTo(bitmap);
              return bitmap;
          }
  
          // Stack Blur v1.0 from
          // http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html
          //
          // Java Author: Mario Klingemann <mario at quasimondo.com>
          // http://incubator.quasimondo.com
          // created Feburary 29, 2004
          // Android port : Yahel Bouaziz <yahel at kayenko.com>
          // http://www.kayenko.com
          // ported april 5th, 2012
  
          // This is a compromise between Gaussian Blur and Box blur
          // It creates much better looking blurs than Box Blur, but is
          // 7x faster than my Gaussian Blur implementation.
          //
          // I called it Stack Blur because this describes best how this
          // filter works internally: it creates a kind of moving stack
          // of colors whilst scanning through the image. Thereby it
          // just has to add one new block of color to the right side
          // of the stack and remove the leftmost color. The remaining
          // colors on the topmost layer of the stack are either added on
          // or reduced by one, depending on if they are on the right or
          // on the left side of the stack.
          //
          // If you are using this algorithm in your code please add
          // the following line:
          //
          // Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com>
  
          Bitmap bitmap = sentBitmap.copy(sentBitmap.getConfig(), true);
  
          if (radius < 1) {
              return (null);
          }
  
          int w = bitmap.getWidth();
          int h = bitmap.getHeight();
  
          int[] pix = new int[w * h];
          Log.e("pix", w + " " + h + " " + pix.length);
          bitmap.getPixels(pix, 0, w, 0, 0, w, h);
  
          int wm = w - 1;
          int hm = h - 1;
          int wh = w * h;
          int div = radius + radius + 1;
  
          int r[] = new int[wh];
          int g[] = new int[wh];
          int b[] = new int[wh];
          int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
          int vmin[] = new int[Math.max(w, h)];
  
          int divsum = (div + 1) >> 1;
          divsum *= divsum;
          int dv[] = new int[256 * divsum];
          for (i = 0; i < 256 * divsum; i++) {
              dv[i] = (i / divsum);
          }
  
          yw = yi = 0;
  
          int[][] stack = new int[div][3];
          int stackpointer;
          int stackstart;
          int[] sir;
          int rbs;
          int r1 = radius + 1;
          int routsum, goutsum, boutsum;
          int rinsum, ginsum, binsum;
  
          for (y = 0; y < h; y++) {
              rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
              for (i = -radius; i <= radius; i++) {
                  p = pix[yi + Math.min(wm, Math.max(i, 0))];
                  sir = stack[i + radius];
                  sir[0] = (p & 0xff0000) >> 16;
                  sir[1] = (p & 0x00ff00) >> 8;
                  sir[2] = (p & 0x0000ff);
                  rbs = r1 - Math.abs(i);
                  rsum += sir[0] * rbs;
                  gsum += sir[1] * rbs;
                  bsum += sir[2] * rbs;
                  if (i > 0) {
                      rinsum += sir[0];
                      ginsum += sir[1];
                      binsum += sir[2];
                  } else {
                      routsum += sir[0];
                      goutsum += sir[1];
                      boutsum += sir[2];
                  }
              }
              stackpointer = radius;
  
              for (x = 0; x < w; x++) {
  
                  r[yi] = dv[rsum];
                  g[yi] = dv[gsum];
                  b[yi] = dv[bsum];
  
                  rsum -= routsum;
                  gsum -= goutsum;
                  bsum -= boutsum;
  
                  stackstart = stackpointer - radius + div;
                  sir = stack[stackstart % div];
  
                  routsum -= sir[0];
                  goutsum -= sir[1];
                  boutsum -= sir[2];
  
                  if (y == 0) {
                      vmin[x] = Math.min(x + radius + 1, wm);
                  }
                  p = pix[yw + vmin[x]];
  
                  sir[0] = (p & 0xff0000) >> 16;
                  sir[1] = (p & 0x00ff00) >> 8;
                  sir[2] = (p & 0x0000ff);
  
                  rinsum += sir[0];
                  ginsum += sir[1];
                  binsum += sir[2];
  
                  rsum += rinsum;
                  gsum += ginsum;
                  bsum += binsum;
  
                  stackpointer = (stackpointer + 1) % div;
                  sir = stack[(stackpointer) % div];
  
                  routsum += sir[0];
                  goutsum += sir[1];
                  boutsum += sir[2];
  
                  rinsum -= sir[0];
                  ginsum -= sir[1];
                  binsum -= sir[2];
  
                  yi++;
              }
              yw += w;
          }
          for (x = 0; x < w; x++) {
              rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
              yp = -radius * w;
              for (i = -radius; i <= radius; i++) {
                  yi = Math.max(0, yp) + x;
  
                  sir = stack[i + radius];
  
                  sir[0] = r[yi];
                  sir[1] = g[yi];
                  sir[2] = b[yi];
  
                  rbs = r1 - Math.abs(i);
  
                  rsum += r[yi] * rbs;
                  gsum += g[yi] * rbs;
                  bsum += b[yi] * rbs;
  
                  if (i > 0) {
                      rinsum += sir[0];
                      ginsum += sir[1];
                      binsum += sir[2];
                  } else {
                      routsum += sir[0];
                      goutsum += sir[1];
                      boutsum += sir[2];
                  }
  
                  if (i < hm) {
                      yp += w;
                  }
              }
              yi = x;
              stackpointer = radius;
              for (y = 0; y < h; y++) {
                  // Preserve alpha channel: ( 0xff000000 & pix[yi] )
                  pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16)
                          | (dv[gsum] << 8) | dv[bsum];
  
                  rsum -= routsum;
                  gsum -= goutsum;
                  bsum -= boutsum;
  
                  stackstart = stackpointer - radius + div;
                  sir = stack[stackstart % div];
  
                  routsum -= sir[0];
                  goutsum -= sir[1];
                  boutsum -= sir[2];
  
                  if (x == 0) {
                      vmin[y] = Math.min(y + r1, hm) * w;
                  }
                  p = x + vmin[y];
  
                  sir[0] = r[p];
                  sir[1] = g[p];
                  sir[2] = b[p];
  
                  rinsum += sir[0];
                  ginsum += sir[1];
                  binsum += sir[2];
  
                  rsum += rinsum;
                  gsum += ginsum;
                  bsum += binsum;
  
                  stackpointer = (stackpointer + 1) % div;
                  sir = stack[stackpointer];
  
                  routsum += sir[0];
                  goutsum += sir[1];
                  boutsum += sir[2];
  
                  rinsum -= sir[0];
                  ginsum -= sir[1];
                  binsum -= sir[2];
  
                  yi += w;
              }
          }
  
          Log.e("pix", w + " " + h + " " + pix.length);
          bitmap.setPixels(pix, 0, w, 0, 0, w, h);
          return (bitmap);
      }
  
      /**
       * 饱和度处理
       *
       * @param bitmap          原图
       * @param saturationValue 新的饱和度值
       * @return 改变了饱和度值之后的图片
       */
      public static Bitmap saturation(Bitmap bitmap, int saturationValue) {
          // 计算出符合要求的饱和度值
          float newSaturationValue = saturationValue * 1.0F / 127;
          // 创建一个颜色矩阵
          ColorMatrix saturationColorMatrix = new ColorMatrix();
          // 设置饱和度值
          saturationColorMatrix.setSaturation(newSaturationValue);
          // 创建一个画笔并设置其颜色过滤器
          Paint paint = new Paint();
          paint.setColorFilter(new ColorMatrixColorFilter(saturationColorMatrix));
          // 创建一个新的图片并创建画布
          Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth(),
                  bitmap.getHeight(), Config.ARGB_8888);
          Canvas canvas = new Canvas(newBitmap);
          // 将原图使用给定的画笔画到画布上
          canvas.drawBitmap(bitmap, 0, 0, paint);
          return newBitmap;
      }
  
      /**
       * 亮度处理
       *
       * @param bitmap   原图
       * @param lumValue 新的亮度值
       * @return 改变了亮度值之后的图片
       */
      public static Bitmap lum(Bitmap bitmap, int lumValue) {
          // 计算出符合要求的亮度值
          float newlumValue = lumValue * 1.0F / 127;
          // 创建一个颜色矩阵
          ColorMatrix lumColorMatrix = new ColorMatrix();
          // 设置亮度值
          lumColorMatrix.setScale(newlumValue, newlumValue, newlumValue, 1);
          // 创建一个画笔并设置其颜色过滤器
          Paint paint = new Paint();
          paint.setColorFilter(new ColorMatrixColorFilter(lumColorMatrix));
          // 创建一个新的图片并创建画布
          Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth(),
                  bitmap.getHeight(), Config.ARGB_8888);
          Canvas canvas = new Canvas(newBitmap);
          // 将原图使用给定的画笔画到画布上
          canvas.drawBitmap(bitmap, 0, 0, paint);
          return newBitmap;
      }
  
      /**
       * 色相处理
       *
       * @param bitmap   原图
       * @param hueValue 新的色相值
       * @return 改变了色相值之后的图片
       */
      public static Bitmap hue(Bitmap bitmap, int hueValue) {
          // 计算出符合要求的色相值
          float newHueValue = (hueValue - 127) * 1.0F / 127 * 180;
          // 创建一个颜色矩阵
          ColorMatrix hueColorMatrix = new ColorMatrix();
          // 控制让红色区在色轮上旋转的角度
          hueColorMatrix.setRotate(0, newHueValue);
          // 控制让绿红色区在色轮上旋转的角度
          hueColorMatrix.setRotate(1, newHueValue);
          // 控制让蓝色区在色轮上旋转的角度
          hueColorMatrix.setRotate(2, newHueValue);
          // 创建一个画笔并设置其颜色过滤器
          Paint paint = new Paint();
          paint.setColorFilter(new ColorMatrixColorFilter(hueColorMatrix));
          // 创建一个新的图片并创建画布
          Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth(),
                  bitmap.getHeight(), Config.ARGB_8888);
          Canvas canvas = new Canvas(newBitmap);
          // 将原图使用给定的画笔画到画布上
          canvas.drawBitmap(bitmap, 0, 0, paint);
          return newBitmap;
      }
  
      /**
       * 亮度、色相、饱和度处理
       *
       * @param bitmap          原图
       * @param lumValue        亮度值
       * @param hueValue        色相值
       * @param saturationValue 饱和度值
       * @return 亮度、色相、饱和度处理后的图片
       */
      public static Bitmap lumAndHueAndSaturation(Bitmap bitmap, int lumValue,
                                                  int hueValue, int saturationValue) {
          // 计算出符合要求的饱和度值
          float newSaturationValue = saturationValue * 1.0F / 127;
          // 计算出符合要求的亮度值
          float newlumValue = lumValue * 1.0F / 127;
          // 计算出符合要求的色相值
          float newHueValue = (hueValue - 127) * 1.0F / 127 * 180;
  
          // 创建一个颜色矩阵并设置其饱和度
          ColorMatrix colorMatrix = new ColorMatrix();
  
          // 设置饱和度值
          colorMatrix.setSaturation(newSaturationValue);
          // 设置亮度值
          colorMatrix.setScale(newlumValue, newlumValue, newlumValue, 1);
          // 控制让红色区在色轮上旋转的角度
          colorMatrix.setRotate(0, newHueValue);
          // 控制让绿红色区在色轮上旋转的角度
          colorMatrix.setRotate(1, newHueValue);
          // 控制让蓝色区在色轮上旋转的角度
          colorMatrix.setRotate(2, newHueValue);
  
          // 创建一个画笔并设置其颜色过滤器
          Paint paint = new Paint();
          paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
          // 创建一个新的图片并创建画布
          Bitmap newBitmap = Bitmap.createBitmap(bitmap.getWidth(),
                  bitmap.getHeight(), Config.ARGB_8888);
          Canvas canvas = new Canvas(newBitmap);
          // 将原图使用给定的画笔画到画布上
          canvas.drawBitmap(bitmap, 0, 0, paint);
          return newBitmap;
      }
  
      /**
       * 怀旧效果处理
       *
       * @param bitmap 原图
       * @return 怀旧效果处理后的图片
       */
      public static Bitmap nostalgic(Bitmap bitmap) {
          int width = bitmap.getWidth();
          int height = bitmap.getHeight();
          Bitmap newBitmap = Bitmap.createBitmap(width, height,
                  Config.RGB_565);
          int pixColor = 0;
          int pixR = 0;
          int pixG = 0;
          int pixB = 0;
          int newR = 0;
          int newG = 0;
          int newB = 0;
          int[] pixels = new int[width * height];
          bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
          for (int i = 0; i < height; i++) {
              for (int k = 0; k < width; k++) {
                  pixColor = pixels[width * i + k];
                  pixR = Color.red(pixColor);
                  pixG = Color.green(pixColor);
                  pixB = Color.blue(pixColor);
                  newR = (int) (0.393 * pixR + 0.769 * pixG + 0.189 * pixB);
                  newG = (int) (0.349 * pixR + 0.686 * pixG + 0.168 * pixB);
                  newB = (int) (0.272 * pixR + 0.534 * pixG + 0.131 * pixB);
                  int newColor = Color.argb(255, newR > 255 ? 255 : newR,
                          newG > 255 ? 255 : newG, newB > 255 ? 255 : newB);
                  pixels[width * i + k] = newColor;
              }
          }
          newBitmap.setPixels(pixels, 0, width, 0, 0, width, height);
          return newBitmap;
      }
  
      /**
       * 柔化效果处理
       *
       * @param bitmap 原图
       * @return 柔化效果处理后的图片
       */
      public static Bitmap soften(Bitmap bitmap) {
          // 高斯矩阵
          int[] gauss = new int[]{1, 2, 1, 2, 4, 2, 1, 2, 1};
  
          int width = bitmap.getWidth();
          int height = bitmap.getHeight();
          Bitmap newBitmap = Bitmap.createBitmap(width, height,
                  Config.RGB_565);
  
          int pixR = 0;
          int pixG = 0;
          int pixB = 0;
  
          int pixColor = 0;
  
          int newR = 0;
          int newG = 0;
          int newB = 0;
  
          int delta = 16; // 值越小图片会越亮，越大则越暗
  
          int idx = 0;
          int[] pixels = new int[width * height];
          bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
          for (int i = 1, length = height - 1; i < length; i++) {
              for (int k = 1, len = width - 1; k < len; k++) {
                  idx = 0;
                  for (int m = -1; m <= 1; m++) {
                      for (int n = -1; n <= 1; n++) {
                          pixColor = pixels[(i + m) * width + k + n];
                          pixR = Color.red(pixColor);
                          pixG = Color.green(pixColor);
                          pixB = Color.blue(pixColor);
  
                          newR = newR + pixR * gauss[idx];
                          newG = newG + pixG * gauss[idx];
                          newB = newB + pixB * gauss[idx];
                          idx++;
                      }
                  }
  
                  newR /= delta;
                  newG /= delta;
                  newB /= delta;
  
                  newR = Math.min(255, Math.max(0, newR));
                  newG = Math.min(255, Math.max(0, newG));
                  newB = Math.min(255, Math.max(0, newB));
  
                  pixels[i * width + k] = Color.argb(255, newR, newG, newB);
  
                  newR = 0;
                  newG = 0;
                  newB = 0;
              }
          }
  
          newBitmap.setPixels(pixels, 0, width, 0, 0, width, height);
          return newBitmap;
      }
  
      /**
       * 光照效果处理
       *
       * @param bitmap  原图
       * @param centerX 光源在X轴的位置
       * @param centerY 光源在Y轴的位置
       * @return 光照效果处理后的图片
       */
      public static Bitmap sunshine(Bitmap bitmap, int centerX, int centerY) {
          final int width = bitmap.getWidth();
          final int height = bitmap.getHeight();
          Bitmap newBitmap = Bitmap.createBitmap(width, height,
                  Config.RGB_565);
  
          int pixR = 0;
          int pixG = 0;
          int pixB = 0;
  
          int pixColor = 0;
  
          int newR = 0;
          int newG = 0;
          int newB = 0;
          int radius = Math.min(centerX, centerY);
  
          final float strength = 150F; // 光照强度 100~150
          int[] pixels = new int[width * height];
          bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
          int pos = 0;
          for (int i = 1, length = height - 1; i < length; i++) {
              for (int k = 1, len = width - 1; k < len; k++) {
                  pos = i * width + k;
                  pixColor = pixels[pos];
  
                  pixR = Color.red(pixColor);
                  pixG = Color.green(pixColor);
                  pixB = Color.blue(pixColor);
  
                  newR = pixR;
                  newG = pixG;
                  newB = pixB;
  
                  // 计算当前点到光照中心的距离，平面座标系中求两点之间的距离
                  int distance = (int) (Math.pow((centerY - i), 2) + Math.pow(
                          centerX - k, 2));
                  if (distance < radius * radius) {
                      // 按照距离大小计算增加的光照值
                      int result = (int) (strength * (1.0 - Math.sqrt(distance)
                              / radius));
                      newR = pixR + result;
                      newG = pixG + result;
                      newB = pixB + result;
                  }
  
                  newR = Math.min(255, Math.max(0, newR));
                  newG = Math.min(255, Math.max(0, newG));
                  newB = Math.min(255, Math.max(0, newB));
  
                  pixels[pos] = Color.argb(255, newR, newG, newB);
              }
          }
  
          newBitmap.setPixels(pixels, 0, width, 0, 0, width, height);
          return newBitmap;
      }
  
      /**
       * 底片效果处理
       *
       * @param bitmap 原图
       * @return 底片效果处理后的图片
       */
      public static Bitmap film(Bitmap bitmap) {
          // RGBA的最大值
          final int MAX_VALUE = 255;
          int width = bitmap.getWidth();
          int height = bitmap.getHeight();
          Bitmap newBitmap = Bitmap.createBitmap(width, height,
                  Config.RGB_565);
  
          int pixR = 0;
          int pixG = 0;
          int pixB = 0;
  
          int pixColor = 0;
  
          int newR = 0;
          int newG = 0;
          int newB = 0;
  
          int[] pixels = new int[width * height];
          bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
          int pos = 0;
          for (int i = 1, length = height - 1; i < length; i++) {
              for (int k = 1, len = width - 1; k < len; k++) {
                  pos = i * width + k;
                  pixColor = pixels[pos];
  
                  pixR = Color.red(pixColor);
                  pixG = Color.green(pixColor);
                  pixB = Color.blue(pixColor);
  
                  newR = MAX_VALUE - pixR;
                  newG = MAX_VALUE - pixG;
                  newB = MAX_VALUE - pixB;
  
                  newR = Math.min(MAX_VALUE, Math.max(0, newR));
                  newG = Math.min(MAX_VALUE, Math.max(0, newG));
                  newB = Math.min(MAX_VALUE, Math.max(0, newB));
  
                  pixels[pos] = Color.argb(MAX_VALUE, newR, newG, newB);
              }
          }
  
          newBitmap.setPixels(pixels, 0, width, 0, 0, width, height);
          return newBitmap;
      }
  
      /**
       * 锐化效果处理
       *
       * @param bitmap 原图
       * @return 锐化效果处理后的图片
       */
      public static Bitmap sharpen(Bitmap bitmap) {
          // 拉普拉斯矩阵
          int[] laplacian = new int[]{-1, -1, -1, -1, 9, -1, -1, -1, -1};
  
          int width = bitmap.getWidth();
          int height = bitmap.getHeight();
          Bitmap newBitmap = Bitmap.createBitmap(width, height,
                  Config.RGB_565);
  
          int pixR = 0;
          int pixG = 0;
          int pixB = 0;
  
          int pixColor = 0;
  
          int newR = 0;
          int newG = 0;
          int newB = 0;
  
          int idx = 0;
          float alpha = 0.3F;
          int[] pixels = new int[width * height];
          bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
          for (int i = 1, length = height - 1; i < length; i++) {
              for (int k = 1, len = width - 1; k < len; k++) {
                  idx = 0;
                  for (int m = -1; m <= 1; m++) {
                      for (int n = -1; n <= 1; n++) {
                          pixColor = pixels[(i + n) * width + k + m];
                          pixR = Color.red(pixColor);
                          pixG = Color.green(pixColor);
                          pixB = Color.blue(pixColor);
  
                          newR = newR + (int) (pixR * laplacian[idx] * alpha);
                          newG = newG + (int) (pixG * laplacian[idx] * alpha);
                          newB = newB + (int) (pixB * laplacian[idx] * alpha);
                          idx++;
                      }
                  }
  
                  newR = Math.min(255, Math.max(0, newR));
                  newG = Math.min(255, Math.max(0, newG));
                  newB = Math.min(255, Math.max(0, newB));
  
                  pixels[i * width + k] = Color.argb(255, newR, newG, newB);
                  newR = 0;
                  newG = 0;
                  newB = 0;
              }
          }
  
          newBitmap.setPixels(pixels, 0, width, 0, 0, width, height);
          return newBitmap;
      }
  
      /**
       * 浮雕效果处理
       *
       * @param bitmap 原图
       * @return 浮雕效果处理后的图片
       */
      public static Bitmap emboss(Bitmap bitmap) {
          int width = bitmap.getWidth();
          int height = bitmap.getHeight();
          Bitmap newBitmap = Bitmap.createBitmap(width, height,
                  Config.RGB_565);
  
          int pixR = 0;
          int pixG = 0;
          int pixB = 0;
  
          int pixColor = 0;
  
          int newR = 0;
          int newG = 0;
          int newB = 0;
  
          int[] pixels = new int[width * height];
          bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
          int pos = 0;
          for (int i = 1, length = height - 1; i < length; i++) {
              for (int k = 1, len = width - 1; k < len; k++) {
                  pos = i * width + k;
                  pixColor = pixels[pos];
  
                  pixR = Color.red(pixColor);
                  pixG = Color.green(pixColor);
                  pixB = Color.blue(pixColor);
  
                  pixColor = pixels[pos + 1];
                  newR = Color.red(pixColor) - pixR + 127;
                  newG = Color.green(pixColor) - pixG + 127;
                  newB = Color.blue(pixColor) - pixB + 127;
  
                  newR = Math.min(255, Math.max(0, newR));
                  newG = Math.min(255, Math.max(0, newG));
                  newB = Math.min(255, Math.max(0, newB));
  
                  pixels[pos] = Color.argb(255, newR, newG, newB);
              }
          }
  
          newBitmap.setPixels(pixels, 0, width, 0, 0, width, height);
          return newBitmap;
      }
  
      /**
       * 将YUV格式的图片的源数据从横屏模式转为竖屏模式，注意：将源图片的宽高互换一下就是新图片的宽高
       *
       * @param sourceData YUV格式的图片的源数据
       * @param width      源图片的宽
       * @param height     源图片的高
       * @return
       */
      public static byte[] yuvLandscapeToPortrait(byte[] sourceData,
                                                  int width, int height) {
          byte[] rotatedData = new byte[sourceData.length];
          for (int y = 0; y < height; y++) {
              for (int x = 0; x < width; x++)
                  rotatedData[x * height + height - y - 1] = sourceData[x + y
                          * width];
          }
          return rotatedData;
      }
  
  }
  