|
@ -52,7 +52,7 @@ install:
|
|||
}
|
||||
else
|
||||
{
|
||||
c:\python34\python.exe c:\pillow\winbuild\build_dep.py
|
||||
c:\python37\python.exe c:\pillow\winbuild\build_dep.py
|
||||
c:\pillow\winbuild\build_deps.cmd
|
||||
$host.SetShouldExit(0)
|
||||
}
|
||||
|
|
2
.github/CONTRIBUTING.md
vendored
|
@ -9,7 +9,7 @@ Please send a pull request to the master branch. Please include [documentation](
|
|||
- Fork the Pillow repository.
|
||||
- Create a branch from master.
|
||||
- Develop bug fixes, features, tests, etc.
|
||||
- Run the test suite on Python 2.7 and 3.x. You can enable [Travis CI](https://travis-ci.org/profile/) and [AppVeyor](https://ci.appveyor.com/projects/new) on your repo to catch test failures prior to the pull request, and [Coveralls](https://coveralls.io/repos/new) to see if the changed code is covered by tests.
|
||||
- Run the test suite on Python 2.7 and 3.x. You can enable [Travis CI](https://travis-ci.org/profile/) and [AppVeyor](https://ci.appveyor.com/projects/new) on your repo to catch test failures prior to the pull request, and [Codecov](https://codecov.io/gh) to see if the changed code is covered by tests.
|
||||
- Create a pull request to pull the changes from your branch to the Pillow master.
|
||||
|
||||
### Guidelines
|
||||
|
|
27
.travis.yml
|
@ -18,40 +18,25 @@ matrix:
|
|||
env: LINT="true"
|
||||
- python: "pypy2.7-6.0"
|
||||
name: "PyPy2 Xenial"
|
||||
dist: xenial
|
||||
- python: "pypy3.5-6.0"
|
||||
name: "PyPy3 Xenial"
|
||||
dist: xenial
|
||||
- python: '3.7'
|
||||
name: "3.7 Xenial"
|
||||
- python: '2.7'
|
||||
name: "2.7 Xenial"
|
||||
- python: '2.7'
|
||||
name: "2.7 Trusty"
|
||||
dist: trusty
|
||||
- python: "2.7_with_system_site_packages" # For PyQt4
|
||||
name: "2.7_with_system_site_packages Xenial"
|
||||
services: xvfb
|
||||
- python: "2.7_with_system_site_packages" # For PyQt4
|
||||
name: "2.7_with_system_site_packages Trusty"
|
||||
dist: trusty
|
||||
- python: '3.6'
|
||||
name: "3.6 Xenial"
|
||||
- python: '3.6'
|
||||
name: "3.6 Trusty PYTHONOPTIMIZE=1"
|
||||
dist: trusty
|
||||
name: "3.6 Xenial PYTHONOPTIMIZE=1"
|
||||
env: PYTHONOPTIMIZE=1
|
||||
- python: '3.5'
|
||||
name: "3.5 Xenial"
|
||||
- python: '3.5'
|
||||
name: "3.5 Trusty PYTHONOPTIMIZE=2"
|
||||
dist: trusty
|
||||
name: "3.5 Xenial PYTHONOPTIMIZE=2"
|
||||
env: PYTHONOPTIMIZE=2
|
||||
- python: "3.8-dev"
|
||||
name: "3.8-dev Xenial"
|
||||
- env: DOCKER="alpine" DOCKER_TAG="master"
|
||||
- env: DOCKER="arch" DOCKER_TAG="master" # contains PyQt5
|
||||
- env: DOCKER="ubuntu-trusty-x86" DOCKER_TAG="master"
|
||||
- env: DOCKER="ubuntu-xenial-amd64" DOCKER_TAG="master"
|
||||
- env: DOCKER="debian-stretch-x86" DOCKER_TAG="master"
|
||||
- env: DOCKER="centos-6-amd64" DOCKER_TAG="master"
|
||||
|
@ -75,14 +60,6 @@ install:
|
|||
.travis/install.sh;
|
||||
fi
|
||||
|
||||
before_script:
|
||||
# Qt needs a display for some of the tests, and it's only run on the system site packages install
|
||||
- |
|
||||
if [ "$TRAVIS_JOB_NAME" == "2.7_with_system_site_packages Trusty" ]; then
|
||||
export DISPLAY=:99.0
|
||||
sh -e /etc/init.d/xvfb start
|
||||
fi
|
||||
|
||||
script:
|
||||
- |
|
||||
if [ "$LINT" == "true" ]; then
|
||||
|
|
62
CHANGES.rst
|
@ -2,12 +2,72 @@
|
|||
Changelog (Pillow)
|
||||
==================
|
||||
|
||||
6.0.0 (unreleased)
|
||||
6.0.0 (2019-04-01)
|
||||
------------------
|
||||
|
||||
- Python 2.7 support will be removed in Pillow 7.0.0 #3682
|
||||
[hugovk]
|
||||
|
||||
- Add EXIF class #3625
|
||||
[radarhere]
|
||||
|
||||
- Add ImageOps exif_transpose method #3687
|
||||
[radarhere]
|
||||
|
||||
- Added warnings to deprecated CMSProfile attributes #3615
|
||||
[hugovk]
|
||||
|
||||
- Documented reading TIFF multiframe images #3720
|
||||
[akuchling]
|
||||
|
||||
- Improved speed of opening an MPO file #3658
|
||||
[Glandos]
|
||||
|
||||
- Update palette in quantize #3721
|
||||
[radarhere]
|
||||
|
||||
- Improvements to TIFF is_animated and n_frames #3714
|
||||
[radarhere]
|
||||
|
||||
- Fixed incompatible pointer type warnings #3754
|
||||
[radarhere]
|
||||
|
||||
- Improvements to PA and LA conversion and palette operations #3728
|
||||
[radarhere]
|
||||
|
||||
- Consistent DPI rounding #3709
|
||||
[radarhere]
|
||||
|
||||
- Change size of MPO image to match frame #3588
|
||||
[radarhere]
|
||||
|
||||
- Read Photoshop resolution data #3701
|
||||
[radarhere]
|
||||
|
||||
- Ensure image is mutable before saving #3724
|
||||
[radarhere]
|
||||
|
||||
- Correct remap_palette documentation #3740
|
||||
[radarhere]
|
||||
|
||||
- Promote P images to PA in putalpha #3726
|
||||
[radarhere]
|
||||
|
||||
- Allow RGB and RGBA values for new P images #3719
|
||||
[radarhere]
|
||||
|
||||
- Fixed TIFF bug when seeking backwards and then forwards #3713
|
||||
[radarhere]
|
||||
|
||||
- Cache EXIF information #3498
|
||||
[Glandos]
|
||||
|
||||
- Added transparency for all PNG greyscale modes #3744
|
||||
[radarhere]
|
||||
|
||||
- Fix deprecation warnings in Python 3.8 #3749
|
||||
[radarhere]
|
||||
|
||||
- Fixed GIF bug when rewinding to a non-zero frame #3716
|
||||
[radarhere]
|
||||
|
||||
|
|
49
README.rst
|
@ -20,6 +20,30 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors <https://github.
|
|||
* - social
|
||||
- |gitter| |twitter|
|
||||
|
||||
.. end-badges
|
||||
|
||||
More Information
|
||||
----------------
|
||||
|
||||
- `Documentation <https://pillow.readthedocs.io/>`_
|
||||
|
||||
- `Installation <https://pillow.readthedocs.io/en/latest/installation.html>`_
|
||||
- `Handbook <https://pillow.readthedocs.io/en/latest/handbook/index.html>`_
|
||||
|
||||
- `Contribute <https://github.com/python-pillow/Pillow/blob/master/.github/CONTRIBUTING.md>`_
|
||||
|
||||
- `Issues <https://github.com/python-pillow/Pillow/issues>`_
|
||||
- `Pull requests <https://github.com/python-pillow/Pillow/pulls>`_
|
||||
|
||||
- `Changelog <https://github.com/python-pillow/Pillow/blob/master/CHANGES.rst>`_
|
||||
|
||||
- `Pre-fork <https://github.com/python-pillow/Pillow/blob/master/CHANGES.rst#pre-fork>`_
|
||||
|
||||
Report a Vulnerability
|
||||
----------------------
|
||||
|
||||
To report a security vulnerability, please follow the procedure described in the `Tidelift security policy <https://tidelift.com/docs/security>`_.
|
||||
|
||||
.. |docs| image:: https://readthedocs.org/projects/pillow/badge/?version=latest
|
||||
:target: https://pillow.readthedocs.io/?badge=latest
|
||||
:alt: Documentation Status
|
||||
|
@ -36,8 +60,8 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors <https://github.
|
|||
:target: https://ci.appveyor.com/project/python-pillow/Pillow
|
||||
:alt: AppVeyor CI build status (Windows)
|
||||
|
||||
.. |coverage| image:: https://coveralls.io/repos/python-pillow/Pillow/badge.svg?branch=master&service=github
|
||||
:target: https://coveralls.io/github/python-pillow/Pillow?branch=master
|
||||
.. |coverage| image:: https://codecov.io/gh/python-pillow/Pillow/branch/master/graph/badge.svg
|
||||
:target: https://codecov.io/gh/python-pillow/Pillow
|
||||
:alt: Code coverage
|
||||
|
||||
.. |zenodo| image:: https://zenodo.org/badge/17549/python-pillow/Pillow.svg
|
||||
|
@ -61,24 +85,3 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors <https://github.
|
|||
.. |twitter| image:: https://img.shields.io/badge/tweet-on%20Twitter-00aced.svg
|
||||
:target: https://twitter.com/PythonPillow
|
||||
:alt: Follow on https://twitter.com/PythonPillow
|
||||
|
||||
.. end-badges
|
||||
|
||||
|
||||
|
||||
More Information
|
||||
----------------
|
||||
|
||||
- `Documentation <https://pillow.readthedocs.io/>`_
|
||||
|
||||
- `Installation <https://pillow.readthedocs.io/en/latest/installation.html>`_
|
||||
- `Handbook <https://pillow.readthedocs.io/en/latest/handbook/index.html>`_
|
||||
|
||||
- `Contribute <https://github.com/python-pillow/Pillow/blob/master/.github/CONTRIBUTING.md>`_
|
||||
|
||||
- `Issues <https://github.com/python-pillow/Pillow/issues>`_
|
||||
- `Pull requests <https://github.com/python-pillow/Pillow/pulls>`_
|
||||
|
||||
- `Changelog <https://github.com/python-pillow/Pillow/blob/master/CHANGES.rst>`_
|
||||
|
||||
- `Pre-fork <https://github.com/python-pillow/Pillow/blob/master/CHANGES.rst#pre-fork>`_
|
||||
|
|
|
@ -102,4 +102,4 @@ Released as needed privately to individual vendors for critical security-related
|
|||
|
||||
## Documentation
|
||||
|
||||
* [ ] Make sure the default version for Read the Docs is the latest tagged release e.g. `d2d43879` (5.4.0)
|
||||
* [ ] Make sure the [default version for Read the Docs](https://pillow.readthedocs.io/en/stable/) is up-to-date with the release changes
|
||||
|
|
BIN
Tests/images/1_trns.png
Normal file
After Width: | Height: | Size: 612 B |
BIN
Tests/images/drawing_roundDown.emf
Normal file
BIN
Tests/images/fujifilm.mpo
Normal file
After Width: | Height: | Size: 9.5 MiB |
BIN
Tests/images/hopper_orientation_2.jpg
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
Tests/images/hopper_orientation_2.webp
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
Tests/images/hopper_orientation_3.jpg
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
Tests/images/hopper_orientation_3.webp
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
Tests/images/hopper_orientation_4.jpg
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
Tests/images/hopper_orientation_4.webp
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
Tests/images/hopper_orientation_5.jpg
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
Tests/images/hopper_orientation_5.webp
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
Tests/images/hopper_orientation_6.jpg
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
Tests/images/hopper_orientation_6.webp
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
Tests/images/hopper_orientation_7.jpg
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
Tests/images/hopper_orientation_7.webp
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
Tests/images/hopper_orientation_8.jpg
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
Tests/images/hopper_orientation_8.webp
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
Tests/images/hopper_roundDown.bmp
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
Tests/images/hopper_roundDown_2.tif
Normal file
BIN
Tests/images/hopper_roundDown_3.tif
Normal file
BIN
Tests/images/hopper_roundDown_None.tif
Normal file
BIN
Tests/images/hopper_roundUp_2.tif
Normal file
BIN
Tests/images/hopper_roundUp_3.tif
Normal file
BIN
Tests/images/hopper_roundUp_None.tif
Normal file
BIN
Tests/images/i_trns.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
Tests/images/iptc_roundDown.jpg
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
Tests/images/iptc_roundUp.jpg
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
Tests/images/sugarshack_frame_size.mpo
Normal file
After Width: | Height: | Size: 117 KiB |
|
@ -71,6 +71,27 @@ class TestFileBmp(PillowTestCase):
|
|||
self.assertEqual(im.size, reloaded.size)
|
||||
self.assertEqual(reloaded.format, "JPEG")
|
||||
|
||||
def test_load_dpi_rounding(self):
|
||||
# Round up
|
||||
im = Image.open('Tests/images/hopper.bmp')
|
||||
self.assertEqual(im.info["dpi"], (96, 96))
|
||||
|
||||
# Round down
|
||||
im = Image.open('Tests/images/hopper_roundDown.bmp')
|
||||
self.assertEqual(im.info["dpi"], (72, 72))
|
||||
|
||||
def test_save_dpi_rounding(self):
|
||||
outfile = self.tempfile("temp.bmp")
|
||||
im = Image.open('Tests/images/hopper.bmp')
|
||||
|
||||
im.save(outfile, dpi=(72.2, 72.2))
|
||||
reloaded = Image.open(outfile)
|
||||
self.assertEqual(reloaded.info["dpi"], (72, 72))
|
||||
|
||||
im.save(outfile, dpi=(72.8, 72.8))
|
||||
reloaded = Image.open(outfile)
|
||||
self.assertEqual(reloaded.info["dpi"], (73, 73))
|
||||
|
||||
def test_load_dib(self):
|
||||
# test for #1293, Imagegrab returning Unsupported Bitfields Format
|
||||
im = Image.open('Tests/images/clipboard.dib')
|
||||
|
|
|
@ -524,6 +524,27 @@ class TestFileJpeg(PillowTestCase):
|
|||
reloaded.load()
|
||||
self.assertEqual(im.info['dpi'], reloaded.info['dpi'])
|
||||
|
||||
def test_load_dpi_rounding(self):
|
||||
# Round up
|
||||
im = Image.open('Tests/images/iptc_roundUp.jpg')
|
||||
self.assertEqual(im.info["dpi"], (44, 44))
|
||||
|
||||
# Round down
|
||||
im = Image.open('Tests/images/iptc_roundDown.jpg')
|
||||
self.assertEqual(im.info["dpi"], (2, 2))
|
||||
|
||||
def test_save_dpi_rounding(self):
|
||||
outfile = self.tempfile("temp.jpg")
|
||||
im = Image.open('Tests/images/hopper.jpg')
|
||||
|
||||
im.save(outfile, dpi=(72.2, 72.2))
|
||||
reloaded = Image.open(outfile)
|
||||
self.assertEqual(reloaded.info["dpi"], (72, 72))
|
||||
|
||||
im.save(outfile, dpi=(72.8, 72.8))
|
||||
reloaded = Image.open(outfile)
|
||||
self.assertEqual(reloaded.info["dpi"], (73, 73))
|
||||
|
||||
def test_dpi_tuple_from_exif(self):
|
||||
# Arrange
|
||||
# This Photoshop CC 2017 image has DPI in EXIF not metadata
|
||||
|
@ -590,6 +611,15 @@ class TestFileJpeg(PillowTestCase):
|
|||
# Act / Assert
|
||||
self.assertEqual(im._getexif()[306], '2017:03:13 23:03:09')
|
||||
|
||||
def test_photoshop(self):
|
||||
im = Image.open("Tests/images/photoshop-200dpi.jpg")
|
||||
self.assertEqual(im.info["photoshop"][0x03ed], {
|
||||
'XResolution': 200.0,
|
||||
'DisplayedUnitsX': 1,
|
||||
'YResolution': 200.0,
|
||||
'DisplayedUnitsY': 1,
|
||||
})
|
||||
|
||||
|
||||
@unittest.skipUnless(sys.platform.startswith('win32'), "Windows only")
|
||||
class TestFileCloseW32(PillowTestCase):
|
||||
|
|
|
@ -55,6 +55,27 @@ class TestFileMpo(PillowTestCase):
|
|||
self.assertEqual(info[296], 2)
|
||||
self.assertEqual(info[34665], 188)
|
||||
|
||||
def test_frame_size(self):
|
||||
# This image has been hexedited to contain a different size
|
||||
# in the EXIF data of the second frame
|
||||
im = Image.open("Tests/images/sugarshack_frame_size.mpo")
|
||||
self.assertEqual(im.size, (640, 480))
|
||||
|
||||
im.seek(1)
|
||||
self.assertEqual(im.size, (680, 480))
|
||||
|
||||
def test_parallax(self):
|
||||
# Nintendo
|
||||
im = Image.open("Tests/images/sugarshack.mpo")
|
||||
exif = im.getexif()
|
||||
self.assertEqual(exif.get_ifd(0x927c)[0x1101]["Parallax"], -44.798187255859375)
|
||||
|
||||
# Fujifilm
|
||||
im = Image.open("Tests/images/fujifilm.mpo")
|
||||
im.seek(1)
|
||||
exif = im.getexif()
|
||||
self.assertEqual(exif.get_ifd(0x927c)[0xb211], -3.125)
|
||||
|
||||
def test_mp(self):
|
||||
for test_file in test_files:
|
||||
im = Image.open(test_file)
|
||||
|
|
|
@ -291,30 +291,32 @@ class TestFilePng(PillowTestCase):
|
|||
self.assert_image(im, "RGBA", (10, 10))
|
||||
self.assertEqual(im.getcolors(), [(100, (0, 0, 0, 0))])
|
||||
|
||||
def test_save_l_transparency(self):
|
||||
# There are 559 transparent pixels in l_trns.png.
|
||||
num_transparent = 559
|
||||
def test_save_greyscale_transparency(self):
|
||||
for mode, num_transparent in {
|
||||
"1": 1994,
|
||||
"L": 559,
|
||||
"I": 559,
|
||||
}.items():
|
||||
in_file = "Tests/images/"+mode.lower()+"_trns.png"
|
||||
im = Image.open(in_file)
|
||||
self.assertEqual(im.mode, mode)
|
||||
self.assertEqual(im.info["transparency"], 255)
|
||||
|
||||
in_file = "Tests/images/l_trns.png"
|
||||
im = Image.open(in_file)
|
||||
self.assertEqual(im.mode, "L")
|
||||
self.assertEqual(im.info["transparency"], 255)
|
||||
im_rgba = im.convert('RGBA')
|
||||
self.assertEqual(
|
||||
im_rgba.getchannel("A").getcolors()[0][0], num_transparent)
|
||||
|
||||
im_rgba = im.convert('RGBA')
|
||||
self.assertEqual(
|
||||
im_rgba.getchannel("A").getcolors()[0][0], num_transparent)
|
||||
test_file = self.tempfile("temp.png")
|
||||
im.save(test_file)
|
||||
|
||||
test_file = self.tempfile("temp.png")
|
||||
im.save(test_file)
|
||||
test_im = Image.open(test_file)
|
||||
self.assertEqual(test_im.mode, mode)
|
||||
self.assertEqual(test_im.info["transparency"], 255)
|
||||
self.assert_image_equal(im, test_im)
|
||||
|
||||
test_im = Image.open(test_file)
|
||||
self.assertEqual(test_im.mode, "L")
|
||||
self.assertEqual(test_im.info["transparency"], 255)
|
||||
self.assert_image_equal(im, test_im)
|
||||
|
||||
test_im_rgba = test_im.convert('RGBA')
|
||||
self.assertEqual(
|
||||
test_im_rgba.getchannel('A').getcolors()[0][0], num_transparent)
|
||||
test_im_rgba = test_im.convert('RGBA')
|
||||
self.assertEqual(
|
||||
test_im_rgba.getchannel('A').getcolors()[0][0], num_transparent)
|
||||
|
||||
def test_save_rgb_single_transparency(self):
|
||||
in_file = "Tests/images/caption_6_33_22.png"
|
||||
|
@ -387,6 +389,24 @@ class TestFilePng(PillowTestCase):
|
|||
im = roundtrip(im, dpi=(100, 100))
|
||||
self.assertEqual(im.info["dpi"], (100, 100))
|
||||
|
||||
def test_load_dpi_rounding(self):
|
||||
# Round up
|
||||
im = Image.open(TEST_PNG_FILE)
|
||||
self.assertEqual(im.info["dpi"], (96, 96))
|
||||
|
||||
# Round down
|
||||
im = Image.open("Tests/images/icc_profile_none.png")
|
||||
self.assertEqual(im.info["dpi"], (72, 72))
|
||||
|
||||
def test_save_dpi_rounding(self):
|
||||
im = Image.open(TEST_PNG_FILE)
|
||||
|
||||
im = roundtrip(im, dpi=(72.2, 72.2))
|
||||
self.assertEqual(im.info["dpi"], (72, 72))
|
||||
|
||||
im = roundtrip(im, dpi=(72.8, 72.8))
|
||||
self.assertEqual(im.info["dpi"], (73, 73))
|
||||
|
||||
def test_roundtrip_text(self):
|
||||
# Check text roundtripping
|
||||
|
||||
|
|
|
@ -126,6 +126,30 @@ class TestFileTiff(PillowTestCase):
|
|||
im._setup()
|
||||
self.assertEqual(im.info['dpi'], (71., 71.))
|
||||
|
||||
def test_load_dpi_rounding(self):
|
||||
for resolutionUnit, dpi in ((None, (72, 73)),
|
||||
(2, (72, 73)),
|
||||
(3, (183, 185))):
|
||||
im = Image.open(
|
||||
"Tests/images/hopper_roundDown_"+str(resolutionUnit)+".tif")
|
||||
self.assertEqual(im.tag_v2.get(RESOLUTION_UNIT), resolutionUnit)
|
||||
self.assertEqual(im.info['dpi'], (dpi[0], dpi[0]))
|
||||
|
||||
im = Image.open("Tests/images/hopper_roundUp_"+str(resolutionUnit)+".tif")
|
||||
self.assertEqual(im.tag_v2.get(RESOLUTION_UNIT), resolutionUnit)
|
||||
self.assertEqual(im.info['dpi'], (dpi[1], dpi[1]))
|
||||
|
||||
def test_save_dpi_rounding(self):
|
||||
outfile = self.tempfile("temp.tif")
|
||||
im = Image.open("Tests/images/hopper.tif")
|
||||
|
||||
for dpi in (72.2, 72.8):
|
||||
im.save(outfile, dpi=(dpi, dpi))
|
||||
|
||||
reloaded = Image.open(outfile)
|
||||
reloaded.load()
|
||||
self.assertEqual((round(dpi), round(dpi)), reloaded.info['dpi'])
|
||||
|
||||
def test_save_setting_missing_resolution(self):
|
||||
b = BytesIO()
|
||||
Image.open("Tests/images/10ct_32bit_128.tiff").save(
|
||||
|
@ -229,11 +253,6 @@ class TestFileTiff(PillowTestCase):
|
|||
['Tests/images/multipage-lastframe.tif', 1],
|
||||
['Tests/images/multipage.tiff', 3]
|
||||
]:
|
||||
# Test is_animated before n_frames
|
||||
im = Image.open(path)
|
||||
self.assertEqual(im.is_animated, n_frames != 1)
|
||||
|
||||
# Test is_animated after n_frames
|
||||
im = Image.open(path)
|
||||
self.assertEqual(im.n_frames, n_frames)
|
||||
self.assertEqual(im.is_animated, n_frames != 1)
|
||||
|
@ -263,6 +282,11 @@ class TestFileTiff(PillowTestCase):
|
|||
self.assertEqual(im.size, (10, 10))
|
||||
self.assertEqual(im.convert('RGB').getpixel((0, 0)), (255, 0, 0))
|
||||
|
||||
im.seek(0)
|
||||
im.load()
|
||||
self.assertEqual(im.size, (10, 10))
|
||||
self.assertEqual(im.convert('RGB').getpixel((0, 0)), (0, 128, 0))
|
||||
|
||||
im.seek(2)
|
||||
im.load()
|
||||
self.assertEqual(im.size, (20, 20))
|
||||
|
|
|
@ -45,6 +45,15 @@ class TestFileWmf(PillowTestCase):
|
|||
# Restore the state before this test
|
||||
WmfImagePlugin.register_handler(None)
|
||||
|
||||
def test_load_dpi_rounding(self):
|
||||
# Round up
|
||||
im = Image.open('Tests/images/drawing.emf')
|
||||
self.assertEqual(im.info["dpi"], 1424)
|
||||
|
||||
# Round down
|
||||
im = Image.open('Tests/images/drawing_roundDown.emf')
|
||||
self.assertEqual(im.info["dpi"], 1426)
|
||||
|
||||
def test_save(self):
|
||||
im = hopper()
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@ from .helper import unittest, PillowTestCase, hopper
|
|||
from PIL import Image
|
||||
from PIL._util import py3
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
|
||||
|
||||
class TestImage(PillowTestCase):
|
||||
|
@ -121,6 +123,16 @@ class TestImage(PillowTestCase):
|
|||
im.paste(0, (0, 0, 100, 100))
|
||||
self.assertFalse(im.readonly)
|
||||
|
||||
@unittest.skipIf(sys.platform.startswith('win32'),
|
||||
"Test requires opening tempfile twice")
|
||||
def test_readonly_save(self):
|
||||
temp_file = self.tempfile("temp.bmp")
|
||||
shutil.copy("Tests/images/rgb32bf-rgba.bmp", temp_file)
|
||||
|
||||
im = Image.open(temp_file)
|
||||
self.assertTrue(im.readonly)
|
||||
im.save(temp_file)
|
||||
|
||||
def test_dump(self):
|
||||
im = Image.new("L", (10, 10))
|
||||
im._dump(self.tempfile("temp_L.ppm"))
|
||||
|
@ -522,6 +534,16 @@ class TestImage(PillowTestCase):
|
|||
_make_new(im, blank_p, ImagePalette.ImagePalette())
|
||||
_make_new(im, blank_pa, ImagePalette.ImagePalette())
|
||||
|
||||
def test_p_from_rgb_rgba(self):
|
||||
for mode, color in [
|
||||
("RGB", '#DDEEFF'),
|
||||
("RGB", (221, 238, 255)),
|
||||
("RGBA", (221, 238, 255, 255))
|
||||
]:
|
||||
im = Image.new("P", (100, 100), color)
|
||||
expected = Image.new(mode, (100, 100), color)
|
||||
self.assert_image_equal(im.convert(mode), expected)
|
||||
|
||||
def test_no_resource_warning_on_save(self):
|
||||
# https://github.com/python-pillow/Pillow/issues/835
|
||||
# Arrange
|
||||
|
|
|
@ -12,7 +12,8 @@ class TestImageConvert(PillowTestCase):
|
|||
self.assertEqual(out.mode, mode)
|
||||
self.assertEqual(out.size, im.size)
|
||||
|
||||
modes = "1", "L", "I", "F", "RGB", "RGBA", "RGBX", "CMYK", "YCbCr"
|
||||
modes = ("1", "L", "LA", "P", "PA", "I", "F",
|
||||
"RGB", "RGBA", "RGBX", "CMYK", "YCbCr")
|
||||
|
||||
for mode in modes:
|
||||
im = hopper(mode)
|
||||
|
|
|
@ -42,7 +42,7 @@ class TestImageMode(PillowTestCase):
|
|||
self.assertEqual(signature, result)
|
||||
check("1", "L", "L", 1, ("1",))
|
||||
check("L", "L", "L", 1, ("L",))
|
||||
check("P", "RGB", "L", 1, ("P",))
|
||||
check("P", "P", "L", 1, ("P",))
|
||||
check("I", "L", "I", 1, ("I",))
|
||||
check("F", "L", "F", 1, ("F",))
|
||||
check("RGB", "RGB", "L", 3, ("R", "G", "B"))
|
||||
|
|
|
@ -28,6 +28,13 @@ class TestImagePutAlpha(PillowTestCase):
|
|||
self.assertEqual(im.mode, 'LA')
|
||||
self.assertEqual(im.getpixel((0, 0)), (1, 2))
|
||||
|
||||
im = Image.new("P", (1, 1), 1)
|
||||
self.assertEqual(im.getpixel((0, 0)), 1)
|
||||
|
||||
im.putalpha(2)
|
||||
self.assertEqual(im.mode, 'PA')
|
||||
self.assertEqual(im.getpixel((0, 0)), (1, 2))
|
||||
|
||||
im = Image.new("RGB", (1, 1), (1, 2, 3))
|
||||
self.assertEqual(im.getpixel((0, 0)), (1, 2, 3))
|
||||
|
||||
|
|
|
@ -14,8 +14,10 @@ class TestImagePutPalette(PillowTestCase):
|
|||
return im.mode, p[:10]
|
||||
return im.mode
|
||||
self.assertRaises(ValueError, palette, "1")
|
||||
self.assertEqual(palette("L"), ("P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))
|
||||
self.assertEqual(palette("P"), ("P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))
|
||||
for mode in ["L", "LA", "P", "PA"]:
|
||||
self.assertEqual(palette(mode),
|
||||
("PA" if "A" in mode else "P",
|
||||
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))
|
||||
self.assertRaises(ValueError, palette, "I")
|
||||
self.assertRaises(ValueError, palette, "F")
|
||||
self.assertRaises(ValueError, palette, "RGB")
|
||||
|
|
|
@ -38,9 +38,10 @@ class TestImageQuantize(PillowTestCase):
|
|||
|
||||
def test_rgba_quantize(self):
|
||||
image = hopper('RGBA')
|
||||
image.quantize()
|
||||
self.assertRaises(ValueError, image.quantize, method=0)
|
||||
|
||||
self.assertEqual(image.quantize().convert().mode, "RGBA")
|
||||
|
||||
def test_quantize(self):
|
||||
image = Image.open('Tests/images/caption_6_33_22.png').convert('RGB')
|
||||
converted = image.quantize()
|
||||
|
|
|
@ -309,7 +309,7 @@ class TestImageCms(PillowTestCase):
|
|||
2: (False, False, True),
|
||||
3: (False, False, True)
|
||||
})
|
||||
self.assertEqual(p.color_space, 'RGB')
|
||||
|
||||
self.assertIsNone(p.colorant_table)
|
||||
self.assertIsNone(p.colorant_table_out)
|
||||
self.assertIsNone(p.colorimetric_intent)
|
||||
|
@ -361,16 +361,9 @@ class TestImageCms(PillowTestCase):
|
|||
(5000.722328847392,))
|
||||
self.assertEqual(p.model,
|
||||
'IEC 61966-2-1 Default RGB Colour Space - sRGB')
|
||||
self.assertEqual(p.pcs, 'XYZ')
|
||||
|
||||
self.assertIsNone(p.perceptual_rendering_intent_gamut)
|
||||
self.assertEqual(p.product_copyright,
|
||||
'Copyright International Color Consortium, 2009')
|
||||
self.assertEqual(p.product_desc, 'sRGB IEC61966-2-1 black scaled')
|
||||
self.assertEqual(p.product_description,
|
||||
'sRGB IEC61966-2-1 black scaled')
|
||||
self.assertEqual(p.product_manufacturer, '')
|
||||
self.assertEqual(
|
||||
p.product_model, 'IEC 61966-2-1 Default RGB Colour Space - sRGB')
|
||||
|
||||
self.assertEqual(
|
||||
p.profile_description, 'sRGB IEC61966-2-1 black scaled')
|
||||
self.assertEqual(
|
||||
|
@ -393,6 +386,40 @@ class TestImageCms(PillowTestCase):
|
|||
'Reference Viewing Condition in IEC 61966-2-1')
|
||||
self.assertEqual(p.xcolor_space, 'RGB ')
|
||||
|
||||
def test_deprecations(self):
|
||||
self.skip_missing()
|
||||
o = ImageCms.getOpenProfile(SRGB)
|
||||
p = o.profile
|
||||
|
||||
def helper_deprecated(attr, expected):
|
||||
result = self.assert_warning(DeprecationWarning, getattr, p, attr)
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
# p.color_space
|
||||
helper_deprecated("color_space", "RGB")
|
||||
|
||||
# p.pcs
|
||||
helper_deprecated("pcs", "XYZ")
|
||||
|
||||
# p.product_copyright
|
||||
helper_deprecated(
|
||||
"product_copyright", "Copyright International Color Consortium, 2009"
|
||||
)
|
||||
|
||||
# p.product_desc
|
||||
helper_deprecated("product_desc", "sRGB IEC61966-2-1 black scaled")
|
||||
|
||||
# p.product_description
|
||||
helper_deprecated("product_description", "sRGB IEC61966-2-1 black scaled")
|
||||
|
||||
# p.product_manufacturer
|
||||
helper_deprecated("product_manufacturer", "")
|
||||
|
||||
# p.product_model
|
||||
helper_deprecated(
|
||||
"product_model", "IEC 61966-2-1 Default RGB Colour Space - sRGB"
|
||||
)
|
||||
|
||||
def test_profile_typesafety(self):
|
||||
""" Profile init type safety
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from .helper import PillowTestCase, hopper, fromstring, tostring
|
||||
from .helper import unittest, PillowTestCase, hopper, fromstring, tostring
|
||||
|
||||
from io import BytesIO
|
||||
|
||||
|
@ -6,6 +6,12 @@ from PIL import Image
|
|||
from PIL import ImageFile
|
||||
from PIL import EpsImagePlugin
|
||||
|
||||
try:
|
||||
from PIL import _webp
|
||||
HAVE_WEBP = True
|
||||
except ImportError:
|
||||
HAVE_WEBP = False
|
||||
|
||||
|
||||
codecs = dir(Image.core)
|
||||
|
||||
|
@ -233,3 +239,97 @@ class TestPyDecoder(PillowTestCase):
|
|||
im = MockImageFile(buf)
|
||||
self.assertIsNone(im.format)
|
||||
self.assertIsNone(im.get_format_mimetype())
|
||||
|
||||
def test_exif_jpeg(self):
|
||||
im = Image.open("Tests/images/exif-72dpi-int.jpg") # Little endian
|
||||
exif = im.getexif()
|
||||
self.assertNotIn(258, exif)
|
||||
self.assertIn(40960, exif)
|
||||
self.assertEqual(exif[40963], 450)
|
||||
self.assertEqual(exif[11], "gThumb 3.0.1")
|
||||
|
||||
out = self.tempfile('temp.jpg')
|
||||
exif[258] = 8
|
||||
del exif[40960]
|
||||
exif[40963] = 455
|
||||
exif[11] = "Pillow test"
|
||||
im.save(out, exif=exif)
|
||||
reloaded = Image.open(out)
|
||||
reloaded_exif = reloaded.getexif()
|
||||
self.assertEqual(reloaded_exif[258], 8)
|
||||
self.assertNotIn(40960, exif)
|
||||
self.assertEqual(reloaded_exif[40963], 455)
|
||||
self.assertEqual(exif[11], "Pillow test")
|
||||
|
||||
im = Image.open("Tests/images/no-dpi-in-exif.jpg") # Big endian
|
||||
exif = im.getexif()
|
||||
self.assertNotIn(258, exif)
|
||||
self.assertIn(40962, exif)
|
||||
self.assertEqual(exif[40963], 200)
|
||||
self.assertEqual(exif[305], "Adobe Photoshop CC 2017 (Macintosh)")
|
||||
|
||||
out = self.tempfile('temp.jpg')
|
||||
exif[258] = 8
|
||||
del exif[34665]
|
||||
exif[40963] = 455
|
||||
exif[305] = "Pillow test"
|
||||
im.save(out, exif=exif)
|
||||
reloaded = Image.open(out)
|
||||
reloaded_exif = reloaded.getexif()
|
||||
self.assertEqual(reloaded_exif[258], 8)
|
||||
self.assertNotIn(40960, exif)
|
||||
self.assertEqual(reloaded_exif[40963], 455)
|
||||
self.assertEqual(exif[305], "Pillow test")
|
||||
|
||||
@unittest.skipIf(not HAVE_WEBP or not _webp.HAVE_WEBPANIM,
|
||||
"WebP support not installed with animation")
|
||||
def test_exif_webp(self):
|
||||
im = Image.open("Tests/images/hopper.webp")
|
||||
exif = im.getexif()
|
||||
self.assertEqual(exif, {})
|
||||
|
||||
out = self.tempfile('temp.webp')
|
||||
exif[258] = 8
|
||||
exif[40963] = 455
|
||||
exif[305] = "Pillow test"
|
||||
|
||||
def check_exif():
|
||||
reloaded = Image.open(out)
|
||||
reloaded_exif = reloaded.getexif()
|
||||
self.assertEqual(reloaded_exif[258], 8)
|
||||
self.assertEqual(reloaded_exif[40963], 455)
|
||||
self.assertEqual(exif[305], "Pillow test")
|
||||
im.save(out, exif=exif)
|
||||
check_exif()
|
||||
im.save(out, exif=exif, save_all=True)
|
||||
check_exif()
|
||||
|
||||
def test_exif_png(self):
|
||||
im = Image.open("Tests/images/exif.png")
|
||||
exif = im.getexif()
|
||||
self.assertEqual(exif, {274: 1})
|
||||
|
||||
out = self.tempfile('temp.png')
|
||||
exif[258] = 8
|
||||
del exif[274]
|
||||
exif[40963] = 455
|
||||
exif[305] = "Pillow test"
|
||||
im.save(out, exif=exif)
|
||||
|
||||
reloaded = Image.open(out)
|
||||
reloaded_exif = reloaded.getexif()
|
||||
self.assertEqual(reloaded_exif, {
|
||||
258: 8,
|
||||
40963: 455,
|
||||
305: 'Pillow test',
|
||||
})
|
||||
|
||||
def test_exif_interop(self):
|
||||
im = Image.open("Tests/images/flower.jpg")
|
||||
exif = im.getexif()
|
||||
self.assertEqual(exif.get_ifd(0xa005), {
|
||||
1: 'R98',
|
||||
2: b'0100',
|
||||
4097: 2272,
|
||||
4098: 1704,
|
||||
})
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
from .helper import PillowTestCase, hopper
|
||||
|
||||
from PIL import ImageOps
|
||||
from PIL import Image
|
||||
from PIL import ImageOps
|
||||
|
||||
try:
|
||||
from PIL import _webp
|
||||
HAVE_WEBP = True
|
||||
except ImportError:
|
||||
HAVE_WEBP = False
|
||||
|
||||
|
||||
class TestImageOps(PillowTestCase):
|
||||
|
@ -62,6 +68,9 @@ class TestImageOps(PillowTestCase):
|
|||
ImageOps.solarize(hopper("L"))
|
||||
ImageOps.solarize(hopper("RGB"))
|
||||
|
||||
ImageOps.exif_transpose(hopper("L"))
|
||||
ImageOps.exif_transpose(hopper("RGB"))
|
||||
|
||||
def test_1pxfit(self):
|
||||
# Division by zero in equalize if image is 1 pixel high
|
||||
newimg = ImageOps.fit(hopper("RGB").resize((1, 1)), (35, 35))
|
||||
|
@ -218,3 +227,36 @@ class TestImageOps(PillowTestCase):
|
|||
(0, 127, 0),
|
||||
threshold=1,
|
||||
msg='white test pixel incorrect')
|
||||
|
||||
def test_exif_transpose(self):
|
||||
exts = [".jpg"]
|
||||
if HAVE_WEBP and _webp.HAVE_WEBPANIM:
|
||||
exts.append(".webp")
|
||||
for ext in exts:
|
||||
base_im = Image.open("Tests/images/hopper"+ext)
|
||||
|
||||
orientations = [base_im]
|
||||
for i in range(2, 9):
|
||||
im = Image.open("Tests/images/hopper_orientation_"+str(i)+ext)
|
||||
orientations.append(im)
|
||||
for i, orientation_im in enumerate(orientations):
|
||||
for im in [
|
||||
orientation_im, # ImageFile
|
||||
orientation_im.copy() # Image
|
||||
]:
|
||||
if i == 0:
|
||||
self.assertNotIn("exif", im.info)
|
||||
else:
|
||||
original_exif = im.info["exif"]
|
||||
transposed_im = ImageOps.exif_transpose(im)
|
||||
self.assert_image_similar(base_im, transposed_im, 17)
|
||||
if i == 0:
|
||||
self.assertNotIn("exif", im.info)
|
||||
else:
|
||||
self.assertNotEqual(transposed_im.info["exif"], original_exif)
|
||||
|
||||
self.assertNotIn(0x0112, transposed_im.getexif())
|
||||
|
||||
# Repeat the operation, to test that it does not keep transposing
|
||||
transposed_im2 = ImageOps.exif_transpose(transposed_im)
|
||||
self.assert_image_equal(transposed_im2, transposed_im)
|
||||
|
|
|
@ -113,12 +113,7 @@ class TestNumpy(PillowTestCase):
|
|||
img = Image.fromarray(arr * 255).convert('1')
|
||||
self.assertEqual(img.mode, '1')
|
||||
arr_back = numpy.array(img)
|
||||
# numpy 1.8 and earlier return this as a boolean. (trusty/precise)
|
||||
if arr_back.dtype == numpy.bool:
|
||||
arr_bool = numpy.array([[1, 0, 0, 1, 0], [0, 1, 0, 0, 0]], numpy.bool)
|
||||
numpy.testing.assert_array_equal(arr_bool, arr_back)
|
||||
else:
|
||||
numpy.testing.assert_array_equal(arr, arr_back)
|
||||
numpy.testing.assert_array_equal(arr, arr_back)
|
||||
|
||||
def test_save_tiff_uint16(self):
|
||||
# Tests that we're getting the pixel value in the right byte order.
|
||||
|
|
|
@ -10,7 +10,6 @@ except ImportError:
|
|||
|
||||
@unittest.skipIf(pyroma is None, "Pyroma is not installed")
|
||||
class TestPyroma(PillowTestCase):
|
||||
|
||||
def test_pyroma(self):
|
||||
# Arrange
|
||||
data = pyroma.projectdata.get_data(".")
|
||||
|
@ -19,12 +18,13 @@ class TestPyroma(PillowTestCase):
|
|||
rating = pyroma.ratings.rate(data)
|
||||
|
||||
# Assert
|
||||
if 'rc' in __version__:
|
||||
# Pyroma needs to chill about RC versions
|
||||
# and not kill all our tests.
|
||||
self.assertEqual(rating, (9, [
|
||||
"The package's version number does not comply with PEP-386."]))
|
||||
if "rc" in __version__:
|
||||
# Pyroma needs to chill about RC versions and not kill all our tests.
|
||||
self.assertEqual(
|
||||
rating,
|
||||
(9, ["The package's version number does not comply with PEP-386."]),
|
||||
)
|
||||
|
||||
else:
|
||||
# Should have a perfect score
|
||||
self.assertEqual(rating, (10, []))
|
||||
# Should have a near-perfect score
|
||||
self.assertEqual(rating, (9, ["Your package does not have license data."]))
|
||||
|
|
|
@ -11,17 +11,6 @@ class TestFromQPixmap(PillowQPixmapTestCase, PillowTestCase):
|
|||
# Qt saves all pixmaps as rgb
|
||||
self.assert_image_equal(result, expected.convert('RGB'))
|
||||
|
||||
def test_sanity_1(self):
|
||||
self.roundtrip(hopper('1'))
|
||||
|
||||
def test_sanity_rgb(self):
|
||||
self.roundtrip(hopper('RGB'))
|
||||
|
||||
def test_sanity_rgba(self):
|
||||
self.roundtrip(hopper('RGBA'))
|
||||
|
||||
def test_sanity_l(self):
|
||||
self.roundtrip(hopper('L'))
|
||||
|
||||
def test_sanity_p(self):
|
||||
self.roundtrip(hopper('P'))
|
||||
def test_sanity(self):
|
||||
for mode in ('1', 'RGB', 'RGBA', 'L', 'P'):
|
||||
self.roundtrip(hopper(mode))
|
||||
|
|
|
@ -21,11 +21,6 @@ jobs:
|
|||
docker: 'arch'
|
||||
name: 'arch'
|
||||
|
||||
- template: .azure-pipelines/jobs/test-docker.yml
|
||||
parameters:
|
||||
docker: 'ubuntu-trusty-x86'
|
||||
name: 'ubuntu_trusty_x86'
|
||||
|
||||
- template: .azure-pipelines/jobs/test-docker.yml
|
||||
parameters:
|
||||
docker: 'ubuntu-xenial-amd64'
|
||||
|
|
|
@ -79,6 +79,26 @@ PILLOW_VERSION constant
|
|||
``PILLOW_VERSION`` has been deprecated and will be removed in the next
|
||||
major release. Use ``__version__`` instead.
|
||||
|
||||
ImageCms.CmsProfile attributes
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 3.2.0
|
||||
|
||||
Some attributes in ``ImageCms.CmsProfile`` are deprecated. From 6.0.0, they issue a
|
||||
``DeprecationWarning``:
|
||||
|
||||
======================== ===============================
|
||||
Deprecated Use instead
|
||||
======================== ===============================
|
||||
``color_space`` Padded ``xcolor_space``
|
||||
``pcs`` Padded ``connection_space``
|
||||
``product_copyright`` Unicode ``copyright``
|
||||
``product_desc`` Unicode ``profile_description``
|
||||
``product_description`` Unicode ``profile_description``
|
||||
``product_manufacturer`` Unicode ``manufacturer``
|
||||
``product_model`` Unicode ``model``
|
||||
======================== ===============================
|
||||
|
||||
Removed features
|
||||
----------------
|
||||
|
||||
|
|
|
@ -104,11 +104,11 @@ The :py:meth:`~PIL.Image.Image.open` method sets the following
|
|||
Reading sequences
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
The GIF loader supports the :py:meth:`~file.seek` and :py:meth:`~file.tell`
|
||||
methods. You can combine these methods to seek to the next frame
|
||||
(``im.seek(im.tell() + 1)``).
|
||||
The GIF loader supports the :py:meth:`~PIL.Image.Image.seek` and
|
||||
:py:meth:`~PIL.Image.Image.tell` methods. You can combine these methods
|
||||
to seek to the next frame (``im.seek(im.tell() + 1)``).
|
||||
|
||||
``im.seek()`` raises an ``EOFError`` if you try to seek after the last frame.
|
||||
``im.seek()`` raises an :py:exc:`EOFError` if you try to seek after the last frame.
|
||||
|
||||
Saving
|
||||
~~~~~~
|
||||
|
@ -464,8 +464,9 @@ Pillow identifies, reads, and writes PNG files containing ``1``, ``L``, ``LA``,
|
|||
v1.1.7.
|
||||
|
||||
As of Pillow 6.0, EXIF data can be read from PNG images. However, unlike other
|
||||
image formats, EXIF data is not guaranteed to have been read until
|
||||
:py:meth:`~PIL.Image.Image.load` has been called.
|
||||
image formats, EXIF data is not guaranteed to be present in
|
||||
:py:attr:`~PIL.Image.Image.info` until :py:meth:`~PIL.Image.Image.load` has been
|
||||
called.
|
||||
|
||||
The :py:meth:`~PIL.Image.Image.open` method sets the following
|
||||
:py:attr:`~PIL.Image.Image.info` properties, when appropriate:
|
||||
|
@ -490,12 +491,12 @@ The :py:meth:`~PIL.Image.Image.open` method sets the following
|
|||
For ``P`` images: Either the palette index for full transparent pixels,
|
||||
or a byte string with alpha values for each palette entry.
|
||||
|
||||
For ``L`` and ``RGB`` images, the color that represents full transparent
|
||||
pixels in this image.
|
||||
For ``1``, ``L``, ``I`` and ``RGB`` images, the color that represents
|
||||
full transparent pixels in this image.
|
||||
|
||||
This key is omitted if the image is not a transparent palette image.
|
||||
|
||||
``Open`` also sets ``Image.text`` to a dictionary 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
|
||||
|
@ -511,8 +512,8 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
|
|||
encoder settings.
|
||||
|
||||
**transparency**
|
||||
For ``P``, ``L``, and ``RGB`` images, this option controls what
|
||||
color image to mark as transparent.
|
||||
For ``P``, ``1``, ``L``, ``I``, and ``RGB`` images, this option controls
|
||||
what color from the image to mark as transparent.
|
||||
|
||||
For ``P`` images, this can be a either the palette index,
|
||||
or a byte string with alpha values for each palette entry.
|
||||
|
@ -569,7 +570,7 @@ Pillow reads and writes SPIDER image files of 32-bit floating point data
|
|||
("F;32F").
|
||||
|
||||
Pillow also reads SPIDER stack files containing sequences of SPIDER images. The
|
||||
:py:meth:`~file.seek` and :py:meth:`~file.tell` methods are supported, and
|
||||
:py:meth:`~PIL.Image.Image.seek` and :py:meth:`~PIL.Image.Image.tell` methods are supported, and
|
||||
random access is allowed.
|
||||
|
||||
The :py:meth:`~PIL.Image.Image.open` method sets the following attributes:
|
||||
|
@ -580,7 +581,7 @@ The :py:meth:`~PIL.Image.Image.open` method sets the following attributes:
|
|||
**istack**
|
||||
Set to 1 if the file is an image stack, else 0.
|
||||
|
||||
**nimages**
|
||||
**n_frames**
|
||||
Set to the number of images in the stack.
|
||||
|
||||
A convenience method, :py:meth:`~PIL.Image.Image.convert2byte`, is provided for
|
||||
|
@ -664,6 +665,17 @@ numbers are returned as a tuple of ``(numerator, denominator)``.
|
|||
|
||||
.. deprecated:: 3.0.0
|
||||
|
||||
Reading Multi-frame TIFF Images
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The TIFF loader supports the :py:meth:`~PIL.Image.Image.seek` and
|
||||
:py:meth:`~PIL.Image.Image.tell` methods, taking and returning frame numbers
|
||||
within the image file. You can combine these methods to seek to the next frame
|
||||
(``im.seek(im.tell() + 1)``). Frames are numbered from 0 to ``im.num_frames - 1``,
|
||||
and can be accessed in any order.
|
||||
|
||||
``im.seek()`` raises an :py:exc:`EOFError` if you try to seek after the
|
||||
last frame.
|
||||
|
||||
Saving Tiff Images
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
@ -851,7 +863,7 @@ is commonly used in fax applications. The DCX decoder can read files containing
|
|||
``1``, ``L``, ``P``, or ``RGB`` data.
|
||||
|
||||
When the file is opened, only the first image is read. You can use
|
||||
:py:meth:`~file.seek` or :py:mod:`~PIL.ImageSequence` to read other images.
|
||||
:py:meth:`~PIL.Image.Image.seek` or :py:mod:`~PIL.ImageSequence` to read other images.
|
||||
|
||||
|
||||
DDS
|
||||
|
@ -943,8 +955,8 @@ MIC
|
|||
^^^
|
||||
|
||||
Pillow identifies and reads Microsoft Image Composer (MIC) files. When opened,
|
||||
the first sprite in the file is loaded. You can use :py:meth:`~file.seek` and
|
||||
:py:meth:`~file.tell` to read other sprites from the file.
|
||||
the first sprite in the file is loaded. You can use :py:meth:`~PIL.Image.Image.seek` and
|
||||
:py:meth:`~PIL.Image.Image.tell` to read other sprites from the file.
|
||||
|
||||
Note that there may be an embedded gamma of 2.2 in MIC files.
|
||||
|
||||
|
@ -952,7 +964,7 @@ MPO
|
|||
^^^
|
||||
|
||||
Pillow identifies and reads Multi Picture Object (MPO) files, loading the primary
|
||||
image when first opened. The :py:meth:`~file.seek` and :py:meth:`~file.tell`
|
||||
image when first opened. The :py:meth:`~PIL.Image.Image.seek` and :py:meth:`~PIL.Image.Image.tell`
|
||||
methods may be used to read other pictures from the file. The pictures are
|
||||
zero-indexed and random access is supported.
|
||||
|
||||
|
|
|
@ -30,8 +30,8 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors <https://github.
|
|||
:target: https://pypi.org/project/Pillow/
|
||||
:alt: Number of PyPI downloads
|
||||
|
||||
.. image:: https://coveralls.io/repos/python-pillow/Pillow/badge.svg?branch=master&service=github
|
||||
:target: https://coveralls.io/github/python-pillow/Pillow?branch=master
|
||||
.. image:: https://codecov.io/gh/python-pillow/Pillow/branch/master/graph/badge.svg
|
||||
:target: https://codecov.io/gh/python-pillow/Pillow
|
||||
:alt: Code coverage
|
||||
|
||||
.. toctree::
|
||||
|
|
|
@ -151,7 +151,7 @@ Many of Pillow's features require external libraries:
|
|||
* **littlecms** provides color management
|
||||
|
||||
* Pillow version 2.2.1 and below uses liblcms1, Pillow 2.3.0 and
|
||||
above uses liblcms2. Tested with **1.19** and **2.7**.
|
||||
above uses liblcms2. Tested with **1.19** and **2.7-2.9**.
|
||||
|
||||
* **libwebp** provides the WebP format.
|
||||
|
||||
|
@ -165,7 +165,7 @@ Many of Pillow's features require external libraries:
|
|||
|
||||
* Pillow has been tested with openjpeg **2.0.0** and **2.1.0**.
|
||||
* Pillow does **not** support the earlier **1.5** series which ships
|
||||
with Ubuntu <= 14.04 and Debian Jessie.
|
||||
with Debian Jessie.
|
||||
|
||||
* **libimagequant** provides improved color quantization
|
||||
|
||||
|
@ -403,10 +403,6 @@ These platforms are built and tested for every change.
|
|||
| Ubuntu Linux 16.04 LTS | 2.7, 3.5, 3.6, 3.7, |x86-64 |
|
||||
| | PyPy, PyPy3 | |
|
||||
+----------------------------------+-------------------------------+-----------------------+
|
||||
| Ubuntu Linux 14.04 LTS | 2.7, 3.5, 3.6 |x86-64 |
|
||||
| +-------------------------------+-----------------------+
|
||||
| | 2.7 |x86 |
|
||||
+----------------------------------+-------------------------------+-----------------------+
|
||||
| Windows Server 2012 R2 | 2.7, 3.5, 3.6, 3.7 |x86, x86-64 |
|
||||
| +-------------------------------+-----------------------+
|
||||
| | PyPy, 3.7/MinGW |x86 |
|
||||
|
|
|
@ -132,21 +132,21 @@ can be easily displayed in a chromaticity diagram, for example).
|
|||
|
||||
.. py:attribute:: manufacturer
|
||||
|
||||
The (english) display string for the device manufacturer (see
|
||||
The (English) display string for the device manufacturer (see
|
||||
9.2.22 of ICC.1:2010).
|
||||
|
||||
:type: :py:class:`unicode` or ``None``
|
||||
|
||||
.. py:attribute:: model
|
||||
|
||||
The (english) display string for the device model of the device
|
||||
The (English) display string for the device model of the device
|
||||
for which this profile is created (see 9.2.23 of ICC.1:2010).
|
||||
|
||||
:type: :py:class:`unicode` or ``None``
|
||||
|
||||
.. py:attribute:: profile_description
|
||||
|
||||
The (english) display string for the profile description (see
|
||||
The (English) display string for the profile description (see
|
||||
9.2.41 of ICC.1:2010).
|
||||
|
||||
:type: :py:class:`unicode` or ``None``
|
||||
|
@ -269,14 +269,14 @@ can be easily displayed in a chromaticity diagram, for example).
|
|||
|
||||
.. py:attribute:: viewing_condition
|
||||
|
||||
The (english) display string for the viewing conditions (see
|
||||
The (English) display string for the viewing conditions (see
|
||||
9.2.48 of ICC.1:2010).
|
||||
|
||||
:type: :py:class:`unicode` or ``None``
|
||||
|
||||
.. py:attribute:: screening_description
|
||||
|
||||
The (english) display string for the screening conditions.
|
||||
The (English) display string for the screening conditions.
|
||||
|
||||
This tag was available in ICC 3.2, but it is removed from
|
||||
version 4.
|
||||
|
|
|
@ -99,6 +99,24 @@ version.
|
|||
|
||||
Use ``PIL.__version__`` instead.
|
||||
|
||||
ImageCms.CmsProfile attributes
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Some attributes in ``ImageCms.CmsProfile`` have been deprecated since Pillow 3.2.0. From
|
||||
6.0.0, they issue a ``DeprecationWarning``:
|
||||
|
||||
======================== ===============================
|
||||
Deprecated Use instead
|
||||
======================== ===============================
|
||||
``color_space`` Padded ``xcolor_space``
|
||||
``pcs`` Padded ``connection_space``
|
||||
``product_copyright`` Unicode ``copyright``
|
||||
``product_desc`` Unicode ``profile_description``
|
||||
``product_description`` Unicode ``profile_description``
|
||||
``product_manufacturer`` Unicode ``manufacturer``
|
||||
``product_model`` Unicode ``model``
|
||||
======================== ===============================
|
||||
|
||||
MIME type improvements
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@ -131,7 +149,7 @@ Image.quantize
|
|||
^^^^^^^^^^^^^^
|
||||
|
||||
The ``dither`` option is now a customisable parameter (was previously hardcoded to ``1``).
|
||||
This parameter takes the same values used in ``Image.convert``.
|
||||
This parameter takes the same values used in :py:meth:`~PIL.Image.Image.convert`.
|
||||
|
||||
New language parameter
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -147,12 +165,26 @@ language-specific glyphs and ligatures from the font:
|
|||
* ``ImageFont.ImageFont.getsize_multiline()``
|
||||
* ``ImageFont.ImageFont.getsize()``
|
||||
|
||||
Added EXIF class
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
:py:meth:`~PIL.Image.Image.getexif` has been added, which returns an
|
||||
:py:class:`~PIL.Image.Exif` instance. Values can be retrieved and set like a
|
||||
dictionary. When saving JPEG, PNG or WEBP, the instance can be passed as an
|
||||
``exif`` argument to include any changes in the output image.
|
||||
|
||||
Added ImageOps.exif_transpose
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
:py:meth:`~PIL.ImageOps.exif_transpose` returns a copy of an image, transposed
|
||||
according to its EXIF Orientation tag.
|
||||
|
||||
PNG EXIF data
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
EXIF data can now be read from and saved to PNG images. However, unlike other image
|
||||
formats, EXIF data is not guaranteed to have been read until
|
||||
:py:meth:`~PIL.Image.Image.load` has been called.
|
||||
formats, EXIF data is not guaranteed to be present in :py:attr:`~PIL.Image.Image.info`
|
||||
until :py:meth:`~PIL.Image.Image.load` has been called.
|
||||
|
||||
Other Changes
|
||||
=============
|
||||
|
@ -172,3 +204,9 @@ TIFF compression codecs
|
|||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Support has been added for the LZMA, Zstd and WebP TIFF compression codecs.
|
||||
|
||||
Improved support for transposing I;16 images
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
I;16, I;16L and I;16B are now supported image modes for all
|
||||
:py:meth:`~PIL.Image.Image.transpose` operations.
|
||||
|
|
13
setup.py
|
@ -765,12 +765,7 @@ try:
|
|||
url='http://python-pillow.org',
|
||||
classifiers=[
|
||||
"Development Status :: 6 - Mature",
|
||||
"Topic :: Multimedia :: Graphics",
|
||||
"Topic :: Multimedia :: Graphics :: Capture :: Digital Camera",
|
||||
"Topic :: Multimedia :: Graphics :: Capture :: Screen Capture",
|
||||
"Topic :: Multimedia :: Graphics :: Graphics Conversion",
|
||||
"Topic :: Multimedia :: Graphics :: Viewers",
|
||||
"License :: Other/Proprietary License",
|
||||
"License :: OSI Approved :: Historical Permission Notice and Disclaimer (HPND)", # noqa: E501
|
||||
"Programming Language :: Python :: 2",
|
||||
"Programming Language :: Python :: 2.7",
|
||||
"Programming Language :: Python :: 3",
|
||||
|
@ -779,6 +774,11 @@ try:
|
|||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: Implementation :: CPython",
|
||||
"Programming Language :: Python :: Implementation :: PyPy",
|
||||
"Topic :: Multimedia :: Graphics",
|
||||
"Topic :: Multimedia :: Graphics :: Capture :: Digital Camera",
|
||||
"Topic :: Multimedia :: Graphics :: Capture :: Screen Capture",
|
||||
"Topic :: Multimedia :: Graphics :: Graphics Conversion",
|
||||
"Topic :: Multimedia :: Graphics :: Viewers",
|
||||
],
|
||||
python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*",
|
||||
cmdclass={"build_ext": pil_build_ext},
|
||||
|
@ -789,7 +789,6 @@ try:
|
|||
packages=["PIL"],
|
||||
package_dir={'': 'src'},
|
||||
keywords=["Imaging", ],
|
||||
license='Standard PIL License',
|
||||
zip_safe=not (debug_build() or PLATFORM_MINGW), )
|
||||
except RequiredDependencyException as err:
|
||||
msg = """
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
from . import Image, ImageFile, ImagePalette
|
||||
from ._binary import i8, i16le as i16, i32le as i32, \
|
||||
o8, o16le as o16, o32le as o32
|
||||
import math
|
||||
|
||||
# __version__ is deprecated and will be removed in a future version. Use
|
||||
# PIL.__version__ instead.
|
||||
|
@ -121,8 +120,7 @@ class BmpImageFile(ImageFile.ImageFile):
|
|||
file_info['colors'] = i32(header_data[28:32])
|
||||
file_info['palette_padding'] = 4
|
||||
self.info["dpi"] = tuple(
|
||||
map(lambda x: int(math.ceil(x / 39.3701)),
|
||||
file_info['pixels_per_meter']))
|
||||
int(x / 39.3701 + 0.5) for x in file_info['pixels_per_meter'])
|
||||
if file_info['compression'] == self.BITFIELDS:
|
||||
if len(header_data) >= 52:
|
||||
for idx, mask in enumerate(['r_mask',
|
||||
|
@ -311,7 +309,7 @@ def _save(im, fp, filename, bitmap_header=True):
|
|||
dpi = info.get("dpi", (96, 96))
|
||||
|
||||
# 1 meter == 39.3701 inches
|
||||
ppm = tuple(map(lambda x: int(x * 39.3701), dpi))
|
||||
ppm = tuple(map(lambda x: int(x * 39.3701 + 0.5), dpi))
|
||||
|
||||
stride = ((im.size[0]*bits+7)//8+3) & (~3)
|
||||
header = 40 # or 64 for OS/2 version 2
|
||||
|
|
|
@ -32,14 +32,12 @@ class GimpPaletteFile(object):
|
|||
if fp.readline()[:12] != b"GIMP Palette":
|
||||
raise SyntaxError("not a GIMP palette file")
|
||||
|
||||
i = 0
|
||||
|
||||
while i <= 255:
|
||||
for i in range(256):
|
||||
|
||||
s = fp.readline()
|
||||
|
||||
if not s:
|
||||
break
|
||||
|
||||
# skip fields and comment lines
|
||||
if re.match(br"\w+:|#", s):
|
||||
continue
|
||||
|
@ -50,10 +48,7 @@ class GimpPaletteFile(object):
|
|||
if len(v) != 3:
|
||||
raise ValueError("bad palette entry")
|
||||
|
||||
if 0 <= i <= 255:
|
||||
self.palette[i] = o8(v[0]) + o8(v[1]) + o8(v[2])
|
||||
|
||||
i += 1
|
||||
self.palette[i] = o8(v[0]) + o8(v[1]) + o8(v[2])
|
||||
|
||||
self.palette = b"".join(self.palette)
|
||||
|
||||
|
|
236
src/PIL/Image.py
|
@ -40,8 +40,8 @@ except ImportError:
|
|||
import __builtin__
|
||||
builtins = __builtin__
|
||||
|
||||
from . import ImageMode
|
||||
from ._binary import i8
|
||||
from . import ImageMode, TiffTags
|
||||
from ._binary import i8, i32le
|
||||
from ._util import isPath, isStringType, deferred_error
|
||||
|
||||
import os
|
||||
|
@ -54,10 +54,10 @@ import atexit
|
|||
import numbers
|
||||
try:
|
||||
# Python 3
|
||||
from collections.abc import Callable
|
||||
from collections.abc import Callable, MutableMapping
|
||||
except ImportError:
|
||||
# Python 2.7
|
||||
from collections import Callable
|
||||
from collections import Callable, MutableMapping
|
||||
|
||||
|
||||
# Silence warning
|
||||
|
@ -247,7 +247,7 @@ _MODEINFO = {
|
|||
"L": ("L", "L", ("L",)),
|
||||
"I": ("L", "I", ("I",)),
|
||||
"F": ("L", "F", ("F",)),
|
||||
"P": ("RGB", "L", ("P",)),
|
||||
"P": ("P", "L", ("P",)),
|
||||
"RGB": ("RGB", "L", ("R", "G", "B")),
|
||||
"RGBX": ("RGB", "L", ("R", "G", "B", "X")),
|
||||
"RGBA": ("RGB", "L", ("R", "G", "B", "A")),
|
||||
|
@ -950,7 +950,7 @@ class Image(object):
|
|||
delete_trns = False
|
||||
# transparency handling
|
||||
if has_transparency:
|
||||
if self.mode in ('L', 'RGB') and mode == 'RGBA':
|
||||
if self.mode in ('1', 'L', 'I', 'RGB') and mode == 'RGBA':
|
||||
# Use transparent conversion to promote from transparent
|
||||
# color to an alpha channel.
|
||||
new_im = self._new(self.im.convert_transparent(
|
||||
|
@ -1096,7 +1096,13 @@ class Image(object):
|
|||
im = self.im.convert("P", dither, palette.im)
|
||||
return self._new(im)
|
||||
|
||||
return self._new(self.im.quantize(colors, method, kmeans))
|
||||
im = self._new(self.im.quantize(colors, method, kmeans))
|
||||
|
||||
from . import ImagePalette
|
||||
mode = im.im.getpalettemode()
|
||||
im.palette = ImagePalette.ImagePalette(mode, im.im.getpalette(mode, mode))
|
||||
|
||||
return im
|
||||
|
||||
def copy(self):
|
||||
"""
|
||||
|
@ -1291,6 +1297,12 @@ class Image(object):
|
|||
return tuple(extrema)
|
||||
return self.im.getextrema()
|
||||
|
||||
def getexif(self):
|
||||
exif = Exif()
|
||||
if "exif" in self.info:
|
||||
exif.load(self.info["exif"])
|
||||
return exif
|
||||
|
||||
def getim(self):
|
||||
"""
|
||||
Returns a capsule that points to the internal image memory.
|
||||
|
@ -1559,7 +1571,7 @@ class Image(object):
|
|||
|
||||
self._ensure_mutable()
|
||||
|
||||
if self.mode not in ("LA", "RGBA"):
|
||||
if self.mode not in ("LA", "PA", "RGBA"):
|
||||
# attempt to promote self to a matching alpha mode
|
||||
try:
|
||||
mode = getmodebase(self.mode) + "A"
|
||||
|
@ -1568,7 +1580,7 @@ class Image(object):
|
|||
except (AttributeError, ValueError):
|
||||
# do things the hard way
|
||||
im = self.im.convert(mode)
|
||||
if im.mode not in ("LA", "RGBA"):
|
||||
if im.mode not in ("LA", "PA", "RGBA"):
|
||||
raise ValueError # sanity check
|
||||
self.im = im
|
||||
self.pyaccess = None
|
||||
|
@ -1576,7 +1588,7 @@ class Image(object):
|
|||
except (KeyError, ValueError):
|
||||
raise ValueError("illegal image mode")
|
||||
|
||||
if self.mode == "LA":
|
||||
if self.mode in ("LA", "PA"):
|
||||
band = 1
|
||||
else:
|
||||
band = 3
|
||||
|
@ -1619,10 +1631,10 @@ class Image(object):
|
|||
|
||||
def putpalette(self, data, rawmode="RGB"):
|
||||
"""
|
||||
Attaches a palette to this image. The image must be a "P" or
|
||||
"L" image, and the palette sequence must contain 768 integer
|
||||
values, where each group of three values represent the red,
|
||||
green, and blue values for the corresponding pixel
|
||||
Attaches a palette to this image. The image must be a "P",
|
||||
"PA", "L" or "LA" image, and the palette sequence must contain
|
||||
768 integer values, where each group of three values represent
|
||||
the red, green, and blue values for the corresponding pixel
|
||||
index. Instead of an integer sequence, you can use an 8-bit
|
||||
string.
|
||||
|
||||
|
@ -1631,7 +1643,7 @@ class Image(object):
|
|||
"""
|
||||
from . import ImagePalette
|
||||
|
||||
if self.mode not in ("L", "P"):
|
||||
if self.mode not in ("L", "LA", "P", "PA"):
|
||||
raise ValueError("illegal image mode")
|
||||
self.load()
|
||||
if isinstance(data, ImagePalette.ImagePalette):
|
||||
|
@ -1643,7 +1655,7 @@ class Image(object):
|
|||
else:
|
||||
data = "".join(chr(x) for x in data)
|
||||
palette = ImagePalette.raw(rawmode, data)
|
||||
self.mode = "P"
|
||||
self.mode = "PA" if "A" in self.mode else "P"
|
||||
self.palette = palette
|
||||
self.palette.mode = "RGB"
|
||||
self.load() # install new palette
|
||||
|
@ -1688,7 +1700,7 @@ class Image(object):
|
|||
Rewrites the image to reorder the palette.
|
||||
|
||||
:param dest_map: A list of indexes into the original palette.
|
||||
e.g. [1,0] would swap a two item palette, and list(range(255))
|
||||
e.g. [1,0] would swap a two item palette, and list(range(256))
|
||||
is the identity transform.
|
||||
:param source_palette: Bytes or None.
|
||||
:returns: An :py:class:`~PIL.Image.Image` object.
|
||||
|
@ -1958,7 +1970,7 @@ class Image(object):
|
|||
filename = fp.name
|
||||
|
||||
# may mutate self!
|
||||
self.load()
|
||||
self._ensure_mutable()
|
||||
|
||||
save_all = params.pop('save_all', False)
|
||||
self.encoderinfo = params
|
||||
|
@ -2371,7 +2383,14 @@ def new(mode, size, color=0):
|
|||
from . import ImageColor
|
||||
color = ImageColor.getcolor(color, mode)
|
||||
|
||||
return Image()._new(core.fill(mode, size, color))
|
||||
im = Image()
|
||||
if mode == "P" and \
|
||||
isinstance(color, (list, tuple)) and len(color) in [3, 4]:
|
||||
# RGB or RGBA value for a P image
|
||||
from . import ImagePalette
|
||||
im.palette = ImagePalette.ImagePalette()
|
||||
color = im.palette.getcolor(color)
|
||||
return im._new(core.fill(mode, size, color))
|
||||
|
||||
|
||||
def frombytes(mode, size, data, decoder_name="raw", *args):
|
||||
|
@ -2992,3 +3011,182 @@ def _apply_env_variables(env=None):
|
|||
|
||||
_apply_env_variables()
|
||||
atexit.register(core.clear_cache)
|
||||
|
||||
|
||||
class Exif(MutableMapping):
|
||||
endian = "<"
|
||||
|
||||
def __init__(self):
|
||||
self._data = {}
|
||||
self._ifds = {}
|
||||
|
||||
def _fixup_dict(self, src_dict):
|
||||
# Helper function for _getexif()
|
||||
# returns a dict with any single item tuples/lists as individual values
|
||||
def _fixup(value):
|
||||
try:
|
||||
if len(value) == 1 and not isinstance(value, dict):
|
||||
return value[0]
|
||||
except Exception:
|
||||
pass
|
||||
return value
|
||||
|
||||
return {k: _fixup(v) for k, v in src_dict.items()}
|
||||
|
||||
def _get_ifd_dict(self, tag):
|
||||
try:
|
||||
# an offset pointer to the location of the nested embedded IFD.
|
||||
# It should be a long, but may be corrupted.
|
||||
self.fp.seek(self._data[tag])
|
||||
except (KeyError, TypeError):
|
||||
pass
|
||||
else:
|
||||
from . import TiffImagePlugin
|
||||
info = TiffImagePlugin.ImageFileDirectory_v1(self.head)
|
||||
info.load(self.fp)
|
||||
return self._fixup_dict(info)
|
||||
|
||||
def load(self, data):
|
||||
# Extract EXIF information. This is highly experimental,
|
||||
# and is likely to be replaced with something better in a future
|
||||
# version.
|
||||
|
||||
# The EXIF record consists of a TIFF file embedded in a JPEG
|
||||
# application marker (!).
|
||||
self.fp = io.BytesIO(data[6:])
|
||||
self.head = self.fp.read(8)
|
||||
# process dictionary
|
||||
from . import TiffImagePlugin
|
||||
info = TiffImagePlugin.ImageFileDirectory_v1(self.head)
|
||||
self.endian = info._endian
|
||||
self.fp.seek(info.next)
|
||||
info.load(self.fp)
|
||||
self._data = dict(self._fixup_dict(info))
|
||||
|
||||
# get EXIF extension
|
||||
ifd = self._get_ifd_dict(0x8769)
|
||||
if ifd:
|
||||
self._data.update(ifd)
|
||||
self._ifds[0x8769] = ifd
|
||||
|
||||
# get gpsinfo extension
|
||||
ifd = self._get_ifd_dict(0x8825)
|
||||
if ifd:
|
||||
self._data[0x8825] = ifd
|
||||
self._ifds[0x8825] = ifd
|
||||
|
||||
def tobytes(self, offset=0):
|
||||
from . import TiffImagePlugin
|
||||
if self.endian == "<":
|
||||
head = b"II\x2A\x00\x08\x00\x00\x00"
|
||||
else:
|
||||
head = b"MM\x00\x2A\x00\x00\x00\x08"
|
||||
ifd = TiffImagePlugin.ImageFileDirectory_v2(ifh=head)
|
||||
for tag, value in self._data.items():
|
||||
ifd[tag] = value
|
||||
return b"Exif\x00\x00"+head+ifd.tobytes(offset)
|
||||
|
||||
def get_ifd(self, tag):
|
||||
if tag not in self._ifds and tag in self._data:
|
||||
if tag == 0xa005: # interop
|
||||
self._ifds[tag] = self._get_ifd_dict(tag)
|
||||
elif tag == 0x927c: # makernote
|
||||
from . import TiffImagePlugin
|
||||
if self._data[0x927c][:8] == b"FUJIFILM":
|
||||
exif_data = self._data[0x927c]
|
||||
ifd_offset = i32le(exif_data[8:12])
|
||||
ifd_data = exif_data[ifd_offset:]
|
||||
|
||||
makernote = {}
|
||||
for i in range(0, struct.unpack("<H", ifd_data[:2])[0]):
|
||||
ifd_tag, typ, count, data = struct.unpack(
|
||||
"<HHL4s", ifd_data[i*12 + 2:(i+1)*12 + 2])
|
||||
try:
|
||||
unit_size, handler =\
|
||||
TiffImagePlugin.ImageFileDirectory_v2._load_dispatch[
|
||||
typ
|
||||
]
|
||||
except KeyError:
|
||||
continue
|
||||
size = count * unit_size
|
||||
if size > 4:
|
||||
offset, = struct.unpack("<L", data)
|
||||
data = ifd_data[offset-12:offset+size-12]
|
||||
else:
|
||||
data = data[:size]
|
||||
|
||||
if len(data) != size:
|
||||
warnings.warn("Possibly corrupt EXIF MakerNote data. "
|
||||
"Expecting to read %d bytes but only got %d."
|
||||
" Skipping tag %s"
|
||||
% (size, len(data), ifd_tag))
|
||||
continue
|
||||
|
||||
if not data:
|
||||
continue
|
||||
|
||||
makernote[ifd_tag] = handler(
|
||||
TiffImagePlugin.ImageFileDirectory_v2(), data, False)
|
||||
self._ifds[0x927c] = dict(self._fixup_dict(makernote))
|
||||
elif self._data.get(0x010f) == "Nintendo":
|
||||
ifd_data = self._data[0x927c]
|
||||
|
||||
makernote = {}
|
||||
for i in range(0, struct.unpack(">H", ifd_data[:2])[0]):
|
||||
ifd_tag, typ, count, data = struct.unpack(
|
||||
">HHL4s", ifd_data[i*12 + 2:(i+1)*12 + 2])
|
||||
if ifd_tag == 0x1101:
|
||||
# CameraInfo
|
||||
offset, = struct.unpack(">L", data)
|
||||
self.fp.seek(offset)
|
||||
|
||||
camerainfo = {'ModelID': self.fp.read(4)}
|
||||
|
||||
self.fp.read(4)
|
||||
# Seconds since 2000
|
||||
camerainfo['TimeStamp'] = i32le(self.fp.read(12))
|
||||
|
||||
self.fp.read(4)
|
||||
camerainfo['InternalSerialNumber'] = self.fp.read(4)
|
||||
|
||||
self.fp.read(12)
|
||||
parallax = self.fp.read(4)
|
||||
handler =\
|
||||
TiffImagePlugin.ImageFileDirectory_v2._load_dispatch[
|
||||
TiffTags.FLOAT
|
||||
][1]
|
||||
camerainfo['Parallax'] = handler(
|
||||
TiffImagePlugin.ImageFileDirectory_v2(),
|
||||
parallax, False)
|
||||
|
||||
self.fp.read(4)
|
||||
camerainfo['Category'] = self.fp.read(2)
|
||||
|
||||
makernote = {0x1101: dict(self._fixup_dict(camerainfo))}
|
||||
self._ifds[0x927c] = makernote
|
||||
return self._ifds.get(tag, {})
|
||||
|
||||
def __str__(self):
|
||||
return str(self._data)
|
||||
|
||||
def __len__(self):
|
||||
return len(self._data)
|
||||
|
||||
def __getitem__(self, tag):
|
||||
return self._data[tag]
|
||||
|
||||
def __contains__(self, tag):
|
||||
return tag in self._data
|
||||
|
||||
if not py3:
|
||||
def has_key(self, tag):
|
||||
return tag in self
|
||||
|
||||
def __setitem__(self, tag, value):
|
||||
self._data[tag] = value
|
||||
|
||||
def __delitem__(self, tag):
|
||||
del self._data[tag]
|
||||
|
||||
def __iter__(self):
|
||||
return iter(set(self._data))
|
||||
|
|
|
@ -686,11 +686,11 @@ def getProfileName(profile):
|
|||
# // 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
|
||||
model = profile.profile.model
|
||||
manufacturer = profile.profile.manufacturer
|
||||
|
||||
if not (model or manufacturer):
|
||||
return profile.profile.product_description + "\n"
|
||||
return (profile.profile.profile_description or "") + "\n"
|
||||
if not manufacturer or len(model) > 30:
|
||||
return model + "\n"
|
||||
return "%s - %s\n" % (model, manufacturer)
|
||||
|
@ -727,8 +727,8 @@ def getProfileInfo(profile):
|
|||
# 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
|
||||
description = profile.profile.profile_description
|
||||
cpright = profile.profile.copyright
|
||||
arr = []
|
||||
for elt in (description, cpright):
|
||||
if elt:
|
||||
|
@ -762,7 +762,7 @@ def getProfileCopyright(profile):
|
|||
# add an extra newline to preserve pyCMS compatibility
|
||||
if not isinstance(profile, ImageCmsProfile):
|
||||
profile = ImageCmsProfile(profile)
|
||||
return profile.profile.product_copyright + "\n"
|
||||
return (profile.profile.copyright or "") + "\n"
|
||||
except (AttributeError, IOError, TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
|
||||
|
@ -790,7 +790,7 @@ def getProfileManufacturer(profile):
|
|||
# add an extra newline to preserve pyCMS compatibility
|
||||
if not isinstance(profile, ImageCmsProfile):
|
||||
profile = ImageCmsProfile(profile)
|
||||
return profile.profile.product_manufacturer + "\n"
|
||||
return (profile.profile.manufacturer or "") + "\n"
|
||||
except (AttributeError, IOError, TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
|
||||
|
@ -819,7 +819,7 @@ def getProfileModel(profile):
|
|||
# add an extra newline to preserve pyCMS compatibility
|
||||
if not isinstance(profile, ImageCmsProfile):
|
||||
profile = ImageCmsProfile(profile)
|
||||
return profile.profile.product_model + "\n"
|
||||
return (profile.profile.model or "") + "\n"
|
||||
except (AttributeError, IOError, TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
|
||||
|
@ -848,7 +848,7 @@ def getProfileDescription(profile):
|
|||
# add an extra newline to preserve pyCMS compatibility
|
||||
if not isinstance(profile, ImageCmsProfile):
|
||||
profile = ImageCmsProfile(profile)
|
||||
return profile.profile.product_description + "\n"
|
||||
return (profile.profile.profile_description or "") + "\n"
|
||||
except (AttributeError, IOError, TypeError, ValueError) as v:
|
||||
raise PyCMSError(v)
|
||||
|
||||
|
|
|
@ -522,3 +522,30 @@ def solarize(image, threshold=128):
|
|||
else:
|
||||
lut.append(255-i)
|
||||
return _lut(image, lut)
|
||||
|
||||
|
||||
def exif_transpose(image):
|
||||
"""
|
||||
If an image has an EXIF Orientation tag, return a new image that is
|
||||
transposed accordingly. Otherwise, return a copy of the image.
|
||||
|
||||
:param image: The image to transpose.
|
||||
:return: An image.
|
||||
"""
|
||||
exif = image.getexif()
|
||||
orientation = exif.get(0x0112)
|
||||
method = {
|
||||
2: Image.FLIP_LEFT_RIGHT,
|
||||
3: Image.ROTATE_180,
|
||||
4: Image.FLIP_TOP_BOTTOM,
|
||||
5: Image.TRANSPOSE,
|
||||
6: Image.ROTATE_270,
|
||||
7: Image.TRANSVERSE,
|
||||
8: Image.ROTATE_90
|
||||
}.get(orientation)
|
||||
if method is not None:
|
||||
transposed_image = image.transpose(method)
|
||||
del exif[0x0112]
|
||||
transposed_image.info["exif"] = exif.tobytes()
|
||||
return transposed_image
|
||||
return image.copy()
|
||||
|
|
|
@ -198,35 +198,9 @@ def getiptcinfo(im):
|
|||
|
||||
elif isinstance(im, JpegImagePlugin.JpegImageFile):
|
||||
# extract the IPTC/NAA resource
|
||||
try:
|
||||
app = im.app["APP13"]
|
||||
if app[:14] == b"Photoshop 3.0\x00":
|
||||
app = app[14:]
|
||||
# parse the image resource block
|
||||
offset = 0
|
||||
while app[offset:offset+4] == b"8BIM":
|
||||
offset += 4
|
||||
# resource code
|
||||
code = i16(app, offset)
|
||||
offset += 2
|
||||
# resource name (usually empty)
|
||||
name_len = i8(app[offset])
|
||||
# name = app[offset+1:offset+1+name_len]
|
||||
offset = 1 + offset + name_len
|
||||
if offset & 1:
|
||||
offset += 1
|
||||
# resource data block
|
||||
size = i32(app, offset)
|
||||
offset += 4
|
||||
if code == 0x0404:
|
||||
# 0x0404 contains IPTC/NAA data
|
||||
data = app[offset:offset+size]
|
||||
break
|
||||
offset = offset + size
|
||||
if offset & 1:
|
||||
offset += 1
|
||||
except (AttributeError, KeyError):
|
||||
pass
|
||||
photoshop = im.info.get("photoshop")
|
||||
if photoshop:
|
||||
data = photoshop.get(0x0404)
|
||||
|
||||
elif isinstance(im, TiffImagePlugin.TiffImageFile):
|
||||
# get raw data from the IPTC/NAA tag (PhotoShop tags the data
|
||||
|
|
|
@ -39,7 +39,7 @@ import struct
|
|||
import io
|
||||
import warnings
|
||||
from . import Image, ImageFile, TiffImagePlugin
|
||||
from ._binary import i8, o8, i16be as i16
|
||||
from ._binary import i8, o8, i16be as i16, i32be as i32
|
||||
from .JpegPresets import presets
|
||||
from ._util import isStringType
|
||||
|
||||
|
@ -86,7 +86,7 @@ def APP(self, marker):
|
|||
self.info["jfif_density"] = jfif_density
|
||||
elif marker == 0xFFE1 and s[:5] == b"Exif\0":
|
||||
if "exif" not in self.info:
|
||||
# extract Exif information (incomplete)
|
||||
# extract EXIF information (incomplete)
|
||||
self.info["exif"] = s # FIXME: value will change
|
||||
elif marker == 0xFFE2 and s[:5] == b"FPXR\0":
|
||||
# extract FlashPix information (incomplete)
|
||||
|
@ -104,6 +104,39 @@ def APP(self, marker):
|
|||
# reassemble the profile, rather than assuming that the APP2
|
||||
# markers appear in the correct sequence.
|
||||
self.icclist.append(s)
|
||||
elif marker == 0xFFED:
|
||||
if s[:14] == b"Photoshop 3.0\x00":
|
||||
blocks = s[14:]
|
||||
# parse the image resource block
|
||||
offset = 0
|
||||
photoshop = {}
|
||||
while blocks[offset:offset+4] == b"8BIM":
|
||||
offset += 4
|
||||
# resource code
|
||||
code = i16(blocks, offset)
|
||||
offset += 2
|
||||
# resource name (usually empty)
|
||||
name_len = i8(blocks[offset])
|
||||
# name = blocks[offset+1:offset+1+name_len]
|
||||
offset = 1 + offset + name_len
|
||||
if offset & 1:
|
||||
offset += 1
|
||||
# resource data block
|
||||
size = i32(blocks, offset)
|
||||
offset += 4
|
||||
data = blocks[offset:offset+size]
|
||||
if code == 0x03ED: # ResolutionInfo
|
||||
data = {
|
||||
'XResolution': i32(data[:4]) / 65536,
|
||||
'DisplayedUnitsX': i16(data[4:8]),
|
||||
'YResolution': i32(data[8:12]) / 65536,
|
||||
'DisplayedUnitsY': i16(data[12:]),
|
||||
}
|
||||
photoshop[code] = data
|
||||
offset = offset + size
|
||||
if offset & 1:
|
||||
offset += 1
|
||||
self.info["photoshop"] = photoshop
|
||||
elif marker == 0xFFEE and s[:5] == b"Adobe":
|
||||
self.info["adobe"] = i16(s, 5)
|
||||
# extract Adobe custom properties
|
||||
|
@ -127,15 +160,15 @@ def APP(self, marker):
|
|||
resolution_unit = exif[0x0128]
|
||||
x_resolution = exif[0x011A]
|
||||
try:
|
||||
dpi = x_resolution[0] / x_resolution[1]
|
||||
dpi = float(x_resolution[0]) / x_resolution[1]
|
||||
except TypeError:
|
||||
dpi = x_resolution
|
||||
if resolution_unit == 3: # cm
|
||||
# 1 dpcm = 2.54 dpi
|
||||
dpi *= 2.54
|
||||
self.info["dpi"] = dpi, dpi
|
||||
self.info["dpi"] = int(dpi + 0.5), int(dpi + 0.5)
|
||||
except (KeyError, SyntaxError, ZeroDivisionError):
|
||||
# SyntaxError for invalid/unreadable exif
|
||||
# SyntaxError for invalid/unreadable EXIF
|
||||
# KeyError for dpi not included
|
||||
# ZeroDivisionError for invalid dpi rational value
|
||||
self.info["dpi"] = 72, 72
|
||||
|
@ -439,60 +472,23 @@ class JpegImageFile(ImageFile.ImageFile):
|
|||
def _fixup_dict(src_dict):
|
||||
# Helper function for _getexif()
|
||||
# returns a dict with any single item tuples/lists as individual values
|
||||
def _fixup(value):
|
||||
try:
|
||||
if len(value) == 1 and not isinstance(value, dict):
|
||||
return value[0]
|
||||
except Exception:
|
||||
pass
|
||||
return value
|
||||
|
||||
return {k: _fixup(v) for k, v in src_dict.items()}
|
||||
exif = Image.Exif()
|
||||
return exif._fixup_dict(src_dict)
|
||||
|
||||
|
||||
def _getexif(self):
|
||||
# Extract EXIF information. This method is highly experimental,
|
||||
# and is likely to be replaced with something better in a future
|
||||
# version.
|
||||
|
||||
# The EXIF record consists of a TIFF file embedded in a JPEG
|
||||
# application marker (!).
|
||||
# Use the cached version if possible
|
||||
try:
|
||||
data = self.info["exif"]
|
||||
return self.info["parsed_exif"]
|
||||
except KeyError:
|
||||
return None
|
||||
fp = io.BytesIO(data[6:])
|
||||
head = fp.read(8)
|
||||
# process dictionary
|
||||
info = TiffImagePlugin.ImageFileDirectory_v1(head)
|
||||
fp.seek(info.next)
|
||||
info.load(fp)
|
||||
exif = dict(_fixup_dict(info))
|
||||
# get exif extension
|
||||
try:
|
||||
# exif field 0x8769 is an offset pointer to the location
|
||||
# of the nested embedded exif ifd.
|
||||
# It should be a long, but may be corrupted.
|
||||
fp.seek(exif[0x8769])
|
||||
except (KeyError, TypeError):
|
||||
pass
|
||||
else:
|
||||
info = TiffImagePlugin.ImageFileDirectory_v1(head)
|
||||
info.load(fp)
|
||||
exif.update(_fixup_dict(info))
|
||||
# get gpsinfo extension
|
||||
try:
|
||||
# exif field 0x8825 is an offset pointer to the location
|
||||
# of the nested embedded gps exif ifd.
|
||||
# It should be a long, but may be corrupted.
|
||||
fp.seek(exif[0x8825])
|
||||
except (KeyError, TypeError):
|
||||
pass
|
||||
else:
|
||||
info = TiffImagePlugin.ImageFileDirectory_v1(head)
|
||||
info.load(fp)
|
||||
exif[0x8825] = _fixup_dict(info)
|
||||
|
||||
if "exif" not in self.info:
|
||||
return None
|
||||
exif = dict(self.getexif())
|
||||
|
||||
# Cache the result for future use
|
||||
self.info["parsed_exif"] = exif
|
||||
return exif
|
||||
|
||||
|
||||
|
@ -728,6 +724,10 @@ def _save(im, fp, filename):
|
|||
|
||||
optimize = info.get("optimize", False)
|
||||
|
||||
exif = info.get("exif", b"")
|
||||
if isinstance(exif, Image.Exif):
|
||||
exif = exif.tobytes()
|
||||
|
||||
# get keyword arguments
|
||||
im.encoderconfig = (
|
||||
quality,
|
||||
|
@ -739,7 +739,7 @@ def _save(im, fp, filename):
|
|||
subsampling,
|
||||
qtables,
|
||||
extra,
|
||||
info.get("exif", b"")
|
||||
exif
|
||||
)
|
||||
|
||||
# if we optimize, libjpeg needs a buffer big enough to hold the whole image
|
||||
|
@ -757,9 +757,9 @@ def _save(im, fp, filename):
|
|||
else:
|
||||
bufsize = im.size[0] * im.size[1]
|
||||
|
||||
# The exif info needs to be written as one block, + APP1, + one spare byte.
|
||||
# The EXIF info needs to be written as one block, + APP1, + one spare byte.
|
||||
# Ensure that our buffer is big enough. Same with the icc_profile block.
|
||||
bufsize = max(ImageFile.MAXBLOCK, bufsize, len(info.get("exif", b"")) + 5,
|
||||
bufsize = max(ImageFile.MAXBLOCK, bufsize, len(exif) + 5,
|
||||
len(extra) + 1)
|
||||
|
||||
ImageFile._save(im, fp, [("jpeg", (0, 0)+im.size, 0, rawmode)], bufsize)
|
||||
|
@ -786,7 +786,8 @@ def jpeg_factory(fp=None, filename=None):
|
|||
if mpheader[45057] > 1:
|
||||
# It's actually an MPO
|
||||
from .MpoImagePlugin import MpoImageFile
|
||||
im = MpoImageFile(fp, filename)
|
||||
# Don't reload everything, just convert it.
|
||||
im = MpoImageFile.adopt(im, mpheader)
|
||||
except (TypeError, IndexError):
|
||||
# It is really a JPEG
|
||||
pass
|
||||
|
|
|
@ -18,7 +18,8 @@
|
|||
# See the README file for information on usage and redistribution.
|
||||
#
|
||||
|
||||
from . import Image, JpegImagePlugin
|
||||
from . import Image, ImageFile, JpegImagePlugin
|
||||
from ._binary import i16be as i16
|
||||
|
||||
# __version__ is deprecated and will be removed in a future version. Use
|
||||
# PIL.__version__ instead.
|
||||
|
@ -46,7 +47,10 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
|
|||
def _open(self):
|
||||
self.fp.seek(0) # prep the fp in order to pass the JPEG test
|
||||
JpegImagePlugin.JpegImageFile._open(self)
|
||||
self.mpinfo = self._getmp()
|
||||
self._after_jpeg_open()
|
||||
|
||||
def _after_jpeg_open(self, mpheader=None):
|
||||
self.mpinfo = mpheader if mpheader is not None else self._getmp()
|
||||
self.__framecount = self.mpinfo[0xB001]
|
||||
self.__mpoffsets = [mpent['DataOffset'] + self.info['mpoffset']
|
||||
for mpent in self.mpinfo[0xB002]]
|
||||
|
@ -78,6 +82,20 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
|
|||
return
|
||||
self.fp = self.__fp
|
||||
self.offset = self.__mpoffsets[frame]
|
||||
|
||||
self.fp.seek(self.offset + 2) # skip SOI marker
|
||||
if "parsed_exif" in self.info:
|
||||
del self.info["parsed_exif"]
|
||||
if i16(self.fp.read(2)) == 0xFFE1: # APP1
|
||||
n = i16(self.fp.read(2))-2
|
||||
self.info["exif"] = ImageFile._safe_read(self.fp, n)
|
||||
|
||||
exif = self._getexif()
|
||||
if 40962 in exif and 40963 in exif:
|
||||
self._size = (exif[40962], exif[40963])
|
||||
elif "exif" in self.info:
|
||||
del self.info["exif"]
|
||||
|
||||
self.tile = [
|
||||
("jpeg", (0, 0) + self.size, self.offset, (self.mode, ""))
|
||||
]
|
||||
|
@ -95,6 +113,22 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
|
|||
finally:
|
||||
self.__fp = None
|
||||
|
||||
@staticmethod
|
||||
def adopt(jpeg_instance, mpheader=None):
|
||||
"""
|
||||
Transform the instance of JpegImageFile into
|
||||
an instance of MpoImageFile.
|
||||
After the call, the JpegImageFile is extended
|
||||
to be an MpoImageFile.
|
||||
|
||||
This is essentially useful when opening a JPEG
|
||||
file that reveals itself as an MPO, to avoid
|
||||
double call to _open.
|
||||
"""
|
||||
jpeg_instance.__class__ = MpoImageFile
|
||||
jpeg_instance._after_jpeg_open(mpheader)
|
||||
return jpeg_instance
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Registry stuff
|
||||
|
|
|
@ -54,19 +54,24 @@ _MAGIC = b"\211PNG\r\n\032\n"
|
|||
|
||||
_MODES = {
|
||||
# supported bits/color combinations, and corresponding modes/rawmodes
|
||||
# Greyscale
|
||||
(1, 0): ("1", "1"),
|
||||
(2, 0): ("L", "L;2"),
|
||||
(4, 0): ("L", "L;4"),
|
||||
(8, 0): ("L", "L"),
|
||||
(16, 0): ("I", "I;16B"),
|
||||
# Truecolour
|
||||
(8, 2): ("RGB", "RGB"),
|
||||
(16, 2): ("RGB", "RGB;16B"),
|
||||
# Indexed-colour
|
||||
(1, 3): ("P", "P;1"),
|
||||
(2, 3): ("P", "P;2"),
|
||||
(4, 3): ("P", "P;4"),
|
||||
(8, 3): ("P", "P"),
|
||||
# Greyscale with alpha
|
||||
(8, 4): ("LA", "LA"),
|
||||
(16, 4): ("RGBA", "LA;16B"), # LA;16B->LA not yet available
|
||||
# Truecolour with alpha
|
||||
(8, 6): ("RGBA", "RGBA"),
|
||||
(16, 6): ("RGBA", "RGBA;16B"),
|
||||
}
|
||||
|
@ -386,7 +391,7 @@ class PngStream(ChunkStream):
|
|||
# otherwise, we have a byte string with one alpha value
|
||||
# for each palette entry
|
||||
self.im_info["transparency"] = s
|
||||
elif self.im_mode == "L":
|
||||
elif self.im_mode in ("1", "L", "I"):
|
||||
self.im_info["transparency"] = i16(s)
|
||||
elif self.im_mode == "RGB":
|
||||
self.im_info["transparency"] = i16(s), i16(s[2:]), i16(s[4:])
|
||||
|
@ -691,8 +696,14 @@ class PngImageFile(ImageFile.ImageFile):
|
|||
def _getexif(self):
|
||||
if "exif" not in self.info:
|
||||
self.load()
|
||||
from .JpegImagePlugin import _getexif
|
||||
return _getexif(self)
|
||||
if "exif" not in self.info:
|
||||
return None
|
||||
return dict(self.getexif())
|
||||
|
||||
def getexif(self):
|
||||
if "exif" not in self.info:
|
||||
self.load()
|
||||
return ImageFile.ImageFile.getexif(self)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
@ -841,7 +852,7 @@ def _save(im, fp, filename, chunk=putchunk):
|
|||
transparency = max(0, min(255, transparency))
|
||||
alpha = b'\xFF' * transparency + b'\0'
|
||||
chunk(fp, b"tRNS", alpha[:alpha_bytes])
|
||||
elif im.mode == "L":
|
||||
elif im.mode in ("1", "L", "I"):
|
||||
transparency = max(0, min(65535, transparency))
|
||||
chunk(fp, b"tRNS", o16(transparency))
|
||||
elif im.mode == "RGB":
|
||||
|
@ -875,6 +886,8 @@ def _save(im, fp, filename, chunk=putchunk):
|
|||
|
||||
exif = im.encoderinfo.get("exif", im.info.get("exif"))
|
||||
if exif:
|
||||
if isinstance(exif, Image.Exif):
|
||||
exif = exif.tobytes(8)
|
||||
if exif.startswith(b"Exif\x00\x00"):
|
||||
exif = exif[6:]
|
||||
chunk(fp, b"eXIf", exif)
|
||||
|
|
|
@ -208,7 +208,7 @@ class SpiderImageFile(ImageFile.ImageFile):
|
|||
|
||||
# given a list of filenames, return a list of images
|
||||
def loadImageSeries(filelist=None):
|
||||
"""create a list of Image.images for use in montage"""
|
||||
"""create a list of :py:class:`~PIL.Image.Image` objects for use in a montage"""
|
||||
if filelist is None or len(filelist) < 1:
|
||||
return
|
||||
|
||||
|
|
|
@ -785,17 +785,12 @@ class ImageFileDirectory_v2(MutableMapping):
|
|||
warnings.warn(str(msg))
|
||||
return
|
||||
|
||||
def save(self, fp):
|
||||
|
||||
if fp.tell() == 0: # skip TIFF header on subsequent pages
|
||||
# tiff header -- PIL always starts the first IFD at offset 8
|
||||
fp.write(self._prefix + self._pack("HL", 42, 8))
|
||||
|
||||
def tobytes(self, offset=0):
|
||||
# FIXME What about tagdata?
|
||||
fp.write(self._pack("H", len(self._tags_v2)))
|
||||
result = self._pack("H", len(self._tags_v2))
|
||||
|
||||
entries = []
|
||||
offset = fp.tell() + len(self._tags_v2) * 12 + 4
|
||||
offset = offset + len(result) + len(self._tags_v2) * 12 + 4
|
||||
stripoffsets = None
|
||||
|
||||
# pass 1: convert tags to binary format
|
||||
|
@ -844,18 +839,29 @@ class ImageFileDirectory_v2(MutableMapping):
|
|||
for tag, typ, count, value, data in entries:
|
||||
if DEBUG > 1:
|
||||
print(tag, typ, count, repr(value), repr(data))
|
||||
fp.write(self._pack("HHL4s", tag, typ, count, value))
|
||||
result += self._pack("HHL4s", tag, typ, count, value)
|
||||
|
||||
# -- overwrite here for multi-page --
|
||||
fp.write(b"\0\0\0\0") # end of entries
|
||||
result += b"\0\0\0\0" # end of entries
|
||||
|
||||
# pass 3: write auxiliary data to file
|
||||
for tag, typ, count, value, data in entries:
|
||||
fp.write(data)
|
||||
result += data
|
||||
if len(data) & 1:
|
||||
fp.write(b"\0")
|
||||
result += b"\0"
|
||||
|
||||
return offset
|
||||
return result
|
||||
|
||||
def save(self, fp):
|
||||
|
||||
if fp.tell() == 0: # skip TIFF header on subsequent pages
|
||||
# tiff header -- PIL always starts the first IFD at offset 8
|
||||
fp.write(self._prefix + self._pack("HL", 42, 8))
|
||||
|
||||
offset = fp.tell()
|
||||
result = self.tobytes(offset)
|
||||
fp.write(result)
|
||||
return offset + len(result)
|
||||
|
||||
|
||||
ImageFileDirectory_v2._load_dispatch = _load_dispatch
|
||||
|
@ -985,7 +991,6 @@ class TiffImageFile(ImageFile.ImageFile):
|
|||
self.__fp = self.fp
|
||||
self._frame_pos = []
|
||||
self._n_frames = None
|
||||
self._is_animated = None
|
||||
|
||||
if DEBUG:
|
||||
print("*** TiffImageFile._open ***")
|
||||
|
@ -999,29 +1004,14 @@ class TiffImageFile(ImageFile.ImageFile):
|
|||
def n_frames(self):
|
||||
if self._n_frames is None:
|
||||
current = self.tell()
|
||||
try:
|
||||
while True:
|
||||
self._seek(self.tell() + 1)
|
||||
except EOFError:
|
||||
self._n_frames = self.tell() + 1
|
||||
self._seek(len(self._frame_pos))
|
||||
while self._n_frames is None:
|
||||
self._seek(self.tell() + 1)
|
||||
self.seek(current)
|
||||
return self._n_frames
|
||||
|
||||
@property
|
||||
def is_animated(self):
|
||||
if self._is_animated is None:
|
||||
if self._n_frames is not None:
|
||||
self._is_animated = self._n_frames != 1
|
||||
else:
|
||||
current = self.tell()
|
||||
|
||||
try:
|
||||
self.seek(1)
|
||||
self._is_animated = True
|
||||
except EOFError:
|
||||
self._is_animated = False
|
||||
|
||||
self.seek(current)
|
||||
return self._is_animated
|
||||
|
||||
def seek(self, frame):
|
||||
|
@ -1053,10 +1043,13 @@ class TiffImageFile(ImageFile.ImageFile):
|
|||
print("Loading tags, location: %s" % self.fp.tell())
|
||||
self.tag_v2.load(self.fp)
|
||||
self.__next = self.tag_v2.next
|
||||
if self.__next == 0:
|
||||
self._n_frames = frame + 1
|
||||
if len(self._frame_pos) == 1:
|
||||
self._is_animated = self.__next != 0
|
||||
self.__frame += 1
|
||||
self.fp.seek(self._frame_pos[frame])
|
||||
self.tag_v2.load(self.fp)
|
||||
self.__next = self.tag_v2.next
|
||||
# fill the legacy tag/ifd entries
|
||||
self.tag = self.ifd = ImageFileDirectory_v1.from_v2(self.tag_v2)
|
||||
self.__frame = frame
|
||||
|
@ -1087,7 +1080,7 @@ class TiffImageFile(ImageFile.ImageFile):
|
|||
def load_end(self):
|
||||
# allow closing if we're on the first frame, there's no next
|
||||
# This is the ImageFile.load path only, libtiff specific below.
|
||||
if self.__frame == 0 and not self.__next:
|
||||
if not self._is_animated:
|
||||
self._close_exclusive_fp_after_loading = True
|
||||
|
||||
def _load_libtiff(self):
|
||||
|
@ -1167,10 +1160,9 @@ class TiffImageFile(ImageFile.ImageFile):
|
|||
self.tile = []
|
||||
self.readonly = 0
|
||||
# libtiff closed the fp in a, we need to close self.fp, if possible
|
||||
if self._exclusive_fp:
|
||||
if self.__frame == 0 and not self.__next:
|
||||
self.fp.close()
|
||||
self.fp = None # might be shared
|
||||
if self._exclusive_fp and not self._is_animated:
|
||||
self.fp.close()
|
||||
self.fp = None # might be shared
|
||||
|
||||
if err < 0:
|
||||
raise IOError(err)
|
||||
|
@ -1261,11 +1253,11 @@ class TiffImageFile(ImageFile.ImageFile):
|
|||
if xres and yres:
|
||||
resunit = self.tag_v2.get(RESOLUTION_UNIT)
|
||||
if resunit == 2: # dots per inch
|
||||
self.info["dpi"] = xres, yres
|
||||
self.info["dpi"] = int(xres + 0.5), int(yres + 0.5)
|
||||
elif resunit == 3: # dots per centimeter. convert to dpi
|
||||
self.info["dpi"] = xres * 2.54, yres * 2.54
|
||||
self.info["dpi"] = int(xres * 2.54 + 0.5), int(yres * 2.54 + 0.5)
|
||||
elif resunit is None: # used to default to 1, but now 2)
|
||||
self.info["dpi"] = xres, yres
|
||||
self.info["dpi"] = int(xres + 0.5), int(yres + 0.5)
|
||||
# For backward compatibility,
|
||||
# we also preserve the old behavior
|
||||
self.info["resolution"] = xres, yres
|
||||
|
@ -1476,8 +1468,8 @@ def _save(im, fp, filename):
|
|||
dpi = im.encoderinfo.get("dpi")
|
||||
if dpi:
|
||||
ifd[RESOLUTION_UNIT] = 2
|
||||
ifd[X_RESOLUTION] = dpi[0]
|
||||
ifd[Y_RESOLUTION] = dpi[1]
|
||||
ifd[X_RESOLUTION] = int(dpi[0] + 0.5)
|
||||
ifd[Y_RESOLUTION] = int(dpi[1] + 0.5)
|
||||
|
||||
if bits != (1,):
|
||||
ifd[BITSPERSAMPLE] = bits
|
||||
|
|
|
@ -93,8 +93,9 @@ class WebPImageFile(ImageFile.ImageFile):
|
|||
self.seek(0)
|
||||
|
||||
def _getexif(self):
|
||||
from .JpegImagePlugin import _getexif
|
||||
return _getexif(self)
|
||||
if "exif" not in self.info:
|
||||
return None
|
||||
return dict(self.getexif())
|
||||
|
||||
@property
|
||||
def n_frames(self):
|
||||
|
@ -216,6 +217,8 @@ def _save_all(im, fp, filename):
|
|||
method = im.encoderinfo.get("method", 0)
|
||||
icc_profile = im.encoderinfo.get("icc_profile", "")
|
||||
exif = im.encoderinfo.get("exif", "")
|
||||
if isinstance(exif, Image.Exif):
|
||||
exif = exif.tobytes()
|
||||
xmp = im.encoderinfo.get("xmp", "")
|
||||
if allow_mixed:
|
||||
lossless = False
|
||||
|
@ -315,6 +318,8 @@ def _save(im, fp, filename):
|
|||
quality = im.encoderinfo.get("quality", 80)
|
||||
icc_profile = im.encoderinfo.get("icc_profile", "")
|
||||
exif = im.encoderinfo.get("exif", "")
|
||||
if isinstance(exif, Image.Exif):
|
||||
exif = exif.tobytes()
|
||||
xmp = im.encoderinfo.get("xmp", "")
|
||||
|
||||
if im.mode not in _VALID_WEBP_LEGACY_MODES:
|
||||
|
|
|
@ -131,8 +131,8 @@ class WmfStubImageFile(ImageFile.StubImageFile):
|
|||
size = x1 - x0, y1 - y0
|
||||
|
||||
# calculate dots per inch from bbox and frame
|
||||
xdpi = 2540 * (x1 - y0) // (frame[2] - frame[0])
|
||||
ydpi = 2540 * (y1 - y0) // (frame[3] - frame[1])
|
||||
xdpi = int(2540.0 * (x1 - y0) / (frame[2] - frame[0]) + 0.5)
|
||||
ydpi = int(2540.0 * (y1 - y0) / (frame[3] - frame[1]) + 0.5)
|
||||
|
||||
self.info["wmf_bbox"] = x0, y0, x1, y1
|
||||
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
# Master version for Pillow
|
||||
__version__ = '6.0.0.dev0'
|
||||
__version__ = '6.1.0.dev0'
|
||||
|
|
|
@ -71,6 +71,7 @@
|
|||
* See the README file for information on usage and redistribution.
|
||||
*/
|
||||
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
#include "Python.h"
|
||||
|
||||
#ifdef HAVE_LIBJPEG
|
||||
|
@ -1564,11 +1565,12 @@ _putpalette(ImagingObject* self, PyObject* args)
|
|||
|
||||
char* rawmode;
|
||||
UINT8* palette;
|
||||
int palettesize;
|
||||
Py_ssize_t palettesize;
|
||||
if (!PyArg_ParseTuple(args, "s"PY_ARG_BYTES_LENGTH, &rawmode, &palette, &palettesize))
|
||||
return NULL;
|
||||
|
||||
if (strcmp(self->image->mode, "L") != 0 && strcmp(self->image->mode, "P")) {
|
||||
if (strcmp(self->image->mode, "L") && strcmp(self->image->mode, "LA") &&
|
||||
strcmp(self->image->mode, "P") && strcmp(self->image->mode, "PA")) {
|
||||
PyErr_SetString(PyExc_ValueError, wrong_mode);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -1626,7 +1628,7 @@ _putpalettealphas(ImagingObject* self, PyObject* args)
|
|||
{
|
||||
int i;
|
||||
UINT8 *values;
|
||||
int length;
|
||||
Py_ssize_t length;
|
||||
if (!PyArg_ParseTuple(args, PY_ARG_BYTES_LENGTH, &values, &length))
|
||||
return NULL;
|
||||
|
||||
|
@ -1770,7 +1772,7 @@ im_setmode(ImagingObject* self, PyObject* args)
|
|||
Imaging im;
|
||||
|
||||
char* mode;
|
||||
int modelen;
|
||||
Py_ssize_t modelen;
|
||||
if (!PyArg_ParseTuple(args, "s#:setmode", &mode, &modelen))
|
||||
return NULL;
|
||||
|
||||
|
@ -2066,8 +2068,8 @@ _getprojection(ImagingObject* self, PyObject* args)
|
|||
ImagingGetProjection(self->image, (unsigned char *)xprofile, (unsigned char *)yprofile);
|
||||
|
||||
result = Py_BuildValue(PY_ARG_BYTES_LENGTH PY_ARG_BYTES_LENGTH,
|
||||
xprofile, self->image->xsize,
|
||||
yprofile, self->image->ysize);
|
||||
xprofile, (Py_ssize_t)self->image->xsize,
|
||||
yprofile, (Py_ssize_t)self->image->ysize);
|
||||
|
||||
free(xprofile);
|
||||
free(yprofile);
|
||||
|
@ -2342,7 +2344,7 @@ _font_new(PyObject* self_, PyObject* args)
|
|||
|
||||
ImagingObject* imagep;
|
||||
unsigned char* glyphdata;
|
||||
int glyphdata_length;
|
||||
Py_ssize_t glyphdata_length;
|
||||
if (!PyArg_ParseTuple(args, "O!"PY_ARG_BYTES_LENGTH,
|
||||
&Imaging_Type, &imagep,
|
||||
&glyphdata, &glyphdata_length))
|
||||
|
|
|
@ -25,6 +25,7 @@ kevin@cazabon.com\n\
|
|||
http://www.cazabon.com\n\
|
||||
"
|
||||
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
#include "Python.h" // Include before wchar.h so _GNU_SOURCE is set
|
||||
#include "wchar.h"
|
||||
#include "datetime.h"
|
||||
|
@ -120,7 +121,7 @@ cms_profile_fromstring(PyObject* self, PyObject* args)
|
|||
cmsHPROFILE hProfile;
|
||||
|
||||
char* pProfile;
|
||||
int nProfile;
|
||||
Py_ssize_t nProfile;
|
||||
#if PY_VERSION_HEX >= 0x03000000
|
||||
if (!PyArg_ParseTuple(args, "y#:profile_frombytes", &pProfile, &nProfile))
|
||||
return NULL;
|
||||
|
@ -735,12 +736,12 @@ _xyz3_py(cmsCIEXYZ* XYZ)
|
|||
cmsXYZ2xyY(&xyY[2], &XYZ[2]);
|
||||
|
||||
return Py_BuildValue("(((d,d,d),(d,d,d),(d,d,d)),((d,d,d),(d,d,d),(d,d,d)))",
|
||||
XYZ[0].X, XYZ[0].Y, XYZ[0].Z,
|
||||
XYZ[1].X, XYZ[1].Y, XYZ[1].Z,
|
||||
XYZ[2].X, XYZ[2].Y, XYZ[2].Z,
|
||||
xyY[0].x, xyY[0].y, xyY[0].Y,
|
||||
xyY[1].x, xyY[1].y, xyY[1].Y,
|
||||
xyY[2].x, xyY[2].y, xyY[2].Y);
|
||||
XYZ[0].X, XYZ[0].Y, XYZ[0].Z,
|
||||
XYZ[1].X, XYZ[1].Y, XYZ[1].Z,
|
||||
XYZ[2].X, XYZ[2].Y, XYZ[2].Z,
|
||||
xyY[0].x, xyY[0].y, xyY[0].Y,
|
||||
xyY[1].x, xyY[1].y, xyY[1].Y,
|
||||
xyY[2].x, xyY[2].y, xyY[2].Y);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
|
@ -783,9 +784,9 @@ _profile_read_ciexyy_triple(CmsProfileObject* self, cmsTagSignature info)
|
|||
/* Note: lcms does all the heavy lifting and error checking (nr of
|
||||
channels == 3). */
|
||||
return Py_BuildValue("((d,d,d),(d,d,d),(d,d,d)),",
|
||||
triple->Red.x, triple->Red.y, triple->Red.Y,
|
||||
triple->Green.x, triple->Green.y, triple->Green.Y,
|
||||
triple->Blue.x, triple->Blue.y, triple->Blue.Y);
|
||||
triple->Red.x, triple->Red.y, triple->Red.Y,
|
||||
triple->Green.x, triple->Green.y, triple->Green.Y,
|
||||
triple->Blue.x, triple->Blue.y, triple->Blue.Y);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
|
@ -817,12 +818,12 @@ _profile_read_named_color_list(CmsProfileObject* self, cmsTagSignature info)
|
|||
for (i = 0; i < n; i++) {
|
||||
PyObject* str;
|
||||
cmsNamedColorInfo(ncl, i, name, NULL, NULL, NULL, NULL);
|
||||
str = PyUnicode_FromString(name);
|
||||
if (str == NULL) {
|
||||
Py_DECREF(result);
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
str = PyUnicode_FromString(name);
|
||||
if (str == NULL) {
|
||||
Py_DECREF(result);
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
PyList_SET_ITEM(result, i, str);
|
||||
}
|
||||
|
||||
|
@ -844,9 +845,9 @@ static cmsBool _calculate_rgb_primaries(CmsProfileObject* self, cmsCIEXYZTRIPLE*
|
|||
|
||||
// transform from our profile to XYZ using doubles for highest precision
|
||||
hTransform = cmsCreateTransform(self->profile, TYPE_RGB_DBL,
|
||||
hXYZ, TYPE_XYZ_DBL,
|
||||
INTENT_RELATIVE_COLORIMETRIC,
|
||||
cmsFLAGS_NOCACHE | cmsFLAGS_NOOPTIMIZE);
|
||||
hXYZ, TYPE_XYZ_DBL,
|
||||
INTENT_RELATIVE_COLORIMETRIC,
|
||||
cmsFLAGS_NOCACHE | cmsFLAGS_NOOPTIMIZE);
|
||||
cmsCloseProfile(hXYZ);
|
||||
if (hTransform == NULL)
|
||||
return 0;
|
||||
|
@ -885,31 +886,31 @@ _is_intent_supported(CmsProfileObject* self, int clut)
|
|||
|
||||
|
||||
n = cmsGetSupportedIntents(INTENTS,
|
||||
intent_ids,
|
||||
intent_descs);
|
||||
intent_ids,
|
||||
intent_descs);
|
||||
for (i = 0; i < n; i++) {
|
||||
int intent = (int) intent_ids[i];
|
||||
PyObject* id;
|
||||
PyObject* entry;
|
||||
PyObject* entry;
|
||||
|
||||
/* Only valid for ICC Intents (otherwise we read invalid memory in lcms cmsio1.c). */
|
||||
if (!(intent == INTENT_PERCEPTUAL || intent == INTENT_RELATIVE_COLORIMETRIC
|
||||
|| intent == INTENT_SATURATION || intent == INTENT_ABSOLUTE_COLORIMETRIC))
|
||||
continue;
|
||||
/* Only valid for ICC Intents (otherwise we read invalid memory in lcms cmsio1.c). */
|
||||
if (!(intent == INTENT_PERCEPTUAL || intent == INTENT_RELATIVE_COLORIMETRIC
|
||||
|| intent == INTENT_SATURATION || intent == INTENT_ABSOLUTE_COLORIMETRIC))
|
||||
continue;
|
||||
|
||||
id = PyInt_FromLong((long) intent);
|
||||
entry = Py_BuildValue("(OOO)",
|
||||
_check_intent(clut, self->profile, intent, LCMS_USED_AS_INPUT) ? Py_True : Py_False,
|
||||
_check_intent(clut, self->profile, intent, LCMS_USED_AS_OUTPUT) ? Py_True : Py_False,
|
||||
_check_intent(clut, self->profile, intent, LCMS_USED_AS_PROOF) ? Py_True : Py_False);
|
||||
if (id == NULL || entry == NULL) {
|
||||
Py_XDECREF(id);
|
||||
Py_XDECREF(entry);
|
||||
Py_XDECREF(result);
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
PyDict_SetItem(result, id, entry);
|
||||
id = PyInt_FromLong((long) intent);
|
||||
entry = Py_BuildValue("(OOO)",
|
||||
_check_intent(clut, self->profile, intent, LCMS_USED_AS_INPUT) ? Py_True : Py_False,
|
||||
_check_intent(clut, self->profile, intent, LCMS_USED_AS_OUTPUT) ? Py_True : Py_False,
|
||||
_check_intent(clut, self->profile, intent, LCMS_USED_AS_PROOF) ? Py_True : Py_False);
|
||||
if (id == NULL || entry == NULL) {
|
||||
Py_XDECREF(id);
|
||||
Py_XDECREF(entry);
|
||||
Py_XDECREF(result);
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
PyDict_SetItem(result, id, entry);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -966,6 +967,8 @@ _profile_getattr(CmsProfileObject* self, cmsInfoType field)
|
|||
static PyObject*
|
||||
cms_profile_getattr_product_desc(CmsProfileObject* self, void* closure)
|
||||
{
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||
"product_desc is deprecated. Use Unicode profile_description instead.", 1);
|
||||
// description was Description != 'Copyright' || or "%s - %s" (manufacturer, model) in 1.x
|
||||
return _profile_getattr(self, cmsInfoDescription);
|
||||
}
|
||||
|
@ -975,24 +978,32 @@ cms_profile_getattr_product_desc(CmsProfileObject* self, void* closure)
|
|||
static PyObject*
|
||||
cms_profile_getattr_product_description(CmsProfileObject* self, void* closure)
|
||||
{
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||
"product_description is deprecated. Use Unicode profile_description instead.", 1);
|
||||
return _profile_getattr(self, cmsInfoDescription);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
cms_profile_getattr_product_model(CmsProfileObject* self, void* closure)
|
||||
{
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||
"product_model is deprecated. Use Unicode model instead.", 1);
|
||||
return _profile_getattr(self, cmsInfoModel);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
cms_profile_getattr_product_manufacturer(CmsProfileObject* self, void* closure)
|
||||
{
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||
"product_manufacturer is deprecated. Use Unicode manufacturer instead.", 1);
|
||||
return _profile_getattr(self, cmsInfoManufacturer);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
cms_profile_getattr_product_copyright(CmsProfileObject* self, void* closure)
|
||||
{
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||
"product_copyright is deprecated. Use Unicode copyright instead.", 1);
|
||||
return _profile_getattr(self, cmsInfoCopyright);
|
||||
}
|
||||
|
||||
|
@ -1005,12 +1016,16 @@ cms_profile_getattr_rendering_intent(CmsProfileObject* self, void* closure)
|
|||
static PyObject*
|
||||
cms_profile_getattr_pcs(CmsProfileObject* self, void* closure)
|
||||
{
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||
"pcs is deprecated. Use padded connection_space instead.", 1);
|
||||
return PyUnicode_DecodeFSDefault(findICmode(cmsGetPCS(self->profile)));
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
cms_profile_getattr_color_space(CmsProfileObject* self, void* closure)
|
||||
{
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||
"color_space is deprecated. Use padded xcolor_space instead.", 1);
|
||||
return PyUnicode_DecodeFSDefault(findICmode(cmsGetColorSpace(self->profile)));
|
||||
}
|
||||
|
||||
|
@ -1070,7 +1085,7 @@ cms_profile_getattr_creation_date(CmsProfileObject* self, void* closure)
|
|||
}
|
||||
|
||||
return PyDateTime_FromDateAndTime(1900 + ct.tm_year, ct.tm_mon, ct.tm_mday,
|
||||
ct.tm_hour, ct.tm_min, ct.tm_sec, 0);
|
||||
ct.tm_hour, ct.tm_min, ct.tm_sec, 0);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
|
@ -1295,7 +1310,7 @@ cms_profile_getattr_green_primary(CmsProfileObject* self, void* closure)
|
|||
result = _calculate_rgb_primaries(self, &primaries);
|
||||
if (! result) {
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
return _xyz_py(&primaries.Green);
|
||||
|
@ -1311,7 +1326,7 @@ cms_profile_getattr_blue_primary(CmsProfileObject* self, void* closure)
|
|||
result = _calculate_rgb_primaries(self, &primaries);
|
||||
if (! result) {
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
return _xyz_py(&primaries.Blue);
|
||||
|
@ -1394,11 +1409,11 @@ cms_profile_getattr_icc_measurement_condition (CmsProfileObject* self, void* clo
|
|||
geo = "unknown";
|
||||
|
||||
return Py_BuildValue("{s:i,s:(ddd),s:s,s:d,s:s}",
|
||||
"observer", mc->Observer,
|
||||
"backing", mc->Backing.X, mc->Backing.Y, mc->Backing.Z,
|
||||
"geo", geo,
|
||||
"flare", mc->Flare,
|
||||
"illuminant_type", _illu_map(mc->IlluminantType));
|
||||
"observer", mc->Observer,
|
||||
"backing", mc->Backing.X, mc->Backing.Y, mc->Backing.Z,
|
||||
"geo", geo,
|
||||
"flare", mc->Flare,
|
||||
"illuminant_type", _illu_map(mc->IlluminantType));
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
|
@ -1419,9 +1434,9 @@ cms_profile_getattr_icc_viewing_condition (CmsProfileObject* self, void* closure
|
|||
}
|
||||
|
||||
return Py_BuildValue("{s:(ddd),s:(ddd),s:s}",
|
||||
"illuminant", vc->IlluminantXYZ.X, vc->IlluminantXYZ.Y, vc->IlluminantXYZ.Z,
|
||||
"surround", vc->SurroundXYZ.X, vc->SurroundXYZ.Y, vc->SurroundXYZ.Z,
|
||||
"illuminant_type", _illu_map(vc->IlluminantType));
|
||||
"illuminant", vc->IlluminantXYZ.X, vc->IlluminantXYZ.Y, vc->IlluminantXYZ.Z,
|
||||
"surround", vc->SurroundXYZ.X, vc->SurroundXYZ.Y, vc->SurroundXYZ.Z,
|
||||
"illuminant_type", _illu_map(vc->IlluminantType));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
* Copyright (c) 1998-2007 by Secret Labs AB
|
||||
*/
|
||||
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
#include "Python.h"
|
||||
#include "Imaging.h"
|
||||
|
||||
|
@ -237,12 +238,12 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw)
|
|||
int error = 0;
|
||||
|
||||
char* filename = NULL;
|
||||
int size;
|
||||
int index = 0;
|
||||
int layout_engine = 0;
|
||||
Py_ssize_t size;
|
||||
Py_ssize_t index = 0;
|
||||
Py_ssize_t layout_engine = 0;
|
||||
unsigned char* encoding;
|
||||
unsigned char* font_bytes;
|
||||
int font_bytes_size = 0;
|
||||
Py_ssize_t font_bytes_size = 0;
|
||||
static char* kwlist[] = {
|
||||
"filename", "size", "index", "encoding", "font_bytes",
|
||||
"layout_engine", NULL
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
/* FIXME: make these pluggable! */
|
||||
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
#include "Python.h"
|
||||
|
||||
#include "Imaging.h"
|
||||
|
@ -117,7 +118,8 @@ static PyObject*
|
|||
_decode(ImagingDecoderObject* decoder, PyObject* args)
|
||||
{
|
||||
UINT8* buffer;
|
||||
int bufsize, status;
|
||||
Py_ssize_t bufsize;
|
||||
int status;
|
||||
ImagingSectionCookie cookie;
|
||||
|
||||
if (!PyArg_ParseTuple(args, PY_ARG_BYTES_LENGTH, &buffer, &bufsize))
|
||||
|
|
53
src/encode.c
|
@ -22,6 +22,7 @@
|
|||
|
||||
/* FIXME: make these pluggable! */
|
||||
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
#include "Python.h"
|
||||
|
||||
#include "Imaging.h"
|
||||
|
@ -123,7 +124,7 @@ _encode(ImagingEncoderObject* encoder, PyObject* args)
|
|||
|
||||
/* Encode to a Python string (allocated by this method) */
|
||||
|
||||
int bufsize = 16384;
|
||||
Py_ssize_t bufsize = 16384;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "|i", &bufsize))
|
||||
return NULL;
|
||||
|
@ -176,8 +177,8 @@ _encode_to_file(ImagingEncoderObject* encoder, PyObject* args)
|
|||
|
||||
/* Encode to a file handle */
|
||||
|
||||
int fh;
|
||||
int bufsize = 16384;
|
||||
Py_ssize_t fh;
|
||||
Py_ssize_t bufsize = 16384;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "i|i", &fh, &bufsize))
|
||||
return NULL;
|
||||
|
@ -221,7 +222,7 @@ _setimage(ImagingEncoderObject* encoder, PyObject* args)
|
|||
PyObject* op;
|
||||
Imaging im;
|
||||
ImagingCodecState state;
|
||||
int x0, y0, x1, y1;
|
||||
Py_ssize_t x0, y0, x1, y1;
|
||||
|
||||
/* Define where image data should be stored */
|
||||
|
||||
|
@ -406,8 +407,8 @@ PyImaging_GifEncoderNew(PyObject* self, PyObject* args)
|
|||
|
||||
char *mode;
|
||||
char *rawmode;
|
||||
int bits = 8;
|
||||
int interlace = 0;
|
||||
Py_ssize_t bits = 8;
|
||||
Py_ssize_t interlace = 0;
|
||||
if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &bits, &interlace))
|
||||
return NULL;
|
||||
|
||||
|
@ -438,7 +439,7 @@ PyImaging_PcxEncoderNew(PyObject* self, PyObject* args)
|
|||
|
||||
char *mode;
|
||||
char *rawmode;
|
||||
int bits = 8;
|
||||
Py_ssize_t bits = 8;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &bits)) {
|
||||
return NULL;
|
||||
|
@ -470,8 +471,8 @@ PyImaging_RawEncoderNew(PyObject* self, PyObject* args)
|
|||
|
||||
char *mode;
|
||||
char *rawmode;
|
||||
int stride = 0;
|
||||
int ystep = 1;
|
||||
Py_ssize_t stride = 0;
|
||||
Py_ssize_t ystep = 1;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &stride, &ystep))
|
||||
return NULL;
|
||||
|
@ -503,7 +504,7 @@ PyImaging_TgaRleEncoderNew(PyObject* self, PyObject* args)
|
|||
|
||||
char *mode;
|
||||
char *rawmode;
|
||||
int ystep = 1;
|
||||
Py_ssize_t ystep = 1;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "ss|i", &mode, &rawmode, &ystep))
|
||||
return NULL;
|
||||
|
@ -561,11 +562,11 @@ PyImaging_ZipEncoderNew(PyObject* self, PyObject* args)
|
|||
|
||||
char* mode;
|
||||
char* rawmode;
|
||||
int optimize = 0;
|
||||
int compress_level = -1;
|
||||
int compress_type = -1;
|
||||
Py_ssize_t optimize = 0;
|
||||
Py_ssize_t compress_level = -1;
|
||||
Py_ssize_t compress_type = -1;
|
||||
char* dictionary = NULL;
|
||||
int dictionary_size = 0;
|
||||
Py_ssize_t dictionary_size = 0;
|
||||
if (!PyArg_ParseTuple(args, "ss|iii"PY_ARG_BYTES_LENGTH, &mode, &rawmode,
|
||||
&optimize,
|
||||
&compress_level, &compress_type,
|
||||
|
@ -701,20 +702,20 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args)
|
|||
|
||||
char *mode;
|
||||
char *rawmode;
|
||||
int quality = 0;
|
||||
int progressive = 0;
|
||||
int smooth = 0;
|
||||
int optimize = 0;
|
||||
int streamtype = 0; /* 0=interchange, 1=tables only, 2=image only */
|
||||
int xdpi = 0, ydpi = 0;
|
||||
int subsampling = -1; /* -1=default, 0=none, 1=medium, 2=high */
|
||||
Py_ssize_t quality = 0;
|
||||
Py_ssize_t progressive = 0;
|
||||
Py_ssize_t smooth = 0;
|
||||
Py_ssize_t optimize = 0;
|
||||
Py_ssize_t streamtype = 0; /* 0=interchange, 1=tables only, 2=image only */
|
||||
Py_ssize_t xdpi = 0, ydpi = 0;
|
||||
Py_ssize_t subsampling = -1; /* -1=default, 0=none, 1=medium, 2=high */
|
||||
PyObject* qtables=NULL;
|
||||
unsigned int *qarrays = NULL;
|
||||
int qtablesLen = 0;
|
||||
char* extra = NULL;
|
||||
int extra_size;
|
||||
Py_ssize_t extra_size;
|
||||
char* rawExif = NULL;
|
||||
int rawExifLen = 0;
|
||||
Py_ssize_t rawExifLen = 0;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "ss|iiiiiiiiO"PY_ARG_BYTES_LENGTH""PY_ARG_BYTES_LENGTH,
|
||||
&mode, &rawmode, &quality,
|
||||
|
@ -805,7 +806,7 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
|
|||
char* rawmode;
|
||||
char* compname;
|
||||
char* filename;
|
||||
int fp;
|
||||
Py_ssize_t fp;
|
||||
|
||||
PyObject *dir;
|
||||
PyObject *key, *value;
|
||||
|
@ -985,14 +986,14 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args)
|
|||
PyObject *offset = NULL, *tile_offset = NULL, *tile_size = NULL;
|
||||
char *quality_mode = "rates";
|
||||
PyObject *quality_layers = NULL;
|
||||
int num_resolutions = 0;
|
||||
Py_ssize_t num_resolutions = 0;
|
||||
PyObject *cblk_size = NULL, *precinct_size = NULL;
|
||||
PyObject *irreversible = NULL;
|
||||
char *progression = "LRCP";
|
||||
OPJ_PROG_ORDER prog_order;
|
||||
char *cinema_mode = "no";
|
||||
OPJ_CINEMA_MODE cine_mode;
|
||||
int fd = -1;
|
||||
Py_ssize_t fd = -1;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "ss|OOOsOIOOOssi", &mode, &format,
|
||||
&offset, &tile_offset, &tile_size,
|
||||
|
|
|
@ -517,6 +517,18 @@ l2cmyk(UINT8* out, const UINT8* in, int xsize)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
la2cmyk(UINT8* out, const UINT8* in, int xsize)
|
||||
{
|
||||
int x;
|
||||
for (x = 0; x < xsize; x++, in += 4) {
|
||||
*out++ = 0;
|
||||
*out++ = 0;
|
||||
*out++ = 0;
|
||||
*out++ = ~(in[0]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
rgb2cmyk(UINT8* out, const UINT8* in, int xsize)
|
||||
{
|
||||
|
@ -592,6 +604,22 @@ i2f(UINT8* out_, const UINT8* in_, int xsize)
|
|||
*out++ = (FLOAT32) *in++;
|
||||
}
|
||||
|
||||
static void
|
||||
i2rgb(UINT8* out, const UINT8* in_, int xsize)
|
||||
{
|
||||
int x;
|
||||
INT32* in = (INT32*) in_;
|
||||
for (x = 0; x < xsize; x++, in++, out+=4) {
|
||||
if (*in <= 0)
|
||||
out[0] = out[1] = out[2] = 0;
|
||||
else if (*in >= 255)
|
||||
out[0] = out[1] = out[2] = 255;
|
||||
else
|
||||
out[0] = out[1] = out[2] = (UINT8) *in;
|
||||
out[3] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------- */
|
||||
/* F conversions */
|
||||
/* ------------- */
|
||||
|
@ -657,6 +685,18 @@ l2ycbcr(UINT8* out, const UINT8* in, int xsize)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
la2ycbcr(UINT8* out, const UINT8* in, int xsize)
|
||||
{
|
||||
int x;
|
||||
for (x = 0; x < xsize; x++, in += 4) {
|
||||
*out++ = in[0];
|
||||
*out++ = 128;
|
||||
*out++ = 128;
|
||||
*out++ = 255;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ycbcr2l(UINT8* out, const UINT8* in, int xsize)
|
||||
{
|
||||
|
@ -665,6 +705,16 @@ ycbcr2l(UINT8* out, const UINT8* in, int xsize)
|
|||
*out++ = in[0];
|
||||
}
|
||||
|
||||
static void
|
||||
ycbcr2la(UINT8* out, const UINT8* in, int xsize)
|
||||
{
|
||||
int x;
|
||||
for (x = 0; x < xsize; x++, in += 4, out += 4) {
|
||||
out[0] = out[1] = out[2] = in[0];
|
||||
out[3] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------- */
|
||||
/* I;16 (16-bit) conversions */
|
||||
/* ------------------------- */
|
||||
|
@ -802,16 +852,21 @@ static struct {
|
|||
{ "LA", "L", la2l },
|
||||
{ "LA", "La", lA2la },
|
||||
{ "LA", "RGB", la2rgb },
|
||||
{ "LA", "RGBX", la2rgb },
|
||||
{ "LA", "RGBA", la2rgb },
|
||||
{ "LA", "RGBX", la2rgb },
|
||||
{ "LA", "CMYK", la2cmyk },
|
||||
{ "LA", "YCbCr", la2ycbcr },
|
||||
|
||||
{ "La", "LA", la2lA },
|
||||
|
||||
{ "I", "L", i2l },
|
||||
{ "I", "F", i2f },
|
||||
{ "I", "L", i2l },
|
||||
{ "I", "F", i2f },
|
||||
{ "I", "RGB", i2rgb },
|
||||
{ "I", "RGBA", i2rgb },
|
||||
{ "I", "RGBX", i2rgb },
|
||||
|
||||
{ "F", "L", f2l },
|
||||
{ "F", "I", f2i },
|
||||
{ "F", "L", f2l },
|
||||
{ "F", "I", f2i },
|
||||
|
||||
{ "RGB", "1", rgb2bit },
|
||||
{ "RGB", "L", rgb2l },
|
||||
|
@ -842,8 +897,9 @@ static struct {
|
|||
|
||||
{ "RGBX", "1", rgb2bit },
|
||||
{ "RGBX", "L", rgb2l },
|
||||
{ "RGBA", "I", rgb2i },
|
||||
{ "RGBA", "F", rgb2f },
|
||||
{ "RGBX", "LA", rgb2la },
|
||||
{ "RGBX", "I", rgb2i },
|
||||
{ "RGBX", "F", rgb2f },
|
||||
{ "RGBX", "RGB", rgba2rgb },
|
||||
{ "RGBX", "CMYK", rgb2cmyk },
|
||||
{ "RGBX", "YCbCr", ImagingConvertRGB2YCbCr },
|
||||
|
@ -853,6 +909,7 @@ static struct {
|
|||
{ "CMYK", "RGBX", cmyk2rgb },
|
||||
|
||||
{ "YCbCr", "L", ycbcr2l },
|
||||
{ "YCbCr", "LA", ycbcr2la },
|
||||
{ "YCbCr", "RGB", ImagingConvertYCbCr2RGB },
|
||||
|
||||
{ "HSV", "RGB", hsv2rgb },
|
||||
|
@ -894,6 +951,15 @@ p2bit(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
|
|||
*out++ = (L(&palette[in[x]*4]) >= 128000) ? 255 : 0;
|
||||
}
|
||||
|
||||
static void
|
||||
pa2bit(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
|
||||
{
|
||||
int x;
|
||||
/* FIXME: precalculate greyscale palette? */
|
||||
for (x = 0; x < xsize; x++, in += 4)
|
||||
*out++ = (L(&palette[in[0]*4]) >= 128000) ? 255 : 0;
|
||||
}
|
||||
|
||||
static void
|
||||
p2l(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
|
||||
{
|
||||
|
@ -903,6 +969,28 @@ p2l(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
|
|||
*out++ = L(&palette[in[x]*4]) / 1000;
|
||||
}
|
||||
|
||||
static void
|
||||
pa2l(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
|
||||
{
|
||||
int x;
|
||||
/* FIXME: precalculate greyscale palette? */
|
||||
for (x = 0; x < xsize; x++, in += 4)
|
||||
*out++ = L(&palette[in[0]*4]) / 1000;
|
||||
}
|
||||
|
||||
static void
|
||||
p2pa(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
|
||||
{
|
||||
int x;
|
||||
for (x = 0; x < xsize; x++, in++) {
|
||||
const UINT8* rgba = &palette[in[0]];
|
||||
*out++ = in[0];
|
||||
*out++ = in[0];
|
||||
*out++ = in[0];
|
||||
*out++ = rgba[3];
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
p2la(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
|
||||
{
|
||||
|
@ -920,9 +1008,9 @@ pa2la(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
|
|||
{
|
||||
int x;
|
||||
/* FIXME: precalculate greyscale palette? */
|
||||
for (x = 0; x < xsize; x++, in += 2) {
|
||||
*out++ = L(&palette[in[0]*4]) / 1000;
|
||||
*out++ = in[1];
|
||||
for (x = 0; x < xsize; x++, in += 4, out += 4) {
|
||||
out[0] = out[1] = out[2] = L(&palette[in[0]*4]) / 1000;
|
||||
out[3] = in[3];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -935,6 +1023,15 @@ p2i(UINT8* out_, const UINT8* in, int xsize, const UINT8* palette)
|
|||
*out++ = L(&palette[in[x]*4]) / 1000;
|
||||
}
|
||||
|
||||
static void
|
||||
pa2i(UINT8* out_, const UINT8* in, int xsize, const UINT8* palette)
|
||||
{
|
||||
int x;
|
||||
INT32* out = (INT32*) out_;
|
||||
for (x = 0; x < xsize; x++, in += 4)
|
||||
*out++ = L(&palette[in[0]*4]) / 1000;
|
||||
}
|
||||
|
||||
static void
|
||||
p2f(UINT8* out_, const UINT8* in, int xsize, const UINT8* palette)
|
||||
{
|
||||
|
@ -944,6 +1041,15 @@ p2f(UINT8* out_, const UINT8* in, int xsize, const UINT8* palette)
|
|||
*out++ = (float) L(&palette[in[x]*4]) / 1000.0F;
|
||||
}
|
||||
|
||||
static void
|
||||
pa2f(UINT8* out_, const UINT8* in, int xsize, const UINT8* palette)
|
||||
{
|
||||
int x;
|
||||
FLOAT32* out = (FLOAT32*) out_;
|
||||
for (x = 0; x < xsize; x++, in += 4)
|
||||
*out++ = (float) L(&palette[in[0]*4]) / 1000.0F;
|
||||
}
|
||||
|
||||
static void
|
||||
p2rgb(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
|
||||
{
|
||||
|
@ -957,6 +1063,19 @@ p2rgb(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pa2rgb(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
|
||||
{
|
||||
int x;
|
||||
for (x = 0; x < xsize; x++, in += 4) {
|
||||
const UINT8* rgb = &palette[in[0] * 4];
|
||||
*out++ = rgb[0];
|
||||
*out++ = rgb[1];
|
||||
*out++ = rgb[2];
|
||||
*out++ = 255;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
p2rgba(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
|
||||
{
|
||||
|
@ -990,6 +1109,13 @@ p2cmyk(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
|
|||
rgb2cmyk(out, out, xsize);
|
||||
}
|
||||
|
||||
static void
|
||||
pa2cmyk(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
|
||||
{
|
||||
pa2rgb(out, in, xsize, palette);
|
||||
rgb2cmyk(out, out, xsize);
|
||||
}
|
||||
|
||||
static void
|
||||
p2ycbcr(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
|
||||
{
|
||||
|
@ -997,6 +1123,13 @@ p2ycbcr(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
|
|||
ImagingConvertRGB2YCbCr(out, out, xsize);
|
||||
}
|
||||
|
||||
static void
|
||||
pa2ycbcr(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
|
||||
{
|
||||
pa2rgb(out, in, xsize, palette);
|
||||
ImagingConvertRGB2YCbCr(out, out, xsize);
|
||||
}
|
||||
|
||||
static Imaging
|
||||
frompalette(Imaging imOut, Imaging imIn, const char *mode)
|
||||
{
|
||||
|
@ -1013,25 +1146,27 @@ frompalette(Imaging imOut, Imaging imIn, const char *mode)
|
|||
alpha = !strcmp(imIn->mode, "PA");
|
||||
|
||||
if (strcmp(mode, "1") == 0)
|
||||
convert = p2bit;
|
||||
convert = alpha ? pa2bit : p2bit;
|
||||
else if (strcmp(mode, "L") == 0)
|
||||
convert = p2l;
|
||||
convert = alpha ? pa2l : p2l;
|
||||
else if (strcmp(mode, "LA") == 0)
|
||||
convert = (alpha) ? pa2la : p2la;
|
||||
convert = alpha ? pa2la : p2la;
|
||||
else if (strcmp(mode, "PA") == 0)
|
||||
convert = p2pa;
|
||||
else if (strcmp(mode, "I") == 0)
|
||||
convert = p2i;
|
||||
convert = alpha ? pa2i : p2i;
|
||||
else if (strcmp(mode, "F") == 0)
|
||||
convert = p2f;
|
||||
convert = alpha ? pa2f : p2f;
|
||||
else if (strcmp(mode, "RGB") == 0)
|
||||
convert = p2rgb;
|
||||
convert = alpha ? pa2rgb : p2rgb;
|
||||
else if (strcmp(mode, "RGBA") == 0)
|
||||
convert = (alpha) ? pa2rgba : p2rgba;
|
||||
convert = alpha ? pa2rgba : p2rgba;
|
||||
else if (strcmp(mode, "RGBX") == 0)
|
||||
convert = p2rgba;
|
||||
convert = alpha ? pa2rgba : p2rgba;
|
||||
else if (strcmp(mode, "CMYK") == 0)
|
||||
convert = p2cmyk;
|
||||
convert = alpha ? pa2cmyk : p2cmyk;
|
||||
else if (strcmp(mode, "YCbCr") == 0)
|
||||
convert = p2ycbcr;
|
||||
convert = alpha ? pa2ycbcr : p2ycbcr;
|
||||
else
|
||||
return (Imaging) ImagingError_ValueError("conversion not supported");
|
||||
|
||||
|
@ -1052,9 +1187,10 @@ frompalette(Imaging imOut, Imaging imIn, const char *mode)
|
|||
#pragma optimize("", off)
|
||||
#endif
|
||||
static Imaging
|
||||
topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither)
|
||||
topalette(Imaging imOut, Imaging imIn, const char *mode, ImagingPalette inpalette, int dither)
|
||||
{
|
||||
ImagingSectionCookie cookie;
|
||||
int alpha;
|
||||
int x, y;
|
||||
ImagingPalette palette = inpalette;;
|
||||
|
||||
|
@ -1062,6 +1198,8 @@ topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither)
|
|||
if (strcmp(imIn->mode, "L") != 0 && strncmp(imIn->mode, "RGB", 3) != 0)
|
||||
return (Imaging) ImagingError_ValueError("conversion not supported");
|
||||
|
||||
alpha = !strcmp(mode, "PA");
|
||||
|
||||
if (palette == NULL) {
|
||||
/* FIXME: make user configurable */
|
||||
if (imIn->bands == 1)
|
||||
|
@ -1073,7 +1211,7 @@ topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither)
|
|||
if (!palette)
|
||||
return (Imaging) ImagingError_ValueError("no palette");
|
||||
|
||||
imOut = ImagingNew2Dirty("P", imOut, imIn);
|
||||
imOut = ImagingNew2Dirty(mode, imOut, imIn);
|
||||
if (!imOut) {
|
||||
if (palette != inpalette)
|
||||
ImagingPaletteDelete(palette);
|
||||
|
@ -1088,8 +1226,13 @@ topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither)
|
|||
|
||||
/* Greyscale palette: copy data as is */
|
||||
ImagingSectionEnter(&cookie);
|
||||
for (y = 0; y < imIn->ysize; y++)
|
||||
memcpy(imOut->image[y], imIn->image[y], imIn->linesize);
|
||||
for (y = 0; y < imIn->ysize; y++) {
|
||||
if (alpha) {
|
||||
l2la((UINT8*) imOut->image[y], (UINT8*) imIn->image[y], imIn->xsize);
|
||||
} else {
|
||||
memcpy(imOut->image[y], imIn->image[y], imIn->linesize);
|
||||
}
|
||||
}
|
||||
ImagingSectionLeave(&cookie);
|
||||
|
||||
} else {
|
||||
|
@ -1120,7 +1263,7 @@ topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither)
|
|||
int g, g0, g1, g2;
|
||||
int b, b0, b1, b2;
|
||||
UINT8* in = (UINT8*) imIn->image[y];
|
||||
UINT8* out = imOut->image8[y];
|
||||
UINT8* out = alpha ? (UINT8*) imOut->image32[y] : imOut->image8[y];
|
||||
int* e = errors;
|
||||
|
||||
r = r0 = r1 = 0;
|
||||
|
@ -1139,7 +1282,12 @@ topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither)
|
|||
cache = &ImagingPaletteCache(palette, r, g, b);
|
||||
if (cache[0] == 0x100)
|
||||
ImagingPaletteCacheUpdate(palette, r, g, b);
|
||||
out[x] = (UINT8) cache[0];
|
||||
if (alpha) {
|
||||
out[x*4] = out[x*4+1] = out[x*4+2] = (UINT8) cache[0];
|
||||
out[x*4+3] = 255;
|
||||
} else {
|
||||
out[x] = (UINT8) cache[0];
|
||||
}
|
||||
|
||||
r -= (int) palette->palette[cache[0]*4];
|
||||
g -= (int) palette->palette[cache[0]*4+1];
|
||||
|
@ -1172,7 +1320,7 @@ topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither)
|
|||
for (y = 0; y < imIn->ysize; y++) {
|
||||
int r, g, b;
|
||||
UINT8* in = (UINT8*) imIn->image[y];
|
||||
UINT8* out = imOut->image8[y];
|
||||
UINT8* out = alpha ? (UINT8*) imOut->image32[y] : imOut->image8[y];
|
||||
|
||||
for (x = 0; x < imIn->xsize; x++, in += 4) {
|
||||
INT16* cache;
|
||||
|
@ -1183,8 +1331,12 @@ topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither)
|
|||
cache = &ImagingPaletteCache(palette, r, g, b);
|
||||
if (cache[0] == 0x100)
|
||||
ImagingPaletteCacheUpdate(palette, r, g, b);
|
||||
out[x] = (UINT8) cache[0];
|
||||
|
||||
if (alpha) {
|
||||
out[x*4] = out[x*4+1] = out[x*4+2] = (UINT8) cache[0];
|
||||
out[x*4+3] = 255;
|
||||
} else {
|
||||
out[x] = (UINT8) cache[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
ImagingSectionLeave(&cookie);
|
||||
|
@ -1314,8 +1466,8 @@ convert(Imaging imOut, Imaging imIn, const char *mode,
|
|||
if (strcmp(imIn->mode, "P") == 0 || strcmp(imIn->mode, "PA") == 0)
|
||||
return frompalette(imOut, imIn, mode);
|
||||
|
||||
if (strcmp(mode, "P") == 0)
|
||||
return topalette(imOut, imIn, palette, dither);
|
||||
if (strcmp(mode, "P") == 0 || strcmp(mode, "PA") == 0)
|
||||
return topalette(imOut, imIn, mode, palette, dither);
|
||||
|
||||
if (dither && strcmp(mode, "1") == 0)
|
||||
return tobilevel(imOut, imIn, dither);
|
||||
|
@ -1385,6 +1537,8 @@ ImagingConvertTransparent(Imaging imIn, const char *mode,
|
|||
}
|
||||
|
||||
if (!((strcmp(imIn->mode, "RGB") == 0 ||
|
||||
strcmp(imIn->mode, "1") == 0 ||
|
||||
strcmp(imIn->mode, "I") == 0 ||
|
||||
strcmp(imIn->mode, "L") == 0)
|
||||
&& strcmp(mode, "RGBA") == 0))
|
||||
#ifdef notdef
|
||||
|
@ -1403,7 +1557,13 @@ ImagingConvertTransparent(Imaging imIn, const char *mode,
|
|||
if (strcmp(imIn->mode, "RGB") == 0) {
|
||||
convert = rgb2rgba;
|
||||
} else {
|
||||
convert = l2rgb;
|
||||
if (strcmp(imIn->mode, "1") == 0) {
|
||||
convert = bit2rgb;
|
||||
} else if (strcmp(imIn->mode, "I") == 0) {
|
||||
convert = i2rgb;
|
||||
} else {
|
||||
convert = l2rgb;
|
||||
}
|
||||
g = b = r;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,8 +29,8 @@ ImagingFlipLeftRight(Imaging imOut, Imaging imIn)
|
|||
|
||||
#define FLIP_LEFT_RIGHT(INT, image) \
|
||||
for (y = 0; y < imIn->ysize; y++) { \
|
||||
INT* in = imIn->image[y]; \
|
||||
INT* out = imOut->image[y]; \
|
||||
INT* in = (INT *)imIn->image[y]; \
|
||||
INT* out = (INT *)imOut->image[y]; \
|
||||
xr = imIn->xsize-1; \
|
||||
for (x = 0; x < imIn->xsize; x++, xr--) \
|
||||
out[xr] = in[x]; \
|
||||
|
@ -105,10 +105,10 @@ ImagingRotate90(Imaging imOut, Imaging imIn)
|
|||
yyysize = yy + ROTATE_SMALL_CHUNK < imIn->ysize ? yy + ROTATE_SMALL_CHUNK : imIn->ysize; \
|
||||
xxxsize = xx + ROTATE_SMALL_CHUNK < imIn->xsize ? xx + ROTATE_SMALL_CHUNK : imIn->xsize; \
|
||||
for (yyy = yy; yyy < yyysize; yyy++) { \
|
||||
INT* in = imIn->image[yyy]; \
|
||||
INT* in = (INT *)imIn->image[yyy]; \
|
||||
xr = imIn->xsize - 1 - xx; \
|
||||
for (xxx = xx; xxx < xxxsize; xxx++, xr--) { \
|
||||
INT* out = imOut->image[xr]; \
|
||||
INT* out = (INT *)imOut->image[xr]; \
|
||||
out[yyy] = in[xxx]; \
|
||||
} \
|
||||
} \
|
||||
|
@ -161,9 +161,9 @@ ImagingTranspose(Imaging imOut, Imaging imIn)
|
|||
yyysize = yy + ROTATE_SMALL_CHUNK < imIn->ysize ? yy + ROTATE_SMALL_CHUNK : imIn->ysize; \
|
||||
xxxsize = xx + ROTATE_SMALL_CHUNK < imIn->xsize ? xx + ROTATE_SMALL_CHUNK : imIn->xsize; \
|
||||
for (yyy = yy; yyy < yyysize; yyy++) { \
|
||||
INT* in = imIn->image[yyy]; \
|
||||
INT* in = (INT *)imIn->image[yyy]; \
|
||||
for (xxx = xx; xxx < xxxsize; xxx++) { \
|
||||
INT* out = imOut->image[xxx]; \
|
||||
INT* out = (INT *)imOut->image[xxx]; \
|
||||
out[yyy] = in[xxx]; \
|
||||
} \
|
||||
} \
|
||||
|
@ -217,10 +217,10 @@ ImagingTransverse(Imaging imOut, Imaging imIn)
|
|||
xxxsize = xx + ROTATE_SMALL_CHUNK < imIn->xsize ? xx + ROTATE_SMALL_CHUNK : imIn->xsize; \
|
||||
yr = imIn->ysize - 1 - yy; \
|
||||
for (yyy = yy; yyy < yyysize; yyy++, yr--) { \
|
||||
INT* in = imIn->image[yyy]; \
|
||||
INT* in = (INT *)imIn->image[yyy]; \
|
||||
xr = imIn->xsize - 1 - xx; \
|
||||
for (xxx = xx; xxx < xxxsize; xxx++, xr--) { \
|
||||
INT* out = imOut->image[xr]; \
|
||||
INT* out = (INT *)imOut->image[xr]; \
|
||||
out[yr] = in[xxx]; \
|
||||
} \
|
||||
} \
|
||||
|
@ -264,8 +264,8 @@ ImagingRotate180(Imaging imOut, Imaging imIn)
|
|||
|
||||
#define ROTATE_180(INT, image) \
|
||||
for (y = 0; y < imIn->ysize; y++, yr--) { \
|
||||
INT* in = imIn->image[y]; \
|
||||
INT* out = imOut->image[yr]; \
|
||||
INT* in = (INT *)imIn->image[y]; \
|
||||
INT* out = (INT *)imOut->image[yr]; \
|
||||
xr = imIn->xsize-1; \
|
||||
for (x = 0; x < imIn->xsize; x++, xr--) \
|
||||
out[xr] = in[x]; \
|
||||
|
@ -317,9 +317,9 @@ ImagingRotate270(Imaging imOut, Imaging imIn)
|
|||
xxxsize = xx + ROTATE_SMALL_CHUNK < imIn->xsize ? xx + ROTATE_SMALL_CHUNK : imIn->xsize; \
|
||||
yr = imIn->ysize - 1 - yy; \
|
||||
for (yyy = yy; yyy < yyysize; yyy++, yr--) { \
|
||||
INT* in = imIn->image[yyy]; \
|
||||
INT* in = (INT *)imIn->image[yyy]; \
|
||||
for (xxx = xx; xxx < xxxsize; xxx++) { \
|
||||
INT* out = imOut->image[xxx]; \
|
||||
INT* out = (INT *)imOut->image[xxx]; \
|
||||
out[yr] = in[xxx]; \
|
||||
} \
|
||||
} \
|
||||
|
|
|
@ -1331,6 +1331,7 @@ static struct {
|
|||
{"RGB", "BGR;5", 16, ImagingUnpackBGR15}, /* compat */
|
||||
{"RGB", "RGBX", 32, copy4},
|
||||
{"RGB", "RGBX;L", 32, unpackRGBAL},
|
||||
{"RGB", "RGBA;L", 32, unpackRGBAL},
|
||||
{"RGB", "BGRX", 32, ImagingUnpackBGRX},
|
||||
{"RGB", "XRGB", 24, ImagingUnpackXRGB},
|
||||
{"RGB", "XBGR", 32, ImagingUnpackXBGR},
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
curl -fsSL -o pypy2.zip https://bitbucket.org/pypy/pypy/downloads/pypy2.7-v7.0.0-win32.zip
|
||||
curl -fsSL -o pypy2.zip https://bitbucket.org/pypy/pypy/downloads/pypy2.7-v7.1.0-win32.zip
|
||||
7z x pypy2.zip -oc:\
|
||||
c:\Python37\Scripts\virtualenv.exe -p c:\pypy2.7-v7.0.0-win32\pypy.exe c:\vp\pypy2
|
||||
c:\Python37\Scripts\virtualenv.exe -p c:\pypy2.7-v7.1.0-win32\pypy.exe c:\vp\pypy2
|
||||
|
|
|
@ -109,6 +109,7 @@ set INCLUDE=%%INCLUDE%%;%%INCLIB%%\%(inc_dir)s;%%INCLIB%%\tcl%(tcl_ver)s\include
|
|||
setlocal
|
||||
set LIB=%%LIB%%;C:\Python%(py_ver)s\tcl%(vc_setup)s
|
||||
call %(python_path)s\%(executable)s setup.py %(imaging_libs)s %%BLDOPT%%
|
||||
call %(python_path)s\%(executable)s -c "from PIL import _webp;import os, shutil;shutil.copy('%%INCLIB%%\\freetype.dll', os.path.dirname(_webp.__file__));"
|
||||
endlocal
|
||||
|
||||
endlocal
|
||||
|
|
|
@ -187,37 +187,30 @@ endlocal
|
|||
return script % compiler
|
||||
|
||||
|
||||
def msbuild_freetype(compiler):
|
||||
if compiler['env_version'] == 'v7.1':
|
||||
return msbuild_freetype_71(compiler)
|
||||
return msbuild_freetype_70(compiler)
|
||||
|
||||
|
||||
def msbuild_freetype_71(compiler):
|
||||
return r"""
|
||||
def msbuild_freetype(compiler, bit):
|
||||
script = r"""
|
||||
rem Build freetype
|
||||
setlocal
|
||||
rd /S /Q %%FREETYPE%%\objs
|
||||
%%MSBUILD%% %%FREETYPE%%\builds\windows\vc%(vc_version)s\freetype.sln /t:Clean;Build /p:Configuration="Release" /p:Platform=%(platform)s /m
|
||||
set DefaultPlatformToolset=v100
|
||||
"""
|
||||
properties = r"""/p:Configuration="Release" /p:Platform=%(platform)s"""
|
||||
if bit == 64:
|
||||
script += r'copy /Y /B ' +\
|
||||
r'"C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Lib\x64\*.Lib" ' +\
|
||||
r'%%FREETYPE%%\builds\windows\vc2010'
|
||||
properties += r" /p:_IsNativeEnvironment=false"
|
||||
script += r"""
|
||||
%%MSBUILD%% %%FREETYPE%%\builds\windows\vc2010\freetype.sln /t:Clean;Build """+properties+r""" /m
|
||||
xcopy /Y /E /Q %%FREETYPE%%\include %%INCLIB%%
|
||||
copy /Y /B %%FREETYPE%%\objs\vc%(vc_version)s\%(platform)s\*.lib %%INCLIB%%\freetype.lib
|
||||
"""
|
||||
freetypeReleaseDir = r"%%FREETYPE%%\objs\%(platform)s\Release"
|
||||
script += r"""
|
||||
copy /Y /B """+freetypeReleaseDir+r"""\freetype.lib %%INCLIB%%\freetype.lib
|
||||
copy /Y /B """+freetypeReleaseDir+r"""\freetype.dll %%INCLIB%%\..\freetype.dll
|
||||
endlocal
|
||||
""" % compiler # noqa: E501
|
||||
|
||||
|
||||
def msbuild_freetype_70(compiler):
|
||||
return r"""
|
||||
rem Build freetype
|
||||
setlocal
|
||||
py -3 %%~dp0\fixproj.py %%FREETYPE%%\builds\windows\vc%(vc_version)s\freetype.sln %(platform)s
|
||||
py -3 %%~dp0\fixproj.py %%FREETYPE%%\builds\windows\vc%(vc_version)s\freetype.vcproj %(platform)s
|
||||
rd /S /Q %%FREETYPE%%\objs
|
||||
%%MSBUILD%% %%FREETYPE%%\builds\windows\vc%(vc_version)s\freetype.sln /t:Clean;Build /p:Configuration="LIB Release";Platform=%(platform)s /m
|
||||
xcopy /Y /E /Q %%FREETYPE%%\include %%INCLIB%%
|
||||
xcopy /Y /E /Q %%FREETYPE%%\objs\win32\vc%(vc_version)s %%INCLIB%%
|
||||
copy /Y /B %%FREETYPE%%\objs\win32\vc%(vc_version)s\*.lib %%INCLIB%%\freetype.lib
|
||||
endlocal
|
||||
""" % compiler # noqa: E501
|
||||
"""
|
||||
return script % compiler # noqa: E501
|
||||
|
||||
|
||||
def build_lcms2(compiler):
|
||||
|
@ -238,9 +231,9 @@ setlocal
|
|||
rd /S /Q %%LCMS%%\Lib
|
||||
rd /S /Q %%LCMS%%\Projects\VC%(vc_version)s\Release
|
||||
%%MSBUILD%% %%LCMS%%\Projects\VC%(vc_version)s\lcms2.sln /t:Clean /p:Configuration="Release" /p:Platform=Win32 /m
|
||||
%%MSBUILD%% %%LCMS%%\Projects\VC%(vc_version)s\lcms2.sln /t:lcms2_static /p:Configuration="Release" /p:Platform=Win32 /m
|
||||
%%MSBUILD%% %%LCMS%%\Projects\VC%(vc_version)s\lcms2.sln /t:lcms2_static /p:Configuration="Release" /p:Platform=Win32 /p:PlatformToolset=v90 /m
|
||||
xcopy /Y /E /Q %%LCMS%%\include %%INCLIB%%
|
||||
copy /Y /B %%LCMS%%\Projects\VC%(vc_version)s\Release\*.lib %%INCLIB%%
|
||||
copy /Y /B %%LCMS%%\Lib\MS\*.lib %%INCLIB%%
|
||||
endlocal
|
||||
""" % compiler # noqa: E501
|
||||
|
||||
|
@ -265,7 +258,7 @@ rem Build gs
|
|||
setlocal
|
||||
""" + vc_setup(compiler, bit) + r"""
|
||||
set MSVC_VERSION=""" + {
|
||||
"2008": "9",
|
||||
"2010": "90",
|
||||
"2015": "14"
|
||||
}[compiler['vc_version']] + r"""
|
||||
set RCOMP="C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Bin\RC.Exe"
|
||||
|
@ -289,7 +282,7 @@ def add_compiler(compiler, bit):
|
|||
|
||||
# script.append(extract_openjpeg(compiler))
|
||||
|
||||
script.append(msbuild_freetype(compiler))
|
||||
script.append(msbuild_freetype(compiler, bit))
|
||||
script.append(build_lcms2(compiler))
|
||||
# script.append(nmake_openjpeg(compiler))
|
||||
script.append(build_ghostscript(compiler, bit))
|
||||
|
@ -308,7 +301,7 @@ if 'PYTHON' in os.environ:
|
|||
else:
|
||||
# for compiler in all_compilers():
|
||||
# add_compiler(compiler)
|
||||
add_compiler(compilers[7.0][2008][32], 32)
|
||||
add_compiler(compilers[7.0][2010][32], 32)
|
||||
|
||||
with open('build_deps.cmd', 'w') as f:
|
||||
f.write("\n".join(script))
|
||||
|
|
|
@ -3,8 +3,8 @@ import os
|
|||
SF_MIRROR = 'http://iweb.dl.sourceforge.net'
|
||||
PILLOW_DEPENDS_DIR = 'C:\\pillow-depends\\'
|
||||
|
||||
pythons = {'27': {'compiler': 7, 'vc': 2008},
|
||||
'pypy2': {'compiler': 7, 'vc': 2008},
|
||||
pythons = {'27': {'compiler': 7, 'vc': 2010},
|
||||
'pypy2': {'compiler': 7, 'vc': 2010},
|
||||
'35': {'compiler': 7.1, 'vc': 2015},
|
||||
'36': {'compiler': 7.1, 'vc': 2015},
|
||||
'37': {'compiler': 7.1, 'vc': 2015}}
|
||||
|
@ -43,9 +43,9 @@ libs = {
|
|||
'dir': 'lcms2-2.7',
|
||||
},
|
||||
'ghostscript': {
|
||||
'url': 'https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs926/ghostscript-9.26.tar.gz', # noqa: E501
|
||||
'filename': PILLOW_DEPENDS_DIR + 'ghostscript-9.26.tar.gz',
|
||||
'dir': 'ghostscript-9.26',
|
||||
'url': 'https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs927/ghostscript-9.27.tar.gz', # noqa: E501
|
||||
'filename': PILLOW_DEPENDS_DIR + 'ghostscript-9.27.tar.gz',
|
||||
'dir': 'ghostscript-9.27',
|
||||
},
|
||||
'tcl-8.5': {
|
||||
'url': SF_MIRROR+'/project/tcl/Tcl/8.5.19/tcl8519-src.zip',
|
||||
|
@ -83,10 +83,10 @@ libs = {
|
|||
|
||||
compilers = {
|
||||
7: {
|
||||
2008: {
|
||||
2010: {
|
||||
64: {
|
||||
'env_version': 'v7.0',
|
||||
'vc_version': '2008',
|
||||
'vc_version': '2010',
|
||||
'env_flags': '/x64 /xp',
|
||||
'inc_dir': 'msvcr90-x64',
|
||||
'platform': 'x64',
|
||||
|
@ -94,7 +94,7 @@ compilers = {
|
|||
},
|
||||
32: {
|
||||
'env_version': 'v7.0',
|
||||
'vc_version': '2008',
|
||||
'vc_version': '2010',
|
||||
'env_flags': '/x86 /xp',
|
||||
'inc_dir': 'msvcr90-x32',
|
||||
'platform': 'Win32',
|
||||
|
|