TaskCompressImageToJpeg.java revision f68a39a0ad362ff0543e11e3f0ce11741a7de48c
1ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin/*
2ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin * Copyright (C) 2014 The Android Open Source Project
3ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin *
4ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin * Licensed under the Apache License, Version 2.0 (the "License");
5ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin * you may not use this file except in compliance with the License.
6ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin * You may obtain a copy of the License at
7ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin *
8ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin *      http://www.apache.org/licenses/LICENSE-2.0
9ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin *
10ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin * Unless required by applicable law or agreed to in writing, software
11ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin * distributed under the License is distributed on an "AS IS" BASIS,
12ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin * See the License for the specific language governing permissions and
14ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin * limitations under the License.
15ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin */
16ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
173830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberlingpackage com.android.camera.processing.imagebackend;
18ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
19ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Linimport android.graphics.ImageFormat;
205aa7eb2930b813c97f3754d93fe7fa978651887bSenpo Huimport android.net.Uri;
21ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
22f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Linimport com.android.camera.Exif;
235aa7eb2930b813c97f3754d93fe7fa978651887bSenpo Huimport com.android.camera.app.MediaSaver;
24f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Linimport com.android.camera.app.OrientationManager;
2509d1f4473af1494572d77949ad87b95e21028135Sascha Haeberlingimport com.android.camera.app.OrientationManager.DeviceOrientation;
26f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Linimport com.android.camera.debug.Log;
273830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberlingimport com.android.camera.exif.ExifInterface;
284dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Linimport com.android.camera.one.v2.camera2proxy.ImageProxy;
2930ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Linimport com.android.camera.session.CaptureSession;
3030ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Linimport com.android.camera.util.JpegUtilNative;
3109d1f4473af1494572d77949ad87b95e21028135Sascha Haeberlingimport com.android.camera.util.Size;
32ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
3330ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Linimport java.nio.ByteBuffer;
34ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Linimport java.util.concurrent.Executor;
35ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
36ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin/**
3730ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * Implements the conversion of a YUV_420_888 image to compressed JPEG byte
384dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * array, using the native implementation of the Camera Application. If the
394dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * image is already JPEG, then it passes it through properly with the assumption
404dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * that the JPEG is already encoded in the proper orientation.
41ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin */
42ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Linpublic class TaskCompressImageToJpeg extends TaskJpegEncode {
434dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin
4430ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin    private static final int DEFAULT_JPEG_COMPRESSION_QUALITY = 90;
45ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
4630ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin    /**
4730ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * Constructor
4830ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *
493830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling     * @param image Image required for computation
5030ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * @param executor Executor to run events
513c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin     * @param imageTaskManager Link to ImageBackend for reference counting
5230ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * @param captureSession Handler for UI/Disk events
5330ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     */
54a1ea88071be696ea8dee99c7bdb4007128ed60efI-Jong Lin    TaskCompressImageToJpeg(ImageToProcess image, Executor executor,
554dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            ImageTaskManager imageTaskManager,
564dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            CaptureSession captureSession) {
573c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin        super(image, executor, imageTaskManager, ProcessingPriority.SLOW, captureSession);
58ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
59ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
604dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin    /**
614dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin     * Wraps the static call to JpegUtilNative for testability. {@see
624dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin     * JpegUtilNative#compressJpegFromYUV420Image}
634dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin     */
644dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin    public int compressJpegFromYUV420Image(ImageProxy img, ByteBuffer outBuf, int quality,
654dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            int degrees) {
664dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin        return JpegUtilNative.compressJpegFromYUV420Image(img, outBuf, quality, degrees);
67ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
68ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
69f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin    /**
70f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin     * Wraps static call to Exif for testability.
71f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin     * @param jpegData  Binary data of the JPEG with EXIF flags
72f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin     *
73f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin     * @return Degrees of rotation of the EXIF flag
74f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin     */
75f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin    public int exifGetOrientation(byte [] jpegData) {
76f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin        return Exif.getOrientation(jpegData);
77f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin    }
78f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin
79ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    @Override
80ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    public void run() {
813830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling        ImageToProcess img = mImage;
82ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
83f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin        // For JPEG, it is the capture devices responsibility to get proper
84f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin        // orientation.
85f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin
86f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin        TaskImage inputImage, resultImage;
87f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin
884dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin        byte[] writeOut;
894dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin        int numBytes;
90f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin        ByteBuffer compressedData;
91f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin
92f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin        switch (img.proxy.getFormat()) {
93f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin            case ImageFormat.JPEG:
94f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                try {
95f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                    // In the cases, we will request a zero-oriented JPEG from
96f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                    // the HAL; the HAL may deliver its orientation in the JPEG
97f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                    // encoding __OR__ EXIF -- we don't know. We need to read
98f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                    // the EXIF setting from byte payload and the EXIF reader
99f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                    // doesn't work on direct buffers. So, we make a local
100f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                    // copy in a non-direct buffer.
101f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                    ByteBuffer origBuffer = img.proxy.getPlanes().get(0).getBuffer();
102f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                    compressedData = ByteBuffer.allocate(origBuffer.capacity());
103f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                    origBuffer.rewind();
104f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                    compressedData.put(origBuffer);
105f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                    origBuffer.rewind();
106f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                    compressedData.rewind();
107f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin
108f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                    // For JPEG, always use the EXIF orientation as ground truth.
109f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                    int exifDerivedRotation = exifGetOrientation(compressedData.array());
110f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin
111f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                    // Ignore the passed-in rotation and use the EXIF from byte[] payload
112f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                    inputImage = new TaskImage(
113f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                            OrientationManager.DeviceOrientation.from(exifDerivedRotation),
114f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                            img.proxy.getWidth(),
115f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                            img.proxy.getHeight(),
116f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                            img.proxy.getFormat());
117f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                    resultImage = inputImage; // Pass through
118f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                } finally {
119f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                    // Release the image now that you have a usable copy in
120f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                    // local memory
121f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                    // Or you failed to process
122f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                    mImageTaskManager.releaseSemaphoreReference(img, mExecutor);
123f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                }
124f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin
125f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                onStart(mId, inputImage, resultImage, TaskInfo.Destination.FINAL_IMAGE);
126f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin
127f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                numBytes = compressedData.capacity();
128f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                break;
129f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin            case ImageFormat.YUV_420_888:
130f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                try {
131f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                    inputImage = new TaskImage(img.rotation, img.proxy.getWidth(),
132f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                            img.proxy.getHeight(),
133f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                            img.proxy.getFormat());
134f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                    Size resultSize = getImageSizeForOrientation(img.proxy.getWidth(),
135f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                            img.proxy.getHeight(),
136f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                            img.rotation);
137f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin
138f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                    // Resulting image will be rotated so that viewers won't
139f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                    // have to rotate. That's why the resulting image will have 0
140f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                    // rotation.
141f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                    resultImage = new TaskImage(
142f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                            DeviceOrientation.CLOCKWISE_0, resultSize.getWidth(),
143f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                            resultSize.getHeight(),
144f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                            ImageFormat.JPEG);
145f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                    onStart(mId, inputImage, resultImage, TaskInfo.Destination.FINAL_IMAGE);
1464dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin
1474dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                    compressedData = ByteBuffer.allocateDirect(3 * resultImage.width
1484dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                            * resultImage.height);
1494dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin
150f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                    // Do the actual compression here.
1514dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                    numBytes = compressJpegFromYUV420Image(
1524dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                            img.proxy, compressedData, DEFAULT_JPEG_COMPRESSION_QUALITY,
153ec604214008248f7858b3a8b66d70919947399a9I-Jong Lin                            inputImage.orientation.getDegrees());
1544dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin
1554dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                    if (numBytes < 0) {
1564dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                        throw new RuntimeException("Error compressing jpeg.");
1574dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                    }
1584dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                    compressedData.limit(numBytes);
159f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                } finally {
160f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                    // Release the image now that you have a usable copy in local memory
161f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                    // Or you failed to process
162f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                    mImageTaskManager.releaseSemaphoreReference(img, mExecutor);
163f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                }
164f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                break;
165f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin            default:
166f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                mImageTaskManager.releaseSemaphoreReference(img, mExecutor);
167f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                throw new IllegalArgumentException(
168f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                        "Unsupported input image format for TaskCompressImageToJpeg");
16930ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin        }
170ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
171f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin        writeOut = new byte[numBytes];
172f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin        compressedData.get(writeOut);
173f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin        compressedData.rewind();
174f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin
1754dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin        onJpegEncodeDone(mId, inputImage, resultImage, writeOut,
1764dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                TaskInfo.Destination.FINAL_IMAGE);
1775aa7eb2930b813c97f3754d93fe7fa978651887bSenpo Hu
178f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin        // In rare cases, TaskCompressImageToJpeg might complete before
179f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin        // TaskConvertImageToRGBPreview. However, session should take care
180f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin        // of out-of-order completion.
181f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin        // EXIF tags are rewritten so that output from this task is normalized.
182f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin        final TaskImage finalInput = inputImage;
183f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin        final TaskImage finalResult = resultImage;
1845aa7eb2930b813c97f3754d93fe7fa978651887bSenpo Hu        mSession.saveAndFinish(writeOut, resultImage.width, resultImage.height,
1855aa7eb2930b813c97f3754d93fe7fa978651887bSenpo Hu                resultImage.orientation.getDegrees(), createExif(resultImage),
1865aa7eb2930b813c97f3754d93fe7fa978651887bSenpo Hu                new MediaSaver.OnMediaSavedListener() {
1875aa7eb2930b813c97f3754d93fe7fa978651887bSenpo Hu                    @Override
1885aa7eb2930b813c97f3754d93fe7fa978651887bSenpo Hu                    public void onMediaSaved(Uri uri) {
189f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin                        onUriResolved(mId, finalInput, finalResult, uri,
19030ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin                                TaskInfo.Destination.FINAL_IMAGE);
1915aa7eb2930b813c97f3754d93fe7fa978651887bSenpo Hu                    }
1925aa7eb2930b813c97f3754d93fe7fa978651887bSenpo Hu                });
193ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
19430ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin
1954dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin    /**
1964dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin     * Wraps a possible log message to be overridden for testability purposes.
197ec604214008248f7858b3a8b66d70919947399a9I-Jong Lin     *
1984dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin     * @param message
1994dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin     */
2004dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin    protected void logWrapper(String message) {
2014dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin        // Do nothing.
2024dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin    }
2034dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin
2044dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin    /**
2054dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin     * Wraps EXIF Interface for JPEG Metadata creation. Can be overridden for
2064dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin     * testing
207ec604214008248f7858b3a8b66d70919947399a9I-Jong Lin     *
2084dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin     * @param image Metadata for a jpeg image to create EXIF Interface
2094dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin     * @return the created Exif Interface
2104dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin     */
2114dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin    protected ExifInterface createExif(TaskImage image) {
2123830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling        ExifInterface exif = new ExifInterface();
2133830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling        exif.setTag(exif.buildTag(ExifInterface.TAG_PIXEL_X_DIMENSION, image.width));
2143830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling        exif.setTag(exif.buildTag(ExifInterface.TAG_PIXEL_Y_DIMENSION, image.height));
2153830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling        exif.setTag(exif.buildTag(ExifInterface.TAG_ORIENTATION,
2163830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling                ExifInterface.getOrientationValueForRotation(image.orientation.getDegrees())));
2173830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling        return exif;
2183830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling    }
21909d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling
22009d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling    /**
22109d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling     * @param originalWidth the width of the original image captured from the
22209d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling     *            camera
22309d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling     * @param originalHeight the height of the original image captured from the
22409d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling     *            camera
22509d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling     * @param orientation the rotation to apply, in degrees.
22609d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling     * @return The size of the final rotated image
22709d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling     */
22809d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling    private Size getImageSizeForOrientation(int originalWidth, int originalHeight,
22909d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling            DeviceOrientation orientation) {
23009d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling        if (orientation == DeviceOrientation.CLOCKWISE_0
23109d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling                || orientation == DeviceOrientation.CLOCKWISE_180) {
23209d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling            return new Size(originalWidth, originalHeight);
23309d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling        } else if (orientation == DeviceOrientation.CLOCKWISE_90
23409d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling                || orientation == DeviceOrientation.CLOCKWISE_270) {
23509d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling            return new Size(originalHeight, originalWidth);
23609d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling        } else {
23709d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling            // Unsupported orientation. Get rid of this once UNKNOWN is gone.
23809d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling            return new Size(originalWidth, originalHeight);
23909d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling        }
24009d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling    }
241ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin}
242