legacy-imlib2/src/modules/loaders/loader_jpeg.c

358 lines
9.1 KiB
C

#include "config.h"
#include "Imlib2_Loader.h"
#include <jpeglib.h>
#include <setjmp.h>
#include "exif.h"
#define DBG_PFX "LDR-jpg"
static const char *const _formats[] = { "jpg", "jpeg", "jfif", "jfi" };
typedef struct {
struct jpeg_error_mgr jem;
sigjmp_buf setjmp_buffer;
uint8_t *data;
} ImLib_JPEG_data;
static void
_JPEGFatalErrorHandler(j_common_ptr jcp)
{
ImLib_JPEG_data *jd = (ImLib_JPEG_data *) jcp->err;
#if 0
jcp->err->output_message(jcp);
#endif
siglongjmp(jd->setjmp_buffer, 1);
}
static void
_JPEGErrorHandler(j_common_ptr jcp)
{
#if 0
ImLib_JPEG_data *jd = (ImLib_JPEG_data *) jcp->err;
jcp->err->output_message(jcp);
siglongjmp(jd->setjmp_buffer, 1);
#endif
}
static void
_JPEGErrorHandler2(j_common_ptr jcp, int msg_level)
{
#if 0
ImLib_JPEG_data *jd = (ImLib_JPEG_data *) jcp->err;
jcp->err->output_message(jcp);
siglongjmp(jd->setjmp_buffer, 1);
#endif
}
static struct jpeg_error_mgr *
_jdata_init(ImLib_JPEG_data * jd)
{
struct jpeg_error_mgr *jem;
jem = jpeg_std_error(&jd->jem);
jd->jem.error_exit = _JPEGFatalErrorHandler;
jd->jem.emit_message = _JPEGErrorHandler2;
jd->jem.output_message = _JPEGErrorHandler;
jd->data = NULL;
return jem;
}
static int
_load(ImlibImage * im, int load_data)
{
int w, h, rc;
struct jpeg_decompress_struct jds;
ImLib_JPEG_data jdata;
uint8_t *ptr, *line[16];
uint32_t *imdata;
int x, y, l, scans, inc;
ExifInfo ei = { 0 };
rc = LOAD_FAIL;
/* set up error handling */
jds.err = _jdata_init(&jdata);
if (sigsetjmp(jdata.setjmp_buffer, 1))
QUIT_WITH_RC(LOAD_FAIL);
jpeg_create_decompress(&jds);
jpeg_mem_src(&jds, im->fi->fdata, im->fi->fsize);
jpeg_save_markers(&jds, JPEG_APP0 + 1, 256);
jpeg_read_header(&jds, TRUE);
rc = LOAD_BADIMAGE; /* Format accepted */
/* Get orientation */
ei.orientation = ORIENT_TOPLEFT;
if (jds.marker_list)
{
jpeg_saved_marker_ptr m = jds.marker_list;
D("Markers: %p: m=%02x len=%d/%d\n", m,
m->marker, m->original_length, m->data_length);
exif_parse(m->data, m->data_length, &ei);
}
w = jds.image_width;
h = jds.image_height;
if (!IMAGE_DIMENSIONS_OK(w, h))
goto quit;
if (ei.swap_wh)
{
im->w = h;
im->h = w;
}
else
{
im->w = w;
im->h = h;
}
if (!load_data)
QUIT_WITH_RC(LOAD_SUCCESS);
/* Load data */
jds.do_fancy_upsampling = FALSE;
jds.do_block_smoothing = FALSE;
jpeg_start_decompress(&jds);
if ((jds.rec_outbuf_height > 16) || (jds.output_components <= 0))
goto quit;
jdata.data = malloc(w * 16 * jds.output_components);
if (!jdata.data)
QUIT_WITH_RC(LOAD_OOM);
/* must set the im->data member before callign progress function */
imdata = __imlib_AllocateData(im);
if (!imdata)
QUIT_WITH_RC(LOAD_OOM);
for (y = 0; y < jds.rec_outbuf_height; y++)
line[y] = jdata.data + (y * w * jds.output_components);
for (l = 0; l < h; l += jds.rec_outbuf_height)
{
jpeg_read_scanlines(&jds, line, jds.rec_outbuf_height);
scans = jds.rec_outbuf_height;
if ((h - l) < scans)
scans = h - l;
for (y = 0; y < scans; y++)
{
ptr = line[y];
switch (ei.orientation)
{
default:
case ORIENT_TOPLEFT:
imdata = im->data + (l + y) * w;
inc = 1;
break;
case ORIENT_TOPRIGHT:
imdata = im->data + (l + y) * w + w - 1;
inc = -1;
break;
case ORIENT_BOTRIGHT:
imdata = im->data + (h - 1 - (l + y)) * w + w - 1;
inc = -1;
break;
case ORIENT_BOTLEFT:
imdata = im->data + (h - 1 - (l + y)) * w;
inc = 1;
break;
case ORIENT_LEFTTOP:
imdata = im->data + (l + y);
inc = h;
break;
case ORIENT_RIGHTTOP:
imdata = im->data + (h - 1 - (l + y));
inc = h;
break;
case ORIENT_RIGHTBOT:
imdata = im->data + (h - 1 - (l + y)) + (w - 1) * h;
inc = -h;
break;
case ORIENT_LEFTBOT:
imdata = im->data + (l + y) + (w - 1) * h;
inc = -h;
break;
}
DL("l,s,y=%d,%d, %d - x,y=%4ld,%4ld\n", l, y, l + y,
(long)((imdata - im->data) % im->w),
(long)((imdata - im->data) / im->w));
switch (jds.out_color_space)
{
default:
goto quit;
case JCS_GRAYSCALE:
for (x = 0; x < w; x++)
{
*imdata = PIXEL_ARGB(0xff, ptr[0], ptr[0], ptr[0]);
ptr++;
imdata += inc;
}
break;
case JCS_RGB:
for (x = 0; x < w; x++)
{
*imdata = PIXEL_ARGB(0xff, ptr[0], ptr[1], ptr[2]);
ptr += jds.output_components;
imdata += inc;
}
break;
case JCS_CMYK:
for (x = 0; x < w; x++)
{
*imdata = PIXEL_ARGB(0xff, ptr[0] * ptr[3] / 255,
ptr[1] * ptr[3] / 255,
ptr[2] * ptr[3] / 255);
ptr += jds.output_components;
imdata += inc;
}
break;
}
}
if (ei.orientation != ORIENT_TOPLEFT &&
ei.orientation != ORIENT_TOPRIGHT)
continue;
if (im->lc && __imlib_LoadProgressRows(im, l, scans))
QUIT_WITH_RC(LOAD_BREAK);
}
if (ei.orientation != ORIENT_TOPLEFT && ei.orientation != ORIENT_TOPRIGHT)
{
if (im->lc)
__imlib_LoadProgressRows(im, 0, im->h);
}
jpeg_finish_decompress(&jds);
rc = LOAD_SUCCESS;
quit:
jpeg_destroy_decompress(&jds);
free(jdata.data);
return rc;
}
static int
_save(ImlibImage * im)
{
int rc;
struct jpeg_compress_struct jcs;
ImLib_JPEG_data jdata;
FILE *f = im->fi->fp;
uint8_t *buf;
const uint32_t *imdata;
JSAMPROW *jbuf;
int y, quality, compression;
ImlibImageTag *tag;
int i, j;
/* allocate a small buffer to convert image data */
buf = malloc(im->w * 3 * sizeof(uint8_t));
if (!buf)
return LOAD_FAIL;
rc = LOAD_FAIL;
/* set up error handling */
jcs.err = _jdata_init(&jdata);
if (sigsetjmp(jdata.setjmp_buffer, 1))
QUIT_WITH_RC(LOAD_FAIL);
/* setup compress params */
jpeg_create_compress(&jcs);
jpeg_stdio_dest(&jcs, f);
jcs.image_width = im->w;
jcs.image_height = im->h;
jcs.input_components = 3;
jcs.in_color_space = JCS_RGB;
/* look for tags attached to image to get extra parameters like quality */
/* settigns etc. - this is the "api" to hint for extra information for */
/* saver modules */
/* compression */
compression = 2;
tag = __imlib_GetTag(im, "compression");
if (tag)
{
compression = tag->val;
if (compression < 0)
compression = 0;
if (compression > 9)
compression = 9;
}
/* convert to quality */
quality = (9 - compression) * 10;
quality = quality * 10 / 9;
/* quality */
tag = __imlib_GetTag(im, "quality");
if (tag)
quality = tag->val;
if (quality < 1)
quality = 1;
if (quality > 100)
quality = 100;
/* set up jepg compression parameters */
jpeg_set_defaults(&jcs);
jpeg_set_quality(&jcs, quality, TRUE);
/* progressive */
if ((tag = __imlib_GetTag(im, "interlacing")) && tag->val)
jpeg_simple_progression(&jcs);
jpeg_start_compress(&jcs, TRUE);
/* get the start pointer */
imdata = im->data;
/* go one scanline at a time... and save */
for (y = 0; jcs.next_scanline < jcs.image_height; y++)
{
/* convcert scaline from ARGB to RGB packed */
for (j = 0, i = 0; i < im->w; i++)
{
uint32_t pixel = *imdata++;
buf[j++] = PIXEL_R(pixel);
buf[j++] = PIXEL_G(pixel);
buf[j++] = PIXEL_B(pixel);
}
/* write scanline */
jbuf = (JSAMPROW *) (&buf);
jpeg_write_scanlines(&jcs, jbuf, 1);
if (im->lc && __imlib_LoadProgressRows(im, y, 1))
QUIT_WITH_RC(LOAD_BREAK);
}
rc = LOAD_SUCCESS;
quit:
/* finish off */
jpeg_finish_compress(&jcs);
jpeg_destroy_compress(&jcs);
free(buf);
return rc;
}
IMLIB_LOADER(_formats, _load, _save);