mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-25 17:36:18 +03:00
Merge branch 'master' into patch-2
This commit is contained in:
commit
21268c87b3
21
CHANGES.rst
21
CHANGES.rst
|
@ -5,6 +5,27 @@ Changelog (Pillow)
|
|||
5.4.0 (unreleased)
|
||||
------------------
|
||||
|
||||
- Improve setup.py configuration #3395
|
||||
[diorcety]
|
||||
|
||||
- Read textual chunks located after IDAT chunks for PNG #3506
|
||||
[radarhere]
|
||||
|
||||
- Performance: Don't try to hash value if enum is empty #3503
|
||||
[Glandos]
|
||||
|
||||
- Added custom int and float TIFF tags #3350
|
||||
[radarhere]
|
||||
|
||||
- Fixes for issues reported by static code analysis #3393
|
||||
[frenzymadness]
|
||||
|
||||
- GIF: Wait until mode is normalized to copy im.info into encoderinfo #3187
|
||||
[radarhere]
|
||||
|
||||
- Docs: Add page of deprecations and removals #3486
|
||||
[hugovk]
|
||||
|
||||
- Travis CI: Upgrade PyPy from 5.8.0 to 6.0 #3488
|
||||
[hugovk]
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors <https://github.
|
|||
* - tests
|
||||
- |linux| |macos| |windows| |coverage|
|
||||
* - package
|
||||
- |zenodo| |version| |downloads|
|
||||
- |zenodo| |tidelift| |version| |downloads|
|
||||
* - social
|
||||
- |gitter| |twitter|
|
||||
|
||||
|
@ -43,6 +43,9 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors <https://github.
|
|||
.. |zenodo| image:: https://zenodo.org/badge/17549/python-pillow/Pillow.svg
|
||||
:target: https://zenodo.org/badge/latestdoi/17549/python-pillow/Pillow
|
||||
|
||||
.. |tidelift| image:: https://tidelift.com/badges/github/python-pillow/Pillow?style=flat
|
||||
:target: https://tidelift.com/subscription/pkg/pypi-pillow?utm_source=pypi-pillow&utm_medium=referral&utm_campaign=readme
|
||||
|
||||
.. |version| image:: https://img.shields.io/pypi/v/pillow.svg
|
||||
:target: https://pypi.org/project/Pillow/
|
||||
:alt: Latest PyPI version
|
||||
|
|
|
@ -536,6 +536,27 @@ class TestFileGif(PillowTestCase):
|
|||
|
||||
self.assertEqual(reloaded.info['transparency'], 253)
|
||||
|
||||
def test_rgb_transparency(self):
|
||||
out = self.tempfile('temp.gif')
|
||||
|
||||
# Single frame
|
||||
im = Image.new('RGB', (1, 1))
|
||||
im.info['transparency'] = (255, 0, 0)
|
||||
self.assert_warning(UserWarning, im.save, out)
|
||||
|
||||
reloaded = Image.open(out)
|
||||
self.assertNotIn('transparency', reloaded.info)
|
||||
|
||||
# Multiple frames
|
||||
im = Image.new('RGB', (1, 1))
|
||||
im.info['transparency'] = b""
|
||||
ims = [Image.new('RGB', (1, 1))]
|
||||
self.assert_warning(UserWarning,
|
||||
im.save, out, save_all=True, append_images=ims)
|
||||
|
||||
reloaded = Image.open(out)
|
||||
self.assertNotIn('transparency', reloaded.info)
|
||||
|
||||
def test_bbox(self):
|
||||
out = self.tempfile('temp.gif')
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import io
|
|||
import logging
|
||||
import itertools
|
||||
import os
|
||||
import distutils.version
|
||||
|
||||
from PIL import Image, TiffImagePlugin, TiffTags
|
||||
|
||||
|
@ -231,6 +232,32 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
|
||||
TiffImagePlugin.WRITE_LIBTIFF = False
|
||||
|
||||
def test_custom_metadata(self):
|
||||
custom = {
|
||||
37000: 4,
|
||||
37001: 4.2
|
||||
}
|
||||
|
||||
libtiff_version = TiffImagePlugin._libtiff_version()
|
||||
|
||||
libtiffs = [False]
|
||||
if distutils.version.StrictVersion(libtiff_version) >= \
|
||||
distutils.version.StrictVersion("4.0"):
|
||||
libtiffs.append(True)
|
||||
|
||||
for libtiff in libtiffs:
|
||||
TiffImagePlugin.WRITE_LIBTIFF = libtiff
|
||||
|
||||
im = hopper()
|
||||
|
||||
out = self.tempfile("temp.tif")
|
||||
im.save(out, tiffinfo=custom)
|
||||
TiffImagePlugin.WRITE_LIBTIFF = False
|
||||
|
||||
reloaded = Image.open(out)
|
||||
for tag, value in custom.items():
|
||||
self.assertEqual(reloaded.tag_v2[tag], value)
|
||||
|
||||
def test_int_dpi(self):
|
||||
# issue #1765
|
||||
im = hopper('RGB')
|
||||
|
|
|
@ -563,6 +563,28 @@ class TestFilePng(PillowTestCase):
|
|||
chunks = PngImagePlugin.getchunks(im)
|
||||
self.assertEqual(len(chunks), 3)
|
||||
|
||||
def test_textual_chunks_after_idat(self):
|
||||
im = Image.open("Tests/images/hopper.png")
|
||||
self.assertIn('comment', im.text.keys())
|
||||
for k, v in {
|
||||
'date:create': '2014-09-04T09:37:08+03:00',
|
||||
'date:modify': '2014-09-04T09:37:08+03:00',
|
||||
}.items():
|
||||
self.assertEqual(im.text[k], v)
|
||||
|
||||
# Raises a SyntaxError in load_end
|
||||
im = Image.open("Tests/images/broken_data_stream.png")
|
||||
with self.assertRaises(IOError):
|
||||
self.assertIsInstance(im.text, dict)
|
||||
|
||||
# Raises a UnicodeDecodeError in load_end
|
||||
im = Image.open("Tests/images/truncated_image.png")
|
||||
# The file is truncated
|
||||
self.assertRaises(IOError, lambda: im.text)
|
||||
ImageFile.LOAD_TRUNCATED_IMAGES = True
|
||||
self.assertIsInstance(im.text, dict)
|
||||
ImageFile.LOAD_TRUNCATED_IMAGES = False
|
||||
|
||||
@unittest.skipUnless(HAVE_WEBP and _webp.HAVE_WEBPANIM,
|
||||
"WebP support not installed with animation")
|
||||
def test_apng(self):
|
||||
|
|
|
@ -135,7 +135,7 @@ class TestFileTiffMetadata(PillowTestCase):
|
|||
for k, v in original.items():
|
||||
if isinstance(v, IFDRational):
|
||||
original[k] = IFDRational(*_limit_rational(v, 2**31))
|
||||
if isinstance(v, tuple) and isinstance(v[0], IFDRational):
|
||||
elif isinstance(v, tuple) and isinstance(v[0], IFDRational):
|
||||
original[k] = tuple(IFDRational(*_limit_rational(elt, 2**31))
|
||||
for elt in v)
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ def version(module, version):
|
|||
|
||||
version(Image, "jpeglib")
|
||||
version(Image, "zlib")
|
||||
version(Image, "libtiff")
|
||||
|
||||
try:
|
||||
from PIL import ImageFont
|
||||
|
|
60
docs/_static/js/script.js
vendored
Normal file
60
docs/_static/js/script.js
vendored
Normal file
|
@ -0,0 +1,60 @@
|
|||
jQuery(document).ready(function ($) {
|
||||
setTimeout(function () {
|
||||
var sectionID = 'base';
|
||||
var search = function ($section, $sidebarItem) {
|
||||
$section.children('.section, .function, .method').each(function () {
|
||||
if ($(this).hasClass('section')) {
|
||||
sectionID = $(this).attr('id');
|
||||
search($(this), $sidebarItem.parent().find('[href=#'+sectionID+']'));
|
||||
} else {
|
||||
var $dt = $(this).children('dt');
|
||||
var id = $dt.attr('id');
|
||||
if (id === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
var $functionsUL = $sidebarItem.siblings('[data-sectionID='+sectionID+']');
|
||||
if (!$functionsUL.length) {
|
||||
$functionsUL = $('<ul />').attr('data-sectionID', sectionID);
|
||||
$functionsUL.insertAfter($sidebarItem);
|
||||
}
|
||||
|
||||
var $li = $('<li />');
|
||||
var $a = $('<a />').css('font-size', '11.5px');
|
||||
var $upperA = $sidebarItem.parent().children('a');
|
||||
var $upperAParent = $upperA.parent();
|
||||
if ($upperAParent.hasClass('toctree-l2')) {
|
||||
$a.css('background-color', '#c9c9c9');
|
||||
$a.css('padding-left', '4em');
|
||||
} else if ($upperAParent.hasClass('toctree-l3')) {
|
||||
if (!$upperA.find('.toctree-expand').length) {
|
||||
$upperA.prepend($('<span />').addClass('toctree-expand'));
|
||||
}
|
||||
$a.css('background-color', '#c9c9c9');
|
||||
$a.css('padding-left', '5em');
|
||||
} else {
|
||||
$a.css('background-color', '#bdbdbd');
|
||||
$a.css('padding-left', '6.25em');
|
||||
}
|
||||
$a.attr('href', '#'+id);
|
||||
$a.text('- '+$dt.find('code').text());
|
||||
$a.click(function () {
|
||||
setTimeout(function () {
|
||||
$a.css('font-weight', 'bold');
|
||||
}, 0);
|
||||
});
|
||||
$li.append($a);
|
||||
$functionsUL.append($li);
|
||||
}
|
||||
});
|
||||
};
|
||||
search($('[itemprop=articleBody] > .section'), $('.wy-nav-side a[href=#]'));
|
||||
}, 0);
|
||||
$(window).on('hashchange', function () {
|
||||
$('ul[data-sectionID]').each(function () {
|
||||
$(this).find('a').each(function () {
|
||||
$(this).css('font-weight', 'normal');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -286,3 +286,7 @@ texinfo_documents = [
|
|||
|
||||
# If true, do not generate a @detailmenu in the "Top" node's menu.
|
||||
# texinfo_no_detailmenu = False
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.add_javascript('js/script.js')
|
||||
|
|
82
docs/deprecations.rst
Normal file
82
docs/deprecations.rst
Normal file
|
@ -0,0 +1,82 @@
|
|||
.. _deprecations:
|
||||
|
||||
Deprecations and removals
|
||||
=========================
|
||||
|
||||
This page lists Pillow features that are deprecated, or have been removed in
|
||||
past major releases, and gives the alternatives to use instead.
|
||||
|
||||
Deprecated features
|
||||
-------------------
|
||||
|
||||
Below are features which are considered deprecated. Where appropriate,
|
||||
a ``DeprecationWarning`` is issued.
|
||||
|
||||
Setting the size of TIFF images
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 5.3.0
|
||||
|
||||
Setting the image size of a TIFF image (eg. ``im.size = (256, 256)``) issues
|
||||
a ``DeprecationWarning``:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
Setting the size of a TIFF image directly is deprecated, and will
|
||||
be removed in a future version. Use the resize method instead.
|
||||
|
||||
PILLOW_VERSION and VERSION constants
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 5.2.0
|
||||
|
||||
Two version constants – ``VERSION`` (the old PIL version, always 1.1.7) and
|
||||
``PILLOW_VERSION`` – have been deprecated and will be removed in the next
|
||||
major release. Use ``__version__`` instead.
|
||||
|
||||
Undocumented ImageOps functions
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 4.3.0
|
||||
|
||||
Several undocumented functions in ``ImageOps`` have been deprecated. They issue
|
||||
a ``DeprecationWarning`` informing which equivalent to use from ``ImageFilter``
|
||||
instead:
|
||||
|
||||
========================== ============================
|
||||
Deprecated Use instead
|
||||
========================== ============================
|
||||
``ImageOps.box_blur`` ``ImageFilter.BoxBlur``
|
||||
``ImageOps.gaussian_blur`` ``ImageFilter.GaussianBlur``
|
||||
``ImageOps.gblur`` ``ImageFilter.GaussianBlur``
|
||||
``ImageOps.usm`` ``ImageFilter.UnsharpMask``
|
||||
``ImageOps.unsharp_mask`` ``ImageFilter.UnsharpMask``
|
||||
========================== ============================
|
||||
|
||||
PIL.OleFileIO
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 4.0.0
|
||||
|
||||
The vendored version of olefile has been removed. Attempting to import
|
||||
``PIL.OleFileIO`` issues a ``DeprecationWarning`` (from 4.0.0) or raises
|
||||
``ImportError`` (from 5.0.0):
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
PIL.OleFileIO is deprecated. Use the olefile Python package
|
||||
instead. This module will be removed in a future version.
|
||||
|
||||
Removed features
|
||||
----------------
|
||||
|
||||
Deprecated features are only removed in major releases after an appropriate
|
||||
period of deprecation has passed.
|
||||
|
||||
Vendored olefile
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
*Removed in version 4.0.0.*
|
||||
|
||||
The vendored version of the olefile Python package was removed in favour of the
|
||||
upstream package. Install if needed (eg. ``pip install olefile``).
|
|
@ -482,7 +482,7 @@ The :py:meth:`~PIL.Image.Image.open` method sets the following
|
|||
|
||||
This key is omitted if the image is not a transparent palette image.
|
||||
|
||||
``Open`` also sets ``Image.text`` to a list of the values of the
|
||||
``Open`` also sets ``Image.text`` to a dictionary of the values of the
|
||||
``tEXt``, ``zTXt``, and ``iTXt`` chunks of the PNG image. Individual
|
||||
compressed chunks are limited to a decompressed size of
|
||||
``PngImagePlugin.MAX_TEXT_CHUNK``, by default 1MB, to prevent
|
||||
|
|
|
@ -43,6 +43,7 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors <https://github.
|
|||
porting.rst
|
||||
about.rst
|
||||
releasenotes/index.rst
|
||||
deprecations.rst
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
|
28
setup.py
28
setup.py
|
@ -147,15 +147,30 @@ LCMS_ROOT = None
|
|||
|
||||
def _pkg_config(name):
|
||||
try:
|
||||
command = [
|
||||
command_libs = [
|
||||
'pkg-config',
|
||||
'--libs-only-L', name,
|
||||
]
|
||||
command_cflags = [
|
||||
'pkg-config',
|
||||
'--cflags-only-I', name,
|
||||
]
|
||||
if not DEBUG:
|
||||
command.append('--silence-errors')
|
||||
libs = subprocess.check_output(command).decode('utf8').split(' ')
|
||||
return libs[1][2:].strip(), libs[0][2:].strip()
|
||||
command_libs.append('--silence-errors')
|
||||
command_cflags.append('--silence-errors')
|
||||
libs = (
|
||||
subprocess.check_output(command_libs)
|
||||
.decode("utf8")
|
||||
.strip()
|
||||
.replace("-L", "")
|
||||
)
|
||||
cflags = (
|
||||
subprocess.check_output(command_cflags)
|
||||
.decode("utf8")
|
||||
.strip()
|
||||
.replace("-I", "")
|
||||
)
|
||||
return (libs, cflags)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
@ -247,6 +262,11 @@ class pil_build_ext(build_ext):
|
|||
IMAGEQUANT_ROOT="libimagequant"
|
||||
).items():
|
||||
root = globals()[root_name]
|
||||
|
||||
if root is None and root_name in os.environ:
|
||||
prefix = os.environ[root_name]
|
||||
root = (os.path.join(prefix, 'lib'), os.path.join(prefix, 'include'))
|
||||
|
||||
if root is None and pkg_config:
|
||||
if isinstance(lib_name, tuple):
|
||||
for lib_name2 in lib_name:
|
||||
|
|
|
@ -393,6 +393,8 @@ def _normalize_palette(im, palette, info):
|
|||
|
||||
def _write_single_frame(im, fp, palette):
|
||||
im_out = _normalize_mode(im, True)
|
||||
for k, v in im_out.info.items():
|
||||
im.encoderinfo.setdefault(k, v)
|
||||
im_out = _normalize_palette(im_out, palette, im.encoderinfo)
|
||||
|
||||
for s in _get_global_header(im_out, im.encoderinfo):
|
||||
|
@ -413,8 +415,8 @@ def _write_single_frame(im, fp, palette):
|
|||
|
||||
def _write_multiple_frames(im, fp, palette):
|
||||
|
||||
duration = im.encoderinfo.get("duration", None)
|
||||
disposal = im.encoderinfo.get('disposal', None)
|
||||
duration = im.encoderinfo.get("duration", im.info.get("duration"))
|
||||
disposal = im.encoderinfo.get("disposal", im.info.get("disposal"))
|
||||
|
||||
im_frames = []
|
||||
frame_count = 0
|
||||
|
@ -423,6 +425,9 @@ def _write_multiple_frames(im, fp, palette):
|
|||
for im_frame in ImageSequence.Iterator(imSequence):
|
||||
# a copy is required here since seek can still mutate the image
|
||||
im_frame = _normalize_mode(im_frame.copy())
|
||||
if frame_count == 0:
|
||||
for k, v in im_frame.info.items():
|
||||
im.encoderinfo.setdefault(k, v)
|
||||
im_frame = _normalize_palette(im_frame, palette, im.encoderinfo)
|
||||
|
||||
encoderinfo = im.encoderinfo.copy()
|
||||
|
@ -481,12 +486,10 @@ def _save_all(im, fp, filename):
|
|||
|
||||
|
||||
def _save(im, fp, filename, save_all=False):
|
||||
for k, v in im.info.items():
|
||||
im.encoderinfo.setdefault(k, v)
|
||||
# header
|
||||
try:
|
||||
palette = im.encoderinfo["palette"]
|
||||
except KeyError:
|
||||
if "palette" in im.encoderinfo or "palette" in im.info:
|
||||
palette = im.encoderinfo.get("palette", im.info.get("palette"))
|
||||
else:
|
||||
palette = None
|
||||
im.encoderinfo["optimize"] = im.encoderinfo.get("optimize", True)
|
||||
|
||||
|
|
|
@ -592,7 +592,7 @@ class PngImageFile(ImageFile.ImageFile):
|
|||
self.mode = self.png.im_mode
|
||||
self._size = self.png.im_size
|
||||
self.info = self.png.im_info
|
||||
self.text = self.png.im_text # experimental
|
||||
self._text = None
|
||||
self.tile = self.png.im_tile
|
||||
|
||||
if self.png.im_palette:
|
||||
|
@ -601,6 +601,15 @@ class PngImageFile(ImageFile.ImageFile):
|
|||
|
||||
self.__idat = length # used by load_read()
|
||||
|
||||
@property
|
||||
def text(self):
|
||||
# experimental
|
||||
if self._text is None:
|
||||
# iTxt, tEXt and zTXt chunks may appear at the end of the file
|
||||
# So load the file to ensure that they are read
|
||||
self.load()
|
||||
return self._text
|
||||
|
||||
def verify(self):
|
||||
"Verify PNG file"
|
||||
|
||||
|
@ -653,7 +662,22 @@ class PngImageFile(ImageFile.ImageFile):
|
|||
|
||||
def load_end(self):
|
||||
"internal: finished reading image data"
|
||||
while True:
|
||||
self.fp.read(4) # CRC
|
||||
|
||||
try:
|
||||
cid, pos, length = self.png.read()
|
||||
except (struct.error, SyntaxError):
|
||||
break
|
||||
|
||||
if cid == b"IEND":
|
||||
break
|
||||
|
||||
try:
|
||||
self.png.call(cid, pos, length)
|
||||
except UnicodeDecodeError:
|
||||
break
|
||||
self._text = self.png.im_text
|
||||
self.png.close()
|
||||
self.png = None
|
||||
|
||||
|
|
|
@ -54,6 +54,7 @@ import os
|
|||
import struct
|
||||
import sys
|
||||
import warnings
|
||||
import distutils.version
|
||||
|
||||
from .TiffTags import TYPES
|
||||
|
||||
|
@ -284,6 +285,10 @@ def _limit_rational(val, max_val):
|
|||
return n_d[::-1] if inv else n_d
|
||||
|
||||
|
||||
def _libtiff_version():
|
||||
return Image.core.libtiff_version.split("\n")[0].split("Version ")[1]
|
||||
|
||||
|
||||
##
|
||||
# Wrapper for TIFF IFDs.
|
||||
|
||||
|
@ -1508,12 +1513,13 @@ def _save(im, fp, filename):
|
|||
getattr(im, 'tag_v2', {}).items(),
|
||||
legacy_ifd.items()):
|
||||
# Libtiff can only process certain core items without adding
|
||||
# them to the custom dictionary. It will segfault if it attempts
|
||||
# to add a custom tag without the dictionary entry
|
||||
#
|
||||
# UNDONE -- add code for the custom dictionary
|
||||
# them to the custom dictionary. Support has only been been added
|
||||
# for int and float values
|
||||
if tag not in TiffTags.LIBTIFF_CORE:
|
||||
continue
|
||||
if (distutils.version.StrictVersion(_libtiff_version()) <
|
||||
distutils.version.StrictVersion("4.0")) \
|
||||
or not (isinstance(value, int) or isinstance(value, float)):
|
||||
continue
|
||||
if tag not in atts and tag not in blocklist:
|
||||
if isinstance(value, str if py3 else unicode): # noqa: F821
|
||||
atts[tag] = value.encode('ascii', 'replace') + b"\0"
|
||||
|
|
|
@ -29,7 +29,10 @@ class TagInfo(namedtuple("_TagInfo", "value name type length enum")):
|
|||
cls, value, name, type, length, enum or {})
|
||||
|
||||
def cvt_enum(self, value):
|
||||
return self.enum.get(value, value)
|
||||
# Using get will call hash(value), which can be expensive
|
||||
# for some types (e.g. Fraction). Since self.enum is rarely
|
||||
# used, it's usually better to test it first.
|
||||
return self.enum.get(value, value) if self.enum else value
|
||||
|
||||
|
||||
def lookup(tag):
|
||||
|
@ -425,6 +428,7 @@ TYPES = {}
|
|||
|
||||
# some of these are not in our TAGS_V2 dict and were included from tiff.h
|
||||
|
||||
# This list also exists in encode.c
|
||||
LIBTIFF_CORE = {255, 256, 257, 258, 259, 262, 263, 266, 274, 277,
|
||||
278, 280, 281, 340, 341, 282, 283, 284, 286, 287,
|
||||
296, 297, 321, 320, 338, 32995, 322, 323, 32998,
|
||||
|
|
|
@ -425,6 +425,7 @@ int load_tkinter_funcs(void)
|
|||
/* Try loading from the main program namespace first */
|
||||
main_program = dlopen(NULL, RTLD_LAZY);
|
||||
if (_func_loader(main_program) == 0) {
|
||||
dlclose(main_program);
|
||||
return 0;
|
||||
}
|
||||
/* Clear exception triggered when we didn't find symbols above */
|
||||
|
@ -453,6 +454,7 @@ int load_tkinter_funcs(void)
|
|||
/* dlclose probably safe because tkinter has been imported. */
|
||||
dlclose(tkinter_lib);
|
||||
exit:
|
||||
dlclose(main_program);
|
||||
Py_XDECREF(pModule);
|
||||
Py_XDECREF(pString);
|
||||
return ret;
|
||||
|
|
|
@ -1053,8 +1053,10 @@ _gaussian_blur(ImagingObject* self, PyObject* args)
|
|||
if (!imOut)
|
||||
return NULL;
|
||||
|
||||
if (!ImagingGaussianBlur(imOut, imIn, radius, passes))
|
||||
if (!ImagingGaussianBlur(imOut, imIn, radius, passes)) {
|
||||
ImagingDelete(imOut);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return PyImagingNew(imOut);
|
||||
}
|
||||
|
@ -1949,8 +1951,10 @@ _box_blur(ImagingObject* self, PyObject* args)
|
|||
if (!imOut)
|
||||
return NULL;
|
||||
|
||||
if (!ImagingBoxBlur(imOut, imIn, radius, n))
|
||||
if (!ImagingBoxBlur(imOut, imIn, radius, n)) {
|
||||
ImagingDelete(imOut);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return PyImagingNew(imOut);
|
||||
}
|
||||
|
@ -2596,6 +2600,7 @@ _draw_arc(ImagingDrawObject* self, PyObject* args)
|
|||
return NULL;
|
||||
if (n != 2) {
|
||||
PyErr_SetString(PyExc_TypeError, must_be_two_coordinates);
|
||||
free(xy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -2633,6 +2638,7 @@ _draw_bitmap(ImagingDrawObject* self, PyObject* args)
|
|||
PyErr_SetString(PyExc_TypeError,
|
||||
"coordinate list must contain exactly 1 coordinate"
|
||||
);
|
||||
free(xy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -2669,6 +2675,7 @@ _draw_chord(ImagingDrawObject* self, PyObject* args)
|
|||
return NULL;
|
||||
if (n != 2) {
|
||||
PyErr_SetString(PyExc_TypeError, must_be_two_coordinates);
|
||||
free(xy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -2705,6 +2712,7 @@ _draw_ellipse(ImagingDrawObject* self, PyObject* args)
|
|||
return NULL;
|
||||
if (n != 2) {
|
||||
PyErr_SetString(PyExc_TypeError, must_be_two_coordinates);
|
||||
free(xy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -2856,6 +2864,7 @@ _draw_pieslice(ImagingDrawObject* self, PyObject* args)
|
|||
return NULL;
|
||||
if (n != 2) {
|
||||
PyErr_SetString(PyExc_TypeError, must_be_two_coordinates);
|
||||
free(xy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -2894,6 +2903,7 @@ _draw_polygon(ImagingDrawObject* self, PyObject* args)
|
|||
PyErr_SetString(PyExc_TypeError,
|
||||
"coordinate list must contain at least 2 coordinates"
|
||||
);
|
||||
free(xy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -2937,6 +2947,7 @@ _draw_rectangle(ImagingDrawObject* self, PyObject* args)
|
|||
return NULL;
|
||||
if (n != 2) {
|
||||
PyErr_SetString(PyExc_TypeError, must_be_two_coordinates);
|
||||
free(xy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -3859,6 +3870,13 @@ setup_module(PyObject* m) {
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBTIFF
|
||||
{
|
||||
extern const char * ImagingTiffVersion(void);
|
||||
PyDict_SetItemString(d, "libtiff_version", PyUnicode_FromString(ImagingTiffVersion()));
|
||||
}
|
||||
#endif
|
||||
|
||||
PyDict_SetItemString(d, "PILLOW_VERSION", PyUnicode_FromString(version));
|
||||
|
||||
return 0;
|
||||
|
|
59
src/encode.c
59
src/encode.c
|
@ -584,11 +584,15 @@ PyImaging_ZipEncoderNew(PyObject* self, PyObject* args)
|
|||
dictionary = NULL;
|
||||
|
||||
encoder = PyImaging_EncoderNew(sizeof(ZIPSTATE));
|
||||
if (encoder == NULL)
|
||||
if (encoder == NULL) {
|
||||
free(dictionary);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (get_packer(encoder, mode, rawmode) < 0)
|
||||
if (get_packer(encoder, mode, rawmode) < 0) {
|
||||
free(dictionary);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
encoder->encode = ImagingZipEncode;
|
||||
encoder->cleanup = ImagingZipEncodeCleanup;
|
||||
|
@ -749,8 +753,10 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args)
|
|||
if (rawExif && rawExifLen > 0) {
|
||||
/* malloc check ok, length is from python parsearg */
|
||||
char* pp = malloc(rawExifLen); // Freed in JpegEncode, Case 5
|
||||
if (!pp)
|
||||
if (!pp) {
|
||||
if (extra) free(extra);
|
||||
return PyErr_NoMemory();
|
||||
}
|
||||
memcpy(pp, rawExif, rawExifLen);
|
||||
rawExif = pp;
|
||||
} else
|
||||
|
@ -804,7 +810,13 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
|
|||
PyObject *dir;
|
||||
PyObject *key, *value;
|
||||
Py_ssize_t pos = 0;
|
||||
int status;
|
||||
int key_int, status, is_core_tag, number_of_tags, i;
|
||||
// This list also exists in TiffTags.py
|
||||
const int tags[] = {
|
||||
256, 257, 258, 259, 262, 263, 266, 269, 274, 277, 278, 280, 281, 340,
|
||||
341, 282, 283, 284, 286, 287, 296, 297, 321, 338, 32995, 32998, 32996,
|
||||
339, 32997, 330, 531, 530
|
||||
};
|
||||
|
||||
Py_ssize_t d_size;
|
||||
PyObject *keys, *values;
|
||||
|
@ -843,23 +855,36 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
number_of_tags = sizeof(tags) / sizeof(int);
|
||||
for (pos = 0; pos < d_size; pos++) {
|
||||
key = PyList_GetItem(keys, pos);
|
||||
key_int = (int)PyInt_AsLong(key);
|
||||
value = PyList_GetItem(values, pos);
|
||||
status = 0;
|
||||
TRACE(("Attempting to set key: %d\n", (int)PyInt_AsLong(key)));
|
||||
is_core_tag = 0;
|
||||
for (i=0; i<number_of_tags; i++) {
|
||||
if (tags[i] == key_int) {
|
||||
is_core_tag = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
TRACE(("Attempting to set key: %d\n", key_int));
|
||||
if (PyInt_Check(value)) {
|
||||
TRACE(("Setting from Int: %d %ld \n", (int)PyInt_AsLong(key),PyInt_AsLong(value)));
|
||||
status = ImagingLibTiffSetField(&encoder->state,
|
||||
(ttag_t) PyInt_AsLong(key),
|
||||
PyInt_AsLong(value));
|
||||
TRACE(("Setting from Int: %d %ld \n", key_int, PyInt_AsLong(value)));
|
||||
if (is_core_tag || !ImagingLibTiffMergeFieldInfo(&encoder->state, TIFF_LONG, key_int)) {
|
||||
status = ImagingLibTiffSetField(&encoder->state,
|
||||
(ttag_t) PyInt_AsLong(key),
|
||||
PyInt_AsLong(value));
|
||||
}
|
||||
} else if (PyFloat_Check(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));
|
||||
TRACE(("Setting from Float: %d, %f \n", key_int, PyFloat_AsDouble(value)));
|
||||
if (is_core_tag || !ImagingLibTiffMergeFieldInfo(&encoder->state, TIFF_DOUBLE, key_int)) {
|
||||
status = ImagingLibTiffSetField(&encoder->state,
|
||||
(ttag_t) PyInt_AsLong(key),
|
||||
(double)PyFloat_AsDouble(value));
|
||||
}
|
||||
} else if (PyBytes_Check(value)) {
|
||||
TRACE(("Setting from Bytes: %d, %s \n", (int)PyInt_AsLong(key),PyBytes_AsString(value)));
|
||||
TRACE(("Setting from Bytes: %d, %s \n", key_int, PyBytes_AsString(value)));
|
||||
status = ImagingLibTiffSetField(&encoder->state,
|
||||
(ttag_t) PyInt_AsLong(key),
|
||||
PyBytes_AsString(value));
|
||||
|
@ -867,7 +892,7 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
|
|||
Py_ssize_t len,i;
|
||||
float *floatav;
|
||||
int *intav;
|
||||
TRACE(("Setting from Tuple: %d \n", (int)PyInt_AsLong(key)));
|
||||
TRACE(("Setting from Tuple: %d \n", key_int));
|
||||
len = PyTuple_Size(value);
|
||||
if (len) {
|
||||
if (PyInt_Check(PyTuple_GetItem(value,0))) {
|
||||
|
@ -898,13 +923,13 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
|
|||
}
|
||||
} else {
|
||||
TRACE(("Unhandled type in tuple for key %d : %s \n",
|
||||
(int)PyInt_AsLong(key),
|
||||
key_int,
|
||||
PyBytes_AsString(PyObject_Str(value))));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
TRACE(("Unhandled type for key %d : %s \n",
|
||||
(int)PyInt_AsLong(key),
|
||||
key_int,
|
||||
PyBytes_AsString(PyObject_Str(value))));
|
||||
}
|
||||
if (!status) {
|
||||
|
|
|
@ -41,7 +41,9 @@ ImagingHistogramNew(Imaging im)
|
|||
|
||||
/* Create histogram descriptor */
|
||||
h = calloc(1, sizeof(struct ImagingHistogramInstance));
|
||||
strncpy(h->mode, im->mode, IMAGING_MODE_LENGTH);
|
||||
strncpy(h->mode, im->mode, IMAGING_MODE_LENGTH-1);
|
||||
h->mode[IMAGING_MODE_LENGTH-1] = 0;
|
||||
|
||||
h->bands = im->bands;
|
||||
h->histogram = calloc(im->pixelsize, 256 * sizeof(long));
|
||||
|
||||
|
@ -80,8 +82,10 @@ ImagingGetHistogram(Imaging im, Imaging imMask, void* minmax)
|
|||
h->histogram[im->image8[y][x]]++;
|
||||
ImagingSectionLeave(&cookie);
|
||||
} else { /* yes, we need the braces. C isn't Python! */
|
||||
if (im->type != IMAGING_TYPE_UINT8)
|
||||
if (im->type != IMAGING_TYPE_UINT8) {
|
||||
ImagingHistogramDelete(h);
|
||||
return ImagingError_ModeError();
|
||||
}
|
||||
ImagingSectionEnter(&cookie);
|
||||
for (y = 0; y < im->ysize; y++) {
|
||||
UINT8* in = (UINT8*) im->image32[y];
|
||||
|
@ -120,8 +124,10 @@ ImagingGetHistogram(Imaging im, Imaging imMask, void* minmax)
|
|||
ImagingSectionLeave(&cookie);
|
||||
break;
|
||||
case IMAGING_TYPE_INT32:
|
||||
if (!minmax)
|
||||
if (!minmax) {
|
||||
ImagingHistogramDelete(h);
|
||||
return ImagingError_ValueError("min/max not given");
|
||||
}
|
||||
if (!im->xsize || !im->ysize)
|
||||
break;
|
||||
imin = ((INT32*) minmax)[0];
|
||||
|
@ -141,8 +147,10 @@ ImagingGetHistogram(Imaging im, Imaging imMask, void* minmax)
|
|||
ImagingSectionLeave(&cookie);
|
||||
break;
|
||||
case IMAGING_TYPE_FLOAT32:
|
||||
if (!minmax)
|
||||
if (!minmax) {
|
||||
ImagingHistogramDelete(h);
|
||||
return ImagingError_ValueError("min/max not given");
|
||||
}
|
||||
if (!im->xsize || !im->ysize)
|
||||
break;
|
||||
fmin = ((FLOAT32*) minmax)[0];
|
||||
|
|
|
@ -37,7 +37,8 @@ ImagingPaletteNew(const char* mode)
|
|||
if (!palette)
|
||||
return (ImagingPalette) ImagingError_MemoryError();
|
||||
|
||||
strncpy(palette->mode, mode, IMAGING_MODE_LENGTH);
|
||||
strncpy(palette->mode, mode, IMAGING_MODE_LENGTH-1);
|
||||
palette->mode[IMAGING_MODE_LENGTH-1] = 0;
|
||||
|
||||
/* Initialize to ramp */
|
||||
for (i = 0; i < 256; i++) {
|
||||
|
|
|
@ -568,6 +568,8 @@ split(BoxNode *node)
|
|||
left=malloc(sizeof(BoxNode));
|
||||
right=malloc(sizeof(BoxNode));
|
||||
if (!left||!right) {
|
||||
free(left);
|
||||
free(right);
|
||||
return 0;
|
||||
}
|
||||
for(i=0;i<3;i++) {
|
||||
|
|
|
@ -481,6 +481,7 @@ error:
|
|||
free(qp);
|
||||
free_color_cube(lookupCube);
|
||||
free_color_cube(coarseLookupCube);
|
||||
free(paletteBuckets);
|
||||
free(paletteBucketsCoarse);
|
||||
free(paletteBucketsFine);
|
||||
free_color_cube(coarseCube);
|
||||
|
|
|
@ -621,6 +621,8 @@ ImagingResampleInner(Imaging imIn, int xsize, int ysize,
|
|||
if ( ! ksize_vert) {
|
||||
free(bounds_horiz);
|
||||
free(kk_horiz);
|
||||
free(bounds_vert);
|
||||
free(kk_vert);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -402,6 +402,26 @@ int ImagingLibTiffEncodeInit(ImagingCodecState state, char *filename, int fp) {
|
|||
|
||||
}
|
||||
|
||||
int ImagingLibTiffMergeFieldInfo(ImagingCodecState state, TIFFDataType field_type, int key){
|
||||
TIFFSTATE *clientstate = (TIFFSTATE *)state->context;
|
||||
char field_name[10];
|
||||
uint32 n;
|
||||
int status = 0;
|
||||
|
||||
const TIFFFieldInfo info[] = {
|
||||
{ key, 0, 1, field_type, FIELD_CUSTOM, 1, 0, field_name }
|
||||
};
|
||||
n = sizeof(info) / sizeof(info[0]);
|
||||
|
||||
// Test for libtiff 4.0 or later, excluding libtiff 3.9.6 and 3.9.7
|
||||
#if TIFFLIB_VERSION >= 20111221 && TIFFLIB_VERSION != 20120218 && TIFFLIB_VERSION != 20120922
|
||||
status = TIFFMergeFieldInfo(clientstate->tiff, info, n);
|
||||
#else
|
||||
TIFFMergeFieldInfo(clientstate->tiff, info, n);
|
||||
#endif
|
||||
return status;
|
||||
}
|
||||
|
||||
int ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...){
|
||||
// after tif_dir.c->TIFFSetField.
|
||||
TIFFSTATE *clientstate = (TIFFSTATE *)state->context;
|
||||
|
@ -501,4 +521,11 @@ int ImagingLibTiffEncode(Imaging im, ImagingCodecState state, UINT8* buffer, int
|
|||
state->errcode = IMAGING_CODEC_END;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char*
|
||||
ImagingTiffVersion(void)
|
||||
{
|
||||
return TIFFGetVersion();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -45,6 +45,7 @@ typedef struct {
|
|||
|
||||
extern int ImagingLibTiffInit(ImagingCodecState state, int fp, int offset);
|
||||
extern int ImagingLibTiffEncodeInit(ImagingCodecState state, char *filename, int fp);
|
||||
extern int ImagingLibTiffMergeFieldInfo(ImagingCodecState state, TIFFDataType field_type, int key);
|
||||
extern int ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...);
|
||||
|
||||
|
||||
|
|
|
@ -82,12 +82,16 @@ path_new(Py_ssize_t count, double* xy, int duplicate)
|
|||
xy = p;
|
||||
}
|
||||
|
||||
if (PyType_Ready(&PyPathType) < 0)
|
||||
if (PyType_Ready(&PyPathType) < 0) {
|
||||
free(xy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
path = PyObject_New(PyPathObject, &PyPathType);
|
||||
if (path == NULL)
|
||||
if (path == NULL) {
|
||||
free(xy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
path->count = count;
|
||||
path->xy = xy;
|
||||
|
|
Loading…
Reference in New Issue
Block a user