JavaのWebアプリケーション開発フレームワークによる、Webサイト開発の顛末記です。

EclipseのMavenを使った、Spring-MVC、Thymeleaf、MyBatis 等のプログラミングテクニックを、
備忘録的に記録しています。実際に動くソースコードを多用して説明していますので、
これからEclipseや、Spring-MVCを始めたいと思っている人にとって、少しでも参考になれば幸いです。
Spring-MVCの散歩道 > 応用の森(Java編) > Java上で画像を合成する方法について

package jp.dip.arimodoki.cntl;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Iterator;

import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;

import org.apache.commons.codec.binary.Base64;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import jp.dip.arimodoki.common.CConst;

/**
 * 画像の合成
 */
@Scope("prototype")
@Controller
public class ImgComposit implements CConst {

    //画像長辺最大ピクセル数
    static final int IMG_MAX_PIXCEL = 640;

    /**
     * 画像投入画面
     */
    @RequestMapping(value = "/imgcomposit")
     public String imgcomposit() throws Exception {
        return "imgcomposit";
    }

    /**
     * 画像合成実行
     */
    @RequestMapping(value = "/composit")
     public String composit(
             @RequestParam MultipartFile uploadfile,
             Model model
         ) throws Exception {

        //一時ファイル名
        String[] tmpfilename = {"c:/temp/" + uploadfile.getName()};

        //submitされた画像データストリームを取得する
        InputStream is = uploadfile.getInputStream();

        //画像圧縮処理
        this.compress(is, tmpfilename);
        is.close();

        //画像合成
        this.composition(tmpfilename[0]);

        //合成結果画像ファイルを再読み込み
        is = new FileInputStream( tmpfilename[0] );

        ByteArrayOutputStream os = new ByteArrayOutputStream();
        byte[] indata = new byte[10240*16];
        int siz;
        //バイト配列に変換
        while( ( siz = is.read(indata, 0, indata.length) ) > 0 ) {
            os.write( indata, 0, siz );
        }
        is.close();

        //画像データをbase64エンコードする
        String base64 = new String(Base64.encodeBase64(os.toByteArray()), "ASCII");

        StringBuffer data = new StringBuffer();
        //画像タイプはJPEG固定
        data.append("data:image/jpeg;base64,");
        data.append(base64);

        //エンコードデータを、データモデルにセット
        model.addAttribute("base64data", data.toString());

        return "imgcomposit";
    }

    /**
    * 画像の合成処理
    * @param base_img 合成元画像データファイル名
    * @exception エラー発生時は、Exceptionをスローします
    */
    private void composition(String base_img) throws Exception {
        Exception exception = null;

        //ベース画像情報取得
        File org = new File(base_img);
        BufferedImage bimg = ImageIO.read(org);
        int owidth  = bimg.getWidth();      //画像幅
        int oheight = bimg.getHeight();     //画像高

        //合成するウォーターマーク画像パス
        String watermark_img = "c:/temp/water_arimodoki.png";
        //ウォーターマーク画像情報を取得
        File watermark = new File(watermark_img);
        BufferedImage wimg = ImageIO.read(watermark);
        //ウォーターマーク画像の幅/高取得
        int lwidth  = wimg.getWidth();      //画像幅
        int lheight = wimg.getHeight();     //画像高

        //ベース画像へのウォーターマーク画像の描画位置を計算(画像中央)
        int lpos_x = (owidth-lwidth)/2;
        int lpos_y = (oheight-lheight)/2;

        //ベースイメージデータのグラフィックを取得
        Graphics2D g = bimg.createGraphics();
        //ベースイメージにウォーターマークを上書きする
        g.drawImage(wimg, new AffineTransform(1f,0f,0f,1f,lpos_x,lpos_y), null);
        //ファイルに出力する
        ImageIO.write(bimg, "jpeg", org);
        g.dispose();
    }

    /**
    * データファイルの圧縮処理
    * @param dataStream 画像データストリーム
    * @param filename 圧縮元ファイル名
    * @exception エラー発生時は、Exceptionをスローします
    */
    private void compress(InputStream dataStream, String[] filename) throws Exception {
        Exception exception = null;

        String target =  filename[0];
        int img_type = 0;
        String  type_str = "JPEG";
        BufferedImage bimg = null;
        BufferedImage tmpImage = null;
        int width  = 0;
        int height = 0;
        int z_size = IMG_MAX_PIXCEL;        //長編640Pixcel
        boolean compress = false;
        byte buf[] = new byte[1024*100];
        int size;
        File outfile = new File(target);

        //一旦テンポラリにファイルを出力する
        FileOutputStream OutFile = new FileOutputStream( outfile );
        while((size = dataStream.read(buf)) > 0) {
            OutFile.write( buf, 0, size);
        }
        OutFile.flush();
        OutFile.close();
        dataStream.close(); //入力ストリームはもう不要なので閉じる

        //出力したテンポラリのファイル情報を取得
        bimg = ImageIO.read( outfile );
        //Source Imageの幅/高取得
        width  = bimg.getWidth();        //Source 画像幅(Pixcel)
        height = bimg.getHeight();       //Source 画像高(Pixcel)

        //ファイルフォーマット形式の取得
        FileInputStream inFile = new FileInputStream(outfile);
        ImageInputStream imageIn = ImageIO.createImageInputStream(inFile);
        ImageReader rd = null;
        Iterator<ImageReader>   readers = ImageIO.getImageReaders(imageIn);

        //画像タイプを取得
        if (readers.hasNext()) {
            rd = readers.next();
            type_str = rd.getFormatName().toUpperCase();
            logger.log_info(this,"src format=["+rd.getFormatName()+"]:["+target+"]");
            rd.dispose();
        } else {
            logger.log_info(this,"src unknown image format["+target+"]");
        }
        imageIn.close();
        inFile..close();

        if(type_str.equals("JPEG")) {
            img_type = 0;
            target += ".jpg";
        }

        //縮小画像ファイル名
        filename[0] = target;

        //指定サイズに圧縮
        //縦横の縮小率を計算して,小さい方を縮小率とする
        //(縦横比を維持するため)
        double ratio = 0;
        if ( width > height) {              //Landscape
            if(width > z_size) {
                compress = true;
            }
            //縮小率
            ratio = (double)z_size / (double)width;
        } else {                                      //Portlate
            if(height > z_size) {
                compress = true;
            }
            //縮小率
            ratio = (double)z_size / (double)height;
        }

        //出力用ファイルストリーム
        OutFile = new FileOutputStream( target );
        if(compress == true && img_type==0) {   //元画像の長辺が640Pixcel以上でかつjpgの場合
                //縮小結果の幅、高
                int shrinkedWidth = (int)(width * ratio);
                int shrinkedHeight = (int)(height * ratio);

                //縮小データ出力イメージバッファ生成
                if( bimg.getType() == 0 ) {
                    tmpImage = new BufferedImage(shrinkedWidth, shrinkedHeight,
                                                        BufferedImage.TYPE_4BYTE_ABGR_PRE);
                } else {
                    tmpImage = new BufferedImage(shrinkedWidth, shrinkedHeight, bimg.getType());
                }

                //縮小データGraphicsの取得
                Graphics   aig = tmpImage.getGraphics();
                //画質優先でスケーリング
                aig.drawImage(bimg.getScaledInstance(shrinkedWidth, shrinkedHeight, Image.SCALE_SMOOTH), 0, 0, null);
                //縮小したイメージデータの出力
                ImageIO.write(tmpImage, "jpeg", OutFile);
                OutFile.close();
                outfile.delete();		//テンポラリファイル削除

        } else {    //元画像の長辺が640Pixcel以下(そのまま)
            //圧縮なしで、出力ファイルに書き込む
            inFile = new FileInputStream( outfile );
            while((size = inFile.read(buf)) > 0) {
                OutFile.write( buf, 0, size);
            }
            inFile.close();
            OutFile.close();
            outfile.delete();		//テンポラリファイル削除
        }
    }
}