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