ImageUtils
JavaSE
package com.easystructure.utils.system; import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream; import javax.imageio.ImageIO; import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGEncodeParam;
import com.sun.image.codec.jpeg.JPEGImageEncoder; @SuppressWarnings("all")
public class ImageUtils { /**
* 把图片印刷到图片上
*
* @param pressImg
* -- 水印文件
* @param targetImg
* -- 目标文件
* @param intAlign
* --位置类型
*/
public final static void pressImage(String pressImg, String targetImg, int intAlign) { try {
// 目标文件
File _file = new File(targetImg);
Image src = ImageIO.read(_file);
int wideth = src.getWidth(null);
int height = src.getHeight(null);
BufferedImage image = new BufferedImage(wideth, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
g.drawImage(src, 0, 0, wideth, height, null); // 水印文件
File _filebiao = new File(pressImg);
Image src_biao = ImageIO.read(_filebiao);
int wideth_biao = src_biao.getWidth(null);
int height_biao = src_biao.getHeight(null);
if (intAlign == 1) {
g.drawImage(src_biao, 5, 10, wideth_biao, height_biao, null);
} else if (intAlign == 2) {
g.drawImage(src_biao, (wideth - wideth_biao) / 2 - 5, 10, wideth_biao, height_biao, null);
} else if (intAlign == 3) {
g.drawImage(src_biao, wideth - wideth_biao - 5, 10, wideth_biao, height_biao, null);
} else if (intAlign == 4) {
g.drawImage(src_biao, 5, (height - height_biao) / 2 - 10, wideth_biao, height_biao, null);
} else if (intAlign == 5) {
g.drawImage(src_biao, (wideth - wideth_biao) / 2 - 5, (height - height_biao) / 2 - 10, wideth_biao, height_biao, null);
} else if (intAlign == 6) {
g.drawImage(src_biao, wideth - wideth_biao - 5, (height - height_biao) / 2 - 10, wideth_biao, height_biao, null);
} else if (intAlign == 7) {
g.drawImage(src_biao, 5, height - height_biao - 10, wideth_biao, height_biao, null);
} else if (intAlign == 8) {
g.drawImage(src_biao, (wideth - wideth_biao) / 2 - 5, height - height_biao - 10, wideth_biao, height_biao, null);
} else if (intAlign == 9) {
g.drawImage(src_biao, wideth - wideth_biao - 5, height - height_biao - 10, wideth_biao, height_biao, null);
}
// 水印文件结束
g.dispose();
FileOutputStream out = new FileOutputStream(targetImg);
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
encoder.encode(image);
out.close();
} catch (Exception e) {
e.printStackTrace();
}
} /**
*
* @param strImageText
* @param fm
* @return
* @author googol Feb 9, 2006
*/
public static int getStringWidth(String strImageText, FontMetrics fm) {
int intReturn = 0;
int intCount = strImageText.length();
char chrImageText[] = strImageText.toCharArray();
for (int i = 0; i < intCount; i++) {
int charWidth = fm.charWidth(chrImageText[i]);
intReturn += charWidth;
} return intReturn += 10;
} /** */
/**
* 打印文字水印图片
*
* @param pressText
* --文字
* @param targetImg
* -- 目标图片
* @param fontName
* -- 字体名
* @param fontStyle
* -- 字体样式
* @param color
* -- 字体颜色
* @param fontSize
* -- 字体大小
* @param intAlign
* --位置类型
*/ public static void pressText(String pressText, String targetImg, String fontName, int fontStyle, Color color, int fontSize, int intAlign) {
try {
File _file = new File(targetImg);
Image src = ImageIO.read(_file);
int wideth = src.getWidth(null);
int height = src.getHeight(null);
BufferedImage image = new BufferedImage(wideth, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
g.drawImage(src, 0, 0, wideth, height, null);
g.setColor(color);
g.setFont(new Font(fontName, fontStyle, fontSize));
int intWidth = getStringWidth(pressText, g.getFontMetrics());
if (wideth > intWidth) {
if (intAlign == 1) {
g.drawString(pressText, 5, 20);
} else if (intAlign == 2) {
g.drawString(pressText, (wideth - intWidth) / 2 - 5, 20);
} else if (intAlign == 3) {
g.drawString(pressText, wideth - intWidth - 5, 20);
} else if (intAlign == 4) {
g.drawString(pressText, 5, height / 2 - 10);
} else if (intAlign == 5) {
g.drawString(pressText, (wideth - intWidth) / 2 - 5, height / 2 - 10);
} else if (intAlign == 6) {
g.drawString(pressText, wideth - intWidth - 5, height / 2 - 10);
} else if (intAlign == 7) {
g.drawString(pressText, 5, height - 10);
} else if (intAlign == 8) {
g.drawString(pressText, (wideth - intWidth) / 2 - 5, height - 10);
} else if (intAlign == 9) {
g.drawString(pressText, wideth - intWidth - 5, height - 10);
}
} else {
g.drawString(pressText, 0, height - 10);
}
g.dispose();
FileOutputStream out = new FileOutputStream(targetImg);
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
encoder.encode(image);
out.close();
} catch (Exception e) {
System.out.println(e);
}
} /**
* 只限制宽度生成缩略图,按比例缩放,如果原图宽度比需要生成的图的宽度还小,则简单拷贝一张图
*
* @author Dennis
*
* 2009-11-17 下午04:41:55
*/
public static void makeMiniature(String strPicturePath, String strOutPath, int intMinWidth) {
File _file = new File(strPicturePath);
Image src;
try {
src = ImageIO.read(_file);
int width = src.getWidth(null);
int height = src.getHeight(null);
if (width <= intMinWidth) {
// 直接copy
makeMiniature(strPicturePath, strOutPath, width, height);
} else {
// 得到缩放比例
float scale = (float) intMinWidth / width;
makeMiniature(strPicturePath, strOutPath, intMinWidth, (int) (height * scale - 1));
} } catch (IOException e) {
System.out.println(e);
}
} /**
* 产生一个缩略
*
* @param strPicturePath
* 原图片位置
* @param strOutPath
* 生成图片的位置 2009-11-17 下午04:43:50
*/
public static void makeMiniature(String strPicturePath, String strOutPath, int intMinWidth, int intMinHeight) { try {
String imageFile = strPicturePath;
File file = new File(imageFile); BufferedImage im = null;
InputStream imageIn = new FileInputStream(file); im = ImageIO.read(imageIn);
int minh = intMinHeight, minw = intMinWidth; BufferedImage imout = new BufferedImage(minw, minh, 1);
Graphics g = imout.getGraphics();
g.drawImage(im, 0, 0, minw, minh, null); imageIn.close(); File out = new File(strOutPath);
if (strOutPath.endsWith(".JPG") || strOutPath.endsWith(".jpg")) {
ImageIO.write(imout, "jpg", out); } else if (strOutPath.endsWith(".GIF") || strOutPath.endsWith(".gif")) {
ImageIO.write(imout, "gif", out);
}
} catch (Exception ex) {
ex.printStackTrace();
}
} /**
* 压缩图片文件<br>
* 先保存原文件,再压缩、上传
*
* @param oldFile
* 要进行压缩的文件全路径
* @param width
* 宽度
* @param height
* 高度
* @param quality
* 质量
* @param smallIcon
* 小图片的后缀
* @return 返回压缩后的文件的全路径
*/
public static String zipImageFile(String oldFile, int width, int height, float quality, String smallIcon) {
if (oldFile == null)
return null;
String newImage = null;
try {
/** 对服务器上的临时文件进行处理 */
Image srcFile = ImageIO.read(new File(oldFile));
double rate1 = ((double) srcFile.getWidth(null)) / (double) width + 0.1;
double rate2 = ((double) srcFile.getHeight(null)) / (double) width + 0.1;
double rate = rate1 > rate2 ? rate1 : rate2;
width = (int) (((double) srcFile.getWidth(null)) / rate);
width = (int) (((double) srcFile.getHeight(null)) / rate); /** 宽,高设定 */
BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
tag.getGraphics().drawImage(srcFile, 0, 0, width, height, null);
String filePrex = oldFile.substring(0, oldFile.indexOf('.'));
/** 压缩后的文件名 */
newImage = filePrex + smallIcon + oldFile.substring(filePrex.length()); /** 压缩之后临时存放位置 */
FileOutputStream out = new FileOutputStream(newImage); JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
JPEGEncodeParam jep = JPEGCodec.getDefaultJPEGEncodeParam(tag);
/** 压缩质量 */
jep.setQuality(quality, true);
encoder.encode(tag, jep);
out.close(); } catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return newImage;
} /**
* 经过缩略图算法的压缩。存放生成图片的路径同源图片。
*
* @param oldFile
* @param width
* @param height
* @param smallIcon
*/
public static void zipImageFileSameOldPath(String oldFile, int intMinWidth, int intMinHeight, String smallIcon) { String strPicturePath = oldFile;
String strOutPath = CommonUtils.addStrToFileName(strPicturePath, smallIcon);
zipImageFileDifferPath(strPicturePath, strOutPath, intMinWidth, intMinHeight);
} /**
* 经过缩略图算法的压缩。存放生成图片的路径为指定路径。
*
* @param oldFile
* @param width
* @param height
* @param smallIcon
*/
public static void zipImageFileDifferPath(String oldFilePath, String newFilePath, int intMinWidth, int intMinHeight) { BufferedImage im = null;
BufferedImage imout = null;
int minh = intMinHeight;
int minw = intMinWidth;
try {
File file = new File(oldFilePath);
InputStream imageIn = new FileInputStream(file);
im = ImageIO.read(imageIn);
imout = new ImageScale().imageZoomOut(im, minw, minh);
imageIn.close(); File out = new File(newFilePath);
if (newFilePath.endsWith(".JPG") || newFilePath.endsWith(".jpg")) {
ImageIO.write(imout, "jpg", out);
} else if (newFilePath.endsWith(".GIF") || newFilePath.endsWith(".gif")) {
ImageIO.write(imout, "gif", out);
}
} catch (Exception ex) {
ex.printStackTrace();
}
} public static void main(String[] args) {
// pressImage("F:/bisonte.png", "F:/123.jpg", 9);
pressText("Dennis", "F:/123.jpg", "宋体", Font.PLAIN, Color.RED, 15, 6);
// createMark("F:/123.jpg", "mmmmnn", Color.RED, 15);
} }
http://blog.csdn.net/googolplex/article/details/6724887
Android
bitmap2Bytes, bytes2Bitmap : bitmap与byteArr互转
drawable2Bitmap, bitmap2Drawable : drawable与bitmap互转
drawable2Bytes, bytes2Drawable : drawable与byteArr互转
getBitmap : 获取bitmap
scale : 缩放图片
clip : 裁剪图片
skew : 倾斜图片
rotate : 旋转图片
getRotateDegree : 获取图片旋转角度
toRound : 转为圆形图片
toRoundCorner : 转为圆角图片
fastBlur : 快速模糊
renderScriptBlur : renderScript模糊图片
stackBlur : stack模糊图片
addFrame : 添加颜色边框
addReflection : 添加倒影
addTextWatermark : 添加文字水印
addImageWatermark : 添加图片水印
toAlpha : 转为alpha位图
toGray : 转为灰度图片
save : 保存图片
isImage : 根据文件名判断文件是否为图片
getImageType : 获取图片类型
compressByScale : 按缩放压缩
compressByQuality : 按质量压缩
compressBySampleSize : 按采样大小压缩
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
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.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.NinePatchDrawable;
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.support.annotation.FloatRange;
import android.support.annotation.IntRange;
import android.view.View; import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream; /**
* <pre>
* author: Blankj
* blog : http://blankj.com
* time : 2016/8/12
* desc : 图片相关工具类
* </pre>
*/
public class ImageUtils { private ImageUtils() {
throw new UnsupportedOperationException("u can't instantiate me...");
} /**
* bitmap转byteArr
*
* @param bitmap bitmap对象
* @param format 格式
* @return 字节数组
*/
public static byte[] bitmap2Bytes(Bitmap bitmap, CompressFormat format) {
if (bitmap == null) return null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(format, 100, baos);
return baos.toByteArray();
} /**
* byteArr转bitmap
*
* @param bytes 字节数组
* @return bitmap
*/
public static Bitmap bytes2Bitmap(byte[] bytes) {
return (bytes == null || bytes.length == 0) ? null : BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
} /**
* drawable转bitmap
*
* @param drawable drawable对象
* @return bitmap
*/
public static Bitmap drawable2Bitmap(Drawable drawable) {
if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap();
} else if (drawable instanceof NinePatchDrawable) {
Bitmap bitmap = Bitmap.createBitmap(
drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight(),
drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
drawable.draw(canvas);
return bitmap;
} else {
return null;
}
} /**
* bitmap转drawable
*
* @param res resources对象
* @param bitmap bitmap对象
* @return drawable
*/
public static Drawable bitmap2Drawable(Resources res, Bitmap bitmap) {
return bitmap == null ? null : new BitmapDrawable(res, bitmap);
} /**
* drawable转byteArr
*
* @param drawable drawable对象
* @param format 格式
* @return 字节数组
*/
public static byte[] drawable2Bytes(Drawable drawable, CompressFormat format) {
return drawable == null ? null : bitmap2Bytes(drawable2Bitmap(drawable), format);
} /**
* byteArr转drawable
*
* @param res resources对象
* @param bytes 字节数组
* @return drawable
*/
public static Drawable bytes2Drawable(Resources res, byte[] bytes) {
return res == null ? null : bitmap2Drawable(res, bytes2Bitmap(bytes));
} /**
* view转Bitmap
*
* @param view 视图
* @return bitmap
*/
public static Bitmap view2Bitmap(View view) {
if (view == null) return null;
Bitmap ret = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(ret);
Drawable bgDrawable = view.getBackground();
if (bgDrawable != null) {
bgDrawable.draw(canvas);
} else {
canvas.drawColor(Color.WHITE);
}
view.draw(canvas);
return ret;
} /**
* 计算采样大小
*
* @param options 选项
* @param maxWidth 最大宽度
* @param maxHeight 最大高度
* @return 采样大小
*/
private static int calculateInSampleSize(BitmapFactory.Options options, int maxWidth, int maxHeight) {
if (maxWidth == 0 || maxHeight == 0) return 1;
int height = options.outHeight;
int width = options.outWidth;
int inSampleSize = 1;
while ((height >>= 1) >= maxHeight && (width >>= 1) >= maxWidth) {
inSampleSize <<= 1;
}
return inSampleSize;
} /**
* 获取bitmap
*
* @param file 文件
* @return bitmap
*/
public static Bitmap getBitmap(File file) {
if (file == null) return null;
InputStream is = null;
try {
is = new BufferedInputStream(new FileInputStream(file));
return BitmapFactory.decodeStream(is);
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
} finally {
CloseUtils.closeIO(is);
}
} /**
* 获取bitmap
*
* @param file 文件
* @param maxWidth 最大宽度
* @param maxHeight 最大高度
* @return bitmap
*/
public static Bitmap getBitmap(File file, int maxWidth, int maxHeight) {
if (file == null) return null;
InputStream is = null;
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
is = new BufferedInputStream(new FileInputStream(file));
BitmapFactory.decodeStream(is, null, options);
options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeStream(is, null, options);
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
} finally {
CloseUtils.closeIO(is);
}
} /**
* 获取bitmap
*
* @param filePath 文件路径
* @return bitmap
*/
public static Bitmap getBitmap(String filePath) {
if (StringUtils.isSpace(filePath)) return null;
return BitmapFactory.decodeFile(filePath);
} /**
* 获取bitmap
*
* @param filePath 文件路径
* @param maxWidth 最大宽度
* @param maxHeight 最大高度
* @return bitmap
*/
public static Bitmap getBitmap(String filePath, int maxWidth, int maxHeight) {
if (StringUtils.isSpace(filePath)) return null;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, options);
options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(filePath, options);
} /**
* 获取bitmap
*
* @param is 输入流
* @return bitmap
*/
public static Bitmap getBitmap(InputStream is) {
if (is == null) return null;
return BitmapFactory.decodeStream(is);
} /**
* 获取bitmap
*
* @param is 输入流
* @param maxWidth 最大宽度
* @param maxHeight 最大高度
* @return bitmap
*/
public static Bitmap getBitmap(InputStream is, int maxWidth, int maxHeight) {
if (is == null) return null;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is, null, options);
options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeStream(is, null, options);
} /**
* 获取bitmap
*
* @param data 数据
* @param offset 偏移量
* @return bitmap
*/
public static Bitmap getBitmap(byte[] data, int offset) {
if (data.length == 0) return null;
return BitmapFactory.decodeByteArray(data, offset, data.length);
} /**
* 获取bitmap
*
* @param data 数据
* @param offset 偏移量
* @param maxWidth 最大宽度
* @param maxHeight 最大高度
* @return bitmap
*/
public static Bitmap getBitmap(byte[] data, int offset, int maxWidth, int maxHeight) {
if (data.length == 0) return null;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(data, offset, data.length, options);
options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeByteArray(data, offset, data.length, options);
} /**
* 获取bitmap
*
* @param res 资源对象
* @param id 资源id
* @return bitmap
*/
public static Bitmap getBitmap(Resources res, int id) {
if (res == null) return null;
return BitmapFactory.decodeResource(res, id);
} /**
* 获取bitmap
*
* @param res 资源对象
* @param id 资源id
* @param maxWidth 最大宽度
* @param maxHeight 最大高度
* @return bitmap
*/
public static Bitmap getBitmap(Resources res, int id, int maxWidth, int maxHeight) {
if (res == null) return null;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, id, options);
options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, id, options);
} /**
* 获取bitmap
*
* @param fd 文件描述
* @return bitmap
*/
public static Bitmap getBitmap(FileDescriptor fd) {
if (fd == null) return null;
return BitmapFactory.decodeFileDescriptor(fd);
} /**
* 获取bitmap
*
* @param fd 文件描述
* @param maxWidth 最大宽度
* @param maxHeight 最大高度
* @return bitmap
*/
public static Bitmap getBitmap(FileDescriptor fd, int maxWidth, int maxHeight) {
if (fd == null) return null;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFileDescriptor(fd, null, options);
options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFileDescriptor(fd, null, options);
} /**
* 缩放图片
*
* @param src 源图片
* @param newWidth 新宽度
* @param newHeight 新高度
* @return 缩放后的图片
*/
public static Bitmap scale(Bitmap src, int newWidth, int newHeight) {
return scale(src, newWidth, newHeight, false);
} /**
* 缩放图片
*
* @param src 源图片
* @param newWidth 新宽度
* @param newHeight 新高度
* @param recycle 是否回收
* @return 缩放后的图片
*/
public static Bitmap scale(Bitmap src, int newWidth, int newHeight, boolean recycle) {
if (isEmptyBitmap(src)) return null;
Bitmap ret = Bitmap.createScaledBitmap(src, newWidth, newHeight, true);
if (recycle && !src.isRecycled()) src.recycle();
return ret;
} /**
* 缩放图片
*
* @param src 源图片
* @param scaleWidth 缩放宽度倍数
* @param scaleHeight 缩放高度倍数
* @return 缩放后的图片
*/
public static Bitmap scale(Bitmap src, float scaleWidth, float scaleHeight) {
return scale(src, scaleWidth, scaleHeight, false);
} /**
* 缩放图片
*
* @param src 源图片
* @param scaleWidth 缩放宽度倍数
* @param scaleHeight 缩放高度倍数
* @param recycle 是否回收
* @return 缩放后的图片
*/
public static Bitmap scale(Bitmap src, float scaleWidth, float scaleHeight, boolean recycle) {
if (isEmptyBitmap(src)) return null;
Matrix matrix = new Matrix();
matrix.setScale(scaleWidth, scaleHeight);
Bitmap ret = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true);
if (recycle && !src.isRecycled()) src.recycle();
return ret;
} /**
* 裁剪图片
*
* @param src 源图片
* @param x 开始坐标x
* @param y 开始坐标y
* @param width 裁剪宽度
* @param height 裁剪高度
* @return 裁剪后的图片
*/
public static Bitmap clip(Bitmap src, int x, int y, int width, int height) {
return clip(src, x, y, width, height, false);
} /**
* 裁剪图片
*
* @param src 源图片
* @param x 开始坐标x
* @param y 开始坐标y
* @param width 裁剪宽度
* @param height 裁剪高度
* @param recycle 是否回收
* @return 裁剪后的图片
*/
public static Bitmap clip(Bitmap src, int x, int y, int width, int height, boolean recycle) {
if (isEmptyBitmap(src)) return null;
Bitmap ret = Bitmap.createBitmap(src, x, y, width, height);
if (recycle && !src.isRecycled()) src.recycle();
return ret;
} /**
* 倾斜图片
*
* @param src 源图片
* @param kx 倾斜因子x
* @param ky 倾斜因子y
* @return 倾斜后的图片
*/
public static Bitmap skew(Bitmap src, float kx, float ky) {
return skew(src, kx, ky, 0, 0, false);
} /**
* 倾斜图片
*
* @param src 源图片
* @param kx 倾斜因子x
* @param ky 倾斜因子y
* @param recycle 是否回收
* @return 倾斜后的图片
*/
public static Bitmap skew(Bitmap src, float kx, float ky, boolean recycle) {
return skew(src, kx, ky, 0, 0, recycle);
} /**
* 倾斜图片
*
* @param src 源图片
* @param kx 倾斜因子x
* @param ky 倾斜因子y
* @param px 平移因子x
* @param py 平移因子y
* @return 倾斜后的图片
*/
public static Bitmap skew(Bitmap src, float kx, float ky, float px, float py) {
return skew(src, kx, ky, px, py, false);
} /**
* 倾斜图片
*
* @param src 源图片
* @param kx 倾斜因子x
* @param ky 倾斜因子y
* @param px 平移因子x
* @param py 平移因子y
* @param recycle 是否回收
* @return 倾斜后的图片
*/
public static Bitmap skew(Bitmap src, float kx, float ky, float px, float py, boolean recycle) {
if (isEmptyBitmap(src)) return null;
Matrix matrix = new Matrix();
matrix.setSkew(kx, ky, px, py);
Bitmap ret = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true);
if (recycle && !src.isRecycled()) src.recycle();
return ret;
} /**
* 旋转图片
*
* @param src 源图片
* @param degrees 旋转角度
* @param px 旋转点横坐标
* @param py 旋转点纵坐标
* @return 旋转后的图片
*/
public static Bitmap rotate(Bitmap src, int degrees, float px, float py) {
return rotate(src, degrees, px, py, false);
} /**
* 旋转图片
*
* @param src 源图片
* @param degrees 旋转角度
* @param px 旋转点横坐标
* @param py 旋转点纵坐标
* @param recycle 是否回收
* @return 旋转后的图片
*/
public static Bitmap rotate(Bitmap src, int degrees, float px, float py, boolean recycle) {
if (isEmptyBitmap(src)) return null;
if (degrees == 0) return src;
Matrix matrix = new Matrix();
matrix.setRotate(degrees, px, py);
Bitmap ret = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true);
if (recycle && !src.isRecycled()) src.recycle();
return ret;
} /**
* 获取图片旋转角度
*
* @param filePath 文件路径
* @return 旋转角度
*/
public static int getRotateDegree(String filePath) {
int degree = 0;
try {
ExifInterface exifInterface = new ExifInterface(filePath);
int orientation = exifInterface.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
default:
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;
} /**
* 转为圆形图片
*
* @param src 源图片
* @return 圆形图片
*/
public static Bitmap toRound(Bitmap src) {
return toRound(src, false);
} /**
* 转为圆形图片
*
* @param src 源图片
* @param recycle 是否回收
* @return 圆形图片
*/
public static Bitmap toRound(Bitmap src, boolean recycle) {
if (isEmptyBitmap(src)) return null;
int width = src.getWidth();
int height = src.getHeight();
int radius = Math.min(width, height) >> 1;
Bitmap ret = Bitmap.createBitmap(width, height, src.getConfig());
Paint paint = new Paint();
Canvas canvas = new Canvas(ret);
Rect rect = new Rect(0, 0, width, height);
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
canvas.drawCircle(width >> 1, height >> 1, radius, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(src, rect, rect, paint);
if (recycle && !src.isRecycled()) src.recycle();
return ret;
} /**
* 转为圆角图片
*
* @param src 源图片
* @param radius 圆角的度数
* @return 圆角图片
*/
public static Bitmap toRoundCorner(Bitmap src, float radius) {
return toRoundCorner(src, radius, false);
} /**
* 转为圆角图片
*
* @param src 源图片
* @param radius 圆角的度数
* @param recycle 是否回收
* @return 圆角图片
*/
public static Bitmap toRoundCorner(Bitmap src, float radius, boolean recycle) {
if (null == src) return null;
int width = src.getWidth();
int height = src.getHeight();
Bitmap ret = Bitmap.createBitmap(width, height, src.getConfig());
Paint paint = new Paint();
Canvas canvas = new Canvas(ret);
Rect rect = new Rect(0, 0, width, height);
paint.setAntiAlias(true);
canvas.drawRoundRect(new RectF(rect), radius, radius, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(src, rect, rect, paint);
if (recycle && !src.isRecycled()) src.recycle();
return ret;
} /**
* 快速模糊
* <p>先缩小原图,对小图进行模糊,再放大回原先尺寸</p>
*
* @param src 源图片
* @param scale 缩放比例(0...1)
* @param radius 模糊半径
* @return 模糊后的图片
*/
public static Bitmap fastBlur(Bitmap src,
@FloatRange(from = 0, to = 1, fromInclusive = false) float scale,
@FloatRange(from = 0, to = 25, fromInclusive = false) float radius) {
return fastBlur(src, scale, radius, false);
} /**
* 快速模糊图片
* <p>先缩小原图,对小图进行模糊,再放大回原先尺寸</p>
*
* @param src 源图片
* @param scale 缩放比例(0...1)
* @param radius 模糊半径(0...25)
* @param recycle 是否回收
* @return 模糊后的图片
*/
public static Bitmap fastBlur(Bitmap src,
@FloatRange(from = 0, to = 1, fromInclusive = false) float scale,
@FloatRange(from = 0, to = 25, fromInclusive = false) float radius,
boolean recycle) {
if (isEmptyBitmap(src)) return null;
int width = src.getWidth();
int height = src.getHeight();
int scaleWidth = (int) (width * scale + 0.5f);
int scaleHeight = (int) (height * scale + 0.5f);
if (scaleWidth == 0 || scaleHeight == 0) return null;
Bitmap scaleBitmap = Bitmap.createScaledBitmap(src, scaleWidth, scaleHeight, true);
Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
Canvas canvas = new Canvas();
PorterDuffColorFilter filter = new PorterDuffColorFilter(
Color.TRANSPARENT, PorterDuff.Mode.SRC_ATOP);
paint.setColorFilter(filter);
canvas.scale(scale, scale);
canvas.drawBitmap(scaleBitmap, 0, 0, paint);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
scaleBitmap = renderScriptBlur(Utils.getContext(), scaleBitmap, radius);
} else {
scaleBitmap = stackBlur(scaleBitmap, (int) radius, recycle);
}
if (scale == 1) return scaleBitmap;
Bitmap ret = Bitmap.createScaledBitmap(scaleBitmap, width, height, true);
if (scaleBitmap != null && !scaleBitmap.isRecycled()) scaleBitmap.recycle();
if (recycle && !src.isRecycled()) src.recycle();
return ret;
} /**
* renderScript模糊图片
* <p>API大于17</p>
*
* @param context 上下文
* @param src 源图片
* @param radius 模糊半径(0...25)
* @return 模糊后的图片
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public static Bitmap renderScriptBlur(Context context, Bitmap src, @FloatRange(from = 0, to = 25, fromInclusive = false) float radius) {
if (isEmptyBitmap(src)) return null;
RenderScript rs = null;
try {
rs = RenderScript.create(context);
rs.setMessageHandler(new RenderScript.RSMessageHandler());
Allocation input = Allocation.createFromBitmap(rs, src, Allocation.MipmapControl.MIPMAP_NONE, Allocation
.USAGE_SCRIPT);
Allocation output = Allocation.createTyped(rs, input.getType());
ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
blurScript.setInput(input);
blurScript.setRadius(radius);
blurScript.forEach(output);
output.copyTo(src);
} finally {
if (rs != null) {
rs.destroy();
}
}
return src;
} /**
* stack模糊图片
*
* @param src 源图片
* @param radius 模糊半径
* @param recycle 是否回收
* @return stack模糊后的图片
*/
public static Bitmap stackBlur(Bitmap src, int radius, boolean recycle) {
Bitmap ret;
if (recycle) {
ret = src;
} else {
ret = src.copy(src.getConfig(), true);
} if (radius < 1) {
return null;
} int w = ret.getWidth();
int h = ret.getHeight(); int[] pix = new int[w * h];
ret.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;
}
}
ret.setPixels(pix, 0, w, 0, 0, w, h);
return ret;
} /**
* 添加颜色边框
*
* @param src 源图片
* @param borderWidth 边框宽度
* @param color 边框的颜色值
* @return 带颜色边框图
*/
public static Bitmap addFrame(Bitmap src, int borderWidth, int color) {
return addFrame(src, borderWidth, color, false);
} /**
* 添加颜色边框
*
* @param src 源图片
* @param borderWidth 边框宽度
* @param color 边框的颜色值
* @param recycle 是否回收
* @return 带颜色边框图
*/
public static Bitmap addFrame(Bitmap src, int borderWidth, int color, boolean recycle) {
if (isEmptyBitmap(src)) return null;
int doubleBorder = borderWidth << 1;
int newWidth = src.getWidth() + doubleBorder;
int newHeight = src.getHeight() + doubleBorder;
Bitmap ret = Bitmap.createBitmap(newWidth, newHeight, src.getConfig());
Canvas canvas = new Canvas(ret);
Rect rect = new Rect(0, 0, newWidth, newHeight);
Paint paint = new Paint();
paint.setColor(color);
paint.setStyle(Paint.Style.STROKE);
// setStrokeWidth是居中画的,所以要两倍的宽度才能画,否则有一半的宽度是空的
paint.setStrokeWidth(doubleBorder);
canvas.drawRect(rect, paint);
canvas.drawBitmap(src, borderWidth, borderWidth, null);
if (recycle && !src.isRecycled()) src.recycle();
return ret;
} /**
* 添加倒影
*
* @param src 源图片的
* @param reflectionHeight 倒影高度
* @return 带倒影图片
*/
public static Bitmap addReflection(Bitmap src, int reflectionHeight) {
return addReflection(src, reflectionHeight, false);
} /**
* 添加倒影
*
* @param src 源图片的
* @param reflectionHeight 倒影高度
* @param recycle 是否回收
* @return 带倒影图片
*/
public static Bitmap addReflection(Bitmap src, int reflectionHeight, boolean recycle) {
if (isEmptyBitmap(src)) return null;
// 原图与倒影之间的间距
final int REFLECTION_GAP = 0;
int srcWidth = src.getWidth();
int srcHeight = src.getHeight();
Matrix matrix = new Matrix();
matrix.preScale(1, -1);
Bitmap reflectionBitmap = Bitmap.createBitmap(src, 0, srcHeight - reflectionHeight,
srcWidth, reflectionHeight, matrix, false);
Bitmap ret = Bitmap.createBitmap(srcWidth, srcHeight + reflectionHeight, src.getConfig());
Canvas canvas = new Canvas(ret);
canvas.drawBitmap(src, 0, 0, null);
canvas.drawBitmap(reflectionBitmap, 0, srcHeight + REFLECTION_GAP, null);
Paint paint = new Paint();
paint.setAntiAlias(true);
LinearGradient shader = new LinearGradient(0, srcHeight,
0, ret.getHeight() + REFLECTION_GAP,
0x70FFFFFF, 0x00FFFFFF, Shader.TileMode.MIRROR);
paint.setShader(shader);
paint.setXfermode(new PorterDuffXfermode(android.graphics.PorterDuff.Mode.DST_IN));
canvas.drawRect(0, srcHeight + REFLECTION_GAP,
srcWidth, ret.getHeight(), paint);
if (!reflectionBitmap.isRecycled()) reflectionBitmap.recycle();
if (recycle && !src.isRecycled()) src.recycle();
return ret;
} /**
* 添加文字水印
*
* @param src 源图片
* @param content 水印文本
* @param textSize 水印字体大小
* @param color 水印字体颜色
* @param x 起始坐标x
* @param y 起始坐标y
* @return 带有文字水印的图片
*/
public static Bitmap addTextWatermark(Bitmap src, String content, int textSize, int color, float x,
float y) {
return addTextWatermark(src, content, textSize, color, x, y, false);
} /**
* 添加文字水印
*
* @param src 源图片
* @param content 水印文本
* @param textSize 水印字体大小
* @param color 水印字体颜色
* @param x 起始坐标x
* @param y 起始坐标y
* @param recycle 是否回收
* @return 带有文字水印的图片
*/
public static Bitmap addTextWatermark(Bitmap src, String content, float textSize, int color, float x,
float y, boolean recycle) {
if (isEmptyBitmap(src) || content == null) return null;
Bitmap ret = src.copy(src.getConfig(), true);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
Canvas canvas = new Canvas(ret);
paint.setColor(color);
paint.setTextSize(textSize);
Rect bounds = new Rect();
paint.getTextBounds(content, 0, content.length(), bounds);
canvas.drawText(content, x, y + textSize, paint);
if (recycle && !src.isRecycled()) src.recycle();
return ret;
} /**
* 添加图片水印
*
* @param src 源图片
* @param watermark 图片水印
* @param x 起始坐标x
* @param y 起始坐标y
* @param alpha 透明度
* @return 带有图片水印的图片
*/
public static Bitmap addImageWatermark(Bitmap src, Bitmap watermark, int x, int y, int alpha) {
return addImageWatermark(src, watermark, x, y, alpha, false);
} /**
* 添加图片水印
*
* @param src 源图片
* @param watermark 图片水印
* @param x 起始坐标x
* @param y 起始坐标y
* @param alpha 透明度
* @param recycle 是否回收
* @return 带有图片水印的图片
*/
public static Bitmap addImageWatermark(Bitmap src, Bitmap watermark, int x, int y, int alpha, boolean recycle) {
if (isEmptyBitmap(src)) return null;
Bitmap ret = src.copy(src.getConfig(), true);
if (!isEmptyBitmap(watermark)) {
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
Canvas canvas = new Canvas(ret);
paint.setAlpha(alpha);
canvas.drawBitmap(watermark, x, y, paint);
}
if (recycle && !src.isRecycled()) src.recycle();
return ret;
} /**
* 转为alpha位图
*
* @param src 源图片
* @return alpha位图
*/
public static Bitmap toAlpha(Bitmap src) {
return toAlpha(src, false);
} /**
* 转为alpha位图
*
* @param src 源图片
* @param recycle 是否回收
* @return alpha位图
*/
public static Bitmap toAlpha(Bitmap src, Boolean recycle) {
if (isEmptyBitmap(src)) return null;
Bitmap ret = src.extractAlpha();
if (recycle && !src.isRecycled()) src.recycle();
return ret;
} /**
* 转为灰度图片
*
* @param src 源图片
* @return 灰度图
*/
public static Bitmap toGray(Bitmap src) {
return toGray(src, false);
} /**
* 转为灰度图片
*
* @param src 源图片
* @param recycle 是否回收
* @return 灰度图
*/
public static Bitmap toGray(Bitmap src, boolean recycle) {
if (isEmptyBitmap(src)) return null;
Bitmap grayBitmap = Bitmap.createBitmap(src.getWidth(),
src.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(grayBitmap);
Paint paint = new Paint();
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.setSaturation(0);
ColorMatrixColorFilter colorMatrixColorFilter = new ColorMatrixColorFilter(colorMatrix);
paint.setColorFilter(colorMatrixColorFilter);
canvas.drawBitmap(src, 0, 0, paint);
if (recycle && !src.isRecycled()) src.recycle();
return grayBitmap;
} /**
* 保存图片
*
* @param src 源图片
* @param filePath 要保存到的文件路径
* @param format 格式
* @return {@code true}: 成功<br>{@code false}: 失败
*/
public static boolean save(Bitmap src, String filePath, CompressFormat format) {
return save(src, FileUtils.getFileByPath(filePath), format, false);
} /**
* 保存图片
*
* @param src 源图片
* @param file 要保存到的文件
* @param format 格式
* @return {@code true}: 成功<br>{@code false}: 失败
*/
public static boolean save(Bitmap src, File file, CompressFormat format) {
return save(src, file, format, false);
} /**
* 保存图片
*
* @param src 源图片
* @param filePath 要保存到的文件路径
* @param format 格式
* @param recycle 是否回收
* @return {@code true}: 成功<br>{@code false}: 失败
*/
public static boolean save(Bitmap src, String filePath, CompressFormat format, boolean recycle) {
return save(src, FileUtils.getFileByPath(filePath), format, recycle);
} /**
* 保存图片
*
* @param src 源图片
* @param file 要保存到的文件
* @param format 格式
* @param recycle 是否回收
* @return {@code true}: 成功<br>{@code false}: 失败
*/
public static boolean save(Bitmap src, File file, CompressFormat format, boolean recycle) {
if (isEmptyBitmap(src) || !FileUtils.createOrExistsFile(file)) return false;
System.out.println(src.getWidth() + ", " + src.getHeight());
OutputStream os = null;
boolean ret = false;
try {
os = new BufferedOutputStream(new FileOutputStream(file));
ret = src.compress(format, 100, os);
if (recycle && !src.isRecycled()) src.recycle();
} catch (IOException e) {
e.printStackTrace();
} finally {
CloseUtils.closeIO(os);
}
return ret;
} /**
* 根据文件名判断文件是否为图片
*
* @param file 文件
* @return {@code true}: 是<br>{@code false}: 否
*/
public static boolean isImage(File file) {
return file != null && isImage(file.getPath());
} /**
* 根据文件名判断文件是否为图片
*
* @param filePath 文件路径
* @return {@code true}: 是<br>{@code false}: 否
*/
public static boolean isImage(String filePath) {
String path = filePath.toUpperCase();
return path.endsWith(".PNG") || path.endsWith(".JPG")
|| path.endsWith(".JPEG") || path.endsWith(".BMP")
|| path.endsWith(".GIF");
} /**
* 获取图片类型
*
* @param filePath 文件路径
* @return 图片类型
*/
public static String getImageType(String filePath) {
return getImageType(FileUtils.getFileByPath(filePath));
} /**
* 获取图片类型
*
* @param file 文件
* @return 图片类型
*/
public static String getImageType(File file) {
if (file == null) return null;
InputStream is = null;
try {
is = new FileInputStream(file);
return getImageType(is);
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
CloseUtils.closeIO(is);
}
} /**
* 流获取图片类型
*
* @param is 图片输入流
* @return 图片类型
*/
public static String getImageType(InputStream is) {
if (is == null) return null;
try {
byte[] bytes = new byte[8];
return is.read(bytes, 0, 8) != -1 ? getImageType(bytes) : null;
} catch (IOException e) {
e.printStackTrace();
return null;
}
} /**
* 获取图片类型
*
* @param bytes bitmap的前8字节
* @return 图片类型
*/
public static String getImageType(byte[] bytes) {
if (isJPEG(bytes)) return "JPEG";
if (isGIF(bytes)) return "GIF";
if (isPNG(bytes)) return "PNG";
if (isBMP(bytes)) return "BMP";
return null;
} private static boolean isJPEG(byte[] b) {
return b.length >= 2
&& (b[0] == (byte) 0xFF) && (b[1] == (byte) 0xD8);
} private static boolean isGIF(byte[] b) {
return b.length >= 6
&& b[0] == 'G' && b[1] == 'I'
&& b[2] == 'F' && b[3] == '8'
&& (b[4] == '7' || b[4] == '9') && b[5] == 'a';
} private static boolean isPNG(byte[] b) {
return b.length >= 8
&& (b[0] == (byte) 137 && b[1] == (byte) 80
&& b[2] == (byte) 78 && b[3] == (byte) 71
&& b[4] == (byte) 13 && b[5] == (byte) 10
&& b[6] == (byte) 26 && b[7] == (byte) 10);
} private static boolean isBMP(byte[] b) {
return b.length >= 2
&& (b[0] == 0x42) && (b[1] == 0x4d);
} /**
* 判断bitmap对象是否为空
*
* @param src 源图片
* @return {@code true}: 是<br>{@code false}: 否
*/
private static boolean isEmptyBitmap(Bitmap src) {
return src == null || src.getWidth() == 0 || src.getHeight() == 0;
} /******************************~~~~~~~~~ 下方和压缩有关 ~~~~~~~~~******************************/ /**
* 按缩放压缩
*
* @param src 源图片
* @param newWidth 新宽度
* @param newHeight 新高度
* @return 缩放压缩后的图片
*/
public static Bitmap compressByScale(Bitmap src, int newWidth, int newHeight) {
return scale(src, newWidth, newHeight, false);
} /**
* 按缩放压缩
*
* @param src 源图片
* @param newWidth 新宽度
* @param newHeight 新高度
* @param recycle 是否回收
* @return 缩放压缩后的图片
*/
public static Bitmap compressByScale(Bitmap src, int newWidth, int newHeight, boolean recycle) {
return scale(src, newWidth, newHeight, recycle);
} /**
* 按缩放压缩
*
* @param src 源图片
* @param scaleWidth 缩放宽度倍数
* @param scaleHeight 缩放高度倍数
* @return 缩放压缩后的图片
*/
public static Bitmap compressByScale(Bitmap src, float scaleWidth, float scaleHeight) {
return scale(src, scaleWidth, scaleHeight, false);
} /**
* 按缩放压缩
*
* @param src 源图片
* @param scaleWidth 缩放宽度倍数
* @param scaleHeight 缩放高度倍数
* @param recycle 是否回收
* @return 缩放压缩后的图片
*/
public static Bitmap compressByScale(Bitmap src, float scaleWidth, float scaleHeight, boolean recycle) {
return scale(src, scaleWidth, scaleHeight, recycle);
} /**
* 按质量压缩
*
* @param src 源图片
* @param quality 质量
* @return 质量压缩后的图片
*/
public static Bitmap compressByQuality(Bitmap src, @IntRange(from = 0, to = 100) int quality) {
return compressByQuality(src, quality, false);
} /**
* 按质量压缩
*
* @param src 源图片
* @param quality 质量
* @param recycle 是否回收
* @return 质量压缩后的图片
*/
public static Bitmap compressByQuality(Bitmap src, @IntRange(from = 0, to = 100) int quality, boolean recycle) {
if (isEmptyBitmap(src)) return null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
src.compress(Bitmap.CompressFormat.JPEG, quality, baos);
byte[] bytes = baos.toByteArray();
if (recycle && !src.isRecycled()) src.recycle();
return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
} /**
* 按质量压缩
*
* @param src 源图片
* @param maxByteSize 允许最大值字节数
* @return 质量压缩压缩过的图片
*/
public static Bitmap compressByQuality(Bitmap src, long maxByteSize) {
return compressByQuality(src, maxByteSize, false);
} /**
* 按质量压缩
*
* @param src 源图片
* @param maxByteSize 允许最大值字节数
* @param recycle 是否回收
* @return 质量压缩压缩过的图片
*/
public static Bitmap compressByQuality(Bitmap src, long maxByteSize, boolean recycle) {
if (isEmptyBitmap(src) || maxByteSize <= 0) return null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int quality = 100;
src.compress(CompressFormat.JPEG, quality, baos);
while (baos.toByteArray().length > maxByteSize && quality > 0) {
baos.reset();
src.compress(CompressFormat.JPEG, quality -= 5, baos);
}
if (quality < 0) return null;
byte[] bytes = baos.toByteArray();
if (recycle && !src.isRecycled()) src.recycle();
return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
} /**
* 按采样大小压缩
*
* @param src 源图片
* @param sampleSize 采样率大小
* @return 按采样率压缩后的图片
*/
public static Bitmap compressBySampleSize(Bitmap src, int sampleSize) {
return compressBySampleSize(src, sampleSize, false);
} /**
* 按采样大小压缩
*
* @param src 源图片
* @param sampleSize 采样率大小
* @param recycle 是否回收
* @return 按采样率压缩后的图片
*/
public static Bitmap compressBySampleSize(Bitmap src, int sampleSize, boolean recycle) {
if (isEmptyBitmap(src)) return null;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = sampleSize;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
src.compress(Bitmap.CompressFormat.JPEG, 100, baos);
byte[] bytes = baos.toByteArray();
if (recycle && !src.isRecycled()) src.recycle();
return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
}
}
http://www.jianshu.com/p/72494773aace
QRcode
package com.summer.common.utils.img; import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.geom.Ellipse2D;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map; import javax.imageio.ImageIO; import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageConfig;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; public class QRUtils { public static void main(String[] args) throws IOException, WriterException {
String format = "jpg";
int QRCODE_SIZE = 1000; MatrixToImageWriter.toBufferedImage(encodePDF17("haha", QRCODE_SIZE));
// writeQR("haha", 102, "C:\\haha.jpg", format);
// BufferedImage ImageOne = getQRImage("houpin.1688.com", QRCODE_SIZE);
MatrixToImageWriter.writeToPath(encodePDF17("haha", QRCODE_SIZE), format, Paths.get("C:\\haha.jpg")); //对第二张图片做相同的处理
// File fileTwo = new File("c:\\1.jpg");
// BufferedImage ImageTwo = ImageIO.read(fileTwo);
// BufferedImage ImageNew = createQRImageWithCirclePortrait(ImageOne, ImageTwo);
// ImageIO.write(ImageNew, format, new File("c://123-A.jpg"));
// ImageIO.write( createQRImageWithSquarePortrait(ImageOne, ImageTwo), format, new File("c://123-B.jpg"));
// ImageIO.write( createQRCodeBitmapWithRoundRectanglePortrait(ImageOne, ImageTwo), format, new File("c://123-C.jpg"));
} // 计算边框比例
private static final int BORDER_LEMON = 30; /**
* 如果小于1像素,返回1px
* @param size
* @return
*/
private static int calBorderPix(int size) {
int border = size /BORDER_LEMON;
return border > 1 ? border : 1;
} /**
* 把source拷贝到RGB BufferedImage
* @param source
* @return
*/
private static BufferedImage copyToRGBImage(BufferedImage source) {
int width = source.getWidth(), height = source.getWidth();
BufferedImage imageNew = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// 从QR中读取RGB
int[] qrImageArray = new int[width * height];
qrImageArray = source.getRGB(0,0,width,height,qrImageArray,0,width);
// QR写入
imageNew.setRGB(0, 0, width, height, qrImageArray, 0, width);
return imageNew;
} /**
* 为QR码添加头像,方形,带白相框
* @param qr
* @param portrait 头像
* @return
*/
public static BufferedImage createQRImageWithSquarePortrait(BufferedImage qr, BufferedImage portrait) {
BufferedImage imageNew = copyToRGBImage(qr);
// portrait
int portraitWidth = portrait.getWidth(), portraitHeight = portrait.getHeight();
// 框偏移
int wBorder = calBorderPix(portraitWidth), hBorder = calBorderPix(portraitHeight);
// 位置计算
int left = (imageNew.getWidth()-portraitWidth) / 2, top = (imageNew.getHeight()-portraitHeight) / 2;
// 先绘制白色背景
// =
for (int x=0; x<portraitWidth; x++) {
for (int y=0; y<hBorder; y++) {
imageNew.setRGB(left+x, top+y, Color.white.getRGB());
imageNew.setRGB(left+x, top+y+portraitHeight - hBorder, Color.white.getRGB());
}
}
//||
for (int x=0; x<wBorder; x++) {
for (int y=0; y<portraitHeight - hBorder; y++) {
imageNew.setRGB(left+x, top+y+hBorder, Color.white.getRGB());
imageNew.setRGB(left+x+portraitWidth-wBorder, top+y+hBorder, Color.white.getRGB());
}
}
// 贴上头像
addPortraitImg(imageNew, portrait, left + wBorder, top + hBorder, wBorder,hBorder,portraitWidth-2*wBorder,portraitHeight-2*hBorder);
return imageNew;
} private static void addPortraitImg(BufferedImage setImg,BufferedImage getImg,
int sX, int sY,
int gX, int gY,
int gW, int gH
) {
int[] portraitArray = new int[gW * gH];
portraitArray = getImg.getRGB(gX, gY, gW, gH,portraitArray,0, gW);
setImg.setRGB(sX, sY, gW, gH, portraitArray, 0, gW);
} /**
* 产生一个平滑,抗锯齿的Graphics2D
* @param img
* @return
*/
private static Graphics2D createGraphics(BufferedImage img) {
Graphics2D g2d = img.createGraphics();
g2d.setStroke(new BasicStroke(1));
// 使用 setRenderingHint 设置抗锯齿
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
return g2d;
} /**
* 为QR码添加头像,圆形,带白相框
* @param qr
* @param portrait
* @return
* @throws IOException
*/
public static BufferedImage createQRImageWithCirclePortrait(BufferedImage qr, BufferedImage portrait) throws IOException {
BufferedImage imageNew = copyToRGBImage(qr);
// portrait
int portraitWidth = portrait.getWidth(), portraitHeight = portrait.getHeight();
// 框偏移
int wBorder = portraitWidth/ 30, hBorder = portraitHeight/ 30;
// 位置计算
int left = (imageNew.getWidth() - portraitWidth) / 2, top = (imageNew.getHeight() - portraitHeight) / 2;
{ // 先绘制白色背景
BufferedImage circleBorder = new BufferedImage(portraitWidth + 2*wBorder, portraitWidth + 2*hBorder, BufferedImage.TYPE_INT_RGB);
addPortraitImg(circleBorder, qr, 0, 0, left-wBorder,top-hBorder, portraitWidth+2*wBorder,portraitHeight+2*hBorder);
Graphics2D g2d = createGraphics(circleBorder);
g2d.setColor(Color.white);
g2d.fill( new Ellipse2D.Double(0,0,portraitWidth+ 2*wBorder,portraitHeight+ 2*hBorder));
g2d.dispose();
addPortraitImg(imageNew, circleBorder, left-wBorder,top-hBorder, 0,0,portraitWidth+ 2*wBorder,portraitHeight+ 2*hBorder);
}
{ // 在画
BufferedImage circleImage = new BufferedImage(portraitWidth, portraitWidth, BufferedImage.TYPE_INT_RGB);
addPortraitImg(circleImage, imageNew, 0, 0, left,top,portraitWidth,portraitHeight);
Graphics2D g2d = createGraphics(circleImage);
g2d.setClip(new Ellipse2D.Double(0,0,portraitWidth,portraitHeight));
g2d.drawImage(portrait, 0, 0, null);
g2d.dispose();
addPortraitImg(imageNew, circleImage, left, top, 0,0,portraitWidth,portraitHeight);
}
return imageNew;
} /**
* 为QR码添加头像,圆角,带白相框
* @param qr
* @param portrait
* @return
* @throws IOException
*/
public static BufferedImage createQRCodeBitmapWithRoundRectanglePortrait(BufferedImage qr, BufferedImage portrait) throws IOException {
BufferedImage imageNew = copyToRGBImage(qr);
// portrait
int portraitWidth = portrait.getWidth(), portraitHeight = portrait.getHeight();
// 框偏移
int wBorder = portraitWidth/ 30, hBorder = portraitHeight/ 30;
// 位置计算
int left = (imageNew.getWidth() - portraitWidth) / 2, top = (imageNew.getHeight() - portraitHeight) / 2;
{ // 先绘制白色背景
BufferedImage circleBorder = new BufferedImage(portraitWidth + 2*wBorder, portraitWidth + 2*hBorder, BufferedImage.TYPE_INT_RGB);
addPortraitImg(circleBorder, qr, 0, 0, left-wBorder,top-hBorder, portraitWidth+2*wBorder,portraitHeight+2*hBorder);
Graphics2D g2d = createGraphics(circleBorder);
g2d.setColor(Color.white);
g2d.fill( new RoundRectangle2D.Double(0,0,portraitWidth+ 2*wBorder,portraitHeight+ 2*hBorder, wBorder *8 , hBorder *8));
g2d.dispose();
addPortraitImg(imageNew, circleBorder, left-wBorder,top-hBorder, 0,0,portraitWidth+ 2*wBorder,portraitHeight+ 2*hBorder);
}
BufferedImage circleImage = new BufferedImage(portraitWidth, portraitWidth, BufferedImage.TYPE_INT_RGB);
addPortraitImg(circleImage, imageNew, 0, 0, left,top,portraitWidth,portraitHeight);
Graphics2D g2d = createGraphics(circleImage);
g2d.setClip(new RoundRectangle2D.Double(0,0,portraitWidth,portraitHeight, wBorder *8 , hBorder *8));
g2d.drawImage(portrait, 0, 0, null);
g2d.dispose();
addPortraitImg(imageNew, circleImage, left, top, 0,0,portraitWidth,portraitHeight); return imageNew;
} /**
* 返回透明的 <code>BufferedImage</code>.
*
* @param image image to be made transparent
* @param transperancy value of the transparency
* @return transparent <code>BufferedImage</code>
*/
public static BufferedImage getTransparentImage(BufferedImage image, float transperancy) {
// Create the image using the
BufferedImage transparentImage = new BufferedImage(image.getWidth(), image.getHeight(), Transparency.TRANSLUCENT);
// Get the images graphics.
Graphics2D g = transparentImage.createGraphics();
// Set the graphics composite to Alpha.
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, transperancy));
// Draw the passed image into the new transparent image.
g.drawImage(image, null, 0, 0);
// Free all system resources.
g.dispose();
// Return the image
return transparentImage;
} /**
* 写入路径的QR
* @param content
* @param sideLength
* @param outfile
* @param format
* @throws IOException
* @throws WriterException
*/
public static void writeQR(String content, int sideLength, String outfile, String format) throws IOException, WriterException {
BitMatrix matrix = encodeQR(content, sideLength);
MatrixToImageWriter.writeToPath(matrix, format, Paths.get(outfile));
} public static BufferedImage getQRImage(String content, int sideLength) throws WriterException {
BitMatrix matrix = encodeQR(content, sideLength);
return MatrixToImageWriter.toBufferedImage(matrix);
} public static BufferedImage getQRImage(String content, int sideLength, MatrixToImageConfig config) throws WriterException {
BitMatrix matrix = encodeQR(content, sideLength);
return MatrixToImageWriter.toBufferedImage(matrix, config);
} /**
* 将BufferedImage转为bytes
* @param image
* @param format
* @return
* @throws IOException
*/
public static byte[] convertBufferedImageToBytes(BufferedImage image, String format) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ImageIO.write(image, format, out);
return out.toByteArray();
} /**
* 将bytes转为BufferedImage
* @param bytes
* @return
* @throws IOException
*/
public static BufferedImage convertBytesToBufferedImage(byte[] bytes) throws IOException {
ByteArrayInputStream in = new ByteArrayInputStream(bytes); //作为输入流;
return ImageIO.read(in); //将in作为输入流,读取图片存入image中,而这里in可以为ByteArrayInputStream();
} /**
* QR编码
* @param content
* @param sideLength
* @return
* @throws WriterException
*/
public static BitMatrix encodeQR(String content, int sideLength) throws WriterException {
// 用于设置QR二维码参数
Hashtable<EncodeHintType, Object> qrParam = new Hashtable<EncodeHintType, Object>();
// 设置QR二维码的纠错级别——这里选择最高H级别
qrParam.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
// 设置编码方式
qrParam.put(EncodeHintType.CHARACTER_SET, "UTF-8");
MultiFormatWriter barcodeWriter = new MultiFormatWriter();
// 编码内容,编码类型,生成图片宽度,生成图片高度,设置参数
BitMatrix matrix = barcodeWriter.encode(content, BarcodeFormat.QR_CODE, sideLength, sideLength, qrParam);
return matrix;
} /**
*
* @param content
* @param sideLength
* @return
* @throws WriterException
*/
public static BitMatrix encodePDF17(String content, int sideLength) throws WriterException {
// 用于设置QR二维码参数
Hashtable<EncodeHintType, Object> qrParam = new Hashtable<EncodeHintType, Object>();
// 设置QR二维码的纠错级别——这里选择最高H级别
qrParam.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
// 设置编码方式
qrParam.put(EncodeHintType.CHARACTER_SET, "UTF-8");
MultiFormatWriter barcodeWriter = new MultiFormatWriter();
// 编码内容,编码类型,生成图片宽度,生成图片高度,设置参数
//BarcodeFormat.DATA_MATRIX
BitMatrix matrix = barcodeWriter.encode(content, BarcodeFormat.PDF_417, sideLength, sideLength, qrParam);
return matrix;
} }
ImageUtils的更多相关文章
- 图片处理工具类 - ImageUtils.java
纯JAVA实现的图片处理工具类,提供图片的裁剪.压缩.获取尺寸.制作圆角等方法. 源码如下:(点击下载 -ImageUtils.java .FolderUtils.java .commons-io-2 ...
- android ImageUtils 图片处理工具类
/** * 加入文字到图片.相似水印文字. * @param gContext * @param gResId * @param gText * @return */ public static Bi ...
- three.js笔记
/*** 场景(scene) ***/ var scene = new THREE.Scene(); // 创建场景 scene.add(x); // 插入场景 /*** 相机(camera) *** ...
- Threejs中的材质贴图
最近项目需要折腾three.js,有关three.js几点说明 1.作用 threejs适合创建简单的模型视图 2.对于复杂的模型图(如:室内模型图)需要美术3D制作,前端导成特定格式文件(如*.mt ...
- java工具类
1.HttpUtilsHttp网络工具类,主要包括httpGet.httpPost以及http参数相关方法,以httpGet为例:static HttpResponse httpGet(HttpReq ...
- 【webGL】threejs常用的api
/*** 场景(scene) ***/ var scene = new THREE.Scene(); // 创建场景 scene.add(x); // 插入场景 /*** 相机(camera) *** ...
- 【webGl】threejs实现一个简单的动画-弹跳的小球
在这里,我们将动态画面简称为动画(animation).正如动画片的原理一样,动画的本质是利用了人眼的视觉暂留特性,快速地变换画面,从而产生物体在运动的假象.而对于Three.js程序而言,动画的实现 ...
- three.js贴图
使用图像作为材质.这时候,就需要导入图像作为纹理贴图,并添加到相应的材质中 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//E ...
- 基于WebGL的三维地形渲染
1.生成WebMap页面 #!/usr/bin/env python # -*- coding: utf-8 -*- import subprocess from jinja2 import Envi ...
随机推荐
- EOJ 3213 向右看齐
题目描述 N 头奶牛被紧急动员起来了,它们排成了一条长列.从左向右看,排在第 i 个位置的奶牛身高为 Hi.约翰一声令下,所有奶牛向右看齐.假设每头奶牛只能看到比自己高的牛.请问它们各自看到的最近的一 ...
- Java 集合类的细节
java集合类 1.Collection,Map层次图 2.Collection接口 list 存放有序且允许重复的集合的接口 这里的有序是指存入顺序和取出顺序相同.子类有:{ ArrayList,L ...
- performSelector与objc_msgSend
- perform:(SEL)aSelector { if (aSelector) return objc_msgSend(self, aSelector); else return [self er ...
- python3 django动态分页引发的list切片下标越界问题
起先是扒了一个包,动态分页的,但这个包分页之前要加载全部的数据,我这东西后台是个爬虫,不一定浏览的完所以这么做有点浪费资源,于是我改造了一下. # :param obj_count: 获得 条目总数# ...
- 第二章 Python数据类型详解
基本概念 迭代(iteration):如果给定一个list或tuple,我们可以通过for循环来遍历,这种遍历我们称为迭代(iteration) 可变:value改变,id不变,可变类型是不可hash ...
- DHCPv6,IPv6的有状态自动配置
DHCPv6,IPv6的有状态自动配置 DHCPv6的工作原理与DHCPv4极其相似,但有一个明显的差别,那就是支持IPV6新增的编址方案.DHCP提供了一些自动配置没有的选项.在自动配置中,根本没有 ...
- luoguP4238 【模板】多项式求逆 NTT
Code: #include <bits/stdc++.h> #define N 1000010 #define mod 998244353 #define setIO(s) freope ...
- 系统中 CPU 时间片是多久
Windows 系统中线程轮转时间也就是时间片大约是20ms,如果某个线程所需要的时间小于20ms,那么不到20ms就会切换到其他线程:如果一个线程所需的时间超过20ms,系统也最多只给20ms,除非 ...
- win10x64位系统中nodejs的安装和配置
官网http://nodejs.cn/download/ 2.下载完成后点击安装包 下一步,安装过的,这里根据自己的需求选择.选择第直接正常安装. 这一步是安装的内容,第一个是安装所有的模块,建议全部 ...
- BZOJ 3203 [SDOI2013]保护出题人 (凸包+三分)
洛谷传送门 题目大意:太长略 每新加入一个僵尸,容易得到方程$ans[i]=max{\frac{sum_{i}-sum_{j-1}}{s_{i}+d(i-j)}}$ 即从头开始每一段僵尸都需要在规定距 ...