Initial C based implementation of ImageMagick plugin

This commit is contained in:
Kovid Goyal 2010-08-01 20:00:59 -06:00
parent b2a06ab07a
commit e4ab5891aa
7 changed files with 598 additions and 1 deletions

View File

@ -72,6 +72,13 @@ extensions = [
lib_dirs=chmlib_lib_dirs,
cflags=["-D__PYTHON__"]),
Extension('magick',
['calibre/utils/magick/magick.c'],
headers=['calibre/utils/magick/magick_constants.h'],
libraries=magick_libs,
lib_dirs=magick_lib_dirs,
inc_dirs=magick_inc_dirs
),
Extension('pdfreflow',
reflow_sources,

View File

@ -60,6 +60,7 @@ if plugins is None:
'pictureflow',
'lzx',
'msdes',
'magick',
'podofo',
'cPalmdoc',
'fontconfig',

View File

@ -0,0 +1,85 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
__license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os
from calibre.constants import plugins
_magick, _merr = plugins['magick']
if _magick is None:
raise RuntimeError('Failed to load ImageMagick: '+_merr)
# class ImageMagick {{{
_initialized = False
def initialize():
global _initialized
if not _initialized:
_magick.genesis()
_initialized = True
def finalize():
global _initialized
if _initialized:
_magick.terminus()
_initialized = False
class ImageMagick(object):
def __enter__(self):
initialize()
def __exit__(self, *args):
finalize()
# }}}
class Image(_magick.Image):
def open(self, path_or_file):
data = path_or_file
if hasattr(data, 'read'):
data = data.read()
else:
data = open(data, 'rb').read()
self.load(data)
@dynamic_property
def format(self):
def fget(self):
ans = super(Image, self).format
return ans.decode('utf-8', 'ignore').lower()
def fset(self, val):
super(Image, self).format = str(val)
return property(fget=fget, fset=fset, doc=_magick.Image.format.__doc__)
@dynamic_property
def size(self):
def fget(self):
return super(Image, self).size
def fset(self, val):
filter = 'CatromFilter'
if len(val) > 2:
filter = val[2]
filter = int(getattr(_magick, filter))
blur = 1.0
if len(val) > 3:
blur = float(val[3])
super(Image, self).format = (int(val[0]), int(val[1]), filter,
blur)
return property(fget=fget, fset=fset, doc=_magick.Image.size.__doc__)
def save(self, path, format=None):
if format is None:
ext = os.path.splitext(path)[1]
if len(ext) < 2:
raise ValueError('No format specified')
format = ext[1:]
with open(path, 'wb') as f:
f.write(self.export(format))

View File

@ -0,0 +1,70 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
__license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os, textwrap, re, subprocess
INC = '/usr/include/ImageMagick'
'''
Various constants defined in the ImageMagick header files. Note that
they are defined as actual numeric constants rather than symbolic names to
ensure that the extension can be compiled against older versions of ImageMagick
than the one this script is run against.
'''
def parse_enums(f):
print '\nParsing:', f
raw = open(os.path.join(INC, f)).read()
raw = re.sub(r'(?s)/\*.*?\*/', '', raw)
raw = re.sub('#.*', '', raw)
for enum in re.findall(r'typedef\s+enum\s+\{([^}]+)', raw):
enum = re.sub(r'(?s)/\*.*?\*/', '', enum)
for x in enum.splitlines():
e = x.split(',')[0].strip().split(' ')[0]
if e:
val = get_value(e)
print e, val
yield e, val
def get_value(const):
t = '''
#include <wand/MagickWand.h>
#include <stdio.h>
int main(int argc, char **argv) {
printf("%%d", %s);
return 0;
}
'''%const
with open('/tmp/ig.c','wb') as f:
f.write(t)
subprocess.check_call(['gcc', '-I/usr/include/ImageMagick', '/tmp/ig.c', '-o', '/tmp/ig', '-lMagickWand'])
return int(subprocess.Popen(["/tmp/ig"],
stdout=subprocess.PIPE).communicate()[0].strip())
def main():
constants = []
for x in ('resample', 'image', 'draw', 'distort'):
constants += list(parse_enums('magick/%s.h'%x))
base = os.path.dirname(__file__)
constants = [
'PyModule_AddIntConstant(m, "{0}", {1});'.format(c, v) for c, v in
constants]
raw = textwrap.dedent('''\
// Generated by generate.py
static void magick_add_module_constants(PyObject *m) {
%s
}
''')%'\n '.join(constants)
with open(os.path.join(base, 'magick_constants.h'), 'wb') as f:
f.write(raw)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,269 @@
#define UNICODE
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <wand/MagickWand.h>
#include "magick_constants.h"
PyObject* magick_set_exception(MagickWand *wand) {
ExceptionType ext;
char *desc = MagickGetException(wand, &ext);
PyErr_SetString(PyExc_Exception, desc);
MagickClearException(wand);
desc = MagickRelinquishMemory(desc);
return NULL;
}
// Image object definition {{{
typedef struct {
PyObject_HEAD
// Type-specific fields go here.
MagickWand *wand;
} magick_Image;
static void
magick_Image_dealloc(magick_Image* self)
{
if (self->wand != NULL) self->wand = DestroyMagickWand(self->wand);
self->ob_type->tp_free((PyObject*)self);
}
static PyObject *
magick_Image_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
magick_Image *self;
self = (magick_Image *)type->tp_alloc(type, 0);
if (self != NULL) {
self->wand = NewMagickWand();
if (self->wand == NULL || self->wand < 0) {
PyErr_SetString(PyExc_Exception, "Failed to allocate wand. Did you initialize ImageMgick?");
self->wand = NULL;
Py_DECREF(self);
return NULL;
}
}
return (PyObject *)self;
}
static PyObject *
magick_Image_load(magick_Image *self, PyObject *args, PyObject *kwargs) {
const char *data;
Py_ssize_t dlen;
if (!PyArg_ParseTuple(args, "s#", &data, &dlen)) return NULL;
if (!MagickReadImageBlob(self->wand, data, dlen))
return magick_set_exception(self->wand);
Py_RETURN_NONE;
}
static PyObject *
magick_Image_export(magick_Image *self, PyObject *args, PyObject *kwargs) {
char *fmt;
unsigned char *data;
PyObject *ans;
size_t len = 0;
if (!PyArg_ParseTuple(args, "s", &fmt)) return NULL;
if (!MagickSetFormat(self->wand, fmt)) {
PyErr_SetString(PyExc_ValueError, "Unknown image format");
return NULL;
}
data = MagickGetImageBlob(self->wand, &len);
if (data == NULL || len < 1)
return magick_set_exception(self->wand);
ans = Py_BuildValue("s#", data, len);
data = MagickRelinquishMemory(data);
return ans;
}
static PyObject *
magick_Image_size_getter(magick_Image *self, void *closure) {
size_t width, height;
width = MagickGetImageWidth(self->wand);
height = MagickGetImageHeight(self->wand);
return Py_BuildValue("II", width, height);
}
static int
magick_Image_size_setter(magick_Image *self, PyObject *val, void *closure) {
Py_ssize_t width, height;
FilterTypes filter;
double blur;
if (val == NULL) {
return -1;
PyErr_SetString(PyExc_TypeError, "Cannot delete image size");
}
if (!PySequence_Check(val) || PySequence_Length(val) < 4) {
PyErr_SetString(PyExc_TypeError, "Must use at least a 4 element sequence to set size");
return -1;
}
width = PyInt_AsSsize_t(PySequence_ITEM(val, 0));
height = PyInt_AsSsize_t(PySequence_ITEM(val, 1));
filter = (FilterTypes)PyInt_AsSsize_t(PySequence_ITEM(val, 2));
blur = PyFloat_AsDouble(PySequence_ITEM(val, 2));
if (PyErr_Occurred()) {
PyErr_SetString(PyExc_TypeError, "Width, height, filter or blur not a number");
return -1;
}
if ( filter <= UndefinedFilter || filter >= SentinelFilter) {
PyErr_SetString(PyExc_ValueError, "Invalid filter");
return -1;
}
if (!MagickResizeImage(self->wand, width, height, filter, blur)) {
magick_set_exception(self->wand);
return -1;
}
return 0;
}
static PyObject *
magick_Image_format_getter(magick_Image *self, void *closure) {
const char *fmt;
fmt = MagickGetImageFormat(self->wand);
return Py_BuildValue("s", fmt);
}
static PyMethodDef magick_Image_methods[] = {
{"load", (PyCFunction)magick_Image_load, METH_VARARGS,
"Load an image from a byte buffer (string)"
},
{"export", (PyCFunction)magick_Image_export, METH_VARARGS,
"export(format) -> bytestring\n\n Export the image as the specified format"
},
{NULL} /* Sentinel */
};
static PyGetSetDef magick_Image_getsetters[] = {
{(char *)"size",
(getter)magick_Image_size_getter, (setter)magick_Image_size_setter,
(char *)"Image size (width, height). When setting pass in (width, height, filter, blur). See MagickResizeImage docs.",
NULL},
{(char *)"format",
(getter)magick_Image_format_getter, NULL,
(char *)"Image format",
NULL},
{NULL} /* Sentinel */
};
static PyTypeObject magick_ImageType = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
"magick.Image", /*tp_name*/
sizeof(magick_Image), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)magick_Image_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
"Images", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
magick_Image_methods, /* tp_methods */
0, /* tp_members */
magick_Image_getsetters, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
magick_Image_new, /* tp_new */
};
// }}}
// Module functions {{{
static PyObject *
magick_genesis(PyObject *self, PyObject *args)
{
MagickWandGenesis();
Py_RETURN_NONE;
}
static PyObject *
magick_terminus(PyObject *self, PyObject *args)
{
MagickWandTerminus();
Py_RETURN_NONE;
}
static PyMethodDef magick_methods[] = {
{"genesis", magick_genesis, METH_VARARGS,
"genesis()\n\n"
"Initializes ImageMagick.\n"
"Must be called before any other use of this module is made. "
},
{"terminus", magick_terminus, METH_VARARGS,
"terminus()\n\n"
"Cleans up ImageMagick memory structures.\n"
"Must be called after you are done using this module. You can call genesis() again after this to resume using the module."
},
{NULL} /* Sentinel */
};
// }}}
// Module initialization {{{
PyMODINIT_FUNC
initmagick(void)
{
PyObject* m;
if (PyType_Ready(&magick_ImageType) < 0)
return;
m = Py_InitModule3("magick", magick_methods,
"Wrapper for the ImageMagick imaging library");
Py_INCREF(&magick_ImageType);
PyModule_AddObject(m, "Image", (PyObject *)&magick_ImageType);
magick_add_module_constants(m);
}
// }}}

View File

@ -0,0 +1,165 @@
// Generated by generate.py
static void magick_add_module_constants(PyObject *m) {
PyModule_AddIntConstant(m, "UndefinedFilter", 0);
PyModule_AddIntConstant(m, "PointFilter", 1);
PyModule_AddIntConstant(m, "BoxFilter", 2);
PyModule_AddIntConstant(m, "TriangleFilter", 3);
PyModule_AddIntConstant(m, "HermiteFilter", 4);
PyModule_AddIntConstant(m, "HanningFilter", 5);
PyModule_AddIntConstant(m, "HammingFilter", 6);
PyModule_AddIntConstant(m, "BlackmanFilter", 7);
PyModule_AddIntConstant(m, "GaussianFilter", 8);
PyModule_AddIntConstant(m, "QuadraticFilter", 9);
PyModule_AddIntConstant(m, "CubicFilter", 10);
PyModule_AddIntConstant(m, "CatromFilter", 11);
PyModule_AddIntConstant(m, "MitchellFilter", 12);
PyModule_AddIntConstant(m, "LanczosFilter", 13);
PyModule_AddIntConstant(m, "BesselFilter", 14);
PyModule_AddIntConstant(m, "SincFilter", 15);
PyModule_AddIntConstant(m, "KaiserFilter", 16);
PyModule_AddIntConstant(m, "WelshFilter", 17);
PyModule_AddIntConstant(m, "ParzenFilter", 18);
PyModule_AddIntConstant(m, "LagrangeFilter", 19);
PyModule_AddIntConstant(m, "BohmanFilter", 20);
PyModule_AddIntConstant(m, "BartlettFilter", 21);
PyModule_AddIntConstant(m, "SentinelFilter", 22);
PyModule_AddIntConstant(m, "UndefinedInterpolatePixel", 0);
PyModule_AddIntConstant(m, "AverageInterpolatePixel", 1);
PyModule_AddIntConstant(m, "BicubicInterpolatePixel", 2);
PyModule_AddIntConstant(m, "BilinearInterpolatePixel", 3);
PyModule_AddIntConstant(m, "FilterInterpolatePixel", 4);
PyModule_AddIntConstant(m, "IntegerInterpolatePixel", 5);
PyModule_AddIntConstant(m, "MeshInterpolatePixel", 6);
PyModule_AddIntConstant(m, "NearestNeighborInterpolatePixel", 7);
PyModule_AddIntConstant(m, "SplineInterpolatePixel", 8);
PyModule_AddIntConstant(m, "UndefinedAlphaChannel", 0);
PyModule_AddIntConstant(m, "ActivateAlphaChannel", 1);
PyModule_AddIntConstant(m, "BackgroundAlphaChannel", 2);
PyModule_AddIntConstant(m, "CopyAlphaChannel", 3);
PyModule_AddIntConstant(m, "DeactivateAlphaChannel", 4);
PyModule_AddIntConstant(m, "ExtractAlphaChannel", 5);
PyModule_AddIntConstant(m, "OpaqueAlphaChannel", 6);
PyModule_AddIntConstant(m, "ResetAlphaChannel", 7);
PyModule_AddIntConstant(m, "SetAlphaChannel", 8);
PyModule_AddIntConstant(m, "ShapeAlphaChannel", 9);
PyModule_AddIntConstant(m, "TransparentAlphaChannel", 10);
PyModule_AddIntConstant(m, "UndefinedType", 0);
PyModule_AddIntConstant(m, "BilevelType", 1);
PyModule_AddIntConstant(m, "GrayscaleType", 2);
PyModule_AddIntConstant(m, "GrayscaleMatteType", 3);
PyModule_AddIntConstant(m, "PaletteType", 4);
PyModule_AddIntConstant(m, "PaletteMatteType", 5);
PyModule_AddIntConstant(m, "TrueColorType", 6);
PyModule_AddIntConstant(m, "TrueColorMatteType", 7);
PyModule_AddIntConstant(m, "ColorSeparationType", 8);
PyModule_AddIntConstant(m, "ColorSeparationMatteType", 9);
PyModule_AddIntConstant(m, "OptimizeType", 10);
PyModule_AddIntConstant(m, "PaletteBilevelMatteType", 11);
PyModule_AddIntConstant(m, "UndefinedInterlace", 0);
PyModule_AddIntConstant(m, "NoInterlace", 1);
PyModule_AddIntConstant(m, "LineInterlace", 2);
PyModule_AddIntConstant(m, "PlaneInterlace", 3);
PyModule_AddIntConstant(m, "PartitionInterlace", 4);
PyModule_AddIntConstant(m, "GIFInterlace", 5);
PyModule_AddIntConstant(m, "JPEGInterlace", 6);
PyModule_AddIntConstant(m, "PNGInterlace", 7);
PyModule_AddIntConstant(m, "UndefinedOrientation", 0);
PyModule_AddIntConstant(m, "TopLeftOrientation", 1);
PyModule_AddIntConstant(m, "TopRightOrientation", 2);
PyModule_AddIntConstant(m, "BottomRightOrientation", 3);
PyModule_AddIntConstant(m, "BottomLeftOrientation", 4);
PyModule_AddIntConstant(m, "LeftTopOrientation", 5);
PyModule_AddIntConstant(m, "RightTopOrientation", 6);
PyModule_AddIntConstant(m, "RightBottomOrientation", 7);
PyModule_AddIntConstant(m, "LeftBottomOrientation", 8);
PyModule_AddIntConstant(m, "UndefinedResolution", 0);
PyModule_AddIntConstant(m, "PixelsPerInchResolution", 1);
PyModule_AddIntConstant(m, "PixelsPerCentimeterResolution", 2);
PyModule_AddIntConstant(m, "UndefinedTransmitType", 0);
PyModule_AddIntConstant(m, "FileTransmitType", 1);
PyModule_AddIntConstant(m, "BlobTransmitType", 2);
PyModule_AddIntConstant(m, "StreamTransmitType", 3);
PyModule_AddIntConstant(m, "ImageTransmitType", 4);
PyModule_AddIntConstant(m, "UndefinedAlign", 0);
PyModule_AddIntConstant(m, "LeftAlign", 1);
PyModule_AddIntConstant(m, "CenterAlign", 2);
PyModule_AddIntConstant(m, "RightAlign", 3);
PyModule_AddIntConstant(m, "UndefinedPathUnits", 0);
PyModule_AddIntConstant(m, "UserSpace", 1);
PyModule_AddIntConstant(m, "UserSpaceOnUse", 2);
PyModule_AddIntConstant(m, "ObjectBoundingBox", 3);
PyModule_AddIntConstant(m, "UndefinedDecoration", 0);
PyModule_AddIntConstant(m, "NoDecoration", 1);
PyModule_AddIntConstant(m, "UnderlineDecoration", 2);
PyModule_AddIntConstant(m, "OverlineDecoration", 3);
PyModule_AddIntConstant(m, "LineThroughDecoration", 4);
PyModule_AddIntConstant(m, "UndefinedDirection", 0);
PyModule_AddIntConstant(m, "RightToLeftDirection", 1);
PyModule_AddIntConstant(m, "LeftToRightDirection", 2);
PyModule_AddIntConstant(m, "UndefinedRule", 0);
PyModule_AddIntConstant(m, "EvenOddRule", 1);
PyModule_AddIntConstant(m, "NonZeroRule", 2);
PyModule_AddIntConstant(m, "UndefinedGradient", 0);
PyModule_AddIntConstant(m, "LinearGradient", 1);
PyModule_AddIntConstant(m, "RadialGradient", 2);
PyModule_AddIntConstant(m, "UndefinedCap", 0);
PyModule_AddIntConstant(m, "ButtCap", 1);
PyModule_AddIntConstant(m, "RoundCap", 2);
PyModule_AddIntConstant(m, "SquareCap", 3);
PyModule_AddIntConstant(m, "UndefinedJoin", 0);
PyModule_AddIntConstant(m, "MiterJoin", 1);
PyModule_AddIntConstant(m, "RoundJoin", 2);
PyModule_AddIntConstant(m, "BevelJoin", 3);
PyModule_AddIntConstant(m, "UndefinedMethod", 0);
PyModule_AddIntConstant(m, "PointMethod", 1);
PyModule_AddIntConstant(m, "ReplaceMethod", 2);
PyModule_AddIntConstant(m, "FloodfillMethod", 3);
PyModule_AddIntConstant(m, "FillToBorderMethod", 4);
PyModule_AddIntConstant(m, "ResetMethod", 5);
PyModule_AddIntConstant(m, "UndefinedPrimitive", 0);
PyModule_AddIntConstant(m, "PointPrimitive", 1);
PyModule_AddIntConstant(m, "LinePrimitive", 2);
PyModule_AddIntConstant(m, "RectanglePrimitive", 3);
PyModule_AddIntConstant(m, "RoundRectanglePrimitive", 4);
PyModule_AddIntConstant(m, "ArcPrimitive", 5);
PyModule_AddIntConstant(m, "EllipsePrimitive", 6);
PyModule_AddIntConstant(m, "CirclePrimitive", 7);
PyModule_AddIntConstant(m, "PolylinePrimitive", 8);
PyModule_AddIntConstant(m, "PolygonPrimitive", 9);
PyModule_AddIntConstant(m, "BezierPrimitive", 10);
PyModule_AddIntConstant(m, "ColorPrimitive", 11);
PyModule_AddIntConstant(m, "MattePrimitive", 12);
PyModule_AddIntConstant(m, "TextPrimitive", 13);
PyModule_AddIntConstant(m, "ImagePrimitive", 14);
PyModule_AddIntConstant(m, "PathPrimitive", 15);
PyModule_AddIntConstant(m, "UndefinedReference", 0);
PyModule_AddIntConstant(m, "GradientReference", 1);
PyModule_AddIntConstant(m, "UndefinedSpread", 0);
PyModule_AddIntConstant(m, "PadSpread", 1);
PyModule_AddIntConstant(m, "ReflectSpread", 2);
PyModule_AddIntConstant(m, "RepeatSpread", 3);
PyModule_AddIntConstant(m, "UndefinedDistortion", 0);
PyModule_AddIntConstant(m, "AffineDistortion", 1);
PyModule_AddIntConstant(m, "AffineProjectionDistortion", 2);
PyModule_AddIntConstant(m, "ScaleRotateTranslateDistortion", 3);
PyModule_AddIntConstant(m, "PerspectiveDistortion", 4);
PyModule_AddIntConstant(m, "PerspectiveProjectionDistortion", 5);
PyModule_AddIntConstant(m, "BilinearForwardDistortion", 6);
PyModule_AddIntConstant(m, "BilinearDistortion", 6);
PyModule_AddIntConstant(m, "BilinearReverseDistortion", 7);
PyModule_AddIntConstant(m, "PolynomialDistortion", 8);
PyModule_AddIntConstant(m, "ArcDistortion", 9);
PyModule_AddIntConstant(m, "PolarDistortion", 10);
PyModule_AddIntConstant(m, "DePolarDistortion", 11);
PyModule_AddIntConstant(m, "BarrelDistortion", 12);
PyModule_AddIntConstant(m, "BarrelInverseDistortion", 13);
PyModule_AddIntConstant(m, "ShepardsDistortion", 14);
PyModule_AddIntConstant(m, "SentinelDistortion", 15);
PyModule_AddIntConstant(m, "UndefinedColorInterpolate", 0);
PyModule_AddIntConstant(m, "BarycentricColorInterpolate", 1);
PyModule_AddIntConstant(m, "BilinearColorInterpolate", 7);
PyModule_AddIntConstant(m, "PolynomialColorInterpolate", 8);
PyModule_AddIntConstant(m, "ShepardsColorInterpolate", 14);
PyModule_AddIntConstant(m, "VoronoiColorInterpolate", 15);
}

View File

@ -421,7 +421,7 @@ initpodofo(void)
return;
m = Py_InitModule3("podofo", podofo_methods,
"Wrapper for the PoDoFo pDF library");
"Wrapper for the PoDoFo PDF library");
Py_INCREF(&podofo_PDFDocType);
PyModule_AddObject(m, "PDFDoc", (PyObject *)&podofo_PDFDocType);