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