001/* 002 * Copyright (c) 2009 The openGion Project. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 013 * either express or implied. See the License for the specific language 014 * governing permissions and limitations under the License. 015 */ 016package org.opengion.fukurou.util; 017 018import org.opengion.fukurou.system.OgRuntimeException ; // 6.4.2.0 (2016/01/29) 019import java.awt.color.ColorSpace; 020import java.awt.color.ICC_ColorSpace; 021import java.awt.color.ICC_Profile; 022import java.awt.Color; // 6.0.2.3 (2014/10/10) mixImage(画像合成) 関係 023import java.awt.Font; // 6.0.2.3 (2014/10/10) mixImage(画像合成) 関係 024import java.awt.Graphics2D; // 6.0.2.3 (2014/10/10) mixImage(画像合成) 関係 025import java.awt.FontMetrics; // 6.0.2.3 (2014/10/10) mixImage(画像合成) 関係 026import java.awt.image.BufferedImage; 027import java.awt.image.ColorConvertOp; 028import java.io.File; 029import java.io.IOException; 030import java.io.InputStream; 031import java.io.ByteArrayOutputStream; 032import java.util.Locale; 033import java.util.Arrays; 034import javax.media.jai.JAI; 035 036import javax.imageio.ImageIO; 037import javax.imageio.IIOException; 038 039import com.sun.media.jai.codec.FileSeekableStream; 040import com.sun.media.jai.util.SimpleCMYKColorSpace; 041 042import org.opengion.fukurou.system.Closer; // 6.4.2.0 (2016/01/29) package変更 fukurou.util → fukurou.system 043import static org.opengion.fukurou.system.HybsConst.CR; // 6.1.0.0 (2014/12/26) refactoring 044 045/** 046 * ImageUtil は、画像ファイル関連の処理を集めたユーティリティクラスです。 047 * 048 * ここでは、イメージファイルを BufferedImage にして取り扱います。 049 * また、ImageResizer で処理していた static メソッドや、関連処理、 050 * org.opengion.hayabusa.servlet.MakeImage の主要な処理もこちらに持ってきます。 051 * 052 * @version 6.0.2.3 (2014/10/10) 053 * @author Hiroki Nakamura 054 * @since JDK6.0, 055 */ 056public final class ImageUtil { 057 058 private static final String ICC_PROFILE = "ISOcoated_v2_eci.icc"; // 5.5.3.4 (2012/06/19) 059 060 // 6.0.2.3 (2014/10/10) テキスト合成で指定できる設定値 061 /** X軸に対して、テキストを画像の左寄せで表示します。 **/ 062 public static final int LEFT = -1 ; 063 /** X軸に対して、テキストを画像の中央揃えで表示します。 **/ 064 public static final int CENTER = -2 ; 065 /** X軸に対して、テキストを画像の右寄せで表示します。 **/ 066 public static final int RIGHT = -3 ; 067 068 /** Y軸に対して、テキストを画像の上揃えで表示します。 **/ 069 public static final int TOP = -4 ; 070 /** Y軸に対して、テキストを画像の中央揃えで表示します。 **/ 071 public static final int MIDDLE = -5 ; 072 /** Y軸に対して、テキストを画像の下揃えで表示します。 **/ 073 public static final int BOTTOM = -6 ; 074 075 public static final String READER_SUFFIXES ; // 5.6.5.3 (2013/06/28) 入力画像の形式 [bmp, gif, jpeg, jpg, png, wbmp] 076 public static final String WRITER_SUFFIXES ; // 5.6.5.3 (2013/06/28) 出力画像の形式 [bmp, gif, jpeg, jpg, png, wbmp] 077 // 5.6.5.3 (2013/06/28) 入力画像,出力画像の形式 を ImageIO から取り出します。 078 static { 079 final String[] rfn = ImageIO.getReaderFileSuffixes(); 080 Arrays.sort( rfn ); 081 READER_SUFFIXES = Arrays.toString( rfn ); 082 083 final String[] wfn = ImageIO.getWriterFileSuffixes(); 084 Arrays.sort( wfn ); 085 WRITER_SUFFIXES = Arrays.toString( wfn ); 086 } 087 088 /** 089 * すべてが staticメソッドなので、コンストラクタを呼び出さなくしておきます。 090 * 091 */ 092 private ImageUtil() {} 093 094 /** 095 * 入力ファイル名を指定し、画像オブジェクトを作成します。 096 * 097 * @og.rev 5.4.3.5 (2012/01/17) CMYK対応 098 * @og.rev 5.4.3.7 (2012/01/20) FAIでのファイル取得方法変更 099 * @og.rev 5.4.3.8 (2012/01/24) エラーメッセージ追加 100 * @og.rev 5.6.5.3 (2013/06/28) 入力画像の形式 を ImageIO から取り出します。 101 * @og.rev 6.0.2.3 (2014/10/10) ImageResizer から、移植しました。 102 * 103 * @param fin 入力ファイル名 104 * @return 読み込まれた画像オブジェクト(BufferedImage) 105 */ 106 public static BufferedImage readFile( final String fin ) { 107 // 5.6.5.3 (2013/06/28) 入力画像の形式 を ImageIO から取り出します。 108 if( !ImageUtil.isReaderSuffix( fin ) ) { 109 final String errMsg = "入力ファイルは" + READER_SUFFIXES + "のいずれかの形式のみ指定可能です。" 110 + "File=[" + fin + "]"; 111 throw new OgRuntimeException( errMsg ); 112 } 113 114 final File inFile = new File( fin ); 115 BufferedImage bi = null; 116 try { 117 bi = ImageIO.read( inFile ); 118 } 119 catch( final IIOException ex ) { // 5.4.3.5 (2012/01/17) 決めうち 120 // API的には、IllegalArgumentException と IOException しか記述されていない。 121 // 何もせずに、下の処理に任せます。 122 // 6.0.2.5 (2014/10/31) refactoring:Avoid empty catch blocks 警告対応 123 final String errMsg = "cmykToSRGB 処理が必要です。" + ex.getMessage(); 124 System.err.println( errMsg ); 125 } 126 catch( final IOException ex ) { 127 final String errMsg = "イメージファイルの読込に失敗しました。" + "File=[" + fin + "]"; 128 throw new OgRuntimeException( errMsg,ex ); 129 } 130 131 // 6.0.0.1 (2014/04/25) IIOException の catch ブロックからの例外出力を外に出します。 132 // bi == null は、結果のストリームを読み込みできないような場合、または、IO例外が発生した場合。 133 if( bi == null ) { 134 FileSeekableStream fsstream = null; 135 try{ 136 // 5.4.3.7 (2012/01/20) ファイルの開放がGC依存なので、streamで取得するように変更 137 // bi = cmykToSRGB(JAI.create("FileLoad",inFile.toString()).getAsBufferedImage(null,null)); 138 fsstream = new FileSeekableStream(inFile.getAbsolutePath()); 139 bi = cmykToSRGB(JAI.create("stream",fsstream).getAsBufferedImage(null,null)); 140 } 141 catch( final IOException ex ){ 142 final String errMsg = "イメージファイルの読込(JAI)に失敗しました。" + "File=[" + fin + "]"; 143 throw new OgRuntimeException( errMsg,ex ); 144 } 145 catch( final RuntimeException ex ) { // 5.4.3.8 (2012/01/23) その他エラーの場合追加 146 final String errMsg = "イメージファイルの読込(JAI)に失敗しました。ファイルが壊れている可能性があります。" + "File=[" + fin + "]"; 147 throw new OgRuntimeException( errMsg,ex ); 148 } 149 finally{ 150 Closer.ioClose(fsstream); 151 } 152 } 153 154 return bi; 155 } 156 157 /** 158 * 画像オブジェクト と、出力ファイル名を指定し、ファイルに書き込みます。 159 * 160 * ImageIO に指定する formatName(ファイル形式)は、出力ファイル名の拡張子から取得します。 161 * [bmp, gif, jpeg, jpg, png, wbmp] 位がサポートされています。 162 * 163 * @og.rev 6.0.2.3 (2014/10/10) 新規作成 164 * 165 * @param image 出力する画像オブジェクト(BufferedImage) 166 * @param fout 出力ファイル名 167 */ 168 public static void saveFile( final BufferedImage image , final String fout ) { 169 final File outFile = new File( fout ); 170 try { 171 final String outSuffix = ImageUtil.getSuffix( fout ); 172 ImageIO.write( image, outSuffix, outFile ); 173 } 174 catch( final IOException ex ) { 175 final String errMsg = "イメージファイルの書き込みに失敗しました。" + "File=[" + fout + "]"; 176 throw new OgRuntimeException( errMsg,ex ); 177 } 178 } 179 180 /** 181 * 入力ファイル名を指定し、画像ファイルの byte配列を作成します。 182 * 183 * ImageIO に指定する formatName(ファイル形式)は、出力ファイル名の拡張子から取得します。 184 * [bmp, gif, jpeg, jpg, png, wbmp] 位がサポートされています。 185 * 186 * @og.rev 6.0.2.3 (2014/10/10) 新規作成 187 * 188 * @param fin 入力ファイル名 189 * @return 読み込まれた画像ファイルの byte配列 190 * @og.rtnNotNull 191 */ 192 public static byte[] byteImage( final String fin ) { 193 final ByteArrayOutputStream baOut = new ByteArrayOutputStream(); 194 195 final BufferedImage img = ImageUtil.readFile( fin ); 196 try { 197 final String suffix = ImageUtil.getSuffix( fin ); 198 ImageIO.write( img, suffix, baOut ); 199 } 200 catch( final IOException ex ) { 201 final String errMsg = "イメージファイルの読み込みに失敗しました。" + "File=[" + fin + "]"; 202 throw new OgRuntimeException( errMsg,ex ); 203 } 204 finally { 205 Closer.ioClose( baOut ); // ByteArrayOutputStreamを閉じても、何の影響もありません。 206 } 207 208 return baOut.toByteArray(); 209 } 210 211 /** 212 * ファイル名から拡張子(小文字)を求めます。 213 * 拡張子 が存在しない場合は、null を返します。 214 * 215 * @og.rev 5.6.5.3 (2013/06/28) private ⇒ public へ変更 216 * @og.rev 6.0.2.3 (2014/10/10) ImageResizer から、移植しました。 217 * 218 * @param fileName ファイル名 219 * 220 * @return 拡張子(小文字)。なければ、null 221 */ 222 public static String getSuffix( final String fileName ) { 223 String suffix = null; 224 if( fileName != null ) { 225 final int sufIdx = fileName.lastIndexOf( '.' ); 226 if( sufIdx >= 0 ) { 227 suffix = fileName.substring( sufIdx + 1 ).toLowerCase( Locale.JAPAN ); 228 } 229 } 230 return suffix; 231 } 232 233 /** 234 * ファイル名から入力画像になりうるかどうかを判定します。 235 * コンストラクターの引数(入力画像)や、実際の処理の中(出力画像)で 236 * 、変換対象となるかどうかをチェックしていますが、それを事前に確認できるようにします。 237 * 238 * @og.rev 5.6.5.3 (2013/06/28) 新規追加 239 * @og.rev 5.6.6.1 (2013/07/12) getSuffix が null を返すケースへの対応 240 * @og.rev 6.0.2.3 (2014/10/10) ImageResizer から、移植しました。 241 * 242 * @param fileName ファイル名 243 * 244 * @return 入力画像として使用できるかどうか。できる場合は、true 245 */ 246 public static boolean isReaderSuffix( final String fileName ) { 247 final String suffix = getSuffix( fileName ); 248 249 return suffix != null && READER_SUFFIXES.indexOf( suffix ) >= 0 ; 250 } 251 252 /** 253 * ファイル名から出力画像になりうるかどうかを判定します。 254 * コンストラクターの引数(入力画像)や、実際の処理の中(出力画像)で 255 * 、変換対象となるかどうかをチェックしていますが、それを事前に確認できるようにします。 256 * 257 * @og.rev 5.6.5.3 (2013/06/28) 新規追加 258 * @og.rev 5.6.6.1 (2013/07/12) getSuffix が null を返すケースへの対応 259 * @og.rev 6.0.2.3 (2014/10/10) ImageResizer から、移植しました。 260 * 261 * @param fileName ファイル名 262 * 263 * @return 出力画像として使用できるかどうか。できる場合は、true 264 */ 265 public static boolean isWriterSuffix( final String fileName ) { 266 final String suffix = getSuffix( fileName ); 267 268 return suffix != null && WRITER_SUFFIXES.indexOf( suffix ) >= 0 ; 269 } 270 271 /** 272 * ファイル名から入力画像になりうるかどうかを判定します。 273 * コンストラクターの引数(入力画像)や、実際の処理の中(出力画像)で、 274 * 変換対象となるかどうかをチェックしていますが、それを事前に確認できるようにします。 275 * 276 * @og.rev 6.0.2.3 (2014/10/10) 新規追加 277 * 278 * @param img 変換対象のBufferedImage 279 * @param fCol 変換対象の色 280 * @param tCol 変換後の色 281 */ 282 public static void changeColor( final BufferedImage img , final Color fCol , final Color tCol ) { 283 final int wd = img.getWidth(); 284 final int ht = img.getHeight(); 285 final int fc = fCol.getRGB(); // 変換元のRGB値。 286 final int tc = tCol.getRGB(); // 変換後のRGB値。例:new Color( 255,255,255,0 ) なら、透明 287 288 for( int y=0; y<ht; y++ ) { 289 for( int x=0; x<wd; x++ ) { 290 if( img.getRGB( x,y ) == fc ) { 291 img.setRGB( x,y,tc ); 292 } 293 } 294 } 295 } 296 297 /** 298 * BufferedImageをISOCoatedのICCプロファイルで読み込み、RGBにした結果を返します。 299 * (CMYKからRBGへの変換、ビット反転) 300 * なお、ここでは、外部の ICC_PROFILE(ISOcoated_v2_eci.icc) を利用して、処理速度アップを図りますが、 301 * 存在しない場合、標準の、com.sun.media.jai.util.SimpleCMYKColorSpace を利用しますので、エラーは出ません。 302 * ただし、ものすごく遅いため、実用的ではありません。 303 * ISOcoated_v2_eci.icc ファイルは、zip圧縮して、拡張子をjar に変更後、(ISOcoated_v2_eci.jar) 304 * javaエクステンション((JAVA_HOME\)jre\lib\ext) にコピーするか、実行時に、CLASSPATHに設定します。 305 * 306 * @og.rev 5.4.3.5 (2012/01/17) 307 * @og.rev 5.5.3.4 (2012/06/19) ICC_PROFILE の取得先を、ISOcoated_v2_eci.icc に変更 308 * @og.rev 6.0.2.3 (2014/10/10) ImageResizer から、移植しました。(static にして) 309 * 310 * @param readImage BufferedImageオブジェクト 311 * 312 * @return 変換後のBufferedImage 313 * @throws IOException 入出力エラーが発生したとき 314 */ 315 public static BufferedImage cmykToSRGB( final BufferedImage readImage ) throws IOException { 316 final ClassLoader loader = Thread.currentThread().getContextClassLoader(); 317 final InputStream icc_stream = loader.getResourceAsStream( ICC_PROFILE ); 318 319 // 5.5.3.4 (2012/06/19) ICC_PROFILE が存在しない場合は、標準のSimpleCMYKColorSpace を使用。 320 ColorSpace cmykCS = null; 321 // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..; 322 if( icc_stream == null ) { 323 // 遅いので標準のスペースは使えない 324 final String errMsg = ICC_PROFILE + " が見つかりません。" + CR 325 + " CLASSPATHの設定されている場所に配備してください。" + CR 326 + " 標準のSimpleCMYKColorSpaceを使用しますのでエラーにはなりませんが、非常に遅いです。" ; 327 System.out.println( errMsg ); 328 cmykCS = SimpleCMYKColorSpace.getInstance(); 329 } 330 else { 331 final ICC_Profile prof = ICC_Profile.getInstance(icc_stream); //変換プロファイル 332 cmykCS = new ICC_ColorSpace(prof); 333 } 334 335 final BufferedImage rgbImage = new BufferedImage(readImage.getWidth(),readImage.getHeight(), BufferedImage.TYPE_INT_RGB); 336 final ColorSpace rgbCS = rgbImage.getColorModel().getColorSpace(); 337 final ColorConvertOp cmykToRgb = new ColorConvertOp(cmykCS, rgbCS, null); 338 cmykToRgb.filter(readImage, rgbImage); 339 340 final int width = rgbImage.getWidth(); 341 final int height = rgbImage.getHeight(); 342 // 反転が必要 343 for( int i=0;i<width;i++ ) { 344 for( int j=0;j<height;j++ ) { 345 int rgb = rgbImage.getRGB(i, j); 346 final int rr = (rgb & 0xff0000) >> 16; 347 final int gg = (rgb & 0x00ff00) >> 8; 348 final int bb = rgb & 0x0000ff ; 349 rgb = (Math.abs(rr - 255) << 16) + (Math.abs(gg - 255) << 8) + (Math.abs(bb - 255)); 350 rgbImage.setRGB(i, j, rgb); 351 } 352 } 353 354 return rgbImage; 355 } 356 357 /** 358 * 画像イメージに、文字列を動的に合成作成して返します。 359 * 360 * 描画指定の位置(x,y)は、テキストの左下の位置を、画像イメージの、左上を起点(0,0)とした 361 * 位置になります。 362 * maxW , maxH を指定すると、テキストのフォントサイズをその範囲に収まるように自動調整します。 363 * 364 * @og.rev 6.0.2.3 (2014/10/10) 新規追加 365 * 366 * @param image 合成する元の画像オブジェクト 367 * @param text 描画される文字列 368 * @param xAxis テキストが描画される位置のx座標。または、{@link #LEFT LEFT},{@link #CENTER CENTER},{@link #RIGHT RIGHT} 指定で、自動計算する。 369 * @param yAxis テキストが描画される位置のy座標。または、{@link #TOP TOP},{@link #MIDDLE MIDDLE},{@link #BOTTOM BOTTOM} 指定で、自動計算する。 370 * @param maxW テキストの最大幅(imageの幅と比較して小さい方の値。0以下の場合は、imageの幅) 371 * @param maxH テキストの最大高さ(imageの高さと比較して小さい方の値。0以下の場合は、imageの高さ) 372 * @param font 描画されるテキストのフォント。null の場合は、初期値(Dialog.plain,12px)が使われる 373 * @param color 描画されるテキストの色(Color)。null の場合は、Color.BLACK が使われる 374 * 375 * @return 合成された画像オブジェクト(BufferedImage) 376 * @og.rtnNotNull 377 * @see #mixImage( BufferedImage, String, int, int, Font, Color ) 378 */ 379 public static BufferedImage mixImage( final BufferedImage image, 380 final String text, final int xAxis, final int yAxis, final int maxW, final int maxH, 381 final Font font, final Color color ) { 382 383 final int imgWidth = image.getWidth(); // 画像の幅 384 final int imgHeight = image.getHeight(); // 画像の高さ 385 386 final int maxWidth = maxW <= 0 ? imgWidth : Math.min( maxW,imgWidth ); 387 final int maxHeight = maxH <= 0 ? imgHeight : Math.min( maxH,imgHeight ); 388 389 final Graphics2D gph = image.createGraphics(); 390 if( font != null ) { gph.setFont( font ); } // new Font("Serif", Font.BOLD, 14) 391 392 float size = 5.0f; // 小さすぎると見えないので、開始はこれくらいから行う。 393 final float step = 0.5f; // 刻み幅 394 while( true ) { 395 final Font tmpFont = gph.getFont().deriveFont( size ); 396 gph.setFont( tmpFont ); 397 398 final FontMetrics fm = gph.getFontMetrics(); 399 final int txtWidth = fm.stringWidth( text ); 400 final int txtHeight = fm.getAscent(); 401 402 if( maxWidth < txtWidth || maxHeight < txtHeight ) { 403 size -= step; // 一つ戻しておく。場合によっては、step分戻して、stepを小さくして続ける方法もある。 404 break; 405 } 406 size += step; 407 } 408 final Font newFont = gph.getFont().deriveFont( size ); 409 410 return mixImage( image, text, xAxis, yAxis, newFont, color ); 411 } 412 413 /** 414 * 画像イメージに、文字列を動的に合成作成して返します。 415 * 416 * 描画指定の位置(x,y)は、テキストの左下の位置を、画像イメージの、左上を起点(0,0)とした 417 * 位置になります。 418 * 419 * @og.rev 6.0.2.3 (2014/10/10) org.opengion.hayabusa.servlet.MakeImage から、移植しました。 420 * 421 * @param image 合成する元の画像オブジェクト 422 * @param text 描画される文字列 423 * @param xAxis テキストが描画される位置のx座標。または、{@link #LEFT LEFT},{@link #CENTER CENTER},{@link #RIGHT RIGHT} 指定で、自動計算する。 424 * @param yAxis テキストが描画される位置のy座標。または、{@link #TOP TOP},{@link #MIDDLE MIDDLE},{@link #BOTTOM BOTTOM} 指定で、自動計算する。 425 * @param font 描画されるテキストのフォント。null の場合は、初期値(Dialog.plain,12px)が使われる 426 * @param color 描画されるテキストの色(Color)。null の場合は、Color.BLACK が使われる 427 * 428 * @return 合成された画像オブジェクト(BufferedImage) 429 * @og.rtnNotNull 430 * @see #mixImage( BufferedImage, String, int, int, int, int, Font, Color ) 431 */ 432 public static BufferedImage mixImage( final BufferedImage image, 433 final String text, final int xAxis, final int yAxis, 434 final Font font, final Color color ) { 435 436 final Graphics2D gph = image.createGraphics(); 437 438 // gph.setRenderingHint( java.awt.RenderingHints.KEY_TEXT_ANTIALIASING,java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON ); 439 440 if( font != null ) { gph.setFont( font ); } // new Font("Serif", Font.BOLD, 14) 441 // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..; 442 if( color == null ) { gph.setColor( Color.BLACK ); } // new Color(0,0,255) など 443 else { gph.setColor( color ); } 444 445 // 実際の位置ではなく、X軸が、LEFT,CENTER,RIGHT 等の指定 446 int x1 = xAxis ; 447 if( x1 < 0 ) { 448 final int imgWidth = image.getWidth(); // 画像の幅 449 final FontMetrics fm = gph.getFontMetrics(); 450 final int txtWidth = fm.stringWidth( text ); // テキストの長さ 451 452 switch( x1 ) { 453 case LEFT : x1 = 0; // 左寄せなので、0 454 break; 455 case CENTER : x1 = imgWidth/2 - txtWidth/2; // 画像の中心から、テキストの中心を引き算 456 break; 457 case RIGHT : x1 = imgWidth - txtWidth; // 右寄せは、画像の右端からテキスト分を引き算 458 break; 459 default : 460 final String errMsg = "X軸 で範囲外のデータが指定されました。" + "text=[" + text + "]" 461 + " (x,y)=[" + xAxis + "," + yAxis + "]" ; 462 throw new OgRuntimeException( errMsg ); 463 // break; 制御は移りません。 464 } 465 } 466 467 // 実際の位置ではなく、Y軸が、TOP,MIDDLE,BOTTOM 等の指定 468 final int Ydef = 2 ; // 良く判らないが、位置合わせに必要。 469 int y1 = yAxis ; 470 if( y1 < 0 ) { 471 final int imgHeight = image.getHeight() -Ydef; // 画像の高さ 472 final FontMetrics fm = gph.getFontMetrics(); 473 final int txtHeight = fm.getAscent() -Ydef; // テキストの幅(=Ascent) 474 475 switch( y1 ) { 476 case TOP : y1 = txtHeight; // 上寄せは、テキストの幅分だけ下げる 477 break; 478 case MIDDLE : y1 = (imgHeight)/2 + (txtHeight)/2 ; // 画像の中心から、テキストの中心分下げる(加算) 479 break; 480 case BOTTOM : y1 = imgHeight; // 下寄せは、画像の高さ分-2 481 break; 482 default : 483 final String errMsg = "Y軸 で範囲外のデータが指定されました。" + "text=[" + text + "]" 484 + " (x,y)=[" + xAxis + "," + yAxis + "]" ; 485 throw new OgRuntimeException( errMsg ); 486 // break; 制御は移りません。 487 } 488 } 489 490 gph.drawString( text, x1, y1 ); 491 gph.dispose(); // グラフィックス・コンテキストを破棄 492 493 return image; 494 } 495 496 /** 497 * アプリケーションのサンプルです。 498 * 499 * 入力イメージファイルを読み取って、テキストを合成して、出力イメージファイル に書き込みます。 500 * テキストの挿入位置を、X軸、Y軸で指定します。 501 * X軸とY軸には、特別な記号があり、左寄せ、右寄せ等の指示が可能です。 502 * 503 * サンプルでは、new Font("Serif", Font.PLAIN, 14); と、new Color(0,0,255);(青色)を固定で渡しています。 504 * 505 * Usage: java org.opengion.fukurou.util.ImageUtil 入力ファイル 出力ファイル 506 * -mix テキスト X軸 Y軸 [-fname=フォント名 -fstyle=スタイル -fsize=サイズ -color=カラー] 507 * X軸 指定(正の値は実際の位置) 508 * -1 ・・・ LEFT 左寄せ 509 * -2 ・・・ CENTER 中央揃え 510 * -3 ・・・ RIGHT 右寄せ 511 * 512 * Y軸 指定(正の値は実際の位置) 513 * -4 ・・・ TOP 上揃え 514 * -5 ・・・ MIDDLE 中央揃え 515 * -6 ・・・ BOTTOM 下揃え 516 * 517 * -fname=フォント名(初期値:Serif) 518 * Serif , SansSerif , Monospaced , Dialog , DialogInput 519 * 520 * -fstyle=スタイル(初期値:0:PLAIN)を、数字で選びます。 521 * 0:PLAIN , 1:BOLD , 2:ITALIC 522 * 523 * -fsize=サイズ(初期値:14) 524 * フォントサイズを整数で指定します。 525 * 526 * -color=カラー 527 * 色を表す文字列(BLUE,GREEN か、#808000 などの16bitRGB表記) 528 * 529 * Usage: java org.opengion.fukurou.util.ImageUtil 入力ファイル 出力ファイル 530 * -trans [-color=カラー -alpha=透過率(0-100%)] 531 * -color=カラー(初期値:WHITE) 532 * 透明色にする色を指定(BLUE,GREEN か、#808000 などの16bitRGB表記) 533 * 534 * -alpha=透過率(0-100%)(初期値:0) 535 * 透過率は、0:透明から100不透明まで指定します。 536 * 537 * @og.rev 6.4.5.1 (2016/04/28) mainメソッドの起動方法を変更します。 538 * 539 * @param args 引数文字列配列 入力ファイル、出力ファイル、縦横最大サイズ 540 */ 541 public static void main( final String[] args ) { 542 if( args.length < 3 ) { 543 final String usage = "Usage: java org.opengion.fukurou.util.ImageUtil 入力ファイル 出力ファイル\n" + 544 " -mix テキスト X軸 Y軸 [-fname=フォント名 -fstyle=スタイル -fsize=サイズ -color=カラー]\n" + 545 "\tX軸とY軸には、特別な記号があり、左寄せ、右寄せ等の指示が可能です。\n" + 546 "\t X軸 指定(正の値は実際の位置)\n" + 547 "\t -1 ・・・ LEFT 左寄せ\n" + 548 "\t -2 ・・・ CENTER 中央揃え\n" + 549 "\t -3 ・・・ RIGHT 右寄せ\n" + 550 "\t\n" + 551 "\t Y軸 指定(正の値は実際の位置)\n" + 552 "\t -4 ・・・ TOP 上揃え\n" + 553 "\t -5 ・・・ MIDDLE 中央揃え\n" + 554 "\t -6 ・・・ BOTTOM 下揃え\n" + 555 "\t\n" + 556 "\t -fname=フォント名(初期値:Serif)\n" + 557 "\t Serif , SansSerif , Monospaced , Dialog , DialogInput\n" + 558 "\t\n" + 559 "\t -fstyle=スタイル(初期値:0:PLAIN)\n" + 560 "\t 0:PLAIN , 1:BOLD , 2:ITALIC\n" + 561 "\t\n" + 562 "\t -fsize=サイズ(初期値:14)\n" + 563 "\t フォントサイズを整数で指定\n" + 564 "\t\n" + 565 "\t -color=カラー\n" + 566 "\t 色を表す文字列(BLUE,GREEN か、#808000 などの16bitRGB表記)\n" + 567 "\t\n" + 568 "Usage: java org.opengion.fukurou.util.ImageUtil 入力ファイル 出力ファイル\n" + 569 " -trans [-color=カラー -alpha=透過率(0-100%)]\n" + 570 "\t -color=カラー\n" + 571 "\t 透明色にする色を指定(BLUE,GREEN か、#808000 などの16bitRGB表記)" + 572 "\t -alpha=透過率(0-100%)\n" + 573 "\t 透過率は、0:透明から100不透明まで指定します。\n" ; 574 System.out.println( usage ); 575 return ; 576 } 577 578 final String inImg = args[0]; 579 final String outImg = args[1]; 580 final String imgType= args[2]; 581 582// final boolean isMix = imgType.equals( "-mix" ); // 文字列合成 583// final boolean isTrn = imgType.equals( "-trans" ); // 透過色指定 584 585 final BufferedImage image = ImageUtil.readFile( inImg ); 586 587 final boolean isMix = imgType.equals( "-mix" ); // 文字列合成 588 if( isMix ) { 589 final String text = args[3]; 590 final int x = Integer.parseInt( args[4] ); 591 final int y = Integer.parseInt( args[5] ); 592 593 String fname = "Serif"; 594 int fstyle = Font.PLAIN; // =0; 595 int fsize = 14; 596 Color color = Color.BLUE; 597 598 for( int i=6; i<args.length; i++ ) { 599 if( args[i].startsWith( "-fname=" ) ) { fname = args[i].substring( 7 ); } // 7 = "-fname=".length() 600 if( args[i].startsWith( "-fstyle=" ) ) { fstyle = Integer.parseInt( args[i].substring( 8 ) ); } // 8 = "-fstyle=".length() 601 if( args[i].startsWith( "-fsize=" ) ) { fsize = Integer.parseInt( args[i].substring( 7 ) ); } // 7 = "-fsize=".length() 602 if( args[i].startsWith( "-color=" ) ) { color = ColorMap.getColorInstance( args[i].substring( 7 ) ); } // 7 = "-color=".length() 603 } 604 605 // 6.9.8.0 (2018/05/28) FindBugs:条件は効果がない 606// if( isMix ) { 607 final Font font = new Font( fname, fstyle, fsize ); 608 ImageUtil.mixImage( image , text , x , y , font , color ); 609// } 610 } 611 612 final boolean isTrn = imgType.equals( "-trans" ); // 透過色指定 613 if( isTrn ) { 614 Color fColor = Color.WHITE; 615 int alpha = 0; 616 617 for( int i=3; i<args.length; i++ ) { 618 if( args[i].startsWith( "-color=" ) ) { fColor = ColorMap.getColorInstance( args[i].substring( 7 ) ); } // 7 = "-color=".length() 619 if( args[i].startsWith( "-alpha=" ) ) { alpha = 255 / 100 * Integer.parseInt( args[i].substring( 7 ) ); } // 7 = "-alpha=".length() 620 } 621 622 final Color tColor = new Color( fColor.getRed() , fColor.getGreen() , fColor.getBlue() , alpha ); 623 624 ImageUtil.changeColor( image , fColor , tColor ); 625 } 626 627 ImageUtil.saveFile( image , outImg ); 628 } 629}