xiejun
2024-11-01 80b6cbfc9c861469146318d0b3dd5f8b8b525b8a
Source/BladeX-Tool/blade-core-tool/src/main/java/org/springblade/core/tool/utils/ImageUtil.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,489 @@
/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill åº„骞 (smallchill@163.com)
 */
package org.springblade.core.tool.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springblade.core.tool.support.IMultiOutputStream;
import org.springblade.core.tool.support.ImagePosition;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.geom.AffineTransform;
import java.awt.image.*;
import java.io.*;
import java.net.URL;
/**
 * å›¾ç‰‡å·¥å…·ç±»
 *
 * @author Chill
 */
public final class ImageUtil {
   /**
    * Logger for this class
    */
   private static Logger LOGGER = LoggerFactory.getLogger(ImageUtil.class);
   /**
    * é»˜è®¤è¾“出图片类型
    */
   public static final String DEFAULT_IMG_TYPE = "JPEG";
   private ImageUtil() {
   }
   /**
    * è½¬æ¢è¾“入流到byte
    *
    * @param src  æº
    * @param type ç±»åž‹
    * @return byte[]
    * @throws IOException å¼‚常
    */
   public static byte[] toByteArray(BufferedImage src, String type) throws IOException {
      ByteArrayOutputStream os = new ByteArrayOutputStream();
      ImageIO.write(src, defaultString(type, DEFAULT_IMG_TYPE), os);
      return os.toByteArray();
   }
   /**
    * èŽ·å–å›¾åƒå†…å®¹
    *
    * @param srcImageFile æ–‡ä»¶è·¯å¾„
    * @return BufferedImage
    */
   public static BufferedImage readImage(String srcImageFile) {
      try {
         return ImageIO.read(new File(srcImageFile));
      } catch (IOException e) {
         LOGGER.error("Error readImage", e);
      }
      return null;
   }
   /**
    * èŽ·å–å›¾åƒå†…å®¹
    *
    * @param srcImageFile æ–‡ä»¶
    * @return BufferedImage
    */
   public static BufferedImage readImage(File srcImageFile) {
      try {
         return ImageIO.read(srcImageFile);
      } catch (IOException e) {
         LOGGER.error("Error readImage", e);
      }
      return null;
   }
   /**
    * èŽ·å–å›¾åƒå†…å®¹
    *
    * @param srcInputStream è¾“入流
    * @return BufferedImage
    */
   public static BufferedImage readImage(InputStream srcInputStream) {
      try {
         return ImageIO.read(srcInputStream);
      } catch (IOException e) {
         LOGGER.error("Error readImage", e);
      }
      return null;
   }
   /**
    * èŽ·å–å›¾åƒå†…å®¹
    *
    * @param url URL地址
    * @return BufferedImage
    */
   public static BufferedImage readImage(URL url) {
      try {
         return ImageIO.read(url);
      } catch (IOException e) {
         LOGGER.error("Error readImage", e);
      }
      return null;
   }
   /**
    * ç¼©æ”¾å›¾åƒï¼ˆæŒ‰æ¯”例缩放)
    *
    * @param src    æºå›¾åƒ
    * @param output è¾“出流
    * @param type   ç±»åž‹
    * @param scale  ç¼©æ”¾æ¯”例
    * @param flag   ç¼©æ”¾é€‰æ‹©:true æ”¾å¤§; false ç¼©å°;
    */
   public final static void zoomScale(BufferedImage src, OutputStream output, String type, double scale, boolean flag) {
      try {
         // å¾—到源图宽
         int width = src.getWidth();
         // å¾—到源图长
         int height = src.getHeight();
         if (flag) {
            // æ”¾å¤§
            width = Long.valueOf(Math.round(width * scale)).intValue();
            height = Long.valueOf(Math.round(height * scale)).intValue();
         } else {
            // ç¼©å°
            width = Long.valueOf(Math.round(width / scale)).intValue();
            height = Long.valueOf(Math.round(height / scale)).intValue();
         }
         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, defaultString(type, DEFAULT_IMG_TYPE), output);
         output.close();
      } catch (IOException e) {
         LOGGER.error("Error in zoom image", e);
      }
   }
   /**
    * ç¼©æ”¾å›¾åƒï¼ˆæŒ‰é«˜åº¦å’Œå®½åº¦ç¼©æ”¾ï¼‰
    *
    * @param src       æºå›¾åƒ
    * @param output    è¾“出流
    * @param type      ç±»åž‹
    * @param height    ç¼©æ”¾åŽçš„高度
    * @param width     ç¼©æ”¾åŽçš„宽度
    * @param bb        æ¯”例不对时是否需要补白:true为补白; false为不补白;
    * @param fillColor å¡«å……色,null时为Color.WHITE
    */
   public final static void zoomFixed(BufferedImage src, OutputStream output, String type, int height, int width, boolean bb, Color fillColor) {
      try {
         double ratio = 0.0;
         Image itemp = src.getScaledInstance(width, height, BufferedImage.SCALE_SMOOTH);
         // è®¡ç®—比例
         if (src.getHeight() > src.getWidth()) {
            ratio = Integer.valueOf(height).doubleValue() / src.getHeight();
         } else {
            ratio = Integer.valueOf(width).doubleValue() / src.getWidth();
         }
         AffineTransformOp op = new AffineTransformOp(AffineTransform.getScaleInstance(ratio, ratio), null);
         itemp = op.filter(src, null);
         if (bb) {
            //补白
            BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            Graphics2D g = image.createGraphics();
            Color fill = fillColor == null ? Color.white : fillColor;
            g.setColor(fill);
            g.fillRect(0, 0, width, height);
            if (width == itemp.getWidth(null)) {
               g.drawImage(itemp, 0, (height - itemp.getHeight(null)) / 2, itemp.getWidth(null), itemp.getHeight(null), fill, null);
            } else {
               g.drawImage(itemp, (width - itemp.getWidth(null)) / 2, 0, itemp.getWidth(null), itemp.getHeight(null), fill, null);
            }
            g.dispose();
            itemp = image;
         }
         // è¾“出为文件
         ImageIO.write((BufferedImage) itemp, defaultString(type, DEFAULT_IMG_TYPE), output);
         // å…³é—­æµ
         output.close();
      } catch (IOException e) {
         LOGGER.error("Error in zoom image", e);
      }
   }
   /**
    * å›¾åƒè£å‰ª(按指定起点坐标和宽高切割)
    *
    * @param src    æºå›¾åƒ
    * @param output åˆ‡ç‰‡åŽçš„图像地址
    * @param type   ç±»åž‹
    * @param x      ç›®æ ‡åˆ‡ç‰‡èµ·ç‚¹åæ ‡X
    * @param y      ç›®æ ‡åˆ‡ç‰‡èµ·ç‚¹åæ ‡Y
    * @param width  ç›®æ ‡åˆ‡ç‰‡å®½åº¦
    * @param height ç›®æ ‡åˆ‡ç‰‡é«˜åº¦
    */
   public final static void crop(BufferedImage src, OutputStream output, String type, int x, int y, int width, int height) {
      try {
         // æºå›¾å®½åº¦
         int srcWidth = src.getWidth();
         // æºå›¾é«˜åº¦
         int srcHeight = src.getHeight();
         if (srcWidth > 0 && srcHeight > 0) {
            Image image = src.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, defaultString(type, DEFAULT_IMG_TYPE), output);
            // å…³é—­æµ
            output.close();
         }
      } catch (Exception e) {
         LOGGER.error("Error in cut image", e);
      }
   }
   /**
    * å›¾åƒåˆ‡å‰²ï¼ˆæŒ‡å®šåˆ‡ç‰‡çš„行数和列数)
    *
    * @param src   æºå›¾åƒåœ°å€
    * @param mos   åˆ‡ç‰‡ç›®æ ‡æ–‡ä»¶å¤¹
    * @param type  ç±»åž‹
    * @param prows ç›®æ ‡åˆ‡ç‰‡è¡Œæ•°ã€‚默认2,必须是范围 [1, 20] ä¹‹å†…
    * @param pcols ç›®æ ‡åˆ‡ç‰‡åˆ—数。默认2,必须是范围 [1, 20] ä¹‹å†…
    */
   public final static void sliceWithNumber(BufferedImage src, IMultiOutputStream mos, String type, int prows, int pcols) {
      try {
         int rows = prows <= 0 || prows > 20 ? 2 : prows;
         int cols = pcols <= 0 || pcols > 20 ? 2 : pcols;
         // æºå›¾å®½åº¦
         int srcWidth = src.getWidth();
         // æºå›¾é«˜åº¦
         int srcHeight = src.getHeight();
         if (srcWidth > 0 && srcHeight > 0) {
            Image img;
            ImageFilter cropFilter;
            Image image = src.getScaledInstance(srcWidth, srcHeight, Image.SCALE_DEFAULT);
            // æ¯å¼ åˆ‡ç‰‡çš„宽度
            int destWidth = (srcWidth % cols == 0) ? (srcWidth / cols) : (srcWidth / cols + 1);
            // æ¯å¼ åˆ‡ç‰‡çš„高度
            int destHeight = (srcHeight % rows == 0) ? (srcHeight / rows) : (srcHeight / rows + 1);
            // å¾ªçŽ¯å»ºç«‹åˆ‡ç‰‡
            // æ”¹è¿›çš„æƒ³æ³•:是否可用多线程加快切割速度
            for (int i = 0; i < rows; i++) {
               for (int j = 0; j < cols; j++) {
                  // å››ä¸ªå‚数分别为图像起点坐标和宽高
                  cropFilter = new CropImageFilter(j * destWidth, i * destHeight, destWidth, destHeight);
                  img = Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(image.getSource(), cropFilter));
                  BufferedImage tag = new BufferedImage(destWidth, destHeight, BufferedImage.TYPE_INT_RGB);
                  Graphics g = tag.getGraphics();
                  // ç»˜åˆ¶ç¼©å°åŽçš„图
                  g.drawImage(img, 0, 0, null);
                  g.dispose();
                  // è¾“出为文件
                  ImageIO.write(tag, defaultString(type, DEFAULT_IMG_TYPE), mos.buildOutputStream(i, j));
               }
            }
         }
      } catch (Exception e) {
         LOGGER.error("Error in slice image", e);
      }
   }
   /**
    * å›¾åƒåˆ‡å‰²ï¼ˆæŒ‡å®šåˆ‡ç‰‡çš„宽度和高度)
    *
    * @param src         æºå›¾åƒåœ°å€
    * @param mos         åˆ‡ç‰‡ç›®æ ‡æ–‡ä»¶å¤¹
    * @param type        ç±»åž‹
    * @param pdestWidth  ç›®æ ‡åˆ‡ç‰‡å®½åº¦ã€‚默认200
    * @param pdestHeight ç›®æ ‡åˆ‡ç‰‡é«˜åº¦ã€‚默认150
    */
   public final static void sliceWithSize(BufferedImage src, IMultiOutputStream mos, String type, int pdestWidth, int pdestHeight) {
      try {
         int destWidth = pdestWidth <= 0 ? 200 : pdestWidth;
         int destHeight = pdestHeight <= 0 ? 150 : pdestHeight;
         // æºå›¾å®½åº¦
         int srcWidth = src.getWidth();
         // æºå›¾é«˜åº¦
         int srcHeight = src.getHeight();
         if (srcWidth > destWidth && srcHeight > destHeight) {
            Image img;
            ImageFilter cropFilter;
            Image image = src.getScaledInstance(srcWidth, srcHeight, Image.SCALE_DEFAULT);
            // åˆ‡ç‰‡æ¨ªå‘数量
            int cols = (srcWidth % destWidth == 0) ? (srcWidth / destWidth) : (srcWidth / destWidth + 1);
            // åˆ‡ç‰‡çºµå‘数量
            int rows = (srcHeight % destHeight == 0) ? (srcHeight / destHeight) : (srcHeight / destHeight + 1);
            // å¾ªçŽ¯å»ºç«‹åˆ‡ç‰‡
            // æ”¹è¿›çš„æƒ³æ³•:是否可用多线程加快切割速度
            for (int i = 0; i < rows; i++) {
               for (int j = 0; j < cols; j++) {
                  // å››ä¸ªå‚数分别为图像起点坐标和宽高
                  cropFilter = new CropImageFilter(j * destWidth, i * destHeight, destWidth, destHeight);
                  img = Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(image.getSource(), cropFilter));
                  BufferedImage tag = new BufferedImage(destWidth, destHeight, BufferedImage.TYPE_INT_RGB);
                  Graphics g = tag.getGraphics();
                  // ç»˜åˆ¶ç¼©å°åŽçš„图
                  g.drawImage(img, 0, 0, null);
                  g.dispose();
                  // è¾“出为文件
                  ImageIO.write(tag, defaultString(type, DEFAULT_IMG_TYPE), mos.buildOutputStream(i, j));
               }
            }
         }
      } catch (Exception e) {
         LOGGER.error("Error in slice image", e);
      }
   }
   /**
    * å›¾åƒç±»åž‹è½¬æ¢ï¼šGIF-JPG、GIF-PNG、PNG-JPG、PNG-GIF(X)、BMP-PNG
    *
    * @param src        æºå›¾åƒåœ°å€
    * @param formatName åŒ…含格式非正式名称的 String:如JPG、JPEG、GIF等
    * @param output     ç›®æ ‡å›¾åƒåœ°å€
    */
   public final static void convert(BufferedImage src, OutputStream output, String formatName) {
      try {
         // è¾“出为文件
         ImageIO.write(src, formatName, output);
         // å…³é—­æµ
         output.close();
      } catch (Exception e) {
         LOGGER.error("Error in convert image", e);
      }
   }
   /**
    * å½©è‰²è½¬ä¸ºé»‘白
    *
    * @param src    æºå›¾åƒåœ°å€
    * @param output ç›®æ ‡å›¾åƒåœ°å€
    * @param type      ç±»åž‹
    */
   public final static void gray(BufferedImage src, OutputStream output, String type) {
      try {
         ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
         ColorConvertOp op = new ColorConvertOp(cs, null);
         src = op.filter(src, null);
         // è¾“出为文件
         ImageIO.write(src, defaultString(type, DEFAULT_IMG_TYPE), output);
         // å…³é—­æµ
         output.close();
      } catch (IOException e) {
         LOGGER.error("Error in gray image", e);
      }
   }
   /**
    * ç»™å›¾ç‰‡æ·»åŠ æ–‡å­—æ°´å°
    *
    * @param src      æºå›¾åƒ
    * @param output   è¾“出流
    * @param type      ç±»åž‹
    * @param text     æ°´å°æ–‡å­—
    * @param font     æ°´å°çš„字体
    * @param color    æ°´å°çš„字体颜色
    * @param position æ°´å°ä½ç½® {@link ImagePosition}
    * @param x        ä¿®æ­£å€¼
    * @param y        ä¿®æ­£å€¼
    * @param alpha    é€æ˜Žåº¦ï¼šalpha å¿…须是范围 [0.0, 1.0] ä¹‹å†…(包含边界值)的一个浮点数字
    */
   public final static void textStamp(BufferedImage src, OutputStream output, String type, String text, Font font, Color color
      , int position, int x, int y, float alpha) {
      try {
         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(color);
         g.setFont(font);
         g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
         // åœ¨æŒ‡å®šåæ ‡ç»˜åˆ¶æ°´å°æ–‡å­—
         ImagePosition boxPos = new ImagePosition(width, height, calcTextWidth(text) * font.getSize(), font.getSize(), position);
         g.drawString(text, boxPos.getX(x), boxPos.getY(y));
         g.dispose();
         // è¾“出为文件
         ImageIO.write((BufferedImage) image, defaultString(type, DEFAULT_IMG_TYPE), output);
         // å…³é—­æµ
         output.close();
      } catch (Exception e) {
         LOGGER.error("Error in textStamp image", e);
      }
   }
   /**
    * ç»™å›¾ç‰‡æ·»åŠ å›¾ç‰‡æ°´å°
    *
    * @param src      æºå›¾åƒ
    * @param output   è¾“出流
    * @param type      ç±»åž‹
    * @param stamp    æ°´å°å›¾ç‰‡
    * @param position æ°´å°ä½ç½® {@link ImagePosition}
    * @param x        ä¿®æ­£å€¼
    * @param y        ä¿®æ­£å€¼
    * @param alpha    é€æ˜Žåº¦ï¼šalpha å¿…须是范围 [0.0, 1.0] ä¹‹å†…(包含边界值)的一个浮点数字
    */
   public final static void imageStamp(BufferedImage src, OutputStream output, String type, BufferedImage stamp
      , int position, int x, int y, float alpha) {
      try {
         int width = src.getWidth();
         int height = src.getHeight();
         BufferedImage image = new BufferedImage(width, height,
            BufferedImage.TYPE_INT_RGB);
         Graphics2D g = image.createGraphics();
         g.drawImage(src, 0, 0, width, height, null);
         // æ°´å°æ–‡ä»¶
         int stampWidth = stamp.getWidth();
         int stampHeight = stamp.getHeight();
         g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
         ImagePosition boxPos = new ImagePosition(width, height, stampWidth, stampHeight, position);
         g.drawImage(stamp, boxPos.getX(x), boxPos.getY(y), stampWidth, stampHeight, null);
         // æ°´å°æ–‡ä»¶ç»“束
         g.dispose();
         // è¾“出为文件
         ImageIO.write((BufferedImage) image, defaultString(type, DEFAULT_IMG_TYPE), output);
         // å…³é—­æµ
         output.close();
      } catch (Exception e) {
         LOGGER.error("Error imageStamp", e);
      }
   }
   /**
    * è®¡ç®—text的长度(一个中文算两个字符)
    *
    * @param text text
    * @return int
    */
   public final static int calcTextWidth(String text) {
      int length = 0;
      for (int i = 0; i < text.length(); i++) {
         if (new String(text.charAt(i) + "").getBytes().length > 1) {
            length += 2;
         } else {
            length += 1;
         }
      }
      return length / 2;
   }
   /**
    * é»˜è®¤å­—符串
    * @param str å­—符串
    * @param defaultStr é»˜è®¤å€¼
    * @return
    */
   public static String defaultString(String str, String defaultStr) {
      return ((str == null) ? defaultStr : str);
   }
}