From 09e83b6a8bdaeca81bc48cff12628a7ebc252b37 Mon Sep 17 00:00:00 2001 From: 10km <10km0811@sohu.com> Date: Thu, 30 Aug 2018 17:10:26 +0800 Subject: [PATCH 1/2] modified logic judgment for checking JPEG format.JFIF and Exif is morst popular file format of JPEG encoded image,but not all,for MJPEG format that used by camera,the flag is 'AVI1' OR not any flag ,starting FFC0 after FFD8. so the old logic is not work for other JPEG format.see my blog for detail : https://blog.csdn.net/10km/article/details/82155967 --- src/mybmp/jpeg.c | 78 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 56 insertions(+), 22 deletions(-) diff --git a/src/mybmp/jpeg.c b/src/mybmp/jpeg.c index e62d94df..6ca8a380 100644 --- a/src/mybmp/jpeg.c +++ b/src/mybmp/jpeg.c @@ -258,7 +258,6 @@ void* __mg_init_jpg (MG_RWops *fp, MYBITMAP* mybmp, RGB* pal) { int i; unsigned char magic[5]; - Uint16 magic_db; /* This struct contains the JPEG decompression parameters * and pointers to working space @@ -273,17 +272,8 @@ void* __mg_init_jpg (MG_RWops *fp, MYBITMAP* mybmp, RGB* pal) if (magic[0] != 0xFF || magic[1] != 0xD8) goto err; /* not JPEG image*/ - magic_db = MGUI_ReadLE16 (fp); - MGUI_RWread (fp, magic, 2, 1); - MGUI_RWread (fp, magic, 4, 1); - magic [4] = '\0'; - if (magic_db != 0xDBFF - && strncmp((char*)magic, "JFIF", 4) != 0 - && strncmp((char*)magic, "Exif", 4) != 0) - goto err; /* not JPEG image*/ - - MGUI_RWseek (fp, -10, SEEK_CUR); + MGUI_RWseek (fp, 0, SEEK_SET); /* Step 1: allocate and initialize JPEG decompression object */ cinfo = calloc (1, sizeof(struct jpeg_decompress_struct)); @@ -318,7 +308,10 @@ void* __mg_init_jpg (MG_RWops *fp, MYBITMAP* mybmp, RGB* pal) my_jpeg_data_src (cinfo, fp); /* Step 3: read file parameters with jpeg_read_header() */ - jpeg_read_header (cinfo, TRUE); + if(JPEG_HEADER_OK != jpeg_read_header (cinfo, TRUE)) + { + longjmp (jerr->setjmp_buffer, 1); + } /* Step 4: set parameters for decompression */ cinfo->out_color_space = (mybmp->flags & MYBMP_LOAD_GRAYSCALE) ? JCS_GRAYSCALE: JCS_RGB; @@ -456,24 +449,65 @@ int __mg_load_jpg (MG_RWops* fp, void* init_info, MYBITMAP *my_bmp, BOOL __mg_check_jpg (MG_RWops* fp) { unsigned char magic [5]; - Uint16 magic_db; if (!MGUI_RWread (fp, magic, 2, 1)) return FALSE; /* not JPEG image*/ if (magic[0] != 0xFF || magic[1] != 0xD8) return FALSE; /* not JPEG image*/ - magic_db = MGUI_ReadLE16 (fp); - MGUI_RWread (fp, magic, 2, 1); + MGUI_RWseek (fp, 0, SEEK_SET); - MGUI_RWread (fp, magic, 4, 1); - magic [4] = '\0'; - if (magic_db != 0xDBFF - && strncmp((char*)magic, "JFIF", 4) != 0 - && strncmp((char*)magic, "Exif", 4) != 0) - return FALSE; /* not JPEG image*/ + /* This struct contains the JPEG decompression parameters + * and pointers to working space + * (which is allocated as needed by the JPEG library). + */ + struct jpeg_decompress_struct *cinfo; + struct my_error_mgr *jerr; + jpeg_init_info_t* init_info; - return TRUE; + /* Step 1: allocate and initialize JPEG decompression object */ + cinfo = calloc (1, sizeof(struct jpeg_decompress_struct)); + jerr = calloc (1, sizeof(struct my_error_mgr)); + init_info = calloc (1, sizeof(jpeg_init_info_t)); + + if (cinfo == NULL || jerr == NULL || init_info == NULL) + return FALSE; + + /* We set up the normal JPEG error routines first. */ + cinfo->err = jpeg_std_error (&jerr->pub); + jerr->pub.error_exit = my_error_exit; + + + /* return value*/ + BOOL ret = TRUE; + + /* Establish the setjmp return context for my_error_exit to use. */ + if (setjmp (jerr->setjmp_buffer)) + { + /* If we get here, the JPEG code has signaled an error */ + ret = FALSE; + goto end; + } + + /* Now we can initialize the JPEG decompression object. */ + jpeg_create_decompress (cinfo); + init_info->dec_started = 0; + + /* Step 2: specify data source */ + my_jpeg_data_src (cinfo, fp); + + /* Step 3: read file parameters with jpeg_read_header() */ + if(JPEG_HEADER_OK != jpeg_read_header (cinfo, TRUE)) + { + longjmp (jerr->setjmp_buffer, 1); + } + +end: + jpeg_destroy_decompress (cinfo); + free (init_info); + free (jerr); + free (cinfo); + return ret; } #endif /* _MGIMAGE_JPG */ From 1297ba836d74176192f8426498caee988a2a61a1 Mon Sep 17 00:00:00 2001 From: 10km <10km0811@sohu.com> Date: Fri, 31 Aug 2018 15:00:50 +0800 Subject: [PATCH 2/2] improve logic of __mg_check_jpg for performance . loop for reading JPEG marker from MG_RWops object,if meet other JPEG marker(eg. SOI,APPn,DQT) skip the payload bytes for next marker,until reach SOF0 or SOF2. about JPEG marker, see also https://en.wikipedia.org/wiki/JPEG --- src/mybmp/jpeg.c | 134 ++++++++++++++++++++++++----------------------- 1 file changed, 69 insertions(+), 65 deletions(-) diff --git a/src/mybmp/jpeg.c b/src/mybmp/jpeg.c index 6ca8a380..00e8f289 100644 --- a/src/mybmp/jpeg.c +++ b/src/mybmp/jpeg.c @@ -254,10 +254,37 @@ static void my_error_exit (j_common_ptr cinfo) longjmp (myerr->setjmp_buffer, 1); } +/* + * Common JPEG markers + * see also https://en.wikipedia.org/wiki/JPEG + * */ +#define JMK_SOI 0xFFD8 /* Start Of Image */ +#define JMK_SOF0 0xFFC0 /* Start Of Frame (baseline DCT) */ +#define JMK_SOF2 0xFFC2 /* Start Of Frame (progressive DCT) */ +#define JMK_DHT 0xFFC4 /* Define Huffman Table(s) */ +#define JMK_DQT 0xFFDB /* Define Quantization Table(s) */ +#define JMK_DRI 0xFFDD /* Define Restart Interval */ +#define JMK_SOS 0xFFDA /* Start Of Scan */ +#define JMK_RST_mask 0xFFD0 /* mask of Restart */ +#define JMK_APP_mask 0xFFE0 /* mask of Application-specific */ +#define JMK_COM 0xFFFE /* Comment */ +#define JMK_EOI 0xFFD9 /* End Of Image */ +/** + * Reads a 16-bit big endian integer from a MG_RWops object. + * return TRUE if sucess,or FALSE if the read failed. + */ +static BOOL read_be16 (MG_RWops *fp,Uint16 *value) +{ + if(-1 == MGUI_RWread(fp, value, (sizeof *value), 1)) + return FALSE; + *value = (ArchSwapBE16(*value)); + return TRUE; +} + void* __mg_init_jpg (MG_RWops *fp, MYBITMAP* mybmp, RGB* pal) { int i; - unsigned char magic[5]; + Uint16 soi_marker; /* This struct contains the JPEG decompression parameters * and pointers to working space @@ -267,11 +294,8 @@ void* __mg_init_jpg (MG_RWops *fp, MYBITMAP* mybmp, RGB* pal) struct my_error_mgr *jerr; jpeg_init_info_t* init_info; - if (!MGUI_RWread (fp, magic, 2, 1)) - goto err; /* not JPEG image*/ - if (magic[0] != 0xFF || magic[1] != 0xD8) - goto err; /* not JPEG image*/ - + if(!read_be16(fp,&soi_marker) || JMK_SOI != soi_marker ) + goto err; /* not JPEG image*/ MGUI_RWseek (fp, 0, SEEK_SET); @@ -446,68 +470,48 @@ int __mg_load_jpg (MG_RWops* fp, void* init_info, MYBITMAP *my_bmp, return ERR_BMP_OK; } + +/** + * loop read JPEG marker from MG_RWops object,until reach SOF0 or SOF2. + * return TRUE if match JPEG image format,or FALSE if the read failed or not JPEG image + */ BOOL __mg_check_jpg (MG_RWops* fp) { - unsigned char magic [5]; - - if (!MGUI_RWread (fp, magic, 2, 1)) - return FALSE; /* not JPEG image*/ - if (magic[0] != 0xFF || magic[1] != 0xD8) - return FALSE; /* not JPEG image*/ - - MGUI_RWseek (fp, 0, SEEK_SET); - - /* This struct contains the JPEG decompression parameters - * and pointers to working space - * (which is allocated as needed by the JPEG library). - */ - struct jpeg_decompress_struct *cinfo; - struct my_error_mgr *jerr; - jpeg_init_info_t* init_info; - - /* Step 1: allocate and initialize JPEG decompression object */ - cinfo = calloc (1, sizeof(struct jpeg_decompress_struct)); - jerr = calloc (1, sizeof(struct my_error_mgr)); - init_info = calloc (1, sizeof(jpeg_init_info_t)); - - if (cinfo == NULL || jerr == NULL || init_info == NULL) - return FALSE; - - /* We set up the normal JPEG error routines first. */ - cinfo->err = jpeg_std_error (&jerr->pub); - jerr->pub.error_exit = my_error_exit; - - - /* return value*/ - BOOL ret = TRUE; - - /* Establish the setjmp return context for my_error_exit to use. */ - if (setjmp (jerr->setjmp_buffer)) - { - /* If we get here, the JPEG code has signaled an error */ - ret = FALSE; - goto end; + for(Uint16 jpeg_marker;read_be16(fp,&jpeg_marker) /* read JPEG marker */;){ + /* payload length of current marker */ + Uint16 payload = 1; /* set 1 for default, mean that current marker followed by payload bytes*/ + switch(jpeg_marker) + { + case JMK_SOI: + payload = 0; /* no payload */ + break; + case JMK_SOF0: + case JMK_SOF2: + return TRUE; /* JPEG image*/ + case JMK_DHT: + case JMK_DQT: + case JMK_DRI: + case JMK_SOS: + case JMK_COM: + break; + case JMK_EOI: + return FALSE; /* not JPEG image*/ + default: + if((0XFFF8 & jpeg_marker) == JMK_RST_mask){ + payload = 0; /* RST0~7(FFD0~FFD7),no payload */ + }else if((0XFFF0 & jpeg_marker) == JMK_APP_mask){ + /* APP0~APP15,do nothing */ + }else + return FALSE; /* not JPEG image*/ + } + if(payload){ + /*read payload length and skip next marker */ + if(!read_be16(fp,&payload)) + return FALSE; /* not JPEG image*/ + MGUI_RWseek (fp, payload- sizeof(payload), SEEK_CUR); + } } - - /* Now we can initialize the JPEG decompression object. */ - jpeg_create_decompress (cinfo); - init_info->dec_started = 0; - - /* Step 2: specify data source */ - my_jpeg_data_src (cinfo, fp); - - /* Step 3: read file parameters with jpeg_read_header() */ - if(JPEG_HEADER_OK != jpeg_read_header (cinfo, TRUE)) - { - longjmp (jerr->setjmp_buffer, 1); - } - -end: - jpeg_destroy_decompress (cinfo); - free (init_info); - free (jerr); - free (cinfo); - return ret; + return FALSE; /* not JPEG image*/ } #endif /* _MGIMAGE_JPG */