TaskCompressImageToJpeg.java revision ec604214008248f7858b3a8b66d70919947399a9
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
225aa7eb2930b813c97f3754d93fe7fa978651887bSenpo Huimport com.android.camera.app.MediaSaver;
2309d1f4473af1494572d77949ad87b95e21028135Sascha Haeberlingimport com.android.camera.app.OrientationManager.DeviceOrientation;
243830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberlingimport com.android.camera.exif.ExifInterface;
254dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Linimport com.android.camera.one.v2.camera2proxy.ImageProxy;
2630ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Linimport com.android.camera.session.CaptureSession;
2730ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Linimport com.android.camera.util.JpegUtilNative;
2809d1f4473af1494572d77949ad87b95e21028135Sascha Haeberlingimport com.android.camera.util.Size;
29ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
3030ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Linimport java.nio.ByteBuffer;
31ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Linimport java.util.concurrent.Executor;
32ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
33ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin/**
3430ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin * Implements the conversion of a YUV_420_888 image to compressed JPEG byte
354dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * array, using the native implementation of the Camera Application. If the
364dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * image is already JPEG, then it passes it through properly with the assumption
374dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin * that the JPEG is already encoded in the proper orientation.
38ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin */
39ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Linpublic class TaskCompressImageToJpeg extends TaskJpegEncode {
404dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin
4130ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin    private static final int DEFAULT_JPEG_COMPRESSION_QUALITY = 90;
42ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
4330ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin    /**
4430ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * Constructor
4530ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     *
463830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling     * @param image Image required for computation
4730ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * @param executor Executor to run events
483c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin     * @param imageTaskManager Link to ImageBackend for reference counting
4930ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     * @param captureSession Handler for UI/Disk events
5030ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin     */
51a1ea88071be696ea8dee99c7bdb4007128ed60efI-Jong Lin    TaskCompressImageToJpeg(ImageToProcess image, Executor executor,
524dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            ImageTaskManager imageTaskManager,
534dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            CaptureSession captureSession) {
543c7b7ec6aa2e51859718a6d6dead3c12d10ea370I-Jong Lin        super(image, executor, imageTaskManager, ProcessingPriority.SLOW, captureSession);
55ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
56ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
574dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin    /**
584dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin     * Wraps the static call to JpegUtilNative for testability. {@see
594dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin     * JpegUtilNative#compressJpegFromYUV420Image}
604dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin     */
614dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin    public int compressJpegFromYUV420Image(ImageProxy img, ByteBuffer outBuf, int quality,
624dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            int degrees) {
634dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin        return JpegUtilNative.compressJpegFromYUV420Image(img, outBuf, quality, degrees);
64ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
65ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
66ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    @Override
67ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    public void run() {
683830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling        ImageToProcess img = mImage;
694dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin        if (img.rotation != DeviceOrientation.CLOCKWISE_0
704dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                && img.proxy.getFormat() == ImageFormat.JPEG) {
714dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            // TODO: Ensure the capture for SimpleOneCameraFactory is always
724dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            // at zero rotation
734dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            // To avoid suboptimal rotation implementation, the JPEG should
744dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            // come with zero orientation. Any post-rotation of JPEG would be
754dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            // REALLY sub-optimal.
764dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin
774dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            // Release image references on error
784dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            mImageTaskManager.releaseSemaphoreReference(img, mExecutor);
794dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin
804dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            throw new IllegalStateException(
814dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                    "Image Rotation for JPEG should be zero, but is actually "
824dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                            + img.rotation.getDegrees());
834dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin        }
84ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
85ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        final TaskImage inputImage = new TaskImage(
864dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                img.rotation, img.proxy.getWidth(), img.proxy.getHeight(),
874dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                img.proxy.getFormat());
884dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin        Size resultSize = getImageSizeForOrientation(img.proxy.getWidth(),
894dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                img.proxy.getHeight(),
9009d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling                img.rotation);
91ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin        final TaskImage resultImage = new TaskImage(
9209d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling                DeviceOrientation.CLOCKWISE_0, resultSize.getWidth(), resultSize.getHeight(),
9309d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling                ImageFormat.JPEG);
944dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin        byte[] writeOut;
954dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin        int numBytes;
964dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin
974dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin        try {
984dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            // Resulting image will be rotated so that viewers won't have to
994dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            // rotate. That's why the resulting image will have 0 rotation.
1004dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin
1014dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            onStart(mId, inputImage, resultImage, TaskInfo.Destination.FINAL_IMAGE);
1024dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            logWrapper("TIMER_END Full-size YUV buffer available, w=" + img.proxy.getWidth()
1034dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                    + " h="
1044dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                    + img.proxy.getHeight() + " of format " + img.proxy.getFormat()
1054dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                    + " (35==YUV_420_888)");
1064dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin
1074dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            ByteBuffer compressedData;
1084dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            switch (inputImage.format) {
1094dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                case ImageFormat.JPEG:
1104dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                    compressedData = img.proxy.getPlanes().get(0).getBuffer();
1114dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                    numBytes = compressedData.capacity();
1124dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                    break;
1134dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                case ImageFormat.YUV_420_888:
1144dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                    compressedData = ByteBuffer.allocateDirect(3 * resultImage.width
1154dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                            * resultImage.height);
1164dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin
1174dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                    // If Orientation is UNKNOWN, treat input image orientation
1184dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                    // as CLOCKWISE_0.
1194dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                    numBytes = compressJpegFromYUV420Image(
1204dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                            img.proxy, compressedData, DEFAULT_JPEG_COMPRESSION_QUALITY,
121ec604214008248f7858b3a8b66d70919947399a9I-Jong Lin                            inputImage.orientation.getDegrees());
1224dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin
1234dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                    if (numBytes < 0) {
1244dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                        throw new RuntimeException("Error compressing jpeg.");
1254dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                    }
1264dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                    compressedData.limit(numBytes);
1274dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                    break;
1284dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                default:
1294dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                    throw new IllegalArgumentException(
1304dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                            "Unsupported input image format for TaskCompressImageToJpeg");
1314dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            }
1324dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin
1334dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            writeOut = new byte[numBytes];
1344dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            compressedData.get(writeOut);
1354dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            compressedData.rewind();
1364dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin        } finally {
1374dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            // Release the image now that you have a usable copy in local memory
1384dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            // Or you failed to process
1394dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin            mImageTaskManager.releaseSemaphoreReference(img, mExecutor);
14030ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin        }
141ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin
1424dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin        onJpegEncodeDone(mId, inputImage, resultImage, writeOut,
1434dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin                TaskInfo.Destination.FINAL_IMAGE);
1445aa7eb2930b813c97f3754d93fe7fa978651887bSenpo Hu
145a1ea88071be696ea8dee99c7bdb4007128ed60efI-Jong Lin        // TODO: the app actually crashes here on a race condition:
146a1ea88071be696ea8dee99c7bdb4007128ed60efI-Jong Lin        // TaskCompressImageToJpeg might complete before
147a1ea88071be696ea8dee99c7bdb4007128ed60efI-Jong Lin        // TaskConvertImageToRGBPreview.
1485aa7eb2930b813c97f3754d93fe7fa978651887bSenpo Hu        mSession.saveAndFinish(writeOut, resultImage.width, resultImage.height,
1495aa7eb2930b813c97f3754d93fe7fa978651887bSenpo Hu                resultImage.orientation.getDegrees(), createExif(resultImage),
1505aa7eb2930b813c97f3754d93fe7fa978651887bSenpo Hu                new MediaSaver.OnMediaSavedListener() {
1515aa7eb2930b813c97f3754d93fe7fa978651887bSenpo Hu                    @Override
1525aa7eb2930b813c97f3754d93fe7fa978651887bSenpo Hu                    public void onMediaSaved(Uri uri) {
15330ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin                        onUriResolved(mId, inputImage, resultImage, uri,
15430ef96534cc65c0ba4665f6da2fc36e879edf196I-Jong Lin                                TaskInfo.Destination.FINAL_IMAGE);
1555aa7eb2930b813c97f3754d93fe7fa978651887bSenpo Hu                    }
1565aa7eb2930b813c97f3754d93fe7fa978651887bSenpo Hu                });
157ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin    }
15830ccdac56450e5b1927e14a6eede2b86a30c42ebI-Jong Lin
1594dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin    /**
1604dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin     * Wraps a possible log message to be overridden for testability purposes.
161ec604214008248f7858b3a8b66d70919947399a9I-Jong Lin     *
1624dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin     * @param message
1634dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin     */
1644dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin    protected void logWrapper(String message) {
1654dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin        // Do nothing.
1664dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin    }
1674dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin
1684dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin    /**
1694dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin     * Wraps EXIF Interface for JPEG Metadata creation. Can be overridden for
1704dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin     * testing
171ec604214008248f7858b3a8b66d70919947399a9I-Jong Lin     *
1724dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin     * @param image Metadata for a jpeg image to create EXIF Interface
1734dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin     * @return the created Exif Interface
1744dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin     */
1754dc301a073dab22b9bc12e0b846530d3a80bf8f7I-Jong Lin    protected ExifInterface createExif(TaskImage image) {
1763830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling        ExifInterface exif = new ExifInterface();
1773830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling        exif.setTag(exif.buildTag(ExifInterface.TAG_PIXEL_X_DIMENSION, image.width));
1783830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling        exif.setTag(exif.buildTag(ExifInterface.TAG_PIXEL_Y_DIMENSION, image.height));
1793830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling        exif.setTag(exif.buildTag(ExifInterface.TAG_ORIENTATION,
1803830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling                ExifInterface.getOrientationValueForRotation(image.orientation.getDegrees())));
1813830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling        return exif;
1823830d419691ef865f01b362fee9618bac2aa8888Sascha Haeberling    }
18309d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling
18409d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling    /**
18509d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling     * @param originalWidth the width of the original image captured from the
18609d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling     *            camera
18709d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling     * @param originalHeight the height of the original image captured from the
18809d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling     *            camera
18909d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling     * @param orientation the rotation to apply, in degrees.
19009d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling     * @return The size of the final rotated image
19109d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling     */
19209d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling    private Size getImageSizeForOrientation(int originalWidth, int originalHeight,
19309d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling            DeviceOrientation orientation) {
19409d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling        if (orientation == DeviceOrientation.CLOCKWISE_0
19509d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling                || orientation == DeviceOrientation.CLOCKWISE_180) {
19609d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling            return new Size(originalWidth, originalHeight);
19709d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling        } else if (orientation == DeviceOrientation.CLOCKWISE_90
19809d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling                || orientation == DeviceOrientation.CLOCKWISE_270) {
19909d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling            return new Size(originalHeight, originalWidth);
20009d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling        } else {
20109d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling            // Unsupported orientation. Get rid of this once UNKNOWN is gone.
20209d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling            return new Size(originalWidth, originalHeight);
20309d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling        }
20409d1f4473af1494572d77949ad87b95e21028135Sascha Haeberling    }
205ed68932f91b4b4ad6766e4e38732deb8be772426I-Jong Lin}
206