mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-04-28 21:13:41 +03:00
Merge branch 'master' into perfperf-improv-ImageDraw-floodfill
This commit is contained in:
commit
5e2d6c951a
|
@ -71,7 +71,7 @@ build_script:
|
||||||
|
|
||||||
test_script:
|
test_script:
|
||||||
- cd c:\pillow
|
- cd c:\pillow
|
||||||
- '%PYTHON%\%PIP_DIR%\pip.exe install "pytest<3.7" pytest-cov'
|
- '%PYTHON%\%PIP_DIR%\pip.exe install pytest pytest-cov'
|
||||||
- '%PYTHON%\%EXECUTABLE% -m pytest -vx --cov PIL --cov-report term --cov-report xml Tests'
|
- '%PYTHON%\%EXECUTABLE% -m pytest -vx --cov PIL --cov-report term --cov-report xml Tests'
|
||||||
#- '%PYTHON%\%EXECUTABLE% test-installed.py -v -s %TEST_OPTIONS%' TODO TEST_OPTIONS with pytest?
|
#- '%PYTHON%\%EXECUTABLE% test-installed.py -v -s %TEST_OPTIONS%' TODO TEST_OPTIONS with pytest?
|
||||||
|
|
||||||
|
|
19
.travis.yml
19
.travis.yml
|
@ -1,3 +1,5 @@
|
||||||
|
dist: xenial
|
||||||
|
sudo: required
|
||||||
language: python
|
language: python
|
||||||
cache: pip
|
cache: pip
|
||||||
|
|
||||||
|
@ -12,13 +14,24 @@ matrix:
|
||||||
fast_finish: true
|
fast_finish: true
|
||||||
include:
|
include:
|
||||||
- python: "pypy"
|
- python: "pypy"
|
||||||
|
dist: trusty
|
||||||
- python: "pypy3"
|
- python: "pypy3"
|
||||||
- python: '3.7-dev'
|
dist: trusty
|
||||||
|
- python: '3.7'
|
||||||
- python: '2.7'
|
- python: '2.7'
|
||||||
|
- python: '2.7'
|
||||||
|
dist: trusty
|
||||||
- python: "2.7_with_system_site_packages" # For PyQt4
|
- python: "2.7_with_system_site_packages" # For PyQt4
|
||||||
|
- python: "2.7_with_system_site_packages" # For PyQt4
|
||||||
|
dist: trusty
|
||||||
- python: '3.6'
|
- python: '3.6'
|
||||||
|
- python: '3.6'
|
||||||
|
dist: trusty
|
||||||
- python: '3.5'
|
- python: '3.5'
|
||||||
|
- python: '3.5'
|
||||||
|
dist: trusty
|
||||||
- python: '3.4'
|
- python: '3.4'
|
||||||
|
dist: trusty
|
||||||
- env: DOCKER="alpine" DOCKER_TAG="pytest"
|
- env: DOCKER="alpine" DOCKER_TAG="pytest"
|
||||||
- env: DOCKER="arch" DOCKER_TAG="pytest" # contains PyQt5
|
- env: DOCKER="arch" DOCKER_TAG="pytest" # contains PyQt5
|
||||||
- env: DOCKER="ubuntu-trusty-x86" DOCKER_TAG="pytest"
|
- env: DOCKER="ubuntu-trusty-x86" DOCKER_TAG="pytest"
|
||||||
|
@ -31,10 +44,6 @@ matrix:
|
||||||
- env: DOCKER="fedora-26-amd64" DOCKER_TAG="pytest"
|
- env: DOCKER="fedora-26-amd64" DOCKER_TAG="pytest"
|
||||||
- env: DOCKER="fedora-27-amd64" DOCKER_TAG="pytest"
|
- env: DOCKER="fedora-27-amd64" DOCKER_TAG="pytest"
|
||||||
|
|
||||||
dist: trusty
|
|
||||||
|
|
||||||
sudo: required
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
- docker
|
- docker
|
||||||
|
|
||||||
|
|
21
CHANGES.rst
21
CHANGES.rst
|
@ -5,6 +5,27 @@ Changelog (Pillow)
|
||||||
5.3.0 (unreleased)
|
5.3.0 (unreleased)
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
- Changed ImageFilter.Kernel to subclass ImageFilter.BuiltinFilter, instead of the other way around #3273
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Remove unused draw.draw_line, draw.draw_point and font.getabc methods #3232
|
||||||
|
[hugovk]
|
||||||
|
|
||||||
|
- Tests: Added ImageFilter tests #3295
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Tests: Added ImageChops tests #3230
|
||||||
|
[hugovk, radarhere]
|
||||||
|
|
||||||
|
- AppVeyor: Download lib if not present in pillow-depends #3316
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Travis CI: Add Python 3.7 and Xenial #3234
|
||||||
|
[hugovk]
|
||||||
|
|
||||||
|
- Docs: Added documentation for NumPy conversion #3301
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
- Depends: Update libimagequant to 2.12.1 #3281
|
- Depends: Update libimagequant to 2.12.1 #3281
|
||||||
[radarhere]
|
[radarhere]
|
||||||
|
|
||||||
|
|
|
@ -89,12 +89,12 @@ Released as needed privately to individual vendors for critical security-related
|
||||||
$ git clone https://github.com/python-pillow/pillow-wheels
|
$ git clone https://github.com/python-pillow/pillow-wheels
|
||||||
$ cd pillow-wheels
|
$ cd pillow-wheels
|
||||||
$ git submodule init
|
$ git submodule init
|
||||||
$ git submodule update
|
$ git submodule update Pillow
|
||||||
$ cd Pillow
|
$ cd Pillow
|
||||||
$ git fetch --all
|
$ git fetch --all
|
||||||
$ git checkout [[release tag]]
|
$ git checkout [[release tag]]
|
||||||
$ cd ..
|
$ cd ..
|
||||||
$ git commit -m "Pillow -> 2.9.0" Pillow
|
$ git commit -m "Pillow -> 5.2.0" Pillow
|
||||||
$ git push
|
$ git push
|
||||||
```
|
```
|
||||||
* [ ] Download distributions from the [Pillow Wheel Builder container](http://a365fff413fe338398b6-1c8a9b3114517dc5fe17b7c3f8c63a43.r19.cf2.rackcdn.com/).
|
* [ ] Download distributions from the [Pillow Wheel Builder container](http://a365fff413fe338398b6-1c8a9b3114517dc5fe17b7c3f8c63a43.r19.cf2.rackcdn.com/).
|
||||||
|
|
|
@ -146,13 +146,13 @@ class TestFileJpeg2k(PillowTestCase):
|
||||||
self.assertEqual(j2k.mode, 'I;16')
|
self.assertEqual(j2k.mode, 'I;16')
|
||||||
self.assertEqual(jp2.mode, 'I;16')
|
self.assertEqual(jp2.mode, 'I;16')
|
||||||
|
|
||||||
def test_16bit_monchrome_jp2_like_tiff(self):
|
def test_16bit_monochrome_jp2_like_tiff(self):
|
||||||
|
|
||||||
tiff_16bit = Image.open('Tests/images/16bit.cropped.tif')
|
tiff_16bit = Image.open('Tests/images/16bit.cropped.tif')
|
||||||
jp2 = Image.open('Tests/images/16bit.cropped.jp2')
|
jp2 = Image.open('Tests/images/16bit.cropped.jp2')
|
||||||
self.assert_image_similar(jp2, tiff_16bit, 1e-3)
|
self.assert_image_similar(jp2, tiff_16bit, 1e-3)
|
||||||
|
|
||||||
def test_16bit_monchrome_j2k_like_tiff(self):
|
def test_16bit_monochrome_j2k_like_tiff(self):
|
||||||
|
|
||||||
tiff_16bit = Image.open('Tests/images/16bit.cropped.tif')
|
tiff_16bit = Image.open('Tests/images/16bit.cropped.tif')
|
||||||
j2k = Image.open('Tests/images/16bit.cropped.j2k')
|
j2k = Image.open('Tests/images/16bit.cropped.j2k')
|
||||||
|
|
|
@ -12,7 +12,7 @@ class TestFileTar(PillowTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
if "zip_decoder" not in codecs and "jpeg_decoder" not in codecs:
|
if "zip_decoder" not in codecs and "jpeg_decoder" not in codecs:
|
||||||
self.skipTest("neither jpeg nor zip support not available")
|
self.skipTest("neither jpeg nor zip support available")
|
||||||
|
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
if "zip_decoder" in codecs:
|
if "zip_decoder" in codecs:
|
||||||
|
|
|
@ -94,6 +94,15 @@ class TestImageFilter(PillowTestCase):
|
||||||
self.assertEqual(rankfilter.size, 1)
|
self.assertEqual(rankfilter.size, 1)
|
||||||
self.assertEqual(rankfilter.rank, 2)
|
self.assertEqual(rankfilter.rank, 2)
|
||||||
|
|
||||||
|
def test_builtinfilter_p(self):
|
||||||
|
builtinFilter = ImageFilter.BuiltinFilter()
|
||||||
|
|
||||||
|
self.assertRaises(ValueError, builtinFilter.filter, hopper("P"))
|
||||||
|
|
||||||
|
def test_kernel_not_enough_coefficients(self):
|
||||||
|
self.assertRaises(ValueError,
|
||||||
|
lambda: ImageFilter.Kernel((3, 3), (0, 0)))
|
||||||
|
|
||||||
def test_consistency_3x3(self):
|
def test_consistency_3x3(self):
|
||||||
source = Image.open("Tests/images/hopper.bmp")
|
source = Image.open("Tests/images/hopper.bmp")
|
||||||
reference = Image.open("Tests/images/hopper_emboss.bmp")
|
reference = Image.open("Tests/images/hopper_emboss.bmp")
|
||||||
|
|
|
@ -3,6 +3,16 @@ from helper import unittest, PillowTestCase, hopper
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from PIL import ImageChops
|
from PIL import ImageChops
|
||||||
|
|
||||||
|
BLACK = (0, 0, 0)
|
||||||
|
BROWN = (127, 64, 0)
|
||||||
|
CYAN = (0, 255, 255)
|
||||||
|
DARK_GREEN = (0, 128, 0)
|
||||||
|
GREEN = (0, 255, 0)
|
||||||
|
ORANGE = (255, 128, 0)
|
||||||
|
WHITE = (255, 255, 255)
|
||||||
|
|
||||||
|
GREY = 128
|
||||||
|
|
||||||
|
|
||||||
class TestImageChops(PillowTestCase):
|
class TestImageChops(PillowTestCase):
|
||||||
|
|
||||||
|
@ -35,6 +45,303 @@ class TestImageChops(PillowTestCase):
|
||||||
ImageChops.offset(im, 10)
|
ImageChops.offset(im, 10)
|
||||||
ImageChops.offset(im, 10, 20)
|
ImageChops.offset(im, 10, 20)
|
||||||
|
|
||||||
|
def test_add(self):
|
||||||
|
# Arrange
|
||||||
|
im1 = Image.open("Tests/images/imagedraw_ellipse_RGB.png")
|
||||||
|
im2 = Image.open("Tests/images/imagedraw_floodfill.png")
|
||||||
|
|
||||||
|
# Act
|
||||||
|
new = ImageChops.add(im1, im2)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assertEqual(new.getbbox(), (25, 25, 76, 76))
|
||||||
|
self.assertEqual(new.getpixel((50, 50)), ORANGE)
|
||||||
|
|
||||||
|
def test_add_scale_offset(self):
|
||||||
|
# Arrange
|
||||||
|
im1 = Image.open("Tests/images/imagedraw_ellipse_RGB.png")
|
||||||
|
im2 = Image.open("Tests/images/imagedraw_floodfill.png")
|
||||||
|
|
||||||
|
# Act
|
||||||
|
new = ImageChops.add(im1, im2, scale=2.5, offset=100)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assertEqual(new.getbbox(), (0, 0, 100, 100))
|
||||||
|
self.assertEqual(new.getpixel((50, 50)), (202, 151, 100))
|
||||||
|
|
||||||
|
def test_add_clip(self):
|
||||||
|
# Arrange
|
||||||
|
im = hopper()
|
||||||
|
|
||||||
|
# Act
|
||||||
|
new = ImageChops.add(im, im)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assertEqual(new.getpixel((50, 50)), (255, 255, 254))
|
||||||
|
|
||||||
|
def test_add_modulo(self):
|
||||||
|
# Arrange
|
||||||
|
im1 = Image.open("Tests/images/imagedraw_ellipse_RGB.png")
|
||||||
|
im2 = Image.open("Tests/images/imagedraw_floodfill.png")
|
||||||
|
|
||||||
|
# Act
|
||||||
|
new = ImageChops.add_modulo(im1, im2)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assertEqual(new.getbbox(), (25, 25, 76, 76))
|
||||||
|
self.assertEqual(new.getpixel((50, 50)), ORANGE)
|
||||||
|
|
||||||
|
def test_add_modulo_no_clip(self):
|
||||||
|
# Arrange
|
||||||
|
im = hopper()
|
||||||
|
|
||||||
|
# Act
|
||||||
|
new = ImageChops.add_modulo(im, im)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assertEqual(new.getpixel((50, 50)), (224, 76, 254))
|
||||||
|
|
||||||
|
def test_blend(self):
|
||||||
|
# Arrange
|
||||||
|
im1 = Image.open("Tests/images/imagedraw_ellipse_RGB.png")
|
||||||
|
im2 = Image.open("Tests/images/imagedraw_floodfill.png")
|
||||||
|
|
||||||
|
# Act
|
||||||
|
new = ImageChops.blend(im1, im2, 0.5)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assertEqual(new.getbbox(), (25, 25, 76, 76))
|
||||||
|
self.assertEqual(new.getpixel((50, 50)), BROWN)
|
||||||
|
|
||||||
|
def test_constant(self):
|
||||||
|
# Arrange
|
||||||
|
im = Image.new("RGB", (20, 10))
|
||||||
|
|
||||||
|
# Act
|
||||||
|
new = ImageChops.constant(im, GREY)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assertEqual(new.size, im.size)
|
||||||
|
self.assertEqual(new.getpixel((0, 0)), GREY)
|
||||||
|
self.assertEqual(new.getpixel((19, 9)), GREY)
|
||||||
|
|
||||||
|
def test_darker_image(self):
|
||||||
|
# Arrange
|
||||||
|
im1 = Image.open("Tests/images/imagedraw_chord_RGB.png")
|
||||||
|
im2 = Image.open("Tests/images/imagedraw_outline_chord_RGB.png")
|
||||||
|
|
||||||
|
# Act
|
||||||
|
new = ImageChops.darker(im1, im2)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assert_image_equal(new, im2)
|
||||||
|
|
||||||
|
def test_darker_pixel(self):
|
||||||
|
# Arrange
|
||||||
|
im1 = hopper()
|
||||||
|
im2 = Image.open("Tests/images/imagedraw_chord_RGB.png")
|
||||||
|
|
||||||
|
# Act
|
||||||
|
new = ImageChops.darker(im1, im2)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assertEqual(new.getpixel((50, 50)), (240, 166, 0))
|
||||||
|
|
||||||
|
def test_difference(self):
|
||||||
|
# Arrange
|
||||||
|
im1 = Image.open("Tests/images/imagedraw_arc_end_le_start.png")
|
||||||
|
im2 = Image.open("Tests/images/imagedraw_arc_no_loops.png")
|
||||||
|
|
||||||
|
# Act
|
||||||
|
new = ImageChops.difference(im1, im2)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assertEqual(new.getbbox(), (25, 25, 76, 76))
|
||||||
|
|
||||||
|
def test_difference_pixel(self):
|
||||||
|
# Arrange
|
||||||
|
im1 = hopper()
|
||||||
|
im2 = Image.open("Tests/images/imagedraw_polygon_kite_RGB.png")
|
||||||
|
|
||||||
|
# Act
|
||||||
|
new = ImageChops.difference(im1, im2)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assertEqual(new.getpixel((50, 50)), (240, 166, 128))
|
||||||
|
|
||||||
|
def test_duplicate(self):
|
||||||
|
# Arrange
|
||||||
|
im = hopper()
|
||||||
|
|
||||||
|
# Act
|
||||||
|
new = ImageChops.duplicate(im)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assert_image_equal(new, im)
|
||||||
|
|
||||||
|
def test_invert(self):
|
||||||
|
# Arrange
|
||||||
|
im = Image.open("Tests/images/imagedraw_floodfill.png")
|
||||||
|
|
||||||
|
# Act
|
||||||
|
new = ImageChops.invert(im)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assertEqual(new.getbbox(), (0, 0, 100, 100))
|
||||||
|
self.assertEqual(new.getpixel((0, 0)), WHITE)
|
||||||
|
self.assertEqual(new.getpixel((50, 50)), CYAN)
|
||||||
|
|
||||||
|
def test_lighter_image(self):
|
||||||
|
# Arrange
|
||||||
|
im1 = Image.open("Tests/images/imagedraw_chord_RGB.png")
|
||||||
|
im2 = Image.open("Tests/images/imagedraw_outline_chord_RGB.png")
|
||||||
|
|
||||||
|
# Act
|
||||||
|
new = ImageChops.lighter(im1, im2)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assert_image_equal(new, im1)
|
||||||
|
|
||||||
|
def test_lighter_pixel(self):
|
||||||
|
# Arrange
|
||||||
|
im1 = hopper()
|
||||||
|
im2 = Image.open("Tests/images/imagedraw_chord_RGB.png")
|
||||||
|
|
||||||
|
# Act
|
||||||
|
new = ImageChops.lighter(im1, im2)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assertEqual(new.getpixel((50, 50)), (255, 255, 127))
|
||||||
|
|
||||||
|
def test_multiply_black(self):
|
||||||
|
"""If you multiply an image with a solid black image,
|
||||||
|
the result is black."""
|
||||||
|
# Arrange
|
||||||
|
im1 = hopper()
|
||||||
|
black = Image.new("RGB", im1.size, "black")
|
||||||
|
|
||||||
|
# Act
|
||||||
|
new = ImageChops.multiply(im1, black)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assert_image_equal(new, black)
|
||||||
|
|
||||||
|
def test_multiply_green(self):
|
||||||
|
# Arrange
|
||||||
|
im = Image.open("Tests/images/imagedraw_floodfill.png")
|
||||||
|
green = Image.new("RGB", im.size, "green")
|
||||||
|
|
||||||
|
# Act
|
||||||
|
new = ImageChops.multiply(im, green)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assertEqual(new.getbbox(), (25, 25, 76, 76))
|
||||||
|
self.assertEqual(new.getpixel((25, 25)), DARK_GREEN)
|
||||||
|
self.assertEqual(new.getpixel((50, 50)), BLACK)
|
||||||
|
|
||||||
|
def test_multiply_white(self):
|
||||||
|
"""If you multiply with a solid white image,
|
||||||
|
the image is unaffected."""
|
||||||
|
# Arrange
|
||||||
|
im1 = hopper()
|
||||||
|
white = Image.new("RGB", im1.size, "white")
|
||||||
|
|
||||||
|
# Act
|
||||||
|
new = ImageChops.multiply(im1, white)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assert_image_equal(new, im1)
|
||||||
|
|
||||||
|
def test_offset(self):
|
||||||
|
# Arrange
|
||||||
|
im = Image.open("Tests/images/imagedraw_ellipse_RGB.png")
|
||||||
|
xoffset = 45
|
||||||
|
yoffset = 20
|
||||||
|
|
||||||
|
# Act
|
||||||
|
new = ImageChops.offset(im, xoffset, yoffset)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assertEqual(new.getbbox(), (0, 45, 100, 96))
|
||||||
|
self.assertEqual(new.getpixel((50, 50)), BLACK)
|
||||||
|
self.assertEqual(new.getpixel((50+xoffset, 50+yoffset)), DARK_GREEN)
|
||||||
|
|
||||||
|
# Test no yoffset
|
||||||
|
self.assertEqual(ImageChops.offset(im, xoffset),
|
||||||
|
ImageChops.offset(im, xoffset, xoffset))
|
||||||
|
|
||||||
|
def test_screen(self):
|
||||||
|
# Arrange
|
||||||
|
im1 = Image.open("Tests/images/imagedraw_ellipse_RGB.png")
|
||||||
|
im2 = Image.open("Tests/images/imagedraw_floodfill.png")
|
||||||
|
|
||||||
|
# Act
|
||||||
|
new = ImageChops.screen(im1, im2)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assertEqual(new.getbbox(), (25, 25, 76, 76))
|
||||||
|
self.assertEqual(new.getpixel((50, 50)), ORANGE)
|
||||||
|
|
||||||
|
def test_subtract(self):
|
||||||
|
# Arrange
|
||||||
|
im1 = Image.open("Tests/images/imagedraw_chord_RGB.png")
|
||||||
|
im2 = Image.open("Tests/images/imagedraw_outline_chord_RGB.png")
|
||||||
|
|
||||||
|
# Act
|
||||||
|
new = ImageChops.subtract(im1, im2)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assertEqual(new.getbbox(), (25, 50, 76, 76))
|
||||||
|
self.assertEqual(new.getpixel((50, 50)), GREEN)
|
||||||
|
self.assertEqual(new.getpixel((50, 51)), BLACK)
|
||||||
|
|
||||||
|
def test_subtract_scale_offset(self):
|
||||||
|
# Arrange
|
||||||
|
im1 = Image.open("Tests/images/imagedraw_chord_RGB.png")
|
||||||
|
im2 = Image.open("Tests/images/imagedraw_outline_chord_RGB.png")
|
||||||
|
|
||||||
|
# Act
|
||||||
|
new = ImageChops.subtract(im1, im2, scale=2.5, offset=100)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assertEqual(new.getbbox(), (0, 0, 100, 100))
|
||||||
|
self.assertEqual(new.getpixel((50, 50)), (100, 202, 100))
|
||||||
|
|
||||||
|
def test_subtract_clip(self):
|
||||||
|
# Arrange
|
||||||
|
im1 = hopper()
|
||||||
|
im2 = Image.open("Tests/images/imagedraw_chord_RGB.png")
|
||||||
|
|
||||||
|
# Act
|
||||||
|
new = ImageChops.subtract(im1, im2)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assertEqual(new.getpixel((50, 50)), (0, 0, 127))
|
||||||
|
|
||||||
|
def test_subtract_modulo(self):
|
||||||
|
# Arrange
|
||||||
|
im1 = Image.open("Tests/images/imagedraw_chord_RGB.png")
|
||||||
|
im2 = Image.open("Tests/images/imagedraw_outline_chord_RGB.png")
|
||||||
|
|
||||||
|
# Act
|
||||||
|
new = ImageChops.subtract_modulo(im1, im2)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assertEqual(new.getbbox(), (25, 50, 76, 76))
|
||||||
|
self.assertEqual(new.getpixel((50, 50)), GREEN)
|
||||||
|
self.assertEqual(new.getpixel((50, 51)), BLACK)
|
||||||
|
|
||||||
|
def test_subtract_modulo_no_clip(self):
|
||||||
|
# Arrange
|
||||||
|
im1 = hopper()
|
||||||
|
im2 = Image.open("Tests/images/imagedraw_chord_RGB.png")
|
||||||
|
|
||||||
|
# Act
|
||||||
|
new = ImageChops.subtract_modulo(im1, im2)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
self.assertEqual(new.getpixel((50, 50)), (241, 167, 127))
|
||||||
|
|
||||||
def test_logical(self):
|
def test_logical(self):
|
||||||
|
|
||||||
def table(op, a, b):
|
def table(op, a, b):
|
||||||
|
|
|
@ -366,6 +366,11 @@ class TestImageDraw(PillowTestCase):
|
||||||
ImageDraw.floodfill(im, (W, H), red)
|
ImageDraw.floodfill(im, (W, H), red)
|
||||||
self.assert_image_equal(im, im_floodfill)
|
self.assert_image_equal(im, im_floodfill)
|
||||||
|
|
||||||
|
# Test filling at the edge of an image
|
||||||
|
im = Image.new("RGB", (1, 1))
|
||||||
|
ImageDraw.floodfill(im, (0, 0), red)
|
||||||
|
self.assert_image_equal(im, Image.new("RGB", (1, 1), red))
|
||||||
|
|
||||||
def test_floodfill_border(self):
|
def test_floodfill_border(self):
|
||||||
# floodfill() is experimental
|
# floodfill() is experimental
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from helper import unittest, PillowTestCase, hopper
|
from helper import unittest, PillowTestCase, hopper
|
||||||
from test_imageqt import PillowQtTestCase, PillowQPixmapTestCase
|
from test_imageqt import PillowQPixmapTestCase
|
||||||
|
|
||||||
from PIL import ImageQt
|
from PIL import ImageQt
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@ from PIL import ImageQt
|
||||||
class TestFromQPixmap(PillowQPixmapTestCase, PillowTestCase):
|
class TestFromQPixmap(PillowQPixmapTestCase, PillowTestCase):
|
||||||
|
|
||||||
def roundtrip(self, expected):
|
def roundtrip(self, expected):
|
||||||
PillowQtTestCase.setUp(self)
|
|
||||||
result = ImageQt.fromqpixmap(ImageQt.toqpixmap(expected))
|
result = ImageQt.fromqpixmap(ImageQt.toqpixmap(expected))
|
||||||
# Qt saves all pixmaps as rgb
|
# Qt saves all pixmaps as rgb
|
||||||
self.assert_image_equal(result, expected.convert('RGB'))
|
self.assert_image_equal(result, expected.convert('RGB'))
|
||||||
|
|
|
@ -25,7 +25,6 @@ if ImageQt.qt_is_installed:
|
||||||
class TestToQImage(PillowQtTestCase, PillowTestCase):
|
class TestToQImage(PillowQtTestCase, PillowTestCase):
|
||||||
|
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
PillowQtTestCase.setUp(self)
|
|
||||||
for mode in ('RGB', 'RGBA', 'L', 'P', '1'):
|
for mode in ('RGB', 'RGBA', 'L', 'P', '1'):
|
||||||
src = hopper(mode)
|
src = hopper(mode)
|
||||||
data = ImageQt.toqimage(src)
|
data = ImageQt.toqimage(src)
|
||||||
|
@ -61,8 +60,6 @@ class TestToQImage(PillowQtTestCase, PillowTestCase):
|
||||||
self.assert_image_equal(reloaded, src)
|
self.assert_image_equal(reloaded, src)
|
||||||
|
|
||||||
def test_segfault(self):
|
def test_segfault(self):
|
||||||
PillowQtTestCase.setUp(self)
|
|
||||||
|
|
||||||
app = QApplication([])
|
app = QApplication([])
|
||||||
ex = Example()
|
ex = Example()
|
||||||
assert(app) # Silence warning
|
assert(app) # Silence warning
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from helper import unittest, PillowTestCase, hopper
|
from helper import unittest, PillowTestCase, hopper
|
||||||
from test_imageqt import PillowQtTestCase, PillowQPixmapTestCase
|
from test_imageqt import PillowQPixmapTestCase
|
||||||
|
|
||||||
from PIL import ImageQt
|
from PIL import ImageQt
|
||||||
|
|
||||||
|
@ -10,8 +10,6 @@ if ImageQt.qt_is_installed:
|
||||||
class TestToQPixmap(PillowQPixmapTestCase, PillowTestCase):
|
class TestToQPixmap(PillowQPixmapTestCase, PillowTestCase):
|
||||||
|
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
PillowQtTestCase.setUp(self)
|
|
||||||
|
|
||||||
for mode in ('1', 'RGB', 'RGBA', 'L', 'P'):
|
for mode in ('1', 'RGB', 'RGBA', 'L', 'P'):
|
||||||
data = ImageQt.toqpixmap(hopper(mode))
|
data = ImageQt.toqpixmap(hopper(mode))
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# install extra test images
|
# install extra test images
|
||||||
|
|
||||||
rm -r test_images
|
rm -rf test_images
|
||||||
|
|
||||||
# Use SVN to just fetch a single git subdirectory
|
# Use SVN to just fetch a single git subdirectory
|
||||||
svn checkout https://github.com/python-pillow/pillow-depends/trunk/test_images
|
svn checkout https://github.com/python-pillow/pillow-depends/trunk/test_images
|
||||||
|
|
2
docs/_templates/sidebarhelp.html
vendored
2
docs/_templates/sidebarhelp.html
vendored
|
@ -1,4 +1,4 @@
|
||||||
<h3>Need help?</h3>
|
<h3>Need help?</h3>
|
||||||
<p>
|
<p>
|
||||||
You can get help via IRC at <a href="irc://irc.freenode.net#pil">irc://irc.freenode.net#pil</a>, <a href="https://gitter.im/python-pillow/Pillow">Gitter</a> or Stack Overflow <a href="https://stackoverflow.com/questions/tagged/pillow">here</a> and <a href="https://stackoverflow.com/questions/tagged/python-imaging-library">here</a>. Please <a href="https://github.com/python-pillow/Pillow/issues/new">report issues on GitHub</a>.
|
You can get help via IRC at <a href="irc://irc.freenode.net#pil">irc://irc.freenode.net#pil</a>, <a href="https://gitter.im/python-pillow/Pillow">Gitter</a> or <a href="https://stackoverflow.com/questions/tagged/python-imaging-library">Stack Overflow</a>. Please <a href="https://github.com/python-pillow/Pillow/issues/new">report issues on GitHub</a>.
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -529,8 +529,8 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
To enable PNG support, you need to build and install the ZLIB compression
|
To enable PNG support, you need to build and install the ZLIB compression
|
||||||
library before building the Python Imaging Library. See the installation
|
library before building the Python Imaging Library. See the `installation
|
||||||
documentation for details.
|
documentation <../installation.html>`_ for details.
|
||||||
|
|
||||||
PPM
|
PPM
|
||||||
^^^
|
^^^
|
||||||
|
|
|
@ -178,7 +178,7 @@ Many of Pillow's features require external libraries:
|
||||||
shaping (using HarfBuzz), and proper script itemization. As a
|
shaping (using HarfBuzz), and proper script itemization. As a
|
||||||
result, Raqm can support most writing systems covered by Unicode.
|
result, Raqm can support most writing systems covered by Unicode.
|
||||||
* libraqm depends on the following libraries: FreeType, HarfBuzz,
|
* libraqm depends on the following libraries: FreeType, HarfBuzz,
|
||||||
FriBiDi, make sure that you install them before install libraqm
|
FriBiDi, make sure that you install them before installing libraqm
|
||||||
if not available as package in your system.
|
if not available as package in your system.
|
||||||
* setting text direction or font features is not supported without
|
* setting text direction or font features is not supported without
|
||||||
libraqm.
|
libraqm.
|
||||||
|
|
|
@ -6,9 +6,10 @@ Added Complex Text Rendering
|
||||||
|
|
||||||
Pillow now supports complex text rendering for scripts requiring glyph
|
Pillow now supports complex text rendering for scripts requiring glyph
|
||||||
composition and bidirectional flow. This optional feature adds three
|
composition and bidirectional flow. This optional feature adds three
|
||||||
dependencies: harfbuzz, fribidi, and raqm. See the install
|
dependencies: harfbuzz, fribidi, and raqm. See the `install
|
||||||
documentation for further details. This feature is tested and works on
|
documentation <../installation.html>`_ for further details. This feature is
|
||||||
Unix and Mac, but has not yet been built on Windows platforms.
|
tested and works on Unix and Mac, but has not yet been built on Windows
|
||||||
|
platforms.
|
||||||
|
|
||||||
New Optional Parameters
|
New Optional Parameters
|
||||||
=======================
|
=======================
|
||||||
|
|
|
@ -2440,9 +2440,20 @@ def fromarray(obj, mode=None):
|
||||||
Creates an image memory from an object exporting the array interface
|
Creates an image memory from an object exporting the array interface
|
||||||
(using the buffer protocol).
|
(using the buffer protocol).
|
||||||
|
|
||||||
If obj is not contiguous, then the tobytes method is called
|
If **obj** is not contiguous, then the tobytes method is called
|
||||||
and :py:func:`~PIL.Image.frombuffer` is used.
|
and :py:func:`~PIL.Image.frombuffer` is used.
|
||||||
|
|
||||||
|
If you have an image in NumPy::
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
|
import numpy as np
|
||||||
|
im = Image.open('hopper.jpg')
|
||||||
|
a = numpy.asarray(im)
|
||||||
|
|
||||||
|
Then this can be used to convert it to a Pillow image::
|
||||||
|
|
||||||
|
im = Image.fromarray(a)
|
||||||
|
|
||||||
:param obj: Object with array interface
|
:param obj: Object with array interface
|
||||||
:param mode: Mode to use (will be determined from type if None)
|
:param mode: Mode to use (will be determined from type if None)
|
||||||
See: :ref:`concept-modes`.
|
See: :ref:`concept-modes`.
|
||||||
|
|
|
@ -353,7 +353,6 @@ def floodfill(image, xy, value, border=None, thresh=0):
|
||||||
return # seed point outside image
|
return # seed point outside image
|
||||||
edge = {(x, y)}
|
edge = {(x, y)}
|
||||||
full_edge = set() # use a set to record each unique pixel processed
|
full_edge = set() # use a set to record each unique pixel processed
|
||||||
if border is None:
|
|
||||||
while edge:
|
while edge:
|
||||||
new_edge = set()
|
new_edge = set()
|
||||||
for (x, y) in edge: # 4 adjacent method
|
for (x, y) in edge: # 4 adjacent method
|
||||||
|
@ -362,33 +361,19 @@ def floodfill(image, xy, value, border=None, thresh=0):
|
||||||
continue # if already processed, skip
|
continue # if already processed, skip
|
||||||
try:
|
try:
|
||||||
p = pixel[s, t]
|
p = pixel[s, t]
|
||||||
except IndexError:
|
except (ValueError, IndexError):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
if _color_diff(p, background) <= thresh:
|
if border is None:
|
||||||
|
fill = _color_diff(p, background) <= thresh
|
||||||
|
else:
|
||||||
|
fill = p != value and p != border
|
||||||
|
if fill:
|
||||||
pixel[s, t] = value
|
pixel[s, t] = value
|
||||||
new_edge.add((s, t))
|
new_edge.add((s, t))
|
||||||
full_edge.add((s, t))
|
full_edge.add((s, t))
|
||||||
full_edge = edge # do not record useless pixels to reduce memory consumption
|
full_edge = edge # do not record useless pixels to reduce memory consumption
|
||||||
edge = new_edge
|
edge = new_edge
|
||||||
else:
|
|
||||||
while edge:
|
|
||||||
new_edge = set()
|
|
||||||
for (x, y) in edge:
|
|
||||||
for (s, t) in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)):
|
|
||||||
if (s, t) in full_edge:
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
p = pixel[s, t]
|
|
||||||
except IndexError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
if p != value and p != border:
|
|
||||||
pixel[s, t] = value
|
|
||||||
new_edge.add((s, t))
|
|
||||||
full_edge.add((s, t))
|
|
||||||
full_edge = edge
|
|
||||||
edge = new_edge
|
|
||||||
|
|
||||||
|
|
||||||
def _color_diff(rgb1, rgb2):
|
def _color_diff(rgb1, rgb2):
|
||||||
|
|
|
@ -33,7 +33,14 @@ class MultibandFilter(Filter):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Kernel(MultibandFilter):
|
class BuiltinFilter(MultibandFilter):
|
||||||
|
def filter(self, image):
|
||||||
|
if image.mode == "P":
|
||||||
|
raise ValueError("cannot filter palette images")
|
||||||
|
return image.filter(*self.filterargs)
|
||||||
|
|
||||||
|
|
||||||
|
class Kernel(BuiltinFilter):
|
||||||
"""
|
"""
|
||||||
Create a convolution kernel. The current version only
|
Create a convolution kernel. The current version only
|
||||||
supports 3x3 and 5x5 integer and floating point kernels.
|
supports 3x3 and 5x5 integer and floating point kernels.
|
||||||
|
@ -60,16 +67,6 @@ class Kernel(MultibandFilter):
|
||||||
raise ValueError("not enough coefficients in kernel")
|
raise ValueError("not enough coefficients in kernel")
|
||||||
self.filterargs = size, scale, offset, kernel
|
self.filterargs = size, scale, offset, kernel
|
||||||
|
|
||||||
def filter(self, image):
|
|
||||||
if image.mode == "P":
|
|
||||||
raise ValueError("cannot filter palette images")
|
|
||||||
return image.filter(*self.filterargs)
|
|
||||||
|
|
||||||
|
|
||||||
class BuiltinFilter(Kernel):
|
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class RankFilter(Filter):
|
class RankFilter(Filter):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -59,7 +59,7 @@ class ImagePalette(object):
|
||||||
|
|
||||||
def getdata(self):
|
def getdata(self):
|
||||||
"""
|
"""
|
||||||
Get palette contents in format suitable # for the low-level
|
Get palette contents in format suitable for the low-level
|
||||||
``im.putpalette`` primitive.
|
``im.putpalette`` primitive.
|
||||||
|
|
||||||
.. warning:: This method is experimental.
|
.. warning:: This method is experimental.
|
||||||
|
|
|
@ -793,7 +793,7 @@ def jpeg_factory(fp=None, filename=None):
|
||||||
return im
|
return im
|
||||||
|
|
||||||
|
|
||||||
# -------------------------------------------------------------------q-
|
# ---------------------------------------------------------------------
|
||||||
# Registry stuff
|
# Registry stuff
|
||||||
|
|
||||||
Image.register_open(JpegImageFile.format, jpeg_factory, _accept)
|
Image.register_open(JpegImageFile.format, jpeg_factory, _accept)
|
||||||
|
|
|
@ -85,7 +85,7 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
|
||||||
return self.__frame
|
return self.__frame
|
||||||
|
|
||||||
|
|
||||||
# -------------------------------------------------------------------q-
|
# ---------------------------------------------------------------------
|
||||||
# Registry stuff
|
# Registry stuff
|
||||||
|
|
||||||
# Note that since MPO shares a factory with JPEG, we do not need to do a
|
# Note that since MPO shares a factory with JPEG, we do not need to do a
|
||||||
|
|
|
@ -2697,22 +2697,6 @@ _draw_ellipse(ImagingDrawObject* self, PyObject* args)
|
||||||
return Py_None;
|
return Py_None;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
|
||||||
_draw_line(ImagingDrawObject* self, PyObject* args)
|
|
||||||
{
|
|
||||||
int x0, y0, x1, y1;
|
|
||||||
int ink;
|
|
||||||
if (!PyArg_ParseTuple(args, "(ii)(ii)i", &x0, &y0, &x1, &y1, &ink))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (ImagingDrawLine(self->image->image, x0, y0, x1, y1,
|
|
||||||
&ink, self->blend) < 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
Py_INCREF(Py_None);
|
|
||||||
return Py_None;
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
_draw_lines(ImagingDrawObject* self, PyObject* args)
|
_draw_lines(ImagingDrawObject* self, PyObject* args)
|
||||||
{
|
{
|
||||||
|
@ -2766,21 +2750,6 @@ _draw_lines(ImagingDrawObject* self, PyObject* args)
|
||||||
return Py_None;
|
return Py_None;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
|
||||||
_draw_point(ImagingDrawObject* self, PyObject* args)
|
|
||||||
{
|
|
||||||
int x, y;
|
|
||||||
int ink;
|
|
||||||
if (!PyArg_ParseTuple(args, "(ii)i", &x, &y, &ink))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (ImagingDrawPoint(self->image->image, x, y, &ink, self->blend) < 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
Py_INCREF(Py_None);
|
|
||||||
return Py_None;
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
_draw_points(ImagingDrawObject* self, PyObject* args)
|
_draw_points(ImagingDrawObject* self, PyObject* args)
|
||||||
{
|
{
|
||||||
|
@ -2961,14 +2930,12 @@ _draw_rectangle(ImagingDrawObject* self, PyObject* args)
|
||||||
static struct PyMethodDef _draw_methods[] = {
|
static struct PyMethodDef _draw_methods[] = {
|
||||||
#ifdef WITH_IMAGEDRAW
|
#ifdef WITH_IMAGEDRAW
|
||||||
/* Graphics (ImageDraw) */
|
/* Graphics (ImageDraw) */
|
||||||
{"draw_line", (PyCFunction)_draw_line, 1},
|
|
||||||
{"draw_lines", (PyCFunction)_draw_lines, 1},
|
{"draw_lines", (PyCFunction)_draw_lines, 1},
|
||||||
#ifdef WITH_ARROW
|
#ifdef WITH_ARROW
|
||||||
{"draw_outline", (PyCFunction)_draw_outline, 1},
|
{"draw_outline", (PyCFunction)_draw_outline, 1},
|
||||||
#endif
|
#endif
|
||||||
{"draw_polygon", (PyCFunction)_draw_polygon, 1},
|
{"draw_polygon", (PyCFunction)_draw_polygon, 1},
|
||||||
{"draw_rectangle", (PyCFunction)_draw_rectangle, 1},
|
{"draw_rectangle", (PyCFunction)_draw_rectangle, 1},
|
||||||
{"draw_point", (PyCFunction)_draw_point, 1},
|
|
||||||
{"draw_points", (PyCFunction)_draw_points, 1},
|
{"draw_points", (PyCFunction)_draw_points, 1},
|
||||||
{"draw_arc", (PyCFunction)_draw_arc, 1},
|
{"draw_arc", (PyCFunction)_draw_arc, 1},
|
||||||
{"draw_bitmap", (PyCFunction)_draw_bitmap, 1},
|
{"draw_bitmap", (PyCFunction)_draw_bitmap, 1},
|
||||||
|
|
|
@ -674,47 +674,6 @@ font_getsize(FontObject* self, PyObject* args)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
|
||||||
font_getabc(FontObject* self, PyObject* args)
|
|
||||||
{
|
|
||||||
FT_ULong ch;
|
|
||||||
FT_Face face;
|
|
||||||
double a, b, c;
|
|
||||||
|
|
||||||
/* calculate ABC values for a given string */
|
|
||||||
|
|
||||||
PyObject* string;
|
|
||||||
if (!PyArg_ParseTuple(args, "O:getabc", &string))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
#if PY_VERSION_HEX >= 0x03000000
|
|
||||||
if (!PyUnicode_Check(string)) {
|
|
||||||
#else
|
|
||||||
if (!PyUnicode_Check(string) && !PyString_Check(string)) {
|
|
||||||
#endif
|
|
||||||
PyErr_SetString(PyExc_TypeError, "expected string");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (font_getchar(string, 0, &ch)) {
|
|
||||||
int index, error;
|
|
||||||
face = self->face;
|
|
||||||
index = FT_Get_Char_Index(face, ch);
|
|
||||||
/* Note: bitmap fonts within ttf fonts do not work, see #891/pr#960 */
|
|
||||||
error = FT_Load_Glyph(face, index, FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP);
|
|
||||||
if (error)
|
|
||||||
return geterror(error);
|
|
||||||
a = face->glyph->metrics.horiBearingX / 64.0;
|
|
||||||
b = face->glyph->metrics.width / 64.0;
|
|
||||||
c = (face->glyph->metrics.horiAdvance -
|
|
||||||
face->glyph->metrics.horiBearingX -
|
|
||||||
face->glyph->metrics.width) / 64.0;
|
|
||||||
} else
|
|
||||||
a = b = c = 0.0;
|
|
||||||
|
|
||||||
return Py_BuildValue("ddd", a, b, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
font_render(FontObject* self, PyObject* args)
|
font_render(FontObject* self, PyObject* args)
|
||||||
{
|
{
|
||||||
|
@ -854,7 +813,6 @@ font_dealloc(FontObject* self)
|
||||||
static PyMethodDef font_methods[] = {
|
static PyMethodDef font_methods[] = {
|
||||||
{"render", (PyCFunction) font_render, METH_VARARGS},
|
{"render", (PyCFunction) font_render, METH_VARARGS},
|
||||||
{"getsize", (PyCFunction) font_getsize, METH_VARARGS},
|
{"getsize", (PyCFunction) font_getsize, METH_VARARGS},
|
||||||
{"getabc", (PyCFunction) font_getabc, METH_VARARGS},
|
|
||||||
{NULL, NULL}
|
{NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ from unzip import unzip
|
||||||
from untar import untar
|
from untar import untar
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from fetch import fetch
|
||||||
from config import compilers, compiler_from_env, libs
|
from config import compilers, compiler_from_env, libs
|
||||||
|
|
||||||
|
|
||||||
|
@ -44,6 +45,8 @@ def extract(src, dest):
|
||||||
def extract_libs():
|
def extract_libs():
|
||||||
for name, lib in libs.items():
|
for name, lib in libs.items():
|
||||||
filename = lib['filename']
|
filename = lib['filename']
|
||||||
|
if not os.path.exists(filename):
|
||||||
|
filename = fetch(lib['url'])
|
||||||
if name == 'openjpeg':
|
if name == 'openjpeg':
|
||||||
for compiler in compilers.values():
|
for compiler in compilers.values():
|
||||||
if not os.path.exists(os.path.join(
|
if not os.path.exists(os.path.join(
|
||||||
|
|
Loading…
Reference in New Issue
Block a user