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

View File

@ -120,25 +120,19 @@ class RTFInput(InputFormatPlugin):
return self.convert_images(imap) return self.convert_images(imap)
def convert_images(self, imap): def convert_images(self, imap):
from calibre.utils.PythonMagickWand import ImageMagick for count, val in imap.items():
with ImageMagick(): try:
for count, val in imap.items(): imap[count] = self.convert_image(val)
try: except:
imap[count] = self.convert_image(val) self.log.exception('Failed to convert', val)
except:
self.log.exception('Failed to convert', val)
return imap return imap
def convert_image(self, name): def convert_image(self, name):
import calibre.utils.PythonMagickWand as p from calibre.utils.magick import Image
img = p.NewMagickWand() img = Image()
if img < 0: img.open(name)
raise RuntimeError('Cannot create wand.')
if not p.MagickReadImage(img, name):
self.log.warn('Failed to read image:', name)
name = name.replace('.wmf', '.jpg') name = name.replace('.wmf', '.jpg')
p.MagickWriteImage(img, name) img.save(name)
return name return name

View File

@ -10,7 +10,7 @@ from copy import deepcopy
from xml.sax.saxutils import escape 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.constants import preferred_encoding
from calibre.customize import CatalogPlugin from calibre.customize import CatalogPlugin
from calibre.customize.conversion import OptionRecommendation, DummyReporter from calibre.customize.conversion import OptionRecommendation, DummyReporter
@ -1202,9 +1202,7 @@ class EPUB_MOBI(CatalogPlugin):
self.generateHTMLByDateRead() self.generateHTMLByDateRead()
self.generateHTMLByTags() self.generateHTMLByTags()
from calibre.utils.PythonMagickWand import ImageMagick self.generateThumbnails()
with ImageMagick():
self.generateThumbnails()
self.generateOPF() self.generateOPF()
self.generateNCXHeader() self.generateNCXHeader()
@ -4057,29 +4055,15 @@ class EPUB_MOBI(CatalogPlugin):
return ' '.join(translated) return ' '.join(translated)
def generateThumbnail(self, title, image_dir, thumb_file): def generateThumbnail(self, title, image_dir, thumb_file):
import calibre.utils.PythonMagickWand as pw from calibre.utils.magick import Image
try: try:
img = pw.NewMagickWand() img = Image()
if img < 0: img.open(title['cover'])
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, width, height # img, width, height
pw.MagickThumbnailImage(thumb, self.thumbWidth, self.thumbHeight) img.thumbnail(self.thumbWidth, self.thumbHeight)
pw.MagickWriteImage(thumb, os.path.join(image_dir, thumb_file)) img.save(os.path.join(image_dir, thumb_file))
pw.DestroyMagickWand(thumb) except:
pw.DestroyMagickWand(img) self.opts.log.error("generateThumbnail(): Error with %s" % title['title'])
except IOError:
self.opts.log.error("generateThumbnail(): IOError with %s" % title['title'])
except RuntimeError:
self.opts.log.error("generateThumbnail(): RuntimeError with %s" % title['title'])
def getMarkerTags(self): def getMarkerTags(self):
''' Return a list of special marker tags to be excluded from genre list ''' ''' 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 _gravity_map = dict([(getattr(_magick, x), x) for x in dir(_magick) if
x.endswith('Gravity')]) x.endswith('Gravity')])
_type_map = dict([(getattr(_magick, x), x) for x in dir(_magick) if
x.endswith('Type')])
# Font metrics {{{ # Font metrics {{{
class Rect(object): class Rect(object):
@ -57,6 +60,11 @@ class FontMetrics(object):
# }}} # }}}
class PixelWand(_magick.PixelWand): # {{{
pass
# }}}
class DrawingWand(_magick.DrawingWand): # {{{ class DrawingWand(_magick.DrawingWand): # {{{
@dynamic_property @dynamic_property
@ -91,6 +99,12 @@ class DrawingWand(_magick.DrawingWand): # {{{
class Image(_magick.Image): # {{{ class Image(_magick.Image): # {{{
@property
def clone(self):
ans = Image()
ans.copy(self)
return ans
def load(self, data): def load(self, data):
return _magick.Image.load(self, bytes(data)) return _magick.Image.load(self, bytes(data))
@ -110,6 +124,15 @@ class Image(_magick.Image): # {{{
self.format_ = str(val) self.format_ = str(val)
return property(fget=fget, fset=fset, doc=_magick.Image.format_.__doc__) 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 @dynamic_property
def size(self): def size(self):
@ -160,6 +183,14 @@ class Image(_magick.Image): # {{{
arguments = [float(x) for x in arguments] arguments = [float(x) for x in arguments]
_magick.Image.distort(self, method, arguments, bestfit) _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(): def main():
constants = [] 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)) constants += list(parse_enums('magick/%s.h'%x))
base = os.path.dirname(__file__) base = os.path.dirname(__file__)
constants = [ 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 {{{ // DrawingWand object definition {{{
typedef struct { typedef struct {
PyObject_HEAD PyObject_HEAD
@ -153,7 +278,6 @@ magick_DrawingWand_gravity_setter(magick_DrawingWand *self, PyObject *val, void
// }}} // }}}
// DrawingWand attr list {{{ // DrawingWand attr list {{{
static PyMethodDef magick_DrawingWand_methods[] = { static PyMethodDef magick_DrawingWand_methods[] = {
{NULL} /* Sentinel */ {NULL} /* Sentinel */
@ -240,6 +364,7 @@ typedef struct {
// Method declarations {{{ // Method declarations {{{
static PyObject* magick_Image_compose(magick_Image *self, PyObject *args, PyObject *kwargs); 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 static void
@ -268,6 +393,7 @@ magick_Image_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return (PyObject *)self; return (PyObject *)self;
} }
// Image.load {{{ // Image.load {{{
static PyObject * static PyObject *
magick_Image_load(magick_Image *self, PyObject *args, PyObject *kwargs) { 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 {{{ // Image attr list {{{
static PyMethodDef magick_Image_methods[] = { 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)" "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", (PyCFunction)magick_Image_font_metrics, METH_VARARGS,
"font_metrics(drawing_wand, text) \n\n Return font metrics for specified drawing wand and text." "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." "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 */ {NULL} /* Sentinel */
}; };
@ -566,6 +906,11 @@ static PyGetSetDef magick_Image_getsetters[] = {
(char *)"Image format", (char *)"Image format",
NULL}, 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 */ {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; return;
if (PyType_Ready(&magick_DrawingWandType) < 0) if (PyType_Ready(&magick_DrawingWandType) < 0)
return; return;
if (PyType_Ready(&magick_PixelWandType) < 0)
return;
m = Py_InitModule3("magick", magick_methods, m = Py_InitModule3("magick", magick_methods,
"Wrapper for the ImageMagick imaging library"); "Wrapper for the ImageMagick imaging library");
@ -697,6 +1061,8 @@ initmagick(void)
PyModule_AddObject(m, "Image", (PyObject *)&magick_ImageType); PyModule_AddObject(m, "Image", (PyObject *)&magick_ImageType);
Py_INCREF(&magick_DrawingWandType); Py_INCREF(&magick_DrawingWandType);
PyModule_AddObject(m, "DrawingWand", (PyObject *)&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); magick_add_module_constants(m);
MagickWandGenesis(); MagickWandGenesis();

View File

@ -263,4 +263,27 @@ static void magick_add_module_constants(PyObject *m) {
PyModule_AddIntConstant(m, "SouthGravity", 8); PyModule_AddIntConstant(m, "SouthGravity", 8);
PyModule_AddIntConstant(m, "SouthEastGravity", 9); PyModule_AddIntConstant(m, "SouthEastGravity", 9);
PyModule_AddIntConstant(m, "StaticGravity", 10); 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);
} }