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