xref: /aosp_15_r20/frameworks/ex/camera2/extensions/jni/JpegEncoder.cpp (revision 51f0e3d5878722200615b7170faeca299e15103d)
1*51f0e3d5SAndroid Build Coastguard Worker /*
2*51f0e3d5SAndroid Build Coastguard Worker  * Copyright (C) 2022 The Android Open Source Project
3*51f0e3d5SAndroid Build Coastguard Worker  *
4*51f0e3d5SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*51f0e3d5SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*51f0e3d5SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*51f0e3d5SAndroid Build Coastguard Worker  *
8*51f0e3d5SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*51f0e3d5SAndroid Build Coastguard Worker  *
10*51f0e3d5SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*51f0e3d5SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*51f0e3d5SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*51f0e3d5SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*51f0e3d5SAndroid Build Coastguard Worker  * limitations under the License.
15*51f0e3d5SAndroid Build Coastguard Worker  */
16*51f0e3d5SAndroid Build Coastguard Worker 
17*51f0e3d5SAndroid Build Coastguard Worker #include <array>
18*51f0e3d5SAndroid Build Coastguard Worker #include <cstring>
19*51f0e3d5SAndroid Build Coastguard Worker #include <cstdio>
20*51f0e3d5SAndroid Build Coastguard Worker #include <inttypes.h>
21*51f0e3d5SAndroid Build Coastguard Worker #include <memory.h>
22*51f0e3d5SAndroid Build Coastguard Worker #include <vector>
23*51f0e3d5SAndroid Build Coastguard Worker #include <iostream>
24*51f0e3d5SAndroid Build Coastguard Worker #include <utils/Log.h>
25*51f0e3d5SAndroid Build Coastguard Worker #include <setjmp.h>
26*51f0e3d5SAndroid Build Coastguard Worker 
27*51f0e3d5SAndroid Build Coastguard Worker #include <android/hardware/camera/device/3.2/types.h>
28*51f0e3d5SAndroid Build Coastguard Worker 
29*51f0e3d5SAndroid Build Coastguard Worker #include "jni.h"
30*51f0e3d5SAndroid Build Coastguard Worker #include <nativehelper/JNIHelp.h>
31*51f0e3d5SAndroid Build Coastguard Worker 
32*51f0e3d5SAndroid Build Coastguard Worker extern "C" {
33*51f0e3d5SAndroid Build Coastguard Worker #include "jpeglib.h"
34*51f0e3d5SAndroid Build Coastguard Worker }
35*51f0e3d5SAndroid Build Coastguard Worker 
36*51f0e3d5SAndroid Build Coastguard Worker using namespace std;
37*51f0e3d5SAndroid Build Coastguard Worker using namespace android;
38*51f0e3d5SAndroid Build Coastguard Worker 
39*51f0e3d5SAndroid Build Coastguard Worker using android::hardware::camera::device::V3_2::CameraBlob;
40*51f0e3d5SAndroid Build Coastguard Worker using android::hardware::camera::device::V3_2::CameraBlobId;
41*51f0e3d5SAndroid Build Coastguard Worker 
42*51f0e3d5SAndroid Build Coastguard Worker class Transform;
43*51f0e3d5SAndroid Build Coastguard Worker struct Plane;
44*51f0e3d5SAndroid Build Coastguard Worker 
sgn(int val)45*51f0e3d5SAndroid Build Coastguard Worker inline int sgn(int val) { return (0 < val) - (val < 0); }
46*51f0e3d5SAndroid Build Coastguard Worker 
min(int a,int b)47*51f0e3d5SAndroid Build Coastguard Worker inline int min(int a, int b) { return a < b ? a : b; }
48*51f0e3d5SAndroid Build Coastguard Worker 
max(int a,int b)49*51f0e3d5SAndroid Build Coastguard Worker inline int max(int a, int b) { return a > b ? a : b; }
50*51f0e3d5SAndroid Build Coastguard Worker 
51*51f0e3d5SAndroid Build Coastguard Worker /**
52*51f0e3d5SAndroid Build Coastguard Worker  * Represents a combined cropping and rotation transformation.
53*51f0e3d5SAndroid Build Coastguard Worker  *
54*51f0e3d5SAndroid Build Coastguard Worker  * The transformation maps the coordinates (mOrigX, mOrigY) and (mOneX, mOneY)
55*51f0e3d5SAndroid Build Coastguard Worker  * in the input image to the origin and (mOutputWidth, mOutputHeight)
56*51f0e3d5SAndroid Build Coastguard Worker  * respectively.
57*51f0e3d5SAndroid Build Coastguard Worker  */
58*51f0e3d5SAndroid Build Coastguard Worker class Transform {
59*51f0e3d5SAndroid Build Coastguard Worker     public:
60*51f0e3d5SAndroid Build Coastguard Worker         Transform(int origX, int origY, int oneX, int oneY);
61*51f0e3d5SAndroid Build Coastguard Worker 
62*51f0e3d5SAndroid Build Coastguard Worker         static Transform forCropFollowedByRotation(int cropLeft, int cropTop,
63*51f0e3d5SAndroid Build Coastguard Worker                 int cropRight, int cropBottom, int rot90);
64*51f0e3d5SAndroid Build Coastguard Worker 
getOutputWidth() const65*51f0e3d5SAndroid Build Coastguard Worker         inline int getOutputWidth() const { return mOutputWidth; }
66*51f0e3d5SAndroid Build Coastguard Worker 
getOutputHeight() const67*51f0e3d5SAndroid Build Coastguard Worker         inline int getOutputHeight() const { return mOutputHeight; }
68*51f0e3d5SAndroid Build Coastguard Worker 
69*51f0e3d5SAndroid Build Coastguard Worker         bool operator==(const Transform& other) const;
70*51f0e3d5SAndroid Build Coastguard Worker 
71*51f0e3d5SAndroid Build Coastguard Worker         /**
72*51f0e3d5SAndroid Build Coastguard Worker          * Transforms the input coordinates.  Coordinates outside the cropped region
73*51f0e3d5SAndroid Build Coastguard Worker          * are clamped to valid values.
74*51f0e3d5SAndroid Build Coastguard Worker          */
75*51f0e3d5SAndroid Build Coastguard Worker         void map(int x, int y, int* outX, int* outY) const;
76*51f0e3d5SAndroid Build Coastguard Worker 
77*51f0e3d5SAndroid Build Coastguard Worker     private:
78*51f0e3d5SAndroid Build Coastguard Worker         int mOutputWidth;
79*51f0e3d5SAndroid Build Coastguard Worker         int mOutputHeight;
80*51f0e3d5SAndroid Build Coastguard Worker 
81*51f0e3d5SAndroid Build Coastguard Worker         // The coordinates of the point to map the origin to.
82*51f0e3d5SAndroid Build Coastguard Worker         const int mOrigX, mOrigY;
83*51f0e3d5SAndroid Build Coastguard Worker         // The coordinates of the point to map the point (getOutputWidth(),
84*51f0e3d5SAndroid Build Coastguard Worker         // getOutputHeight()) to.
85*51f0e3d5SAndroid Build Coastguard Worker         const int mOneX, mOneY;
86*51f0e3d5SAndroid Build Coastguard Worker 
87*51f0e3d5SAndroid Build Coastguard Worker         // A matrix for the rotational component.
88*51f0e3d5SAndroid Build Coastguard Worker         int mMat00, mMat01;
89*51f0e3d5SAndroid Build Coastguard Worker         int mMat10, mMat11;
90*51f0e3d5SAndroid Build Coastguard Worker };
91*51f0e3d5SAndroid Build Coastguard Worker 
92*51f0e3d5SAndroid Build Coastguard Worker /**
93*51f0e3d5SAndroid Build Coastguard Worker  * Represents a model for accessing pixel data for a single plane of an image.
94*51f0e3d5SAndroid Build Coastguard Worker  * Note that the actual data is not owned by this class, and the underlying
95*51f0e3d5SAndroid Build Coastguard Worker  * data does not need to be stored in separate planes.
96*51f0e3d5SAndroid Build Coastguard Worker  */
97*51f0e3d5SAndroid Build Coastguard Worker struct Plane {
98*51f0e3d5SAndroid Build Coastguard Worker     // The dimensions of this plane of the image
99*51f0e3d5SAndroid Build Coastguard Worker     int width;
100*51f0e3d5SAndroid Build Coastguard Worker     int height;
101*51f0e3d5SAndroid Build Coastguard Worker 
102*51f0e3d5SAndroid Build Coastguard Worker     // A pointer to raw pixel data
103*51f0e3d5SAndroid Build Coastguard Worker     const unsigned char* data;
104*51f0e3d5SAndroid Build Coastguard Worker     // The difference in address between consecutive pixels in the same row
105*51f0e3d5SAndroid Build Coastguard Worker     int pixelStride;
106*51f0e3d5SAndroid Build Coastguard Worker     // The difference in address between the start of consecutive rows
107*51f0e3d5SAndroid Build Coastguard Worker     int rowStride;
108*51f0e3d5SAndroid Build Coastguard Worker };
109*51f0e3d5SAndroid Build Coastguard Worker 
110*51f0e3d5SAndroid Build Coastguard Worker /**
111*51f0e3d5SAndroid Build Coastguard Worker  * Provides an interface for simultaneously reading a certain number of rows of
112*51f0e3d5SAndroid Build Coastguard Worker  * an image plane as contiguous arrays, suitable for use with libjpeg.
113*51f0e3d5SAndroid Build Coastguard Worker  */
114*51f0e3d5SAndroid Build Coastguard Worker template <unsigned int ROWS>
115*51f0e3d5SAndroid Build Coastguard Worker class RowIterator {
116*51f0e3d5SAndroid Build Coastguard Worker     public:
117*51f0e3d5SAndroid Build Coastguard Worker         /**
118*51f0e3d5SAndroid Build Coastguard Worker          * Creates a new RowIterator which will crop and rotate with the given
119*51f0e3d5SAndroid Build Coastguard Worker          * transform.
120*51f0e3d5SAndroid Build Coastguard Worker          *
121*51f0e3d5SAndroid Build Coastguard Worker          * @param plane the plane to iterate over
122*51f0e3d5SAndroid Build Coastguard Worker          * @param transform the transformation to map output values into the
123*51f0e3d5SAndroid Build Coastguard Worker          * coordinate space of the plane
124*51f0e3d5SAndroid Build Coastguard Worker          * @param rowLength the length of the rows returned via LoadAt().  If this is
125*51f0e3d5SAndroid Build Coastguard Worker          * longer than the width of the output (after applying the transform), then
126*51f0e3d5SAndroid Build Coastguard Worker          * the right-most value is repeated.
127*51f0e3d5SAndroid Build Coastguard Worker          */
128*51f0e3d5SAndroid Build Coastguard Worker         inline RowIterator(Plane plane, Transform transform, int rowLength);
129*51f0e3d5SAndroid Build Coastguard Worker 
130*51f0e3d5SAndroid Build Coastguard Worker         /**
131*51f0e3d5SAndroid Build Coastguard Worker          * Returns an array of pointers into consecutive rows of contiguous image
132*51f0e3d5SAndroid Build Coastguard Worker          * data starting at y.  That is, samples within each row are contiguous.
133*51f0e3d5SAndroid Build Coastguard Worker          * However, the individual arrays pointed-to may be separate.
134*51f0e3d5SAndroid Build Coastguard Worker          * When the end of the image is reached, the last row of the image is
135*51f0e3d5SAndroid Build Coastguard Worker          * repeated.
136*51f0e3d5SAndroid Build Coastguard Worker          * The returned pointers are valid until the next call to loadAt().
137*51f0e3d5SAndroid Build Coastguard Worker          */
138*51f0e3d5SAndroid Build Coastguard Worker         inline const std::array<unsigned char*, ROWS> loadAt(int baseY);
139*51f0e3d5SAndroid Build Coastguard Worker 
140*51f0e3d5SAndroid Build Coastguard Worker     private:
141*51f0e3d5SAndroid Build Coastguard Worker         Plane mPlane;
142*51f0e3d5SAndroid Build Coastguard Worker         Transform mTransform;
143*51f0e3d5SAndroid Build Coastguard Worker         // The length of a row, with padding to the next multiple of 64.
144*51f0e3d5SAndroid Build Coastguard Worker         int mPaddedRowLength;
145*51f0e3d5SAndroid Build Coastguard Worker         std::vector<unsigned char> mBuffer;
146*51f0e3d5SAndroid Build Coastguard Worker };
147*51f0e3d5SAndroid Build Coastguard Worker 
148*51f0e3d5SAndroid Build Coastguard Worker template <unsigned int ROWS>
RowIterator(Plane plane,Transform transform,int rowLength)149*51f0e3d5SAndroid Build Coastguard Worker RowIterator<ROWS>::RowIterator(Plane plane, Transform transform,
150*51f0e3d5SAndroid Build Coastguard Worker                                          int rowLength)
151*51f0e3d5SAndroid Build Coastguard Worker         : mPlane(plane), mTransform(transform) {
152*51f0e3d5SAndroid Build Coastguard Worker     mPaddedRowLength = rowLength;
153*51f0e3d5SAndroid Build Coastguard Worker     mBuffer = std::vector<unsigned char>(rowLength * ROWS);
154*51f0e3d5SAndroid Build Coastguard Worker }
155*51f0e3d5SAndroid Build Coastguard Worker 
156*51f0e3d5SAndroid Build Coastguard Worker template <unsigned int ROWS>
loadAt(int baseY)157*51f0e3d5SAndroid Build Coastguard Worker const std::array<unsigned char*, ROWS> RowIterator<ROWS>::loadAt(int baseY) {
158*51f0e3d5SAndroid Build Coastguard Worker     std::array<unsigned char*, ROWS> bufPtrs;
159*51f0e3d5SAndroid Build Coastguard Worker     for (unsigned int i = 0; i < ROWS; i++) {
160*51f0e3d5SAndroid Build Coastguard Worker         bufPtrs[i] = &mBuffer[mPaddedRowLength * i];
161*51f0e3d5SAndroid Build Coastguard Worker     }
162*51f0e3d5SAndroid Build Coastguard Worker 
163*51f0e3d5SAndroid Build Coastguard Worker     if (mPlane.width == 0 || mPlane.height == 0) {
164*51f0e3d5SAndroid Build Coastguard Worker         return bufPtrs;
165*51f0e3d5SAndroid Build Coastguard Worker     }
166*51f0e3d5SAndroid Build Coastguard Worker 
167*51f0e3d5SAndroid Build Coastguard Worker     for (unsigned int i = 0; i < ROWS; i++) {
168*51f0e3d5SAndroid Build Coastguard Worker         int y = i + baseY;
169*51f0e3d5SAndroid Build Coastguard Worker         y = min(y, mTransform.getOutputHeight() - 1);
170*51f0e3d5SAndroid Build Coastguard Worker 
171*51f0e3d5SAndroid Build Coastguard Worker         int output_width = mPaddedRowLength;
172*51f0e3d5SAndroid Build Coastguard Worker         output_width = min(output_width, mTransform.getOutputWidth());
173*51f0e3d5SAndroid Build Coastguard Worker         output_width = min(output_width, mPlane.width);
174*51f0e3d5SAndroid Build Coastguard Worker 
175*51f0e3d5SAndroid Build Coastguard Worker         // Each row in the output image will be copied into buf_ by gathering pixels
176*51f0e3d5SAndroid Build Coastguard Worker         // along an axis-aligned line in the plane.
177*51f0e3d5SAndroid Build Coastguard Worker         // The line is defined by (startX, startY) -> (endX, endY), computed via the
178*51f0e3d5SAndroid Build Coastguard Worker         // current Transform.
179*51f0e3d5SAndroid Build Coastguard Worker         int startX;
180*51f0e3d5SAndroid Build Coastguard Worker         int startY;
181*51f0e3d5SAndroid Build Coastguard Worker         mTransform.map(0, y, &startX, &startY);
182*51f0e3d5SAndroid Build Coastguard Worker 
183*51f0e3d5SAndroid Build Coastguard Worker         int endX;
184*51f0e3d5SAndroid Build Coastguard Worker         int endY;
185*51f0e3d5SAndroid Build Coastguard Worker         mTransform.map(output_width - 1, y, &endX, &endY);
186*51f0e3d5SAndroid Build Coastguard Worker 
187*51f0e3d5SAndroid Build Coastguard Worker         // Clamp (startX, startY) and (endX, endY) to the valid bounds of the plane.
188*51f0e3d5SAndroid Build Coastguard Worker         startX = min(startX, mPlane.width - 1);
189*51f0e3d5SAndroid Build Coastguard Worker         startY = min(startY, mPlane.height - 1);
190*51f0e3d5SAndroid Build Coastguard Worker         endX = min(endX, mPlane.width - 1);
191*51f0e3d5SAndroid Build Coastguard Worker         endY = min(endY, mPlane.height - 1);
192*51f0e3d5SAndroid Build Coastguard Worker         startX = max(startX, 0);
193*51f0e3d5SAndroid Build Coastguard Worker         startY = max(startY, 0);
194*51f0e3d5SAndroid Build Coastguard Worker         endX = max(endX, 0);
195*51f0e3d5SAndroid Build Coastguard Worker         endY = max(endY, 0);
196*51f0e3d5SAndroid Build Coastguard Worker 
197*51f0e3d5SAndroid Build Coastguard Worker         // To reduce work inside the copy-loop, precompute the start, end, and
198*51f0e3d5SAndroid Build Coastguard Worker         // stride relating the values to be gathered from mPlane into buf
199*51f0e3d5SAndroid Build Coastguard Worker         // for this particular scan-line.
200*51f0e3d5SAndroid Build Coastguard Worker         int dx = sgn(endX - startX);
201*51f0e3d5SAndroid Build Coastguard Worker         int dy = sgn(endY - startY);
202*51f0e3d5SAndroid Build Coastguard Worker         if (!(dx == 0 || dy == 0)) {
203*51f0e3d5SAndroid Build Coastguard Worker             ALOGE("%s: Unexpected bounds: %dx%d %dx%d!", __FUNCTION__, startX, endX, startY, endY);
204*51f0e3d5SAndroid Build Coastguard Worker             return bufPtrs;
205*51f0e3d5SAndroid Build Coastguard Worker         }
206*51f0e3d5SAndroid Build Coastguard Worker 
207*51f0e3d5SAndroid Build Coastguard Worker         // The index into mPlane.data of (startX, startY)
208*51f0e3d5SAndroid Build Coastguard Worker         int plane_start = startX * mPlane.pixelStride + startY * mPlane.rowStride;
209*51f0e3d5SAndroid Build Coastguard Worker         // The index into mPlane.data of (endX, endY)
210*51f0e3d5SAndroid Build Coastguard Worker         int plane_end = endX * mPlane.pixelStride + endY * mPlane.rowStride;
211*51f0e3d5SAndroid Build Coastguard Worker         // The stride, in terms of indices in plane_data, required to enumerate the
212*51f0e3d5SAndroid Build Coastguard Worker         // samples between the start and end points.
213*51f0e3d5SAndroid Build Coastguard Worker         int stride = dx * mPlane.pixelStride + dy * mPlane.rowStride;
214*51f0e3d5SAndroid Build Coastguard Worker         // In the degenerate-case of a 1x1 plane, startX and endX are equal, so
215*51f0e3d5SAndroid Build Coastguard Worker         // stride would be 0, resulting in an infinite-loop.  To avoid this case,
216*51f0e3d5SAndroid Build Coastguard Worker         // use a stride of at-least 1.
217*51f0e3d5SAndroid Build Coastguard Worker         if (stride == 0) {
218*51f0e3d5SAndroid Build Coastguard Worker             stride = 1;
219*51f0e3d5SAndroid Build Coastguard Worker         }
220*51f0e3d5SAndroid Build Coastguard Worker 
221*51f0e3d5SAndroid Build Coastguard Worker         int outX = 0;
222*51f0e3d5SAndroid Build Coastguard Worker         for (int idx = plane_start; idx >= min(plane_start, plane_end) &&
223*51f0e3d5SAndroid Build Coastguard Worker                 idx <= max(plane_start, plane_end); idx += stride) {
224*51f0e3d5SAndroid Build Coastguard Worker             bufPtrs[i][outX] = mPlane.data[idx];
225*51f0e3d5SAndroid Build Coastguard Worker             outX++;
226*51f0e3d5SAndroid Build Coastguard Worker         }
227*51f0e3d5SAndroid Build Coastguard Worker 
228*51f0e3d5SAndroid Build Coastguard Worker         // Fill the remaining right-edge of the buffer by extending the last
229*51f0e3d5SAndroid Build Coastguard Worker         // value.
230*51f0e3d5SAndroid Build Coastguard Worker         unsigned char right_padding_value = bufPtrs[i][outX - 1];
231*51f0e3d5SAndroid Build Coastguard Worker         for (; outX < mPaddedRowLength; outX++) {
232*51f0e3d5SAndroid Build Coastguard Worker             bufPtrs[i][outX] = right_padding_value;
233*51f0e3d5SAndroid Build Coastguard Worker         }
234*51f0e3d5SAndroid Build Coastguard Worker     }
235*51f0e3d5SAndroid Build Coastguard Worker 
236*51f0e3d5SAndroid Build Coastguard Worker     return bufPtrs;
237*51f0e3d5SAndroid Build Coastguard Worker }
238*51f0e3d5SAndroid Build Coastguard Worker 
239*51f0e3d5SAndroid Build Coastguard Worker template <typename T>
safeDelete(T & t)240*51f0e3d5SAndroid Build Coastguard Worker void safeDelete(T& t) {
241*51f0e3d5SAndroid Build Coastguard Worker     delete t;
242*51f0e3d5SAndroid Build Coastguard Worker     t = nullptr;
243*51f0e3d5SAndroid Build Coastguard Worker }
244*51f0e3d5SAndroid Build Coastguard Worker 
245*51f0e3d5SAndroid Build Coastguard Worker template <typename T>
safeDeleteArray(T & t)246*51f0e3d5SAndroid Build Coastguard Worker void safeDeleteArray(T& t) {
247*51f0e3d5SAndroid Build Coastguard Worker     delete[] t;
248*51f0e3d5SAndroid Build Coastguard Worker     t = nullptr;
249*51f0e3d5SAndroid Build Coastguard Worker }
250*51f0e3d5SAndroid Build Coastguard Worker 
Transform(int origX,int origY,int oneX,int oneY)251*51f0e3d5SAndroid Build Coastguard Worker Transform::Transform(int origX, int origY, int oneX, int oneY)
252*51f0e3d5SAndroid Build Coastguard Worker     : mOrigX(origX), mOrigY(origY), mOneX(oneX), mOneY(oneY) {
253*51f0e3d5SAndroid Build Coastguard Worker     if (origX == oneX || origY == oneY) {
254*51f0e3d5SAndroid Build Coastguard Worker         // Handle the degenerate case of cropping to a 0x0 rectangle.
255*51f0e3d5SAndroid Build Coastguard Worker         mMat00 = 0;
256*51f0e3d5SAndroid Build Coastguard Worker         mMat01 = 0;
257*51f0e3d5SAndroid Build Coastguard Worker         mMat10 = 0;
258*51f0e3d5SAndroid Build Coastguard Worker         mMat11 = 0;
259*51f0e3d5SAndroid Build Coastguard Worker         return;
260*51f0e3d5SAndroid Build Coastguard Worker     }
261*51f0e3d5SAndroid Build Coastguard Worker 
262*51f0e3d5SAndroid Build Coastguard Worker     if (oneX > origX && oneY > origY) {
263*51f0e3d5SAndroid Build Coastguard Worker         // 0-degree rotation
264*51f0e3d5SAndroid Build Coastguard Worker         mMat00 = 1;
265*51f0e3d5SAndroid Build Coastguard Worker         mMat01 = 0;
266*51f0e3d5SAndroid Build Coastguard Worker         mMat10 = 0;
267*51f0e3d5SAndroid Build Coastguard Worker         mMat11 = 1;
268*51f0e3d5SAndroid Build Coastguard Worker         mOutputWidth = abs(oneX - origX);
269*51f0e3d5SAndroid Build Coastguard Worker         mOutputHeight = abs(oneY - origY);
270*51f0e3d5SAndroid Build Coastguard Worker     } else if (oneX < origX && oneY > origY) {
271*51f0e3d5SAndroid Build Coastguard Worker         // 90-degree CCW rotation
272*51f0e3d5SAndroid Build Coastguard Worker         mMat00 = 0;
273*51f0e3d5SAndroid Build Coastguard Worker         mMat01 = -1;
274*51f0e3d5SAndroid Build Coastguard Worker         mMat10 = 1;
275*51f0e3d5SAndroid Build Coastguard Worker         mMat11 = 0;
276*51f0e3d5SAndroid Build Coastguard Worker         mOutputWidth = abs(oneY - origY);
277*51f0e3d5SAndroid Build Coastguard Worker         mOutputHeight = abs(oneX - origX);
278*51f0e3d5SAndroid Build Coastguard Worker     } else if (oneX > origX && oneY < origY) {
279*51f0e3d5SAndroid Build Coastguard Worker         // 270-degree CCW rotation
280*51f0e3d5SAndroid Build Coastguard Worker         mMat00 = 0;
281*51f0e3d5SAndroid Build Coastguard Worker         mMat01 = 1;
282*51f0e3d5SAndroid Build Coastguard Worker         mMat10 = -1;
283*51f0e3d5SAndroid Build Coastguard Worker         mMat11 = 0;
284*51f0e3d5SAndroid Build Coastguard Worker         mOutputWidth = abs(oneY - origY);
285*51f0e3d5SAndroid Build Coastguard Worker         mOutputHeight = abs(oneX - origX);;
286*51f0e3d5SAndroid Build Coastguard Worker     } else if (oneX < origX && oneY < origY) {
287*51f0e3d5SAndroid Build Coastguard Worker         // 180-degree CCW rotation
288*51f0e3d5SAndroid Build Coastguard Worker         mMat00 = -1;
289*51f0e3d5SAndroid Build Coastguard Worker         mMat01 = 0;
290*51f0e3d5SAndroid Build Coastguard Worker         mMat10 = 0;
291*51f0e3d5SAndroid Build Coastguard Worker         mMat11 = -1;
292*51f0e3d5SAndroid Build Coastguard Worker         mOutputWidth = abs(oneX - origX);
293*51f0e3d5SAndroid Build Coastguard Worker         mOutputHeight = abs(oneY - origY);
294*51f0e3d5SAndroid Build Coastguard Worker     }
295*51f0e3d5SAndroid Build Coastguard Worker }
296*51f0e3d5SAndroid Build Coastguard Worker 
forCropFollowedByRotation(int cropLeft,int cropTop,int cropRight,int cropBottom,int rot90)297*51f0e3d5SAndroid Build Coastguard Worker Transform Transform::forCropFollowedByRotation(int cropLeft, int cropTop, int cropRight,
298*51f0e3d5SAndroid Build Coastguard Worker         int cropBottom, int rot90) {
299*51f0e3d5SAndroid Build Coastguard Worker     // The input crop-region excludes cropRight and cropBottom, so transform the
300*51f0e3d5SAndroid Build Coastguard Worker     // crop rect such that it defines the entire valid region of pixels
301*51f0e3d5SAndroid Build Coastguard Worker     // inclusively.
302*51f0e3d5SAndroid Build Coastguard Worker     cropRight -= 1;
303*51f0e3d5SAndroid Build Coastguard Worker     cropBottom -= 1;
304*51f0e3d5SAndroid Build Coastguard Worker 
305*51f0e3d5SAndroid Build Coastguard Worker     int cropXLow = min(cropLeft, cropRight);
306*51f0e3d5SAndroid Build Coastguard Worker     int cropYLow = min(cropTop, cropBottom);
307*51f0e3d5SAndroid Build Coastguard Worker     int cropXHigh = max(cropLeft, cropRight);
308*51f0e3d5SAndroid Build Coastguard Worker     int cropYHigh = max(cropTop, cropBottom);
309*51f0e3d5SAndroid Build Coastguard Worker     rot90 %= 4;
310*51f0e3d5SAndroid Build Coastguard Worker     if (rot90 == 0) {
311*51f0e3d5SAndroid Build Coastguard Worker         return Transform(cropXLow, cropYLow, cropXHigh + 1, cropYHigh + 1);
312*51f0e3d5SAndroid Build Coastguard Worker     } else if (rot90 == 1) {
313*51f0e3d5SAndroid Build Coastguard Worker         return Transform(cropXHigh, cropYLow, cropXLow - 1, cropYHigh + 1);
314*51f0e3d5SAndroid Build Coastguard Worker     } else if (rot90 == 2) {
315*51f0e3d5SAndroid Build Coastguard Worker         return Transform(cropXHigh, cropYHigh, cropXLow - 1, cropYLow - 1);
316*51f0e3d5SAndroid Build Coastguard Worker     } else if (rot90 == 3) {
317*51f0e3d5SAndroid Build Coastguard Worker         return Transform(cropXLow, cropYHigh, cropXHigh + 1, cropYLow - 1);
318*51f0e3d5SAndroid Build Coastguard Worker     }
319*51f0e3d5SAndroid Build Coastguard Worker     // Impossible case.
320*51f0e3d5SAndroid Build Coastguard Worker     return Transform(cropXLow, cropYLow, cropXHigh + 1, cropYHigh + 1);
321*51f0e3d5SAndroid Build Coastguard Worker }
322*51f0e3d5SAndroid Build Coastguard Worker 
operator ==(const Transform & other) const323*51f0e3d5SAndroid Build Coastguard Worker bool Transform::operator==(const Transform& other) const {
324*51f0e3d5SAndroid Build Coastguard Worker     return other.mOrigX == mOrigX &&  //
325*51f0e3d5SAndroid Build Coastguard Worker            other.mOrigY == mOrigY &&  //
326*51f0e3d5SAndroid Build Coastguard Worker            other.mOneX == mOneX &&    //
327*51f0e3d5SAndroid Build Coastguard Worker            other.mOneY == mOneY;
328*51f0e3d5SAndroid Build Coastguard Worker }
329*51f0e3d5SAndroid Build Coastguard Worker 
330*51f0e3d5SAndroid Build Coastguard Worker /**
331*51f0e3d5SAndroid Build Coastguard Worker  * Transforms the input coordinates.  Coordinates outside the cropped region
332*51f0e3d5SAndroid Build Coastguard Worker  * are clamped to valid values.
333*51f0e3d5SAndroid Build Coastguard Worker  */
map(int x,int y,int * outX,int * outY) const334*51f0e3d5SAndroid Build Coastguard Worker void Transform::map(int x, int y, int* outX, int* outY) const {
335*51f0e3d5SAndroid Build Coastguard Worker     x = max(x, 0);
336*51f0e3d5SAndroid Build Coastguard Worker     y = max(y, 0);
337*51f0e3d5SAndroid Build Coastguard Worker     x = min(x, getOutputWidth() - 1);
338*51f0e3d5SAndroid Build Coastguard Worker     y = min(y, getOutputHeight() - 1);
339*51f0e3d5SAndroid Build Coastguard Worker     *outX = x * mMat00 + y * mMat01 + mOrigX;
340*51f0e3d5SAndroid Build Coastguard Worker     *outY = x * mMat10 + y * mMat11 + mOrigY;
341*51f0e3d5SAndroid Build Coastguard Worker }
342*51f0e3d5SAndroid Build Coastguard Worker 
compress(int img_width,int img_height,RowIterator<16> & y_row_generator,RowIterator<8> & cb_row_generator,RowIterator<8> & cr_row_generator,unsigned char * out_buf,size_t out_buf_capacity,std::function<void (size_t)> flush,int quality)343*51f0e3d5SAndroid Build Coastguard Worker int compress(int img_width, int img_height, RowIterator<16>& y_row_generator,
344*51f0e3d5SAndroid Build Coastguard Worker         RowIterator<8>& cb_row_generator, RowIterator<8>& cr_row_generator,
345*51f0e3d5SAndroid Build Coastguard Worker         unsigned char* out_buf, size_t out_buf_capacity, std::function<void(size_t)> flush,
346*51f0e3d5SAndroid Build Coastguard Worker         int quality) {
347*51f0e3d5SAndroid Build Coastguard Worker     // libjpeg requires the use of setjmp/longjmp to recover from errors.  Since
348*51f0e3d5SAndroid Build Coastguard Worker     // this doesn't play well with RAII, we must use pointers and manually call
349*51f0e3d5SAndroid Build Coastguard Worker     // delete. See POSIX documentation for longjmp() for details on why the
350*51f0e3d5SAndroid Build Coastguard Worker     // volatile keyword is necessary.
351*51f0e3d5SAndroid Build Coastguard Worker     volatile jpeg_compress_struct cinfov;
352*51f0e3d5SAndroid Build Coastguard Worker 
353*51f0e3d5SAndroid Build Coastguard Worker     jpeg_compress_struct& cinfo =
354*51f0e3d5SAndroid Build Coastguard Worker             *const_cast<struct jpeg_compress_struct*>(&cinfov);
355*51f0e3d5SAndroid Build Coastguard Worker 
356*51f0e3d5SAndroid Build Coastguard Worker     JSAMPROW* volatile yArr = nullptr;
357*51f0e3d5SAndroid Build Coastguard Worker     JSAMPROW* volatile cbArr = nullptr;
358*51f0e3d5SAndroid Build Coastguard Worker     JSAMPROW* volatile crArr = nullptr;
359*51f0e3d5SAndroid Build Coastguard Worker 
360*51f0e3d5SAndroid Build Coastguard Worker     JSAMPARRAY imgArr[3];
361*51f0e3d5SAndroid Build Coastguard Worker 
362*51f0e3d5SAndroid Build Coastguard Worker     // Error handling
363*51f0e3d5SAndroid Build Coastguard Worker 
364*51f0e3d5SAndroid Build Coastguard Worker     struct my_error_mgr {
365*51f0e3d5SAndroid Build Coastguard Worker         struct jpeg_error_mgr pub;
366*51f0e3d5SAndroid Build Coastguard Worker         jmp_buf setjmp_buffer;
367*51f0e3d5SAndroid Build Coastguard Worker     } err;
368*51f0e3d5SAndroid Build Coastguard Worker 
369*51f0e3d5SAndroid Build Coastguard Worker     cinfo.err = jpeg_std_error(&err.pub);
370*51f0e3d5SAndroid Build Coastguard Worker 
371*51f0e3d5SAndroid Build Coastguard Worker     // Default error_exit will call exit(), so override
372*51f0e3d5SAndroid Build Coastguard Worker     // to return control via setjmp/longjmp.
373*51f0e3d5SAndroid Build Coastguard Worker     err.pub.error_exit = [](j_common_ptr cinfo) {
374*51f0e3d5SAndroid Build Coastguard Worker         my_error_mgr* myerr = reinterpret_cast<my_error_mgr*>(cinfo->err);
375*51f0e3d5SAndroid Build Coastguard Worker 
376*51f0e3d5SAndroid Build Coastguard Worker         (*cinfo->err->output_message)(cinfo);
377*51f0e3d5SAndroid Build Coastguard Worker 
378*51f0e3d5SAndroid Build Coastguard Worker         // Return control to the setjmp point (see call to setjmp()).
379*51f0e3d5SAndroid Build Coastguard Worker         longjmp(myerr->setjmp_buffer, 1);
380*51f0e3d5SAndroid Build Coastguard Worker     };
381*51f0e3d5SAndroid Build Coastguard Worker 
382*51f0e3d5SAndroid Build Coastguard Worker     cinfo.err = (struct jpeg_error_mgr*)&err;
383*51f0e3d5SAndroid Build Coastguard Worker 
384*51f0e3d5SAndroid Build Coastguard Worker     // Set the setjmp point to return to in case of error.
385*51f0e3d5SAndroid Build Coastguard Worker     if (setjmp(err.setjmp_buffer)) {
386*51f0e3d5SAndroid Build Coastguard Worker         // If libjpeg hits an error, control will jump to this point (see call to
387*51f0e3d5SAndroid Build Coastguard Worker         // longjmp()).
388*51f0e3d5SAndroid Build Coastguard Worker         jpeg_destroy_compress(&cinfo);
389*51f0e3d5SAndroid Build Coastguard Worker 
390*51f0e3d5SAndroid Build Coastguard Worker         safeDeleteArray(yArr);
391*51f0e3d5SAndroid Build Coastguard Worker         safeDeleteArray(cbArr);
392*51f0e3d5SAndroid Build Coastguard Worker         safeDeleteArray(crArr);
393*51f0e3d5SAndroid Build Coastguard Worker 
394*51f0e3d5SAndroid Build Coastguard Worker         return -1;
395*51f0e3d5SAndroid Build Coastguard Worker     }
396*51f0e3d5SAndroid Build Coastguard Worker 
397*51f0e3d5SAndroid Build Coastguard Worker     // Create jpeg compression context
398*51f0e3d5SAndroid Build Coastguard Worker     jpeg_create_compress(&cinfo);
399*51f0e3d5SAndroid Build Coastguard Worker 
400*51f0e3d5SAndroid Build Coastguard Worker     // Stores data needed by our c-style callbacks into libjpeg
401*51f0e3d5SAndroid Build Coastguard Worker     struct ClientData {
402*51f0e3d5SAndroid Build Coastguard Worker         unsigned char* out_buf;
403*51f0e3d5SAndroid Build Coastguard Worker         size_t out_buf_capacity;
404*51f0e3d5SAndroid Build Coastguard Worker         std::function<void(size_t)> flush;
405*51f0e3d5SAndroid Build Coastguard Worker         int totalOutputBytes;
406*51f0e3d5SAndroid Build Coastguard Worker     } clientData{out_buf, out_buf_capacity, flush, 0};
407*51f0e3d5SAndroid Build Coastguard Worker 
408*51f0e3d5SAndroid Build Coastguard Worker     cinfo.client_data = &clientData;
409*51f0e3d5SAndroid Build Coastguard Worker 
410*51f0e3d5SAndroid Build Coastguard Worker     // Initialize destination manager
411*51f0e3d5SAndroid Build Coastguard Worker     jpeg_destination_mgr dest;
412*51f0e3d5SAndroid Build Coastguard Worker 
413*51f0e3d5SAndroid Build Coastguard Worker     dest.init_destination = [](j_compress_ptr cinfo) {
414*51f0e3d5SAndroid Build Coastguard Worker         ClientData& cdata = *reinterpret_cast<ClientData*>(cinfo->client_data);
415*51f0e3d5SAndroid Build Coastguard Worker 
416*51f0e3d5SAndroid Build Coastguard Worker         cinfo->dest->next_output_byte = cdata.out_buf;
417*51f0e3d5SAndroid Build Coastguard Worker         cinfo->dest->free_in_buffer = cdata.out_buf_capacity;
418*51f0e3d5SAndroid Build Coastguard Worker     };
419*51f0e3d5SAndroid Build Coastguard Worker 
420*51f0e3d5SAndroid Build Coastguard Worker     dest.empty_output_buffer = [](j_compress_ptr cinfo) -> boolean {
421*51f0e3d5SAndroid Build Coastguard Worker         ClientData& cdata = *reinterpret_cast<ClientData*>(cinfo->client_data);
422*51f0e3d5SAndroid Build Coastguard Worker 
423*51f0e3d5SAndroid Build Coastguard Worker         size_t numBytesInBuffer = cdata.out_buf_capacity;
424*51f0e3d5SAndroid Build Coastguard Worker         cdata.flush(numBytesInBuffer);
425*51f0e3d5SAndroid Build Coastguard Worker         cdata.totalOutputBytes += numBytesInBuffer;
426*51f0e3d5SAndroid Build Coastguard Worker 
427*51f0e3d5SAndroid Build Coastguard Worker         // Reset the buffer
428*51f0e3d5SAndroid Build Coastguard Worker         cinfo->dest->next_output_byte = cdata.out_buf;
429*51f0e3d5SAndroid Build Coastguard Worker         cinfo->dest->free_in_buffer = cdata.out_buf_capacity;
430*51f0e3d5SAndroid Build Coastguard Worker 
431*51f0e3d5SAndroid Build Coastguard Worker         return true;
432*51f0e3d5SAndroid Build Coastguard Worker     };
433*51f0e3d5SAndroid Build Coastguard Worker 
434*51f0e3d5SAndroid Build Coastguard Worker     dest.term_destination = [](j_compress_ptr cinfo __unused) {
435*51f0e3d5SAndroid Build Coastguard Worker         // do nothing to terminate the output buffer
436*51f0e3d5SAndroid Build Coastguard Worker     };
437*51f0e3d5SAndroid Build Coastguard Worker 
438*51f0e3d5SAndroid Build Coastguard Worker     cinfo.dest = &dest;
439*51f0e3d5SAndroid Build Coastguard Worker 
440*51f0e3d5SAndroid Build Coastguard Worker     // Set jpeg parameters
441*51f0e3d5SAndroid Build Coastguard Worker     cinfo.image_width = img_width;
442*51f0e3d5SAndroid Build Coastguard Worker     cinfo.image_height = img_height;
443*51f0e3d5SAndroid Build Coastguard Worker     cinfo.input_components = 3;
444*51f0e3d5SAndroid Build Coastguard Worker 
445*51f0e3d5SAndroid Build Coastguard Worker     // Set defaults based on the above values
446*51f0e3d5SAndroid Build Coastguard Worker     jpeg_set_defaults(&cinfo);
447*51f0e3d5SAndroid Build Coastguard Worker 
448*51f0e3d5SAndroid Build Coastguard Worker     jpeg_set_quality(&cinfo, quality, true);
449*51f0e3d5SAndroid Build Coastguard Worker 
450*51f0e3d5SAndroid Build Coastguard Worker     cinfo.dct_method = JDCT_IFAST;
451*51f0e3d5SAndroid Build Coastguard Worker 
452*51f0e3d5SAndroid Build Coastguard Worker     cinfo.raw_data_in = true;
453*51f0e3d5SAndroid Build Coastguard Worker 
454*51f0e3d5SAndroid Build Coastguard Worker     jpeg_set_colorspace(&cinfo, JCS_YCbCr);
455*51f0e3d5SAndroid Build Coastguard Worker 
456*51f0e3d5SAndroid Build Coastguard Worker     cinfo.comp_info[0].h_samp_factor = 2;
457*51f0e3d5SAndroid Build Coastguard Worker     cinfo.comp_info[0].v_samp_factor = 2;
458*51f0e3d5SAndroid Build Coastguard Worker     cinfo.comp_info[1].h_samp_factor = 1;
459*51f0e3d5SAndroid Build Coastguard Worker     cinfo.comp_info[1].v_samp_factor = 1;
460*51f0e3d5SAndroid Build Coastguard Worker     cinfo.comp_info[2].h_samp_factor = 1;
461*51f0e3d5SAndroid Build Coastguard Worker     cinfo.comp_info[2].v_samp_factor = 1;
462*51f0e3d5SAndroid Build Coastguard Worker 
463*51f0e3d5SAndroid Build Coastguard Worker     jpeg_start_compress(&cinfo, true);
464*51f0e3d5SAndroid Build Coastguard Worker 
465*51f0e3d5SAndroid Build Coastguard Worker     yArr = new JSAMPROW[cinfo.comp_info[0].v_samp_factor * DCTSIZE];
466*51f0e3d5SAndroid Build Coastguard Worker     cbArr = new JSAMPROW[cinfo.comp_info[1].v_samp_factor * DCTSIZE];
467*51f0e3d5SAndroid Build Coastguard Worker     crArr = new JSAMPROW[cinfo.comp_info[2].v_samp_factor * DCTSIZE];
468*51f0e3d5SAndroid Build Coastguard Worker 
469*51f0e3d5SAndroid Build Coastguard Worker     imgArr[0] = const_cast<JSAMPARRAY>(yArr);
470*51f0e3d5SAndroid Build Coastguard Worker     imgArr[1] = const_cast<JSAMPARRAY>(cbArr);
471*51f0e3d5SAndroid Build Coastguard Worker     imgArr[2] = const_cast<JSAMPARRAY>(crArr);
472*51f0e3d5SAndroid Build Coastguard Worker 
473*51f0e3d5SAndroid Build Coastguard Worker     for (int y = 0; y < img_height; y += DCTSIZE * 2) {
474*51f0e3d5SAndroid Build Coastguard Worker         std::array<unsigned char*, 16> yData = y_row_generator.loadAt(y);
475*51f0e3d5SAndroid Build Coastguard Worker         std::array<unsigned char*, 8> cbData = cb_row_generator.loadAt(y / 2);
476*51f0e3d5SAndroid Build Coastguard Worker         std::array<unsigned char*, 8> crData = cr_row_generator.loadAt(y / 2);
477*51f0e3d5SAndroid Build Coastguard Worker 
478*51f0e3d5SAndroid Build Coastguard Worker         for (int row = 0; row < DCTSIZE * 2; row++) {
479*51f0e3d5SAndroid Build Coastguard Worker             yArr[row] = yData[row];
480*51f0e3d5SAndroid Build Coastguard Worker         }
481*51f0e3d5SAndroid Build Coastguard Worker         for (int row = 0; row < DCTSIZE; row++) {
482*51f0e3d5SAndroid Build Coastguard Worker             cbArr[row] = cbData[row];
483*51f0e3d5SAndroid Build Coastguard Worker             crArr[row] = crData[row];
484*51f0e3d5SAndroid Build Coastguard Worker         }
485*51f0e3d5SAndroid Build Coastguard Worker 
486*51f0e3d5SAndroid Build Coastguard Worker         jpeg_write_raw_data(&cinfo, imgArr, DCTSIZE * 2);
487*51f0e3d5SAndroid Build Coastguard Worker     }
488*51f0e3d5SAndroid Build Coastguard Worker 
489*51f0e3d5SAndroid Build Coastguard Worker     jpeg_finish_compress(&cinfo);
490*51f0e3d5SAndroid Build Coastguard Worker 
491*51f0e3d5SAndroid Build Coastguard Worker     int numBytesInBuffer = cinfo.dest->next_output_byte - out_buf;
492*51f0e3d5SAndroid Build Coastguard Worker 
493*51f0e3d5SAndroid Build Coastguard Worker     flush(numBytesInBuffer);
494*51f0e3d5SAndroid Build Coastguard Worker 
495*51f0e3d5SAndroid Build Coastguard Worker     clientData.totalOutputBytes += numBytesInBuffer;
496*51f0e3d5SAndroid Build Coastguard Worker 
497*51f0e3d5SAndroid Build Coastguard Worker     safeDeleteArray(yArr);
498*51f0e3d5SAndroid Build Coastguard Worker     safeDeleteArray(cbArr);
499*51f0e3d5SAndroid Build Coastguard Worker     safeDeleteArray(crArr);
500*51f0e3d5SAndroid Build Coastguard Worker 
501*51f0e3d5SAndroid Build Coastguard Worker     jpeg_destroy_compress(&cinfo);
502*51f0e3d5SAndroid Build Coastguard Worker 
503*51f0e3d5SAndroid Build Coastguard Worker     return clientData.totalOutputBytes;
504*51f0e3d5SAndroid Build Coastguard Worker }
505*51f0e3d5SAndroid Build Coastguard Worker 
compress(int width,int height,unsigned char * yBuf,int yPStride,int yRStride,unsigned char * cbBuf,int cbPStride,int cbRStride,unsigned char * crBuf,int crPStride,int crRStride,unsigned char * outBuf,size_t outBufCapacity,int quality,int cropLeft,int cropTop,int cropRight,int cropBottom,int rot90)506*51f0e3d5SAndroid Build Coastguard Worker int compress(
507*51f0e3d5SAndroid Build Coastguard Worker         /** Input image dimensions */
508*51f0e3d5SAndroid Build Coastguard Worker         int width, int height,
509*51f0e3d5SAndroid Build Coastguard Worker         /** Y Plane */
510*51f0e3d5SAndroid Build Coastguard Worker         unsigned char* yBuf, int yPStride, int yRStride,
511*51f0e3d5SAndroid Build Coastguard Worker         /** Cb Plane */
512*51f0e3d5SAndroid Build Coastguard Worker         unsigned char* cbBuf, int cbPStride, int cbRStride,
513*51f0e3d5SAndroid Build Coastguard Worker         /** Cr Plane */
514*51f0e3d5SAndroid Build Coastguard Worker         unsigned char* crBuf, int crPStride, int crRStride,
515*51f0e3d5SAndroid Build Coastguard Worker         /** Output */
516*51f0e3d5SAndroid Build Coastguard Worker         unsigned char* outBuf, size_t outBufCapacity,
517*51f0e3d5SAndroid Build Coastguard Worker         /** Jpeg compression parameters */
518*51f0e3d5SAndroid Build Coastguard Worker         int quality,
519*51f0e3d5SAndroid Build Coastguard Worker         /** Crop */
520*51f0e3d5SAndroid Build Coastguard Worker         int cropLeft, int cropTop, int cropRight, int cropBottom,
521*51f0e3d5SAndroid Build Coastguard Worker         /** Rotation (multiple of 90).  For example, rot90 = 1 implies a 90 degree
522*51f0e3d5SAndroid Build Coastguard Worker          * rotation. */
523*51f0e3d5SAndroid Build Coastguard Worker         int rot90) {
524*51f0e3d5SAndroid Build Coastguard Worker     int finalWidth;
525*51f0e3d5SAndroid Build Coastguard Worker     int finalHeight;
526*51f0e3d5SAndroid Build Coastguard Worker     finalWidth = cropRight - cropLeft;
527*51f0e3d5SAndroid Build Coastguard Worker     finalHeight = cropBottom - cropTop;
528*51f0e3d5SAndroid Build Coastguard Worker 
529*51f0e3d5SAndroid Build Coastguard Worker     rot90 %= 4;
530*51f0e3d5SAndroid Build Coastguard Worker     // for 90 and 270-degree rotations, flip the final width and height
531*51f0e3d5SAndroid Build Coastguard Worker     if (rot90 == 1) {
532*51f0e3d5SAndroid Build Coastguard Worker         finalWidth = cropBottom - cropTop;
533*51f0e3d5SAndroid Build Coastguard Worker         finalHeight = cropRight - cropLeft;
534*51f0e3d5SAndroid Build Coastguard Worker     } else if (rot90 == 3) {
535*51f0e3d5SAndroid Build Coastguard Worker         finalWidth = cropBottom - cropTop;
536*51f0e3d5SAndroid Build Coastguard Worker         finalHeight = cropRight - cropLeft;
537*51f0e3d5SAndroid Build Coastguard Worker     }
538*51f0e3d5SAndroid Build Coastguard Worker 
539*51f0e3d5SAndroid Build Coastguard Worker     const Plane yP = {width, height, yBuf, yPStride, yRStride};
540*51f0e3d5SAndroid Build Coastguard Worker     const Plane cbP = {width / 2, height / 2, cbBuf, cbPStride, cbRStride};
541*51f0e3d5SAndroid Build Coastguard Worker     const Plane crP = {width / 2, height / 2, crBuf, crPStride, crRStride};
542*51f0e3d5SAndroid Build Coastguard Worker 
543*51f0e3d5SAndroid Build Coastguard Worker     auto flush = [](size_t numBytes __unused) {
544*51f0e3d5SAndroid Build Coastguard Worker         // do nothing
545*51f0e3d5SAndroid Build Coastguard Worker     };
546*51f0e3d5SAndroid Build Coastguard Worker 
547*51f0e3d5SAndroid Build Coastguard Worker     // Round up to the nearest multiple of 64.
548*51f0e3d5SAndroid Build Coastguard Worker     int y_row_length = (finalWidth + 16 + 63) & ~63;
549*51f0e3d5SAndroid Build Coastguard Worker     int cb_row_length = (finalWidth / 2 + 16 + 63) & ~63;
550*51f0e3d5SAndroid Build Coastguard Worker     int cr_row_length = (finalWidth / 2 + 16 + 63) & ~63;
551*51f0e3d5SAndroid Build Coastguard Worker 
552*51f0e3d5SAndroid Build Coastguard Worker     Transform yTrans = Transform::forCropFollowedByRotation(
553*51f0e3d5SAndroid Build Coastguard Worker             cropLeft, cropTop, cropRight, cropBottom, rot90);
554*51f0e3d5SAndroid Build Coastguard Worker 
555*51f0e3d5SAndroid Build Coastguard Worker     Transform chromaTrans = Transform::forCropFollowedByRotation(
556*51f0e3d5SAndroid Build Coastguard Worker             cropLeft / 2, cropTop / 2, cropRight / 2, cropBottom / 2, rot90);
557*51f0e3d5SAndroid Build Coastguard Worker 
558*51f0e3d5SAndroid Build Coastguard Worker     RowIterator<16> yIter(yP, yTrans, y_row_length);
559*51f0e3d5SAndroid Build Coastguard Worker     RowIterator<8> cbIter(cbP, chromaTrans, cb_row_length);
560*51f0e3d5SAndroid Build Coastguard Worker     RowIterator<8> crIter(crP, chromaTrans, cr_row_length);
561*51f0e3d5SAndroid Build Coastguard Worker 
562*51f0e3d5SAndroid Build Coastguard Worker     return compress(finalWidth, finalHeight, yIter, cbIter, crIter, outBuf, outBufCapacity, flush,
563*51f0e3d5SAndroid Build Coastguard Worker             quality);
564*51f0e3d5SAndroid Build Coastguard Worker }
565*51f0e3d5SAndroid Build Coastguard Worker 
566*51f0e3d5SAndroid Build Coastguard Worker extern "C" {
567*51f0e3d5SAndroid Build Coastguard Worker 
JpegEncoder_compressJpegFromYUV420p(JNIEnv * env,jclass clazz __unused,jint width,jint height,jobject yBuf,jint yPStride,jint yRStride,jobject cbBuf,jint cbPStride,jint cbRStride,jobject crBuf,jint crPStride,jint crRStride,jobject outBuf,jint outBufCapacity,jint quality,jint cropLeft,jint cropTop,jint cropRight,jint cropBottom,jint rot90)568*51f0e3d5SAndroid Build Coastguard Worker static jint JpegEncoder_compressJpegFromYUV420p(
569*51f0e3d5SAndroid Build Coastguard Worker         JNIEnv* env, jclass clazz __unused,
570*51f0e3d5SAndroid Build Coastguard Worker         /** Input image dimensions */
571*51f0e3d5SAndroid Build Coastguard Worker         jint width, jint height,
572*51f0e3d5SAndroid Build Coastguard Worker         /** Y Plane */
573*51f0e3d5SAndroid Build Coastguard Worker         jobject yBuf, jint yPStride, jint yRStride,
574*51f0e3d5SAndroid Build Coastguard Worker         /** Cb Plane */
575*51f0e3d5SAndroid Build Coastguard Worker         jobject cbBuf, jint cbPStride, jint cbRStride,
576*51f0e3d5SAndroid Build Coastguard Worker         /** Cr Plane */
577*51f0e3d5SAndroid Build Coastguard Worker         jobject crBuf, jint crPStride, jint crRStride,
578*51f0e3d5SAndroid Build Coastguard Worker         /** Output */
579*51f0e3d5SAndroid Build Coastguard Worker         jobject outBuf, jint outBufCapacity,
580*51f0e3d5SAndroid Build Coastguard Worker         /** Jpeg compression parameters */
581*51f0e3d5SAndroid Build Coastguard Worker         jint quality,
582*51f0e3d5SAndroid Build Coastguard Worker         /** Crop */
583*51f0e3d5SAndroid Build Coastguard Worker         jint cropLeft, jint cropTop, jint cropRight, jint cropBottom,
584*51f0e3d5SAndroid Build Coastguard Worker         /** Rotation (multiple of 90).  For example, rot90 = 1 implies a 90 degree
585*51f0e3d5SAndroid Build Coastguard Worker          * rotation. */
586*51f0e3d5SAndroid Build Coastguard Worker         jint rot90) {
587*51f0e3d5SAndroid Build Coastguard Worker     // ALOGE("Insert log inside JpegEncoder_compressJpegFromYUV420p");
588*51f0e3d5SAndroid Build Coastguard Worker     jbyte* y = (jbyte*)env->GetDirectBufferAddress(yBuf);
589*51f0e3d5SAndroid Build Coastguard Worker     jbyte* cb = (jbyte*)env->GetDirectBufferAddress(cbBuf);
590*51f0e3d5SAndroid Build Coastguard Worker     jbyte* cr = (jbyte*)env->GetDirectBufferAddress(crBuf);
591*51f0e3d5SAndroid Build Coastguard Worker     jbyte* out = (jbyte*)env->GetDirectBufferAddress(outBuf);
592*51f0e3d5SAndroid Build Coastguard Worker 
593*51f0e3d5SAndroid Build Coastguard Worker     size_t actualJpegSize = compress(width, height,
594*51f0e3d5SAndroid Build Coastguard Worker             (unsigned char*)y, yPStride, yRStride,
595*51f0e3d5SAndroid Build Coastguard Worker             (unsigned char*)cb, cbPStride, cbRStride,
596*51f0e3d5SAndroid Build Coastguard Worker             (unsigned char*)cr, crPStride, crRStride,
597*51f0e3d5SAndroid Build Coastguard Worker             (unsigned char*)out, (size_t)outBufCapacity,
598*51f0e3d5SAndroid Build Coastguard Worker             quality, cropLeft, cropTop, cropRight, cropBottom, rot90);
599*51f0e3d5SAndroid Build Coastguard Worker 
600*51f0e3d5SAndroid Build Coastguard Worker     size_t finalJpegSize = actualJpegSize + sizeof(CameraBlob);
601*51f0e3d5SAndroid Build Coastguard Worker     if (finalJpegSize > outBufCapacity) {
602*51f0e3d5SAndroid Build Coastguard Worker         // ALOGE("%s: Final jpeg buffer %zu not large enough for the jpeg blob header with "\
603*51f0e3d5SAndroid Build Coastguard Worker         //         "capacity %d", __FUNCTION__, finalJpegSize, outBufCapacity);
604*51f0e3d5SAndroid Build Coastguard Worker         return actualJpegSize;
605*51f0e3d5SAndroid Build Coastguard Worker     }
606*51f0e3d5SAndroid Build Coastguard Worker 
607*51f0e3d5SAndroid Build Coastguard Worker     int8_t* header = static_cast<int8_t *> (out) +
608*51f0e3d5SAndroid Build Coastguard Worker             (outBufCapacity - sizeof(CameraBlob));
609*51f0e3d5SAndroid Build Coastguard Worker     CameraBlob *blob = reinterpret_cast<CameraBlob *> (header);
610*51f0e3d5SAndroid Build Coastguard Worker     blob->blobId = CameraBlobId::JPEG;
611*51f0e3d5SAndroid Build Coastguard Worker     blob->blobSize = actualJpegSize;
612*51f0e3d5SAndroid Build Coastguard Worker 
613*51f0e3d5SAndroid Build Coastguard Worker     return actualJpegSize;
614*51f0e3d5SAndroid Build Coastguard Worker }
615*51f0e3d5SAndroid Build Coastguard Worker 
616*51f0e3d5SAndroid Build Coastguard Worker } // extern "C"
617*51f0e3d5SAndroid Build Coastguard Worker 
RegisterMethodsOrDie(JNIEnv * env,const char * className,const JNINativeMethod * gMethods,int numMethods)618*51f0e3d5SAndroid Build Coastguard Worker static inline int RegisterMethodsOrDie(JNIEnv* env, const char* className,
619*51f0e3d5SAndroid Build Coastguard Worker         const JNINativeMethod* gMethods, int numMethods) {
620*51f0e3d5SAndroid Build Coastguard Worker     int res = jniRegisterNativeMethods(env, className, gMethods, numMethods);
621*51f0e3d5SAndroid Build Coastguard Worker     return res;
622*51f0e3d5SAndroid Build Coastguard Worker }
623*51f0e3d5SAndroid Build Coastguard Worker 
624*51f0e3d5SAndroid Build Coastguard Worker static const JNINativeMethod gJpegEncoderMethods[] = {
625*51f0e3d5SAndroid Build Coastguard Worker     {"compressJpegFromYUV420pNative",
626*51f0e3d5SAndroid Build Coastguard Worker     "(IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IIIIIII)I",
627*51f0e3d5SAndroid Build Coastguard Worker     (void*)JpegEncoder_compressJpegFromYUV420p}};
628*51f0e3d5SAndroid Build Coastguard Worker 
register_android_hardware_camera2_impl_JpegEncoder(JNIEnv * env)629*51f0e3d5SAndroid Build Coastguard Worker int register_android_hardware_camera2_impl_JpegEncoder(JNIEnv* env) {
630*51f0e3d5SAndroid Build Coastguard Worker     return RegisterMethodsOrDie(env, "androidx/camera/extensions/impl/advanced/JpegEncoder",
631*51f0e3d5SAndroid Build Coastguard Worker             gJpegEncoderMethods, NELEM(gJpegEncoderMethods));
632*51f0e3d5SAndroid Build Coastguard Worker }
633*51f0e3d5SAndroid Build Coastguard Worker 
JNI_OnLoad(JavaVM * vm,void * reserved)634*51f0e3d5SAndroid Build Coastguard Worker jint JNI_OnLoad(JavaVM* vm, void* reserved) {
635*51f0e3d5SAndroid Build Coastguard Worker     JNIEnv* env;
636*51f0e3d5SAndroid Build Coastguard Worker 
637*51f0e3d5SAndroid Build Coastguard Worker     if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
638*51f0e3d5SAndroid Build Coastguard Worker         return -1;
639*51f0e3d5SAndroid Build Coastguard Worker     }
640*51f0e3d5SAndroid Build Coastguard Worker 
641*51f0e3d5SAndroid Build Coastguard Worker     if (register_android_hardware_camera2_impl_JpegEncoder(env) < 0) {
642*51f0e3d5SAndroid Build Coastguard Worker         ALOGE("ERROR: JpegEncoder native registration failed");
643*51f0e3d5SAndroid Build Coastguard Worker         return -1;
644*51f0e3d5SAndroid Build Coastguard Worker     }
645*51f0e3d5SAndroid Build Coastguard Worker 
646*51f0e3d5SAndroid Build Coastguard Worker     return JNI_VERSION_1_6;
647*51f0e3d5SAndroid Build Coastguard Worker }