功能介绍
对图片生成图片水印或者文字水印 ,用户可以自定义自己的图片水印
思路就是将文字和图片水印合成到一张图片上面去,前台展示合成效果
后台合成后保存到cos上面,并在数据库保存相关信息,方便用户下次引用
前台样式
后端代码
接参DTO
@Data
public class WatermarkTemplateDTO extends BasePageParamDTO{/*** 主键ID*/@ApiModelProperty(value="主键ID")private Long id;/*** 用户名*/@ApiModelProperty(value="用户名")private Long userId;/*** 水印名称*/@ApiModelProperty(value="水印名称")private String watermarkName;/*** 合成后的水印图片地址*/@ApiModelProperty(value="合成后的水印图片地址")private String watermarkUrl;/*** 素材图片URL地址*/@ApiModelProperty(value="素材图片URL地址")private String picUrl;/*** 图片透明度*/@ApiModelProperty(value="图片透明度")private Integer picOpacity;/*** 图片左边距*/@ApiModelProperty(value="图片左边距")private Integer picLeft;/*** 图片上边距*/@ApiModelProperty(value="图片上边距")private Integer picTop;/*** 图片宽度*/@ApiModelProperty(value="图片宽度")private Integer picWidth;/*** 图片高度*/@ApiModelProperty(value="图片高度")private Integer picHeight;/*** 水印文字*/@ApiModelProperty(value="水印文字")private String watermarkText;/*** 文字透明度*/@ApiModelProperty(value="文字透明度")private Float textOpacity;/*** 文字字体大小*/@ApiModelProperty(value="文字字体大小")private Integer textSize;/*** 文字字体颜色*/@ApiModelProperty(value="文字字体颜色")private String textColor;/*** 文字位置-距离图片的左边距*/@ApiModelProperty(value="文字位置-距离图片的左边距")private Integer textLeft;/*** 文字位置-距离图片的上边距*/@ApiModelProperty(value="文字位置-距离图片的上边距")private Integer textTop;/*** 文字字体*/@ApiModelProperty(value="文字字体")private String textFont;/*** 图片翻转,0:不翻转 1: 水平翻转 2 : 垂直翻转*/@ApiModelProperty(value="图片翻转,0:不翻转 1: 水平翻转 2 : 垂直翻转")private Integer picTurn;/*** 图片旋转角度*/@ApiModelProperty(value="图片旋转角度")private Integer picRotate;/*** 创建时间*/@ApiModelProperty(value="创建时间")private Date createDate;}
数据库实体类
/*** 水印模板表*/
@ApiModel(value = "com-menglar-soap-item-pojo-mysql-WatermarkTemplate")
@Data
@NoArgsConstructor
@TableName(value = "item_watermark_template")
public class WatermarkTemplate implements Serializable {/*** 主键ID*/@ApiModelProperty(value = "主键ID")private Long id;/*** 用户名*/@ApiModelProperty(value = "用户名")private Long userId;/*** 水印名称*/@ApiModelProperty(value = "水印名称")private String watermarkName;/*** 合成后的水印图片地址*/@ApiModelProperty(value = "合成后的水印图片地址")private String watermarkUrl;/*** 素材图片URL地址*/@ApiModelProperty(value = "素材图片URL地址")private String picUrl;/*** 图片透明度*/@ApiModelProperty(value = "图片透明度")private Integer picOpacity;/*** 图片左边距*/@ApiModelProperty(value = "图片左边距")private Integer picLeft;/*** 图片上边距*/@ApiModelProperty(value = "图片上边距")private Integer picTop;/*** 图片宽度*/@ApiModelProperty(value = "图片宽度")private Integer picWidth;/*** 图片高度*/@ApiModelProperty(value = "图片高度")private Integer picHeight;/*** 水印文字*/@ApiModelProperty(value = "水印文字")private String watermarkText;/*** 文字透明度*/@ApiModelProperty(value = "文字透明度")private Float textOpacity;/*** 文字字体大小*/@ApiModelProperty(value = "文字字体大小")private Integer textSize;/*** 文字字体颜色*/@ApiModelProperty(value = "文字字体颜色")private String textColor;/*** 文字位置-距离图片的左边距*/@ApiModelProperty(value = "文字位置-距离图片的左边距")private Integer textLeft;/*** 文字位置-距离图片的上边距*/@ApiModelProperty(value = "文字位置-距离图片的上边距")private Integer textTop;/*** 文字字体*/@ApiModelProperty(value = "文字字体")private String textFont;/*** 图片翻转,0:不翻转 1: 水平翻转 2 : 垂直翻转*/@ApiModelProperty(value = "图片翻转,0:不翻转 1: 水平翻转 2 : 垂直翻转")private Integer picTurn;/*** 图片旋转角度*/@ApiModelProperty(value = "图片旋转角度")private Integer picRotate;/*** 创建时间*/@ApiModelProperty(value = "创建时间")private Date createDate;private static final long serialVersionUID = 1L;
}
水印生成方法
/*** 上传水印模板* @param wtDto* @return*/@Overridepublic Result uploadWatermarkPic(WatermarkTemplateDTO wtDto) {if (StringUtils.isBlank(wtDto.getPicUrl()) && StringUtils.isBlank(wtDto.getWatermarkText())) {throw new BusinessException("素材图片/水印文字不能为空");}try {BufferedImage output = new BufferedImage(800, 800, BufferedImage.TYPE_INT_ARGB);Graphics2D g2 = output.createGraphics();output = g2.getDeviceConfiguration().createCompatibleImage(800, 800, Transparency.TRANSLUCENT);g2 = output.createGraphics();//调制透明度for (int j1 = output.getMinY(); j1 < output.getHeight(); j1++) {for (int j2 = output.getMinX(); j2 < output.getWidth(); j2++) {int rgb = output.getRGB(j2, j1);rgb = ((0 * 255 / 10) << 24) | (rgb & 0x00ffffff);output.setRGB(j2, j1, rgb);}}//1、先画一张完全透明的背景图g2.drawImage(output, 0, 0, 800, 800, null);//图片水印if(StringUtils.isNotBlank(wtDto.getPicUrl())){log.info("图片参数:opacity:{},height:{},picLeft:{},picTop:{},picWidth:{}", wtDto.getPicOpacity(), wtDto.getPicHeight(), wtDto.getPicLeft(), wtDto.getPicTop(), wtDto.getPicWidth());Integer alpha = wtDto.getPicOpacity();if(alpha == null|| wtDto.getPicOpacity() == null|| wtDto.getPicHeight() == null|| wtDto.getPicLeft() == null|| wtDto.getPicTop() == null|| wtDto.getPicWidth() == null){throw new BusinessException("图片参数错误!");}//检查透明度是否越界if (alpha < 0) {alpha = 0;} else if (alpha > 10) {alpha = 10;}InputStream inputStream = CompositeWatermarkUtil.downloadFile(wtDto.getPicUrl());BufferedImage image = ImageIO.read(inputStream);//2、再画水印素材图片g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,alpha.floatValue()/10));g2.drawImage(image, equalRatioCalculation(wtDto.getPicLeft()), equalRatioCalculation(wtDto.getPicTop()), equalRatioCalculation(wtDto.getPicWidth()), equalRatioCalculation(wtDto.getPicHeight()), null);}//文字水印if (StringUtils.isNotBlank(wtDto.getWatermarkText())) {log.info("文字参数:textLeft:{},TextLeft:{},TextOpacity:{},TextColor:{},TextSize:{},TextFont:{}", wtDto.getTextLeft(), wtDto.getTextTop(), wtDto.getTextOpacity(), wtDto.getTextColor(), wtDto.getTextSize(), wtDto.getTextFont());if(wtDto.getTextLeft() == null|| wtDto.getTextTop() == null|| wtDto.getTextOpacity() == null|| wtDto.getTextColor() == null|| wtDto.getTextSize() == null|| wtDto.getTextFont() == null){throw new BusinessException("文字参数错误!");}try {String[] availableFontFamilyNames = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();log.info("测试环境字体列表数组为:" + availableFontFamilyNames);if(availableFontFamilyNames != null){log.info("测试环境字体列表数组长度:" + availableFontFamilyNames.length);log.info("测试环境字体列表获取首个:" + availableFontFamilyNames[0]);}for(String s : availableFontFamilyNames){log.info(s);}} catch (Exception e) {e.printStackTrace();}//3、画文字String textColor = wtDto.getTextColor();//字体Font font = new Font(wtDto.getTextFont(), Font.BOLD, wtDto.getTextSize());//水印字体,大小//颜色Color color = new Color(Integer.decode(textColor));g2.setFont(font);g2.setColor(color);g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, wtDto.getTextOpacity()));log.info("画文字开始");g2.drawString(wtDto.getWatermarkText(), equalRatioCalculation(wtDto.getTextLeft()), equalRatioCalculation(wtDto.getTextTop()));log.info("画文字结束");}//上传合成的水印ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();ImageIO.write(output, "png", arrayOutputStream);byte[] bytes = arrayOutputStream.toByteArray();ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);Long userId = SecurityUtils.getUserId();//如果是编辑后保存,则有水印模板URL,使用原水印模板地址,新水印模板会替换掉旧的if (BeanUtil.isNotEmpty(wtDto.getId())) {WatermarkTemplate watermarkTemplate = watermarkTemplateMapper.selectOne(Wrappers.lambdaQuery(WatermarkTemplate.class).eq(WatermarkTemplate::getId, wtDto.getId()));String key = cosClient.buildKey(watermarkTemplate.getWatermarkUrl());cosClient.uploadFile(byteArrayInputStream, key);BeanUtil.copyProperties(wtDto, watermarkTemplate, CopyOptions.create().setIgnoreError(true).ignoreNullValue());watermarkTemplateMapper.updateByPrimaryKeySelective(watermarkTemplate);} else {//没有水印模板URL则是新增水印模板,生成新的String key = cosClient.buildStoragePath(FileTypeEnum.IMAGE, "png", userId);String watermarkTemplateUrl = cosClient.uploadFile(byteArrayInputStream, key);WatermarkTemplate watermarkTemplate = new WatermarkTemplate();BeanUtil.copyProperties(wtDto, watermarkTemplate, CopyOptions.create().setIgnoreError(true).ignoreNullValue());watermarkTemplate.setWatermarkUrl(watermarkTemplateUrl);watermarkTemplate.setCreateDate(new Date());watermarkTemplate.setUserId(userId);watermarkTemplateMapper.insert(watermarkTemplate);}return Result.ok(StatusCode.SUCCESS, "保存水印成功");} catch (Exception e) {e.printStackTrace();return Result.errorMessage("保存水印失败,系统异常", StatusCode.FAILURE.code());}}/*** 水印素材位置等比例计算* @param value* @return*/private Integer equalRatioCalculation(Integer value){BigDecimal waterSize = new BigDecimal(800);//水印最大尺寸BigDecimal editerSize = new BigDecimal(500);//前端编辑框尺寸BigDecimal ratio = editerSize.divide(waterSize);BigDecimal valBd = new BigDecimal(value);BigDecimal finalVal = valBd.divide(ratio);int i = finalVal.intValue();return i;}