From c592845bd4b854717477674aae5a7a5c3b2040aa Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 1 Oct 2013 21:55:59 -0700 Subject: [PATCH 001/107] Error handling has changed, methods return null on error --- _imagingcms.c | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/_imagingcms.c b/_imagingcms.c index 968042795..ff1477863 100644 --- a/_imagingcms.c +++ b/_imagingcms.c @@ -24,7 +24,7 @@ http://www.cazabon.com\n\ " #include "Python.h" -#include "lcms.h" +#include "lcms2.h" #include "Imaging.h" #include "py3.h" @@ -37,11 +37,12 @@ http://www.cazabon.com\n\ #include #endif -#define PYCMSVERSION "0.1.0 pil" +#define PYCMSVERSION "1.0.0 pil" /* version history */ /* + 1.0.0 pil Integrating littleCMS2 0.1.0 pil integration & refactoring 0.0.2 alpha: Minor updates, added interfaces to littleCMS features, Jan 6, 2003 - fixed some memory holes in how transforms/profiles were created and passed back to Python @@ -107,8 +108,6 @@ cms_profile_open(PyObject* self, PyObject* args) if (!PyArg_ParseTuple(args, "s:profile_open", &sProfile)) return NULL; - cmsErrorAction(LCMS_ERROR_IGNORE); - hProfile = cmsOpenProfileFromFile(sProfile, "r"); if (!hProfile) { PyErr_SetString(PyExc_IOError, "cannot open profile file"); @@ -133,8 +132,6 @@ cms_profile_fromstring(PyObject* self, PyObject* args) return NULL; #endif - cmsErrorAction(LCMS_ERROR_IGNORE); - hProfile = cmsOpenProfileFromMem(pProfile, nProfile); if (!hProfile) { PyErr_SetString(PyExc_IOError, "cannot open profile from string"); @@ -273,8 +270,6 @@ _buildTransform(cmsHPROFILE hInputProfile, cmsHPROFILE hOutputProfile, char *sIn { cmsHTRANSFORM hTransform; - cmsErrorAction(LCMS_ERROR_IGNORE); - Py_BEGIN_ALLOW_THREADS /* create the transform */ @@ -297,8 +292,6 @@ _buildProofTransform(cmsHPROFILE hInputProfile, cmsHPROFILE hOutputProfile, cmsH { cmsHTRANSFORM hTransform; - cmsErrorAction(LCMS_ERROR_IGNORE); - Py_BEGIN_ALLOW_THREADS /* create the transform */ @@ -336,8 +329,6 @@ buildTransform(PyObject *self, PyObject *args) { if (!PyArg_ParseTuple(args, "O!O!ss|ii:buildTransform", &CmsProfile_Type, &pInputProfile, &CmsProfile_Type, &pOutputProfile, &sInMode, &sOutMode, &iRenderingIntent, &cmsFLAGS)) return NULL; - cmsErrorAction(LCMS_ERROR_IGNORE); - transform = _buildTransform(pInputProfile->profile, pOutputProfile->profile, sInMode, sOutMode, iRenderingIntent, cmsFLAGS); if (!transform) @@ -363,8 +354,6 @@ buildProofTransform(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "O!O!O!ss|iii:buildProofTransform", &CmsProfile_Type, &pInputProfile, &CmsProfile_Type, &pOutputProfile, &CmsProfile_Type, &pProofProfile, &sInMode, &sOutMode, &iRenderingIntent, &iProofIntent, &cmsFLAGS)) return NULL; - cmsErrorAction(LCMS_ERROR_IGNORE); - transform = _buildProofTransform(pInputProfile->profile, pOutputProfile->profile, pProofProfile->profile, sInMode, sOutMode, iRenderingIntent, iProofIntent, cmsFLAGS); if (!transform) @@ -390,8 +379,6 @@ cms_transform_apply(CmsTransformObject *self, PyObject *args) im = (Imaging) idIn; imOut = (Imaging) idOut; - cmsErrorAction(LCMS_ERROR_IGNORE); - result = pyCMSdoTransform(im, imOut, self->transform); return Py_BuildValue("i", result); @@ -412,8 +399,6 @@ createProfile(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "s|i:createProfile", &sColorSpace, &iColorTemp)) return NULL; - cmsErrorAction(LCMS_ERROR_IGNORE); - if (strcmp(sColorSpace, "LAB") == 0) { if (iColorTemp > 0) { result = cmsWhitePointFromTemp(iColorTemp, whitePoint); From af1e5259141248c3cb4d0d13ee162e7e950775a6 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 1 Oct 2013 22:03:52 -0700 Subject: [PATCH 002/107] ic -> cms prefix change --- _imagingcms.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/_imagingcms.c b/_imagingcms.c index ff1477863..e04118f0e 100644 --- a/_imagingcms.c +++ b/_imagingcms.c @@ -189,20 +189,20 @@ cms_transform_dealloc(CmsTransformObject* self) /* internal functions */ static const char* -findICmode(icColorSpaceSignature cs) +findICmode(cmsColorSpaceSignature cs) { switch (cs) { - case icSigXYZData: return "XYZ"; - case icSigLabData: return "LAB"; - case icSigLuvData: return "LUV"; - case icSigYCbCrData: return "YCbCr"; - case icSigYxyData: return "YXY"; - case icSigRgbData: return "RGB"; - case icSigGrayData: return "L"; - case icSigHsvData: return "HSV"; - case icSigHlsData: return "HLS"; - case icSigCmykData: return "CMYK"; - case icSigCmyData: return "CMY"; + case cmsSigXYZData: return "XYZ"; + case cmsSigLabData: return "LAB"; + case cmsSigLuvData: return "LUV"; + case cmsSigYCbCrData: return "YCbCr"; + case cmsSigYxyData: return "YXY"; + case cmsSigRgbData: return "RGB"; + case cmsSigGrayData: return "L"; + case cmsSigHsvData: return "HSV"; + case cmsSigHlsData: return "HLS"; + case cmsSigCmykData: return "CMYK"; + case cmsSigCmyData: return "CMY"; default: return ""; /* other TBA */ } } From e5a6615ad2939a678392691b16e742dae9c6f427 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 1 Oct 2013 22:07:35 -0700 Subject: [PATCH 003/107] DWORD -> cmsUInt32Number --- _imagingcms.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/_imagingcms.c b/_imagingcms.c index e04118f0e..390290298 100644 --- a/_imagingcms.c +++ b/_imagingcms.c @@ -207,7 +207,7 @@ findICmode(cmsColorSpaceSignature cs) } } -static DWORD +static cmsUInt32Number findLCMStype(char* PILmode) { if (strcmp(PILmode, "RGB") == 0) { @@ -266,7 +266,7 @@ pyCMSdoTransform(Imaging im, Imaging imOut, cmsHTRANSFORM hTransform) } static cmsHTRANSFORM -_buildTransform(cmsHPROFILE hInputProfile, cmsHPROFILE hOutputProfile, char *sInMode, char *sOutMode, int iRenderingIntent, DWORD cmsFLAGS) +_buildTransform(cmsHPROFILE hInputProfile, cmsHPROFILE hOutputProfile, char *sInMode, char *sOutMode, int iRenderingIntent, cmsUInt32Number cmsFLAGS) { cmsHTRANSFORM hTransform; @@ -288,7 +288,7 @@ _buildTransform(cmsHPROFILE hInputProfile, cmsHPROFILE hOutputProfile, char *sIn } static cmsHTRANSFORM -_buildProofTransform(cmsHPROFILE hInputProfile, cmsHPROFILE hOutputProfile, cmsHPROFILE hProofProfile, char *sInMode, char *sOutMode, int iRenderingIntent, int iProofIntent, DWORD cmsFLAGS) +_buildProofTransform(cmsHPROFILE hInputProfile, cmsHPROFILE hOutputProfile, cmsHPROFILE hProofProfile, char *sInMode, char *sOutMode, int iRenderingIntent, int iProofIntent, cmsUInt32Number cmsFLAGS) { cmsHTRANSFORM hTransform; @@ -450,7 +450,7 @@ static PyObject * cms_get_display_profile_win32(PyObject* self, PyObject* args) { char filename[MAX_PATH]; - DWORD filename_size; + cmsUInt32Number filename_size; BOOL ok; int handle = 0; From db4f11a05c6f6d9069923d241f4a7558b04f9ea9 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 1 Oct 2013 22:16:59 -0700 Subject: [PATCH 004/107] DWORD -> cmsUInt32Number --- _imagingcms.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/_imagingcms.c b/_imagingcms.c index 390290298..1c85523ac 100644 --- a/_imagingcms.c +++ b/_imagingcms.c @@ -392,30 +392,34 @@ createProfile(PyObject *self, PyObject *args) { char *sColorSpace; cmsHPROFILE hProfile; - int iColorTemp = 0; - LPcmsCIExyY whitePoint = NULL; - LCMSBOOL result; + cmsFloat64Number dColorTemp = 0.0; + cmsCIExyY *whitePoint = NULL; + cmsBool result; - if (!PyArg_ParseTuple(args, "s|i:createProfile", &sColorSpace, &iColorTemp)) + if (!PyArg_ParseTuple(args, "s|d:createProfile", &sColorSpace, &dColorTemp)) return NULL; if (strcmp(sColorSpace, "LAB") == 0) { - if (iColorTemp > 0) { - result = cmsWhitePointFromTemp(iColorTemp, whitePoint); + if (dColorTemp > 0.0) { + result = cmsWhitePointFromTemp(whitePoint, dColorTemp); if (!result) { - PyErr_SetString(PyExc_ValueError, "ERROR: Could not calculate white point from color temperature provided, must be integer in degrees Kelvin"); + PyErr_SetString(PyExc_ValueError, "ERROR: Could not calculate white point from color temperature provided, must be float in degrees Kelvin"); return NULL; } hProfile = cmsCreateLabProfile(whitePoint); - } else + } else { hProfile = cmsCreateLabProfile(NULL); + } } - else if (strcmp(sColorSpace, "XYZ") == 0) + else if (strcmp(sColorSpace, "XYZ") == 0) { hProfile = cmsCreateXYZProfile(); - else if (strcmp(sColorSpace, "sRGB") == 0) + } + else if (strcmp(sColorSpace, "sRGB") == 0) { hProfile = cmsCreate_sRGBProfile(); - else + } + else { hProfile = NULL; + } if (!hProfile) { PyErr_SetString(PyExc_ValueError, "failed to create requested color space"); From a2fd0e99e2c6bb2ff986701c15dc34e859474536 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 1 Oct 2013 22:22:56 -0700 Subject: [PATCH 005/107] createprofile --- _imagingcms.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_imagingcms.c b/_imagingcms.c index 1c85523ac..3dbe21198 100644 --- a/_imagingcms.c +++ b/_imagingcms.c @@ -406,9 +406,9 @@ createProfile(PyObject *self, PyObject *args) PyErr_SetString(PyExc_ValueError, "ERROR: Could not calculate white point from color temperature provided, must be float in degrees Kelvin"); return NULL; } - hProfile = cmsCreateLabProfile(whitePoint); + hProfile = cmsCreateLab2Profile(whitePoint); } else { - hProfile = cmsCreateLabProfile(NULL); + hProfile = cmsCreateLab2Profile(NULL); } } else if (strcmp(sColorSpace, "XYZ") == 0) { From 9c0f4a131f3c59ad8e2ba382ea12116f09bab720 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 1 Oct 2013 22:24:41 -0700 Subject: [PATCH 006/107] LCMSBOOL -> cmsBool --- _imagingcms.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_imagingcms.c b/_imagingcms.c index 3dbe21198..b5ba1250f 100644 --- a/_imagingcms.c +++ b/_imagingcms.c @@ -435,7 +435,7 @@ createProfile(PyObject *self, PyObject *args) static PyObject * cms_profile_is_intent_supported(CmsProfileObject *self, PyObject *args) { - LCMSBOOL result; + cmsBool result; int intent; int direction; From 88c584c5de755c636d2ff116c5fd857e14886dbb Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 1 Oct 2013 22:54:39 -0700 Subject: [PATCH 007/107] product attributes --- _imagingcms.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/_imagingcms.c b/_imagingcms.c index b5ba1250f..bc55414ba 100644 --- a/_imagingcms.c +++ b/_imagingcms.c @@ -507,28 +507,47 @@ static struct PyMethodDef cms_profile_methods[] = { {NULL, NULL} /* sentinel */ }; +static PyObject* +_profile_getattr(CmsProfileObject* self, cmsInfoType field) +{ + // UNDONE -- check that I'm getting the right fields on these. + // return PyUnicode_DecodeFSDefault(cmsTakeProductName(self->profile)); + wchar_t buf[256]; + cmsUInt32Number written; + written = cmsGetProfileInfo(self->profile, + field, + "en", + "us", + buf, + 256); + if (written) { + return PyUnicode_DecodeFSDefault(buf); + } + return NULL; +} + static PyObject* cms_profile_getattr_product_name(CmsProfileObject* self, void* closure) { - return PyUnicode_DecodeFSDefault(cmsTakeProductName(self->profile)); + return _profile_getattr(self, cmsInfoModel); } static PyObject* cms_profile_getattr_product_desc(CmsProfileObject* self, void* closure) -{ - return PyUnicode_DecodeFSDefault(cmsTakeProductDesc(self->profile)); +{ + return _profile_getattr(self, cmsInfoDescription); } static PyObject* cms_profile_getattr_product_info(CmsProfileObject* self, void* closure) { - return PyUnicode_DecodeFSDefault(cmsTakeProductInfo(self->profile)); + return _profile_getattr(self, cmsInfoManufacturer); } static PyObject* cms_profile_getattr_rendering_intent(CmsProfileObject* self, void* closure) { - return PyInt_FromLong(cmsTakeRenderingIntent(self->profile)); + return PyInt_FromLong(cmsGetHeaderRenderingIntent(self->profile)); } static PyObject* From e9e55994120a65c92d860810e38f9986794f104e Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 1 Oct 2013 23:05:56 -0700 Subject: [PATCH 008/107] buildable, selftest registers properly --- selftest.py | 2 +- setup.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/selftest.py b/selftest.py index 463902648..1eb7738e3 100644 --- a/selftest.py +++ b/selftest.py @@ -195,7 +195,7 @@ if __name__ == "__main__": check_codec("ZLIB (PNG/ZIP)", "zip") check_codec("G4 TIFF", "group4") check_module("FREETYPE2", "PIL._imagingft") - check_module("LITTLECMS", "PIL._imagingcms") + check_module("LITTLECMS2", "PIL._imagingcms") check_module("WEBP", "PIL._webp") try: from PIL import _webp diff --git a/setup.py b/setup.py index 0d2d80c35..75b68e936 100644 --- a/setup.py +++ b/setup.py @@ -331,8 +331,8 @@ class pil_build_ext(build_ext): _add_directory(self.compiler.include_dirs, dir, 0) if feature.want('lcms'): - if _find_include_file(self, "lcms.h"): - if _find_library_file(self, "lcms"): + if _find_include_file(self, "lcms2.h"): + if _find_library_file(self, "lcms2"): feature.lcms = "lcms" if _tkinter and _find_include_file(self, "tk.h"): @@ -415,7 +415,7 @@ class pil_build_ext(build_ext): if sys.platform == "win32": extra.extend(["user32", "gdi32"]) exts.append(Extension( - "PIL._imagingcms", ["_imagingcms.c"], libraries=["lcms"] + extra)) + "PIL._imagingcms", ["_imagingcms.c"], libraries=["lcms2"] + extra)) if os.path.isfile("_webp.c") and feature.webp: libs = ["webp"] @@ -491,7 +491,7 @@ class pil_build_ext(build_ext): (feature.zlib, "ZLIB (PNG/ZIP)"), (feature.tiff, "TIFF G3/G4 (experimental)"), (feature.freetype, "FREETYPE2"), - (feature.lcms, "LITTLECMS"), + (feature.lcms, "LITTLECMS2"), (feature.webp, "WEBP"), (feature.webpmux, "WEBPMUX"), ] From 135c47e82a3b27d0ece8975ab681007de962b076 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 1 Oct 2013 23:22:37 -0700 Subject: [PATCH 009/107] try the ascii version of cmsGetProfileInfo --- _imagingcms.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/_imagingcms.c b/_imagingcms.c index bc55414ba..d2efbb8d2 100644 --- a/_imagingcms.c +++ b/_imagingcms.c @@ -512,18 +512,20 @@ _profile_getattr(CmsProfileObject* self, cmsInfoType field) { // UNDONE -- check that I'm getting the right fields on these. // return PyUnicode_DecodeFSDefault(cmsTakeProductName(self->profile)); - wchar_t buf[256]; + //wchar_t buf[256]; -- UNDONE need wchar_t for unicode version. + char buf[256]; cmsUInt32Number written; - written = cmsGetProfileInfo(self->profile, - field, - "en", - "us", - buf, - 256); + written = cmsGetProfileInfoASCII(self->profile, + field, + "en", + "us", + buf, + 256); if (written) { return PyUnicode_DecodeFSDefault(buf); } - return NULL; + // UNDONE suppressing error here by sending back blank string. + return PyUnicode_DecodeFSDefault(""); } static PyObject* From 13860addc4b7dd10227baffff234550cb59d1ecf Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 1 Oct 2013 23:22:45 -0700 Subject: [PATCH 010/107] versioning --- PIL/ImageCms.py | 4 +++- Tests/test_imagecms.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/PIL/ImageCms.py b/PIL/ImageCms.py index e3cb11f36..4aba23c65 100644 --- a/PIL/ImageCms.py +++ b/PIL/ImageCms.py @@ -42,6 +42,8 @@ pyCMS Version History: + 1.0.0 pil Oct 2013 Port to LCMS 2. + 0.1.0 pil mod March 10, 2009 Renamed display profile to proof profile. The proof @@ -77,7 +79,7 @@ pyCMS """ -VERSION = "0.1.0 pil" +VERSION = "1.0.0 pil" # --------------------------------------------------------------------. diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index 29e578192..fec299ace 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -14,7 +14,7 @@ def test_sanity(): # this mostly follows the cms_test outline. v = ImageCms.versions() # should return four strings - assert_equal(v[0], '0.1.0 pil') + assert_equal(v[0], '1.0.0 pil') assert_equal(list(map(type, v)), [str, str, str, str]) # internal version number From 16eed660951ca3e92817bdb7738c946c33a32829 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 2 Oct 2013 14:26:47 -0700 Subject: [PATCH 011/107] requiring lcms > 2 --- _imagingcms.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/_imagingcms.c b/_imagingcms.c index d2efbb8d2..f9dd5d0a9 100644 --- a/_imagingcms.c +++ b/_imagingcms.c @@ -28,10 +28,6 @@ http://www.cazabon.com\n\ #include "Imaging.h" #include "py3.h" -#if LCMS_VERSION < 117 -#define LCMSBOOL BOOL -#endif - #ifdef WIN32 #include #include From bded4abc9dfd93e869968841216fc556cc28af5a Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 2 Oct 2013 16:13:07 -0700 Subject: [PATCH 012/107] profile name mostly working, profile info failing still since it's complicated sfuff that's not easily exposed in lcms2 --- _imagingcms.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 2 deletions(-) diff --git a/_imagingcms.c b/_imagingcms.c index f9dd5d0a9..1473a922a 100644 --- a/_imagingcms.c +++ b/_imagingcms.c @@ -527,21 +527,90 @@ _profile_getattr(CmsProfileObject* self, cmsInfoType field) static PyObject* cms_profile_getattr_product_name(CmsProfileObject* self, void* closure) { - return _profile_getattr(self, cmsInfoModel); + // name was "%s - %s" (model, manufacturer) || Description , + // but if the Model and Manufacturer were the same or the model + // was long, Just the model, in 1.x + PyObject *model = _profile_getattr(self, cmsInfoModel); + PyObject *manufacturer = _profile_getattr(self, cmsInfoManufacturer); + + if (!PyString_Size(model) && !PyString_Size(manufacturer)){ + return _profile_getattr(self, cmsInfoDescription); + } + if (!PyString_Size(manufacturer)){ + return model; + } + PyString_Concat(&model, + PyString_FromString(" - ")); + PyString_Concat(&model,_profile_getattr(self, cmsInfoManufacturer)); + return model; } static PyObject* cms_profile_getattr_product_desc(CmsProfileObject* self, void* closure) { + // description was Description != 'Copyright' || or "%s - %s" (manufacturer, model) in 1.x return _profile_getattr(self, cmsInfoDescription); } +void _info_concat(PyObject **ret, PyObject *elt){ + if (PyString_Size(elt)){ + PyString_Concat(ret, elt); + PyString_Concat(ret, PyString_FromString("\r\n\r\n")); + } +} + static PyObject* cms_profile_getattr_product_info(CmsProfileObject* self, void* closure) -{ +{ + // info was description \r\n\r\n copyright \r\n\r\n K007 tag \r\n\r\n whitepoint + PyObject *description = _profile_getattr(self, cmsInfoDescription); + PyObject *copyright = _profile_getattr(self, cmsInfoCopyright); + PyObject *ret = PyString_FromString(""); + + _info_concat(&ret, description); + _info_concat(&ret, copyright); + +#define K007 (icTagSignature)0x4B303037 + if (cmsIsTag(self->profile, cmsSigMediaWhitePointTag)){ + cmsCIExyY *WhitePt; + cmsFloat64Number tempK; + + WhitePt = (cmsCIExyY *) cmsReadTag(self->profile, cmsSigMediaWhitePointTag); + if (cmsTempFromWhitePoint(&tempK, WhitePt)){ + char tempstr[10]; + snprintf(tempstr, 10, "%5.0f", tempK); + _info_concat(&ret, PyString_FromFormat("White Point: %sK", tempstr)); + } + } + return ret; +} + +/* use these four for the individual fields. + */ +static PyObject* +cms_profile_getattr_product_description(CmsProfileObject* self, void* closure) +{ + return _profile_getattr(self, cmsInfoDescription); +} + +static PyObject* +cms_profile_getattr_product_model(CmsProfileObject* self, void* closure) +{ + return _profile_getattr(self, cmsInfoModel); +} + +static PyObject* +cms_profile_getattr_product_manufacturer(CmsProfileObject* self, void* closure) +{ return _profile_getattr(self, cmsInfoManufacturer); } +static PyObject* +cms_profile_getattr_product_copyright(CmsProfileObject* self, void* closure) +{ + return _profile_getattr(self, cmsInfoCopyright); +} + static PyObject* cms_profile_getattr_rendering_intent(CmsProfileObject* self, void* closure) { @@ -565,6 +634,10 @@ static struct PyGetSetDef cms_profile_getsetters[] = { { "product_name", (getter) cms_profile_getattr_product_name }, { "product_desc", (getter) cms_profile_getattr_product_desc }, { "product_info", (getter) cms_profile_getattr_product_info }, + { "product_description", (getter) cms_profile_getattr_product_description }, + { "product_manufacturer", (getter) cms_profile_getattr_product_manufacturer }, + { "product_model", (getter) cms_profile_getattr_product_model }, + { "product_copyright", (getter) cms_profile_getattr_product_copyright }, { "rendering_intent", (getter) cms_profile_getattr_rendering_intent }, { "pcs", (getter) cms_profile_getattr_pcs }, { "color_space", (getter) cms_profile_getattr_color_space }, From 2c46984ed1accc311cfbc551562cf4f50ac9454e Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 2 Oct 2013 16:34:46 -0700 Subject: [PATCH 013/107] one more condition on the name field --- _imagingcms.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_imagingcms.c b/_imagingcms.c index 1473a922a..cb30503e9 100644 --- a/_imagingcms.c +++ b/_imagingcms.c @@ -536,7 +536,7 @@ cms_profile_getattr_product_name(CmsProfileObject* self, void* closure) if (!PyString_Size(model) && !PyString_Size(manufacturer)){ return _profile_getattr(self, cmsInfoDescription); } - if (!PyString_Size(manufacturer)){ + if (!PyString_Size(manufacturer) || PyString_Size(model)> 30){ return model; } PyString_Concat(&model, From ce4671c14cae421068eb737a1f10a1c2e01fbffe Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 2 Oct 2013 16:35:07 -0700 Subject: [PATCH 014/107] better white point calculation, but still questionable --- _imagingcms.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/_imagingcms.c b/_imagingcms.c index cb30503e9..5bea640fe 100644 --- a/_imagingcms.c +++ b/_imagingcms.c @@ -570,13 +570,14 @@ cms_profile_getattr_product_info(CmsProfileObject* self, void* closure) _info_concat(&ret, description); _info_concat(&ret, copyright); -#define K007 (icTagSignature)0x4B303037 if (cmsIsTag(self->profile, cmsSigMediaWhitePointTag)){ - cmsCIExyY *WhitePt; + cmsCIEXYZ *WhitePt; + cmsCIExyY xyyWhitePt; cmsFloat64Number tempK; - WhitePt = (cmsCIExyY *) cmsReadTag(self->profile, cmsSigMediaWhitePointTag); - if (cmsTempFromWhitePoint(&tempK, WhitePt)){ + WhitePt = (cmsCIEXYZ *) cmsReadTag(self->profile, cmsSigMediaWhitePointTag); + cmsXYZ2xyY(&xyyWhitePt, WhitePt); + if (cmsTempFromWhitePoint(&tempK, &xyyWhitePt)){ char tempstr[10]; snprintf(tempstr, 10, "%5.0f", tempK); _info_concat(&ret, PyString_FromFormat("White Point: %sK", tempstr)); From 95b2434eb2be40401250d71646be6530026e889f Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 2 Oct 2013 16:36:20 -0700 Subject: [PATCH 015/107] added accessors for the 4 individual info fields --- PIL/ImageCms.py | 116 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/PIL/ImageCms.py b/PIL/ImageCms.py index 4aba23c65..3bc782d2d 100644 --- a/PIL/ImageCms.py +++ b/PIL/ImageCms.py @@ -599,6 +599,11 @@ def getProfileName(profile): # add an extra newline to preserve pyCMS compatibility if not isinstance(profile, ImageCmsProfile): profile = ImageCmsProfile(profile) + ## print ("get profile name") + ## print ("\n".join([profile.profile.product_model, + ## profile.profile.product_description, + ## profile.profile.product_manufacturer, + ## profile.profile.product_copyright])) return profile.profile.product_name + "\n" except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) @@ -631,6 +636,117 @@ def getProfileInfo(profile): except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) + +## +# (pyCMS) Gets the copyright for the given profile. +# +# If profile isn't a valid CmsProfile object or filename to a profile, +# a PyCMSError is raised. +# +# If an error occurs while trying to obtain the copyright tag, a PyCMSError +# is raised +# +# Use this function to obtain the information stored in the profile's +# copyright tag. +# +# @param profile EITHER a valid CmsProfile object, OR a string of the filename +# of an ICC profile. +# @return A string containing the internal profile information stored in an ICC +# tag. +# @exception PyCMSError + +def getProfileCopyright(profile): + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return profile.profile.product_copyright + "\n" + except (AttributeError, IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + +## +# (pyCMS) Gets the manufacturer for the given profile. +# +# If profile isn't a valid CmsProfile object or filename to a profile, +# a PyCMSError is raised. +# +# If an error occurs while trying to obtain the manufacturer tag, a PyCMSError +# is raised +# +# Use this function to obtain the information stored in the profile's +# manufacturer tag. +# +# @param profile EITHER a valid CmsProfile object, OR a string of the filename +# of an ICC profile. +# @return A string containing the internal profile information stored in an ICC +# tag. +# @exception PyCMSError + +def getProfileManufacturer(profile): + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return profile.profile.product_manufacturer + "\n" + except (AttributeError, IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + +## +# (pyCMS) Gets the model for the given profile. +# +# If profile isn't a valid CmsProfile object or filename to a profile, +# a PyCMSError is raised. +# +# If an error occurs while trying to obtain the model tag, a PyCMSError +# is raised +# +# Use this function to obtain the information stored in the profile's +# model tag. +# +# @param profile EITHER a valid CmsProfile object, OR a string of the filename +# of an ICC profile. +# @return A string containing the internal profile information stored in an ICC +# tag. +# @exception PyCMSError + +def getProfileModel(profile): + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return profile.profile.product_model + "\n" + except (AttributeError, IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + +## +# (pyCMS) Gets the description for the given profile. +# +# If profile isn't a valid CmsProfile object or filename to a profile, +# a PyCMSError is raised. +# +# If an error occurs while trying to obtain the description tag, a PyCMSError +# is raised +# +# Use this function to obtain the information stored in the profile's +# description tag. +# +# @param profile EITHER a valid CmsProfile object, OR a string of the filename +# of an ICC profile. +# @return A string containing the internal profile information stored in an ICC +# tag. +# @exception PyCMSError + +def getProfileDescription(profile): + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return profile.profile.product_description + "\n" + except (AttributeError, IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + + + ## # (pyCMS) Gets the default intent name for the given profile. # From 1a6ca03ec7de8b587c5380cd217e94eee71483f1 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 2 Oct 2013 16:37:04 -0700 Subject: [PATCH 016/107] switched travis to lcms2 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 58f493b1b..88118b1b2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ python: - 3.2 - 3.3 -install: "sudo apt-get -qq install libfreetype6-dev liblcms1-dev libwebp-dev" +install: "sudo apt-get -qq install libfreetype6-dev liblcms2-dev libwebp-dev" script: - python setup.py clean From c5216d79c26b852eafd6a97a2509564f5e649234 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 2 Oct 2013 20:26:34 -0700 Subject: [PATCH 017/107] Py3 same test results as py2 --- _imagingcms.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/_imagingcms.c b/_imagingcms.c index 5bea640fe..f4c97e9fe 100644 --- a/_imagingcms.c +++ b/_imagingcms.c @@ -518,10 +518,10 @@ _profile_getattr(CmsProfileObject* self, cmsInfoType field) buf, 256); if (written) { - return PyUnicode_DecodeFSDefault(buf); + return PyUnicode_FromString(buf); } // UNDONE suppressing error here by sending back blank string. - return PyUnicode_DecodeFSDefault(""); + return PyUnicode_FromString(""); } static PyObject* @@ -532,17 +532,18 @@ cms_profile_getattr_product_name(CmsProfileObject* self, void* closure) // was long, Just the model, in 1.x PyObject *model = _profile_getattr(self, cmsInfoModel); PyObject *manufacturer = _profile_getattr(self, cmsInfoManufacturer); + PyObject *result; - if (!PyString_Size(model) && !PyString_Size(manufacturer)){ + if (!PyUnicode_GetSize(model) && !PyUnicode_GetSize(manufacturer)){ return _profile_getattr(self, cmsInfoDescription); } - if (!PyString_Size(manufacturer) || PyString_Size(model)> 30){ + if (!PyUnicode_GetSize(manufacturer) || PyUnicode_GetSize(model)> 30){ return model; } - PyString_Concat(&model, - PyString_FromString(" - ")); - PyString_Concat(&model,_profile_getattr(self, cmsInfoManufacturer)); - return model; + result = PyUnicode_Concat(model, + PyUnicode_FromString(" - ")); + result = PyUnicode_Concat(result,_profile_getattr(self, cmsInfoManufacturer)); + return result; } static PyObject* @@ -553,9 +554,9 @@ cms_profile_getattr_product_desc(CmsProfileObject* self, void* closure) } void _info_concat(PyObject **ret, PyObject *elt){ - if (PyString_Size(elt)){ - PyString_Concat(ret, elt); - PyString_Concat(ret, PyString_FromString("\r\n\r\n")); + if (PyUnicode_GetSize(elt)){ + *ret = PyUnicode_Concat(*ret, elt); + *ret = PyUnicode_Concat(*ret, PyUnicode_FromString("\r\n\r\n")); } } @@ -565,7 +566,7 @@ cms_profile_getattr_product_info(CmsProfileObject* self, void* closure) // info was description \r\n\r\n copyright \r\n\r\n K007 tag \r\n\r\n whitepoint PyObject *description = _profile_getattr(self, cmsInfoDescription); PyObject *copyright = _profile_getattr(self, cmsInfoCopyright); - PyObject *ret = PyString_FromString(""); + PyObject *ret = PyUnicode_FromString(""); _info_concat(&ret, description); _info_concat(&ret, copyright); @@ -580,7 +581,7 @@ cms_profile_getattr_product_info(CmsProfileObject* self, void* closure) if (cmsTempFromWhitePoint(&tempK, &xyyWhitePt)){ char tempstr[10]; snprintf(tempstr, 10, "%5.0f", tempK); - _info_concat(&ret, PyString_FromFormat("White Point: %sK", tempstr)); + _info_concat(&ret, PyUnicode_FromFormat("White Point: %sK", tempstr)); } } return ret; From 9b6af55702694d78e783537f0c02902e0eef2157 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Domen=20Ko=C5=BEar?= Date: Mon, 7 Oct 2013 21:43:39 +0200 Subject: [PATCH 018/107] respect CFLAGS/LDFLAGS when searching for headers/libs --- setup.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/setup.py b/setup.py index ad1dbdb49..6bf002fab 100644 --- a/setup.py +++ b/setup.py @@ -214,6 +214,14 @@ class pil_build_ext(build_ext): _add_directory(library_dirs, "/usr/local/lib") # FIXME: check /opt/stuff directories here? + # respect CFLAGS/LDFLAGS + for k in 'CFLAGS LDFLAGS'.split(): + if k in os.environ: + for match in re.finditer(r'-I([^\s]+)', os.environ[k]): + _add_directory(include_dirs, match.group(1)) + for match in re.finditer(r'-L([^\s]+)', os.environ[k]): + _add_directory(library_dirs, match.group(1)) + # include, rpath, if set as environment variables: for k in 'C_INCLUDE_PATH INCLUDE'.split(): if k in os.environ: From 6fea2a5ad3edff997afbdf95fb4631a6e5dfd25f Mon Sep 17 00:00:00 2001 From: Steve Johnson Date: Tue, 8 Oct 2013 16:19:35 -0700 Subject: [PATCH 019/107] Simplify docs/conf.py --- docs/conf.py | 199 ++++----------------------------------------------- 1 file changed, 14 insertions(+), 185 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index a9ae431bb..34eb6b7ef 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,96 +1,39 @@ # -*- coding: utf-8 -*- -# -# Pillow (PIL fork) documentation build configuration file, created by -# sphinx-quickstart on Fri Apr 12 19:51:26 2013. -# -# This file is execfile()d with the current directory set to its containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. +import os +import sys -import sys, os - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('../')) import PIL -# -- General configuration ----------------------------------------------------- +### general configuration ### -# If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +needs_sphinx = '1.0' -# Add any Sphinx extension module names here, as strings. They can be extensions -# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx.ext.intersphinx'] intersphinx_mapping = {'http://docs.python.org/2/': None} -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. source_suffix = '.rst' - -# The encoding of source files. +templates_path = ['_templates'] #source_encoding = 'utf-8-sig' - -# The master toctree document. master_doc = 'index' -# General information about the project. project = u'Pillow (PIL fork)' -copyright = u'1997-2011 by Secret Labs AB, 1995-2011 by Fredrik Lundh, 2010-2013 Alex Clark' +copyright = (u'1997-2011 by Secret Labs AB,' + u' 1995-2011 by Fredrik Lundh, 2010-2013 Alex Clark') -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# # The short X.Y version. version = PIL.PILLOW_VERSION # The full version, including alpha/beta/rc tags. release = version -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. +# currently excluding autodoc'd plugs exclude_patterns = ['_build', 'plugins.rst'] -# The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - - -# -- Options for HTML output --------------------------------------------------- +### HTML output ### from better import better_theme_path html_theme_path = [better_theme_path] @@ -100,9 +43,6 @@ html_title = "Pillow v{release} (PIL fork)".format(release=release) html_short_title = "Home" html_static_path = ['_static'] -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. html_theme_options = {} html_sidebars = { @@ -110,130 +50,19 @@ html_sidebars = { 'index': ['searchbox.html'], } -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -#html_title = None - # Output file base name for HTML help builder. -htmlhelp_basename = 'PillowPILforkdoc' +htmlhelp_basename = 'Pillowdoc' -# -- Options for LaTeX output -------------------------------------------------- +### LaTeX output (RtD PDF output as well) ### -latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', +latex_elements = {} -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'PillowPILfork.tex', u'Pillow (PIL fork) Documentation', - u'Author', 'manual'), + ('index', 'Pillow.tex', u'Pillow (PIL fork) Documentation', u'Author', + 'manual'), ] -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# If true, show page references after internal links. -#latex_show_pagerefs = False - -# If true, show URL addresses after external links. -#latex_show_urls = False - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_domain_indices = True - - -# -- Options for manual page output -------------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'pillowpilfork', u'Pillow (PIL fork) Documentation', - [u'Author'], 1) -] - -# If true, show URL addresses after external links. -#man_show_urls = False - - -# -- Options for Texinfo output ------------------------------------------------ - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ('index', 'PillowPILfork', u'Pillow (PIL fork) Documentation', - u'Author', 'PillowPILfork', 'One line description of project.', - 'Miscellaneous'), -] - -# Documents to append as an appendix to all manuals. -#texinfo_appendices = [] - -# If false, no module index is generated. -#texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' - - -# -- Options for Epub output --------------------------------------------------- - -# Bibliographic Dublin Core info. -epub_title = u'Pillow (PIL fork)' -epub_author = u'Author' -epub_publisher = u'Author' -epub_copyright = u'2013, Author' - -# The language of the text. It defaults to the language option -# or en if the language is not set. -#epub_language = '' - -# The scheme of the identifier. Typical schemes are ISBN or URL. -#epub_scheme = '' - -# The unique identifier of the text. This can be a ISBN number -# or the project homepage. -#epub_identifier = '' - -# A unique identification for the text. -#epub_uid = '' - -# A tuple containing the cover image and cover page html template filenames. -#epub_cover = () - -# HTML files that should be inserted before the pages created by sphinx. -# The format is a list of tuples containing the path and title. -#epub_pre_files = [] - -# HTML files shat should be inserted after the pages created by sphinx. -# The format is a list of tuples containing the path and title. -#epub_post_files = [] - -# A list of files that should not be packed into the epub file. -#epub_exclude_files = [] - -# The depth of the table of contents in toc.ncx. -#epub_tocdepth = 3 - -# Allow duplicate toc entries. -#epub_tocdup = True # skip_api_docs setting will skip PIL.rst if True. Used for working on the # guides; makes livereload basically instantaneous. From 36b370e4654e63514bf76bdce8fc98ab56663aa3 Mon Sep 17 00:00:00 2001 From: Steve Johnson Date: Tue, 8 Oct 2013 16:34:24 -0700 Subject: [PATCH 020/107] Helpful sidebar --- docs/Guardfile | 1 + docs/_templates/sidebarhelp.html | 18 ++++++++++++++++++ docs/conf.py | 5 +++-- docs/index.rst | 1 - 4 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 docs/_templates/sidebarhelp.html diff --git a/docs/Guardfile b/docs/Guardfile index 2bbb2ef91..f8f3051ed 100644 --- a/docs/Guardfile +++ b/docs/Guardfile @@ -5,5 +5,6 @@ from livereload.compiler import shell Task.add('*.rst', shell('make html')) Task.add('*/*.rst', shell('make html')) Task.add('_static/*.css', shell('make clean html')) +Task.add('_templates/*', shell('make clean html')) Task.add('Makefile', shell('make html')) Task.add('conf.py', shell('make html')) diff --git a/docs/_templates/sidebarhelp.html b/docs/_templates/sidebarhelp.html new file mode 100644 index 000000000..330b3de45 --- /dev/null +++ b/docs/_templates/sidebarhelp.html @@ -0,0 +1,18 @@ +

Need help?

+ +

+ You can seek realtime assistance via IRC at + irc://irc.freenode.net#pil. You can + also post to the + + Image-SIG mailing list. And, of course, there's + + Stack Overflow. +

+ +

+ If you've discovered a bug, you can + open an issue + on Github. +

+ diff --git a/docs/conf.py b/docs/conf.py index 34eb6b7ef..bb1008063 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -46,8 +46,9 @@ html_static_path = ['_static'] html_theme_options = {} html_sidebars = { - '**': ['localtoc.html', 'sourcelink.html', 'searchbox.html'], - 'index': ['searchbox.html'], + '**': ['localtoc.html', 'sourcelink.html', 'sidebarhelp.html', + 'searchbox.html'], + 'index': ['sidebarhelp.html', 'searchbox.html'], } # Output file base name for HTML help builder. diff --git a/docs/index.rst b/docs/index.rst index 883b1bdb3..62a1ff9c9 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -21,7 +21,6 @@ aware that it was last updated for PIL 1.1.5. handbook/guides.rst handbook/appendices.rst PIL - plugins Indices and tables ================== From ab1f35a8a8e71e39efe4ddba81ac65a474f28175 Mon Sep 17 00:00:00 2001 From: Steve Johnson Date: Tue, 8 Oct 2013 17:13:06 -0700 Subject: [PATCH 021/107] Copy/move a bunch of README content to the doc site --- README.rst | 369 +-------------------------------- docs/about.rst | 47 +++++ docs/guides.rst | 10 + docs/handbook/guides.rst | 9 - docs/index.rst | 51 ++++- docs/installation.rst | 191 +++++++++++++++++ docs/original-readme.rst | 306 +++++++++++++++++++++++++++ docs/porting-pil-to-pillow.rst | 17 ++ 8 files changed, 625 insertions(+), 375 deletions(-) create mode 100644 docs/about.rst create mode 100644 docs/guides.rst delete mode 100644 docs/handbook/guides.rst create mode 100644 docs/installation.rst create mode 100644 docs/original-readme.rst create mode 100644 docs/porting-pil-to-pillow.rst diff --git a/README.rst b/README.rst index 5917d6a21..3b5e56d58 100644 --- a/README.rst +++ b/README.rst @@ -16,6 +16,10 @@ Pillow is the "friendly" PIL fork by Alex Clark and Contributors. PIL is the Pyt :target: https://pypi.python.org/pypi/Pillow/ :alt: Number of PyPI downloads +The full documentation is hosted at http://pillow.readthedocs.org/. It +contains everything in this file plus tutorials, reference, compatibility +details, and more. + Introduction ------------ @@ -30,25 +34,10 @@ The fork author's goal is to foster active development of PIL through: - Regular releases to the `Python Package Index `_ - Solicitation for community contributions and involvement on `Image-SIG `_ -Why a fork? -~~~~~~~~~~~ +For information about why this fork exists and how it differs from PIL, see +`the About page in the documentation`_. -PIL is not setuptools compatible. Please see http://mail.python.org/pipermail/image-sig/2010-August/006480.html for a more detailed explanation. Also, PIL's current bi-yearly (or greater) release schedule is too infrequent to accomodate the large number and frequency of issues reported. - -What about the official PIL? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. Note:: Prior to Pillow 2.0.0, very few image code changes were made. Pillow 2.0.0 added Python 3 support and includes many bug fixes from many contributors. - -As more time passes since the last PIL release, the likelyhood of a new PIL release decreases. However, we've yet to hear an official "PIL is dead" announcement. So if you still want to support PIL, please report issues here first: - -- https://bitbucket.org/effbot/pil-2009-raclette/issues - -Then open a Pillow ticket here: - -- https://github.com/python-imaging/Pillow/issues - -Please provide a link to the PIL ticket so we can track the issue(s) upstream. +.. _the About page in the documentation: http://pillow.readthedocs.org/en/latest/about.html Installation ------------ @@ -69,11 +58,6 @@ Or download the compressed archive from PyPI, extract it, and inside it run:: For more information, please see http://pillow.readthedocs.org/en/latest/ or below. -Documentation -------------- - -The API documentation included with PIL has been converted (from HTML generated by pythondoc) to reStructured text (via pandoc) and is now `hosted by readthedocs.org `_. This is a work in progress: in order to re-generate new API documentation, either `pythondoc `_ will have to be run again or the pythondoc functionality must be converted to Sphinx. - Community Support ----------------- @@ -205,36 +189,8 @@ Python Wheels Platform support ~~~~~~~~~~~~~~~~ -Current platform support for Pillow. Binary distributions are contributed for each release on a volunteer basis, but the source should compile and run everywhere platform support is listed. In general, we aim to support all current versions of Linux, OS X, and Windows. - -.. Note:: Contributors please test on your platform, edit this document and send a pull request - -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -|**Operating system** |**Supported**|**Tested Python versions** |**Tested Pillow versions** |**Tested processors** | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| CentOS 6.3 |Yes | 2.7,3.3 | |x86 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Mac OS X 10.8 Mountain Lion |Yes | 2.6,2.7,3.2,3.3 | |x86-64 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Mac OS X 10.7 Lion |Yes | 2.6,2.7,3.2,3.3 | 2.2.0 |x86-64 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Redhat Linux 6 |Yes | 2.6 | |x86 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Ubuntu Linux 10.04 LTS |Yes | 2.6 | 2.2.0 |x86,x86-64 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Ubuntu Linux 12.04 LTS |Yes | 2.6,2.7,3.2,3.3,PyPy2.1 | 2.2.0 |x86,x86-64 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Raspian Wheezy |Yes | 2.7,3.2 | 2.2.0 |arm | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Gentoo Linux |Yes | 2.7,3.2 | 2.1.0 |x86-64 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Windows 7 Pro |Yes | 2.7,3.2 | |x86 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Windows Server 2008 R2 Enterprise|Yes | 3.3 | |x86-64 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Windows 8 Pro |Yes | 2.6,2.7,3.2,3.3,3.4a3 | 2.2.0 |x86,x86-64 | -+----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ - +Current platform support for Pillow is documented here: +http://pillow.readthedocs.org/en/latest/installation.html#platform-support Port existing PIL-based code to Pillow ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -253,310 +209,3 @@ The preferred, future proof method of importing the private ``_imaging`` module from PIL import Image _imaging = Image.core - -Python Imaging Library -====================== - -.. Note:: What follows is the original PIL 1.1.7 README file contents. - -:: - - The Python Imaging Library - $Id$ - - Release 1.1.7 (November 15, 2009) - - ==================================================================== - The Python Imaging Library 1.1.7 - ==================================================================== - - Contents - -------- - - + Introduction - + Support Options - - Commercial support - - Free support - + Software License - + Build instructions (all platforms) - - Additional notes for Mac OS X - - Additional notes for Windows - - -------------------------------------------------------------------- - Introduction - -------------------------------------------------------------------- - - The Python Imaging Library (PIL) adds image processing capabilities - to your Python environment. This library provides extensive file - format support, an efficient internal representation, and powerful - image processing capabilities. - - This source kit has been built and tested with Python 2.0 and newer, - on Windows, Mac OS X, and major Unix platforms. Large parts of the - library also work on 1.5.2 and 1.6. - - The main distribution site for this software is: - - http://www.pythonware.com/products/pil/ - - That site also contains information about free and commercial support - options, PIL add-ons, answers to frequently asked questions, and more. - - - Development versions (alphas, betas) are available here: - - http://effbot.org/downloads/ - - - The PIL handbook is not included in this distribution; to get the - latest version, check: - - http://www.pythonware.com/library/ - http://effbot.org/books/imagingbook/ (drafts) - - - For installation and licensing details, see below. - - - -------------------------------------------------------------------- - Support Options - -------------------------------------------------------------------- - - + Commercial Support - - Secret Labs (PythonWare) offers support contracts for companies using - the Python Imaging Library in commercial applications, and in mission- - critical environments. The support contract includes technical support, - bug fixes, extensions to the PIL library, sample applications, and more. - - For the full story, check: - - http://www.pythonware.com/products/pil/support.htm - - - + Free Support - - For support and general questions on the Python Imaging Library, send - e-mail to the Image SIG mailing list: - - image-sig@python.org - - You can join the Image SIG by sending a mail to: - - image-sig-request@python.org - - Put "subscribe" in the message body to automatically subscribe to the - list, or "help" to get additional information. Alternatively, you can - send your questions to the Python mailing list, python-list@python.org, - or post them to the newsgroup comp.lang.python. DO NOT SEND SUPPORT - QUESTIONS TO PYTHONWARE ADDRESSES. - - - -------------------------------------------------------------------- - Software License - -------------------------------------------------------------------- - - The Python Imaging Library is - - Copyright (c) 1997-2009 by Secret Labs AB - Copyright (c) 1995-2009 by Fredrik Lundh - - By obtaining, using, and/or copying this software and/or its - associated documentation, you agree that you have read, understood, - and will comply with the following terms and conditions: - - Permission to use, copy, modify, and distribute this software and its - associated documentation for any purpose and without fee is hereby - granted, provided that the above copyright notice appears in all - copies, and that both that copyright notice and this permission notice - appear in supporting documentation, and that the name of Secret Labs - AB or the author not be used in advertising or publicity pertaining to - distribution of the software without specific, written prior - permission. - - SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO - THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR - ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT - OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - - -------------------------------------------------------------------- - Build instructions (all platforms) - -------------------------------------------------------------------- - - For a list of changes in this release, see the CHANGES document. - - 0. If you're in a hurry, try this: - - $ tar xvfz Imaging-1.1.7.tar.gz - $ cd Imaging-1.1.7 - $ python setup.py install - - If you prefer to know what you're doing, read on. - - - 1. Prerequisites. - - If you need any of the features described below, make sure you - have the necessary libraries before building PIL. - - feature library - ----------------------------------------------------------------- - JPEG support libjpeg (6a or 6b) - - http://www.ijg.org - http://www.ijg.org/files/jpegsrc.v6b.tar.gz - ftp://ftp.uu.net/graphics/jpeg/ - - PNG support zlib (1.2.3 or later is recommended) - - http://www.gzip.org/zlib/ - - OpenType/TrueType freetype2 (2.3.9 or later is recommended) - support - http://www.freetype.org - http://freetype.sourceforge.net - - CMS support littleCMS (1.1.5 or later is recommended) - support - http://www.littlecms.com/ - - If you have a recent Linux version, the libraries provided with the - operating system usually work just fine. If some library is - missing, installing a prebuilt version (jpeg-devel, zlib-devel, - etc) is usually easier than building from source. For example, for - Ubuntu 9.10 (karmic), you can install the following libraries: - - sudo apt-get install libjpeg62-dev - sudo apt-get install zlib1g-dev - sudo apt-get install libfreetype6-dev - sudo apt-get install liblcms1-dev - - If you're using Mac OS X, you can use the 'fink' tool to install - missing libraries (also see the Mac OS X section below). - - Similar tools are available for many other platforms. - - - 2. To build under Python 1.5.2, you need to install the stand-alone - version of the distutils library: - - http://www.python.org/sigs/distutils-sig/download.html - - You can fetch distutils 1.0.2 from the Python source repository: - - svn export http://svn.python.org/projects/python/tags/Distutils-1_0_2/Lib/distutils/ - - For newer releases, the distutils library is included in the - Python standard library. - - NOTE: Version 1.1.7 is not fully compatible with 1.5.2. Some - more recent additions to the library may not work, but the core - functionality is available. - - - 3. If you didn't build Python from sources, make sure you have - Python's build support files on your machine. If you've down- - loaded a prebuilt package (e.g. a Linux RPM), you probably - need additional developer packages. Look for packages named - "python-dev", "python-devel", or similar. For example, for - Ubuntu 9.10 (karmic), use the following command: - - sudo apt-get install python-dev - - - 4. When you have everything you need, unpack the PIL distribution - (the file Imaging-1.1.7.tar.gz) in a suitable work directory: - - $ cd MyExtensions # example - $ gunzip Imaging-1.1.7.tar.gz - $ tar xvf Imaging-1.1.7.tar - - - 5. Build the library. We recommend that you do an in-place build, - and run the self test before installing. - - $ cd Imaging-1.1.7 - $ python setup.py build_ext -i - $ python selftest.py - - During the build process, the setup.py will display a summary - report that lists what external components it found. The self- - test will display a similar report, with what external components - the tests found in the actual build files: - - ---------------------------------------------------------------- - PIL 1.1.7 SETUP SUMMARY - ---------------------------------------------------------------- - *** TKINTER support not available (Tcl/Tk 8.5 libraries needed) - --- JPEG support available - --- ZLIB (PNG/ZIP) support available - --- FREETYPE support available - ---------------------------------------------------------------- - - Make sure that the optional components you need are included. - - If the build script won't find a given component, you can edit the - setup.py file and set the appropriate ROOT variable. For details, - see instructions in the file. - - If the build script finds the component, but the tests cannot - identify it, try rebuilding *all* modules: - - $ python setup.py clean - $ python setup.py build_ext -i - - - 6. If the setup.py and selftest.py commands finish without any - errors, you're ready to install the library: - - $ python setup.py install - - (depending on how Python has been installed on your machine, - you might have to log in as a superuser to run the 'install' - command, or use the 'sudo' command to run 'install'.) - - - -------------------------------------------------------------------- - Additional notes for Mac OS X - -------------------------------------------------------------------- - - On Mac OS X you will usually install additional software such as - libjpeg or freetype with the "fink" tool, and then it ends up in - "/sw". If you have installed the libraries elsewhere, you may have - to tweak the "setup.py" file before building. - - - -------------------------------------------------------------------- - Additional notes for Windows - -------------------------------------------------------------------- - - On Windows, you need to tweak the ROOT settings in the "setup.py" - file, to make it find the external libraries. See comments in the - file for details. - - Make sure to build PIL and the external libraries with the same - runtime linking options as was used for the Python interpreter - (usually /MD, under Visual Studio). - - - Note that most Python distributions for Windows include libraries - compiled for Microsoft Visual Studio. You can get the free Express - edition of Visual Studio from: - - http://www.microsoft.com/Express/ - - To build extensions using other tool chains, see the "Using - non-Microsoft compilers on Windows" section in the distutils handbook: - - http://www.python.org/doc/current/inst/non-ms-compilers.html - - For additional information on how to build extensions using the - popular MinGW compiler, see: - - http://mingw.org (compiler) - http://sebsauvage.net/python/mingw.html (build instructions) - http://sourceforge.net/projects/gnuwin32 (prebuilt libraries) diff --git a/docs/about.rst b/docs/about.rst new file mode 100644 index 000000000..dfd88b605 --- /dev/null +++ b/docs/about.rst @@ -0,0 +1,47 @@ +About Pillow +============ + +Goals +----- + +The fork authors' goal is to foster active development of PIL through: + +- Continuous integration testing via `Travis CI`_ +- Publicized development activity on `GitHub`_ +- Regular releases to the `Python Package Index`_ +- Solicitation for community contributions and involvement on `Image-SIG`_ + +.. _Travis CI: https://travis-ci.org/python-imaging/Pillow +.. _GitHub: https://github.com/python-imaging/Pillow +.. _Python Package Index: https://pypi.python.org/pypi/Pillow +.. _Image-SIG: http://mail.python.org/mailman/listinfo/image-sig + +Why a fork? +----------- + +PIL is not setuptools compatible. Please see `this Image-SIG post`_ for a more +detailed explanation. Also, PIL's current bi-yearly (or greater) release +schedule is too infrequent to accomodate the large number and frequency of +issues reported. + +.. _this Image-SIG post: https://mail.python.org/pipermail/image-sig/2010-August/006480.html + +What about the official PIL? +---------------------------- + +.. note:: + + Prior to Pillow 2.0.0, very few image code changes were made. Pillow 2.0.0 + added Python 3 support and includes many bug fixes from many contributors. + +As more time passes since the last PIL release, the likelyhood of a new PIL +release decreases. However, we've yet to hear an official "PIL is dead" +announcement. So if you still want to support PIL, please +`report issues here first`_, then +`open the corresponding Pillow tickets here`_. + +.. _report issues here first: https://bitbucket.org/effbot/pil-2009-raclette/issues + +.. _open the corresponding Pillow tickets here: https://github.com/python-imaging/Pillow/issues + +Please provide a link to the PIL ticket so we can track the issue(s) upstream. diff --git a/docs/guides.rst b/docs/guides.rst new file mode 100644 index 000000000..926d3cfcc --- /dev/null +++ b/docs/guides.rst @@ -0,0 +1,10 @@ +Guides +====== + +.. toctree:: + :maxdepth: 2 + + handbook/overview + handbook/tutorial + handbook/concepts + porting-pil-to-pillow diff --git a/docs/handbook/guides.rst b/docs/handbook/guides.rst deleted file mode 100644 index edec8bea8..000000000 --- a/docs/handbook/guides.rst +++ /dev/null @@ -1,9 +0,0 @@ -Guides -====== - -.. toctree:: - :maxdepth: 2 - - overview - tutorial - concepts diff --git a/docs/index.rst b/docs/index.rst index 62a1ff9c9..96259e436 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -4,23 +4,62 @@ Pillow: a modern fork of PIL Pillow is the "friendly" PIL fork by Alex Clark and Contributors. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. -Pillow >= 2.0.0 supports Python versions 2.6, 2.7, 3.2, 3.3. +.. image:: https://travis-ci.org/python-imaging/Pillow.png + :target: https://travis-ci.org/python-imaging/Pillow -Pillow < 2.0.0 supports Python versions 2.4, 2.5, 2.6, 2.7. +.. image:: https://pypip.in/v/Pillow/badge.png + :target: https://pypi.python.org/pypi/Pillow/ + :alt: Latest PyPI version + +.. image:: https://pypip.in/d/Pillow/badge.png + :target: https://pypi.python.org/pypi/Pillow/ + :alt: Number of PyPI downloads + +.. note:: Pillow >= 2.0.0 supports Python versions 2.6, 2.7, 3.2, 3.3. + +.. note:: Pillow < 2.0.0 supports Python versions 2.4, 2.5, 2.6, 2.7. + +For full compatibility, you'll want to read the complete :doc:`installation +instructions `. -For general information including installation instructions, see `README.rst`_. If you can't find the information you need, try the old `PIL Handbook`_, but be aware that it was last updated for PIL 1.1.5. -.. _README.rst: https://github.com/python-imaging/Pillow/blob/master/README.rst .. _PIL Handbook: http://effbot.org/imagingbook/pil-index.htm .. toctree:: :maxdepth: 2 - handbook/guides.rst - handbook/appendices.rst + installation + about + guides + handbook/appendices PIL + original-readme + +Support Pillow! +=============== + +PIL needs you! Please help us maintain the Python Imaging Library here: + +- `GitHub `_ +- `Freenode `_ +- `Image-SIG `_ + +Financial +--------- + +Pillow is a volunteer effort led by Alex Clark. If you can't help with +development, please help us financially; your assistance is very much needed +and appreciated! + +.. note:: Contributors: please add your name and donation preference here. + +======================================= ======================================= +**Developer** **Preference** +======================================= ======================================= +Alex Clark (fork author) http://gittip.com/aclark4life +======================================= ======================================= Indices and tables ================== diff --git a/docs/installation.rst b/docs/installation.rst new file mode 100644 index 000000000..eac9fb97d --- /dev/null +++ b/docs/installation.rst @@ -0,0 +1,191 @@ +Installation +============ + +.. warning:: PIL and Pillow currently cannot co-exist in the same environment. + If you want to use Pillow, please remove PIL first. + +Simple installation +------------------- + +.. note:: + + The following instructions will install Pillow with support for most formats. + See :ref:`external-libraries` for the features you would gain by installing + the external libraries first. This page probably also include specific + instructions for your platform. + +You can install Pillow with ``pip``:: + + $ pip install Pillow + +Or :command:`easy_install` (for installing `Python Eggs +`_, as pip does not support +them):: + + $ easy_install Pillow + +Or download the `compressed archive from PyPI`_, extract it, and inside it +run:: + + $ python setup.py install + +.. _compressed archive from PyPI: https://pypi.python.org/pypi/Pillow + +.. _external-libraries: + +External libraries +------------------ + +Many of Pillow's features require external libraries: + +* **libjpeg** provides JPEG functionality. + + * Pillow has been tested with libjpeg versions **6b**, **8**, and **9** + +* **zlib** provides access to compressed PNGs + +* **libtiff** provides group4 tiff functionality + + * Pillow has been tested with libtiff versions **3.x** and **4.0** + +* **libfreetype** provides type related services + +* **littlecms** provides color management + +* **libwebp** provides the Webp format. + + * Pillow has been tested with version **0.1.3**, which does not read transparent webp files. Version **0.3.0** supports transparency. + +* **tcl/tk** provides support for tkinter bitmap and photo images. + +If the prerequisites are installed in the standard library locations for your +machine (e.g. :file:`/usr` or :file:`/usr/local`), no additional configuration +should be required. If they are installed in a non-standard location, you may +need to configure setuptools to use those locations (i.e. by editing +:file:`setup.py` and/or :file:`setup.cfg`). Once you have installed the +prerequisites, run:: + + $ pip install Pillow + +Linux installation +------------------ + +.. note:: + + Fedora, Debian/Ubuntu, and ArchLinux include Pillow (instead of PIL) with + their distributions. Consider using those instead of installing manually. + +.. note:: + + You *do not* need to install all of the external libraries to get Pillow's + basics to work. + +**We do not provide binaries for Linux.** If you didn't build Python from +source, make sure you have Python's development libraries installed. In Debian +or Ubuntu:: + + $ sudo apt-get install python-dev python-setuptools + +Or for Python 3:: + + $ sudo apt-get install python3-dev python3-setuptools + +Prerequisites are installed on **Ubuntu 10.04 LTS** with:: + + $ sudo apt-get install libtiff4-dev libjpeg62-dev zlib1g-dev \ + libfreetype6-dev liblcms1-dev tcl8.5-dev tk8.5-dev + +Prerequisites are installed with on **Ubuntu 12.04 LTS** or **Raspian Wheezy +7.0** with:: + + $ sudo apt-get install libtiff4-dev libjpeg8-dev zlib1g-dev \ + libfreetype6-dev liblcms1-dev libwebp-dev tcl8.5-dev tk8.5-dev + +Mac OS X installation +--------------------- + +.. note:: + + You *do not* need to install all of the external libraries to get Pillow's + basics to work. + +**We do not provide binaries for OS X**, so you'll need XCode to install +Pillow. (XCode 4.2 on 10.6 will work with the Official Python binary +distribution. Otherwise, use whatever XCode you used to compile Python.) + +The easiest way to install the prerequisites is via `Homebrew +`_. After you install Homebrew, run:: + + $ brew install libtiff libjpeg webp littlecms + +If you've built your own Python, then you should be able to install Pillow +using:: + + $ pip install Pillow + +Windows installation +-------------------- + +We provide binaries for Windows in the form of Python Eggs and `Python Wheels +`_: + +Python Eggs +^^^^^^^^^^^ + +.. note:: + + :command:`pip` does not support Python Eggs; use :command:`easy_install` + instead. + +:: + + $ easy_install Pillow + +Python Wheels +^^^^^^^^^^^^^ + +.. Note:: Experimental. Requires setuptools >=0.8 and pip >=1.4.1 + +:: + + $ pip install --use-wheel Pillow + +Platform support +---------------- + +Current platform support for Pillow. Binary distributions are contributed for +each release on a volunteer basis, but the source should compile and run +everywhere platform support is listed. In general, we aim to support all +current versions of Linux, OS X, and Windows. + +.. note:: + + Contributors please test on your platform, edit this document, and send a + pull request. + ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +|**Operating system** |**Supported**|**Tested Python versions** |**Tested Pillow versions** |**Tested processors** | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| CentOS 6.3 |Yes | 2.7,3.3 | |x86 | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| Mac OS X 10.8 Mountain Lion |Yes | 2.6,2.7,3.2,3.3 | |x86-64 | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| Mac OS X 10.7 Lion |Yes | 2.6,2.7,3.2,3.3 | 2.2.0 |x86-64 | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| Redhat Linux 6 |Yes | 2.6 | |x86 | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| Ubuntu Linux 10.04 LTS |Yes | 2.6 | 2.2.0 |x86,x86-64 | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| Ubuntu Linux 12.04 LTS |Yes | 2.6,2.7,3.2,3.3,PyPy2.1 | 2.2.0 |x86,x86-64 | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| Raspian Wheezy |Yes | 2.7,3.2 | 2.2.0 |arm | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| Gentoo Linux |Yes | 2.7,3.2 | 2.1.0 |x86-64 | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| Windows 7 Pro |Yes | 2.7,3.2 | |x86 | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| Windows Server 2008 R2 Enterprise|Yes | 3.3 | |x86-64 | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| Windows 8 Pro |Yes | 2.6,2.7,3.2,3.3,3.4a3 | 2.2.0 |x86,x86-64 | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ + diff --git a/docs/original-readme.rst b/docs/original-readme.rst new file mode 100644 index 000000000..73b941f37 --- /dev/null +++ b/docs/original-readme.rst @@ -0,0 +1,306 @@ +Original PIL README +=================== + +What follows is the original PIL 1.1.7 README file contents. + +:: + + The Python Imaging Library + $Id$ + + Release 1.1.7 (November 15, 2009) + + ==================================================================== + The Python Imaging Library 1.1.7 + ==================================================================== + + Contents + -------- + + + Introduction + + Support Options + - Commercial support + - Free support + + Software License + + Build instructions (all platforms) + - Additional notes for Mac OS X + - Additional notes for Windows + + -------------------------------------------------------------------- + Introduction + -------------------------------------------------------------------- + + The Python Imaging Library (PIL) adds image processing capabilities + to your Python environment. This library provides extensive file + format support, an efficient internal representation, and powerful + image processing capabilities. + + This source kit has been built and tested with Python 2.0 and newer, + on Windows, Mac OS X, and major Unix platforms. Large parts of the + library also work on 1.5.2 and 1.6. + + The main distribution site for this software is: + + http://www.pythonware.com/products/pil/ + + That site also contains information about free and commercial support + options, PIL add-ons, answers to frequently asked questions, and more. + + + Development versions (alphas, betas) are available here: + + http://effbot.org/downloads/ + + + The PIL handbook is not included in this distribution; to get the + latest version, check: + + http://www.pythonware.com/library/ + http://effbot.org/books/imagingbook/ (drafts) + + + For installation and licensing details, see below. + + + -------------------------------------------------------------------- + Support Options + -------------------------------------------------------------------- + + + Commercial Support + + Secret Labs (PythonWare) offers support contracts for companies using + the Python Imaging Library in commercial applications, and in mission- + critical environments. The support contract includes technical support, + bug fixes, extensions to the PIL library, sample applications, and more. + + For the full story, check: + + http://www.pythonware.com/products/pil/support.htm + + + + Free Support + + For support and general questions on the Python Imaging Library, send + e-mail to the Image SIG mailing list: + + image-sig@python.org + + You can join the Image SIG by sending a mail to: + + image-sig-request@python.org + + Put "subscribe" in the message body to automatically subscribe to the + list, or "help" to get additional information. Alternatively, you can + send your questions to the Python mailing list, python-list@python.org, + or post them to the newsgroup comp.lang.python. DO NOT SEND SUPPORT + QUESTIONS TO PYTHONWARE ADDRESSES. + + + -------------------------------------------------------------------- + Software License + -------------------------------------------------------------------- + + The Python Imaging Library is + + Copyright (c) 1997-2009 by Secret Labs AB + Copyright (c) 1995-2009 by Fredrik Lundh + + By obtaining, using, and/or copying this software and/or its + associated documentation, you agree that you have read, understood, + and will comply with the following terms and conditions: + + Permission to use, copy, modify, and distribute this software and its + associated documentation for any purpose and without fee is hereby + granted, provided that the above copyright notice appears in all + copies, and that both that copyright notice and this permission notice + appear in supporting documentation, and that the name of Secret Labs + AB or the author not be used in advertising or publicity pertaining to + distribution of the software without specific, written prior + permission. + + SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO + THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR + ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + + -------------------------------------------------------------------- + Build instructions (all platforms) + -------------------------------------------------------------------- + + For a list of changes in this release, see the CHANGES document. + + 0. If you're in a hurry, try this: + + $ tar xvfz Imaging-1.1.7.tar.gz + $ cd Imaging-1.1.7 + $ python setup.py install + + If you prefer to know what you're doing, read on. + + + 1. Prerequisites. + + If you need any of the features described below, make sure you + have the necessary libraries before building PIL. + + feature library + ----------------------------------------------------------------- + JPEG support libjpeg (6a or 6b) + + http://www.ijg.org + http://www.ijg.org/files/jpegsrc.v6b.tar.gz + ftp://ftp.uu.net/graphics/jpeg/ + + PNG support zlib (1.2.3 or later is recommended) + + http://www.gzip.org/zlib/ + + OpenType/TrueType freetype2 (2.3.9 or later is recommended) + support + http://www.freetype.org + http://freetype.sourceforge.net + + CMS support littleCMS (1.1.5 or later is recommended) + support + http://www.littlecms.com/ + + If you have a recent Linux version, the libraries provided with the + operating system usually work just fine. If some library is + missing, installing a prebuilt version (jpeg-devel, zlib-devel, + etc) is usually easier than building from source. For example, for + Ubuntu 9.10 (karmic), you can install the following libraries: + + sudo apt-get install libjpeg62-dev + sudo apt-get install zlib1g-dev + sudo apt-get install libfreetype6-dev + sudo apt-get install liblcms1-dev + + If you're using Mac OS X, you can use the 'fink' tool to install + missing libraries (also see the Mac OS X section below). + + Similar tools are available for many other platforms. + + + 2. To build under Python 1.5.2, you need to install the stand-alone + version of the distutils library: + + http://www.python.org/sigs/distutils-sig/download.html + + You can fetch distutils 1.0.2 from the Python source repository: + + svn export http://svn.python.org/projects/python/tags/Distutils-1_0_2/Lib/distutils/ + + For newer releases, the distutils library is included in the + Python standard library. + + NOTE: Version 1.1.7 is not fully compatible with 1.5.2. Some + more recent additions to the library may not work, but the core + functionality is available. + + + 3. If you didn't build Python from sources, make sure you have + Python's build support files on your machine. If you've down- + loaded a prebuilt package (e.g. a Linux RPM), you probably + need additional developer packages. Look for packages named + "python-dev", "python-devel", or similar. For example, for + Ubuntu 9.10 (karmic), use the following command: + + sudo apt-get install python-dev + + + 4. When you have everything you need, unpack the PIL distribution + (the file Imaging-1.1.7.tar.gz) in a suitable work directory: + + $ cd MyExtensions # example + $ gunzip Imaging-1.1.7.tar.gz + $ tar xvf Imaging-1.1.7.tar + + + 5. Build the library. We recommend that you do an in-place build, + and run the self test before installing. + + $ cd Imaging-1.1.7 + $ python setup.py build_ext -i + $ python selftest.py + + During the build process, the setup.py will display a summary + report that lists what external components it found. The self- + test will display a similar report, with what external components + the tests found in the actual build files: + + ---------------------------------------------------------------- + PIL 1.1.7 SETUP SUMMARY + ---------------------------------------------------------------- + *** TKINTER support not available (Tcl/Tk 8.5 libraries needed) + --- JPEG support available + --- ZLIB (PNG/ZIP) support available + --- FREETYPE support available + ---------------------------------------------------------------- + + Make sure that the optional components you need are included. + + If the build script won't find a given component, you can edit the + setup.py file and set the appropriate ROOT variable. For details, + see instructions in the file. + + If the build script finds the component, but the tests cannot + identify it, try rebuilding *all* modules: + + $ python setup.py clean + $ python setup.py build_ext -i + + + 6. If the setup.py and selftest.py commands finish without any + errors, you're ready to install the library: + + $ python setup.py install + + (depending on how Python has been installed on your machine, + you might have to log in as a superuser to run the 'install' + command, or use the 'sudo' command to run 'install'.) + + + -------------------------------------------------------------------- + Additional notes for Mac OS X + -------------------------------------------------------------------- + + On Mac OS X you will usually install additional software such as + libjpeg or freetype with the "fink" tool, and then it ends up in + "/sw". If you have installed the libraries elsewhere, you may have + to tweak the "setup.py" file before building. + + + -------------------------------------------------------------------- + Additional notes for Windows + -------------------------------------------------------------------- + + On Windows, you need to tweak the ROOT settings in the "setup.py" + file, to make it find the external libraries. See comments in the + file for details. + + Make sure to build PIL and the external libraries with the same + runtime linking options as was used for the Python interpreter + (usually /MD, under Visual Studio). + + + Note that most Python distributions for Windows include libraries + compiled for Microsoft Visual Studio. You can get the free Express + edition of Visual Studio from: + + http://www.microsoft.com/Express/ + + To build extensions using other tool chains, see the "Using + non-Microsoft compilers on Windows" section in the distutils handbook: + + http://www.python.org/doc/current/inst/non-ms-compilers.html + + For additional information on how to build extensions using the + popular MinGW compiler, see: + + http://mingw.org (compiler) + http://sebsauvage.net/python/mingw.html (build instructions) + http://sourceforge.net/projects/gnuwin32 (prebuilt libraries) diff --git a/docs/porting-pil-to-pillow.rst b/docs/porting-pil-to-pillow.rst new file mode 100644 index 000000000..93bc672af --- /dev/null +++ b/docs/porting-pil-to-pillow.rst @@ -0,0 +1,17 @@ +Porting existing PIL-based code to Pillow +========================================= + +Pillow is a functional drop-in replacement for the Python Imaging Library. To +run your existing PIL-compatible code with Pillow, it needs to be modified to +import the ``Imaging`` module from the ``PIL`` namespace *instead* of the +global namespace. Change this:: + + import Image + +to this:: + + from PIL import Image + +The :py:mod:`_imaging` module has been moved. You can now import it like this:: + + from PIL.Image import core as _imaging From ea83ab9c584a8c4f5240e1ecae6fee524b094bf1 Mon Sep 17 00:00:00 2001 From: Steve Johnson Date: Tue, 8 Oct 2013 17:15:50 -0700 Subject: [PATCH 022/107] Clarify contributor updates --- README.rst | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/README.rst b/README.rst index 3b5e56d58..7d947a7f0 100644 --- a/README.rst +++ b/README.rst @@ -75,13 +75,16 @@ Financial Pillow is a volunteer effort led by Alex Clark. If you can't help with development, please help us financially; your assistance is very much needed and appreciated! -.. Note:: Contributors: please add your name and donation preference here. +.. note:: -+--------------------------------------+---------------------------------------+ -| **Developer** | **Preference** | -+--------------------------------------+---------------------------------------+ -| Alex Clark (fork author) | http://gittip.com/aclark4life | -+--------------------------------------+---------------------------------------+ + Contributors: please add your name and donation preference here, as well as + at the bottom of docs/index.rst. + +======================================= ======================================= +**Developer** **Preference** +======================================= ======================================= +Alex Clark (fork author) http://gittip.com/aclark4life +======================================= ======================================= Developer Notes --------------- From 9116813e70188ea938c7727be42f6156595cc191 Mon Sep 17 00:00:00 2001 From: Steve Johnson Date: Tue, 8 Oct 2013 17:15:58 -0700 Subject: [PATCH 023/107] Remove PIL porting guide from Readme --- README.rst | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/README.rst b/README.rst index 7d947a7f0..9e3b5fc7c 100644 --- a/README.rst +++ b/README.rst @@ -194,21 +194,3 @@ Platform support Current platform support for Pillow is documented here: http://pillow.readthedocs.org/en/latest/installation.html#platform-support - -Port existing PIL-based code to Pillow -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Pillow is a functional drop-in replacement for the Python Imaging Library. To run your existing PIL-compatible code with Pillow, it needs to be modified to import the ``Imaging`` module from the ``PIL`` namespace *instead* of the global namespace. I.e. change:: - - import Image - -to:: - - from PIL import Image - -.. Note:: If your code imports from ``_imaging``, it will no longer work. - -The preferred, future proof method of importing the private ``_imaging`` module is:: - - from PIL import Image - _imaging = Image.core From cdaf584b63f00a3ace24fca4c7093a1f6dc6b581 Mon Sep 17 00:00:00 2001 From: Steve Johnson Date: Tue, 8 Oct 2013 17:16:45 -0700 Subject: [PATCH 024/107] Fix some readme formatting --- README.rst | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 9e3b5fc7c..ce566acbb 100644 --- a/README.rst +++ b/README.rst @@ -75,10 +75,7 @@ Financial Pillow is a volunteer effort led by Alex Clark. If you can't help with development, please help us financially; your assistance is very much needed and appreciated! -.. note:: - - Contributors: please add your name and donation preference here, as well as - at the bottom of docs/index.rst. +.. Note:: Contributors: please add your name and donation preference here, as well as at the bottom of docs/index.rst. ======================================= ======================================= **Developer** **Preference** From 2c5073e2e10f2877ac383eb88342db4a7d675c3c Mon Sep 17 00:00:00 2001 From: Steve Johnson Date: Tue, 8 Oct 2013 17:17:21 -0700 Subject: [PATCH 025/107] Bump required pillow version in docs/requirements.txt --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index af38b4b69..d825a5fcc 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,7 +1,7 @@ # requirements for working on docs # install pillow from master if you're into that, but RtD needs this -pillow>=2.2.0 +pillow>=2.2.1 Jinja2==2.7.1 MarkupSafe==0.18 From f218b8d68fdcc9bb2d67774f4b25c62871185f1b Mon Sep 17 00:00:00 2001 From: Steve Johnson Date: Tue, 8 Oct 2013 17:19:49 -0700 Subject: [PATCH 026/107] Link to PyPI from index.rst; formatting fixes --- docs/index.rst | 3 ++- docs/installation.rst | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 96259e436..352bc4ed5 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -23,7 +23,8 @@ For full compatibility, you'll want to read the complete :doc:`installation instructions `. If you can't find the information you need, try the old `PIL Handbook`_, but be -aware that it was last updated for PIL 1.1.5. +aware that it was last updated for PIL 1.1.5. You can download archives and old +versions from `PyPI `_. .. _PIL Handbook: http://effbot.org/imagingbook/pil-index.htm diff --git a/docs/installation.rst b/docs/installation.rst index eac9fb97d..0a9934bf9 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -14,13 +14,13 @@ Simple installation the external libraries first. This page probably also include specific instructions for your platform. -You can install Pillow with ``pip``:: +You can install Pillow with :command:`pip`:: $ pip install Pillow Or :command:`easy_install` (for installing `Python Eggs -`_, as pip does not support -them):: +`_, as :command:`pip` does +not support them):: $ easy_install Pillow From ea8b1ed32144e2951cc5be39624d8f9e4a44360a Mon Sep 17 00:00:00 2001 From: Steve Johnson Date: Tue, 8 Oct 2013 17:23:39 -0700 Subject: [PATCH 027/107] Add global ToC back to index page --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index bb1008063..987b6dbb3 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -48,7 +48,7 @@ html_theme_options = {} html_sidebars = { '**': ['localtoc.html', 'sourcelink.html', 'sidebarhelp.html', 'searchbox.html'], - 'index': ['sidebarhelp.html', 'searchbox.html'], + 'index': ['globaltoc.html', 'sidebarhelp.html', 'searchbox.html'], } # Output file base name for HTML help builder. From 7bcf0a17a43547672504fa10496333aa63be6e2d Mon Sep 17 00:00:00 2001 From: Steve Johnson Date: Tue, 8 Oct 2013 17:24:42 -0700 Subject: [PATCH 028/107] Add myself to CONTRIBUTORS.rst --- CONTRIBUTORS.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.rst b/CONTRIBUTORS.rst index 778be7151..56efba356 100644 --- a/CONTRIBUTORS.rst +++ b/CONTRIBUTORS.rst @@ -27,6 +27,7 @@ Contributors (Pillow) - Sandro Mani - Simon Law - Stéphane Klein +- Steve Johnson - Takeshi KOMIYA - Tom Gross - Tom Payne From c3aaa9e9c4cb37ab8e195c79a071a7a1a57ee0ab Mon Sep 17 00:00:00 2001 From: Steve Johnson Date: Tue, 8 Oct 2013 17:28:09 -0700 Subject: [PATCH 029/107] Link to Github --- docs/index.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index 352bc4ed5..9dad0ec4f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -24,7 +24,8 @@ instructions `. If you can't find the information you need, try the old `PIL Handbook`_, but be aware that it was last updated for PIL 1.1.5. You can download archives and old -versions from `PyPI `_. +versions from `PyPI `_. You can get the +source and contribute at https://github.com/python-imaging/Pillow. .. _PIL Handbook: http://effbot.org/imagingbook/pil-index.htm From 473f5d8fc719e0e9dba6331c2117c29546508ea9 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 9 Oct 2013 21:44:05 -0700 Subject: [PATCH 030/107] Changed ''.split to tuples --- setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 6bf002fab..4698aef0b 100644 --- a/setup.py +++ b/setup.py @@ -215,7 +215,7 @@ class pil_build_ext(build_ext): # FIXME: check /opt/stuff directories here? # respect CFLAGS/LDFLAGS - for k in 'CFLAGS LDFLAGS'.split(): + for k in ('CFLAGS', 'LDFLAGS'): if k in os.environ: for match in re.finditer(r'-I([^\s]+)', os.environ[k]): _add_directory(include_dirs, match.group(1)) @@ -223,12 +223,12 @@ class pil_build_ext(build_ext): _add_directory(library_dirs, match.group(1)) # include, rpath, if set as environment variables: - for k in 'C_INCLUDE_PATH INCLUDE'.split(): + for k in ('C_INCLUDE_PATH', 'INCLUDE'): if k in os.environ: for d in os.environ[k].split(os.path.pathsep): _add_directory(include_dirs, d) - for k in 'LD_RUN_PATH LIBRARY_PATH LIB'.split(): + for k in ('LD_RUN_PATH', 'LIBRARY_PATH', 'LIB'): if k in os.environ: for d in os.environ[k].split(os.path.pathsep): _add_directory(library_dirs, d) From 8ae54f2b407a7aa608c883a550328e7485a01d57 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Thu, 10 Oct 2013 05:10:12 -0400 Subject: [PATCH 031/107] Add history [ci skip] --- CHANGES.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 1f10204e4..81503e5e3 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,8 @@ Changelog (Pillow) 2.3.0 (2014-01-01) ------------------ +- Respect CFLAGS/LDFLAGS when searching for headers/libs [iElectric] + - Port PIL Handbook tutorial and appendices [irksep] - Alpha Premultiplication support for transform and resize [wiredfool] From 8f81298a5abeef0fd8eac12066d3f2406fc1eff9 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Thu, 10 Oct 2013 05:24:10 -0400 Subject: [PATCH 032/107] Add history [ci skip] --- CHANGES.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 81503e5e3..7b6443535 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,8 @@ Changelog (Pillow) 2.3.0 (2014-01-01) ------------------ +- Move or copy content from README.rst to docs/ [irksep] + - Respect CFLAGS/LDFLAGS when searching for headers/libs [iElectric] - Port PIL Handbook tutorial and appendices [irksep] From ce041fd1995fe95de86804c83d100ed7d8ecdb3b Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 10 Oct 2013 22:12:45 -0700 Subject: [PATCH 033/107] moving string functions into python, py27 and py32 now really work the same --- PIL/ImageCms.py | 34 +++++++++++++++++++++-------- _imagingcms.c | 58 ------------------------------------------------- 2 files changed, 25 insertions(+), 67 deletions(-) diff --git a/PIL/ImageCms.py b/PIL/ImageCms.py index 3bc782d2d..d9c69e909 100644 --- a/PIL/ImageCms.py +++ b/PIL/ImageCms.py @@ -153,8 +153,8 @@ class ImageCmsProfile: self.profile = profile self.filename = filename if profile: - self.product_name = profile.product_name - self.product_info = profile.product_info + self.product_name = None #profile.product_name + self.product_info = None #profile.product_info else: self.product_name = None self.product_info = None @@ -599,12 +599,19 @@ def getProfileName(profile): # add an extra newline to preserve pyCMS compatibility if not isinstance(profile, ImageCmsProfile): profile = ImageCmsProfile(profile) - ## print ("get profile name") - ## print ("\n".join([profile.profile.product_model, - ## profile.profile.product_description, - ## profile.profile.product_manufacturer, - ## profile.profile.product_copyright])) - return profile.profile.product_name + "\n" + # do it in python, not c. + # // name was "%s - %s" (model, manufacturer) || Description , + # // but if the Model and Manufacturer were the same or the model + # // was long, Just the model, in 1.x + model = profile.profile.product_model + manufacturer = profile.profile.product_manufacturer + + if not (model or manufacturer): + return profile.profile.product_description+"\n" + if not manufacturer or len(model) > 30: + return model + "\n" + return "%s - %s\n" % (model, manufacturer) + except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) @@ -632,7 +639,16 @@ def getProfileInfo(profile): if not isinstance(profile, ImageCmsProfile): profile = ImageCmsProfile(profile) # add an extra newline to preserve pyCMS compatibility - return profile.product_info + "\n" + # Python, not C. the white point bits weren't working well, so skipping. + # // info was description \r\n\r\n copyright \r\n\r\n K007 tag \r\n\r\n whitepoint + description = profile.profile.product_description + cpright = profile.profile.product_copyright + arr = [] + for elt in (description, cpright): + if elt: + arr.append(elt) + return "\r\n\r\n".join(arr)+"\r\n\r\n" + except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) diff --git a/_imagingcms.c b/_imagingcms.c index f4c97e9fe..20d740a08 100644 --- a/_imagingcms.c +++ b/_imagingcms.c @@ -524,28 +524,6 @@ _profile_getattr(CmsProfileObject* self, cmsInfoType field) return PyUnicode_FromString(""); } -static PyObject* -cms_profile_getattr_product_name(CmsProfileObject* self, void* closure) -{ - // name was "%s - %s" (model, manufacturer) || Description , - // but if the Model and Manufacturer were the same or the model - // was long, Just the model, in 1.x - PyObject *model = _profile_getattr(self, cmsInfoModel); - PyObject *manufacturer = _profile_getattr(self, cmsInfoManufacturer); - PyObject *result; - - if (!PyUnicode_GetSize(model) && !PyUnicode_GetSize(manufacturer)){ - return _profile_getattr(self, cmsInfoDescription); - } - if (!PyUnicode_GetSize(manufacturer) || PyUnicode_GetSize(model)> 30){ - return model; - } - result = PyUnicode_Concat(model, - PyUnicode_FromString(" - ")); - result = PyUnicode_Concat(result,_profile_getattr(self, cmsInfoManufacturer)); - return result; -} - static PyObject* cms_profile_getattr_product_desc(CmsProfileObject* self, void* closure) { @@ -553,40 +531,6 @@ cms_profile_getattr_product_desc(CmsProfileObject* self, void* closure) return _profile_getattr(self, cmsInfoDescription); } -void _info_concat(PyObject **ret, PyObject *elt){ - if (PyUnicode_GetSize(elt)){ - *ret = PyUnicode_Concat(*ret, elt); - *ret = PyUnicode_Concat(*ret, PyUnicode_FromString("\r\n\r\n")); - } -} - -static PyObject* -cms_profile_getattr_product_info(CmsProfileObject* self, void* closure) -{ - // info was description \r\n\r\n copyright \r\n\r\n K007 tag \r\n\r\n whitepoint - PyObject *description = _profile_getattr(self, cmsInfoDescription); - PyObject *copyright = _profile_getattr(self, cmsInfoCopyright); - PyObject *ret = PyUnicode_FromString(""); - - _info_concat(&ret, description); - _info_concat(&ret, copyright); - - if (cmsIsTag(self->profile, cmsSigMediaWhitePointTag)){ - cmsCIEXYZ *WhitePt; - cmsCIExyY xyyWhitePt; - cmsFloat64Number tempK; - - WhitePt = (cmsCIEXYZ *) cmsReadTag(self->profile, cmsSigMediaWhitePointTag); - cmsXYZ2xyY(&xyyWhitePt, WhitePt); - if (cmsTempFromWhitePoint(&tempK, &xyyWhitePt)){ - char tempstr[10]; - snprintf(tempstr, 10, "%5.0f", tempK); - _info_concat(&ret, PyUnicode_FromFormat("White Point: %sK", tempstr)); - } - } - return ret; -} - /* use these four for the individual fields. */ static PyObject* @@ -633,9 +577,7 @@ cms_profile_getattr_color_space(CmsProfileObject* self, void* closure) /* FIXME: add more properties (creation_datetime etc) */ static struct PyGetSetDef cms_profile_getsetters[] = { - { "product_name", (getter) cms_profile_getattr_product_name }, { "product_desc", (getter) cms_profile_getattr_product_desc }, - { "product_info", (getter) cms_profile_getattr_product_info }, { "product_description", (getter) cms_profile_getattr_product_description }, { "product_manufacturer", (getter) cms_profile_getattr_product_manufacturer }, { "product_model", (getter) cms_profile_getattr_product_model }, From 9042f8c61f9727f4bb68256cdd6837349f812dac Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 10 Oct 2013 22:16:56 -0700 Subject: [PATCH 034/107] split tests, disable info difference tests --- Tests/test_imagecms.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index fec299ace..ad02ba836 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -39,44 +39,53 @@ def test_sanity(): i = ImageCms.applyTransform(lena(), t) assert_image(i, "RGB", (128, 128)) + # test PointTransform convenience API + im = lena().point(t) + +def test_name(): # get profile information for file assert_equal(ImageCms.getProfileName(SRGB).strip(), 'IEC 61966-2.1 Default RGB colour space - sRGB') +def x_test_info(): assert_equal(ImageCms.getProfileInfo(SRGB).splitlines(), ['sRGB IEC61966-2.1', '', 'Copyright (c) 1998 Hewlett-Packard Company', '', 'WhitePoint : D65 (daylight)', '', 'Tests/icc/sRGB.icm']) + +def test_intent(): assert_equal(ImageCms.getDefaultIntent(SRGB), 0) assert_equal(ImageCms.isIntentSupported( SRGB, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, ImageCms.DIRECTION_INPUT), 1) +def test_profile_object(): # same, using profile object p = ImageCms.createProfile("sRGB") - assert_equal(ImageCms.getProfileName(p).strip(), - 'sRGB built-in - (lcms internal)') - assert_equal(ImageCms.getProfileInfo(p).splitlines(), - ['sRGB built-in', '', 'WhitePoint : D65 (daylight)', '', '']) +# assert_equal(ImageCms.getProfileName(p).strip(), +# 'sRGB built-in - (lcms internal)') +# assert_equal(ImageCms.getProfileInfo(p).splitlines(), +# ['sRGB built-in', '', 'WhitePoint : D65 (daylight)', '', '']) assert_equal(ImageCms.getDefaultIntent(p), 0) assert_equal(ImageCms.isIntentSupported( p, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, ImageCms.DIRECTION_INPUT), 1) +def test_extensions(): # extensions i = Image.open("Tests/images/rgb.jpg") p = ImageCms.getOpenProfile(BytesIO(i.info["icc_profile"])) assert_equal(ImageCms.getProfileName(p).strip(), 'IEC 61966-2.1 Default RGB colour space - sRGB') +def test_exceptions(): # the procedural pyCMS API uses PyCMSError for all sorts of errors assert_exception(ImageCms.PyCMSError, lambda: ImageCms.profileToProfile(lena(), "foo", "bar")) assert_exception(ImageCms.PyCMSError, lambda: ImageCms.buildTransform("foo", "bar", "RGB", "RGB")) assert_exception(ImageCms.PyCMSError, lambda: ImageCms.getProfileName(None)) assert_exception(ImageCms.PyCMSError, lambda: ImageCms.isIntentSupported(SRGB, None, None)) - # test PointTransform convenience API - im = lena().point(t) +def test_display_profile(): # try fetching the profile for the current display device assert_no_exception(lambda: ImageCms.get_display_profile()) From b506e2ad443103f609a4a1cde3cd1832d9662fd0 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 10 Oct 2013 22:42:27 -0700 Subject: [PATCH 035/107] Fixed ability to create LAB profiles with color temperatures --- PIL/ImageCms.py | 8 ++++---- Tests/test_imagecms.py | 15 +++++++++++++++ _imagingcms.c | 6 +++--- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/PIL/ImageCms.py b/PIL/ImageCms.py index d9c69e909..20ba6a11f 100644 --- a/PIL/ImageCms.py +++ b/PIL/ImageCms.py @@ -566,10 +566,10 @@ def createProfile(colorSpace, colorTemp=-1): raise PyCMSError("Color space not supported for on-the-fly profile creation (%s)" % colorSpace) if colorSpace == "LAB": - if isinstance(colorTemp, float): - colorTemp = int(colorTemp + 0.5) - if not isinstance(colorTemp, int): - raise PyCMSError("Color temperature must be a positive integer, \"%s\" not valid" % colorTemp) + try: + colorTemp = float(colorTemp) + except: + raise PyCMSError("Color temperature must be numeric, \"%s\" not valid" % colorTemp) try: return core.createProfile(colorSpace, colorTemp) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index ad02ba836..6c4e0e702 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -89,3 +89,18 @@ def test_exceptions(): def test_display_profile(): # try fetching the profile for the current display device assert_no_exception(lambda: ImageCms.get_display_profile()) + + +def test_lab_color_profile(): + pLab = ImageCms.createProfile("LAB", 5000) + pLab = ImageCms.createProfile("LAB", 6500) + +def test_lab_color(): + pLab = ImageCms.createProfile("LAB") + t = ImageCms.buildTransform(SRGB, pLab, "RGB", "RGB") + i = ImageCms.applyTransform(lena(), t) + assert_image(i, "RGB", (128, 128)) + + target = Image.open('Tests/images/lena.Lab.tif') + + assert_image_similar(i,target, 1) diff --git a/_imagingcms.c b/_imagingcms.c index 20d740a08..d489eed60 100644 --- a/_imagingcms.c +++ b/_imagingcms.c @@ -389,7 +389,7 @@ createProfile(PyObject *self, PyObject *args) char *sColorSpace; cmsHPROFILE hProfile; cmsFloat64Number dColorTemp = 0.0; - cmsCIExyY *whitePoint = NULL; + cmsCIExyY whitePoint; cmsBool result; if (!PyArg_ParseTuple(args, "s|d:createProfile", &sColorSpace, &dColorTemp)) @@ -397,12 +397,12 @@ createProfile(PyObject *self, PyObject *args) if (strcmp(sColorSpace, "LAB") == 0) { if (dColorTemp > 0.0) { - result = cmsWhitePointFromTemp(whitePoint, dColorTemp); + result = cmsWhitePointFromTemp(&whitePoint, dColorTemp); if (!result) { PyErr_SetString(PyExc_ValueError, "ERROR: Could not calculate white point from color temperature provided, must be float in degrees Kelvin"); return NULL; } - hProfile = cmsCreateLab2Profile(whitePoint); + hProfile = cmsCreateLab2Profile(&whitePoint); } else { hProfile = cmsCreateLab2Profile(NULL); } From ac38d91a2d2866c21b85fe20a2197043eea3d6b5 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Thu, 10 Oct 2013 23:02:33 -0700 Subject: [PATCH 036/107] Well, now I know _why_ it's failing. We don't have a concept for LAB color --- Tests/images/lena.Lab.tif | Bin 0 -> 63140 bytes Tests/test_imagecms.py | 2 ++ 2 files changed, 2 insertions(+) create mode 100644 Tests/images/lena.Lab.tif diff --git a/Tests/images/lena.Lab.tif b/Tests/images/lena.Lab.tif new file mode 100644 index 0000000000000000000000000000000000000000..335598210bd45ea8a2b31c98727e0f4630b11524 GIT binary patch literal 63140 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 i^u)vz04M Date: Thu, 10 Oct 2013 23:27:34 -0700 Subject: [PATCH 037/107] Added LAB mode, core dumped --- PIL/Image.py | 2 ++ Tests/test_imagecms.py | 6 +++--- _imagingcms.c | 3 +++ libImaging/Imaging.h | 1 + libImaging/Pack.c | 6 ++++++ libImaging/Storage.c | 8 +++++++- 6 files changed, 22 insertions(+), 4 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 56a4b7093..9c92bf98f 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -200,6 +200,7 @@ _MODEINFO = { "RGBA": ("RGB", "L", ("R", "G", "B", "A")), "CMYK": ("RGB", "L", ("C", "M", "Y", "K")), "YCbCr": ("RGB", "L", ("Y", "Cb", "Cr")), + "LAB": ("RGB", "L", ("L", "A", "B")), # Experimental modes include I;16, I;16L, I;16B, RGBa, BGR;15, and # BGR;24. Use these modes only if you know exactly what you're @@ -224,6 +225,7 @@ _MODE_CONV = { "RGBA": ('|u1', 4), "CMYK": ('|u1', 4), "YCbCr": ('|u1', 3), + "LAB": ('|u1', 3), "I;16": ('=u2', None), "I;16B": ('>u2', None), "I;16L": ('pixelsize = 4; im->linesize = xsize * 4; + } else if (strcmp(mode, "LAB") == 0) { + /* 24-bit color, luminance, + 2 color channels */ + im->bands = 3; + im->pixelsize = 4; + im->linesize = xsize * 4; + } else { free(im); - return (Imaging) ImagingError_ValueError("unrecognized mode"); + return (Imaging) ImagingError_ValueError("unrecognized mode"); } /* Setup image descriptor */ From b25e8c5d180926c34014bbd77aee745968c140db Mon Sep 17 00:00:00 2001 From: Stephen Johnson Date: Fri, 11 Oct 2013 22:18:28 -0700 Subject: [PATCH 038/107] Fully document PIL.Image from handbook --- docs/PIL.rst | 8 -- docs/index.rst | 2 +- docs/reference/Image.rst | 189 +++++++++++++++++++++++++++++++++++++++ docs/reference/index.rst | 8 ++ 4 files changed, 198 insertions(+), 9 deletions(-) create mode 100644 docs/reference/Image.rst create mode 100644 docs/reference/index.rst diff --git a/docs/PIL.rst b/docs/PIL.rst index 8918fce0e..2595c22ba 100644 --- a/docs/PIL.rst +++ b/docs/PIL.rst @@ -1,14 +1,6 @@ PIL Package =========== -:mod:`Image` Module -------------------- - -.. automodule:: PIL.Image - :members: - :undoc-members: - :show-inheritance: - :mod:`BdfFontFile` Module ------------------------- diff --git a/docs/index.rst b/docs/index.rst index 9dad0ec4f..70e8871b7 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -36,7 +36,7 @@ source and contribute at https://github.com/python-imaging/Pillow. about guides handbook/appendices - PIL + reference/index.rst original-readme Support Pillow! diff --git a/docs/reference/Image.rst b/docs/reference/Image.rst new file mode 100644 index 000000000..107c8a753 --- /dev/null +++ b/docs/reference/Image.rst @@ -0,0 +1,189 @@ +.. py:module:: PIL.Image +.. py:currentmodule:: PIL.Image + +:mod:`Image` Module +=================== + +The :py:mod:`~PIL.Image` module provides a class with the same name which is +used to represent a PIL image. The module also provides a number of factory +functions, including functions to load images from files, and to create new +images. + +Examples +-------- + +The following script loads an image, rotates it 45 degrees, and displays it +using an external viewer (usually xv on Unix, and the paint program on +Windows). + +Open, rotate, and display an image (using the default viewer) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: python + + from PIL import Image + im = Image.open("bride.jpg") + im.rotate(45).show() + +The following script creates nice 128x128 thumbnails of all JPEG images in the +current directory. + +Create thumbnails +^^^^^^^^^^^^^^^^^ + +.. code-block:: python + + from PIL import Image + import glob, os + + size = 128, 128 + + for infile in glob.glob("*.jpg"): + file, ext = os.path.splitext(infile) + im = Image.open(infile) + im.thumbnail(size, Image.ANTIALIAS) + im.save(file + ".thumbnail", "JPEG") + +Functions +--------- + +.. autofunction:: open + +Image processing +^^^^^^^^^^^^^^^^ + +.. autofunction:: alpha_composite +.. autofunction:: blend +.. autofunction:: composite +.. autofunction:: eval +.. autofunction:: merge + +Constructing images +^^^^^^^^^^^^^^^^^^^ + +.. autofunction:: new +.. autofunction:: fromarray +.. autofunction:: frombytes +.. autofunction:: fromstring +.. autofunction:: frombuffer + +Registering plugins +^^^^^^^^^^^^^^^^^^^ + +.. note:: + + These functions are for use by plugin authors. Application authors can + ignore them. + +.. autofunction:: register_open +.. autofunction:: register_mime +.. autofunction:: register_save +.. autofunction:: register_extension + +The Image Class +--------------- + +.. autoclass:: PIL.Image.Image + +An instance of the :py:class:`~PIL.Image.Image` class has the following +methods. Unless otherwise stated, all methods return a new instance of the +:py:class:`~PIL.Image.Image` class, holding the resulting image. + +.. automethod:: PIL.Image.Image.convert + +The following example converts an RGB image (linearly calibrated according to +ITU-R 709, using the D65 luminant) to the CIE XYZ color space: + +.. code-block:: python + + rgb2xyz = ( + 0.412453, 0.357580, 0.180423, 0, + 0.212671, 0.715160, 0.072169, 0, + 0.019334, 0.119193, 0.950227, 0 ) + out = im.convert("RGB", rgb2xyz) + +.. automethod:: PIL.Image.Image.copy +.. automethod:: PIL.Image.Image.crop +.. automethod:: PIL.Image.Image.draft +.. automethod:: PIL.Image.Image.filter +.. automethod:: PIL.Image.Image.getbands +.. automethod:: PIL.Image.Image.getbbox +.. automethod:: PIL.Image.Image.getcolors +.. automethod:: PIL.Image.Image.getdata +.. automethod:: PIL.Image.Image.getextrema +.. automethod:: PIL.Image.Image.getpixel +.. automethod:: PIL.Image.Image.histogram +.. automethod:: PIL.Image.Image.offset +.. automethod:: PIL.Image.Image.paste +.. automethod:: PIL.Image.Image.point +.. automethod:: PIL.Image.Image.putalpha +.. automethod:: PIL.Image.Image.putdata +.. automethod:: PIL.Image.Image.putpalette +.. automethod:: PIL.Image.Image.putpixel +.. automethod:: PIL.Image.Image.quantize +.. automethod:: PIL.Image.Image.resize +.. automethod:: PIL.Image.Image.rotate +.. automethod:: PIL.Image.Image.save +.. automethod:: PIL.Image.Image.seek +.. automethod:: PIL.Image.Image.show +.. automethod:: PIL.Image.Image.split +.. automethod:: PIL.Image.Image.tell +.. automethod:: PIL.Image.Image.thumbnail +.. automethod:: PIL.Image.Image.tobitmap +.. automethod:: PIL.Image.Image.tostring +.. automethod:: PIL.Image.Image.transform +.. automethod:: PIL.Image.Image.transpose +.. automethod:: PIL.Image.Image.verify + +.. automethod:: PIL.Image.Image.fromstring +.. deprecated:: 2.0 + +.. automethod:: PIL.Image.Image.load + +Attributes +---------- + +Instances of the :py:class:`Image` class have the following attributes: + +.. py:attribute:: format + + The file format of the source file. For images created by the library + itself (via a factory function, or by running a method on an existing + image), this attribute is set to ``None``. + + :type: :py:class:`string` or ``None`` + +.. py:attribute:: mode + + Image mode. This is a string specifying the pixel format used by the image. + Typical values are “1”, “L”, “RGB”, or “CMYK.” See + :doc:`../handbook/concepts` for a full list. + + :type: :py:class:`string` + +.. py:attribute:: size + + Image size, in pixels. The size is given as a 2-tuple (width, height). + + :type: ``(width, height)`` + +.. py:attribute:: palette + + Colour palette table, if any. If mode is “P”, this should be an instance of + the :py:class:`~PIL.ImagePalette.ImagePalette` class. Otherwise, it should + be set to ``None``. + + :type: :py:class:`~PIL.ImagePalette.ImagePalette` or ``None`` + +.. py:attribute:: info + + A dictionary holding data associated with the image. This dictionary is + used by file handlers to pass on various non-image information read from + the file. See documentation for the various file handlers for details. + + Most methods ignore the dictionary when returning new images; since the + keys are not standardized, it’s not possible for a method to know if the + operation affects the dictionary. If you need the information later on, + keep a reference to the info dictionary returned from the open method. + + :type: :py:class:`dict` diff --git a/docs/reference/index.rst b/docs/reference/index.rst new file mode 100644 index 000000000..3f6385c47 --- /dev/null +++ b/docs/reference/index.rst @@ -0,0 +1,8 @@ +Reference +========= + +.. toctree:: + :maxdepth: 2 + + Image + ../PIL From b533aa665e64a37c37ce0edeb16a598e38681735 Mon Sep 17 00:00:00 2001 From: Stephen Johnson Date: Fri, 11 Oct 2013 22:18:40 -0700 Subject: [PATCH 039/107] Fix many formatting mistakes in PIL.Image's docstrings --- PIL/Image.py | 250 ++++++++++++++++++++++++++------------------------- 1 file changed, 129 insertions(+), 121 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 58b85d5d2..6f5e37591 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -57,7 +57,7 @@ try: except ImportError as v: core = _imaging_not_installed() - # Explanations for ways that we know we might have an import error + # Explanations for ways that we know we might have an import error if str(v).startswith("Module use of python"): # The _imaging C module is present, but not compiled for # the right version (windows only). Print a warning, if @@ -81,8 +81,8 @@ except ImportError as v: "recompile PIL or build Python --with-wide-unicode. ", RuntimeWarning ) - # Fail here anyway. Don't let people run with a mostly broken Pillow. - raise + # Fail here anyway. Don't let people run with a mostly broken Pillow. + raise try: import builtins @@ -281,10 +281,10 @@ def getmodetype(mode): def getmodebandnames(mode): """ - Gets a list of individual band names. Given a mode, this function - returns a tuple containing the names of individual bands (use - :func:`PIL.Image.getmodetype` to get the mode used to store each individual - band. + Gets a list of individual band names. Given a mode, this function returns + a tuple containing the names of individual bands (use + :py:method:`~PIL.Image.getmodetype` to get the mode used to store each + individual band. :param mode: Input mode. :returns: A tuple containing band names. The length of the tuple @@ -443,13 +443,14 @@ def _getscaleoffset(expr): class Image: """ - This class represents an image object. To create Image objects, use - the appropriate factory functions. There's hardly ever any reason - to call the Image constructor directly. + This class represents an image object. To create + :py:class:`~PIL.Image.Image` objects, use the appropriate factory + functions. There's hardly ever any reason to call the Image constructor + directly. - * :func:`PIL.Image.open` - * :func:`PIL.Image.new` - * :func:`PIL.Image.frombytes` + * :py:func:`~PIL.Image.open` + * :py:func:`~PIL.Image.new` + * :py:func:`~PIL.Image.frombytes` """ format = None format_description = None @@ -588,8 +589,8 @@ class Image: """ Loads this image with pixel data from a bytes object. - This method is similar to the :func:`PIL.Image.frombytes` function, but - loads data into this image instead of creating a new image object. + This method is similar to the :py:func:`~PIL.Image.frombytes` function, + but loads data into this image instead of creating a new image object. """ # may pass tuple instead of argument list @@ -611,7 +612,10 @@ class Image: raise ValueError("cannot decode image data") def fromstring(self, *args, **kw): - """ Deprecated alias to frombytes """ + """Deprecated alias to frombytes. + + .. deprecated:: 2.0 + """ warnings.warn('fromstring() is deprecated. Please call frombytes() instead.', DeprecationWarning) return self.frombytes(*args, **kw) @@ -651,7 +655,7 @@ class Image: """ pass - def convert(self, mode=None, data=None, dither=None, + def convert(self, mode=None, matrix=None, dither=None, palette=WEB, colors=256): """ Returns a converted copy of this image. For the "P" mode, this @@ -660,16 +664,17 @@ class Image: and the palette can be represented without a palette. The current version supports all possible conversions between - "L", "RGB" and "CMYK." + "L", "RGB" and "CMYK." The **matrix** argument only supports "L" + and "RGB". - When translating a colour image to black and white (mode "L"), - the library uses the ITU-R 601-2 luma transform: + When translating a color image to black and white (mode "L"), + the library uses the ITU-R 601-2 luma transform:: L = R * 299/1000 + G * 587/1000 + B * 114/1000 When translating a greyscale image into a bilevel image (mode "1"), all non-zero values are set to 255 (white). To use other - thresholds, use the :func:`PIL.Image.Image.point` method. + thresholds, use the :py:meth:`~PIL.Image.Image.point` method. :param mode: The requested mode. :param matrix: An optional conversion matrix. If given, this @@ -681,8 +686,8 @@ class Image: to "P". Available palettes are WEB or ADAPTIVE. :param colors: Number of colors to use for the ADAPTIVE palette. Defaults to 256. - :rtype: :class:`PIL.Image.Image` - :returns: An Image object. + :rtype: :py:class:`~PIL.Image.Image` + :returns: An :py:class:`~PIL.Image.Image` object. """ if not mode: @@ -698,18 +703,18 @@ class Image: self.load() - if data: + if matrix: # matrix conversion if mode not in ("L", "RGB"): raise ValueError("illegal conversion") - im = self.im.convert_matrix(mode, data) + im = self.im.convert_matrix(mode, matrix) return self._new(im) if mode == "P" and palette == ADAPTIVE: im = self.im.quantize(colors) return self._new(im) - # colourspace conversion + # colorspace conversion if dither is None: dither = FLOYDSTEINBERG @@ -767,8 +772,8 @@ class Image: Copies this image. Use this method if you wish to paste things into an image, but still retain the original. - :rtype: :class:`PIL.Image.Image` - :returns: An Image object. + :rtype: :py:class:`~PIL.Image.Image` + :returns: An :py:class:`~PIL.Image.Image` object. """ self.load() im = self.im.copy() @@ -782,12 +787,12 @@ class Image: This is a lazy operation. Changes to the source image may or may not be reflected in the cropped image. To break the - connection, call the {@link #Image.load} method on the cropped - copy. + connection, call the :py:meth:`~PIL.Image.Image.load` method on + the cropped copy. :param box: The crop rectangle, as a (left, upper, right, lower)-tuple. - :rtype: :class:`PIL.Image.Image` - :returns: An Image object. + :rtype: :py:class:`~PIL.Image.Image` + :returns: An :py:class:`~PIL.Image.Image` object. """ self.load() @@ -801,12 +806,13 @@ class Image: """ Configures the image file loader so it returns a version of the image that as closely as possible matches the given mode and - size. For example, you can use this method to convert a colour + size. For example, you can use this method to convert a color JPEG to greyscale while loading it, or to extract a 128x192 version from a PCD file. - Note that this method modifies the Image object in place. If - the image has already been loaded, this method has no effect. + Note that this method modifies the :py:class:`~PIL.Image.Image` object + in place. If the image has already been loaded, this method has no + effect. :param mode: The requested mode. :param size: The requested size. @@ -822,11 +828,10 @@ class Image: def filter(self, filter): """ Filters this image using the given filter. For a list of - available filters, see the :mod:`PIL.ImageFilter` module. + available filters, see the :py:mod:`~PIL.ImageFilter` module. :param filter: Filter kernel. - :returns: An Image object. - """ + :returns: An :py:class:`~PIL.Image.Image` object. """ self.load() @@ -1014,18 +1019,18 @@ class Image: def offset(self, xoffset, yoffset=None): """ - (Deprecated) Returns a copy of the image where the data has been - offset by the given distances. Data wraps around the edges. If - yoffset is omitted, it is assumed to be equal to xoffset. + .. deprecated:: 2.0 - This method is deprecated. New code should use the - :func:`PIL.ImageChops.offset` function in the - :mod:`PIL.ImageChops` module. + .. note:: New code should use :py:func:`PIL.ImageChops.offset`. + + Returns a copy of the image where the data has been offset by the given + distances. Data wraps around the edges. If **yoffset** is omitted, it + is assumed to be equal to **xoffset**. :param xoffset: The horizontal distance. :param yoffset: The vertical distance. If omitted, both distances are set to the same value. - :returns: An Image object. + :returns: An :py:class:`~PIL.Image.Image` object. """ if warnings: warnings.warn( @@ -1043,14 +1048,14 @@ class Image: (0, 0)). If a 4-tuple is given, the size of the pasted image must match the size of the region. - If the modes don't match, the pasted image is converted to the - mode of this image (see the :func:`PIL.Image.Image.convert` method for + If the modes don't match, the pasted image is converted to the mode of + this image (see the :py:meth:`~PIL.Image.Image.convert` method for details). Instead of an image, the source can be a integer or tuple containing pixel values. The method then fills the region - with the given colour. When creating RGB images, you can - also use colour strings as supported by the ImageColor module. + with the given color. When creating RGB images, you can + also use color strings as supported by the ImageColor module. If a mask is given, this method updates only the regions indicated by the mask. You can use either "1", "L" or "RGBA" @@ -1073,7 +1078,7 @@ class Image: third, the box defaults to (0, 0), and the second argument is interpreted as a mask image. :param mask: An optional mask image. - :returns: An Image object. + :returns: An :py:class:`~PIL.Image.Image` object. """ if isImageType(box) and mask is None: @@ -1131,7 +1136,7 @@ class Image: :param mode: Output mode (default is same as input). In the current version, this can only be used if the source image has mode "L" or "P", and the output has mode "1". - :returns: An Image object. + :returns: An :py:class:`~PIL.Image.Image` object. """ self.load() @@ -1260,19 +1265,19 @@ class Image: def putpixel(self, xy, value): """ - Modifies the pixel at the given position. The colour is given as + Modifies the pixel at the given position. The color is given as a single numerical value for single-band images, and a tuple for multi-band images. - Note that this method is relatively slow. For more extensive - changes, use :func:`PIL.Image.Image.paste` or the :mod:`PIL.ImageDraw` + Note that this method is relatively slow. For more extensive changes, + use :py:meth:`~PIL.Image.Image.paste` or the :py:mod:`~PIL.ImageDraw` module instead. See: - * :func:`PIL.Image.Image.paste` - * :func:`PIL.Image.Image.putdata` - * :mod:`PIL.ImageDraw` + * :py:meth:`~PIL.Image.Image.paste` + * :py:meth:`~PIL.Image.Image.putdata` + * :py:mod:`~PIL.ImageDraw` :param xy: The pixel coordinate, given as (x, y). :param value: The pixel value. @@ -1291,14 +1296,14 @@ class Image: :param size: The requested size in pixels, as a 2-tuple: (width, height). :param filter: An optional resampling filter. This can be - one of :attr:`PIL.Image.NEAREST` (use nearest neighbour), - :attr:`PIL.Image.BILINEAR` (linear interpolation in a 2x2 - environment), :attr:`PIL.Image.BICUBIC` (cubic spline + one of :py:attr:`PIL.Image.NEAREST` (use nearest neighbour), + :py:attr:`PIL.Image.BILINEAR` (linear interpolation in a 2x2 + environment), :py:attr:`PIL.Image.BICUBIC` (cubic spline interpolation in a 4x4 environment), or - :attr:`PIL.Image.ANTIALIAS` (a high-quality downsampling filter). + :py:attr:`PIL.Image.ANTIALIAS` (a high-quality downsampling filter). If omitted, or if the image has mode "1" or "P", it is - set :attr:`PIL.Image.NEAREST`. - :returns: An Image object. + set :py:attr:`PIL.Image.NEAREST`. + :returns: An :py:class:`~PIL.Image.Image` object. """ if resample not in (NEAREST, BILINEAR, BICUBIC, ANTIALIAS): @@ -1331,17 +1336,17 @@ class Image: :param angle: In degrees counter clockwise. :param filter: An optional resampling filter. This can be - one of :attr:`PIL.Image.NEAREST` (use nearest neighbour), - :attr:`PIL.Image.BILINEAR` (linear interpolation in a 2x2 - environment), or :attr:`PIL.Image.BICUBIC` + one of :py:attr:`PIL.Image.NEAREST` (use nearest neighbour), + :py:attr:`PIL.Image.BILINEAR` (linear interpolation in a 2x2 + environment), or :py:attr:`PIL.Image.BICUBIC` (cubic spline interpolation in a 4x4 environment). If omitted, or if the image has mode "1" or "P", it is - set :attr:`PIL.Image.NEAREST`. + set :py:attr:`PIL.Image.NEAREST`. :param expand: Optional expansion flag. If true, expands the output image to make it large enough to hold the entire rotated image. If false or omitted, make the output image the same size as the input image. - :returns: An Image object. + :returns: An :py:class:`~PIL.Image.Image` object. """ if expand: @@ -1469,7 +1474,7 @@ class Image: Note that in the current version of the library, most sequence formats only allows you to seek to the next frame. - See :func:`PIL.Image.Image.tell`. + See :py:meth:`~PIL.Image.Image.tell`. :param frame: Frame number, starting at 0. :exception EOFError: If the call attempts to seek beyond the end @@ -1520,7 +1525,7 @@ class Image: def tell(self): """ - Returns the current frame number. See :func:`PIL.Image.Image.seek`. + Returns the current frame number. See :py:meth:`~PIL.Image.Image.seek`. :returns: Frame number, starting with 0. """ @@ -1532,24 +1537,24 @@ class Image: image to contain a thumbnail version of itself, no larger than the given size. This method calculates an appropriate thumbnail size to preserve the aspect of the image, calls the - :func:`PIL.Image.Image.draft` method to configure the file reader + :py:meth:`~PIL.Image.Image.draft` method to configure the file reader (where applicable), and finally resizes the image. Note that the bilinear and bicubic filters in the current version of PIL are not well-suited for thumbnail generation. - You should use :attr:`PIL.Image.ANTIALIAS` unless speed is much more + You should use :py:attr:`PIL.Image.ANTIALIAS` unless speed is much more important than quality. - Also note that this function modifies the Image object in place. - If you need to use the full resolution image as well, apply this - method to a :func:`PIL.Image.Image.copy` of the original image. + Also note that this function modifies the :py:class:`~PIL.Image.Image` + object in place. If you need to use the full resolution image as well, apply + this method to a :py:meth:`~PIL.Image.Image.copy` of the original image. :param size: Requested size. :param resample: Optional resampling filter. This can be one - of :attr:`PIL.Image.NEAREST`, :attr:`PIL.Image.BILINEAR`, - :attr:`PIL.Image.BICUBIC`, or :attr:`PIL.Image.ANTIALIAS` + of :py:attr:`PIL.Image.NEAREST`, :py:attr:`PIL.Image.BILINEAR`, + :py:attr:`PIL.Image.BICUBIC`, or :py:attr:`PIL.Image.ANTIALIAS` (best quality). If omitted, it defaults to - :attr:`PIL.Image.NEAREST` (this will be changed to ANTIALIAS in a + :py:attr:`PIL.Image.NEAREST` (this will be changed to ANTIALIAS in a future version). :returns: None """ @@ -1593,20 +1598,20 @@ class Image: :param size: The output size. :param method: The transformation method. This is one of - :attr:`PIL.Image.EXTENT` (cut out a rectangular subregion), - :attr:`PIL.Image.AFFINE` (affine transform), - :attr:`PIL.Image.PERSPECTIVE` (perspective transform), - :attr:`PIL.Image.QUAD` (map a quadrilateral to a rectangle), or - :attr:`PIL.Image.MESH` (map a number of source quadrilaterals + :py:attr:`PIL.Image.EXTENT` (cut out a rectangular subregion), + :py:attr:`PIL.Image.AFFINE` (affine transform), + :py:attr:`PIL.Image.PERSPECTIVE` (perspective transform), + :py:attr:`PIL.Image.QUAD` (map a quadrilateral to a rectangle), or + :py:attr:`PIL.Image.MESH` (map a number of source quadrilaterals in one operation). :param data: Extra data to the transformation method. :param resample: Optional resampling filter. It can be one of - :attr:`PIL.Image.NEAREST` (use nearest neighbour), - :attr:`PIL.Image.BILINEAR` (linear interpolation in a 2x2 - environment), or :attr:`PIL.Image.BICUBIC` (cubic spline + :py:attr:`PIL.Image.NEAREST` (use nearest neighbour), + :py:attr:`PIL.Image.BILINEAR` (linear interpolation in a 2x2 + environment), or :py:attr:`PIL.Image.BICUBIC` (cubic spline interpolation in a 4x4 environment). If omitted, or if the image - has mode "1" or "P", it is set to :attr:`PIL.Image.NEAREST`. - :returns: An Image object. + has mode "1" or "P", it is set to :py:attr:`PIL.Image.NEAREST`. + :returns: An :py:class:`~PIL.Image.Image` object. """ if self.mode == 'RGBA': @@ -1619,7 +1624,7 @@ class Image: method, data = method.getdata() if data is None: raise ValueError("missing method data") - + im = new(self.mode, size, None) if method == MESH: # list of quads @@ -1627,7 +1632,7 @@ class Image: im.__transformer(box, self, QUAD, quad, resample, fill) else: im.__transformer((0, 0)+size, self, method, data, resample, fill) - + return im def __transformer(self, box, image, method, data, @@ -1682,9 +1687,9 @@ class Image: """ Transpose image (flip or rotate in 90 degree steps) - :param method: One of :attr:`PIL.Image.FLIP_LEFT_RIGHT`, - :attr:`PIL.Image.FLIP_TOP_BOTTOM`, :attr:`PIL.Image.ROTATE_90`, - :attr:`PIL.Image.ROTATE_180`, or :attr:`PIL.Image.ROTATE_270`. + :param method: One of :py:attr:`PIL.Image.FLIP_LEFT_RIGHT`, + :py:attr:`PIL.Image.FLIP_TOP_BOTTOM`, :py:attr:`PIL.Image.ROTATE_90`, + :py:attr:`PIL.Image.ROTATE_180`, or :py:attr:`PIL.Image.ROTATE_270`. :returns: Returns a flipped or rotated copy of this image. """ @@ -1756,13 +1761,13 @@ def new(mode, size, color=0): :param mode: The mode to use for the new image. :param size: A 2-tuple, containing (width, height) in pixels. - :param color: What colour to use for the image. Default is black. + :param color: What color to use for the image. Default is black. If given, this should be a single integer or floating point value for single-band modes, and a tuple for multi-band modes (one value - per band). When creating RGB images, you can also use colour - strings as supported by the ImageColor module. If the colour is + per band). When creating RGB images, you can also use color + strings as supported by the ImageColor module. If the color is None, the image is not initialised. - :returns: An Image object. + :returns: An :py:class:`~PIL.Image.Image` object. """ if color is None: @@ -1791,14 +1796,15 @@ def frombytes(mode, size, data, decoder_name="raw", *args): Note that this function decodes pixel data only, not entire images. If you have an entire image in a string, wrap it in a - **BytesIO** object, and use :func:`PIL.Image.open` to load it. + :py:class:`~io.BytesIO` object, and use :py:func:`~PIL.Image.open` to load + it. :param mode: The image mode. :param size: The image size. :param data: A byte buffer containing raw data for the given mode. :param decoder_name: What decoder to use. :param args: Additional parameters for the given decoder. - :returns: An Image object. + :returns: An :py:class:`~PIL.Image.Image` object. """ # may pass tuple instead of argument list @@ -1813,7 +1819,10 @@ def frombytes(mode, size, data, decoder_name="raw", *args): return im def fromstring(*args, **kw): - " Deprecated alias to frombytes " + """Deprecated alias to frombytes. + + .. deprecated:: 2.0 + """ warnings.warn( 'fromstring() is deprecated. Please call frombytes() instead.', DeprecationWarning, @@ -1826,21 +1835,20 @@ def frombuffer(mode, size, data, decoder_name="raw", *args): """ Creates an image memory referencing pixel data in a byte buffer. - This function is similar to :func:`PIL.Image.frombytes`, but uses data in - the byte buffer, where possible. This means that changes to the - original buffer object are reflected in this image). Not all modes - can share memory; supported modes include "L", "RGBX", "RGBA", and - "CMYK". + This function is similar to :py:func:`~PIL.Image.frombytes`, but uses data + in the byte buffer, where possible. This means that changes to the + original buffer object are reflected in this image). Not all modes can + share memory; supported modes include "L", "RGBX", "RGBA", and "CMYK". Note that this function decodes pixel data only, not entire images. If you have an entire image file in a string, wrap it in a - **BytesIO** object, and use :func:`PIL.Image.open` to load it. + **BytesIO** object, and use :py:func:`~PIL.Image.open` to load it. In the current version, the default parameters used for the "raw" decoder - differs from that used for :func:`PIL.Image.fromstring`. This is a bug, - and will probably be fixed in a future release. The current release issues - a warning if you do this; to disable the warning, you should provide the - full set of parameters. See below for details. + differs from that used for :py:func:`~PIL.Image.fromstring`. This is a + bug, and will probably be fixed in a future release. The current release + issues a warning if you do this; to disable the warning, you should provide + the full set of parameters. See below for details. :param mode: The image mode. :param size: The image size. @@ -1853,7 +1861,7 @@ def frombuffer(mode, size, data, decoder_name="raw", *args): frombuffer(mode, size, data, "raw", mode, 0, 1) - :returns: An Image object. + :returns: An :py:class:`~PIL.Image.Image` object. .. versionadded:: 1.1.4 """ @@ -1890,7 +1898,7 @@ def fromarray(obj, mode=None): (using the buffer protocol). If obj is not contiguous, then the tobytes method is called - and :func:`PIL.Image.frombuffer` is used. + and :py:func:`~PIL.Image.frombuffer` is used. :param obj: Object with array interface :param mode: Mode to use (will be determined from type if None) @@ -1961,14 +1969,14 @@ def open(fp, mode="r"): This is a lazy operation; this function identifies the file, but the actual image data is not read from the file until you try to process - the data (or call the :func:`PIL.Image.Image.load` method). - See :func:`PIL.Image.new` + the data (or call the :py:meth:`~PIL.Image.Image.load` method). + See :py:func:`~PIL.Image.new`. :param file: A filename (string) or a file object. The file object - must implement **read**, **seek**, and **tell** methods, - and be opened in binary mode. + must implement :py:meth:`~file.read`, :py:meth:`~file.seek`, and + :py:meth:`~file.tell` methods, and be opened in binary mode. :param mode: The mode. If given, this argument must be "r". - :returns: An Image object. + :returns: An :py:class:`~PIL.Image.Image` object. :exception IOError: If the file cannot be found, or the image cannot be opened and identified. """ @@ -2022,7 +2030,7 @@ def alpha_composite(im1, im2): :param im1: The first image. :param im2: The second image. Must have the same mode and size as the first image. - :returns: An Image object. + :returns: An :py:class:`~PIL.Image.Image` object. """ im1.load() @@ -2045,7 +2053,7 @@ def blend(im1, im2, alpha): the second image is returned. There are no restrictions on the alpha value. If necessary, the result is clipped to fit into the allowed output range. - :returns: An Image object. + :returns: An :py:class:`~PIL.Image.Image` object. """ im1.load() @@ -2080,7 +2088,7 @@ def eval(image, *args): :param image: The input image. :param function: A function object, taking one integer argument. - :returns: An Image object. + :returns: An :py:class:`~PIL.Image.Image` object. """ return image.point(args[0]) @@ -2094,7 +2102,7 @@ def merge(mode, bands): :param bands: A sequence containing one single-band image for each band in the output image. All bands must have the same size. - :returns: An Image object. + :returns: An :py:class:`~PIL.Image.Image` object. """ if getmodebands(mode) != len(bands) or "*" in mode: From 1111fca8c6f6838a685960a7d067d97af66fd772 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 11 Oct 2013 22:29:19 -0700 Subject: [PATCH 040/107] Solved Segfault --- libImaging/Access.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libImaging/Access.c b/libImaging/Access.c index fdc1a8886..70eb1af4c 100644 --- a/libImaging/Access.c +++ b/libImaging/Access.c @@ -237,6 +237,7 @@ ImagingAccessInit() ADD("RGBX", line_32, get_pixel_32, put_pixel_32); ADD("CMYK", line_32, get_pixel_32, put_pixel_32); ADD("YCbCr", line_32, get_pixel_32, put_pixel_32); + ADD("LAB", line_32, get_pixel_32, put_pixel_32); } ImagingAccess From eca93101debaabf5d27c6358831c97a4e5740c35 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 11 Oct 2013 22:40:14 -0700 Subject: [PATCH 041/107] B->A, spacing --- libImaging/Pack.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libImaging/Pack.c b/libImaging/Pack.c index 52a478208..d38d9e5e3 100644 --- a/libImaging/Pack.c +++ b/libImaging/Pack.c @@ -527,9 +527,9 @@ static struct { {"YCbCr", "Cr", 8, band2}, /* LAB Color */ - {"LAB", "LAB", 24, ImagingPackRGB}, - {"LAB", "L", 8, band0}, - {"LAB", "B", 8, band1}, + {"LAB", "LAB", 24, ImagingPackRGB}, + {"LAB", "L", 8, band0}, + {"LAB", "A", 8, band1}, {"LAB", "B", 8, band2}, /* integer */ From 13d62c9f913c8d722e60c4e3c66889b7b1c998d0 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 11 Oct 2013 22:40:37 -0700 Subject: [PATCH 042/107] Added LAB to Unpack --- libImaging/Unpack.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libImaging/Unpack.c b/libImaging/Unpack.c index d014fb815..0d0cad292 100644 --- a/libImaging/Unpack.c +++ b/libImaging/Unpack.c @@ -957,6 +957,12 @@ static struct { {"YCbCr", "YCbCrX", 32, copy4}, {"YCbCr", "YCbCrK", 32, copy4}, + /* LAB Color */ + {"LAB", "LAB", 24, ImagingUnpackRGB}, + {"LAB", "L", 8, band0}, + {"LAB", "A", 8, band1}, + {"LAB", "B", 8, band2}, + /* integer variations */ {"I", "I", 32, copy4}, {"I", "I;8", 8, unpackI8}, From 39d5e639f01b2518276fe1f9e220b6e11412ce2c Mon Sep 17 00:00:00 2001 From: Stephen Johnson Date: Fri, 11 Oct 2013 22:45:09 -0700 Subject: [PATCH 043/107] Fully document PIL.ImageChops --- docs/reference/ImageChops.rst | 41 +++++++++++++++++++++++++++++++++++ docs/reference/index.rst | 1 + 2 files changed, 42 insertions(+) create mode 100644 docs/reference/ImageChops.rst diff --git a/docs/reference/ImageChops.rst b/docs/reference/ImageChops.rst new file mode 100644 index 000000000..d2e921c59 --- /dev/null +++ b/docs/reference/ImageChops.rst @@ -0,0 +1,41 @@ +.. py:module:: PIL.ImageChops +.. py:currentmodule:: PIL.ImageChops + +:mod:`ImageChops` Module +======================== + +The :py:mod:`ImageChops` module contains a number of arithmetical image +operations, called channel operations (“chops”). These can be used for various +purposes, including special effects, image compositions, algorithmic painting, +and more. + +For more pre-made operations, see :py:mod:`ImageOps`. + +At this time, most channel operations are only implemented for 8-bit images +(e.g. “L” and “RGB”). + +Functions +--------- + +Most channel operations take one or two image arguments and returns a new +image. Unless otherwise noted, the result of a channel operation is always +clipped to the range 0 to MAX (which is 255 for all modes supported by the +operations in this module). + +.. autofunction:: PIL.ImageChops.add +.. autofunction:: PIL.ImageChops.add_modulo +.. autofunction:: PIL.ImageChops.blend +.. autofunction:: PIL.ImageChops.composite +.. autofunction:: PIL.ImageChops.constant +.. autofunction:: PIL.ImageChops.darker +.. autofunction:: PIL.ImageChops.difference +.. autofunction:: PIL.ImageChops.duplicate +.. autofunction:: PIL.ImageChops.invert +.. autofunction:: PIL.ImageChops.lighter +.. autofunction:: PIL.ImageChops.logical_and +.. autofunction:: PIL.ImageChops.logical_or +.. autofunction:: PIL.ImageChops.multiply +.. autofunction:: PIL.ImageChops.offset +.. autofunction:: PIL.ImageChops.screen +.. autofunction:: PIL.ImageChops.subtract +.. autofunction:: PIL.ImageChops.subtract_modulo diff --git a/docs/reference/index.rst b/docs/reference/index.rst index 3f6385c47..6b8b45c9f 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -5,4 +5,5 @@ Reference :maxdepth: 2 Image + ImageChops ../PIL From 546c20491fa1a7e39609acb237ece58e032625c1 Mon Sep 17 00:00:00 2001 From: Stephen Johnson Date: Fri, 11 Oct 2013 22:45:23 -0700 Subject: [PATCH 044/107] Improve ImageChops docstrings --- PIL/ImageChops.py | 166 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 148 insertions(+), 18 deletions(-) diff --git a/PIL/ImageChops.py b/PIL/ImageChops.py index dfd71eeeb..4ddaf05c3 100644 --- a/PIL/ImageChops.py +++ b/PIL/ImageChops.py @@ -41,7 +41,10 @@ from PIL import Image # @return An image object. def constant(image, value): - "Fill a channel with a given grey level" + """Fill a channel with a given grey level. + + :rtype: :py:class:`~PIL.Image.Image` + """ return Image.new("L", image.size, value) @@ -52,7 +55,10 @@ def constant(image, value): # @return A copy of the source image. def duplicate(image): - "Create a copy of a channel" + """Copy a channel. Alias for :py:meth:`PIL.Image.Image.copy`. + + :rtype: :py:class:`~PIL.Image.Image` + """ return image.copy() @@ -64,7 +70,15 @@ def duplicate(image): # @return An image object. def invert(image): - "Invert a channel" + """ + Invert an image (channel). + + .. code-block:: python + + out = MAX - image + + :rtype: :py:class:`~PIL.Image.Image` + """ image.load() return image._new(image.im.chop_invert()) @@ -81,7 +95,16 @@ def invert(image): # @return An image object. def lighter(image1, image2): - "Select the lighter pixels from each image" + """ + Compares the two images, pixel by pixel, and returns a new image containing + the lighter values. + + .. code-block:: python + + out = max(image1, image2) + + :rtype: :py:class:`~PIL.Image.Image` + """ image1.load() image2.load() @@ -99,7 +122,16 @@ def lighter(image1, image2): # @return An image object. def darker(image1, image2): - "Select the darker pixels from each image" + """ + Compares the two images, pixel by pixel, and returns a new image + containing the darker values. + + .. code-block:: python + + out = min(image1, image2) + + :rtype: :py:class:`~PIL.Image.Image` + """ image1.load() image2.load() @@ -116,7 +148,16 @@ def darker(image1, image2): # @return An image object. def difference(image1, image2): - "Subtract one image from another" + """ + Returns the absolute value of the pixel-by-pixel difference between the two + images. + + .. code-block:: python + + out = abs(image1 - image2) + + :rtype: :py:class:`~PIL.Image.Image` + """ image1.load() image2.load() @@ -135,7 +176,18 @@ def difference(image1, image2): # @return An image object. def multiply(image1, image2): - "Superimpose two positive images" + """ + Superimposes two images on top of each other. + + If you multiply an image with a solid black image, the result is black. If + you multiply with a solid white image, the image is unaffected. + + .. code-block:: python + + out = image1 * image2 / MAX + + :rtype: :py:class:`~PIL.Image.Image` + """ image1.load() image2.load() @@ -152,7 +204,15 @@ def multiply(image1, image2): # @return An image object. def screen(image1, image2): - "Superimpose two negative images" + """ + Superimposes two inverted images on top of each other. + + .. code-block:: python + + out = MAX - ((MAX - image1) * (MAX - image2) / MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ image1.load() image2.load() @@ -170,7 +230,16 @@ def screen(image1, image2): # @return An image object. def add(image1, image2, scale=1.0, offset=0): - "Add two images" + """ + Adds two images, dividing the result by scale and adding the + offset. If omitted, scale defaults to 1.0, and offset to 0.0. + + .. code-block:: python + + out = ((image1 + image2) / scale + offset) + + :rtype: :py:class:`~PIL.Image.Image` + """ image1.load() image2.load() @@ -188,7 +257,16 @@ def add(image1, image2, scale=1.0, offset=0): # @return An image object. def subtract(image1, image2, scale=1.0, offset=0): - "Subtract two images" + """ + Subtracts two images, dividing the result by scale and adding the + offset. If omitted, scale defaults to 1.0, and offset to 0.0. + + .. code-block:: python + + out = ((image1 - image2) / scale + offset) + + :rtype: :py:class:`~PIL.Image.Image` + """ image1.load() image2.load() @@ -205,7 +283,14 @@ def subtract(image1, image2, scale=1.0, offset=0): # @return An image object. def add_modulo(image1, image2): - "Add two images without clipping" + """Add two images, without clipping the result. + + .. code-block:: python + + out = ((image1 + image2) % MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ image1.load() image2.load() @@ -222,7 +307,14 @@ def add_modulo(image1, image2): # @return An image object. def subtract_modulo(image1, image2): - "Subtract two images without clipping" + """Subtract two images, without clipping the result. + + .. code-block:: python + + out = ((image1 - image2) % MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ image1.load() image2.load() @@ -233,7 +325,14 @@ def subtract_modulo(image1, image2): # (image1 and image2). def logical_and(image1, image2): - "Logical and between two images" + """Logical AND between two images. + + .. code-block:: python + + out = ((image1 and image2) % MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ image1.load() image2.load() @@ -244,7 +343,14 @@ def logical_and(image1, image2): # (image1 or image2). def logical_or(image1, image2): - "Logical or between two images" + """Logical OR between two images. + + .. code-block:: python + + out = ((image1 or image2) % MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ image1.load() image2.load() @@ -255,7 +361,14 @@ def logical_or(image1, image2): # (image1 xor image2). def logical_xor(image1, image2): - "Logical xor between two images" + """Logical XOR between two images. + + .. code-block:: python + + out = ((bool(image1) != bool(image2)) % MAX) + + :rtype: :py:class:`~PIL.Image.Image` + """ image1.load() image2.load() @@ -267,7 +380,11 @@ def logical_xor(image1, image2): # Same as the blend function in the Image module. def blend(image1, image2, alpha): - "Blend two images using a constant transparency weight" + """Blend images using constant transparency weight. Alias for + :py:meth:`PIL.Image.Image.blend`. + + :rtype: :py:class:`~PIL.Image.Image` + """ return Image.blend(image1, image2, alpha) @@ -277,7 +394,11 @@ def blend(image1, image2, alpha): # Same as the composite function in the Image module. def composite(image1, image2, mask): - "Create composite image by blending images using a transparency mask" + """Create composite using transparency mask. Alias for + :py:meth:`PIL.Image.Image.composite`. + + :rtype: :py:class:`~PIL.Image.Image` + """ return Image.composite(image1, image2, mask) @@ -295,7 +416,16 @@ def composite(image1, image2, mask): # @return An Image object. def offset(image, xoffset, yoffset=None): - "Offset image in horizontal and/or vertical direction" + """Returns a copy of the image where data has been offset by the given + distances. Data wraps around the edges. If **yoffset** is omitted, it + is assumed to be equal to **xoffset**. + + :param xoffset: The horizontal distance. + :param yoffset: The vertical distance. If omitted, both + distances are set to the same value. + :rtype: :py:class:`~PIL.Image.Image` + """ + if yoffset is None: yoffset = xoffset image.load() From a31a84f012d04602b06c91982d15bcbac5a6cf4e Mon Sep 17 00:00:00 2001 From: Stephen Johnson Date: Fri, 11 Oct 2013 22:46:39 -0700 Subject: [PATCH 045/107] Remove ImageChops from PIL.rst --- docs/PIL.rst | 8 -------- 1 file changed, 8 deletions(-) diff --git a/docs/PIL.rst b/docs/PIL.rst index 2595c22ba..c191d42a3 100644 --- a/docs/PIL.rst +++ b/docs/PIL.rst @@ -57,14 +57,6 @@ PIL Package :undoc-members: :show-inheritance: -:mod:`ImageChops` Module ------------------------- - -.. automodule:: PIL.ImageChops - :members: - :undoc-members: - :show-inheritance: - :mod:`ImageCms` Module ---------------------- From 1e726ea36a517b041a7543a8e4d2516ffd728023 Mon Sep 17 00:00:00 2001 From: Stephen Johnson Date: Fri, 11 Oct 2013 22:46:57 -0700 Subject: [PATCH 046/107] Remove old ImageChops comments; redundant with docstrings --- PIL/ImageChops.py | 149 ---------------------------------------------- 1 file changed, 149 deletions(-) diff --git a/PIL/ImageChops.py b/PIL/ImageChops.py index 4ddaf05c3..ba5350e02 100644 --- a/PIL/ImageChops.py +++ b/PIL/ImageChops.py @@ -17,28 +17,6 @@ from PIL import Image -## -# The ImageChops module contains a number of arithmetical image -# operations, called channel operations ("chops"). These can be -# used for various purposes, including special effects, image -# compositions, algorithmic painting, and more. -#

-# At this time, channel operations are only implemented for 8-bit -# images (e.g. "L" and "RGB"). -#

-# Most channel operations take one or two image arguments and returns -# a new image. Unless otherwise noted, the result of a channel -# operation is always clipped to the range 0 to MAX (which is 255 for -# all modes supported by the operations in this module). -## - -## -# Return an image with the same size as the given image, but filled -# with the given pixel value. -# -# @param image Reference image. -# @param value Pixel value. -# @return An image object. def constant(image, value): """Fill a channel with a given grey level. @@ -48,11 +26,6 @@ def constant(image, value): return Image.new("L", image.size, value) -## -# Copy image. -# -# @param image Source image. -# @return A copy of the source image. def duplicate(image): """Copy a channel. Alias for :py:meth:`PIL.Image.Image.copy`. @@ -62,12 +35,6 @@ def duplicate(image): return image.copy() -## -# Inverts an image -# (MAX - image). -# -# @param image Source image. -# @return An image object. def invert(image): """ @@ -83,16 +50,6 @@ def invert(image): image.load() return image._new(image.im.chop_invert()) -## -# Compare images, and return lighter pixel value -# (max(image1, image2)). -#

-# Compares the two images, pixel by pixel, and returns a new image -# containing the lighter values. -# -# @param image1 First image. -# @param image1 Second image. -# @return An image object. def lighter(image1, image2): """ @@ -110,16 +67,6 @@ def lighter(image1, image2): image2.load() return image1._new(image1.im.chop_lighter(image2.im)) -## -# Compare images, and return darker pixel value -# (min(image1, image2)). -#

-# Compares the two images, pixel by pixel, and returns a new image -# containing the darker values. -# -# @param image1 First image. -# @param image1 Second image. -# @return An image object. def darker(image1, image2): """ @@ -137,15 +84,6 @@ def darker(image1, image2): image2.load() return image1._new(image1.im.chop_darker(image2.im)) -## -# Calculate absolute difference -# (abs(image1 - image2)). -#

-# Returns the absolute value of the difference between the two images. -# -# @param image1 First image. -# @param image1 Second image. -# @return An image object. def difference(image1, image2): """ @@ -163,17 +101,6 @@ def difference(image1, image2): image2.load() return image1._new(image1.im.chop_difference(image2.im)) -## -# Superimpose positive images -# (image1 * image2 / MAX). -#

-# Superimposes two images on top of each other. If you multiply an -# image with a solid black image, the result is black. If you multiply -# with a solid white image, the image is unaffected. -# -# @param image1 First image. -# @param image1 Second image. -# @return An image object. def multiply(image1, image2): """ @@ -193,15 +120,6 @@ def multiply(image1, image2): image2.load() return image1._new(image1.im.chop_multiply(image2.im)) -## -# Superimpose negative images -# (MAX - ((MAX - image1) * (MAX - image2) / MAX)). -#

-# Superimposes two inverted images on top of each other. -# -# @param image1 First image. -# @param image1 Second image. -# @return An image object. def screen(image1, image2): """ @@ -218,16 +136,6 @@ def screen(image1, image2): image2.load() return image1._new(image1.im.chop_screen(image2.im)) -## -# Add images -# ((image1 + image2) / scale + offset). -#

-# Adds two images, dividing the result by scale and adding the -# offset. If omitted, scale defaults to 1.0, and offset to 0.0. -# -# @param image1 First image. -# @param image1 Second image. -# @return An image object. def add(image1, image2, scale=1.0, offset=0): """ @@ -245,16 +153,6 @@ def add(image1, image2, scale=1.0, offset=0): image2.load() return image1._new(image1.im.chop_add(image2.im, scale, offset)) -## -# Subtract images -# ((image1 - image2) / scale + offset). -#

-# Subtracts two images, dividing the result by scale and adding the -# offset. If omitted, scale defaults to 1.0, and offset to 0.0. -# -# @param image1 First image. -# @param image1 Second image. -# @return An image object. def subtract(image1, image2, scale=1.0, offset=0): """ @@ -272,15 +170,6 @@ def subtract(image1, image2, scale=1.0, offset=0): image2.load() return image1._new(image1.im.chop_subtract(image2.im, scale, offset)) -## -# Add images without clipping -# ((image1 + image2) % MAX). -#

-# Adds two images, without clipping the result. -# -# @param image1 First image. -# @param image1 Second image. -# @return An image object. def add_modulo(image1, image2): """Add two images, without clipping the result. @@ -296,15 +185,6 @@ def add_modulo(image1, image2): image2.load() return image1._new(image1.im.chop_add_modulo(image2.im)) -## -# Subtract images without clipping -# ((image1 - image2) % MAX). -#

-# Subtracts two images, without clipping the result. -# -# @param image1 First image. -# @param image1 Second image. -# @return An image object. def subtract_modulo(image1, image2): """Subtract two images, without clipping the result. @@ -320,9 +200,6 @@ def subtract_modulo(image1, image2): image2.load() return image1._new(image1.im.chop_subtract_modulo(image2.im)) -## -# Logical AND -# (image1 and image2). def logical_and(image1, image2): """Logical AND between two images. @@ -338,9 +215,6 @@ def logical_and(image1, image2): image2.load() return image1._new(image1.im.chop_and(image2.im)) -## -# Logical OR -# (image1 or image2). def logical_or(image1, image2): """Logical OR between two images. @@ -356,9 +230,6 @@ def logical_or(image1, image2): image2.load() return image1._new(image1.im.chop_or(image2.im)) -## -# Logical XOR -# (image1 xor image2). def logical_xor(image1, image2): """Logical XOR between two images. @@ -374,10 +245,6 @@ def logical_xor(image1, image2): image2.load() return image1._new(image1.im.chop_xor(image2.im)) -## -# Blend images using constant transparency weight. -#

-# Same as the blend function in the Image module. def blend(image1, image2, alpha): """Blend images using constant transparency weight. Alias for @@ -388,10 +255,6 @@ def blend(image1, image2, alpha): return Image.blend(image1, image2, alpha) -## -# Create composite using transparency mask. -#

-# Same as the composite function in the Image module. def composite(image1, image2, mask): """Create composite using transparency mask. Alias for @@ -402,18 +265,6 @@ def composite(image1, image2, mask): return Image.composite(image1, image2, mask) -## -# Offset image data. -#

-# Returns a copy of the image where data has been offset by the given -# distances. Data wraps around the edges. If yoffset is omitted, it -# is assumed to be equal to xoffset. -# -# @param image Source image. -# @param xoffset The horizontal distance. -# @param yoffset The vertical distance. If omitted, both -# distances are set to the same value. -# @return An Image object. def offset(image, xoffset, yoffset=None): """Returns a copy of the image where data has been offset by the given From 88c700f3b2811378ccc4ee86aea5140a2ed0cf65 Mon Sep 17 00:00:00 2001 From: Stephen Johnson Date: Fri, 11 Oct 2013 22:54:24 -0700 Subject: [PATCH 047/107] Fully document PIL.ImageColor --- PIL/ImageColor.py | 19 ++++++++++++++++ docs/PIL.rst | 8 ------- docs/reference/ImageColor.rst | 41 +++++++++++++++++++++++++++++++++++ docs/reference/index.rst | 1 + 4 files changed, 61 insertions(+), 8 deletions(-) create mode 100644 docs/reference/ImageColor.rst diff --git a/PIL/ImageColor.py b/PIL/ImageColor.py index 86f221adc..c14257151 100644 --- a/PIL/ImageColor.py +++ b/PIL/ImageColor.py @@ -30,6 +30,15 @@ import re # as an RGB value. def getrgb(color): + """ + Convert a color string to an RGB tuple. If the string cannot be parsed, + this function raises a :py:exc:`ValueError` exception. + + .. versionadded:: 1.1.4 + + :param color: A color string + :return: ``(red, green, blue)`` + """ try: rgb = colormap[color] except KeyError: @@ -97,6 +106,16 @@ def getrgb(color): raise ValueError("unknown color specifier: %r" % color) def getcolor(color, mode): + """ + Same as :py:func:`~PIL.ImageColor.getrgb`, but converts the RGB value to a + greyscale value if the mode is not color or a palette image. If the string + cannot be parsed, this function raises a :py:exc:`ValueError` exception. + + .. versionadded:: 1.1.4 + + :param color: A color string + :return: ``(red, green, blue)`` + """ # same as getrgb, but converts the result to the given mode color = getrgb(color) if mode == "RGB": diff --git a/docs/PIL.rst b/docs/PIL.rst index c191d42a3..517439caa 100644 --- a/docs/PIL.rst +++ b/docs/PIL.rst @@ -65,14 +65,6 @@ PIL Package :undoc-members: :show-inheritance: -:mod:`ImageColor` Module ------------------------- - -.. automodule:: PIL.ImageColor - :members: - :undoc-members: - :show-inheritance: - :mod:`ImageDraw` Module ----------------------- diff --git a/docs/reference/ImageColor.rst b/docs/reference/ImageColor.rst new file mode 100644 index 000000000..2d6dd0355 --- /dev/null +++ b/docs/reference/ImageColor.rst @@ -0,0 +1,41 @@ +.. py:module:: PIL.ImageColor +.. py:currentmodule:: PIL.ImageColor + +:mod:`ImageColor` Module +======================== + +The :py:mod:`ImageColor` module contains color tables and converters from +CSS3-style color specifiers to RGB tuples. This module is used by +:py:meth:`PIL.Image.Image.new` and the :py:mod:`~PIL.ImageDraw` module, among +others. + +Color Names +----------- + +The ImageColor module supports the following string formats: + +* Hexadecimal color specifiers, given as ``#rgb`` or ``#rrggbb``. For example, + ``#ff0000`` specifies pure red. + +* RGB functions, given as ``rgb(red, green, blue)`` where the color values are + integers in the range 0 to 255. Alternatively, the color values can be given + as three percentages (0% to 100%). For example, ``rgb(255,0,0)`` and + ``rgb(100%,0%,0%)`` both specify pure red. + +* Hue-Saturation-Lightness (HSL) functions, given as ``hsl(hue, saturation%, + lightness%)`` where hue is the color given as an angle between 0 and 360 + (red=0, green=120, blue=240), saturation is a value between 0% and 100% + (gray=0%, full color=100%), and lightness is a value between 0% and 100% + (black=0%, normal=50%, white=100%). For example, ``hsl(0,100%,50%)`` is pure + red. + +* Common HTML color names. The :py:mod:`~PIL.ImageColor` module provides some + 140 standard color names, based on the colors supported by the X Window + system and most web browsers. color names are case insensitive. For example, + ``red`` and ``Red`` both specify pure red. + +Functions +--------- + +.. autofunction:: getrgb +.. autofunction:: getcolor diff --git a/docs/reference/index.rst b/docs/reference/index.rst index 6b8b45c9f..8604a8787 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -6,4 +6,5 @@ Reference Image ImageChops + ImageColor ../PIL From cb9440a2f5f6204257ea167cb418f00a4cf11dd8 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 11 Oct 2013 23:31:26 -0700 Subject: [PATCH 048/107] packing into 24bit --- libImaging/Pack.c | 2 +- libImaging/Storage.c | 6 ++++-- libImaging/Unpack.c | 9 ++++++++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/libImaging/Pack.c b/libImaging/Pack.c index d38d9e5e3..f8470fd73 100644 --- a/libImaging/Pack.c +++ b/libImaging/Pack.c @@ -527,7 +527,7 @@ static struct { {"YCbCr", "Cr", 8, band2}, /* LAB Color */ - {"LAB", "LAB", 24, ImagingPackRGB}, + {"LAB", "LAB", 24, copy3}, {"LAB", "L", 8, band0}, {"LAB", "A", 8, band1}, {"LAB", "B", 8, band2}, diff --git a/libImaging/Storage.c b/libImaging/Storage.c index 1cfcf0913..f8248a079 100644 --- a/libImaging/Storage.c +++ b/libImaging/Storage.c @@ -180,9 +180,11 @@ ImagingNewPrologueSubtype(const char *mode, unsigned xsize, unsigned ysize, } else if (strcmp(mode, "LAB") == 0) { /* 24-bit color, luminance, + 2 color channels */ + /* L is uint8, a,b are int8 */ im->bands = 3; - im->pixelsize = 4; - im->linesize = xsize * 4; + im->pixelsize = 3; + im->linesize = (xsize*4 + 3) & -4; + im->type = IMAGING_TYPE_SPECIAL; } else { free(im); diff --git a/libImaging/Unpack.c b/libImaging/Unpack.c index 0d0cad292..1330f1434 100644 --- a/libImaging/Unpack.c +++ b/libImaging/Unpack.c @@ -674,6 +674,13 @@ copy2(UINT8* out, const UINT8* in, int pixels) memcpy(out, in, pixels*2); } +static void +copy3(UINT8* out, const UINT8* in, int pixels) +{ + /* LAB triples, 24bit */ + memcpy(out, in, 3 * pixels); +} + static void copy4(UINT8* out, const UINT8* in, int pixels) { @@ -958,7 +965,7 @@ static struct { {"YCbCr", "YCbCrK", 32, copy4}, /* LAB Color */ - {"LAB", "LAB", 24, ImagingUnpackRGB}, + {"LAB", "LAB", 24, copy3}, {"LAB", "L", 8, band0}, {"LAB", "A", 8, band1}, {"LAB", "B", 8, band2}, From ace78d073433d1b3997350465742215b6eeb3c3e Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 11 Oct 2013 23:31:47 -0700 Subject: [PATCH 049/107] Lab is Uint, Int, Int. Tests failing --- PIL/Image.py | 2 +- Tests/test_imagecms.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/PIL/Image.py b/PIL/Image.py index 9c92bf98f..39bf668be 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -225,7 +225,7 @@ _MODE_CONV = { "RGBA": ('|u1', 4), "CMYK": ('|u1', 4), "YCbCr": ('|u1', 3), - "LAB": ('|u1', 3), + "LAB": ('|u1', 3), # UNDONE - unsigned |u1i1i1 "I;16": ('=u2', None), "I;16B": ('>u2', None), "I;16L": (' Date: Fri, 11 Oct 2013 23:39:16 -0700 Subject: [PATCH 050/107] added lab->srgb and srgb->lab->srgb tests --- Tests/test_imagecms.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index d244e1588..0725571f9 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -108,3 +108,25 @@ def test_lab_color(): target = Image.open('Tests/images/lena.Lab.tif') assert_image_similar(i, target, 1) + +def test_lab_srgb(): + pLab = ImageCms.createProfile("LAB") + t = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB") + + img = Image.open('Tests/images/lena.Lab.tif') + + img_srgb = ImageCms.applyTransform(img, t) + + assert_image_similar(lena(), img_srgb, 1) + +def test_lab_roundtrip(): + # check to see if we're at least internally consistent. + pLab = ImageCms.createProfile("LAB") + t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") + + t2 = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB") + + i = ImageCms.applyTransform(lena(), t) + out = ImageCms.applyTransform(i, t2) + + assert_image_similar(lena(), out, 2) From 5c9329d5ba79a6d9f24428306fdb803e14f7a68a Mon Sep 17 00:00:00 2001 From: Stephen Johnson Date: Sat, 12 Oct 2013 00:52:01 -0700 Subject: [PATCH 051/107] Fully document PIL.ImageDraw --- docs/reference/ImageColor.rst | 2 + docs/reference/ImageDraw.rst | 239 ++++++++++++++++++++++++++++++++++ docs/reference/index.rst | 1 + 3 files changed, 242 insertions(+) create mode 100644 docs/reference/ImageDraw.rst diff --git a/docs/reference/ImageColor.rst b/docs/reference/ImageColor.rst index 2d6dd0355..3115b975f 100644 --- a/docs/reference/ImageColor.rst +++ b/docs/reference/ImageColor.rst @@ -9,6 +9,8 @@ CSS3-style color specifiers to RGB tuples. This module is used by :py:meth:`PIL.Image.Image.new` and the :py:mod:`~PIL.ImageDraw` module, among others. +.. _color-names: + Color Names ----------- diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst new file mode 100644 index 000000000..cc3377c3e --- /dev/null +++ b/docs/reference/ImageDraw.rst @@ -0,0 +1,239 @@ +.. py:module:: PIL.ImageDraw +.. py:currentmodule:: PIL.ImageDraw + +:mod:`ImageDraw` Module +======================= + +The :py:mod:`ImageDraw` module provide simple 2D graphics for +:py:class:`~PIL.Image.Image` objects. You can use this module to create new +images, annotate or retouch existing images, and to generate graphics on the +fly for web use. + +For a more advanced drawing library for PIL, see the `aggdraw module`_. + +.. _aggdraw module: http://effbot.org/zone/aggdraw-index.htm + +Example: Draw a gray cross over an image +---------------------------------------- + +.. code-block:: python + + import Image, ImageDraw + + im = Image.open("lena.pgm") + + draw = ImageDraw.Draw(im) + draw.line((0, 0) + im.size, fill=128) + draw.line((0, im.size[1], im.size[0], 0), fill=128) + del draw + + # write to stdout + im.save(sys.stdout, "PNG") + + +Concepts +-------- + +Coordinates +^^^^^^^^^^^ + +The graphics interface uses the same coordinate system as PIL itself, with (0, +0) in the upper left corner. + +Colors +^^^^^^ + +To specify colors, you can use numbers or tuples just as you would use with +:py:meth:`PIL.Image.Image.new` or :py:meth:`PIL.Image.Image.putpixel`. For “1”, +“L”, and “I” images, use integers. For “RGB” images, use a 3-tuple containing +integer values. For “F” images, use integer or floating point values. + +For palette images (mode “P”), use integers as color indexes. In 1.1.4 and +later, you can also use RGB 3-tuples or color names (see below). The drawing +layer will automatically assign color indexes, as long as you don’t draw with +more than 256 colors. + +Color Names +^^^^^^^^^^^ + +See :ref:`color-names` for the color names supported by Pillow. + +Fonts +^^^^^ + +PIL can use bitmap fonts or OpenType/TrueType fonts. + +Bitmap fonts are stored in PIL’s own format, where each font typically consists +of a two files, one named .pil and the other usually named .pbm. The former +contains font metrics, the latter raster data. + +To load a bitmap font, use the load functions in the :py:mod:`~PIL.ImageFont` +module. + +To load a OpenType/TrueType font, use the truetype function in the +:py:mod:`~PIL.ImageFont` module. Note that this function depends on third-party +libraries, and may not available in all PIL builds. + +Functions +--------- + +.. py:class:: PIL.ImageDraw.Draw(im, mode=None) + + Creates an object that can be used to draw in the given image. + + Note that the image will be modified in place. + +Methods +------- + +.. py:method:: PIL.ImageDraw.Draw.arc(xy, start, end, fill=None) + + Draws an arc (a portion of a circle outline) between the start and end + angles, inside the given bounding box. + + :param xy: Four points to define the bounding box. Sequence of either + ``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``. + :param outline: Color to use for the outline. + +.. py:method:: PIL.ImageDraw.Draw.bitmap(xy, bitmap, fill=None) + + Draws a bitmap (mask) at the given position, using the current fill color + for the non-zero portions. The bitmap should be a valid transparency mask + (mode “1”) or matte (mode “L” or “RGBA”). + + This is equivalent to doing ``image.paste(xy, color, bitmap)``. + + To paste pixel data into an image, use the + :py:meth:`~PIL.Image.Image.paste` method on the image itself. + +.. py:method:: PIL.ImageDraw.Draw.chord(xy, start, end, fill=None, outline=None) + + Same as :py:meth:`~PIL.ImageDraw.Draw.arc`, but connects the end points + with a straight line. + + :param xy: Four points to define the bounding box. Sequence of either + ``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``. + :param outline: Color to use for the outline. + :param fill: Color to use for the fill. + +.. py:method:: PIL.ImageDraw.Draw.ellipse(xy, fill=None, outline=None) + + Draws an ellipse inside the given bounding box. + + :param xy: Four points to define the bounding box. Sequence of either + ``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``. + :param outline: Color to use for the outline. + :param fill: Color to use for the fill. + +.. py:method:: PIL.ImageDraw.Draw.line(xy, fill=None, width=0) + + Draws a line between the coordinates in the **xy** list. + + :param xy: Sequence of either 2-tuples like ``[(x, y), (x, y), ...]`` or + numeric values like ``[x, y, x, y, ...]``. + :param fill: Color to use for the line. + :param width: The line width, in pixels. Note that line + joins are not handled well, so wide polylines will not look good. + + .. versionadded:: 1.1.5 + + .. note:: This option was broken until version 1.1.6. + +.. py:method:: PIL.ImageDraw.Draw.pieslice(xy, start, end, fill=None, outline=None) + + Same as arc, but also draws straight lines between the end points and the + center of the bounding box. + + :param xy: Four points to define the bounding box. Sequence of either + ``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``. + :param outline: Color to use for the outline. + :param fill: Color to use for the fill. + +.. py:method:: PIL.ImageDraw.Draw.point(xy, fill=None) + + Draws points (individual pixels) at the given coordinates. + + :param xy: Sequence of either 2-tuples like ``[(x, y), (x, y), ...]`` or + numeric values like ``[x, y, x, y, ...]``. + :param fill: Color to use for the point. + +.. py:method:: PIL.ImageDraw.Draw.polygon(xy, fill=None, outline=None) + + Draws a polygon. + + The polygon outline consists of straight lines between the given + coordinates, plus a straight line between the last and the first + coordinate. + + :param xy: Sequence of either 2-tuples like ``[(x, y), (x, y), ...]`` or + numeric values like ``[x, y, x, y, ...]``. + :param outline: Color to use for the outline. + :param fill: Color to use for the fill. + +.. py:method:: PIL.ImageDraw.Draw.rectangle(xy, fill=None, outline=None) + + Draws a rectangle. + + :param xy: Four points to define the bounding box. Sequence of either + ``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``. The second point + is just outside the drawn rectangle. + :param outline: Color to use for the outline. + :param fill: Color to use for the fill. + +.. py:method:: PIL.ImageDraw.Draw.shape(shape, fill=None, outline=None) + + .. warning:: This method is experimental. + + Draw a shape. + +.. py:method:: PIL.ImageDraw.Draw.text(xy, text, fill=None, font=None, anchor=None) + + Draws the string at the given position. + + :param xy: Top left corner of the text. + :param text: Text to be drawn. + :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. + :param fill: Color to use for the text. + +.. py:method:: PIL.ImageDraw.Draw.textsize(text, font=None) + + Return the size of the given string, in pixels. + + :param text: Text to be measured. + :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. + +Legacy API +---------- + +The :py:class:`~PIL.ImageDraw.Draw` class contains a constructor and a number +of methods which are provided for backwards compatibility only. For this to +work properly, you should either use options on the drawing primitives, or +these methods. Do not mix the old and new calling conventions. + + +.. py:function:: PIL.ImageDraw.ImageDraw(image) + + :rtype: :py:class:`~PIL.ImageDraw.Draw` + +.. py:method:: PIL.ImageDraw.Draw.setink(ink) + + .. deprecated:: 1.1.5 + + Sets the color to use for subsequent draw and fill operations. + +.. py:method:: PIL.ImageDraw.Draw.setfill(fill) + + .. deprecated:: 1.1.5 + + Sets the fill mode. + + If the mode is 0, subsequently drawn shapes (like polygons and rectangles) + are outlined. If the mode is 1, they are filled. + +.. py:method:: PIL.ImageDraw.Draw.setfont(font) + + .. deprecated:: 1.1.5 + + Sets the default font to use for the text method. + + :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. diff --git a/docs/reference/index.rst b/docs/reference/index.rst index 8604a8787..34669c7a2 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -7,4 +7,5 @@ Reference Image ImageChops ImageColor + ImageDraw ../PIL From 7b9c19b29331212d8efdf2c600e23b365fbc5012 Mon Sep 17 00:00:00 2001 From: Stephen Johnson Date: Sat, 12 Oct 2013 00:53:46 -0700 Subject: [PATCH 052/107] Tweak PIL.rst namespace and description --- docs/PIL.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/PIL.rst b/docs/PIL.rst index 517439caa..1f4863c24 100644 --- a/docs/PIL.rst +++ b/docs/PIL.rst @@ -1,5 +1,8 @@ -PIL Package -=========== +PIL Package (autodoc) +===================== + +Reference for modules whose documentation has not yet been ported or written +can be found here. :mod:`BdfFontFile` Module ------------------------- From 5cf0d35a5bc21e74ca19048020f6090988857dc2 Mon Sep 17 00:00:00 2001 From: Stephen Johnson Date: Sat, 12 Oct 2013 00:54:25 -0700 Subject: [PATCH 053/107] Remove ImageDraw from PIL.rst --- docs/PIL.rst | 8 -------- 1 file changed, 8 deletions(-) diff --git a/docs/PIL.rst b/docs/PIL.rst index 1f4863c24..4754d739d 100644 --- a/docs/PIL.rst +++ b/docs/PIL.rst @@ -68,14 +68,6 @@ can be found here. :undoc-members: :show-inheritance: -:mod:`ImageDraw` Module ------------------------ - -.. automodule:: PIL.ImageDraw - :members: - :undoc-members: - :show-inheritance: - :mod:`ImageDraw2` Module ------------------------ From c69c6df8b5aee303c3346a6e2ee9937e82b3cdb2 Mon Sep 17 00:00:00 2001 From: Stephen Johnson Date: Sat, 12 Oct 2013 00:55:39 -0700 Subject: [PATCH 054/107] Clearer PIL.rst title --- docs/PIL.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/PIL.rst b/docs/PIL.rst index 4754d739d..0f4f196fd 100644 --- a/docs/PIL.rst +++ b/docs/PIL.rst @@ -1,5 +1,5 @@ -PIL Package (autodoc) -===================== +PIL Package (autodoc of remaining modules) +========================================== Reference for modules whose documentation has not yet been ported or written can be found here. From b24fbfaab648189a83e7f7d01239565fd7a78118 Mon Sep 17 00:00:00 2001 From: Stephen Johnson Date: Sat, 12 Oct 2013 00:56:39 -0700 Subject: [PATCH 055/107] Move reference above appendices in ToC --- docs/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index 70e8871b7..e560c2594 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -35,8 +35,8 @@ source and contribute at https://github.com/python-imaging/Pillow. installation about guides - handbook/appendices reference/index.rst + handbook/appendices original-readme Support Pillow! From ceb325eb07410793e9bd8b95ff16b849e8be54ad Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sat, 12 Oct 2013 05:44:53 -0400 Subject: [PATCH 056/107] Add history [ci skip] --- CHANGES.rst | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 7b6443535..0b9d7e9e4 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,15 +4,23 @@ Changelog (Pillow) 2.3.0 (2014-01-01) ------------------ -- Move or copy content from README.rst to docs/ [irksep] +- Port and update docs for Image, ImageChops, ImageColor, and ImageDraw + [irksep] -- Respect CFLAGS/LDFLAGS when searching for headers/libs [iElectric] +- Move or copy content from README.rst to docs/ + [irksep] -- Port PIL Handbook tutorial and appendices [irksep] +- Respect CFLAGS/LDFLAGS when searching for headers/libs + [iElectric] -- Alpha Premultiplication support for transform and resize [wiredfool] +- Port PIL Handbook tutorial and appendices + [irksep] -- Fixes to make Pypy 2.1.0 work on Ubuntu 12.04/64 [wiredfool] +- Alpha Premultiplication support for transform and resize + [wiredfool] + +- Fixes to make Pypy 2.1.0 work on Ubuntu 12.04/64 + [wiredfool] 2.2.1 (2013-10-02) ------------------ From f2be739fdf8cea6478f73444483a44fb28347a3a Mon Sep 17 00:00:00 2001 From: Stephen Johnson Date: Sat, 12 Oct 2013 13:48:34 -0700 Subject: [PATCH 057/107] Fully document PIL.ImageEnhance --- PIL/ImageEnhance.py | 75 ++++++++++++++++----------------- docs/PIL.rst | 8 ---- docs/reference/ImageEnhance.rst | 41 ++++++++++++++++++ docs/reference/index.rst | 1 + 4 files changed, 78 insertions(+), 47 deletions(-) create mode 100644 docs/reference/ImageEnhance.rst diff --git a/PIL/ImageEnhance.py b/PIL/ImageEnhance.py index 34d69d569..10433343e 100644 --- a/PIL/ImageEnhance.py +++ b/PIL/ImageEnhance.py @@ -20,71 +20,68 @@ from PIL import Image, ImageFilter, ImageStat + class _Enhance: - ## - # Returns an enhanced image. The enhancement factor is a floating - # point value controlling the enhancement. Factor 1.0 always - # returns a copy of the original image, lower factors mean less - # colour (brightness, contrast, etc), and higher values more. - # There are no restrictions on this value. - # - # @param factor Enhancement factor. - # @return An enhanced image. - def enhance(self, factor): + """ + Returns an enhanced image. + + :param factor: A floating point value controlling the enhancement. + Factor 1.0 always returns a copy of the original image, + lower factors mean less color (brightness, contrast, + etc), and higher values more. There are no restrictions + on this value. + :rtype: :py:class:`~PIL.Image.Image` + """ return Image.blend(self.degenerate, self.image, factor) -## -# Color enhancement object. -#

-# This class can be used to adjust the colour balance of an image, in -# a manner similar to the controls on a colour TV set. An enhancement -# factor of 0.0 gives a black and white image, a factor of 1.0 gives -# the original image. class Color(_Enhance): - "Adjust image colour balance" + """Adjust image color balance. + + This class can be used to adjust the colour balance of an image, in + a manner similar to the controls on a colour TV set. An enhancement + factor of 0.0 gives a black and white image. A factor of 1.0 gives + the original image. + """ def __init__(self, image): self.image = image self.degenerate = image.convert("L").convert(image.mode) -## -# Contrast enhancement object. -#

-# This class can be used to control the contrast of an image, similar -# to the contrast control on a TV set. An enhancement factor of 0.0 -# gives a solid grey image, factor 1.0 gives the original image. class Contrast(_Enhance): - "Adjust image contrast" + """Adjust image contrast. + + This class can be used to control the contrast of an image, similar + to the contrast control on a TV set. An enhancement factor of 0.0 + gives a solid grey image. A factor of 1.0 gives the original image. + """ def __init__(self, image): self.image = image mean = int(ImageStat.Stat(image.convert("L")).mean[0] + 0.5) self.degenerate = Image.new("L", image.size, mean).convert(image.mode) -## -# Brightness enhancement object. -#

-# This class can be used to control the brighntess of an image. An -# enhancement factor of 0.0 gives a black image, factor 1.0 gives the -# original image. class Brightness(_Enhance): - "Adjust image brightness" + """Adjust image brightness. + + This class can be used to control the brighntess of an image. An + enhancement factor of 0.0 gives a black image. A factor of 1.0 gives the + original image. + """ def __init__(self, image): self.image = image self.degenerate = Image.new(image.mode, image.size, 0) -## -# Sharpness enhancement object. -#

-# This class can be used to adjust the sharpness of an image. The -# enhancement factor 0.0 gives a blurred image, 1.0 gives the original -# image, and a factor of 2.0 gives a sharpened image. class Sharpness(_Enhance): - "Adjust image sharpness" + """Adjust image sharpness. + + This class can be used to adjust the sharpness of an image. An + enhancement factor of 0.0 gives a blurred image, a factor of 1.0 gives the + original image, and a factor of 2.0 gives a sharpened image. + """ def __init__(self, image): self.image = image self.degenerate = image.filter(ImageFilter.SMOOTH) diff --git a/docs/PIL.rst b/docs/PIL.rst index 0f4f196fd..32e9fe4ea 100644 --- a/docs/PIL.rst +++ b/docs/PIL.rst @@ -76,14 +76,6 @@ can be found here. :undoc-members: :show-inheritance: -:mod:`ImageEnhance` Module --------------------------- - -.. automodule:: PIL.ImageEnhance - :members: - :undoc-members: - :show-inheritance: - :mod:`ImageFile` Module ----------------------- diff --git a/docs/reference/ImageEnhance.rst b/docs/reference/ImageEnhance.rst new file mode 100644 index 000000000..94e1c9b2a --- /dev/null +++ b/docs/reference/ImageEnhance.rst @@ -0,0 +1,41 @@ +.. py:module:: PIL.ImageEnhance +.. py:currentmodule:: PIL.ImageEnhance + +:mod:`ImageEnhance` Module +========================== + +The :py:mod:`ImageEnhance` module contains a number of classes that can be used +for image enhancement. + +Example +------- + +Vary the Sharpness of an Image +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: python + + import ImageEnhance + + enhancer = ImageEnhance.Sharpness(image) + + for i in range(8): + factor = i / 4.0 + enhancer.enhance(factor).show("Sharpness %f" % factor) + +Also see the :file:`enhancer.py` demo program in the :file:`Scripts/` +directory. + +Classes +------- + +All enhancement classes implement a common interface, containing a single +method: + +.. autoclass:: PIL.ImageEnhance._Enhance + :members: + +.. autoclass:: PIL.ImageEnhance.Color +.. autoclass:: PIL.ImageEnhance.Contrast +.. autoclass:: PIL.ImageEnhance.Brightness +.. autoclass:: PIL.ImageEnhance.Sharpness diff --git a/docs/reference/index.rst b/docs/reference/index.rst index 34669c7a2..8198fdcbc 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -8,4 +8,5 @@ Reference ImageChops ImageColor ImageDraw + ImageEnhance ../PIL From b98c3f05cdc2641b9060ca6bddfb76a70eb38c35 Mon Sep 17 00:00:00 2001 From: Stephen Johnson Date: Sat, 12 Oct 2013 13:53:31 -0700 Subject: [PATCH 058/107] Fix a couple of example imports --- docs/reference/ImageDraw.rst | 2 +- docs/reference/ImageEnhance.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index cc3377c3e..c3210f6fc 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -18,7 +18,7 @@ Example: Draw a gray cross over an image .. code-block:: python - import Image, ImageDraw + from PIL import Image, ImageDraw im = Image.open("lena.pgm") diff --git a/docs/reference/ImageEnhance.rst b/docs/reference/ImageEnhance.rst index 94e1c9b2a..db068bb84 100644 --- a/docs/reference/ImageEnhance.rst +++ b/docs/reference/ImageEnhance.rst @@ -15,7 +15,7 @@ Vary the Sharpness of an Image .. code-block:: python - import ImageEnhance + from PIL import ImageEnhance enhancer = ImageEnhance.Sharpness(image) From 348daf9490cde850e7d5c5c71b7ea3010ff123c9 Mon Sep 17 00:00:00 2001 From: Stephen Johnson Date: Sat, 12 Oct 2013 13:57:27 -0700 Subject: [PATCH 059/107] Document ImageFile.Parser --- PIL/ImageFile.py | 10 ++++++--- docs/PIL.rst | 8 ------- docs/reference/ImageFile.rst | 41 ++++++++++++++++++++++++++++++++++++ docs/reference/index.rst | 1 + 4 files changed, 49 insertions(+), 11 deletions(-) create mode 100644 docs/reference/ImageFile.rst diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index c86f0a177..70762b4b0 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -137,7 +137,7 @@ class ImageFile(Image.Image): readonly = 0 if self.filename and len(self.tile) == 1 and not hasattr(sys, 'pypy_version_info'): - # As of pypy 2.1.0, memory mapping was failing here. + # As of pypy 2.1.0, memory mapping was failing here. # try memory mapping d, e, o, a = self.tile[0] if d == "raw" and a[0] == self.mode and a[0] in Image._MAPMODES: @@ -299,6 +299,8 @@ class Parser: """ Incremental image parser. This class implements the standard feed/close consumer interface. + + In Python 2.x, this is an old-style class. """ incremental = None image = None @@ -318,7 +320,7 @@ class Parser: """ (Consumer) Feed data to the parser. - :param data" A string buffer. + :param data: A string buffer. :exception IOError: If the parser failed to parse the image file. """ # collect data @@ -404,7 +406,9 @@ class Parser: (Consumer) Close the stream. :returns: An image object. - :exception IOError: If the parser failed to parse the image file. + :exception IOError: If the parser failed to parse the image file either + because it cannot be identified or cannot be + decoded. """ # finish decoding if self.decoder: diff --git a/docs/PIL.rst b/docs/PIL.rst index 32e9fe4ea..2665ce9c4 100644 --- a/docs/PIL.rst +++ b/docs/PIL.rst @@ -76,14 +76,6 @@ can be found here. :undoc-members: :show-inheritance: -:mod:`ImageFile` Module ------------------------ - -.. automodule:: PIL.ImageFile - :members: - :undoc-members: - :show-inheritance: - :mod:`ImageFileIO` Module ------------------------- diff --git a/docs/reference/ImageFile.rst b/docs/reference/ImageFile.rst new file mode 100644 index 000000000..57476780d --- /dev/null +++ b/docs/reference/ImageFile.rst @@ -0,0 +1,41 @@ +.. py:module:: PIL.ImageFile +.. py:currentmodule:: PIL.ImageFile + +:mod:`ImageFile` Module +======================= + +The :py:mod:`ImageFile` module provides support functions for the image open +and save functions. + +In addition, it provides a :py:class:`Parser` class which can be used to decode +an image piece by piece (e.g. while receiving it over a network connection). +This class implements the same consumer interface as the standard **sgmllib** +and **xmllib** modules. + +Example: Parse an image +----------------------- + +.. code-block:: python + + from PIL import ImageFile + + fp = open("lena.pgm", "rb") + + p = ImageFile.Parser() + + while 1: + s = fp.read(1024) + if not s: + break + p.feed(s) + + im = p.close() + + im.save("copy.jpg") + + +:py:class:`~PIL.ImageFile.Parser` +--------------------------------- + +.. autoclass:: PIL.ImageFile.Parser() + :members: diff --git a/docs/reference/index.rst b/docs/reference/index.rst index 8198fdcbc..0ef01aa56 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -9,4 +9,5 @@ Reference ImageColor ImageDraw ImageEnhance + ImageFile ../PIL From 11cd6a9150bff1d625dfc1563b4338aa5517483f Mon Sep 17 00:00:00 2001 From: Stephen Johnson Date: Sat, 12 Oct 2013 13:58:53 -0700 Subject: [PATCH 060/107] Comments identifying modules to skip documenting --- docs/PIL.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/PIL.rst b/docs/PIL.rst index 2665ce9c4..2ec1e1b70 100644 --- a/docs/PIL.rst +++ b/docs/PIL.rst @@ -68,6 +68,7 @@ can be found here. :undoc-members: :show-inheritance: +.. intentionally skipped documenting this because it's not documented anywhere :mod:`ImageDraw2` Module ------------------------ @@ -76,6 +77,7 @@ can be found here. :undoc-members: :show-inheritance: +.. intentionally skipped documenting this because it's deprecated :mod:`ImageFileIO` Module ------------------------- From a2c67dc3afe9a5f63e4b4d1721217f975aa46b7d Mon Sep 17 00:00:00 2001 From: Stephen Johnson Date: Sat, 12 Oct 2013 16:49:32 -0700 Subject: [PATCH 061/107] Fully document PIL.ImageFilter --- PIL/ImageFilter.py | 154 ++++++++++++++------------------- docs/reference/ImageFilter.rst | 47 ++++++++++ docs/reference/index.rst | 1 + 3 files changed, 114 insertions(+), 88 deletions(-) create mode 100644 docs/reference/ImageFilter.rst diff --git a/PIL/ImageFilter.py b/PIL/ImageFilter.py index c13f75bbc..ac8fe9f19 100644 --- a/PIL/ImageFilter.py +++ b/PIL/ImageFilter.py @@ -17,31 +17,28 @@ from functools import reduce + class Filter(object): pass -## -# Convolution filter kernel. class Kernel(Filter): + """ + Create a convolution kernel. The current version only + supports 3x3 and 5x5 integer and floating point kernels. - ## - # Create a convolution kernel. The current version only - # supports 3x3 and 5x5 integer and floating point kernels. - #

- # In the current version, kernels can only be applied to - # "L" and "RGB" images. - # - # @def __init__(size, kernel, **options) - # @param size Kernel size, given as (width, height). In - # the current version, this must be (3,3) or (5,5). - # @param kernel A sequence containing kernel weights. - # @param **options Optional keyword arguments. - # @keyparam scale Scale factor. If given, the result for each - # pixel is divided by this value. The default is the sum - # of the kernel weights. - # @keyparam offset Offset. If given, this value is added to the - # result, after it has been divided by the scale factor. + In the current version, kernels can only be applied to + "L" and "RGB" images. + + :param size: Kernel size, given as (width, height). In the current + version, this must be (3,3) or (5,5). + :param kernel: A sequence containing kernel weights. + :param scale: Scale factor. If given, the result for each pixel is + divided by this value. the default is the sum of the + kernel weights. + :param offset: Offset. If given, this value is added to the result, + after it has been divided by the scale factor. + """ def __init__(self, size, kernel, scale=None, offset=0): if scale is None: @@ -56,24 +53,23 @@ class Kernel(Filter): raise ValueError("cannot filter palette images") return image.filter(*self.filterargs) + class BuiltinFilter(Kernel): def __init__(self): pass -## -# Rank filter. class RankFilter(Filter): - name = "Rank" + """ + Create a rank filter. The rank filter sorts all pixels in + a window of the given size, and returns the **rank**'th value. - ## - # Create a rank filter. The rank filter sorts all pixels in - # a window of the given size, and returns the rank'th value. - # - # @param size The kernel size, in pixels. - # @param rank What pixel value to pick. Use 0 for a min filter, - # size*size/2 for a median filter, size*size-1 for a max filter, - # etc. + :param size: The kernel size, in pixels. + :param rank: What pixel value to pick. Use 0 for a min filter, + ``size * size / 2`` for a median filter, ``size * size - 1`` + for a max filter, etc. + """ + name = "Rank" def __init__(self, size, rank): self.size = size @@ -85,99 +81,99 @@ class RankFilter(Filter): image = image.expand(self.size//2, self.size//2) return image.rankfilter(self.size, self.rank) -## -# Median filter. Picks the median pixel value in a window with the -# given size. class MedianFilter(RankFilter): - name = "Median" + """ + Create a median filter. Picks the median pixel value in a window with the + given size. - ## - # Create a median filter. - # - # @param size The kernel size, in pixels. + :param size: The kernel size, in pixels. + """ + name = "Median" def __init__(self, size=3): self.size = size self.rank = size*size//2 -## -# Min filter. Picks the lowest pixel value in a window with the given -# size. class MinFilter(RankFilter): - name = "Min" + """ + Create a min filter. Picks the lowest pixel value in a window with the + given size. - ## - # Create a min filter. - # - # @param size The kernel size, in pixels. + :param size: The kernel size, in pixels. + """ + name = "Min" def __init__(self, size=3): self.size = size self.rank = 0 -## -# Max filter. Picks the largest pixel value in a window with the -# given size. class MaxFilter(RankFilter): - name = "Max" + """ + Create a max filter. Picks the largest pixel value in a window with the + given size. - ## - # Create a max filter. - # - # @param size The kernel size, in pixels. + :param size: The kernel size, in pixels. + """ + name = "Max" def __init__(self, size=3): self.size = size self.rank = size*size-1 -## -# Mode filter. Picks the most frequent pixel value in a box with the -# given size. Pixel values that occur only once or twice are ignored; -# if no pixel value occurs more than twice, the original pixel value -# is preserved. class ModeFilter(Filter): - name = "Mode" + """ - ## - # Create a mode filter. - # - # @param size The kernel size, in pixels. + Create a mode filter. Picks the most frequent pixel value in a box with the + given size. Pixel values that occur only once or twice are ignored; if no + pixel value occurs more than twice, the original pixel value is preserved. + + :param size: The kernel size, in pixels. + """ + name = "Mode" def __init__(self, size=3): self.size = size + def filter(self, image): return image.modefilter(self.size) -## -# Gaussian blur filter. class GaussianBlur(Filter): + """Gaussian blur filter. + + :param radius: Blur radius. + """ name = "GaussianBlur" def __init__(self, radius=2): self.radius = radius + def filter(self, image): return image.gaussian_blur(self.radius) -## -# Unsharp mask filter. class UnsharpMask(Filter): + """Unsharp mask filter. + + See Wikipedia's entry on `digital unsharp masking`_ for an explanation of + the parameters. + + .. _digital unsharp masking: https://en.wikipedia.org/wiki/Unsharp_masking#Digital_unsharp_masking + """ name = "UnsharpMask" def __init__(self, radius=2, percent=150, threshold=3): self.radius = radius self.percent = percent self.threshold = threshold + def filter(self, image): return image.unsharp_mask(self.radius, self.percent, self.threshold) -## -# Simple blur filter. class BLUR(BuiltinFilter): name = "Blur" @@ -189,8 +185,6 @@ class BLUR(BuiltinFilter): 1, 1, 1, 1, 1 ) -## -# Simple contour filter. class CONTOUR(BuiltinFilter): name = "Contour" @@ -200,8 +194,6 @@ class CONTOUR(BuiltinFilter): -1, -1, -1 ) -## -# Simple detail filter. class DETAIL(BuiltinFilter): name = "Detail" @@ -211,8 +203,6 @@ class DETAIL(BuiltinFilter): 0, -1, 0 ) -## -# Simple edge enhancement filter. class EDGE_ENHANCE(BuiltinFilter): name = "Edge-enhance" @@ -222,8 +212,6 @@ class EDGE_ENHANCE(BuiltinFilter): -1, -1, -1 ) -## -# Simple stronger edge enhancement filter. class EDGE_ENHANCE_MORE(BuiltinFilter): name = "Edge-enhance More" @@ -233,8 +221,6 @@ class EDGE_ENHANCE_MORE(BuiltinFilter): -1, -1, -1 ) -## -# Simple embossing filter. class EMBOSS(BuiltinFilter): name = "Emboss" @@ -244,8 +230,6 @@ class EMBOSS(BuiltinFilter): 0, 0, 0 ) -## -# Simple edge-finding filter. class FIND_EDGES(BuiltinFilter): name = "Find Edges" @@ -255,8 +239,6 @@ class FIND_EDGES(BuiltinFilter): -1, -1, -1 ) -## -# Simple smoothing filter. class SMOOTH(BuiltinFilter): name = "Smooth" @@ -266,8 +248,6 @@ class SMOOTH(BuiltinFilter): 1, 1, 1 ) -## -# Simple stronger smoothing filter. class SMOOTH_MORE(BuiltinFilter): name = "Smooth More" @@ -279,8 +259,6 @@ class SMOOTH_MORE(BuiltinFilter): 1, 1, 1, 1, 1 ) -## -# Simple sharpening filter. class SHARPEN(BuiltinFilter): name = "Sharpen" diff --git a/docs/reference/ImageFilter.rst b/docs/reference/ImageFilter.rst new file mode 100644 index 000000000..0a4f29bfa --- /dev/null +++ b/docs/reference/ImageFilter.rst @@ -0,0 +1,47 @@ +.. py:module:: PIL.ImageFilter +.. py:currentmodule:: PIL.ImageFilter + +:mod:`ImageFilter` Module +========================= + +The :py:mod:`ImageFilter` module contains definitions for a pre-defined set of +filters, which can be be used with :py:meth:`Image.filter() +`. + +Example Filter an image +----------------------- + +.. code-block:: python + + import ImageFilter + + im1 = im.filter(ImageFilter.BLUR) + + im2 = im.filter(ImageFilter.MinFilter(3)) + im3 = im.filter(ImageFilter.MinFilter) # same as MinFilter(3) + +Filters +------- + +The current version of the library provides the following set of predefined +image enhancement filters: + +* **BLUR** +* **CONTOUR** +* **DETAIL** +* **EDGE_ENHANCE** +* **EDGE_ENHANCE_MORE** +* **EMBOSS** +* **FIND_EDGES** +* **SMOOTH** +* **SMOOTH_MORE** +* **SHARPEN** + +.. autoclass:: PIL.ImageFilter.GaussianBlur +.. autoclass:: PIL.ImageFilter.UnsharpMask +.. autoclass:: PIL.ImageFilter.Kernel +.. autoclass:: PIL.ImageFilter.RankFilter +.. autoclass:: PIL.ImageFilter.MedianFilter +.. autoclass:: PIL.ImageFilter.MinFilter +.. autoclass:: PIL.ImageFilter.MaxFilter +.. autoclass:: PIL.ImageFilter.ModeFilter diff --git a/docs/reference/index.rst b/docs/reference/index.rst index 0ef01aa56..510970303 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -10,4 +10,5 @@ Reference ImageDraw ImageEnhance ImageFile + ImageFilter ../PIL From bc0f53aceba284c5c9b85da11228adbec394ad44 Mon Sep 17 00:00:00 2001 From: Stephen Johnson Date: Sat, 12 Oct 2013 17:14:12 -0700 Subject: [PATCH 062/107] Fully document PIL.ImageFont --- PIL/ImageFont.py | 99 ++++++++++++++------------------- docs/reference/ImageEnhance.rst | 7 +-- docs/reference/ImageFilter.rst | 10 ++-- docs/reference/ImageFont.rst | 69 +++++++++++++++++++++++ docs/reference/index.rst | 1 + 5 files changed, 120 insertions(+), 66 deletions(-) create mode 100644 docs/reference/ImageFont.rst diff --git a/PIL/ImageFont.py b/PIL/ImageFont.py index 826b37d5c..8313ee3ba 100644 --- a/PIL/ImageFont.py +++ b/PIL/ImageFont.py @@ -61,21 +61,6 @@ except ImportError: # position according to dx, dy. # -------------------------------------------------------------------- -## -# The ImageFont module defines a class with the same name. -# Instances of this class store bitmap fonts, and are used with the -# text method of the ImageDraw class. -#

-# PIL uses it's own font file format to store bitmap fonts. You can -# use the pilfont utility to convert BDF and PCF font -# descriptors (X window font formats) to this format. -#

-# Starting with version 1.1.4, PIL can be configured to support -# TrueType and OpenType fonts. For earlier version, TrueType -# support is only available as part of the imToolkit package -# -# @see ImageDraw#ImageDraw.text -# @see pilfont class ImageFont: "PIL font wrapper" @@ -197,41 +182,42 @@ class TransposedFont: return im.transpose(self.orientation) return im -## -# Load font file. This function loads a font object from the given -# bitmap font file, and returns the corresponding font object. -# -# @param filename Name of font file. -# @return A font object. -# @exception IOError If the file could not be read. def load(filename): - "Load a font file." + """ + Load a font file. This function loads a font object from the given + bitmap font file, and returns the corresponding font object. + + :param filename: Name of font file. + :return: A font object. + :exception IOError: If the file could not be read. + """ f = ImageFont() f._load_pilfont(filename) return f -## -# Load a TrueType or OpenType font file, and create a font object. -# This function loads a font object from the given file, and creates -# a font object for a font of the given size. -#

-# This function requires the _imagingft service. -# -# @param filename A truetype font file. Under Windows, if the file -# is not found in this filename, the loader also looks in Windows -# fonts directory -# @param size The requested size, in points. -# @param index Which font face to load (default is first available face). -# @param encoding Which font encoding to use (default is Unicode). Common -# encodings are "unic" (Unicode), "symb" (Microsoft Symbol), "ADOB" -# (Adobe Standard), "ADBE" (Adobe Expert), and "armn" (Apple Roman). -# See the FreeType documentation for more information. -# @return A font object. -# @exception IOError If the file could not be read. def truetype(font=None, size=10, index=0, encoding="", filename=None): - "Load a truetype font file." + """ + Load a TrueType or OpenType font file, and create a font object. + This function loads a font object from the given file, and creates + a font object for a font of the given size. + + This function requires the _imagingft service. + + :param filename: A truetype font file. Under Windows, if the file + is not found in this filename, the loader also looks in + Windows :file:`fonts/` directory. + :param size: The requested size, in points. + :param index: Which font face to load (default is first available face). + :param encoding: Which font encoding to use (default is Unicode). Common + encodings are "unic" (Unicode), "symb" (Microsoft + Symbol), "ADOB" (Adobe Standard), "ADBE" (Adobe Expert), + and "armn" (Apple Roman). See the FreeType documentation + for more information. + :return: A font object. + :exception IOError: If the file could not be read. + """ if filename: if warnings: @@ -251,17 +237,16 @@ def truetype(font=None, size=10, index=0, encoding="", filename=None): return FreeTypeFont(filename, size, index, encoding) raise -## -# Load font file. Same as load, but searches for a bitmap font along -# the Python path. -# -# @param filename Name of font file. -# @return A font object. -# @exception IOError If the file could not be read. -# @see #load def load_path(filename): - "Load a font file, searching along the Python path." + """ + Load font file. Same as :py:func:`~PIL.ImageFont.load`, but searches for a + bitmap font along the Python path. + + :param filename: Name of font file. + :return: A font object. + :exception IOError: If the file could not be read. + """ for dir in sys.path: if isDirectory(dir): if not isinstance(filename, str): @@ -275,13 +260,14 @@ def load_path(filename): pass raise IOError("cannot find font file") -## -# Load a (probably rather ugly) default font. -# -# @return A font object. def load_default(): - "Load a default font." + """Load a "better than nothing" default font. + + .. versionadded:: 1.1.4 + + :return: A font object. + """ from io import BytesIO import base64 f = ImageFont() @@ -406,6 +392,7 @@ w7IkEbzhVQAAAABJRU5ErkJggg== ''')))) return f + if __name__ == "__main__": # create font data chunk for embedding import base64, os, sys diff --git a/docs/reference/ImageEnhance.rst b/docs/reference/ImageEnhance.rst index db068bb84..e951b8e53 100644 --- a/docs/reference/ImageEnhance.rst +++ b/docs/reference/ImageEnhance.rst @@ -7,11 +7,8 @@ The :py:mod:`ImageEnhance` module contains a number of classes that can be used for image enhancement. -Example -------- - -Vary the Sharpness of an Image -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Example: Vary the sharpness of an image +--------------------------------------- .. code-block:: python diff --git a/docs/reference/ImageFilter.rst b/docs/reference/ImageFilter.rst index 0a4f29bfa..9866792ff 100644 --- a/docs/reference/ImageFilter.rst +++ b/docs/reference/ImageFilter.rst @@ -5,15 +5,15 @@ ========================= The :py:mod:`ImageFilter` module contains definitions for a pre-defined set of -filters, which can be be used with :py:meth:`Image.filter() -`. +filters, which can be be used with the :py:meth:`Image.filter() +` method. -Example Filter an image ------------------------ +Example: Filter an image +------------------------ .. code-block:: python - import ImageFilter + from PIL import ImageFilter im1 = im.filter(ImageFilter.BLUR) diff --git a/docs/reference/ImageFont.rst b/docs/reference/ImageFont.rst new file mode 100644 index 000000000..c93df7d3b --- /dev/null +++ b/docs/reference/ImageFont.rst @@ -0,0 +1,69 @@ +.. py:module:: PIL.ImageFont +.. py:currentmodule:: PIL.ImageFont + +:mod:`ImageFont` Module +======================= + +The :py:mod:`ImageFont` module defines a class with the same name. Instances of +this class store bitmap fonts, and are used with the +:py:meth:`PIL.ImageDraw.Draw.text` method. + +PIL uses its own font file format to store bitmap fonts. You can use the +:command`pilfont` utility to convert BDF and PCF font descriptors (X window +font formats) to this format. + +Starting with version 1.1.4, PIL can be configured to support TrueType and +OpenType fonts (as well as other font formats supported by the FreeType +library). For earlier versions, TrueType support is only available as part of +the imToolkit package + +Example +------- + +.. code-block:: python + + from PIL import ImageFont, ImageDraw + + draw = ImageDraw.Draw(image) + + # use a bitmap font + font = ImageFont.load("arial.pil") + + draw.text((10, 10), "hello", font=font) + + # use a truetype font + font = ImageFont.truetype("arial.ttf", 15) + + draw.text((10, 25), "world", font=font) + +Functions +--------- + +.. autofunction:: PIL.ImageFont.load +.. autofunction:: PIL.ImageFont.load_path +.. autofunction:: PIL.ImageFont.truetype +.. autofunction:: PIL.ImageFont.load_default + +Methods +------- + +.. py:method:: PIL.ImageFont.ImageFont.getsize(text) + + :return: (width, height) + +.. py:method:: PIL.ImageFont.ImageFont.getmask(text, mode='') + + Create a bitmap for the text. + + If the font uses antialiasing, the bitmap should have mode “L” and use a + maximum value of 255. Otherwise, it should have mode “1”. + + :param text: Text to render. + :param mode: Used by some graphics drivers to indicate what mode the + driver prefers; if empty, the renderer may return either + mode. Note that the mode is always a string, to simplify + C-level implementations. + + .. versionadded:: 1.1.5 + :return: An internal PIL storage memory instance as defined by the + :py:mod:`PIL.Image.core` interface module. diff --git a/docs/reference/index.rst b/docs/reference/index.rst index 510970303..5f8d6fac3 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -11,4 +11,5 @@ Reference ImageEnhance ImageFile ImageFilter + ImageFont ../PIL From cf7b72f9b9159dd919110ea662fb696d6c560331 Mon Sep 17 00:00:00 2001 From: Stephen Johnson Date: Sat, 12 Oct 2013 17:22:02 -0700 Subject: [PATCH 063/107] Fully document PIL.ImageGrab --- PIL/ImageGrab.py | 51 ++++++++++++++++++------------------ docs/reference/ImageGrab.rst | 15 +++++++++++ docs/reference/index.rst | 1 + 3 files changed, 42 insertions(+), 25 deletions(-) create mode 100644 docs/reference/ImageGrab.rst diff --git a/PIL/ImageGrab.py b/PIL/ImageGrab.py index 59cc04706..52668ab04 100644 --- a/PIL/ImageGrab.py +++ b/PIL/ImageGrab.py @@ -17,33 +17,31 @@ from PIL import Image -## -# (New in 1.1.3) The ImageGrab module can be used to copy -# the contents of the screen to a PIL image memory. -#

-# The current version works on Windows only.

-# -# @since 1.1.3 -## try: # built-in driver (1.1.3 and later) grabber = Image.core.grabscreen except AttributeError: - # stand-alone driver (pil plus) - import _grabscreen - grabber = _grabscreen.grab + try: + # stand-alone driver (pil plus) + import _grabscreen + grabber = _grabscreen.grab + except ImportError: + # allow the module to be imported by autodoc + grabber = None -## -# (New in 1.1.3) Take a snapshot of the screen. The pixels inside the -# bounding box are returned as an "RGB" image. If the bounding box is -# omitted, the entire screen is copied. -# -# @param bbox What region to copy. Default is the entire screen. -# @return An image -# @since 1.1.3 def grab(bbox=None): + """ + Take a snapshot of the screen. The pixels inside the bounding box are + returned as an "RGB" image. If the bounding box is omitted, the entire + screen is copied. + + .. versionadded:: 1.1.3 + + :param bbox: What region to copy. Default is the entire screen. + :return: An image + """ size, data = grabber() im = Image.frombytes( "RGB", size, data, @@ -55,14 +53,17 @@ def grab(bbox=None): return im ## -# (New in 1.1.4) Take a snapshot of the clipboard image, if any. -# -# @return An image, a list of filenames, or None if the clipboard does -# not contain image data or filenames. Note that if a list is -# returned, the filenames may not represent image files. -# @since 1.1.4 def grabclipboard(): + """ + Take a snapshot of the clipboard image, if any. + + .. versionadded:: 1.1.4 + + :return: An image, a list of filenames, or None if the clipboard does + not contain image data or filenames. Note that if a list is + returned, the filenames may not represent image files. + """ debug = 0 # temporary interface data = Image.core.grabclipboard(debug) if isinstance(data, bytes): diff --git a/docs/reference/ImageGrab.rst b/docs/reference/ImageGrab.rst new file mode 100644 index 000000000..28865ccc3 --- /dev/null +++ b/docs/reference/ImageGrab.rst @@ -0,0 +1,15 @@ +.. py:module:: PIL.ImageGrab +.. py:currentmodule:: PIL.ImageGrab + +:mod:`ImageGrab` Module (Windows-only) +====================================== + +The :py:mod:`ImageGrab` module can be used to copy the contents of the screen +or the clipboard to a PIL image memory. + +.. note:: The current version works on Windows only. + +.. versionadded:: 1.1.3 + +.. autofunction:: PIL.ImageGrab.grab +.. autofunction:: PIL.ImageGrab.grabclipboard diff --git a/docs/reference/index.rst b/docs/reference/index.rst index 5f8d6fac3..5bee86b49 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -12,4 +12,5 @@ Reference ImageFile ImageFilter ImageFont + ImageGrab ../PIL From e2d88b5a518393e1a8ab2b067578159595414e57 Mon Sep 17 00:00:00 2001 From: Stephen Johnson Date: Sat, 12 Oct 2013 17:40:14 -0700 Subject: [PATCH 064/107] Fully document ImageMath --- PIL/ImageMath.py | 20 +++--- docs/reference/ImageMath.rst | 127 +++++++++++++++++++++++++++++++++++ docs/reference/index.rst | 1 + 3 files changed, 139 insertions(+), 9 deletions(-) create mode 100644 docs/reference/ImageMath.rst diff --git a/PIL/ImageMath.py b/PIL/ImageMath.py index 395323fd3..b2355ed1d 100644 --- a/PIL/ImageMath.py +++ b/PIL/ImageMath.py @@ -199,17 +199,19 @@ for k, v in list(globals().items()): if k[:10] == "imagemath_": ops[k[10:]] = v -## -# Evaluates an image expression. -# -# @param expression A string containing a Python-style expression. -# @keyparam options Values to add to the evaluation context. You -# can either use a dictionary, or one or more keyword arguments. -# @return The evaluated expression. This is usually an image object, -# but can also be an integer, a floating point value, or a pixel -# tuple, depending on the expression. def eval(expression, _dict={}, **kw): + """ + Evaluates an image expression. + + :param expression: A string containing a Python-style expression. + :param options: Values to add to the evaluation context. You + can either use a dictionary, or one or more keyword + arguments. + :return: The evaluated expression. This is usually an image object, but can + also be an integer, a floating point value, or a pixel tuple, + depending on the expression. + """ # build execution namespace args = ops.copy() diff --git a/docs/reference/ImageMath.rst b/docs/reference/ImageMath.rst new file mode 100644 index 000000000..6327987ed --- /dev/null +++ b/docs/reference/ImageMath.rst @@ -0,0 +1,127 @@ +.. py:module:: PIL.ImageMath +.. py:currentmodule:: PIL.ImageMath + +:mod:`ImageMath` Module +======================= + +The :py:mod:`ImageMath` module can be used to evaluate “image expressions”. The +module provides a single eval function, which takes an expression string and +one or more images. + +Example: Using the :py:mod:`~PIL.ImageMath` module +-------------------------------------------------- + +.. code-block:: python + + import Image, ImageMath + + im1 = Image.open("image1.jpg") + im2 = Image.open("image2.jpg") + + out = ImageMath.eval("convert(min(a, b), 'L')", a=im1, b=im2) + out.save("result.png") + +.. py:function:: eval(expression, environment) + + Evaluate expression in the given environment. + + In the current version, :py:mod:`~PIL.ImageMath` only supports + single-layer images. To process multi-band images, use the + :py:meth:`~PIL.Image.Image.split` method or :py:func:`~PIL.Image.merge` + function. + + :param expression: A string which uses the standard Python expression + syntax. In addition to the standard operators, you can + also use the functions described below. + :param environment: A dictionary that maps image names to Image instances. + You can use one or more keyword arguments instead of a + dictionary, as shown in the above example. Note that + the names must be valid Python identifiers. + :return: An image, an integer value, a floating point value, + or a pixel tuple, depending on the expression. + +Expression syntax +----------------- + +Expressions are standard Python expressions, but they’re evaluated in a +non-standard environment. You can use PIL methods as usual, plus the following +set of operators and functions: + +Standard Operators +^^^^^^^^^^^^^^^^^^ + +You can use standard arithmetical operators for addition (+), subtraction (-), +multiplication (*), and division (/). + +The module also supports unary minus (-), modulo (%), and power (**) operators. + +Note that all operations are done with 32-bit integers or 32-bit floating +point values, as necessary. For example, if you add two 8-bit images, the +result will be a 32-bit integer image. If you add a floating point constant to +an 8-bit image, the result will be a 32-bit floating point image. + +You can force conversion using the :py:func:`~PIL.ImageMath.convert`, +:py:func:`~PIL.ImageMath.float`, and :py:func:`~PIL.ImageMath.int` functions +described below. + +Bitwise Operators +^^^^^^^^^^^^^^^^^ + +The module also provides operations that operate on individual bits. This +includes and (&), or (|), and exclusive or (^). You can also invert (~) all +pixel bits. + +Note that the operands are converted to 32-bit signed integers before the +bitwise operation is applied. This means that you’ll get negative values if +you invert an ordinary greyscale image. You can use the and (&) operator to +mask off unwanted bits. + +Bitwise operators don’t work on floating point images. + +Logical Operators +^^^^^^^^^^^^^^^^^ + +Logical operators like :keyword:`and`, :keyword:`or`, and :keyword:`not` work +on entire images, rather than individual pixels. + +An empty image (all pixels zero) is treated as false. All other images are +treated as true. + +Note that :keyword:`and` and :keyword:`or` return the last evaluated operand, +while not always returns a boolean value. + +Built-in Functions +^^^^^^^^^^^^^^^^^^ + +These functions are applied to each individual pixel. + +.. py:currentmodule:: None + +.. py:function:: abs(image) + + Absolute value. + +.. py:function:: convert(image, mode) + + Convert image to the given mode. The mode must be given as a string + constant. + +.. py:function:: float(image) + + Convert image to 32-bit floating point. This is equivalent to + convert(image, “F”). + +.. py:function:: int(image) + + Convert image to 32-bit integer. This is equivalent to convert(image, “I”). + + Note that 1-bit and 8-bit images are automatically converted to 32-bit + integers if necessary to get a correct result. + +.. py:function:: max(image1, image2) + + Maximum value. + +.. py:function:: min(image1, image2) + + Minimum value. diff --git a/docs/reference/index.rst b/docs/reference/index.rst index 5bee86b49..5681d2c4e 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -13,4 +13,5 @@ Reference ImageFilter ImageFont ImageGrab + ImageMath ../PIL From 20b9d9774a2fb3f3be3a445b470905f9737ac362 Mon Sep 17 00:00:00 2001 From: Stephen Johnson Date: Sat, 12 Oct 2013 22:17:45 -0700 Subject: [PATCH 065/107] Fully document PIL.ImageOps; fix some :py:mod: markup --- PIL/ImageOps.py | 249 ++++++++++++++++---------------- docs/reference/Image.rst | 4 +- docs/reference/ImageChops.rst | 4 +- docs/reference/ImageColor.rst | 4 +- docs/reference/ImageDraw.rst | 4 +- docs/reference/ImageEnhance.rst | 4 +- docs/reference/ImageFile.rst | 4 +- docs/reference/ImageFilter.rst | 4 +- docs/reference/ImageFont.rst | 4 +- docs/reference/ImageGrab.rst | 4 +- docs/reference/ImageMath.rst | 4 +- docs/reference/ImageOps.rst | 27 ++++ docs/reference/index.rst | 1 + 13 files changed, 169 insertions(+), 148 deletions(-) create mode 100644 docs/reference/ImageOps.rst diff --git a/PIL/ImageOps.py b/PIL/ImageOps.py index 943c6cafb..0d22f8c64 100644 --- a/PIL/ImageOps.py +++ b/PIL/ImageOps.py @@ -22,14 +22,6 @@ from PIL._util import isStringType import operator from functools import reduce -## -# (New in 1.1.3) The ImageOps module contains a number of -# 'ready-made' image processing operations. This module is somewhat -# experimental, and most operators only work on L and RGB images. -# -# @since 1.1.3 -## - # # helpers @@ -63,20 +55,20 @@ def _lut(image, lut): # # actions -## -# Maximize (normalize) image contrast. This function calculates a -# histogram of the input image, removes cutoff percent of the -# lightest and darkest pixels from the histogram, and remaps the image -# so that the darkest pixel becomes black (0), and the lightest -# becomes white (255). -# -# @param image The image to process. -# @param cutoff How many percent to cut off from the histogram. -# @param ignore The background pixel value (use None for no background). -# @return An image. def autocontrast(image, cutoff=0, ignore=None): - "Maximize image contrast, based on histogram" + """ + Maximize (normalize) image contrast. This function calculates a + histogram of the input image, removes **cutoff** percent of the + lightest and darkest pixels from the histogram, and remaps the image + so that the darkest pixel becomes black (0), and the lightest + becomes white (255). + + :param image: The image to process. + :param cutoff: How many percent to cut off from the histogram. + :param ignore: The background pixel value (use None for no background). + :return: An image. + """ histogram = image.histogram() lut = [] for layer in range(0, len(histogram), 256): @@ -139,19 +131,19 @@ def autocontrast(image, cutoff=0, ignore=None): lut.append(ix) return _lut(image, lut) -## -# Colorize grayscale image. The black and white -# arguments should be RGB tuples; this function calculates a colour -# wedge mapping all black pixels in the source image to the first -# colour, and all white pixels to the second colour. -# -# @param image The image to colourize. -# @param black The colour to use for black input pixels. -# @param white The colour to use for white input pixels. -# @return An image. def colorize(image, black, white): - "Colorize a grayscale image" + """ + Colorize grayscale image. The **black** and **white** + arguments should be RGB tuples; this function calculates a color + wedge mapping all black pixels in the source image to the first + color, and all white pixels to the second color. + + :param image: The image to colorize. + :param black: The color to use for black input pixels. + :param white: The color to use for white input pixels. + :return: An image. + """ assert image.mode == "L" black = _color(black, "RGB") white = _color(white, "RGB") @@ -163,49 +155,50 @@ def colorize(image, black, white): image = image.convert("RGB") return _lut(image, red + green + blue) -## -# Remove border from image. The same amount of pixels are removed -# from all four sides. This function works on all image modes. -# -# @param image The image to crop. -# @param border The number of pixels to remove. -# @return An image. -# @see Image#Image.crop def crop(image, border=0): - "Crop border off image" + """ + Remove border from image. The same amount of pixels are removed + from all four sides. This function works on all image modes. + + .. seealso:: :py:meth:`~PIL.Image.Image.crop` + + :param image: The image to crop. + :param border: The number of pixels to remove. + :return: An image. + """ left, top, right, bottom = _border(border) return image.crop( (left, top, image.size[0]-right, image.size[1]-bottom) ) -## -# Deform the image. -# -# @param image The image to deform. -# @param deformer A deformer object. Any object that implements a -# getmesh method can be used. -# @param resample What resampling filter to use. -# @return An image. def deform(image, deformer, resample=Image.BILINEAR): - "Deform image using the given deformer" + """ + Deform the image. + + :param image: The image to deform. + :param deformer: A deformer object. Any object that implements a + **getmesh** method can be used. + :param resample: What resampling filter to use. + :return: An image. + """ return image.transform( image.size, Image.MESH, deformer.getmesh(image), resample ) -## -# Equalize the image histogram. This function applies a non-linear -# mapping to the input image, in order to create a uniform -# distribution of grayscale values in the output image. -# -# @param image The image to equalize. -# @param mask An optional mask. If given, only the pixels selected by -# the mask are included in the analysis. -# @return An image. def equalize(image, mask=None): - "Equalize image histogram" + """ + Equalize the image histogram. This function applies a non-linear + mapping to the input image, in order to create a uniform + distribution of grayscale values in the output image. + + :param image: The image to equalize. + :param mask: An optional mask. If given, only the pixels selected by + the mask are included in the analysis. + :return: An image. + """ if image.mode == "P": image = image.convert("RGB") h = image.histogram(mask) @@ -225,15 +218,16 @@ def equalize(image, mask=None): n = n + h[i+b] return _lut(image, lut) -## -# Add border to the image -# -# @param image The image to expand. -# @param border Border width, in pixels. -# @param fill Pixel fill value (a colour value). Default is 0 (black). -# @return An image. def expand(image, border=0, fill=0): + """ + Add border to the image + + :param image: The image to expand. + :param border: Border width, in pixels. + :param fill: Pixel fill value (a color value). Default is 0 (black). + :return: An image. + """ "Add border to image" left, top, right, bottom = _border(border) width = left + image.size[0] + right @@ -242,33 +236,32 @@ def expand(image, border=0, fill=0): out.paste(image, (left, top)) return out -## -# Returns a sized and cropped version of the image, cropped to the -# requested aspect ratio and size. -#

-# The fit function was contributed by Kevin Cazabon. -# -# @param size The requested output size in pixels, given as a -# (width, height) tuple. -# @param method What resampling method to use. Default is Image.NEAREST. -# @param bleed Remove a border around the outside of the image (from all -# four edges. The value is a decimal percentage (use 0.01 for one -# percent). The default value is 0 (no border). -# @param centering Control the cropping position. Use (0.5, 0.5) for -# center cropping (e.g. if cropping the width, take 50% off of the -# left side, and therefore 50% off the right side). (0.0, 0.0) -# will crop from the top left corner (i.e. if cropping the width, -# take all of the crop off of the right side, and if cropping the -# height, take all of it off the bottom). (1.0, 0.0) will crop -# from the bottom left corner, etc. (i.e. if cropping the width, -# take all of the crop off the left side, and if cropping the height -# take none from the top, and therefore all off the bottom). -# @return An image. def fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)): """ - This method returns a sized and cropped version of the image, - cropped to the aspect ratio and size that you request. + Returns a sized and cropped version of the image, cropped to the + requested aspect ratio and size. + + This function was contributed by Kevin Cazabon. + + :param size: The requested output size in pixels, given as a + (width, height) tuple. + :param method: What resampling method to use. Default is + :py:attr:`PIL.Image.NEAREST`. + :param bleed: Remove a border around the outside of the image (from all + four edges. The value is a decimal percentage (use 0.01 for + one percent). The default value is 0 (no border). + :param centering: Control the cropping position. Use (0.5, 0.5) for + center cropping (e.g. if cropping the width, take 50% off + of the left side, and therefore 50% off the right side). + (0.0, 0.0) will crop from the top left corner (i.e. if + cropping the width, take all of the crop off of the right + side, and if cropping the height, take all of it off the + bottom). (1.0, 0.0) will crop from the bottom left + corner, etc. (i.e. if cropping the width, take all of the + crop off the left side, and if cropping the height take + none from the top, and therefore all off the bottom). + :return: An image. """ # by Kevin Cazabon, Feb 17/2000 @@ -336,73 +329,73 @@ def fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)): # resize the image and return it return out.resize(size, method) -## -# Flip the image vertically (top to bottom). -# -# @param image The image to flip. -# @return An image. def flip(image): - "Flip image vertically" + """ + Flip the image vertically (top to bottom). + + :param image: The image to flip. + :return: An image. + """ return image.transpose(Image.FLIP_TOP_BOTTOM) -## -# Convert the image to grayscale. -# -# @param image The image to convert. -# @return An image. def grayscale(image): - "Convert to grayscale" + """ + Convert the image to grayscale. + + :param image: The image to convert. + :return: An image. + """ return image.convert("L") -## -# Invert (negate) the image. -# -# @param image The image to invert. -# @return An image. def invert(image): - "Invert image (negate)" + """ + Invert (negate) the image. + + :param image: The image to invert. + :return: An image. + """ lut = [] for i in range(256): lut.append(255-i) return _lut(image, lut) -## -# Flip image horizontally (left to right). -# -# @param image The image to mirror. -# @return An image. def mirror(image): - "Flip image horizontally" + """ + Flip image horizontally (left to right). + + :param image: The image to mirror. + :return: An image. + """ return image.transpose(Image.FLIP_LEFT_RIGHT) -## -# Reduce the number of bits for each colour channel. -# -# @param image The image to posterize. -# @param bits The number of bits to keep for each channel (1-8). -# @return An image. def posterize(image, bits): - "Reduce the number of bits per color channel" + """ + Reduce the number of bits for each color channel. + + :param image: The image to posterize. + :param bits: The number of bits to keep for each channel (1-8). + :return: An image. + """ lut = [] mask = ~(2**(8-bits)-1) for i in range(256): lut.append(i & mask) return _lut(image, lut) -## -# Invert all pixel values above a threshold. -# -# @param image The image to posterize. -# @param threshold All pixels above this greyscale level are inverted. -# @return An image. def solarize(image, threshold=128): - "Invert all values above threshold" + """ + Invert all pixel values above a threshold. + + :param image: The image to posterize. + :param threshold: All pixels above this greyscale level are inverted. + :return: An image. + """ lut = [] for i in range(256): if i < threshold: diff --git a/docs/reference/Image.rst b/docs/reference/Image.rst index 107c8a753..fe13c882b 100644 --- a/docs/reference/Image.rst +++ b/docs/reference/Image.rst @@ -1,8 +1,8 @@ .. py:module:: PIL.Image .. py:currentmodule:: PIL.Image -:mod:`Image` Module -=================== +:py:mod:`Image` Module +====================== The :py:mod:`~PIL.Image` module provides a class with the same name which is used to represent a PIL image. The module also provides a number of factory diff --git a/docs/reference/ImageChops.rst b/docs/reference/ImageChops.rst index d2e921c59..c95363c5d 100644 --- a/docs/reference/ImageChops.rst +++ b/docs/reference/ImageChops.rst @@ -1,8 +1,8 @@ .. py:module:: PIL.ImageChops .. py:currentmodule:: PIL.ImageChops -:mod:`ImageChops` Module -======================== +:py:mod:`ImageChops` Module +=========================== The :py:mod:`ImageChops` module contains a number of arithmetical image operations, called channel operations (“chops”). These can be used for various diff --git a/docs/reference/ImageColor.rst b/docs/reference/ImageColor.rst index 3115b975f..da9b406e1 100644 --- a/docs/reference/ImageColor.rst +++ b/docs/reference/ImageColor.rst @@ -1,8 +1,8 @@ .. py:module:: PIL.ImageColor .. py:currentmodule:: PIL.ImageColor -:mod:`ImageColor` Module -======================== +:py:mod:`ImageColor` Module +=========================== The :py:mod:`ImageColor` module contains color tables and converters from CSS3-style color specifiers to RGB tuples. This module is used by diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index c3210f6fc..68855eb5b 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -1,8 +1,8 @@ .. py:module:: PIL.ImageDraw .. py:currentmodule:: PIL.ImageDraw -:mod:`ImageDraw` Module -======================= +:py:mod:`ImageDraw` Module +========================== The :py:mod:`ImageDraw` module provide simple 2D graphics for :py:class:`~PIL.Image.Image` objects. You can use this module to create new diff --git a/docs/reference/ImageEnhance.rst b/docs/reference/ImageEnhance.rst index e951b8e53..e6eae85f0 100644 --- a/docs/reference/ImageEnhance.rst +++ b/docs/reference/ImageEnhance.rst @@ -1,8 +1,8 @@ .. py:module:: PIL.ImageEnhance .. py:currentmodule:: PIL.ImageEnhance -:mod:`ImageEnhance` Module -========================== +:py:mod:`ImageEnhance` Module +============================= The :py:mod:`ImageEnhance` module contains a number of classes that can be used for image enhancement. diff --git a/docs/reference/ImageFile.rst b/docs/reference/ImageFile.rst index 57476780d..9612658e9 100644 --- a/docs/reference/ImageFile.rst +++ b/docs/reference/ImageFile.rst @@ -1,8 +1,8 @@ .. py:module:: PIL.ImageFile .. py:currentmodule:: PIL.ImageFile -:mod:`ImageFile` Module -======================= +:py:mod:`ImageFile` Module +========================== The :py:mod:`ImageFile` module provides support functions for the image open and save functions. diff --git a/docs/reference/ImageFilter.rst b/docs/reference/ImageFilter.rst index 9866792ff..e89fafbcf 100644 --- a/docs/reference/ImageFilter.rst +++ b/docs/reference/ImageFilter.rst @@ -1,8 +1,8 @@ .. py:module:: PIL.ImageFilter .. py:currentmodule:: PIL.ImageFilter -:mod:`ImageFilter` Module -========================= +:py:mod:`ImageFilter` Module +============================ The :py:mod:`ImageFilter` module contains definitions for a pre-defined set of filters, which can be be used with the :py:meth:`Image.filter() diff --git a/docs/reference/ImageFont.rst b/docs/reference/ImageFont.rst index c93df7d3b..166d977a6 100644 --- a/docs/reference/ImageFont.rst +++ b/docs/reference/ImageFont.rst @@ -1,8 +1,8 @@ .. py:module:: PIL.ImageFont .. py:currentmodule:: PIL.ImageFont -:mod:`ImageFont` Module -======================= +:py:mod:`ImageFont` Module +========================== The :py:mod:`ImageFont` module defines a class with the same name. Instances of this class store bitmap fonts, and are used with the diff --git a/docs/reference/ImageGrab.rst b/docs/reference/ImageGrab.rst index 28865ccc3..f22847b5d 100644 --- a/docs/reference/ImageGrab.rst +++ b/docs/reference/ImageGrab.rst @@ -1,8 +1,8 @@ .. py:module:: PIL.ImageGrab .. py:currentmodule:: PIL.ImageGrab -:mod:`ImageGrab` Module (Windows-only) -====================================== +:py:mod:`ImageGrab` Module (Windows-only) +========================================= The :py:mod:`ImageGrab` module can be used to copy the contents of the screen or the clipboard to a PIL image memory. diff --git a/docs/reference/ImageMath.rst b/docs/reference/ImageMath.rst index 6327987ed..e3f9ed8d6 100644 --- a/docs/reference/ImageMath.rst +++ b/docs/reference/ImageMath.rst @@ -1,8 +1,8 @@ .. py:module:: PIL.ImageMath .. py:currentmodule:: PIL.ImageMath -:mod:`ImageMath` Module -======================= +:py:mod:`ImageMath` Module +========================== The :py:mod:`ImageMath` module can be used to evaluate “image expressions”. The module provides a single eval function, which takes an expression string and diff --git a/docs/reference/ImageOps.rst b/docs/reference/ImageOps.rst new file mode 100644 index 000000000..50cea90ca --- /dev/null +++ b/docs/reference/ImageOps.rst @@ -0,0 +1,27 @@ +.. py:module:: PIL.ImageOps +.. py:currentmodule:: PIL.ImageOps + +:py:mod:`ImageOps` Module +========================== + +The :py:mod:`ImageOps` module contains a number of ‘ready-made’ image +processing operations. This module is somewhat experimental, and most operators +only work on L and RGB images. + +Only bug fixes have been added since the Pillow fork. + +.. versionadded:: 1.1.3 + +.. autofunction:: autocontrast +.. autofunction:: colorize +.. autofunction:: crop +.. autofunction:: deform +.. autofunction:: equalize +.. autofunction:: expand +.. autofunction:: fit +.. autofunction:: flip +.. autofunction:: grayscale +.. autofunction:: invert +.. autofunction:: mirror +.. autofunction:: posterize +.. autofunction:: solarize diff --git a/docs/reference/index.rst b/docs/reference/index.rst index 5681d2c4e..b996c9160 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -14,4 +14,5 @@ Reference ImageFont ImageGrab ImageMath + ImageOps ../PIL From fdc48cba6f905ecfd218d1f0cec2a1816670ca7c Mon Sep 17 00:00:00 2001 From: Stephen Johnson Date: Sat, 12 Oct 2013 22:20:41 -0700 Subject: [PATCH 066/107] Update PIL.rst to remove documented modules --- docs/PIL.rst | 40 ---------------------------------------- 1 file changed, 40 deletions(-) diff --git a/docs/PIL.rst b/docs/PIL.rst index 2ec1e1b70..9b2cb37b9 100644 --- a/docs/PIL.rst +++ b/docs/PIL.rst @@ -86,46 +86,6 @@ can be found here. :undoc-members: :show-inheritance: -:mod:`ImageFilter` Module -------------------------- - -.. automodule:: PIL.ImageFilter - :members: - :undoc-members: - :show-inheritance: - -:mod:`ImageFont` Module ------------------------ - -.. automodule:: PIL.ImageFont - :members: - :undoc-members: - :show-inheritance: - -:mod:`ImageMath` Module ------------------------ - -.. automodule:: PIL.ImageMath - :members: - :undoc-members: - :show-inheritance: - -:mod:`ImageMode` Module ------------------------ - -.. automodule:: PIL.ImageMode - :members: - :undoc-members: - :show-inheritance: - -:mod:`ImageOps` Module ----------------------- - -.. automodule:: PIL.ImageOps - :members: - :undoc-members: - :show-inheritance: - :mod:`ImagePalette` Module -------------------------- From 0f3f6dd1a3051832b353326dfaef130de742b417 Mon Sep 17 00:00:00 2001 From: Stephen Johnson Date: Sun, 13 Oct 2013 09:56:33 -0700 Subject: [PATCH 067/107] Move ImageGrab docs back to ImageGrab.rst instead of autodoc --- PIL/ImageGrab.py | 30 +++--------------------------- docs/reference/ImageGrab.rst | 22 ++++++++++++++++++++-- 2 files changed, 23 insertions(+), 29 deletions(-) diff --git a/PIL/ImageGrab.py b/PIL/ImageGrab.py index 52668ab04..9bb190934 100644 --- a/PIL/ImageGrab.py +++ b/PIL/ImageGrab.py @@ -22,26 +22,12 @@ try: # built-in driver (1.1.3 and later) grabber = Image.core.grabscreen except AttributeError: - try: - # stand-alone driver (pil plus) - import _grabscreen - grabber = _grabscreen.grab - except ImportError: - # allow the module to be imported by autodoc - grabber = None + # stand-alone driver (pil plus) + import _grabscreen + grabber = _grabscreen.grab def grab(bbox=None): - """ - Take a snapshot of the screen. The pixels inside the bounding box are - returned as an "RGB" image. If the bounding box is omitted, the entire - screen is copied. - - .. versionadded:: 1.1.3 - - :param bbox: What region to copy. Default is the entire screen. - :return: An image - """ size, data = grabber() im = Image.frombytes( "RGB", size, data, @@ -52,18 +38,8 @@ def grab(bbox=None): im = im.crop(bbox) return im -## def grabclipboard(): - """ - Take a snapshot of the clipboard image, if any. - - .. versionadded:: 1.1.4 - - :return: An image, a list of filenames, or None if the clipboard does - not contain image data or filenames. Note that if a list is - returned, the filenames may not represent image files. - """ debug = 0 # temporary interface data = Image.core.grabclipboard(debug) if isinstance(data, bytes): diff --git a/docs/reference/ImageGrab.rst b/docs/reference/ImageGrab.rst index f22847b5d..117be885b 100644 --- a/docs/reference/ImageGrab.rst +++ b/docs/reference/ImageGrab.rst @@ -11,5 +11,23 @@ or the clipboard to a PIL image memory. .. versionadded:: 1.1.3 -.. autofunction:: PIL.ImageGrab.grab -.. autofunction:: PIL.ImageGrab.grabclipboard +.. py:function:: PIL.ImageGrab.grab(bbox=None) + + Take a snapshot of the screen. The pixels inside the bounding box are + returned as an "RGB" image. If the bounding box is omitted, the entire + screen is copied. + + .. versionadded:: 1.1.3 + + :param bbox: What region to copy. Default is the entire screen. + :return: An image + +.. py:function:: PIL.ImageGrab.grabclipboard() + + Take a snapshot of the clipboard image, if any. + + .. versionadded:: 1.1.4 + + :return: An image, a list of filenames, or None if the clipboard does + not contain image data or filenames. Note that if a list is + returned, the filenames may not represent image files. From 58e6d89f80a3fa5911a4528f8e53dc54b035772f Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Sun, 13 Oct 2013 13:05:01 -0400 Subject: [PATCH 068/107] Add history [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 0b9d7e9e4..025592e1a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.3.0 (2014-01-01) ------------------ +- Document ImageEnhance, ImageFile, ImageFilter, ImageFont, ImageGrab, ImageMath, and ImageOps + [irksep] + - Port and update docs for Image, ImageChops, ImageColor, and ImageDraw [irksep] From 7881c86bbaf6bdaaef4caab82665fe43d3f893de Mon Sep 17 00:00:00 2001 From: Stephen Johnson Date: Sun, 13 Oct 2013 21:22:18 -0700 Subject: [PATCH 069/107] Document PIL.ImagePalette as best I can --- PIL/ImagePalette.py | 30 +++++++++++++++++++----------- docs/PIL.rst | 8 -------- docs/reference/ImagePalette.rst | 21 +++++++++++++++++++++ docs/reference/index.rst | 1 + 4 files changed, 41 insertions(+), 19 deletions(-) create mode 100644 docs/reference/ImagePalette.rst diff --git a/PIL/ImagePalette.py b/PIL/ImagePalette.py index 2fa051407..61affdb19 100644 --- a/PIL/ImagePalette.py +++ b/PIL/ImagePalette.py @@ -19,11 +19,9 @@ import array from PIL import Image, ImageColor -## -# Colour palette wrapper for palette mapped images. class ImagePalette: - "Colour palette for palette mapped images" + "Color palette for palette mapped images" def __init__(self, mode = "RGB", palette = None): self.mode = mode @@ -35,14 +33,21 @@ class ImagePalette: raise ValueError("wrong palette size") def getdata(self): - # experimental: get palette contents in format suitable - # for the low-level im.putpalette primitive + """ + Get palette contents in format suitable # for the low-level + ``im.putpalette`` primitive. + + .. warning:: This method is experimental. + """ if self.rawmode: return self.rawmode, self.palette return self.mode + ";L", self.tobytes() def tobytes(self): - # experimental: convert palette to bytes + """Convert palette to bytes. + + .. warning:: This method is experimental. + """ if self.rawmode: raise ValueError("palette contains raw palette data") if isinstance(self.palette, bytes): @@ -57,7 +62,10 @@ class ImagePalette: tostring = tobytes def getcolor(self, color): - # experimental: given an rgb tuple, allocate palette entry + """Given an rgb tuple, allocate palette entry. + + .. warning:: This method is experimental. + """ if self.rawmode: raise ValueError("palette contains raw palette data") if isinstance(color, tuple): @@ -80,7 +88,10 @@ class ImagePalette: raise ValueError("unknown color specifier: %r" % color) def save(self, fp): - # (experimental) save palette to text file + """Save palette to text file. + + .. warning:: This method is experimental. + """ if self.rawmode: raise ValueError("palette contains raw palette data") if isinstance(fp, str): @@ -192,6 +203,3 @@ def load(filename): raise IOError("cannot load palette") return lut # data, rawmode - - -# add some psuedocolour palettes as well diff --git a/docs/PIL.rst b/docs/PIL.rst index 9b2cb37b9..431f8f3ee 100644 --- a/docs/PIL.rst +++ b/docs/PIL.rst @@ -86,14 +86,6 @@ can be found here. :undoc-members: :show-inheritance: -:mod:`ImagePalette` Module --------------------------- - -.. automodule:: PIL.ImagePalette - :members: - :undoc-members: - :show-inheritance: - :mod:`ImagePath` Module ----------------------- diff --git a/docs/reference/ImagePalette.rst b/docs/reference/ImagePalette.rst new file mode 100644 index 000000000..15b8aed8f --- /dev/null +++ b/docs/reference/ImagePalette.rst @@ -0,0 +1,21 @@ +.. py:module:: PIL.ImagePalette +.. py:currentmodule:: PIL.ImagePalette + +:py:mod:`ImagePalette` Module +============================= + +The :py:mod:`ImagePalette` module contains a class of the same name to +represent the color palette of palette mapped images. + +.. note:: + + This module was never well-documented. It hasn't changed since 2001, + though, so it's probably safe for you to read the source code and puzzle + out the internals if you need to. + + The :py:class:`~PIL.ImagePalette.ImagePalette` class has several methods, + but they are all marked as "experimental." Read that as you will. The + ``[source]`` link is there for a reason. + +.. autoclass:: PIL.ImagePalette.ImagePalette + :members: diff --git a/docs/reference/index.rst b/docs/reference/index.rst index b996c9160..607ffe10b 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -15,4 +15,5 @@ Reference ImageGrab ImageMath ImageOps + ImagePalette ../PIL From 6566a73bcff226bdfe8dcb24c8740318fd19a770 Mon Sep 17 00:00:00 2001 From: Stephen Johnson Date: Sun, 13 Oct 2013 21:41:21 -0700 Subject: [PATCH 070/107] Fully document PIL.ImagePath --- PIL/ImagePath.py | 11 ++---- docs/PIL.rst | 8 ----- docs/reference/ImagePath.rst | 68 ++++++++++++++++++++++++++++++++++++ docs/reference/index.rst | 1 + 4 files changed, 72 insertions(+), 16 deletions(-) create mode 100644 docs/reference/ImagePath.rst diff --git a/PIL/ImagePath.py b/PIL/ImagePath.py index 63e367063..656d5ce61 100644 --- a/PIL/ImagePath.py +++ b/PIL/ImagePath.py @@ -16,17 +16,12 @@ from PIL import Image -## -# Path wrapper. + +# the Python class below is overridden by the C implementation. + class Path: - ## - # Creates a path object. - # - # @param xy Sequence. The sequence can contain 2-tuples [(x, y), ...] - # or a flat list of numbers [x, y, ...]. - def __init__(self, xy): pass diff --git a/docs/PIL.rst b/docs/PIL.rst index 431f8f3ee..cd2252826 100644 --- a/docs/PIL.rst +++ b/docs/PIL.rst @@ -86,14 +86,6 @@ can be found here. :undoc-members: :show-inheritance: -:mod:`ImagePath` Module ------------------------ - -.. automodule:: PIL.ImagePath - :members: - :undoc-members: - :show-inheritance: - :mod:`ImageQt` Module --------------------- diff --git a/docs/reference/ImagePath.rst b/docs/reference/ImagePath.rst new file mode 100644 index 000000000..700464144 --- /dev/null +++ b/docs/reference/ImagePath.rst @@ -0,0 +1,68 @@ +.. py:module:: PIL.ImagePath +.. py:currentmodule:: PIL.ImagePath + +:py:mod:`ImagePath` Module +========================== + +The :py:mod:`ImagePath` module is used to store and manipulate 2-dimensional +vector data. Path objects can be passed to the methods on the +:py:mod:`~PIL.ImageDraw` module. + +.. py:class:: PIL.ImagePath.Path + + A path object. The coordinate list can be any sequence object containing + either 2-tuples [(x, y), …] or numeric values [x, y, …]. + + You can also create a path object from another path object. + + In 1.1.6 and later, you can also pass in any object that implements + Python’s buffer API. The buffer should provide read access, and contain C + floats in machine byte order. + + The path object implements most parts of the Python sequence interface, and + behaves like a list of (x, y) pairs. You can use len(), item access, and + slicing as usual. However, the current version does not support slice + assignment, or item and slice deletion. + + :param xy: A sequence. The sequence can contain 2-tuples [(x, y), ...] + or a flat list of numbers [x, y, ...]. + +.. py:method:: PIL.ImagePath.Path.compact(distance=2) + + Compacts the path, by removing points that are close to each other. This + method modifies the path in place, and returns the number of points left in + the path. + + **distance** is measured as `Manhattan distance`_ and defaults to two + pixels. + +.. _Manhattan distance: http://en.wikipedia.org/wiki/Manhattan_distance + +.. py:method:: PIL.ImagePath.Path.getbbox() + + Gets the bounding box of the path. + + :return: ``(x0, y0, x1, y1)`` + +.. py:method:: PIL.ImagePath.Path.map(function) + + Maps the path through a function. + +.. py:method:: PIL.ImagePath.Path.tolist(flat=0) + + Converts the path to a Python list [(x, y), …]. + + :param flat: By default, this function returns a list of 2-tuples + [(x, y), ...]. If this argument is :keyword:`True`, it + returns a flat list [x, y, ...] instead. + :return: A list of coordinates. See **flat**. + +.. py:method:: PIL.ImagePath.Path.transform(matrix) + + Transforms the path in place, using an affine transform. The matrix is a + 6-tuple (a, b, c, d, e, f), and each point is mapped as follows: + + .. code-block:: python + + xOut = xIn * a + yIn * b + c + yOut = xIn * d + yIn * e + f diff --git a/docs/reference/index.rst b/docs/reference/index.rst index 607ffe10b..8d206dbb1 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -16,4 +16,5 @@ Reference ImageMath ImageOps ImagePalette + ImagePath ../PIL From 1a72ba260a46c3783423e121d061da93cae0c953 Mon Sep 17 00:00:00 2001 From: Stephen Johnson Date: Sun, 13 Oct 2013 21:45:44 -0700 Subject: [PATCH 071/107] Fully document PIL.ImageQt --- docs/PIL.rst | 8 -------- docs/reference/ImageQt.rst | 20 ++++++++++++++++++++ docs/reference/index.rst | 1 + 3 files changed, 21 insertions(+), 8 deletions(-) create mode 100644 docs/reference/ImageQt.rst diff --git a/docs/PIL.rst b/docs/PIL.rst index cd2252826..a4c709719 100644 --- a/docs/PIL.rst +++ b/docs/PIL.rst @@ -86,14 +86,6 @@ can be found here. :undoc-members: :show-inheritance: -:mod:`ImageQt` Module ---------------------- - -.. automodule:: PIL.ImageQt - :members: - :undoc-members: - :show-inheritance: - :mod:`ImageSequence` Module --------------------------- diff --git a/docs/reference/ImageQt.rst b/docs/reference/ImageQt.rst new file mode 100644 index 000000000..2f5cccf45 --- /dev/null +++ b/docs/reference/ImageQt.rst @@ -0,0 +1,20 @@ +.. py:module:: PIL.ImageQt +.. py:currentmodule:: PIL.ImageQt + +:py:mod:`ImageQt` Module +======================== + +The :py:mod:`ImageQt` module contains support for creating PyQt4 QImage objects +from PIL images. + +.. versionadded:: 1.1.6 + +.. py:class:: ImageQt.ImageQt(image) + + Creates an :py:class:`~PIL.ImageQt.ImageQt` object from a PIL + :py:class:`~PIL.Image.Image` object. This class is a subclass of + QtGui.QImage, which means that you can pass the resulting objects directly + to PyQt4 API functions and methods. + + This operation is currently supported for mode 1, L, P, RGB, and RGBA + images. To handle other modes, you need to convert the image first. diff --git a/docs/reference/index.rst b/docs/reference/index.rst index 8d206dbb1..925487781 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -17,4 +17,5 @@ Reference ImageOps ImagePalette ImagePath + ImageQt ../PIL From f284c194ca9073ec1fca4ebf2b71cae11d1f64a4 Mon Sep 17 00:00:00 2001 From: Stephen Johnson Date: Sun, 13 Oct 2013 21:49:35 -0700 Subject: [PATCH 072/107] Fully document PIL.ImageSequence --- PIL/ImageSequence.py | 15 +++++++++------ docs/PIL.rst | 8 -------- docs/reference/ImageSequence.rst | 27 +++++++++++++++++++++++++++ docs/reference/index.rst | 1 + 4 files changed, 37 insertions(+), 14 deletions(-) create mode 100644 docs/reference/ImageSequence.rst diff --git a/PIL/ImageSequence.py b/PIL/ImageSequence.py index e94ca0b1e..513c9247b 100644 --- a/PIL/ImageSequence.py +++ b/PIL/ImageSequence.py @@ -14,15 +14,18 @@ # ## -# This class implements an iterator object that can be used to loop -# over an image sequence. class Iterator: + """ + This class implements an iterator object that can be used to loop + over an image sequence. - ## - # Create an iterator. - # - # @param im An image object. + You can use the ``[]`` operator to access elements by index. This operator + will raise an :py:exc:`IndexError` if you try to access a nonexistent + frame. + + :param im: An image object. + """ def __init__(self, im): if not hasattr(im, "seek"): diff --git a/docs/PIL.rst b/docs/PIL.rst index a4c709719..10774bcc7 100644 --- a/docs/PIL.rst +++ b/docs/PIL.rst @@ -86,14 +86,6 @@ can be found here. :undoc-members: :show-inheritance: -:mod:`ImageSequence` Module ---------------------------- - -.. automodule:: PIL.ImageSequence - :members: - :undoc-members: - :show-inheritance: - :mod:`ImageShow` Module ----------------------- diff --git a/docs/reference/ImageSequence.rst b/docs/reference/ImageSequence.rst new file mode 100644 index 000000000..85539cb9e --- /dev/null +++ b/docs/reference/ImageSequence.rst @@ -0,0 +1,27 @@ +.. py:module:: PIL.ImageSequence +.. py:currentmodule:: PIL.ImageSequence + +:py:mod:`ImageSequence` Module +============================== + +The :py:mod:`ImageSequence` module contains a wrapper class that lets you +iterate over the frames of an image sequence. + +Extracting frames from an animation +----------------------------------- + +.. code-block:: python + + from PIL import Image, ImageSequence + + im = Image.open("animation.fli") + + index = 1 + for frame in ImageSequence.Iterator(im): + frame.save("frame%d.png" % index) + index = index + 1 + +The :py:class:`~PIL.ImageSequence.Iterator` class +------------------------------------------------- + +.. autoclass:: PIL.ImageSequence.Iterator diff --git a/docs/reference/index.rst b/docs/reference/index.rst index 925487781..929ac099e 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -18,4 +18,5 @@ Reference ImagePalette ImagePath ImageQt + ImageSequence ../PIL From 81ea5c35cba7f004881ae4fae1eafc135d6d094f Mon Sep 17 00:00:00 2001 From: Stephen Johnson Date: Sun, 13 Oct 2013 21:57:07 -0700 Subject: [PATCH 073/107] Fully document PIL.ImageStat --- PIL/ImageStat.py | 17 ------------ docs/PIL.rst | 8 ------ docs/reference/ImageStat.rst | 53 ++++++++++++++++++++++++++++++++++++ docs/reference/index.rst | 1 + 4 files changed, 54 insertions(+), 25 deletions(-) create mode 100644 docs/reference/ImageStat.rst diff --git a/PIL/ImageStat.py b/PIL/ImageStat.py index 508d6b46e..ef63b7857 100644 --- a/PIL/ImageStat.py +++ b/PIL/ImageStat.py @@ -25,25 +25,8 @@ from PIL import Image import operator, math from functools import reduce -## -# The ImageStat module calculates global statistics for an -# image, or a region of an image. -## - -## -# Calculate statistics for the given image. If a mask is included, -# only the regions covered by that mask are included in the -# statistics. class Stat: - "Get image or feature statistics" - - ## - # Create a statistics object. - # - # @def __init__(image, mask=None) - # @param image A PIL image, or a precalculate histogram. - # @param mask An optional mask. def __init__(self, image_or_list, mask = None): try: diff --git a/docs/PIL.rst b/docs/PIL.rst index 10774bcc7..f5ebb69a4 100644 --- a/docs/PIL.rst +++ b/docs/PIL.rst @@ -94,14 +94,6 @@ can be found here. :undoc-members: :show-inheritance: -:mod:`ImageStat` Module ------------------------ - -.. automodule:: PIL.ImageStat - :members: - :undoc-members: - :show-inheritance: - :mod:`ImageTk` Module --------------------- diff --git a/docs/reference/ImageStat.rst b/docs/reference/ImageStat.rst new file mode 100644 index 000000000..c8dfe3062 --- /dev/null +++ b/docs/reference/ImageStat.rst @@ -0,0 +1,53 @@ +.. py:module:: PIL.ImageStat +.. py:currentmodule:: PIL.ImageStat + +:py:mod:`ImageStat` Module +========================== + +The :py:mod:`ImageStat` module calculates global statistics for an image, or +for a region of an image. + +.. py:class:: PIL.ImageStat.Stat(image_or_list, mask=None) + + Calculate statistics for the given image. If a mask is included, + only the regions covered by that mask are included in the + statistics. You can also pass in a previously calculated histogram. + + :param image: A PIL image, or a precalculated histogram. + :param mask: An optional mask. + + .. py:attribute:: extrema + + Min/max values for each band in the image. + + .. py:attribute:: count + + Total number of pixels. + + .. py:attribute:: sum + + Sum of all pixels. + + .. py:attribute:: sum2 + + Squared sum of all pixels. + + .. py:attribute:: pixel + + Average pixel level. + + .. py:attribute:: median + + Median pixel level. + + .. py:attribute:: rms + + RMS (root-mean-square). + + .. py:attribute:: var + + Variance. + + .. py:attribute:: stddev + + Standard deviation. diff --git a/docs/reference/index.rst b/docs/reference/index.rst index 929ac099e..fa0522b99 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -19,4 +19,5 @@ Reference ImagePath ImageQt ImageSequence + ImageStat ../PIL From 4b4f090258450d6853d98fb71d7775e7e5a69d20 Mon Sep 17 00:00:00 2001 From: Stephen Johnson Date: Sun, 13 Oct 2013 22:09:02 -0700 Subject: [PATCH 074/107] Fully document PIL.ImageTk --- PIL/ImageTk.py | 157 ++++++++++++++++------------------ docs/PIL.rst | 8 -- docs/reference/ImageChops.rst | 4 +- docs/reference/ImageTk.rst | 16 ++++ docs/reference/index.rst | 1 + 5 files changed, 94 insertions(+), 92 deletions(-) create mode 100644 docs/reference/ImageTk.rst diff --git a/PIL/ImageTk.py b/PIL/ImageTk.py index 1b75422fc..1e81d240e 100644 --- a/PIL/ImageTk.py +++ b/PIL/ImageTk.py @@ -34,13 +34,6 @@ except ImportError: from PIL import Image -## -# The ImageTk module contains support to create and modify -# Tkinter BitmapImage and PhotoImage objects. -#

-# For examples, see the demo programs in the Scripts -# directory. -## # -------------------------------------------------------------------- # Check for Tkinter interface hooks @@ -61,28 +54,25 @@ def _pilbitmap_check(): # -------------------------------------------------------------------- # PhotoImage -## -# Creates a Tkinter-compatible photo image. This can be used -# everywhere Tkinter expects an image object. If the image is an RGBA -# image, pixels having alpha 0 are treated as transparent. - class PhotoImage: + """ + A Tkinter-compatible photo image. This can be used + everywhere Tkinter expects an image object. If the image is an RGBA + image, pixels having alpha 0 are treated as transparent. - ## - # Create a photo image object. The constructor takes either - # a PIL image, or a mode and a size. Alternatively, you can - # use the file or data options to initialize - # the photo image object. - #

- # @def __init__(image=None, size=None, **options) - # @param image Either a PIL image, or a mode string. If a - # mode string is used, a size must also be given. - # @param size If the first argument is a mode string, this - # defines the size of the image. - # @keyparam file A filename to load the image from (using - # Image.open(file)). - # @keyparam data An 8-bit string containing image data (as - # loaded from an image file). + The constructor takes either a PIL image, or a mode and a size. + Alternatively, you can use the **file** or **data** options to initialize + the photo image object. + + :param image: Either a PIL image, or a mode string. If a mode string is + used, a size must also be given. + :param size: If the first argument is a mode string, this defines the size + of the image. + :keyword file: A filename to load the image from (using + ``Image.open(file)``). + :keyword data: An 8-bit string containing image data (as loaded from an + image file). + """ def __init__(self, image=None, size=None, **kw): @@ -130,44 +120,48 @@ class PhotoImage: except: pass # ignore internal errors - ## - # Get the Tkinter photo image identifier. This method is - # automatically called by Tkinter whenever a PhotoImage object is - # passed to a Tkinter method. - # - # @return A Tkinter photo image identifier (a string). def __str__(self): + """ + Get the Tkinter photo image identifier. This method is automatically + called by Tkinter whenever a PhotoImage object is passed to a Tkinter + method. + + :return: A Tkinter photo image identifier (a string). + """ return str(self.__photo) - ## - # Get the width of the image. - # - # @return The width, in pixels. def width(self): + """ + Get the width of the image. + + :return: The width, in pixels. + """ return self.__size[0] - ## - # Get the height of the image. - # - # @return The height, in pixels. def height(self): + """ + Get the height of the image. + + :return: The height, in pixels. + """ return self.__size[1] - ## - # Paste a PIL image into the photo image. Note that this can - # be very slow if the photo image is displayed. - # - # @param im A PIL image. The size must match the target region. - # If the mode does not match, the image is converted to the - # mode of the bitmap image. - # @param box A 4-tuple defining the left, upper, right, and - # lower pixel coordinate. If None is given instead of a - # tuple, all of the image is assumed. def paste(self, im, box=None): + """ + Paste a PIL image into the photo image. Note that this can + be very slow if the photo image is displayed. + + :param im: A PIL image. The size must match the target region. If the + mode does not match, the image is converted to the mode of + the bitmap image. + :param box: A 4-tuple defining the left, upper, right, and lower pixel + coordinate. If None is given instead of a tuple, all of + the image is assumed. + """ # convert to blittable im.load() @@ -197,24 +191,21 @@ class PhotoImage: # -------------------------------------------------------------------- # BitmapImage -## -# Create a Tkinter-compatible bitmap image. This can be used -# everywhere Tkinter expects an image object. class BitmapImage: + """ - ## - # Create a Tkinter-compatible bitmap image. - #

- # The given image must have mode "1". Pixels having value 0 are - # treated as transparent. Options, if any, are passed on to - # Tkinter. The most commonly used option is foreground, - # which is used to specify the colour for the non-transparent - # parts. See the Tkinter documentation for information on how to - # specify colours. - # - # @def __init__(image=None, **options) - # @param image A PIL image. + A Tkinter-compatible bitmap image. This can be used everywhere Tkinter + expects an image object. + + The given image must have mode "1". Pixels having value 0 are treated as + transparent. Options, if any, are passed on to Tkinter. The most commonly + used option is **foreground**, which is used to specify the color for the + non-transparent parts. See the Tkinter documentation for information on + how to specify colours. + + :param image: A PIL image. + """ def __init__(self, image=None, **kw): @@ -249,36 +240,38 @@ class BitmapImage: except: pass # ignore internal errors - ## - # Get the width of the image. - # - # @return The width, in pixels. def width(self): + """ + Get the width of the image. + + :return: The width, in pixels. + """ return self.__size[0] - ## - # Get the height of the image. - # - # @return The height, in pixels. def height(self): + """ + Get the height of the image. + + :return: The height, in pixels. + """ return self.__size[1] - ## - # Get the Tkinter bitmap image identifier. This method is - # automatically called by Tkinter whenever a BitmapImage object - # is passed to a Tkinter method. - # - # @return A Tkinter bitmap image identifier (a string). def __str__(self): + """ + Get the Tkinter bitmap image identifier. This method is automatically + called by Tkinter whenever a BitmapImage object is passed to a Tkinter + method. + + :return: A Tkinter bitmap image identifier (a string). + """ return str(self.__photo) -## -# Copies the contents of a PhotoImage to a PIL image memory. def getimage(photo): + """Copies the contents of a PhotoImage to a PIL image memory.""" photo.tk.call("PyImagingPhotoGet", photo) # -------------------------------------------------------------------- diff --git a/docs/PIL.rst b/docs/PIL.rst index f5ebb69a4..8c787862f 100644 --- a/docs/PIL.rst +++ b/docs/PIL.rst @@ -94,14 +94,6 @@ can be found here. :undoc-members: :show-inheritance: -:mod:`ImageTk` Module ---------------------- - -.. automodule:: PIL.ImageTk - :members: - :undoc-members: - :show-inheritance: - :mod:`ImageTransform` Module ---------------------------- diff --git a/docs/reference/ImageChops.rst b/docs/reference/ImageChops.rst index c95363c5d..8d08315b0 100644 --- a/docs/reference/ImageChops.rst +++ b/docs/reference/ImageChops.rst @@ -1,8 +1,8 @@ .. py:module:: PIL.ImageChops .. py:currentmodule:: PIL.ImageChops -:py:mod:`ImageChops` Module -=========================== +:py:mod:`ImageChops` ("Channel Operations") Module +================================================== The :py:mod:`ImageChops` module contains a number of arithmetical image operations, called channel operations (“chops”). These can be used for various diff --git a/docs/reference/ImageTk.rst b/docs/reference/ImageTk.rst new file mode 100644 index 000000000..7ee4af029 --- /dev/null +++ b/docs/reference/ImageTk.rst @@ -0,0 +1,16 @@ +.. py:module:: PIL.ImageTk +.. py:currentmodule:: PIL.ImageTk + +:py:mod:`ImageTk` Module +======================== + +The :py:mod:`ImageTk` module contains support to create and modify Tkinter +BitmapImage and PhotoImage objects from PIL images. + +For examples, see the demo programs in the Scripts directory. + +.. autoclass:: PIL.ImageTk.BitmapImage + :members: + +.. autoclass:: PIL.ImageTk.PhotoImage + :members: diff --git a/docs/reference/index.rst b/docs/reference/index.rst index fa0522b99..d6460ad70 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -20,4 +20,5 @@ Reference ImageQt ImageSequence ImageStat + ImageTk ../PIL From 7030e50b36841ee1537f85de48c9c2ddf2a9ad4b Mon Sep 17 00:00:00 2001 From: Stephen Johnson Date: Sun, 13 Oct 2013 22:19:12 -0700 Subject: [PATCH 075/107] Fully document PIL.ImageWin --- PIL/ImageWin.py | 139 ++++++++++++++++++++---------------- docs/PIL.rst | 8 --- docs/reference/ImageWin.rst | 29 ++++++++ docs/reference/index.rst | 1 + 4 files changed, 108 insertions(+), 69 deletions(-) create mode 100644 docs/reference/ImageWin.rst diff --git a/PIL/ImageWin.py b/PIL/ImageWin.py index d83763e01..aa90b887b 100644 --- a/PIL/ImageWin.py +++ b/PIL/ImageWin.py @@ -20,44 +20,49 @@ import warnings from PIL import Image -## -# The ImageWin module contains support to create and display -# images under Windows 95/98, NT, 2000 and later. class HDC: + """ + Wraps a HDC integer. The resulting object can be passed to the + :py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose` + methods. + """ def __init__(self, dc): self.dc = dc def __int__(self): return self.dc class HWND: + """ + Wraps a HWND integer. The resulting object can be passed to the + :py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose` + methods, instead of a DC. + """ def __init__(self, wnd): self.wnd = wnd def __int__(self): return self.wnd -## -# Create a Windows bitmap with the given mode and size. The mode can -# be one of "1", "L", "P", or "RGB". -# -# If the display requires a palette, this constructor creates a -# suitable palette and associates it with the image. For an "L" image, -# 128 greylevels are allocated. For an "RGB" image, a 6x6x6 colour -# cube is used, together with 20 greylevels. -# -# To make sure that palettes work properly under Windows, you must -# call the palette method upon certain events from Windows. class Dib: + """ + A Windows bitmap with the given mode and size. The mode can be one of "1", + "L", "P", or "RGB". - ## - # Create Windows bitmap. - # - # @param image Either a PIL image, or a mode string. If a - # mode string is used, a size must also be given. The - # mode can be one of "1", "L", "P", or "RGB". - # @param size If the first argument is a mode string, this - # defines the size of the image. + If the display requires a palette, this constructor creates a suitable + palette and associates it with the image. For an "L" image, 128 greylevels + are allocated. For an "RGB" image, a 6x6x6 colour cube is used, together + with 20 greylevels. + + To make sure that palettes work properly under Windows, you must call the + **palette** method upon certain events from Windows. + + :param image: Either a PIL image, or a mode string. If a mode string is + used, a size must also be given. The mode can be one of "1", + "L", "P", or "RGB". + :param size: If the first argument is a mode string, this + defines the size of the image. + """ def __init__(self, image, size=None): if hasattr(image, "mode") and hasattr(image, "size"): @@ -74,15 +79,15 @@ class Dib: if image: self.paste(image) - ## - # Copy the bitmap contents to a device context. - # - # @param handle Device context (HDC), cast to a Python integer, - # or a HDC or HWND instance. In PythonWin, you can use the - # GetHandleAttrib method of the CDC class to get - # a suitable handle. def expose(self, handle): + """ + Copy the bitmap contents to a device context. + + :param handle: Device context (HDC), cast to a Python integer, or a HDC + or HWND instance. In PythonWin, you can use the + :py:meth:`CDC.GetHandleAttrib` to get a suitable handle. + """ if isinstance(handle, HWND): dc = self.image.getdc(handle) try: @@ -94,6 +99,15 @@ class Dib: return result def draw(self, handle, dst, src=None): + """ + Same as expose, but allows you to specify where to draw the image, and + what part of it to draw. + + The destination and source areas are given as 4-tuple rectangles. If + the source is omitted, the entire image is copied. If the source and + the destination have different sizes, the image is resized as + necessary. + """ if not src: src = (0,0) + self.size if isinstance(handle, HWND): @@ -106,22 +120,22 @@ class Dib: result = self.image.draw(handle, dst, src) return result - ## - # Installs the palette associated with the image in the - # given device context. - #

- # This method should be called upon QUERYNEWPALETTE - # and PALETTECHANGED events from Windows. If this - # method returns a non-zero value, one or more display - # palette entries were changed, and the image should be - # redrawn. - # - # @param handle Device context (HDC), cast to a Python integer, - # or an HDC or HWND instance. - # @return A true value if one or more entries were changed - # (this indicates that the image should be redrawn). def query_palette(self, handle): + """ + Installs the palette associated with the image in the given device + context. + + This method should be called upon **QUERYNEWPALETTE** and + **PALETTECHANGED** events from Windows. If this method returns a + non-zero value, one or more display palette entries were changed, and + the image should be redrawn. + + :param handle: Device context (HDC), cast to a Python integer, or an + HDC or HWND instance. + :return: A true value if one or more entries were changed (this + indicates that the image should be redrawn). + """ if isinstance(handle, HWND): handle = self.image.getdc(handle) try: @@ -132,17 +146,18 @@ class Dib: result = self.image.query_palette(handle) return result - ## - # Paste a PIL image into the bitmap image. - # - # @param im A PIL image. The size must match the target region. - # If the mode does not match, the image is converted to the - # mode of the bitmap image. - # @param box A 4-tuple defining the left, upper, right, and - # lower pixel coordinate. If None is given instead of a - # tuple, all of the image is assumed. def paste(self, im, box=None): + """ + Paste a PIL image into the bitmap image. + + :param im: A PIL image. The size must match the target region. + If the mode does not match, the image is converted to the + mode of the bitmap image. + :param box: A 4-tuple defining the left, upper, right, and + lower pixel coordinate. If None is given instead of a + tuple, all of the image is assumed. + """ im.load() if self.mode != im.mode: im = im.convert(self.mode) @@ -151,21 +166,23 @@ class Dib: else: self.image.paste(im.im) - ## - # Load display memory contents from byte data. - # - # @param buffer A buffer containing display data (usually - # data returned from tobytes) def frombytes(self, buffer): + """ + Load display memory contents from byte data. + + :param buffer: A buffer containing display data (usually + data returned from tobytes) + """ return self.image.frombytes(buffer) - ## - # Copy display memory contents to bytes object. - # - # @return A bytes object containing display data. def tobytes(self): + """ + Copy display memory contents to bytes object. + + :return: A bytes object containing display data. + """ return self.image.tobytes() ## diff --git a/docs/PIL.rst b/docs/PIL.rst index 8c787862f..99f029596 100644 --- a/docs/PIL.rst +++ b/docs/PIL.rst @@ -102,14 +102,6 @@ can be found here. :undoc-members: :show-inheritance: -:mod:`ImageWin` Module ----------------------- - -.. automodule:: PIL.ImageWin - :members: - :undoc-members: - :show-inheritance: - :mod:`JpegPresets` Module ------------------------- diff --git a/docs/reference/ImageWin.rst b/docs/reference/ImageWin.rst new file mode 100644 index 000000000..2696e7e99 --- /dev/null +++ b/docs/reference/ImageWin.rst @@ -0,0 +1,29 @@ +.. py:module:: PIL.ImageWin +.. py:currentmodule:: PIL.ImageWin + +:py:mod:`ImageWin` Module (Windows-only) +======================================== + +The :py:mod:`ImageWin` module contains support to create and display images on +Windows. + +ImageWin can be used with PythonWin and other user interface toolkits that +provide access to Windows device contexts or window handles. For example, +Tkinter makes the window handle available via the winfo_id method: + +.. code-block:: python + + from PIL import ImageWin + + dib = ImageWin.Dib(...) + + hwnd = ImageWin.HWND(widget.winfo_id()) + dib.draw(hwnd, xy) + + +.. autoclass:: PIL.ImageWin.Dib + :members: + + +.. autoclass:: PIL.ImageWin.HDC +.. autoclass:: PIL.ImageWin.HWND diff --git a/docs/reference/index.rst b/docs/reference/index.rst index d6460ad70..2e148e65a 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -21,4 +21,5 @@ Reference ImageSequence ImageStat ImageTk + ImageWin ../PIL From c3de637362e309c1f78424144ca7ef668efe909b Mon Sep 17 00:00:00 2001 From: Stephen Johnson Date: Sun, 13 Oct 2013 22:30:00 -0700 Subject: [PATCH 076/107] Fully document PIL.PSDraw --- PIL/PSDraw.py | 42 ++++++++++++++++++++++++++++++++++++--- docs/PIL.rst | 8 -------- docs/reference/PSDraw.rst | 11 ++++++++++ docs/reference/index.rst | 1 + 4 files changed, 51 insertions(+), 11 deletions(-) create mode 100644 docs/reference/PSDraw.rst diff --git a/PIL/PSDraw.py b/PIL/PSDraw.py index dd4acd8af..88593bb9d 100644 --- a/PIL/PSDraw.py +++ b/PIL/PSDraw.py @@ -23,6 +23,10 @@ from PIL import EpsImagePlugin # Simple Postscript graphics interface. class PSDraw: + """ + Sets up printing to the given file. If **file** is omitted, + :py:attr:`sys.stdout` is assumed. + """ def __init__(self, fp=None): if not fp: @@ -31,7 +35,7 @@ class PSDraw: self.fp = fp def begin_document(self, id = None): - "Write Postscript DSC header" + """Set up printing of a document. (Write Postscript DSC header.)""" # FIXME: incomplete self.fp.write("%!PS-Adobe-3.0\n" "save\n" @@ -45,7 +49,7 @@ class PSDraw: self.isofont = {} def end_document(self): - "Write Postscript DSC footer" + """Ends printing. (Write Postscript DSC footer.)""" self.fp.write("%%EndDocument\n" "restore showpage\n" "%%End\n") @@ -53,6 +57,12 @@ class PSDraw: self.fp.flush() def setfont(self, font, size): + """ + Selects which font to use. + + :param font: A Postscript font name + :param size: Size in points. + """ if font not in self.isofont: # reencode font self.fp.write("/PSDraw-%s ISOLatin1Encoding /%s E\n" %\ @@ -62,23 +72,49 @@ class PSDraw: self.fp.write("/F0 %d /PSDraw-%s F\n" % (size, font)) def setink(self, ink): + """ + .. warning:: + + This has been in the PIL API for ages but was never implemented. + """ print("*** NOT YET IMPLEMENTED ***") def line(self, xy0, xy1): + """ + Draws a line between the two points. Coordinates are given in + Postscript point coordinates (72 points per inch, (0, 0) is the lower + left corner of the page). + """ xy = xy0 + xy1 self.fp.write("%d %d %d %d Vl\n" % xy) def rectangle(self, box): + """ + Draws a rectangle. + + :param box: A 4-tuple of integers whose order and function is currently + undocumented. + + Hint: the tuple is passed into this format string: + + .. code-block:: python + + %d %d M %d %d 0 Vr\n + """ self.fp.write("%d %d M %d %d 0 Vr\n" % box) def text(self, xy, text): + """ + Draws text at the given position. You must use + :py:meth:`~PIL.PSDraw.PSDraw.setfont` before calling this method. + """ text = "\\(".join(text.split("(")) text = "\\)".join(text.split(")")) xy = xy + (text,) self.fp.write("%d %d M (%s) S\n" % xy) def image(self, box, im, dpi = None): - "Write an PIL image" + """Draw a PIL image, centered in the given box.""" # default resolution depends on mode if not dpi: if im.mode == "1": diff --git a/docs/PIL.rst b/docs/PIL.rst index 99f029596..6726f661f 100644 --- a/docs/PIL.rst +++ b/docs/PIL.rst @@ -118,14 +118,6 @@ can be found here. :undoc-members: :show-inheritance: -:mod:`PSDraw` Module --------------------- - -.. automodule:: PIL.PSDraw - :members: - :undoc-members: - :show-inheritance: - :mod:`PaletteFile` Module ------------------------- diff --git a/docs/reference/PSDraw.rst b/docs/reference/PSDraw.rst new file mode 100644 index 000000000..2b5b9b340 --- /dev/null +++ b/docs/reference/PSDraw.rst @@ -0,0 +1,11 @@ +.. py:module:: PIL.PSDraw +.. py:currentmodule:: PIL.PSDraw + +:py:mod:`PSDraw` Module +======================= + +The :py:mod:`PSDraw` module provides simple print support for Postscript +printers. You can print text, graphics and images through this module. + +.. autoclass:: PIL.PSDraw.PSDraw + :members: diff --git a/docs/reference/index.rst b/docs/reference/index.rst index 2e148e65a..2d57e37be 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -22,4 +22,5 @@ Reference ImageStat ImageTk ImageWin + PSDraw ../PIL From 18e2f3fff6ff8a38fc423a47d6d53e1270722b2a Mon Sep 17 00:00:00 2001 From: David Schmidt Date: Mon, 14 Oct 2013 11:54:18 +0200 Subject: [PATCH 077/107] Update installation.rst win7 --- docs/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index 0a9934bf9..1559c674c 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -182,7 +182,7 @@ current versions of Linux, OS X, and Windows. +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | Gentoo Linux |Yes | 2.7,3.2 | 2.1.0 |x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ -| Windows 7 Pro |Yes | 2.7,3.2 | |x86 | +| Windows 7 Pro |Yes | 2.7,3.2,3.3 | 2.2.1 |x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ | Windows Server 2008 R2 Enterprise|Yes | 3.3 | |x86-64 | +----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ From b905ee8ba5c54ee1c3fd8a85793dff0f78827d65 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Mon, 14 Oct 2013 06:10:58 -0400 Subject: [PATCH 078/107] Add history [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 025592e1a..b3f52efd5 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.3.0 (2014-01-01) ------------------ +- Document remaining Image* modules listed in PIL handbook + [irksep] + - Document ImageEnhance, ImageFile, ImageFilter, ImageFont, ImageGrab, ImageMath, and ImageOps [irksep] From 4458787f5ec99820eea865049d3eaa3189403079 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Mon, 14 Oct 2013 22:47:27 -0700 Subject: [PATCH 079/107] Back to RGBx format, 3 bands in 4 bytes. Tests for internal consistency --- Tests/images/lab.tif | Bin 0 -> 24364 bytes Tests/test_image_lab.py | 25 +++++++++++++++++++++++++ libImaging/Pack.c | 2 +- libImaging/Storage.c | 5 ++--- libImaging/Unpack.c | 2 +- 5 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 Tests/images/lab.tif create mode 100644 Tests/test_image_lab.py diff --git a/Tests/images/lab.tif b/Tests/images/lab.tif new file mode 100644 index 0000000000000000000000000000000000000000..7dab9b2badda29bc0be9cc72139661865f427178 GIT binary patch literal 24364 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 ANB{r; literal 0 HcmV?d00001 diff --git a/Tests/test_image_lab.py b/Tests/test_image_lab.py new file mode 100644 index 000000000..bbc59a2c7 --- /dev/null +++ b/Tests/test_image_lab.py @@ -0,0 +1,25 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + i = Image.open('Tests/images/lab.tif') + + bits = i.load() + + assert_equal(i.mode, 'LAB') + + assert_equal(i.getbands(), ('L','A', 'B')) + + k = i.getpixel((0,0)) + assert_equal(k, (255,0,0)) + + L = i.getdata(0) + a = i.getdata(1) + b = i.getdata(2) + + assert_equal(list(L), [255]*100) + assert_equal(list(a), [0]*100) + assert_equal(list(b), [0]*100) + + diff --git a/libImaging/Pack.c b/libImaging/Pack.c index f8470fd73..d38d9e5e3 100644 --- a/libImaging/Pack.c +++ b/libImaging/Pack.c @@ -527,7 +527,7 @@ static struct { {"YCbCr", "Cr", 8, band2}, /* LAB Color */ - {"LAB", "LAB", 24, copy3}, + {"LAB", "LAB", 24, ImagingPackRGB}, {"LAB", "L", 8, band0}, {"LAB", "A", 8, band1}, {"LAB", "B", 8, band2}, diff --git a/libImaging/Storage.c b/libImaging/Storage.c index f8248a079..50259be47 100644 --- a/libImaging/Storage.c +++ b/libImaging/Storage.c @@ -182,9 +182,8 @@ ImagingNewPrologueSubtype(const char *mode, unsigned xsize, unsigned ysize, /* 24-bit color, luminance, + 2 color channels */ /* L is uint8, a,b are int8 */ im->bands = 3; - im->pixelsize = 3; - im->linesize = (xsize*4 + 3) & -4; - im->type = IMAGING_TYPE_SPECIAL; + im->pixelsize = 4; + im->linesize = xsize * 4; } else { free(im); diff --git a/libImaging/Unpack.c b/libImaging/Unpack.c index 1330f1434..e05728f49 100644 --- a/libImaging/Unpack.c +++ b/libImaging/Unpack.c @@ -965,7 +965,7 @@ static struct { {"YCbCr", "YCbCrK", 32, copy4}, /* LAB Color */ - {"LAB", "LAB", 24, copy3}, + {"LAB", "LAB", 24, ImagingUnpackRGB}, {"LAB", "L", 8, band0}, {"LAB", "A", 8, band1}, {"LAB", "B", 8, band2}, From 539f1463cc961bdd04a7befaa9233140bdc61256 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Tue, 15 Oct 2013 08:44:46 -0400 Subject: [PATCH 080/107] Make notes more prominent --- README.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index ce566acbb..3a111b457 100644 --- a/README.rst +++ b/README.rst @@ -23,9 +23,13 @@ details, and more. Introduction ------------ +.. Note:: Pillow < 2.0.0 supports Python versions 2.4, 2.5, 2.6, 2.7; Pillow >= 2.0.0 supports Python versions 2.6, 2.7, 3.2, 3.3. + .. Note:: Pillow >= 2.1.0 no longer supports "import _imaging". Please use "from PIL.Image import core as _imaging" instead. -.. Note:: Pillow < 2.0.0 supports Python versions 2.4, 2.5, 2.6, 2.7; Pillow >= 2.0.0 supports Python versions 2.6, 2.7, 3.2, 3.3. +.. Note:: Pillow >= 1.0 no longer supports "import Image". Please use "from PIL import Image" instead. + +.. Note:: PIL and Pillow currently cannot co-exist in the same environment. If you want to use Pillow, please remove PIL first. The fork author's goal is to foster active development of PIL through: @@ -42,8 +46,6 @@ For information about why this fork exists and how it differs from PIL, see Installation ------------ -.. Note:: PIL and Pillow currently cannot co-exist in the same environment. If you want to use Pillow, please remove PIL first. - You can install Pillow with ``pip``:: $ pip install Pillow From 08ac59e33c123d18284503da973dcb024874eaf5 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Tue, 15 Oct 2013 08:47:10 -0400 Subject: [PATCH 081/107] Move warnings to docs --- README.rst | 8 -------- docs/installation.rst | 6 ++++++ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/README.rst b/README.rst index 3a111b457..312c2ccc5 100644 --- a/README.rst +++ b/README.rst @@ -23,14 +23,6 @@ details, and more. Introduction ------------ -.. Note:: Pillow < 2.0.0 supports Python versions 2.4, 2.5, 2.6, 2.7; Pillow >= 2.0.0 supports Python versions 2.6, 2.7, 3.2, 3.3. - -.. Note:: Pillow >= 2.1.0 no longer supports "import _imaging". Please use "from PIL.Image import core as _imaging" instead. - -.. Note:: Pillow >= 1.0 no longer supports "import Image". Please use "from PIL import Image" instead. - -.. Note:: PIL and Pillow currently cannot co-exist in the same environment. If you want to use Pillow, please remove PIL first. - The fork author's goal is to foster active development of PIL through: - Continuous integration testing via `Travis CI `_ diff --git a/docs/installation.rst b/docs/installation.rst index 1559c674c..4c4ae18d4 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -4,6 +4,12 @@ Installation .. warning:: PIL and Pillow currently cannot co-exist in the same environment. If you want to use Pillow, please remove PIL first. +.. warning:: Pillow < 2.0.0 supports Python versions 2.4, 2.5, 2.6, 2.7; Pillow >= 2.0.0 supports Python versions 2.6, 2.7, 3.2, 3.3. + +.. warning:: Pillow >= 2.1.0 no longer supports "import _imaging". Please use "from PIL.Image import core as _imaging" instead. + +.. warning:: Pillow >= 1.0 no longer supports "import Image". Please use "from PIL import Image" instead. + Simple installation ------------------- From a556a46f5207df30d684bdd80dcca616e163d9a3 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Tue, 15 Oct 2013 08:48:52 -0400 Subject: [PATCH 082/107] Shuffle and reclassify --- docs/installation.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 4c4ae18d4..2aa74caaf 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -1,15 +1,15 @@ Installation ============ -.. warning:: PIL and Pillow currently cannot co-exist in the same environment. - If you want to use Pillow, please remove PIL first. - -.. warning:: Pillow < 2.0.0 supports Python versions 2.4, 2.5, 2.6, 2.7; Pillow >= 2.0.0 supports Python versions 2.6, 2.7, 3.2, 3.3. - .. warning:: Pillow >= 2.1.0 no longer supports "import _imaging". Please use "from PIL.Image import core as _imaging" instead. .. warning:: Pillow >= 1.0 no longer supports "import Image". Please use "from PIL import Image" instead. +.. warning:: PIL and Pillow currently cannot co-exist in the same environment. + If you want to use Pillow, please remove PIL first. + +.. Note:: Pillow < 2.0.0 supports Python versions 2.4, 2.5, 2.6, 2.7; Pillow >= 2.0.0 supports Python versions 2.6, 2.7, 3.2, 3.3. + Simple installation ------------------- From e0e4c4d52cc94334ab3b4c448a65ee2efb8b3072 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Tue, 15 Oct 2013 08:51:15 -0400 Subject: [PATCH 083/107] Shuffle --- docs/index.rst | 4 ---- docs/installation.rst | 4 +++- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index e560c2594..548be1706 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -15,10 +15,6 @@ Python Imaging Library by Fredrik Lundh and Contributors. :target: https://pypi.python.org/pypi/Pillow/ :alt: Number of PyPI downloads -.. note:: Pillow >= 2.0.0 supports Python versions 2.6, 2.7, 3.2, 3.3. - -.. note:: Pillow < 2.0.0 supports Python versions 2.4, 2.5, 2.6, 2.7. - For full compatibility, you'll want to read the complete :doc:`installation instructions `. diff --git a/docs/installation.rst b/docs/installation.rst index 2aa74caaf..b28c74b56 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -8,7 +8,9 @@ Installation .. warning:: PIL and Pillow currently cannot co-exist in the same environment. If you want to use Pillow, please remove PIL first. -.. Note:: Pillow < 2.0.0 supports Python versions 2.4, 2.5, 2.6, 2.7; Pillow >= 2.0.0 supports Python versions 2.6, 2.7, 3.2, 3.3. +.. note:: Pillow >= 2.0.0 supports Python versions 2.6, 2.7, 3.2, 3.3. + +.. note:: Pillow < 2.0.0 supports Python versions 2.4, 2.5, 2.6, 2.7. Simple installation ------------------- From 238c5b99fbe3dbbd59054300684065f8768b2b29 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Tue, 15 Oct 2013 08:53:58 -0400 Subject: [PATCH 084/107] Wording [ci skip] --- docs/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index 548be1706..e2243450d 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -15,7 +15,7 @@ Python Imaging Library by Fredrik Lundh and Contributors. :target: https://pypi.python.org/pypi/Pillow/ :alt: Number of PyPI downloads -For full compatibility, you'll want to read the complete :doc:`installation +To start using Pillow, read the :doc:`installation instructions `. If you can't find the information you need, try the old `PIL Handbook`_, but be From 4ea4abe57d4e7287413ac1aecc49290ba1740d48 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Tue, 15 Oct 2013 08:58:05 -0400 Subject: [PATCH 085/107] Don't duplicate info --- README.rst | 170 +---------------------------------------------------- 1 file changed, 1 insertion(+), 169 deletions(-) diff --git a/README.rst b/README.rst index 312c2ccc5..865992ab0 100644 --- a/README.rst +++ b/README.rst @@ -16,172 +16,4 @@ Pillow is the "friendly" PIL fork by Alex Clark and Contributors. PIL is the Pyt :target: https://pypi.python.org/pypi/Pillow/ :alt: Number of PyPI downloads -The full documentation is hosted at http://pillow.readthedocs.org/. It -contains everything in this file plus tutorials, reference, compatibility -details, and more. - -Introduction ------------- - -The fork author's goal is to foster active development of PIL through: - -- Continuous integration testing via `Travis CI `_ -- Publicized development activity on `GitHub `_ -- Regular releases to the `Python Package Index `_ -- Solicitation for community contributions and involvement on `Image-SIG `_ - -For information about why this fork exists and how it differs from PIL, see -`the About page in the documentation`_. - -.. _the About page in the documentation: http://pillow.readthedocs.org/en/latest/about.html - -Installation ------------- - -You can install Pillow with ``pip``:: - - $ pip install Pillow - -Or ``easy_install`` (for installing `Python Eggs `_, as pip does not support them):: - - $ easy_install Pillow - -Or download the compressed archive from PyPI, extract it, and inside it run:: - - $ python setup.py install - -For more information, please see http://pillow.readthedocs.org/en/latest/ or below. - -Community Support ------------------ - -Developer -~~~~~~~~~ - -PIL needs you! Please help us maintain the Python Imaging Library here: - -- GitHub (https://github.com/python-imaging/Pillow) -- Freenode (irc://irc.freenode.net#pil) -- Image-SIG (http://mail.python.org/mailman/listinfo/image-sig) - -Financial -~~~~~~~~~ - -Pillow is a volunteer effort led by Alex Clark. If you can't help with development, please help us financially; your assistance is very much needed and appreciated! - -.. Note:: Contributors: please add your name and donation preference here, as well as at the bottom of docs/index.rst. - -======================================= ======================================= -**Developer** **Preference** -======================================= ======================================= -Alex Clark (fork author) http://gittip.com/aclark4life -======================================= ======================================= - -Developer Notes ---------------- - -.. Note:: If there is a binary package for your system, that is the easiest way to install Pillow. Currently we only provide binaries for Windows (via Python eggs). - -Build from source -~~~~~~~~~~~~~~~~~ - -Many of Pillow's features require external libraries: - -* **libjpeg** provides JPEG functionality. - - * Pillow has been tested with libjpeg versions **6b**, **8**, and **9** - -* **zlib** provides access to compressed PNGs - -* **libtiff** provides group4 tiff functionality - - * Pillow has been tested with libtiff versions **3.x** and **4.0** - -* **libfreetype** provides type related services - -* **littlecms** provides color management - -* **libwebp** provides the Webp format. - - * Pillow has been tested with version **0.1.3**, which does not read transparent webp files. Version **0.3.0** supports transparency. - -* **tcl/tk** provides support for tkinter bitmap and photo images. - -If the prerequisites are installed in the standard library locations for your machine (e.g. /usr or /usr/local), no additional configuration should be required. If they are installed in a non-standard location, you may need to configure setuptools to use those locations (i.e. by editing setup.py and/or setup.cfg). Once you have installed the prerequisites, run:: - - $ pip install Pillow - -Platform-specific instructions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Linux -+++++ - -**We do not provide binaries for Linux.** If you didn't build Python from source, make sure you have Python's development libraries installed. In Debian or Ubuntu:: - - $ sudo apt-get install python-dev python-setuptools - -Or for Python 3:: - - $ sudo apt-get install python3-dev python3-setuptools - -Prerequisites are installed on **Ubuntu 10.04 LTS** with:: - - $ sudo apt-get install libtiff4-dev libjpeg62-dev zlib1g-dev libfreetype6-dev liblcms1-dev tcl8.5-dev tk8.5-dev - -Prerequisites are installed with on **Ubuntu 12.04 LTS** or **Raspian Wheezy 7.0** with:: - - $ sudo apt-get install libtiff4-dev libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms1-dev libwebp-dev tcl8.5-dev tk8.5-dev - -Distributions -^^^^^^^^^^^^^ - -.. Note:: XXX Provide links - -Additionally, many Linux distributions now include Pillow (instead of PIL) with their distribution: - -- Fedora -- Debian/Ubuntu -- ArchLinux - -Mac OS X -++++++++ - -**We do not provide binaries for OS X.** So you'll need XCode to install Pillow. (XCode 4.2 on 10.6 will work with the Official Python binary distribution. Otherwise, use whatever XCode you used to compile Python.) - -The easiest way to install the prerequisites is via `Homebrew `_. After you install Homebrew, run:: - - $ brew install libtiff libjpeg webp littlecms - -If you've built your own Python, then you should be able to install Pillow using:: - - $ pip install Pillow - -Windows -+++++++ - -We provide binaries for Windows in the form of Python Eggs and `Python Wheels `_: - -Python Eggs -^^^^^^^^^^^ - -.. Note:: Pip does not support Python Eggs; use easy_install instead. - -:: - - $ easy_install Pillow - -Python Wheels -^^^^^^^^^^^^^ - -.. Note:: Experimental. Requires Setuptools >=0.8 and Pip >=1.4.1 - -:: - - $ pip install --use-wheel Pillow - -Platform support -~~~~~~~~~~~~~~~~ - -Current platform support for Pillow is documented here: -http://pillow.readthedocs.org/en/latest/installation.html#platform-support +The full documentation is hosted at http://pillow.readthedocs.org/. It contains installation instructions, tutorials, reference, compatibility details, and more. From 6f09474b6cc56f579830810072a07519d017d15d Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Tue, 15 Oct 2013 08:58:40 -0400 Subject: [PATCH 086/107] Wording [ci skip] --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 865992ab0..78a256c0d 100644 --- a/README.rst +++ b/README.rst @@ -16,4 +16,4 @@ Pillow is the "friendly" PIL fork by Alex Clark and Contributors. PIL is the Pyt :target: https://pypi.python.org/pypi/Pillow/ :alt: Number of PyPI downloads -The full documentation is hosted at http://pillow.readthedocs.org/. It contains installation instructions, tutorials, reference, compatibility details, and more. +The documentation is hosted at http://pillow.readthedocs.org/. It contains installation instructions, tutorials, reference, compatibility details, and more. From fc474a7b6f06120d5f921c3eb39d42989a4ac16b Mon Sep 17 00:00:00 2001 From: Seth VanHeulen Date: Tue, 15 Oct 2013 11:35:53 -0400 Subject: [PATCH 087/107] Added more raw decoder 16 bit pixel formats --- libImaging/Unpack.c | 99 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 1 deletion(-) diff --git a/libImaging/Unpack.c b/libImaging/Unpack.c index d014fb815..5be744ac5 100644 --- a/libImaging/Unpack.c +++ b/libImaging/Unpack.c @@ -441,6 +441,36 @@ ImagingUnpackBGR(UINT8* out, const UINT8* in, int pixels) } } +void +ImagingUnpackRGB15(UINT8* out, const UINT8* in, int pixels) +{ + int i, pixel; + /* RGB, 5 bits per pixel */ + for (i = 0; i < pixels; i++) { + pixel = in[0] + (in[1] << 8); + out[R] = (pixel & 31) * 255 / 31; + out[G] = ((pixel>>5) & 31) * 255 / 31; + out[B] = ((pixel>>10) & 31) * 255 / 31; + out[A] = 255; + out += 4; in += 2; + } +} + +void +ImagingUnpackRGBA15(UINT8* out, const UINT8* in, int pixels) +{ + int i, pixel; + /* RGB, 5/5/5/1 bits per pixel */ + for (i = 0; i < pixels; i++) { + pixel = in[0] + (in[1] << 8); + out[R] = (pixel & 31) * 255 / 31; + out[G] = ((pixel>>5) & 31) * 255 / 31; + out[B] = ((pixel>>10) & 31) * 255 / 31; + out[A] = (pixel>>15) * 255; + out += 4; in += 2; + } +} + void ImagingUnpackBGR15(UINT8* out, const UINT8* in, int pixels) { @@ -456,6 +486,36 @@ ImagingUnpackBGR15(UINT8* out, const UINT8* in, int pixels) } } +void +ImagingUnpackBGRA15(UINT8* out, const UINT8* in, int pixels) +{ + int i, pixel; + /* RGB, reversed bytes, 5/5/5/1 bits per pixel */ + for (i = 0; i < pixels; i++) { + pixel = in[0] + (in[1] << 8); + out[B] = (pixel & 31) * 255 / 31; + out[G] = ((pixel>>5) & 31) * 255 / 31; + out[R] = ((pixel>>10) & 31) * 255 / 31; + out[A] = (pixel>>15) * 255; + out += 4; in += 2; + } +} + +void +ImagingUnpackRGB16(UINT8* out, const UINT8* in, int pixels) +{ + int i, pixel; + /* RGB, 5/6/5 bits per pixel */ + for (i = 0; i < pixels; i++) { + pixel = in[0] + (in[1] << 8); + out[R] = (pixel & 31) * 255 / 31; + out[G] = ((pixel>>5) & 63) * 255 / 63; + out[B] = ((pixel>>11) & 31) * 255 / 31; + out[A] = 255; + out += 4; in += 2; + } +} + void ImagingUnpackBGR16(UINT8* out, const UINT8* in, int pixels) { @@ -471,6 +531,36 @@ ImagingUnpackBGR16(UINT8* out, const UINT8* in, int pixels) } } +void +ImagingUnpackRGB4B(UINT8* out, const UINT8* in, int pixels) +{ + int i, pixel; + /* RGB, 4 bits per pixel */ + for (i = 0; i < pixels; i++) { + pixel = in[0] + (in[1] << 8); + out[R] = (pixel & 15) * 17; + out[G] = ((pixel>>4) & 15) * 17; + out[B] = ((pixel>>8) & 15) * 17; + out[A] = 255; + out += 4; in += 2; + } +} + +void +ImagingUnpackRGBA4B(UINT8* out, const UINT8* in, int pixels) +{ + int i, pixel; + /* RGBA, 4 bits per pixel */ + for (i = 0; i < pixels; i++) { + pixel = in[0] + (in[1] << 8); + out[R] = (pixel & 15) * 17; + out[G] = ((pixel>>4) & 15) * 17; + out[B] = ((pixel>>8) & 15) * 17; + out[A] = ((pixel>>12) & 15) * 17; + out += 4; in += 2; + } +} + static void ImagingUnpackBGRX(UINT8* out, const UINT8* in, int pixels) { @@ -889,8 +979,11 @@ static struct { {"RGB", "RGB;R", 24, unpackRGBR}, {"RGB", "RGB;16B", 48, unpackRGB16B}, {"RGB", "BGR", 24, ImagingUnpackBGR}, + {"RGB", "RGB;15", 16, ImagingUnpackRGB15}, {"RGB", "BGR;15", 16, ImagingUnpackBGR15}, + {"RGB", "RGB;16", 16, ImagingUnpackRGB16}, {"RGB", "BGR;16", 16, ImagingUnpackBGR16}, + {"RGB", "RGB;4B", 16, ImagingUnpackRGB4B}, {"RGB", "BGR;5", 16, ImagingUnpackBGR15}, /* compat */ {"RGB", "RGBX", 32, copy4}, {"RGB", "RGBX;L", 32, unpackRGBAL}, @@ -909,6 +1002,9 @@ static struct { {"RGBA", "RGBa", 32, unpackRGBa}, {"RGBA", "RGBA;I", 32, unpackRGBAI}, {"RGBA", "RGBA;L", 32, unpackRGBAL}, + {"RGBA", "RGBA;15", 16, ImagingUnpackRGBA15}, + {"RGBA", "BGRA;15", 16, ImagingUnpackBGRA15}, + {"RGBA", "RGBA;4B", 16, ImagingUnpackRGBA4B}, {"RGBA", "RGBA;16B", 64, unpackRGBA16B}, {"RGBA", "BGRA", 32, unpackBGRA}, {"RGBA", "ARGB", 32, unpackARGB}, @@ -924,8 +1020,9 @@ static struct { {"RGBX", "RGB;L", 24, unpackRGBL}, {"RGBX", "RGB;16B", 48, unpackRGB16B}, {"RGBX", "BGR", 24, ImagingUnpackBGR}, + {"RGBX", "RGB;15", 16, ImagingUnpackRGB15}, {"RGBX", "BGR;15", 16, ImagingUnpackBGR15}, - {"RGB", "BGR;16", 16, ImagingUnpackBGR16}, + {"RGBX", "RGB;4B", 16, ImagingUnpackRGB4B}, {"RGBX", "BGR;5", 16, ImagingUnpackBGR15}, /* compat */ {"RGBX", "RGBX", 32, copy4}, {"RGBX", "RGBX;L", 32, unpackRGBAL}, From 89b265313564ca2b1f2e21565a9939b7af7f5d3a Mon Sep 17 00:00:00 2001 From: Seth VanHeulen Date: Tue, 15 Oct 2013 14:10:11 -0400 Subject: [PATCH 088/107] Added tests for new 16 bit raw unpackers --- Tests/test_lib_pack.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Tests/test_lib_pack.py b/Tests/test_lib_pack.py index b3355b65f..7675348b3 100644 --- a/Tests/test_lib_pack.py +++ b/Tests/test_lib_pack.py @@ -101,8 +101,11 @@ def test_unpack(): assert_equal(unpack("RGB", "RGB;R", 3), (128, 64, 192)) assert_equal(unpack("RGB", "RGB;16B", 6), (1, 3, 5)) # ? assert_equal(unpack("RGB", "BGR", 3), (3, 2, 1)) + assert_equal(unpack("RGB", "RGB;15", 2), (8, 131, 0)) assert_equal(unpack("RGB", "BGR;15", 2), (0, 131, 8)) + assert_equal(unpack("RGB", "RGB;16", 2), (8, 64, 0)) assert_equal(unpack("RGB", "BGR;16", 2), (0, 64, 8)) + assert_equal(unpack("RGB", "RGB;4B", 2), (17, 0, 34)) assert_equal(unpack("RGB", "RGBX", 4), (1, 2, 3)) assert_equal(unpack("RGB", "BGRX", 4), (3, 2, 1)) @@ -113,11 +116,17 @@ def test_unpack(): assert_equal(unpack("RGBA", "BGRA", 4), (3, 2, 1, 4)) assert_equal(unpack("RGBA", "ARGB", 4), (2, 3, 4, 1)) assert_equal(unpack("RGBA", "ABGR", 4), (4, 3, 2, 1)) + assert_equal(unpack("RGBA", "RGBA;15", 2), (8, 131, 0, 0)) + assert_equal(unpack("RGBA", "BGRA;15", 2), (0, 131, 8, 0)) + assert_equal(unpack("RGBA", "RGBA;4B", 2), (17, 0, 34, 0)) assert_equal(unpack("RGBX", "RGBX", 4), (1, 2, 3, 4)) # 4->255? assert_equal(unpack("RGBX", "BGRX", 4), (3, 2, 1, 255)) assert_equal(unpack("RGBX", "XRGB", 4), (2, 3, 4, 255)) assert_equal(unpack("RGBX", "XBGR", 4), (4, 3, 2, 255)) + assert_equal(unpack("RGBX", "RGB;15", 2), (8, 131, 0, 255)) + assert_equal(unpack("RGBX", "BGR;15", 2), (0, 131, 8, 255)) + assert_equal(unpack("RGBX", "RGB;4B", 2), (17, 0, 34, 255)) assert_equal(unpack("CMYK", "CMYK", 4), (1, 2, 3, 4)) assert_equal(unpack("CMYK", "CMYK;I", 4), (254, 253, 252, 251)) From 1865a5c438c4d9bf40370eaa58b1003fc5d9fbdf Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 15 Oct 2013 22:01:30 -0700 Subject: [PATCH 089/107] Shifting the midpoint of the ab channels to 128. unshifting back to signed int on pack --- Tests/images/lab-green.tif | Bin 0 -> 24380 bytes Tests/images/lab-red.tif | Bin 0 -> 24380 bytes Tests/test_format_lab.py | 41 +++++++++++++++++++++++++++++++++++++ Tests/test_image_lab.py | 25 ---------------------- libImaging/Pack.c | 15 +++++++++++++- libImaging/Unpack.c | 27 +++++++++++++++++++++++- 6 files changed, 81 insertions(+), 27 deletions(-) create mode 100644 Tests/images/lab-green.tif create mode 100644 Tests/images/lab-red.tif create mode 100644 Tests/test_format_lab.py delete mode 100644 Tests/test_image_lab.py diff --git a/Tests/images/lab-green.tif b/Tests/images/lab-green.tif new file mode 100644 index 0000000000000000000000000000000000000000..76c129ee90812f8cb9934f5865437647f644e9dd GIT binary patch literal 24380 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�pxoJyuX^f_3q9Mn)_( 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 Date: Tue, 15 Oct 2013 22:04:22 -0700 Subject: [PATCH 090/107] format including the padding byte --- _imagingcms.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/_imagingcms.c b/_imagingcms.c index 1548769d5..b2903adbf 100644 --- a/_imagingcms.c +++ b/_imagingcms.c @@ -237,7 +237,8 @@ findLCMStype(char* PILmode) return TYPE_YCbCr_8; } else if (strcmp(PILmode, "LAB") == 0) { - return TYPE_Lab_8; + // LabX equvalent like ALab, but not reversed -- no #define in lcms2 + return (COLORSPACE_SH(PT_LabV2)|CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)); } else { From deb424c5cdd7c90c3aaee6e0c6bf178215bdaa5a Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 15 Oct 2013 22:04:51 -0700 Subject: [PATCH 091/107] working tests for LAB color conversions using ImagingCMS --- Tests/test_imagecms.py | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index 0725571f9..d18132598 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -94,6 +94,29 @@ def test_display_profile(): def test_lab_color_profile(): pLab = ImageCms.createProfile("LAB", 5000) pLab = ImageCms.createProfile("LAB", 6500) + +def test_simple_lab(): + i = Image.new('RGB', (10,10), (128,128,128)) + + pLab = ImageCms.createProfile("LAB") + t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") + + i_lab = ImageCms.applyTransform(i, t) + + + assert_equal(i_lab.mode, 'LAB') + + k = i_lab.getpixel((0,0)) + assert_equal(k, (137,128,128)) # not a linear luminance map. so L != 128 + + L = i_lab.getdata(0) + a = i_lab.getdata(1) + b = i_lab.getdata(2) + + assert_equal(list(L), [137]*100) + assert_equal(list(a), [128]*100) + assert_equal(list(b), [128]*100) + def test_lab_color(): pLab = ImageCms.createProfile("LAB") @@ -103,11 +126,11 @@ def test_lab_color(): i = ImageCms.applyTransform(lena(), t) assert_image(i, "LAB", (128, 128)) - i.save('temp.lab.tif') + # i.save('temp.lab.tif') # visually verified vs PS. target = Image.open('Tests/images/lena.Lab.tif') - assert_image_similar(i, target, 1) + assert_image_similar(i, target, 30) def test_lab_srgb(): pLab = ImageCms.createProfile("LAB") @@ -117,7 +140,9 @@ def test_lab_srgb(): img_srgb = ImageCms.applyTransform(img, t) - assert_image_similar(lena(), img_srgb, 1) + # img_srgb.save('temp.srgb.tif') # visually verified vs ps. + + assert_image_similar(lena(), img_srgb, 30) def test_lab_roundtrip(): # check to see if we're at least internally consistent. @@ -130,3 +155,5 @@ def test_lab_roundtrip(): out = ImageCms.applyTransform(i, t2) assert_image_similar(lena(), out, 2) + + From 646364d05bb833e3bb488d86a5fe234586de5b95 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Wed, 16 Oct 2013 06:11:00 -0400 Subject: [PATCH 092/107] Add history [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index b3f52efd5..4a102a3d3 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.3.0 (2014-01-01) ------------------ +- Added more raw decoder 16 bit pixel formats + [svanheulen] + - Document remaining Image* modules listed in PIL handbook [irksep] From f1e7b0bf0fad877c27b814a8c783330bfe59e410 Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Wed, 16 Oct 2013 12:10:54 -0400 Subject: [PATCH 093/107] Add history [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 4a102a3d3..56d763432 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 2.3.0 (2014-01-01) ------------------ +- LCMS support upgraded from version 1 to version 2, fixes #343 + [wiredfool] + - Added more raw decoder 16 bit pixel formats [svanheulen] From c91bd27eec97bf6a05f85393af1fdff143339369 Mon Sep 17 00:00:00 2001 From: hugovk Date: Thu, 17 Oct 2013 17:36:38 +0300 Subject: [PATCH 094/107] Add tests for rotated and non-rotated transposed font Check the box size of an ImageFont.TransposedFont matches that of the original ImageFont. --- Tests/test_imagefont.py | 47 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 85940d087..9ac2cdd89 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -88,5 +88,48 @@ def test_render_multiline(): # setting a tight epsilon, I'm showing the original test failure # at epsilon = ~38. assert_image_similar(im, target_img,.5) - - + + +def test_rotated_transposed_font(): + img_grey = Image.new("L", (100, 100)) + draw = ImageDraw.Draw(img_grey) + word = "testing" + font = ImageFont.truetype(font_path, font_size) + + orientation = Image.ROTATE_90 + transposed_font = ImageFont.TransposedFont(font, orientation=orientation) + + # Original font + draw.setfont(font) + box_size_a = draw.textsize(word) + + # Rotated font + draw.setfont(transposed_font) + box_size_b = draw.textsize(word) + + # Check (w,h) of box a is (h,w) of box b + assert_equal(box_size_a[0], box_size_b[1]) + assert_equal(box_size_a[1], box_size_b[0]) + + +def test_unrotated_transposed_font(): + img_grey = Image.new("L", (100, 100)) + draw = ImageDraw.Draw(img_grey) + word = "testing" + font = ImageFont.truetype(font_path, font_size) + + orientation = None + transposed_font = ImageFont.TransposedFont(font, orientation=orientation) + + # Original font + draw.setfont(font) + box_size_a = draw.textsize(word) + + # Rotated font + draw.setfont(transposed_font) + box_size_b = draw.textsize(word) + + # Check boxes a and b are same size + assert_equal(box_size_a, box_size_b) + + From 92507e5d0a692ffc3a5e61f0302a4d29ba3d6d42 Mon Sep 17 00:00:00 2001 From: hugovk Date: Thu, 17 Oct 2013 19:00:42 +0300 Subject: [PATCH 095/107] Fix issue #382: TypeError with TransposedFont's getsize() When getting the size of text with a TransposedFont it was failing: File "/usr/local/lib/python2.7/site-packages/PIL/ImageDraw.py", line 281, in textsize return font.getsize(text) File "/usr/local/lib/python2.7/site-packages/PIL/ImageFont.py", line 189, in getsize w, h = self.font.getsize(text)[0] TypeError: 'int' object is not iterable This is because self.font.getsize(text) returns a (w, h) tuple. To fix, remove the [0]. Test cases have been created in test_imagefont.py: test_rotated_transposed_font() test_unrotated_transposed_font() Both fail before the fix, both pass with the fix. Furthermore, the code I'm using this from ( https://github.com/mattdeboard/word_cloud ) now works as expected and creates a word cloud similar to the ones that PIL created. --- PIL/ImageFont.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/ImageFont.py b/PIL/ImageFont.py index 8313ee3ba..9366937dd 100644 --- a/PIL/ImageFont.py +++ b/PIL/ImageFont.py @@ -171,7 +171,7 @@ class TransposedFont: self.orientation = orientation # any 'transpose' argument, or None def getsize(self, text): - w, h = self.font.getsize(text)[0] + w, h = self.font.getsize(text) if self.orientation in (Image.ROTATE_90, Image.ROTATE_270): return h, w return w, h From 175c57243272a2f3263a44d83804b987f5db94a1 Mon Sep 17 00:00:00 2001 From: Eric Soroos Date: Sat, 19 Oct 2013 03:28:31 +0000 Subject: [PATCH 096/107] I;16 images are little endian, not native endian --- PIL/Image.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 6f53d5148..e1d88fe59 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -226,16 +226,17 @@ _MODE_CONV = { "CMYK": ('|u1', 4), "YCbCr": ('|u1', 3), "LAB": ('|u1', 3), # UNDONE - unsigned |u1i1i1 - "I;16": ('=u2', None), + # I;16 == I;16L, and I;32 == I;32L + "I;16": ('u2', None), "I;16L": ('i2', None), "I;16LS": ('u4', None), "I;32L": ('i4', None), "I;32LS": (' Date: Sat, 19 Oct 2013 03:30:02 +0000 Subject: [PATCH 097/107] uint16 varies from platform to platform, u2'), - ("I;16L", 'uint16'), + ("I;16L", ' Date: Mon, 21 Oct 2013 05:19:52 +0000 Subject: [PATCH 098/107] Tests for endian issues in decoding 16bit tif images --- Tests/images/12bit.MM.cropped.tif | Bin 0 -> 8448 bytes Tests/images/12bit.MM.deflate.tif | Bin 0 -> 5230 bytes Tests/images/12bit.deflate.tif | Bin 0 -> 5112 bytes Tests/test_file_libtiff.py | 22 ++++++++++++++++++++++ Tests/test_file_tiff.py | 25 +++++++++++++++++++++++++ 5 files changed, 47 insertions(+) create mode 100644 Tests/images/12bit.MM.cropped.tif create mode 100644 Tests/images/12bit.MM.deflate.tif create mode 100644 Tests/images/12bit.deflate.tif diff --git a/Tests/images/12bit.MM.cropped.tif b/Tests/images/12bit.MM.cropped.tif new file mode 100644 index 0000000000000000000000000000000000000000..c4c24e2eb97b134e15079c31cd02a30cc145c8df GIT binary patch literal 8448 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%0DTfbAD2!(pR(-y~XQ_PpKao33!_w?!6A% zgXz=4IHd!vniw|Jbn149dAB2Im^s0TKBBXpgXg^=oW#@8S68aAyKM<6RGs2K8*;tm z5$#YM2(IyQ*+Qt`$9hbG%rZqqx0ZZ2ZmvCMi+J@=-2ZmceYiQcic`O+KkxC})@QP! zmpC@qrtY!OS-3EER=}Ns^@D#Up$52);XSm}i^38p_J%I3j^-zD2~BOpE0W*U zfyYT9)-w^U4+KJl-aa>+i@JD1(7n^sLFX?R0swfG1=kSFH{}sP`I0|QhuIRf#5*CN z1V@C#ixjSDSwWqwnvNZk~nsr;WS0%nxeETlT0%%`ly4 zBLp1gZ&RX}JBps{)sGNPl*f(IRt~m`Rw9+hPFU4)fNcn9x4YX`F33vEH6?x{b{Hx= z93;rLzW+#&Y-?#Qm4JV_=Lv{^HI#3FEST|dGdnb5ZxzADdjgN^4npo0na|74V)LQj z%iElD%8mT>s5HaRg!vw7YxORG{K=m&oV!^^<muKdw(EL!9K9~gmO_^ zB%hFkC>&*F&HYcBmaaj3ekGs)%6&ntLng0DF21@7B|Xu z3_AMlk|XU}3=Q;n$J^;y9`Jbi*_q~2JSGM;mg2Ou8`k-D9lrDKM#qlD+hwsd53xOp z8tdMhO=UlZ#9&&rG?G1;kSCv!fRL2mjh>khH_sOTx*?m;4Ky_(?%@>xpSze`b1;Y@vu3SW@lSB(q!m^MDL-qduZooop!MGj z0uS^5P+L3xm5IMwz422TE>hC&8)?S9_CnQxsX=-kUF|DVUFmI7sDL;d1A-sT)_*+b z>LI*HT9I*E#Mlj|`=IaZ>B4yRpXZcIa-JI$NJr2V9@6g-4v?=&!amasqKL=x4jI`c zO-s7;9aOCm5Y+TZszVu%U|l<}aI~4;##FAWb`?Z_3SVKmZ-uR`TqGzwVubd~+(s<$ zTdJ4)l@-MuH=eo$C{0h#H;+XWI%sy2NswLiU6SJSH@-YoO z6hCxDCe@}!`TOEC+bcUv^))KT=5AVW8zigw`_ujtG0dc8FbAd!i-t-fZ7xweOII#P zbqg{81dIKZjVN-0ROkRvRVr=r6DyXh17I2GXKh#Q;6C|HW|8+uh;CTazO?qTYW||- z@kOTQF6^qg;`6MvVE>&{jbcr``jU%rie__Cc26H*JB-OD%%meY9x$Iafg@W|`#Q$7 zJquyV-~3~uq*Gy-+NB|$W?r#-hj}(q^F(||x_-+i8)oTmYN&Xz|Jwsrtom4M z@$!_=_VnutO84k260_paa-NXnotU@IId;+Z>}lkR|3507S(AT~_mh5KXx8s~OCkEX z-q_p?C)a#I%$Q8G?G(7i88BZSJH*JkD_5r)vb|fDL|yb3?rW{_{?@|%DcCyJx-|N9 z5MM+l#9MwAiY#FTv1%h{5}& zw0xBdu(m8L^9<8u9Sd%3+tMn6jSm;Sa~Z`QsD#4yF1Y2xes&l;p88$ZUY1*P>(*2T zv-8^+km8V`sprr>x|k9xno_pw=3TRu2v*GZs`ag@)l-=R3y2jRiaUQV^4BHBS;Mg< zBLxK|H3cOF1%=+~T}toPS*|C@RJ O_rG`JKbgNPivI(%CH_wU literal 0 HcmV?d00001 diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 037fc022d..d4767c95a 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -105,3 +105,25 @@ def test_adobe_deflate_tiff(): assert_no_exception(lambda: im.load()) +def test_little_endian(): + im = Image.open('Tests/images/12bit.deflate.tif') + assert_equal(im.getpixel((0,0)), 480) + assert_equal(im.mode, 'I;16') + + b = im.tobytes() + # Bytes are in image native order (little endian) + assert_equal(b[0], b'\xe0') + assert_equal(b[1], b'\x01') + +def test_big_endian(): + im = Image.open('Tests/images/12bit.MM.deflate.tif') + + assert_equal(im.getpixel((0,0)), 480) + assert_equal(im.mode, 'I;16B') + + b = im.tobytes() + + # Bytes are in image native order (big endian) + assert_equal(b[0], b'\x01') + assert_equal(b[1], b'\xe0') + diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index ddca24876..c88e103b4 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -71,3 +71,28 @@ def test_xyres_tiff(): im.tag.tags[Y_RESOLUTION] = (72,) im._setup() assert_equal(im.info['dpi'], (72., 72.)) + + +def test_little_endian(): + im = Image.open('Tests/images/12bit.cropped.tif') + assert_equal(im.getpixel((0,0)), 480) + assert_equal(im.mode, 'I;16') + + b = im.tobytes() + # Bytes are in image native order (little endian) + assert_equal(b[0], b'\xe0') + assert_equal(b[1], b'\x01') + +def test_big_endian(): + im = Image.open('Tests/images/12bit.MM.cropped.tif') + assert_equal(im.getpixel((0,0)), 480) + assert_equal(im.mode, 'I;16B') + + b = im.tobytes() + + # Bytes are in image native order (big endian) + assert_equal(b[0], b'\x01') + assert_equal(b[1], b'\xe0') + + + From 6f8d968cbbe1d00ffebf28768aad01178c00bc0c Mon Sep 17 00:00:00 2001 From: Eric Soroos Date: Mon, 21 Oct 2013 22:17:33 +0000 Subject: [PATCH 099/107] MM and II 16 bit integer tiffs unpack properly using libtiff on bigendian platform --- PIL/TiffImagePlugin.py | 6 ++++++ libImaging/Storage.c | 3 ++- libImaging/Unpack.c | 24 ++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 25ad3578e..f4a68d1fd 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -804,6 +804,12 @@ class TiffImageFile(ImageFile.ImageFile): # fillorder==2 modes have a corresponding # fillorder=1 mode self.mode, rawmode = OPEN_INFO[key] + # libtiff always returns the bytes in native order. + # we're expecting image byte order. So, if the rawmode + # contains I;16, we need to convert from native to image + # byte order. + if self.mode in ('I;16B', 'I;16'): + rawmode = 'I;16N' # Offset in the tile tuple is 0, we go from 0,0 to # w,h, and we only do this once -- eds diff --git a/libImaging/Storage.c b/libImaging/Storage.c index 50259be47..314f462c8 100644 --- a/libImaging/Storage.c +++ b/libImaging/Storage.c @@ -105,7 +105,8 @@ ImagingNewPrologueSubtype(const char *mode, unsigned xsize, unsigned ysize, im->linesize = xsize * 4; im->type = IMAGING_TYPE_INT32; - } else if (strcmp(mode, "I;16") == 0 || strcmp(mode, "I;16L") == 0 || strcmp(mode, "I;16B") == 0) { + } else if (strcmp(mode, "I;16") == 0 || strcmp(mode, "I;16L") == 0 \ + || strcmp(mode, "I;16B") == 0 || strcmp(mode, "I;16N") == 0) { /* EXPERIMENTAL */ /* 16-bit raw integer images */ im->bands = 1; diff --git a/libImaging/Unpack.c b/libImaging/Unpack.c index 2fba92e98..f6d6718d1 100644 --- a/libImaging/Unpack.c +++ b/libImaging/Unpack.c @@ -775,6 +775,26 @@ ImagingUnpackLAB(UINT8* out, const UINT8* in, int pixels) } } +static void +unpackI16N_I16B(UINT8* out, const UINT8* in, int pixels){ + int i; + UINT8* tmp = (UINT8*) out; + for (i = 0; i < pixels; i++) { + C16B; + in += 2; tmp += 2; + } + +} +static void +unpackI16N_I16(UINT8* out, const UINT8* in, int pixels){ + int i; + UINT8* tmp = (UINT8*) out; + for (i = 0; i < pixels; i++) { + C16L; + in += 2; tmp += 2; + } +} + static void copy1(UINT8* out, const UINT8* in, int pixels) { @@ -1139,6 +1159,10 @@ static struct { {"I;16B", "I;16B", 16, copy2}, {"I;16L", "I;16L", 16, copy2}, + {"I;16", "I;16N", 16, unpackI16N_I16}, // LibTiff native->image endian. + {"I;16L", "I;16N", 16, unpackI16N_I16}, // LibTiff native->image endian. + {"I;16B", "I;16N", 16, unpackI16N_I16B}, + {NULL} /* sentinel */ }; From 3128a7649547a59cfe5f4a54361958acf1992fb2 Mon Sep 17 00:00:00 2001 From: Eric Soroos Date: Mon, 21 Oct 2013 22:37:20 +0000 Subject: [PATCH 100/107] MM and II 16 bit integer tiffs pack/write properly using libtiff on bigendian platform --- PIL/TiffImagePlugin.py | 8 ++++++++ Tests/test_file_libtiff.py | 17 +++++++++++++++++ libImaging/Pack.c | 24 ++++++++++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index f4a68d1fd..54c7f1e26 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -1040,6 +1040,14 @@ def _save(im, fp, filename): pass if Image.DEBUG: print (atts) + + # libtiff always returns the bytes in native order. + # we're expecting image byte order. So, if the rawmode + # contains I;16, we need to convert from native to image + # byte order. + if im.mode in ('I;16B', 'I;16'): + rawmode = 'I;16N' + a = (rawmode, compression, _fp, filename, atts) e = Image._getencoder(im.mode, compression, a, im.encoderconfig) e.setimage(im.im, (0,0)+im.size) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index d4767c95a..6b3ab114e 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -115,6 +115,16 @@ def test_little_endian(): assert_equal(b[0], b'\xe0') assert_equal(b[1], b'\x01') + out = tempfile("temp.tif") + out = "temp.le.tif" + im.save(out) + reread = Image.open(out) + + assert_equal(reread.info['compression'], im.info['compression']) + assert_equal(reread.getpixel((0,0)), 480) + # UNDONE - libtiff defaults to writing in native endian, so + # on big endian, we'll get back mode = 'I;16B' here. + def test_big_endian(): im = Image.open('Tests/images/12bit.MM.deflate.tif') @@ -127,3 +137,10 @@ def test_big_endian(): assert_equal(b[0], b'\x01') assert_equal(b[1], b'\xe0') + out = tempfile("temp.tif") + im.save(out) + reread = Image.open(out) + + assert_equal(reread.info['compression'], im.info['compression']) + assert_equal(reread.getpixel((0,0)), 480) + diff --git a/libImaging/Pack.c b/libImaging/Pack.c index 320e94d67..1cc1f3a94 100644 --- a/libImaging/Pack.c +++ b/libImaging/Pack.c @@ -361,6 +361,27 @@ packI16B(UINT8* out, const UINT8* in_, int pixels) } } +static void +packI16N_I16B(UINT8* out, const UINT8* in, int pixels){ + int i; + UINT8* tmp = (UINT8*) in; + for (i = 0; i < pixels; i++) { + C16B; + out += 2; tmp += 2; + } + +} +static void +packI16N_I16(UINT8* out, const UINT8* in, int pixels){ + int i; + UINT8* tmp = (UINT8*) in; + for (i = 0; i < pixels; i++) { + C16L; + out += 2; tmp += 2; + } +} + + static void packI32S(UINT8* out, const UINT8* in, int pixels) { @@ -560,6 +581,9 @@ static struct { {"I;16", "I;16", 16, copy2}, {"I;16B", "I;16B", 16, copy2}, {"I;16L", "I;16L", 16, copy2}, + {"I;16", "I;16N", 16, packI16N_I16}, // LibTiff native->image endian. + {"I;16L", "I;16N", 16, packI16N_I16}, + {"I;16B", "I;16N", 16, packI16N_I16B}, {"BGR;15", "BGR;15", 16, copy2}, {"BGR;16", "BGR;16", 16, copy2}, {"BGR;24", "BGR;24", 24, copy3}, From 8d21ce1df799dce8608076050044bd8f37844234 Mon Sep 17 00:00:00 2001 From: Eric Soroos Date: Mon, 21 Oct 2013 22:53:35 +0000 Subject: [PATCH 101/107] py3k --- Tests/test_file_libtiff.py | 17 +++++++++++++---- Tests/test_file_tiff.py | 20 +++++++++++++------- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 6b3ab114e..67e351530 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -112,8 +112,13 @@ def test_little_endian(): b = im.tobytes() # Bytes are in image native order (little endian) - assert_equal(b[0], b'\xe0') - assert_equal(b[1], b'\x01') + if py3: + assert_equal(b[0], ord(b'\xe0')) + assert_equal(b[1], ord(b'\x01')) + else: + assert_equal(b[0], b'\xe0') + assert_equal(b[1], b'\x01') + out = tempfile("temp.tif") out = "temp.le.tif" @@ -134,8 +139,12 @@ def test_big_endian(): b = im.tobytes() # Bytes are in image native order (big endian) - assert_equal(b[0], b'\x01') - assert_equal(b[1], b'\xe0') + if py3: + assert_equal(b[0], ord(b'\x01')) + assert_equal(b[1], ord(b'\xe0')) + else: + assert_equal(b[0], b'\x01') + assert_equal(b[1], b'\xe0') out = tempfile("temp.tif") im.save(out) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index c88e103b4..88c1aa8fd 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -80,8 +80,13 @@ def test_little_endian(): b = im.tobytes() # Bytes are in image native order (little endian) - assert_equal(b[0], b'\xe0') - assert_equal(b[1], b'\x01') + if py3: + assert_equal(b[0], ord(b'\xe0')) + assert_equal(b[1], ord(b'\x01')) + else: + assert_equal(b[0], b'\xe0') + assert_equal(b[1], b'\x01') + def test_big_endian(): im = Image.open('Tests/images/12bit.MM.cropped.tif') @@ -91,8 +96,9 @@ def test_big_endian(): b = im.tobytes() # Bytes are in image native order (big endian) - assert_equal(b[0], b'\x01') - assert_equal(b[1], b'\xe0') - - - + if py3: + assert_equal(b[0], ord(b'\x01')) + assert_equal(b[1], ord(b'\xe0')) + else: + assert_equal(b[0], b'\x01') + assert_equal(b[1], b'\xe0') From 32e2f97b215985933ef3aada4451d03f478f0be4 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 22 Oct 2013 09:18:41 -0700 Subject: [PATCH 102/107] Tag issues with libtiff -- Need to parse the tuples for the ifd and the im.ifd. getattr + default for the im.ifd instead of silent try/except --- PIL/TiffImagePlugin.py | 50 ++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 54c7f1e26..3a77525f3 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -1011,33 +1011,30 @@ def _save(im, fp, filename): _fp = os.dup(fp.fileno()) blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS, ROWSPERSTRIP, ICCPROFILE] # ICC Profile crashes. - atts = dict([(k,v) for (k,(v,)) in ifd.items() if k not in blocklist]) - try: - # pull in more bits from the original file, e.g x,y resolution - # so that we can save(load('')) == original file. - for k,v in im.ifd.items(): - if k not in atts and k not in blocklist: - if type(v[0]) == tuple and len(v) > 1: - # A tuple of more than one rational tuples - # flatten to floats, following tiffcp.c->cpTag->TIFF_RATIONAL - atts[k] = [float(elt[0])/float(elt[1]) for elt in v] - continue - if type(v[0]) == tuple and len(v) == 1: - # A tuple of one rational tuples - # flatten to floats, following tiffcp.c->cpTag->TIFF_RATIONAL - atts[k] = float(v[0][0])/float(v[0][1]) - continue - if type(v) == tuple and len(v) == 1: - # int or similar - atts[k] = v[0] - continue - if type(v) == str: - atts[k] = v - continue + atts={} + # Merge the ones that we have with (optional) more bits from + # the original file, e.g x,y resolution so that we can + # save(load('')) == original file. + for k,v in itertools.chain(ifd.items(), getattr(im, 'ifd', {}).items()): + if k not in atts and k not in blocklist: + if type(v[0]) == tuple and len(v) > 1: + # A tuple of more than one rational tuples + # flatten to floats, following tiffcp.c->cpTag->TIFF_RATIONAL + atts[k] = [float(elt[0])/float(elt[1]) for elt in v] + continue + if type(v[0]) == tuple and len(v) == 1: + # A tuple of one rational tuples + # flatten to floats, following tiffcp.c->cpTag->TIFF_RATIONAL + atts[k] = float(v[0][0])/float(v[0][1]) + continue + if type(v) == tuple and len(v) == 1: + # int or similar + atts[k] = v[0] + continue + if type(v) == str: + atts[k] = bytes(v.encode('ascii', 'replace')) + b"\0" + continue - except: - # if we don't have an ifd here, just punt. - pass if Image.DEBUG: print (atts) @@ -1049,6 +1046,7 @@ def _save(im, fp, filename): rawmode = 'I;16N' a = (rawmode, compression, _fp, filename, atts) + print (im.mode, compression, a, im.encoderconfig) e = Image._getencoder(im.mode, compression, a, im.encoderconfig) e.setimage(im.im, (0,0)+im.size) while 1: From 8655209b4a707dd61105db583089d84fd48e5bcb Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 22 Oct 2013 09:24:51 -0700 Subject: [PATCH 103/107] rm debugging print --- PIL/TiffImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 3a77525f3..24d177f2d 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -1046,7 +1046,7 @@ def _save(im, fp, filename): rawmode = 'I;16N' a = (rawmode, compression, _fp, filename, atts) - print (im.mode, compression, a, im.encoderconfig) + # print (im.mode, compression, a, im.encoderconfig) e = Image._getencoder(im.mode, compression, a, im.encoderconfig) e.setimage(im.im, (0,0)+im.size) while 1: From c41a9b2b233caf25a47f4707a3aa00347be8f0ee Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 22 Oct 2013 09:29:56 -0700 Subject: [PATCH 104/107] correcting debugging strings --- encode.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/encode.c b/encode.c index 783e67a8e..10ed90d12 100644 --- a/encode.c +++ b/encode.c @@ -773,11 +773,10 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) (ttag_t) PyInt_AsLong(key), PyInt_AsLong(value)); } else if(PyBytes_Check(value)) { - TRACE(("Setting from String: %d, %s \n", (int)PyInt_AsLong(key),PyBytes_AsString(value))); + TRACE(("Setting from Bytes: %d, %s \n", (int)PyInt_AsLong(key),PyBytes_AsString(value))); status = ImagingLibTiffSetField(&encoder->state, (ttag_t) PyInt_AsLong(key), PyBytes_AsString(value)); - } else if(PyList_Check(value)) { int len,i; float *floatav; @@ -795,12 +794,12 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) free(floatav); } } else if (PyFloat_Check(value)) { - TRACE(("Setting from String: %d, %f \n", (int)PyInt_AsLong(key),PyFloat_AsDouble(value))); + TRACE(("Setting from Float: %d, %f \n", (int)PyInt_AsLong(key),PyFloat_AsDouble(value))); status = ImagingLibTiffSetField(&encoder->state, (ttag_t) PyInt_AsLong(key), (float)PyFloat_AsDouble(value)); } else { - TRACE(("Unhandled type for key %d : %s ", + TRACE(("Unhandled type for key %d : %s \n", (int)PyInt_AsLong(key), PyBytes_AsString(PyObject_Str(value)))); } From 758da6c5819f1827a71497ed0d48192b89086e8a Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 22 Oct 2013 10:06:33 -0700 Subject: [PATCH 105/107] whitespace error --- Tests/test_file_libtiff.py | 76 +++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 67e351530..c717be1e2 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -106,50 +106,50 @@ def test_adobe_deflate_tiff(): def test_little_endian(): - im = Image.open('Tests/images/12bit.deflate.tif') - assert_equal(im.getpixel((0,0)), 480) - assert_equal(im.mode, 'I;16') + im = Image.open('Tests/images/12bit.deflate.tif') + assert_equal(im.getpixel((0,0)), 480) + assert_equal(im.mode, 'I;16') - b = im.tobytes() - # Bytes are in image native order (little endian) - if py3: - assert_equal(b[0], ord(b'\xe0')) - assert_equal(b[1], ord(b'\x01')) - else: - assert_equal(b[0], b'\xe0') - assert_equal(b[1], b'\x01') - + b = im.tobytes() + # Bytes are in image native order (little endian) + if py3: + assert_equal(b[0], ord(b'\xe0')) + assert_equal(b[1], ord(b'\x01')) + else: + assert_equal(b[0], b'\xe0') + assert_equal(b[1], b'\x01') + - out = tempfile("temp.tif") - out = "temp.le.tif" - im.save(out) - reread = Image.open(out) + out = tempfile("temp.tif") + out = "temp.le.tif" + im.save(out) + reread = Image.open(out) - assert_equal(reread.info['compression'], im.info['compression']) - assert_equal(reread.getpixel((0,0)), 480) - # UNDONE - libtiff defaults to writing in native endian, so - # on big endian, we'll get back mode = 'I;16B' here. - + assert_equal(reread.info['compression'], im.info['compression']) + assert_equal(reread.getpixel((0,0)), 480) + # UNDONE - libtiff defaults to writing in native endian, so + # on big endian, we'll get back mode = 'I;16B' here. + def test_big_endian(): - im = Image.open('Tests/images/12bit.MM.deflate.tif') + im = Image.open('Tests/images/12bit.MM.deflate.tif') - assert_equal(im.getpixel((0,0)), 480) - assert_equal(im.mode, 'I;16B') + assert_equal(im.getpixel((0,0)), 480) + assert_equal(im.mode, 'I;16B') - b = im.tobytes() + b = im.tobytes() - # Bytes are in image native order (big endian) - if py3: - assert_equal(b[0], ord(b'\x01')) - assert_equal(b[1], ord(b'\xe0')) - else: - assert_equal(b[0], b'\x01') - assert_equal(b[1], b'\xe0') - - out = tempfile("temp.tif") - im.save(out) - reread = Image.open(out) + # Bytes are in image native order (big endian) + if py3: + assert_equal(b[0], ord(b'\x01')) + assert_equal(b[1], ord(b'\xe0')) + else: + assert_equal(b[0], b'\x01') + assert_equal(b[1], b'\xe0') + + out = tempfile("temp.tif") + im.save(out) + reread = Image.open(out) - assert_equal(reread.info['compression'], im.info['compression']) - assert_equal(reread.getpixel((0,0)), 480) + assert_equal(reread.info['compression'], im.info['compression']) + assert_equal(reread.getpixel((0,0)), 480) From 6357b4707c492cf2389b995ef620693cc11140f3 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 22 Oct 2013 10:09:25 -0700 Subject: [PATCH 106/107] String attr test case --- Tests/test_file_libtiff.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index c717be1e2..c68633780 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -93,6 +93,8 @@ def test_g4_write(): _assert_noerr(reread) assert_image_equal(reread, rot) + assert_equal(reread.info['compression'], orig.info['compression']) + assert_false(orig.tobytes() == reread.tobytes()) def test_adobe_deflate_tiff(): @@ -153,3 +155,17 @@ def test_big_endian(): assert_equal(reread.info['compression'], im.info['compression']) assert_equal(reread.getpixel((0,0)), 480) +def test_g4_string_info(): + """Tests String data in info directory""" + file = "Tests/images/lena_g4_500.tif" + orig = Image.open(file) + + out = tempfile("temp.tif") + + orig.tag[269] = 'temp.tif' + orig.save(out) + + reread = Image.open(out) + assert_equal('temp.tif', reread.tag[269]) + + From 6a0516d2c9f904d5bc3e38890add4b6b1df093e6 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 22 Oct 2013 10:10:37 -0700 Subject: [PATCH 107/107] final pass through atts: handling single rational tuple, using isStringType, flattening from single element tuple to string or int --- PIL/TiffImagePlugin.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 24d177f2d..695a2034a 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -46,6 +46,7 @@ __version__ = "1.3.5" from PIL import Image, ImageFile from PIL import ImagePalette from PIL import _binary +from PIL._util import isStringType import warnings import array, sys @@ -1027,13 +1028,25 @@ def _save(im, fp, filename): # flatten to floats, following tiffcp.c->cpTag->TIFF_RATIONAL atts[k] = float(v[0][0])/float(v[0][1]) continue - if type(v) == tuple and len(v) == 1: - # int or similar - atts[k] = v[0] + if type(v) == tuple and len(v) > 2: + # List of ints? + # BitsPerSample is one example, I get (8,8,8) + # UNDONE continue - if type(v) == str: + if type(v) == tuple and len(v) == 2: + # one rational tuple + # flatten to float, following tiffcp.c->cpTag->TIFF_RATIONAL + atts[k] = float(v[0])/float(v[1]) + continue + if type(v) == tuple and len(v) == 1: + v = v[0] + # drop through + if isStringType(v): atts[k] = bytes(v.encode('ascii', 'replace')) + b"\0" continue + else: + # int or similar + atts[k] = v if Image.DEBUG: print (atts)