/* * Copyright (c) 2020, Actions Semi Co., Inc. * * SPDX-License-Identifier: Apache-2.0 */ /** ****************************************************************************** * @file jpeg_parser.c * @brief JPEG HAL module driver. * This file provides firmware functions to manage the following * functionalities of the JPEG HW decoder peripheral: * + Initialization and de-initialization functions * + IO operation functions * + Peripheral Control functions * + Peripheral State and Errors functions * */ #include #include #include #include const int exiftag = 0x66697845; //'e''x''i''f' const uint8_t zigzag[64]= { 0, 1, 8,16, 9, 2, 3,10, 17,24,32,25,18,11, 4, 5, 12,19,26,33,40,48,41,34, 27,20,13, 6, 7,14,21,28, 35,42,49,56,57,50,43,36, 29,22,15,23,30,37,44,51, 58,59,52,45,38,31,39,46, 53,60,61,54,47,55,62,63 }; int JFREAD(jpeg_parser_info_t *parser_info, void *buf, int len) { if (parser_info->jpeg_current_offset + len > parser_info->jpeg_size) { len = parser_info->jpeg_size - parser_info->jpeg_current_offset; } memcpy(buf, &parser_info->jpeg_base[parser_info->jpeg_current_offset], len); parser_info->jpeg_current_offset += len; return len; } #define JFTELL(parser_info) parser_info->jpeg_current_offset #define JFOFFSET(parser_info) parser_info->jpeg_current_offset /********************************************************** * get one bytes from jpeg *********************************************************** **/ static inline uint8_t _jpeg_parser_getbyte(struct jpeg_parser_info *parser_info) { uint8_t rval =0; if (JFREAD(parser_info, &rval, 1) == 0) { parser_info->nodata = 1; } return rval; } /********************************************************** * get two bytes from jpeg *********************************************************** **/ static inline int _jpeg_parser_get2bytes(struct jpeg_parser_info *parser_info) { int len = (_jpeg_parser_getbyte(parser_info)<<8); len |= (int)_jpeg_parser_getbyte(parser_info); return len; } static inline int _jpeg_parser_get2bytesL(struct jpeg_parser_info *parser_info) { int len = _jpeg_parser_getbyte(parser_info); len |= (_jpeg_parser_getbyte(parser_info)<<8); return len; } /********************************************************** * get four bytes from jpeg *********************************************************** **/ static inline int _jpeg_parser_get4bytes(struct jpeg_parser_info *parser_info) { int len = (_jpeg_parser_getbyte(parser_info) << 24); len |= (_jpeg_parser_getbyte(parser_info) << 16); len |= (_jpeg_parser_getbyte(parser_info) << 8); len |= (int)_jpeg_parser_getbyte(parser_info); return len; } static inline int _jpeg_parser_get4bytesL(struct jpeg_parser_info *parser_info) { int len = _jpeg_parser_getbyte(parser_info); len |= (_jpeg_parser_getbyte(parser_info) << 8); len |= (_jpeg_parser_getbyte(parser_info) << 16); len |= (_jpeg_parser_getbyte(parser_info) << 24); return len; } /********************************************************** * skip len data *********************************************************** **/ static int _jpeg_parser_skipbytes(struct jpeg_parser_info *parser_info, uint32_t len) { #define TMP_BUF_LEN 128 uint8_t tmpbuf[TMP_BUF_LEN]; int rnum =0; while(len > 0) { if (len > TMP_BUF_LEN) { rnum = JFREAD(parser_info, tmpbuf, TMP_BUF_LEN); if (rnum != TMP_BUF_LEN) { parser_info->nodata = 1; break; } len -= TMP_BUF_LEN; } else { rnum = JFREAD(parser_info, tmpbuf, len); if (rnum != len) { parser_info->nodata = 1; } len = 0; } } return 0; } static int _jpeg_parser_get_data(struct jpeg_parser_info *parser_info, uint8_t *buf , uint32_t len) { int rnum = JFREAD(parser_info, buf, len); if (rnum != len) { parser_info->nodata = 1; } return rnum; } static int _jpeg_parser_mark_skip(struct jpeg_parser_info *parser_info) { int section_len = _jpeg_parser_get2bytes(parser_info) - 2; _jpeg_parser_skipbytes(parser_info, section_len); return EN_NORMAL; } /********************************************************** * jpeg deal for 0xc0 mark *********************************************************** **/ static int _jpeg_parser_sof0(struct jpeg_parser_info *parser_info) { struct jpeg_info_t *jpeg_info = &parser_info->jpeg_info; int sof_len=0; int i=0; int w=0; int h=0; int Nnum=0; int HV=0; int H[3] = {0}; int V[3]= {0}; sof_len = _jpeg_parser_get2bytes(parser_info); _jpeg_parser_getbyte(parser_info); //x,y h = _jpeg_parser_get2bytes(parser_info); w = _jpeg_parser_get2bytes(parser_info); Nnum = _jpeg_parser_getbyte(parser_info); if (Nnum > 3) { return EN_NOSUPPORT; } sof_len -= 8; for (i = 0; i < Nnum; i++) { _jpeg_parser_getbyte(parser_info); HV = _jpeg_parser_getbyte(parser_info); H[i] = HV >> 4; V[i] = HV & 0xf; _jpeg_parser_getbyte(parser_info); sof_len -= 3; } jpeg_info->yuv_mode = YUV_OTHER; if ((H[0]==1)&&(V[0]==1)&&(Nnum == 1)) { jpeg_info->yuv_mode = YUV100; } if ((H[0]==2)&&(V[0]==2)&&(H[1]==1)&&(V[1]==1)&&(H[2]==1)&&(V[2]==1)) { jpeg_info->yuv_mode = YUV420; } if ((H[0]==2)&&(V[0]==2)&&(H[1]==1)&&(V[1]==2)&&(H[2]==1)&&(V[2]==2)) { jpeg_info->yuv_mode = YUV422; } if ((H[0]==2)&&(V[0]==1)&&(H[1]==1)&&(V[1]==1)&&(H[2]==1)&&(V[2]==1)) { jpeg_info->yuv_mode = YUV211H; } if ((H[0]==1)&&(V[0]==2)&&(H[1]==1)&&(V[1]==1)&&(H[2]==1)&&(V[2]==1)) { jpeg_info->yuv_mode = YUV211V; } if ((H[0]==1)&&(V[0]==1)&&(H[1]==1)&&(V[1]==1)&&(H[2]==1)&&(V[2]==1)) { jpeg_info->yuv_mode = YUV111; } if (sof_len > 0) { _jpeg_parser_skipbytes(parser_info, sof_len); } jpeg_info->image_w = w; jpeg_info->image_h = h; return EN_NORMAL; } /********************************************************** * jpeg deal for 0xe1 mark ***********************************************************/ static int _jpeg_parser_app1(struct jpeg_parser_info *parser_info) { struct jpeg_info_t *jpeg_info = &parser_info->jpeg_info; int app1_len =0; int idf_entry=0; int cur_offset=0; int idf1_offset=0; int i=0; int tag=0; int start_file_pos=0; int thumbnail_pos=0; int DateTimeValueOffset=0; int byteorder = 0; int (*get2b)(struct jpeg_parser_info *) = 0; int (*get4b)(struct jpeg_parser_info *) = 0; int ifdoffset = 0; cur_offset = 0; //len app1_len = _jpeg_parser_get2bytes(parser_info); if (app1_len < 6) { return EN_NORMAL; } //exif tag = _jpeg_parser_get4bytesL(parser_info); if ((tag != exiftag) || (app1_len <= 8)) { _jpeg_parser_skipbytes(parser_info, app1_len - 2 - 4); //printk("tag %x exiftag %x \n",tag,exiftag); return EN_NORMAL; } //00 00 _jpeg_parser_skipbytes(parser_info, 2); //offset start start_file_pos = JFTELL(parser_info); byteorder = _jpeg_parser_get2bytes(parser_info); if (byteorder == 0x4d4d) { get2b = _jpeg_parser_get2bytes; //big get4b = _jpeg_parser_get4bytes; } else { get2b = _jpeg_parser_get2bytesL; //little get4b = _jpeg_parser_get4bytesL; } //_jpeg_parser_skipbytes(pjpg, 6); _jpeg_parser_skipbytes(parser_info, 2); ifdoffset = get4b(parser_info); if (ifdoffset != 0x08) { _jpeg_parser_skipbytes(parser_info, app1_len - 8 - 8); //printk("ifdoffset %x \n",ifdoffset); return EN_NORMAL; } //IDF0 idf_entry = get2b(parser_info); for (i = 0;i < idf_entry; i++) { tag = get2b(parser_info); if (tag == 0x0132) { _jpeg_parser_skipbytes(parser_info, 6); DateTimeValueOffset = get4b(parser_info); continue; } _jpeg_parser_skipbytes(parser_info, 10); } cur_offset += 10 + idf_entry * 12; idf1_offset = get2b(parser_info); cur_offset += 2; if (idf1_offset == 0) //some pic have padding??? { idf1_offset = get2b(parser_info); cur_offset += 2; if (idf1_offset == 0) { _jpeg_parser_skipbytes(parser_info, app1_len - (cur_offset + 8)); //printk("ifdoffset %x \n",idf1_offset); return EN_NORMAL; } } //IDF1 if ((DateTimeValueOffset == 0) || (DateTimeValueOffset > idf1_offset)) { _jpeg_parser_skipbytes(parser_info, idf1_offset - cur_offset); } else { _jpeg_parser_skipbytes(parser_info, DateTimeValueOffset - cur_offset); //get time _jpeg_parser_get_data(parser_info, jpeg_info->Date, 20); _jpeg_parser_skipbytes(parser_info, idf1_offset - DateTimeValueOffset - 20); } cur_offset = idf1_offset; idf_entry = get2b(parser_info); for(i = 0;i < idf_entry; i++) { tag = get2b(parser_info); if (tag == 0x0201) { _jpeg_parser_skipbytes(parser_info, 6); thumbnail_pos = get4b(parser_info); continue; } _jpeg_parser_skipbytes(parser_info, 10); } cur_offset += 2 + (idf_entry) * 12; if (DateTimeValueOffset > idf1_offset) { _jpeg_parser_skipbytes(parser_info, DateTimeValueOffset - cur_offset); //get time _jpeg_parser_get_data(parser_info, jpeg_info->Date, 20); app1_len -= (DateTimeValueOffset + 20 + 8); } else { app1_len -= (idf1_offset + 8); app1_len -= (2 + (idf_entry)*12); } if (app1_len > 0) { _jpeg_parser_skipbytes(parser_info, app1_len); } parser_info->thumbnailoffset = thumbnail_pos + start_file_pos; //start_file_pos = JFTELL(parser_info); return EN_NORMAL; } /********************************************************** * jpeg deal for 0xc4 mark *********************************************************** **/ static int _jpeg_parser_dht(struct jpeg_parser_info *parser_info) { struct jpeg_info_t *jpeg_info = &parser_info->jpeg_info; uint8_t *curtable=0; uint8_t *curvaltable=0; int dht_len=0; int len=0; int i=0; uint8_t tcth=0; dht_len = _jpeg_parser_get2bytes(parser_info); dht_len -= 2; //get len while(dht_len > 16) { //len i tcth = _jpeg_parser_getbyte(parser_info); if (tcth & 0xf0) { if (tcth & 0x0f) { curtable = &jpeg_info->AC_TAB1[0]; curvaltable = (uint8_t*)ACHuf_1; } else { curtable = &jpeg_info->AC_TAB0[0]; curvaltable = (uint8_t*)ACHuf_0; } } else { if (tcth & 0x0f) { curtable = &jpeg_info->DC_TAB1[0]; curvaltable = (uint8_t*)DCHuf_1; } else { curtable = &jpeg_info->DC_TAB0[0]; curvaltable = (uint8_t*)DCHuf_0; } } _jpeg_parser_get_data(parser_info, curtable, 16); len = 0; for(i = 0;i < 16;i++) { len += curtable[i]; } //val i _jpeg_parser_get_data(parser_info, curvaltable, len); dht_len -= (len + 17); } if (dht_len) { _jpeg_parser_skipbytes(parser_info, dht_len); } #if 0 printk("ACHuf_0 table: \n"); for(i = 0; i < 64; i++){ printk("0x%x ",*(uint8_t *)(ACHuf_0 + i)); } printk("\n"); printk("ACHuf_1 table: \n"); for(i = 0; i < 64; i++){ printk("0x%x ",*(uint8_t *)(ACHuf_1 + i)); } printk("\n"); printk("DCHuf_0 table: \n"); for(i = 0; i < 64; i++){ printk("0x%x ",*(uint8_t *)(DCHuf_0 + i)); } printk("\n"); printk("DCHuf_1 table: \n"); for(i = 0; i < 64; i++){ printk("0x%x ",*(uint8_t *)(DCHuf_1 + i)); } printk("\n"); #endif return EN_NORMAL; } /* len: 2bytes table 0-3 { tq V[64] () } */ /********************************************************** * jpeg deal for 0xdb mark *********************************************************** **/ static int _jpeg_parser_dqt(struct jpeg_parser_info *parser_info) { struct jpeg_info_t *jpeg_info = &parser_info->jpeg_info; uint8_t *curtable=0; int dqt_len=0; int i=0; uint8_t tq=0; dqt_len = _jpeg_parser_get2bytes(parser_info); dqt_len -= 2; //get len while(dqt_len > 16) { //len i tq = _jpeg_parser_getbyte(parser_info); if (tq == 0x0) { curtable = (uint8_t*)QT_0; } else if (tq == 0x1) { curtable = (uint8_t*)QT_1; } else { curtable = (uint8_t*)QT_2; } //len = 0; for (i = 0; i < 64; i++) { curtable[zigzag[i]]= _jpeg_parser_getbyte(parser_info); } dqt_len -= 65; jpeg_info->getQTablenum++; if (tq == 0x1) { memcpy((char*)QT_2, (const char*)QT_1, 64); } } if (dqt_len) { _jpeg_parser_skipbytes(parser_info, dqt_len); } #if 0 printk("QT_0 table: tq %d \n",tq); for(i = 0; i < 64; i++){ printk("0x%x ",*(uint8_t *)(QT_0 + i)); } printk("\n"); printk("QT_1 table: \n"); for(i = 0; i < 64; i++){ printk("0x%x ",*(uint8_t *)(QT_1 + i)); } printk("\n"); printk("QT_2 table: \n"); for(i = 0; i < 64; i++){ printk("0x%x ",*(uint8_t *)(QT_2 + i)); } printk("\n"); #endif return EN_NORMAL; } /********************************************************** * jpeg deal for 0xda mark *********************************************************** **/ static int _jpeg_parser_sos(struct jpeg_parser_info *parser_info) { struct jpeg_info_t *jpeg_info = &parser_info->jpeg_info; int sos_len=0; int i=0; unsigned int tmp=0; unsigned int cIndex=0; sos_len = _jpeg_parser_get2bytes(parser_info); sos_len -= 2; jpeg_info->scan.Ns = _jpeg_parser_getbyte(parser_info); if (jpeg_info->scan.Ns > 3) { return EN_NOSUPPORT; } if (jpeg_info->scan.Ns == 1) { tmp = _jpeg_parser_getbyte(parser_info); cIndex = tmp - 1; if (cIndex > 2) { return EN_NOSUPPORT; } jpeg_info->scan.Cs[cIndex] = tmp; tmp = _jpeg_parser_getbyte(parser_info); jpeg_info->scan.Td[cIndex] = tmp >> 4; jpeg_info->scan.Ta[cIndex] = tmp & 0x0F; /* decoding info */ jpeg_info->amountOfQTables = 1; } else { for (i = 0; i < jpeg_info->scan.Ns; i++) { jpeg_info->scan.Cs[i] = _jpeg_parser_getbyte(parser_info); tmp = _jpeg_parser_getbyte(parser_info); jpeg_info->scan.Td[i] = tmp >> 4; /* which DC table */ jpeg_info->scan.Ta[i] = tmp & 0x0F; /* which AC table */ } jpeg_info->amountOfQTables = 3; } //ss,se,ahal _jpeg_parser_skipbytes(parser_info, 3); if ((jpeg_info->amountOfQTables == 3)&&(jpeg_info->getQTablenum == 1)) { memcpy((char*)QT_1, (const char*)QT_0, 64); memcpy((char*)QT_2, (const char*)QT_1, 64); } jpeg_info->stream_addr = &parser_info->jpeg_base[parser_info->jpeg_current_offset]; jpeg_info->stream_size = parser_info->jpeg_size - parser_info->jpeg_current_offset; return EN_NORMAL; } /********************************************************** * get mark from jpeg *********************************************************** **/ static int _jpeg_parser_get_mark(struct jpeg_parser_info *parser_info) { uint8_t tag=0; do { do { if (parser_info->nodata) { return M_NODATA; } tag = _jpeg_parser_getbyte(parser_info); } while(tag != 0xff); while(tag == 0xff) { if (parser_info->nodata) { return M_NODATA; } tag = _jpeg_parser_getbyte(parser_info); } if (tag != 0) { break; } }while(1); return tag; } const jpeg_marker_handle_t jpeg_rout[]= { {M_SOF0, _jpeg_parser_sof0},//0xc0 //{M_APP0, _jpeg_parser_app0},//0xe0 {M_APP1, _jpeg_parser_app1},//0xe1 {M_DHT, _jpeg_parser_dht},//0xc4 {M_DQT, _jpeg_parser_dqt},//0xdb {M_SOS, _jpeg_parser_sos},//0xda }; /********************************************************** * parser jpeg info *********************************************************** **/ int jpeg_parser_process(struct jpeg_parser_info *parser_info, int mode) { jpeg_marker_handle_t *mark_handle = NULL; uint8_t tag = 0; int i = 0; int rts = EN_NORMAL; for(;;) { if (parser_info->nodata) { break; } //get mark tag = _jpeg_parser_get_mark(parser_info); //printk("tag: %x \n",tag); switch (tag) { case M_SOF1: // 0xc1 case M_SOF2: // 0xc2 case M_SOF3: // 0xc3 case M_SOF9: // 0xc9 case M_SOF10: // 0xca case M_SOF11: // 0xcb return EN_NOSUPPORT; //break; case M_SOI: // 0xd8 continue; default: break; } mark_handle = NULL; for (i = 0; i < 5; i++) { if (tag == jpeg_rout[i].marker) { mark_handle = (jpeg_marker_handle_t *)&jpeg_rout[i]; } } if (mark_handle) { rts = (rt_status_t)mark_handle->marker_pro((void *)parser_info); if (rts || (parser_info->thumbnailoffset != 0)) { return rts; } } else { _jpeg_parser_mark_skip((void *)parser_info); } if (tag == M_SOS) { return 0; } } // da end return 0; }