워터마크 개발은 웹 개발에서 비교적 흔한 기능이며, 구현된 코드는 매우 간단합니다. 이 글에서는 주로 JAVA 이미지 워터마크 개발 사례를 소개하며, 관심 있는 친구들이 참고할 수 있습니다
앞부분에 작성했습니다.
지난 주에 저는 워터마크 개발을 연구하는 데 일주일을 보냈고, 이제 마침내 입문용 데모를 작성하여 모든 사람이 참고할 수 있도록 공유했습니다. Demo는 지난번 작성한 JAVA 실습 사례의 파일 가져오기 및 내보내기(POI 방식) 프레임워크를 기반으로 Spring+SpringMVC를 기반으로 구축되었습니다. 실수가 있으면 정정해 주세요.간단한 소개
워터마크 유형:
단일 텍스트 워터마크단일 사진 워터마크
다중 텍스트 워터마크
다중 사진 워터마크
워터마크 개발 프로세스: 이미지 캐시 개체 만들기
- Java 그리기 도구 개체 만들기
- 그리기 도구 개체를 사용하여 원본 이미지를 캐시 이미지 개체에 그립니다
- 그리기 도구 개체를 사용하여 캐시 이미지에 워터마크(텍스트/그림)를 그립니다
- 이미지 인코딩 도구 클래스 만들기
- 이미지 인코딩 도구 클래스를 사용하여 캐시된 이미지를 대상 파일로 출력
렌더링:
원본 이미지:
단일 텍스트 워터마크:
단일 이미지 워터마크:
다중 텍스트 워터마크:
다중 이미지 워터마크:
ImageIO 도구 클래스를 통해 해당 이미지를 디코딩한 다음 BufferImage
개체를 만들고,BufferImage 개체를 통해 Graphics2D 개체를 만든 다음 원본 이미지를 BufferImage 개체에 그리는 것입니다. Graphics2D 개체를 통해. 그런 다음 Graphics2D 객체를 사용하여 워터마크 내용, 글꼴 크기, 글꼴 스타일 등과 같은 워터마크 관련 정보를 설정할 수도 있습니다. 여기서 설명해야 할 것은 워터마크 텍스트의 너비를 계산해야 한다는 것입니다. 중국어 길이는 텍스트 너비이고 영어 길이는 텍스트 너비의 절반입니다. 자세한 내용은 내 소스 코드의 관련 내용을 참조하세요.
//计算水印文本长度 //1、中文长度即文本长度 2、英文长度为文本长度二分之一 public int getTextLength(String text){ //水印文字长度 int length = text.length(); for (int i = 0; i < text.length(); i++) { String s =String.valueOf(text.charAt(i)); if (s.getBytes().length>1) { length++; } } length = length%2==0?length/2:length/2+1; return length; }
//添加单条文字水印方法 public String textWaterMark(MultipartFile myFile,String imageFileName) { InputStream is =null; OutputStream os =null; int X = 636; int Y = 700; try { //使用ImageIO解码图片 Image image = ImageIO.read(myFile.getInputStream()); //计算原始图片宽度长度 int width = image.getWidth(null); int height = image.getHeight(null); //创建图片缓存对象 BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); //创建java绘图工具对象 Graphics2D graphics2d = bufferedImage.createGraphics(); //参数主要是,原图,坐标,宽高 graphics2d.drawImage(image, 0, 0, width, height, null); graphics2d.setFont(new Font(FONT_NAME, FONT_STYLE, FONT_SIZE)); graphics2d.setColor(FONT_COLOR); //使用绘图工具将水印绘制到图片上 //计算文字水印宽高值 int waterWidth = FONT_SIZE*getTextLength(MARK_TEXT); int waterHeight = FONT_SIZE; //计算水印与原图高宽差 int widthDiff = width-waterWidth; int heightDiff = height-waterHeight; //水印坐标设置 if (X > widthDiff) { X = widthDiff; } if (Y > heightDiff) { Y = heightDiff; } //水印透明设置 graphics2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, ALPHA)); //纵坐标在下方,不增加字体高度会靠上 graphics2d.drawString(MARK_TEXT, X, Y+FONT_SIZE); graphics2d.dispose(); os = new FileOutputStream(UPLOAD_PATH+"/"+imageFileName); //创建图像编码工具类 JPEGImageEncoder en = JPEGCodec.createJPEGEncoder(os); //使用图像编码工具类,输出缓存图像到目标文件 en.encode(bufferedImage); if(is!=null){ is.close(); } if(os!=null){ os.close(); } } catch (IOException e) { e.printStackTrace(); } return "success"; }
단일 이미지 워터마크 개발
먼저 워터마크 이미지의 경로를 가져온 다음 워터마크 파일 객체를 생성해야 합니다. 또한 ImageIO 도구 클래스를 통해 워터마크 이미지를 디코딩합니다. 텍스트 하나의 길이와 너비가 워터마크 이미지의 길이이기 때문입니다.
//水印图片路径 //水印坐标设置 String logoPath = "/img/logo.png"; String realPath = request.getSession().getServletContext().getRealPath(logoPath); File logo = new File(realPath); Image imageLogo = ImageIO.read(logo); int widthLogo = imageLogo.getWidth(null); int heightLogo = imageLogo.getHeight(null); int widthDiff = width-widthLogo; int heightDiff = height-heightLogo; //水印坐标设置 if (X > widthDiff) { X = widthDiff; } if (Y > heightDiff) { Y = heightDiff; } //水印透明设置 graphics2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, ALPHA)); graphics2d.drawImage(imageLogo, X, Y, null);
다중 텍스트 워터마크 개발
//旋转原图,注意旋转角度为弧度制。后面两个参数为旋转的坐标中心 graphics2d.rotate(Math.toRadians(30), bufferedImage.getWidth()/2, bufferedImage.getHeight()/2); int x = -width/2; int y = -height/2; while(x < width*1.5){ y = -height/2; while(y < height*1.5){ graphics2d.drawString(MARK_TEXT, x, y); y+=waterHeight+100; } x+=waterWidth+100; }다중 사진 워터마크 개발
//水印图片路径 String logoPath = "/img/logo.png"; String realPath = request.getSession().getServletContext().getRealPath(logoPath); File logo = new File(realPath); Image imageLogo = ImageIO.read(logo); int widthLogo = imageLogo.getWidth(null); int heightLogo = imageLogo.getHeight(null); //水印透明设置 graphics2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, ALPHA)); graphics2d.rotate(Math.toRadians(30), bufferedImage.getWidth()/2, bufferedImage.getHeight()/2); int x = -width/2; int y = -height/2; while(x < width*1.5){ y = -height/2; while(y < height*1.5){ graphics2d.drawImage(imageLogo, x, y, null); y+=heightLogo+100; } x+=widthLogo+100; }
전체 비즈니스 코드:
import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Image; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import javax.imageio.ImageIO; import javax.servlet.http.HttpServletRequest; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import com.allan.service.WaterMarkService; import com.sun.image.codec.jpeg.JPEGCodec; import com.sun.image.codec.jpeg.JPEGImageEncoder; @Service public class WaterMarkServiceImpl implements WaterMarkService{ //定义上传的文件夹 private static final String UPLOAD_PATH = "E:/save"; //定义水印文字样式 private static final String MARK_TEXT = "小卖铺的老爷爷"; private static final String FONT_NAME = "微软雅黑"; private static final int FONT_STYLE = Font.BOLD; private static final int FONT_SIZE = 60; private static final Color FONT_COLOR = Color.black; private static final float ALPHA = 0.3F; //1、上传图片 public String uploadImage(MultipartFile myFile,String imageFileName) { InputStream is =null; OutputStream os =null; try{ is = myFile.getInputStream(); os = new FileOutputStream(UPLOAD_PATH+"/"+imageFileName); byte[] buffer =new byte[1024]; int len = 0; while ((len=is.read(buffer))>0){ os.write(buffer); } }catch(Exception e){ e.printStackTrace(); }finally{ if(is!=null){ try { is.close(); } catch (IOException e) { e.printStackTrace(); } } if(os!=null){ try { os.close(); } catch (IOException e2) { e2.printStackTrace(); } } } return "success"; } //添加单条文字水印 public String textWaterMark(MultipartFile myFile,String imageFileName) { InputStream is =null; OutputStream os =null; int X = 636; int Y = 700; try { Image image = ImageIO.read(myFile.getInputStream()); //计算原始图片宽度长度 int width = image.getWidth(null); int height = image.getHeight(null); //创建图片缓存对象 BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); //创建java绘图工具对象 Graphics2D graphics2d = bufferedImage.createGraphics(); //参数主要是,原图,坐标,宽高 graphics2d.drawImage(image, 0, 0, width, height, null); graphics2d.setFont(new Font(FONT_NAME, FONT_STYLE, FONT_SIZE)); graphics2d.setColor(FONT_COLOR); //使用绘图工具将水印绘制到图片上 //计算文字水印宽高值 int waterWidth = FONT_SIZE*getTextLength(MARK_TEXT); int waterHeight = FONT_SIZE; //计算水印与原图高宽差 int widthDiff = width-waterWidth; int heightDiff = height-waterHeight; //水印坐标设置 if (X > widthDiff) { X = widthDiff; } if (Y > heightDiff) { Y = heightDiff; } //水印透明设置 graphics2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, ALPHA)); graphics2d.drawString(MARK_TEXT, X, Y+FONT_SIZE); graphics2d.dispose(); os = new FileOutputStream(UPLOAD_PATH+"/"+imageFileName); //创建图像编码工具类 JPEGImageEncoder en = JPEGCodec.createJPEGEncoder(os); //使用图像编码工具类,输出缓存图像到目标文件 en.encode(bufferedImage); if(is!=null){ is.close(); } if(os!=null){ os.close(); } } catch (IOException e) { e.printStackTrace(); } return "success"; } //添加单图片水印 public String imageWaterMark(MultipartFile myFile,String imageFileName,HttpServletRequest request) { InputStream is =null; OutputStream os =null; int X = 636; int Y = 763; try { Image image = ImageIO.read(myFile.getInputStream()); //计算原始图片宽度长度 int width = image.getWidth(null); int height = image.getHeight(null); //创建图片缓存对象 BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); //创建java绘图工具对象 Graphics2D graphics2d = bufferedImage.createGraphics(); //参数主要是,原图,坐标,宽高 graphics2d.drawImage(image, 0, 0, width, height, null); graphics2d.setFont(new Font(FONT_NAME, FONT_STYLE, FONT_SIZE)); graphics2d.setColor(FONT_COLOR); //水印图片路径 String logoPath = "/img/logo.png"; String realPath = request.getSession().getServletContext().getRealPath(logoPath); File logo = new File(realPath); Image imageLogo = ImageIO.read(logo); int widthLogo = imageLogo.getWidth(null); int heightLogo = imageLogo.getHeight(null); int widthDiff = width-widthLogo; int heightDiff = height-heightLogo; //水印坐标设置 if (X > widthDiff) { X = widthDiff; } if (Y > heightDiff) { Y = heightDiff; } //水印透明设置 graphics2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, ALPHA)); graphics2d.drawImage(imageLogo, X, Y, null); graphics2d.dispose(); os = new FileOutputStream(UPLOAD_PATH+"/"+imageFileName); //创建图像编码工具类 JPEGImageEncoder en = JPEGCodec.createJPEGEncoder(os); //使用图像编码工具类,输出缓存图像到目标文件 en.encode(bufferedImage); if(is!=null){ is.close(); } if(os!=null){ os.close(); } } catch (IOException e) { e.printStackTrace(); } return "success"; } //添加多条文字水印 public String moreTextWaterMark(MultipartFile myFile,String imageFileName) { InputStream is =null; OutputStream os =null; int X = 636; int Y = 763; try { Image image = ImageIO.read(myFile.getInputStream()); //计算原始图片宽度长度 int width = image.getWidth(null); int height = image.getHeight(null); //创建图片缓存对象 BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); //创建java绘图工具对象 Graphics2D graphics2d = bufferedImage.createGraphics(); //参数主要是,原图,坐标,宽高 graphics2d.drawImage(image, 0, 0, width, height, null); graphics2d.setFont(new Font(FONT_NAME, FONT_STYLE, FONT_SIZE)); graphics2d.setColor(FONT_COLOR); //使用绘图工具将水印绘制到图片上 //计算文字水印宽高值 int waterWidth = FONT_SIZE*getTextLength(MARK_TEXT); int waterHeight = FONT_SIZE; //水印透明设置 graphics2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, ALPHA)); graphics2d.rotate(Math.toRadians(30), bufferedImage.getWidth()/2, bufferedImage.getHeight()/2); int x = -width/2; int y = -height/2; while(x < width*1.5){ y = -height/2; while(y < height*1.5){ graphics2d.drawString(MARK_TEXT, x, y); y+=waterHeight+100; } x+=waterWidth+100; } graphics2d.dispose(); os = new FileOutputStream(UPLOAD_PATH+"/"+imageFileName); //创建图像编码工具类 JPEGImageEncoder en = JPEGCodec.createJPEGEncoder(os); //使用图像编码工具类,输出缓存图像到目标文件 en.encode(bufferedImage); if(is!=null){ is.close(); } if(os!=null){ os.close(); } } catch (IOException e) { e.printStackTrace(); } return "success"; } //多图片水印 public String moreImageWaterMark(MultipartFile myFile,String imageFileName,HttpServletRequest request) { InputStream is =null; OutputStream os =null; int X = 636; int Y = 763; try { Image image = ImageIO.read(myFile.getInputStream()); //计算原始图片宽度长度 int width = image.getWidth(null); int height = image.getHeight(null); //创建图片缓存对象 BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); //创建java绘图工具对象 Graphics2D graphics2d = bufferedImage.createGraphics(); //参数主要是,原图,坐标,宽高 graphics2d.drawImage(image, 0, 0, width, height, null); graphics2d.setFont(new Font(FONT_NAME, FONT_STYLE, FONT_SIZE)); graphics2d.setColor(FONT_COLOR); //水印图片路径 String logoPath = "/img/logo.png"; String realPath = request.getSession().getServletContext().getRealPath(logoPath); File logo = new File(realPath); Image imageLogo = ImageIO.read(logo); int widthLogo = imageLogo.getWidth(null); int heightLogo = imageLogo.getHeight(null); //水印透明设置 graphics2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, ALPHA)); graphics2d.rotate(Math.toRadians(30), bufferedImage.getWidth()/2, bufferedImage.getHeight()/2); int x = -width/2; int y = -height/2; while(x < width*1.5){ y = -height/2; while(y < height*1.5){ graphics2d.drawImage(imageLogo, x, y, null); y+=heightLogo+100; } x+=widthLogo+100; } graphics2d.dispose(); os = new FileOutputStream(UPLOAD_PATH+"/"+imageFileName); //创建图像编码工具类 JPEGImageEncoder en = JPEGCodec.createJPEGEncoder(os); //使用图像编码工具类,输出缓存图像到目标文件 en.encode(bufferedImage); if(is!=null){ is.close(); } if(os!=null){ os.close(); } } catch (IOException e) { e.printStackTrace(); } return "success"; } //计算水印文本长度 //1、中文长度即文本长度 2、英文长度为文本长度二分之一 public int getTextLength(String text){ //水印文字长度 int length = text.length(); for (int i = 0; i < text.length(); i++) { String s =String.valueOf(text.charAt(i)); if (s.getBytes().length>1) { length++; } } length = length%2==0?length/2:length/2+1; return length; } }
마지막으로 이 데모는 마지막 파일 가져오기 및 내보내기 프레임워크를 기반으로 작성되었습니다. 소스 코드에는 주로 사용되는 몇 가지 다른 데모 코드가 있습니다. WaterMarkController.java, WaterMarkService.java, WaterMarkServiceImpl.java를 E:/save 폴더에 하드코딩했기 때문에 실행을 원하시면 먼저 이 폴더를 생성하시거나 다른 파일로 변경해 주시기 바랍니다. 괜찮아요.
위 내용은 Java에서 사진에 워터마크를 추가하는 방법에 대한 예제 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

JVM은 Java 코드를 기계 코드로 변환하고 리소스를 관리하여 작동합니다. 1) 클래스로드 : .class 파일을 메모리에로드하십시오. 2) 런타임 데이터 영역 : 메모리 영역 관리. 3) 실행 엔진 : 해석 또는 컴파일 바이트 코드. 4) 로컬 메소드 인터페이스 : JNI를 통해 운영 체제와 상호 작용합니다.

JVM을 통해 Java는 플랫폼을 가로 질러 실행할 수 있습니다. 1) JVM 하중, 검증 및 바이트 코드를 실행합니다. 2) JVM의 작업에는 클래스 로딩, 바이트 코드 검증, 해석 실행 및 메모리 관리가 포함됩니다. 3) JVM은 동적 클래스 로딩 및 반사와 같은 고급 기능을 지원합니다.

jvmmanagesgarbageCollectionAcrossplatformSefficialthegendercationalStrationallySticallySticallySuciationalStrationalSproachandAptingToosandHardwaredifferences.ITEMPLOYSVARIOUSCOLLECTORSLIKESERIAL, PARALING, CMS, 및 G1, 각각의 소지 firedFferentscenarios.performanceCanbetwithflags-xex : xa

Java의 "Write Onge, Run Everywhere"철학은 JVM (Java Virtual Machine)에서 구현되므로 Java Code는 수정없이 다른 운영 체제에서 실행할 수 있습니다. 컴파일 된 Java Bytecode와 운영 체제 사이의 중개자로서 JVM은 바이트 코드를 특정 시스템 지침으로 변환하여 프로그램이 JVM이 설치된 모든 플랫폼에서 독립적으로 실행될 수 있도록합니다.

Java 프로그램의 편집 및 실행은 Bytecode 및 JVM을 통해 플랫폼 독립성을 달성합니다. 1) Java 소스 코드를 작성하여 바이트 코드로 컴파일하십시오. 2) JVM을 사용하여 모든 플랫폼에서 바이트 코드를 실행하여 코드가 플랫폼에서 실행되도록합니다.

Java 성능은 하드웨어 아키텍처와 밀접한 관련이 있으며이 관계를 이해하면 프로그래밍 기능이 크게 향상 될 수 있습니다. 1) JVM은 JIT 컴파일을 통해 Java Bytecode를 기계 지침으로 변환하여 CPU 아키텍처의 영향을받습니다. 2) 메모리 관리 및 쓰레기 수집은 RAM 및 메모리 버스 속도의 영향을받습니다. 3) 캐시 및 분기 예측은 Java 코드 실행을 최적화합니다. 4) 멀티 코어 시스템의 멀티 스레딩 및 병렬 처리는 성능을 향상시킵니다.

기본 라이브러리를 사용하면 각 운영 체제마다 별도로 컴파일해야하기 때문에 Java의 플랫폼 독립성이 파괴됩니다. 1) 기본 라이브러리는 JNI를 통해 Java와 상호 작용하여 Java가 직접 구현할 수없는 기능을 제공합니다. 2) 기본 라이브러리를 사용하면 프로젝트 복잡성이 증가하고 다른 플랫폼에 대한 라이브러리 파일을 관리해야합니다. 3) 기본 라이브러리는 성능을 향상시킬 수 있지만,주의해서 사용해야하고 크로스 플랫폼 테스트를 수행해야합니다.

JVM은 JNI (JavanativeInterface) 및 Java 표준 라이브러리를 통한 운영 체제 API 차이를 처리합니다. 1. JNI는 Java 코드가 로컬 코드를 호출하고 운영 체제 API와 직접 상호 작용할 수 있습니다. 2. Java Standard Library는 통합 API를 제공하며,이 API는 내부적으로 다른 운영 체제 API에 매핑되어 코드가 플랫폼에서 실행되도록합니다.


핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

Video Face Swap
완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

인기 기사

뜨거운 도구

mPDF
mPDF는 UTF-8로 인코딩된 HTML에서 PDF 파일을 생성할 수 있는 PHP 라이브러리입니다. 원저자인 Ian Back은 자신의 웹 사이트에서 "즉시" PDF 파일을 출력하고 다양한 언어를 처리하기 위해 mPDF를 작성했습니다. HTML2FPDF와 같은 원본 스크립트보다 유니코드 글꼴을 사용할 때 속도가 느리고 더 큰 파일을 생성하지만 CSS 스타일 등을 지원하고 많은 개선 사항이 있습니다. RTL(아랍어, 히브리어), CJK(중국어, 일본어, 한국어)를 포함한 거의 모든 언어를 지원합니다. 중첩된 블록 수준 요소(예: P, DIV)를 지원합니다.

안전한 시험 브라우저
안전한 시험 브라우저는 온라인 시험을 안전하게 치르기 위한 보안 브라우저 환경입니다. 이 소프트웨어는 모든 컴퓨터를 안전한 워크스테이션으로 바꿔줍니다. 이는 모든 유틸리티에 대한 액세스를 제어하고 학생들이 승인되지 않은 리소스를 사용하는 것을 방지합니다.

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

SecList
SecLists는 최고의 보안 테스터의 동반자입니다. 보안 평가 시 자주 사용되는 다양한 유형의 목록을 한 곳에 모아 놓은 것입니다. SecLists는 보안 테스터에게 필요할 수 있는 모든 목록을 편리하게 제공하여 보안 테스트를 더욱 효율적이고 생산적으로 만드는 데 도움이 됩니다. 목록 유형에는 사용자 이름, 비밀번호, URL, 퍼징 페이로드, 민감한 데이터 패턴, 웹 셸 등이 포함됩니다. 테스터는 이 저장소를 새로운 테스트 시스템으로 간단히 가져올 수 있으며 필요한 모든 유형의 목록에 액세스할 수 있습니다.

Eclipse용 SAP NetWeaver 서버 어댑터
Eclipse를 SAP NetWeaver 애플리케이션 서버와 통합합니다.
