| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 1 | #include "CreateJavaOutputStreamAdaptor.h" | 
| Kevin Lubick | 1175dc0 | 2022-02-28 12:41:27 -0500 | [diff] [blame] | 2 | #include "SkStream.h" | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 3 | #include "YuvToJpegEncoder.h" | 
| Mathias Agopian | 8f2423e | 2010-02-16 17:33:37 -0800 | [diff] [blame] | 4 | #include <ui/PixelFormat.h> | 
| Harish Mahendrakar | 6b09b82 | 2023-10-12 20:45:45 +0000 | [diff] [blame] | 5 | #include <utils/Errors.h> | 
| Mathias Agopian | 8f2423e | 2010-02-16 17:33:37 -0800 | [diff] [blame] | 6 | #include <hardware/hardware.h> | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 7 |  | 
| Derek Sollenberger | c5882c4 | 2019-10-25 11:11:32 -0400 | [diff] [blame] | 8 | #include "graphics_jni_helpers.h" | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 9 |  | 
| Kevin Lubick | ba25360 | 2022-08-09 21:13:39 +0000 | [diff] [blame] | 10 | #include <csetjmp> | 
|  | 11 |  | 
| Kevin Lubick | 4c39197 | 2023-02-16 12:54:46 +0000 | [diff] [blame] | 12 | extern "C" { | 
|  | 13 | // We need to include stdio.h before jpeg because jpeg does not include it, but uses FILE | 
|  | 14 | // See https://github.com/libjpeg-turbo/libjpeg-turbo/issues/17 | 
|  | 15 | #include <stdio.h> | 
|  | 16 | #include "jpeglib.h" | 
|  | 17 | #include "jerror.h" | 
|  | 18 | #include "jmorecfg.h" | 
|  | 19 | } | 
|  | 20 |  | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 21 | YuvToJpegEncoder* YuvToJpegEncoder::create(int format, int* strides) { | 
| Mathias Agopian | a696f5d | 2010-02-17 17:53:09 -0800 | [diff] [blame] | 22 | // Only ImageFormat.NV21 and ImageFormat.YUY2 are supported | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 23 | // for now. | 
| Mathias Agopian | a696f5d | 2010-02-17 17:53:09 -0800 | [diff] [blame] | 24 | if (format == HAL_PIXEL_FORMAT_YCrCb_420_SP) { | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 25 | return new Yuv420SpToJpegEncoder(strides); | 
| Mathias Agopian | 8f2423e | 2010-02-16 17:33:37 -0800 | [diff] [blame] | 26 | } else if (format == HAL_PIXEL_FORMAT_YCbCr_422_I) { | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 27 | return new Yuv422IToJpegEncoder(strides); | 
|  | 28 | } else { | 
|  | 29 | return NULL; | 
|  | 30 | } | 
|  | 31 | } | 
|  | 32 |  | 
|  | 33 | YuvToJpegEncoder::YuvToJpegEncoder(int* strides) : fStrides(strides) { | 
|  | 34 | } | 
|  | 35 |  | 
| Leon Scroggins III | 1c3ded39 | 2018-02-28 13:23:32 -0500 | [diff] [blame] | 36 | struct ErrorMgr { | 
|  | 37 | struct jpeg_error_mgr pub; | 
|  | 38 | jmp_buf jmp; | 
|  | 39 | }; | 
|  | 40 |  | 
|  | 41 | void error_exit(j_common_ptr cinfo) { | 
|  | 42 | ErrorMgr* err = (ErrorMgr*) cinfo->err; | 
|  | 43 | (*cinfo->err->output_message) (cinfo); | 
|  | 44 | longjmp(err->jmp, 1); | 
|  | 45 | } | 
|  | 46 |  | 
| Kevin Lubick | 4c39197 | 2023-02-16 12:54:46 +0000 | [diff] [blame] | 47 | /* | 
|  | 48 | * Destination struct for directing decompressed pixels to a SkStream. | 
|  | 49 | */ | 
|  | 50 | static constexpr size_t kMgrBufferSize = 1024; | 
|  | 51 | struct skstream_destination_mgr : jpeg_destination_mgr { | 
|  | 52 | skstream_destination_mgr(SkWStream* stream); | 
|  | 53 |  | 
|  | 54 | SkWStream* const fStream; | 
|  | 55 |  | 
|  | 56 | uint8_t fBuffer[kMgrBufferSize]; | 
|  | 57 | }; | 
|  | 58 |  | 
|  | 59 | static void sk_init_destination(j_compress_ptr cinfo) { | 
|  | 60 | skstream_destination_mgr* dest = (skstream_destination_mgr*)cinfo->dest; | 
|  | 61 |  | 
|  | 62 | dest->next_output_byte = dest->fBuffer; | 
|  | 63 | dest->free_in_buffer = kMgrBufferSize; | 
|  | 64 | } | 
|  | 65 |  | 
|  | 66 | static boolean sk_empty_output_buffer(j_compress_ptr cinfo) { | 
|  | 67 | skstream_destination_mgr* dest = (skstream_destination_mgr*)cinfo->dest; | 
|  | 68 |  | 
|  | 69 | if (!dest->fStream->write(dest->fBuffer, kMgrBufferSize)) { | 
|  | 70 | ERREXIT(cinfo, JERR_FILE_WRITE); | 
|  | 71 | return FALSE; | 
|  | 72 | } | 
|  | 73 |  | 
|  | 74 | dest->next_output_byte = dest->fBuffer; | 
|  | 75 | dest->free_in_buffer = kMgrBufferSize; | 
|  | 76 | return TRUE; | 
|  | 77 | } | 
|  | 78 |  | 
|  | 79 | static void sk_term_destination(j_compress_ptr cinfo) { | 
|  | 80 | skstream_destination_mgr* dest = (skstream_destination_mgr*)cinfo->dest; | 
|  | 81 |  | 
|  | 82 | size_t size = kMgrBufferSize - dest->free_in_buffer; | 
|  | 83 | if (size > 0) { | 
|  | 84 | if (!dest->fStream->write(dest->fBuffer, size)) { | 
|  | 85 | ERREXIT(cinfo, JERR_FILE_WRITE); | 
|  | 86 | return; | 
|  | 87 | } | 
|  | 88 | } | 
|  | 89 |  | 
|  | 90 | dest->fStream->flush(); | 
|  | 91 | } | 
|  | 92 |  | 
|  | 93 | skstream_destination_mgr::skstream_destination_mgr(SkWStream* stream) | 
|  | 94 | : fStream(stream) { | 
|  | 95 | this->init_destination = sk_init_destination; | 
|  | 96 | this->empty_output_buffer = sk_empty_output_buffer; | 
|  | 97 | this->term_destination = sk_term_destination; | 
|  | 98 | } | 
|  | 99 |  | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 100 | bool YuvToJpegEncoder::encode(SkWStream* stream, void* inYuv, int width, | 
|  | 101 | int height, int* offsets, int jpegQuality) { | 
| Kevin Lubick | 4c39197 | 2023-02-16 12:54:46 +0000 | [diff] [blame] | 102 | jpeg_compress_struct      cinfo; | 
|  | 103 | ErrorMgr                  err; | 
|  | 104 | skstream_destination_mgr  sk_wstream(stream); | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 105 |  | 
| Leon Scroggins III | 1c3ded39 | 2018-02-28 13:23:32 -0500 | [diff] [blame] | 106 | cinfo.err = jpeg_std_error(&err.pub); | 
|  | 107 | err.pub.error_exit = error_exit; | 
|  | 108 |  | 
|  | 109 | if (setjmp(err.jmp)) { | 
|  | 110 | jpeg_destroy_compress(&cinfo); | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 111 | return false; | 
|  | 112 | } | 
|  | 113 | jpeg_create_compress(&cinfo); | 
|  | 114 |  | 
|  | 115 | cinfo.dest = &sk_wstream; | 
|  | 116 |  | 
|  | 117 | setJpegCompressStruct(&cinfo, width, height, jpegQuality); | 
|  | 118 |  | 
|  | 119 | jpeg_start_compress(&cinfo, TRUE); | 
|  | 120 |  | 
|  | 121 | compress(&cinfo, (uint8_t*) inYuv, offsets); | 
|  | 122 |  | 
|  | 123 | jpeg_finish_compress(&cinfo); | 
|  | 124 |  | 
| Leon Scroggins III | 1c3ded39 | 2018-02-28 13:23:32 -0500 | [diff] [blame] | 125 | jpeg_destroy_compress(&cinfo); | 
|  | 126 |  | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 127 | return true; | 
|  | 128 | } | 
|  | 129 |  | 
|  | 130 | void YuvToJpegEncoder::setJpegCompressStruct(jpeg_compress_struct* cinfo, | 
|  | 131 | int width, int height, int quality) { | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 132 | cinfo->image_width = width; | 
|  | 133 | cinfo->image_height = height; | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 134 | cinfo->input_components = 3; | 
|  | 135 | cinfo->in_color_space = JCS_YCbCr; | 
|  | 136 | jpeg_set_defaults(cinfo); | 
| Chia-chi Yeh | aa86859 | 2010-03-10 17:08:58 +0800 | [diff] [blame] | 137 |  | 
|  | 138 | jpeg_set_quality(cinfo, quality, TRUE); | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 139 | jpeg_set_colorspace(cinfo, JCS_YCbCr); | 
|  | 140 | cinfo->raw_data_in = TRUE; | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 141 | cinfo->dct_method = JDCT_IFAST; | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 142 | configSamplingFactors(cinfo); | 
|  | 143 | } | 
|  | 144 |  | 
|  | 145 | /////////////////////////////////////////////////////////////////// | 
|  | 146 | Yuv420SpToJpegEncoder::Yuv420SpToJpegEncoder(int* strides) : | 
|  | 147 | YuvToJpegEncoder(strides) { | 
|  | 148 | fNumPlanes = 2; | 
|  | 149 | } | 
|  | 150 |  | 
|  | 151 | void Yuv420SpToJpegEncoder::compress(jpeg_compress_struct* cinfo, | 
|  | 152 | uint8_t* yuv, int* offsets) { | 
| Sally Qi | 7e3f93b | 2021-07-15 00:00:54 +0000 | [diff] [blame] | 153 | ALOGD("onFlyCompress"); | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 154 | JSAMPROW y[16]; | 
|  | 155 | JSAMPROW cb[8]; | 
|  | 156 | JSAMPROW cr[8]; | 
|  | 157 | JSAMPARRAY planes[3]; | 
|  | 158 | planes[0] = y; | 
|  | 159 | planes[1] = cb; | 
|  | 160 | planes[2] = cr; | 
|  | 161 |  | 
|  | 162 | int width = cinfo->image_width; | 
|  | 163 | int height = cinfo->image_height; | 
|  | 164 | uint8_t* yPlanar = yuv + offsets[0]; | 
|  | 165 | uint8_t* vuPlanar = yuv + offsets[1]; //width * height; | 
|  | 166 | uint8_t* uRows = new uint8_t [8 * (width >> 1)]; | 
|  | 167 | uint8_t* vRows = new uint8_t [8 * (width >> 1)]; | 
|  | 168 |  | 
|  | 169 |  | 
|  | 170 | // process 16 lines of Y and 8 lines of U/V each time. | 
|  | 171 | while (cinfo->next_scanline < cinfo->image_height) { | 
|  | 172 | //deitnerleave u and v | 
| Wu-cheng Li | 4b63f14 | 2012-10-16 23:40:03 +0800 | [diff] [blame] | 173 | deinterleave(vuPlanar, uRows, vRows, cinfo->next_scanline, width, height); | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 174 |  | 
| Wu-cheng Li | 4b63f14 | 2012-10-16 23:40:03 +0800 | [diff] [blame] | 175 | // Jpeg library ignores the rows whose indices are greater than height. | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 176 | for (int i = 0; i < 16; i++) { | 
|  | 177 | // y row | 
|  | 178 | y[i] = yPlanar + (cinfo->next_scanline + i) * fStrides[0]; | 
|  | 179 |  | 
|  | 180 | // construct u row and v row | 
|  | 181 | if ((i & 1) == 0) { | 
|  | 182 | // height and width are both halved because of downsampling | 
|  | 183 | int offset = (i >> 1) * (width >> 1); | 
|  | 184 | cb[i/2] = uRows + offset; | 
|  | 185 | cr[i/2] = vRows + offset; | 
|  | 186 | } | 
|  | 187 | } | 
|  | 188 | jpeg_write_raw_data(cinfo, planes, 16); | 
|  | 189 | } | 
|  | 190 | delete [] uRows; | 
|  | 191 | delete [] vRows; | 
|  | 192 |  | 
|  | 193 | } | 
|  | 194 |  | 
|  | 195 | void Yuv420SpToJpegEncoder::deinterleave(uint8_t* vuPlanar, uint8_t* uRows, | 
| Wu-cheng Li | 4b63f14 | 2012-10-16 23:40:03 +0800 | [diff] [blame] | 196 | uint8_t* vRows, int rowIndex, int width, int height) { | 
|  | 197 | int numRows = (height - rowIndex) / 2; | 
|  | 198 | if (numRows > 8) numRows = 8; | 
|  | 199 | for (int row = 0; row < numRows; ++row) { | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 200 | int offset = ((rowIndex >> 1) + row) * fStrides[1]; | 
|  | 201 | uint8_t* vu = vuPlanar + offset; | 
|  | 202 | for (int i = 0; i < (width >> 1); ++i) { | 
|  | 203 | int index = row * (width >> 1) + i; | 
|  | 204 | uRows[index] = vu[1]; | 
|  | 205 | vRows[index] = vu[0]; | 
|  | 206 | vu += 2; | 
|  | 207 | } | 
|  | 208 | } | 
|  | 209 | } | 
|  | 210 |  | 
|  | 211 | void Yuv420SpToJpegEncoder::configSamplingFactors(jpeg_compress_struct* cinfo) { | 
|  | 212 | // cb and cr are horizontally downsampled and vertically downsampled as well. | 
|  | 213 | cinfo->comp_info[0].h_samp_factor = 2; | 
|  | 214 | cinfo->comp_info[0].v_samp_factor = 2; | 
|  | 215 | cinfo->comp_info[1].h_samp_factor = 1; | 
|  | 216 | cinfo->comp_info[1].v_samp_factor = 1; | 
|  | 217 | cinfo->comp_info[2].h_samp_factor = 1; | 
|  | 218 | cinfo->comp_info[2].v_samp_factor = 1; | 
|  | 219 | } | 
|  | 220 |  | 
|  | 221 | /////////////////////////////////////////////////////////////////////////////// | 
|  | 222 | Yuv422IToJpegEncoder::Yuv422IToJpegEncoder(int* strides) : | 
|  | 223 | YuvToJpegEncoder(strides) { | 
|  | 224 | fNumPlanes = 1; | 
|  | 225 | } | 
|  | 226 |  | 
|  | 227 | void Yuv422IToJpegEncoder::compress(jpeg_compress_struct* cinfo, | 
|  | 228 | uint8_t* yuv, int* offsets) { | 
| Sally Qi | 7e3f93b | 2021-07-15 00:00:54 +0000 | [diff] [blame] | 229 | ALOGD("onFlyCompress_422"); | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 230 | JSAMPROW y[16]; | 
|  | 231 | JSAMPROW cb[16]; | 
|  | 232 | JSAMPROW cr[16]; | 
|  | 233 | JSAMPARRAY planes[3]; | 
|  | 234 | planes[0] = y; | 
|  | 235 | planes[1] = cb; | 
|  | 236 | planes[2] = cr; | 
|  | 237 |  | 
|  | 238 | int width = cinfo->image_width; | 
|  | 239 | int height = cinfo->image_height; | 
|  | 240 | uint8_t* yRows = new uint8_t [16 * width]; | 
|  | 241 | uint8_t* uRows = new uint8_t [16 * (width >> 1)]; | 
|  | 242 | uint8_t* vRows = new uint8_t [16 * (width >> 1)]; | 
|  | 243 |  | 
|  | 244 | uint8_t* yuvOffset = yuv + offsets[0]; | 
|  | 245 |  | 
|  | 246 | // process 16 lines of Y and 16 lines of U/V each time. | 
|  | 247 | while (cinfo->next_scanline < cinfo->image_height) { | 
|  | 248 | deinterleave(yuvOffset, yRows, uRows, vRows, cinfo->next_scanline, width, height); | 
|  | 249 |  | 
| Wu-cheng Li | 4b63f14 | 2012-10-16 23:40:03 +0800 | [diff] [blame] | 250 | // Jpeg library ignores the rows whose indices are greater than height. | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 251 | for (int i = 0; i < 16; i++) { | 
|  | 252 | // y row | 
|  | 253 | y[i] = yRows + i * width; | 
|  | 254 |  | 
|  | 255 | // construct u row and v row | 
|  | 256 | // width is halved because of downsampling | 
|  | 257 | int offset = i * (width >> 1); | 
|  | 258 | cb[i] = uRows + offset; | 
|  | 259 | cr[i] = vRows + offset; | 
|  | 260 | } | 
|  | 261 |  | 
|  | 262 | jpeg_write_raw_data(cinfo, planes, 16); | 
|  | 263 | } | 
|  | 264 | delete [] yRows; | 
|  | 265 | delete [] uRows; | 
|  | 266 | delete [] vRows; | 
|  | 267 | } | 
|  | 268 |  | 
|  | 269 |  | 
|  | 270 | void Yuv422IToJpegEncoder::deinterleave(uint8_t* yuv, uint8_t* yRows, uint8_t* uRows, | 
|  | 271 | uint8_t* vRows, int rowIndex, int width, int height) { | 
| Wu-cheng Li | 4b63f14 | 2012-10-16 23:40:03 +0800 | [diff] [blame] | 272 | int numRows = height - rowIndex; | 
|  | 273 | if (numRows > 16) numRows = 16; | 
|  | 274 | for (int row = 0; row < numRows; ++row) { | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 275 | uint8_t* yuvSeg = yuv + (rowIndex + row) * fStrides[0]; | 
|  | 276 | for (int i = 0; i < (width >> 1); ++i) { | 
|  | 277 | int indexY = row * width + (i << 1); | 
|  | 278 | int indexU = row * (width >> 1) + i; | 
|  | 279 | yRows[indexY] = yuvSeg[0]; | 
|  | 280 | yRows[indexY + 1] = yuvSeg[2]; | 
|  | 281 | uRows[indexU] = yuvSeg[1]; | 
|  | 282 | vRows[indexU] = yuvSeg[3]; | 
|  | 283 | yuvSeg += 4; | 
|  | 284 | } | 
|  | 285 | } | 
|  | 286 | } | 
|  | 287 |  | 
|  | 288 | void Yuv422IToJpegEncoder::configSamplingFactors(jpeg_compress_struct* cinfo) { | 
|  | 289 | // cb and cr are horizontally downsampled and vertically downsampled as well. | 
|  | 290 | cinfo->comp_info[0].h_samp_factor = 2; | 
|  | 291 | cinfo->comp_info[0].v_samp_factor = 2; | 
|  | 292 | cinfo->comp_info[1].h_samp_factor = 1; | 
|  | 293 | cinfo->comp_info[1].v_samp_factor = 2; | 
|  | 294 | cinfo->comp_info[2].h_samp_factor = 1; | 
|  | 295 | cinfo->comp_info[2].v_samp_factor = 2; | 
|  | 296 | } | 
|  | 297 | /////////////////////////////////////////////////////////////////////////////// | 
|  | 298 |  | 
| Harish Mahendrakar | 6b09b82 | 2023-10-12 20:45:45 +0000 | [diff] [blame] | 299 | using namespace ultrahdr; | 
| Dichen Zhang | 3b2c0ce | 2022-12-14 19:58:55 +0000 | [diff] [blame] | 300 |  | 
| Dichen Zhang | 7087aaf | 2023-04-14 19:01:05 +0000 | [diff] [blame] | 301 | ultrahdr_color_gamut P010Yuv420ToJpegREncoder::findColorGamut(JNIEnv* env, int aDataSpace) { | 
| Dichen Zhang | 3b2c0ce | 2022-12-14 19:58:55 +0000 | [diff] [blame] | 302 | switch (aDataSpace & ADataSpace::STANDARD_MASK) { | 
|  | 303 | case ADataSpace::STANDARD_BT709: | 
| Dichen Zhang | 7087aaf | 2023-04-14 19:01:05 +0000 | [diff] [blame] | 304 | return ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709; | 
| Dichen Zhang | 3b2c0ce | 2022-12-14 19:58:55 +0000 | [diff] [blame] | 305 | case ADataSpace::STANDARD_DCI_P3: | 
| Dichen Zhang | 7087aaf | 2023-04-14 19:01:05 +0000 | [diff] [blame] | 306 | return ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_P3; | 
| Dichen Zhang | 3b2c0ce | 2022-12-14 19:58:55 +0000 | [diff] [blame] | 307 | case ADataSpace::STANDARD_BT2020: | 
| Dichen Zhang | 7087aaf | 2023-04-14 19:01:05 +0000 | [diff] [blame] | 308 | return ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100; | 
| Dichen Zhang | 3b2c0ce | 2022-12-14 19:58:55 +0000 | [diff] [blame] | 309 | default: | 
|  | 310 | jclass IllegalArgumentException = env->FindClass("java/lang/IllegalArgumentException"); | 
|  | 311 | env->ThrowNew(IllegalArgumentException, | 
|  | 312 | "The requested color gamut is not supported by JPEG/R."); | 
|  | 313 | } | 
|  | 314 |  | 
| Dichen Zhang | 7087aaf | 2023-04-14 19:01:05 +0000 | [diff] [blame] | 315 | return ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED; | 
| Dichen Zhang | 3b2c0ce | 2022-12-14 19:58:55 +0000 | [diff] [blame] | 316 | } | 
|  | 317 |  | 
| Dichen Zhang | 7087aaf | 2023-04-14 19:01:05 +0000 | [diff] [blame] | 318 | ultrahdr_transfer_function P010Yuv420ToJpegREncoder::findHdrTransferFunction(JNIEnv* env, | 
| Dichen Zhang | 3b2c0ce | 2022-12-14 19:58:55 +0000 | [diff] [blame] | 319 | int aDataSpace) { | 
|  | 320 | switch (aDataSpace & ADataSpace::TRANSFER_MASK) { | 
|  | 321 | case ADataSpace::TRANSFER_ST2084: | 
| Dichen Zhang | 7087aaf | 2023-04-14 19:01:05 +0000 | [diff] [blame] | 322 | return ultrahdr_transfer_function::ULTRAHDR_TF_PQ; | 
| Dichen Zhang | 3b2c0ce | 2022-12-14 19:58:55 +0000 | [diff] [blame] | 323 | case ADataSpace::TRANSFER_HLG: | 
| Dichen Zhang | 7087aaf | 2023-04-14 19:01:05 +0000 | [diff] [blame] | 324 | return ultrahdr_transfer_function::ULTRAHDR_TF_HLG; | 
| Dichen Zhang | 3b2c0ce | 2022-12-14 19:58:55 +0000 | [diff] [blame] | 325 | default: | 
|  | 326 | jclass IllegalArgumentException = env->FindClass("java/lang/IllegalArgumentException"); | 
|  | 327 | env->ThrowNew(IllegalArgumentException, | 
|  | 328 | "The requested HDR transfer function is not supported by JPEG/R."); | 
|  | 329 | } | 
|  | 330 |  | 
| Dichen Zhang | 7087aaf | 2023-04-14 19:01:05 +0000 | [diff] [blame] | 331 | return ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED; | 
| Dichen Zhang | 3b2c0ce | 2022-12-14 19:58:55 +0000 | [diff] [blame] | 332 | } | 
|  | 333 |  | 
|  | 334 | bool P010Yuv420ToJpegREncoder::encode(JNIEnv* env, | 
|  | 335 | SkWStream* stream, void* hdr, int hdrColorSpace, void* sdr, int sdrColorSpace, | 
| Dichen Zhang | 843f1bd | 2023-10-26 02:38:21 +0000 | [diff] [blame] | 336 | int width, int height, int jpegQuality, ScopedByteArrayRO* jExif, | 
|  | 337 | ScopedIntArrayRO* jHdrStrides, ScopedIntArrayRO* jSdrStrides) { | 
| Dichen Zhang | 3b2c0ce | 2022-12-14 19:58:55 +0000 | [diff] [blame] | 338 | // Check SDR color space. Now we only support SRGB transfer function | 
|  | 339 | if ((sdrColorSpace & ADataSpace::TRANSFER_MASK) !=  ADataSpace::TRANSFER_SRGB) { | 
|  | 340 | jclass IllegalArgumentException = env->FindClass("java/lang/IllegalArgumentException"); | 
|  | 341 | env->ThrowNew(IllegalArgumentException, | 
|  | 342 | "The requested SDR color space is not supported. Transfer function must be SRGB"); | 
|  | 343 | return false; | 
|  | 344 | } | 
| Dichen Zhang | 843f1bd | 2023-10-26 02:38:21 +0000 | [diff] [blame] | 345 | // Check HDR and SDR strides length. | 
|  | 346 | // HDR is YCBCR_P010 color format, and its strides length must be 2 (Y, chroma (Cb, Cr)). | 
|  | 347 | // SDR is YUV_420_888 color format, and its strides length must be 3 (Y, Cb, Cr). | 
|  | 348 | if (jHdrStrides->size() != 2) { | 
|  | 349 | jclass IllegalArgumentException = env->FindClass("java/lang/IllegalArgumentException"); | 
|  | 350 | env->ThrowNew(IllegalArgumentException, "HDR stride length must be 2."); | 
|  | 351 | return false; | 
|  | 352 | } | 
|  | 353 | if (jSdrStrides->size() != 3) { | 
|  | 354 | jclass IllegalArgumentException = env->FindClass("java/lang/IllegalArgumentException"); | 
|  | 355 | env->ThrowNew(IllegalArgumentException, "SDR stride length must be 3."); | 
|  | 356 | return false; | 
|  | 357 | } | 
| Dichen Zhang | 3b2c0ce | 2022-12-14 19:58:55 +0000 | [diff] [blame] | 358 |  | 
| Dichen Zhang | 7087aaf | 2023-04-14 19:01:05 +0000 | [diff] [blame] | 359 | ultrahdr_color_gamut hdrColorGamut = findColorGamut(env, hdrColorSpace); | 
|  | 360 | ultrahdr_color_gamut sdrColorGamut = findColorGamut(env, sdrColorSpace); | 
|  | 361 | ultrahdr_transfer_function hdrTransferFunction = findHdrTransferFunction(env, hdrColorSpace); | 
| Dichen Zhang | 3b2c0ce | 2022-12-14 19:58:55 +0000 | [diff] [blame] | 362 |  | 
| Dichen Zhang | 7087aaf | 2023-04-14 19:01:05 +0000 | [diff] [blame] | 363 | if (hdrColorGamut == ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED | 
|  | 364 | || sdrColorGamut == ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED | 
|  | 365 | || hdrTransferFunction == ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED) { | 
| Dichen Zhang | 3b2c0ce | 2022-12-14 19:58:55 +0000 | [diff] [blame] | 366 | return false; | 
|  | 367 | } | 
|  | 368 |  | 
| Dichen Zhang | 843f1bd | 2023-10-26 02:38:21 +0000 | [diff] [blame] | 369 | const int* hdrStrides = reinterpret_cast<const int*>(jHdrStrides->get()); | 
|  | 370 | const int* sdrStrides = reinterpret_cast<const int*>(jSdrStrides->get()); | 
|  | 371 |  | 
| Dichen Zhang | f84ed40 | 2023-02-10 22:41:46 +0000 | [diff] [blame] | 372 | JpegR jpegREncoder; | 
| Dichen Zhang | 3b2c0ce | 2022-12-14 19:58:55 +0000 | [diff] [blame] | 373 |  | 
|  | 374 | jpegr_uncompressed_struct p010; | 
|  | 375 | p010.data = hdr; | 
|  | 376 | p010.width = width; | 
|  | 377 | p010.height = height; | 
| Dichen Zhang | 843f1bd | 2023-10-26 02:38:21 +0000 | [diff] [blame] | 378 | // Divided by 2 because unit in libultrader is pixel and in YuvImage it is byte. | 
|  | 379 | p010.luma_stride = (hdrStrides[0] + 1) / 2; | 
|  | 380 | p010.chroma_stride = (hdrStrides[1] + 1) / 2; | 
| Dichen Zhang | 3b2c0ce | 2022-12-14 19:58:55 +0000 | [diff] [blame] | 381 | p010.colorGamut = hdrColorGamut; | 
|  | 382 |  | 
|  | 383 | jpegr_uncompressed_struct yuv420; | 
|  | 384 | yuv420.data = sdr; | 
|  | 385 | yuv420.width = width; | 
|  | 386 | yuv420.height = height; | 
| Dichen Zhang | 843f1bd | 2023-10-26 02:38:21 +0000 | [diff] [blame] | 387 | yuv420.luma_stride = sdrStrides[0]; | 
|  | 388 | yuv420.chroma_stride = sdrStrides[1]; | 
| Dichen Zhang | 3b2c0ce | 2022-12-14 19:58:55 +0000 | [diff] [blame] | 389 | yuv420.colorGamut = sdrColorGamut; | 
|  | 390 |  | 
| Dichen Zhang | bfd2380 | 2023-10-26 01:29:19 +0000 | [diff] [blame] | 391 | jpegr_exif_struct exif; | 
|  | 392 | exif.data = const_cast<void*>(reinterpret_cast<const void*>(jExif->get())); | 
|  | 393 | exif.length = jExif->size(); | 
|  | 394 |  | 
| Dichen Zhang | 3b2c0ce | 2022-12-14 19:58:55 +0000 | [diff] [blame] | 395 | jpegr_compressed_struct jpegR; | 
|  | 396 | jpegR.maxLength = width * height * sizeof(uint8_t); | 
|  | 397 |  | 
|  | 398 | std::unique_ptr<uint8_t[]> jpegr_data = std::make_unique<uint8_t[]>(jpegR.maxLength); | 
|  | 399 | jpegR.data = jpegr_data.get(); | 
|  | 400 |  | 
| Dichen Zhang | f84ed40 | 2023-02-10 22:41:46 +0000 | [diff] [blame] | 401 | if (int success = jpegREncoder.encodeJPEGR(&p010, &yuv420, | 
| Dichen Zhang | 3b2c0ce | 2022-12-14 19:58:55 +0000 | [diff] [blame] | 402 | hdrTransferFunction, | 
| Dichen Zhang | bfd2380 | 2023-10-26 01:29:19 +0000 | [diff] [blame] | 403 | &jpegR, jpegQuality, | 
| Dichen Zhang | aaa85ca | 2023-12-07 00:29:53 +0000 | [diff] [blame] | 404 | exif.length > 0 ? &exif : NULL); success != JPEGR_NO_ERROR) { | 
| Dichen Zhang | 3b2c0ce | 2022-12-14 19:58:55 +0000 | [diff] [blame] | 405 | ALOGW("Encode JPEG/R failed, error code: %d.", success); | 
|  | 406 | return false; | 
|  | 407 | } | 
|  | 408 |  | 
|  | 409 | if (!stream->write(jpegR.data, jpegR.length)) { | 
|  | 410 | ALOGW("Writing JPEG/R to stream failed."); | 
|  | 411 | return false; | 
|  | 412 | } | 
|  | 413 |  | 
|  | 414 | return true; | 
|  | 415 | } | 
|  | 416 |  | 
|  | 417 | /////////////////////////////////////////////////////////////////////////////// | 
|  | 418 |  | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 419 | static jboolean YuvImage_compressToJpeg(JNIEnv* env, jobject, jbyteArray inYuv, | 
| Ashok Bhat | 39029b2 | 2014-01-10 16:24:38 +0000 | [diff] [blame] | 420 | jint format, jint width, jint height, jintArray offsets, | 
|  | 421 | jintArray strides, jint jpegQuality, jobject jstream, | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 422 | jbyteArray jstorage) { | 
|  | 423 | jbyte* yuv = env->GetByteArrayElements(inYuv, NULL); | 
|  | 424 | SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage); | 
|  | 425 |  | 
|  | 426 | jint* imgOffsets = env->GetIntArrayElements(offsets, NULL); | 
|  | 427 | jint* imgStrides = env->GetIntArrayElements(strides, NULL); | 
|  | 428 | YuvToJpegEncoder* encoder = YuvToJpegEncoder::create(format, imgStrides); | 
| Pawel Augustyn | 7c68a407 | 2012-07-16 11:23:54 +0200 | [diff] [blame] | 429 | jboolean result = JNI_FALSE; | 
|  | 430 | if (encoder != NULL) { | 
|  | 431 | encoder->encode(strm, yuv, width, height, imgOffsets, jpegQuality); | 
|  | 432 | delete encoder; | 
|  | 433 | result = JNI_TRUE; | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 434 | } | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 435 |  | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 436 | env->ReleaseByteArrayElements(inYuv, yuv, 0); | 
|  | 437 | env->ReleaseIntArrayElements(offsets, imgOffsets, 0); | 
|  | 438 | env->ReleaseIntArrayElements(strides, imgStrides, 0); | 
| Martin Wallgren | d865900 | 2014-08-20 14:58:58 +0200 | [diff] [blame] | 439 | delete strm; | 
| Pawel Augustyn | 7c68a407 | 2012-07-16 11:23:54 +0200 | [diff] [blame] | 440 | return result; | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 441 | } | 
| Dichen Zhang | 3b2c0ce | 2022-12-14 19:58:55 +0000 | [diff] [blame] | 442 |  | 
|  | 443 | static jboolean YuvImage_compressToJpegR(JNIEnv* env, jobject, jbyteArray inHdr, | 
|  | 444 | jint hdrColorSpace, jbyteArray inSdr, jint sdrColorSpace, | 
|  | 445 | jint width, jint height, jint quality, jobject jstream, | 
| Dichen Zhang | 843f1bd | 2023-10-26 02:38:21 +0000 | [diff] [blame] | 446 | jbyteArray jstorage, jbyteArray jExif, | 
|  | 447 | jintArray jHdrStrides, jintArray jSdrStrides) { | 
| Dichen Zhang | 3b2c0ce | 2022-12-14 19:58:55 +0000 | [diff] [blame] | 448 | jbyte* hdr = env->GetByteArrayElements(inHdr, NULL); | 
|  | 449 | jbyte* sdr = env->GetByteArrayElements(inSdr, NULL); | 
| Dichen Zhang | bfd2380 | 2023-10-26 01:29:19 +0000 | [diff] [blame] | 450 | ScopedByteArrayRO exif(env, jExif); | 
| Dichen Zhang | 843f1bd | 2023-10-26 02:38:21 +0000 | [diff] [blame] | 451 | ScopedIntArrayRO hdrStrides(env, jHdrStrides); | 
|  | 452 | ScopedIntArrayRO sdrStrides(env, jSdrStrides); | 
| Dichen Zhang | bfd2380 | 2023-10-26 01:29:19 +0000 | [diff] [blame] | 453 |  | 
| Dichen Zhang | 3b2c0ce | 2022-12-14 19:58:55 +0000 | [diff] [blame] | 454 | SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage); | 
|  | 455 | P010Yuv420ToJpegREncoder encoder; | 
|  | 456 |  | 
|  | 457 | jboolean result = JNI_FALSE; | 
|  | 458 | if (encoder.encode(env, strm, hdr, hdrColorSpace, sdr, sdrColorSpace, | 
| Dichen Zhang | 843f1bd | 2023-10-26 02:38:21 +0000 | [diff] [blame] | 459 | width, height, quality, &exif, | 
|  | 460 | &hdrStrides, &sdrStrides)) { | 
| Dichen Zhang | 3b2c0ce | 2022-12-14 19:58:55 +0000 | [diff] [blame] | 461 | result = JNI_TRUE; | 
|  | 462 | } | 
|  | 463 |  | 
|  | 464 | env->ReleaseByteArrayElements(inHdr, hdr, 0); | 
|  | 465 | env->ReleaseByteArrayElements(inSdr, sdr, 0); | 
| Dichen Zhang | 843f1bd | 2023-10-26 02:38:21 +0000 | [diff] [blame] | 466 |  | 
| Dichen Zhang | 3b2c0ce | 2022-12-14 19:58:55 +0000 | [diff] [blame] | 467 | delete strm; | 
|  | 468 | return result; | 
|  | 469 | } | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 470 | /////////////////////////////////////////////////////////////////////////////// | 
|  | 471 |  | 
| Daniel Micay | 76f6a86 | 2015-09-19 17:31:01 -0400 | [diff] [blame] | 472 | static const JNINativeMethod gYuvImageMethods[] = { | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 473 | {   "nativeCompressToJpeg",  "([BIII[I[IILjava/io/OutputStream;[B)Z", | 
| Dichen Zhang | 3b2c0ce | 2022-12-14 19:58:55 +0000 | [diff] [blame] | 474 | (void*)YuvImage_compressToJpeg }, | 
| Dichen Zhang | 843f1bd | 2023-10-26 02:38:21 +0000 | [diff] [blame] | 475 | {   "nativeCompressToJpegR",  "([BI[BIIIILjava/io/OutputStream;[B[B[I[I)Z", | 
| Dichen Zhang | 3b2c0ce | 2022-12-14 19:58:55 +0000 | [diff] [blame] | 476 | (void*)YuvImage_compressToJpegR } | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 477 | }; | 
|  | 478 |  | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 479 | int register_android_graphics_YuvImage(JNIEnv* env) | 
|  | 480 | { | 
| Andreas Gampe | ed6b9df | 2014-11-20 22:02:20 -0800 | [diff] [blame] | 481 | return android::RegisterMethodsOrDie(env, "android/graphics/YuvImage", gYuvImageMethods, | 
|  | 482 | NELEM(gYuvImageMethods)); | 
| Wei-Ta Chen | bca2d61 | 2009-11-30 17:52:05 +0800 | [diff] [blame] | 483 | } |