记录一个制作图片水印的功能

功能介绍

对图片生成图片水印或者文字水印 ,用户可以自定义自己的图片水印

思路就是将文字和图片水印合成到一张图片上面去,前台展示合成效果

后台合成后保存到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
	 */
	@Override
	public 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;
	}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值