#define PERL_NO_GET_CONTEXT
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include <stddef.h>
#include <stdio.h>
#include "jpeglib.h"
enum
error_action { QUIET = 1, WARN, FATAL };
struct
my_error_mgr {
struct
jpeg_error_mgr base;
enum
error_action on_error, on_warning;
jmp_buf
jmp_buffer;
int
error_pos;
char
error_buffers[2][JMSG_LENGTH_MAX];
};
#define LAST_ERROR( mgr) ((mgr)->error_buffers[ (mgr)->error_pos ])
#define OTHER_ERROR(mgr) ((mgr)->error_buffers[ 1 - (mgr)->error_pos ])
#define CLEAR_ERRORS(mgr) do { \
(mgr)->error_pos = 0; \
(mgr)->error_buffers[0][0] =
'\0'
; \
(mgr)->error_buffers[1][0] =
'\0'
; \
}
while
(0)
typedef
struct
sizer *Image__JPEG__Size;
struct
sizer {
struct
jpeg_decompress_struct cinfo;
struct
my_error_mgr error_mgr;
};
#define EQ_STR_LIT(str, len, lit) \
((len) ==
sizeof
(
""
lit) - 1 &&
strcmp
((str), lit) == 0)
static
enum
error_action
parse_action(pTHX_ SV *sv,
const
char
*what)
{
const
char
*str;
STRLEN len;
SvGETMAGIC(sv);
if
(!SvPOK(sv)) {
goto
bad;
}
str = SvPV(sv, len);
if
(EQ_STR_LIT(str, len,
"quiet"
)) {
return
QUIET;
}
else
if
(EQ_STR_LIT(str, len,
"fatal"
)) {
return
FATAL;
}
else
if
(EQ_STR_LIT(str, len,
"warn"
)) {
return
WARN;
}
bad:
croak(
"Invalid %s-handling action %"
SVf, what, sv);
}
static
struct
my_error_mgr *
get_error_mgr(j_common_ptr cinfo)
{
return
(
struct
my_error_mgr *) cinfo->err;
}
static
void
my_output_message(j_common_ptr cinfo)
{
struct
my_error_mgr *mgr = get_error_mgr(cinfo);
mgr->error_pos = 1 - mgr->error_pos;
cinfo->err->format_message(cinfo, LAST_ERROR(mgr));
}
static
void
my_emit_message(j_common_ptr cinfo,
int
msg_level)
{
if
(msg_level == -1) {
struct
my_error_mgr *mgr = get_error_mgr(cinfo);
if
(mgr->on_warning == WARN) {
cinfo->err->output_message(cinfo);
if
(strNE( LAST_ERROR(mgr), OTHER_ERROR(mgr) )) {
warn(
"%s"
, LAST_ERROR(mgr));
}
}
else
if
(mgr->on_warning == FATAL) {
cinfo->err->output_message(cinfo);
longjmp
(mgr->jmp_buffer, WARN);
}
}
}
static
void
my_error_exit(j_common_ptr cinfo)
{
struct
my_error_mgr *mgr = get_error_mgr(cinfo);
if
(mgr->on_error != QUIET) {
cinfo->err->output_message(cinfo);
}
longjmp
(mgr->jmp_buffer, FATAL);
}
MODULE = Image::JPEG::Size PACKAGE = Image::JPEG::Size
PROTOTYPES: DISABLE
Image::JPEG::Size
_new(package, options)
char
*package
SV *options
INIT:
HV *opthv;
SV *optsv, **svp;
struct
sizer *self;
enum
error_action on_warning;
struct
my_error_mgr error_mgr;
CODE:
if
(!options || !SvROK(options)
|| SvTYPE( (optsv = SvRV(options)) ) != SVt_PVHV) {
croak(
"Options must be a hash ref"
);
}
opthv = (HV *) optsv;
if
((svp = hv_fetchs(opthv,
"error"
, FALSE))) {
error_mgr.on_error = parse_action(aTHX_ *svp,
"error"
);
}
else
{
error_mgr.on_error = FATAL;
}
if
((svp = hv_fetchs(opthv,
"warning"
, FALSE))) {
error_mgr.on_warning = parse_action(aTHX_ *svp,
"warning"
);
}
else
{
error_mgr.on_warning = WARN;
}
Newxc(self, 1,
struct
sizer,
struct
sizer);
self->error_mgr = error_mgr;
CLEAR_ERRORS(&self->error_mgr);
self->cinfo.err = jpeg_std_error(&self->error_mgr.base);
self->error_mgr.base.error_exit = my_error_exit;
self->error_mgr.base.emit_message = my_emit_message;
self->error_mgr.base.output_message = my_output_message;
if
(
setjmp
(self->error_mgr.jmp_buffer)) {
char
error[JMSG_LENGTH_MAX];
my_strlcpy(error, LAST_ERROR(&self->error_mgr),
sizeof
error);
jpeg_destroy_decompress(&self->cinfo);
Safefree(self);
croak(
"%s"
, error);
}
jpeg_create_decompress(&self->cinfo);
RETVAL = self;
OUTPUT:
RETVAL
void
_destroy(self)
Image::JPEG::Size self
CODE:
jpeg_destroy_decompress(&self->cinfo);
Safefree(self);
void
file_dimensions(self, filename)
Image::JPEG::Size self
char
*filename
INIT:
FILE
*f;
JDIMENSION width = 0, height = 0;
int
longjmp_reason;
PPCODE:
f =
fopen
(filename,
"rb"
);
if
(!f) {
croak(
"Can't open %s: %s"
, filename,
strerror
(
errno
));
}
CLEAR_ERRORS(&self->error_mgr);
if
((longjmp_reason =
setjmp
(self->error_mgr.jmp_buffer))) {
fclose
(f);
if
(longjmp_reason == WARN || self->error_mgr.on_error == FATAL) {
jpeg_abort_decompress(&self->cinfo);
croak(
"%s"
, LAST_ERROR(&self->error_mgr));
}
else
if
(self->error_mgr.on_error == WARN) {
warn(
"%s"
, LAST_ERROR(&self->error_mgr));
}
}
else
{
jpeg_stdio_src(&self->cinfo, f);
jpeg_read_header(&self->cinfo, 1);
width = self->cinfo.image_width;
height = self->cinfo.image_height;
fclose
(f);
}
jpeg_abort_decompress(&self->cinfo);
EXTEND(SP, 2);
mPUSHu(width);
mPUSHu(height);