Remove ImageMagick

This commit is contained in:
Kovid Goyal 2016-05-10 15:40:33 +05:30
parent 1cf86cb89e
commit 11f239c6e5
10 changed files with 351 additions and 2607 deletions

View File

@ -863,3 +863,82 @@ QImage oil_paint(const QImage &image, const float radius, const bool high_qualit
return(buffer); return(buffer);
} // }}} } // }}}
bool has_transparent_pixels(const QImage &image) { // {{{
QImage img(image);
QImage::Format fmt = img.format();
if (!img.hasAlphaChannel()) return false;
if (fmt != QImage::Format_ARGB32 && fmt != QImage::Format_ARGB32_Premultiplied) {
img = img.convertToFormat(QImage::Format_ARGB32);
if (img.isNull()) throw std::bad_alloc();
}
int w = image.width(), h = image.height();
for (int r = 0; r < h; r++) {
const QRgb *line = reinterpret_cast<const QRgb*>(img.constScanLine(r));
for (int c = 0; c < w; c++) {
if (qAlpha(*(line + c)) != 0xff) return true;
}
}
return false;
} // }}}
QImage set_opacity(const QImage &image, double alpha) { // {{{
QImage img(image);
QImage::Format fmt = img.format();
if (fmt != QImage::Format_ARGB32) {
img = img.convertToFormat(QImage::Format_ARGB32);
if (img.isNull()) throw std::bad_alloc();
}
int w = image.width(), h = image.height();
for (int r = 0; r < h; r++) {
QRgb *line = reinterpret_cast<QRgb*>(img.scanLine(r));
for (int c = 0; c < w; c++) {
QRgb pixel = *(line + c);
*(line + c) = qRgba(qRed(pixel), qGreen(pixel), qBlue(pixel), qAlpha(pixel) * alpha);
}
}
return img;
} // }}}
QImage texture_image(const QImage &image, const QImage &texturei) { // {{{
QImage canvas(image);
QImage texture(texturei);
if (texture.isNull()) throw std::out_of_range("Cannot use null texture image");
if (canvas.isNull()) throw std::out_of_range("Cannot use null canvas image");
ENSURE32(canvas); ENSURE32(texture);
int x = 0, y = 0, cw = canvas.width(), ch = canvas.height(), tw = texture.width(), th = texture.height();
const bool overwrite = !texturei.hasAlphaChannel();
if (!overwrite) {
if (texture.format() != QImage::Format_ARGB32_Premultiplied) {
texture = texture.convertToFormat(QImage::Format_ARGB32_Premultiplied);
if (texture.isNull()) throw std::bad_alloc();
}
}
while (y < ch) {
int ylimit = MIN(th, ch - y);
x = 0;
while (x < cw) {
for (int r = 0; r < ylimit; r++) {
const QRgb *src = reinterpret_cast<const QRgb*>(texture.constScanLine(r));
QRgb *dest = reinterpret_cast<QRgb*>(canvas.scanLine(r + y)) + x;
int xlimit = MIN(tw, cw - x);
if (overwrite) {
memcpy(dest, src, xlimit * sizeof(QRgb));
} else {
for (int c = 0; c < xlimit; c++) {
// See the overlay function() for an explanation of the alpha blending code below
QRgb s = src[c];
if (s >= 0xff000000) dest[c] = s;
else if (s != 0) dest[c] = s + BYTE_MUL(dest[c], qAlpha(~s));
}
}
}
x += tw;
}
y += th;
}
return canvas;
} // }}}

View File

@ -19,6 +19,9 @@ void overlay(const QImage &image, QImage &canvas, unsigned int left, unsigned in
QImage normalize(const QImage &image); QImage normalize(const QImage &image);
QImage oil_paint(const QImage &image, const float radius=-1, const bool high_quality=true); QImage oil_paint(const QImage &image, const float radius=-1, const bool high_quality=true);
QImage quantize(const QImage &image, unsigned int maximum_colors, bool dither, const QVector<QRgb> &palette); QImage quantize(const QImage &image, unsigned int maximum_colors, bool dither, const QVector<QRgb> &palette);
bool has_transparent_pixels(const QImage &image);
QImage set_opacity(const QImage &image, double alpha);
QImage texture_image(const QImage &image, const QImage &texturei);
class ScopedGILRelease { class ScopedGILRelease {
public: public:

View File

@ -78,3 +78,24 @@ QImage quantize(const QImage &image, unsigned int maximum_colors, bool dither, c
sipRes = new QImage(quantize(*a0, a1, a2, *a3)); sipRes = new QImage(quantize(*a0, a1, a2, *a3));
IMAGEOPS_SUFFIX IMAGEOPS_SUFFIX
%End %End
bool has_transparent_pixels(const QImage &image);
%MethodCode
IMAGEOPS_PREFIX
sipRes = has_transparent_pixels(*a0);
IMAGEOPS_SUFFIX
%End
QImage set_opacity(const QImage &image, double alpha);
%MethodCode
IMAGEOPS_PREFIX
sipRes = new QImage(set_opacity(*a0, a1));
IMAGEOPS_SUFFIX
%End
QImage texture_image(const QImage &image, const QImage &texturei);
%MethodCode
IMAGEOPS_PREFIX
sipRes = new QImage(texture_image(*a0, *a1));
IMAGEOPS_SUFFIX
%End

View File

@ -27,18 +27,8 @@ def get_exe_path(name):
return name return name
return os.path.join(base, name) return os.path.join(base, name)
_qimage_pixel_map = None def null_image():
return QImage()
def get_pixel_map():
' Get the order of pixels in QImage (RGBA or BGRA usually) '
global _qimage_pixel_map
if _qimage_pixel_map is None:
i = QImage(1, 1, QImage.Format_ARGB32)
i.fill(QColor(0, 1, 2, 3))
raw = bytearray(i.constBits().asstring(4))
_qimage_pixel_map = {c:raw.index(x) for c, x in zip('RGBA', b'\x00\x01\x02\x03')}
_qimage_pixel_map = ''.join(sorted(_qimage_pixel_map, key=_qimage_pixel_map.get))
return _qimage_pixel_map
def image_from_data(data): def image_from_data(data):
if isinstance(data, QImage): if isinstance(data, QImage):
@ -48,6 +38,21 @@ def image_from_data(data):
raise NotImage('Not a valid image') raise NotImage('Not a valid image')
return i return i
def image_from_path(path):
with lopen(path, 'rb') as f:
return image_from_data(f.read())
def image_from_x(x):
if isinstance(x, type('')):
return image_from_path(x)
if hasattr(x, 'read'):
return image_from_data(x.read())
if isinstance(x, (bytes, QImage)):
return image_from_data(x)
if isinstance(x, bytearray):
return image_from_data(bytes(x))
raise TypeError('Unknown image src type: %s' % type(x))
def image_and_format_from_data(data): def image_and_format_from_data(data):
ba = QByteArray(data) ba = QByteArray(data)
buf = QBuffer(ba) buf = QBuffer(ba)
@ -62,10 +67,10 @@ def add_borders_to_image(img, left=0, top=0, right=0, bottom=0, border_color='#f
return img return img
canvas = QImage(img.width() + left + right, img.height() + top + bottom, QImage.Format_RGB32) canvas = QImage(img.width() + left + right, img.height() + top + bottom, QImage.Format_RGB32)
canvas.fill(QColor(border_color)) canvas.fill(QColor(border_color))
overlay(img, canvas, left, top) overlay_image(img, canvas, left, top)
return canvas return canvas
def overlay(img, canvas=None, left=0, top=0): def overlay_image(img, canvas=None, left=0, top=0):
if canvas is None: if canvas is None:
canvas = QImage(img.size(), QImage.Format_RGB32) canvas = QImage(img.size(), QImage.Format_RGB32)
canvas.fill(Qt.white) canvas.fill(Qt.white)
@ -85,7 +90,7 @@ def overlay(img, canvas=None, left=0, top=0):
def blend_image(img, bgcolor='#ffffff'): def blend_image(img, bgcolor='#ffffff'):
canvas = QImage(img.size(), QImage.Format_RGB32) canvas = QImage(img.size(), QImage.Format_RGB32)
canvas.fill(QColor(bgcolor)) canvas.fill(QColor(bgcolor))
overlay(img, canvas) overlay_image(img, canvas)
return canvas return canvas
def image_to_data(img, compression_quality=95, fmt='JPEG', png_compression_level=9, jpeg_optimized=True, jpeg_progressive=False): def image_to_data(img, compression_quality=95, fmt='JPEG', png_compression_level=9, jpeg_optimized=True, jpeg_progressive=False):
@ -119,6 +124,12 @@ def image_to_data(img, compression_quality=95, fmt='JPEG', png_compression_level
raise ValueError('Failed to export image as ' + fmt + ' with error: ' + w.errorString()) raise ValueError('Failed to export image as ' + fmt + ' with error: ' + w.errorString())
return ba.data() return ba.data()
def save_image(img, path, **kw):
fmt = path.rpartition('.')[-1]
kw['fmt'] = kw.get('fmt', fmt)
with lopen(path, 'wb') as f:
f.write(image_to_data(image_from_data(img), **kw))
def resize_image(img, width, height): def resize_image(img, width, height):
return img.scaled(int(width), int(height), Qt.IgnoreAspectRatio, Qt.SmoothTransformation) return img.scaled(int(width), int(height), Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
@ -174,10 +185,18 @@ def normalize_format_name(fmt):
def grayscale_image(img): def grayscale_image(img):
if imageops is not None: if imageops is not None:
return imageops.grayscale(img) return imageops.grayscale(image_from_data(img))
return img return img
def save_cover_data_to(data, path=None, bgcolor='#ffffff', resize_to=None, compression_quality=90, minify_to=None, grayscale=False): def set_image_opacity(img, alpha=0.5):
''' Change the opacity of `img`. Note that the alpha value is multiplied to
any existing alpha values, so you cannot use this function to convert a
semi-transparent image to an opaque one. For that use `blend_image()`. '''
if imageops is None:
raise RuntimeError(imageops_err)
return imageops.set_opacity(image_from_data(img), alpha)
def save_cover_data_to(data, path=None, bgcolor='#ffffff', resize_to=None, compression_quality=90, minify_to=None, grayscale=False, data_fmt='jpeg'):
''' '''
Saves image in data to path, in the format specified by the path Saves image in data to path, in the format specified by the path
extension. Removes any transparency. If there is no transparency and no extension. Removes any transparency. If there is no transparency and no
@ -186,6 +205,7 @@ def save_cover_data_to(data, path=None, bgcolor='#ffffff', resize_to=None, compr
:param data: Image data as bytestring :param data: Image data as bytestring
:param path: If None img data is returned, in JPEG format :param path: If None img data is returned, in JPEG format
:param data_fmt: The fmt to return data in when path is None. Defaults to JPEG
:param compression_quality: The quality of the image after compression. :param compression_quality: The quality of the image after compression.
Number between 1 and 100. 1 means highest compression, 100 means no Number between 1 and 100. 1 means highest compression, 100 means no
compression (lossless). compression (lossless).
@ -197,7 +217,7 @@ def save_cover_data_to(data, path=None, bgcolor='#ffffff', resize_to=None, compr
''' '''
img, fmt = image_and_format_from_data(data) img, fmt = image_and_format_from_data(data)
orig_fmt = normalize_format_name(fmt) orig_fmt = normalize_format_name(fmt)
fmt = normalize_format_name('jpeg' if path is None else os.path.splitext(path)[1][1:]) fmt = normalize_format_name(data_fmt if path is None else os.path.splitext(path)[1][1:])
changed = fmt != orig_fmt changed = fmt != orig_fmt
if resize_to is not None: if resize_to is not None:
changed = True changed = True
@ -228,7 +248,7 @@ def blend_on_canvas(img, width, height, bgcolor='#ffffff'):
w, h = nw, nh w, h = nw, nh
canvas = QImage(width, height, QImage.Format_RGB32) canvas = QImage(width, height, QImage.Format_RGB32)
canvas.fill(QColor(bgcolor)) canvas.fill(QColor(bgcolor))
overlay(img, canvas, (width - w)//2, (height - h)//2) overlay_image(img, canvas, (width - w)//2, (height - h)//2)
return canvas return canvas
class Canvas(object): class Canvas(object):
@ -245,14 +265,28 @@ class Canvas(object):
def compose(self, img, x=0, y=0): def compose(self, img, x=0, y=0):
img = image_from_data(img) img = image_from_data(img)
overlay(img, self.img, x, y) overlay_image(img, self.img, x, y)
def export(self, fmt='JPEG', compression_quality=95): def export(self, fmt='JPEG', compression_quality=95):
return image_to_data(self.img, compression_quality=compression_quality, fmt=fmt) return image_to_data(self.img, compression_quality=compression_quality, fmt=fmt)
def create_canvas(width, height, bgcolor='#ffffff'):
img = QImage(width, height, QImage.Format_RGB32)
img.fill(QColor(bgcolor))
return img
def flip_image(img, horizontal=False, vertical=False): def flip_image(img, horizontal=False, vertical=False):
return image_from_data(img).mirrored(horizontal, vertical) return image_from_data(img).mirrored(horizontal, vertical)
def image_has_transparent_pixels(img):
' Return True iff the image has at least one semi-transparent pixel '
img = image_from_data(img)
if img.isNull():
return False
if imageops is None:
raise RuntimeError(imageops_err)
return imageops.has_transparent_pixels(img)
def rotate_image(img, degrees): def rotate_image(img, degrees):
t = QTransform() t = QTransform()
t.rotate(degrees) t.rotate(degrees)
@ -314,6 +348,14 @@ def quantize_image(img, max_colors=256, dither=True, palette=''):
palette = palette.split() palette = palette.split()
return imageops.quantize(img, max_colors, dither, [QColor(x).rgb() for x in palette]) return imageops.quantize(img, max_colors, dither, [QColor(x).rgb() for x in palette])
def texture_image(canvas, texture):
' Repeatedly tile the image `texture` across and down the image `canvas` '
if imageops is None:
raise RuntimeError(imageops_err)
if canvas.hasAlphaChannel():
canvas = blend_image(canvas)
return imageops.texture_image(canvas, texture)
# Optimization of images {{{ # Optimization of images {{{
def run_optimizer(file_path, cmd, as_filter=False, input_data=None): def run_optimizer(file_path, cmd, as_filter=False, input_data=None):

View File

@ -5,284 +5,10 @@ __license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>' __copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import os from calibre.utils.magick.legacy import Image, PixelWand
from calibre.constants import plugins, filesystem_encoding if False:
from calibre.utils.img import get_pixel_map, image_and_format_from_data PixelWand
_magick, _merr = plugins['magick']
if _magick is None:
raise RuntimeError('Failed to load ImageMagick: '+_merr)
_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')])
_colorspace_map = dict([(getattr(_magick, x), x) for x in dir(_magick) if
x.endswith('Colorspace')])
def qimage_to_magick(img):
ans = Image()
ans.from_qimage(img)
return ans
# Font metrics {{{
class Rect(object):
def __init__(self, left, top, right, bottom):
self.left, self.top, self.right, self.bottom = left, top, right, bottom
def __str__(self):
return '(%s, %s) -- (%s, %s)'%(self.left, self.top, self.right,
self.bottom)
class FontMetrics(object):
def __init__(self, ret):
self._attrs = []
for i, x in enumerate(('char_width', 'char_height', 'ascender',
'descender', 'text_width', 'text_height',
'max_horizontal_advance')):
setattr(self, x, ret[i])
self._attrs.append(x)
self.bounding_box = Rect(ret[7], ret[8], ret[9], ret[10])
self.x, self.y = ret[11], ret[12]
self._attrs.extend(['bounding_box', 'x', 'y'])
self._attrs = tuple(self._attrs)
def __str__(self):
return '''FontMetrics:
char_width: %s
char_height: %s
ascender: %s
descender: %s
text_width: %s
text_height: %s
max_horizontal_advance: %s
bounding_box: %s
x: %s
y: %s
'''%tuple([getattr(self, x) for x in self._attrs])
# }}}
class PixelWand(_magick.PixelWand): # {{{
pass
# }}}
class DrawingWand(_magick.DrawingWand): # {{{
@dynamic_property
def font(self):
def fget(self):
return self.font_.decode(filesystem_encoding, 'replace').lower()
def fset(self, val):
if isinstance(val, unicode):
val = val.encode(filesystem_encoding)
self.font_ = str(val)
return property(fget=fget, fset=fset, doc=_magick.DrawingWand.font_.__doc__)
@dynamic_property
def gravity(self):
def fget(self):
val = self.gravity_
return _gravity_map[val]
def fset(self, val):
val = getattr(_magick, str(val))
self.gravity_ = val
return property(fget=fget, fset=fset, doc=_magick.DrawingWand.gravity_.__doc__)
@dynamic_property
def font_size(self):
def fget(self):
return self.font_size_
def fset(self, val):
self.font_size_ = float(val)
return property(fget=fget, fset=fset, doc=_magick.DrawingWand.font_size_.__doc__)
@dynamic_property
def stroke_color(self):
def fget(self):
return self.stroke_color_.color
def fset(self, val):
col = PixelWand()
col.color = unicode(val)
self.stroke_color_ = col
return property(fget=fget, fset=fset, doc=_magick.DrawingWand.font_size_.__doc__)
@dynamic_property
def fill_color(self):
def fget(self):
return self.fill_color_.color
def fset(self, val):
col = PixelWand()
col.color = unicode(val)
self.fill_color_ = col
return property(fget=fget, fset=fset, doc=_magick.DrawingWand.font_size_.__doc__)
# }}}
class Image(_magick.Image): # {{{
read_format = None
@property
def clone(self):
ans = Image()
ans.copy(self)
return ans
def from_qimage(self, img):
from PyQt5.Qt import QImage
fmt = get_pixel_map()
if not img.hasAlphaChannel():
if img.format() != img.Format_RGB32:
img = img.convertToFormat(QImage.Format_RGB32)
fmt = fmt.replace('A', 'P')
else:
if img.format() != img.Format_ARGB32:
img = img.convertToFormat(QImage.Format_ARGB32)
raw = img.constBits().ascapsule()
self.constitute(img.width(), img.height(), fmt, raw)
def to_qimage(self):
from PyQt5.Qt import QImage, QByteArray
fmt = get_pixel_map()
# ImageMagick can only output raw data in some formats that can be
# read into QImage directly, if the QImage format is not one of those, use
# PNG
if fmt in {'RGBA', 'BGRA'}:
w, h = self.size
self.depth = 8 # QImage expects 8bpp
raw = self.export(fmt)
i = QImage(raw, w, h, QImage.Format_ARGB32)
del raw # According to the documentation, raw is supposed to not be deleted, but it works, so make it explicit
return i
else:
raw = self.export('PNG')
return QImage.fromData(QByteArray(raw), 'PNG')
def load(self, data):
if not data:
raise ValueError('Cannot open image from empty data string')
img, self.read_format = image_and_format_from_data(data)
self.from_qimage(img)
def open(self, path_or_file):
data = path_or_file
if hasattr(data, 'read'):
data = data.read()
else:
with lopen(data, 'rb') as f:
data = f.read()
if not data:
raise ValueError('%r is an empty file'%path_or_file)
self.load(data)
@dynamic_property
def format(self):
def fget(self):
if self.format_ is None:
return self.read_format
return self.format_.decode('utf-8', 'ignore').lower()
def fset(self, val):
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 colorspace(self):
def fget(self):
return _colorspace_map[self.colorspace_]
def fset(self, val):
val = getattr(_magick, str(val))
self.colorspace_ = val
return property(fget=fget, fset=fset, doc=_magick.Image.type_.__doc__)
@dynamic_property
def size(self):
def fget(self):
return 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])
self.size_ = (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:]
format = format.upper()
with lopen(path, 'wb') as f:
f.write(self.export(format))
def compose(self, img, left=0, top=0, operation='OverCompositeOp'):
op = getattr(_magick, operation)
bounds = self.size
if left < 0 or top < 0 or left >= bounds[0] or top >= bounds[1]:
raise ValueError('left and/or top out of bounds')
_magick.Image.compose(self, img, int(left), int(top), op)
def compare(self, img, metric='RootMeanSquaredErrorMetric'):
return _magick.Image.compare(self, img, getattr(_magick, metric))
def font_metrics(self, drawing_wand, text):
if isinstance(text, unicode):
text = text.encode('UTF-8')
return FontMetrics(_magick.Image.font_metrics(self, drawing_wand, text))
def annotate(self, drawing_wand, x, y, angle, text):
if isinstance(text, unicode):
text = text.encode('UTF-8')
return _magick.Image.annotate(self, drawing_wand, x, y, angle, text)
def distort(self, method, arguments, bestfit):
method = getattr(_magick, method)
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)
def identify(self, data):
img, fmt = image_and_format_from_data(data)
return img.width(), img.height(), fmt
def trim(self, fuzz):
try:
_magick.Image.remove_border(self, fuzz)
except AttributeError:
_magick.Image.trim(self, fuzz)
# }}}
def create_canvas(width, height, bgcolor='#ffffff'): def create_canvas(width, height, bgcolor='#ffffff'):
canvas = Image() canvas = Image()

View File

@ -7,10 +7,10 @@ __docformat__ = 'restructuredtext en'
import os import os
from calibre.utils.magick import Image, DrawingWand, create_canvas from calibre.utils.magick import Image, create_canvas
from calibre.constants import __appname__, __version__ from calibre.utils.img import save_cover_data_to as _save_cover_data_to, image_to_data, add_borders_to_image as abti
from calibre.utils.config import tweaks from calibre.utils.imghdr import identify as _identify
from calibre import fit_image, force_unicode from calibre import fit_image
def _data_to_image(data): def _data_to_image(data):
if isinstance(data, Image): if isinstance(data, Image):
@ -40,12 +40,6 @@ def minify_image(data, minify_to=(1200, 1600), preserve_aspect_ratio=True):
img.size = (nwidth, nheight) img.size = (nwidth, nheight)
return img return img
def normalize_format_name(fmt):
fmt = fmt.lower()
if fmt == 'jpeg':
fmt = 'jpg'
return fmt
def save_cover_data_to(data, path, bgcolor='#ffffff', resize_to=None, def save_cover_data_to(data, path, bgcolor='#ffffff', resize_to=None,
return_data=False, compression_quality=90, minify_to=None, return_data=False, compression_quality=90, minify_to=None,
grayscale=False): grayscale=False):
@ -67,50 +61,13 @@ def save_cover_data_to(data, path, bgcolor='#ffffff', resize_to=None,
tweak is used. tweak is used.
''' '''
changed = False
img = _data_to_image(data)
orig_fmt = normalize_format_name(img.format)
fmt = os.path.splitext(path)[1] fmt = os.path.splitext(path)[1]
fmt = normalize_format_name(fmt[1:])
if grayscale:
img.type = "GrayscaleType"
changed = True
if resize_to is not None:
img.size = (resize_to[0], resize_to[1])
changed = True
owidth, oheight = img.size
nwidth, nheight = tweaks['maximum_cover_size'] if minify_to is None else minify_to
scaled, nwidth, nheight = fit_image(owidth, oheight, nwidth, nheight)
if scaled:
img.size = (nwidth, nheight)
changed = True
if img.has_transparent_pixels():
canvas = create_canvas(img.size[0], img.size[1], bgcolor)
canvas.compose(img)
img = canvas
changed = True
if not changed:
changed = fmt != orig_fmt
ret = None
if return_data: if return_data:
ret = data path = None
if changed or isinstance(ret, Image): if isinstance(data, Image):
if hasattr(img, 'set_compression_quality') and fmt == 'jpg': data = Image.img
img.set_compression_quality(compression_quality) return _save_cover_data_to(
ret = img.export(fmt) data, path, bgcolor=bgcolor, resize_to=resize_to, compression_quality=compression_quality, minify_to=minify_to, grayscale=grayscale, data_fmt=fmt)
else:
if changed or isinstance(ret, Image):
if hasattr(img, 'set_compression_quality') and fmt == 'jpg':
img.set_compression_quality(compression_quality)
img.save(path)
else:
with lopen(path, 'wb') as f:
f.write(data)
return ret
def thumbnail(data, width=120, height=120, bgcolor='#ffffff', fmt='jpg', def thumbnail(data, width=120, height=120, bgcolor='#ffffff', fmt='jpg',
preserve_aspect_ratio=True, compression_quality=70): preserve_aspect_ratio=True, compression_quality=70):
@ -131,9 +88,8 @@ def thumbnail(data, width=120, height=120, bgcolor='#ffffff', fmt='jpg',
img.size = (nwidth, nheight) img.size = (nwidth, nheight)
canvas = create_canvas(img.size[0], img.size[1], bgcolor) canvas = create_canvas(img.size[0], img.size[1], bgcolor)
canvas.compose(img) canvas.compose(img)
if fmt == 'jpg': data = image_to_data(canvas.img, compression_quality=compression_quality)
canvas.set_compression_quality(compression_quality) return (canvas.size[0], canvas.size[1], data)
return (canvas.size[0], canvas.size[1], canvas.export(fmt))
def identify_data(data): def identify_data(data):
''' '''
@ -141,8 +97,8 @@ def identify_data(data):
(width, height, format) (width, height, format)
or raises an Exception if data is not an image. or raises an Exception if data is not an image.
''' '''
img = Image() fmt, width, height = _identify(data)
return img.identify(data) return width, height, fmt
def identify(path): def identify(path):
''' '''
@ -151,149 +107,10 @@ def identify(path):
or raises an Exception. or raises an Exception.
''' '''
with lopen(path, 'rb') as f: with lopen(path, 'rb') as f:
data = f.read() fmt, width, height = _identify(f)
return identify_data(data) return width, height, fmt
def add_borders_to_image(img_data, left=0, top=0, right=0, bottom=0, def add_borders_to_image(img_data, left=0, top=0, right=0, bottom=0,
border_color='#ffffff', fmt='jpg'): border_color='#ffffff', fmt='jpg'):
img = Image() img = abti(img_data, left=left, top=top, right=right, bottom=bottom, border_color=border_color)
img.load(img_data) return image_to_data(img, fmt=fmt)
lwidth, lheight = img.size
canvas = create_canvas(lwidth+left+right, lheight+top+bottom,
border_color)
canvas.compose(img, left, top)
return canvas.export(fmt)
def create_text_wand(font_size, font_path=None):
ans = DrawingWand()
if font_path is not None:
ans.font = font_path
ans.font_size = font_size
ans.gravity = 'CenterGravity'
ans.text_alias = True
return ans
def create_text_arc(text, font_size, font=None, bgcolor='#ffffff'):
if isinstance(text, unicode):
text = text.encode('utf-8')
canvas = create_canvas(300, 300, bgcolor)
tw = create_text_wand(font_size, font_path=font)
m = canvas.font_metrics(tw, text)
canvas = create_canvas(int(m.text_width)+20, int(m.text_height*3.5), bgcolor)
canvas.annotate(tw, 0, 0, 0, text)
canvas.distort("ArcDistortion", [120], True)
canvas.trim(0)
return canvas
def _get_line(img, dw, tokens, line_width):
line, rest = tokens, []
while True:
m = img.font_metrics(dw, ' '.join(line))
width = m.text_width
if width < line_width:
return line, rest
rest = line[-1:] + rest
line = line[:-1]
def annotate_img(img, dw, left, top, rotate, text,
translate_from_top_left=True):
if isinstance(text, unicode):
text = text.encode('utf-8')
if translate_from_top_left:
m = img.font_metrics(dw, text)
img_width, img_height = img.size
left = left - img_width/2. + m.text_width/2.
top = top - img_height/2. + m.text_height/2.
img.annotate(dw, left, top, rotate, text)
def draw_centered_line(img, dw, line, top):
m = img.font_metrics(dw, line)
width, height = m.text_width, m.text_height
img_width = img.size[0]
left = max(int((img_width - width)/2.), 0)
annotate_img(img, dw, left, top, 0, line)
return top + height
def draw_centered_text(img, dw, text, top, margin=10):
img_width = img.size[0]
tokens = text.split(' ')
while tokens:
line, tokens = _get_line(img, dw, tokens, img_width-2*margin)
if not line:
# Could not fit the first token on the line
line = tokens[:1]
tokens = tokens[1:]
bottom = draw_centered_line(img, dw, ' '.join(line), top)
top = bottom
return top
class TextLine(object):
def __init__(self, text, font_size, bottom_margin=30, font_path=None):
self.text, self.font_size, = text, font_size
self.bottom_margin = bottom_margin
if font_path is None:
if not isinstance(text, unicode):
text = force_unicode(text)
from calibre.utils.fonts.utils import get_font_for_text
fd = get_font_for_text(text)
if fd is not None:
from calibre.ptempfile import PersistentTemporaryFile
pt = PersistentTemporaryFile('.ttf')
pt.write(fd)
pt.close()
font_path = pt.name
self.font_path = font_path
def __repr__(self):
return u'TextLine:%r:%f'%(self.text, self.font_size)
def create_cover_page(top_lines, logo_path, width=590, height=750,
bgcolor='#ffffff', output_format='jpg', texture_data=None,
texture_opacity=1.0):
'''
Create the standard calibre cover page and return it as a byte string in
the specified output_format.
'''
canvas = create_canvas(width, height, bgcolor)
if texture_data and hasattr(canvas, 'texture'):
texture = Image()
texture.load(texture_data)
texture.set_opacity(texture_opacity)
canvas.texture(texture)
bottom = 10
for line in top_lines:
twand = create_text_wand(line.font_size, font_path=line.font_path)
bottom = draw_centered_text(canvas, twand, line.text, bottom)
bottom += line.bottom_margin
bottom -= top_lines[-1].bottom_margin
foot_font = P('fonts/liberation/LiberationMono-Regular.ttf')
vanity = create_text_arc(__appname__ + ' ' + __version__, 24,
font=foot_font, bgcolor='#00000000')
lwidth, lheight = vanity.size
left = int(max(0, (width - lwidth)/2.))
top = height - lheight - 10
canvas.compose(vanity, left, top)
available = (width, int(top - bottom)-20)
if available[1] > 40:
logo = Image()
logo.open(logo_path)
lwidth, lheight = logo.size
scaled, lwidth, lheight = fit_image(lwidth, lheight, *available)
if scaled:
logo.size = (lwidth, lheight)
left = int(max(0, (width - lwidth)/2.))
top = bottom+10
extra = int((available[1] - lheight)/2.0)
if extra > 0:
top += extra
canvas.compose(logo, left, top)
return canvas.export(output_format)

View File

@ -1,73 +0,0 @@
#!/usr/bin/env python2
# 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-6'
'''
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 = '''
#define MAGICKCORE_QUANTUM_DEPTH 16
#define MAGICKCORE_HDRI_ENABLE 0
#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'+INC, '/tmp/ig.c', '-o', '/tmp/ig', '-lMagickWand-6.Q16'])
return int(subprocess.Popen(["/tmp/ig"],
stdout=subprocess.PIPE).communicate()[0].strip())
def main():
constants = []
for x in ('resample', 'image', 'draw', 'distort', 'composite', 'geometry',
'colorspace', 'compare', 'compress'):
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,166 @@
#!/usr/bin/env python2
# vim:fileencoding=utf-8
# License: GPLv3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
from __future__ import (unicode_literals, division, absolute_import,
print_function)
import os
from io import BytesIO
from calibre.utils.img import (
image_and_format_from_data, clone_image, null_image, resize_image,
overlay_image, rotate_image, quantize_image, remove_borders_from_image,
add_borders_to_image, gaussian_blur_image, create_canvas, despeckle_image,
image_to_data, flip_image, image_has_transparent_pixels, set_image_opacity,
gaussian_sharpen_image, texture_image
)
from calibre.utils.imghdr import identify
class PixelWand(object):
def __init__(self):
self.color = '#ffffff'
class Image(object):
def __init__(self):
self.read_format = None
self.write_format = None
self.img = null_image()
@property
def clone(self):
ans = Image()
ans.img = clone_image(self.img)
ans.read_format = self.read_format
ans.write_format = self.write_format
return ans
def open(self, path_or_file):
if hasattr(path_or_file, 'read'):
self.load(path_or_file.read())
else:
with lopen(path_or_file, 'rb') as f:
self.load(f.read())
def load(self, data):
if not data:
raise ValueError('No image data present')
self.img, self.read_format = image_and_format_from_data(data)
read = load
def from_qimage(self, img):
self.img = clone_image(img)
def to_qimage(self):
return clone_image(self.img)
@dynamic_property
def format(self):
def fget(self):
return self.write_format or self.read_format
def fset(self, val):
self.write_format = val
return property(fget=fget, fset=fset)
@dynamic_property
def colorspace(self):
def fget(self):
return 'RGBColorspace'
def fset(self, val):
raise NotImplementedError('Changing image colorspace is not supported')
return property(fget=fget, fset=fset)
@dynamic_property
def size(self):
def fget(self):
return self.img.width(), self.img.height()
def fset(self, val):
w, h = val[:2]
self.img = resize_image(self.img, w, h)
return property(fget=fget, fset=fset)
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:]
format = format.upper()
with lopen(path, 'wb') as f:
f.write(self.export(format))
def compose(self, img, left=0, top=0, operation='OverCompositeOp'):
bounds = self.size
if left < 0 or top < 0 or left >= bounds[0] or top >= bounds[1]:
raise ValueError('left and/or top out of bounds')
self.img = overlay_image(img.img, self.img, left=left, top=top)
def rotate(self, background_pixel_wand, degrees):
self.img = rotate_image(self.img, degrees)
def quantize(self, number_colors, colorspace='RGBColorspace', treedepth=0, dither=True, measure_error=False):
self.img = quantize_image(self.img, max_colors=number_colors, dither=dither)
def identify(self, data):
fmt, width, height = identify(data)
return width, height, fmt
def remove_border(self, fuzz=None):
if fuzz is not None and fuzz < 0 or fuzz > 255:
fuzz = None
self.img = remove_borders_from_image(self.img, fuzz)
trim = remove_border
def add_border(self, pixel_wand, dx, dy):
self.img = add_borders_to_image(self.img, left=dx, top=dy, right=dx, bottom=dy, border_color=pixel_wand.color)
def blur(self, radius=-1, sigma=3.0):
self.img = gaussian_blur_image(self.img, radius, sigma)
def copy(self, img):
self.img = clone_image(img.img)
def create_canvas(self, width, height, background_pixel_wand):
self.img = create_canvas(width, height, background_pixel_wand)
def despeckle(self):
self.img = despeckle_image(self.img)
def export(self, fmt='JPEG'):
if fmt.lower() == 'gif':
data = image_to_data(self.img, fmt='PNG', png_compression_level=0)
from PIL import Image
i = Image.open(data)
buf = BytesIO()
i.save(buf, 'gif')
return buf.getvalue()
return image_to_data(self.img, fmt=fmt)
def flip(self, vertical=True):
self.img = flip_image(self.img, horizontal=not vertical, vertical=vertical)
def has_transparent_pixels(self):
return image_has_transparent_pixels(self.img)
def set_border_color(self, *args, **kw):
pass # no-op
def set_compression_quality(self, *args, **kw):
pass # no-op
def set_opacity(self, alpha=0.5):
self.img = set_image_opacity(self.img, alpha)
def set_page(self, *args, **kw):
pass # no-op
def sharpen(self, radius=0, sigma=3):
self.img = gaussian_sharpen_image(self.img, radius, sigma)
def texture(self, img):
self.img = texture_image(self.img, img.img)
def thumbnail(self, width, height):
self.img = resize_image(self.img, width, height)

File diff suppressed because it is too large Load Diff

View File

@ -1,344 +0,0 @@
// 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, "JincFilter", 13);
PyModule_AddIntConstant(m, "SincFilter", 14);
PyModule_AddIntConstant(m, "SincFastFilter", 15);
PyModule_AddIntConstant(m, "KaiserFilter", 16);
PyModule_AddIntConstant(m, "WelshFilter", 17);
PyModule_AddIntConstant(m, "ParzenFilter", 18);
PyModule_AddIntConstant(m, "BohmanFilter", 19);
PyModule_AddIntConstant(m, "BartlettFilter", 20);
PyModule_AddIntConstant(m, "LagrangeFilter", 21);
PyModule_AddIntConstant(m, "LanczosFilter", 22);
PyModule_AddIntConstant(m, "LanczosSharpFilter", 23);
PyModule_AddIntConstant(m, "Lanczos2Filter", 24);
PyModule_AddIntConstant(m, "Lanczos2SharpFilter", 25);
PyModule_AddIntConstant(m, "RobidouxFilter", 26);
PyModule_AddIntConstant(m, "RobidouxSharpFilter", 27);
PyModule_AddIntConstant(m, "CosineFilter", 28);
PyModule_AddIntConstant(m, "SplineFilter", 29);
PyModule_AddIntConstant(m, "LanczosRadiusFilter", 30);
PyModule_AddIntConstant(m, "SentinelFilter", 31);
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, "FlattenAlphaChannel", 11);
PyModule_AddIntConstant(m, "RemoveAlphaChannel", 12);
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, "Cylinder2PlaneDistortion", 12);
PyModule_AddIntConstant(m, "Plane2CylinderDistortion", 13);
PyModule_AddIntConstant(m, "BarrelDistortion", 14);
PyModule_AddIntConstant(m, "BarrelInverseDistortion", 15);
PyModule_AddIntConstant(m, "ShepardsDistortion", 16);
PyModule_AddIntConstant(m, "ResizeDistortion", 17);
PyModule_AddIntConstant(m, "SentinelDistortion", 18);
PyModule_AddIntConstant(m, "UndefinedColorInterpolate", 0);
PyModule_AddIntConstant(m, "BarycentricColorInterpolate", 1);
PyModule_AddIntConstant(m, "BilinearColorInterpolate", 7);
PyModule_AddIntConstant(m, "PolynomialColorInterpolate", 8);
PyModule_AddIntConstant(m, "ShepardsColorInterpolate", 16);
PyModule_AddIntConstant(m, "VoronoiColorInterpolate", 18);
PyModule_AddIntConstant(m, "InverseColorInterpolate", 19);
PyModule_AddIntConstant(m, "UndefinedCompositeOp", 0);
PyModule_AddIntConstant(m, "NoCompositeOp", 1);
PyModule_AddIntConstant(m, "ModulusAddCompositeOp", 2);
PyModule_AddIntConstant(m, "AtopCompositeOp", 3);
PyModule_AddIntConstant(m, "BlendCompositeOp", 4);
PyModule_AddIntConstant(m, "BumpmapCompositeOp", 5);
PyModule_AddIntConstant(m, "ChangeMaskCompositeOp", 6);
PyModule_AddIntConstant(m, "ClearCompositeOp", 7);
PyModule_AddIntConstant(m, "ColorBurnCompositeOp", 8);
PyModule_AddIntConstant(m, "ColorDodgeCompositeOp", 9);
PyModule_AddIntConstant(m, "ColorizeCompositeOp", 10);
PyModule_AddIntConstant(m, "CopyBlackCompositeOp", 11);
PyModule_AddIntConstant(m, "CopyBlueCompositeOp", 12);
PyModule_AddIntConstant(m, "CopyCompositeOp", 13);
PyModule_AddIntConstant(m, "CopyCyanCompositeOp", 14);
PyModule_AddIntConstant(m, "CopyGreenCompositeOp", 15);
PyModule_AddIntConstant(m, "CopyMagentaCompositeOp", 16);
PyModule_AddIntConstant(m, "CopyOpacityCompositeOp", 17);
PyModule_AddIntConstant(m, "CopyRedCompositeOp", 18);
PyModule_AddIntConstant(m, "CopyYellowCompositeOp", 19);
PyModule_AddIntConstant(m, "DarkenCompositeOp", 20);
PyModule_AddIntConstant(m, "DstAtopCompositeOp", 21);
PyModule_AddIntConstant(m, "DstCompositeOp", 22);
PyModule_AddIntConstant(m, "DstInCompositeOp", 23);
PyModule_AddIntConstant(m, "DstOutCompositeOp", 24);
PyModule_AddIntConstant(m, "DstOverCompositeOp", 25);
PyModule_AddIntConstant(m, "DifferenceCompositeOp", 26);
PyModule_AddIntConstant(m, "DisplaceCompositeOp", 27);
PyModule_AddIntConstant(m, "DissolveCompositeOp", 28);
PyModule_AddIntConstant(m, "ExclusionCompositeOp", 29);
PyModule_AddIntConstant(m, "HardLightCompositeOp", 30);
PyModule_AddIntConstant(m, "HueCompositeOp", 31);
PyModule_AddIntConstant(m, "InCompositeOp", 32);
PyModule_AddIntConstant(m, "LightenCompositeOp", 33);
PyModule_AddIntConstant(m, "LinearLightCompositeOp", 34);
PyModule_AddIntConstant(m, "LuminizeCompositeOp", 35);
PyModule_AddIntConstant(m, "MinusDstCompositeOp", 36);
PyModule_AddIntConstant(m, "ModulateCompositeOp", 37);
PyModule_AddIntConstant(m, "MultiplyCompositeOp", 38);
PyModule_AddIntConstant(m, "OutCompositeOp", 39);
PyModule_AddIntConstant(m, "OverCompositeOp", 40);
PyModule_AddIntConstant(m, "OverlayCompositeOp", 41);
PyModule_AddIntConstant(m, "PlusCompositeOp", 42);
PyModule_AddIntConstant(m, "ReplaceCompositeOp", 43);
PyModule_AddIntConstant(m, "SaturateCompositeOp", 44);
PyModule_AddIntConstant(m, "ScreenCompositeOp", 45);
PyModule_AddIntConstant(m, "SoftLightCompositeOp", 46);
PyModule_AddIntConstant(m, "SrcAtopCompositeOp", 47);
PyModule_AddIntConstant(m, "SrcCompositeOp", 48);
PyModule_AddIntConstant(m, "SrcInCompositeOp", 49);
PyModule_AddIntConstant(m, "SrcOutCompositeOp", 50);
PyModule_AddIntConstant(m, "SrcOverCompositeOp", 51);
PyModule_AddIntConstant(m, "ModulusSubtractCompositeOp", 52);
PyModule_AddIntConstant(m, "ThresholdCompositeOp", 53);
PyModule_AddIntConstant(m, "XorCompositeOp", 54);
PyModule_AddIntConstant(m, "DivideDstCompositeOp", 55);
PyModule_AddIntConstant(m, "DistortCompositeOp", 56);
PyModule_AddIntConstant(m, "BlurCompositeOp", 57);
PyModule_AddIntConstant(m, "PegtopLightCompositeOp", 58);
PyModule_AddIntConstant(m, "VividLightCompositeOp", 59);
PyModule_AddIntConstant(m, "PinLightCompositeOp", 60);
PyModule_AddIntConstant(m, "LinearDodgeCompositeOp", 61);
PyModule_AddIntConstant(m, "LinearBurnCompositeOp", 62);
PyModule_AddIntConstant(m, "MathematicsCompositeOp", 63);
PyModule_AddIntConstant(m, "DivideSrcCompositeOp", 64);
PyModule_AddIntConstant(m, "MinusSrcCompositeOp", 65);
PyModule_AddIntConstant(m, "DarkenIntensityCompositeOp", 66);
PyModule_AddIntConstant(m, "LightenIntensityCompositeOp", 67);
PyModule_AddIntConstant(m, "NoValue", 0);
PyModule_AddIntConstant(m, "XValue", 1);
PyModule_AddIntConstant(m, "XiValue", 1);
PyModule_AddIntConstant(m, "YValue", 2);
PyModule_AddIntConstant(m, "PsiValue", 2);
PyModule_AddIntConstant(m, "WidthValue", 4);
PyModule_AddIntConstant(m, "RhoValue", 4);
PyModule_AddIntConstant(m, "HeightValue", 8);
PyModule_AddIntConstant(m, "SigmaValue", 8);
PyModule_AddIntConstant(m, "ChiValue", 16);
PyModule_AddIntConstant(m, "XiNegative", 32);
PyModule_AddIntConstant(m, "XNegative", 32);
PyModule_AddIntConstant(m, "PsiNegative", 64);
PyModule_AddIntConstant(m, "YNegative", 64);
PyModule_AddIntConstant(m, "ChiNegative", 128);
PyModule_AddIntConstant(m, "PercentValue", 4096);
PyModule_AddIntConstant(m, "AspectValue", 8192);
PyModule_AddIntConstant(m, "NormalizeValue", 8192);
PyModule_AddIntConstant(m, "LessValue", 16384);
PyModule_AddIntConstant(m, "GreaterValue", 32768);
PyModule_AddIntConstant(m, "MinimumValue", 65536);
PyModule_AddIntConstant(m, "CorrelateNormalizeValue", 65536);
PyModule_AddIntConstant(m, "AreaValue", 131072);
PyModule_AddIntConstant(m, "DecimalValue", 262144);
PyModule_AddIntConstant(m, "SeparatorValue", 524288);
PyModule_AddIntConstant(m, "AllValues", 2147483647);
PyModule_AddIntConstant(m, "UndefinedGravity", 0);
PyModule_AddIntConstant(m, "ForgetGravity", 0);
PyModule_AddIntConstant(m, "NorthWestGravity", 1);
PyModule_AddIntConstant(m, "NorthGravity", 2);
PyModule_AddIntConstant(m, "NorthEastGravity", 3);
PyModule_AddIntConstant(m, "WestGravity", 4);
PyModule_AddIntConstant(m, "CenterGravity", 5);
PyModule_AddIntConstant(m, "EastGravity", 6);
PyModule_AddIntConstant(m, "SouthWestGravity", 7);
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);
PyModule_AddIntConstant(m, "LuvColorspace", 23);
PyModule_AddIntConstant(m, "HCLColorspace", 24);
PyModule_AddIntConstant(m, "LCHColorspace", 25);
PyModule_AddIntConstant(m, "LMSColorspace", 26);
PyModule_AddIntConstant(m, "LCHabColorspace", 27);
PyModule_AddIntConstant(m, "LCHuvColorspace", 28);
PyModule_AddIntConstant(m, "scRGBColorspace", 29);
PyModule_AddIntConstant(m, "HSIColorspace", 30);
PyModule_AddIntConstant(m, "HSVColorspace", 31);
PyModule_AddIntConstant(m, "HCLpColorspace", 32);
PyModule_AddIntConstant(m, "YDbDrColorspace", 33);
PyModule_AddIntConstant(m, "UndefinedMetric", 0);
PyModule_AddIntConstant(m, "AbsoluteErrorMetric", 1);
PyModule_AddIntConstant(m, "MeanAbsoluteErrorMetric", 2);
PyModule_AddIntConstant(m, "MeanErrorPerPixelMetric", 3);
PyModule_AddIntConstant(m, "MeanSquaredErrorMetric", 4);
PyModule_AddIntConstant(m, "PeakAbsoluteErrorMetric", 5);
PyModule_AddIntConstant(m, "PeakSignalToNoiseRatioMetric", 6);
PyModule_AddIntConstant(m, "RootMeanSquaredErrorMetric", 7);
PyModule_AddIntConstant(m, "NormalizedCrossCorrelationErrorMetric", 8);
PyModule_AddIntConstant(m, "FuzzErrorMetric", 9);
PyModule_AddIntConstant(m, "UndefinedErrorMetric", 0);
PyModule_AddIntConstant(m, "UndefinedCompression", 0);
PyModule_AddIntConstant(m, "NoCompression", 1);
PyModule_AddIntConstant(m, "BZipCompression", 2);
PyModule_AddIntConstant(m, "DXT1Compression", 3);
PyModule_AddIntConstant(m, "DXT3Compression", 4);
PyModule_AddIntConstant(m, "DXT5Compression", 5);
PyModule_AddIntConstant(m, "FaxCompression", 6);
PyModule_AddIntConstant(m, "Group4Compression", 7);
PyModule_AddIntConstant(m, "JPEGCompression", 8);
PyModule_AddIntConstant(m, "JPEG2000Compression", 9);
PyModule_AddIntConstant(m, "LosslessJPEGCompression", 10);
PyModule_AddIntConstant(m, "LZWCompression", 11);
PyModule_AddIntConstant(m, "RLECompression", 12);
PyModule_AddIntConstant(m, "ZipCompression", 13);
PyModule_AddIntConstant(m, "ZipSCompression", 14);
PyModule_AddIntConstant(m, "PizCompression", 15);
PyModule_AddIntConstant(m, "Pxr24Compression", 16);
PyModule_AddIntConstant(m, "B44Compression", 17);
PyModule_AddIntConstant(m, "B44ACompression", 18);
PyModule_AddIntConstant(m, "LZMACompression", 19);
PyModule_AddIntConstant(m, "JBIG1Compression", 20);
PyModule_AddIntConstant(m, "JBIG2Compression", 21);
}