前言 最近总结了Java处理图片的一些方法,特此分享下。
其中主要涉及到2种不同的类包。
一种是Java自带的ImageIO
、Graphics2D
、BufferedImage
等类的使用;另一种是使用了net.coobird.thumbnailator
包下的图片相关类。
net.coobird.thumbnailator
包中对图片的操作底层其实也使用了Java自带的图片操作类相关方法,这儿不做过多讨论,有兴趣的可以直接看下它的源码。
PS: net.coobird.thumbnailator
这个工具包在2014年12月 0.4.8版本后就再也没有维护过了。
1 2 3 4 5 <dependency > <groupId > net.coobird</groupId > <artifactId > thumbnailator</artifactId > <version > 0.4.8</version > </dependency >
正文 我们来看一下相关的处理方法。
获取图片真实格式(与后缀无关) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public static String getExtension (String filePath) { File file = new File(filePath); if (!file.exists()) { throw new RuntimeException("图片地址不正确,请传入正确的图片地址!!!" ); } String format = "" ; try (ImageInputStream iis = ImageIO.createImageInputStream(file)) { Iterator<ImageReader> iter = ImageIO.getImageReaders(iis); if (iter.hasNext()) { format = iter.next().getFormatName(); } } catch (IOException e) { throw new RuntimeException("获取图片真实格式时出现异常!!" + e); } return format; }
该方法可以获得图片的真实格式,这个格式和图片的后缀无关。
其返回的格式为大写,如JPEG,PNG等。
PS: jpg和jpeg类型的图片返回的格式均为JPEG。
图片彩色转为黑白 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public static String gray (String sourceImagePath, String targetImagePath) throws IOException { String sourceFormat = getExtension(sourceImagePath).toLowerCase(); BufferedImage src = ImageIO.read(new File(sourceImagePath)); ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY); ColorConvertOp op = new ColorConvertOp(cs, null ); src = op.filter(src, null ); String targetFile = targetImagePath + "/" + getUUID() + "." + sourceFormat; ImageIO.write(src, sourceFormat, new File(targetFile)); return targetFile; }
该方法可对彩色图片进行黑白处理。
图片格式转换 A方法:使用Thumbnails进行图片格式转换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public static String convertA (String targetFormat, String sourceFilePath, String targetFilePath) throws IOException { if (isBlank(targetFormat) || isBlank(sourceFilePath) || isBlank(targetFilePath)) { throw new RuntimeException("参数错误!" ); } String sourceFormat = getExtension(sourceFilePath).toLowerCase(); String suffixFormat = sourceFilePath.substring(sourceFilePath.lastIndexOf("." ) + 1 ).toLowerCase(); String targetFormatStr = targetFormat.toLowerCase(); if (sourceFormat.equals(targetFormatStr) && suffixFormat.equals(targetFormatStr)) { return sourceFilePath; } String targetFile = targetFilePath + "/" + getUUID() + "." + targetFormatStr; Thumbnails.of(sourceFilePath).scale(1.0f ).outputFormat(targetFormatStr).toFile(targetFile); return targetFile; }
B方法:使用ImageIO进行图片格式转换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public static String convertB (String targetFormat, String sourceFilePath, String targetFilePath) throws IOException { if (isBlank(targetFormat) || isBlank(sourceFilePath) || isBlank(targetFilePath)) { throw new RuntimeException("参数错误!" ); } String sourceFormat = getExtension(sourceFilePath).toLowerCase(); String suffixFormat = sourceFilePath.substring(sourceFilePath.lastIndexOf("." ) + 1 ).toLowerCase(); String targetFormatStr = targetFormat.toLowerCase(); if (sourceFormat.equals(targetFormatStr) && suffixFormat.equals(targetFormatStr)) { return sourceFilePath; } File file = new File(sourceFilePath); String targetFile = targetFilePath + "/" + getUUID() + "." + targetFormatStr; BufferedImage src = ImageIO.read(file); ImageIO.write(src, targetFormatStr, new File(targetFile)); return targetFile; }
两种方法都可以实现图片格式转换。
图片旋转相关 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public static String rotate (String sourceImage, String targetImagePath,double angle) throws IOException { if (isBlank(sourceImage)||isBlank(targetImagePath)){ throw new RuntimeException("参数错误!" ); } String sourceFormat = getExtension(sourceImage).toLowerCase(); String targetFile = targetImagePath + "/" + getUUID() + "." + sourceFormat; Thumbnails.of(sourceImage).scale(1.0f ).rotate(angle).outputFormat(sourceFormat).toFile(targetFile); return targetFile; }
这儿我们使用Thumbnails工具来实现图片的旋转。
图片缩放相关 图片缩放有两种类型,一种是根据比例进行缩放,另一种是按照大小进行缩放。
我们分别来看一下。
A方法:使用Thumbnails进行图片比例缩放和大小缩放
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 public static String scale (String sourceImage, String targetImagePath, double scaleWidth, double scaleHeight) throws IOException { if (isBlank(sourceImage) || isBlank(targetImagePath) || scaleWidth <= 0 || scaleHeight <= 0 ) { throw new RuntimeException("参数错误!" ); } String sourceFormat = getExtension(sourceImage).toLowerCase(); String targetFile = targetImagePath + "/" + getUUID() + "." + sourceFormat; Thumbnails.of(sourceImage).scale(scaleWidth, scaleHeight).toFile(targetFile); return targetFile; } public static String size (String sourceImage, String targetImagePath, int width, int height, boolean keep) throws IOException { if (isBlank(sourceImage) || isBlank(targetImagePath) || width <= 0 || height <= 0 ) { throw new RuntimeException("参数错误!" ); } String sourceFormat = getExtension(sourceImage).toLowerCase(); String targetFile = targetImagePath + "/" + getUUID() + "." + sourceFormat; Thumbnails.of(sourceImage).size(width, height).keepAspectRatio(keep).toFile(targetFile); return targetFile; }
B方法:使用ImageIO进行图片比例缩放和大小缩放
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 public static String scaleB (String sourceImage, String targetImagePath, double scaleWidth, double scaleHeight) throws IOException { if (isBlank(sourceImage) || isBlank(targetImagePath) || scaleWidth <= 0 || scaleHeight <= 0 ) { throw new RuntimeException("参数错误!" ); } String sourceFormat = getExtension(sourceImage).toLowerCase(); String targetFile = targetImagePath + "/" + getUUID() + "." + sourceFormat; BufferedImage src = ImageIO.read(new File(sourceImage)); int width = src.getWidth(); int height = src.getHeight(); width = (int ) (width * scaleWidth); height = (int ) (height * scaleHeight); Image image = src.getScaledInstance(width, height, Image.SCALE_DEFAULT); BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics g = tag.getGraphics(); g.drawImage(image, 0 , 0 , null ); g.dispose(); ImageIO.write(tag, sourceFormat, new File(targetFile)); return targetFile; } public static String sizeB (String sourceImage, String targetImagePath, int width, int height, boolean keep) throws IOException { if (isBlank(sourceImage) || isBlank(targetImagePath) || width <= 0 || height <= 0 ) { throw new RuntimeException("参数错误!" ); } String sourceFormat = getExtension(sourceImage).toLowerCase(); String targetFile = targetImagePath + "/" + getUUID() + "." + sourceFormat; double ratio = 0.0 ; BufferedImage bi = ImageIO.read(new File(sourceImage)); Image image = bi.getScaledInstance(width, height, Image.SCALE_SMOOTH); if ((bi.getHeight() > height) || (bi.getWidth() > width)) { if (bi.getHeight() > bi.getWidth()) { ratio = (double ) (height / bi.getHeight()); } else { ratio = (double ) (width / bi.getWidth()); } AffineTransformOp op = new AffineTransformOp(AffineTransform.getScaleInstance(ratio, ratio), null ); image = op.filter(bi, null ); } if (keep) { BufferedImage temp = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D g = temp.createGraphics(); g.setColor(Color.WHITE); g.fillRect(0 , 0 , width, height); if (width == image.getWidth(null )) { g.drawImage(image, 0 , (height - image.getHeight(null )) / 2 , image.getWidth(null ), image.getHeight(null ),Color.WHITE, null ); } else { g.drawImage(image, (width - image.getWidth(null )) / 2 , 0 , image.getWidth(null ), image.getHeight(null ),Color.WHITE, null ); } g.dispose(); image = temp; } ImageIO.write((BufferedImage) image, sourceFormat, new File(targetFile)); return targetFile; }
我们一般使用Thumbnails工具类提供的缩放来处理即可,这是比较常用的。
图片压缩相关 图片压缩也大致有两种形式,一种是指定图片质量系数进行压缩,另一种是根据大小进行压缩。
PS:这儿对于根据大小进行压缩的意思是图片压缩后大小不会超过指定值,而不是压缩到指定值,因为理论上压缩到指定值是十分困难的,也是不必要的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 public static String compress (String sourceImage, String targetImagePath, float quality) throws IOException { if (isBlank(sourceImage) || isBlank(targetImagePath) || quality < 0 || quality > 1 ) { throw new RuntimeException("参数错误!" ); } String sourceFormat = getExtension(sourceImage).toLowerCase(); String targetFile = targetImagePath + "/" + getUUID() + "." + sourceFormat; Thumbnails.of(sourceImage).scale(1.0f ).outputQuality(quality).outputFormat(sourceFormat).toFile(targetFile); return targetFile; } public static String compress (String sourceImage, String targetImagePath, long maxSize) throws IOException { if (isBlank(sourceImage) || isBlank(targetImagePath) || maxSize <= 0 ) { throw new RuntimeException("参数错误!" ); } String sourceFormat = getExtension(sourceImage).toLowerCase(); String targetFile = targetImagePath + "/" + getUUID() + "." + sourceFormat; File source = new File(sourceImage); long size = source.length(); if (size <= maxSize) { return sourceImage; } double scale = (double ) (maxSize / size); Thumbnails.of(source).scale(1.0f ).outputQuality(scale).outputFormat(sourceFormat).toFile(targetFile); return targetFile; }
我们同样使用了Thumbnails工具类进行处理。
图片切割相关 切割主要有两种形式,一种是在原图指定位置切割一张指定大小的图片,另一种是把图片切割成若干份。
A形式:从原图指定位置切割(裁剪)一张小图片
可以通过Thumbnails工具类或者ImageIO实现,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 public static String cutA (String sourceImage, String targetImagePath, Positions positions, int width, int height) throws IOException { if (isBlank(sourceImage) || isBlank(targetImagePath) || positions == null || width <= 0 || height <= 0 ) { throw new RuntimeException("参数错误!" ); } String sourceFormat = getExtension(sourceImage).toLowerCase(); String targetFile = targetImagePath + "/" + getUUID() + "." + sourceFormat; Thumbnails.of(sourceImage).scale(1.0f ).sourceRegion(positions, width, height).outputFormat(sourceFormat).toFile(targetFile); return targetFile; } public static String cutB (String sourceImage, String targetImagePath, int x, int y, int width, int height) throws IOException { if (isBlank(sourceImage) || isBlank(targetImagePath) || x < 0 || y < 0 || width <= 0 || height <= 0 ) { throw new RuntimeException("参数错误!" ); } String sourceFormat = getExtension(sourceImage).toLowerCase(); String targetFile = targetImagePath + "/" + getUUID() + "." + sourceFormat; Thumbnails.of(sourceImage).scale(1.0f ).sourceRegion(x, y, width, height).outputFormat(sourceFormat).toFile(targetFile); return targetFile; } public static String cutC (String sourceImage, String targetImagePath, int x, int y, int width, int height) throws IOException { if (isBlank(sourceImage) || isBlank(targetImagePath) || x < 0 || y < 0 || width <= 0 || height <= 0 ) { throw new RuntimeException("参数错误!" ); } String sourceFormat = getExtension(sourceImage).toLowerCase(); String targetFile = targetImagePath + "/" + getUUID() + "." + sourceFormat; BufferedImage bi = ImageIO.read(new File(sourceImage)); int srcWidth = bi.getHeight(); int srcHeight = bi.getWidth(); Image image = bi.getScaledInstance(srcWidth, srcHeight,Image.SCALE_DEFAULT); ImageFilter cropFilter = new CropImageFilter(x, y, width, height); Image img = Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(image.getSource(), cropFilter)); BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics g = tag.getGraphics(); g.drawImage(img, 0 , 0 , width, height, null ); g.dispose(); ImageIO.write(tag, sourceFormat, new File(targetFile)); return targetFile; }
B形式:将原图分割成若干小图片
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 public static String[][] splitImage(String filePath, int rows, int cols, String splitImageBasePath) throws IOException { if (rows <= 0 || cols <= 0 || isBlank(splitImageBasePath) || isBlank(filePath)) { throw new RuntimeException("参数错误!!" ); } String imageType = getExtension(filePath); imageType = imageType.toLowerCase(); if (!(JPG.equals(imageType) || JPEG.equals(imageType) || PNG.equals(imageType))) { throw new RuntimeException("请使用jpg、jpeg、png类型的图片" ); } File file = new File(filePath); String fileName = file.getName(); String fileNamePrefix = fileName.substring(0 , fileName.lastIndexOf("." )); FileInputStream fis = new FileInputStream(file); BufferedImage image = ImageIO.read(fis); int chunkWidth = image.getWidth() / cols; int chunkHeight = image.getHeight() / rows; BufferedImage[][] imgs = new BufferedImage[rows][cols]; for (int x = 0 ; x < rows; x++) { for (int y = 0 ; y < cols; y++) { imgs[x][y] = new BufferedImage(chunkWidth, chunkHeight, image.getType()); Graphics2D gr = imgs[x][y].createGraphics(); gr.drawImage(image, 0 , 0 , chunkWidth, chunkHeight, chunkWidth * y, chunkHeight * x, chunkWidth * y + chunkWidth, chunkHeight * x + chunkHeight, null ); gr.dispose(); } } String[][] splitImages = new String[rows][cols]; for (int i = 0 ; i < imgs.length; i++) { for (int j = 0 ; j < imgs[i].length; j++) { String splitImagePath = splitImageBasePath + "/" + fileNamePrefix + i + "-" + j + "." + imageType; splitImages[i][j] = splitImagePath; ImageIO.write(imgs[i][j], imageType, new File(splitImagePath)); } } return splitImages; } public static List<String> splitImageReturnList (String filePath, int rows, int cols, String splitImageBasePath) throws IOException { String[][] strings = splitImage(filePath, rows, cols, splitImageBasePath); List<String> list = new ArrayList<>(rows * cols); for (String[] strs : strings) { list.addAll(Arrays.asList(strs)); } return list; } public static String[][] splitImageB(String filePath, int imageWidth, int imageHeight, String splitImageBasePath) throws IOException { if (isBlank(filePath)||imageWidth<=0 ||imageHeight<=0 ||isBlank(splitImageBasePath)){ throw new RuntimeException("参数错误!" ); } BufferedImage bufferedImage = ImageIO.read(new File(filePath)); int srcWidth = bufferedImage.getHeight(); int srcHeight = bufferedImage.getWidth(); if (srcWidth<imageWidth||srcHeight<imageHeight){ throw new RuntimeException("基础图片长宽不符合要求!" ); } int cols; int rows; if (srcWidth % imageWidth == 0 ) { cols = srcWidth / imageWidth; } else { cols = (int ) Math.floor((double )(srcWidth / imageWidth)) + 1 ; } if (srcHeight % imageHeight == 0 ) { rows = srcHeight / imageHeight; } else { rows = (int ) Math.floor((double )(srcHeight / imageHeight)) + 1 ; } return splitImage(filePath,rows,cols,splitImageBasePath); }
上述图片分割代码我们可以把一张图片分成9块来制造九宫格图片等。
图片合并相关 图片合并可以将两张或者多张图片按照顺序合成一整张图片。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 public static String mergeImage (String[][] images, String finalImageBasePath) throws IOException { if (images == null || images.length == 0 || isBlank(finalImageBasePath)) { throw new RuntimeException("参数不正确!!" ); } int rows = 0 ; int cols = 0 ; for (int i = 0 ; i < images.length; i++) { rows++; int temp = 0 ; for (int j = 0 ; j < images[i].length; j++) { temp++; } if (i == 0 ) { cols = temp; } else { if (cols != temp) { throw new RuntimeException("需要保证a * b 格式的图片" ); } } } String imageType = getExtension(images[0 ][0 ]); imageType = imageType.toLowerCase(); if (!(JPG.equals(imageType) || JPEG.equals(imageType) || PNG.equals(imageType))) { throw new RuntimeException("请使用jpg、jpeg、png类型的图片" ); } int chunkWidth, chunkHeight; int type; BufferedImage[][] buffImages = new BufferedImage[rows][cols]; for (int i = 0 ; i < rows; i++) { for (int j = 0 ; j < cols; j++) { getExtension(images[i][j]); buffImages[i][j] = ImageIO.read(new File(images[i][j])); } } type = buffImages[0 ][0 ].getType(); chunkWidth = buffImages[0 ][0 ].getWidth(); chunkHeight = buffImages[0 ][0 ].getHeight(); BufferedImage finalImg = new BufferedImage(chunkWidth * cols, chunkHeight * rows, type); for (int i = 0 ; i < rows; i++) { for (int j = 0 ; j < cols; j++) { finalImg.createGraphics().drawImage(buffImages[i][j], chunkWidth * j, chunkHeight * i, null ); } } String filePath = finalImageBasePath + "/" + getUUID() + "." + imageType; ImageIO.write(finalImg, imageType, new File(filePath)); return filePath; } public static String mergeImage (List<String> imageList, int rows, String finalImageBasePath) throws IOException { if (isEmpty(imageList)) { throw new RuntimeException("参数错误" ); } if (rows < 0 || imageList.size() % rows != 0 ) { throw new RuntimeException("参数错误" ); } int cols = imageList.size() / rows; String[][] strings = new String[rows][cols]; int temp = 0 ; for (int i = 0 ; i < strings.length; i++) { for (int j = 0 ; j < strings[i].length; j++) { strings[i][j] = imageList.get(temp); temp++; } } return mergeImage(strings, finalImageBasePath); }
图片水印相关 根据文字生成相应图片 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public static String createImageByText (String text, int picFontSize, Color bgColor, Color fontColor, Font font, String basePath) throws IOException { if (isBlank(text) || picFontSize <= 0 || bgColor == null || fontColor == null || font == null || isBlank(basePath)) { throw new RuntimeException("参数错误!" ); } float beautify = picFontSize / 20.0f ; int width = text.length() * picFontSize + (int ) (5 * beautify); int height = picFontSize + (int ) (5 * beautify); BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D g = bufferedImage.createGraphics(); g.setColor(bgColor); g.fillRect(0 , 0 , width, height); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.setColor(fontColor); g.setFont(font.deriveFont((float ) picFontSize)); g.drawString(text, beautify, picFontSize); g.dispose(); String fileName = basePath + "/" + getUUID() + "." + PictureConstants.JPG; try (FileOutputStream out = new FileOutputStream(fileName)) { ImageIO.write(bufferedImage, PictureConstants.JPEG, out); } return fileName; }
该方法可以根据输入的文字、字体、颜色等生成一张图片。
为图片添加图片水印 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 public static String watermarkPic (String watermark, String sourceImage, Positions positions, float opacity, String targetFilePath) throws IOException { if (isBlank(watermark) || isBlank(sourceImage) || isBlank(targetFilePath) || positions == null || opacity < 0 || opacity > 1 ) { throw new RuntimeException("参数错误!" ); } String sourceFormat = getExtension(sourceImage).toLowerCase(); String targetFile = targetFilePath + "/" + getUUID() + "." + sourceFormat; Thumbnails.of(sourceImage).scale(1.0f ).watermark(positions, ImageIO.read(new File(watermark)), opacity).outputFormat(sourceFormat).toFile(targetFile); return targetFile; } public static String watermarkPic (String watermark, String sourceImage, int x, int y, float opacity, String targetFilePath) throws IOException { if (isBlank(watermark) || isBlank(sourceImage) || isBlank(targetFilePath) || opacity < 0 || opacity > 1 ) { throw new RuntimeException("参数错误!" ); } String sourceFormat = getExtension(sourceImage).toLowerCase(); String targetFile = targetFilePath + "/" + getUUID() + "." + sourceFormat; Image src = ImageIO.read(new File(sourceImage)); int width = src.getWidth(null ); int height = src.getHeight(null ); BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D g = image.createGraphics(); g.drawImage(src, 0 , 0 , width, height, null ); Image waterPic = ImageIO.read(new File(watermark)); int waterWidth = waterPic.getWidth(null ); int waterHeight = waterPic.getHeight(null ); g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, opacity)); g.drawImage(waterPic, (width - waterWidth) / 2 + x, (height - waterHeight) / 2 + y, waterWidth, waterHeight, null ); g.dispose(); ImageIO.write(image, sourceFormat, new File(targetFile)); return targetFile; }
上述方法分别使用了Thumbnails工具类和ImageIO类为原图片添加水印图片。
为图片添加文字水印 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public static String watermarkText (String text, String sourceImage, Color fontColor, Font font, int x, int y, float opacity, String targetFilePath) throws IOException { if (isBlank(text) || isBlank(sourceImage) || fontColor == null || font == null || isBlank(targetFilePath) || opacity < 0 || opacity > 1 ) { throw new RuntimeException("参数错误!" ); } String sourceFormat = getExtension(sourceImage).toLowerCase(); String targetFile = targetFilePath + "/" + getUUID() + "." + sourceFormat; Image src = ImageIO.read(new File(sourceImage)); int width = src.getWidth(null ); int height = src.getHeight(null ); BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D g = image.createGraphics(); g.drawImage(src, 0 , 0 , width, height, null ); g.setColor(fontColor); g.setFont(font); g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, opacity)); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.drawString(text, (width - (getLength(text) * font.getSize())) / 2 + x, (height - font.getSize()) / 2 + y); g.dispose(); ImageIO.write(image, sourceFormat, new File(targetFile)); return targetFile; }
上述方法可为图片在指定位置添加文字水印。
PS:
这儿说下图片去水印的问题,一般情况下,对于一个项目,是保留原图,需要的时候再添加水印,从而实现添加水印和去除水印的效果。
而如果就是想给一张有水印的图片去除水印,可以使用[OpenCV](https://opencv.org/)等相关进行处理。
相关处理在这儿就不过多介绍了。
Java在图片深度处理这方面是很一般的,一般进行图片深度处理时,都会调用其它语言的API。
如上面提到的OpenCV,其核心算法就是由C++语言实现的,同时提供了其它语言的接口。
GIF图片的生成与转换 GIF相关操作可以使用com.madgag.animated-gif-lib
相关Jar包,这个工具类Maven地址如下:
1 2 3 4 5 <dependency > <groupId > com.madgag</groupId > <artifactId > animated-gif-lib</artifactId > <version > 1.4</version > </dependency >
这个工具包在 2017年7月维护到最新版本1.4后就再也没有维护过了。
这个工具包里有4个类AnimatedGifEncoder.java
、GifDecoder.java
、LZWEncoder.java
、NeuQuant.java
。
将传入的若干图片生成gif 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public static String imageToGif (List<String> imageList, String basePath, int delay) throws IOException { if (isEmpty(imageList) || isBlank(basePath) || delay < 0 ){ throw new RuntimeException("参数错误!" ); } String fileName = basePath + "/" + getUUID() + "." + PictureConstants.GIF; File file = new File(fileName); try ( ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); FileOutputStream fileOutputStream = new FileOutputStream(file); ) { AnimatedGifEncoder e = new AnimatedGifEncoder(); e.start(byteArrayOutputStream); e.setDelay(delay); for (String imagePath : imageList) { e.addFrame(ImageIO.read(new FileInputStream(imagePath))); } e.finish(); byte [] byteArray = byteArrayOutputStream.toByteArray(); fileOutputStream.write(byteArray); fileOutputStream.flush(); } return fileName; }
调用此方法,可以将传入的一系列图片生成一张gif,我们应尽量保证传入的图片格式一致。
将gif图片拆分 我们这里以jpg为例,将gif图片拆分成一张张jpg格式的图片。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public static List<String> gifToImage (String gifPath, String basePath) throws IOException { if (isBlank(gifPath) || isBlank(basePath)){ throw new RuntimeException("参数错误!" ); } File file = new File(gifPath); String fileName = file.getName(); String fileNamePrefix = fileName.substring(0 , fileName.lastIndexOf("." )); String imagePath = basePath + "/" + fileNamePrefix + "_%s." + PictureConstants.JPG; GifDecoder gifDecoder = new GifDecoder(); gifDecoder.read(gifPath); int count = gifDecoder.getFrameCount(); List<String> imageList = new ArrayList<>(count); for (int i = 0 ; i < count; i++) { BufferedImage bufferedImage = gifDecoder.getFrame(i); String imageFileName = String.format(imagePath, i); try (FileOutputStream fileOutputStream = new FileOutputStream(imageFileName)) { ImageIO.write(bufferedImage, PictureConstants.JPEG, fileOutputStream); } imageList.add(imageFileName); } return imageList; }
总结 将一些需要的代码记录下来形成自己的工具类包,提高自身经验总结,对以后是蛮有帮助的。
源码 源码详见我的 Github