1/*
2 * Copyright (C) Texas Instruments - http://www.ti.com/
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/**
18* @file Encoder_libjpeg.cpp
19*
20* This file encodes a YUV422I buffer to a jpeg
21* TODO(XXX): Need to support formats other than yuv422i
22*            Change interface to pre/post-proc algo framework
23*
24*/
25
26#include "Encoder_libjpeg.h"
27#include "NV12_resize.h"
28#include "TICameraParameters.h"
29
30#include <stdlib.h>
31#include <unistd.h>
32#include <sys/types.h>
33#include <sys/stat.h>
34#include <fcntl.h>
35#include <stdio.h>
36#include <errno.h>
37#include <math.h>
38
39extern "C" {
40    #include "jpeglib.h"
41    #include "jerror.h"
42}
43
44#define ARRAY_SIZE(array) (sizeof((array)) / sizeof((array)[0]))
45#define MIN(x,y) ((x < y) ? x : y)
46
47namespace Ti {
48namespace Camera {
49
50struct integer_string_pair {
51    unsigned int integer;
52    const char* string;
53};
54
55static integer_string_pair degress_to_exif_lut [] = {
56    // degrees, exif_orientation
57    {0,   "1"},
58    {90,  "6"},
59    {180, "3"},
60    {270, "8"},
61};
62struct libjpeg_destination_mgr : jpeg_destination_mgr {
63    libjpeg_destination_mgr(uint8_t* input, int size);
64
65    uint8_t* buf;
66    int bufsize;
67    size_t jpegsize;
68};
69
70static void libjpeg_init_destination (j_compress_ptr cinfo) {
71    libjpeg_destination_mgr* dest = (libjpeg_destination_mgr*)cinfo->dest;
72
73    dest->next_output_byte = dest->buf;
74    dest->free_in_buffer = dest->bufsize;
75    dest->jpegsize = 0;
76}
77
78static boolean libjpeg_empty_output_buffer(j_compress_ptr cinfo) {
79    libjpeg_destination_mgr* dest = (libjpeg_destination_mgr*)cinfo->dest;
80
81    dest->next_output_byte = dest->buf;
82    dest->free_in_buffer = dest->bufsize;
83    return TRUE; // ?
84}
85
86static void libjpeg_term_destination (j_compress_ptr cinfo) {
87    libjpeg_destination_mgr* dest = (libjpeg_destination_mgr*)cinfo->dest;
88    dest->jpegsize = dest->bufsize - dest->free_in_buffer;
89}
90
91libjpeg_destination_mgr::libjpeg_destination_mgr(uint8_t* input, int size) {
92    this->init_destination = libjpeg_init_destination;
93    this->empty_output_buffer = libjpeg_empty_output_buffer;
94    this->term_destination = libjpeg_term_destination;
95
96    this->buf = input;
97    this->bufsize = size;
98
99    jpegsize = 0;
100}
101
102/* private static functions */
103static void nv21_to_yuv(uint8_t* dst, uint8_t* y, uint8_t* uv, int width) {
104    if (!dst || !y || !uv) {
105        return;
106    }
107
108    while ((width--) > 0) {
109        uint8_t y0 = y[0];
110        uint8_t v0 = uv[0];
111        uint8_t u0 = *(uv+1);
112        dst[0] = y0;
113        dst[1] = u0;
114        dst[2] = v0;
115        dst += 3;
116        y++;
117        if(!(width % 2)) uv+=2;
118    }
119}
120
121static void uyvy_to_yuv(uint8_t* dst, uint32_t* src, int width) {
122    if (!dst || !src) {
123        return;
124    }
125
126    if (width % 2) {
127        return; // not supporting odd widths
128    }
129
130    // currently, neon routine only supports multiple of 16 width
131    if (width % 16) {
132        while ((width-=2) >= 0) {
133            uint8_t u0 = (src[0] >> 0) & 0xFF;
134            uint8_t y0 = (src[0] >> 8) & 0xFF;
135            uint8_t v0 = (src[0] >> 16) & 0xFF;
136            uint8_t y1 = (src[0] >> 24) & 0xFF;
137            dst[0] = y0;
138            dst[1] = u0;
139            dst[2] = v0;
140            dst[3] = y1;
141            dst[4] = u0;
142            dst[5] = v0;
143            dst += 6;
144            src++;
145        }
146    } else {
147        int n = width;
148        asm volatile (
149        "   pld [%[src], %[src_stride], lsl #2]                         \n\t"
150        "   cmp %[n], #16                                               \n\t"
151        "   blt 5f                                                      \n\t"
152        "0: @ 16 pixel swap                                             \n\t"
153        "   vld2.8  {q0, q1} , [%[src]]! @ q0 = uv q1 = y               \n\t"
154        "   vuzp.8 q0, q2                @ d0 = u d4 = v                \n\t"
155        "   vmov d1, d0                  @ q0 = u0u1u2..u0u1u2...       \n\t"
156        "   vmov d5, d4                  @ q2 = v0v1v2..v0v1v2...       \n\t"
157        "   vzip.8 d0, d1                @ q0 = u0u0u1u1u2u2...         \n\t"
158        "   vzip.8 d4, d5                @ q2 = v0v0v1v1v2v2...         \n\t"
159        "   vswp q0, q1                  @ now q0 = y q1 = u q2 = v     \n\t"
160        "   vst3.8  {d0,d2,d4},[%[dst]]!                                \n\t"
161        "   vst3.8  {d1,d3,d5},[%[dst]]!                                \n\t"
162        "   sub %[n], %[n], #16                                         \n\t"
163        "   cmp %[n], #16                                               \n\t"
164        "   bge 0b                                                      \n\t"
165        "5: @ end                                                       \n\t"
166#ifdef NEEDS_ARM_ERRATA_754319_754320
167        "   vmov s0,s0  @ add noop for errata item                      \n\t"
168#endif
169        : [dst] "+r" (dst), [src] "+r" (src), [n] "+r" (n)
170        : [src_stride] "r" (width)
171        : "cc", "memory", "q0", "q1", "q2"
172        );
173    }
174}
175
176static void yuyv_to_yuv(uint8_t* dst, uint32_t* src, int width) {
177    if (!dst || !src) {
178        return;
179    }
180
181    if (width % 2) {
182        return; // not supporting odd widths
183    }
184
185    // currently, neon routine only supports multiple of 16 width
186    if (width % 16) {
187        while ((width-=2) >= 0) {
188            uint8_t y0 = (src[0] >> 0) & 0xFF;
189            uint8_t u0 = (src[0] >> 8) & 0xFF;
190            uint8_t y1 = (src[0] >> 16) & 0xFF;
191            uint8_t v0 = (src[0] >> 24) & 0xFF;
192            dst[0] = y0;
193            dst[1] = u0;
194            dst[2] = v0;
195            dst[3] = y1;
196            dst[4] = u0;
197            dst[5] = v0;
198            dst += 6;
199            src++;
200        }
201    } else {
202        int n = width;
203        asm volatile (
204        "   pld [%[src], %[src_stride], lsl #2]                         \n\t"
205        "   cmp %[n], #16                                               \n\t"
206        "   blt 5f                                                      \n\t"
207        "0: @ 16 pixel swap                                             \n\t"
208        "   vld2.8  {q0, q1} , [%[src]]! @ q0 = yyyy.. q1 = uvuv..      \n\t"
209        "   vuzp.8 q1, q2                @ d2 = u d4 = v                \n\t"
210        "   vmov d3, d2                  @ q1 = u0u1u2..u0u1u2...       \n\t"
211        "   vmov d5, d4                  @ q2 = v0v1v2..v0v1v2...       \n\t"
212        "   vzip.8 d2, d3                @ q1 = u0u0u1u1u2u2...         \n\t"
213        "   vzip.8 d4, d5                @ q2 = v0v0v1v1v2v2...         \n\t"
214        "                                @ now q0 = y q1 = u q2 = v     \n\t"
215        "   vst3.8  {d0,d2,d4},[%[dst]]!                                \n\t"
216        "   vst3.8  {d1,d3,d5},[%[dst]]!                                \n\t"
217        "   sub %[n], %[n], #16                                         \n\t"
218        "   cmp %[n], #16                                               \n\t"
219        "   bge 0b                                                      \n\t"
220        "5: @ end                                                       \n\t"
221#ifdef NEEDS_ARM_ERRATA_754319_754320
222        "   vmov s0,s0  @ add noop for errata item                      \n\t"
223#endif
224        : [dst] "+r" (dst), [src] "+r" (src), [n] "+r" (n)
225        : [src_stride] "r" (width)
226        : "cc", "memory", "q0", "q1", "q2"
227        );
228    }
229}
230
231static void resize_nv12(Encoder_libjpeg::params* params, uint8_t* dst_buffer) {
232    structConvImage o_img_ptr, i_img_ptr;
233
234    if (!params || !dst_buffer) {
235        return;
236    }
237
238    //input
239    i_img_ptr.uWidth =  params->in_width;
240    i_img_ptr.uStride =  i_img_ptr.uWidth;
241    i_img_ptr.uHeight =  params->in_height;
242    i_img_ptr.eFormat = IC_FORMAT_YCbCr420_lp;
243    i_img_ptr.imgPtr = (uint8_t*) params->src;
244    i_img_ptr.clrPtr = i_img_ptr.imgPtr + (i_img_ptr.uWidth * i_img_ptr.uHeight);
245    i_img_ptr.uOffset = 0;
246
247    //ouput
248    o_img_ptr.uWidth = params->out_width;
249    o_img_ptr.uStride = o_img_ptr.uWidth;
250    o_img_ptr.uHeight = params->out_height;
251    o_img_ptr.eFormat = IC_FORMAT_YCbCr420_lp;
252    o_img_ptr.imgPtr = dst_buffer;
253    o_img_ptr.clrPtr = o_img_ptr.imgPtr + (o_img_ptr.uWidth * o_img_ptr.uHeight);
254    o_img_ptr.uOffset = 0;
255
256    VT_resizeFrame_Video_opt2_lp(&i_img_ptr, &o_img_ptr, NULL, 0);
257}
258
259/* public static functions */
260const char* ExifElementsTable::degreesToExifOrientation(unsigned int degrees) {
261    for (unsigned int i = 0; i < ARRAY_SIZE(degress_to_exif_lut); i++) {
262        if (degrees == degress_to_exif_lut[i].integer) {
263            return degress_to_exif_lut[i].string;
264        }
265    }
266    return NULL;
267}
268
269void ExifElementsTable::stringToRational(const char* str, unsigned int* num, unsigned int* den) {
270    int len;
271    char * tempVal = NULL;
272
273    if (str != NULL) {
274        len = strlen(str);
275        tempVal = (char*) malloc( sizeof(char) * (len + 1));
276    }
277
278    if (tempVal != NULL) {
279        // convert the decimal string into a rational
280        size_t den_len;
281        char *ctx;
282        unsigned int numerator = 0;
283        unsigned int denominator = 0;
284        char* temp = NULL;
285
286        memset(tempVal, '\0', len + 1);
287        strncpy(tempVal, str, len);
288        temp = strtok_r(tempVal, ".", &ctx);
289
290        if (temp != NULL)
291            numerator = atoi(temp);
292
293        if (!numerator)
294            numerator = 1;
295
296        temp = strtok_r(NULL, ".", &ctx);
297        if (temp != NULL) {
298            den_len = strlen(temp);
299            if(HUGE_VAL == den_len ) {
300                den_len = 0;
301            }
302
303            denominator = static_cast<unsigned int>(pow(10, den_len));
304            numerator = numerator * denominator + atoi(temp);
305        } else {
306            denominator = 1;
307        }
308
309        free(tempVal);
310
311        *num = numerator;
312        *den = denominator;
313    }
314}
315
316bool ExifElementsTable::isAsciiTag(const char* tag) {
317    // TODO(XXX): Add tags as necessary
318    return (strcmp(tag, TAG_GPS_PROCESSING_METHOD) == 0);
319}
320
321void ExifElementsTable::insertExifToJpeg(unsigned char* jpeg, size_t jpeg_size) {
322    ReadMode_t read_mode = (ReadMode_t)(READ_METADATA | READ_IMAGE);
323
324    ResetJpgfile();
325    if (ReadJpegSectionsFromBuffer(jpeg, jpeg_size, read_mode)) {
326        jpeg_opened = true;
327#ifdef ANDROID_API_JB_OR_LATER
328        create_EXIF(table, exif_tag_count, gps_tag_count, has_datetime_tag);
329#else
330        create_EXIF(table, exif_tag_count, gps_tag_count);
331#endif
332    }
333}
334
335status_t ExifElementsTable::insertExifThumbnailImage(const char* thumb, int len) {
336    status_t ret = NO_ERROR;
337
338    if ((len > 0) && jpeg_opened) {
339        ret = ReplaceThumbnailFromBuffer(thumb, len) ? NO_ERROR : UNKNOWN_ERROR;
340        CAMHAL_LOGDB("insertExifThumbnailImage. ReplaceThumbnail(). ret=%d", ret);
341    }
342
343    return ret;
344}
345
346void ExifElementsTable::saveJpeg(unsigned char* jpeg, size_t jpeg_size) {
347    if (jpeg_opened) {
348       WriteJpegToBuffer(jpeg, jpeg_size);
349       DiscardData();
350       jpeg_opened = false;
351    }
352}
353
354/* public functions */
355ExifElementsTable::~ExifElementsTable() {
356    int num_elements = gps_tag_count + exif_tag_count;
357
358    for (int i = 0; i < num_elements; i++) {
359        if (table[i].Value) {
360            free(table[i].Value);
361        }
362    }
363
364    if (jpeg_opened) {
365        DiscardData();
366    }
367}
368
369status_t ExifElementsTable::insertElement(const char* tag, const char* value) {
370    unsigned int value_length = 0;
371    status_t ret = NO_ERROR;
372
373    if (!value || !tag) {
374        return -EINVAL;
375    }
376
377    if (position >= MAX_EXIF_TAGS_SUPPORTED) {
378        CAMHAL_LOGEA("Max number of EXIF elements already inserted");
379        return NO_MEMORY;
380    }
381
382    if (isAsciiTag(tag)) {
383        value_length = sizeof(ExifAsciiPrefix) + strlen(value + sizeof(ExifAsciiPrefix));
384    } else {
385        value_length = strlen(value);
386    }
387
388    if (IsGpsTag(tag)) {
389        table[position].GpsTag = TRUE;
390        table[position].Tag = GpsTagNameToValue(tag);
391        gps_tag_count++;
392    } else {
393        table[position].GpsTag = FALSE;
394        table[position].Tag = TagNameToValue(tag);
395        exif_tag_count++;
396
397        if (strcmp(tag, TAG_DATETIME) == 0) {
398#ifdef ANDROID_API_JB_OR_LATER
399            has_datetime_tag = true;
400#else
401            // jhead isn't taking datetime tag...this is a WA
402            ImageInfo.numDateTimeTags = 1;
403            memcpy(ImageInfo.DateTime, value,
404                   MIN(ARRAY_SIZE(ImageInfo.DateTime), value_length + 1));
405#endif
406        }
407    }
408
409    table[position].DataLength = 0;
410    table[position].Value = (char*) malloc(sizeof(char) * (value_length + 1));
411
412    if (table[position].Value) {
413        memcpy(table[position].Value, value, value_length + 1);
414        table[position].DataLength = value_length + 1;
415    }
416
417    position++;
418    return ret;
419}
420
421/* private member functions */
422size_t Encoder_libjpeg::encode(params* input) {
423    jpeg_compress_struct    cinfo;
424    jpeg_error_mgr jerr;
425    jpeg_destination_mgr jdest;
426    uint8_t* src = NULL, *resize_src = NULL;
427    uint8_t* row_tmp = NULL;
428    uint8_t* row_src = NULL;
429    uint8_t* row_uv = NULL; // used only for NV12
430    int out_width = 0, in_width = 0;
431    int out_height = 0, in_height = 0;
432    int bpp = 2; // for uyvy
433    int right_crop = 0, start_offset = 0;
434
435    if (!input) {
436        return 0;
437    }
438
439    out_width = input->out_width;
440    in_width = input->in_width;
441    out_height = input->out_height;
442    in_height = input->in_height;
443    right_crop = input->right_crop;
444    start_offset = input->start_offset;
445    src = input->src;
446    input->jpeg_size = 0;
447
448    libjpeg_destination_mgr dest_mgr(input->dst, input->dst_size);
449
450    // param check...
451    if ((in_width < 2) || (out_width < 2) || (in_height < 2) || (out_height < 2) ||
452         (src == NULL) || (input->dst == NULL) || (input->quality < 1) || (input->src_size < 1) ||
453         (input->dst_size < 1) || (input->format == NULL)) {
454        goto exit;
455    }
456
457    if (strcmp(input->format, android::CameraParameters::PIXEL_FORMAT_YUV420SP) == 0) {
458        bpp = 1;
459        if ((in_width != out_width) || (in_height != out_height)) {
460            resize_src = (uint8_t*) malloc(input->dst_size);
461            resize_nv12(input, resize_src);
462            if (resize_src) src = resize_src;
463        }
464    } else if (strcmp(input->format, android::CameraParameters::PIXEL_FORMAT_YUV422I) &&
465               strcmp(input->format, TICameraParameters::PIXEL_FORMAT_YUV422I_UYVY)) {
466        // we currently only support yuv422i and yuv420sp
467        CAMHAL_LOGEB("Encoder: format not supported: %s", input->format);
468        goto exit;
469    } else if ((in_width != out_width) || (in_height != out_height)) {
470        CAMHAL_LOGEB("Encoder: resizing is not supported for this format: %s", input->format);
471        goto exit;
472    }
473
474    cinfo.err = jpeg_std_error(&jerr);
475
476    jpeg_create_compress(&cinfo);
477
478    CAMHAL_LOGDB("encoding...  \n\t"
479                 "width: %d    \n\t"
480                 "height:%d    \n\t"
481                 "dest %p      \n\t"
482                 "dest size:%d \n\t"
483                 "mSrc %p \n\t"
484                 "format: %s",
485                 out_width, out_height, input->dst,
486                 input->dst_size, src, input->format);
487
488    cinfo.dest = &dest_mgr;
489    cinfo.image_width = out_width - right_crop;
490    cinfo.image_height = out_height;
491    cinfo.input_components = 3;
492    cinfo.in_color_space = JCS_YCbCr;
493    cinfo.input_gamma = 1;
494
495    jpeg_set_defaults(&cinfo);
496    jpeg_set_quality(&cinfo, input->quality, TRUE);
497    cinfo.dct_method = JDCT_IFAST;
498
499    jpeg_start_compress(&cinfo, TRUE);
500
501    row_tmp = (uint8_t*)malloc((out_width - right_crop) * 3);
502    row_src = src + start_offset;
503    row_uv = src + out_width * out_height * bpp;
504
505    while ((cinfo.next_scanline < cinfo.image_height) && !mCancelEncoding) {
506        JSAMPROW row[1];    /* pointer to JSAMPLE row[s] */
507
508        // convert input yuv format to yuv444
509        if (strcmp(input->format, android::CameraParameters::PIXEL_FORMAT_YUV420SP) == 0) {
510            nv21_to_yuv(row_tmp, row_src, row_uv, out_width - right_crop);
511        } else if (strcmp(input->format, TICameraParameters::PIXEL_FORMAT_YUV422I_UYVY) == 0) {
512            uyvy_to_yuv(row_tmp, (uint32_t*)row_src, out_width - right_crop);
513        } else if (strcmp(input->format, android::CameraParameters::PIXEL_FORMAT_YUV422I) == 0) {
514            yuyv_to_yuv(row_tmp, (uint32_t*)row_src, out_width - right_crop);
515        }
516
517        row[0] = row_tmp;
518        jpeg_write_scanlines(&cinfo, row, 1);
519        row_src = row_src + out_width*bpp;
520
521        // move uv row if input format needs it
522        if (strcmp(input->format, android::CameraParameters::PIXEL_FORMAT_YUV420SP) == 0) {
523            if (!(cinfo.next_scanline % 2))
524                row_uv = row_uv +  out_width * bpp;
525        }
526    }
527
528    // no need to finish encoding routine if we are prematurely stopping
529    // we will end up crashing in dest_mgr since data is incomplete
530    if (!mCancelEncoding)
531        jpeg_finish_compress(&cinfo);
532    jpeg_destroy_compress(&cinfo);
533
534    if (resize_src) free(resize_src);
535    if (row_tmp) free(row_tmp);
536
537 exit:
538    input->jpeg_size = dest_mgr.jpegsize;
539    return dest_mgr.jpegsize;
540}
541
542} // namespace Camera
543} // namespace Ti
544