Remove PythonMagickWand

This commit is contained in:
Kovid Goyal 2010-08-04 16:25:47 -06:00
parent 0b1f4588ac
commit dbb7233d19
8 changed files with 531 additions and 4584 deletions

View File

@ -8,7 +8,6 @@ Based on ideas from comiclrf created by FangornUK.
'''
import os, shutil, traceback, textwrap, time, codecs
from ctypes import byref
from Queue import Empty
from calibre.customize.conversion import InputFormatPlugin, OptionRecommendation
@ -71,141 +70,119 @@ class PageProcessor(list):
def render(self):
import calibre.utils.PythonMagickWand as pw
img = pw.NewMagickWand()
if img < 0:
raise RuntimeError('Cannot create wand.')
if not pw.MagickReadImage(img, self.path_to_page):
severity = pw.ExceptionType(0)
msg = pw.MagickGetException(img, byref(severity))
raise IOError('Failed to read image from: %s: %s'
%(self.path_to_page, msg))
width = pw.MagickGetImageWidth(img)
height = pw.MagickGetImageHeight(img)
from calibre.utils.magick import Image
img = Image()
img.open(self.path_to_page)
width, height = img.size
if self.num == 0: # First image so create a thumbnail from it
thumb = pw.CloneMagickWand(img)
if thumb < 0:
raise RuntimeError('Cannot create wand.')
pw.MagickThumbnailImage(thumb, 60, 80)
pw.MagickWriteImage(thumb, os.path.join(self.dest, 'thumbnail.png'))
pw.DestroyMagickWand(thumb)
thumb = img.clone
thumb.thumbnail(60, 80)
thumb.save(os.path.join(self.dest, 'thumbnail.png'))
self.pages = [img]
if width > height:
if self.opts.landscape:
self.rotate = True
else:
split1, split2 = map(pw.CloneMagickWand, (img, img))
pw.DestroyMagickWand(img)
if split1 < 0 or split2 < 0:
raise RuntimeError('Cannot create wand.')
pw.MagickCropImage(split1, (width/2)-1, height, 0, 0)
pw.MagickCropImage(split2, (width/2)-1, height, width/2, 0 )
split1, split2 = img.clone, img.clone
half = int(width/2)
split1.crop(half-1, height, 0, 0)
split2.crop(half-1, height, half, 0)
self.pages = [split2, split1] if self.opts.right2left else [split1, split2]
self.process_pages()
def process_pages(self):
import calibre.utils.PythonMagickWand as p
from calibre.utils.magick import PixelWand
for i, wand in enumerate(self.pages):
pw = p.NewPixelWand()
try:
if pw < 0:
raise RuntimeError('Cannot create wand.')
p.PixelSetColor(pw, 'white')
pw = PixelWand()
pw.color = 'white'
p.MagickSetImageBorderColor(wand, pw)
if self.rotate:
p.MagickRotateImage(wand, pw, -90)
wand.set_border_color(pw)
if self.rotate:
wand.rotate(pw, -90)
# 25 percent fuzzy trim?
if not self.opts.disable_trim:
p.MagickTrimImage(wand, 25*65535/100)
p.MagickSetImagePage(wand, 0,0,0,0) #Clear page after trim, like a "+repage"
# Do the Photoshop "Auto Levels" equivalent
if not self.opts.dont_normalize:
p.MagickNormalizeImage(wand)
sizex = p.MagickGetImageWidth(wand)
sizey = p.MagickGetImageHeight(wand)
# 25 percent fuzzy trim?
if not self.opts.disable_trim:
wand.trim(25*65535/100)
wand.set_page(0, 0, 0, 0) #Clear page after trim, like a "+repage"
# Do the Photoshop "Auto Levels" equivalent
if not self.opts.dont_normalize:
wand.normalize()
sizex, sizey = wand.size
SCRWIDTH, SCRHEIGHT = self.opts.output_profile.comic_screen_size
SCRWIDTH, SCRHEIGHT = self.opts.output_profile.comic_screen_size
if self.opts.keep_aspect_ratio:
# Preserve the aspect ratio by adding border
aspect = float(sizex) / float(sizey)
if aspect <= (float(SCRWIDTH) / float(SCRHEIGHT)):
newsizey = SCRHEIGHT
newsizex = int(newsizey * aspect)
deltax = (SCRWIDTH - newsizex) / 2
deltay = 0
else:
newsizex = SCRWIDTH
newsizey = int(newsizex / aspect)
deltax = 0
deltay = (SCRHEIGHT - newsizey) / 2
p.MagickResizeImage(wand, newsizex, newsizey, p.CatromFilter, 1.0)
p.MagickSetImageBorderColor(wand, pw)
p.MagickBorderImage(wand, pw, deltax, deltay)
elif self.opts.wide:
# Keep aspect and Use device height as scaled image width so landscape mode is clean
aspect = float(sizex) / float(sizey)
screen_aspect = float(SCRWIDTH) / float(SCRHEIGHT)
# Get dimensions of the landscape mode screen
# Add 25px back to height for the battery bar.
wscreenx = SCRHEIGHT + 25
wscreeny = int(wscreenx / screen_aspect)
if aspect <= screen_aspect:
newsizey = wscreeny
newsizex = int(newsizey * aspect)
deltax = (wscreenx - newsizex) / 2
deltay = 0
else:
newsizex = wscreenx
newsizey = int(newsizex / aspect)
deltax = 0
deltay = (wscreeny - newsizey) / 2
p.MagickResizeImage(wand, newsizex, newsizey, p.CatromFilter, 1.0)
p.MagickSetImageBorderColor(wand, pw)
p.MagickBorderImage(wand, pw, deltax, deltay)
if self.opts.keep_aspect_ratio:
# Preserve the aspect ratio by adding border
aspect = float(sizex) / float(sizey)
if aspect <= (float(SCRWIDTH) / float(SCRHEIGHT)):
newsizey = SCRHEIGHT
newsizex = int(newsizey * aspect)
deltax = (SCRWIDTH - newsizex) / 2
deltay = 0
else:
p.MagickResizeImage(wand, SCRWIDTH, SCRHEIGHT, p.CatromFilter, 1.0)
newsizex = SCRWIDTH
newsizey = int(newsizex / aspect)
deltax = 0
deltay = (SCRHEIGHT - newsizey) / 2
wand.size = (newsizex, newsizey)
wand.set_border_color(pw)
wand.add_border(pw, deltax, deltay)
elif self.opts.wide:
# Keep aspect and Use device height as scaled image width so landscape mode is clean
aspect = float(sizex) / float(sizey)
screen_aspect = float(SCRWIDTH) / float(SCRHEIGHT)
# Get dimensions of the landscape mode screen
# Add 25px back to height for the battery bar.
wscreenx = SCRHEIGHT + 25
wscreeny = int(wscreenx / screen_aspect)
if aspect <= screen_aspect:
newsizey = wscreeny
newsizex = int(newsizey * aspect)
deltax = (wscreenx - newsizex) / 2
deltay = 0
else:
newsizex = wscreenx
newsizey = int(newsizex / aspect)
deltax = 0
deltay = (wscreeny - newsizey) / 2
wand.size = (newsizex, newsizey)
wand.set_border_color(pw)
wand.add_border(pw, deltax, deltay)
else:
wand.size = (SCRWIDTH, SCRHEIGHT)
if not self.opts.dont_sharpen:
p.MagickSharpenImage(wand, 0.0, 1.0)
if not self.opts.dont_sharpen:
wand.sharpen(0.0, 1.0)
if not self.opts.dont_grayscale:
p.MagickSetImageType(wand, p.GrayscaleType)
if not self.opts.dont_grayscale:
wand.type = 'GrayscaleType'
if self.opts.despeckle:
p.MagickDespeckleImage(wand)
if self.opts.despeckle:
wand.despeckle()
p.MagickQuantizeImage(wand, self.opts.colors, p.RGBColorspace, 0, 1, 0)
dest = '%d_%d.%s'%(self.num, i, self.opts.output_format)
dest = os.path.join(self.dest, dest)
p.MagickWriteImage(wand, dest+'8')
os.rename(dest+'8', dest)
self.append(dest)
finally:
if pw > 0:
p.DestroyPixelWand(pw)
p.DestroyMagickWand(wand)
wand.quantize(self.opts.colors)
dest = '%d_%d.%s'%(self.num, i, self.opts.output_format)
dest = os.path.join(self.dest, dest)
wand.save(dest+'8')
os.rename(dest+'8', dest)
self.append(dest)
def render_pages(tasks, dest, opts, notification=lambda x, y: x):
'''
Entry point for the job server.
'''
failures, pages = [], []
from calibre.utils.PythonMagickWand import ImageMagick
with ImageMagick():
for num, path in tasks:
try:
pages.extend(PageProcessor(path, dest, opts, num))
msg = _('Rendered %s')%path
except:
failures.append(path)
msg = _('Failed %s')%path
if opts.verbose:
msg += '\n' + traceback.format_exc()
prints(msg)
notification(0.5, msg)
for num, path in tasks:
try:
pages.extend(PageProcessor(path, dest, opts, num))
msg = _('Rendered %s')%path
except:
failures.append(path)
msg = _('Failed %s')%path
if opts.verbose:
msg += '\n' + traceback.format_exc()
prints(msg)
notification(0.5, msg)
return pages, failures
@ -226,9 +203,6 @@ def process_pages(pages, opts, update, tdir):
'''
Render all identified comic pages.
'''
from calibre.utils.PythonMagickWand import ImageMagick
ImageMagick
progress = Progress(len(pages), update)
server = Server()
jobs = []

View File

@ -120,25 +120,19 @@ class RTFInput(InputFormatPlugin):
return self.convert_images(imap)
def convert_images(self, imap):
from calibre.utils.PythonMagickWand import ImageMagick
with ImageMagick():
for count, val in imap.items():
try:
imap[count] = self.convert_image(val)
except:
self.log.exception('Failed to convert', val)
for count, val in imap.items():
try:
imap[count] = self.convert_image(val)
except:
self.log.exception('Failed to convert', val)
return imap
def convert_image(self, name):
import calibre.utils.PythonMagickWand as p
img = p.NewMagickWand()
if img < 0:
raise RuntimeError('Cannot create wand.')
if not p.MagickReadImage(img, name):
self.log.warn('Failed to read image:', name)
from calibre.utils.magick import Image
img = Image()
img.open(name)
name = name.replace('.wmf', '.jpg')
p.MagickWriteImage(img, name)
img.save(name)
return name
@ -199,11 +193,11 @@ class RTFInput(InputFormatPlugin):
except RtfInvalidCodeException, e:
raise ValueError(_('This RTF file has a feature calibre does not '
'support. Convert it to HTML first and then try it.\n%s')%e)
'''dataxml = open('dataxml.xml', 'w')
dataxml.write(xml)
dataxml.close'''
d = glob.glob(os.path.join('*_rtf_pict_dir', 'picts.rtf'))
if d:
imap = {}
@ -211,7 +205,7 @@ class RTFInput(InputFormatPlugin):
imap = self.extract_images(d[0])
except:
self.log.exception('Failed to extract images...')
self.log('Parsing XML...')
parser = etree.XMLParser(recover=True, no_network=True)
doc = etree.fromstring(xml, parser=parser)

View File

@ -10,7 +10,7 @@ from copy import deepcopy
from xml.sax.saxutils import escape
from calibre import filesystem_encoding, prints, prepare_string_for_xml, strftime
from calibre import prints, prepare_string_for_xml, strftime
from calibre.constants import preferred_encoding
from calibre.customize import CatalogPlugin
from calibre.customize.conversion import OptionRecommendation, DummyReporter
@ -1202,9 +1202,7 @@ class EPUB_MOBI(CatalogPlugin):
self.generateHTMLByDateRead()
self.generateHTMLByTags()
from calibre.utils.PythonMagickWand import ImageMagick
with ImageMagick():
self.generateThumbnails()
self.generateThumbnails()
self.generateOPF()
self.generateNCXHeader()
@ -4057,29 +4055,15 @@ class EPUB_MOBI(CatalogPlugin):
return ' '.join(translated)
def generateThumbnail(self, title, image_dir, thumb_file):
import calibre.utils.PythonMagickWand as pw
from calibre.utils.magick import Image
try:
img = pw.NewMagickWand()
if img < 0:
raise RuntimeError('generateThumbnail(): Cannot create wand')
# Read the cover
if not pw.MagickReadImage(img,
title['cover'].encode(filesystem_encoding)):
self.opts.log.error('generateThumbnail(): Failed to read cover image from: %s' % title['cover'])
raise IOError
thumb = pw.CloneMagickWand(img)
if thumb < 0:
self.opts.log.error('generateThumbnail(): Cannot clone cover')
raise RuntimeError
img = Image()
img.open(title['cover'])
# img, width, height
pw.MagickThumbnailImage(thumb, self.thumbWidth, self.thumbHeight)
pw.MagickWriteImage(thumb, os.path.join(image_dir, thumb_file))
pw.DestroyMagickWand(thumb)
pw.DestroyMagickWand(img)
except IOError:
self.opts.log.error("generateThumbnail(): IOError with %s" % title['title'])
except RuntimeError:
self.opts.log.error("generateThumbnail(): RuntimeError with %s" % title['title'])
img.thumbnail(self.thumbWidth, self.thumbHeight)
img.save(os.path.join(image_dir, thumb_file))
except:
self.opts.log.error("generateThumbnail(): Error with %s" % title['title'])
def getMarkerTags(self):
''' Return a list of special marker tags to be excluded from genre list '''

File diff suppressed because it is too large Load Diff

View File

@ -17,6 +17,9 @@ if _magick is None:
_gravity_map = dict([(getattr(_magick, x), x) for x in dir(_magick) if
x.endswith('Gravity')])
_type_map = dict([(getattr(_magick, x), x) for x in dir(_magick) if
x.endswith('Type')])
# Font metrics {{{
class Rect(object):
@ -57,6 +60,11 @@ class FontMetrics(object):
# }}}
class PixelWand(_magick.PixelWand): # {{{
pass
# }}}
class DrawingWand(_magick.DrawingWand): # {{{
@dynamic_property
@ -91,6 +99,12 @@ class DrawingWand(_magick.DrawingWand): # {{{
class Image(_magick.Image): # {{{
@property
def clone(self):
ans = Image()
ans.copy(self)
return ans
def load(self, data):
return _magick.Image.load(self, bytes(data))
@ -110,6 +124,15 @@ class Image(_magick.Image): # {{{
self.format_ = str(val)
return property(fget=fget, fset=fset, doc=_magick.Image.format_.__doc__)
@dynamic_property
def type(self):
def fget(self):
return _type_map[self.type_]
def fset(self, val):
val = getattr(_magick, str(val))
self.type_ = val
return property(fget=fget, fset=fset, doc=_magick.Image.type_.__doc__)
@dynamic_property
def size(self):
@ -160,6 +183,14 @@ class Image(_magick.Image): # {{{
arguments = [float(x) for x in arguments]
_magick.Image.distort(self, method, arguments, bestfit)
def rotate(self, background_pixel_wand, degrees):
_magick.Image.rotate(self, background_pixel_wand, float(degrees))
def quantize(self, number_colors, colorspace='RGBColorspace', treedepth=0, dither=True,
measure_error=False):
colorspace = getattr(_magick, colorspace)
_magick.Image.quantize(self, number_colors, colorspace, treedepth, dither,
measure_error)
# }}}

View File

@ -49,7 +49,8 @@ def get_value(const):
def main():
constants = []
for x in ('resample', 'image', 'draw', 'distort', 'composite', 'geometry'):
for x in ('resample', 'image', 'draw', 'distort', 'composite', 'geometry',
'colorspace'):
constants += list(parse_enums('magick/%s.h'%x))
base = os.path.dirname(__file__)
constants = [

View File

@ -16,6 +16,131 @@ PyObject* magick_set_exception(MagickWand *wand) {
}
// }}}
// PixelWand object definition {{{
typedef struct {
PyObject_HEAD
// Type-specific fields go here.
PixelWand *wand;
} magick_PixelWand;
static void
magick_PixelWand_dealloc(magick_PixelWand* self)
{
if (self->wand != NULL) self->wand = DestroyPixelWand(self->wand);
self->ob_type->tp_free((PyObject*)self);
}
static PyObject *
magick_PixelWand_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
magick_PixelWand *self;
self = (magick_PixelWand *)type->tp_alloc(type, 0);
if (self != NULL) {
self->wand = NewPixelWand();
if (self->wand == NULL || self->wand < 0) {
PyErr_SetString(PyExc_Exception, "Failed to allocate wand.");
self->wand = NULL;
Py_DECREF(self);
return NULL;
}
}
return (PyObject *)self;
}
// PixelWand.color {{{
static PyObject *
magick_PixelWand_color_getter(magick_PixelWand *self, void *closure) {
const char *fp;
fp = PixelGetColorAsNormalizedString(self->wand);
return Py_BuildValue("s", fp);
}
static int
magick_PixelWand_color_setter(magick_PixelWand *self, PyObject *val, void *closure) {
char *fmt;
if (val == NULL) {
PyErr_SetString(PyExc_TypeError, "Cannot delete PixelWand color");
return -1;
}
fmt = PyString_AsString(val);
if (fmt == NULL) return -1;
if (!PixelSetColor(self->wand, fmt)) {
PyErr_SetString(PyExc_ValueError, "Unknown color");
return -1;
}
return 0;
}
// }}}
// PixelWand attr list {{{
static PyMethodDef magick_PixelWand_methods[] = {
{NULL} /* Sentinel */
};
static PyGetSetDef magick_PixelWand_getsetters[] = {
{(char *)"color",
(getter)magick_PixelWand_color_getter, (setter)magick_PixelWand_color_setter,
(char *)"PixelWand color. ImageMagick color specification.",
NULL},
{NULL} /* Sentinel */
};
// }}}
static PyTypeObject magick_PixelWandType = { // {{{
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
"magick.PixelWand", /*tp_name*/
sizeof(magick_PixelWand), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)magick_PixelWand_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*/
"PixelWand", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
magick_PixelWand_methods, /* tp_methods */
0, /* tp_members */
magick_PixelWand_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_PixelWand_new, /* tp_new */
}; // }}}
// }}}
// DrawingWand object definition {{{
typedef struct {
PyObject_HEAD
@ -153,7 +278,6 @@ magick_DrawingWand_gravity_setter(magick_DrawingWand *self, PyObject *val, void
// }}}
// DrawingWand attr list {{{
static PyMethodDef magick_DrawingWand_methods[] = {
{NULL} /* Sentinel */
@ -240,6 +364,7 @@ typedef struct {
// Method declarations {{{
static PyObject* magick_Image_compose(magick_Image *self, PyObject *args, PyObject *kwargs);
static PyObject* magick_Image_copy(magick_Image *self, PyObject *args, PyObject *kwargs);
// }}}
static void
@ -268,6 +393,7 @@ magick_Image_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return (PyObject *)self;
}
// Image.load {{{
static PyObject *
magick_Image_load(magick_Image *self, PyObject *args, PyObject *kwargs) {
@ -515,6 +641,176 @@ magick_Image_trim(magick_Image *self, PyObject *args, PyObject *kwargs) {
}
// }}}
// Image.thumbnail {{{
static PyObject *
magick_Image_thumbnail(magick_Image *self, PyObject *args, PyObject *kwargs) {
Py_ssize_t width, height;
if (!PyArg_ParseTuple(args, "nn", &width, &height)) return NULL;
if (!MagickThumbnailImage(self->wand, width, height)) return magick_set_exception(self->wand);
Py_RETURN_NONE;
}
// }}}
// Image.crop {{{
static PyObject *
magick_Image_crop(magick_Image *self, PyObject *args, PyObject *kwargs) {
Py_ssize_t width, height, x, y;
if (!PyArg_ParseTuple(args, "dddd", &width, &height, &x, &y)) return NULL;
if (!MagickCropImage(self->wand, width, height, x, y)) return magick_set_exception(self->wand);
Py_RETURN_NONE;
}
// }}}
// Image.set_border_color {{{
static PyObject *
magick_Image_set_border_color(magick_Image *self, PyObject *args, PyObject *kwargs) {
PyObject *obj;
magick_PixelWand *pw;
if (!PyArg_ParseTuple(args, "O!", &magick_PixelWandType, &obj)) return NULL;
pw = (magick_PixelWand*)obj;
if (!IsPixelWand(pw->wand)) { PyErr_SetString(PyExc_TypeError, "Invalid PixelWand"); return NULL; }
if (!MagickSetImageBorderColor(self->wand, pw->wand)) return magick_set_exception(self->wand);
Py_RETURN_NONE;
}
// }}}
// Image.rotate {{{
static PyObject *
magick_Image_rotate(magick_Image *self, PyObject *args, PyObject *kwargs) {
PyObject *obj;
magick_PixelWand *pw;
double degrees;
if (!PyArg_ParseTuple(args, "O!d", &magick_PixelWandType, &obj, &degrees)) return NULL;
pw = (magick_PixelWand*)obj;
if (!IsPixelWand(pw->wand)) { PyErr_SetString(PyExc_TypeError, "Invalid PixelWand"); return NULL; }
if (!MagickRotateImage(self->wand, pw->wand, degrees)) return magick_set_exception(self->wand);
Py_RETURN_NONE;
}
// }}}
// Image.set_page {{{
static PyObject *
magick_Image_set_page(magick_Image *self, PyObject *args, PyObject *kwargs) {
Py_ssize_t width, height, x, y;
if (!PyArg_ParseTuple(args, "dddd", &width, &height, &x, &y)) return NULL;
if (!MagickSetImagePage(self->wand, width, height, x, y)) return magick_set_exception(self->wand);
Py_RETURN_NONE;
}
// }}}
// Image.normalize {{{
static PyObject *
magick_Image_normalize(magick_Image *self, PyObject *args, PyObject *kwargs) {
if (!MagickNormalizeImage(self->wand)) return magick_set_exception(self->wand);
Py_RETURN_NONE;
}
// }}}
// Image.add_border {{{
static PyObject *
magick_Image_add_border(magick_Image *self, PyObject *args, PyObject *kwargs) {
Py_ssize_t dx, dy;
PyObject *obj;
magick_PixelWand *pw;
if (!PyArg_ParseTuple(args, "O!dd", &magick_PixelWandType, &obj, &dx, &dy)) return NULL;
pw = (magick_PixelWand*)obj;
if (!IsPixelWand(pw->wand)) { PyErr_SetString(PyExc_TypeError, "Invalid PixelWand"); return NULL; }
if (!MagickBorderImage(self->wand, pw->wand, dx, dy)) return magick_set_exception(self->wand);
Py_RETURN_NONE;
}
// }}}
// Image.sharpen {{{
static PyObject *
magick_Image_sharpen(magick_Image *self, PyObject *args, PyObject *kwargs) {
double radius, sigma;
if (!PyArg_ParseTuple(args, "dd", &radius, &sigma)) return NULL;
if (!MagickSharpenImage(self->wand, radius, sigma)) return magick_set_exception(self->wand);
Py_RETURN_NONE;
}
// }}}
// Image.quantize {{{
static PyObject *
magick_Image_quantize(magick_Image *self, PyObject *args, PyObject *kwargs) {
Py_ssize_t number_colors, treedepth;
ColorspaceType colorspace;
PyObject *dither, *measure_error;
if (!PyArg_ParseTuple(args, "nnnOO", &number_colors, &colorspace, &treedepth, &dither, &measure_error)) return NULL;
if (!MagickQuantizeImage(self->wand, number_colors, colorspace, treedepth, PyObject_IsTrue(dither), PyObject_IsTrue(measure_error))) return magick_set_exception(self->wand);
Py_RETURN_NONE;
}
// }}}
// Image.despeckle {{{
static PyObject *
magick_Image_despeckle(magick_Image *self, PyObject *args, PyObject *kwargs) {
if (!MagickDespeckleImage(self->wand)) return magick_set_exception(self->wand);
Py_RETURN_NONE;
}
// }}}
// Image.type {{{
static PyObject *
magick_Image_type_getter(magick_Image *self, void *closure) {
return Py_BuildValue("n", MagickGetImageType(self->wand));
}
static int
magick_Image_type_setter(magick_Image *self, PyObject *val, void *closure) {
ImageType type;
if (val == NULL) {
PyErr_SetString(PyExc_TypeError, "Cannot delete image type");
return -1;
}
type = (ImageType)PyInt_AsSsize_t(val);
if (!MagickSetImageType(self->wand, type)) {
PyErr_SetString(PyExc_ValueError, "Unknown image type");
return -1;
}
return 0;
}
// }}}
// Image attr list {{{
static PyMethodDef magick_Image_methods[] = {
@ -536,6 +832,10 @@ static PyMethodDef magick_Image_methods[] = {
"compose(img, left, top, op) \n\n Compose img using operation op at (left, top)"
},
{"copy", (PyCFunction)magick_Image_copy, METH_VARARGS,
"copy(img) \n\n Copy img to self."
},
{"font_metrics", (PyCFunction)magick_Image_font_metrics, METH_VARARGS,
"font_metrics(drawing_wand, text) \n\n Return font metrics for specified drawing wand and text."
},
@ -552,6 +852,46 @@ static PyMethodDef magick_Image_methods[] = {
"trim(fuzz) \n\n Trim image."
},
{"crop", (PyCFunction)magick_Image_crop, METH_VARARGS,
"crop(width, height, x, y) \n\n Crop image."
},
{"set_page", (PyCFunction)magick_Image_set_page, METH_VARARGS,
"set_page(width, height, x, y) \n\n Sets the page geometry of the image."
},
{"thumbnail", (PyCFunction)magick_Image_thumbnail, METH_VARARGS,
"thumbnail(width, height) \n\n Convert to a thumbnail of specified size."
},
{"set_border_color", (PyCFunction)magick_Image_set_border_color, METH_VARARGS,
"set_border_color(pixel_wand) \n\n Set border color to the specified PixelWand."
},
{"rotate", (PyCFunction)magick_Image_rotate, METH_VARARGS,
"rotate(background_pixel_wand, degrees) \n\n Rotate image by specified degrees."
},
{"normalize", (PyCFunction)magick_Image_normalize, METH_VARARGS,
"normalize() \n\n enhances the contrast of a color image by adjusting the pixels color to span the entire range of colors available."
},
{"add_border", (PyCFunction)magick_Image_add_border, METH_VARARGS,
"add_border(pixel_wand, width, height) \n\n surrounds the image with a border of the color defined by the bordercolor pixel wand."
},
{"sharpen", (PyCFunction)magick_Image_sharpen, METH_VARARGS,
"sharpen(radius, sigma) \n\n sharpens an image. We convolve the image with a Gaussian operator of the given radius and standard deviation (sigma). For reasonable results, the radius should be larger than sigma. Use a radius of 0 and MagickSharpenImage() selects a suitable radius for you."
},
{"despeckle", (PyCFunction)magick_Image_despeckle, METH_VARARGS,
"despeckle() \n\n reduces the speckle noise in an image while perserving the edges of the original image."
},
{"quantize", (PyCFunction)magick_Image_quantize, METH_VARARGS,
"quantize(number_colors, colorspace, treedepth, dither, measure_error) \n\n nalyzes the colors within a reference image and chooses a fixed number of colors to represent the image. The goal of the algorithm is to minimize the color difference between the input and output image while minimizing the processing time."
},
{NULL} /* Sentinel */
};
@ -566,6 +906,11 @@ static PyGetSetDef magick_Image_getsetters[] = {
(char *)"Image format",
NULL},
{(char *)"type_",
(getter)magick_Image_type_getter, (setter)magick_Image_type_setter,
(char *)"the image type: UndefinedType, BilevelType, GrayscaleType, GrayscaleMatteType, PaletteType, PaletteMatteType, TrueColorType, TrueColorMatteType, ColorSeparationType, ColorSeparationMatteType, or OptimizeType.",
NULL},
{NULL} /* Sentinel */
};
@ -641,6 +986,23 @@ magick_Image_compose(magick_Image *self, PyObject *args, PyObject *kwargs)
}
// }}}
// Image.clone {{{
static PyObject *
magick_Image_copy(magick_Image *self, PyObject *args, PyObject *kwargs)
{
PyObject *img;
magick_Image *src;
if (!PyArg_ParseTuple(args, "O!", &magick_ImageType, &img)) return NULL;
src = (magick_Image*)img;
if (!IsMagickWand(src->wand)) {PyErr_SetString(PyExc_TypeError, "Not a valid ImageMagick wand"); return NULL;}
self->wand = DestroyMagickWand(self->wand);
self->wand = CloneMagickWand(src->wand);
if (self->wand == NULL) { return PyErr_NoMemory(); }
Py_RETURN_NONE;
}
// }}}
// }}}
@ -689,6 +1051,8 @@ initmagick(void)
return;
if (PyType_Ready(&magick_DrawingWandType) < 0)
return;
if (PyType_Ready(&magick_PixelWandType) < 0)
return;
m = Py_InitModule3("magick", magick_methods,
"Wrapper for the ImageMagick imaging library");
@ -697,6 +1061,8 @@ initmagick(void)
PyModule_AddObject(m, "Image", (PyObject *)&magick_ImageType);
Py_INCREF(&magick_DrawingWandType);
PyModule_AddObject(m, "DrawingWand", (PyObject *)&magick_DrawingWandType);
Py_INCREF(&magick_PixelWandType);
PyModule_AddObject(m, "PixelWand", (PyObject *)&magick_PixelWandType);
magick_add_module_constants(m);
MagickWandGenesis();

View File

@ -263,4 +263,27 @@ static void magick_add_module_constants(PyObject *m) {
PyModule_AddIntConstant(m, "SouthGravity", 8);
PyModule_AddIntConstant(m, "SouthEastGravity", 9);
PyModule_AddIntConstant(m, "StaticGravity", 10);
PyModule_AddIntConstant(m, "UndefinedColorspace", 0);
PyModule_AddIntConstant(m, "RGBColorspace", 1);
PyModule_AddIntConstant(m, "GRAYColorspace", 2);
PyModule_AddIntConstant(m, "TransparentColorspace", 3);
PyModule_AddIntConstant(m, "OHTAColorspace", 4);
PyModule_AddIntConstant(m, "LabColorspace", 5);
PyModule_AddIntConstant(m, "XYZColorspace", 6);
PyModule_AddIntConstant(m, "YCbCrColorspace", 7);
PyModule_AddIntConstant(m, "YCCColorspace", 8);
PyModule_AddIntConstant(m, "YIQColorspace", 9);
PyModule_AddIntConstant(m, "YPbPrColorspace", 10);
PyModule_AddIntConstant(m, "YUVColorspace", 11);
PyModule_AddIntConstant(m, "CMYKColorspace", 12);
PyModule_AddIntConstant(m, "sRGBColorspace", 13);
PyModule_AddIntConstant(m, "HSBColorspace", 14);
PyModule_AddIntConstant(m, "HSLColorspace", 15);
PyModule_AddIntConstant(m, "HWBColorspace", 16);
PyModule_AddIntConstant(m, "Rec601LumaColorspace", 17);
PyModule_AddIntConstant(m, "Rec601YCbCrColorspace", 18);
PyModule_AddIntConstant(m, "Rec709LumaColorspace", 19);
PyModule_AddIntConstant(m, "Rec709YCbCrColorspace", 20);
PyModule_AddIntConstant(m, "LogColorspace", 21);
PyModule_AddIntConstant(m, "CMYColorspace", 22);
}