summaryrefslogtreecommitdiff
path: root/src/com/android/camera/processing/imagebackend/TaskJpegEncode.java
blob: b1f1fbf19d7528314943df3df8556f61eb6bd2d5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.camera.processing.imagebackend;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.Rect;
import android.graphics.YuvImage;
import android.net.Uri;

import com.android.camera.debug.Log;
import com.android.camera.one.v2.camera2proxy.ImageProxy;
import com.android.camera.session.CaptureSession;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.concurrent.Executor;

/**
 * TaskJpegEncode are the base class of tasks that wish to do JPEG
 * encoding/decoding. Various helper functions are held in this class.
 */
public abstract class TaskJpegEncode extends TaskImageContainer {

    protected final static Log.Tag TAG = new Log.Tag("TaskJpegEnc");

    /**
     * Constructor to use for NOT passing the image reference forward.
     *
     * @param otherTask Parent task that is spawning this task
     * @param processingPriority Preferred processing priority for this task
     */
    public TaskJpegEncode(TaskImageContainer otherTask, ProcessingPriority processingPriority) {
        super(otherTask, processingPriority);
    }

    /**
     * Constructor to use for initial task definition or complex shared state
     * sharing.
     *
     * @param image Image reference that is required for computation
     * @param executor Executor to avoid thread control leakage
     * @param imageTaskManager ImageBackend associated with
     * @param preferredLane Preferred processing priority for this task
     * @param captureSession Session associated for UI handling
     */
    public TaskJpegEncode(ImageToProcess image, Executor executor,
            ImageTaskManager imageTaskManager,
            TaskImageContainer.ProcessingPriority preferredLane, CaptureSession captureSession) {
        super(image, executor, imageTaskManager, preferredLane, captureSession);
    }

    /**
     * Converts the YUV420_888 Image into a packed NV21 of a single byte array,
     * suitable for JPEG compression by the method convertNv21toJpeg. This
     * version will allocate its own byte buffer memory.
     *
     * @param img image to be converted
     * @return byte array of NV21 packed image
     */
    public byte[] convertYUV420ImageToPackedNV21(ImageProxy img) {
        final List<ImageProxy.Plane> planeList = img.getPlanes();

        ByteBuffer y_buffer = planeList.get(0).getBuffer();
        ByteBuffer u_buffer = planeList.get(1).getBuffer();
        ByteBuffer v_buffer = planeList.get(2).getBuffer();
        byte[] dataCopy = new byte[y_buffer.capacity() + u_buffer.capacity() + v_buffer.capacity()];

        return convertYUV420ImageToPackedNV21(img, dataCopy);
    }

    /**
     * Converts the YUV420_888 Image into a packed NV21 of a single byte array,
     * suitable for JPEG compression by the method convertNv21toJpeg. Creates a
     * memory block with the y component at the head and interleaves the u,v
     * components following the y component. Caller is responsible to allocate a
     * large enough buffer for results.
     *
     * @param img image to be converted
     * @param dataCopy buffer to write NV21 packed image
     * @return byte array of NV21 packed image
     */
    public byte[] convertYUV420ImageToPackedNV21(ImageProxy img, byte[] dataCopy) {
        // Get all the relevant information and then release the image.
        final int w = img.getWidth();
        final int h = img.getHeight();
        final List<ImageProxy.Plane> planeList = img.getPlanes();

        ByteBuffer y_buffer = planeList.get(0).getBuffer();
        ByteBuffer u_buffer = planeList.get(1).getBuffer();
        ByteBuffer v_buffer = planeList.get(2).getBuffer();
        final int color_pixel_stride = planeList.get(1).getPixelStride();
        final int y_size = y_buffer.capacity();
        final int u_size = u_buffer.capacity();
        final int data_offset = w * h;

        for (int i = 0; i < y_size; i++) {
            dataCopy[i] = (byte) (y_buffer.get(i) & 255);
        }

        for (int i = 0; i < u_size / color_pixel_stride; i++) {
            dataCopy[data_offset + 2 * i] = v_buffer.get(i * color_pixel_stride);
            dataCopy[data_offset + 2 * i + 1] = u_buffer.get(i * color_pixel_stride);
        }

        return dataCopy;
    }

    /**
     * Creates a stub shaded image for testing in packed NV21 format.
     *
     * @param dataCopy Buffer to contained shaded test image
     * @param w Width of image
     * @param h Height of Image
     */
    public void stubConvertYUV420ImageToPackedNV21(byte[] dataCopy,
            final int w, final int h) {
        final int y_size = w * h;
        final int data_offset = w * h;

        for (int i = 0; i < y_size; i++) {
            dataCopy[i] = (byte) ((((i % w) * 255) / w) & 255);
            dataCopy[i] = 0;
        }

        for (int i = 0; i < h / 2; i++) {
            for (int j = 0; j < w / 2; j++) {
                int offset = data_offset + w * i + j * 2;
                dataCopy[offset] = (byte) ((255 * i) / (h / 2) & 255);
                dataCopy[offset + 1] = (byte) ((255 * j) / (w / 2) & 255);
            }
        }
    }

    /**
     * Wraps the Android built-in YUV to Jpeg conversion routine. Pass in a
     * valid NV21 image and get back a compressed JPEG buffer. A good default
     * JPEG compression implementation that should be supported on all
     * platforms.
     *
     * @param data_copy byte buffer that contains the NV21 image
     * @param w width of NV21 image
     * @param h height of N21 image
     * @return byte array of compressed JPEG image
     */
    public byte[] convertNv21toJpeg(byte[] data_copy, int w, int h, int[] strides) {
        Log.e(TAG, "TIMER_BEGIN NV21 to Jpeg Conversion.");
        YuvImage yuvImage = new YuvImage(data_copy, ImageFormat.NV21, w, h, strides);

        ByteArrayOutputStream postViewBytes = new ByteArrayOutputStream();

        yuvImage.compressToJpeg(new Rect(0, 0, w, h), 90, postViewBytes);
        try {
            postViewBytes.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }

        Log.e(TAG, "TIMER_END NV21 to Jpeg Conversion.");
        return postViewBytes.toByteArray();
    }

    /**
     * Implement cropping through the decompression and re-compression of the JPEG using
     * the built-in Android bitmap utilities.
     *
     * @param jpegData Compressed Image to be cropped
     * @param crop Crop to be applied
     * @param recompressionQuality Recompression quality value for cropped JPEG Image
     * @return JPEG compressed byte array representing the cropped image
     */
    public byte[] decompressCropAndRecompressJpegData(final byte[] jpegData, Rect crop,
            int recompressionQuality) {
        Bitmap original = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length);

        final Bitmap croppedResult = Bitmap.createBitmap(original, crop.left, crop.top,
                crop.width(), crop.height());;

        ByteArrayOutputStream stream = new ByteArrayOutputStream();

        croppedResult.compress(Bitmap.CompressFormat.JPEG, recompressionQuality, stream);
        return stream.toByteArray();
    }

    /**
     * Wraps the onResultCompressed listener for ease of use.
     *
     * @param id Unique content id
     * @param input Specification of image input size
     * @param result Specification of resultant input size
     * @param data Container for uncompressed data that represents image
     */
    public void onJpegEncodeDone(long id, TaskImage input, TaskImage result, byte[] data,
            TaskInfo.Destination aDestination) {
        TaskInfo job = new TaskInfo(id, input, result, aDestination);
        final ImageProcessorListener listener = mImageTaskManager.getProxyListener();
        listener.onResultCompressed(job, new CompressedPayload(data));
    }

    /**
     * Wraps the onResultUri listener for ease of use.
     *
     * @param id Unique content id
     * @param input Specification of image input size
     * @param result Specification of resultant input size
     * @param imageUri URI of the saved image.
     * @param destination Specifies the purpose of the image artifact
     */
    public void onUriResolved(long id, TaskImage input, TaskImage result, final Uri imageUri,
            TaskInfo.Destination destination) {
        final TaskInfo job = new TaskInfo(id, input, result, destination);
        final ImageProcessorListener listener = mImageTaskManager.getProxyListener();
        listener.onResultUri(job, imageUri);
    }
}