1*89a0ef05SAndroid Build Coastguard Worker /*
2*89a0ef05SAndroid Build Coastguard Worker * Copyright 2022 The Android Open Source Project
3*89a0ef05SAndroid Build Coastguard Worker *
4*89a0ef05SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*89a0ef05SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*89a0ef05SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*89a0ef05SAndroid Build Coastguard Worker *
8*89a0ef05SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*89a0ef05SAndroid Build Coastguard Worker *
10*89a0ef05SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*89a0ef05SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*89a0ef05SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*89a0ef05SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*89a0ef05SAndroid Build Coastguard Worker * limitations under the License.
15*89a0ef05SAndroid Build Coastguard Worker */
16*89a0ef05SAndroid Build Coastguard Worker
17*89a0ef05SAndroid Build Coastguard Worker #include <errno.h>
18*89a0ef05SAndroid Build Coastguard Worker #include <setjmp.h>
19*89a0ef05SAndroid Build Coastguard Worker
20*89a0ef05SAndroid Build Coastguard Worker #include <cmath>
21*89a0ef05SAndroid Build Coastguard Worker #include <cstring>
22*89a0ef05SAndroid Build Coastguard Worker #include <map>
23*89a0ef05SAndroid Build Coastguard Worker #include <memory>
24*89a0ef05SAndroid Build Coastguard Worker #include <string>
25*89a0ef05SAndroid Build Coastguard Worker
26*89a0ef05SAndroid Build Coastguard Worker #include "ultrahdr/ultrahdrcommon.h"
27*89a0ef05SAndroid Build Coastguard Worker #include "ultrahdr/jpegencoderhelper.h"
28*89a0ef05SAndroid Build Coastguard Worker
29*89a0ef05SAndroid Build Coastguard Worker namespace ultrahdr {
30*89a0ef05SAndroid Build Coastguard Worker
31*89a0ef05SAndroid Build Coastguard Worker /*!\brief map of sub sampling format and jpeg h_samp_factor, v_samp_factor */
32*89a0ef05SAndroid Build Coastguard Worker std::map<uhdr_img_fmt_t, std::vector<int>> sample_factors = {
33*89a0ef05SAndroid Build Coastguard Worker {UHDR_IMG_FMT_8bppYCbCr400,
34*89a0ef05SAndroid Build Coastguard Worker {1 /*h0*/, 1 /*v0*/, 0 /*h1*/, 0 /*v1*/, 0 /*h2*/, 0 /*v2*/, 1 /*maxh*/, 1 /*maxv*/}},
35*89a0ef05SAndroid Build Coastguard Worker {UHDR_IMG_FMT_24bppYCbCr444,
36*89a0ef05SAndroid Build Coastguard Worker {1 /*h0*/, 1 /*v0*/, 1 /*h1*/, 1 /*v1*/, 1 /*h2*/, 1 /*v2*/, 1 /*maxh*/, 1 /*maxv*/}},
37*89a0ef05SAndroid Build Coastguard Worker {UHDR_IMG_FMT_16bppYCbCr440,
38*89a0ef05SAndroid Build Coastguard Worker {1 /*h0*/, 2 /*v0*/, 1 /*h1*/, 1 /*v1*/, 1 /*h2*/, 1 /*v2*/, 1 /*maxh*/, 2 /*maxv*/}},
39*89a0ef05SAndroid Build Coastguard Worker {UHDR_IMG_FMT_16bppYCbCr422,
40*89a0ef05SAndroid Build Coastguard Worker {2 /*h0*/, 1 /*v0*/, 1 /*h1*/, 1 /*v1*/, 1 /*h2*/, 1 /*v2*/, 2 /*maxh*/, 1 /*maxv*/}},
41*89a0ef05SAndroid Build Coastguard Worker {UHDR_IMG_FMT_12bppYCbCr420,
42*89a0ef05SAndroid Build Coastguard Worker {2 /*h0*/, 2 /*v0*/, 1 /*h1*/, 1 /*v1*/, 1 /*h2*/, 1 /*v2*/, 2 /*maxh*/, 2 /*maxv*/}},
43*89a0ef05SAndroid Build Coastguard Worker {UHDR_IMG_FMT_12bppYCbCr411,
44*89a0ef05SAndroid Build Coastguard Worker {4 /*h0*/, 1 /*v0*/, 1 /*h1*/, 1 /*v1*/, 1 /*h2*/, 1 /*v2*/, 4 /*maxh*/, 1 /*maxv*/}},
45*89a0ef05SAndroid Build Coastguard Worker {UHDR_IMG_FMT_10bppYCbCr410,
46*89a0ef05SAndroid Build Coastguard Worker {4 /*h0*/, 2 /*v0*/, 1 /*h1*/, 1 /*v1*/, 1 /*h2*/, 1 /*v2*/, 4 /*maxh*/, 2 /*maxv*/}},
47*89a0ef05SAndroid Build Coastguard Worker {UHDR_IMG_FMT_24bppRGB888,
48*89a0ef05SAndroid Build Coastguard Worker {1 /*h0*/, 1 /*v0*/, 1 /*h1*/, 1 /*v1*/, 1 /*h2*/, 1 /*v2*/, 1 /*maxh*/, 1 /*maxv*/}},
49*89a0ef05SAndroid Build Coastguard Worker };
50*89a0ef05SAndroid Build Coastguard Worker
51*89a0ef05SAndroid Build Coastguard Worker /*!\brief jpeg encoder library destination manager callback functions implementation */
52*89a0ef05SAndroid Build Coastguard Worker
53*89a0ef05SAndroid Build Coastguard Worker /*!\brief called by jpeg_start_compress() before any data is actually written. This function is
54*89a0ef05SAndroid Build Coastguard Worker * expected to initialize fields next_output_byte (place to write encoded output) and
55*89a0ef05SAndroid Build Coastguard Worker * free_in_buffer (size of the buffer supplied) of jpeg destination manager. free_in_buffer must
56*89a0ef05SAndroid Build Coastguard Worker * be initialized to a positive value.*/
initDestination(j_compress_ptr cinfo)57*89a0ef05SAndroid Build Coastguard Worker static void initDestination(j_compress_ptr cinfo) {
58*89a0ef05SAndroid Build Coastguard Worker destination_mgr_impl* dest = reinterpret_cast<destination_mgr_impl*>(cinfo->dest);
59*89a0ef05SAndroid Build Coastguard Worker std::vector<JOCTET>& buffer = dest->mResultBuffer;
60*89a0ef05SAndroid Build Coastguard Worker buffer.resize(dest->kBlockSize);
61*89a0ef05SAndroid Build Coastguard Worker dest->next_output_byte = &buffer[0];
62*89a0ef05SAndroid Build Coastguard Worker dest->free_in_buffer = buffer.size();
63*89a0ef05SAndroid Build Coastguard Worker }
64*89a0ef05SAndroid Build Coastguard Worker
65*89a0ef05SAndroid Build Coastguard Worker /*!\brief called if buffer provided for storing encoded data is exhausted during encoding. This
66*89a0ef05SAndroid Build Coastguard Worker * function is expected to consume the encoded output and provide fresh buffer to continue
67*89a0ef05SAndroid Build Coastguard Worker * encoding. */
emptyOutputBuffer(j_compress_ptr cinfo)68*89a0ef05SAndroid Build Coastguard Worker static boolean emptyOutputBuffer(j_compress_ptr cinfo) {
69*89a0ef05SAndroid Build Coastguard Worker destination_mgr_impl* dest = reinterpret_cast<destination_mgr_impl*>(cinfo->dest);
70*89a0ef05SAndroid Build Coastguard Worker std::vector<JOCTET>& buffer = dest->mResultBuffer;
71*89a0ef05SAndroid Build Coastguard Worker size_t oldsize = buffer.size();
72*89a0ef05SAndroid Build Coastguard Worker buffer.resize(oldsize + dest->kBlockSize);
73*89a0ef05SAndroid Build Coastguard Worker dest->next_output_byte = &buffer[oldsize];
74*89a0ef05SAndroid Build Coastguard Worker dest->free_in_buffer = dest->kBlockSize;
75*89a0ef05SAndroid Build Coastguard Worker return TRUE;
76*89a0ef05SAndroid Build Coastguard Worker }
77*89a0ef05SAndroid Build Coastguard Worker
78*89a0ef05SAndroid Build Coastguard Worker /*!\brief called by jpeg_finish_compress() to flush out all the remaining encoded data. client
79*89a0ef05SAndroid Build Coastguard Worker * can use either next_output_byte or free_in_buffer to determine how much data is in the buffer.
80*89a0ef05SAndroid Build Coastguard Worker */
terminateDestination(j_compress_ptr cinfo)81*89a0ef05SAndroid Build Coastguard Worker static void terminateDestination(j_compress_ptr cinfo) {
82*89a0ef05SAndroid Build Coastguard Worker destination_mgr_impl* dest = reinterpret_cast<destination_mgr_impl*>(cinfo->dest);
83*89a0ef05SAndroid Build Coastguard Worker std::vector<JOCTET>& buffer = dest->mResultBuffer;
84*89a0ef05SAndroid Build Coastguard Worker buffer.resize(buffer.size() - dest->free_in_buffer);
85*89a0ef05SAndroid Build Coastguard Worker }
86*89a0ef05SAndroid Build Coastguard Worker
87*89a0ef05SAndroid Build Coastguard Worker /*!\brief module for managing error */
88*89a0ef05SAndroid Build Coastguard Worker struct jpeg_error_mgr_impl : jpeg_error_mgr {
89*89a0ef05SAndroid Build Coastguard Worker jmp_buf setjmp_buffer;
90*89a0ef05SAndroid Build Coastguard Worker };
91*89a0ef05SAndroid Build Coastguard Worker
92*89a0ef05SAndroid Build Coastguard Worker /*!\brief jpeg encoder library error manager callback function implementations */
jpegrerror_exit(j_common_ptr cinfo)93*89a0ef05SAndroid Build Coastguard Worker static void jpegrerror_exit(j_common_ptr cinfo) {
94*89a0ef05SAndroid Build Coastguard Worker jpeg_error_mgr_impl* err = reinterpret_cast<jpeg_error_mgr_impl*>(cinfo->err);
95*89a0ef05SAndroid Build Coastguard Worker longjmp(err->setjmp_buffer, 1);
96*89a0ef05SAndroid Build Coastguard Worker }
97*89a0ef05SAndroid Build Coastguard Worker
98*89a0ef05SAndroid Build Coastguard Worker /* receive most recent jpeg error message and print */
outputErrorMessage(j_common_ptr cinfo)99*89a0ef05SAndroid Build Coastguard Worker static void outputErrorMessage(j_common_ptr cinfo) {
100*89a0ef05SAndroid Build Coastguard Worker char buffer[JMSG_LENGTH_MAX];
101*89a0ef05SAndroid Build Coastguard Worker
102*89a0ef05SAndroid Build Coastguard Worker /* Create the message */
103*89a0ef05SAndroid Build Coastguard Worker (*cinfo->err->format_message)(cinfo, buffer);
104*89a0ef05SAndroid Build Coastguard Worker ALOGE("%s\n", buffer);
105*89a0ef05SAndroid Build Coastguard Worker }
106*89a0ef05SAndroid Build Coastguard Worker
compressImage(const uhdr_raw_image_t * img,const int qfactor,const void * iccBuffer,const size_t iccSize)107*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t JpegEncoderHelper::compressImage(const uhdr_raw_image_t* img, const int qfactor,
108*89a0ef05SAndroid Build Coastguard Worker const void* iccBuffer, const size_t iccSize) {
109*89a0ef05SAndroid Build Coastguard Worker const uint8_t* planes[3]{reinterpret_cast<uint8_t*>(img->planes[UHDR_PLANE_Y]),
110*89a0ef05SAndroid Build Coastguard Worker reinterpret_cast<uint8_t*>(img->planes[UHDR_PLANE_U]),
111*89a0ef05SAndroid Build Coastguard Worker reinterpret_cast<uint8_t*>(img->planes[UHDR_PLANE_V])};
112*89a0ef05SAndroid Build Coastguard Worker const unsigned int strides[3]{img->stride[UHDR_PLANE_Y], img->stride[UHDR_PLANE_U],
113*89a0ef05SAndroid Build Coastguard Worker img->stride[UHDR_PLANE_V]};
114*89a0ef05SAndroid Build Coastguard Worker return compressImage(planes, strides, img->w, img->h, img->fmt, qfactor, iccBuffer, iccSize);
115*89a0ef05SAndroid Build Coastguard Worker }
116*89a0ef05SAndroid Build Coastguard Worker
compressImage(const uint8_t * planes[3],const unsigned int strides[3],const int width,const int height,const uhdr_img_fmt_t format,const int qfactor,const void * iccBuffer,const size_t iccSize)117*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t JpegEncoderHelper::compressImage(const uint8_t* planes[3],
118*89a0ef05SAndroid Build Coastguard Worker const unsigned int strides[3], const int width,
119*89a0ef05SAndroid Build Coastguard Worker const int height, const uhdr_img_fmt_t format,
120*89a0ef05SAndroid Build Coastguard Worker const int qfactor, const void* iccBuffer,
121*89a0ef05SAndroid Build Coastguard Worker const size_t iccSize) {
122*89a0ef05SAndroid Build Coastguard Worker return encode(planes, strides, width, height, format, qfactor, iccBuffer, iccSize);
123*89a0ef05SAndroid Build Coastguard Worker }
124*89a0ef05SAndroid Build Coastguard Worker
getCompressedImage()125*89a0ef05SAndroid Build Coastguard Worker uhdr_compressed_image_t JpegEncoderHelper::getCompressedImage() {
126*89a0ef05SAndroid Build Coastguard Worker uhdr_compressed_image_t img;
127*89a0ef05SAndroid Build Coastguard Worker
128*89a0ef05SAndroid Build Coastguard Worker img.data = mDestMgr.mResultBuffer.data();
129*89a0ef05SAndroid Build Coastguard Worker img.capacity = img.data_sz = mDestMgr.mResultBuffer.size();
130*89a0ef05SAndroid Build Coastguard Worker img.cg = UHDR_CG_UNSPECIFIED;
131*89a0ef05SAndroid Build Coastguard Worker img.ct = UHDR_CT_UNSPECIFIED;
132*89a0ef05SAndroid Build Coastguard Worker img.range = UHDR_CR_UNSPECIFIED;
133*89a0ef05SAndroid Build Coastguard Worker
134*89a0ef05SAndroid Build Coastguard Worker return img;
135*89a0ef05SAndroid Build Coastguard Worker }
136*89a0ef05SAndroid Build Coastguard Worker
encode(const uint8_t * planes[3],const unsigned int strides[3],const int width,const int height,const uhdr_img_fmt_t format,const int qfactor,const void * iccBuffer,const size_t iccSize)137*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t JpegEncoderHelper::encode(const uint8_t* planes[3], const unsigned int strides[3],
138*89a0ef05SAndroid Build Coastguard Worker const int width, const int height,
139*89a0ef05SAndroid Build Coastguard Worker const uhdr_img_fmt_t format, const int qfactor,
140*89a0ef05SAndroid Build Coastguard Worker const void* iccBuffer, const size_t iccSize) {
141*89a0ef05SAndroid Build Coastguard Worker jpeg_compress_struct cinfo;
142*89a0ef05SAndroid Build Coastguard Worker jpeg_error_mgr_impl myerr;
143*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t status = g_no_error;
144*89a0ef05SAndroid Build Coastguard Worker
145*89a0ef05SAndroid Build Coastguard Worker if (sample_factors.find(format) == sample_factors.end()) {
146*89a0ef05SAndroid Build Coastguard Worker status.error_code = UHDR_CODEC_INVALID_PARAM;
147*89a0ef05SAndroid Build Coastguard Worker status.has_detail = 1;
148*89a0ef05SAndroid Build Coastguard Worker snprintf(status.detail, sizeof status.detail, "unrecognized input format %d", format);
149*89a0ef05SAndroid Build Coastguard Worker return status;
150*89a0ef05SAndroid Build Coastguard Worker }
151*89a0ef05SAndroid Build Coastguard Worker std::vector<int>& factors = sample_factors.find(format)->second;
152*89a0ef05SAndroid Build Coastguard Worker
153*89a0ef05SAndroid Build Coastguard Worker cinfo.err = jpeg_std_error(&myerr);
154*89a0ef05SAndroid Build Coastguard Worker myerr.error_exit = jpegrerror_exit;
155*89a0ef05SAndroid Build Coastguard Worker myerr.output_message = outputErrorMessage;
156*89a0ef05SAndroid Build Coastguard Worker
157*89a0ef05SAndroid Build Coastguard Worker if (0 == setjmp(myerr.setjmp_buffer)) {
158*89a0ef05SAndroid Build Coastguard Worker jpeg_create_compress(&cinfo);
159*89a0ef05SAndroid Build Coastguard Worker
160*89a0ef05SAndroid Build Coastguard Worker // initialize destination manager
161*89a0ef05SAndroid Build Coastguard Worker mDestMgr.init_destination = &initDestination;
162*89a0ef05SAndroid Build Coastguard Worker mDestMgr.empty_output_buffer = &emptyOutputBuffer;
163*89a0ef05SAndroid Build Coastguard Worker mDestMgr.term_destination = &terminateDestination;
164*89a0ef05SAndroid Build Coastguard Worker mDestMgr.mResultBuffer.clear();
165*89a0ef05SAndroid Build Coastguard Worker cinfo.dest = reinterpret_cast<struct jpeg_destination_mgr*>(&mDestMgr);
166*89a0ef05SAndroid Build Coastguard Worker
167*89a0ef05SAndroid Build Coastguard Worker // initialize configuration parameters
168*89a0ef05SAndroid Build Coastguard Worker cinfo.image_width = width;
169*89a0ef05SAndroid Build Coastguard Worker cinfo.image_height = height;
170*89a0ef05SAndroid Build Coastguard Worker bool isGainMapImg = true;
171*89a0ef05SAndroid Build Coastguard Worker if (format == UHDR_IMG_FMT_24bppRGB888) {
172*89a0ef05SAndroid Build Coastguard Worker cinfo.input_components = 3;
173*89a0ef05SAndroid Build Coastguard Worker cinfo.in_color_space = JCS_RGB;
174*89a0ef05SAndroid Build Coastguard Worker } else {
175*89a0ef05SAndroid Build Coastguard Worker if (format == UHDR_IMG_FMT_8bppYCbCr400) {
176*89a0ef05SAndroid Build Coastguard Worker cinfo.input_components = 1;
177*89a0ef05SAndroid Build Coastguard Worker cinfo.in_color_space = JCS_GRAYSCALE;
178*89a0ef05SAndroid Build Coastguard Worker } else if (format == UHDR_IMG_FMT_12bppYCbCr420 || format == UHDR_IMG_FMT_24bppYCbCr444 ||
179*89a0ef05SAndroid Build Coastguard Worker format == UHDR_IMG_FMT_16bppYCbCr422 || format == UHDR_IMG_FMT_16bppYCbCr440 ||
180*89a0ef05SAndroid Build Coastguard Worker format == UHDR_IMG_FMT_12bppYCbCr411 || format == UHDR_IMG_FMT_10bppYCbCr410) {
181*89a0ef05SAndroid Build Coastguard Worker cinfo.input_components = 3;
182*89a0ef05SAndroid Build Coastguard Worker cinfo.in_color_space = JCS_YCbCr;
183*89a0ef05SAndroid Build Coastguard Worker isGainMapImg = false;
184*89a0ef05SAndroid Build Coastguard Worker } else {
185*89a0ef05SAndroid Build Coastguard Worker status.error_code = UHDR_CODEC_ERROR;
186*89a0ef05SAndroid Build Coastguard Worker status.has_detail = 1;
187*89a0ef05SAndroid Build Coastguard Worker snprintf(status.detail, sizeof status.detail,
188*89a0ef05SAndroid Build Coastguard Worker "unrecognized input color format for encoding, color format %d", format);
189*89a0ef05SAndroid Build Coastguard Worker jpeg_destroy_compress(&cinfo);
190*89a0ef05SAndroid Build Coastguard Worker return status;
191*89a0ef05SAndroid Build Coastguard Worker }
192*89a0ef05SAndroid Build Coastguard Worker }
193*89a0ef05SAndroid Build Coastguard Worker jpeg_set_defaults(&cinfo);
194*89a0ef05SAndroid Build Coastguard Worker jpeg_set_quality(&cinfo, qfactor, TRUE);
195*89a0ef05SAndroid Build Coastguard Worker for (int i = 0; i < cinfo.num_components; i++) {
196*89a0ef05SAndroid Build Coastguard Worker cinfo.comp_info[i].h_samp_factor = factors[i * 2];
197*89a0ef05SAndroid Build Coastguard Worker cinfo.comp_info[i].v_samp_factor = factors[i * 2 + 1];
198*89a0ef05SAndroid Build Coastguard Worker mPlaneWidth[i] =
199*89a0ef05SAndroid Build Coastguard Worker std::ceil(((float)cinfo.image_width * cinfo.comp_info[i].h_samp_factor) / factors[6]);
200*89a0ef05SAndroid Build Coastguard Worker mPlaneHeight[i] =
201*89a0ef05SAndroid Build Coastguard Worker std::ceil(((float)cinfo.image_height * cinfo.comp_info[i].v_samp_factor) / factors[7]);
202*89a0ef05SAndroid Build Coastguard Worker }
203*89a0ef05SAndroid Build Coastguard Worker if (format != UHDR_IMG_FMT_24bppRGB888) cinfo.raw_data_in = TRUE;
204*89a0ef05SAndroid Build Coastguard Worker cinfo.dct_method = JDCT_ISLOW;
205*89a0ef05SAndroid Build Coastguard Worker
206*89a0ef05SAndroid Build Coastguard Worker // start compress
207*89a0ef05SAndroid Build Coastguard Worker jpeg_start_compress(&cinfo, TRUE);
208*89a0ef05SAndroid Build Coastguard Worker if (iccBuffer != nullptr && iccSize > 0) {
209*89a0ef05SAndroid Build Coastguard Worker jpeg_write_marker(&cinfo, JPEG_APP0 + 2, static_cast<const JOCTET*>(iccBuffer), iccSize);
210*89a0ef05SAndroid Build Coastguard Worker }
211*89a0ef05SAndroid Build Coastguard Worker if (isGainMapImg) {
212*89a0ef05SAndroid Build Coastguard Worker char comment[255];
213*89a0ef05SAndroid Build Coastguard Worker snprintf(comment, sizeof comment,
214*89a0ef05SAndroid Build Coastguard Worker "Source: google libuhdr v%s, Coder: libjpeg v%d, Attrib: GainMap Image",
215*89a0ef05SAndroid Build Coastguard Worker UHDR_LIB_VERSION_STR, JPEG_LIB_VERSION);
216*89a0ef05SAndroid Build Coastguard Worker jpeg_write_marker(&cinfo, JPEG_COM, reinterpret_cast<JOCTET*>(comment), strlen(comment));
217*89a0ef05SAndroid Build Coastguard Worker }
218*89a0ef05SAndroid Build Coastguard Worker if (format == UHDR_IMG_FMT_24bppRGB888) {
219*89a0ef05SAndroid Build Coastguard Worker while (cinfo.next_scanline < cinfo.image_height) {
220*89a0ef05SAndroid Build Coastguard Worker JSAMPROW row_pointer[]{
221*89a0ef05SAndroid Build Coastguard Worker const_cast<JSAMPROW>(&planes[0][cinfo.next_scanline * strides[0] * 3])};
222*89a0ef05SAndroid Build Coastguard Worker JDIMENSION processed = jpeg_write_scanlines(&cinfo, row_pointer, 1);
223*89a0ef05SAndroid Build Coastguard Worker if (1 != processed) {
224*89a0ef05SAndroid Build Coastguard Worker status.error_code = UHDR_CODEC_ERROR;
225*89a0ef05SAndroid Build Coastguard Worker status.has_detail = 1;
226*89a0ef05SAndroid Build Coastguard Worker snprintf(status.detail, sizeof status.detail,
227*89a0ef05SAndroid Build Coastguard Worker "jpeg_read_scanlines returned %d, expected %d", processed, 1);
228*89a0ef05SAndroid Build Coastguard Worker jpeg_destroy_compress(&cinfo);
229*89a0ef05SAndroid Build Coastguard Worker return status;
230*89a0ef05SAndroid Build Coastguard Worker }
231*89a0ef05SAndroid Build Coastguard Worker }
232*89a0ef05SAndroid Build Coastguard Worker } else {
233*89a0ef05SAndroid Build Coastguard Worker status = compressYCbCr(&cinfo, planes, strides);
234*89a0ef05SAndroid Build Coastguard Worker if (status.error_code != UHDR_CODEC_OK) {
235*89a0ef05SAndroid Build Coastguard Worker jpeg_destroy_compress(&cinfo);
236*89a0ef05SAndroid Build Coastguard Worker return status;
237*89a0ef05SAndroid Build Coastguard Worker }
238*89a0ef05SAndroid Build Coastguard Worker }
239*89a0ef05SAndroid Build Coastguard Worker } else {
240*89a0ef05SAndroid Build Coastguard Worker status.error_code = UHDR_CODEC_ERROR;
241*89a0ef05SAndroid Build Coastguard Worker status.has_detail = 1;
242*89a0ef05SAndroid Build Coastguard Worker cinfo.err->format_message((j_common_ptr)&cinfo, status.detail);
243*89a0ef05SAndroid Build Coastguard Worker jpeg_destroy_compress(&cinfo);
244*89a0ef05SAndroid Build Coastguard Worker return status;
245*89a0ef05SAndroid Build Coastguard Worker }
246*89a0ef05SAndroid Build Coastguard Worker
247*89a0ef05SAndroid Build Coastguard Worker jpeg_finish_compress(&cinfo);
248*89a0ef05SAndroid Build Coastguard Worker jpeg_destroy_compress(&cinfo);
249*89a0ef05SAndroid Build Coastguard Worker return status;
250*89a0ef05SAndroid Build Coastguard Worker }
251*89a0ef05SAndroid Build Coastguard Worker
compressYCbCr(jpeg_compress_struct * cinfo,const uint8_t * planes[3],const unsigned int strides[3])252*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t JpegEncoderHelper::compressYCbCr(jpeg_compress_struct* cinfo,
253*89a0ef05SAndroid Build Coastguard Worker const uint8_t* planes[3],
254*89a0ef05SAndroid Build Coastguard Worker const unsigned int strides[3]) {
255*89a0ef05SAndroid Build Coastguard Worker JSAMPROW mcuRows[kMaxNumComponents][2 * DCTSIZE];
256*89a0ef05SAndroid Build Coastguard Worker JSAMPROW mcuRowsTmp[kMaxNumComponents][2 * DCTSIZE];
257*89a0ef05SAndroid Build Coastguard Worker size_t alignedPlaneWidth[kMaxNumComponents]{};
258*89a0ef05SAndroid Build Coastguard Worker JSAMPARRAY subImage[kMaxNumComponents];
259*89a0ef05SAndroid Build Coastguard Worker
260*89a0ef05SAndroid Build Coastguard Worker for (int i = 0; i < cinfo->num_components; i++) {
261*89a0ef05SAndroid Build Coastguard Worker alignedPlaneWidth[i] = ALIGNM(mPlaneWidth[i], DCTSIZE);
262*89a0ef05SAndroid Build Coastguard Worker if (strides[i] < alignedPlaneWidth[i]) {
263*89a0ef05SAndroid Build Coastguard Worker mPlanesMCURow[i] = std::make_unique<uint8_t[]>(alignedPlaneWidth[i] * DCTSIZE *
264*89a0ef05SAndroid Build Coastguard Worker cinfo->comp_info[i].v_samp_factor);
265*89a0ef05SAndroid Build Coastguard Worker uint8_t* mem = mPlanesMCURow[i].get();
266*89a0ef05SAndroid Build Coastguard Worker for (int j = 0; j < DCTSIZE * cinfo->comp_info[i].v_samp_factor;
267*89a0ef05SAndroid Build Coastguard Worker j++, mem += alignedPlaneWidth[i]) {
268*89a0ef05SAndroid Build Coastguard Worker mcuRowsTmp[i][j] = mem;
269*89a0ef05SAndroid Build Coastguard Worker if (i > 0) {
270*89a0ef05SAndroid Build Coastguard Worker memset(mem + mPlaneWidth[i], 128, alignedPlaneWidth[i] - mPlaneWidth[i]);
271*89a0ef05SAndroid Build Coastguard Worker }
272*89a0ef05SAndroid Build Coastguard Worker }
273*89a0ef05SAndroid Build Coastguard Worker } else if (mPlaneHeight[i] % DCTSIZE != 0) {
274*89a0ef05SAndroid Build Coastguard Worker mPlanesMCURow[i] = std::make_unique<uint8_t[]>(alignedPlaneWidth[i]);
275*89a0ef05SAndroid Build Coastguard Worker if (i > 0) {
276*89a0ef05SAndroid Build Coastguard Worker memset(mPlanesMCURow[i].get(), 128, alignedPlaneWidth[i]);
277*89a0ef05SAndroid Build Coastguard Worker }
278*89a0ef05SAndroid Build Coastguard Worker }
279*89a0ef05SAndroid Build Coastguard Worker subImage[i] = strides[i] < alignedPlaneWidth[i] ? mcuRowsTmp[i] : mcuRows[i];
280*89a0ef05SAndroid Build Coastguard Worker }
281*89a0ef05SAndroid Build Coastguard Worker
282*89a0ef05SAndroid Build Coastguard Worker while (cinfo->next_scanline < cinfo->image_height) {
283*89a0ef05SAndroid Build Coastguard Worker JDIMENSION mcu_scanline_start[kMaxNumComponents];
284*89a0ef05SAndroid Build Coastguard Worker
285*89a0ef05SAndroid Build Coastguard Worker for (int i = 0; i < cinfo->num_components; i++) {
286*89a0ef05SAndroid Build Coastguard Worker mcu_scanline_start[i] =
287*89a0ef05SAndroid Build Coastguard Worker std::ceil(((float)cinfo->next_scanline * cinfo->comp_info[i].v_samp_factor) /
288*89a0ef05SAndroid Build Coastguard Worker cinfo->max_v_samp_factor);
289*89a0ef05SAndroid Build Coastguard Worker
290*89a0ef05SAndroid Build Coastguard Worker for (int j = 0; j < cinfo->comp_info[i].v_samp_factor * DCTSIZE; j++) {
291*89a0ef05SAndroid Build Coastguard Worker JDIMENSION scanline = mcu_scanline_start[i] + j;
292*89a0ef05SAndroid Build Coastguard Worker
293*89a0ef05SAndroid Build Coastguard Worker if (scanline < mPlaneHeight[i]) {
294*89a0ef05SAndroid Build Coastguard Worker mcuRows[i][j] = const_cast<uint8_t*>(planes[i] + (size_t)scanline * strides[i]);
295*89a0ef05SAndroid Build Coastguard Worker if (strides[i] < alignedPlaneWidth[i]) {
296*89a0ef05SAndroid Build Coastguard Worker memcpy(mcuRowsTmp[i][j], mcuRows[i][j], mPlaneWidth[i]);
297*89a0ef05SAndroid Build Coastguard Worker }
298*89a0ef05SAndroid Build Coastguard Worker } else {
299*89a0ef05SAndroid Build Coastguard Worker mcuRows[i][j] = mPlanesMCURow[i].get();
300*89a0ef05SAndroid Build Coastguard Worker }
301*89a0ef05SAndroid Build Coastguard Worker }
302*89a0ef05SAndroid Build Coastguard Worker }
303*89a0ef05SAndroid Build Coastguard Worker int processed = jpeg_write_raw_data(cinfo, subImage, DCTSIZE * cinfo->max_v_samp_factor);
304*89a0ef05SAndroid Build Coastguard Worker if (processed != DCTSIZE * cinfo->max_v_samp_factor) {
305*89a0ef05SAndroid Build Coastguard Worker uhdr_error_info_t status;
306*89a0ef05SAndroid Build Coastguard Worker status.error_code = UHDR_CODEC_ERROR;
307*89a0ef05SAndroid Build Coastguard Worker status.has_detail = 1;
308*89a0ef05SAndroid Build Coastguard Worker snprintf(status.detail, sizeof status.detail,
309*89a0ef05SAndroid Build Coastguard Worker "number of scan lines processed %d does not equal requested scan lines %d ",
310*89a0ef05SAndroid Build Coastguard Worker processed, DCTSIZE * cinfo->max_v_samp_factor);
311*89a0ef05SAndroid Build Coastguard Worker return status;
312*89a0ef05SAndroid Build Coastguard Worker }
313*89a0ef05SAndroid Build Coastguard Worker }
314*89a0ef05SAndroid Build Coastguard Worker return g_no_error;
315*89a0ef05SAndroid Build Coastguard Worker }
316*89a0ef05SAndroid Build Coastguard Worker
317*89a0ef05SAndroid Build Coastguard Worker } // namespace ultrahdr
318