summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCedric BAIL <cedric@osg.samsung.com>2015-01-26 15:49:15 +0100
committerCedric BAIL <cedric@osg.samsung.com>2015-02-16 14:47:49 +0100
commit7408ef40677aa40dffcf56867477d631b616676d (patch)
tree2396e46ddfe47f687c4f494617ce00e8b6b2d411
parent687f8d6e7237e52443caadac26e6c161d9b7a0c0 (diff)
downloadefl-7408ef40677aa40dffcf56867477d631b616676d.tar.gz
emile: add JPEG support.
-rw-r--r--configure.ac2
-rw-r--r--src/lib/emile/emile_image.c1283
2 files changed, 1275 insertions, 10 deletions
diff --git a/configure.ac b/configure.ac
index 5daab00e3d..8e7d5bc404 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1053,6 +1053,8 @@ EFL_LIB_START([Emile])
### Checks for libraries
+EFL_CHECK_LIBS([EMILE], [libjpeg])
+
## Compatibility layers
EFL_PLATFORM_DEPEND([EMILE], [evil])
diff --git a/src/lib/emile/emile_image.c b/src/lib/emile/emile_image.c
index 9f26a9aa00..9aa9a75653 100644
--- a/src/lib/emile/emile_image.c
+++ b/src/lib/emile/emile_image.c
@@ -6,6 +6,10 @@
# include <netinet/in.h>
#endif
+#ifdef HAVE_EVIL
+# include <Evil.h>
+#endif
+
#ifdef _WIN32
# include <winsock2.h>
#endif /* ifdef _WIN32 */
@@ -16,6 +20,14 @@
# include "lz4.h"
#endif
+#ifdef _WIN32
+# define XMD_H /* This prevents libjpeg to redefine INT32 */
+#endif
+
+#include <stdio.h>
+#include <setjmp.h>
+#include <jpeglib.h>
+
#include "rg_etc1.h"
#include "Emile.h"
@@ -23,6 +35,45 @@
#include <arm_neon.h>
#endif
+#define IMG_MAX_SIZE 65000
+
+#define IMG_TOO_BIG(w, h) \
+ ((((unsigned long long)w) * ((unsigned long long)h)) >= \
+ ((1ULL << (29 * (sizeof(void *) / 4))) - 2048))
+
+#define SPANS_COMMON(x1, w1, x2, w2) \
+ (!(( (int)((x2) + (int)(w2)) <= (int)(x1)) || (int)((x2) >= (int)((x1) + (int)(w1)))))
+
+#define RECTS_INTERSECT(x, y, w, h, xx, yy, ww, hh) \
+ ((SPANS_COMMON((x), (w), (xx), (ww))) && (SPANS_COMMON((y), (h), (yy), (hh))))
+
+#define RECTS_CLIP_TO_RECT(_x, _y, _w, _h, _cx, _cy, _cw, _ch) \
+ { \
+ if (RECTS_INTERSECT(_x, _y, _w, _h, _cx, _cy, _cw, _ch)) \
+ { \
+ if ((int)_x < (int)(_cx)) \
+ { \
+ if ((int)_w + ((int)_x - (int)(_cx)) < 0) _w = 0; \
+ else _w += ((int)_x - (int)(_cx)); \
+ _x = (_cx); \
+ } \
+ if ((int)(_x + _w) > (int)((_cx) + (_cw))) \
+ _w = (_cx) + (_cw) - _x; \
+ if ((int)_y < (int)(_cy)) \
+ { \
+ if ((int)_h + ((int)_y - (int)(_cy)) < 0) _h = 0; \
+ else _h += ((int)_y - (int)(_cy)); \
+ _y = (_cy); \
+ } \
+ if ((int)(_y + _h) > (int)((_cy) + (_ch))) \
+ _h = (_cy) + (_ch) - _y; \
+ } \
+ else \
+ { \
+ _w = 0; _h = 0; \
+ } \
+ }
+
#ifndef WORDS_BIGENDIAN
/* x86 */
#define A_VAL(p) (((uint8_t *)(p))[3])
@@ -37,6 +88,9 @@
#define B_VAL(p) (((uint8_t *)(p))[3])
#endif
+#define ARGB_JOIN(a,r,g,b) \
+ (((a) << 24) + ((r) << 16) + ((g) << 8) + (b))
+
#define OFFSET_BLOCK_SIZE 4
#define OFFSET_ALGORITHM 5
#define OFFSET_OPTIONS 6
@@ -147,6 +201,11 @@ static const Emile_Colorspace cspaces_etc1_alpha[2] = {
EMILE_COLORSPACE_ARGB8888
};
+static const Emile_Colorspace cspaces_gry[2] = {
+ EMILE_COLORSPACE_GRY8,
+ EMILE_COLORSPACE_ARGB8888
+};
+
/**************************************************************
* The TGV file format is oriented around compression mecanism
* that hardware are good at decompressing. We do still provide
@@ -181,8 +240,8 @@ static const Emile_Colorspace cspaces_etc1_alpha[2] = {
static Eina_Bool
_emile_tgv_bind(Emile_Image *image,
- Emile_Image_Load_Opts *opts,
- Emile_Image_Animated *animated,
+ Emile_Image_Load_Opts *opts EINA_UNUSED,
+ Emile_Image_Animated *animated EINA_UNUSED,
Emile_Image_Load_Error *error)
{
const unsigned char *m;
@@ -220,6 +279,9 @@ _emile_tgv_head(Emile_Image *image,
m = _emile_image_file_source_map(image, &length);
if (!m) return EINA_FALSE;
+ // This can be used for later ABI change of the structure.
+ if (sizeof (Emile_Image_Property) != property_size) return EINA_FALSE;
+
switch (m[OFFSET_ALGORITHM] & 0xFF)
{
case 0:
@@ -343,6 +405,8 @@ _emile_tgv_data(Emile_Image *image,
return EINA_FALSE;
}
+ if (sizeof (Emile_Image_Property) != property_size) return EINA_FALSE;
+
offset = OFFSET_BLOCKS;
*error = EMILE_IMAGE_LOAD_ERROR_CORRUPT_FILE;
@@ -412,7 +476,6 @@ _emile_tgv_data(Emile_Image *image,
Eina_Rectangle current;
Eina_Binbuf *data_start;
const unsigned char *it;
- unsigned int expand_length;
unsigned int i, j;
block_length = _tgv_length_get(m + offset, length, &offset);
@@ -566,22 +629,692 @@ _emile_tgv_close(Emile_Image *image EINA_UNUSED)
}
/* JPEG Handling */
+
+typedef struct _JPEG_error_mgr *emptr;
+struct _JPEG_error_mgr
+{
+ struct jpeg_error_mgr pub;
+ jmp_buf setjmp_buffer;
+};
+
+struct jpeg_membuf_src
+{
+ struct jpeg_source_mgr pub;
+
+ const unsigned char *buf;
+ size_t len;
+ struct jpeg_membuf_src *self;
+};
+
+static void
+_JPEGFatalErrorHandler(j_common_ptr cinfo)
+{
+ emptr errmgr;
+
+ errmgr = (emptr) cinfo->err;
+ longjmp(errmgr->setjmp_buffer, 1);
+ return;
+}
+
+static void
+_JPEGErrorHandler(j_common_ptr cinfo EINA_UNUSED)
+{
+ return;
+}
+
+static void
+_JPEGErrorHandler2(j_common_ptr cinfo EINA_UNUSED, int msg_level EINA_UNUSED)
+{
+ return;
+}
+
+static void
+_emile_jpeg_membuf_src_init(j_decompress_ptr cinfo EINA_UNUSED)
+{
+}
+
+static boolean
+_emile_jpeg_membuf_src_fill(j_decompress_ptr cinfo)
+{
+ static const JOCTET jpeg_eoi[2] = { 0xFF, JPEG_EOI };
+ struct jpeg_membuf_src *src = (struct jpeg_membuf_src *)cinfo->src;
+
+ src->pub.bytes_in_buffer = sizeof(jpeg_eoi);
+ src->pub.next_input_byte = jpeg_eoi;
+
+ return TRUE;
+}
+
+static void
+_emile_jpeg_membuf_src_skip(j_decompress_ptr cinfo,
+ long num_bytes)
+{
+ struct jpeg_membuf_src *src = (struct jpeg_membuf_src *)cinfo->src;
+
+ if ((((long)src->pub.bytes_in_buffer - (long)src->len) > num_bytes) ||
+ ((long)src->pub.bytes_in_buffer < num_bytes))
+ {
+ (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo));
+ return;
+ }
+ src->pub.bytes_in_buffer -= num_bytes;
+ src->pub.next_input_byte += num_bytes;
+}
+
+static void
+_emile_jpeg_membuf_src_term(j_decompress_ptr cinfo)
+{
+ struct jpeg_membuf_src *src = (struct jpeg_membuf_src *)cinfo->src;
+ if (!src) return;
+ free(src);
+ cinfo->src = NULL;
+}
+
+static int
+_emile_jpeg_membuf_src(j_decompress_ptr cinfo,
+ const void *map, size_t length)
+{
+ struct jpeg_membuf_src *src;
+
+ src = calloc(1, sizeof(*src));
+ if (!src)
+ return -1;
+
+ src->self = src;
+
+ cinfo->src = &src->pub;
+ src->buf = map;
+ src->len = length;
+ src->pub.init_source = _emile_jpeg_membuf_src_init;
+ src->pub.fill_input_buffer = _emile_jpeg_membuf_src_fill;
+ src->pub.skip_input_data = _emile_jpeg_membuf_src_skip;
+ src->pub.resync_to_restart = jpeg_resync_to_restart;
+ src->pub.term_source = _emile_jpeg_membuf_src_term;
+ src->pub.bytes_in_buffer = src->len;
+ src->pub.next_input_byte = src->buf;
+
+ return 0;
+}
+
+/*! Magic number for EXIF header, App0, App1*/
+static const unsigned char ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00};
+static const unsigned char JfifHeader[] = {0x4A, 0x46, 0x49, 0x46, 0x00};
+static const unsigned char JfxxHeader[] = {0x4A, 0x46, 0x58, 0x58, 0x00};
+static const unsigned char App0[] = {0xff, 0xe0};
+static const unsigned char App1[] = {0xff, 0xe1};
+static const unsigned char II[] = {0x49, 0x49};
+static const unsigned char MM[] = {0x4d, 0x4d};
+typedef enum {
+ EXIF_BYTE_ALIGN_II,
+ EXIF_BYTE_ALIGN_MM
+} ExifByteAlign;
+
static Eina_Bool
-_emile_jpeg_bind(Emile_Image *image,
- Emile_Image_Load_Opts *opts,
- Emile_Image_Animated *animated,
- Emile_Image_Load_Error *error)
+_get_next_app0(const unsigned char *map, size_t fsize, size_t *position)
+{
+ unsigned short length = 0;
+ unsigned int w = 0, h = 0;
+ unsigned int format = 0;
+ unsigned int data_size = 0;
+ const unsigned char *app0_head, *p;
+
+ /* header_mark:2, length:2, identifier:5 version:2, unit:1, den=4 thum=2 */
+ if ((*position + 16) >= fsize) return EINA_FALSE;
+ app0_head = map + *position;
+
+ /* p is appn's start pointer excluding app0 marker */
+ p = app0_head + 2;
+
+ length = ((*p << 8) + *(p + 1));
+
+ /* JFIF segment format */
+ if (!memcmp(p + 2, JfifHeader, sizeof (JfifHeader)))
+ {
+ format = 3;
+ w = *(p + 14);
+ h = *(p + 15);
+ }
+ else if (!memcmp(p + 2, JfxxHeader, sizeof (JfxxHeader)))
+ {
+ if (*(p + 7) == 0x11)
+ format = 1;
+ else
+ format = 3;
+ w = *(p + 8);
+ h = *(p + 9);
+ }
+
+ data_size = format * w * h;
+
+ if ((*position + 2+ length + data_size) > fsize)
+ return EINA_FALSE;
+
+ *position = *position + 2 + length + data_size;
+
+ return EINA_TRUE;
+}
+
+/* If app1 data is abnormal, returns EINA_FALSE.
+ If app1 data is normal, returns EINA_TRUE.
+ If app1 data is normal but not orientation data, orientation value is -1.
+ */
+
+static Eina_Bool
+_get_orientation_app1(const unsigned char *map, size_t fsize, size_t *position,
+ int *orientation_res, Eina_Bool *flipped)
{
+ const unsigned char *app1_head, *buf;
+ unsigned char orientation[2];
+ ExifByteAlign byte_align;
+ unsigned int num_directory = 0;
+ unsigned int i, j;
+ int direction;
+ unsigned int data_size = 0;
+
+ /* app1 mark:2, data_size:2, exif:6 tiff:8 */
+ if ((*position + 18) >= fsize) return EINA_FALSE;
+ app1_head = map + *position;
+ buf = app1_head;
+
+ data_size = ((*(buf + 2) << 8) + *(buf + 3));
+ if ((*position + 2 + data_size) > fsize) return EINA_FALSE;
+
+ if (memcmp(buf + 4, ExifHeader, sizeof (ExifHeader)))
+ {
+ *position = *position + 2 + data_size;
+ *orientation_res = -1;
+ return EINA_TRUE;
+ }
+
+ /* 2. get 10&11 byte get info of "II(0x4949)" or "MM(0x4d4d)" */
+ /* 3. get [18]&[19] get directory entry # */
+ if (!memcmp(buf + 10, MM, sizeof (MM)))
+ {
+ byte_align = EXIF_BYTE_ALIGN_MM;
+ num_directory = ((*(buf + 18) << 8) + *(buf + 19));
+ orientation[0] = 0x01;
+ orientation[1] = 0x12;
+ }
+ else if (!memcmp(buf + 10, II, sizeof (II)))
+ {
+ byte_align = EXIF_BYTE_ALIGN_II;
+ num_directory = ((*(buf + 19) << 8) + *(buf + 18));
+ orientation[0] = 0x12;
+ orientation[1] = 0x01;
+ }
+ else return EINA_FALSE;
+
+ /* check num_directory data */
+ if ((*position + (12 * num_directory + 20)) > fsize) return EINA_FALSE;
+
+ buf = app1_head + 20;
+
+ j = 0;
+
+ for (i = 0; i < num_directory; i++ )
+ {
+ if (!memcmp(buf + j, orientation, 2))
+ {
+ /*get orientation tag */
+ if (byte_align == EXIF_BYTE_ALIGN_MM)
+ direction = *(buf+ j + 9);
+ else direction = *(buf+ j + 8);
+ switch (direction)
+ {
+ case 3:
+ *orientation_res = 180;
+ *flipped = EINA_FALSE;
+ return EINA_TRUE;
+ case 4:
+ *orientation_res = 180;
+ *flipped = EINA_TRUE;
+ return EINA_TRUE;
+ case 6:
+ *orientation_res = 90;
+ *flipped = EINA_FALSE;
+ return EINA_TRUE;
+ case 7:
+ *orientation_res = 90;
+ *flipped = EINA_TRUE;
+ return EINA_TRUE;
+ case 5:
+ *orientation_res = 270;
+ *flipped = EINA_TRUE;
+ return EINA_TRUE;
+ case 8:
+ *orientation_res = 270;
+ *flipped = EINA_FALSE;
+ return EINA_TRUE;
+ case 2:
+ *orientation_res = 0;
+ *flipped = EINA_TRUE;
+ return EINA_TRUE;
+ default:
+ *orientation_res = 0;
+ *flipped = EINA_FALSE;
+ return EINA_TRUE;
+ }
+ }
+ else
+ j = j + 12;
+ }
return EINA_FALSE;
}
+static int
+_get_orientation(const void *map, size_t length, Eina_Bool *flipped)
+{
+ unsigned char *buf;
+ size_t position = 0;
+ int orientation = -1;
+ Eina_Bool res = EINA_FALSE;
+
+ *flipped = EINA_FALSE;
+
+ /* open file and get 22 byte frome file */
+ if (!map) return 0;
+ /* 1. read 22byte */
+ if (length < 22) return 0;
+ buf = (unsigned char *)map;
+
+ position = 2;
+ /* 2. check 2,3 bypte with APP0(0xFFE0) or APP1(0xFFE1) */
+ while((length - position) > 0)
+ {
+ if (!memcmp(buf + position, App0, sizeof (App0)))
+ {
+ res = _get_next_app0(map, length, &position);
+ if (!res) break;
+ }
+ else if (!memcmp(buf + position, App1, sizeof (App1)))
+ {
+ res = _get_orientation_app1(map, length, &position, &orientation, flipped);
+ if (!res) break;
+ if (orientation != -1) return orientation;
+ }
+ else break;
+ }
+ return 0;
+}
+
+static void
+_rotate_region(unsigned int *r_x, unsigned int *r_y, unsigned int *r_w, unsigned int *r_h,
+ unsigned int x, unsigned int y, unsigned int w, unsigned int h,
+ unsigned int output_w, unsigned int output_h,
+ int degree, Eina_Bool flipped)
+{
+ switch (degree)
+ {
+ case 90:
+ if (flipped)
+ {
+ *r_x = output_w - (y + h);
+ *r_y = output_h - (x + w);
+ *r_w = h;
+ *r_h = w;
+ }
+ else
+ {
+ *r_x = y;
+ *r_y = output_h - (x + y);
+ *r_w = h;
+ *r_h = w;
+ }
+ break;
+ case 180:
+ if (flipped)
+ {
+ *r_y = output_h - (y + h);
+ }
+ else
+ {
+ *r_x = output_w - (x + w);
+ *r_y = output_h - (y + h);
+ }
+ break;
+ case 270:
+ if (flipped)
+ {
+ *r_x = y;
+ *r_y = x;
+ *r_w = h;
+ *r_h = w;
+ }
+ else
+ {
+ *r_x = output_w - (y + h);
+ *r_y = x;
+ *r_w = h;
+ *r_h = w;
+ }
+ break;
+ default:
+ if (flipped)
+ *r_x = output_w - (x + w);
+ break;
+ }
+}
+
+static void
+_rotate_180(uint32_t *data, int w, int h)
+{
+ uint32_t *p1, *p2;
+ uint32_t pt;
+ int x;
+
+ p1 = data;
+ p2 = data + (h * w) - 1;
+ for (x = (w * h) / 2; --x >= 0;)
+ {
+ pt = *p1;
+ *p1 = *p2;
+ *p2 = pt;
+ p1++;
+ p2--;
+ }
+}
+
+static void
+_flip_horizontal(uint32_t *data, int w, int h)
+{
+ uint32_t *p1, *p2;
+ uint32_t pt;
+ int x, y;
+
+ for (y = 0; y < h; y++)
+ {
+ p1 = data + (y * w);
+ p2 = data + ((y + 1) * w) - 1;
+ for (x = 0; x < (w >> 1); x++)
+ {
+ pt = *p1;
+ *p1 = *p2;
+ *p2 = pt;
+ p1++;
+ p2--;
+ }
+ }
+}
+
+static void
+_flip_vertical(uint32_t *data, int w, int h)
+{
+ uint32_t *p1, *p2;
+ uint32_t pt;
+ int x, y;
+
+ for (y = 0; y < (h >> 1); y++)
+ {
+ p1 = data + (y * w);
+ p2 = data + ((h - 1 - y) * w);
+ for (x = 0; x < w; x++)
+ {
+ pt = *p1;
+ *p1 = *p2;
+ *p2 = pt;
+ p1++;
+ p2++;
+ }
+ }
+}
+
+static void
+_rotate_change_wh(uint32_t *to, uint32_t *from,
+ int w, int h,
+ int dx, int dy)
+{
+ int x, y;
+
+ for (x = h; --x >= 0;)
+ {
+ for (y = w; --y >= 0;)
+ {
+ *to = *from;
+ from++;
+ to += dy;
+ }
+ to += dx;
+ }
+}
+
+static Eina_Bool
+_emile_jpeg_bind(Emile_Image *image EINA_UNUSED,
+ Emile_Image_Load_Opts *opts EINA_UNUSED,
+ Emile_Image_Animated *animated EINA_UNUSED,
+ Emile_Image_Load_Error *error EINA_UNUSED)
+{
+ return EINA_TRUE;
+}
+
static Eina_Bool
_emile_jpeg_head(Emile_Image *image,
Emile_Image_Property *prop,
unsigned int property_size,
Emile_Image_Load_Error *error)
{
- return EINA_FALSE;
+ Emile_Image_Load_Opts *opts = NULL;
+ const unsigned char *m;
+ unsigned int scalew, scaleh;
+ struct jpeg_decompress_struct cinfo;
+ struct _JPEG_error_mgr jerr;
+ unsigned int length;
+
+ /* for rotation decoding */
+ int degree = 0;
+ Eina_Bool change_wh = EINA_FALSE;
+ unsigned int load_opts_w = 0, load_opts_h = 0;
+
+
+ if (sizeof (Emile_Image_Property) != property_size)
+ return EINA_FALSE;
+
+ m = _emile_image_file_source_map(image, &length);
+ if (!m) return EINA_FALSE;
+
+ if (image->load_opts)
+ opts = &image->opts;
+
+ memset(&cinfo, 0, sizeof(cinfo));
+ cinfo.err = jpeg_std_error(&(jerr.pub));
+ jerr.pub.error_exit = _JPEGFatalErrorHandler;
+ jerr.pub.emit_message = _JPEGErrorHandler2;
+ jerr.pub.output_message = _JPEGErrorHandler;
+ if (setjmp(jerr.setjmp_buffer))
+ {
+ jpeg_destroy_decompress(&cinfo);
+ _emile_jpeg_membuf_src_term(&cinfo);
+ if (cinfo.saw_JFIF_marker)
+ *error = EMILE_IMAGE_LOAD_ERROR_CORRUPT_FILE;
+ else
+ *error = EMILE_IMAGE_LOAD_ERROR_UNKNOWN_FORMAT;
+ return EINA_FALSE;
+ }
+ jpeg_create_decompress(&cinfo);
+
+ if (_emile_jpeg_membuf_src(&cinfo, m, length))
+ {
+ jpeg_destroy_decompress(&cinfo);
+ _emile_jpeg_membuf_src_term(&cinfo);
+ *error = EMILE_IMAGE_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
+ return EINA_FALSE;
+ }
+
+ if (cinfo.output_components == 1)
+ {
+ // We do handle GRY8 colorspace as an output for JPEG
+ prop->cspaces = cspaces_gry;
+ }
+
+ jpeg_read_header(&cinfo, TRUE);
+ cinfo.do_fancy_upsampling = FALSE;
+ cinfo.do_block_smoothing = FALSE;
+ cinfo.dct_method = JDCT_ISLOW; // JDCT_FLOAT JDCT_IFAST(quality loss)
+ cinfo.dither_mode = JDITHER_ORDERED;
+ cinfo.buffered_image = TRUE; // buffered mode in case jpg is progressive
+ jpeg_start_decompress(&cinfo);
+
+ /* rotation decoding */
+ if (opts->orientation)
+ {
+ degree = _get_orientation(m, length, &prop->flipped);
+ if (degree != 0 || prop->flipped)
+ {
+ opts->degree = degree;
+ prop->rotated = EINA_TRUE;
+
+ if (degree == 90 || degree == 270)
+ change_wh = EINA_TRUE;
+ }
+
+ }
+
+ /* head decoding */
+ prop->w = cinfo.output_width;
+ prop->h = cinfo.output_height;
+ if ((prop->w < 1) || (prop->h < 1) || (prop->w > IMG_MAX_SIZE) || (prop->h > IMG_MAX_SIZE) ||
+ (IMG_TOO_BIG(prop->w, prop->h)))
+ {
+ jpeg_destroy_decompress(&cinfo);
+ _emile_jpeg_membuf_src_term(&cinfo);
+ if (IMG_TOO_BIG(prop->w, prop->h))
+ *error = EMILE_IMAGE_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
+ else
+ *error = EMILE_IMAGE_LOAD_ERROR_GENERIC;
+ return EINA_FALSE;
+ }
+ if (opts->scale_down_by > 1)
+ {
+ prop->w /= opts->scale_down_by;
+ prop->h /= opts->scale_down_by;
+ }
+ else if (opts->dpi > 0.0)
+ {
+ prop->w = (prop->w * opts->dpi) / 90.0;
+ prop->h = (prop->h * opts->dpi) / 90.0;
+ }
+ else if ((opts->w > 0) && (opts->h > 0))
+ {
+ unsigned int w2 = prop->w, h2 = prop->h;
+ /* user set load_opts' w,h on the assumption
+ that image already rotated according to it's orientation info */
+ if (change_wh)
+ {
+ load_opts_w = opts->w;
+ load_opts_h = opts->h;
+ opts->w = load_opts_h;
+ opts->h = load_opts_w;
+ }
+
+ if (opts->w > 0)
+ {
+ w2 = opts->w;
+ h2 = (opts->w * prop->h) / prop->w;
+ if ((opts->h > 0) && (h2 > opts->h))
+ {
+ unsigned int w3;
+ h2 = opts->h;
+ w3 = (opts->h * prop->w) / prop->h;
+ if (w3 > w2)
+ w2 = w3;
+ }
+ }
+ else if (opts->h > 0)
+ {
+ h2 = opts->h;
+ w2 = (opts->h * prop->w) / prop->h;
+ }
+ prop->w = w2;
+ prop->h = h2;
+ if (change_wh)
+ {
+ opts->w = load_opts_w;
+ opts->h = load_opts_h;
+ }
+ }
+ if (prop->w < 1) prop->w = 1;
+ if (prop->h < 1) prop->h = 1;
+
+ if ((prop->w != cinfo.output_width) || (prop->h != cinfo.output_height))
+ {
+ scalew = cinfo.output_width / prop->w;
+ scaleh = cinfo.output_height / prop->h;
+
+ prop->scale = scalew;
+ if (scaleh < scalew) prop->scale = scaleh;
+
+ if (prop->scale > 8) prop->scale = 8;
+ else if (prop->scale < 1) prop->scale = 1;
+
+ if (prop->scale == 3) prop->scale = 2;
+ else if (prop->scale == 5) prop->scale = 4;
+ else if (prop->scale == 6) prop->scale = 4;
+ else if (prop->scale == 7) prop->scale = 4;
+ }
+
+ if (prop->scale > 1)
+ {
+ jpeg_destroy_decompress(&cinfo);
+ _emile_jpeg_membuf_src_term(&cinfo);
+ jpeg_create_decompress(&cinfo);
+
+ if (_emile_jpeg_membuf_src(&cinfo, m, length))
+ {
+ jpeg_destroy_decompress(&cinfo);
+ _emile_jpeg_membuf_src_term(&cinfo);
+ *error = EMILE_IMAGE_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
+ return EINA_FALSE;
+ }
+
+ jpeg_read_header(&cinfo, TRUE);
+ cinfo.do_fancy_upsampling = FALSE;
+ cinfo.do_block_smoothing = FALSE;
+ cinfo.scale_num = 1;
+ cinfo.scale_denom = prop->scale;
+ cinfo.buffered_image = TRUE; // buffered mode in case jpg is progressive
+ jpeg_calc_output_dimensions(&(cinfo));
+ jpeg_start_decompress(&cinfo);
+ }
+
+ prop->w = cinfo.output_width;
+ prop->h = cinfo.output_height;
+
+ // be nice and clip region to image. if its totally outside, fail load
+ if ((opts->region.w > 0) && (opts->region.h > 0))
+ {
+ unsigned int load_region_x = opts->region.x, load_region_y = opts->region.y;
+ unsigned int load_region_w = opts->region.w, load_region_h = opts->region.h;
+ if (prop->rotated)
+ {
+ _rotate_region(&load_region_x, &load_region_y, &load_region_w, &load_region_h,
+ opts->region.x, opts->region.y, opts->region.w, opts->region.h,
+ prop->w, prop->h, degree, prop->flipped);
+ }
+ RECTS_CLIP_TO_RECT(load_region_x, load_region_y,
+ load_region_w, load_region_h,
+ 0, 0, prop->w, prop->h);
+ if ((load_region_w <= 0) || (load_region_h <= 0))
+ {
+ jpeg_destroy_decompress(&cinfo);
+ _emile_jpeg_membuf_src_term(&cinfo);
+ *error = EMILE_IMAGE_LOAD_ERROR_GENERIC;
+ return EINA_FALSE;
+ }
+ prop->w = load_region_w;
+ prop->h = load_region_h;
+ }
+/* end head decoding */
+
+ if (change_wh)
+ {
+ unsigned int tmp;
+ tmp = prop->w;
+ prop->w = prop->h;
+ prop->h = tmp;
+ }
+ jpeg_destroy_decompress(&cinfo);
+ _emile_jpeg_membuf_src_term(&cinfo);
+ *error = EMILE_IMAGE_LOAD_ERROR_NONE;
+ return EINA_TRUE;
}
static Eina_Bool
@@ -591,12 +1324,542 @@ _emile_jpeg_data(Emile_Image *image,
void *pixels,
Emile_Image_Load_Error *error)
{
- return EINA_FALSE;
+ // Handle RGB, ARGB and GRY
+ Emile_Image_Load_Opts *opts = NULL;
+ unsigned int w, h;
+ struct jpeg_decompress_struct cinfo;
+ struct _JPEG_error_mgr jerr;
+ const unsigned char *m = NULL;
+ uint8_t *ptr, *line[16], *data;
+ uint32_t *ptr2, *ptr_rotate = NULL;
+ unsigned int x, y, l, i, scans;
+ int region = 0;
+ /* rotation setting */
+ unsigned int ie_w = 0, ie_h = 0;
+ struct {
+ unsigned int x, y, w, h;
+ } opts_region;
+ volatile int degree = 0;
+ volatile Eina_Bool change_wh = EINA_FALSE;
+ Eina_Bool line_done = EINA_FALSE;
+ unsigned int length;
+
+ if (sizeof (Emile_Image_Property) != property_size)
+ return EINA_FALSE;
+
+ m = _emile_image_file_source_map(image, &length);
+ if (!m) return EINA_FALSE;
+
+ if (image->load_opts)
+ opts = &image->opts;
+
+ memset(&cinfo, 0, sizeof(cinfo));
+ if (prop->rotated)
+ {
+ degree = opts->degree;
+ if (degree == 90 || degree == 270)
+ change_wh = EINA_TRUE;
+ }
+
+ cinfo.err = jpeg_std_error(&(jerr.pub));
+ jerr.pub.error_exit = _JPEGFatalErrorHandler;
+ jerr.pub.emit_message = _JPEGErrorHandler2;
+ jerr.pub.output_message = _JPEGErrorHandler;
+ if (setjmp(jerr.setjmp_buffer))
+ {
+ jpeg_destroy_decompress(&cinfo);
+ _emile_jpeg_membuf_src_term(&cinfo);
+ *error = EMILE_IMAGE_LOAD_ERROR_CORRUPT_FILE;
+ return EINA_FALSE;
+ }
+ jpeg_create_decompress(&cinfo);
+
+ if (_emile_jpeg_membuf_src(&cinfo, m, length))
+ {
+ jpeg_destroy_decompress(&cinfo);
+ _emile_jpeg_membuf_src_term(&cinfo);
+ *error = EMILE_IMAGE_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
+ return 0;
+ }
+
+ jpeg_read_header(&cinfo, TRUE);
+ cinfo.do_fancy_upsampling = FALSE;
+ cinfo.do_block_smoothing = FALSE;
+ cinfo.dct_method = JDCT_ISLOW; // JDCT_FLOAT JDCT_IFAST(quality loss)
+ cinfo.dither_mode = JDITHER_ORDERED;
+
+ if (prop->scale > 1)
+ {
+ cinfo.scale_num = 1;
+ cinfo.scale_denom = prop->scale;
+ }
+
+ /* Colorspace conversion options */
+ /* libjpeg can do the following conversions: */
+ /* GRAYSCLAE => RGB YCbCr => RGB and YCCK => CMYK */
+ switch (cinfo.jpeg_color_space)
+ {
+ case JCS_UNKNOWN:
+ break;
+ case JCS_GRAYSCALE:
+ case JCS_RGB:
+ case JCS_YCbCr:
+ cinfo.out_color_space = JCS_RGB;
+ break;
+ case JCS_CMYK:
+ case JCS_YCCK:
+ cinfo.out_color_space = JCS_CMYK;
+ break;
+ default:
+ cinfo.out_color_space = JCS_RGB;
+ break;
+ }
+
+/* head decoding */
+ jpeg_calc_output_dimensions(&(cinfo));
+ jpeg_start_decompress(&cinfo);
+
+ w = cinfo.output_width;
+ h = cinfo.output_height;
+
+ if (change_wh)
+ {
+ ie_w = prop->h;
+ ie_h = prop->w;
+ }
+ else
+ {
+ ie_w = prop->w;
+ ie_h = prop->h;
+ }
+
+ if ((opts->region.w > 0) && (opts->region.h > 0))
+ {
+ region = 1;
+
+ opts_region.x = opts->region.x;
+ opts_region.y = opts->region.y;
+ opts_region.w = opts->region.w;
+ opts_region.h = opts->region.h;
+
+ if (prop->rotated)
+ {
+ unsigned int load_region_x = 0, load_region_y = 0;
+ unsigned int load_region_w = 0, load_region_h = 0;
+
+ load_region_x = opts->region.x;
+ load_region_y = opts->region.y;
+ load_region_w = opts->region.w;
+ load_region_h = opts->region.h;
+
+ _rotate_region(&opts_region.x, &opts_region.y, &opts_region.w, &opts_region.h,
+ load_region_x, load_region_y, load_region_w, load_region_h,
+ w, h, degree, prop->flipped);
+ }
+#ifdef BUILD_LOADER_JPEG_REGION
+ cinfo.region_x = opts_region.x;
+ cinfo.region_y = opts_region.y;
+ cinfo.region_w = opts_region.w;
+ cinfo.region_h = opts_region.h;
+#endif
+ }
+ if ((!region) && ((w != ie_w) || (h != ie_h)))
+ {
+ // race condition, the file could have change from when we call header
+ // this test will not solve the problem with region code.
+ jpeg_destroy_decompress(&cinfo);
+ _emile_jpeg_membuf_src_term(&cinfo);
+ *error = EMILE_IMAGE_LOAD_ERROR_GENERIC;
+ return EINA_FALSE;
+ }
+ if ((region) &&
+ ((ie_w != opts_region.w) || (ie_h != opts_region.h)))
+ {
+ jpeg_destroy_decompress(&cinfo);
+ _emile_jpeg_membuf_src_term(&cinfo);
+ *error = EMILE_IMAGE_LOAD_ERROR_GENERIC;
+ return EINA_FALSE;
+ /* ie_w = opts_region.w; */
+ /* ie_h = opts_region.h; */
+ /* if (change_wh) */
+ /* { */
+ /* ie->w = ie_h; */
+ /* ie->h = ie_w; */
+ /* } */
+ /* else */
+ /* { */
+ /* ie->w = ie_w; */
+ /* ie->h = ie_h; */
+ /* } */
+ }
+
+ if (!(((cinfo.out_color_space == JCS_RGB) &&
+ ((cinfo.output_components == 3) || (cinfo.output_components == 1))) ||
+ ((cinfo.out_color_space == JCS_CMYK) && (cinfo.output_components == 4))))
+ {
+ jpeg_destroy_decompress(&cinfo);
+ _emile_jpeg_membuf_src_term(&cinfo);
+ *error = EMILE_IMAGE_LOAD_ERROR_UNKNOWN_FORMAT;
+ return EINA_FALSE;
+ }
+
+/* end head decoding */
+/* data decoding */
+ if (cinfo.rec_outbuf_height > 16)
+ {
+ jpeg_destroy_decompress(&cinfo);
+ _emile_jpeg_membuf_src_term(&cinfo);
+ *error = EMILE_IMAGE_LOAD_ERROR_UNKNOWN_FORMAT;
+ return EINA_FALSE;
+ }
+ data = alloca(w * 16 * cinfo.output_components);
+ if ((prop->rotated) && change_wh)
+ {
+ ptr2 = malloc(w * h * sizeof(uint32_t));
+ ptr_rotate = ptr2;
+ }
+ else
+ ptr2 = pixels;
+
+ if (!ptr2)
+ {
+ *error = EMILE_IMAGE_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED;
+ return EINA_FALSE;
+ }
+
+ /* We handle first CMYK (4 components) */
+ if (cinfo.output_components == 4)
+ {
+ // FIXME: handle region
+ for (i = 0; (int)i < cinfo.rec_outbuf_height; i++)
+ line[i] = data + (i * w * 4);
+ for (l = 0; l < h; l += cinfo.rec_outbuf_height)
+ {
+ jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height);
+ scans = cinfo.rec_outbuf_height;
+ if ((h - l) < scans) scans = h - l;
+ ptr = data;
+ if (!region)
+ {
+ for (y = 0; y < scans; y++)
+ {
+ if (cinfo.saw_Adobe_marker)
+ {
+ for (x = 0; x < w; x++)
+ {
+ /* According to libjpeg doc, Photoshop inverse the values of C, M, Y and K, */
+ /* that is C is replaces by 255 - C, etc...*/
+ /* See the comment below for the computation of RGB values from CMYK ones. */
+ *ptr2 =
+ (0xff000000) |
+ ((ptr[0] * ptr[3] / 255) << 16) |
+ ((ptr[1] * ptr[3] / 255) << 8) |
+ ((ptr[2] * ptr[3] / 255));
+ ptr += 4;
+ ptr2++;
+ }
+ }
+ else
+ {
+ for (x = 0; x < w; x++)
+ {
+ /* Conversion from CMYK to RGB is done in 2 steps: */
+ /* CMYK => CMY => RGB (see http://www.easyrgb.com/index.php?X=MATH) */
+ /* after computation, if C, M, Y and K are between 0 and 1, we have: */
+ /* R = (1 - C) * (1 - K) * 255 */
+ /* G = (1 - M) * (1 - K) * 255 */
+ /* B = (1 - Y) * (1 - K) * 255 */
+ /* libjpeg stores CMYK values between 0 and 255, */
+ /* so we replace C by C * 255 / 255, etc... and we obtain: */
+ /* R = (255 - C) * (255 - K) / 255 */
+ /* G = (255 - M) * (255 - K) / 255 */
+ /* B = (255 - Y) * (255 - K) / 255 */
+ /* with C, M, Y and K between 0 and 255. */
+ *ptr2 =
+ (0xff000000) |
+ (((255 - ptr[0]) * (255 - ptr[3]) / 255) << 16) |
+ (((255 - ptr[1]) * (255 - ptr[3]) / 255) << 8) |
+ (((255 - ptr[2]) * (255 - ptr[3]) / 255));
+ ptr += 4;
+ ptr2++;
+ }
+ }
+ }
+ }
+ else
+ {
+ // if line # > region last line, break
+ if (l >= (opts_region.y + opts_region.h))
+ {
+ line_done = EINA_TRUE;
+ /* if rotation flag is set , we have to rotate image */
+ goto done;
+ /*jpeg_destroy_decompress(&cinfo);
+ _emile_jpeg_membuf_src_term(&cinfo);
+ *error = EMILE_IMAGE_LOAD_ERROR_NONE;
+ return EINA_FALSE;*/
+ }
+ // els if scan block intersects region start or later
+ else if ((l + scans) >
+ (opts_region.y))
+ {
+ for (y = 0; y < scans; y++)
+ {
+ if (((y + l) >= opts_region.y) &&
+ ((y + l) < (opts_region.y + opts_region.h)))
+ {
+ ptr += opts_region.x;
+ if (cinfo.saw_Adobe_marker)
+ {
+ for (x = 0; x < opts_region.w; x++)
+ {
+ /* According to libjpeg doc, Photoshop inverse the values of C, M, Y and K, */
+ /* that is C is replaces by 255 - C, etc...*/
+ /* See the comment below for the computation of RGB values from CMYK ones. */
+ *ptr2 =
+ (0xff000000) |
+ ((ptr[0] * ptr[3] / 255) << 16) |
+ ((ptr[1] * ptr[3] / 255) << 8) |
+ ((ptr[2] * ptr[3] / 255));
+ ptr += 4;
+ ptr2++;
+ }
+ }
+ else
+ {
+ for (x = 0; x < opts_region.w; x++)
+ {
+ /* Conversion from CMYK to RGB is done in 2 steps: */
+ /* CMYK => CMY => RGB (see http://www.easyrgb.com/index.php?X=MATH) */
+ /* after computation, if C, M, Y and K are between 0 and 1, we have: */
+ /* R = (1 - C) * (1 - K) * 255 */
+ /* G = (1 - M) * (1 - K) * 255 */
+ /* B = (1 - Y) * (1 - K) * 255 */
+ /* libjpeg stores CMYK values between 0 and 255, */
+ /* so we replace C by C * 255 / 255, etc... and we obtain: */
+ /* R = (255 - C) * (255 - K) / 255 */
+ /* G = (255 - M) * (255 - K) / 255 */
+ /* B = (255 - Y) * (255 - K) / 255 */
+ /* with C, M, Y and K between 0 and 255. */
+ *ptr2 =
+ (0xff000000) |
+ (((255 - ptr[0]) * (255 - ptr[3]) / 255) << 16) |
+ (((255 - ptr[1]) * (255 - ptr[3]) / 255) << 8) |
+ (((255 - ptr[2]) * (255 - ptr[3]) / 255));
+ ptr += 4;
+ ptr2++;
+ }
+ }
+ ptr += (4 * (w - (opts_region.x + opts_region.w)));
+ }
+ else
+ ptr += (4 * w);
+ }
+ }
+ }
+ }
+ }
+ /* We handle then RGB with 3 components */
+ else if (cinfo.output_components == 3)
+ {
+/*
+ double t;
+ if (region)
+ {
+ // debug for now
+ printf("R| %p %5ix%5i %s: %5i %5i %5ix%5i - ",
+ ie,
+ ie->w, ie->h,
+ ie->file,
+ opts_region.x,
+ opts_region.y,
+ opts_region.w,
+ opts_region.h);
+ }
+ t = get_time();
+ */
+ for (i = 0; (int)i < cinfo.rec_outbuf_height; i++)
+ line[i] = data + (i * w * 3);
+ for (l = 0; l < h; l += cinfo.rec_outbuf_height)
+ {
+ jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height);
+ scans = cinfo.rec_outbuf_height;
+ if ((h - l) < scans) scans = h - l;
+ ptr = data;
+ if (!region)
+ {
+ for (y = 0; y < scans; y++)
+ {
+ for (x = 0; x < w; x++)
+ {
+ *ptr2 = ARGB_JOIN(0xff, ptr[0], ptr[1], ptr[2]);
+ ptr += 3;
+ ptr2++;
+ }
+ }
+ }
+ else
+ {
+ // if line # > region last line, break
+ // but not return immediately for rotation job
+ if (l >= (opts_region.y + opts_region.h))
+ {
+ line_done = EINA_TRUE;
+ /* if rotation flag is set , we have to rotate image */
+ goto done;
+ }
+ // else if scan block intersects region start or later
+ else if ((l + scans) >
+ (opts_region.y))
+ {
+ for (y = 0; y < scans; y++)
+ {
+ if (((y + l) >= opts_region.y) &&
+ ((y + l) < (opts_region.y + opts_region.h)))
+ {
+ ptr += (3 * opts_region.x);
+ for (x = 0; x < opts_region.w; x++)
+ {
+ *ptr2 = ARGB_JOIN(0xff, ptr[0], ptr[1], ptr[2]);
+ ptr += 3;
+ ptr2++;
+ }
+ ptr += (3 * (w - (opts_region.x + opts_region.w)));
+ }
+ else
+ ptr += (3 * w);
+ }
+ }
+ }
+ }
+/*
+ t = get_time() - t;
+ printf("%3.3f\n", t);
+ */
+ }
+ /* We finally handle RGB with 1 component */
+ else if (cinfo.output_components == 1)
+ {
+ for (i = 0; (int)i < cinfo.rec_outbuf_height; i++)
+ line[i] = data + (i * w);
+ for (l = 0; l < h; l += cinfo.rec_outbuf_height)
+ {
+ jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height);
+ scans = cinfo.rec_outbuf_height;
+ if ((h - l) < scans) scans = h - l;
+ ptr = data;
+ if (!region)
+ {
+ for (y = 0; y < scans; y++)
+ {
+ for (x = 0; x < w; x++)
+ {
+ *ptr2 = ARGB_JOIN(0xff, ptr[0], ptr[0], ptr[0]);
+ ptr++;
+ ptr2++;
+ }
+ }
+ }
+ else
+ {
+ // if line # > region last line, break
+ if (l >= (opts_region.y + opts_region.h))
+ {
+ line_done = EINA_TRUE;
+ /* if rotation flag is set , we have to rotate image */
+ goto done;
+ /*jpeg_destroy_decompress(&cinfo);
+ _emile_jpeg_membuf_src_term(&cinfo);
+ *error = EMILE_IMAGE_LOAD_ERROR_NONE;
+ return EINA_TRUE;*/
+ }
+ // els if scan block intersects region start or later
+ else if ((l + scans) >
+ (opts_region.y))
+ {
+ for (y = 0; y < scans; y++)
+ {
+ if (((y + l) >= opts_region.y) &&
+ ((y + l) < (opts_region.y + opts_region.h)))
+ {
+ ptr += opts_region.x;
+ for (x = 0; x < opts_region.w; x++)
+ {
+ *ptr2 = ARGB_JOIN(0xff, ptr[0], ptr[0], ptr[0]);
+ ptr++;
+ ptr2++;
+ }
+ ptr += w - (opts_region.x + opts_region.w);
+ }
+ else
+ ptr += w;
+ }
+ }
+ }
+ }
+ }
+ /* if rotation operation need, rotate it */
+done:
+
+ if (prop->rotated)
+ {
+ uint32_t *to;
+ int hw;
+
+ hw = w * h;
+ to = pixels;
+
+ switch (degree)
+ {
+ case 90:
+ if (prop->flipped)
+ _rotate_change_wh(to + hw - 1, ptr_rotate, w, h, hw - 1, -h);
+ else
+ _rotate_change_wh(to + h - 1, ptr_rotate, w, h, -hw - 1, h);
+ break;
+ case 180:
+ if (prop->flipped)
+ _flip_vertical(to, w, h);
+ else
+ _rotate_180(to, w, h);
+ break;
+ case 270:
+ if (prop->flipped)
+ _rotate_change_wh(to, ptr_rotate, w, h, -hw + 1, h);
+ else
+ _rotate_change_wh(to + hw - h, ptr_rotate, w, h, hw + 1, -h);
+ break;
+ default:
+ if (prop->flipped)
+ _flip_horizontal(to, w, h);
+ break;
+ }
+ if (ptr_rotate)
+ {
+ free(ptr_rotate);
+ ptr_rotate = NULL;
+ }
+ }
+
+ if (line_done)
+ {
+ jpeg_destroy_decompress(&cinfo);
+ _emile_jpeg_membuf_src_term(&cinfo);
+ *error = EMILE_IMAGE_LOAD_ERROR_NONE;
+ return EINA_FALSE;
+ }
+ /* end data decoding */
+ jpeg_finish_decompress(&cinfo);
+ jpeg_destroy_decompress(&cinfo);
+ _emile_jpeg_membuf_src_term(&cinfo);
+ *error = EMILE_IMAGE_LOAD_ERROR_NONE;
+ return EINA_TRUE;
}
static void
-_emile_jpeg_close(Emile_Image *image)
+_emile_jpeg_close(Emile_Image *image EINA_UNUSED)
{
+ // JPEG file loader doesn't keep any data allocated around (for now)
}
/* Generic helper to instantiate a new Emile_Image */