標籤:
在app的後台中,有時候為了標示著作權,需要給圖片加上浮水印。
在liunx中,IM4JAVA+GraphicsMagick是個高效處理圖片的方案,圖片的裁剪是使用了這個技術方案,為了減少不必要的開發成本和營運成本,對應浮水印,我們是打算繼續採用這個方案。
但在開發的過程中,發現這個方案對中文浮水印支援得不好。
根據網上的搜尋結果,就算採用了im4java的GMOperation,並將浮水印的字串轉成GBK的編碼,添加中文浮水印時,對於奇數個數的中文,沒問題;但對於偶數個數的中文,就出現亂碼了。
試了多次後,發現這個問題沒法解決,於是試了JMagick+ ImageMagick。
但是,在網上找到一份資料,http://javantsky.iteye.com/blog/747807, 裡面提到JMagick+ImageMagick作為應用服務的缺點,並建議可以使用IM4JAVA:
The "JNI hazard" here is that if something you use (f.ex libtiff for reading TIFF files) has a memory bug then it can make your whole JVM crash. Thats of course frustrating and therefore its great to have im4java around, which starts IM as an external process, so JVM crashes are avoided. * * Coolest thing would be if JMagick and im4java could have the same API so it was easy to switch depending on luckyness. Ive asked the author of im4java to attemt to be as compatible as possible, but as im4java is very much different internally its limited how much can be done in that direction. If you don't like the risk, stick to im4java. If your want optimal performance give JMagick a try. And, its not JMagick that is buggy, its what it depends on (hereunder IM) that is not always (memory) bug free on long running processes. I also have never seen a mismatch between JMagick binary and ImageMagick binaries leading to crashes.
最後,採用了以下的方案視線圖片的中文浮水印:
1.先把中文產生背景透明的png圖片
2.把這些png圖片作為圖片浮水印貼在圖上
代碼如下:
package test;import java.awt.BasicStroke;import java.awt.Color;import java.awt.Font;import java.awt.Graphics;import java.awt.Graphics2D;import java.awt.Image;import java.awt.Transparency;import java.awt.font.FontRenderContext;import java.awt.geom.AffineTransform;import java.awt.geom.Rectangle2D;import java.awt.image.BufferedImage;import java.io.File;import java.io.IOException;import javax.imageio.ImageIO;import org.im4java.core.CompositeCmd;import org.im4java.core.ConvertCmd;import org.im4java.core.GMOperation;import org.im4java.core.GraphicsMagickCmd;import org.im4java.core.IM4JavaException;import org.im4java.core.IMOperation;public class Watermark {/** * 按九宮格位置添加浮水印 * @param srcPath原圖片路徑 * @param distPath新圖片路徑 * @param watermarkImg浮水印圖片路徑 * @param position九宮格位置[1-9],從上往下,從左至右排序 * @param x 橫向邊距 * @param y 縱向邊距 * @param alpha透明度 * @throws IOException * @throws InterruptedException * @throws IM4JavaException */public void WatermarkImg(String srcPath,String distPath,String watermarkImg, int position, int x, int y, int alpha) throws IOException, InterruptedException, IM4JavaException{int[] watermarkImgSide = getImageSide(watermarkImg);int[] srcImgSide = getImageSide(srcPath);int[] xy = getXY(srcImgSide, watermarkImgSide, position, y, x);watermarkImg(srcPath,distPath,watermarkImg,watermarkImgSide[0],watermarkImgSide[1],xy[0],xy[1],alpha);}private int[] getImageSide(String imgPath) throws IOException { int[] side = new int[2]; Image img = ImageIO.read(new File(imgPath)); side[0] = img.getWidth(null); side[1] =img.getHeight(null); return side;}/** * 添加圖片浮水印 * @param srcPath原圖片路徑 * @param distPath新圖片路徑 * @param watermarkImg浮水印圖片路徑 * @param width浮水印寬度(可以於浮水印圖片大小不同) * @param height浮水印高度(可以於浮水印圖片大小不同) * @param x浮水印開始X座標 * @param y浮水印開始Y座標 * @param alpha透明度[0-100] * @throws IOException * @throws InterruptedException * @throws IM4JavaException */private synchronized void watermarkImg(String srcPath,String distPath,String watermarkImg, int width, int height, int x, int y, int alpha) throws IOException, InterruptedException, IM4JavaException{ CompositeCmd cmd = new CompositeCmd(true); String path = "C://Program Files//GraphicsMagick-1.3.19-Q8";cmd.setSearchPath(path);IMOperation op = new IMOperation();op.dissolve(alpha);op.geometry(width, height, x, y);op.addImage(watermarkImg); op.addImage(srcPath); op.addImage(distPath); cmd.run(op);}private int[] getXY(int[] image, int[] watermark, int position, int x, int y) { int[] xy = new int[2];if(position==1){xy[0] = x;xy[1] = y;}else if(position==2){xy[0] = (image[0]-watermark[0])/2;//橫向邊距xy[1] = y; //縱向邊距}else if(position==3){xy[0] = image[0]-watermark[0]-x;xy[1] = y;}else if(position==4){xy[0] = x;xy[1] = (image[1]-watermark[1])/2;}else if(position==5){xy[0] = (image[0]-watermark[0])/2;xy[1] = (image[1]-watermark[1])/2;}else if(position==6){xy[0] = image[0]-watermark[0]-x;xy[1] = (image[1] - watermark[1])/2; }else if(position==7){xy[0] = x;xy[1] = image[1] - watermark[1] - y;}else if(position==8){xy[0] = (image[0]-watermark[0])/2;xy[1] = image[1] - watermark[1] - y;}else{xy[0] = image[0]-watermark[0]-x;xy[1] = image[1] - watermark[1] - y;}return xy;}/** * 把文字轉化為一張背景透明的png圖片 * @param str 文字的內容 * @param fontType 字型,例如宋體 * @param fontSize 字型大小 * @param colorStr 字型顏色,不帶#號,例如"990033" * @param outfile png圖片的路徑 * @throws Exception */public void converFontToImage(String str,String fontType,int fontSize,String colorStr, String outfile) throws Exception{Font font=new Font(fontType,Font.BOLD,fontSize);File file=new File(outfile);//擷取font的樣式應用在str上的整個矩形 Rectangle2D r=font.getStringBounds(str, new FontRenderContext(AffineTransform.getScaleInstance(1, 1),false,false)); int unitHeight=(int)Math.floor(r.getHeight());//擷取單個字元的高度//擷取整個str用了font樣式的寬度這裡用四捨五入後+1保證寬度絕對能容納這個字串作為圖片的寬度 int width=(int)Math.round(r.getWidth())+1; int height=unitHeight+3;//把單個字元的高度+3保證高度絕對能容納字串作為圖片的高度//建立圖片 BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D g2d = image.createGraphics(); image = g2d.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT); g2d.dispose(); g2d = image.createGraphics(); g2d.setColor(Color.WHITE); g2d.setStroke(new BasicStroke(1)); g2d.setColor(new Color(Integer.parseInt(colorStr, 16)));//在換成所需要的字型顏色 g2d.setFont(font); g2d.drawString(str, 0,font.getSize()); ImageIO.write(image, "png", file);//輸出png圖片}}
測試執行個體:
package test;import java.awt.BasicStroke;import java.awt.Color;import java.awt.Font;import java.awt.Graphics;import java.awt.Graphics2D;import java.awt.Transparency;import java.awt.font.FontRenderContext;import java.awt.geom.AffineTransform;import java.awt.geom.Rectangle2D;import java.awt.image.BufferedImage;import java.io.File;import javax.imageio.ImageIO;import org.im4java.core.CompositeCmd;import org.im4java.core.ConvertCmd;import org.im4java.core.GMOperation;import org.im4java.core.IMOperation;public class test {public static void main(String[] args) { try {String src="D://src.jpg"; //需要加浮水印的源圖片String desc="D://desc.jpg"; //產生的浮水印圖片的路徑String water="D://water.png"; //用中文轉換成的背景透明的png圖片String fontType="C:\\Windows\\Fonts\\simsun.ttc"; //指定字型檔為宋體String colorStr="990033"; //顏色int fontSize=18;Watermark watermark=new Watermark();/* * 把文字轉化為一張背景透明的png圖片 * @param str 文字的內容 * @param fontType 字型,例如宋體 * @param fontSize 字型大小 * @param colorStr 字型顏色,不帶#號,例如"990033" * @param outfile png圖片的路徑 * @throws Exception */watermark.converFontToImage("中華人們", fontType, fontSize, colorStr, water);/* * 把文字的png圖片貼在原圖上,產生浮水印 * @param srcPath原圖片路徑 * @param distPath新圖片路徑 * @param watermarkImg浮水印圖片路徑 * @param position九宮格位置[1-9],從上往下,從左至右排序 * @param x 橫向邊距 * @param y 縱向邊距 * @param alpha透明度 */watermark.WatermarkImg(src, desc, water, 1, 20,20, 100);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();} }}
源碼已放在github:https://github.com/newjueqi/ChineseWaterMark#
app後端系列文章總目錄
建立了“app後端技術” 交流qq群:254659220
[文章作者]曾健生
[作者郵箱][email protected]
[作者QQ]190678908
[新浪微博] @newjueqi
[部落格]http://blog.csdn.net/newjueqi
app後端設計(13)--IM4JAVA+GraphicsMagick實現中文浮水印