mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 18:24:30 -04:00
Remove PythonMagickWand
This commit is contained in:
parent
0b1f4588ac
commit
dbb7233d19
@ -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 = []
|
||||
|
@ -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)
|
||||
|
@ -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
@ -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)
|
||||
|
||||
# }}}
|
||||
|
||||
|
@ -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 = [
|
||||
|
@ -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, °rees)) 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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user