TaskCompressImageToJpeg.java revision 02d42f96dc463f76beae47b9ac4d681e87c06c57
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; 2002d42f96dc463f76beae47b9ac4d681e87c06c57Pengchong Jinimport android.media.CameraProfile; 215aa7eb2930b813c97f3754d93fe7fa978651887bSenpo Huimport android.net.Uri; 22ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin 23f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Linimport com.android.camera.Exif; 245aa7eb2930b813c97f3754d93fe7fa978651887bSenpo Huimport com.android.camera.app.MediaSaver; 25f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Linimport com.android.camera.app.OrientationManager; 2609d1f4473af1494572d77949ad87b95e21028135Sascha Haeberlingimport com.android.camera.app.OrientationManager.DeviceOrientation; 276a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Linimport com.android.camera.debug.Log; 283830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberlingimport com.android.camera.exif.ExifInterface; 294dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Linimport com.android.camera.one.v2.camera2proxy.ImageProxy; 3030ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Linimport com.android.camera.session.CaptureSession; 3130ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Linimport com.android.camera.util.JpegUtilNative; 3209d1f4473af1494572d77949ad87b95e21028135Sascha Haeberlingimport com.android.camera.util.Size; 33ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin 3430ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Linimport java.nio.ByteBuffer; 356a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Linimport java.util.HashMap; 366a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Linimport java.util.Map; 37ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Linimport java.util.concurrent.Executor; 38ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin 39ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin/** 4030ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * Implements the conversion of a YUV_420_888 image to compressed JPEG byte 414dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * array, using the native implementation of the Camera Application. If the 424dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * image is already JPEG, then it passes it through properly with the assumption 434dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * that the JPEG is already encoded in the proper orientation. 44ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin */ 45ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Linpublic class TaskCompressImageToJpeg extends TaskJpegEncode { 464dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin 4702d42f96dc463f76beae47b9ac4d681e87c06c57Pengchong Jin private static final int DEFAULT_JPEG_COMPRESSION_QUALITY = CameraProfile. 4802d42f96dc463f76beae47b9ac4d681e87c06c57Pengchong Jin getJpegEncodingQualityParameter(CameraProfile.QUALITY_HIGH); 49ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin 5030ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin /** 5130ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * Constructor 5230ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * 533830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling * @param image Image required for computation 5430ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * @param executor Executor to run events 553c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin * @param imageTaskManager Link to ImageBackend for reference counting 5630ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * @param captureSession Handler for UI/Disk events 5730ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin */ 58a1ea88071be696ea8dee99c7bdb4007128ed60efI-Jong Lin TaskCompressImageToJpeg(ImageToProcess image, Executor executor, 594dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin ImageTaskManager imageTaskManager, 604dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin CaptureSession captureSession) { 613c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin super(image, executor, imageTaskManager, ProcessingPriority.SLOW, captureSession); 62ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin } 63ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin 644dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin /** 654dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * Wraps the static call to JpegUtilNative for testability. {@see 664dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * JpegUtilNative#compressJpegFromYUV420Image} 674dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin */ 684dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin public int compressJpegFromYUV420Image(ImageProxy img, ByteBuffer outBuf, int quality, 694dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin int degrees) { 704dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin return JpegUtilNative.compressJpegFromYUV420Image(img, outBuf, quality, degrees); 71ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin } 72ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin 73f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin /** 746a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin * Encapsulates the required EXIF Tag parse for Image processing. 75f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin * 767a72d72e97fe280fd88127b7891efe9112bcaeacI-Jong Lin * @param jpegData Binary data of the JPEG with EXIF flags 776a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin * @return A Minimal Map from ExifInterface.Tag value to values required for Image processing 78f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin */ 796a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin public Map<Integer, Integer> exifGetMinimalTags(byte[] jpegData) { 806a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin if (jpegData == null) { 816a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin return null; 826a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin } 836a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin 846a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin ExifInterface exif = Exif.getExif(jpegData); 856a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin Map<Integer, Integer> map = new HashMap<Integer, Integer>(); 866a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin map.put(ExifInterface.TAG_ORIENTATION, 876a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin ExifInterface.getRotationForOrientationValue((short) Exif.getOrientation(exif))); 886a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin map.put(ExifInterface.TAG_PIXEL_X_DIMENSION, exif.getTagIntValue( 896a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin ExifInterface.TAG_PIXEL_X_DIMENSION)); 906a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin map.put(ExifInterface.TAG_PIXEL_Y_DIMENSION, exif.getTagIntValue( 916a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin ExifInterface.TAG_PIXEL_Y_DIMENSION)); 926a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin return map; 93f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin } 94f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin 95ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin @Override 96ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin public void run() { 973830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling ImageToProcess img = mImage; 98ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin 99f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin // For JPEG, it is the capture devices responsibility to get proper 100f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin // orientation. 101f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin 102f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin TaskImage inputImage, resultImage; 1034dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin byte[] writeOut; 1044dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin int numBytes; 105f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin ByteBuffer compressedData; 106f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin 107f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin switch (img.proxy.getFormat()) { 108f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin case ImageFormat.JPEG: 109f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin try { 110f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin // In the cases, we will request a zero-oriented JPEG from 111f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin // the HAL; the HAL may deliver its orientation in the JPEG 112f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin // encoding __OR__ EXIF -- we don't know. We need to read 113f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin // the EXIF setting from byte payload and the EXIF reader 114f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin // doesn't work on direct buffers. So, we make a local 115f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin // copy in a non-direct buffer. 116f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin ByteBuffer origBuffer = img.proxy.getPlanes().get(0).getBuffer(); 1177a72d72e97fe280fd88127b7891efe9112bcaeacI-Jong Lin compressedData = ByteBuffer.allocate(origBuffer.limit()); 118f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin origBuffer.rewind(); 119f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin compressedData.put(origBuffer); 120f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin origBuffer.rewind(); 121f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin compressedData.rewind(); 122f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin 1236a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin // For JPEG, always use the EXIF orientation as ground 1246a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin // truth on orientation, width and height. 1256a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin Map<Integer, Integer> minimalExifTags = exifGetMinimalTags(compressedData 1266a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin .array()); 1276a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin 1286a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin Integer exifOrientation = minimalExifTags.get(ExifInterface.TAG_ORIENTATION); 1296a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin Integer exifPixelXDimension = minimalExifTags 1306a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin .get(ExifInterface.TAG_PIXEL_X_DIMENSION); 1316a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin Integer exifPixelYDimension = minimalExifTags 1326a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin .get(ExifInterface.TAG_PIXEL_Y_DIMENSION); 1336a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin 1346a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin final DeviceOrientation exifDerivedRotation; 1356a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin if (exifOrientation == null) { 1366a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin // No existing rotation value is assumed to be 0 1376a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin // rotation. 1386a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin exifDerivedRotation = DeviceOrientation.CLOCKWISE_0; 1396a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin } else { 1406a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin exifDerivedRotation = OrientationManager.DeviceOrientation 1416a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin .from(exifOrientation); 1426a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin } 143f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin 1446a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin final int imageWidth; 1456a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin final int imageHeight; 1466a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin if (exifPixelXDimension == null || exifPixelYDimension == null) { 1476a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin Log.w(TAG, "Cannot parse EXIF for image dimensions, passing 0x0 dimensions"); 1486a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin imageHeight = 0; 1496a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin imageWidth = 0; 1506a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin } else { 1516a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin imageWidth = exifPixelXDimension; 1526a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin imageHeight = exifPixelYDimension; 1536a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin } 1546a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin 1556a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin // Ignore the device rotation on ImageToProcess and use the EXIF from 1566a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin // byte[] payload 157f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin inputImage = new TaskImage( 1586a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin exifDerivedRotation, 1596a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin imageWidth, 1606a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin imageHeight, 161f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin img.proxy.getFormat()); 162f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin resultImage = inputImage; // Pass through 163f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin } finally { 164f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin // Release the image now that you have a usable copy in 165f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin // local memory 166f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin // Or you failed to process 167f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin mImageTaskManager.releaseSemaphoreReference(img, mExecutor); 168f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin } 169f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin 170f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin onStart(mId, inputImage, resultImage, TaskInfo.Destination.FINAL_IMAGE); 171f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin 1727a72d72e97fe280fd88127b7891efe9112bcaeacI-Jong Lin numBytes = compressedData.limit(); 173f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin break; 174f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin case ImageFormat.YUV_420_888: 175f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin try { 176f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin inputImage = new TaskImage(img.rotation, img.proxy.getWidth(), 177f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin img.proxy.getHeight(), 178f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin img.proxy.getFormat()); 179f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin Size resultSize = getImageSizeForOrientation(img.proxy.getWidth(), 180f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin img.proxy.getHeight(), 181f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin img.rotation); 182f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin 183f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin // Resulting image will be rotated so that viewers won't 184f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin // have to rotate. That's why the resulting image will have 0 185f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin // rotation. 186f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin resultImage = new TaskImage( 187f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin DeviceOrientation.CLOCKWISE_0, resultSize.getWidth(), 188f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin resultSize.getHeight(), 189f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin ImageFormat.JPEG); 1906a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin // Image rotation is already encoded into the bytes. 1916a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin 192f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin onStart(mId, inputImage, resultImage, TaskInfo.Destination.FINAL_IMAGE); 1934dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin 1944dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin compressedData = ByteBuffer.allocateDirect(3 * resultImage.width 1954dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * resultImage.height); 1964dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin 197f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin // Do the actual compression here. 1984dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin numBytes = compressJpegFromYUV420Image( 1994dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin img.proxy, compressedData, DEFAULT_JPEG_COMPRESSION_QUALITY, 200ec604214008248f7858b3a8b66d70919947399a9I-Jong Lin inputImage.orientation.getDegrees()); 2014dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin 2024dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin if (numBytes < 0) { 2034dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin throw new RuntimeException("Error compressing jpeg."); 2044dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin } 2054dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin compressedData.limit(numBytes); 206f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin } finally { 207f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin // Release the image now that you have a usable copy in local memory 208f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin // Or you failed to process 209f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin mImageTaskManager.releaseSemaphoreReference(img, mExecutor); 210f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin } 211f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin break; 212f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin default: 213f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin mImageTaskManager.releaseSemaphoreReference(img, mExecutor); 214f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin throw new IllegalArgumentException( 215f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin "Unsupported input image format for TaskCompressImageToJpeg"); 21630ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin } 217ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin 218f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin writeOut = new byte[numBytes]; 219f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin compressedData.get(writeOut); 220f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin compressedData.rewind(); 221f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin 2224dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin onJpegEncodeDone(mId, inputImage, resultImage, writeOut, 2234dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin TaskInfo.Destination.FINAL_IMAGE); 2245aa7eb2930b813c97f3754d93fe7fa978651887bSenpo Hu 225f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin // In rare cases, TaskCompressImageToJpeg might complete before 226f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin // TaskConvertImageToRGBPreview. However, session should take care 227f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin // of out-of-order completion. 228f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin // EXIF tags are rewritten so that output from this task is normalized. 229f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin final TaskImage finalInput = inputImage; 230f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin final TaskImage finalResult = resultImage; 2316a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin 2325aa7eb2930b813c97f3754d93fe7fa978651887bSenpo Hu mSession.saveAndFinish(writeOut, resultImage.width, resultImage.height, 2335aa7eb2930b813c97f3754d93fe7fa978651887bSenpo Hu resultImage.orientation.getDegrees(), createExif(resultImage), 2345aa7eb2930b813c97f3754d93fe7fa978651887bSenpo Hu new MediaSaver.OnMediaSavedListener() { 2355aa7eb2930b813c97f3754d93fe7fa978651887bSenpo Hu @Override 2365aa7eb2930b813c97f3754d93fe7fa978651887bSenpo Hu public void onMediaSaved(Uri uri) { 237f68a39a0ad362ff0543e11e3f0ce11741a7de48cI-Jong Lin onUriResolved(mId, finalInput, finalResult, uri, 23830ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin TaskInfo.Destination.FINAL_IMAGE); 2395aa7eb2930b813c97f3754d93fe7fa978651887bSenpo Hu } 2405aa7eb2930b813c97f3754d93fe7fa978651887bSenpo Hu }); 241ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin } 24230ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin 2434dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin /** 2444dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * Wraps a possible log message to be overridden for testability purposes. 245ec604214008248f7858b3a8b66d70919947399a9I-Jong Lin * 2464dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * @param message 2474dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin */ 2484dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin protected void logWrapper(String message) { 2494dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin // Do nothing. 2504dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin } 2514dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin 2524dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin /** 2534dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * Wraps EXIF Interface for JPEG Metadata creation. Can be overridden for 2544dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * testing 255ec604214008248f7858b3a8b66d70919947399a9I-Jong Lin * 2564dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * @param image Metadata for a jpeg image to create EXIF Interface 2574dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * @return the created Exif Interface 2584dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin */ 2594dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin protected ExifInterface createExif(TaskImage image) { 2603830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling ExifInterface exif = new ExifInterface(); 2616a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin exif.setTag(exif.buildTag(ExifInterface.TAG_PIXEL_X_DIMENSION, image.width)); 2626a7c7722d1eb49418b8ee02df0018f03719a8c61I-Jong Lin exif.setTag(exif.buildTag(ExifInterface.TAG_PIXEL_Y_DIMENSION, image.height)); 2637a72d72e97fe280fd88127b7891efe9112bcaeacI-Jong Lin exif.setTag(exif.buildTag(ExifInterface.TAG_IMAGE_WIDTH, image.width)); 2647a72d72e97fe280fd88127b7891efe9112bcaeacI-Jong Lin exif.setTag(exif.buildTag(ExifInterface.TAG_IMAGE_LENGTH, image.height)); 2653830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling exif.setTag(exif.buildTag(ExifInterface.TAG_ORIENTATION, 2663830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling ExifInterface.getOrientationValueForRotation(image.orientation.getDegrees()))); 2673830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling return exif; 2683830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling } 26909d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling 27009d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling /** 27109d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling * @param originalWidth the width of the original image captured from the 27209d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling * camera 27309d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling * @param originalHeight the height of the original image captured from the 27409d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling * camera 27509d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling * @param orientation the rotation to apply, in degrees. 27609d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling * @return The size of the final rotated image 27709d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling */ 27809d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling private Size getImageSizeForOrientation(int originalWidth, int originalHeight, 27909d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling DeviceOrientation orientation) { 28009d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling if (orientation == DeviceOrientation.CLOCKWISE_0 28109d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling || orientation == DeviceOrientation.CLOCKWISE_180) { 28209d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling return new Size(originalWidth, originalHeight); 28309d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling } else if (orientation == DeviceOrientation.CLOCKWISE_90 28409d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling || orientation == DeviceOrientation.CLOCKWISE_270) { 28509d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling return new Size(originalHeight, originalWidth); 28609d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling } else { 28709d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling // Unsupported orientation. Get rid of this once UNKNOWN is gone. 28809d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling return new Size(originalWidth, originalHeight); 28909d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling } 29009d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling } 291ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin} 292