javacv 图片合成
/** * 图片组合 */ @Slf4j public class ComposeImageUtil { /** * 合成商品链接 * * @param composeGoodsImageCmd * @param oSSUtil * @return */ public static String handleComposeGoodsImage(ComposeGoodsImageCmd composeGoodsImageCmd, OSSUtil oSSUtil) { //图片上传路径 String keyName = composeGoodsImageCmd.getKeyName(); //合成图宽高 Long imgWidth = composeGoodsImageCmd.getImgWidth(); Long imgHigh = composeGoodsImageCmd.getImgHigh(); //修正规则 String amendmentRules = composeGoodsImageCmd.getAmendmentRules(); //主商品有效面积(英文逗号隔开) String effectiveArea = composeGoodsImageCmd.getEffectiveArea(); //主商品中心点坐标(x_y,x_y,x_y) String centralPoint = composeGoodsImageCmd.getCentralPoint(); //主商品合成顺序(英文逗号隔开) String order = composeGoodsImageCmd.getOrder(); //前置商品数 Integer frontGoodsNum = composeGoodsImageCmd.getFrontGoodsNum() == null ? 0 : composeGoodsImageCmd.getFrontGoodsNum(); List<DubboWeimobCollocationGoodsBaseDTO> collocationGoodsBaseList = composeGoodsImageCmd.getCollocationGoodsBaseDTOList(); if ( ObjectUtils.isEmpty(imgWidth) || imgWidth < 1 || ObjectUtils.isEmpty(imgHigh) || imgHigh < 1 || StringUtils.isBlank(amendmentRules) || StringUtils.isBlank(effectiveArea) || StringUtils.isBlank(centralPoint) || StringUtils.isBlank(order) || ObjectUtils.isEmpty(collocationGoodsBaseList) || StringUtils.isBlank(keyName) ) { log.error("handleComposeGoodsImage attribute is null. imgWidth ={},imgHigh ={},amendmentRules ={},EffectiveArea ={},CentralPoint ={},Order ={},collocationGoodsBaseList size={}", imgWidth, imgHigh, amendmentRules, effectiveArea, centralPoint, order, collocationGoodsBaseList.size()); return null; } //修正规则 Map<Integer, List<AmendmentRule>> aidaCollocateAmendmentRuleMap = JSONObject.parseObject(amendmentRules, HashMap.class); //主商品有效面积转换 String[] EffectiveAreaStr = effectiveArea.split(","); long[] EffectiveAreaArray = new long[EffectiveAreaStr.length]; for (int EffectiveAreaindex = 0; EffectiveAreaindex < EffectiveAreaStr.length; EffectiveAreaindex++) { EffectiveAreaArray[EffectiveAreaindex] = Integer.parseInt(EffectiveAreaStr[EffectiveAreaindex]); } //主商品中心点坐标(x_y,x_y,x_y)转换 String[] CentralPointStr = centralPoint.split(","); int[][] CentralPointArray = new int[CentralPointStr.length][2]; for (int CentralPointindex = 0; CentralPointindex < CentralPointStr.length; CentralPointindex++) { String[] point = CentralPointStr[CentralPointindex].split("_"); CentralPointArray[CentralPointindex] = new int[]{Integer.parseInt(point[0]), Integer.parseInt(point[1])}; } //组合顺序转换 String[] orderStr = order.split(","); int[] orderArray = new int[orderStr.length]; for (int Orderindex = 0; Orderindex < orderStr.length; Orderindex++) { orderArray[Orderindex] = Integer.parseInt(orderStr[Orderindex]); } if ( collocationGoodsBaseList.size() != EffectiveAreaArray.length || collocationGoodsBaseList.size() != CentralPointArray.length || collocationGoodsBaseList.size() != orderArray.length ) { log.error("handleComposeGoodsImage data Num fail. collocationGoodsBaseList size={},EffectiveAreaArray size ={},aidaCollocateAmendmentRuleMap size ={},CentralPointArray size ={},orderArray size={}", collocationGoodsBaseList.size(), EffectiveAreaArray.length, aidaCollocateAmendmentRuleMap.size(), CentralPointArray.length, orderArray.length); //素材数量不足退出 return null; } //时间统计 StopWatch clock = new StopWatch(); clock.start(" <=== 图片【" + keyName + "】初始化png透明背景时间 "); //初始化png透明背景 BufferedImage bufferedImage = createPng(imgWidth.intValue(), imgHigh.intValue()); clock.stop(); //遍历组合顺序下标 for (int orderIndex : orderArray) { int position = orderIndex - 1; //获取修正规则 List<AmendmentRule> amendmentRulesList = aidaCollocateAmendmentRuleMap.get(position); position = position - frontGoodsNum; clock.start(" <=== 图片【" + keyName + "】商品下标【" + position + "】时间 "); //得到商品 DubboWeimobCollocationGoodsBaseDTO collocationGoodsBase = collocationGoodsBaseList.get(position); //得到商品理应的有限 Long meffectiveArea = EffectiveAreaArray[position]; //得到中心点坐标 int[] point = CentralPointArray[position]; //旋转角度 int angle = 0; //计算 int[] calculate = AmendmentRuleUtil.calculate(amendmentRulesList, meffectiveArea.intValue(), angle, collocationGoodsBase.getWidth(), collocationGoodsBase.getHigh(), point[0], point[1]); //计算出缩放比 double scale = Math.sqrt(((double) calculate[0]) / collocationGoodsBase.getEffectiveArea()); //角度 angle = calculate[1]; //宽高计算、压缩 int width = (int) (collocationGoodsBase.getWidth() * scale); int high = (int) (collocationGoodsBase.getHigh() * scale); //旋转后 int[] calcRotatedSize = calcRotatedSize(width, high, angle); width = calcRotatedSize[0]; high = calcRotatedSize[1]; //计算中心点 int centerPointX = (width / 2); int centerPointY = (high / 2); //计算左上角坐标 int x = calculate[2] - centerPointX; int y = calculate[3] - centerPointY; //图片Url处理 String imgUrl = collocationGoodsBase.getCuttingDiagramImageIntranetUrl(); imgUrl = OSSUrlUtil.rotate(imgUrl, angle); imgUrl = OSSUrlUtil.specifyWidthAndHeight(imgUrl, width, high); try { stickImg(bufferedImage, ImgUtil.read(new URL(imgUrl)), x, y); } catch (Exception e) { e.printStackTrace(); } clock.stop(); } clock.start(" <=== 图片【" + keyName + "】bufferedImage转InputStream时间 "); //上传 InputStream inputStream = BufferedImageCollocateUtil.bufferedImageInputStreamBGRA(bufferedImage); clock.stop(); //上传云存储 clock.start(" <=== 图片【" + keyName + "】上传云存储时间 "); oSSUtil.internalPutFileByInputStream(keyName, inputStream); try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } clock.stop(); log.debug("图片【" + keyName + "】耗时统计:{}", clock.prettyPrint()); //获取云存储地址 return oSSUtil.getURLOfTheFileInternalCheck(keyName); } /** * 贴按顺序贴图返回整图 * * @param stickImgOrderCmd 配饰参数 */ public static BufferedImage stickImgOrderToBufferedImage(StickImgOrderCmd stickImgOrderCmd) { if (ObjectUtils.isEmpty(stickImgOrderCmd)) { return null; } Long width = stickImgOrderCmd.getWidth(); Long heigh = stickImgOrderCmd.getHeigh(); if (ObjectUtils.isEmpty(width) || ObjectUtils.isEmpty(heigh)) { return null; } //贴图顺序 Byte[] layerOrderArray = {0, 1, 2}; if (StringUtils.isNotBlank(stickImgOrderCmd.getLayerOrder())) { String[] layerOrderSplit = stickImgOrderCmd.getLayerOrder().split(","); layerOrderArray = new Byte[layerOrderSplit.length]; for (int layerOrderIndex = 0; layerOrderIndex < layerOrderSplit.length; layerOrderIndex++) { layerOrderArray[layerOrderIndex] = Byte.parseByte(layerOrderSplit[layerOrderIndex]); } } //图片的图片地址 Map<Byte, String> imageUrlMap = new HashMap<>(); imageUrlMap.put(AidaCompositeConstant.AIDACOMPOSE_LAYERORDER_0, stickImgOrderCmd.getBackGroundUrl()); imageUrlMap.put(AidaCompositeConstant.AIDACOMPOSE_LAYERORDER_1, stickImgOrderCmd.getGoodsImageMasterUrl()); imageUrlMap.put(AidaCompositeConstant.AIDACOMPOSE_LAYERORDER_2, stickImgOrderCmd.getGoodsImageSlaveUrl()); //合成图片 BufferedImage bufferedImage = ComposeImageUtil.createPng(width.intValue(), heigh.intValue()); for (Byte layer : layerOrderArray) { String imageUrl = imageUrlMap.get(layer); if (StringUtils.isNotBlank(imageUrl)) { try { stickImg(bufferedImage, ImgUtil.read(new URL(imageUrl)), 0, 0); } catch (Exception e) { } } } return bufferedImage; } /** * 贴按顺序贴图返回组装对象 * * @param stickImgOrderCmd 配饰参数 */ public static List<ComposeImageCoordinateDTO> stickImgOrderToJson(StickImgOrderCmd stickImgOrderCmd) { if (ObjectUtils.isEmpty(stickImgOrderCmd)) { return null; } Long width = stickImgOrderCmd.getWidth(); Long heigh = stickImgOrderCmd.getHeigh(); //贴图顺序 Byte[] layerOrderArray = {0, 1, 2}; if (StringUtils.isNotBlank(stickImgOrderCmd.getLayerOrder())) { String[] layerOrderSplit = stickImgOrderCmd.getLayerOrder().split(","); layerOrderArray = new Byte[layerOrderSplit.length]; for (int layerOrderIndex = 0; layerOrderIndex < layerOrderSplit.length; layerOrderIndex++) { layerOrderArray[layerOrderIndex] = Byte.parseByte(layerOrderSplit[layerOrderIndex]); } } //图片的图片地址 Map<Byte, String> imageUrlMap = new HashMap<>(); imageUrlMap.put(AidaCompositeConstant.AIDACOMPOSE_LAYERORDER_0, stickImgOrderCmd.getBackGroundUrl()); imageUrlMap.put(AidaCompositeConstant.AIDACOMPOSE_LAYERORDER_1, stickImgOrderCmd.getGoodsImageMasterUrl()); imageUrlMap.put(AidaCompositeConstant.AIDACOMPOSE_LAYERORDER_2, stickImgOrderCmd.getGoodsImageSlaveUrl()); //合成图片 //中心点坐标x Long centerPointX = width / 2; //中心点坐标y Long centerPointY = heigh / 2; //数据组装 List<ComposeImageCoordinateDTO> coordinateList = new ArrayList<>(); for (int layerOrder = 0; layerOrder < layerOrderArray.length; layerOrder++) { Byte layer = layerOrderArray[layerOrder]; String imageUrl = imageUrlMap.get(layer); if (StringUtils.isNotBlank(imageUrl)) { ComposeImageCoordinateDTO composeImageCoordinate = new ComposeImageCoordinateDTO(); composeImageCoordinate.setWidth(width); composeImageCoordinate.setHeigh(heigh); composeImageCoordinate.setCenterPointX(centerPointX.intValue()); composeImageCoordinate.setCenterPointY(centerPointY.intValue()); composeImageCoordinate.setImageUrl(OSSUrlUtil.specifyWidthAndHeight(imageUrl, width.intValue(), heigh.intValue())); composeImageCoordinate.setLayer(layerOrder); coordinateList.add(composeImageCoordinate); } } return coordinateList; } /** * 创建透明图 * * @param width * @param height * @return */ public static BufferedImage createPng(int width, int height) { // 创建BufferedImage对象 BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); // 获取Graphics2D Graphics2D g2d = image.createGraphics(); // ---------- 背景透明开始 ----------------- image = g2d.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT); g2d.dispose(); // ---------- 背景透明代码结束 ----------------- return image; } /** * 计算转换后目标矩形的宽高 * * @param width * @param height * @param angle 角度 * @return 目标矩形 */ private static int[] calcRotatedSize(int width, int height, int angle) { if (angle == 0 || angle % 360 == 0) { return new int[]{width, height}; } double cos = Math.abs(Math.cos(Math.toRadians(angle))); double sin = Math.abs(Math.sin(Math.toRadians(angle))); int des_width = (int) (width * cos) + (int) (height * sin); int des_height = (int) (height * cos) + (int) (width * sin); return new int[]{des_width, des_height}; } /** * 贴图片 * * @param srcImage 底图 * @param pressImg 贴图 * @param x 坐标x(必填) * @param y 坐标y(必填) */ public static void stickImg(BufferedImage srcImage, BufferedImage pressImg, int x, int y) { if (ObjectUtils.isEmpty(srcImage) || ObjectUtils.isEmpty(pressImg)) { return; } //将图片放到指定位置 Graphics2D graphics2D = srcImage.createGraphics(); graphics2D.drawImage(pressImg, x, y, null); graphics2D.dispose(); } /** * 贴文字 * * @param srcImage 底图 * @param color 颜色 * @param font 字体 * @param text 文本 * @param x 坐标x(必填) * @param y 坐标y(必填) */ public static void stickText(BufferedImage srcImage, Color color, Font font, String text, int x, int y) { if (ObjectUtils.isEmpty(srcImage) || ObjectUtils.isEmpty(font) || StringUtils.isBlank(text)) { return; } // 创建画笔 Graphics2D graphics2D = srcImage.createGraphics(); // 设置画笔颜色为白色 // pen.setColor(Color.WHITE); graphics2D.setColor(color); // 设置画笔字体样式为微软雅黑,斜体,文字大小为20px graphics2D.setFont(font); // 写上水印文字和坐标 graphics2D.drawString(text, x, y); graphics2D.dispose(); } }