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 }