1.1 項目背景
最近公司上了不少傳播方面的需求,免不了合成各種營銷圖片,圖片合成本身并不是什么高深的技術(shù),但用底層api去搞確實繁瑣,于是抽時間封裝了一個小工具,初衷是解放生產(chǎn)力,后來發(fā)現(xiàn)挺好使,那就開源吧,花了一個整天重新整理了一下代碼,作為自己從業(yè)十年第一個開源項目(打破零記錄,哈哈),希望能夠幫助到需要的小伙伴~
1.2 ImageCombiner能夠做什么?
ImageCombiner是一個專門用于Java服務(wù)端圖片合成的工具,沒有很復(fù)雜的功能,簡單實用,從實際業(yè)務(wù)場景出發(fā),提供簡單的接口,幾行代碼即可實現(xiàn)圖片拼合(當(dāng)然用于合成水印也可以),素材上支持圖片、文本、矩形三種,支持定位、縮放、旋轉(zhuǎn)、圓角、透明度、顏色、字體、字號、刪除線、居中繪制、文本自動換行等特性,足夠覆蓋圖片合成的日常需求。
1.3 先看一下效果
基本功能展示(更多效果可自行探索)
專門開了一個作品搜集&展示的issue,可以秀一秀成果,順帶分享下思路~
作品收集&展示專貼,大家可以把作品發(fā)到這里~ · Issue #I4FVGB · dromara/image-combiner - Gitee.com
1.4 UML
1.5 ImageCombiner怎么使用
ImageCombiner使用起來相當(dāng)簡單,主要的類只用一個,new一個ImageCombiner對象,指定背景圖片和輸出格式,然后加入各種素材元素,設(shè)置元素的位置、大小和效果(如圓角、顏色、透明度等),調(diào)用combine()方法即可。combine()方法直接返回BufferedImage對象,也可以調(diào)用getCombinedImageStream()獲得流,方便上傳oss等后續(xù)操作,或者調(diào)用save()方法保存到本地,調(diào)試的時候比較方便。
1.6 版本要求
項目不依賴任何框架,完全基于JDK本身編寫,沒有各種花里胡哨的東西,性能還是相當(dāng)不錯的。
二. 示例代碼
2.1 安裝
注意:合成圖片若包含文字的話,開發(fā)機和服務(wù)器要先安裝相應(yīng)的字體,否則看不出效果,默認使用的字體為“阿里巴巴普惠體”(見font目錄)。
PS1:一些小伙伴反應(yīng)安裝了字體后仍然看不出效果,這多數(shù)是因為實際的字體名稱跟你看到的文件名是不一樣的,可以跑一下測試項目里的showFonts()方法,打印出系統(tǒng)可用字體列表,找一找你剛安裝的字體的真實的名字。
PS2:版本2.6.3開始字體文件可以打包進項目資源,使用時將字體名改為文件路徑即可(不需要在服務(wù)器安裝字體了),如果報空指針,請檢查打包后的資源是否包含字體文件,以及路徑是否正確。字體文件隨項目,性能不及在服務(wù)器安裝,不是首推。
在項目中加入以下依賴:
<dependency> <groupId>com.freewayso</groupId> <artifactId>image-combiner</artifactId> <version>2.6.3</version> </dependency>
2.2 最簡單的例子
public void simpleDemo() throws Exception { //合成器(指定背景圖和輸出格式,整個圖片的寬高和相關(guān)計算依賴于背景圖,所以背景圖的大小是個基準) ImageCombiner combiner = new ImageCombiner("http://xxx.com/image/bg.jpg", OutputFormat.JPG); //加圖片元素 combiner.addImageElement("http://xxx.com/image/product.png", 0, 300); //加文本元素 combiner.addTextElement("周末大放送", 60, 100, 960); //執(zhí)行圖片合并 combiner.combine(); //可以獲取流(并上傳oss等) InputStream is = combiner.getCombinedImageStream(); //也可以保存到本地 //combiner.save("d://image.jpg");
2.3 完整示例
public void demo() throws Exception { //圖片元素可以是Url(支持file協(xié)議),也可以是BufferImage對象 String bgImageUrl = "http://xxx.com/image/bg.jpg"; //背景圖(url) String qrCodeUrl = "file:///d:/qrCode.png"; //二維碼(file協(xié)議) String productImageUrl = "https://xxx.com/image/product.jpg"; //商品圖 BufferedImage waterMark = ImageIO.read(new URL("https://xxx.com/image/waterMark.jpg")); //水印圖 BufferedImage avatar = ImageIO.read(new File("d:/avatar.jpg")); //頭像 String title = "# 最愛的家居"; //標(biāo)題文本 String content = "蘇格拉底說:“如果沒有那個桌子,可能就沒有那個水壺”"; //內(nèi)容文本 //創(chuàng)建合成器(指定背景圖和輸出格式,整個圖片的寬高和相關(guān)計算依賴于背景圖,所以背景圖的大小是個基準) ImageCombiner combiner = new ImageCombiner(bgImageUrl, 1500, 0, ZoomMode.Height, OutputFormat.JPG); //v1.1.4之后可以指定背景圖新寬高了(不指定則默認用圖片原寬高) //針對背景和整圖的設(shè)置 combiner.setBackgroundBlur(30); //設(shè)置背景高斯模糊(毛玻璃效果) combiner.setCanvasRoundCorner(100); //設(shè)置整圖圓角(輸出格式必須為PNG) combiner.setQuality(.8f); //設(shè)置圖片保存質(zhì)量(0.0~1.0,Java9以下僅jpg格式有效) //標(biāo)題(默認字體為阿里普惠、黑色,也可以自己指定Font對象) combiner.addTextElement(title, 0, 150, 1400) .setCenter(true) //居中繪制(會忽略x坐標(biāo),改為自動計算) .setAlpha(.8f) //透明度(0.0~1.0) .setRotate(45) //旋轉(zhuǎn)(0~360) .setColor(Color.Red) //顏色 .setDirection(Direction.RightLeft) //繪制方向(從右到左,用于需要右對齊場景) .setAutoFitWidth(200); //自適應(yīng)最大寬度(超出則自動縮小字體) //副標(biāo)題(v2.6.3版本開始支持加載項目內(nèi)字體文件,可以不用在服務(wù)器安裝,性能略低) combiner.addTextElement("年度狂歡", "/font/yahei.ttf", 0, 150, 1450) //內(nèi)容(設(shè)置文本自動換行,需要指定最大寬度(超出則換行)、最大行數(shù)(超出則丟棄)、行高) combiner.addTextElement(content, "微軟雅黑", Font.BOLD, 40, 150, 1480) .setSpace(.5f) //字間距 .setStrikeThrough(true) //刪除線 .setAutoBreakLine(837, 2, 60); //自動換行(還有一個LineAlign參數(shù)可以指定對齊方式) //商品圖(設(shè)置坐標(biāo)、寬高和縮放模式,若按寬度縮放,則高度按比例自動計算) combiner.addImageElement(productImageUrl, 0, 160, 837, 0, ZoomMode.Width) .setCenter(true) //居中繪制(會忽略x坐標(biāo),改為自動計算) .setRoundCorner(46) //設(shè)置圓角 //頭像(圓角設(shè)置一定的大小,可以把頭像變成圓的) combiner.addImageElement(avatar, 200, 1200) .setRoundCorner(200); //圓角 //水?。ㄔO(shè)置透明度,0.0~1.0) combiner.addImageElement(waterMark, 630, 1200) .setAlpha(.8f) //透明度(0.0~1.0) .setRotate(45) //旋轉(zhuǎn)(0~360) .setBlur(20) //高斯模糊(1~100) .setRepeat(true, 100, 50); //平鋪繪制(可設(shè)置水平、垂直間距) //加入圓角矩形元素(版本>=1.2.0),作為二維碼的底襯 combiner.addRectangleElement(138, 1707, 300, 300) .setColor(Color.WHITE) .setRoundCorner(50) //該值大于等于寬高時,就是圓形,如設(shè)為300 .setAlpha(.8f) .setGradient(Color.yellow,Color.blue,GradientDirection.LeftRight); //顏色漸變 .setBorderSize(5); //設(shè)置border大小就是空心,不設(shè)置就是實心 //二維碼(強制按指定寬度、高度縮放) combiner.addImageElement(qrCodeUrl, 138, 1707, 186, 186, ZoomMode.WidthHeight); //價格(元素對象也可以直接new,然后手動加入待繪制列表) TextElement textPrice = new TextElement("¥1290", 60, 230, 1300); textPrice.setColor(Color.red); //紅色 textPrice.setStrikeThrough(true); //刪除線 combiner.addElement(textPrice); //加入待繪制集合 //執(zhí)行圖片合并 combiner.combine(); //可以獲取流(并上傳oss等) InputStream is = combiner.getCombinedImageStream(); //也可以保存到本地 //combiner.save("d://image.jpg"); }
更多示例請參見項目測試方法,傳送門
2.4 小技巧
2.4.1 如何動態(tài)拼接文本
實際需求中,經(jīng)常會在一段固定文案里,填充寬度不定的文本或數(shù)字(如用戶昵稱、價格等),那中間待填充的空白部分留多少合適呢? 在這個場景下,我們一般會把一行文案拆分成多段,構(gòu)建多個TextElement,共同拼成一句話,后一個TextElement的x坐標(biāo), 通過動態(tài)計算前一個TextElement的實際寬度后,累加得來。
以下例子中,我們以“您出征XX,共在前線戰(zhàn)斗了XX天!”這行為例, 由于兩個XX都是調(diào)用時傳進來的參數(shù),實際繪制寬度不固定,所以我們把這一行切分成5段,用5個TextElement動態(tài)計算位置,然后拼接起來。
public void dynamicWidthDemoTest() throws Exception { String bg = "http://xxx.com/image/bg.jpg"; ImageCombiner combiner = new ImageCombiner(bg, OutputFormat.JPG); String str1 = "您出征"; String str2 = "某城市"; //外部傳參,內(nèi)容不定,寬度也不定 String str3 = ",共在前線戰(zhàn)斗了"; String str4 = "365"; //外部傳參,內(nèi)容不定,寬度也不定 String str5 = "天!"; int fontSize = 60; int xxxFontSize = 80; int offsetX = 20; //通過計算前一個元素的實際寬度,并累加這個偏移量,得到后一個元素正確的x坐標(biāo)值 int y = 300; //第一段 TextElement element1 = combiner.addTextElement(str1, fontSize, offsetX, y) .setBaseLine(BaseLine.Bottom); //設(shè)置坐標(biāo)參考基線為文字左下角(可以認為是下對齊) offsetX += element1.getWidth(); //計算寬度,并累加偏移量 //第二段(內(nèi)容不定,寬度也不定) TextElement element2 = combiner.addTextElement(str2, xxxFontSize, offsetX, y) .setBaseLine(BaseLine.Bottom) .setColor(Color.red); offsetX += element2.getWidth(); //第三段 TextElement element3 = combiner.addTextElement(str3, fontSize, offsetX, y) .setBaseLine(BaseLine.Bottom); offsetX += element3.getWidth(); //第四段(內(nèi)容不定,寬度也不定) TextElement element4 = combiner.addTextElement(str4, xxxFontSize, offsetX, y) .setBaseLine(BaseLine.Bottom) .setColor(Color.red); offsetX += element4.getWidth(); //第五段 combiner.addTextElement(str5, fontSize, offsetX, y) .setBaseLine(BaseLine.Bottom); combiner.combine(); combiner.save("d://demo.jpg"); }
實際運行效果
動態(tài)計算高度也是同樣的原理,比方要把價格顯示在商品描述下面,但商品描述不定有多少行,那此時價格元素的y坐標(biāo)就是不確定的,可以通過調(diào)用textElement.getHeight()方法,得到上一個元素的高度,累加計算后續(xù)元素的y坐標(biāo)。
2.4.2 如何文字豎排
graphics2D沒有直接的豎排方法,本項目也未做封裝,不過可以變相通過自動換行特性來實現(xiàn),只要將textElement設(shè)置為自動換行,且行寬設(shè)為0即可(v2.3.8以下版本,要設(shè)為大于1個字符且小于2個字符的寬度),行間距可以通過行高參數(shù)調(diào)節(jié)。
public void verticalTextTest() throws Exception { ImageCombiner combiner = new ImageCombiner("https://img.thebeastshop.com/combine_image/funny_topic/resource/bg_3x4.png", OutputFormat.JPG); //添加文字,并設(shè)置為自動換行,且行寬設(shè)為0 combiner.addTextElement("通過自動換行功能,實現(xiàn)文字豎排", 50, 200, 100) .setAutoBreakLine(0, 50, 60); combiner.addTextElement("將文本元素設(shè)為自動換行,且行寬設(shè)為0即可", 50, 300, 100) .setAutoBreakLine(0, 50, 60, LineAlign.Center); //合成圖片 combiner.combine(); combiner.save("d://verticalTextTest.jpg"); }
實際運行效果
2.4.3 如何與設(shè)計稿保持一致
最近有朋友反饋,嚴格按照設(shè)計師給的UI稿設(shè)置坐標(biāo),出來的效果與UI稿并不完全一樣。這是一個已知問題,涉及文本繪制的一些基本概念,文本有多種基線(見下圖) ,諸如sketch等設(shè)計軟件是基于ascent來顯示邊距,而graphics2D則是基于base繪制的,所以會出現(xiàn)y軸上的位置偏差,除了基線以外,行高也會影響文本的最終位置。 ImageCombiner在v2.0.0版本優(yōu)化了這個問題,讓繪制參照基線與UI設(shè)計軟件保持一致,同時也考慮了行高因素,這樣開發(fā)同學(xué)只要按照UI稿設(shè)置參數(shù)就ok了。
v2.5.1版本開始支持設(shè)置參考基線了(左上或左下),用于某些希望底部對齊的場景,如2.4.1中的示例。
注意:如您在項目里已使用低版本(<2.0.0),由于計算原理不同,升級后文本位置會出現(xiàn)偏差,需要手動調(diào)整,以適應(yīng)新版本計算方式,請慎重更新?。?!
下圖為ImageComber繪制結(jié)果與sketch設(shè)計稿對比,程序代碼:combiner.addTextElement("點擊畫布上方", 60, 0, 0).setLineHeight(100);
2.6.0版本開始,支持對文本元素設(shè)置Baseline,默認為Top,即文字頂部作為坐標(biāo)參考基線
下圖分別為設(shè)置Top、Middle、Bottom、Base的效果(y坐標(biāo)相同),其中Top、Bottom會連帶行高(如果設(shè)置)一起計算。
2.5 代碼截圖
2.6 元素支持的特性
具體ImageElement
、TextElement
、RectangleElement
對象支持的特性如下表:
元素類型 | 特性 | 相關(guān)方法 |
---|---|---|
ImageElement |
圖片 |
setImage() ,setImgUrl()
|
ImageElement |
位置 |
setX() ,setY()
|
ImageElement |
縮放 |
setWidth() ,setHeight() ,ZoomMode
|
ImageElement |
旋轉(zhuǎn) | setRotate() |
ImageElement |
圓角 | setRoundCorner() |
ImageElement |
居中繪制 | setCenter() |
ImageElement |
透明度 | setAlpha() |
ImageElement |
高斯模糊 | setBlur() |
ImageElement |
繪制方向 | setDirection() |
ImageElement |
平鋪繪制 | setRepeat() |
----------------- | ||
TextElement |
文本 | setText() |
TextElement |
位置 |
setX() ,setY()
|
TextElement |
居中繪制 | setCenter() |
TextElement |
旋轉(zhuǎn) | setRotate() |
TextElement |
透明度 | setAlpha() |
TextElement |
顏色 | setColor() |
TextElement |
字體 | setFont() |
TextElement |
字號 | setFont() |
TextElement |
行高 | setLineHeight() |
TextElement |
刪除線 | setStrikeThrough() |
TextElement |
自動換行 | setAutoBreakLine() |
TextElement |
繪制方向 | setDirection() |
TextElement |
字間距 | setSpace() |
TextElement |
平鋪繪制 | setRepeat() |
TextElement |
自適應(yīng)寬度 | setAutoFitWidth() |
TextElement |
坐標(biāo)參考基線 | setBaseLine() |
----------------- | ||
RectangleElement |
位置 |
setX() ,setY()
|
RectangleElement |
居中繪制 | setCenter() |
RectangleElement |
圓角 | setRoundCorner() |
RectangleElement |
透明度 | setAlpha() |
RectangleElement |
顏色 | setColor() |
RectangleElement |
顏色漸變 | setGradient() |
RectangleElement |
繪制方向 | setDirection() |
RectangleElement |
平鋪繪制 | setRepeat() |
RectangleElement |
邊框大小 | setBorderSize() |
2.7 后續(xù)計劃
作者日常需求中已經(jīng)夠用了,各位小伙伴如果有額外的需求可以考慮再進一步擴充,如增加旋轉(zhuǎn)、毛玻璃、藝術(shù)字等特效,歡迎加群交流
2.8 更新日志
v1.0.0
- 基本功能完善
v1.1.0
- 修復(fù)一些小bug
- 開放文本寬度、高度計算等方法,方便外部動態(tài)計算元素位置
- 文本和圖片元素支持旋轉(zhuǎn)
v1.1.1
- 背景和圖片元素支持高斯模糊(毛玻璃效果)
v1.1.2
- 修復(fù)一個ImageElement構(gòu)造函數(shù)bug
v1.1.3
- 修復(fù)背景圖為png時,合成后背景圖透明部分變黑的問題
- 整理了下測試方法
v1.1.4
- ImageCombiner合成器對象新增兩個構(gòu)造函數(shù),可以指定背景圖的新寬高
v1.1.5
- 修復(fù)設(shè)置背景圖寬高構(gòu)造函數(shù)的小bug
v1.2.0
- 修復(fù)當(dāng)前元素透明度設(shè)置會影響后續(xù)元素的問題
- 新增RectangleElement類型元素,用于繪制矩形/圓角矩形/圓形等簡單元素,作為其他元素的底襯(如示例圖片3中的商品區(qū)域白底,頭像白邊,二維碼白底等)
v1.3.0
- 優(yōu)化文本居中繪制邏輯
- 計算文本寬高的方法移至單獨的ElementUtil類中
- 新增無需指定背景圖的ImageCombiner構(gòu)造方法,方便從零開始繪制
v1.3.1
- 矩形元素增加顏色漸變屬性,可用于充當(dāng)文字元素的半透明漸變底襯
- ImageCombiner對象增加setCanvasRoundCorner()整圖圓角的方法
v2.0.0(破壞性更新)
- 文本元素y坐標(biāo)參照基線由base改為ascent(可理解為由左下角改為了左上角,保持與設(shè)計稿一致)
- 文本元素支持lineHeight行高設(shè)置(保持與設(shè)計稿一致)
- 如您在項目里已使用低版本,由于計算原理不同,升級后文本位置會出現(xiàn)偏差,需要手動調(diào)整,以適應(yīng)新版本計算方式,請慎重更新?。?!
v2.1.0
- 文本自動換行支持指定對齊方式(見LineAlign枚舉)
- 優(yōu)化了一些代碼,提升繪制效率
v2.2.0
- 終于!終于!終于!支持中央倉庫了?。?!
- 取消了ElementUtil類,將相關(guān)計算方法移到了元素對象里,如ElementUtil.computeTextWidth(element1)變?yōu)榱薳lement1.getWidth(),語義上更加自然。
- 優(yōu)化代碼,提升繪制效率
v2.2.1
- 將包名由freeway改為freewayso,與groupId一致
v2.2.2
- 內(nèi)部一個斷詞判斷的方法,最大長度限制由500改為2000(一般用戶可以忽略這個更新)
v2.3.2
- 新增繪制方向參數(shù),文本、圖片、矩形元素皆適用(setDirection方法),用于需要右對齊的場景
v2.3.3
- 新增設(shè)置字間距setSpace()方法
v2.3.4
- 修復(fù)一個背景圖縮放時,寬高賦值搞反的小bug?[I59LYK]
v2.3.5
- 支持素材平鋪繪制,可用作水印圖片、文字平鋪效果?[I58H39]
v2.3.6
- 修復(fù)中文標(biāo)點符號判斷邏輯(文本自動換行相關(guān))
v2.3.7
- 輸出格式增加支持jpeg
v2.3.8
- 優(yōu)化斷詞行寬判斷,方便文本豎排設(shè)置
- 文檔增加文字豎排demo
v2.3.9
- 文本元素增加字體樣式設(shè)置參數(shù),如加粗、斜體等
v2.4.0
- 矩形元素支持設(shè)置邊框大?。J畫實心矩形,設(shè)置后畫空心矩形)
v2.4.1
- 自動換行支持手動指定換行符setBreakLineSplitter(支持正則,會忽略自動寬度計算,方便多行文本繪制,v2.5.3已取消)
v2.5.0
- 文本元素新增自適應(yīng)最大寬度,超出指定寬度則自動縮小字號,以適應(yīng)之
v2.5.1
- 文本元素支持設(shè)置坐標(biāo)參考基線,默認LeftTop(文字左上角為xy坐標(biāo)點),也可以設(shè)置為左下角,一行文本里存在多種字號時,下對齊方便
v2.5.2
- 支持設(shè)置圖片保存質(zhì)量,如combiner.setQuality(0.8f)
v2.5.3(破壞性更新)
- 取消設(shè)置換行符方法,改為自動換行方法一個重載參數(shù)(以免使用上有誤解),即按指定換行符換行,而不是按最大寬度計算
- 自動換行(setAutoBreakLine)增加若干重載方法
v2.6.0
- 支持對文本元素設(shè)置Baseline(詳見2.4.3章節(jié)示例圖)
v2.6.3
- 文本元素支持加載項目內(nèi)字體文件,可以不用在服務(wù)器安裝字體文件(性能略低),原fontName參數(shù)改為fontNameOrPath,直接傳文件路徑即可(參數(shù)中帶"."則認為是字體文件,否則認為是字體名,詳見測試方法testLoadFont)
源碼地址:文章來源:http://www.zghlxwxcb.cn/news/detail-457891.html
image-combiner: ImageCombiner是一個專門用于Java服務(wù)端圖片合成的工具,沒有很復(fù)雜的功能,簡單實用,從實際業(yè)務(wù)場景出發(fā),提供簡單的接口,幾行代碼即可實現(xiàn)圖片拼合(當(dāng)然用于合成水印也可以),素材上支持圖片、文本、矩形三種,支持定位、縮放、旋轉(zhuǎn)、圓角、透明度、顏色、字體、字號、刪除線、居中繪制、文本自動換行等特性,足夠覆蓋圖片合成的日常需求。文章來源地址http://www.zghlxwxcb.cn/news/detail-457891.html
到了這里,關(guān)于ImageCombiner是一個專門用于Java服務(wù)端圖片合成的工具的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!