くらげになりたい。

くらげのようにふわふわ生きたい日曜プログラマなブログ。趣味の備忘録です。

Androidで回転情報(EXIF情報のTAG_ORIENTATION)を含めて画像を縮小する

Androidからサーバに画像を保存する際に、Android側で縮小したいなと思った時の備忘録。

特にハマったのはローテート情報...
縮小するだけだとEXIFがないので、ImageViewで表示すると回転しない。。

流れとしては、以下の感じ

  1. 元画像のローテーション情報(EXIF情報)を取得
  2. リサイズ処理。縮小したBitmapを取得
  3. 縮小したBitmapにファイルに書き出し
  4. 書き出したファイルにローテーション情報(EXIF情報)を追記

全量はこちら

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.media.ExifInterface;
import android.util.Log;
import android.util.Size;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;

import static android.support.media.ExifInterface.ORIENTATION_UNDEFINED;
import static android.support.media.ExifInterface.TAG_ORIENTATION;

public class BitmapHandler {
    private static final String TAG = BitmapHandler.class.getSimpleName();
    private static final double MAX_HEIGHT = 1920.0;
    private static final double MAX_WIDTH = 1080.0;
    private Context context;


    public BitmapHandler(Context context) {
        this.context = context;
    }


    /**
     * ファイルサイズがHDより大きい場合に、リサイズしてたファイルを返却
     *
     * @param uri リサイズしたいファイルのURI
     * @return リサイズ後のFile
     * @throws IOException IOException
     */
    public File getResizedFile(Uri uri) throws IOException {

        // 元画像のローテーション情報を取得
        int orientation = getOrientation(uri);

        // リサイズ処理。リサイズ後のBitmapを取得
        Bitmap resized = resizedBitmap(uri);

        // ファイルの書き出し処理
        // バッファ書き出し後にEXIFを追記
        return writeFile(resized);
    }

    /**
     * 画像のORIENTATIONを取得
     *
     * @param uri 対象画像のURI
     * @return TAG_ORIENTATIONの値
     */
    private int getOrientation(@NonNull Uri uri) {
        try (InputStream src = context.getContentResolver().openInputStream(uri)) {
            if (src == null) return ORIENTATION_UNDEFINED;

            // 画像の向きを取得
            ExifInterface exifInterface = new ExifInterface(src);
            int orientation = exifInterface.getAttributeInt(TAG_ORIENTATION, ORIENTATION_UNDEFINED);
            return orientation;
        } catch (IOException e) {
            Log.w(TAG, e.getLocalizedMessage(), e);
            return ORIENTATION_UNDEFINED;
        }

    }

    /**
     * リサイズ後のBitmapを返却
     *
     * @param uri リサイズ対象画像のURI
     * @return リサイズ後のBitmap
     * @throws IOException IOException
     */
    private Bitmap resizedBitmap(@NonNull Uri uri) throws IOException {
        // 任意の縮尺でビットマップを生成する
        Size scale = getScale(uri);
        try (InputStream src = context.getContentResolver().openInputStream(uri)) {
            Bitmap bitmap = BitmapFactory.decodeStream(src);
            return Bitmap.createScaledBitmap(bitmap, scale.getWidth(), scale.getHeight(), true);
        }
    }

    /**
     * リサイズ後のスケールを計算
     *
     * @param uri リサイズ対象画像のURI
     * @return リサイズ後のサイズ
     * @throws IOException IOException
     */
    private Size getScale(Uri uri) throws IOException {
        try (InputStream src = context.getContentResolver().openInputStream(uri)) {
            // オプションオブジェクトを生成
            BitmapFactory.Options options = new BitmapFactory.Options();
            // 画像をメモリに展開せずにサイズ情報のみ取得する
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(src, null, options);

            // スケールの計算
            double pixels = options.outWidth * options.outHeight;
            double scale = 1.0;
            // 最大サイズよりもピクセル数が多い場合のみ縮小
            if (pixels > MAX_HEIGHT * MAX_WIDTH) {
                if (options.outWidth < options.outHeight) { // 縦長の場合
                    scale = MAX_HEIGHT / options.outHeight;
                } else { // 横長の場合
                    scale = MAX_HEIGHT / options.outWidth;
                }
            }

            int newWidth = (int) Math.ceil(options.outWidth * scale);
            int newHeight = (int) Math.ceil(options.outHeight * scale);
            return new Size(newWidth, newHeight);
        } catch (IOException e) {
            Log.w(TAG, e.getLocalizedMessage(), e);
            throw e;
        }
    }

    /**
     * Bitmapのファイル書き出し
     *
     * @param bitmap      書き出すBitmap
     * @param orientation ローテーション情報
     * @return 書き出し後のファイル
     * @throws IOException IOException
     */
    private File writeFile(Bitmap bitmap, int orientation) throws IOException {
        String fileName = UUID.randomUUID().toString();
        File file = new File(context.getCacheDir(), fileName);
        file.createNewFile();
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100 /*ignored for PNG*/, bos);
            try (FileOutputStream fos = new FileOutputStream(file)) {
                fos.write(bos.toByteArray());
                fos.flush();
            }

            ExifInterface exif = new ExifInterface(file.getPath());
            exif.setAttribute(TAG_ORIENTATION, String.valueOf(orientation));
            exif.saveAttributes();
        }
        return file;
    }
}

以上!!

参考にしたサイト様