1 /* 2 * Copyright 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.camera.view.transform; 18 19 import static androidx.camera.core.impl.utils.TransformUtils.getExifTransform; 20 import static androidx.camera.core.impl.utils.TransformUtils.getNormalizedToBuffer; 21 import static androidx.camera.core.impl.utils.TransformUtils.rectToSize; 22 23 import android.content.ContentResolver; 24 import android.graphics.Bitmap; 25 import android.graphics.BitmapFactory; 26 import android.graphics.Matrix; 27 import android.graphics.Rect; 28 import android.media.ExifInterface; 29 import android.net.Uri; 30 31 import androidx.camera.core.ImageCapture; 32 import androidx.camera.core.UseCase; 33 import androidx.camera.core.impl.utils.Exif; 34 import androidx.camera.view.TransformExperimental; 35 36 import org.jspecify.annotations.NonNull; 37 38 import java.io.File; 39 import java.io.FileInputStream; 40 import java.io.IOException; 41 import java.io.InputStream; 42 43 /** 44 * Factory for extracting transform info from image files. 45 * 46 * <p> This class is for extracting a {@link OutputTransform} from an image file saved by 47 * {@link ImageCapture}. The {@link OutputTransform} represents the transform being applied to 48 * the original camera buffer, which can be used by {@link CoordinateTransform} to transform 49 * coordinates between {@link UseCase}s. 50 * 51 * @see OutputTransform 52 * @see CoordinateTransform 53 */ 54 @TransformExperimental 55 public final class FileTransformFactory { 56 57 private boolean mUsingExifOrientation; 58 59 /** 60 * Whether to include the {@link ExifInterface#TAG_ORIENTATION}. 61 * 62 * By default, this value is false, e.g. loading image with {@link BitmapFactory} does 63 * not apply the exif orientation to the loaded {@link Bitmap}. Only set this if the exif 64 * orientation is applied to the loaded file. For example, if the image is loaded by a 3P 65 * library that automatically applies exif orientation. 66 */ setUsingExifOrientation(boolean usingExifOrientation)67 public void setUsingExifOrientation(boolean usingExifOrientation) { 68 mUsingExifOrientation = usingExifOrientation; 69 } 70 71 /** 72 * Whether the factory respects the exif of the image file. 73 */ isUsingExifOrientation()74 public boolean isUsingExifOrientation() { 75 return mUsingExifOrientation; 76 } 77 78 /** 79 * Extracts transform info from the given {@link Uri}. 80 */ getOutputTransform(@onNull ContentResolver contentResolver, @NonNull Uri uri)81 public @NonNull OutputTransform getOutputTransform(@NonNull ContentResolver contentResolver, 82 @NonNull Uri uri) 83 throws IOException { 84 try (InputStream inputStream = contentResolver.openInputStream(uri)) { 85 return getOutputTransform(inputStream); 86 } 87 } 88 89 /** 90 * Extracts transform info from the given {@link File}. 91 */ getOutputTransform(@onNull File file)92 public @NonNull OutputTransform getOutputTransform(@NonNull File file) throws IOException { 93 try (InputStream inputStream = new FileInputStream(file)) { 94 return getOutputTransform(inputStream); 95 } 96 } 97 98 /** 99 * Extracts transform info from the given {@link InputStream}. 100 */ getOutputTransform(@onNull InputStream inputStream)101 public @NonNull OutputTransform getOutputTransform(@NonNull InputStream inputStream) 102 throws IOException { 103 Exif exif = Exif.createFromInputStream(inputStream); 104 Rect cropRect = new Rect(0, 0, exif.getWidth(), exif.getHeight()); 105 106 // Map the normalized space to the image buffer. 107 Matrix matrix = getNormalizedToBuffer(cropRect); 108 109 if (mUsingExifOrientation) { 110 // Add exif transform if enabled. 111 matrix.postConcat( 112 getExifTransform(exif.getOrientation(), exif.getWidth(), exif.getHeight())); 113 } 114 115 return new OutputTransform(matrix, rectToSize(cropRect)); 116 } 117 } 118