From: hj kim Date: Thu, 1 Feb 2018 07:59:53 +0000 (+0900) Subject: Add new API mm_util_extract_image_info() X-Git-Tag: submit/tizen/20180201.081645^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=a49182c31ba4ab8b29375a1c6393042e77185246;p=platform%2Fcore%2Fmultimedia%2Flibmm-utility.git Add new API mm_util_extract_image_info() Change-Id: I788c81943a4ff766a50aa154354f6a996e35852c --- diff --git a/magick/include/mm_util_magick.h b/magick/include/mm_util_magick.h index be054b3..dd5d58c 100755 --- a/magick/include/mm_util_magick.h +++ b/magick/include/mm_util_magick.h @@ -41,7 +41,6 @@ typedef enum { MM_UTIL_IMG_FMT_NUM, /**< Number of image formats */ } mm_util_magick_format; - /** * Image rotation types */ @@ -55,6 +54,21 @@ typedef enum { MM_UTIL_ROTATE_NUM /**< Number of rotation types */ } mm_util_magick_rotate_type; +typedef enum { + IMG_CODEC_UNKNOWN_TYPE = -2, + IMG_CODEC_BIG_PROGRESSIVE_JPEG = -1, + IMG_CODEC_NONE = 0, + IMG_CODEC_GIF = (1 << 0), + IMG_CODEC_PNG = (1 << 1), + IMG_CODEC_WBMP = (1 << 2), + IMG_CODEC_JPEG = (1 << 3), + IMG_CODEC_BMP = (1 << 4), + IMG_CODEC_TIF = (1 << 5), + IMG_CODEC_AGIF = (1 << 6), + IMG_CODEC_PROGRESSIVE_JPEG = (1 << 7), + IMG_CODEC_DRM = (1 << 8), +} mm_util_img_code_type; + int mm_util_create_handle(mm_util_image_h *handle, const unsigned char *buffer, unsigned int width, unsigned int height, size_t size, mm_util_magick_format format); /*You must release buffer using free().*/ @@ -73,5 +87,6 @@ int mm_util_resize_P_P(const char *src_path, unsigned int req_width, unsigned in int mm_util_convert_B_B(mm_util_image_h src_handle, mm_util_magick_format req_format, mm_util_image_h *dst_handle); +int mm_util_extract_image_info(const char *path, mm_util_img_code_type *type, unsigned int *width, unsigned int *height); #endif /*__MM_UTILITY_MAGICK_H__*/ diff --git a/magick/mm_util_info.c b/magick/mm_util_info.c new file mode 100755 index 0000000..f9de67f --- /dev/null +++ b/magick/mm_util_info.c @@ -0,0 +1,530 @@ +/* + * libmm-utility + * + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All rights reserved. + * + * Contact: YoungHun Kim + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include "mm_util_private.h" +#include "mm_util_magick.h" + +#define MINIMUM_HEADER_BYTES 8 + +#define PNG_HEADER_LENGTH 8 +#define JPG_HEADER_LENGTH 2 +#define GIF_HEADER_LENGTH 3 +#define BMP_HEADER_LENGTH 2 +#define WBMP_HEADER_LENGTH 2 + +#define JPG_HEADER_TYPE_LENGTH 2 +#define JPG_BLOCK_SIZE_LENGTH 2 +#define JPG_IMAGE_SIZE_LENGTH 8 + +#define FILE_READ_SIZE 4096 + +typedef struct _stream { + FILE *fd; + unsigned int buffpos; + unsigned int filepos; + unsigned int filesize; + unsigned int buffend; + unsigned int debugpos; + unsigned char *buffer; +} IFEGSTREAMCTRL; + +static unsigned char gIfegPNGHeader[] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a }; +static unsigned char gIfegJPEGHeader[] = { 0xFF, 0xD8 }; +static unsigned char gIfegGIFHeader[] = { "GIF" }; +static unsigned char gIfegBMPHeader[] = { 0x42, 0x4D }; +static unsigned char gIfegWBMPHeader[] = { 0x00, 0x00 }; + +void __ImgGetFileAttributes(const char *szPathName, unsigned long *pFileAttr) +{ + FILE *f = NULL; + + f = fopen(szPathName, "r"); + if (f != NULL) { + fseek(f, 0, SEEK_END); + *pFileAttr = ftell(f); + fclose(f); + } +} + +static int _CheckBuffer(IFEGSTREAMCTRL *pIfegstreamctrl, unsigned int size) +{ + unsigned long fileread; + + if ((size + pIfegstreamctrl->buffpos) > pIfegstreamctrl->buffend) { + if (pIfegstreamctrl->filepos == pIfegstreamctrl->filesize) + return MM_UTIL_ERROR_INVALID_OPERATION; + + if (pIfegstreamctrl->buffpos == 0) { + fileread = fread(pIfegstreamctrl->buffer, sizeof(char), FILE_READ_SIZE, pIfegstreamctrl->fd); + if (fileread == 0) + return MM_UTIL_ERROR_INVALID_OPERATION; + + pIfegstreamctrl->buffend = fileread; + pIfegstreamctrl->filepos += fileread; + pIfegstreamctrl->buffpos = 0; + } else { + + if (size >= 2048 || pIfegstreamctrl->buffend - pIfegstreamctrl->buffpos > FILE_READ_SIZE) + return MM_UTIL_ERROR_INVALID_OPERATION; + + memcpy(pIfegstreamctrl->buffer, &pIfegstreamctrl->buffer[pIfegstreamctrl->buffpos], pIfegstreamctrl->buffend - pIfegstreamctrl->buffpos); + fileread = fread(&pIfegstreamctrl->buffer[pIfegstreamctrl->buffend - pIfegstreamctrl->buffpos], sizeof(char), pIfegstreamctrl->buffpos, pIfegstreamctrl->fd); + if (fileread == 0) + return MM_UTIL_ERROR_INVALID_OPERATION; + + pIfegstreamctrl->buffend = pIfegstreamctrl->buffend - pIfegstreamctrl->buffpos + fileread; + pIfegstreamctrl->buffpos = 0; + pIfegstreamctrl->filepos += fileread; + } + return MM_UTIL_ERROR_NONE; + } + return MM_UTIL_ERROR_NONE; +} + +static unsigned int _IfegReadUINT(unsigned char *pBuffer) +{ + return (((*pBuffer) << 24) | ((*(pBuffer + 1)) << 16) | ((*(pBuffer + 2)) << 8) | (*(pBuffer + 3))); +} + +static int _ImgGetImageInfo(FILE *hFile, unsigned long fileSize, char *fileExt, mm_util_img_code_type *type, unsigned int *width, unsigned int *height) +{ + unsigned int fileleft; + unsigned long fileread; + unsigned char EncodedDataBuffer[4096]; + + unsigned int *pWidth = NULL; + unsigned int *pHeight = NULL; + int ret = MM_UTIL_ERROR_NONE; + + if (type == NULL || width == NULL || height == NULL) { + return MM_UTIL_ERROR_INVALID_PARAMETER; + } else { + pWidth = width; + pHeight = height; + + *type = IMG_CODEC_UNKNOWN_TYPE; + *pWidth = 0; + *pHeight = 0; + } + + memset(EncodedDataBuffer, 0, 4096); + + fileread = fread(EncodedDataBuffer, sizeof(char), 8, hFile); + if (fileread < MINIMUM_HEADER_BYTES) { + mm_util_warn("IMG_CODEC_UNKNOWN_TYPE"); + return ret; + } + + if (memcmp(EncodedDataBuffer, gIfegJPEGHeader, JPG_HEADER_LENGTH) == 0) { + unsigned char header_type[JPG_HEADER_TYPE_LENGTH]; + unsigned char block_size[JPG_BLOCK_SIZE_LENGTH]; + unsigned char image_size[JPG_IMAGE_SIZE_LENGTH]; + + rewind(hFile); + + unsigned short block_length = EncodedDataBuffer[4] * 256 + EncodedDataBuffer[5]; + mm_util_debug("block length : %d", block_length); + unsigned int i = 4; + + if (fseek(hFile, block_length + 4, SEEK_CUR) < 0) { + mm_util_error("fseek was failed"); + return MM_UTIL_ERROR_INVALID_OPERATION; + } + + while (i < fileSize) { + i += block_length; + if (i >= fileSize) { + mm_util_warn("Failed to get w / h from jpeg at index [%d]", i); + break; + } + + memset(header_type, 0, JPG_HEADER_TYPE_LENGTH); + fileread = fread(header_type, sizeof(char), JPG_HEADER_TYPE_LENGTH, hFile); + + if (header_type[0] != 0xFF) { + mm_util_warn("Failed to get w / h from jpeg at index [%d]", i); + break; + } + + if (header_type[1] == 0xC0 || header_type[1] == 0xC2) { + memset(image_size, 0, JPG_IMAGE_SIZE_LENGTH); + fileread = fread(image_size, sizeof(char), JPG_IMAGE_SIZE_LENGTH, hFile); + + *pWidth = image_size[5] * 256 + image_size[6]; + *pHeight = image_size[3] * 256 + image_size[4]; + break; + } else { + i += 2; + memset(block_size, 0, JPG_BLOCK_SIZE_LENGTH); + fileread = fread(block_size, sizeof(char), JPG_BLOCK_SIZE_LENGTH, hFile); + block_length = block_size[0] * 256 + block_size[1]; + mm_util_debug("new block length : %d", block_length); + + if (fseek(hFile, block_length-JPG_BLOCK_SIZE_LENGTH, SEEK_CUR) < 0) { + mm_util_error("fseek was failed"); + return MM_UTIL_ERROR_INVALID_OPERATION; + } + } + } + mm_util_debug("IMG_CODEC_JPEG : W[%d] H[%d]", *pWidth, *pHeight); + *type = IMG_CODEC_JPEG; + } + /*********************** PNG *************************/ + else if (memcmp(EncodedDataBuffer, gIfegPNGHeader, PNG_HEADER_LENGTH) == 0) { + unsigned char tmp; + + fileread = fread(EncodedDataBuffer, sizeof(char), 32, hFile); + if (fileread < 32) { + mm_util_warn("IMG_CODEC_UNKNOWN_TYPE in PNG"); + return ret; + } + /* Get Image Width */ + if (pWidth) + *pWidth = _IfegReadUINT((EncodedDataBuffer + 8)); + + /* Get Image Height */ + if (pHeight) + *pHeight = _IfegReadUINT((EncodedDataBuffer + 12)); + + /* Read Interlace byte */ + tmp = *(EncodedDataBuffer + 20); + /* If image is interlaced then multiple should be 2 */ + if (tmp) { + mm_util_debug("Interlaced PNG Image."); + } + mm_util_debug("IMG_CODEC_PNG"); + *type = IMG_CODEC_PNG; + } + /*********************** BMP *************************/ + else if (memcmp(EncodedDataBuffer, gIfegBMPHeader, BMP_HEADER_LENGTH) == 0) { + /* Parse BMP File and get image width and image height */ + fileread = fread(&EncodedDataBuffer[8], sizeof(char), 18, hFile); + if (fileread < 18) { + mm_util_warn("IMG_CODEC_UNKNOWN_TYPE in BMP"); + return ret; + } + if (pWidth) { + *pWidth = + EncodedDataBuffer[18] | (EncodedDataBuffer[19] << 8) + | (EncodedDataBuffer[20] << 16) | + (EncodedDataBuffer[21] << 24); + } + if (pHeight) { + // add the reference function abs(). may have negative height values in bmp header. + *pHeight = abs(EncodedDataBuffer[22] | (EncodedDataBuffer[23] << 8) | (EncodedDataBuffer[24] << 16) | (EncodedDataBuffer[25] << 24)); + } + + mm_util_debug("IMG_CODEC_BMP"); + *type = IMG_CODEC_BMP; + } + /*********************** GIF *************************/ + else if (memcmp(EncodedDataBuffer, gIfegGIFHeader, GIF_HEADER_LENGTH) == 0) { + unsigned int tablelength = 0; + unsigned int imagecount = 0; + int finished = 0; + unsigned char temp; + unsigned int length; + IFEGSTREAMCTRL ifegstreamctrl; + + if (13 > fileSize) { + mm_util_warn("IMG_CODEC_UNKNOWN_TYPE in GIF"); + return ret; + } + + fileread = fread(&EncodedDataBuffer[8], sizeof(char), 5, hFile); + if (fileread < 5) { + mm_util_warn("IMG_CODEC_UNKNOWN_TYPE in GIF"); + return ret; + } + + if (EncodedDataBuffer[0] != 'G' || EncodedDataBuffer[1] != 'I' + || EncodedDataBuffer[2] != 'F' || EncodedDataBuffer[3] < '0' + || EncodedDataBuffer[3] > '9' || EncodedDataBuffer[4] < '0' + || EncodedDataBuffer[4] > '9' || EncodedDataBuffer[5] < 'A' + || EncodedDataBuffer[5] > 'z') { + mm_util_warn("IMG_CODEC_UNKNOWN_TYPE in GIF"); + return ret; + } + + *pWidth = EncodedDataBuffer[6] | (EncodedDataBuffer[7] << 8); + *pHeight = EncodedDataBuffer[8] | (EncodedDataBuffer[9] << 8); + + mm_util_debug("Logical width : %d, Height : %d", *pWidth, *pHeight); + + if ((EncodedDataBuffer[10] & 0x80) != 0) { /* Global color table */ + temp = (EncodedDataBuffer[10] & 0x7) + 1; + tablelength = (1 << temp) * 3; + + if ((tablelength * sizeof(char)) > sizeof(EncodedDataBuffer)) { + mm_util_warn("_ImgGetInfoStreaming :table length is more than buffer length"); + return ret; + } + + if (13 + tablelength > fileSize) { + mm_util_warn("IMG_CODEC_UNKNOWN_TYPE in GIF"); + return ret; + } + /* coverity[ -tainted_data_argument : EncodedDataBuffer ] */ + fileread = fread(EncodedDataBuffer, sizeof(char), tablelength, hFile); + if (fileread < tablelength) { + mm_util_warn("IMG_CODEC_UNKNOWN_TYPE in GIF"); + return ret; + } + } + + fileleft = fileSize - 13 - tablelength; + + ifegstreamctrl.fd = hFile; + ifegstreamctrl.filesize = fileleft; + ifegstreamctrl.filepos = 0; + ifegstreamctrl.buffpos = 0; + ifegstreamctrl.buffend = 0; + ifegstreamctrl.buffer = EncodedDataBuffer; + + while (!finished) { + if (ifegstreamctrl.buffpos > ifegstreamctrl.buffend) + break; + if (_CheckBuffer(&ifegstreamctrl, 1) != MM_UTIL_ERROR_NONE) { + mm_util_warn("_CheckBuffer was failed"); + return ret; + } + + switch (EncodedDataBuffer[ifegstreamctrl.buffpos++]) { + + case 0x3b: /* End of the GIF dataset */ + finished = 1; + break; + + case 0x21: /* Extension Block */ + if (_CheckBuffer(&ifegstreamctrl, 1) != MM_UTIL_ERROR_NONE) { + mm_util_warn("_CheckBuffer was failed"); + return ret; + } + + switch (EncodedDataBuffer[ifegstreamctrl.buffpos++]) { + + case 0xf9: /* Graphic control extension block */ + if (_CheckBuffer(&ifegstreamctrl, 6) != MM_UTIL_ERROR_NONE) { + mm_util_warn("_CheckBuffer was failed"); + return ret; + } + + if (4 != EncodedDataBuffer[ifegstreamctrl.buffpos++]) { /* data length : fixed 4 bytes */ + *type = 0; + } + ifegstreamctrl.buffpos += 4; + ifegstreamctrl.buffpos++; /* block end */ + break; + + case 0x01: /* Plain Text block */ + case 0xfe: /* Comment Extension block */ + case 0xff: /* Appliation Extension block */ + if (_CheckBuffer(&ifegstreamctrl, 1) != MM_UTIL_ERROR_NONE) { + mm_util_warn("_CheckBuffer was failed"); + return ret; + } + + if (ifegstreamctrl.buffpos > sizeof(EncodedDataBuffer)) { + mm_util_warn("buffer position exceeds buffer max length "); + return ret; + } + + while ((ifegstreamctrl.buffpos < sizeof(EncodedDataBuffer)) + && ((length = EncodedDataBuffer[ifegstreamctrl.buffpos++]) > 0)) { /* get the data length */ + if (_CheckBuffer(&ifegstreamctrl, length) != MM_UTIL_ERROR_NONE) { + mm_util_warn("_CheckBuffer was failed"); + return ret; + } + + /* Check integer overflow */ + if (ifegstreamctrl.buffpos > 0xffffffff - length) { + mm_util_error("Prevent integer overflow.."); + return ret; + } + + ifegstreamctrl.buffpos += (length); + /* File End Check */ + } + break; + + default: + break; + } + + break; + + case 0x2c: /* Start of an image object. Read the image description. */ + + if (_CheckBuffer(&ifegstreamctrl, 9) != MM_UTIL_ERROR_NONE) { + mm_util_warn("_CheckBuffer was failed"); + return ret; + } +#if 1 + if (imagecount == 0) { + /* Regard the width/height of the first image block as the size of thumbnails. */ + int img_block_w = 0, img_block_h = 0; + + img_block_w = EncodedDataBuffer[ifegstreamctrl.buffpos + 4] |(EncodedDataBuffer[ifegstreamctrl.buffpos + 5] << 8); + img_block_h = EncodedDataBuffer[ifegstreamctrl.buffpos + 6] |(EncodedDataBuffer[ifegstreamctrl.buffpos + 7] << 8); + mm_util_debug("Image block width : %d, Height : %d", img_block_w, img_block_h); + + *pWidth = img_block_w; + *pHeight = img_block_h; + } +#endif + /* Color Resolution */ + if ((EncodedDataBuffer[ifegstreamctrl.buffpos + 8] & 0x80) != 0) { /* Logical color table */ + temp = (EncodedDataBuffer[ifegstreamctrl.buffpos + 8] & 0x7) + 1; + length = (1 << temp) * 3; + if (_CheckBuffer(&ifegstreamctrl, length + 9) != MM_UTIL_ERROR_NONE) { + mm_util_warn("_CheckBuffer was failed"); + return ret; + } + + ifegstreamctrl.buffpos += length; + /* File End Check */ + } + + ifegstreamctrl.buffpos += 9; + + if (_CheckBuffer(&ifegstreamctrl, 1) != MM_UTIL_ERROR_NONE) { + mm_util_warn("_CheckBuffer was failed"); + return ret; + } + + temp = EncodedDataBuffer[ifegstreamctrl.buffpos++]; + if (temp < 2 || 9 < temp) { + return ret; + } + + do { + if (_CheckBuffer(&ifegstreamctrl, 1) != MM_UTIL_ERROR_NONE) { + mm_util_warn("_CheckBuffer was failed"); + return ret; + } + + length = EncodedDataBuffer[ifegstreamctrl.buffpos++]; + if ((length + ifegstreamctrl.buffpos) > ifegstreamctrl.buffend) { + length = length + ifegstreamctrl.buffpos - ifegstreamctrl.buffend; + if (fseek(ifegstreamctrl.fd, length, SEEK_CUR) < 0) { + if (imagecount) { + mm_util_debug("IMG_CODEC_AGIF"); + *type = IMG_CODEC_AGIF; + return ret; + } + return MM_UTIL_ERROR_INVALID_OPERATION; + } + ifegstreamctrl.filepos += length; + ifegstreamctrl.buffpos = 0; + ifegstreamctrl.buffend = 0; + } else { + ifegstreamctrl.buffpos += length; + } + + /* File End Check */ + } while (length); + if (!imagecount) + imagecount++; + else { + mm_util_debug("IMG_CODEC_AGIF"); + *type = IMG_CODEC_AGIF; + return ret; + } + break; + + default: + finished = 0; + break; + + } + } + mm_util_debug("IMG_CODEC_GIF"); + *type = IMG_CODEC_GIF; + } + /*********************** WBMP *************************/ + else if ((memcmp(EncodedDataBuffer, gIfegWBMPHeader, WBMP_HEADER_LENGTH) == 0) && (strcasecmp(fileExt, "wbmp") == 0)) { + /* Parse BMP File and get image width and image height */ + if (pWidth) + *pWidth = EncodedDataBuffer[2]; + + if (pHeight) + *pHeight = EncodedDataBuffer[3]; + + mm_util_debug("IMG_CODEC_WBMP W[%d] H[%d]", *pWidth, *pHeight); + *type = IMG_CODEC_WBMP; + } + return ret; +} + +static int _ImgGetFileExt(const char *path, char *file_ext, int max_len) +{ + int i = 0; + + for (i = (int)strlen(path); i >= 0; i--) { + if ((path[i] == '.') && (i < (int)strlen(path))) { + strncpy(file_ext, &path[i + 1], max_len); + return MM_UTIL_ERROR_NONE; + } + + /* meet the dir. no ext */ + if (path[i] == '/') + return MM_UTIL_ERROR_INVALID_PARAMETER; + } + + return MM_UTIL_ERROR_INVALID_PARAMETER; +} + +int mm_util_extract_image_info(const char *path, mm_util_img_code_type *type, unsigned int *width, unsigned int *height) +{ + FILE *hFile; + unsigned long file_size = 0; + char file_ext[10] = { 0, }; + int ret = MM_UTIL_ERROR_NONE; + + if (path == NULL) + return MM_UTIL_ERROR_INVALID_PARAMETER; + + hFile = fopen(path, "rb"); + if (hFile == NULL) { + mm_util_error("file open error: %s", path); + return MM_UTIL_ERROR_INVALID_PARAMETER; + } + + __ImgGetFileAttributes(path, &file_size); + if (file_size == 0) { + fclose(hFile); + return MM_UTIL_ERROR_INVALID_PARAMETER; + } + + ret = _ImgGetFileExt(path, file_ext, sizeof(file_ext)); + if (ret != MM_UTIL_ERROR_NONE) { + mm_util_warn("_ImgGetFileExt failed"); + } + + ret = _ImgGetImageInfo(hFile, file_size, file_ext, type, width, height); + + fseek(hFile, 0, SEEK_SET); + fclose(hFile); + + return ret; +} + diff --git a/packaging/libmm-utility.spec b/packaging/libmm-utility.spec index 636e869..4a315a5 100755 --- a/packaging/libmm-utility.spec +++ b/packaging/libmm-utility.spec @@ -1,6 +1,6 @@ Name: libmm-utility Summary: Multimedia Framework Utility Library -Version: 0.1.7 +Version: 0.1.8 Release: 0 Group: System/Libraries License: Apache-2.0